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/mman.h>
20 #include <sys/stat.h>
21 #include <sys/socket.h>
22 
23 #include <assert.h>
24 #include <ctype.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <inttypes.h>
28 #include <limits.h>
29 #include <math.h> /* HUGE_VAL */
30 #include <signal.h>
31 #include <stdarg.h>
32 #include <stdint.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 
38 #include "kcgi.h"
39 #include "extern.h"
40 
41 /*
42  * Maximum size of printing a signed 64-bit integer.
43  */
44 #define	INT_MAXSZ	 22
45 
46 const char *const kschemes[KSCHEME__MAX] = {
47 	"aaa", /* KSCHEME_AAA */
48 	"aaas", /* KSCHEME_AAAS */
49 	"about", /* KSCHEME_ABOUT */
50 	"acap", /* KSCHEME_ACAP */
51 	"acct", /* KSCHEME_ACCT */
52 	"cap", /* KSCHEME_CAP */
53 	"cid", /* KSCHEME_CID */
54 	"coap", /* KSCHEME_COAP */
55 	"coaps", /* KSCHEME_COAPS */
56 	"crid", /* KSCHEME_CRID */
57 	"data", /* KSCHEME_DATA */
58 	"dav", /* KSCHEME_DAV */
59 	"dict", /* KSCHEME_DICT */
60 	"dns", /* KSCHEME_DNS */
61 	"file", /* KSCHEME_FILE */
62 	"ftp", /* KSCHEME_FTP */
63 	"geo", /* KSCHEME_GEO */
64 	"go", /* KSCHEME_GO */
65 	"gopher", /* KSCHEME_GOPHER */
66 	"h323", /* KSCHEME_H323 */
67 	"http", /* KSCHEME_HTTP */
68 	"https", /* KSCHEME_HTTPS */
69 	"iax", /* KSCHEME_IAX */
70 	"icap", /* KSCHEME_ICAP */
71 	"im", /* KSCHEME_IM */
72 	"imap", /* KSCHEME_IMAP */
73 	"info", /* KSCHEME_INFO */
74 	"ipp", /* KSCHEME_IPP */
75 	"iris", /* KSCHEME_IRIS */
76 	"iris.beep", /* KSCHEME_IRIS_BEEP */
77 	"iris.xpc", /* KSCHEME_IRIS_XPC */
78 	"iris.xpcs", /* KSCHEME_IRIS_XPCS */
79 	"iris.lwz", /* KSCHEME_IRIS_LWZ */
80 	"jabber", /* KSCHEME_JABBER */
81 	"ldap", /* KSCHEME_LDAP */
82 	"mailto", /* KSCHEME_MAILTO */
83 	"mid", /* KSCHEME_MID */
84 	"msrp", /* KSCHEME_MSRP */
85 	"msrps", /* KSCHEME_MSRPS */
86 	"mtqp", /* KSCHEME_MTQP */
87 	"mupdate", /* KSCHEME_MUPDATE */
88 	"news", /* KSCHEME_NEWS */
89 	"nfs", /* KSCHEME_NFS */
90 	"ni", /* KSCHEME_NI */
91 	"nih", /* KSCHEME_NIH */
92 	"nntp", /* KSCHEME_NNTP */
93 	"opaquelocktoken", /* KSCHEME_OPAQUELOCKTOKEN */
94 	"pop", /* KSCHEME_POP */
95 	"pres", /* KSCHEME_PRES */
96 	"reload", /* KSCHEME_RELOAD */
97 	"rtsp", /* KSCHEME_RTSP */
98 	"rtsps", /* KSCHEME_RTSPS */
99 	"rtspu", /* KSCHEME_RTSPU */
100 	"service", /* KSCHEME_SERVICE */
101 	"session", /* KSCHEME_SESSION */
102 	"shttp", /* KSCHEME_SHTTP */
103 	"sieve", /* KSCHEME_SIEVE */
104 	"sip", /* KSCHEME_SIP */
105 	"sips", /* KSCHEME_SIPS */
106 	"sms", /* KSCHEME_SMS */
107 	"snmp", /* KSCHEME_SNMP */
108 	"soap.beep", /* KSCHEME_SOAP_BEEP */
109 	"soap.beeps", /* KSCHEME_SOAP_BEEPS */
110 	"stun", /* KSCHEME_STUN */
111 	"stuns", /* KSCHEME_STUNS */
112 	"tag", /* KSCHEME_TAG */
113 	"tel", /* KSCHEME_TEL */
114 	"telnet", /* KSCHEME_TELNET */
115 	"tftp", /* KSCHEME_TFTP */
116 	"thismessage", /* KSCHEME_THISMESSAGE */
117 	"tn3270", /* KSCHEME_TN3270 */
118 	"tip", /* KSCHEME_TIP */
119 	"turn", /* KSCHEME_TURN */
120 	"turns", /* KSCHEME_TURNS */
121 	"tv", /* KSCHEME_TV */
122 	"urn", /* KSCHEME_URN */
123 	"vemmi", /* KSCHEME_VEMMI */
124 	"ws", /* KSCHEME_WS */
125 	"wss", /* KSCHEME_WSS */
126 	"xcon", /* KSCHEME_XCON */
127 	"xcon-userid", /* KSCHEME_XCON_USERID */
128 	"xmlrpc.beep", /* KSCHEME_XMLRPC_BEEP */
129 	"xmlrpc.beeps", /* KSCHEME_XMLRPC_BEEPS */
130 	"xmpp", /* KSCHEME_XMPP */
131 	"z39.50r", /* KSCHEME_Z39_50R */
132 	"z39.50s", /* KSCHEME_Z39_50S */
133 };
134 
135 const char *const kresps[KRESP__MAX] = {
136 	"Access-Control-Allow-Origin", /* KRESP_ACCESS_CONTROL_ALLOW_ORIGIN */
137 	"Accept-Ranges", /* KRESP_ACCEPT_RANGES */
138 	"Age", /* KRESP_AGE */
139 	"Allow", /* KRESP_ALLOW */
140 	"Cache-Control", /* KRESP_CACHE_CONTROL */
141 	"Connection", /* KRESP_CONNECTION */
142 	"Content-Encoding", /* KRESP_CONTENT_ENCODING */
143 	"Content-Language", /* KRESP_CONTENT_LANGUAGE */
144 	"Content-Length", /* KRESP_CONTENT_LENGTH */
145 	"Content-Location", /* KRESP_CONTENT_LOCATION */
146 	"Content-MD5", /* KRESP_CONTENT_MD5 */
147 	"Content-Disposition", /* KRESP_CONTENT_DISPOSITION */
148 	"Content-Range", /* KRESP_CONTENT_RANGE */
149 	"Content-Type", /* KRESP_CONTENT_TYPE */
150 	"Date", /* KRESP_DATE */
151 	"ETag", /* KRESP_ETAG */
152 	"Expires", /* KRESP_EXPIRES */
153 	"Last-Modified", /* KRESP_LAST_MODIFIED */
154 	"Link", /* KRESP_LINK */
155 	"Location", /* KRESP_LOCATION */
156 	"P3P", /* KRESP_P3P */
157 	"Pragma", /* KRESP_PRAGMA */
158 	"Proxy-Authenticate", /* KRESP_PROXY_AUTHENTICATE */
159 	"Refresh", /* KRESP_REFRESH */
160 	"Retry-After", /* KRESP_RETRY_AFTER */
161 	"Server", /* KRESP_SERVER */
162 	"Set-Cookie", /* KRESP_SET_COOKIE */
163 	"Status", /* KRESP_STATUS */
164 	"Strict-Transport-Security", /* KRESP_STRICT_TRANSPORT_SECURITY */
165 	"Trailer", /* KRESP_TRAILER */
166 	"Transfer-Encoding", /* KRESP_TRANSFER_ENCODING */
167 	"Upgrade", /* KRESP_UPGRADE */
168 	"Vary", /* KRESP_VARY */
169 	"Via", /* KRESP_VIA */
170 	"Warning", /* KRESP_WARNING */
171 	"WWW-Authenticate", /* KRESP_WWW_AUTHENTICATE */
172 	"X-Frame-Options", /* KRESP_X_FRAME_OPTIONS */
173 };
174 
175 const char *const kmimetypes[KMIME__MAX] = {
176 	"application/x-javascript", /* KMIME_APP_JAVASCRIPT */
177 	"application/json", /* KMIME_APP_JSON */
178 	"application/octet-stream", /* KMIME_APP_OCTET_STREAM */
179 	"application/pdf", /* KMIME_APP_PDF */
180 	"application/xml", /* KMIME_APP_XML */
181 	"application/zip", /* KMIME_APP_ZIP */
182 	"image/gif", /* KMIME_IMAGE_GIF */
183 	"image/jpeg", /* KMIME_IMAGE_JPEG */
184 	"image/png", /* KMIME_IMAGE_PNG */
185 	"image/svg+xml", /* KMIME_IMAGE_SVG_XML */
186 	"text/calendar", /* KMIME_TEXT_CALENDAR */
187 	"text/css", /* KMIME_TEXT_CSS */
188 	"text/csv", /* KMIME_TEXT_CSV */
189 	"text/html", /* KMIME_TEXT_HTML */
190 	"text/plain", /* KMIME_TEXT_PLAIN */
191 	"text/xml", /* KMIME_TEXT_XML */
192 };
193 
194 const char *const khttps[KHTTP__MAX] = {
195 	"100 Continue",				/* KHTTP_100 */
196 	"101 Switching Protocols",              /* KHTTP_101 */
197 	"103 Checkpoint",                       /* KHTTP_103 */
198 	"200 OK",                               /* KHTTP_200 */
199 	"201 Created",                          /* KHTTP_201 */
200 	"202 Accepted",                         /* KHTTP_202 */
201 	"203 Non-Authoritative Information",    /* KHTTP_203 */
202 	"204 No Content",                       /* KHTTP_204 */
203 	"205 Reset Content",                    /* KHTTP_205 */
204 	"206 Partial Content",                  /* KHTTP_206 */
205 	"207 Multi-Status",                     /* KHTTP_207 */
206 	"300 Multiple Choices",                 /* KHTTP_300 */
207 	"301 Moved Permanently",                /* KHTTP_301 */
208 	"302 Found",                            /* KHTTP_302 */
209 	"303 See Other",                        /* KHTTP_303 */
210 	"304 Not Modified",                     /* KHTTP_304 */
211 	"306 Switch Proxy",                     /* KHTTP_306 */
212 	"307 Temporary Redirect",               /* KHTTP_307 */
213 	"308 Resume Incomplete",                /* KHTTP_308 */
214 	"400 Bad Request",                      /* KHTTP_400 */
215 	"401 Unauthorized",                     /* KHTTP_401 */
216 	"402 Payment Required",                 /* KHTTP_402 */
217 	"403 Forbidden",                        /* KHTTP_403 */
218 	"404 Not Found",                        /* KHTTP_404 */
219 	"405 Method Not Allowed",               /* KHTTP_405 */
220 	"406 Not Acceptable",                   /* KHTTP_406 */
221 	"407 Proxy Authentication Required",    /* KHTTP_407 */
222 	"408 Request Timeout",                  /* KHTTP_408 */
223 	"409 Conflict",                         /* KHTTP_409 */
224 	"410 Gone",                             /* KHTTP_410 */
225 	"411 Length Required",                  /* KHTTP_411 */
226 	"412 Precondition Failed",              /* KHTTP_412 */
227 	"413 Request Entity Too Large",         /* KHTTP_413 */
228 	"414 Request-URI Too Long",             /* KHTTP_414 */
229 	"415 Unsupported Media Type",           /* KHTTP_415 */
230 	"416 Requested Range Not Satisfiable",  /* KHTTP_416 */
231 	"417 Expectation Failed",               /* KHTTP_417 */
232 	"424 Failed Dependency",                /* KHTTP_424 */
233 	"428 Precondition Required",            /* KHTTP_428 */
234 	"429 Too Many Requests",                /* KHTTP_429 */
235 	"431 Request Header Fields Too Large",  /* KHTTP_431 */
236 	"500 Internal Server Error",            /* KHTTP_500 */
237 	"501 Not Implemented",                  /* KHTTP_501 */
238 	"502 Bad Gateway",                      /* KHTTP_502 */
239 	"503 Service Unavailable",              /* KHTTP_503 */
240 	"504 Gateway Timeout",                  /* KHTTP_504 */
241 	"505 HTTP Version Not Supported",       /* KHTTP_505 */
242 	"507 Insufficient Storage",             /* KHTTP_507 */
243 	"511 Network Authentication Required",  /* KHTTP_511 */
244 };
245 
246 /*
247  * This doesn't have a preset size.
248  */
249 const struct kmimemap ksuffixmap[] = {
250 	{ "css", KMIME_TEXT_CSS },
251 	{ "csv", KMIME_TEXT_CSV },
252 	{ "gif", KMIME_IMAGE_GIF },
253 	{ "htm", KMIME_TEXT_HTML },
254 	{ "html", KMIME_TEXT_HTML },
255 	{ "ical", KMIME_TEXT_CALENDAR },
256 	{ "icalendar", KMIME_TEXT_CALENDAR },
257 	{ "ics", KMIME_TEXT_CALENDAR },
258 	{ "ifb", KMIME_TEXT_CALENDAR },
259 	{ "jpg", KMIME_IMAGE_JPEG },
260 	{ "jpeg", KMIME_IMAGE_JPEG },
261 	{ "js", KMIME_APP_JAVASCRIPT },
262 	{ "json", KMIME_APP_JSON },
263 	{ "pdf", KMIME_APP_PDF },
264 	{ "png", KMIME_IMAGE_PNG },
265 	{ "shtml", KMIME_TEXT_HTML },
266 	{ "svg", KMIME_IMAGE_SVG_XML },
267 	{ "svgz", KMIME_IMAGE_SVG_XML },
268 	{ "txt", KMIME_TEXT_PLAIN },
269 	{ "xml", KMIME_TEXT_XML },
270 	{ "zip", KMIME_APP_ZIP },
271 	{ NULL, KMIME__MAX },
272 };
273 
274 /*
275  * Default MIME suffix per type.
276  */
277 const char *const ksuffixes[KMIME__MAX] = {
278 	"js",	/* KMIME_APP_JAVASCRIPT */
279 	"json", /* KMIME_APP_JSON */
280 	NULL,	/* KMIME_APP_OCTET_STREAM */
281 	"pdf",	/* KMIME_APP_PDF */
282 	"xml",	/* KMIME_APP_XML */
283 	"zip",	/* KMIME_APP_ZIP */
284 	"gif",	/* KMIME_IMAGE_GIF */
285 	"jpg",	/* KMIME_IMAGE_JPEG */
286 	"png",	/* KMIME_IMAGE_PNG */
287 	"svg",	/* KMIME_IMAGE_PNG */
288 	"ics",	/* KMIME_TEXT_CALENDAR */
289 	"css",	/* KMIME_TEXT_CSS */
290 	"csv",	/* KMIME_TEXT_CSV */
291 	"html",	/* KMIME_TEXT_HTML */
292 	"txt",	/* KMIME_TEXT_PLAIN */
293 	"xml",	/* KMIME_TEXT_XML */
294 };
295 
296 const char *const kerrors[] = {
297 	"success", 			/* KCGI_OK */
298 	"cannot allocate memory",	/* KCGI_ENOMEM */
299 	"FastCGI exit",			/* KCGI_EXIT */
300 	"end-point connection closed",	/* KCGI_HUP */
301 	"too many open sockets",	/* KCGI_ENFILE */
302 	"failed to fork child",		/* KCGI_EAGAIN */
303 	"internal error",		/* KCGI_FORM */
304 	"system error",			/* KCGI_SYSTEM */
305 	"writer error",			/* KCGI_WRITER */
306 };
307 
308 const char *
kcgi_strerror(enum kcgi_err er)309 kcgi_strerror(enum kcgi_err er)
310 {
311 
312 	assert(er <= KCGI_WRITER);
313 	return kerrors[er];
314 }
315 
316 /*
317  * Safe strdup(): don't return on exhaustion.
318  */
319 char *
kstrdup(const char * cp)320 kstrdup(const char *cp)
321 {
322 	char	*p;
323 
324 	if ((p = kxstrdup(cp)) == NULL)
325 		exit(EXIT_FAILURE);
326 	return p;
327 }
328 
329 /*
330  * Safe realloc(): don't return on exhaustion.
331  */
332 void *
krealloc(void * pp,size_t sz)333 krealloc(void *pp, size_t sz)
334 {
335 	char	*p;
336 
337 	if ((p = kxrealloc(pp, sz)) == NULL)
338 		exit(EXIT_FAILURE);
339 	return p;
340 }
341 
342 /*
343  * Safe reallocarray(): don't return on exhaustion.
344  */
345 void *
kreallocarray(void * pp,size_t nm,size_t sz)346 kreallocarray(void *pp, size_t nm, size_t sz)
347 {
348 	char	*p;
349 
350 	if ((p = kxreallocarray(pp, nm, sz)) == NULL)
351 		exit(EXIT_FAILURE);
352 	return p;
353 }
354 
355 /*
356  * Safe asprintf(): don't return on exhaustion.
357  */
358 int
kasprintf(char ** p,const char * fmt,...)359 kasprintf(char **p, const char *fmt, ...)
360 {
361 	va_list	 ap;
362 	int	 len;
363 
364 	va_start(ap, fmt);
365 	len = kxvasprintf(p, fmt, ap);
366 	va_end(ap);
367 	if (len == -1)
368 		exit(EXIT_FAILURE);
369 	return len;
370 }
371 
372 /*
373  * Safe vasprintf(): don't return on exhaustion.
374  */
375 int
kvasprintf(char ** p,const char * fmt,va_list ap)376 kvasprintf(char **p, const char *fmt, va_list ap)
377 {
378 	int	 len;
379 
380 	if ((len = kxvasprintf(p, fmt, ap)) == -1)
381 		exit(EXIT_FAILURE);
382 	return len;
383 }
384 
385 /*
386  * Safe calloc(): don't return on exhaustion.
387  */
388 void *
kcalloc(size_t nm,size_t sz)389 kcalloc(size_t nm, size_t sz)
390 {
391 	char	*p;
392 
393 	if ((p = kxcalloc(nm, sz)) == NULL)
394 		exit(EXIT_FAILURE);
395 	return p;
396 }
397 
398 /*
399  * Safe malloc(): don't return on exhaustion.
400  */
401 void *
kmalloc(size_t sz)402 kmalloc(size_t sz)
403 {
404 	char	*p;
405 
406 	if ((p = kxmalloc(sz)) == NULL)
407 		exit(EXIT_FAILURE);
408 	return p;
409 }
410 
411 /*
412  * Deprecated form.
413  */
414 char *
kutil_urlencode(const char * cp)415 kutil_urlencode(const char *cp)
416 {
417 
418 	return (cp == NULL) ? NULL : khttp_urlencode(cp);
419 }
420 
421 char *
khttp_urlencode(const char * cp)422 khttp_urlencode(const char *cp)
423 {
424 	char	*p;
425 	char	 ch;
426 	size_t	 sz, cur;
427 	int	 rc;
428 
429 	if (cp == NULL)
430 		return kxstrdup("");
431 
432 	/*
433 	 * Leave three bytes per input byte for encoding.
434 	 * This ensures we needn't range-check.
435 	 * First check whether our size overflows.
436 	 * We do this here because we need our size!
437 	 */
438 
439 	sz = strlen(cp) + 1;
440 	if (SIZE_MAX / 3 < sz) {
441 		kutil_warnx(NULL, NULL, "multiplicative overflow");
442 		return NULL;
443 	}
444 	if ((p = kxcalloc(sz, 3)) == NULL)
445 		return NULL;
446 
447 	for (cur = 0; (ch = *cp) != '\0'; cp++) {
448 		if (isalnum((unsigned char)ch) || ch == '-' ||
449 		    ch == '_' || ch == '.' || ch == '~') {
450 			p[cur++] = ch;
451 			continue;
452 		} else if (' ' == ch) {
453 			p[cur++] = '+';
454 			continue;
455 		}
456 		rc = snprintf(p + cur, 4, "%%%.2hhX",
457 			(unsigned char)ch);
458 		if (rc != 3) {
459 			kutil_warnx(NULL, NULL, "snprintf");
460 			free(p);
461 			return NULL;
462 		}
463 		cur += 3;
464 	}
465 
466 	return p;
467 }
468 
469 /*
470  * Deprecated form.
471  */
472 enum kcgi_err
kutil_urldecode_inplace(char * p)473 kutil_urldecode_inplace(char *p)
474 {
475 
476 	return khttp_urldecode_inplace(p);
477 }
478 
479 enum kcgi_err
khttp_urldecode_inplace(char * p)480 khttp_urldecode_inplace(char *p)
481 {
482 	char	 	 c, d;
483 	const char	*tail;
484 
485 	if (p == NULL)
486 		return KCGI_FORM;
487 
488 	/*
489 	 * Keep track of two positions: "p", where we'll write the
490 	 * decoded results, and "tail", which is from where we'll
491 	 * decode hex or copy data.
492 	 */
493 
494 	for (tail = p; (c = *tail) != '\0'; *p++ = c) {
495 		if (c != '%') {
496 			if (c == '+')
497 				c = ' ';
498 			tail++;
499 			continue;
500 		}
501 
502 		/*
503 		 * Read hex '%xy' as two unsigned chars "c" and "d" then
504 		 * combine them back into "c".
505 		 */
506 
507 		if (sscanf(tail + 1, "%1hhx%1hhx", &d, &c) != 2 ||
508 		    (c |= d << 4) == '\0') {
509 			kutil_warnx(NULL, NULL,
510 				"malformed percent-encoded sequence");
511 			return KCGI_FORM;
512 		}
513 		tail += 3;
514 	}
515 
516 	*p = '\0';
517 	return KCGI_OK;
518 }
519 
520 /*
521  * Deprecated form.
522  */
523 enum kcgi_err
kutil_urldecode(const char * src,char ** dst)524 kutil_urldecode(const char *src, char **dst)
525 {
526 
527 	return khttp_urldecode(src, dst);
528 }
529 
530 enum kcgi_err
khttp_urldecode(const char * src,char ** dst)531 khttp_urldecode(const char *src, char **dst)
532 {
533 	enum kcgi_err	 er;
534 
535 	/* In case of error. */
536 
537 	if (dst != NULL)
538 		*dst = NULL;
539 
540 	if (src == NULL || dst == NULL)
541 		return KCGI_FORM;
542 	if ((*dst = kxstrdup(src)) == NULL)
543 		return KCGI_ENOMEM;
544 	if ((er = khttp_urldecode_inplace(*dst)) == KCGI_OK)
545 		return KCGI_OK;
546 
547 	/* If we have decoding errors, clear the output. */
548 
549 	free(*dst);
550 	*dst = NULL;
551 	return er;
552 }
553 
554 /*
555  * Append key-value triplets in "ap" to "p".
556  * Reallocate as necessary.
557  * A NULL key signifies termination of the list.
558  * Returns the URL string or NULL on memory failure.
559  */
560 static char *
khttp_url_query_string(char * p,va_list ap)561 khttp_url_query_string(char *p, va_list ap)
562 {
563 	char	*pp, *keyp, *valp;
564 	size_t	 total, count = 0;
565 
566 	total = strlen(p) + 1;
567 
568 	while ((pp = va_arg(ap, char *)) != NULL) {
569 		if ((keyp = khttp_urlencode(pp)) == NULL) {
570 			free(p);
571 			return NULL;
572 		}
573 
574 		valp = khttp_urlencode(va_arg(ap, char *));
575 		if (valp == NULL) {
576 			free(p);
577 			free(keyp);
578 			return NULL;
579 		}
580 
581 		/* Size for key, value, ? or &, and =. */
582 		/* FIXME: check for overflow! */
583 
584 		total += strlen(keyp) + strlen(valp) + 2;
585 
586 		if ((pp = kxrealloc(p, total)) == NULL) {
587 			free(p);
588 			free(keyp);
589 			free(valp);
590 			return NULL;
591 		}
592 		p = pp;
593 
594 		if (count > 0)
595 			(void)strlcat(p, "&", total);
596 		else
597 			(void)strlcat(p, "?", total);
598 
599 		(void)strlcat(p, keyp, total);
600 		(void)strlcat(p, "=", total);
601 		(void)strlcat(p, valp, total);
602 
603 		free(keyp);
604 		free(valp);
605 		count++;
606 	}
607 
608 	return p;
609 }
610 
611 /*
612  * Append key-type-value triplets in "ap" to "p".
613  * Reallocate as necessary.
614  * A NULL key signifies termination of the list.
615  * Returns the URL string or NULL on memory failure.
616  */
617 static char *
khttp_url_query_stringx(char * p,va_list ap)618 khttp_url_query_stringx(char *p, va_list ap)
619 {
620 	char	*pp, *keyp, *valp, *valpp;
621 	size_t	 total, count = 0;
622 	char 	 buf[256]; /* max double/int64_t */
623 
624 	total = strlen(p) + 1;
625 
626 	while ((pp = va_arg(ap, char *)) != NULL) {
627 		if ((keyp = khttp_urlencode(pp)) == NULL) {
628 			free(p);
629 			return NULL;
630 		}
631 
632 		valp = valpp = NULL;
633 
634 		switch (va_arg(ap, enum kattrx)) {
635 		case KATTRX_STRING:
636 			valp = khttp_urlencode(va_arg(ap, char *));
637 			valpp = valp;
638 			break;
639 		case KATTRX_INT:
640 			(void)snprintf(buf, sizeof(buf),
641 				"%" PRId64, va_arg(ap, int64_t));
642 			valp = buf;
643 			break;
644 		case KATTRX_DOUBLE:
645 			(void)snprintf(buf, sizeof(buf),
646 				"%g", va_arg(ap, double));
647 			valp = buf;
648 			break;
649 		default:
650 			free(p);
651 			free(keyp);
652 			return NULL;
653 		}
654 
655 		if (valp == NULL) {
656 			free(p);
657 			free(keyp);
658 			return NULL;
659 		}
660 
661 		/* Size for key, value, ? or &, and =. */
662 		/* FIXME: check for overflow! */
663 
664 		total += strlen(keyp) + strlen(valp) + 2;
665 
666 		if ((pp = kxrealloc(p, total)) == NULL) {
667 			free(p);
668 			free(keyp);
669 			free(valpp);
670 			return NULL;
671 		}
672 		p = pp;
673 
674 		if (count > 0)
675 			(void)strlcat(p, "&", total);
676 		else
677 			(void)strlcat(p, "?", total);
678 
679 		(void)strlcat(p, keyp, total);
680 		(void)strlcat(p, "=", total);
681 		(void)strlcat(p, valp, total);
682 
683 		free(keyp);
684 		free(valpp);
685 		count++;
686 	}
687 
688 	return p;
689 }
690 
691 /*
692  * Deprecated form.
693  */
694 char *
kutil_urlabs(enum kscheme scheme,const char * host,uint16_t port,const char * path)695 kutil_urlabs(enum kscheme scheme,
696 	const char *host, uint16_t port, const char *path)
697 {
698 	char	*p;
699 
700 	kxasprintf(&p, "%s://%s:%" PRIu16 "%s",
701 	    kschemes[scheme], host, port, path);
702 	return p;
703 }
704 
705 char *
khttp_urlabs(enum kscheme scheme,const char * host,uint16_t port,const char * path,...)706 khttp_urlabs(enum kscheme scheme, const char *host,
707 	uint16_t port, const char *path, ...)
708 {
709 	char	*p;
710 	va_list	 ap;
711 
712 	va_start(ap, path);
713 	p = khttp_vurlabs(scheme, host, port, path, ap);
714 	va_end(ap);
715 	return p;
716 }
717 
718 char *
khttp_vurlabs(enum kscheme scheme,const char * host,uint16_t port,const char * path,va_list ap)719 khttp_vurlabs(enum kscheme scheme, const char *host,
720 	uint16_t port, const char *path, va_list ap)
721 {
722 	char	*p;
723 	int	 len;
724 
725 	if (host == NULL || host[0] == '\0') {
726 		len = kxasprintf(&p, "%s:%s", kschemes[scheme],
727 			path == NULL ? "" : path);
728 	} else if (port == 0) {
729 		len = kxasprintf(&p, "%s://%s%s%s",
730 		    kschemes[scheme], host,
731 		    path != NULL && path[0] != '\0' &&
732 		    	path[0] != '/' ? "/" : "",
733 		    path == NULL ? "" : path);
734 	} else {
735 		len = kxasprintf(&p, "%s://%s:%" PRIu16 "%s%s",
736 		    kschemes[scheme], host, port,
737 		    path != NULL && path[0] != '\0' &&
738 		    	path[0] != '/' ? "/" : "",
739 		    path == NULL ? "" : path);
740 	}
741 
742 	if (len == -1)
743 		return NULL;
744 	return khttp_url_query_string(p, ap);
745 }
746 
747 /*
748  * Deprecated form.
749  */
750 char *
kutil_urlpartx(struct kreq * req,const char * path,const char * mime,const char * page,...)751 kutil_urlpartx(struct kreq *req, const char *path,
752 	const char *mime, const char *page, ...)
753 {
754 	char	*ret;
755 	va_list	 ap;
756 
757 	if (page == NULL)
758 		return NULL;
759 
760 	va_start(ap, page);
761 	ret = khttp_vurlpartx(path, mime, page, ap);
762 	va_end(ap);
763 	return ret;
764 }
765 
766 char *
khttp_urlpartx(const char * path,const char * mime,const char * page,...)767 khttp_urlpartx(const char *path,
768 	const char *mime, const char *page, ...)
769 {
770 	char	*ret;
771 	va_list	 ap;
772 
773 	va_start(ap, page);
774 	ret = khttp_vurlpartx(path, mime, page, ap);
775 	va_end(ap);
776 	return ret;
777 }
778 
779 char *
khttp_vurlpartx(const char * path,const char * mime,const char * page,va_list ap)780 khttp_vurlpartx(const char *path,
781 	const char *mime, const char *page, va_list ap)
782 {
783 	int	 len;
784 	char	*p, *pageenc = NULL;
785 
786 	if (page != NULL && (pageenc = khttp_urlencode(page)) == NULL)
787 		return NULL;
788 
789 	if ((mime == NULL || mime[0] == '\0') ||
790 	    (page == NULL || page[0] == '\0'))
791 		len = kxasprintf(&p, "%s%s%s",
792 			path != NULL ? path : "",
793 			path != NULL ? "/" : "",
794 			pageenc != NULL ? pageenc : "");
795 	else {
796 		assert(pageenc != NULL);
797 		len = kxasprintf(&p, "%s%s%s.%s",
798 			path != NULL ? path : "",
799 			path != NULL ? "/" : "", pageenc, mime);
800 	}
801 
802 	free(pageenc);
803 	pageenc = NULL;
804 
805 	if (len == -1)
806 		return NULL;
807 	return khttp_url_query_stringx(p, ap);
808 }
809 
810 /*
811  * Deprecated form.
812  */
813 char *
kutil_urlpart(struct kreq * req,const char * path,const char * mime,const char * page,...)814 kutil_urlpart(struct kreq *req, const char *path,
815 	const char *mime, const char *page, ...)
816 {
817 	char	*ret;
818 	va_list	 ap;
819 
820 	if (page == NULL)
821 		return NULL;
822 	va_start(ap, page);
823 	ret = khttp_vurlpart(path, mime, page, ap);
824 	va_end(ap);
825 	return ret;
826 }
827 
828 char *
khttp_urlpart(const char * path,const char * mime,const char * page,...)829 khttp_urlpart(const char *path,
830 	const char *mime, const char *page, ...)
831 {
832 	char	*ret;
833 	va_list	 ap;
834 
835 	va_start(ap, page);
836 	ret = khttp_vurlpart(path, mime, page, ap);
837 	va_end(ap);
838 	return ret;
839 }
840 
841 char *
khttp_vurlpart(const char * path,const char * mime,const char * page,va_list ap)842 khttp_vurlpart(const char *path,
843 	const char *mime, const char *page, va_list ap)
844 {
845 	char	*p, *pageenc = NULL;
846 	int	 len;
847 
848 	if (page != NULL && (pageenc = khttp_urlencode(page)) == NULL)
849 		return NULL;
850 
851 	/*
852 	 * Only append the MIME suffix if we have it AND if the page is
853 	 * non-NULL and non-empty.
854 	 */
855 
856 	if ((mime == NULL || mime[0] == '\0') ||
857 	    (page == NULL || page[0] == '\0'))
858 		len = kxasprintf(&p, "%s%s%s",
859 			path != NULL ? path : "",
860 			path != NULL ? "/" : "",
861 			pageenc != NULL ? pageenc : "");
862 	else {
863 		assert(pageenc != NULL);
864 		len = kxasprintf(&p, "%s%s%s.%s",
865 			path != NULL ? path : "",
866 			path != NULL ? "/" : "", pageenc, mime);
867 	}
868 
869 	free(pageenc);
870 	pageenc = NULL;
871 
872 	if (len == -1)
873 		return NULL;
874 	return khttp_url_query_string(p, ap);
875 }
876 
877 static void
kpair_free(struct kpair * p,size_t sz)878 kpair_free(struct kpair *p, size_t sz)
879 {
880 	size_t	 i;
881 
882 	for (i = 0; i < sz; i++) {
883 		free(p[i].key);
884 		free(p[i].val);
885 		free(p[i].file);
886 		free(p[i].ctype);
887 		free(p[i].xcode);
888 	}
889 	free(p);
890 }
891 
892 void
kreq_free(struct kreq * req)893 kreq_free(struct kreq *req)
894 {
895 	size_t	 i;
896 
897 	for (i = 0; i < req->reqsz; i++) {
898 		free(req->reqs[i].key);
899 		free(req->reqs[i].val);
900 	}
901 
902 	free(req->reqs);
903 	kpair_free(req->cookies, req->cookiesz);
904 	kpair_free(req->fields, req->fieldsz);
905 	free(req->path);
906 	free(req->fullpath);
907 	free(req->remote);
908 	free(req->host);
909 	free(req->cookiemap);
910 	free(req->cookienmap);
911 	free(req->fieldmap);
912 	free(req->fieldnmap);
913 	free(req->suffix);
914 	free(req->pagename);
915 	free(req->pname);
916 	free(req->rawauth.digest);
917 
918 	if (req->rawauth.type == KAUTH_DIGEST) {
919 		free(req->rawauth.d.digest.user);
920 		free(req->rawauth.d.digest.uri);
921 		free(req->rawauth.d.digest.realm);
922 		free(req->rawauth.d.digest.nonce);
923 		free(req->rawauth.d.digest.cnonce);
924 		free(req->rawauth.d.digest.response);
925 		free(req->rawauth.d.digest.opaque);
926 	} else if (req->rawauth.type == KAUTH_BASIC) {
927 		free(req->rawauth.d.basic.response);
928 	} else if (req->rawauth.type == KAUTH_BEARER) {
929 		free(req->rawauth.d.basic.response);
930 	}
931 }
932 
933 enum kcgi_err
khttp_parse(struct kreq * req,const struct kvalid * keys,size_t keysz,const char * const * pages,size_t pagesz,size_t defpage)934 khttp_parse(struct kreq *req,
935 	const struct kvalid *keys, size_t keysz,
936 	const char *const *pages, size_t pagesz,
937 	size_t defpage)
938 {
939 
940 	return khttp_parsex(req, ksuffixmap, kmimetypes,
941 		KMIME__MAX, keys, keysz, pages, pagesz,
942 		KMIME_TEXT_HTML, defpage, NULL, NULL, 0, NULL);
943 }
944 
945 enum kcgi_err
khttp_parsex(struct kreq * req,const struct kmimemap * suffixmap,const char * const * mimes,size_t mimesz,const struct kvalid * keys,size_t keysz,const char * const * pages,size_t pagesz,size_t defmime,size_t defpage,void * arg,void (* argfree)(void * arg),unsigned debugging,const struct kopts * opts)946 khttp_parsex(struct kreq *req,
947 	const struct kmimemap *suffixmap,
948 	const char *const *mimes, size_t mimesz,
949 	const struct kvalid *keys, size_t keysz,
950 	const char *const *pages, size_t pagesz,
951 	size_t defmime, size_t defpage, void *arg,
952 	void (*argfree)(void *arg), unsigned debugging,
953 	const struct kopts *opts)
954 {
955 	const struct kmimemap *mm;
956 	enum kcgi_err	  kerr;
957 	int 		  er;
958 	struct kopts	  kopts;
959 	int		  work_dat[2];
960 	pid_t		  work_pid;
961 
962 	memset(req, 0, sizeof(struct kreq));
963 
964 	/*
965 	 * We'll be using poll(2) for reading our HTTP document, so this
966 	 * must be non-blocking in order to make the reads not spin the
967 	 * CPU.
968 	 */
969 
970 	if (kxsocketprep(STDIN_FILENO) != KCGI_OK)
971 		return KCGI_SYSTEM;
972 	if (kxsocketpair(work_dat) != KCGI_OK)
973 		return KCGI_SYSTEM;
974 
975 	if ((work_pid = fork()) == -1) {
976 		er = errno;
977 		kutil_warn(NULL, NULL, "fork");
978 
979 		close(work_dat[KWORKER_PARENT]);
980 		close(work_dat[KWORKER_CHILD]);
981 		return (er == EAGAIN) ? KCGI_EAGAIN : KCGI_ENOMEM;
982 	} else if (work_pid == 0) {
983 		if (argfree != NULL)
984 			(*argfree)(arg);
985 
986 		close(STDOUT_FILENO);
987 		close(work_dat[KWORKER_PARENT]);
988 
989 		er = EXIT_SUCCESS;
990 		if (!ksandbox_init_child(SAND_WORKER,
991 		    work_dat[KWORKER_CHILD], -1, -1, -1))
992 			er = EXIT_FAILURE;
993 		else if (kworker_child(work_dat[KWORKER_CHILD],
994 		    keys, keysz, mimes, mimesz, debugging) != KCGI_OK)
995 			er = EXIT_FAILURE;
996 
997 		close(work_dat[KWORKER_CHILD]);
998 		_exit(er);
999 		/* NOTREACHED */
1000 	}
1001 
1002 	close(work_dat[KWORKER_CHILD]);
1003 	work_dat[KWORKER_CHILD] = -1;
1004 
1005 	if (opts == NULL)
1006 		kopts.sndbufsz = -1;
1007 	else
1008 		memcpy(&kopts, opts, sizeof(struct kopts));
1009 
1010 	if (kopts.sndbufsz < 0)
1011 		kopts.sndbufsz = 1024 * 8;
1012 
1013 	kerr = KCGI_ENOMEM;
1014 
1015 	/*
1016 	 * After this point, all errors should use "goto err", which
1017 	 * will properly free up our memory and close any extant file
1018 	 * descriptors.
1019 	 * Also, we're running our child in the background, so make sure
1020 	 * that it gets killed!
1021 	 */
1022 
1023 	req->arg = arg;
1024 	req->keys = keys;
1025 	req->keysz = keysz;
1026 	req->kdata = kdata_alloc(-1, -1, 0, debugging, &kopts);
1027 	if (req->kdata == NULL)
1028 		goto err;
1029 
1030 	if (keysz) {
1031 		req->cookiemap = kxcalloc(keysz, sizeof(struct kpair *));
1032 		if (req->cookiemap == NULL)
1033 			goto err;
1034 		req->cookienmap = kxcalloc(keysz, sizeof(struct kpair *));
1035 		if (req->cookienmap == NULL)
1036 			goto err;
1037 		req->fieldmap = kxcalloc(keysz, sizeof(struct kpair *));
1038 		if (req->fieldmap == NULL)
1039 			goto err;
1040 		req->fieldnmap = kxcalloc(keysz, sizeof(struct kpair *));
1041 		if (req->fieldnmap == NULL)
1042 			goto err;
1043 	}
1044 
1045 	/*
1046 	 * Now read the input fields from the child and conditionally
1047 	 * assign them to our lookup table.
1048 	 */
1049 
1050 	kerr = kworker_parent
1051 		(work_dat[KWORKER_PARENT], req, 1, mimesz);
1052 	if (kerr != KCGI_OK)
1053 		goto err;
1054 
1055 	/* Look up page type from component. */
1056 
1057 	req->page = defpage;
1058 	if (*req->pagename != '\0')
1059 		for (req->page = 0; req->page < pagesz; req->page++)
1060 			if (strcasecmp
1061 			    (pages[req->page], req->pagename) == 0)
1062 				break;
1063 
1064 	/*
1065 	 * Look up the MIME type, defaulting to defmime if none.
1066 	 * If we can't find it, use the maximum (mimesz).
1067 	 */
1068 
1069 	req->mime = defmime;
1070 	if (*req->suffix != '\0') {
1071 		for (mm = suffixmap; mm->name != NULL; mm++)
1072 			if (strcasecmp(mm->name, req->suffix) == 0) {
1073 				req->mime = mm->mime;
1074 				break;
1075 			}
1076 		if (mm->name == NULL)
1077 			req->mime = mimesz;
1078 	}
1079 
1080 	close(work_dat[KWORKER_PARENT]);
1081 	work_dat[KWORKER_PARENT] = -1;
1082 	kerr = kxwaitpid(work_pid);
1083 	work_pid = -1;
1084 	if (kerr != KCGI_OK)
1085 		goto err;
1086 	return kerr;
1087 err:
1088 	assert(kerr != KCGI_OK);
1089 	if (work_dat[KWORKER_PARENT] != -1)
1090 		close(work_dat[KWORKER_PARENT]);
1091 	if (work_pid != -1)
1092 		kxwaitpid(work_pid);
1093 	kdata_free(req->kdata, 0);
1094 	req->kdata = NULL;
1095 	kreq_free(req);
1096 	return kerr;
1097 }
1098 
1099 void
kutil_invalidate(struct kreq * r,struct kpair * kp)1100 kutil_invalidate(struct kreq *r, struct kpair *kp)
1101 {
1102 	struct kpair	*p, *lastp;
1103 	size_t		 i;
1104 
1105 	if (kp == NULL)
1106 		return;
1107 
1108 	kp->type = KPAIR__MAX;
1109 	kp->state = KPAIR_INVALID;
1110 	memset(&kp->parsed, 0, sizeof(union parsed));
1111 
1112 	/* We're not bucketed. */
1113 
1114 	if ((i = kp->keypos) == r->keysz)
1115 		return;
1116 
1117 	/* Is it in our fieldmap? */
1118 
1119 	if (r->fieldmap[i] != NULL) {
1120 		if (kp == r->fieldmap[i]) {
1121 			r->fieldmap[i] = kp->next;
1122 			kp->next = r->fieldnmap[i];
1123 			r->fieldnmap[i] = kp;
1124 			return;
1125 		}
1126 		lastp = r->fieldmap[i];
1127 		p = lastp->next;
1128 		for ( ; p != NULL; lastp = p, p = p->next)
1129 			if (kp == p) {
1130 				lastp->next = kp->next;
1131 				kp->next = r->fieldnmap[i];
1132 				r->fieldnmap[i] = kp;
1133 				return;
1134 			}
1135 	}
1136 
1137 	/* ...cookies? */
1138 
1139 	if (r->cookiemap[i] != NULL) {
1140 		if (kp == r->cookiemap[i]) {
1141 			r->cookiemap[i] = kp->next;
1142 			kp->next = r->cookienmap[i];
1143 			r->cookienmap[i] = kp;
1144 			return;
1145 		}
1146 		lastp = r->cookiemap[i];
1147 		p = lastp->next;
1148 		for ( ; p != NULL; lastp = p, p = p->next)
1149 			if (kp == p) {
1150 				lastp->next = kp->next;
1151 				kp->next = r->cookienmap[i];
1152 				r->cookienmap[i] = kp;
1153 				return;
1154 			}
1155 	}
1156 }
1157 
1158 void
khttp_child_free(struct kreq * req)1159 khttp_child_free(struct kreq *req)
1160 {
1161 
1162 	kdata_free(req->kdata, 0);
1163 	req->kdata = NULL;
1164 	kreq_free(req);
1165 }
1166 
1167 void
khttp_free(struct kreq * req)1168 khttp_free(struct kreq *req)
1169 {
1170 
1171 	kdata_free(req->kdata, 1);
1172 	req->kdata = NULL;
1173 	kreq_free(req);
1174 }
1175 
1176 /*
1177  * Trim leading and trailing whitespace from a word.
1178  * Note that this returns a pointer within "val" and optionally sets the
1179  * NUL-terminator, so don't free() the returned value.
1180  */
1181 static char *
trim(char * val)1182 trim(char *val)
1183 {
1184 	char	*cp;
1185 
1186 	while (isspace((unsigned char)*val))
1187 		val++;
1188 
1189 	cp = strchr(val, '\0') - 1;
1190 	while (cp > val && isspace((unsigned char)*cp))
1191 		*cp-- = '\0';
1192 
1193 	return val;
1194 }
1195 
1196 /*
1197  * Simple email address validation: this is NOT according to the spec,
1198  * but a simple heuristic look at the address.
1199  * Note that this lowercases the mail address.
1200  */
1201 static char *
valid_email(char * p)1202 valid_email(char *p)
1203 {
1204 	char	*cp, *start;
1205 	size_t	 sz;
1206 
1207 	/*
1208 	 * Trim all white-space before and after.
1209 	 * Length check (min: a@b, max: 254 bytes).
1210 	 * Make sure we have an at-sign.
1211 	 * Make sure at signs aren't at the start or end.
1212 	 */
1213 
1214 	cp = start = trim(p);
1215 
1216 	if ((sz = strlen(cp)) < 3 || sz > 254)
1217 		return NULL;
1218 	if (cp[0] == '@' || cp[sz - 1] == '@')
1219 		return NULL;
1220 	if (strchr(cp, '@') == NULL)
1221 		return NULL;
1222 
1223 	for (cp = start; *cp != '\0'; cp++)
1224 		*cp = tolower((unsigned char)*cp);
1225 
1226 	return start;
1227 }
1228 
1229 int
kvalid_date(struct kpair * kp)1230 kvalid_date(struct kpair *kp)
1231 {
1232 	int		 mday, mon, year;
1233 
1234 	if (kp->valsz != 10 || kp->val[10] != '\0' ||
1235 	    !isdigit((unsigned char)kp->val[0]) ||
1236 	    !isdigit((unsigned char)kp->val[1]) ||
1237 	    !isdigit((unsigned char)kp->val[2]) ||
1238 	    !isdigit((unsigned char)kp->val[3]) ||
1239 	    kp->val[4] != '-' ||
1240 	    !isdigit((unsigned char)kp->val[5]) ||
1241 	    !isdigit((unsigned char)kp->val[6]) ||
1242 	    kp->val[7] != '-' ||
1243 	    !isdigit((unsigned char)kp->val[8]) ||
1244 	    !isdigit((unsigned char)kp->val[9]))
1245 		return 0;
1246 
1247 	/*
1248 	 * We know these are all positive integer values, so no need to
1249 	 * use the more complicated interface for this.
1250 	 */
1251 
1252 	year = atoi(&kp->val[0]);
1253 	mon = atoi(&kp->val[5]);
1254 	mday = atoi(&kp->val[8]);
1255 
1256 	if (!khttp_date2epoch(&kp->parsed.i, mday, mon, year))
1257 		return 0;
1258 
1259 	kp->type = KPAIR_INTEGER;
1260 	return 1;
1261 }
1262 
1263 int
kvalid_stringne(struct kpair * p)1264 kvalid_stringne(struct kpair *p)
1265 {
1266 	/*
1267 	 * To check if we're a valid string, simply make sure that the
1268 	 * NUL-terminator is where we expect it to be.
1269 	 */
1270 
1271 	if (strlen(p->val) != p->valsz || p->valsz == 0)
1272 		return 0;
1273 	p->type = KPAIR_STRING;
1274 	p->parsed.s = p->val;
1275 	return 1;
1276 }
1277 
1278 int
kvalid_string(struct kpair * p)1279 kvalid_string(struct kpair *p)
1280 {
1281 	/*
1282 	 * To check if we're a valid string, simply make sure that the
1283 	 * NUL-terminator is where we expect it to be.
1284 	 */
1285 
1286 	if (strlen(p->val) != p->valsz)
1287 		return 0;
1288 	p->type = KPAIR_STRING;
1289 	p->parsed.s = p->val;
1290 	return 1;
1291 }
1292 
1293 int
kvalid_email(struct kpair * p)1294 kvalid_email(struct kpair *p)
1295 {
1296 
1297 	if (!kvalid_stringne(p))
1298 		return 0;
1299 	return (p->parsed.s = valid_email(p->val)) != NULL;
1300 }
1301 
1302 int
kvalid_udouble(struct kpair * p)1303 kvalid_udouble(struct kpair *p)
1304 {
1305 
1306 	return kvalid_double(p) && p->parsed.d > 0.0;
1307 }
1308 
1309 int
kvalid_double(struct kpair * p)1310 kvalid_double(struct kpair *p)
1311 {
1312 	char		*ep;
1313 	const char	*nval;
1314 	double		 lval;
1315 	int		 er;
1316 
1317 	if (!kvalid_stringne(p))
1318 		return 0;
1319 
1320 	/*
1321 	 * We might get an empty string from trim, which constitutes a
1322 	 * valid double (!?), so double check that the string is
1323 	 * non-empty after trimming whitespace.
1324 	 * We trim white-space because strtod(3) accepts white-space
1325 	 * before but not after the string.
1326 	 */
1327 
1328 	nval = trim(p->val);
1329 	if (nval[0] == '\0')
1330 		return 0;
1331 
1332 	/* Save errno so we can restore it later. */
1333 
1334 	er = errno;
1335 	errno = 0;
1336 	lval = strtod(nval, &ep);
1337 	if (errno == ERANGE)
1338 		return 0;
1339 
1340 	/* Restore errno. */
1341 
1342 	errno = er;
1343 
1344 	if (*ep != '\0')
1345 		return 0;
1346 
1347 	p->parsed.d = lval;
1348 	p->type = KPAIR_DOUBLE;
1349 	return 1;
1350 }
1351 
1352 int
kvalid_int(struct kpair * p)1353 kvalid_int(struct kpair *p)
1354 {
1355 	const char	*ep;
1356 
1357 	if (!kvalid_stringne(p))
1358 		return 0;
1359 	p->parsed.i = strtonum
1360 		(trim(p->val), INT64_MIN, INT64_MAX, &ep);
1361 	p->type = KPAIR_INTEGER;
1362 	return ep == NULL;
1363 }
1364 
1365 int
kvalid_bit(struct kpair * p)1366 kvalid_bit(struct kpair *p)
1367 {
1368 
1369 	if (!kvalid_uint(p))
1370 		return 0;
1371 	return p->parsed.i <= 64;
1372 }
1373 
1374 int
kvalid_uint(struct kpair * p)1375 kvalid_uint(struct kpair *p)
1376 {
1377 	const char	*ep;
1378 
1379 	p->parsed.i = strtonum(trim(p->val), 0, INT64_MAX, &ep);
1380 	p->type = KPAIR_INTEGER;
1381 	return ep == NULL;
1382 }
1383 
1384 enum kcgi_err
kcgi_buf_write(const char * s,size_t sz,void * arg)1385 kcgi_buf_write(const char *s, size_t sz, void *arg)
1386 {
1387 	struct kcgi_buf	*b = arg;
1388 	void		*pp;
1389 
1390 	/* Let empty/NULL pass through. */
1391 
1392 	if (s == NULL || sz == 0)
1393 		return KCGI_OK;
1394 
1395 	/* Grow the buffer and leave some slop space. */
1396 
1397 	if (b->sz + sz + 1 > b->maxsz) {
1398 		b->maxsz = b->sz + sz + 1 +
1399 			(b->growsz == 0 ? 1024 : b->growsz);
1400 		if ((pp = kxrealloc(b->buf, b->maxsz)) == NULL)
1401 			return KCGI_ENOMEM;
1402 		b->buf = pp;
1403 	}
1404 
1405 	/* Always NUL-terminate even though we accept binary data. */
1406 
1407 	memcpy(&b->buf[b->sz], s, sz);
1408 	b->sz += sz;
1409 	b->buf[b->sz] = '\0';
1410 	return KCGI_OK;
1411 }
1412 
1413 enum kcgi_err
kcgi_buf_printf(struct kcgi_buf * buf,const char * fmt,...)1414 kcgi_buf_printf(struct kcgi_buf *buf, const char *fmt, ...)
1415 {
1416 	char		*nbuf;
1417 	int		 len;
1418 	va_list		 ap;
1419 	enum kcgi_err	 er;
1420 
1421 	/* Let this bogus case pass through. */
1422 
1423 	if (fmt == NULL)
1424 		return KCGI_OK;
1425 
1426 	/* Allocate temporary buffer. */
1427 
1428 	va_start(ap, fmt);
1429 	len = kxvasprintf(&nbuf, fmt, ap);
1430 	va_end(ap);
1431 	if (len == -1)
1432 		return KCGI_ENOMEM;
1433 
1434 	/* Write and free. */
1435 
1436 	er = kcgi_buf_write(nbuf, (size_t)len, buf);
1437 	free(nbuf);
1438 	return er;
1439 }
1440 
1441 enum kcgi_err
kcgi_buf_putc(struct kcgi_buf * buf,char c)1442 kcgi_buf_putc(struct kcgi_buf *buf, char c)
1443 {
1444 
1445 	return kcgi_buf_write(&c, 1, buf);
1446 }
1447 
1448 enum kcgi_err
kcgi_buf_puts(struct kcgi_buf * buf,const char * cp)1449 kcgi_buf_puts(struct kcgi_buf *buf, const char *cp)
1450 {
1451 
1452 	if (cp == NULL)
1453 		return KCGI_OK;
1454 	return kcgi_buf_write(cp, strlen(cp), buf);
1455 }
1456