File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / ntp / lib / isc / httpd.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue May 29 12:08:38 2012 UTC (12 years, 7 months ago) by misho
Branches: ntp, MAIN
CVS tags: v4_2_6p5p0, v4_2_6p5, HEAD
ntp 4.2.6p5

    1: /*
    2:  * Copyright (C) 2006-2008  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: httpd.c,v 1.1.1.1 2012/05/29 12:08:38 misho Exp $ */
   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:  */
   68: struct 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 */
   79: struct 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 */
  137: struct 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: };
  155: 
  156: /*%
  157:  * HTTP methods.
  158:  */
  159: #define ISC_HTTPD_METHODUNKNOWN	0
  160: #define ISC_HTTPD_METHODGET	1
  161: #define ISC_HTTPD_METHODPOST	2
  162: 
  163: /*%
  164:  * Client states.
  165:  *
  166:  * _IDLE	The client is not doing anything at all.  This state should
  167:  *		only occur just after creation, and just before being
  168:  *		destroyed.
  169:  *
  170:  * _RECV	The client is waiting for data after issuing a socket recv().
  171:  *
  172:  * _RECVDONE	Data has been received, and is being processed.
  173:  *
  174:  * _SEND	All data for a response has completed, and a reply was
  175:  *		sent via a socket send() call.
  176:  *
  177:  * _SENDDONE	Send is completed.
  178:  *
  179:  * Badly formatted state table:
  180:  *
  181:  *	IDLE -> RECV when client has a recv() queued.
  182:  *
  183:  *	RECV -> RECVDONE when recvdone event received.
  184:  *
  185:  *	RECVDONE -> SEND if the data for a reply is at hand.
  186:  *
  187:  *	SEND -> RECV when a senddone event was received.
  188:  *
  189:  *	At any time -> RECV on error.  If RECV fails, the client will
  190:  *	self-destroy, closing the socket and freeing memory.
  191:  */
  192: #define ISC_HTTPD_STATEIDLE	0
  193: #define ISC_HTTPD_STATERECV	1
  194: #define ISC_HTTPD_STATERECVDONE	2
  195: #define ISC_HTTPD_STATESEND	3
  196: #define ISC_HTTPD_STATESENDDONE	4
  197: 
  198: #define ISC_HTTPD_ISRECV(c)	((c)->state == ISC_HTTPD_STATERECV)
  199: #define ISC_HTTPD_ISRECVDONE(c)	((c)->state == ISC_HTTPD_STATERECVDONE)
  200: #define ISC_HTTPD_ISSEND(c)	((c)->state == ISC_HTTPD_STATESEND)
  201: #define ISC_HTTPD_ISSENDDONE(c)	((c)->state == ISC_HTTPD_STATESENDDONE)
  202: 
  203: /*%
  204:  * Overall magic test that means we're not idle.
  205:  */
  206: #define ISC_HTTPD_SETRECV(c)	((c)->state = ISC_HTTPD_STATERECV)
  207: #define ISC_HTTPD_SETRECVDONE(c)	((c)->state = ISC_HTTPD_STATERECVDONE)
  208: #define ISC_HTTPD_SETSEND(c)	((c)->state = ISC_HTTPD_STATESEND)
  209: #define ISC_HTTPD_SETSENDDONE(c)	((c)->state = ISC_HTTPD_STATESENDDONE)
  210: 
  211: static void isc_httpd_accept(isc_task_t *, isc_event_t *);
  212: static void isc_httpd_recvdone(isc_task_t *, isc_event_t *);
  213: static void isc_httpd_senddone(isc_task_t *, isc_event_t *);
  214: static void destroy_client(isc_httpd_t **);
  215: static isc_result_t process_request(isc_httpd_t *, int);
  216: static void httpdmgr_destroy(isc_httpdmgr_t *);
  217: static isc_result_t grow_headerspace(isc_httpd_t *);
  218: static void reset_client(isc_httpd_t *httpd);
  219: static isc_result_t render_404(const char *, const char *,
  220: 			       void *,
  221: 			       unsigned int *, const char **,
  222: 			       const char **, isc_buffer_t *,
  223: 			       isc_httpdfree_t **, void **);
  224: 
  225: static void
  226: destroy_client(isc_httpd_t **httpdp)
  227: {
  228: 	isc_httpd_t *httpd = *httpdp;
  229: 	isc_httpdmgr_t *httpdmgr = httpd->mgr;
  230: 
  231: 	*httpdp = NULL;
  232: 
  233: 	LOCK(&httpdmgr->lock);
  234: 
  235: 	isc_socket_detach(&httpd->sock);
  236: 	ISC_LIST_UNLINK(httpdmgr->running, httpd, link);
  237: 
  238: 	if (httpd->headerlen > 0)
  239: 		isc_mem_put(httpdmgr->mctx, httpd->headerdata,
  240: 			    httpd->headerlen);
  241: 
  242: 	isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
  243: 
  244: 	UNLOCK(&httpdmgr->lock);
  245: 
  246: 	httpdmgr_destroy(httpdmgr);
  247: }
  248: 
  249: isc_result_t
  250: isc_httpdmgr_create(isc_mem_t *mctx, isc_socket_t *sock, isc_task_t *task,
  251: 		    isc_httpdclientok_t *client_ok,
  252: 		    isc_httpdondestroy_t *ondestroy, void *cb_arg,
  253: 		    isc_timermgr_t *tmgr, isc_httpdmgr_t **httpdp)
  254: {
  255: 	isc_result_t result;
  256: 	isc_httpdmgr_t *httpd;
  257: 
  258: 	REQUIRE(mctx != NULL);
  259: 	REQUIRE(sock != NULL);
  260: 	REQUIRE(task != NULL);
  261: 	REQUIRE(tmgr != NULL);
  262: 	REQUIRE(httpdp != NULL && *httpdp == NULL);
  263: 
  264: 	httpd = isc_mem_get(mctx, sizeof(isc_httpdmgr_t));
  265: 	if (httpd == NULL)
  266: 		return (ISC_R_NOMEMORY);
  267: 
  268: 	result = isc_mutex_init(&httpd->lock);
  269: 	if (result != ISC_R_SUCCESS) {
  270: 		isc_mem_put(mctx, httpd, sizeof(isc_httpdmgr_t));
  271: 		return (result);
  272: 	}
  273: 	httpd->mctx = NULL;
  274: 	isc_mem_attach(mctx, &httpd->mctx);
  275: 	httpd->sock = NULL;
  276: 	isc_socket_attach(sock, &httpd->sock);
  277: 	httpd->task = NULL;
  278: 	isc_task_attach(task, &httpd->task);
  279: 	httpd->timermgr = tmgr; /* XXXMLG no attach function? */
  280: 	httpd->client_ok = client_ok;
  281: 	httpd->ondestroy = ondestroy;
  282: 	httpd->cb_arg = cb_arg;
  283: 
  284: 	ISC_LIST_INIT(httpd->running);
  285: 	ISC_LIST_INIT(httpd->urls);
  286: 
  287: 	/* XXXMLG ignore errors on isc_socket_listen() */
  288: 	result = isc_socket_listen(sock, SOMAXCONN);
  289: 	if (result != ISC_R_SUCCESS) {
  290: 		UNEXPECTED_ERROR(__FILE__, __LINE__,
  291: 				 "isc_socket_listen() failed: %s",
  292: 				 isc_result_totext(result));
  293: 		goto cleanup;
  294: 	}
  295: 
  296: 	(void)isc_socket_filter(sock, "httpready");
  297: 
  298: 	result = isc_socket_accept(sock, task, isc_httpd_accept, httpd);
  299: 	if (result != ISC_R_SUCCESS)
  300: 		goto cleanup;
  301: 
  302: 	httpd->render_404 = render_404;
  303: 
  304: 	*httpdp = httpd;
  305: 	return (ISC_R_SUCCESS);
  306: 
  307:   cleanup:
  308: 	isc_task_detach(&httpd->task);
  309: 	isc_socket_detach(&httpd->sock);
  310: 	isc_mem_detach(&httpd->mctx);
  311: 	isc_mutex_destroy(&httpd->lock);
  312: 	isc_mem_put(mctx, httpd, sizeof(isc_httpdmgr_t));
  313: 	return (result);
  314: }
  315: 
  316: static void
  317: httpdmgr_destroy(isc_httpdmgr_t *httpdmgr)
  318: {
  319: 	isc_mem_t *mctx;
  320: 	isc_httpdurl_t *url;
  321: 
  322: 	ENTER("httpdmgr_destroy");
  323: 
  324: 	LOCK(&httpdmgr->lock);
  325: 
  326: 	if (!MSHUTTINGDOWN(httpdmgr)) {
  327: 		NOTICE("httpdmgr_destroy not shutting down yet");
  328: 		UNLOCK(&httpdmgr->lock);
  329: 		return;
  330: 	}
  331: 
  332: 	/*
  333: 	 * If all clients are not shut down, don't do anything yet.
  334: 	 */
  335: 	if (!ISC_LIST_EMPTY(httpdmgr->running)) {
  336: 		NOTICE("httpdmgr_destroy clients still active");
  337: 		UNLOCK(&httpdmgr->lock);
  338: 		return;
  339: 	}
  340: 
  341: 	NOTICE("httpdmgr_destroy detaching socket, task, and timermgr");
  342: 
  343: 	isc_socket_detach(&httpdmgr->sock);
  344: 	isc_task_detach(&httpdmgr->task);
  345: 	httpdmgr->timermgr = NULL;
  346: 
  347: 	/*
  348: 	 * Clear out the list of all actions we know about.  Just free the
  349: 	 * memory.
  350: 	 */
  351: 	url = ISC_LIST_HEAD(httpdmgr->urls);
  352: 	while (url != NULL) {
  353: 		isc_mem_free(httpdmgr->mctx, url->url);
  354: 		ISC_LIST_UNLINK(httpdmgr->urls, url, link);
  355: 		isc_mem_put(httpdmgr->mctx, url, sizeof(isc_httpdurl_t));
  356: 		url = ISC_LIST_HEAD(httpdmgr->urls);
  357: 	}
  358: 
  359: 	UNLOCK(&httpdmgr->lock);
  360: 	isc_mutex_destroy(&httpdmgr->lock);
  361: 
  362: 	if (httpdmgr->ondestroy != NULL)
  363: 		(httpdmgr->ondestroy)(httpdmgr->cb_arg);
  364: 
  365: 	mctx = httpdmgr->mctx;
  366: 	isc_mem_putanddetach(&mctx, httpdmgr, sizeof(isc_httpdmgr_t));
  367: 
  368: 	EXIT("httpdmgr_destroy");
  369: }
  370: 
  371: #define LENGTHOK(s) (httpd->recvbuf - (s) < (int)httpd->recvlen)
  372: #define BUFLENOK(s) (httpd->recvbuf - (s) < HTTP_RECVLEN)
  373: 
  374: static isc_result_t
  375: process_request(isc_httpd_t *httpd, int length)
  376: {
  377: 	char *s;
  378: 	char *p;
  379: 	int delim;
  380: 
  381: 	ENTER("request");
  382: 
  383: 	httpd->recvlen += length;
  384: 
  385: 	httpd->recvbuf[httpd->recvlen] = 0;
  386: 
  387: 	/*
  388: 	 * If we don't find a blank line in our buffer, return that we need
  389: 	 * more data.
  390: 	 */
  391: 	s = strstr(httpd->recvbuf, "\r\n\r\n");
  392: 	delim = 1;
  393: 	if (s == NULL) {
  394: 		s = strstr(httpd->recvbuf, "\n\n");
  395: 		delim = 2;
  396: 	}
  397: 	if (s == NULL)
  398: 		return (ISC_R_NOTFOUND);
  399: 
  400: 	/*
  401: 	 * Determine if this is a POST or GET method.  Any other values will
  402: 	 * cause an error to be returned.
  403: 	 */
  404: 	if (strncmp(httpd->recvbuf, "GET ", 4) == 0) {
  405: 		httpd->method = ISC_HTTPD_METHODGET;
  406: 		p = httpd->recvbuf + 4;
  407: 	} else if (strncmp(httpd->recvbuf, "POST ", 5) == 0) {
  408: 		httpd->method = ISC_HTTPD_METHODPOST;
  409: 		p = httpd->recvbuf + 5;
  410: 	} else {
  411: 		return (ISC_R_RANGE);
  412: 	}
  413: 
  414: 	/*
  415: 	 * From now on, p is the start of our buffer.
  416: 	 */
  417: 
  418: 	/*
  419: 	 * Extract the URL.
  420: 	 */
  421: 	s = p;
  422: 	while (LENGTHOK(s) && BUFLENOK(s) &&
  423: 	       (*s != '\n' && *s != '\r' && *s != '\0' && *s != ' '))
  424: 		s++;
  425: 	if (!LENGTHOK(s))
  426: 		return (ISC_R_NOTFOUND);
  427: 	if (!BUFLENOK(s))
  428: 		return (ISC_R_NOMEMORY);
  429: 	*s = 0;
  430: 
  431: 	/*
  432: 	 * Make the URL relative.
  433: 	 */
  434: 	if ((strncmp(p, "http:/", 6) == 0)
  435: 	    || (strncmp(p, "https:/", 7) == 0)) {
  436: 		/* Skip first / */
  437: 		while (*p != '/' && *p != 0)
  438: 			p++;
  439: 		if (*p == 0)
  440: 			return (ISC_R_RANGE);
  441: 		p++;
  442: 		/* Skip second / */
  443: 		while (*p != '/' && *p != 0)
  444: 			p++;
  445: 		if (*p == 0)
  446: 			return (ISC_R_RANGE);
  447: 		p++;
  448: 		/* Find third / */
  449: 		while (*p != '/' && *p != 0)
  450: 			p++;
  451: 		if (*p == 0) {
  452: 			p--;
  453: 			*p = '/';
  454: 		}
  455: 	}
  456: 
  457: 	httpd->url = p;
  458: 	p = s + delim;
  459: 	s = p;
  460: 
  461: 	/*
  462: 	 * Now, see if there is a ? mark in the URL.  If so, this is
  463: 	 * part of the query string, and we will split it from the URL.
  464: 	 */
  465: 	httpd->querystring = strchr(httpd->url, '?');
  466: 	if (httpd->querystring != NULL) {
  467: 		*(httpd->querystring) = 0;
  468: 		httpd->querystring++;
  469: 	}
  470: 
  471: 	/*
  472: 	 * Extract the HTTP/1.X protocol.  We will bounce on anything but
  473: 	 * HTTP/1.1 for now.
  474: 	 */
  475: 	while (LENGTHOK(s) && BUFLENOK(s) &&
  476: 	       (*s != '\n' && *s != '\r' && *s != '\0'))
  477: 		s++;
  478: 	if (!LENGTHOK(s))
  479: 		return (ISC_R_NOTFOUND);
  480: 	if (!BUFLENOK(s))
  481: 		return (ISC_R_NOMEMORY);
  482: 	*s = 0;
  483: 	if ((strncmp(p, "HTTP/1.0", 8) != 0)
  484: 	    && (strncmp(p, "HTTP/1.1", 8) != 0))
  485: 		return (ISC_R_RANGE);
  486: 	httpd->protocol = p;
  487: 	p = s + 1;
  488: 	s = p;
  489: 
  490: 	if (strstr(s, "Connection: close") != NULL)
  491: 		httpd->flags |= HTTPD_CLOSE;
  492: 
  493: 	if (strstr(s, "Host: ") != NULL)
  494: 		httpd->flags |= HTTPD_FOUNDHOST;
  495: 
  496: 	/*
  497: 	 * Standards compliance hooks here.
  498: 	 */
  499: 	if (strcmp(httpd->protocol, "HTTP/1.1") == 0
  500: 	    && ((httpd->flags & HTTPD_FOUNDHOST) == 0))
  501: 		return (ISC_R_RANGE);
  502: 
  503: 	EXIT("request");
  504: 
  505: 	return (ISC_R_SUCCESS);
  506: }
  507: 
  508: static void
  509: isc_httpd_accept(isc_task_t *task, isc_event_t *ev)
  510: {
  511: 	isc_result_t result;
  512: 	isc_httpdmgr_t *httpdmgr = ev->ev_arg;
  513: 	isc_httpd_t *httpd;
  514: 	isc_region_t r;
  515: 	isc_socket_newconnev_t *nev = (isc_socket_newconnev_t *)ev;
  516: 	isc_sockaddr_t peeraddr;
  517: 
  518: 	ENTER("accept");
  519: 
  520: 	LOCK(&httpdmgr->lock);
  521: 	if (MSHUTTINGDOWN(httpdmgr)) {
  522: 		NOTICE("accept shutting down, goto out");
  523: 		goto out;
  524: 	}
  525: 
  526: 	if (nev->result == ISC_R_CANCELED) {
  527: 		NOTICE("accept canceled, goto out");
  528: 		goto out;
  529: 	}
  530: 
  531: 	if (nev->result != ISC_R_SUCCESS) {
  532: 		/* XXXMLG log failure */
  533: 		NOTICE("accept returned failure, goto requeue");
  534: 		goto requeue;
  535: 	}
  536: 
  537: 	(void)isc_socket_getpeername(nev->newsocket, &peeraddr);
  538: 	if (httpdmgr->client_ok != NULL &&
  539: 	    !(httpdmgr->client_ok)(&peeraddr, httpdmgr->cb_arg)) {
  540: 		isc_socket_detach(&nev->newsocket);
  541: 		goto requeue;
  542: 	}
  543: 
  544: 	httpd = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpd_t));
  545: 	if (httpd == NULL) {
  546: 		/* XXXMLG log failure */
  547: 		NOTICE("accept failed to allocate memory, goto requeue");
  548: 		isc_socket_detach(&nev->newsocket);
  549: 		goto requeue;
  550: 	}
  551: 
  552: 	httpd->mgr = httpdmgr;
  553: 	ISC_LINK_INIT(httpd, link);
  554: 	ISC_LIST_APPEND(httpdmgr->running, httpd, link);
  555: 	ISC_HTTPD_SETRECV(httpd);
  556: 	httpd->sock = nev->newsocket;
  557: 	isc_socket_setname(httpd->sock, "httpd", NULL);
  558: 	httpd->flags = 0;
  559: 
  560: 	/*
  561: 	 * Initialize the buffer for our headers.
  562: 	 */
  563: 	httpd->headerdata = isc_mem_get(httpdmgr->mctx, HTTP_SENDGROW);
  564: 	if (httpd->headerdata == NULL) {
  565: 		isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
  566: 		isc_socket_detach(&nev->newsocket);
  567: 		goto requeue;
  568: 	}
  569: 	httpd->headerlen = HTTP_SENDGROW;
  570: 	isc_buffer_init(&httpd->headerbuffer, httpd->headerdata,
  571: 			httpd->headerlen);
  572: 
  573: 	ISC_LIST_INIT(httpd->bufflist);
  574: 
  575: 	isc_buffer_initnull(&httpd->bodybuffer);
  576: 	reset_client(httpd);
  577: 
  578: 	r.base = (unsigned char *)httpd->recvbuf;
  579: 	r.length = HTTP_RECVLEN - 1;
  580: 	result = isc_socket_recv(httpd->sock, &r, 1, task, isc_httpd_recvdone,
  581: 				 httpd);
  582: 	NOTICE("accept queued recv on socket");
  583: 
  584:  requeue:
  585: 	result = isc_socket_accept(httpdmgr->sock, task, isc_httpd_accept,
  586: 				   httpdmgr);
  587: 	if (result != ISC_R_SUCCESS) {
  588: 		/* XXXMLG what to do?  Log failure... */
  589: 		NOTICE("accept could not reaccept due to failure");
  590: 	}
  591: 
  592:  out:
  593: 	UNLOCK(&httpdmgr->lock);
  594: 
  595: 	httpdmgr_destroy(httpdmgr);
  596: 
  597: 	isc_event_free(&ev);
  598: 
  599: 	EXIT("accept");
  600: }
  601: 
  602: static isc_result_t
  603: render_404(const char *url, const char *querystring,
  604: 	   void *arg,
  605: 	   unsigned int *retcode, const char **retmsg,
  606: 	   const char **mimetype, isc_buffer_t *b,
  607: 	   isc_httpdfree_t **freecb, void **freecb_args)
  608: {
  609: 	static char msg[] = "No such URL.";
  610: 
  611: 	UNUSED(url);
  612: 	UNUSED(querystring);
  613: 	UNUSED(arg);
  614: 
  615: 	*retcode = 404;
  616: 	*retmsg = "No such URL";
  617: 	*mimetype = "text/plain";
  618: 	isc_buffer_reinit(b, msg, strlen(msg));
  619: 	isc_buffer_add(b, strlen(msg));
  620: 	*freecb = NULL;
  621: 	*freecb_args = NULL;
  622: 
  623: 	return (ISC_R_SUCCESS);
  624: }
  625: 
  626: static void
  627: isc_httpd_recvdone(isc_task_t *task, isc_event_t *ev)
  628: {
  629: 	isc_region_t r;
  630: 	isc_result_t result;
  631: 	isc_httpd_t *httpd = ev->ev_arg;
  632: 	isc_socketevent_t *sev = (isc_socketevent_t *)ev;
  633: 	isc_httpdurl_t *url;
  634: 	isc_time_t now;
  635: 	char datebuf[32];  /* Only need 30, but safety first */
  636: 
  637: 	ENTER("recv");
  638: 
  639: 	INSIST(ISC_HTTPD_ISRECV(httpd));
  640: 
  641: 	if (sev->result != ISC_R_SUCCESS) {
  642: 		NOTICE("recv destroying client");
  643: 		destroy_client(&httpd);
  644: 		goto out;
  645: 	}
  646: 
  647: 	result = process_request(httpd, sev->n);
  648: 	if (result == ISC_R_NOTFOUND) {
  649: 		if (httpd->recvlen >= HTTP_RECVLEN - 1) {
  650: 			destroy_client(&httpd);
  651: 			goto out;
  652: 		}
  653: 		r.base = (unsigned char *)httpd->recvbuf + httpd->recvlen;
  654: 		r.length = HTTP_RECVLEN - httpd->recvlen - 1;
  655: 		result = isc_socket_recv(httpd->sock, &r, 1, task,
  656: 					 isc_httpd_recvdone, httpd);
  657: 		goto out;
  658: 	} else if (result != ISC_R_SUCCESS) {
  659: 		destroy_client(&httpd);
  660: 		goto out;
  661: 	}
  662: 
  663: 	ISC_HTTPD_SETSEND(httpd);
  664: 
  665: 	/*
  666: 	 * XXXMLG Call function here.  Provide an add-header function
  667: 	 * which will append the common headers to a response we generate.
  668: 	 */
  669: 	isc_buffer_initnull(&httpd->bodybuffer);
  670: 	isc_time_now(&now);
  671: 	isc_time_formathttptimestamp(&now, datebuf, sizeof(datebuf));
  672: 	url = ISC_LIST_HEAD(httpd->mgr->urls);
  673: 	while (url != NULL) {
  674: 		if (strcmp(httpd->url, url->url) == 0)
  675: 			break;
  676: 		url = ISC_LIST_NEXT(url, link);
  677: 	}
  678: 	if (url == NULL)
  679: 		result = httpd->mgr->render_404(httpd->url, httpd->querystring,
  680: 						NULL,
  681: 						&httpd->retcode,
  682: 						&httpd->retmsg,
  683: 						&httpd->mimetype,
  684: 						&httpd->bodybuffer,
  685: 						&httpd->freecb,
  686: 						&httpd->freecb_arg);
  687: 	else
  688: 		result = url->action(httpd->url, httpd->querystring,
  689: 				     url->action_arg,
  690: 				     &httpd->retcode, &httpd->retmsg,
  691: 				     &httpd->mimetype, &httpd->bodybuffer,
  692: 				     &httpd->freecb, &httpd->freecb_arg);
  693: 	if (result != ISC_R_SUCCESS) {
  694: 		destroy_client(&httpd);
  695: 		goto out;
  696: 	}
  697: 
  698: 	isc_httpd_response(httpd);
  699: 	isc_httpd_addheader(httpd, "Content-Type", httpd->mimetype);
  700: 	isc_httpd_addheader(httpd, "Date", datebuf);
  701: 	isc_httpd_addheader(httpd, "Expires", datebuf);
  702: 	isc_httpd_addheader(httpd, "Last-Modified", datebuf);
  703: 	isc_httpd_addheader(httpd, "Pragma: no-cache", NULL);
  704: 	isc_httpd_addheader(httpd, "Cache-Control: no-cache", NULL);
  705: 	isc_httpd_addheader(httpd, "Server: libisc", NULL);
  706: 	isc_httpd_addheaderuint(httpd, "Content-Length",
  707: 				isc_buffer_usedlength(&httpd->bodybuffer));
  708: 	isc_httpd_endheaders(httpd);  /* done */
  709: 
  710: 	ISC_LIST_APPEND(httpd->bufflist, &httpd->headerbuffer, link);
  711: 	/*
  712: 	 * Link the data buffer into our send queue, should we have any data
  713: 	 * rendered into it.  If no data is present, we won't do anything
  714: 	 * with the buffer.
  715: 	 */
  716: 	if (isc_buffer_length(&httpd->bodybuffer) > 0)
  717: 		ISC_LIST_APPEND(httpd->bufflist, &httpd->bodybuffer, link);
  718: 
  719: 	result = isc_socket_sendv(httpd->sock, &httpd->bufflist, task,
  720: 				  isc_httpd_senddone, httpd);
  721: 
  722:  out:
  723: 	isc_event_free(&ev);
  724: 	EXIT("recv");
  725: }
  726: 
  727: void
  728: isc_httpdmgr_shutdown(isc_httpdmgr_t **httpdmgrp)
  729: {
  730: 	isc_httpdmgr_t *httpdmgr;
  731: 	isc_httpd_t *httpd;
  732: 	httpdmgr = *httpdmgrp;
  733: 	*httpdmgrp = NULL;
  734: 
  735: 	ENTER("isc_httpdmgr_shutdown");
  736: 
  737: 	LOCK(&httpdmgr->lock);
  738: 
  739: 	MSETSHUTTINGDOWN(httpdmgr);
  740: 
  741: 	isc_socket_cancel(httpdmgr->sock, httpdmgr->task, ISC_SOCKCANCEL_ALL);
  742: 
  743: 	httpd = ISC_LIST_HEAD(httpdmgr->running);
  744: 	while (httpd != NULL) {
  745: 		isc_socket_cancel(httpd->sock, httpdmgr->task,
  746: 				  ISC_SOCKCANCEL_ALL);
  747: 		httpd = ISC_LIST_NEXT(httpd, link);
  748: 	}
  749: 
  750: 	UNLOCK(&httpdmgr->lock);
  751: 
  752: 	EXIT("isc_httpdmgr_shutdown");
  753: }
  754: 
  755: static isc_result_t
  756: grow_headerspace(isc_httpd_t *httpd)
  757: {
  758: 	char *newspace;
  759: 	unsigned int newlen;
  760: 	isc_region_t r;
  761: 
  762: 	newlen = httpd->headerlen + HTTP_SENDGROW;
  763: 	if (newlen > HTTP_SEND_MAXLEN)
  764: 		return (ISC_R_NOSPACE);
  765: 
  766: 	newspace = isc_mem_get(httpd->mgr->mctx, newlen);
  767: 	if (newspace == NULL)
  768: 		return (ISC_R_NOMEMORY);
  769: 	isc_buffer_region(&httpd->headerbuffer, &r);
  770: 	isc_buffer_reinit(&httpd->headerbuffer, newspace, newlen);
  771: 
  772: 	isc_mem_put(httpd->mgr->mctx, r.base, r.length);
  773: 
  774: 	return (ISC_R_SUCCESS);
  775: }
  776: 
  777: isc_result_t
  778: isc_httpd_response(isc_httpd_t *httpd)
  779: {
  780: 	isc_result_t result;
  781: 	unsigned int needlen;
  782: 
  783: 	needlen = strlen(httpd->protocol) + 1; /* protocol + space */
  784: 	needlen += 3 + 1;  /* room for response code, always 3 bytes */
  785: 	needlen += strlen(httpd->retmsg) + 2;  /* return msg + CRLF */
  786: 
  787: 	if (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
  788: 		result = grow_headerspace(httpd);
  789: 		if (result != ISC_R_SUCCESS)
  790: 			return (result);
  791: 	}
  792: 
  793: 	sprintf(isc_buffer_used(&httpd->headerbuffer), "%s %03d %s\r\n",
  794: 		httpd->protocol, httpd->retcode, httpd->retmsg);
  795: 	isc_buffer_add(&httpd->headerbuffer, needlen);
  796: 
  797: 	return (ISC_R_SUCCESS);
  798: }
  799: 
  800: isc_result_t
  801: isc_httpd_addheader(isc_httpd_t *httpd, const char *name,
  802: 		    const char *val)
  803: {
  804: 	isc_result_t result;
  805: 	unsigned int needlen;
  806: 
  807: 	needlen = strlen(name); /* name itself */
  808: 	if (val != NULL)
  809: 		needlen += 2 + strlen(val); /* :<space> and val */
  810: 	needlen += 2; /* CRLF */
  811: 
  812: 	if (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
  813: 		result = grow_headerspace(httpd);
  814: 		if (result != ISC_R_SUCCESS)
  815: 			return (result);
  816: 	}
  817: 
  818: 	if (val != NULL)
  819: 		sprintf(isc_buffer_used(&httpd->headerbuffer),
  820: 			"%s: %s\r\n", name, val);
  821: 	else
  822: 		sprintf(isc_buffer_used(&httpd->headerbuffer),
  823: 			"%s\r\n", name);
  824: 
  825: 	isc_buffer_add(&httpd->headerbuffer, needlen);
  826: 
  827: 	return (ISC_R_SUCCESS);
  828: }
  829: 
  830: isc_result_t
  831: isc_httpd_endheaders(isc_httpd_t *httpd)
  832: {
  833: 	isc_result_t result;
  834: 
  835: 	if (isc_buffer_availablelength(&httpd->headerbuffer) < 2) {
  836: 		result = grow_headerspace(httpd);
  837: 		if (result != ISC_R_SUCCESS)
  838: 			return (result);
  839: 	}
  840: 
  841: 	sprintf(isc_buffer_used(&httpd->headerbuffer), "\r\n");
  842: 	isc_buffer_add(&httpd->headerbuffer, 2);
  843: 
  844: 	return (ISC_R_SUCCESS);
  845: }
  846: 
  847: isc_result_t
  848: isc_httpd_addheaderuint(isc_httpd_t *httpd, const char *name, int val) {
  849: 	isc_result_t result;
  850: 	unsigned int needlen;
  851: 	char buf[sizeof "18446744073709551616"];
  852: 
  853: 	sprintf(buf, "%d", val);
  854: 
  855: 	needlen = strlen(name); /* name itself */
  856: 	needlen += 2 + strlen(buf); /* :<space> and val */
  857: 	needlen += 2; /* CRLF */
  858: 
  859: 	if (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
  860: 		result = grow_headerspace(httpd);
  861: 		if (result != ISC_R_SUCCESS)
  862: 			return (result);
  863: 	}
  864: 
  865: 	sprintf(isc_buffer_used(&httpd->headerbuffer),
  866: 		"%s: %s\r\n", name, buf);
  867: 
  868: 	isc_buffer_add(&httpd->headerbuffer, needlen);
  869: 
  870: 	return (ISC_R_SUCCESS);
  871: }
  872: 
  873: static void
  874: isc_httpd_senddone(isc_task_t *task, isc_event_t *ev)
  875: {
  876: 	isc_httpd_t *httpd = ev->ev_arg;
  877: 	isc_region_t r;
  878: 	isc_result_t result;
  879: 	isc_socketevent_t *sev = (isc_socketevent_t *)ev;
  880: 
  881: 	ENTER("senddone");
  882: 	INSIST(ISC_HTTPD_ISSEND(httpd));
  883: 
  884: 	/*
  885: 	 * First, unlink our header buffer from the socket's bufflist.  This
  886: 	 * is sort of an evil hack, since we know our buffer will be there,
  887: 	 * and we know it's address, so we can just remove it directly.
  888: 	 */
  889: 	NOTICE("senddone unlinked header");
  890: 	ISC_LIST_UNLINK(sev->bufferlist, &httpd->headerbuffer, link);
  891: 
  892: 	/*
  893: 	 * We will always want to clean up our receive buffer, even if we
  894: 	 * got an error on send or we are shutting down.
  895: 	 *
  896: 	 * We will pass in the buffer only if there is data in it.  If
  897: 	 * there is no data, we will pass in a NULL.
  898: 	 */
  899: 	if (httpd->freecb != NULL) {
  900: 		isc_buffer_t *b = NULL;
  901: 		if (isc_buffer_length(&httpd->bodybuffer) > 0)
  902: 			b = &httpd->bodybuffer;
  903: 		httpd->freecb(b, httpd->freecb_arg);
  904: 		NOTICE("senddone free callback performed");
  905: 	}
  906: 	if (ISC_LINK_LINKED(&httpd->bodybuffer, link)) {
  907: 		ISC_LIST_UNLINK(sev->bufferlist, &httpd->bodybuffer, link);
  908: 		NOTICE("senddone body buffer unlinked");
  909: 	}
  910: 
  911: 	if (sev->result != ISC_R_SUCCESS) {
  912: 		destroy_client(&httpd);
  913: 		goto out;
  914: 	}
  915: 
  916: 	if ((httpd->flags & HTTPD_CLOSE) != 0) {
  917: 		destroy_client(&httpd);
  918: 		goto out;
  919: 	}
  920: 
  921: 	ISC_HTTPD_SETRECV(httpd);
  922: 
  923: 	NOTICE("senddone restarting recv on socket");
  924: 
  925: 	reset_client(httpd);
  926: 
  927: 	r.base = (unsigned char *)httpd->recvbuf;
  928: 	r.length = HTTP_RECVLEN - 1;
  929: 	result = isc_socket_recv(httpd->sock, &r, 1, task, isc_httpd_recvdone,
  930: 				 httpd);
  931: 
  932: out:
  933: 	isc_event_free(&ev);
  934: 	EXIT("senddone");
  935: }
  936: 
  937: static void
  938: reset_client(isc_httpd_t *httpd)
  939: {
  940: 	/*
  941: 	 * Catch errors here.  We MUST be in RECV mode, and we MUST NOT have
  942: 	 * any outstanding buffers.  If we have buffers, we have a leak.
  943: 	 */
  944: 	INSIST(ISC_HTTPD_ISRECV(httpd));
  945: 	INSIST(!ISC_LINK_LINKED(&httpd->headerbuffer, link));
  946: 	INSIST(!ISC_LINK_LINKED(&httpd->bodybuffer, link));
  947: 
  948: 	httpd->recvbuf[0] = 0;
  949: 	httpd->recvlen = 0;
  950: 	httpd->method = ISC_HTTPD_METHODUNKNOWN;
  951: 	httpd->url = NULL;
  952: 	httpd->querystring = NULL;
  953: 	httpd->protocol = NULL;
  954: 	httpd->flags = 0;
  955: 
  956: 	isc_buffer_clear(&httpd->headerbuffer);
  957: 	isc_buffer_invalidate(&httpd->bodybuffer);
  958: }
  959: 
  960: isc_result_t
  961: isc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url,
  962: 		    isc_httpdaction_t *func, void *arg)
  963: {
  964: 	isc_httpdurl_t *item;
  965: 
  966: 	if (url == NULL) {
  967: 		httpdmgr->render_404 = func;
  968: 		return (ISC_R_SUCCESS);
  969: 	}
  970: 
  971: 	item = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpdurl_t));
  972: 	if (item == NULL)
  973: 		return (ISC_R_NOMEMORY);
  974: 
  975: 	item->url = isc_mem_strdup(httpdmgr->mctx, url);
  976: 	if (item->url == NULL) {
  977: 		isc_mem_put(httpdmgr->mctx, item, sizeof(isc_httpdurl_t));
  978: 		return (ISC_R_NOMEMORY);
  979: 	}
  980: 
  981: 	item->action = func;
  982: 	item->action_arg = arg;
  983: 	ISC_LINK_INIT(item, link);
  984: 	ISC_LIST_APPEND(httpdmgr->urls, item, link);
  985: 
  986: 	return (ISC_R_SUCCESS);
  987: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>