1 /*- 2 * SPDX-License-Identifier: ISC 3 * 4 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 5 * Copyright (c) 1998,1999 by Internet Software Consortium. 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 17 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #if defined(LIBC_SCCS) && !defined(lint) 21 static const char rcsid[] = "$Id: inet_cidr_ntop.c,v 1.7 2006/10/11 02:18:18 marka Exp $"; 22 #endif 23 #include "port_before.h" 24 25 #include <sys/types.h> 26 #include <sys/socket.h> 27 #include <netinet/in.h> 28 #include <arpa/nameser.h> 29 #include <arpa/inet.h> 30 31 #include <errno.h> 32 #include <stdio.h> 33 #include <string.h> 34 #include <stdlib.h> 35 36 #include "port_after.h" 37 38 #ifdef SPRINTF_CHAR 39 # define SPRINTF(x) strlen(sprintf/**/x) 40 #else 41 # define SPRINTF(x) ((size_t)sprintf x) 42 #endif 43 44 static char * 45 inet_cidr_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size); 46 static char * 47 inet_cidr_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size); 48 49 /*% 50 * char * 51 * inet_cidr_ntop(af, src, bits, dst, size) 52 * convert network address from network to presentation format. 53 * "src"'s size is determined from its "af". 54 * return: 55 * pointer to dst, or NULL if an error occurred (check errno). 56 * note: 57 * 192.5.5.1/28 has a nonzero host part, which means it isn't a network 58 * as called for by inet_net_ntop() but it can be a host address with 59 * an included netmask. 60 * author: 61 * Paul Vixie (ISC), October 1998 62 */ 63 char * 64 inet_cidr_ntop(int af, const void *src, int bits, char *dst, size_t size) { 65 switch (af) { 66 case AF_INET: 67 return (inet_cidr_ntop_ipv4(src, bits, dst, size)); 68 case AF_INET6: 69 return (inet_cidr_ntop_ipv6(src, bits, dst, size)); 70 default: 71 errno = EAFNOSUPPORT; 72 return (NULL); 73 } 74 } 75 76 static int 77 decoct(const u_char *src, int bytes, char *dst, size_t size) { 78 char *odst = dst; 79 char *t; 80 int b; 81 82 for (b = 1; b <= bytes; b++) { 83 if (size < sizeof "255.") 84 return (0); 85 t = dst; 86 dst += SPRINTF((dst, "%u", *src++)); 87 if (b != bytes) { 88 *dst++ = '.'; 89 *dst = '\0'; 90 } 91 size -= (size_t)(dst - t); 92 } 93 return (dst - odst); 94 } 95 96 /*% 97 * static char * 98 * inet_cidr_ntop_ipv4(src, bits, dst, size) 99 * convert IPv4 network address from network to presentation format. 100 * "src"'s size is determined from its "af". 101 * return: 102 * pointer to dst, or NULL if an error occurred (check errno). 103 * note: 104 * network byte order assumed. this means 192.5.5.240/28 has 105 * 0b11110000 in its fourth octet. 106 * author: 107 * Paul Vixie (ISC), October 1998 108 */ 109 static char * 110 inet_cidr_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size) { 111 char *odst = dst; 112 size_t len = 4; 113 size_t b; 114 size_t bytes; 115 116 if ((bits < -1) || (bits > 32)) { 117 errno = EINVAL; 118 return (NULL); 119 } 120 121 /* Find number of significant bytes in address. */ 122 if (bits == -1) 123 len = 4; 124 else 125 for (len = 1, b = 1 ; b < 4U; b++) 126 if (*(src + b)) 127 len = b + 1; 128 129 /* Format whole octets plus nonzero trailing octets. */ 130 bytes = (((bits <= 0) ? 1 : bits) + 7) / 8; 131 if (len > bytes) 132 bytes = len; 133 b = decoct(src, bytes, dst, size); 134 if (b == 0U) 135 goto emsgsize; 136 dst += b; 137 size -= b; 138 139 if (bits != -1) { 140 /* Format CIDR /width. */ 141 if (size < sizeof "/32") 142 goto emsgsize; 143 dst += SPRINTF((dst, "/%u", bits)); 144 } 145 146 return (odst); 147 148 emsgsize: 149 errno = EMSGSIZE; 150 return (NULL); 151 } 152 153 static char * 154 inet_cidr_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size) { 155 /* 156 * Note that int32_t and int16_t need only be "at least" large enough 157 * to contain a value of the specified size. On some systems, like 158 * Crays, there is no such thing as an integer variable with 16 bits. 159 * Keep this in mind if you think this function should have been coded 160 * to use pointer overlays. All the world's not a VAX. 161 */ 162 char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255/128"]; 163 char *tp; 164 struct { int base, len; } best, cur; 165 u_int words[NS_IN6ADDRSZ / NS_INT16SZ]; 166 int i; 167 168 if ((bits < -1) || (bits > 128)) { 169 errno = EINVAL; 170 return (NULL); 171 } 172 173 /* 174 * Preprocess: 175 * Copy the input (bytewise) array into a wordwise array. 176 * Find the longest run of 0x00's in src[] for :: shorthanding. 177 */ 178 memset(words, '\0', sizeof words); 179 for (i = 0; i < NS_IN6ADDRSZ; i++) 180 words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); 181 best.base = -1; 182 best.len = 0; 183 cur.base = -1; 184 cur.len = 0; 185 for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { 186 if (words[i] == 0) { 187 if (cur.base == -1) 188 cur.base = i, cur.len = 1; 189 else 190 cur.len++; 191 } else { 192 if (cur.base != -1) { 193 if (best.base == -1 || cur.len > best.len) 194 best = cur; 195 cur.base = -1; 196 } 197 } 198 } 199 if (cur.base != -1) { 200 if (best.base == -1 || cur.len > best.len) 201 best = cur; 202 } 203 if (best.base != -1 && best.len < 2) 204 best.base = -1; 205 206 /* 207 * Format the result. 208 */ 209 tp = tmp; 210 for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { 211 /* Are we inside the best run of 0x00's? */ 212 if (best.base != -1 && i >= best.base && 213 i < (best.base + best.len)) { 214 if (i == best.base) 215 *tp++ = ':'; 216 continue; 217 } 218 /* Are we following an initial run of 0x00s or any real hex? */ 219 if (i != 0) 220 *tp++ = ':'; 221 /* Is this address an encapsulated IPv4? */ 222 if (i == 6 && best.base == 0 && (best.len == 6 || 223 (best.len == 7 && words[7] != 0x0001) || 224 (best.len == 5 && words[5] == 0xffff))) { 225 int n; 226 227 if (src[15] || bits == -1 || bits > 120) 228 n = 4; 229 else if (src[14] || bits > 112) 230 n = 3; 231 else 232 n = 2; 233 n = decoct(src+12, n, tp, sizeof tmp - (tp - tmp)); 234 if (n == 0) { 235 errno = EMSGSIZE; 236 return (NULL); 237 } 238 tp += strlen(tp); 239 break; 240 } 241 tp += SPRINTF((tp, "%x", words[i])); 242 } 243 244 /* Was it a trailing run of 0x00's? */ 245 if (best.base != -1 && (best.base + best.len) == 246 (NS_IN6ADDRSZ / NS_INT16SZ)) 247 *tp++ = ':'; 248 *tp = '\0'; 249 250 if (bits != -1) 251 tp += SPRINTF((tp, "/%u", bits)); 252 253 /* 254 * Check for overflow, copy, and we're done. 255 */ 256 if ((size_t)(tp - tmp) > size) { 257 errno = EMSGSIZE; 258 return (NULL); 259 } 260 strcpy(dst, tmp); 261 return (dst); 262 } 263 264 /*! \file */ 265