Coverage Report

Created: 2026-01-25 07:18

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
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
}