getaddrinfo.c 19.1 KB
Newer Older
1
2
3
/*****************************************************************************
 * getaddrinfo.c: getaddrinfo/getnameinfo replacement functions
 *****************************************************************************
4
 * Copyright (C) 2005 the VideoLAN team
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 * Copyright (C) 2002-2004 Rémi Denis-Courmont
 * $Id$
 *
 * Author: Rémi Denis-Courmont <rem # videolan.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

#include <vlc/vlc.h>

#include <stddef.h> /* size_t */
#include <string.h> /* strncpy(), strlen(), memcpy(), memset(), strchr() */
#include <stdlib.h> /* malloc(), free(), strtoul() */
#include <sys/types.h>
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#include <errno.h>

39
#if defined( WIN32 ) || defined( UNDER_CE )
40
41
42
#   if defined(UNDER_CE) && defined(sockaddr_storage)
#       undef sockaddr_storage
#   endif
43
44
#   include <winsock2.h>
#   include <ws2tcpip.h>
45
#else
46
47
48
49
50
51
52
53
54
55
#   include <sys/socket.h>
#   include <netinet/in.h>
#   ifdef HAVE_ARPA_INET_H
#       include <arpa/inet.h>
#   endif
#   include <netdb.h>
#endif

#ifdef HAVE_UNISTD_H
#   include <unistd.h>
56
57
#endif

58
59
#include "network.h"

60
#ifndef NO_ADDRESS
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
61
#   define NO_ADDRESS  NO_DATA
62
63
#endif
#ifndef INADDR_NONE
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
64
#   define INADDR_NONE 0xFFFFFFFF
65
66
#endif
#ifndef AF_UNSPEC
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
67
#   define AF_UNSPEC   0
Eric Petit's avatar
Eric Petit committed
68
#endif
69

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
70
71
72
#define _NI_MASK (NI_NUMERICHOST|NI_NUMERICSERV|NI_NOFQDN|NI_NAMEREQD|\
                  NI_DGRAM)
#define _AI_MASK (AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST)
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224


#ifndef HAVE_GAI_STRERROR
static struct
{
    int code;
    const char *msg;
} const __gai_errlist[] =
{
    { 0,              "Error 0" },
    { EAI_BADFLAGS,   "Invalid flag used" },
    { EAI_NONAME,     "Host or service not found" },
    { EAI_AGAIN,      "Temporary name service failure" },
    { EAI_FAIL,       "Non-recoverable name service failure" },
    { EAI_NODATA,     "No data for host name" },
    { EAI_FAMILY,     "Unsupported address family" },
    { EAI_SOCKTYPE,   "Unsupported socket type" },
    { EAI_SERVICE,    "Incompatible service for socket type" },
    { EAI_ADDRFAMILY, "Unavailable address family for host name" },
    { EAI_MEMORY,     "Memory allocation failure" },
    { EAI_SYSTEM,     "System error" },
    { 0,              NULL }
};

static const char *__gai_unknownerr = "Unrecognized error number";

/****************************************************************************
 * Converts an EAI_* error code into human readable english text.
 ****************************************************************************/
const char *vlc_gai_strerror( int errnum )
{
    int i;

    for (i = 0; __gai_errlist[i].msg != NULL; i++)
        if (errnum == __gai_errlist[i].code)
            return __gai_errlist[i].msg;

    return __gai_unknownerr;
}
# undef _EAI_POSITIVE_MAX
#else /* ifndef HAVE_GAI_STRERROR */
const char *vlc_gai_strerror( int errnum )
{
    return gai_strerror( errnum );
}
#endif

#if !(defined (HAVE_GETNAMEINFO) && defined (HAVE_GETADDRINFO))
/*
 * Converts the current herrno error value into an EAI_* error code.
 * That error code is normally returned by getnameinfo() or getaddrinfo().
 */
static int
gai_error_from_herrno( void )
{
    switch(h_errno)
    {
        case HOST_NOT_FOUND:
            return EAI_NONAME;

        case NO_ADDRESS:
# if (NO_ADDRESS != NO_DATA)
        case NO_DATA:
# endif
            return EAI_NODATA;

        case NO_RECOVERY:
            return EAI_FAIL;

        case TRY_AGAIN:
            return EAI_AGAIN;
    }
    return EAI_SYSTEM;
}
#endif /* if !(HAVE_GETNAMEINFO && HAVE_GETADDRINFO) */

#ifndef HAVE_GETNAMEINFO
/*
 * getnameinfo() non-thread-safe IPv4-only implementation,
 * Address-family-independant address to hostname translation
 * (reverse DNS lookup in case of IPv4).
 *
 * This is meant for use on old IP-enabled systems that are not IPv6-aware,
 * and probably do not have getnameinfo(), but have the old gethostbyaddr()
 * function.
 *
 * GNU C library 2.0.x is known to lack this function, even though it defines
 * getaddrinfo().
 */
static int
__getnameinfo( const struct sockaddr *sa, socklen_t salen,
               char *host, int hostlen, char *serv, int servlen, int flags )
{
    if (((unsigned)salen < sizeof (struct sockaddr_in))
     || (sa->sa_family != AF_INET))
        return EAI_FAMILY;
    else if (flags & (~_NI_MASK))
        return EAI_BADFLAGS;
    else
    {
        const struct sockaddr_in *addr;

        addr = (const struct sockaddr_in *)sa;

        if (host != NULL)
        {
            int solved = 0;

            /* host name resolution */
            if (!(flags & NI_NUMERICHOST))
            {
                struct hostent *hent;

                hent = gethostbyaddr ((const void*)&addr->sin_addr,
                                      4, AF_INET);

                if (hent != NULL)
                {
                    strncpy (host, hent->h_name, hostlen);
                    host[hostlen - 1] = '\0';

                    /*
                     * only keep first part of hostname
                     * if user don't want fully qualified
                     * domain name
                     */
                    if (flags & NI_NOFQDN)
                    {
                        char *ptr;

                        ptr = strchr (host, '.');
                        if (ptr != NULL)
                            *ptr = 0;
                    }

                    solved = 1;
                }
                else if (flags & NI_NAMEREQD)
                    return gai_error_from_herrno ();
            }

            if (!solved)
            {
                /* inet_ntoa() can't fail */
                strncpy (host, inet_ntoa (addr->sin_addr), hostlen);
                host[hostlen - 1] = '\0';
            }
        }

        if (serv != NULL)
        {
            struct servent *sent = NULL;
Eric Petit's avatar
Eric Petit committed
225
226

#ifndef SYS_BEOS /* No getservbyport() */
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
            int solved = 0;

            /* service name resolution */
            if (!(flags & NI_NUMERICSERV))
            {

                sent = getservbyport(addr->sin_port,
                                     (flags & NI_DGRAM)
                                     ? "udp" : "tcp");
                if (sent != NULL)
                {
                    strncpy (serv, sent->s_name, servlen);
                    serv[servlen - 1] = 0;
                    solved = 1;
                }
            }
Eric Petit's avatar
Eric Petit committed
243
244
245
#else
            sent = NULL;
#endif
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
            if (sent == NULL)
            {
                snprintf (serv, servlen, "%u",
                          (unsigned int)ntohs (addr->sin_port));
                serv[servlen - 1] = '\0';
            }
        }
    }
    return 0;
}

#endif /* if !HAVE_GETNAMEINFO */


#ifndef HAVE_GETADDRINFO
/*
 * This functions must be used to free the memory allocated by getaddrinfo().
 */
static void
__freeaddrinfo (struct addrinfo *res)
{
    if (res != NULL)
    {
        if (res->ai_canonname != NULL)
            free (res->ai_canonname);
        if (res->ai_addr != NULL)
            free (res->ai_addr);
        if (res->ai_next != NULL)
            free (res->ai_next);
        free (res);
    }
}


/*
 * Internal function that builds an addrinfo struct.
 */
static struct addrinfo *
makeaddrinfo (int af, int type, int proto,
              const struct sockaddr *addr, size_t addrlen,
              const char *canonname)
{
    struct addrinfo *res;

    res = (struct addrinfo *)malloc (sizeof (struct addrinfo));
    if (res != NULL)
    {
        res->ai_flags = 0;
        res->ai_family = af;
        res->ai_socktype = type;
        res->ai_protocol = proto;
        res->ai_addrlen = addrlen;
        res->ai_addr = malloc (addrlen);
        res->ai_canonname = NULL;
        res->ai_next = NULL;

        if (res->ai_addr != NULL)
        {
            memcpy (res->ai_addr, addr, addrlen);

            if (canonname != NULL)
            {
                res->ai_canonname = strdup (canonname);
                if (res->ai_canonname != NULL)
                    return res; /* success ! */
            }
            else
                return res;
        }
    }
    /* failsafe */
Eric Petit's avatar
Eric Petit committed
317
    vlc_freeaddrinfo (res);
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
    return NULL;
}


static struct addrinfo *
makeipv4info (int type, int proto, u_long ip, u_short port, const char *name)
{
    struct sockaddr_in addr;

    memset (&addr, 0, sizeof (addr));
    addr.sin_family = AF_INET;
# ifdef HAVE_SA_LEN
    addr.sin_len = sizeof (addr);
# endif
    addr.sin_port = port;
    addr.sin_addr.s_addr = ip;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
335
    return makeaddrinfo (AF_INET, type, proto,
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
                         (struct sockaddr*)&addr, sizeof (addr), name);
}


/*
 * getaddrinfo() non-thread-safe IPv4-only implementation
 * Address-family-independant hostname to address resolution.
 *
 * This is meant for IPv6-unaware systems that do probably not provide
 * getaddrinfo(), but still have old function gethostbyname().
 *
 * Only UDP and TCP over IPv4 are supported here.
 */
static int
__getaddrinfo (const char *node, const char *service,
               const struct addrinfo *hints, struct addrinfo **res)
{
    struct addrinfo *info;
    u_long ip;
    u_short port;
    int protocol = 0, flags = 0;
    const char *name = NULL;

    if (hints != NULL)
    {
        flags = hints->ai_flags;

        if (flags & ~_AI_MASK)
            return EAI_BADFLAGS;
        /* only accept AF_INET and AF_UNSPEC */
        if (hints->ai_family && (hints->ai_family != AF_INET))
            return EAI_FAMILY;

        /* protocol sanity check */
        switch (hints->ai_socktype)
        {
            case SOCK_STREAM:
                protocol = IPPROTO_TCP;
                break;

            case SOCK_DGRAM:
                protocol = IPPROTO_UDP;
                break;

Eric Petit's avatar
Eric Petit committed
380
#ifndef SYS_BEOS
381
            case SOCK_RAW:
Eric Petit's avatar
Eric Petit committed
382
#endif
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
            case 0:
                break;

            default:
                return EAI_SOCKTYPE;
        }
        if (hints->ai_protocol && protocol
         && (protocol != hints->ai_protocol))
            return EAI_SERVICE;
    }

    *res = NULL;

    /* default values */
    if (node == NULL)
    {
        if (flags & AI_PASSIVE)
            ip = htonl (INADDR_ANY);
        else
            ip = htonl (INADDR_LOOPBACK);
    }
    else
    if ((ip = inet_addr (node)) == INADDR_NONE)
    {
        struct hostent *entry = NULL;

        /* hostname resolution */
        if (!(flags & AI_NUMERICHOST))
            entry = gethostbyname (node);

        if (entry == NULL)
            return EAI_NONAME;

        if ((entry->h_length != 4) || (entry->h_addrtype != AF_INET))
            return EAI_FAMILY;

        ip = *((u_long *) entry->h_addr);
        if (flags & AI_CANONNAME)
            name = entry->h_name;
    }

    if ((flags & AI_CANONNAME) && (name == NULL))
        name = node;

    /* service resolution */
    if (service == NULL)
        port = 0;
    else
    {
        long d;
        char *end;

        d = strtoul (service, &end, 0);
        if (end[0] /* service is not a number */
         || (d > 65535))
        {
            struct servent *entry;
            const char *protoname;

            switch (protocol)
            {
                case IPPROTO_TCP:
                    protoname = "tcp";
                    break;

                case IPPROTO_UDP:
                    protoname = "udp";
                    break;

                default:
                    protoname = NULL;
            }

            entry = getservbyname (service, protoname);
            if (entry == NULL)
                return EAI_SERVICE;

            port = entry->s_port;
        }
        else
            port = htons ((u_short)d);
    }

    /* building results... */
    if ((!protocol) || (protocol == IPPROTO_UDP))
    {
        info = makeipv4info (SOCK_DGRAM, IPPROTO_UDP, ip, port, name);
        if (info == NULL)
        {
            errno = ENOMEM;
            return EAI_SYSTEM;
        }
        if (flags & AI_PASSIVE)
            info->ai_flags |= AI_PASSIVE;
        *res = info;
    }
    if ((!protocol) || (protocol == IPPROTO_TCP))
    {
        info = makeipv4info (SOCK_STREAM, IPPROTO_TCP, ip, port, name);
        if (info == NULL)
        {
            errno = ENOMEM;
            return EAI_SYSTEM;
        }
        info->ai_next = *res;
        if (flags & AI_PASSIVE)
            info->ai_flags |= AI_PASSIVE;
        *res = info;
    }

    return 0;
}
#endif /* if !HAVE_GETADDRINFO */


498
int vlc_getnameinfo( const struct sockaddr *sa, int salen,
499
                     char *host, int hostlen, int *portnum, int flags )
500
{
501
502
    char psz_servbuf[6], *psz_serv;
    int i_servlen, i_val;
503
504
505
506
507
508
509
510
511
512
#ifdef WIN32
    /*
     * Here is the kind of kludge you need to keep binary compatibility among
     * varying OS versions...
     */
    typedef int (CALLBACK * GETNAMEINFO) ( const struct sockaddr*, socklen_t,
                                           char*, DWORD, char*, DWORD, int );
    HINSTANCE wship6_module;
    GETNAMEINFO ws2_getnameinfo;
#endif
513
514
515
516
517
518
519
520
521
522
523
524

    flags |= NI_NUMERICSERV;
    if( portnum != NULL )
    {
        psz_serv = psz_servbuf;
        i_servlen = sizeof( psz_servbuf );
    }
    else
    {
        psz_serv = NULL;
        i_servlen = 0;
    }
525
#ifdef WIN32    
526
527
528
529
530
531
532
533
    wship6_module = LoadLibrary( "wship6.dll" );
    if( wship6_module != NULL )
    {
        ws2_getnameinfo = (GETNAMEINFO)GetProcAddress( wship6_module,
                                                       "getnameinfo" );

        if( ws2_getnameinfo != NULL )
        {
534
535
            i_val = ws2_getnameinfo( sa, salen, host, hostlen, psz_serv,
                                     i_servlen, flags );
536
            FreeLibrary( wship6_module );
537
538
539

            if( portnum != NULL )
                *portnum = atoi( psz_serv );
540
541
542
543
544
545
546
            return i_val;
        }
            
        FreeLibrary( wship6_module );
    }
#endif
#if HAVE_GETNAMEINFO
547
548
    i_val = getnameinfo( sa, salen, host, hostlen, psz_serv, i_servlen,
                         flags );
549
#else
550
    {
551
552
# ifdef HAVE_USABLE_MUTEX_THAT_DONT_NEED_LIBVLC_POINTER
        static vlc_value_t lock;
553
554
    
        /* my getnameinfo implementation is not thread-safe as it uses
555
         * gethostbyaddr and the likes */
556
        vlc_mutex_lock( lock.p_address );
557
#else
558
//# warning FIXME : This is not thread-safe!
559
#endif
560
561
        i_val = __getnameinfo( sa, salen, host, hostlen, psz_serv, i_servlen,
                               flags );
562
# ifdef HAVE_USABLE_MUTEX_THAT_DONT_NEED_LIBVLC_POINTER
563
        vlc_mutex_unlock( lock.p_address );
564
# endif
565
566
    }
#endif
567

568
569
    if( portnum != NULL )
        *portnum = atoi( psz_serv );
570
571
572
573
574
575
576

    return i_val;
}


/* TODO: support for setting sin6_scope_id */
int vlc_getaddrinfo( vlc_object_t *p_this, const char *node,
577
                     int i_port, const struct addrinfo *p_hints,
578
579
580
                     struct addrinfo **res )
{
    struct addrinfo hints;
581
582
583
584
585
586
587
588
589
590
591
592
593
594
    char psz_buf[NI_MAXHOST], *psz_node, psz_service[6];

    /*
     * In VLC, we always use port number as integer rather than strings
     * for historical reasons (and portability).
     */
    if( ( i_port > 65535 ) || ( i_port < 0 ) )
    {
        msg_Err( p_this, "invalid port number %d specified", i_port );
        return EAI_SERVICE;
    }

    /* cannot overflow */
    snprintf( psz_service, 6, "%d", i_port );
595
596
597
598
599
600
601

    /* Check if we have to force ipv4 or ipv6 */
    if( p_hints == NULL )
        memset( &hints, 0, sizeof( hints ) );
    else
        memcpy( &hints, p_hints, sizeof( hints ) );

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
602
    if( hints.ai_family == AF_UNSPEC )
603
604
605
606
607
608
    {
        vlc_value_t val;

        var_Create( p_this, "ipv4", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
        var_Get( p_this, "ipv4", &val );
        if( val.b_bool )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
609
            hints.ai_family = AF_INET;
610

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
611
#ifdef AF_INET6
612
613
614
        var_Create( p_this, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
        var_Get( p_this, "ipv6", &val );
        if( val.b_bool )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
615
            hints.ai_family = AF_INET6;
Eric Petit's avatar
Eric Petit committed
616
#endif
617
618
619
620
621
622
623
624
    }

    /* 
     * VLC extensions :
     * - accept "" as NULL
     * - ignore square brackets
     */
    if( ( node == NULL ) || (node[0] == '\0' ) )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
625
    {
626
        psz_node = NULL;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
627
    }
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
    else
    {
        strncpy( psz_buf, node, NI_MAXHOST );
        psz_buf[NI_MAXHOST - 1] = '\0';

        psz_node = psz_buf;

        if( psz_buf[0] == '[' )
        {
            char *ptr;

            ptr = strrchr( psz_buf, ']' );
            if( ( ptr != NULL ) && (ptr[1] == '\0' ) )
            {
                *ptr = '\0';
                psz_node++;
            }
        }
    }

#ifdef WIN32
    {
650
651
652
653
654
655
656
657
        typedef int (CALLBACK * GETADDRINFO) ( const char *, const char *,
                                            const struct addrinfo *,
                                            struct addrinfo ** );
        HINSTANCE wship6_module;
        GETADDRINFO ws2_getaddrinfo;
         
        wship6_module = LoadLibrary( "wship6.dll" );
        if( wship6_module != NULL )
658
        {
659
660
            ws2_getaddrinfo = (GETADDRINFO)GetProcAddress( wship6_module,
                                                        "getaddrinfo" );
661

662
663
664
            if( ws2_getaddrinfo != NULL )
            {
                int i_ret;
665

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
666
                i_ret = ws2_getaddrinfo( psz_node, psz_service, &hints, res );
667
668
669
670
671
672
                FreeLibrary( wship6_module ); /* is this wise ? */
                return i_ret;
            }

            FreeLibrary( wship6_module );
        }
673
674
675
    }
#endif
#if HAVE_GETADDRINFO
676
    return getaddrinfo( psz_node, psz_service, &hints, res );
677
678
679
680
681
682
683
684
685
686
#else
{
    int i_ret;

    vlc_value_t lock;

    var_Create( p_this->p_libvlc, "getaddrinfo_mutex", VLC_VAR_MUTEX );
    var_Get( p_this->p_libvlc, "getaddrinfo_mutex", &lock );
    vlc_mutex_lock( lock.p_address );

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
687
    i_ret = __getaddrinfo( psz_node, psz_service, &hints, res );
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
    vlc_mutex_unlock( lock.p_address );
    return i_ret;
}
#endif
}


void vlc_freeaddrinfo( struct addrinfo *infos )
{
#ifdef WIN32
    typedef void (CALLBACK * FREEADDRINFO) ( struct addrinfo * );
    HINSTANCE wship6_module;
    FREEADDRINFO ws2_freeaddrinfo;
     
    wship6_module = LoadLibrary( "wship6.dll" );
    if( wship6_module != NULL )
    {
        ws2_freeaddrinfo = (FREEADDRINFO)GetProcAddress( wship6_module,
                                                         "freeaddrinfo" );

        /*
         * NOTE: it is assumed that wship6.dll defines either both
         * getaddrinfo and freeaddrinfo or none of them.
         */
        if( ws2_freeaddrinfo != NULL )
        {
            ws2_freeaddrinfo( infos );
            FreeLibrary( wship6_module );
            return;
        }

        FreeLibrary( wship6_module );
    }
#endif
#ifdef HAVE_GETADDRINFO
    freeaddrinfo( infos );
#else
    __freeaddrinfo( infos );
#endif
}