1 /*
2 * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2008, 2009, 2010, 2011, 2012,
3 * 2013, 2016, 2017, 2019, 2020
4 * Inferno Nettverk A/S, Norway. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. The above copyright notice, this list of conditions and the following
10 * disclaimer must appear in all copies of the software, derivative works
11 * or modified versions, and any portions thereof, aswell as in all
12 * supporting documentation.
13 * 2. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by
16 * Inferno Nettverk A/S, Norway.
17 * 3. The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 * Inferno Nettverk A/S requests users of this software to return to
32 *
33 * Software Distribution Coordinator or sdc@inet.no
34 * Inferno Nettverk A/S
35 * Oslo Research Park
36 * Gaustadall�en 21
37 * NO-0349 Oslo
38 * Norway
39 *
40 * any improvements or extensions that they make and grant Inferno Nettverk A/S
41 * the rights to redistribute these changes.
42 *
43 */
44
45 #define DISABLE_GETHOSTBYNAME_CACHE (1)
46 #include "common.h"
47
48 static const char rcsid[] =
49 "$Id: hostcache.c,v 1.172.4.9.2.4.4.3 2020/11/11 16:11:54 karls Exp $";
50
51 #if 0
52 #warning "XXX change back to LOG_DEBUG"
53 #define LOG_DEBUG LOG_INFO
54 #endif
55
56 #if !SOCKS_CLIENT
57
58 #define MIN_HASH_SIZE (10240)
59 /*
60 * Assume this is so small a cache that it's better to just scan it
61 * sequentially rather than expire DNS-entries that we could otherwise
62 * have continued to keep in the cache.
63 */
64
65 static size_t
66 hosthash(const char *name, const size_t size);
67 /*
68 * Calculates a hash value for "name" and returns it's value.
69 * Size of hash table is given by "size".
70 */
71
72 static hostentry_t *
73 hostentcopy(hostentry_t *to, const struct hostent *from);
74 /*
75 * Copies all the values in "from" into "to", or as much as there is room
76 * for.
77 *
78 * The only reason this function may fail is if "from" is too big, i.e.
79 * has names that are too long (according to the dns spec) or similar.
80 *
81 * Note that the caller must set to->h_name or to->ipv4 when creating the
82 * original entry based on the result of a gethostby*() call. This is because
83 * the result returned by the gethostby*() call may not be the address
84 * used when gethostby*() was called, but the latter is what we want to use
85 * in our cache.
86 *
87 * Returns "to" on success, NULL on failure.
88 */
89
90
91
92 static size_t
93 addrhash(const struct sockaddr_storage *addr, const size_t size);
94 /*
95 * Calculates a hash value for the IP-address "addr" and returns it's value.
96 * Size of hash table is given by "size".
97 */
98
99 static int
100 addrisinlist(const struct sockaddr *addr, struct addrinfo *ailist,
101 struct addrinfo *ailist_last);
102 /*
103 * "ailist_last" is the last member in the "ailist" that is valid.
104 *
105 * Returns true if the address "addr" is in the list "ailist".
106 * Returns false otherwise.
107 */
108
109 dnsinfo_t *
110 getoldest( const size_t starti);
111 /*
112 * Returns the oldest entry at index "starti" or later.
113 */
114
115 static int
116 name_matches_cache(const dnsinfo_t *cacheentry,
117 const char *name, const char *service,
118 const struct addrinfo *hints);
119 static int
120 addr_matches_cache(const dnsinfo_t *cacheentry, const struct sockaddr *addr,
121 const int flags);
122
123 /*
124 * If the passed arguments match the cache entry "cacheentry",
125 * the functions returns true.
126 * Otherwise they returns false.
127 */
128
129
130
131 #if STANDALONE_UNIT_TEST
132 size_t cbyname_hit, cbyname_miss;
133 size_t cbyaddr_hit, cbyaddr_miss;
134 #endif /* !STANDALONE_UNIT_TEST */
135
136 /*
137 * hostname/ipaddress cache. Shared among all processes.
138 */
139 UNIT_TEST_STATIC_SCOPE dnsinfo_t *hostcache;
140
141
142 static int
143 gai2h_errno(const int gai_rc);
144 /*
145 * Returns the h_errno version of the getaddrinfo/getnameinfo error "gai_rc".
146 */
147
148 #undef cgetaddrinfo
149
150 #endif /* !SOCKS_CLIENT */
151
152 UNIT_TEST_STATIC_SCOPE int
153 addrinfocopy(dnsinfo_t *to, const struct addrinfo *from,
154 const struct addrinfo *hints);
155 /*
156 * Copies all the values in "from" into "to", or as much as there is room
157 * for. "hints" is taken into consideration concerning what to copy.
158 *
159 * The only reason this function may fail is if "from" is too big, i.e.
160 * has names that are too long (according to the dns spec) or similar,
161 * *OR* if "hints" precludes some entries from being copied.
162 *
163 * Returns an errocode of the same type as getaddrinfo(3).
164 */
165
166
167 #if SOCKSLIBRARY_DYNAMIC
168
169 #define gethostbyaddr(addr, len, type) sys_gethostbyaddr(addr, len, type)
170
171 #if SOCKS_CLIENT
172
173 #undef gethostbyname
174 #define gethostbyname(name) sys_gethostbyname(name)
175
176 #endif /* SOCKS_CLIENT */
177
178 #endif /* SOCKSLIBRARY_DYNAMIC */
179
180 #define GAI_RC_IS_SYSTEMPROBLEM(gai_rc) \
181 ((gai_rc) == EAI_MEMORY \
182 || (gai_rc) == EAI_SYSTEM)
183
184 #define GAI_RC_IS_EAGAIN(gai_rc) \
185 ((gai_rc) == EAI_AGAIN)
186
187 #if HAVE_ERR_EAI_OVERFLOW
188 #define GAI_RC_IS_INTERNALERROR(gai_rc) \
189 ( ((gai_rc) == EAI_BADFLAGS) \
190 || ((gai_rc) == EAI_FAMILY) \
191 || ((gai_rc) == EAI_SOCKTYPE) \
192 || ((gai_rc) == EAI_OVERFLOW))
193 #else
194 #define GAI_RC_IS_INTERNALERROR(gai_rc) \
195 ( ((gai_rc) == EAI_BADFLAGS) \
196 || ((gai_rc) == EAI_FAMILY) \
197 || ((gai_rc) == EAI_SOCKTYPE))
198 #endif /* HAVE_ERR_EAI_OVERFLOW */
199
200 int
cgetaddrinfo(name,service,hints,res,resmem)201 cgetaddrinfo(name, service, hints, res, resmem)
202 const char *name;
203 const char *service;
204 const struct addrinfo *hints;
205 struct addrinfo **res;
206 dnsinfo_t *resmem;
207 {
208 const char *function = "cgetaddrinfo()";
209 char namebuf[MAXHOSTNAMELEN * 4], servicebuf[MAXSERVICELEN * 4];
210 int gai_rc;
211
212 #if SOCKS_CLIENT
213 /*
214 * No cache for client. Just a slightly different interface, mainly
215 * to limit differences in code called by both client and server, as
216 * the server caches the result of getaddrinfo(3).
217 */
218
219 SASSERTX(res != NULL);
220 SASSERTX(resmem != NULL);
221
222 if ((gai_rc = getaddrinfo(name, service, hints, res)) != 0)
223 return gai_rc;
224
225 SASSERTX(*res != NULL);
226
227 /*
228 * have resmem. Copy into that and don't use the one returned from
229 * getaddrinfo(3).
230 */
231 gai_rc = addrinfocopy(resmem, *res, hints);
232
233 freeaddrinfo(*res);
234
235 if (gai_rc != 0) {
236 if (GAI_RC_IS_SYSTEMPROBLEM(gai_rc))
237 swarnx("%s: addrinfocopy() failed for hostname \"%s\", service \"%s\"",
238 function,
239 str2vis(name, strlen(name), namebuf, sizeof(namebuf)),
240 service == NULL ?
241 "<NULL>" : str2vis(service,
242 strlen(service),
243 servicebuf,
244 sizeof(servicebuf)));
245
246
247 return gai_rc;
248 }
249
250 *res = &resmem->data.getaddr.addrinfo;
251
252 return gai_rc;
253
254 #else /* !SOCKS_CLIENT */
255
256 static size_t i;
257 static int count;
258 const time_t timenow = time_monotonic(NULL);
259 #if !STANDALONE_UNIT_TEST
260 static size_t cbyname_hit, cbyname_miss;
261 #endif /* !STANDALONE_UNIT_TEST */
262 dnsinfo_t *freehost;
263 size_t hashi;
264 int have_oldres = 0;
265
266 SASSERTX(res != NULL);
267 SASSERTX(resmem != NULL);
268
269 if (count++ % SOCKD_CACHESTAT == 0)
270 slog(LOG_DEBUG, "%s, hit: %lu, miss: %lu",
271 function, (unsigned long)cbyname_hit, (unsigned long)cbyname_miss);
272
273 if (strlen(name) >= sizeof(freehost->id.name)) {
274 swarnx("%s: hostname \"%s\" is too long. Max length is %lu",
275 function,
276 str2vis(name, strlen(name), namebuf, sizeof(namebuf)),
277 (unsigned long)sizeof(freehost->id.name) - 1);
278
279 return EAI_MEMORY;
280 }
281
282 if (service != NULL && strlen(service) >= sizeof(freehost->service)) {
283 swarn("max length of servicename was at compiletime set to %lu, but "
284 "now being called with servicename of length %lu. Recompile "
285 "required to support a servicename this long. "
286 "Servicename: \"%s\"",
287 (unsigned long)sizeof(freehost->service) - 1,
288 (unsigned long)strlen(service) + 1,
289 str2vis(service, strlen(service), servicebuf, sizeof(servicebuf)));
290
291 return EAI_MEMORY;
292 }
293
294 socks_lock(sockscf.hostfd, 0, 0, 0, 1);
295
296 /*
297 * First check if the desired name is the same as last time.
298 */
299 if (i < SOCKD_HOSTCACHE
300 && name_matches_cache(&hostcache[i], name, service, hints)) {
301 if (socks_difftime(timenow, hostcache[i].written) < SOCKD_CACHETIMEOUT) {
302 ++cbyname_hit;
303
304 if (hostcache[i].gai_rc == 0) {
305 gai_rc = addrinfocopy(resmem,
306 &hostcache[i].data.getaddr.addrinfo,
307 hints);
308 SASSERTX(gai_rc == 0);
309
310 *res = &resmem->data.getaddr.addrinfo;
311 }
312
313 socks_unlock(sockscf.hostfd, 0, 0);
314 return hostcache[i].gai_rc;
315 }
316
317 /*
318 * Else; in cache, but expired already.
319 */
320 freehost = &hostcache[i];
321 hashi = hosthash(name, SOCKD_HOSTCACHE);
322
323 if (freehost->gai_rc == 0) {
324 gai_rc = addrinfocopy(resmem,
325 &freehost->data.getaddr.addrinfo,
326 hints);
327
328 SASSERTX(gai_rc == 0);
329 have_oldres = 1;
330 }
331 }
332 else {
333 /*
334 * Go through the entire in the cache, looking for a match.
335 */
336
337 hashi = hosthash(name, SOCKD_HOSTCACHE);
338 for (i = hashi, freehost = NULL; i < SOCKD_HOSTCACHE; ++i) {
339 if (!hostcache[i].allocated) {
340 if (freehost == NULL)
341 freehost = &hostcache[i];
342
343 continue;
344 }
345
346 if (name_matches_cache(&hostcache[i], name, service, hints)) {
347 if (socks_difftime(timenow, hostcache[i].written)
348 >= SOCKD_CACHETIMEOUT) {
349 freehost = &hostcache[i];
350
351 if (freehost->gai_rc == 0) {
352 gai_rc = addrinfocopy(resmem,
353 &freehost->data.getaddr.addrinfo,
354 hints);
355
356 SASSERTX(gai_rc == 0);
357 have_oldres = 1;
358 }
359
360 break;
361 }
362
363 ++cbyname_hit;
364
365 if (hostcache[i].gai_rc != 0) {
366 socks_unlock(sockscf.hostfd, 0, 0);
367 return hostcache[i].gai_rc;
368 }
369
370 gai_rc = addrinfocopy(resmem,
371 &hostcache[i].data.getaddr.addrinfo,
372 hints);
373
374 SASSERTX(gai_rc == 0);
375
376 socks_unlock(sockscf.hostfd, 0, 0);
377
378 *res = &resmem->data.getaddr.addrinfo;
379 return 0;
380 }
381 }
382 }
383
384 /*
385 * Nope, this name is not in the cache. Have to resolve.
386 */
387
388 socks_unlock(sockscf.hostfd, 0, 0);
389
390 ++cbyname_miss;
391
392 gai_rc = getaddrinfo(name, service, hints, res);
393
394 slog(LOG_DEBUG,
395 "%s: getaddrinfo(%s, %s, { %s }) returned %d and %p: %s",
396 function,
397 str2vis(name, strlen(name), namebuf, sizeof(namebuf)),
398 service == NULL ? "<NULL>" : str2vis(service,
399 strlen(service),
400 servicebuf,
401 sizeof(servicebuf)),
402 hints == NULL ? "<NULL>" : aihints2string(hints, NULL, 0),
403 gai_rc,
404 gai_rc == 0 ? *res : NULL,
405 gai_rc == 0 ? "no error" : gai_strerror(gai_rc));
406
407 if (gai_rc == EAI_SYSTEM && errno == EMFILE) {
408 if (sockscf.state.reservedfdv[0] != -1) {
409 close(sockscf.state.reservedfdv[0]);
410
411 gai_rc = getaddrinfo(name, service, hints, res);
412
413 sockscf.state.reservedfdv[0] = makedummyfd(0, 0);
414
415 if (gai_rc != 0)
416 slog(LOG_DEBUG, "%s: getaddrinfo(%s, %s) failed again: %s",
417 function,
418 str2vis(name, strlen(name), namebuf, sizeof(namebuf)),
419 service == NULL ?
420 "<NULL>" : str2vis(service,
421 strlen(service),
422 servicebuf,
423 sizeof(servicebuf)),
424 gai_strerror(gai_rc));
425 }
426 }
427
428 #if HAVE_LINUX_BUGS
429 /*
430 * glibc calls connect(2) to something that fails from __GI_getaddrinfo(),
431 * and then does not clear the errno, so even though the above getaddrinfo()
432 * call succeeded, errno is now set, at least on the glibc used by
433 * 3.7.3-101.fc17.x86_64.
434 */
435 if (gai_rc == 0)
436 errno = 0;
437 #endif /* HAVE_LINUX_BUGS */
438
439 if (gai_rc != 0) {
440 /*
441 * check if the problem is ours, or the DNS's.
442 */
443
444 if (GAI_RC_IS_SYSTEMPROBLEM(gai_rc)) {
445 swarn("%s: getaddrinfo(%s, %s) failed again: %s",
446 function,
447 str2vis(name, strlen(name), namebuf, sizeof(namebuf)),
448 service == NULL ?
449 "<NULL>" : str2vis(service,
450 strlen(service),
451 servicebuf,
452 sizeof(servicebuf)),
453 gai_strerror(gai_rc));
454
455 }
456
457 if (have_oldres) {
458 slog(LOG_DEBUG,
459 "%s: resolve of %s failed. Returning stale cache entry",
460 function, str2vis(name, strlen(name), namebuf, sizeof(namebuf))),
461
462 *res = &resmem->data.getaddr.addrinfo;
463 return 0;
464 }
465
466 if (GAI_RC_IS_SYSTEMPROBLEM(gai_rc))
467 return gai_rc;
468 else if (GAI_RC_IS_EAGAIN(gai_rc))
469 return gai_rc;
470 else if (GAI_RC_IS_INTERNALERROR(gai_rc))
471 SERR(gai_rc);
472
473 /* else; assume problem is not ours. */
474 }
475
476 /*
477 * Either things resolved ok, or the reason for failure is not our own
478 * (i.e., not lack of memory, free fds, etc.).
479 */
480
481 if (gai_rc == 0)
482 SASSERTX(*res != NULL);
483 else {
484 /*
485 * else; assume host does not exist at the moment and cache that result.
486 */
487
488 *res = NULL;
489 res = NULL;
490 }
491
492 socks_lock(sockscf.hostfd, 0, 0, 1, 1);
493
494 if (freehost == NULL)
495 freehost = getoldest(hashi);
496 /*
497 * else: contents pointed to can have changed, in which case we may now
498 * be overwriting one of the most recent entries, but never mind that;
499 * that unlikely case is not important enough to warrant locking and
500 * scanning for the oldest entry now.
501 */
502
503 SASSERTX(freehost != NULL);
504
505 if (gai_rc == 0) {
506 gai_rc = addrinfocopy(freehost, *res, hints);
507
508 /*
509 * have resmem. Don't use the mem returned from getaddrinfo(3).
510 */
511 freeaddrinfo(*res);
512
513 if (gai_rc != 0) {
514 if (GAI_RC_IS_SYSTEMPROBLEM(gai_rc))
515 swarnx("%s: addrinfocopy() failed for \"%s\"/service \"%s\": %s",
516 function,
517 str2vis(name, strlen(name), namebuf, sizeof(namebuf)),
518 service == NULL ?
519 "<NULL>" : str2vis(service,
520 strlen(service),
521 servicebuf,
522 sizeof(servicebuf)),
523 gai_strerror(gai_rc));
524
525 socks_unlock(sockscf.hostfd, 0, 0);
526 return gai_rc;
527 }
528 }
529
530 if (hints == NULL)
531 freehost->data.getaddr.hints = NULL;
532 else {
533 freehost->data.getaddr.hints = &freehost->data.getaddr.hints_mem;
534 *freehost->data.getaddr.hints = *hints;
535 }
536
537 freehost->key = id_name;
538 STRCPY_ASSERTLEN(freehost->id.name, name);
539
540 if (service == NULL)
541 *freehost->service = NUL;
542 else
543 STRCPY_ASSERTLEN(freehost->service, service);
544
545 freehost->written = timenow;
546 freehost->gai_rc = gai_rc;
547 freehost->allocated = 1;
548
549 SASSERTX(freehost->key == id_name);
550
551 if (gai_rc == 0) {
552 gai_rc = addrinfocopy(resmem, &freehost->data.getaddr.addrinfo, hints);
553 SASSERTX(gai_rc == 0);
554 }
555
556 socks_unlock(sockscf.hostfd, 0, 0);
557
558 resmem->gai_rc = gai_rc;
559 if (resmem->gai_rc == 0)
560 *res = &resmem->data.getaddr.addrinfo;
561
562 return resmem->gai_rc;
563 #endif /* !SOCKS_CLIENT */
564 }
565
566 #if !SOCKS_CLIENT
567 int
cgetnameinfo(addr,addrlen,_host,_hostlen,_service,_servicelen,flags)568 cgetnameinfo(addr, addrlen, _host, _hostlen, _service, _servicelen, flags)
569 const struct sockaddr *addr;
570 const socklen_t addrlen;
571 char *_host;
572 const socklen_t _hostlen;
573 char *_service;
574 const socklen_t _servicelen;
575 const int flags;
576 {
577 const char *function = "cgetnameinfo()";
578 const time_t timenow = time_monotonic(NULL);
579 #if !STANDALONE_UNIT_TEST
580 static size_t cbyaddr_hit, cbyaddr_miss;
581 #endif /* !STANDALONE_UNIT_TEST */
582 static size_t i;
583 static int count;
584 dnsinfo_t *freehost;
585 size_t hashi;
586 char host[sizeof(freehost->data.getname.name)],
587 service[sizeof(freehost->service)];
588 int gai_rc, have_oldres = 0;
589
590 #define DOCOPY(_host, _hostlen, _service, _servicelen, cacheentry) \
591 do { \
592 if ((_hostlen) > 0) { \
593 strncpy((_host), (cacheentry)->data.getname.name, (_hostlen) - 1); \
594 (_host)[(_hostlen) - 1] = NUL; \
595 } \
596 \
597 if ((_servicelen) > 0) { \
598 strncpy((_service), (cacheentry)->service, (_servicelen) - 1); \
599 (_service)[(_servicelen) - 1] = NUL; \
600 } \
601 } while (/* CONSTCOND */ 0)
602
603 if (count++ % SOCKD_CACHESTAT == 0) {
604 slog(LOG_DEBUG, "%s: hit: %lu, miss: %lu",
605 function, (unsigned long)cbyaddr_hit, (unsigned long)cbyaddr_miss);
606 }
607
608 socks_lock(sockscf.hostfd, 0, 0, 0, 1);
609
610 /*
611 * First check if the desired name is the same as last time.
612 */
613 if (i < SOCKD_HOSTCACHE
614 && addr_matches_cache(&hostcache[i], addr, flags)) {
615 if (socks_difftime(timenow, hostcache[i].written) < SOCKD_CACHETIMEOUT) {
616 ++cbyaddr_hit;
617
618 if (hostcache[i].gai_rc == 0)
619 DOCOPY(_host, _hostlen, _service, _servicelen, &hostcache[i]);
620
621 socks_unlock(sockscf.hostfd, 0, 0);
622 return hostcache[i].gai_rc;
623 }
624
625 /*
626 * Else; have an entry, but it's expired already.
627 */
628
629 freehost = &hostcache[i];
630 hashi = addrhash(TOCSS(addr), SOCKD_HOSTCACHE);
631
632 if (freehost->gai_rc == 0) {
633 DOCOPY(_host, _hostlen, _service, _servicelen, freehost);
634 have_oldres = 1;
635 }
636
637 }
638 else {
639 /*
640 * Go through the cache and see if there is match there.
641 */
642 hashi = addrhash(TOCSS(addr), SOCKD_HOSTCACHE);
643 for (i = hashi, freehost = NULL; i < SOCKD_HOSTCACHE; ++i) {
644 if (!hostcache[i].allocated) {
645 if (freehost == NULL)
646 freehost = &hostcache[i];
647
648 continue;
649 }
650
651 if (addr_matches_cache(&hostcache[i], addr, flags)) {
652 if (socks_difftime(timenow, hostcache[i].written)
653 >= SOCKD_CACHETIMEOUT) {
654 freehost = &hostcache[i];
655
656 if (freehost->gai_rc == 0) {
657 DOCOPY(_host, _hostlen, _service, _servicelen, freehost);
658 have_oldres = 1;
659 }
660
661 break;
662 }
663
664 ++cbyaddr_hit;
665
666 if (hostcache[i].gai_rc != 0) {
667 socks_unlock(sockscf.hostfd, 0, 0);
668 return hostcache[i].gai_rc;
669 }
670
671 DOCOPY(_host, _hostlen, _service, _servicelen, &hostcache[i]);
672
673 socks_unlock(sockscf.hostfd, 0, 0);
674 return 0;
675 }
676 }
677 }
678
679 ++cbyaddr_miss;
680
681 socks_unlock(sockscf.hostfd, 0, 0);
682
683 #if SOCKSLIBRARY_DYNAMIC
684 /*
685 * In case getnameinfo(3) resolves to some other libresolv call
686 * which we are also interpositioning. E.g. on FreeBSD 9.1 it's
687 * getnameinfo(3) -> getipnodebyaddr(3) -> gethostbyaddr(3).
688 * We don't want that; when we call getnameinfo(3), we don't want
689 * that to resolve to something possibly interpositioned by us.
690 */
691
692 socks_markasnative("*");
693 #endif /* SOCKSLIBRARY_DYNAMIC */
694
695 gai_rc = getnameinfo(addr,
696 addrlen,
697 host,
698 sizeof(host),
699 service,
700 sizeof(service),
701 flags);
702
703 #if SOCKSLIBRARY_DYNAMIC
704 socks_markasnormal("*");
705 #endif /* SOCKSLIBRARY_DYNAMIC */
706
707 slog(LOG_DEBUG, "%s: getnameinfo(%s) returned %d: %s",
708 function,
709 sockaddr2string(TOCSS(addr), NULL, 0),
710 gai_rc,
711 gai_strerror(gai_rc));
712
713 if (gai_rc != 0 && GAI_RC_IS_SYSTEMPROBLEM(gai_rc)) {
714 if (sockscf.state.reservedfdv[0] != -1) {
715 close(sockscf.state.reservedfdv[0]);
716
717 gai_rc = getnameinfo(addr,
718 addrlen,
719 host,
720 sizeof(host),
721 service,
722 sizeof(service),
723 flags);
724
725 sockscf.state.reservedfdv[0] = makedummyfd(0, 0);
726
727 if (gai_rc != 0) {
728 slog(LOG_DEBUG, "%s: getnameinfo(%s) failed again: %s",
729 function,
730 sockaddr2string(TOCSS(addr), NULL, 0),
731 gai_strerror(gai_rc));
732 }
733 }
734 }
735
736 if (gai_rc != 0) {
737 if (have_oldres) {
738 slog(LOG_DEBUG,
739 "%s: resolve of %s failed. Returning stale cache entry",
740 function, sockaddr2string(TOCSS(addr), NULL, 0));
741
742 /* should be copied into _host and _service already. */
743 return 0;
744 }
745
746 if (GAI_RC_IS_SYSTEMPROBLEM(gai_rc)) {
747 swarn("%s: getnameinfo(%s) failed: %s",
748 function,
749 sockaddr2string(TOCSS(addr), NULL, 0),
750 gai_strerror(gai_rc));
751
752 return gai_rc;
753 }
754 else if (GAI_RC_IS_EAGAIN(gai_rc))
755 return gai_rc;
756 else if (GAI_RC_IS_INTERNALERROR(gai_rc))
757 SERR(gai_rc);
758 }
759
760 socks_lock(sockscf.hostfd, 0, 0, 1, -1);
761
762 if (freehost == NULL)
763 freehost = getoldest(hashi);
764
765 SASSERTX(freehost != NULL);
766
767 freehost->gai_rc = gai_rc;
768
769 STRCPY_ASSERTSIZE(freehost->service, service);
770 STRCPY_ASSERTSIZE(freehost->data.getname.name, host);
771
772 freehost->data.getname.flags = flags;
773 freehost->written = timenow;
774 freehost->key = id_addr;
775 memcpy(&freehost->id.addr, addr, MIN(sizeof(freehost->id.addr), addrlen));
776
777 freehost->allocated = 1;
778
779 socks_unlock(sockscf.hostfd, 0, 0);
780
781 if (freehost->gai_rc == 0)
782 DOCOPY(_host, _hostlen, _service, _servicelen, freehost);
783
784 return freehost->gai_rc;
785 }
786 #endif /* !SOCKS_CLIENT */
787
788
789 int
addrinfo_issupported(ai)790 addrinfo_issupported(ai)
791 const struct addrinfo *ai;
792 {
793
794 if (!safamily_issupported(ai->ai_family))
795 return 0;
796
797 switch (ai->ai_socktype) {
798 case 0: /* Solaris. :-(. */
799 case SOCK_STREAM:
800 case SOCK_DGRAM:
801 break;
802
803 default:
804 return 0;
805 }
806
807 switch (ai->ai_protocol) {
808 case 0: /* Solaris. :-(. */
809 case IPPROTO_TCP:
810 case IPPROTO_UDP:
811 break;
812
813 default:
814 return 0;
815 }
816
817 return 1;
818 }
819
820 UNIT_TEST_STATIC_SCOPE int
addrinfocopy(to,from,hints)821 addrinfocopy(to, from, hints)
822 dnsinfo_t *to;
823 const struct addrinfo *from;
824 const struct addrinfo *hints;
825 {
826 const char *function = "addrinfocopy()";
827 const struct addrinfo *from_ai;
828 const size_t maxentries = ELEMENTS(to->data.getaddr.ai_addr_mem);
829 struct addrinfo *to_ai, *to_ai_previous;
830
831 #if !SOCKS_CLIENT
832
833 struct addrinfo *to_ai_start;
834
835 #endif /* !SOCKS_CLIENT */
836
837 size_t i;
838 char visbuf[MAXHOSTNAMELEN * 4];
839
840 bzero(to, sizeof(*to));
841
842 from_ai = from;
843 to_ai = &to->data.getaddr.addrinfo;
844
845 #if !SOCKS_CLIENT
846
847 to_ai_start = to_ai;
848
849 #endif /* !SOCKS_CLIENT */
850
851 to_ai_previous = to_ai;
852 i = 0;
853
854 while (i < maxentries && from_ai != NULL) {
855
856 #if !SOCKS_CLIENT
857 int doskip = 0;
858
859 if (!addrinfo_issupported(from_ai))
860 doskip = 1;
861 else if (i > 0
862 && ntohs(GET_SOCKADDRPORT(TOSS(from_ai->ai_addr))) == 0
863 && ntohs(GET_SOCKADDRPORT(TOSS(to_ai_previous->ai_addr))) == 0
864 && addrisinlist(from_ai->ai_addr, to_ai_start, to_ai_previous)) {
865 /*
866 * same address as before, just different protocol. We don't
867 * care about protocol and don't want to waste memory on that.
868 *
869 * We compare against 0 since a portnumber of 0 should mean
870 * the caller doesn't care either (though this will still not
871 * make things work in the generic case, where the caller may
872 * be looking for an entry with the appropriate protocol),
873 * and makes it easy to set a portnumber (different from 0)
874 * when used in a particular unit-test. Apart from that, it
875 * would be more correct to ignore the portnumber of course.
876 */
877 slog(LOG_DEBUG, "%s: skipping address %s, protocol %d",
878 function,
879 sockaddr2string(TOSS(from_ai->ai_addr), NULL, 0),
880 from_ai->ai_protocol);
881
882 doskip = 1;
883 }
884 else if (from_ai->ai_addr->sa_family == AF_INET6
885 && IN6_IS_ADDR_V4MAPPED(&TOIN6(from_ai->ai_addr)->sin6_addr)) {
886 if (hints != NULL
887 && hints->ai_family != 0
888 && hints->ai_family != AF_INET)
889 doskip = 1;
890 else if (!external_has_safamily(AF_INET)) {
891 /*
892 * no point in converting to IPv4 if we don't have any
893 * IPv4 address to use. Lets hope there is another
894 * address for this hostname.
895 */
896 doskip = 1;
897 }
898 else {
899 /*
900 * Have IPv4 on external interface and hints does not preclude
901 * us from returning IPv4, so can convert the address from
902 * IPv4-mapped IPv6 (which we don't want to use), to a regular
903 * IPv4 address.
904 */
905
906 *to_ai = *from_ai; /* most attributes will remain the same. */
907
908 to_ai->ai_addr = TOSA(&to->data.getaddr.ai_addr_mem[i]);
909 bzero(to_ai->ai_addr, salen(AF_INET));
910 SET_SOCKADDR(TOSS(to_ai->ai_addr), AF_INET);
911
912 ipv4_mapped_to_regular(&TOIN6(from_ai->ai_addr)->sin6_addr,
913 &TOIN(to_ai->ai_addr)->sin_addr);
914
915 to_ai->ai_family = AF_INET;
916 to_ai->ai_addrlen = salen(AF_INET);
917 }
918 }
919 else {
920 *to_ai = *from_ai;
921 to_ai->ai_addr = TOSA(&to->data.getaddr.ai_addr_mem[i]);
922 memcpy(to_ai->ai_addr, from_ai->ai_addr, from_ai->ai_addrlen);
923 }
924
925 if (doskip) {
926 slog(LOG_DEBUG, "%s: skipping address family %d",
927 function, from_ai->ai_addr->sa_family);
928
929 from_ai = from_ai->ai_next;
930 continue;
931 }
932
933 #else /* SOCKS_CLIENT */
934
935 *to_ai = *from_ai;
936 to_ai->ai_addr = TOSA(&to->data.getaddr.ai_addr_mem[i]);
937 memcpy(to_ai->ai_addr, from_ai->ai_addr, from_ai->ai_addrlen);
938
939 #endif /* !SOCKS_CLIENT */
940
941 if (from_ai->ai_canonname != NULL) {
942 const size_t len = strlen(from_ai->ai_canonname);
943
944 if (len >= sizeof(to->data.getaddr.ai_canonname_mem)) {
945 swarnx("%s: DNS-name %s is %lu bytes long, expected max is %lu",
946 function,
947 str2vis(from_ai->ai_canonname, len, visbuf, sizeof(visbuf)),
948 (unsigned long)len,
949 (unsigned long)sizeof(to->data.getaddr.ai_canonname_mem) - 1);
950
951 return EAI_MEMORY;
952 }
953
954 /*
955 * Whether any entries but the first entry have an ai_canonname
956 * appears to vary.
957 *
958 * glibc: no, at least the version on 3.7.3-101.fc17.x86_64.
959 * libresolv: yes, at least the version with FreeBSD 9.1-RELEASE.
960 *
961 * In both cases, all ai_canonnames are the same however, so
962 * we do not need to allocate separate memory for them but can
963 * let them all use the memory of the first entry.
964 */
965
966 if (i == 0) /* first entry; initialize memory. */
967 memcpy(to->data.getaddr.ai_canonname_mem,
968 from_ai->ai_canonname,
969 len + 1);
970
971 to_ai->ai_canonname = to->data.getaddr.ai_canonname_mem;
972 }
973 else
974 to_ai->ai_canonname = NULL;
975
976 from_ai = from_ai->ai_next;
977
978 to_ai_previous = to_ai;
979 to_ai->ai_next = &to->data.getaddr.ai_next_mem[i];
980 to_ai = to_ai->ai_next;
981
982 ++i;
983 }
984
985 to_ai->ai_next = NULL;
986
987 if (from_ai == NULL || i >= maxentries)
988 to_ai_previous->ai_next = NULL;
989
990 if (i == 0) {
991 slog(LOG_DEBUG, "%s: strange, no entries copied", function);
992
993 bzero(&to->data.getaddr.addrinfo, sizeof(to->data.getaddr.addrinfo));
994 return EAI_FAMILY;
995 }
996
997 return 0;
998 }
999
1000
1001 #if !SOCKS_CLIENT
1002
1003 void
hostcacheinvalidate(void)1004 hostcacheinvalidate(void)
1005 {
1006 const char *function = "hostcacheinvalidate()";
1007 size_t i;
1008
1009 slog(LOG_DEBUG, "%s", function);
1010
1011 socks_lock(sockscf.hostfd, 0, 0, 0, 1);
1012
1013 for (i = 0; i < SOCKD_HOSTCACHE; ++i)
1014 hostcache[i].allocated = 0;
1015
1016 socks_unlock(sockscf.hostfd, 0, 0);
1017 }
1018
1019 static int
gai2h_errno(gai_rc)1020 gai2h_errno(gai_rc)
1021 const int gai_rc;
1022 {
1023
1024 switch (gai_rc) {
1025 case EAI_AGAIN:
1026 case EAI_MEMORY:
1027 return TRY_AGAIN;
1028
1029 case EAI_NONAME:
1030 return HOST_NOT_FOUND;
1031 }
1032
1033 return NO_RECOVERY;
1034 }
1035
1036 void
hostcachesetup(void)1037 hostcachesetup(void)
1038 {
1039 const char *function = "hostcachesetup()";
1040
1041 if ((sockscf.hostfd = socks_mklock(SOCKD_SHMEMFILE, NULL, 0)) == -1)
1042 serr("%s: socks_mklock() failed to create shmemfile using base %s",
1043 function, SOCKD_SHMEMFILE);
1044
1045 if ((hostcache = sockd_mmap(NULL,
1046 sizeof(*hostcache) * SOCKD_HOSTCACHE,
1047 PROT_READ | PROT_WRITE,
1048 MAP_SHARED,
1049 sockscf.hostfd,
1050 1)) == MAP_FAILED)
1051 serr("%s: failed to mmap(2) hostcache of size %lu",
1052 function, (unsigned long)(sizeof(*hostcache) * SOCKD_HOSTCACHE));
1053 }
1054
1055
1056 struct hostent *
cgethostbyname(name)1057 cgethostbyname(name)
1058 const char *name;
1059 {
1060 const char *function = "cgethostbyname()";
1061 static hostentry_t hostentry;
1062 static dnsinfo_t resmem;
1063 static char *nullist[] = { NULL }, *addrv[HOSTENT_MAX_ALIASES + 1];
1064 struct addrinfo *ainfo, *next, hints;
1065 struct hostent he;
1066 size_t addrc;
1067 int rc;
1068
1069 bzero(&hints, sizeof(hints));
1070 hints.ai_flags = AI_CANONNAME;
1071 hints.ai_family = AF_INET;
1072
1073 if ((rc = cgetaddrinfo(name, NULL, &hints, &ainfo, &resmem)) != 0) {
1074 char visbuf[MAXHOSTNAMELEN * 4];
1075
1076 slog(LOG_DEBUG, "%s: getaddrinfo(%s) failed: %s",
1077 function,
1078 str2vis(name, strlen(name), visbuf, sizeof(visbuf)),
1079 gai_strerror(rc));
1080
1081 h_errno = gai2h_errno(rc);
1082 return NULL;
1083 }
1084
1085 SASSERTX(ainfo != NULL);
1086
1087 addrc = 0;
1088 for (next = ainfo; next != NULL; next = next->ai_next) {
1089 SASSERTX(addrc < ELEMENTS(addrv));
1090 SASSERTX(next->ai_addr != NULL);
1091 SASSERTX(next->ai_family == next->ai_addr->sa_family);
1092
1093 if (next->ai_addr->sa_family == AF_INET) {
1094 he.h_addrtype = next->ai_addr->sa_family;
1095 he.h_length = sizeof(struct in_addr);
1096 addrv[addrc++] = (char *)&TOIN(next->ai_addr)->sin_addr;
1097
1098 if (addrc + 1 >= ELEMENTS(addrv))
1099 break;
1100 }
1101 else
1102 slog(LOG_DEBUG,
1103 "%s: ai_family = %d. Skipped", function, next->ai_family);
1104 }
1105
1106 if (addrc == 0) {
1107 h_errno = NO_ADDRESS;
1108 return NULL;
1109 }
1110
1111 addrv[addrc] = NULL;
1112
1113 he.h_name = ainfo->ai_canonname;
1114 he.h_aliases = nullist;
1115 he.h_addr_list = addrv;
1116
1117 hostentcopy(&hostentry, &he);
1118
1119 return &hostentry.hostent;
1120 }
1121
1122
1123 struct hostent *
cgethostbyaddr(addr,len,type)1124 cgethostbyaddr(addr, len, type)
1125 const void *addr;
1126 socklen_t len;
1127 int type;
1128 {
1129 const char *function = "cgethostbyaddr()";
1130 static hostentry_t hostentry;
1131 static char *nullist[] = { NULL }, *onelist[2];
1132 struct sockaddr_storage sa;
1133 struct hostent he;
1134 char host[MAXHOSTNAMELEN];
1135 int rc;
1136
1137 SASSERTX(type == AF_INET);
1138 SASSERTX(len == sizeof(struct in_addr));
1139
1140 bzero(&sa, sizeof(sa));
1141 SET_SOCKADDR(&sa, AF_INET);
1142 TOIN(&sa)->sin_addr = *(const struct in_addr *)addr;
1143
1144 if ((rc = cgetnameinfo(TOSA(&sa),
1145 salen(sa.ss_family),
1146 host,
1147 sizeof(host),
1148 NULL,
1149 0,
1150 NI_NAMEREQD)) != 0) {
1151 slog(LOG_DEBUG, "%s: cgetnameinfo(%s) failed: %s",
1152 function, sockaddr2string(&sa, NULL, 0), gai_strerror(rc));
1153
1154 h_errno = gai2h_errno(rc);
1155 return NULL;
1156 }
1157
1158 he.h_name = host;
1159 he.h_aliases = nullist;
1160 he.h_addrtype = sa.ss_family;
1161 he.h_length = sizeof(struct in_addr);
1162 he.h_addr_list = onelist;
1163 he.h_addr_list[0] = (char *)&TOIN(&sa)->sin_addr;
1164 he.h_addr_list[1] = NULL;
1165
1166 hostentcopy(&hostentry, &he);
1167
1168 return &hostentry.hostent;
1169 }
1170
1171 static hostentry_t *
hostentcopy(to,from)1172 hostentcopy(to, from)
1173 hostentry_t *to;
1174 const struct hostent *from;
1175 {
1176 const char *function = "hostentcopy()";
1177 const size_t maxaliases = ELEMENTS(to->h_aliases);
1178 size_t i;
1179
1180 if ((size_t)from->h_length > sizeof(to->h_addr_listmem[0])) {
1181 swarnx("%s: h_length of %s is %d bytes long, max expected is %lu",
1182 function,
1183 from->h_name,
1184 from->h_length,
1185 (unsigned long)sizeof(to->h_addr_listmem[0]));
1186
1187 return NULL;
1188 }
1189
1190 if (strlen(from->h_name) >= sizeof(to->h_name)) {
1191 swarnx("%s: name %s is %lu bytes long, max expected is %lu",
1192 function,
1193 from->h_name,
1194 (unsigned long)strlen(from->h_name),
1195 (unsigned long)sizeof(to->h_name) - 1);
1196
1197 return NULL;
1198 }
1199
1200 to->hostent.h_addrtype = from->h_addrtype;
1201 to->hostent.h_length = from->h_length;
1202 to->hostent.h_name = to->h_name;
1203 strcpy(to->hostent.h_name, from->h_name);
1204
1205 for (i = 0; i < (maxaliases - 1) && from->h_aliases[i] != NULL; ++i) {
1206 if (strlen(from->h_aliases[i]) >= sizeof(to->h_aliasesmem[0])) {
1207 swarnx("%s: name %s is %lu bytes long, max expected is %lu",
1208 function,
1209 from->h_aliases[i],
1210 (unsigned long)strlen(from->h_aliases[i]),
1211 (unsigned long)sizeof(to->h_aliases[0]) - 1);
1212
1213 return NULL;
1214 }
1215
1216 to->h_aliases[i] = to->h_aliasesmem[i];
1217 strcpy(to->h_aliases[i], from->h_aliases[i]);
1218 }
1219
1220 to->hostent.h_aliases = to->h_aliases;
1221 to->hostent.h_aliases[i] = NULL;
1222
1223 for (i = 0; i < (maxaliases - 1) && from->h_addr_list[i] != NULL; ++i) {
1224 to->h_addr_list[i] = to->h_addr_listmem[i];
1225 memcpy(to->h_addr_list[i], from->h_addr_list[i], (size_t)from->h_length);
1226 }
1227
1228 to->hostent.h_addr_list = to->h_addr_list;
1229 to->hostent.h_addr_list[i] = NULL;
1230
1231 return to;
1232 }
1233
1234 static size_t
hosthash(name,size)1235 hosthash(name, size)
1236 const char *name;
1237 const size_t size;
1238 {
1239 unsigned int value;
1240 char *end;
1241
1242 if (size < MIN_HASH_SIZE)
1243 return 0;
1244
1245 /* don't bother scanning past second dot. */
1246 if ((end = strchr(name, '.')) != NULL)
1247 end = strchr(end + 1, '.');
1248
1249 if (end == NULL) /* zero or one dot in name. */
1250 end = strchr(name, NUL);
1251
1252 SASSERTX(name <= end);
1253
1254 value = 0;
1255 while (name != end) {
1256 value = (value << 5) + *name; /* MAW - DS&A: Horner's rule. */
1257 ++name;
1258 }
1259
1260 return value % size;
1261 }
1262
1263 static size_t
addrhash(addr,size)1264 addrhash(addr, size)
1265 const struct sockaddr_storage *addr;
1266 const size_t size;
1267 {
1268 size_t sum;
1269
1270 if (size < MIN_HASH_SIZE)
1271 /*
1272 * Assume this is so small a cache that it's better to just scan
1273 * it sequentially, rather than risk premature expiry of cached
1274 * DNS-entries.
1275 */
1276 return 0;
1277
1278 switch (addr->ss_family) {
1279 case AF_INET:
1280 sum = ntohl(TOCIN(addr)->sin_addr.s_addr);
1281 break;
1282
1283 case AF_INET6: {
1284 const unsigned char *byte = TOCIN6(addr)->sin6_addr.s6_addr;
1285 const size_t len = sizeof(TOCIN6(addr)->sin6_addr.s6_addr),
1286 digits_wanted = 4;
1287 ssize_t i;
1288 size_t digits_summed;
1289
1290 sum = 0;
1291 digits_summed = 0;
1292 i = len - 1;
1293
1294 while (i >= 0 && digits_summed < digits_wanted) {
1295 if (byte[i] != 0) {
1296 sum += byte[i];
1297 ++digits_summed;
1298 }
1299
1300 --i;
1301 }
1302
1303 break;
1304 }
1305
1306 default:
1307 SERRX(addr->ss_family);
1308 }
1309
1310 return sum % size;
1311 }
1312
1313 dnsinfo_t *
getoldest(starti)1314 getoldest(starti)
1315 const size_t starti;
1316 {
1317 dnsinfo_t *oldest;
1318 size_t i;
1319
1320 for (i = starti, oldest = NULL; i < SOCKD_HOSTCACHE; ++i) {
1321 if (oldest == NULL
1322 || hostcache[i].written < oldest->written)
1323 oldest = &hostcache[i];
1324 }
1325
1326 SASSERTX(oldest != NULL);
1327 return oldest;
1328 }
1329
1330 static int
name_matches_cache(ce,name,service,hints)1331 name_matches_cache(ce, name, service, hints)
1332 const dnsinfo_t *ce; /* CacheEntry. */
1333 const char *name;
1334 const char *service;
1335 const struct addrinfo *hints;
1336 {
1337 if (ce->allocated && ce->key == id_name) {
1338 if (hints == NULL) {
1339 if (ce->data.getaddr.hints != NULL)
1340 return 0;
1341 }
1342 else {
1343 if (ce->data.getaddr.hints == NULL)
1344 return 0;
1345
1346 if (memcmp(ce->data.getaddr.hints, hints, sizeof(*hints)) != 0)
1347 return 0;
1348 }
1349
1350 if (strcasecmp(ce->id.name, name) != 0)
1351 return 0;
1352
1353 if (service == NULL) {
1354 if (*ce->service != NUL)
1355 return 0;
1356 }
1357 else {
1358 if (*ce->service == NUL)
1359 return 0;
1360
1361 if (strcasecmp(ce->service, service) != 0)
1362 return 0;
1363 }
1364
1365 /* all matches */
1366 return 1;
1367 }
1368
1369 return 0;
1370 }
1371
1372 static int
addr_matches_cache(ce,addr,flags)1373 addr_matches_cache(ce, addr, flags)
1374 const dnsinfo_t *ce; /* cache entry. */
1375 const struct sockaddr *addr;
1376 const int flags;
1377 {
1378 if (ce->allocated
1379 && ce->data.getname.flags == flags
1380 && ce->key == id_addr
1381 && sockaddrareeq(&ce->id.addr, TOCSS(addr), 0))
1382 return 1;
1383
1384 return 0;
1385 }
1386
1387 static int
addrisinlist(addr,ailist,ailist_last)1388 addrisinlist(addr, ailist, ailist_last)
1389 const struct sockaddr *addr;
1390 struct addrinfo *ailist;
1391 struct addrinfo *ailist_last;
1392 {
1393 const char *function = "addrisinlist()";
1394 struct addrinfo *ai_next_original = ailist_last->ai_next;
1395 int isinlist;
1396
1397 ailist_last->ai_next = NULL;
1398
1399 switch(addr->sa_family) {
1400 case AF_INET: {
1401 struct in_addr mask;/* not const due to Solaris's "struct in struct" */
1402
1403 mask.s_addr = htonl(IPV4_FULLNETMASK);
1404
1405 isinlist = ipv4_addrisinlist(&TOCIN(addr)->sin_addr, &mask, ailist)
1406 == NULL ? 0 : 1;
1407 break;
1408 }
1409
1410 case AF_INET6: {
1411 const unsigned int mask = { IPV6_NETMASKBITS };
1412
1413 isinlist = ipv6_addrisinlist(&TOCIN6(addr)->sin6_addr, mask, ailist)
1414 == NULL ? 0 : 1;
1415 break;
1416 }
1417
1418 default:
1419 slog(PRERELEASE ? LOG_NOTICE /* does this ever happen? */ : LOG_DEBUG,
1420 "%s: unknown/unused sa_family %d in addrinfo struct",
1421 function, addr->sa_family);
1422
1423 isinlist = 1;
1424 }
1425
1426 ailist_last->ai_next = ai_next_original;
1427
1428 return isinlist;
1429 }
1430 #endif /* !SOCKS_CLIENT */
1431