2
0

vquic.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  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. /* WIP, experimental: use recvmmsg() on linux
  25. * we have no configure check, yet
  26. * and also it is only available for _GNU_SOURCE, which
  27. * we do not use otherwise.
  28. #define HAVE_SENDMMSG
  29. */
  30. #if defined(HAVE_SENDMMSG)
  31. #define _GNU_SOURCE
  32. #include <sys/socket.h>
  33. #undef _GNU_SOURCE
  34. #endif
  35. #include "curl_setup.h"
  36. #ifdef HAVE_FCNTL_H
  37. #include <fcntl.h>
  38. #endif
  39. #include "urldata.h"
  40. #include "bufq.h"
  41. #include "dynbuf.h"
  42. #include "cfilters.h"
  43. #include "curl_log.h"
  44. #include "curl_msh3.h"
  45. #include "curl_ngtcp2.h"
  46. #include "curl_quiche.h"
  47. #include "vquic.h"
  48. #include "vquic_int.h"
  49. /* The last 3 #include files should be in this order */
  50. #include "curl_printf.h"
  51. #include "curl_memory.h"
  52. #include "memdebug.h"
  53. #ifdef ENABLE_QUIC
  54. #ifdef O_BINARY
  55. #define QLOGMODE O_WRONLY|O_CREAT|O_BINARY
  56. #else
  57. #define QLOGMODE O_WRONLY|O_CREAT
  58. #endif
  59. #define NW_CHUNK_SIZE (64 * 1024)
  60. #define NW_SEND_CHUNKS 2
  61. void Curl_quic_ver(char *p, size_t len)
  62. {
  63. #if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
  64. Curl_ngtcp2_ver(p, len);
  65. #elif defined(USE_QUICHE)
  66. Curl_quiche_ver(p, len);
  67. #elif defined(USE_MSH3)
  68. Curl_msh3_ver(p, len);
  69. #endif
  70. }
  71. CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx)
  72. {
  73. Curl_bufq_init2(&qctx->sendbuf, NW_CHUNK_SIZE, NW_SEND_CHUNKS,
  74. BUFQ_OPT_SOFT_LIMIT);
  75. #if defined(__linux__) && defined(UDP_SEGMENT) && defined(HAVE_SENDMSG)
  76. qctx->no_gso = FALSE;
  77. #else
  78. qctx->no_gso = TRUE;
  79. #endif
  80. return CURLE_OK;
  81. }
  82. void vquic_ctx_free(struct cf_quic_ctx *qctx)
  83. {
  84. Curl_bufq_free(&qctx->sendbuf);
  85. }
  86. static CURLcode send_packet_no_gso(struct Curl_cfilter *cf,
  87. struct Curl_easy *data,
  88. struct cf_quic_ctx *qctx,
  89. const uint8_t *pkt, size_t pktlen,
  90. size_t gsolen, size_t *psent);
  91. static CURLcode do_sendmsg(struct Curl_cfilter *cf,
  92. struct Curl_easy *data,
  93. struct cf_quic_ctx *qctx,
  94. const uint8_t *pkt, size_t pktlen, size_t gsolen,
  95. size_t *psent)
  96. {
  97. #ifdef HAVE_SENDMSG
  98. struct iovec msg_iov;
  99. struct msghdr msg = {0};
  100. ssize_t sent;
  101. #if defined(__linux__) && defined(UDP_SEGMENT)
  102. uint8_t msg_ctrl[32];
  103. struct cmsghdr *cm;
  104. #endif
  105. *psent = 0;
  106. msg_iov.iov_base = (uint8_t *)pkt;
  107. msg_iov.iov_len = pktlen;
  108. msg.msg_iov = &msg_iov;
  109. msg.msg_iovlen = 1;
  110. #if defined(__linux__) && defined(UDP_SEGMENT)
  111. if(pktlen > gsolen) {
  112. /* Only set this, when we need it. macOS, for example,
  113. * does not seem to like a msg_control of length 0. */
  114. msg.msg_control = msg_ctrl;
  115. assert(sizeof(msg_ctrl) >= CMSG_SPACE(sizeof(uint16_t)));
  116. msg.msg_controllen = CMSG_SPACE(sizeof(uint16_t));
  117. cm = CMSG_FIRSTHDR(&msg);
  118. cm->cmsg_level = SOL_UDP;
  119. cm->cmsg_type = UDP_SEGMENT;
  120. cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));
  121. *(uint16_t *)(void *)CMSG_DATA(cm) = gsolen & 0xffff;
  122. }
  123. #endif
  124. while((sent = sendmsg(qctx->sockfd, &msg, 0)) == -1 && SOCKERRNO == EINTR)
  125. ;
  126. if(sent == -1) {
  127. switch(SOCKERRNO) {
  128. case EAGAIN:
  129. #if EAGAIN != EWOULDBLOCK
  130. case EWOULDBLOCK:
  131. #endif
  132. return CURLE_AGAIN;
  133. case EMSGSIZE:
  134. /* UDP datagram is too large; caused by PMTUD. Just let it be lost. */
  135. break;
  136. case EIO:
  137. if(pktlen > gsolen) {
  138. /* GSO failure */
  139. failf(data, "sendmsg() returned %zd (errno %d); disable GSO", sent,
  140. SOCKERRNO);
  141. qctx->no_gso = TRUE;
  142. return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent);
  143. }
  144. /* FALLTHROUGH */
  145. default:
  146. failf(data, "sendmsg() returned %zd (errno %d)", sent, SOCKERRNO);
  147. return CURLE_SEND_ERROR;
  148. }
  149. }
  150. else {
  151. assert(pktlen == (size_t)sent);
  152. }
  153. #else
  154. ssize_t sent;
  155. (void)gsolen;
  156. *psent = 0;
  157. while((sent = send(qctx->sockfd,
  158. (const char *)pkt, (SEND_TYPE_ARG3)pktlen, 0)) == -1 &&
  159. SOCKERRNO == EINTR)
  160. ;
  161. if(sent == -1) {
  162. if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
  163. return CURLE_AGAIN;
  164. }
  165. else {
  166. failf(data, "send() returned %zd (errno %d)", sent, SOCKERRNO);
  167. if(SOCKERRNO != EMSGSIZE) {
  168. return CURLE_SEND_ERROR;
  169. }
  170. /* UDP datagram is too large; caused by PMTUD. Just let it be
  171. lost. */
  172. }
  173. }
  174. #endif
  175. (void)cf;
  176. *psent = pktlen;
  177. return CURLE_OK;
  178. }
  179. static CURLcode send_packet_no_gso(struct Curl_cfilter *cf,
  180. struct Curl_easy *data,
  181. struct cf_quic_ctx *qctx,
  182. const uint8_t *pkt, size_t pktlen,
  183. size_t gsolen, size_t *psent)
  184. {
  185. const uint8_t *p, *end = pkt + pktlen;
  186. size_t sent;
  187. *psent = 0;
  188. for(p = pkt; p < end; p += gsolen) {
  189. size_t len = CURLMIN(gsolen, (size_t)(end - p));
  190. CURLcode curlcode = do_sendmsg(cf, data, qctx, p, len, len, &sent);
  191. if(curlcode != CURLE_OK) {
  192. return curlcode;
  193. }
  194. *psent += sent;
  195. }
  196. return CURLE_OK;
  197. }
  198. static CURLcode vquic_send_packets(struct Curl_cfilter *cf,
  199. struct Curl_easy *data,
  200. struct cf_quic_ctx *qctx,
  201. const uint8_t *pkt, size_t pktlen,
  202. size_t gsolen, size_t *psent)
  203. {
  204. if(qctx->no_gso && pktlen > gsolen) {
  205. return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent);
  206. }
  207. return do_sendmsg(cf, data, qctx, pkt, pktlen, gsolen, psent);
  208. }
  209. CURLcode vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data,
  210. struct cf_quic_ctx *qctx)
  211. {
  212. const unsigned char *buf;
  213. size_t blen, sent;
  214. CURLcode result;
  215. size_t gsolen;
  216. while(Curl_bufq_peek(&qctx->sendbuf, &buf, &blen)) {
  217. gsolen = qctx->gsolen;
  218. if(qctx->split_len) {
  219. gsolen = qctx->split_gsolen;
  220. if(blen > qctx->split_len)
  221. blen = qctx->split_len;
  222. }
  223. DEBUGF(LOG_CF(data, cf, "vquic_send(len=%zu, gso=%zu)",
  224. blen, gsolen));
  225. result = vquic_send_packets(cf, data, qctx, buf, blen, gsolen, &sent);
  226. DEBUGF(LOG_CF(data, cf, "vquic_send(len=%zu, gso=%zu) -> %d, sent=%zu",
  227. blen, gsolen, result, sent));
  228. if(result) {
  229. if(result == CURLE_AGAIN) {
  230. Curl_bufq_skip(&qctx->sendbuf, sent);
  231. if(qctx->split_len)
  232. qctx->split_len -= sent;
  233. }
  234. return result;
  235. }
  236. Curl_bufq_skip(&qctx->sendbuf, sent);
  237. if(qctx->split_len)
  238. qctx->split_len -= sent;
  239. }
  240. return CURLE_OK;
  241. }
  242. CURLcode vquic_send(struct Curl_cfilter *cf, struct Curl_easy *data,
  243. struct cf_quic_ctx *qctx, size_t gsolen)
  244. {
  245. qctx->gsolen = gsolen;
  246. return vquic_flush(cf, data, qctx);
  247. }
  248. CURLcode vquic_send_tail_split(struct Curl_cfilter *cf, struct Curl_easy *data,
  249. struct cf_quic_ctx *qctx, size_t gsolen,
  250. size_t tail_len, size_t tail_gsolen)
  251. {
  252. DEBUGASSERT(Curl_bufq_len(&qctx->sendbuf) > tail_len);
  253. qctx->split_len = Curl_bufq_len(&qctx->sendbuf) - tail_len;
  254. qctx->split_gsolen = gsolen;
  255. qctx->gsolen = tail_gsolen;
  256. DEBUGF(LOG_CF(data, cf, "vquic_send_tail_split: [%zu gso=%zu][%zu gso=%zu]",
  257. qctx->split_len, qctx->split_gsolen,
  258. tail_len, qctx->gsolen));
  259. return vquic_flush(cf, data, qctx);
  260. }
  261. #ifdef HAVE_SENDMMSG
  262. static CURLcode recvmmsg_packets(struct Curl_cfilter *cf,
  263. struct Curl_easy *data,
  264. struct cf_quic_ctx *qctx,
  265. size_t max_pkts,
  266. vquic_recv_pkt_cb *recv_cb, void *userp)
  267. {
  268. #define MMSG_NUM 64
  269. struct iovec msg_iov[MMSG_NUM];
  270. struct mmsghdr mmsg[MMSG_NUM];
  271. uint8_t bufs[MMSG_NUM][2*1024];
  272. struct sockaddr_storage remote_addr[MMSG_NUM];
  273. size_t total_nread, pkts;
  274. int mcount, i, n;
  275. CURLcode result = CURLE_OK;
  276. DEBUGASSERT(max_pkts > 0);
  277. pkts = 0;
  278. total_nread = 0;
  279. while(pkts < max_pkts) {
  280. n = (int)CURLMIN(MMSG_NUM, max_pkts);
  281. memset(&mmsg, 0, sizeof(mmsg));
  282. for(i = 0; i < n; ++i) {
  283. msg_iov[i].iov_base = bufs[i];
  284. msg_iov[i].iov_len = (int)sizeof(bufs[i]);
  285. mmsg[i].msg_hdr.msg_iov = &msg_iov[i];
  286. mmsg[i].msg_hdr.msg_iovlen = 1;
  287. mmsg[i].msg_hdr.msg_name = &remote_addr[i];
  288. mmsg[i].msg_hdr.msg_namelen = sizeof(remote_addr[i]);
  289. }
  290. while((mcount = recvmmsg(qctx->sockfd, mmsg, n, 0, NULL)) == -1 &&
  291. SOCKERRNO == EINTR)
  292. ;
  293. if(mcount == -1) {
  294. if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
  295. DEBUGF(LOG_CF(data, cf, "ingress, recvmmsg -> EAGAIN"));
  296. goto out;
  297. }
  298. if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
  299. const char *r_ip;
  300. int r_port;
  301. Curl_cf_socket_peek(cf->next, data, NULL, NULL,
  302. &r_ip, &r_port, NULL, NULL);
  303. failf(data, "QUIC: connection to %s port %u refused",
  304. r_ip, r_port);
  305. result = CURLE_COULDNT_CONNECT;
  306. goto out;
  307. }
  308. failf(data, "QUIC: recvmsg() unexpectedly returned %d (errno=%d)",
  309. mcount, SOCKERRNO);
  310. result = CURLE_RECV_ERROR;
  311. goto out;
  312. }
  313. DEBUGF(LOG_CF(data, cf, "recvmmsg() -> %d packets", mcount));
  314. pkts += mcount;
  315. for(i = 0; i < mcount; ++i) {
  316. total_nread += mmsg[i].msg_len;
  317. result = recv_cb(bufs[i], mmsg[i].msg_len,
  318. mmsg[i].msg_hdr.msg_name, mmsg[i].msg_hdr.msg_namelen,
  319. 0, userp);
  320. if(result)
  321. goto out;
  322. }
  323. }
  324. out:
  325. DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zu bytes -> %d",
  326. pkts, total_nread, result));
  327. return result;
  328. }
  329. #elif defined(HAVE_SENDMSG)
  330. static CURLcode recvmsg_packets(struct Curl_cfilter *cf,
  331. struct Curl_easy *data,
  332. struct cf_quic_ctx *qctx,
  333. size_t max_pkts,
  334. vquic_recv_pkt_cb *recv_cb, void *userp)
  335. {
  336. struct iovec msg_iov;
  337. struct msghdr msg;
  338. uint8_t buf[64*1024];
  339. struct sockaddr_storage remote_addr;
  340. size_t total_nread, pkts;
  341. ssize_t nread;
  342. CURLcode result = CURLE_OK;
  343. msg_iov.iov_base = buf;
  344. msg_iov.iov_len = (int)sizeof(buf);
  345. memset(&msg, 0, sizeof(msg));
  346. msg.msg_iov = &msg_iov;
  347. msg.msg_iovlen = 1;
  348. DEBUGASSERT(max_pkts > 0);
  349. for(pkts = 0, total_nread = 0; pkts < max_pkts;) {
  350. msg.msg_name = &remote_addr;
  351. msg.msg_namelen = sizeof(remote_addr);
  352. while((nread = recvmsg(qctx->sockfd, &msg, 0)) == -1 &&
  353. SOCKERRNO == EINTR)
  354. ;
  355. if(nread == -1) {
  356. if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
  357. goto out;
  358. }
  359. if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
  360. const char *r_ip;
  361. int r_port;
  362. Curl_cf_socket_peek(cf->next, data, NULL, NULL,
  363. &r_ip, &r_port, NULL, NULL);
  364. failf(data, "QUIC: connection to %s port %u refused",
  365. r_ip, r_port);
  366. result = CURLE_COULDNT_CONNECT;
  367. goto out;
  368. }
  369. failf(data, "QUIC: recvmsg() unexpectedly returned %zd (errno=%d)",
  370. nread, SOCKERRNO);
  371. result = CURLE_RECV_ERROR;
  372. goto out;
  373. }
  374. ++pkts;
  375. total_nread += (size_t)nread;
  376. result = recv_cb(buf, (size_t)nread, msg.msg_name, msg.msg_namelen,
  377. 0, userp);
  378. if(result)
  379. goto out;
  380. }
  381. out:
  382. DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zu bytes -> %d",
  383. pkts, total_nread, result));
  384. return result;
  385. }
  386. #else /* HAVE_SENDMMSG || HAVE_SENDMSG */
  387. static CURLcode recvfrom_packets(struct Curl_cfilter *cf,
  388. struct Curl_easy *data,
  389. struct cf_quic_ctx *qctx,
  390. size_t max_pkts,
  391. vquic_recv_pkt_cb *recv_cb, void *userp)
  392. {
  393. uint8_t buf[64*1024];
  394. int bufsize = (int)sizeof(buf);
  395. struct sockaddr_storage remote_addr;
  396. socklen_t remote_addrlen = sizeof(remote_addr);
  397. size_t total_nread, pkts;
  398. ssize_t nread;
  399. CURLcode result = CURLE_OK;
  400. DEBUGASSERT(max_pkts > 0);
  401. for(pkts = 0, total_nread = 0; pkts < max_pkts;) {
  402. while((nread = recvfrom(qctx->sockfd, (char *)buf, bufsize, 0,
  403. (struct sockaddr *)&remote_addr,
  404. &remote_addrlen)) == -1 &&
  405. SOCKERRNO == EINTR)
  406. ;
  407. if(nread == -1) {
  408. if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
  409. DEBUGF(LOG_CF(data, cf, "ingress, recvfrom -> EAGAIN"));
  410. goto out;
  411. }
  412. if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
  413. const char *r_ip;
  414. int r_port;
  415. Curl_cf_socket_peek(cf->next, data, NULL, NULL,
  416. &r_ip, &r_port, NULL, NULL);
  417. failf(data, "QUIC: connection to %s port %u refused",
  418. r_ip, r_port);
  419. result = CURLE_COULDNT_CONNECT;
  420. goto out;
  421. }
  422. failf(data, "QUIC: recvfrom() unexpectedly returned %zd (errno=%d)",
  423. nread, SOCKERRNO);
  424. result = CURLE_RECV_ERROR;
  425. goto out;
  426. }
  427. ++pkts;
  428. total_nread += (size_t)nread;
  429. result = recv_cb(buf, (size_t)nread, &remote_addr, remote_addrlen,
  430. 0, userp);
  431. if(result)
  432. goto out;
  433. }
  434. out:
  435. DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zu bytes -> %d",
  436. pkts, total_nread, result));
  437. return result;
  438. }
  439. #endif /* !HAVE_SENDMMSG && !HAVE_SENDMSG */
  440. CURLcode vquic_recv_packets(struct Curl_cfilter *cf,
  441. struct Curl_easy *data,
  442. struct cf_quic_ctx *qctx,
  443. size_t max_pkts,
  444. vquic_recv_pkt_cb *recv_cb, void *userp)
  445. {
  446. #if defined(HAVE_SENDMMSG)
  447. return recvmmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp);
  448. #elif defined(HAVE_SENDMSG)
  449. return recvmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp);
  450. #else
  451. return recvfrom_packets(cf, data, qctx, max_pkts, recv_cb, userp);
  452. #endif
  453. }
  454. /*
  455. * If the QLOGDIR environment variable is set, open and return a file
  456. * descriptor to write the log to.
  457. *
  458. * This function returns error if something failed outside of failing to
  459. * create the file. Open file success is deemed by seeing if the returned fd
  460. * is != -1.
  461. */
  462. CURLcode Curl_qlogdir(struct Curl_easy *data,
  463. unsigned char *scid,
  464. size_t scidlen,
  465. int *qlogfdp)
  466. {
  467. const char *qlog_dir = getenv("QLOGDIR");
  468. *qlogfdp = -1;
  469. if(qlog_dir) {
  470. struct dynbuf fname;
  471. CURLcode result;
  472. unsigned int i;
  473. Curl_dyn_init(&fname, DYN_QLOG_NAME);
  474. result = Curl_dyn_add(&fname, qlog_dir);
  475. if(!result)
  476. result = Curl_dyn_add(&fname, "/");
  477. for(i = 0; (i < scidlen) && !result; i++) {
  478. char hex[3];
  479. msnprintf(hex, 3, "%02x", scid[i]);
  480. result = Curl_dyn_add(&fname, hex);
  481. }
  482. if(!result)
  483. result = Curl_dyn_add(&fname, ".sqlog");
  484. if(!result) {
  485. int qlogfd = open(Curl_dyn_ptr(&fname), QLOGMODE,
  486. data->set.new_file_perms);
  487. if(qlogfd != -1)
  488. *qlogfdp = qlogfd;
  489. }
  490. Curl_dyn_free(&fname);
  491. if(result)
  492. return result;
  493. }
  494. return CURLE_OK;
  495. }
  496. CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
  497. struct Curl_easy *data,
  498. struct connectdata *conn,
  499. const struct Curl_addrinfo *ai,
  500. int transport)
  501. {
  502. (void)transport;
  503. DEBUGASSERT(transport == TRNSPRT_QUIC);
  504. #if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
  505. return Curl_cf_ngtcp2_create(pcf, data, conn, ai);
  506. #elif defined(USE_QUICHE)
  507. return Curl_cf_quiche_create(pcf, data, conn, ai);
  508. #elif defined(USE_MSH3)
  509. return Curl_cf_msh3_create(pcf, data, conn, ai);
  510. #else
  511. *pcf = NULL;
  512. (void)data;
  513. (void)conn;
  514. (void)ai;
  515. return CURLE_NOT_BUILT_IN;
  516. #endif
  517. }
  518. bool Curl_conn_is_http3(const struct Curl_easy *data,
  519. const struct connectdata *conn,
  520. int sockindex)
  521. {
  522. #if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
  523. return Curl_conn_is_ngtcp2(data, conn, sockindex);
  524. #elif defined(USE_QUICHE)
  525. return Curl_conn_is_quiche(data, conn, sockindex);
  526. #elif defined(USE_MSH3)
  527. return Curl_conn_is_msh3(data, conn, sockindex);
  528. #else
  529. return ((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
  530. (conn->httpversion == 30));
  531. #endif
  532. }
  533. CURLcode Curl_conn_may_http3(struct Curl_easy *data,
  534. const struct connectdata *conn)
  535. {
  536. if(conn->transport == TRNSPRT_UNIX) {
  537. /* cannot do QUIC over a unix domain socket */
  538. return CURLE_QUIC_CONNECT_ERROR;
  539. }
  540. if(!(conn->handler->flags & PROTOPT_SSL)) {
  541. failf(data, "HTTP/3 requested for non-HTTPS URL");
  542. return CURLE_URL_MALFORMAT;
  543. }
  544. #ifndef CURL_DISABLE_PROXY
  545. if(conn->bits.socksproxy) {
  546. failf(data, "HTTP/3 is not supported over a SOCKS proxy");
  547. return CURLE_URL_MALFORMAT;
  548. }
  549. if(conn->bits.httpproxy && conn->bits.tunnel_proxy) {
  550. failf(data, "HTTP/3 is not supported over a HTTP proxy");
  551. return CURLE_URL_MALFORMAT;
  552. }
  553. #endif
  554. return CURLE_OK;
  555. }
  556. #else /* ENABLE_QUIC */
  557. CURLcode Curl_conn_may_http3(struct Curl_easy *data,
  558. const struct connectdata *conn)
  559. {
  560. (void)conn;
  561. (void)data;
  562. DEBUGF(infof(data, "QUIC is not supported in this build"));
  563. return CURLE_NOT_BUILT_IN;
  564. }
  565. #endif /* !ENABLE_QUIC */