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 
27 static int
secstream_ws(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)28 secstream_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 
190 const struct lws_protocols protocol_secstream_ws = {
191 	"lws-secstream-ws",
192 	secstream_ws,
193 	0,
194 	0,
195 };
196 /*
197  * Munge connect info according to protocol-specific considerations... this
198  * usually means interpreting aux in a protocol-specific way and using the
199  * pieces at connection setup time, eg, http url pieces.
200  *
201  * len bytes of buf can be used for things with scope until after the actual
202  * connect.
203  *
204  * For ws, protocol aux is <url path>;<ws subprotocol name>
205  */
206 
207 static int
secstream_connect_munge_ws(lws_ss_handle_t * h,char * buf,size_t len,struct lws_client_connect_info * i,union lws_ss_contemp * ct)208 secstream_connect_munge_ws(lws_ss_handle_t *h, char *buf, size_t len,
209 			   struct lws_client_connect_info *i,
210 			   union lws_ss_contemp *ct)
211 {
212 	const char *pbasis = h->policy->u.http.url;
213 	size_t used_in, used_out;
214 	lws_strexp_t exp;
215 
216 	lwsl_notice("%s\n", __func__);
217 
218 	/* i.path on entry is used to override the policy urlpath if not "" */
219 
220 	if (i->path[0])
221 		pbasis = i->path;
222 
223 	if (!pbasis)
224 		return 0;
225 
226 	/* protocol aux is the path part ; ws subprotocol name */
227 
228 	i->path = buf;
229 	buf[0] = '/';
230 
231 	lws_strexp_init(&exp, (void *)h, lws_ss_exp_cb_metadata, buf + 1, len - 1);
232 
233 	if (lws_strexp_expand(&exp, pbasis, strlen(pbasis),
234 			      &used_in, &used_out) != LSTRX_DONE)
235 		return 1;
236 
237 	i->protocol = h->policy->u.http.u.ws.subprotocol;
238 
239 	lwsl_notice("%s: url %s, ws subprotocol %s\n", __func__, buf, i->protocol);
240 
241 	return 0;
242 }
243 
244 const struct ss_pcols ss_pcol_ws = {
245 	"ws",  "http/1.1",  &protocol_secstream_ws, secstream_connect_munge_ws
246 };
247