1 /*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2019 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 #include "extension-permessage-deflate.h"
28
29 void
lws_context_init_extensions(const struct lws_context_creation_info * info,struct lws_context * context)30 lws_context_init_extensions(const struct lws_context_creation_info *info,
31 struct lws_context *context)
32 {
33 lwsl_cx_info(context, " LWS_MAX_EXTENSIONS_ACTIVE: %u", LWS_MAX_EXTENSIONS_ACTIVE);
34 }
35
36 enum lws_ext_option_parser_states {
37 LEAPS_SEEK_NAME,
38 LEAPS_EAT_NAME,
39 LEAPS_SEEK_VAL,
40 LEAPS_EAT_DEC,
41 LEAPS_SEEK_ARG_TERM
42 };
43
44 int
lws_ext_parse_options(const struct lws_extension * ext,struct lws * wsi,void * ext_user,const struct lws_ext_options * opts,const char * in,int len)45 lws_ext_parse_options(const struct lws_extension *ext, struct lws *wsi,
46 void *ext_user, const struct lws_ext_options *opts,
47 const char *in, int len)
48 {
49 enum lws_ext_option_parser_states leap = LEAPS_SEEK_NAME;
50 unsigned int match_map = 0, n, m, w = 0, count_options = 0,
51 pending_close_quote = 0;
52 struct lws_ext_option_arg oa;
53
54 oa.option_name = NULL;
55
56 while (opts[count_options].name)
57 count_options++;
58 while (len) {
59 lwsl_wsi_ext(wsi, "'%c' %d", *in, leap);
60 switch (leap) {
61 case LEAPS_SEEK_NAME:
62 if (*in == ' ')
63 break;
64 if (*in == ',') {
65 len = 1;
66 break;
67 }
68 match_map = (unsigned int)(1 << count_options) - 1;
69 leap = LEAPS_EAT_NAME;
70 w = 0;
71
72 /* fallthru */
73
74 case LEAPS_EAT_NAME:
75 oa.start = NULL;
76 oa.len = 0;
77 m = match_map;
78 n = 0;
79 pending_close_quote = 0;
80 while (m) {
81 if (!(m & 1)) {
82 m >>= 1;
83 n++;
84 continue;
85 }
86 lwsl_wsi_ext(wsi, " m=%d, n=%d, w=%d", m, n, w);
87
88 if (*in == opts[n].name[w]) {
89 if (!opts[n].name[w + 1]) {
90 oa.option_index = (int)n;
91 lwsl_wsi_ext(wsi, "hit %d",
92 oa.option_index);
93 leap = LEAPS_SEEK_VAL;
94 if (len == 1)
95 goto set_arg;
96 break;
97 }
98 } else {
99 match_map &= (unsigned int)~(1 << n);
100 if (!match_map) {
101 lwsl_wsi_ext(wsi, "empty match map");
102 return -1;
103 }
104 }
105
106 m >>= 1;
107 n++;
108 }
109 w++;
110 break;
111 case LEAPS_SEEK_VAL:
112 if (*in == ' ')
113 break;
114 if (*in == ',') {
115 len = 1;
116 break;
117 }
118 if (*in == ';' || len == 1) { /* ie,nonoptional */
119 if (opts[oa.option_index].type == EXTARG_DEC)
120 return -1;
121 leap = LEAPS_SEEK_NAME;
122 goto set_arg;
123 }
124 if (*in == '=') {
125 w = 0;
126 pending_close_quote = 0;
127 if (opts[oa.option_index].type == EXTARG_NONE)
128 return -1;
129
130 leap = LEAPS_EAT_DEC;
131 break;
132 }
133 return -1;
134
135 case LEAPS_EAT_DEC:
136 if (*in >= '0' && *in <= '9') {
137 if (!w)
138 oa.start = in;
139 w++;
140 if (len != 1)
141 break;
142 }
143 if (!w && *in =='"') {
144 pending_close_quote = 1;
145 break;
146 }
147 if (!w)
148 return -1;
149 if (pending_close_quote && *in != '"' && len != 1)
150 return -1;
151 leap = LEAPS_SEEK_ARG_TERM;
152 if (oa.start)
153 oa.len = lws_ptr_diff(in, oa.start);
154 if (len == 1)
155 oa.len++;
156
157 set_arg:
158 ext->callback(lws_get_context(wsi),
159 ext, wsi, LWS_EXT_CB_OPTION_SET,
160 ext_user, (char *)&oa, 0);
161 if (len == 1)
162 break;
163 if (pending_close_quote && *in == '"')
164 break;
165
166 /* fallthru */
167
168 case LEAPS_SEEK_ARG_TERM:
169 if (*in == ' ')
170 break;
171 if (*in == ';') {
172 leap = LEAPS_SEEK_NAME;
173 break;
174 }
175 if (*in == ',') {
176 len = 1;
177 break;
178 }
179 return -1;
180 }
181 len--;
182 in++;
183 }
184
185 return 0;
186 }
187
188
189 /* 0 = nobody had nonzero return, 1 = somebody had positive return, -1 = fail */
190
lws_ext_cb_active(struct lws * wsi,int reason,void * arg,int len)191 int lws_ext_cb_active(struct lws *wsi, int reason, void *arg, int len)
192 {
193 int n, m, handled = 0;
194
195 if (!wsi->ws)
196 return 0;
197
198 for (n = 0; n < wsi->ws->count_act_ext; n++) {
199 m = wsi->ws->active_extensions[n]->callback(
200 lws_get_context(wsi), wsi->ws->active_extensions[n],
201 wsi, (enum lws_extension_callback_reasons)reason, wsi->ws->act_ext_user[n], arg, (size_t)len);
202 if (m < 0) {
203 lwsl_wsi_ext(wsi, "Ext '%s' failed to handle callback %d!",
204 wsi->ws->active_extensions[n]->name, reason);
205 return -1;
206 }
207 /* valgrind... */
208 if (reason == LWS_EXT_CB_DESTROY)
209 wsi->ws->act_ext_user[n] = NULL;
210 if (m > handled)
211 handled = m;
212 }
213
214 return handled;
215 }
216
lws_ext_cb_all_exts(struct lws_context * context,struct lws * wsi,int reason,void * arg,int len)217 int lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi,
218 int reason, void *arg, int len)
219 {
220 int n = 0, m, handled = 0;
221 const struct lws_extension *ext;
222
223 if (!wsi || !wsi->a.vhost || !wsi->ws)
224 return 0;
225
226 ext = wsi->a.vhost->ws.extensions;
227
228 while (ext && ext->callback && !handled) {
229 m = ext->callback(context, ext, wsi, (enum lws_extension_callback_reasons)reason,
230 (void *)(lws_intptr_t)n, arg, (size_t)len);
231 if (m < 0) {
232 lwsl_wsi_ext(wsi, "Ext '%s' failed to handle callback %d!",
233 wsi->ws->active_extensions[n]->name, reason);
234 return -1;
235 }
236 if (m)
237 handled = 1;
238
239 ext++;
240 n++;
241 }
242
243 return 0;
244 }
245
246 int
lws_issue_raw_ext_access(struct lws * wsi,unsigned char * buf,size_t len)247 lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len)
248 {
249 struct lws_tokens ebuf;
250 int ret, m, n = 0;
251
252 ebuf.token = buf;
253 ebuf.len = (int)len;
254
255 /*
256 * while we have original buf to spill ourselves, or extensions report
257 * more in their pipeline
258 */
259
260 ret = 1;
261 while (ret == 1) {
262
263 /* default to nobody has more to spill */
264
265 ret = 0;
266
267 /* show every extension the new incoming data */
268 m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_TX_PRESEND,
269 &ebuf, 0);
270 if (m < 0)
271 return -1;
272 if (m) /* handled */
273 ret = 1;
274
275 if (buf != ebuf.token)
276 /*
277 * extension recreated it:
278 * need to buffer this if not all sent
279 */
280 wsi->ws->clean_buffer = 0;
281
282 /* assuming they left us something to send, send it */
283
284 if (ebuf.len) {
285 n = lws_issue_raw(wsi, ebuf.token, (size_t)ebuf.len);
286 if (n < 0) {
287 lwsl_wsi_info(wsi, "closing from ext access");
288 return -1;
289 }
290
291 /* always either sent it all or privately buffered */
292 if (wsi->ws->clean_buffer)
293 len = (size_t)n;
294
295 lwsl_wsi_ext(wsi, "written %d bytes to client", n);
296 }
297
298 /* no extension has more to spill? Then we can go */
299
300 if (!ret)
301 break;
302
303 /* we used up what we had */
304
305 ebuf.token = NULL;
306 ebuf.len = 0;
307
308 /*
309 * Did that leave the pipe choked?
310 * Or we had to hold on to some of it?
311 */
312
313 if (!lws_send_pipe_choked(wsi) && !lws_has_buffered_out(wsi))
314 /* no we could add more, lets's do that */
315 continue;
316
317 lwsl_wsi_debug(wsi, "choked");
318
319 /*
320 * Yes, he's choked. Don't spill the rest now get a callback
321 * when he is ready to send and take care of it there
322 */
323 lws_callback_on_writable(wsi);
324 wsi->ws->extension_data_pending = 1;
325 ret = 0;
326 }
327
328 return (int)len;
329 }
330
331 int
lws_any_extension_handled(struct lws * wsi,enum lws_extension_callback_reasons r,void * v,size_t len)332 lws_any_extension_handled(struct lws *wsi, enum lws_extension_callback_reasons r,
333 void *v, size_t len)
334 {
335 struct lws_context *context = wsi->a.context;
336 int n, handled = 0;
337
338 if (!wsi->ws)
339 return 0;
340
341 /* maybe an extension will take care of it for us */
342
343 for (n = 0; n < wsi->ws->count_act_ext && !handled; n++) {
344 if (!wsi->ws->active_extensions[n]->callback)
345 continue;
346
347 handled |= wsi->ws->active_extensions[n]->callback(context,
348 wsi->ws->active_extensions[n], wsi,
349 r, wsi->ws->act_ext_user[n], v, len);
350 }
351
352 return handled;
353 }
354
355 int
lws_set_extension_option(struct lws * wsi,const char * ext_name,const char * opt_name,const char * opt_val)356 lws_set_extension_option(struct lws *wsi, const char *ext_name,
357 const char *opt_name, const char *opt_val)
358 {
359 struct lws_ext_option_arg oa;
360 int idx = 0;
361
362 if (!wsi->ws)
363 return 0;
364
365 /* first identify if the ext is active on this wsi */
366 while (idx < wsi->ws->count_act_ext &&
367 strcmp(wsi->ws->active_extensions[idx]->name, ext_name))
368 idx++;
369
370 if (idx == wsi->ws->count_act_ext)
371 return -1; /* request ext not active on this wsi */
372
373 oa.option_name = opt_name;
374 oa.option_index = 0;
375 oa.start = opt_val;
376 oa.len = 0;
377
378 return wsi->ws->active_extensions[idx]->callback(wsi->a.context,
379 wsi->ws->active_extensions[idx], wsi,
380 LWS_EXT_CB_NAMED_OPTION_SET, wsi->ws->act_ext_user[idx],
381 &oa, 0);
382 }
383