From 24daa5813f277023f7b8453338870369bf00a3ca Mon Sep 17 00:00:00 2001 From: Laurent Bercot Date: Sat, 4 Jul 2026 04:03:12 +0000 Subject: Implement FastCGI, initial version --- src/include/tipidee/fcgi.h | 127 +++------ src/include/tipidee/headers.h | 1 + src/include/tipidee/tipidee.h | 1 + src/libtipidee/deps-lib/tipidee | 24 +- src/libtipidee/tipidee_fcgi_addenv.c | 41 +++ src/libtipidee/tipidee_fcgi_beginrequest.c | 9 + .../tipidee_fcgi_beginrequest_body_pack.c | 14 - .../tipidee_fcgi_beginrequest_body_unpack.c | 14 - .../tipidee_fcgi_beginrequest_record_pack.c | 9 - .../tipidee_fcgi_beginrequest_record_unpack.c | 9 - src/libtipidee/tipidee_fcgi_endrequest_body_pack.c | 14 - .../tipidee_fcgi_endrequest_body_unpack.c | 14 - .../tipidee_fcgi_endrequest_record_pack.c | 9 - .../tipidee_fcgi_endrequest_record_unpack.c | 9 - src/libtipidee/tipidee_fcgi_flush.c | 10 + src/libtipidee/tipidee_fcgi_header_pack.c | 15 - src/libtipidee/tipidee_fcgi_header_unpack.c | 14 - src/libtipidee/tipidee_fcgi_params.c | 8 + src/libtipidee/tipidee_fcgi_put.c | 34 +++ src/libtipidee/tipidee_fcgi_putv.c | 47 +++ src/libtipidee/tipidee_fcgi_read.c | 59 ++++ src/libtipidee/tipidee_fcgi_startread.c | 12 + src/libtipidee/tipidee_fcgi_startwrite.c | 12 + src/libtipidee/tipidee_fcgi_stdin.c | 8 + .../tipidee_fcgi_unknowntype_body_pack.c | 11 - .../tipidee_fcgi_unknowntype_body_unpack.c | 11 - .../tipidee_fcgi_unknowntype_record_pack.c | 9 - .../tipidee_fcgi_unknowntype_record_unpack.c | 9 - src/libtipidee/tipidee_headers_parse.c | 46 ++- src/tipideed/cgi.c | 107 +------ src/tipideed/deps-exe/tipideed | 1 + src/tipideed/fastcgi.c | 314 +++++++++++++++++++-- src/tipideed/process_cgi_headers.c | 115 ++++++++ src/tipideed/tipideed-internal.h | 9 +- src/tipideed/tipideed.c | 2 +- 35 files changed, 754 insertions(+), 394 deletions(-) create mode 100644 src/libtipidee/tipidee_fcgi_addenv.c create mode 100644 src/libtipidee/tipidee_fcgi_beginrequest.c delete mode 100644 src/libtipidee/tipidee_fcgi_beginrequest_body_pack.c delete mode 100644 src/libtipidee/tipidee_fcgi_beginrequest_body_unpack.c delete mode 100644 src/libtipidee/tipidee_fcgi_beginrequest_record_pack.c delete mode 100644 src/libtipidee/tipidee_fcgi_beginrequest_record_unpack.c delete mode 100644 src/libtipidee/tipidee_fcgi_endrequest_body_pack.c delete mode 100644 src/libtipidee/tipidee_fcgi_endrequest_body_unpack.c delete mode 100644 src/libtipidee/tipidee_fcgi_endrequest_record_pack.c delete mode 100644 src/libtipidee/tipidee_fcgi_endrequest_record_unpack.c create mode 100644 src/libtipidee/tipidee_fcgi_flush.c delete mode 100644 src/libtipidee/tipidee_fcgi_header_pack.c delete mode 100644 src/libtipidee/tipidee_fcgi_header_unpack.c create mode 100644 src/libtipidee/tipidee_fcgi_params.c create mode 100644 src/libtipidee/tipidee_fcgi_put.c create mode 100644 src/libtipidee/tipidee_fcgi_putv.c create mode 100644 src/libtipidee/tipidee_fcgi_read.c create mode 100644 src/libtipidee/tipidee_fcgi_startread.c create mode 100644 src/libtipidee/tipidee_fcgi_startwrite.c create mode 100644 src/libtipidee/tipidee_fcgi_stdin.c delete mode 100644 src/libtipidee/tipidee_fcgi_unknowntype_body_pack.c delete mode 100644 src/libtipidee/tipidee_fcgi_unknowntype_body_unpack.c delete mode 100644 src/libtipidee/tipidee_fcgi_unknowntype_record_pack.c delete mode 100644 src/libtipidee/tipidee_fcgi_unknowntype_record_unpack.c create mode 100644 src/tipideed/process_cgi_headers.c (limited to 'src') diff --git a/src/include/tipidee/fcgi.h b/src/include/tipidee/fcgi.h index be2f0af..597702d 100644 --- a/src/include/tipidee/fcgi.h +++ b/src/include/tipidee/fcgi.h @@ -4,17 +4,50 @@ #define TIPIDEE_FCGI_H #include +#include +#include +#include -typedef struct fcgi_header_s fcgi_header, *fcgi_header_ref ; -struct fcgi_header_s +#include +#include +#include + +typedef struct tipidee_fcgi_s tipidee_fcgi, *tipidee_fcgi_ref ; +struct tipidee_fcgi_s { - uint8_t version ; - uint8_t type ; - uint16_t requestid ; - uint16_t len ; - uint8_t padlen ; - uint8_t reserved ; + buffer b ; + char buf[4096] ; + tain deadline ; + uint16_t id ; } ; +#define TIPIDEE_FCGI_ZERO { .b = BUFFER_ZERO, .deadline = TAIN_ZERO, .id = 0 } + +extern void tipidee_fcgi_startwrite (tipidee_fcgi *, int, pid_t, tain const *) ; +extern void tipidee_fcgi_startread (tipidee_fcgi *, int, pid_t, tain const *) ; + +extern size_t tipidee_fcgi_put (tipidee_fcgi *, uint8_t, char const *, size_t, tain *) ; +#define tipidee_fcgi_put_g(fc, rectype, s, len) tipidee_fcgi_put(fc, rectype, s, (len), &STAMP) + +extern size_t tipidee_fcgi_putv (tipidee_fcgi *, uint8_t, struct iovec const *, unsigned int, tain *) ; +#define tipidee_fcgi_putv_g(fc, rectype, v, n) tipidee_fcgi_putv(fc, rectype, v, (n), &STAMP) + +extern int tipidee_fcgi_flush (tipidee_fcgi *, tain *) ; +#define tipidee_fcgi_flush_g(fc) tipidee_fcgi_flush((fc), &STAMP) + +extern int tipidee_fcgi_beginrequest (tipidee_fcgi *, tain *) ; +#define tipidee_fcgi_beginrequest_g(fc) tipidee_fcgi_beginrequest((fc), &STAMP) + +extern int tipidee_fcgi_addenvb (stralloc *, char const *, size_t, char const *, size_t) ; +#define tipidee_fcgi_addenv(sa, key, val) tipidee_fcgi_addenvb(sa, key, strlen(key), val, strlen(val)) + +extern int tipidee_fcgi_params (tipidee_fcgi *, char const *, size_t, tain *) ; +#define tipidee_fcgi_params_g(fc, s, len) tipidee_fcgi_params(fc, s, (len), &STAMP) + +extern int tipidee_fcgi_stdin (tipidee_fcgi *, char const *, size_t, tain *) ; +#define tipidee_fcgi_stdin_g(fc, s, len) tipidee_fcgi_stdin(fc, s, (len), &STAMP) + +extern int tipidee_fcgi_read (tipidee_fcgi *, uint32_t *, stralloc *, tain *) ; +#define tipidee_fcgi_read_g(fc, status, sa) tipidee_fcgi_read(fc, status, (sa), &STAMP) typedef enum fcgi_type_e fcgi_type ; enum fcgi_type_e @@ -30,33 +63,11 @@ enum fcgi_type_e FCGI_GET_VALUES, FCGI_GET_VALUES_RESULT, FCGI_UNKNOWN_TYPE, - FCGI_MAXTYPE -} ; - -#define FCGI_NULL_REQUEST_ID 0 - -extern void tipidee_fcgi_header_pack (char *, fcgi_header const *) ; -extern void tipidee_fcgi_header_unpack (char const *, fcgi_header *) ; - - -typedef struct fcgi_beginrequest_body_s fcgi_beginrequest_body, *fcgi_beginrequest_body_ref ; -struct fcgi_beginrequest_body_s -{ - uint16_t role ; - uint8_t flags; - unsigned char reserved[5] ; } ; -extern void tipidee_fcgi_beginrequest_body_pack (char *, fcgi_beginrequest_body const *) ; -extern void tipidee_fcgi_beginrequest_body_unpack (char const *, fcgi_beginrequest_body *) ; +#define FCGI_VERSION_1 1 - -typedef struct fcgi_beginrequest_record_s fcgi_beginrequest_record, *fcgi_beginrequest_record_ref ; -struct fcgi_beginrequest_record_s -{ - fcgi_header header ; - fcgi_beginrequest_body body ; -} ; +#define FCGI_NULL_REQUEST_ID 0 #define FCGI_KEEP_CONN 1 @@ -64,61 +75,9 @@ struct fcgi_beginrequest_record_s #define FCGI_AUTHORIZER 2 #define FCGI_FILTER 3 -extern void tipidee_fcgi_beginrequest_record_pack (char *, fcgi_beginrequest_record const *) ; -extern void tipidee_fcgi_beginrequest_record_unpack (char const *, fcgi_beginrequest_record *) ; - - -typedef struct fcgi_endrequest_body_s fcgi_endrequest_body, *fcgi_endrequest_body_ref ; -struct fcgi_endrequest_body_s -{ - uint32_t appstatus ; - uint8_t protostatus ; - uint8_t reserved[3] ; -} ; - -extern void tipidee_fcgi_endrequest_body_pack (char *, fcgi_endrequest_body const *) ; -extern void tipidee_fcgi_endrequest_body_unpack (char const *, fcgi_endrequest_body *) ; - - -typedef struct fcgi_endrequest_record_s fcgi_endrequest_record, *fcgi_endrequest_record_ref ; -struct fcgi_endrequest_record_s -{ - fcgi_header header ; - fcgi_endrequest_body body ; -} ; - #define FCGI_REQUEST_COMPLETE 0 #define FCGI_CANT_MPX_CONN 1 #define FCGI_OVERLOADED 2 #define FCGI_UNKNOWN_ROLE 3 -#define FCGI_MAX_CONNS "FCGI_MAX_CONNS" -#define FCGI_MAX_REQS "FCGI_MAX_REQS" -#define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS" - -extern void tipidee_fcgi_endrequest_record_pack (char *, fcgi_endrequest_record const *) ; -extern void tipidee_fcgi_endrequest_record_unpack (char const *, fcgi_endrequest_record *) ; - - -typedef struct fcgi_unknowntype_body_s fcgi_unknowntype_body, *fcgi_unknowntype_body_ref ; -struct fcgi_unknowntype_body_s -{ - uint8_t type ; - uint8_t reserved[7] ; -} ; - -extern void tipidee_fcgi_unknowntype_body_pack (char *, fcgi_unknowntype_body const *) ; -extern void tipidee_fcgi_unknowntype_body_unpack (char const *, fcgi_unknowntype_body *) ; - - -typedef struct fcgi_unknowntype_record_s fcgi_unknowntype_record, *fcgi_unknowntype_record_ref ; -struct fcgi_unknowntype_record_s -{ - fcgi_header header ; - fcgi_unknowntype_body body ; -} ; - -extern void tipidee_fcgi_unknowntype_record_pack (char *, fcgi_unknowntype_record const *) ; -extern void tipidee_fcgi_unknowntype_record_unpack (char const *, fcgi_unknowntype_record *) ; - #endif diff --git a/src/include/tipidee/headers.h b/src/include/tipidee/headers.h index 420b4c9..d4a1a10 100644 --- a/src/include/tipidee/headers.h +++ b/src/include/tipidee/headers.h @@ -28,6 +28,7 @@ struct tipidee_headers_s extern void tipidee_headers_init (tipidee_headers *, char *, size_t) ; +extern int tipidee_headers_parse_fromstring_nb (char const *, size_t, size_t *, tipidee_headers *, disize *, uint32_t *) ; extern int tipidee_headers_parse_nb (buffer *, tipidee_headers *, disize *, uint32_t *) ; extern int tipidee_headers_timed_parse (buffer *, tipidee_headers *, tain const *, tain *) ; #define tipidee_headers_timed_parse_g(b, hdr, deadline) tipidee_headers_timed_parse(b, hdr, (deadline), &STAMP) diff --git a/src/include/tipidee/tipidee.h b/src/include/tipidee/tipidee.h index 50ee7bf..ea0ca72 100644 --- a/src/include/tipidee/tipidee.h +++ b/src/include/tipidee/tipidee.h @@ -5,6 +5,7 @@ #include #include +#include #include #include #include diff --git a/src/libtipidee/deps-lib/tipidee b/src/libtipidee/deps-lib/tipidee index 1a73f52..497b74a 100644 --- a/src/libtipidee/deps-lib/tipidee +++ b/src/libtipidee/deps-lib/tipidee @@ -10,20 +10,16 @@ tipidee_conf_get_responseheaders.o tipidee_conf_get_string.o tipidee_conf_get_uint32.o tipidee_conf_init.o -tipidee_fcgi_beginrequest_body_pack.o -tipidee_fcgi_beginrequest_body_unpack.o -tipidee_fcgi_beginrequest_record_pack.o -tipidee_fcgi_beginrequest_record_unpack.o -tipidee_fcgi_endrequest_body_pack.o -tipidee_fcgi_endrequest_body_unpack.o -tipidee_fcgi_endrequest_record_pack.o -tipidee_fcgi_endrequest_record_unpack.o -tipidee_fcgi_header_pack.o -tipidee_fcgi_header_unpack.o -tipidee_fcgi_unknowntype_body_pack.o -tipidee_fcgi_unknowntype_body_unpack.o -tipidee_fcgi_unknowntype_record_pack.o -tipidee_fcgi_unknowntype_record_unpack.o +tipidee_fcgi_addenv.o +tipidee_fcgi_beginrequest.o +tipidee_fcgi_flush.o +tipidee_fcgi_params.o +tipidee_fcgi_put.o +tipidee_fcgi_putv.o +tipidee_fcgi_read.o +tipidee_fcgi_startread.o +tipidee_fcgi_startwrite.o +tipidee_fcgi_stdin.o tipidee_headers_get_content_length.o tipidee_headers_init.o tipidee_headers_parse.o diff --git a/src/libtipidee/tipidee_fcgi_addenv.c b/src/libtipidee/tipidee_fcgi_addenv.c new file mode 100644 index 0000000..fb9e859 --- /dev/null +++ b/src/libtipidee/tipidee_fcgi_addenv.c @@ -0,0 +1,41 @@ +/* ISC license. */ + +#include + +#include +#include + +#include + +static int encode (stralloc *sa, size_t n) +{ + if (n <= 0x7f) + { + char c = n ; + return stralloc_catb(sa, &c, 1) ; + } + else if (n < 0x7ffffffful) + { + char pack[4] ; + uint32_pack_big(pack, n | 0x80000000) ; + return stralloc_catb(sa, pack, 4) ; + } + else return (errno = EOVERFLOW, 0) ; +} + +int tipidee_fcgi_addenvb (stralloc *sa, char const *key, size_t keylen, char const *val, size_t vallen) +{ + size_t start = sa->len ; + if (!encode(sa, keylen)) return 0 ; + if (!encode(sa, vallen) + || !stralloc_catb(sa, key, keylen) + || !stralloc_catb(sa, val, vallen)) goto err ; + if (sa->len > 0x7fff) goto err0 ; + return 1 ; + + err0: + errno = E2BIG ; + err: + sa->len = start ; + return 0 ; +} diff --git a/src/libtipidee/tipidee_fcgi_beginrequest.c b/src/libtipidee/tipidee_fcgi_beginrequest.c new file mode 100644 index 0000000..8a5a9a9 --- /dev/null +++ b/src/libtipidee/tipidee_fcgi_beginrequest.c @@ -0,0 +1,9 @@ +/* ISC license. */ + +#include + +int tipidee_fcgi_beginrequest (tipidee_fcgi *fc, tain *stamp) +{ + char body[8] = { 0, FCGI_RESPONDER, FCGI_KEEP_CONN, 0, 0, 0, 0, 0 } ; + return tipidee_fcgi_put(fc, FCGI_BEGIN_REQUEST, body, 8, stamp) == 8 ; +} diff --git a/src/libtipidee/tipidee_fcgi_beginrequest_body_pack.c b/src/libtipidee/tipidee_fcgi_beginrequest_body_pack.c deleted file mode 100644 index 400be69..0000000 --- a/src/libtipidee/tipidee_fcgi_beginrequest_body_pack.c +++ /dev/null @@ -1,14 +0,0 @@ -/* ISC license. */ - -#include - -#include - -#include - -void tipidee_fcgi_beginrequest_body_pack (char *s, fcgi_beginrequest_body const *bd) -{ - uint16_pack_big(s, bd->role) ; s += 2 ; - *s++ = bd->flags ; - memset(s, 0, 5) ; -} diff --git a/src/libtipidee/tipidee_fcgi_beginrequest_body_unpack.c b/src/libtipidee/tipidee_fcgi_beginrequest_body_unpack.c deleted file mode 100644 index a729b03..0000000 --- a/src/libtipidee/tipidee_fcgi_beginrequest_body_unpack.c +++ /dev/null @@ -1,14 +0,0 @@ -/* ISC license. */ - -#include - -#include - -#include - -void tipidee_fcgi_beginrequest_body_unpack (char const *s, fcgi_beginrequest_body *bd) -{ - uint16_unpack_big(s, &bd->role) ; s += 2 ; - bd->flags = *s++ ; - memcpy((char *)bd->reserved, s, 5) ; s += 5 ; -} diff --git a/src/libtipidee/tipidee_fcgi_beginrequest_record_pack.c b/src/libtipidee/tipidee_fcgi_beginrequest_record_pack.c deleted file mode 100644 index 3329e8d..0000000 --- a/src/libtipidee/tipidee_fcgi_beginrequest_record_pack.c +++ /dev/null @@ -1,9 +0,0 @@ -/* ISC license. */ - -#include - -void tipidee_fcgi_beginrequest_record_pack (char *s, fcgi_beginrequest_record const *rec) -{ - tipidee_fcgi_header_pack(s, &rec->header) ; s += 8 ; - tipidee_fcgi_beginrequest_body_pack(s, &rec->body) ; -} diff --git a/src/libtipidee/tipidee_fcgi_beginrequest_record_unpack.c b/src/libtipidee/tipidee_fcgi_beginrequest_record_unpack.c deleted file mode 100644 index c68a08f..0000000 --- a/src/libtipidee/tipidee_fcgi_beginrequest_record_unpack.c +++ /dev/null @@ -1,9 +0,0 @@ -/* ISC license. */ - -#include - -void tipidee_fcgi_beginrequest_record_unpack (char const *s, fcgi_beginrequest_record *rec) -{ - tipidee_fcgi_header_unpack(s, &rec->header) ; s += 8 ; - tipidee_fcgi_beginrequest_body_unpack(s, &rec->body) ; -} diff --git a/src/libtipidee/tipidee_fcgi_endrequest_body_pack.c b/src/libtipidee/tipidee_fcgi_endrequest_body_pack.c deleted file mode 100644 index d289d6d..0000000 --- a/src/libtipidee/tipidee_fcgi_endrequest_body_pack.c +++ /dev/null @@ -1,14 +0,0 @@ -/* ISC license. */ - -#include - -#include - -#include - -void tipidee_fcgi_endrequest_body_pack (char *s, fcgi_endrequest_body const *bd) -{ - uint32_pack_big(s, bd->appstatus) ; s += 4 ; - *s++ = bd->protostatus ; - memset(s, 0, 3) ; -} diff --git a/src/libtipidee/tipidee_fcgi_endrequest_body_unpack.c b/src/libtipidee/tipidee_fcgi_endrequest_body_unpack.c deleted file mode 100644 index 9f52f18..0000000 --- a/src/libtipidee/tipidee_fcgi_endrequest_body_unpack.c +++ /dev/null @@ -1,14 +0,0 @@ -/* ISC license. */ - -#include - -#include - -#include - -void tipidee_fcgi_endrequest_body_unpack (char const *s, fcgi_endrequest_body *bd) -{ - uint32_unpack_big(s, &bd->appstatus) ; s += 4 ; - bd->protostatus = *s++ ; - memcpy((char *)bd->reserved, s, 3) ; s += 3 ; -} diff --git a/src/libtipidee/tipidee_fcgi_endrequest_record_pack.c b/src/libtipidee/tipidee_fcgi_endrequest_record_pack.c deleted file mode 100644 index 46f3ee9..0000000 --- a/src/libtipidee/tipidee_fcgi_endrequest_record_pack.c +++ /dev/null @@ -1,9 +0,0 @@ -/* ISC license. */ - -#include - -void tipidee_fcgi_endrequest_record_pack (char *s, fcgi_endrequest_record const *rec) -{ - tipidee_fcgi_header_pack(s, &rec->header) ; s += 8 ; - tipidee_fcgi_endrequest_body_pack(s, &rec->body) ; -} diff --git a/src/libtipidee/tipidee_fcgi_endrequest_record_unpack.c b/src/libtipidee/tipidee_fcgi_endrequest_record_unpack.c deleted file mode 100644 index dc0acd5..0000000 --- a/src/libtipidee/tipidee_fcgi_endrequest_record_unpack.c +++ /dev/null @@ -1,9 +0,0 @@ -/* ISC license. */ - -#include - -void tipidee_fcgi_endrequest_record_unpack (char const *s, fcgi_endrequest_record *rec) -{ - tipidee_fcgi_header_unpack(s, &rec->header) ; s += 8 ; - tipidee_fcgi_endrequest_body_unpack(s, &rec->body) ; -} diff --git a/src/libtipidee/tipidee_fcgi_flush.c b/src/libtipidee/tipidee_fcgi_flush.c new file mode 100644 index 0000000..f32f8d4 --- /dev/null +++ b/src/libtipidee/tipidee_fcgi_flush.c @@ -0,0 +1,10 @@ +/* ISC license. */ + +#include + +#include + +int tipidee_fcgi_flush (tipidee_fcgi *fc, tain *stamp) +{ + return buffer_timed_flush(&fc->b, &fc->deadline, stamp) ; +} diff --git a/src/libtipidee/tipidee_fcgi_header_pack.c b/src/libtipidee/tipidee_fcgi_header_pack.c deleted file mode 100644 index 2f1a550..0000000 --- a/src/libtipidee/tipidee_fcgi_header_pack.c +++ /dev/null @@ -1,15 +0,0 @@ -/* ISC license. */ - -#include - -#include - -void tipidee_fcgi_header_pack (char *s, fcgi_header const *hdr) -{ - *s++ = hdr->version ; - *s++ = hdr->type ; - uint16_pack_big(s, hdr->requestid) ; s += 2 ; - uint16_pack_big(s, hdr->len) ; s += 2 ; - *s++ = hdr->padlen ; - *s++ = 0 ; -} diff --git a/src/libtipidee/tipidee_fcgi_header_unpack.c b/src/libtipidee/tipidee_fcgi_header_unpack.c deleted file mode 100644 index 53debd7..0000000 --- a/src/libtipidee/tipidee_fcgi_header_unpack.c +++ /dev/null @@ -1,14 +0,0 @@ -/* ISC license. */ - -#include - -#include - -void tipidee_fcgi_header_unpack (char const *s, fcgi_header *hdr) -{ - hdr->version = *s++ ; - hdr->type = *s++ ; - uint16_unpack_big(s, &hdr->requestid) ; s += 2 ; - uint16_unpack_big(s, &hdr->len) ; s += 2 ; - hdr->padlen = *s++ ; -} diff --git a/src/libtipidee/tipidee_fcgi_params.c b/src/libtipidee/tipidee_fcgi_params.c new file mode 100644 index 0000000..cac4e50 --- /dev/null +++ b/src/libtipidee/tipidee_fcgi_params.c @@ -0,0 +1,8 @@ +/* ISC license. */ + +#include + +int tipidee_fcgi_params (tipidee_fcgi *fc, char const *s, size_t len, tain *stamp) +{ + return tipidee_fcgi_put(fc, FCGI_PARAMS, s, len, stamp) == len ; +} diff --git a/src/libtipidee/tipidee_fcgi_put.c b/src/libtipidee/tipidee_fcgi_put.c new file mode 100644 index 0000000..0f4b493 --- /dev/null +++ b/src/libtipidee/tipidee_fcgi_put.c @@ -0,0 +1,34 @@ +/* ISC license. */ + +#include + +#include +#include +#include + +#include + +static uint16_t putit (tipidee_fcgi *fc, uint8_t rectype, char const *s, uint16_t len, tain *stamp) +{ + char hdr[8] = { 1, (char)rectype, fc->id >> 8, fc->id & 0xff, 0, 0, 0, 0 } ; + uint16_t w ; + uint16_pack_big(hdr + 4, len) ; + w = buffer_timed_put(&fc->b, hdr, 8, &fc->deadline, stamp) ; + if (w < 8) return 0 ; + return buffer_timed_put(&fc->b, s, len, &fc->deadline, stamp) ; +} + +size_t tipidee_fcgi_put (tipidee_fcgi *fc, uint8_t rectype, char const *s, size_t len, tain *stamp) +{ + size_t w = 0 ; + while (len > 0xffffu) + { + uint16_t r = putit(fc, rectype, s, 0xffffu, stamp) ; + w += r ; + if (r < 0xffffu) return w ; + s += 0xffff ; + len -= 0xffff ; + } + w += putit(fc, rectype, s, len, stamp) ; + return w ; +} diff --git a/src/libtipidee/tipidee_fcgi_putv.c b/src/libtipidee/tipidee_fcgi_putv.c new file mode 100644 index 0000000..32f5ba5 --- /dev/null +++ b/src/libtipidee/tipidee_fcgi_putv.c @@ -0,0 +1,47 @@ +/* ISC license. */ + +#include +#include +#include + +#include +#include +#include +#include + +#include + +static uint16_t putvit (tipidee_fcgi *fc, uint8_t rectype, struct iovec const *v, unsigned int n, uint16_t len, tain *stamp) +{ + char hdr[8] = { 1, (char)rectype, fc->id >> 8, fc->id & 0xff, 0, 0, 0, 0 } ; + uint16_t w ; + uint16_pack_big(hdr + 4, len) ; + w = buffer_timed_put(&fc->b, hdr, 8, &fc->deadline, stamp) ; + if (w < 8) return 0 ; + return buffer_timed_putv(&fc->b, v, n, &fc->deadline, stamp) ; +} + +size_t tipidee_fcgi_putv (tipidee_fcgi *fc, uint8_t rectype, struct iovec const *v, unsigned int n, tain *stamp) +{ + if (!n) return 0 ; + size_t w = 0 ; + unsigned int base = 0 ; + for (unsigned int i = 0 ; i < n ; i++) + if (v[i].iov_len > 0xffffu) return (errno = EMSGSIZE, 0) ; + while (base < n) + { + size_t len = 0 ; + unsigned int i = 0 ; + uint16_t r ; + for (; base + i < n ; i++) + { + if (len + v[base + i].iov_len > 0xffffu) break ; + len += v[base + i].iov_len ; + } + r = putvit(fc, rectype, v + base, i, len, stamp) ; + w += r ; + if (r < len) break ; + base += i ; + } + return w ; +} diff --git a/src/libtipidee/tipidee_fcgi_read.c b/src/libtipidee/tipidee_fcgi_read.c new file mode 100644 index 0000000..9b6dcb0 --- /dev/null +++ b/src/libtipidee/tipidee_fcgi_read.c @@ -0,0 +1,59 @@ +/* ISC license. */ + +#include +#include + +#include +#include +#include +#include + +#include + +#include + +int tipidee_fcgi_read (tipidee_fcgi *fc, uint32_t *status, stralloc *sa, tain *stamp) +{ + int stream ; + uint16_t u ; + char hdr[8] ; + if (buffer_timed_get(&fc->b, hdr, 8, &fc->deadline, stamp) < 8) return -1 ; + if (hdr[0] != 1) return (errno = EPROTO, -1) ; + uint16_unpack_big(hdr + 2, &u) ; + if (u != fc->id) return (errno = EPROTO, -1) ; + uint16_unpack_big(hdr + 4, &u) ; + switch (hdr[1]) + { + case FCGI_STDOUT : stream = 1 ; + case FCGI_STDERR : stream = 2 ; + case FCGI_END_REQUEST : stream = 0 ; + default : return (errno = EPROTO, -1) ; + } + if (stream) + { + if (!stralloc_readyplus(sa, u)) return -1 ; + if (buffer_timed_get(&fc->b, sa->s + sa->len, u, &fc->deadline, stamp) < u) return -1 ; + sa->len += u ; + } + else + { + char endreq[8] ; + if (u != 8) return (errno = EPROTO, -1) ; + if (buffer_timed_get(&fc->b, endreq, 8, &fc->deadline, stamp) < 8) return -1 ; + switch (endreq[4]) + { + case FCGI_REQUEST_COMPLETE : break ; + case FCGI_CANT_MPX_CONN : return (errno = ECONNREFUSED, -1) ; + case FCGI_OVERLOADED : return (errno = ENFILE, -1) ; + case FCGI_UNKNOWN_ROLE : return (errno = EAFNOSUPPORT, -1) ; + default : return (errno = EPROTO, 1) ; + } + uint32_unpack_big(endreq, status) ; + } + if (hdr[6]) + { + char tmp[(uint8_t)hdr[6]] ; + if (buffer_timed_get(&fc->b, tmp, (uint8_t)hdr[6], &fc->deadline, stamp)) return -1 ; + } + return stream ; +} diff --git a/src/libtipidee/tipidee_fcgi_startread.c b/src/libtipidee/tipidee_fcgi_startread.c new file mode 100644 index 0000000..4899e55 --- /dev/null +++ b/src/libtipidee/tipidee_fcgi_startread.c @@ -0,0 +1,12 @@ +/* ISC license. */ + +#include + +#include + +void tipidee_fcgi_startread (tipidee_fcgi *fc, int fd, pid_t pid, tain const *deadline) +{ + buffer_init(&fc->b, &buffer_read, fd, fc->buf, 4096) ; + fc->deadline = *deadline ; + fc->id = pid & 0xffffu ; fc->id += !fc->id ; +} diff --git a/src/libtipidee/tipidee_fcgi_startwrite.c b/src/libtipidee/tipidee_fcgi_startwrite.c new file mode 100644 index 0000000..12fd628 --- /dev/null +++ b/src/libtipidee/tipidee_fcgi_startwrite.c @@ -0,0 +1,12 @@ +/* ISC license. */ + +#include + +#include + +void tipidee_fcgi_startwrite (tipidee_fcgi *fc, int fd, pid_t pid, tain const *deadline) +{ + buffer_init(&fc->b, &buffer_write, fd, fc->buf, 4096) ; + fc->deadline = *deadline ; + fc->id = pid & 0xffffu ; fc->id += !fc->id ; +} diff --git a/src/libtipidee/tipidee_fcgi_stdin.c b/src/libtipidee/tipidee_fcgi_stdin.c new file mode 100644 index 0000000..b1a641a --- /dev/null +++ b/src/libtipidee/tipidee_fcgi_stdin.c @@ -0,0 +1,8 @@ +/* ISC license. */ + +#include + +int tipidee_fcgi_stdin (tipidee_fcgi *fc, char const *s, size_t len, tain *stamp) +{ + return tipidee_fcgi_put(fc, FCGI_STDIN, s, len, stamp) == len ; +} diff --git a/src/libtipidee/tipidee_fcgi_unknowntype_body_pack.c b/src/libtipidee/tipidee_fcgi_unknowntype_body_pack.c deleted file mode 100644 index f99da8f..0000000 --- a/src/libtipidee/tipidee_fcgi_unknowntype_body_pack.c +++ /dev/null @@ -1,11 +0,0 @@ -/* ISC license. */ - -#include - -#include - -void tipidee_fcgi_unknowntype_body_pack (char *s, fcgi_unknowntype_body const *bd) -{ - *s++ = bd->type ; - memset(s, 0, 7) ; -} diff --git a/src/libtipidee/tipidee_fcgi_unknowntype_body_unpack.c b/src/libtipidee/tipidee_fcgi_unknowntype_body_unpack.c deleted file mode 100644 index eb0684c..0000000 --- a/src/libtipidee/tipidee_fcgi_unknowntype_body_unpack.c +++ /dev/null @@ -1,11 +0,0 @@ -/* ISC license. */ - -#include - -#include - -void tipidee_fcgi_unknowntype_body_unpack (char const *s, fcgi_unknowntype_body *bd) -{ - bd->type = *s++ ; - memcpy((char *)bd->reserved, s, 7) ; s += 7 ; -} diff --git a/src/libtipidee/tipidee_fcgi_unknowntype_record_pack.c b/src/libtipidee/tipidee_fcgi_unknowntype_record_pack.c deleted file mode 100644 index 9d962e2..0000000 --- a/src/libtipidee/tipidee_fcgi_unknowntype_record_pack.c +++ /dev/null @@ -1,9 +0,0 @@ -/* ISC license. */ - -#include - -void tipidee_fcgi_unknowntype_record_pack (char *s, fcgi_unknowntype_record const *rec) -{ - tipidee_fcgi_header_pack(s, &rec->header) ; s += 8 ; - tipidee_fcgi_unknowntype_body_pack(s, &rec->body) ; -} diff --git a/src/libtipidee/tipidee_fcgi_unknowntype_record_unpack.c b/src/libtipidee/tipidee_fcgi_unknowntype_record_unpack.c deleted file mode 100644 index 24c6631..0000000 --- a/src/libtipidee/tipidee_fcgi_unknowntype_record_unpack.c +++ /dev/null @@ -1,9 +0,0 @@ -/* ISC license. */ - -#include - -void tipidee_fcgi_unknowntype_record_unpack (char const *s, fcgi_unknowntype_record *rec) -{ - tipidee_fcgi_header_unpack(s, &rec->header) ; s += 8 ; - tipidee_fcgi_unknowntype_body_unpack(s, &rec->body) ; -} diff --git a/src/libtipidee/tipidee_headers_parse.c b/src/libtipidee/tipidee_headers_parse.c index 4881633..b621b70 100644 --- a/src/libtipidee/tipidee_headers_parse.c +++ b/src/libtipidee/tipidee_headers_parse.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -71,26 +72,43 @@ states: 4 bits, actions: 7 bits */ -struct tainp_s +struct buffertime_s { + buffer *b ; tain const *deadline ; tain *stamp ; } ; -typedef ssize_t get1_func (buffer *, char *, struct tainp_s *) ; +struct string_s +{ + char const *s ; + size_t len ; + size_t *i ; +} ; + +typedef ssize_t get1_func (char *, void *) ; typedef get1_func *get1_func_ref ; -static ssize_t get1_timed (buffer *b, char *c, struct tainp_s *d) +static ssize_t get1_timed (char *c, void *param) { - return buffer_timed_get(b, c, 1, d->deadline, d->stamp) ; + struct buffertime_s *p = param ; + return buffer_timed_get(p->b, c, 1, p->deadline, p->stamp) ; } -static ssize_t get1_notimed (buffer *b, char *c, struct tainp_s *data) +static ssize_t get1_notimed (char *c, void *param) { - (void)data ; + buffer *b = param ; return buffer_get(b, c, 1) ; } +static ssize_t get1_string (char *c, void *param) +{ + struct string_s *st = param ; + if ((*st->i) >= st->len) return (errno = EAGAIN, -1) ; + *c = st->s[(*st->i)++] ; + return 1 ; +} + static uint8_t cclass (char c) { static uint8_t const ctable[128] = "00000000032001000000000000000000365666665566566566666666664565655666666666666666666666666665556666666666666666666666666666656560" ; @@ -104,7 +122,7 @@ static int needs_processing (char const *s) return 1 ; } -static int tipidee_headers_parse_with (buffer *b, tipidee_headers *hdr, get1_func_ref next, struct tainp_s *data, disize *header, uint32_t *state) +static int tipidee_headers_parse_with (tipidee_headers *hdr, disize *header, uint32_t *state, get1_func_ref next, void *param) { static uint16_t const table[10][8] = { @@ -123,7 +141,7 @@ static int tipidee_headers_parse_with (buffer *b, tipidee_headers *hdr, get1_fun { uint16_t c ; char cur = 0 ; - if ((*next)(b, &cur, data) < 0) + if ((*next)(&cur, param) == -1) return errno == ETIMEDOUT ? 408 : error_isagain(errno) ? -2 : -1 ; c = table[*state][cclass(cur)] ; /* @@ -201,13 +219,19 @@ static int tipidee_headers_parse_with (buffer *b, tipidee_headers *hdr, get1_fun int tipidee_headers_timed_parse (buffer *b, tipidee_headers *hdr, tain const *deadline, tain *stamp) { - struct tainp_s d = { .deadline = deadline, .stamp = stamp } ; + struct buffertime_s bt = { .b = b, .deadline = deadline, .stamp = stamp } ; disize header = DISIZE_ZERO ; uint32_t state = 0 ; - return tipidee_headers_parse_with(b, hdr, &get1_timed, &d, &header, &state) ; + return tipidee_headers_parse_with(hdr, &header, &state, &get1_timed, &bt) ; } int tipidee_headers_parse_nb (buffer *b, tipidee_headers *hdr, disize *header, uint32_t *state) { - return tipidee_headers_parse_with(b, hdr, &get1_notimed, 0, header, state) ; + return tipidee_headers_parse_with(hdr, header, state, &get1_notimed, b) ; +} + +int tipidee_headers_parse_fromstring_nb (char const *s, size_t len, size_t *i, tipidee_headers *hdr, disize *header, uint32_t *state) +{ + struct string_s st = { .s = s, .len = len, .i = i } ; + return tipidee_headers_parse_with(hdr, header, state, &get1_string, &st) ; } diff --git a/src/tipideed/cgi.c b/src/tipideed/cgi.c index ca66ec5..83f349d 100644 --- a/src/tipideed/cgi.c +++ b/src/tipideed/cgi.c @@ -151,33 +151,9 @@ static inline int do_nph (tipidee_rql const *rql, char const *docroot, char cons die500sys(rql, errno == ENOENT ? 127 : 126, docroot, "exec nph ", argv[0]) ; } -static inline int local_redirect (tipidee_rql *rql, char const *docroot, char const *loc, char *uribuf, char const *cginame) -{ - size_t n ; - size_t hostlen = strlen(rql->uri.host) ; - uint16_t port = rql->uri.port ; - uint8_t ishttps = rql->uri.https ; - char hosttmp[hostlen + 1] ; - memcpy(hosttmp, rql->uri.host, hostlen + 1) ; - n = tipidee_uri_parse(uribuf, URI_BUFSIZE, loc, &rql->uri) ; - if (!n || n + hostlen + 1 > URI_BUFSIZE) - die502x(rql, 2, docroot, "cgi ", cginame, " returned an invalid ", "Location", " value", " for local redirection") ; - memcpy(uribuf + n, hosttmp, hostlen + 1) ; - rql->uri.host = uribuf + n ; - rql->uri.port = port ; - rql->uri.https = ishttps ; - tipidee_log_debug(g.logv, "cgi ", cginame, ": local redirect to ", rql->uri.path) ; - return 1 ; -} - static inline int do_cgi (tipidee_rql *rql, char const *docroot, char const *const *argv, char const *const *envp, char const *body, size_t bodylen, char *uribuf, int autochunk, uint32_t raflags) { - char const *reason = "OK" ; - char const *location ; - char const *contentlength ; - char const *statusfield ; uint64_t rbodylen = 0 ; - unsigned int status = 0 ; int r ; tipidee_headers hdr ; char hdrbuf[4096] ; @@ -249,7 +225,6 @@ static inline int do_cgi (tipidee_rql *rql, char const *docroot, char const *con return 0 ; normalcase: - if (x[1].fd >= 0) { if (timed_write_g(x[1].fd, body + bodyw, bodylen - bodyw, &deadline) < bodylen - bodyw) @@ -262,91 +237,19 @@ static inline int do_cgi (tipidee_rql *rql, char const *docroot, char const *con } tain_add_g(&deadline, &g.writetto) ; - statusfield = tipidee_headers_search(&hdr, "Status") ; - location = tipidee_headers_search(&hdr, "Location") ; - contentlength = tipidee_headers_search(&hdr, "Content-Length") ; - if (contentlength) - { - if (!uint640_scan(contentlength, &rbodylen)) - die502x(rql, 2, docroot, "cgi ", argv[0], " returned an invalid ", "Content-Length", " header") ; - } - if (statusfield) - { - size_t m = uint_scan(statusfield, &status) ; - if (!m || (statusfield[m] && statusfield[m] != ' ')) - die502x(rql, 2, docroot, "cgi ", argv[0], " returned an invalid ", "Status", " header") ; - if (statusfield[m]) reason = statusfield + m + 1 ; - else - { - tipidee_defaulttext dt ; - reason = tipidee_util_defaulttext(status, &dt) ? dt.reason : "" ; - } - if (!location && (status == 301 || status == 302 || status == 307 || status == 308)) - die502x(rql, 2, docroot, "cgi ", argv[0], " returned a redirection status code without a ", "Location", " header") ; - if (status < 100 || status > 999) - die502x(rql, 2, docroot, "cgi ", argv[0], " returned an invalid ", "Status", " value") ; - } - if (location) - { - if (!location[0]) die502x(rql, 2, docroot, "cgi ", argv[0], " returned an invalid ", "Location", " header") ; - if (location[0] == '/' && location[1] != '/') - { - close(x[0].fd) ; - return local_redirect(rql, docroot, location, uribuf, argv[0]) ; - } - if (rbodylen) - { - if (!status) - die502x(rql, 2, docroot, "cgi ", argv[0], " didn't output a ", "Status", " header", " for a client redirect response with document") ; - if (status < 300 || status > 399) - die502x(rql, 2, docroot, "cgi ", argv[0], " returned an invalid ", "Status", " value", " for a client redirect response with document") ; - } - else - { - for (size_t i = 0 ; i < hdr.n ; i++) - { - char const *key = hdr.buf + hdr.list[i].left ; - if (!strcasecmp(key, "Location") || !strcasecmp(key, "Status")) continue ; - if (str_start(key, "X-CGI-")) continue ; - die502x(rql, 2, docroot, "cgi ", argv[0], " returned extra headers", " for a client redirect response without document") ; - } - if (!status) - { - status = 302 ; - reason = "Found" ; - } - } - } - else - { - if (!status) status = 200 ; - if (status != 304 && (status < 400 || status > 599) && !tipidee_headers_search(&hdr, "Content-Type")) - die502x(rql, 2, docroot, "cgi ", argv[0], " didn't output a ", "Content-Type", " header") ; - } - - tipidee_response_status(buffer_1, rql, status, reason) ; - tipidee_response_header_writemerge_G(buffer_1, g.rhdr, g.rhdrn, &hdr, !g.cont) ; - - if (contentlength) + r = process_cgi_headers(rql, &hdr, docroot, "cgi", argv[0], autochunk, &rbodylen) ; + if (r == 2) { - char fmt[UINT64_FMT] ; - fmt[uint64_fmt(fmt, rbodylen)] = 0 ; - buffer_putsnoflush(buffer_1, "Content-Length: ") ; - buffer_putsnoflush(buffer_1, fmt) ; - buffer_putnoflush(buffer_1, "\r\n", 2) ; + close(x[0].fd) ; + return local_redirect(rql, docroot, tipidee_headers_search(&hdr, "Location"), uribuf, "cgi", argv[0]) ; } - else if (autochunk) - buffer_putsnoflush(buffer_1, "Transfer-Encoding: chunked\r\n") ; - - buffer_putnoflush(buffer_1, "\r\n", 2) ; - tipidee_log_answer(g.logv, rql, status, rbodylen) ; if (rql->m == TIPIDEE_METHOD_HEAD) { if (!buffer_timed_flush_g(buffer_1, &deadline)) strerr_diefu1sys(111, "write to stdout") ; } - else if (contentlength) + else if (r) { size_t len ; struct iovec v[2] ; diff --git a/src/tipideed/deps-exe/tipideed b/src/tipideed/deps-exe/tipideed index bc32b06..1527240 100644 --- a/src/tipideed/deps-exe/tipideed +++ b/src/tipideed/deps-exe/tipideed @@ -3,6 +3,7 @@ errors.o harden.o fastcgi.o options.o +process_cgi_headers.o regular.o redirection.o rproxy.o diff --git a/src/tipideed/fastcgi.c b/src/tipideed/fastcgi.c index 5d5debf..ffd2c84 100644 --- a/src/tipideed/fastcgi.c +++ b/src/tipideed/fastcgi.c @@ -1,10 +1,15 @@ /* ISC license. */ +#include #include #include +#include #include +#include #include +#include +#include #include #include #include @@ -16,51 +21,320 @@ #include #include "tipideed-internal.h" -#define putit(b, s, deadline) if (buffer_timed_puts_g(b, s, deadline) == -1) die500sys(rql, 111, docroot, "write to ", fn) +typedef struct fctx_s fctx, *fctx_ref ; +struct fctx_s +{ + tipidee_fcgi fc ; + tipidee_rql *rql ; + char const *sub ; + char const *docroot ; + char const *fn ; +} ; + + +/* + Scan g.sa for environment variables + + +st\ev \0 = other + 0 1 2 + +0 m +KSTART X X KEY + +1 k +KEY KSTART VSTART KEY + +2 mv m m +VSTART KSTART VALUE VALUE + +3 v +VALUE KSTART VALUE VALUE + +4 +X + +0x10 k store key start and length +0x20 m mark +0x40 v store value start and length + +*/ + +static uint8_t cclass (char c) gccattr_pure ; +static uint8_t cclass (char c) +{ + switch (c) + { + case 0 : return 0 ; + case '=' : return 1 ; + default : return 2 ; + } +} + +static int parse_vars (stralloc *sa, char const *s, size_t len) +{ + static uint8_t table[4][3] = + { + { 0x04, 0x04, 0x21 }, + { 0x00, 0x12, 0x01 }, + { 0x60, 0x23, 0x23 }, + { 0x40, 0x03, 0x03 } + } ; + size_t keystart = 0, keylen = 0, mark = 0 ; + uint8_t state = 0 ; + for (size_t i = 0 ; i < len ; i++) + { + uint8_t c = table[state][cclass(s[i])] ; + state = c & 0x07 ; + if (state >= 4) return (errno = EDOM, 0) ; + if (c & 0x10) { keystart = mark ; keylen = i - mark ; } + if (c & 0x20) mark = i ; + if (c & 0x40) + { + if (!tipidee_fcgi_addenvb(sa, s + keystart, keylen, s + mark, i - mark)) return 0 ; + } + } + return 1 ; +} -static void do_fastcgi (tipidee_rql const *rql, int fd, char const *sub, char const *docroot, tipidee_headers const *hdr, char const *body, size_t bodylen, tain const *deadline, char const *fn) +static void addenv (fctx *c, stralloc *sa, char const *key, char const *val) { - char buf[4096] ; - buffer b = BUFFER_INIT(&buffer_write, fd, buf, 4096) ; - _exit(0) ; + if (!tipidee_fcgi_addenv(sa, key, val)) + { + if (errno != E2BIG) die500sys(c->rql, 111, c->docroot, "prepare environment for ", "fastcgi", " ", c->fn) ; + if (!tipidee_fcgi_params_g(&c->fc, sa->s, sa->len)) die500sys(c->rql, 111, c->docroot, "send environment to ", "fastcgi", " ", c->fn) ; + sa->len = 0 ; + if (!tipidee_fcgi_addenv(sa, key, val)) die500sys(c->rql, 111, c->docroot, "prepare environment for ", "fastcgi", " ", c->fn) ; + } } -static void fastcgi_unix (tipidee_rql const *rql, char const *socketpath, char const *sub, char const *docroot, tipidee_headers const *hdr, char const *body, size_t bodylen) +static void prepare_env (fctx *c, tipidee_headers const *hdr, size_t bodylen, stralloc *sa) +{ + size_t docrootlen = strlen(c->docroot) ; + sa->len = 0 ; + if (!parse_vars(sa, g.sa.s + g.cwdlen + 1, g.sa.len - (g.cwdlen + 1))) + { + if (errno == EDOM) die500sys(c->rql, 102, c->docroot, "can't happen: invalid environment string") ; + else die500sys(c->rql, 111, c->docroot, "encode ", "environment string for ", "fastcgi", " ", c->fn) ; + } + if (bodylen) + { + char fmt[SIZE_FMT] ; + fmt[size_fmt(fmt, bodylen)] = 0 ; + addenv(c, sa, "CONTENT_LENGTH", fmt) ; + } + if (c->rql->uri.query) + { + size_t pathlen = strlen(c->rql->uri.path) ; + size_t querylen = strlen(c->rql->uri.query) ; + char val[pathlen + querylen + 2] ; + memcpy(val, c->rql->uri.path, pathlen) ; + val[pathlen] = '?' ; + memcpy(val + pathlen + 1, c->rql->uri.query, querylen + 1) ; + addenv(c, sa, "REQUEST_URI", val) ; + } + else addenv(c, sa, "REQUEST_URI", c->rql->uri.path) ; + addenv(c, sa, "REQUEST_METHOD", tipidee_method_tostr(c->rql->m)) ; + addenv(c, sa, "QUERY_STRING", c->rql->uri.query ? c->rql->uri.query : "") ; + if (c->sub) + { + size_t sublen = strlen(c->sub) ; + size_t pathlen = strlen(c->rql->uri.path) ; + char val[pathlen - sublen - docrootlen + 1] ; + memcpy(val, c->rql->uri.path + docrootlen, pathlen - sublen - docrootlen) ; + val[pathlen - sublen - docrootlen] = 0 ; + addenv(c, sa, "PATH_INFO", c->sub) ; + addenv(c, sa, "SCRIPT_NAME", val) ; + } + else addenv(c, sa, "SCRIPT_NAME", c->rql->uri.path + docrootlen) ; + addenv(c, sa, "SERVER_NAME", c->rql->uri.host) ; + addenv(c, sa, "SERVER_PROTOCOL", c->rql->http_minor ? "HTTP/1.1" : "HTTP/1.0") ; + + for (size_t i = 0 ; i < hdr->n ; i++) + { + char const *key = hdr->buf + hdr->list[i].left ; + char const *val = hdr->buf + hdr->list[i].right ; + if (!strcasecmp(key, "Authorization")) + { + size_t len = strlen(val) ; + size_t n = byte_chr(val, len, ' ') ; + if (n < len) + { + char tmp[n+1] ; + memcpy(tmp, val, n) ; + tmp[n] = 0 ; + addenv(c, sa, "AUTH_TYPE", tmp) ; + } + if (g.flagcgipassauth) addenv(c, sa, "HTTP_AUTHORIZATION", val) ; + } + else if (!strcasecmp(key, "Content-Type")) addenv(c, sa, "CONTENT_TYPE", val) ; + else if (!strcasecmp(key, "Content-Length") || !strcasecmp(key, "Connection")) ; + else + { + size_t keylen = strlen(key) ; + char tmp[keylen + 6] ; + memcpy(tmp, "HTTP_", 5) ; + for (size_t i = 0 ; i < keylen ; i++) + tmp[5 + i] = key[i] == '-' ? '_' : key[i] >= 'a' && key[i] <= 'z' ? key[i] - 32 : key[i] ; + tmp[5 + keylen] = 0 ; + addenv(c, sa, tmp, val) ; + } + } + if (sa->len && !tipidee_fcgi_params_g(&c->fc, sa->s, sa->len)) + die500sys(c->rql, 111, c->docroot, "send", " environment to ", "fastcgi", " ", c->fn) ; + sa->len = 0 ; + if (!tipidee_fcgi_params_g(&c->fc, 0, 0)) + die500sys(c->rql, 111, c->docroot, "finish sending", " environment to ", "fastcgi", " ", c->fn) ; +} + +static void do_write (fctx *c, char const *s, size_t len, uint64_t cl, uint64_t *w) +{ + tain wdeadline ; + tain_add_g(&wdeadline, &g.writetto) ; + if (cl + len > *w) + die502x(c->rql, 111, c->docroot, "fastcgi", " ", c->fn, " sent more data than advertised") ; + if (!cl) + { + char fmt[SIZE_XFMT] ; + size_t w = size_xfmt(fmt, len) ; + if (buffer_timed_put_g(buffer_1, fmt, w, &wdeadline) < w + || buffer_timed_put_g(buffer_1, "\r\n", 2, &wdeadline) < 2) + strerr_diefusys(111, "write to stdout") ; + } + if (buffer_timed_put_g(buffer_1, s, len, &wdeadline) < len) + strerr_diefusys(111, "write to stdout") ; + if (!cl && buffer_timed_put_g(buffer_1, "\r\n", 2, &wdeadline) < 2) + strerr_diefusys(111, "write to stdout") ; + if (!buffer_timed_flush_g(buffer_1, &wdeadline)) + strerr_diefusys(111, "write to stdout") ; + *w += len ; +} + +static int do_fastcgi (fctx *c, int fd, tipidee_headers const *hdr, char const *body, size_t bodylen, tain const *deadline, char *uribuf) +{ + static stralloc sa = STRALLOC_ZERO ; + tipidee_headers rhdr ; + char rhdrbuf[4096] ; + uint64_t cl = 0 ; + uint64_t w = 0 ; + uint32_t status = 0 ; + disize curheader = DISIZE_ZERO ; + uint32_t parserstate = 0 ; + int stream = 1 ; + int state = 0 ; + + tipidee_headers_init(&rhdr, rhdrbuf, 4096) ; + tipidee_fcgi_startwrite(&c->fc, fd, getpid(), deadline) ; + if (!tipidee_fcgi_beginrequest_g(&c->fc)) die500sys(c->rql, 111, c->docroot, "send request to ", "fastcgi", " ", c->fn) ; + prepare_env(c, hdr, bodylen, &sa) ; + if (bodylen && !tipidee_fcgi_stdin_g(&c->fc, body, bodylen)) die500sys(c->rql, 111, c->docroot, "send", " request body to ", "fastcgi", " ", c->fn) ; + if (!tipidee_fcgi_stdin_g(&c->fc, 0, 0)) die500sys(c->rql, 111, c->docroot, "finish sending", " request body to ", "fastcgi", " ", c->fn) ; + + tipidee_fcgi_startread(&c->fc, fd, c->fc.id, deadline) ; + while (stream) + { + stream = tipidee_fcgi_read_g(&c->fc, &status, &sa) ; + if (stream == -1) + { + switch (errno) + { + case ECONNREFUSED: die502x(c->rql, 111, c->docroot, "fastcgi", " ", c->fn, " said it can only process one connection") ; + case ENFILE: die502x(c->rql, 111, c->docroot, "fastcgi", " ", c->fn, " is overloaded") ; + case EAFNOSUPPORT: die502x(c->rql, 111, c->docroot, "fastcgi", " ", c->fn, " does not support the Responder role") ; + case EPROTO : die502x(c->rql, 111, c->docroot, "fastcgi", " ", c->fn, " spoke a weird dialect") ; + default : die502sys(c->rql, 111, c->docroot, "unable to ", "read", " from ", "fastcgi", " ", c->fn) ; + } + } + else if (stream == 1) + { + if (!state) + { + size_t i = 0 ; + int r ; + switch (tipidee_headers_parse_fromstring_nb(sa.s, sa.len, &i, &rhdr, &curheader, &parserstate)) + { + case -2 : goto cont ; + case -1 : die500sys(c->rql, 111, c->docroot, "read", " from ", "fastcgi", " ", c->fn) ; + case 0 : break ; + case 400 : die502x(c->rql, 2, c->docroot, "invalid output", " from ", "fastcgi", " ", c->fn) ; + case 413 : die502x(c->rql, 2, c->docroot, rhdr.n >= TIPIDEE_HEADERS_MAX ? "Too many headers" : "Too much header data", " from ", "fastcgi", " ", c->fn) ; + case 500 : die500x(c->rql, 101, c->docroot, "can't happen: ", "avltreen_insert failed", " in do_fastcgi") ; + default : die500x(c->rql, 101, c->docroot, "can't happen: ", "unknown tipidee_headers_parse return code", " in do_fastcgi") ; + } + + r = process_cgi_headers(c->rql, &rhdr, c->docroot, "fastcgi", c->fn, 1, &cl) ; + if (r == 2) + { + fd_close(fd) ; + return local_redirect(c->rql, c->docroot, tipidee_headers_search(&rhdr, "Location"), uribuf, "fastcgi", c->fn) ; + } + if (c->rql->m == TIPIDEE_METHOD_HEAD) + { + tain wdeadline ; + tain_add_g(&wdeadline, &g.writetto) ; + if (!buffer_timed_flush_g(buffer_1, &wdeadline)) + strerr_diefu1sys(111, "write to stdout") ; + return 0 ; + } + state = 1 ; + if (i < sa.len) do_write(c, sa.s + i, sa.len - i, cl, &w) ; + } + else if (state == 1) do_write(c, sa.s, sa.len, cl, &w) ; + else die502x(c->rql, 111, c->docroot, "fastcgi", " ", c->fn, " kept sending FCGI_STDOUT data after EOF marker") ; + cont: + if (!sa.len) state = 2 ; + sa.len = 0 ; + } + else if (stream == 2) + { + buffer_putflush(buffer_2, sa.s, sa.len) ; + tain_now_g() ; + sa.len = 0 ; + } + else die502x(c->rql, 111, c->docroot, "fastcgi", " ", c->fn, " sent data on an unsupported stream") ; + } + fd_close(fd) ; + if (cl && w < cl) + die502x(c->rql, 111, c->docroot, "fastcgi", " ", c->fn, " sent less data than advertised") ; + return 0 ; +} + +static int fastcgi_unix (fctx *c, char const *socketpath, tipidee_headers const *hdr, char const *body, size_t bodylen, char *uribuf) { tain deadline ; int fd = ipc_stream_nbcoe() ; - if (fd == -1) die500sys(rql, 111, docroot, "create socket") ; + if (fd == -1) die500sys(c->rql, 111, c->docroot, "create socket") ; tain_add_g(&deadline, &g.readtto) ; - if (!ipc_timed_connect_g(fd, socketpath, &deadline)) die500sys(rql, 111, docroot, "connect to ", socketpath) ; - do_fastcgi(rql, fd, sub, docroot, hdr, body, bodylen, &deadline, socketpath) ; - fd_close(fd) ; + if (!ipc_timed_connect_g(fd, socketpath, &deadline)) die500sys(c->rql, 111, c->docroot, "connect to ", socketpath) ; + c->fn = socketpath ; + return do_fastcgi(c, fd, hdr, body, bodylen, &deadline, uribuf) ; } -static inline void fastcgi_tcp (tipidee_rql const *rql, char const *ip, uint16_t port, int is6, char const *sub, char const *docroot, tipidee_headers const *hdr, char const *body, size_t bodylen) +static inline int fastcgi_tcp (fctx *c, char const *ip, uint16_t port, int is6, tipidee_headers const *hdr, char const *body, size_t bodylen, char *uribuf) { tain deadline ; size_t m = 0 ; int fd = socket_tcp46_nbcoe(is6) ; char fmt[IP6_FMT + UINT16_FMT + 2] ; - if (fd == -1) die500sys(rql, 111, docroot, "create socket") ; - tain_add_g(&deadline, &g.readtto) ; + if (fd == -1) die500sys(c->rql, 111, c->docroot, "create socket") ; + tain_add_g(&deadline, &g.cgitto) ; if (is6) fmt[m++] = '[' ; m += is6 ? ip6_fmt(fmt + m, ip) : ip4_fmt(fmt + m, ip) ; if (is6) fmt[m++] = ']' ; fmt[m++] = ':' ; m += uint16_fmt(fmt + m, port) ; fmt[m++] = 0 ; - if (!(is6 ? socket_deadlineconnstamp6_g(fd, ip, port, &deadline) : socket_deadlineconnstamp4_g(fd, ip, port, &deadline))) - die500sys(rql, 111, docroot, "connect to ", fmt) ; - do_fastcgi(rql, fd, sub, docroot, hdr, body, bodylen, &deadline, fmt) ; - fd_close(fd) ; + die500sys(c->rql, 111, c->docroot, "connect to ", fmt) ; + c->fn = fmt ; + return do_fastcgi(c, fd, hdr, body, bodylen, &deadline, uribuf) ; } -void fastcgi (tipidee_rql const *rql, tipidee_redirection const *rd, char const *docroot, tipidee_headers const *hdr, char const *body, size_t bodylen) +int fastcgi (tipidee_rql *rql, tipidee_redirection const *rd, char const *docroot, tipidee_headers const *hdr, char const *body, size_t bodylen, char *uribuf) { + fctx c = { .fc = TIPIDEE_FCGI_ZERO, .rql = rql, .sub = rd->sub, .docroot = docroot, .fn = 0 } ; if (rd->flags & TIPIDEE_REDIR_ISINET) - fastcgi_tcp(rql, rd->addr, rd->port, !!(rd->flags & TIPIDEE_REDIR_ISV6), rd->sub, docroot, hdr, body, bodylen) ; + return fastcgi_tcp(&c, rd->addr, rd->port, !!(rd->flags & TIPIDEE_REDIR_ISV6), hdr, body, bodylen, uribuf) ; else - fastcgi_unix(rql, rd->addr, rd->sub, docroot, hdr, body, bodylen) ; + return fastcgi_unix(&c, rd->addr, hdr, body, bodylen, uribuf) ; } diff --git a/src/tipideed/process_cgi_headers.c b/src/tipideed/process_cgi_headers.c new file mode 100644 index 0000000..a02dde7 --- /dev/null +++ b/src/tipideed/process_cgi_headers.c @@ -0,0 +1,115 @@ +/* ISC license. */ + +#include + +#include +#include +#include +#include + +#include +#include "tipideed-internal.h" + +int local_redirect (tipidee_rql *rql, char const *docroot, char const *loc, char *uribuf, char const *what, char const *cginame) +{ + size_t n ; + size_t hostlen = strlen(rql->uri.host) ; + uint16_t port = rql->uri.port ; + uint8_t ishttps = rql->uri.https ; + char hosttmp[hostlen + 1] ; + memcpy(hosttmp, rql->uri.host, hostlen + 1) ; + n = tipidee_uri_parse(uribuf, URI_BUFSIZE, loc, &rql->uri) ; + if (!n || n + hostlen + 1 > URI_BUFSIZE) + die502x(rql, 2, docroot, what, " ", cginame, " returned an invalid ", "Location", " value", " for local redirection") ; + memcpy(uribuf + n, hosttmp, hostlen + 1) ; + rql->uri.host = uribuf + n ; + rql->uri.port = port ; + rql->uri.https = ishttps ; + tipidee_log_debug(g.logv, what, " ", cginame, ": local redirect to ", rql->uri.path) ; + return 1 ; +} + +int process_cgi_headers (tipidee_rql const *rql, tipidee_headers *hdr, char const *docroot, char const *what, char const *app, int autochunk, uint64_t *cl) +{ + char const *reason = "OK" ; + char const *location ; + char const *contentlength ; + char const *statusfield ; + unsigned int status = 0 ; + + statusfield = tipidee_headers_search(hdr, "Status") ; + location = tipidee_headers_search(hdr, "Location") ; + contentlength = tipidee_headers_search(hdr, "Content-Length") ; + if (contentlength) + { + if (!uint640_scan(contentlength, cl)) + die502x(rql, 2, docroot, what, " ", app, " returned an invalid ", "Content-Length", " header") ; + } + if (statusfield) + { + size_t m = uint_scan(statusfield, &status) ; + if (!m || (statusfield[m] && statusfield[m] != ' ')) + die502x(rql, 2, docroot, what, " ", app, " returned an invalid ", "Status", " header") ; + if (statusfield[m]) reason = statusfield + m + 1 ; + else + { + tipidee_defaulttext dt ; + reason = tipidee_util_defaulttext(status, &dt) ? dt.reason : "" ; + } + if (!location && (status == 301 || status == 302 || status == 307 || status == 308)) + die502x(rql, 2, docroot, what, " ", app, " returned a redirection status code without a ", "Location", " header") ; + if (status < 100 || status > 999) + die502x(rql, 2, docroot, what, " ", app, " returned an invalid ", "Status", " value") ; + } + if (location) + { + if (!location[0]) die502x(rql, 2, docroot, what, " ", app, " returned an invalid ", "Location", " header") ; + if (location[0] == '/' && location[1] != '/') return 2 ; + if (*cl) + { + if (!status) + die502x(rql, 2, docroot, what, " ", app, " didn't output a ", "Status", " header", " for a client redirect response with document") ; + if (status < 300 || status > 399) + die502x(rql, 2, docroot, what, " ", app, " returned an invalid ", "Status", " value", " for a client redirect response with document") ; + } + else + { + for (uint32_t i = 0 ; i < hdr->n ; i++) + { + char const *key = hdr->buf + hdr->list[i].left ; + if (!strcasecmp(key, "Location") || !strcasecmp(key, "Status")) continue ; + if (str_start(key, "X-CGI-")) continue ; + die502x(rql, 2, docroot, what, " ", app, " returned extra headers", " for a client redirect response without document") ; + } + if (!status) + { + status = 302 ; + reason = "Found" ; + } + } + } + else + { + if (!status) status = 200 ; + if (status != 304 && (status < 400 || status > 599) && !tipidee_headers_search(hdr, "Content-Type")) + die502x(rql, 2, docroot, what, " ", app, " didn't output a ", "Content-Type", " header") ; + } + + tipidee_response_status(buffer_1, rql, status, reason) ; + tipidee_response_header_writemerge_G(buffer_1, g.rhdr, g.rhdrn, hdr, !g.cont) ; + + if (contentlength) + { + char fmt[UINT64_FMT] ; + fmt[uint64_fmt(fmt, *cl)] = 0 ; + buffer_putsnoflush(buffer_1, "Content-Length: ") ; + buffer_putsnoflush(buffer_1, fmt) ; + buffer_putnoflush(buffer_1, "\r\n", 2) ; + } + else if (autochunk) + buffer_putsnoflush(buffer_1, "Transfer-Encoding: chunked\r\n") ; + + buffer_putnoflush(buffer_1, "\r\n", 2) ; + tipidee_log_answer(g.logv, rql, status, *cl) ; + return !!contentlength ; +} diff --git a/src/tipideed/tipideed-internal.h b/src/tipideed/tipideed-internal.h index 5d72cd6..9b076cd 100644 --- a/src/tipideed/tipideed-internal.h +++ b/src/tipideed/tipideed-internal.h @@ -119,6 +119,7 @@ extern void respond_416 (tipidee_rql const *, char const *, uint64_t) ; #define die500x(r, e, d, ...) diefx(r, e, d, 500, __VA_ARGS__) #define die500sys(r, e, d, ...) diefusys(r, e, d, 500, __VA_ARGS__) #define die502x(r, e, d, ...) diefx(r, e, d, 502, __VA_ARGS__) +#define die502sys(r, e, d, ...) diefusys(r, e, d, 502, __VA_ARGS__) /* redirection */ @@ -133,7 +134,7 @@ extern void rproxy (tipidee_rql const *, tipidee_redirection const *, char const /* fastcgi */ -extern void fastcgi (tipidee_rql const *, tipidee_redirection const *, char const *, tipidee_headers const *, char const *, size_t) ; +extern int fastcgi (tipidee_rql *, tipidee_redirection const *, char const *, tipidee_headers const *, char const *, size_t, char *) ; /* trace */ @@ -174,6 +175,12 @@ extern int respond_304 (tipidee_rql const *, char const *, struct stat const *) extern int respond_cgi (tipidee_rql *, char const *, char const *, size_t, char const *, char *, tipidee_headers const *, tipidee_resattr const *, char const *, size_t) ; + /* process_cgi_headers */ + +extern int process_cgi_headers (tipidee_rql const *, tipidee_headers *, char const *, char const *, char const *, int, uint64_t *) ; +extern int local_redirect (tipidee_rql *, char const *, char const *, char *, char const *, char const *) ; + + /* util */ extern size_t translate_path (char const *) ; diff --git a/src/tipideed/tipideed.c b/src/tipideed/tipideed.c index 4bd3242..858f078 100644 --- a/src/tipideed/tipideed.c +++ b/src/tipideed/tipideed.c @@ -246,7 +246,7 @@ static inline int serve (tipidee_rql *rql, char const *docroot, char *uribuf, ti case TIPIDEE_REDIR_NONE : break ; case TIPIDEE_REDIR_REDIRECT : respond_30x(rql, &rd) ; return 0 ; case TIPIDEE_REDIR_RPROXY : rproxy(rql, &rd, docroot, hdr, body, bodylen) ; return 0 ; - case TIPIDEE_REDIR_FASTCGI : fastcgi(rql, &rd, docroot, hdr, body, bodylen) ; return 0 ; + case TIPIDEE_REDIR_FASTCGI : fastcgi(rql, &rd, docroot, hdr, body, bodylen, uribuf) ; return 0 ; default : strerr_dief(101, "can't happen: ", "unknown redirection type") ; } } -- cgit v1.3.1