190 lines
5.5 KiB
C
190 lines
5.5 KiB
C
/* Copyright (c) 2017 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 "lsquic_int_types.h"
|
|
#include "lsquic.h"
|
|
|
|
#include "lsquic_handshake.h"
|
|
#include "lsquic_chsk_stream.h"
|
|
#include "lsquic_ver_neg.h"
|
|
#include "lsquic_conn.h"
|
|
#include "lsquic_mm.h"
|
|
|
|
#define LSQUIC_LOGGER_MODULE LSQLM_HSK_ADAPTER
|
|
#define LSQUIC_LOG_CONN_ID lsquic_conn_id(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;
|
|
|
|
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 = handle_chlo_reply(c_hsk->lconn->cn_enc_session,
|
|
c_hsk->buf_in, c_hsk->buf_off);
|
|
LSQ_DEBUG("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);
|
|
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 (is_hs_done(c_hsk->lconn->cn_enc_session))
|
|
{
|
|
LSQ_DEBUG("handshake is complete, inform connection");
|
|
c_hsk->lconn->cn_if->ci_handshake_done(c_hsk->lconn);
|
|
}
|
|
else
|
|
{
|
|
LSQ_DEBUG("handshake not yet complete, will generate another "
|
|
"message");
|
|
lsquic_stream_wantwrite(stream, 1);
|
|
}
|
|
break;
|
|
default:
|
|
LSQ_WARN("handle_chlo_reply returned unknown value %d", s);
|
|
case DATA_FORMAT_ERROR:
|
|
LSQ_INFO("handle_chlo_reply returned an error");
|
|
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 != 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,
|
|
};
|