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 #include <sys/cdefs.h> 32 __RCSID("$NetBSD: resolv.c,v 1.6 2004/05/23 16:59:11 christos Exp $"); 33 34 #include <sys/types.h> 35 #include <sys/socket.h> 36 #include <assert.h> 37 #include <errno.h> 38 #include <pthread.h> 39 #include <stdio.h> 40 #include <stdatomic.h> 41 #include <netdb.h> 42 #include <stdlib.h> 43 #include <unistd.h> 44 #include <string.h> 45 #include <stringlist.h> 46 47 #include <atf-c.h> 48 49 #define NTHREADS 10 50 #define NHOSTS 100 51 #define WS " \t\n\r" 52 53 enum method { 54 METHOD_GETADDRINFO, 55 METHOD_GETHOSTBY, 56 METHOD_GETIPNODEBY 57 }; 58 59 static StringList *hosts = NULL; 60 static _Atomic(int) *ask = NULL; 61 static _Atomic(int) *got = NULL; 62 static bool debug_output = 0; 63 64 static void load(const char *); 65 static void resolvone(long, int, enum method); 66 static void *resolvloop(void *); 67 static pthread_t run(int, enum method, long); 68 69 #define DBG(...) do { \ 70 if (debug_output) \ 71 dprintf(STDOUT_FILENO, __VA_ARGS__); \ 72 } while (0) 73 74 static void 75 load(const char *fname) 76 { 77 FILE *fp; 78 size_t linecap; 79 char *line; 80 81 fp = fopen(fname, "r"); 82 ATF_REQUIRE(fp != NULL); 83 line = NULL; 84 linecap = 0; 85 while (getline(&line, &linecap, fp) >= 0) { 86 char *ptr; 87 88 for (ptr = strtok(line, WS); ptr; ptr = strtok(NULL, WS)) { 89 if (ptr[0] == '#') 90 break; 91 sl_add(hosts, strdup(ptr)); 92 } 93 } 94 free(line); 95 96 (void)fclose(fp); 97 } 98 99 static int 100 resolv_getaddrinfo(long threadnum, char *host, int port, const char **errstr) 101 { 102 char portstr[6], hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; 103 struct addrinfo hints, *res; 104 int error; 105 106 snprintf(portstr, sizeof(portstr), "%d", port); 107 memset(&hints, 0, sizeof(hints)); 108 hints.ai_family = AF_UNSPEC; 109 hints.ai_flags = AI_PASSIVE; 110 hints.ai_socktype = SOCK_STREAM; 111 error = getaddrinfo(host, portstr, &hints, &res); 112 if (error == 0) { 113 DBG("T%ld: host %s ok\n", threadnum, host); 114 memset(hbuf, 0, sizeof(hbuf)); 115 memset(pbuf, 0, sizeof(pbuf)); 116 getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf), 117 pbuf, sizeof(pbuf), 0); 118 DBG("T%ld: reverse %s %s\n", threadnum, hbuf, pbuf); 119 freeaddrinfo(res); 120 } else { 121 *errstr = gai_strerror(error); 122 DBG("T%ld: host %s not found: %s\n", threadnum, host, *errstr); 123 } 124 return error; 125 } 126 127 static int 128 resolv_gethostby(long threadnum, char *host, const char **errstr) 129 { 130 char buf[1024]; 131 struct hostent *hp, *hp2; 132 133 hp = gethostbyname(host); 134 if (hp) { 135 DBG("T%ld: host %s ok\n", threadnum, host); 136 memcpy(buf, hp->h_addr, hp->h_length); 137 hp2 = gethostbyaddr(buf, hp->h_length, hp->h_addrtype); 138 if (hp2) { 139 DBG("T%ld: reverse %s\n", threadnum, hp2->h_name); 140 } 141 } else { 142 *errstr = hstrerror(h_errno); 143 DBG("T%ld: host %s not found: %s\n", threadnum, host, *errstr); 144 } 145 return hp ? 0 : h_errno; 146 } 147 148 static int 149 resolv_getipnodeby(long threadnum, char *host, const char **errstr) 150 { 151 char buf[1024]; 152 struct hostent *hp, *hp2; 153 int error = 0; 154 155 hp = getipnodebyname(host, AF_INET, 0, &error); 156 if (hp) { 157 DBG("T%ld: host %s ok\n", threadnum, host); 158 memcpy(buf, hp->h_addr, hp->h_length); 159 hp2 = getipnodebyaddr(buf, hp->h_length, hp->h_addrtype, 160 &error); 161 if (hp2) { 162 DBG("T%ld: reverse %s\n", threadnum, hp2->h_name); 163 freehostent(hp2); 164 } 165 freehostent(hp); 166 } else { 167 *errstr = hstrerror(error); 168 DBG("T%ld: host %s not found: %s\n", threadnum, host, *errstr); 169 } 170 return hp ? 0 : error; 171 } 172 173 static void 174 resolvone(long threadnum, int n, enum method method) 175 { 176 const char* errstr = NULL; 177 size_t i = (random() & 0x0fffffff) % hosts->sl_cur; 178 char *host = hosts->sl_str[i]; 179 int error; 180 181 DBG("T%ld: %d resolving %s %zd\n", threadnum, n, host, i); 182 switch (method) { 183 case METHOD_GETADDRINFO: 184 error = resolv_getaddrinfo(threadnum, host, i, &errstr); 185 break; 186 case METHOD_GETHOSTBY: 187 error = resolv_gethostby(threadnum, host, &errstr); 188 break; 189 case METHOD_GETIPNODEBY: 190 error = resolv_getipnodeby(threadnum, host, &errstr); 191 break; 192 default: 193 /* UNREACHABLE */ 194 /* XXX Needs an __assert_unreachable() for userland. */ 195 assert(0 && "Unreachable segment reached"); 196 abort(); 197 break; 198 } 199 atomic_fetch_add_explicit(&ask[i], 1, memory_order_relaxed); 200 if (error == 0) 201 atomic_fetch_add_explicit(&got[i], 1, memory_order_relaxed); 202 else if (got[i] != 0) 203 fprintf(stderr, 204 "T%ld ERROR after previous success for %s: %d (%s)\n", 205 threadnum, hosts->sl_str[i], error, errstr); 206 } 207 208 struct resolvloop_args { 209 int nhosts; 210 enum method method; 211 long threadnum; 212 }; 213 214 static void * 215 resolvloop(void *p) 216 { 217 struct resolvloop_args *args = p; 218 int nhosts = args->nhosts; 219 220 if (nhosts == 0) { 221 free(args); 222 return NULL; 223 } 224 225 do { 226 resolvone(args->threadnum, nhosts, args->method); 227 } while (--nhosts); 228 free(args); 229 return (void *)(uintptr_t)nhosts; 230 } 231 232 static pthread_t 233 run(int nhosts, enum method method, long i) 234 { 235 pthread_t t; 236 int rc; 237 struct resolvloop_args *args; 238 239 /* Created thread is responsible for free(). */ 240 args = malloc(sizeof(*args)); 241 ATF_REQUIRE(args != NULL); 242 243 args->nhosts = nhosts; 244 args->method = method; 245 args->threadnum = i + 1; 246 rc = pthread_create(&t, NULL, resolvloop, args); 247 ATF_REQUIRE_MSG(rc == 0, "pthread_create failed: %s", strerror(rc)); 248 return t; 249 } 250 251 static int 252 run_tests(const char *hostlist_file, enum method method) 253 { 254 size_t nthreads = NTHREADS; 255 pthread_t *threads; 256 size_t nhosts = NHOSTS; 257 size_t i; 258 int c; 259 hosts = sl_init(); 260 261 srandom(1234); 262 debug_output = getenv("DEBUG_OUTPUT") != NULL; 263 264 load(hostlist_file); 265 266 ATF_REQUIRE_MSG(0 < hosts->sl_cur, "0 hosts in %s", hostlist_file); 267 268 ask = calloc(hosts->sl_cur, sizeof(int)); 269 ATF_REQUIRE(ask != NULL); 270 271 got = calloc(hosts->sl_cur, sizeof(int)); 272 ATF_REQUIRE(got != NULL); 273 274 threads = calloc(nthreads, sizeof(pthread_t)); 275 ATF_REQUIRE(threads != NULL); 276 277 for (i = 0; i < nthreads; i++) { 278 threads[i] = run(nhosts, method, i); 279 } 280 /* Wait for all threads to join and check that they checked all hosts */ 281 for (i = 0; i < nthreads; i++) { 282 size_t remaining; 283 284 remaining = (uintptr_t)pthread_join(threads[i], NULL); 285 ATF_CHECK_EQ_MSG(0, remaining, 286 "Thread %zd still had %zd hosts to check!", i, remaining); 287 } 288 289 c = 0; 290 for (i = 0; i < hosts->sl_cur; i++) { 291 if (got[i] != 0) { 292 ATF_CHECK_EQ_MSG(ask[i], got[i], 293 "Error: host %s ask %d got %d", hosts->sl_str[i], 294 ask[i], got[i]); 295 c += ask[i] != got[i]; 296 } 297 } 298 free(threads); 299 free(ask); 300 free(got); 301 sl_free(hosts, 1); 302 return c; 303 } 304 305 #define HOSTLIST_FILE "mach" 306 307 #define RUN_TESTS(tc, method) \ 308 do { \ 309 char *_hostlist_file; \ 310 ATF_REQUIRE(0 < asprintf(&_hostlist_file, "%s/%s", \ 311 atf_tc_get_config_var(tc, "srcdir"), HOSTLIST_FILE)); \ 312 ATF_REQUIRE(run_tests(_hostlist_file, method) == 0); \ 313 } while(0) 314 315 ATF_TC(getaddrinfo_test); 316 ATF_TC_HEAD(getaddrinfo_test, tc) { 317 atf_tc_set_md_var(tc, "timeout", "1200"); 318 } 319 ATF_TC_BODY(getaddrinfo_test, tc) 320 { 321 322 RUN_TESTS(tc, METHOD_GETADDRINFO); 323 } 324 325 ATF_TC(gethostby_test); 326 ATF_TC_HEAD(gethostby_test, tc) { 327 atf_tc_set_md_var(tc, "timeout", "1200"); 328 } 329 ATF_TC_BODY(gethostby_test, tc) 330 { 331 332 RUN_TESTS(tc, METHOD_GETHOSTBY); 333 } 334 335 ATF_TC(getipnodeby_test); 336 ATF_TC_HEAD(getipnodeby_test, tc) { 337 338 atf_tc_set_md_var(tc, "timeout", "1200"); 339 } 340 ATF_TC_BODY(getipnodeby_test, tc) 341 { 342 343 RUN_TESTS(tc, METHOD_GETIPNODEBY); 344 } 345 346 ATF_TP_ADD_TCS(tp) 347 { 348 349 ATF_TP_ADD_TC(tp, getaddrinfo_test); 350 ATF_TP_ADD_TC(tp, gethostby_test); 351 ATF_TP_ADD_TC(tp, getipnodeby_test); 352 353 return (atf_no_error()); 354 } 355