provide our own inet_ntoa_r() proto if the system has none on its own
[platform/upstream/curl.git] / lib / inet_ntop.c
1 /*
2  * Original code by Paul Vixie. "curlified" by Gisle Vanem.
3  */
4
5 #include "setup.h"
6
7 #ifndef HAVE_INET_NTOP
8
9 #ifdef HAVE_SYS_PARAM_H
10 #include <sys/param.h>
11 #endif
12 #ifdef HAVE_SYS_TYPES_H
13 #include <sys/types.h>
14 #endif
15 #ifdef HAVE_SYS_SOCKET_H
16 #include <sys/socket.h>
17 #endif
18 #ifdef HAVE_NETINET_IN_H
19 #include <netinet/in.h>
20 #endif
21 #ifdef HAVE_ARPA_INET_H
22 #include <arpa/inet.h>
23 #endif
24 #include <string.h>
25 #include <errno.h>
26
27 #include "inet_ntop.h"
28
29 #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
30 /* this platform has a inet_ntoa_r() function, but no proto declared anywhere
31    so we include our own proto to make compilers happy */
32 #include "inet_ntoa_r.h"
33 #endif
34
35 #define IN6ADDRSZ       16
36 #define INADDRSZ         4
37 #define INT16SZ          2
38
39 #ifdef WIN32
40 #define EAFNOSUPPORT    WSAEAFNOSUPPORT
41 #define SET_ERRNO(e)    WSASetLastError(errno = (e))
42 #else
43 #define SET_ERRNO(e)    errno = e
44 #endif
45
46 /*
47  * Format an IPv4 address, more or less like inet_ntoa().
48  *
49  * Returns `dst' (as a const)
50  * Note:
51  *  - uses no statics
52  *  - takes a u_char* not an in_addr as input
53  */
54 static const char *inet_ntop4 (const u_char *src, char *dst, size_t size)
55 {
56 #ifdef HAVE_INET_NTOA_R
57   return inet_ntoa_r(*(struct in_addr*)src, dst, size);
58 #else
59   const char *addr = inet_ntoa(*(struct in_addr*)src);
60
61   if (strlen(addr) >= size)
62   {
63     SET_ERRNO(ENOSPC);
64     return (NULL);
65   }
66   return strcpy(dst, addr);
67 #endif
68 }
69
70 #ifdef ENABLE_IPV6
71 /*
72  * Convert IPv6 binary address into presentation (printable) format.
73  */
74 static const char *inet_ntop6 (const u_char *src, char *dst, size_t size)
75 {
76   /*
77    * Note that int32_t and int16_t need only be "at least" large enough
78    * to contain a value of the specified size.  On some systems, like
79    * Crays, there is no such thing as an integer variable with 16 bits.
80    * Keep this in mind if you think this function should have been coded
81    * to use pointer overlays.  All the world's not a VAX.
82    */
83   char  tmp [sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
84   char *tp;
85   struct {
86     long base;
87     long len;
88   } best, cur;
89   u_long words [IN6ADDRSZ / INT16SZ];
90   int    i;
91
92   /* Preprocess:
93    *  Copy the input (bytewise) array into a wordwise array.
94    *  Find the longest run of 0x00's in src[] for :: shorthanding.
95    */
96   memset(words, 0, sizeof(words));
97   for (i = 0; i < IN6ADDRSZ; i++)
98       words[i/2] |= (src[i] << ((1 - (i % 2)) << 3));
99
100   best.base = -1;
101   cur.base  = -1;
102   for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++)
103   {
104     if (words[i] == 0)
105     {
106       if (cur.base == -1)
107         cur.base = i, cur.len = 1;
108       else
109         cur.len++;
110     }
111     else if (cur.base != -1)
112     {
113       if (best.base == -1 || cur.len > best.len)
114          best = cur;
115       cur.base = -1;
116     }
117   }
118   if ((cur.base != -1) && (best.base == -1 || cur.len > best.len))
119      best = cur;
120   if (best.base != -1 && best.len < 2)
121      best.base = -1;
122
123   /* Format the result.
124    */
125   tp = tmp;
126   for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++)
127   {
128     /* Are we inside the best run of 0x00's?
129      */
130     if (best.base != -1 && i >= best.base && i < (best.base + best.len))
131     {
132       if (i == best.base)
133          *tp++ = ':';
134       continue;
135     }
136
137     /* Are we following an initial run of 0x00s or any real hex?
138      */
139     if (i != 0)
140        *tp++ = ':';
141
142     /* Is this address an encapsulated IPv4?
143      */
144     if (i == 6 && best.base == 0 &&
145         (best.len == 6 || (best.len == 5 && words[5] == 0xffff)))
146     {
147       if (!inet_ntop4(src+12, tp, sizeof(tmp) - (tp - tmp)))
148       {
149         SET_ERRNO(ENOSPC);
150         return (NULL);
151       }
152       tp += strlen(tp);
153       break;
154     }
155     tp += sprintf (tp, "%lx", words[i]);
156   }
157
158   /* Was it a trailing run of 0x00's?
159    */
160   if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ))
161      *tp++ = ':';
162   *tp++ = '\0';
163
164   /* Check for overflow, copy, and we're done.
165    */
166   if ((size_t)(tp - tmp) > size)
167   {
168     SET_ERRNO(ENOSPC);
169     return (NULL);
170   }
171   return strcpy (dst, tmp);
172 }
173 #endif  /* ENABLE_IPV6 */
174
175 /*
176  * Convert a network format address to presentation format.
177  *
178  * Returns pointer to presentation format address (`dst'),
179  * Returns NULL on error (see errno).
180  */
181 const char *Curl_inet_ntop(int af, const void *src, char *buf, size_t size)
182 {
183   switch (af) {
184   case AF_INET:
185     return inet_ntop4((const u_char*)src, buf, size);
186 #ifdef ENABLE_IPV6
187   case AF_INET6:
188     return inet_ntop6((const u_char*)src, buf, size);
189 #endif
190   default:
191     SET_ERRNO(EAFNOSUPPORT);
192     return NULL;
193   }
194 }
195 #endif  /* HAVE_INET_NTOP */