test_common.c revision a74702c6
1/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc.  See LICENSE. */
2#if __GNUC__
3#define _GNU_SOURCE     /* For struct in6_pktinfo */
4#endif
5#include <assert.h>
6#include <errno.h>
7#include <string.h>
8#include <stdlib.h>
9#include <stdio.h>
10#include <sys/types.h>
11#if defined(__APPLE__)
12#   define __APPLE_USE_RFC_3542 1
13#endif
14#ifndef WIN32
15#include <netinet/in.h>
16#include <netinet/ip.h>
17#include <arpa/inet.h>
18#include <sys/socket.h>
19#include <unistd.h>
20#else
21#include <Windows.h>
22#include <WinSock2.h>
23#include <MSWSock.h>
24#include<io.h>
25#pragma warning(disable:4996)//posix name deprecated
26#define close closesocket
27#endif
28#include <sys/stat.h>
29#include <sys/queue.h>
30#include <fcntl.h>
31
32#include "test_config.h"
33
34#if HAVE_REGEX
35#ifndef WIN32
36#include <regex.h>
37#else
38#include <pcreposix.h>
39#endif
40#endif
41
42#include <event2/event.h>
43
44#include "lsquic.h"
45#include "test_common.h"
46#include "prog.h"
47#include "lsxpack_header.h"
48
49#include "../src/liblsquic/lsquic_logger.h"
50
51#define MAX(a, b) ((a) > (b) ? (a) : (b))
52#define MIN(a, b) ((a) < (b) ? (a) : (b))
53
54#ifndef LSQUIC_USE_POOLS
55#define LSQUIC_USE_POOLS 1
56#endif
57
58#if __linux__
59#   define NDROPPED_SZ CMSG_SPACE(sizeof(uint32_t))  /* SO_RXQ_OVFL */
60#else
61#   define NDROPPED_SZ 0
62#endif
63
64#if __linux__ && defined(IP_RECVORIGDSTADDR)
65#   define DST_MSG_SZ sizeof(struct sockaddr_in)
66#elif WIN32
67#   define DST_MSG_SZ sizeof(struct sockaddr_in)
68#elif __linux__
69#   define DST_MSG_SZ sizeof(struct in_pktinfo)
70#else
71#   define DST_MSG_SZ sizeof(struct sockaddr_in)
72#endif
73
74#if ECN_SUPPORTED
75#define ECN_SZ CMSG_SPACE(sizeof(int))
76#else
77#define ECN_SZ 0
78#endif
79
80#define MAX_PACKET_SZ 0xffff
81
82#define CTL_SZ (CMSG_SPACE(MAX(DST_MSG_SZ, \
83                        sizeof(struct in6_pktinfo))) + NDROPPED_SZ + ECN_SZ)
84
85/* There are `n_alloc' elements in `vecs', `local_addresses', and
86 * `peer_addresses' arrays.  `ctlmsg_data' is n_alloc * CTL_SZ.  Each packets
87 * gets a single `vecs' element that points somewhere into `packet_data'.
88 *
89 * `n_alloc' is calculated at run-time based on the socket's receive buffer
90 * size.
91 */
92struct packets_in
93{
94    unsigned char           *packet_data;
95    unsigned char           *ctlmsg_data;
96#ifndef WIN32
97    struct iovec            *vecs;
98#else
99    WSABUF                  *vecs;
100#endif
101#if ECN_SUPPORTED
102    int                     *ecn;
103#endif
104    struct sockaddr_storage *local_addresses,
105                            *peer_addresses;
106    unsigned                 n_alloc;
107    unsigned                 data_sz;
108};
109
110
111#if WIN32
112LPFN_WSARECVMSG pfnWSARecvMsg;
113GUID recvGuid = WSAID_WSARECVMSG;
114LPFN_WSASENDMSG pfnWSASendMsg;
115GUID sendGuid = WSAID_WSASENDMSG;
116
117CRITICAL_SECTION initLock;
118LONG initialized = 0;
119
120static void getExtensionPtrs()
121{
122    if (InterlockedCompareExchange(&initialized, 1, 0) == 0)
123    {
124        InitializeCriticalSection(&initLock);
125    }
126    EnterCriticalSection(&initLock);
127    if(pfnWSARecvMsg == NULL|| pfnWSASendMsg == NULL)
128    {
129        SOCKET sock= socket(PF_INET, SOCK_DGRAM, 0);
130        DWORD dwBytes;
131        int rc = 0;
132        if (pfnWSARecvMsg == NULL)
133        {
134            rc = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &recvGuid,
135                    sizeof(recvGuid), &pfnWSARecvMsg, sizeof(pfnWSARecvMsg),
136                    &dwBytes, NULL, NULL);
137        }
138        if (rc != SOCKET_ERROR)
139        {
140            if (pfnWSASendMsg == NULL)
141            {
142                rc = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
143                        &sendGuid, sizeof(sendGuid), &pfnWSASendMsg,
144                        sizeof(pfnWSASendMsg), &dwBytes, NULL, NULL);
145            }
146        }
147        if (rc == SOCKET_ERROR)
148        {
149            LSQ_ERROR("Can't get extension function pointers: %d",
150                                                        WSAGetLastError());
151        }
152        closesocket(sock);
153    }
154    LeaveCriticalSection(&initLock);
155}
156
157
158#endif
159
160
161
162
163static struct packets_in *
164allocate_packets_in (SOCKET_TYPE fd)
165{
166    struct packets_in *packs_in;
167    unsigned n_alloc;
168    socklen_t opt_len;
169    int recvsz;
170
171    opt_len = sizeof(recvsz);
172    if (0 != getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&recvsz, &opt_len))
173    {
174        LSQ_ERROR("getsockopt failed: %s", strerror(errno));
175        return NULL;
176    }
177
178    n_alloc = (unsigned) recvsz / 1370;
179    LSQ_INFO("socket buffer size: %d bytes; max # packets is set to %u",
180        recvsz, n_alloc);
181    recvsz += MAX_PACKET_SZ;
182
183    packs_in = malloc(sizeof(*packs_in));
184    packs_in->data_sz = recvsz;
185    packs_in->n_alloc = n_alloc;
186    packs_in->packet_data = malloc(recvsz);
187    packs_in->ctlmsg_data = malloc(n_alloc * CTL_SZ);
188    packs_in->vecs = malloc(n_alloc * sizeof(packs_in->vecs[0]));
189    packs_in->local_addresses = malloc(n_alloc * sizeof(packs_in->local_addresses[0]));
190    packs_in->peer_addresses = malloc(n_alloc * sizeof(packs_in->peer_addresses[0]));
191#if ECN_SUPPORTED
192    packs_in->ecn = malloc(n_alloc * sizeof(packs_in->ecn[0]));
193#endif
194
195    return packs_in;
196}
197
198
199static void
200free_packets_in (struct packets_in *packs_in)
201{
202#if ECN_SUPPORTED
203    free(packs_in->ecn);
204#endif
205    free(packs_in->peer_addresses);
206    free(packs_in->local_addresses);
207    free(packs_in->ctlmsg_data);
208    free(packs_in->vecs);
209    free(packs_in->packet_data);
210    free(packs_in);
211}
212
213
214void
215sport_destroy (struct service_port *sport)
216{
217    if (sport->ev)
218    {
219        event_del(sport->ev);
220        event_free(sport->ev);
221    }
222    if (sport->fd >= 0)
223        (void) CLOSE_SOCKET(sport->fd);
224    if (sport->packs_in)
225        free_packets_in(sport->packs_in);
226    free(sport->sp_token_buf);
227    free(sport);
228}
229
230
231struct service_port *
232sport_new (const char *optarg, struct prog *prog)
233{
234    struct service_port *const sport = calloc(1, sizeof(*sport));
235#if HAVE_REGEX
236    regex_t re;
237    regmatch_t matches[5];
238    int re_code;
239    const char *port_str;
240    char errbuf[80];
241#else
242    char *port_str;
243#endif
244    int port, e;
245    const char *host;
246    struct addrinfo hints, *res = NULL;
247#if __linux__
248    sport->n_dropped = 0;
249    sport->drop_init = 0;
250#endif
251    sport->ev = NULL;
252    sport->packs_in = NULL;
253    sport->fd = -1;
254    char *const addr = strdup(optarg);
255#if __linux__
256    char *if_name;
257    if_name = strrchr(addr, ',');
258    if (if_name)
259    {
260        strncpy(sport->if_name, if_name + 1, sizeof(sport->if_name) - 1);
261        sport->if_name[ sizeof(sport->if_name) - 1 ] = '\0';
262        *if_name = '\0';
263    }
264    else
265        sport->if_name[0] = '\0';
266#endif
267#if HAVE_REGEX
268    re_code = regcomp(&re, "^(.*):([0-9][0-9]*)$"
269                          "|^([0-9][0-9]*)$"
270                          "|^(..*)$"
271                                                    , REG_EXTENDED);
272    if (re_code != 0)
273    {
274        regerror(re_code, &re, errbuf, sizeof(errbuf));
275        LSQ_ERROR("cannot compile regex: %s", errbuf);
276        goto err;
277    }
278    if (0 != regexec(&re, addr, sizeof(matches) / sizeof(matches[0]),
279                                                            matches, 0))
280    {
281        LSQ_ERROR("Invalid argument `%s'", addr);
282        goto err;
283    }
284    if (matches[1].rm_so >= 0)
285    {
286        addr[ matches[1].rm_so + matches[1].rm_eo ] = '\0';
287        host = addr;
288        port_str = &addr[ matches[2].rm_so ];
289        port = atoi(port_str);
290    }
291    else if (matches[3].rm_so >= 0)
292    {
293        if (!prog->prog_hostname)
294        {
295            LSQ_ERROR("hostname is not specified");
296            goto err;
297        }
298        host = prog->prog_hostname;
299        port_str = &addr[ matches[3].rm_so ];
300        port = atoi(port_str);
301    }
302    else
303    {
304        assert(matches[4].rm_so >= 0);
305        host = addr;
306        port_str = "443";
307        port = 443;
308    }
309#else
310    host = addr;
311    port_str = strrchr(addr, ':');
312    if (port_str)
313    {
314        *port_str++ = '\0';
315        port = atoi(port_str);
316    }
317    else
318    {
319        port_str = "443";
320        port = 443;
321    }
322#endif
323    assert(host);
324    LSQ_DEBUG("host: %s; port: %d", host, port);
325    if (strlen(host) > sizeof(sport->host) - 1)
326    {
327        LSQ_ERROR("argument `%s' too long", host);
328        goto err;
329    }
330    strcpy(sport->host, host);
331
332    struct sockaddr_in  *const sa4 = (void *) &sport->sas;
333    struct sockaddr_in6 *const sa6 = (void *) &sport->sas;
334    if        (inet_pton(AF_INET, host, &sa4->sin_addr)) {
335        sa4->sin_family = AF_INET;
336        sa4->sin_port   = htons(port);
337    } else if (memset(sa6, 0, sizeof(*sa6)),
338                    inet_pton(AF_INET6, host, &sa6->sin6_addr)) {
339        sa6->sin6_family = AF_INET6;
340        sa6->sin6_port   = htons(port);
341    } else
342    {
343        memset(&hints, 0, sizeof(hints));
344        hints.ai_flags = AI_NUMERICSERV;
345        if (prog->prog_ipver == 4)
346            hints.ai_family = AF_INET;
347        else if (prog->prog_ipver == 6)
348            hints.ai_family = AF_INET6;
349        e = getaddrinfo(host, port_str, &hints, &res);
350        if (e != 0)
351        {
352            LSQ_ERROR("could not resolve %s:%s: %s", host, port_str,
353                                                        gai_strerror(e));
354            goto err;
355        }
356        if (res->ai_addrlen > sizeof(sport->sas))
357        {
358            LSQ_ERROR("resolved socket length is too long");
359            goto err;
360        }
361        memcpy(&sport->sas, res->ai_addr, res->ai_addrlen);
362        if (!prog->prog_hostname)
363            prog->prog_hostname = sport->host;
364    }
365
366#if HAVE_REGEX
367    if (0 == re_code)
368        regfree(&re);
369#endif
370    if (res)
371        freeaddrinfo(res);
372    free(addr);
373    sport->sp_prog = prog;
374    return sport;
375
376  err:
377#if HAVE_REGEX
378    if (0 == re_code)
379        regfree(&re);
380#endif
381    if (res)
382        freeaddrinfo(res);
383    free(sport);
384    free(addr);
385    return NULL;
386}
387
388
389/* Replace IP address part of `sa' with that provided in ancillary messages
390 * in `msg'.
391 */
392static void
393proc_ancillary (
394#ifndef WIN32
395                struct msghdr
396#else
397                WSAMSG
398#endif
399                              *msg, struct sockaddr_storage *storage
400#if __linux__
401                , uint32_t *n_dropped
402#endif
403#if ECN_SUPPORTED
404                , int *ecn
405#endif
406                )
407{
408    const struct in6_pktinfo *in6_pkt;
409    struct cmsghdr *cmsg;
410
411    for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg))
412    {
413        if (cmsg->cmsg_level == IPPROTO_IP &&
414            cmsg->cmsg_type  ==
415#if __linux__ && defined(IP_RECVORIGDSTADDR)
416                                IP_ORIGDSTADDR
417#elif __linux__ || WIN32 || __APPLE__
418                                IP_PKTINFO
419#else
420                                IP_RECVDSTADDR
421#endif
422                                              )
423        {
424#if __linux__ && defined(IP_RECVORIGDSTADDR)
425            memcpy(storage, CMSG_DATA(cmsg), sizeof(struct sockaddr_in));
426#elif WIN32
427            const struct in_pktinfo *in_pkt;
428            in_pkt = (void *) WSA_CMSG_DATA(cmsg);
429            ((struct sockaddr_in *) storage)->sin_addr = in_pkt->ipi_addr;
430#elif __linux__ || __APPLE__
431            const struct in_pktinfo *in_pkt;
432            in_pkt = (void *) CMSG_DATA(cmsg);
433            ((struct sockaddr_in *) storage)->sin_addr = in_pkt->ipi_addr;
434#else
435            memcpy(&((struct sockaddr_in *) storage)->sin_addr,
436                            CMSG_DATA(cmsg), sizeof(struct in_addr));
437#endif
438        }
439        else if (cmsg->cmsg_level == IPPROTO_IPV6 &&
440                 cmsg->cmsg_type  == IPV6_PKTINFO)
441        {
442#ifndef WIN32
443            in6_pkt = (void *) CMSG_DATA(cmsg);
444#else
445            in6_pkt = (void *) WSA_CMSG_DATA(cmsg);
446#endif
447            ((struct sockaddr_in6 *) storage)->sin6_addr =
448                                                    in6_pkt->ipi6_addr;
449        }
450#if __linux__
451        else if (cmsg->cmsg_level == SOL_SOCKET &&
452                 cmsg->cmsg_type  == SO_RXQ_OVFL)
453            memcpy(n_dropped, CMSG_DATA(cmsg), sizeof(*n_dropped));
454#endif
455#if ECN_SUPPORTED
456        else if ((cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TOS)
457                 || (cmsg->cmsg_level == IPPROTO_IPV6
458                                            && cmsg->cmsg_type == IPV6_TCLASS))
459        {
460            memcpy(ecn, CMSG_DATA(cmsg), sizeof(*ecn));
461            *ecn &= IPTOS_ECN_MASK;
462        }
463#ifdef __FreeBSD__
464        else if (cmsg->cmsg_level == IPPROTO_IP
465                                            && cmsg->cmsg_type == IP_RECVTOS)
466        {
467            unsigned char tos;
468            memcpy(&tos, CMSG_DATA(cmsg), sizeof(tos));
469            *ecn = tos & IPTOS_ECN_MASK;
470        }
471#endif
472#endif
473    }
474}
475
476
477struct read_iter
478{
479    struct service_port     *ri_sport;
480    unsigned                 ri_idx;    /* Current element */
481    unsigned                 ri_off;    /* Offset into packet_data */
482};
483
484
485enum rop { ROP_OK, ROP_NOROOM, ROP_ERROR, };
486
487static enum rop
488read_one_packet (struct read_iter *iter)
489{
490    unsigned char *ctl_buf;
491    struct packets_in *packs_in;
492#if __linux__
493    uint32_t n_dropped;
494#endif
495#ifndef WIN32
496    ssize_t nread;
497#else
498    DWORD nread;
499    int socket_ret;
500#endif
501    struct sockaddr_storage *local_addr;
502    struct service_port *sport;
503
504    sport = iter->ri_sport;
505    packs_in = sport->packs_in;
506
507    if (iter->ri_idx >= packs_in->n_alloc ||
508        iter->ri_off + MAX_PACKET_SZ > packs_in->data_sz)
509    {
510        LSQ_DEBUG("out of room in packets_in");
511        return ROP_NOROOM;
512    }
513
514#ifndef WIN32
515    packs_in->vecs[iter->ri_idx].iov_base = packs_in->packet_data + iter->ri_off;
516    packs_in->vecs[iter->ri_idx].iov_len  = MAX_PACKET_SZ;
517#else
518    packs_in->vecs[iter->ri_idx].buf = (char*)packs_in->packet_data + iter->ri_off;
519    packs_in->vecs[iter->ri_idx].len = MAX_PACKET_SZ;
520#endif
521
522#ifndef WIN32
523  top:
524#endif
525    ctl_buf = packs_in->ctlmsg_data + iter->ri_idx * CTL_SZ;
526
527#ifndef WIN32
528    struct msghdr msg = {
529        .msg_name       = &packs_in->peer_addresses[iter->ri_idx],
530        .msg_namelen    = sizeof(packs_in->peer_addresses[iter->ri_idx]),
531        .msg_iov        = &packs_in->vecs[iter->ri_idx],
532        .msg_iovlen     = 1,
533        .msg_control    = ctl_buf,
534        .msg_controllen = CTL_SZ,
535    };
536    nread = recvmsg(sport->fd, &msg, 0);
537    if (-1 == nread) {
538        if (!(EAGAIN == errno || EWOULDBLOCK == errno))
539            LSQ_ERROR("recvmsg: %s", strerror(errno));
540        return ROP_ERROR;
541    }
542    if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC))
543    {
544        if (msg.msg_flags & MSG_TRUNC)
545            LSQ_INFO("packet truncated - drop it");
546        if (msg.msg_flags & MSG_CTRUNC)
547            LSQ_WARN("packet's auxilicary data truncated - drop it");
548        goto top;
549    }
550#else
551    WSAMSG msg = {
552        .name       = (LPSOCKADDR)&packs_in->peer_addresses[iter->ri_idx],
553        .namelen    = sizeof(packs_in->peer_addresses[iter->ri_idx]),
554        .lpBuffers        = &packs_in->vecs[iter->ri_idx],
555        .dwBufferCount     = 1,
556        .Control = {CTL_SZ,(char*)ctl_buf}
557    };
558    socket_ret = pfnWSARecvMsg(sport->fd, &msg, &nread, NULL, NULL);
559    if (SOCKET_ERROR == socket_ret) {
560        if (WSAEWOULDBLOCK != WSAGetLastError())
561            LSQ_ERROR("recvmsg: %d", WSAGetLastError());
562	return ROP_ERROR;
563    }
564#endif
565
566    local_addr = &packs_in->local_addresses[iter->ri_idx];
567    memcpy(local_addr, &sport->sp_local_addr, sizeof(*local_addr));
568#if __linux__
569    n_dropped = 0;
570#endif
571#if ECN_SUPPORTED
572    packs_in->ecn[iter->ri_idx] = 0;
573#endif
574    proc_ancillary(&msg, local_addr
575#if __linux__
576        , &n_dropped
577#endif
578#if ECN_SUPPORTED
579        , &packs_in->ecn[iter->ri_idx]
580#endif
581    );
582#if LSQUIC_ECN_BLACK_HOLE && ECN_SUPPORTED
583    {
584        const char *s;
585        s = getenv("LSQUIC_ECN_BLACK_HOLE");
586        if (s && atoi(s) && packs_in->ecn[iter->ri_idx])
587        {
588            LSQ_NOTICE("ECN blackhole: drop packet");
589            return ROP_OK;
590        }
591    }
592#endif
593#if __linux__
594    if (sport->drop_init)
595    {
596        if (sport->n_dropped < n_dropped)
597            LSQ_INFO("dropped %u packets", n_dropped - sport->n_dropped);
598    }
599    else
600        sport->drop_init = 1;
601    sport->n_dropped = n_dropped;
602#endif
603
604#ifndef WIN32
605    packs_in->vecs[iter->ri_idx].iov_len = nread;
606#else
607    packs_in->vecs[iter->ri_idx].len = nread;
608#endif
609    iter->ri_off += nread;
610    iter->ri_idx += 1;
611
612    return ROP_OK;
613}
614
615
616#if HAVE_RECVMMSG
617static enum rop
618read_using_recvmmsg (struct read_iter *iter)
619{
620#if __linux__
621    uint32_t n_dropped;
622#endif
623    int s;
624    unsigned n;
625    struct sockaddr_storage *local_addr;
626    struct service_port *const sport = iter->ri_sport;
627    struct packets_in *const packs_in = sport->packs_in;
628    /* XXX TODO We allocate this array on the stack and initialize the
629     * headers each time the function is invoked.  This is suboptimal.
630     * What we should really be doing is allocate mmsghdrs as part of
631     * packs_in and initialize it there.  While we are at it, we should
632     * make packs_in shared between all service ports.
633     */
634    struct mmsghdr mmsghdrs[ packs_in->n_alloc  ];
635
636    /* Sanity check: we assume that the iterator is reset */
637    assert(iter->ri_off == 0 && iter->ri_idx == 0);
638
639    /* Initialize mmsghdrs */
640    for (n = 0; n < sizeof(mmsghdrs) / sizeof(mmsghdrs[0]); ++n)
641    {
642        packs_in->vecs[n].iov_base = packs_in->packet_data + MAX_PACKET_SZ * n;
643        packs_in->vecs[n].iov_len  = MAX_PACKET_SZ;
644        mmsghdrs[n].msg_hdr = (struct msghdr) {
645            .msg_name       = &packs_in->peer_addresses[n],
646            .msg_namelen    = sizeof(packs_in->peer_addresses[n]),
647            .msg_iov        = &packs_in->vecs[n],
648            .msg_iovlen     = 1,
649            .msg_control    = packs_in->ctlmsg_data + CTL_SZ * n,
650            .msg_controllen = CTL_SZ,
651        };
652    }
653
654    /* Read packets */
655    s = recvmmsg(sport->fd, mmsghdrs, n, 0, NULL);
656    if (s < 0)
657    {
658        if (!(EAGAIN == errno || EWOULDBLOCK == errno))
659            LSQ_ERROR("recvmmsg: %s", strerror(errno));
660        return ROP_ERROR;
661    }
662
663    /* Process ancillary data and update vecs */
664    for (n = 0; n < (unsigned) s; ++n)
665    {
666        local_addr = &packs_in->local_addresses[n];
667        memcpy(local_addr, &sport->sp_local_addr, sizeof(*local_addr));
668#if __linux__
669        n_dropped = 0;
670#endif
671#if ECN_SUPPORTED
672        packs_in->ecn[n] = 0;
673#endif
674        proc_ancillary(&mmsghdrs[n].msg_hdr, local_addr
675#if __linux__
676            , &n_dropped
677#endif
678#if ECN_SUPPORTED
679            , &packs_in->ecn[n]
680#endif
681        );
682#if __linux__
683        if (sport->drop_init)
684        {
685            if (sport->n_dropped < n_dropped)
686                LSQ_INFO("dropped %u packets", n_dropped - sport->n_dropped);
687        }
688        else
689            sport->drop_init = 1;
690        sport->n_dropped = n_dropped;
691#endif
692        packs_in->vecs[n].iov_len = mmsghdrs[n].msg_len;
693    }
694
695    iter->ri_idx = n;
696
697    return n == sizeof(mmsghdrs) / sizeof(mmsghdrs[0]) ? ROP_NOROOM : ROP_OK;
698}
699
700
701#endif
702
703
704#if __GNUC__
705#   define UNLIKELY(cond) __builtin_expect(cond, 0)
706#else
707#   define UNLIKELY(cond) cond
708#endif
709
710
711static void
712read_handler (evutil_socket_t fd, short flags, void *ctx)
713{
714    struct service_port *sport = ctx;
715    lsquic_engine_t *const engine = sport->engine;
716    struct packets_in *packs_in = sport->packs_in;
717    struct read_iter iter;
718    unsigned n, n_batches;
719    /* Save the value in case program is stopped packs_in is freed: */
720    const unsigned n_alloc = packs_in->n_alloc;
721    enum rop rop;
722
723    n_batches = 0;
724    iter.ri_sport = sport;
725
726    sport->sp_prog->prog_read_count += 1;
727    do
728    {
729        iter.ri_off = 0;
730        iter.ri_idx = 0;
731
732#if HAVE_RECVMMSG
733        if (sport->sp_prog->prog_use_recvmmsg)
734            rop = read_using_recvmmsg(&iter);
735        else
736#endif
737            do
738                rop = read_one_packet(&iter);
739            while (ROP_OK == rop);
740
741        if (UNLIKELY(ROP_ERROR == rop && (sport->sp_flags & SPORT_CONNECT)
742                                                    && errno == ECONNREFUSED))
743        {
744            LSQ_ERROR("connection refused: exit program");
745            prog_cleanup(sport->sp_prog);
746            exit(1);
747        }
748
749        n_batches += iter.ri_idx > 0;
750
751        for (n = 0; n < iter.ri_idx; ++n)
752            if (0 > lsquic_engine_packet_in(engine,
753#ifndef WIN32
754                        packs_in->vecs[n].iov_base,
755                        packs_in->vecs[n].iov_len,
756#else
757                        (const unsigned char *) packs_in->vecs[n].buf,
758                        packs_in->vecs[n].len,
759#endif
760                        (struct sockaddr *) &packs_in->local_addresses[n],
761                        (struct sockaddr *) &packs_in->peer_addresses[n],
762                        sport,
763#if ECN_SUPPORTED
764                        packs_in->ecn[n]
765#else
766                        0
767#endif
768                        ))
769                break;
770
771        if (n > 0)
772            prog_process_conns(sport->sp_prog);
773    }
774    while (ROP_NOROOM == rop && !prog_is_stopped());
775
776    if (n_batches)
777        n += n_alloc * (n_batches - 1);
778
779    LSQ_DEBUG("read %u packet%.*s in %u batch%s", n, n != 1, "s", n_batches, n_batches != 1 ? "es" : "");
780}
781
782
783static int
784add_to_event_loop (struct service_port *sport, struct event_base *eb)
785{
786    sport->ev = event_new(eb, sport->fd, EV_READ|EV_PERSIST, read_handler,
787                                                                    sport);
788    if (sport->ev)
789    {
790        event_add(sport->ev, NULL);
791        return 0;
792    }
793    else
794        return -1;
795}
796
797
798int
799sport_init_server (struct service_port *sport, struct lsquic_engine *engine,
800                   struct event_base *eb)
801{
802    const struct sockaddr *sa_local = (struct sockaddr *) &sport->sas;
803    int sockfd, saved_errno, s;
804#ifndef WIN32
805    int flags;
806#endif
807    SOCKOPT_VAL on;
808    socklen_t socklen;
809    char addr_str[0x20];
810
811    switch (sa_local->sa_family)
812    {
813    case AF_INET:
814        socklen = sizeof(struct sockaddr_in);
815        break;
816    case AF_INET6:
817        socklen = sizeof(struct sockaddr_in6);
818        break;
819    default:
820        errno = EINVAL;
821        return -1;
822    }
823
824#if WIN32
825    getExtensionPtrs();
826#endif
827    sockfd = socket(sa_local->sa_family, SOCK_DGRAM, 0);
828    if (-1 == sockfd)
829        return -1;
830
831    if (0 != bind(sockfd, sa_local, socklen)) {
832        saved_errno = errno;
833        LSQ_WARN("bind failed: %s", strerror(errno));
834        close(sockfd);
835        errno = saved_errno;
836        return -1;
837    }
838
839    /* Make socket non-blocking */
840#ifndef WIN32
841    flags = fcntl(sockfd, F_GETFL);
842    if (-1 == flags) {
843        saved_errno = errno;
844        close(sockfd);
845        errno = saved_errno;
846        return -1;
847    }
848    flags |= O_NONBLOCK;
849    if (0 != fcntl(sockfd, F_SETFL, flags)) {
850        saved_errno = errno;
851        close(sockfd);
852        errno = saved_errno;
853        return -1;
854    }
855#else
856    {
857        u_long on = 1;
858        ioctlsocket(sockfd, FIONBIO, &on);
859    }
860#endif
861
862    on = 1;
863    if (AF_INET == sa_local->sa_family)
864        s = setsockopt(sockfd, IPPROTO_IP,
865#if __linux__ && defined(IP_RECVORIGDSTADDR)
866                                           IP_RECVORIGDSTADDR,
867#elif __linux__ || __APPLE__ || defined(WIN32)
868                                           IP_PKTINFO,
869#else
870                                           IP_RECVDSTADDR,
871#endif
872                                                               CHAR_CAST &on, sizeof(on));
873    else
874    {
875#ifndef WIN32
876        s = setsockopt(sockfd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
877#else
878        s = setsockopt(sockfd, IPPROTO_IPV6, IPV6_PKTINFO, CHAR_CAST &on, sizeof(on));
879#endif
880    }
881
882    if (0 != s)
883    {
884        saved_errno = errno;
885        close(sockfd);
886        errno = saved_errno;
887        return -1;
888    }
889
890#if (__linux__ && !defined(IP_RECVORIGDSTADDR)) || __APPLE__ || defined(WIN32)
891    /* Need to set IP_PKTINFO for sending */
892    if (AF_INET == sa_local->sa_family)
893    {
894        on = 1;
895        s = setsockopt(sockfd, IPPROTO_IP, IP_PKTINFO, CHAR_CAST &on, sizeof(on));
896        if (0 != s)
897        {
898            saved_errno = errno;
899            close(sockfd);
900            errno = saved_errno;
901            return -1;
902        }
903    }
904#elif IP_RECVDSTADDR != IP_SENDSRCADDR
905    /* On FreeBSD, IP_RECVDSTADDR is the same as IP_SENDSRCADDR, but I do not
906     * know about other BSD systems.
907     */
908    if (AF_INET == sa_local->sa_family)
909    {
910        on = 1;
911        s = setsockopt(sockfd, IPPROTO_IP, IP_SENDSRCADDR, &on, sizeof(on));
912        if (0 != s)
913        {
914            saved_errno = errno;
915            close(sockfd);
916            errno = saved_errno;
917            return -1;
918        }
919    }
920#endif
921
922#if __linux__ && defined(SO_RXQ_OVFL)
923    on = 1;
924    s = setsockopt(sockfd, SOL_SOCKET, SO_RXQ_OVFL, &on, sizeof(on));
925    if (0 != s)
926    {
927        saved_errno = errno;
928        close(sockfd);
929        errno = saved_errno;
930        return -1;
931    }
932#endif
933
934#if __linux__
935    if (sport->if_name[0] &&
936        0 != setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, sport->if_name,
937                                                               IFNAMSIZ))
938    {
939        saved_errno = errno;
940        close(sockfd);
941        errno = saved_errno;
942        return -1;
943    }
944#endif
945
946#if LSQUIC_DONTFRAG_SUPPORTED
947    if (!(sport->sp_flags & SPORT_FRAGMENT_OK))
948    {
949        if (AF_INET == sa_local->sa_family)
950        {
951#if __linux__
952            on = IP_PMTUDISC_PROBE;
953            s = setsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &on,
954                                                                sizeof(on));
955#else
956            on = 1;
957            s = setsockopt(sockfd, IPPROTO_IP, IP_DONTFRAG, CHAR_CAST &on, sizeof(on));
958#endif
959            if (0 != s)
960            {
961                saved_errno = errno;
962                close(sockfd);
963                errno = saved_errno;
964                return -1;
965            }
966        }
967    }
968#endif
969
970#if ECN_SUPPORTED
971    on = 1;
972    if (AF_INET == sa_local->sa_family)
973        s = setsockopt(sockfd, IPPROTO_IP, IP_RECVTOS, CHAR_CAST &on, sizeof(on));
974    else
975        s = setsockopt(sockfd, IPPROTO_IPV6, IPV6_RECVTCLASS, CHAR_CAST &on, sizeof(on));
976    if (0 != s)
977    {
978        saved_errno = errno;
979        close(sockfd);
980        errno = saved_errno;
981        return -1;
982    }
983#endif
984
985    if (sport->sp_flags & SPORT_SET_SNDBUF)
986    {
987        s = setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, CHAR_CAST &sport->sp_sndbuf,
988                                                    sizeof(sport->sp_sndbuf));
989        if (0 != s)
990        {
991            saved_errno = errno;
992            close(sockfd);
993            errno = saved_errno;
994            return -1;
995        }
996    }
997
998    if (sport->sp_flags & SPORT_SET_RCVBUF)
999    {
1000        s = setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, CHAR_CAST &sport->sp_rcvbuf,
1001                                                    sizeof(sport->sp_rcvbuf));
1002        if (0 != s)
1003        {
1004            saved_errno = errno;
1005            close(sockfd);
1006            errno = saved_errno;
1007            return -1;
1008        }
1009    }
1010
1011    if (0 != getsockname(sockfd, (struct sockaddr *) sa_local, &socklen))
1012    {
1013        saved_errno = errno;
1014        close(sockfd);
1015        errno = saved_errno;
1016        return -1;
1017    }
1018
1019    sport->packs_in = allocate_packets_in(sockfd);
1020    if (!sport->packs_in)
1021    {
1022        saved_errno = errno;
1023        close(sockfd);
1024        errno = saved_errno;
1025        return -1;
1026    }
1027
1028    memcpy((void *) &sport->sp_local_addr, sa_local,
1029        sa_local->sa_family == AF_INET ?
1030        sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
1031    switch (sa_local->sa_family) {
1032    case AF_INET:
1033        LSQ_DEBUG("local address: %s:%d",
1034            inet_ntop(AF_INET, &((struct sockaddr_in *) sa_local)->sin_addr,
1035            addr_str, sizeof(addr_str)),
1036            ntohs(((struct sockaddr_in *) sa_local)->sin_port));
1037        break;
1038    }
1039
1040    sport->engine = engine;
1041    sport->fd = sockfd;
1042    sport->sp_flags |= SPORT_SERVER;
1043
1044    return add_to_event_loop(sport, eb);
1045}
1046
1047
1048int
1049sport_init_client (struct service_port *sport, struct lsquic_engine *engine,
1050                   struct event_base *eb)
1051{
1052    const struct sockaddr *sa_peer = (struct sockaddr *) &sport->sas;
1053    int saved_errno, s;
1054#ifndef WIN32
1055    int flags;
1056#endif
1057    SOCKET_TYPE sockfd;
1058    socklen_t socklen, peer_socklen;
1059    union {
1060        struct sockaddr_in  sin;
1061        struct sockaddr_in6 sin6;
1062    } u;
1063    struct sockaddr *sa_local = (struct sockaddr *) &u;
1064    char addr_str[0x20];
1065
1066    switch (sa_peer->sa_family)
1067    {
1068    case AF_INET:
1069        socklen = sizeof(struct sockaddr_in);
1070        u.sin.sin_family      = AF_INET;
1071        u.sin.sin_addr.s_addr = INADDR_ANY;
1072        u.sin.sin_port        = 0;
1073        break;
1074    case AF_INET6:
1075        socklen = sizeof(struct sockaddr_in6);
1076        memset(&u.sin6, 0, sizeof(u.sin6));
1077        u.sin6.sin6_family = AF_INET6;
1078        break;
1079    default:
1080        errno = EINVAL;
1081        return -1;
1082    }
1083
1084#if WIN32
1085    getExtensionPtrs();
1086#endif
1087    sockfd = socket(sa_peer->sa_family, SOCK_DGRAM, 0);
1088    if (-1 == sockfd)
1089        return -1;
1090
1091    if (0 != bind(sockfd, sa_local, socklen)) {
1092        saved_errno = errno;
1093        CLOSE_SOCKET(sockfd);
1094        errno = saved_errno;
1095        return -1;
1096    }
1097
1098    if (sport->sp_flags & SPORT_CONNECT)
1099    {
1100        peer_socklen = AF_INET == sa_peer->sa_family
1101                    ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
1102        if (0 != connect(sockfd, sa_peer, peer_socklen))
1103        {
1104            saved_errno = errno;
1105            CLOSE_SOCKET(sockfd);
1106            errno = saved_errno;
1107            return -1;
1108        }
1109    }
1110
1111    /* Make socket non-blocking */
1112#ifndef WIN32
1113    flags = fcntl(sockfd, F_GETFL);
1114    if (-1 == flags) {
1115        saved_errno = errno;
1116        CLOSE_SOCKET(sockfd);
1117        errno = saved_errno;
1118        return -1;
1119    }
1120    flags |= O_NONBLOCK;
1121    if (0 != fcntl(sockfd, F_SETFL, flags)) {
1122        saved_errno = errno;
1123        CLOSE_SOCKET(sockfd);
1124        errno = saved_errno;
1125        return -1;
1126    }
1127#else
1128    {
1129        u_long on = 1;
1130        ioctlsocket(sockfd, FIONBIO, &on);
1131    }
1132#endif
1133
1134#if LSQUIC_DONTFRAG_SUPPORTED
1135    if (!(sport->sp_flags & SPORT_FRAGMENT_OK))
1136    {
1137        if (AF_INET == sa_local->sa_family)
1138        {
1139        int on;
1140#if __linux__
1141            on = IP_PMTUDISC_PROBE;
1142            s = setsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &on,
1143                                                                sizeof(on));
1144#elif WIN32
1145            on = 1;
1146            s = setsockopt(sockfd, IPPROTO_IP, IP_DONTFRAGMENT, CHAR_CAST &on, sizeof(on));
1147#else
1148            on = 1;
1149            s = setsockopt(sockfd, IPPROTO_IP, IP_DONTFRAG, &on, sizeof(on));
1150#endif
1151            if (0 != s)
1152            {
1153                saved_errno = errno;
1154                CLOSE_SOCKET(sockfd);
1155                errno = saved_errno;
1156                return -1;
1157            }
1158        }
1159    }
1160#endif
1161
1162#if ECN_SUPPORTED
1163    {
1164        int on = 1;
1165        if (AF_INET == sa_local->sa_family)
1166            s = setsockopt(sockfd, IPPROTO_IP, IP_RECVTOS,
1167                                            CHAR_CAST &on, sizeof(on));
1168        else
1169            s = setsockopt(sockfd, IPPROTO_IPV6, IPV6_RECVTCLASS,
1170                                            CHAR_CAST &on, sizeof(on));
1171        if (0 != s)
1172        {
1173            saved_errno = errno;
1174            close(sockfd);
1175            errno = saved_errno;
1176            return -1;
1177        }
1178    }
1179#endif
1180
1181    if (sport->sp_flags & SPORT_SET_SNDBUF)
1182    {
1183        s = setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF,
1184                       CHAR_CAST &sport->sp_sndbuf, sizeof(sport->sp_sndbuf));
1185        if (0 != s)
1186        {
1187            saved_errno = errno;
1188            CLOSE_SOCKET(sockfd);
1189            errno = saved_errno;
1190            return -1;
1191        }
1192    }
1193
1194    if (sport->sp_flags & SPORT_SET_RCVBUF)
1195    {
1196        s = setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
1197                       CHAR_CAST &sport->sp_rcvbuf, sizeof(sport->sp_rcvbuf));
1198        if (0 != s)
1199        {
1200            saved_errno = errno;
1201            CLOSE_SOCKET(sockfd);
1202            errno = saved_errno;
1203            return -1;
1204        }
1205    }
1206
1207    if (0 != getsockname(sockfd, sa_local, &socklen))
1208    {
1209        saved_errno = errno;
1210        CLOSE_SOCKET(sockfd);
1211        errno = saved_errno;
1212        return -1;
1213    }
1214
1215    sport->packs_in = allocate_packets_in(sockfd);
1216    if (!sport->packs_in)
1217    {
1218        saved_errno = errno;
1219        CLOSE_SOCKET(sockfd);
1220        errno = saved_errno;
1221        return -1;
1222    }
1223
1224    memcpy((void *) &sport->sp_local_addr, sa_local,
1225        sa_local->sa_family == AF_INET ?
1226        sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
1227    switch (sa_local->sa_family) {
1228    case AF_INET:
1229        LSQ_DEBUG("local address: %s:%d",
1230            inet_ntop(AF_INET, &u.sin.sin_addr, addr_str, sizeof(addr_str)),
1231            ntohs(u.sin.sin_port));
1232        break;
1233    }
1234
1235    sport->engine = engine;
1236    sport->fd = sockfd;
1237
1238    return add_to_event_loop(sport, eb);
1239}
1240
1241
1242/* Sometimes it is useful to impose an artificial limit for testing */
1243static unsigned
1244packet_out_limit (void)
1245{
1246    const char *env = getenv("LSQUIC_PACKET_OUT_LIMIT");
1247    if (env)
1248        return atoi(env);
1249    else
1250        return 0;
1251}
1252
1253
1254enum ctl_what
1255{
1256    CW_SENDADDR     = 1 << 0,
1257#if ECN_SUPPORTED
1258    CW_ECN          = 1 << 1,
1259#endif
1260};
1261
1262static void
1263setup_control_msg (
1264#ifndef WIN32
1265                   struct msghdr
1266#else
1267                   WSAMSG
1268#endif
1269                                 *msg, enum ctl_what cw,
1270        const struct lsquic_out_spec *spec, unsigned char *buf, size_t bufsz)
1271{
1272    struct cmsghdr *cmsg;
1273    struct sockaddr_in *local_sa;
1274    struct sockaddr_in6 *local_sa6;
1275#if __linux__ || __APPLE__ || WIN32
1276    struct in_pktinfo info;
1277#endif
1278    struct in6_pktinfo info6;
1279    size_t ctl_len;
1280
1281#ifndef WIN32
1282    msg->msg_control    = buf;
1283    msg->msg_controllen = bufsz;
1284#else
1285    msg->Control.buf    = (char*)buf;
1286    msg->Control.len = bufsz;
1287#endif
1288
1289    /* Need to zero the buffer due to a bug(?) in CMSG_NXTHDR.  See
1290     * https://stackoverflow.com/questions/27601849/cmsg-nxthdr-returns-null-even-though-there-are-more-cmsghdr-objects
1291     */
1292    memset(buf, 0, bufsz);
1293
1294    ctl_len = 0;
1295    for (cmsg = CMSG_FIRSTHDR(msg); cw && cmsg; cmsg = CMSG_NXTHDR(msg, cmsg))
1296    {
1297        if (cw & CW_SENDADDR)
1298        {
1299            if (AF_INET == spec->dest_sa->sa_family)
1300            {
1301                local_sa = (struct sockaddr_in *) spec->local_sa;
1302#if __linux__ || __APPLE__
1303                memset(&info, 0, sizeof(info));
1304                info.ipi_spec_dst = local_sa->sin_addr;
1305                cmsg->cmsg_level    = IPPROTO_IP;
1306                cmsg->cmsg_type     = IP_PKTINFO;
1307                cmsg->cmsg_len      = CMSG_LEN(sizeof(info));
1308                ctl_len += CMSG_SPACE(sizeof(info));
1309                memcpy(CMSG_DATA(cmsg), &info, sizeof(info));
1310#elif WIN32
1311                memset(&info, 0, sizeof(info));
1312                info.ipi_addr = local_sa->sin_addr;
1313                cmsg->cmsg_level    = IPPROTO_IP;
1314                cmsg->cmsg_type     = IP_PKTINFO;
1315                cmsg->cmsg_len      = CMSG_LEN(sizeof(info));
1316                ctl_len += CMSG_SPACE(sizeof(info));
1317                memcpy(WSA_CMSG_DATA(cmsg), &info, sizeof(info));
1318#else
1319                cmsg->cmsg_level    = IPPROTO_IP;
1320                cmsg->cmsg_type     = IP_SENDSRCADDR;
1321                cmsg->cmsg_len      = CMSG_LEN(sizeof(local_sa->sin_addr));
1322                ctl_len += CMSG_SPACE(sizeof(local_sa->sin_addr));
1323                memcpy(CMSG_DATA(cmsg), &local_sa->sin_addr,
1324                                                    sizeof(local_sa->sin_addr));
1325#endif
1326            }
1327            else
1328            {
1329                local_sa6 = (struct sockaddr_in6 *) spec->local_sa;
1330                memset(&info6, 0, sizeof(info6));
1331                info6.ipi6_addr = local_sa6->sin6_addr;
1332                cmsg->cmsg_level    = IPPROTO_IPV6;
1333                cmsg->cmsg_type     = IPV6_PKTINFO;
1334                cmsg->cmsg_len      = CMSG_LEN(sizeof(info6));
1335#ifndef WIN32
1336                memcpy(CMSG_DATA(cmsg), &info6, sizeof(info6));
1337#else
1338                memcpy(WSA_CMSG_DATA(cmsg), &info6, sizeof(info6));
1339#endif
1340                ctl_len += CMSG_SPACE(sizeof(info6));
1341            }
1342            cw &= ~CW_SENDADDR;
1343        }
1344#if ECN_SUPPORTED
1345        else if (cw & CW_ECN)
1346        {
1347            if (AF_INET == spec->dest_sa->sa_family)
1348            {
1349                const
1350#if defined(__FreeBSD__)
1351                      unsigned char
1352#else
1353                      int
1354#endif
1355                                    tos = spec->ecn;
1356                cmsg->cmsg_level = IPPROTO_IP;
1357                cmsg->cmsg_type  = IP_TOS;
1358                cmsg->cmsg_len   = CMSG_LEN(sizeof(tos));
1359                memcpy(CMSG_DATA(cmsg), &tos, sizeof(tos));
1360                ctl_len += CMSG_SPACE(sizeof(tos));
1361            }
1362            else
1363            {
1364                const int tos = spec->ecn;
1365                cmsg->cmsg_level = IPPROTO_IPV6;
1366                cmsg->cmsg_type  = IPV6_TCLASS;
1367                cmsg->cmsg_len   = CMSG_LEN(sizeof(tos));
1368                memcpy(CMSG_DATA(cmsg), &tos, sizeof(tos));
1369                ctl_len += CMSG_SPACE(sizeof(tos));
1370            }
1371            cw &= ~CW_ECN;
1372        }
1373#endif
1374        else
1375            assert(0);
1376    }
1377
1378#ifndef WIN32
1379    msg->msg_controllen = ctl_len;
1380#else
1381    msg->Control.len = ctl_len;
1382#endif
1383}
1384
1385
1386#if HAVE_SENDMMSG
1387static int
1388send_packets_using_sendmmsg (const struct lsquic_out_spec *specs,
1389                                                        unsigned count)
1390{
1391#ifndef NDEBUG
1392    {
1393        /* This only works for a single port!  If the specs contain more
1394         * than one socket, this function does *NOT* work.  We check it
1395         * here just in case:
1396         */
1397        void *ctx;
1398        unsigned i;
1399        for (i = 1, ctx = specs[i].peer_ctx;
1400                i < count;
1401                    ctx = specs[i].peer_ctx, ++i)
1402            assert(ctx == specs[i - 1].peer_ctx);
1403    }
1404#endif
1405
1406    const struct service_port *const sport = specs[0].peer_ctx;
1407    const int fd = sport->fd;
1408    enum ctl_what cw;
1409    unsigned i;
1410    int s, saved_errno;
1411    uintptr_t ancil_key, prev_ancil_key;
1412    struct mmsghdr mmsgs[1024];
1413    union {
1414        /* cmsg(3) recommends union for proper alignment */
1415        unsigned char buf[ CMSG_SPACE(
1416            MAX(
1417#if __linux__
1418                                        sizeof(struct in_pktinfo)
1419#else
1420                                        sizeof(struct in_addr)
1421#endif
1422                                        , sizeof(struct in6_pktinfo))
1423                                                                  )
1424#if ECN_SUPPORTED
1425            + CMSG_SPACE(sizeof(int))
1426#endif
1427                                                                    ];
1428        struct cmsghdr cmsg;
1429    } ancil [ sizeof(mmsgs) / sizeof(mmsgs[0]) ];
1430
1431    prev_ancil_key = 0;
1432    for (i = 0; i < count && i < sizeof(mmsgs) / sizeof(mmsgs[0]); ++i)
1433    {
1434        mmsgs[i].msg_hdr.msg_name       = (void *) specs[i].dest_sa;
1435        mmsgs[i].msg_hdr.msg_namelen    = (AF_INET == specs[i].dest_sa->sa_family ?
1436                                            sizeof(struct sockaddr_in) :
1437                                            sizeof(struct sockaddr_in6)),
1438        mmsgs[i].msg_hdr.msg_iov        = specs[i].iov;
1439        mmsgs[i].msg_hdr.msg_iovlen     = specs[i].iovlen;
1440        mmsgs[i].msg_hdr.msg_flags      = 0;
1441        if ((sport->sp_flags & SPORT_SERVER) && specs[i].local_sa->sa_family)
1442        {
1443            cw = CW_SENDADDR;
1444            ancil_key = (uintptr_t) specs[i].local_sa;
1445            assert(0 == (ancil_key & 3));
1446        }
1447        else
1448        {
1449            cw = 0;
1450            ancil_key = 0;
1451        }
1452#if ECN_SUPPORTED
1453        if (sport->sp_prog->prog_api.ea_settings->es_ecn && specs[i].ecn)
1454        {
1455            cw |= CW_ECN;
1456            ancil_key |= specs[i].ecn;
1457        }
1458#endif
1459        if (cw && prev_ancil_key == ancil_key)
1460        {
1461            /* Reuse previous ancillary message */
1462            assert(i > 0);
1463#ifndef WIN32
1464            mmsgs[i].msg_hdr.msg_control    = mmsgs[i - 1].msg_hdr.msg_control;
1465            mmsgs[i].msg_hdr.msg_controllen = mmsgs[i - 1].msg_hdr.msg_controllen;
1466#else
1467            mmsgs[i].msg_hdr.Control.buf    = mmsgs[i - 1].msg_hdr.Control.buf;
1468            mmsgs[i].msg_hdr.Control.len    = mmsgs[i - 1].msg_hdr.Control.len;
1469#endif
1470        }
1471        else if (cw)
1472        {
1473            prev_ancil_key = ancil_key;
1474            setup_control_msg(&mmsgs[i].msg_hdr, cw, &specs[i], ancil[i].buf,
1475                                                    sizeof(ancil[i].buf));
1476        }
1477        else
1478        {
1479            prev_ancil_key = 0;
1480#ifndef WIN32
1481            mmsgs[i].msg_hdr.msg_control    = NULL;
1482            mmsgs[i].msg_hdr.msg_controllen = 0;
1483#else
1484            mmsgs[i].msg_hdr.Control.buf    = NULL;
1485            mmsgs[i].msg_hdr.Control.len    = 0;
1486#endif
1487        }
1488    }
1489
1490    s = sendmmsg(fd, mmsgs, count, 0);
1491    if (s < (int) count)
1492    {
1493        saved_errno = errno;
1494        prog_sport_cant_send(sport->sp_prog, sport->fd);
1495        if (s < 0)
1496        {
1497            LSQ_WARN("sendmmsg failed: %s", strerror(saved_errno));
1498            errno = saved_errno;
1499        }
1500        else if (s > 0)
1501            errno = EAGAIN;
1502        else
1503            errno = saved_errno;
1504    }
1505
1506    return s;
1507}
1508
1509
1510#endif
1511
1512
1513#if LSQUIC_PREFERRED_ADDR
1514static const struct service_port *
1515find_sport (struct prog *prog, const struct sockaddr *local_sa)
1516{
1517    const struct service_port *sport;
1518    const struct sockaddr *addr;
1519    size_t len;
1520
1521    TAILQ_FOREACH(sport, prog->prog_sports, next_sport)
1522    {
1523        addr = (struct sockaddr *) &sport->sp_local_addr;
1524        if (addr->sa_family == local_sa->sa_family)
1525        {
1526            len = addr->sa_family == AF_INET ? sizeof(struct sockaddr_in)
1527                                             : sizeof(struct sockaddr_in6);
1528            if (0 == memcmp(addr, local_sa, len))
1529                return sport;
1530        }
1531    }
1532
1533    assert(0);
1534    return NULL;
1535}
1536
1537
1538#endif
1539
1540
1541static int
1542send_packets_one_by_one (const struct lsquic_out_spec *specs, unsigned count)
1543{
1544    const struct service_port *sport;
1545    enum ctl_what cw;
1546    unsigned n;
1547    int s = 0;
1548#ifndef WIN32
1549    struct msghdr msg;
1550#else
1551    DWORD bytes;
1552    WSAMSG msg;
1553    LPWSABUF pWsaBuf = NULL;
1554#endif
1555    union {
1556        /* cmsg(3) recommends union for proper alignment */
1557#if __linux__ || WIN32
1558#	define SIZE1 sizeof(struct in_pktinfo)
1559#else
1560#	define SIZE1 sizeof(struct in_addr)
1561#endif
1562        unsigned char buf[
1563            CMSG_SPACE(MAX(SIZE1, sizeof(struct in6_pktinfo)))
1564#if ECN_SUPPORTED
1565            + CMSG_SPACE(sizeof(int))
1566#endif
1567        ];
1568        struct cmsghdr cmsg;
1569    } ancil;
1570    uintptr_t ancil_key, prev_ancil_key;
1571
1572    if (0 == count)
1573        return 0;
1574
1575    const unsigned orig_count = count;
1576    const unsigned out_limit = packet_out_limit();
1577    if (out_limit && count > out_limit)
1578        count = out_limit;
1579#if LSQUIC_RANDOM_SEND_FAILURE
1580    {
1581        const char *freq_str = getenv("LSQUIC_RANDOM_SEND_FAILURE");
1582        int freq;
1583        if (freq_str)
1584            freq = atoi(freq_str);
1585        else
1586            freq = 10;
1587        if (rand() % freq == 0)
1588        {
1589            assert(count > 0);
1590            sport = specs[0].peer_ctx;
1591            LSQ_NOTICE("sending \"randomly\" fails");
1592            prog_sport_cant_send(sport->sp_prog, sport->fd);
1593            goto random_send_failure;
1594        }
1595    }
1596#endif
1597
1598    n = 0;
1599    prev_ancil_key = 0;
1600#ifdef WIN32
1601    #define MAX_OUT_BATCH_SIZE 1024
1602    pWsaBuf = malloc(sizeof(*pWsaBuf)*MAX_OUT_BATCH_SIZE*2);
1603    if (NULL == pWsaBuf) {
1604        return -1;
1605    }
1606#endif
1607
1608    do
1609    {
1610        sport = specs[n].peer_ctx;
1611#if LSQUIC_PREFERRED_ADDR
1612        if (sport->sp_prog->prog_flags & PROG_SEARCH_ADDRS)
1613            sport = find_sport(sport->sp_prog, specs[n].local_sa);
1614#endif
1615#ifndef WIN32
1616        msg.msg_name       = (void *) specs[n].dest_sa;
1617        msg.msg_namelen    = (AF_INET == specs[n].dest_sa->sa_family ?
1618                                            sizeof(struct sockaddr_in) :
1619                                            sizeof(struct sockaddr_in6)),
1620        msg.msg_iov        = specs[n].iov;
1621        msg.msg_iovlen     = specs[n].iovlen;
1622        msg.msg_flags      = 0;
1623#else
1624        for (int i = 0; i < specs[n].iovlen; i++)
1625        {
1626            pWsaBuf[i].buf = specs[n].iov[i].iov_base;
1627            pWsaBuf[i].len = specs[n].iov[i].iov_len;
1628        }
1629        msg.name           = (void *) specs[n].dest_sa;
1630        msg.namelen        = (AF_INET == specs[n].dest_sa->sa_family ?
1631                                            sizeof(struct sockaddr_in) :
1632                                            sizeof(struct sockaddr_in6));
1633        msg.dwBufferCount  = specs[n].iovlen;
1634        msg.lpBuffers      = pWsaBuf;
1635        msg.dwFlags        = 0;
1636#endif
1637        if ((sport->sp_flags & SPORT_SERVER) && specs[n].local_sa->sa_family)
1638        {
1639            cw = CW_SENDADDR;
1640            ancil_key = (uintptr_t) specs[n].local_sa;
1641            assert(0 == (ancil_key & 3));
1642        }
1643        else
1644        {
1645            cw = 0;
1646            ancil_key = 0;
1647        }
1648#if ECN_SUPPORTED
1649        if (sport->sp_prog->prog_api.ea_settings->es_ecn && specs[n].ecn)
1650        {
1651            cw |= CW_ECN;
1652            ancil_key |= specs[n].ecn;
1653        }
1654#endif
1655        if (cw && prev_ancil_key == ancil_key)
1656        {
1657            /* Reuse previous ancillary message */
1658            ;
1659        }
1660        else if (cw)
1661        {
1662            prev_ancil_key = ancil_key;
1663            setup_control_msg(&msg, cw, &specs[n], ancil.buf, sizeof(ancil.buf));
1664        }
1665        else
1666        {
1667            prev_ancil_key = 0;
1668#ifndef WIN32
1669            msg.msg_control = NULL;
1670            msg.msg_controllen = 0;
1671#else
1672            msg.Control.buf = NULL;
1673            msg.Control.len = 0;
1674#endif
1675        }
1676#ifndef WIN32
1677        s = sendmsg(sport->fd, &msg, 0);
1678#else
1679        s = pfnWSASendMsg(sport->fd, &msg, 0, &bytes, NULL, NULL);
1680#endif
1681        if (s < 0)
1682        {
1683#ifndef WIN32
1684            LSQ_INFO("sendto failed: %s", strerror(errno));
1685#else
1686            LSQ_INFO("sendto failed: %s", WSAGetLastError());
1687#endif
1688            break;
1689        }
1690        ++n;
1691    }
1692    while (n < count);
1693
1694    if (n < orig_count)
1695        prog_sport_cant_send(sport->sp_prog, sport->fd);
1696
1697#ifdef WIN32
1698    if (NULL != pWsaBuf) {
1699        free(pWsaBuf);
1700        pWsaBuf = NULL;
1701    }
1702#endif
1703
1704    if (n > 0)
1705    {
1706        if (n < orig_count && out_limit)
1707            errno = EAGAIN;
1708        return n;
1709    }
1710    else
1711    {
1712        assert(s < 0);
1713#if LSQUIC_RANDOM_SEND_FAILURE
1714  random_send_failure:
1715#endif
1716        return -1;
1717    }
1718}
1719
1720
1721int
1722sport_packets_out (void *ctx, const struct lsquic_out_spec *specs,
1723                   unsigned count)
1724{
1725#if HAVE_SENDMMSG
1726    const struct prog *prog = ctx;
1727    if (prog->prog_use_sendmmsg)
1728        return send_packets_using_sendmmsg(specs, count);
1729    else
1730#endif
1731        return send_packets_one_by_one(specs, count);
1732}
1733
1734
1735int
1736set_engine_option (struct lsquic_engine_settings *settings,
1737                   int *version_cleared, const char *name)
1738{
1739    int len;
1740    const char *val = strchr(name, '=');
1741    if (!val)
1742        return -1;
1743    len = val - name;
1744    ++val;
1745
1746    switch (len)
1747    {
1748    case 2:
1749        if (0 == strncmp(name, "ua", 2))
1750        {
1751            settings->es_ua = val;
1752            return 0;
1753        }
1754        break;
1755    case 3:
1756        if (0 == strncmp(name, "ecn", 1))
1757        {
1758            settings->es_ecn = atoi(val);
1759#if !ECN_SUPPORTED
1760            if (settings->es_ecn)
1761            {
1762                LSQ_ERROR("ECN is not supported on this platform");
1763                break;
1764            }
1765#endif
1766            return 0;
1767        }
1768        break;
1769    case 4:
1770        if (0 == strncmp(name, "cfcw", 4))
1771        {
1772            settings->es_cfcw = atoi(val);
1773            return 0;
1774        }
1775        if (0 == strncmp(name, "sfcw", 4))
1776        {
1777            settings->es_sfcw = atoi(val);
1778            return 0;
1779        }
1780        if (0 == strncmp(name, "spin", 4))
1781        {
1782            settings->es_spin = atoi(val);
1783            return 0;
1784        }
1785        break;
1786    case 7:
1787        if (0 == strncmp(name, "version", 7))
1788        {
1789            if (!*version_cleared)
1790            {
1791                *version_cleared = 1;
1792                settings->es_versions = 0;
1793            }
1794            enum lsquic_version ver = lsquic_str2ver(val, strlen(val));
1795            if ((unsigned) ver < N_LSQVER)
1796            {
1797                settings->es_versions |= 1 << ver;
1798                return 0;
1799            }
1800            ver = lsquic_alpn2ver(val, strlen(val));
1801            if ((unsigned) ver < N_LSQVER)
1802            {
1803                settings->es_versions |= 1 << ver;
1804                return 0;
1805            }
1806        }
1807        else if (0 == strncmp(name, "rw_once", 7))
1808        {
1809            settings->es_rw_once = atoi(val);
1810            return 0;
1811        }
1812        else if (0 == strncmp(name, "cc_algo", 7))
1813        {
1814            settings->es_cc_algo = atoi(val);
1815            return 0;
1816        }
1817        else if (0 == strncmp(name, "ql_bits", 7))
1818        {
1819            settings->es_ql_bits = atoi(val);
1820            return 0;
1821        }
1822        break;
1823    case 8:
1824        if (0 == strncmp(name, "max_cfcw", 8))
1825        {
1826            settings->es_max_cfcw = atoi(val);
1827            return 0;
1828        }
1829        if (0 == strncmp(name, "max_sfcw", 8))
1830        {
1831            settings->es_max_sfcw = atoi(val);
1832            return 0;
1833        }
1834        if (0 == strncmp(name, "scid_len", 8))
1835        {
1836            settings->es_scid_len = atoi(val);
1837            return 0;
1838        }
1839        if (0 == strncmp(name, "dplpmtud", 8))
1840        {
1841            settings->es_dplpmtud = atoi(val);
1842            return 0;
1843        }
1844        break;
1845    case 9:
1846        if (0 == strncmp(name, "send_prst", 9))
1847        {
1848            settings->es_send_prst = atoi(val);
1849            return 0;
1850        }
1851        break;
1852    case 10:
1853        if (0 == strncmp(name, "honor_prst", 10))
1854        {
1855            settings->es_honor_prst = atoi(val);
1856            return 0;
1857        }
1858        if (0 == strncmp(name, "timestamps", 10))
1859        {
1860            settings->es_timestamps = atoi(val);
1861            return 0;
1862        }
1863        if (0 == strncmp(name, "max_plpmtu", 10))
1864        {
1865            settings->es_max_plpmtu = atoi(val);
1866            return 0;
1867        }
1868        break;
1869    case 11:
1870        if (0 == strncmp(name, "ping_period", 11))
1871        {
1872            settings->es_ping_period = atoi(val);
1873            return 0;
1874        }
1875        if (0 == strncmp(name, "base_plpmtu", 11))
1876        {
1877            settings->es_base_plpmtu = atoi(val);
1878            return 0;
1879        }
1880        if (0 == strncmp(name, "ptpc_target", 11))
1881        {
1882            settings->es_ptpc_target = atof(val);
1883            return 0;
1884        }
1885        break;
1886    case 12:
1887        if (0 == strncmp(name, "idle_conn_to", 12))
1888        {
1889            settings->es_idle_conn_to = atoi(val);
1890            return 0;
1891        }
1892        if (0 == strncmp(name, "idle_timeout", 12))
1893        {
1894            settings->es_idle_timeout = atoi(val);
1895            return 0;
1896        }
1897        if (0 == strncmp(name, "silent_close", 12))
1898        {
1899            settings->es_silent_close = atoi(val);
1900            return 0;
1901        }
1902        if (0 == strncmp(name, "support_push", 12))
1903        {
1904            settings->es_support_push = atoi(val);
1905            return 0;
1906        }
1907        if (0 == strncmp(name, "support_nstp", 12))
1908        {
1909            settings->es_support_nstp = atoi(val);
1910            return 0;
1911        }
1912        if (0 == strncmp(name, "pace_packets", 12))
1913        {
1914            settings->es_pace_packets = atoi(val);
1915            return 0;
1916        }
1917        if (0 == strncmp(name, "handshake_to", 12))
1918        {
1919            settings->es_handshake_to = atoi(val);
1920            return 0;
1921        }
1922        if (0 == strncmp(name, "delayed_acks", 12))
1923        {
1924            settings->es_delayed_acks = atoi(val);
1925            return 0;
1926        }
1927        break;
1928    case 13:
1929        if (0 == strncmp(name, "support_tcid0", 13))
1930        {
1931            settings->es_support_tcid0 = atoi(val);
1932            return 0;
1933        }
1934        if (0 == strncmp(name, "init_max_data", 13))
1935        {
1936            settings->es_init_max_data = atoi(val);
1937            return 0;
1938        }
1939        if (0 == strncmp(name, "scid_iss_rate", 13))
1940        {
1941            settings->es_scid_iss_rate = atoi(val);
1942            return 0;
1943        }
1944        if (0 == strncmp(name, "ext_http_prio", 13))
1945        {
1946            settings->es_ext_http_prio = atoi(val);
1947            return 0;
1948        }
1949        if (0 == strncmp(name, "ptpc_int_gain", 13))
1950        {
1951            settings->es_ptpc_int_gain = atof(val);
1952            return 0;
1953        }
1954        if (0 == strncmp(name, "delay_onclose", 13))
1955        {
1956            settings->es_delay_onclose = atoi(val);
1957            return 0;
1958        }
1959        break;
1960    case 14:
1961        if (0 == strncmp(name, "max_streams_in", 14))
1962        {
1963            settings->es_max_streams_in = atoi(val);
1964            return 0;
1965        }
1966        if (0 == strncmp(name, "progress_check", 14))
1967        {
1968            settings->es_progress_check = atoi(val);
1969            return 0;
1970        }
1971        if (0 == strncmp(name, "ptpc_prop_gain", 14))
1972        {
1973            settings->es_ptpc_prop_gain = atof(val);
1974            return 0;
1975        }
1976        if (0 == strncmp(name, "max_batch_size", 14))
1977        {
1978            settings->es_max_batch_size = atoi(val);
1979            return 0;
1980        }
1981        break;
1982    case 15:
1983        if (0 == strncmp(name, "allow_migration", 15))
1984        {
1985            settings->es_allow_migration = atoi(val);
1986            return 0;
1987        }
1988        if (0 == strncmp(name, "grease_quic_bit", 15))
1989        {
1990            settings->es_grease_quic_bit = atoi(val);
1991            return 0;
1992        }
1993        if (0 == strncmp(name, "ptpc_dyn_target", 15))
1994        {
1995            settings->es_ptpc_dyn_target = atoi(val);
1996            return 0;
1997        }
1998        if (0 == strncmp(name, "ptpc_err_thresh", 15))
1999        {
2000            settings->es_ptpc_err_thresh = atof(val);
2001            return 0;
2002        }
2003        break;
2004    case 16:
2005        if (0 == strncmp(name, "proc_time_thresh", 16))
2006        {
2007            settings->es_proc_time_thresh = atoi(val);
2008            return 0;
2009        }
2010        if (0 == strncmp(name, "qpack_experiment", 16))
2011        {
2012            settings->es_qpack_experiment = atoi(val);
2013            return 0;
2014        }
2015        if (0 == strncmp(name, "ptpc_periodicity", 16))
2016        {
2017            settings->es_ptpc_periodicity = atoi(val);
2018            return 0;
2019        }
2020        if (0 == strncmp(name, "ptpc_max_packtol", 16))
2021        {
2022            settings->es_ptpc_max_packtol = atoi(val);
2023            return 0;
2024        }
2025        if (0 == strncmp(name, "ptpc_err_divisor", 16))
2026        {
2027            settings->es_ptpc_err_divisor = atof(val);
2028            return 0;
2029        }
2030        break;
2031    case 18:
2032        if (0 == strncmp(name, "qpack_enc_max_size", 18))
2033        {
2034            settings->es_qpack_enc_max_size = atoi(val);
2035            return 0;
2036        }
2037        if (0 == strncmp(name, "qpack_dec_max_size", 18))
2038        {
2039            settings->es_qpack_dec_max_size = atoi(val);
2040            return 0;
2041        }
2042        if (0 == strncmp(name, "noprogress_timeout", 18))
2043        {
2044            settings->es_noprogress_timeout = atoi(val);
2045            return 0;
2046        }
2047        break;
2048    case 20:
2049        if (0 == strncmp(name, "max_header_list_size", 20))
2050        {
2051            settings->es_max_header_list_size = atoi(val);
2052            return 0;
2053        }
2054        if (0 == strncmp(name, "init_max_streams_uni", 20))
2055        {
2056            settings->es_init_max_streams_uni = atoi(val);
2057            return 0;
2058        }
2059        break;
2060    case 21:
2061        if (0 == strncmp(name, "qpack_enc_max_blocked", 21))
2062        {
2063            settings->es_qpack_enc_max_blocked = atoi(val);
2064            return 0;
2065        }
2066        if (0 == strncmp(name, "qpack_dec_max_blocked", 21))
2067        {
2068            settings->es_qpack_dec_max_blocked = atoi(val);
2069            return 0;
2070        }
2071        if (0 == strncmp(name, "init_max_streams_bidi", 21))
2072        {
2073            settings->es_init_max_streams_bidi = atoi(val);
2074            return 0;
2075        }
2076        break;
2077    case 23:
2078        if (0 == strncmp(name, "max_udp_payload_size_rx", 23))
2079        {
2080            settings->es_max_udp_payload_size_rx = atoi(val);
2081            return 0;
2082        }
2083        break;
2084    case 24:
2085        if (0 == strncmp(name, "init_max_stream_data_uni", 24))
2086        {
2087            settings->es_init_max_stream_data_uni = atoi(val);
2088            return 0;
2089        }
2090        break;
2091    case 31:
2092        if (0 == strncmp(name, "init_max_stream_data_bidi_local", 31))
2093        {
2094            settings->es_init_max_stream_data_bidi_local = atoi(val);
2095            return 0;
2096        }
2097        break;
2098    case 32:
2099        if (0 == strncmp(name, "init_max_stream_data_bidi_remote", 32))
2100        {
2101            settings->es_init_max_stream_data_bidi_remote = atoi(val);
2102            return 0;
2103        }
2104        break;
2105    }
2106
2107    return -1;
2108}
2109
2110
2111/* So that largest allocation in PBA fits in 4KB */
2112#define PBA_SIZE_MAX 0x1000
2113#define PBA_SIZE_THRESH (PBA_SIZE_MAX - sizeof(uintptr_t))
2114
2115struct packout_buf
2116{
2117    SLIST_ENTRY(packout_buf)    next_free_pb;
2118};
2119
2120
2121void
2122pba_init (struct packout_buf_allocator *pba, unsigned max)
2123{
2124    SLIST_INIT(&pba->free_packout_bufs);
2125    pba->max   = max;
2126    pba->n_out = 0;
2127}
2128
2129
2130void *
2131pba_allocate (void *packout_buf_allocator, void *peer_ctx,
2132                lsquic_conn_ctx_t *conn_ctx, unsigned short size, char is_ipv6)
2133{
2134    struct packout_buf_allocator *const pba = packout_buf_allocator;
2135    struct packout_buf *pb;
2136
2137    if (pba->max && pba->n_out >= pba->max)
2138    {
2139        LSQ_DEBUG("# outstanding packout bufs reached the limit of %u, "
2140            "returning NULL", pba->max);
2141        return NULL;
2142    }
2143
2144#if LSQUIC_USE_POOLS
2145    pb = SLIST_FIRST(&pba->free_packout_bufs);
2146    if (pb && size <= PBA_SIZE_THRESH)
2147        SLIST_REMOVE_HEAD(&pba->free_packout_bufs, next_free_pb);
2148    else if (size <= PBA_SIZE_THRESH)
2149        pb = malloc(PBA_SIZE_MAX);
2150    else
2151        pb = malloc(sizeof(uintptr_t) + size);
2152#else
2153    pb = malloc(sizeof(uintptr_t) + size);
2154#endif
2155
2156    if (pb)
2157    {
2158        * (uintptr_t *) pb = size;
2159        ++pba->n_out;
2160        return (uintptr_t *) pb + 1;
2161    }
2162    else
2163        return NULL;
2164}
2165
2166
2167void
2168pba_release (void *packout_buf_allocator, void *peer_ctx, void *obj, char ipv6)
2169{
2170    struct packout_buf_allocator *const pba = packout_buf_allocator;
2171    obj = (uintptr_t *) obj - 1;
2172#if LSQUIC_USE_POOLS
2173    if (* (uintptr_t *) obj <= PBA_SIZE_THRESH)
2174    {
2175        struct packout_buf *const pb = obj;
2176        SLIST_INSERT_HEAD(&pba->free_packout_bufs, pb, next_free_pb);
2177    }
2178    else
2179#endif
2180        free(obj);
2181    --pba->n_out;
2182}
2183
2184
2185void
2186pba_cleanup (struct packout_buf_allocator *pba)
2187{
2188#if LSQUIC_USE_POOLS
2189    unsigned n = 0;
2190    struct packout_buf *pb;
2191#endif
2192
2193    if (pba->n_out)
2194        LSQ_WARN("%u packout bufs outstanding at deinit", pba->n_out);
2195
2196#if LSQUIC_USE_POOLS
2197    while ((pb = SLIST_FIRST(&pba->free_packout_bufs)))
2198    {
2199        SLIST_REMOVE_HEAD(&pba->free_packout_bufs, next_free_pb);
2200        free(pb);
2201        ++n;
2202    }
2203
2204    LSQ_INFO("pba deinitialized, freed %u packout bufs", n);
2205#endif
2206}
2207
2208
2209void
2210print_conn_info (const lsquic_conn_t *conn)
2211{
2212    const char *cipher;
2213
2214    cipher = lsquic_conn_crypto_cipher(conn);
2215
2216    LSQ_INFO("Connection info: version: %u; cipher: %s; key size: %d, alg key size: %d",
2217        lsquic_conn_quic_version(conn),
2218        cipher ? cipher : "<null>",
2219        lsquic_conn_crypto_keysize(conn),
2220        lsquic_conn_crypto_alg_keysize(conn)
2221    );
2222}
2223
2224
2225struct reader_ctx
2226{
2227    size_t  file_size;
2228    size_t  nread;
2229    int     fd;
2230};
2231
2232
2233size_t
2234test_reader_size (void *void_ctx)
2235{
2236    struct reader_ctx *const ctx = void_ctx;
2237    return ctx->file_size - ctx->nread;
2238}
2239
2240
2241size_t
2242test_reader_read (void *void_ctx, void *buf, size_t count)
2243{
2244    struct reader_ctx *const ctx = void_ctx;
2245    ssize_t nread;
2246
2247    if (count > test_reader_size(ctx))
2248        count = test_reader_size(ctx);
2249
2250#ifndef WIN32
2251    nread = read(ctx->fd, buf, count);
2252#else
2253    nread = _read(ctx->fd, buf, count);
2254#endif
2255    if (nread >= 0)
2256    {
2257        ctx->nread += nread;
2258        return nread;
2259    }
2260    else
2261    {
2262        LSQ_WARN("%s: error reading from file: %s", __func__, strerror(errno));
2263        ctx->nread = ctx->file_size = 0;
2264        return 0;
2265    }
2266}
2267
2268
2269struct reader_ctx *
2270create_lsquic_reader_ctx (const char *filename)
2271{
2272    int fd;
2273    struct stat st;
2274
2275#ifndef WIN32
2276    fd = open(filename, O_RDONLY);
2277#else
2278    fd = _open(filename, _O_RDONLY);
2279#endif
2280    if (fd < 0)
2281    {
2282        LSQ_ERROR("cannot open %s for reading: %s", filename, strerror(errno));
2283        return NULL;
2284    }
2285
2286    if (0 != fstat(fd, &st))
2287    {
2288        LSQ_ERROR("cannot fstat(%s) failed: %s", filename, strerror(errno));
2289        (void) close(fd);
2290        return NULL;
2291    }
2292    struct reader_ctx *ctx = malloc(sizeof(*ctx));
2293    ctx->file_size = st.st_size;
2294    ctx->nread = 0;
2295    ctx->fd = fd;
2296    return ctx;
2297}
2298
2299
2300void
2301destroy_lsquic_reader_ctx (struct reader_ctx *ctx)
2302{
2303    (void) close(ctx->fd);
2304    free(ctx);
2305}
2306
2307
2308int
2309sport_set_token (struct service_port *sport, const char *token_str)
2310{
2311    static const unsigned char c2b[0x100] =
2312    {
2313        [(int)'0'] = 0,
2314        [(int)'1'] = 1,
2315        [(int)'2'] = 2,
2316        [(int)'3'] = 3,
2317        [(int)'4'] = 4,
2318        [(int)'5'] = 5,
2319        [(int)'6'] = 6,
2320        [(int)'7'] = 7,
2321        [(int)'8'] = 8,
2322        [(int)'9'] = 9,
2323        [(int)'A'] = 0xA,
2324        [(int)'B'] = 0xB,
2325        [(int)'C'] = 0xC,
2326        [(int)'D'] = 0xD,
2327        [(int)'E'] = 0xE,
2328        [(int)'F'] = 0xF,
2329        [(int)'a'] = 0xA,
2330        [(int)'b'] = 0xB,
2331        [(int)'c'] = 0xC,
2332        [(int)'d'] = 0xD,
2333        [(int)'e'] = 0xE,
2334        [(int)'f'] = 0xF,
2335    };
2336    unsigned char *token;
2337    int len, i;
2338
2339    len = strlen(token_str);
2340    token = malloc(len / 2);
2341    if (!token)
2342        return -1;
2343    for (i = 0; i < len / 2; ++i)
2344        token[i] = (c2b[ (int) token_str[i * 2] ] << 4)
2345                 |  c2b[ (int) token_str[i * 2 + 1] ];
2346
2347    free(sport->sp_token_buf);
2348    sport->sp_token_buf = token;
2349    sport->sp_token_sz = len / 2;
2350    return 0;
2351}
2352
2353
2354int
2355header_set_ptr (struct lsxpack_header *hdr, struct header_buf *header_buf,
2356                const char *name, size_t name_len,
2357                const char *val, size_t val_len)
2358{
2359    if (header_buf->off + name_len + val_len <= sizeof(header_buf->buf))
2360    {
2361        memcpy(header_buf->buf + header_buf->off, name, name_len);
2362        memcpy(header_buf->buf + header_buf->off + name_len, val, val_len);
2363        lsxpack_header_set_offset2(hdr, header_buf->buf + header_buf->off,
2364                                            0, name_len, name_len, val_len);
2365        header_buf->off += name_len + val_len;
2366        return 0;
2367    }
2368    else
2369        return -1;
2370}
2371