Use a consistent style for including <config.h>.
[gnulib.git] / lib / inet_ntop.c
1 /*
2  * Copyright (c) 1996-1999 by Internet Software Consortium.
3  * Copyright (c) 2005  Free Software Foundation, Inc.
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
10  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
11  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
12  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
14  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
15  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
16  * SOFTWARE.
17  */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 /* Specification.  */
24 #include "inet_ntop.h"
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <errno.h>
29
30 #define NS_IN6ADDRSZ 16
31 #define NS_INT16SZ 2
32
33 /*
34  * WARNING: Don't even consider trying to compile this on a system where
35  * sizeof(int) < 4.  sizeof(int) > 4 is fine; all the world's not a VAX.
36  */
37 typedef int verify_int_size[2 * sizeof(int) - 7];
38
39 #if HAVE_IPV4
40 static const char *inet_ntop4 (const unsigned char *src, char *dst, socklen_t size);
41 #endif
42 #if HAVE_IPV6
43 static const char *inet_ntop6 (const unsigned char *src, char *dst, socklen_t size);
44 #endif
45
46
47 /* char *
48  * inet_ntop(af, src, dst, size)
49  *      convert a network format address to presentation format.
50  * return:
51  *      pointer to presentation format address (`dst'), or NULL (see errno).
52  * author:
53  *      Paul Vixie, 1996.
54  */
55 const char *
56 inet_ntop(af, src, dst, size)
57         int af;
58         const void *src;
59         char *dst;
60         socklen_t size;
61 {
62         switch (af) {
63
64 #if HAVE_IPV4
65         case AF_INET:
66                 return (inet_ntop4(src, dst, size));
67 #endif
68
69 #if HAVE_IPV6
70         case AF_INET6:
71                 return (inet_ntop6(src, dst, size));
72 #endif
73         default:
74                 errno = EAFNOSUPPORT;
75                 return (NULL);
76         }
77         /* NOTREACHED */
78 }
79
80 #if HAVE_IPV4
81
82 /* const char *
83  * inet_ntop4(src, dst, size)
84  *      format an IPv4 address
85  * return:
86  *      `dst' (as a const)
87  * notes:
88  *      (1) uses no statics
89  *      (2) takes a u_char* not an in_addr as input
90  * author:
91  *      Paul Vixie, 1996.
92  */
93 static const char *
94 inet_ntop4(src, dst, size)
95         const unsigned char *src;
96         char *dst;
97         socklen_t size;
98 {
99         static const char fmt[] = "%u.%u.%u.%u";
100         char tmp[sizeof "255.255.255.255"];
101
102         if (sprintf(tmp, fmt, src[0], src[1], src[2], src[3]) > size) {
103                 errno = ENOSPC;
104                 return (NULL);
105         }
106         return strcpy(dst, tmp);
107 }
108
109 #endif
110
111 #if HAVE_IPV6
112
113 /* const char *
114  * inet_ntop6(src, dst, size)
115  *      convert IPv6 binary address into presentation (printable) format
116  * author:
117  *      Paul Vixie, 1996.
118  */
119 static const char *
120 inet_ntop6(src, dst, size)
121         const unsigned char *src;
122         char *dst;
123         socklen_t size;
124 {
125         /*
126          * Note that int32_t and int16_t need only be "at least" large enough
127          * to contain a value of the specified size.  On some systems, like
128          * Crays, there is no such thing as an integer variable with 16 bits.
129          * Keep this in mind if you think this function should have been coded
130          * to use pointer overlays.  All the world's not a VAX.
131          */
132         char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
133         struct { int base, len; } best, cur;
134         unsigned int words[NS_IN6ADDRSZ / NS_INT16SZ];
135         int i;
136
137         /*
138          * Preprocess:
139          *      Copy the input (bytewise) array into a wordwise array.
140          *      Find the longest run of 0x00's in src[] for :: shorthanding.
141          */
142         memset(words, '\0', sizeof words);
143         for (i = 0; i < NS_IN6ADDRSZ; i += 2)
144                 words[i / 2] = (src[i] << 8) | src[i + 1];
145         best.base = -1;
146         cur.base = -1;
147         for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
148                 if (words[i] == 0) {
149                         if (cur.base == -1)
150                                 cur.base = i, cur.len = 1;
151                         else
152                                 cur.len++;
153                 } else {
154                         if (cur.base != -1) {
155                                 if (best.base == -1 || cur.len > best.len)
156                                         best = cur;
157                                 cur.base = -1;
158                         }
159                 }
160         }
161         if (cur.base != -1) {
162                 if (best.base == -1 || cur.len > best.len)
163                         best = cur;
164         }
165         if (best.base != -1 && best.len < 2)
166                 best.base = -1;
167
168         /*
169          * Format the result.
170          */
171         tp = tmp;
172         for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
173                 /* Are we inside the best run of 0x00's? */
174                 if (best.base != -1 && i >= best.base &&
175                     i < (best.base + best.len)) {
176                         if (i == best.base)
177                                 *tp++ = ':';
178                         continue;
179                 }
180                 /* Are we following an initial run of 0x00s or any real hex? */
181                 if (i != 0)
182                         *tp++ = ':';
183                 /* Is this address an encapsulated IPv4? */
184                 if (i == 6 && best.base == 0 &&
185                     (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
186                         if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp)))
187                                 return (NULL);
188                         tp += strlen(tp);
189                         break;
190                 }
191                 tp += sprintf(tp, "%x", words[i]);
192         }
193         /* Was it a trailing run of 0x00's? */
194         if (best.base != -1 && (best.base + best.len) ==
195             (NS_IN6ADDRSZ / NS_INT16SZ))
196                 *tp++ = ':';
197         *tp++ = '\0';
198
199         /*
200          * Check for overflow, copy, and we're done.
201          */
202         if ((socklen_t)(tp - tmp) > size) {
203                 errno = ENOSPC;
204                 return (NULL);
205         }
206         return strcpy(dst, tmp);
207 }
208
209 #endif