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