• 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 #define LWS_MAX_ELEM_NAME 32
28 
29 enum urldecode_stateful {
30 	US_NAME,
31 	US_IDLE,
32 	US_PC1,
33 	US_PC2,
34 
35 	MT_LOOK_BOUND_IN,
36 	MT_HNAME,
37 	MT_DISP,
38 	MT_TYPE,
39 	MT_IGNORE1,
40 	MT_IGNORE2,
41 	MT_IGNORE3,
42 	MT_COMPLETED,
43 };
44 
45 static struct mp_hdr {
46 	const char * const	hdr;
47 	uint8_t			hdr_len;
48 } mp_hdrs[] = {
49 	{ "content-disposition: ", 21 },
50 	{ "content-type: ", 14 },
51 	{ "\x0d\x0a", 2 }
52 };
53 
54 struct lws_spa;
55 
56 typedef int (*lws_urldecode_stateful_cb)(struct lws_spa *spa,
57 		const char *name, char **buf, int len, int final);
58 
59 struct lws_urldecode_stateful {
60 	char *out;
61 	struct lws_spa *data;
62 	struct lws *wsi;
63 	char name[LWS_MAX_ELEM_NAME];
64 	char temp[LWS_MAX_ELEM_NAME];
65 	char content_type[32];
66 	char content_disp[32];
67 	char content_disp_filename[256];
68 	char mime_boundary[128];
69 	int out_len;
70 	int pos;
71 	int hdr_idx;
72 	int mp;
73 	int sum;
74 
75 	uint8_t matchable;
76 
77 	uint8_t multipart_form_data:1;
78 	uint8_t inside_quote:1;
79 	uint8_t subname:1;
80 	uint8_t boundary_real_crlf:1;
81 
82 	enum urldecode_stateful state;
83 
84 	lws_urldecode_stateful_cb output;
85 };
86 
87 struct lws_spa {
88 	struct lws_urldecode_stateful *s;
89 	lws_spa_create_info_t i;
90 	int *param_length;
91 	char finalized;
92 	char **params;
93 	char *storage;
94 	char *end;
95 };
96 
97 static struct lws_urldecode_stateful *
lws_urldecode_s_create(struct lws_spa * spa,struct lws * wsi,char * out,int out_len,lws_urldecode_stateful_cb output)98 lws_urldecode_s_create(struct lws_spa *spa, struct lws *wsi, char *out,
99 		       int out_len, lws_urldecode_stateful_cb output)
100 {
101 	struct lws_urldecode_stateful *s;
102 	char buf[205], *p;
103 	int m = 0;
104 
105 	if (spa->i.ac)
106 		s = lwsac_use_zero(spa->i.ac, sizeof(*s), spa->i.ac_chunk_size);
107 	else
108 		s = lws_zalloc(sizeof(*s), "stateful urldecode");
109 
110 	if (!s)
111 		return NULL;
112 
113 	s->out = out;
114 	s->out_len  = out_len;
115 	s->output = output;
116 	s->pos = 0;
117 	s->sum = 0;
118 	s->mp = 0;
119 	s->state = US_NAME;
120 	s->name[0] = '\0';
121 	s->data = spa;
122 	s->wsi = wsi;
123 
124 	if (lws_hdr_copy(wsi, buf, sizeof(buf),
125 			 WSI_TOKEN_HTTP_CONTENT_TYPE) > 0) {
126 	/* multipart/form-data;
127 	 * boundary=----WebKitFormBoundarycc7YgAPEIHvgE9Bf */
128 
129 		if (!strncmp(buf, "multipart/form-data", 19) ||
130 		    !strncmp(buf, "multipart/related", 17)) {
131 			s->multipart_form_data = 1;
132 			s->state = MT_LOOK_BOUND_IN;
133 			s->mp = 2;
134 			p = strstr(buf, "boundary=");
135 			if (p) {
136 				p += 9;
137 				s->mime_boundary[m++] = '\x0d';
138 				s->mime_boundary[m++] = '\x0a';
139 				s->mime_boundary[m++] = '-';
140 				s->mime_boundary[m++] = '-';
141 				while (m < (int)sizeof(s->mime_boundary) - 1 &&
142 				       *p && *p != ' ' && *p != ';')
143 					s->mime_boundary[m++] = *p++;
144 				s->mime_boundary[m] = '\0';
145 
146 				lwsl_notice("boundary '%s'\n", s->mime_boundary);
147 			}
148 		}
149 	}
150 
151 	return s;
152 }
153 
154 static int
lws_urldecode_s_process(struct lws_urldecode_stateful * s,const char * in,int len)155 lws_urldecode_s_process(struct lws_urldecode_stateful *s, const char *in,
156 			int len)
157 {
158 	int n, hit;
159 	char c;
160 
161 	while (len--) {
162 		if (s->pos == s->out_len - s->mp - 1) {
163 			if (s->output(s->data, s->name, &s->out, s->pos,
164 				      LWS_UFS_CONTENT))
165 				return -1;
166 
167 			s->pos = 0;
168 		}
169 
170 		switch (s->state) {
171 
172 		/* states for url arg style */
173 
174 		case US_NAME:
175 			s->inside_quote = 0;
176 			if (*in == '=') {
177 				s->name[s->pos] = '\0';
178 				s->pos = 0;
179 				s->state = US_IDLE;
180 				in++;
181 				continue;
182 			}
183 			if (*in == '&') {
184 				s->name[s->pos] = '\0';
185 				if (s->output(s->data, s->name, &s->out,
186 					      s->pos, LWS_UFS_FINAL_CONTENT))
187 					return -1;
188 				s->pos = 0;
189 				s->state = US_IDLE;
190 				in++;
191 				continue;
192 			}
193 			if (s->pos >= (int)sizeof(s->name) - 1) {
194 				lwsl_hexdump_notice(s->name, s->pos);
195 				lwsl_notice("Name too long...\n");
196 				return -1;
197 			}
198 			s->name[s->pos++] = *in++;
199 			break;
200 		case US_IDLE:
201 			if (*in == '%') {
202 				s->state++;
203 				in++;
204 				continue;
205 			}
206 			if (*in == '&') {
207 				s->out[s->pos] = '\0';
208 				if (s->output(s->data, s->name, &s->out,
209 					      s->pos, LWS_UFS_FINAL_CONTENT))
210 					return -1;
211 				s->pos = 0;
212 				s->state = US_NAME;
213 				in++;
214 				continue;
215 			}
216 			if (*in == '+') {
217 				in++;
218 				s->out[s->pos++] = ' ';
219 				continue;
220 			}
221 			s->out[s->pos++] = *in++;
222 			break;
223 		case US_PC1:
224 			n = char_to_hex(*in);
225 			if (n < 0)
226 				return -1;
227 
228 			in++;
229 			s->sum = n << 4;
230 			s->state++;
231 			break;
232 
233 		case US_PC2:
234 			n = char_to_hex(*in);
235 			if (n < 0)
236 				return -1;
237 
238 			in++;
239 			s->out[s->pos++] = s->sum | n;
240 			s->state = US_IDLE;
241 			break;
242 
243 
244 		/* states for multipart / mime style */
245 
246 		case MT_LOOK_BOUND_IN:
247 retry_as_first:
248 			if (*in == s->mime_boundary[s->mp] &&
249 			    s->mime_boundary[s->mp]) {
250 				in++;
251 				s->mp++;
252 				if (!s->mime_boundary[s->mp]) {
253 					s->mp = 0;
254 					s->state = MT_IGNORE1;
255 
256 					if (s->output(s->data, s->name,
257 						      &s->out, s->pos,
258 						      LWS_UFS_FINAL_CONTENT))
259 						return -1;
260 
261 					s->pos = 0;
262 
263 					s->content_disp[0] = '\0';
264 					s->name[0] = '\0';
265 					s->content_disp_filename[0] = '\0';
266 					s->boundary_real_crlf = 1;
267 				}
268 				continue;
269 			}
270 			if (s->mp) {
271 				n = 0;
272 				if (!s->boundary_real_crlf)
273 					n = 2;
274 				if (s->mp >= n) {
275 					memcpy(s->out + s->pos,
276 					       s->mime_boundary + n, s->mp - n);
277 					s->pos += s->mp;
278 					s->mp = 0;
279 					goto retry_as_first;
280 				}
281 			}
282 
283 			s->out[s->pos++] = *in;
284 			in++;
285 			s->mp = 0;
286 			break;
287 
288 		case MT_HNAME:
289 			c =*in;
290 			if (c >= 'A' && c <= 'Z')
291 				c += 'a' - 'A';
292 			if (!s->mp)
293 				/* initially, any of them might match */
294 				s->matchable = (1 << LWS_ARRAY_SIZE(mp_hdrs)) - 1;
295 
296 			hit = -1;
297 			for (n = 0; n < (int)LWS_ARRAY_SIZE(mp_hdrs); n++) {
298 
299 				if (!(s->matchable & (1 << n)))
300 					continue;
301 				/* this guy is still in contention... */
302 
303 				if (s->mp >= mp_hdrs[n].hdr_len) {
304 					/* he went past the end of it */
305 					s->matchable &= ~(1 << n);
306 					continue;
307 				}
308 
309 				if (c != mp_hdrs[n].hdr[s->mp]) {
310 					/* mismatched a char */
311 					s->matchable &= ~(1 << n);
312 					continue;
313 				}
314 
315 				if (s->mp + 1 == mp_hdrs[n].hdr_len) {
316 					/* we have a winner... */
317 					hit = n;
318 					break;
319 				}
320 			}
321 
322 			in++;
323 			if (hit == -1 && !s->matchable) {
324 				/* We ruled them all out */
325 				s->state = MT_IGNORE1;
326 				s->mp = 0;
327 				continue;
328 			}
329 
330 			s->mp++;
331 			if (hit < 0)
332 				continue;
333 
334 			/* we matched the one in hit */
335 
336 			s->mp = 0;
337 			s->temp[0] = '\0';
338 			s->subname = 0;
339 
340 			if (hit == 2)
341 				s->state = MT_LOOK_BOUND_IN;
342 			else
343 				s->state += hit + 1;
344 			break;
345 
346 		case MT_DISP:
347 			/* form-data; name="file"; filename="t.txt" */
348 
349 			if (*in == '\x0d') {
350 				if (s->content_disp_filename[0])
351 					if (s->output(s->data, s->name,
352 						      &s->out, s->pos,
353 						      LWS_UFS_OPEN))
354 						return -1;
355 				s->state = MT_IGNORE2;
356 				goto done;
357 			}
358 			if (*in == ';') {
359 				s->subname = 1;
360 				s->temp[0] = '\0';
361 				s->mp = 0;
362 				goto done;
363 			}
364 
365 			if (*in == '\"') {
366 				s->inside_quote ^= 1;
367 				goto done;
368 			}
369 
370 			if (s->subname) {
371 				if (*in == '=') {
372 					s->temp[s->mp] = '\0';
373 					s->subname = 0;
374 					s->mp = 0;
375 					goto done;
376 				}
377 				if (s->mp < (int)sizeof(s->temp) - 1 &&
378 				    (*in != ' ' || s->inside_quote))
379 					s->temp[s->mp++] = *in;
380 				goto done;
381 			}
382 
383 			if (!s->temp[0]) {
384 				if (s->mp < (int)sizeof(s->content_disp) - 1)
385 					s->content_disp[s->mp++] = *in;
386 				if (s->mp < (int)sizeof(s->content_disp))
387 					s->content_disp[s->mp] = '\0';
388 				goto done;
389 			}
390 
391 			if (!strcmp(s->temp, "name")) {
392 				if (s->mp < (int)sizeof(s->name) - 1)
393 					s->name[s->mp++] = *in;
394 				else
395 					s->mp = (int)sizeof(s->name) - 1;
396 				s->name[s->mp] = '\0';
397 				goto done;
398 			}
399 
400 			if (!strcmp(s->temp, "filename")) {
401 				if (s->mp < (int)sizeof(s->content_disp_filename) - 1)
402 					s->content_disp_filename[s->mp++] = *in;
403 				s->content_disp_filename[s->mp] = '\0';
404 				goto done;
405 			}
406 done:
407 			in++;
408 			break;
409 
410 		case MT_TYPE:
411 			if (*in == '\x0d')
412 				s->state = MT_IGNORE2;
413 			else {
414 				if (s->mp < (int)sizeof(s->content_type) - 1)
415 					s->content_type[s->mp++] = *in;
416 				s->content_type[s->mp] = '\0';
417 			}
418 			in++;
419 			break;
420 
421 		case MT_IGNORE1:
422 			if (*in == '\x0d')
423 				s->state = MT_IGNORE2;
424 			if (*in == '-')
425 				s->state = MT_IGNORE3;
426 			in++;
427 			break;
428 
429 		case MT_IGNORE2:
430 			s->mp = 0;
431 			if (*in == '\x0a')
432 				s->state = MT_HNAME;
433 			in++;
434 			break;
435 
436 		case MT_IGNORE3:
437 			if (*in == '\x0d')
438 				s->state = MT_IGNORE1;
439 			if (*in == '-') {
440 				s->state = MT_COMPLETED;
441 				s->wsi->http.rx_content_remain = 0;
442 			}
443 			in++;
444 			break;
445 		case MT_COMPLETED:
446 			break;
447 		}
448 	}
449 
450 	return 0;
451 }
452 
453 static int
lws_urldecode_s_destroy(struct lws_spa * spa,struct lws_urldecode_stateful * s)454 lws_urldecode_s_destroy(struct lws_spa *spa, struct lws_urldecode_stateful *s)
455 {
456 	int ret = 0;
457 
458 	if (s->state != US_IDLE)
459 		ret = -1;
460 
461 	if (!ret)
462 		if (s->output(s->data, s->name, &s->out, s->pos,
463 			      LWS_UFS_FINAL_CONTENT))
464 			ret = -1;
465 
466 	if (s->output(s->data, s->name, NULL, 0, LWS_UFS_CLOSE))
467 		return -1;
468 
469 	if (!spa->i.ac)
470 		lws_free(s);
471 
472 	return ret;
473 }
474 
475 static int
lws_urldecode_spa_lookup(struct lws_spa * spa,const char * name)476 lws_urldecode_spa_lookup(struct lws_spa *spa, const char *name)
477 {
478 	const char * const *pp = spa->i.param_names;
479 	int n;
480 
481 	for (n = 0; n < spa->i.count_params; n++) {
482 		if (!strcmp(*pp, name))
483 			return n;
484 
485 		if (spa->i.param_names_stride)
486 			pp = (const char * const *)(((char *)pp) + spa->i.param_names_stride);
487 		else
488 			pp++;
489 	}
490 
491 	return -1;
492 }
493 
494 static int
lws_urldecode_spa_cb(struct lws_spa * spa,const char * name,char ** buf,int len,int final)495 lws_urldecode_spa_cb(struct lws_spa *spa, const char *name, char **buf, int len,
496 		     int final)
497 {
498 	int n;
499 
500 	if (final == LWS_UFS_CLOSE || spa->s->content_disp_filename[0]) {
501 		if (spa->i.opt_cb) {
502 			n = spa->i.opt_cb(spa->i.opt_data, name,
503 					spa->s->content_disp_filename,
504 					buf ? *buf : NULL, len, final);
505 
506 			if (n < 0)
507 				return -1;
508 		}
509 		return 0;
510 	}
511 	n = lws_urldecode_spa_lookup(spa, name);
512 	if (n == -1 || !len) /* unrecognized */
513 		return 0;
514 
515 	if (!spa->i.ac) {
516 		if (!spa->params[n])
517 			spa->params[n] = *buf;
518 
519 		if ((*buf) + len >= spa->end) {
520 			lwsl_info("%s: exceeded storage\n", __func__);
521 			return -1;
522 		}
523 
524 		/* move it on inside storage */
525 		(*buf) += len;
526 		*((*buf)++) = '\0';
527 
528 		spa->s->out_len -= len + 1;
529 	} else {
530 		spa->params[n] = lwsac_use(spa->i.ac, len + 1,
531 					   spa->i.ac_chunk_size);
532 		if (!spa->params[n])
533 			return -1;
534 
535 		memcpy(spa->params[n], *buf, len);
536 		spa->params[n][len] = '\0';
537 	}
538 
539 	spa->param_length[n] += len;
540 
541 	return 0;
542 }
543 
544 struct lws_spa *
lws_spa_create_via_info(struct lws * wsi,const lws_spa_create_info_t * i)545 lws_spa_create_via_info(struct lws *wsi, const lws_spa_create_info_t *i)
546 {
547 	struct lws_spa *spa;
548 
549 	if (i->ac)
550 		spa = lwsac_use_zero(i->ac, sizeof(*spa), i->ac_chunk_size);
551 	else
552 		spa = lws_zalloc(sizeof(*spa), "spa");
553 
554 	if (!spa)
555 		return NULL;
556 
557 	spa->i = *i;
558 	if (!spa->i.max_storage)
559 		spa->i.max_storage = 512;
560 
561 	if (i->ac)
562 		spa->storage = lwsac_use(i->ac, spa->i.max_storage,
563 					 i->ac_chunk_size);
564 	else
565 		spa->storage = lws_malloc(spa->i.max_storage, "spa");
566 
567 	if (!spa->storage)
568 		goto bail2;
569 
570 	spa->end = spa->storage + i->max_storage - 1;
571 
572 	if (i->count_params) {
573 		if (i->ac)
574 			spa->params = lwsac_use_zero(i->ac,
575 				sizeof(char *) * i->count_params, i->ac_chunk_size);
576 		else
577 			spa->params = lws_zalloc(sizeof(char *) * i->count_params,
578 					 "spa params");
579 		if (!spa->params)
580 			goto bail3;
581 	}
582 
583 	spa->s = lws_urldecode_s_create(spa, wsi, spa->storage, i->max_storage,
584 					lws_urldecode_spa_cb);
585 	if (!spa->s)
586 		goto bail4;
587 
588 	if (i->count_params) {
589 		if (i->ac)
590 			spa->param_length = lwsac_use_zero(i->ac,
591 				sizeof(int) * i->count_params, i->ac_chunk_size);
592 		else
593 			spa->param_length = lws_zalloc(sizeof(int) * i->count_params,
594 						"spa param len");
595 		if (!spa->param_length)
596 			goto bail5;
597 	}
598 
599 	lwsl_notice("%s: Created SPA %p\n", __func__, spa);
600 
601 	return spa;
602 
603 bail5:
604 	lws_urldecode_s_destroy(spa, spa->s);
605 bail4:
606 	if (!i->ac)
607 		lws_free(spa->params);
608 bail3:
609 	if (!i->ac)
610 		lws_free(spa->storage);
611 bail2:
612 	if (!i->ac)
613 		lws_free(spa);
614 
615 	if (i->ac)
616 		lwsac_free(i->ac);
617 
618 	return NULL;
619 }
620 
621 struct lws_spa *
lws_spa_create(struct lws * wsi,const char * const * param_names,int count_params,int max_storage,lws_spa_fileupload_cb opt_cb,void * opt_data)622 lws_spa_create(struct lws *wsi, const char * const *param_names,
623 	       int count_params, int max_storage,
624 	       lws_spa_fileupload_cb opt_cb, void *opt_data)
625 {
626 	lws_spa_create_info_t i;
627 
628 	memset(&i, 0, sizeof(i));
629 	i.count_params = count_params;
630 	i.max_storage = max_storage;
631 	i.opt_cb = opt_cb;
632 	i.opt_data = opt_data;
633 	i.param_names = param_names;
634 
635 	return lws_spa_create_via_info(wsi, &i);
636 }
637 
638 int
lws_spa_process(struct lws_spa * spa,const char * in,int len)639 lws_spa_process(struct lws_spa *spa, const char *in, int len)
640 {
641 	if (!spa) {
642 		lwsl_err("%s: NULL spa\n", __func__);
643 		return -1;
644 	}
645 	/* we reject any junk after the last part arrived and we finalized */
646 	if (spa->finalized)
647 		return 0;
648 
649 	return lws_urldecode_s_process(spa->s, in, len);
650 }
651 
652 int
lws_spa_get_length(struct lws_spa * spa,int n)653 lws_spa_get_length(struct lws_spa *spa, int n)
654 {
655 	if (n >= spa->i.count_params)
656 		return 0;
657 
658 	return spa->param_length[n];
659 }
660 
661 const char *
lws_spa_get_string(struct lws_spa * spa,int n)662 lws_spa_get_string(struct lws_spa *spa, int n)
663 {
664 	if (n >= spa->i.count_params)
665 		return NULL;
666 
667 	return spa->params[n];
668 }
669 
670 int
lws_spa_finalize(struct lws_spa * spa)671 lws_spa_finalize(struct lws_spa *spa)
672 {
673 	if (!spa)
674 		return 0;
675 
676 	if (spa->s) {
677 		lws_urldecode_s_destroy(spa, spa->s);
678 		spa->s = NULL;
679 	}
680 
681 	spa->finalized = 1;
682 
683 	return 0;
684 }
685 
686 int
lws_spa_destroy(struct lws_spa * spa)687 lws_spa_destroy(struct lws_spa *spa)
688 {
689 	int n = 0;
690 
691 	lwsl_info("%s: destroy spa %p\n", __func__, spa);
692 
693 	if (spa->s)
694 		lws_urldecode_s_destroy(spa, spa->s);
695 
696 	if (spa->i.ac)
697 		lwsac_free(spa->i.ac);
698 	else {
699 		lws_free(spa->param_length);
700 		lws_free(spa->params);
701 		lws_free(spa->storage);
702 		lws_free(spa);
703 	}
704 
705 	return n;
706 }
707