lsquic_chsk_stream.c revision 5392f7a3
1/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */ 2/* 3 * Stream/crypto handshake adapter for the client side. 4 * 5 * The client composes CHLO, writes it to the stream, and wait for the 6 * server response, which it processes. 7 */ 8 9#include <assert.h> 10#include <errno.h> 11#include <stdarg.h> 12#include <stdlib.h> 13#include <string.h> 14#include <sys/queue.h> 15 16#include "lsquic_int_types.h" 17#include "lsquic.h" 18 19#include "lsquic_str.h" 20#include "lsquic_enc_sess.h" 21#include "lsquic_chsk_stream.h" 22#include "lsquic_ver_neg.h" 23#include "lsquic_hash.h" 24#include "lsquic_conn.h" 25#include "lsquic_mm.h" 26#include "lsquic_sizes.h" 27#include "lsquic_full_conn.h" 28 29#define LSQUIC_LOGGER_MODULE LSQLM_HSK_ADAPTER 30#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(c_hsk->lconn) 31#include "lsquic_logger.h" 32 33 34static lsquic_stream_ctx_t * 35hsk_client_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream) 36{ 37 struct client_hsk_ctx *const c_hsk = stream_if_ctx; 38 39 LSQ_DEBUG("stream created"); 40 41#if LSQUIC_ENABLE_HANDSHAKE_DISABLE 42 if (getenv("LSQUIC_DISABLE_HANDSHAKE")) 43 { 44 LSQ_WARN("Handshake disabled: faking it"); 45 c_hsk->lconn->cn_flags |= LSCONN_NO_CRYPTO; 46 c_hsk->lconn->cn_if->ci_handshake_ok(c_hsk->lconn); 47 return (void *) c_hsk; 48 } 49#endif 50 51 lsquic_stream_wantwrite(stream, 1); 52 53 return (void *) c_hsk; 54} 55 56 57static void 58hsk_client_on_read (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh) 59{ 60 struct client_hsk_ctx *const c_hsk = (struct client_hsk_ctx *) sh; 61 ssize_t nread; 62 int s; 63 enum lsquic_hsk_status status; 64 65 if (!c_hsk->buf_in) 66 { 67 c_hsk->buf_in = lsquic_mm_get_16k(c_hsk->mm); 68 if (!c_hsk->buf_in) 69 { 70 LSQ_WARN("could not get buffer: %s", strerror(errno)); 71 lsquic_stream_wantread(stream, 0); 72 lsquic_conn_close(c_hsk->lconn); 73 return; 74 } 75 c_hsk->buf_sz = 16 * 1024; 76 c_hsk->buf_off = 0; 77 } 78 79 nread = lsquic_stream_read(stream, c_hsk->buf_in + c_hsk->buf_off, 80 c_hsk->buf_sz - c_hsk->buf_off); 81 if (nread <= 0) 82 { 83 if (nread < 0) 84 LSQ_INFO("Could not read from handshake stream: %s", 85 strerror(errno)); 86 else 87 LSQ_INFO("Handshake stream closed (odd)"); 88 lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in); 89 c_hsk->buf_in = NULL; 90 lsquic_stream_wantread(stream, 0); 91 lsquic_conn_close(c_hsk->lconn); 92 return; 93 } 94 c_hsk->buf_off += nread; 95 96 s = c_hsk->lconn->cn_esf.g->esf_handle_chlo_reply(c_hsk->lconn->cn_enc_session, 97 c_hsk->buf_in, c_hsk->buf_off); 98 LSQ_DEBUG("lsquic_enc_session_handle_chlo_reply returned %d", s); 99 switch (s) 100 { 101 case DATA_NOT_ENOUGH: 102 if (c_hsk->buf_off < c_hsk->buf_sz) 103 LSQ_INFO("not enough server response has arrived, continue " 104 "buffering"); 105 else 106 { 107 LSQ_INFO("read in %u bytes of server response, and it is still " 108 "not enough: giving up", c_hsk->buf_off); 109 lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in); 110 c_hsk->buf_in = NULL; 111 lsquic_stream_wantread(stream, 0); 112 c_hsk->lconn->cn_if->ci_hsk_done(c_hsk->lconn, LSQ_HSK_FAIL); 113 lsquic_conn_close(c_hsk->lconn); 114 } 115 break; 116 case DATA_NO_ERROR: 117 lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in); 118 c_hsk->buf_in = NULL; 119 lsquic_stream_wantread(stream, 0); 120 if (c_hsk->lconn->cn_esf.g->esf_is_hsk_done(c_hsk->lconn->cn_enc_session)) 121 { 122 LSQ_DEBUG("handshake is successful, inform connection"); 123 status = (c_hsk->lconn->cn_esf_c->esf_did_zero_rtt_succeed( 124 c_hsk->lconn->cn_enc_session)) ? LSQ_HSK_0RTT_OK : LSQ_HSK_OK; 125 c_hsk->lconn->cn_if->ci_hsk_done(c_hsk->lconn, status); 126 } 127 else 128 { 129 LSQ_DEBUG("handshake not yet complete, will generate another " 130 "message"); 131 lsquic_stream_wantwrite(stream, 1); 132 } 133 break; 134 case HS_SREJ: 135 LSQ_DEBUG("got HS_SREJ"); 136 c_hsk->buf_off = 0; 137 lsquic_stream_wantread(stream, 0); 138 if (0 == lsquic_gquic_full_conn_srej(c_hsk->lconn)) 139 lsquic_stream_wantwrite(stream, 1); 140 break; 141 default: 142 LSQ_WARN("lsquic_enc_session_handle_chlo_reply returned unknown value %d", s); 143 /* fallthru */ 144 case DATA_FORMAT_ERROR: 145 LSQ_INFO("lsquic_enc_session_handle_chlo_reply returned an error"); 146 c_hsk->buf_in = NULL; 147 lsquic_stream_wantread(stream, 0); 148 c_hsk->lconn->cn_if->ci_hsk_done(c_hsk->lconn, LSQ_HSK_FAIL); 149 lsquic_conn_close(c_hsk->lconn); 150 break; 151 } 152} 153 154 155/* In this function, we assume that we can write the whole message in one 156 * shot. Otherwise, this is an error. 157 */ 158static void 159hsk_client_on_write (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh) 160{ 161 struct client_hsk_ctx *const c_hsk = (struct client_hsk_ctx *) sh; 162 unsigned char *buf; 163 size_t len; 164 ssize_t nw; 165 166 lsquic_stream_wantwrite(stream, 0); 167 168 buf = lsquic_mm_get_4k(c_hsk->mm); 169 if (!buf) 170 { 171 LSQ_WARN("cannot allocate buffer: %s", strerror(errno)); 172 lsquic_conn_close(c_hsk->lconn); 173 return; 174 } 175 len = 4 * 1024; 176 177 if (0 != c_hsk->lconn->cn_esf.g->esf_gen_chlo(c_hsk->lconn->cn_enc_session, 178 c_hsk->ver_neg->vn_ver, buf, &len)) 179 { 180 LSQ_WARN("cannot create CHLO message"); 181 lsquic_mm_put_4k(c_hsk->mm, buf); 182 lsquic_conn_close(c_hsk->lconn); 183 return; 184 } 185 186 nw = lsquic_stream_write(stream, buf, len); 187 lsquic_mm_put_4k(c_hsk->mm, buf); 188 189 if (nw < 0) 190 LSQ_INFO("error writing to stream: %s", strerror(errno)); 191 else if ((size_t) nw == len) 192 { 193 LSQ_INFO("wrote %zd bytes of CHLO to stream", nw); 194 lsquic_stream_flush(stream); 195 lsquic_stream_wantread(stream, 1); 196 } 197 else 198 LSQ_INFO("could only write %zd bytes to stream instead of %zd", 199 nw, len); 200} 201 202 203static void 204hsk_client_on_close (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh) 205{ 206 struct client_hsk_ctx *const c_hsk = (struct client_hsk_ctx *) sh; 207 if (c_hsk->buf_in) 208 lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in); 209 LSQ_DEBUG("stream closed"); 210} 211 212 213const struct lsquic_stream_if lsquic_client_hsk_stream_if = 214{ 215 .on_new_stream = hsk_client_on_new_stream, 216 .on_read = hsk_client_on_read, 217 .on_write = hsk_client_on_write, 218 .on_close = hsk_client_on_close, 219}; 220