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