summaryrefslogtreecommitdiff
path: root/lib/vquic/quiche.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vquic/quiche.c')
-rw-r--r--lib/vquic/quiche.c229
1 files changed, 229 insertions, 0 deletions
diff --git a/lib/vquic/quiche.c b/lib/vquic/quiche.c
new file mode 100644
index 000000000..f6c4ad42c
--- /dev/null
+++ b/lib/vquic/quiche.c
@@ -0,0 +1,229 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_QUICHE
+#include <quiche.h>
+#include <openssl/err.h>
+#include "urldata.h"
+#include "sendf.h"
+#include "strdup.h"
+#include "rand.h"
+#include "quic.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define QUIC_MAX_STREAMS (256*1024)
+#define QUIC_MAX_DATA (1*1024*1024)
+#define QUIC_IDLE_TIMEOUT 60 * 1000 /* milliseconds */
+
+static CURLcode process_ingress(struct connectdata *conn,
+ curl_socket_t sockfd);
+
+static CURLcode flush_egress(struct connectdata *conn, curl_socket_t sockfd);
+
+static Curl_recv quic_stream_recv;
+static Curl_send quic_stream_send;
+
+
+CURLcode Curl_quic_connect(struct connectdata *conn, curl_socket_t sockfd,
+ const struct sockaddr *addr, socklen_t addrlen)
+{
+ CURLcode result;
+ struct quicsocket *qs = &conn->quic;
+ (void)addr;
+ (void)addrlen;
+
+ infof(conn->data, "Connecting socket %d over QUIC\n", sockfd);
+
+ qs->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION);
+ if(!qs->cfg)
+ return CURLE_FAILED_INIT; /* TODO: better return code */
+
+ quiche_config_set_idle_timeout(qs->cfg, QUIC_IDLE_TIMEOUT);
+ quiche_config_set_initial_max_data(qs->cfg, QUIC_MAX_DATA);
+ quiche_config_set_initial_max_stream_data_bidi_local(qs->cfg, QUIC_MAX_DATA);
+ quiche_config_set_initial_max_stream_data_bidi_remote(qs->cfg, QUIC_MAX_DATA);
+ quiche_config_set_initial_max_stream_data_uni(qs->cfg, QUIC_MAX_DATA);
+ quiche_config_set_initial_max_streams_bidi(qs->cfg, QUIC_MAX_STREAMS);
+ quiche_config_set_initial_max_streams_uni(qs->cfg, QUIC_MAX_STREAMS);
+ quiche_config_set_application_protos(qs->cfg, (uint8_t *) "\x05hq-20", 6);
+
+ result = Curl_rand(conn->data, qs->scid, sizeof(qs->scid));
+ if(result)
+ return result;
+
+ qs->conn = quiche_connect(conn->host.name, (const uint8_t *) qs->scid,
+ sizeof(qs->scid), qs->cfg);
+ if(!qs->conn)
+ return CURLE_FAILED_INIT; /* TODO: better return code */
+
+ result = flush_egress(conn, sockfd);
+ if(result)
+ return CURLE_FAILED_INIT; /* TODO: better return code */
+
+ infof(conn->data, "Sent QUIC client Initial\n");
+
+ return CURLE_OK;
+}
+
+CURLcode Curl_quic_is_connected(struct connectdata *conn, int sockindex,
+ bool *done)
+{
+ CURLcode result;
+ struct quicsocket *qs = &conn->quic;
+ curl_socket_t sockfd = conn->sock[sockindex];
+
+ result = process_ingress(conn, sockfd);
+ if(result)
+ return result;
+
+ result = flush_egress(conn, sockfd);
+ if(result)
+ return result;
+
+ if(quiche_conn_is_established(qs->conn)) {
+ conn->recv[sockindex] = quic_stream_recv;
+ conn->send[sockindex] = quic_stream_send;
+ *done = TRUE;
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode process_ingress(struct connectdata *conn, int sockfd)
+{
+ ssize_t recvd;
+ struct quicsocket *qs = &conn->quic;
+ static uint8_t buf[65535];
+
+ do {
+ recvd = recv(sockfd, buf, sizeof(buf), 0);
+ if((recvd < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
+ break;
+
+ if(recvd < 0)
+ return CURLE_RECV_ERROR;
+
+ recvd = quiche_conn_recv(qs->conn, buf, recvd);
+ if(recvd == QUICHE_ERR_DONE)
+ break;
+
+ if(recvd < 0)
+ return CURLE_RECV_ERROR;
+ } while(1);
+
+ return CURLE_OK;
+}
+
+static CURLcode flush_egress(struct connectdata *conn, int sockfd)
+{
+ ssize_t sent;
+ struct quicsocket *qs = &conn->quic;
+ static uint8_t out[1200];
+
+ do {
+ sent = quiche_conn_send(qs->conn, out, sizeof(out));
+ if(sent == QUICHE_ERR_DONE)
+ break;
+
+ if(sent < 0)
+ return CURLE_SEND_ERROR;
+
+ sent = send(sockfd, out, sent, 0);
+ if(sent < 0)
+ return CURLE_SEND_ERROR;
+ } while(1);
+
+ return CURLE_OK;
+}
+
+static ssize_t quic_stream_recv(struct connectdata *conn,
+ int sockindex,
+ char *buf,
+ size_t buffersize,
+ CURLcode *curlcode)
+{
+ bool fin;
+ ssize_t recvd;
+ struct quicsocket *qs = &conn->quic;
+ curl_socket_t sockfd = conn->sock[sockindex];
+
+ if(process_ingress(conn, sockfd)) {
+ *curlcode = CURLE_RECV_ERROR;
+ return -1;
+ }
+
+ recvd = quiche_conn_stream_recv(qs->conn, 0, (uint8_t *) buf, buffersize, &fin);
+ if(recvd == QUICHE_ERR_DONE) {
+ *curlcode = CURLE_AGAIN;
+ return -1;
+ }
+
+ if(recvd < 0) {
+ *curlcode = CURLE_RECV_ERROR;
+ return -1;
+ }
+
+ *curlcode = CURLE_OK;
+ return recvd;
+}
+
+static ssize_t quic_stream_send(struct connectdata *conn,
+ int sockindex,
+ const void *mem,
+ size_t len,
+ CURLcode *curlcode)
+{
+ ssize_t sent;
+ struct quicsocket *qs = &conn->quic;
+ curl_socket_t sockfd = conn->sock[sockindex];
+
+ sent = quiche_conn_stream_send(qs->conn, 0, mem, len, true);
+ if(sent < 0) {
+ *curlcode = CURLE_SEND_ERROR;
+ return -1;
+ }
+
+ if(flush_egress(conn, sockfd)) {
+ *curlcode = CURLE_SEND_ERROR;
+ return -1;
+ }
+
+ *curlcode = CURLE_OK;
+ return sent;
+}
+
+/*
+ * Store quiche version info in this buffer, Prefix with a space. Return total
+ * length written.
+ */
+int Curl_quic_ver(char *p, size_t len)
+{
+ return msnprintf(p, len, " quiche");
+}
+
+#endif