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