PageRenderTime 92ms CodeModel.GetById 20ms app.highlight 65ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/bind9/lib/isc/httpd.c

https://bitbucket.org/freebsd/freebsd-head/
C | 1028 lines | 688 code | 166 blank | 174 comment | 162 complexity | 3a11c6d84fbf32ffc2c9d0ce6c4079b7 MD5 | raw file
   1/*
   2 * Copyright (C) 2006-2008, 2010-2012  Internet Systems Consortium, Inc. ("ISC")
   3 *
   4 * Permission to use, copy, modify, and/or distribute this software for any
   5 * purpose with or without fee is hereby granted, provided that the above
   6 * copyright notice and this permission notice appear in all copies.
   7 *
   8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
   9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  10 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  14 * PERFORMANCE OF THIS SOFTWARE.
  15 */
  16
  17/* $Id$ */
  18
  19/*! \file */
  20
  21#include <config.h>
  22
  23#include <isc/buffer.h>
  24#include <isc/httpd.h>
  25#include <isc/mem.h>
  26#include <isc/socket.h>
  27#include <isc/string.h>
  28#include <isc/task.h>
  29#include <isc/util.h>
  30
  31#include <string.h>
  32
  33/*%
  34 * TODO:
  35 *
  36 *  o  Put in better checks to make certain things are passed in correctly.
  37 *     This includes a magic number for externally-visible structures,
  38 *     checking for NULL-ness before dereferencing, etc.
  39 *  o  Make the URL processing external functions which will fill-in a buffer
  40 *     structure we provide, or return an error and we will render a generic
  41 *     page and close the client.
  42 */
  43
  44#define MSHUTTINGDOWN(cm) ((cm->flags & ISC_HTTPDMGR_FLAGSHUTTINGDOWN) != 0)
  45#define MSETSHUTTINGDOWN(cm) (cm->flags |= ISC_HTTPDMGR_FLAGSHUTTINGDOWN)
  46
  47#ifdef DEBUG_HTTPD
  48#define ENTER(x) do { fprintf(stderr, "ENTER %s\n", (x)); } while (0)
  49#define EXIT(x) do { fprintf(stderr, "EXIT %s\n", (x)); } while (0)
  50#define NOTICE(x) do { fprintf(stderr, "NOTICE %s\n", (x)); } while (0)
  51#else
  52#define ENTER(x) do { } while(0)
  53#define EXIT(x) do { } while(0)
  54#define NOTICE(x) do { } while(0)
  55#endif
  56
  57#define HTTP_RECVLEN			1024
  58#define HTTP_SENDGROW			1024
  59#define HTTP_SEND_MAXLEN		10240
  60
  61/*%
  62 * HTTP urls.  These are the URLs we manage, and the function to call to
  63 * provide the data for it.  We pass in the base url (so the same function
  64 * can handle multiple requests), and a structure to fill in to return a
  65 * result to the client.  We also pass in a pointer to be filled in for
  66 * the data cleanup function.
  67 */
  68struct isc_httpdurl {
  69	char			       *url;
  70	isc_httpdaction_t	       *action;
  71	void			       *action_arg;
  72	ISC_LINK(isc_httpdurl_t)	link;
  73};
  74
  75#define HTTPD_CLOSE		0x0001 /* Got a Connection: close header */
  76#define HTTPD_FOUNDHOST		0x0002 /* Got a Host: header */
  77
  78/*% http client */
  79struct isc_httpd {
  80	isc_httpdmgr_t	       *mgr;		/*%< our parent */
  81	ISC_LINK(isc_httpd_t)	link;
  82	unsigned int		state;
  83	isc_socket_t		*sock;
  84
  85	/*%
  86	 * Received data state.
  87	 */
  88	char			recvbuf[HTTP_RECVLEN]; /*%< receive buffer */
  89	isc_uint32_t		recvlen;	/*%< length recv'd */
  90	unsigned int		method;
  91	char		       *url;
  92	char		       *querystring;
  93	char		       *protocol;
  94
  95	/*
  96	 * Flags on the httpd client.
  97	 */
  98	int			flags;
  99
 100	/*%
 101	 * Transmit data state.
 102	 *
 103	 * This is the data buffer we will transmit.
 104	 *
 105	 * This free function pointer is filled in by the rendering function
 106	 * we call.  The free function is called after the data is transmitted
 107	 * to the client.
 108	 *
 109	 * The bufflist is the list of buffers we are currently transmitting.
 110	 * The headerdata is where we render our headers to.  If we run out of
 111	 * space when rendering a header, we will change the size of our
 112	 * buffer.  We will not free it until we are finished, and will
 113	 * allocate an additional HTTP_SENDGROW bytes per header space grow.
 114	 *
 115	 * We currently use two buffers total, one for the headers (which
 116	 * we manage) and another for the client to fill in (which it manages,
 117	 * it provides the space for it, etc) -- we will pass that buffer
 118	 * structure back to the caller, who is responsible for managing the
 119	 * space it may have allocated as backing store for it.  This second
 120	 * buffer is bodybuffer, and we only allocate the buffer itself, not
 121	 * the backing store.
 122	 */
 123	isc_bufferlist_t	bufflist;
 124	char		       *headerdata; /*%< send header buf */
 125	unsigned int		headerlen;  /*%< current header buffer size */
 126	isc_buffer_t		headerbuffer;
 127
 128	const char	       *mimetype;
 129	unsigned int		retcode;
 130	const char	       *retmsg;
 131	isc_buffer_t		bodybuffer;
 132	isc_httpdfree_t	       *freecb;
 133	void		       *freecb_arg;
 134};
 135
 136/*% lightweight socket manager for httpd output */
 137struct isc_httpdmgr {
 138	isc_mem_t	       *mctx;
 139	isc_socket_t	       *sock;		/*%< listening socket */
 140	isc_task_t	       *task;		/*%< owning task */
 141	isc_timermgr_t	       *timermgr;
 142
 143	isc_httpdclientok_t    *client_ok;	/*%< client validator */
 144	isc_httpdondestroy_t   *ondestroy;	/*%< cleanup callback */
 145	void		       *cb_arg;		/*%< argument for the above */
 146
 147	unsigned int		flags;
 148	ISC_LIST(isc_httpd_t)	running;	/*%< running clients */
 149
 150	isc_mutex_t		lock;
 151
 152	ISC_LIST(isc_httpdurl_t) urls;		/*%< urls we manage */
 153	isc_httpdaction_t      *render_404;
 154	isc_httpdaction_t      *render_500;
 155};
 156
 157/*%
 158 * HTTP methods.
 159 */
 160#define ISC_HTTPD_METHODUNKNOWN	0
 161#define ISC_HTTPD_METHODGET	1
 162#define ISC_HTTPD_METHODPOST	2
 163
 164/*%
 165 * Client states.
 166 *
 167 * _IDLE	The client is not doing anything at all.  This state should
 168 *		only occur just after creation, and just before being
 169 *		destroyed.
 170 *
 171 * _RECV	The client is waiting for data after issuing a socket recv().
 172 *
 173 * _RECVDONE	Data has been received, and is being processed.
 174 *
 175 * _SEND	All data for a response has completed, and a reply was
 176 *		sent via a socket send() call.
 177 *
 178 * _SENDDONE	Send is completed.
 179 *
 180 * Badly formatted state table:
 181 *
 182 *	IDLE -> RECV when client has a recv() queued.
 183 *
 184 *	RECV -> RECVDONE when recvdone event received.
 185 *
 186 *	RECVDONE -> SEND if the data for a reply is at hand.
 187 *
 188 *	SEND -> RECV when a senddone event was received.
 189 *
 190 *	At any time -> RECV on error.  If RECV fails, the client will
 191 *	self-destroy, closing the socket and freeing memory.
 192 */
 193#define ISC_HTTPD_STATEIDLE	0
 194#define ISC_HTTPD_STATERECV	1
 195#define ISC_HTTPD_STATERECVDONE	2
 196#define ISC_HTTPD_STATESEND	3
 197#define ISC_HTTPD_STATESENDDONE	4
 198
 199#define ISC_HTTPD_ISRECV(c)	((c)->state == ISC_HTTPD_STATERECV)
 200#define ISC_HTTPD_ISRECVDONE(c)	((c)->state == ISC_HTTPD_STATERECVDONE)
 201#define ISC_HTTPD_ISSEND(c)	((c)->state == ISC_HTTPD_STATESEND)
 202#define ISC_HTTPD_ISSENDDONE(c)	((c)->state == ISC_HTTPD_STATESENDDONE)
 203
 204/*%
 205 * Overall magic test that means we're not idle.
 206 */
 207#define ISC_HTTPD_SETRECV(c)	((c)->state = ISC_HTTPD_STATERECV)
 208#define ISC_HTTPD_SETRECVDONE(c)	((c)->state = ISC_HTTPD_STATERECVDONE)
 209#define ISC_HTTPD_SETSEND(c)	((c)->state = ISC_HTTPD_STATESEND)
 210#define ISC_HTTPD_SETSENDDONE(c)	((c)->state = ISC_HTTPD_STATESENDDONE)
 211
 212static void isc_httpd_accept(isc_task_t *, isc_event_t *);
 213static void isc_httpd_recvdone(isc_task_t *, isc_event_t *);
 214static void isc_httpd_senddone(isc_task_t *, isc_event_t *);
 215static void destroy_client(isc_httpd_t **);
 216static isc_result_t process_request(isc_httpd_t *, int);
 217static void httpdmgr_destroy(isc_httpdmgr_t *);
 218static isc_result_t grow_headerspace(isc_httpd_t *);
 219static void reset_client(isc_httpd_t *httpd);
 220static isc_result_t render_404(const char *, const char *,
 221			       void *,
 222			       unsigned int *, const char **,
 223			       const char **, isc_buffer_t *,
 224			       isc_httpdfree_t **, void **);
 225static isc_result_t render_500(const char *, const char *,
 226			       void *,
 227			       unsigned int *, const char **,
 228			       const char **, isc_buffer_t *,
 229			       isc_httpdfree_t **, void **);
 230
 231static void
 232destroy_client(isc_httpd_t **httpdp)
 233{
 234	isc_httpd_t *httpd = *httpdp;
 235	isc_httpdmgr_t *httpdmgr = httpd->mgr;
 236
 237	*httpdp = NULL;
 238
 239	LOCK(&httpdmgr->lock);
 240
 241	isc_socket_detach(&httpd->sock);
 242	ISC_LIST_UNLINK(httpdmgr->running, httpd, link);
 243
 244	if (httpd->headerlen > 0)
 245		isc_mem_put(httpdmgr->mctx, httpd->headerdata,
 246			    httpd->headerlen);
 247
 248	isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
 249
 250	UNLOCK(&httpdmgr->lock);
 251
 252	httpdmgr_destroy(httpdmgr);
 253}
 254
 255isc_result_t
 256isc_httpdmgr_create(isc_mem_t *mctx, isc_socket_t *sock, isc_task_t *task,
 257		    isc_httpdclientok_t *client_ok,
 258		    isc_httpdondestroy_t *ondestroy, void *cb_arg,
 259		    isc_timermgr_t *tmgr, isc_httpdmgr_t **httpdp)
 260{
 261	isc_result_t result;
 262	isc_httpdmgr_t *httpd;
 263
 264	REQUIRE(mctx != NULL);
 265	REQUIRE(sock != NULL);
 266	REQUIRE(task != NULL);
 267	REQUIRE(tmgr != NULL);
 268	REQUIRE(httpdp != NULL && *httpdp == NULL);
 269
 270	httpd = isc_mem_get(mctx, sizeof(isc_httpdmgr_t));
 271	if (httpd == NULL)
 272		return (ISC_R_NOMEMORY);
 273
 274	result = isc_mutex_init(&httpd->lock);
 275	if (result != ISC_R_SUCCESS) {
 276		isc_mem_put(mctx, httpd, sizeof(isc_httpdmgr_t));
 277		return (result);
 278	}
 279	httpd->mctx = NULL;
 280	isc_mem_attach(mctx, &httpd->mctx);
 281	httpd->sock = NULL;
 282	isc_socket_attach(sock, &httpd->sock);
 283	httpd->task = NULL;
 284	isc_task_attach(task, &httpd->task);
 285	httpd->timermgr = tmgr; /* XXXMLG no attach function? */
 286	httpd->client_ok = client_ok;
 287	httpd->ondestroy = ondestroy;
 288	httpd->cb_arg = cb_arg;
 289
 290	ISC_LIST_INIT(httpd->running);
 291	ISC_LIST_INIT(httpd->urls);
 292
 293	/* XXXMLG ignore errors on isc_socket_listen() */
 294	result = isc_socket_listen(sock, SOMAXCONN);
 295	if (result != ISC_R_SUCCESS) {
 296		UNEXPECTED_ERROR(__FILE__, __LINE__,
 297				 "isc_socket_listen() failed: %s",
 298				 isc_result_totext(result));
 299		goto cleanup;
 300	}
 301
 302	(void)isc_socket_filter(sock, "httpready");
 303
 304	result = isc_socket_accept(sock, task, isc_httpd_accept, httpd);
 305	if (result != ISC_R_SUCCESS)
 306		goto cleanup;
 307
 308	httpd->render_404 = render_404;
 309	httpd->render_500 = render_500;
 310
 311	*httpdp = httpd;
 312	return (ISC_R_SUCCESS);
 313
 314  cleanup:
 315	isc_task_detach(&httpd->task);
 316	isc_socket_detach(&httpd->sock);
 317	isc_mem_detach(&httpd->mctx);
 318	(void)isc_mutex_destroy(&httpd->lock);
 319	isc_mem_put(mctx, httpd, sizeof(isc_httpdmgr_t));
 320	return (result);
 321}
 322
 323static void
 324httpdmgr_destroy(isc_httpdmgr_t *httpdmgr)
 325{
 326	isc_mem_t *mctx;
 327	isc_httpdurl_t *url;
 328
 329	ENTER("httpdmgr_destroy");
 330
 331	LOCK(&httpdmgr->lock);
 332
 333	if (!MSHUTTINGDOWN(httpdmgr)) {
 334		NOTICE("httpdmgr_destroy not shutting down yet");
 335		UNLOCK(&httpdmgr->lock);
 336		return;
 337	}
 338
 339	/*
 340	 * If all clients are not shut down, don't do anything yet.
 341	 */
 342	if (!ISC_LIST_EMPTY(httpdmgr->running)) {
 343		NOTICE("httpdmgr_destroy clients still active");
 344		UNLOCK(&httpdmgr->lock);
 345		return;
 346	}
 347
 348	NOTICE("httpdmgr_destroy detaching socket, task, and timermgr");
 349
 350	isc_socket_detach(&httpdmgr->sock);
 351	isc_task_detach(&httpdmgr->task);
 352	httpdmgr->timermgr = NULL;
 353
 354	/*
 355	 * Clear out the list of all actions we know about.  Just free the
 356	 * memory.
 357	 */
 358	url = ISC_LIST_HEAD(httpdmgr->urls);
 359	while (url != NULL) {
 360		isc_mem_free(httpdmgr->mctx, url->url);
 361		ISC_LIST_UNLINK(httpdmgr->urls, url, link);
 362		isc_mem_put(httpdmgr->mctx, url, sizeof(isc_httpdurl_t));
 363		url = ISC_LIST_HEAD(httpdmgr->urls);
 364	}
 365
 366	UNLOCK(&httpdmgr->lock);
 367	(void)isc_mutex_destroy(&httpdmgr->lock);
 368
 369	if (httpdmgr->ondestroy != NULL)
 370		(httpdmgr->ondestroy)(httpdmgr->cb_arg);
 371
 372	mctx = httpdmgr->mctx;
 373	isc_mem_putanddetach(&mctx, httpdmgr, sizeof(isc_httpdmgr_t));
 374
 375	EXIT("httpdmgr_destroy");
 376}
 377
 378#define LENGTHOK(s) (httpd->recvbuf - (s) < (int)httpd->recvlen)
 379#define BUFLENOK(s) (httpd->recvbuf - (s) < HTTP_RECVLEN)
 380
 381static isc_result_t
 382process_request(isc_httpd_t *httpd, int length)
 383{
 384	char *s;
 385	char *p;
 386	int delim;
 387
 388	ENTER("request");
 389
 390	httpd->recvlen += length;
 391
 392	httpd->recvbuf[httpd->recvlen] = 0;
 393
 394	/*
 395	 * If we don't find a blank line in our buffer, return that we need
 396	 * more data.
 397	 */
 398	s = strstr(httpd->recvbuf, "\r\n\r\n");
 399	delim = 1;
 400	if (s == NULL) {
 401		s = strstr(httpd->recvbuf, "\n\n");
 402		delim = 2;
 403	}
 404	if (s == NULL)
 405		return (ISC_R_NOTFOUND);
 406
 407	/*
 408	 * Determine if this is a POST or GET method.  Any other values will
 409	 * cause an error to be returned.
 410	 */
 411	if (strncmp(httpd->recvbuf, "GET ", 4) == 0) {
 412		httpd->method = ISC_HTTPD_METHODGET;
 413		p = httpd->recvbuf + 4;
 414	} else if (strncmp(httpd->recvbuf, "POST ", 5) == 0) {
 415		httpd->method = ISC_HTTPD_METHODPOST;
 416		p = httpd->recvbuf + 5;
 417	} else {
 418		return (ISC_R_RANGE);
 419	}
 420
 421	/*
 422	 * From now on, p is the start of our buffer.
 423	 */
 424
 425	/*
 426	 * Extract the URL.
 427	 */
 428	s = p;
 429	while (LENGTHOK(s) && BUFLENOK(s) &&
 430	       (*s != '\n' && *s != '\r' && *s != '\0' && *s != ' '))
 431		s++;
 432	if (!LENGTHOK(s))
 433		return (ISC_R_NOTFOUND);
 434	if (!BUFLENOK(s))
 435		return (ISC_R_NOMEMORY);
 436	*s = 0;
 437
 438	/*
 439	 * Make the URL relative.
 440	 */
 441	if ((strncmp(p, "http:/", 6) == 0)
 442	    || (strncmp(p, "https:/", 7) == 0)) {
 443		/* Skip first / */
 444		while (*p != '/' && *p != 0)
 445			p++;
 446		if (*p == 0)
 447			return (ISC_R_RANGE);
 448		p++;
 449		/* Skip second / */
 450		while (*p != '/' && *p != 0)
 451			p++;
 452		if (*p == 0)
 453			return (ISC_R_RANGE);
 454		p++;
 455		/* Find third / */
 456		while (*p != '/' && *p != 0)
 457			p++;
 458		if (*p == 0) {
 459			p--;
 460			*p = '/';
 461		}
 462	}
 463
 464	httpd->url = p;
 465	p = s + delim;
 466	s = p;
 467
 468	/*
 469	 * Now, see if there is a ? mark in the URL.  If so, this is
 470	 * part of the query string, and we will split it from the URL.
 471	 */
 472	httpd->querystring = strchr(httpd->url, '?');
 473	if (httpd->querystring != NULL) {
 474		*(httpd->querystring) = 0;
 475		httpd->querystring++;
 476	}
 477
 478	/*
 479	 * Extract the HTTP/1.X protocol.  We will bounce on anything but
 480	 * HTTP/1.1 for now.
 481	 */
 482	while (LENGTHOK(s) && BUFLENOK(s) &&
 483	       (*s != '\n' && *s != '\r' && *s != '\0'))
 484		s++;
 485	if (!LENGTHOK(s))
 486		return (ISC_R_NOTFOUND);
 487	if (!BUFLENOK(s))
 488		return (ISC_R_NOMEMORY);
 489	*s = 0;
 490	if ((strncmp(p, "HTTP/1.0", 8) != 0)
 491	    && (strncmp(p, "HTTP/1.1", 8) != 0))
 492		return (ISC_R_RANGE);
 493	httpd->protocol = p;
 494	p = s + 1;
 495	s = p;
 496
 497	if (strstr(s, "Connection: close") != NULL)
 498		httpd->flags |= HTTPD_CLOSE;
 499
 500	if (strstr(s, "Host: ") != NULL)
 501		httpd->flags |= HTTPD_FOUNDHOST;
 502
 503	/*
 504	 * Standards compliance hooks here.
 505	 */
 506	if (strcmp(httpd->protocol, "HTTP/1.1") == 0
 507	    && ((httpd->flags & HTTPD_FOUNDHOST) == 0))
 508		return (ISC_R_RANGE);
 509
 510	EXIT("request");
 511
 512	return (ISC_R_SUCCESS);
 513}
 514
 515static void
 516isc_httpd_accept(isc_task_t *task, isc_event_t *ev)
 517{
 518	isc_result_t result;
 519	isc_httpdmgr_t *httpdmgr = ev->ev_arg;
 520	isc_httpd_t *httpd;
 521	isc_region_t r;
 522	isc_socket_newconnev_t *nev = (isc_socket_newconnev_t *)ev;
 523	isc_sockaddr_t peeraddr;
 524
 525	ENTER("accept");
 526
 527	LOCK(&httpdmgr->lock);
 528	if (MSHUTTINGDOWN(httpdmgr)) {
 529		NOTICE("accept shutting down, goto out");
 530		goto out;
 531	}
 532
 533	if (nev->result == ISC_R_CANCELED) {
 534		NOTICE("accept canceled, goto out");
 535		goto out;
 536	}
 537
 538	if (nev->result != ISC_R_SUCCESS) {
 539		/* XXXMLG log failure */
 540		NOTICE("accept returned failure, goto requeue");
 541		goto requeue;
 542	}
 543
 544	(void)isc_socket_getpeername(nev->newsocket, &peeraddr);
 545	if (httpdmgr->client_ok != NULL &&
 546	    !(httpdmgr->client_ok)(&peeraddr, httpdmgr->cb_arg)) {
 547		isc_socket_detach(&nev->newsocket);
 548		goto requeue;
 549	}
 550
 551	httpd = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpd_t));
 552	if (httpd == NULL) {
 553		/* XXXMLG log failure */
 554		NOTICE("accept failed to allocate memory, goto requeue");
 555		isc_socket_detach(&nev->newsocket);
 556		goto requeue;
 557	}
 558
 559	httpd->mgr = httpdmgr;
 560	ISC_LINK_INIT(httpd, link);
 561	ISC_LIST_APPEND(httpdmgr->running, httpd, link);
 562	ISC_HTTPD_SETRECV(httpd);
 563	httpd->sock = nev->newsocket;
 564	isc_socket_setname(httpd->sock, "httpd", NULL);
 565	httpd->flags = 0;
 566
 567	/*
 568	 * Initialize the buffer for our headers.
 569	 */
 570	httpd->headerdata = isc_mem_get(httpdmgr->mctx, HTTP_SENDGROW);
 571	if (httpd->headerdata == NULL) {
 572		isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
 573		isc_socket_detach(&nev->newsocket);
 574		goto requeue;
 575	}
 576	httpd->headerlen = HTTP_SENDGROW;
 577	isc_buffer_init(&httpd->headerbuffer, httpd->headerdata,
 578			httpd->headerlen);
 579
 580	ISC_LIST_INIT(httpd->bufflist);
 581
 582	isc_buffer_initnull(&httpd->bodybuffer);
 583	reset_client(httpd);
 584
 585	r.base = (unsigned char *)httpd->recvbuf;
 586	r.length = HTTP_RECVLEN - 1;
 587	result = isc_socket_recv(httpd->sock, &r, 1, task, isc_httpd_recvdone,
 588				 httpd);
 589	/* FIXME!!! */
 590	POST(result);
 591	NOTICE("accept queued recv on socket");
 592
 593 requeue:
 594	result = isc_socket_accept(httpdmgr->sock, task, isc_httpd_accept,
 595				   httpdmgr);
 596	if (result != ISC_R_SUCCESS) {
 597		/* XXXMLG what to do?  Log failure... */
 598		NOTICE("accept could not reaccept due to failure");
 599	}
 600
 601 out:
 602	UNLOCK(&httpdmgr->lock);
 603
 604	httpdmgr_destroy(httpdmgr);
 605
 606	isc_event_free(&ev);
 607
 608	EXIT("accept");
 609}
 610
 611static isc_result_t
 612render_404(const char *url, const char *querystring,
 613	   void *arg,
 614	   unsigned int *retcode, const char **retmsg,
 615	   const char **mimetype, isc_buffer_t *b,
 616	   isc_httpdfree_t **freecb, void **freecb_args)
 617{
 618	static char msg[] = "No such URL.";
 619
 620	UNUSED(url);
 621	UNUSED(querystring);
 622	UNUSED(arg);
 623
 624	*retcode = 404;
 625	*retmsg = "No such URL";
 626	*mimetype = "text/plain";
 627	isc_buffer_reinit(b, msg, strlen(msg));
 628	isc_buffer_add(b, strlen(msg));
 629	*freecb = NULL;
 630	*freecb_args = NULL;
 631
 632	return (ISC_R_SUCCESS);
 633}
 634
 635static isc_result_t
 636render_500(const char *url, const char *querystring,
 637	   void *arg,
 638	   unsigned int *retcode, const char **retmsg,
 639	   const char **mimetype, isc_buffer_t *b,
 640	   isc_httpdfree_t **freecb, void **freecb_args)
 641{
 642	static char msg[] = "Internal server failure.";
 643
 644	UNUSED(url);
 645	UNUSED(querystring);
 646	UNUSED(arg);
 647
 648	*retcode = 500;
 649	*retmsg = "Internal server failure";
 650	*mimetype = "text/plain";
 651	isc_buffer_reinit(b, msg, strlen(msg));
 652	isc_buffer_add(b, strlen(msg));
 653	*freecb = NULL;
 654	*freecb_args = NULL;
 655
 656	return (ISC_R_SUCCESS);
 657}
 658
 659static void
 660isc_httpd_recvdone(isc_task_t *task, isc_event_t *ev)
 661{
 662	isc_region_t r;
 663	isc_result_t result;
 664	isc_httpd_t *httpd = ev->ev_arg;
 665	isc_socketevent_t *sev = (isc_socketevent_t *)ev;
 666	isc_httpdurl_t *url;
 667	isc_time_t now;
 668	char datebuf[32];  /* Only need 30, but safety first */
 669
 670	ENTER("recv");
 671
 672	INSIST(ISC_HTTPD_ISRECV(httpd));
 673
 674	if (sev->result != ISC_R_SUCCESS) {
 675		NOTICE("recv destroying client");
 676		destroy_client(&httpd);
 677		goto out;
 678	}
 679
 680	result = process_request(httpd, sev->n);
 681	if (result == ISC_R_NOTFOUND) {
 682		if (httpd->recvlen >= HTTP_RECVLEN - 1) {
 683			destroy_client(&httpd);
 684			goto out;
 685		}
 686		r.base = (unsigned char *)httpd->recvbuf + httpd->recvlen;
 687		r.length = HTTP_RECVLEN - httpd->recvlen - 1;
 688		/* check return code? */
 689		(void)isc_socket_recv(httpd->sock, &r, 1, task,
 690				      isc_httpd_recvdone, httpd);
 691		goto out;
 692	} else if (result != ISC_R_SUCCESS) {
 693		destroy_client(&httpd);
 694		goto out;
 695	}
 696
 697	ISC_HTTPD_SETSEND(httpd);
 698
 699	/*
 700	 * XXXMLG Call function here.  Provide an add-header function
 701	 * which will append the common headers to a response we generate.
 702	 */
 703	isc_buffer_initnull(&httpd->bodybuffer);
 704	isc_time_now(&now);
 705	isc_time_formathttptimestamp(&now, datebuf, sizeof(datebuf));
 706	url = ISC_LIST_HEAD(httpd->mgr->urls);
 707	while (url != NULL) {
 708		if (strcmp(httpd->url, url->url) == 0)
 709			break;
 710		url = ISC_LIST_NEXT(url, link);
 711	}
 712	if (url == NULL)
 713		result = httpd->mgr->render_404(httpd->url, httpd->querystring,
 714						NULL,
 715						&httpd->retcode,
 716						&httpd->retmsg,
 717						&httpd->mimetype,
 718						&httpd->bodybuffer,
 719						&httpd->freecb,
 720						&httpd->freecb_arg);
 721	else
 722		result = url->action(httpd->url, httpd->querystring,
 723				     url->action_arg,
 724				     &httpd->retcode, &httpd->retmsg,
 725				     &httpd->mimetype, &httpd->bodybuffer,
 726				     &httpd->freecb, &httpd->freecb_arg);
 727	if (result != ISC_R_SUCCESS) {
 728		result = httpd->mgr->render_500(httpd->url, httpd->querystring,
 729						NULL, &httpd->retcode,
 730						&httpd->retmsg,
 731						&httpd->mimetype,
 732						&httpd->bodybuffer,
 733						&httpd->freecb,
 734						&httpd->freecb_arg);
 735		RUNTIME_CHECK(result == ISC_R_SUCCESS);
 736	}
 737
 738	isc_httpd_response(httpd);
 739	isc_httpd_addheader(httpd, "Content-Type", httpd->mimetype);
 740	isc_httpd_addheader(httpd, "Date", datebuf);
 741	isc_httpd_addheader(httpd, "Expires", datebuf);
 742	isc_httpd_addheader(httpd, "Last-Modified", datebuf);
 743	isc_httpd_addheader(httpd, "Pragma: no-cache", NULL);
 744	isc_httpd_addheader(httpd, "Cache-Control: no-cache", NULL);
 745	isc_httpd_addheader(httpd, "Server: libisc", NULL);
 746	isc_httpd_addheaderuint(httpd, "Content-Length",
 747				isc_buffer_usedlength(&httpd->bodybuffer));
 748	isc_httpd_endheaders(httpd);  /* done */
 749
 750	ISC_LIST_APPEND(httpd->bufflist, &httpd->headerbuffer, link);
 751	/*
 752	 * Link the data buffer into our send queue, should we have any data
 753	 * rendered into it.  If no data is present, we won't do anything
 754	 * with the buffer.
 755	 */
 756	if (isc_buffer_length(&httpd->bodybuffer) > 0)
 757		ISC_LIST_APPEND(httpd->bufflist, &httpd->bodybuffer, link);
 758
 759	/* check return code? */
 760	(void)isc_socket_sendv(httpd->sock, &httpd->bufflist, task,
 761			       isc_httpd_senddone, httpd);
 762
 763 out:
 764	isc_event_free(&ev);
 765	EXIT("recv");
 766}
 767
 768void
 769isc_httpdmgr_shutdown(isc_httpdmgr_t **httpdmgrp)
 770{
 771	isc_httpdmgr_t *httpdmgr;
 772	isc_httpd_t *httpd;
 773	httpdmgr = *httpdmgrp;
 774	*httpdmgrp = NULL;
 775
 776	ENTER("isc_httpdmgr_shutdown");
 777
 778	LOCK(&httpdmgr->lock);
 779
 780	MSETSHUTTINGDOWN(httpdmgr);
 781
 782	isc_socket_cancel(httpdmgr->sock, httpdmgr->task, ISC_SOCKCANCEL_ALL);
 783
 784	httpd = ISC_LIST_HEAD(httpdmgr->running);
 785	while (httpd != NULL) {
 786		isc_socket_cancel(httpd->sock, httpdmgr->task,
 787				  ISC_SOCKCANCEL_ALL);
 788		httpd = ISC_LIST_NEXT(httpd, link);
 789	}
 790
 791	UNLOCK(&httpdmgr->lock);
 792
 793	EXIT("isc_httpdmgr_shutdown");
 794}
 795
 796static isc_result_t
 797grow_headerspace(isc_httpd_t *httpd)
 798{
 799	char *newspace;
 800	unsigned int newlen;
 801	isc_region_t r;
 802
 803	newlen = httpd->headerlen + HTTP_SENDGROW;
 804	if (newlen > HTTP_SEND_MAXLEN)
 805		return (ISC_R_NOSPACE);
 806
 807	newspace = isc_mem_get(httpd->mgr->mctx, newlen);
 808	if (newspace == NULL)
 809		return (ISC_R_NOMEMORY);
 810	isc_buffer_region(&httpd->headerbuffer, &r);
 811	isc_buffer_reinit(&httpd->headerbuffer, newspace, newlen);
 812
 813	isc_mem_put(httpd->mgr->mctx, r.base, r.length);
 814
 815	return (ISC_R_SUCCESS);
 816}
 817
 818isc_result_t
 819isc_httpd_response(isc_httpd_t *httpd)
 820{
 821	isc_result_t result;
 822	unsigned int needlen;
 823
 824	needlen = strlen(httpd->protocol) + 1; /* protocol + space */
 825	needlen += 3 + 1;  /* room for response code, always 3 bytes */
 826	needlen += strlen(httpd->retmsg) + 2;  /* return msg + CRLF */
 827
 828	while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
 829		result = grow_headerspace(httpd);
 830		if (result != ISC_R_SUCCESS)
 831			return (result);
 832	}
 833
 834	sprintf(isc_buffer_used(&httpd->headerbuffer), "%s %03d %s\r\n",
 835		httpd->protocol, httpd->retcode, httpd->retmsg);
 836	isc_buffer_add(&httpd->headerbuffer, needlen);
 837
 838	return (ISC_R_SUCCESS);
 839}
 840
 841isc_result_t
 842isc_httpd_addheader(isc_httpd_t *httpd, const char *name,
 843		    const char *val)
 844{
 845	isc_result_t result;
 846	unsigned int needlen;
 847
 848	needlen = strlen(name); /* name itself */
 849	if (val != NULL)
 850		needlen += 2 + strlen(val); /* :<space> and val */
 851	needlen += 2; /* CRLF */
 852
 853	while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
 854		result = grow_headerspace(httpd);
 855		if (result != ISC_R_SUCCESS)
 856			return (result);
 857	}
 858
 859	if (val != NULL)
 860		sprintf(isc_buffer_used(&httpd->headerbuffer),
 861			"%s: %s\r\n", name, val);
 862	else
 863		sprintf(isc_buffer_used(&httpd->headerbuffer),
 864			"%s\r\n", name);
 865
 866	isc_buffer_add(&httpd->headerbuffer, needlen);
 867
 868	return (ISC_R_SUCCESS);
 869}
 870
 871isc_result_t
 872isc_httpd_endheaders(isc_httpd_t *httpd)
 873{
 874	isc_result_t result;
 875
 876	while (isc_buffer_availablelength(&httpd->headerbuffer) < 2) {
 877		result = grow_headerspace(httpd);
 878		if (result != ISC_R_SUCCESS)
 879			return (result);
 880	}
 881
 882	sprintf(isc_buffer_used(&httpd->headerbuffer), "\r\n");
 883	isc_buffer_add(&httpd->headerbuffer, 2);
 884
 885	return (ISC_R_SUCCESS);
 886}
 887
 888isc_result_t
 889isc_httpd_addheaderuint(isc_httpd_t *httpd, const char *name, int val) {
 890	isc_result_t result;
 891	unsigned int needlen;
 892	char buf[sizeof "18446744073709551616"];
 893
 894	sprintf(buf, "%d", val);
 895
 896	needlen = strlen(name); /* name itself */
 897	needlen += 2 + strlen(buf); /* :<space> and val */
 898	needlen += 2; /* CRLF */
 899
 900	while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
 901		result = grow_headerspace(httpd);
 902		if (result != ISC_R_SUCCESS)
 903			return (result);
 904	}
 905
 906	sprintf(isc_buffer_used(&httpd->headerbuffer),
 907		"%s: %s\r\n", name, buf);
 908
 909	isc_buffer_add(&httpd->headerbuffer, needlen);
 910
 911	return (ISC_R_SUCCESS);
 912}
 913
 914static void
 915isc_httpd_senddone(isc_task_t *task, isc_event_t *ev)
 916{
 917	isc_httpd_t *httpd = ev->ev_arg;
 918	isc_region_t r;
 919	isc_socketevent_t *sev = (isc_socketevent_t *)ev;
 920
 921	ENTER("senddone");
 922	INSIST(ISC_HTTPD_ISSEND(httpd));
 923
 924	/*
 925	 * First, unlink our header buffer from the socket's bufflist.  This
 926	 * is sort of an evil hack, since we know our buffer will be there,
 927	 * and we know it's address, so we can just remove it directly.
 928	 */
 929	NOTICE("senddone unlinked header");
 930	ISC_LIST_UNLINK(sev->bufferlist, &httpd->headerbuffer, link);
 931
 932	/*
 933	 * We will always want to clean up our receive buffer, even if we
 934	 * got an error on send or we are shutting down.
 935	 *
 936	 * We will pass in the buffer only if there is data in it.  If
 937	 * there is no data, we will pass in a NULL.
 938	 */
 939	if (httpd->freecb != NULL) {
 940		isc_buffer_t *b = NULL;
 941		if (isc_buffer_length(&httpd->bodybuffer) > 0)
 942			b = &httpd->bodybuffer;
 943		httpd->freecb(b, httpd->freecb_arg);
 944		NOTICE("senddone free callback performed");
 945	}
 946	if (ISC_LINK_LINKED(&httpd->bodybuffer, link)) {
 947		ISC_LIST_UNLINK(sev->bufferlist, &httpd->bodybuffer, link);
 948		NOTICE("senddone body buffer unlinked");
 949	}
 950
 951	if (sev->result != ISC_R_SUCCESS) {
 952		destroy_client(&httpd);
 953		goto out;
 954	}
 955
 956	if ((httpd->flags & HTTPD_CLOSE) != 0) {
 957		destroy_client(&httpd);
 958		goto out;
 959	}
 960
 961	ISC_HTTPD_SETRECV(httpd);
 962
 963	NOTICE("senddone restarting recv on socket");
 964
 965	reset_client(httpd);
 966
 967	r.base = (unsigned char *)httpd->recvbuf;
 968	r.length = HTTP_RECVLEN - 1;
 969	/* check return code? */
 970	(void)isc_socket_recv(httpd->sock, &r, 1, task,
 971			      isc_httpd_recvdone, httpd);
 972
 973out:
 974	isc_event_free(&ev);
 975	EXIT("senddone");
 976}
 977
 978static void
 979reset_client(isc_httpd_t *httpd)
 980{
 981	/*
 982	 * Catch errors here.  We MUST be in RECV mode, and we MUST NOT have
 983	 * any outstanding buffers.  If we have buffers, we have a leak.
 984	 */
 985	INSIST(ISC_HTTPD_ISRECV(httpd));
 986	INSIST(!ISC_LINK_LINKED(&httpd->headerbuffer, link));
 987	INSIST(!ISC_LINK_LINKED(&httpd->bodybuffer, link));
 988
 989	httpd->recvbuf[0] = 0;
 990	httpd->recvlen = 0;
 991	httpd->method = ISC_HTTPD_METHODUNKNOWN;
 992	httpd->url = NULL;
 993	httpd->querystring = NULL;
 994	httpd->protocol = NULL;
 995	httpd->flags = 0;
 996
 997	isc_buffer_clear(&httpd->headerbuffer);
 998	isc_buffer_invalidate(&httpd->bodybuffer);
 999}
1000
1001isc_result_t
1002isc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url,
1003		    isc_httpdaction_t *func, void *arg)
1004{
1005	isc_httpdurl_t *item;
1006
1007	if (url == NULL) {
1008		httpdmgr->render_404 = func;
1009		return (ISC_R_SUCCESS);
1010	}
1011
1012	item = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpdurl_t));
1013	if (item == NULL)
1014		return (ISC_R_NOMEMORY);
1015
1016	item->url = isc_mem_strdup(httpdmgr->mctx, url);
1017	if (item->url == NULL) {
1018		isc_mem_put(httpdmgr->mctx, item, sizeof(isc_httpdurl_t));
1019		return (ISC_R_NOMEMORY);
1020	}
1021
1022	item->action = func;
1023	item->action_arg = arg;
1024	ISC_LINK_INIT(item, link);
1025	ISC_LIST_APPEND(httpdmgr->urls, item, link);
1026
1027	return (ISC_R_SUCCESS);
1028}