2 * Copyright (c) 2020-2022 Tracey Emery <tracey@traceyemery.net>
3 * Copyright (c) 2013 David Gwynne <dlg@openbsd.org>
4 * Copyright (c) 2013 Florian Obser <florian@openbsd.org>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include "got_compat.h"
21 #include <arpa/inet.h>
22 #include <sys/queue.h>
23 #include <sys/socket.h>
24 #include <sys/types.h>
37 #include "got_error.h"
38 #include "got_reference.h"
44 size_t fcgi_parse_record(uint8_t *, size_t, struct request
*);
45 void fcgi_parse_begin_request(uint8_t *, uint16_t, struct request
*,
47 void fcgi_parse_params(uint8_t *, uint16_t, struct request
*, uint16_t);
48 int fcgi_send_response(struct request
*, int, const void *, size_t);
50 void dump_fcgi_record_header(const char *, struct fcgi_record_header
*);
51 void dump_fcgi_begin_request_body(const char *,
52 struct fcgi_begin_request_body
*);
53 void dump_fcgi_end_request_body(const char *,
54 struct fcgi_end_request_body
*);
56 extern int cgi_inflight
;
57 extern volatile int client_cnt
;
60 fcgi_request(int fd
, short events
, void *arg
)
62 struct request
*c
= arg
;
66 n
= read(fd
, c
->buf
+ c
->buf_pos
+ c
->buf_len
,
67 FCGI_RECORD_SIZE
- c
->buf_pos
-c
->buf_len
);
81 log_debug("closed connection");
90 * Parse the records as they are received. Per the FastCGI
91 * specification, the server need only receive the FastCGI
92 * parameter records in full; it is free to begin execution
93 * at that point, which is what happens here.
96 parsed
= fcgi_parse_record(c
->buf
+ c
->buf_pos
, c
->buf_len
, c
);
102 /* drop the parsed record */
103 if (parsed
!= 0 && c
->buf_len
> 0) {
104 bcopy(c
->buf
+ c
->buf_pos
, c
->buf
, c
->buf_len
);
107 } while (parsed
> 0 && c
->buf_len
> 0);
111 fcgi_cleanup_request(c
);
115 fcgi_parse_record(uint8_t *buf
, size_t n
, struct request
*c
)
117 struct fcgi_record_header
*h
;
119 if (n
< sizeof(struct fcgi_record_header
))
122 h
= (struct fcgi_record_header
*) buf
;
124 dump_fcgi_record("", h
);
126 if (n
< sizeof(struct fcgi_record_header
) + ntohs(h
->content_len
)
131 log_warn("wrong version");
134 case FCGI_BEGIN_REQUEST
:
135 fcgi_parse_begin_request(buf
+
136 sizeof(struct fcgi_record_header
),
137 ntohs(h
->content_len
), c
, ntohs(h
->id
));
140 fcgi_parse_params(buf
+ sizeof(struct fcgi_record_header
),
141 ntohs(h
->content_len
), c
, ntohs(h
->id
));
144 case FCGI_ABORT_REQUEST
:
145 if (c
->sock
->client_status
!= CLIENT_DISCONNECT
&&
146 c
->outbuf_len
!= 0) {
147 fcgi_send_response(c
, FCGI_STDOUT
, c
->outbuf
,
151 fcgi_create_end_record(c
);
152 fcgi_cleanup_request(c
);
155 log_warn("unimplemented type %d", h
->type
);
159 return (sizeof(struct fcgi_record_header
) + ntohs(h
->content_len
)
164 fcgi_parse_begin_request(uint8_t *buf
, uint16_t n
,
165 struct request
*c
, uint16_t id
)
167 /* XXX -- FCGI_CANT_MPX_CONN */
168 if (c
->request_started
) {
169 log_warn("unexpected FCGI_BEGIN_REQUEST, ignoring");
173 if (n
!= sizeof(struct fcgi_begin_request_body
)) {
174 log_warn("wrong size %d != %lu", n
,
175 sizeof(struct fcgi_begin_request_body
));
179 c
->request_started
= 1;
184 fcgi_parse_params(uint8_t *buf
, uint16_t n
, struct request
*c
, uint16_t id
)
186 uint32_t name_len
, val_len
;
189 if (!c
->request_started
) {
190 log_warn("FCGI_PARAMS without FCGI_BEGIN_REQUEST, ignoring");
195 log_warn("unexpected id, ignoring");
200 gotweb_process_request(c
);
205 if (buf
[0] >> 7 == 0) {
211 name_len
= ((buf
[0] & 0x7f) << 24) +
212 (buf
[1] << 16) + (buf
[2] << 8) + buf
[3];
222 if (buf
[0] >> 7 == 0) {
228 val_len
= ((buf
[0] & 0x7f) << 24) +
229 (buf
[1] << 16) + (buf
[2] << 8) +
237 if (n
< name_len
+ val_len
)
240 val
= buf
+ name_len
;
242 if (c
->querystring
[0] == '\0' &&
243 val_len
< MAX_QUERYSTRING
&&
245 strncmp(buf
, "QUERY_STRING", 12) == 0) {
246 memcpy(c
->querystring
, val
, val_len
);
247 c
->querystring
[val_len
] = '\0';
250 if (c
->document_uri
[0] == '\0' &&
251 val_len
< MAX_DOCUMENT_URI
&&
253 strncmp(buf
, "DOCUMENT_URI", 12) == 0) {
254 memcpy(c
->document_uri
, val
, val_len
);
255 c
->document_uri
[val_len
] = '\0';
258 if (c
->server_name
[0] == '\0' &&
259 val_len
< MAX_SERVER_NAME
&&
261 strncmp(buf
, "SERVER_NAME", 11) == 0) {
262 memcpy(c
->server_name
, val
, val_len
);
263 c
->server_name
[val_len
] = '\0';
267 strncmp(buf
, "HTTPS", 5) == 0)
270 buf
+= name_len
+ val_len
;
271 n
-= name_len
- val_len
;
276 fcgi_timeout(int fd
, short events
, void *arg
)
278 fcgi_cleanup_request((struct request
*) arg
);
282 fcgi_puts(struct template *tp
, const char *str
)
286 return fcgi_gen_binary_response(tp
->tp_arg
, str
, strlen(str
));
290 fcgi_putc(struct template *tp
, int ch
)
293 return fcgi_gen_binary_response(tp
->tp_arg
, &c
, 1);
297 fcgi_vprintf(struct request
*c
, const char *fmt
, va_list ap
)
302 r
= vasprintf(&str
, fmt
, ap
);
304 log_warn("%s: asprintf", __func__
);
308 r
= fcgi_gen_binary_response(c
, str
, r
);
314 fcgi_printf(struct request
*c
, const char *fmt
, ...)
320 r
= fcgi_vprintf(c
, fmt
, ap
);
327 fcgi_gen_binary_response(struct request
*c
, const uint8_t *data
, int len
)
331 if (c
->sock
->client_status
== CLIENT_DISCONNECT
)
334 if (data
== NULL
|| len
== 0)
338 * special case: send big replies -like blobs- directly
341 if (len
> sizeof(c
->outbuf
)) {
342 if (c
->outbuf_len
> 0) {
343 fcgi_send_response(c
, FCGI_STDOUT
,
344 c
->outbuf
, c
->outbuf_len
);
347 return fcgi_send_response(c
, FCGI_STDOUT
, data
, len
);
350 if (len
< sizeof(c
->outbuf
) - c
->outbuf_len
) {
351 memcpy(c
->outbuf
+ c
->outbuf_len
, data
, len
);
352 c
->outbuf_len
+= len
;
356 r
= fcgi_send_response(c
, FCGI_STDOUT
, c
->outbuf
, c
->outbuf_len
);
360 memcpy(c
->outbuf
, data
, len
);
366 send_response(struct request
*c
, int type
, const uint8_t *data
,
369 static const uint8_t padding
[FCGI_PADDING_SIZE
];
370 struct fcgi_record_header header
;
374 size_t padded_len
, tot
;
375 int i
, err
= 0, th
= 2000;
380 memset(&header
, 0, sizeof(header
));
383 header
.id
= htons(c
->id
);
384 header
.content_len
= htons(len
);
386 /* The FastCGI spec suggests to align the output buffer */
387 tot
= sizeof(header
) + len
;
388 padded_len
= FCGI_ALIGN(tot
);
389 if (padded_len
> tot
) {
390 header
.padding_len
= padded_len
- tot
;
391 tot
+= header
.padding_len
;
394 iov
[0].iov_base
= &header
;
395 iov
[0].iov_len
= sizeof(header
);
397 iov
[1].iov_base
= (void *)data
;
398 iov
[1].iov_len
= len
;
400 iov
[2].iov_base
= (void *)padding
;
401 iov
[2].iov_len
= header
.padding_len
;
403 dump_fcgi_record("resp ", &header
);
406 * XXX: add some simple write heuristics here
407 * On slower VMs, spotty connections, etc., we don't want to go right to
408 * disconnect. Let's at least try to write the data a few times before
412 nw
= writev(c
->fd
, iov
, nitems(iov
));
414 c
->sock
->client_status
= CLIENT_DISCONNECT
;
419 if (errno
== EAGAIN
&& err
< th
) {
420 nanosleep(&ts
, NULL
);
423 log_debug("%s: write failure: %s", __func__
,
425 c
->sock
->client_status
= CLIENT_DISCONNECT
;
430 log_debug("%s: partial write: %zu vs %zu", __func__
,
434 for (i
= 0; i
< nitems(iov
); ++i
) {
435 if (nw
< iov
[i
].iov_len
) {
436 iov
[i
].iov_base
+= nw
;
437 iov
[i
].iov_len
-= nw
;
440 nw
-= iov
[i
].iov_len
;
449 fcgi_send_response(struct request
*c
, int type
, const void *data
,
452 if (c
->sock
->client_status
== CLIENT_DISCONNECT
)
455 while (len
> FCGI_CONTENT_SIZE
) {
456 if (send_response(c
, type
, data
, len
) == -1)
459 data
+= FCGI_CONTENT_SIZE
;
460 len
-= FCGI_CONTENT_SIZE
;
466 return send_response(c
, type
, data
, len
);
470 fcgi_create_end_record(struct request
*c
)
472 struct fcgi_end_request_body end_request
;
474 memset(&end_request
, 0, sizeof(end_request
));
475 end_request
.app_status
= htonl(0); /* script status */
476 end_request
.protocol_status
= FCGI_REQUEST_COMPLETE
;
478 fcgi_send_response(c
, FCGI_END_REQUEST
, &end_request
,
479 sizeof(end_request
));
483 fcgi_cleanup_request(struct request
*c
)
488 evtimer_del(&c
->tmo
);
489 if (event_initialized(&c
->ev
))
493 template_free(c
->tp
);
495 gotweb_free_transport(c
->t
);
500 dump_fcgi_record(const char *p
, struct fcgi_record_header
*h
)
502 dump_fcgi_record_header(p
, h
);
504 if (h
->type
== FCGI_BEGIN_REQUEST
)
505 dump_fcgi_begin_request_body(p
,
506 (struct fcgi_begin_request_body
*)(h
+ 1));
507 else if (h
->type
== FCGI_END_REQUEST
)
508 dump_fcgi_end_request_body(p
,
509 (struct fcgi_end_request_body
*)(h
+ 1));
513 dump_fcgi_record_header(const char* p
, struct fcgi_record_header
*h
)
515 log_debug("%sversion: %d", p
, h
->version
);
516 log_debug("%stype: %d", p
, h
->type
);
517 log_debug("%srequestId: %d", p
, ntohs(h
->id
));
518 log_debug("%scontentLength: %d", p
, ntohs(h
->content_len
));
519 log_debug("%spaddingLength: %d", p
, h
->padding_len
);
520 log_debug("%sreserved: %d", p
, h
->reserved
);
524 dump_fcgi_begin_request_body(const char *p
, struct fcgi_begin_request_body
*b
)
526 log_debug("%srole %d", p
, ntohs(b
->role
));
527 log_debug("%sflags %d", p
, b
->flags
);
531 dump_fcgi_end_request_body(const char *p
, struct fcgi_end_request_body
*b
)
533 log_debug("%sappStatus: %d", p
, ntohl(b
->app_status
));
534 log_debug("%sprotocolStatus: %d", p
, b
->protocol_status
);