ksgi
/
parent.c
380 строк · 9.7 Кб
1/* $Id$ */
2/*
3* Copyright (c) 2012, 2014--2017 Kristaps Dzonsons <kristaps@bsd.lv>
4*
5* Permission to use, copy, modify, and distribute this software for any
6* purpose with or without fee is hereby granted, provided that the above
7* copyright notice and this permission notice appear in all copies.
8*
9* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16*/
17#include "config.h"18
19#include <assert.h>20#if HAVE_MD521# include <sys/types.h>22# include <md5.h>23#endif24#include <stdarg.h>25#include <stdio.h>26#include <stdint.h>27#include <stddef.h>28#include <stdlib.h>29#include <string.h>30
31#include "kcgi.h"32#include "extern.h"33
34/*
35* Read a single kpair from the child.
36* This returns 0 if there are no more pairs to read (and eofok has been
37* set) and -1 if any errors occur (the parent should also exit with
38* server failure).
39* Otherwise, it returns 1 and the pair is zeroed and filled in.
40*/
41static int42input(enum input *type, struct kpair *kp, int fd,43enum kcgi_err *ke, int eofok, size_t mimesz, size_t keysz)44{
45size_t sz;46int rc;47ptrdiff_t diff;48
49memset(kp, 0, sizeof(struct kpair));50
51rc = fullread(fd, type, sizeof(enum input), 1, ke);52if (rc == 0) {53if (eofok)54return 0;55kutil_warnx(NULL, NULL, "unexpected EOF from child");56*ke = KCGI_FORM;57return (-1);58} else if (rc < 0) {59kutil_warnx(NULL, NULL, "failed read kpair type");60return (-1);61}62
63if (*type == IN__MAX)64return 0;65
66if (*type > IN__MAX) {67kutil_warnx(NULL, NULL, "invalid kpair type");68*ke = KCGI_FORM;69return (-1);70}71
72*ke = fullreadword(fd, &kp->key);73if (*ke != KCGI_OK) {74kutil_warnx(NULL, NULL, "failed read kpair key");75return (-1);76}77
78*ke = fullreadwordsz(fd, &kp->val, &kp->valsz);79if (*ke != KCGI_OK) {80kutil_warnx(NULL, NULL, "failed read kpair value");81return (-1);82}83
84sz = sizeof(enum kpairstate);85if (fullread(fd, &kp->state, sz, 0, ke) < 0) {86kutil_warnx(NULL, NULL, "failed read kpair state");87return (-1);88} else if (kp->state > KPAIR_INVALID) {89kutil_warnx(NULL, NULL, "invalid kpair state");90return (-1);91}92
93sz = sizeof(enum kpairtype);94if (fullread(fd, &kp->type, sz, 0, ke) < 0) {95kutil_warnx(NULL, NULL, "failed read kpair type");96return (-1);97} else if (kp->type > KPAIR__MAX) {98kutil_warnx(NULL, NULL, "invalid kpair type");99return (-1);100}101
102sz = sizeof(size_t);103if (fullread(fd, &kp->keypos, sz, 0, ke) < 0) {104kutil_warnx(NULL, NULL, "failed read kpair position");105return (-1);106} else if (kp->keypos > keysz) {107kutil_warnx(NULL, NULL, "invalid kpair position");108return (-1);109}110
111if (kp->state == KPAIR_VALID)112switch (kp->type) {113case KPAIR_DOUBLE:114sz = sizeof(double);115if (fullread(fd, &kp->parsed.d, sz, 0, ke) > 0)116break;117kutil_warnx(NULL, NULL,118"failed read kpair double");119return (-1);120case KPAIR_INTEGER:121sz = sizeof(int64_t);122if (fullread(fd, &kp->parsed.i, sz, 0, ke) > 0)123break;124kutil_warnx(NULL, NULL,125"failed read kpair integer");126return (-1);127case KPAIR_STRING:128sz = sizeof(ptrdiff_t);129if (fullread(fd, &diff, sz, 0, ke) < 0) {130kutil_warnx(NULL, NULL,131"failed read kpair ptrdiff");132return (-1);133}134if (diff > (ssize_t)kp->valsz) {135*ke = KCGI_FORM;136kutil_warnx(NULL, NULL,137"invalid kpair ptrdiff");138return (-1);139}140kp->parsed.s = kp->val + diff;141break;142default:143break;144}145
146*ke = fullreadword(fd, &kp->file);147if (*ke != KCGI_OK) {148kutil_warnx(NULL, NULL,149"failed read kpair filename");150return (-1);151}152
153*ke = fullreadword(fd, &kp->ctype);154if (*ke != KCGI_OK) {155kutil_warnx(NULL, NULL,156"failed read kpair content type");157return (-1);158}159
160sz = sizeof(size_t);161if (fullread(fd, &kp->ctypepos, sz, 0, ke) < 0) {162kutil_warnx(NULL, NULL,163"failed read kpair MIME position");164return (-1);165} else if (kp->ctypepos > mimesz) {166kutil_warnx(NULL, NULL,167"invalid kpair MIME position");168return (-1);169}170
171*ke = fullreadword(fd, &kp->xcode);172if (*ke != KCGI_OK) {173kutil_warnx(NULL, NULL,174"failed read kpair content transfer encoding");175return (-1);176}177
178return 1;179}
180
181static struct kpair *182kpair_expand(struct kpair **kv, size_t *kvsz)183{
184void *pp;185
186pp = kxreallocarray(*kv, *kvsz + 1, sizeof(struct kpair));187if (pp == NULL)188return NULL;189
190*kv = pp;191memset(&(*kv)[*kvsz], 0, sizeof(struct kpair));192(*kvsz)++;193return(&(*kv)[*kvsz - 1]);194}
195
196/*
197* This is the parent kcgi process.
198* It spins on input from the child until all fields have been received.
199* These fields are sent from the child's output() function.
200* Each input field consists of the data and its validation state.
201* We build up the kpair arrays here with this data, then assign the
202* kpairs into named buckets.
203*/
204enum kcgi_err205kworker_parent(int fd, struct kreq *r, int eofok, size_t mimesz)206{
207struct kpair kp;208struct kpair *kpp;209enum krequ requ;210enum input type;211int rc;212enum kcgi_err ke;213size_t i, dgsz;214
215/* Pointers freed at "out" label. */216
217memset(&kp, 0, sizeof(struct kpair));218
219/*220* First read all of our parsed parameters.
221* Each parsed parameter is handled a little differently.
222* This list will end with META__MAX.
223*/
224
225if (fullread(fd, &r->reqsz, sizeof(size_t), 0, &ke) < 0) {226kutil_warnx(NULL, NULL, "read request header size");227goto out;228}229
230if (r->reqsz) {231r->reqs = kxcalloc(r->reqsz, sizeof(struct khead));232if (r->reqs == NULL) {233ke = KCGI_ENOMEM;234goto out;235}236}237
238for (i = 0; i < r->reqsz; i++) {239if (fullread(fd, &requ, sizeof(enum krequ), 0, &ke) < 0) {240kutil_warnx(NULL, NULL, "read request identifier");241goto out;242}243if ((ke = fullreadword(fd, &r->reqs[i].key)) != KCGI_OK) {244kutil_warnx(NULL, NULL, "read request key");245goto out;246}247if ((ke = fullreadword(fd, &r->reqs[i].val)) != KCGI_OK) {248kutil_warnx(NULL, NULL, "read request value");249goto out;250}251if (requ != KREQU__MAX)252r->reqmap[requ] = &r->reqs[i];253}254
255if (fullread(fd, &r->method, sizeof(enum kmethod), 0, &ke) < 0) {256kutil_warnx(NULL, NULL, "failed read request method");257goto out;258} else if (fullread(fd, &r->auth, sizeof(enum kauth), 0, &ke) < 0) {259kutil_warnx(NULL, NULL, "failed read authorisation type");260goto out;261} else if ((ke = kworker_auth_parent(fd, &r->rawauth)) != KCGI_OK) {262kutil_warnx(NULL, NULL, "failed read raw authorisation");263goto out;264} else if (fullread(fd, &r->scheme, sizeof(enum kscheme), 0, &ke) < 0) {265kutil_warnx(NULL, NULL, "failed read scheme");266goto out;267} else if ((ke = fullreadword(fd, &r->remote)) != KCGI_OK) {268kutil_warnx(NULL, NULL, "failed read remote");269goto out;270} else if ((ke = fullreadword(fd, &r->fullpath)) != KCGI_OK) {271kutil_warnx(NULL, NULL, "failed read fullpath");272goto out;273} else if ((ke = fullreadword(fd, &r->suffix)) != KCGI_OK) {274kutil_warnx(NULL, NULL, "failed read suffix");275goto out;276} else if ((ke = fullreadword(fd, &r->pagename)) != KCGI_OK) {277kutil_warnx(NULL, NULL, "failed read page part");278goto out;279} else if ((ke = fullreadword(fd, &r->path)) != KCGI_OK) {280kutil_warnx(NULL, NULL, "failed read path part");281goto out;282} else if ((ke = fullreadword(fd, &r->pname)) != KCGI_OK) {283kutil_warnx(NULL, NULL, "failed read script name");284goto out;285} else if ((ke = fullreadword(fd, &r->host)) != KCGI_OK) {286kutil_warnx(NULL, NULL, "failed read host name");287goto out;288} else if (fullread(fd, &r->port, sizeof(uint16_t), 0, &ke) < 0) {289kutil_warnx(NULL, NULL, "failed read port");290goto out;291} else if (fullread(fd, &dgsz, sizeof(size_t), 0, &ke) < 0) {292kutil_warnx(NULL, NULL, "failed read digest length");293goto out;294} else if (dgsz == MD5_DIGEST_LENGTH) {295/* This is a binary value. */296if ((r->rawauth.digest = kxmalloc(dgsz)) == NULL)297goto out;298if (fullread(fd, r->rawauth.digest, dgsz, 0, &ke) < 0) {299kutil_warnx(NULL, NULL, "failed read digest");300goto out;301}302}303
304for (;;) {305rc = input(&type, &kp, fd, &ke,306eofok, mimesz, r->keysz);307if (rc < 0)308goto out;309else if (rc == 0)310break;311
312/*313* We have a parsed field from the child process.
314* Begin by expanding the number of parsed fields
315* depending on whether we have a cookie or form input.
316* Then copy the new data.
317*/
318
319assert(type < IN__MAX);320kpp = type == IN_COOKIE ?321kpair_expand(&r->cookies, &r->cookiesz) :322kpair_expand(&r->fields, &r->fieldsz);323
324if (kpp == NULL) {325ke = KCGI_ENOMEM;326goto out;327}328
329*kpp = kp;330}331
332assert(rc == 0);333
334/*335* Now that the field and cookie arrays are fixed and not going
336* to be reallocated any more, we run through both arrays and
337* assign the named fields into buckets.
338* The assignment enqueues into the field.
339*/
340
341for (i = 0; i < r->fieldsz; i++) {342kpp = &r->fields[i];343if (kpp->keypos == r->keysz)344continue;345assert(kpp->keypos < r->keysz);346
347if (kpp->state != KPAIR_INVALID) {348kpp->next = r->fieldmap[kpp->keypos];349r->fieldmap[kpp->keypos] = kpp;350} else {351kpp->next = r->fieldnmap[kpp->keypos];352r->fieldnmap[kpp->keypos] = kpp;353}354}355
356for (i = 0; i < r->cookiesz; i++) {357kpp = &r->cookies[i];358if (kpp->keypos == r->keysz)359continue;360assert(kpp->keypos < r->keysz);361
362if (kpp->state != KPAIR_INVALID) {363kpp->next = r->cookiemap[kpp->keypos];364r->cookiemap[kpp->keypos] = kpp;365} else {366kpp->next = r->cookienmap[kpp->keypos];367r->cookienmap[kpp->keypos] = kpp;368}369}370
371return KCGI_OK;372out:373assert(ke != KCGI_OK);374free(kp.key);375free(kp.val);376free(kp.file);377free(kp.ctype);378free(kp.xcode);379return ke;380}
381