Coverage Report

Created: 2026-04-29 07:00

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