• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n", 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_ext("'%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 = (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_ext("    m=%d, n=%d, w=%d\n", m, n, w);
87 
88 				if (*in == opts[n].name[w]) {
89 					if (!opts[n].name[w + 1]) {
90 						oa.option_index = n;
91 						lwsl_ext("hit %d\n",
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 &= ~(1 << n);
100 					if (!match_map) {
101 						lwsl_ext("empty match map\n");
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, reason, wsi->ws->act_ext_user[n], arg, len);
202 		if (m < 0) {
203 			lwsl_ext("Ext '%s' failed to handle callback %d!\n",
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->vhost || !wsi->ws)
224 		return 0;
225 
226 	ext = wsi->vhost->ws.extensions;
227 
228 	while (ext && ext->callback && !handled) {
229 		m = ext->callback(context, ext, wsi, reason,
230 				  (void *)(lws_intptr_t)n, arg, len);
231 		if (m < 0) {
232 			lwsl_ext("Ext '%s' failed to handle callback %d!\n",
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, ebuf.len);
286 			if (n < 0) {
287 				lwsl_info("closing from ext access\n");
288 				return -1;
289 			}
290 
291 			/* always either sent it all or privately buffered */
292 			if (wsi->ws->clean_buffer)
293 				len = n;
294 
295 			lwsl_ext("%s: written %d bytes to client\n",
296 				 __func__, n);
297 		}
298 
299 		/* no extension has more to spill?  Then we can go */
300 
301 		if (!ret)
302 			break;
303 
304 		/* we used up what we had */
305 
306 		ebuf.token = NULL;
307 		ebuf.len = 0;
308 
309 		/*
310 		 * Did that leave the pipe choked?
311 		 * Or we had to hold on to some of it?
312 		 */
313 
314 		if (!lws_send_pipe_choked(wsi) && !lws_has_buffered_out(wsi))
315 			/* no we could add more, lets's do that */
316 			continue;
317 
318 		lwsl_debug("choked\n");
319 
320 		/*
321 		 * Yes, he's choked.  Don't spill the rest now get a callback
322 		 * when he is ready to send and take care of it there
323 		 */
324 		lws_callback_on_writable(wsi);
325 		wsi->ws->extension_data_pending = 1;
326 		ret = 0;
327 	}
328 
329 	return (int)len;
330 }
331 
332 int
lws_any_extension_handled(struct lws * wsi,enum lws_extension_callback_reasons r,void * v,size_t len)333 lws_any_extension_handled(struct lws *wsi, enum lws_extension_callback_reasons r,
334 			  void *v, size_t len)
335 {
336 	struct lws_context *context = wsi->context;
337 	int n, handled = 0;
338 
339 	if (!wsi->ws)
340 		return 0;
341 
342 	/* maybe an extension will take care of it for us */
343 
344 	for (n = 0; n < wsi->ws->count_act_ext && !handled; n++) {
345 		if (!wsi->ws->active_extensions[n]->callback)
346 			continue;
347 
348 		handled |= wsi->ws->active_extensions[n]->callback(context,
349 			wsi->ws->active_extensions[n], wsi,
350 			r, wsi->ws->act_ext_user[n], v, len);
351 	}
352 
353 	return handled;
354 }
355 
356 int
lws_set_extension_option(struct lws * wsi,const char * ext_name,const char * opt_name,const char * opt_val)357 lws_set_extension_option(struct lws *wsi, const char *ext_name,
358 			 const char *opt_name, const char *opt_val)
359 {
360 	struct lws_ext_option_arg oa;
361 	int idx = 0;
362 
363 	if (!wsi->ws)
364 		return 0;
365 
366 	/* first identify if the ext is active on this wsi */
367 	while (idx < wsi->ws->count_act_ext &&
368 	       strcmp(wsi->ws->active_extensions[idx]->name, ext_name))
369 		idx++;
370 
371 	if (idx == wsi->ws->count_act_ext)
372 		return -1; /* request ext not active on this wsi */
373 
374 	oa.option_name = opt_name;
375 	oa.option_index = 0;
376 	oa.start = opt_val;
377 	oa.len = 0;
378 
379 	return wsi->ws->active_extensions[idx]->callback(wsi->context,
380 			wsi->ws->active_extensions[idx], wsi,
381 			LWS_EXT_CB_NAMED_OPTION_SET, wsi->ws->act_ext_user[idx],
382 			&oa, 0);
383 }
384