1/* Copyright (c) 2017 - 2022 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    lsquic_stream_wantwrite(stream, 1);
42
43    return (void *) c_hsk;
44}
45
46
47static void
48hsk_client_on_read (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh)
49{
50    struct client_hsk_ctx *const c_hsk = (struct client_hsk_ctx *) sh;
51    ssize_t nread;
52    int s;
53    enum lsquic_hsk_status status;
54
55    if (!c_hsk->buf_in)
56    {
57        c_hsk->buf_in  = lsquic_mm_get_16k(c_hsk->mm);
58        if (!c_hsk->buf_in)
59        {
60            LSQ_WARN("could not get buffer: %s", strerror(errno));
61            lsquic_stream_wantread(stream, 0);
62            lsquic_conn_close(c_hsk->lconn);
63            return;
64        }
65        c_hsk->buf_sz  = 16 * 1024;
66        c_hsk->buf_off = 0;
67    }
68
69    nread = lsquic_stream_read(stream, c_hsk->buf_in + c_hsk->buf_off,
70                                            c_hsk->buf_sz - c_hsk->buf_off);
71    if (nread <= 0)
72    {
73        if (nread < 0)
74            LSQ_INFO("Could not read from handshake stream: %s",
75                                                            strerror(errno));
76        else
77            LSQ_INFO("Handshake stream closed (odd)");
78        lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
79        c_hsk->buf_in = NULL;
80        lsquic_stream_wantread(stream, 0);
81        lsquic_conn_close(c_hsk->lconn);
82        return;
83    }
84    c_hsk->buf_off += nread;
85
86    s = c_hsk->lconn->cn_esf.g->esf_handle_chlo_reply(c_hsk->lconn->cn_enc_session,
87                                        c_hsk->buf_in, c_hsk->buf_off);
88    LSQ_DEBUG("lsquic_enc_session_handle_chlo_reply returned %d", s);
89    switch (s)
90    {
91    case DATA_NOT_ENOUGH:
92        if (c_hsk->buf_off < c_hsk->buf_sz)
93            LSQ_INFO("not enough server response has arrived, continue "
94                                                                "buffering");
95        else
96        {
97            LSQ_INFO("read in %u bytes of server response, and it is still "
98                        "not enough: giving up", c_hsk->buf_off);
99            lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
100            c_hsk->buf_in = NULL;
101            lsquic_stream_wantread(stream, 0);
102            c_hsk->lconn->cn_if->ci_hsk_done(c_hsk->lconn, LSQ_HSK_FAIL);
103            lsquic_conn_close(c_hsk->lconn);
104        }
105        break;
106    case DATA_NO_ERROR:
107        lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
108        c_hsk->buf_in = NULL;
109        lsquic_stream_wantread(stream, 0);
110        if (c_hsk->lconn->cn_esf.g->esf_is_hsk_done(c_hsk->lconn->cn_enc_session))
111        {
112            LSQ_DEBUG("handshake is successful, inform connection");
113            status = (c_hsk->lconn->cn_esf_c->esf_did_sess_resume_succeed(
114                c_hsk->lconn->cn_enc_session)) ? LSQ_HSK_RESUMED_OK : LSQ_HSK_OK;
115            c_hsk->lconn->cn_if->ci_hsk_done(c_hsk->lconn, status);
116        }
117        else
118        {
119            LSQ_DEBUG("handshake not yet complete, will generate another "
120                                                                    "message");
121            lsquic_stream_wantwrite(stream, 1);
122        }
123        break;
124    case HS_SREJ:
125        LSQ_DEBUG("got HS_SREJ");
126        c_hsk->buf_off = 0;
127        lsquic_stream_wantread(stream, 0);
128        if (0 == lsquic_gquic_full_conn_srej(c_hsk->lconn))
129            lsquic_stream_wantwrite(stream, 1);
130        break;
131    default:
132        LSQ_WARN("lsquic_enc_session_handle_chlo_reply returned unknown value %d", s);
133        /* fallthru */
134    case DATA_FORMAT_ERROR:
135        LSQ_INFO("lsquic_enc_session_handle_chlo_reply returned an error");
136        lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
137        c_hsk->buf_in = NULL;
138        lsquic_stream_wantread(stream, 0);
139        c_hsk->lconn->cn_if->ci_hsk_done(c_hsk->lconn, LSQ_HSK_FAIL);
140        lsquic_conn_close(c_hsk->lconn);
141        break;
142    }
143}
144
145
146/* In this function, we assume that we can write the whole message in one
147 * shot.  Otherwise, this is an error.
148 */
149static void
150hsk_client_on_write (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh)
151{
152    struct client_hsk_ctx *const c_hsk = (struct client_hsk_ctx *) sh;
153    unsigned char *buf;
154    size_t len;
155    ssize_t nw;
156
157    lsquic_stream_wantwrite(stream, 0);
158
159    buf = lsquic_mm_get_4k(c_hsk->mm);
160    if (!buf)
161    {
162        LSQ_WARN("cannot allocate buffer: %s", strerror(errno));
163        lsquic_conn_close(c_hsk->lconn);
164        return;
165    }
166    len = 4 * 1024;
167
168    if (0 != c_hsk->lconn->cn_esf.g->esf_gen_chlo(c_hsk->lconn->cn_enc_session,
169                                            c_hsk->ver_neg->vn_ver, buf, &len))
170    {
171        LSQ_WARN("cannot create CHLO message");
172        lsquic_mm_put_4k(c_hsk->mm, buf);
173        lsquic_conn_close(c_hsk->lconn);
174        return;
175    }
176
177    nw = lsquic_stream_write(stream, buf, len);
178    lsquic_mm_put_4k(c_hsk->mm, buf);
179
180    if (nw < 0)
181        LSQ_INFO("error writing to stream: %s", strerror(errno));
182    else if ((size_t) nw == len)
183    {
184        LSQ_INFO("wrote %zd bytes of CHLO to stream", nw);
185        lsquic_stream_flush(stream);
186        lsquic_stream_wantread(stream, 1);
187    }
188    else
189        LSQ_INFO("could only write %zd bytes to stream instead of %zd",
190                                                                    nw, len);
191}
192
193
194static void
195hsk_client_on_close (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh)
196{
197    struct client_hsk_ctx *const c_hsk = (struct client_hsk_ctx *) sh;
198    if (c_hsk->buf_in)
199        lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
200    LSQ_DEBUG("stream closed");
201}
202
203
204const struct lsquic_stream_if lsquic_client_hsk_stream_if =
205{
206    .on_new_stream = hsk_client_on_new_stream,
207    .on_read       = hsk_client_on_read,
208    .on_write      = hsk_client_on_write,
209    .on_close      = hsk_client_on_close,
210};
211