1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) 2021, BELLSOFT. All rights reserved. 4 * Copyright (c) 2015 Fujitsu Ltd. 5 * Copyright (c) International Business Machines Corp., 2001 6 * 7 * Author: David L Stevens 8 */ 9 10/*\ 11 * [Description] 12 * 13 * Basic getaddrinfo() tests. 14 * 15 * The test adds LTP specific addresses and names to /etc/hosts to avoid 16 * DNS, hostname setup issues and conflicts with existing configuration. 17 */ 18 19#include <unistd.h> 20#include <errno.h> 21#include <stdlib.h> 22 23#include <sys/socket.h> 24#include <netdb.h> 25#include <arpa/inet.h> 26#include <sys/param.h> 27 28#include "tst_safe_stdio.h" 29#include "tst_test.h" 30#include "tst_safe_net.h" 31 32#ifndef AI_V4MAPPED 33# define AI_V4MAPPED 0x0008 /* IPv4 mapped addresses are acceptable. */ 34#endif 35 36static const char *const host_file = "/etc/hosts"; 37static const char *hostname; 38static const char *shortname; 39static sa_family_t family; 40static int host_file_changed; 41 42static void verify_res(struct addrinfo *res, int sock_type, in_port_t servnum, 43 int (*test_cb)(struct addrinfo *)) 44{ 45 sa_family_t sin_family = 0; 46 in_port_t sin_port = 0; 47 struct addrinfo *p = res; 48 int got_tcp = 0; 49 int got_udp = 0; 50 int ret = 0; 51 52 size_t exp_addrlen = (family == AF_INET) ? sizeof(struct sockaddr_in) : 53 sizeof(struct sockaddr_in6); 54 55 for (; p; p = p->ai_next) { 56 ret |= p->ai_family != family; 57 ret |= p->ai_addrlen != exp_addrlen; 58 ret |= p->ai_addr == 0; 59 got_tcp |= p->ai_socktype == SOCK_STREAM; 60 got_udp |= p->ai_socktype == SOCK_DGRAM; 61 62 if (p->ai_addr) { 63 64 if (test_cb) 65 ret |= test_cb(p); 66 67 if (p->ai_family == AF_INET) { 68 struct sockaddr_in *psin; 69 70 psin = (struct sockaddr_in *)p->ai_addr; 71 sin_family = psin->sin_family; 72 sin_port = psin->sin_port; 73 } else { 74 struct sockaddr_in6 *psin6; 75 76 psin6 = (struct sockaddr_in6 *)p->ai_addr; 77 sin_family = psin6->sin6_family; 78 sin_port = psin6->sin6_port; 79 } 80 81 ret |= sin_family != family; 82 ret |= sin_port != htons(servnum); 83 } 84 85 if (ret) 86 break; 87 } 88 89 if (!sock_type && (!got_tcp || !got_udp)) { 90 tst_brk(TFAIL, "socktype 0,%d TCP %d UDP %d", 91 htons(sin_port), got_tcp, got_udp); 92 } 93 94 if (ret) { 95 tst_brk(TFAIL, "family %d alen %d sin family %d port %d", 96 p->ai_family, p->ai_addrlen, sin_family, 97 htons(sin_port)); 98 } 99} 100 101static void print_test_family(const char *name) 102{ 103 tst_res(TINFO, "test %s: %s", (family == AF_INET) ? "IPv4" : "IPv6", 104 name); 105} 106 107static void check_addrinfo(int safe, const char *name, const char *host, 108 in_port_t servnum, const char *service, 109 int flags, int type, int proto, 110 int (*test_cb)(struct addrinfo *)) 111{ 112 struct addrinfo *res = NULL; 113 struct addrinfo hints; 114 115 print_test_family(name); 116 117 memset(&hints, 0, sizeof(hints)); 118 hints.ai_family = family; 119 hints.ai_flags = flags; 120 hints.ai_socktype = type; 121 hints.ai_protocol = proto; 122 123 if (safe) 124 SAFE_GETADDRINFO(host, service, &hints, &res); 125 else 126 TEST(getaddrinfo(host, service, &hints, &res)); 127 128 if (res) { 129 verify_res(res, type, servnum, test_cb); 130 freeaddrinfo(res); 131 tst_res(TPASS, "%s", name); 132 } 133} 134 135static void check_addrinfo_name(const char *name) 136{ 137 struct addrinfo *p, *res; 138 struct addrinfo hints; 139 140 print_test_family(name); 141 142 memset(&hints, 0, sizeof(hints)); 143 hints.ai_family = family; 144 hints.ai_flags = AI_CANONNAME; 145 146 SAFE_GETADDRINFO(shortname, 0, &hints, &res); 147 148 for (p = res; p; p = p->ai_next) { 149 if (p->ai_canonname) 150 break; 151 } 152 if (!p) 153 tst_brk(TFAIL, "%s: no entries with canonical name set", name); 154 else if (strcasecmp(hostname, p->ai_canonname)) 155 tst_brk(TFAIL, "%s: ai_canonname '%s' doesn't match hostname '%s'", 156 name, p->ai_canonname, hostname); 157 158 tst_res(TPASS, "%s: ai_canonname '%s'", name, p->ai_canonname); 159 freeaddrinfo(res); 160} 161 162static void check_addrinfo_badflags(const char *name) 163{ 164 if (TST_RET == EAI_BADFLAGS) { 165 tst_res(TPASS, "%s returns %ld '%s'", name, 166 TST_RET, gai_strerror(TST_RET)); 167 } else if (TST_RET) { 168 tst_brk(TFAIL, "%s returns %ld '%s'", name, 169 TST_RET, gai_strerror(TST_RET)); 170 } 171} 172 173static int test_loopback(struct addrinfo *p) 174{ 175 /* hostname not set; addr should be loopback */ 176 if (family == AF_INET) { 177 struct sockaddr_in *psin = (struct sockaddr_in *)p->ai_addr; 178 179 return psin->sin_addr.s_addr != htonl(INADDR_LOOPBACK); 180 } else { 181 struct sockaddr_in6 *psin6 = (struct sockaddr_in6 *)p->ai_addr; 182 183 return memcmp(&psin6->sin6_addr, &in6addr_loopback, 184 sizeof(struct in6_addr)) != 0; 185 } 186} 187 188static int test_passive(struct addrinfo *p) 189{ 190 if (family == AF_INET) { 191 struct sockaddr_in *psin = (struct sockaddr_in *)p->ai_addr; 192 193 return psin->sin_addr.s_addr == 0; 194 } else { 195 struct sockaddr_in6 *psin6 = (struct sockaddr_in6 *)p->ai_addr; 196 197 return memcmp(&psin6->sin6_addr, &in6addr_any, 198 sizeof(struct in6_addr)) == 0; 199 } 200} 201 202static int test_passive_no_host(struct addrinfo *p) 203{ 204 if (family == AF_INET) { 205 struct sockaddr_in *psin = (struct sockaddr_in *)p->ai_addr; 206 207 return psin->sin_addr.s_addr != 0; 208 } else { 209 struct sockaddr_in6 *psin6 = (struct sockaddr_in6 *)p->ai_addr; 210 211 return memcmp(&psin6->sin6_addr, &in6addr_any, 212 sizeof(struct in6_addr)); 213 } 214} 215 216static void gaiv(void) 217{ 218 check_addrinfo(1, "basic lookup", hostname, 0, NULL, 0, 0, 0, NULL); 219 check_addrinfo_name("canonical name"); 220 221 /* 222 * These are hard-coded for echo/7 to avoid using getservbyname(), 223 * since it isn't thread-safe and these tests may be re-used 224 * multithreaded. Sigh. 225 */ 226 check_addrinfo(1, "host+service", hostname, 7, "echo", 0, 0, 0, NULL); 227 228 check_addrinfo(1, "host+service, AI_PASSIVE", hostname, 9462, "9462", 229 AI_PASSIVE, SOCK_STREAM, 0, test_passive); 230 231 check_addrinfo(0, "host+service, AI_NUMERICHOST", hostname, 7, "echo", 232 AI_NUMERICHOST, SOCK_STREAM, 0, NULL); 233 if (TST_RET != EAI_NONAME) 234 tst_brk(TFAIL, "AI_NUMERICHOST: ret %ld exp %d (EAI_NONAME)", 235 TST_RET, EAI_NONAME); 236 tst_res(TPASS, "AI_NUMERICHOST: expected %ld (EAI_NONAME)", TST_RET); 237 238 check_addrinfo(1, "0+service, AI_PASSIVE", NULL, 9462, "9462", 239 AI_PASSIVE, SOCK_STREAM, 0, test_passive_no_host); 240 241 check_addrinfo(0, "0+service", NULL, 9462, "9462", 242 0, SOCK_STREAM, 0, test_loopback); 243 check_addrinfo_badflags("0+service ('', '9462')"); 244 245#ifdef AI_NUMERICSERV 246 check_addrinfo(0, "host+service, AI_NUMERICSERV", hostname, 7, "echo", 247 AI_NUMERICSERV, 0, 0, NULL); 248 if (TST_RET != EAI_NONAME) 249 tst_brk(TFAIL, "AI_NUMERICSERV: returns %ld '%s', expected %d (EAI_NONAME)", 250 TST_RET, gai_strerror(TST_RET), EAI_NONAME); 251 tst_res(TPASS, "AI_NUMERICSERV: returns %ld (EAI_NONAME)", TST_RET); 252#else 253 tst_res(TCONF, "AI_NUMERICSERV: flag not implemented"); 254#endif 255 256 check_addrinfo(0, "SOCK_STREAM/IPPROTO_UDP", NULL, 0, NULL, 0, 257 SOCK_STREAM, IPPROTO_UDP, NULL); 258 if (!TST_RET) 259 tst_brk(TFAIL, "SOCK_STREAM/IPPROTO_UDP: unexpected pass"); 260 tst_res(TPASS, "SOCK_STREAM/IPPROTO_UDP: failed as expected"); 261 262 check_addrinfo(0, "socktype 0,513", NULL, 513, "513", 0, 0, 0, NULL); 263 check_addrinfo_badflags("socktype 0,513"); 264 265 check_addrinfo(1, "AI_V4MAPPED", NULL, 513, "513", 266 AI_V4MAPPED, 0, 0, NULL); 267} 268 269static struct tcase { 270 sa_family_t family; 271 const char *const addr; 272 const char *const name; 273 const char *const alias; 274} tcases[] = { 275 { AF_INET, "127.0.127.1", "getaddrinfo01.ltp", "getaddrinfo01-ipv4" }, 276 { AF_INET6, "::127", "getaddrinfo01.ipv6.ltp", "getaddrinfo01-ipv6" } 277}; 278 279static void setup(void) 280{ 281 unsigned int i; 282 int fd; 283 284 if (access(host_file, W_OK)) 285 tst_brk(TCONF | TERRNO, "%s file not available", host_file); 286 287 SAFE_CP(host_file, "hosts"); 288 289 host_file_changed = 1; 290 fd = SAFE_OPEN(host_file, O_WRONLY|O_APPEND); 291 292 for (i = 0; i < ARRAY_SIZE(tcases); ++i) { 293 char *entry; 294 295 SAFE_ASPRINTF(&entry, "%s %s %s\n", 296 tcases[i].addr, tcases[i].name, tcases[i].alias); 297 SAFE_WRITE(SAFE_WRITE_ANY, fd, entry, strlen(entry)); 298 free(entry); 299 } 300 SAFE_CLOSE(fd); 301} 302 303static void cleanup(void) 304{ 305 if (host_file_changed) 306 SAFE_CP("hosts", host_file); 307} 308 309static void do_test(unsigned int i) 310{ 311 family = tcases[i].family; 312 hostname = tcases[i].name; 313 shortname = tcases[i].alias; 314 gaiv(); 315} 316 317static struct tst_test test = { 318 .needs_root = 1, 319 .needs_tmpdir = 1, 320 .setup = setup, 321 .cleanup = cleanup, 322 .tcnt = ARRAY_SIZE(tcases), 323 .test = do_test, 324}; 325