lsquic_chsk_stream.c revision 229fce07
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
15#include "lsquic_int_types.h"
16#include "lsquic.h"
17
18#include "lsquic_str.h"
19#include "lsquic_handshake.h"
20#include "lsquic_chsk_stream.h"
21#include "lsquic_ver_neg.h"
22#include "lsquic_conn.h"
23#include "lsquic_mm.h"
24
25#define LSQUIC_LOGGER_MODULE LSQLM_HSK_ADAPTER
26#define LSQUIC_LOG_CONN_ID lsquic_conn_id(c_hsk->lconn)
27#include "lsquic_logger.h"
28
29
30static lsquic_stream_ctx_t *
31hsk_client_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
32{
33    struct client_hsk_ctx *const c_hsk = stream_if_ctx;
34
35    LSQ_DEBUG("stream created");
36
37
38    lsquic_stream_wantwrite(stream, 1);
39
40    return (void *) c_hsk;
41}
42
43
44static void
45hsk_client_on_read (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh)
46{
47    struct client_hsk_ctx *const c_hsk = (struct client_hsk_ctx *) sh;
48    ssize_t nread;
49    int s;
50
51    if (!c_hsk->buf_in)
52    {
53        c_hsk->buf_in  = lsquic_mm_get_16k(c_hsk->mm);
54        if (!c_hsk->buf_in)
55        {
56            LSQ_WARN("could not get buffer: %s", strerror(errno));
57            lsquic_stream_wantread(stream, 0);
58            lsquic_conn_close(c_hsk->lconn);
59            return;
60        }
61        c_hsk->buf_sz  = 16 * 1024;
62        c_hsk->buf_off = 0;
63    }
64
65    nread = lsquic_stream_read(stream, c_hsk->buf_in + c_hsk->buf_off,
66                                            c_hsk->buf_sz - c_hsk->buf_off);
67    if (nread <= 0)
68    {
69        if (nread < 0)
70            LSQ_INFO("Could not read from handshake stream: %s",
71                                                            strerror(errno));
72        else
73            LSQ_INFO("Handshake stream closed (odd)");
74        lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
75        c_hsk->buf_in = NULL;
76        lsquic_stream_wantread(stream, 0);
77        lsquic_conn_close(c_hsk->lconn);
78        return;
79    }
80    c_hsk->buf_off += nread;
81
82    s = c_hsk->lconn->cn_esf->esf_handle_chlo_reply(c_hsk->lconn->cn_enc_session,
83                                        c_hsk->buf_in, c_hsk->buf_off);
84    LSQ_DEBUG("lsquic_enc_session_handle_chlo_reply returned %d", s);
85    switch (s)
86    {
87    case DATA_NOT_ENOUGH:
88        if (c_hsk->buf_off < c_hsk->buf_sz)
89            LSQ_INFO("not enough server response has arrived, continue "
90                                                                "buffering");
91        else
92        {
93            LSQ_INFO("read in %u bytes of server response, and it is still "
94                        "not enough: giving up", c_hsk->buf_off);
95            lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
96            c_hsk->buf_in = NULL;
97            lsquic_stream_wantread(stream, 0);
98            c_hsk->lconn->cn_if->ci_handshake_failed(c_hsk->lconn);
99            lsquic_conn_close(c_hsk->lconn);
100        }
101        break;
102    case DATA_NO_ERROR:
103        lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
104        c_hsk->buf_in = NULL;
105        lsquic_stream_wantread(stream, 0);
106        if (c_hsk->lconn->cn_esf->esf_is_hsk_done(c_hsk->lconn->cn_enc_session))
107        {
108            LSQ_DEBUG("handshake is successful, inform connection");
109            c_hsk->lconn->cn_if->ci_handshake_ok(c_hsk->lconn);
110        }
111        else
112        {
113            LSQ_DEBUG("handshake not yet complete, will generate another "
114                                                                    "message");
115            lsquic_stream_wantwrite(stream, 1);
116        }
117        break;
118    default:
119        LSQ_WARN("lsquic_enc_session_handle_chlo_reply returned unknown value %d", s);
120        /* fallthru */
121    case DATA_FORMAT_ERROR:
122        LSQ_INFO("lsquic_enc_session_handle_chlo_reply returned an error");
123        c_hsk->buf_in = NULL;
124        lsquic_stream_wantread(stream, 0);
125        c_hsk->lconn->cn_if->ci_handshake_failed(c_hsk->lconn);
126        lsquic_conn_close(c_hsk->lconn);
127        break;
128    }
129}
130
131
132/* In this function, we assume that we can write the whole message in one
133 * shot.  Otherwise, this is an error.
134 */
135static void
136hsk_client_on_write (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh)
137{
138    struct client_hsk_ctx *const c_hsk = (struct client_hsk_ctx *) sh;
139    unsigned char *buf;
140    size_t len;
141    ssize_t nw;
142
143    lsquic_stream_wantwrite(stream, 0);
144
145    buf = lsquic_mm_get_4k(c_hsk->mm);
146    if (!buf)
147    {
148        LSQ_WARN("cannot allocate buffer: %s", strerror(errno));
149        lsquic_conn_close(c_hsk->lconn);
150        return;
151    }
152    len = 4 * 1024;
153
154    if (0 != c_hsk->lconn->cn_esf->esf_gen_chlo(c_hsk->lconn->cn_enc_session,
155                                            c_hsk->ver_neg->vn_ver, buf, &len))
156    {
157        LSQ_WARN("cannot create CHLO message");
158        lsquic_mm_put_4k(c_hsk->mm, buf);
159        lsquic_conn_close(c_hsk->lconn);
160        return;
161    }
162
163    nw = lsquic_stream_write(stream, buf, len);
164    lsquic_mm_put_4k(c_hsk->mm, buf);
165
166    if (nw < 0)
167        LSQ_INFO("error writing to stream: %s", strerror(errno));
168    else if ((size_t) nw == len)
169    {
170        LSQ_INFO("wrote %zd bytes of CHLO to stream", nw);
171        lsquic_stream_flush(stream);
172        lsquic_stream_wantread(stream, 1);
173    }
174    else
175        LSQ_INFO("could only write %zd bytes to stream instead of %zd",
176                                                                    nw, len);
177}
178
179
180static void
181hsk_client_on_close (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh)
182{
183    struct client_hsk_ctx *const c_hsk = (struct client_hsk_ctx *) sh;
184    if (c_hsk->buf_in)
185        lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
186    LSQ_DEBUG("stream closed");
187}
188
189
190const struct lsquic_stream_if lsquic_client_hsk_stream_if =
191{
192    .on_new_stream = hsk_client_on_new_stream,
193    .on_read       = hsk_client_on_read,
194    .on_write      = hsk_client_on_write,
195    .on_close      = hsk_client_on_close,
196};
197