xref: /curl/lib/vquic/vquic.c (revision 9b863ac6)
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24 
25 #include "curl_setup.h"
26 
27 #ifdef HAVE_NETINET_UDP_H
28 #include <netinet/udp.h>
29 #endif
30 #ifdef HAVE_FCNTL_H
31 #include <fcntl.h>
32 #endif
33 #include "urldata.h"
34 #include "bufq.h"
35 #include "dynbuf.h"
36 #include "cfilters.h"
37 #include "curl_trc.h"
38 #include "curl_msh3.h"
39 #include "curl_ngtcp2.h"
40 #include "curl_osslq.h"
41 #include "curl_quiche.h"
42 #include "multiif.h"
43 #include "rand.h"
44 #include "vquic.h"
45 #include "vquic_int.h"
46 #include "strerror.h"
47 
48 /* The last 3 #include files should be in this order */
49 #include "curl_printf.h"
50 #include "curl_memory.h"
51 #include "memdebug.h"
52 
53 
54 #ifdef USE_HTTP3
55 
56 #ifdef O_BINARY
57 #define QLOGMODE O_WRONLY|O_CREAT|O_BINARY
58 #else
59 #define QLOGMODE O_WRONLY|O_CREAT
60 #endif
61 
62 #define NW_CHUNK_SIZE     (64 * 1024)
63 #define NW_SEND_CHUNKS    2
64 
65 
Curl_quic_ver(char * p,size_t len)66 void Curl_quic_ver(char *p, size_t len)
67 {
68 #if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
69   Curl_ngtcp2_ver(p, len);
70 #elif defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)
71   Curl_osslq_ver(p, len);
72 #elif defined(USE_QUICHE)
73   Curl_quiche_ver(p, len);
74 #elif defined(USE_MSH3)
75   Curl_msh3_ver(p, len);
76 #endif
77 }
78 
vquic_ctx_init(struct cf_quic_ctx * qctx)79 CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx)
80 {
81   Curl_bufq_init2(&qctx->sendbuf, NW_CHUNK_SIZE, NW_SEND_CHUNKS,
82                   BUFQ_OPT_SOFT_LIMIT);
83 #if defined(__linux__) && defined(UDP_SEGMENT) && defined(HAVE_SENDMSG)
84   qctx->no_gso = FALSE;
85 #else
86   qctx->no_gso = TRUE;
87 #endif
88 #ifdef DEBUGBUILD
89   {
90     char *p = getenv("CURL_DBG_QUIC_WBLOCK");
91     if(p) {
92       long l = strtol(p, NULL, 10);
93       if(l >= 0 && l <= 100)
94         qctx->wblock_percent = (int)l;
95     }
96   }
97 #endif
98   vquic_ctx_update_time(qctx);
99 
100   return CURLE_OK;
101 }
102 
vquic_ctx_free(struct cf_quic_ctx * qctx)103 void vquic_ctx_free(struct cf_quic_ctx *qctx)
104 {
105   Curl_bufq_free(&qctx->sendbuf);
106 }
107 
vquic_ctx_update_time(struct cf_quic_ctx * qctx)108 void vquic_ctx_update_time(struct cf_quic_ctx *qctx)
109 {
110   qctx->last_op = Curl_now();
111 }
112 
113 static CURLcode send_packet_no_gso(struct Curl_cfilter *cf,
114                                    struct Curl_easy *data,
115                                    struct cf_quic_ctx *qctx,
116                                    const uint8_t *pkt, size_t pktlen,
117                                    size_t gsolen, size_t *psent);
118 
do_sendmsg(struct Curl_cfilter * cf,struct Curl_easy * data,struct cf_quic_ctx * qctx,const uint8_t * pkt,size_t pktlen,size_t gsolen,size_t * psent)119 static CURLcode do_sendmsg(struct Curl_cfilter *cf,
120                            struct Curl_easy *data,
121                            struct cf_quic_ctx *qctx,
122                            const uint8_t *pkt, size_t pktlen, size_t gsolen,
123                            size_t *psent)
124 {
125 #ifdef HAVE_SENDMSG
126   struct iovec msg_iov;
127   struct msghdr msg = {0};
128   ssize_t sent;
129 #if defined(__linux__) && defined(UDP_SEGMENT)
130   uint8_t msg_ctrl[32];
131   struct cmsghdr *cm;
132 #endif
133 
134   *psent = 0;
135   msg_iov.iov_base = (uint8_t *)pkt;
136   msg_iov.iov_len = pktlen;
137   msg.msg_iov = &msg_iov;
138   msg.msg_iovlen = 1;
139 
140 #if defined(__linux__) && defined(UDP_SEGMENT)
141   if(pktlen > gsolen) {
142     /* Only set this, when we need it. macOS, for example,
143      * does not seem to like a msg_control of length 0. */
144     msg.msg_control = msg_ctrl;
145     assert(sizeof(msg_ctrl) >= CMSG_SPACE(sizeof(int)));
146     msg.msg_controllen = CMSG_SPACE(sizeof(int));
147     cm = CMSG_FIRSTHDR(&msg);
148     cm->cmsg_level = SOL_UDP;
149     cm->cmsg_type = UDP_SEGMENT;
150     cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));
151     *(uint16_t *)(void *)CMSG_DATA(cm) = gsolen & 0xffff;
152   }
153 #endif
154 
155 
156   while((sent = sendmsg(qctx->sockfd, &msg, 0)) == -1 && SOCKERRNO == EINTR)
157     ;
158 
159   if(sent == -1) {
160     switch(SOCKERRNO) {
161     case EAGAIN:
162 #if EAGAIN != EWOULDBLOCK
163     case EWOULDBLOCK:
164 #endif
165       return CURLE_AGAIN;
166     case EMSGSIZE:
167       /* UDP datagram is too large; caused by PMTUD. Just let it be lost. */
168       break;
169     case EIO:
170       if(pktlen > gsolen) {
171         /* GSO failure */
172         failf(data, "sendmsg() returned %zd (errno %d); disable GSO", sent,
173               SOCKERRNO);
174         qctx->no_gso = TRUE;
175         return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent);
176       }
177       FALLTHROUGH();
178     default:
179       failf(data, "sendmsg() returned %zd (errno %d)", sent, SOCKERRNO);
180       return CURLE_SEND_ERROR;
181     }
182   }
183   else {
184     assert(pktlen == (size_t)sent);
185   }
186 #else
187   ssize_t sent;
188   (void)gsolen;
189 
190   *psent = 0;
191 
192   while((sent = send(qctx->sockfd,
193                      (const char *)pkt, (SEND_TYPE_ARG3)pktlen, 0)) == -1 &&
194         SOCKERRNO == EINTR)
195     ;
196 
197   if(sent == -1) {
198     if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
199       return CURLE_AGAIN;
200     }
201     else {
202       failf(data, "send() returned %zd (errno %d)", sent, SOCKERRNO);
203       if(SOCKERRNO != EMSGSIZE) {
204         return CURLE_SEND_ERROR;
205       }
206       /* UDP datagram is too large; caused by PMTUD. Just let it be
207          lost. */
208     }
209   }
210 #endif
211   (void)cf;
212   *psent = pktlen;
213 
214   return CURLE_OK;
215 }
216 
send_packet_no_gso(struct Curl_cfilter * cf,struct Curl_easy * data,struct cf_quic_ctx * qctx,const uint8_t * pkt,size_t pktlen,size_t gsolen,size_t * psent)217 static CURLcode send_packet_no_gso(struct Curl_cfilter *cf,
218                                    struct Curl_easy *data,
219                                    struct cf_quic_ctx *qctx,
220                                    const uint8_t *pkt, size_t pktlen,
221                                    size_t gsolen, size_t *psent)
222 {
223   const uint8_t *p, *end = pkt + pktlen;
224   size_t sent;
225 
226   *psent = 0;
227 
228   for(p = pkt; p < end; p += gsolen) {
229     size_t len = CURLMIN(gsolen, (size_t)(end - p));
230     CURLcode curlcode = do_sendmsg(cf, data, qctx, p, len, len, &sent);
231     if(curlcode != CURLE_OK) {
232       return curlcode;
233     }
234     *psent += sent;
235   }
236 
237   return CURLE_OK;
238 }
239 
vquic_send_packets(struct Curl_cfilter * cf,struct Curl_easy * data,struct cf_quic_ctx * qctx,const uint8_t * pkt,size_t pktlen,size_t gsolen,size_t * psent)240 static CURLcode vquic_send_packets(struct Curl_cfilter *cf,
241                                    struct Curl_easy *data,
242                                    struct cf_quic_ctx *qctx,
243                                    const uint8_t *pkt, size_t pktlen,
244                                    size_t gsolen, size_t *psent)
245 {
246   CURLcode result;
247 #ifdef DEBUGBUILD
248   /* simulate network blocking/partial writes */
249   if(qctx->wblock_percent > 0) {
250     unsigned char c;
251     Curl_rand(data, &c, 1);
252     if(c >= ((100-qctx->wblock_percent)*256/100)) {
253       CURL_TRC_CF(data, cf, "vquic_flush() simulate EWOULDBLOCK");
254       return CURLE_AGAIN;
255     }
256   }
257 #endif
258   if(qctx->no_gso && pktlen > gsolen) {
259     result = send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent);
260   }
261   else {
262     result = do_sendmsg(cf, data, qctx, pkt, pktlen, gsolen, psent);
263   }
264   if(!result)
265     qctx->last_io = qctx->last_op;
266   return result;
267 }
268 
vquic_flush(struct Curl_cfilter * cf,struct Curl_easy * data,struct cf_quic_ctx * qctx)269 CURLcode vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data,
270                      struct cf_quic_ctx *qctx)
271 {
272   const unsigned char *buf;
273   size_t blen, sent;
274   CURLcode result;
275   size_t gsolen;
276 
277   while(Curl_bufq_peek(&qctx->sendbuf, &buf, &blen)) {
278     gsolen = qctx->gsolen;
279     if(qctx->split_len) {
280       gsolen = qctx->split_gsolen;
281       if(blen > qctx->split_len)
282         blen = qctx->split_len;
283     }
284 
285     result = vquic_send_packets(cf, data, qctx, buf, blen, gsolen, &sent);
286     CURL_TRC_CF(data, cf, "vquic_send(len=%zu, gso=%zu) -> %d, sent=%zu",
287                 blen, gsolen, result, sent);
288     if(result) {
289       if(result == CURLE_AGAIN) {
290         Curl_bufq_skip(&qctx->sendbuf, sent);
291         if(qctx->split_len)
292           qctx->split_len -= sent;
293       }
294       return result;
295     }
296     Curl_bufq_skip(&qctx->sendbuf, sent);
297     if(qctx->split_len)
298       qctx->split_len -= sent;
299   }
300   return CURLE_OK;
301 }
302 
vquic_send(struct Curl_cfilter * cf,struct Curl_easy * data,struct cf_quic_ctx * qctx,size_t gsolen)303 CURLcode vquic_send(struct Curl_cfilter *cf, struct Curl_easy *data,
304                         struct cf_quic_ctx *qctx, size_t gsolen)
305 {
306   qctx->gsolen = gsolen;
307   return vquic_flush(cf, data, qctx);
308 }
309 
vquic_send_tail_split(struct Curl_cfilter * cf,struct Curl_easy * data,struct cf_quic_ctx * qctx,size_t gsolen,size_t tail_len,size_t tail_gsolen)310 CURLcode vquic_send_tail_split(struct Curl_cfilter *cf, struct Curl_easy *data,
311                                struct cf_quic_ctx *qctx, size_t gsolen,
312                                size_t tail_len, size_t tail_gsolen)
313 {
314   DEBUGASSERT(Curl_bufq_len(&qctx->sendbuf) > tail_len);
315   qctx->split_len = Curl_bufq_len(&qctx->sendbuf) - tail_len;
316   qctx->split_gsolen = gsolen;
317   qctx->gsolen = tail_gsolen;
318   CURL_TRC_CF(data, cf, "vquic_send_tail_split: [%zu gso=%zu][%zu gso=%zu]",
319               qctx->split_len, qctx->split_gsolen,
320               tail_len, qctx->gsolen);
321   return vquic_flush(cf, data, qctx);
322 }
323 
324 #if defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG)
vquic_msghdr_get_udp_gro(struct msghdr * msg)325 static size_t vquic_msghdr_get_udp_gro(struct msghdr *msg)
326 {
327   int gso_size = 0;
328 #if defined(__linux__) && defined(UDP_GRO)
329   struct cmsghdr *cmsg;
330 
331   /* Workaround musl CMSG_NXTHDR issue */
332 #if defined(__clang__) && !defined(__GLIBC__)
333 #pragma clang diagnostic push
334 #pragma clang diagnostic ignored "-Wsign-compare"
335 #pragma clang diagnostic ignored "-Wcast-align"
336 #endif
337   for(cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
338 #if defined(__clang__) && !defined(__GLIBC__)
339 #pragma clang diagnostic pop
340 #endif
341     if(cmsg->cmsg_level == SOL_UDP && cmsg->cmsg_type == UDP_GRO) {
342       memcpy(&gso_size, CMSG_DATA(cmsg), sizeof(gso_size));
343 
344       break;
345     }
346   }
347 #endif
348   (void)msg;
349 
350   return (size_t)gso_size;
351 }
352 #endif
353 
354 #ifdef HAVE_SENDMMSG
recvmmsg_packets(struct Curl_cfilter * cf,struct Curl_easy * data,struct cf_quic_ctx * qctx,size_t max_pkts,vquic_recv_pkt_cb * recv_cb,void * userp)355 static CURLcode recvmmsg_packets(struct Curl_cfilter *cf,
356                                  struct Curl_easy *data,
357                                  struct cf_quic_ctx *qctx,
358                                  size_t max_pkts,
359                                  vquic_recv_pkt_cb *recv_cb, void *userp)
360 {
361 #define MMSG_NUM  16
362   struct iovec msg_iov[MMSG_NUM];
363   struct mmsghdr mmsg[MMSG_NUM];
364   uint8_t msg_ctrl[MMSG_NUM * CMSG_SPACE(sizeof(int))];
365   struct sockaddr_storage remote_addr[MMSG_NUM];
366   size_t total_nread = 0, pkts;
367   int mcount, i, n;
368   char errstr[STRERROR_LEN];
369   CURLcode result = CURLE_OK;
370   size_t gso_size;
371   size_t pktlen;
372   size_t offset, to;
373   char *sockbuf = NULL;
374   uint8_t (*bufs)[64*1024] = NULL;
375 
376   DEBUGASSERT(max_pkts > 0);
377   result = Curl_multi_xfer_sockbuf_borrow(data, MMSG_NUM * sizeof(bufs[0]),
378                                           &sockbuf);
379   if(result)
380     goto out;
381   bufs = (uint8_t (*)[64*1024])sockbuf;
382 
383   pkts = 0;
384   total_nread = 0;
385   while(pkts < max_pkts) {
386     n = (int)CURLMIN(MMSG_NUM, max_pkts);
387     memset(&mmsg, 0, sizeof(mmsg));
388     for(i = 0; i < n; ++i) {
389       msg_iov[i].iov_base = bufs[i];
390       msg_iov[i].iov_len = (int)sizeof(bufs[i]);
391       mmsg[i].msg_hdr.msg_iov = &msg_iov[i];
392       mmsg[i].msg_hdr.msg_iovlen = 1;
393       mmsg[i].msg_hdr.msg_name = &remote_addr[i];
394       mmsg[i].msg_hdr.msg_namelen = sizeof(remote_addr[i]);
395       mmsg[i].msg_hdr.msg_control = &msg_ctrl[i * CMSG_SPACE(sizeof(int))];
396       mmsg[i].msg_hdr.msg_controllen = CMSG_SPACE(sizeof(int));
397     }
398 
399     while((mcount = recvmmsg(qctx->sockfd, mmsg, n, 0, NULL)) == -1 &&
400           SOCKERRNO == EINTR)
401       ;
402     if(mcount == -1) {
403       if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
404         CURL_TRC_CF(data, cf, "ingress, recvmmsg -> EAGAIN");
405         goto out;
406       }
407       if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
408         struct ip_quadruple ip;
409         Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip);
410         failf(data, "QUIC: connection to %s port %u refused",
411               ip.remote_ip, ip.remote_port);
412         result = CURLE_COULDNT_CONNECT;
413         goto out;
414       }
415       Curl_strerror(SOCKERRNO, errstr, sizeof(errstr));
416       failf(data, "QUIC: recvmsg() unexpectedly returned %d (errno=%d; %s)",
417                   mcount, SOCKERRNO, errstr);
418       result = CURLE_RECV_ERROR;
419       goto out;
420     }
421 
422     CURL_TRC_CF(data, cf, "recvmmsg() -> %d packets", mcount);
423     for(i = 0; i < mcount; ++i) {
424       total_nread += mmsg[i].msg_len;
425 
426       gso_size = vquic_msghdr_get_udp_gro(&mmsg[i].msg_hdr);
427       if(gso_size == 0) {
428         gso_size = mmsg[i].msg_len;
429       }
430 
431       for(offset = 0; offset < mmsg[i].msg_len; offset = to) {
432         ++pkts;
433 
434         to = offset + gso_size;
435         if(to > mmsg[i].msg_len) {
436           pktlen = mmsg[i].msg_len - offset;
437         }
438         else {
439           pktlen = gso_size;
440         }
441 
442         result = recv_cb(bufs[i] + offset, pktlen, mmsg[i].msg_hdr.msg_name,
443                          mmsg[i].msg_hdr.msg_namelen, 0, userp);
444         if(result)
445           goto out;
446       }
447     }
448   }
449 
450 out:
451   if(total_nread || result)
452     CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d",
453                 pkts, total_nread, result);
454   Curl_multi_xfer_sockbuf_release(data, sockbuf);
455   return result;
456 }
457 
458 #elif defined(HAVE_SENDMSG)
recvmsg_packets(struct Curl_cfilter * cf,struct Curl_easy * data,struct cf_quic_ctx * qctx,size_t max_pkts,vquic_recv_pkt_cb * recv_cb,void * userp)459 static CURLcode recvmsg_packets(struct Curl_cfilter *cf,
460                                 struct Curl_easy *data,
461                                 struct cf_quic_ctx *qctx,
462                                 size_t max_pkts,
463                                 vquic_recv_pkt_cb *recv_cb, void *userp)
464 {
465   struct iovec msg_iov;
466   struct msghdr msg;
467   uint8_t buf[64*1024];
468   struct sockaddr_storage remote_addr;
469   size_t total_nread, pkts;
470   ssize_t nread;
471   char errstr[STRERROR_LEN];
472   CURLcode result = CURLE_OK;
473   uint8_t msg_ctrl[CMSG_SPACE(sizeof(int))];
474   size_t gso_size;
475   size_t pktlen;
476   size_t offset, to;
477 
478   msg_iov.iov_base = buf;
479   msg_iov.iov_len = (int)sizeof(buf);
480 
481   memset(&msg, 0, sizeof(msg));
482   msg.msg_iov = &msg_iov;
483   msg.msg_iovlen = 1;
484   msg.msg_control = msg_ctrl;
485 
486   DEBUGASSERT(max_pkts > 0);
487   for(pkts = 0, total_nread = 0; pkts < max_pkts;) {
488     msg.msg_name = &remote_addr;
489     msg.msg_namelen = sizeof(remote_addr);
490     msg.msg_controllen = sizeof(msg_ctrl);
491     while((nread = recvmsg(qctx->sockfd, &msg, 0)) == -1 &&
492           SOCKERRNO == EINTR)
493       ;
494     if(nread == -1) {
495       if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
496         goto out;
497       }
498       if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
499         struct ip_quadruple ip;
500         Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip);
501         failf(data, "QUIC: connection to %s port %u refused",
502               ip.remote_ip, ip.remote_port);
503         result = CURLE_COULDNT_CONNECT;
504         goto out;
505       }
506       Curl_strerror(SOCKERRNO, errstr, sizeof(errstr));
507       failf(data, "QUIC: recvmsg() unexpectedly returned %zd (errno=%d; %s)",
508                   nread, SOCKERRNO, errstr);
509       result = CURLE_RECV_ERROR;
510       goto out;
511     }
512 
513     total_nread += (size_t)nread;
514 
515     gso_size = vquic_msghdr_get_udp_gro(&msg);
516     if(gso_size == 0) {
517       gso_size = (size_t)nread;
518     }
519 
520     for(offset = 0; offset < (size_t)nread; offset = to) {
521       ++pkts;
522 
523       to = offset + gso_size;
524       if(to > (size_t)nread) {
525         pktlen = (size_t)nread - offset;
526       }
527       else {
528         pktlen = gso_size;
529       }
530 
531       result =
532         recv_cb(buf + offset, pktlen, msg.msg_name, msg.msg_namelen, 0, userp);
533       if(result)
534         goto out;
535     }
536   }
537 
538 out:
539   if(total_nread || result)
540     CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d",
541                 pkts, total_nread, result);
542   return result;
543 }
544 
545 #else /* HAVE_SENDMMSG || HAVE_SENDMSG */
recvfrom_packets(struct Curl_cfilter * cf,struct Curl_easy * data,struct cf_quic_ctx * qctx,size_t max_pkts,vquic_recv_pkt_cb * recv_cb,void * userp)546 static CURLcode recvfrom_packets(struct Curl_cfilter *cf,
547                                  struct Curl_easy *data,
548                                  struct cf_quic_ctx *qctx,
549                                  size_t max_pkts,
550                                  vquic_recv_pkt_cb *recv_cb, void *userp)
551 {
552   uint8_t buf[64*1024];
553   int bufsize = (int)sizeof(buf);
554   struct sockaddr_storage remote_addr;
555   socklen_t remote_addrlen = sizeof(remote_addr);
556   size_t total_nread, pkts;
557   ssize_t nread;
558   char errstr[STRERROR_LEN];
559   CURLcode result = CURLE_OK;
560 
561   DEBUGASSERT(max_pkts > 0);
562   for(pkts = 0, total_nread = 0; pkts < max_pkts;) {
563     while((nread = recvfrom(qctx->sockfd, (char *)buf, bufsize, 0,
564                             (struct sockaddr *)&remote_addr,
565                             &remote_addrlen)) == -1 &&
566           SOCKERRNO == EINTR)
567       ;
568     if(nread == -1) {
569       if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
570         CURL_TRC_CF(data, cf, "ingress, recvfrom -> EAGAIN");
571         goto out;
572       }
573       if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
574         struct ip_quadruple ip;
575         Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip);
576         failf(data, "QUIC: connection to %s port %u refused",
577               ip.remote_ip, ip.remote_port);
578         result = CURLE_COULDNT_CONNECT;
579         goto out;
580       }
581       Curl_strerror(SOCKERRNO, errstr, sizeof(errstr));
582       failf(data, "QUIC: recvfrom() unexpectedly returned %zd (errno=%d; %s)",
583                   nread, SOCKERRNO, errstr);
584       result = CURLE_RECV_ERROR;
585       goto out;
586     }
587 
588     ++pkts;
589     total_nread += (size_t)nread;
590     result = recv_cb(buf, (size_t)nread, &remote_addr, remote_addrlen,
591                      0, userp);
592     if(result)
593       goto out;
594   }
595 
596 out:
597   if(total_nread || result)
598     CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d",
599                 pkts, total_nread, result);
600   return result;
601 }
602 #endif /* !HAVE_SENDMMSG && !HAVE_SENDMSG */
603 
vquic_recv_packets(struct Curl_cfilter * cf,struct Curl_easy * data,struct cf_quic_ctx * qctx,size_t max_pkts,vquic_recv_pkt_cb * recv_cb,void * userp)604 CURLcode vquic_recv_packets(struct Curl_cfilter *cf,
605                             struct Curl_easy *data,
606                             struct cf_quic_ctx *qctx,
607                             size_t max_pkts,
608                             vquic_recv_pkt_cb *recv_cb, void *userp)
609 {
610   CURLcode result;
611 #if defined(HAVE_SENDMMSG)
612   result = recvmmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp);
613 #elif defined(HAVE_SENDMSG)
614   result = recvmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp);
615 #else
616   result = recvfrom_packets(cf, data, qctx, max_pkts, recv_cb, userp);
617 #endif
618   if(!result) {
619     if(!qctx->got_first_byte) {
620       qctx->got_first_byte = TRUE;
621       qctx->first_byte_at = qctx->last_op;
622     }
623     qctx->last_io = qctx->last_op;
624   }
625   return result;
626 }
627 
628 /*
629  * If the QLOGDIR environment variable is set, open and return a file
630  * descriptor to write the log to.
631  *
632  * This function returns error if something failed outside of failing to
633  * create the file. Open file success is deemed by seeing if the returned fd
634  * is != -1.
635  */
Curl_qlogdir(struct Curl_easy * data,unsigned char * scid,size_t scidlen,int * qlogfdp)636 CURLcode Curl_qlogdir(struct Curl_easy *data,
637                       unsigned char *scid,
638                       size_t scidlen,
639                       int *qlogfdp)
640 {
641   const char *qlog_dir = getenv("QLOGDIR");
642   *qlogfdp = -1;
643   if(qlog_dir) {
644     struct dynbuf fname;
645     CURLcode result;
646     unsigned int i;
647     Curl_dyn_init(&fname, DYN_QLOG_NAME);
648     result = Curl_dyn_add(&fname, qlog_dir);
649     if(!result)
650       result = Curl_dyn_add(&fname, "/");
651     for(i = 0; (i < scidlen) && !result; i++) {
652       char hex[3];
653       msnprintf(hex, 3, "%02x", scid[i]);
654       result = Curl_dyn_add(&fname, hex);
655     }
656     if(!result)
657       result = Curl_dyn_add(&fname, ".sqlog");
658 
659     if(!result) {
660       int qlogfd = open(Curl_dyn_ptr(&fname), QLOGMODE,
661                         data->set.new_file_perms);
662       if(qlogfd != -1)
663         *qlogfdp = qlogfd;
664     }
665     Curl_dyn_free(&fname);
666     if(result)
667       return result;
668   }
669 
670   return CURLE_OK;
671 }
672 
Curl_cf_quic_create(struct Curl_cfilter ** pcf,struct Curl_easy * data,struct connectdata * conn,const struct Curl_addrinfo * ai,int transport)673 CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
674                              struct Curl_easy *data,
675                              struct connectdata *conn,
676                              const struct Curl_addrinfo *ai,
677                              int transport)
678 {
679   (void)transport;
680   DEBUGASSERT(transport == TRNSPRT_QUIC);
681 #if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
682   return Curl_cf_ngtcp2_create(pcf, data, conn, ai);
683 #elif defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)
684   return Curl_cf_osslq_create(pcf, data, conn, ai);
685 #elif defined(USE_QUICHE)
686   return Curl_cf_quiche_create(pcf, data, conn, ai);
687 #elif defined(USE_MSH3)
688   return Curl_cf_msh3_create(pcf, data, conn, ai);
689 #else
690   *pcf = NULL;
691   (void)data;
692   (void)conn;
693   (void)ai;
694   return CURLE_NOT_BUILT_IN;
695 #endif
696 }
697 
Curl_conn_is_http3(const struct Curl_easy * data,const struct connectdata * conn,int sockindex)698 bool Curl_conn_is_http3(const struct Curl_easy *data,
699                         const struct connectdata *conn,
700                         int sockindex)
701 {
702 #if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
703   return Curl_conn_is_ngtcp2(data, conn, sockindex);
704 #elif defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)
705   return Curl_conn_is_osslq(data, conn, sockindex);
706 #elif defined(USE_QUICHE)
707   return Curl_conn_is_quiche(data, conn, sockindex);
708 #elif defined(USE_MSH3)
709   return Curl_conn_is_msh3(data, conn, sockindex);
710 #else
711   return ((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
712           (conn->httpversion == 30));
713 #endif
714 }
715 
Curl_conn_may_http3(struct Curl_easy * data,const struct connectdata * conn)716 CURLcode Curl_conn_may_http3(struct Curl_easy *data,
717                              const struct connectdata *conn)
718 {
719   if(conn->transport == TRNSPRT_UNIX) {
720     /* cannot do QUIC over a Unix domain socket */
721     return CURLE_QUIC_CONNECT_ERROR;
722   }
723   if(!(conn->handler->flags & PROTOPT_SSL)) {
724     failf(data, "HTTP/3 requested for non-HTTPS URL");
725     return CURLE_URL_MALFORMAT;
726   }
727 #ifndef CURL_DISABLE_PROXY
728   if(conn->bits.socksproxy) {
729     failf(data, "HTTP/3 is not supported over a SOCKS proxy");
730     return CURLE_URL_MALFORMAT;
731   }
732   if(conn->bits.httpproxy && conn->bits.tunnel_proxy) {
733     failf(data, "HTTP/3 is not supported over an HTTP proxy");
734     return CURLE_URL_MALFORMAT;
735   }
736 #endif
737 
738   return CURLE_OK;
739 }
740 
741 #else /* USE_HTTP3 */
742 
Curl_conn_may_http3(struct Curl_easy * data,const struct connectdata * conn)743 CURLcode Curl_conn_may_http3(struct Curl_easy *data,
744                              const struct connectdata *conn)
745 {
746   (void)conn;
747   (void)data;
748   DEBUGF(infof(data, "QUIC is not supported in this build"));
749   return CURLE_NOT_BUILT_IN;
750 }
751 
752 #endif /* !USE_HTTP3 */
753