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