1 /*
2  *   stunnel       TLS offloading and load-balancing proxy
3  *   Copyright (C) 1998-2021 Michal Trojnara <Michal.Trojnara@stunnel.org>
4  *
5  *   This program is free software; you can redistribute it and/or modify it
6  *   under the terms of the GNU General Public License as published by the
7  *   Free Software Foundation; either version 2 of the License, or (at your
8  *   option) any later version.
9  *
10  *   This program is distributed in the hope that it will be useful,
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  *   See the GNU General Public License for more details.
14  *
15  *   You should have received a copy of the GNU General Public License along
16  *   with this program; if not, see <http://www.gnu.org/licenses>.
17  *
18  *   Linking stunnel statically or dynamically with other modules is making
19  *   a combined work based on stunnel. Thus, the terms and conditions of
20  *   the GNU General Public License cover the whole combination.
21  *
22  *   In addition, as a special exception, the copyright holder of stunnel
23  *   gives you permission to combine stunnel with free software programs or
24  *   libraries that are released under the GNU LGPL and with code included
25  *   in the standard release of OpenSSL under the OpenSSL License (or
26  *   modified versions of such code, with unchanged license). You may copy
27  *   and distribute such a system following the terms of the GNU GPL for
28  *   stunnel and the licenses of the other code concerned.
29  *
30  *   Note that people who make modified versions of stunnel are not obligated
31  *   to grant this special exception for their modified versions; it is their
32  *   choice whether to do so. The GNU General Public License gives permission
33  *   to release a modified version without this exception; this exception
34  *   also makes it possible to release a modified version which carries
35  *   forward this exception.
36  */
37 
38 #include "common.h"
39 #include "prototypes.h"
40 
41 /**************************************** prototypes */
42 
43 #if defined(USE_WIN32) && !defined(_WIN32_WCE)
44 NOEXPORT int get_ipv6(LPTSTR);
45 #endif
46 NOEXPORT void addrlist2addr(SOCKADDR_UNION *, SOCKADDR_LIST *);
47 NOEXPORT void addrlist_reset(SOCKADDR_LIST *);
48 
49 #ifndef HAVE_GETADDRINFO
50 
51 #ifndef EAI_MEMORY
52 #define EAI_MEMORY 1
53 #endif
54 #ifndef EAI_NONAME
55 #define EAI_NONAME 2
56 #endif
57 #ifndef EAI_SERVICE
58 #define EAI_SERVICE 8
59 #endif
60 
61 /* rename some potentially locally shadowed declarations */
62 #define getaddrinfo     local_getaddrinfo
63 #define freeaddrinfo    local_freeaddrinfo
64 
65 #ifndef HAVE_STRUCT_ADDRINFO
66 struct addrinfo {
67     int ai_flags;
68     int ai_family;
69     int ai_socktype;
70     int ai_protocol;
71     int ai_addrlen;
72     struct sockaddr *ai_addr;
73     char *ai_canonname;
74     struct addrinfo *ai_next;
75 };
76 #endif
77 
78 #ifndef AI_PASSIVE
79 #define AI_PASSIVE 1
80 #endif
81 
82 NOEXPORT int getaddrinfo(const char *, const char *,
83     const struct addrinfo *, struct addrinfo **);
84 NOEXPORT int alloc_addresses(struct hostent *, const struct addrinfo *,
85     u_short port, struct addrinfo **, struct addrinfo **);
86 NOEXPORT void freeaddrinfo(struct addrinfo *);
87 
88 #endif /* !defined HAVE_GETADDRINFO */
89 
90 /**************************************** resolver initialization */
91 
92 #if defined(USE_WIN32) && !defined(_WIN32_WCE)
93 GETADDRINFO s_getaddrinfo;
94 FREEADDRINFO s_freeaddrinfo;
95 GETNAMEINFO s_getnameinfo;
96 #endif
97 
resolver_init()98 void resolver_init() {
99 #if defined(USE_WIN32) && !defined(_WIN32_WCE)
100     if(get_ipv6(TEXT("ws2_32.dll"))) /* IPv6 in Windows XP or higher */
101         return;
102     if(get_ipv6(TEXT("wship6.dll"))) /* experimental IPv6 for Windows 2000 */
103         return;
104     /* fall back to the built-in emulation */
105 #endif
106 }
107 
108 #if defined(USE_WIN32) && !defined(_WIN32_WCE)
109 #ifdef __GNUC__
110 #pragma GCC diagnostic push
111 #pragma GCC diagnostic ignored "-Wpragmas"
112 #pragma GCC diagnostic ignored "-Wcast-function-type"
113 #endif /* __GNUC__ */
get_ipv6(LPTSTR file)114 NOEXPORT int get_ipv6(LPTSTR file) {
115     HINSTANCE handle;
116 
117     handle=LoadLibrary(file);
118     if(!handle)
119         return 0;
120     s_getaddrinfo=(GETADDRINFO)GetProcAddress(handle, "getaddrinfo");
121     s_freeaddrinfo=(FREEADDRINFO)GetProcAddress(handle, "freeaddrinfo");
122     s_getnameinfo=(GETNAMEINFO)GetProcAddress(handle, "getnameinfo");
123     if(!s_getaddrinfo || !s_freeaddrinfo || !s_getnameinfo) {
124         s_getaddrinfo=NULL;
125         s_freeaddrinfo=NULL;
126         s_getnameinfo=NULL;
127         FreeLibrary(handle);
128         return 0;
129     }
130     return 1; /* IPv6 detected -> OK */
131 }
132 #ifdef __GNUC__
133 #pragma GCC diagnostic pop
134 #endif /* __GNUC__ */
135 #endif
136 
137 /**************************************** stunnel resolver API */
138 
name2addr(SOCKADDR_UNION * addr,char * name,int passive)139 unsigned name2addr(SOCKADDR_UNION *addr, char *name, int passive) {
140     SOCKADDR_LIST *addr_list;
141     unsigned retval;
142 
143     addr_list=str_alloc(sizeof(SOCKADDR_LIST));
144     addrlist_clear(addr_list, passive);
145     retval=name2addrlist(addr_list, name);
146     if(retval)
147         addrlist2addr(addr, addr_list);
148     str_free(addr_list->addr);
149     str_free(addr_list);
150     return retval;
151 }
152 
hostport2addr(SOCKADDR_UNION * addr,char * host_name,char * port_name,int passive)153 unsigned hostport2addr(SOCKADDR_UNION *addr,
154         char *host_name, char *port_name, int passive) {
155     SOCKADDR_LIST *addr_list;
156     unsigned num;
157 
158     addr_list=str_alloc(sizeof(SOCKADDR_LIST));
159     addrlist_clear(addr_list, passive);
160     num=hostport2addrlist(addr_list, host_name, port_name);
161     if(num)
162         addrlist2addr(addr, addr_list);
163     str_free(addr_list->addr);
164     str_free(addr_list);
165     return num;
166 }
167 
addrlist2addr(SOCKADDR_UNION * addr,SOCKADDR_LIST * addr_list)168 NOEXPORT void addrlist2addr(SOCKADDR_UNION *addr, SOCKADDR_LIST *addr_list) {
169     unsigned i;
170 
171     for(i=0; i<addr_list->num; ++i) { /* find the first IPv4 address */
172         if(addr_list->addr[i].in.sin_family==AF_INET) {
173             memcpy(addr, &addr_list->addr[i], sizeof(SOCKADDR_UNION));
174             return;
175         }
176     }
177 #ifdef USE_IPv6
178     for(i=0; i<addr_list->num; ++i) { /* find the first IPv6 address */
179         if(addr_list->addr[i].in.sin_family==AF_INET6) {
180             memcpy(addr, &addr_list->addr[i], sizeof(SOCKADDR_UNION));
181             return;
182         }
183     }
184 #endif
185     /* copy the first address resolved (currently AF_UNIX) */
186     memcpy(addr, &addr_list->addr[0], sizeof(SOCKADDR_UNION));
187 }
188 
name2addrlist(SOCKADDR_LIST * addr_list,char * name)189 unsigned name2addrlist(SOCKADDR_LIST *addr_list, char *name) {
190     char *tmp, *host_name, *port_name;
191     unsigned num;
192 
193     /* first check if this is a UNIX socket */
194 #ifdef HAVE_STRUCT_SOCKADDR_UN
195     if(*name=='/') {
196         if(offsetof(struct sockaddr_un, sun_path)+strlen(name)+1
197                 > sizeof(struct sockaddr_un)) {
198             s_log(LOG_ERR, "Unix socket path is too long");
199             return 0; /* no results */
200         }
201         addr_list->addr=str_realloc_detached(addr_list->addr,
202             (addr_list->num+1)*sizeof(SOCKADDR_UNION));
203         addr_list->addr[addr_list->num].un.sun_family=AF_UNIX;
204         strcpy(addr_list->addr[addr_list->num].un.sun_path, name);
205         ++(addr_list->num);
206         return 1; /* ok - return the number of new addresses */
207     }
208 #endif
209 
210     /* setup host_name and port_name */
211     tmp=str_dup(name);
212     port_name=strrchr(tmp, ':');
213     if(port_name) {
214         host_name=tmp;
215         *port_name++='\0';
216     } else { /* no ':' - use default host IP */
217         host_name=NULL;
218         port_name=tmp;
219     }
220 
221     /* fill addr_list structure */
222     num=hostport2addrlist(addr_list, host_name, port_name);
223     str_free(tmp);
224     return num; /* ok - return the number of new addresses */
225 }
226 
hostport2addrlist(SOCKADDR_LIST * addr_list,char * host_name,char * port_name)227 unsigned hostport2addrlist(SOCKADDR_LIST *addr_list,
228         char *host_name, char *port_name) {
229     struct addrinfo hints, *res, *cur;
230     int err, retry=0;
231     unsigned num;
232 
233     memset(&hints, 0, sizeof hints);
234 #if defined(USE_IPv6) || defined(USE_WIN32)
235     hints.ai_family=AF_UNSPEC;
236 #else
237     hints.ai_family=AF_INET;
238 #endif
239     hints.ai_socktype=SOCK_STREAM;
240     hints.ai_protocol=IPPROTO_TCP;
241     hints.ai_flags=0;
242     if(addr_list->passive)
243         hints.ai_flags|=AI_PASSIVE;
244 #ifdef AI_ADDRCONFIG
245     hints.ai_flags|=AI_ADDRCONFIG;
246 #endif
247     for(;;) {
248         res=NULL;
249         err=getaddrinfo(host_name, port_name, &hints, &res);
250         if(!err) /* success */
251             break;
252         if(err==EAI_SERVICE) {
253             s_log(LOG_ERR, "Unknown TCP service \"%s\"", port_name);
254             return 0; /* error */
255         }
256         if(err==EAI_AGAIN && ++retry<=3) {
257             s_log(LOG_DEBUG, "getaddrinfo: EAI_AGAIN received: retrying");
258             s_poll_sleep(1, 0);
259             continue;
260         }
261 #ifdef AI_ADDRCONFIG
262         if(hints.ai_flags&AI_ADDRCONFIG) {
263             hints.ai_flags&=~AI_ADDRCONFIG;
264             continue; /* retry for unconfigured network interfaces */
265         }
266 #endif
267         s_log(LOG_ERR, "Error resolving \"%s\": %s",
268             host_name ? host_name :
269                 (addr_list->passive ? DEFAULT_ANY : DEFAULT_LOOPBACK),
270             s_gai_strerror(err));
271         return 0; /* error */
272     }
273 
274     /* find the number of newly resolved addresses */
275     num=0;
276     for(cur=res; cur; cur=cur->ai_next) {
277         if(cur->ai_addrlen>(int)sizeof(SOCKADDR_UNION)) {
278             s_log(LOG_ERR, "INTERNAL ERROR: ai_addrlen value too big");
279             freeaddrinfo(res);
280             return 0; /* no results */
281         }
282         ++num;
283     }
284 
285     /* append the newly resolved addresses to addr_list->addr */
286     addr_list->addr=str_realloc_detached(addr_list->addr,
287         (addr_list->num+num)*sizeof(SOCKADDR_UNION));
288     for(cur=res; cur; cur=cur->ai_next)
289         memcpy(&addr_list->addr[(addr_list->num)++], cur->ai_addr,
290             (size_t)cur->ai_addrlen);
291 
292     freeaddrinfo(res);
293     return num; /* ok - return the number of new addresses */
294 }
295 
296 /* initialize the structure */
addrlist_clear(SOCKADDR_LIST * addr_list,int passive)297 void addrlist_clear(SOCKADDR_LIST *addr_list, int passive) {
298     addrlist_reset(addr_list);
299     addr_list->names=NULL;
300     addr_list->passive=passive;
301 }
302 
303 /* prepare the structure to resolve new hosts */
addrlist_reset(SOCKADDR_LIST * addr_list)304 NOEXPORT void addrlist_reset(SOCKADDR_LIST *addr_list) {
305     addr_list->num=0;
306     addr_list->addr=NULL;
307     addr_list->start=0;
308     addr_list->parent=addr_list; /* allow a copy to locate its parent */
309 }
310 
addrlist_dup(SOCKADDR_LIST * dst,const SOCKADDR_LIST * src)311 unsigned addrlist_dup(SOCKADDR_LIST *dst, const SOCKADDR_LIST *src) {
312     memcpy(dst, src, sizeof(SOCKADDR_LIST));
313     if(src->num) { /* already resolved */
314         dst->addr=str_alloc_detached(src->num*sizeof(SOCKADDR_UNION));
315         memcpy(dst->addr, src->addr, src->num*sizeof(SOCKADDR_UNION));
316     } else { /* delayed resolver */
317         addrlist_resolve(dst);
318     }
319     return dst->num;
320 }
321 
addrlist_resolve(SOCKADDR_LIST * addr_list)322 unsigned addrlist_resolve(SOCKADDR_LIST *addr_list) {
323     unsigned num=0, rnd=0;
324     NAME_LIST *host;
325 
326     addrlist_reset(addr_list);
327     for(host=addr_list->names; host; host=host->next)
328         num+=name2addrlist(addr_list, host->name);
329     if(num<2) {
330         addr_list->start=0;
331     } else {
332         /* randomize the initial value of round-robin counter */
333         /* ignore the error value and the distribution bias */
334         RAND_bytes((unsigned char *)&rnd, sizeof rnd);
335         addr_list->start=rnd%num;
336     }
337     return num;
338 }
339 
s_ntop(SOCKADDR_UNION * addr,socklen_t addrlen)340 char *s_ntop(SOCKADDR_UNION *addr, socklen_t addrlen) {
341     int err;
342     char *host, *port, *retval;
343 
344     if(addrlen==sizeof(u_short)) /* see UNIX(7) manual for details */
345         return str_dup("unnamed socket");
346     host=str_alloc(256);
347     port=str_alloc(256); /* needs to be long enough for AF_UNIX path */
348     err=getnameinfo(&addr->sa, addrlen,
349         host, 256, port, 256, NI_NUMERICHOST|NI_NUMERICSERV);
350     if(err) {
351         s_log(LOG_ERR, "getnameinfo: %s", s_gai_strerror(err));
352         retval=str_dup("unresolvable address");
353     } else
354         retval=str_printf("%s:%s", host, port);
355     str_free(host);
356     str_free(port);
357     return retval;
358 }
359 
addr_len(const SOCKADDR_UNION * addr)360 socklen_t addr_len(const SOCKADDR_UNION *addr) {
361     switch(addr->sa.sa_family) {
362     case AF_UNSPEC: /* 0 */
363         return 0;
364     case AF_INET: /* 2 (almost universally) */
365         return sizeof(struct sockaddr_in);
366 #ifdef USE_IPv6
367     case AF_INET6:
368         return sizeof(struct sockaddr_in6);
369 #endif
370 #ifdef HAVE_STRUCT_SOCKADDR_UN
371     case AF_UNIX:
372         return sizeof(struct sockaddr_un);
373 #endif
374     }
375     s_log(LOG_ERR, "INTERNAL ERROR: Unknown sa_family: %d",
376         addr->sa.sa_family);
377     return sizeof(SOCKADDR_UNION);
378 }
379 
380 /**************************************** my getaddrinfo() */
381 /* implementation is limited to functionality needed by stunnel */
382 
383 #ifndef HAVE_GETADDRINFO
getaddrinfo(const char * node,const char * service,const struct addrinfo * hints,struct addrinfo ** res)384 NOEXPORT int getaddrinfo(const char *node, const char *service,
385         const struct addrinfo *hints, struct addrinfo **res) {
386     struct hostent *h;
387 #ifndef _WIN32_WCE
388     struct servent *p;
389 #endif
390     u_short port;
391     struct addrinfo *ai;
392     int retval;
393     char *tmpstr;
394 
395     if(!node)
396         node=(hints->ai_flags & AI_PASSIVE) ? DEFAULT_ANY : DEFAULT_LOOPBACK;
397 #if defined(USE_WIN32) && !defined(_WIN32_WCE)
398     if(s_getaddrinfo)
399         return s_getaddrinfo(node, service, hints, res);
400 #endif
401     /* decode service name */
402     port=htons((u_short)strtol(service, &tmpstr, 10));
403     if(tmpstr==service || *tmpstr) { /* not a number */
404 #ifdef _WIN32_WCE
405         return EAI_NONAME;
406 #else /* defined(_WIN32_WCE) */
407         p=getservbyname(service, "tcp");
408         if(!p)
409             return EAI_NONAME;
410         port=(u_short)p->s_port;
411 #endif /* defined(_WIN32_WCE) */
412     }
413 
414     /* allocate addrlist structure */
415     ai=str_alloc(sizeof(struct addrinfo));
416     if(hints)
417         memcpy(ai, hints, sizeof(struct addrinfo));
418 
419     /* try to decode numerical address */
420 #if defined(USE_IPv6) && !defined(USE_WIN32)
421     ai->ai_family=AF_INET6;
422     ai->ai_addrlen=sizeof(struct sockaddr_in6);
423     ai->ai_addr=str_alloc((size_t)ai->ai_addrlen);
424     ai->ai_addr->sa_family=AF_INET6;
425     if(inet_pton(AF_INET6, node,
426             &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr)>0) {
427 #else
428     ai->ai_family=AF_INET;
429     ai->ai_addrlen=sizeof(struct sockaddr_in);
430     ai->ai_addr=str_alloc(ai->ai_addrlen);
431     ai->ai_addr->sa_family=AF_INET;
432     ((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr=inet_addr(node);
433     if(((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr+1) {
434     /* (signed)((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr!=-1 */
435 #endif
436         ((struct sockaddr_in *)ai->ai_addr)->sin_port=port;
437         *res=ai;
438         return 0; /* numerical address resolved */
439     }
440     str_free(ai->ai_addr);
441     str_free(ai);
442 
443     /* not numerical: need to call resolver library */
444     *res=NULL;
445     ai=NULL;
446     CRYPTO_THREAD_write_lock(stunnel_locks[LOCK_INET]);
447 #ifdef HAVE_GETHOSTBYNAME2
448     h=gethostbyname2(node, AF_INET6);
449     if(h) /* some IPv6 addresses found */
450         alloc_addresses(h, hints, port, res, &ai); /* ignore the error */
451 #endif
452     h=gethostbyname(node); /* get list of addresses */
453     if(h)
454         retval=ai ?
455             alloc_addresses(h, hints, port, &ai->ai_next, &ai) :
456             alloc_addresses(h, hints, port, res, &ai);
457     else if(!*res)
458         retval=EAI_NONAME; /* no results */
459     else
460         retval=0;
461 #ifdef HAVE_ENDHOSTENT
462     endhostent();
463 #endif
464     CRYPTO_THREAD_unlock(stunnel_locks[LOCK_INET]);
465     if(retval) { /* error: free allocated memory */
466         freeaddrinfo(*res);
467         *res=NULL;
468     }
469     return retval;
470 }
471 
472 NOEXPORT int alloc_addresses(struct hostent *h, const struct addrinfo *hints,
473         u_short port, struct addrinfo **head, struct addrinfo **tail) {
474     int i;
475     struct addrinfo *ai;
476 
477     /* copy addresses */
478     for(i=0; h->h_addr_list[i]; i++) {
479         ai=str_alloc(sizeof(struct addrinfo));
480         if(hints)
481             memcpy(ai, hints, sizeof(struct addrinfo));
482         ai->ai_next=NULL; /* just in case */
483         if(*tail) { /* list not empty: add a node */
484             (*tail)->ai_next=ai;
485             *tail=ai;
486         } else { /* list empty: create it */
487             *head=ai;
488             *tail=ai;
489         }
490         ai->ai_family=h->h_addrtype;
491 #if defined(USE_IPv6)
492         if(h->h_addrtype==AF_INET6) {
493             ai->ai_addrlen=sizeof(struct sockaddr_in6);
494             ai->ai_addr=str_alloc((size_t)ai->ai_addrlen);
495             memcpy(&((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr,
496                 h->h_addr_list[i], (size_t)h->h_length);
497         } else
498 #endif
499         {
500             ai->ai_addrlen=sizeof(struct sockaddr_in);
501             ai->ai_addr=str_alloc((size_t)ai->ai_addrlen);
502             memcpy(&((struct sockaddr_in *)ai->ai_addr)->sin_addr,
503                 h->h_addr_list[i], (size_t)h->h_length);
504         }
505         ai->ai_addr->sa_family=(u_short)h->h_addrtype;
506         /* offsets of sin_port and sin6_port should be the same */
507         ((struct sockaddr_in *)ai->ai_addr)->sin_port=port;
508     }
509     return 0; /* success */
510 }
511 
512 NOEXPORT void freeaddrinfo(struct addrinfo *current) {
513     struct addrinfo *next;
514 
515 #if defined(USE_WIN32) && !defined(_WIN32_WCE)
516     if(s_freeaddrinfo) {
517         s_freeaddrinfo(current);
518     return;
519     }
520 #endif
521     while(current) {
522         str_free(current->ai_addr);
523         str_free(current->ai_canonname);
524         next=current->ai_next;
525         str_free(current);
526         current=next;
527     }
528 }
529 #endif /* !defined HAVE_GETADDRINFO */
530 
531 /* due to a problem with Mingw32 I decided to define my own gai_strerror() */
532 const char *s_gai_strerror(int err) {
533     switch(err) {
534 #ifdef EAI_BADFLAGS
535         case EAI_BADFLAGS:
536             return "Invalid value for ai_flags (EAI_BADFLAGS)";
537 #endif
538         case EAI_NONAME:
539             return "Neither nodename nor servname known (EAI_NONAME)";
540 #ifdef EAI_AGAIN
541         case EAI_AGAIN:
542             return "Temporary failure in name resolution (EAI_AGAIN)";
543 #endif
544 #ifdef EAI_FAIL
545         case EAI_FAIL:
546             return "Non-recoverable failure in name resolution (EAI_FAIL)";
547 #endif
548 #ifdef EAI_NODATA
549 #if EAI_NODATA!=EAI_NONAME
550         case EAI_NODATA:
551             return "No address associated with nodename (EAI_NODATA)";
552 #endif /* EAI_NODATA!=EAI_NONAME */
553 #endif /* defined EAI_NODATA */
554 #ifdef EAI_FAMILY
555         case EAI_FAMILY:
556             return "ai_family not supported (EAI_FAMILY)";
557 #endif
558 #ifdef EAI_SOCKTYPE
559         case EAI_SOCKTYPE:
560             return "ai_socktype not supported (EAI_SOCKTYPE)";
561 #endif
562 #ifdef EAI_SERVICE
563         case EAI_SERVICE:
564             return "servname is not supported for ai_socktype (EAI_SERVICE)";
565 #endif
566 #ifdef EAI_ADDRFAMILY
567         case EAI_ADDRFAMILY:
568             return "Address family for nodename not supported (EAI_ADDRFAMILY)";
569 #endif /* EAI_ADDRFAMILY */
570         case EAI_MEMORY:
571             return "Memory allocation failure (EAI_MEMORY)";
572 #ifdef EAI_SYSTEM
573         case EAI_SYSTEM:
574             return "System error returned in errno (EAI_SYSTEM)";
575 #endif /* EAI_SYSTEM */
576         default:
577             return "Unknown error";
578     }
579 }
580 
581 /**************************************** my getnameinfo() */
582 /* implementation is limited to functionality needed by stunnel */
583 
584 #ifndef HAVE_GETNAMEINFO
585 int getnameinfo(const struct sockaddr *sa, socklen_t salen,
586     char *host, size_t hostlen, char *serv, size_t servlen, int flags) {
587 
588 #if defined(USE_WIN32) && !defined(_WIN32_WCE)
589     if(s_getnameinfo)
590         return s_getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
591 #endif
592     if(host && hostlen) {
593 #if defined(USE_IPv6) && !defined(USE_WIN32)
594         inet_ntop(sa->sa_family, sa->sa_family==AF_INET6 ?
595                 (void *)&((struct sockaddr_in6 *)sa)->sin6_addr :
596                 (void *)&((struct sockaddr_in *)sa)->sin_addr,
597             host, hostlen);
598 #else /* USE_IPv6 */
599         /* inet_ntoa is not mt-safe */
600         CRYPTO_THREAD_write_lock(stunnel_locks[LOCK_INET]);
601         strncpy(host, inet_ntoa(((struct sockaddr_in *)sa)->sin_addr),
602             hostlen);
603         CRYPTO_THREAD_unlock(stunnel_locks[LOCK_INET]);
604         host[hostlen-1]='\0';
605 #endif /* USE_IPv6 */
606     }
607     if(serv && servlen)
608         sprintf(serv, "%u", ntohs(((struct sockaddr_in *)sa)->sin_port));
609     /* sin_port is in the same place both in sockaddr_in and sockaddr_in6 */
610     /* ignore servlen since it's long enough in stunnel code */
611     return 0;
612 }
613 #endif
614 
615 /* end of resolver.c */
616