diff options
author | shadchin <shadchin@yandex-team.ru> | 2022-04-09 00:41:32 +0300 |
---|---|---|
committer | shadchin <shadchin@yandex-team.ru> | 2022-04-09 00:41:32 +0300 |
commit | 3bbb3d7e7e261716b0a83c1a35022e5d36f7875f (patch) | |
tree | 96955fafe753aad4486b22280aafe9050c508954 /contrib/libs/curl/lib/vtls/rustls.c | |
parent | beae089571d44f32f0edd323496377d3abe1bce6 (diff) | |
download | ydb-3bbb3d7e7e261716b0a83c1a35022e5d36f7875f.tar.gz |
CONTRIB-2513 Update contrib/libs/curl to 7.77.0
ref:88dc545867909534adecc7502c44301cabf1a899
Diffstat (limited to 'contrib/libs/curl/lib/vtls/rustls.c')
-rw-r--r-- | contrib/libs/curl/lib/vtls/rustls.c | 271 |
1 files changed, 147 insertions, 124 deletions
diff --git a/contrib/libs/curl/lib/vtls/rustls.c b/contrib/libs/curl/lib/vtls/rustls.c index 944a80e706..e9b75a5dc5 100644 --- a/contrib/libs/curl/lib/vtls/rustls.c +++ b/contrib/libs/curl/lib/vtls/rustls.c @@ -37,16 +37,11 @@ #include "multiif.h" -/* Per https://www.bearssl.org/api1.html, max TLS record size plus max - per-record overhead. */ -#define TLSBUF_SIZE (16384 + 325) - struct ssl_backend_data { const struct rustls_client_config *config; - struct rustls_client_session *session; + struct rustls_connection *conn; bool data_pending; - uint8_t *tlsbuf; }; /* For a given rustls_result error code, return the best-matching CURLcode. */ @@ -82,6 +77,28 @@ cr_connect(struct Curl_easy *data UNUSED_PARAM, return CURLE_SSL_CONNECT_ERROR; } +static int +read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n) +{ + ssize_t n = sread(*(int *)userdata, buf, len); + if(n < 0) { + return SOCKERRNO; + } + *out_n = n; + return 0; +} + +static int +write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n) +{ + ssize_t n = swrite(*(int *)userdata, buf, len); + if(n < 0) { + return SOCKERRNO; + } + *out_n = n; + return 0; +} + /* * On each run: * - Read a chunk of bytes from the socket into rustls' TLS input buffer. @@ -101,65 +118,44 @@ cr_recv(struct Curl_easy *data, int sockindex, struct connectdata *conn = data->conn; struct ssl_connect_data *const connssl = &conn->ssl[sockindex]; struct ssl_backend_data *const backend = connssl->backend; - struct rustls_client_session *const session = backend->session; - curl_socket_t sockfd = conn->sock[sockindex]; + struct rustls_connection *const rconn = backend->conn; size_t n = 0; - ssize_t tls_bytes_read = 0; - size_t tls_bytes_processed = 0; + size_t tls_bytes_read = 0; size_t plain_bytes_copied = 0; rustls_result rresult = 0; char errorbuf[255]; + rustls_io_result io_error; - tls_bytes_read = sread(sockfd, backend->tlsbuf, TLSBUF_SIZE); - if(tls_bytes_read == 0) { - failf(data, "connection closed without TLS close_notify alert"); + io_error = rustls_connection_read_tls(rconn, read_cb, + &conn->sock[sockindex], &tls_bytes_read); + if(io_error == EAGAIN || io_error == EWOULDBLOCK) { + infof(data, "sread: EAGAIN or EWOULDBLOCK\n"); + } + else if(io_error) { + failf(data, "reading from socket: %s", strerror(io_error)); *err = CURLE_READ_ERROR; return -1; } - else if(tls_bytes_read < 0) { - if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { - infof(data, "sread: EAGAIN or EWOULDBLOCK\n"); - *err = CURLE_AGAIN; - return -1; - } - failf(data, "reading from socket: %s", strerror(SOCKERRNO)); + else if(tls_bytes_read == 0) { + failf(data, "connection closed without TLS close_notify alert"); *err = CURLE_READ_ERROR; return -1; } - /* - * Now pull those bytes from the buffer into ClientSession. - */ - DEBUGASSERT(tls_bytes_read > 0); - while(tls_bytes_processed < (size_t)tls_bytes_read) { - rresult = rustls_client_session_read_tls(session, - backend->tlsbuf + tls_bytes_processed, - tls_bytes_read - tls_bytes_processed, - &n); - if(rresult != RUSTLS_RESULT_OK) { - failf(data, "error in rustls_client_session_read_tls"); - *err = CURLE_READ_ERROR; - return -1; - } - else if(n == 0) { - infof(data, "EOF from rustls_client_session_read_tls\n"); - break; - } + infof(data, "cr_recv read %ld bytes from the network\n", tls_bytes_read); - rresult = rustls_client_session_process_new_packets(session); - if(rresult != RUSTLS_RESULT_OK) { - rustls_error(rresult, errorbuf, sizeof(errorbuf), &n); - failf(data, "%.*s", n, errorbuf); - *err = map_error(rresult); - return -1; - } - - tls_bytes_processed += n; - backend->data_pending = TRUE; + rresult = rustls_connection_process_new_packets(rconn); + if(rresult != RUSTLS_RESULT_OK) { + rustls_error(rresult, errorbuf, sizeof(errorbuf), &n); + failf(data, "%.*s", n, errorbuf); + *err = map_error(rresult); + return -1; } + backend->data_pending = TRUE; + while(plain_bytes_copied < plainlen) { - rresult = rustls_client_session_read(session, + rresult = rustls_connection_read(rconn, (uint8_t *)plainbuf + plain_bytes_copied, plainlen - plain_bytes_copied, &n); @@ -168,20 +164,21 @@ cr_recv(struct Curl_easy *data, int sockindex, return 0; } else if(rresult != RUSTLS_RESULT_OK) { - failf(data, "error in rustls_client_session_read"); + failf(data, "error in rustls_connection_read"); *err = CURLE_READ_ERROR; return -1; } else if(n == 0) { - /* rustls returns 0 from client_session_read to mean "all currently + /* rustls returns 0 from connection_read to mean "all currently available data has been read." If we bring in more ciphertext with read_tls, more plaintext will become available. So don't tell curl this is an EOF. Instead, say "come back later." */ - infof(data, "EOF from rustls_client_session_read\n"); + infof(data, "cr_recv got 0 bytes of plaintext\n"); backend->data_pending = FALSE; break; } else { + infof(data, "cr_recv copied out %ld bytes of plaintext\n", n); plain_bytes_copied += n; } } @@ -214,68 +211,50 @@ cr_send(struct Curl_easy *data, int sockindex, struct connectdata *conn = data->conn; struct ssl_connect_data *const connssl = &conn->ssl[sockindex]; struct ssl_backend_data *const backend = connssl->backend; - struct rustls_client_session *const session = backend->session; - curl_socket_t sockfd = conn->sock[sockindex]; - ssize_t n = 0; + struct rustls_connection *const rconn = backend->conn; size_t plainwritten = 0; - size_t tlslen = 0; size_t tlswritten = 0; + size_t tlswritten_total = 0; rustls_result rresult; + rustls_io_result io_error; + + infof(data, "cr_send %ld bytes of plaintext\n", plainlen); if(plainlen > 0) { - rresult = rustls_client_session_write(session, - plainbuf, plainlen, &plainwritten); + rresult = rustls_connection_write(rconn, plainbuf, plainlen, + &plainwritten); if(rresult != RUSTLS_RESULT_OK) { - failf(data, "error in rustls_client_session_write"); + failf(data, "error in rustls_connection_write"); *err = CURLE_WRITE_ERROR; return -1; } else if(plainwritten == 0) { - failf(data, "EOF in rustls_client_session_write"); + failf(data, "EOF in rustls_connection_write"); *err = CURLE_WRITE_ERROR; return -1; } } - while(rustls_client_session_wants_write(session)) { - rresult = rustls_client_session_write_tls( - session, backend->tlsbuf, TLSBUF_SIZE, &tlslen); - if(rresult != RUSTLS_RESULT_OK) { - failf(data, "error in rustls_client_session_write_tls"); - *err = CURLE_WRITE_ERROR; + while(rustls_connection_wants_write(rconn)) { + io_error = rustls_connection_write_tls(rconn, write_cb, + &conn->sock[sockindex], &tlswritten); + if(io_error == EAGAIN || io_error == EWOULDBLOCK) { + infof(data, "swrite: EAGAIN after %ld bytes\n", tlswritten_total); + *err = CURLE_AGAIN; return -1; } - else if(tlslen == 0) { - failf(data, "EOF in rustls_client_session_write_tls"); + else if(io_error) { + failf(data, "writing to socket: %s", strerror(io_error)); *err = CURLE_WRITE_ERROR; return -1; } - - tlswritten = 0; - - while(tlswritten < tlslen) { - n = swrite(sockfd, backend->tlsbuf + tlswritten, tlslen - tlswritten); - if(n < 0) { - if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { - /* Since recv is called from poll, there should be room to - write at least some bytes before hitting EAGAIN. */ - infof(data, "swrite: EAGAIN after %ld bytes\n", tlswritten); - DEBUGASSERT(tlswritten > 0); - break; - } - failf(data, "error in swrite"); - *err = CURLE_WRITE_ERROR; - return -1; - } - if(n == 0) { - failf(data, "EOF in swrite"); - *err = CURLE_WRITE_ERROR; - return -1; - } - tlswritten += n; + if(tlswritten == 0) { + failf(data, "EOF in swrite"); + *err = CURLE_WRITE_ERROR; + return -1; } - - DEBUGASSERT(tlswritten <= tlslen); + infof(data, "cr_send wrote %ld bytes to network\n", tlswritten); + tlswritten_total += tlswritten; } return plainwritten; @@ -310,7 +289,7 @@ static CURLcode cr_init_backend(struct Curl_easy *data, struct connectdata *conn, struct ssl_backend_data *const backend) { - struct rustls_client_session *session = backend->session; + struct rustls_connection *rconn = backend->conn; struct rustls_client_config_builder *config_builder = NULL; const char *const ssl_cafile = SSL_CONN_CONFIG(CAfile); const bool verifypeer = SSL_CONN_CONFIG(verifypeer); @@ -318,19 +297,26 @@ cr_init_backend(struct Curl_easy *data, struct connectdata *conn, char errorbuf[256]; size_t errorlen; int result; - - backend->tlsbuf = calloc(TLSBUF_SIZE, 1); - if(backend->tlsbuf == NULL) { - return CURLE_OUT_OF_MEMORY; - } + rustls_slice_bytes alpn[2] = { + { (const uint8_t *)ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH }, + { (const uint8_t *)ALPN_H2, ALPN_H2_LENGTH }, + }; config_builder = rustls_client_config_builder_new(); +#ifdef USE_HTTP2 + infof(data, "offering ALPN for HTTP/1.1 and HTTP/2\n"); + rustls_client_config_builder_set_protocols(config_builder, alpn, 2); +#else + infof(data, "offering ALPN for HTTP/1.1 only\n"); + rustls_client_config_builder_set_protocols(config_builder, alpn, 1); +#endif if(!verifypeer) { rustls_client_config_builder_dangerous_set_certificate_verifier( - config_builder, cr_verify_none, NULL); + config_builder, cr_verify_none); /* rustls doesn't support IP addresses (as of 0.19.0), and will reject - * sessions created with an IP address, even when certificate verification - * is turned off. Set a placeholder hostname and disable SNI. */ + * connections created with an IP address, even when certificate + * verification is turned off. Set a placeholder hostname and disable + * SNI. */ if(cr_hostname_is_ip(hostname)) { rustls_client_config_builder_set_enable_sni(config_builder, false); hostname = "example.invalid"; @@ -357,18 +343,51 @@ cr_init_backend(struct Curl_easy *data, struct connectdata *conn, } backend->config = rustls_client_config_builder_build(config_builder); - DEBUGASSERT(session == NULL); - result = rustls_client_session_new( - backend->config, hostname, &session); + DEBUGASSERT(rconn == NULL); + result = rustls_client_connection_new(backend->config, hostname, &rconn); if(result != RUSTLS_RESULT_OK) { rustls_error(result, errorbuf, sizeof(errorbuf), &errorlen); - failf(data, "failed to create client session: %.*s", errorlen, errorbuf); + failf(data, "rustls_client_connection_new: %.*s", errorlen, errorbuf); return CURLE_COULDNT_CONNECT; } - backend->session = session; + rustls_connection_set_userdata(rconn, backend); + backend->conn = rconn; return CURLE_OK; } +static void +cr_set_negotiated_alpn(struct Curl_easy *data, struct connectdata *conn, + const struct rustls_connection *rconn) +{ + const uint8_t *protocol = NULL; + size_t len = 0; + + rustls_connection_get_alpn_protocol(rconn, &protocol, &len); + if(NULL == protocol) { + infof(data, "ALPN, server did not agree to a protocol\n"); + return; + } + +#ifdef USE_HTTP2 + if(len == ALPN_H2_LENGTH && 0 == memcmp(ALPN_H2, protocol, len)) { + infof(data, "ALPN, negotiated h2\n"); + conn->negnpn = CURL_HTTP_VERSION_2; + } + else +#endif + if(len == ALPN_HTTP_1_1_LENGTH && + 0 == memcmp(ALPN_HTTP_1_1, protocol, len)) { + infof(data, "ALPN, negotiated http/1.1\n"); + conn->negnpn = CURL_HTTP_VERSION_1_1; + } + else { + infof(data, "ALPN, negotiated an unrecognized protocol\n"); + } + + Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ? + BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); +} + static CURLcode cr_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn, int sockindex, bool *done) @@ -376,7 +395,7 @@ cr_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn, struct ssl_connect_data *const connssl = &conn->ssl[sockindex]; curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_backend_data *const backend = connssl->backend; - struct rustls_client_session *session = NULL; + struct rustls_connection *rconn = NULL; CURLcode tmperr = CURLE_OK; int result; int what; @@ -393,7 +412,7 @@ cr_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn, connssl->state = ssl_connection_negotiating; } - session = backend->session; + rconn = backend->conn; /* Read/write data until the handshake is done or the socket would block. */ for(;;) { @@ -404,18 +423,21 @@ cr_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn, * then becomes true when we first write data, then becomes false again * once the handshake is done. */ - if(!rustls_client_session_is_handshaking(session)) { + if(!rustls_connection_is_handshaking(rconn)) { infof(data, "Done handshaking\n"); /* Done with the handshake. Set up callbacks to send/receive data. */ connssl->state = ssl_connection_complete; + + cr_set_negotiated_alpn(data, conn, rconn); + conn->recv[sockindex] = cr_recv; conn->send[sockindex] = cr_send; *done = TRUE; return CURLE_OK; } - wants_read = rustls_client_session_wants_read(session); - wants_write = rustls_client_session_wants_write(session); + wants_read = rustls_connection_wants_read(rconn); + wants_write = rustls_connection_wants_write(rconn); DEBUGASSERT(wants_read || wants_write); writefd = wants_write?sockfd:CURL_SOCKET_BAD; readfd = wants_read?sockfd:CURL_SOCKET_BAD; @@ -439,7 +461,7 @@ cr_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn, /* socket is readable or writable */ if(wants_write) { - infof(data, "ClientSession wants us to write_tls.\n"); + infof(data, "rustls_connection wants us to write_tls.\n"); cr_send(data, sockindex, NULL, 0, &tmperr); if(tmperr == CURLE_AGAIN) { infof(data, "writing would block\n"); @@ -451,7 +473,7 @@ cr_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn, } if(wants_read) { - infof(data, "ClientSession wants us to read_tls.\n"); + infof(data, "rustls_connection wants us to read_tls.\n"); cr_recv(data, sockindex, NULL, 0, &tmperr); if(tmperr == CURLE_AGAIN) { @@ -482,13 +504,13 @@ cr_getsock(struct connectdata *conn, curl_socket_t *socks) struct ssl_connect_data *const connssl = &conn->ssl[FIRSTSOCKET]; curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; struct ssl_backend_data *const backend = connssl->backend; - struct rustls_client_session *session = backend->session; + struct rustls_connection *rconn = backend->conn; - if(rustls_client_session_wants_write(session)) { + if(rustls_connection_wants_write(rconn)) { socks[0] = sockfd; return GETSOCK_WRITESOCK(0); } - if(rustls_client_session_wants_read(session)) { + if(rustls_connection_wants_read(rconn)) { socks[0] = sockfd; return GETSOCK_READSOCK(0); } @@ -501,7 +523,7 @@ cr_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { struct ssl_backend_data *backend = connssl->backend; - return &backend->session; + return &backend->conn; } static void @@ -513,21 +535,20 @@ cr_close(struct Curl_easy *data, struct connectdata *conn, CURLcode tmperr = CURLE_OK; ssize_t n = 0; - if(backend->session) { - rustls_client_session_send_close_notify(backend->session); + if(backend->conn) { + rustls_connection_send_close_notify(backend->conn); n = cr_send(data, sockindex, NULL, 0, &tmperr); if(n < 0) { failf(data, "error sending close notify: %d", tmperr); } - rustls_client_session_free(backend->session); - backend->session = NULL; + rustls_connection_free(backend->conn); + backend->conn = NULL; } if(backend->config) { rustls_client_config_free(backend->config); backend->config = NULL; } - free(backend->tlsbuf); } const struct Curl_ssl Curl_ssl_rustls = { @@ -554,7 +575,9 @@ const struct Curl_ssl Curl_ssl_rustls = { Curl_none_set_engine_default, /* set_engine_default */ Curl_none_engines_list, /* engines_list */ Curl_none_false_start, /* false_start */ - NULL /* sha256sum */ + NULL, /* sha256sum */ + NULL, /* associate_connection */ + NULL /* disassociate_connection */ }; #endif /* USE_RUSTLS */ |