diff --git a/include/picotls.h b/include/picotls.h index e6864ac10..921a72324 100644 --- a/include/picotls.h +++ b/include/picotls.h @@ -22,6 +22,10 @@ #ifndef picotls_h #define picotls_h +#ifdef __cplusplus +extern "C" { +#endif + #include <assert.h> #include <inttypes.h> #include <sys/types.h> @@ -766,7 +770,7 @@ inline ptls_iovec_t ptls_iovec_init(const void *p, size_t len) inline void ptls_buffer_init(ptls_buffer_t *buf, void *smallbuf, size_t smallbuf_size) { assert(smallbuf != NULL); - buf->base = smallbuf; + buf->base = (uint8_t *)smallbuf; buf->off = 0; buf->capacity = smallbuf_size; buf->is_allocated = 0; @@ -807,4 +811,8 @@ inline size_t ptls_aead_decrypt(ptls_aead_context_t *ctx, void *output, const vo int ptls_load_certificates(ptls_context_t *ctx, char *cert_pem_file); +#ifdef __cplusplus +} +#endif + #endif diff --git a/lib/picotls.c b/lib/picotls.c index c6eb81517..1facda18c 100644 --- a/lib/picotls.c +++ b/lib/picotls.c @@ -231,6 +231,7 @@ struct st_ptls_client_hello_t { ptls_iovec_t tbs; ptls_iovec_t ch1_hash; ptls_iovec_t signature; + unsigned sent_key_share : 1; } cookie; struct { const uint8_t *hash_end; @@ -468,40 +469,74 @@ static int aead_decrypt(struct st_ptls_traffic_protection_t *ctx, void *output, return 0; } +#define buffer_push_record(buf, type, block) \ + do { \ + ptls_buffer_push((buf), (type), PTLS_RECORD_VERSION_MAJOR, PTLS_RECORD_VERSION_MINOR); \ + ptls_buffer_push_block((buf), 2, block); \ + } while (0) + +static int buffer_push_encrypted_records(ptls_buffer_t *buf, uint8_t type, const uint8_t *src, size_t len, + struct st_ptls_traffic_protection_t *enc) +{ + int ret = 0; + + while (len != 0) { + size_t chunk_size = len; + if (chunk_size > PTLS_MAX_PLAINTEXT_RECORD_SIZE) + chunk_size = PTLS_MAX_PLAINTEXT_RECORD_SIZE; + buffer_push_record(buf, PTLS_CONTENT_TYPE_APPDATA, { + if ((ret = ptls_buffer_reserve(buf, chunk_size + enc->aead->algo->tag_size + 1)) != 0) + goto Exit; + buf->off += aead_encrypt(enc, buf->base + buf->off, src, chunk_size, type); + }); + src += chunk_size; + len -= chunk_size; + } + +Exit: + return ret; +} + static int buffer_encrypt_record(ptls_buffer_t *buf, size_t rec_start, struct st_ptls_traffic_protection_t *enc) { - uint8_t encrypted[PTLS_MAX_ENCRYPTED_RECORD_SIZE]; - size_t enclen, bodylen = buf->off - rec_start - 5; + size_t bodylen = buf->off - rec_start - 5; + uint8_t *tmpbuf, type = buf->base[rec_start]; int ret; - assert(bodylen <= PTLS_MAX_PLAINTEXT_RECORD_SIZE); + /* fast path: do in-place encryption if only one record needs to be emitted */ + if (bodylen <= PTLS_MAX_PLAINTEXT_RECORD_SIZE) { + size_t overhead = 1 + enc->aead->algo->tag_size; + if ((ret = ptls_buffer_reserve(buf, overhead)) != 0) + return ret; + size_t encrypted_len = aead_encrypt(enc, buf->base + rec_start + 5, buf->base + rec_start + 5, bodylen, type); + assert(encrypted_len == bodylen + overhead); + buf->off += overhead; + buf->base[rec_start] = PTLS_CONTENT_TYPE_APPDATA; + buf->base[rec_start + 3] = (encrypted_len >> 8) & 0xff; + buf->base[rec_start + 4] = encrypted_len & 0xff; + return 0; + } - enclen = aead_encrypt(enc, encrypted, buf->base + rec_start + 5, bodylen, buf->base[rec_start]); + /* move plaintext to temporary buffer */ + if ((tmpbuf = malloc(bodylen)) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + memcpy(tmpbuf, buf->base + rec_start + 5, bodylen); + ptls_clear_memory(buf->base + rec_start, bodylen + 5); buf->off = rec_start; - ptls_buffer_push(buf, PTLS_CONTENT_TYPE_APPDATA, 3, 1); - ptls_buffer_push16(buf, (uint16_t)enclen); - ptls_buffer_pushv(buf, encrypted, enclen); + + /* push encrypted records */ + ret = buffer_push_encrypted_records(buf, type, tmpbuf, bodylen, enc); Exit: + if (tmpbuf != NULL) { + ptls_clear_memory(tmpbuf, bodylen); + free(tmpbuf); + } return ret; } -#define buffer_push_record(buf, type, block) \ - do { \ - ptls_buffer_push((buf), (type), PTLS_RECORD_VERSION_MAJOR, PTLS_RECORD_VERSION_MINOR); \ - ptls_buffer_push_block((buf), 2, block); \ - } while (0) - -#define buffer_encrypt(buf, enc, block) \ - do { \ - size_t rec_start = (buf)->off; \ - do { \ - block \ - } while (0); \ - if ((ret = buffer_encrypt_record((buf), rec_start, (enc))) != 0) \ - goto Exit; \ - } while (0); - #define buffer_push_handshake_core(buf, key_sched, type, mess_start, block) \ do { \ ptls_buffer_push((buf), (type)); \ @@ -511,14 +546,19 @@ static int buffer_encrypt_record(ptls_buffer_t *buf, size_t rec_start, struct st } while (0); \ }); \ if ((key_sched) != NULL) \ - key_schedule_update_hash((key_sched), (buf)->base + mess_start, (buf)->off - (mess_start)); \ + key_schedule_update_hash((key_sched), (buf)->base + (mess_start), (buf)->off - (mess_start)); \ } while (0) -#define buffer_push_handshake(buf, key_sched, type, block) \ - buffer_push_record((buf), PTLS_CONTENT_TYPE_HANDSHAKE, { \ - size_t mess_start = (buf)->off; \ - buffer_push_handshake_core((buf), (key_sched), (type), mess_start, block); \ - }) +#define buffer_push_handshake(buf, key_sched, enc, type, block) \ + do { \ + size_t rec_start = (buf)->off; \ + buffer_push_record((buf), PTLS_CONTENT_TYPE_HANDSHAKE, \ + { buffer_push_handshake_core((buf), (key_sched), (type), rec_start + 5, block); }); \ + if ((enc) != NULL) { \ + if ((ret = buffer_encrypt_record((buf), rec_start, (enc))) != 0) \ + goto Exit; \ + } \ + } while (0) #define buffer_calc_handshake_hash(buf, key_sched, type, block) \ do { \ @@ -1004,14 +1044,12 @@ static int send_finished(ptls_t *tls, ptls_buffer_t *sendbuf) { int ret; - buffer_encrypt(sendbuf, &tls->traffic_protection.enc, { - buffer_push_handshake(sendbuf, tls->key_schedule, PTLS_HANDSHAKE_TYPE_FINISHED, { - if ((ret = ptls_buffer_reserve(sendbuf, tls->key_schedule->algo->digest_size)) != 0) - goto Exit; - if ((ret = calc_verify_data(sendbuf->base + sendbuf->off, tls->key_schedule, tls->traffic_protection.enc.secret)) != 0) - goto Exit; - sendbuf->off += tls->key_schedule->algo->digest_size; - }); + buffer_push_handshake(sendbuf, tls->key_schedule, &tls->traffic_protection.enc, PTLS_HANDSHAKE_TYPE_FINISHED, { + if ((ret = ptls_buffer_reserve(sendbuf, tls->key_schedule->algo->digest_size)) != 0) + goto Exit; + if ((ret = calc_verify_data(sendbuf->base + sendbuf->off, tls->key_schedule, tls->traffic_protection.enc.secret)) != 0) + goto Exit; + sendbuf->off += tls->key_schedule->algo->digest_size; }); Exit: @@ -1050,21 +1088,19 @@ static int send_session_ticket(ptls_t *tls, ptls_buffer_t *sendbuf) goto Exit; /* encrypt and send */ - buffer_encrypt(sendbuf, &tls->traffic_protection.enc, { - buffer_push_handshake(sendbuf, tls->key_schedule, PTLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET, { - ptls_buffer_push32(sendbuf, tls->ctx->ticket_lifetime); - ptls_buffer_push32(sendbuf, ticket_age_add); - ptls_buffer_push_block(sendbuf, 1, {}); - ptls_buffer_push_block(sendbuf, 2, { - if ((ret = tls->ctx->encrypt_ticket->cb(tls->ctx->encrypt_ticket, tls, 1, sendbuf, - ptls_iovec_init(session_id.base, session_id.off))) != 0) - goto Exit; - }); - ptls_buffer_push_block(sendbuf, 2, { - if (tls->ctx->max_early_data_size != 0) - buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_EARLY_DATA, - { ptls_buffer_push32(sendbuf, tls->ctx->max_early_data_size); }); - }); + buffer_push_handshake(sendbuf, tls->key_schedule, &tls->traffic_protection.enc, PTLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET, { + ptls_buffer_push32(sendbuf, tls->ctx->ticket_lifetime); + ptls_buffer_push32(sendbuf, ticket_age_add); + ptls_buffer_push_block(sendbuf, 1, {}); + ptls_buffer_push_block(sendbuf, 2, { + if ((ret = tls->ctx->encrypt_ticket->cb(tls->ctx->encrypt_ticket, tls, 1, sendbuf, + ptls_iovec_init(session_id.base, session_id.off))) != 0) + goto Exit; + }); + ptls_buffer_push_block(sendbuf, 2, { + if (tls->ctx->max_early_data_size != 0) + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_EARLY_DATA, + { ptls_buffer_push32(sendbuf, tls->ctx->max_early_data_size); }); }); }); @@ -1138,7 +1174,7 @@ static int send_client_hello(ptls_t *tls, ptls_buffer_t *sendbuf, ptls_handshake } msghash_off = sendbuf->off + 5; - buffer_push_handshake(sendbuf, NULL, PTLS_HANDSHAKE_TYPE_CLIENT_HELLO, { + buffer_push_handshake(sendbuf, NULL, NULL, PTLS_HANDSHAKE_TYPE_CLIENT_HELLO, { /* legacy_version */ ptls_buffer_push16(sendbuf, 0x0303); /* random_bytes */ @@ -1693,8 +1729,8 @@ static int client_handle_finished(ptls_t *tls, ptls_buffer_t *sendbuf, ptls_iove if (tls->early_data != NULL) { assert(tls->traffic_protection.enc.aead != NULL); if (!tls->skip_early_data) { - buffer_encrypt(sendbuf, &tls->traffic_protection.enc, - { buffer_push_handshake(sendbuf, tls->key_schedule, PTLS_HANDSHAKE_TYPE_END_OF_EARLY_DATA, {}); }); + buffer_push_handshake(sendbuf, tls->key_schedule, &tls->traffic_protection.enc, PTLS_HANDSHAKE_TYPE_END_OF_EARLY_DATA, + {}); } if ((ret = retire_early_data_secret(tls, 1)) != 0) goto Exit; @@ -1974,10 +2010,25 @@ static int decode_client_hello(ptls_t *tls, struct st_ptls_client_hello_t *ch, c ptls_decode_block(src, end, 2, { ch->cookie.tbs.base = (void *)src; ptls_decode_open_block(src, end, 2, { - ptls_decode_block(src, end, 1, { + ptls_decode_open_block(src, end, 1, { ch->cookie.ch1_hash = ptls_iovec_init(src, end - src); src = end; }); + if (src == end) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + switch (*src++) { + case 0: + assert(!ch->cookie.sent_key_share); + break; + case 1: + ch->cookie.sent_key_share = 1; + break; + default: + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } }); ch->cookie.tbs.len = src - ch->cookie.tbs.base; ptls_decode_block(src, end, 1, { @@ -2219,17 +2270,22 @@ static int calc_cookie_signature(ptls_t *tls, ptls_handshake_properties_t *prope static int server_handle_hello(ptls_t *tls, ptls_buffer_t *sendbuf, ptls_iovec_t message, ptls_handshake_properties_t *properties) { #define EMIT_HRR(sched, negotiated_group, additional_extensions) \ - buffer_push_handshake(sendbuf, sched, PTLS_HANDSHAKE_TYPE_HELLO_RETRY_REQUEST, { \ - ptls_buffer_push16(sendbuf, PTLS_PROTOCOL_VERSION_DRAFT21); \ - ptls_buffer_push16(sendbuf, tls->cipher_suite->id); \ - ptls_buffer_push_block(sendbuf, 2, { \ - buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_KEY_SHARE, \ - { ptls_buffer_push16(sendbuf, (negotiated_group)->id); }); \ - { \ - additional_extensions \ - } \ + do { \ + ptls_key_exchange_algorithm_t *_negotiated_group = (negotiated_group); \ + buffer_push_handshake(sendbuf, (sched), NULL, PTLS_HANDSHAKE_TYPE_HELLO_RETRY_REQUEST, { \ + ptls_buffer_push16(sendbuf, PTLS_PROTOCOL_VERSION_DRAFT21); \ + ptls_buffer_push16(sendbuf, tls->cipher_suite->id); \ + ptls_buffer_push_block(sendbuf, 2, { \ + if (_negotiated_group != NULL) { \ + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_KEY_SHARE, \ + { ptls_buffer_push16(sendbuf, _negotiated_group->id); }); \ + } \ + { \ + additional_extensions \ + } \ + }); \ }); \ - }) + } while (0) struct st_ptls_client_hello_t ch = {NULL, {NULL}, 0, {NULL}, {NULL}, {NULL}, {{0}}, {NULL}, {{{NULL}}}, {{NULL}}, {NULL}, {{UINT16_MAX}}}; @@ -2318,7 +2374,7 @@ static int server_handle_hello(ptls_t *tls, ptls_buffer_t *sendbuf, ptls_iovec_t key_schedule_extract(tls->key_schedule, ptls_iovec_init(NULL, 0)); /* ... reusing sendbuf to rebuild HRR for hash calculation */ size_t hrr_start = sendbuf->off; - EMIT_HRR(tls->key_schedule, key_share.algorithm, { + EMIT_HRR(tls->key_schedule, ch.cookie.sent_key_share ? key_share.algorithm : NULL, { buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_COOKIE, { ptls_buffer_pushv(sendbuf, ch.cookie.all.base, ch.cookie.all.len); }); }); @@ -2340,7 +2396,7 @@ static int server_handle_hello(ptls_t *tls, ptls_buffer_t *sendbuf, ptls_iovec_t key_schedule_extract(tls->key_schedule, ptls_iovec_init(NULL, 0)); } /* emit HelloRetryRequest */ - EMIT_HRR(enforce_cookie_use ? NULL : tls->key_schedule, negotiated_group, { + EMIT_HRR(enforce_cookie_use ? NULL : tls->key_schedule, key_share.algorithm != NULL ? NULL : negotiated_group, { if (enforce_cookie_use) { buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_COOKIE, { ptls_buffer_push_block(sendbuf, 2, { @@ -2355,6 +2411,8 @@ static int server_handle_hello(ptls_t *tls, ptls_buffer_t *sendbuf, ptls_iovec_t key_schedule_extract_ch1hash(tls->key_schedule, sendbuf->base + sendbuf->off); sendbuf->off += sz; }); + /* second is if we have sent key_share extension */ + ptls_buffer_push(sendbuf, key_share.algorithm == NULL); /* we can add more data here */ }); size_t tbs_len = sendbuf->off - tbs_start; @@ -2449,7 +2507,7 @@ static int server_handle_hello(ptls_t *tls, ptls_buffer_t *sendbuf, ptls_iovec_t } /* send ServerHello */ - buffer_push_handshake(sendbuf, tls->key_schedule, PTLS_HANDSHAKE_TYPE_SERVER_HELLO, { + buffer_push_handshake(sendbuf, tls->key_schedule, NULL, PTLS_HANDSHAKE_TYPE_SERVER_HELLO, { ptls_buffer_push16(sendbuf, PTLS_PROTOCOL_VERSION_DRAFT21); if ((ret = ptls_buffer_reserve(sendbuf, PTLS_HELLO_RANDOM_SIZE)) != 0) goto Exit; @@ -2486,78 +2544,72 @@ static int server_handle_hello(ptls_t *tls, ptls_buffer_t *sendbuf, ptls_iovec_t } /* send EncryptedExtensions */ - buffer_encrypt(sendbuf, &tls->traffic_protection.enc, { - buffer_push_handshake(sendbuf, tls->key_schedule, PTLS_HANDSHAKE_TYPE_ENCRYPTED_EXTENSIONS, { - ptls_buffer_push_block(sendbuf, 2, { - if (tls->server_name != NULL) { - /* In this event, the server SHALL include an extension of type "server_name" in the (extended) server - * hello. The "extension_data" field of this extension SHALL be empty. (RFC 6066 section 3) */ - buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SERVER_NAME, {}); - } - if (tls->negotiated_protocol != NULL) { - buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ALPN, { - ptls_buffer_push_block(sendbuf, 2, { - ptls_buffer_push_block(sendbuf, 1, { - ptls_buffer_pushv(sendbuf, tls->negotiated_protocol, strlen(tls->negotiated_protocol)); - }); + buffer_push_handshake(sendbuf, tls->key_schedule, &tls->traffic_protection.enc, PTLS_HANDSHAKE_TYPE_ENCRYPTED_EXTENSIONS, { + ptls_buffer_push_block(sendbuf, 2, { + if (tls->server_name != NULL) { + /* In this event, the server SHALL include an extension of type "server_name" in the (extended) server + * hello. The "extension_data" field of this extension SHALL be empty. (RFC 6066 section 3) */ + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SERVER_NAME, {}); + } + if (tls->negotiated_protocol != NULL) { + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ALPN, { + ptls_buffer_push_block(sendbuf, 2, { + ptls_buffer_push_block(sendbuf, 1, { + ptls_buffer_pushv(sendbuf, tls->negotiated_protocol, strlen(tls->negotiated_protocol)); }); }); - } - if (tls->early_data != NULL && tls->traffic_protection.dec.aead != NULL) - buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_EARLY_DATA, {}); - if ((ret = push_additional_extensions(properties, sendbuf)) != 0) - goto Exit; - }); + }); + } + if (tls->early_data != NULL && tls->traffic_protection.dec.aead != NULL) + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_EARLY_DATA, {}); + if ((ret = push_additional_extensions(properties, sendbuf)) != 0) + goto Exit; }); }); if (mode == HANDSHAKE_MODE_FULL) { /* send Certificate */ - buffer_encrypt(sendbuf, &tls->traffic_protection.enc, { - buffer_push_handshake(sendbuf, tls->key_schedule, PTLS_HANDSHAKE_TYPE_CERTIFICATE, { - ptls_buffer_push(sendbuf, 0); - ptls_buffer_push_block(sendbuf, 3, { - size_t i; - for (i = 0; i != tls->ctx->certificates.count; ++i) { - ptls_buffer_push_block(sendbuf, 3, { - ptls_buffer_pushv(sendbuf, tls->ctx->certificates.list[i].base, tls->ctx->certificates.list[i].len); - }); - ptls_buffer_push_block(sendbuf, 2, { - /* emit OCSP stapling only when requested and when the callback successfully returns one */ - if (ch.status_request && i == 0 && tls->ctx->staple_ocsp != NULL) { - size_t reset_off_to = sendbuf->off; - buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_STATUS_REQUEST, { - ptls_buffer_push(sendbuf, 1); /* status_type == ocsp */ - ptls_buffer_push_block(sendbuf, 3, { - if ((ret = tls->ctx->staple_ocsp->cb(tls->ctx->staple_ocsp, tls, sendbuf, i)) == 0) - reset_off_to = 0; - }); + buffer_push_handshake(sendbuf, tls->key_schedule, &tls->traffic_protection.enc, PTLS_HANDSHAKE_TYPE_CERTIFICATE, { + ptls_buffer_push(sendbuf, 0); + ptls_buffer_push_block(sendbuf, 3, { + size_t i; + for (i = 0; i != tls->ctx->certificates.count; ++i) { + ptls_buffer_push_block(sendbuf, 3, { + ptls_buffer_pushv(sendbuf, tls->ctx->certificates.list[i].base, tls->ctx->certificates.list[i].len); + }); + ptls_buffer_push_block(sendbuf, 2, { + /* emit OCSP stapling only when requested and when the callback successfully returns one */ + if (ch.status_request && i == 0 && tls->ctx->staple_ocsp != NULL) { + size_t reset_off_to = sendbuf->off; + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_STATUS_REQUEST, { + ptls_buffer_push(sendbuf, 1); /* status_type == ocsp */ + ptls_buffer_push_block(sendbuf, 3, { + if ((ret = tls->ctx->staple_ocsp->cb(tls->ctx->staple_ocsp, tls, sendbuf, i)) == 0) + reset_off_to = 0; }); - if (reset_off_to != 0) - sendbuf->off = reset_off_to; - } - }); - } - }); + }); + if (reset_off_to != 0) + sendbuf->off = reset_off_to; + } + }); + } }); }); /* build and send CertificateVerify */ - buffer_encrypt(sendbuf, &tls->traffic_protection.enc, { - buffer_push_handshake(sendbuf, tls->key_schedule, PTLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY, { - size_t algo_off = sendbuf->off; - ptls_buffer_push16(sendbuf, 0); /* filled in later */ - ptls_buffer_push_block(sendbuf, 2, { - uint16_t algo; - uint8_t data[PTLS_MAX_CERTIFICATE_VERIFY_SIGNDATA_SIZE]; - size_t datalen = - build_certificate_verify_signdata(data, tls->key_schedule, PTLS_SERVER_CERTIFICATE_VERIFY_CONTEXT_STRING); - if ((ret = tls->ctx->sign_certificate->cb(tls->ctx->sign_certificate, tls, &algo, sendbuf, - ptls_iovec_init(data, datalen), ch.signature_algorithms.list, - ch.signature_algorithms.count)) != 0) - goto Exit; - sendbuf->base[algo_off] = (uint8_t)(algo >> 8); - sendbuf->base[algo_off + 1] = (uint8_t)algo; - }); + buffer_push_handshake(sendbuf, tls->key_schedule, &tls->traffic_protection.enc, PTLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY, { + size_t algo_off = sendbuf->off; + ptls_buffer_push16(sendbuf, 0); /* filled in later */ + ptls_buffer_push_block(sendbuf, 2, { + uint16_t algo; + uint8_t data[PTLS_MAX_CERTIFICATE_VERIFY_SIGNDATA_SIZE]; + size_t datalen = + build_certificate_verify_signdata(data, tls->key_schedule, PTLS_SERVER_CERTIFICATE_VERIFY_CONTEXT_STRING); + if ((ret = tls->ctx->sign_certificate->cb(tls->ctx->sign_certificate, tls, &algo, sendbuf, + ptls_iovec_init(data, datalen), ch.signature_algorithms.list, + ch.signature_algorithms.count)) != 0) + goto Exit; + sendbuf->base[algo_off] = (uint8_t)(algo >> 8); + sendbuf->base[algo_off + 1] = (uint8_t)algo; }); }); } @@ -2991,6 +3043,7 @@ static int handle_handshake_record(ptls_t *tls, int (*cb)(ptls_t *tls, ptls_buff memmove(tls->recvbuf.mess.base, src, src_end - src); } tls->recvbuf.mess.off = src_end - src; + ret = PTLS_ERROR_IN_PROGRESS; } else { ptls_buffer_dispose(&tls->recvbuf.mess); } @@ -3161,28 +3214,10 @@ int ptls_receive(ptls_t *tls, ptls_buffer_t *decryptbuf, const void *_input, siz return ret; } -int ptls_send(ptls_t *tls, ptls_buffer_t *sendbuf, const void *_input, size_t inlen) +int ptls_send(ptls_t *tls, ptls_buffer_t *sendbuf, const void *input, size_t inlen) { - const uint8_t *input = (const uint8_t *)_input; - size_t pt_size; - int ret = 0; - assert(tls->traffic_protection.enc.aead != NULL); - - for (; inlen != 0; input += pt_size, inlen -= pt_size) { - pt_size = inlen; - if (pt_size > PTLS_MAX_PLAINTEXT_RECORD_SIZE) - pt_size = PTLS_MAX_PLAINTEXT_RECORD_SIZE; - buffer_push_record(sendbuf, PTLS_CONTENT_TYPE_APPDATA, { - if ((ret = ptls_buffer_reserve(sendbuf, pt_size + tls->traffic_protection.enc.aead->algo->tag_size + 1)) != 0) - goto Exit; - sendbuf->off += - aead_encrypt(&tls->traffic_protection.enc, sendbuf->base + sendbuf->off, input, pt_size, PTLS_CONTENT_TYPE_APPDATA); - }); - } - -Exit: - return ret; + return buffer_push_encrypted_records(sendbuf, PTLS_CONTENT_TYPE_APPDATA, input, inlen, &tls->traffic_protection.enc); } size_t ptls_get_record_overhead(ptls_t *tls) diff --git a/t/cli.c b/t/cli.c index 06a586873..775fd8efc 100644 --- a/t/cli.c +++ b/t/cli.c @@ -226,7 +226,7 @@ static int run_server(struct sockaddr *sa, socklen_t salen, ptls_context_t *ctx, { int listen_fd, conn_fd, on = 1; - if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + if ((listen_fd = socket(sa->sa_family, SOCK_STREAM, 0)) == -1) { perror("socket(2) failed"); return 1; } @@ -275,6 +275,8 @@ static void usage(const char *cmd) printf("Usage: %s [options] host port\n" "\n" "Options:\n" + " -4 force IPv4\n" + " -6 force IPv6\n" " -c certificate-file\n" " -k key-file specifies the credentials to be used for running the\n" " server. If omitted, the command runs as a client.\n" @@ -305,9 +307,16 @@ int main(int argc, char **argv) int use_early_data = 0, ch; struct sockaddr_storage sa; socklen_t salen; + int family = 0; - while ((ch = getopt(argc, argv, "c:k:es:l:vh")) != -1) { + while ((ch = getopt(argc, argv, "46c:k:es:l:vh")) != -1) { switch (ch) { + case '4': + family = AF_INET; + break; + case '6': + family = AF_INET6; + break; case 'c': load_certificate_chain(&ctx, optarg); break; @@ -354,7 +363,7 @@ int main(int argc, char **argv) host = (--argc, *argv++); port = (--argc, *argv++); - if (resolve_address((struct sockaddr *)&sa, &salen, host, port, SOCK_STREAM, IPPROTO_TCP) != 0) + if (resolve_address((struct sockaddr *)&sa, &salen, host, port, family, SOCK_STREAM, IPPROTO_TCP) != 0) exit(1); if (ctx.certificates.count != 0) { diff --git a/t/picotls.c b/t/picotls.c index 1e49408aa..31bb6d1ed 100644 --- a/t/picotls.c +++ b/t/picotls.c @@ -221,14 +221,14 @@ static void test_fragmented_message(void) SET_RECORD("\x01\x00\x00\x03" "a"); ret = handle_handshake_record(&tls, test_fragmented_message_record, NULL, &rec, NULL); - ok(ret == 0); + ok(ret == PTLS_ERROR_IN_PROGRESS); ok(tls.recvbuf.mess.base != NULL); ok(test_fragmented_message_queue.count == 0); SET_RECORD("bc\x02\x00\x00\x02" "de" "\x03"); ret = handle_handshake_record(&tls, test_fragmented_message_record, NULL, &rec, NULL); - ok(ret == 0); + ok(ret == PTLS_ERROR_IN_PROGRESS); ok(test_fragmented_message_queue.count == 2); ok(test_fragmented_message_queue.vec[0].len == 7); ok(memcmp(test_fragmented_message_queue.vec[0].buf, "\x01\x00\x00\x03" diff --git a/t/util.h b/t/util.h index eff0e4f8b..47010fa5d 100644 --- a/t/util.h +++ b/t/util.h @@ -231,12 +231,13 @@ static inline void setup_session_cache(ptls_context_t *ctx) ctx->encrypt_ticket = &sc.super; } -static inline int resolve_address(struct sockaddr *sa, socklen_t *salen, const char *host, const char *port, int type, int proto) +static inline int resolve_address(struct sockaddr *sa, socklen_t *salen, const char *host, const char *port, int family, int type, int proto) { struct addrinfo hints, *res; int err; memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; hints.ai_socktype = type; hints.ai_protocol = proto; hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV | AI_PASSIVE;