diff options
Diffstat (limited to '3rdparty/openpgm-svn-r1135/pgm/if_unittest.c')
-rw-r--r-- | 3rdparty/openpgm-svn-r1135/pgm/if_unittest.c | 1497 |
1 files changed, 1497 insertions, 0 deletions
diff --git a/3rdparty/openpgm-svn-r1135/pgm/if_unittest.c b/3rdparty/openpgm-svn-r1135/pgm/if_unittest.c new file mode 100644 index 0000000..ada444a --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/if_unittest.c @@ -0,0 +1,1497 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * unit tests for network interface declaration parsing. + * + * CAUTION: Assumes host is IPv4 by default for AF_UNSPEC + * + * Copyright (c) 2009-2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* IFF_UP */ +#ifndef _BSD_SOURCE +# define _BSD_SOURCE 1 +#endif + +#include <errno.h> +#include <netdb.h> +#include <signal.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <net/if.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/types.h> + +#include <glib.h> +#include <check.h> + + +/* mock state */ + +struct mock_host_t { + struct sockaddr_storage address; + char* canonical_hostname; + char* alias; +}; + +struct mock_network_t { + char* name; + struct sockaddr_storage number; + char** aliases; +}; + +struct mock_interface_t { + unsigned int index; + char* name; + unsigned int flags; + struct sockaddr_storage addr; + struct sockaddr_storage netmask; +}; + +static GList *mock_hosts = NULL, *mock_networks = NULL, *mock_interfaces = NULL; + +#define MOCK_HOSTNAME "kiku" +#define MOCK_HOSTNAME6 "ip6-kiku" /* ping6 doesn't work on fe80:: */ +#define MOCK_NETWORK "private" /* /etc/networks */ +#define MOCK_NETWORK6 "ip6-private" +#define MOCK_PGM_NETWORK "pgm-private" +#define MOCK_PGM_NETWORK6 "pgm-ip6-private" +#define MOCK_INTERFACE "eth0" +#define MOCK_INTERFACE_INDEX 2 +#define MOCK_ADDRESS "10.6.28.33" +#define MOCK_GROUP ((in_addr_t) 0xefc00001) /* 239.192.0.1 */ +#define MOCK_GROUP6_INIT { { { 0xff,8,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } } /* ff08::1 */ +static const struct in6_addr mock_group6_addr = MOCK_GROUP6_INIT; +#define MOCK_ADDRESS6 "2002:dce8:d28e::33" +#define MOCK_ADDRESS6_INIT { { { 0x20,2,0xdc,0xe8,0xd2,0x8e,0,0,0,0,0,0,0,0,0,0x33 } } } +static const struct in6_addr mock_address6_addr = MOCK_ADDRESS6_INIT; + +static int mock_family = 0; +static char* mock_kiku = MOCK_HOSTNAME; +static char* mock_localhost = "localhost"; +static char* mock_invalid = "invalid.invalid"; /* RFC 2606 */ +static char* mock_toolong = "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij12345"; /* 65 */ +static char* mock_hostname = NULL; + +struct pgm_ifaddrs_t; +struct pgm_error_t; + +bool mock_pgm_getifaddrs (struct pgm_ifaddrs_t**, struct pgm_error_t**); +void mock_pgm_freeifaddrs (struct pgm_ifaddrs_t*); +unsigned mock_pgm_if_nametoindex (const sa_family_t, const char*); +char* mock_if_indextoname (unsigned int, char*); +int mock_getnameinfo (const struct sockaddr*, socklen_t, char*, size_t, char*, size_t, int); +int mock_getaddrinfo (const char*, const char*, const struct addrinfo*, struct addrinfo**); +void mock_freeaddrinfo (struct addrinfo*); +int mock_gethostname (char*, size_t); +struct netent* mock_getnetbyname (const char*); +bool mock_pgm_if_getnodeaddr (const sa_family_t, struct sockaddr*, const socklen_t, struct pgm_error_t**); + +#define pgm_getifaddrs mock_pgm_getifaddrs +#define pgm_freeifaddrs mock_pgm_freeifaddrs +#define pgm_if_nametoindex mock_pgm_if_nametoindex +#define if_indextoname mock_if_indextoname +#define getnameinfo mock_getnameinfo +#define getaddrinfo mock_getaddrinfo +#define freeaddrinfo mock_freeaddrinfo +#define gethostname mock_gethostname +#define getnetbyname mock_getnetbyname +#define pgm_if_getnodeaddr mock_pgm_if_getnodeaddr + + +#define IF_DEBUG +#include "if.c" + + +static +gpointer +create_host ( + const char* address, + const char* canonical_hostname, + const char* alias + ) +{ + struct mock_host_t* new_host; + + g_assert (address); + g_assert (canonical_hostname); + + new_host = g_slice_alloc0 (sizeof(struct mock_host_t)); + g_assert (pgm_sockaddr_pton (address, (struct sockaddr*)&new_host->address)); + new_host->canonical_hostname = g_strdup (canonical_hostname); + new_host->alias = alias ? g_strdup (alias) : NULL; + + return new_host; +} + +static +gpointer +create_network ( + const char* name, + const char* number + ) +{ + struct mock_network_t* new_network; + + g_assert (name); + g_assert (number); + + new_network = g_slice_alloc0 (sizeof(struct mock_network_t)); + new_network->name = g_strdup (name); + g_assert (pgm_sockaddr_pton (number, (struct sockaddr*)&new_network->number)); + + return new_network; +} + +static +gpointer +create_interface ( + const unsigned index, + const char* name, + const char* flags + ) +{ + struct mock_interface_t* new_interface; + + g_assert (name); + g_assert (flags); + + new_interface = g_slice_alloc0 (sizeof(struct mock_interface_t)); + new_interface->index = index; + new_interface->name = g_strdup (name); + + struct sockaddr_in* sin = (gpointer)&new_interface->addr; + struct sockaddr_in6* sin6 = (gpointer)&new_interface->addr; + + gchar** tokens = g_strsplit (flags, ",", 0); + for (guint i = 0; tokens[i]; i++) + { + if (strcmp (tokens[i], "up") == 0) + new_interface->flags |= IFF_UP; + else if (strcmp (tokens[i], "down") == 0) + new_interface->flags |= 0; + else if (strcmp (tokens[i], "loop") == 0) + new_interface->flags |= IFF_LOOPBACK; + else if (strcmp (tokens[i], "broadcast") == 0) + new_interface->flags |= IFF_BROADCAST; + else if (strcmp (tokens[i], "multicast") == 0) + new_interface->flags |= IFF_MULTICAST; + else if (strncmp (tokens[i], "ip=", strlen("ip=")) == 0) { + const char* addr = tokens[i] + strlen("ip="); + g_assert (pgm_sockaddr_pton (addr, (struct sockaddr*)&new_interface->addr)); + } + else if (strncmp (tokens[i], "netmask=", strlen("netmask=")) == 0) { + const char* addr = tokens[i] + strlen("netmask="); + g_assert (pgm_sockaddr_pton (addr, (struct sockaddr*)&new_interface->netmask)); + } + else if (strncmp (tokens[i], "scope=", strlen("scope=")) == 0) { + const char* scope = tokens[i] + strlen("scope="); + g_assert (AF_INET6 == ((struct sockaddr*)&new_interface->addr)->sa_family); + ((struct sockaddr_in6*)&new_interface->addr)->sin6_scope_id = atoi (scope); + } + else + g_error ("parsing failed for flag %s%s%s", + tokens[i] ? "\"" : "", tokens[i] ? tokens[i] : "(null)", tokens[i] ? "\"" : ""); + } + + g_strfreev (tokens); + return new_interface; +} + +#define APPEND_HOST2(a,b,c) \ + do { \ + gpointer data = create_host ((a), (b), (c)); \ + g_assert (data); \ + mock_hosts = g_list_append (mock_hosts, data); \ + g_assert (mock_hosts); g_assert (mock_hosts->data); \ + } while (0) +#define APPEND_HOST(a,b) APPEND_HOST2((a),(b),NULL) +#define APPEND_NETWORK(a,b) \ + do { \ + gpointer data = create_network ((a), (b)); \ + g_assert (data); \ + mock_networks = g_list_append (mock_networks, data); \ + g_assert (mock_networks); g_assert (mock_networks->data); \ + } while (0) +#define APPEND_INTERFACE(a,b,c) \ + do { \ + gpointer data = create_interface ((a), (b), (c)); \ + g_assert (data); \ + mock_interfaces = g_list_append (mock_interfaces, data); \ + g_assert (mock_interfaces); g_assert (mock_interfaces->data); \ + } while (0) +static +void +mock_setup_net (void) +{ + mock_hostname = mock_kiku; + + APPEND_HOST ( "127.0.0.1", "localhost"); + APPEND_HOST2( "10.6.28.33", "kiku.hk.miru.hk", "kiku"); + APPEND_HOST2( "2002:dce8:d28e::33", "ip6-kiku", "kiku"); + APPEND_HOST2( "172.12.90.1", "mi-hee.ko.miru.hk", "mi-hee"); + APPEND_HOST2( "::1", "ip6-localhost", "ip6-loopback"); + APPEND_HOST ( "239.192.0.1", "PGM.MCAST.NET"); + APPEND_HOST ( "ff08::1", "IP6-PGM.MCAST.NET"); + + APPEND_NETWORK( "loopback", "127.0.0.0"); + APPEND_NETWORK( "private", "10.6.28.0"); + APPEND_NETWORK( "private2", "172.16.90.0"); + APPEND_NETWORK( "pgm-private", "239.192.0.1"); +#ifdef CONFIG_HAVE_IP6_NETWORKS + APPEND_NETWORK( "ip6-private", "2002:dce8:d28e:0:0:0"); + APPEND_NETWORK( "ip6-pgm-private","ff08::1"); +#endif + + APPEND_INTERFACE( 1, "lo", "up,loop"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast"); + APPEND_INTERFACE( 3, "eth1", "down,broadcast,multicast"); + APPEND_INTERFACE( 1, "lo", "up,loop,ip=127.0.0.1,netmask=255.0.0.0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=10.6.28.33,netmask=255.255.255.0"); + APPEND_INTERFACE( 1, "lo", "up,loop,ip=::1,netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff,scope=0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=2002:dce8:d28e::33,netmask=ffff:ffff:ffff:ffff::0,scope=0"); + APPEND_INTERFACE( 2, "eth0", "up,broadcast,multicast,ip=fe80::214:5eff:febd:6dda,netmask=ffff:ffff:ffff:ffff::0,scope=2"); +} + +static +void +mock_teardown_net (void) +{ + GList* list; + + list = mock_hosts; + while (list) { + struct mock_host_t* host = list->data; + g_free (host->canonical_hostname); + if (host->alias) + g_free (host->alias); + g_slice_free1 (sizeof(struct mock_host_t), host); + list = list->next; + } + g_list_free (mock_hosts); + + list = mock_networks; + while (list) { + struct mock_network_t* network = list->data; + g_free (network->name); + g_slice_free1 (sizeof(struct mock_network_t), network); + list = list->next; + } + g_list_free (mock_networks); + + list = mock_interfaces; + while (list) { + struct mock_interface_t* interface = list->data; + g_free (interface->name); + g_slice_free1 (sizeof(struct mock_interface_t), interface); + list = list->next; + } + g_list_free (mock_interfaces); +} + +/* mock functions for external references */ + +size_t +pgm_transport_pkt_offset2 ( + const bool can_fragment, + const bool use_pgmcc + ) +{ + return 0; +} + +bool +mock_pgm_getifaddrs ( + struct pgm_ifaddrs_t** ifap, + pgm_error_t** err + ) +{ + if (NULL == ifap) { + return -1; + } + + g_debug ("mock_getifaddrs (ifap:%p err:%p)", (gpointer)ifap, (gpointer)err); + + GList* list = mock_interfaces; + int n = g_list_length (list); + struct pgm_ifaddrs_t* ifa = calloc (n, sizeof(struct pgm_ifaddrs_t)); + struct pgm_ifaddrs_t* ift = ifa; + while (list) { + struct mock_interface_t* interface = list->data; + ift->ifa_addr = (gpointer)&interface->addr; + ift->ifa_name = interface->name; + ift->ifa_flags = interface->flags; + ift->ifa_netmask = (gpointer)&interface->netmask; + list = list->next; + if (list) { + ift->ifa_next = ift + 1; + ift = ift->ifa_next; + } + } + + *ifap = ifa; + return TRUE; +} + +void +mock_pgm_freeifaddrs ( + struct pgm_ifaddrs_t* ifa + ) +{ + free (ifa); +} + +unsigned +mock_pgm_if_nametoindex ( + const sa_family_t iffamily, + const char* ifname + ) +{ + GList* list = mock_interfaces; + while (list) { + const struct mock_interface_t* interface = list->data; + if (0 == strcmp (ifname, interface->name)) + return interface->index; + list = list->next; + } + return 0; +} + +char* +mock_if_indextoname ( + unsigned ifindex, + char* ifname + ) +{ + GList* list = mock_interfaces; + while (list) { + const struct mock_interface_t* interface = list->data; + if (interface->index == ifindex) { + strcpy (ifname, interface->name); + return ifname; + } + list = list->next; + } + errno = ENXIO; + return NULL; +} + +int +mock_getnameinfo ( + const struct sockaddr* sa, + socklen_t salen, + char* host, + size_t hostlen, + char* serv, + size_t servlen, + int flags + ) +{ + if ((0 == hostlen && 0 == servlen) || + (NULL == host && NULL == serv)) + return EAI_NONAME; + + if (flags & NI_NUMERICHOST && flags & NI_NAMEREQD) + return EAI_BADFLAGS; + +/* pre-conditions */ + g_assert (NULL != host); + g_assert (hostlen > 0); + g_assert (NULL == serv); + g_assert (0 == servlen); + + const int sa_family = sa->sa_family; + + if (AF_INET == sa_family) + g_assert (sizeof(struct sockaddr_in) == salen); + else { + g_assert (AF_INET6 == sa_family); + g_assert (sizeof(struct sockaddr_in6) == salen); + } + + if (!(flags & NI_NUMERICHOST)) + { + GList* list = mock_hosts; + while (list) { + const struct mock_host_t* _host = list->data; + const int host_family = ((struct sockaddr*)&_host->address)->sa_family; + const size_t host_len = AF_INET == host_family ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); + + if (host_family == sa_family && + host_len == salen && + 0 == memcmp (sa, &_host->address, salen)) + { + if (hostlen < (1 + strlen(_host->canonical_hostname))) + return EAI_OVERFLOW; + strncpy (host, _host->canonical_hostname, hostlen); + return 0; + } + list = list->next; + } + + if (flags & NI_NAMEREQD) + return EAI_NONAME; + } + + if (AF_INET == sa_family) + pgm_inet_ntop (sa_family, &((const struct sockaddr_in*)sa)->sin_addr, host, hostlen); + else { + const unsigned scope = ((const struct sockaddr_in6*)sa)->sin6_scope_id; + pgm_inet_ntop (sa_family, &((const struct sockaddr_in6*)sa)->sin6_addr, host, hostlen); + if (scope) { + char buffer[1+IF_NAMESIZE]; + strcat (host, "%"); + strcat (host, mock_if_indextoname (scope, buffer)); + } + } + return 0; +} + +int +mock_getaddrinfo ( + const char* node, + const char* service, + const struct addrinfo* hints, + struct addrinfo** res + ) +{ + const int ai_flags = hints ? hints->ai_flags : (AI_V4MAPPED | AI_ADDRCONFIG); + const int ai_family = hints ? hints->ai_family : AF_UNSPEC; + GList* list; + struct sockaddr_storage addr; + + if (NULL == node && NULL == service) + return EAI_NONAME; + +/* pre-conditions */ + g_assert (NULL != node); + g_assert (NULL == service); + g_assert (!(ai_flags & AI_CANONNAME)); + g_assert (!(ai_flags & AI_NUMERICSERV)); + g_assert (!(ai_flags & AI_V4MAPPED)); + + g_message ("mock_getaddrinfo (node:%s%s%s service:%s%s%s hints:%p res:%p)", + node ? "\"" : "", node ? node : "(null)", node ? "\"" : "", + service ? "\"" : "", service ? service : "(null)", service ? "\"" : "", + (gpointer)hints, + (gpointer)res); + + gboolean has_ip4_config; + gboolean has_ip6_config; + + if (hints && hints->ai_flags & AI_ADDRCONFIG) + { + has_ip4_config = has_ip6_config = FALSE; + list = mock_interfaces; + while (list) { + const struct mock_interface_t* interface = list->data; + if (AF_INET == ((struct sockaddr*)&interface->addr)->sa_family) + has_ip4_config = TRUE; + else if (AF_INET6 == ((struct sockaddr*)&interface->addr)->sa_family) + has_ip6_config = TRUE; + if (has_ip4_config && has_ip6_config) + break; + list = list->next; + } + } else { + has_ip4_config = has_ip6_config = TRUE; + } + + if (ai_flags & AI_NUMERICHOST) { + pgm_sockaddr_pton (node, (struct sockaddr*)&addr); + } + list = mock_hosts; + while (list) { + struct mock_host_t* host = list->data; + const int host_family = ((struct sockaddr*)&host->address)->sa_family; + if (((strcmp (host->canonical_hostname, node) == 0) || + (host->alias && strcmp (host->alias, node) == 0) || + (ai_flags & AI_NUMERICHOST && + 0 == pgm_sockaddr_cmp ((struct sockaddr*)&addr, (struct sockaddr*)&host->address))) + && + (host_family == ai_family || AF_UNSPEC == ai_family) && + ((AF_INET == host_family && has_ip4_config) || (AF_INET6 == host_family && has_ip6_config))) + { + struct addrinfo* ai = malloc (sizeof(struct addrinfo)); + memset (ai, 0, sizeof(struct addrinfo)); + ai->ai_family = host_family; + ai->ai_addrlen = AF_INET == host_family ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); + ai->ai_addr = (gpointer)&host->address; + *res = ai; + return 0; + } + list = list->next; + } + return EAI_NONAME; +} + +void +mock_freeaddrinfo ( + struct addrinfo* res + ) +{ + free (res); +} + +int +mock_gethostname ( + char* name, + size_t len + ) +{ + if (NULL == name) { + errno = EFAULT; + return -1; + } + if (len < 0) { + errno = EINVAL; + return -1; + } + if (len < (1 + strlen (mock_hostname))) { + errno = ENAMETOOLONG; + return -1; + } +/* force an error */ + if (mock_hostname == mock_toolong) { + errno = ENAMETOOLONG; + return -1; + } + strncpy (name, mock_hostname, len); + if (len > 0) + name[len - 1] = '\0'; + return 0; +} + +struct netent* +mock_getnetbyname ( + const char* name + ) +{ + static struct netent ne; + GList* list = mock_networks; + + if (NULL == name) + return NULL; + + while (list) { + const struct mock_network_t* network = list->data; + if (strcmp (network->name, name) == 0) { + ne.n_name = network->name; + ne.n_aliases = network->aliases; + ne.n_addrtype = AF_INET; + ne.n_net = g_ntohl (((struct sockaddr_in*)&network->number)->sin_addr.s_addr); + return ≠ + } + list = list->next; + } + return NULL; +} + +PGM_GNUC_INTERNAL +bool +mock_pgm_if_getnodeaddr ( + const sa_family_t family, + struct sockaddr* addr, + const socklen_t cnt, + pgm_error_t** error + ) +{ + switch (family) { + case AF_UNSPEC: + case AF_INET: + ((struct sockaddr*)addr)->sa_family = AF_INET; + ((struct sockaddr_in*)addr)->sin_addr.s_addr = inet_addr(MOCK_ADDRESS); + break; + case AF_INET6: + ((struct sockaddr*)addr)->sa_family = AF_INET6; + ((struct sockaddr_in6*)addr)->sin6_addr = mock_address6_addr; + break; + default: + g_assert_not_reached(); + } + return TRUE; +} + +/* following tests will use AF_UNSPEC address family */ + +static +void +mock_setup_unspec (void) +{ + mock_family = AF_UNSPEC; +} + +/* following tests will use AF_INET address family */ + +static +void +mock_setup_ip4 (void) +{ + mock_family = AF_INET; +} + +/* following tests will use AF_INET6 address family */ + +static +void +mock_setup_ip6 (void) +{ + mock_family = AF_INET6; +} + + +/* return 0 if gsr multicast group does not match the default PGM group for + * the address family, return -1 on no match. + */ + +static +gboolean +match_default_group ( + const int ai_family, + const struct group_source_req* gsr + ) +{ + const struct sockaddr_in sa_default = { + .sin_family = AF_INET, + .sin_addr.s_addr = g_htonl (MOCK_GROUP) + }; + const struct sockaddr_in6 sa6_default = { + .sin6_family = AF_INET6, + .sin6_addr = MOCK_GROUP6_INIT + }; + gboolean is_match = FALSE; + + switch (ai_family) { + case AF_UNSPEC: + case AF_INET: + is_match = (0 == pgm_sockaddr_cmp ((struct sockaddr*)&gsr->gsr_group, (const struct sockaddr*)&sa_default)); + if (!is_match) { + char addr1[INET6_ADDRSTRLEN], addr2[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&gsr->gsr_group, addr1, sizeof(addr1)); + pgm_sockaddr_ntop ((struct sockaddr*)&sa_default, addr2, sizeof(addr2)); + g_message ("FALSE == cmp(%s%s%s, default-group %s%s%s)", + addr1 ? "\"" : "", addr1 ? addr1 : "(null)", addr1 ? "\"" : "", + addr2 ? "\"" : "", addr2 ? addr2 : "(null)", addr2 ? "\"" : ""); + } + break; + case AF_INET6: + is_match = (0 == pgm_sockaddr_cmp ((struct sockaddr*)&gsr->gsr_group, (const struct sockaddr*)&sa6_default)); + if (!is_match) { + char addr1[INET6_ADDRSTRLEN], addr2[INET6_ADDRSTRLEN]; + pgm_sockaddr_ntop ((struct sockaddr*)&gsr->gsr_group, addr1, sizeof(addr1)); + pgm_sockaddr_ntop ((struct sockaddr*)&sa6_default, addr2, sizeof(addr2)); + g_message ("FALSE == cmp(%s%s%s, default-group %s%s%s)", + addr1 ? "\"" : "", addr1 ? addr1 : "(null)", addr1 ? "\"" : "", + addr2 ? "\"" : "", addr2 ? addr2 : "(null)", addr2 ? "\"" : ""); + } + default: + break; + } + return is_match; +} + +/* return 0 if gsr source inteface does not match the INADDR_ANY reserved + * address, return -1 on no match. + */ + +static +int +match_default_source ( + const int ai_family, + const struct group_source_req* gsr + ) +{ + if (0 != gsr->gsr_interface) + return FALSE; + +/* ASM: source == group */ + return (0 == pgm_sockaddr_cmp ((const struct sockaddr*)&gsr->gsr_group, (const struct sockaddr*)&gsr->gsr_source)); +} + +/* return 0 if gsr source interface does not match the hosts default interface, + * return -1 on mismatch + */ + +static +int +match_default_interface ( + const int ai_family, + const struct group_source_req* gsr + ) +{ + if (MOCK_INTERFACE_INDEX != gsr->gsr_interface) + return FALSE; + +/* ASM: source == group */ + return (0 == pgm_sockaddr_cmp ((const struct sockaddr*)&gsr->gsr_group, (const struct sockaddr*)&gsr->gsr_source)); +} + +/* target: + * bool + * pgm_getaddrinfo ( + * const char* s, + * const struct pgm_addrinfo_t* const hints, + * struct pgm_addrinfo_t** res, + * pgm_error_t** err + * ) + */ + +struct test_case_t { + const char* ip4; + const char* ip6; +}; + +#define IP4_AND_IP6(x) x, x + +static const struct test_case_t cases_001[] = { + { IP4_AND_IP6("") }, + { IP4_AND_IP6(";") }, + { IP4_AND_IP6(";;") }, + { "239.192.0.1", "ff08::1" }, + { "239.192.0.1", "[ff08::1]" }, + { ";239.192.0.1", ";ff08::1" }, + { ";239.192.0.1", ";[ff08::1]" }, + { ";239.192.0.1;239.192.0.1", ";ff08::1;ff08::1" }, + { ";239.192.0.1;239.192.0.1", ";[ff08::1];[ff08::1]" }, + { "PGM.MCAST.NET", "IP6-PGM.MCAST.NET" }, + { ";PGM.MCAST.NET", ";IP6-PGM.MCAST.NET" }, + { ";PGM.MCAST.NET;PGM.MCAST.NET", ";IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, + { ";239.192.0.1;PGM.MCAST.NET", ";ff08::1;IP6-PGM.MCAST.NET" }, + { ";239.192.0.1;PGM.MCAST.NET", ";[ff08::1];IP6-PGM.MCAST.NET" }, + { ";PGM.MCAST.NET;239.192.0.1", ";IP6-PGM.MCAST.NET;ff08::1" }, + { ";PGM.MCAST.NET;239.192.0.1", ";IP6-PGM.MCAST.NET;[ff08::1]" }, + { "pgm-private", /* ‡ */ "pgm-ip6-private" }, + { ";pgm-private", /* ‡ */ ";pgm-ip6-private" }, + { ";pgm-private;pgm-private", /* ‡ */ ";pgm-ip6-private;pgm-ip6-private" }, + { ";PGM.MCAST.NET;pgm-private", /* ‡ */ ";IP6-PGM.MCAST.NET;pgm-ip6-private" }, + { ";pgm-private;PGM.MCAST.NET", /* ‡ */ ";pgm-ip6-private;IP6-PGM.MCAST.NET" }, + { ";239.192.0.1;pgm-private", /* ‡ */ ";ff08::1;pgm-ip6-private" }, + { ";239.192.0.1;pgm-private", /* ‡ */ ";[ff08::1];pgm-ip6-private" }, + { ";pgm-private;239.192.0.1", /* ‡ */ ";pgm-ip6-private;ff08::1" }, + { ";pgm-private;239.192.0.1", /* ‡ */ ";pgm-ip6-private;[ff08::1]" }, +}; + +START_TEST (test_parse_transport_pass_001) +{ + fail_unless (mock_family == AF_UNSPEC || mock_family == AF_INET || mock_family == AF_INET6, "invalid mock address family"); + + const char* s = (mock_family == AF_INET6) ? cases_001[_i].ip6 : cases_001[_i].ip4; + struct pgm_addrinfo_t hints = { + .ai_family = mock_family + }, *res = NULL; + pgm_error_t* err = NULL; + + g_message ("%i: test_parse_transport_001(%s, %s%s%s)", + _i, + (mock_family == AF_INET6) ? "AF_INET6" : ( (mock_family == AF_INET) ? "AF_INET" : "AF_UNSPEC" ), + s ? "\"" : "", s ? s : "(null)", s ? "\"" : ""); + +/* ‡ Linux does not support IPv6 /etc/networks so IPv6 entries appear as 255.255.255.255 and + * pgm_if_parse_transport will fail. + */ +#ifndef CONFIG_HAVE_IP6_NETWORKS + if (NULL != strstr (s, MOCK_NETWORK6) || NULL != strstr (s, MOCK_PGM_NETWORK6)) + { + g_message ("IPv6 exception, /etc/networks not supported on this platform."); + return; + } +#endif + + gboolean retval = pgm_getaddrinfo (s, &hints, &res, &err); + if (!retval) { + g_message ("pgm_getaddrinfo: %s", + (err && err->message) ? err->message : "(null)"); + } + fail_unless (TRUE == retval, "pgm_getaddrinfo failed"); + fail_if (NULL == res, "no result"); + fail_unless (NULL == err, "error raised"); + + fail_unless (1 == res->ai_recv_addrs_len, "not exactly one receive address"); + fail_unless (match_default_group (mock_family, &res->ai_recv_addrs[0]), "receive address not match default group"); + fail_unless (match_default_source (mock_family, &res->ai_recv_addrs[0]), "receive address not match default source"); + fail_unless (1 == res->ai_send_addrs_len, "not exactly one send address"); + fail_unless (match_default_group (mock_family, &res->ai_send_addrs[0]), "send address not match default group"); + fail_unless (match_default_source (mock_family, &res->ai_send_addrs[0]), "send address not match default source"); +} +END_TEST + +/* interface name + * + * pre-condition: interface defined to match running host + * ipv4 and ipv6 hostnames are different, otherwise "<hostname>" tests might go unexpected. + */ + +static const struct test_case_t cases_002[] = { + { MOCK_INTERFACE, /* † */ MOCK_INTERFACE }, + { MOCK_INTERFACE ";", /* † */ MOCK_INTERFACE ";" }, + { MOCK_INTERFACE ";;", /* † */ MOCK_INTERFACE ";;" }, + { MOCK_INTERFACE ";239.192.0.1", /* † */ MOCK_INTERFACE ";ff08::1" }, + { MOCK_INTERFACE ";239.192.0.1", /* † */ MOCK_INTERFACE ";[ff08::1]" }, + { MOCK_INTERFACE ";239.192.0.1;239.192.0.1", /* † */ MOCK_INTERFACE ";ff08::1;ff08::1" }, + { MOCK_INTERFACE ";239.192.0.1;239.192.0.1", /* † */ MOCK_INTERFACE ";[ff08::1];[ff08::1]" }, + { MOCK_INTERFACE ";PGM.MCAST.NET", /* † */ MOCK_INTERFACE ";IP6-PGM.MCAST.NET" }, + { MOCK_INTERFACE ";PGM.MCAST.NET;PGM.MCAST.NET",/* † */ MOCK_INTERFACE ";IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, + { MOCK_INTERFACE ";239.192.0.1;PGM.MCAST.NET", /* † */ MOCK_INTERFACE ";ff08::1;IP6-PGM.MCAST.NET" }, + { MOCK_INTERFACE ";239.192.0.1;PGM.MCAST.NET", /* † */ MOCK_INTERFACE ";[ff08::1];IP6-PGM.MCAST.NET" }, + { MOCK_INTERFACE ";PGM.MCAST.NET;239.192.0.1", /* † */ MOCK_INTERFACE ";IP6-PGM.MCAST.NET;ff08::1" }, + { MOCK_INTERFACE ";PGM.MCAST.NET;239.192.0.1", /* † */ MOCK_INTERFACE ";IP6-PGM.MCAST.NET;[ff08::1]" }, + { MOCK_INTERFACE ";pgm-private", /* ‡ */ MOCK_INTERFACE ";pgm-ip6-private" }, + { MOCK_INTERFACE ";pgm-private;pgm-private", /* ‡ */ MOCK_INTERFACE ";pgm-ip6-private;pgm-ip6-private" }, + { MOCK_ADDRESS, MOCK_ADDRESS6 }, + { MOCK_ADDRESS, "[" MOCK_ADDRESS6 "]" }, + { MOCK_ADDRESS ";", MOCK_ADDRESS6 ";" }, + { MOCK_ADDRESS ";", "[" MOCK_ADDRESS6 "];" }, + { MOCK_ADDRESS ";;", MOCK_ADDRESS6 ";;" }, + { MOCK_ADDRESS ";;", "[" MOCK_ADDRESS6 "];;" }, + { MOCK_ADDRESS ";239.192.0.1", MOCK_ADDRESS6 ";ff08::1" }, + { MOCK_ADDRESS ";239.192.0.1", "[" MOCK_ADDRESS6 "];[ff08::1]" }, + { MOCK_ADDRESS ";239.192.0.1;239.192.0.1", MOCK_ADDRESS6 ";ff08::1;ff08::1" }, + { MOCK_ADDRESS ";239.192.0.1;239.192.0.1", "[" MOCK_ADDRESS6 "];[ff08::1];[ff08::1]" }, + { MOCK_ADDRESS ";PGM.MCAST.NET", MOCK_ADDRESS6 ";IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS ";PGM.MCAST.NET", "[" MOCK_ADDRESS6 "];IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS ";PGM.MCAST.NET;PGM.MCAST.NET", MOCK_ADDRESS6 ";IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS ";PGM.MCAST.NET;PGM.MCAST.NET", "[" MOCK_ADDRESS6 "];IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS ";239.192.0.1;PGM.MCAST.NET", MOCK_ADDRESS6 ";ff08::1;IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS ";239.192.0.1;PGM.MCAST.NET", "[" MOCK_ADDRESS6 "];[ff08::1];IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS ";PGM.MCAST.NET;239.192.0.1", MOCK_ADDRESS6 ";IP6-PGM.MCAST.NET;ff08::1" }, + { MOCK_ADDRESS ";PGM.MCAST.NET;239.192.0.1", "[" MOCK_ADDRESS6 "];IP6-PGM.MCAST.NET;[ff08::1]" }, + { MOCK_ADDRESS ";pgm-private", MOCK_ADDRESS6 ";pgm-ip6-private" }, + { MOCK_ADDRESS ";pgm-private", "[" MOCK_ADDRESS6 "];pgm-ip6-private" }, + { MOCK_ADDRESS ";pgm-private;pgm-private", MOCK_ADDRESS6 ";pgm-ip6-private;pgm-ip6-private" }, + { MOCK_ADDRESS ";pgm-private;pgm-private", "[" MOCK_ADDRESS6 "];pgm-ip6-private;pgm-ip6-private" }, + { MOCK_NETWORK, /* ‡ */ MOCK_NETWORK6 }, + { MOCK_NETWORK ";", /* ‡ */ MOCK_NETWORK6 ";" }, + { MOCK_NETWORK ";;", /* ‡ */ MOCK_NETWORK6 ";;" }, + { MOCK_NETWORK ";239.192.0.1", /* ‡ */ MOCK_NETWORK6 ";ff08::1" }, + { MOCK_NETWORK ";239.192.0.1", /* ‡ */ MOCK_NETWORK6 ";[ff08::1]" }, + { MOCK_NETWORK ";239.192.0.1;239.192.0.1", /* ‡ */ MOCK_NETWORK6 ";ff08::1;ff08::1" }, + { MOCK_NETWORK ";239.192.0.1;239.192.0.1", /* ‡ */ MOCK_NETWORK6 ";[ff08::1];[ff08::1]" }, + { MOCK_NETWORK ";PGM.MCAST.NET", /* ‡ */ MOCK_NETWORK6 ";IP6-PGM.MCAST.NET" }, + { MOCK_NETWORK ";PGM.MCAST.NET;PGM.MCAST.NET", /* ‡ */ MOCK_NETWORK6 ";IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, + { MOCK_NETWORK ";239.192.0.1;PGM.MCAST.NET", /* ‡ */ MOCK_NETWORK6 ";ff08::1;IP6-PGM.MCAST.NET" }, + { MOCK_NETWORK ";239.192.0.1;PGM.MCAST.NET", /* ‡ */ MOCK_NETWORK6 ";[ff08::1];IP6-PGM.MCAST.NET" }, + { MOCK_NETWORK ";PGM.MCAST.NET;239.192.0.1", /* ‡ */ MOCK_NETWORK6 ";IP6-PGM.MCAST.NET;ff08::1" }, + { MOCK_NETWORK ";PGM.MCAST.NET;239.192.0.1", /* ‡ */ MOCK_NETWORK6 ";IP6-PGM.MCAST.NET;[ff08::1]" }, + { MOCK_NETWORK ";pgm-private", /* ‡ */ MOCK_NETWORK6 ";pgm-ip6-private" }, + { MOCK_NETWORK ";pgm-private;pgm-private", /* ‡ */ MOCK_NETWORK6 ";pgm-ip6-private;pgm-ip6-private" }, + { MOCK_HOSTNAME, MOCK_HOSTNAME6 }, + { MOCK_HOSTNAME ";", MOCK_HOSTNAME6 ";" }, + { MOCK_HOSTNAME ";;", MOCK_HOSTNAME6 ";;" }, + { MOCK_HOSTNAME ";239.192.0.1", MOCK_HOSTNAME6 ";ff08::1" }, + { MOCK_HOSTNAME ";239.192.0.1", MOCK_HOSTNAME6 ";[ff08::1]" }, + { MOCK_HOSTNAME ";239.192.0.1;239.192.0.1", MOCK_HOSTNAME6 ";ff08::1;ff08::1" }, + { MOCK_HOSTNAME ";239.192.0.1;239.192.0.1", MOCK_HOSTNAME6 ";[ff08::1];[ff08::1]" }, + { MOCK_HOSTNAME ";PGM.MCAST.NET", MOCK_HOSTNAME6 ";IP6-PGM.MCAST.NET" }, + { MOCK_HOSTNAME ";PGM.MCAST.NET;PGM.MCAST.NET", MOCK_HOSTNAME6 ";IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, + { MOCK_HOSTNAME ";239.192.0.1;PGM.MCAST.NET", MOCK_HOSTNAME6 ";ff08::1;IP6-PGM.MCAST.NET" }, + { MOCK_HOSTNAME ";239.192.0.1;PGM.MCAST.NET", MOCK_HOSTNAME6 ";[ff08::1];IP6-PGM.MCAST.NET" }, + { MOCK_HOSTNAME ";PGM.MCAST.NET;239.192.0.1", MOCK_HOSTNAME6 ";IP6-PGM.MCAST.NET;ff08::1" }, + { MOCK_HOSTNAME ";PGM.MCAST.NET;239.192.0.1", MOCK_HOSTNAME6 ";IP6-PGM.MCAST.NET;[ff08::1]" }, + { MOCK_HOSTNAME ";pgm-private", MOCK_HOSTNAME6 ";pgm-ip6-private" }, + { MOCK_HOSTNAME ";pgm-private;pgm-private", MOCK_HOSTNAME6 ";pgm-ip6-private;pgm-ip6-private" }, +}; + +START_TEST (test_parse_transport_pass_002) +{ + fail_unless (mock_family == AF_UNSPEC || mock_family == AF_INET || mock_family == AF_INET6, "invalid mock address family"); + + const char* s = (mock_family == AF_INET6) ? cases_002[_i].ip6 : cases_002[_i].ip4; + struct pgm_addrinfo_t hints = { + .ai_family = mock_family + }, *res = NULL; + pgm_error_t* err = NULL; + + g_message ("%i: test_parse_transport_002(%s, %s%s%s)", + _i, + (mock_family == AF_INET6) ? "AF_INET6" : ( (mock_family == AF_INET) ? "AF_INET" : "AF_UNSPEC" ), + s ? "\"" : "", s ? s : "(null)", s ? "\"" : ""); + +/* ‡ Linux does not support IPv6 /etc/networks so IPv6 entries appear as 255.255.255.255 and + * pgm_if_parse_transport will fail. + */ +#ifndef CONFIG_HAVE_IP6_NETWORKS + if (NULL != strstr (s, MOCK_NETWORK6) || NULL != strstr (s, MOCK_PGM_NETWORK6)) + { + g_message ("IPv6 exception, /etc/networks not supported on this platform."); + return; + } +#endif + +/* † Multiple scoped IPv6 interfaces match a simple interface name network parameter and so + * pgm-if_parse_transport will fail finding multiple matching interfaces + */ + if (AF_INET6 == mock_family && 0 == strncmp (s, MOCK_INTERFACE, strlen (MOCK_INTERFACE))) + { + g_message ("IPv6 exception, multiple scoped addresses on one interface"); + fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); + fail_if (NULL == err, "error not raised"); + fail_unless (PGM_ERROR_NOTUNIQ == err->code, "interfaces not found unique"); + return; + } + + fail_unless (TRUE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (1 == res->ai_recv_addrs_len, "not exactly one receive address"); + fail_unless (match_default_group (mock_family, &res->ai_recv_addrs[0]), "receive address not match default group"); + fail_unless (match_default_interface (mock_family, &res->ai_recv_addrs[0]), "receive address not match default interface"); + fail_unless (1 == res->ai_send_addrs_len, "not exactly one send address"); + fail_unless (match_default_group (mock_family, &res->ai_send_addrs[0]), "send address not match default group"); + fail_unless (match_default_interface (mock_family, &res->ai_send_addrs[0]), "send address not match default interface"); +} +END_TEST + +/* network to node address in bits, 8-32 + * + * e.g. 127.0.0.1/16 + */ + +static const struct test_case_t cases_003[] = { + { MOCK_ADDRESS "/24", MOCK_ADDRESS6 "/64" }, + { MOCK_ADDRESS "/24;", MOCK_ADDRESS6 "/64;" }, + { MOCK_ADDRESS "/24;;", MOCK_ADDRESS6 "/64;;" }, + { MOCK_ADDRESS "/24;239.192.0.1", MOCK_ADDRESS6 "/64;ff08::1" }, + { MOCK_ADDRESS "/24;239.192.0.1", MOCK_ADDRESS6 "/64;[ff08::1]" }, + { MOCK_ADDRESS "/24;239.192.0.1;239.192.0.1", MOCK_ADDRESS6 "/64;ff08::1;ff08::1" }, + { MOCK_ADDRESS "/24;239.192.0.1;239.192.0.1", MOCK_ADDRESS6 "/64;[ff08::1];[ff08::1]" }, + { MOCK_ADDRESS "/24;PGM.MCAST.NET", MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS "/24;PGM.MCAST.NET;PGM.MCAST.NET",MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS "/24;239.192.0.1;PGM.MCAST.NET", MOCK_ADDRESS6 "/64;ff08::1;IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS "/24;239.192.0.1;PGM.MCAST.NET", MOCK_ADDRESS6 "/64;[ff08::1];IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS "/24;PGM.MCAST.NET;239.192.0.1", MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET;ff08::1" }, + { MOCK_ADDRESS "/24;PGM.MCAST.NET;239.192.0.1", MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET;[ff08::1]" }, + { MOCK_ADDRESS "/24;PGM.MCAST.NET", MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS "/24;PGM.MCAST.NET;PGM.MCAST.NET",MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET;IP6-PGM.MCAST.NET" }, + { MOCK_ADDRESS "/24;pgm-private", /* ‡ */ MOCK_ADDRESS6 "/64;pgm-ip6-private" }, + { MOCK_ADDRESS "/24;pgm-private;pgm-private", /* ‡ */ MOCK_ADDRESS6 "/64;pgm-ip6-private;pgm-ip6-private" }, + { MOCK_ADDRESS "/24;239.192.0.1;pgm-private", /* ‡ */ MOCK_ADDRESS6 "/64;ff08::1;pgm-ip6-private" }, + { MOCK_ADDRESS "/24;239.192.0.1;pgm-private", /* ‡ */ MOCK_ADDRESS6 "/64;[ff08::1];pgm-ip6-private" }, + { MOCK_ADDRESS "/24;pgm-private;239.192.0.1", /* ‡ */ MOCK_ADDRESS6 "/64;pgm-ip6-private;ff08::1" }, + { MOCK_ADDRESS "/24;pgm-private;239.192.0.1", /* ‡ */ MOCK_ADDRESS6 "/64;pgm-ip6-private;[ff08::1]" }, + { MOCK_ADDRESS "/24;PGM.MCAST.NET;pgm-private", /* ‡ */ MOCK_ADDRESS6 "/64;IP6-PGM.MCAST.NET;pgm-ip6-private" }, + { MOCK_ADDRESS "/24;pgm-private;PGM.MCAST.NET", /* ‡ */ MOCK_ADDRESS6 "/64;pgm-ip6-private;IP6-PGM.MCAST.NET" }, +}; + +START_TEST (test_parse_transport_pass_003) +{ + fail_unless (mock_family == AF_UNSPEC || mock_family == AF_INET || mock_family == AF_INET6, "invalid mock address family"); + + const char* s = (mock_family == AF_INET6) ? cases_003[_i].ip6 : cases_003[_i].ip4; + struct pgm_addrinfo_t hints = { + .ai_family = mock_family + }, *res = NULL; + pgm_error_t* err = NULL; + + g_message ("%i: test_parse_transport_003(%s, %s%s%s)", + _i, + (mock_family == AF_INET6) ? "AF_INET6" : ( (mock_family == AF_INET) ? "AF_INET" : "AF_UNSPEC" ), + s ? "\"" : "", s ? s : "(null)", s ? "\"" : ""); + +/* ‡ Linux does not support IPv6 /etc/networks so IPv6 entries appear as 255.255.255.255 and + * pgm_if_parse_transport will fail. + */ +#ifndef CONFIG_HAVE_IP6_NETWORKS + if (NULL != strstr (s, MOCK_NETWORK6) || NULL != strstr (s, MOCK_PGM_NETWORK6)) + { + g_message ("IPv6 exception, /etc/networks not supported on this platform."); + return; + } +#endif + + gboolean retval = pgm_getaddrinfo (s, &hints, &res, &err); + if (!retval) { + g_message ("pgm_getaddrinfo: %s", + (err && err->message) ? err->message : "(null)"); + } + fail_unless (TRUE == retval, "pgm_getaddrinfo failed"); + fail_unless (1 == res->ai_recv_addrs_len, "not exactly one receive address"); + fail_unless (match_default_group (mock_family, &res->ai_recv_addrs[0]), "receive address not match default group"); + fail_unless (match_default_interface (mock_family, &res->ai_recv_addrs[0]), "receive address not match default interface"); + fail_unless (1 == res->ai_send_addrs_len, "not exactly one send address"); + fail_unless (match_default_group (mock_family, &res->ai_send_addrs[0]), "send address not match default group"); + fail_unless (match_default_interface (mock_family, &res->ai_send_addrs[0]), "send address not match default interface"); +} +END_TEST + +/* asymmetric groups + */ + +START_TEST (test_parse_transport_pass_004) +{ + fail_unless (mock_family == AF_UNSPEC || mock_family == AF_INET || mock_family == AF_INET6, "invalid mock address family"); + + const char* s = (mock_family == AF_INET6) ? ";ff08::1;ff08::2" + /* AF_INET */: ";239.192.56.1;239.192.56.2"; + struct pgm_addrinfo_t hints = { + .ai_family = mock_family + }, *res = NULL; + pgm_error_t* err = NULL; + struct sockaddr_storage addr; + + fail_unless (TRUE == pgm_getaddrinfo (s, &hints, &res, &err), "get_transport_info failed"); + fail_unless (1 == res->ai_recv_addrs_len, "not exactly one receive address"); + fail_unless (1 == res->ai_send_addrs_len, "not exactly one send address"); + if (mock_family == AF_INET6) + { + inet_pton (AF_INET6, "ff08::1", &((struct sockaddr_in6*)&addr)->sin6_addr); + ((struct sockaddr*)&addr)->sa_family = mock_family; + ((struct sockaddr_in6*)&addr)->sin6_port = 0; + ((struct sockaddr_in6*)&addr)->sin6_flowinfo = 0; + ((struct sockaddr_in6*)&addr)->sin6_scope_id = 0; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_recv_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); + inet_pton (AF_INET6, "ff08::2", &((struct sockaddr_in6*)&addr)->sin6_addr); + ((struct sockaddr*)&addr)->sa_family = mock_family; + ((struct sockaddr_in6*)&addr)->sin6_port = 0; + ((struct sockaddr_in6*)&addr)->sin6_flowinfo = 0; + ((struct sockaddr_in6*)&addr)->sin6_scope_id = 0; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_send_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); + } else { + inet_pton (AF_INET, "239.192.56.1", &((struct sockaddr_in*)&addr)->sin_addr); + ((struct sockaddr*)&addr)->sa_family = AF_INET; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_recv_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); + inet_pton (AF_INET, "239.192.56.2", &((struct sockaddr_in*)&addr)->sin_addr); + ((struct sockaddr*)&addr)->sa_family = AF_INET; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_send_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); + } + fail_unless (match_default_source (mock_family, &res->ai_recv_addrs[0]), "source not match"); + fail_unless (match_default_source (mock_family, &res->ai_send_addrs[0]), "source not match"); +} +END_TEST + +/* multiple receive groups and asymmetric sending + */ + +START_TEST (test_parse_transport_pass_005) +{ + fail_unless (mock_family == AF_UNSPEC || mock_family == AF_INET || mock_family == AF_INET6, "invalid mock address family"); + + const char* s = (mock_family == AF_INET6) ? ";ff08::1,ff08::2;ff08::3" + /* AF_INET */: ";239.192.56.1,239.192.56.2;239.192.56.3"; + struct pgm_addrinfo_t hints = { + .ai_family = mock_family + }, *res = NULL; + pgm_error_t* err = NULL; + struct sockaddr_storage addr; + + fail_unless (TRUE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (2 == res->ai_recv_addrs_len, "not exactly one receive address"); + fail_unless (1 == res->ai_send_addrs_len, "not exactly one send address"); + if (mock_family == AF_INET6) + { + inet_pton (AF_INET6, "ff08::1", &((struct sockaddr_in6*)&addr)->sin6_addr); + ((struct sockaddr*)&addr)->sa_family = mock_family; + ((struct sockaddr_in6*)&addr)->sin6_port = 0; + ((struct sockaddr_in6*)&addr)->sin6_flowinfo = 0; + ((struct sockaddr_in6*)&addr)->sin6_scope_id = 0; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_recv_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); + inet_pton (AF_INET6, "ff08::2", &((struct sockaddr_in6*)&addr)->sin6_addr); + ((struct sockaddr*)&addr)->sa_family = mock_family; + ((struct sockaddr_in6*)&addr)->sin6_port = 0; + ((struct sockaddr_in6*)&addr)->sin6_flowinfo = 0; + ((struct sockaddr_in6*)&addr)->sin6_scope_id = 0; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_recv_addrs[1].gsr_group, (struct sockaddr*)&addr), "group not match"); + inet_pton (AF_INET6, "ff08::3", &((struct sockaddr_in6*)&addr)->sin6_addr); + ((struct sockaddr*)&addr)->sa_family = mock_family; + ((struct sockaddr_in6*)&addr)->sin6_port = 0; + ((struct sockaddr_in6*)&addr)->sin6_flowinfo = 0; + ((struct sockaddr_in6*)&addr)->sin6_scope_id = 0; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_send_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); + } else { + inet_pton (AF_INET, "239.192.56.1", &((struct sockaddr_in*)&addr)->sin_addr); + ((struct sockaddr*)&addr)->sa_family = AF_INET; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_recv_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); + inet_pton (AF_INET, "239.192.56.2", &((struct sockaddr_in*)&addr)->sin_addr); + ((struct sockaddr*)&addr)->sa_family = AF_INET; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_recv_addrs[1].gsr_group, (struct sockaddr*)&addr), "group not match"); + inet_pton (AF_INET, "239.192.56.3", &((struct sockaddr_in*)&addr)->sin_addr); + ((struct sockaddr*)&addr)->sa_family = AF_INET; + fail_unless (0 == pgm_sockaddr_cmp ((struct sockaddr*)&res->ai_send_addrs[0].gsr_group, (struct sockaddr*)&addr), "group not match"); + } + fail_unless (match_default_source (mock_family, &res->ai_recv_addrs[0]), "source not match"); + fail_unless (match_default_source (mock_family, &res->ai_send_addrs[0]), "source not match"); +} +END_TEST + + +/* too many interfaces + */ +START_TEST (test_parse_transport_fail_001) +{ + const char* s = "eth0,lo;;;"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* invalid characters, or simply just bogus + */ +START_TEST (test_parse_transport_fail_002) +{ + const char* s = "!@#$%^&*()"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* too many groups + */ +START_TEST (test_parse_transport_fail_003) +{ + const char* s = ";239.192.0.1,239.192.0.2,239.192.0.3,239.192.0.4,239.192.0.5,239.192.0.6,239.192.0.7,239.192.0.8,239.192.0.9,239.192.0.10,239.192.0.11,239.192.0.12,239.192.0.13,239.192.0.14,239.192.0.15,239.192.0.16,239.192.0.17,239.192.0.18,239.192.0.19,239.192.0.20;239.192.0.21"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* too many receiver groups in asymmetric pairing + */ +START_TEST (test_parse_transport_fail_004) +{ + const char* s = ";239.192.0.1,239.192.0.2,239.192.0.3,239.192.0.4,239.192.0.5,239.192.0.6,239.192.0.7,239.192.0.8,239.192.0.9,239.192.0.10,239.192.0.11,239.192.0.12,239.192.0.13,239.192.0.14,239.192.0.15,239.192.0.16,239.192.0.17,239.192.0.18,239.192.0.19,239.192.0.20,239.192.0.21;239.192.0.22"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* null string + */ +START_TEST (test_parse_transport_fail_005) +{ + const char* s = NULL; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* invalid address family + */ +START_TEST (test_parse_transport_fail_006) +{ + const char* s = ";"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_IPX + }, *res = NULL; + pgm_error_t* err = NULL; + + fail_unless (FALSE == pgm_getaddrinfo (s, &hints, &res, &err), "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* invalid transport info pointer + */ +START_TEST (test_parse_transport_fail_007) +{ + const char* s = ";"; + pgm_error_t* err = NULL; + + fail_unless (FALSE == pgm_getaddrinfo (s, NULL, NULL, &err), "pgm_getaddrinfo failed"); +} +END_TEST + +/* invalid interface + */ +START_TEST (test_parse_transport_fail_008) +{ + const char* s = "qe0;"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + gboolean retval = pgm_getaddrinfo (s, &hints, &res, &err); + if (!retval) { + g_message ("pgm_getaddrinfo: %s", err ? err->message : "(null)"); + } + fail_unless (FALSE == retval, "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* non-existing interface IP address + */ +START_TEST (test_parse_transport_fail_009) +{ + const char* s = "172.16.90.1;"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + gboolean retval = pgm_getaddrinfo (s, &hints, &res, &err); + if (!retval) { + g_message ("pgm_getaddrinfo: %s", + (err && err->message) ? err->message : "(null)"); + } + fail_unless (FALSE == retval, "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* non-existing network name address + */ +START_TEST (test_parse_transport_fail_010) +{ + const char* s = "private2;"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + gboolean retval = pgm_getaddrinfo (s, &hints, &res, &err); + if (!retval) { + g_message ("pgm_getaddrinfo: %s", + (err && err->message) ? err->message : "(null)"); + } + fail_unless (FALSE == retval, "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* non-existing host name interface + */ +START_TEST (test_parse_transport_fail_011) +{ + const char* s = "mi-hee.ko.miru.hk;"; + struct pgm_addrinfo_t hints = { + .ai_family = AF_UNSPEC + }, *res = NULL; + pgm_error_t* err = NULL; + + gboolean retval = pgm_getaddrinfo (s, &hints, &res, &err); + if (!retval) { + g_message ("pgm_getaddrinfo: %s", + (err && err->message) ? err->message : "(null)"); + } + fail_unless (FALSE == retval, "pgm_getaddrinfo failed"); + fail_unless (NULL == res, "unexpected result"); +} +END_TEST + +/* target: + * pgm_if_print_all (void) + */ + +START_TEST (test_print_all_pass_001) +{ + pgm_if_print_all (); +} +END_TEST + + +/* target: + * bool + * is_in_net ( + * const struct in_addr* addr, -- in host byte order + * const struct in_addr* netaddr, + * const struct in_addr* netmask + * ) + */ + +struct test_case_net_t { + const char* addr; + const char* netaddr; + const char* netmask; + const gboolean answer; +}; + +static const struct test_case_net_t cases_004[] = { + { "127.0.0.1", "127.0.0.1", "255.0.0.0", TRUE }, + { "127.0.0.1", "127.0.0.1", "255.255.0.0", TRUE }, + { "127.0.0.1", "127.0.0.1", "255.255.255.0", TRUE }, + { "127.0.0.1", "127.0.0.1", "255.255.255.255", TRUE }, + { "127.0.0.1", "127.0.0.0", "255.0.0.0", TRUE }, + { "127.0.0.1", "127.0.0.0", "255.255.0.0", TRUE }, + { "127.0.0.1", "127.0.0.0", "255.255.255.0", TRUE }, + { "127.0.0.1", "127.0.0.0", "255.255.255.255", FALSE }, + { "172.15.1.1", "172.16.0.0", "255.240.0.0", FALSE }, + { "172.16.1.1", "172.16.0.0", "255.240.0.0", TRUE }, + { "172.18.1.1", "172.16.0.0", "255.240.0.0", TRUE }, + { "172.31.1.1", "172.16.0.0", "255.240.0.0", TRUE }, + { "172.32.1.1", "172.16.0.0", "255.240.0.0", FALSE }, +}; + +START_TEST (test_is_in_net_pass_001) +{ + struct in_addr addr, netaddr, netmask; + fail_unless (pgm_inet_pton (AF_INET, cases_004[_i].addr, &addr)); + fail_unless (pgm_inet_pton (AF_INET, cases_004[_i].netaddr, &netaddr)); + fail_unless (pgm_inet_pton (AF_INET, cases_004[_i].netmask, &netmask)); + const gboolean answer = cases_004[_i].answer; + + addr.s_addr = g_ntohl (addr.s_addr); + netaddr.s_addr = g_ntohl (netaddr.s_addr); + netmask.s_addr = g_ntohl (netmask.s_addr); + gboolean result = is_in_net (&addr, &netaddr, &netmask); + + g_message ("result %s (%s)", + result ? "TRUE" : "FALSE", + answer ? "TRUE" : "FALSE"); + + fail_unless (answer == result); +} +END_TEST + +static const struct test_case_net_t cases_005[] = { + { "::1", "::1", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", TRUE }, + { "fe80::203:baff:fe4e:6cc8", "fe80::", "ffff:0000:0000:0000:0000:0000:0000:0000", TRUE }, + { "2002:dec8:d28e::36", "2002:dec8:d28e::", "ffff:ffff:ffff:0000:0000:0000:0000:0000", TRUE }, + { "2002:dec8:d28e::36", "2002:dafa:939:0::", "ffff:ffff:ffff:ffff:0000:0000:0000:0000", FALSE }, +}; + +START_TEST (test_is_in_net6_pass_001) +{ + struct in6_addr addr, netaddr, netmask; + fail_unless (pgm_inet_pton (AF_INET6, cases_005[_i].addr, &addr)); + fail_unless (pgm_inet_pton (AF_INET6, cases_005[_i].netaddr, &netaddr)); + fail_unless (pgm_inet_pton (AF_INET6, cases_005[_i].netmask, &netmask)); + const gboolean answer = cases_005[_i].answer; + + gboolean result = is_in_net6 (&addr, &netaddr, &netmask); + + g_message ("result %s (%s)", + result ? "TRUE" : "FALSE", + answer ? "TRUE" : "FALSE"); + + fail_unless (answer == result); +} +END_TEST + + + +static +Suite* +make_test_suite (void) +{ + Suite* s; + + s = suite_create (__FILE__); + + TCase* tc_is_in_net = tcase_create ("is_in_net"); + suite_add_tcase (s, tc_is_in_net); + tcase_add_checked_fixture (tc_is_in_net, mock_setup_net, mock_teardown_net); + tcase_add_checked_fixture (tc_is_in_net, mock_setup_unspec, NULL); + tcase_add_loop_test (tc_is_in_net, test_is_in_net_pass_001, 0, G_N_ELEMENTS(cases_004)); + + TCase* tc_is_in_net6 = tcase_create ("is_in_net6"); + suite_add_tcase (s, tc_is_in_net6); + tcase_add_checked_fixture (tc_is_in_net6, mock_setup_net, mock_teardown_net); + tcase_add_checked_fixture (tc_is_in_net6, mock_setup_unspec, NULL); + tcase_add_loop_test (tc_is_in_net6, test_is_in_net6_pass_001, 0, G_N_ELEMENTS(cases_005)); + +/* three variations of all parse-transport tests, one for each valid + * address family value: AF_UNSPEC, AF_INET, AF_INET6. + */ + +/* unspecified address family, ai_family == AF_UNSPEC */ + TCase* tc_parse_transport_unspec = tcase_create ("parse_transport/unspec"); + suite_add_tcase (s, tc_parse_transport_unspec); + tcase_add_checked_fixture (tc_parse_transport_unspec, mock_setup_net, mock_teardown_net); + tcase_add_checked_fixture (tc_parse_transport_unspec, mock_setup_unspec, NULL); + tcase_add_loop_test (tc_parse_transport_unspec, test_parse_transport_pass_001, 0, G_N_ELEMENTS(cases_001)); + tcase_add_loop_test (tc_parse_transport_unspec, test_parse_transport_pass_002, 0, G_N_ELEMENTS(cases_002)); + tcase_add_loop_test (tc_parse_transport_unspec, test_parse_transport_pass_003, 0, G_N_ELEMENTS(cases_003)); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_pass_004); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_pass_005); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_001); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_002); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_003); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_004); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_005); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_006); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_007); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_008); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_009); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_010); + tcase_add_test (tc_parse_transport_unspec, test_parse_transport_fail_011); + +/* IP version 4, ai_family = AF_INET */ + TCase* tc_parse_transport_ip4 = tcase_create ("parse_transport/af_inet"); + suite_add_tcase (s, tc_parse_transport_ip4); + tcase_add_checked_fixture (tc_parse_transport_ip4, mock_setup_net, mock_teardown_net); + tcase_add_checked_fixture (tc_parse_transport_ip4, mock_setup_ip4, NULL); + tcase_add_loop_test (tc_parse_transport_ip4, test_parse_transport_pass_001, 0, G_N_ELEMENTS(cases_001)); + tcase_add_loop_test (tc_parse_transport_ip4, test_parse_transport_pass_002, 0, G_N_ELEMENTS(cases_002)); + tcase_add_loop_test (tc_parse_transport_ip4, test_parse_transport_pass_003, 0, G_N_ELEMENTS(cases_003)); + tcase_add_test (tc_parse_transport_ip4, test_parse_transport_pass_004); + tcase_add_test (tc_parse_transport_ip4, test_parse_transport_pass_005); + +/* IP version 6, ai_family = AF_INET6 */ + TCase* tc_parse_transport_ip6 = tcase_create ("parse_transport/af_inet6"); + suite_add_tcase (s, tc_parse_transport_ip6); + tcase_add_checked_fixture (tc_parse_transport_ip6, mock_setup_net, mock_teardown_net); + tcase_add_checked_fixture (tc_parse_transport_ip6, mock_setup_ip6, NULL); + tcase_add_loop_test (tc_parse_transport_ip6, test_parse_transport_pass_001, 0, G_N_ELEMENTS(cases_001)); + tcase_add_loop_test (tc_parse_transport_ip6, test_parse_transport_pass_002, 0, G_N_ELEMENTS(cases_002)); + tcase_add_loop_test (tc_parse_transport_ip6, test_parse_transport_pass_003, 0, G_N_ELEMENTS(cases_003)); + tcase_add_test (tc_parse_transport_ip6, test_parse_transport_pass_004); + tcase_add_test (tc_parse_transport_ip6, test_parse_transport_pass_005); + + TCase* tc_print_all = tcase_create ("print-all"); + tcase_add_checked_fixture (tc_print_all, mock_setup_net, mock_teardown_net); + suite_add_tcase (s, tc_print_all); + tcase_add_test (tc_print_all, test_print_all_pass_001); + + return s; +} + +static +Suite* +make_master_suite (void) +{ + Suite* s = suite_create ("Master"); + return s; +} + +int +main (void) +{ + SRunner* sr = srunner_create (make_master_suite ()); + srunner_add_suite (sr, make_test_suite ()); + srunner_run_all (sr, CK_ENV); + int number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* eof */ |