Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  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. } TCPContext;
  43.  
  44. #define OFFSET(x) offsetof(TCPContext, x)
  45. #define D AV_OPT_FLAG_DECODING_PARAM
  46. #define E AV_OPT_FLAG_ENCODING_PARAM
  47. static const AVOption options[] = {
  48.     { "listen",          "Listen for incoming connections",  OFFSET(listen),         AV_OPT_TYPE_INT, { .i64 = 0 },     0,       2,       .flags = D|E },
  49.     { "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 },
  50.     { "listen_timeout",  "Connection awaiting timeout (in milliseconds)",      OFFSET(listen_timeout), AV_OPT_TYPE_INT, { .i64 = -1 },         -1, INT_MAX, .flags = D|E },
  51.     { NULL }
  52. };
  53.  
  54. static const AVClass tcp_class = {
  55.     .class_name = "tcp",
  56.     .item_name  = av_default_item_name,
  57.     .option     = options,
  58.     .version    = LIBAVUTIL_VERSION_INT,
  59. };
  60.  
  61. /* return non zero if error */
  62. static int tcp_open(URLContext *h, const char *uri, int flags)
  63. {
  64.     struct addrinfo hints = { 0 }, *ai, *cur_ai;
  65.     int port, fd = -1;
  66.     TCPContext *s = h->priv_data;
  67.     const char *p;
  68.     char buf[256];
  69.     int ret;
  70.     char hostname[1024],proto[1024],path[1024];
  71.     char portstr[10];
  72.     s->open_timeout = 5000000;
  73.  
  74.     av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname),
  75.         &port, path, sizeof(path), uri);
  76.     if (strcmp(proto, "tcp"))
  77.         return AVERROR(EINVAL);
  78.     if (port <= 0 || port >= 65536) {
  79.         av_log(h, AV_LOG_ERROR, "Port missing in uri\n");
  80.         return AVERROR(EINVAL);
  81.     }
  82.     p = strchr(uri, '?');
  83.     if (p) {
  84.         if (av_find_info_tag(buf, sizeof(buf), "listen", p)) {
  85.             char *endptr = NULL;
  86.             s->listen = strtol(buf, &endptr, 10);
  87.             /* assume if no digits were found it is a request to enable it */
  88.             if (buf == endptr)
  89.                 s->listen = 1;
  90.         }
  91.         if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) {
  92.             s->rw_timeout = strtol(buf, NULL, 10);
  93.         }
  94.         if (av_find_info_tag(buf, sizeof(buf), "listen_timeout", p)) {
  95.             s->listen_timeout = strtol(buf, NULL, 10);
  96.         }
  97.     }
  98.     if (s->rw_timeout >= 0) {
  99.         s->open_timeout =
  100.         h->rw_timeout   = s->rw_timeout;
  101.     }
  102.     hints.ai_family = AF_UNSPEC;
  103.     hints.ai_socktype = SOCK_STREAM;
  104.     snprintf(portstr, sizeof(portstr), "%d", port);
  105.     if (s->listen)
  106.         hints.ai_flags |= AI_PASSIVE;
  107.     if (!hostname[0])
  108.         ret = getaddrinfo(NULL, portstr, &hints, &ai);
  109.     else
  110.         ret = getaddrinfo(hostname, portstr, &hints, &ai);
  111.     if (ret) {
  112.         av_log(h, AV_LOG_ERROR,
  113.                "Failed to resolve hostname %s: %s\n",
  114.                hostname, gai_strerror(ret));
  115.         return AVERROR(EIO);
  116.     }
  117.  
  118.     cur_ai = ai;
  119.  
  120.  restart:
  121.     fd = ff_socket(cur_ai->ai_family,
  122.                    cur_ai->ai_socktype,
  123.                    cur_ai->ai_protocol);
  124.     if (fd < 0) {
  125.         ret = ff_neterrno();
  126.         goto fail;
  127.     }
  128.  
  129.     if (s->listen == 2) {
  130.         // multi-client
  131.         if ((ret = ff_listen(fd, cur_ai->ai_addr, cur_ai->ai_addrlen)) < 0)
  132.             goto fail1;
  133.     } else if (s->listen == 1) {
  134.         // single client
  135.         if ((fd = ff_listen_bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
  136.                                  s->listen_timeout, h)) < 0) {
  137.             ret = fd;
  138.             goto fail1;
  139.         }
  140.     } else {
  141.         if ((ret = ff_listen_connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
  142.                                      s->open_timeout / 1000, h, !!cur_ai->ai_next)) < 0) {
  143.  
  144.             if (ret == AVERROR_EXIT)
  145.                 goto fail1;
  146.             else
  147.                 goto fail;
  148.         }
  149.     }
  150.  
  151.     h->is_streamed = 1;
  152.     s->fd = fd;
  153.     freeaddrinfo(ai);
  154.     return 0;
  155.  
  156.  fail:
  157.     if (cur_ai->ai_next) {
  158.         /* Retry with the next sockaddr */
  159.         cur_ai = cur_ai->ai_next;
  160.         if (fd >= 0)
  161.             closesocket(fd);
  162.         ret = 0;
  163.         goto restart;
  164.     }
  165.  fail1:
  166.     if (fd >= 0)
  167.         closesocket(fd);
  168.     freeaddrinfo(ai);
  169.     return ret;
  170. }
  171.  
  172. static int tcp_accept(URLContext *s, URLContext **c)
  173. {
  174.     TCPContext *sc = s->priv_data;
  175.     TCPContext *cc;
  176.     int ret;
  177.     av_assert0(sc->listen);
  178.     if ((ret = ffurl_alloc(c, s->filename, s->flags, &s->interrupt_callback)) < 0)
  179.         return ret;
  180.     cc = (*c)->priv_data;
  181.     ret = ff_accept(sc->fd, sc->listen_timeout, s);
  182.     if (ret < 0)
  183.         return ff_neterrno();
  184.     cc->fd = ret;
  185.     return 0;
  186. }
  187.  
  188. static int tcp_read(URLContext *h, uint8_t *buf, int size)
  189. {
  190.     TCPContext *s = h->priv_data;
  191.     int ret;
  192.  
  193.     if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
  194.         ret = ff_network_wait_fd_timeout(s->fd, 0, h->rw_timeout, &h->interrupt_callback);
  195.         if (ret)
  196.             return ret;
  197.     }
  198.     ret = recv(s->fd, buf, size, 0);
  199.     return ret < 0 ? ff_neterrno() : ret;
  200. }
  201.  
  202. static int tcp_write(URLContext *h, const uint8_t *buf, int size)
  203. {
  204.     TCPContext *s = h->priv_data;
  205.     int ret;
  206.  
  207.     if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
  208.         ret = ff_network_wait_fd_timeout(s->fd, 1, h->rw_timeout, &h->interrupt_callback);
  209.         if (ret)
  210.             return ret;
  211.     }
  212.     ret = send(s->fd, buf, size, MSG_NOSIGNAL);
  213.     return ret < 0 ? ff_neterrno() : ret;
  214. }
  215.  
  216. static int tcp_shutdown(URLContext *h, int flags)
  217. {
  218.     TCPContext *s = h->priv_data;
  219.     int how;
  220.  
  221.     if (flags & AVIO_FLAG_WRITE && flags & AVIO_FLAG_READ) {
  222.         how = SHUT_RDWR;
  223.     } else if (flags & AVIO_FLAG_WRITE) {
  224.         how = SHUT_WR;
  225.     } else {
  226.         how = SHUT_RD;
  227.     }
  228.  
  229.     return shutdown(s->fd, how);
  230. }
  231.  
  232. static int tcp_close(URLContext *h)
  233. {
  234.     TCPContext *s = h->priv_data;
  235.     closesocket(s->fd);
  236.     return 0;
  237. }
  238.  
  239. static int tcp_get_file_handle(URLContext *h)
  240. {
  241.     TCPContext *s = h->priv_data;
  242.     return s->fd;
  243. }
  244.  
  245. URLProtocol ff_tcp_protocol = {
  246.     .name                = "tcp",
  247.     .url_open            = tcp_open,
  248.     .url_accept          = tcp_accept,
  249.     .url_read            = tcp_read,
  250.     .url_write           = tcp_write,
  251.     .url_close           = tcp_close,
  252.     .url_get_file_handle = tcp_get_file_handle,
  253.     .url_shutdown        = tcp_shutdown,
  254.     .priv_data_size      = sizeof(TCPContext),
  255.     .flags               = URL_PROTOCOL_FLAG_NETWORK,
  256.     .priv_data_class     = &tcp_class,
  257. };
  258.