/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 | | #include "network.h" |
33 | | #include "tls.h" |
34 | | #include "url.h" |
35 | | #include "libavutil/avassert.h" |
36 | | #include "libavutil/mem.h" |
37 | | #include "libavutil/time.h" |
38 | | |
39 | | int ff_tls_init(void) |
40 | 0 | { |
41 | | #if CONFIG_TLS_PROTOCOL |
42 | | #if CONFIG_GNUTLS |
43 | | ff_gnutls_init(); |
44 | | #endif |
45 | | #endif |
46 | 0 | return 0; |
47 | 0 | } |
48 | | |
49 | | void ff_tls_deinit(void) |
50 | 0 | { |
51 | | #if CONFIG_TLS_PROTOCOL |
52 | | #if CONFIG_GNUTLS |
53 | | ff_gnutls_deinit(); |
54 | | #endif |
55 | | #endif |
56 | 0 | } |
57 | | |
58 | | /** |
59 | | * Initialize the network subsystem. On Windows, this calls WSAStartup(). |
60 | | * |
61 | | * @return 0 on success, a negative AVERROR code on failure. |
62 | | */ |
63 | | int ff_network_init(void) |
64 | 0 | { |
65 | | #if HAVE_WINSOCK2_H |
66 | | WSADATA wsaData; |
67 | | |
68 | | if (WSAStartup(MAKEWORD(1,1), &wsaData)) |
69 | | return AVERROR(EIO); |
70 | | #endif |
71 | 0 | return 0; |
72 | 0 | } |
73 | | |
74 | | int ff_network_wait_fd(int fd, int write) |
75 | 0 | { |
76 | 0 | int ev = write ? POLLOUT : POLLIN; |
77 | 0 | struct pollfd p = { .fd = fd, .events = ev, .revents = 0 }; |
78 | 0 | int ret; |
79 | 0 | ret = poll(&p, 1, POLLING_TIME); |
80 | 0 | return ret < 0 ? ff_neterrno() : p.revents & (ev | POLLERR | POLLHUP) ? 0 : AVERROR(EAGAIN); |
81 | 0 | } |
82 | | |
83 | | int ff_network_wait_fd_timeout(int fd, int write, int64_t timeout, AVIOInterruptCB *int_cb) |
84 | 0 | { |
85 | 0 | int ret; |
86 | 0 | int64_t wait_start = 0; |
87 | |
|
88 | 0 | while (1) { |
89 | 0 | if (ff_check_interrupt(int_cb)) |
90 | 0 | return AVERROR_EXIT; |
91 | 0 | ret = ff_network_wait_fd(fd, write); |
92 | 0 | if (ret != AVERROR(EAGAIN)) |
93 | 0 | return ret; |
94 | 0 | if (timeout > 0) { |
95 | 0 | if (!wait_start) |
96 | 0 | wait_start = av_gettime_relative(); |
97 | 0 | else if (av_gettime_relative() - wait_start > timeout) |
98 | 0 | return AVERROR(ETIMEDOUT); |
99 | 0 | } |
100 | 0 | } |
101 | 0 | } |
102 | | |
103 | | int ff_network_sleep_interruptible(int64_t timeout, AVIOInterruptCB *int_cb) |
104 | 0 | { |
105 | 0 | int64_t wait_start = av_gettime_relative(); |
106 | |
|
107 | 0 | while (1) { |
108 | 0 | int64_t time_left; |
109 | |
|
110 | 0 | if (ff_check_interrupt(int_cb)) |
111 | 0 | return AVERROR_EXIT; |
112 | | |
113 | 0 | time_left = timeout - (av_gettime_relative() - wait_start); |
114 | 0 | if (time_left <= 0) |
115 | 0 | return AVERROR(ETIMEDOUT); |
116 | | |
117 | 0 | av_usleep(FFMIN(time_left, POLLING_TIME * 1000)); |
118 | 0 | } |
119 | 0 | } |
120 | | |
121 | | void ff_network_close(void) |
122 | 0 | { |
123 | | #if HAVE_WINSOCK2_H |
124 | | WSACleanup(); |
125 | | #endif |
126 | 0 | } |
127 | | |
128 | | #if HAVE_WINSOCK2_H |
129 | | int ff_neterrno(void) |
130 | | { |
131 | | int err = WSAGetLastError(); |
132 | | switch (err) { |
133 | | case WSAEWOULDBLOCK: |
134 | | return AVERROR(EAGAIN); |
135 | | case WSAEINTR: |
136 | | return AVERROR(EINTR); |
137 | | case WSAEPROTONOSUPPORT: |
138 | | return AVERROR(EPROTONOSUPPORT); |
139 | | case WSAETIMEDOUT: |
140 | | return AVERROR(ETIMEDOUT); |
141 | | case WSAECONNREFUSED: |
142 | | return AVERROR(ECONNREFUSED); |
143 | | case WSAEINPROGRESS: |
144 | | return AVERROR(EINPROGRESS); |
145 | | } |
146 | | return -err; |
147 | | } |
148 | | #endif |
149 | | |
150 | | int ff_is_multicast_address(struct sockaddr *addr) |
151 | 0 | { |
152 | 0 | if (addr->sa_family == AF_INET) { |
153 | 0 | return IN_MULTICAST(ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr)); |
154 | 0 | } |
155 | 0 | #if HAVE_STRUCT_SOCKADDR_IN6 |
156 | 0 | if (addr->sa_family == AF_INET6) { |
157 | 0 | return IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)addr)->sin6_addr); |
158 | 0 | } |
159 | 0 | #endif |
160 | | |
161 | 0 | return 0; |
162 | 0 | } |
163 | | |
164 | | static int ff_poll_interrupt(struct pollfd *p, nfds_t nfds, int timeout, |
165 | | AVIOInterruptCB *cb) |
166 | 0 | { |
167 | 0 | int runs = timeout / POLLING_TIME; |
168 | 0 | int ret = 0; |
169 | |
|
170 | 0 | do { |
171 | 0 | if (ff_check_interrupt(cb)) |
172 | 0 | return AVERROR_EXIT; |
173 | 0 | ret = poll(p, nfds, POLLING_TIME); |
174 | 0 | if (ret != 0) { |
175 | 0 | if (ret < 0) |
176 | 0 | ret = ff_neterrno(); |
177 | 0 | if (ret == AVERROR(EINTR)) |
178 | 0 | continue; |
179 | 0 | break; |
180 | 0 | } |
181 | 0 | } while (timeout <= 0 || runs-- > 0); |
182 | | |
183 | 0 | if (!ret) |
184 | 0 | return AVERROR(ETIMEDOUT); |
185 | 0 | return ret; |
186 | 0 | } |
187 | | |
188 | | int ff_socket(int af, int type, int proto, void *logctx) |
189 | 0 | { |
190 | 0 | int fd; |
191 | |
|
192 | 0 | #ifdef SOCK_CLOEXEC |
193 | 0 | fd = socket(af, type | SOCK_CLOEXEC, proto); |
194 | 0 | if (fd == -1 && errno == EINVAL) |
195 | 0 | #endif |
196 | 0 | { |
197 | 0 | fd = socket(af, type, proto); |
198 | 0 | #if HAVE_FCNTL |
199 | 0 | if (fd != -1) { |
200 | 0 | if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) |
201 | 0 | av_log(logctx, AV_LOG_DEBUG, "Failed to set close on exec\n"); |
202 | 0 | } |
203 | 0 | #endif |
204 | 0 | } |
205 | | #ifdef SO_NOSIGPIPE |
206 | | if (fd != -1) { |
207 | | if (setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &(int){1}, sizeof(int))) { |
208 | | av_log(logctx, AV_LOG_WARNING, "setsockopt(SO_NOSIGPIPE) failed\n"); |
209 | | } |
210 | | } |
211 | | #endif |
212 | 0 | return fd; |
213 | 0 | } |
214 | | |
215 | | int ff_listen(int fd, const struct sockaddr *addr, |
216 | | socklen_t addrlen, void *logctx) |
217 | 0 | { |
218 | 0 | int ret; |
219 | 0 | int reuse = 1; |
220 | 0 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))) { |
221 | 0 | av_log(logctx, AV_LOG_WARNING, "setsockopt(SO_REUSEADDR) failed\n"); |
222 | 0 | } |
223 | 0 | ret = bind(fd, addr, addrlen); |
224 | 0 | if (ret) |
225 | 0 | return ff_neterrno(); |
226 | | |
227 | 0 | ret = listen(fd, 1); |
228 | 0 | if (ret) |
229 | 0 | return ff_neterrno(); |
230 | 0 | return ret; |
231 | 0 | } |
232 | | |
233 | | int ff_accept(int fd, int timeout, URLContext *h) |
234 | 0 | { |
235 | 0 | int ret; |
236 | 0 | struct pollfd lp = { fd, POLLIN, 0 }; |
237 | |
|
238 | 0 | ret = ff_poll_interrupt(&lp, 1, timeout, &h->interrupt_callback); |
239 | 0 | if (ret < 0) |
240 | 0 | return ret; |
241 | | |
242 | 0 | ret = accept(fd, NULL, NULL); |
243 | 0 | if (ret < 0) |
244 | 0 | return ff_neterrno(); |
245 | 0 | if (ff_socket_nonblock(ret, 1) < 0) |
246 | 0 | av_log(h, AV_LOG_DEBUG, "ff_socket_nonblock failed\n"); |
247 | |
|
248 | 0 | return ret; |
249 | 0 | } |
250 | | |
251 | | int ff_listen_bind(int fd, const struct sockaddr *addr, |
252 | | socklen_t addrlen, int timeout, URLContext *h) |
253 | 0 | { |
254 | 0 | int ret; |
255 | 0 | if ((ret = ff_listen(fd, addr, addrlen, h)) < 0) |
256 | 0 | return ret; |
257 | 0 | if ((ret = ff_accept(fd, timeout, h)) < 0) |
258 | 0 | return ret; |
259 | 0 | closesocket(fd); |
260 | 0 | return ret; |
261 | 0 | } |
262 | | |
263 | | int ff_listen_connect(int fd, const struct sockaddr *addr, |
264 | | socklen_t addrlen, int timeout, URLContext *h, |
265 | | int will_try_next) |
266 | 0 | { |
267 | 0 | struct pollfd p = {fd, POLLOUT, 0}; |
268 | 0 | int ret; |
269 | 0 | socklen_t optlen; |
270 | |
|
271 | 0 | if (ff_socket_nonblock(fd, 1) < 0) |
272 | 0 | av_log(h, AV_LOG_DEBUG, "ff_socket_nonblock failed\n"); |
273 | |
|
274 | 0 | while ((ret = connect(fd, addr, addrlen))) { |
275 | 0 | ret = ff_neterrno(); |
276 | 0 | switch (ret) { |
277 | 0 | case AVERROR(EINTR): |
278 | 0 | if (ff_check_interrupt(&h->interrupt_callback)) |
279 | 0 | return AVERROR_EXIT; |
280 | 0 | continue; |
281 | 0 | case AVERROR(EINPROGRESS): |
282 | 0 | case AVERROR(EAGAIN): |
283 | 0 | ret = ff_poll_interrupt(&p, 1, timeout, &h->interrupt_callback); |
284 | 0 | if (ret < 0) |
285 | 0 | return ret; |
286 | 0 | optlen = sizeof(ret); |
287 | 0 | if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen)) |
288 | 0 | ret = AVUNERROR(ff_neterrno()); |
289 | 0 | if (ret != 0) { |
290 | 0 | ret = AVERROR(ret); |
291 | 0 | if (will_try_next) |
292 | 0 | av_log(h, AV_LOG_WARNING, |
293 | 0 | "Connection to %s failed (%s), trying next address\n", |
294 | 0 | h->filename, av_err2str(ret)); |
295 | 0 | else |
296 | 0 | av_log(h, AV_LOG_ERROR, "Connection to %s failed: %s\n", |
297 | 0 | h->filename, av_err2str(ret)); |
298 | 0 | } |
299 | 0 | av_fallthrough; |
300 | 0 | default: |
301 | 0 | return ret; |
302 | 0 | } |
303 | 0 | } |
304 | 0 | return ret; |
305 | 0 | } |
306 | | |
307 | | static void interleave_addrinfo(struct addrinfo *base) |
308 | 0 | { |
309 | 0 | struct addrinfo **next = &base->ai_next; |
310 | 0 | while (*next) { |
311 | 0 | struct addrinfo *cur = *next; |
312 | | // Iterate forward until we find an entry of a different family. |
313 | 0 | if (cur->ai_family == base->ai_family) { |
314 | 0 | next = &cur->ai_next; |
315 | 0 | continue; |
316 | 0 | } |
317 | 0 | if (cur == base->ai_next) { |
318 | | // If the first one following base is of a different family, just |
319 | | // move base forward one step and continue. |
320 | 0 | base = cur; |
321 | 0 | next = &base->ai_next; |
322 | 0 | continue; |
323 | 0 | } |
324 | | // Unchain cur from the rest of the list from its current spot. |
325 | 0 | *next = cur->ai_next; |
326 | | // Hook in cur directly after base. |
327 | 0 | cur->ai_next = base->ai_next; |
328 | 0 | base->ai_next = cur; |
329 | | // Restart with a new base. We know that before moving the cur element, |
330 | | // everything between the previous base and cur had the same family, |
331 | | // different from cur->ai_family. Therefore, we can keep next pointing |
332 | | // where it was, and continue from there with base at the one after |
333 | | // cur. |
334 | 0 | base = cur->ai_next; |
335 | 0 | } |
336 | 0 | } |
337 | | |
338 | | static void print_address_list(void *ctx, const struct addrinfo *addr, |
339 | | const char *title) |
340 | 0 | { |
341 | 0 | char hostbuf[100], portbuf[20]; |
342 | 0 | av_log(ctx, AV_LOG_DEBUG, "%s:\n", title); |
343 | 0 | while (addr) { |
344 | 0 | getnameinfo(addr->ai_addr, addr->ai_addrlen, |
345 | 0 | hostbuf, sizeof(hostbuf), portbuf, sizeof(portbuf), |
346 | 0 | NI_NUMERICHOST | NI_NUMERICSERV); |
347 | 0 | av_log(ctx, AV_LOG_DEBUG, "Address %s port %s\n", hostbuf, portbuf); |
348 | 0 | addr = addr->ai_next; |
349 | 0 | } |
350 | 0 | } |
351 | | |
352 | | struct ConnectionAttempt { |
353 | | int fd; |
354 | | int64_t deadline_us; |
355 | | struct addrinfo *addr; |
356 | | }; |
357 | | |
358 | | // Returns < 0 on error, 0 on successfully started connection attempt, |
359 | | // > 0 for a connection that succeeded already. |
360 | | static int start_connect_attempt(struct ConnectionAttempt *attempt, |
361 | | struct addrinfo **ptr, int timeout_ms, |
362 | | URLContext *h, |
363 | | int (*customize_fd)(void *, int, int), void *customize_ctx) |
364 | 0 | { |
365 | 0 | struct addrinfo *ai = *ptr; |
366 | 0 | int ret; |
367 | |
|
368 | 0 | *ptr = ai->ai_next; |
369 | |
|
370 | 0 | attempt->fd = ff_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol, h); |
371 | 0 | if (attempt->fd < 0) |
372 | 0 | return ff_neterrno(); |
373 | 0 | attempt->deadline_us = av_gettime_relative() + timeout_ms * 1000; |
374 | 0 | attempt->addr = ai; |
375 | |
|
376 | 0 | ff_socket_nonblock(attempt->fd, 1); |
377 | |
|
378 | 0 | if (customize_fd) { |
379 | 0 | ret = customize_fd(customize_ctx, attempt->fd, ai->ai_family); |
380 | 0 | if (ret) { |
381 | 0 | closesocket(attempt->fd); |
382 | 0 | attempt->fd = -1; |
383 | 0 | return ret; |
384 | 0 | } |
385 | 0 | } |
386 | | |
387 | 0 | while ((ret = connect(attempt->fd, ai->ai_addr, ai->ai_addrlen))) { |
388 | 0 | ret = ff_neterrno(); |
389 | 0 | switch (ret) { |
390 | 0 | case AVERROR(EINTR): |
391 | 0 | if (ff_check_interrupt(&h->interrupt_callback)) { |
392 | 0 | closesocket(attempt->fd); |
393 | 0 | attempt->fd = -1; |
394 | 0 | return AVERROR_EXIT; |
395 | 0 | } |
396 | 0 | continue; |
397 | 0 | case AVERROR(EINPROGRESS): |
398 | 0 | case AVERROR(EAGAIN): |
399 | 0 | return 0; |
400 | 0 | default: |
401 | 0 | closesocket(attempt->fd); |
402 | 0 | attempt->fd = -1; |
403 | 0 | return ret; |
404 | 0 | } |
405 | 0 | } |
406 | 0 | return 1; |
407 | 0 | } |
408 | | |
409 | | // Try a new connection to another address after 200 ms, as suggested in |
410 | | // RFC 8305 (or sooner if an earlier attempt fails). |
411 | 0 | #define NEXT_ATTEMPT_DELAY_MS 200 |
412 | | |
413 | | int ff_connect_parallel(struct addrinfo *addrs, int timeout_ms_per_address, |
414 | | int parallel, URLContext *h, int *fd, |
415 | | int (*customize_fd)(void *, int, int), void *customize_ctx) |
416 | 0 | { |
417 | 0 | struct ConnectionAttempt attempts[3]; |
418 | 0 | struct pollfd pfd[3]; |
419 | 0 | int nb_attempts = 0, i, j; |
420 | 0 | int64_t next_attempt_us = av_gettime_relative(), next_deadline_us; |
421 | 0 | int last_err = AVERROR(EIO); |
422 | 0 | socklen_t optlen; |
423 | 0 | char hostbuf[100], portbuf[20]; |
424 | |
|
425 | 0 | if (parallel > FF_ARRAY_ELEMS(attempts)) |
426 | 0 | parallel = FF_ARRAY_ELEMS(attempts); |
427 | |
|
428 | 0 | print_address_list(h, addrs, "Original list of addresses"); |
429 | | // This mutates the list, but the head of the list is still the same |
430 | | // element, so the caller, who owns the list, doesn't need to get |
431 | | // an updated pointer. |
432 | 0 | interleave_addrinfo(addrs); |
433 | 0 | print_address_list(h, addrs, "Interleaved list of addresses"); |
434 | |
|
435 | 0 | while (nb_attempts > 0 || addrs) { |
436 | | // Start a new connection attempt, if possible. |
437 | 0 | if (nb_attempts < parallel && addrs) { |
438 | 0 | getnameinfo(addrs->ai_addr, addrs->ai_addrlen, |
439 | 0 | hostbuf, sizeof(hostbuf), portbuf, sizeof(portbuf), |
440 | 0 | NI_NUMERICHOST | NI_NUMERICSERV); |
441 | 0 | av_log(h, AV_LOG_VERBOSE, "Starting connection attempt to %s port %s\n", |
442 | 0 | hostbuf, portbuf); |
443 | 0 | last_err = start_connect_attempt(&attempts[nb_attempts], &addrs, |
444 | 0 | timeout_ms_per_address, h, |
445 | 0 | customize_fd, customize_ctx); |
446 | 0 | if (last_err < 0) { |
447 | 0 | av_log(h, AV_LOG_VERBOSE, "Connected attempt failed: %s\n", |
448 | 0 | av_err2str(last_err)); |
449 | 0 | continue; |
450 | 0 | } |
451 | 0 | if (last_err > 0) { |
452 | 0 | for (i = 0; i < nb_attempts; i++) |
453 | 0 | closesocket(attempts[i].fd); |
454 | 0 | *fd = attempts[nb_attempts].fd; |
455 | 0 | return 0; |
456 | 0 | } |
457 | 0 | pfd[nb_attempts].fd = attempts[nb_attempts].fd; |
458 | 0 | pfd[nb_attempts].events = POLLOUT; |
459 | 0 | next_attempt_us = av_gettime_relative() + NEXT_ATTEMPT_DELAY_MS * 1000; |
460 | 0 | nb_attempts++; |
461 | 0 | } |
462 | | |
463 | 0 | av_assert0(nb_attempts > 0); |
464 | | // The connection attempts are sorted from oldest to newest, so the |
465 | | // first one will have the earliest deadline. |
466 | 0 | next_deadline_us = attempts[0].deadline_us; |
467 | | // If we can start another attempt in parallel, wait until that time. |
468 | 0 | if (nb_attempts < parallel && addrs) |
469 | 0 | next_deadline_us = FFMIN(next_deadline_us, next_attempt_us); |
470 | 0 | last_err = ff_poll_interrupt(pfd, nb_attempts, |
471 | 0 | (next_deadline_us - av_gettime_relative())/1000, |
472 | 0 | &h->interrupt_callback); |
473 | 0 | if (last_err < 0 && last_err != AVERROR(ETIMEDOUT)) |
474 | 0 | break; |
475 | | |
476 | | // Check the status from the poll output. |
477 | 0 | for (i = 0; i < nb_attempts; i++) { |
478 | 0 | last_err = 0; |
479 | 0 | if (pfd[i].revents) { |
480 | | // Some sort of action for this socket, check its status (either |
481 | | // a successful connection or an error). |
482 | 0 | optlen = sizeof(last_err); |
483 | 0 | if (getsockopt(attempts[i].fd, SOL_SOCKET, SO_ERROR, &last_err, &optlen)) |
484 | 0 | last_err = ff_neterrno(); |
485 | 0 | else if (last_err != 0) |
486 | 0 | last_err = AVERROR(last_err); |
487 | 0 | if (last_err == 0) { |
488 | | // Everything is ok, we seem to have a successful |
489 | | // connection. Close other sockets and return this one. |
490 | 0 | for (j = 0; j < nb_attempts; j++) |
491 | 0 | if (j != i) |
492 | 0 | closesocket(attempts[j].fd); |
493 | 0 | *fd = attempts[i].fd; |
494 | 0 | getnameinfo(attempts[i].addr->ai_addr, attempts[i].addr->ai_addrlen, |
495 | 0 | hostbuf, sizeof(hostbuf), portbuf, sizeof(portbuf), |
496 | 0 | NI_NUMERICHOST | NI_NUMERICSERV); |
497 | 0 | av_log(h, AV_LOG_VERBOSE, "Successfully connected to %s port %s\n", |
498 | 0 | hostbuf, portbuf); |
499 | 0 | return 0; |
500 | 0 | } |
501 | 0 | } |
502 | 0 | if (attempts[i].deadline_us < av_gettime_relative() && !last_err) |
503 | 0 | last_err = AVERROR(ETIMEDOUT); |
504 | 0 | if (!last_err) |
505 | 0 | continue; |
506 | | // Error (or timeout) for this socket; close the socket and remove |
507 | | // it from the attempts/pfd arrays, to let a new attempt start |
508 | | // directly. |
509 | 0 | getnameinfo(attempts[i].addr->ai_addr, attempts[i].addr->ai_addrlen, |
510 | 0 | hostbuf, sizeof(hostbuf), portbuf, sizeof(portbuf), |
511 | 0 | NI_NUMERICHOST | NI_NUMERICSERV); |
512 | 0 | av_log(h, AV_LOG_VERBOSE, "Connection attempt to %s port %s " |
513 | 0 | "failed: %s\n", hostbuf, portbuf, av_err2str(last_err)); |
514 | 0 | closesocket(attempts[i].fd); |
515 | 0 | memmove(&attempts[i], &attempts[i + 1], |
516 | 0 | (nb_attempts - i - 1) * sizeof(*attempts)); |
517 | 0 | memmove(&pfd[i], &pfd[i + 1], |
518 | 0 | (nb_attempts - i - 1) * sizeof(*pfd)); |
519 | 0 | i--; |
520 | 0 | nb_attempts--; |
521 | 0 | } |
522 | 0 | } |
523 | 0 | for (i = 0; i < nb_attempts; i++) |
524 | 0 | closesocket(attempts[i].fd); |
525 | 0 | if (last_err >= 0) |
526 | 0 | last_err = AVERROR(ECONNREFUSED); |
527 | 0 | if (last_err != AVERROR_EXIT) { |
528 | 0 | av_log(h, AV_LOG_ERROR, "Connection to %s failed: %s\n", |
529 | 0 | h->filename, av_err2str(last_err)); |
530 | 0 | } |
531 | 0 | return last_err; |
532 | 0 | } |
533 | | |
534 | | static int match_host_pattern(const char *pattern, const char *hostname) |
535 | 0 | { |
536 | 0 | int len_p, len_h; |
537 | 0 | if (!strcmp(pattern, "*")) |
538 | 0 | return 1; |
539 | | // Skip a possible *. at the start of the pattern |
540 | 0 | if (pattern[0] == '*') |
541 | 0 | pattern++; |
542 | 0 | if (pattern[0] == '.') |
543 | 0 | pattern++; |
544 | 0 | len_p = strlen(pattern); |
545 | 0 | len_h = strlen(hostname); |
546 | 0 | if (len_p > len_h) |
547 | 0 | return 0; |
548 | | // Simply check if the end of hostname is equal to 'pattern' |
549 | 0 | if (!strcmp(pattern, &hostname[len_h - len_p])) { |
550 | 0 | if (len_h == len_p) |
551 | 0 | return 1; // Exact match |
552 | 0 | if (hostname[len_h - len_p - 1] == '.') |
553 | 0 | return 1; // The matched substring is a domain and not just a substring of a domain |
554 | 0 | } |
555 | 0 | return 0; |
556 | 0 | } |
557 | | |
558 | | int ff_http_match_no_proxy(const char *no_proxy, const char *hostname) |
559 | 0 | { |
560 | 0 | char *buf, *start; |
561 | 0 | int ret = 0; |
562 | 0 | if (!no_proxy) |
563 | 0 | return 0; |
564 | 0 | if (!hostname) |
565 | 0 | return 0; |
566 | 0 | buf = av_strdup(no_proxy); |
567 | 0 | if (!buf) |
568 | 0 | return 0; |
569 | 0 | start = buf; |
570 | 0 | while (start) { |
571 | 0 | char *sep, *next = NULL; |
572 | 0 | start += strspn(start, " ,"); |
573 | 0 | sep = start + strcspn(start, " ,"); |
574 | 0 | if (*sep) { |
575 | 0 | next = sep + 1; |
576 | 0 | *sep = '\0'; |
577 | 0 | } |
578 | 0 | if (match_host_pattern(start, hostname)) { |
579 | 0 | ret = 1; |
580 | 0 | break; |
581 | 0 | } |
582 | 0 | start = next; |
583 | 0 | } |
584 | 0 | av_free(buf); |
585 | 0 | return ret; |
586 | 0 | } |
587 | | |
588 | | void ff_log_net_error(void *ctx, int level, const char* prefix) |
589 | 0 | { |
590 | 0 | av_log(ctx, level, "%s: %s\n", prefix, av_err2str(ff_neterrno())); |
591 | 0 | } |