mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-11-21 10:55:51 +02:00
631c56a8e4
It was sort of optional before - if you didn't call it, networking was initialized on demand, and an ugly warning was logged. Also, the doxygen comments threatened that it would be made strictly required one day. Make it explicitly optional. I would prefer to deprecate it fully, but there might still be legitimate reasons to use this. But the average user won't need it. This is needed only for two reasons: to initialize TLS libraries like OpenSSL and GnuTLS, and winsock. OpenSSL and GnuTLS were already silently initialized on demand if the global network init function was not called. They also have various thread-safety acrobatics, which make concurrent initialization within libavformat safe. In addition, the libraries are moving towards making their global init functions safe, which removes all need for central global init. In particular, GnuTLS 3.5.16 and OpenSSL 1.1.0g have been found to have safe init functions. In all cases, they use internal reference counters to avoid that the global uninit functions interfere with concurrent uses of the library by other API users who called global init. winsock should be thread-safe as well, and maintains an internal reference counter as well. Since we still support ancient TLS libraries, which do not have this fixed, and since it's unknown whether winsock and GnuTLS reinitialization is costly in any way, don't deprecate the libavformat functions yet.
349 lines
8.7 KiB
C
349 lines
8.7 KiB
C
/*
|
|
* Copyright (c) 2007 The FFmpeg Project
|
|
*
|
|
* This file is part of FFmpeg.
|
|
*
|
|
* FFmpeg 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.
|
|
*
|
|
* FFmpeg 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 FFmpeg; 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/avutil.h"
|
|
#include "libavutil/mem.h"
|
|
#include "libavutil/time.h"
|
|
|
|
int ff_tls_init(void)
|
|
{
|
|
#if CONFIG_TLS_PROTOCOL
|
|
#if CONFIG_OPENSSL
|
|
int ret;
|
|
if ((ret = ff_openssl_init()) < 0)
|
|
return ret;
|
|
#endif
|
|
#if CONFIG_GNUTLS
|
|
ff_gnutls_init();
|
|
#endif
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
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_init(void)
|
|
{
|
|
#if HAVE_WINSOCK2_H
|
|
WSADATA wsaData;
|
|
|
|
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, POLLING_TIME);
|
|
return ret < 0 ? ff_neterrno() : p.revents & (ev | POLLERR | POLLHUP) ? 0 : AVERROR(EAGAIN);
|
|
}
|
|
|
|
int ff_network_wait_fd_timeout(int fd, int write, int64_t timeout, AVIOInterruptCB *int_cb)
|
|
{
|
|
int ret;
|
|
int64_t wait_start = 0;
|
|
|
|
while (1) {
|
|
if (ff_check_interrupt(int_cb))
|
|
return AVERROR_EXIT;
|
|
ret = ff_network_wait_fd(fd, write);
|
|
if (ret != AVERROR(EAGAIN))
|
|
return ret;
|
|
if (timeout > 0) {
|
|
if (!wait_start)
|
|
wait_start = av_gettime_relative();
|
|
else if (av_gettime_relative() - wait_start > timeout)
|
|
return AVERROR(ETIMEDOUT);
|
|
}
|
|
}
|
|
}
|
|
|
|
int ff_network_sleep_interruptible(int64_t timeout, AVIOInterruptCB *int_cb)
|
|
{
|
|
int64_t wait_start = av_gettime_relative();
|
|
|
|
while (1) {
|
|
int64_t time_left;
|
|
|
|
if (ff_check_interrupt(int_cb))
|
|
return AVERROR_EXIT;
|
|
|
|
time_left = timeout - (av_gettime_relative() - wait_start);
|
|
if (time_left <= 0)
|
|
return AVERROR(ETIMEDOUT);
|
|
|
|
av_usleep(FFMIN(time_left, POLLING_TIME * 1000));
|
|
}
|
|
}
|
|
|
|
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 ff_neterrno();
|
|
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) {
|
|
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
|
|
av_log(NULL, AV_LOG_DEBUG, "Failed to set close on exec\n");
|
|
}
|
|
#endif
|
|
}
|
|
#ifdef SO_NOSIGPIPE
|
|
if (fd != -1)
|
|
setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &(int){1}, sizeof(int));
|
|
#endif
|
|
return fd;
|
|
}
|
|
|
|
int ff_listen(int fd, const struct sockaddr *addr,
|
|
socklen_t addrlen)
|
|
{
|
|
int ret;
|
|
int reuse = 1;
|
|
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))) {
|
|
av_log(NULL, AV_LOG_WARNING, "setsockopt(SO_REUSEADDR) failed\n");
|
|
}
|
|
ret = bind(fd, addr, addrlen);
|
|
if (ret)
|
|
return ff_neterrno();
|
|
|
|
ret = listen(fd, 1);
|
|
if (ret)
|
|
return ff_neterrno();
|
|
return ret;
|
|
}
|
|
|
|
int ff_accept(int fd, int timeout, URLContext *h)
|
|
{
|
|
int ret;
|
|
struct pollfd lp = { fd, POLLIN, 0 };
|
|
|
|
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();
|
|
if (ff_socket_nonblock(ret, 1) < 0)
|
|
av_log(NULL, AV_LOG_DEBUG, "ff_socket_nonblock failed\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ff_listen_bind(int fd, const struct sockaddr *addr,
|
|
socklen_t addrlen, int timeout, URLContext *h)
|
|
{
|
|
int ret;
|
|
if ((ret = ff_listen(fd, addr, addrlen)) < 0)
|
|
return ret;
|
|
if ((ret = ff_accept(fd, timeout, h)) < 0)
|
|
return ret;
|
|
closesocket(fd);
|
|
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;
|
|
|
|
if (ff_socket_nonblock(fd, 1) < 0)
|
|
av_log(NULL, AV_LOG_DEBUG, "ff_socket_nonblock failed\n");
|
|
|
|
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;
|
|
}
|