]> git.sesse.net Git - vlc/blob - src/misc/getaddrinfo.c
7b9ab13a7224b32d5740aa2318e3d33683708723
[vlc] / src / misc / getaddrinfo.c
1 /*****************************************************************************
2  * getaddrinfo.c: getaddrinfo/getnameinfo replacement functions
3  *****************************************************************************
4  * Copyright (C) 2005 VideoLAN
5  * Copyright (C) 2002-2004 Rémi Denis-Courmont
6  * $Id$
7  *
8  * Author: Rémi Denis-Courmont <rem # videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 #include <vlc/vlc.h>
26 #include "network.h"
27
28 #include <stddef.h> /* size_t */
29 #include <string.h> /* strncpy(), strlen(), memcpy(), memset(), strchr() */
30 #include <stdlib.h> /* malloc(), free(), strtoul() */
31 #include <sys/types.h>
32 #ifdef HAVE_ARPA_INET_H
33 # include <arpa/inet.h>
34 #endif
35 #ifdef HAVE_NETINET_IN_H
36 # include <netinet/in.h>
37 #endif
38 #include <errno.h>
39
40 #if defined (UNDER_CE)
41 # include <winsock.h>
42 #elif defined WIN32
43 # include <winsock2.h>
44 # include <ws2tcpip.h>
45 #else
46 # include <netdb.h>
47 #endif
48
49 #ifdef SYS_BEOS
50 #define NO_ADDRESS  NO_DATA
51 #define PF_INET     AF_INET
52 #define INADDR_NONE 0xFFFFFFFF
53 #define AF_UNSPEC   0
54 #endif
55
56 #define _NI_MASK  (NI_NUMERICHOST|NI_NUMERICSERV|NI_NOFQDN|NI_NAMEREQD|\
57                    NI_DGRAM)
58 # define _AI_MASK (AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST)
59
60
61 #ifndef HAVE_GAI_STRERROR
62 static struct
63 {
64     int code;
65     const char *msg;
66 } const __gai_errlist[] =
67 {
68     { 0,              "Error 0" },
69     { EAI_BADFLAGS,   "Invalid flag used" },
70     { EAI_NONAME,     "Host or service not found" },
71     { EAI_AGAIN,      "Temporary name service failure" },
72     { EAI_FAIL,       "Non-recoverable name service failure" },
73     { EAI_NODATA,     "No data for host name" },
74     { EAI_FAMILY,     "Unsupported address family" },
75     { EAI_SOCKTYPE,   "Unsupported socket type" },
76     { EAI_SERVICE,    "Incompatible service for socket type" },
77     { EAI_ADDRFAMILY, "Unavailable address family for host name" },
78     { EAI_MEMORY,     "Memory allocation failure" },
79     { EAI_SYSTEM,     "System error" },
80     { 0,              NULL }
81 };
82
83 static const char *__gai_unknownerr = "Unrecognized error number";
84
85 /****************************************************************************
86  * Converts an EAI_* error code into human readable english text.
87  ****************************************************************************/
88 const char *vlc_gai_strerror( int errnum )
89 {
90     int i;
91
92     for (i = 0; __gai_errlist[i].msg != NULL; i++)
93         if (errnum == __gai_errlist[i].code)
94             return __gai_errlist[i].msg;
95
96     return __gai_unknownerr;
97 }
98 # undef _EAI_POSITIVE_MAX
99 #else /* ifndef HAVE_GAI_STRERROR */
100 const char *vlc_gai_strerror( int errnum )
101 {
102     return gai_strerror( errnum );
103 }
104 #endif
105
106 #if !(defined (HAVE_GETNAMEINFO) && defined (HAVE_GETADDRINFO))
107 /*
108  * Converts the current herrno error value into an EAI_* error code.
109  * That error code is normally returned by getnameinfo() or getaddrinfo().
110  */
111 static int
112 gai_error_from_herrno( void )
113 {
114     switch(h_errno)
115     {
116         case HOST_NOT_FOUND:
117             return EAI_NONAME;
118
119         case NO_ADDRESS:
120 # if (NO_ADDRESS != NO_DATA)
121         case NO_DATA:
122 # endif
123             return EAI_NODATA;
124
125         case NO_RECOVERY:
126             return EAI_FAIL;
127
128         case TRY_AGAIN:
129             return EAI_AGAIN;
130     }
131     return EAI_SYSTEM;
132 }
133 #endif /* if !(HAVE_GETNAMEINFO && HAVE_GETADDRINFO) */
134
135 #ifndef HAVE_GETNAMEINFO
136 /*
137  * getnameinfo() non-thread-safe IPv4-only implementation,
138  * Address-family-independant address to hostname translation
139  * (reverse DNS lookup in case of IPv4).
140  *
141  * This is meant for use on old IP-enabled systems that are not IPv6-aware,
142  * and probably do not have getnameinfo(), but have the old gethostbyaddr()
143  * function.
144  *
145  * GNU C library 2.0.x is known to lack this function, even though it defines
146  * getaddrinfo().
147  */
148 static int
149 __getnameinfo( const struct sockaddr *sa, socklen_t salen,
150                char *host, int hostlen, char *serv, int servlen, int flags )
151 {
152     if (((unsigned)salen < sizeof (struct sockaddr_in))
153      || (sa->sa_family != AF_INET))
154         return EAI_FAMILY;
155     else if (flags & (~_NI_MASK))
156         return EAI_BADFLAGS;
157     else
158     {
159         const struct sockaddr_in *addr;
160
161         addr = (const struct sockaddr_in *)sa;
162
163         if (host != NULL)
164         {
165             int solved = 0;
166
167             /* host name resolution */
168             if (!(flags & NI_NUMERICHOST))
169             {
170                 struct hostent *hent;
171
172                 hent = gethostbyaddr ((const void*)&addr->sin_addr,
173                                       4, AF_INET);
174
175                 if (hent != NULL)
176                 {
177                     strncpy (host, hent->h_name, hostlen);
178                     host[hostlen - 1] = '\0';
179
180                     /*
181                      * only keep first part of hostname
182                      * if user don't want fully qualified
183                      * domain name
184                      */
185                     if (flags & NI_NOFQDN)
186                     {
187                         char *ptr;
188
189                         ptr = strchr (host, '.');
190                         if (ptr != NULL)
191                             *ptr = 0;
192                     }
193
194                     solved = 1;
195                 }
196                 else if (flags & NI_NAMEREQD)
197                     return gai_error_from_herrno ();
198             }
199
200             if (!solved)
201             {
202                 /* inet_ntoa() can't fail */
203                 strncpy (host, inet_ntoa (addr->sin_addr), hostlen);
204                 host[hostlen - 1] = '\0';
205             }
206         }
207
208         if (serv != NULL)
209         {
210             struct servent *sent = NULL;
211
212 #ifndef SYS_BEOS /* No getservbyport() */
213             int solved = 0;
214
215             /* service name resolution */
216             if (!(flags & NI_NUMERICSERV))
217             {
218
219                 sent = getservbyport(addr->sin_port,
220                                      (flags & NI_DGRAM)
221                                      ? "udp" : "tcp");
222                 if (sent != NULL)
223                 {
224                     strncpy (serv, sent->s_name, servlen);
225                     serv[servlen - 1] = 0;
226                     solved = 1;
227                 }
228             }
229 #else
230             sent = NULL;
231 #endif
232             if (sent == NULL)
233             {
234                 snprintf (serv, servlen, "%u",
235                           (unsigned int)ntohs (addr->sin_port));
236                 serv[servlen - 1] = '\0';
237             }
238         }
239     }
240     return 0;
241 }
242
243 #endif /* if !HAVE_GETNAMEINFO */
244
245
246 #ifndef HAVE_GETADDRINFO
247 /*
248  * This functions must be used to free the memory allocated by getaddrinfo().
249  */
250 static void
251 __freeaddrinfo (struct addrinfo *res)
252 {
253     if (res != NULL)
254     {
255         if (res->ai_canonname != NULL)
256             free (res->ai_canonname);
257         if (res->ai_addr != NULL)
258             free (res->ai_addr);
259         if (res->ai_next != NULL)
260             free (res->ai_next);
261         free (res);
262     }
263 }
264
265
266 /*
267  * Internal function that builds an addrinfo struct.
268  */
269 static struct addrinfo *
270 makeaddrinfo (int af, int type, int proto,
271               const struct sockaddr *addr, size_t addrlen,
272               const char *canonname)
273 {
274     struct addrinfo *res;
275
276     res = (struct addrinfo *)malloc (sizeof (struct addrinfo));
277     if (res != NULL)
278     {
279         res->ai_flags = 0;
280         res->ai_family = af;
281         res->ai_socktype = type;
282         res->ai_protocol = proto;
283         res->ai_addrlen = addrlen;
284         res->ai_addr = malloc (addrlen);
285         res->ai_canonname = NULL;
286         res->ai_next = NULL;
287
288         if (res->ai_addr != NULL)
289         {
290             memcpy (res->ai_addr, addr, addrlen);
291
292             if (canonname != NULL)
293             {
294                 res->ai_canonname = strdup (canonname);
295                 if (res->ai_canonname != NULL)
296                     return res; /* success ! */
297             }
298             else
299                 return res;
300         }
301     }
302     /* failsafe */
303     vlc_freeaddrinfo (res);
304     return NULL;
305 }
306
307
308 static struct addrinfo *
309 makeipv4info (int type, int proto, u_long ip, u_short port, const char *name)
310 {
311     struct sockaddr_in addr;
312
313     memset (&addr, 0, sizeof (addr));
314     addr.sin_family = AF_INET;
315 # ifdef HAVE_SA_LEN
316     addr.sin_len = sizeof (addr);
317 # endif
318     addr.sin_port = port;
319     addr.sin_addr.s_addr = ip;
320
321     return makeaddrinfo (PF_INET, type, proto,
322                          (struct sockaddr*)&addr, sizeof (addr), name);
323 }
324
325
326 /*
327  * getaddrinfo() non-thread-safe IPv4-only implementation
328  * Address-family-independant hostname to address resolution.
329  *
330  * This is meant for IPv6-unaware systems that do probably not provide
331  * getaddrinfo(), but still have old function gethostbyname().
332  *
333  * Only UDP and TCP over IPv4 are supported here.
334  */
335 static int
336 __getaddrinfo (const char *node, const char *service,
337                const struct addrinfo *hints, struct addrinfo **res)
338 {
339     struct addrinfo *info;
340     u_long ip;
341     u_short port;
342     int protocol = 0, flags = 0;
343     const char *name = NULL;
344
345     if (hints != NULL)
346     {
347         flags = hints->ai_flags;
348
349         if (flags & ~_AI_MASK)
350             return EAI_BADFLAGS;
351         /* only accept AF_INET and AF_UNSPEC */
352         if (hints->ai_family && (hints->ai_family != AF_INET))
353             return EAI_FAMILY;
354
355         /* protocol sanity check */
356         switch (hints->ai_socktype)
357         {
358             case SOCK_STREAM:
359                 protocol = IPPROTO_TCP;
360                 break;
361
362             case SOCK_DGRAM:
363                 protocol = IPPROTO_UDP;
364                 break;
365
366 #ifndef SYS_BEOS
367             case SOCK_RAW:
368 #endif
369             case 0:
370                 break;
371
372             default:
373                 return EAI_SOCKTYPE;
374         }
375         if (hints->ai_protocol && protocol
376          && (protocol != hints->ai_protocol))
377             return EAI_SERVICE;
378     }
379
380     *res = NULL;
381
382     /* default values */
383     if (node == NULL)
384     {
385         if (flags & AI_PASSIVE)
386             ip = htonl (INADDR_ANY);
387         else
388             ip = htonl (INADDR_LOOPBACK);
389     }
390     else
391     if ((ip = inet_addr (node)) == INADDR_NONE)
392     {
393         struct hostent *entry = NULL;
394
395         /* hostname resolution */
396         if (!(flags & AI_NUMERICHOST))
397             entry = gethostbyname (node);
398
399         if (entry == NULL)
400             return EAI_NONAME;
401
402         if ((entry->h_length != 4) || (entry->h_addrtype != AF_INET))
403             return EAI_FAMILY;
404
405         ip = *((u_long *) entry->h_addr);
406         if (flags & AI_CANONNAME)
407             name = entry->h_name;
408     }
409
410     if ((flags & AI_CANONNAME) && (name == NULL))
411         name = node;
412
413     /* service resolution */
414     if (service == NULL)
415         port = 0;
416     else
417     {
418         long d;
419         char *end;
420
421         d = strtoul (service, &end, 0);
422         if (end[0] /* service is not a number */
423          || (d > 65535))
424         {
425             struct servent *entry;
426             const char *protoname;
427
428             switch (protocol)
429             {
430                 case IPPROTO_TCP:
431                     protoname = "tcp";
432                     break;
433
434                 case IPPROTO_UDP:
435                     protoname = "udp";
436                     break;
437
438                 default:
439                     protoname = NULL;
440             }
441
442             entry = getservbyname (service, protoname);
443             if (entry == NULL)
444                 return EAI_SERVICE;
445
446             port = entry->s_port;
447         }
448         else
449             port = htons ((u_short)d);
450     }
451
452     /* building results... */
453     if ((!protocol) || (protocol == IPPROTO_UDP))
454     {
455         info = makeipv4info (SOCK_DGRAM, IPPROTO_UDP, ip, port, name);
456         if (info == NULL)
457         {
458             errno = ENOMEM;
459             return EAI_SYSTEM;
460         }
461         if (flags & AI_PASSIVE)
462             info->ai_flags |= AI_PASSIVE;
463         *res = info;
464     }
465     if ((!protocol) || (protocol == IPPROTO_TCP))
466     {
467         info = makeipv4info (SOCK_STREAM, IPPROTO_TCP, ip, port, name);
468         if (info == NULL)
469         {
470             errno = ENOMEM;
471             return EAI_SYSTEM;
472         }
473         info->ai_next = *res;
474         if (flags & AI_PASSIVE)
475             info->ai_flags |= AI_PASSIVE;
476         *res = info;
477     }
478
479     return 0;
480 }
481 #endif /* if !HAVE_GETADDRINFO */
482
483
484 int vlc_getnameinfo( vlc_object_t *p_this, const struct sockaddr *sa, int salen,
485                      char *host, int hostlen, char *serv, int servlen, int flags )
486 {
487 #ifdef WIN32
488     /*
489      * Here is the kind of kludge you need to keep binary compatibility among
490      * varying OS versions...
491      */
492     typedef int (CALLBACK * GETNAMEINFO) ( const struct sockaddr*, socklen_t,
493                                            char*, DWORD, char*, DWORD, int );
494     HINSTANCE wship6_module;
495     GETNAMEINFO ws2_getnameinfo;
496      
497     wship6_module = LoadLibrary( "wship6.dll" );
498     if( wship6_module != NULL )
499     {
500         ws2_getnameinfo = (GETNAMEINFO)GetProcAddress( wship6_module,
501                                                        "getnameinfo" );
502
503         if( ws2_getnameinfo != NULL )
504         {
505             int i_val;
506
507             i_val = ws2_getnameinfo( sa, salen, host, hostlen, serv, servlen,
508                                      flags );
509             FreeLibrary( wship6_module );
510             return i_val;
511         }
512             
513         FreeLibrary( wship6_module );
514     }
515 #endif
516 #if HAVE_GETNAMEINFO
517     return getnameinfo( sa, salen, host, hostlen, serv, servlen, flags );
518 #else
519 {
520     vlc_value_t lock;
521     int i_val;
522
523     /* my getnameinfo implementation is not thread-safe as it uses
524      * gethostbyaddr and the likes */
525     var_Create( p_this->p_libvlc, "getnameinfo_mutex", VLC_VAR_MUTEX );
526     var_Get( p_this->p_libvlc, "getnameinfo_mutex", &lock );
527     vlc_mutex_lock( lock.p_address );
528
529     i_val = __getnameinfo( sa, salen, host, hostlen, serv, servlen, flags );
530     vlc_mutex_unlock( lock.p_address );
531     return i_val;
532 }
533 #endif
534 }
535
536
537 /* TODO: support for setting sin6_scope_id */
538 int vlc_getaddrinfo( vlc_object_t *p_this, const char *node,
539                      const char *service, const struct addrinfo *p_hints,
540                      struct addrinfo **res )
541 {
542     struct addrinfo hints;
543     char psz_buf[NI_MAXHOST], *psz_node;
544
545     /* Check if we have to force ipv4 or ipv6 */
546     if( p_hints == NULL )
547         memset( &hints, 0, sizeof( hints ) );
548     else
549         memcpy( &hints, p_hints, sizeof( hints ) );
550
551     if( hints.ai_family == AF_UNSPEC )
552     {
553         vlc_value_t val;
554
555         var_Create( p_this, "ipv4", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
556         var_Get( p_this, "ipv4", &val );
557         if( val.b_bool )
558             hints.ai_family = AF_INET;
559
560 #ifdef HAVE_INET_PTON
561         var_Create( p_this, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
562         var_Get( p_this, "ipv6", &val );
563         if( val.b_bool )
564             hints.ai_family = AF_INET6;
565 #endif
566     }
567
568     /* 
569      * VLC extensions :
570      * - accept "" as NULL
571      * - ignore square brackets
572      */
573     if( ( node == NULL ) || (node[0] == '\0' ) )
574         psz_node = NULL;
575     else
576     {
577         strncpy( psz_buf, node, NI_MAXHOST );
578         psz_buf[NI_MAXHOST - 1] = '\0';
579
580         psz_node = psz_buf;
581
582         if( psz_buf[0] == '[' )
583         {
584             char *ptr;
585
586             ptr = strrchr( psz_buf, ']' );
587             if( ( ptr != NULL ) && (ptr[1] == '\0' ) )
588             {
589                 *ptr = '\0';
590                 psz_node++;
591             }
592         }
593     }
594
595     if( ( service != NULL ) && ( *service == '\0' ) )
596         /* We could put NULL, but you can't have both node and service NULL */
597         service = "0";
598
599 #ifdef WIN32
600     typedef int (CALLBACK * GETADDRINFO) ( const char *, const char *,
601                                            const struct addrinfo *,
602                                            struct addrinfo ** );
603     HINSTANCE wship6_module;
604     GETADDRINFO ws2_getaddrinfo;
605      
606     wship6_module = LoadLibrary( "wship6.dll" );
607     if( wship6_module != NULL )
608     {
609         ws2_getaddrinfo = (GETADDRINFO)GetProcAddress( wship6_module,
610                                                        "getaddrinfo" );
611
612         if( ws2_getaddrinfo != NULL )
613         {
614             int i_ret;
615
616             i_ret = ws2_getaddrinfo( psz_node, service, &hints, res );
617             FreeLibrary( wship6_module ); /* is this wise ? */
618             return i_ret;
619         }
620
621         FreeLibrary( wship6_module );
622     }
623 #endif
624 #if HAVE_GETADDRINFO
625     return getaddrinfo( psz_node, service, &hints, res );
626 #else
627 {
628     int i_ret;
629
630     vlc_value_t lock;
631
632     var_Create( p_this->p_libvlc, "getaddrinfo_mutex", VLC_VAR_MUTEX );
633     var_Get( p_this->p_libvlc, "getaddrinfo_mutex", &lock );
634     vlc_mutex_lock( lock.p_address );
635
636     i_ret = __getaddrinfo( psz_node, service, &hints, res );
637     vlc_mutex_unlock( lock.p_address );
638     return i_ret;
639 }
640 #endif
641 }
642
643
644 void vlc_freeaddrinfo( struct addrinfo *infos )
645 {
646 #ifdef WIN32
647     typedef void (CALLBACK * FREEADDRINFO) ( struct addrinfo * );
648     HINSTANCE wship6_module;
649     FREEADDRINFO ws2_freeaddrinfo;
650      
651     wship6_module = LoadLibrary( "wship6.dll" );
652     if( wship6_module != NULL )
653     {
654         ws2_freeaddrinfo = (FREEADDRINFO)GetProcAddress( wship6_module,
655                                                          "freeaddrinfo" );
656
657         /*
658          * NOTE: it is assumed that wship6.dll defines either both
659          * getaddrinfo and freeaddrinfo or none of them.
660          */
661         if( ws2_freeaddrinfo != NULL )
662         {
663             ws2_freeaddrinfo( infos );
664             FreeLibrary( wship6_module );
665             return;
666         }
667
668         FreeLibrary( wship6_module );
669     }
670 #endif
671 #ifdef HAVE_GETADDRINFO
672     freeaddrinfo( infos );
673 #else
674     __freeaddrinfo( infos );
675 #endif
676 }