VirtualBox

source: vbox/trunk/src/libs/curl-8.0.1/lib/vquic/vquic.c@ 99344

Last change on this file since 99344 was 99344, checked in by vboxsync, 22 months ago

curl-8.0.1: Applied and adjusted our curl changes to 7.87.0 bugref:10417

  • Property svn:eol-style set to native
File size: 10.8 KB
Line 
1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <[email protected]>, 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_FCNTL_H
28#include <fcntl.h>
29#endif
30#include "urldata.h"
31#include "dynbuf.h"
32#include "cfilters.h"
33#include "curl_log.h"
34#include "curl_msh3.h"
35#include "curl_ngtcp2.h"
36#include "curl_quiche.h"
37#include "vquic.h"
38#include "vquic_int.h"
39
40/* The last 3 #include files should be in this order */
41#include "curl_printf.h"
42#include "curl_memory.h"
43#include "memdebug.h"
44
45
46#ifdef ENABLE_QUIC
47
48#ifdef O_BINARY
49#define QLOGMODE O_WRONLY|O_CREAT|O_BINARY
50#else
51#define QLOGMODE O_WRONLY|O_CREAT
52#endif
53
54void Curl_quic_ver(char *p, size_t len)
55{
56#ifdef USE_NGTCP2
57 Curl_ngtcp2_ver(p, len);
58#elif defined(USE_QUICHE)
59 Curl_quiche_ver(p, len);
60#elif defined(USE_MSH3)
61 Curl_msh3_ver(p, len);
62#endif
63}
64
65CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx, size_t pktbuflen)
66{
67 qctx->num_blocked_pkt = 0;
68 qctx->num_blocked_pkt_sent = 0;
69 memset(&qctx->blocked_pkt, 0, sizeof(qctx->blocked_pkt));
70
71 qctx->pktbuflen = pktbuflen;
72 qctx->pktbuf = malloc(qctx->pktbuflen);
73 if(!qctx->pktbuf)
74 return CURLE_OUT_OF_MEMORY;
75
76#if defined(__linux__) && defined(UDP_SEGMENT) && defined(HAVE_SENDMSG)
77 qctx->no_gso = FALSE;
78#else
79 qctx->no_gso = TRUE;
80#endif
81
82 return CURLE_OK;
83}
84
85void vquic_ctx_free(struct cf_quic_ctx *qctx)
86{
87 free(qctx->pktbuf);
88 qctx->pktbuf = NULL;
89}
90
91static CURLcode send_packet_no_gso(struct Curl_cfilter *cf,
92 struct Curl_easy *data,
93 struct cf_quic_ctx *qctx,
94 const uint8_t *pkt, size_t pktlen,
95 size_t gsolen, size_t *psent);
96
97static CURLcode do_sendmsg(struct Curl_cfilter *cf,
98 struct Curl_easy *data,
99 struct cf_quic_ctx *qctx,
100 const uint8_t *pkt, size_t pktlen, size_t gsolen,
101 size_t *psent)
102{
103#ifdef HAVE_SENDMSG
104 struct iovec msg_iov;
105 struct msghdr msg = {0};
106 ssize_t sent;
107#if defined(__linux__) && defined(UDP_SEGMENT)
108 uint8_t msg_ctrl[32];
109 struct cmsghdr *cm;
110#endif
111
112 *psent = 0;
113 msg_iov.iov_base = (uint8_t *)pkt;
114 msg_iov.iov_len = pktlen;
115 msg.msg_iov = &msg_iov;
116 msg.msg_iovlen = 1;
117
118#if defined(__linux__) && defined(UDP_SEGMENT)
119 if(pktlen > gsolen) {
120 /* Only set this, when we need it. macOS, for example,
121 * does not seem to like a msg_control of length 0. */
122 msg.msg_control = msg_ctrl;
123 assert(sizeof(msg_ctrl) >= CMSG_SPACE(sizeof(uint16_t)));
124 msg.msg_controllen = CMSG_SPACE(sizeof(uint16_t));
125 cm = CMSG_FIRSTHDR(&msg);
126 cm->cmsg_level = SOL_UDP;
127 cm->cmsg_type = UDP_SEGMENT;
128 cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));
129 *(uint16_t *)(void *)CMSG_DATA(cm) = gsolen & 0xffff;
130 }
131#endif
132
133
134 while((sent = sendmsg(qctx->sockfd, &msg, 0)) == -1 && SOCKERRNO == EINTR)
135 ;
136
137 if(sent == -1) {
138 switch(SOCKERRNO) {
139 case EAGAIN:
140#if EAGAIN != EWOULDBLOCK
141 case EWOULDBLOCK:
142#endif
143 return CURLE_AGAIN;
144 case EMSGSIZE:
145 /* UDP datagram is too large; caused by PMTUD. Just let it be lost. */
146 break;
147 case EIO:
148 if(pktlen > gsolen) {
149 /* GSO failure */
150 failf(data, "sendmsg() returned %zd (errno %d); disable GSO", sent,
151 SOCKERRNO);
152 qctx->no_gso = TRUE;
153 return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent);
154 }
155 /* FALLTHROUGH */
156 default:
157 failf(data, "sendmsg() returned %zd (errno %d)", sent, SOCKERRNO);
158 return CURLE_SEND_ERROR;
159 }
160 }
161 else {
162 assert(pktlen == (size_t)sent);
163 }
164#else
165 ssize_t sent;
166 (void)gsolen;
167
168 *psent = 0;
169
170 while((sent = send(qctx->sockfd,
171 (const char *)pkt, (SEND_TYPE_ARG3)pktlen, 0)) == -1 &&
172 SOCKERRNO == EINTR)
173 ;
174
175 if(sent == -1) {
176 if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
177 return CURLE_AGAIN;
178 }
179 else {
180 failf(data, "send() returned %zd (errno %d)", sent, SOCKERRNO);
181 if(SOCKERRNO != EMSGSIZE) {
182 return CURLE_SEND_ERROR;
183 }
184 /* UDP datagram is too large; caused by PMTUD. Just let it be
185 lost. */
186 }
187 }
188#endif
189 (void)cf;
190 *psent = pktlen;
191
192 return CURLE_OK;
193}
194
195static CURLcode send_packet_no_gso(struct Curl_cfilter *cf,
196 struct Curl_easy *data,
197 struct cf_quic_ctx *qctx,
198 const uint8_t *pkt, size_t pktlen,
199 size_t gsolen, size_t *psent)
200{
201 const uint8_t *p, *end = pkt + pktlen;
202 size_t sent;
203
204 *psent = 0;
205
206 for(p = pkt; p < end; p += gsolen) {
207 size_t len = CURLMIN(gsolen, (size_t)(end - p));
208 CURLcode curlcode = do_sendmsg(cf, data, qctx, p, len, len, &sent);
209 if(curlcode != CURLE_OK) {
210 return curlcode;
211 }
212 *psent += sent;
213 }
214
215 return CURLE_OK;
216}
217
218CURLcode vquic_send_packet(struct Curl_cfilter *cf,
219 struct Curl_easy *data,
220 struct cf_quic_ctx *qctx,
221 const uint8_t *pkt, size_t pktlen, size_t gsolen,
222 size_t *psent)
223{
224 if(qctx->no_gso && pktlen > gsolen) {
225 return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent);
226 }
227
228 return do_sendmsg(cf, data, qctx, pkt, pktlen, gsolen, psent);
229}
230
231
232
233void vquic_push_blocked_pkt(struct Curl_cfilter *cf,
234 struct cf_quic_ctx *qctx,
235 const uint8_t *pkt, size_t pktlen, size_t gsolen)
236{
237 struct vquic_blocked_pkt *blkpkt;
238
239 (void)cf;
240 assert(qctx->num_blocked_pkt <
241 sizeof(qctx->blocked_pkt) / sizeof(qctx->blocked_pkt[0]));
242
243 blkpkt = &qctx->blocked_pkt[qctx->num_blocked_pkt++];
244
245 blkpkt->pkt = pkt;
246 blkpkt->pktlen = pktlen;
247 blkpkt->gsolen = gsolen;
248}
249
250CURLcode vquic_send_blocked_pkt(struct Curl_cfilter *cf,
251 struct Curl_easy *data,
252 struct cf_quic_ctx *qctx)
253{
254 size_t sent;
255 CURLcode curlcode;
256 struct vquic_blocked_pkt *blkpkt;
257
258 (void)cf;
259 for(; qctx->num_blocked_pkt_sent < qctx->num_blocked_pkt;
260 ++qctx->num_blocked_pkt_sent) {
261 blkpkt = &qctx->blocked_pkt[qctx->num_blocked_pkt_sent];
262 curlcode = vquic_send_packet(cf, data, qctx, blkpkt->pkt,
263 blkpkt->pktlen, blkpkt->gsolen, &sent);
264
265 if(curlcode) {
266 if(curlcode == CURLE_AGAIN) {
267 blkpkt->pkt += sent;
268 blkpkt->pktlen -= sent;
269 }
270 return curlcode;
271 }
272 }
273
274 qctx->num_blocked_pkt = 0;
275 qctx->num_blocked_pkt_sent = 0;
276
277 return CURLE_OK;
278}
279
280/*
281 * If the QLOGDIR environment variable is set, open and return a file
282 * descriptor to write the log to.
283 *
284 * This function returns error if something failed outside of failing to
285 * create the file. Open file success is deemed by seeing if the returned fd
286 * is != -1.
287 */
288CURLcode Curl_qlogdir(struct Curl_easy *data,
289 unsigned char *scid,
290 size_t scidlen,
291 int *qlogfdp)
292{
293 const char *qlog_dir = getenv("QLOGDIR");
294 *qlogfdp = -1;
295 if(qlog_dir) {
296 struct dynbuf fname;
297 CURLcode result;
298 unsigned int i;
299 Curl_dyn_init(&fname, DYN_QLOG_NAME);
300 result = Curl_dyn_add(&fname, qlog_dir);
301 if(!result)
302 result = Curl_dyn_add(&fname, "/");
303 for(i = 0; (i < scidlen) && !result; i++) {
304 char hex[3];
305 msnprintf(hex, 3, "%02x", scid[i]);
306 result = Curl_dyn_add(&fname, hex);
307 }
308 if(!result)
309 result = Curl_dyn_add(&fname, ".sqlog");
310
311 if(!result) {
312 int qlogfd = open(Curl_dyn_ptr(&fname), QLOGMODE,
313 data->set.new_file_perms);
314 if(qlogfd != -1)
315 *qlogfdp = qlogfd;
316 }
317 Curl_dyn_free(&fname);
318 if(result)
319 return result;
320 }
321
322 return CURLE_OK;
323}
324
325CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
326 struct Curl_easy *data,
327 struct connectdata *conn,
328 const struct Curl_addrinfo *ai,
329 int transport)
330{
331 (void)transport;
332 DEBUGASSERT(transport == TRNSPRT_QUIC);
333#ifdef USE_NGTCP2
334 return Curl_cf_ngtcp2_create(pcf, data, conn, ai);
335#elif defined(USE_QUICHE)
336 return Curl_cf_quiche_create(pcf, data, conn, ai);
337#elif defined(USE_MSH3)
338 return Curl_cf_msh3_create(pcf, data, conn, ai);
339#else
340 *pcf = NULL;
341 (void)data;
342 (void)conn;
343 (void)ai;
344 return CURLE_NOT_BUILT_IN;
345#endif
346}
347
348bool Curl_conn_is_http3(const struct Curl_easy *data,
349 const struct connectdata *conn,
350 int sockindex)
351{
352#ifdef USE_NGTCP2
353 return Curl_conn_is_ngtcp2(data, conn, sockindex);
354#elif defined(USE_QUICHE)
355 return Curl_conn_is_quiche(data, conn, sockindex);
356#elif defined(USE_MSH3)
357 return Curl_conn_is_msh3(data, conn, sockindex);
358#else
359 return ((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
360 (conn->httpversion == 30));
361#endif
362}
363
364CURLcode Curl_conn_may_http3(struct Curl_easy *data,
365 const struct connectdata *conn)
366{
367 if(conn->transport == TRNSPRT_UNIX) {
368 /* cannot do QUIC over a unix domain socket */
369 return CURLE_QUIC_CONNECT_ERROR;
370 }
371 if(!(conn->handler->flags & PROTOPT_SSL)) {
372 failf(data, "HTTP/3 requested for non-HTTPS URL");
373 return CURLE_URL_MALFORMAT;
374 }
375#ifndef CURL_DISABLE_PROXY
376 if(conn->bits.socksproxy) {
377 failf(data, "HTTP/3 is not supported over a SOCKS proxy");
378 return CURLE_URL_MALFORMAT;
379 }
380 if(conn->bits.httpproxy && conn->bits.tunnel_proxy) {
381 failf(data, "HTTP/3 is not supported over a HTTP proxy");
382 return CURLE_URL_MALFORMAT;
383 }
384#endif
385
386 return CURLE_OK;
387}
388
389#else /* ENABLE_QUIC */
390
391CURLcode Curl_conn_may_http3(struct Curl_easy *data,
392 const struct connectdata *conn)
393{
394 (void)conn;
395 (void)data;
396 DEBUGF(infof(data, "QUIC is not supported in this build"));
397 return CURLE_NOT_BUILT_IN;
398}
399
400#endif /* !ENABLE_QUIC */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette