1 /* $NetBSD: httpd.c,v 1.8 2015/07/08 17:28:59 christos Exp $ */ 2 3 /* 4 * Copyright (C) 2006-2008, 2010-2015 Internet Systems Consortium, Inc. ("ISC") 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 11 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 12 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 13 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 15 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 16 * PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* Id */ 20 21 /*! \file */ 22 23 #include <config.h> 24 25 #include <isc/buffer.h> 26 #include <isc/httpd.h> 27 #include <isc/mem.h> 28 #include <isc/socket.h> 29 #include <isc/string.h> 30 #include <isc/task.h> 31 #include <isc/time.h> 32 #include <isc/util.h> 33 34 #include <string.h> 35 36 /*% 37 * TODO: 38 * 39 * o Put in better checks to make certain things are passed in correctly. 40 * This includes a magic number for externally-visible structures, 41 * checking for NULL-ness before dereferencing, etc. 42 * o Make the URL processing external functions which will fill-in a buffer 43 * structure we provide, or return an error and we will render a generic 44 * page and close the client. 45 */ 46 47 #define MSHUTTINGDOWN(cm) ((cm->flags & ISC_HTTPDMGR_FLAGSHUTTINGDOWN) != 0) 48 #define MSETSHUTTINGDOWN(cm) (cm->flags |= ISC_HTTPDMGR_FLAGSHUTTINGDOWN) 49 50 #ifdef DEBUG_HTTPD 51 #define ENTER(x) do { fprintf(stderr, "ENTER %s\n", (x)); } while (/*CONSTCOND*/0) 52 #define EXIT(x) do { fprintf(stderr, "EXIT %s\n", (x)); } while (/*CONSTCOND*/0) 53 #define NOTICE(x) do { fprintf(stderr, "NOTICE %s\n", (x)); } while (/*CONSTCOND*/0) 54 #else 55 #define ENTER(x) do { } while(/*CONSTCOND*/0) 56 #define EXIT(x) do { } while(/*CONSTCOND*/0) 57 #define NOTICE(x) do { } while(/*CONSTCOND*/0) 58 #endif 59 60 #define HTTP_RECVLEN 1024 61 #define HTTP_SENDGROW 1024 62 #define HTTP_SEND_MAXLEN 10240 63 64 #define HTTPD_CLOSE 0x0001 /* Got a Connection: close header */ 65 #define HTTPD_FOUNDHOST 0x0002 /* Got a Host: header */ 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 **httpdp) 242 { 243 isc_result_t result; 244 isc_httpdmgr_t *httpd; 245 246 REQUIRE(mctx != NULL); 247 REQUIRE(sock != NULL); 248 REQUIRE(task != NULL); 249 REQUIRE(tmgr != NULL); 250 REQUIRE(httpdp != NULL && *httpdp == NULL); 251 252 httpd = isc_mem_get(mctx, sizeof(isc_httpdmgr_t)); 253 if (httpd == NULL) 254 return (ISC_R_NOMEMORY); 255 256 result = isc_mutex_init(&httpd->lock); 257 if (result != ISC_R_SUCCESS) { 258 isc_mem_put(mctx, httpd, sizeof(isc_httpdmgr_t)); 259 return (result); 260 } 261 httpd->mctx = NULL; 262 isc_mem_attach(mctx, &httpd->mctx); 263 httpd->sock = NULL; 264 isc_socket_attach(sock, &httpd->sock); 265 httpd->task = NULL; 266 isc_task_attach(task, &httpd->task); 267 httpd->timermgr = tmgr; /* XXXMLG no attach function? */ 268 httpd->client_ok = client_ok; 269 httpd->ondestroy = ondestroy; 270 httpd->cb_arg = cb_arg; 271 272 ISC_LIST_INIT(httpd->running); 273 ISC_LIST_INIT(httpd->urls); 274 275 /* XXXMLG ignore errors on isc_socket_listen() */ 276 result = isc_socket_listen(sock, SOMAXCONN); 277 if (result != ISC_R_SUCCESS) { 278 UNEXPECTED_ERROR(__FILE__, __LINE__, 279 "isc_socket_listen() failed: %s", 280 isc_result_totext(result)); 281 goto cleanup; 282 } 283 284 (void)isc_socket_filter(sock, "httpready"); 285 286 result = isc_socket_accept(sock, task, isc_httpd_accept, httpd); 287 if (result != ISC_R_SUCCESS) 288 goto cleanup; 289 290 httpd->render_404 = render_404; 291 httpd->render_500 = render_500; 292 293 *httpdp = httpd; 294 return (ISC_R_SUCCESS); 295 296 cleanup: 297 isc_task_detach(&httpd->task); 298 isc_socket_detach(&httpd->sock); 299 isc_mem_detach(&httpd->mctx); 300 (void)isc_mutex_destroy(&httpd->lock); 301 isc_mem_put(mctx, httpd, sizeof(isc_httpdmgr_t)); 302 return (result); 303 } 304 305 static void 306 httpdmgr_destroy(isc_httpdmgr_t *httpdmgr) { 307 isc_mem_t *mctx; 308 isc_httpdurl_t *url; 309 310 ENTER("httpdmgr_destroy"); 311 312 LOCK(&httpdmgr->lock); 313 314 if (!MSHUTTINGDOWN(httpdmgr)) { 315 NOTICE("httpdmgr_destroy not shutting down yet"); 316 UNLOCK(&httpdmgr->lock); 317 return; 318 } 319 320 /* 321 * If all clients are not shut down, don't do anything yet. 322 */ 323 if (!ISC_LIST_EMPTY(httpdmgr->running)) { 324 NOTICE("httpdmgr_destroy clients still active"); 325 UNLOCK(&httpdmgr->lock); 326 return; 327 } 328 329 NOTICE("httpdmgr_destroy detaching socket, task, and timermgr"); 330 331 isc_socket_detach(&httpdmgr->sock); 332 isc_task_detach(&httpdmgr->task); 333 httpdmgr->timermgr = NULL; 334 335 /* 336 * Clear out the list of all actions we know about. Just free the 337 * memory. 338 */ 339 url = ISC_LIST_HEAD(httpdmgr->urls); 340 while (url != NULL) { 341 isc_mem_free(httpdmgr->mctx, url->url); 342 ISC_LIST_UNLINK(httpdmgr->urls, url, link); 343 isc_mem_put(httpdmgr->mctx, url, sizeof(isc_httpdurl_t)); 344 url = ISC_LIST_HEAD(httpdmgr->urls); 345 } 346 347 UNLOCK(&httpdmgr->lock); 348 (void)isc_mutex_destroy(&httpdmgr->lock); 349 350 if (httpdmgr->ondestroy != NULL) 351 (httpdmgr->ondestroy)(httpdmgr->cb_arg); 352 353 mctx = httpdmgr->mctx; 354 isc_mem_putanddetach(&mctx, httpdmgr, sizeof(isc_httpdmgr_t)); 355 356 EXIT("httpdmgr_destroy"); 357 } 358 359 #define LENGTHOK(s) (httpd->recvbuf - (s) < (int)httpd->recvlen) 360 #define BUFLENOK(s) (httpd->recvbuf - (s) < HTTP_RECVLEN) 361 362 static isc_result_t 363 process_request(isc_httpd_t *httpd, int length) { 364 char *s; 365 char *p; 366 int delim; 367 368 ENTER("request"); 369 370 httpd->recvlen += length; 371 372 httpd->recvbuf[httpd->recvlen] = 0; 373 httpd->headers = NULL; 374 375 /* 376 * If we don't find a blank line in our buffer, return that we need 377 * more data. 378 */ 379 s = strstr(httpd->recvbuf, "\r\n\r\n"); 380 delim = 1; 381 if (s == NULL) { 382 s = strstr(httpd->recvbuf, "\n\n"); 383 delim = 2; 384 } 385 if (s == NULL) 386 return (ISC_R_NOTFOUND); 387 388 /* 389 * Determine if this is a POST or GET method. Any other values will 390 * cause an error to be returned. 391 */ 392 if (strncmp(httpd->recvbuf, "GET ", 4) == 0) { 393 httpd->method = ISC_HTTPD_METHODGET; 394 p = httpd->recvbuf + 4; 395 } else if (strncmp(httpd->recvbuf, "POST ", 5) == 0) { 396 httpd->method = ISC_HTTPD_METHODPOST; 397 p = httpd->recvbuf + 5; 398 } else { 399 return (ISC_R_RANGE); 400 } 401 402 /* 403 * From now on, p is the start of our buffer. 404 */ 405 406 /* 407 * Extract the URL. 408 */ 409 s = p; 410 while (LENGTHOK(s) && BUFLENOK(s) && 411 (*s != '\n' && *s != '\r' && *s != '\0' && *s != ' ')) 412 s++; 413 if (!LENGTHOK(s)) 414 return (ISC_R_NOTFOUND); 415 if (!BUFLENOK(s)) 416 return (ISC_R_NOMEMORY); 417 *s = 0; 418 419 /* 420 * Make the URL relative. 421 */ 422 if ((strncmp(p, "http:/", 6) == 0) 423 || (strncmp(p, "https:/", 7) == 0)) { 424 /* Skip first / */ 425 while (*p != '/' && *p != 0) 426 p++; 427 if (*p == 0) 428 return (ISC_R_RANGE); 429 p++; 430 /* Skip second / */ 431 while (*p != '/' && *p != 0) 432 p++; 433 if (*p == 0) 434 return (ISC_R_RANGE); 435 p++; 436 /* Find third / */ 437 while (*p != '/' && *p != 0) 438 p++; 439 if (*p == 0) { 440 p--; 441 *p = '/'; 442 } 443 } 444 445 httpd->url = p; 446 p = s + delim; 447 s = p; 448 449 /* 450 * Now, see if there is a ? mark in the URL. If so, this is 451 * part of the query string, and we will split it from the URL. 452 */ 453 httpd->querystring = strchr(httpd->url, '?'); 454 if (httpd->querystring != NULL) { 455 *(httpd->querystring) = 0; 456 httpd->querystring++; 457 } 458 459 /* 460 * Extract the HTTP/1.X protocol. We will bounce on anything but 461 * HTTP/1.1 for now. 462 */ 463 while (LENGTHOK(s) && BUFLENOK(s) && 464 (*s != '\n' && *s != '\r' && *s != '\0')) 465 s++; 466 if (!LENGTHOK(s)) 467 return (ISC_R_NOTFOUND); 468 if (!BUFLENOK(s)) 469 return (ISC_R_NOMEMORY); 470 *s = 0; 471 if ((strncmp(p, "HTTP/1.0", 8) != 0) 472 && (strncmp(p, "HTTP/1.1", 8) != 0)) 473 return (ISC_R_RANGE); 474 httpd->protocol = p; 475 p = s + 1; 476 s = p; 477 478 httpd->headers = s; 479 480 if (strstr(s, "Connection: close") != NULL) 481 httpd->flags |= HTTPD_CLOSE; 482 483 if (strstr(s, "Host: ") != NULL) 484 httpd->flags |= HTTPD_FOUNDHOST; 485 486 /* 487 * Standards compliance hooks here. 488 */ 489 if (strcmp(httpd->protocol, "HTTP/1.1") == 0 490 && ((httpd->flags & HTTPD_FOUNDHOST) == 0)) 491 return (ISC_R_RANGE); 492 493 EXIT("request"); 494 495 return (ISC_R_SUCCESS); 496 } 497 498 static void 499 isc_httpd_accept(isc_task_t *task, isc_event_t *ev) { 500 isc_result_t result; 501 isc_httpdmgr_t *httpdmgr = ev->ev_arg; 502 isc_httpd_t *httpd; 503 isc_region_t r; 504 isc_socket_newconnev_t *nev = (isc_socket_newconnev_t *)ev; 505 isc_sockaddr_t peeraddr; 506 507 ENTER("accept"); 508 509 LOCK(&httpdmgr->lock); 510 if (MSHUTTINGDOWN(httpdmgr)) { 511 NOTICE("accept shutting down, goto out"); 512 goto out; 513 } 514 515 if (nev->result == ISC_R_CANCELED) { 516 NOTICE("accept canceled, goto out"); 517 goto out; 518 } 519 520 if (nev->result != ISC_R_SUCCESS) { 521 /* XXXMLG log failure */ 522 NOTICE("accept returned failure, goto requeue"); 523 goto requeue; 524 } 525 526 (void)isc_socket_getpeername(nev->newsocket, &peeraddr); 527 if (httpdmgr->client_ok != NULL && 528 !(httpdmgr->client_ok)(&peeraddr, httpdmgr->cb_arg)) { 529 isc_socket_detach(&nev->newsocket); 530 goto requeue; 531 } 532 533 httpd = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpd_t)); 534 if (httpd == NULL) { 535 /* XXXMLG log failure */ 536 NOTICE("accept failed to allocate memory, goto requeue"); 537 isc_socket_detach(&nev->newsocket); 538 goto requeue; 539 } 540 541 httpd->mgr = httpdmgr; 542 ISC_LINK_INIT(httpd, link); 543 ISC_LIST_APPEND(httpdmgr->running, httpd, link); 544 ISC_HTTPD_SETRECV(httpd); 545 httpd->sock = nev->newsocket; 546 isc_socket_setname(httpd->sock, "httpd", NULL); 547 httpd->flags = 0; 548 549 /* 550 * Initialize the buffer for our headers. 551 */ 552 httpd->headerdata = isc_mem_get(httpdmgr->mctx, HTTP_SENDGROW); 553 if (httpd->headerdata == NULL) { 554 isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t)); 555 isc_socket_detach(&nev->newsocket); 556 goto requeue; 557 } 558 httpd->headerlen = HTTP_SENDGROW; 559 isc_buffer_init(&httpd->headerbuffer, httpd->headerdata, 560 httpd->headerlen); 561 562 ISC_LIST_INIT(httpd->bufflist); 563 564 isc_buffer_initnull(&httpd->bodybuffer); 565 reset_client(httpd); 566 567 r.base = (unsigned char *)httpd->recvbuf; 568 r.length = HTTP_RECVLEN - 1; 569 result = isc_socket_recv(httpd->sock, &r, 1, task, isc_httpd_recvdone, 570 httpd); 571 /* FIXME!!! */ 572 POST(result); 573 NOTICE("accept queued recv on socket"); 574 575 requeue: 576 result = isc_socket_accept(httpdmgr->sock, task, isc_httpd_accept, 577 httpdmgr); 578 if (result != ISC_R_SUCCESS) { 579 /* XXXMLG what to do? Log failure... */ 580 NOTICE("accept could not reaccept due to failure"); 581 } 582 583 out: 584 UNLOCK(&httpdmgr->lock); 585 586 httpdmgr_destroy(httpdmgr); 587 588 isc_event_free(&ev); 589 590 EXIT("accept"); 591 } 592 593 static isc_result_t 594 render_404(const char *url, isc_httpdurl_t *urlinfo, 595 const char *querystring, const char *headers, void *arg, 596 unsigned int *retcode, const char **retmsg, 597 const char **mimetype, isc_buffer_t *b, 598 isc_httpdfree_t **freecb, void **freecb_args) 599 { 600 static char msg[] = "No such URL."; 601 602 UNUSED(url); 603 UNUSED(urlinfo); 604 UNUSED(querystring); 605 UNUSED(headers); 606 UNUSED(arg); 607 608 *retcode = 404; 609 *retmsg = "No such URL"; 610 *mimetype = "text/plain"; 611 isc_buffer_reinit(b, msg, strlen(msg)); 612 isc_buffer_add(b, strlen(msg)); 613 *freecb = NULL; 614 *freecb_args = NULL; 615 616 return (ISC_R_SUCCESS); 617 } 618 619 static isc_result_t 620 render_500(const char *url, isc_httpdurl_t *urlinfo, 621 const char *querystring, const char *headers, void *arg, 622 unsigned int *retcode, const char **retmsg, 623 const char **mimetype, isc_buffer_t *b, 624 isc_httpdfree_t **freecb, void **freecb_args) 625 { 626 static char msg[] = "Internal server failure."; 627 628 UNUSED(url); 629 UNUSED(urlinfo); 630 UNUSED(querystring); 631 UNUSED(headers); 632 UNUSED(arg); 633 634 *retcode = 500; 635 *retmsg = "Internal server failure"; 636 *mimetype = "text/plain"; 637 isc_buffer_reinit(b, msg, strlen(msg)); 638 isc_buffer_add(b, strlen(msg)); 639 *freecb = NULL; 640 *freecb_args = NULL; 641 642 return (ISC_R_SUCCESS); 643 } 644 645 static void 646 isc_httpd_recvdone(isc_task_t *task, isc_event_t *ev) { 647 isc_region_t r; 648 isc_result_t result; 649 isc_httpd_t *httpd = ev->ev_arg; 650 isc_socketevent_t *sev = (isc_socketevent_t *)ev; 651 isc_httpdurl_t *url; 652 isc_time_t now; 653 char datebuf[ISC_FORMATHTTPTIMESTAMP_SIZE]; 654 655 ENTER("recv"); 656 657 INSIST(ISC_HTTPD_ISRECV(httpd)); 658 659 if (sev->result != ISC_R_SUCCESS) { 660 NOTICE("recv destroying client"); 661 destroy_client(&httpd); 662 goto out; 663 } 664 665 result = process_request(httpd, sev->n); 666 if (result == ISC_R_NOTFOUND) { 667 if (httpd->recvlen >= HTTP_RECVLEN - 1) { 668 destroy_client(&httpd); 669 goto out; 670 } 671 r.base = (unsigned char *)httpd->recvbuf + httpd->recvlen; 672 r.length = HTTP_RECVLEN - httpd->recvlen - 1; 673 /* check return code? */ 674 (void)isc_socket_recv(httpd->sock, &r, 1, task, 675 isc_httpd_recvdone, httpd); 676 goto out; 677 } else if (result != ISC_R_SUCCESS) { 678 destroy_client(&httpd); 679 goto out; 680 } 681 682 ISC_HTTPD_SETSEND(httpd); 683 684 /* 685 * XXXMLG Call function here. Provide an add-header function 686 * which will append the common headers to a response we generate. 687 */ 688 isc_buffer_initnull(&httpd->bodybuffer); 689 isc_time_now(&now); 690 isc_time_formathttptimestamp(&now, datebuf, sizeof(datebuf)); 691 url = ISC_LIST_HEAD(httpd->mgr->urls); 692 while (url != NULL) { 693 if (strcmp(httpd->url, url->url) == 0) 694 break; 695 url = ISC_LIST_NEXT(url, link); 696 } 697 if (url == NULL) 698 result = httpd->mgr->render_404(httpd->url, NULL, 699 httpd->querystring, 700 NULL, NULL, 701 &httpd->retcode, 702 &httpd->retmsg, 703 &httpd->mimetype, 704 &httpd->bodybuffer, 705 &httpd->freecb, 706 &httpd->freecb_arg); 707 else 708 result = url->action(httpd->url, url, 709 httpd->querystring, 710 httpd->headers, 711 url->action_arg, 712 &httpd->retcode, &httpd->retmsg, 713 &httpd->mimetype, &httpd->bodybuffer, 714 &httpd->freecb, &httpd->freecb_arg); 715 if (result != ISC_R_SUCCESS) { 716 result = httpd->mgr->render_500(httpd->url, url, 717 httpd->querystring, 718 NULL, NULL, 719 &httpd->retcode, 720 &httpd->retmsg, 721 &httpd->mimetype, 722 &httpd->bodybuffer, 723 &httpd->freecb, 724 &httpd->freecb_arg); 725 RUNTIME_CHECK(result == ISC_R_SUCCESS); 726 } 727 728 isc_httpd_response(httpd); 729 isc_httpd_addheader(httpd, "Content-Type", httpd->mimetype); 730 isc_httpd_addheader(httpd, "Date", datebuf); 731 isc_httpd_addheader(httpd, "Expires", datebuf); 732 733 if (url != NULL && url->isstatic) { 734 char loadbuf[ISC_FORMATHTTPTIMESTAMP_SIZE]; 735 isc_time_formathttptimestamp(&url->loadtime, 736 loadbuf, sizeof(loadbuf)); 737 isc_httpd_addheader(httpd, "Last-Modified", loadbuf); 738 isc_httpd_addheader(httpd, "Cache-Control: public", NULL); 739 } else { 740 isc_httpd_addheader(httpd, "Last-Modified", datebuf); 741 isc_httpd_addheader(httpd, "Pragma: no-cache", NULL); 742 isc_httpd_addheader(httpd, "Cache-Control: no-cache", NULL); 743 } 744 745 isc_httpd_addheader(httpd, "Server: libisc", NULL); 746 isc_httpd_addheaderuint(httpd, "Content-Length", 747 isc_buffer_usedlength(&httpd->bodybuffer)); 748 isc_httpd_endheaders(httpd); /* done */ 749 750 ISC_LIST_APPEND(httpd->bufflist, &httpd->headerbuffer, link); 751 /* 752 * Link the data buffer into our send queue, should we have any data 753 * rendered into it. If no data is present, we won't do anything 754 * with the buffer. 755 */ 756 if (isc_buffer_length(&httpd->bodybuffer) > 0) 757 ISC_LIST_APPEND(httpd->bufflist, &httpd->bodybuffer, link); 758 759 /* check return code? */ 760 (void)isc_socket_sendv(httpd->sock, &httpd->bufflist, task, 761 isc_httpd_senddone, httpd); 762 763 out: 764 isc_event_free(&ev); 765 EXIT("recv"); 766 } 767 768 void 769 isc_httpdmgr_shutdown(isc_httpdmgr_t **httpdmgrp) { 770 isc_httpdmgr_t *httpdmgr; 771 isc_httpd_t *httpd; 772 httpdmgr = *httpdmgrp; 773 *httpdmgrp = NULL; 774 775 ENTER("isc_httpdmgr_shutdown"); 776 777 LOCK(&httpdmgr->lock); 778 779 MSETSHUTTINGDOWN(httpdmgr); 780 781 isc_socket_cancel(httpdmgr->sock, httpdmgr->task, ISC_SOCKCANCEL_ALL); 782 783 httpd = ISC_LIST_HEAD(httpdmgr->running); 784 while (httpd != NULL) { 785 isc_socket_cancel(httpd->sock, httpdmgr->task, 786 ISC_SOCKCANCEL_ALL); 787 httpd = ISC_LIST_NEXT(httpd, link); 788 } 789 790 UNLOCK(&httpdmgr->lock); 791 792 EXIT("isc_httpdmgr_shutdown"); 793 } 794 795 static isc_result_t 796 grow_headerspace(isc_httpd_t *httpd) { 797 char *newspace; 798 unsigned int newlen; 799 isc_region_t r; 800 801 newlen = httpd->headerlen + HTTP_SENDGROW; 802 if (newlen > HTTP_SEND_MAXLEN) 803 return (ISC_R_NOSPACE); 804 805 newspace = isc_mem_get(httpd->mgr->mctx, newlen); 806 if (newspace == NULL) 807 return (ISC_R_NOMEMORY); 808 isc_buffer_region(&httpd->headerbuffer, &r); 809 isc_buffer_reinit(&httpd->headerbuffer, newspace, newlen); 810 811 isc_mem_put(httpd->mgr->mctx, r.base, r.length); 812 813 return (ISC_R_SUCCESS); 814 } 815 816 isc_result_t 817 isc_httpd_response(isc_httpd_t *httpd) { 818 isc_result_t result; 819 unsigned int needlen; 820 821 needlen = strlen(httpd->protocol) + 1; /* protocol + space */ 822 needlen += 3 + 1; /* room for response code, always 3 bytes */ 823 needlen += strlen(httpd->retmsg) + 2; /* return msg + CRLF */ 824 825 while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) { 826 result = grow_headerspace(httpd); 827 if (result != ISC_R_SUCCESS) 828 return (result); 829 } 830 831 sprintf(isc_buffer_used(&httpd->headerbuffer), "%s %03d %s\r\n", 832 httpd->protocol, httpd->retcode, httpd->retmsg); 833 isc_buffer_add(&httpd->headerbuffer, needlen); 834 835 return (ISC_R_SUCCESS); 836 } 837 838 isc_result_t 839 isc_httpd_addheader(isc_httpd_t *httpd, const char *name, 840 const char *val) 841 { 842 isc_result_t result; 843 unsigned int needlen; 844 845 needlen = strlen(name); /* name itself */ 846 if (val != NULL) 847 needlen += 2 + strlen(val); /* :<space> and val */ 848 needlen += 2; /* CRLF */ 849 850 while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) { 851 result = grow_headerspace(httpd); 852 if (result != ISC_R_SUCCESS) 853 return (result); 854 } 855 856 if (val != NULL) 857 sprintf(isc_buffer_used(&httpd->headerbuffer), 858 "%s: %s\r\n", name, val); 859 else 860 sprintf(isc_buffer_used(&httpd->headerbuffer), 861 "%s\r\n", name); 862 863 isc_buffer_add(&httpd->headerbuffer, needlen); 864 865 return (ISC_R_SUCCESS); 866 } 867 868 isc_result_t 869 isc_httpd_endheaders(isc_httpd_t *httpd) { 870 isc_result_t result; 871 872 while (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 884 isc_result_t 885 isc_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 while (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 910 static void 911 isc_httpd_senddone(isc_task_t *task, isc_event_t *ev) { 912 isc_httpd_t *httpd = ev->ev_arg; 913 isc_region_t r; 914 isc_socketevent_t *sev = (isc_socketevent_t *)ev; 915 916 ENTER("senddone"); 917 INSIST(ISC_HTTPD_ISSEND(httpd)); 918 919 /* 920 * First, unlink our header buffer from the socket's bufflist. This 921 * is sort of an evil hack, since we know our buffer will be there, 922 * and we know it's address, so we can just remove it directly. 923 */ 924 NOTICE("senddone unlinked header"); 925 ISC_LIST_UNLINK(sev->bufferlist, &httpd->headerbuffer, link); 926 927 /* 928 * We will always want to clean up our receive buffer, even if we 929 * got an error on send or we are shutting down. 930 * 931 * We will pass in the buffer only if there is data in it. If 932 * there is no data, we will pass in a NULL. 933 */ 934 if (httpd->freecb != NULL) { 935 isc_buffer_t *b = NULL; 936 if (isc_buffer_length(&httpd->bodybuffer) > 0) { 937 b = &httpd->bodybuffer; 938 httpd->freecb(b, httpd->freecb_arg); 939 } 940 NOTICE("senddone free callback performed"); 941 } 942 if (ISC_LINK_LINKED(&httpd->bodybuffer, link)) { 943 ISC_LIST_UNLINK(sev->bufferlist, &httpd->bodybuffer, link); 944 NOTICE("senddone body buffer unlinked"); 945 } 946 947 if (sev->result != ISC_R_SUCCESS) { 948 destroy_client(&httpd); 949 goto out; 950 } 951 952 if ((httpd->flags & HTTPD_CLOSE) != 0) { 953 destroy_client(&httpd); 954 goto out; 955 } 956 957 ISC_HTTPD_SETRECV(httpd); 958 959 NOTICE("senddone restarting recv on socket"); 960 961 reset_client(httpd); 962 963 r.base = (unsigned char *)httpd->recvbuf; 964 r.length = HTTP_RECVLEN - 1; 965 /* check return code? */ 966 (void)isc_socket_recv(httpd->sock, &r, 1, task, 967 isc_httpd_recvdone, httpd); 968 969 out: 970 isc_event_free(&ev); 971 EXIT("senddone"); 972 } 973 974 static void 975 reset_client(isc_httpd_t *httpd) { 976 /* 977 * Catch errors here. We MUST be in RECV mode, and we MUST NOT have 978 * any outstanding buffers. If we have buffers, we have a leak. 979 */ 980 INSIST(ISC_HTTPD_ISRECV(httpd)); 981 INSIST(!ISC_LINK_LINKED(&httpd->headerbuffer, link)); 982 INSIST(!ISC_LINK_LINKED(&httpd->bodybuffer, link)); 983 984 httpd->recvbuf[0] = 0; 985 httpd->recvlen = 0; 986 httpd->headers = NULL; 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 997 isc_result_t 998 isc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url, 999 isc_httpdaction_t *func, void *arg) 1000 { 1001 return (isc_httpdmgr_addurl2(httpdmgr, url, ISC_FALSE, func, arg)); 1002 } 1003 1004 isc_result_t 1005 isc_httpdmgr_addurl2(isc_httpdmgr_t *httpdmgr, const char *url, 1006 isc_boolean_t isstatic, 1007 isc_httpdaction_t *func, void *arg) 1008 { 1009 isc_httpdurl_t *item; 1010 1011 if (url == NULL) { 1012 httpdmgr->render_404 = func; 1013 return (ISC_R_SUCCESS); 1014 } 1015 1016 item = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpdurl_t)); 1017 if (item == NULL) 1018 return (ISC_R_NOMEMORY); 1019 1020 item->url = isc_mem_strdup(httpdmgr->mctx, url); 1021 if (item->url == NULL) { 1022 isc_mem_put(httpdmgr->mctx, item, sizeof(isc_httpdurl_t)); 1023 return (ISC_R_NOMEMORY); 1024 } 1025 1026 item->action = func; 1027 item->action_arg = arg; 1028 item->isstatic = isstatic; 1029 isc_time_now(&item->loadtime); 1030 1031 ISC_LINK_INIT(item, link); 1032 ISC_LIST_APPEND(httpdmgr->urls, item, link); 1033 1034 return (ISC_R_SUCCESS); 1035 } 1036