From 10f6fc5141a15e368dac813a38942cb66d5ddef4 Mon Sep 17 00:00:00 2001 From: Garrett D'Amore Date: Sun, 22 Dec 2024 12:18:33 -0800 Subject: HTTP handler: limit host names to 256 bytes (RFC 1035 specifies 253.) This also makes `nng_http_handler_set_host` never fail (API break). --- docs/man/nng_http_handler_set_host.3http.adoc | 15 +++------ docs/ref/migrate/nng1.md | 13 +++++++- include/nng/supplemental/http/http.h | 2 +- src/supplemental/http/http_api.h | 2 +- src/supplemental/http/http_public.c | 5 ++- src/supplemental/http/http_server.c | 46 ++++++++++----------------- src/supplemental/websocket/websocket.c | 5 +-- 7 files changed, 39 insertions(+), 49 deletions(-) diff --git a/docs/man/nng_http_handler_set_host.3http.adoc b/docs/man/nng_http_handler_set_host.3http.adoc index 0deae488..3f25172f 100644 --- a/docs/man/nng_http_handler_set_host.3http.adoc +++ b/docs/man/nng_http_handler_set_host.3http.adoc @@ -1,6 +1,6 @@ = nng_http_handler_set_host(3http) // -// Copyright 2018 Staysail Systems, Inc. +// Copyright 2024 Staysail Systems, Inc. // Copyright 2018 Capitar IT Group BV // // This document is supplied under the terms of the MIT License, a @@ -20,7 +20,7 @@ nng_http_handler_set_host - set host for HTTP handler #include #include -int nng_http_handler_set_host(nng_http_handler *handler, const char *host); +void nng_http_handler_set_host(nng_http_handler *handler, const char *host); ---- == DESCRIPTION @@ -41,15 +41,8 @@ ports, the port number can be elided. The matching test only considers the hostname or IP address, and ignores any trailing port number. -== RETURN VALUES - -This function returns 0 on success, and non-zero otherwise. - -== ERRORS - -[horizontal] -`NNG_ENOMEM`:: Insufficient free memory to perform the operation. -`NNG_ENOTSUP`:: No support for HTTP in the library. +NOTE: This should not be used with an IP address normally, as `Host:` header +is used with virtual hosts in HTTP/1.1, and not supported for HTTP/1.0. == SEE ALSO diff --git a/docs/ref/migrate/nng1.md b/docs/ref/migrate/nng1.md index 8a1af631..544f15b6 100644 --- a/docs/ref/migrate/nng1.md +++ b/docs/ref/migrate/nng1.md @@ -221,8 +221,19 @@ accessors functions are provided: ## HTTP API -- [`nng_http_req_set_method`] no longer returns a value. It never fails, but it may truncate an unreasonably long value. +A few limits on string lengths of certain values are now applied, which allows us to preallocate values +and eliminate certain unreasonable error paths. If values longer than these are supplied in certain APIs +they may be silently truncated to the limit: + +- Hostnames are limited per RFC 1035 to 253 characters (not including terminating "." or zero byte.) +- HTTP Method names are limited to 32 bytes (the longest IANA registered method is currently 18 bytes, used for WebDAV.) +- The fixed part of URI pathnames used with HTTP handlers is limited to 1024 bytes. + +The following API changes are present: + +- [`nng_http_req_set_method`] no longer returns a value, and cannot fail. - [`nng_http_res_set_status`] no longer returns a value, and cannot fail. +- [`nng_http_handler_set_host`] no longer returns a value and cannot fail. ## Security Descriptors (Windows Only) diff --git a/include/nng/supplemental/http/http.h b/include/nng/supplemental/http/http.h index fa485243..729e25ad 100644 --- a/include/nng/supplemental/http/http.h +++ b/include/nng/supplemental/http/http.h @@ -362,7 +362,7 @@ NNG_DECL int nng_http_handler_set_method(nng_http_handler *, const char *); // default, then the Host: header is not considered when matching the // handler.) Note that the Host: header must match *exactly* (except // that case is not considered.) -NNG_DECL int nng_http_handler_set_host(nng_http_handler *, const char *); +NNG_DECL void nng_http_handler_set_host(nng_http_handler *, const char *); // nng_http_handler_collect_body is used to indicate the server should // check for, and process, data sent by the client, which will be attached diff --git a/src/supplemental/http/http_api.h b/src/supplemental/http/http_api.h index 349f3a49..d759e27b 100644 --- a/src/supplemental/http/http_api.h +++ b/src/supplemental/http/http_api.h @@ -322,7 +322,7 @@ extern int nni_http_handler_set_tree_exclusive(nni_http_handler *); // on port number as we assume that clients MUST have gotten that part right // as we do not support virtual hosting on multiple separate ports; the // server only listens on a single port. -extern int nni_http_handler_set_host(nni_http_handler *, const char *); +extern void nni_http_handler_set_host(nni_http_handler *, const char *); // nni_http_handler_set_method limits the handler to only being called // for the given HTTP method. By default a handler is called for GET diff --git a/src/supplemental/http/http_public.c b/src/supplemental/http/http_public.c index e3093d45..a60743fd 100644 --- a/src/supplemental/http/http_public.c +++ b/src/supplemental/http/http_public.c @@ -609,15 +609,14 @@ nng_http_handler_collect_body(nng_http_handler *h, bool want, size_t len) #endif } -int +void nng_http_handler_set_host(nng_http_handler *h, const char *host) { #ifdef NNG_SUPP_HTTP - return (nni_http_handler_set_host(h, host)); + nni_http_handler_set_host(h, host); #else NNI_ARG_UNUSED(h); NNI_ARG_UNUSED(host); - return (NNG_ENOTSUP); #endif } diff --git a/src/supplemental/http/http_server.c b/src/supplemental/http/http_server.c index fa9ad2f4..e9c8bea3 100644 --- a/src/supplemental/http/http_server.c +++ b/src/supplemental/http/http_server.c @@ -26,7 +26,7 @@ struct nng_http_handler { nni_list_node node; char *uri; char method[32]; - char *host; + char host[256]; // RFC 1035 nng_sockaddr host_addr; bool host_ip; bool tree; @@ -122,12 +122,12 @@ nni_http_handler_init( h->cb = cb; h->data = NULL; h->dtor = NULL; - h->host = NULL; h->tree = false; h->tree_exclusive = false; h->maxbody = 1024 * 1024; // Up to 1MB of body h->getbody = true; - strcpy(h->method, "GET"); + (void) strcpy(h->method, "GET"); + (void) strcpy(h->host, ""); *hp = h; return (0); } @@ -143,7 +143,6 @@ nni_http_handler_fini(nni_http_handler *h) if (h->dtor != NULL) { h->dtor(h->data); } - nni_strfree(h->host); nni_strfree(h->uri); NNI_FREE_STRUCT(h); } @@ -203,19 +202,15 @@ nni_http_handler_set_tree_exclusive(nni_http_handler *h) return (0); } -int +void nni_http_handler_set_host(nni_http_handler *h, const char *host) { - char *dup; + NNI_ASSERT(!nni_atomic_get_bool(&h->busy)); - if (nni_atomic_get_bool(&h->busy) != 0) { - return (NNG_EBUSY); - } if ((host == NULL) || (strcmp(host, "*") == 0) || strcmp(host, "") == 0) { - nni_strfree(h->host); - h->host = NULL; - return (0); + (void) strcpy(h->host, ""); + return; } if (nni_parse_ip(host, &h->host_addr) == 0) { uint8_t wild[16] = { 0 }; @@ -224,28 +219,21 @@ nni_http_handler_set_host(nni_http_handler *h, const char *host) switch (h->host_addr.s_family) { case NNG_AF_INET: if (h->host_addr.s_in.sa_addr == 0) { - nni_strfree(h->host); - h->host = NULL; - return (0); + (void) strcpy(h->host, ""); + return; } break; case NNG_AF_INET6: if (memcmp(h->host_addr.s_in6.sa_addr, wild, 16) == 0) { - nni_strfree(h->host); - h->host = NULL; - return (0); + (void) strcpy(h->host, ""); + return; } break; } h->host_ip = true; } - if ((dup = nni_strdup(host)) == NULL) { - return (NNG_ENOMEM); - } - nni_strfree(h->host); - h->host = dup; - return (0); + (void) snprintf(h->host, sizeof(h->host), "%s", host); } int @@ -499,7 +487,7 @@ http_handler_host_match(nni_http_handler *h, const char *host) nng_sockaddr sa; size_t len; - if (h->host == NULL) { + if ((len = strlen(h->host)) == '\0') { return (true); } if (host == NULL) { @@ -529,8 +517,6 @@ http_handler_host_match(nni_http_handler *h, const char *host) } } - len = strlen(h->host); - if ((nni_strncasecmp(host, h->host, len) != 0)) { return (false); } @@ -1205,13 +1191,13 @@ nni_http_server_add_handler(nni_http_server *s, nni_http_handler *h) NNI_LIST_FOREACH (&s->handlers, h2) { size_t len2; - if ((h2->host != NULL) && (h->host != NULL) && + if ((h2->host[0] != 0) && (h->host[0] != 0) && (nni_strcasecmp(h2->host, h->host) != 0)) { // Hosts don't match, so we are safe. continue; } - if (((h2->host == NULL) && (h->host != NULL)) || - ((h->host == NULL) && (h2->host != NULL))) { + if (((h2->host[0] == 0) && (h->host[0] != 0)) || + ((h->host[0] == 0) && (h2->host[0] != 0))) { continue; // Host specified for just one. } if (((h->method[0] == 0) && (h2->method[0] != 0)) || diff --git a/src/supplemental/websocket/websocket.c b/src/supplemental/websocket/websocket.c index 3dfb3e8e..ba304149 100644 --- a/src/supplemental/websocket/websocket.c +++ b/src/supplemental/websocket/websocket.c @@ -2150,8 +2150,9 @@ nni_ws_listener_alloc(nng_stream_listener **wslp, const nng_url *url) return (rv); } - if (((rv = nni_http_handler_set_host(l->handler, host)) != 0) || - ((rv = nni_http_handler_set_data(l->handler, l, 0)) != 0) || + nni_http_handler_set_host(l->handler, host); + + if (((rv = nni_http_handler_set_data(l->handler, l, 0)) != 0) || ((rv = nni_http_server_init(&l->server, url)) != 0)) { ws_listener_free(l); return (rv); -- cgit v1.2.3-70-g09d2