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