diff options
| author | Garrett D'Amore <garrett@damore.org> | 2020-10-31 18:47:07 -0700 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2020-10-31 23:10:12 -0700 |
| commit | 452ecf5ae83adc9ae77518746f4f81171c42248c (patch) | |
| tree | d81730eef3c19775abf0715831dc18e3f9885d21 | |
| parent | 587bc765ee69acfabf3bc8b88a70806c07b61f87 (diff) | |
| download | nng-452ecf5ae83adc9ae77518746f4f81171c42248c.tar.gz nng-452ecf5ae83adc9ae77518746f4f81171c42248c.tar.bz2 nng-452ecf5ae83adc9ae77518746f4f81171c42248c.zip | |
fixes #1311 reduce wasted use for nni_aio
fixes #1317 IPv6 listener get port is incorrect
fixes #1319 Want symbolic service names
This is phase 1 of reducing the memory foot-print of aios, and
also of pipes. This removes the largest consumer the socket
address information, from the aio, which was only used by a few
consumers.
| -rw-r--r-- | src/core/aio.c | 12 | ||||
| -rw-r--r-- | src/core/aio.h | 17 | ||||
| -rw-r--r-- | src/core/platform.h | 17 | ||||
| -rw-r--r-- | src/platform/posix/posix_resolv_gai.c | 154 | ||||
| -rw-r--r-- | src/platform/posix/posix_tcpdial.c | 6 | ||||
| -rw-r--r-- | src/platform/windows/win_resolv.c | 166 | ||||
| -rw-r--r-- | src/platform/windows/win_tcpdial.c | 7 | ||||
| -rw-r--r-- | src/supplemental/tcp/tcp.c | 72 | ||||
| -rw-r--r-- | src/transport/tcp/tcp.c | 5 | ||||
| -rw-r--r-- | src/transport/tls/tls.c | 8 | ||||
| -rw-r--r-- | tests/CMakeLists.txt | 5 | ||||
| -rw-r--r-- | tests/resolv.c | 345 |
12 files changed, 359 insertions, 455 deletions
diff --git a/src/core/aio.c b/src/core/aio.c index 97bb9153..6a390677 100644 --- a/src/core/aio.c +++ b/src/core/aio.c @@ -699,15 +699,3 @@ nni_aio_sys_init(void) nni_thr_run(thr); return (0); } - -void -nni_aio_set_sockaddr(nni_aio *aio, const nng_sockaddr *sa) -{ - memcpy(&aio->a_sockaddr, sa, sizeof(*sa)); -} - -void -nni_aio_get_sockaddr(nni_aio *aio, nng_sockaddr *sa) -{ - memcpy(sa, &aio->a_sockaddr, sizeof(*sa)); -}
\ No newline at end of file diff --git a/src/core/aio.h b/src/core/aio.h index c2776bc0..2f699245 100644 --- a/src/core/aio.h +++ b/src/core/aio.h @@ -155,12 +155,9 @@ extern void nni_aio_get_iov(nni_aio *, unsigned *, nni_iov **); extern void nni_aio_normalize_timeout(nni_aio *, nng_duration); extern void nni_aio_bump_count(nni_aio *, size_t); -extern void nni_aio_set_sockaddr(nni_aio *aio, const nng_sockaddr *); -extern void nni_aio_get_sockaddr(nni_aio *aio, nng_sockaddr *); - // nni_aio_schedule indicates that the AIO has begun, and is scheduled for -// asychronous completion. This also starts the expiration timer. Note that -// prior to this, the aio is uncancellable. If the operation has a zero +// asynchronous completion. This also starts the expiration timer. Note that +// prior to this, the aio cannot be canceled. If the operation has a zero // timeout (NNG_FLAG_NONBLOCK) then NNG_ETIMEDOUT is returned. If the // operation has already been canceled, or should not be run, then an error // is returned. (In that case the caller should probably either return an @@ -198,7 +195,7 @@ struct nng_aio { // User scratch data. Consumers may store values here, which // must be preserved by providers and the framework. - void *a_user_data[4]; + void *a_user_data[2]; // Operation inputs & outputs. Up to 4 inputs and 4 outputs may be // specified. The semantics of these will vary, and depend on the @@ -210,13 +207,7 @@ struct nng_aio { nni_aio_cancelfn a_cancel_fn; void * a_cancel_arg; nni_list_node a_prov_node; // Linkage on provider list. - void * a_prov_extra[4]; // Extra data used by provider - - // Socket address. This turns out to be very useful, as we wind up - // needing socket addresses for numerous connection related routines. - // It would be cleaner to not have this and avoid burning the space, - // but having this hear dramatically simplifies lots of code. - nng_sockaddr a_sockaddr; + void * a_prov_extra[2]; // Extra data used by provider // Expire node. nni_list_node a_expire_node; diff --git a/src/core/platform.h b/src/core/platform.h index 6eff2f7d..704e338d 100644 --- a/src/core/platform.h +++ b/src/core/platform.h @@ -287,7 +287,7 @@ extern void nni_tcp_dialer_close(nni_tcp_dialer *); // nni_tcp_dial attempts to create an outgoing connection, // asynchronously, to the address in the aio. On success, the first (and only) // output will be an nni_tcp_conn * associated with the remote server. -extern void nni_tcp_dial(nni_tcp_dialer *, nni_aio *); +extern void nni_tcp_dial(nni_tcp_dialer *, const nng_sockaddr *, nni_aio *); // nni_tcp_dialer_getopt gets an option from the dialer. extern int nni_tcp_dialer_setopt( @@ -331,17 +331,16 @@ extern int nni_tcp_listener_setopt( extern int nni_tcp_listener_getopt( nni_tcp_listener *, const char *, void *, size_t *, nni_type); -// nni_tcp_resolv resolves a TCP name asynchronously. The family -// should be one of NNG_AF_INET, NNG_AF_INET6, or NNG_AF_UNSPEC. The -// first two constrain the name to those families, while the third will +// nni_resolv_ip resolves a DNS host and service name asynchronously. +// The family should be one of NNG_AF_INET, NNG_AF_INET6, or NNG_AF_UNSPEC. +// The first two constrain the name to those families, while the third will // return names of either family. The passive flag indicates that the // name will be used for bind(), otherwise the name will be used with // connect(). The host part may be NULL only if passive is true. -extern void nni_tcp_resolv(const char *, const char *, int, int, nni_aio *); - -// nni_udp_resolv is just like nni_tcp_resolv, but looks up -// service names using UDP. -extern void nni_udp_resolv(const char *, const char *, int, int, nni_aio *); +// 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 *); // nni_parse_ip parses an IP address, without a port. extern int nni_parse_ip(const char *, nng_sockaddr *); diff --git a/src/platform/posix/posix_resolv_gai.c b/src/platform/posix/posix_resolv_gai.c index c6732a12..888cc9b7 100644 --- a/src/platform/posix/posix_resolv_gai.c +++ b/src/platform/posix/posix_resolv_gai.c @@ -16,6 +16,7 @@ #include <errno.h> #include <netdb.h> #include <netinet/in.h> +#include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/un.h> @@ -40,18 +41,23 @@ static nni_thr resolv_thrs[NNG_RESOLV_CONCURRENCY]; typedef struct resolv_item resolv_item; struct resolv_item { - int family; - int passive; - char name_buf[256]; - char * name; - int proto; - int socktype; - uint16_t port; - nni_aio * aio; - nng_sockaddr sa; + int family; + bool passive; + char * host; + char * serv; + nni_aio * aio; + nng_sockaddr *sa; }; static void +resolv_free_item(resolv_item *item) +{ + nni_strfree(item->serv); + nni_strfree(item->host); + NNI_FREE_STRUCT(item); +} + +static void resolv_cancel(nni_aio *aio, void *arg, int rv) { resolv_item *item = arg; @@ -68,13 +74,14 @@ resolv_cancel(nni_aio *aio, void *arg, int rv) // so we can just discard everything. nni_aio_list_remove(aio); nni_mtx_unlock(&resolv_mtx); - NNI_FREE_STRUCT(item); + resolv_free_item(item); } else { // This case indicates the resolver is still processing our // node. We can discard our interest in the result, but we // can't interrupt the resolver itself. (Too bad, name // resolution is utterly synchronous for now.) item->aio = NULL; + item->sa = NULL; nni_mtx_unlock(&resolv_mtx); } nni_aio_finish_error(aio, rv); @@ -135,20 +142,36 @@ resolv_task(resolv_item *item) // host part are split. memset(&hints, 0, sizeof(hints)); #ifdef AI_ADDRCONFIG - hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; -#else - hints.ai_flags = AI_NUMERICSERV; + hints.ai_flags = AI_ADDRCONFIG; #endif if (item->passive) { hints.ai_flags |= AI_PASSIVE; } - hints.ai_protocol = item->proto; hints.ai_family = item->family; - hints.ai_socktype = item->socktype; + 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; + } + } + } // 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->name, "80", &hints, &results)) != 0) { + if ((rv = getaddrinfo(item->host, item->serv, &hints, &results)) != + 0) { rv = posix_gai_errno(rv); goto done; } @@ -164,29 +187,31 @@ resolv_task(resolv_item *item) } } - if (probe != NULL) { + nni_mtx_lock(&resolv_mtx); + if ((probe != NULL) && (item->aio != NULL)) { struct sockaddr_in * sin; struct sockaddr_in6 *sin6; - nng_sockaddr * sa = &item->sa; + nng_sockaddr * sa = item->sa; switch (probe->ai_addr->sa_family) { case AF_INET: rv = 0; sin = (void *) probe->ai_addr; sa->s_in.sa_family = NNG_AF_INET; - sa->s_in.sa_port = item->port; + sa->s_in.sa_port = sin->sin_port; sa->s_in.sa_addr = sin->sin_addr.s_addr; break; case AF_INET6: rv = 0; sin6 = (void *) probe->ai_addr; sa->s_in6.sa_family = NNG_AF_INET6; - sa->s_in6.sa_port = item->port; + sa->s_in6.sa_port = sin6->sin6_port; sa->s_in6.sa_scope = sin6->sin6_scope_id; memcpy(sa->s_in6.sa_addr, sin6->sin6_addr.s6_addr, 16); break; } } + nni_mtx_unlock(&resolv_mtx); done: @@ -197,19 +222,18 @@ done: return (rv); } -static void -resolv_ip(const char *host, const char *serv, int passive, int family, - int proto, int socktype, nni_aio *aio) +void +nni_resolv_ip(const char *host, const char *serv, int af, bool passive, + nng_sockaddr *sa, nni_aio *aio) { resolv_item *item; sa_family_t fam; int rv; - int port; if (nni_aio_begin(aio) != 0) { return; } - switch (family) { + switch (af) { case NNG_AF_INET: fam = AF_INET; break; @@ -224,62 +248,30 @@ resolv_ip(const char *host, const char *serv, int passive, int family, return; } - // We can't use the resolver to look up up ports with AI_NUMERICSERV, - // because some resolver(s) is(are?) broken. For example, the - // systemd resolver takes a port number of 1000000 and just rips off - // the high order bits and lets it through! - port = 0; - if (serv != NULL) { - while (isdigit(*serv)) { - port *= 10; - port += (*serv - '0'); - if (port > 0xffff) { - // Port number out of range. - nni_aio_finish_error(aio, NNG_EADDRINVAL); - return; - } - serv++; - } - if (*serv != '\0') { - nni_aio_finish_error(aio, NNG_EADDRINVAL); - return; - } - } - if ((port == 0) && (!passive)) { - nni_aio_finish_error(aio, NNG_EADDRINVAL); - return; - } - if ((item = NNI_ALLOC_STRUCT(item)) == NULL) { nni_aio_finish_error(aio, NNG_ENOMEM); return; } - // 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)) { - NNI_FREE_STRUCT(item); - nni_aio_finish_error(aio, NNG_EADDRINVAL); + 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; } - if (host == NULL) { - item->name = NULL; - } else { - nni_strlcpy(item->name_buf, host, sizeof(item->name_buf)); - item->name = item->name_buf; + item->host = NULL; + } else if ((item->host = nni_strdup(host)) == NULL) { + nni_aio_finish_error(aio, NNG_ENOMEM); + resolv_free_item(item); + return; } - memset(&item->sa, 0, sizeof(item->sa)); - item->proto = proto; - item->aio = aio; - item->family = fam; - item->passive = passive; - item->socktype = socktype; - item->port = htons((uint16_t) port); + item->aio = aio; + item->family = fam; + item->passive = passive; + item->sa = sa; nni_mtx_lock(&resolv_mtx); if (resolv_fini) { @@ -290,7 +282,7 @@ resolv_ip(const char *host, const char *serv, int passive, int family, } if (rv != 0) { nni_mtx_unlock(&resolv_mtx); - NNI_FREE_STRUCT(item); + resolv_free_item(item); nni_aio_finish_error(aio, rv); return; } @@ -300,20 +292,6 @@ resolv_ip(const char *host, const char *serv, int passive, int family, } void -nni_tcp_resolv( - const char *host, const char *serv, int family, int passive, nni_aio *aio) -{ - resolv_ip(host, serv, passive, family, IPPROTO_TCP, SOCK_STREAM, aio); -} - -void -nni_udp_resolv( - const char *host, const char *serv, int family, int passive, nni_aio *aio) -{ - resolv_ip(host, serv, passive, family, IPPROTO_UDP, SOCK_DGRAM, aio); -} - -void resolv_worker(void *unused) { @@ -348,11 +326,11 @@ resolv_worker(void *unused) nni_aio_set_prov_extra(aio, 0, NULL); item->aio = NULL; + item->sa = NULL; - nni_aio_set_sockaddr(aio, &item->sa); nni_aio_finish(aio, rv, 0); } - NNI_FREE_STRUCT(item); + resolv_free_item(item); } nni_mtx_unlock(&resolv_mtx); } diff --git a/src/platform/posix/posix_tcpdial.c b/src/platform/posix/posix_tcpdial.c index 3fabc28a..9b3a91f5 100644 --- a/src/platform/posix/posix_tcpdial.c +++ b/src/platform/posix/posix_tcpdial.c @@ -169,7 +169,7 @@ tcp_dialer_cb(nni_posix_pfd *pfd, unsigned ev, void *arg) // We don't give local address binding support. Outbound dialers always // get an ephemeral port. void -nni_tcp_dial(nni_tcp_dialer *d, nni_aio *aio) +nni_tcp_dial(nni_tcp_dialer *d, const nni_sockaddr *sa, nni_aio *aio) { nni_tcp_conn * c; nni_posix_pfd * pfd = NULL; @@ -179,14 +179,12 @@ nni_tcp_dial(nni_tcp_dialer *d, nni_aio *aio) int rv; int ka; int nd; - nng_sockaddr sa; if (nni_aio_begin(aio) != 0) { return; } - nni_aio_get_sockaddr(aio, &sa); - if (((sslen = nni_posix_nn2sockaddr(&ss, &sa)) == 0) || + if (((sslen = nni_posix_nn2sockaddr(&ss, sa)) == 0) || ((ss.ss_family != AF_INET) && (ss.ss_family != AF_INET6))) { nni_aio_finish_error(aio, NNG_EADDRINVAL); return; diff --git a/src/platform/windows/win_resolv.c b/src/platform/windows/win_resolv.c index d810ecac..8628719f 100644 --- a/src/platform/windows/win_resolv.c +++ b/src/platform/windows/win_resolv.c @@ -11,7 +11,7 @@ #include "core/nng_impl.h" #include <ctype.h> -#include <stdio.h> +#include <stdlib.h> #include <string.h> #ifdef NNG_PLATFORM_WINDOWS @@ -34,17 +34,23 @@ static nni_thr resolv_thrs[NNG_RESOLV_CONCURRENCY]; typedef struct resolv_item resolv_item; struct resolv_item { - int family; - int passive; - char * name; - int proto; - int socktype; - uint16_t port; - nni_aio * aio; - nng_sockaddr sa; + int family; + bool passive; + char * host; + char * serv; + nni_aio * aio; + nng_sockaddr *sa; }; static void +resolv_free_item(resolv_item *item) +{ + nni_strfree(item->serv); + nni_strfree(item->host); + NNI_FREE_STRUCT(item); +} + +static void resolv_cancel(nni_aio *aio, void *arg, int rv) { resolv_item *item = arg; @@ -60,12 +66,12 @@ resolv_cancel(nni_aio *aio, void *arg, int rv) // so we can just discard everything. nni_aio_list_remove(aio); nni_mtx_unlock(&resolv_mtx); - nni_strfree(item->name); - NNI_FREE_STRUCT(item); + resolv_free_item(item); } else { // Resolver still working, so just unlink our AIO to // discard our interest in the results. item->aio = NULL; + item->sa = NULL; nni_mtx_unlock(&resolv_mtx); } nni_aio_finish_error(aio, rv); @@ -108,18 +114,34 @@ resolv_task(resolv_item *item) results = NULL; - // We treat these all as IP addresses. The service and the - // host part are split. memset(&hints, 0, sizeof(hints)); - hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; + hints.ai_flags = AI_ADDRCONFIG; if (item->passive) { hints.ai_flags |= AI_PASSIVE; } - hints.ai_protocol = item->proto; hints.ai_family = item->family; - hints.ai_socktype = item->socktype; + 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; + } + } + } - if ((rv = getaddrinfo(item->name, "80", &hints, &results)) != 0) { + if ((rv = getaddrinfo(item->host, item->serv, &hints, &results)) != + 0) { rv = resolv_errno(rv); goto done; } @@ -135,31 +157,33 @@ resolv_task(resolv_item *item) } } + nni_mtx_lock(&resolv_mtx); if ((probe != NULL) && (item->aio != NULL)) { struct sockaddr_in * sin; struct sockaddr_in6 *sin6; - nni_sockaddr sa; + nni_sockaddr * sa; + + sa = item->sa; switch (probe->ai_addr->sa_family) { case AF_INET: - rv = 0; - sin = (void *) probe->ai_addr; - sa.s_in.sa_family = NNG_AF_INET; - sa.s_in.sa_port = item->port; - sa.s_in.sa_addr = sin->sin_addr.s_addr; - nni_aio_set_sockaddr(item->aio, &sa); + rv = 0; + sin = (void *) probe->ai_addr; + sa->s_in.sa_family = NNG_AF_INET; + sa->s_in.sa_port = sin->sin_port; + sa->s_in.sa_addr = sin->sin_addr.s_addr; break; case AF_INET6: - rv = 0; - sin6 = (void *) probe->ai_addr; - sa.s_in6.sa_family = NNG_AF_INET6; - sa.s_in6.sa_port = item->port; - sa.s_in6.sa_scope = sin6->sin6_scope_id; - memcpy(sa.s_in6.sa_addr, sin6->sin6_addr.s6_addr, 16); - nni_aio_set_sockaddr(item->aio, &sa); + rv = 0; + sin6 = (void *) probe->ai_addr; + sa->s_in6.sa_family = NNG_AF_INET6; + sa->s_in6.sa_port = sin6->sin6_port; + sa->s_in6.sa_scope = sin6->sin6_scope_id; + memcpy(sa->s_in6.sa_addr, sin6->sin6_addr.s6_addr, 16); break; } } + nni_mtx_unlock(&resolv_mtx); done: @@ -169,14 +193,13 @@ done: return (rv); } -static void -resolv_ip(const char *host, const char *serv, int passive, int family, - int proto, int socktype, nni_aio *aio) +void +nni_resolv_ip(const char *host, const char *serv, int family, bool passive, + nng_sockaddr *sa, nni_aio *aio) { resolv_item *item; int fam; int rv; - int port; if (nni_aio_begin(aio) != 0) { return; @@ -196,52 +219,30 @@ resolv_ip(const char *host, const char *serv, int passive, int family, return; } - // We can't use the resolver to look up up ports with AI_NUMERICSERV, - // because Windows' resolver is broken. For example, the resolver - // takes a port number of 1000000 and just rips off the high order - // bits and lets it through! (It seems to time out though, so - // maybe it is ignoring AI_NUMERICSERV.) - port = 0; - if (serv != NULL) { - while (isdigit(*serv)) { - port *= 10; - port += (*serv - '0'); - if (port > 0xffff) { - // Port number out of range. - nni_aio_finish_error(aio, NNG_EADDRINVAL); - return; - } - serv++; - } - if (*serv != '\0') { - nni_aio_finish_error(aio, NNG_EADDRINVAL); - return; - } - } - if ((port == 0) && (!passive)) { - nni_aio_finish_error(aio, NNG_EADDRINVAL); - return; - } - if ((item = NNI_ALLOC_STRUCT(item)) == NULL) { nni_aio_finish_error(aio, NNG_ENOMEM); return; } if (host == NULL) { - item->name = NULL; - } else if ((item->name = nni_strdup(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); - NNI_FREE_STRUCT(item); + resolv_free_item(item); return; } - memset(&item->sa, 0, sizeof(item->sa)); - item->passive = passive; - item->proto = proto; - item->aio = aio; - item->family = fam; - item->socktype = socktype; - item->port = htons((uint16_t) port); + item->sa = sa; + item->passive = passive; + item->aio = aio; + item->family = fam; nni_mtx_lock(&resolv_mtx); if (resolv_fini) { @@ -252,8 +253,7 @@ resolv_ip(const char *host, const char *serv, int passive, int family, } if (rv != 0) { nni_mtx_unlock(&resolv_mtx); - nni_strfree(item->name); - NNI_FREE_STRUCT(item); + resolv_free_item(item); nni_aio_finish_error(aio, rv); return; } @@ -263,20 +263,6 @@ resolv_ip(const char *host, const char *serv, int passive, int family, } void -nni_tcp_resolv( - const char *host, const char *serv, int family, int passive, nni_aio *aio) -{ - resolv_ip(host, serv, passive, family, IPPROTO_TCP, SOCK_STREAM, aio); -} - -void -nni_udp_resolv( - const char *host, const char *serv, int family, int passive, nni_aio *aio) -{ - resolv_ip(host, serv, passive, family, IPPROTO_UDP, SOCK_DGRAM, aio); -} - -void resolv_worker(void *notused) { @@ -308,11 +294,11 @@ resolv_worker(void *notused) if ((aio = item->aio) != NULL) { nni_aio_set_prov_extra(aio, 0, NULL); item->aio = NULL; + item->sa = NULL; nni_aio_finish(aio, rv, 0); } - nni_strfree(item->name); - NNI_FREE_STRUCT(item); + resolv_free_item(item); } nni_mtx_unlock(&resolv_mtx); } diff --git a/src/platform/windows/win_tcpdial.c b/src/platform/windows/win_tcpdial.c index 3d470ca1..89c5bf6a 100644 --- a/src/platform/windows/win_tcpdial.c +++ b/src/platform/windows/win_tcpdial.c @@ -173,22 +173,19 @@ tcp_dial_cb(nni_win_io *io, int rv, size_t cnt) } void -nni_tcp_dial(nni_tcp_dialer *d, nni_aio *aio) +nni_tcp_dial(nni_tcp_dialer *d, const nni_sockaddr *sa, nni_aio *aio) { SOCKET s; SOCKADDR_STORAGE ss; int len; nni_tcp_conn * c; int rv; - nng_sockaddr sa; - - nni_aio_get_sockaddr(aio, &sa); if (nni_aio_begin(aio) != 0) { return; } - if ((len = nni_win_nn2sockaddr(&ss, &sa)) <= 0) { + if ((len = nni_win_nn2sockaddr(&ss, sa)) <= 0) { nni_aio_finish_error(aio, NNG_EADDRINVAL); return; } diff --git a/src/supplemental/tcp/tcp.c b/src/supplemental/tcp/tcp.c index 2fb7f56d..cde79051 100644 --- a/src/supplemental/tcp/tcp.c +++ b/src/supplemental/tcp/tcp.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> // Copyright 2019 Devolutions <info@devolutions.net> // @@ -9,7 +9,6 @@ // found online at https://opensource.org/licenses/MIT. // -#include <stddef.h> #include <stdint.h> #include <string.h> @@ -28,7 +27,6 @@ typedef struct { nni_tcp_dialer * d; // platform dialer implementation nni_aio * resaio; // resolver aio nni_aio * conaio; // platform connection aio - nni_list resaios; nni_list conaios; nni_mtx mtx; } tcp_dialer; @@ -45,8 +43,6 @@ tcp_dial_cancel(nni_aio *aio, void *arg, int rv) if (nni_list_empty(&d->conaios)) { nni_aio_abort(d->conaio, NNG_ECANCELED); - } - if (nni_list_empty(&d->resaios)) { nni_aio_abort(d->resaio, NNG_ECANCELED); } } @@ -54,6 +50,15 @@ tcp_dial_cancel(nni_aio *aio, void *arg, int rv) } static void +tcp_dial_start_next(tcp_dialer *d) +{ + if (nni_list_empty(&d->conaios)) { + return; + } + nni_resolv_ip(d->host, d->port, d->af, false, &d->sa, d->resaio); +} + +static void tcp_dial_res_cb(void *arg) { tcp_dialer *d = arg; @@ -61,34 +66,27 @@ tcp_dial_res_cb(void *arg) int rv; nni_mtx_lock(&d->mtx); - if (d->closed || ((aio = nni_list_first(&d->resaios)) == NULL)) { + if (d->closed || ((aio = nni_list_first(&d->conaios)) == NULL)) { // ignore this. - while ((aio = nni_list_first(&d->resaios)) != NULL) { - nni_list_remove(&d->resaios, aio); + while ((aio = nni_list_first(&d->conaios)) != NULL) { + nni_list_remove(&d->conaios, aio); nni_aio_finish_error(aio, NNG_ECLOSED); } nni_mtx_unlock(&d->mtx); return; } - nni_list_remove(&d->resaios, aio); - if ((rv = nni_aio_result(d->resaio)) != 0) { + nni_list_remove(&d->conaios, aio); nni_aio_finish_error(aio, rv); + + // try DNS again for next connection... + tcp_dial_start_next(d); + } else { - nng_sockaddr sa; - nni_aio_get_sockaddr(d->resaio, &sa); - nni_aio_set_sockaddr(aio, &sa); - nni_list_append(&d->conaios, aio); - if (nni_list_first(&d->conaios) == aio) { - nni_aio_set_sockaddr(d->conaio, &sa); - nni_tcp_dial(d->d, d->conaio); - } + nni_tcp_dial(d->d, &d->sa, d->conaio); } - if (!nni_list_empty(&d->resaios)) { - nni_tcp_resolv(d->host, d->port, d->af, 0, d->resaio); - } nni_mtx_unlock(&d->mtx); } @@ -118,12 +116,7 @@ tcp_dial_con_cb(void *arg) nni_aio_finish(aio, 0, 0); } - if ((aio = nni_list_first(&d->conaios)) != NULL) { - nng_sockaddr sa; - nni_aio_get_sockaddr(aio, &sa); - nni_aio_set_sockaddr(d->conaio, &sa); - nni_tcp_dial(d->d, d->conaio); - } + tcp_dial_start_next(d); nni_mtx_unlock(&d->mtx); } @@ -134,10 +127,6 @@ tcp_dialer_close(void *arg) nni_aio * aio; nni_mtx_lock(&d->mtx); d->closed = true; - while ((aio = nni_list_first(&d->resaios)) != NULL) { - nni_list_remove(&d->resaios, aio); - nni_aio_finish_error(aio, NNG_ECLOSED); - } while ((aio = nni_list_first(&d->conaios)) != NULL) { nni_list_remove(&d->conaios, aio); nni_aio_finish_error(aio, NNG_ECLOSED); @@ -189,17 +178,9 @@ tcp_dialer_dial(void *arg, nng_aio *aio) nni_aio_finish_error(aio, rv); return; } - if (d->host != NULL) { - nni_list_append(&d->resaios, aio); - if (nni_list_first(&d->resaios) == aio) { - nni_tcp_resolv(d->host, d->port, d->af, 0, d->resaio); - } - } else { - nni_list_append(&d->conaios, aio); - if (nni_list_first(&d->conaios) == aio) { - nni_aio_set_sockaddr(d->conaio, &d->sa); - nni_tcp_dial(d->d, d->conaio); - } + nni_list_append(&d->conaios, aio); + if (nni_list_first(&d->conaios) == aio) { + tcp_dial_start_next(d); } nni_mtx_unlock(&d->mtx); } @@ -231,7 +212,6 @@ tcp_dialer_alloc(tcp_dialer **dp) } nni_mtx_init(&d->mtx); - nni_aio_list_init(&d->resaios); nni_aio_list_init(&d->conaios); if (((rv = nni_aio_alloc(&d->resaio, tcp_dial_res_cb, d)) != 0) || @@ -352,8 +332,9 @@ tcp_listener_get_port(void *arg, void *buf, size_t *szp, nni_type t) break; case NNG_AF_INET6: - paddr = (void *) &sa.s_in.sa_port; + paddr = (void *) &sa.s_in6.sa_port; break; + default: paddr = NULL; break; @@ -442,14 +423,13 @@ nni_tcp_listener_alloc(nng_stream_listener **lp, const nng_url *url) if ((h != NULL) && ((strcmp(h, "*") == 0) || (strcmp(h, "") == 0))) { h = NULL; } - nni_tcp_resolv(h, url->u_port, af, 1, aio); + nni_resolv_ip(h, url->u_port, af, true, &sa, aio); nni_aio_wait(aio); if ((rv = nni_aio_result(aio)) != 0) { nni_aio_free(aio); return (rv); } - nni_aio_get_sockaddr(aio, &sa); nni_aio_free(aio); return (tcp_listener_alloc_addr(lp, &sa)); diff --git a/src/transport/tcp/tcp.c b/src/transport/tcp/tcp.c index aee917e9..c240f4ef 100644 --- a/src/transport/tcp/tcp.c +++ b/src/transport/tcp/tcp.c @@ -728,11 +728,8 @@ tcptran_url_parse_source(nng_url *url, nng_sockaddr *sa, const nng_url *surl) return (rv); } - nni_tcp_resolv(src, 0, af, 1, aio); + nni_resolv_ip(src, "0", af, true, sa, aio); nni_aio_wait(aio); - if ((rv = nni_aio_result(aio)) == 0) { - nni_aio_get_sockaddr(aio, sa); - } nni_aio_free(aio); nni_free(src, len + 1); return (rv); diff --git a/src/transport/tls/tls.c b/src/transport/tls/tls.c index c291ecf7..76e182a4 100644 --- a/src/transport/tls/tls.c +++ b/src/transport/tls/tls.c @@ -701,11 +701,8 @@ tlstran_url_parse_source(nni_url *url, nng_sockaddr *sa, const nni_url *surl) return (rv); } - nni_tcp_resolv(src, 0, af, 1, aio); + nni_resolv_ip(src, "0", af, 1, sa, aio); nni_aio_wait(aio); - if ((rv = nni_aio_result(aio)) == 0) { - nni_aio_get_sockaddr(aio, sa); - } nni_aio_free(aio); nni_free(src, len + 1); return (rv); @@ -939,8 +936,7 @@ tlstran_ep_init_listener(void **lp, nni_url *url, nni_listener *nlistener) tlstran_ep_fini(ep); return (rv); } - nni_aio_set_input(aio, 0, &ep->sa); - nni_tcp_resolv(host, url->u_port, af, 1, aio); + nni_resolv_ip(host, url->u_port, af, true, &ep->sa, aio); nni_aio_wait(aio); rv = nni_aio_result(aio); nni_aio_free(aio); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e1a708d5..737aa4d7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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> # Copyright (c) 2012 Martin Sustrik All rights reserved. # Copyright (c) 2013 GoPivotal, Inc. All rights reserved. @@ -130,8 +130,10 @@ nng_test(bug1247) nng_test(id) nng_test(platform) nng_test(reconnect) +nng_test(resolv) nng_test(sock) + add_nng_test(device 5) add_nng_test(errors 2) add_nng_test(files 5) @@ -149,7 +151,6 @@ add_nng_test(nonblock 60) add_nng_test(options 5) add_nng_test(pipe 5) add_nng_test(pollfd 5) -add_nng_test1(resolv 10 NNG_STATIC_LIB) add_nng_test(scalability 20 ON) add_nng_test(set_recvmaxsize 2) add_nng_test1(stats 5 NNG_ENABLE_STATS) diff --git a/tests/resolv.c b/tests/resolv.c index bcfb78a4..43168cdb 100644 --- a/tests/resolv.c +++ b/tests/resolv.c @@ -1,5 +1,5 @@ // -// Copyright 2018 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 @@ -8,199 +8,192 @@ // found online at https://opensource.org/licenses/MIT. // +#include "testutil.h" + #include <string.h> -#include "convey.h" #include "core/nng_impl.h" #include "stubs.h" +#include "acutest.h" + #ifndef _WIN32 -#include <arpa/inet.h> +#include <arpa/inet.h> // for htons, htonl #endif -static const char * -ip4tostr(void *addr) +uint8_t v6loop[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; + +void +test_google_dns(void) { - static char buf[256]; + nng_aio * aio; + nng_sockaddr sa; + + TEST_NNG_PASS(nng_aio_alloc(&aio, NULL, NULL)); + nni_resolv_ip("google-public-dns-a.google.com", "80", NNG_AF_INET, + true, &sa, aio); + nng_aio_wait(aio); + TEST_NNG_PASS(nng_aio_result(aio)); + TEST_CHECK(sa.s_in.sa_family == NNG_AF_INET); + TEST_CHECK(sa.s_in.sa_port == ntohs(80)); + TEST_CHECK(sa.s_in.sa_addr == 0x08080808); // aka 8.8.8.8 + nng_aio_free(aio); +} -#ifdef _WIN32 - return (InetNtop(AF_INET, addr, buf, sizeof(buf))); +void +test_numeric_addr(void) +{ + nng_aio * aio; + nng_sockaddr sa; + + TEST_NNG_PASS(nng_aio_alloc(&aio, NULL, NULL)); + nni_resolv_ip("8.8.4.4", "69", NNG_AF_INET, true, &sa, aio); + nng_aio_wait(aio); + TEST_NNG_PASS(nng_aio_result(aio)); + TEST_CHECK(sa.s_in.sa_family == NNG_AF_INET); + TEST_CHECK(sa.s_in.sa_port == ntohs(69)); + TEST_CHECK(sa.s_in.sa_addr == ntohl(0x08080404)); // 8.8.4.4. + nng_aio_free(aio); +} -#else - return (inet_ntop(AF_INET, addr, buf, sizeof(buf))); +void +test_numeric_v6(void) +{ + nng_aio * aio; + nng_sockaddr sa; + + // Travis CI has moved some of their services to host that + // apparently don't support IPv6 at all. This is very sad. + // CircleCI 2.0 is in the same boat. (Amazon to blame.) + if ((getenv("TRAVIS") != NULL) || (getenv("CIRCLECI") != NULL)) { + return; // skip this one. + } + + TEST_NNG_PASS(nng_aio_alloc(&aio, NULL, NULL)); + nni_resolv_ip("::1", "80", NNG_AF_INET6, true, &sa, aio); + nng_aio_wait(aio); + TEST_NNG_PASS(nng_aio_result(aio)); + TEST_CHECK(sa.s_in6.sa_family == NNG_AF_INET6); + TEST_CHECK(sa.s_in6.sa_port == ntohs(80)); + TEST_CHECK(memcmp(sa.s_in6.sa_addr, v6loop, 16) == 0); + nng_aio_free(aio); +} -#endif +void +test_service_names(void) +{ + nng_aio * aio; + nng_sockaddr sa; + + TEST_NNG_PASS(nng_aio_alloc(&aio, NULL, NULL)); + nni_resolv_ip("8.8.4.4", "http", NNG_AF_INET, true, &sa, aio); + nng_aio_wait(aio); + TEST_NNG_PASS(nng_aio_result(aio)); + TEST_CHECK(sa.s_in.sa_port == ntohs(80)); + TEST_CHECK(sa.s_in.sa_addr = ntohl(0x08080404)); + nng_aio_free(aio); } -static const char * -ip6tostr(void *addr) +void +test_localhost_v4(void) { - static char buf[256]; + nng_aio * aio; + nng_sockaddr sa; + + TEST_NNG_PASS(nng_aio_alloc(&aio, NULL, NULL)); + nni_resolv_ip("localhost", "80", NNG_AF_INET, true, &sa, aio); + nng_aio_wait(aio); + TEST_NNG_PASS(nng_aio_result(aio)); + TEST_CHECK(sa.s_in.sa_family == NNG_AF_INET); + TEST_CHECK(sa.s_in.sa_port == ntohs(80)); + TEST_CHECK(sa.s_in.sa_addr == ntohl(0x7f000001)); + nng_aio_free(aio); +} -#ifdef _WIN32 - return (InetNtop(AF_INET6, addr, buf, sizeof(buf))); +void +test_localhost_unspec(void) +{ + nng_aio * aio; + nng_sockaddr sa; + + TEST_NNG_PASS(nng_aio_alloc(&aio, NULL, NULL)); + nni_resolv_ip("localhost", "80", NNG_AF_UNSPEC, true, &sa, aio); + nng_aio_wait(aio); + TEST_NNG_PASS(nng_aio_result(aio)); + TEST_CHECK( + (sa.s_family == NNG_AF_INET) || (sa.s_family == NNG_AF_INET6)); + switch (sa.s_family) { + case NNG_AF_INET: + TEST_CHECK(sa.s_in.sa_port == ntohs(80)); + TEST_CHECK(sa.s_in.sa_addr == ntohl(0x7f000001)); + break; + case NNG_AF_INET6: + TEST_CHECK(sa.s_in6.sa_port == ntohs(80)); + TEST_CHECK(memcmp(sa.s_in6.sa_addr, v6loop, 16) == 0); + break; + } + nng_aio_free(aio); +} -#else - return (inet_ntop(AF_INET6, addr, buf, sizeof(buf))); +void +test_null_passive(void) +{ + nng_aio * aio; + nng_sockaddr sa; + + TEST_NNG_PASS(nng_aio_alloc(&aio, NULL, NULL)); + nni_resolv_ip(NULL, "80", NNG_AF_INET, true, &sa, aio); + nng_aio_wait(aio); + TEST_NNG_PASS(nng_aio_result(aio)); + TEST_CHECK(sa.s_in.sa_family == NNG_AF_INET); + TEST_CHECK(sa.s_in.sa_port == ntohs(80)); + TEST_CHECK(sa.s_in.sa_addr == 0); // INADDR_ANY + nng_aio_free(aio); +} -#endif +void +test_null_not_passive(void) +{ + nng_aio * aio; + nng_sockaddr sa; + + TEST_NNG_PASS(nng_aio_alloc(&aio, NULL, NULL)); + nni_resolv_ip(NULL, "80", NNG_AF_INET, false, &sa, aio); + nng_aio_wait(aio); + // We can either get NNG_EADDRINVAL, or a loopback address. + // Most systems do the former, but Linux does the latter. + if (nng_aio_result(aio) == 0) { + TEST_CHECK(sa.s_family == NNG_AF_INET); + TEST_CHECK(sa.s_in.sa_addr == htonl(0x7f000001)); + TEST_CHECK(sa.s_in.sa_port == htons(80)); + } else { + TEST_NNG_FAIL(nng_aio_result(aio), NNG_EADDRINVAL); + } + nng_aio_free(aio); } -// These work on Darwin, and should work on illumos, but they may -// depend on the local resolver configuration. We elect not to depend -// too much on them, since localhost can be configured weirdly. Notably -// the normal assumptions on Linux do *not* hold true. -#if 0 - Convey("Localhost IPv6 resolves", { - nng_aio *aio; - const char *str; - nng_sockaddr sa; - So(nng_aio_alloc(&aio, NULL, NULL) == 0); - So(nng_aio_set_input(aio, 0, &sa) == 0); - nni_tcp_resolv("localhost", "80", NNG_AF_INET6, 1, aio); - nng_aio_wait(aio); - So(nng_aio_result(aio) == 0); - So(sa.s_in6.sa_family == NNG_AF_INET6); - So(sa.s_in6.sa_port == ntohs(80)); - str = ip6tostr(&sa.s_in6.sa_addr); - So(strcmp(str, "::1") == 0); - nng_aio_free(aio); - } -#endif +void +test_bad_port_number(void) +{ + nng_aio * aio; + nng_sockaddr sa; + + TEST_NNG_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); + TEST_NNG_FAIL(nng_aio_result(aio), NNG_EADDRINVAL); + nng_aio_free(aio); +} -TestMain("Resolver", { - nni_init(); - - Convey("Google DNS IPv4 resolves", { - nng_aio * aio; - const char * str; - nng_sockaddr sa; - - So(nng_aio_alloc(&aio, NULL, NULL) == 0); - nni_tcp_resolv("google-public-dns-a.google.com", "80", - NNG_AF_INET, 1, aio); - nng_aio_wait(aio); - So(nng_aio_result(aio) == 0); - nni_aio_get_sockaddr(aio, &sa); - So(sa.s_in.sa_family == NNG_AF_INET); - So(sa.s_in.sa_port == ntohs(80)); - str = ip4tostr(&sa.s_in.sa_addr); - So(strcmp(str, "8.8.8.8") == 0); - nng_aio_free(aio); - }); - Convey("Numeric UDP resolves", { - nng_aio * aio; - const char * str; - nng_sockaddr sa; - - So(nng_aio_alloc(&aio, NULL, NULL) == 0); - nni_udp_resolv("8.8.4.4", "69", NNG_AF_INET, 1, aio); - nng_aio_wait(aio); - So(nng_aio_result(aio) == 0); - nni_aio_get_sockaddr(aio, &sa); - So(sa.s_in.sa_family == NNG_AF_INET); - So(sa.s_in.sa_port == ntohs(69)); - str = ip4tostr(&sa.s_in.sa_addr); - So(strcmp(str, "8.8.4.4") == 0); - nng_aio_free(aio); - }); - Convey("Numeric v4 resolves", { - nng_aio * aio; - const char * str; - nng_sockaddr sa; - - So(nng_aio_alloc(&aio, NULL, NULL) == 0); - nni_tcp_resolv("8.8.4.4", "80", NNG_AF_INET, 1, aio); - nng_aio_wait(aio); - So(nng_aio_result(aio) == 0); - nni_aio_get_sockaddr(aio, &sa); - So(sa.s_in.sa_family == NNG_AF_INET); - So(sa.s_in.sa_port == ntohs(80)); - str = ip4tostr(&sa.s_in.sa_addr); - So(strcmp(str, "8.8.4.4") == 0); - nng_aio_free(aio); - }); - - Convey("Numeric v6 resolves", { - nng_aio * aio; - const char * str; - nng_sockaddr sa; - - // Travis CI has moved some of their services to host that - // apparently don't support IPv6 at all. This is very sad. - // CircleCI 2.0 is in the same boat. (Amazon to blame.) - if ((getenv("TRAVIS") != NULL) || - (getenv("CIRCLECI") != NULL)) { - ConveySkip("IPv6 missing from CI provider"); - } - - So(nng_aio_alloc(&aio, NULL, NULL) == 0); - nni_tcp_resolv("::1", "80", NNG_AF_INET6, 1, aio); - nng_aio_wait(aio); - So(nng_aio_result(aio) == 0); - nni_aio_get_sockaddr(aio, &sa); - So(sa.s_in6.sa_family == NNG_AF_INET6); - So(sa.s_in6.sa_port == ntohs(80)); - str = ip6tostr(&sa.s_in6.sa_addr); - So(strcmp(str, "::1") == 0); - nng_aio_free(aio); - }); - - Convey("Name service names not supported", { - nng_aio *aio; - - So(nng_aio_alloc(&aio, NULL, NULL) == 0); - nni_tcp_resolv("8.8.4.4", "http", NNG_AF_INET, 1, aio); - nng_aio_wait(aio); - So(nng_aio_result(aio) == NNG_EADDRINVAL); - nng_aio_free(aio); - }); - - Convey("Localhost IPv4 resolves", { - nng_aio * aio; - const char * str; - nng_sockaddr sa; - - So(nng_aio_alloc(&aio, NULL, NULL) == 0); - nni_tcp_resolv("localhost", "80", NNG_AF_INET, 1, aio); - nng_aio_wait(aio); - So(nng_aio_result(aio) == 0); - nni_aio_get_sockaddr(aio, &sa); - So(sa.s_in.sa_family == NNG_AF_INET); - So(sa.s_in.sa_port == ntohs(80)); - So(sa.s_in.sa_addr == ntohl(0x7f000001)); - str = ip4tostr(&sa.s_in.sa_addr); - So(strcmp(str, "127.0.0.1") == 0); - nng_aio_free(aio); - }); - - Convey("Localhost UNSPEC resolves", { - nng_aio * aio; - const char * str; - nng_sockaddr sa; - - So(nng_aio_alloc(&aio, NULL, NULL) == 0); - nni_tcp_resolv("localhost", "80", NNG_AF_UNSPEC, 1, aio); - nng_aio_wait(aio); - So(nng_aio_result(aio) == 0); - nni_aio_get_sockaddr(aio, &sa); - So((sa.s_family == NNG_AF_INET) || - (sa.s_family == NNG_AF_INET6)); - switch (sa.s_family) { - case NNG_AF_INET: - So(sa.s_in.sa_port == ntohs(80)); - So(sa.s_in.sa_addr == ntohl(0x7f000001)); - str = ip4tostr(&sa.s_in.sa_addr); - So(strcmp(str, "127.0.0.1") == 0); - break; - case NNG_AF_INET6: - So(sa.s_in6.sa_port == ntohs(80)); - str = ip6tostr(&sa.s_in6.sa_addr); - So(strcmp(str, "::1") == 0); - break; - } - nng_aio_free(aio); - }); - - nni_fini(); -}) +TEST_LIST = { + { "resolve google dns", test_google_dns }, + { "resolve numeric addr", test_numeric_addr }, + { "resolve numeric v6", test_numeric_v6 }, + { "resolve service names", test_service_names }, + { "resolve localhost v4", test_localhost_v4 }, + { "resolve localhost unspec", test_localhost_unspec }, + { "resolve null passive", test_null_passive }, + { "resolve null not passive", test_null_not_passive }, + { "resolve bad port number", test_bad_port_number }, + { NULL, NULL }, +}; |
