ksgi

Форк
0
/
kcgiregress.c 
1210 строк · 25.8 Кб
1
/*	$Id$ */
2
/*
3
 * Copyright (c) 2014--2016, 2019 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 <sys/socket.h>
20
#include <sys/wait.h>
21
#include <sys/stat.h>
22
#include <sys/un.h>
23
#include <netinet/in.h>
24

25
#include <assert.h>
26
#include <ctype.h>
27
#include <errno.h>
28
#include <fcntl.h>
29
#include <inttypes.h>
30
#include <poll.h>
31
#include <signal.h>
32
#include <stdio.h>
33
#include <stdint.h>
34
#include <stdlib.h>
35
#include <string.h>
36
#include <unistd.h>
37

38
#include "kcgiregress.h"
39

40
#define	FCGI_HDR_SIZE		8
41

42
/*
43
 * The FastCGI header.
44
 * This is duplicated elsewhere in the code, but is harmless as it isn't
45
 * really going to change.
46
 */
47
struct 	fcgi_hdr {
48
	uint8_t	 version;
49
	uint8_t	 type;
50
#define	FCGI_HDR_END		3
51
#define	FCGI_HDR_HEAD		4
52
#define	FCGI_HDR_DATA_IN	5
53
#define	FCGI_HDR_DATA_OUT	6
54
	uint16_t requestId;
55
	uint16_t contentLength;
56
	uint8_t	 paddingLength;
57
	uint8_t	 reserved;
58
};
59

60
/*
61
 * Non-blocking read.
62
 * Returns <0 on failure (system error) or bytes read, which may be
63
 * zero in the event of EOF.
64
 */
65
static ssize_t
66
nb_read(int fd, void *buf, size_t bufsz)
67
{
68
	struct pollfd	 pfd;
69
	int		 rc;
70

71
	assert(buf != NULL && bufsz);
72

73
	pfd.fd = fd;
74
	pfd.events = POLLIN;
75

76
	if ((rc = poll(&pfd, 1, -1)) < 0) {
77
		perror("poll");
78
		return (-1);
79
	} else if (rc == 0) {
80
		fprintf(stderr, "poll: timeout!?\n");
81
		return (-1);
82
	} 
83
	
84
	return read(fd, buf, bufsz);
85
}
86

87
/*
88
 * Non-blocking write of "bufsz" bytes in "buf".
89
 * Return TRUE on success, FALSE on failure (system error).
90
 */
91
static int
92
nb_write(int fd, const void *buf, size_t bufsz)
93
{
94
	ssize_t	 	 ssz;
95
	size_t	 	 sz;
96
	struct pollfd	 pfd;
97
	int		 rc;
98

99
	if (buf == NULL || bufsz == 0)
100
		return 1;
101

102
	pfd.fd = fd;
103
	pfd.events = POLLOUT;
104

105
	for (sz = 0; sz < bufsz; sz += (size_t)ssz) {
106
		if ((rc = poll(&pfd, 1, -1)) < 0)
107
			perror("poll");
108
		else if (rc == 0)
109
			fprintf(stderr, "poll: timeout!?\n");
110
		else if ((ssz = write(fd, buf + sz, bufsz - sz)) < 0)
111
			perror("write");
112
		else if (sz > SIZE_MAX - (size_t)ssz)
113
			fprintf(stderr, "write: overflow: "
114
				"%zu, %zd", sz, ssz);
115
		else
116
			continue;
117
		return 0;
118
	}
119

120
	return 1;
121
}
122

123
/*
124
 * Blocking write of "sz" bytes in "buf".
125
 * Return TRUE on success, FALSE on failure (system error).
126
 */
127
static int
128
b_write(int fd, const void *buf, size_t sz)
129
{
130
	ssize_t	 ssz;
131
	size_t	 wsz = 0;
132

133
	while (sz > 0) {
134
		if ((ssz = write(fd, buf + wsz, sz)) == -1) {
135
			perror("write");
136
			return 0;
137
		}
138
		sz -= (size_t)ssz;
139
		wsz += (size_t)ssz;
140
	}
141

142
	return 1;
143
}
144

145
/*
146
 * Blocking read (discarding bytes).
147
 * Discards "sz" bytes and returns TRUE on success, FALSE on failure
148
 * (system error or EOF).
149
 */
150
static int
151
b_ignore(int fd, size_t sz)
152
{
153
	ssize_t	 ssz;
154
	char	 buf;
155

156
	while (sz > 0) {
157
		if ((ssz = read(fd, &buf, 1)) == -1) {
158
			perror("read");
159
			return 0;
160
		} else if (ssz == 0) {
161
			fputs("read: unexpected EOF\n", stderr);
162
			return 0;
163
		}
164
		sz--;
165
	}
166

167
	return 1;
168
}
169

170
/*
171
 * Blocking read.
172
 * Reads "sz" bytes into buf and returns TRUE on success, FALSE on
173
 * failure (system error or EOF).
174
 */
175
static int
176
b_read(int fd, void *buf, size_t sz)
177
{
178
	ssize_t	 ssz;
179
	size_t	 rsz = 0;
180

181
	while (sz > 0) {
182
		if ((ssz = read(fd, buf + rsz, sz)) == -1) {
183
			perror("read");
184
			return 0;
185
		} else if (ssz == 0) {
186
			fputs("read: unexpected EOF\n", stderr);
187
			return 0;
188
		}
189
		sz -= (size_t)ssz;
190
		rsz += (size_t)ssz;
191
	}
192

193
	return 1;
194
}
195

196
/*
197
 * Write a entire FastCGI header to the child.
198
 * Note that the members need to be in network-byte order, so perform
199
 * your htons and so on before invoking this function.
200
 */
201
static int
202
fcgi_hdr_write(int fd, const struct fcgi_hdr *hdr)
203
{
204

205
	if (!b_write(fd, &hdr->version, 1))
206
		fprintf(stderr, "%s: version\n", __func__);
207
	else if (!b_write(fd, &hdr->type, 1))
208
		fprintf(stderr, "%s: type\n", __func__);
209
	else if (!b_write(fd, &hdr->requestId, 2))
210
		fprintf(stderr, "%s: requestId\n", __func__);
211
	else if (!b_write(fd, &hdr->contentLength, 2))
212
		fprintf(stderr, "%s: data length\n", __func__);
213
	else if (!b_write(fd, &hdr->paddingLength, 1))
214
		fprintf(stderr, "%s: pad length\n", __func__);
215
	else if (!b_write(fd, &hdr->reserved, 1))
216
		fprintf(stderr, "%s: reserved\n", __func__);
217
	else
218
		return 1;
219

220
	return 0;
221
}
222

223
static int
224
fcgi_hdr_read(int fd, struct fcgi_hdr *hdr)
225
{
226
	char		 buf[FCGI_HDR_SIZE];
227

228
	if (!b_read(fd, buf, sizeof(buf))) {
229
		fprintf(stderr, "%s: header\n", __func__);
230
		return 0;
231
	}
232

233
	hdr->version = buf[0];
234
	hdr->type = buf[1];
235
	hdr->requestId = ntohs(*(uint16_t *)&buf[2]);
236
	hdr->contentLength = ntohs(*(uint16_t *)&buf[4]);
237
	hdr->paddingLength = buf[6];
238
	hdr->reserved = buf[7];
239

240
	return 1;
241
}
242

243
static int
244
fcgi_data_write(int fd, const void *buf, size_t sz)
245
{
246
	struct fcgi_hdr	 hdr;
247

248
	hdr.version = 1;
249
	hdr.type = FCGI_HDR_DATA_IN;
250
	hdr.requestId = htons(1);
251
	hdr.contentLength = htons(sz);
252
	hdr.paddingLength = 0;
253
	hdr.reserved = 0;
254

255
	if (!fcgi_hdr_write(fd, &hdr))
256
		fprintf(stderr, "%s: header\n", __func__);
257
	else if (!b_write(fd, buf, sz))
258
		fprintf(stderr, "%s: data\n", __func__);
259
	else
260
		return 1;
261

262
	return 0;
263
}
264

265
static int
266
fcgi_end_read(int fd, int *status)
267
{
268
	uint32_t	 st;
269
	uint8_t		 pst;
270
	uint8_t		 res[3];
271

272
	if (!b_read(fd, &st, 4)) {
273
		fprintf(stderr, "%s: status\n", __func__);
274
		return 0;
275
	} else if (!b_read(fd, &pst, 1)) {
276
		fprintf(stderr, "%s: flags\n", __func__);
277
		return 0;
278
	} else if (!b_read(fd, res, sizeof(res))) {
279
		fprintf(stderr, "%s: reserved\n", __func__);
280
		return 0;
281
	}
282

283
	/*
284
	 * We use EXIT_SUCCESS for our status message.
285
	 * See output.c for where this is set.
286
	 */
287

288
	*status = ntohl(st) == EXIT_SUCCESS ? 1 : 0;
289
	return 1;
290
}
291

292
static int
293
fcgi_begin_write(int fd)
294
{
295
	struct fcgi_hdr	 hdr;
296
	uint16_t	 role = htons(1);
297
	uint8_t		 flags = 0;
298
	uint8_t		 res[5];
299

300
	hdr.version = 1;
301
	hdr.type = 1;
302
	hdr.requestId = htons(1);
303
	hdr.contentLength = htons(8);
304
	hdr.paddingLength = 0;
305
	hdr.reserved = 0;
306

307
	memset(res, 0, sizeof(res));
308

309
	if (!fcgi_hdr_write(fd, &hdr))
310
		fprintf(stderr, "%s: header\n", __func__);
311
	else if (!b_write(fd, &role, 2))
312
		fprintf(stderr, "%s: role\n", __func__);
313
	else if (!b_write(fd, &flags, 1))
314
		fprintf(stderr, "%s: flags\n", __func__);
315
	else if (!b_write(fd, res, sizeof(res)))
316
		fprintf(stderr, "%s: reserved\n", __func__);
317
	else
318
		return 1;
319

320
	return 0;
321
}
322

323
/*
324
 * Set the environment variable for a CGI script.
325
 * This involves actually setting the environment variable.
326
 */
327
static int
328
dochild_params_cgi(const char *key, const char *val, void *arg)
329
{
330

331
	setenv(key, val, 1);
332
	return 1;
333
}
334

335
/*
336
 * Set the environment variable for a FastCGI script.
337
 * We'll need to bundle it into a FastCGI request and write that to the
338
 * child.
339
 */
340
static int
341
dochild_params_fcgi(const char *key, const char *val, void *arg)
342
{
343
	int	 	 fd = *(int *)arg;
344
	struct fcgi_hdr	 hdr;
345
	uint32_t	 lenl;
346
	uint8_t		 lens;
347
	size_t		 sz;
348

349
	sz = strlen(key) + (strlen(key) > 127 ? 4 : 1) +
350
		strlen(val) + (strlen(val) > 127 ? 4 : 1);
351

352
	/* Start with the FastCGI header. */
353

354
	hdr.version = 1;
355
	hdr.type = FCGI_HDR_HEAD;
356
	hdr.requestId = htons(1);
357
	hdr.contentLength = htons(sz);
358
	hdr.paddingLength = 0;
359
	hdr.reserved = 0;
360

361
	if (!fcgi_hdr_write(fd, &hdr)) {
362
		fprintf(stderr, "%s: header\n", __func__);
363
		return(0);
364
	}
365

366
	/* Key and value lengths. */
367

368
	if ((sz = strlen(key)) > 127) {
369
		lenl = htonl(sz);
370
		if (!b_write(fd, &lenl, 4)) {
371
			fprintf(stderr, "%s: key length", __func__);
372
			return 0;
373
		}
374
	} else {
375
		lens = sz;
376
		if (!b_write(fd, &lens, 1)) {
377
			fprintf(stderr, "%s: key length", __func__);
378
			return 0;
379
		}
380
	}
381
	if ((sz = strlen(val)) > 127) {
382
		lenl = htonl(sz);
383
		if (!b_write(fd, &lenl, 4)) {
384
			fprintf(stderr, "%s: val length", __func__);
385
			return 0;
386
		}
387
	} else {
388
		lens = sz;
389
		if (!b_write(fd, &lens, 1)) {
390
			fprintf(stderr, "%s: val length", __func__);
391
			return 0;
392
		}
393
	}
394

395
	/* Key and value data. */
396
	if (!b_write(fd, key, strlen(key))) {
397
		fprintf(stderr, "%s: key", __func__);
398
		return 0;
399
	} else if (!b_write(fd, val, strlen(val))) {
400
		fprintf(stderr, "%s: val", __func__);
401
		return 0;
402
	}
403

404
	return 1;
405
}
406

407
/*
408
 * Parse HTTP header lines from input.
409
 * This obviously isn't the best way of doing things, but it's simple
410
 * and easy to fix for the purposes of this regression suite.
411
 * This will continually parse lines from `fd' until it reaches a blank
412
 * line, at which point it will return control.
413
 * It returns zero on failure (read failure, or possibly write failure
414
 * when serialising to the CGI context) or non-zero on success.
415
 */
416
static int
417
dochild_params(int fd, void *arg, size_t *length,
418
	int (*fp)(const char *, const char *, void *)) 
419
{
420
	int		 first;
421
	char		 head[BUFSIZ], buf[BUFSIZ];
422
	ssize_t		 ssz;
423
	size_t		 sz;
424
	char		*cp, *path, *query, *key, *val;
425
	char		 c;
426
	extern char	*__progname;
427

428
	if (length != NULL)
429
		*length = 0;
430

431
	if (!fp("SCRIPT_NAME", __progname, arg))
432
		return 0;
433

434
	/*
435
	 * Read header lines without buffering and clobbering the data
436
	 * yet to be read on the wire.
437
	 * Process them as the environment input to our CGI.
438
	 */
439

440
	for (first = 1; ; ) {
441
		/*
442
		 * Start by reading the header itself into a
443
		 * fixed-length buffer, making sure to respect that the
444
		 * descriptor is non-blocking.
445
		 */
446

447
		for (sz = 0; sz < BUFSIZ; ) {
448
			ssz = nb_read(fd, &c, 1);
449
			if (ssz < 0) {
450
				perror("read");
451
				return 0;
452
			} else if (ssz == 0 || (head[sz++] = c) == '\n')
453
				break;
454
		}
455

456
		/* Strip CRLF. */
457

458
		if (sz < 2 || sz == BUFSIZ) {
459
			fprintf(stderr, "Bad HTTP header\n");
460
			return 0;
461
		} else if (head[sz - 2] != '\r') {
462
			fprintf(stderr, "Bad HTTP header CRLF\n");
463
			return 0;
464
		}
465
		head[sz - 2] = '\0';
466

467
		/* Empty line: now we're at the CGI document. */
468

469
		if (head[0] == '\0')
470
			break;
471

472
		/* Process our header. */
473

474
		if (first) {
475
			/* Snarf the first GET/POST line. */
476

477
			if (strncmp(head, "GET ", 4) == 0) {
478
				if (!fp("REQUEST_METHOD", "GET", arg))
479
					return 0;
480
				path = head + 4;
481
			} else if (strncmp(head, "POST ", 5) == 0) {
482
				if (!fp("REQUEST_METHOD", "POST", arg))
483
					return(0);
484
				path = head + 5;
485
			} else {
486
				fprintf(stderr, "Unknown HTTP "
487
					"first line: %s\n", head);
488
				return 0;
489
			}
490

491
			/* Split this into the path and query. */
492

493
			cp = path;
494
			while (*cp != '\0' && !isspace((unsigned char)*cp))
495
				cp++;
496
			*cp = '\0';
497
			if ((query = strchr(path, '?')) != NULL)
498
				*query++ = '\0';
499

500
			first = 0;
501
			if (!fp("PATH_INFO", path, arg))
502
				return 0;
503
			if (query != NULL)
504
				if (!fp("QUERY_STRING", query, arg))
505
					return 0;
506
			continue;
507
		}
508

509
		/* 
510
		 * Split headers into key/value parts.
511
		 * Strip the leading spaces on the latter. 
512
		 * Let baddies (no value) just go by.
513
		 */
514

515
		key = head;
516
		if ((val = strchr(key, ':')) == NULL)
517
			continue;
518
		*val++ = '\0';
519
		while (*val != '\0' && isspace((unsigned char)*val))
520
			val++;
521

522
		/* Recognise some attributes... */
523

524
		if (strcmp(key, "Content-Length") == 0) {
525
			if (length != NULL)
526
				*length = atoi(val);
527
			if (!fp("CONTENT_LENGTH", val, arg))
528
				return 0;
529
			continue;
530
		} else if (strcmp(key, "Content-Type") == 0) {
531
			if (!fp("CONTENT_TYPE", val, arg))
532
				return 0;
533
			continue;
534
		}
535

536
		/*
537
		 * Now we have "regular" attributes that we want to cast
538
		 * directly as HTTP attributes in the CGI environment.
539
		 */
540

541
		strlcpy(buf, "HTTP_", sizeof(buf));
542
		sz = strlcat(buf, key, sizeof(buf));
543
		assert(sz < sizeof(buf));
544
		for (cp = buf;  *cp != '\0'; cp++) 
545
			if (*cp == '-')
546
				*cp = '_';
547
			else if (isalpha((unsigned char)*cp))
548
				*cp = toupper((unsigned char)*cp);
549

550
		if (!fp(buf, val, arg))
551
			return 0;
552
	}
553

554
	return 1;
555
}
556

557
/*
558
 * First, read regress().
559
 * This is the "child" portion of that description.
560
 * Bind to the local port over which we'll directly accept test requests
561
 * from our regression suite.
562
 * This port is acting as an ad hoc web server.
563
 * Return the file descriptor on success or -1 on failure.
564
 */
565
static int
566
dochild_prepare(void)
567
{
568
	int	 	 s, in, opt = 1;
569
	struct sockaddr_in ad, rem;
570
	socklen_t	 len;
571

572
	memset(&ad, 0, sizeof(struct sockaddr_in));
573

574
	/*
575
	 * Bind and listen to our reusable testing socket.
576
	 * We pretty much just choose a random port for this.
577
	 */
578

579
	if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
580
		perror("socket");
581
		return (-1);
582
	} else if (setsockopt(s, SOL_SOCKET, 
583
		   SO_REUSEADDR, &opt, sizeof(opt)) == -1) {
584
		perror("setsockopt");
585
		close(s);
586
		return (-1);
587
	}
588

589
	ad.sin_family = AF_INET;
590
	ad.sin_port = htons(KCGI_REGRESS_PORT);
591
	ad.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
592

593
	if (bind(s, (struct sockaddr *)&ad, sizeof(ad)) == -1) {
594
		perror("bind");
595
		close(s);
596
		return (-1);
597
	} else if (listen(s, 1) == -1) {
598
		perror("listen");
599
		close(s);
600
		return (-1);
601
	}
602

603
	/*
604
	 * Tell our parent that we've bound to our socket and are ready
605
	 * to receive data.
606
	 * They'll do a waitpid() to see when we're sleeping, then wake
607
	 * us up when we're already ready to go.
608
	 */
609

610
	kill(getpid(), SIGSTOP);
611

612
	/*
613
	 * Wait for a single testing connection.
614
	 * When we accept it, immediately note as non-blocking: kcgi(3)
615
	 * uses polling, so it will need a non-blocking descriptor.
616
	 */
617

618
	len = sizeof(struct sockaddr_in);
619
	if ((in = accept(s, (struct sockaddr *)&rem, &len)) == -1) {
620
		perror("accept");
621
		close(s);
622
		return (-1);
623
	} else if (fcntl(in, F_SETFL, O_NONBLOCK) == -1) {
624
		perror("fcntl: O_NONBLOCK");
625
		close(s);
626
		close(in);
627
		return 0;
628
	}
629

630
	close(s);
631
	return in;
632
}
633

634
/*
635
 * Broker a FastCGI process child.
636
 * Return zero on failure and non-zero on success.
637
 */
638
static int
639
dochild_fcgi(kcgi_regress_server child, void *carg)
640
{
641
	int			 in, rc = 0, fd;
642
	size_t			 sz, len, vecsz = 0, headsz;
643
	pid_t			 pid;
644
	struct sockaddr_un	 un;
645
	struct sockaddr		*ss;
646
	ssize_t			 ssz;
647
	extern char		*__progname;
648
	const char		*end = NULL, *start, *msg, *ovec, *cp;
649
	char			 sfn[22], buf[BUFSIZ];
650
	char			*vec = NULL;
651
	void			*pp;
652
	struct fcgi_hdr		 hdr;
653
	mode_t		  	 mode;
654

655
	/* 
656
	 * Create a temporary file, close it, then unlink it.
657
	 * The child will recreate this as a socket.
658
	 */
659

660
	strlcpy(sfn, "/tmp/kfcgi.XXXXXXXXXX", sizeof(sfn));
661

662
	/* This shuts up Coverity. */
663

664
	mode = umask(S_IXUSR | S_IRWXG | S_IRWXO);
665
	if ((fd = mkstemp(sfn)) == -1) {
666
		perror(sfn);
667
		return 0;
668
	} else if (close(fd) == -1 || unlink(sfn) == -1) {
669
		perror(sfn);
670
		return 0;
671
	}
672
	umask(mode);
673

674
	/* Do the usual dance to set up UNIX sockets. */
675

676
	ss = (struct sockaddr *)&un;
677
	memset(&un, 0, sizeof(un));
678
	un.sun_family = AF_UNIX;
679
	sz = strlcpy(un.sun_path, sfn, sizeof(un.sun_path));
680
	if (sz >= sizeof(un.sun_path)) {
681
		fprintf(stderr, "socket path to long\n");
682
		return 0;
683
	}
684
#if !defined(__linux__) && !defined(__sun)
685
	un.sun_len = sz;
686
#endif
687

688
	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
689
		perror("socket");
690
		return 0;
691
	} else if (bind(fd, ss, sizeof(un)) == -1) {
692
		perror(sfn);
693
		close(fd);
694
		return 0;
695
	} else if (listen(fd, 5) == -1) {
696
		perror(sfn);
697
		close(fd);
698
		return 0;
699
	}
700

701
	/*
702
	 * Now fork the FastCGI process.
703
	 * We need to use a separate process because we're going to
704
	 * package the request as a FastCGI request and ship it into the
705
	 * UNIX socket.
706
	 */
707

708
	if ((pid = fork()) == -1) {
709
		perror("fork");
710
		unlink(sfn);
711
		close(fd);
712
		return 0;
713
	} else if (pid == 0) {
714
		if (dup2(fd, STDIN_FILENO) == -1)
715
			_exit(EXIT_FAILURE);
716
		close(fd);
717
		return child(carg);
718
	}
719

720
	/* 
721
	 * Close the socket, as we're going to connect to it.
722
	 * The child has a reference to the object (via its dup2), so
723
	 * we're not going to totally remove the file.
724
	 */
725

726
	close(fd);
727
	fd = -1;
728

729
	/* Get the next incoming connection and FILE-ise it. */
730

731
	if ((in = dochild_prepare()) == -1) 
732
		goto out;
733

734
	/*
735
	 * Open a new socket to the FastCGI object and connect to it,
736
	 * reusing the prior socket address.
737
	 * Then remove the object, as nobody needs it any more.
738
	 */
739

740
	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
741
		perror(sfn);
742
		goto out;
743
	} else if (connect(fd, ss, sizeof(un)) == -1) {
744
		perror(sfn);
745
		goto out;
746
	} else if (unlink(sfn) == -1) {
747
		perror(sfn);
748
		goto out;
749
	} 
750
	sfn[0] = '\0';
751

752
	/* Write the request, its parameters, and all data. */
753

754
	if (!fcgi_begin_write(fd))
755
		goto out;
756
	if (!dochild_params(in, &fd, &len, dochild_params_fcgi))
757
		goto out;
758

759
	/* Forward any data from the test's parent. */
760

761
	while (len > 0) {
762
		ssz = nb_read(in, buf, 
763
			len < sizeof(buf) ? len : sizeof(buf));
764
		if (ssz < 0) {
765
			perror("read");
766
			goto out;
767
		} else if (ssz == 0)
768
			break;
769

770
		if (!fcgi_data_write(fd, buf, ssz)) {
771
			fprintf(stderr, "%s: stdout\n", __func__);
772
			goto out;
773
		}
774
		len -= ssz;
775
	}
776

777
	/* Indicate end of input. */
778

779
	if (!fcgi_data_write(fd, NULL, 0)) {
780
		fprintf(stderr, "%s: stdout (FIN)\n", __func__);
781
		goto out;
782
	}
783

784
	/* Read and buffer into "vec" til EOF or end of headers. */
785

786
	while (fcgi_hdr_read(fd, &hdr)) {
787
		if (hdr.type == FCGI_HDR_END) {
788
			if (!fcgi_end_read(fd, &rc)) {
789
				fprintf(stderr, "%s: bad fin\n", __func__);
790
				goto out;
791
			}
792
			break;
793
		} else if (hdr.type != FCGI_HDR_DATA_OUT) {
794
			fprintf(stderr, "%s: bad type: %" 
795
				PRIu8 "\n", __func__, hdr.type);
796
			goto out;
797
		}
798
		if (hdr.contentLength > 0) {
799
			pp = realloc(vec, vecsz + hdr.contentLength);
800
			if (pp == NULL) {
801
				perror(NULL);
802
				goto out;
803
			}
804
			vec = pp;
805
			if (!b_read(fd, vec + vecsz, hdr.contentLength)) {
806
				fprintf(stderr, "%s: bad read\n", __func__);
807
				goto out;
808
			}
809
			vecsz += hdr.contentLength;
810
		}
811
		if (b_ignore(fd, hdr.paddingLength) == 0) {
812
			fprintf(stderr, "%s: bad ignore\n", __func__);
813
			goto out;
814
		} 
815
		if (vecsz == 0)
816
			continue;
817
		end = memmem(vec, vecsz, "\r\n\r\n", 4);
818
		if (end != NULL)
819
			break;
820
	}
821

822
	/* Uh-oh: we read the whole message and no headers. */
823

824
	if (end == NULL) {
825
		fprintf(stderr, "FastCGI script did "
826
			"not terminate headers\n");
827
		rc = 1;
828
		goto out;
829
	}
830

831
	/* 
832
	 * No "status" is ok: convert it to a 200.
833
	 * If we do have a status, print it with the HTTP type.
834
	 */
835

836
	headsz = (size_t)(end - vec);
837

838
	if ((start = memmem(vec, headsz, "Status:", 7)) == NULL) {
839
		msg = "HTTP/1.1 200 OK\r\n";
840
		if (!b_write(in, msg, strlen(msg)))
841
			goto out;
842
		fprintf(stderr, "FastCGI script did "
843
			"not specify status\n");
844
		ovec = vec;
845
	} else {
846
		msg = "HTTP/1.1";
847
		if (!nb_write(in, msg, strlen(msg)))
848
			goto out;
849
		cp = start + 7;
850
		while (cp < end) {
851
			if (!nb_write(in, cp, 1))
852
				goto out;
853
			cp++;
854
			if (cp[-1] == '\n')
855
				break;
856
		}
857
		if (!nb_write(in, vec, (size_t)(start - vec)))
858
			goto out;
859
		vecsz -= (cp - vec);
860
		ovec = cp;
861
	}
862

863
	/* Print remaining buffered data. */
864

865
	if (!nb_write(in, ovec, vecsz))
866
		goto out;
867

868
	/* Forward remaining script output. */
869

870
	while (fcgi_hdr_read(fd, &hdr)) {
871
		if (hdr.type == FCGI_HDR_END) {
872
			if (!fcgi_end_read(fd, &rc)) {
873
				fprintf(stderr, "%s: bad fin\n", __func__);
874
				goto out;
875
			}
876
			rc = 1;
877
			break;
878
		} else if (hdr.type != FCGI_HDR_DATA_OUT) {
879
			fprintf(stderr, "%s: bad type: %" 
880
				PRIu8 "\n", __func__, hdr.type);
881
			goto out;
882
		}
883
		while (hdr.contentLength > 0) {
884
			vecsz = hdr.contentLength > sizeof(buf) ?
885
				sizeof(buf) : hdr.contentLength;
886
			if (!b_read(fd, buf, vecsz)) {
887
				fprintf(stderr, "%s: bad read\n", __func__);
888
				goto out;
889
			} else if (!nb_write(in, buf, vecsz)) {
890
				fprintf(stderr, "%s: bad write\n", __func__);
891
				goto out;
892
			}
893
			hdr.contentLength -= vecsz;
894
		}
895
		if (b_ignore(fd, hdr.paddingLength) == 0) {
896
			fprintf(stderr, "%s: bad ignore\n", __func__);
897
			goto out;
898
		} 
899
	}
900

901
	if (rc == 0)
902
		fprintf(stderr, "%s: no fin\n", __func__);
903
out:
904
	/* 
905
	 * Begin by asking the child to exit.
906
	 * Then close all of our comm channels.
907
	 */
908

909
	kill(pid, SIGTERM);
910
	if (in != -1)
911
		close(in);
912
	if (sfn[0] != '\0')
913
		unlink(sfn);
914
	if (fd != -1)
915
		close(fd);
916

917
	/*
918
	 * Now mandate that the child dies and reap its resources.
919
	 * FIXME: we might kill the process before it's done actually
920
	 * terminating, which is unfair and will raise spurious
921
	 * warnings elsewhere.
922
	 */
923

924
	if (waitpid(pid, NULL, 0) == -1)
925
		perror("waitpid");
926
	free(vec);
927
	return rc;
928
}
929

930
/*
931
 * Broker a CGI process child.
932
 * Return zero on failure and non-zero on success.
933
 */
934
static int
935
dochild_cgi(kcgi_regress_server child, void *carg)
936
{
937
	int		 in, fd[2], rc;
938
	const char	*msg;
939
	pid_t		 pid;
940
	char		*vec, *end, *start, *cp, *ovec;
941
	size_t		 vecsz, headsz;
942
	void		*pp;
943
	char		 buf[BUFSIZ];
944
	ssize_t		 ssz;
945

946
	if ((in = dochild_prepare()) == -1) 
947
		return 0;
948

949
	/*
950
	 * We need to do some filtering from the CGI script's output
951
	 * (just its Status message), so create a socketpair which we'll
952
	 * use to scrub its output.
953
	 * This is because the CGI protocol is stupid: it would have
954
	 * been a lot easier to just require an HTTP status message, but
955
	 * I guess the intent was to make the web server worry about
956
	 * formatting for various versions of HTTP.
957
	 * Whatever.
958
	 */
959

960
	if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fd) == -1) {
961
		perror("socketpair");
962
		close(in);
963
		return 0;
964
	} 
965

966
	/* Launch the actual CGI process. */
967

968
	if ((pid = fork()) == -1) {
969
		perror("fork");
970
		close(fd[0]);
971
		close(fd[1]);
972
		close(in);
973
		return 0;
974
	} else if (pid == 0) {
975
		close(fd[1]);
976
		/*
977
		 * First, we suck down the HTTP headeres into our CGI
978
		 * environment and run the child.
979
		 * Next, re-assign our stdin to be the server's file
980
		 * descriptor "in".
981
		 * Then assign our stdout to be fd[0].
982
		 */
983

984
		if (!dochild_params
985
		    (in, NULL, NULL, dochild_params_cgi)) {
986
			close(in);
987
			close(fd[0]);
988
			return(0);
989
		}
990

991
		if (dup2(in, STDIN_FILENO) != STDIN_FILENO) {
992
			perror("dup2");
993
			close(in);
994
			close(fd[0]);
995
			_exit(EXIT_FAILURE);
996
		} 
997
		close(in);
998

999
		if (dup2(fd[0], STDOUT_FILENO) != STDOUT_FILENO) {
1000
			perror("dup2");
1001
			close(fd[0]);
1002
			return 0;
1003
		} 
1004
		close(fd[0]);
1005

1006
		child(carg);
1007
		_exit(in ? EXIT_SUCCESS : EXIT_FAILURE);
1008
	}
1009

1010
	/*
1011
	 * We're in the parent.
1012
	 * Read and buffer the output of the CGI process until we get
1013
	 * its Status value.
1014
	 * Do so by copying through a static buffer into a growable
1015
	 * vector that we'll scan for the Status message to re-write.
1016
	 * Of course, if the CGI process is ill-designed, we'll consume
1017
	 * all of our memory and die.
1018
	 */
1019

1020
	close(fd[0]);
1021
	vecsz = 0;
1022
	vec = end = NULL;
1023
	rc = 0;
1024

1025
	while ((ssz = read(fd[1], buf, sizeof(buf))) > 0) {
1026
		pp = realloc(vec, vecsz + ssz);
1027
		if (pp == NULL) {
1028
			perror("realloc");
1029
			goto out;
1030
		}
1031
		vec = pp;
1032
		memcpy(vec + vecsz, buf, ssz);
1033
		vecsz += ssz;
1034
		end = memmem(vec, vecsz, "\r\n\r\n", 4);
1035
		if (end != NULL)
1036
			break;
1037
	}
1038

1039
	if (ssz < 0) {
1040
		perror("read");
1041
		goto out;
1042
	}
1043

1044
	/*
1045
	 * Now all our headers are in vec plus extra; of if it's NULL,
1046
	 * we reached the end of input without getting headers.
1047
	 * Now we start to scan for the status line or dump.
1048
	 */
1049

1050
	if (end != NULL) {
1051
		/* Look for the status field. */
1052

1053
		headsz = (size_t)(end - vec);
1054
		start = memmem(vec, headsz, "Status:", 7);
1055
		if (start == NULL) {
1056
			/*
1057
			 * No status field.
1058
			 * This is Ok (according to CGI).
1059
			 * However, we do not provide a valid status, so
1060
			 * do what others do and 200 it.
1061
			 */
1062

1063
			msg = "HTTP/1.1 200 OK\r\n";
1064
			if (!nb_write(in, msg, strlen(msg)))
1065
				goto out;
1066
			fprintf(stderr, "CGI script did "
1067
				"not specify status\n");
1068
			ovec = vec;
1069
		} else {
1070
			/*
1071
			 * We found the status.
1072
			 * Print it out now, then everything that came
1073
			 * before it.
1074
			 */
1075

1076
			msg = "HTTP/1.1";
1077
			if (!nb_write(in, msg, strlen(msg)))
1078
				goto out;
1079
			cp = start + 7;
1080
			while (cp < end) {
1081
				if (!nb_write(in, cp, 1))
1082
					goto out;
1083
				cp++;
1084
				if (cp[-1] == '\n')
1085
					break;
1086
			}
1087
			if (!nb_write(in, vec, (size_t)(start - vec)))
1088
				goto out;
1089
			vecsz -= (cp - vec);
1090
			ovec = cp;
1091
		}
1092

1093
		/*
1094
		 * Print everything else in our vector array, then poll
1095
		 * on the CGI script til its dry.
1096
		 */
1097

1098
		if (!nb_write(in, ovec, vecsz))
1099
			goto out;
1100

1101
		while ((ssz = read(fd[1], buf, sizeof(buf))) > 0)
1102
			if ( ! nb_write(in, buf, ssz))
1103
				goto out;
1104

1105
		if (ssz < 0) {
1106
			perror("read");
1107
			goto out;
1108
		}
1109
	} else {
1110
		if (!nb_write(in, vec, vecsz))
1111
			goto out;
1112
		fprintf(stderr, "CGI script did "
1113
			"not terminate headers\n");
1114
	} 
1115

1116
	rc = 1;
1117
out:
1118
	if (waitpid(pid, NULL, 0) == -1)
1119
		perror("waitpid");
1120

1121
	free(vec);
1122
	close(in);
1123
	close(fd[1]);
1124
	return rc;
1125
}
1126

1127
/*
1128
 * This is the real beginning of the regression system.
1129
 * Create a child process first, and wait for it to go to sleep.
1130
 * When the child has gone to sleep, we know that it has started up and
1131
 * bound to a socket, so we can wake it up (SIGCONT) and test against
1132
 * the open socket.
1133
 */
1134
static int
1135
regress(int fastcgi,
1136
	kcgi_regress_client parent, void *parg, 
1137
	kcgi_regress_server child, void *carg)
1138
{
1139
	pid_t	 chld, pid;
1140
	int	 rc, st;
1141

1142
	/* 
1143
	 * Create our "test framework" child.
1144
	 * The child will return EXIT_SUCCESS or EXIT_FAILURE in the
1145
	 * usual way.
1146
	 */
1147

1148
	if ((chld = fork()) == -1) {
1149
		perror(NULL);
1150
		exit(EXIT_FAILURE);
1151
	} else if (chld == 0) {
1152
		rc = fastcgi ?
1153
			dochild_fcgi(child, carg) :
1154
			dochild_cgi(child, carg);
1155
		exit(rc ? EXIT_SUCCESS : EXIT_FAILURE);
1156
	}
1157

1158
	/* 
1159
	 * Wait for it to sleep.
1160
	 * We do this to prevent the "parent" from trying to access the
1161
	 * web application over a socket that hasn't been opened yet.
1162
	 */
1163

1164
	do {
1165
		pid = waitpid(chld, &st, WUNTRACED);
1166
	} while (pid == -1 && errno == EINTR);
1167

1168
	/*
1169
	 * Once the child sleeps, we know that it has bound itself to
1170
	 * the listening socket. 
1171
	 * So simply wake it up and continue our work.
1172
	 */
1173

1174
	if (pid == -1) {
1175
		perror(NULL);
1176
		exit(EXIT_FAILURE);
1177
	} else if (!WIFSTOPPED(st)) {
1178
		fprintf(stderr, "child not sleeping\n");
1179
		exit(EXIT_FAILURE);
1180
	} else if (kill(chld, SIGCONT) == -1) {
1181
		perror(NULL);
1182
		exit(EXIT_FAILURE);
1183
	}
1184

1185
	rc = parent(parg);
1186

1187
	if (waitpid(pid, &st, 0) == -1) {
1188
		perror(NULL);
1189
		exit(EXIT_FAILURE);
1190
	} 
1191

1192
	return(rc && WIFEXITED(st) && 
1193
	       EXIT_SUCCESS == WEXITSTATUS(st));
1194
}
1195

1196
int
1197
kcgi_regress_cgi(kcgi_regress_client parent, void *parg,
1198
	kcgi_regress_server child, void *carg)
1199
{
1200

1201
	return regress(0, parent, parg, child, carg);
1202
}
1203

1204
int
1205
kcgi_regress_fcgi(kcgi_regress_client parent, void *parg,
1206
	kcgi_regress_server child, void *carg)
1207
{
1208

1209
	return regress(1, parent, parg, child, carg);
1210
}
1211

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

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

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

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