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