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