httpd.c revision 224092
1/* 2 * Copyright (C) 2006-2008, 2010 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.20 2010-11-16 05:38:31 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 */ 68struct 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 */ 79struct 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 */ 137struct 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 isc_httpdaction_t *render_500; 155}; 156 157/*% 158 * HTTP methods. 159 */ 160#define ISC_HTTPD_METHODUNKNOWN 0 161#define ISC_HTTPD_METHODGET 1 162#define ISC_HTTPD_METHODPOST 2 163 164/*% 165 * Client states. 166 * 167 * _IDLE The client is not doing anything at all. This state should 168 * only occur just after creation, and just before being 169 * destroyed. 170 * 171 * _RECV The client is waiting for data after issuing a socket recv(). 172 * 173 * _RECVDONE Data has been received, and is being processed. 174 * 175 * _SEND All data for a response has completed, and a reply was 176 * sent via a socket send() call. 177 * 178 * _SENDDONE Send is completed. 179 * 180 * Badly formatted state table: 181 * 182 * IDLE -> RECV when client has a recv() queued. 183 * 184 * RECV -> RECVDONE when recvdone event received. 185 * 186 * RECVDONE -> SEND if the data for a reply is at hand. 187 * 188 * SEND -> RECV when a senddone event was received. 189 * 190 * At any time -> RECV on error. If RECV fails, the client will 191 * self-destroy, closing the socket and freeing memory. 192 */ 193#define ISC_HTTPD_STATEIDLE 0 194#define ISC_HTTPD_STATERECV 1 195#define ISC_HTTPD_STATERECVDONE 2 196#define ISC_HTTPD_STATESEND 3 197#define ISC_HTTPD_STATESENDDONE 4 198 199#define ISC_HTTPD_ISRECV(c) ((c)->state == ISC_HTTPD_STATERECV) 200#define ISC_HTTPD_ISRECVDONE(c) ((c)->state == ISC_HTTPD_STATERECVDONE) 201#define ISC_HTTPD_ISSEND(c) ((c)->state == ISC_HTTPD_STATESEND) 202#define ISC_HTTPD_ISSENDDONE(c) ((c)->state == ISC_HTTPD_STATESENDDONE) 203 204/*% 205 * Overall magic test that means we're not idle. 206 */ 207#define ISC_HTTPD_SETRECV(c) ((c)->state = ISC_HTTPD_STATERECV) 208#define ISC_HTTPD_SETRECVDONE(c) ((c)->state = ISC_HTTPD_STATERECVDONE) 209#define ISC_HTTPD_SETSEND(c) ((c)->state = ISC_HTTPD_STATESEND) 210#define ISC_HTTPD_SETSENDDONE(c) ((c)->state = ISC_HTTPD_STATESENDDONE) 211 212static void isc_httpd_accept(isc_task_t *, isc_event_t *); 213static void isc_httpd_recvdone(isc_task_t *, isc_event_t *); 214static void isc_httpd_senddone(isc_task_t *, isc_event_t *); 215static void destroy_client(isc_httpd_t **); 216static isc_result_t process_request(isc_httpd_t *, int); 217static void httpdmgr_destroy(isc_httpdmgr_t *); 218static isc_result_t grow_headerspace(isc_httpd_t *); 219static void reset_client(isc_httpd_t *httpd); 220static isc_result_t render_404(const char *, const char *, 221 void *, 222 unsigned int *, const char **, 223 const char **, isc_buffer_t *, 224 isc_httpdfree_t **, void **); 225static isc_result_t render_500(const char *, const char *, 226 void *, 227 unsigned int *, const char **, 228 const char **, isc_buffer_t *, 229 isc_httpdfree_t **, void **); 230 231static void 232destroy_client(isc_httpd_t **httpdp) 233{ 234 isc_httpd_t *httpd = *httpdp; 235 isc_httpdmgr_t *httpdmgr = httpd->mgr; 236 237 *httpdp = NULL; 238 239 LOCK(&httpdmgr->lock); 240 241 isc_socket_detach(&httpd->sock); 242 ISC_LIST_UNLINK(httpdmgr->running, httpd, link); 243 244 if (httpd->headerlen > 0) 245 isc_mem_put(httpdmgr->mctx, httpd->headerdata, 246 httpd->headerlen); 247 248 isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t)); 249 250 UNLOCK(&httpdmgr->lock); 251 252 httpdmgr_destroy(httpdmgr); 253} 254 255isc_result_t 256isc_httpdmgr_create(isc_mem_t *mctx, isc_socket_t *sock, isc_task_t *task, 257 isc_httpdclientok_t *client_ok, 258 isc_httpdondestroy_t *ondestroy, void *cb_arg, 259 isc_timermgr_t *tmgr, isc_httpdmgr_t **httpdp) 260{ 261 isc_result_t result; 262 isc_httpdmgr_t *httpd; 263 264 REQUIRE(mctx != NULL); 265 REQUIRE(sock != NULL); 266 REQUIRE(task != NULL); 267 REQUIRE(tmgr != NULL); 268 REQUIRE(httpdp != NULL && *httpdp == NULL); 269 270 httpd = isc_mem_get(mctx, sizeof(isc_httpdmgr_t)); 271 if (httpd == NULL) 272 return (ISC_R_NOMEMORY); 273 274 result = isc_mutex_init(&httpd->lock); 275 if (result != ISC_R_SUCCESS) { 276 isc_mem_put(mctx, httpd, sizeof(isc_httpdmgr_t)); 277 return (result); 278 } 279 httpd->mctx = NULL; 280 isc_mem_attach(mctx, &httpd->mctx); 281 httpd->sock = NULL; 282 isc_socket_attach(sock, &httpd->sock); 283 httpd->task = NULL; 284 isc_task_attach(task, &httpd->task); 285 httpd->timermgr = tmgr; /* XXXMLG no attach function? */ 286 httpd->client_ok = client_ok; 287 httpd->ondestroy = ondestroy; 288 httpd->cb_arg = cb_arg; 289 290 ISC_LIST_INIT(httpd->running); 291 ISC_LIST_INIT(httpd->urls); 292 293 /* XXXMLG ignore errors on isc_socket_listen() */ 294 result = isc_socket_listen(sock, SOMAXCONN); 295 if (result != ISC_R_SUCCESS) { 296 UNEXPECTED_ERROR(__FILE__, __LINE__, 297 "isc_socket_listen() failed: %s", 298 isc_result_totext(result)); 299 goto cleanup; 300 } 301 302 (void)isc_socket_filter(sock, "httpready"); 303 304 result = isc_socket_accept(sock, task, isc_httpd_accept, httpd); 305 if (result != ISC_R_SUCCESS) 306 goto cleanup; 307 308 httpd->render_404 = render_404; 309 httpd->render_500 = render_500; 310 311 *httpdp = httpd; 312 return (ISC_R_SUCCESS); 313 314 cleanup: 315 isc_task_detach(&httpd->task); 316 isc_socket_detach(&httpd->sock); 317 isc_mem_detach(&httpd->mctx); 318 isc_mutex_destroy(&httpd->lock); 319 isc_mem_put(mctx, httpd, sizeof(isc_httpdmgr_t)); 320 return (result); 321} 322 323static void 324httpdmgr_destroy(isc_httpdmgr_t *httpdmgr) 325{ 326 isc_mem_t *mctx; 327 isc_httpdurl_t *url; 328 329 ENTER("httpdmgr_destroy"); 330 331 LOCK(&httpdmgr->lock); 332 333 if (!MSHUTTINGDOWN(httpdmgr)) { 334 NOTICE("httpdmgr_destroy not shutting down yet"); 335 UNLOCK(&httpdmgr->lock); 336 return; 337 } 338 339 /* 340 * If all clients are not shut down, don't do anything yet. 341 */ 342 if (!ISC_LIST_EMPTY(httpdmgr->running)) { 343 NOTICE("httpdmgr_destroy clients still active"); 344 UNLOCK(&httpdmgr->lock); 345 return; 346 } 347 348 NOTICE("httpdmgr_destroy detaching socket, task, and timermgr"); 349 350 isc_socket_detach(&httpdmgr->sock); 351 isc_task_detach(&httpdmgr->task); 352 httpdmgr->timermgr = NULL; 353 354 /* 355 * Clear out the list of all actions we know about. Just free the 356 * memory. 357 */ 358 url = ISC_LIST_HEAD(httpdmgr->urls); 359 while (url != NULL) { 360 isc_mem_free(httpdmgr->mctx, url->url); 361 ISC_LIST_UNLINK(httpdmgr->urls, url, link); 362 isc_mem_put(httpdmgr->mctx, url, sizeof(isc_httpdurl_t)); 363 url = ISC_LIST_HEAD(httpdmgr->urls); 364 } 365 366 UNLOCK(&httpdmgr->lock); 367 isc_mutex_destroy(&httpdmgr->lock); 368 369 if (httpdmgr->ondestroy != NULL) 370 (httpdmgr->ondestroy)(httpdmgr->cb_arg); 371 372 mctx = httpdmgr->mctx; 373 isc_mem_putanddetach(&mctx, httpdmgr, sizeof(isc_httpdmgr_t)); 374 375 EXIT("httpdmgr_destroy"); 376} 377 378#define LENGTHOK(s) (httpd->recvbuf - (s) < (int)httpd->recvlen) 379#define BUFLENOK(s) (httpd->recvbuf - (s) < HTTP_RECVLEN) 380 381static isc_result_t 382process_request(isc_httpd_t *httpd, int length) 383{ 384 char *s; 385 char *p; 386 int delim; 387 388 ENTER("request"); 389 390 httpd->recvlen += length; 391 392 httpd->recvbuf[httpd->recvlen] = 0; 393 394 /* 395 * If we don't find a blank line in our buffer, return that we need 396 * more data. 397 */ 398 s = strstr(httpd->recvbuf, "\r\n\r\n"); 399 delim = 1; 400 if (s == NULL) { 401 s = strstr(httpd->recvbuf, "\n\n"); 402 delim = 2; 403 } 404 if (s == NULL) 405 return (ISC_R_NOTFOUND); 406 407 /* 408 * Determine if this is a POST or GET method. Any other values will 409 * cause an error to be returned. 410 */ 411 if (strncmp(httpd->recvbuf, "GET ", 4) == 0) { 412 httpd->method = ISC_HTTPD_METHODGET; 413 p = httpd->recvbuf + 4; 414 } else if (strncmp(httpd->recvbuf, "POST ", 5) == 0) { 415 httpd->method = ISC_HTTPD_METHODPOST; 416 p = httpd->recvbuf + 5; 417 } else { 418 return (ISC_R_RANGE); 419 } 420 421 /* 422 * From now on, p is the start of our buffer. 423 */ 424 425 /* 426 * Extract the URL. 427 */ 428 s = p; 429 while (LENGTHOK(s) && BUFLENOK(s) && 430 (*s != '\n' && *s != '\r' && *s != '\0' && *s != ' ')) 431 s++; 432 if (!LENGTHOK(s)) 433 return (ISC_R_NOTFOUND); 434 if (!BUFLENOK(s)) 435 return (ISC_R_NOMEMORY); 436 *s = 0; 437 438 /* 439 * Make the URL relative. 440 */ 441 if ((strncmp(p, "http:/", 6) == 0) 442 || (strncmp(p, "https:/", 7) == 0)) { 443 /* Skip first / */ 444 while (*p != '/' && *p != 0) 445 p++; 446 if (*p == 0) 447 return (ISC_R_RANGE); 448 p++; 449 /* Skip second / */ 450 while (*p != '/' && *p != 0) 451 p++; 452 if (*p == 0) 453 return (ISC_R_RANGE); 454 p++; 455 /* Find third / */ 456 while (*p != '/' && *p != 0) 457 p++; 458 if (*p == 0) { 459 p--; 460 *p = '/'; 461 } 462 } 463 464 httpd->url = p; 465 p = s + delim; 466 s = p; 467 468 /* 469 * Now, see if there is a ? mark in the URL. If so, this is 470 * part of the query string, and we will split it from the URL. 471 */ 472 httpd->querystring = strchr(httpd->url, '?'); 473 if (httpd->querystring != NULL) { 474 *(httpd->querystring) = 0; 475 httpd->querystring++; 476 } 477 478 /* 479 * Extract the HTTP/1.X protocol. We will bounce on anything but 480 * HTTP/1.1 for now. 481 */ 482 while (LENGTHOK(s) && BUFLENOK(s) && 483 (*s != '\n' && *s != '\r' && *s != '\0')) 484 s++; 485 if (!LENGTHOK(s)) 486 return (ISC_R_NOTFOUND); 487 if (!BUFLENOK(s)) 488 return (ISC_R_NOMEMORY); 489 *s = 0; 490 if ((strncmp(p, "HTTP/1.0", 8) != 0) 491 && (strncmp(p, "HTTP/1.1", 8) != 0)) 492 return (ISC_R_RANGE); 493 httpd->protocol = p; 494 p = s + 1; 495 s = p; 496 497 if (strstr(s, "Connection: close") != NULL) 498 httpd->flags |= HTTPD_CLOSE; 499 500 if (strstr(s, "Host: ") != NULL) 501 httpd->flags |= HTTPD_FOUNDHOST; 502 503 /* 504 * Standards compliance hooks here. 505 */ 506 if (strcmp(httpd->protocol, "HTTP/1.1") == 0 507 && ((httpd->flags & HTTPD_FOUNDHOST) == 0)) 508 return (ISC_R_RANGE); 509 510 EXIT("request"); 511 512 return (ISC_R_SUCCESS); 513} 514 515static void 516isc_httpd_accept(isc_task_t *task, isc_event_t *ev) 517{ 518 isc_result_t result; 519 isc_httpdmgr_t *httpdmgr = ev->ev_arg; 520 isc_httpd_t *httpd; 521 isc_region_t r; 522 isc_socket_newconnev_t *nev = (isc_socket_newconnev_t *)ev; 523 isc_sockaddr_t peeraddr; 524 525 ENTER("accept"); 526 527 LOCK(&httpdmgr->lock); 528 if (MSHUTTINGDOWN(httpdmgr)) { 529 NOTICE("accept shutting down, goto out"); 530 goto out; 531 } 532 533 if (nev->result == ISC_R_CANCELED) { 534 NOTICE("accept canceled, goto out"); 535 goto out; 536 } 537 538 if (nev->result != ISC_R_SUCCESS) { 539 /* XXXMLG log failure */ 540 NOTICE("accept returned failure, goto requeue"); 541 goto requeue; 542 } 543 544 (void)isc_socket_getpeername(nev->newsocket, &peeraddr); 545 if (httpdmgr->client_ok != NULL && 546 !(httpdmgr->client_ok)(&peeraddr, httpdmgr->cb_arg)) { 547 isc_socket_detach(&nev->newsocket); 548 goto requeue; 549 } 550 551 httpd = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpd_t)); 552 if (httpd == NULL) { 553 /* XXXMLG log failure */ 554 NOTICE("accept failed to allocate memory, goto requeue"); 555 isc_socket_detach(&nev->newsocket); 556 goto requeue; 557 } 558 559 httpd->mgr = httpdmgr; 560 ISC_LINK_INIT(httpd, link); 561 ISC_LIST_APPEND(httpdmgr->running, httpd, link); 562 ISC_HTTPD_SETRECV(httpd); 563 httpd->sock = nev->newsocket; 564 isc_socket_setname(httpd->sock, "httpd", NULL); 565 httpd->flags = 0; 566 567 /* 568 * Initialize the buffer for our headers. 569 */ 570 httpd->headerdata = isc_mem_get(httpdmgr->mctx, HTTP_SENDGROW); 571 if (httpd->headerdata == NULL) { 572 isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t)); 573 isc_socket_detach(&nev->newsocket); 574 goto requeue; 575 } 576 httpd->headerlen = HTTP_SENDGROW; 577 isc_buffer_init(&httpd->headerbuffer, httpd->headerdata, 578 httpd->headerlen); 579 580 ISC_LIST_INIT(httpd->bufflist); 581 582 isc_buffer_initnull(&httpd->bodybuffer); 583 reset_client(httpd); 584 585 r.base = (unsigned char *)httpd->recvbuf; 586 r.length = HTTP_RECVLEN - 1; 587 result = isc_socket_recv(httpd->sock, &r, 1, task, isc_httpd_recvdone, 588 httpd); 589 NOTICE("accept queued recv on socket"); 590 591 requeue: 592 result = isc_socket_accept(httpdmgr->sock, task, isc_httpd_accept, 593 httpdmgr); 594 if (result != ISC_R_SUCCESS) { 595 /* XXXMLG what to do? Log failure... */ 596 NOTICE("accept could not reaccept due to failure"); 597 } 598 599 out: 600 UNLOCK(&httpdmgr->lock); 601 602 httpdmgr_destroy(httpdmgr); 603 604 isc_event_free(&ev); 605 606 EXIT("accept"); 607} 608 609static isc_result_t 610render_404(const char *url, const char *querystring, 611 void *arg, 612 unsigned int *retcode, const char **retmsg, 613 const char **mimetype, isc_buffer_t *b, 614 isc_httpdfree_t **freecb, void **freecb_args) 615{ 616 static char msg[] = "No such URL."; 617 618 UNUSED(url); 619 UNUSED(querystring); 620 UNUSED(arg); 621 622 *retcode = 404; 623 *retmsg = "No such URL"; 624 *mimetype = "text/plain"; 625 isc_buffer_reinit(b, msg, strlen(msg)); 626 isc_buffer_add(b, strlen(msg)); 627 *freecb = NULL; 628 *freecb_args = NULL; 629 630 return (ISC_R_SUCCESS); 631} 632 633static isc_result_t 634render_500(const char *url, const char *querystring, 635 void *arg, 636 unsigned int *retcode, const char **retmsg, 637 const char **mimetype, isc_buffer_t *b, 638 isc_httpdfree_t **freecb, void **freecb_args) 639{ 640 static char msg[] = "Internal server failure."; 641 642 UNUSED(url); 643 UNUSED(querystring); 644 UNUSED(arg); 645 646 *retcode = 500; 647 *retmsg = "Internal server failure"; 648 *mimetype = "text/plain"; 649 isc_buffer_reinit(b, msg, strlen(msg)); 650 isc_buffer_add(b, strlen(msg)); 651 *freecb = NULL; 652 *freecb_args = NULL; 653 654 return (ISC_R_SUCCESS); 655} 656 657static void 658isc_httpd_recvdone(isc_task_t *task, isc_event_t *ev) 659{ 660 isc_region_t r; 661 isc_result_t result; 662 isc_httpd_t *httpd = ev->ev_arg; 663 isc_socketevent_t *sev = (isc_socketevent_t *)ev; 664 isc_httpdurl_t *url; 665 isc_time_t now; 666 char datebuf[32]; /* Only need 30, but safety first */ 667 668 ENTER("recv"); 669 670 INSIST(ISC_HTTPD_ISRECV(httpd)); 671 672 if (sev->result != ISC_R_SUCCESS) { 673 NOTICE("recv destroying client"); 674 destroy_client(&httpd); 675 goto out; 676 } 677 678 result = process_request(httpd, sev->n); 679 if (result == ISC_R_NOTFOUND) { 680 if (httpd->recvlen >= HTTP_RECVLEN - 1) { 681 destroy_client(&httpd); 682 goto out; 683 } 684 r.base = (unsigned char *)httpd->recvbuf + httpd->recvlen; 685 r.length = HTTP_RECVLEN - httpd->recvlen - 1; 686 result = isc_socket_recv(httpd->sock, &r, 1, task, 687 isc_httpd_recvdone, httpd); 688 goto out; 689 } else if (result != ISC_R_SUCCESS) { 690 destroy_client(&httpd); 691 goto out; 692 } 693 694 ISC_HTTPD_SETSEND(httpd); 695 696 /* 697 * XXXMLG Call function here. Provide an add-header function 698 * which will append the common headers to a response we generate. 699 */ 700 isc_buffer_initnull(&httpd->bodybuffer); 701 isc_time_now(&now); 702 isc_time_formathttptimestamp(&now, datebuf, sizeof(datebuf)); 703 url = ISC_LIST_HEAD(httpd->mgr->urls); 704 while (url != NULL) { 705 if (strcmp(httpd->url, url->url) == 0) 706 break; 707 url = ISC_LIST_NEXT(url, link); 708 } 709 if (url == NULL) 710 result = httpd->mgr->render_404(httpd->url, httpd->querystring, 711 NULL, 712 &httpd->retcode, 713 &httpd->retmsg, 714 &httpd->mimetype, 715 &httpd->bodybuffer, 716 &httpd->freecb, 717 &httpd->freecb_arg); 718 else 719 result = url->action(httpd->url, httpd->querystring, 720 url->action_arg, 721 &httpd->retcode, &httpd->retmsg, 722 &httpd->mimetype, &httpd->bodybuffer, 723 &httpd->freecb, &httpd->freecb_arg); 724 if (result != ISC_R_SUCCESS) { 725 result = httpd->mgr->render_500(httpd->url, httpd->querystring, 726 NULL, 727 &httpd->retcode, 728 &httpd->retmsg, 729 &httpd->mimetype, 730 &httpd->bodybuffer, 731 &httpd->freecb, 732 &httpd->freecb_arg); 733 } 734 735 isc_httpd_response(httpd); 736 isc_httpd_addheader(httpd, "Content-Type", httpd->mimetype); 737 isc_httpd_addheader(httpd, "Date", datebuf); 738 isc_httpd_addheader(httpd, "Expires", datebuf); 739 isc_httpd_addheader(httpd, "Last-Modified", datebuf); 740 isc_httpd_addheader(httpd, "Pragma: no-cache", NULL); 741 isc_httpd_addheader(httpd, "Cache-Control: no-cache", NULL); 742 isc_httpd_addheader(httpd, "Server: libisc", NULL); 743 isc_httpd_addheaderuint(httpd, "Content-Length", 744 isc_buffer_usedlength(&httpd->bodybuffer)); 745 isc_httpd_endheaders(httpd); /* done */ 746 747 ISC_LIST_APPEND(httpd->bufflist, &httpd->headerbuffer, link); 748 /* 749 * Link the data buffer into our send queue, should we have any data 750 * rendered into it. If no data is present, we won't do anything 751 * with the buffer. 752 */ 753 if (isc_buffer_length(&httpd->bodybuffer) > 0) 754 ISC_LIST_APPEND(httpd->bufflist, &httpd->bodybuffer, link); 755 756 result = isc_socket_sendv(httpd->sock, &httpd->bufflist, task, 757 isc_httpd_senddone, httpd); 758 759 out: 760 isc_event_free(&ev); 761 EXIT("recv"); 762} 763 764void 765isc_httpdmgr_shutdown(isc_httpdmgr_t **httpdmgrp) 766{ 767 isc_httpdmgr_t *httpdmgr; 768 isc_httpd_t *httpd; 769 httpdmgr = *httpdmgrp; 770 *httpdmgrp = NULL; 771 772 ENTER("isc_httpdmgr_shutdown"); 773 774 LOCK(&httpdmgr->lock); 775 776 MSETSHUTTINGDOWN(httpdmgr); 777 778 isc_socket_cancel(httpdmgr->sock, httpdmgr->task, ISC_SOCKCANCEL_ALL); 779 780 httpd = ISC_LIST_HEAD(httpdmgr->running); 781 while (httpd != NULL) { 782 isc_socket_cancel(httpd->sock, httpdmgr->task, 783 ISC_SOCKCANCEL_ALL); 784 httpd = ISC_LIST_NEXT(httpd, link); 785 } 786 787 UNLOCK(&httpdmgr->lock); 788 789 EXIT("isc_httpdmgr_shutdown"); 790} 791 792static isc_result_t 793grow_headerspace(isc_httpd_t *httpd) 794{ 795 char *newspace; 796 unsigned int newlen; 797 isc_region_t r; 798 799 newlen = httpd->headerlen + HTTP_SENDGROW; 800 if (newlen > HTTP_SEND_MAXLEN) 801 return (ISC_R_NOSPACE); 802 803 newspace = isc_mem_get(httpd->mgr->mctx, newlen); 804 if (newspace == NULL) 805 return (ISC_R_NOMEMORY); 806 isc_buffer_region(&httpd->headerbuffer, &r); 807 isc_buffer_reinit(&httpd->headerbuffer, newspace, newlen); 808 809 isc_mem_put(httpd->mgr->mctx, r.base, r.length); 810 811 return (ISC_R_SUCCESS); 812} 813 814isc_result_t 815isc_httpd_response(isc_httpd_t *httpd) 816{ 817 isc_result_t result; 818 unsigned int needlen; 819 820 needlen = strlen(httpd->protocol) + 1; /* protocol + space */ 821 needlen += 3 + 1; /* room for response code, always 3 bytes */ 822 needlen += strlen(httpd->retmsg) + 2; /* return msg + CRLF */ 823 824 if (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) { 825 result = grow_headerspace(httpd); 826 if (result != ISC_R_SUCCESS) 827 return (result); 828 } 829 830 sprintf(isc_buffer_used(&httpd->headerbuffer), "%s %03d %s\r\n", 831 httpd->protocol, httpd->retcode, httpd->retmsg); 832 isc_buffer_add(&httpd->headerbuffer, needlen); 833 834 return (ISC_R_SUCCESS); 835} 836 837isc_result_t 838isc_httpd_addheader(isc_httpd_t *httpd, const char *name, 839 const char *val) 840{ 841 isc_result_t result; 842 unsigned int needlen; 843 844 needlen = strlen(name); /* name itself */ 845 if (val != NULL) 846 needlen += 2 + strlen(val); /* :<space> and val */ 847 needlen += 2; /* CRLF */ 848 849 if (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) { 850 result = grow_headerspace(httpd); 851 if (result != ISC_R_SUCCESS) 852 return (result); 853 } 854 855 if (val != NULL) 856 sprintf(isc_buffer_used(&httpd->headerbuffer), 857 "%s: %s\r\n", name, val); 858 else 859 sprintf(isc_buffer_used(&httpd->headerbuffer), 860 "%s\r\n", name); 861 862 isc_buffer_add(&httpd->headerbuffer, needlen); 863 864 return (ISC_R_SUCCESS); 865} 866 867isc_result_t 868isc_httpd_endheaders(isc_httpd_t *httpd) 869{ 870 isc_result_t result; 871 872 if (isc_buffer_availablelength(&httpd->headerbuffer) < 2) { 873 result = grow_headerspace(httpd); 874 if (result != ISC_R_SUCCESS) 875 return (result); 876 } 877 878 sprintf(isc_buffer_used(&httpd->headerbuffer), "\r\n"); 879 isc_buffer_add(&httpd->headerbuffer, 2); 880 881 return (ISC_R_SUCCESS); 882} 883 884isc_result_t 885isc_httpd_addheaderuint(isc_httpd_t *httpd, const char *name, int val) { 886 isc_result_t result; 887 unsigned int needlen; 888 char buf[sizeof "18446744073709551616"]; 889 890 sprintf(buf, "%d", val); 891 892 needlen = strlen(name); /* name itself */ 893 needlen += 2 + strlen(buf); /* :<space> and val */ 894 needlen += 2; /* CRLF */ 895 896 if (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) { 897 result = grow_headerspace(httpd); 898 if (result != ISC_R_SUCCESS) 899 return (result); 900 } 901 902 sprintf(isc_buffer_used(&httpd->headerbuffer), 903 "%s: %s\r\n", name, buf); 904 905 isc_buffer_add(&httpd->headerbuffer, needlen); 906 907 return (ISC_R_SUCCESS); 908} 909 910static void 911isc_httpd_senddone(isc_task_t *task, isc_event_t *ev) 912{ 913 isc_httpd_t *httpd = ev->ev_arg; 914 isc_region_t r; 915 isc_result_t result; 916 isc_socketevent_t *sev = (isc_socketevent_t *)ev; 917 918 ENTER("senddone"); 919 INSIST(ISC_HTTPD_ISSEND(httpd)); 920 921 /* 922 * First, unlink our header buffer from the socket's bufflist. This 923 * is sort of an evil hack, since we know our buffer will be there, 924 * and we know it's address, so we can just remove it directly. 925 */ 926 NOTICE("senddone unlinked header"); 927 ISC_LIST_UNLINK(sev->bufferlist, &httpd->headerbuffer, link); 928 929 /* 930 * We will always want to clean up our receive buffer, even if we 931 * got an error on send or we are shutting down. 932 * 933 * We will pass in the buffer only if there is data in it. If 934 * there is no data, we will pass in a NULL. 935 */ 936 if (httpd->freecb != NULL) { 937 isc_buffer_t *b = NULL; 938 if (isc_buffer_length(&httpd->bodybuffer) > 0) 939 b = &httpd->bodybuffer; 940 httpd->freecb(b, httpd->freecb_arg); 941 NOTICE("senddone free callback performed"); 942 } 943 if (ISC_LINK_LINKED(&httpd->bodybuffer, link)) { 944 ISC_LIST_UNLINK(sev->bufferlist, &httpd->bodybuffer, link); 945 NOTICE("senddone body buffer unlinked"); 946 } 947 948 if (sev->result != ISC_R_SUCCESS) { 949 destroy_client(&httpd); 950 goto out; 951 } 952 953 if ((httpd->flags & HTTPD_CLOSE) != 0) { 954 destroy_client(&httpd); 955 goto out; 956 } 957 958 ISC_HTTPD_SETRECV(httpd); 959 960 NOTICE("senddone restarting recv on socket"); 961 962 reset_client(httpd); 963 964 r.base = (unsigned char *)httpd->recvbuf; 965 r.length = HTTP_RECVLEN - 1; 966 result = isc_socket_recv(httpd->sock, &r, 1, task, isc_httpd_recvdone, 967 httpd); 968 969out: 970 isc_event_free(&ev); 971 EXIT("senddone"); 972} 973 974static void 975reset_client(isc_httpd_t *httpd) 976{ 977 /* 978 * Catch errors here. We MUST be in RECV mode, and we MUST NOT have 979 * any outstanding buffers. If we have buffers, we have a leak. 980 */ 981 INSIST(ISC_HTTPD_ISRECV(httpd)); 982 INSIST(!ISC_LINK_LINKED(&httpd->headerbuffer, link)); 983 INSIST(!ISC_LINK_LINKED(&httpd->bodybuffer, link)); 984 985 httpd->recvbuf[0] = 0; 986 httpd->recvlen = 0; 987 httpd->method = ISC_HTTPD_METHODUNKNOWN; 988 httpd->url = NULL; 989 httpd->querystring = NULL; 990 httpd->protocol = NULL; 991 httpd->flags = 0; 992 993 isc_buffer_clear(&httpd->headerbuffer); 994 isc_buffer_invalidate(&httpd->bodybuffer); 995} 996 997isc_result_t 998isc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url, 999 isc_httpdaction_t *func, void *arg) 1000{ 1001 isc_httpdurl_t *item; 1002 1003 if (url == NULL) { 1004 httpdmgr->render_404 = func; 1005 return (ISC_R_SUCCESS); 1006 } 1007 1008 item = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpdurl_t)); 1009 if (item == NULL) 1010 return (ISC_R_NOMEMORY); 1011 1012 item->url = isc_mem_strdup(httpdmgr->mctx, url); 1013 if (item->url == NULL) { 1014 isc_mem_put(httpdmgr->mctx, item, sizeof(isc_httpdurl_t)); 1015 return (ISC_R_NOMEMORY); 1016 } 1017 1018 item->action = func; 1019 item->action_arg = arg; 1020 ISC_LINK_INIT(item, link); 1021 ISC_LIST_APPEND(httpdmgr->urls, item, link); 1022 1023 return (ISC_R_SUCCESS); 1024} 1025