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