test_common.c revision 06b2a236
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    WSABUF wsaBuf;
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    do
1599    {
1600        sport = specs[n].peer_ctx;
1601#if LSQUIC_PREFERRED_ADDR
1602        if (sport->sp_prog->prog_flags & PROG_SEARCH_ADDRS)
1603            sport = find_sport(sport->sp_prog, specs[n].local_sa);
1604#endif
1605#ifndef WIN32
1606        msg.msg_name       = (void *) specs[n].dest_sa;
1607        msg.msg_namelen    = (AF_INET == specs[n].dest_sa->sa_family ?
1608                                            sizeof(struct sockaddr_in) :
1609                                            sizeof(struct sockaddr_in6)),
1610        msg.msg_iov        = specs[n].iov;
1611        msg.msg_iovlen     = specs[n].iovlen;
1612        msg.msg_flags      = 0;
1613#else
1614        wsaBuf.buf = specs[n].iov->iov_base;
1615        wsaBuf.len = specs[n].iov->iov_len;
1616        msg.name           = (void *) specs[n].dest_sa;
1617        msg.namelen        = (AF_INET == specs[n].dest_sa->sa_family ?
1618                                            sizeof(struct sockaddr_in) :
1619                                            sizeof(struct sockaddr_in6));
1620        msg.dwBufferCount  = 1;
1621        msg.lpBuffers      = &wsaBuf;
1622        msg.dwFlags        = 0;
1623#endif
1624        if ((sport->sp_flags & SPORT_SERVER) && specs[n].local_sa->sa_family)
1625        {
1626            cw = CW_SENDADDR;
1627            ancil_key = (uintptr_t) specs[n].local_sa;
1628            assert(0 == (ancil_key & 3));
1629        }
1630        else
1631        {
1632            cw = 0;
1633            ancil_key = 0;
1634        }
1635#if ECN_SUPPORTED
1636        if (sport->sp_prog->prog_api.ea_settings->es_ecn && specs[n].ecn)
1637        {
1638            cw |= CW_ECN;
1639            ancil_key |= specs[n].ecn;
1640        }
1641#endif
1642        if (cw && prev_ancil_key == ancil_key)
1643        {
1644            /* Reuse previous ancillary message */
1645            ;
1646        }
1647        else if (cw)
1648        {
1649            prev_ancil_key = ancil_key;
1650            setup_control_msg(&msg, cw, &specs[n], ancil.buf, sizeof(ancil.buf));
1651        }
1652        else
1653        {
1654            prev_ancil_key = 0;
1655#ifndef WIN32
1656            msg.msg_control = NULL;
1657            msg.msg_controllen = 0;
1658#else
1659            msg.Control.buf = NULL;
1660            msg.Control.len = 0;
1661#endif
1662        }
1663#ifndef WIN32
1664        s = sendmsg(sport->fd, &msg, 0);
1665#else
1666        s = pfnWSASendMsg(sport->fd, &msg, 0, &bytes, NULL, NULL);
1667#endif
1668        if (s < 0)
1669        {
1670#ifndef WIN32
1671            LSQ_INFO("sendto failed: %s", strerror(errno));
1672#else
1673            LSQ_INFO("sendto failed: %s", WSAGetLastError());
1674#endif
1675            break;
1676        }
1677        ++n;
1678    }
1679    while (n < count);
1680
1681    if (n < orig_count)
1682        prog_sport_cant_send(sport->sp_prog, sport->fd);
1683
1684    if (n > 0)
1685    {
1686        if (n < orig_count && out_limit)
1687            errno = EAGAIN;
1688        return n;
1689    }
1690    else
1691    {
1692        assert(s < 0);
1693#if LSQUIC_RANDOM_SEND_FAILURE
1694  random_send_failure:
1695#endif
1696        return -1;
1697    }
1698}
1699
1700
1701int
1702sport_packets_out (void *ctx, const struct lsquic_out_spec *specs,
1703                   unsigned count)
1704{
1705#if HAVE_SENDMMSG
1706    const struct prog *prog = ctx;
1707    if (prog->prog_use_sendmmsg)
1708        return send_packets_using_sendmmsg(specs, count);
1709    else
1710#endif
1711        return send_packets_one_by_one(specs, count);
1712}
1713
1714
1715int
1716set_engine_option (struct lsquic_engine_settings *settings,
1717                   int *version_cleared, const char *name)
1718{
1719    int len;
1720    const char *val = strchr(name, '=');
1721    if (!val)
1722        return -1;
1723    len = val - name;
1724    ++val;
1725
1726    switch (len)
1727    {
1728    case 2:
1729        if (0 == strncmp(name, "ua", 2))
1730        {
1731            settings->es_ua = val;
1732            return 0;
1733        }
1734        break;
1735    case 3:
1736        if (0 == strncmp(name, "ecn", 1))
1737        {
1738            settings->es_ecn = atoi(val);
1739#if !ECN_SUPPORTED
1740            if (settings->es_ecn)
1741            {
1742                LSQ_ERROR("ECN is not supported on this platform");
1743                break;
1744            }
1745#endif
1746            return 0;
1747        }
1748        break;
1749    case 4:
1750        if (0 == strncmp(name, "cfcw", 4))
1751        {
1752            settings->es_cfcw = atoi(val);
1753            return 0;
1754        }
1755        if (0 == strncmp(name, "sfcw", 4))
1756        {
1757            settings->es_sfcw = atoi(val);
1758            return 0;
1759        }
1760        if (0 == strncmp(name, "spin", 4))
1761        {
1762            settings->es_spin = atoi(val);
1763            return 0;
1764        }
1765        break;
1766    case 7:
1767        if (0 == strncmp(name, "version", 7))
1768        {
1769            if (!*version_cleared)
1770            {
1771                *version_cleared = 1;
1772                settings->es_versions = 0;
1773            }
1774            enum lsquic_version ver = lsquic_str2ver(val, strlen(val));
1775            if ((unsigned) ver < N_LSQVER)
1776            {
1777                settings->es_versions |= 1 << ver;
1778                return 0;
1779            }
1780            ver = lsquic_alpn2ver(val, strlen(val));
1781            if ((unsigned) ver < N_LSQVER)
1782            {
1783                settings->es_versions |= 1 << ver;
1784                return 0;
1785            }
1786        }
1787        else if (0 == strncmp(name, "rw_once", 7))
1788        {
1789            settings->es_rw_once = atoi(val);
1790            return 0;
1791        }
1792        else if (0 == strncmp(name, "cc_algo", 7))
1793        {
1794            settings->es_cc_algo = atoi(val);
1795            return 0;
1796        }
1797        else if (0 == strncmp(name, "ql_bits", 7))
1798        {
1799            settings->es_ql_bits = atoi(val);
1800            return 0;
1801        }
1802        break;
1803    case 8:
1804        if (0 == strncmp(name, "max_cfcw", 8))
1805        {
1806            settings->es_max_cfcw = atoi(val);
1807            return 0;
1808        }
1809        if (0 == strncmp(name, "max_sfcw", 8))
1810        {
1811            settings->es_max_sfcw = atoi(val);
1812            return 0;
1813        }
1814        if (0 == strncmp(name, "scid_len", 8))
1815        {
1816            settings->es_scid_len = atoi(val);
1817            return 0;
1818        }
1819        if (0 == strncmp(name, "dplpmtud", 8))
1820        {
1821            settings->es_dplpmtud = atoi(val);
1822            return 0;
1823        }
1824        break;
1825    case 9:
1826        if (0 == strncmp(name, "send_prst", 9))
1827        {
1828            settings->es_send_prst = atoi(val);
1829            return 0;
1830        }
1831        break;
1832    case 10:
1833        if (0 == strncmp(name, "honor_prst", 10))
1834        {
1835            settings->es_honor_prst = atoi(val);
1836            return 0;
1837        }
1838        if (0 == strncmp(name, "timestamps", 10))
1839        {
1840            settings->es_timestamps = atoi(val);
1841            return 0;
1842        }
1843        if (0 == strncmp(name, "max_plpmtu", 10))
1844        {
1845            settings->es_max_plpmtu = atoi(val);
1846            return 0;
1847        }
1848        break;
1849    case 11:
1850        if (0 == strncmp(name, "ping_period", 11))
1851        {
1852            settings->es_ping_period = atoi(val);
1853            return 0;
1854        }
1855        if (0 == strncmp(name, "base_plpmtu", 11))
1856        {
1857            settings->es_base_plpmtu = atoi(val);
1858            return 0;
1859        }
1860        if (0 == strncmp(name, "ptpc_target", 11))
1861        {
1862            settings->es_ptpc_target = atof(val);
1863            return 0;
1864        }
1865        break;
1866    case 12:
1867        if (0 == strncmp(name, "idle_conn_to", 12))
1868        {
1869            settings->es_idle_conn_to = atoi(val);
1870            return 0;
1871        }
1872        if (0 == strncmp(name, "idle_timeout", 12))
1873        {
1874            settings->es_idle_timeout = atoi(val);
1875            return 0;
1876        }
1877        if (0 == strncmp(name, "silent_close", 12))
1878        {
1879            settings->es_silent_close = atoi(val);
1880            return 0;
1881        }
1882        if (0 == strncmp(name, "support_push", 12))
1883        {
1884            settings->es_support_push = atoi(val);
1885            return 0;
1886        }
1887        if (0 == strncmp(name, "support_nstp", 12))
1888        {
1889            settings->es_support_nstp = atoi(val);
1890            return 0;
1891        }
1892        if (0 == strncmp(name, "pace_packets", 12))
1893        {
1894            settings->es_pace_packets = atoi(val);
1895            return 0;
1896        }
1897        if (0 == strncmp(name, "handshake_to", 12))
1898        {
1899            settings->es_handshake_to = atoi(val);
1900            return 0;
1901        }
1902        if (0 == strncmp(name, "delayed_acks", 12))
1903        {
1904            settings->es_delayed_acks = atoi(val);
1905            return 0;
1906        }
1907        break;
1908    case 13:
1909        if (0 == strncmp(name, "support_tcid0", 13))
1910        {
1911            settings->es_support_tcid0 = atoi(val);
1912            return 0;
1913        }
1914        if (0 == strncmp(name, "init_max_data", 13))
1915        {
1916            settings->es_init_max_data = atoi(val);
1917            return 0;
1918        }
1919        if (0 == strncmp(name, "scid_iss_rate", 13))
1920        {
1921            settings->es_scid_iss_rate = atoi(val);
1922            return 0;
1923        }
1924        if (0 == strncmp(name, "ext_http_prio", 13))
1925        {
1926            settings->es_ext_http_prio = atoi(val);
1927            return 0;
1928        }
1929        if (0 == strncmp(name, "ptpc_int_gain", 13))
1930        {
1931            settings->es_ptpc_int_gain = atof(val);
1932            return 0;
1933        }
1934        if (0 == strncmp(name, "delay_onclose", 13))
1935        {
1936            settings->es_delay_onclose = atoi(val);
1937            return 0;
1938        }
1939        break;
1940    case 14:
1941        if (0 == strncmp(name, "max_streams_in", 14))
1942        {
1943            settings->es_max_streams_in = atoi(val);
1944            return 0;
1945        }
1946        if (0 == strncmp(name, "progress_check", 14))
1947        {
1948            settings->es_progress_check = atoi(val);
1949            return 0;
1950        }
1951        if (0 == strncmp(name, "ptpc_prop_gain", 14))
1952        {
1953            settings->es_ptpc_prop_gain = atof(val);
1954            return 0;
1955        }
1956        if (0 == strncmp(name, "max_batch_size", 14))
1957        {
1958            settings->es_max_batch_size = atoi(val);
1959            return 0;
1960        }
1961        break;
1962    case 15:
1963        if (0 == strncmp(name, "allow_migration", 15))
1964        {
1965            settings->es_allow_migration = atoi(val);
1966            return 0;
1967        }
1968        if (0 == strncmp(name, "grease_quic_bit", 15))
1969        {
1970            settings->es_grease_quic_bit = atoi(val);
1971            return 0;
1972        }
1973        if (0 == strncmp(name, "ptpc_dyn_target", 15))
1974        {
1975            settings->es_ptpc_dyn_target = atoi(val);
1976            return 0;
1977        }
1978        if (0 == strncmp(name, "ptpc_err_thresh", 15))
1979        {
1980            settings->es_ptpc_err_thresh = atof(val);
1981            return 0;
1982        }
1983        break;
1984    case 16:
1985        if (0 == strncmp(name, "proc_time_thresh", 16))
1986        {
1987            settings->es_proc_time_thresh = atoi(val);
1988            return 0;
1989        }
1990        if (0 == strncmp(name, "qpack_experiment", 16))
1991        {
1992            settings->es_qpack_experiment = atoi(val);
1993            return 0;
1994        }
1995        if (0 == strncmp(name, "ptpc_periodicity", 16))
1996        {
1997            settings->es_ptpc_periodicity = atoi(val);
1998            return 0;
1999        }
2000        if (0 == strncmp(name, "ptpc_max_packtol", 16))
2001        {
2002            settings->es_ptpc_max_packtol = atoi(val);
2003            return 0;
2004        }
2005        if (0 == strncmp(name, "ptpc_err_divisor", 16))
2006        {
2007            settings->es_ptpc_err_divisor = atof(val);
2008            return 0;
2009        }
2010        break;
2011    case 18:
2012        if (0 == strncmp(name, "qpack_enc_max_size", 18))
2013        {
2014            settings->es_qpack_enc_max_size = atoi(val);
2015            return 0;
2016        }
2017        if (0 == strncmp(name, "qpack_dec_max_size", 18))
2018        {
2019            settings->es_qpack_dec_max_size = atoi(val);
2020            return 0;
2021        }
2022        if (0 == strncmp(name, "noprogress_timeout", 18))
2023        {
2024            settings->es_noprogress_timeout = atoi(val);
2025            return 0;
2026        }
2027        break;
2028    case 20:
2029        if (0 == strncmp(name, "max_header_list_size", 20))
2030        {
2031            settings->es_max_header_list_size = atoi(val);
2032            return 0;
2033        }
2034        if (0 == strncmp(name, "init_max_streams_uni", 20))
2035        {
2036            settings->es_init_max_streams_uni = atoi(val);
2037            return 0;
2038        }
2039        break;
2040    case 21:
2041        if (0 == strncmp(name, "qpack_enc_max_blocked", 21))
2042        {
2043            settings->es_qpack_enc_max_blocked = atoi(val);
2044            return 0;
2045        }
2046        if (0 == strncmp(name, "qpack_dec_max_blocked", 21))
2047        {
2048            settings->es_qpack_dec_max_blocked = atoi(val);
2049            return 0;
2050        }
2051        if (0 == strncmp(name, "init_max_streams_bidi", 21))
2052        {
2053            settings->es_init_max_streams_bidi = atoi(val);
2054            return 0;
2055        }
2056        break;
2057    case 23:
2058        if (0 == strncmp(name, "max_udp_payload_size_rx", 23))
2059        {
2060            settings->es_max_udp_payload_size_rx = atoi(val);
2061            return 0;
2062        }
2063        break;
2064    case 24:
2065        if (0 == strncmp(name, "init_max_stream_data_uni", 24))
2066        {
2067            settings->es_init_max_stream_data_uni = atoi(val);
2068            return 0;
2069        }
2070        break;
2071    case 31:
2072        if (0 == strncmp(name, "init_max_stream_data_bidi_local", 31))
2073        {
2074            settings->es_init_max_stream_data_bidi_local = atoi(val);
2075            return 0;
2076        }
2077        break;
2078    case 32:
2079        if (0 == strncmp(name, "init_max_stream_data_bidi_remote", 32))
2080        {
2081            settings->es_init_max_stream_data_bidi_remote = atoi(val);
2082            return 0;
2083        }
2084        break;
2085    }
2086
2087    return -1;
2088}
2089
2090
2091/* So that largest allocation in PBA fits in 4KB */
2092#define PBA_SIZE_MAX 0x1000
2093#define PBA_SIZE_THRESH (PBA_SIZE_MAX - sizeof(uintptr_t))
2094
2095struct packout_buf
2096{
2097    SLIST_ENTRY(packout_buf)    next_free_pb;
2098};
2099
2100
2101void
2102pba_init (struct packout_buf_allocator *pba, unsigned max)
2103{
2104    SLIST_INIT(&pba->free_packout_bufs);
2105    pba->max   = max;
2106    pba->n_out = 0;
2107}
2108
2109
2110void *
2111pba_allocate (void *packout_buf_allocator, void *peer_ctx,
2112                lsquic_conn_ctx_t *conn_ctx, unsigned short size, char is_ipv6)
2113{
2114    struct packout_buf_allocator *const pba = packout_buf_allocator;
2115    struct packout_buf *pb;
2116
2117    if (pba->max && pba->n_out >= pba->max)
2118    {
2119        LSQ_DEBUG("# outstanding packout bufs reached the limit of %u, "
2120            "returning NULL", pba->max);
2121        return NULL;
2122    }
2123
2124#if LSQUIC_USE_POOLS
2125    pb = SLIST_FIRST(&pba->free_packout_bufs);
2126    if (pb && size <= PBA_SIZE_THRESH)
2127        SLIST_REMOVE_HEAD(&pba->free_packout_bufs, next_free_pb);
2128    else if (size <= PBA_SIZE_THRESH)
2129        pb = malloc(PBA_SIZE_MAX);
2130    else
2131        pb = malloc(sizeof(uintptr_t) + size);
2132#else
2133    pb = malloc(sizeof(uintptr_t) + size);
2134#endif
2135
2136    if (pb)
2137    {
2138        * (uintptr_t *) pb = size;
2139        ++pba->n_out;
2140        return (uintptr_t *) pb + 1;
2141    }
2142    else
2143        return NULL;
2144}
2145
2146
2147void
2148pba_release (void *packout_buf_allocator, void *peer_ctx, void *obj, char ipv6)
2149{
2150    struct packout_buf_allocator *const pba = packout_buf_allocator;
2151    obj = (uintptr_t *) obj - 1;
2152#if LSQUIC_USE_POOLS
2153    if (* (uintptr_t *) obj <= PBA_SIZE_THRESH)
2154    {
2155        struct packout_buf *const pb = obj;
2156        SLIST_INSERT_HEAD(&pba->free_packout_bufs, pb, next_free_pb);
2157    }
2158    else
2159#endif
2160        free(obj);
2161    --pba->n_out;
2162}
2163
2164
2165void
2166pba_cleanup (struct packout_buf_allocator *pba)
2167{
2168#if LSQUIC_USE_POOLS
2169    unsigned n = 0;
2170    struct packout_buf *pb;
2171#endif
2172
2173    if (pba->n_out)
2174        LSQ_WARN("%u packout bufs outstanding at deinit", pba->n_out);
2175
2176#if LSQUIC_USE_POOLS
2177    while ((pb = SLIST_FIRST(&pba->free_packout_bufs)))
2178    {
2179        SLIST_REMOVE_HEAD(&pba->free_packout_bufs, next_free_pb);
2180        free(pb);
2181        ++n;
2182    }
2183
2184    LSQ_INFO("pba deinitialized, freed %u packout bufs", n);
2185#endif
2186}
2187
2188
2189void
2190print_conn_info (const lsquic_conn_t *conn)
2191{
2192    const char *cipher;
2193
2194    cipher = lsquic_conn_crypto_cipher(conn);
2195
2196    LSQ_INFO("Connection info: version: %u; cipher: %s; key size: %d, alg key size: %d",
2197        lsquic_conn_quic_version(conn),
2198        cipher ? cipher : "<null>",
2199        lsquic_conn_crypto_keysize(conn),
2200        lsquic_conn_crypto_alg_keysize(conn)
2201    );
2202}
2203
2204
2205struct reader_ctx
2206{
2207    size_t  file_size;
2208    size_t  nread;
2209    int     fd;
2210};
2211
2212
2213size_t
2214test_reader_size (void *void_ctx)
2215{
2216    struct reader_ctx *const ctx = void_ctx;
2217    return ctx->file_size - ctx->nread;
2218}
2219
2220
2221size_t
2222test_reader_read (void *void_ctx, void *buf, size_t count)
2223{
2224    struct reader_ctx *const ctx = void_ctx;
2225    ssize_t nread;
2226
2227    if (count > test_reader_size(ctx))
2228        count = test_reader_size(ctx);
2229
2230#ifndef WIN32
2231    nread = read(ctx->fd, buf, count);
2232#else
2233    nread = _read(ctx->fd, buf, count);
2234#endif
2235    if (nread >= 0)
2236    {
2237        ctx->nread += nread;
2238        return nread;
2239    }
2240    else
2241    {
2242        LSQ_WARN("%s: error reading from file: %s", __func__, strerror(errno));
2243        ctx->nread = ctx->file_size = 0;
2244        return 0;
2245    }
2246}
2247
2248
2249struct reader_ctx *
2250create_lsquic_reader_ctx (const char *filename)
2251{
2252    int fd;
2253    struct stat st;
2254
2255#ifndef WIN32
2256    fd = open(filename, O_RDONLY);
2257#else
2258    fd = _open(filename, _O_RDONLY);
2259#endif
2260    if (fd < 0)
2261    {
2262        LSQ_ERROR("cannot open %s for reading: %s", filename, strerror(errno));
2263        return NULL;
2264    }
2265
2266    if (0 != fstat(fd, &st))
2267    {
2268        LSQ_ERROR("cannot fstat(%s) failed: %s", filename, strerror(errno));
2269        (void) close(fd);
2270        return NULL;
2271    }
2272    struct reader_ctx *ctx = malloc(sizeof(*ctx));
2273    ctx->file_size = st.st_size;
2274    ctx->nread = 0;
2275    ctx->fd = fd;
2276    return ctx;
2277}
2278
2279
2280void
2281destroy_lsquic_reader_ctx (struct reader_ctx *ctx)
2282{
2283    (void) close(ctx->fd);
2284    free(ctx);
2285}
2286
2287
2288int
2289sport_set_token (struct service_port *sport, const char *token_str)
2290{
2291    static const unsigned char c2b[0x100] =
2292    {
2293        [(int)'0'] = 0,
2294        [(int)'1'] = 1,
2295        [(int)'2'] = 2,
2296        [(int)'3'] = 3,
2297        [(int)'4'] = 4,
2298        [(int)'5'] = 5,
2299        [(int)'6'] = 6,
2300        [(int)'7'] = 7,
2301        [(int)'8'] = 8,
2302        [(int)'9'] = 9,
2303        [(int)'A'] = 0xA,
2304        [(int)'B'] = 0xB,
2305        [(int)'C'] = 0xC,
2306        [(int)'D'] = 0xD,
2307        [(int)'E'] = 0xE,
2308        [(int)'F'] = 0xF,
2309        [(int)'a'] = 0xA,
2310        [(int)'b'] = 0xB,
2311        [(int)'c'] = 0xC,
2312        [(int)'d'] = 0xD,
2313        [(int)'e'] = 0xE,
2314        [(int)'f'] = 0xF,
2315    };
2316    unsigned char *token;
2317    int len, i;
2318
2319    len = strlen(token_str);
2320    token = malloc(len / 2);
2321    if (!token)
2322        return -1;
2323    for (i = 0; i < len / 2; ++i)
2324        token[i] = (c2b[ (int) token_str[i * 2] ] << 4)
2325                 |  c2b[ (int) token_str[i * 2 + 1] ];
2326
2327    free(sport->sp_token_buf);
2328    sport->sp_token_buf = token;
2329    sport->sp_token_sz = len / 2;
2330    return 0;
2331}
2332
2333
2334int
2335header_set_ptr (struct lsxpack_header *hdr, struct header_buf *header_buf,
2336                const char *name, size_t name_len,
2337                const char *val, size_t val_len)
2338{
2339    if (header_buf->off + name_len + val_len <= sizeof(header_buf->buf))
2340    {
2341        memcpy(header_buf->buf + header_buf->off, name, name_len);
2342        memcpy(header_buf->buf + header_buf->off + name_len, val, val_len);
2343        lsxpack_header_set_offset2(hdr, header_buf->buf + header_buf->off,
2344                                            0, name_len, name_len, val_len);
2345        header_buf->off += name_len + val_len;
2346        return 0;
2347    }
2348    else
2349        return -1;
2350}
2351