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 const char * const method_names[] = {
28 "GET", "POST",
29 #if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
30 "OPTIONS", "PUT", "PATCH", "DELETE",
31 #endif
32 "CONNECT", "HEAD",
33 #ifdef LWS_WITH_HTTP2
34 ":path",
35 #endif
36 };
37
38 #if defined(LWS_WITH_FILE_OPS)
39 static const char * const intermediates[] = { "private", "public" };
40 #endif
41
42 /*
43 * return 0: all done
44 * 1: nonfatal error
45 * <0: fatal error
46 *
47 * REQUIRES CONTEXT LOCK HELD
48 */
49
50 #if defined(LWS_WITH_SERVER)
51 int
_lws_vhost_init_server(const struct lws_context_creation_info * info,struct lws_vhost * vhost)52 _lws_vhost_init_server(const struct lws_context_creation_info *info,
53 struct lws_vhost *vhost)
54 {
55 int n, opt = 1, limit = 1;
56 lws_sockfd_type sockfd;
57 struct lws_vhost *vh;
58 struct lws *wsi;
59 int m = 0, is;
60
61 (void)method_names;
62 (void)opt;
63
64 if (info) {
65 vhost->iface = info->iface;
66 vhost->listen_port = info->port;
67 }
68
69 /* set up our external listening socket we serve on */
70
71 if (vhost->listen_port == CONTEXT_PORT_NO_LISTEN ||
72 vhost->listen_port == CONTEXT_PORT_NO_LISTEN_SERVER)
73 return 0;
74
75 vh = vhost->context->vhost_list;
76 while (vh) {
77 if (vh->listen_port == vhost->listen_port) {
78 if (((!vhost->iface && !vh->iface) ||
79 (vhost->iface && vh->iface &&
80 !strcmp(vhost->iface, vh->iface))) &&
81 vh->lserv_wsi
82 ) {
83 lwsl_notice(" using listen skt from vhost %s\n",
84 vh->name);
85 return 0;
86 }
87 }
88 vh = vh->vhost_next;
89 }
90
91 if (vhost->iface) {
92 /*
93 * let's check before we do anything else about the disposition
94 * of the interface he wants to bind to...
95 */
96 is = lws_socket_bind(vhost, LWS_SOCK_INVALID, vhost->listen_port,
97 vhost->iface, 1);
98 lwsl_debug("initial if check says %d\n", is);
99
100 if (is == LWS_ITOSA_BUSY)
101 /* treat as fatal */
102 return -1;
103
104 deal:
105
106 lws_start_foreach_llp(struct lws_vhost **, pv,
107 vhost->context->no_listener_vhost_list) {
108 if (is >= LWS_ITOSA_USABLE && *pv == vhost) {
109 /* on the list and shouldn't be: remove it */
110 lwsl_debug("deferred iface: removing vh %s\n",
111 (*pv)->name);
112 *pv = vhost->no_listener_vhost_list;
113 vhost->no_listener_vhost_list = NULL;
114 goto done_list;
115 }
116 if (is < LWS_ITOSA_USABLE && *pv == vhost)
117 goto done_list;
118 } lws_end_foreach_llp(pv, no_listener_vhost_list);
119
120 /* not on the list... */
121
122 if (is < LWS_ITOSA_USABLE) {
123
124 /* ... but needs to be: so add it */
125
126 lwsl_debug("deferred iface: adding vh %s\n", vhost->name);
127 vhost->no_listener_vhost_list =
128 vhost->context->no_listener_vhost_list;
129 vhost->context->no_listener_vhost_list = vhost;
130 }
131
132 done_list:
133
134 switch (is) {
135 default:
136 break;
137 case LWS_ITOSA_NOT_EXIST:
138 /* can't add it */
139 if (info) /* first time */
140 lwsl_err("VH %s: iface %s port %d DOESN'T EXIST\n",
141 vhost->name, vhost->iface, vhost->listen_port);
142 else
143 return -1;
144 return (info->options & LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND) ==
145 LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND?
146 -1 : 1;
147 case LWS_ITOSA_NOT_USABLE:
148 /* can't add it */
149 if (info) /* first time */
150 lwsl_err("VH %s: iface %s port %d NOT USABLE\n",
151 vhost->name, vhost->iface, vhost->listen_port);
152 else
153 return -1;
154 return (info->options & LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND) ==
155 LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND?
156 -1 : 1;
157 }
158 }
159
160 (void)n;
161 #if defined(__linux__)
162 #ifdef LWS_WITH_UNIX_SOCK
163 /*
164 * A Unix domain sockets cannot be bound for several times, even if we set
165 * the SO_REUSE* options on.
166 * However, fortunately, each thread is able to independently listen when
167 * running on a reasonably new Linux kernel. So we can safely assume
168 * creating just one listening socket for a multi-threaded environment won't
169 * fail in most cases.
170 */
171 if (!LWS_UNIX_SOCK_ENABLED(vhost))
172 #endif
173 limit = vhost->context->count_threads;
174 #endif
175
176 for (m = 0; m < limit; m++) {
177 #ifdef LWS_WITH_UNIX_SOCK
178 if (LWS_UNIX_SOCK_ENABLED(vhost))
179 sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
180 else
181 #endif
182 #ifdef LWS_WITH_IPV6
183 if (LWS_IPV6_ENABLED(vhost))
184 sockfd = socket(AF_INET6, SOCK_STREAM, 0);
185 else
186 #endif
187 sockfd = socket(AF_INET, SOCK_STREAM, 0);
188
189 if (sockfd == LWS_SOCK_INVALID) {
190 lwsl_err("ERROR opening socket\n");
191 return 1;
192 }
193 #if !defined(LWS_PLAT_FREERTOS)
194 #if (defined(WIN32) || defined(_WIN32)) && defined(SO_EXCLUSIVEADDRUSE)
195 /*
196 * only accept that we are the only listener on the port
197 * https://msdn.microsoft.com/zh-tw/library/
198 * windows/desktop/ms740621(v=vs.85).aspx
199 *
200 * for lws, to match Linux, we default to exclusive listen
201 */
202 if (!lws_check_opt(vhost->options,
203 LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE)) {
204 if (setsockopt(sockfd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
205 (const void *)&opt, sizeof(opt)) < 0) {
206 lwsl_err("reuseaddr failed\n");
207 compatible_close(sockfd);
208 return -1;
209 }
210 } else
211 #endif
212
213 /*
214 * allow us to restart even if old sockets in TIME_WAIT
215 */
216 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
217 (const void *)&opt, sizeof(opt)) < 0) {
218 lwsl_err("reuseaddr failed\n");
219 compatible_close(sockfd);
220 return -1;
221 }
222
223 #if defined(LWS_WITH_IPV6) && defined(IPV6_V6ONLY)
224 if (LWS_IPV6_ENABLED(vhost) &&
225 vhost->options & LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY) {
226 int value = (vhost->options &
227 LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE) ? 1 : 0;
228 if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
229 (const void*)&value, sizeof(value)) < 0) {
230 compatible_close(sockfd);
231 return -1;
232 }
233 }
234 #endif
235
236 #if defined(__linux__) && defined(SO_REUSEPORT)
237 /* keep coverity happy */
238 #if LWS_MAX_SMP > 1
239 n = 1;
240 #else
241 n = lws_check_opt(vhost->options,
242 LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE);
243 #endif
244 if (n && vhost->context->count_threads > 1)
245 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT,
246 (const void *)&opt, sizeof(opt)) < 0) {
247 compatible_close(sockfd);
248 return -1;
249 }
250 #endif
251 #endif
252 lws_plat_set_socket_options(vhost, sockfd, 0);
253
254 is = lws_socket_bind(vhost, sockfd, vhost->listen_port,
255 vhost->iface, 1);
256 if (is == LWS_ITOSA_BUSY) {
257 /* treat as fatal */
258 compatible_close(sockfd);
259
260 return -1;
261 }
262
263 /*
264 * There is a race where the network device may come up and then
265 * go away and fail here. So correctly handle unexpected failure
266 * here despite we earlier confirmed it.
267 */
268 if (is < 0) {
269 lwsl_info("%s: lws_socket_bind says %d\n", __func__, is);
270 compatible_close(sockfd);
271 goto deal;
272 }
273
274 wsi = lws_zalloc(sizeof(struct lws), "listen wsi");
275 if (wsi == NULL) {
276 lwsl_err("Out of mem\n");
277 goto bail;
278 }
279
280 #ifdef LWS_WITH_UNIX_SOCK
281 if (!LWS_UNIX_SOCK_ENABLED(vhost))
282 #endif
283 {
284 wsi->unix_skt = 1;
285 vhost->listen_port = is;
286
287 lwsl_debug("%s: lws_socket_bind says %d\n", __func__, is);
288 }
289
290 wsi->context = vhost->context;
291 wsi->desc.sockfd = sockfd;
292 lws_role_transition(wsi, 0, LRS_UNCONNECTED, &role_ops_listen);
293 wsi->protocol = vhost->protocols;
294 wsi->tsi = m;
295 lws_vhost_bind_wsi(vhost, wsi);
296 wsi->listener = 1;
297
298 if (wsi->context->event_loop_ops->init_vhost_listen_wsi)
299 wsi->context->event_loop_ops->init_vhost_listen_wsi(wsi);
300
301 if (__insert_wsi_socket_into_fds(vhost->context, wsi)) {
302 lwsl_notice("inserting wsi socket into fds failed\n");
303 goto bail;
304 }
305
306 vhost->context->count_wsi_allocated++;
307 vhost->lserv_wsi = wsi;
308
309 n = listen(wsi->desc.sockfd, LWS_SOMAXCONN);
310 if (n < 0) {
311 lwsl_err("listen failed with error %d\n", LWS_ERRNO);
312 vhost->lserv_wsi = NULL;
313 vhost->context->count_wsi_allocated--;
314 __remove_wsi_socket_from_fds(wsi);
315 goto bail;
316 }
317 } /* for each thread able to independently listen */
318
319 if (!lws_check_opt(vhost->context->options,
320 LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) {
321 #ifdef LWS_WITH_UNIX_SOCK
322 if (LWS_UNIX_SOCK_ENABLED(vhost))
323 lwsl_info(" Listening on \"%s\"\n", vhost->iface);
324 else
325 #endif
326 lwsl_info(" Listening on port %d\n", vhost->listen_port);
327 }
328
329 // info->port = vhost->listen_port;
330
331 return 0;
332
333 bail:
334 compatible_close(sockfd);
335
336 return -1;
337 }
338 #endif
339
340 struct lws_vhost *
lws_select_vhost(struct lws_context * context,int port,const char * servername)341 lws_select_vhost(struct lws_context *context, int port, const char *servername)
342 {
343 struct lws_vhost *vhost = context->vhost_list;
344 const char *p;
345 int n, colon;
346
347 n = (int)strlen(servername);
348 colon = n;
349 p = strchr(servername, ':');
350 if (p)
351 colon = lws_ptr_diff(p, servername);
352
353 /* Priotity 1: first try exact matches */
354
355 while (vhost) {
356 if (port == vhost->listen_port &&
357 !strncmp(vhost->name, servername, colon)) {
358 lwsl_info("SNI: Found: %s\n", servername);
359 return vhost;
360 }
361 vhost = vhost->vhost_next;
362 }
363
364 /*
365 * Priority 2: if no exact matches, try matching *.vhost-name
366 * unintentional matches are possible but resolve to x.com for *.x.com
367 * which is reasonable. If exact match exists we already chose it and
368 * never reach here. SSL will still fail it if the cert doesn't allow
369 * *.x.com.
370 */
371 vhost = context->vhost_list;
372 while (vhost) {
373 int m = (int)strlen(vhost->name);
374 if (port && port == vhost->listen_port &&
375 m <= (colon - 2) &&
376 servername[colon - m - 1] == '.' &&
377 !strncmp(vhost->name, servername + colon - m, m)) {
378 lwsl_info("SNI: Found %s on wildcard: %s\n",
379 servername, vhost->name);
380 return vhost;
381 }
382 vhost = vhost->vhost_next;
383 }
384
385 /* Priority 3: match the first vhost on our port */
386
387 vhost = context->vhost_list;
388 while (vhost) {
389 if (port && port == vhost->listen_port) {
390 lwsl_info("%s: vhost match to %s based on port %d\n",
391 __func__, vhost->name, port);
392 return vhost;
393 }
394 vhost = vhost->vhost_next;
395 }
396
397 /* no match */
398
399 return NULL;
400 }
401
402 static const struct lws_mimetype {
403 const char *extension;
404 const char *mimetype;
405 } server_mimetypes[] = {
406 { ".html", "text/html" },
407 { ".htm", "text/html" },
408 { ".js", "text/javascript" },
409 { ".css", "text/css" },
410 { ".png", "image/png" },
411 { ".jpg", "image/jpeg" },
412 { ".jpeg", "image/jpeg" },
413 { ".ico", "image/x-icon" },
414 { ".gif", "image/gif" },
415 { ".svg", "image/svg+xml" },
416 { ".ttf", "application/x-font-ttf" },
417 { ".otf", "application/font-woff" },
418 { ".woff", "application/font-woff" },
419 { ".woff2", "application/font-woff2" },
420 { ".gz", "application/gzip" },
421 { ".txt", "text/plain" },
422 { ".xml", "application/xml" },
423 { ".json", "application/json" },
424 };
425
426 const char *
lws_get_mimetype(const char * file,const struct lws_http_mount * m)427 lws_get_mimetype(const char *file, const struct lws_http_mount *m)
428 {
429 const struct lws_protocol_vhost_options *pvo;
430 size_t n = strlen(file), len, i;
431 const char *fallback_mimetype = NULL;
432 const struct lws_mimetype *mt;
433
434 /* prioritize user-defined mimetypes */
435 for (pvo = m ? m->extra_mimetypes : NULL; pvo; pvo = pvo->next) {
436 /* ie, match anything */
437 if (!fallback_mimetype && pvo->name[0] == '*') {
438 fallback_mimetype = pvo->value;
439 continue;
440 }
441
442 len = strlen(pvo->name);
443 if (n > len && !strcasecmp(&file[n - len], pvo->name)) {
444 lwsl_info("%s: match to user mimetype: %s\n", __func__,
445 pvo->value);
446 return pvo->value;
447 }
448 }
449
450 /* fallback to server-defined mimetypes */
451 for (i = 0; i < LWS_ARRAY_SIZE(server_mimetypes); ++i) {
452 mt = &server_mimetypes[i];
453
454 len = strlen(mt->extension);
455 if (n > len && !strcasecmp(&file[n - len], mt->extension)) {
456 lwsl_info("%s: match to server mimetype: %s\n", __func__,
457 mt->mimetype);
458 return mt->mimetype;
459 }
460 }
461
462 /* fallback to '*' if defined */
463 if (fallback_mimetype) {
464 lwsl_info("%s: match to any mimetype: %s\n", __func__,
465 fallback_mimetype);
466 return fallback_mimetype;
467 }
468
469 return NULL;
470 }
471
472 #if defined(LWS_WITH_FILE_OPS)
473 static lws_fop_flags_t
lws_vfs_prepare_flags(struct lws * wsi)474 lws_vfs_prepare_flags(struct lws *wsi)
475 {
476 lws_fop_flags_t f = 0;
477
478 if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING))
479 return f;
480
481 if (strstr(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING),
482 "gzip")) {
483 lwsl_info("client indicates GZIP is acceptable\n");
484 f |= LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP;
485 }
486
487 return f;
488 }
489
490 static int
lws_http_serve(struct lws * wsi,char * uri,const char * origin,const struct lws_http_mount * m)491 lws_http_serve(struct lws *wsi, char *uri, const char *origin,
492 const struct lws_http_mount *m)
493 {
494 const struct lws_protocol_vhost_options *pvo = m->interpret;
495 struct lws_process_html_args args;
496 const char *mimetype;
497 #if !defined(_WIN32_WCE)
498 const struct lws_plat_file_ops *fops;
499 const char *vpath;
500 lws_fop_flags_t fflags = LWS_O_RDONLY;
501 #if defined(WIN32) && defined(LWS_HAVE__STAT32I64)
502 struct _stat32i64 st;
503 #else
504 struct stat st;
505 #endif
506 int spin = 0;
507 #endif
508 char path[256], sym[2048];
509 unsigned char *p = (unsigned char *)sym + 32 + LWS_PRE, *start = p;
510 unsigned char *end = p + sizeof(sym) - 32 - LWS_PRE;
511 #if !defined(WIN32) && !defined(LWS_PLAT_FREERTOS)
512 size_t len;
513 #endif
514 int n;
515
516 wsi->handling_404 = 0;
517 if (!wsi->vhost)
518 return -1;
519
520 #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
521 if (wsi->vhost->http.error_document_404 &&
522 !strcmp(uri, wsi->vhost->http.error_document_404))
523 wsi->handling_404 = 1;
524 #endif
525
526 lws_snprintf(path, sizeof(path) - 1, "%s/%s", origin, uri);
527
528 #if !defined(_WIN32_WCE)
529
530 fflags |= lws_vfs_prepare_flags(wsi);
531
532 do {
533 spin++;
534 fops = lws_vfs_select_fops(wsi->context->fops, path, &vpath);
535
536 if (wsi->http.fop_fd)
537 lws_vfs_file_close(&wsi->http.fop_fd);
538
539 wsi->http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops,
540 path, vpath, &fflags);
541 if (!wsi->http.fop_fd) {
542 lwsl_info("%s: Unable to open '%s': errno %d\n",
543 __func__, path, errno);
544
545 return 1;
546 }
547
548 /* if it can't be statted, don't try */
549 if (fflags & LWS_FOP_FLAG_VIRTUAL)
550 break;
551 #if defined(LWS_PLAT_FREERTOS)
552 break;
553 #endif
554 #if !defined(WIN32)
555 if (fstat(wsi->http.fop_fd->fd, &st)) {
556 lwsl_info("unable to stat %s\n", path);
557 goto notfound;
558 }
559 #else
560 #if defined(LWS_HAVE__STAT32I64)
561 if (_stat32i64(path, &st)) {
562 lwsl_info("unable to stat %s\n", path);
563 goto notfound;
564 }
565 #else
566 if (stat(path, &st)) {
567 lwsl_info("unable to stat %s\n", path);
568 goto notfound;
569 }
570 #endif
571 #endif
572
573 wsi->http.fop_fd->mod_time = (uint32_t)st.st_mtime;
574 fflags |= LWS_FOP_FLAG_MOD_TIME_VALID;
575
576 #if !defined(WIN32) && !defined(LWS_PLAT_FREERTOS)
577 if ((S_IFMT & st.st_mode) == S_IFLNK) {
578 len = readlink(path, sym, sizeof(sym) - 1);
579 if (len) {
580 lwsl_err("Failed to read link %s\n", path);
581 goto notfound;
582 }
583 sym[len] = '\0';
584 lwsl_debug("symlink %s -> %s\n", path, sym);
585 lws_snprintf(path, sizeof(path) - 1, "%s", sym);
586 }
587 #endif
588 if ((S_IFMT & st.st_mode) == S_IFDIR) {
589 lwsl_debug("default filename append to dir\n");
590 lws_snprintf(path, sizeof(path) - 1, "%s/%s/%s",
591 origin, uri, m->def ? m->def : "index.html");
592 }
593
594 } while ((S_IFMT & st.st_mode) != S_IFREG && spin < 5);
595
596 if (spin == 5)
597 lwsl_err("symlink loop %s \n", path);
598
599 n = sprintf(sym, "%08llX%08lX",
600 (unsigned long long)lws_vfs_get_length(wsi->http.fop_fd),
601 (unsigned long)lws_vfs_get_mod_time(wsi->http.fop_fd));
602
603 /* disable ranges if IF_RANGE token invalid */
604
605 if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_RANGE))
606 if (strcmp(sym, lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_IF_RANGE)))
607 /* differs - defeat Range: */
608 wsi->http.ah->frag_index[WSI_TOKEN_HTTP_RANGE] = 0;
609
610 if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_NONE_MATCH)) {
611 /*
612 * he thinks he has some version of it already,
613 * check if the tag matches
614 */
615 if (!strcmp(sym, lws_hdr_simple_ptr(wsi,
616 WSI_TOKEN_HTTP_IF_NONE_MATCH))) {
617
618 char cache_control[50], *cc = "no-store";
619 int cclen = 8;
620
621 lwsl_debug("%s: ETAG match %s %s\n", __func__,
622 uri, origin);
623
624 /* we don't need to send the payload */
625 if (lws_add_http_header_status(wsi,
626 HTTP_STATUS_NOT_MODIFIED, &p, end)) {
627 lwsl_err("%s: failed adding not modified\n",
628 __func__);
629 return -1;
630 }
631
632 if (lws_add_http_header_by_token(wsi,
633 WSI_TOKEN_HTTP_ETAG,
634 (unsigned char *)sym, n, &p, end))
635 return -1;
636
637 /* but we still need to send cache control... */
638
639 if (m->cache_max_age && m->cache_reusable) {
640 if (!m->cache_revalidate) {
641 cc = cache_control;
642 cclen = sprintf(cache_control,
643 "%s, max-age=%u",
644 intermediates[wsi->cache_intermediaries],
645 m->cache_max_age);
646 } else {
647 cc = cache_control;
648 cclen = sprintf(cache_control,
649 "must-revalidate, %s, max-age=%u",
650 intermediates[wsi->cache_intermediaries],
651 m->cache_max_age);
652 }
653 }
654
655 if (lws_add_http_header_by_token(wsi,
656 WSI_TOKEN_HTTP_CACHE_CONTROL,
657 (unsigned char *)cc, cclen, &p, end))
658 return -1;
659
660 if (lws_finalize_http_header(wsi, &p, end))
661 return -1;
662
663 n = lws_write(wsi, start, p - start,
664 LWS_WRITE_HTTP_HEADERS |
665 LWS_WRITE_H2_STREAM_END);
666 if (n != (p - start)) {
667 lwsl_err("_write returned %d from %ld\n", n,
668 (long)(p - start));
669 return -1;
670 }
671
672 lws_vfs_file_close(&wsi->http.fop_fd);
673
674 if (lws_http_transaction_completed(wsi))
675 return -1;
676
677 return 0;
678 }
679 }
680
681 if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ETAG,
682 (unsigned char *)sym, n, &p, end))
683 return -1;
684 #endif
685
686 mimetype = lws_get_mimetype(path, m);
687 if (!mimetype) {
688 lwsl_info("unknown mimetype for %s\n", path);
689 if (lws_return_http_status(wsi,
690 HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL) ||
691 lws_http_transaction_completed(wsi))
692 return -1;
693
694 return 0;
695 }
696 if (!mimetype[0])
697 lwsl_debug("sending no mimetype for %s\n", path);
698
699 wsi->sending_chunked = 0;
700 wsi->interpreting = 0;
701
702 /*
703 * check if this is in the list of file suffixes to be interpreted by
704 * a protocol
705 */
706 while (pvo) {
707 n = (int)strlen(path);
708 if (n > (int)strlen(pvo->name) &&
709 !strcmp(&path[n - strlen(pvo->name)], pvo->name)) {
710 wsi->interpreting = 1;
711 if (!wsi->mux_substream)
712 wsi->sending_chunked = 1;
713
714 wsi->protocol_interpret_idx = (char)(
715 lws_vhost_name_to_protocol(wsi->vhost,
716 pvo->value) -
717 &lws_get_vhost(wsi)->protocols[0]);
718
719 lwsl_debug("want %s interpreted by %s (pcol is %s)\n", path,
720 wsi->vhost->protocols[
721 (int)wsi->protocol_interpret_idx].name,
722 wsi->protocol->name);
723 if (lws_bind_protocol(wsi, &wsi->vhost->protocols[
724 (int)wsi->protocol_interpret_idx], __func__))
725 return -1;
726
727 if (lws_ensure_user_space(wsi))
728 return -1;
729 break;
730 }
731 pvo = pvo->next;
732 }
733
734 if (wsi->sending_chunked) {
735 if (lws_add_http_header_by_token(wsi,
736 WSI_TOKEN_HTTP_TRANSFER_ENCODING,
737 (unsigned char *)"chunked", 7,
738 &p, end))
739 return -1;
740 }
741
742 if (m->protocol) {
743 const struct lws_protocols *pp = lws_vhost_name_to_protocol(
744 wsi->vhost, m->protocol);
745
746 if (lws_bind_protocol(wsi, pp, __func__))
747 return -1;
748 args.p = (char *)p;
749 args.max_len = lws_ptr_diff(end, p);
750 if (pp->callback(wsi, LWS_CALLBACK_ADD_HEADERS,
751 wsi->user_space, &args, 0))
752 return -1;
753 p = (unsigned char *)args.p;
754 }
755
756 *p = '\0';
757 n = lws_serve_http_file(wsi, path, mimetype, (char *)start,
758 lws_ptr_diff(p, start));
759
760 if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi)))
761 return -1; /* error or can't reuse connection: close the socket */
762
763 return 0;
764
765 notfound:
766
767 return 1;
768 }
769 #endif
770
771 #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
772 const struct lws_http_mount *
lws_find_mount(struct lws * wsi,const char * uri_ptr,int uri_len)773 lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len)
774 {
775 const struct lws_http_mount *hm, *hit = NULL;
776 int best = 0;
777
778 hm = wsi->vhost->http.mount_list;
779 while (hm) {
780 if (uri_len >= hm->mountpoint_len &&
781 !strncmp(uri_ptr, hm->mountpoint, hm->mountpoint_len) &&
782 (uri_ptr[hm->mountpoint_len] == '\0' ||
783 uri_ptr[hm->mountpoint_len] == '/' ||
784 hm->mountpoint_len == 1)
785 ) {
786 if (hm->origin_protocol == LWSMPRO_CALLBACK ||
787 ((hm->origin_protocol == LWSMPRO_CGI ||
788 lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) ||
789 lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) ||
790 lws_hdr_total_length(wsi, WSI_TOKEN_HEAD_URI) ||
791 #if defined(LWS_ROLE_H2)
792 (wsi->mux_substream &&
793 lws_hdr_total_length(wsi,
794 WSI_TOKEN_HTTP_COLON_PATH)) ||
795 #endif
796 hm->protocol) &&
797 hm->mountpoint_len > best)) {
798 best = hm->mountpoint_len;
799 hit = hm;
800 }
801 }
802 hm = hm->mount_next;
803 }
804
805 return hit;
806 }
807 #endif
808
809 #if defined(LWS_WITH_HTTP_BASIC_AUTH) && !defined(LWS_PLAT_FREERTOS) && defined(LWS_WITH_FILE_OPS)
810 static int
lws_find_string_in_file(const char * filename,const char * string,int stringlen)811 lws_find_string_in_file(const char *filename, const char *string, int stringlen)
812 {
813 char buf[128];
814 int fd, match = 0, pos = 0, n = 0, hit = 0;
815
816 fd = lws_open(filename, O_RDONLY);
817 if (fd < 0) {
818 lwsl_err("can't open auth file: %s\n", filename);
819 return 0;
820 }
821
822 while (1) {
823 if (pos == n) {
824 n = read(fd, buf, sizeof(buf));
825 if (n <= 0) {
826 if (match == stringlen)
827 hit = 1;
828 break;
829 }
830 pos = 0;
831 }
832
833 if (match == stringlen) {
834 if (buf[pos] == '\r' || buf[pos] == '\n') {
835 hit = 1;
836 break;
837 }
838 match = 0;
839 }
840
841 if (buf[pos] == string[match])
842 match++;
843 else
844 match = 0;
845
846 pos++;
847 }
848
849 close(fd);
850
851 return hit;
852 }
853 #endif
854
855 #if defined(LWS_WITH_HTTP_BASIC_AUTH)
856
857 int
lws_unauthorised_basic_auth(struct lws * wsi)858 lws_unauthorised_basic_auth(struct lws *wsi)
859 {
860 struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
861 unsigned char *start = pt->serv_buf + LWS_PRE,
862 *p = start, *end = p + 2048;
863 char buf[64];
864 int n;
865
866 /* no auth... tell him it is required */
867
868 if (lws_add_http_header_status(wsi, HTTP_STATUS_UNAUTHORIZED, &p, end))
869 return -1;
870
871 n = lws_snprintf(buf, sizeof(buf), "Basic realm=\"lwsws\"");
872 if (lws_add_http_header_by_token(wsi,
873 WSI_TOKEN_HTTP_WWW_AUTHENTICATE,
874 (unsigned char *)buf, n, &p, end))
875 return -1;
876
877 if (lws_add_http_header_content_length(wsi, 0, &p, end))
878 return -1;
879
880 if (lws_finalize_http_header(wsi, &p, end))
881 return -1;
882
883 n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS |
884 LWS_WRITE_H2_STREAM_END);
885 if (n < 0)
886 return -1;
887
888 return lws_http_transaction_completed(wsi);
889
890 }
891
892 #endif
893
lws_clean_url(char * p)894 int lws_clean_url(char *p)
895 {
896 if (p[0] == 'h' && p[1] == 't' && p[2] == 't' && p[3] == 'p') {
897 p += 4;
898 if (*p == 's')
899 p++;
900 if (*p == ':') {
901 p++;
902 if (*p == '/')
903 p++;
904 }
905 }
906
907 while (*p) {
908 if (p[0] == '/' && p[1] == '/') {
909 char *p1 = p;
910 while (*p1) {
911 *p1 = p1[1];
912 p1++;
913 }
914 continue;
915 }
916 p++;
917 }
918
919 return 0;
920 }
921
922 static const unsigned char methods[] = {
923 WSI_TOKEN_GET_URI,
924 WSI_TOKEN_POST_URI,
925 #if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
926 WSI_TOKEN_OPTIONS_URI,
927 WSI_TOKEN_PUT_URI,
928 WSI_TOKEN_PATCH_URI,
929 WSI_TOKEN_DELETE_URI,
930 #endif
931 WSI_TOKEN_CONNECT,
932 WSI_TOKEN_HEAD_URI,
933 #ifdef LWS_WITH_HTTP2
934 WSI_TOKEN_HTTP_COLON_PATH,
935 #endif
936 };
937
938 int
lws_http_get_uri_and_method(struct lws * wsi,char ** puri_ptr,int * puri_len)939 lws_http_get_uri_and_method(struct lws *wsi, char **puri_ptr, int *puri_len)
940 {
941 int n, count = 0;
942
943 for (n = 0; n < (int)LWS_ARRAY_SIZE(methods); n++)
944 if (lws_hdr_total_length(wsi, methods[n]))
945 count++;
946 if (!count) {
947 lwsl_warn("Missing URI in HTTP request\n");
948 return -1;
949 }
950
951 if (count != 1 &&
952 !((wsi->mux_substream || wsi->h2_stream_carries_ws)
953 #if defined(LWS_ROLE_H2)
954 &&
955 lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH)
956 #endif
957 )) {
958 lwsl_warn("multiple methods?\n");
959 return -1;
960 }
961
962 for (n = 0; n < (int)LWS_ARRAY_SIZE(methods); n++)
963 if (lws_hdr_total_length(wsi, methods[n])) {
964 *puri_ptr = lws_hdr_simple_ptr(wsi, methods[n]);
965 *puri_len = lws_hdr_total_length(wsi, methods[n]);
966 return n;
967 }
968
969 return -1;
970 }
971
972 #if defined(LWS_WITH_HTTP_BASIC_AUTH)
973
974 enum lws_check_basic_auth_results
lws_check_basic_auth(struct lws * wsi,const char * basic_auth_login_file,unsigned int auth_mode)975 lws_check_basic_auth(struct lws *wsi, const char *basic_auth_login_file,
976 unsigned int auth_mode)
977 {
978 #if defined(LWS_WITH_FILE_OPS)
979 char b64[160], plain[(sizeof(b64) * 3) / 4], *pcolon;
980 int m, ml, fi, bar;
981
982 if (!basic_auth_login_file && auth_mode == LWSAUTHM_DEFAULT)
983 return LCBA_CONTINUE;
984
985 /* Did he send auth? */
986 ml = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_AUTHORIZATION);
987 if (!ml)
988 return LCBA_FAILED_AUTH;
989
990 /* Disallow fragmentation monkey business */
991
992 fi = wsi->http.ah->frag_index[WSI_TOKEN_HTTP_AUTHORIZATION];
993 if (wsi->http.ah->frags[fi].nfrag) {
994 lwsl_err("fragmented basic auth header not allowed\n");
995 return LCBA_FAILED_AUTH;
996 }
997
998 m = lws_hdr_copy(wsi, b64, sizeof(b64),
999 WSI_TOKEN_HTTP_AUTHORIZATION);
1000 if (m < 7) {
1001 lwsl_err("b64 auth too long\n");
1002 return LCBA_END_TRANSACTION;
1003 }
1004
1005 b64[5] = '\0';
1006 if (strcasecmp(b64, "Basic")) {
1007 lwsl_err("auth missing basic: %s\n", b64);
1008 return LCBA_END_TRANSACTION;
1009 }
1010
1011 /* It'll be like Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l */
1012
1013 m = lws_b64_decode_string(b64 + 6, plain, sizeof(plain) - 1);
1014 if (m < 0) {
1015 lwsl_err("plain auth too long\n");
1016 return LCBA_END_TRANSACTION;
1017 }
1018
1019 plain[m] = '\0';
1020 pcolon = strchr(plain, ':');
1021 if (!pcolon) {
1022 lwsl_err("basic auth format broken\n");
1023 return LCBA_END_TRANSACTION;
1024 }
1025
1026 switch (auth_mode) {
1027 case LWSAUTHM_DEFAULT:
1028 if (lws_find_string_in_file(basic_auth_login_file, plain, m))
1029 break;
1030 lwsl_err("%s: basic auth lookup failed\n", __func__);
1031 return LCBA_FAILED_AUTH;
1032
1033 case LWSAUTHM_BASIC_AUTH_CALLBACK:
1034 bar = wsi->protocol->callback(wsi,
1035 LWS_CALLBACK_VERIFY_BASIC_AUTHORIZATION,
1036 wsi->user_space, plain, m);
1037 if (!bar)
1038 return LCBA_FAILED_AUTH;
1039 break;
1040 default:
1041 /* Invalid auth mode so lets fail all authentication attempts */
1042 return LCBA_FAILED_AUTH;
1043 }
1044
1045 /*
1046 * Rewrite WSI_TOKEN_HTTP_AUTHORIZATION so it is just the
1047 * authorized username
1048 */
1049
1050 *pcolon = '\0';
1051 wsi->http.ah->frags[fi].len = lws_ptr_diff(pcolon, plain);
1052 pcolon = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_AUTHORIZATION);
1053 strncpy(pcolon, plain, ml - 1);
1054 pcolon[ml - 1] = '\0';
1055 lwsl_info("%s: basic auth accepted for %s\n", __func__,
1056 lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_AUTHORIZATION));
1057
1058 return LCBA_CONTINUE;
1059 #else
1060 return LCBA_FAILED_AUTH;
1061 #endif
1062 }
1063
1064 #endif
1065
1066 #if defined(LWS_WITH_HTTP_PROXY)
1067 /*
1068 * Set up an onward http proxy connection according to the mount this
1069 * uri falls under. Notice this can also be starting the proxying of what was
1070 * originally an incoming h1 upgrade, or an h2 ws "upgrade".
1071 */
1072 int
lws_http_proxy_start(struct lws * wsi,const struct lws_http_mount * hit,char * uri_ptr,char ws)1073 lws_http_proxy_start(struct lws *wsi, const struct lws_http_mount *hit,
1074 char *uri_ptr, char ws)
1075 {
1076 char ads[96], rpath[256], host[96], *pcolon, *pslash, unix_skt = 0;
1077 struct lws_client_connect_info i;
1078 struct lws *cwsi;
1079 int n, na;
1080
1081 #if defined(LWS_ROLE_WS)
1082 if (ws)
1083 /*
1084 * Neither our inbound ws upgrade request side, nor our onward
1085 * ws client connection on our side can bind to the actual
1086 * protocol that only the remote inbound side and the remote
1087 * onward side understand.
1088 *
1089 * Instead these are both bound to our built-in "lws-ws-proxy"
1090 * protocol, which understands how to proxy between the two
1091 * sides.
1092 *
1093 * We bind the parent, inbound part here and our side of the
1094 * onward client connection is bound to the same handler using
1095 * the .local_protocol_name.
1096 */
1097 lws_bind_protocol(wsi, &lws_ws_proxy, __func__);
1098 #endif
1099 memset(&i, 0, sizeof(i));
1100 i.context = lws_get_context(wsi);
1101
1102 if (hit->origin[0] == '+')
1103 unix_skt = 1;
1104
1105 pcolon = strchr(hit->origin, ':');
1106 pslash = strchr(hit->origin, '/');
1107 if (!pslash) {
1108 lwsl_err("Proxy mount origin '%s' must have /\n", hit->origin);
1109 return -1;
1110 }
1111
1112 if (unix_skt) {
1113 if (!pcolon) {
1114 lwsl_err("Proxy mount origin for unix skt must "
1115 "have address delimited by :\n");
1116
1117 return -1;
1118 }
1119 n = lws_ptr_diff(pcolon, hit->origin);
1120 pslash = pcolon;
1121 } else {
1122 if (pcolon > pslash)
1123 pcolon = NULL;
1124
1125 if (pcolon)
1126 n = (int)(pcolon - hit->origin);
1127 else
1128 n = (int)(pslash - hit->origin);
1129
1130 if (n >= (int)sizeof(ads) - 2)
1131 n = sizeof(ads) - 2;
1132 }
1133
1134 memcpy(ads, hit->origin, n);
1135 ads[n] = '\0';
1136
1137 i.address = ads;
1138 i.port = 80;
1139 if (hit->origin_protocol == LWSMPRO_HTTPS) {
1140 i.port = 443;
1141 i.ssl_connection = 1;
1142 }
1143 if (pcolon)
1144 i.port = atoi(pcolon + 1);
1145
1146 n = lws_snprintf(rpath, sizeof(rpath) - 1, "/%s/%s",
1147 pslash + 1, uri_ptr + hit->mountpoint_len) - 2;
1148 lws_clean_url(rpath);
1149 na = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_URI_ARGS);
1150 if (na) {
1151 char *p;
1152
1153 if (!n) /* don't start with the ?... use the first / if so */
1154 n++;
1155
1156 p = rpath + n;
1157
1158 if (na >= (int)sizeof(rpath) - n - 2) {
1159 lwsl_info("%s: query string %d longer "
1160 "than we can handle\n", __func__,
1161 na);
1162
1163 return -1;
1164 }
1165
1166 *p++ = '?';
1167 if (lws_hdr_copy(wsi, p,
1168 (int)(&rpath[sizeof(rpath) - 1] - p),
1169 WSI_TOKEN_HTTP_URI_ARGS) > 0)
1170 while (na--) {
1171 if (*p == '\0')
1172 *p = '&';
1173 p++;
1174 }
1175 *p = '\0';
1176 }
1177
1178 i.path = rpath;
1179
1180 /* incoming may be h1 or h2... if he sends h1 HOST, use that
1181 * directly, otherwise we must convert h2 :authority to h1
1182 * host */
1183
1184 i.host = NULL;
1185 #if defined(LWS_ROLE_H2)
1186 n = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_AUTHORITY);
1187 if (n > 0)
1188 i.host = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_AUTHORITY);
1189 else
1190 #endif
1191 {
1192 n = lws_hdr_total_length(wsi, WSI_TOKEN_HOST);
1193 if (n > 0) {
1194 i.host = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST);
1195 }
1196 }
1197
1198 #if 0
1199 if (i.address[0] != '+' ||
1200 !lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST))
1201 i.host = i.address;
1202 else
1203 i.host = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST);
1204 #endif
1205 i.origin = NULL;
1206 if (!ws) {
1207 if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_POST_URI)
1208 #if defined(LWS_WITH_HTTP2)
1209 || (
1210 lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD) &&
1211 !strcmp(lws_hdr_simple_ptr(wsi,
1212 WSI_TOKEN_HTTP_COLON_METHOD), "post")
1213 )
1214 #endif
1215 )
1216 i.method = "POST";
1217 else
1218 i.method = "GET";
1219 }
1220
1221 if (i.host)
1222 lws_snprintf(host, sizeof(host), "%s:%u", i.host,
1223 wsi->vhost->listen_port);
1224 else
1225 lws_snprintf(host, sizeof(host), "%s:%d", i.address, i.port);
1226
1227 i.host = host;
1228
1229 i.alpn = "http/1.1";
1230 i.parent_wsi = wsi;
1231 i.pwsi = &cwsi;
1232 #if defined(LWS_ROLE_WS)
1233 i.protocol = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL);
1234 if (ws)
1235 i.local_protocol_name = "lws-ws-proxy";
1236 #endif
1237
1238 // i.uri_replace_from = hit->origin;
1239 // i.uri_replace_to = hit->mountpoint;
1240
1241 lwsl_info("proxying to %s port %d url %s, ssl %d, from %s, to %s\n",
1242 i.address, i.port, i.path, i.ssl_connection,
1243 i.uri_replace_from, i.uri_replace_to);
1244
1245 if (!lws_client_connect_via_info(&i)) {
1246 lwsl_err("proxy connect fail\n");
1247
1248 /*
1249 * ... we can't do the proxy action, but we can
1250 * cleanly return him a 503 and a description
1251 */
1252
1253 lws_return_http_status(wsi,
1254 HTTP_STATUS_SERVICE_UNAVAILABLE,
1255 "<h1>Service Temporarily Unavailable</h1>"
1256 "The server is temporarily unable to service "
1257 "your request due to maintenance downtime or "
1258 "capacity problems. Please try again later.");
1259
1260 return 1;
1261 }
1262
1263 lwsl_info("%s: setting proxy clientside on %p (parent %p)\n",
1264 __func__, cwsi, lws_get_parent(cwsi));
1265
1266 cwsi->http.proxy_clientside = 1;
1267 if (ws) {
1268 wsi->proxied_ws_parent = 1;
1269 cwsi->h1_ws_proxied = 1;
1270 if (i.protocol) {
1271 lwsl_debug("%s: (requesting '%s')\n",
1272 __func__, i.protocol);
1273 }
1274 }
1275
1276 return 0;
1277 }
1278 #endif
1279
1280
1281 static const char * const oprot[] = {
1282 "http://", "https://"
1283 };
1284
1285
1286 static int
lws_http_redirect_hit(struct lws_context_per_thread * pt,struct lws * wsi,const struct lws_http_mount * hit,char * uri_ptr,int uri_len,int * h)1287 lws_http_redirect_hit(struct lws_context_per_thread *pt, struct lws *wsi,
1288 const struct lws_http_mount *hit, char *uri_ptr,
1289 int uri_len, int *h)
1290 {
1291 char *s;
1292 int n;
1293
1294 *h = 0;
1295 s = uri_ptr + hit->mountpoint_len;
1296
1297 /*
1298 * if we have a mountpoint like https://xxx.com/yyy
1299 * there is an implied / at the end for our purposes since
1300 * we can only mount on a "directory".
1301 *
1302 * But if we just go with that, the browser cannot understand
1303 * that he is actually looking down one "directory level", so
1304 * even though we give him /yyy/abc.html he acts like the
1305 * current directory level is /. So relative urls like "x.png"
1306 * wrongly look outside the mountpoint.
1307 *
1308 * Therefore if we didn't come in on a url with an explicit
1309 * / at the end, we must redirect to add it so the browser
1310 * understands he is one "directory level" down.
1311 */
1312 if ((hit->mountpoint_len > 1 ||
1313 (hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
1314 hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) &&
1315 (*s != '/' ||
1316 (hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
1317 hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) &&
1318 (hit->origin_protocol != LWSMPRO_CGI &&
1319 hit->origin_protocol != LWSMPRO_CALLBACK)) {
1320 unsigned char *start = pt->serv_buf + LWS_PRE, *p = start,
1321 *end = p + wsi->context->pt_serv_buf_size -
1322 LWS_PRE - 512;
1323
1324 *h = 1;
1325
1326 lwsl_info("Doing 301 '%s' org %s\n", s, hit->origin);
1327
1328 /* > at start indicates deal with by redirect */
1329 if (hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
1330 hit->origin_protocol == LWSMPRO_REDIR_HTTPS)
1331 n = lws_snprintf((char *)end, 256, "%s%s",
1332 oprot[hit->origin_protocol & 1],
1333 hit->origin);
1334 else {
1335 if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
1336 #if defined(LWS_ROLE_H2)
1337 if (!lws_hdr_total_length(wsi,
1338 WSI_TOKEN_HTTP_COLON_AUTHORITY))
1339 #endif
1340 goto bail_nuke_ah;
1341 #if defined(LWS_ROLE_H2)
1342 n = lws_snprintf((char *)end, 256,
1343 "%s%s%s/", oprot[!!lws_is_ssl(wsi)],
1344 lws_hdr_simple_ptr(wsi,
1345 WSI_TOKEN_HTTP_COLON_AUTHORITY),
1346 uri_ptr);
1347 #else
1348 ;
1349 #endif
1350 } else
1351 n = lws_snprintf((char *)end, 256,
1352 "%s%s%s/", oprot[!!lws_is_ssl(wsi)],
1353 lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST),
1354 uri_ptr);
1355 }
1356
1357 lws_clean_url((char *)end);
1358 n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
1359 end, n, &p, end);
1360 if ((int)n < 0)
1361 goto bail_nuke_ah;
1362
1363 return lws_http_transaction_completed(wsi);
1364 }
1365
1366 return 0;
1367
1368 bail_nuke_ah:
1369 lws_header_table_detach(wsi, 1);
1370
1371 return 1;
1372 }
1373
1374 int
lws_http_action(struct lws * wsi)1375 lws_http_action(struct lws *wsi)
1376 {
1377 struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
1378 int uri_len = 0, meth, m, http_version_len, ha;
1379 const struct lws_http_mount *hit = NULL;
1380 enum http_version request_version;
1381 struct lws_process_html_args args;
1382 enum http_conn_type conn_type;
1383 char content_length_str[32];
1384 char http_version_str[12];
1385 char *uri_ptr = NULL, *s;
1386 char http_conn_str[25];
1387 unsigned int n;
1388
1389 meth = lws_http_get_uri_and_method(wsi, &uri_ptr, &uri_len);
1390 if (meth < 0 || meth >= (int)LWS_ARRAY_SIZE(method_names))
1391 goto bail_nuke_ah;
1392
1393 /* we insist on absolute paths */
1394
1395 if (!uri_ptr || uri_ptr[0] != '/') {
1396 lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
1397
1398 goto bail_nuke_ah;
1399 }
1400
1401 lwsl_info("Method: '%s' (%d), request for '%s'\n", method_names[meth],
1402 meth, uri_ptr);
1403
1404 if (wsi->role_ops && wsi->role_ops->check_upgrades)
1405 switch (wsi->role_ops->check_upgrades(wsi)) {
1406 case LWS_UPG_RET_DONE:
1407 return 0;
1408 case LWS_UPG_RET_CONTINUE:
1409 break;
1410 case LWS_UPG_RET_BAIL:
1411 goto bail_nuke_ah;
1412 }
1413
1414 if (lws_ensure_user_space(wsi))
1415 goto bail_nuke_ah;
1416
1417 /* HTTP header had a content length? */
1418
1419 wsi->http.rx_content_length = 0;
1420 wsi->http.content_length_explicitly_zero = 0;
1421 if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)
1422 #if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
1423 ||
1424 lws_hdr_total_length(wsi, WSI_TOKEN_PATCH_URI) ||
1425 lws_hdr_total_length(wsi, WSI_TOKEN_PUT_URI)
1426 #endif
1427 )
1428 wsi->http.rx_content_length = 100 * 1024 * 1024;
1429
1430 if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH) &&
1431 lws_hdr_copy(wsi, content_length_str,
1432 sizeof(content_length_str) - 1,
1433 WSI_TOKEN_HTTP_CONTENT_LENGTH) > 0) {
1434 wsi->http.rx_content_remain = wsi->http.rx_content_length =
1435 atoll(content_length_str);
1436 if (!wsi->http.rx_content_length) {
1437 wsi->http.content_length_explicitly_zero = 1;
1438 lwsl_debug("%s: explicit 0 content-length\n", __func__);
1439 }
1440 }
1441
1442 if (wsi->mux_substream) {
1443 wsi->http.request_version = HTTP_VERSION_2;
1444 } else {
1445 /* http_version? Default to 1.0, override with token: */
1446 request_version = HTTP_VERSION_1_0;
1447
1448 /* Works for single digit HTTP versions. : */
1449 http_version_len = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP);
1450 if (http_version_len > 7 &&
1451 lws_hdr_copy(wsi, http_version_str,
1452 sizeof(http_version_str) - 1,
1453 WSI_TOKEN_HTTP) > 0 &&
1454 http_version_str[5] == '1' && http_version_str[7] == '1')
1455 request_version = HTTP_VERSION_1_1;
1456
1457 wsi->http.request_version = request_version;
1458
1459 /* HTTP/1.1 defaults to "keep-alive", 1.0 to "close" */
1460 if (request_version == HTTP_VERSION_1_1)
1461 conn_type = HTTP_CONNECTION_KEEP_ALIVE;
1462 else
1463 conn_type = HTTP_CONNECTION_CLOSE;
1464
1465 /* Override default if http "Connection:" header: */
1466 if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION) &&
1467 lws_hdr_copy(wsi, http_conn_str, sizeof(http_conn_str) - 1,
1468 WSI_TOKEN_CONNECTION) > 0) {
1469 http_conn_str[sizeof(http_conn_str) - 1] = '\0';
1470 if (!strcasecmp(http_conn_str, "keep-alive"))
1471 conn_type = HTTP_CONNECTION_KEEP_ALIVE;
1472 else
1473 if (!strcasecmp(http_conn_str, "close"))
1474 conn_type = HTTP_CONNECTION_CLOSE;
1475 }
1476 wsi->http.conn_type = conn_type;
1477 }
1478
1479 n = wsi->protocol->callback(wsi, LWS_CALLBACK_FILTER_HTTP_CONNECTION,
1480 wsi->user_space, uri_ptr, uri_len);
1481 if (n) {
1482 lwsl_info("LWS_CALLBACK_HTTP closing\n");
1483
1484 return 1;
1485 }
1486 /*
1487 * if there is content supposed to be coming,
1488 * put a timeout on it having arrived
1489 */
1490 if (!wsi->mux_stream_immortal)
1491 lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
1492 wsi->context->timeout_secs);
1493 #ifdef LWS_WITH_TLS
1494 if (wsi->tls.redirect_to_https) {
1495 /*
1496 * we accepted http:// only so we could redirect to
1497 * https://, so issue the redirect. Create the redirection
1498 * URI from the host: header and ignore the path part
1499 */
1500 unsigned char *start = pt->serv_buf + LWS_PRE, *p = start,
1501 *end = p + wsi->context->pt_serv_buf_size - LWS_PRE;
1502
1503 n = lws_hdr_total_length(wsi, WSI_TOKEN_HOST);
1504 if (!n || n > 128)
1505 goto bail_nuke_ah;
1506
1507 p += lws_snprintf((char *)p, lws_ptr_diff(end, p), "https://");
1508 memcpy(p, lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST), n);
1509 p += n;
1510 *p++ = '/';
1511 *p = '\0';
1512 n = lws_ptr_diff(p, start);
1513
1514 p += LWS_PRE;
1515 n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
1516 start, n, &p, end);
1517 if ((int)n < 0)
1518 goto bail_nuke_ah;
1519
1520 return lws_http_transaction_completed(wsi);
1521 }
1522 #endif
1523
1524 #ifdef LWS_WITH_ACCESS_LOG
1525 lws_prepare_access_log_info(wsi, uri_ptr, uri_len, meth);
1526 #endif
1527
1528 /* can we serve it from the mount list? */
1529
1530 hit = lws_find_mount(wsi, uri_ptr, uri_len);
1531 if (!hit) {
1532 /* deferred cleanup and reset to protocols[0] */
1533
1534 lwsl_info("no hit\n");
1535
1536 if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0],
1537 "no mount hit"))
1538 return 1;
1539
1540 lwsi_set_state(wsi, LRS_DOING_TRANSACTION);
1541
1542 m = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
1543 wsi->user_space, uri_ptr, uri_len);
1544
1545 goto after;
1546 }
1547
1548 s = uri_ptr + hit->mountpoint_len;
1549 n = lws_http_redirect_hit(pt, wsi, hit, uri_ptr, uri_len, &ha);
1550 if (ha)
1551 return n;
1552
1553 #if defined(LWS_WITH_HTTP_BASIC_AUTH)
1554
1555 /* basic auth? */
1556
1557 switch (lws_check_basic_auth(wsi, hit->basic_auth_login_file,
1558 hit->auth_mask & AUTH_MODE_MASK)) {
1559 case LCBA_CONTINUE:
1560 break;
1561 case LCBA_FAILED_AUTH:
1562 return lws_unauthorised_basic_auth(wsi);
1563 case LCBA_END_TRANSACTION:
1564 lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
1565 return lws_http_transaction_completed(wsi);
1566 }
1567 #endif
1568
1569 #if defined(LWS_WITH_HTTP_PROXY)
1570 /*
1571 * The mount is a reverse proxy?
1572 */
1573
1574 // if (hit)
1575 // lwsl_notice("%s: origin_protocol: %d\n", __func__, hit->origin_protocol);
1576 //else
1577 // lwsl_notice("%s: no hit\n", __func__);
1578
1579 if (hit->origin_protocol == LWSMPRO_HTTPS ||
1580 hit->origin_protocol == LWSMPRO_HTTP) {
1581 n = lws_http_proxy_start(wsi, hit, uri_ptr, 0);
1582 // lwsl_notice("proxy start says %d\n", n);
1583 if (n)
1584 return n;
1585
1586 goto deal_body;
1587 }
1588 #endif
1589
1590 /*
1591 * A particular protocol callback is mounted here?
1592 *
1593 * For the duration of this http transaction, bind us to the
1594 * associated protocol
1595 */
1596 if (hit->origin_protocol == LWSMPRO_CALLBACK || hit->protocol) {
1597 const struct lws_protocols *pp;
1598 const char *name = hit->origin;
1599 if (hit->protocol)
1600 name = hit->protocol;
1601
1602 pp = lws_vhost_name_to_protocol(wsi->vhost, name);
1603 if (!pp) {
1604 lwsl_err("Unable to find plugin '%s'\n",
1605 hit->origin);
1606 return 1;
1607 }
1608
1609 if (lws_bind_protocol(wsi, pp, "http action CALLBACK bind"))
1610 return 1;
1611
1612 lwsl_debug("%s: %s, checking access rights for mask 0x%x\n",
1613 __func__, hit->origin, hit->auth_mask);
1614
1615 args.p = uri_ptr;
1616 args.len = uri_len;
1617 args.max_len = hit->auth_mask & ~AUTH_MODE_MASK;
1618 args.final = 0; /* used to signal callback dealt with it */
1619 args.chunked = 0;
1620
1621 n = wsi->protocol->callback(wsi,
1622 LWS_CALLBACK_CHECK_ACCESS_RIGHTS,
1623 wsi->user_space, &args, 0);
1624 if (n) {
1625 lws_return_http_status(wsi, HTTP_STATUS_UNAUTHORIZED,
1626 NULL);
1627 goto bail_nuke_ah;
1628 }
1629 if (args.final) /* callback completely handled it well */
1630 return 0;
1631
1632 if (hit->cgienv && wsi->protocol->callback(wsi,
1633 LWS_CALLBACK_HTTP_PMO,
1634 wsi->user_space, (void *)hit->cgienv, 0))
1635 return 1;
1636
1637 if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
1638 m = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
1639 wsi->user_space,
1640 uri_ptr + hit->mountpoint_len,
1641 uri_len - hit->mountpoint_len);
1642 goto after;
1643 }
1644 }
1645
1646 #ifdef LWS_WITH_CGI
1647 /* did we hit something with a cgi:// origin? */
1648 if (hit->origin_protocol == LWSMPRO_CGI) {
1649 const char *cmd[] = {
1650 NULL, /* replace with cgi path */
1651 NULL
1652 };
1653
1654 lwsl_debug("%s: cgi\n", __func__);
1655 cmd[0] = hit->origin;
1656
1657 n = 5;
1658 if (hit->cgi_timeout)
1659 n = hit->cgi_timeout;
1660
1661 n = lws_cgi(wsi, cmd, hit->mountpoint_len, n,
1662 hit->cgienv);
1663 if (n) {
1664 lwsl_err("%s: cgi failed\n", __func__);
1665 return -1;
1666 }
1667
1668 goto deal_body;
1669 }
1670 #endif
1671
1672 n = uri_len - lws_ptr_diff(s, uri_ptr);
1673 if (s[0] == '\0' || (n == 1 && s[n - 1] == '/'))
1674 s = (char *)hit->def;
1675 if (!s)
1676 s = "index.html";
1677
1678 wsi->cache_secs = hit->cache_max_age;
1679 wsi->cache_reuse = hit->cache_reusable;
1680 wsi->cache_revalidate = hit->cache_revalidate;
1681 wsi->cache_intermediaries = hit->cache_intermediaries;
1682
1683 m = 1;
1684 #if defined(LWS_WITH_FILE_OPS)
1685 if (hit->origin_protocol == LWSMPRO_FILE)
1686 m = lws_http_serve(wsi, s, hit->origin, hit);
1687
1688 if (m > 0)
1689 #endif
1690 {
1691 /*
1692 * lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
1693 */
1694 if (hit->protocol) {
1695 const struct lws_protocols *pp =
1696 lws_vhost_name_to_protocol(
1697 wsi->vhost, hit->protocol);
1698
1699 lwsi_set_state(wsi, LRS_DOING_TRANSACTION);
1700
1701 if (lws_bind_protocol(wsi, pp, "http_action HTTP"))
1702 return 1;
1703
1704 m = pp->callback(wsi, LWS_CALLBACK_HTTP,
1705 wsi->user_space,
1706 uri_ptr + hit->mountpoint_len,
1707 uri_len - hit->mountpoint_len);
1708 } else
1709 m = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
1710 wsi->user_space, uri_ptr, uri_len);
1711 }
1712
1713 after:
1714 if (m) {
1715 lwsl_info("LWS_CALLBACK_HTTP closing\n");
1716
1717 return 1;
1718 }
1719
1720 #if defined(LWS_WITH_CGI) || defined(LWS_WITH_HTTP_PROXY)
1721 deal_body:
1722 #endif
1723 /*
1724 * If we're not issuing a file, check for content_length or
1725 * HTTP keep-alive. No keep-alive header allocation for
1726 * ISSUING_FILE, as this uses HTTP/1.0.
1727 *
1728 * In any case, return 0 and let lws_read decide how to
1729 * proceed based on state
1730 */
1731 if (lwsi_state(wsi) == LRS_ISSUING_FILE)
1732 return 0;
1733
1734 /* Prepare to read body if we have a content length: */
1735 lwsl_debug("wsi->http.rx_content_length %lld %d %d\n",
1736 (long long)wsi->http.rx_content_length,
1737 wsi->upgraded_to_http2, wsi->mux_substream);
1738
1739 if (wsi->http.content_length_explicitly_zero &&
1740 lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
1741
1742 /*
1743 * POST with an explicit content-length of zero
1744 *
1745 * If we don't give the user code the empty HTTP_BODY callback,
1746 * he may become confused to hear the HTTP_BODY_COMPLETION (due
1747 * to, eg, instantiation of lws_spa never happened).
1748 *
1749 * HTTP_BODY_COMPLETION is responsible for sending the result
1750 * status code and result body if any, and to do the transaction
1751 * complete processing.
1752 */
1753 if (wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_BODY,
1754 wsi->user_space, NULL, 0))
1755 return 1;
1756 if (wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_BODY_COMPLETION,
1757 wsi->user_space, NULL, 0))
1758 return 1;
1759
1760 return 0;
1761 }
1762
1763 if (wsi->http.rx_content_length <= 0)
1764 return 0;
1765
1766 if (lwsi_state(wsi) != LRS_DISCARD_BODY) {
1767 lwsi_set_state(wsi, LRS_BODY);
1768 lwsl_info("%s: %p: LRS_BODY state set (0x%x)\n", __func__, wsi,
1769 (int)wsi->wsistate);
1770 }
1771 wsi->http.rx_content_remain = wsi->http.rx_content_length;
1772
1773 /*
1774 * At this point we have transitioned from deferred
1775 * action to expecting BODY on the stream wsi, if it's
1776 * in a bundle like h2. So if the stream wsi has its
1777 * own buflist, we need to deal with that first.
1778 */
1779
1780 while (1) {
1781 struct lws_tokens ebuf;
1782 int m;
1783
1784 ebuf.len = (int)lws_buflist_next_segment_len(&wsi->buflist,
1785 &ebuf.token);
1786 if (!ebuf.len)
1787 break;
1788
1789 lwsl_debug("%s: consuming %d\n", __func__, (int)ebuf.len);
1790 m = lws_read_h1(wsi, ebuf.token, ebuf.len);
1791 if (m < 0)
1792 return -1;
1793
1794 if (lws_buflist_aware_finished_consuming(wsi, &ebuf, m, 1,
1795 __func__))
1796 return -1;
1797 }
1798
1799 return 0;
1800
1801 bail_nuke_ah:
1802 lws_header_table_detach(wsi, 1);
1803
1804 return 1;
1805 }
1806
1807 int
lws_confirm_host_header(struct lws * wsi)1808 lws_confirm_host_header(struct lws *wsi)
1809 {
1810 struct lws_tokenize ts;
1811 lws_tokenize_elem e;
1812 int port = 80, n;
1813 char buf[128];
1814
1815 /*
1816 * this vhost wants us to validate what the
1817 * client sent against our vhost name
1818 */
1819
1820 if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
1821 lwsl_info("%s: missing host on upgrade\n", __func__);
1822
1823 return 1;
1824 }
1825
1826 #if defined(LWS_WITH_TLS)
1827 if (wsi->tls.ssl)
1828 port = 443;
1829 #endif
1830
1831 lws_tokenize_init(&ts, buf, LWS_TOKENIZE_F_DOT_NONTERM /* server.com */|
1832 LWS_TOKENIZE_F_NO_FLOATS /* 1.server.com */|
1833 LWS_TOKENIZE_F_MINUS_NONTERM /* a-b.com */);
1834 n = lws_hdr_copy(wsi, buf, sizeof(buf) - 1, WSI_TOKEN_HOST);
1835 if (n <= 0) {
1836 lwsl_info("%s: missing or oversize host header\n", __func__);
1837 return 1;
1838 }
1839 ts.len = n;
1840
1841 if (lws_tokenize(&ts) != LWS_TOKZE_TOKEN)
1842 goto bad_format;
1843
1844 if (strncmp(ts.token, wsi->vhost->name, ts.token_len)) {
1845 buf[(ts.token - buf) + ts.token_len] = '\0';
1846 lwsl_info("%s: '%s' in host hdr but vhost name %s\n",
1847 __func__, ts.token, wsi->vhost->name);
1848 return 1;
1849 }
1850
1851 e = lws_tokenize(&ts);
1852 if (e == LWS_TOKZE_DELIMITER && ts.token[0] == ':') {
1853 if (lws_tokenize(&ts) != LWS_TOKZE_INTEGER)
1854 goto bad_format;
1855 else
1856 port = atoi(ts.token);
1857 } else
1858 if (e != LWS_TOKZE_ENDED)
1859 goto bad_format;
1860
1861 if (wsi->vhost->listen_port != port) {
1862 lwsl_info("%s: host port %d mismatches vhost port %d\n",
1863 __func__, port, wsi->vhost->listen_port);
1864 return 1;
1865 }
1866
1867 lwsl_debug("%s: host header OK\n", __func__);
1868
1869 return 0;
1870
1871 bad_format:
1872 lwsl_info("%s: bad host header format\n", __func__);
1873
1874 return 1;
1875 }
1876
1877 #if defined(LWS_WITH_SERVER)
1878 int
lws_http_to_fallback(struct lws * wsi,unsigned char * obuf,size_t olen)1879 lws_http_to_fallback(struct lws *wsi, unsigned char *obuf, size_t olen)
1880 {
1881 const struct lws_role_ops *role = &role_ops_raw_skt;
1882 const struct lws_protocols *p1, *protocol =
1883 &wsi->vhost->protocols[wsi->vhost->raw_protocol_index];
1884 char ipbuf[64];
1885 int n;
1886
1887 if (wsi->vhost->listen_accept_role &&
1888 lws_role_by_name(wsi->vhost->listen_accept_role))
1889 role = lws_role_by_name(wsi->vhost->listen_accept_role);
1890
1891 if (wsi->vhost->listen_accept_protocol) {
1892 p1 = lws_vhost_name_to_protocol(wsi->vhost,
1893 wsi->vhost->listen_accept_protocol);
1894 if (p1)
1895 protocol = p1;
1896 }
1897
1898 lws_bind_protocol(wsi, protocol, __func__);
1899
1900 lws_role_transition(wsi, LWSIFR_SERVER, LRS_ESTABLISHED, role);
1901
1902 lws_header_table_detach(wsi, 0);
1903 lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
1904
1905 n = LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED;
1906 if (wsi->role_ops->adoption_cb[1])
1907 n = wsi->role_ops->adoption_cb[1];
1908
1909 ipbuf[0] = '\0';
1910 #if !defined(LWS_PLAT_OPTEE)
1911 lws_get_peer_simple(wsi, ipbuf, sizeof(ipbuf));
1912 #endif
1913
1914 lwsl_notice("%s: vh %s, peer: %s, role %s, "
1915 "protocol %s, cb %d, ah %p\n", __func__, wsi->vhost->name,
1916 ipbuf, role->name, protocol->name, n, wsi->http.ah);
1917
1918 if ((wsi->protocol->callback)(wsi, n, wsi->user_space, NULL, 0))
1919 return 1;
1920
1921 n = LWS_CALLBACK_RAW_RX;
1922 if (wsi->role_ops->rx_cb[lwsi_role_server(wsi)])
1923 n = wsi->role_ops->rx_cb[lwsi_role_server(wsi)];
1924 if (wsi->protocol->callback(wsi, n, wsi->user_space, obuf, olen))
1925 return 1;
1926
1927 return 0;
1928 }
1929
1930 int
lws_handshake_server(struct lws * wsi,unsigned char ** buf,size_t len)1931 lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
1932 {
1933 struct lws_context *context = lws_get_context(wsi);
1934 struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
1935 #if defined(LWS_WITH_HTTP2)
1936 struct allocated_headers *ah;
1937 #endif
1938 unsigned char *obuf = *buf;
1939 #if defined(LWS_WITH_HTTP2)
1940 char tbuf[128], *p;
1941 #endif
1942 size_t olen = len;
1943 int n = 0, m, i;
1944
1945 if (len >= 10000000) {
1946 lwsl_err("%s: assert: len %ld\n", __func__, (long)len);
1947 assert(0);
1948 }
1949
1950 if (!wsi->http.ah) {
1951 lwsl_err("%s: assert: NULL ah\n", __func__);
1952 assert(0);
1953 }
1954
1955 while (len) {
1956 if (!lwsi_role_server(wsi) || !lwsi_role_http(wsi)) {
1957 lwsl_err("%s: bad wsi role 0x%x\n", __func__,
1958 (int)lwsi_role(wsi));
1959 goto bail_nuke_ah;
1960 }
1961
1962 i = (int)len;
1963 m = lws_parse(wsi, *buf, &i);
1964 lwsl_info("%s: parsed count %d\n", __func__, (int)len - i);
1965 (*buf) += (int)len - i;
1966 len = i;
1967
1968 if (m == LPR_DO_FALLBACK) {
1969
1970 /*
1971 * http parser went off the rails and
1972 * LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_
1973 * ACCEPT_CONFIG is set on this vhost.
1974 *
1975 * We are transitioning from http with an AH, to
1976 * a backup role (raw-skt, by default). Drop
1977 * the ah, bind to the role with mode as
1978 * ESTABLISHED.
1979 */
1980 raw_transition:
1981
1982 if (lws_http_to_fallback(wsi, obuf, olen)) {
1983 lwsl_info("%s: fallback -> close\n", __func__);
1984 goto bail_nuke_ah;
1985 }
1986
1987 (*buf) = obuf + olen;
1988
1989 return 0;
1990 }
1991 if (m) {
1992 lwsl_info("lws_parse failed\n");
1993 goto bail_nuke_ah;
1994 }
1995
1996 if (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE)
1997 continue;
1998
1999 lwsl_parser("%s: lws_parse sees parsing complete\n", __func__);
2000
2001 /* select vhost */
2002
2003 if (wsi->vhost->listen_port &&
2004 lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
2005 struct lws_vhost *vhost = lws_select_vhost(
2006 context, wsi->vhost->listen_port,
2007 lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
2008
2009 if (vhost)
2010 lws_vhost_bind_wsi(vhost, wsi);
2011 } else
2012 lwsl_info("no host\n");
2013
2014 if (!lwsi_role_h2(wsi) || !lwsi_role_server(wsi)) {
2015 #if defined(LWS_WITH_SERVER_STATUS)
2016 wsi->vhost->conn_stats.h1_trans++;
2017 #endif
2018 if (!wsi->conn_stat_done) {
2019 #if defined(LWS_WITH_SERVER_STATUS)
2020 wsi->vhost->conn_stats.h1_conn++;
2021 #endif
2022 wsi->conn_stat_done = 1;
2023 }
2024 }
2025
2026 /* check for unwelcome guests */
2027 #if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
2028 if (wsi->context->reject_service_keywords) {
2029 const struct lws_protocol_vhost_options *rej =
2030 wsi->context->reject_service_keywords;
2031 char ua[384], *msg = NULL;
2032
2033 if (lws_hdr_copy(wsi, ua, sizeof(ua) - 1,
2034 WSI_TOKEN_HTTP_USER_AGENT) > 0) {
2035 #ifdef LWS_WITH_ACCESS_LOG
2036 char *uri_ptr = NULL;
2037 int meth, uri_len;
2038 #endif
2039 ua[sizeof(ua) - 1] = '\0';
2040 while (rej) {
2041 if (!strstr(ua, rej->name)) {
2042 rej = rej->next;
2043 continue;
2044 }
2045
2046 msg = strchr(rej->value, ' ');
2047 if (msg)
2048 msg++;
2049 lws_return_http_status(wsi,
2050 atoi(rej->value), msg);
2051 #ifdef LWS_WITH_ACCESS_LOG
2052 meth = lws_http_get_uri_and_method(wsi,
2053 &uri_ptr, &uri_len);
2054 if (meth >= 0)
2055 lws_prepare_access_log_info(wsi,
2056 uri_ptr, uri_len, meth);
2057
2058 /* wsi close will do the log */
2059 #endif
2060 #if defined(LWS_WITH_SERVER_STATUS)
2061 wsi->vhost->conn_stats.rejected++;
2062 #endif
2063 /*
2064 * We don't want anything from
2065 * this rejected guy. Follow
2066 * the close flow, not the
2067 * transaction complete flow.
2068 */
2069 goto bail_nuke_ah;
2070 }
2071 }
2072 }
2073 #endif
2074 /*
2075 * So he may have come to us requesting one or another kind
2076 * of upgrade from http... but we may want to redirect him at
2077 * http level. In that case, we need to check the redirect
2078 * situation even though he's not actually wanting http and
2079 * prioritize returning that if there is one.
2080 */
2081
2082 {
2083 const struct lws_http_mount *hit = NULL;
2084 int uri_len = 0, ha, n;
2085 char *uri_ptr = NULL;
2086
2087 n = lws_http_get_uri_and_method(wsi, &uri_ptr, &uri_len);
2088 if (n >= 0) {
2089 hit = lws_find_mount(wsi, uri_ptr, uri_len);
2090 if (hit) {
2091 n = lws_http_redirect_hit(pt, wsi, hit, uri_ptr,
2092 uri_len, &ha);
2093 if (ha)
2094 return n;
2095 }
2096 }
2097 }
2098
2099
2100
2101 if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECT)) {
2102 lwsl_info("Changing to RAW mode\n");
2103 m = 0;
2104 goto raw_transition;
2105 }
2106
2107 lwsi_set_state(wsi, LRS_PRE_WS_SERVING_ACCEPT);
2108 lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
2109
2110 if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
2111
2112 const char *up = lws_hdr_simple_ptr(wsi,
2113 WSI_TOKEN_UPGRADE);
2114
2115 if (strcasecmp(up, "websocket") &&
2116 strcasecmp(up, "h2c")) {
2117 lwsl_info("Unknown upgrade '%s'\n", up);
2118
2119 if (lws_return_http_status(wsi,
2120 HTTP_STATUS_FORBIDDEN, NULL) ||
2121 lws_http_transaction_completed(wsi))
2122 goto bail_nuke_ah;
2123 }
2124
2125 n = user_callback_handle_rxflow(wsi->protocol->callback,
2126 wsi, LWS_CALLBACK_HTTP_CONFIRM_UPGRADE,
2127 wsi->user_space, (char *)up, 0);
2128
2129 /* just hang up? */
2130
2131 if (n < 0)
2132 goto bail_nuke_ah;
2133
2134 /* callback returned headers already, do t_c? */
2135
2136 if (n > 0) {
2137 if (lws_http_transaction_completed(wsi))
2138 goto bail_nuke_ah;
2139
2140 /* continue on */
2141
2142 return 0;
2143 }
2144
2145 /* callback said 0, it was allowed */
2146
2147 if (wsi->vhost->options &
2148 LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK &&
2149 lws_confirm_host_header(wsi))
2150 goto bail_nuke_ah;
2151
2152 if (!strcasecmp(up, "websocket")) {
2153 #if defined(LWS_ROLE_WS)
2154 #if defined(LWS_WITH_SERVER_STATUS)
2155 wsi->vhost->conn_stats.ws_upg++;
2156 #endif
2157 lwsl_info("Upgrade to ws\n");
2158 goto upgrade_ws;
2159 #endif
2160 }
2161 #if defined(LWS_WITH_HTTP2)
2162 if (!strcasecmp(up, "h2c")) {
2163 #if defined(LWS_WITH_SERVER_STATUS)
2164 wsi->vhost->conn_stats.h2_upg++;
2165 #endif
2166 lwsl_info("Upgrade to h2c\n");
2167 goto upgrade_h2c;
2168 }
2169 #endif
2170 }
2171
2172 /* no upgrade ack... he remained as HTTP */
2173
2174 lwsl_info("%s: %p: No upgrade\n", __func__, wsi);
2175
2176 lwsi_set_state(wsi, LRS_ESTABLISHED);
2177 #if defined(LWS_WITH_FILE_OPS)
2178 wsi->http.fop_fd = NULL;
2179 #endif
2180
2181 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2182 lws_http_compression_validate(wsi);
2183 #endif
2184
2185 lwsl_debug("%s: wsi %p: ah %p\n", __func__, (void *)wsi,
2186 (void *)wsi->http.ah);
2187
2188 n = lws_http_action(wsi);
2189
2190 return n;
2191
2192 #if defined(LWS_WITH_HTTP2)
2193 upgrade_h2c:
2194 if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP2_SETTINGS)) {
2195 lwsl_info("missing http2_settings\n");
2196 goto bail_nuke_ah;
2197 }
2198
2199 lwsl_info("h2c upgrade...\n");
2200
2201 p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP2_SETTINGS);
2202 /* convert the peer's HTTP-Settings */
2203 n = lws_b64_decode_string(p, tbuf, sizeof(tbuf));
2204 if (n < 0) {
2205 lwsl_parser("HTTP2_SETTINGS too long\n");
2206 return 1;
2207 }
2208
2209 wsi->upgraded_to_http2 = 1;
2210
2211 /* adopt the header info */
2212
2213 ah = wsi->http.ah;
2214 lws_role_transition(wsi, LWSIFR_SERVER, LRS_H2_AWAIT_PREFACE,
2215 &role_ops_h2);
2216
2217 /* http2 union member has http union struct at start */
2218 wsi->http.ah = ah;
2219
2220 if (!wsi->h2.h2n) {
2221 wsi->h2.h2n = lws_zalloc(sizeof(*wsi->h2.h2n), "h2n");
2222 if (!wsi->h2.h2n)
2223 return 1;
2224 }
2225
2226 lws_h2_init(wsi);
2227
2228 /* HTTP2 union */
2229
2230 lws_h2_settings(wsi, &wsi->h2.h2n->peer_set, (uint8_t *)tbuf, n);
2231
2232 lws_hpack_dynamic_size(wsi, wsi->h2.h2n->peer_set.s[
2233 H2SET_HEADER_TABLE_SIZE]);
2234
2235 strcpy(tbuf, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
2236 "Connection: Upgrade\x0d\x0a"
2237 "Upgrade: h2c\x0d\x0a\x0d\x0a");
2238 m = (int)strlen(tbuf);
2239 n = lws_issue_raw(wsi, (unsigned char *)tbuf, m);
2240 if (n != m) {
2241 lwsl_debug("http2 switch: ERROR writing to socket\n");
2242 return 1;
2243 }
2244
2245 return 0;
2246 #endif
2247 #if defined(LWS_ROLE_WS)
2248 upgrade_ws:
2249 if (lws_process_ws_upgrade(wsi))
2250 goto bail_nuke_ah;
2251
2252 return 0;
2253 #endif
2254 } /* while all chars are handled */
2255
2256 return 0;
2257
2258 bail_nuke_ah:
2259 /* drop the header info */
2260 lws_header_table_detach(wsi, 1);
2261
2262 return 1;
2263 }
2264 #endif
2265
2266 int LWS_WARN_UNUSED_RESULT
lws_http_transaction_completed(struct lws * wsi)2267 lws_http_transaction_completed(struct lws *wsi)
2268 {
2269 int n;
2270
2271 if (wsi->http.cgi_transaction_complete)
2272 return 0;
2273
2274 if (lws_has_buffered_out(wsi)
2275 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2276 || wsi->http.comp_ctx.buflist_comp ||
2277 wsi->http.comp_ctx.may_have_more
2278 #endif
2279 ) {
2280 /*
2281 * ...so he tried to send something large as the http reply,
2282 * it went as a partial, but he immediately said the
2283 * transaction was completed.
2284 *
2285 * Defer the transaction completed until the last part of the
2286 * partial is sent.
2287 */
2288 lwsl_debug("%s: %p: deferring due to partial\n", __func__, wsi);
2289 wsi->http.deferred_transaction_completed = 1;
2290 lws_callback_on_writable(wsi);
2291
2292 return 0;
2293 }
2294 /*
2295 * Are we finishing the transaction before we have consumed any body?
2296 *
2297 * For h1 this would kill keepalive pipelining, and for h2, considering
2298 * it can extend over multiple DATA frames, it would kill the network
2299 * connection.
2300 */
2301 if (wsi->http.rx_content_length && wsi->http.rx_content_remain) {
2302 /*
2303 * are we already in LRS_DISCARD_BODY and didn't clear the
2304 * remaining before trying to complete the transaction again?
2305 */
2306 if (lwsi_state(wsi) == LRS_DISCARD_BODY)
2307 return -1;
2308 /*
2309 * let's defer transaction completed processing until we
2310 * discarded the remaining body
2311 */
2312 lwsi_set_state(wsi, LRS_DISCARD_BODY);
2313
2314 return 0;
2315 }
2316
2317 lwsl_info("%s: wsi %p\n", __func__, wsi);
2318
2319 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2320 lws_http_compression_destroy(wsi);
2321 #endif
2322 lws_access_log(wsi);
2323
2324 if (!wsi->hdr_parsing_completed
2325 #if defined(LWS_WITH_CGI)
2326 && !wsi->http.cgi
2327 #endif
2328 ) {
2329 char peer[64];
2330
2331 #if !defined(LWS_PLAT_OPTEE)
2332 lws_get_peer_simple(wsi, peer, sizeof(peer) - 1);
2333 #else
2334 peer[0] = '\0';
2335 #endif
2336 peer[sizeof(peer) - 1] = '\0';
2337 lwsl_notice("%s: (from %s) ignoring, ah parsing incomplete\n",
2338 __func__, peer);
2339 return 0;
2340 }
2341
2342 #if defined(LWS_WITH_CGI)
2343 if (wsi->http.cgi) {
2344 lwsl_debug("%s: cleaning cgi\n", __func__);
2345 wsi->http.cgi_transaction_complete = 1;
2346 lws_cgi_remove_and_kill(wsi);
2347 lws_spawn_piped_destroy(&wsi->http.cgi->lsp);
2348
2349 lws_free_set_NULL(wsi->http.cgi);
2350 wsi->http.cgi_transaction_complete = 0;
2351 }
2352 #endif
2353
2354 /* if we can't go back to accept new headers, drop the connection */
2355 if (wsi->mux_substream)
2356 return 1;
2357
2358 if (wsi->seen_zero_length_recv)
2359 return 1;
2360
2361 if (wsi->http.conn_type != HTTP_CONNECTION_KEEP_ALIVE) {
2362 lwsl_info("%s: %p: close connection\n", __func__, wsi);
2363 return 1;
2364 }
2365
2366 if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0], __func__))
2367 return 1;
2368
2369 /*
2370 * otherwise set ourselves up ready to go again, but because we have no
2371 * idea about the wsi writability, we make put it in a holding state
2372 * until we can verify POLLOUT. The part of this that confirms POLLOUT
2373 * with no partials is in lws_server_socket_service() below.
2374 */
2375 lwsl_debug("%s: %p: setting DEF_ACT from 0x%x: %p\n", __func__,
2376 wsi, (int)wsi->wsistate, wsi->buflist);
2377 lwsi_set_state(wsi, LRS_DEFERRING_ACTION);
2378 wsi->http.tx_content_length = 0;
2379 wsi->http.tx_content_remain = 0;
2380 wsi->hdr_parsing_completed = 0;
2381 wsi->sending_chunked = 0;
2382 #ifdef LWS_WITH_ACCESS_LOG
2383 wsi->http.access_log.sent = 0;
2384 #endif
2385 #if defined(LWS_WITH_FILE_OPS) && (defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2))
2386 if (lwsi_role_http(wsi) && lwsi_role_server(wsi) &&
2387 wsi->http.fop_fd != NULL)
2388 lws_vfs_file_close(&wsi->http.fop_fd);
2389 #endif
2390
2391 n = NO_PENDING_TIMEOUT;
2392 if (wsi->vhost->keepalive_timeout)
2393 n = PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE;
2394 lws_set_timeout(wsi, n, wsi->vhost->keepalive_timeout);
2395
2396 /*
2397 * We already know we are on http1.1 / keepalive and the next thing
2398 * coming will be another header set.
2399 *
2400 * If there is no pending rx and we still have the ah, drop it and
2401 * reacquire a new ah when the new headers start to arrive. (Otherwise
2402 * we needlessly hog an ah indefinitely.)
2403 *
2404 * However if there is pending rx and we know from the keepalive state
2405 * that is already at least the start of another header set, simply
2406 * reset the existing header table and keep it.
2407 */
2408 if (wsi->http.ah) {
2409 // lws_buflist_describe(&wsi->buflist, wsi, __func__);
2410 if (!lws_buflist_next_segment_len(&wsi->buflist, NULL)) {
2411 lwsl_debug("%s: %p: nothing in buflist, detaching ah\n",
2412 __func__, wsi);
2413 lws_header_table_detach(wsi, 1);
2414 #ifdef LWS_WITH_TLS
2415 /*
2416 * additionally... if we are hogging an SSL instance
2417 * with no pending pipelined headers (or ah now), and
2418 * SSL is scarce, drop this connection without waiting
2419 */
2420
2421 if (wsi->vhost->tls.use_ssl &&
2422 wsi->context->simultaneous_ssl_restriction &&
2423 wsi->context->simultaneous_ssl ==
2424 wsi->context->simultaneous_ssl_restriction) {
2425 lwsl_info("%s: simultaneous_ssl_restriction\n",
2426 __func__);
2427 return 1;
2428 }
2429 #endif
2430 } else {
2431 lwsl_info("%s: %p: resetting/keeping ah as pipeline\n",
2432 __func__, wsi);
2433 lws_header_table_reset(wsi, 0);
2434 /*
2435 * If we kept the ah, we should restrict the amount
2436 * of time we are willing to keep it. Otherwise it
2437 * will be bound the whole time the connection remains
2438 * open.
2439 */
2440 lws_set_timeout(wsi, PENDING_TIMEOUT_HOLDING_AH,
2441 wsi->vhost->keepalive_timeout);
2442 }
2443 /* If we're (re)starting on headers, need other implied init */
2444 if (wsi->http.ah)
2445 wsi->http.ah->ues = URIES_IDLE;
2446
2447 //lwsi_set_state(wsi, LRS_ESTABLISHED); // !!!
2448 } else
2449 if (lws_buflist_next_segment_len(&wsi->buflist, NULL))
2450 if (lws_header_table_attach(wsi, 0))
2451 lwsl_debug("acquired ah\n");
2452
2453 lwsl_debug("%s: %p: keep-alive await new transaction (state 0x%x)\n",
2454 __func__, wsi, (int)wsi->wsistate);
2455 lws_callback_on_writable(wsi);
2456
2457 return 0;
2458 }
2459
2460 #if defined(LWS_WITH_FILE_OPS)
2461 int
lws_serve_http_file(struct lws * wsi,const char * file,const char * content_type,const char * other_headers,int other_headers_len)2462 lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
2463 const char *other_headers, int other_headers_len)
2464 {
2465 struct lws_context *context = lws_get_context(wsi);
2466 struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
2467 unsigned char *response = pt->serv_buf + LWS_PRE;
2468 #if defined(LWS_WITH_RANGES)
2469 struct lws_range_parsing *rp = &wsi->http.range;
2470 #endif
2471 int ret = 0, cclen = 8, n = HTTP_STATUS_OK;
2472 char cache_control[50], *cc = "no-store";
2473 lws_fop_flags_t fflags = LWS_O_RDONLY;
2474 const struct lws_plat_file_ops *fops;
2475 lws_filepos_t total_content_length;
2476 unsigned char *p = response;
2477 unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE;
2478 const char *vpath;
2479 #if defined(LWS_WITH_RANGES)
2480 int ranges;
2481 #endif
2482
2483 if (wsi->handling_404)
2484 n = HTTP_STATUS_NOT_FOUND;
2485
2486 /*
2487 * We either call the platform fops .open with first arg platform fops,
2488 * or we call fops_zip .open with first arg platform fops, and fops_zip
2489 * open will decide whether to switch to fops_zip or stay with fops_def.
2490 *
2491 * If wsi->http.fop_fd is already set, the caller already opened it
2492 */
2493 if (!wsi->http.fop_fd) {
2494 fops = lws_vfs_select_fops(wsi->context->fops, file, &vpath);
2495 fflags |= lws_vfs_prepare_flags(wsi);
2496 wsi->http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops,
2497 file, vpath, &fflags);
2498 if (!wsi->http.fop_fd) {
2499 lwsl_info("%s: Unable to open: '%s': errno %d\n",
2500 __func__, file, errno);
2501 if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND,
2502 NULL))
2503 return -1;
2504 return !wsi->mux_substream;
2505 }
2506 }
2507
2508 /*
2509 * Caution... wsi->http.fop_fd is live from here
2510 */
2511
2512 wsi->http.filelen = lws_vfs_get_length(wsi->http.fop_fd);
2513 total_content_length = wsi->http.filelen;
2514
2515 #if defined(LWS_WITH_RANGES)
2516 ranges = lws_ranges_init(wsi, rp, wsi->http.filelen);
2517
2518 lwsl_debug("Range count %d\n", ranges);
2519 /*
2520 * no ranges -> 200;
2521 * 1 range -> 206 + Content-Type: normal; Content-Range;
2522 * more -> 206 + Content-Type: multipart/byteranges
2523 * Repeat the true Content-Type in each multipart header
2524 * along with Content-Range
2525 */
2526 if (ranges < 0) {
2527 /* it means he expressed a range in Range:, but it was illegal */
2528 lws_return_http_status(wsi,
2529 HTTP_STATUS_REQ_RANGE_NOT_SATISFIABLE, NULL);
2530 if (lws_http_transaction_completed(wsi))
2531 goto bail; /* <0 means just hang up */
2532
2533 lws_vfs_file_close(&wsi->http.fop_fd);
2534
2535 return 0; /* == 0 means we did the transaction complete */
2536 }
2537 if (ranges)
2538 n = HTTP_STATUS_PARTIAL_CONTENT;
2539 #endif
2540
2541 if (lws_add_http_header_status(wsi, n, &p, end))
2542 goto bail;
2543
2544 if ((wsi->http.fop_fd->flags & (LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP |
2545 LWS_FOP_FLAG_COMPR_IS_GZIP)) ==
2546 (LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP | LWS_FOP_FLAG_COMPR_IS_GZIP)) {
2547 if (lws_add_http_header_by_token(wsi,
2548 WSI_TOKEN_HTTP_CONTENT_ENCODING,
2549 (unsigned char *)"gzip", 4, &p, end))
2550 goto bail;
2551 lwsl_info("file is being provided in gzip\n");
2552 }
2553 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2554 else {
2555 /*
2556 * if we know its very compressible, and we can use
2557 * compression, then use the most preferred compression
2558 * method that the client said he will accept
2559 */
2560
2561 if (!wsi->interpreting && (
2562 !strncmp(content_type, "text/", 5) ||
2563 !strcmp(content_type, "application/javascript") ||
2564 !strcmp(content_type, "image/svg+xml")))
2565 lws_http_compression_apply(wsi, NULL, &p, end, 0);
2566 }
2567 #endif
2568
2569 if (
2570 #if defined(LWS_WITH_RANGES)
2571 ranges < 2 &&
2572 #endif
2573 content_type && content_type[0])
2574 if (lws_add_http_header_by_token(wsi,
2575 WSI_TOKEN_HTTP_CONTENT_TYPE,
2576 (unsigned char *)content_type,
2577 (int)strlen(content_type),
2578 &p, end))
2579 goto bail;
2580
2581 #if defined(LWS_WITH_RANGES)
2582 if (ranges >= 2) { /* multipart byteranges */
2583 lws_strncpy(wsi->http.multipart_content_type, content_type,
2584 sizeof(wsi->http.multipart_content_type));
2585
2586 if (lws_add_http_header_by_token(wsi,
2587 WSI_TOKEN_HTTP_CONTENT_TYPE,
2588 (unsigned char *)
2589 "multipart/byteranges; "
2590 "boundary=_lws",
2591 20, &p, end))
2592 goto bail;
2593
2594 /*
2595 * our overall content length has to include
2596 *
2597 * - (n + 1) x "_lws\r\n"
2598 * - n x Content-Type: xxx/xxx\r\n
2599 * - n x Content-Range: bytes xxx-yyy/zzz\r\n
2600 * - n x /r/n
2601 * - the actual payloads (aggregated in rp->agg)
2602 *
2603 * Precompute it for the main response header
2604 */
2605
2606 total_content_length = (lws_filepos_t)rp->agg +
2607 6 /* final _lws\r\n */;
2608
2609 lws_ranges_reset(rp);
2610 while (lws_ranges_next(rp)) {
2611 n = lws_snprintf(cache_control, sizeof(cache_control),
2612 "bytes %llu-%llu/%llu",
2613 rp->start, rp->end, rp->extent);
2614
2615 total_content_length +=
2616 6 /* header _lws\r\n */ +
2617 /* Content-Type: xxx/xxx\r\n */
2618 14 + strlen(content_type) + 2 +
2619 /* Content-Range: xxxx\r\n */
2620 15 + n + 2 +
2621 2; /* /r/n */
2622 }
2623
2624 lws_ranges_reset(rp);
2625 lws_ranges_next(rp);
2626 }
2627
2628 if (ranges == 1) {
2629 total_content_length = (lws_filepos_t)rp->agg;
2630 n = lws_snprintf(cache_control, sizeof(cache_control),
2631 "bytes %llu-%llu/%llu",
2632 rp->start, rp->end, rp->extent);
2633
2634 if (lws_add_http_header_by_token(wsi,
2635 WSI_TOKEN_HTTP_CONTENT_RANGE,
2636 (unsigned char *)cache_control,
2637 n, &p, end))
2638 goto bail;
2639 }
2640
2641 wsi->http.range.inside = 0;
2642
2643 if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ACCEPT_RANGES,
2644 (unsigned char *)"bytes", 5, &p, end))
2645 goto bail;
2646 #endif
2647
2648 if (!wsi->mux_substream) {
2649 /* for http/1.1 ... */
2650 if (!wsi->sending_chunked
2651 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2652 && !wsi->http.lcs
2653 #endif
2654 ) {
2655 /* ... if not already using chunked and not using an
2656 * http compression translation, then send the naive
2657 * content length
2658 */
2659 if (lws_add_http_header_content_length(wsi,
2660 total_content_length, &p, end))
2661 goto bail;
2662 } else {
2663
2664 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2665 if (wsi->http.lcs) {
2666
2667 /* ...otherwise, for http 1 it must go chunked.
2668 * For the compression case, the reason is we
2669 * compress on the fly and do not know the
2670 * compressed content-length until it has all
2671 * been sent. Http/1.1 pipelining must be able
2672 * to know where the transaction boundaries are
2673 * ... so chunking...
2674 */
2675 if (lws_add_http_header_by_token(wsi,
2676 WSI_TOKEN_HTTP_TRANSFER_ENCODING,
2677 (unsigned char *)"chunked", 7,
2678 &p, end))
2679 goto bail;
2680
2681 /*
2682 * ...this is fun, isn't it :-) For h1 that is
2683 * using an http compression translation, the
2684 * compressor must chunk its output privately.
2685 *
2686 * h2 doesn't need (or support) any of this
2687 * crap.
2688 */
2689 lwsl_debug("setting chunking\n");
2690 wsi->http.comp_ctx.chunking = 1;
2691 }
2692 #endif
2693 }
2694 }
2695
2696 if (wsi->cache_secs && wsi->cache_reuse) {
2697 if (!wsi->cache_revalidate) {
2698 cc = cache_control;
2699 cclen = sprintf(cache_control, "%s, max-age=%u",
2700 intermediates[wsi->cache_intermediaries],
2701 wsi->cache_secs);
2702 } else {
2703 cc = cache_control;
2704 cclen = sprintf(cache_control,
2705 "must-revalidate, %s, max-age=%u",
2706 intermediates[wsi->cache_intermediaries],
2707 wsi->cache_secs);
2708
2709 }
2710 }
2711
2712 /* Only add cache control if its not specified by any other_headers. */
2713 if (!other_headers ||
2714 (!strstr(other_headers, "cache-control") &&
2715 !strstr(other_headers, "Cache-Control"))) {
2716 if (lws_add_http_header_by_token(wsi,
2717 WSI_TOKEN_HTTP_CACHE_CONTROL,
2718 (unsigned char *)cc, cclen, &p, end))
2719 goto bail;
2720 }
2721
2722 if (other_headers) {
2723 if ((end - p) < other_headers_len)
2724 goto bail;
2725 memcpy(p, other_headers, other_headers_len);
2726 p += other_headers_len;
2727 }
2728
2729 if (lws_finalize_http_header(wsi, &p, end))
2730 goto bail;
2731
2732 ret = lws_write(wsi, response, p - response, LWS_WRITE_HTTP_HEADERS);
2733 if (ret != (p - response)) {
2734 lwsl_err("_write returned %d from %ld\n", ret,
2735 (long)(p - response));
2736 goto bail;
2737 }
2738
2739 wsi->http.filepos = 0;
2740 lwsi_set_state(wsi, LRS_ISSUING_FILE);
2741
2742 if (lws_hdr_total_length(wsi, WSI_TOKEN_HEAD_URI)) {
2743 /* we do not emit the body */
2744 lws_vfs_file_close(&wsi->http.fop_fd);
2745 if (lws_http_transaction_completed(wsi))
2746 goto bail;
2747
2748 return 0;
2749 }
2750
2751 lws_callback_on_writable(wsi);
2752
2753 return 0;
2754
2755 bail:
2756 lws_vfs_file_close(&wsi->http.fop_fd);
2757
2758 return -1;
2759 }
2760 #endif
2761
2762 #if defined(LWS_WITH_FILE_OPS)
2763
lws_serve_http_file_fragment(struct lws * wsi)2764 int lws_serve_http_file_fragment(struct lws *wsi)
2765 {
2766 struct lws_context *context = wsi->context;
2767 struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
2768 struct lws_process_html_args args;
2769 lws_filepos_t amount, poss;
2770 unsigned char *p, *pstart;
2771 #if defined(LWS_WITH_RANGES)
2772 unsigned char finished = 0;
2773 #endif
2774 int n, m;
2775
2776 lwsl_debug("wsi->mux_substream %d\n", wsi->mux_substream);
2777
2778 do {
2779
2780 /* priority 1: buffered output */
2781
2782 if (lws_has_buffered_out(wsi)) {
2783 if (lws_issue_raw(wsi, NULL, 0) < 0) {
2784 lwsl_info("%s: closing\n", __func__);
2785 goto file_had_it;
2786 }
2787 break;
2788 }
2789
2790 /* priority 2: buffered pre-compression-transform */
2791
2792 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2793 if (wsi->http.comp_ctx.buflist_comp ||
2794 wsi->http.comp_ctx.may_have_more) {
2795 enum lws_write_protocol wp = LWS_WRITE_HTTP;
2796
2797 lwsl_info("%s: completing comp partial (buflist %p, may %d)\n",
2798 __func__, wsi->http.comp_ctx.buflist_comp,
2799 wsi->http.comp_ctx.may_have_more);
2800
2801 if (wsi->role_ops->write_role_protocol(wsi, NULL, 0, &wp) < 0) {
2802 lwsl_info("%s signalling to close\n", __func__);
2803 goto file_had_it;
2804 }
2805 lws_callback_on_writable(wsi);
2806
2807 break;
2808 }
2809 #endif
2810
2811 if (wsi->http.filepos == wsi->http.filelen)
2812 goto all_sent;
2813
2814 n = 0;
2815 p = pstart = pt->serv_buf + LWS_H2_FRAME_HEADER_LENGTH;
2816
2817 #if defined(LWS_WITH_RANGES)
2818 if (wsi->http.range.count_ranges && !wsi->http.range.inside) {
2819
2820 lwsl_notice("%s: doing range start %llu\n", __func__,
2821 wsi->http.range.start);
2822
2823 if ((long long)lws_vfs_file_seek_cur(wsi->http.fop_fd,
2824 wsi->http.range.start -
2825 wsi->http.filepos) < 0)
2826 goto file_had_it;
2827
2828 wsi->http.filepos = wsi->http.range.start;
2829
2830 if (wsi->http.range.count_ranges > 1) {
2831 n = lws_snprintf((char *)p,
2832 context->pt_serv_buf_size -
2833 LWS_H2_FRAME_HEADER_LENGTH,
2834 "_lws\x0d\x0a"
2835 "Content-Type: %s\x0d\x0a"
2836 "Content-Range: bytes "
2837 "%llu-%llu/%llu\x0d\x0a"
2838 "\x0d\x0a",
2839 wsi->http.multipart_content_type,
2840 wsi->http.range.start,
2841 wsi->http.range.end,
2842 wsi->http.range.extent);
2843 p += n;
2844 }
2845
2846 wsi->http.range.budget = wsi->http.range.end -
2847 wsi->http.range.start + 1;
2848 wsi->http.range.inside = 1;
2849 }
2850 #endif
2851
2852 poss = context->pt_serv_buf_size - n -
2853 LWS_H2_FRAME_HEADER_LENGTH;
2854
2855 if (wsi->http.tx_content_length)
2856 if (poss > wsi->http.tx_content_remain)
2857 poss = wsi->http.tx_content_remain;
2858
2859 /*
2860 * If there is a hint about how much we will do well to send at
2861 * one time, restrict ourselves to only trying to send that.
2862 */
2863 if (wsi->protocol->tx_packet_size &&
2864 poss > wsi->protocol->tx_packet_size)
2865 poss = wsi->protocol->tx_packet_size;
2866
2867 if (wsi->role_ops->tx_credit) {
2868 lws_filepos_t txc =
2869 wsi->role_ops->tx_credit(wsi, LWSTXCR_US_TO_PEER, 0);
2870
2871 if (!txc) {
2872 /*
2873 * We shouldn't've been able to get the
2874 * WRITEABLE if we are skint
2875 */
2876 lwsl_notice("%s: %p: no tx credit\n", __func__,
2877 wsi);
2878
2879 return 0;
2880 }
2881 if (txc < poss)
2882 poss = txc;
2883
2884 /*
2885 * Tracking consumption of the actual payload amount
2886 * will be handled when the role data frame is sent...
2887 */
2888 }
2889
2890 #if defined(LWS_WITH_RANGES)
2891 if (wsi->http.range.count_ranges) {
2892 if (wsi->http.range.count_ranges > 1)
2893 poss -= 7; /* allow for final boundary */
2894 if (poss > wsi->http.range.budget)
2895 poss = wsi->http.range.budget;
2896 }
2897 #endif
2898 if (wsi->sending_chunked) {
2899 /* we need to drop the chunk size in here */
2900 p += 10;
2901 /* allow for the chunk to grow by 128 in translation */
2902 poss -= 10 + 128;
2903 }
2904
2905 if (lws_vfs_file_read(wsi->http.fop_fd, &amount, p, poss) < 0)
2906 goto file_had_it; /* caller will close */
2907
2908 if (wsi->sending_chunked)
2909 n = (int)amount;
2910 else
2911 n = lws_ptr_diff(p, pstart) + (int)amount;
2912
2913 lwsl_debug("%s: sending %d\n", __func__, n);
2914
2915 if (n) {
2916 lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
2917 context->timeout_secs);
2918
2919 if (wsi->interpreting) {
2920 args.p = (char *)p;
2921 args.len = n;
2922 args.max_len = (unsigned int)poss + 128;
2923 args.final = wsi->http.filepos + n ==
2924 wsi->http.filelen;
2925 args.chunked = wsi->sending_chunked;
2926 if (user_callback_handle_rxflow(
2927 wsi->vhost->protocols[
2928 (int)wsi->protocol_interpret_idx].callback,
2929 wsi, LWS_CALLBACK_PROCESS_HTML,
2930 wsi->user_space, &args, 0) < 0)
2931 goto file_had_it;
2932 n = args.len;
2933 p = (unsigned char *)args.p;
2934 } else
2935 p = pstart;
2936
2937 #if defined(LWS_WITH_RANGES)
2938 if (wsi->http.range.send_ctr + 1 ==
2939 wsi->http.range.count_ranges && // last range
2940 wsi->http.range.count_ranges > 1 && // was 2+ ranges (ie, multipart)
2941 wsi->http.range.budget - amount == 0) {// final part
2942 n += lws_snprintf((char *)pstart + n, 6,
2943 "_lws\x0d\x0a"); // append trailing boundary
2944 lwsl_debug("added trailing boundary\n");
2945 }
2946 #endif
2947 m = lws_write(wsi, p, n, wsi->http.filepos + amount ==
2948 wsi->http.filelen ?
2949 LWS_WRITE_HTTP_FINAL : LWS_WRITE_HTTP);
2950 if (m < 0)
2951 goto file_had_it;
2952
2953 wsi->http.filepos += amount;
2954
2955 #if defined(LWS_WITH_RANGES)
2956 if (wsi->http.range.count_ranges >= 1) {
2957 wsi->http.range.budget -= amount;
2958 if (wsi->http.range.budget == 0) {
2959 lwsl_notice("range budget exhausted\n");
2960 wsi->http.range.inside = 0;
2961 wsi->http.range.send_ctr++;
2962
2963 if (lws_ranges_next(&wsi->http.range) < 1) {
2964 finished = 1;
2965 goto all_sent;
2966 }
2967 }
2968 }
2969 #endif
2970
2971 if (m != n) {
2972 /* adjust for what was not sent */
2973 if (lws_vfs_file_seek_cur(wsi->http.fop_fd,
2974 m - n) ==
2975 (lws_fileofs_t)-1)
2976 goto file_had_it;
2977 }
2978 }
2979
2980 all_sent:
2981 if ((!lws_has_buffered_out(wsi)
2982 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2983 && !wsi->http.comp_ctx.buflist_comp &&
2984 !wsi->http.comp_ctx.may_have_more
2985 #endif
2986 ) && (wsi->http.filepos >= wsi->http.filelen
2987 #if defined(LWS_WITH_RANGES)
2988 || finished)
2989 #else
2990 )
2991 #endif
2992 ) {
2993 lwsi_set_state(wsi, LRS_ESTABLISHED);
2994 /* we might be in keepalive, so close it off here */
2995 lws_vfs_file_close(&wsi->http.fop_fd);
2996
2997 lwsl_debug("file completed\n");
2998
2999 if (wsi->protocol->callback &&
3000 user_callback_handle_rxflow(wsi->protocol->callback,
3001 wsi, LWS_CALLBACK_HTTP_FILE_COMPLETION,
3002 wsi->user_space, NULL, 0) < 0) {
3003 /*
3004 * For http/1.x, the choices from
3005 * transaction_completed are either
3006 * 0 to use the connection for pipelined
3007 * or nonzero to hang it up.
3008 *
3009 * However for http/2. while we are
3010 * still interested in hanging up the
3011 * nwsi if there was a network-level
3012 * fatal error, simply completing the
3013 * transaction is a matter of the stream
3014 * state, not the root connection at the
3015 * network level
3016 */
3017 if (wsi->mux_substream)
3018 return 1;
3019 else
3020 return -1;
3021 }
3022
3023 return 1; /* >0 indicates completed */
3024 }
3025 /*
3026 * while(1) here causes us to spam the whole file contents into
3027 * a hugely bloated output buffer if it ever can't send the
3028 * whole chunk...
3029 */
3030 } while (!lws_send_pipe_choked(wsi));
3031
3032 lws_callback_on_writable(wsi);
3033
3034 return 0; /* indicates further processing must be done */
3035
3036 file_had_it:
3037 lws_vfs_file_close(&wsi->http.fop_fd);
3038
3039 return -1;
3040 }
3041
3042 #endif
3043
3044 #if defined(LWS_WITH_SERVER)
3045 void
lws_server_get_canonical_hostname(struct lws_context * context,const struct lws_context_creation_info * info)3046 lws_server_get_canonical_hostname(struct lws_context *context,
3047 const struct lws_context_creation_info *info)
3048 {
3049 if (lws_check_opt(info->options,
3050 LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME))
3051 return;
3052 #if !defined(LWS_PLAT_FREERTOS)
3053 /* find canonical hostname */
3054 gethostname((char *)context->canonical_hostname,
3055 sizeof(context->canonical_hostname) - 1);
3056
3057 lwsl_info(" canonical_hostname = %s\n", context->canonical_hostname);
3058 #else
3059 (void)context;
3060 #endif
3061 }
3062 #endif
3063
3064 int
lws_chunked_html_process(struct lws_process_html_args * args,struct lws_process_html_state * s)3065 lws_chunked_html_process(struct lws_process_html_args *args,
3066 struct lws_process_html_state *s)
3067 {
3068 char *sp, buffer[32];
3069 const char *pc;
3070 int old_len, n;
3071
3072 /* do replacements */
3073 sp = args->p;
3074 old_len = args->len;
3075 args->len = 0;
3076 s->start = sp;
3077 while (sp < args->p + old_len) {
3078
3079 if (args->len + 7 >= args->max_len) {
3080 lwsl_err("Used up interpret padding\n");
3081 return -1;
3082 }
3083
3084 if ((!s->pos && *sp == '$') || s->pos) {
3085 int hits = 0, hit = 0;
3086
3087 if (!s->pos)
3088 s->start = sp;
3089 s->swallow[s->pos++] = *sp;
3090 if (s->pos == sizeof(s->swallow) - 1)
3091 goto skip;
3092 for (n = 0; n < s->count_vars; n++)
3093 if (!strncmp(s->swallow, s->vars[n], s->pos)) {
3094 hits++;
3095 hit = n;
3096 }
3097 if (!hits) {
3098 skip:
3099 s->swallow[s->pos] = '\0';
3100 memcpy(s->start, s->swallow, s->pos);
3101 args->len++;
3102 s->pos = 0;
3103 sp = s->start + 1;
3104 continue;
3105 }
3106 if (hits == 1 && s->pos == (int)strlen(s->vars[hit])) {
3107 pc = s->replace(s->data, hit);
3108 if (!pc)
3109 pc = "NULL";
3110 n = (int)strlen(pc);
3111 s->swallow[s->pos] = '\0';
3112 if (n != s->pos) {
3113 memmove(s->start + n, s->start + s->pos,
3114 old_len - (sp - args->p) - 1);
3115 old_len += (n - s->pos) + 1;
3116 }
3117 memcpy(s->start, pc, n);
3118 args->len++;
3119 sp = s->start + 1;
3120
3121 s->pos = 0;
3122 }
3123 sp++;
3124 continue;
3125 }
3126
3127 args->len++;
3128 sp++;
3129 }
3130
3131 if (args->chunked) {
3132 /* no space left for final chunk trailer */
3133 if (args->final && args->len + 7 >= args->max_len)
3134 return -1;
3135
3136 n = sprintf(buffer, "%X\x0d\x0a", args->len);
3137
3138 args->p -= n;
3139 memcpy(args->p, buffer, n);
3140 args->len += n;
3141
3142 if (args->final) {
3143 sp = args->p + args->len;
3144 *sp++ = '\x0d';
3145 *sp++ = '\x0a';
3146 *sp++ = '0';
3147 *sp++ = '\x0d';
3148 *sp++ = '\x0a';
3149 *sp++ = '\x0d';
3150 *sp++ = '\x0a';
3151 args->len += 7;
3152 } else {
3153 sp = args->p + args->len;
3154 *sp++ = '\x0d';
3155 *sp++ = '\x0a';
3156 args->len += 2;
3157 }
3158 }
3159
3160 return 0;
3161 }
3162