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 * 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 inet_cidr_ntop(int af, const void *src, int bits, char *dst, size_t size) 57 { 58 switch (af) 59 { 60 case PGSQL_AF_INET: 61 return inet_cidr_ntop_ipv4(src, bits, dst, size); 62 case PGSQL_AF_INET6: 63 return inet_cidr_ntop_ipv6(src, bits, dst, size); 64 default: 65 errno = EAFNOSUPPORT; 66 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 inet_cidr_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size) 86 { 87 char *odst = dst; 88 char *t; 89 u_int m; 90 int b; 91 92 if (bits < 0 || bits > 32) 93 { 94 errno = EINVAL; 95 return NULL; 96 } 97 98 if (bits == 0) 99 { 100 if (size < sizeof "0") 101 goto emsgsize; 102 *dst++ = '0'; 103 size--; 104 *dst = '\0'; 105 } 106 107 /* Format whole octets. */ 108 for (b = bits / 8; b > 0; b--) 109 { 110 if (size <= sizeof "255.") 111 goto emsgsize; 112 t = dst; 113 dst += SPRINTF((dst, "%u", *src++)); 114 if (b > 1) 115 { 116 *dst++ = '.'; 117 *dst = '\0'; 118 } 119 size -= (size_t) (dst - t); 120 } 121 122 /* Format partial octet. */ 123 b = bits % 8; 124 if (b > 0) 125 { 126 if (size <= sizeof ".255") 127 goto emsgsize; 128 t = dst; 129 if (dst != odst) 130 *dst++ = '.'; 131 m = ((1 << b) - 1) << (8 - b); 132 dst += SPRINTF((dst, "%u", *src & m)); 133 size -= (size_t) (dst - t); 134 } 135 136 /* Format CIDR /width. */ 137 if (size <= sizeof "/32") 138 goto emsgsize; 139 dst += SPRINTF((dst, "/%u", bits)); 140 return odst; 141 142 emsgsize: 143 errno = EMSGSIZE; 144 return NULL; 145 } 146 147 /* 148 * static char * 149 * inet_cidr_ntop_ipv6(src, bits, fakebits, 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 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 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 if (bits < 0 || bits > 128) 183 { 184 errno = EINVAL; 185 return NULL; 186 } 187 188 cp = outbuf; 189 190 if (bits == 0) 191 { 192 *cp++ = ':'; 193 *cp++ = ':'; 194 *cp = '\0'; 195 } 196 else 197 { 198 /* Copy src to private buffer. Zero host part. */ 199 p = (bits + 7) / 8; 200 memcpy(inbuf, src, p); 201 memset(inbuf + p, 0, 16 - p); 202 b = bits % 8; 203 if (b != 0) 204 { 205 m = ((u_int) ~0) << (8 - b); 206 inbuf[p - 1] &= m; 207 } 208 209 s = inbuf; 210 211 /* how many words need to be displayed in output */ 212 words = (bits + 15) / 16; 213 if (words == 1) 214 words = 2; 215 216 /* Find the longest substring of zero's */ 217 zero_s = zero_l = tmp_zero_s = tmp_zero_l = 0; 218 for (i = 0; i < (words * 2); i += 2) 219 { 220 if ((s[i] | s[i + 1]) == 0) 221 { 222 if (tmp_zero_l == 0) 223 tmp_zero_s = i / 2; 224 tmp_zero_l++; 225 } 226 else 227 { 228 if (tmp_zero_l && zero_l < tmp_zero_l) 229 { 230 zero_s = tmp_zero_s; 231 zero_l = tmp_zero_l; 232 tmp_zero_l = 0; 233 } 234 } 235 } 236 237 if (tmp_zero_l && zero_l < tmp_zero_l) 238 { 239 zero_s = tmp_zero_s; 240 zero_l = tmp_zero_l; 241 } 242 243 if (zero_l != words && zero_s == 0 && ((zero_l == 6) || 244 ((zero_l == 5 && s[10] == 0xff && s[11] == 0xff) || 245 ((zero_l == 7 && s[14] != 0 && s[15] != 1))))) 246 is_ipv4 = 1; 247 248 /* Format whole words. */ 249 for (p = 0; p < words; p++) 250 { 251 if (zero_l != 0 && p >= zero_s && p < zero_s + zero_l) 252 { 253 /* Time to skip some zeros */ 254 if (p == zero_s) 255 *cp++ = ':'; 256 if (p == words - 1) 257 *cp++ = ':'; 258 s++; 259 s++; 260 continue; 261 } 262 263 if (is_ipv4 && p > 5) 264 { 265 *cp++ = (p == 6) ? ':' : '.'; 266 cp += SPRINTF((cp, "%u", *s++)); 267 /* we can potentially drop the last octet */ 268 if (p != 7 || bits > 120) 269 { 270 *cp++ = '.'; 271 cp += SPRINTF((cp, "%u", *s++)); 272 } 273 } 274 else 275 { 276 if (cp != outbuf) 277 *cp++ = ':'; 278 cp += SPRINTF((cp, "%x", *s * 256 + s[1])); 279 s += 2; 280 } 281 } 282 } 283 /* Format CIDR /width. */ 284 (void) SPRINTF((cp, "/%u", bits)); 285 if (strlen(outbuf) + 1 > size) 286 goto emsgsize; 287 strcpy(dst, outbuf); 288 289 return dst; 290 291 emsgsize: 292 errno = EMSGSIZE; 293 return NULL; 294 } 295