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 #if defined(LWS_WITH_HTTP_PROXY)
28 static int
proxy_header(struct lws * wsi,struct lws * par,unsigned char * temp,int temp_len,int index,unsigned char ** p,unsigned char * end)29 proxy_header(struct lws *wsi, struct lws *par, unsigned char *temp,
30 int temp_len, int index, unsigned char **p, unsigned char *end)
31 {
32 int n = lws_hdr_total_length(par, index);
33
34 if (n < 1) {
35 lwsl_debug("%s: no index %d:\n", __func__, index);
36 return 0;
37 }
38
39 if (lws_hdr_copy(par, (char *)temp, temp_len, index) < 0)
40 return -1;
41
42 lwsl_debug("%s: index %d: %s\n", __func__, index, (char *)temp);
43
44 if (lws_add_http_header_by_token(wsi, index, temp, n, p, end))
45 return -1;
46
47 return 0;
48 }
49
50 static int
stream_close(struct lws * wsi)51 stream_close(struct lws *wsi)
52 {
53 char buf[LWS_PRE + 6], *out = buf + LWS_PRE;
54
55 if (wsi->http.did_stream_close)
56 return 0;
57
58 wsi->http.did_stream_close = 1;
59
60 if (wsi->mux_substream) {
61 if (lws_write(wsi, (unsigned char *)buf + LWS_PRE, 0,
62 LWS_WRITE_HTTP_FINAL) < 0) {
63 lwsl_info("%s: COMPL_CLIENT_HTTP: h2 fin wr failed\n",
64 __func__);
65
66 return -1;
67 }
68 } else {
69 *out++ = '0';
70 *out++ = '\x0d';
71 *out++ = '\x0a';
72 *out++ = '\x0d';
73 *out++ = '\x0a';
74
75 if (lws_write(wsi, (unsigned char *)buf + LWS_PRE, 5,
76 LWS_WRITE_HTTP_FINAL) < 0) {
77 lwsl_err("%s: COMPL_CLIENT_HTTP: "
78 "h2 final write failed\n", __func__);
79
80 return -1;
81 }
82 }
83
84 return 0;
85 }
86
87 #endif
88
89 struct lws_proxy_pkt {
90 struct lws_dll2 pkt_list;
91 size_t len;
92 char binary;
93 char first;
94 char final;
95
96 /* data follows */
97 };
98
99 #if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_ROLE_WS)
100 int
lws_callback_ws_proxy(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)101 lws_callback_ws_proxy(struct lws *wsi, enum lws_callback_reasons reason,
102 void *user, void *in, size_t len)
103 {
104 struct lws_proxy_pkt *pkt;
105 struct lws_dll2 *dll;
106
107 switch (reason) {
108
109 /* h1 ws proxying... child / client / onward */
110
111 case LWS_CALLBACK_CLIENT_ESTABLISHED:
112 if (!wsi->h1_ws_proxied || !wsi->parent)
113 break;
114
115 lws_process_ws_upgrade2(wsi->parent);
116
117 #if defined(LWS_WITH_HTTP2)
118 if (wsi->parent->mux_substream)
119 lwsl_info("%s: proxied h2 -> h1 ws established\n", __func__);
120 #endif
121 break;
122
123 case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
124 return 1;
125
126 case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
127 case LWS_CALLBACK_CLIENT_CLOSED:
128 lwsl_user("%s: client closed: parent %p\n", __func__, wsi->parent);
129 if (wsi->parent)
130 lws_set_timeout(wsi->parent, 1, LWS_TO_KILL_ASYNC);
131 break;
132
133 case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
134 {
135 unsigned char **p = (unsigned char **)in, *end = (*p) + len,
136 tmp[128];
137
138 proxy_header(wsi, wsi->parent, tmp, sizeof(tmp),
139 WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, p, end);
140
141 proxy_header(wsi, wsi->parent, tmp, sizeof(tmp),
142 WSI_TOKEN_HTTP_COOKIE, p, end);
143
144 proxy_header(wsi, wsi->parent, tmp, sizeof(tmp),
145 WSI_TOKEN_HTTP_SET_COOKIE, p, end);
146 break;
147 }
148
149 case LWS_CALLBACK_CLIENT_RECEIVE:
150 wsi->parent->ws->proxy_buffered += len;
151 if (wsi->parent->ws->proxy_buffered > 10 * 1024 * 1024) {
152 lwsl_err("%s: proxied ws connection excessive buffering: dropping\n",
153 __func__);
154 return -1;
155 }
156 pkt = lws_zalloc(sizeof(*pkt) + LWS_PRE + len, __func__);
157 if (!pkt)
158 return -1;
159
160 pkt->len = len;
161 pkt->first = lws_is_first_fragment(wsi);
162 pkt->final = lws_is_final_fragment(wsi);
163 pkt->binary = lws_frame_is_binary(wsi);
164
165 memcpy(((uint8_t *)&pkt[1]) + LWS_PRE, in, len);
166
167 lws_dll2_add_tail(&pkt->pkt_list, &wsi->parent->ws->proxy_owner);
168 lws_callback_on_writable(wsi->parent);
169 break;
170
171 case LWS_CALLBACK_CLIENT_WRITEABLE:
172 dll = lws_dll2_get_head(&wsi->ws->proxy_owner);
173 if (!dll)
174 break;
175
176 pkt = (struct lws_proxy_pkt *)dll;
177 if (lws_write(wsi, ((unsigned char *)&pkt[1]) +
178 LWS_PRE, pkt->len, lws_write_ws_flags(
179 pkt->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT,
180 pkt->first, pkt->final)) < 0)
181 return -1;
182
183 lws_dll2_remove(dll);
184 lws_free(pkt);
185
186 if (lws_dll2_get_head(&wsi->ws->proxy_owner))
187 lws_callback_on_writable(wsi);
188 break;
189
190 /* h1 ws proxying... parent / server / incoming */
191
192 case LWS_CALLBACK_CONFIRM_EXTENSION_OKAY:
193 return 1;
194
195 case LWS_CALLBACK_CLOSED:
196 lwsl_user("%s: closed\n", __func__);
197 return -1;
198
199 case LWS_CALLBACK_RECEIVE:
200 pkt = lws_zalloc(sizeof(*pkt) + LWS_PRE + len, __func__);
201 if (!pkt)
202 return -1;
203
204 pkt->len = len;
205 pkt->first = lws_is_first_fragment(wsi);
206 pkt->final = lws_is_final_fragment(wsi);
207 pkt->binary = lws_frame_is_binary(wsi);
208
209 memcpy(((uint8_t *)&pkt[1]) + LWS_PRE, in, len);
210
211 lws_dll2_add_tail(&pkt->pkt_list, &wsi->child_list->ws->proxy_owner);
212 lws_callback_on_writable(wsi->child_list);
213 break;
214
215 case LWS_CALLBACK_SERVER_WRITEABLE:
216 dll = lws_dll2_get_head(&wsi->ws->proxy_owner);
217 if (!dll)
218 break;
219
220 pkt = (struct lws_proxy_pkt *)dll;
221 if (lws_write(wsi, ((unsigned char *)&pkt[1]) +
222 LWS_PRE, pkt->len, lws_write_ws_flags(
223 pkt->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT,
224 pkt->first, pkt->final)) < 0)
225 return -1;
226
227 wsi->ws->proxy_buffered -= pkt->len;
228
229 lws_dll2_remove(dll);
230 lws_free(pkt);
231
232 if (lws_dll2_get_head(&wsi->ws->proxy_owner))
233 lws_callback_on_writable(wsi);
234 break;
235
236 default:
237 return 0;
238 }
239
240 return 0;
241 }
242
243 const struct lws_protocols lws_ws_proxy = {
244 "lws-ws-proxy",
245 lws_callback_ws_proxy,
246 0,
247 8192,
248 8192, NULL, 0
249 };
250
251 #endif
252
253 int
lws_callback_http_dummy(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)254 lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
255 void *user, void *in, size_t len)
256 {
257 struct lws_ssl_info *si;
258 #ifdef LWS_WITH_CGI
259 struct lws_cgi_args *args;
260 #endif
261 #if defined(LWS_WITH_CGI) || defined(LWS_WITH_HTTP_PROXY)
262 char buf[LWS_PRE + 32 + 8192];
263 int n;
264 #endif
265 #if defined(LWS_WITH_HTTP_PROXY)
266 unsigned char **p, *end;
267 struct lws *parent;
268 #endif
269
270 switch (reason) {
271 #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
272 case LWS_CALLBACK_HTTP:
273 #if defined(LWS_WITH_SERVER)
274 if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL))
275 return -1;
276
277 if (lws_http_transaction_completed(wsi))
278 #endif
279 return -1;
280 break;
281 #if defined(LWS_WITH_SERVER)
282 case LWS_CALLBACK_HTTP_BODY_COMPLETION:
283 #if defined(LWS_WITH_HTTP_PROXY)
284 if (wsi->child_list) {
285 lwsl_user("%s: LWS_CALLBACK_HTTP_BODY_COMPLETION: %d\n", __func__, (int)len);
286 break;
287 }
288 #endif
289 /* fallthru */
290 case LWS_CALLBACK_HTTP_FILE_COMPLETION:
291 if (lws_http_transaction_completed(wsi))
292 return -1;
293 break;
294 #endif
295
296 #if defined(LWS_WITH_HTTP_PROXY)
297 case LWS_CALLBACK_HTTP_BODY:
298 if (wsi->child_list) {
299 lwsl_user("%s: LWS_CALLBACK_HTTP_BODY: stashing %d\n", __func__, (int)len);
300 if (lws_buflist_append_segment(&wsi->http.buflist_post_body, in, len) < 0)
301 return -1;
302 lws_callback_on_writable(wsi->child_list);
303 }
304 break;
305 #endif
306
307 case LWS_CALLBACK_HTTP_WRITEABLE:
308 // lwsl_err("%s: LWS_CALLBACK_HTTP_WRITEABLE\n", __func__);
309 #ifdef LWS_WITH_CGI
310 if (wsi->reason_bf & (LWS_CB_REASON_AUX_BF__CGI_HEADERS |
311 LWS_CB_REASON_AUX_BF__CGI)) {
312 n = lws_cgi_write_split_stdout_headers(wsi);
313 if (n < 0) {
314 lwsl_debug("AUX_BF__CGI forcing close\n");
315 return -1;
316 }
317 if (!n && wsi->http.cgi &&
318 wsi->http.cgi->lsp->stdwsi[LWS_STDOUT])
319 lws_rx_flow_control(
320 wsi->http.cgi->lsp->stdwsi[LWS_STDOUT], 1);
321
322 if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_HEADERS)
323 wsi->reason_bf &=
324 ~LWS_CB_REASON_AUX_BF__CGI_HEADERS;
325 else
326 wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__CGI;
327
328 if (wsi->http.cgi && wsi->http.cgi->cgi_transaction_over)
329 return -1;
330 break;
331 }
332
333 if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_CHUNK_END) {
334 if (!wsi->mux_substream) {
335 memcpy(buf + LWS_PRE, "0\x0d\x0a\x0d\x0a", 5);
336 lwsl_debug("writing chunk term and exiting\n");
337 lws_write(wsi, (unsigned char *)buf +
338 LWS_PRE, 5, LWS_WRITE_HTTP);
339 } else
340 lws_write(wsi, (unsigned char *)buf +
341 LWS_PRE, 0,
342 LWS_WRITE_HTTP_FINAL);
343
344 /* always close after sending it */
345 if (lws_http_transaction_completed(wsi))
346 return -1;
347 return 0;
348 }
349 #endif
350 #if defined(LWS_WITH_HTTP_PROXY)
351
352 if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY_HEADERS) {
353
354 wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__PROXY_HEADERS;
355
356 n = LWS_WRITE_HTTP_HEADERS;
357 if (!wsi->http.prh_content_length)
358 n |= LWS_WRITE_H2_STREAM_END;
359
360 lwsl_debug("%s: %p: issuing proxy headers: clen %d\n",
361 __func__, wsi, (int)wsi->http.prh_content_length);
362 n = lws_write(wsi, wsi->http.pending_return_headers +
363 LWS_PRE,
364 wsi->http.pending_return_headers_len, n);
365
366 lws_free_set_NULL(wsi->http.pending_return_headers);
367
368 if (n < 0) {
369 lwsl_err("%s: EST_CLIENT_HTTP: write failed\n",
370 __func__);
371 return -1;
372 }
373
374 lws_callback_on_writable(wsi);
375 break;
376 }
377
378 if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY) {
379 char *px = buf + LWS_PRE;
380 int lenx = sizeof(buf) - LWS_PRE - 32;
381
382 /*
383 * our sink is writeable and our source has something
384 * to read. So read a lump of source material of
385 * suitable size to send or what's available, whichever
386 * is the smaller.
387 */
388 wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__PROXY;
389 if (!lws_get_child(wsi))
390 break;
391
392 /* this causes LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ */
393 if (lws_http_client_read(lws_get_child(wsi), &px,
394 &lenx) < 0) {
395 lwsl_info("%s: LWS_CB_REASON_AUX_BF__PROXY: "
396 "client closed\n", __func__);
397
398 stream_close(wsi);
399
400 return -1;
401 }
402 break;
403 }
404
405 if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY_TRANS_END) {
406 lwsl_info("%s: LWS_CB_REASON_AUX_BF__PROXY_TRANS_END\n",
407 __func__);
408
409 wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__PROXY_TRANS_END;
410
411 if (stream_close(wsi))
412 return -1;
413
414 if (lws_http_transaction_completed(wsi))
415 return -1;
416 }
417 #endif
418 break;
419
420 #if defined(LWS_WITH_HTTP_PROXY)
421 case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
422 assert(lws_get_parent(wsi));
423 if (!lws_get_parent(wsi))
424 break;
425 lws_get_parent(wsi)->reason_bf |= LWS_CB_REASON_AUX_BF__PROXY;
426 lws_callback_on_writable(lws_get_parent(wsi));
427 break;
428
429 case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: {
430 char *out = buf + LWS_PRE;
431
432 assert(lws_get_parent(wsi));
433
434 if (wsi->http.proxy_parent_chunked) {
435
436 if (len > sizeof(buf) - LWS_PRE - 16) {
437 lwsl_err("oversize buf %d %d\n", (int)len,
438 (int)sizeof(buf) - LWS_PRE - 16);
439 return -1;
440 }
441
442 /*
443 * this only needs dealing with on http/1.1 to allow
444 * pipelining
445 */
446 n = lws_snprintf(out, 14, "%X\x0d\x0a", (int)len);
447 out += n;
448 memcpy(out, in, len);
449 out += len;
450 *out++ = '\x0d';
451 *out++ = '\x0a';
452
453 n = lws_write(lws_get_parent(wsi),
454 (unsigned char *)buf + LWS_PRE,
455 len + n + 2, LWS_WRITE_HTTP);
456 } else
457 n = lws_write(lws_get_parent(wsi), (unsigned char *)in,
458 len, LWS_WRITE_HTTP);
459 if (n < 0)
460 return -1;
461 break; }
462
463 /* h1 http proxying... */
464
465 case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: {
466 unsigned char *start, *p, *end;
467
468 /*
469 * We want to proxy these headers, but we are being called
470 * at the point the onward client was established, which is
471 * unrelated to the state or writability of our proxy
472 * connection.
473 *
474 * Therefore produce the headers using the onward client ah
475 * while we have it, and stick them on the output buflist to be
476 * written on the proxy connection as soon as convenient.
477 */
478
479 parent = lws_get_parent(wsi);
480
481 if (!parent)
482 return 0;
483
484 start = p = (unsigned char *)buf + LWS_PRE;
485 end = p + sizeof(buf) - LWS_PRE - 256;
486
487 if (lws_add_http_header_status(lws_get_parent(wsi),
488 lws_http_client_http_response(wsi), &p, end))
489 return 1;
490
491 /*
492 * copy these headers from the client connection to the parent
493 */
494
495 proxy_header(parent, wsi, end, 256,
496 WSI_TOKEN_HTTP_CONTENT_LENGTH, &p, end);
497 proxy_header(parent, wsi, end, 256,
498 WSI_TOKEN_HTTP_CONTENT_TYPE, &p, end);
499 proxy_header(parent, wsi, end, 256,
500 WSI_TOKEN_HTTP_ETAG, &p, end);
501 proxy_header(parent, wsi, end, 256,
502 WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, &p, end);
503 proxy_header(parent, wsi, end, 256,
504 WSI_TOKEN_HTTP_CONTENT_ENCODING, &p, end);
505 proxy_header(parent, wsi, end, 256,
506 WSI_TOKEN_HTTP_CACHE_CONTROL, &p, end);
507 proxy_header(parent, wsi, end, 256,
508 WSI_TOKEN_HTTP_SET_COOKIE, &p, end);
509 proxy_header(parent, wsi, end, 256,
510 WSI_TOKEN_HTTP_LOCATION, &p, end);
511
512 if (!parent->mux_substream)
513 if (lws_add_http_header_by_token(parent,
514 WSI_TOKEN_CONNECTION, (unsigned char *)"close",
515 5, &p, end))
516 return -1;
517
518 /*
519 * We proxy using h1 only atm, and strip any chunking so it
520 * can go back out on h2 just fine.
521 *
522 * However if we are actually going out on h1, we need to add
523 * our own chunking since we still don't know the size.
524 */
525
526 if (!parent->mux_substream &&
527 !lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
528 lwsl_debug("downstream parent chunked\n");
529 if (lws_add_http_header_by_token(parent,
530 WSI_TOKEN_HTTP_TRANSFER_ENCODING,
531 (unsigned char *)"chunked", 7, &p, end))
532 return -1;
533
534 wsi->http.proxy_parent_chunked = 1;
535 }
536
537 if (lws_finalize_http_header(parent, &p, end))
538 return 1;
539
540 parent->http.prh_content_length = -1;
541 if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH))
542 parent->http.prh_content_length = atoll(
543 lws_hdr_simple_ptr(wsi,
544 WSI_TOKEN_HTTP_CONTENT_LENGTH));
545
546 parent->http.pending_return_headers_len = lws_ptr_diff(p, start);
547 parent->http.pending_return_headers =
548 lws_malloc(parent->http.pending_return_headers_len +
549 LWS_PRE, "return proxy headers");
550 if (!parent->http.pending_return_headers)
551 return -1;
552
553 memcpy(parent->http.pending_return_headers + LWS_PRE, start,
554 parent->http.pending_return_headers_len);
555
556 parent->reason_bf |= LWS_CB_REASON_AUX_BF__PROXY_HEADERS;
557
558 lwsl_debug("%s: LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: "
559 "prepared %d headers (len %d)\n", __func__,
560 lws_http_client_http_response(wsi),
561 (int)parent->http.prh_content_length);
562
563 /*
564 * so at this point, the onward client connection can bear
565 * traffic. We might be doing a POST and have pending cached
566 * inbound stuff to send, it can go now.
567 */
568
569 lws_callback_on_writable(parent);
570
571 break; }
572
573 case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
574 lwsl_info("%s: COMPLETED_CLIENT_HTTP: %p (parent %p)\n",
575 __func__, wsi, lws_get_parent(wsi));
576 if (!lws_get_parent(wsi))
577 break;
578 lws_get_parent(wsi)->reason_bf |=
579 LWS_CB_REASON_AUX_BF__PROXY_TRANS_END;
580 lws_callback_on_writable(lws_get_parent(wsi));
581 break;
582
583 case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
584 if (!lws_get_parent(wsi))
585 break;
586 // lwsl_err("%s: LWS_CALLBACK_CLOSED_CLIENT_HTTP\n", __func__);
587 lws_set_timeout(lws_get_parent(wsi), LWS_TO_KILL_ASYNC,
588 PENDING_TIMEOUT_KILLED_BY_PROXY_CLIENT_CLOSE);
589 break;
590
591 case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
592 parent = lws_get_parent(wsi);
593 if (!parent)
594 break;
595
596 p = (unsigned char **)in;
597 end = (*p) + len;
598
599 /*
600 * copy these headers from the parent request to the client
601 * connection's request
602 */
603
604 proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
605 WSI_TOKEN_HTTP_ETAG, p, end);
606 proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
607 WSI_TOKEN_HTTP_IF_MODIFIED_SINCE, p, end);
608 proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
609 WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, p, end);
610 proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
611 WSI_TOKEN_HTTP_ACCEPT_ENCODING, p, end);
612 proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
613 WSI_TOKEN_HTTP_CACHE_CONTROL, p, end);
614 proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
615 WSI_TOKEN_HTTP_COOKIE, p, end);
616
617 buf[0] = '\0';
618 lws_get_peer_simple(parent, buf, sizeof(buf));
619 if (lws_add_http_header_by_token(wsi, WSI_TOKEN_X_FORWARDED_FOR,
620 (unsigned char *)buf, (int)strlen(buf), p, end))
621 return -1;
622
623 break;
624 #endif
625
626 #ifdef LWS_WITH_CGI
627 /* CGI IO events (POLLIN/OUT) appear here, our default policy is:
628 *
629 * - POST data goes on subprocess stdin
630 * - subprocess stdout goes on http via writeable callback
631 * - subprocess stderr goes to the logs
632 */
633 case LWS_CALLBACK_CGI:
634 args = (struct lws_cgi_args *)in;
635 switch (args->ch) { /* which of stdin/out/err ? */
636 case LWS_STDIN:
637 /* TBD stdin rx flow control */
638 break;
639 case LWS_STDOUT:
640 /* quench POLLIN on STDOUT until MASTER got writeable */
641 lws_rx_flow_control(args->stdwsi[LWS_STDOUT], 0);
642 wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI;
643 /* when writing to MASTER would not block */
644 lws_callback_on_writable(wsi);
645 break;
646 case LWS_STDERR:
647 n = lws_get_socket_fd(args->stdwsi[LWS_STDERR]);
648 if (n < 0)
649 break;
650 n = read(n, buf, sizeof(buf) - 2);
651 if (n > 0) {
652 if (buf[n - 1] != '\n')
653 buf[n++] = '\n';
654 buf[n] = '\0';
655 lwsl_notice("CGI-stderr: %s\n", buf);
656 }
657 break;
658 }
659 break;
660
661 case LWS_CALLBACK_CGI_TERMINATED:
662 lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: %d %" PRIu64 "\n",
663 wsi->http.cgi->explicitly_chunked,
664 (uint64_t)wsi->http.cgi->content_length);
665 if (!(wsi->http.cgi->explicitly_chunked && wsi->mux_substream) &&
666 !wsi->http.cgi->content_length) {
667 /* send terminating chunk */
668 lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: ending\n");
669 wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_CHUNK_END;
670 lws_callback_on_writable(wsi);
671 lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, 3);
672 break;
673 }
674 if (wsi->mux_substream && !wsi->cgi_stdout_zero_length)
675 lws_write(wsi, (unsigned char *)buf + LWS_PRE, 0,
676 LWS_WRITE_HTTP_FINAL);
677
678 if (lws_http_transaction_completed(wsi))
679 return -1;
680 return 0;
681
682 case LWS_CALLBACK_CGI_STDIN_DATA: /* POST body for stdin */
683 args = (struct lws_cgi_args *)in;
684 args->data[args->len] = '\0';
685 if (!args->stdwsi[LWS_STDIN])
686 return -1;
687 n = lws_get_socket_fd(args->stdwsi[LWS_STDIN]);
688 if (n < 0)
689 return -1;
690
691 #if defined(LWS_WITH_ZLIB)
692 if (wsi->http.cgi->gzip_inflate) {
693 /* gzip handling */
694
695 if (!wsi->http.cgi->gzip_init) {
696 lwsl_info("inflating gzip\n");
697
698 memset(&wsi->http.cgi->inflate, 0,
699 sizeof(wsi->http.cgi->inflate));
700
701 if (inflateInit2(&wsi->http.cgi->inflate,
702 16 + 15) != Z_OK) {
703 lwsl_err("%s: iniflateInit failed\n",
704 __func__);
705 return -1;
706 }
707
708 wsi->http.cgi->gzip_init = 1;
709 }
710
711 wsi->http.cgi->inflate.next_in = args->data;
712 wsi->http.cgi->inflate.avail_in = args->len;
713
714 do {
715
716 wsi->http.cgi->inflate.next_out =
717 wsi->http.cgi->inflate_buf;
718 wsi->http.cgi->inflate.avail_out =
719 sizeof(wsi->http.cgi->inflate_buf);
720
721 n = inflate(&wsi->http.cgi->inflate,
722 Z_SYNC_FLUSH);
723
724 switch (n) {
725 case Z_NEED_DICT:
726 case Z_STREAM_ERROR:
727 case Z_DATA_ERROR:
728 case Z_MEM_ERROR:
729 inflateEnd(&wsi->http.cgi->inflate);
730 wsi->http.cgi->gzip_init = 0;
731 lwsl_err("zlib error inflate %d\n", n);
732 return -1;
733 }
734
735 if (wsi->http.cgi->inflate.avail_out !=
736 sizeof(wsi->http.cgi->inflate_buf)) {
737 int written;
738
739 written = write(args->stdwsi[LWS_STDIN]->desc.filefd,
740 wsi->http.cgi->inflate_buf,
741 sizeof(wsi->http.cgi->inflate_buf) -
742 wsi->http.cgi->inflate.avail_out);
743
744 if (written != (int)(
745 sizeof(wsi->http.cgi->inflate_buf) -
746 wsi->http.cgi->inflate.avail_out)) {
747 lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: "
748 "sent %d only %d went", n, args->len);
749 }
750
751 if (n == Z_STREAM_END) {
752 lwsl_err("gzip inflate end\n");
753 inflateEnd(&wsi->http.cgi->inflate);
754 wsi->http.cgi->gzip_init = 0;
755 break;
756 }
757
758 } else
759 break;
760
761 if (wsi->http.cgi->inflate.avail_out)
762 break;
763
764 } while (1);
765
766 return args->len;
767 }
768 #endif /* WITH_ZLIB */
769
770 n = write(n, args->data, args->len);
771 // lwsl_hexdump_notice(args->data, args->len);
772 if (n < args->len)
773 lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: "
774 "sent %d only %d went", n, args->len);
775
776 if (wsi->http.cgi->post_in_expected && args->stdwsi[LWS_STDIN] &&
777 args->stdwsi[LWS_STDIN]->desc.filefd > 0) {
778 wsi->http.cgi->post_in_expected -= n;
779 if (!wsi->http.cgi->post_in_expected) {
780 struct lws *siwsi = args->stdwsi[LWS_STDIN];
781
782 lwsl_debug("%s: expected POST in end: "
783 "closing stdin wsi %p, fd %d\n",
784 __func__, siwsi, siwsi->desc.sockfd);
785
786 __remove_wsi_socket_from_fds(siwsi);
787 lwsi_set_state(siwsi, LRS_DEAD_SOCKET);
788 siwsi->socket_is_permanently_unusable = 1;
789 // lws_remove_child_from_any_parent(siwsi);
790 if (wsi->context->event_loop_ops->
791 close_handle_manually) {
792
793 wsi->context->event_loop_ops->
794 close_handle_manually(siwsi);
795 siwsi->told_event_loop_closed = 1;
796 } else {
797 compatible_close(siwsi->desc.sockfd);
798 __lws_free_wsi(siwsi);
799 }
800 wsi->http.cgi->lsp->pipe_fds[LWS_STDIN][1] = -1;
801
802 // args->stdwsi[LWS_STDIN] = NULL;
803 }
804 }
805
806 return n;
807 #endif /* WITH_CGI */
808 #endif /* ROLE_ H1 / H2 */
809 case LWS_CALLBACK_SSL_INFO:
810 si = in;
811
812 (void)si;
813 lwsl_notice("LWS_CALLBACK_SSL_INFO: where: 0x%x, ret: 0x%x\n",
814 si->where, si->ret);
815 break;
816
817 #if LWS_MAX_SMP > 1
818 case LWS_CALLBACK_GET_THREAD_ID:
819 return (int)(unsigned long long)pthread_self();
820 #endif
821
822 default:
823 break;
824 }
825
826 return 0;
827 }
828