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