aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGarrett D'Amore <garrett@damore.org>2024-11-17 18:23:17 -0800
committerGarrett D'Amore <garrett@damore.org>2024-11-17 22:05:20 -0800
commit85aff44e00e836eda618d4f1cf013bce38b3fd44 (patch)
tree94b2dca800d6d254baae17932a017e031c17ce67
parentef82d4792bf59b1fe8053d9bb5ac924b443d8a78 (diff)
downloadnng-85aff44e00e836eda618d4f1cf013bce38b3fd44.tar.gz
nng-85aff44e00e836eda618d4f1cf013bce38b3fd44.tar.bz2
nng-85aff44e00e836eda618d4f1cf013bce38b3fd44.zip
URL u_port should be a number not a string.
The idea here is to reduce the dynamic allocations used for URLs, and also the back and forth with parsing begin strings and port numbers. We always resolve to a port number, and this is easier for everyone. The real goal in the long term is to eliminate dynamic allocation of the URL fields altogether, but that requires a little more work. This is a step in the right direction.
-rw-r--r--docs/ref/api/url.md22
-rw-r--r--docs/ref/migrate/nng1.md7
-rw-r--r--include/nng/nng.h6
-rw-r--r--src/core/platform.h6
-rw-r--r--src/core/tcp.c24
-rw-r--r--src/core/url.c84
-rw-r--r--src/core/url.h16
-rw-r--r--src/core/url_test.c55
-rw-r--r--src/platform/posix/posix_resolv_gai.c73
-rw-r--r--src/platform/resolver_test.c57
-rw-r--r--src/platform/windows/win_resolv.c74
-rw-r--r--src/sp/transport/tcp/tcp.c2
-rw-r--r--src/sp/transport/tls/tls.c2
-rw-r--r--src/sp/transport/udp/udp.c3
-rw-r--r--src/supplemental/http/http_msg.c33
-rw-r--r--src/supplemental/http/http_server.c8
16 files changed, 253 insertions, 219 deletions
diff --git a/docs/ref/api/url.md b/docs/ref/api/url.md
index 6c104854..0928180e 100644
--- a/docs/ref/api/url.md
+++ b/docs/ref/api/url.md
@@ -12,16 +12,16 @@ that are not part of the IETF standards.
```c
typedef struct nng_url {
- char *u_rawurl;
- char *u_scheme;
- char *u_userinfo;
- char *u_host;
- char *u_hostname;
- char *u_port;
- char *u_path;
- char *u_query;
- char *u_fragment;
- char *u_requri;
+ char *u_rawurl;
+ const char *u_scheme;
+ char *u_userinfo;
+ char *u_host;
+ char *u_hostname;
+ uint16_t u_port;
+ char *u_path;
+ char *u_query;
+ char *u_fragment;
+ char *u_requri;
} nng_url;
```
@@ -37,7 +37,7 @@ The fields of an `nng_url` object are as follows:
- `u_userinfo`: This username and password if supplied in the URL string. Will be `NULL` when not present.
- `u_host`: The full host part of the URL, including the port if present (separated by a colon.)
- `u_hostname`: The name of the host, and may be the empty string in some cases.
-- `u_port`: The port. May be empty if irrelevant or not specified.
+- `u_port`: The port. May be zero if irrelevant or not specified.
- `u_path`: The path, typically used with HTTP or WebSockets. Will be empty string if not specified.
- `u_query`: The query info (typically following `?` in the URL.) Will be `NULL` if not present.
- `u_fragment`: This is used for specifying an anchor, the part after `#` in a URL. Will be `NULL` if not present.
diff --git a/docs/ref/migrate/nng1.md b/docs/ref/migrate/nng1.md
index d3440fe4..b7ddea33 100644
--- a/docs/ref/migrate/nng1.md
+++ b/docs/ref/migrate/nng1.md
@@ -160,4 +160,11 @@ A number of the [statistics][statistic] functions take, or return, `const nng_st
of plain `nng_stat *`. The ABI has not changed, but it may be necessary to declare
certain methods variables `const` to avoid warnings about misuse of `const`.
+## Url Structure Members
+
+The details of [`nng_url`] have changed as follows:
+
+- `u_port` is no longer a string, but a `uint16_t`
+- `u_scheme` is a const char \*
+
{{#include ../xref.md}}
diff --git a/include/nng/nng.h b/include/nng/nng.h
index a41deeb4..6c1eaafd 100644
--- a/include/nng/nng.h
+++ b/include/nng/nng.h
@@ -1088,9 +1088,9 @@ typedef struct nng_url {
char *u_userinfo; // will be NULL if not specified
char *u_host; // including colon and port
char *u_hostname; // name only, will be "" if not specified
- char *u_port; // port, will be "" if not specified
- char *u_path; // path, will be "" if not specified
- char *u_query; // without '?', will be NULL if not specified
+ uint16_t u_port; // port, may be zero for schemes that do not use
+ char *u_path; // path, will be "" if not specified
+ char *u_query; // without '?', will be NULL if not specified
char *u_fragment; // without '#', will be NULL if not specified
char *u_requri; // includes query and fragment, "" if not specified
} nng_url;
diff --git a/src/core/platform.h b/src/core/platform.h
index 62a321bf..783cc9fc 100644
--- a/src/core/platform.h
+++ b/src/core/platform.h
@@ -343,7 +343,7 @@ extern int nni_tcp_listener_get(
// Symbolic service names will be looked up assuming SOCK_STREAM, so
// they may not work with UDP.
extern void nni_resolv_ip(
- const char *, const char *, int, bool, nng_sockaddr *sa, nni_aio *);
+ const char *, uint16_t, int, bool, nng_sockaddr *sa, nni_aio *);
// nni_parse_ip parses an IP address, without a port.
extern int nni_parse_ip(const char *, nng_sockaddr *);
@@ -351,6 +351,10 @@ 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 *);
+// nni_get_port_by_name resolves a name (which may be an ASCII representation
+// of a number) to a port number (the value returned is in native byte order.)
+extern int nni_get_port_by_name(const char *, uint16_t *);
+
//
// IPC (UNIX Domain Sockets & Named Pipes) Support.
//
diff --git a/src/core/tcp.c b/src/core/tcp.c
index 7fb67228..5d324a13 100644
--- a/src/core/tcp.c
+++ b/src/core/tcp.c
@@ -1,5 +1,5 @@
//
-// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2024 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
// Copyright 2019 Devolutions <info@devolutions.net>
//
@@ -10,6 +10,7 @@
//
#include <stdint.h>
+#include <stdio.h>
#include <string.h>
#include <nng/nng.h>
@@ -20,8 +21,8 @@
typedef struct {
nng_stream_dialer ops;
- char *host;
- char *port;
+ char host[256];
+ uint16_t port;
int af; // address family
bool closed;
nng_sockaddr sa;
@@ -155,8 +156,6 @@ tcp_dialer_free(void *arg)
nni_tcp_dialer_fini(d->d);
}
nni_mtx_fini(&d->mtx);
- nni_strfree(d->host);
- nni_strfree(d->port);
NNI_FREE_STRUCT(d);
}
@@ -236,17 +235,13 @@ nni_tcp_dialer_alloc(nng_stream_dialer **dp, const nng_url *url)
{
tcp_dialer *d;
int rv;
- const char *p;
if ((rv = tcp_dialer_alloc(&d)) != 0) {
return (rv);
}
- if (((p = url->u_port) == NULL) || (strlen(p) == 0)) {
- p = nni_url_default_port(url->u_scheme);
- }
-
- if ((strlen(p) == 0) || (strlen(url->u_hostname) == 0)) {
+ if ((url->u_port == 0) || strlen(url->u_hostname) == 0 ||
+ strlen(url->u_hostname) >= sizeof(d->host)) {
// Dialer needs both a destination hostname and port.
tcp_dialer_free(d);
return (NNG_EADDRINVAL);
@@ -260,11 +255,8 @@ nni_tcp_dialer_alloc(nng_stream_dialer **dp, const nng_url *url)
d->af = NNG_AF_UNSPEC;
}
- if (((d->host = nng_strdup(url->u_hostname)) == NULL) ||
- ((d->port = nng_strdup(p)) == NULL)) {
- tcp_dialer_free(d);
- return (NNG_ENOMEM);
- }
+ snprintf(d->host, sizeof(d->host), "%s", url->u_hostname);
+ d->port = url->u_port;
*dp = (void *) d;
return (0);
diff --git a/src/core/url.c b/src/core/url.c
index c1318719..9bdcfd23 100644
--- a/src/core/url.c
+++ b/src/core/url.c
@@ -15,6 +15,7 @@
#include <stdio.h>
#include <string.h>
+#include "core/platform.h"
#include "url.h"
static uint8_t
@@ -234,22 +235,26 @@ url_canonify_uri(char **outp, const char *in)
static struct {
const char *scheme;
- const char *port;
+ uint16_t port;
} nni_url_default_ports[] = {
// This list is not exhaustive, but likely covers the main ones we
// care about. Feel free to add additional ones as use cases arise.
// Note also that we don't use "default" ports for SP protocols
// that have no "default" port, like tcp:// or tls+tcp://.
// clang-format off
- { "git", "9418" },
- { "gopher", "70" },
- { "http", "80" },
- { "https", "443" },
- { "ssh", "22" },
- { "telnet", "23" },
- { "ws", "80" },
- { "wss", "443" },
- { NULL, NULL },
+ { "git", 9418 },
+ { "gopher", 70 },
+ { "http", 80 },
+ { "https", 443 },
+ { "ssh", 22 },
+ { "telnet", 23 },
+ { "ws", 80 },
+ { "ws4", 80 },
+ { "ws6", 80 },
+ { "wss", 443 },
+ { "wss4", 443 },
+ { "wss6", 443 },
+ { NULL, 0 },
// clang-format on
};
@@ -274,6 +279,9 @@ static const char *nni_schemes[] = {
"wss",
"wss4",
"wss6",
+ "udp",
+ "udp4",
+ "udp6",
// we don't support these
"file",
"mailto",
@@ -284,10 +292,11 @@ static const char *nni_schemes[] = {
"telnet",
"irc",
"imap",
+ "imaps",
NULL,
};
-const char *
+uint16_t
nni_url_default_port(const char *scheme)
{
const char *s;
@@ -310,7 +319,7 @@ nni_url_default_port(const char *scheme)
break;
}
}
- return ("");
+ return (0);
}
// URLs usually follow the following format:
@@ -503,6 +512,11 @@ nni_url_parse(nni_url **urlp, const char *raw)
}
}
}
+ // hostname length check
+ if (len >= 256) {
+ rv = NNG_EADDRINVAL;
+ goto error;
+ }
if ((url->u_hostname = nni_alloc(len + 1)) == NULL) {
rv = NNG_ENOMEM;
goto error;
@@ -521,13 +535,12 @@ nni_url_parse(nni_url **urlp, const char *raw)
rv = NNG_EINVAL;
goto error;
}
- url->u_port = nni_strdup(s + 1);
+ rv = nni_get_port_by_name(s + 1, &url->u_port);
+ if (rv != 0) {
+ goto error;
+ }
} else {
- url->u_port = nni_strdup(nni_url_default_port(url->u_scheme));
- }
- if (url->u_port == NULL) {
- rv = NNG_ENOMEM;
- goto error;
+ url->u_port = nni_url_default_port(url->u_scheme);
}
*urlp = url;
@@ -546,7 +559,6 @@ nni_url_free(nni_url *url)
nni_strfree(url->u_userinfo);
nni_strfree(url->u_host);
nni_strfree(url->u_hostname);
- nni_strfree(url->u_port);
nni_strfree(url->u_path);
nni_strfree(url->u_query);
nni_strfree(url->u_fragment);
@@ -558,11 +570,11 @@ nni_url_free(nni_url *url)
int
nni_url_asprintf(char **str, const nni_url *url)
{
- const char *scheme = url->u_scheme;
- const char *port = url->u_port;
- const char *host = url->u_hostname;
- const char *hostob = "";
- const char *hostcb = "";
+ const char *scheme = url->u_scheme;
+ const char *host = url->u_hostname;
+ const char *hostob = "";
+ const char *hostcb = "";
+ bool do_port = true;
if ((strcmp(scheme, "ipc") == 0) || (strcmp(scheme, "inproc") == 0) ||
(strcmp(scheme, "unix") == 0) ||
@@ -570,11 +582,8 @@ nni_url_asprintf(char **str, const nni_url *url)
return (nni_asprintf(str, "%s://%s", scheme, url->u_path));
}
- if (port != NULL) {
- if ((strlen(port) == 0) ||
- (strcmp(nni_url_default_port(scheme), port) == 0)) {
- port = NULL;
- }
+ if (url->u_port == nni_url_default_port(scheme)) {
+ do_port = false;
}
if (strcmp(host, "*") == 0) {
host = "";
@@ -583,9 +592,14 @@ nni_url_asprintf(char **str, const nni_url *url)
hostob = "[";
hostcb = "]";
}
- return (nni_asprintf(str, "%s://%s%s%s%s%s%s", scheme, hostob, host,
- hostcb, port != NULL ? ":" : "", port != NULL ? port : "",
- url->u_requri != NULL ? url->u_requri : ""));
+ char portstr[8];
+ if (do_port) {
+ snprintf(portstr, sizeof(portstr), ":%u", url->u_port);
+ } else {
+ portstr[0] = 0;
+ }
+ return (nni_asprintf(str, "%s://%s%s%s%s%s", scheme, hostob, host,
+ hostcb, portstr, url->u_requri != NULL ? url->u_requri : ""));
}
// nni_url_asprintf_port is like nni_url_asprintf, but includes a port
@@ -594,12 +608,10 @@ nni_url_asprintf(char **str, const nni_url *url)
int
nni_url_asprintf_port(char **str, const nni_url *url, int port)
{
- char portstr[16];
nni_url myurl = *url;
if (port > 0) {
- (void) snprintf(portstr, sizeof(portstr), "%d", port);
- myurl.u_port = portstr;
+ myurl.u_port = (uint16_t) port;
}
return (nni_url_asprintf(str, &myurl));
}
@@ -618,7 +630,6 @@ nni_url_clone(nni_url **dstp, const nni_url *src)
URL_COPYSTR(dst->u_userinfo, src->u_userinfo) ||
URL_COPYSTR(dst->u_host, src->u_host) ||
URL_COPYSTR(dst->u_hostname, src->u_hostname) ||
- URL_COPYSTR(dst->u_port, src->u_port) ||
URL_COPYSTR(dst->u_requri, src->u_requri) ||
URL_COPYSTR(dst->u_path, src->u_path) ||
URL_COPYSTR(dst->u_query, src->u_query) ||
@@ -627,6 +638,7 @@ nni_url_clone(nni_url **dstp, const nni_url *src)
return (NNG_ENOMEM);
}
dst->u_scheme = src->u_scheme;
+ dst->u_port = src->u_port;
*dstp = dst;
return (0);
}
diff --git a/src/core/url.h b/src/core/url.h
index 1761f6fd..71a093d0 100644
--- a/src/core/url.h
+++ b/src/core/url.h
@@ -13,13 +13,13 @@
#include "core/defs.h"
-extern int nni_url_parse(nni_url **, const char *path);
-extern void nni_url_free(nni_url *);
-extern int nni_url_clone(nni_url **, const nni_url *);
-extern const char *nni_url_default_port(const char *);
-extern int nni_url_asprintf(char **, const nni_url *);
-extern int nni_url_asprintf_port(char **, const nni_url *, int);
-extern size_t nni_url_decode(uint8_t *, const char *, size_t);
-extern int nni_url_to_address(nng_sockaddr *, const nni_url *);
+extern int nni_url_parse(nni_url **, const char *path);
+extern void nni_url_free(nni_url *);
+extern int nni_url_clone(nni_url **, const nni_url *);
+extern uint16_t nni_url_default_port(const char *);
+extern int nni_url_asprintf(char **, const nni_url *);
+extern int nni_url_asprintf_port(char **, const nni_url *, int);
+extern size_t nni_url_decode(uint8_t *, const char *, size_t);
+extern int nni_url_to_address(nng_sockaddr *, const nni_url *);
#endif // CORE_URL_H
diff --git a/src/core/url_test.c b/src/core/url_test.c
index 232cabc8..b5114551 100644
--- a/src/core/url_test.c
+++ b/src/core/url_test.c
@@ -22,7 +22,7 @@ test_url_host(void)
NUTS_TRUE(strcmp(url->u_scheme, "http") == 0);
NUTS_TRUE(strcmp(url->u_host, "www.google.com") == 0);
NUTS_TRUE(strcmp(url->u_hostname, "www.google.com") == 0);
- NUTS_TRUE(strcmp(url->u_port, "80") == 0);
+ NUTS_TRUE(url->u_port == 80);
NUTS_TRUE(strcmp(url->u_path, "") == 0);
NUTS_TRUE(strcmp(url->u_requri, "") == 0);
NUTS_TRUE(url->u_query == NULL);
@@ -32,6 +32,20 @@ test_url_host(void)
}
void
+test_url_host_too_long(void)
+{
+ nng_url *url;
+ char buffer[512]; //
+
+ memset(buffer, 0, sizeof(buffer));
+ snprintf(buffer, sizeof(buffer), "http://");
+ for (size_t i = strlen(buffer); i < sizeof(buffer) - 1; i++) {
+ buffer[i] = 'a';
+ }
+ NUTS_FAIL(nng_url_parse(&url, buffer), NNG_EADDRINVAL);
+}
+
+void
test_url_host_port(void)
{
nng_url *url;
@@ -40,7 +54,7 @@ test_url_host_port(void)
NUTS_TRUE(strcmp(url->u_scheme, "http") == 0);
NUTS_TRUE(strcmp(url->u_host, "www.google.com:1234") == 0);
NUTS_TRUE(strcmp(url->u_hostname, "www.google.com") == 0);
- NUTS_TRUE(strcmp(url->u_port, "1234") == 0);
+ NUTS_TRUE(url->u_port == 1234);
NUTS_TRUE(strcmp(url->u_path, "") == 0);
NUTS_TRUE(strcmp(url->u_requri, "") == 0);
NUTS_TRUE(url->u_query == NULL);
@@ -59,7 +73,7 @@ test_url_host_port_path(void)
NUTS_TRUE(strcmp(url->u_scheme, "http") == 0);
NUTS_TRUE(strcmp(url->u_host, "www.google.com:1234") == 0);
NUTS_TRUE(strcmp(url->u_hostname, "www.google.com") == 0);
- NUTS_TRUE(strcmp(url->u_port, "1234") == 0);
+ NUTS_TRUE(url->u_port == 1234);
NUTS_TRUE(strcmp(url->u_path, "/somewhere") == 0);
NUTS_TRUE(strcmp(url->u_requri, "/somewhere") == 0);
NUTS_TRUE(url->u_userinfo == NULL);
@@ -79,7 +93,7 @@ test_url_user_info(void)
NUTS_MATCH(url->u_userinfo, "garrett");
NUTS_MATCH(url->u_host, "www.google.com:1234");
NUTS_MATCH(url->u_hostname, "www.google.com");
- NUTS_MATCH(url->u_port, "1234");
+ NUTS_TRUE(url->u_port == 1234);
NUTS_MATCH(url->u_path, "/somewhere");
NUTS_MATCH(url->u_requri, "/somewhere");
NUTS_NULL(url->u_query);
@@ -97,7 +111,7 @@ test_url_path_query_param(void)
NUTS_MATCH(url->u_scheme, "http");
NUTS_MATCH(url->u_host, "www.google.com");
NUTS_MATCH(url->u_hostname, "www.google.com");
- NUTS_MATCH(url->u_port, "80");
+ NUTS_TRUE(url->u_port == 80);
NUTS_MATCH(url->u_path, "/somewhere");
NUTS_MATCH(url->u_query, "result=yes");
NUTS_MATCH(url->u_requri, "/somewhere?result=yes");
@@ -117,7 +131,7 @@ test_url_query_param_anchor(void)
NUTS_MATCH(url->u_scheme, "http");
NUTS_MATCH(url->u_host, "www.google.com");
NUTS_MATCH(url->u_hostname, "www.google.com");
- NUTS_MATCH(url->u_port, "80");
+ NUTS_TRUE(url->u_port == 80);
NUTS_MATCH(url->u_path, "/somewhere");
NUTS_MATCH(url->u_query, "result=yes");
NUTS_MATCH(url->u_fragment, "chapter1");
@@ -136,7 +150,7 @@ test_url_path_anchor(void)
NUTS_MATCH(url->u_scheme, "http");
NUTS_MATCH(url->u_host, "www.google.com");
NUTS_MATCH(url->u_hostname, "www.google.com");
- NUTS_MATCH(url->u_port, "80");
+ NUTS_TRUE(url->u_port == 80);
NUTS_MATCH(url->u_path, "/somewhere");
NUTS_MATCH(url->u_fragment, "chapter2");
NUTS_MATCH(url->u_requri, "/somewhere#chapter2");
@@ -155,7 +169,7 @@ test_url_anchor(void)
NUTS_MATCH(url->u_host, "www.google.com");
NUTS_MATCH(url->u_hostname, "www.google.com");
NUTS_MATCH(url->u_path, "");
- NUTS_MATCH(url->u_port, "80");
+ NUTS_TRUE(url->u_port == 80);
NUTS_MATCH(url->u_fragment, "chapter3");
NUTS_MATCH(url->u_requri, "#chapter3");
NUTS_NULL(url->u_query);
@@ -173,7 +187,7 @@ test_url_query_param(void)
NUTS_MATCH(url->u_host, "www.google.com");
NUTS_MATCH(url->u_hostname, "www.google.com");
NUTS_MATCH(url->u_path, "");
- NUTS_MATCH(url->u_port, "80");
+ NUTS_TRUE(url->u_port == 80);
NUTS_MATCH(url->u_query, "color=red");
NUTS_MATCH(url->u_requri, "?color=red");
NUTS_ASSERT(url != NULL);
@@ -191,7 +205,7 @@ test_url_v6_host(void)
NUTS_MATCH(url->u_host, "[::1]");
NUTS_MATCH(url->u_hostname, "::1");
NUTS_MATCH(url->u_path, "");
- NUTS_MATCH(url->u_port, "80");
+ NUTS_TRUE(url->u_port == 80);
NUTS_NULL(url->u_query);
NUTS_NULL(url->u_fragment);
NUTS_NULL(url->u_userinfo);
@@ -208,7 +222,7 @@ test_url_v6_host_port(void)
NUTS_MATCH(url->u_host, "[::1]:29");
NUTS_MATCH(url->u_hostname, "::1");
NUTS_MATCH(url->u_path, "");
- NUTS_MATCH(url->u_port, "29");
+ NUTS_TRUE(url->u_port == 29);
NUTS_NULL(url->u_query);
NUTS_NULL(url->u_fragment);
NUTS_NULL(url->u_userinfo);
@@ -225,7 +239,7 @@ test_url_v6_host_port_path(void)
NUTS_MATCH(url->u_host, "[::1]:29");
NUTS_MATCH(url->u_hostname, "::1");
NUTS_MATCH(url->u_path, "/bottles");
- NUTS_MATCH(url->u_port, "29");
+ NUTS_TRUE(url->u_port == 29);
NUTS_NULL(url->u_query);
NUTS_NULL(url->u_fragment);
NUTS_NULL(url->u_userinfo);
@@ -242,7 +256,7 @@ test_url_tcp_port(void)
NUTS_MATCH(url->u_host, ":9876");
NUTS_MATCH(url->u_hostname, "");
NUTS_MATCH(url->u_path, "/");
- NUTS_MATCH(url->u_port, "9876");
+ NUTS_TRUE(url->u_port == 9876);
NUTS_NULL(url->u_query);
NUTS_NULL(url->u_fragment);
NUTS_NULL(url->u_userinfo);
@@ -260,7 +274,7 @@ test_url_bare_ws(void)
NUTS_MATCH(url->u_host, "");
NUTS_MATCH(url->u_hostname, "");
NUTS_MATCH(url->u_path, "");
- NUTS_MATCH(url->u_port, "80");
+ NUTS_TRUE(url->u_port == 80);
NUTS_NULL(url->u_query);
NUTS_NULL(url->u_fragment);
NUTS_NULL(url->u_userinfo);
@@ -277,7 +291,7 @@ test_url_ws_wildcard(void)
NUTS_MATCH(url->u_host, ":12345");
NUTS_MATCH(url->u_hostname, "");
NUTS_MATCH(url->u_path, "/foobar");
- NUTS_MATCH(url->u_port, "12345");
+ NUTS_TRUE(url->u_port == 12345);
NUTS_NULL(url->u_query);
NUTS_NULL(url->u_fragment);
NUTS_NULL(url->u_userinfo);
@@ -294,7 +308,7 @@ test_url_ssh(void)
NUTS_MATCH(url->u_host, "host.example.com");
NUTS_MATCH(url->u_hostname, "host.example.com");
NUTS_MATCH(url->u_path, "");
- NUTS_MATCH(url->u_port, "22");
+ NUTS_TRUE(url->u_port == 22);
NUTS_NULL(url->u_query);
NUTS_NULL(url->u_fragment);
NUTS_MATCH(url->u_userinfo, "user");
@@ -332,7 +346,7 @@ test_url_canonify(void)
NUTS_ASSERT(url != NULL);
NUTS_MATCH(url->u_scheme, "http");
NUTS_MATCH(url->u_hostname, "www.example.com");
- NUTS_MATCH(url->u_port, "80");
+ NUTS_TRUE(url->u_port == 80);
NUTS_MATCH(url->u_path, "/~garrett");
nng_url_free(url);
}
@@ -345,7 +359,7 @@ test_url_path_resolve(void)
nng_url_parse(&url, "http://www.x.com//abc/def/./x/..///./../y"));
NUTS_MATCH(url->u_scheme, "http");
NUTS_MATCH(url->u_hostname, "www.x.com");
- NUTS_MATCH(url->u_port, "80");
+ NUTS_TRUE(url->u_port == 80);
NUTS_MATCH(url->u_path, "/abc/y");
nng_url_free(url);
}
@@ -359,7 +373,7 @@ test_url_query_info_pass(void)
NUTS_ASSERT(url != NULL);
NUTS_MATCH(url->u_scheme, "http");
NUTS_MATCH(url->u_hostname, "www.x.com");
- NUTS_MATCH(url->u_port, "80");
+ NUTS_TRUE(url->u_port == 80);
NUTS_MATCH(url->u_path, "/");
NUTS_MATCH(url->u_query, "/abc/def/./x/.././../y");
nng_url_free(url);
@@ -383,7 +397,7 @@ test_url_good_utf8(void)
NUTS_ASSERT(url != NULL);
NUTS_MATCH(url->u_scheme, "http");
NUTS_MATCH(url->u_hostname, "www.x.com");
- NUTS_MATCH(url->u_port, "80");
+ NUTS_TRUE(url->u_port == 80);
NUTS_MATCH(url->u_path, "/\xc2\xa2_cents");
nng_url_free(url);
}
@@ -446,6 +460,7 @@ test_url_decode(void)
NUTS_TESTS = {
{ "url host", test_url_host },
+ { "url host too long", test_url_host_too_long },
{ "url host port", test_url_host_port },
{ "url host port path", test_url_host_port_path },
{ "url user info", test_url_user_info },
diff --git a/src/platform/posix/posix_resolv_gai.c b/src/platform/posix/posix_resolv_gai.c
index f85c27c1..7a0f8e1f 100644
--- a/src/platform/posix/posix_resolv_gai.c
+++ b/src/platform/posix/posix_resolv_gai.c
@@ -10,6 +10,7 @@
#include "core/init.h"
#include "core/nng_impl.h"
+#include "nng/nng.h"
#ifdef NNG_USE_POSIX_RESOLV_GAI
@@ -17,6 +18,7 @@
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
@@ -49,8 +51,8 @@ typedef struct resolv_item resolv_item;
struct resolv_item {
int family;
bool passive;
- char *host;
- char *serv;
+ char host[256];
+ char serv[8];
nni_aio *aio;
nng_sockaddr *sa;
};
@@ -58,8 +60,6 @@ struct resolv_item {
static void
resolv_free_item(resolv_item *item)
{
- nni_strfree(item->serv);
- nni_strfree(item->host);
NNI_FREE_STRUCT(item);
}
@@ -155,29 +155,12 @@ resolv_task(resolv_item *item)
}
hints.ai_family = item->family;
hints.ai_socktype = SOCK_STREAM;
-
- // Check to see if this is a numeric port number, and if it is
- // make sure that it's in the valid range (because Windows may
- // incorrectly simple do a conversion and mask off upper bits.
- if (item->serv != NULL) {
- long port;
- char *end;
- port = strtol(item->serv, &end, 10);
- if (*end == '\0') { // we fully converted it as a number...
- hints.ai_flags |= AI_NUMERICSERV;
-
- // Not a valid port number. Fail.
- if ((port < 0) || (port > 0xffff)) {
- rv = NNG_EADDRINVAL;
- goto done;
- }
- }
- }
+ hints.ai_flags |= AI_NUMERICSERV;
// We can pass any non-zero service number, but we have to pass
// *something*, in case we are using a NULL hostname.
- if ((rv = getaddrinfo(item->host, item->serv, &hints, &results)) !=
- 0) {
+ if ((rv = getaddrinfo(item->host[0] != 0 ? item->host : NULL,
+ item->serv, &hints, &results)) != 0) {
rv = posix_gai_errno(rv);
goto done;
}
@@ -237,7 +220,7 @@ done:
}
void
-nni_resolv_ip(const char *host, const char *serv, int af, bool passive,
+nni_resolv_ip(const char *host, uint16_t port, int af, bool passive,
nng_sockaddr *sa, nni_aio *aio)
{
resolv_item *item;
@@ -247,6 +230,10 @@ nni_resolv_ip(const char *host, const char *serv, int af, bool passive,
if (nni_aio_begin(aio) != 0) {
return;
}
+ if (host != NULL && strlen(host) >= sizeof(item->host)) {
+ nni_aio_finish_error(aio, NNG_EADDRINVAL);
+ return;
+ }
switch (af) {
case NNG_AF_INET:
fam = AF_INET;
@@ -275,19 +262,11 @@ nni_resolv_ip(const char *host, const char *serv, int af, bool passive,
return;
}
- if (serv == NULL || strcmp(serv, "") == 0) {
- item->serv = NULL;
- } else if ((item->serv = nni_strdup(serv)) == NULL) {
- nni_aio_finish_error(aio, NNG_ENOMEM);
- resolv_free_item(item);
- return;
- }
+ snprintf(item->serv, sizeof(item->serv), "%u", port);
if (host == NULL) {
- item->host = NULL;
- } else if ((item->host = nni_strdup(host)) == NULL) {
- nni_aio_finish_error(aio, NNG_ENOMEM);
- resolv_free_item(item);
- return;
+ item->host[0] = '\0';
+ } else {
+ snprintf(item->host, sizeof(item->host), "%s", host);
}
item->aio = aio;
@@ -478,6 +457,26 @@ nni_parse_ip_port(const char *addr, nni_sockaddr *sa)
}
int
+nni_get_port_by_name(const char *name, uint16_t *portp)
+{
+ struct servent *se;
+ long port;
+ char *end = NULL;
+
+ port = strtol(name, &end, 10);
+ if ((*end == '\0') && (port >= 0) && (port <= 0xffff)) {
+ *portp = (uint16_t) port;
+ return (0);
+ }
+
+ if ((se = getservbyname(name, "tcp")) != NULL) {
+ *portp = (uint16_t) ntohs(se->s_port);
+ return (0);
+ }
+ return (NNG_EADDRINVAL);
+}
+
+int
nni_posix_resolv_sysinit(nng_init_params *params)
{
resolv_fini = false;
diff --git a/src/platform/resolver_test.c b/src/platform/resolver_test.c
index 64554291..9c7db2c0 100644
--- a/src/platform/resolver_test.c
+++ b/src/platform/resolver_test.c
@@ -9,6 +9,7 @@
//
#include "core/nng_impl.h"
+#include "core/platform.h"
#include <nuts.h>
@@ -41,8 +42,8 @@ test_google_dns(void)
nng_sockaddr sa;
NUTS_PASS(nng_aio_alloc(&aio, NULL, NULL));
- nni_resolv_ip("google-public-dns-a.google.com", "80", NNG_AF_INET,
- true, &sa, aio);
+ nni_resolv_ip(
+ "google-public-dns-a.google.com", 80, NNG_AF_INET, true, &sa, aio);
nng_aio_wait(aio);
NUTS_PASS(nng_aio_result(aio));
NUTS_TRUE(sa.s_in.sa_family == NNG_AF_INET);
@@ -52,13 +53,29 @@ test_google_dns(void)
}
void
+test_hostname_too_long(void)
+{
+ nng_aio *aio;
+ nng_sockaddr sa;
+ char buffer[512];
+
+ memset(buffer, 'a', sizeof(buffer) - 1);
+ buffer[sizeof(buffer) - 1] = '\0';
+ NUTS_PASS(nng_aio_alloc(&aio, NULL, NULL));
+ nni_resolv_ip(buffer, 80, NNG_AF_INET, true, &sa, aio);
+ nng_aio_wait(aio);
+ NUTS_FAIL(nng_aio_result(aio), NNG_EADDRINVAL);
+ nng_aio_free(aio);
+}
+
+void
test_numeric_addr(void)
{
nng_aio *aio;
nng_sockaddr sa;
NUTS_PASS(nng_aio_alloc(&aio, NULL, NULL));
- nni_resolv_ip("8.8.4.4", "69", NNG_AF_INET, true, &sa, aio);
+ nni_resolv_ip("8.8.4.4", 69, NNG_AF_INET, true, &sa, aio);
nng_aio_wait(aio);
NUTS_PASS(nng_aio_result(aio));
NUTS_TRUE(sa.s_in.sa_family == NNG_AF_INET);
@@ -79,7 +96,7 @@ test_numeric_v6(void)
}
NUTS_MSG("IPV6 support present");
NUTS_PASS(nng_aio_alloc(&aio, NULL, NULL));
- nni_resolv_ip("::1", "80", NNG_AF_INET6, true, &sa, aio);
+ nni_resolv_ip("::1", 80, NNG_AF_INET6, true, &sa, aio);
nng_aio_wait(aio);
NUTS_PASS(nng_aio_result(aio));
NUTS_TRUE(sa.s_in6.sa_family == NNG_AF_INET6);
@@ -94,14 +111,21 @@ test_service_names(void)
{
nng_aio *aio;
nng_sockaddr sa;
+ uint16_t port;
NUTS_PASS(nng_aio_alloc(&aio, NULL, NULL));
- nni_resolv_ip("8.8.4.4", "http", NNG_AF_INET, true, &sa, aio);
+ nni_resolv_ip("8.8.4.4", 80, NNG_AF_INET, true, &sa, aio);
nng_aio_wait(aio);
NUTS_PASS(nng_aio_result(aio));
NUTS_TRUE(sa.s_in.sa_port == nuts_be16(80));
NUTS_TRUE(sa.s_in.sa_addr = nuts_be32(0x08080404));
nng_aio_free(aio);
+
+ NUTS_PASS(nni_get_port_by_name("http", &port));
+ NUTS_TRUE(port == 80);
+
+ NUTS_PASS(nni_get_port_by_name("25", &port));
+ NUTS_TRUE(port == 25);
}
void
@@ -111,7 +135,7 @@ test_localhost_v4(void)
nng_sockaddr sa;
NUTS_PASS(nng_aio_alloc(&aio, NULL, NULL));
- nni_resolv_ip("localhost", "80", NNG_AF_INET, true, &sa, aio);
+ nni_resolv_ip("localhost", 80, NNG_AF_INET, true, &sa, aio);
nng_aio_wait(aio);
NUTS_PASS(nng_aio_result(aio));
NUTS_TRUE(sa.s_in.sa_family == NNG_AF_INET);
@@ -127,7 +151,7 @@ test_localhost_unspecified(void)
nng_sockaddr sa;
NUTS_PASS(nng_aio_alloc(&aio, NULL, NULL));
- nni_resolv_ip("localhost", "80", NNG_AF_UNSPEC, true, &sa, aio);
+ nni_resolv_ip("localhost", 80, NNG_AF_UNSPEC, true, &sa, aio);
nng_aio_wait(aio);
NUTS_PASS(nng_aio_result(aio));
NUTS_TRUE(
@@ -154,7 +178,7 @@ test_null_passive(void)
nng_sockaddr sa;
NUTS_PASS(nng_aio_alloc(&aio, NULL, NULL));
- nni_resolv_ip(NULL, "80", NNG_AF_INET, true, &sa, aio);
+ nni_resolv_ip(NULL, 80, NNG_AF_INET, true, &sa, aio);
nng_aio_wait(aio);
NUTS_PASS(nng_aio_result(aio));
NUTS_TRUE(sa.s_in.sa_family == NNG_AF_INET);
@@ -170,7 +194,7 @@ test_null_not_passive(void)
nng_sockaddr sa;
NUTS_PASS(nng_aio_alloc(&aio, NULL, NULL));
- nni_resolv_ip(NULL, "80", NNG_AF_INET, false, &sa, aio);
+ nni_resolv_ip(NULL, 80, NNG_AF_INET, false, &sa, aio);
nng_aio_wait(aio);
// We can either get invalid address, or a loopback address.
// Most systems do the former, but Linux does the latter.
@@ -184,21 +208,9 @@ test_null_not_passive(void)
nng_aio_free(aio);
}
-void
-test_bad_port_number(void)
-{
- nng_aio *aio;
- nng_sockaddr sa;
-
- NUTS_PASS(nng_aio_alloc(&aio, NULL, NULL));
- nni_resolv_ip("1.1.1.1", "1000000", NNG_AF_INET, true, &sa, aio);
- nng_aio_wait(aio);
- NUTS_FAIL(nng_aio_result(aio), NNG_EADDRINVAL);
- nng_aio_free(aio);
-}
-
NUTS_TESTS = {
{ "resolve google dns", test_google_dns },
+ { "resolve hostname too long", test_hostname_too_long },
{ "resolve numeric addr", test_numeric_addr },
#ifdef NNG_ENABLE_IPV6
{ "resolve numeric v6", test_numeric_v6 },
@@ -208,6 +220,5 @@ NUTS_TESTS = {
{ "resolve localhost unspecified", test_localhost_unspecified },
{ "resolve null passive", test_null_passive },
{ "resolve null not passive", test_null_not_passive },
- { "resolve bad port number", test_bad_port_number },
{ NULL, NULL },
};
diff --git a/src/platform/windows/win_resolv.c b/src/platform/windows/win_resolv.c
index 1b1ae7b9..ece14655 100644
--- a/src/platform/windows/win_resolv.c
+++ b/src/platform/windows/win_resolv.c
@@ -33,8 +33,8 @@ typedef struct resolv_item resolv_item;
struct resolv_item {
int family;
bool passive;
- char *host;
- char *serv;
+ char host[256];
+ char serv[8];
nni_aio *aio;
nng_sockaddr *sa;
};
@@ -42,8 +42,6 @@ struct resolv_item {
static void
resolv_free_item(resolv_item *item)
{
- nni_strfree(item->serv);
- nni_strfree(item->host);
NNI_FREE_STRUCT(item);
}
@@ -118,27 +116,10 @@ resolv_task(resolv_item *item)
}
hints.ai_family = item->family;
hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags |= AI_NUMERICSERV;
- // Check to see if this is a numeric port number, and if it is
- // make sure that it's in the valid range (because Windows may
- // incorrectly simple do a conversion and mask off upper bits.
- if (item->serv != NULL) {
- long port;
- char *end;
- port = strtol(item->serv, &end, 10);
- if (*end == '\0') { // we fully converted it as a number...
- hints.ai_flags |= AI_NUMERICSERV;
-
- // Not a valid port number. Fail.
- if ((port < 0) || (port > 0xffff)) {
- rv = NNG_EADDRINVAL;
- goto done;
- }
- }
- }
-
- if ((rv = getaddrinfo(item->host, item->serv, &hints, &results)) !=
- 0) {
+ if ((rv = getaddrinfo(item->host[0] != 0 ? item->host : NULL,
+ item->serv, &hints, &results)) != 0) {
rv = resolv_errno(rv);
goto done;
}
@@ -199,7 +180,7 @@ done:
}
void
-nni_resolv_ip(const char *host, const char *serv, int family, bool passive,
+nni_resolv_ip(const char *host, uint16_t port, int family, bool passive,
nng_sockaddr *sa, nni_aio *aio)
{
resolv_item *item;
@@ -209,6 +190,11 @@ nni_resolv_ip(const char *host, const char *serv, int family, bool passive,
if (nni_aio_begin(aio) != 0) {
return;
}
+ if (host != NULL && strlen(host) >= sizeof(item->host)) {
+ nni_aio_finish_error(aio, NNG_EADDRINVAL);
+ return;
+ }
+
switch (family) {
case NNG_AF_INET:
fam = AF_INET;
@@ -234,20 +220,12 @@ nni_resolv_ip(const char *host, const char *serv, int family, bool passive,
nni_aio_finish_error(aio, NNG_ENOMEM);
return;
}
- if (host == NULL) {
- item->host = NULL;
- } else if ((item->host = nni_strdup(host)) == NULL) {
- nni_aio_finish_error(aio, NNG_ENOMEM);
- resolv_free_item(item);
- return;
- }
- if (serv == NULL) {
- item->serv = NULL;
- } else if ((item->serv = nni_strdup(serv)) == NULL) {
- nni_aio_finish_error(aio, NNG_ENOMEM);
- resolv_free_item(item);
- return;
+ snprintf(item->serv, sizeof(item->serv), "%u", port);
+ if (host == NULL) {
+ item->host[0] = '\0';
+ } else {
+ snprintf(item->host, sizeof(item->host), "%s", host);
}
item->sa = sa;
@@ -432,6 +410,26 @@ nni_parse_ip_port(const char *addr, nni_sockaddr *sa)
}
int
+nni_get_port_by_name(const char *name, uint16_t *portp)
+{
+ struct servent *se;
+ long port;
+ char *end = NULL;
+
+ port = strtol(name, &end, 10);
+ if ((*end == '\0') && (port >= 0) && (port <= 0xffff)) {
+ *portp = (uint16_t) port;
+ return (0);
+ }
+
+ if ((se = getservbyname(name, "tcp")) != NULL) {
+ *portp = (uint16_t) ntohs(se->s_port);
+ return (0);
+ }
+ return (NNG_EADDRINVAL);
+}
+
+int
nni_win_resolv_sysinit(nng_init_params *params)
{
nni_aio_list_init(&resolv_aios);
diff --git a/src/sp/transport/tcp/tcp.c b/src/sp/transport/tcp/tcp.c
index 54d31726..86d0ac07 100644
--- a/src/sp/transport/tcp/tcp.c
+++ b/src/sp/transport/tcp/tcp.c
@@ -874,7 +874,7 @@ tcptran_dialer_init(void **dp, nng_url *url, nni_dialer *ndialer)
}
if ((url->u_fragment != NULL) || (url->u_userinfo != NULL) ||
(url->u_query != NULL) || (strlen(url->u_hostname) == 0) ||
- (strlen(url->u_port) == 0)) {
+ (url->u_port == 0)) {
return (NNG_EADDRINVAL);
}
diff --git a/src/sp/transport/tls/tls.c b/src/sp/transport/tls/tls.c
index 7a1d5582..b1aea7b3 100644
--- a/src/sp/transport/tls/tls.c
+++ b/src/sp/transport/tls/tls.c
@@ -837,7 +837,7 @@ tlstran_ep_init_dialer(void **dp, nni_url *url, nni_dialer *ndialer)
}
if ((url->u_fragment != NULL) || (url->u_userinfo != NULL) ||
(url->u_query != NULL) || (strlen(url->u_hostname) == 0) ||
- (strlen(url->u_port) == 0)) {
+ (url->u_port == 0)) {
return (NNG_EADDRINVAL);
}
diff --git a/src/sp/transport/udp/udp.c b/src/sp/transport/udp/udp.c
index 25899070..29a516d8 100644
--- a/src/sp/transport/udp/udp.c
+++ b/src/sp/transport/udp/udp.c
@@ -1443,8 +1443,7 @@ udp_check_url(nng_url *url, bool listen)
return (NNG_EADDRINVAL);
}
if (!listen) {
- if ((strlen(url->u_hostname) == 0) ||
- (strlen(url->u_port) == 0) || (atoi(url->u_port) == 0)) {
+ if ((strlen(url->u_hostname) == 0) || (url->u_port == 0)) {
return (NNG_EADDRINVAL);
}
}
diff --git a/src/supplemental/http/http_msg.c b/src/supplemental/http/http_msg.c
index 9f5996ca..28c89ec7 100644
--- a/src/supplemental/http/http_msg.c
+++ b/src/supplemental/http/http_msg.c
@@ -22,13 +22,13 @@
// a comma. From experience, for example, Firefox uses a Connection:
// header with two values, "keepalive", and "upgrade".
typedef struct http_header {
- char * name;
- char * value;
+ char *name;
+ char *value;
nni_list_node node;
} http_header;
typedef struct nni_http_entity {
- char * data;
+ char *data;
size_t size; // allocated/expected size
size_t len; // current length
bool own; // if true, data is "ours", and should be freed
@@ -37,10 +37,10 @@ typedef struct nni_http_entity {
struct nng_http_req {
nni_list hdrs;
nni_http_entity data;
- char * meth;
- char * uri;
- char * vers;
- char * buf;
+ char *meth;
+ char *uri;
+ char *vers;
+ char *buf;
size_t bufsz;
bool parsed;
};
@@ -49,9 +49,9 @@ struct nng_http_res {
nni_list hdrs;
nni_http_entity data;
uint16_t code;
- char * rsn;
- char * vers;
- char * buf;
+ char *rsn;
+ char *vers;
+ char *buf;
size_t bufsz;
bool parsed;
bool iserr;
@@ -492,7 +492,7 @@ http_asprintf(char **bufp, size_t *szp, nni_list *hdrs, const char *fmt, ...)
va_list ap;
size_t len;
size_t n;
- char * buf;
+ char *buf;
va_start(ap, fmt);
len = vsnprintf(NULL, 0, fmt, ap);
@@ -550,7 +550,7 @@ http_res_prepare(nni_http_res *res)
char *
nni_http_req_headers(nni_http_req *req)
{
- char * s;
+ char *s;
size_t len;
len = http_sprintf_headers(NULL, 0, &req->hdrs) + 1;
@@ -563,7 +563,7 @@ nni_http_req_headers(nni_http_req *req)
char *
nni_http_res_headers(nni_http_res *res)
{
- char * s;
+ char *s;
size_t len;
len = http_sprintf_headers(NULL, 0, &res->hdrs) + 1;
@@ -625,8 +625,7 @@ nni_http_req_alloc(nni_http_req **reqp, const nni_url *url)
// Add a Host: header since we know that from the URL. Also,
// only include the :port portion if it isn't the default port.
- if (strcmp(nni_url_default_port(url->u_scheme), url->u_port) ==
- 0) {
+ if (nni_url_default_port(url->u_scheme) == url->u_port) {
host = url->u_hostname;
} else {
host = url->u_host;
@@ -735,7 +734,7 @@ http_scan_line(void *vbuf, size_t n, size_t *lenp)
{
size_t len;
char lc;
- char * buf = vbuf;
+ char *buf = vbuf;
lc = 0;
for (len = 0; len < n; len++) {
@@ -1042,7 +1041,7 @@ nni_http_alloc_html_error(char **html, uint16_t code, const char *details)
int
nni_http_res_alloc_error(nni_http_res **resp, uint16_t err)
{
- char * html = NULL;
+ char *html = NULL;
nni_http_res *res = NULL;
int rv;
diff --git a/src/supplemental/http/http_server.c b/src/supplemental/http/http_server.c
index ac57cf5b..03b3cf68 100644
--- a/src/supplemental/http/http_server.c
+++ b/src/supplemental/http/http_server.c
@@ -1,5 +1,5 @@
//
-// Copyright 2024 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2023 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
// Copyright 2018 QXSoftware <lh563566994@126.com>
// Copyright 2019 Devolutions <info@devolutions.net>
@@ -962,9 +962,7 @@ http_server_init(nni_http_server **serverp, const nni_url *url)
return (rv);
}
- // NB: We only support number port numbers, and the URL framework
- // expands empty port numbers to 80 or 443 as appropriate.
- s->port = atoi(url->u_port);
+ s->port = url->u_port;
if ((s->hostname = nni_strdup(url->u_hostname)) == NULL) {
http_server_fini(s);
@@ -989,7 +987,7 @@ nni_http_server_init(nni_http_server **serverp, const nni_url *url)
nni_mtx_lock(&http_servers_lk);
NNI_LIST_FOREACH (&http_servers, s) {
- if ((!s->closed) && (atoi(url->u_port) == s->port) &&
+ if ((!s->closed) && (url->u_port == s->port) &&
(strcmp(url->u_hostname, s->hostname) == 0)) {
*serverp = s;
s->refcnt++;