Commit | Line | Data |
---|---|---|
171bbb03 FB |
1 | /* |
2 | * TCP protocol | |
406792e7 | 3 | * Copyright (c) 2002 Fabrice Bellard |
171bbb03 | 4 | * |
2912e87a | 5 | * This file is part of Libav. |
b78e7197 | 6 | * |
2912e87a | 7 | * Libav is free software; you can redistribute it and/or |
171bbb03 FB |
8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | |
b78e7197 | 10 | * version 2.1 of the License, or (at your option) any later version. |
171bbb03 | 11 | * |
2912e87a | 12 | * Libav is distributed in the hope that it will be useful, |
171bbb03 FB |
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 | |
2912e87a | 18 | * License along with Libav; if not, write to the Free Software |
5509bffa | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
171bbb03 FB |
20 | */ |
21 | #include "avformat.h" | |
3d42d493 | 22 | #include "libavutil/parseutils.h" |
e4a9e3cc | 23 | #include "internal.h" |
42572ef5 | 24 | #include "network.h" |
087b3272 | 25 | #include "os_support.h" |
5cec8971 | 26 | #include "url.h" |
a8475bbd LB |
27 | #if HAVE_POLL_H |
28 | #include <poll.h> | |
6ad1c9c9 | 29 | #endif |
171bbb03 FB |
30 | |
31 | typedef struct TCPContext { | |
32 | int fd; | |
33 | } TCPContext; | |
34 | ||
171bbb03 FB |
35 | /* return non zero if error */ |
36 | static int tcp_open(URLContext *h, const char *uri, int flags) | |
37 | { | |
a92be9b8 | 38 | struct addrinfo hints = { 0 }, *ai, *cur_ai; |
171bbb03 | 39 | int port, fd = -1; |
7e580505 | 40 | TCPContext *s = h->priv_data; |
3d42d493 LB |
41 | int listen_socket = 0; |
42 | const char *p; | |
43 | char buf[256]; | |
a8475bbd | 44 | int ret; |
09787fb8 | 45 | socklen_t optlen; |
ebb6b27a | 46 | int timeout = 100; |
f23a9759 | 47 | char hostname[1024],proto[1024],path[1024]; |
fdcdd539 | 48 | char portstr[10]; |
6ba5cbc6 | 49 | |
f3bfe388 | 50 | av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), |
a3303add RB |
51 | &port, path, sizeof(path), uri); |
52 | if (strcmp(proto,"tcp") || port <= 0 || port >= 65536) | |
8b9af28d | 53 | return AVERROR(EINVAL); |
115329f1 | 54 | |
3d42d493 LB |
55 | p = strchr(uri, '?'); |
56 | if (p) { | |
57 | if (av_find_info_tag(buf, sizeof(buf), "listen", p)) | |
58 | listen_socket = 1; | |
ebb6b27a LB |
59 | if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) { |
60 | timeout = strtol(buf, NULL, 10); | |
61 | } | |
3d42d493 | 62 | } |
88248b76 | 63 | hints.ai_family = AF_UNSPEC; |
fdcdd539 MS |
64 | hints.ai_socktype = SOCK_STREAM; |
65 | snprintf(portstr, sizeof(portstr), "%d", port); | |
58f3e09e JO |
66 | if (listen_socket) |
67 | hints.ai_flags |= AI_PASSIVE; | |
ef882e46 JO |
68 | if (!hostname[0]) |
69 | ret = getaddrinfo(NULL, portstr, &hints, &ai); | |
70 | else | |
71 | ret = getaddrinfo(hostname, portstr, &hints, &ai); | |
63638a3c | 72 | if (ret) { |
c60112f2 | 73 | av_log(h, AV_LOG_ERROR, |
63638a3c RB |
74 | "Failed to resolve hostname %s: %s\n", |
75 | hostname, gai_strerror(ret)); | |
8b9af28d | 76 | return AVERROR(EIO); |
63638a3c | 77 | } |
171bbb03 | 78 | |
fdcdd539 MS |
79 | cur_ai = ai; |
80 | ||
81 | restart: | |
ebb6b27a | 82 | ret = AVERROR(EIO); |
fdcdd539 | 83 | fd = socket(cur_ai->ai_family, cur_ai->ai_socktype, cur_ai->ai_protocol); |
171bbb03 | 84 | if (fd < 0) |
fdcdd539 | 85 | goto fail; |
115329f1 | 86 | |
3d42d493 LB |
87 | if (listen_socket) { |
88 | int fd1; | |
b7c3772b MS |
89 | int reuse = 1; |
90 | setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); | |
3d42d493 | 91 | ret = bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen); |
641f4a88 MS |
92 | if (ret) { |
93 | ret = ff_neterrno(); | |
94 | goto fail1; | |
95 | } | |
a7cc78cb JO |
96 | ret = listen(fd, 1); |
97 | if (ret) { | |
98 | ret = ff_neterrno(); | |
99 | goto fail1; | |
100 | } | |
3d42d493 | 101 | fd1 = accept(fd, NULL, NULL); |
641f4a88 MS |
102 | if (fd1 < 0) { |
103 | ret = ff_neterrno(); | |
104 | goto fail1; | |
105 | } | |
3d42d493 LB |
106 | closesocket(fd); |
107 | fd = fd1; | |
ebb6b27a | 108 | ff_socket_nonblock(fd, 1); |
3d42d493 | 109 | } else { |
09787fb8 | 110 | redo: |
ebb6b27a | 111 | ff_socket_nonblock(fd, 1); |
3d42d493 LB |
112 | ret = connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen); |
113 | } | |
114 | ||
09787fb8 | 115 | if (ret < 0) { |
a8475bbd | 116 | struct pollfd p = {fd, POLLOUT, 0}; |
ebb6b27a LB |
117 | ret = ff_neterrno(); |
118 | if (ret == AVERROR(EINTR)) { | |
9957cdbf | 119 | if (ff_check_interrupt(&h->interrupt_callback)) { |
c76374c6 | 120 | ret = AVERROR_EXIT; |
1aa58c64 | 121 | goto fail1; |
c76374c6 | 122 | } |
09787fb8 | 123 | goto redo; |
1aa58c64 | 124 | } |
ebb6b27a LB |
125 | if (ret != AVERROR(EINPROGRESS) && |
126 | ret != AVERROR(EAGAIN)) | |
09787fb8 | 127 | goto fail; |
171bbb03 | 128 | |
09787fb8 | 129 | /* wait until we are connected or until abort */ |
ebb6b27a | 130 | while(timeout--) { |
9957cdbf | 131 | if (ff_check_interrupt(&h->interrupt_callback)) { |
c76374c6 | 132 | ret = AVERROR_EXIT; |
09787fb8 FB |
133 | goto fail1; |
134 | } | |
a8475bbd LB |
135 | ret = poll(&p, 1, 100); |
136 | if (ret > 0) | |
09787fb8 FB |
137 | break; |
138 | } | |
ebb6b27a LB |
139 | if (ret <= 0) { |
140 | ret = AVERROR(ETIMEDOUT); | |
141 | goto fail; | |
142 | } | |
09787fb8 FB |
143 | /* test error */ |
144 | optlen = sizeof(ret); | |
bb6c1abb MS |
145 | if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen)) |
146 | ret = AVUNERROR(ff_neterrno()); | |
63638a3c | 147 | if (ret != 0) { |
a840cdda MS |
148 | char errbuf[100]; |
149 | ret = AVERROR(ret); | |
150 | av_strerror(ret, errbuf, sizeof(errbuf)); | |
c60112f2 | 151 | av_log(h, AV_LOG_ERROR, |
63638a3c | 152 | "TCP connection to %s:%d failed: %s\n", |
a840cdda | 153 | hostname, port, errbuf); |
09787fb8 | 154 | goto fail; |
63638a3c | 155 | } |
09787fb8 | 156 | } |
47f944a2 | 157 | h->is_streamed = 1; |
171bbb03 | 158 | s->fd = fd; |
fdcdd539 | 159 | freeaddrinfo(ai); |
171bbb03 FB |
160 | return 0; |
161 | ||
162 | fail: | |
fdcdd539 MS |
163 | if (cur_ai->ai_next) { |
164 | /* Retry with the next sockaddr */ | |
165 | cur_ai = cur_ai->ai_next; | |
166 | if (fd >= 0) | |
167 | closesocket(fd); | |
168 | goto restart; | |
169 | } | |
09787fb8 | 170 | fail1: |
171bbb03 | 171 | if (fd >= 0) |
e9d511dc | 172 | closesocket(fd); |
fdcdd539 | 173 | freeaddrinfo(ai); |
09787fb8 | 174 | return ret; |
171bbb03 FB |
175 | } |
176 | ||
0c1a9eda | 177 | static int tcp_read(URLContext *h, uint8_t *buf, int size) |
171bbb03 FB |
178 | { |
179 | TCPContext *s = h->priv_data; | |
ad3cffb6 | 180 | int ret; |
171bbb03 | 181 | |
f87b1b37 | 182 | if (!(h->flags & AVIO_FLAG_NONBLOCK)) { |
ebba2b3e | 183 | ret = ff_network_wait_fd(s->fd, 0); |
ad3cffb6 | 184 | if (ret < 0) |
51b317d2 | 185 | return ret; |
171bbb03 | 186 | } |
ad3cffb6 NG |
187 | ret = recv(s->fd, buf, size, 0); |
188 | return ret < 0 ? ff_neterrno() : ret; | |
171bbb03 FB |
189 | } |
190 | ||
27241cbf | 191 | static int tcp_write(URLContext *h, const uint8_t *buf, int size) |
171bbb03 FB |
192 | { |
193 | TCPContext *s = h->priv_data; | |
ad3cffb6 | 194 | int ret; |
171bbb03 | 195 | |
f87b1b37 | 196 | if (!(h->flags & AVIO_FLAG_NONBLOCK)) { |
ebba2b3e | 197 | ret = ff_network_wait_fd(s->fd, 1); |
ad3cffb6 | 198 | if (ret < 0) |
51b317d2 | 199 | return ret; |
171bbb03 | 200 | } |
ad3cffb6 NG |
201 | ret = send(s->fd, buf, size, 0); |
202 | return ret < 0 ? ff_neterrno() : ret; | |
171bbb03 FB |
203 | } |
204 | ||
4a9ca935 SP |
205 | static int tcp_shutdown(URLContext *h, int flags) |
206 | { | |
207 | TCPContext *s = h->priv_data; | |
208 | int how; | |
209 | ||
210 | if (flags & AVIO_FLAG_WRITE && flags & AVIO_FLAG_READ) { | |
211 | how = SHUT_RDWR; | |
212 | } else if (flags & AVIO_FLAG_WRITE) { | |
213 | how = SHUT_WR; | |
214 | } else { | |
215 | how = SHUT_RD; | |
216 | } | |
217 | ||
218 | return shutdown(s->fd, how); | |
219 | } | |
220 | ||
171bbb03 FB |
221 | static int tcp_close(URLContext *h) |
222 | { | |
223 | TCPContext *s = h->priv_data; | |
9ddd71fc | 224 | closesocket(s->fd); |
171bbb03 FB |
225 | return 0; |
226 | } | |
227 | ||
f0a80394 RB |
228 | static int tcp_get_file_handle(URLContext *h) |
229 | { | |
230 | TCPContext *s = h->priv_data; | |
231 | return s->fd; | |
232 | } | |
233 | ||
c6610a21 | 234 | URLProtocol ff_tcp_protocol = { |
f35ff97f AK |
235 | .name = "tcp", |
236 | .url_open = tcp_open, | |
237 | .url_read = tcp_read, | |
238 | .url_write = tcp_write, | |
239 | .url_close = tcp_close, | |
f0a80394 | 240 | .url_get_file_handle = tcp_get_file_handle, |
4a9ca935 | 241 | .url_shutdown = tcp_shutdown, |
7e580505 | 242 | .priv_data_size = sizeof(TCPContext), |
32b83aee | 243 | .flags = URL_PROTOCOL_FLAG_NETWORK, |
171bbb03 | 244 | }; |