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