aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/platform.h6
-rw-r--r--src/platform/posix/posix_resolv_gai.c111
-rw-r--r--src/platform/windows/win_resolv.c104
-rw-r--r--src/supplemental/http/http_server.c110
-rw-r--r--src/supplemental/tcp/tcp.c2
5 files changed, 305 insertions, 28 deletions
diff --git a/src/core/platform.h b/src/core/platform.h
index 70420061..c6f4ef30 100644
--- a/src/core/platform.h
+++ b/src/core/platform.h
@@ -339,6 +339,12 @@ extern void nni_tcp_resolv(const char *, const char *, int, int, nni_aio *);
// service names using UDP.
extern void nni_udp_resolv(const char *, const char *, int, int, nni_aio *);
+// nni_parse_ip parses an IP address, without a port.
+extern int nni_parse_ip(const char *, nng_sockaddr *);
+
+// nni_parse_ip_port parses an IP address with an optional port appended.
+extern int nni_parse_ip_port(const char *, nng_sockaddr *);
+
//
// IPC (UNIX Domain Sockets & Named Pipes) Support.
//
diff --git a/src/platform/posix/posix_resolv_gai.c b/src/platform/posix/posix_resolv_gai.c
index 5cec0570..88313045 100644
--- a/src/platform/posix/posix_resolv_gai.c
+++ b/src/platform/posix/posix_resolv_gai.c
@@ -1,5 +1,5 @@
//
-// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
//
// This software is supplied under the terms of the MIT License, a
@@ -257,8 +257,9 @@ resolv_ip(const char *host, const char *serv, int passive, int family,
// NB: must remain valid until this is completed. So we have to
// keep our own copy.
- if (host != NULL && nni_strnlen(host, sizeof(item->name_buf)) >=
- sizeof(item->name_buf)) {
+ if (host != NULL &&
+ nni_strnlen(host, sizeof(item->name_buf)) >=
+ sizeof(item->name_buf)) {
NNI_FREE_STRUCT(item);
nni_aio_finish_error(aio, NNG_EADDRINVAL);
return;
@@ -354,6 +355,110 @@ resolv_worker(void *unused)
}
int
+parse_ip(const char *addr, nng_sockaddr *sa, bool want_port)
+{
+ struct addrinfo hints;
+ struct addrinfo *results;
+ int rv;
+ bool v6 = false;
+ bool wrapped = false;
+ char * port;
+ char * host;
+ char * buf;
+ size_t buf_len;
+
+ if (addr == NULL) {
+ addr = "";
+ }
+
+ buf_len = strlen(addr) + 1;
+ if ((buf = nni_alloc(buf_len)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+ memcpy(buf, addr, buf_len);
+ host = buf;
+ if (*host == '[') {
+ v6 = true;
+ wrapped = true;
+ host++;
+ } else {
+ char *s;
+ for (s = host; *s != '\0'; s++) {
+ if (*s == '.') {
+ break;
+ }
+ if (*s == ':') {
+ v6 = true;
+ break;
+ }
+ }
+ }
+ for (port = host; *port != '\0'; port++) {
+ if (wrapped) {
+ if (*port == ']') {
+ *port++ = '\0';
+ wrapped = false;
+ break;
+ }
+ } else if (!v6) {
+ if (*port == ':') {
+ break;
+ }
+ }
+ }
+
+ if (wrapped) {
+ // Never got the closing bracket.
+ rv = NNG_EADDRINVAL;
+ goto done;
+ }
+
+ if ((!want_port) && (*port != '\0')) {
+ rv = NNG_EADDRINVAL;
+ goto done;
+ } else if (*port == ':') {
+ *port++ = '\0';
+ }
+
+ if (*port == '\0') {
+ port = "0";
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_NUMERICSERV | AI_NUMERICHOST | AI_PASSIVE;
+ if (v6) {
+ hints.ai_family = AF_INET6;
+ }
+#ifdef AI_ADDRCONFIG
+ hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+
+ rv = getaddrinfo(host, port, &hints, &results);
+ if ((rv != 0) || (results == NULL)) {
+ rv = nni_plat_errno(rv);
+ goto done;
+ }
+ nni_posix_sockaddr2nn(sa, (void *) results->ai_addr);
+ freeaddrinfo(results);
+
+done:
+ nni_free(buf, buf_len);
+ return (rv);
+}
+
+int
+nni_parse_ip(const char *addr, nni_sockaddr *sa)
+{
+ return (parse_ip(addr, sa, false));
+}
+
+int
+nni_parse_ip_port(const char *addr, nni_sockaddr *sa)
+{
+ return (parse_ip(addr, sa, true));
+}
+
+int
nni_posix_resolv_sysinit(void)
{
nni_mtx_init(&resolv_mtx);
diff --git a/src/platform/windows/win_resolv.c b/src/platform/windows/win_resolv.c
index ff356700..d80b5ddf 100644
--- a/src/platform/windows/win_resolv.c
+++ b/src/platform/windows/win_resolv.c
@@ -1,5 +1,5 @@
//
-// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
//
// This software is supplied under the terms of the MIT License, a
@@ -317,6 +317,108 @@ resolv_worker(void *notused)
}
int
+parse_ip(const char *addr, nng_sockaddr *sa, bool want_port)
+{
+ struct addrinfo hints;
+ struct addrinfo *results;
+ int rv;
+ bool v6 = false;
+ bool wrapped = false;
+ char * port;
+ char * host;
+ char * buf;
+ size_t buf_len;
+
+ if (addr == NULL) {
+ addr = "";
+ }
+
+ buf_len = strlen(addr) + 1;
+ if ((buf = nni_alloc(buf_len)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+ memcpy(buf, addr, buf_len);
+ host = buf;
+ if (*host == '[') {
+ v6 = true;
+ wrapped = true;
+ host++;
+ } else {
+ char *s;
+ for (s = host; *s != '\0'; s++) {
+ if (*s == '.') {
+ break;
+ }
+ if (*s == ':') {
+ v6 = true;
+ break;
+ }
+ }
+ }
+ for (port = host; *port != '\0'; port++) {
+ if (wrapped) {
+ if (*port == ']') {
+ *port++ = '\0';
+ wrapped = false;
+ break;
+ }
+ } else if (!v6) {
+ if (*port == ':') {
+ break;
+ }
+ }
+ }
+
+ if (wrapped) {
+ // Never got the closing bracket.
+ rv = NNG_EADDRINVAL;
+ goto done;
+ }
+
+ if ((!want_port) && (*port != '\0')) {
+ rv = NNG_EADDRINVAL;
+ goto done;
+ } else if (*port == ':') {
+ *port++ = '\0';
+ }
+
+ if (*port == '\0') {
+ port = "0";
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags =
+ AI_ADDRCONFIG | AI_NUMERICSERV | AI_NUMERICHOST | AI_PASSIVE;
+ if (v6) {
+ hints.ai_family = AF_INET6;
+ }
+
+ rv = getaddrinfo(host, port, &hints, &results);
+ if ((rv != 0) || (results == NULL)) {
+ rv = nni_win_error(rv);
+ goto done;
+ }
+ nni_win_sockaddr2nn(sa, (void *) results->ai_addr);
+ freeaddrinfo(results);
+
+done:
+ nni_free(buf, buf_len);
+ return (rv);
+}
+
+int
+nni_parse_ip(const char *addr, nni_sockaddr *sa)
+{
+ return (parse_ip(addr, sa, false));
+}
+
+int
+nni_parse_ip_port(const char *addr, nni_sockaddr *sa)
+{
+ return (parse_ip(addr, sa, true));
+}
+
+int
nni_win_resolv_sysinit(void)
{
nni_mtx_init(&resolv_mtx);
diff --git a/src/supplemental/http/http_server.c b/src/supplemental/http/http_server.c
index 1b90c172..e711c2a2 100644
--- a/src/supplemental/http/http_server.c
+++ b/src/supplemental/http/http_server.c
@@ -36,6 +36,8 @@ struct nng_http_handler {
char * uri;
char * method;
char * host;
+ nng_sockaddr host_addr;
+ bool host_ip;
bool tree;
bool tree_exclusive;
nni_atomic_u64 ref;
@@ -203,11 +205,35 @@ nni_http_handler_set_host(nni_http_handler *h, const char *host)
if (nni_atomic_get_bool(&h->busy) != 0) {
return (NNG_EBUSY);
}
- if (host == NULL) {
+ if ((host == NULL) || (strcmp(host, "*") == 0) ||
+ strcmp(host, "") == 0) {
nni_strfree(h->host);
h->host = NULL;
return (0);
}
+ if (nni_parse_ip(host, &h->host_addr) == 0) {
+ uint8_t wild[16] = { 0 };
+
+ // Check for wild card addresses.
+ 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);
+ }
+ 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);
+ }
+ break;
+ }
+ h->host_ip = true;
+ }
if ((dup = nni_strdup(host)) == NULL) {
return (NNG_ENOMEM);
}
@@ -472,6 +498,64 @@ nni_http_hijack(nni_http_conn *conn)
return (0);
}
+static bool
+http_handler_host_match(nni_http_handler *h, const char *host)
+{
+ nng_sockaddr sa;
+ size_t len;
+
+ if (h->host == NULL) {
+ return (true);
+ }
+ if (host == NULL) {
+ // Virtual hosts not possible under HTTP/1.0
+ return (false);
+ }
+ if (h->host_ip) {
+ if (nni_parse_ip_port(host, &sa) != 0) {
+ return (false);
+ }
+ switch (h->host_addr.s_family) {
+ case NNG_AF_INET:
+ if ((sa.s_in.sa_family != NNG_AF_INET) ||
+ (sa.s_in.sa_addr != h->host_addr.s_in.sa_addr)) {
+ return (false);
+ }
+ return (true);
+ case NNG_AF_INET6:
+ if (sa.s_in6.sa_family != NNG_AF_INET6) {
+ return (false);
+ }
+ if (memcmp(sa.s_in6.sa_addr,
+ h->host_addr.s_in6.sa_addr, 16) != 0) {
+ return (false);
+ }
+ return (true);
+ }
+ }
+
+ len = strlen(h->host);
+
+ if ((nni_strncasecmp(host, h->host, len) != 0)) {
+ return (false);
+ }
+
+ // At least the first part matches. If the ending
+ // part is a lone "." (legal in DNS), or a port
+ // number, we match it. (We do not validate the
+ // port number.) Note that there may be false matches
+ // with IPv6 addresses, but addresses shouldn't be
+ // used with virtual hosts anyway. With both addresses
+ // and ports, a false match would be unlikely since
+ // they'd still have to *connect* using that info.
+ if ((host[len] != '\0') && (host[len] != ':') &&
+ ((host[len] != '.') || (host[len + 1] != '\0'))) {
+ return (false);
+ }
+
+ return (true);
+}
+
static void
http_sconn_rxdone(void *arg)
{
@@ -556,29 +640,9 @@ http_sconn_rxdone(void *arg)
nni_mtx_lock(&s->mtx);
NNI_LIST_FOREACH (&s->handlers, h) {
size_t len;
- if (h->host != NULL) {
- if (host == NULL) {
- // HTTP/1.0 cannot access virtual hosts.
- continue;
- }
- len = strlen(h->host);
- if ((nni_strncasecmp(host, h->host, len) != 0)) {
- continue;
- }
-
- // At least the first part matches. If the ending
- // part is a lone "." (legal in DNS), or a port
- // number, we match it. (We do not validate the
- // port number.) Note that there may be false matches
- // with IPv6 addresses, but addresses shouldn't be
- // used with virtual hosts anyway. With both addresses
- // and ports, a false match would be unlikely since
- // they'd still have to *connect* using that info.
- if ((host[len] != '\0') && (host[len] != ':') &&
- ((host[len] != '.') || (host[len + 1] != '\0'))) {
- continue;
- }
+ if (!http_handler_host_match(h, host)) {
+ continue;
}
len = strlen(h->uri);
diff --git a/src/supplemental/tcp/tcp.c b/src/supplemental/tcp/tcp.c
index dd6f28ff..2fb7f56d 100644
--- a/src/supplemental/tcp/tcp.c
+++ b/src/supplemental/tcp/tcp.c
@@ -439,7 +439,7 @@ nni_tcp_listener_alloc(nng_stream_listener **lp, const nng_url *url)
h = url->u_hostname;
// Wildcard special case, which means bind to INADDR_ANY.
- if ((h != NULL) && ((strcmp(h, "*") == 0) || (strlen(h) == 0))) {
+ if ((h != NULL) && ((strcmp(h, "*") == 0) || (strcmp(h, "") == 0))) {
h = NULL;
}
nni_tcp_resolv(h, url->u_port, af, 1, aio);