httpd.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2006-2008, 2010-2015  Internet Systems Consortium, Inc. ("ISC")
00003  *
00004  * Permission to use, copy, modify, and/or distribute this software for any
00005  * purpose with or without fee is hereby granted, provided that the above
00006  * copyright notice and this permission notice appear in all copies.
00007  *
00008  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
00009  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00010  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
00011  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
00012  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
00013  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00014  * PERFORMANCE OF THIS SOFTWARE.
00015  */
00016 
00017 /* $Id$ */
00018 
00019 /*! \file */
00020 
00021 #include <config.h>
00022 
00023 #include <isc/buffer.h>
00024 #include <isc/httpd.h>
00025 #include <isc/mem.h>
00026 #include <isc/socket.h>
00027 #include <isc/string.h>
00028 #include <isc/task.h>
00029 #include <isc/time.h>
00030 #include <isc/util.h>
00031 
00032 #include <string.h>
00033 
00034 /*%
00035  * TODO:
00036  *
00037  *  o  Put in better checks to make certain things are passed in correctly.
00038  *     This includes a magic number for externally-visible structures,
00039  *     checking for NULL-ness before dereferencing, etc.
00040  *  o  Make the URL processing external functions which will fill-in a buffer
00041  *     structure we provide, or return an error and we will render a generic
00042  *     page and close the client.
00043  */
00044 
00045 #define MSHUTTINGDOWN(cm) ((cm->flags & ISC_HTTPDMGR_FLAGSHUTTINGDOWN) != 0)
00046 #define MSETSHUTTINGDOWN(cm) (cm->flags |= ISC_HTTPDMGR_FLAGSHUTTINGDOWN)
00047 
00048 #ifdef DEBUG_HTTPD
00049 #define ENTER(x) do { fprintf(stderr, "ENTER %s\n", (x)); } while (0)
00050 #define EXIT(x) do { fprintf(stderr, "EXIT %s\n", (x)); } while (0)
00051 #define NOTICE(x) do { fprintf(stderr, "NOTICE %s\n", (x)); } while (0)
00052 #else
00053 #define ENTER(x) do { } while(0)
00054 #define EXIT(x) do { } while(0)
00055 #define NOTICE(x) do { } while(0)
00056 #endif
00057 
00058 #define HTTP_RECVLEN                    1024
00059 #define HTTP_SENDGROW                   1024
00060 #define HTTP_SEND_MAXLEN                10240
00061 
00062 #define HTTPD_CLOSE             0x0001 /* Got a Connection: close header */
00063 #define HTTPD_FOUNDHOST         0x0002 /* Got a Host: header */
00064 
00065 /*% http client */
00066 struct isc_httpd {
00067         isc_httpdmgr_t         *mgr;            /*%< our parent */
00068         ISC_LINK(isc_httpd_t)   link;
00069         unsigned int            state;
00070         isc_socket_t            *sock;
00071 
00072         /*%
00073          * Received data state.
00074          */
00075         char                    recvbuf[HTTP_RECVLEN]; /*%< receive buffer */
00076         isc_uint32_t            recvlen;        /*%< length recv'd */
00077         char                   *headers;        /*%< set in process_request() */
00078         unsigned int            method;
00079         char                   *url;
00080         char                   *querystring;
00081         char                   *protocol;
00082 
00083         /*
00084          * Flags on the httpd client.
00085          */
00086         int                     flags;
00087 
00088         /*%
00089          * Transmit data state.
00090          *
00091          * This is the data buffer we will transmit.
00092          *
00093          * This free function pointer is filled in by the rendering function
00094          * we call.  The free function is called after the data is transmitted
00095          * to the client.
00096          *
00097          * The bufflist is the list of buffers we are currently transmitting.
00098          * The headerdata is where we render our headers to.  If we run out of
00099          * space when rendering a header, we will change the size of our
00100          * buffer.  We will not free it until we are finished, and will
00101          * allocate an additional HTTP_SENDGROW bytes per header space grow.
00102          *
00103          * We currently use two buffers total, one for the headers (which
00104          * we manage) and another for the client to fill in (which it manages,
00105          * it provides the space for it, etc) -- we will pass that buffer
00106          * structure back to the caller, who is responsible for managing the
00107          * space it may have allocated as backing store for it.  This second
00108          * buffer is bodybuffer, and we only allocate the buffer itself, not
00109          * the backing store.
00110          */
00111         isc_bufferlist_t        bufflist;
00112         char                   *headerdata; /*%< send header buf */
00113         unsigned int            headerlen;  /*%< current header buffer size */
00114         isc_buffer_t            headerbuffer;
00115 
00116         const char             *mimetype;
00117         unsigned int            retcode;
00118         const char             *retmsg;
00119         isc_buffer_t            bodybuffer;
00120         isc_httpdfree_t        *freecb;
00121         void                   *freecb_arg;
00122 };
00123 
00124 /*% lightweight socket manager for httpd output */
00125 struct isc_httpdmgr {
00126         isc_mem_t              *mctx;
00127         isc_socket_t           *sock;           /*%< listening socket */
00128         isc_task_t             *task;           /*%< owning task */
00129         isc_timermgr_t         *timermgr;
00130 
00131         isc_httpdclientok_t    *client_ok;      /*%< client validator */
00132         isc_httpdondestroy_t   *ondestroy;      /*%< cleanup callback */
00133         void                   *cb_arg;         /*%< argument for the above */
00134 
00135         unsigned int            flags;
00136         ISC_LIST(isc_httpd_t)   running;        /*%< running clients */
00137 
00138         isc_mutex_t             lock;
00139 
00140         ISC_LIST(isc_httpdurl_t) urls;          /*%< urls we manage */
00141         isc_httpdaction_t      *render_404;
00142         isc_httpdaction_t      *render_500;
00143 };
00144 
00145 /*%
00146  * HTTP methods.
00147  */
00148 #define ISC_HTTPD_METHODUNKNOWN 0
00149 #define ISC_HTTPD_METHODGET     1
00150 #define ISC_HTTPD_METHODPOST    2
00151 
00152 /*%
00153  * Client states.
00154  *
00155  * _IDLE        The client is not doing anything at all.  This state should
00156  *              only occur just after creation, and just before being
00157  *              destroyed.
00158  *
00159  * _RECV        The client is waiting for data after issuing a socket recv().
00160  *
00161  * _RECVDONE    Data has been received, and is being processed.
00162  *
00163  * _SEND        All data for a response has completed, and a reply was
00164  *              sent via a socket send() call.
00165  *
00166  * _SENDDONE    Send is completed.
00167  *
00168  * Badly formatted state table:
00169  *
00170  *      IDLE -> RECV when client has a recv() queued.
00171  *
00172  *      RECV -> RECVDONE when recvdone event received.
00173  *
00174  *      RECVDONE -> SEND if the data for a reply is at hand.
00175  *
00176  *      SEND -> RECV when a senddone event was received.
00177  *
00178  *      At any time -> RECV on error.  If RECV fails, the client will
00179  *      self-destroy, closing the socket and freeing memory.
00180  */
00181 #define ISC_HTTPD_STATEIDLE     0
00182 #define ISC_HTTPD_STATERECV     1
00183 #define ISC_HTTPD_STATERECVDONE 2
00184 #define ISC_HTTPD_STATESEND     3
00185 #define ISC_HTTPD_STATESENDDONE 4
00186 
00187 #define ISC_HTTPD_ISRECV(c)     ((c)->state == ISC_HTTPD_STATERECV)
00188 #define ISC_HTTPD_ISRECVDONE(c) ((c)->state == ISC_HTTPD_STATERECVDONE)
00189 #define ISC_HTTPD_ISSEND(c)     ((c)->state == ISC_HTTPD_STATESEND)
00190 #define ISC_HTTPD_ISSENDDONE(c) ((c)->state == ISC_HTTPD_STATESENDDONE)
00191 
00192 /*%
00193  * Overall magic test that means we're not idle.
00194  */
00195 #define ISC_HTTPD_SETRECV(c)    ((c)->state = ISC_HTTPD_STATERECV)
00196 #define ISC_HTTPD_SETRECVDONE(c)        ((c)->state = ISC_HTTPD_STATERECVDONE)
00197 #define ISC_HTTPD_SETSEND(c)    ((c)->state = ISC_HTTPD_STATESEND)
00198 #define ISC_HTTPD_SETSENDDONE(c)        ((c)->state = ISC_HTTPD_STATESENDDONE)
00199 
00200 static void isc_httpd_accept(isc_task_t *, isc_event_t *);
00201 static void isc_httpd_recvdone(isc_task_t *, isc_event_t *);
00202 static void isc_httpd_senddone(isc_task_t *, isc_event_t *);
00203 static void destroy_client(isc_httpd_t **);
00204 static isc_result_t process_request(isc_httpd_t *, int);
00205 static void httpdmgr_destroy(isc_httpdmgr_t *);
00206 static isc_result_t grow_headerspace(isc_httpd_t *);
00207 static void reset_client(isc_httpd_t *httpd);
00208 
00209 static isc_httpdaction_t render_404;
00210 static isc_httpdaction_t render_500;
00211 
00212 static void
00213 destroy_client(isc_httpd_t **httpdp) {
00214         isc_httpd_t *httpd = *httpdp;
00215         isc_httpdmgr_t *httpdmgr = httpd->mgr;
00216 
00217         *httpdp = NULL;
00218 
00219         LOCK(&httpdmgr->lock);
00220 
00221         isc_socket_detach(&httpd->sock);
00222         ISC_LIST_UNLINK(httpdmgr->running, httpd, link);
00223 
00224         if (httpd->headerlen > 0)
00225                 isc_mem_put(httpdmgr->mctx, httpd->headerdata,
00226                             httpd->headerlen);
00227 
00228         isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
00229 
00230         UNLOCK(&httpdmgr->lock);
00231 
00232         httpdmgr_destroy(httpdmgr);
00233 }
00234 
00235 isc_result_t
00236 isc_httpdmgr_create(isc_mem_t *mctx, isc_socket_t *sock, isc_task_t *task,
00237                     isc_httpdclientok_t *client_ok,
00238                     isc_httpdondestroy_t *ondestroy, void *cb_arg,
00239                     isc_timermgr_t *tmgr, isc_httpdmgr_t **httpdp)
00240 {
00241         isc_result_t result;
00242         isc_httpdmgr_t *httpd;
00243 
00244         REQUIRE(mctx != NULL);
00245         REQUIRE(sock != NULL);
00246         REQUIRE(task != NULL);
00247         REQUIRE(tmgr != NULL);
00248         REQUIRE(httpdp != NULL && *httpdp == NULL);
00249 
00250         httpd = isc_mem_get(mctx, sizeof(isc_httpdmgr_t));
00251         if (httpd == NULL)
00252                 return (ISC_R_NOMEMORY);
00253 
00254         result = isc_mutex_init(&httpd->lock);
00255         if (result != ISC_R_SUCCESS) {
00256                 isc_mem_put(mctx, httpd, sizeof(isc_httpdmgr_t));
00257                 return (result);
00258         }
00259         httpd->mctx = NULL;
00260         isc_mem_attach(mctx, &httpd->mctx);
00261         httpd->sock = NULL;
00262         isc_socket_attach(sock, &httpd->sock);
00263         httpd->task = NULL;
00264         isc_task_attach(task, &httpd->task);
00265         httpd->timermgr = tmgr; /* XXXMLG no attach function? */
00266         httpd->client_ok = client_ok;
00267         httpd->ondestroy = ondestroy;
00268         httpd->cb_arg = cb_arg;
00269 
00270         ISC_LIST_INIT(httpd->running);
00271         ISC_LIST_INIT(httpd->urls);
00272 
00273         /* XXXMLG ignore errors on isc_socket_listen() */
00274         result = isc_socket_listen(sock, SOMAXCONN);
00275         if (result != ISC_R_SUCCESS) {
00276                 UNEXPECTED_ERROR(__FILE__, __LINE__,
00277                                  "isc_socket_listen() failed: %s",
00278                                  isc_result_totext(result));
00279                 goto cleanup;
00280         }
00281 
00282         (void)isc_socket_filter(sock, "httpready");
00283 
00284         result = isc_socket_accept(sock, task, isc_httpd_accept, httpd);
00285         if (result != ISC_R_SUCCESS)
00286                 goto cleanup;
00287 
00288         httpd->render_404 = render_404;
00289         httpd->render_500 = render_500;
00290 
00291         *httpdp = httpd;
00292         return (ISC_R_SUCCESS);
00293 
00294   cleanup:
00295         isc_task_detach(&httpd->task);
00296         isc_socket_detach(&httpd->sock);
00297         isc_mem_detach(&httpd->mctx);
00298         (void)isc_mutex_destroy(&httpd->lock);
00299         isc_mem_put(mctx, httpd, sizeof(isc_httpdmgr_t));
00300         return (result);
00301 }
00302 
00303 static void
00304 httpdmgr_destroy(isc_httpdmgr_t *httpdmgr) {
00305         isc_mem_t *mctx;
00306         isc_httpdurl_t *url;
00307 
00308         ENTER("httpdmgr_destroy");
00309 
00310         LOCK(&httpdmgr->lock);
00311 
00312         if (!MSHUTTINGDOWN(httpdmgr)) {
00313                 NOTICE("httpdmgr_destroy not shutting down yet");
00314                 UNLOCK(&httpdmgr->lock);
00315                 return;
00316         }
00317 
00318         /*
00319          * If all clients are not shut down, don't do anything yet.
00320          */
00321         if (!ISC_LIST_EMPTY(httpdmgr->running)) {
00322                 NOTICE("httpdmgr_destroy clients still active");
00323                 UNLOCK(&httpdmgr->lock);
00324                 return;
00325         }
00326 
00327         NOTICE("httpdmgr_destroy detaching socket, task, and timermgr");
00328 
00329         isc_socket_detach(&httpdmgr->sock);
00330         isc_task_detach(&httpdmgr->task);
00331         httpdmgr->timermgr = NULL;
00332 
00333         /*
00334          * Clear out the list of all actions we know about.  Just free the
00335          * memory.
00336          */
00337         url = ISC_LIST_HEAD(httpdmgr->urls);
00338         while (url != NULL) {
00339                 isc_mem_free(httpdmgr->mctx, url->url);
00340                 ISC_LIST_UNLINK(httpdmgr->urls, url, link);
00341                 isc_mem_put(httpdmgr->mctx, url, sizeof(isc_httpdurl_t));
00342                 url = ISC_LIST_HEAD(httpdmgr->urls);
00343         }
00344 
00345         UNLOCK(&httpdmgr->lock);
00346         (void)isc_mutex_destroy(&httpdmgr->lock);
00347 
00348         if (httpdmgr->ondestroy != NULL)
00349                 (httpdmgr->ondestroy)(httpdmgr->cb_arg);
00350 
00351         mctx = httpdmgr->mctx;
00352         isc_mem_putanddetach(&mctx, httpdmgr, sizeof(isc_httpdmgr_t));
00353 
00354         EXIT("httpdmgr_destroy");
00355 }
00356 
00357 #define LENGTHOK(s) (httpd->recvbuf - (s) < (int)httpd->recvlen)
00358 #define BUFLENOK(s) (httpd->recvbuf - (s) < HTTP_RECVLEN)
00359 
00360 static isc_result_t
00361 process_request(isc_httpd_t *httpd, int length) {
00362         char *s;
00363         char *p;
00364         int delim;
00365 
00366         ENTER("request");
00367 
00368         httpd->recvlen += length;
00369 
00370         httpd->recvbuf[httpd->recvlen] = 0;
00371         httpd->headers = NULL;
00372 
00373         /*
00374          * If we don't find a blank line in our buffer, return that we need
00375          * more data.
00376          */
00377         s = strstr(httpd->recvbuf, "\r\n\r\n");
00378         delim = 1;
00379         if (s == NULL) {
00380                 s = strstr(httpd->recvbuf, "\n\n");
00381                 delim = 2;
00382         }
00383         if (s == NULL)
00384                 return (ISC_R_NOTFOUND);
00385 
00386         /*
00387          * Determine if this is a POST or GET method.  Any other values will
00388          * cause an error to be returned.
00389          */
00390         if (strncmp(httpd->recvbuf, "GET ", 4) == 0) {
00391                 httpd->method = ISC_HTTPD_METHODGET;
00392                 p = httpd->recvbuf + 4;
00393         } else if (strncmp(httpd->recvbuf, "POST ", 5) == 0) {
00394                 httpd->method = ISC_HTTPD_METHODPOST;
00395                 p = httpd->recvbuf + 5;
00396         } else {
00397                 return (ISC_R_RANGE);
00398         }
00399 
00400         /*
00401          * From now on, p is the start of our buffer.
00402          */
00403 
00404         /*
00405          * Extract the URL.
00406          */
00407         s = p;
00408         while (LENGTHOK(s) && BUFLENOK(s) &&
00409                (*s != '\n' && *s != '\r' && *s != '\0' && *s != ' '))
00410                 s++;
00411         if (!LENGTHOK(s))
00412                 return (ISC_R_NOTFOUND);
00413         if (!BUFLENOK(s))
00414                 return (ISC_R_NOMEMORY);
00415         *s = 0;
00416 
00417         /*
00418          * Make the URL relative.
00419          */
00420         if ((strncmp(p, "http:/", 6) == 0)
00421             || (strncmp(p, "https:/", 7) == 0)) {
00422                 /* Skip first / */
00423                 while (*p != '/' && *p != 0)
00424                         p++;
00425                 if (*p == 0)
00426                         return (ISC_R_RANGE);
00427                 p++;
00428                 /* Skip second / */
00429                 while (*p != '/' && *p != 0)
00430                         p++;
00431                 if (*p == 0)
00432                         return (ISC_R_RANGE);
00433                 p++;
00434                 /* Find third / */
00435                 while (*p != '/' && *p != 0)
00436                         p++;
00437                 if (*p == 0) {
00438                         p--;
00439                         *p = '/';
00440                 }
00441         }
00442 
00443         httpd->url = p;
00444         p = s + delim;
00445         s = p;
00446 
00447         /*
00448          * Now, see if there is a ? mark in the URL.  If so, this is
00449          * part of the query string, and we will split it from the URL.
00450          */
00451         httpd->querystring = strchr(httpd->url, '?');
00452         if (httpd->querystring != NULL) {
00453                 *(httpd->querystring) = 0;
00454                 httpd->querystring++;
00455         }
00456 
00457         /*
00458          * Extract the HTTP/1.X protocol.  We will bounce on anything but
00459          * HTTP/1.1 for now.
00460          */
00461         while (LENGTHOK(s) && BUFLENOK(s) &&
00462                (*s != '\n' && *s != '\r' && *s != '\0'))
00463                 s++;
00464         if (!LENGTHOK(s))
00465                 return (ISC_R_NOTFOUND);
00466         if (!BUFLENOK(s))
00467                 return (ISC_R_NOMEMORY);
00468         *s = 0;
00469         if ((strncmp(p, "HTTP/1.0", 8) != 0)
00470             && (strncmp(p, "HTTP/1.1", 8) != 0))
00471                 return (ISC_R_RANGE);
00472         httpd->protocol = p;
00473         p = s + 1;
00474         s = p;
00475 
00476         httpd->headers = s;
00477 
00478         if (strstr(s, "Connection: close") != NULL)
00479                 httpd->flags |= HTTPD_CLOSE;
00480 
00481         if (strstr(s, "Host: ") != NULL)
00482                 httpd->flags |= HTTPD_FOUNDHOST;
00483 
00484         /*
00485          * Standards compliance hooks here.
00486          */
00487         if (strcmp(httpd->protocol, "HTTP/1.1") == 0
00488             && ((httpd->flags & HTTPD_FOUNDHOST) == 0))
00489                 return (ISC_R_RANGE);
00490 
00491         EXIT("request");
00492 
00493         return (ISC_R_SUCCESS);
00494 }
00495 
00496 static void
00497 isc_httpd_accept(isc_task_t *task, isc_event_t *ev) {
00498         isc_result_t result;
00499         isc_httpdmgr_t *httpdmgr = ev->ev_arg;
00500         isc_httpd_t *httpd;
00501         isc_region_t r;
00502         isc_socket_newconnev_t *nev = (isc_socket_newconnev_t *)ev;
00503         isc_sockaddr_t peeraddr;
00504 
00505         ENTER("accept");
00506 
00507         LOCK(&httpdmgr->lock);
00508         if (MSHUTTINGDOWN(httpdmgr)) {
00509                 NOTICE("accept shutting down, goto out");
00510                 goto out;
00511         }
00512 
00513         if (nev->result == ISC_R_CANCELED) {
00514                 NOTICE("accept canceled, goto out");
00515                 goto out;
00516         }
00517 
00518         if (nev->result != ISC_R_SUCCESS) {
00519                 /* XXXMLG log failure */
00520                 NOTICE("accept returned failure, goto requeue");
00521                 goto requeue;
00522         }
00523 
00524         (void)isc_socket_getpeername(nev->newsocket, &peeraddr);
00525         if (httpdmgr->client_ok != NULL &&
00526             !(httpdmgr->client_ok)(&peeraddr, httpdmgr->cb_arg)) {
00527                 isc_socket_detach(&nev->newsocket);
00528                 goto requeue;
00529         }
00530 
00531         httpd = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpd_t));
00532         if (httpd == NULL) {
00533                 /* XXXMLG log failure */
00534                 NOTICE("accept failed to allocate memory, goto requeue");
00535                 isc_socket_detach(&nev->newsocket);
00536                 goto requeue;
00537         }
00538 
00539         httpd->mgr = httpdmgr;
00540         ISC_LINK_INIT(httpd, link);
00541         ISC_LIST_APPEND(httpdmgr->running, httpd, link);
00542         ISC_HTTPD_SETRECV(httpd);
00543         httpd->sock = nev->newsocket;
00544         isc_socket_setname(httpd->sock, "httpd", NULL);
00545         httpd->flags = 0;
00546 
00547         /*
00548          * Initialize the buffer for our headers.
00549          */
00550         httpd->headerdata = isc_mem_get(httpdmgr->mctx, HTTP_SENDGROW);
00551         if (httpd->headerdata == NULL) {
00552                 isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
00553                 isc_socket_detach(&nev->newsocket);
00554                 goto requeue;
00555         }
00556         httpd->headerlen = HTTP_SENDGROW;
00557         isc_buffer_init(&httpd->headerbuffer, httpd->headerdata,
00558                         httpd->headerlen);
00559 
00560         ISC_LIST_INIT(httpd->bufflist);
00561 
00562         isc_buffer_initnull(&httpd->bodybuffer);
00563         reset_client(httpd);
00564 
00565         r.base = (unsigned char *)httpd->recvbuf;
00566         r.length = HTTP_RECVLEN - 1;
00567         result = isc_socket_recv(httpd->sock, &r, 1, task, isc_httpd_recvdone,
00568                                  httpd);
00569         /* FIXME!!! */
00570         POST(result);
00571         NOTICE("accept queued recv on socket");
00572 
00573  requeue:
00574         result = isc_socket_accept(httpdmgr->sock, task, isc_httpd_accept,
00575                                    httpdmgr);
00576         if (result != ISC_R_SUCCESS) {
00577                 /* XXXMLG what to do?  Log failure... */
00578                 NOTICE("accept could not reaccept due to failure");
00579         }
00580 
00581  out:
00582         UNLOCK(&httpdmgr->lock);
00583 
00584         httpdmgr_destroy(httpdmgr);
00585 
00586         isc_event_free(&ev);
00587 
00588         EXIT("accept");
00589 }
00590 
00591 static isc_result_t
00592 render_404(const char *url, isc_httpdurl_t *urlinfo,
00593            const char *querystring, const char *headers, void *arg,
00594            unsigned int *retcode, const char **retmsg,
00595            const char **mimetype, isc_buffer_t *b,
00596            isc_httpdfree_t **freecb, void **freecb_args)
00597 {
00598         static char msg[] = "No such URL.";
00599 
00600         UNUSED(url);
00601         UNUSED(urlinfo);
00602         UNUSED(querystring);
00603         UNUSED(headers);
00604         UNUSED(arg);
00605 
00606         *retcode = 404;
00607         *retmsg = "No such URL";
00608         *mimetype = "text/plain";
00609         isc_buffer_reinit(b, msg, strlen(msg));
00610         isc_buffer_add(b, strlen(msg));
00611         *freecb = NULL;
00612         *freecb_args = NULL;
00613 
00614         return (ISC_R_SUCCESS);
00615 }
00616 
00617 static isc_result_t
00618 render_500(const char *url, isc_httpdurl_t *urlinfo,
00619            const char *querystring, const char *headers, void *arg,
00620            unsigned int *retcode, const char **retmsg,
00621            const char **mimetype, isc_buffer_t *b,
00622            isc_httpdfree_t **freecb, void **freecb_args)
00623 {
00624         static char msg[] = "Internal server failure.";
00625 
00626         UNUSED(url);
00627         UNUSED(urlinfo);
00628         UNUSED(querystring);
00629         UNUSED(headers);
00630         UNUSED(arg);
00631 
00632         *retcode = 500;
00633         *retmsg = "Internal server failure";
00634         *mimetype = "text/plain";
00635         isc_buffer_reinit(b, msg, strlen(msg));
00636         isc_buffer_add(b, strlen(msg));
00637         *freecb = NULL;
00638         *freecb_args = NULL;
00639 
00640         return (ISC_R_SUCCESS);
00641 }
00642 
00643 static void
00644 isc_httpd_recvdone(isc_task_t *task, isc_event_t *ev) {
00645         isc_region_t r;
00646         isc_result_t result;
00647         isc_httpd_t *httpd = ev->ev_arg;
00648         isc_socketevent_t *sev = (isc_socketevent_t *)ev;
00649         isc_httpdurl_t *url;
00650         isc_time_t now;
00651         char datebuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
00652 
00653         ENTER("recv");
00654 
00655         INSIST(ISC_HTTPD_ISRECV(httpd));
00656 
00657         if (sev->result != ISC_R_SUCCESS) {
00658                 NOTICE("recv destroying client");
00659                 destroy_client(&httpd);
00660                 goto out;
00661         }
00662 
00663         result = process_request(httpd, sev->n);
00664         if (result == ISC_R_NOTFOUND) {
00665                 if (httpd->recvlen >= HTTP_RECVLEN - 1) {
00666                         destroy_client(&httpd);
00667                         goto out;
00668                 }
00669                 r.base = (unsigned char *)httpd->recvbuf + httpd->recvlen;
00670                 r.length = HTTP_RECVLEN - httpd->recvlen - 1;
00671                 /* check return code? */
00672                 (void)isc_socket_recv(httpd->sock, &r, 1, task,
00673                                       isc_httpd_recvdone, httpd);
00674                 goto out;
00675         } else if (result != ISC_R_SUCCESS) {
00676                 destroy_client(&httpd);
00677                 goto out;
00678         }
00679 
00680         ISC_HTTPD_SETSEND(httpd);
00681 
00682         /*
00683          * XXXMLG Call function here.  Provide an add-header function
00684          * which will append the common headers to a response we generate.
00685          */
00686         isc_buffer_initnull(&httpd->bodybuffer);
00687         isc_time_now(&now);
00688         isc_time_formathttptimestamp(&now, datebuf, sizeof(datebuf));
00689         url = ISC_LIST_HEAD(httpd->mgr->urls);
00690         while (url != NULL) {
00691                 if (strcmp(httpd->url, url->url) == 0)
00692                         break;
00693                 url = ISC_LIST_NEXT(url, link);
00694         }
00695         if (url == NULL)
00696                 result = httpd->mgr->render_404(httpd->url, NULL,
00697                                                 httpd->querystring,
00698                                                 NULL, NULL,
00699                                                 &httpd->retcode,
00700                                                 &httpd->retmsg,
00701                                                 &httpd->mimetype,
00702                                                 &httpd->bodybuffer,
00703                                                 &httpd->freecb,
00704                                                 &httpd->freecb_arg);
00705         else
00706                 result = url->action(httpd->url, url,
00707                                      httpd->querystring,
00708                                      httpd->headers,
00709                                      url->action_arg,
00710                                      &httpd->retcode, &httpd->retmsg,
00711                                      &httpd->mimetype, &httpd->bodybuffer,
00712                                      &httpd->freecb, &httpd->freecb_arg);
00713         if (result != ISC_R_SUCCESS) {
00714                 result = httpd->mgr->render_500(httpd->url, url,
00715                                                 httpd->querystring,
00716                                                 NULL, NULL,
00717                                                 &httpd->retcode,
00718                                                 &httpd->retmsg,
00719                                                 &httpd->mimetype,
00720                                                 &httpd->bodybuffer,
00721                                                 &httpd->freecb,
00722                                                 &httpd->freecb_arg);
00723                 RUNTIME_CHECK(result == ISC_R_SUCCESS);
00724         }
00725 
00726         isc_httpd_response(httpd);
00727         isc_httpd_addheader(httpd, "Content-Type", httpd->mimetype);
00728         isc_httpd_addheader(httpd, "Date", datebuf);
00729         isc_httpd_addheader(httpd, "Expires", datebuf);
00730 
00731         if (url != NULL && url->isstatic) {
00732                 char loadbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
00733                 isc_time_formathttptimestamp(&url->loadtime,
00734                                              loadbuf, sizeof(loadbuf));
00735                 isc_httpd_addheader(httpd, "Last-Modified", loadbuf);
00736                 isc_httpd_addheader(httpd, "Cache-Control: public", NULL);
00737         } else {
00738                 isc_httpd_addheader(httpd, "Last-Modified", datebuf);
00739                 isc_httpd_addheader(httpd, "Pragma: no-cache", NULL);
00740                 isc_httpd_addheader(httpd, "Cache-Control: no-cache", NULL);
00741         }
00742 
00743         isc_httpd_addheader(httpd, "Server: libisc", NULL);
00744         isc_httpd_addheaderuint(httpd, "Content-Length",
00745                                 isc_buffer_usedlength(&httpd->bodybuffer));
00746         isc_httpd_endheaders(httpd);  /* done */
00747 
00748         ISC_LIST_APPEND(httpd->bufflist, &httpd->headerbuffer, link);
00749         /*
00750          * Link the data buffer into our send queue, should we have any data
00751          * rendered into it.  If no data is present, we won't do anything
00752          * with the buffer.
00753          */
00754         if (isc_buffer_length(&httpd->bodybuffer) > 0)
00755                 ISC_LIST_APPEND(httpd->bufflist, &httpd->bodybuffer, link);
00756 
00757         /* check return code? */
00758         (void)isc_socket_sendv(httpd->sock, &httpd->bufflist, task,
00759                                isc_httpd_senddone, httpd);
00760 
00761  out:
00762         isc_event_free(&ev);
00763         EXIT("recv");
00764 }
00765 
00766 void
00767 isc_httpdmgr_shutdown(isc_httpdmgr_t **httpdmgrp) {
00768         isc_httpdmgr_t *httpdmgr;
00769         isc_httpd_t *httpd;
00770         httpdmgr = *httpdmgrp;
00771         *httpdmgrp = NULL;
00772 
00773         ENTER("isc_httpdmgr_shutdown");
00774 
00775         LOCK(&httpdmgr->lock);
00776 
00777         MSETSHUTTINGDOWN(httpdmgr);
00778 
00779         isc_socket_cancel(httpdmgr->sock, httpdmgr->task, ISC_SOCKCANCEL_ALL);
00780 
00781         httpd = ISC_LIST_HEAD(httpdmgr->running);
00782         while (httpd != NULL) {
00783                 isc_socket_cancel(httpd->sock, httpdmgr->task,
00784                                   ISC_SOCKCANCEL_ALL);
00785                 httpd = ISC_LIST_NEXT(httpd, link);
00786         }
00787 
00788         UNLOCK(&httpdmgr->lock);
00789 
00790         EXIT("isc_httpdmgr_shutdown");
00791 }
00792 
00793 static isc_result_t
00794 grow_headerspace(isc_httpd_t *httpd) {
00795         char *newspace;
00796         unsigned int newlen;
00797         isc_region_t r;
00798 
00799         newlen = httpd->headerlen + HTTP_SENDGROW;
00800         if (newlen > HTTP_SEND_MAXLEN)
00801                 return (ISC_R_NOSPACE);
00802 
00803         newspace = isc_mem_get(httpd->mgr->mctx, newlen);
00804         if (newspace == NULL)
00805                 return (ISC_R_NOMEMORY);
00806         isc_buffer_region(&httpd->headerbuffer, &r);
00807         isc_buffer_reinit(&httpd->headerbuffer, newspace, newlen);
00808 
00809         isc_mem_put(httpd->mgr->mctx, r.base, r.length);
00810 
00811         return (ISC_R_SUCCESS);
00812 }
00813 
00814 isc_result_t
00815 isc_httpd_response(isc_httpd_t *httpd) {
00816         isc_result_t result;
00817         unsigned int needlen;
00818 
00819         needlen = strlen(httpd->protocol) + 1; /* protocol + space */
00820         needlen += 3 + 1;  /* room for response code, always 3 bytes */
00821         needlen += strlen(httpd->retmsg) + 2;  /* return msg + CRLF */
00822 
00823         while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
00824                 result = grow_headerspace(httpd);
00825                 if (result != ISC_R_SUCCESS)
00826                         return (result);
00827         }
00828 
00829         sprintf(isc_buffer_used(&httpd->headerbuffer), "%s %03d %s\r\n",
00830                 httpd->protocol, httpd->retcode, httpd->retmsg);
00831         isc_buffer_add(&httpd->headerbuffer, needlen);
00832 
00833         return (ISC_R_SUCCESS);
00834 }
00835 
00836 isc_result_t
00837 isc_httpd_addheader(isc_httpd_t *httpd, const char *name,
00838                     const char *val)
00839 {
00840         isc_result_t result;
00841         unsigned int needlen;
00842 
00843         needlen = strlen(name); /* name itself */
00844         if (val != NULL)
00845                 needlen += 2 + strlen(val); /* :<space> and val */
00846         needlen += 2; /* CRLF */
00847 
00848         while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
00849                 result = grow_headerspace(httpd);
00850                 if (result != ISC_R_SUCCESS)
00851                         return (result);
00852         }
00853 
00854         if (val != NULL)
00855                 sprintf(isc_buffer_used(&httpd->headerbuffer),
00856                         "%s: %s\r\n", name, val);
00857         else
00858                 sprintf(isc_buffer_used(&httpd->headerbuffer),
00859                         "%s\r\n", name);
00860 
00861         isc_buffer_add(&httpd->headerbuffer, needlen);
00862 
00863         return (ISC_R_SUCCESS);
00864 }
00865 
00866 isc_result_t
00867 isc_httpd_endheaders(isc_httpd_t *httpd) {
00868         isc_result_t result;
00869 
00870         while (isc_buffer_availablelength(&httpd->headerbuffer) < 2) {
00871                 result = grow_headerspace(httpd);
00872                 if (result != ISC_R_SUCCESS)
00873                         return (result);
00874         }
00875 
00876         sprintf(isc_buffer_used(&httpd->headerbuffer), "\r\n");
00877         isc_buffer_add(&httpd->headerbuffer, 2);
00878 
00879         return (ISC_R_SUCCESS);
00880 }
00881 
00882 isc_result_t
00883 isc_httpd_addheaderuint(isc_httpd_t *httpd, const char *name, int val) {
00884         isc_result_t result;
00885         unsigned int needlen;
00886         char buf[sizeof "18446744073709551616"];
00887 
00888         sprintf(buf, "%d", val);
00889 
00890         needlen = strlen(name); /* name itself */
00891         needlen += 2 + strlen(buf); /* :<space> and val */
00892         needlen += 2; /* CRLF */
00893 
00894         while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
00895                 result = grow_headerspace(httpd);
00896                 if (result != ISC_R_SUCCESS)
00897                         return (result);
00898         }
00899 
00900         sprintf(isc_buffer_used(&httpd->headerbuffer),
00901                 "%s: %s\r\n", name, buf);
00902 
00903         isc_buffer_add(&httpd->headerbuffer, needlen);
00904 
00905         return (ISC_R_SUCCESS);
00906 }
00907 
00908 static void
00909 isc_httpd_senddone(isc_task_t *task, isc_event_t *ev) {
00910         isc_httpd_t *httpd = ev->ev_arg;
00911         isc_region_t r;
00912         isc_socketevent_t *sev = (isc_socketevent_t *)ev;
00913 
00914         ENTER("senddone");
00915         INSIST(ISC_HTTPD_ISSEND(httpd));
00916 
00917         /*
00918          * First, unlink our header buffer from the socket's bufflist.  This
00919          * is sort of an evil hack, since we know our buffer will be there,
00920          * and we know it's address, so we can just remove it directly.
00921          */
00922         NOTICE("senddone unlinked header");
00923         ISC_LIST_UNLINK(sev->bufferlist, &httpd->headerbuffer, link);
00924 
00925         /*
00926          * We will always want to clean up our receive buffer, even if we
00927          * got an error on send or we are shutting down.
00928          *
00929          * We will pass in the buffer only if there is data in it.  If
00930          * there is no data, we will pass in a NULL.
00931          */
00932         if (httpd->freecb != NULL) {
00933                 isc_buffer_t *b = NULL;
00934                 if (isc_buffer_length(&httpd->bodybuffer) > 0) {
00935                         b = &httpd->bodybuffer;
00936                         httpd->freecb(b, httpd->freecb_arg);
00937                 }
00938                 NOTICE("senddone free callback performed");
00939         }
00940         if (ISC_LINK_LINKED(&httpd->bodybuffer, link)) {
00941                 ISC_LIST_UNLINK(sev->bufferlist, &httpd->bodybuffer, link);
00942                 NOTICE("senddone body buffer unlinked");
00943         }
00944 
00945         if (sev->result != ISC_R_SUCCESS) {
00946                 destroy_client(&httpd);
00947                 goto out;
00948         }
00949 
00950         if ((httpd->flags & HTTPD_CLOSE) != 0) {
00951                 destroy_client(&httpd);
00952                 goto out;
00953         }
00954 
00955         ISC_HTTPD_SETRECV(httpd);
00956 
00957         NOTICE("senddone restarting recv on socket");
00958 
00959         reset_client(httpd);
00960 
00961         r.base = (unsigned char *)httpd->recvbuf;
00962         r.length = HTTP_RECVLEN - 1;
00963         /* check return code? */
00964         (void)isc_socket_recv(httpd->sock, &r, 1, task,
00965                               isc_httpd_recvdone, httpd);
00966 
00967 out:
00968         isc_event_free(&ev);
00969         EXIT("senddone");
00970 }
00971 
00972 static void
00973 reset_client(isc_httpd_t *httpd) {
00974         /*
00975          * Catch errors here.  We MUST be in RECV mode, and we MUST NOT have
00976          * any outstanding buffers.  If we have buffers, we have a leak.
00977          */
00978         INSIST(ISC_HTTPD_ISRECV(httpd));
00979         INSIST(!ISC_LINK_LINKED(&httpd->headerbuffer, link));
00980         INSIST(!ISC_LINK_LINKED(&httpd->bodybuffer, link));
00981 
00982         httpd->recvbuf[0] = 0;
00983         httpd->recvlen = 0;
00984         httpd->headers = NULL;
00985         httpd->method = ISC_HTTPD_METHODUNKNOWN;
00986         httpd->url = NULL;
00987         httpd->querystring = NULL;
00988         httpd->protocol = NULL;
00989         httpd->flags = 0;
00990 
00991         isc_buffer_clear(&httpd->headerbuffer);
00992         isc_buffer_invalidate(&httpd->bodybuffer);
00993 }
00994 
00995 isc_result_t
00996 isc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url,
00997                     isc_httpdaction_t *func, void *arg)
00998 {
00999         return (isc_httpdmgr_addurl2(httpdmgr, url, ISC_FALSE, func, arg));
01000 }
01001 
01002 isc_result_t
01003 isc_httpdmgr_addurl2(isc_httpdmgr_t *httpdmgr, const char *url,
01004                      isc_boolean_t isstatic,
01005                      isc_httpdaction_t *func, void *arg)
01006 {
01007         isc_httpdurl_t *item;
01008 
01009         if (url == NULL) {
01010                 httpdmgr->render_404 = func;
01011                 return (ISC_R_SUCCESS);
01012         }
01013 
01014         item = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpdurl_t));
01015         if (item == NULL)
01016                 return (ISC_R_NOMEMORY);
01017 
01018         item->url = isc_mem_strdup(httpdmgr->mctx, url);
01019         if (item->url == NULL) {
01020                 isc_mem_put(httpdmgr->mctx, item, sizeof(isc_httpdurl_t));
01021                 return (ISC_R_NOMEMORY);
01022         }
01023 
01024         item->action = func;
01025         item->action_arg = arg;
01026         item->isstatic = isstatic;
01027         isc_time_now(&item->loadtime);
01028 
01029         ISC_LINK_INIT(item, link);
01030         ISC_LIST_APPEND(httpdmgr->urls, item, link);
01031 
01032         return (ISC_R_SUCCESS);
01033 }

Generated on Tue Apr 28 17:41:04 2015 by Doxygen 1.5.4 for BIND9 Internals 9.11.0pre-alpha