1 /*- 2 * SPDX-License-Identifier: ISC 3 * 4 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 5 * Copyright (c) 1996,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_net_ntop.c,v 1.5 2006/06/20 02:50:14 marka Exp $"; 22 #endif 23 #include <sys/cdefs.h> 24 __FBSDID("$FreeBSD$"); 25 26 #include "port_before.h" 27 28 #include <sys/types.h> 29 #include <sys/socket.h> 30 #include <netinet/in.h> 31 #include <arpa/inet.h> 32 33 #include <errno.h> 34 #include <stdio.h> 35 #include <string.h> 36 #include <stdlib.h> 37 38 #include "port_after.h" 39 40 #ifdef SPRINTF_CHAR 41 # define SPRINTF(x) strlen(sprintf/**/x) 42 #else 43 # define SPRINTF(x) ((size_t)sprintf x) 44 #endif 45 46 static char * inet_net_ntop_ipv4(const u_char *src, int bits, char *dst, 47 size_t size); 48 static char * inet_net_ntop_ipv6(const u_char *src, int bits, char *dst, 49 size_t size); 50 51 /*% 52 * char * 53 * inet_net_ntop(af, src, bits, dst, size) 54 * convert network number from network to presentation format. 55 * generates CIDR style result always. 56 * return: 57 * pointer to dst, or NULL if an error occurred (check errno). 58 * author: 59 * Paul Vixie (ISC), July 1996 60 */ 61 char * 62 inet_net_ntop(int af, const void *src, int bits, char *dst, size_t size) 63 { 64 switch (af) { 65 case AF_INET: 66 return (inet_net_ntop_ipv4(src, bits, dst, size)); 67 case AF_INET6: 68 return (inet_net_ntop_ipv6(src, bits, dst, size)); 69 default: 70 errno = EAFNOSUPPORT; 71 return (NULL); 72 } 73 } 74 75 /*% 76 * static char * 77 * inet_net_ntop_ipv4(src, bits, dst, size) 78 * convert IPv4 network number from network to presentation format. 79 * generates CIDR style result always. 80 * return: 81 * pointer to dst, or NULL if an error occurred (check errno). 82 * note: 83 * network byte order assumed. this means 192.5.5.240/28 has 84 * 0b11110000 in its fourth octet. 85 * author: 86 * Paul Vixie (ISC), July 1996 87 */ 88 static char * 89 inet_net_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size) 90 { 91 char *odst = dst; 92 char *t; 93 u_int m; 94 int b; 95 96 if (bits < 0 || bits > 32) { 97 errno = EINVAL; 98 return (NULL); 99 } 100 101 if (bits == 0) { 102 if (size < sizeof "0") 103 goto emsgsize; 104 *dst++ = '0'; 105 size--; 106 *dst = '\0'; 107 } 108 109 /* Format whole octets. */ 110 for (b = bits / 8; b > 0; b--) { 111 if (size <= sizeof "255.") 112 goto emsgsize; 113 t = dst; 114 dst += SPRINTF((dst, "%u", *src++)); 115 if (b > 1) { 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 if (size <= sizeof ".255") 126 goto emsgsize; 127 t = dst; 128 if (dst != odst) 129 *dst++ = '.'; 130 m = ((1 << b) - 1) << (8 - b); 131 dst += SPRINTF((dst, "%u", *src & m)); 132 size -= (size_t)(dst - t); 133 } 134 135 /* Format CIDR /width. */ 136 if (size <= sizeof "/32") 137 goto emsgsize; 138 dst += SPRINTF((dst, "/%u", bits)); 139 return (odst); 140 141 emsgsize: 142 errno = EMSGSIZE; 143 return (NULL); 144 } 145 146 /*% 147 * static char * 148 * inet_net_ntop_ipv6(src, bits, fakebits, dst, size) 149 * convert IPv6 network number from network to presentation format. 150 * generates CIDR style result always. Picks the shortest representation 151 * unless the IP is really IPv4. 152 * always prints specified number of bits (bits). 153 * return: 154 * pointer to dst, or NULL if an error occurred (check errno). 155 * note: 156 * network byte order assumed. this means 192.5.5.240/28 has 157 * 0b11110000 in its fourth octet. 158 * author: 159 * Vadim Kogan (UCB), June 2001 160 * Original version (IPv4) by Paul Vixie (ISC), July 1996 161 */ 162 163 static char * 164 inet_net_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size) { 165 u_int m; 166 int b; 167 int p; 168 int zero_s, zero_l, tmp_zero_s, tmp_zero_l; 169 int i; 170 int is_ipv4 = 0; 171 unsigned char inbuf[16]; 172 char outbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")]; 173 char *cp; 174 int words; 175 u_char *s; 176 177 if (bits < 0 || bits > 128) { 178 errno = EINVAL; 179 return (NULL); 180 } 181 182 cp = outbuf; 183 184 if (bits == 0) { 185 *cp++ = ':'; 186 *cp++ = ':'; 187 *cp = '\0'; 188 } else { 189 /* Copy src to private buffer. Zero host part. */ 190 p = (bits + 7) / 8; 191 memcpy(inbuf, src, p); 192 memset(inbuf + p, 0, 16 - p); 193 b = bits % 8; 194 if (b != 0) { 195 m = ~0 << (8 - b); 196 inbuf[p-1] &= m; 197 } 198 199 s = inbuf; 200 201 /* how many words need to be displayed in output */ 202 words = (bits + 15) / 16; 203 if (words == 1) 204 words = 2; 205 206 /* Find the longest substring of zero's */ 207 zero_s = zero_l = tmp_zero_s = tmp_zero_l = 0; 208 for (i = 0; i < (words * 2); i += 2) { 209 if ((s[i] | s[i+1]) == 0) { 210 if (tmp_zero_l == 0) 211 tmp_zero_s = i / 2; 212 tmp_zero_l++; 213 } else { 214 if (tmp_zero_l && zero_l < tmp_zero_l) { 215 zero_s = tmp_zero_s; 216 zero_l = tmp_zero_l; 217 tmp_zero_l = 0; 218 } 219 } 220 } 221 222 if (tmp_zero_l && zero_l < tmp_zero_l) { 223 zero_s = tmp_zero_s; 224 zero_l = tmp_zero_l; 225 } 226 227 if (zero_l != words && zero_s == 0 && ((zero_l == 6) || 228 ((zero_l == 5 && s[10] == 0xff && s[11] == 0xff) || 229 ((zero_l == 7 && s[14] != 0 && s[15] != 1))))) 230 is_ipv4 = 1; 231 232 /* Format whole words. */ 233 for (p = 0; p < words; p++) { 234 if (zero_l != 0 && p >= zero_s && p < zero_s + zero_l) { 235 /* Time to skip some zeros */ 236 if (p == zero_s) 237 *cp++ = ':'; 238 if (p == words - 1) 239 *cp++ = ':'; 240 s++; 241 s++; 242 continue; 243 } 244 245 if (is_ipv4 && p > 5 ) { 246 *cp++ = (p == 6) ? ':' : '.'; 247 cp += SPRINTF((cp, "%u", *s++)); 248 /* we can potentially drop the last octet */ 249 if (p != 7 || bits > 120) { 250 *cp++ = '.'; 251 cp += SPRINTF((cp, "%u", *s++)); 252 } 253 } else { 254 if (cp != outbuf) 255 *cp++ = ':'; 256 cp += SPRINTF((cp, "%x", *s * 256 + s[1])); 257 s += 2; 258 } 259 } 260 } 261 /* Format CIDR /width. */ 262 sprintf(cp, "/%u", bits); 263 if (strlen(outbuf) + 1 > size) 264 goto emsgsize; 265 strcpy(dst, outbuf); 266 267 return (dst); 268 269 emsgsize: 270 errno = EMSGSIZE; 271 return (NULL); 272 } 273 274 /* 275 * Weak aliases for applications that use certain private entry points, 276 * and fail to include <arpa/inet.h>. 277 */ 278 #undef inet_net_ntop 279 __weak_reference(__inet_net_ntop, inet_net_ntop); 280 281 /*! \file */ 282