1 1.13 christos /* $NetBSD: httpd.c,v 1.13 2026/08/29 14:55:18 christos Exp $ */ 2 1.1 christos 3 1.1 christos /* 4 1.1 christos * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 1.1 christos * 6 1.7 christos * SPDX-License-Identifier: MPL-2.0 7 1.7 christos * 8 1.1 christos * This Source Code Form is subject to the terms of the Mozilla Public 9 1.1 christos * License, v. 2.0. If a copy of the MPL was not distributed with this 10 1.6 christos * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 1.1 christos * 12 1.1 christos * See the COPYRIGHT file distributed with this work for additional 13 1.1 christos * information regarding copyright ownership. 14 1.1 christos */ 15 1.1 christos 16 1.1 christos /*! \file */ 17 1.1 christos 18 1.9 christos #include <ctype.h> 19 1.3 christos #include <inttypes.h> 20 1.3 christos #include <stdbool.h> 21 1.3 christos #include <string.h> 22 1.3 christos 23 1.1 christos #include <isc/buffer.h> 24 1.1 christos #include <isc/httpd.h> 25 1.10 christos #include <isc/list.h> 26 1.1 christos #include <isc/mem.h> 27 1.9 christos #include <isc/netmgr.h> 28 1.4 christos #include <isc/refcount.h> 29 1.9 christos #include <isc/sockaddr.h> 30 1.1 christos #include <isc/string.h> 31 1.1 christos #include <isc/time.h> 32 1.9 christos #include <isc/url.h> 33 1.1 christos #include <isc/util.h> 34 1.1 christos 35 1.9 christos #include "netmgr/netmgr-int.h" 36 1.9 christos #include "picohttpparser.h" 37 1.9 christos 38 1.1 christos #ifdef HAVE_ZLIB 39 1.1 christos #include <zlib.h> 40 1.4 christos #endif /* ifdef HAVE_ZLIB */ 41 1.1 christos 42 1.9 christos /* 43 1.9 christos * Size the recv buffer to hold at maximum two full buffers from isc_nm_read(), 44 1.9 christos * so we don't have to handle the truncation. 45 1.1 christos */ 46 1.9 christos #define HTTP_RECVLEN ISC_NETMGR_TCP_RECVBUF_SIZE * 2 47 1.9 christos #define HTTP_SENDLEN ISC_NETMGR_TCP_RECVBUF_SIZE 48 1.9 christos #define HTTP_HEADERS_NUM 100 49 1.9 christos #define HTTP_MAX_REQUEST_LEN 4096 50 1.9 christos 51 1.9 christos typedef enum httpd_flags { 52 1.9 christos CONNECTION_CLOSE = 1 << 0, /* connection must close */ 53 1.9 christos CONNECTION_KEEP_ALIVE = 1 << 1, /* response needs a keep-alive header */ 54 1.9 christos ACCEPT_DEFLATE = 1 << 2, /* response can be compressed */ 55 1.9 christos } httpd_flags_t; 56 1.4 christos 57 1.4 christos #define HTTPD_MAGIC ISC_MAGIC('H', 't', 'p', 'd') 58 1.4 christos #define VALID_HTTPD(m) ISC_MAGIC_VALID(m, HTTPD_MAGIC) 59 1.4 christos 60 1.4 christos #define HTTPDMGR_MAGIC ISC_MAGIC('H', 'p', 'd', 'm') 61 1.4 christos #define VALID_HTTPDMGR(m) ISC_MAGIC_VALID(m, HTTPDMGR_MAGIC) 62 1.1 christos 63 1.9 christos /*% 64 1.9 christos * HTTP methods. 65 1.9 christos */ 66 1.9 christos typedef enum { METHOD_UNKNOWN = 0, METHOD_GET = 1, METHOD_POST = 2 } method_t; 67 1.9 christos 68 1.9 christos /*% 69 1.9 christos * HTTP urls. These are the URLs we manage, and the function to call to 70 1.9 christos * provide the data for it. We pass in the base url (so the same function 71 1.9 christos * can handle multiple requests), and a structure to fill in to return a 72 1.9 christos * result to the client. We also pass in a pointer to be filled in for 73 1.9 christos * the data cleanup function. 74 1.9 christos */ 75 1.9 christos struct isc_httpdurl { 76 1.9 christos char *url; 77 1.9 christos isc_httpdaction_t *action; 78 1.9 christos void *action_arg; 79 1.9 christos bool isstatic; 80 1.9 christos isc_time_t loadtime; 81 1.9 christos ISC_LINK(isc_httpdurl_t) link; 82 1.9 christos }; 83 1.9 christos 84 1.1 christos /*% http client */ 85 1.1 christos struct isc_httpd { 86 1.4 christos unsigned int magic; /* HTTPD_MAGIC */ 87 1.10 christos isc_refcount_t references; 88 1.9 christos 89 1.4 christos isc_httpdmgr_t *mgr; /*%< our parent */ 90 1.4 christos ISC_LINK(isc_httpd_t) link; 91 1.9 christos 92 1.10 christos isc_nmhandle_t *handle; /* Permanent pointer to handle */ 93 1.1 christos 94 1.1 christos /*% 95 1.1 christos * Received data state. 96 1.1 christos */ 97 1.4 christos char recvbuf[HTTP_RECVLEN]; /*%< receive buffer */ 98 1.9 christos size_t recvlen; /*%< length recv'd */ 99 1.9 christos size_t consume; /*%< length of last command */ 100 1.1 christos 101 1.9 christos method_t method; 102 1.9 christos int minor_version; 103 1.9 christos httpd_flags_t flags; 104 1.9 christos const char *path; 105 1.9 christos isc_url_parser_t up; 106 1.9 christos isc_time_t if_modified_since; 107 1.1 christos }; 108 1.1 christos 109 1.10 christos #if ISC_HTTPD_TRACE 110 1.10 christos #define isc_httpd_ref(ptr) isc_httpd__ref(ptr, __func__, __FILE__, __LINE__) 111 1.10 christos #define isc_httpd_unref(ptr) isc_httpd__unref(ptr, __func__, __FILE__, __LINE__) 112 1.10 christos #define isc_httpd_attach(ptr, ptrp) \ 113 1.10 christos isc_httpd__attach(ptr, ptrp, __func__, __FILE__, __LINE__) 114 1.10 christos #define isc_httpd_detach(ptrp) \ 115 1.10 christos isc_httpd__detach(ptrp, __func__, __FILE__, __LINE__) 116 1.10 christos ISC_REFCOUNT_TRACE_DECL(isc_httpd); 117 1.10 christos #else 118 1.10 christos ISC_REFCOUNT_DECL(isc_httpd); 119 1.10 christos #endif 120 1.10 christos 121 1.1 christos struct isc_httpdmgr { 122 1.4 christos unsigned int magic; /* HTTPDMGR_MAGIC */ 123 1.4 christos isc_refcount_t references; 124 1.4 christos isc_mem_t *mctx; 125 1.9 christos isc_nmsocket_t *sock; 126 1.4 christos 127 1.4 christos isc_httpdclientok_t *client_ok; /*%< client validator */ 128 1.4 christos isc_httpdondestroy_t *ondestroy; /*%< cleanup callback */ 129 1.4 christos void *cb_arg; /*%< argument for the above */ 130 1.4 christos 131 1.4 christos unsigned int flags; 132 1.4 christos ISC_LIST(isc_httpd_t) running; /*%< running clients */ 133 1.4 christos 134 1.4 christos isc_mutex_t lock; 135 1.4 christos 136 1.4 christos ISC_LIST(isc_httpdurl_t) urls; /*%< urls we manage */ 137 1.4 christos isc_httpdaction_t *render_404; 138 1.4 christos isc_httpdaction_t *render_500; 139 1.1 christos }; 140 1.1 christos 141 1.10 christos #if ISC_HTTPD_TRACE 142 1.10 christos #define isc_httpdmgr_ref(ptr) \ 143 1.10 christos isc_httpdmgr__ref(ptr, __func__, __FILE__, __LINE__) 144 1.10 christos #define isc_httpdmgr_unref(ptr) \ 145 1.10 christos isc_httpdmgr__unref(ptr, __func__, __FILE__, __LINE__) 146 1.10 christos #define isc_httpdmgr_attach(ptr, ptrp) \ 147 1.10 christos isc_httpdmgr__attach(ptr, ptrp, __func__, __FILE__, __LINE__) 148 1.10 christos #define isc_httpdmgr_detach(ptrp) \ 149 1.10 christos isc_httpdmgr__detach(ptrp, __func__, __FILE__, __LINE__) 150 1.10 christos ISC_REFCOUNT_TRACE_DECL(isc_httpdmgr); 151 1.10 christos #else 152 1.10 christos ISC_REFCOUNT_DECL(isc_httpdmgr); 153 1.10 christos #endif 154 1.10 christos 155 1.9 christos typedef struct isc_httpd_sendreq { 156 1.9 christos isc_mem_t *mctx; 157 1.9 christos isc_httpd_t *httpd; 158 1.1 christos 159 1.9 christos /*% 160 1.9 christos * Transmit data state. 161 1.9 christos * 162 1.9 christos * This is the data buffer we will transmit. 163 1.9 christos * 164 1.9 christos * This free function pointer is filled in by the rendering function 165 1.9 christos * we call. The free function is called after the data is transmitted 166 1.9 christos * to the client. 167 1.9 christos * 168 1.9 christos * We currently use three buffers total: 169 1.9 christos * 170 1.9 christos * sendbuffer - gets filled as we gather the data 171 1.9 christos * 172 1.9 christos * bodybuffer - for the client to fill in (which it manages, it provides 173 1.9 christos * the space for it, etc) -- we will pass that buffer structure back to 174 1.9 christos * the caller, who is responsible for managing the space it may have 175 1.9 christos * allocated as backing store for it. we only allocate the buffer 176 1.9 christos * itself, not the backing store. 177 1.9 christos * 178 1.9 christos * compbuffer - managed by us, that contains the compressed HTTP data, 179 1.9 christos * if compression is used. 180 1.9 christos */ 181 1.9 christos isc_buffer_t *sendbuffer; 182 1.9 christos isc_buffer_t *compbuffer; 183 1.1 christos 184 1.9 christos isc_buffer_t bodybuffer; 185 1.4 christos 186 1.9 christos const char *mimetype; 187 1.9 christos unsigned int retcode; 188 1.9 christos const char *retmsg; 189 1.9 christos isc_httpdfree_t *freecb; 190 1.9 christos void *freecb_arg; 191 1.1 christos 192 1.9 christos } isc_httpd_sendreq_t; 193 1.1 christos 194 1.9 christos static isc_result_t 195 1.9 christos httpd_newconn(isc_nmhandle_t *, isc_result_t, void *); 196 1.1 christos static void 197 1.9 christos httpd_request(isc_nmhandle_t *, isc_result_t, isc_region_t *, void *); 198 1.4 christos static void 199 1.9 christos httpd_senddone(isc_nmhandle_t *, isc_result_t, void *); 200 1.4 christos static void 201 1.10 christos httpd_free(isc_httpd_t *httpd); 202 1.4 christos 203 1.4 christos static void 204 1.9 christos httpd_addheader(isc_httpd_sendreq_t *, const char *, const char *); 205 1.4 christos static void 206 1.9 christos httpd_addheaderuint(isc_httpd_sendreq_t *, const char *, int); 207 1.4 christos static void 208 1.9 christos httpd_endheaders(isc_httpd_sendreq_t *); 209 1.7 christos static void 210 1.9 christos httpd_response(isc_httpd_t *, isc_httpd_sendreq_t *); 211 1.1 christos 212 1.9 christos static isc_result_t 213 1.9 christos process_request(isc_httpd_t *, size_t); 214 1.8 christos 215 1.9 christos static isc_httpdaction_t render_404; 216 1.9 christos static isc_httpdaction_t render_500; 217 1.4 christos 218 1.4 christos #if ENABLE_AFL 219 1.9 christos static void (*finishhook)(void) = NULL; 220 1.4 christos #endif /* ENABLE_AFL */ 221 1.4 christos 222 1.1 christos isc_result_t 223 1.9 christos isc_httpdmgr_create(isc_nm_t *nm, isc_mem_t *mctx, isc_sockaddr_t *addr, 224 1.1 christos isc_httpdclientok_t *client_ok, 225 1.1 christos isc_httpdondestroy_t *ondestroy, void *cb_arg, 226 1.9 christos isc_httpdmgr_t **httpdmgrp) { 227 1.1 christos isc_result_t result; 228 1.9 christos isc_httpdmgr_t *httpdmgr = NULL; 229 1.1 christos 230 1.9 christos REQUIRE(nm != NULL); 231 1.1 christos REQUIRE(mctx != NULL); 232 1.1 christos REQUIRE(httpdmgrp != NULL && *httpdmgrp == NULL); 233 1.1 christos 234 1.1 christos httpdmgr = isc_mem_get(mctx, sizeof(isc_httpdmgr_t)); 235 1.9 christos *httpdmgr = (isc_httpdmgr_t){ .client_ok = client_ok, 236 1.4 christos .ondestroy = ondestroy, 237 1.4 christos .cb_arg = cb_arg, 238 1.4 christos .render_404 = render_404, 239 1.4 christos .render_500 = render_500 }; 240 1.1 christos 241 1.3 christos isc_mutex_init(&httpdmgr->lock); 242 1.1 christos isc_mem_attach(mctx, &httpdmgr->mctx); 243 1.1 christos 244 1.1 christos ISC_LIST_INIT(httpdmgr->running); 245 1.1 christos ISC_LIST_INIT(httpdmgr->urls); 246 1.1 christos 247 1.4 christos isc_refcount_init(&httpdmgr->references, 1); 248 1.4 christos 249 1.10 christos CHECK(isc_nm_listentcp(nm, ISC_NM_LISTEN_ONE, addr, httpd_newconn, 250 1.10 christos httpdmgr, 5, NULL, &httpdmgr->sock)); 251 1.1 christos 252 1.4 christos httpdmgr->magic = HTTPDMGR_MAGIC; 253 1.9 christos *httpdmgrp = httpdmgr; 254 1.4 christos 255 1.10 christos return ISC_R_SUCCESS; 256 1.1 christos 257 1.4 christos cleanup: 258 1.4 christos httpdmgr->magic = 0; 259 1.6 christos isc_refcount_decrementz(&httpdmgr->references); 260 1.4 christos isc_refcount_destroy(&httpdmgr->references); 261 1.1 christos isc_mem_detach(&httpdmgr->mctx); 262 1.3 christos isc_mutex_destroy(&httpdmgr->lock); 263 1.1 christos isc_mem_put(mctx, httpdmgr, sizeof(isc_httpdmgr_t)); 264 1.9 christos 265 1.10 christos return result; 266 1.4 christos } 267 1.4 christos 268 1.4 christos static void 269 1.4 christos destroy_httpdmgr(isc_httpdmgr_t *httpdmgr) { 270 1.4 christos isc_refcount_destroy(&httpdmgr->references); 271 1.1 christos 272 1.1 christos LOCK(&httpdmgr->lock); 273 1.1 christos 274 1.9 christos REQUIRE((httpdmgr->flags & ISC_HTTPDMGR_SHUTTINGDOWN) != 0); 275 1.9 christos REQUIRE(ISC_LIST_EMPTY(httpdmgr->running)); 276 1.9 christos 277 1.4 christos httpdmgr->magic = 0; 278 1.1 christos 279 1.9 christos if (httpdmgr->sock != NULL) { 280 1.9 christos isc_nmsocket_close(&httpdmgr->sock); 281 1.9 christos } 282 1.1 christos 283 1.1 christos /* 284 1.1 christos * Clear out the list of all actions we know about. Just free the 285 1.1 christos * memory. 286 1.1 christos */ 287 1.10 christos isc_httpdurl_t *url, *next; 288 1.11 christos ISC_LIST_FOREACH_SAFE(httpdmgr->urls, url, link, next) { 289 1.1 christos isc_mem_free(httpdmgr->mctx, url->url); 290 1.1 christos ISC_LIST_UNLINK(httpdmgr->urls, url, link); 291 1.1 christos isc_mem_put(httpdmgr->mctx, url, sizeof(isc_httpdurl_t)); 292 1.1 christos } 293 1.1 christos 294 1.1 christos UNLOCK(&httpdmgr->lock); 295 1.3 christos isc_mutex_destroy(&httpdmgr->lock); 296 1.1 christos 297 1.4 christos if (httpdmgr->ondestroy != NULL) { 298 1.1 christos (httpdmgr->ondestroy)(httpdmgr->cb_arg); 299 1.4 christos } 300 1.4 christos isc_mem_putanddetach(&httpdmgr->mctx, httpdmgr, sizeof(isc_httpdmgr_t)); 301 1.1 christos } 302 1.1 christos 303 1.10 christos #if ISC_HTTPD_TRACE 304 1.10 christos ISC_REFCOUNT_TRACE_IMPL(isc_httpdmgr, destroy_httpdmgr) 305 1.10 christos #else 306 1.10 christos ISC_REFCOUNT_IMPL(isc_httpdmgr, destroy_httpdmgr); 307 1.10 christos #endif 308 1.10 christos 309 1.9 christos static bool 310 1.9 christos name_match(const struct phr_header *header, const char *match) { 311 1.9 christos size_t match_len = strlen(match); 312 1.9 christos if (match_len != header->name_len) { 313 1.10 christos return false; 314 1.9 christos } 315 1.10 christos return strncasecmp(header->name, match, match_len) == 0; 316 1.9 christos } 317 1.1 christos 318 1.3 christos static bool 319 1.9 christos value_match(const struct phr_header *header, const char *match) { 320 1.9 christos size_t match_len = strlen(match); 321 1.9 christos size_t limit; 322 1.9 christos 323 1.9 christos if (match_len > header->value_len) { 324 1.10 christos return false; 325 1.1 christos } 326 1.1 christos 327 1.9 christos limit = header->value_len - match_len + 1; 328 1.1 christos 329 1.9 christos for (size_t i = 0; i < limit; i++) { 330 1.9 christos if (isspace((unsigned char)header->value[i])) { 331 1.9 christos while (i < limit && 332 1.9 christos isspace((unsigned char)header->value[i])) 333 1.9 christos { 334 1.9 christos i++; 335 1.4 christos } 336 1.1 christos continue; 337 1.1 christos } 338 1.1 christos 339 1.9 christos if (strncasecmp(&header->value[i], match, match_len) == 0) { 340 1.9 christos i += match_len; 341 1.1 christos /* 342 1.9 christos * Sanity check; f.e. for 'deflate' match only 343 1.9 christos * 'deflate[,;]', but not 'deflateyou' 344 1.1 christos */ 345 1.9 christos if (i == header->value_len || header->value[i] == ',' || 346 1.9 christos header->value[i] == ';') 347 1.9 christos { 348 1.10 christos return true; 349 1.4 christos } 350 1.1 christos } 351 1.9 christos 352 1.9 christos while (i < limit && header->value[i] != ',') { 353 1.9 christos i++; 354 1.9 christos } 355 1.1 christos } 356 1.10 christos return false; 357 1.1 christos } 358 1.1 christos 359 1.1 christos static isc_result_t 360 1.9 christos process_request(isc_httpd_t *httpd, size_t last_len) { 361 1.9 christos int pret; 362 1.9 christos const char *method = NULL; 363 1.9 christos size_t method_len = 0; 364 1.9 christos const char *path; 365 1.9 christos size_t path_len = 0; 366 1.9 christos struct phr_header headers[HTTP_HEADERS_NUM]; 367 1.9 christos size_t num_headers; 368 1.9 christos isc_result_t result; 369 1.1 christos 370 1.9 christos num_headers = ARRAY_SIZE(headers); 371 1.1 christos 372 1.9 christos pret = phr_parse_request(httpd->recvbuf, httpd->recvlen, &method, 373 1.9 christos &method_len, &path, &path_len, 374 1.9 christos &httpd->minor_version, headers, &num_headers, 375 1.9 christos last_len); 376 1.1 christos 377 1.9 christos if (pret == -1) { 378 1.9 christos /* Parse Error */ 379 1.10 christos return ISC_R_UNEXPECTED; 380 1.9 christos } else if (pret == -2) { 381 1.9 christos /* Need more data */ 382 1.10 christos return ISC_R_NOMORE; 383 1.1 christos } 384 1.9 christos 385 1.9 christos INSIST(pret > 0); 386 1.9 christos 387 1.9 christos if (pret > HTTP_MAX_REQUEST_LEN) { 388 1.10 christos return ISC_R_RANGE; 389 1.4 christos } 390 1.1 christos 391 1.9 christos httpd->consume = pret; 392 1.1 christos 393 1.1 christos /* 394 1.1 christos * Determine if this is a POST or GET method. Any other values will 395 1.1 christos * cause an error to be returned. 396 1.1 christos */ 397 1.9 christos if (strncmp(method, "GET ", method_len) == 0) { 398 1.9 christos httpd->method = METHOD_GET; 399 1.9 christos } else if (strncmp(method, "POST ", method_len) == 0) { 400 1.9 christos httpd->method = METHOD_POST; 401 1.1 christos } else { 402 1.10 christos return ISC_R_RANGE; 403 1.1 christos } 404 1.1 christos 405 1.1 christos /* 406 1.9 christos * Parse the URL 407 1.1 christos */ 408 1.9 christos result = isc_url_parse(path, path_len, 0, &httpd->up); 409 1.9 christos if (result != ISC_R_SUCCESS) { 410 1.10 christos return result; 411 1.9 christos } 412 1.9 christos httpd->path = path; 413 1.1 christos 414 1.1 christos /* 415 1.9 christos * Examine headers that can affect this request's response 416 1.1 christos */ 417 1.9 christos httpd->flags = 0; 418 1.9 christos 419 1.9 christos size_t content_len = 0; 420 1.9 christos bool keep_alive = false; 421 1.9 christos bool host_header = false; 422 1.9 christos 423 1.9 christos isc_time_set(&httpd->if_modified_since, 0, 0); 424 1.9 christos 425 1.9 christos for (size_t i = 0; i < num_headers; i++) { 426 1.9 christos struct phr_header *header = &headers[i]; 427 1.9 christos 428 1.9 christos if (name_match(header, "Content-Length")) { 429 1.9 christos char *endptr; 430 1.12 christos errno = 0; 431 1.9 christos long val = strtol(header->value, &endptr, 10); 432 1.9 christos 433 1.9 christos /* ensure we consumed all digits */ 434 1.9 christos if ((header->value + header->value_len) != endptr) { 435 1.10 christos return ISC_R_BADNUMBER; 436 1.9 christos } 437 1.9 christos /* ensure there was no minus sign */ 438 1.9 christos if (val < 0) { 439 1.10 christos return ISC_R_BADNUMBER; 440 1.9 christos } 441 1.9 christos /* ensure it did not overflow */ 442 1.9 christos if (errno != 0) { 443 1.10 christos return ISC_R_RANGE; 444 1.9 christos } 445 1.9 christos content_len = val; 446 1.9 christos } else if (name_match(header, "Connection")) { 447 1.9 christos /* multiple fields in a connection header are allowed */ 448 1.9 christos if (value_match(header, "close")) { 449 1.9 christos httpd->flags |= CONNECTION_CLOSE; 450 1.9 christos } 451 1.9 christos if (value_match(header, "keep-alive")) { 452 1.9 christos keep_alive = true; 453 1.9 christos } 454 1.9 christos } else if (name_match(header, "Host")) { 455 1.9 christos host_header = true; 456 1.9 christos } else if (name_match(header, "Accept-Encoding")) { 457 1.9 christos if (value_match(header, "deflate")) { 458 1.9 christos httpd->flags |= ACCEPT_DEFLATE; 459 1.9 christos } 460 1.9 christos } else if (name_match(header, "If-Modified-Since") && 461 1.9 christos header->value_len < ISC_FORMATHTTPTIMESTAMP_SIZE) 462 1.9 christos { 463 1.9 christos char timestamp[ISC_FORMATHTTPTIMESTAMP_SIZE + 1]; 464 1.9 christos memmove(timestamp, header->value, header->value_len); 465 1.9 christos timestamp[header->value_len] = 0; 466 1.9 christos 467 1.9 christos /* Ignore the value if it can't be parsed */ 468 1.9 christos (void)isc_time_parsehttptimestamp( 469 1.9 christos timestamp, &httpd->if_modified_since); 470 1.1 christos } 471 1.1 christos } 472 1.1 christos 473 1.1 christos /* 474 1.9 christos * The Content-Length is optional in an HTTP request. 475 1.9 christos * For a GET the length must be zero. 476 1.1 christos */ 477 1.9 christos if (httpd->method == METHOD_GET && content_len != 0) { 478 1.10 christos return ISC_R_BADNUMBER; 479 1.1 christos } 480 1.1 christos 481 1.9 christos if (content_len >= HTTP_MAX_REQUEST_LEN) { 482 1.10 christos return ISC_R_RANGE; 483 1.4 christos } 484 1.1 christos 485 1.9 christos size_t consume = httpd->consume + content_len; 486 1.9 christos if (consume > httpd->recvlen) { 487 1.9 christos /* The request data isn't complete yet. */ 488 1.10 christos return ISC_R_NOMORE; 489 1.4 christos } 490 1.1 christos 491 1.9 christos /* Consume the request's data, which we do not use. */ 492 1.9 christos httpd->consume = consume; 493 1.1 christos 494 1.9 christos switch (httpd->minor_version) { 495 1.9 christos case 0: 496 1.9 christos /* 497 1.9 christos * RFC 9112 section 9.3 says close takes priority if 498 1.9 christos * keep-alive is also present 499 1.9 christos */ 500 1.9 christos if ((httpd->flags & CONNECTION_CLOSE) == 0 && keep_alive) { 501 1.9 christos httpd->flags |= CONNECTION_KEEP_ALIVE; 502 1.4 christos } else { 503 1.9 christos httpd->flags |= CONNECTION_CLOSE; 504 1.9 christos } 505 1.9 christos break; 506 1.9 christos case 1: 507 1.9 christos if (!host_header) { 508 1.10 christos return ISC_R_RANGE; 509 1.4 christos } 510 1.9 christos break; 511 1.9 christos default: 512 1.10 christos return ISC_R_UNEXPECTED; 513 1.1 christos } 514 1.1 christos 515 1.1 christos /* 516 1.9 christos * Looks like a a valid request, so now we know we won't have 517 1.9 christos * to process this buffer again. We can NULL-terminate the 518 1.9 christos * URL for the caller's benefit, and set recvlen to 0 so 519 1.9 christos * the next read will overwrite this one instead of appending 520 1.9 christos * to the buffer. 521 1.1 christos */ 522 1.1 christos 523 1.10 christos return ISC_R_SUCCESS; 524 1.4 christos } 525 1.1 christos 526 1.4 christos static void 527 1.10 christos httpd_free(isc_httpd_t *httpd) { 528 1.9 christos isc_httpdmgr_t *httpdmgr = NULL; 529 1.9 christos 530 1.9 christos REQUIRE(VALID_HTTPD(httpd)); 531 1.9 christos 532 1.9 christos httpdmgr = httpd->mgr; 533 1.4 christos 534 1.4 christos REQUIRE(VALID_HTTPDMGR(httpdmgr)); 535 1.4 christos 536 1.9 christos LOCK(&httpdmgr->lock); 537 1.9 christos ISC_LIST_UNLINK(httpdmgr->running, httpd, link); 538 1.9 christos UNLOCK(&httpdmgr->lock); 539 1.9 christos 540 1.9 christos httpd->recvbuf[0] = 0; 541 1.9 christos httpd->recvlen = 0; 542 1.9 christos httpd->consume = 0; 543 1.9 christos httpd->method = METHOD_UNKNOWN; 544 1.9 christos httpd->flags = 0; 545 1.9 christos 546 1.9 christos httpd->minor_version = -1; 547 1.9 christos httpd->path = NULL; 548 1.9 christos httpd->up = (isc_url_parser_t){ 0 }; 549 1.9 christos isc_time_set(&httpd->if_modified_since, 0, 0); 550 1.10 christos 551 1.10 christos httpd->magic = 0; 552 1.10 christos httpd->mgr = NULL; 553 1.10 christos 554 1.10 christos isc_mem_put(httpdmgr->mctx, httpd, sizeof(*httpd)); 555 1.10 christos 556 1.10 christos isc_httpdmgr_detach(&httpdmgr); 557 1.10 christos 558 1.10 christos #if ENABLE_AFL 559 1.10 christos if (finishhook != NULL) { 560 1.10 christos finishhook(); 561 1.10 christos } 562 1.10 christos #endif /* ENABLE_AFL */ 563 1.9 christos } 564 1.9 christos 565 1.10 christos #if ISC_HTTPD_TRACE 566 1.10 christos ISC_REFCOUNT_TRACE_IMPL(isc_httpd, httpd_free) 567 1.10 christos #else 568 1.10 christos ISC_REFCOUNT_IMPL(isc_httpd, httpd_free); 569 1.10 christos #endif 570 1.10 christos 571 1.9 christos static void 572 1.9 christos isc__httpd_sendreq_free(isc_httpd_sendreq_t *req) { 573 1.9 christos /* Clean up buffers */ 574 1.9 christos 575 1.9 christos isc_buffer_free(&req->sendbuffer); 576 1.9 christos 577 1.9 christos isc_mem_putanddetach(&req->mctx, req, sizeof(*req)); 578 1.9 christos } 579 1.9 christos 580 1.9 christos static isc_httpd_sendreq_t * 581 1.9 christos isc__httpd_sendreq_new(isc_httpd_t *httpd) { 582 1.9 christos isc_httpdmgr_t *httpdmgr = httpd->mgr; 583 1.9 christos isc_httpd_sendreq_t *req; 584 1.4 christos 585 1.9 christos REQUIRE(VALID_HTTPDMGR(httpdmgr)); 586 1.4 christos 587 1.9 christos req = isc_mem_get(httpdmgr->mctx, sizeof(*req)); 588 1.9 christos *req = (isc_httpd_sendreq_t){ 0 }; 589 1.4 christos 590 1.9 christos isc_mem_attach(httpdmgr->mctx, &req->mctx); 591 1.4 christos 592 1.4 christos /* 593 1.4 christos * Initialize the buffer for our headers. 594 1.4 christos */ 595 1.9 christos isc_buffer_allocate(req->mctx, &req->sendbuffer, HTTP_SENDLEN); 596 1.9 christos isc_buffer_clear(req->sendbuffer); 597 1.9 christos 598 1.9 christos isc_buffer_initnull(&req->bodybuffer); 599 1.4 christos 600 1.10 christos isc_httpd_attach(httpd, &req->httpd); 601 1.4 christos 602 1.10 christos return req; 603 1.1 christos } 604 1.1 christos 605 1.1 christos static void 606 1.9 christos new_httpd(isc_httpdmgr_t *httpdmgr, isc_nmhandle_t *handle) { 607 1.4 christos isc_httpd_t *httpd = NULL; 608 1.1 christos 609 1.4 christos REQUIRE(VALID_HTTPDMGR(httpdmgr)); 610 1.1 christos 611 1.10 christos httpd = isc_mem_get(httpdmgr->mctx, sizeof(*httpd)); 612 1.10 christos *httpd = (isc_httpd_t){ 613 1.10 christos .magic = HTTPD_MAGIC, 614 1.10 christos .link = ISC_LINK_INITIALIZER, 615 1.10 christos .references = ISC_REFCOUNT_INITIALIZER(1), 616 1.10 christos }; 617 1.1 christos 618 1.10 christos isc_nmhandle_attach(handle, &httpd->handle); 619 1.1 christos 620 1.10 christos isc_httpdmgr_attach(httpdmgr, &httpd->mgr); 621 1.1 christos 622 1.9 christos LOCK(&httpdmgr->lock); 623 1.9 christos ISC_LIST_APPEND(httpdmgr->running, httpd, link); 624 1.9 christos UNLOCK(&httpdmgr->lock); 625 1.1 christos 626 1.10 christos isc_nm_read(handle, httpd_request, httpd); 627 1.9 christos } 628 1.1 christos 629 1.9 christos static isc_result_t 630 1.9 christos httpd_newconn(isc_nmhandle_t *handle, isc_result_t result, void *arg) { 631 1.9 christos isc_httpdmgr_t *httpdmgr = (isc_httpdmgr_t *)arg; 632 1.9 christos isc_sockaddr_t peeraddr; 633 1.4 christos 634 1.9 christos REQUIRE(VALID_HTTPDMGR(httpdmgr)); 635 1.1 christos 636 1.9 christos if ((httpdmgr->flags & ISC_HTTPDMGR_SHUTTINGDOWN) != 0) { 637 1.10 christos return ISC_R_CANCELED; 638 1.9 christos } else if (result == ISC_R_CANCELED) { 639 1.9 christos isc_httpdmgr_shutdown(&httpdmgr); 640 1.10 christos return result; 641 1.9 christos } else if (result != ISC_R_SUCCESS) { 642 1.10 christos return result; 643 1.9 christos } 644 1.1 christos 645 1.9 christos peeraddr = isc_nmhandle_peeraddr(handle); 646 1.9 christos if (httpdmgr->client_ok != NULL && 647 1.9 christos !(httpdmgr->client_ok)(&peeraddr, httpdmgr->cb_arg)) 648 1.9 christos { 649 1.10 christos return ISC_R_FAILURE; 650 1.4 christos } 651 1.1 christos 652 1.9 christos new_httpd(httpdmgr, handle); 653 1.9 christos 654 1.10 christos return ISC_R_SUCCESS; 655 1.1 christos } 656 1.1 christos 657 1.1 christos static isc_result_t 658 1.9 christos render_404(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, void *arg, 659 1.9 christos unsigned int *retcode, const char **retmsg, const char **mimetype, 660 1.9 christos isc_buffer_t *b, isc_httpdfree_t **freecb, void **freecb_args) { 661 1.1 christos static char msg[] = "No such URL.\r\n"; 662 1.1 christos 663 1.9 christos UNUSED(httpd); 664 1.1 christos UNUSED(urlinfo); 665 1.1 christos UNUSED(arg); 666 1.1 christos 667 1.1 christos *retcode = 404; 668 1.1 christos *retmsg = "No such URL"; 669 1.1 christos *mimetype = "text/plain"; 670 1.1 christos isc_buffer_reinit(b, msg, strlen(msg)); 671 1.1 christos isc_buffer_add(b, strlen(msg)); 672 1.1 christos *freecb = NULL; 673 1.1 christos *freecb_args = NULL; 674 1.1 christos 675 1.10 christos return ISC_R_SUCCESS; 676 1.1 christos } 677 1.1 christos 678 1.1 christos static isc_result_t 679 1.9 christos render_500(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, void *arg, 680 1.9 christos unsigned int *retcode, const char **retmsg, const char **mimetype, 681 1.9 christos isc_buffer_t *b, isc_httpdfree_t **freecb, void **freecb_args) { 682 1.1 christos static char msg[] = "Internal server failure.\r\n"; 683 1.1 christos 684 1.9 christos UNUSED(httpd); 685 1.1 christos UNUSED(urlinfo); 686 1.1 christos UNUSED(arg); 687 1.1 christos 688 1.1 christos *retcode = 500; 689 1.1 christos *retmsg = "Internal server failure"; 690 1.1 christos *mimetype = "text/plain"; 691 1.1 christos isc_buffer_reinit(b, msg, strlen(msg)); 692 1.1 christos isc_buffer_add(b, strlen(msg)); 693 1.1 christos *freecb = NULL; 694 1.1 christos *freecb_args = NULL; 695 1.1 christos 696 1.10 christos return ISC_R_SUCCESS; 697 1.1 christos } 698 1.1 christos 699 1.1 christos #ifdef HAVE_ZLIB 700 1.1 christos /*%< 701 1.1 christos * Tries to compress httpd->bodybuffer to httpd->compbuffer, extending it 702 1.1 christos * if necessary. 703 1.1 christos * 704 1.1 christos * Requires: 705 1.1 christos *\li httpd a valid isc_httpd_t object 706 1.1 christos * 707 1.1 christos * Returns: 708 1.1 christos *\li #ISC_R_SUCCESS -- all is well. 709 1.1 christos *\li #ISC_R_NOMEMORY -- not enough memory to compress data 710 1.1 christos *\li #ISC_R_FAILURE -- error during compression or compressed 711 1.1 christos * data would be larger than input data 712 1.1 christos */ 713 1.1 christos static isc_result_t 714 1.9 christos httpd_compress(isc_httpd_sendreq_t *req) { 715 1.1 christos z_stream zstr; 716 1.9 christos int ret, inputlen; 717 1.1 christos 718 1.1 christos /* 719 1.1 christos * We're setting output buffer size to input size so it fails if the 720 1.1 christos * compressed data size would be bigger than the input size. 721 1.1 christos */ 722 1.9 christos inputlen = isc_buffer_usedlength(&req->bodybuffer); 723 1.9 christos if (inputlen == 0) { 724 1.10 christos return ISC_R_FAILURE; 725 1.9 christos } 726 1.9 christos 727 1.9 christos isc_buffer_allocate(req->mctx, &req->compbuffer, inputlen); 728 1.9 christos isc_buffer_clear(req->compbuffer); 729 1.1 christos 730 1.9 christos zstr = (z_stream){ 731 1.9 christos .total_in = inputlen, 732 1.9 christos .avail_out = inputlen, 733 1.9 christos .avail_in = inputlen, 734 1.9 christos .next_in = isc_buffer_base(&req->bodybuffer), 735 1.9 christos .next_out = isc_buffer_base(req->compbuffer), 736 1.9 christos }; 737 1.1 christos 738 1.1 christos ret = deflateInit(&zstr, Z_DEFAULT_COMPRESSION); 739 1.1 christos if (ret == Z_OK) { 740 1.1 christos ret = deflate(&zstr, Z_FINISH); 741 1.1 christos } 742 1.1 christos deflateEnd(&zstr); 743 1.1 christos if (ret == Z_STREAM_END) { 744 1.9 christos isc_buffer_add(req->compbuffer, zstr.total_out); 745 1.10 christos return ISC_R_SUCCESS; 746 1.1 christos } else { 747 1.9 christos isc_buffer_free(&req->compbuffer); 748 1.10 christos return ISC_R_FAILURE; 749 1.1 christos } 750 1.1 christos } 751 1.4 christos #endif /* ifdef HAVE_ZLIB */ 752 1.1 christos 753 1.13 christos static isc_result_t 754 1.10 christos prepare_response(void *arg) { 755 1.10 christos isc_httpd_sendreq_t *req = arg; 756 1.10 christos isc_httpd_t *httpd = req->httpd; 757 1.10 christos isc_httpdmgr_t *mgr = httpd->mgr; 758 1.10 christos isc_time_t now = isc_time_now(); 759 1.9 christos char datebuf[ISC_FORMATHTTPTIMESTAMP_SIZE]; 760 1.9 christos const char *path = "/"; 761 1.9 christos size_t path_len = 1; 762 1.3 christos bool is_compressed = false; 763 1.9 christos isc_httpdurl_t *url = NULL; 764 1.9 christos isc_result_t result; 765 1.1 christos 766 1.4 christos REQUIRE(VALID_HTTPD(httpd)); 767 1.10 christos REQUIRE(req != NULL); 768 1.1 christos 769 1.9 christos isc_time_formathttptimestamp(&now, datebuf, sizeof(datebuf)); 770 1.1 christos 771 1.9 christos if (httpd->up.field_set & (1 << ISC_UF_PATH)) { 772 1.9 christos path = &httpd->path[httpd->up.field_data[ISC_UF_PATH].off]; 773 1.9 christos path_len = httpd->up.field_data[ISC_UF_PATH].len; 774 1.1 christos } 775 1.1 christos 776 1.9 christos LOCK(&mgr->lock); 777 1.11 christos ISC_LIST_FOREACH(mgr->urls, url, link) { 778 1.9 christos if (strncmp(path, url->url, path_len) == 0) { 779 1.1 christos break; 780 1.4 christos } 781 1.1 christos } 782 1.9 christos UNLOCK(&mgr->lock); 783 1.9 christos 784 1.4 christos if (url == NULL) { 785 1.9 christos result = mgr->render_404(httpd, NULL, NULL, &req->retcode, 786 1.9 christos &req->retmsg, &req->mimetype, 787 1.9 christos &req->bodybuffer, &req->freecb, 788 1.9 christos &req->freecb_arg); 789 1.4 christos } else { 790 1.9 christos result = url->action(httpd, url, url->action_arg, &req->retcode, 791 1.9 christos &req->retmsg, &req->mimetype, 792 1.9 christos &req->bodybuffer, &req->freecb, 793 1.9 christos &req->freecb_arg); 794 1.4 christos } 795 1.1 christos if (result != ISC_R_SUCCESS) { 796 1.9 christos result = mgr->render_500(httpd, url, NULL, &req->retcode, 797 1.9 christos &req->retmsg, &req->mimetype, 798 1.9 christos &req->bodybuffer, &req->freecb, 799 1.9 christos &req->freecb_arg); 800 1.1 christos RUNTIME_CHECK(result == ISC_R_SUCCESS); 801 1.1 christos } 802 1.1 christos 803 1.1 christos #ifdef HAVE_ZLIB 804 1.9 christos if ((httpd->flags & ACCEPT_DEFLATE) != 0) { 805 1.9 christos result = httpd_compress(req); 806 1.4 christos if (result == ISC_R_SUCCESS) { 807 1.4 christos is_compressed = true; 808 1.4 christos } 809 1.1 christos } 810 1.4 christos #endif /* ifdef HAVE_ZLIB */ 811 1.1 christos 812 1.9 christos httpd_response(httpd, req); 813 1.9 christos /* RFC 9112 9.6: SHOULD send Connection: close in last response */ 814 1.9 christos if ((httpd->flags & CONNECTION_CLOSE) != 0) { 815 1.9 christos httpd_addheader(req, "Connection", "close"); 816 1.9 christos } else if ((httpd->flags & CONNECTION_KEEP_ALIVE) != 0) { 817 1.9 christos httpd_addheader(req, "Connection", "Keep-Alive"); 818 1.9 christos } 819 1.9 christos httpd_addheader(req, "Content-Type", req->mimetype); 820 1.9 christos httpd_addheader(req, "Date", datebuf); 821 1.9 christos httpd_addheader(req, "Expires", datebuf); 822 1.1 christos 823 1.1 christos if (url != NULL && url->isstatic) { 824 1.1 christos char loadbuf[ISC_FORMATHTTPTIMESTAMP_SIZE]; 825 1.4 christos isc_time_formathttptimestamp(&url->loadtime, loadbuf, 826 1.4 christos sizeof(loadbuf)); 827 1.9 christos httpd_addheader(req, "Last-Modified", loadbuf); 828 1.9 christos httpd_addheader(req, "Cache-Control: public", NULL); 829 1.1 christos } else { 830 1.9 christos httpd_addheader(req, "Last-Modified", datebuf); 831 1.9 christos httpd_addheader(req, "Pragma: no-cache", NULL); 832 1.9 christos httpd_addheader(req, "Cache-Control: no-cache", NULL); 833 1.1 christos } 834 1.1 christos 835 1.9 christos httpd_addheader(req, "Server: libisc", NULL); 836 1.1 christos 837 1.5 christos if (is_compressed) { 838 1.9 christos httpd_addheader(req, "Content-Encoding", "deflate"); 839 1.9 christos httpd_addheaderuint(req, "Content-Length", 840 1.9 christos isc_buffer_usedlength(req->compbuffer)); 841 1.1 christos } else { 842 1.9 christos httpd_addheaderuint(req, "Content-Length", 843 1.9 christos isc_buffer_usedlength(&req->bodybuffer)); 844 1.1 christos } 845 1.1 christos 846 1.9 christos httpd_endheaders(req); /* done */ 847 1.1 christos 848 1.1 christos /* 849 1.3 christos * Append either the compressed or the non-compressed response body to 850 1.3 christos * the response headers and store the result in httpd->sendbuffer. 851 1.1 christos */ 852 1.9 christos if (is_compressed) { 853 1.9 christos isc_buffer_putmem(req->sendbuffer, 854 1.9 christos isc_buffer_base(req->compbuffer), 855 1.9 christos isc_buffer_usedlength(req->compbuffer)); 856 1.9 christos isc_buffer_free(&req->compbuffer); 857 1.9 christos } else { 858 1.9 christos isc_buffer_putmem(req->sendbuffer, 859 1.9 christos isc_buffer_base(&req->bodybuffer), 860 1.9 christos isc_buffer_usedlength(&req->bodybuffer)); 861 1.9 christos } 862 1.9 christos 863 1.9 christos /* Free the bodybuffer */ 864 1.9 christos if (req->freecb != NULL && isc_buffer_length(&req->bodybuffer) > 0) { 865 1.9 christos req->freecb(&req->bodybuffer, req->freecb_arg); 866 1.9 christos } 867 1.9 christos 868 1.9 christos /* Consume the request from the recv buffer. */ 869 1.9 christos INSIST(httpd->consume != 0); 870 1.9 christos INSIST(httpd->consume <= httpd->recvlen); 871 1.9 christos if (httpd->consume < httpd->recvlen) { 872 1.9 christos memmove(httpd->recvbuf, httpd->recvbuf + httpd->consume, 873 1.9 christos httpd->recvlen - httpd->consume); 874 1.9 christos } 875 1.9 christos httpd->recvlen -= httpd->consume; 876 1.9 christos httpd->consume = 0; 877 1.13 christos 878 1.13 christos return ISC_R_SUCCESS; 879 1.10 christos } 880 1.10 christos 881 1.10 christos static void 882 1.13 christos prepare_response_done(void *arg, isc_result_t result) { 883 1.10 christos isc_region_t r; 884 1.10 christos isc_httpd_sendreq_t *req = arg; 885 1.10 christos isc_httpd_t *httpd = req->httpd; 886 1.9 christos 887 1.9 christos /* 888 1.10 christos * Determine total response size. 889 1.9 christos */ 890 1.10 christos isc_buffer_usedregion(req->sendbuffer, &r); 891 1.9 christos 892 1.13 christos if (result != ISC_R_SUCCESS) { 893 1.13 christos httpd_senddone(httpd->handle, result, req); 894 1.13 christos return; 895 1.13 christos } 896 1.13 christos 897 1.10 christos isc_nm_send(httpd->handle, &r, httpd_senddone, req); 898 1.9 christos } 899 1.9 christos 900 1.9 christos static void 901 1.9 christos httpd_request(isc_nmhandle_t *handle, isc_result_t eresult, 902 1.9 christos isc_region_t *region, void *arg) { 903 1.10 christos isc_httpd_t *httpd = arg; 904 1.10 christos isc_httpdmgr_t *mgr = httpd->mgr; 905 1.10 christos size_t last_len = 0; 906 1.9 christos isc_result_t result; 907 1.9 christos 908 1.9 christos REQUIRE(VALID_HTTPD(httpd)); 909 1.9 christos 910 1.9 christos REQUIRE(httpd->handle == handle); 911 1.9 christos 912 1.9 christos if (eresult != ISC_R_SUCCESS) { 913 1.9 christos goto close_readhandle; 914 1.9 christos } 915 1.9 christos 916 1.10 christos REQUIRE((mgr->flags & ISC_HTTPDMGR_SHUTTINGDOWN) == 0); 917 1.9 christos 918 1.10 christos isc_nm_read_stop(handle); 919 1.9 christos 920 1.9 christos /* 921 1.9 christos * If we are being called from httpd_senddone(), the last HTTP request 922 1.9 christos * was processed successfully, reset the last_len to 0, even if there's 923 1.9 christos * data in the httpd->recvbuf. 924 1.9 christos */ 925 1.9 christos last_len = (region == NULL) ? 0 : httpd->recvlen; 926 1.9 christos 927 1.9 christos /* Store the received data into the recvbuf */ 928 1.9 christos if (region != NULL) { 929 1.9 christos if (httpd->recvlen + region->length > sizeof(httpd->recvbuf)) { 930 1.9 christos goto close_readhandle; 931 1.9 christos } 932 1.9 christos 933 1.9 christos memmove(httpd->recvbuf + httpd->recvlen, region->base, 934 1.9 christos region->length); 935 1.9 christos httpd->recvlen += region->length; 936 1.9 christos } 937 1.9 christos 938 1.9 christos result = process_request(httpd, last_len); 939 1.9 christos 940 1.9 christos if (result == ISC_R_NOMORE) { 941 1.9 christos if (httpd->recvlen > HTTP_MAX_REQUEST_LEN) { 942 1.9 christos goto close_readhandle; 943 1.9 christos } 944 1.9 christos 945 1.10 christos /* Wait for more data, the handle is still attached */ 946 1.10 christos isc_nm_read(handle, httpd_request, arg); 947 1.9 christos return; 948 1.9 christos } 949 1.9 christos 950 1.9 christos /* XXXFANF it would be more polite to reply 400 bad request */ 951 1.9 christos if (result != ISC_R_SUCCESS) { 952 1.9 christos goto close_readhandle; 953 1.9 christos } 954 1.9 christos 955 1.10 christos isc_httpd_sendreq_t *req = isc__httpd_sendreq_new(httpd); 956 1.10 christos isc_nmhandle_ref(handle); 957 1.13 christos isc_work_enqueue(isc_loop(), ISC_WORKLANE_SLOW, prepare_response, 958 1.13 christos prepare_response_done, req); 959 1.9 christos return; 960 1.9 christos 961 1.9 christos close_readhandle: 962 1.10 christos isc_nmhandle_close(httpd->handle); 963 1.10 christos isc_nmhandle_detach(&httpd->handle); 964 1.10 christos 965 1.10 christos isc_httpd_detach(&httpd); 966 1.1 christos } 967 1.1 christos 968 1.1 christos void 969 1.1 christos isc_httpdmgr_shutdown(isc_httpdmgr_t **httpdmgrp) { 970 1.10 christos isc_httpdmgr_t *httpdmgr = NULL; 971 1.4 christos 972 1.4 christos REQUIRE(httpdmgrp != NULL); 973 1.9 christos REQUIRE(VALID_HTTPDMGR(*httpdmgrp)); 974 1.9 christos 975 1.1 christos httpdmgr = *httpdmgrp; 976 1.1 christos *httpdmgrp = NULL; 977 1.9 christos 978 1.9 christos isc_nm_stoplistening(httpdmgr->sock); 979 1.1 christos 980 1.1 christos LOCK(&httpdmgr->lock); 981 1.10 christos 982 1.10 christos isc_httpd_t *httpd = NULL, *next = NULL; 983 1.11 christos ISC_LIST_FOREACH_SAFE(httpdmgr->running, httpd, link, next) { 984 1.10 christos if (httpd->handle != NULL) { 985 1.10 christos httpd_request(httpd->handle, ISC_R_SUCCESS, NULL, 986 1.10 christos httpd); 987 1.10 christos } 988 1.10 christos } 989 1.10 christos 990 1.9 christos httpdmgr->flags |= ISC_HTTPDMGR_SHUTTINGDOWN; 991 1.1 christos 992 1.1 christos UNLOCK(&httpdmgr->lock); 993 1.1 christos 994 1.9 christos isc_nmsocket_close(&httpdmgr->sock); 995 1.1 christos 996 1.10 christos isc_httpdmgr_detach(&httpdmgr); 997 1.1 christos } 998 1.1 christos 999 1.9 christos static void 1000 1.9 christos httpd_response(isc_httpd_t *httpd, isc_httpd_sendreq_t *req) { 1001 1.1 christos isc_result_t result; 1002 1.1 christos 1003 1.9 christos result = isc_buffer_printf(req->sendbuffer, "HTTP/1.%u %03u %s\r\n", 1004 1.9 christos httpd->minor_version, req->retcode, 1005 1.9 christos req->retmsg); 1006 1.4 christos 1007 1.9 christos RUNTIME_CHECK(result == ISC_R_SUCCESS); 1008 1.1 christos } 1009 1.1 christos 1010 1.9 christos static void 1011 1.9 christos httpd_addheader(isc_httpd_sendreq_t *req, const char *name, const char *val) { 1012 1.1 christos isc_result_t result; 1013 1.4 christos 1014 1.4 christos if (val != NULL) { 1015 1.9 christos result = isc_buffer_printf(req->sendbuffer, "%s: %s\r\n", name, 1016 1.9 christos val); 1017 1.9 christos } else { 1018 1.9 christos result = isc_buffer_printf(req->sendbuffer, "%s\r\n", name); 1019 1.4 christos } 1020 1.1 christos 1021 1.9 christos RUNTIME_CHECK(result == ISC_R_SUCCESS); 1022 1.1 christos } 1023 1.1 christos 1024 1.9 christos static void 1025 1.9 christos httpd_endheaders(isc_httpd_sendreq_t *req) { 1026 1.1 christos isc_result_t result; 1027 1.1 christos 1028 1.9 christos result = isc_buffer_printf(req->sendbuffer, "\r\n"); 1029 1.4 christos 1030 1.9 christos RUNTIME_CHECK(result == ISC_R_SUCCESS); 1031 1.1 christos } 1032 1.1 christos 1033 1.9 christos static void 1034 1.9 christos httpd_addheaderuint(isc_httpd_sendreq_t *req, const char *name, int val) { 1035 1.1 christos isc_result_t result; 1036 1.4 christos 1037 1.9 christos result = isc_buffer_printf(req->sendbuffer, "%s: %d\r\n", name, val); 1038 1.1 christos 1039 1.9 christos RUNTIME_CHECK(result == ISC_R_SUCCESS); 1040 1.1 christos } 1041 1.1 christos 1042 1.1 christos static void 1043 1.9 christos httpd_senddone(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) { 1044 1.9 christos isc_httpd_sendreq_t *req = (isc_httpd_sendreq_t *)arg; 1045 1.9 christos isc_httpd_t *httpd = req->httpd; 1046 1.1 christos 1047 1.4 christos REQUIRE(VALID_HTTPD(httpd)); 1048 1.4 christos 1049 1.10 christos if ((httpd->mgr->flags & ISC_HTTPDMGR_SHUTTINGDOWN) != 0) { 1050 1.9 christos goto detach; 1051 1.1 christos } 1052 1.1 christos 1053 1.10 christos if (eresult == ISC_R_SUCCESS && (httpd->flags & CONNECTION_CLOSE) != 0) 1054 1.9 christos { 1055 1.10 christos eresult = ISC_R_EOF; 1056 1.1 christos } 1057 1.1 christos 1058 1.10 christos /* 1059 1.10 christos * Calling httpd_request() with region NULL restarts reading. 1060 1.10 christos */ 1061 1.10 christos httpd_request(handle, eresult, NULL, httpd); 1062 1.10 christos 1063 1.9 christos detach: 1064 1.9 christos isc_nmhandle_detach(&handle); 1065 1.9 christos isc__httpd_sendreq_free(req); 1066 1.10 christos isc_httpd_detach(&httpd); 1067 1.1 christos } 1068 1.1 christos 1069 1.1 christos isc_result_t 1070 1.9 christos isc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url, bool isstatic, 1071 1.4 christos isc_httpdaction_t *func, void *arg) { 1072 1.1 christos isc_httpdurl_t *item; 1073 1.1 christos 1074 1.4 christos REQUIRE(VALID_HTTPDMGR(httpdmgr)); 1075 1.4 christos 1076 1.1 christos if (url == NULL) { 1077 1.1 christos httpdmgr->render_404 = func; 1078 1.10 christos return ISC_R_SUCCESS; 1079 1.1 christos } 1080 1.1 christos 1081 1.1 christos item = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpdurl_t)); 1082 1.1 christos 1083 1.1 christos item->url = isc_mem_strdup(httpdmgr->mctx, url); 1084 1.1 christos 1085 1.1 christos item->action = func; 1086 1.1 christos item->action_arg = arg; 1087 1.1 christos item->isstatic = isstatic; 1088 1.10 christos item->loadtime = isc_time_now(); 1089 1.1 christos 1090 1.1 christos ISC_LINK_INIT(item, link); 1091 1.4 christos 1092 1.4 christos LOCK(&httpdmgr->lock); 1093 1.1 christos ISC_LIST_APPEND(httpdmgr->urls, item, link); 1094 1.4 christos UNLOCK(&httpdmgr->lock); 1095 1.1 christos 1096 1.10 christos return ISC_R_SUCCESS; 1097 1.1 christos } 1098 1.1 christos 1099 1.1 christos void 1100 1.4 christos isc_httpd_setfinishhook(void (*fn)(void)) { 1101 1.4 christos #if ENABLE_AFL 1102 1.1 christos finishhook = fn; 1103 1.4 christos #else /* ENABLE_AFL */ 1104 1.4 christos UNUSED(fn); 1105 1.4 christos #endif /* ENABLE_AFL */ 1106 1.1 christos } 1107 1.9 christos 1108 1.9 christos bool 1109 1.9 christos isc_httpdurl_isstatic(const isc_httpdurl_t *url) { 1110 1.10 christos return url->isstatic; 1111 1.9 christos } 1112 1.9 christos 1113 1.9 christos const isc_time_t * 1114 1.9 christos isc_httpdurl_loadtime(const isc_httpdurl_t *url) { 1115 1.10 christos return &url->loadtime; 1116 1.9 christos } 1117 1.9 christos 1118 1.9 christos const isc_time_t * 1119 1.9 christos isc_httpd_if_modified_since(const isc_httpd_t *httpd) { 1120 1.10 christos return (const isc_time_t *)&httpd->if_modified_since; 1121 1.9 christos } 1122