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 Libav.
6  *
7  * Libav 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  * Libav 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 Libav; 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 <unistd.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 #include <sys/time.h>
32 
33 typedef struct TCPContext {
34  int fd;
35 } TCPContext;
36 
37 /* return non zero if error */
38 static int tcp_open(URLContext *h, const char *uri, int flags)
39 {
40  struct addrinfo hints, *ai, *cur_ai;
41  int port, fd = -1;
42  TCPContext *s = h->priv_data;
43  int listen_socket = 0;
44  const char *p;
45  char buf[256];
46  int ret;
47  socklen_t optlen;
48  int timeout = 100;
49  char hostname[1024],proto[1024],path[1024];
50  char portstr[10];
51 
52  av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname),
53  &port, path, sizeof(path), uri);
54  if (strcmp(proto,"tcp") || port <= 0 || port >= 65536)
55  return AVERROR(EINVAL);
56 
57  p = strchr(uri, '?');
58  if (p) {
59  if (av_find_info_tag(buf, sizeof(buf), "listen", p))
60  listen_socket = 1;
61  if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) {
62  timeout = strtol(buf, NULL, 10);
63  }
64  }
65  memset(&hints, 0, sizeof(hints));
66  hints.ai_family = AF_UNSPEC;
67  hints.ai_socktype = SOCK_STREAM;
68  snprintf(portstr, sizeof(portstr), "%d", port);
69  ret = getaddrinfo(hostname, portstr, &hints, &ai);
70  if (ret) {
72  "Failed to resolve hostname %s: %s\n",
73  hostname, gai_strerror(ret));
74  return AVERROR(EIO);
75  }
76 
77  cur_ai = ai;
78 
79  restart:
80  ret = AVERROR(EIO);
81  fd = socket(cur_ai->ai_family, cur_ai->ai_socktype, cur_ai->ai_protocol);
82  if (fd < 0)
83  goto fail;
84 
85  if (listen_socket) {
86  int fd1;
87  ret = bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen);
88  listen(fd, 1);
89  fd1 = accept(fd, NULL, NULL);
90  closesocket(fd);
91  fd = fd1;
92  ff_socket_nonblock(fd, 1);
93  } else {
94  redo:
95  ff_socket_nonblock(fd, 1);
96  ret = connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen);
97  }
98 
99  if (ret < 0) {
100  struct pollfd p = {fd, POLLOUT, 0};
101  ret = ff_neterrno();
102  if (ret == AVERROR(EINTR)) {
104  ret = AVERROR_EXIT;
105  goto fail1;
106  }
107  goto redo;
108  }
109  if (ret != AVERROR(EINPROGRESS) &&
110  ret != AVERROR(EAGAIN))
111  goto fail;
112 
113  /* wait until we are connected or until abort */
114  while(timeout--) {
116  ret = AVERROR_EXIT;
117  goto fail1;
118  }
119  ret = poll(&p, 1, 100);
120  if (ret > 0)
121  break;
122  }
123  if (ret <= 0) {
124  ret = AVERROR(ETIMEDOUT);
125  goto fail;
126  }
127  /* test error */
128  optlen = sizeof(ret);
129  getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen);
130  if (ret != 0) {
131  av_log(h, AV_LOG_ERROR,
132  "TCP connection to %s:%d failed: %s\n",
133  hostname, port, strerror(ret));
134  ret = AVERROR(ret);
135  goto fail;
136  }
137  }
138  h->is_streamed = 1;
139  s->fd = fd;
140  freeaddrinfo(ai);
141  return 0;
142 
143  fail:
144  if (cur_ai->ai_next) {
145  /* Retry with the next sockaddr */
146  cur_ai = cur_ai->ai_next;
147  if (fd >= 0)
148  closesocket(fd);
149  goto restart;
150  }
151  fail1:
152  if (fd >= 0)
153  closesocket(fd);
154  freeaddrinfo(ai);
155  return ret;
156 }
157 
158 static int tcp_read(URLContext *h, uint8_t *buf, int size)
159 {
160  TCPContext *s = h->priv_data;
161  int ret;
162 
163  if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
164  ret = ff_network_wait_fd(s->fd, 0);
165  if (ret < 0)
166  return ret;
167  }
168  ret = recv(s->fd, buf, size, 0);
169  return ret < 0 ? ff_neterrno() : ret;
170 }
171 
172 static int tcp_write(URLContext *h, const uint8_t *buf, int size)
173 {
174  TCPContext *s = h->priv_data;
175  int ret;
176 
177  if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
178  ret = ff_network_wait_fd(s->fd, 1);
179  if (ret < 0)
180  return ret;
181  }
182  ret = send(s->fd, buf, size, 0);
183  return ret < 0 ? ff_neterrno() : ret;
184 }
185 
186 static int tcp_close(URLContext *h)
187 {
188  TCPContext *s = h->priv_data;
189  closesocket(s->fd);
190  return 0;
191 }
192 
194 {
195  TCPContext *s = h->priv_data;
196  return s->fd;
197 }
198 
200  .name = "tcp",
201  .url_open = tcp_open,
202  .url_read = tcp_read,
203  .url_write = tcp_write,
204  .url_close = tcp_close,
205  .url_get_file_handle = tcp_get_file_handle,
206  .priv_data_size = sizeof(TCPContext),
208 };