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