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