From 6af4337171e0895dbdd1b8c827cdcf5dc38a073f Mon Sep 17 00:00:00 2001 From: reyk Date: Fri, 25 Jul 2014 23:23:39 +0000 Subject: [PATCH] It is recommended to use a URL in the Location header of 3xx responses. To accomplish this, add some semantics to retrieve the server host name of a connection: either IP, IP:PORT (if not 80) or [IP6]:PORT, or Host value (if valid). --- usr.sbin/httpd/http.h | 10 ++++-- usr.sbin/httpd/httpd.h | 5 ++- usr.sbin/httpd/server.c | 25 ++++++++++---- usr.sbin/httpd/server_file.c | 5 +-- usr.sbin/httpd/server_http.c | 64 +++++++++++++++++++++++++++++++----- 5 files changed, 89 insertions(+), 20 deletions(-) diff --git a/usr.sbin/httpd/http.h b/usr.sbin/httpd/http.h index af2627f6d2e..994e98b1a84 100644 --- a/usr.sbin/httpd/http.h +++ b/usr.sbin/httpd/http.h @@ -1,4 +1,4 @@ -/* $OpenBSD: http.h,v 1.3 2014/07/13 15:11:23 reyk Exp $ */ +/* $OpenBSD: http.h,v 1.4 2014/07/25 23:23:39 reyk Exp $ */ /* * Copyright (c) 2012 - 2014 Reyk Floeter @@ -19,6 +19,9 @@ #ifndef _HTTP_H #define _HTTP_H +#define HTTP_PORT 80 +#define HTTPS_PORT 443 + enum httpmethod { HTTP_METHOD_NONE = 0, @@ -148,13 +151,14 @@ struct http_descriptor { #define query_key http_matchquery.kv_key #define query_val http_matchquery.kv_value - char *http_version; + char http_host[MAXHOSTNAMELEN]; enum httpmethod http_method; int http_chunked; + char *http_version; /* A tree of headers and attached lists for repeated headers. */ - struct kvtree http_headers; struct kv *http_lastheader; + struct kvtree http_headers; }; #endif /* _HTTP_H */ diff --git a/usr.sbin/httpd/httpd.h b/usr.sbin/httpd/httpd.h index f2e3dcec5ef..f88a46f2ca6 100644 --- a/usr.sbin/httpd/httpd.h +++ b/usr.sbin/httpd/httpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: httpd.h,v 1.13 2014/07/25 21:29:58 reyk Exp $ */ +/* $OpenBSD: httpd.h,v 1.14 2014/07/25 23:23:39 reyk Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter @@ -256,6 +256,7 @@ struct client { void *clt_srv; void *clt_srv_conf; u_int32_t clt_srv_id; + struct sockaddr_storage clt_srv_ss; int clt_s; in_port_t clt_port; @@ -412,6 +413,8 @@ int server_response_http(struct client *, u_int, struct media_type *, void server_reset_http(struct client *); void server_close_http(struct client *); int server_response(struct httpd *, struct client *); +const char * + server_http_host(struct sockaddr_storage *, char *, size_t); /* server_file.c */ int server_file(struct httpd *, struct client *); diff --git a/usr.sbin/httpd/server.c b/usr.sbin/httpd/server.c index 21ccff20faa..18fe06ca8a6 100644 --- a/usr.sbin/httpd/server.c +++ b/usr.sbin/httpd/server.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server.c,v 1.11 2014/07/25 16:23:19 reyk Exp $ */ +/* $OpenBSD: server.c,v 1.12 2014/07/25 23:23:39 reyk Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter @@ -503,6 +503,19 @@ server_accept(int fd, short event, void *arg) clt->clt_srv_id = srv->srv_conf.id; clt->clt_pid = getpid(); clt->clt_inflight = 1; + + /* get local address */ + slen = sizeof(clt->clt_srv_ss); + if (getsockname(s, (struct sockaddr *)&clt->clt_srv_ss, + &slen) == -1) { + server_close(clt, "listen address lookup failed"); + return; + } + + /* get client address */ + memcpy(&clt->clt_ss, &ss, sizeof(clt->clt_ss)); + + /* get ports */ switch (ss.ss_family) { case AF_INET: clt->clt_port = ((struct sockaddr_in *)&ss)->sin_port; @@ -511,7 +524,6 @@ server_accept(int fd, short event, void *arg) clt->clt_port = ((struct sockaddr_in6 *)&ss)->sin6_port; break; } - memcpy(&clt->clt_ss, &ss, sizeof(clt->clt_ss)); getmonotime(&clt->clt_tv_start); memcpy(&clt->clt_tv_last, &clt->clt_tv_start, sizeof(clt->clt_tv_last)); @@ -571,7 +583,8 @@ server_inflight_dec(struct client *clt, const char *why) void server_close(struct client *clt, const char *msg) { - char ibuf[128], obuf[128], *ptr = NULL; + char ibuf[MAXHOSTNAMELEN], obuf[MAXHOSTNAMELEN]; + char *ptr = NULL; struct server *srv = clt->clt_srv; struct server_config *srv_conf = clt->clt_srv_conf; @@ -590,14 +603,14 @@ server_close(struct client *clt, const char *msg) memset(&ibuf, 0, sizeof(ibuf)); memset(&obuf, 0, sizeof(obuf)); (void)print_host(&clt->clt_ss, ibuf, sizeof(ibuf)); - (void)print_host(&srv_conf->ss, obuf, sizeof(obuf)); + (void)server_http_host(&clt->clt_srv_ss, obuf, sizeof(obuf)); if (EVBUFFER_LENGTH(clt->clt_log) && evbuffer_add_printf(clt->clt_log, "\r\n") != -1) ptr = evbuffer_readline(clt->clt_log); log_info("server %s, " - "client %d (%d active), %s -> %s:%d, " + "client %d (%d active), %s:%u -> %s, " "%s%s%s", srv_conf->name, clt->clt_id, server_clients, - ibuf, obuf, ntohs(clt->clt_port), msg, + ibuf, ntohs(clt->clt_port), obuf, msg, ptr == NULL ? "" : ",", ptr == NULL ? "" : ptr); if (ptr != NULL) free(ptr); diff --git a/usr.sbin/httpd/server_file.c b/usr.sbin/httpd/server_file.c index 81346ce3159..8031a8fc213 100644 --- a/usr.sbin/httpd/server_file.c +++ b/usr.sbin/httpd/server_file.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server_file.c,v 1.15 2014/07/25 21:29:58 reyk Exp $ */ +/* $OpenBSD: server_file.c,v 1.16 2014/07/25 23:23:39 reyk Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter @@ -72,7 +72,8 @@ server_file_access(struct http_descriptor *desc, char *path, size_t len, /* Redirect to path with trailing "/" */ if (path[strlen(path) - 1] != '/') { - if (asprintf(&newpath, "%s/", desc->http_path) == -1) + if (asprintf(&newpath, "http://%s%s/", + desc->http_host, desc->http_path) == -1) return (500); free(desc->http_path); desc->http_path = newpath; diff --git a/usr.sbin/httpd/server_http.c b/usr.sbin/httpd/server_http.c index 2751ac35527..a05f25cc7d7 100644 --- a/usr.sbin/httpd/server_http.c +++ b/usr.sbin/httpd/server_http.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server_http.c,v 1.17 2014/07/25 21:48:05 reyk Exp $ */ +/* $OpenBSD: server_http.c,v 1.18 2014/07/25 23:23:39 reyk Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter @@ -528,6 +528,38 @@ server_http_date(char *tmbuf, size_t len) strftime(tmbuf, len, "%a, %d %h %Y %T %Z", &tm); } +const char * +server_http_host(struct sockaddr_storage *ss, char *buf, size_t len) +{ + char hbuf[MAXHOSTNAMELEN]; + in_port_t port; + + if (print_host(ss, buf, len) == NULL) + return (NULL); + + port = ntohs(server_socket_getport(ss)); + if (port == HTTP_PORT) + return (buf); + + switch (ss->ss_family) { + case AF_INET: + if ((size_t)snprintf(hbuf, sizeof(hbuf), + "%s:%u", buf, port) >= sizeof(hbuf)) + return (NULL); + break; + case AF_INET6: + if ((size_t)snprintf(hbuf, sizeof(hbuf), + "[%s]:%u", buf, port) >= sizeof(hbuf)) + return (NULL); + break; + } + + if (strlcpy(buf, hbuf, len) >= len) + return (NULL); + + return (buf); +} + void server_abort_http(struct client *clt, u_int code, const char *msg) { @@ -636,8 +668,8 @@ server_response(struct httpd *httpd, struct client *clt) char path[MAXPATHLEN]; struct http_descriptor *desc = clt->clt_desc; struct server *srv = clt->clt_srv; - struct server_config *srv_conf; - struct kv *kv, key; + struct server_config *srv_conf = &srv->srv_conf; + struct kv *kv, key, *host; int ret; /* Canonicalize the request path */ @@ -648,10 +680,14 @@ server_response(struct httpd *httpd, struct client *clt) if ((desc->http_path = strdup(path)) == NULL) goto fail; + key.kv_key = "Host"; + if ((host = kv_find(&desc->http_headers, &key)) != NULL && + host->kv_value == NULL) + host = NULL; + if (strcmp(desc->http_version, "HTTP/1.1") == 0) { /* Host header is mandatory */ - key.kv_key = "Host"; - if ((kv = kv_find(&desc->http_headers, &key)) == NULL) + if (host == NULL) goto fail; /* Is the connection persistent? */ @@ -675,19 +711,31 @@ server_response(struct httpd *httpd, struct client *clt) * Do we have a Host header and matching configuration? * XXX the Host can also appear in the URL path. */ - key.kv_key = "Host"; - if ((kv = kv_find(&desc->http_headers, &key)) != NULL) { + if (host != NULL) { /* XXX maybe better to turn srv_hosts into a tree */ TAILQ_FOREACH(srv_conf, &srv->srv_hosts, entry) { - if (fnmatch(srv_conf->name, kv->kv_value, + if (fnmatch(srv_conf->name, host->kv_value, FNM_CASEFOLD) == 0) { /* Replace host configuration */ clt->clt_srv_conf = srv_conf; + srv_conf = NULL; break; } } } + if (srv_conf != NULL) { + /* Use the actual server IP address */ + if (server_http_host(&clt->clt_srv_ss, desc->http_host, + sizeof(desc->http_host)) == NULL) + goto fail; + } else { + /* Host header was valid and found */ + if (strlcpy(desc->http_host, host->kv_value, + sizeof(desc->http_host)) >= sizeof(desc->http_host)) + goto fail; + } + if ((ret = server_file(httpd, clt)) == -1) return (-1); -- 2.20.1