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
29void
30lws_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
36enum 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
44int
45lws_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
157set_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
191int 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
217int 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
246int
247lws_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
331int
332lws_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
355int
356lws_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