/src/ffmpeg/libavformat/network.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2007 The FFmpeg Project |
3 | | * |
4 | | * This file is part of FFmpeg. |
5 | | * |
6 | | * FFmpeg is free software; you can redistribute it and/or |
7 | | * modify it under the terms of the GNU Lesser General Public |
8 | | * License as published by the Free Software Foundation; either |
9 | | * version 2.1 of the License, or (at your option) any later version. |
10 | | * |
11 | | * FFmpeg is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | | * Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General Public |
17 | | * License along with FFmpeg; if not, write to the Free Software |
18 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
19 | | */ |
20 | | |
21 | | #include <string.h> |
22 | | |
23 | | #include "config.h" |
24 | | #include "config_components.h" |
25 | | #include "libavutil/attributes.h" |
26 | | |
27 | | #if CONFIG_TLS_PROTOCOL && CONFIG_OPENSSL |
28 | | #include <openssl/opensslv.h> |
29 | | #endif |
30 | | |
31 | | #include <fcntl.h> |
32 | | #if HAVE_EVENTFD |
33 | | #include <sys/eventfd.h> |
34 | | #endif |
35 | | #include "network.h" |
36 | | #include "tls.h" |
37 | | #include "url.h" |
38 | | #include "libavutil/avassert.h" |
39 | | #include "libavutil/mem.h" |
40 | | #include "libavutil/time.h" |
41 | | |
42 | | int ff_tls_init(void) |
43 | 0 | { |
44 | | #if CONFIG_TLS_PROTOCOL |
45 | | #if CONFIG_GNUTLS |
46 | | ff_gnutls_init(); |
47 | | #endif |
48 | | #endif |
49 | 0 | return 0; |
50 | 0 | } |
51 | | |
52 | | void ff_tls_deinit(void) |
53 | 0 | { |
54 | | #if CONFIG_TLS_PROTOCOL |
55 | | #if CONFIG_GNUTLS |
56 | | ff_gnutls_deinit(); |
57 | | #endif |
58 | | #endif |
59 | 0 | } |
60 | | |
61 | | /** |
62 | | * Initialize the network subsystem. On Windows, this calls WSAStartup(). |
63 | | * |
64 | | * @return 0 on success, a negative AVERROR code on failure. |
65 | | */ |
66 | | int ff_network_init(void) |
67 | 0 | { |
68 | | #if HAVE_WINSOCK2_H |
69 | | WSADATA wsaData; |
70 | | |
71 | | if (WSAStartup(MAKEWORD(1,1), &wsaData)) |
72 | | return AVERROR(EIO); |
73 | | #endif |
74 | 0 | return 0; |
75 | 0 | } |
76 | | |
77 | | int ff_network_wait_fd(int fd, int write) |
78 | 0 | { |
79 | 0 | int ev = write ? POLLOUT : POLLIN; |
80 | 0 | struct pollfd p = { .fd = fd, .events = ev, .revents = 0 }; |
81 | 0 | int ret; |
82 | 0 | ret = poll(&p, 1, POLLING_TIME); |
83 | 0 | return ret < 0 ? ff_neterrno() : p.revents & (ev | POLLERR | POLLHUP) ? 0 : AVERROR(EAGAIN); |
84 | 0 | } |
85 | | |
86 | | int ff_network_wait_fd_timeout(int fd, int write, int64_t timeout, AVIOInterruptCB *int_cb) |
87 | 0 | { |
88 | 0 | int ret; |
89 | 0 | int64_t wait_start = 0; |
90 | |
|
91 | 0 | while (1) { |
92 | 0 | if (ff_check_interrupt(int_cb)) |
93 | 0 | return AVERROR_EXIT; |
94 | 0 | ret = ff_network_wait_fd(fd, write); |
95 | 0 | if (ret != AVERROR(EAGAIN)) |
96 | 0 | return ret; |
97 | 0 | if (timeout > 0) { |
98 | 0 | if (!wait_start) |
99 | 0 | wait_start = av_gettime_relative(); |
100 | 0 | else if (av_gettime_relative() - wait_start > timeout) |
101 | 0 | return AVERROR(ETIMEDOUT); |
102 | 0 | } |
103 | 0 | } |
104 | 0 | } |
105 | | |
106 | | int ff_network_wakeup_open(FFNetWakeup *w) |
107 | 0 | { |
108 | | #if HAVE_WINSOCK2_H |
109 | | /* select() and WSAPoll() only accept sockets, so use a datagram socket |
110 | | * connected to itself: sending a datagram to it makes it readable. |
111 | | */ |
112 | | struct sockaddr_in addr = { .sin_family = AF_INET }; |
113 | | socklen_t len = sizeof(addr); |
114 | | int ret, fd = ff_socket(AF_INET, SOCK_DGRAM, 0, NULL); |
115 | | |
116 | | if (fd < 0) |
117 | | return ff_neterrno(); |
118 | | |
119 | | addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
120 | | if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0 || |
121 | | getsockname(fd, (struct sockaddr *)&addr, &len) < 0 || |
122 | | connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { |
123 | | ret = ff_neterrno(); |
124 | | closesocket(fd); |
125 | | return ret; |
126 | | } |
127 | | |
128 | | w->rfd = w->wfd = fd; |
129 | | #elif HAVE_EVENTFD |
130 | 0 | int fd = eventfd(0, EFD_CLOEXEC); |
131 | |
|
132 | 0 | if (fd < 0) |
133 | 0 | return AVERROR(errno); |
134 | | |
135 | 0 | w->rfd = w->wfd = fd; |
136 | | #else |
137 | | int fds[2]; |
138 | | |
139 | | if (pipe(fds) < 0) |
140 | | return AVERROR(errno); |
141 | | |
142 | | #if HAVE_FCNTL |
143 | | for (int i = 0; i < 2; i++) { |
144 | | if (fcntl(fds[i], F_SETFD, FD_CLOEXEC) < 0) { |
145 | | int ret = AVERROR(errno); |
146 | | closesocket(fds[0]); |
147 | | closesocket(fds[1]); |
148 | | return ret; |
149 | | } |
150 | | } |
151 | | #endif |
152 | | |
153 | | w->rfd = fds[0]; |
154 | | w->wfd = fds[1]; |
155 | | #endif |
156 | |
|
157 | 0 | atomic_init(&w->signaled, 0); |
158 | |
|
159 | 0 | return 0; |
160 | 0 | } |
161 | | |
162 | | int ff_network_wakeup_signal(FFNetWakeup *w) |
163 | 0 | { |
164 | | /* An eventfd only accepts 8-byte writes; a pipe or a socket takes any |
165 | | * payload, so the same value serves every channel type. |
166 | | */ |
167 | 0 | const uint64_t one = 1; |
168 | 0 | int ret; |
169 | | |
170 | | /* wfd is blocking; writing at most once keeps the write from blocking. |
171 | | * The flag publishes no other data, so relaxed ordering is enough. |
172 | | */ |
173 | 0 | if (atomic_exchange_explicit(&w->signaled, 1, memory_order_relaxed)) |
174 | 0 | return 0; |
175 | | |
176 | | #if HAVE_WINSOCK2_H |
177 | | ret = send(w->wfd, (const char *)&one, sizeof(one), 0); |
178 | | #else |
179 | 0 | ret = write(w->wfd, &one, sizeof(one)); |
180 | 0 | #endif |
181 | |
|
182 | 0 | return ret < 0 ? ff_neterrno() : 0; |
183 | 0 | } |
184 | | |
185 | | void ff_network_wakeup_close(FFNetWakeup *w) |
186 | 0 | { |
187 | 0 | if (w->wfd != w->rfd) |
188 | 0 | closesocket(w->wfd); |
189 | 0 | closesocket(w->rfd); |
190 | |
|
191 | 0 | w->rfd = w->wfd = -1; |
192 | 0 | } |
193 | | |
194 | | int ff_network_wait_fd_wakeup(int fd, int write, int timeout_ms, |
195 | | const FFNetWakeup *w) |
196 | 0 | { |
197 | 0 | int ev = write ? POLLOUT : POLLIN; |
198 | 0 | struct pollfd p[2] = { |
199 | 0 | { .fd = w->rfd, .events = POLLIN }, |
200 | 0 | { .fd = fd, .events = ev }, |
201 | 0 | }; |
202 | 0 | int ret; |
203 | | |
204 | | /* Return without polling once signaled. This also ends the wait when |
205 | | * the write in ff_network_wakeup_signal() failed and rfd never became |
206 | | * readable. |
207 | | */ |
208 | 0 | if (atomic_load_explicit(&w->signaled, memory_order_relaxed)) |
209 | 0 | return AVERROR_EXIT; |
210 | | |
211 | 0 | ret = poll(p, 2, timeout_ms); |
212 | 0 | if (ret < 0) |
213 | 0 | return ff_neterrno(); |
214 | | |
215 | 0 | if (p[0].revents & POLLIN) |
216 | 0 | return AVERROR_EXIT; |
217 | | |
218 | 0 | return p[1].revents & (ev | POLLERR | POLLHUP) ? 0 : AVERROR(EAGAIN); |
219 | 0 | } |
220 | | |
221 | | int ff_network_sleep_interruptible(int64_t timeout, AVIOInterruptCB *int_cb) |
222 | 0 | { |
223 | 0 | int64_t wait_start = av_gettime_relative(); |
224 | |
|
225 | 0 | while (1) { |
226 | 0 | int64_t time_left; |
227 | |
|
228 | 0 | if (ff_check_interrupt(int_cb)) |
229 | 0 | return AVERROR_EXIT; |
230 | | |
231 | 0 | time_left = timeout - (av_gettime_relative() - wait_start); |
232 | 0 | if (time_left <= 0) |
233 | 0 | return AVERROR(ETIMEDOUT); |
234 | | |
235 | 0 | av_usleep(FFMIN(time_left, POLLING_TIME * 1000)); |
236 | 0 | } |
237 | 0 | } |
238 | | |
239 | | void ff_network_close(void) |
240 | 0 | { |
241 | | #if HAVE_WINSOCK2_H |
242 | | WSACleanup(); |
243 | | #endif |
244 | 0 | } |
245 | | |
246 | | #if HAVE_WINSOCK2_H |
247 | | int ff_neterrno(void) |
248 | | { |
249 | | int err = WSAGetLastError(); |
250 | | switch (err) { |
251 | | case WSAEWOULDBLOCK: |
252 | | return AVERROR(EAGAIN); |
253 | | case WSAEINTR: |
254 | | return AVERROR(EINTR); |
255 | | case WSAEPROTONOSUPPORT: |
256 | | return AVERROR(EPROTONOSUPPORT); |
257 | | case WSAETIMEDOUT: |
258 | | return AVERROR(ETIMEDOUT); |
259 | | case WSAECONNREFUSED: |
260 | | return AVERROR(ECONNREFUSED); |
261 | | case WSAEINPROGRESS: |
262 | | return AVERROR(EINPROGRESS); |
263 | | } |
264 | | return -err; |
265 | | } |
266 | | #endif |
267 | | |
268 | | int ff_is_multicast_address(struct sockaddr *addr) |
269 | 0 | { |
270 | 0 | if (addr->sa_family == AF_INET) { |
271 | 0 | return IN_MULTICAST(ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr)); |
272 | 0 | } |
273 | 0 | #if HAVE_STRUCT_SOCKADDR_IN6 |
274 | 0 | if (addr->sa_family == AF_INET6) { |
275 | 0 | return IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)addr)->sin6_addr); |
276 | 0 | } |
277 | 0 | #endif |
278 | | |
279 | 0 | return 0; |
280 | 0 | } |
281 | | |
282 | | static int ff_poll_interrupt(struct pollfd *p, nfds_t nfds, int timeout, |
283 | | AVIOInterruptCB *cb) |
284 | 0 | { |
285 | 0 | int runs = timeout / POLLING_TIME; |
286 | 0 | int ret = 0; |
287 | |
|
288 | 0 | do { |
289 | 0 | if (ff_check_interrupt(cb)) |
290 | 0 | return AVERROR_EXIT; |
291 | 0 | ret = poll(p, nfds, POLLING_TIME); |
292 | 0 | if (ret != 0) { |
293 | 0 | if (ret < 0) |
294 | 0 | ret = ff_neterrno(); |
295 | 0 | if (ret == AVERROR(EINTR)) |
296 | 0 | continue; |
297 | 0 | break; |
298 | 0 | } |
299 | 0 | } while (timeout <= 0 || runs-- > 0); |
300 | | |
301 | 0 | if (!ret) |
302 | 0 | return AVERROR(ETIMEDOUT); |
303 | 0 | return ret; |
304 | 0 | } |
305 | | |
306 | | int ff_socket(int af, int type, int proto, void *logctx) |
307 | 0 | { |
308 | 0 | int fd; |
309 | |
|
310 | 0 | #ifdef SOCK_CLOEXEC |
311 | 0 | fd = socket(af, type | SOCK_CLOEXEC, proto); |
312 | 0 | if (fd == -1 && errno == EINVAL) |
313 | 0 | #endif |
314 | 0 | { |
315 | 0 | fd = socket(af, type, proto); |
316 | 0 | #if HAVE_FCNTL |
317 | 0 | if (fd != -1) { |
318 | 0 | if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) |
319 | 0 | av_log(logctx, AV_LOG_DEBUG, "Failed to set close on exec\n"); |
320 | 0 | } |
321 | 0 | #endif |
322 | 0 | } |
323 | | #ifdef SO_NOSIGPIPE |
324 | | if (fd != -1) { |
325 | | if (setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &(int){1}, sizeof(int))) { |
326 | | av_log(logctx, AV_LOG_WARNING, "setsockopt(SO_NOSIGPIPE) failed\n"); |
327 | | } |
328 | | } |
329 | | #endif |
330 | 0 | return fd; |
331 | 0 | } |
332 | | |
333 | | int ff_listen(int fd, const struct sockaddr *addr, |
334 | | socklen_t addrlen, void *logctx) |
335 | 0 | { |
336 | 0 | int ret; |
337 | 0 | int reuse = 1; |
338 | 0 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))) { |
339 | 0 | av_log(logctx, AV_LOG_WARNING, "setsockopt(SO_REUSEADDR) failed\n"); |
340 | 0 | } |
341 | 0 | ret = bind(fd, addr, addrlen); |
342 | 0 | if (ret) |
343 | 0 | return ff_neterrno(); |
344 | | |
345 | 0 | ret = listen(fd, 1); |
346 | 0 | if (ret) |
347 | 0 | return ff_neterrno(); |
348 | 0 | return ret; |
349 | 0 | } |
350 | | |
351 | | int ff_accept(int fd, int timeout, URLContext *h) |
352 | 0 | { |
353 | 0 | int ret; |
354 | 0 | struct pollfd lp = { fd, POLLIN, 0 }; |
355 | |
|
356 | 0 | ret = ff_poll_interrupt(&lp, 1, timeout, &h->interrupt_callback); |
357 | 0 | if (ret < 0) |
358 | 0 | return ret; |
359 | | |
360 | 0 | ret = accept(fd, NULL, NULL); |
361 | 0 | if (ret < 0) |
362 | 0 | return ff_neterrno(); |
363 | 0 | if (ff_socket_nonblock(ret, 1) < 0) |
364 | 0 | av_log(h, AV_LOG_DEBUG, "ff_socket_nonblock failed\n"); |
365 | |
|
366 | 0 | return ret; |
367 | 0 | } |
368 | | |
369 | | int ff_listen_bind(int fd, const struct sockaddr *addr, |
370 | | socklen_t addrlen, int timeout, URLContext *h) |
371 | 0 | { |
372 | 0 | int ret; |
373 | 0 | if ((ret = ff_listen(fd, addr, addrlen, h)) < 0) |
374 | 0 | return ret; |
375 | 0 | if ((ret = ff_accept(fd, timeout, h)) < 0) |
376 | 0 | return ret; |
377 | 0 | closesocket(fd); |
378 | 0 | return ret; |
379 | 0 | } |
380 | | |
381 | | int ff_listen_connect(int fd, const struct sockaddr *addr, |
382 | | socklen_t addrlen, int timeout, URLContext *h, |
383 | | int will_try_next) |
384 | 0 | { |
385 | 0 | struct pollfd p = {fd, POLLOUT, 0}; |
386 | 0 | int ret; |
387 | 0 | socklen_t optlen; |
388 | |
|
389 | 0 | if (ff_socket_nonblock(fd, 1) < 0) |
390 | 0 | av_log(h, AV_LOG_DEBUG, "ff_socket_nonblock failed\n"); |
391 | |
|
392 | 0 | while ((ret = connect(fd, addr, addrlen))) { |
393 | 0 | ret = ff_neterrno(); |
394 | 0 | switch (ret) { |
395 | 0 | case AVERROR(EINTR): |
396 | 0 | if (ff_check_interrupt(&h->interrupt_callback)) |
397 | 0 | return AVERROR_EXIT; |
398 | 0 | continue; |
399 | 0 | case AVERROR(EINPROGRESS): |
400 | 0 | case AVERROR(EAGAIN): |
401 | 0 | ret = ff_poll_interrupt(&p, 1, timeout, &h->interrupt_callback); |
402 | 0 | if (ret < 0) |
403 | 0 | return ret; |
404 | 0 | optlen = sizeof(ret); |
405 | 0 | if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen)) |
406 | 0 | ret = AVUNERROR(ff_neterrno()); |
407 | 0 | if (ret != 0) { |
408 | 0 | ret = AVERROR(ret); |
409 | 0 | if (will_try_next) |
410 | 0 | av_log(h, AV_LOG_WARNING, |
411 | 0 | "Connection to %s failed (%s), trying next address\n", |
412 | 0 | h->filename, av_err2str(ret)); |
413 | 0 | else |
414 | 0 | av_log(h, AV_LOG_ERROR, "Connection to %s failed: %s\n", |
415 | 0 | h->filename, av_err2str(ret)); |
416 | 0 | } |
417 | 0 | av_fallthrough; |
418 | 0 | default: |
419 | 0 | return ret; |
420 | 0 | } |
421 | 0 | } |
422 | 0 | return ret; |
423 | 0 | } |
424 | | |
425 | | static void interleave_addrinfo(struct addrinfo *base) |
426 | 0 | { |
427 | 0 | struct addrinfo **next = &base->ai_next; |
428 | 0 | while (*next) { |
429 | 0 | struct addrinfo *cur = *next; |
430 | | // Iterate forward until we find an entry of a different family. |
431 | 0 | if (cur->ai_family == base->ai_family) { |
432 | 0 | next = &cur->ai_next; |
433 | 0 | continue; |
434 | 0 | } |
435 | 0 | if (cur == base->ai_next) { |
436 | | // If the first one following base is of a different family, just |
437 | | // move base forward one step and continue. |
438 | 0 | base = cur; |
439 | 0 | next = &base->ai_next; |
440 | 0 | continue; |
441 | 0 | } |
442 | | // Unchain cur from the rest of the list from its current spot. |
443 | 0 | *next = cur->ai_next; |
444 | | // Hook in cur directly after base. |
445 | 0 | cur->ai_next = base->ai_next; |
446 | 0 | base->ai_next = cur; |
447 | | // Restart with a new base. We know that before moving the cur element, |
448 | | // everything between the previous base and cur had the same family, |
449 | | // different from cur->ai_family. Therefore, we can keep next pointing |
450 | | // where it was, and continue from there with base at the one after |
451 | | // cur. |
452 | 0 | base = cur->ai_next; |
453 | 0 | } |
454 | 0 | } |
455 | | |
456 | | static void print_address_list(void *ctx, const struct addrinfo *addr, |
457 | | const char *title) |
458 | 0 | { |
459 | 0 | char hostbuf[100], portbuf[20]; |
460 | 0 | av_log(ctx, AV_LOG_DEBUG, "%s:\n", title); |
461 | 0 | while (addr) { |
462 | 0 | getnameinfo(addr->ai_addr, addr->ai_addrlen, |
463 | 0 | hostbuf, sizeof(hostbuf), portbuf, sizeof(portbuf), |
464 | 0 | NI_NUMERICHOST | NI_NUMERICSERV); |
465 | 0 | av_log(ctx, AV_LOG_DEBUG, "Address %s port %s\n", hostbuf, portbuf); |
466 | 0 | addr = addr->ai_next; |
467 | 0 | } |
468 | 0 | } |
469 | | |
470 | | struct ConnectionAttempt { |
471 | | int fd; |
472 | | int64_t deadline_us; |
473 | | struct addrinfo *addr; |
474 | | }; |
475 | | |
476 | | // Returns < 0 on error, 0 on successfully started connection attempt, |
477 | | // > 0 for a connection that succeeded already. |
478 | | static int start_connect_attempt(struct ConnectionAttempt *attempt, |
479 | | struct addrinfo **ptr, int timeout_ms, |
480 | | URLContext *h, |
481 | | int (*customize_fd)(void *, int, int), void *customize_ctx) |
482 | 0 | { |
483 | 0 | struct addrinfo *ai = *ptr; |
484 | 0 | int ret; |
485 | |
|
486 | 0 | *ptr = ai->ai_next; |
487 | |
|
488 | 0 | attempt->fd = ff_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol, h); |
489 | 0 | if (attempt->fd < 0) |
490 | 0 | return ff_neterrno(); |
491 | 0 | attempt->deadline_us = av_gettime_relative() + timeout_ms * 1000; |
492 | 0 | attempt->addr = ai; |
493 | |
|
494 | 0 | ff_socket_nonblock(attempt->fd, 1); |
495 | |
|
496 | 0 | if (customize_fd) { |
497 | 0 | ret = customize_fd(customize_ctx, attempt->fd, ai->ai_family); |
498 | 0 | if (ret) { |
499 | 0 | closesocket(attempt->fd); |
500 | 0 | attempt->fd = -1; |
501 | 0 | return ret; |
502 | 0 | } |
503 | 0 | } |
504 | | |
505 | 0 | while ((ret = connect(attempt->fd, ai->ai_addr, ai->ai_addrlen))) { |
506 | 0 | ret = ff_neterrno(); |
507 | 0 | switch (ret) { |
508 | 0 | case AVERROR(EINTR): |
509 | 0 | if (ff_check_interrupt(&h->interrupt_callback)) { |
510 | 0 | closesocket(attempt->fd); |
511 | 0 | attempt->fd = -1; |
512 | 0 | return AVERROR_EXIT; |
513 | 0 | } |
514 | 0 | continue; |
515 | 0 | case AVERROR(EINPROGRESS): |
516 | 0 | case AVERROR(EAGAIN): |
517 | 0 | return 0; |
518 | 0 | default: |
519 | 0 | closesocket(attempt->fd); |
520 | 0 | attempt->fd = -1; |
521 | 0 | return ret; |
522 | 0 | } |
523 | 0 | } |
524 | 0 | return 1; |
525 | 0 | } |
526 | | |
527 | | // Try a new connection to another address after 200 ms, as suggested in |
528 | | // RFC 8305 (or sooner if an earlier attempt fails). |
529 | 0 | #define NEXT_ATTEMPT_DELAY_MS 200 |
530 | | |
531 | | int ff_connect_parallel(struct addrinfo *addrs, int timeout_ms_per_address, |
532 | | int parallel, URLContext *h, int *fd, |
533 | | int (*customize_fd)(void *, int, int), void *customize_ctx) |
534 | 0 | { |
535 | 0 | struct ConnectionAttempt attempts[3]; |
536 | 0 | struct pollfd pfd[3]; |
537 | 0 | int nb_attempts = 0, i, j; |
538 | 0 | int64_t next_attempt_us = av_gettime_relative(), next_deadline_us; |
539 | 0 | int last_err = AVERROR(EIO); |
540 | 0 | socklen_t optlen; |
541 | 0 | char hostbuf[100], portbuf[20]; |
542 | |
|
543 | 0 | if (parallel > FF_ARRAY_ELEMS(attempts)) |
544 | 0 | parallel = FF_ARRAY_ELEMS(attempts); |
545 | |
|
546 | 0 | print_address_list(h, addrs, "Original list of addresses"); |
547 | | // This mutates the list, but the head of the list is still the same |
548 | | // element, so the caller, who owns the list, doesn't need to get |
549 | | // an updated pointer. |
550 | 0 | interleave_addrinfo(addrs); |
551 | 0 | print_address_list(h, addrs, "Interleaved list of addresses"); |
552 | |
|
553 | 0 | while (nb_attempts > 0 || addrs) { |
554 | | // Start a new connection attempt, if possible. |
555 | 0 | if (nb_attempts < parallel && addrs) { |
556 | 0 | getnameinfo(addrs->ai_addr, addrs->ai_addrlen, |
557 | 0 | hostbuf, sizeof(hostbuf), portbuf, sizeof(portbuf), |
558 | 0 | NI_NUMERICHOST | NI_NUMERICSERV); |
559 | 0 | av_log(h, AV_LOG_VERBOSE, "Starting connection attempt to %s port %s\n", |
560 | 0 | hostbuf, portbuf); |
561 | 0 | last_err = start_connect_attempt(&attempts[nb_attempts], &addrs, |
562 | 0 | timeout_ms_per_address, h, |
563 | 0 | customize_fd, customize_ctx); |
564 | 0 | if (last_err < 0) { |
565 | 0 | av_log(h, AV_LOG_VERBOSE, "Connected attempt failed: %s\n", |
566 | 0 | av_err2str(last_err)); |
567 | 0 | continue; |
568 | 0 | } |
569 | 0 | if (last_err > 0) { |
570 | 0 | for (i = 0; i < nb_attempts; i++) |
571 | 0 | closesocket(attempts[i].fd); |
572 | 0 | *fd = attempts[nb_attempts].fd; |
573 | 0 | return 0; |
574 | 0 | } |
575 | 0 | pfd[nb_attempts].fd = attempts[nb_attempts].fd; |
576 | 0 | pfd[nb_attempts].events = POLLOUT; |
577 | 0 | next_attempt_us = av_gettime_relative() + NEXT_ATTEMPT_DELAY_MS * 1000; |
578 | 0 | nb_attempts++; |
579 | 0 | } |
580 | | |
581 | 0 | av_assert0(nb_attempts > 0); |
582 | | // The connection attempts are sorted from oldest to newest, so the |
583 | | // first one will have the earliest deadline. |
584 | 0 | next_deadline_us = attempts[0].deadline_us; |
585 | | // If we can start another attempt in parallel, wait until that time. |
586 | 0 | if (nb_attempts < parallel && addrs) |
587 | 0 | next_deadline_us = FFMIN(next_deadline_us, next_attempt_us); |
588 | 0 | last_err = ff_poll_interrupt(pfd, nb_attempts, |
589 | 0 | (next_deadline_us - av_gettime_relative())/1000, |
590 | 0 | &h->interrupt_callback); |
591 | 0 | if (last_err < 0 && last_err != AVERROR(ETIMEDOUT)) |
592 | 0 | break; |
593 | | |
594 | | // Check the status from the poll output. |
595 | 0 | for (i = 0; i < nb_attempts; i++) { |
596 | 0 | last_err = 0; |
597 | 0 | if (pfd[i].revents) { |
598 | | // Some sort of action for this socket, check its status (either |
599 | | // a successful connection or an error). |
600 | 0 | optlen = sizeof(last_err); |
601 | 0 | if (getsockopt(attempts[i].fd, SOL_SOCKET, SO_ERROR, &last_err, &optlen)) |
602 | 0 | last_err = ff_neterrno(); |
603 | 0 | else if (last_err != 0) |
604 | 0 | last_err = AVERROR(last_err); |
605 | 0 | if (last_err == 0) { |
606 | | // Everything is ok, we seem to have a successful |
607 | | // connection. Close other sockets and return this one. |
608 | 0 | for (j = 0; j < nb_attempts; j++) |
609 | 0 | if (j != i) |
610 | 0 | closesocket(attempts[j].fd); |
611 | 0 | *fd = attempts[i].fd; |
612 | 0 | getnameinfo(attempts[i].addr->ai_addr, attempts[i].addr->ai_addrlen, |
613 | 0 | hostbuf, sizeof(hostbuf), portbuf, sizeof(portbuf), |
614 | 0 | NI_NUMERICHOST | NI_NUMERICSERV); |
615 | 0 | av_log(h, AV_LOG_VERBOSE, "Successfully connected to %s port %s\n", |
616 | 0 | hostbuf, portbuf); |
617 | 0 | return 0; |
618 | 0 | } |
619 | 0 | } |
620 | 0 | if (attempts[i].deadline_us < av_gettime_relative() && !last_err) |
621 | 0 | last_err = AVERROR(ETIMEDOUT); |
622 | 0 | if (!last_err) |
623 | 0 | continue; |
624 | | // Error (or timeout) for this socket; close the socket and remove |
625 | | // it from the attempts/pfd arrays, to let a new attempt start |
626 | | // directly. |
627 | 0 | getnameinfo(attempts[i].addr->ai_addr, attempts[i].addr->ai_addrlen, |
628 | 0 | hostbuf, sizeof(hostbuf), portbuf, sizeof(portbuf), |
629 | 0 | NI_NUMERICHOST | NI_NUMERICSERV); |
630 | 0 | av_log(h, AV_LOG_VERBOSE, "Connection attempt to %s port %s " |
631 | 0 | "failed: %s\n", hostbuf, portbuf, av_err2str(last_err)); |
632 | 0 | closesocket(attempts[i].fd); |
633 | 0 | memmove(&attempts[i], &attempts[i + 1], |
634 | 0 | (nb_attempts - i - 1) * sizeof(*attempts)); |
635 | 0 | memmove(&pfd[i], &pfd[i + 1], |
636 | 0 | (nb_attempts - i - 1) * sizeof(*pfd)); |
637 | 0 | i--; |
638 | 0 | nb_attempts--; |
639 | 0 | } |
640 | 0 | } |
641 | 0 | for (i = 0; i < nb_attempts; i++) |
642 | 0 | closesocket(attempts[i].fd); |
643 | 0 | if (last_err >= 0) |
644 | 0 | last_err = AVERROR(ECONNREFUSED); |
645 | 0 | if (last_err != AVERROR_EXIT) { |
646 | 0 | av_log(h, AV_LOG_ERROR, "Connection to %s failed: %s\n", |
647 | 0 | h->filename, av_err2str(last_err)); |
648 | 0 | } |
649 | 0 | return last_err; |
650 | 0 | } |
651 | | |
652 | | static int match_host_pattern(const char *pattern, const char *hostname) |
653 | 0 | { |
654 | 0 | int len_p, len_h; |
655 | 0 | if (!strcmp(pattern, "*")) |
656 | 0 | return 1; |
657 | | // Skip a possible *. at the start of the pattern |
658 | 0 | if (pattern[0] == '*') |
659 | 0 | pattern++; |
660 | 0 | if (pattern[0] == '.') |
661 | 0 | pattern++; |
662 | 0 | len_p = strlen(pattern); |
663 | 0 | len_h = strlen(hostname); |
664 | 0 | if (len_p > len_h) |
665 | 0 | return 0; |
666 | | // Simply check if the end of hostname is equal to 'pattern' |
667 | 0 | if (!strcmp(pattern, &hostname[len_h - len_p])) { |
668 | 0 | if (len_h == len_p) |
669 | 0 | return 1; // Exact match |
670 | 0 | if (hostname[len_h - len_p - 1] == '.') |
671 | 0 | return 1; // The matched substring is a domain and not just a substring of a domain |
672 | 0 | } |
673 | 0 | return 0; |
674 | 0 | } |
675 | | |
676 | | int ff_http_match_no_proxy(const char *no_proxy, const char *hostname) |
677 | 0 | { |
678 | 0 | char *buf, *start; |
679 | 0 | int ret = 0; |
680 | 0 | if (!no_proxy) |
681 | 0 | return 0; |
682 | 0 | if (!hostname) |
683 | 0 | return 0; |
684 | 0 | buf = av_strdup(no_proxy); |
685 | 0 | if (!buf) |
686 | 0 | return 0; |
687 | 0 | start = buf; |
688 | 0 | while (start) { |
689 | 0 | char *sep, *next = NULL; |
690 | 0 | start += strspn(start, " ,"); |
691 | 0 | sep = start + strcspn(start, " ,"); |
692 | 0 | if (*sep) { |
693 | 0 | next = sep + 1; |
694 | 0 | *sep = '\0'; |
695 | 0 | } |
696 | 0 | if (match_host_pattern(start, hostname)) { |
697 | 0 | ret = 1; |
698 | 0 | break; |
699 | 0 | } |
700 | 0 | start = next; |
701 | 0 | } |
702 | 0 | av_free(buf); |
703 | 0 | return ret; |
704 | 0 | } |
705 | | |
706 | | void ff_log_net_error(void *ctx, int level, const char* prefix) |
707 | 0 | { |
708 | 0 | av_log(ctx, level, "%s: %s\n", prefix, av_err2str(ff_neterrno())); |
709 | 0 | } |