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