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