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