From fe89d216a5ff19bf5927bb046606aba29f85cc68 Mon Sep 17 00:00:00 2001 From: Erik Boasson Date: Sun, 14 Jun 2020 15:18:22 +0200 Subject: [PATCH] Remove IPv4/IPv6-specific code in adding peers This removes the special handling of IP addresses in adding peer locators from the configuration, instead relying on the general string-to-locator conversion routines. * This extends the common IP handling to code to handle the optional presence of a port and the use of brackets, allowing them always for IPv6 addresses, but requiring them only when needed for disambiguating numerical IPv6 addresses when a port is present. * The "multicast generator" format is now handled in UDPv4 code. Signed-off-by: Erik Boasson --- src/core/ddsi/include/dds/ddsi/ddsi_tran.h | 6 +- src/core/ddsi/include/dds/ddsi/ddsi_udp.h | 2 +- src/core/ddsi/src/ddsi_ipaddr.c | 103 ++++- src/core/ddsi/src/ddsi_ownip.c | 2 +- src/core/ddsi/src/ddsi_udp.c | 58 ++- src/core/ddsi/src/q_addrset.c | 145 +++---- src/core/ddsi/tests/CMakeLists.txt | 1 + src/core/ddsi/tests/locators.c | 428 +++++++++++++++++++++ 8 files changed, 628 insertions(+), 117 deletions(-) create mode 100644 src/core/ddsi/tests/locators.c diff --git a/src/core/ddsi/include/dds/ddsi/ddsi_tran.h b/src/core/ddsi/include/dds/ddsi/ddsi_tran.h index f18b2db..ca1baaf 100644 --- a/src/core/ddsi/include/dds/ddsi/ddsi_tran.h +++ b/src/core/ddsi/include/dds/ddsi/ddsi_tran.h @@ -203,8 +203,8 @@ struct ddsi_tran_qos void ddsi_tran_factories_fini (struct ddsi_domaingv *gv); void ddsi_factory_add (struct ddsi_domaingv *gv, ddsi_tran_factory_t factory); -void ddsi_factory_free (ddsi_tran_factory_t factory); -ddsi_tran_factory_t ddsi_factory_find (const struct ddsi_domaingv *gv, const char * type); +DDS_EXPORT void ddsi_factory_free (ddsi_tran_factory_t factory); +DDS_EXPORT ddsi_tran_factory_t ddsi_factory_find (const struct ddsi_domaingv *gv, const char * type); ddsi_tran_factory_t ddsi_factory_find_supported_kind (const struct ddsi_domaingv *gv, int32_t kind); void ddsi_factory_conn_init (const struct ddsi_tran_factory *factory, ddsi_tran_conn_t conn); @@ -261,7 +261,7 @@ int ddsi_is_mcaddr (const struct ddsi_domaingv *gv, const nn_locator_t *loc); int ddsi_is_ssm_mcaddr (const struct ddsi_domaingv *gv, const nn_locator_t *loc); enum ddsi_nearby_address_result ddsi_is_nearby_address (const nn_locator_t *loc, const nn_locator_t *ownloc, size_t ninterf, const struct nn_interface *interf); -enum ddsi_locator_from_string_result ddsi_locator_from_string (const struct ddsi_domaingv *gv, nn_locator_t *loc, const char *str, ddsi_tran_factory_t default_factory); +DDS_EXPORT enum ddsi_locator_from_string_result ddsi_locator_from_string (const struct ddsi_domaingv *gv, nn_locator_t *loc, const char *str, ddsi_tran_factory_t default_factory); /* 8 for transport/ 1 for [ diff --git a/src/core/ddsi/include/dds/ddsi/ddsi_udp.h b/src/core/ddsi/include/dds/ddsi/ddsi_udp.h index 4b58a19..2d36180 100644 --- a/src/core/ddsi/include/dds/ddsi/ddsi_udp.h +++ b/src/core/ddsi/include/dds/ddsi/ddsi_udp.h @@ -24,7 +24,7 @@ typedef struct nn_udpv4mcgen_address { uint8_t idx; /* must be last: then sorting will put them consecutively */ } nn_udpv4mcgen_address_t; -int ddsi_udp_init (struct ddsi_domaingv *gv); +DDS_EXPORT int ddsi_udp_init (struct ddsi_domaingv *gv); #if defined (__cplusplus) } diff --git a/src/core/ddsi/src/ddsi_ipaddr.c b/src/core/ddsi/src/ddsi_ipaddr.c index b6c6b71..a461584 100644 --- a/src/core/ddsi/src/ddsi_ipaddr.c +++ b/src/core/ddsi/src/ddsi_ipaddr.c @@ -77,6 +77,8 @@ enum ddsi_nearby_address_result ddsi_ipaddr_is_nearby_address (const nn_locator_ enum ddsi_locator_from_string_result ddsi_ipaddr_from_string (ddsi_tran_factory_t tran, nn_locator_t *loc, const char *str, int32_t kind) { + DDSRT_WARNING_MSVC_OFF(4996); + char copy[264]; int af = AF_INET; struct sockaddr_storage tmpaddr; @@ -94,28 +96,101 @@ enum ddsi_locator_from_string_result ddsi_ipaddr_from_string (ddsi_tran_factory_ return AFSR_MISMATCH; } - (void)tran; - if (ddsrt_sockaddrfromstr(af, str, (struct sockaddr *) &tmpaddr) != 0) { -#if DDSRT_HAVE_DNS - /* Not a valid IP address. User may have specified a hostname instead. */ - ddsrt_hostent_t *hent = NULL; - if (ddsrt_gethostbyname(str, af, &hent) != 0) { - return AFSR_UNKNOWN; - } - memcpy(&tmpaddr, &hent->addrs[0], sizeof(hent->addrs[0])); - ddsrt_free (hent); -#else + // IPv6: we format numeric ones surrounded by brackets and we want to + // parse port numbers, so a copy is the most pragamatic approach. POSIX + // hostnames seem to be limited to 255 characters, add a port of max 5 + // digits and a colon, and so 262 should be enough. (Numerical addresses + // add a few other characters, but even so this ought to be plenty.) + const size_t len = strlen(str); + if (len == 0 || len >= sizeof(copy)) + return AFSR_INVALID; + memcpy(copy, str, len + 1); + char *ipstr = copy; + char *portstr = strrchr(copy, ':'); + if (af == AF_INET6 && portstr != strchr(copy, ':') && ipstr[0] != '[') + { + // IPv6 numerical addresses contain colons, so if there are multiple + // colons, we require disambiguation by enclosing the IP part in + // brackets and hence consider "portstr" only if the first character + // is '[' + portstr = NULL; + } + uint16_t port = 0; + if (portstr) { + unsigned tmpport; + int pos; + if (sscanf (portstr + 1, "%u%n", &tmpport, &pos) == 1 && portstr[1 + pos] == 0) + { + if (tmpport < 1 || tmpport > 65535) + return AFSR_INVALID; + *portstr = 0; + port = (uint16_t) tmpport; + } + else if (af == AF_INET) + { + // no colons in IPv4 addresses return AFSR_INVALID; + } + else + { + // allow for IPv6 address embedding IPv4 ones, like ff02::ffff:239.255.0.1 + portstr = NULL; + } + } + +#if DDSRT_HAVE_IPV6 + if (af == AF_INET6) + { + if (copy[0] == '[') { + // strip brackets: last character before the port must be a ']', + // in the absence of a port, the last character in the string. + ipstr = copy + 1; + if (portstr == NULL) { + if (copy[len - 1] != ']') + return AFSR_INVALID; + copy[len - 1] = 0; + } else { + assert (portstr > copy); + if (portstr[-1] != ']') + return AFSR_INVALID; + portstr[-1] = 0; + } + } + } +#endif + + if (ddsrt_sockaddrfromstr(af, ipstr, (struct sockaddr *) &tmpaddr) != 0) { +#if DDSRT_HAVE_DNS + /* Not a valid IP address. User may have specified a hostname instead. */ + ddsrt_hostent_t *hent = NULL; + if (ddsrt_gethostbyname(ipstr, af, &hent) != 0) { + return AFSR_UNKNOWN; + } + memcpy(&tmpaddr, &hent->addrs[0], sizeof(hent->addrs[0])); + ddsrt_free (hent); +#else + return AFSR_INVALID; #endif } + // patch in port (sin_port/sin6_port is undefined at this point and must always be set + // before calling ddsi_ipaddr_to_loc if (tmpaddr.ss_family != af) { return AFSR_MISMATCH; + } else if (af == AF_INET) { + struct sockaddr_in *x = (struct sockaddr_in *) &tmpaddr; + x->sin_port = htons (port); + } else { +#if DDSRT_HAVE_IPV6 + assert (af == AF_INET6); + struct sockaddr_in6 *x = (struct sockaddr_in6 *) &tmpaddr; + x->sin6_port = htons (port); +#else + abort (); +#endif } ddsi_ipaddr_to_loc (tran, loc, (struct sockaddr *)&tmpaddr, kind); - /* This is just an address, so there is no valid value for port, other than INVALID. - Without a guarantee that tmpaddr has port 0, best is to set it explicitly here */ - loc->port = NN_LOCATOR_PORT_INVALID; return AFSR_OK; + DDSRT_WARNING_MSVC_ON(4996); } char *ddsi_ipaddr_to_string (char *dst, size_t sizeof_dst, const nn_locator_t *loc, int with_port) diff --git a/src/core/ddsi/src/ddsi_ownip.c b/src/core/ddsi/src/ddsi_ownip.c index 4b181fb..83a6527 100644 --- a/src/core/ddsi/src/ddsi_ownip.c +++ b/src/core/ddsi/src/ddsi_ownip.c @@ -274,7 +274,7 @@ int find_own_ip (struct ddsi_domaingv *gv, const char *requested_address) if (i < gv->n_interfaces) selected_idx = i; else - GVERROR ("%s: does not match an available interface\n", gv->config.networkAddressString); + GVERROR ("%s: does not match an available interface supporting %s\n", gv->config.networkAddressString, gv->m_factory->m_typename); } if (selected_idx < 0) diff --git a/src/core/ddsi/src/ddsi_udp.c b/src/core/ddsi/src/ddsi_udp.c index d67929d..50f1d20 100644 --- a/src/core/ddsi/src/ddsi_udp.c +++ b/src/core/ddsi/src/ddsi_udp.c @@ -16,6 +16,7 @@ #include "dds/ddsrt/log.h" #include "dds/ddsrt/misc.h" #include "dds/ddsrt/sockets.h" +#include "dds/ddsrt/string.h" #include "ddsi_eth.h" #include "dds/ddsi/ddsi_tran.h" #include "dds/ddsi/ddsi_udp.h" @@ -174,7 +175,8 @@ static ssize_t ddsi_udp_conn_write (ddsi_tran_conn_t conn_cmn, const nn_locator_ } else if (rc != DDS_RETCODE_OK && rc != DDS_RETCODE_NOT_ALLOWED && rc != DDS_RETCODE_NO_CONNECTION) { - GVERROR ("ddsi_udp_conn_write failed with retcode %"PRId32"\n", rc); + char locbuf[DDSI_LOCSTRLEN]; + GVERROR ("ddsi_udp_conn_write to %s failed with retcode %"PRId32"\n", ddsi_locator_to_string (locbuf, sizeof (locbuf), dst), rc); } return (rc == DDS_RETCODE_OK) ? ret : -1; } @@ -699,9 +701,61 @@ static int ddsi_udp_is_ssm_mcaddr (const ddsi_tran_factory_t tran, const nn_loca } #endif +static enum ddsi_locator_from_string_result mcgen_address_from_string (ddsi_tran_factory_t tran, nn_locator_t *loc, const char *str) +{ + // check for UDPv4MCGEN string, be lazy and refuse to recognize as a MCGEN form if there's anything "wrong" with it + DDSRT_WARNING_MSVC_OFF(4996); + char ipstr[280]; + unsigned base, count, idx; + int ipstrlen, pos; + if (strlen (str) + 10 >= sizeof (ipstr)) // + 6 for appending a port + return AFSR_INVALID; + else if (sscanf (str, "%255[^;]%n;%u;%u;%u%n", ipstr, &ipstrlen, &base, &count, &idx, &pos) != 4) + return AFSR_INVALID; + else if (str[pos] != 0 && str[pos] != ':') + return AFSR_INVALID; + else if (!(count > 0 && base < 28 && count < 28 && base + count < 28 && idx < count)) + return AFSR_INVALID; + if (str[pos] == ':') + { + unsigned port; + int pos2; + if (sscanf (str + pos, ":%u%n", &port, &pos2) != 1 || str[pos + pos2] != 0) + return AFSR_INVALID; + // append port to IP component so that ddsi_ipaddr_from_string can do all of the work + // except for filling the specials + assert (ipstrlen >= 0 && (size_t) ipstrlen < sizeof (ipstr)); + assert (pos2 >= 0 && (size_t) pos2 < sizeof (ipstr) - (size_t) ipstrlen); + ddsrt_strlcpy (ipstr + ipstrlen, str + pos, sizeof (ipstr) - (size_t) ipstrlen); + } + + enum ddsi_locator_from_string_result res = ddsi_ipaddr_from_string (tran, loc, ipstr, tran->m_kind); + if (res != AFSR_OK) + return res; + assert (loc->kind == NN_LOCATOR_KIND_UDPv4); + if (!ddsi_udp_is_mcaddr (tran, loc)) + return AFSR_INVALID; + + nn_udpv4mcgen_address_t x; + DDSRT_STATIC_ASSERT (sizeof (x) <= sizeof (loc->address)); + memset (&x, 0, sizeof(x)); + memcpy (&x.ipv4, loc->address + 12, 4); + x.base = (unsigned char) base; + x.count = (unsigned char) count; + x.idx = (unsigned char) idx; + memset (loc->address, 0, sizeof (loc->address)); + memcpy (loc->address, &x, sizeof (x)); + loc->kind = NN_LOCATOR_KIND_UDPv4MCGEN; + return AFSR_OK; + DDSRT_WARNING_MSVC_ON(4996); +} + static enum ddsi_locator_from_string_result ddsi_udp_address_from_string (ddsi_tran_factory_t tran, nn_locator_t *loc, const char *str) { - return ddsi_ipaddr_from_string (tran, loc, str, tran->m_kind); + if (tran->m_kind == TRANS_UDP && mcgen_address_from_string (tran, loc, str) == AFSR_OK) + return AFSR_OK; + else + return ddsi_ipaddr_from_string (tran, loc, str, tran->m_kind); } static char *ddsi_udp_locator_to_string (char *dst, size_t sizeof_dst, const nn_locator_t *loc, int with_port) diff --git a/src/core/ddsi/src/q_addrset.c b/src/core/ddsi/src/q_addrset.c index 98e5406..236fb2b 100644 --- a/src/core/ddsi/src/q_addrset.c +++ b/src/core/ddsi/src/q_addrset.c @@ -44,86 +44,46 @@ static int compare_locators_vwrap (const void *va, const void *vb); static const ddsrt_avl_ctreedef_t addrset_treedef = DDSRT_AVL_CTREEDEF_INITIALIZER (offsetof (struct addrset_node, avlnode), offsetof (struct addrset_node, loc), compare_locators_vwrap, 0); -static int add_addresses_to_addrset_1 (const struct ddsi_domaingv *gv, struct addrset *as, const char *ip, int port_mode, const char *msgtag, int req_mc, int mcgen_base, int mcgen_count, int mcgen_idx) +static int add_addresses_to_addrset_1 (const struct ddsi_domaingv *gv, struct addrset *as, nn_locator_t *loc, int port_mode, const char *msgtag) { char buf[DDSI_LOCSTRLEN]; - nn_locator_t loc; + int32_t maxidx; - switch (ddsi_locator_from_string(gv, &loc, ip, gv->m_factory)) + // check whether port number, address type and mode make sense, and prepare the + // locator by patching the first port number to use if none is given + if (loc->port != NN_LOCATOR_PORT_INVALID) { - case AFSR_OK: - break; - case AFSR_INVALID: - GVERROR ("%s: %s: not a valid address\n", msgtag, ip); - return -1; - case AFSR_UNKNOWN: - GVERROR ("%s: %s: unknown address\n", msgtag, ip); - return -1; - case AFSR_MISMATCH: - GVERROR ("%s: %s: address family mismatch\n", msgtag, ip); + if (port_mode >= 0 && loc->port != (uint32_t) port_mode) + { + GVERROR ("%s: %s: port mismatch (expecting no port or %d)\n", msgtag, ddsi_locator_to_string (buf, sizeof(buf), loc), port_mode); return -1; + } + maxidx = 0; } - - if (req_mc && !ddsi_is_mcaddr (gv, &loc)) + else if (port_mode >= 0) { - GVERROR ("%s: %s: not a multicast address\n", msgtag, ip); - return -1; + loc->port = (uint32_t) port_mode; + maxidx = 0; } - - if (mcgen_base == -1 && mcgen_count == -1 && mcgen_idx == -1) - ; - else if (loc.kind == NN_LOCATOR_KIND_UDPv4 && ddsi_is_mcaddr(gv, &loc) && mcgen_base >= 0 && mcgen_count > 0 && mcgen_base + mcgen_count < 28 && mcgen_idx >= 0 && mcgen_idx < mcgen_count) + else if (ddsi_is_mcaddr (gv, loc)) { - nn_udpv4mcgen_address_t x; - memset(&x, 0, sizeof(x)); - memcpy(&x.ipv4, loc.address + 12, 4); - x.base = (unsigned char) mcgen_base; - x.count = (unsigned char) mcgen_count; - x.idx = (unsigned char) mcgen_idx; - memset(loc.address, 0, sizeof(loc.address)); - memcpy(loc.address, &x, sizeof(x)); - loc.kind = NN_LOCATOR_KIND_UDPv4MCGEN; + loc->port = ddsi_get_port (&gv->config, DDSI_PORT_MULTI_DISC, 0); + maxidx = 0; } else { - GVERROR ("%s: %s,%d,%d,%d: IPv4 multicast address generator invalid or out of place\n", - msgtag, ip, mcgen_base, mcgen_count, mcgen_idx); - return -1; + loc->port = ddsi_get_port (&gv->config, DDSI_PORT_UNI_DISC, 0); + maxidx = gv->config.maxAutoParticipantIndex; } - if (port_mode >= 0) + GVLOG (DDS_LC_CONFIG, "%s: add %s", msgtag, ddsi_locator_to_string (buf, sizeof (buf), loc)); + add_to_addrset (gv, as, loc); + for (int32_t i = 1; i < maxidx; i++) { - loc.port = (unsigned) port_mode; - GVLOG (DDS_LC_CONFIG, "%s: add %s", msgtag, ddsi_locator_to_string(buf, sizeof(buf), &loc)); - add_to_addrset (gv, as, &loc); + loc->port = ddsi_get_port (&gv->config, DDSI_PORT_UNI_DISC, i); + GVLOG (DDS_LC_CONFIG, ", :%"PRIu32, loc->port); + add_to_addrset (gv, as, loc); } - else - { - GVLOG (DDS_LC_CONFIG, "%s: add ", msgtag); - if (!ddsi_is_mcaddr (gv, &loc)) - { - assert (gv->config.maxAutoParticipantIndex >= 0); - for (int32_t i = 0; i <= gv->config.maxAutoParticipantIndex; i++) - { - loc.port = ddsi_get_port (&gv->config, DDSI_PORT_UNI_DISC, i); - if (i == 0) - GVLOG (DDS_LC_CONFIG, "%s", ddsi_locator_to_string(buf, sizeof(buf), &loc)); - else - GVLOG (DDS_LC_CONFIG, ", :%"PRIu32, loc.port); - add_to_addrset (gv, as, &loc); - } - } - else - { - if (port_mode == -1) - loc.port = ddsi_get_port (&gv->config, DDSI_PORT_MULTI_DISC, 0); - else - loc.port = (uint32_t) port_mode; - GVLOG (DDS_LC_CONFIG, "%s", ddsi_locator_to_string(buf, sizeof(buf), &loc)); - add_to_addrset (gv, as, &loc); - } - } - GVLOG (DDS_LC_CONFIG, "\n"); return 0; } @@ -134,50 +94,43 @@ int add_addresses_to_addrset (const struct ddsi_domaingv *gv, struct addrset *as port_mode >= 0 => always set port to port_mode */ DDSRT_WARNING_MSVC_OFF(4996); - char *addrs_copy, *ip, *cursor, *a; + char *addrs_copy, *cursor, *a; int retval = -1; addrs_copy = ddsrt_strdup (addrs); - ip = ddsrt_malloc (strlen (addrs) + 1); cursor = addrs_copy; while ((a = ddsrt_strsep (&cursor, ",")) != NULL) { - int port = 0, pos; - int mcgen_base = -1, mcgen_count = -1, mcgen_idx = -1; - if (gv->config.transport_selector == TRANS_UDP || gv->config.transport_selector == TRANS_TCP) + nn_locator_t loc; + char buf[DDSI_LOCSTRLEN]; + + switch (ddsi_locator_from_string (gv, &loc, a, gv->m_factory)) { - if (port_mode == -1 && sscanf (a, "%[^:]:%d%n", ip, &port, &pos) == 2 && a[pos] == 0) - ; /* XYZ:PORT */ - else if (sscanf (a, "%[^;];%d;%d;%d%n", ip, &mcgen_base, &mcgen_count, &mcgen_idx, &pos) == 4 && a[pos] == 0) - port = port_mode; /* XYZ;BASE;COUNT;IDX for IPv4 MC address generators */ - else if (sscanf (a, "%[^:]%n", ip, &pos) == 1 && a[pos] == 0) - port = port_mode; /* XYZ */ - else { /* XY:Z -- illegal, but conversion routine should flag it */ - strcpy (ip, a); - port = 0; - } - } - else - { - if (port_mode == -1 && sscanf (a, "[%[^]]]:%d%n", ip, &port, &pos) == 2 && a[pos] == 0) - ; /* [XYZ]:PORT */ - else if (sscanf (a, "[%[^]]]%n", ip, &pos) == 1 && a[pos] == 0) - port = port_mode; /* [XYZ] */ - else { /* XYZ -- let conversion routines handle errors */ - strcpy (ip, a); - port = 0; - } + case AFSR_OK: + break; + case AFSR_INVALID: + GVERROR ("%s: %s: not a valid address\n", msgtag, a); + goto error; + case AFSR_UNKNOWN: + GVERROR ("%s: %s: unknown address\n", msgtag, a); + goto error; + case AFSR_MISMATCH: + GVERROR ("%s: %s: address family mismatch\n", msgtag, a); + goto error; } - if ((port > 0 && port <= 65535) || (port_mode == -1 && port == -1)) { - if (add_addresses_to_addrset_1 (gv, as, ip, port, msgtag, req_mc, mcgen_base, mcgen_count, mcgen_idx) < 0) - goto error; - } else { - GVERROR ("%s: %s: port %d invalid\n", msgtag, a, port); + if (req_mc && !ddsi_is_mcaddr (gv, &loc)) + { + GVERROR ("%s: %s: not a multicast address\n", msgtag, ddsi_locator_to_string_no_port (buf, sizeof(buf), &loc)); + goto error; + } + + if (add_addresses_to_addrset_1 (gv, as, &loc, port_mode, msgtag) < 0) + { + goto error; } } retval = 0; error: - ddsrt_free (ip); ddsrt_free (addrs_copy); return retval; DDSRT_WARNING_MSVC_ON(4996); diff --git a/src/core/ddsi/tests/CMakeLists.txt b/src/core/ddsi/tests/CMakeLists.txt index a071abe..5f04c73 100644 --- a/src/core/ddsi/tests/CMakeLists.txt +++ b/src/core/ddsi/tests/CMakeLists.txt @@ -12,6 +12,7 @@ include(CUnit) set(ddsi_test_sources + "locators.c" "plist_generic.c" "plist.c" "mem_ser.h") diff --git a/src/core/ddsi/tests/locators.c b/src/core/ddsi/tests/locators.c new file mode 100644 index 0000000..e3be6eb --- /dev/null +++ b/src/core/ddsi/tests/locators.c @@ -0,0 +1,428 @@ +/* + * Copyright(c) 2020 ADLINK Technology Limited and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +#include +#include + +#include "dds/ddsrt/heap.h" +#include "dds/ddsrt/sockets.h" +#include "dds/ddsi/ddsi_tran.h" +#include "dds/ddsi/ddsi_domaingv.h" +#include "dds/ddsi/ddsi_udp.h" +#include "dds/ddsi/ddsi_tcp.h" +#include "dds/ddsi/q_config.h" +#include "dds/ddsi/q_rtps.h" +#include "CUnit/Theory.h" + +static bool prefix_zero (const nn_locator_t *loc, size_t n) +{ + assert (n <= sizeof (loc->address)); + for (size_t i = 0; i < n; i++) + if (loc->address[i] != 0) + return false; + return true; +} + +static bool check_ipv4_address (const nn_locator_t *loc, const uint8_t x[4]) +{ + return prefix_zero (loc, 12) && memcmp (loc->address + 12, x, 4) == 0; +} + +static bool check_ipv64_address (const nn_locator_t *loc, const uint8_t x[4]) +{ + return prefix_zero (loc, 10) && loc->address[10] == 0xff && loc->address[11] == 0xff && memcmp (loc->address + 12, x, 4) == 0; +} + +static struct ddsi_tran_factory *init (struct ddsi_domaingv *gv, enum transport_selector tr) +{ + memset (gv, 0, sizeof (*gv)); + gv->config.transport_selector = tr; + ddsi_udp_init (gv); + ddsi_tcp_init (gv); + switch (tr) + { + case TRANS_UDP: return ddsi_factory_find (gv, "udp"); + case TRANS_TCP: return ddsi_factory_find (gv, "tcp"); + case TRANS_UDP6: return ddsi_factory_find (gv, "udp6"); + case TRANS_TCP6: return ddsi_factory_find (gv, "tcp6"); + default: return NULL; + } +} + +static void fini (struct ddsi_domaingv *gv) +{ + while (gv->ddsi_tran_factories) + { + struct ddsi_tran_factory *f = gv->ddsi_tran_factories; + gv->ddsi_tran_factories = f->m_factory; + ddsi_factory_free (f); + } +} + +CU_Test (ddsi_locator_from_string, bogusproto) +{ + struct ddsi_domaingv gv; + struct ddsi_tran_factory * const fact = init (&gv, TRANS_UDP); + nn_locator_t loc; + enum ddsi_locator_from_string_result res; + res = ddsi_locator_from_string (&gv, &loc, "bogusproto/xyz", fact); + CU_ASSERT_FATAL (res == AFSR_UNKNOWN); + res = ddsi_locator_from_string (&gv, &loc, "bogusproto/xyz:1234", fact); + CU_ASSERT_FATAL (res == AFSR_UNKNOWN); + res = ddsi_locator_from_string (&gv, &loc, "bogusproto/1.2.3.4:1234", fact); + CU_ASSERT_FATAL (res == AFSR_UNKNOWN); + fini (&gv); +} + +CU_TheoryDataPoints(ddsi_locator_from_string, ipv4_invalid) = { + CU_DataPoints(enum transport_selector, TRANS_UDP, TRANS_TCP) +}; + +CU_Theory ((enum transport_selector tr), ddsi_locator_from_string, ipv4_invalid) +{ + struct ddsi_domaingv gv; + struct ddsi_tran_factory * const fact = init (&gv, tr); + nn_locator_t loc; + enum ddsi_locator_from_string_result res; + char astr[40]; + snprintf (astr, sizeof (astr), "%s/", fact->m_typename); + res = ddsi_locator_from_string (&gv, &loc, astr, fact); + CU_ASSERT_FATAL (res == AFSR_INVALID); + snprintf (astr, sizeof (astr), "%s/:", fact->m_typename); + res = ddsi_locator_from_string (&gv, &loc, astr, fact); + CU_ASSERT_FATAL (res == AFSR_INVALID); + snprintf (astr, sizeof (astr), "%s/1.2:", fact->m_typename); + res = ddsi_locator_from_string (&gv, &loc, astr, fact); + CU_ASSERT_FATAL (res == AFSR_INVALID); + snprintf (astr, sizeof (astr), "%s/1.2:99999", fact->m_typename); + res = ddsi_locator_from_string (&gv, &loc, astr, fact); + CU_ASSERT_FATAL (res == AFSR_INVALID); + // if DNS is supported, a hostname lookup is tried whenever parsing as a numerical address fails + // which means we may get UNKNOWN + snprintf (astr, sizeof (astr), "%s/:1234", fact->m_typename); + res = ddsi_locator_from_string (&gv, &loc, astr, fact); + CU_ASSERT_FATAL (res == AFSR_INVALID || res == AFSR_UNKNOWN); + snprintf (astr, sizeof (astr), "%s/[1.2.3.4]", fact->m_typename); + res = ddsi_locator_from_string (&gv, &loc, astr, fact); + CU_ASSERT_FATAL (res == AFSR_INVALID || res == AFSR_UNKNOWN); + snprintf (astr, sizeof (astr), "%s/[1.2.3.4]:1234", fact->m_typename); + res = ddsi_locator_from_string (&gv, &loc, astr, fact); + CU_ASSERT_FATAL (res == AFSR_INVALID || res == AFSR_UNKNOWN); + fini (&gv); +} + +CU_TheoryDataPoints(ddsi_locator_from_string, ipv4) = { + CU_DataPoints(enum transport_selector, TRANS_UDP, TRANS_TCP) +}; + +CU_Theory ((enum transport_selector tr), ddsi_locator_from_string, ipv4) +{ + struct ddsi_domaingv gv; + struct ddsi_tran_factory * const fact = init (&gv, tr); + nn_locator_t loc; + enum ddsi_locator_from_string_result res; + char astr[40]; + +#if DDSRT_HAVE_DNS + { + enum ddsi_locator_from_string_result exp; + struct sockaddr_in localhost; + ddsrt_hostent_t *hent = NULL; + if (ddsrt_gethostbyname ("localhost", AF_INET, &hent) != 0) + exp = AFSR_UNKNOWN; + else + { + CU_ASSERT_FATAL (hent->addrs[0].ss_family == AF_INET); + memcpy (&localhost, &hent->addrs[0], sizeof (localhost)); + ddsrt_free (hent); + exp = AFSR_OK; + } + res = ddsi_locator_from_string (&gv, &loc, "localhost", fact); + CU_ASSERT_FATAL (res == exp); + if (res == AFSR_OK) + { + CU_ASSERT_FATAL (loc.kind == fact->m_kind); + CU_ASSERT_FATAL (loc.port == NN_LOCATOR_PORT_INVALID); + CU_ASSERT_FATAL (loc.tran == fact); + CU_ASSERT_FATAL (prefix_zero (&loc, 12) && memcmp (loc.address + 12, &localhost.sin_addr.s_addr, 4) == 0); + } + res = ddsi_locator_from_string (&gv, &loc, "localhost:1234", fact); + CU_ASSERT_FATAL (res == exp); + if (res == AFSR_OK) + { + CU_ASSERT_FATAL (loc.kind == fact->m_kind); + CU_ASSERT_FATAL (loc.port == 1234); + CU_ASSERT_FATAL (loc.tran == fact); + CU_ASSERT_FATAL (prefix_zero (&loc, 12) && memcmp (loc.address + 12, &localhost.sin_addr.s_addr, 4) == 0); + } + } +#endif + + res = ddsi_locator_from_string (&gv, &loc, "1.2.3.4", fact); + CU_ASSERT_FATAL (res == AFSR_OK); + CU_ASSERT_FATAL (loc.kind == fact->m_kind); + CU_ASSERT_FATAL (loc.port == NN_LOCATOR_PORT_INVALID); + CU_ASSERT_FATAL (loc.tran == fact); + CU_ASSERT_FATAL (check_ipv4_address (&loc, (uint8_t[]){1,2,3,4})); + + snprintf (astr, sizeof (astr), "%s/1.2.3.4", fact->m_typename); + res = ddsi_locator_from_string (&gv, &loc, astr, fact); + CU_ASSERT_FATAL (res == AFSR_OK); + CU_ASSERT_FATAL (loc.kind == fact->m_kind); + CU_ASSERT_FATAL (loc.port == NN_LOCATOR_PORT_INVALID); + CU_ASSERT_FATAL (loc.tran == fact); + CU_ASSERT_FATAL (check_ipv4_address (&loc, (uint8_t[]){1,2,3,4})); + + snprintf (astr, sizeof (astr), "%s/1.2.3.4:1234", fact->m_typename); + res = ddsi_locator_from_string (&gv, &loc, astr, fact); + CU_ASSERT_FATAL (res == AFSR_OK); + CU_ASSERT_FATAL (loc.kind == fact->m_kind); + CU_ASSERT_FATAL (loc.port == 1234); + CU_ASSERT_FATAL (loc.tran == fact); + CU_ASSERT_FATAL (check_ipv4_address (&loc, (uint8_t[]){1,2,3,4})); + fini (&gv); +} + +CU_Test (ddsi_locator_from_string, ipv4_cross1) +{ + struct ddsi_domaingv gv; + struct ddsi_tran_factory * const fact = init (&gv, TRANS_UDP); + nn_locator_t loc; + enum ddsi_locator_from_string_result res; + res = ddsi_locator_from_string (&gv, &loc, "tcp/1.2.3.4:1234", fact); + CU_ASSERT_FATAL (res == AFSR_OK); + CU_ASSERT_FATAL (loc.kind == NN_LOCATOR_KIND_TCPv4); + CU_ASSERT_FATAL (loc.port == 1234); + CU_ASSERT_FATAL (loc.tran != fact); + CU_ASSERT_FATAL (check_ipv4_address (&loc, (uint8_t[]){1,2,3,4})); + fini (&gv); +} + +CU_Test (ddsi_locator_from_string, ipv4_cross2) +{ + struct ddsi_domaingv gv; + struct ddsi_tran_factory * const fact = init (&gv, TRANS_TCP); + nn_locator_t loc; + enum ddsi_locator_from_string_result res; + res = ddsi_locator_from_string (&gv, &loc, "udp/1.2.3.4:1234", fact); + CU_ASSERT_FATAL (res == AFSR_OK); + CU_ASSERT_FATAL (loc.kind == NN_LOCATOR_KIND_UDPv4); + CU_ASSERT_FATAL (loc.port == 1234); + CU_ASSERT_FATAL (loc.tran != fact); + CU_ASSERT_FATAL (check_ipv4_address (&loc, (uint8_t[]){1,2,3,4})); + fini (&gv); +} + +CU_Test (ddsi_locator_from_string, udpv4mcgen) +{ + struct ddsi_domaingv gv; + struct ddsi_tran_factory * const fact = init (&gv, TRANS_UDP); + nn_locator_t loc; + enum ddsi_locator_from_string_result res; + res = ddsi_locator_from_string (&gv, &loc, "239.255.0.1;4;8;1:1234", fact); + CU_ASSERT_FATAL (res == AFSR_OK); + CU_ASSERT_FATAL (loc.kind == NN_LOCATOR_KIND_UDPv4MCGEN); + CU_ASSERT_FATAL (loc.port == 1234); + CU_ASSERT_FATAL (loc.tran == fact); + CU_ASSERT_FATAL (loc.address[0] == 239 && loc.address[1] == 255 && loc.address[2] == 0 && loc.address[3] == 1); + CU_ASSERT_FATAL (loc.address[4] == 4 && loc.address[5] == 8 && loc.address[6] == 1); + CU_ASSERT_FATAL (loc.address[7] == 0 && loc.address[8] == 0 && loc.address[9] == 0); + CU_ASSERT_FATAL (loc.address[10] == 0 && loc.address[11] == 0 && loc.address[12] == 0); + CU_ASSERT_FATAL (loc.address[13] == 0 && loc.address[14] == 0 && loc.address[15] == 0); + + res = ddsi_locator_from_string (&gv, &loc, "239.255.0.1;4;0;1:1234", fact); + CU_ASSERT_FATAL (res == AFSR_INVALID || res == AFSR_UNKNOWN); + res = ddsi_locator_from_string (&gv, &loc, "239.255.0.1;4;0;1:2345", fact); + CU_ASSERT_FATAL (res == AFSR_INVALID || res == AFSR_UNKNOWN); + res = ddsi_locator_from_string (&gv, &loc, "239.255.0.1;30;1;1:3456", fact); + CU_ASSERT_FATAL (res == AFSR_INVALID || res == AFSR_UNKNOWN); + res = ddsi_locator_from_string (&gv, &loc, "239.255.0.1;4;24;1:4567", fact); + CU_ASSERT_FATAL (res == AFSR_INVALID || res == AFSR_UNKNOWN); + res = ddsi_locator_from_string (&gv, &loc, "239.255.0.1;4;3;3:5678", fact); + CU_ASSERT_FATAL (res == AFSR_INVALID || res == AFSR_UNKNOWN); + fini (&gv); +} + +CU_TheoryDataPoints(ddsi_locator_from_string, ipv6_invalid) = { + CU_DataPoints(enum transport_selector, TRANS_UDP6, TRANS_TCP6) +}; + +CU_Theory ((enum transport_selector tr), ddsi_locator_from_string, ipv6_invalid) +{ +#if DDSRT_HAVE_IPV6 + struct ddsi_domaingv gv; + struct ddsi_tran_factory * const fact = init (&gv, tr); + nn_locator_t loc; + enum ddsi_locator_from_string_result res; + char astr[40]; + snprintf (astr, sizeof (astr), "%s/", fact->m_typename); + res = ddsi_locator_from_string (&gv, &loc, astr, fact); + CU_ASSERT_FATAL (res == AFSR_INVALID); + // if DNS is supported, a hostname lookup is tried whenever parsing as a numerical address fails + // which means we may get UNKNOWN + snprintf (astr, sizeof (astr), "%s/::1:31415", fact->m_typename); + res = ddsi_locator_from_string (&gv, &loc, astr, fact); + CU_ASSERT_FATAL (res == AFSR_INVALID || res == AFSR_UNKNOWN); + snprintf (astr, sizeof (astr), "%s/:", fact->m_typename); + res = ddsi_locator_from_string (&gv, &loc, astr, fact); + CU_ASSERT_FATAL (res == AFSR_INVALID || res == AFSR_UNKNOWN); + snprintf (astr, sizeof (astr), "%s/1.2:", fact->m_typename); + res = ddsi_locator_from_string (&gv, &loc, astr, fact); + CU_ASSERT_FATAL (res == AFSR_INVALID || res == AFSR_UNKNOWN); + snprintf (astr, sizeof (astr), "%s/]:", fact->m_typename); + res = ddsi_locator_from_string (&gv, &loc, astr, fact); + CU_ASSERT_FATAL (res == AFSR_INVALID || res == AFSR_UNKNOWN); + snprintf (astr, sizeof (astr), "%s/[", fact->m_typename); + res = ddsi_locator_from_string (&gv, &loc, astr, fact); + CU_ASSERT_FATAL (res == AFSR_INVALID || res == AFSR_UNKNOWN); + snprintf (astr, sizeof (astr), "%s/[]", fact->m_typename); + res = ddsi_locator_from_string (&gv, &loc, astr, fact); + CU_ASSERT_FATAL (res == AFSR_INVALID || res == AFSR_UNKNOWN); + snprintf (astr, sizeof (astr), "%s/:1234", fact->m_typename); + res = ddsi_locator_from_string (&gv, &loc, astr, fact); + CU_ASSERT_FATAL (res == AFSR_INVALID || res == AFSR_UNKNOWN); + fini (&gv); +#else + CU_PASS ("No IPv6 support"); +#endif +} + +CU_TheoryDataPoints(ddsi_locator_from_string, ipv6) = { + CU_DataPoints(enum transport_selector, TRANS_UDP6, TRANS_TCP6) +}; + +CU_Theory ((enum transport_selector tr), ddsi_locator_from_string, ipv6) +{ +#if DDSRT_HAVE_IPV6 + struct ddsi_domaingv gv; + struct ddsi_tran_factory * const fact = init (&gv, tr); + nn_locator_t loc; + enum ddsi_locator_from_string_result res; + char astr[40]; + +#if DDSRT_HAVE_DNS + { + enum ddsi_locator_from_string_result exp; + struct sockaddr_in6 localhost; + ddsrt_hostent_t *hent = NULL; + if (ddsrt_gethostbyname ("localhost", AF_INET6, &hent) != 0) + exp = AFSR_UNKNOWN; + else + { + CU_ASSERT_FATAL (hent->addrs[0].ss_family == AF_INET6); + memcpy (&localhost, &hent->addrs[0], sizeof (localhost)); + ddsrt_free (hent); + exp = AFSR_OK; + } + res = ddsi_locator_from_string (&gv, &loc, "localhost", fact); + CU_ASSERT_FATAL (res == exp); + if (res == AFSR_OK) + { + CU_ASSERT_FATAL (loc.kind == fact->m_kind); + CU_ASSERT_FATAL (loc.port == NN_LOCATOR_PORT_INVALID); + CU_ASSERT_FATAL (loc.tran == fact); + CU_ASSERT_FATAL (memcmp (loc.address, &localhost.sin6_addr.s6_addr, 16) == 0); + } + res = ddsi_locator_from_string (&gv, &loc, "[localhost]", fact); + CU_ASSERT_FATAL (res == exp); + if (res == AFSR_OK) + { + CU_ASSERT_FATAL (loc.kind == fact->m_kind); + CU_ASSERT_FATAL (loc.port == NN_LOCATOR_PORT_INVALID); + CU_ASSERT_FATAL (loc.tran == fact); + CU_ASSERT_FATAL (memcmp (loc.address, &localhost.sin6_addr.s6_addr, 16) == 0); + } + res = ddsi_locator_from_string (&gv, &loc, "localhost:1234", fact); + CU_ASSERT_FATAL (res == exp); + if (res == AFSR_OK) + { + CU_ASSERT_FATAL (loc.kind == fact->m_kind); + CU_ASSERT_FATAL (loc.port == 1234); + CU_ASSERT_FATAL (loc.tran == fact); + CU_ASSERT_FATAL (memcmp (loc.address, &localhost.sin6_addr.s6_addr, 16) == 0); + } + res = ddsi_locator_from_string (&gv, &loc, "[localhost]:4567", fact); + CU_ASSERT_FATAL (res == exp); + if (res == AFSR_OK) + { + CU_ASSERT_FATAL (loc.kind == fact->m_kind); + CU_ASSERT_FATAL (loc.port == 4567); + CU_ASSERT_FATAL (loc.tran == fact); + CU_ASSERT_FATAL (memcmp (loc.address, &localhost.sin6_addr.s6_addr, 16) == 0); + } + } +#endif + + res = ddsi_locator_from_string (&gv, &loc, "1.2.3.4", fact); + CU_ASSERT_FATAL (res == AFSR_OK || res == AFSR_UNKNOWN); + if (res == AFSR_OK) + { + CU_ASSERT_FATAL (loc.kind == fact->m_kind); + CU_ASSERT_FATAL (loc.port == NN_LOCATOR_PORT_INVALID); + CU_ASSERT_FATAL (loc.tran == fact); + CU_ASSERT_FATAL (check_ipv64_address (&loc, (uint8_t[]){1,2,3,4})); + } + + res = ddsi_locator_from_string (&gv, &loc, "[1.2.3.4]", fact); + CU_ASSERT_FATAL (res == AFSR_OK || res == AFSR_UNKNOWN); + if (res == AFSR_OK) + { + CU_ASSERT_FATAL (loc.kind == fact->m_kind); + CU_ASSERT_FATAL (loc.port == NN_LOCATOR_PORT_INVALID); + CU_ASSERT_FATAL (loc.tran == fact); + CU_ASSERT_FATAL (check_ipv64_address (&loc, (uint8_t[]){1,2,3,4})); + } + + snprintf (astr, sizeof (astr), "%s/1.2.3.4", fact->m_typename); + res = ddsi_locator_from_string (&gv, &loc, astr, fact); + CU_ASSERT_FATAL (res == AFSR_OK || res == AFSR_UNKNOWN); if (res == AFSR_OK) + { + CU_ASSERT_FATAL (loc.kind == fact->m_kind); + CU_ASSERT_FATAL (loc.port == NN_LOCATOR_PORT_INVALID); + CU_ASSERT_FATAL (loc.tran == fact); + CU_ASSERT_FATAL (check_ipv64_address (&loc, (uint8_t[]){1,2,3,4})); + } + + snprintf (astr, sizeof (astr), "%s/1.2.3.4:6789", fact->m_typename); + res = ddsi_locator_from_string (&gv, &loc, astr, fact); + CU_ASSERT_FATAL (res == AFSR_OK || res == AFSR_UNKNOWN); + if (res == AFSR_OK) + { + CU_ASSERT_FATAL (loc.kind == fact->m_kind); + CU_ASSERT_FATAL (loc.port == 6789); + CU_ASSERT_FATAL (loc.tran == fact); + CU_ASSERT_FATAL (check_ipv64_address (&loc, (uint8_t[]){1,2,3,4})); + } + + snprintf (astr, sizeof (astr), "%s/[1.2.3.4]:7890", fact->m_typename); + res = ddsi_locator_from_string (&gv, &loc, astr, fact); + if (res == AFSR_OK) + { + CU_ASSERT_FATAL (res == AFSR_OK || res == AFSR_UNKNOWN); + CU_ASSERT_FATAL (loc.kind == fact->m_kind); + CU_ASSERT_FATAL (loc.port == 7890); + CU_ASSERT_FATAL (loc.tran == fact); + CU_ASSERT_FATAL (check_ipv64_address (&loc, (uint8_t[]){1,2,3,4})); + } + + snprintf (astr, sizeof (astr), "%s/[::1]:8901", fact->m_typename); + res = ddsi_locator_from_string (&gv, &loc, astr, fact); + CU_ASSERT_FATAL (res == AFSR_OK); + CU_ASSERT_FATAL (loc.kind == fact->m_kind); + CU_ASSERT_FATAL (loc.port == 8901); + CU_ASSERT_FATAL (loc.tran == fact); + CU_ASSERT_FATAL (prefix_zero (&loc, 15) && loc.address[15] == 1); + + fini (&gv); +#else + CU_PASS ("No IPv6 support"); +#endif +}