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