Annotation of embedaddon/ntp/lib/isc/httpd.c, revision 1.1.1.1

1.1       misho       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.16 2008/08/08 05:06:49 marka 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>