mirror of
https://github.com/xenia-project/FFmpeg.git
synced 2024-11-30 23:10:32 +00:00
61cec5adaa
TLS is currently implemented over either OpenSSL or GnuTLS, with more backends likely to appear in the future. Currently, those backend libraries are part of the protocol names used during e.g. the configure stage of a build. Hide those details behind a generically-named declaration for the TLS protocol to avoid leaking those details into the configuration stage.
293 lines
7.3 KiB
C
293 lines
7.3 KiB
C
/*
|
|
* Copyright (c) 2007 The Libav Project
|
|
*
|
|
* This file is part of Libav.
|
|
*
|
|
* Libav is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* Libav is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with Libav; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <fcntl.h>
|
|
#include "network.h"
|
|
#include "tls.h"
|
|
#include "url.h"
|
|
#include "libavcodec/internal.h"
|
|
#include "libavutil/mem.h"
|
|
|
|
void ff_tls_init(void)
|
|
{
|
|
#if CONFIG_TLS_PROTOCOL
|
|
#if CONFIG_OPENSSL
|
|
ff_openssl_init();
|
|
#endif
|
|
#if CONFIG_GNUTLS
|
|
ff_gnutls_init();
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
void ff_tls_deinit(void)
|
|
{
|
|
#if CONFIG_TLS_PROTOCOL
|
|
#if CONFIG_OPENSSL
|
|
ff_openssl_deinit();
|
|
#endif
|
|
#if CONFIG_GNUTLS
|
|
ff_gnutls_deinit();
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
int ff_network_inited_globally;
|
|
|
|
int ff_network_init(void)
|
|
{
|
|
#if HAVE_WINSOCK2_H
|
|
WSADATA wsaData;
|
|
#endif
|
|
|
|
if (!ff_network_inited_globally)
|
|
av_log(NULL, AV_LOG_WARNING, "Using network protocols without global "
|
|
"network initialization. Please use "
|
|
"avformat_network_init(), this will "
|
|
"become mandatory later.\n");
|
|
#if HAVE_WINSOCK2_H
|
|
if (WSAStartup(MAKEWORD(1,1), &wsaData))
|
|
return 0;
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
int ff_network_wait_fd(int fd, int write)
|
|
{
|
|
int ev = write ? POLLOUT : POLLIN;
|
|
struct pollfd p = { .fd = fd, .events = ev, .revents = 0 };
|
|
int ret;
|
|
ret = poll(&p, 1, 100);
|
|
return ret < 0 ? ff_neterrno() : p.revents & (ev | POLLERR | POLLHUP) ? 0 : AVERROR(EAGAIN);
|
|
}
|
|
|
|
void ff_network_close(void)
|
|
{
|
|
#if HAVE_WINSOCK2_H
|
|
WSACleanup();
|
|
#endif
|
|
}
|
|
|
|
#if HAVE_WINSOCK2_H
|
|
int ff_neterrno(void)
|
|
{
|
|
int err = WSAGetLastError();
|
|
switch (err) {
|
|
case WSAEWOULDBLOCK:
|
|
return AVERROR(EAGAIN);
|
|
case WSAEINTR:
|
|
return AVERROR(EINTR);
|
|
case WSAEPROTONOSUPPORT:
|
|
return AVERROR(EPROTONOSUPPORT);
|
|
case WSAETIMEDOUT:
|
|
return AVERROR(ETIMEDOUT);
|
|
case WSAECONNREFUSED:
|
|
return AVERROR(ECONNREFUSED);
|
|
case WSAEINPROGRESS:
|
|
return AVERROR(EINPROGRESS);
|
|
}
|
|
return -err;
|
|
}
|
|
#endif
|
|
|
|
int ff_is_multicast_address(struct sockaddr *addr)
|
|
{
|
|
if (addr->sa_family == AF_INET) {
|
|
return IN_MULTICAST(ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr));
|
|
}
|
|
#if HAVE_STRUCT_SOCKADDR_IN6
|
|
if (addr->sa_family == AF_INET6) {
|
|
return IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)addr)->sin6_addr);
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ff_poll_interrupt(struct pollfd *p, nfds_t nfds, int timeout,
|
|
AVIOInterruptCB *cb)
|
|
{
|
|
int runs = timeout / POLLING_TIME;
|
|
int ret = 0;
|
|
|
|
do {
|
|
if (ff_check_interrupt(cb))
|
|
return AVERROR_EXIT;
|
|
ret = poll(p, nfds, POLLING_TIME);
|
|
if (ret != 0)
|
|
break;
|
|
} while (timeout < 0 || runs-- > 0);
|
|
|
|
if (!ret)
|
|
return AVERROR(ETIMEDOUT);
|
|
if (ret < 0)
|
|
return AVERROR(errno);
|
|
return ret;
|
|
}
|
|
|
|
int ff_socket(int af, int type, int proto)
|
|
{
|
|
int fd;
|
|
|
|
#ifdef SOCK_CLOEXEC
|
|
fd = socket(af, type | SOCK_CLOEXEC, proto);
|
|
if (fd == -1 && errno == EINVAL)
|
|
#endif
|
|
{
|
|
fd = socket(af, type, proto);
|
|
#if HAVE_FCNTL
|
|
if (fd != -1)
|
|
fcntl(fd, F_SETFD, FD_CLOEXEC);
|
|
#endif
|
|
}
|
|
#ifdef SO_NOSIGPIPE
|
|
if (fd != -1)
|
|
setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &(int){1}, sizeof(int));
|
|
#endif
|
|
return fd;
|
|
}
|
|
|
|
int ff_listen_bind(int fd, const struct sockaddr *addr,
|
|
socklen_t addrlen, int timeout, URLContext *h)
|
|
{
|
|
int ret;
|
|
int reuse = 1;
|
|
struct pollfd lp = { fd, POLLIN, 0 };
|
|
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
|
|
ret = bind(fd, addr, addrlen);
|
|
if (ret)
|
|
return ff_neterrno();
|
|
|
|
ret = listen(fd, 1);
|
|
if (ret)
|
|
return ff_neterrno();
|
|
|
|
ret = ff_poll_interrupt(&lp, 1, timeout, &h->interrupt_callback);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = accept(fd, NULL, NULL);
|
|
if (ret < 0)
|
|
return ff_neterrno();
|
|
|
|
closesocket(fd);
|
|
|
|
ff_socket_nonblock(ret, 1);
|
|
return ret;
|
|
}
|
|
|
|
int ff_listen_connect(int fd, const struct sockaddr *addr,
|
|
socklen_t addrlen, int timeout, URLContext *h,
|
|
int will_try_next)
|
|
{
|
|
struct pollfd p = {fd, POLLOUT, 0};
|
|
int ret;
|
|
socklen_t optlen;
|
|
|
|
ff_socket_nonblock(fd, 1);
|
|
|
|
while ((ret = connect(fd, addr, addrlen))) {
|
|
ret = ff_neterrno();
|
|
switch (ret) {
|
|
case AVERROR(EINTR):
|
|
if (ff_check_interrupt(&h->interrupt_callback))
|
|
return AVERROR_EXIT;
|
|
continue;
|
|
case AVERROR(EINPROGRESS):
|
|
case AVERROR(EAGAIN):
|
|
ret = ff_poll_interrupt(&p, 1, timeout, &h->interrupt_callback);
|
|
if (ret < 0)
|
|
return ret;
|
|
optlen = sizeof(ret);
|
|
if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen))
|
|
ret = AVUNERROR(ff_neterrno());
|
|
if (ret != 0) {
|
|
char errbuf[100];
|
|
ret = AVERROR(ret);
|
|
av_strerror(ret, errbuf, sizeof(errbuf));
|
|
if (will_try_next)
|
|
av_log(h, AV_LOG_WARNING,
|
|
"Connection to %s failed (%s), trying next address\n",
|
|
h->filename, errbuf);
|
|
else
|
|
av_log(h, AV_LOG_ERROR, "Connection to %s failed: %s\n",
|
|
h->filename, errbuf);
|
|
}
|
|
default:
|
|
return ret;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int match_host_pattern(const char *pattern, const char *hostname)
|
|
{
|
|
int len_p, len_h;
|
|
if (!strcmp(pattern, "*"))
|
|
return 1;
|
|
// Skip a possible *. at the start of the pattern
|
|
if (pattern[0] == '*')
|
|
pattern++;
|
|
if (pattern[0] == '.')
|
|
pattern++;
|
|
len_p = strlen(pattern);
|
|
len_h = strlen(hostname);
|
|
if (len_p > len_h)
|
|
return 0;
|
|
// Simply check if the end of hostname is equal to 'pattern'
|
|
if (!strcmp(pattern, &hostname[len_h - len_p])) {
|
|
if (len_h == len_p)
|
|
return 1; // Exact match
|
|
if (hostname[len_h - len_p - 1] == '.')
|
|
return 1; // The matched substring is a domain and not just a substring of a domain
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int ff_http_match_no_proxy(const char *no_proxy, const char *hostname)
|
|
{
|
|
char *buf, *start;
|
|
int ret = 0;
|
|
if (!no_proxy)
|
|
return 0;
|
|
if (!hostname)
|
|
return 0;
|
|
buf = av_strdup(no_proxy);
|
|
if (!buf)
|
|
return 0;
|
|
start = buf;
|
|
while (start) {
|
|
char *sep, *next = NULL;
|
|
start += strspn(start, " ,");
|
|
sep = start + strcspn(start, " ,");
|
|
if (*sep) {
|
|
next = sep + 1;
|
|
*sep = '\0';
|
|
}
|
|
if (match_host_pattern(start, hostname)) {
|
|
ret = 1;
|
|
break;
|
|
}
|
|
start = next;
|
|
}
|
|
av_free(buf);
|
|
return ret;
|
|
}
|