ksgi

Форк
0
/
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_MD5
21
# include <sys/types.h>
22
# include <md5.h>
23
#endif
24
#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
 */
41
static int
42
input(enum input *type, struct kpair *kp, int fd, 
43
	enum kcgi_err *ke, int eofok, size_t mimesz, size_t keysz)
44
{
45
	size_t		 sz;
46
	int		 rc;
47
	ptrdiff_t	 diff;
48

49
	memset(kp, 0, sizeof(struct kpair));
50

51
	rc = fullread(fd, type, sizeof(enum input), 1, ke);
52
	if (rc == 0) {
53
		if (eofok) 
54
			return 0;
55
		kutil_warnx(NULL, NULL, "unexpected EOF from child");
56
		*ke = KCGI_FORM;
57
		return (-1);
58
	} else if (rc < 0) {
59
		kutil_warnx(NULL, NULL, "failed read kpair type");
60
		return (-1);
61
	}
62

63
	if (*type == IN__MAX)
64
		return 0;
65

66
	if (*type > IN__MAX) {
67
		kutil_warnx(NULL, NULL, "invalid kpair type");
68
		*ke = KCGI_FORM;
69
		return (-1);
70
	}
71

72
	*ke = fullreadword(fd, &kp->key);
73
	if (*ke != KCGI_OK) {
74
		kutil_warnx(NULL, NULL, "failed read kpair key");
75
		return (-1);
76
	}
77

78
	*ke = fullreadwordsz(fd, &kp->val, &kp->valsz);
79
	if (*ke != KCGI_OK) {
80
		kutil_warnx(NULL, NULL, "failed read kpair value");
81
		return (-1);
82
	}
83

84
	sz = sizeof(enum kpairstate);
85
	if (fullread(fd, &kp->state, sz, 0, ke) < 0) {
86
		kutil_warnx(NULL, NULL, "failed read kpair state");
87
		return (-1);
88
	} else if (kp->state > KPAIR_INVALID) {
89
		kutil_warnx(NULL, NULL, "invalid kpair state");
90
		return (-1);
91
	}
92

93
	sz = sizeof(enum kpairtype);
94
	if (fullread(fd, &kp->type, sz, 0, ke) < 0) {
95
		kutil_warnx(NULL, NULL, "failed read kpair type");
96
		return (-1);
97
	} else if (kp->type > KPAIR__MAX) {
98
		kutil_warnx(NULL, NULL, "invalid kpair type");
99
		return (-1);
100
	}
101

102
	sz = sizeof(size_t);
103
	if (fullread(fd, &kp->keypos, sz, 0, ke) < 0) {
104
		kutil_warnx(NULL, NULL, "failed read kpair position");
105
		return (-1);
106
	} else if (kp->keypos > keysz) {
107
		kutil_warnx(NULL, NULL, "invalid kpair position");
108
		return (-1);
109
	}
110

111
	if (kp->state == KPAIR_VALID)
112
		switch (kp->type) {
113
		case KPAIR_DOUBLE:
114
			sz = sizeof(double);
115
			if (fullread(fd, &kp->parsed.d, sz, 0, ke) > 0)
116
				break;
117
			kutil_warnx(NULL, NULL, 
118
				"failed read kpair double");
119
			return (-1);
120
		case KPAIR_INTEGER:
121
			sz = sizeof(int64_t);
122
			if (fullread(fd, &kp->parsed.i, sz, 0, ke) > 0)
123
				break;
124
			kutil_warnx(NULL, NULL, 
125
				"failed read kpair integer");
126
			return (-1);
127
		case KPAIR_STRING:
128
			sz = sizeof(ptrdiff_t);
129
			if (fullread(fd, &diff, sz, 0, ke) < 0) {
130
				kutil_warnx(NULL, NULL, 
131
					"failed read kpair ptrdiff");
132
				return (-1);
133
			}
134
			if (diff > (ssize_t)kp->valsz) {
135
				*ke = KCGI_FORM;
136
				kutil_warnx(NULL, NULL, 
137
					"invalid kpair ptrdiff");
138
				return (-1);
139
			}
140
			kp->parsed.s = kp->val + diff;
141
			break;
142
		default:
143
			break;
144
		}
145

146
	*ke = fullreadword(fd, &kp->file);
147
	if (*ke != KCGI_OK) {
148
		kutil_warnx(NULL, NULL, 
149
			"failed read kpair filename");
150
		return (-1);
151
	}
152

153
	*ke = fullreadword(fd, &kp->ctype);
154
	if (*ke != KCGI_OK) {
155
		kutil_warnx(NULL, NULL, 
156
			"failed read kpair content type");
157
		return (-1);
158
	}
159

160
	sz = sizeof(size_t);
161
	if (fullread(fd, &kp->ctypepos, sz, 0, ke) < 0) {
162
		kutil_warnx(NULL, NULL, 
163
			"failed read kpair MIME position");
164
		return (-1);
165
	} else if (kp->ctypepos > mimesz) {
166
		kutil_warnx(NULL, NULL, 
167
			"invalid kpair MIME position");
168
		return (-1);
169
	}
170

171
	*ke = fullreadword(fd, &kp->xcode);
172
	if (*ke != KCGI_OK) {
173
		kutil_warnx(NULL, NULL, 
174
			"failed read kpair content transfer encoding");
175
		return (-1);
176
	}
177

178
	return 1;
179
}
180

181
static struct kpair *
182
kpair_expand(struct kpair **kv, size_t *kvsz)
183
{
184
	void	*pp;
185

186
	pp = kxreallocarray(*kv, *kvsz + 1, sizeof(struct kpair));
187
	if (pp == NULL)
188
		return NULL;
189

190
	*kv = pp;
191
	memset(&(*kv)[*kvsz], 0, sizeof(struct kpair));
192
	(*kvsz)++;
193
	return(&(*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
 */
204
enum kcgi_err
205
kworker_parent(int fd, struct kreq *r, int eofok, size_t mimesz)
206
{
207
	struct kpair	 kp;
208
	struct kpair	*kpp;
209
	enum krequ	 requ;
210
	enum input	 type;
211
	int		 rc;
212
	enum kcgi_err	 ke;
213
	size_t		 i, dgsz;
214

215
	/* Pointers freed at "out" label. */
216

217
	memset(&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

225
	if (fullread(fd, &r->reqsz, sizeof(size_t), 0, &ke) < 0) {
226
		kutil_warnx(NULL, NULL, "read request header size");
227
		goto out;
228
	}
229

230
	if (r->reqsz) {
231
		r->reqs = kxcalloc(r->reqsz, sizeof(struct khead));
232
		if (r->reqs == NULL) {
233
			ke = KCGI_ENOMEM;
234
			goto out;
235
		}
236
	}
237

238
	for (i = 0; i < r->reqsz; i++) {
239
		if (fullread(fd, &requ, sizeof(enum krequ), 0, &ke) < 0) {
240
			kutil_warnx(NULL, NULL, "read request identifier");
241
			goto out;
242
		}
243
		if ((ke = fullreadword(fd, &r->reqs[i].key)) != KCGI_OK) {
244
			kutil_warnx(NULL, NULL, "read request key");
245
			goto out;
246
		}
247
		if ((ke = fullreadword(fd, &r->reqs[i].val)) != KCGI_OK) {
248
			kutil_warnx(NULL, NULL, "read request value");
249
			goto out;
250
		}
251
		if (requ != KREQU__MAX)
252
			r->reqmap[requ] = &r->reqs[i];
253
	}
254

255
	if (fullread(fd, &r->method, sizeof(enum kmethod), 0, &ke) < 0) {
256
		kutil_warnx(NULL, NULL, "failed read request method");
257
		goto out;
258
	} else if (fullread(fd, &r->auth, sizeof(enum kauth), 0, &ke) < 0) {
259
		kutil_warnx(NULL, NULL, "failed read authorisation type");
260
		goto out;
261
	} else if ((ke = kworker_auth_parent(fd, &r->rawauth)) != KCGI_OK) {
262
		kutil_warnx(NULL, NULL, "failed read raw authorisation");
263
		goto out;
264
	} else if (fullread(fd, &r->scheme, sizeof(enum kscheme), 0, &ke) < 0) {
265
		kutil_warnx(NULL, NULL, "failed read scheme");
266
		goto out;
267
	} else if ((ke = fullreadword(fd, &r->remote)) != KCGI_OK) {
268
		kutil_warnx(NULL, NULL, "failed read remote");
269
		goto out;
270
	} else if ((ke = fullreadword(fd, &r->fullpath)) != KCGI_OK) {
271
		kutil_warnx(NULL, NULL, "failed read fullpath");
272
		goto out;
273
	} else if ((ke = fullreadword(fd, &r->suffix)) != KCGI_OK) {
274
		kutil_warnx(NULL, NULL, "failed read suffix");
275
		goto out;
276
	} else if ((ke = fullreadword(fd, &r->pagename)) != KCGI_OK) {
277
		kutil_warnx(NULL, NULL, "failed read page part");
278
		goto out;
279
	} else if ((ke = fullreadword(fd, &r->path)) != KCGI_OK) {
280
		kutil_warnx(NULL, NULL, "failed read path part");
281
		goto out;
282
	} else if ((ke = fullreadword(fd, &r->pname)) != KCGI_OK) {
283
		kutil_warnx(NULL, NULL, "failed read script name");
284
		goto out;
285
	} else if ((ke = fullreadword(fd, &r->host)) != KCGI_OK) {
286
		kutil_warnx(NULL, NULL, "failed read host name");
287
		goto out;
288
	} else if (fullread(fd, &r->port, sizeof(uint16_t), 0, &ke) < 0) {
289
		kutil_warnx(NULL, NULL, "failed read port");
290
		goto out;
291
	} else if (fullread(fd, &dgsz, sizeof(size_t), 0, &ke) < 0) {
292
		kutil_warnx(NULL, NULL, "failed read digest length");
293
		goto out;
294
	} else if (dgsz == MD5_DIGEST_LENGTH) {
295
		/* This is a binary value. */
296
		if ((r->rawauth.digest = kxmalloc(dgsz)) == NULL)
297
			goto out;
298
		if (fullread(fd, r->rawauth.digest, dgsz, 0, &ke) < 0) {
299
			kutil_warnx(NULL, NULL, "failed read digest");
300
			goto out;
301
		}
302
	}
303

304
	for (;;) {
305
		rc = input(&type, &kp, fd, &ke, 
306
			eofok, mimesz, r->keysz);
307
		if (rc < 0)
308
			goto out;
309
		else if (rc == 0)
310
			break;
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

319
		assert(type < IN__MAX);
320
		kpp = type == IN_COOKIE ?
321
			kpair_expand(&r->cookies, &r->cookiesz) :
322
			kpair_expand(&r->fields, &r->fieldsz);
323

324
		if (kpp == NULL) {
325
			ke = KCGI_ENOMEM;
326
			goto out;
327
		}
328

329
		*kpp = kp;
330
	}
331

332
	assert(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

341
	for (i = 0; i < r->fieldsz; i++) {
342
		kpp = &r->fields[i];
343
		if (kpp->keypos == r->keysz)
344
			continue;
345
		assert(kpp->keypos < r->keysz);
346

347
		if (kpp->state != KPAIR_INVALID) {
348
			kpp->next = r->fieldmap[kpp->keypos];
349
			r->fieldmap[kpp->keypos] = kpp;
350
		} else {
351
			kpp->next = r->fieldnmap[kpp->keypos];
352
			r->fieldnmap[kpp->keypos] = kpp;
353
		}
354
	}
355

356
	for (i = 0; i < r->cookiesz; i++) {
357
		kpp = &r->cookies[i];
358
		if (kpp->keypos == r->keysz)
359
			continue;
360
		assert(kpp->keypos < r->keysz);
361

362
		if (kpp->state != KPAIR_INVALID) {
363
			kpp->next = r->cookiemap[kpp->keypos];
364
			r->cookiemap[kpp->keypos] = kpp;
365
		} else {
366
			kpp->next = r->cookienmap[kpp->keypos];
367
			r->cookienmap[kpp->keypos] = kpp;
368
		}
369
	}
370

371
	return KCGI_OK;
372
out:
373
	assert(ke != KCGI_OK);
374
	free(kp.key);
375
	free(kp.val);
376
	free(kp.file);
377
	free(kp.ctype);
378
	free(kp.xcode);
379
	return ke;
380
}
381

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.