xref: /netbsd-src/external/mpl/dhcp/bind/dist/lib/isc/httpd.c (revision 4afad4b7fa6d4a0d3dedf41d1587a7250710ae54)
1 /*	$NetBSD: httpd.c,v 1.1 2024/02/18 20:57:49 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 /*! \file */
17 
18 #include <inttypes.h>
19 #include <stdbool.h>
20 #include <string.h>
21 
22 #include <isc/buffer.h>
23 #include <isc/httpd.h>
24 #include <isc/mem.h>
25 #include <isc/print.h>
26 #include <isc/refcount.h>
27 #include <isc/socket.h>
28 #include <isc/string.h>
29 #include <isc/task.h>
30 #include <isc/time.h>
31 #include <isc/util.h>
32 
33 #ifdef HAVE_ZLIB
34 #include <zlib.h>
35 #endif /* ifdef HAVE_ZLIB */
36 
37 /*%
38  * TODO:
39  *
40  *  o  Make the URL processing external functions which will fill-in a buffer
41  *     structure we provide, or return an error and we will render a generic
42  *     page and close the client.
43  */
44 
45 #define MSHUTTINGDOWN(cm)    ((cm->flags & ISC_HTTPDMGR_FLAGSHUTTINGDOWN) != 0)
46 #define MSETSHUTTINGDOWN(cm) (cm->flags |= ISC_HTTPDMGR_FLAGSHUTTINGDOWN)
47 
48 #define HTTP_RECVLEN	 1024
49 #define HTTP_SENDGROW	 1024
50 #define HTTP_SEND_MAXLEN 10240
51 
52 #define HTTPD_CLOSE	     0x0001 /* Got a Connection: close header */
53 #define HTTPD_FOUNDHOST	     0x0002 /* Got a Host: header */
54 #define HTTPD_KEEPALIVE	     0x0004 /* Got a Connection: Keep-Alive */
55 #define HTTPD_ACCEPT_DEFLATE 0x0008
56 
57 #define HTTPD_MAGIC    ISC_MAGIC('H', 't', 'p', 'd')
58 #define VALID_HTTPD(m) ISC_MAGIC_VALID(m, HTTPD_MAGIC)
59 
60 #define HTTPDMGR_MAGIC	  ISC_MAGIC('H', 'p', 'd', 'm')
61 #define VALID_HTTPDMGR(m) ISC_MAGIC_VALID(m, HTTPDMGR_MAGIC)
62 
63 /*% http client */
64 struct isc_httpd {
65 	unsigned int magic; /* HTTPD_MAGIC */
66 	isc_refcount_t references;
67 	isc_httpdmgr_t *mgr; /*%< our parent */
68 	ISC_LINK(isc_httpd_t) link;
69 	unsigned int state;
70 	isc_socket_t *sock;
71 
72 	/*%
73 	 * Received data state.
74 	 */
75 	char recvbuf[HTTP_RECVLEN]; /*%< receive buffer */
76 	uint32_t recvlen;	    /*%< length recv'd */
77 	char *headers;		    /*%< set in process_request() */
78 	unsigned int method;
79 	char *url;
80 	char *querystring;
81 	char *protocol;
82 
83 	/*
84 	 * Flags on the httpd client.
85 	 */
86 	int flags;
87 
88 	/*%
89 	 * Transmit data state.
90 	 *
91 	 * This is the data buffer we will transmit.
92 	 *
93 	 * This free function pointer is filled in by the rendering function
94 	 * we call.  The free function is called after the data is transmitted
95 	 * to the client.
96 	 *
97 	 * The bufflist is the list of buffers we are currently transmitting.
98 	 * The headerbuffer is where we render our headers to.  If we run out
99 	 * of space when rendering a header, we will change the size of our
100 	 * buffer.  We will not free it until we are finished, and will
101 	 * allocate an additional HTTP_SENDGROW bytes per header space grow.
102 	 *
103 	 * We currently use three buffers total, one for the headers (which
104 	 * we manage), another for the client to fill in (which it manages,
105 	 * it provides the space for it, etc) -- we will pass that buffer
106 	 * structure back to the caller, who is responsible for managing the
107 	 * space it may have allocated as backing store for it.  This second
108 	 * buffer is bodybuffer, and we only allocate the buffer itself, not
109 	 * the backing store.
110 	 * The third buffer is compbuffer, managed by us, that contains the
111 	 * compressed HTTP data, if compression is used.
112 	 *
113 	 */
114 	isc_buffer_t headerbuffer;
115 	isc_buffer_t compbuffer;
116 	isc_buffer_t *sendbuffer;
117 
118 	const char *mimetype;
119 	unsigned int retcode;
120 	const char *retmsg;
121 	isc_buffer_t bodybuffer;
122 	isc_httpdfree_t *freecb;
123 	void *freecb_arg;
124 };
125 
126 /*% lightweight socket manager for httpd output */
127 struct isc_httpdmgr {
128 	unsigned int magic; /* HTTPDMGR_MAGIC */
129 	isc_refcount_t references;
130 	isc_mem_t *mctx;
131 	isc_socket_t *sock; /*%< listening socket */
132 	isc_task_t *task;   /*%< owning task */
133 	isc_timermgr_t *timermgr;
134 
135 	isc_httpdclientok_t *client_ok;	 /*%< client validator */
136 	isc_httpdondestroy_t *ondestroy; /*%< cleanup callback */
137 	void *cb_arg;			 /*%< argument for the above */
138 
139 	unsigned int flags;
140 	ISC_LIST(isc_httpd_t) running; /*%< running clients */
141 
142 	isc_mutex_t lock;
143 
144 	ISC_LIST(isc_httpdurl_t) urls; /*%< urls we manage */
145 	isc_httpdaction_t *render_404;
146 	isc_httpdaction_t *render_500;
147 };
148 
149 /*%
150  * HTTP methods.
151  */
152 #define ISC_HTTPD_METHODUNKNOWN 0
153 #define ISC_HTTPD_METHODGET	1
154 #define ISC_HTTPD_METHODPOST	2
155 
156 /*%
157  * Client states.
158  *
159  * _IDLE	The client is not doing anything at all.  This state should
160  *		only occur just after creation, and just before being
161  *		destroyed.
162  *
163  * _RECV	The client is waiting for data after issuing a socket recv().
164  *
165  * _RECVDONE	Data has been received, and is being processed.
166  *
167  * _SEND	All data for a response has completed, and a reply was
168  *		sent via a socket send() call.
169  *
170  * _SENDDONE	Send is completed.
171  *
172  * Badly formatted state table:
173  *
174  *	IDLE -> RECV when client has a recv() queued.
175  *
176  *	RECV -> RECVDONE when recvdone event received.
177  *
178  *	RECVDONE -> SEND if the data for a reply is at hand.
179  *
180  *	SEND -> RECV when a senddone event was received.
181  *
182  *	At any time -> RECV on error.  If RECV fails, the client will
183  *	self-destroy, closing the socket and freeing memory.
184  */
185 #define ISC_HTTPD_STATEIDLE	0
186 #define ISC_HTTPD_STATERECV	1
187 #define ISC_HTTPD_STATERECVDONE 2
188 #define ISC_HTTPD_STATESEND	3
189 #define ISC_HTTPD_STATESENDDONE 4
190 
191 #define ISC_HTTPD_ISRECV(c)	((c)->state == ISC_HTTPD_STATERECV)
192 #define ISC_HTTPD_ISRECVDONE(c) ((c)->state == ISC_HTTPD_STATERECVDONE)
193 #define ISC_HTTPD_ISSEND(c)	((c)->state == ISC_HTTPD_STATESEND)
194 #define ISC_HTTPD_ISSENDDONE(c) ((c)->state == ISC_HTTPD_STATESENDDONE)
195 
196 /*%
197  * Overall magic test that means we're not idle.
198  */
199 #define ISC_HTTPD_SETRECV(c)	 ((c)->state = ISC_HTTPD_STATERECV)
200 #define ISC_HTTPD_SETRECVDONE(c) ((c)->state = ISC_HTTPD_STATERECVDONE)
201 #define ISC_HTTPD_SETSEND(c)	 ((c)->state = ISC_HTTPD_STATESEND)
202 #define ISC_HTTPD_SETSENDDONE(c) ((c)->state = ISC_HTTPD_STATESENDDONE)
203 
204 static void
205 isc_httpd_accept(isc_task_t *, isc_event_t *);
206 static void
207 isc_httpd_recvdone(isc_task_t *, isc_event_t *);
208 static void
209 isc_httpd_senddone(isc_task_t *, isc_event_t *);
210 static isc_result_t
211 process_request(isc_httpd_t *, int);
212 static isc_result_t
213 grow_headerspace(isc_httpd_t *);
214 static void
215 reset_client(isc_httpd_t *httpd);
216 
217 static isc_httpdaction_t render_404;
218 static isc_httpdaction_t render_500;
219 
220 #if ENABLE_AFL
221 static void (*finishhook)(void) = NULL;
222 #endif /* ENABLE_AFL */
223 
224 static void
225 maybe_destroy_httpd(isc_httpd_t *);
226 static void
227 destroy_httpd(isc_httpd_t *);
228 static void
229 maybe_destroy_httpdmgr(isc_httpdmgr_t *);
230 static void
231 destroy_httpdmgr(isc_httpdmgr_t *);
232 
233 static void
234 isc_httpdmgr_attach(isc_httpdmgr_t *, isc_httpdmgr_t **);
235 static void
236 isc_httpdmgr_detach(isc_httpdmgr_t **);
237 
238 static void
maybe_destroy_httpd(isc_httpd_t * httpd)239 maybe_destroy_httpd(isc_httpd_t *httpd) {
240 	if (isc_refcount_decrement(&httpd->references) == 1) {
241 		destroy_httpd(httpd);
242 	}
243 }
244 
245 static void
free_buffer(isc_mem_t * mctx,isc_buffer_t * buffer)246 free_buffer(isc_mem_t *mctx, isc_buffer_t *buffer) {
247 	isc_region_t r;
248 
249 	isc_buffer_region(buffer, &r);
250 	if (r.length > 0) {
251 		isc_mem_put(mctx, r.base, r.length);
252 	}
253 
254 	isc_buffer_initnull(buffer);
255 }
256 
257 static void
destroy_httpd(isc_httpd_t * httpd)258 destroy_httpd(isc_httpd_t *httpd) {
259 	isc_httpdmgr_t *httpdmgr;
260 
261 	REQUIRE(VALID_HTTPD(httpd));
262 
263 	httpdmgr = httpd->mgr;
264 	REQUIRE(VALID_HTTPDMGR(httpdmgr));
265 
266 	/*
267 	 * Unlink before calling isc_socket_detach so
268 	 * isc_httpdmgr_shutdown does not dereference a NULL pointer
269 	 * when calling isc_socket_cancel().
270 	 */
271 	LOCK(&httpdmgr->lock);
272 	ISC_LIST_UNLINK(httpdmgr->running, httpd, link);
273 	UNLOCK(&httpdmgr->lock);
274 
275 	httpd->magic = 0;
276 	isc_refcount_destroy(&httpd->references);
277 	isc_socket_detach(&httpd->sock);
278 
279 	free_buffer(httpdmgr->mctx, &httpd->headerbuffer);
280 	free_buffer(httpdmgr->mctx, &httpd->compbuffer);
281 
282 	isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
283 
284 #if ENABLE_AFL
285 	if (finishhook != NULL) {
286 		finishhook();
287 	}
288 #endif /* ENABLE_AFL */
289 
290 	isc_httpdmgr_detach(&httpdmgr);
291 }
292 
293 static isc_result_t
httpdmgr_socket_accept(isc_task_t * task,isc_httpdmgr_t * httpdmgr)294 httpdmgr_socket_accept(isc_task_t *task, isc_httpdmgr_t *httpdmgr) {
295 	isc_result_t result = ISC_R_SUCCESS;
296 
297 	/* decremented in isc_httpd_accept */
298 	isc_refcount_increment(&httpdmgr->references);
299 	result = isc_socket_accept(httpdmgr->sock, task, isc_httpd_accept,
300 				   httpdmgr);
301 	if (result != ISC_R_SUCCESS) {
302 		INSIST(isc_refcount_decrement(&httpdmgr->references) > 1);
303 	}
304 	return (result);
305 }
306 
307 static void
httpd_socket_recv(isc_httpd_t * httpd,isc_region_t * region,isc_task_t * task)308 httpd_socket_recv(isc_httpd_t *httpd, isc_region_t *region, isc_task_t *task) {
309 	isc_result_t result = ISC_R_SUCCESS;
310 
311 	/* decremented in isc_httpd_recvdone */
312 	(void)isc_refcount_increment(&httpd->references);
313 	result = isc_socket_recv(httpd->sock, region, 1, task,
314 				 isc_httpd_recvdone, httpd);
315 	if (result != ISC_R_SUCCESS) {
316 		INSIST(isc_refcount_decrement(&httpd->references) > 1);
317 	}
318 }
319 
320 static void
httpd_socket_send(isc_httpd_t * httpd,isc_region_t * region,isc_task_t * task)321 httpd_socket_send(isc_httpd_t *httpd, isc_region_t *region, isc_task_t *task) {
322 	isc_result_t result = ISC_R_SUCCESS;
323 
324 	/* decremented in isc_httpd_senddone */
325 	(void)isc_refcount_increment(&httpd->references);
326 	result = isc_socket_send(httpd->sock, region, task, isc_httpd_senddone,
327 				 httpd);
328 	if (result != ISC_R_SUCCESS) {
329 		INSIST(isc_refcount_decrement(&httpd->references) > 1);
330 	}
331 }
332 
333 isc_result_t
isc_httpdmgr_create(isc_mem_t * mctx,isc_socket_t * sock,isc_task_t * task,isc_httpdclientok_t * client_ok,isc_httpdondestroy_t * ondestroy,void * cb_arg,isc_timermgr_t * tmgr,isc_httpdmgr_t ** httpdmgrp)334 isc_httpdmgr_create(isc_mem_t *mctx, isc_socket_t *sock, isc_task_t *task,
335 		    isc_httpdclientok_t *client_ok,
336 		    isc_httpdondestroy_t *ondestroy, void *cb_arg,
337 		    isc_timermgr_t *tmgr, isc_httpdmgr_t **httpdmgrp) {
338 	isc_result_t result;
339 	isc_httpdmgr_t *httpdmgr;
340 
341 	REQUIRE(mctx != NULL);
342 	REQUIRE(sock != NULL);
343 	REQUIRE(task != NULL);
344 	REQUIRE(tmgr != NULL);
345 	REQUIRE(httpdmgrp != NULL && *httpdmgrp == NULL);
346 
347 	httpdmgr = isc_mem_get(mctx, sizeof(isc_httpdmgr_t));
348 
349 	*httpdmgr = (isc_httpdmgr_t){ .timermgr = tmgr, /* XXXMLG no attach
350 							 * function? */
351 				      .client_ok = client_ok,
352 				      .ondestroy = ondestroy,
353 				      .cb_arg = cb_arg,
354 				      .render_404 = render_404,
355 				      .render_500 = render_500 };
356 
357 	isc_mutex_init(&httpdmgr->lock);
358 	isc_mem_attach(mctx, &httpdmgr->mctx);
359 	isc_socket_attach(sock, &httpdmgr->sock);
360 	isc_task_attach(task, &httpdmgr->task);
361 
362 	ISC_LIST_INIT(httpdmgr->running);
363 	ISC_LIST_INIT(httpdmgr->urls);
364 
365 	isc_refcount_init(&httpdmgr->references, 1);
366 
367 	/* XXXMLG ignore errors on isc_socket_listen() */
368 	result = isc_socket_listen(sock, SOMAXCONN);
369 	if (result != ISC_R_SUCCESS) {
370 		UNEXPECTED_ERROR(__FILE__, __LINE__,
371 				 "isc_socket_listen() failed: %s",
372 				 isc_result_totext(result));
373 		goto cleanup;
374 	}
375 
376 	(void)isc_socket_filter(sock, "httpready");
377 
378 	httpdmgr->magic = HTTPDMGR_MAGIC;
379 
380 	result = httpdmgr_socket_accept(task, httpdmgr);
381 	if (result != ISC_R_SUCCESS) {
382 		goto cleanup;
383 	}
384 
385 	*httpdmgrp = httpdmgr;
386 	return (ISC_R_SUCCESS);
387 
388 cleanup:
389 	httpdmgr->magic = 0;
390 	isc_refcount_decrementz(&httpdmgr->references);
391 	isc_refcount_destroy(&httpdmgr->references);
392 	isc_task_detach(&httpdmgr->task);
393 	isc_socket_detach(&httpdmgr->sock);
394 	isc_mem_detach(&httpdmgr->mctx);
395 	isc_mutex_destroy(&httpdmgr->lock);
396 	isc_mem_put(mctx, httpdmgr, sizeof(isc_httpdmgr_t));
397 	return (result);
398 }
399 
400 static void
isc_httpdmgr_attach(isc_httpdmgr_t * source,isc_httpdmgr_t ** targetp)401 isc_httpdmgr_attach(isc_httpdmgr_t *source, isc_httpdmgr_t **targetp) {
402 	REQUIRE(VALID_HTTPDMGR(source));
403 	REQUIRE(targetp != NULL && *targetp == NULL);
404 
405 	isc_refcount_increment(&source->references);
406 
407 	*targetp = source;
408 }
409 
410 static void
isc_httpdmgr_detach(isc_httpdmgr_t ** httpdmgrp)411 isc_httpdmgr_detach(isc_httpdmgr_t **httpdmgrp) {
412 	REQUIRE(httpdmgrp != NULL && VALID_HTTPDMGR(*httpdmgrp));
413 	isc_httpdmgr_t *httpdmgr = *httpdmgrp;
414 	*httpdmgrp = NULL;
415 
416 	maybe_destroy_httpdmgr(httpdmgr);
417 }
418 
419 static void
maybe_destroy_httpdmgr(isc_httpdmgr_t * httpdmgr)420 maybe_destroy_httpdmgr(isc_httpdmgr_t *httpdmgr) {
421 	if (isc_refcount_decrement(&httpdmgr->references) == 1) {
422 		destroy_httpdmgr(httpdmgr);
423 	}
424 }
425 
426 static void
destroy_httpdmgr(isc_httpdmgr_t * httpdmgr)427 destroy_httpdmgr(isc_httpdmgr_t *httpdmgr) {
428 	isc_httpdurl_t *url;
429 
430 	isc_refcount_destroy(&httpdmgr->references);
431 
432 	LOCK(&httpdmgr->lock);
433 
434 	httpdmgr->magic = 0;
435 
436 	INSIST(MSHUTTINGDOWN(httpdmgr));
437 	INSIST(ISC_LIST_EMPTY(httpdmgr->running));
438 
439 	isc_socket_detach(&httpdmgr->sock);
440 	isc_task_detach(&httpdmgr->task);
441 	httpdmgr->timermgr = NULL;
442 
443 	/*
444 	 * Clear out the list of all actions we know about.  Just free the
445 	 * memory.
446 	 */
447 	url = ISC_LIST_HEAD(httpdmgr->urls);
448 	while (url != NULL) {
449 		isc_mem_free(httpdmgr->mctx, url->url);
450 		ISC_LIST_UNLINK(httpdmgr->urls, url, link);
451 		isc_mem_put(httpdmgr->mctx, url, sizeof(isc_httpdurl_t));
452 		url = ISC_LIST_HEAD(httpdmgr->urls);
453 	}
454 
455 	UNLOCK(&httpdmgr->lock);
456 	isc_mutex_destroy(&httpdmgr->lock);
457 
458 	if (httpdmgr->ondestroy != NULL) {
459 		(httpdmgr->ondestroy)(httpdmgr->cb_arg);
460 	}
461 	isc_mem_putanddetach(&httpdmgr->mctx, httpdmgr, sizeof(isc_httpdmgr_t));
462 }
463 
464 #define LENGTHOK(s) (httpd->recvbuf - (s) < (int)httpd->recvlen)
465 #define BUFLENOK(s) (httpd->recvbuf - (s) < HTTP_RECVLEN)
466 
467 /*
468  * Look for the given header in headers.
469  * If value is specified look for it terminated with a character in eov.
470  */
471 static bool
have_header(isc_httpd_t * httpd,const char * header,const char * value,const char * eov)472 have_header(isc_httpd_t *httpd, const char *header, const char *value,
473 	    const char *eov) {
474 	char *cr, *nl, *h;
475 	size_t hlen, vlen = 0;
476 
477 	h = httpd->headers;
478 	hlen = strlen(header);
479 	if (value != NULL) {
480 		INSIST(eov != NULL);
481 		vlen = strlen(value);
482 	}
483 
484 	for (;;) {
485 		if (strncasecmp(h, header, hlen) != 0) {
486 			/*
487 			 * Skip to next line;
488 			 */
489 			cr = strchr(h, '\r');
490 			if (cr != NULL && cr[1] == '\n') {
491 				cr++;
492 			}
493 			nl = strchr(h, '\n');
494 
495 			/* last header? */
496 			h = cr;
497 			if (h == NULL || (nl != NULL && nl < h)) {
498 				h = nl;
499 			}
500 			if (h == NULL) {
501 				return (false);
502 			}
503 			h++;
504 			continue;
505 		}
506 
507 		if (value == NULL) {
508 			return (true);
509 		}
510 
511 		/*
512 		 * Skip optional leading white space.
513 		 */
514 		h += hlen;
515 		while (*h == ' ' || *h == '\t') {
516 			h++;
517 		}
518 		/*
519 		 * Terminate token search on NULL or EOL.
520 		 */
521 		while (*h != 0 && *h != '\r' && *h != '\n') {
522 			if (strncasecmp(h, value, vlen) == 0) {
523 				if (strchr(eov, h[vlen]) != NULL) {
524 					return (true);
525 					/*
526 					 * Skip to next token.
527 					 */
528 				}
529 			}
530 			/*
531 			 * Skip to next token.
532 			 */
533 			h += strcspn(h, eov);
534 			if (h[0] == '\r' && h[1] == '\n') {
535 				h++;
536 			}
537 			if (h[0] != 0) {
538 				h++;
539 			}
540 		}
541 		return (false);
542 	}
543 }
544 
545 static isc_result_t
process_request(isc_httpd_t * httpd,int length)546 process_request(isc_httpd_t *httpd, int length) {
547 	char *s;
548 	char *p;
549 	int delim;
550 
551 	httpd->recvlen += length;
552 
553 	httpd->recvbuf[httpd->recvlen] = 0;
554 	httpd->headers = NULL;
555 
556 	/*
557 	 * If we don't find a blank line in our buffer, return that we need
558 	 * more data.
559 	 */
560 	s = strstr(httpd->recvbuf, "\r\n\r\n");
561 	delim = 2;
562 	if (s == NULL) {
563 		s = strstr(httpd->recvbuf, "\n\n");
564 		delim = 1;
565 	}
566 	if (s == NULL) {
567 		return (ISC_R_NOTFOUND);
568 	}
569 
570 	/*
571 	 * NUL terminate request at the blank line.
572 	 */
573 	s[delim] = 0;
574 
575 	/*
576 	 * Determine if this is a POST or GET method.  Any other values will
577 	 * cause an error to be returned.
578 	 */
579 	if (strncmp(httpd->recvbuf, "GET ", 4) == 0) {
580 		httpd->method = ISC_HTTPD_METHODGET;
581 		p = httpd->recvbuf + 4;
582 	} else if (strncmp(httpd->recvbuf, "POST ", 5) == 0) {
583 		httpd->method = ISC_HTTPD_METHODPOST;
584 		p = httpd->recvbuf + 5;
585 	} else {
586 		return (ISC_R_RANGE);
587 	}
588 
589 	/*
590 	 * From now on, p is the start of our buffer.
591 	 */
592 
593 	/*
594 	 * Extract the URL.
595 	 */
596 	s = p;
597 	while (LENGTHOK(s) && BUFLENOK(s) &&
598 	       (*s != '\n' && *s != '\r' && *s != '\0' && *s != ' '))
599 	{
600 		s++;
601 	}
602 	if (!LENGTHOK(s)) {
603 		return (ISC_R_NOTFOUND);
604 	}
605 	if (!BUFLENOK(s)) {
606 		return (ISC_R_NOMEMORY);
607 	}
608 	*s = 0;
609 
610 	/*
611 	 * Make the URL relative.
612 	 */
613 	if ((strncmp(p, "http:/", 6) == 0) || (strncmp(p, "https:/", 7) == 0)) {
614 		/* Skip first / */
615 		while (*p != '/' && *p != 0) {
616 			p++;
617 		}
618 		if (*p == 0) {
619 			return (ISC_R_RANGE);
620 		}
621 		p++;
622 		/* Skip second / */
623 		while (*p != '/' && *p != 0) {
624 			p++;
625 		}
626 		if (*p == 0) {
627 			return (ISC_R_RANGE);
628 		}
629 		p++;
630 		/* Find third / */
631 		while (*p != '/' && *p != 0) {
632 			p++;
633 		}
634 		if (*p == 0) {
635 			p--;
636 			*p = '/';
637 		}
638 	}
639 
640 	httpd->url = p;
641 	p = s + 1;
642 	s = p;
643 
644 	/*
645 	 * Now, see if there is a ? mark in the URL.  If so, this is
646 	 * part of the query string, and we will split it from the URL.
647 	 */
648 	httpd->querystring = strchr(httpd->url, '?');
649 	if (httpd->querystring != NULL) {
650 		*(httpd->querystring) = 0;
651 		httpd->querystring++;
652 	}
653 
654 	/*
655 	 * Extract the HTTP/1.X protocol.  We will bounce on anything but
656 	 * HTTP/1.0 or HTTP/1.1 for now.
657 	 */
658 	while (LENGTHOK(s) && BUFLENOK(s) &&
659 	       (*s != '\n' && *s != '\r' && *s != '\0'))
660 	{
661 		s++;
662 	}
663 	if (!LENGTHOK(s)) {
664 		return (ISC_R_NOTFOUND);
665 	}
666 	if (!BUFLENOK(s)) {
667 		return (ISC_R_NOMEMORY);
668 	}
669 	/*
670 	 * Check that we have the expected eol delimiter.
671 	 */
672 	if (strncmp(s, delim == 1 ? "\n" : "\r\n", delim) != 0) {
673 		return (ISC_R_RANGE);
674 	}
675 	*s = 0;
676 	if ((strncmp(p, "HTTP/1.0", 8) != 0) &&
677 	    (strncmp(p, "HTTP/1.1", 8) != 0))
678 	{
679 		return (ISC_R_RANGE);
680 	}
681 	httpd->protocol = p;
682 	p = s + delim; /* skip past eol */
683 	s = p;
684 
685 	httpd->headers = s;
686 
687 	if (have_header(httpd, "Connection:", "close", ", \t\r\n")) {
688 		httpd->flags |= HTTPD_CLOSE;
689 	}
690 
691 	if (have_header(httpd, "Host:", NULL, NULL)) {
692 		httpd->flags |= HTTPD_FOUNDHOST;
693 	}
694 
695 	if (strncmp(httpd->protocol, "HTTP/1.0", 8) == 0) {
696 		if (have_header(httpd, "Connection:", "Keep-Alive", ", \t\r\n"))
697 		{
698 			httpd->flags |= HTTPD_KEEPALIVE;
699 		} else {
700 			httpd->flags |= HTTPD_CLOSE;
701 		}
702 	}
703 
704 	/*
705 	 * Check for Accept-Encoding:
706 	 */
707 #ifdef HAVE_ZLIB
708 	if (have_header(httpd, "Accept-Encoding:", "deflate", ";, \t\r\n")) {
709 		httpd->flags |= HTTPD_ACCEPT_DEFLATE;
710 	}
711 #endif /* ifdef HAVE_ZLIB */
712 
713 	/*
714 	 * Standards compliance hooks here.
715 	 */
716 	if (strcmp(httpd->protocol, "HTTP/1.1") == 0 &&
717 	    ((httpd->flags & HTTPD_FOUNDHOST) == 0))
718 	{
719 		return (ISC_R_RANGE);
720 	}
721 
722 	return (ISC_R_SUCCESS);
723 }
724 
725 static void
isc_httpd_create(isc_httpdmgr_t * httpdmgr,isc_socket_t * sock,isc_httpd_t ** httpdp)726 isc_httpd_create(isc_httpdmgr_t *httpdmgr, isc_socket_t *sock,
727 		 isc_httpd_t **httpdp) {
728 	isc_httpd_t *httpd;
729 	char *headerdata;
730 
731 	REQUIRE(VALID_HTTPDMGR(httpdmgr));
732 	REQUIRE(httpdp != NULL && *httpdp == NULL);
733 
734 	httpd = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpd_t));
735 
736 	*httpd = (isc_httpd_t){ .sock = sock };
737 
738 	isc_httpdmgr_attach(httpdmgr, &httpd->mgr);
739 
740 	isc_refcount_init(&httpd->references, 1);
741 	ISC_HTTPD_SETRECV(httpd);
742 	isc_socket_setname(httpd->sock, "httpd", NULL);
743 
744 	/*
745 	 * Initialize the buffer for our headers.
746 	 */
747 	headerdata = isc_mem_get(httpdmgr->mctx, HTTP_SENDGROW);
748 	isc_buffer_init(&httpd->headerbuffer, headerdata, HTTP_SENDGROW);
749 
750 	isc_buffer_initnull(&httpd->compbuffer);
751 	isc_buffer_initnull(&httpd->bodybuffer);
752 	reset_client(httpd);
753 
754 	ISC_LINK_INIT(httpd, link);
755 	ISC_LIST_APPEND(httpdmgr->running, httpd, link);
756 
757 	httpd->magic = HTTPD_MAGIC;
758 
759 	*httpdp = httpd;
760 }
761 
762 static void
isc_httpd_accept(isc_task_t * task,isc_event_t * ev)763 isc_httpd_accept(isc_task_t *task, isc_event_t *ev) {
764 	isc_httpdmgr_t *httpdmgr = ev->ev_arg;
765 	isc_httpd_t *httpd = NULL;
766 	isc_region_t r;
767 	isc_socket_newconnev_t *nev = (isc_socket_newconnev_t *)ev;
768 	isc_sockaddr_t peeraddr;
769 
770 	REQUIRE(VALID_HTTPDMGR(httpdmgr));
771 
772 	LOCK(&httpdmgr->lock);
773 	if (MSHUTTINGDOWN(httpdmgr)) {
774 		goto out;
775 	}
776 
777 	if (nev->result == ISC_R_CANCELED) {
778 		goto out;
779 	}
780 
781 	if (nev->result != ISC_R_SUCCESS) {
782 		/* XXXMLG log failure */
783 		goto requeue;
784 	}
785 
786 	(void)isc_socket_getpeername(nev->newsocket, &peeraddr);
787 	if (httpdmgr->client_ok != NULL &&
788 	    !(httpdmgr->client_ok)(&peeraddr, httpdmgr->cb_arg))
789 	{
790 		isc_socket_detach(&nev->newsocket);
791 		goto requeue;
792 	}
793 
794 	isc_httpd_create(httpdmgr, nev->newsocket, &httpd);
795 
796 	r.base = (unsigned char *)httpd->recvbuf;
797 	r.length = HTTP_RECVLEN - 1;
798 
799 	httpd_socket_recv(httpd, &r, task);
800 
801 requeue:
802 	(void)httpdmgr_socket_accept(task, httpdmgr);
803 
804 out:
805 	UNLOCK(&httpdmgr->lock);
806 
807 	if (httpd != NULL) {
808 		maybe_destroy_httpd(httpd);
809 	}
810 	maybe_destroy_httpdmgr(httpdmgr);
811 
812 	isc_event_free(&ev);
813 }
814 
815 static isc_result_t
render_404(const char * url,isc_httpdurl_t * urlinfo,const char * querystring,const char * headers,void * arg,unsigned int * retcode,const char ** retmsg,const char ** mimetype,isc_buffer_t * b,isc_httpdfree_t ** freecb,void ** freecb_args)816 render_404(const char *url, isc_httpdurl_t *urlinfo, const char *querystring,
817 	   const char *headers, void *arg, unsigned int *retcode,
818 	   const char **retmsg, const char **mimetype, isc_buffer_t *b,
819 	   isc_httpdfree_t **freecb, void **freecb_args) {
820 	static char msg[] = "No such URL.\r\n";
821 
822 	UNUSED(url);
823 	UNUSED(urlinfo);
824 	UNUSED(querystring);
825 	UNUSED(headers);
826 	UNUSED(arg);
827 
828 	*retcode = 404;
829 	*retmsg = "No such URL";
830 	*mimetype = "text/plain";
831 	isc_buffer_reinit(b, msg, strlen(msg));
832 	isc_buffer_add(b, strlen(msg));
833 	*freecb = NULL;
834 	*freecb_args = NULL;
835 
836 	return (ISC_R_SUCCESS);
837 }
838 
839 static isc_result_t
render_500(const char * url,isc_httpdurl_t * urlinfo,const char * querystring,const char * headers,void * arg,unsigned int * retcode,const char ** retmsg,const char ** mimetype,isc_buffer_t * b,isc_httpdfree_t ** freecb,void ** freecb_args)840 render_500(const char *url, isc_httpdurl_t *urlinfo, const char *querystring,
841 	   const char *headers, void *arg, unsigned int *retcode,
842 	   const char **retmsg, const char **mimetype, isc_buffer_t *b,
843 	   isc_httpdfree_t **freecb, void **freecb_args) {
844 	static char msg[] = "Internal server failure.\r\n";
845 
846 	UNUSED(url);
847 	UNUSED(urlinfo);
848 	UNUSED(querystring);
849 	UNUSED(headers);
850 	UNUSED(arg);
851 
852 	*retcode = 500;
853 	*retmsg = "Internal server failure";
854 	*mimetype = "text/plain";
855 	isc_buffer_reinit(b, msg, strlen(msg));
856 	isc_buffer_add(b, strlen(msg));
857 	*freecb = NULL;
858 	*freecb_args = NULL;
859 
860 	return (ISC_R_SUCCESS);
861 }
862 
863 #ifdef HAVE_ZLIB
864 /*%<
865  * Reallocates compbuffer to size, does nothing if compbuffer is already
866  * larger than size.
867  *
868  * Requires:
869  *\li	httpd a valid isc_httpd_t object
870  *
871  * Returns:
872  *\li	#ISC_R_SUCCESS		-- all is well.
873  *\li	#ISC_R_NOMEMORY		-- not enough memory to extend buffer
874  */
875 static isc_result_t
alloc_compspace(isc_httpd_t * httpd,unsigned int size)876 alloc_compspace(isc_httpd_t *httpd, unsigned int size) {
877 	char *newspace;
878 	isc_region_t r;
879 
880 	isc_buffer_region(&httpd->compbuffer, &r);
881 	if (size < r.length) {
882 		return (ISC_R_SUCCESS);
883 	}
884 
885 	newspace = isc_mem_get(httpd->mgr->mctx, size);
886 	isc_buffer_reinit(&httpd->compbuffer, newspace, size);
887 
888 	if (r.base != NULL) {
889 		isc_mem_put(httpd->mgr->mctx, r.base, r.length);
890 	}
891 
892 	return (ISC_R_SUCCESS);
893 }
894 
895 /*%<
896  * Tries to compress httpd->bodybuffer to httpd->compbuffer, extending it
897  * if necessary.
898  *
899  * Requires:
900  *\li	httpd a valid isc_httpd_t object
901  *
902  * Returns:
903  *\li	#ISC_R_SUCCESS	  -- all is well.
904  *\li	#ISC_R_NOMEMORY	  -- not enough memory to compress data
905  *\li	#ISC_R_FAILURE	  -- error during compression or compressed
906  *			     data would be larger than input data
907  */
908 static isc_result_t
isc_httpd_compress(isc_httpd_t * httpd)909 isc_httpd_compress(isc_httpd_t *httpd) {
910 	z_stream zstr;
911 	isc_region_t r;
912 	isc_result_t result;
913 	int ret;
914 	int inputlen;
915 
916 	inputlen = isc_buffer_usedlength(&httpd->bodybuffer);
917 	result = alloc_compspace(httpd, inputlen);
918 	if (result != ISC_R_SUCCESS) {
919 		return (result);
920 	}
921 	isc_buffer_clear(&httpd->compbuffer);
922 	isc_buffer_region(&httpd->compbuffer, &r);
923 
924 	/*
925 	 * We're setting output buffer size to input size so it fails if the
926 	 * compressed data size would be bigger than the input size.
927 	 */
928 	memset(&zstr, 0, sizeof(zstr));
929 	zstr.total_in = zstr.avail_in = zstr.total_out = zstr.avail_out =
930 		inputlen;
931 
932 	zstr.next_in = isc_buffer_base(&httpd->bodybuffer);
933 	zstr.next_out = r.base;
934 
935 	ret = deflateInit(&zstr, Z_DEFAULT_COMPRESSION);
936 	if (ret == Z_OK) {
937 		ret = deflate(&zstr, Z_FINISH);
938 	}
939 	deflateEnd(&zstr);
940 	if (ret == Z_STREAM_END) {
941 		isc_buffer_add(&httpd->compbuffer, inputlen - zstr.avail_out);
942 		return (ISC_R_SUCCESS);
943 	} else {
944 		return (ISC_R_FAILURE);
945 	}
946 }
947 #endif /* ifdef HAVE_ZLIB */
948 
949 static void
isc_httpd_recvdone(isc_task_t * task,isc_event_t * ev)950 isc_httpd_recvdone(isc_task_t *task, isc_event_t *ev) {
951 	isc_result_t result;
952 	isc_httpd_t *httpd = ev->ev_arg;
953 	isc_socketevent_t *sev = (isc_socketevent_t *)ev;
954 	isc_buffer_t *databuffer;
955 	isc_httpdurl_t *url;
956 	isc_time_t now;
957 	isc_region_t r;
958 	bool is_compressed = false;
959 	char datebuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
960 
961 	REQUIRE(VALID_HTTPD(httpd));
962 
963 	INSIST(ISC_HTTPD_ISRECV(httpd));
964 
965 	if (sev->result != ISC_R_SUCCESS) {
966 		goto out;
967 	}
968 
969 	result = process_request(httpd, sev->n);
970 	if (result == ISC_R_NOTFOUND) {
971 		if (httpd->recvlen >= HTTP_RECVLEN - 1) {
972 			goto out;
973 		}
974 		r.base = (unsigned char *)httpd->recvbuf + httpd->recvlen;
975 		r.length = HTTP_RECVLEN - httpd->recvlen - 1;
976 
977 		httpd_socket_recv(httpd, &r, task);
978 		goto out;
979 	} else if (result != ISC_R_SUCCESS) {
980 		goto out;
981 	}
982 
983 	ISC_HTTPD_SETSEND(httpd);
984 
985 	/*
986 	 * XXXMLG Call function here.  Provide an add-header function
987 	 * which will append the common headers to a response we generate.
988 	 */
989 	isc_buffer_initnull(&httpd->bodybuffer);
990 	isc_time_now(&now);
991 	isc_time_formathttptimestamp(&now, datebuf, sizeof(datebuf));
992 	LOCK(&httpd->mgr->lock);
993 	url = ISC_LIST_HEAD(httpd->mgr->urls);
994 	while (url != NULL) {
995 		if (strcmp(httpd->url, url->url) == 0) {
996 			break;
997 		}
998 		url = ISC_LIST_NEXT(url, link);
999 	}
1000 	UNLOCK(&httpd->mgr->lock);
1001 
1002 	if (url == NULL) {
1003 		result = httpd->mgr->render_404(
1004 			httpd->url, NULL, httpd->querystring, NULL, NULL,
1005 			&httpd->retcode, &httpd->retmsg, &httpd->mimetype,
1006 			&httpd->bodybuffer, &httpd->freecb, &httpd->freecb_arg);
1007 	} else {
1008 		result = url->action(httpd->url, url, httpd->querystring,
1009 				     httpd->headers, url->action_arg,
1010 				     &httpd->retcode, &httpd->retmsg,
1011 				     &httpd->mimetype, &httpd->bodybuffer,
1012 				     &httpd->freecb, &httpd->freecb_arg);
1013 	}
1014 	if (result != ISC_R_SUCCESS) {
1015 		result = httpd->mgr->render_500(
1016 			httpd->url, url, httpd->querystring, NULL, NULL,
1017 			&httpd->retcode, &httpd->retmsg, &httpd->mimetype,
1018 			&httpd->bodybuffer, &httpd->freecb, &httpd->freecb_arg);
1019 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
1020 	}
1021 
1022 #ifdef HAVE_ZLIB
1023 	if ((httpd->flags & HTTPD_ACCEPT_DEFLATE) != 0) {
1024 		result = isc_httpd_compress(httpd);
1025 		if (result == ISC_R_SUCCESS) {
1026 			is_compressed = true;
1027 		}
1028 	}
1029 #endif /* ifdef HAVE_ZLIB */
1030 
1031 	isc_httpd_response(httpd);
1032 	if ((httpd->flags & HTTPD_KEEPALIVE) != 0) {
1033 		isc_httpd_addheader(httpd, "Connection", "Keep-Alive");
1034 	}
1035 	isc_httpd_addheader(httpd, "Content-Type", httpd->mimetype);
1036 	isc_httpd_addheader(httpd, "Date", datebuf);
1037 	isc_httpd_addheader(httpd, "Expires", datebuf);
1038 
1039 	if (url != NULL && url->isstatic) {
1040 		char loadbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
1041 		isc_time_formathttptimestamp(&url->loadtime, loadbuf,
1042 					     sizeof(loadbuf));
1043 		isc_httpd_addheader(httpd, "Last-Modified", loadbuf);
1044 		isc_httpd_addheader(httpd, "Cache-Control: public", NULL);
1045 	} else {
1046 		isc_httpd_addheader(httpd, "Last-Modified", datebuf);
1047 		isc_httpd_addheader(httpd, "Pragma: no-cache", NULL);
1048 		isc_httpd_addheader(httpd, "Cache-Control: no-cache", NULL);
1049 	}
1050 
1051 	isc_httpd_addheader(httpd, "Server: libisc", NULL);
1052 
1053 	if (is_compressed) {
1054 		isc_httpd_addheader(httpd, "Content-Encoding", "deflate");
1055 		isc_httpd_addheaderuint(
1056 			httpd, "Content-Length",
1057 			isc_buffer_usedlength(&httpd->compbuffer));
1058 	} else {
1059 		isc_httpd_addheaderuint(
1060 			httpd, "Content-Length",
1061 			isc_buffer_usedlength(&httpd->bodybuffer));
1062 	}
1063 
1064 	isc_httpd_endheaders(httpd); /* done */
1065 
1066 	/*
1067 	 * Append either the compressed or the non-compressed response body to
1068 	 * the response headers and store the result in httpd->sendbuffer.
1069 	 */
1070 	isc_buffer_dup(httpd->mgr->mctx, &httpd->sendbuffer,
1071 		       &httpd->headerbuffer);
1072 	isc_buffer_setautorealloc(httpd->sendbuffer, true);
1073 	databuffer = (is_compressed ? &httpd->compbuffer : &httpd->bodybuffer);
1074 	isc_buffer_usedregion(databuffer, &r);
1075 	result = isc_buffer_copyregion(httpd->sendbuffer, &r);
1076 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
1077 	/*
1078 	 * Determine total response size.
1079 	 */
1080 	isc_buffer_usedregion(httpd->sendbuffer, &r);
1081 
1082 	httpd_socket_send(httpd, &r, task);
1083 
1084 out:
1085 	maybe_destroy_httpd(httpd);
1086 	isc_event_free(&ev);
1087 }
1088 
1089 void
isc_httpdmgr_shutdown(isc_httpdmgr_t ** httpdmgrp)1090 isc_httpdmgr_shutdown(isc_httpdmgr_t **httpdmgrp) {
1091 	isc_httpdmgr_t *httpdmgr;
1092 	isc_httpd_t *httpd;
1093 
1094 	REQUIRE(httpdmgrp != NULL);
1095 	httpdmgr = *httpdmgrp;
1096 	*httpdmgrp = NULL;
1097 	REQUIRE(VALID_HTTPDMGR(httpdmgr));
1098 
1099 	LOCK(&httpdmgr->lock);
1100 
1101 	MSETSHUTTINGDOWN(httpdmgr);
1102 
1103 	isc_socket_cancel(httpdmgr->sock, httpdmgr->task, ISC_SOCKCANCEL_ALL);
1104 
1105 	httpd = ISC_LIST_HEAD(httpdmgr->running);
1106 	while (httpd != NULL) {
1107 		isc_socket_cancel(httpd->sock, httpdmgr->task,
1108 				  ISC_SOCKCANCEL_ALL);
1109 		httpd = ISC_LIST_NEXT(httpd, link);
1110 	}
1111 
1112 	UNLOCK(&httpdmgr->lock);
1113 
1114 	maybe_destroy_httpdmgr(httpdmgr);
1115 }
1116 
1117 static isc_result_t
grow_headerspace(isc_httpd_t * httpd)1118 grow_headerspace(isc_httpd_t *httpd) {
1119 	char *newspace;
1120 	unsigned int newlen;
1121 	isc_region_t r;
1122 
1123 	isc_buffer_region(&httpd->headerbuffer, &r);
1124 	newlen = r.length + HTTP_SENDGROW;
1125 	if (newlen > HTTP_SEND_MAXLEN) {
1126 		return (ISC_R_NOSPACE);
1127 	}
1128 
1129 	newspace = isc_mem_get(httpd->mgr->mctx, newlen);
1130 
1131 	isc_buffer_reinit(&httpd->headerbuffer, newspace, newlen);
1132 
1133 	isc_mem_put(httpd->mgr->mctx, r.base, r.length);
1134 
1135 	return (ISC_R_SUCCESS);
1136 }
1137 
1138 isc_result_t
isc_httpd_response(isc_httpd_t * httpd)1139 isc_httpd_response(isc_httpd_t *httpd) {
1140 	isc_result_t result;
1141 	unsigned int needlen;
1142 
1143 	REQUIRE(VALID_HTTPD(httpd));
1144 
1145 	needlen = strlen(httpd->protocol) + 1; /* protocol + space */
1146 	needlen += 3 + 1; /* room for response code, always 3 bytes */
1147 	needlen += strlen(httpd->retmsg) + 2; /* return msg + CRLF */
1148 
1149 	while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
1150 		result = grow_headerspace(httpd);
1151 		if (result != ISC_R_SUCCESS) {
1152 			return (result);
1153 		}
1154 	}
1155 
1156 	return (isc_buffer_printf(&httpd->headerbuffer, "%s %03u %s\r\n",
1157 				  httpd->protocol, httpd->retcode,
1158 				  httpd->retmsg));
1159 }
1160 
1161 isc_result_t
isc_httpd_addheader(isc_httpd_t * httpd,const char * name,const char * val)1162 isc_httpd_addheader(isc_httpd_t *httpd, const char *name, const char *val) {
1163 	isc_result_t result;
1164 	unsigned int needlen;
1165 
1166 	REQUIRE(VALID_HTTPD(httpd));
1167 
1168 	needlen = strlen(name); /* name itself */
1169 	if (val != NULL) {
1170 		needlen += 2 + strlen(val); /* :<space> and val */
1171 	}
1172 	needlen += 2; /* CRLF */
1173 
1174 	while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
1175 		result = grow_headerspace(httpd);
1176 		if (result != ISC_R_SUCCESS) {
1177 			return (result);
1178 		}
1179 	}
1180 
1181 	if (val != NULL) {
1182 		return (isc_buffer_printf(&httpd->headerbuffer, "%s: %s\r\n",
1183 					  name, val));
1184 	} else {
1185 		return (isc_buffer_printf(&httpd->headerbuffer, "%s\r\n",
1186 					  name));
1187 	}
1188 }
1189 
1190 isc_result_t
isc_httpd_endheaders(isc_httpd_t * httpd)1191 isc_httpd_endheaders(isc_httpd_t *httpd) {
1192 	isc_result_t result;
1193 
1194 	REQUIRE(VALID_HTTPD(httpd));
1195 
1196 	while (isc_buffer_availablelength(&httpd->headerbuffer) < 2) {
1197 		result = grow_headerspace(httpd);
1198 		if (result != ISC_R_SUCCESS) {
1199 			return (result);
1200 		}
1201 	}
1202 
1203 	return (isc_buffer_printf(&httpd->headerbuffer, "\r\n"));
1204 }
1205 
1206 isc_result_t
isc_httpd_addheaderuint(isc_httpd_t * httpd,const char * name,int val)1207 isc_httpd_addheaderuint(isc_httpd_t *httpd, const char *name, int val) {
1208 	isc_result_t result;
1209 	unsigned int needlen;
1210 	char buf[sizeof "18446744073709551616"];
1211 
1212 	REQUIRE(VALID_HTTPD(httpd));
1213 
1214 	snprintf(buf, sizeof(buf), "%d", val);
1215 
1216 	needlen = strlen(name);	    /* name itself */
1217 	needlen += 2 + strlen(buf); /* :<space> and val */
1218 	needlen += 2;		    /* CRLF */
1219 
1220 	while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
1221 		result = grow_headerspace(httpd);
1222 		if (result != ISC_R_SUCCESS) {
1223 			return (result);
1224 		}
1225 	}
1226 
1227 	return (isc_buffer_printf(&httpd->headerbuffer, "%s: %s\r\n", name,
1228 				  buf));
1229 }
1230 
1231 static void
isc_httpd_senddone(isc_task_t * task,isc_event_t * ev)1232 isc_httpd_senddone(isc_task_t *task, isc_event_t *ev) {
1233 	isc_httpd_t *httpd = ev->ev_arg;
1234 	isc_region_t r;
1235 	isc_socketevent_t *sev = (isc_socketevent_t *)ev;
1236 
1237 	REQUIRE(VALID_HTTPD(httpd));
1238 
1239 	INSIST(ISC_HTTPD_ISSEND(httpd));
1240 
1241 	isc_buffer_free(&httpd->sendbuffer);
1242 
1243 	/*
1244 	 * We will always want to clean up our receive buffer, even if we
1245 	 * got an error on send or we are shutting down.
1246 	 *
1247 	 * We will pass in the buffer only if there is data in it.  If
1248 	 * there is no data, we will pass in a NULL.
1249 	 */
1250 	if (httpd->freecb != NULL) {
1251 		isc_buffer_t *b = NULL;
1252 		if (isc_buffer_length(&httpd->bodybuffer) > 0) {
1253 			b = &httpd->bodybuffer;
1254 			httpd->freecb(b, httpd->freecb_arg);
1255 		}
1256 	}
1257 
1258 	if (sev->result != ISC_R_SUCCESS) {
1259 		goto out;
1260 	}
1261 
1262 	if ((httpd->flags & HTTPD_CLOSE) != 0) {
1263 		goto out;
1264 	}
1265 
1266 	ISC_HTTPD_SETRECV(httpd);
1267 
1268 	reset_client(httpd);
1269 
1270 	r.base = (unsigned char *)httpd->recvbuf;
1271 	r.length = HTTP_RECVLEN - 1;
1272 
1273 	httpd_socket_recv(httpd, &r, task);
1274 
1275 out:
1276 	maybe_destroy_httpd(httpd);
1277 	isc_event_free(&ev);
1278 }
1279 
1280 static void
reset_client(isc_httpd_t * httpd)1281 reset_client(isc_httpd_t *httpd) {
1282 	/*
1283 	 * Catch errors here.  We MUST be in RECV mode, and we MUST NOT have
1284 	 * any outstanding buffers.  If we have buffers, we have a leak.
1285 	 */
1286 	INSIST(ISC_HTTPD_ISRECV(httpd));
1287 	INSIST(!ISC_LINK_LINKED(&httpd->headerbuffer, link));
1288 	INSIST(!ISC_LINK_LINKED(&httpd->bodybuffer, link));
1289 
1290 	httpd->recvbuf[0] = 0;
1291 	httpd->recvlen = 0;
1292 	httpd->headers = NULL;
1293 	httpd->method = ISC_HTTPD_METHODUNKNOWN;
1294 	httpd->url = NULL;
1295 	httpd->querystring = NULL;
1296 	httpd->protocol = NULL;
1297 	httpd->flags = 0;
1298 
1299 	isc_buffer_clear(&httpd->headerbuffer);
1300 	isc_buffer_clear(&httpd->compbuffer);
1301 	isc_buffer_invalidate(&httpd->bodybuffer);
1302 }
1303 
1304 isc_result_t
isc_httpdmgr_addurl(isc_httpdmgr_t * httpdmgr,const char * url,isc_httpdaction_t * func,void * arg)1305 isc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url,
1306 		    isc_httpdaction_t *func, void *arg) {
1307 	/* REQUIRE(VALID_HTTPDMGR(httpdmgr)); Dummy function */
1308 
1309 	return (isc_httpdmgr_addurl2(httpdmgr, url, false, func, arg));
1310 }
1311 
1312 isc_result_t
isc_httpdmgr_addurl2(isc_httpdmgr_t * httpdmgr,const char * url,bool isstatic,isc_httpdaction_t * func,void * arg)1313 isc_httpdmgr_addurl2(isc_httpdmgr_t *httpdmgr, const char *url, bool isstatic,
1314 		     isc_httpdaction_t *func, void *arg) {
1315 	isc_httpdurl_t *item;
1316 
1317 	REQUIRE(VALID_HTTPDMGR(httpdmgr));
1318 
1319 	if (url == NULL) {
1320 		httpdmgr->render_404 = func;
1321 		return (ISC_R_SUCCESS);
1322 	}
1323 
1324 	item = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpdurl_t));
1325 
1326 	item->url = isc_mem_strdup(httpdmgr->mctx, url);
1327 
1328 	item->action = func;
1329 	item->action_arg = arg;
1330 	item->isstatic = isstatic;
1331 	isc_time_now(&item->loadtime);
1332 
1333 	ISC_LINK_INIT(item, link);
1334 
1335 	LOCK(&httpdmgr->lock);
1336 	ISC_LIST_APPEND(httpdmgr->urls, item, link);
1337 	UNLOCK(&httpdmgr->lock);
1338 
1339 	return (ISC_R_SUCCESS);
1340 }
1341 
1342 void
isc_httpd_setfinishhook(void (* fn)(void))1343 isc_httpd_setfinishhook(void (*fn)(void)) {
1344 #if ENABLE_AFL
1345 	finishhook = fn;
1346 #else  /* ENABLE_AFL */
1347 	UNUSED(fn);
1348 #endif /* ENABLE_AFL */
1349 }
1350