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, 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
206 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)207 secstream_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
247 const struct ss_pcols ss_pcol_ws = {
248 "ws", "http/1.1", &protocol_secstream_ws, secstream_connect_munge_ws, 0, 0
249 };
250