| /* Tests for __inet6_scopeid_pton and IPv6 scopes in getaddrinfo. |
| Copyright (C) 2016-2018 Free Software Foundation, Inc. |
| This file is part of the GNU C Library. |
| |
| The GNU C 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. |
| |
| The GNU C 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 the GNU C Library; if not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include <arpa/inet.h> |
| #include <inttypes.h> |
| #include <net-internal.h> |
| #include <net/if.h> |
| #include <netdb.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <support/check.h> |
| #include <support/support.h> |
| #include <support/test-driver.h> |
| |
| /* An interface which is known to the system. */ |
| static const char *interface_name; |
| static uint32_t interface_index; |
| |
| /* Initiale the variables above. */ |
| static void |
| setup_interface (void) |
| { |
| struct if_nameindex *list = if_nameindex (); |
| if (list != NULL && list[0].if_index != 0 && list[0].if_name[0] != '\0') |
| { |
| interface_name = list[0].if_name; |
| interface_index = list[0].if_index; |
| } |
| } |
| |
| /* Convert ADDRESS to struct in6_addr. */ |
| static struct in6_addr |
| from_string (const char *address) |
| { |
| struct in6_addr addr; |
| if (inet_pton (AF_INET6, address, &addr) != 1) |
| FAIL_EXIT1 ("inet_pton (\"%s\")", address); |
| return addr; |
| } |
| |
| /* Invoke getaddrinfo to parse ADDRESS%SCOPE. Return true if |
| getaddrinfo was successful. */ |
| static bool |
| call_gai (int family, const char *address, const char *scope, |
| struct sockaddr_in6 *result) |
| { |
| struct addrinfo hints = |
| { |
| .ai_family = family, |
| .ai_flags = AI_NUMERICHOST, |
| .ai_socktype = SOCK_DGRAM, |
| .ai_protocol = IPPROTO_UDP, |
| }; |
| char *fulladdr = xasprintf ("%s%%%s", address, scope); |
| struct addrinfo *ai = NULL; |
| int ret = getaddrinfo (fulladdr, NULL, &hints, &ai); |
| if (ret == EAI_ADDRFAMILY || ret == EAI_NONAME) |
| { |
| if (test_verbose > 0) |
| printf ("info: getaddrinfo (\"%s\"): %s (%d)\n", |
| fulladdr, gai_strerror (ret), ret); |
| free (fulladdr); |
| return false; |
| } |
| if (ret != 0) |
| FAIL_EXIT1 ("getaddrinfo (\"%s\"): %s (%d)\n", |
| fulladdr, gai_strerror (ret), ret); |
| TEST_VERIFY_EXIT (ai != NULL); |
| TEST_VERIFY_EXIT (ai->ai_addrlen == sizeof (*result)); |
| TEST_VERIFY (ai->ai_family == AF_INET6); |
| TEST_VERIFY (ai->ai_next == NULL); |
| memcpy (result, ai->ai_addr, sizeof (*result)); |
| free (fulladdr); |
| freeaddrinfo (ai); |
| return true; |
| } |
| |
| /* Verify that a successful call to getaddrinfo returned the expected |
| scope data. */ |
| static void |
| check_ai (const char *what, const char *addr_string, const char *scope_string, |
| const struct sockaddr_in6 *sa, |
| const struct in6_addr *addr, uint32_t scope) |
| { |
| if (memcmp (addr, &sa->sin6_addr, sizeof (*addr)) != 0) |
| { |
| support_record_failure (); |
| printf ("error: getaddrinfo %s address mismatch for %s%%%s\n", |
| what, addr_string, scope_string); |
| } |
| if (sa->sin6_scope_id != scope) |
| { |
| support_record_failure (); |
| printf ("error: getaddrinfo %s scope mismatch for %s%%%s\n" |
| " expected: %" PRIu32 "\n" |
| " actual: %" PRIu32 "\n", |
| what, addr_string, scope_string, scope, sa->sin6_scope_id); |
| } |
| } |
| |
| /* Check a single address were we expected a failure. */ |
| static void |
| expect_failure (const char *address, const char *scope) |
| { |
| if (test_verbose > 0) |
| printf ("info: expecting failure for %s%%%s\n", address, scope); |
| struct in6_addr addr = from_string (address); |
| uint32_t result = 1234; |
| if (__inet6_scopeid_pton (&addr, scope, &result) == 0) |
| { |
| support_record_failure (); |
| printf ("error: unexpected success for %s%%%s\n", |
| address, scope); |
| } |
| if (result != 1234) |
| { |
| support_record_failure (); |
| printf ("error: unexpected result update for %s%%%s\n", |
| address, scope); |
| } |
| |
| struct sockaddr_in6 sa; |
| if (call_gai (AF_UNSPEC, address, scope, &sa)) |
| { |
| support_record_failure (); |
| printf ("error: unexpected getaddrinfo success for %s%%%s (AF_UNSPEC)\n", |
| address, scope); |
| } |
| if (call_gai (AF_INET6, address, scope, &sa)) |
| { |
| support_record_failure (); |
| printf ("error: unexpected getaddrinfo success for %s%%%s (AF_INET6)\n", |
| address, scope); |
| } |
| } |
| |
| /* Check a single address were we expected a success. */ |
| static void |
| expect_success (const char *address, const char *scope, uint32_t expected) |
| { |
| if (test_verbose > 0) |
| printf ("info: expecting success for %s%%%s\n", address, scope); |
| struct in6_addr addr = from_string (address); |
| uint32_t actual = expected + 1; |
| if (__inet6_scopeid_pton (&addr, scope, &actual) != 0) |
| { |
| support_record_failure (); |
| printf ("error: unexpected failure for %s%%%s\n", |
| address, scope); |
| } |
| if (actual != expected) |
| { |
| support_record_failure (); |
| printf ("error: unexpected result for for %s%%%s\n", |
| address, scope); |
| printf (" expected: %" PRIu32 "\n", expected); |
| printf (" actual: %" PRIu32 "\n", actual); |
| } |
| |
| struct sockaddr_in6 sa; |
| memset (&sa, 0xc0, sizeof (sa)); |
| if (call_gai (AF_UNSPEC, address, scope, &sa)) |
| check_ai ("AF_UNSPEC", address, scope, &sa, &addr, expected); |
| else |
| { |
| support_record_failure (); |
| printf ("error: unexpected getaddrinfo failure for %s%%%s (AF_UNSPEC)\n", |
| address, scope); |
| } |
| memset (&sa, 0xc0, sizeof (sa)); |
| if (call_gai (AF_INET6, address, scope, &sa)) |
| check_ai ("AF_INET6", address, scope, &sa, &addr, expected); |
| else |
| { |
| support_record_failure (); |
| printf ("error: unexpected getaddrinfo failure for %s%%%s (AF_INET6)\n", |
| address, scope); |
| } |
| } |
| |
| static int |
| do_test (void) |
| { |
| setup_interface (); |
| |
| static const char *test_addresses[] |
| = { "::", "::1", "2001:db8::1", NULL }; |
| for (int i = 0; test_addresses[i] != NULL; ++i) |
| { |
| expect_success (test_addresses[i], "0", 0); |
| expect_success (test_addresses[i], "5555", 5555); |
| |
| expect_failure (test_addresses[i], ""); |
| expect_failure (test_addresses[i], "-1"); |
| expect_failure (test_addresses[i], "-99"); |
| expect_failure (test_addresses[i], "037777777777"); |
| expect_failure (test_addresses[i], "0x"); |
| expect_failure (test_addresses[i], "0x1"); |
| } |
| |
| if (interface_name != NULL) |
| { |
| expect_success ("fe80::1", interface_name, interface_index); |
| expect_success ("ff02::1", interface_name, interface_index); |
| expect_success ("ff01::1", interface_name, interface_index); |
| expect_failure ("::", interface_name); |
| expect_failure ("::1", interface_name); |
| expect_failure ("2001:db8::1", interface_name); |
| } |
| |
| return 0; |
| } |
| |
| #include <support/test-driver.c> |