1258945Sroberto/* 2280849Scy * Copyright (C) 2006-2008, 2010-2012 Internet Systems Consortium, Inc. ("ISC") 3258945Sroberto * 4258945Sroberto * Permission to use, copy, modify, and/or distribute this software for any 5258945Sroberto * purpose with or without fee is hereby granted, provided that the above 6258945Sroberto * copyright notice and this permission notice appear in all copies. 7258945Sroberto * 8258945Sroberto * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 9258945Sroberto * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10258945Sroberto * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 11258945Sroberto * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12258945Sroberto * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13258945Sroberto * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14258945Sroberto * PERFORMANCE OF THIS SOFTWARE. 15258945Sroberto */ 16258945Sroberto 17280849Scy/* $Id$ */ 18258945Sroberto 19258945Sroberto/*! \file */ 20258945Sroberto 21258945Sroberto#include <config.h> 22258945Sroberto 23258945Sroberto#include <isc/buffer.h> 24258945Sroberto#include <isc/httpd.h> 25258945Sroberto#include <isc/mem.h> 26258945Sroberto#include <isc/socket.h> 27258945Sroberto#include <isc/string.h> 28258945Sroberto#include <isc/task.h> 29258945Sroberto#include <isc/util.h> 30258945Sroberto 31258945Sroberto#include <string.h> 32258945Sroberto 33258945Sroberto/*% 34258945Sroberto * TODO: 35258945Sroberto * 36258945Sroberto * o Put in better checks to make certain things are passed in correctly. 37258945Sroberto * This includes a magic number for externally-visible structures, 38258945Sroberto * checking for NULL-ness before dereferencing, etc. 39258945Sroberto * o Make the URL processing external functions which will fill-in a buffer 40258945Sroberto * structure we provide, or return an error and we will render a generic 41258945Sroberto * page and close the client. 42258945Sroberto */ 43258945Sroberto 44258945Sroberto#define MSHUTTINGDOWN(cm) ((cm->flags & ISC_HTTPDMGR_FLAGSHUTTINGDOWN) != 0) 45258945Sroberto#define MSETSHUTTINGDOWN(cm) (cm->flags |= ISC_HTTPDMGR_FLAGSHUTTINGDOWN) 46258945Sroberto 47258945Sroberto#ifdef DEBUG_HTTPD 48258945Sroberto#define ENTER(x) do { fprintf(stderr, "ENTER %s\n", (x)); } while (0) 49258945Sroberto#define EXIT(x) do { fprintf(stderr, "EXIT %s\n", (x)); } while (0) 50258945Sroberto#define NOTICE(x) do { fprintf(stderr, "NOTICE %s\n", (x)); } while (0) 51258945Sroberto#else 52258945Sroberto#define ENTER(x) do { } while(0) 53258945Sroberto#define EXIT(x) do { } while(0) 54258945Sroberto#define NOTICE(x) do { } while(0) 55258945Sroberto#endif 56258945Sroberto 57258945Sroberto#define HTTP_RECVLEN 1024 58258945Sroberto#define HTTP_SENDGROW 1024 59258945Sroberto#define HTTP_SEND_MAXLEN 10240 60258945Sroberto 61258945Sroberto/*% 62258945Sroberto * HTTP urls. These are the URLs we manage, and the function to call to 63258945Sroberto * provide the data for it. We pass in the base url (so the same function 64258945Sroberto * can handle multiple requests), and a structure to fill in to return a 65258945Sroberto * result to the client. We also pass in a pointer to be filled in for 66258945Sroberto * the data cleanup function. 67258945Sroberto */ 68258945Srobertostruct isc_httpdurl { 69258945Sroberto char *url; 70258945Sroberto isc_httpdaction_t *action; 71258945Sroberto void *action_arg; 72258945Sroberto ISC_LINK(isc_httpdurl_t) link; 73258945Sroberto}; 74258945Sroberto 75258945Sroberto#define HTTPD_CLOSE 0x0001 /* Got a Connection: close header */ 76258945Sroberto#define HTTPD_FOUNDHOST 0x0002 /* Got a Host: header */ 77258945Sroberto 78258945Sroberto/*% http client */ 79258945Srobertostruct isc_httpd { 80258945Sroberto isc_httpdmgr_t *mgr; /*%< our parent */ 81258945Sroberto ISC_LINK(isc_httpd_t) link; 82258945Sroberto unsigned int state; 83258945Sroberto isc_socket_t *sock; 84258945Sroberto 85258945Sroberto /*% 86258945Sroberto * Received data state. 87258945Sroberto */ 88258945Sroberto char recvbuf[HTTP_RECVLEN]; /*%< receive buffer */ 89258945Sroberto isc_uint32_t recvlen; /*%< length recv'd */ 90258945Sroberto unsigned int method; 91258945Sroberto char *url; 92258945Sroberto char *querystring; 93258945Sroberto char *protocol; 94258945Sroberto 95258945Sroberto /* 96258945Sroberto * Flags on the httpd client. 97258945Sroberto */ 98258945Sroberto int flags; 99258945Sroberto 100258945Sroberto /*% 101258945Sroberto * Transmit data state. 102258945Sroberto * 103258945Sroberto * This is the data buffer we will transmit. 104258945Sroberto * 105258945Sroberto * This free function pointer is filled in by the rendering function 106258945Sroberto * we call. The free function is called after the data is transmitted 107258945Sroberto * to the client. 108258945Sroberto * 109258945Sroberto * The bufflist is the list of buffers we are currently transmitting. 110258945Sroberto * The headerdata is where we render our headers to. If we run out of 111258945Sroberto * space when rendering a header, we will change the size of our 112258945Sroberto * buffer. We will not free it until we are finished, and will 113258945Sroberto * allocate an additional HTTP_SENDGROW bytes per header space grow. 114258945Sroberto * 115258945Sroberto * We currently use two buffers total, one for the headers (which 116258945Sroberto * we manage) and another for the client to fill in (which it manages, 117258945Sroberto * it provides the space for it, etc) -- we will pass that buffer 118258945Sroberto * structure back to the caller, who is responsible for managing the 119258945Sroberto * space it may have allocated as backing store for it. This second 120258945Sroberto * buffer is bodybuffer, and we only allocate the buffer itself, not 121258945Sroberto * the backing store. 122258945Sroberto */ 123258945Sroberto isc_bufferlist_t bufflist; 124258945Sroberto char *headerdata; /*%< send header buf */ 125258945Sroberto unsigned int headerlen; /*%< current header buffer size */ 126258945Sroberto isc_buffer_t headerbuffer; 127258945Sroberto 128258945Sroberto const char *mimetype; 129258945Sroberto unsigned int retcode; 130258945Sroberto const char *retmsg; 131258945Sroberto isc_buffer_t bodybuffer; 132258945Sroberto isc_httpdfree_t *freecb; 133258945Sroberto void *freecb_arg; 134258945Sroberto}; 135258945Sroberto 136258945Sroberto/*% lightweight socket manager for httpd output */ 137258945Srobertostruct isc_httpdmgr { 138258945Sroberto isc_mem_t *mctx; 139258945Sroberto isc_socket_t *sock; /*%< listening socket */ 140258945Sroberto isc_task_t *task; /*%< owning task */ 141258945Sroberto isc_timermgr_t *timermgr; 142258945Sroberto 143258945Sroberto isc_httpdclientok_t *client_ok; /*%< client validator */ 144258945Sroberto isc_httpdondestroy_t *ondestroy; /*%< cleanup callback */ 145258945Sroberto void *cb_arg; /*%< argument for the above */ 146258945Sroberto 147258945Sroberto unsigned int flags; 148258945Sroberto ISC_LIST(isc_httpd_t) running; /*%< running clients */ 149258945Sroberto 150258945Sroberto isc_mutex_t lock; 151258945Sroberto 152258945Sroberto ISC_LIST(isc_httpdurl_t) urls; /*%< urls we manage */ 153258945Sroberto isc_httpdaction_t *render_404; 154280849Scy isc_httpdaction_t *render_500; 155258945Sroberto}; 156258945Sroberto 157258945Sroberto/*% 158258945Sroberto * HTTP methods. 159258945Sroberto */ 160258945Sroberto#define ISC_HTTPD_METHODUNKNOWN 0 161258945Sroberto#define ISC_HTTPD_METHODGET 1 162258945Sroberto#define ISC_HTTPD_METHODPOST 2 163258945Sroberto 164258945Sroberto/*% 165258945Sroberto * Client states. 166258945Sroberto * 167258945Sroberto * _IDLE The client is not doing anything at all. This state should 168258945Sroberto * only occur just after creation, and just before being 169258945Sroberto * destroyed. 170258945Sroberto * 171258945Sroberto * _RECV The client is waiting for data after issuing a socket recv(). 172258945Sroberto * 173258945Sroberto * _RECVDONE Data has been received, and is being processed. 174258945Sroberto * 175258945Sroberto * _SEND All data for a response has completed, and a reply was 176258945Sroberto * sent via a socket send() call. 177258945Sroberto * 178258945Sroberto * _SENDDONE Send is completed. 179258945Sroberto * 180258945Sroberto * Badly formatted state table: 181258945Sroberto * 182258945Sroberto * IDLE -> RECV when client has a recv() queued. 183258945Sroberto * 184258945Sroberto * RECV -> RECVDONE when recvdone event received. 185258945Sroberto * 186258945Sroberto * RECVDONE -> SEND if the data for a reply is at hand. 187258945Sroberto * 188258945Sroberto * SEND -> RECV when a senddone event was received. 189258945Sroberto * 190258945Sroberto * At any time -> RECV on error. If RECV fails, the client will 191258945Sroberto * self-destroy, closing the socket and freeing memory. 192258945Sroberto */ 193258945Sroberto#define ISC_HTTPD_STATEIDLE 0 194258945Sroberto#define ISC_HTTPD_STATERECV 1 195258945Sroberto#define ISC_HTTPD_STATERECVDONE 2 196258945Sroberto#define ISC_HTTPD_STATESEND 3 197258945Sroberto#define ISC_HTTPD_STATESENDDONE 4 198258945Sroberto 199258945Sroberto#define ISC_HTTPD_ISRECV(c) ((c)->state == ISC_HTTPD_STATERECV) 200258945Sroberto#define ISC_HTTPD_ISRECVDONE(c) ((c)->state == ISC_HTTPD_STATERECVDONE) 201258945Sroberto#define ISC_HTTPD_ISSEND(c) ((c)->state == ISC_HTTPD_STATESEND) 202258945Sroberto#define ISC_HTTPD_ISSENDDONE(c) ((c)->state == ISC_HTTPD_STATESENDDONE) 203258945Sroberto 204258945Sroberto/*% 205258945Sroberto * Overall magic test that means we're not idle. 206258945Sroberto */ 207258945Sroberto#define ISC_HTTPD_SETRECV(c) ((c)->state = ISC_HTTPD_STATERECV) 208258945Sroberto#define ISC_HTTPD_SETRECVDONE(c) ((c)->state = ISC_HTTPD_STATERECVDONE) 209258945Sroberto#define ISC_HTTPD_SETSEND(c) ((c)->state = ISC_HTTPD_STATESEND) 210258945Sroberto#define ISC_HTTPD_SETSENDDONE(c) ((c)->state = ISC_HTTPD_STATESENDDONE) 211258945Sroberto 212258945Srobertostatic void isc_httpd_accept(isc_task_t *, isc_event_t *); 213258945Srobertostatic void isc_httpd_recvdone(isc_task_t *, isc_event_t *); 214258945Srobertostatic void isc_httpd_senddone(isc_task_t *, isc_event_t *); 215258945Srobertostatic void destroy_client(isc_httpd_t **); 216258945Srobertostatic isc_result_t process_request(isc_httpd_t *, int); 217258945Srobertostatic void httpdmgr_destroy(isc_httpdmgr_t *); 218258945Srobertostatic isc_result_t grow_headerspace(isc_httpd_t *); 219258945Srobertostatic void reset_client(isc_httpd_t *httpd); 220258945Srobertostatic isc_result_t render_404(const char *, const char *, 221258945Sroberto void *, 222258945Sroberto unsigned int *, const char **, 223258945Sroberto const char **, isc_buffer_t *, 224258945Sroberto isc_httpdfree_t **, void **); 225280849Scystatic isc_result_t render_500(const char *, const char *, 226280849Scy void *, 227280849Scy unsigned int *, const char **, 228280849Scy const char **, isc_buffer_t *, 229280849Scy isc_httpdfree_t **, void **); 230258945Sroberto 231258945Srobertostatic void 232258945Srobertodestroy_client(isc_httpd_t **httpdp) 233258945Sroberto{ 234258945Sroberto isc_httpd_t *httpd = *httpdp; 235258945Sroberto isc_httpdmgr_t *httpdmgr = httpd->mgr; 236258945Sroberto 237258945Sroberto *httpdp = NULL; 238258945Sroberto 239258945Sroberto LOCK(&httpdmgr->lock); 240258945Sroberto 241258945Sroberto isc_socket_detach(&httpd->sock); 242258945Sroberto ISC_LIST_UNLINK(httpdmgr->running, httpd, link); 243258945Sroberto 244258945Sroberto if (httpd->headerlen > 0) 245258945Sroberto isc_mem_put(httpdmgr->mctx, httpd->headerdata, 246258945Sroberto httpd->headerlen); 247258945Sroberto 248258945Sroberto isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t)); 249258945Sroberto 250258945Sroberto UNLOCK(&httpdmgr->lock); 251258945Sroberto 252258945Sroberto httpdmgr_destroy(httpdmgr); 253258945Sroberto} 254258945Sroberto 255258945Srobertoisc_result_t 256258945Srobertoisc_httpdmgr_create(isc_mem_t *mctx, isc_socket_t *sock, isc_task_t *task, 257258945Sroberto isc_httpdclientok_t *client_ok, 258258945Sroberto isc_httpdondestroy_t *ondestroy, void *cb_arg, 259258945Sroberto isc_timermgr_t *tmgr, isc_httpdmgr_t **httpdp) 260258945Sroberto{ 261258945Sroberto isc_result_t result; 262258945Sroberto isc_httpdmgr_t *httpd; 263258945Sroberto 264258945Sroberto REQUIRE(mctx != NULL); 265258945Sroberto REQUIRE(sock != NULL); 266258945Sroberto REQUIRE(task != NULL); 267258945Sroberto REQUIRE(tmgr != NULL); 268258945Sroberto REQUIRE(httpdp != NULL && *httpdp == NULL); 269258945Sroberto 270258945Sroberto httpd = isc_mem_get(mctx, sizeof(isc_httpdmgr_t)); 271258945Sroberto if (httpd == NULL) 272258945Sroberto return (ISC_R_NOMEMORY); 273258945Sroberto 274258945Sroberto result = isc_mutex_init(&httpd->lock); 275258945Sroberto if (result != ISC_R_SUCCESS) { 276258945Sroberto isc_mem_put(mctx, httpd, sizeof(isc_httpdmgr_t)); 277258945Sroberto return (result); 278258945Sroberto } 279258945Sroberto httpd->mctx = NULL; 280258945Sroberto isc_mem_attach(mctx, &httpd->mctx); 281258945Sroberto httpd->sock = NULL; 282258945Sroberto isc_socket_attach(sock, &httpd->sock); 283258945Sroberto httpd->task = NULL; 284258945Sroberto isc_task_attach(task, &httpd->task); 285258945Sroberto httpd->timermgr = tmgr; /* XXXMLG no attach function? */ 286258945Sroberto httpd->client_ok = client_ok; 287258945Sroberto httpd->ondestroy = ondestroy; 288258945Sroberto httpd->cb_arg = cb_arg; 289258945Sroberto 290258945Sroberto ISC_LIST_INIT(httpd->running); 291258945Sroberto ISC_LIST_INIT(httpd->urls); 292258945Sroberto 293258945Sroberto /* XXXMLG ignore errors on isc_socket_listen() */ 294258945Sroberto result = isc_socket_listen(sock, SOMAXCONN); 295258945Sroberto if (result != ISC_R_SUCCESS) { 296258945Sroberto UNEXPECTED_ERROR(__FILE__, __LINE__, 297258945Sroberto "isc_socket_listen() failed: %s", 298258945Sroberto isc_result_totext(result)); 299258945Sroberto goto cleanup; 300258945Sroberto } 301258945Sroberto 302258945Sroberto (void)isc_socket_filter(sock, "httpready"); 303258945Sroberto 304258945Sroberto result = isc_socket_accept(sock, task, isc_httpd_accept, httpd); 305258945Sroberto if (result != ISC_R_SUCCESS) 306258945Sroberto goto cleanup; 307258945Sroberto 308258945Sroberto httpd->render_404 = render_404; 309280849Scy httpd->render_500 = render_500; 310258945Sroberto 311258945Sroberto *httpdp = httpd; 312258945Sroberto return (ISC_R_SUCCESS); 313258945Sroberto 314258945Sroberto cleanup: 315258945Sroberto isc_task_detach(&httpd->task); 316258945Sroberto isc_socket_detach(&httpd->sock); 317258945Sroberto isc_mem_detach(&httpd->mctx); 318280849Scy (void)isc_mutex_destroy(&httpd->lock); 319258945Sroberto isc_mem_put(mctx, httpd, sizeof(isc_httpdmgr_t)); 320258945Sroberto return (result); 321258945Sroberto} 322258945Sroberto 323258945Srobertostatic void 324258945Srobertohttpdmgr_destroy(isc_httpdmgr_t *httpdmgr) 325258945Sroberto{ 326258945Sroberto isc_mem_t *mctx; 327258945Sroberto isc_httpdurl_t *url; 328258945Sroberto 329258945Sroberto ENTER("httpdmgr_destroy"); 330258945Sroberto 331258945Sroberto LOCK(&httpdmgr->lock); 332258945Sroberto 333258945Sroberto if (!MSHUTTINGDOWN(httpdmgr)) { 334258945Sroberto NOTICE("httpdmgr_destroy not shutting down yet"); 335258945Sroberto UNLOCK(&httpdmgr->lock); 336258945Sroberto return; 337258945Sroberto } 338258945Sroberto 339258945Sroberto /* 340258945Sroberto * If all clients are not shut down, don't do anything yet. 341258945Sroberto */ 342258945Sroberto if (!ISC_LIST_EMPTY(httpdmgr->running)) { 343258945Sroberto NOTICE("httpdmgr_destroy clients still active"); 344258945Sroberto UNLOCK(&httpdmgr->lock); 345258945Sroberto return; 346258945Sroberto } 347258945Sroberto 348258945Sroberto NOTICE("httpdmgr_destroy detaching socket, task, and timermgr"); 349258945Sroberto 350258945Sroberto isc_socket_detach(&httpdmgr->sock); 351258945Sroberto isc_task_detach(&httpdmgr->task); 352258945Sroberto httpdmgr->timermgr = NULL; 353258945Sroberto 354258945Sroberto /* 355258945Sroberto * Clear out the list of all actions we know about. Just free the 356258945Sroberto * memory. 357258945Sroberto */ 358258945Sroberto url = ISC_LIST_HEAD(httpdmgr->urls); 359258945Sroberto while (url != NULL) { 360258945Sroberto isc_mem_free(httpdmgr->mctx, url->url); 361258945Sroberto ISC_LIST_UNLINK(httpdmgr->urls, url, link); 362258945Sroberto isc_mem_put(httpdmgr->mctx, url, sizeof(isc_httpdurl_t)); 363258945Sroberto url = ISC_LIST_HEAD(httpdmgr->urls); 364258945Sroberto } 365258945Sroberto 366258945Sroberto UNLOCK(&httpdmgr->lock); 367280849Scy (void)isc_mutex_destroy(&httpdmgr->lock); 368258945Sroberto 369258945Sroberto if (httpdmgr->ondestroy != NULL) 370258945Sroberto (httpdmgr->ondestroy)(httpdmgr->cb_arg); 371258945Sroberto 372258945Sroberto mctx = httpdmgr->mctx; 373258945Sroberto isc_mem_putanddetach(&mctx, httpdmgr, sizeof(isc_httpdmgr_t)); 374258945Sroberto 375258945Sroberto EXIT("httpdmgr_destroy"); 376258945Sroberto} 377258945Sroberto 378258945Sroberto#define LENGTHOK(s) (httpd->recvbuf - (s) < (int)httpd->recvlen) 379258945Sroberto#define BUFLENOK(s) (httpd->recvbuf - (s) < HTTP_RECVLEN) 380258945Sroberto 381258945Srobertostatic isc_result_t 382258945Srobertoprocess_request(isc_httpd_t *httpd, int length) 383258945Sroberto{ 384258945Sroberto char *s; 385258945Sroberto char *p; 386258945Sroberto int delim; 387258945Sroberto 388258945Sroberto ENTER("request"); 389258945Sroberto 390258945Sroberto httpd->recvlen += length; 391258945Sroberto 392258945Sroberto httpd->recvbuf[httpd->recvlen] = 0; 393258945Sroberto 394258945Sroberto /* 395258945Sroberto * If we don't find a blank line in our buffer, return that we need 396258945Sroberto * more data. 397258945Sroberto */ 398258945Sroberto s = strstr(httpd->recvbuf, "\r\n\r\n"); 399258945Sroberto delim = 1; 400258945Sroberto if (s == NULL) { 401258945Sroberto s = strstr(httpd->recvbuf, "\n\n"); 402258945Sroberto delim = 2; 403258945Sroberto } 404258945Sroberto if (s == NULL) 405258945Sroberto return (ISC_R_NOTFOUND); 406258945Sroberto 407258945Sroberto /* 408258945Sroberto * Determine if this is a POST or GET method. Any other values will 409258945Sroberto * cause an error to be returned. 410258945Sroberto */ 411258945Sroberto if (strncmp(httpd->recvbuf, "GET ", 4) == 0) { 412258945Sroberto httpd->method = ISC_HTTPD_METHODGET; 413258945Sroberto p = httpd->recvbuf + 4; 414258945Sroberto } else if (strncmp(httpd->recvbuf, "POST ", 5) == 0) { 415258945Sroberto httpd->method = ISC_HTTPD_METHODPOST; 416258945Sroberto p = httpd->recvbuf + 5; 417258945Sroberto } else { 418258945Sroberto return (ISC_R_RANGE); 419258945Sroberto } 420258945Sroberto 421258945Sroberto /* 422258945Sroberto * From now on, p is the start of our buffer. 423258945Sroberto */ 424258945Sroberto 425258945Sroberto /* 426258945Sroberto * Extract the URL. 427258945Sroberto */ 428258945Sroberto s = p; 429258945Sroberto while (LENGTHOK(s) && BUFLENOK(s) && 430258945Sroberto (*s != '\n' && *s != '\r' && *s != '\0' && *s != ' ')) 431258945Sroberto s++; 432258945Sroberto if (!LENGTHOK(s)) 433258945Sroberto return (ISC_R_NOTFOUND); 434258945Sroberto if (!BUFLENOK(s)) 435258945Sroberto return (ISC_R_NOMEMORY); 436258945Sroberto *s = 0; 437258945Sroberto 438258945Sroberto /* 439258945Sroberto * Make the URL relative. 440258945Sroberto */ 441258945Sroberto if ((strncmp(p, "http:/", 6) == 0) 442258945Sroberto || (strncmp(p, "https:/", 7) == 0)) { 443258945Sroberto /* Skip first / */ 444258945Sroberto while (*p != '/' && *p != 0) 445258945Sroberto p++; 446258945Sroberto if (*p == 0) 447258945Sroberto return (ISC_R_RANGE); 448258945Sroberto p++; 449258945Sroberto /* Skip second / */ 450258945Sroberto while (*p != '/' && *p != 0) 451258945Sroberto p++; 452258945Sroberto if (*p == 0) 453258945Sroberto return (ISC_R_RANGE); 454258945Sroberto p++; 455258945Sroberto /* Find third / */ 456258945Sroberto while (*p != '/' && *p != 0) 457258945Sroberto p++; 458258945Sroberto if (*p == 0) { 459258945Sroberto p--; 460258945Sroberto *p = '/'; 461258945Sroberto } 462258945Sroberto } 463258945Sroberto 464258945Sroberto httpd->url = p; 465258945Sroberto p = s + delim; 466258945Sroberto s = p; 467258945Sroberto 468258945Sroberto /* 469258945Sroberto * Now, see if there is a ? mark in the URL. If so, this is 470258945Sroberto * part of the query string, and we will split it from the URL. 471258945Sroberto */ 472258945Sroberto httpd->querystring = strchr(httpd->url, '?'); 473258945Sroberto if (httpd->querystring != NULL) { 474258945Sroberto *(httpd->querystring) = 0; 475258945Sroberto httpd->querystring++; 476258945Sroberto } 477258945Sroberto 478258945Sroberto /* 479258945Sroberto * Extract the HTTP/1.X protocol. We will bounce on anything but 480258945Sroberto * HTTP/1.1 for now. 481258945Sroberto */ 482258945Sroberto while (LENGTHOK(s) && BUFLENOK(s) && 483258945Sroberto (*s != '\n' && *s != '\r' && *s != '\0')) 484258945Sroberto s++; 485258945Sroberto if (!LENGTHOK(s)) 486258945Sroberto return (ISC_R_NOTFOUND); 487258945Sroberto if (!BUFLENOK(s)) 488258945Sroberto return (ISC_R_NOMEMORY); 489258945Sroberto *s = 0; 490258945Sroberto if ((strncmp(p, "HTTP/1.0", 8) != 0) 491258945Sroberto && (strncmp(p, "HTTP/1.1", 8) != 0)) 492258945Sroberto return (ISC_R_RANGE); 493258945Sroberto httpd->protocol = p; 494258945Sroberto p = s + 1; 495258945Sroberto s = p; 496258945Sroberto 497258945Sroberto if (strstr(s, "Connection: close") != NULL) 498258945Sroberto httpd->flags |= HTTPD_CLOSE; 499258945Sroberto 500258945Sroberto if (strstr(s, "Host: ") != NULL) 501258945Sroberto httpd->flags |= HTTPD_FOUNDHOST; 502258945Sroberto 503258945Sroberto /* 504258945Sroberto * Standards compliance hooks here. 505258945Sroberto */ 506258945Sroberto if (strcmp(httpd->protocol, "HTTP/1.1") == 0 507258945Sroberto && ((httpd->flags & HTTPD_FOUNDHOST) == 0)) 508258945Sroberto return (ISC_R_RANGE); 509258945Sroberto 510258945Sroberto EXIT("request"); 511258945Sroberto 512258945Sroberto return (ISC_R_SUCCESS); 513258945Sroberto} 514258945Sroberto 515258945Srobertostatic void 516258945Srobertoisc_httpd_accept(isc_task_t *task, isc_event_t *ev) 517258945Sroberto{ 518258945Sroberto isc_result_t result; 519258945Sroberto isc_httpdmgr_t *httpdmgr = ev->ev_arg; 520258945Sroberto isc_httpd_t *httpd; 521258945Sroberto isc_region_t r; 522258945Sroberto isc_socket_newconnev_t *nev = (isc_socket_newconnev_t *)ev; 523258945Sroberto isc_sockaddr_t peeraddr; 524258945Sroberto 525258945Sroberto ENTER("accept"); 526258945Sroberto 527258945Sroberto LOCK(&httpdmgr->lock); 528258945Sroberto if (MSHUTTINGDOWN(httpdmgr)) { 529258945Sroberto NOTICE("accept shutting down, goto out"); 530258945Sroberto goto out; 531258945Sroberto } 532258945Sroberto 533258945Sroberto if (nev->result == ISC_R_CANCELED) { 534258945Sroberto NOTICE("accept canceled, goto out"); 535258945Sroberto goto out; 536258945Sroberto } 537258945Sroberto 538258945Sroberto if (nev->result != ISC_R_SUCCESS) { 539258945Sroberto /* XXXMLG log failure */ 540258945Sroberto NOTICE("accept returned failure, goto requeue"); 541258945Sroberto goto requeue; 542258945Sroberto } 543258945Sroberto 544258945Sroberto (void)isc_socket_getpeername(nev->newsocket, &peeraddr); 545258945Sroberto if (httpdmgr->client_ok != NULL && 546258945Sroberto !(httpdmgr->client_ok)(&peeraddr, httpdmgr->cb_arg)) { 547258945Sroberto isc_socket_detach(&nev->newsocket); 548258945Sroberto goto requeue; 549258945Sroberto } 550258945Sroberto 551258945Sroberto httpd = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpd_t)); 552258945Sroberto if (httpd == NULL) { 553258945Sroberto /* XXXMLG log failure */ 554258945Sroberto NOTICE("accept failed to allocate memory, goto requeue"); 555258945Sroberto isc_socket_detach(&nev->newsocket); 556258945Sroberto goto requeue; 557258945Sroberto } 558258945Sroberto 559258945Sroberto httpd->mgr = httpdmgr; 560258945Sroberto ISC_LINK_INIT(httpd, link); 561258945Sroberto ISC_LIST_APPEND(httpdmgr->running, httpd, link); 562258945Sroberto ISC_HTTPD_SETRECV(httpd); 563258945Sroberto httpd->sock = nev->newsocket; 564258945Sroberto isc_socket_setname(httpd->sock, "httpd", NULL); 565258945Sroberto httpd->flags = 0; 566258945Sroberto 567258945Sroberto /* 568258945Sroberto * Initialize the buffer for our headers. 569258945Sroberto */ 570258945Sroberto httpd->headerdata = isc_mem_get(httpdmgr->mctx, HTTP_SENDGROW); 571258945Sroberto if (httpd->headerdata == NULL) { 572258945Sroberto isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t)); 573258945Sroberto isc_socket_detach(&nev->newsocket); 574258945Sroberto goto requeue; 575258945Sroberto } 576258945Sroberto httpd->headerlen = HTTP_SENDGROW; 577258945Sroberto isc_buffer_init(&httpd->headerbuffer, httpd->headerdata, 578258945Sroberto httpd->headerlen); 579258945Sroberto 580258945Sroberto ISC_LIST_INIT(httpd->bufflist); 581258945Sroberto 582258945Sroberto isc_buffer_initnull(&httpd->bodybuffer); 583258945Sroberto reset_client(httpd); 584258945Sroberto 585258945Sroberto r.base = (unsigned char *)httpd->recvbuf; 586258945Sroberto r.length = HTTP_RECVLEN - 1; 587258945Sroberto result = isc_socket_recv(httpd->sock, &r, 1, task, isc_httpd_recvdone, 588258945Sroberto httpd); 589280849Scy /* FIXME!!! */ 590280849Scy POST(result); 591258945Sroberto NOTICE("accept queued recv on socket"); 592258945Sroberto 593258945Sroberto requeue: 594258945Sroberto result = isc_socket_accept(httpdmgr->sock, task, isc_httpd_accept, 595258945Sroberto httpdmgr); 596258945Sroberto if (result != ISC_R_SUCCESS) { 597258945Sroberto /* XXXMLG what to do? Log failure... */ 598258945Sroberto NOTICE("accept could not reaccept due to failure"); 599258945Sroberto } 600258945Sroberto 601258945Sroberto out: 602258945Sroberto UNLOCK(&httpdmgr->lock); 603258945Sroberto 604258945Sroberto httpdmgr_destroy(httpdmgr); 605258945Sroberto 606258945Sroberto isc_event_free(&ev); 607258945Sroberto 608258945Sroberto EXIT("accept"); 609258945Sroberto} 610258945Sroberto 611258945Srobertostatic isc_result_t 612258945Srobertorender_404(const char *url, const char *querystring, 613258945Sroberto void *arg, 614258945Sroberto unsigned int *retcode, const char **retmsg, 615258945Sroberto const char **mimetype, isc_buffer_t *b, 616258945Sroberto isc_httpdfree_t **freecb, void **freecb_args) 617258945Sroberto{ 618258945Sroberto static char msg[] = "No such URL."; 619258945Sroberto 620258945Sroberto UNUSED(url); 621258945Sroberto UNUSED(querystring); 622258945Sroberto UNUSED(arg); 623258945Sroberto 624258945Sroberto *retcode = 404; 625258945Sroberto *retmsg = "No such URL"; 626258945Sroberto *mimetype = "text/plain"; 627258945Sroberto isc_buffer_reinit(b, msg, strlen(msg)); 628258945Sroberto isc_buffer_add(b, strlen(msg)); 629258945Sroberto *freecb = NULL; 630258945Sroberto *freecb_args = NULL; 631258945Sroberto 632258945Sroberto return (ISC_R_SUCCESS); 633258945Sroberto} 634258945Sroberto 635280849Scystatic isc_result_t 636280849Scyrender_500(const char *url, const char *querystring, 637280849Scy void *arg, 638280849Scy unsigned int *retcode, const char **retmsg, 639280849Scy const char **mimetype, isc_buffer_t *b, 640280849Scy isc_httpdfree_t **freecb, void **freecb_args) 641280849Scy{ 642280849Scy static char msg[] = "Internal server failure."; 643280849Scy 644280849Scy UNUSED(url); 645280849Scy UNUSED(querystring); 646280849Scy UNUSED(arg); 647280849Scy 648280849Scy *retcode = 500; 649280849Scy *retmsg = "Internal server failure"; 650280849Scy *mimetype = "text/plain"; 651280849Scy isc_buffer_reinit(b, msg, strlen(msg)); 652280849Scy isc_buffer_add(b, strlen(msg)); 653280849Scy *freecb = NULL; 654280849Scy *freecb_args = NULL; 655280849Scy 656280849Scy return (ISC_R_SUCCESS); 657280849Scy} 658280849Scy 659258945Srobertostatic void 660258945Srobertoisc_httpd_recvdone(isc_task_t *task, isc_event_t *ev) 661258945Sroberto{ 662258945Sroberto isc_region_t r; 663258945Sroberto isc_result_t result; 664258945Sroberto isc_httpd_t *httpd = ev->ev_arg; 665258945Sroberto isc_socketevent_t *sev = (isc_socketevent_t *)ev; 666258945Sroberto isc_httpdurl_t *url; 667258945Sroberto isc_time_t now; 668258945Sroberto char datebuf[32]; /* Only need 30, but safety first */ 669258945Sroberto 670258945Sroberto ENTER("recv"); 671258945Sroberto 672258945Sroberto INSIST(ISC_HTTPD_ISRECV(httpd)); 673258945Sroberto 674258945Sroberto if (sev->result != ISC_R_SUCCESS) { 675258945Sroberto NOTICE("recv destroying client"); 676258945Sroberto destroy_client(&httpd); 677258945Sroberto goto out; 678258945Sroberto } 679258945Sroberto 680258945Sroberto result = process_request(httpd, sev->n); 681258945Sroberto if (result == ISC_R_NOTFOUND) { 682258945Sroberto if (httpd->recvlen >= HTTP_RECVLEN - 1) { 683258945Sroberto destroy_client(&httpd); 684258945Sroberto goto out; 685258945Sroberto } 686258945Sroberto r.base = (unsigned char *)httpd->recvbuf + httpd->recvlen; 687258945Sroberto r.length = HTTP_RECVLEN - httpd->recvlen - 1; 688280849Scy /* check return code? */ 689280849Scy (void)isc_socket_recv(httpd->sock, &r, 1, task, 690280849Scy isc_httpd_recvdone, httpd); 691258945Sroberto goto out; 692258945Sroberto } else if (result != ISC_R_SUCCESS) { 693258945Sroberto destroy_client(&httpd); 694258945Sroberto goto out; 695258945Sroberto } 696258945Sroberto 697258945Sroberto ISC_HTTPD_SETSEND(httpd); 698258945Sroberto 699258945Sroberto /* 700258945Sroberto * XXXMLG Call function here. Provide an add-header function 701258945Sroberto * which will append the common headers to a response we generate. 702258945Sroberto */ 703258945Sroberto isc_buffer_initnull(&httpd->bodybuffer); 704258945Sroberto isc_time_now(&now); 705258945Sroberto isc_time_formathttptimestamp(&now, datebuf, sizeof(datebuf)); 706258945Sroberto url = ISC_LIST_HEAD(httpd->mgr->urls); 707258945Sroberto while (url != NULL) { 708258945Sroberto if (strcmp(httpd->url, url->url) == 0) 709258945Sroberto break; 710258945Sroberto url = ISC_LIST_NEXT(url, link); 711258945Sroberto } 712258945Sroberto if (url == NULL) 713258945Sroberto result = httpd->mgr->render_404(httpd->url, httpd->querystring, 714258945Sroberto NULL, 715258945Sroberto &httpd->retcode, 716258945Sroberto &httpd->retmsg, 717258945Sroberto &httpd->mimetype, 718258945Sroberto &httpd->bodybuffer, 719258945Sroberto &httpd->freecb, 720258945Sroberto &httpd->freecb_arg); 721258945Sroberto else 722258945Sroberto result = url->action(httpd->url, httpd->querystring, 723258945Sroberto url->action_arg, 724258945Sroberto &httpd->retcode, &httpd->retmsg, 725258945Sroberto &httpd->mimetype, &httpd->bodybuffer, 726258945Sroberto &httpd->freecb, &httpd->freecb_arg); 727258945Sroberto if (result != ISC_R_SUCCESS) { 728280849Scy result = httpd->mgr->render_500(httpd->url, httpd->querystring, 729280849Scy NULL, &httpd->retcode, 730280849Scy &httpd->retmsg, 731280849Scy &httpd->mimetype, 732280849Scy &httpd->bodybuffer, 733280849Scy &httpd->freecb, 734280849Scy &httpd->freecb_arg); 735280849Scy RUNTIME_CHECK(result == ISC_R_SUCCESS); 736258945Sroberto } 737258945Sroberto 738258945Sroberto isc_httpd_response(httpd); 739258945Sroberto isc_httpd_addheader(httpd, "Content-Type", httpd->mimetype); 740258945Sroberto isc_httpd_addheader(httpd, "Date", datebuf); 741258945Sroberto isc_httpd_addheader(httpd, "Expires", datebuf); 742258945Sroberto isc_httpd_addheader(httpd, "Last-Modified", datebuf); 743258945Sroberto isc_httpd_addheader(httpd, "Pragma: no-cache", NULL); 744258945Sroberto isc_httpd_addheader(httpd, "Cache-Control: no-cache", NULL); 745258945Sroberto isc_httpd_addheader(httpd, "Server: libisc", NULL); 746258945Sroberto isc_httpd_addheaderuint(httpd, "Content-Length", 747258945Sroberto isc_buffer_usedlength(&httpd->bodybuffer)); 748258945Sroberto isc_httpd_endheaders(httpd); /* done */ 749258945Sroberto 750258945Sroberto ISC_LIST_APPEND(httpd->bufflist, &httpd->headerbuffer, link); 751258945Sroberto /* 752258945Sroberto * Link the data buffer into our send queue, should we have any data 753258945Sroberto * rendered into it. If no data is present, we won't do anything 754258945Sroberto * with the buffer. 755258945Sroberto */ 756258945Sroberto if (isc_buffer_length(&httpd->bodybuffer) > 0) 757258945Sroberto ISC_LIST_APPEND(httpd->bufflist, &httpd->bodybuffer, link); 758258945Sroberto 759280849Scy /* check return code? */ 760280849Scy (void)isc_socket_sendv(httpd->sock, &httpd->bufflist, task, 761280849Scy isc_httpd_senddone, httpd); 762258945Sroberto 763258945Sroberto out: 764258945Sroberto isc_event_free(&ev); 765258945Sroberto EXIT("recv"); 766258945Sroberto} 767258945Sroberto 768258945Srobertovoid 769258945Srobertoisc_httpdmgr_shutdown(isc_httpdmgr_t **httpdmgrp) 770258945Sroberto{ 771258945Sroberto isc_httpdmgr_t *httpdmgr; 772258945Sroberto isc_httpd_t *httpd; 773258945Sroberto httpdmgr = *httpdmgrp; 774258945Sroberto *httpdmgrp = NULL; 775258945Sroberto 776258945Sroberto ENTER("isc_httpdmgr_shutdown"); 777258945Sroberto 778258945Sroberto LOCK(&httpdmgr->lock); 779258945Sroberto 780258945Sroberto MSETSHUTTINGDOWN(httpdmgr); 781258945Sroberto 782258945Sroberto isc_socket_cancel(httpdmgr->sock, httpdmgr->task, ISC_SOCKCANCEL_ALL); 783258945Sroberto 784258945Sroberto httpd = ISC_LIST_HEAD(httpdmgr->running); 785258945Sroberto while (httpd != NULL) { 786258945Sroberto isc_socket_cancel(httpd->sock, httpdmgr->task, 787258945Sroberto ISC_SOCKCANCEL_ALL); 788258945Sroberto httpd = ISC_LIST_NEXT(httpd, link); 789258945Sroberto } 790258945Sroberto 791258945Sroberto UNLOCK(&httpdmgr->lock); 792258945Sroberto 793258945Sroberto EXIT("isc_httpdmgr_shutdown"); 794258945Sroberto} 795258945Sroberto 796258945Srobertostatic isc_result_t 797258945Srobertogrow_headerspace(isc_httpd_t *httpd) 798258945Sroberto{ 799258945Sroberto char *newspace; 800258945Sroberto unsigned int newlen; 801258945Sroberto isc_region_t r; 802258945Sroberto 803258945Sroberto newlen = httpd->headerlen + HTTP_SENDGROW; 804258945Sroberto if (newlen > HTTP_SEND_MAXLEN) 805258945Sroberto return (ISC_R_NOSPACE); 806258945Sroberto 807258945Sroberto newspace = isc_mem_get(httpd->mgr->mctx, newlen); 808258945Sroberto if (newspace == NULL) 809258945Sroberto return (ISC_R_NOMEMORY); 810258945Sroberto isc_buffer_region(&httpd->headerbuffer, &r); 811258945Sroberto isc_buffer_reinit(&httpd->headerbuffer, newspace, newlen); 812258945Sroberto 813258945Sroberto isc_mem_put(httpd->mgr->mctx, r.base, r.length); 814258945Sroberto 815258945Sroberto return (ISC_R_SUCCESS); 816258945Sroberto} 817258945Sroberto 818258945Srobertoisc_result_t 819258945Srobertoisc_httpd_response(isc_httpd_t *httpd) 820258945Sroberto{ 821258945Sroberto isc_result_t result; 822258945Sroberto unsigned int needlen; 823258945Sroberto 824258945Sroberto needlen = strlen(httpd->protocol) + 1; /* protocol + space */ 825258945Sroberto needlen += 3 + 1; /* room for response code, always 3 bytes */ 826258945Sroberto needlen += strlen(httpd->retmsg) + 2; /* return msg + CRLF */ 827258945Sroberto 828280849Scy while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) { 829258945Sroberto result = grow_headerspace(httpd); 830258945Sroberto if (result != ISC_R_SUCCESS) 831258945Sroberto return (result); 832258945Sroberto } 833258945Sroberto 834258945Sroberto sprintf(isc_buffer_used(&httpd->headerbuffer), "%s %03d %s\r\n", 835258945Sroberto httpd->protocol, httpd->retcode, httpd->retmsg); 836258945Sroberto isc_buffer_add(&httpd->headerbuffer, needlen); 837258945Sroberto 838258945Sroberto return (ISC_R_SUCCESS); 839258945Sroberto} 840258945Sroberto 841258945Srobertoisc_result_t 842258945Srobertoisc_httpd_addheader(isc_httpd_t *httpd, const char *name, 843258945Sroberto const char *val) 844258945Sroberto{ 845258945Sroberto isc_result_t result; 846258945Sroberto unsigned int needlen; 847258945Sroberto 848258945Sroberto needlen = strlen(name); /* name itself */ 849258945Sroberto if (val != NULL) 850258945Sroberto needlen += 2 + strlen(val); /* :<space> and val */ 851258945Sroberto needlen += 2; /* CRLF */ 852258945Sroberto 853280849Scy while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) { 854258945Sroberto result = grow_headerspace(httpd); 855258945Sroberto if (result != ISC_R_SUCCESS) 856258945Sroberto return (result); 857258945Sroberto } 858258945Sroberto 859258945Sroberto if (val != NULL) 860258945Sroberto sprintf(isc_buffer_used(&httpd->headerbuffer), 861258945Sroberto "%s: %s\r\n", name, val); 862258945Sroberto else 863258945Sroberto sprintf(isc_buffer_used(&httpd->headerbuffer), 864258945Sroberto "%s\r\n", name); 865258945Sroberto 866258945Sroberto isc_buffer_add(&httpd->headerbuffer, needlen); 867258945Sroberto 868258945Sroberto return (ISC_R_SUCCESS); 869258945Sroberto} 870258945Sroberto 871258945Srobertoisc_result_t 872258945Srobertoisc_httpd_endheaders(isc_httpd_t *httpd) 873258945Sroberto{ 874258945Sroberto isc_result_t result; 875258945Sroberto 876280849Scy while (isc_buffer_availablelength(&httpd->headerbuffer) < 2) { 877258945Sroberto result = grow_headerspace(httpd); 878258945Sroberto if (result != ISC_R_SUCCESS) 879258945Sroberto return (result); 880258945Sroberto } 881258945Sroberto 882258945Sroberto sprintf(isc_buffer_used(&httpd->headerbuffer), "\r\n"); 883258945Sroberto isc_buffer_add(&httpd->headerbuffer, 2); 884258945Sroberto 885258945Sroberto return (ISC_R_SUCCESS); 886258945Sroberto} 887258945Sroberto 888258945Srobertoisc_result_t 889258945Srobertoisc_httpd_addheaderuint(isc_httpd_t *httpd, const char *name, int val) { 890258945Sroberto isc_result_t result; 891258945Sroberto unsigned int needlen; 892258945Sroberto char buf[sizeof "18446744073709551616"]; 893258945Sroberto 894258945Sroberto sprintf(buf, "%d", val); 895258945Sroberto 896258945Sroberto needlen = strlen(name); /* name itself */ 897258945Sroberto needlen += 2 + strlen(buf); /* :<space> and val */ 898258945Sroberto needlen += 2; /* CRLF */ 899258945Sroberto 900280849Scy while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) { 901258945Sroberto result = grow_headerspace(httpd); 902258945Sroberto if (result != ISC_R_SUCCESS) 903258945Sroberto return (result); 904258945Sroberto } 905258945Sroberto 906258945Sroberto sprintf(isc_buffer_used(&httpd->headerbuffer), 907258945Sroberto "%s: %s\r\n", name, buf); 908258945Sroberto 909258945Sroberto isc_buffer_add(&httpd->headerbuffer, needlen); 910258945Sroberto 911258945Sroberto return (ISC_R_SUCCESS); 912258945Sroberto} 913258945Sroberto 914258945Srobertostatic void 915258945Srobertoisc_httpd_senddone(isc_task_t *task, isc_event_t *ev) 916258945Sroberto{ 917258945Sroberto isc_httpd_t *httpd = ev->ev_arg; 918258945Sroberto isc_region_t r; 919258945Sroberto isc_socketevent_t *sev = (isc_socketevent_t *)ev; 920258945Sroberto 921258945Sroberto ENTER("senddone"); 922258945Sroberto INSIST(ISC_HTTPD_ISSEND(httpd)); 923258945Sroberto 924258945Sroberto /* 925258945Sroberto * First, unlink our header buffer from the socket's bufflist. This 926258945Sroberto * is sort of an evil hack, since we know our buffer will be there, 927258945Sroberto * and we know it's address, so we can just remove it directly. 928258945Sroberto */ 929258945Sroberto NOTICE("senddone unlinked header"); 930258945Sroberto ISC_LIST_UNLINK(sev->bufferlist, &httpd->headerbuffer, link); 931258945Sroberto 932258945Sroberto /* 933258945Sroberto * We will always want to clean up our receive buffer, even if we 934258945Sroberto * got an error on send or we are shutting down. 935258945Sroberto * 936258945Sroberto * We will pass in the buffer only if there is data in it. If 937258945Sroberto * there is no data, we will pass in a NULL. 938258945Sroberto */ 939258945Sroberto if (httpd->freecb != NULL) { 940258945Sroberto isc_buffer_t *b = NULL; 941258945Sroberto if (isc_buffer_length(&httpd->bodybuffer) > 0) 942258945Sroberto b = &httpd->bodybuffer; 943258945Sroberto httpd->freecb(b, httpd->freecb_arg); 944258945Sroberto NOTICE("senddone free callback performed"); 945258945Sroberto } 946258945Sroberto if (ISC_LINK_LINKED(&httpd->bodybuffer, link)) { 947258945Sroberto ISC_LIST_UNLINK(sev->bufferlist, &httpd->bodybuffer, link); 948258945Sroberto NOTICE("senddone body buffer unlinked"); 949258945Sroberto } 950258945Sroberto 951258945Sroberto if (sev->result != ISC_R_SUCCESS) { 952258945Sroberto destroy_client(&httpd); 953258945Sroberto goto out; 954258945Sroberto } 955258945Sroberto 956258945Sroberto if ((httpd->flags & HTTPD_CLOSE) != 0) { 957258945Sroberto destroy_client(&httpd); 958258945Sroberto goto out; 959258945Sroberto } 960258945Sroberto 961258945Sroberto ISC_HTTPD_SETRECV(httpd); 962258945Sroberto 963258945Sroberto NOTICE("senddone restarting recv on socket"); 964258945Sroberto 965258945Sroberto reset_client(httpd); 966258945Sroberto 967258945Sroberto r.base = (unsigned char *)httpd->recvbuf; 968258945Sroberto r.length = HTTP_RECVLEN - 1; 969280849Scy /* check return code? */ 970280849Scy (void)isc_socket_recv(httpd->sock, &r, 1, task, 971280849Scy isc_httpd_recvdone, httpd); 972258945Sroberto 973258945Srobertoout: 974258945Sroberto isc_event_free(&ev); 975258945Sroberto EXIT("senddone"); 976258945Sroberto} 977258945Sroberto 978258945Srobertostatic void 979258945Srobertoreset_client(isc_httpd_t *httpd) 980258945Sroberto{ 981258945Sroberto /* 982258945Sroberto * Catch errors here. We MUST be in RECV mode, and we MUST NOT have 983258945Sroberto * any outstanding buffers. If we have buffers, we have a leak. 984258945Sroberto */ 985258945Sroberto INSIST(ISC_HTTPD_ISRECV(httpd)); 986258945Sroberto INSIST(!ISC_LINK_LINKED(&httpd->headerbuffer, link)); 987258945Sroberto INSIST(!ISC_LINK_LINKED(&httpd->bodybuffer, link)); 988258945Sroberto 989258945Sroberto httpd->recvbuf[0] = 0; 990258945Sroberto httpd->recvlen = 0; 991258945Sroberto httpd->method = ISC_HTTPD_METHODUNKNOWN; 992258945Sroberto httpd->url = NULL; 993258945Sroberto httpd->querystring = NULL; 994258945Sroberto httpd->protocol = NULL; 995258945Sroberto httpd->flags = 0; 996258945Sroberto 997258945Sroberto isc_buffer_clear(&httpd->headerbuffer); 998258945Sroberto isc_buffer_invalidate(&httpd->bodybuffer); 999258945Sroberto} 1000258945Sroberto 1001258945Srobertoisc_result_t 1002258945Srobertoisc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url, 1003258945Sroberto isc_httpdaction_t *func, void *arg) 1004258945Sroberto{ 1005258945Sroberto isc_httpdurl_t *item; 1006258945Sroberto 1007258945Sroberto if (url == NULL) { 1008258945Sroberto httpdmgr->render_404 = func; 1009258945Sroberto return (ISC_R_SUCCESS); 1010258945Sroberto } 1011258945Sroberto 1012258945Sroberto item = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpdurl_t)); 1013258945Sroberto if (item == NULL) 1014258945Sroberto return (ISC_R_NOMEMORY); 1015258945Sroberto 1016258945Sroberto item->url = isc_mem_strdup(httpdmgr->mctx, url); 1017258945Sroberto if (item->url == NULL) { 1018258945Sroberto isc_mem_put(httpdmgr->mctx, item, sizeof(isc_httpdurl_t)); 1019258945Sroberto return (ISC_R_NOMEMORY); 1020258945Sroberto } 1021258945Sroberto 1022258945Sroberto item->action = func; 1023258945Sroberto item->action_arg = arg; 1024258945Sroberto ISC_LINK_INIT(item, link); 1025258945Sroberto ISC_LIST_APPEND(httpdmgr->urls, item, link); 1026258945Sroberto 1027258945Sroberto return (ISC_R_SUCCESS); 1028258945Sroberto} 1029