git: 531345778067 - main - libc/tests: add getaddrinfo test
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 28 Mar 2025 21:37:49 UTC
The branch main has been updated by glebius: URL: https://cgit.FreeBSD.org/src/commit/?id=53134577806796c8c12119c854a30ed4faf5a73e commit 53134577806796c8c12119c854a30ed4faf5a73e Author: Gleb Smirnoff <glebius@FreeBSD.org> AuthorDate: 2025-03-28 21:34:55 +0000 Commit: Gleb Smirnoff <glebius@FreeBSD.org> CommitDate: 2025-03-28 21:36:40 +0000 libc/tests: add getaddrinfo test A test suite for getaddrinfo(3) written in C. Unlike NetBSD test, this one will be mostly focused on what the API should return when something isn't good with your DNS. Test emulates bad DNS servers in resolv.conf intercepting fopen(2) and emulates downed network intercepting send(2). Initial version covers three main scenarios: all good, server(s) timed out, network down. For each scenario we test hostname with trailing dot and without, since libc resolver uses quite different code paths, sometimes even yielding with different error codes. All current error codes in the test are what our libc returns right now, meaning the test documents what we have, not what there should be. Reviewed by: kib Differential Revision: https://reviews.freebsd.org/D49410 --- lib/libc/tests/net/getaddrinfo/Makefile | 1 + lib/libc/tests/net/getaddrinfo/getaddrinfo.c | 267 +++++++++++++++++++++++++++ 2 files changed, 268 insertions(+) diff --git a/lib/libc/tests/net/getaddrinfo/Makefile b/lib/libc/tests/net/getaddrinfo/Makefile index 19c7ccbe0030..1c8d68d8cc25 100644 --- a/lib/libc/tests/net/getaddrinfo/Makefile +++ b/lib/libc/tests/net/getaddrinfo/Makefile @@ -7,6 +7,7 @@ TESTSRC= ${SRCTOP}/contrib/netbsd-tests/lib/libc/net/${.CURDIR:T} BINDIR= ${TESTSDIR} NETBSD_ATF_TESTS_SH= getaddrinfo_test +ATF_TESTS_C= getaddrinfo PROGS= h_gai diff --git a/lib/libc/tests/net/getaddrinfo/getaddrinfo.c b/lib/libc/tests/net/getaddrinfo/getaddrinfo.c new file mode 100644 index 000000000000..9d8575232d2a --- /dev/null +++ b/lib/libc/tests/net/getaddrinfo/getaddrinfo.c @@ -0,0 +1,267 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025 Gleb Smirnoff <glebius@FreeBSD.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <dlfcn.h> +#include <errno.h> +#include <netdb.h> +#include <stdlib.h> +#include <string.h> +#include <resolv.h> + +#include <atf-c.h> + +static const char goodname[] = "www.freebsd.org"; +static const char goodname_dot[] = "www.freebsd.org."; +static const char badname[] = "does-not-exist.freebsd.org"; +static const char badname_dot[] = "does-not-exist.freebsd.org."; +static const char ipv6onlyname[] = "beefy15.nyi.freebsd.org"; +static const char ipv6onlyname_dot[] = "beefy15.nyi.freebsd.org."; +static const char ipv4onlyname[] = "ipv4only.arpa"; +static const char ipv4onlyname_dot[] = "ipv4only.arpa."; +/* + * We need an IP address that doesn't exist, but not reported with ICMP + * unreachable by the nearest router. Let's try TEST-NET-3. + */ +static char badresolvconf[] = "nameserver 203.0.113.1"; +static char badresolvconf2[] = "nameserver 203.0.113.1\n" + "nameserver 203.0.113.2"; +static char *resconf = NULL; +FILE * +fopen(const char * restrict path, const char * restrict mode) +{ + static FILE *(*orig)(const char *, const char *); + + if (orig == NULL && (orig = dlsym(RTLD_NEXT, "fopen")) == NULL) + atf_libc_error(ENOENT, "dlsym(fopen): %s", dlerror()); + if (resconf != NULL && strcmp(path, _PATH_RESCONF) == 0) + return (fmemopen(resconf, strlen(resconf), mode)); + else + return (orig(path, mode)); +} + +static int send_error = 0; +ssize_t +send(int s, const void *msg, size_t len, int flags) +{ + static ssize_t (*orig)(int, const void *, size_t, int); + + if (orig == NULL && (orig = dlsym(RTLD_NEXT, "send")) == NULL) + atf_libc_error(ENOENT, "dlsym(send): %s", dlerror()); + if (send_error != 0) { + errno = send_error; + return (-1); + } else { + return (orig(s, msg, len, flags)); + } +} + +ATF_TC_WITHOUT_HEAD(basic); +ATF_TC_BODY(basic, tc) +{ + static const struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_flags = AI_CANONNAME, + }; + struct addrinfo *res; + int rv; + + rv = getaddrinfo(goodname, NULL, &hints, &res); + ATF_REQUIRE_MSG(rv == 0, + "Expected 0, got %d (%s)", rv, gai_strerror(rv)); + freeaddrinfo(res); + + rv = getaddrinfo(goodname_dot, NULL, &hints, &res); + ATF_REQUIRE_MSG(rv == 0, + "Expected 0, got %d (%s)", rv, gai_strerror(rv)); + freeaddrinfo(res); + + rv = getaddrinfo(badname, NULL, &hints, &res); + ATF_REQUIRE_MSG(rv == EAI_NONAME, + "Expected %d (EAI_NONAME), got %d (%s)", + EAI_NONAME, rv, gai_strerror(rv)); + + rv = getaddrinfo(badname_dot, NULL, &hints, &res); + ATF_REQUIRE_MSG(rv == EAI_NONAME, + "Expected %d (EAI_NONAME), got %d (%s)", + EAI_NONAME, rv, gai_strerror(rv)); +} + +ATF_TC_WITHOUT_HEAD(timeout); +ATF_TC_BODY(timeout, tc) +{ + static const struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_flags = AI_CANONNAME, + }; + struct addrinfo *res; + int rv; + + resconf = badresolvconf; + rv = getaddrinfo(goodname, NULL, &hints, &res); + /* + * XXXGL: EAI_ADDRFAMILY is most likely a regression from 144361386696. + * Error code on timeout used to be EAI_NONAME for many years and IMHO + * this is not correct either. Should be EAI_AGAIN. + */ + ATF_REQUIRE_MSG(rv == EAI_ADDRFAMILY, + "Expected %d (EAI_ADDRFAMILY), got %d (%s)", + EAI_ADDRFAMILY, rv, gai_strerror(rv)); + + rv = getaddrinfo(goodname_dot, NULL, &hints, &res); + ATF_REQUIRE_MSG(rv == EAI_ADDRFAMILY, + "Expected %d (EAI_ADDRFAMILY), got %d (%s)", + EAI_ADDRFAMILY, rv, gai_strerror(rv)); +} + +ATF_TC_WITHOUT_HEAD(timeout_specific); +ATF_TC_BODY(timeout_specific, tc) +{ + static const struct addrinfo hints = { + .ai_family = AF_INET, + .ai_socktype = SOCK_STREAM, + .ai_flags = AI_CANONNAME, + }; + struct addrinfo *res; + int rv; + + resconf = badresolvconf; + rv = getaddrinfo(goodname, "666", &hints, &res); + ATF_REQUIRE_MSG(rv == EAI_ADDRFAMILY, + "Expected %d (EAI_ADDRFAMILY), got %d (%s)", + EAI_ADDRFAMILY, rv, gai_strerror(rv)); + + rv = getaddrinfo(goodname_dot, "666", &hints, &res); + ATF_REQUIRE_MSG(rv == EAI_ADDRFAMILY, + "Expected %d (EAI_ADDRFAMILY), got %d (%s)", + EAI_ADDRFAMILY, rv, gai_strerror(rv)); +} + +ATF_TC_WITHOUT_HEAD(timeout2); +ATF_TC_BODY(timeout2, tc) +{ + static const struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_flags = AI_CANONNAME, + }; + struct addrinfo *res; + int rv; + + resconf = badresolvconf2; + rv = getaddrinfo(goodname, NULL, &hints, &res); + ATF_REQUIRE_MSG(rv == EAI_ADDRFAMILY, + "Expected %d (EAI_ADDRFAMILY), got %d (%s)", + EAI_ADDRFAMILY, rv, gai_strerror(rv)); + + rv = getaddrinfo(goodname_dot, NULL, &hints, &res); + ATF_REQUIRE_MSG(rv == EAI_ADDRFAMILY, + "Expected %d (EAI_ADDRFAMILY), got %d (%s)", + EAI_ADDRFAMILY, rv, gai_strerror(rv)); +} + +/* + * Emulate interface/network down. + */ +ATF_TC_WITHOUT_HEAD(netdown); +ATF_TC_BODY(netdown, tc) +{ + static const struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_flags = AI_CANONNAME, + }; + struct addrinfo *res; + int rv; + + send_error = ENETDOWN; + rv = getaddrinfo(goodname, NULL, &hints, &res); + ATF_REQUIRE_MSG(rv == EAI_NONAME, + "Expected %d (EAI_NONAME), got %d (%s)", + EAI_NONAME, rv, gai_strerror(rv)); + + rv = getaddrinfo(goodname_dot, NULL, &hints, &res); + ATF_REQUIRE_MSG(rv == EAI_ADDRFAMILY, + "Expected %d (EAI_ADDRFAMILY), got %d (%s)", + EAI_ADDRFAMILY, rv, gai_strerror(rv)); +} + +/* + * See https://reviews.freebsd.org/D37139. + */ +ATF_TC_WITHOUT_HEAD(nofamily); +ATF_TC_BODY(nofamily, tc) +{ + static const struct addrinfo hints4 = { + .ai_family = AF_INET, + .ai_flags = AI_CANONNAME, + }, hints6 = { + .ai_family = AF_INET6, + .ai_flags = AI_CANONNAME, + }; + struct addrinfo *res; + int rv; + + rv = getaddrinfo(ipv6onlyname, NULL, &hints4, &res); + ATF_REQUIRE_MSG(rv == EAI_ADDRFAMILY, + "Expected %d (EAI_ADDRFAMILY), got %d (%s)", + EAI_ADDRFAMILY, rv, gai_strerror(rv)); + + rv = getaddrinfo(ipv6onlyname_dot, NULL, &hints4, &res); + ATF_REQUIRE_MSG(rv == EAI_ADDRFAMILY, + "Expected %d (EAI_ADDRFAMILY), got %d (%s)", + EAI_ADDRFAMILY, rv, gai_strerror(rv)); + + rv = getaddrinfo(ipv4onlyname, NULL, &hints6, &res); + ATF_REQUIRE_MSG(rv == EAI_ADDRFAMILY, + "Expected %d (EAI_ADDRFAMILY), got %d (%s)", + EAI_ADDRFAMILY, rv, gai_strerror(rv)); + + rv = getaddrinfo(ipv4onlyname_dot, NULL, &hints6, &res); + ATF_REQUIRE_MSG(rv == EAI_ADDRFAMILY, + "Expected %d (EAI_ADDRFAMILY), got %d (%s)", + EAI_ADDRFAMILY, rv, gai_strerror(rv)); + + rv = getaddrinfo(badname, NULL, &hints4, &res); + ATF_REQUIRE_MSG(rv == EAI_NONAME, + "Expected %d (EAI_NONAME), got %d (%s)", + EAI_NONAME, rv, gai_strerror(rv)); + + rv = getaddrinfo(badname_dot, NULL, &hints6, &res); + ATF_REQUIRE_MSG(rv == EAI_NONAME, + "Expected %d (EAI_NONAME), got %d (%s)", + EAI_NONAME, rv, gai_strerror(rv)); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, basic); + ATF_TP_ADD_TC(tp, timeout); + ATF_TP_ADD_TC(tp, timeout_specific); + ATF_TP_ADD_TC(tp, timeout2); + ATF_TP_ADD_TC(tp, netdown); + ATF_TP_ADD_TC(tp, nofamily); + + return (atf_no_error()); +}