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_request_body(const char *, struct fcgi_record_header
*);
51 void dump_fcgi_record_header(const char *, struct fcgi_record_header
*);
52 void dump_fcgi_begin_request_body(const char *,
53 struct fcgi_begin_request_body
*);
54 void dump_fcgi_end_request_body(const char *,
55 struct fcgi_end_request_body
*);
57 extern int cgi_inflight
;
58 extern volatile int client_cnt
;
61 fcgi_request(int fd
, short events
, void *arg
)
63 struct request
*c
= arg
;
67 n
= read(fd
, c
->buf
+ c
->buf_pos
+ c
->buf_len
,
68 FCGI_RECORD_SIZE
- c
->buf_pos
- c
->buf_len
);
82 log_info("closed connection");
91 * Parse the records as they are received. Per the FastCGI
92 * specification, the server need only receive the FastCGI
93 * parameter records in full; it is free to begin execution
94 * at that point, which is what happens here.
97 parsed
= fcgi_parse_record(c
->buf
+ c
->buf_pos
, c
->buf_len
, c
);
100 c
->buf_len
-= parsed
;
103 /* drop the parsed record */
104 if (parsed
!= 0 && c
->buf_len
> 0) {
105 memmove(c
->buf
, c
->buf
+ c
->buf_pos
, c
->buf_len
);
108 } while (parsed
> 0 && c
->buf_len
> 0);
112 fcgi_cleanup_request(c
);
116 fcgi_parse_record(uint8_t *buf
, size_t n
, struct request
*c
)
118 struct fcgi_record_header
*h
;
120 if (n
< sizeof(struct fcgi_record_header
))
123 h
= (struct fcgi_record_header
*) buf
;
125 dump_fcgi_record_header("", h
);
127 if (n
< sizeof(struct fcgi_record_header
) + ntohs(h
->content_len
)
131 dump_fcgi_request_body("", h
);
134 log_warn("wrong version");
137 case FCGI_BEGIN_REQUEST
:
138 fcgi_parse_begin_request(buf
+
139 sizeof(struct fcgi_record_header
),
140 ntohs(h
->content_len
), c
, ntohs(h
->id
));
143 fcgi_parse_params(buf
+ sizeof(struct fcgi_record_header
),
144 ntohs(h
->content_len
), c
, ntohs(h
->id
));
147 case FCGI_ABORT_REQUEST
:
148 fcgi_create_end_record(c
);
149 fcgi_cleanup_request(c
);
152 log_warn("unimplemented type %d", h
->type
);
156 return (sizeof(struct fcgi_record_header
) + ntohs(h
->content_len
)
161 fcgi_parse_begin_request(uint8_t *buf
, uint16_t n
,
162 struct request
*c
, uint16_t id
)
164 /* XXX -- FCGI_CANT_MPX_CONN */
165 if (c
->request_started
) {
166 log_warn("unexpected FCGI_BEGIN_REQUEST, ignoring");
170 if (n
!= sizeof(struct fcgi_begin_request_body
)) {
171 log_warn("wrong size %d != %lu", n
,
172 sizeof(struct fcgi_begin_request_body
));
176 c
->request_started
= 1;
181 fcgi_parse_params(uint8_t *buf
, uint16_t n
, struct request
*c
, uint16_t id
)
183 uint32_t name_len
, val_len
;
186 if (!c
->request_started
) {
187 log_warn("FCGI_PARAMS without FCGI_BEGIN_REQUEST, ignoring");
192 log_warn("unexpected id, ignoring");
197 gotweb_process_request(c
);
198 template_flush(c
->tp
);
203 if (buf
[0] >> 7 == 0) {
209 name_len
= ((buf
[0] & 0x7f) << 24) +
210 (buf
[1] << 16) + (buf
[2] << 8) + buf
[3];
220 if (buf
[0] >> 7 == 0) {
226 val_len
= ((buf
[0] & 0x7f) << 24) +
227 (buf
[1] << 16) + (buf
[2] << 8) +
235 if (n
< name_len
+ val_len
)
238 val
= buf
+ name_len
;
240 if (val_len
< MAX_QUERYSTRING
&&
242 strncmp(buf
, "QUERY_STRING", 12) == 0) {
243 memcpy(c
->querystring
, val
, val_len
);
244 c
->querystring
[val_len
] = '\0';
247 if (val_len
< MAX_DOCUMENT_URI
&&
249 strncmp(buf
, "DOCUMENT_URI", 12) == 0) {
250 memcpy(c
->document_uri
, val
, val_len
);
251 c
->document_uri
[val_len
] = '\0';
254 if (val_len
< MAX_SERVER_NAME
&&
256 strncmp(buf
, "SERVER_NAME", 11) == 0) {
257 memcpy(c
->server_name
, val
, val_len
);
258 c
->server_name
[val_len
] = '\0';
262 strncmp(buf
, "HTTPS", 5) == 0)
265 buf
+= name_len
+ val_len
;
266 n
-= name_len
- val_len
;
271 fcgi_timeout(int fd
, short events
, void *arg
)
273 fcgi_cleanup_request((struct request
*) arg
);
277 send_response(struct request
*c
, int type
, const uint8_t *data
,
280 static const uint8_t padding
[FCGI_PADDING_SIZE
];
281 struct fcgi_record_header header
;
285 size_t padded_len
, tot
;
286 int i
, err
= 0, th
= 2000;
291 memset(&header
, 0, sizeof(header
));
294 header
.id
= htons(c
->id
);
295 header
.content_len
= htons(len
);
297 /* The FastCGI spec suggests to align the output buffer */
298 tot
= sizeof(header
) + len
;
299 padded_len
= FCGI_ALIGN(tot
);
300 if (padded_len
> tot
) {
301 header
.padding_len
= padded_len
- tot
;
302 tot
+= header
.padding_len
;
305 iov
[0].iov_base
= &header
;
306 iov
[0].iov_len
= sizeof(header
);
308 iov
[1].iov_base
= (void *)data
;
309 iov
[1].iov_len
= len
;
311 iov
[2].iov_base
= (void *)padding
;
312 iov
[2].iov_len
= header
.padding_len
;
314 dump_fcgi_record_header("resp ", &header
);
317 * XXX: add some simple write heuristics here
318 * On slower VMs, spotty connections, etc., we don't want to go right to
319 * disconnect. Let's at least try to write the data a few times before
323 nw
= writev(c
->fd
, iov
, nitems(iov
));
325 c
->sock
->client_status
= CLIENT_DISCONNECT
;
330 if (errno
== EAGAIN
&& err
< th
) {
331 nanosleep(&ts
, NULL
);
334 log_warn("%s: write failure", __func__
);
335 c
->sock
->client_status
= CLIENT_DISCONNECT
;
340 log_warnx("%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_request_body(const char *p
, struct fcgi_record_header
*h
)
420 if (h
->type
== FCGI_BEGIN_REQUEST
)
421 dump_fcgi_begin_request_body(p
,
422 (struct fcgi_begin_request_body
*)(h
+ 1));
423 else if (h
->type
== FCGI_END_REQUEST
)
424 dump_fcgi_end_request_body(p
,
425 (struct fcgi_end_request_body
*)(h
+ 1));
429 dump_fcgi_record_header(const char* p
, struct fcgi_record_header
*h
)
431 log_debug("%sversion: %d", p
, h
->version
);
432 log_debug("%stype: %d", p
, h
->type
);
433 log_debug("%srequestId: %d", p
, ntohs(h
->id
));
434 log_debug("%scontentLength: %d", p
, ntohs(h
->content_len
));
435 log_debug("%spaddingLength: %d", p
, h
->padding_len
);
436 log_debug("%sreserved: %d", p
, h
->reserved
);
440 dump_fcgi_begin_request_body(const char *p
, struct fcgi_begin_request_body
*b
)
442 log_debug("%srole %d", p
, ntohs(b
->role
));
443 log_debug("%sflags %d", p
, b
->flags
);
447 dump_fcgi_end_request_body(const char *p
, struct fcgi_end_request_body
*b
)
449 log_debug("%sappStatus: %d", p
, ntohl(b
->app_status
));
450 log_debug("%sprotocolStatus: %d", p
, b
->protocol_status
);