Line data Source code
1 : /*
2 : * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
3 : * Copyright (c) 1996,1999 by Internet Software Consortium.
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 ISC DISCLAIMS ALL WARRANTIES
10 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
12 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
15 : * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 : *
17 : * src/backend/utils/adt/inet_cidr_ntop.c
18 : */
19 :
20 : #if defined(LIBC_SCCS) && !defined(lint)
21 : static const char rcsid[] = "Id: inet_net_ntop.c,v 1.1.2.2 2004/03/09 09:17:27 marka Exp $";
22 : #endif
23 :
24 : #include "postgres.h"
25 :
26 : #include <sys/socket.h>
27 : #include <netinet/in.h>
28 : #include <arpa/inet.h>
29 :
30 : #include "utils/builtins.h"
31 : #include "utils/inet.h"
32 :
33 :
34 : #ifdef SPRINTF_CHAR
35 : #define SPRINTF(x) strlen(sprintf/**/x)
36 : #else
37 : #define SPRINTF(x) ((size_t)sprintf x)
38 : #endif
39 :
40 : static char *inet_cidr_ntop_ipv4(const u_char *src, int bits,
41 : char *dst, size_t size);
42 : static char *inet_cidr_ntop_ipv6(const u_char *src, int bits,
43 : char *dst, size_t size);
44 :
45 : /*
46 : * char *
47 : * pg_inet_cidr_ntop(af, src, bits, dst, size)
48 : * convert network number from network to presentation format.
49 : * generates CIDR style result always.
50 : * return:
51 : * pointer to dst, or NULL if an error occurred (check errno).
52 : * author:
53 : * Paul Vixie (ISC), July 1996
54 : */
55 : char *
56 102 : pg_inet_cidr_ntop(int af, const void *src, int bits, char *dst, size_t size)
57 : {
58 102 : switch (af)
59 : {
60 84 : case PGSQL_AF_INET:
61 84 : return inet_cidr_ntop_ipv4(src, bits, dst, size);
62 18 : case PGSQL_AF_INET6:
63 18 : return inet_cidr_ntop_ipv6(src, bits, dst, size);
64 0 : default:
65 0 : errno = EAFNOSUPPORT;
66 0 : return NULL;
67 : }
68 : }
69 :
70 :
71 : /*
72 : * static char *
73 : * inet_cidr_ntop_ipv4(src, bits, dst, size)
74 : * convert IPv4 network number from network to presentation format.
75 : * generates CIDR style result always.
76 : * return:
77 : * pointer to dst, or NULL if an error occurred (check errno).
78 : * note:
79 : * network byte order assumed. this means 192.5.5.240/28 has
80 : * 0b11110000 in its fourth octet.
81 : * author:
82 : * Paul Vixie (ISC), July 1996
83 : */
84 : static char *
85 84 : inet_cidr_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size)
86 : {
87 84 : char *odst = dst;
88 : char *t;
89 : u_int m;
90 : int b;
91 :
92 84 : if (bits < 0 || bits > 32)
93 : {
94 0 : errno = EINVAL;
95 0 : return NULL;
96 : }
97 :
98 84 : if (bits == 0)
99 : {
100 0 : if (size < sizeof "0")
101 0 : goto emsgsize;
102 0 : *dst++ = '0';
103 0 : size--;
104 0 : *dst = '\0';
105 : }
106 :
107 : /* Format whole octets. */
108 294 : for (b = bits / 8; b > 0; b--)
109 : {
110 210 : if (size <= sizeof "255.")
111 0 : goto emsgsize;
112 210 : t = dst;
113 210 : dst += SPRINTF((dst, "%u", *src++));
114 210 : if (b > 1)
115 : {
116 126 : *dst++ = '.';
117 126 : *dst = '\0';
118 : }
119 210 : size -= (size_t) (dst - t);
120 : }
121 :
122 : /* Format partial octet. */
123 84 : b = bits % 8;
124 84 : if (b > 0)
125 : {
126 6 : if (size <= sizeof ".255")
127 0 : goto emsgsize;
128 6 : t = dst;
129 6 : if (dst != odst)
130 6 : *dst++ = '.';
131 6 : m = ((1 << b) - 1) << (8 - b);
132 6 : dst += SPRINTF((dst, "%u", *src & m));
133 6 : size -= (size_t) (dst - t);
134 : }
135 :
136 : /* Format CIDR /width. */
137 84 : if (size <= sizeof "/32")
138 0 : goto emsgsize;
139 84 : dst += SPRINTF((dst, "/%u", bits));
140 84 : return odst;
141 :
142 0 : emsgsize:
143 0 : errno = EMSGSIZE;
144 0 : return NULL;
145 : }
146 :
147 : /*
148 : * static char *
149 : * inet_cidr_ntop_ipv6(src, bits, dst, size)
150 : * convert IPv6 network number from network to presentation format.
151 : * generates CIDR style result always. Picks the shortest representation
152 : * unless the IP is really IPv4.
153 : * always prints specified number of bits (bits).
154 : * return:
155 : * pointer to dst, or NULL if an error occurred (check errno).
156 : * note:
157 : * network byte order assumed. this means 192.5.5.240/28 has
158 : * 0x11110000 in its fourth octet.
159 : * author:
160 : * Vadim Kogan (UCB), June 2001
161 : * Original version (IPv4) by Paul Vixie (ISC), July 1996
162 : */
163 :
164 : static char *
165 18 : inet_cidr_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size)
166 : {
167 : u_int m;
168 : int b;
169 : int p;
170 : int zero_s,
171 : zero_l,
172 : tmp_zero_s,
173 : tmp_zero_l;
174 : int i;
175 18 : int is_ipv4 = 0;
176 : unsigned char inbuf[16];
177 : char outbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
178 : char *cp;
179 : int words;
180 : u_char *s;
181 :
182 18 : if (bits < 0 || bits > 128)
183 : {
184 0 : errno = EINVAL;
185 0 : return NULL;
186 : }
187 :
188 18 : cp = outbuf;
189 :
190 18 : if (bits == 0)
191 : {
192 0 : *cp++ = ':';
193 0 : *cp++ = ':';
194 0 : *cp = '\0';
195 : }
196 : else
197 : {
198 : /* Copy src to private buffer. Zero host part. */
199 18 : p = (bits + 7) / 8;
200 18 : memcpy(inbuf, src, p);
201 18 : memset(inbuf + p, 0, 16 - p);
202 18 : b = bits % 8;
203 18 : if (b != 0)
204 : {
205 6 : m = ((u_int) ~0) << (8 - b);
206 6 : inbuf[p - 1] &= m;
207 : }
208 :
209 18 : s = inbuf;
210 :
211 : /* how many words need to be displayed in output */
212 18 : words = (bits + 15) / 16;
213 18 : if (words == 1)
214 0 : words = 2;
215 :
216 : /* Find the longest substring of zero's */
217 18 : zero_s = zero_l = tmp_zero_s = tmp_zero_l = 0;
218 162 : for (i = 0; i < (words * 2); i += 2)
219 : {
220 144 : if ((s[i] | s[i + 1]) == 0)
221 : {
222 90 : if (tmp_zero_l == 0)
223 18 : tmp_zero_s = i / 2;
224 90 : tmp_zero_l++;
225 : }
226 : else
227 : {
228 54 : if (tmp_zero_l && zero_l < tmp_zero_l)
229 : {
230 18 : zero_s = tmp_zero_s;
231 18 : zero_l = tmp_zero_l;
232 18 : tmp_zero_l = 0;
233 : }
234 : }
235 : }
236 :
237 18 : if (tmp_zero_l && zero_l < tmp_zero_l)
238 : {
239 0 : zero_s = tmp_zero_s;
240 0 : zero_l = tmp_zero_l;
241 : }
242 :
243 18 : if (zero_l != words && zero_s == 0 && ((zero_l == 6) ||
244 6 : ((zero_l == 5 && s[10] == 0xff && s[11] == 0xff) ||
245 0 : ((zero_l == 7 && s[14] != 0 && s[15] != 1)))))
246 6 : is_ipv4 = 1;
247 :
248 : /* Format whole words. */
249 162 : for (p = 0; p < words; p++)
250 : {
251 144 : if (zero_l != 0 && p >= zero_s && p < zero_s + zero_l)
252 : {
253 : /* Time to skip some zeros */
254 90 : if (p == zero_s)
255 18 : *cp++ = ':';
256 90 : if (p == words - 1)
257 0 : *cp++ = ':';
258 90 : s++;
259 90 : s++;
260 90 : continue;
261 : }
262 :
263 54 : if (is_ipv4 && p > 5)
264 : {
265 12 : *cp++ = (p == 6) ? ':' : '.';
266 12 : cp += SPRINTF((cp, "%u", *s++));
267 : /* we can potentially drop the last octet */
268 12 : if (p != 7 || bits > 120)
269 : {
270 12 : *cp++ = '.';
271 12 : cp += SPRINTF((cp, "%u", *s++));
272 : }
273 : }
274 : else
275 : {
276 42 : if (cp != outbuf)
277 30 : *cp++ = ':';
278 42 : cp += SPRINTF((cp, "%x", *s * 256 + s[1]));
279 42 : s += 2;
280 : }
281 : }
282 : }
283 : /* Format CIDR /width. */
284 18 : (void) SPRINTF((cp, "/%u", bits));
285 18 : if (strlen(outbuf) + 1 > size)
286 0 : goto emsgsize;
287 18 : strcpy(dst, outbuf);
288 :
289 18 : return dst;
290 :
291 0 : emsgsize:
292 0 : errno = EMSGSIZE;
293 0 : return NULL;
294 : }
|