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 <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 53enum method { 54 METHOD_GETADDRINFO, 55 METHOD_GETHOSTBY, 56 METHOD_GETIPNODEBY 57}; 58 59static StringList *hosts = NULL; 60static int *ask = NULL; 61static int *got = NULL; 62 63static void load(const char *); 64static void resolvone(int, enum method); 65static void *resolvloop(void *); 66static void run(int *, enum method); 67 68static pthread_mutex_t stats = PTHREAD_MUTEX_INITIALIZER; 69 70static void 71load(const char *fname) 72{ 73 FILE *fp; 74 size_t len; 75 char *line; 76 77 if ((fp = fopen(fname, "r")) == NULL) 78 ATF_REQUIRE(fp != NULL); 79 while ((line = fgetln(fp, &len)) != NULL) { 80 char c = line[len - 1]; 81 char *ptr; 82 line[len - 1] = '\0'; 83 for (ptr = strtok(line, WS); ptr; ptr = strtok(NULL, WS)) { 84 if (ptr == '\0' || ptr[0] == '#') 85 continue; 86 sl_add(hosts, strdup(ptr)); 87 } 88 line[len - 1] = c; 89 } 90 91 (void)fclose(fp); 92} 93 94static int 95resolv_getaddrinfo(pthread_t self, char *host, int port) 96{ 97 char portstr[6], buf[1024], hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; 98 struct addrinfo hints, *res; 99 int error, len; 100 101 snprintf(portstr, sizeof(portstr), "%d", port); 102 memset(&hints, 0, sizeof(hints)); 103 hints.ai_family = AF_UNSPEC; 104 hints.ai_flags = AI_PASSIVE; 105 hints.ai_socktype = SOCK_STREAM; 106 error = getaddrinfo(host, portstr, &hints, &res); 107 len = snprintf(buf, sizeof(buf), "%p: host %s %s\n", 108 self, host, error ? "not found" : "ok"); 109 (void)write(STDOUT_FILENO, buf, len); 110 if (error == 0) { 111 memset(hbuf, 0, sizeof(hbuf)); 112 memset(pbuf, 0, sizeof(pbuf)); 113 getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf), 114 pbuf, sizeof(pbuf), 0); 115 len = snprintf(buf, sizeof(buf), 116 "%p: reverse %s %s\n", self, hbuf, pbuf); 117 (void)write(STDOUT_FILENO, buf, len); 118 } 119 if (error == 0) 120 freeaddrinfo(res); 121 return error; 122} 123 124static int 125resolv_gethostby(pthread_t self, char *host) 126{ 127 char buf[1024]; 128 struct hostent *hp, *hp2; 129 int len; 130 131 hp = gethostbyname(host); 132 len = snprintf(buf, sizeof(buf), "%p: host %s %s\n", 133 self, host, (hp == NULL) ? "not found" : "ok"); 134 (void)write(STDOUT_FILENO, buf, len); 135 if (hp) { 136 memcpy(buf, hp->h_addr, hp->h_length); 137 hp2 = gethostbyaddr(buf, hp->h_length, hp->h_addrtype); 138 if (hp2) { 139 len = snprintf(buf, sizeof(buf), 140 "%p: reverse %s\n", self, hp2->h_name); 141 (void)write(STDOUT_FILENO, buf, len); 142 } 143 } 144 return hp ? 0 : -1; 145} 146 147static int 148resolv_getipnodeby(pthread_t self, char *host) 149{ 150 char buf[1024]; 151 struct hostent *hp, *hp2; 152 int len, h_error; 153 154 hp = getipnodebyname(host, AF_INET, 0, &h_error); 155 len = snprintf(buf, sizeof(buf), "%p: host %s %s\n", 156 self, host, (hp == NULL) ? "not found" : "ok"); 157 (void)write(STDOUT_FILENO, buf, len); 158 if (hp) { 159 memcpy(buf, hp->h_addr, hp->h_length); 160 hp2 = getipnodebyaddr(buf, hp->h_length, hp->h_addrtype, 161 &h_error); 162 if (hp2) { 163 len = snprintf(buf, sizeof(buf), 164 "%p: reverse %s\n", self, hp2->h_name); 165 (void)write(STDOUT_FILENO, buf, len); 166 } 167 if (hp2) 168 freehostent(hp2); 169 } 170 if (hp) 171 freehostent(hp); 172 return hp ? 0 : -1; 173} 174 175static void 176resolvone(int n, enum method method) 177{ 178 char buf[1024]; 179 pthread_t self = pthread_self(); 180 size_t i = (random() & 0x0fffffff) % hosts->sl_cur; 181 char *host = hosts->sl_str[i]; 182 int error, len; 183 184 len = snprintf(buf, sizeof(buf), "%p: %d resolving %s %d\n", 185 self, n, host, (int)i); 186 (void)write(STDOUT_FILENO, buf, len); 187 error = 0; 188 switch (method) { 189 case METHOD_GETADDRINFO: 190 error = resolv_getaddrinfo(self, host, i); 191 break; 192 case METHOD_GETHOSTBY: 193 error = resolv_gethostby(self, host); 194 break; 195 case METHOD_GETIPNODEBY: 196 error = resolv_getipnodeby(self, host); 197 break; 198 default: 199 /* UNREACHABLE */ 200 /* XXX Needs an __assert_unreachable() for userland. */ 201 assert(0 && "Unreachable segment reached"); 202 abort(); 203 break; 204 } 205 pthread_mutex_lock(&stats); 206 ask[i]++; 207 got[i] += error == 0; 208 pthread_mutex_unlock(&stats); 209} 210 211struct resolvloop_args { 212 int *nhosts; 213 enum method method; 214}; 215 216static void * 217resolvloop(void *p) 218{ 219 struct resolvloop_args *args = p; 220 221 if (*args->nhosts == 0) { 222 free(args); 223 return NULL; 224 } 225 226 do 227 resolvone(*args->nhosts, args->method); 228 while (--(*args->nhosts)); 229 free(args); 230 return NULL; 231} 232 233static void 234run(int *nhosts, enum method method) 235{ 236 pthread_t self; 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 self = pthread_self(); 247 rc = pthread_create(&self, NULL, resolvloop, args); 248 ATF_REQUIRE_MSG(rc == 0, "pthread_create failed: %s", strerror(rc)); 249} 250 251static int 252run_tests(const char *hostlist_file, enum method method) 253{ 254 size_t nthreads = NTHREADS; 255 size_t nhosts = NHOSTS; 256 size_t i; 257 int c, done, *nleft; 258 hosts = sl_init(); 259 260 srandom(1234); 261 262 load(hostlist_file); 263 264 ATF_REQUIRE_MSG(0 < hosts->sl_cur, "0 hosts in %s", hostlist_file); 265 266 nleft = malloc(nthreads * sizeof(int)); 267 ATF_REQUIRE(nleft != NULL); 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 for (i = 0; i < nthreads; i++) { 276 nleft[i] = nhosts; 277 run(&nleft[i], method); 278 } 279 280 for (done = 0; !done;) { 281 done = 1; 282 for (i = 0; i < nthreads; i++) { 283 if (nleft[i] != 0) { 284 done = 0; 285 break; 286 } 287 } 288 sleep(1); 289 } 290 c = 0; 291 for (i = 0; i < hosts->sl_cur; i++) { 292 if (ask[i] != got[i] && got[i] != 0) { 293 printf("Error: host %s ask %d got %d\n", 294 hosts->sl_str[i], ask[i], got[i]); 295 c++; 296 } 297 } 298 free(nleft); 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) \ 308do { \ 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 315ATF_TC(getaddrinfo_test); 316ATF_TC_HEAD(getaddrinfo_test, tc) { 317 atf_tc_set_md_var(tc, "timeout", "1200"); 318} 319ATF_TC_BODY(getaddrinfo_test, tc) 320{ 321 322 RUN_TESTS(tc, METHOD_GETADDRINFO); 323} 324 325ATF_TC(gethostby_test); 326ATF_TC_HEAD(gethostby_test, tc) { 327 atf_tc_set_md_var(tc, "timeout", "1200"); 328} 329ATF_TC_BODY(gethostby_test, tc) 330{ 331 332 RUN_TESTS(tc, METHOD_GETHOSTBY); 333} 334 335ATF_TC(getipnodeby_test); 336ATF_TC_HEAD(getipnodeby_test, tc) { 337 338 atf_tc_set_md_var(tc, "timeout", "1200"); 339} 340ATF_TC_BODY(getipnodeby_test, tc) 341{ 342 343 RUN_TESTS(tc, METHOD_GETIPNODEBY); 344} 345 346ATF_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