1/*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25#include <private-lib-core.h>
26
27static int
28secstream_ws(struct lws *wsi, enum lws_callback_reasons reason, void *user,
29	     void *in, size_t len)
30{
31#if defined(LWS_WITH_SERVER)
32	struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
33#endif
34	lws_ss_handle_t *h = (lws_ss_handle_t *)lws_get_opaque_user_data(wsi);
35	uint8_t buf[LWS_PRE + 1400];
36	lws_ss_state_return_t r;
37	int f = 0, f1, n;
38	size_t buflen;
39
40	switch (reason) {
41
42	/* because we are protocols[0] ... */
43	case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
44		lwsl_info("%s: CLIENT_CONNECTION_ERROR: %s\n", __func__,
45			 in ? (char *)in : "(null)");
46		if (!h)
47			break;
48
49#if defined(LWS_WITH_CONMON)
50		lws_conmon_ss_json(h);
51#endif
52
53		r = lws_ss_event_helper(h, LWSSSCS_UNREACHABLE);
54		if (r == LWSSSSRET_DESTROY_ME)
55			return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
56
57		h->wsi = NULL;
58		r = lws_ss_backoff(h);
59		if (r != LWSSSSRET_OK)
60			return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
61		break;
62
63	case LWS_CALLBACK_CLOSED: /* server */
64	case LWS_CALLBACK_CLIENT_CLOSED:
65		if (!h)
66			break;
67		lws_sul_cancel(&h->sul_timeout);
68
69#if defined(LWS_WITH_CONMON)
70		lws_conmon_ss_json(h);
71#endif
72
73		r = lws_ss_event_helper(h, LWSSSCS_DISCONNECTED);
74		if (r == LWSSSSRET_DESTROY_ME)
75			return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
76
77		if (h->wsi)
78			lws_set_opaque_user_data(h->wsi, NULL);
79		h->wsi = NULL;
80
81#if defined(LWS_WITH_SERVER)
82		lws_pt_lock(pt, __func__);
83		lws_dll2_remove(&h->cli_list);
84		lws_pt_unlock(pt);
85#endif
86
87		if (reason == LWS_CALLBACK_CLIENT_CLOSED) {
88			if (h->policy &&
89			    !(h->policy->flags & LWSSSPOLF_OPPORTUNISTIC) &&
90#if defined(LWS_WITH_SERVER)
91			    !(h->info.flags & LWSSSINFLAGS_ACCEPTED) && /* not server */
92#endif
93			    !wsi->a.context->being_destroyed) {
94				r = lws_ss_backoff(h);
95				if (r != LWSSSSRET_OK)
96					return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
97				break;
98			}
99
100#if defined(LWS_WITH_SERVER)
101			if (h->info.flags & LWSSSINFLAGS_ACCEPTED) {
102				/*
103				 * was an accepted client connection to
104				 * our server, so the stream is over now
105				 */
106				lws_ss_destroy(&h);
107				return 0;
108			}
109#endif
110
111		}
112		break;
113
114	case LWS_CALLBACK_ESTABLISHED:
115	case LWS_CALLBACK_CLIENT_ESTABLISHED:
116		h->retry = 0;
117		h->seqstate = SSSEQ_CONNECTED;
118		lws_sul_cancel(&h->sul);
119#if defined(LWS_WITH_SYS_METRICS)
120		/*
121		 * If any hanging caliper measurement, dump it, and free any tags
122		 */
123		lws_metrics_caliper_report_hist(h->cal_txn, (struct lws *)NULL);
124#endif
125		r = lws_ss_event_helper(h, LWSSSCS_CONNECTED);
126		if (r != LWSSSSRET_OK)
127			return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
128		break;
129
130	case LWS_CALLBACK_RECEIVE:
131	case LWS_CALLBACK_CLIENT_RECEIVE:
132		// lwsl_user("LWS_CALLBACK_CLIENT_RECEIVE: read %d\n", (int)len);
133		if (!h || !h->info.rx)
134			return 0;
135		if (lws_is_first_fragment(wsi))
136			f |= LWSSS_FLAG_SOM;
137		if (lws_is_final_fragment(wsi))
138			f |= LWSSS_FLAG_EOM;
139		// lws_frame_is_binary(wsi);
140
141		h->subseq = 1;
142
143		r = h->info.rx(ss_to_userobj(h), (const uint8_t *)in, len, f);
144		if (r != LWSSSSRET_OK)
145			return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
146
147		return 0; /* don't passthru */
148
149	case LWS_CALLBACK_SERVER_WRITEABLE:
150	case LWS_CALLBACK_CLIENT_WRITEABLE:
151		// lwsl_notice("%s: %s: WRITEABLE\n", __func__, lws_ss_tag(h));
152		if (!h || !h->info.tx)
153			return 0;
154
155		if (h->seqstate != SSSEQ_CONNECTED) {
156			lwsl_warn("%s: seqstate %d\n", __func__, h->seqstate);
157			break;
158		}
159
160		buflen = sizeof(buf) - LWS_PRE;
161		r = h->info.tx(ss_to_userobj(h),  h->txord++, buf + LWS_PRE,
162				  &buflen, &f);
163		if (r == LWSSSSRET_TX_DONT_SEND)
164			return 0;
165		if (r != LWSSSSRET_OK)
166			return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
167
168		f1 = lws_write_ws_flags(h->policy->u.http.u.ws.binary ?
169					   LWS_WRITE_BINARY : LWS_WRITE_TEXT,
170					!!(f & LWSSS_FLAG_SOM),
171					!!(f & LWSSS_FLAG_EOM));
172
173		n = lws_write(wsi, buf + LWS_PRE, buflen, (enum lws_write_protocol)f1);
174		if (n < (int)buflen) {
175			lwsl_info("%s: write failed %d %d\n", __func__,
176					n, (int)buflen);
177
178			return -1;
179		}
180
181		return 0;
182
183	default:
184		break;
185	}
186
187	return lws_callback_http_dummy(wsi, reason, user, in, len);
188}
189
190const struct lws_protocols protocol_secstream_ws = {
191	"lws-secstream-ws",
192	secstream_ws,
193	0, 0, 0, NULL, 0
194};
195/*
196 * Munge connect info according to protocol-specific considerations... this
197 * usually means interpreting aux in a protocol-specific way and using the
198 * pieces at connection setup time, eg, http url pieces.
199 *
200 * len bytes of buf can be used for things with scope until after the actual
201 * connect.
202 *
203 * For ws, protocol aux is <url path>;<ws subprotocol name>
204 */
205
206static int
207secstream_connect_munge_ws(lws_ss_handle_t *h, char *buf, size_t len,
208			   struct lws_client_connect_info *i,
209			   union lws_ss_contemp *ct)
210{
211	const char *pbasis = h->policy->u.http.url;
212	size_t used_in, used_out;
213	lws_strexp_t exp;
214
215	/* i.path on entry is used to override the policy urlpath if not "" */
216
217	if (i->path[0])
218		pbasis = i->path;
219
220	if (!pbasis)
221		return 0;
222
223	if (h->policy->flags & LWSSSPOLF_HTTP_CACHE_COOKIES)
224		i->ssl_connection |= LCCSCF_CACHE_COOKIES;
225
226	if (h->policy->flags & LWSSSPOLF_PRIORITIZE_READS)
227		i->ssl_connection |= LCCSCF_PRIORITIZE_READS;
228
229	/* protocol aux is the path part ; ws subprotocol name */
230
231	i->path = buf;
232	buf[0] = '/';
233
234	lws_strexp_init(&exp, (void *)h, lws_ss_exp_cb_metadata, buf + 1, len - 1);
235
236	if (lws_strexp_expand(&exp, pbasis, strlen(pbasis),
237			      &used_in, &used_out) != LSTRX_DONE)
238		return 1;
239
240	i->protocol = h->policy->u.http.u.ws.subprotocol;
241
242	lwsl_ss_info(h, "url %s, ws subprotocol %s", buf, i->protocol);
243
244	return 0;
245}
246
247const struct ss_pcols ss_pcol_ws = {
248	"ws",  "http/1.1",  &protocol_secstream_ws, secstream_connect_munge_ws, 0, 0
249};
250