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