LCOV - code coverage report
Current view: top level - libavformat - tcp.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 128 0.0 %
Date: 2017-12-10 21:22:29 Functions: 0 8 0.0 %

          Line data    Source code
       1             : /*
       2             :  * TCP protocol
       3             :  * Copyright (c) 2002 Fabrice Bellard
       4             :  *
       5             :  * This file is part of FFmpeg.
       6             :  *
       7             :  * FFmpeg is free software; you can redistribute it and/or
       8             :  * modify it under the terms of the GNU Lesser General Public
       9             :  * License as published by the Free Software Foundation; either
      10             :  * version 2.1 of the License, or (at your option) any later version.
      11             :  *
      12             :  * FFmpeg is distributed in the hope that it will be useful,
      13             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15             :  * Lesser General Public License for more details.
      16             :  *
      17             :  * You should have received a copy of the GNU Lesser General Public
      18             :  * License along with FFmpeg; if not, write to the Free Software
      19             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
      20             :  */
      21             : #include "avformat.h"
      22             : #include "libavutil/avassert.h"
      23             : #include "libavutil/parseutils.h"
      24             : #include "libavutil/opt.h"
      25             : #include "libavutil/time.h"
      26             : 
      27             : #include "internal.h"
      28             : #include "network.h"
      29             : #include "os_support.h"
      30             : #include "url.h"
      31             : #if HAVE_POLL_H
      32             : #include <poll.h>
      33             : #endif
      34             : 
      35             : typedef struct TCPContext {
      36             :     const AVClass *class;
      37             :     int fd;
      38             :     int listen;
      39             :     int open_timeout;
      40             :     int rw_timeout;
      41             :     int listen_timeout;
      42             :     int recv_buffer_size;
      43             :     int send_buffer_size;
      44             :     int tcp_nodelay;
      45             : } TCPContext;
      46             : 
      47             : #define OFFSET(x) offsetof(TCPContext, x)
      48             : #define D AV_OPT_FLAG_DECODING_PARAM
      49             : #define E AV_OPT_FLAG_ENCODING_PARAM
      50             : static const AVOption options[] = {
      51             :     { "listen",          "Listen for incoming connections",  OFFSET(listen),         AV_OPT_TYPE_INT, { .i64 = 0 },     0,       2,       .flags = D|E },
      52             :     { "timeout",     "set timeout (in microseconds) of socket I/O operations", OFFSET(rw_timeout),     AV_OPT_TYPE_INT, { .i64 = -1 },         -1, INT_MAX, .flags = D|E },
      53             :     { "listen_timeout",  "Connection awaiting timeout (in milliseconds)",      OFFSET(listen_timeout), AV_OPT_TYPE_INT, { .i64 = -1 },         -1, INT_MAX, .flags = D|E },
      54             :     { "send_buffer_size", "Socket send buffer size (in bytes)",                OFFSET(send_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 },         -1, INT_MAX, .flags = D|E },
      55             :     { "recv_buffer_size", "Socket receive buffer size (in bytes)",             OFFSET(recv_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 },         -1, INT_MAX, .flags = D|E },
      56             :     { "tcp_nodelay", "Use TCP_NODELAY to disable nagle's algorithm",           OFFSET(tcp_nodelay), AV_OPT_TYPE_BOOL, { .i64 = 0 },             0, 1, .flags = D|E },
      57             :     { NULL }
      58             : };
      59             : 
      60             : static const AVClass tcp_class = {
      61             :     .class_name = "tcp",
      62             :     .item_name  = av_default_item_name,
      63             :     .option     = options,
      64             :     .version    = LIBAVUTIL_VERSION_INT,
      65             : };
      66             : 
      67             : /* return non zero if error */
      68           0 : static int tcp_open(URLContext *h, const char *uri, int flags)
      69             : {
      70           0 :     struct addrinfo hints = { 0 }, *ai, *cur_ai;
      71           0 :     int port, fd = -1;
      72           0 :     TCPContext *s = h->priv_data;
      73             :     const char *p;
      74             :     char buf[256];
      75             :     int ret;
      76             :     char hostname[1024],proto[1024],path[1024];
      77             :     char portstr[10];
      78           0 :     s->open_timeout = 5000000;
      79             : 
      80           0 :     av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname),
      81             :         &port, path, sizeof(path), uri);
      82           0 :     if (strcmp(proto, "tcp"))
      83           0 :         return AVERROR(EINVAL);
      84           0 :     if (port <= 0 || port >= 65536) {
      85           0 :         av_log(h, AV_LOG_ERROR, "Port missing in uri\n");
      86           0 :         return AVERROR(EINVAL);
      87             :     }
      88           0 :     p = strchr(uri, '?');
      89           0 :     if (p) {
      90           0 :         if (av_find_info_tag(buf, sizeof(buf), "listen", p)) {
      91           0 :             char *endptr = NULL;
      92           0 :             s->listen = strtol(buf, &endptr, 10);
      93             :             /* assume if no digits were found it is a request to enable it */
      94           0 :             if (buf == endptr)
      95           0 :                 s->listen = 1;
      96             :         }
      97           0 :         if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) {
      98           0 :             s->rw_timeout = strtol(buf, NULL, 10);
      99             :         }
     100           0 :         if (av_find_info_tag(buf, sizeof(buf), "listen_timeout", p)) {
     101           0 :             s->listen_timeout = strtol(buf, NULL, 10);
     102             :         }
     103             :     }
     104           0 :     if (s->rw_timeout >= 0) {
     105           0 :         s->open_timeout =
     106           0 :         h->rw_timeout   = s->rw_timeout;
     107             :     }
     108           0 :     hints.ai_family = AF_UNSPEC;
     109           0 :     hints.ai_socktype = SOCK_STREAM;
     110           0 :     snprintf(portstr, sizeof(portstr), "%d", port);
     111           0 :     if (s->listen)
     112           0 :         hints.ai_flags |= AI_PASSIVE;
     113           0 :     if (!hostname[0])
     114           0 :         ret = getaddrinfo(NULL, portstr, &hints, &ai);
     115             :     else
     116           0 :         ret = getaddrinfo(hostname, portstr, &hints, &ai);
     117           0 :     if (ret) {
     118           0 :         av_log(h, AV_LOG_ERROR,
     119             :                "Failed to resolve hostname %s: %s\n",
     120             :                hostname, gai_strerror(ret));
     121           0 :         return AVERROR(EIO);
     122             :     }
     123             : 
     124           0 :     cur_ai = ai;
     125             : 
     126           0 :  restart:
     127             : #if HAVE_STRUCT_SOCKADDR_IN6
     128             :     // workaround for IOS9 getaddrinfo in IPv6 only network use hardcode IPv4 address can not resolve port number.
     129           0 :     if (cur_ai->ai_family == AF_INET6){
     130           0 :         struct sockaddr_in6 * sockaddr_v6 = (struct sockaddr_in6 *)cur_ai->ai_addr;
     131           0 :         if (!sockaddr_v6->sin6_port){
     132           0 :             sockaddr_v6->sin6_port = htons(port);
     133             :         }
     134             :     }
     135             : #endif
     136             : 
     137           0 :     fd = ff_socket(cur_ai->ai_family,
     138             :                    cur_ai->ai_socktype,
     139             :                    cur_ai->ai_protocol);
     140           0 :     if (fd < 0) {
     141           0 :         ret = ff_neterrno();
     142           0 :         goto fail;
     143             :     }
     144             : 
     145             :     /* Set the socket's send or receive buffer sizes, if specified.
     146             :        If unspecified or setting fails, system default is used. */
     147           0 :     if (s->recv_buffer_size > 0) {
     148           0 :         setsockopt (fd, SOL_SOCKET, SO_RCVBUF, &s->recv_buffer_size, sizeof (s->recv_buffer_size));
     149             :     }
     150           0 :     if (s->send_buffer_size > 0) {
     151           0 :         setsockopt (fd, SOL_SOCKET, SO_SNDBUF, &s->send_buffer_size, sizeof (s->send_buffer_size));
     152             :     }
     153           0 :     if (s->tcp_nodelay > 0) {
     154           0 :         setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &s->tcp_nodelay, sizeof (s->tcp_nodelay));
     155             :     }
     156             : 
     157           0 :     if (s->listen == 2) {
     158             :         // multi-client
     159           0 :         if ((ret = ff_listen(fd, cur_ai->ai_addr, cur_ai->ai_addrlen)) < 0)
     160           0 :             goto fail1;
     161           0 :     } else if (s->listen == 1) {
     162             :         // single client
     163           0 :         if ((ret = ff_listen_bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
     164             :                                   s->listen_timeout, h)) < 0)
     165           0 :             goto fail1;
     166             :         // Socket descriptor already closed here. Safe to overwrite to client one.
     167           0 :         fd = ret;
     168             :     } else {
     169           0 :         if ((ret = ff_listen_connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
     170           0 :                                      s->open_timeout / 1000, h, !!cur_ai->ai_next)) < 0) {
     171             : 
     172           0 :             if (ret == AVERROR_EXIT)
     173           0 :                 goto fail1;
     174             :             else
     175           0 :                 goto fail;
     176             :         }
     177             :     }
     178             : 
     179           0 :     h->is_streamed = 1;
     180           0 :     s->fd = fd;
     181             : 
     182           0 :     freeaddrinfo(ai);
     183           0 :     return 0;
     184             : 
     185           0 :  fail:
     186           0 :     if (cur_ai->ai_next) {
     187             :         /* Retry with the next sockaddr */
     188           0 :         cur_ai = cur_ai->ai_next;
     189           0 :         if (fd >= 0)
     190           0 :             closesocket(fd);
     191           0 :         ret = 0;
     192           0 :         goto restart;
     193             :     }
     194           0 :  fail1:
     195           0 :     if (fd >= 0)
     196           0 :         closesocket(fd);
     197           0 :     freeaddrinfo(ai);
     198           0 :     return ret;
     199             : }
     200             : 
     201           0 : static int tcp_accept(URLContext *s, URLContext **c)
     202             : {
     203           0 :     TCPContext *sc = s->priv_data;
     204             :     TCPContext *cc;
     205             :     int ret;
     206           0 :     av_assert0(sc->listen);
     207           0 :     if ((ret = ffurl_alloc(c, s->filename, s->flags, &s->interrupt_callback)) < 0)
     208           0 :         return ret;
     209           0 :     cc = (*c)->priv_data;
     210           0 :     ret = ff_accept(sc->fd, sc->listen_timeout, s);
     211           0 :     if (ret < 0)
     212           0 :         return ret;
     213           0 :     cc->fd = ret;
     214           0 :     return 0;
     215             : }
     216             : 
     217           0 : static int tcp_read(URLContext *h, uint8_t *buf, int size)
     218             : {
     219           0 :     TCPContext *s = h->priv_data;
     220             :     int ret;
     221             : 
     222           0 :     if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
     223           0 :         ret = ff_network_wait_fd_timeout(s->fd, 0, h->rw_timeout, &h->interrupt_callback);
     224           0 :         if (ret)
     225           0 :             return ret;
     226             :     }
     227           0 :     ret = recv(s->fd, buf, size, 0);
     228           0 :     return ret < 0 ? ff_neterrno() : ret;
     229             : }
     230             : 
     231           0 : static int tcp_write(URLContext *h, const uint8_t *buf, int size)
     232             : {
     233           0 :     TCPContext *s = h->priv_data;
     234             :     int ret;
     235             : 
     236           0 :     if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
     237           0 :         ret = ff_network_wait_fd_timeout(s->fd, 1, h->rw_timeout, &h->interrupt_callback);
     238           0 :         if (ret)
     239           0 :             return ret;
     240             :     }
     241           0 :     ret = send(s->fd, buf, size, MSG_NOSIGNAL);
     242           0 :     return ret < 0 ? ff_neterrno() : ret;
     243             : }
     244             : 
     245           0 : static int tcp_shutdown(URLContext *h, int flags)
     246             : {
     247           0 :     TCPContext *s = h->priv_data;
     248             :     int how;
     249             : 
     250           0 :     if (flags & AVIO_FLAG_WRITE && flags & AVIO_FLAG_READ) {
     251           0 :         how = SHUT_RDWR;
     252           0 :     } else if (flags & AVIO_FLAG_WRITE) {
     253           0 :         how = SHUT_WR;
     254             :     } else {
     255           0 :         how = SHUT_RD;
     256             :     }
     257             : 
     258           0 :     return shutdown(s->fd, how);
     259             : }
     260             : 
     261           0 : static int tcp_close(URLContext *h)
     262             : {
     263           0 :     TCPContext *s = h->priv_data;
     264           0 :     closesocket(s->fd);
     265           0 :     return 0;
     266             : }
     267             : 
     268           0 : static int tcp_get_file_handle(URLContext *h)
     269             : {
     270           0 :     TCPContext *s = h->priv_data;
     271           0 :     return s->fd;
     272             : }
     273             : 
     274           0 : static int tcp_get_window_size(URLContext *h)
     275             : {
     276           0 :     TCPContext *s = h->priv_data;
     277             :     int avail;
     278           0 :     socklen_t avail_len = sizeof(avail);
     279             : 
     280             : #if HAVE_WINSOCK2_H
     281             :     /* SO_RCVBUF with winsock only reports the actual TCP window size when
     282             :     auto-tuning has been disabled via setting SO_RCVBUF */
     283             :     if (s->recv_buffer_size < 0) {
     284             :         return AVERROR(ENOSYS);
     285             :     }
     286             : #endif
     287             : 
     288           0 :     if (getsockopt(s->fd, SOL_SOCKET, SO_RCVBUF, &avail, &avail_len)) {
     289           0 :         return ff_neterrno();
     290             :     }
     291           0 :     return avail;
     292             : }
     293             : 
     294             : const URLProtocol ff_tcp_protocol = {
     295             :     .name                = "tcp",
     296             :     .url_open            = tcp_open,
     297             :     .url_accept          = tcp_accept,
     298             :     .url_read            = tcp_read,
     299             :     .url_write           = tcp_write,
     300             :     .url_close           = tcp_close,
     301             :     .url_get_file_handle = tcp_get_file_handle,
     302             :     .url_get_short_seek  = tcp_get_window_size,
     303             :     .url_shutdown        = tcp_shutdown,
     304             :     .priv_data_size      = sizeof(TCPContext),
     305             :     .flags               = URL_PROTOCOL_FLAG_NETWORK,
     306             :     .priv_data_class     = &tcp_class,
     307             : };

Generated by: LCOV version 1.13