litespeed-quic/src/liblsquic/lsquic_chsk_stream.c

211 lines
6.5 KiB
C

/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc. See LICENSE. */
/*
* Stream/crypto handshake adapter for the client side.
*
* The client composes CHLO, writes it to the stream, and wait for the
* server response, which it processes.
*/
#include <assert.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic_int_types.h"
#include "lsquic.h"
#include "lsquic_str.h"
#include "lsquic_enc_sess.h"
#include "lsquic_chsk_stream.h"
#include "lsquic_ver_neg.h"
#include "lsquic_hash.h"
#include "lsquic_conn.h"
#include "lsquic_mm.h"
#include "lsquic_sizes.h"
#include "lsquic_full_conn.h"
#define LSQUIC_LOGGER_MODULE LSQLM_HSK_ADAPTER
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(c_hsk->lconn)
#include "lsquic_logger.h"
static lsquic_stream_ctx_t *
hsk_client_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
{
struct client_hsk_ctx *const c_hsk = stream_if_ctx;
LSQ_DEBUG("stream created");
lsquic_stream_wantwrite(stream, 1);
return (void *) c_hsk;
}
static void
hsk_client_on_read (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh)
{
struct client_hsk_ctx *const c_hsk = (struct client_hsk_ctx *) sh;
ssize_t nread;
int s;
enum lsquic_hsk_status status;
if (!c_hsk->buf_in)
{
c_hsk->buf_in = lsquic_mm_get_16k(c_hsk->mm);
if (!c_hsk->buf_in)
{
LSQ_WARN("could not get buffer: %s", strerror(errno));
lsquic_stream_wantread(stream, 0);
lsquic_conn_close(c_hsk->lconn);
return;
}
c_hsk->buf_sz = 16 * 1024;
c_hsk->buf_off = 0;
}
nread = lsquic_stream_read(stream, c_hsk->buf_in + c_hsk->buf_off,
c_hsk->buf_sz - c_hsk->buf_off);
if (nread <= 0)
{
if (nread < 0)
LSQ_INFO("Could not read from handshake stream: %s",
strerror(errno));
else
LSQ_INFO("Handshake stream closed (odd)");
lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
c_hsk->buf_in = NULL;
lsquic_stream_wantread(stream, 0);
lsquic_conn_close(c_hsk->lconn);
return;
}
c_hsk->buf_off += nread;
s = c_hsk->lconn->cn_esf.g->esf_handle_chlo_reply(c_hsk->lconn->cn_enc_session,
c_hsk->buf_in, c_hsk->buf_off);
LSQ_DEBUG("lsquic_enc_session_handle_chlo_reply returned %d", s);
switch (s)
{
case DATA_NOT_ENOUGH:
if (c_hsk->buf_off < c_hsk->buf_sz)
LSQ_INFO("not enough server response has arrived, continue "
"buffering");
else
{
LSQ_INFO("read in %u bytes of server response, and it is still "
"not enough: giving up", c_hsk->buf_off);
lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
c_hsk->buf_in = NULL;
lsquic_stream_wantread(stream, 0);
c_hsk->lconn->cn_if->ci_hsk_done(c_hsk->lconn, LSQ_HSK_FAIL);
lsquic_conn_close(c_hsk->lconn);
}
break;
case DATA_NO_ERROR:
lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
c_hsk->buf_in = NULL;
lsquic_stream_wantread(stream, 0);
if (c_hsk->lconn->cn_esf.g->esf_is_hsk_done(c_hsk->lconn->cn_enc_session))
{
LSQ_DEBUG("handshake is successful, inform connection");
status = (c_hsk->lconn->cn_esf_c->esf_did_sess_resume_succeed(
c_hsk->lconn->cn_enc_session)) ? LSQ_HSK_RESUMED_OK : LSQ_HSK_OK;
c_hsk->lconn->cn_if->ci_hsk_done(c_hsk->lconn, status);
}
else
{
LSQ_DEBUG("handshake not yet complete, will generate another "
"message");
lsquic_stream_wantwrite(stream, 1);
}
break;
case HS_SREJ:
LSQ_DEBUG("got HS_SREJ");
c_hsk->buf_off = 0;
lsquic_stream_wantread(stream, 0);
if (0 == lsquic_gquic_full_conn_srej(c_hsk->lconn))
lsquic_stream_wantwrite(stream, 1);
break;
default:
LSQ_WARN("lsquic_enc_session_handle_chlo_reply returned unknown value %d", s);
/* fallthru */
case DATA_FORMAT_ERROR:
LSQ_INFO("lsquic_enc_session_handle_chlo_reply returned an error");
lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
c_hsk->buf_in = NULL;
lsquic_stream_wantread(stream, 0);
c_hsk->lconn->cn_if->ci_hsk_done(c_hsk->lconn, LSQ_HSK_FAIL);
lsquic_conn_close(c_hsk->lconn);
break;
}
}
/* In this function, we assume that we can write the whole message in one
* shot. Otherwise, this is an error.
*/
static void
hsk_client_on_write (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh)
{
struct client_hsk_ctx *const c_hsk = (struct client_hsk_ctx *) sh;
unsigned char *buf;
size_t len;
ssize_t nw;
lsquic_stream_wantwrite(stream, 0);
buf = lsquic_mm_get_4k(c_hsk->mm);
if (!buf)
{
LSQ_WARN("cannot allocate buffer: %s", strerror(errno));
lsquic_conn_close(c_hsk->lconn);
return;
}
len = 4 * 1024;
if (0 != c_hsk->lconn->cn_esf.g->esf_gen_chlo(c_hsk->lconn->cn_enc_session,
c_hsk->ver_neg->vn_ver, buf, &len))
{
LSQ_WARN("cannot create CHLO message");
lsquic_mm_put_4k(c_hsk->mm, buf);
lsquic_conn_close(c_hsk->lconn);
return;
}
nw = lsquic_stream_write(stream, buf, len);
lsquic_mm_put_4k(c_hsk->mm, buf);
if (nw < 0)
LSQ_INFO("error writing to stream: %s", strerror(errno));
else if ((size_t) nw == len)
{
LSQ_INFO("wrote %zd bytes of CHLO to stream", nw);
lsquic_stream_flush(stream);
lsquic_stream_wantread(stream, 1);
}
else
LSQ_INFO("could only write %zd bytes to stream instead of %zd",
nw, len);
}
static void
hsk_client_on_close (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh)
{
struct client_hsk_ctx *const c_hsk = (struct client_hsk_ctx *) sh;
if (c_hsk->buf_in)
lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
LSQ_DEBUG("stream closed");
}
const struct lsquic_stream_if lsquic_client_hsk_stream_if =
{
.on_new_stream = hsk_client_on_new_stream,
.on_read = hsk_client_on_read,
.on_write = hsk_client_on_write,
.on_close = hsk_client_on_close,
};