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