ksgi

Форк
0
/
wrappers.c 
631 строка · 13.4 Кб
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 <sys/stat.h>
20
#include <sys/socket.h>
21
#include <sys/uio.h>
22
#include <sys/wait.h>
23

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

36
#include "kcgi.h"
37
#include "extern.h"
38

39
int
40
kxvasprintf(char **p, const char *fmt, va_list ap)
41
{
42
	int	 len;
43

44
	if ((len = vasprintf(p, fmt, ap)) >= 0)
45
		return len;
46

47
	kutil_warn(NULL, NULL, "vasprintf");
48
	*p = NULL;
49
	return -1;
50
}
51

52
int
53
kxasprintf(char **p, const char *fmt, ...)
54
{
55
	va_list	 ap;
56
	int	 ret;
57

58
	va_start(ap, fmt);
59
	ret = kxvasprintf(p, fmt, ap);
60
	va_end(ap);
61
	return ret;
62
}
63

64
void *
65
kxcalloc(size_t nm, size_t sz)
66
{
67
	void	 *p;
68

69
	if (nm == 0 || sz == 0)  {
70
		kutil_warnx(NULL, NULL, "calloc: zero length");
71
		return NULL;
72
	} else if ((p = calloc(nm, sz)) != NULL)
73
		return p;
74

75
	kutil_warn(NULL, NULL, "calloc: %zu, %zu", nm, sz);
76
	return NULL;
77
}
78

79
void *
80
kxmalloc(size_t sz)
81
{
82
	void	 *p;
83

84
	if (sz == 0) {
85
		kutil_warnx(NULL, NULL, "malloc: zero length");
86
		return NULL;
87
	} else if ((p = malloc(sz)) != NULL)
88
		return p;
89

90
	kutil_warn(NULL, NULL, "malloc: %zu", sz);
91
	return NULL;
92
}
93

94
void *
95
kxrealloc(void *pp, size_t sz)
96
{
97
	void	 *p;
98

99
	if (sz == 0) {
100
		kutil_warnx(NULL, NULL, "realloc: zero length");
101
		return NULL;
102
	} else if ((p = realloc(pp, sz)) != NULL)
103
		return p;
104

105
	kutil_warn(NULL, NULL, "realloc: %zu", sz);
106
	return NULL;
107
}
108

109
void *
110
kxreallocarray(void *pp, size_t nm, size_t sz)
111
{
112
	void	 *p;
113

114
	if (sz == 0 || nm == 0) {
115
		kutil_warnx(NULL, NULL, "reallocarray: zero length");
116
		return NULL;
117
	} else if ((p = reallocarray(pp, nm, sz)) != NULL)
118
		return p;
119

120
	kutil_warn(NULL, NULL, "reallocarray: %zu, %zu", nm, sz);
121
	return NULL;
122
}
123

124
char *
125
kxstrdup(const char *cp)
126
{
127
	char	*p;
128

129
	if (cp == NULL) {
130
		kutil_warnx(NULL, NULL, "strdup: NULL string");
131
		return NULL;
132
	} else if ((p = strdup(cp)) != NULL)
133
		return p;
134

135
	kutil_warn(NULL, NULL, "strdup");
136
	return NULL;
137
}
138

139
/*
140
 * waitpid() and logging anything but a return with EXIT_SUCCESS.
141
 * Returns KCGI_OK on EXIT_SUCCESS, KCGI_SYSTEM on waitpid() error,
142
 * KCGI_FORM on child process failure.
143
 */
144
enum kcgi_err
145
kxwaitpid(pid_t pid)
146
{
147
	int	st;
148

149
	if (waitpid(pid, &st, 0) == -1) {
150
		kutil_warn(NULL, NULL, "waitpid");
151
		return KCGI_SYSTEM;
152
	} else if (WIFEXITED(st) && WEXITSTATUS(st) == EXIT_SUCCESS)
153
		return KCGI_OK;
154

155
	if (WIFEXITED(st))
156
		kutil_warnx(NULL, NULL, "waitpid: child failure");
157
	else 
158
		kutil_warnx(NULL, NULL, "waitpid: child signal");
159

160
	return KCGI_FORM;
161
}
162

163
/*
164
 * Set a file-descriptor as being non-blocking.
165
 * Returns KCGI_SYSTEM on error, KCGI_OK on success.
166
 */
167
enum kcgi_err
168
kxsocketprep(int sock)
169
{
170
	int	 fl;
171

172
	if ((fl = fcntl(sock, F_GETFL, 0)) == -1) {
173
		kutil_warn(NULL, NULL, "fcntl");
174
		return KCGI_SYSTEM;
175
	} else if (fcntl(sock, F_SETFL, fl | O_NONBLOCK) == -1) {
176
		kutil_warn(NULL, NULL, "fcntl");
177
		return KCGI_SYSTEM;
178
	}
179

180
	return KCGI_OK;
181
}
182

183
/*
184
 * Create a non-blocking socketpair (AF_UNIX, SOCK_STREAM, protocol 0).
185
 * Return KCGI_ENFILE on temporary failure, KCGI_SYSTEM on fatal error,
186
 * KCGI_OK on success.
187
 * The input socket pair is only valid on success.
188
 */
189
enum kcgi_err
190
kxsocketpair(int sock[2])
191
{
192
	int	 	 rc;
193
	enum kcgi_err	 er;
194

195
	rc = socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
196
	if (rc == -1 && (EMFILE == errno || ENFILE == errno)) {
197
		kutil_warn(NULL, NULL, "socketpair");
198
		return KCGI_ENFILE;
199
	} else if (rc == -1) {
200
		kutil_warn(NULL, NULL, "socketpair");
201
		return KCGI_SYSTEM;
202
	}
203

204
	if ((er = kxsocketprep(sock[0])) == KCGI_OK &&
205
	   ((er = kxsocketprep(sock[1])) == KCGI_OK))
206
		return KCGI_OK;
207
	
208
	close(sock[0]);
209
	close(sock[1]);
210
	return er;
211
}
212

213
/*
214
 * Write a string "buf".
215
 * If "buf" is NULL, then write a zero-length string.
216
 * See fullreadword().
217
 * On error (which shouldn't happen), this will exit the process with
218
 * the exit code EXIT_FAILURE.
219
 */
220
void
221
fullwriteword(int fd, const char *buf)
222
{
223
	size_t	 sz;
224

225
	if (buf != NULL) {
226
		sz = strlen(buf);
227
		fullwrite(fd, &sz, sizeof(size_t));
228
		fullwrite(fd, buf, sz);
229
	} else {
230
		sz = 0;
231
		fullwrite(fd, &sz, sizeof(size_t));
232
	}
233
}
234

235
/*
236
 * This is like fullwrite() but it does not bail out on errors.
237
 * We need this for writing our response to a socket that may be closed
238
 * at any time, like the FastCGI one.
239
 * Returns KCGI_SYSTEM, KCGI_HUP, or KCGI_OK.
240
 * Ignores SIGPIPE, restoring it on exit.
241
 */
242
enum kcgi_err
243
fullwritenoerr(int fd, const void *buf, size_t bufsz)
244
{
245
	ssize_t	 	  ssz;
246
	size_t	 	  sz;
247
	struct pollfd	  pfd;
248
	int		  rc;
249
	enum kcgi_err	  er = KCGI_OK;
250
	void		(*sig)(int);
251

252
	pfd.fd = fd;
253
	pfd.events = POLLOUT;
254

255
	if ((sig = signal(SIGPIPE, SIG_IGN)) == SIG_ERR) {
256
		kutil_warn(NULL, NULL, "signal");
257
		return KCGI_SYSTEM;
258
	}
259

260
	for (sz = 0; sz < bufsz; sz += (size_t)ssz) {
261
		if ((rc = poll(&pfd, 1, -1)) < 0) {
262
			kutil_warn(NULL, NULL, "poll");
263
			er = KCGI_SYSTEM;
264
			break;
265
		} else if (rc == 0) {
266
			kutil_warnx(NULL, NULL, "poll: timeout!?");
267
			ssz = 0;
268
			continue;
269
		}
270

271
		if (pfd.revents & POLLHUP) {
272
			kutil_warnx(NULL, NULL, "poll: hangup");
273
			er = KCGI_HUP;
274
			break;
275
		} else if (pfd.revents & POLLERR) {
276
			kutil_warnx(NULL, NULL, "poll: error");
277
			er = KCGI_SYSTEM;
278
			break;
279
		}
280

281
		/* See note in fullwrite(). */
282

283
#ifdef __APPLE__
284
		if (!(pfd.revents & POLLOUT) && 
285
		    !(pfd.revents & POLLNVAL)) {
286
			kutil_warnx(NULL, NULL, "poll: no output");
287
			er = KCGI_SYSTEM;
288
			break;
289
		}
290
#else
291
		if (!(pfd.revents & POLLOUT)) {
292
			kutil_warnx(NULL, NULL, "poll: no output");
293
			er = KCGI_SYSTEM;
294
			break;
295
		}
296
#endif
297

298
		if ((ssz = write(fd, buf + sz, bufsz - sz)) < 0) {
299
			er = errno == EPIPE ? KCGI_HUP : KCGI_SYSTEM;
300
			kutil_warn(NULL, NULL, "write");
301
			break;
302
		} else if (sz > SIZE_MAX - (size_t)ssz) {
303
			kutil_warnx(NULL, NULL, "write: overflow");
304
			er = KCGI_SYSTEM;
305
			break;
306
		} 
307
	}
308

309
	if (signal(SIGPIPE, sig) == SIG_ERR) {
310
		kutil_warn(NULL, NULL, "signal");
311
		er = KCGI_SYSTEM;
312
	}
313

314
	return er;
315
}
316

317
/*
318
 * Write "buf", which can be NULL so long as bufsz is zero in which case
319
 * it's a noop.
320
 * On error (which shouldn't happen), this will exit the process with
321
 * the exit code EXIT_FAILURE.
322
 */
323
void
324
fullwrite(int fd, const void *buf, size_t bufsz)
325
{
326
	ssize_t	 	 ssz;
327
	size_t	 	 sz;
328
	struct pollfd	 pfd;
329
	int		 rc;
330

331
	if (bufsz == 0)
332
		return;
333

334
	assert(buf != NULL);
335
	pfd.fd = fd;
336
	pfd.events = POLLOUT;
337

338
	for (sz = 0; sz < bufsz; sz += (size_t)ssz) {
339
		if ((rc = poll(&pfd, 1, INFTIM)) == 0) {
340
			kutil_warnx(NULL, NULL, "poll: timeout!?");
341
			ssz = 0;
342
			continue;
343
		} else if (rc < 0)
344
			kutil_err(NULL, NULL, "poll");
345

346
		if (pfd.revents & POLLHUP)
347
			kutil_errx(NULL, NULL, "poll: hangup");
348
		else if (pfd.revents & POLLERR)
349
			kutil_errx(NULL, NULL, "poll: error");
350

351
		/*
352
		 * This CPP exists because testing on Mac OS X will have
353
		 * "fd" point to a device and poll(2) returns POLLNVAL
354
		 * if the descriptor is to a device.
355
		 * This is documented in the BUGS section of poll(2).
356
		 */
357
#ifdef __APPLE__
358
		if (!(pfd.revents & POLLOUT) && 
359
		    !(pfd.revents & POLLNVAL))
360
			kutil_errx(NULL, NULL, "poll: no output");
361
#else
362
		if (!(pfd.revents & POLLOUT))
363
			kutil_errx(NULL, NULL, "poll: no output");
364
#endif
365

366
		if ((ssz = write(fd, buf + sz, bufsz - sz)) < 0)
367
			kutil_err(NULL, NULL, "write");
368
		else if (sz > SIZE_MAX - (size_t)ssz)
369
			kutil_errx(NULL, NULL, "write: overflow");
370
	}
371
}
372

373
/*
374
 * Read the contents of "buf" of size "bufsz".
375
 * If "eofok" is set, return zero if there is no data to read.
376
 * If it's not set, this condition returns <0.
377
 * Returns <0 on errors and >0 on success.
378
 */
379
int
380
fullread(int fd, void *buf, size_t bufsz, int eofok, enum kcgi_err *er)
381
{
382
	ssize_t	 	 ssz;
383
	size_t	 	 sz;
384
	struct pollfd	 pfd;
385
	int		 rc;
386

387
	pfd.fd = fd;
388
	pfd.events = POLLIN;
389

390
	for (sz = 0; sz < bufsz; sz += (size_t)ssz) {
391
		if ((rc = poll(&pfd, 1, INFTIM)) < 0) {
392
			kutil_warn(NULL, NULL, "poll");
393
			*er = KCGI_SYSTEM;
394
			return (-1);
395
		} else if (rc == 0) {
396
			kutil_warnx(NULL, NULL, "poll: timeout!?");
397
			ssz = 0;
398
			continue;
399
		} 
400
		
401
		if (!(pfd.revents & POLLIN)) {
402
			if (eofok && sz == 0) {
403
				*er = KCGI_OK;
404
				return 0;
405
			}
406
			kutil_warnx(NULL, NULL, "poll: no input");
407
			*er = KCGI_FORM;
408
			return (-1);
409
		} 
410
		
411
		if ((ssz = read(fd, buf + sz, bufsz - sz)) < 0) {
412
			kutil_warn(NULL, NULL, "read");
413
			*er = KCGI_SYSTEM;
414
			return (-1);
415
		} else if (ssz == 0 && sz > 0) {
416
			kutil_warnx(NULL, NULL, "read: short read");
417
			*er = KCGI_FORM;
418
			return (-1);
419
		} else if (ssz == 0 && sz == 0 && !eofok) {
420
			kutil_warnx(NULL, NULL, "read: end of file");
421
			*er = KCGI_FORM;
422
			return (-1);
423
		} else if (ssz == 0 && sz == 0 && eofok) {
424
			*er = KCGI_OK;
425
			return 0;
426
		} else if (sz > SIZE_MAX - (size_t)ssz) {
427
			kutil_warnx(NULL, NULL, "read: overflow");
428
			*er = KCGI_FORM;
429
			return (-1);
430
		}
431
	}
432

433
	*er = KCGI_OK;
434
	return 1;
435
}
436

437
/*
438
 * Read a string from the stream.
439
 * Return KCGI_OK on success or another on error.
440
 * This will set cp to NULL and sz to zero on failure.
441
 * The cp array (on success) is always NUL-terminated, although the
442
 * buffer it reads is opaque.
443
 * On success, "sz" may legit be zero.
444
 */
445
enum kcgi_err
446
fullreadwordsz(int fd, char **cp, size_t *sz)
447
{
448
	enum kcgi_err	 ke;
449
	int		 rc;
450

451
	*cp = NULL;
452
	*sz = 0;
453

454
	if (fullread(fd, sz, sizeof(size_t), 0, &ke) < 0)
455
		return ke;
456

457
	/* TODO: check additive overflow of "sz + 1". */
458

459
	if ((*cp = kxmalloc(*sz + 1)) == NULL) {
460
		*sz = 0;
461
		return KCGI_ENOMEM;
462
	}
463
	(*cp)[*sz] = '\0';
464

465
	if (*sz == 0)
466
		return KCGI_OK;
467

468
	/* Because we don't set "eofok", never returns zero. */
469

470
	if ((rc = fullread(fd, *cp, *sz, 0, &ke)) > 0) {
471
		assert(ke == KCGI_OK);
472
		return ke;
473
	}
474

475
	assert(rc < 0);
476
	assert(ke != KCGI_OK);
477
	free(*cp);
478
	*cp = NULL;
479
	*sz = 0;
480
	return ke;
481
}
482

483
/*
484
 * See fullreadwordsz() with a discarded size.
485
 */
486
enum kcgi_err
487
fullreadword(int fd, char **cp)
488
{
489
	size_t sz;
490

491
	return fullreadwordsz(fd, cp, &sz);
492
}
493

494
/*
495
 * Write a file-descriptor "sendfd" and a buffer "b" of length "bsz",
496
 * which must be 256 bytes or fewer, but not zero.
497
 * See fullwritefd().
498
 * Returns zero on failure (any kind), non-zero on success.
499
 */
500
int
501
fullwritefd(int fd, int sendfd, void *b, size_t bsz)
502
{
503
	struct msghdr	 msg;
504
	int		 rc;
505
	char		 buf[CMSG_SPACE(sizeof(fd))];
506
	struct iovec 	 io;
507
	struct cmsghdr	*cmsg;
508
	struct pollfd	 pfd;
509

510
	assert(bsz <= 256 && bsz > 0);
511

512
	memset(buf, 0, sizeof(buf));
513
	memset(&msg, 0, sizeof(struct msghdr));
514
	memset(&io, 0, sizeof(struct iovec));
515

516
	io.iov_base = b;
517
	io.iov_len = bsz;
518

519
	msg.msg_iov = &io;
520
	msg.msg_iovlen = 1;
521
	msg.msg_control = buf;
522
	msg.msg_controllen = sizeof(buf);
523

524
	cmsg = CMSG_FIRSTHDR(&msg);
525
	cmsg->cmsg_level = SOL_SOCKET;
526
	cmsg->cmsg_type = SCM_RIGHTS;
527
	cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
528

529
	*((int *)CMSG_DATA(cmsg)) = sendfd;
530

531
	msg.msg_controllen = cmsg->cmsg_len;
532

533
	pfd.fd = fd;
534
	pfd.events = POLLOUT;
535

536
again:
537
	if ((rc = poll(&pfd, 1, INFTIM)) < 0) {
538
		kutil_warn(NULL, NULL, "poll");
539
		return 0;
540
	} else if (rc == 0) {
541
		kutil_warnx(NULL, NULL, "poll: timeout!?");
542
		goto again;
543
	}
544
	
545
	if (!(pfd.revents & POLLOUT)) {
546
		kutil_warnx(NULL, NULL, "poll: no output");
547
		return 0;
548
	} 
549
	
550
	if ((rc = sendmsg(fd, &msg, 0)) < 0) {
551
		kutil_warn(NULL, NULL, "sendmsg");
552
		return 0;
553
	} else if (rc == 0) {
554
		kutil_warnx(NULL, NULL, "sendmsg: short write");
555
		return 0;
556
	}
557

558
	return 1;
559
}
560

561
/*
562
 * Read a file-descriptor into "recvfd" and a buffer "b" of length
563
 * "bsz", which must be 256 bytes or fewer, but not zero.
564
 * See fullwritefd().
565
 * Returns <0 on system failure, 0 on hangup (remote end closed), and >0
566
 * on success.
567
 * The output pointers are only set on success.
568
 */
569
int 
570
fullreadfd(int fd, int *recvfd, void *b, size_t bsz)
571
{
572
	struct msghdr	 msg;
573
	char		 m_buffer[256];
574
	char 		 c_buffer[256];
575
	struct iovec	 io;
576
	struct cmsghdr	*cmsg;
577
	int		 rc;
578
	struct pollfd	 pfd;
579

580
	assert(bsz <= 256 && bsz > 0);
581

582
	memset(&msg, 0, sizeof(struct msghdr));
583
	memset(&io, 0, sizeof(struct iovec));
584

585
	io.iov_base = m_buffer;
586
	io.iov_len = sizeof(m_buffer);
587
	msg.msg_iov = &io;
588
	msg.msg_iovlen = 1;
589

590
	msg.msg_control = c_buffer;
591
	msg.msg_controllen = sizeof(c_buffer);
592

593
	pfd.fd = fd;
594
	pfd.events = POLLIN;
595

596
again:
597
	if ((rc = poll(&pfd, 1, INFTIM)) < 0) {
598
		kutil_warn(NULL, NULL, "poll");
599
		return (-1);
600
	} else if (rc == 0) {
601
		kutil_warnx(NULL, NULL, "poll timeout!?!?");
602
		goto again;
603
	}
604
	
605
	if (!(pfd.revents & POLLIN)) {
606
		kutil_warnx(NULL, NULL, "poll: no input");
607
		return 0;
608
	} 
609
	
610
	if ((rc = recvmsg(fd, &msg, 0)) < 0) {
611
		kutil_warn(NULL, NULL, "recvmsg");
612
		return (-1);
613
	} else if (rc == 0) {
614
		kutil_warnx(NULL, NULL, "recvmsg: short read");
615
		return 0;
616
	}
617

618
	memcpy(b, m_buffer, bsz);
619
	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
620
		 cmsg = CMSG_NXTHDR(&msg, cmsg)) {
621
		if (cmsg->cmsg_len == CMSG_LEN(sizeof(int)) &&
622
		    cmsg->cmsg_level == SOL_SOCKET &&
623
		    cmsg->cmsg_type == SCM_RIGHTS) {
624
			*recvfd = *(int *)CMSG_DATA(cmsg);
625
			return 1;
626
		}
627
	}
628

629
	kutil_warnx(NULL, NULL, "recvmsg: no SCM_RIGHTS");
630
	return (-1);
631
}
632

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

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

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

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