FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
tcp.c
Go to the documentation of this file.
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/parseutils.h"
23 #include "libavutil/opt.h"
24 #include "libavutil/time.h"
25 #include "internal.h"
26 #include "network.h"
27 #include "os_support.h"
28 #include "url.h"
29 #if HAVE_POLL_H
30 #include <poll.h>
31 #endif
32 
33 typedef struct TCPContext {
34  const AVClass *class;
35  int fd;
36  int listen;
39 } TCPContext;
40 
41 #define OFFSET(x) offsetof(TCPContext, x)
42 #define D AV_OPT_FLAG_DECODING_PARAM
43 #define E AV_OPT_FLAG_ENCODING_PARAM
44 static const AVOption options[] = {
45 {"listen", "listen on port instead of connecting", OFFSET(listen), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D|E },
46 {"timeout", "timeout of socket i/o operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, D|E },
47 {"listen_timeout", "connection awaiting timeout", OFFSET(listen_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
48 {NULL}
49 };
50 
51 static const AVClass tcp_context_class = {
52  .class_name = "tcp",
53  .item_name = av_default_item_name,
54  .option = options,
55  .version = LIBAVUTIL_VERSION_INT,
56 };
57 
58 /* return non zero if error */
59 static int tcp_open(URLContext *h, const char *uri, int flags)
60 {
61  struct addrinfo hints = { 0 }, *ai, *cur_ai;
62  int port, fd = -1;
63  TCPContext *s = h->priv_data;
64  const char *p;
65  char buf[256];
66  int ret;
67  socklen_t optlen;
68  char hostname[1024],proto[1024],path[1024];
69  char portstr[10];
70  h->rw_timeout = 5000000;
71 
72  av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname),
73  &port, path, sizeof(path), uri);
74  if (strcmp(proto, "tcp"))
75  return AVERROR(EINVAL);
76  if (port <= 0 || port >= 65536) {
77  av_log(h, AV_LOG_ERROR, "Port missing in uri\n");
78  return AVERROR(EINVAL);
79  }
80  p = strchr(uri, '?');
81  if (p) {
82  if (av_find_info_tag(buf, sizeof(buf), "listen", p))
83  s->listen = 1;
84  if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) {
85  s->rw_timeout = strtol(buf, NULL, 10);
86  }
87  if (av_find_info_tag(buf, sizeof(buf), "listen_timeout", p)) {
88  s->listen_timeout = strtol(buf, NULL, 10);
89  }
90  }
91  h->rw_timeout = s->rw_timeout;
92  hints.ai_family = AF_UNSPEC;
93  hints.ai_socktype = SOCK_STREAM;
94  snprintf(portstr, sizeof(portstr), "%d", port);
95  if (s->listen)
96  hints.ai_flags |= AI_PASSIVE;
97  if (!hostname[0])
98  ret = getaddrinfo(NULL, portstr, &hints, &ai);
99  else
100  ret = getaddrinfo(hostname, portstr, &hints, &ai);
101  if (ret) {
102  av_log(h, AV_LOG_ERROR,
103  "Failed to resolve hostname %s: %s\n",
104  hostname, gai_strerror(ret));
105  return AVERROR(EIO);
106  }
107 
108  cur_ai = ai;
109 
110  restart:
111  ret = AVERROR(EIO);
112  fd = socket(cur_ai->ai_family, cur_ai->ai_socktype, cur_ai->ai_protocol);
113  if (fd < 0)
114  goto fail;
115 
116  if (s->listen) {
117  int fd1;
118  int reuse = 1;
119  struct pollfd lp = { fd, POLLIN, 0 };
120  setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
121  ret = bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen);
122  if (ret) {
123  ret = ff_neterrno();
124  goto fail1;
125  }
126  ret = listen(fd, 1);
127  if (ret) {
128  ret = ff_neterrno();
129  goto fail1;
130  }
131  ret = poll(&lp, 1, s->listen_timeout >= 0 ? s->listen_timeout : -1);
132  if (ret <= 0) {
133  ret = AVERROR(ETIMEDOUT);
134  goto fail1;
135  }
136  fd1 = accept(fd, NULL, NULL);
137  if (fd1 < 0) {
138  ret = ff_neterrno();
139  goto fail1;
140  }
141  closesocket(fd);
142  fd = fd1;
143  ff_socket_nonblock(fd, 1);
144  } else {
145  redo:
146  ff_socket_nonblock(fd, 1);
147  ret = connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen);
148  }
149 
150  if (ret < 0) {
151  struct pollfd p = {fd, POLLOUT, 0};
152  int64_t wait_started;
153  ret = ff_neterrno();
154  if (ret == AVERROR(EINTR)) {
156  ret = AVERROR_EXIT;
157  goto fail1;
158  }
159  goto redo;
160  }
161  if (ret != AVERROR(EINPROGRESS) &&
162  ret != AVERROR(EAGAIN))
163  goto fail;
164 
165  /* wait until we are connected or until abort */
166  wait_started = av_gettime();
167  do {
169  ret = AVERROR_EXIT;
170  goto fail1;
171  }
172  ret = poll(&p, 1, 100);
173  if (ret > 0)
174  break;
175  } while (!h->rw_timeout || (av_gettime() - wait_started < h->rw_timeout));
176  if (ret <= 0) {
177  ret = AVERROR(ETIMEDOUT);
178  goto fail;
179  }
180  /* test error */
181  optlen = sizeof(ret);
182  if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen))
183  ret = AVUNERROR(ff_neterrno());
184  if (ret != 0) {
185  char errbuf[100];
186  ret = AVERROR(ret);
187  av_strerror(ret, errbuf, sizeof(errbuf));
188  av_log(h, AV_LOG_ERROR,
189  "TCP connection to %s:%d failed: %s\n",
190  hostname, port, errbuf);
191  goto fail;
192  }
193  }
194  h->is_streamed = 1;
195  s->fd = fd;
196  freeaddrinfo(ai);
197  return 0;
198 
199  fail:
200  if (cur_ai->ai_next) {
201  /* Retry with the next sockaddr */
202  cur_ai = cur_ai->ai_next;
203  if (fd >= 0)
204  closesocket(fd);
205  goto restart;
206  }
207  fail1:
208  if (fd >= 0)
209  closesocket(fd);
210  freeaddrinfo(ai);
211  return ret;
212 }
213 
214 static int tcp_read(URLContext *h, uint8_t *buf, int size)
215 {
216  TCPContext *s = h->priv_data;
217  int ret;
218 
219  if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
221  if (ret)
222  return ret;
223  }
224  ret = recv(s->fd, buf, size, 0);
225  return ret < 0 ? ff_neterrno() : ret;
226 }
227 
228 static int tcp_write(URLContext *h, const uint8_t *buf, int size)
229 {
230  TCPContext *s = h->priv_data;
231  int ret;
232 
233  if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
235  if (ret)
236  return ret;
237  }
238  ret = send(s->fd, buf, size, 0);
239  return ret < 0 ? ff_neterrno() : ret;
240 }
241 
242 static int tcp_shutdown(URLContext *h, int flags)
243 {
244  TCPContext *s = h->priv_data;
245  int how;
246 
247  if (flags & AVIO_FLAG_WRITE && flags & AVIO_FLAG_READ) {
248  how = SHUT_RDWR;
249  } else if (flags & AVIO_FLAG_WRITE) {
250  how = SHUT_WR;
251  } else {
252  how = SHUT_RD;
253  }
254 
255  return shutdown(s->fd, how);
256 }
257 
258 static int tcp_close(URLContext *h)
259 {
260  TCPContext *s = h->priv_data;
261  closesocket(s->fd);
262  return 0;
263 }
264 
266 {
267  TCPContext *s = h->priv_data;
268  return s->fd;
269 }
270 
272  .name = "tcp",
273  .url_open = tcp_open,
274  .url_read = tcp_read,
275  .url_write = tcp_write,
276  .url_close = tcp_close,
277  .url_get_file_handle = tcp_get_file_handle,
278  .url_shutdown = tcp_shutdown,
279  .priv_data_size = sizeof(TCPContext),
280  .priv_data_class = &tcp_context_class,
282 };