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