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"
43 size_t fcgi_parse_record(uint8_t *, size_t, struct request
*);
44 void fcgi_parse_begin_request(uint8_t *, uint16_t, struct request
*,
46 void fcgi_parse_params(uint8_t *, uint16_t, struct request
*, uint16_t);
47 int fcgi_send_response(struct request
*, int, const void *, size_t);
49 void dump_fcgi_record_header(const char *, struct fcgi_record_header
*);
50 void dump_fcgi_begin_request_body(const char *,
51 struct fcgi_begin_request_body
*);
52 void dump_fcgi_end_request_body(const char *,
53 struct fcgi_end_request_body
*);
55 extern int cgi_inflight
;
56 extern volatile int client_cnt
;
59 fcgi_request(int fd
, short events
, void *arg
)
61 struct request
*c
= arg
;
65 n
= read(fd
, c
->buf
+ c
->buf_pos
+ c
->buf_len
,
66 FCGI_RECORD_SIZE
- c
->buf_pos
-c
->buf_len
);
80 log_debug("closed connection");
89 * Parse the records as they are received. Per the FastCGI
90 * specification, the server need only receive the FastCGI
91 * parameter records in full; it is free to begin execution
92 * at that point, which is what happens here.
95 parsed
= fcgi_parse_record(c
->buf
+ c
->buf_pos
, c
->buf_len
, c
);
101 /* drop the parsed record */
102 if (parsed
!= 0 && c
->buf_len
> 0) {
103 bcopy(c
->buf
+ c
->buf_pos
, c
->buf
, c
->buf_len
);
106 } while (parsed
> 0 && c
->buf_len
> 0);
110 fcgi_cleanup_request(c
);
114 fcgi_parse_record(uint8_t *buf
, size_t n
, struct request
*c
)
116 struct fcgi_record_header
*h
;
118 if (n
< sizeof(struct fcgi_record_header
))
121 h
= (struct fcgi_record_header
*) buf
;
123 dump_fcgi_record("", h
);
125 if (n
< sizeof(struct fcgi_record_header
) + ntohs(h
->content_len
)
130 log_warn("wrong version");
133 case FCGI_BEGIN_REQUEST
:
134 fcgi_parse_begin_request(buf
+
135 sizeof(struct fcgi_record_header
),
136 ntohs(h
->content_len
), c
, ntohs(h
->id
));
139 fcgi_parse_params(buf
+ sizeof(struct fcgi_record_header
),
140 ntohs(h
->content_len
), c
, ntohs(h
->id
));
143 case FCGI_ABORT_REQUEST
:
144 fcgi_create_end_record(c
);
145 fcgi_cleanup_request(c
);
148 log_warn("unimplemented type %d", h
->type
);
152 return (sizeof(struct fcgi_record_header
) + ntohs(h
->content_len
)
157 fcgi_parse_begin_request(uint8_t *buf
, uint16_t n
,
158 struct request
*c
, uint16_t id
)
160 /* XXX -- FCGI_CANT_MPX_CONN */
161 if (c
->request_started
) {
162 log_warn("unexpected FCGI_BEGIN_REQUEST, ignoring");
166 if (n
!= sizeof(struct fcgi_begin_request_body
)) {
167 log_warn("wrong size %d != %lu", n
,
168 sizeof(struct fcgi_begin_request_body
));
172 c
->request_started
= 1;
177 fcgi_parse_params(uint8_t *buf
, uint16_t n
, struct request
*c
, uint16_t id
)
179 uint32_t name_len
, val_len
;
182 if (!c
->request_started
) {
183 log_warn("FCGI_PARAMS without FCGI_BEGIN_REQUEST, ignoring");
188 log_warn("unexpected id, ignoring");
193 gotweb_process_request(c
);
194 template_flush(c
->tp
);
199 if (buf
[0] >> 7 == 0) {
205 name_len
= ((buf
[0] & 0x7f) << 24) +
206 (buf
[1] << 16) + (buf
[2] << 8) + buf
[3];
216 if (buf
[0] >> 7 == 0) {
222 val_len
= ((buf
[0] & 0x7f) << 24) +
223 (buf
[1] << 16) + (buf
[2] << 8) +
231 if (n
< name_len
+ val_len
)
234 val
= buf
+ name_len
;
236 if (c
->querystring
[0] == '\0' &&
237 val_len
< MAX_QUERYSTRING
&&
239 strncmp(buf
, "QUERY_STRING", 12) == 0) {
240 memcpy(c
->querystring
, val
, val_len
);
241 c
->querystring
[val_len
] = '\0';
244 if (c
->document_uri
[0] == '\0' &&
245 val_len
< MAX_DOCUMENT_URI
&&
247 strncmp(buf
, "DOCUMENT_URI", 12) == 0) {
248 memcpy(c
->document_uri
, val
, val_len
);
249 c
->document_uri
[val_len
] = '\0';
252 if (c
->server_name
[0] == '\0' &&
253 val_len
< MAX_SERVER_NAME
&&
255 strncmp(buf
, "SERVER_NAME", 11) == 0) {
256 memcpy(c
->server_name
, val
, val_len
);
257 c
->server_name
[val_len
] = '\0';
261 strncmp(buf
, "HTTPS", 5) == 0)
264 buf
+= name_len
+ val_len
;
265 n
-= name_len
- val_len
;
270 fcgi_timeout(int fd
, short events
, void *arg
)
272 fcgi_cleanup_request((struct request
*) arg
);
276 send_response(struct request
*c
, int type
, const uint8_t *data
,
279 static const uint8_t padding
[FCGI_PADDING_SIZE
];
280 struct fcgi_record_header header
;
284 size_t padded_len
, tot
;
285 int i
, err
= 0, th
= 2000;
290 memset(&header
, 0, sizeof(header
));
293 header
.id
= htons(c
->id
);
294 header
.content_len
= htons(len
);
296 /* The FastCGI spec suggests to align the output buffer */
297 tot
= sizeof(header
) + len
;
298 padded_len
= FCGI_ALIGN(tot
);
299 if (padded_len
> tot
) {
300 header
.padding_len
= padded_len
- tot
;
301 tot
+= header
.padding_len
;
304 iov
[0].iov_base
= &header
;
305 iov
[0].iov_len
= sizeof(header
);
307 iov
[1].iov_base
= (void *)data
;
308 iov
[1].iov_len
= len
;
310 iov
[2].iov_base
= (void *)padding
;
311 iov
[2].iov_len
= header
.padding_len
;
313 dump_fcgi_record("resp ", &header
);
316 * XXX: add some simple write heuristics here
317 * On slower VMs, spotty connections, etc., we don't want to go right to
318 * disconnect. Let's at least try to write the data a few times before
322 nw
= writev(c
->fd
, iov
, nitems(iov
));
324 c
->sock
->client_status
= CLIENT_DISCONNECT
;
329 if (errno
== EAGAIN
&& err
< th
) {
330 nanosleep(&ts
, NULL
);
333 log_debug("%s: write failure: %s", __func__
,
335 c
->sock
->client_status
= CLIENT_DISCONNECT
;
340 log_debug("%s: partial write: %zu vs %zu", __func__
,
344 for (i
= 0; i
< nitems(iov
); ++i
) {
345 if (nw
< iov
[i
].iov_len
) {
346 iov
[i
].iov_base
+= nw
;
347 iov
[i
].iov_len
-= nw
;
350 nw
-= iov
[i
].iov_len
;
359 fcgi_send_response(struct request
*c
, int type
, const void *data
,
362 if (c
->sock
->client_status
== CLIENT_DISCONNECT
)
365 while (len
> FCGI_CONTENT_SIZE
) {
366 if (send_response(c
, type
, data
, len
) == -1)
369 data
+= FCGI_CONTENT_SIZE
;
370 len
-= FCGI_CONTENT_SIZE
;
376 return send_response(c
, type
, data
, len
);
380 fcgi_write(void *arg
, const void *buf
, size_t len
)
382 struct request
*c
= arg
;
384 return fcgi_send_response(c
, FCGI_STDOUT
, buf
, len
);
388 fcgi_create_end_record(struct request
*c
)
390 struct fcgi_end_request_body end_request
;
392 memset(&end_request
, 0, sizeof(end_request
));
393 end_request
.app_status
= htonl(0); /* script status */
394 end_request
.protocol_status
= FCGI_REQUEST_COMPLETE
;
396 fcgi_send_response(c
, FCGI_END_REQUEST
, &end_request
,
397 sizeof(end_request
));
401 fcgi_cleanup_request(struct request
*c
)
406 evtimer_del(&c
->tmo
);
407 if (event_initialized(&c
->ev
))
411 template_free(c
->tp
);
413 gotweb_free_transport(c
->t
);
418 dump_fcgi_record(const char *p
, struct fcgi_record_header
*h
)
420 dump_fcgi_record_header(p
, h
);
422 if (h
->type
== FCGI_BEGIN_REQUEST
)
423 dump_fcgi_begin_request_body(p
,
424 (struct fcgi_begin_request_body
*)(h
+ 1));
425 else if (h
->type
== FCGI_END_REQUEST
)
426 dump_fcgi_end_request_body(p
,
427 (struct fcgi_end_request_body
*)(h
+ 1));
431 dump_fcgi_record_header(const char* p
, struct fcgi_record_header
*h
)
433 log_debug("%sversion: %d", p
, h
->version
);
434 log_debug("%stype: %d", p
, h
->type
);
435 log_debug("%srequestId: %d", p
, ntohs(h
->id
));
436 log_debug("%scontentLength: %d", p
, ntohs(h
->content_len
));
437 log_debug("%spaddingLength: %d", p
, h
->padding_len
);
438 log_debug("%sreserved: %d", p
, h
->reserved
);
442 dump_fcgi_begin_request_body(const char *p
, struct fcgi_begin_request_body
*b
)
444 log_debug("%srole %d", p
, ntohs(b
->role
));
445 log_debug("%sflags %d", p
, b
->flags
);
449 dump_fcgi_end_request_body(const char *p
, struct fcgi_end_request_body
*b
)
451 log_debug("%sappStatus: %d", p
, ntohl(b
->app_status
));
452 log_debug("%sprotocolStatus: %d", p
, b
->protocol_status
);