Annotation of embedaddon/ntp/lib/isc/httpd.c, revision 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>