1 /* $NetBSD: resolv.c,v 1.6 2004/05/23 16:59:11 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2004 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Christos Zoulas. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 /* $FreeBSD$ */ 32 #include <sys/cdefs.h> 33 __RCSID("$NetBSD: resolv.c,v 1.6 2004/05/23 16:59:11 christos Exp $"); 34 35 #include <sys/types.h> 36 #include <sys/socket.h> 37 #include <assert.h> 38 #include <errno.h> 39 #include <pthread.h> 40 #include <stdio.h> 41 #include <stdatomic.h> 42 #include <netdb.h> 43 #include <stdlib.h> 44 #include <unistd.h> 45 #include <string.h> 46 #include <stringlist.h> 47 48 #include <atf-c.h> 49 50 #define NTHREADS 10 51 #define NHOSTS 100 52 #define WS " \t\n\r" 53 54 enum method { 55 METHOD_GETADDRINFO, 56 METHOD_GETHOSTBY, 57 METHOD_GETIPNODEBY 58 }; 59 60 static StringList *hosts = NULL; 61 static _Atomic(int) *ask = NULL; 62 static _Atomic(int) *got = NULL; 63 static bool debug_output = 0; 64 65 static void load(const char *); 66 static void resolvone(long, int, enum method); 67 static void *resolvloop(void *); 68 static pthread_t run(int, enum method, long); 69 70 #define DBG(...) do { \ 71 if (debug_output) \ 72 dprintf(STDOUT_FILENO, __VA_ARGS__); \ 73 } while (0) 74 75 static void 76 load(const char *fname) 77 { 78 FILE *fp; 79 size_t linecap; 80 char *line; 81 82 fp = fopen(fname, "r"); 83 ATF_REQUIRE(fp != NULL); 84 line = NULL; 85 linecap = 0; 86 while (getline(&line, &linecap, fp) >= 0) { 87 char *ptr; 88 89 for (ptr = strtok(line, WS); ptr; ptr = strtok(NULL, WS)) { 90 if (ptr[0] == '#') 91 break; 92 sl_add(hosts, strdup(ptr)); 93 } 94 } 95 free(line); 96 97 (void)fclose(fp); 98 } 99 100 static int 101 resolv_getaddrinfo(long threadnum, char *host, int port, const char **errstr) 102 { 103 char portstr[6], hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; 104 struct addrinfo hints, *res; 105 int error; 106 107 snprintf(portstr, sizeof(portstr), "%d", port); 108 memset(&hints, 0, sizeof(hints)); 109 hints.ai_family = AF_UNSPEC; 110 hints.ai_flags = AI_PASSIVE; 111 hints.ai_socktype = SOCK_STREAM; 112 error = getaddrinfo(host, portstr, &hints, &res); 113 if (error == 0) { 114 DBG("T%ld: host %s ok\n", threadnum, host); 115 memset(hbuf, 0, sizeof(hbuf)); 116 memset(pbuf, 0, sizeof(pbuf)); 117 getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf), 118 pbuf, sizeof(pbuf), 0); 119 DBG("T%ld: reverse %s %s\n", threadnum, hbuf, pbuf); 120 freeaddrinfo(res); 121 } else { 122 *errstr = gai_strerror(error); 123 DBG("T%ld: host %s not found: %s\n", threadnum, host, *errstr); 124 } 125 return error; 126 } 127 128 static int 129 resolv_gethostby(long threadnum, char *host, const char **errstr) 130 { 131 char buf[1024]; 132 struct hostent *hp, *hp2; 133 134 hp = gethostbyname(host); 135 if (hp) { 136 DBG("T%ld: host %s ok\n", threadnum, host); 137 memcpy(buf, hp->h_addr, hp->h_length); 138 hp2 = gethostbyaddr(buf, hp->h_length, hp->h_addrtype); 139 if (hp2) { 140 DBG("T%ld: reverse %s\n", threadnum, hp2->h_name); 141 } 142 } else { 143 *errstr = hstrerror(h_errno); 144 DBG("T%ld: host %s not found: %s\n", threadnum, host, *errstr); 145 } 146 return hp ? 0 : h_errno; 147 } 148 149 static int 150 resolv_getipnodeby(long threadnum, char *host, const char **errstr) 151 { 152 char buf[1024]; 153 struct hostent *hp, *hp2; 154 int error = 0; 155 156 hp = getipnodebyname(host, AF_INET, 0, &error); 157 if (hp) { 158 DBG("T%ld: host %s ok\n", threadnum, host); 159 memcpy(buf, hp->h_addr, hp->h_length); 160 hp2 = getipnodebyaddr(buf, hp->h_length, hp->h_addrtype, 161 &error); 162 if (hp2) { 163 DBG("T%ld: reverse %s\n", threadnum, hp2->h_name); 164 freehostent(hp2); 165 } 166 freehostent(hp); 167 } else { 168 *errstr = hstrerror(error); 169 DBG("T%ld: host %s not found: %s\n", threadnum, host, *errstr); 170 } 171 return hp ? 0 : error; 172 } 173 174 static void 175 resolvone(long threadnum, int n, enum method method) 176 { 177 const char* errstr = NULL; 178 size_t i = (random() & 0x0fffffff) % hosts->sl_cur; 179 char *host = hosts->sl_str[i]; 180 int error; 181 182 DBG("T%ld: %d resolving %s %zd\n", threadnum, n, host, i); 183 switch (method) { 184 case METHOD_GETADDRINFO: 185 error = resolv_getaddrinfo(threadnum, host, i, &errstr); 186 break; 187 case METHOD_GETHOSTBY: 188 error = resolv_gethostby(threadnum, host, &errstr); 189 break; 190 case METHOD_GETIPNODEBY: 191 error = resolv_getipnodeby(threadnum, host, &errstr); 192 break; 193 default: 194 /* UNREACHABLE */ 195 /* XXX Needs an __assert_unreachable() for userland. */ 196 assert(0 && "Unreachable segment reached"); 197 abort(); 198 break; 199 } 200 atomic_fetch_add_explicit(&ask[i], 1, memory_order_relaxed); 201 if (error == 0) 202 atomic_fetch_add_explicit(&got[i], 1, memory_order_relaxed); 203 else if (got[i] != 0) 204 fprintf(stderr, 205 "T%ld ERROR after previous success for %s: %d (%s)\n", 206 threadnum, hosts->sl_str[i], error, errstr); 207 } 208 209 struct resolvloop_args { 210 int nhosts; 211 enum method method; 212 long threadnum; 213 }; 214 215 static void * 216 resolvloop(void *p) 217 { 218 struct resolvloop_args *args = p; 219 int nhosts = args->nhosts; 220 221 if (nhosts == 0) { 222 free(args); 223 return NULL; 224 } 225 226 do { 227 resolvone(args->threadnum, nhosts, args->method); 228 } while (--nhosts); 229 free(args); 230 return (void *)(uintptr_t)nhosts; 231 } 232 233 static pthread_t 234 run(int nhosts, enum method method, long i) 235 { 236 pthread_t t; 237 int rc; 238 struct resolvloop_args *args; 239 240 /* Created thread is responsible for free(). */ 241 args = malloc(sizeof(*args)); 242 ATF_REQUIRE(args != NULL); 243 244 args->nhosts = nhosts; 245 args->method = method; 246 args->threadnum = i + 1; 247 rc = pthread_create(&t, NULL, resolvloop, args); 248 ATF_REQUIRE_MSG(rc == 0, "pthread_create failed: %s", strerror(rc)); 249 return t; 250 } 251 252 static int 253 run_tests(const char *hostlist_file, enum method method) 254 { 255 size_t nthreads = NTHREADS; 256 pthread_t *threads; 257 size_t nhosts = NHOSTS; 258 size_t i; 259 int c; 260 hosts = sl_init(); 261 262 srandom(1234); 263 debug_output = getenv("DEBUG_OUTPUT") != NULL; 264 265 load(hostlist_file); 266 267 ATF_REQUIRE_MSG(0 < hosts->sl_cur, "0 hosts in %s", hostlist_file); 268 269 ask = calloc(hosts->sl_cur, sizeof(int)); 270 ATF_REQUIRE(ask != NULL); 271 272 got = calloc(hosts->sl_cur, sizeof(int)); 273 ATF_REQUIRE(got != NULL); 274 275 threads = calloc(nthreads, sizeof(pthread_t)); 276 ATF_REQUIRE(threads != NULL); 277 278 for (i = 0; i < nthreads; i++) { 279 threads[i] = run(nhosts, method, i); 280 } 281 /* Wait for all threads to join and check that they checked all hosts */ 282 for (i = 0; i < nthreads; i++) { 283 size_t remaining; 284 285 remaining = (uintptr_t)pthread_join(threads[i], NULL); 286 ATF_CHECK_EQ_MSG(0, remaining, 287 "Thread %zd still had %zd hosts to check!", i, remaining); 288 } 289 290 c = 0; 291 for (i = 0; i < hosts->sl_cur; i++) { 292 if (got[i] != 0) { 293 ATF_CHECK_EQ_MSG(ask[i], got[i], 294 "Error: host %s ask %d got %d", hosts->sl_str[i], 295 ask[i], got[i]); 296 c += ask[i] != got[i]; 297 } 298 } 299 free(threads); 300 free(ask); 301 free(got); 302 sl_free(hosts, 1); 303 return c; 304 } 305 306 #define HOSTLIST_FILE "mach" 307 308 #define RUN_TESTS(tc, method) \ 309 do { \ 310 char *_hostlist_file; \ 311 ATF_REQUIRE(0 < asprintf(&_hostlist_file, "%s/%s", \ 312 atf_tc_get_config_var(tc, "srcdir"), HOSTLIST_FILE)); \ 313 ATF_REQUIRE(run_tests(_hostlist_file, method) == 0); \ 314 } while(0) 315 316 ATF_TC(getaddrinfo_test); 317 ATF_TC_HEAD(getaddrinfo_test, tc) { 318 atf_tc_set_md_var(tc, "timeout", "1200"); 319 } 320 ATF_TC_BODY(getaddrinfo_test, tc) 321 { 322 323 RUN_TESTS(tc, METHOD_GETADDRINFO); 324 } 325 326 ATF_TC(gethostby_test); 327 ATF_TC_HEAD(gethostby_test, tc) { 328 atf_tc_set_md_var(tc, "timeout", "1200"); 329 } 330 ATF_TC_BODY(gethostby_test, tc) 331 { 332 333 RUN_TESTS(tc, METHOD_GETHOSTBY); 334 } 335 336 ATF_TC(getipnodeby_test); 337 ATF_TC_HEAD(getipnodeby_test, tc) { 338 339 atf_tc_set_md_var(tc, "timeout", "1200"); 340 } 341 ATF_TC_BODY(getipnodeby_test, tc) 342 { 343 344 RUN_TESTS(tc, METHOD_GETIPNODEBY); 345 } 346 347 ATF_TP_ADD_TCS(tp) 348 { 349 350 ATF_TP_ADD_TC(tp, getaddrinfo_test); 351 ATF_TP_ADD_TC(tp, gethostby_test); 352 ATF_TP_ADD_TC(tp, getipnodeby_test); 353 354 return (atf_no_error()); 355 } 356