/*- * SPDX-License-Identifier: ISC * * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1998,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #if defined(LIBC_SCCS) && !defined(lint) static const char rcsid[] = "$Id: inet_cidr_ntop.c,v 1.7 2006/10/11 02:18:18 marka Exp $"; #endif #include __FBSDID("$FreeBSD$"); #include "port_before.h" #include #include #include #include #include #include #include #include #include #include "port_after.h" #ifdef SPRINTF_CHAR # define SPRINTF(x) strlen(sprintf/**/x) #else # define SPRINTF(x) ((size_t)sprintf x) #endif static char * inet_cidr_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size); static char * inet_cidr_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size); /*% * char * * inet_cidr_ntop(af, src, bits, dst, size) * convert network address from network to presentation format. * "src"'s size is determined from its "af". * return: * pointer to dst, or NULL if an error occurred (check errno). * note: * 192.5.5.1/28 has a nonzero host part, which means it isn't a network * as called for by inet_net_ntop() but it can be a host address with * an included netmask. * author: * Paul Vixie (ISC), October 1998 */ char * inet_cidr_ntop(int af, const void *src, int bits, char *dst, size_t size) { switch (af) { case AF_INET: return (inet_cidr_ntop_ipv4(src, bits, dst, size)); case AF_INET6: return (inet_cidr_ntop_ipv6(src, bits, dst, size)); default: errno = EAFNOSUPPORT; return (NULL); } } static int decoct(const u_char *src, int bytes, char *dst, size_t size) { char *odst = dst; char *t; int b; for (b = 1; b <= bytes; b++) { if (size < sizeof "255.") return (0); t = dst; dst += SPRINTF((dst, "%u", *src++)); if (b != bytes) { *dst++ = '.'; *dst = '\0'; } size -= (size_t)(dst - t); } return (dst - odst); } /*% * static char * * inet_cidr_ntop_ipv4(src, bits, dst, size) * convert IPv4 network address from network to presentation format. * "src"'s size is determined from its "af". * return: * pointer to dst, or NULL if an error occurred (check errno). * note: * network byte order assumed. this means 192.5.5.240/28 has * 0b11110000 in its fourth octet. * author: * Paul Vixie (ISC), October 1998 */ static char * inet_cidr_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size) { char *odst = dst; size_t len = 4; size_t b; size_t bytes; if ((bits < -1) || (bits > 32)) { errno = EINVAL; return (NULL); } /* Find number of significant bytes in address. */ if (bits == -1) len = 4; else for (len = 1, b = 1 ; b < 4U; b++) if (*(src + b)) len = b + 1; /* Format whole octets plus nonzero trailing octets. */ bytes = (((bits <= 0) ? 1 : bits) + 7) / 8; if (len > bytes) bytes = len; b = decoct(src, bytes, dst, size); if (b == 0U) goto emsgsize; dst += b; size -= b; if (bits != -1) { /* Format CIDR /width. */ if (size < sizeof "/32") goto emsgsize; dst += SPRINTF((dst, "/%u", bits)); } return (odst); emsgsize: errno = EMSGSIZE; return (NULL); } static char * inet_cidr_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size) { /* * Note that int32_t and int16_t need only be "at least" large enough * to contain a value of the specified size. On some systems, like * Crays, there is no such thing as an integer variable with 16 bits. * Keep this in mind if you think this function should have been coded * to use pointer overlays. All the world's not a VAX. */ char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255/128"]; char *tp; struct { int base, len; } best, cur; u_int words[NS_IN6ADDRSZ / NS_INT16SZ]; int i; if ((bits < -1) || (bits > 128)) { errno = EINVAL; return (NULL); } /* * Preprocess: * Copy the input (bytewise) array into a wordwise array. * Find the longest run of 0x00's in src[] for :: shorthanding. */ memset(words, '\0', sizeof words); for (i = 0; i < NS_IN6ADDRSZ; i++) words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); best.base = -1; best.len = 0; cur.base = -1; cur.len = 0; for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { if (words[i] == 0) { if (cur.base == -1) cur.base = i, cur.len = 1; else cur.len++; } else { if (cur.base != -1) { if (best.base == -1 || cur.len > best.len) best = cur; cur.base = -1; } } } if (cur.base != -1) { if (best.base == -1 || cur.len > best.len) best = cur; } if (best.base != -1 && best.len < 2) best.base = -1; /* * Format the result. */ tp = tmp; for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { /* Are we inside the best run of 0x00's? */ if (best.base != -1 && i >= best.base && i < (best.base + best.len)) { if (i == best.base) *tp++ = ':'; continue; } /* Are we following an initial run of 0x00s or any real hex? */ if (i != 0) *tp++ = ':'; /* Is this address an encapsulated IPv4? */ if (i == 6 && best.base == 0 && (best.len == 6 || (best.len == 7 && words[7] != 0x0001) || (best.len == 5 && words[5] == 0xffff))) { int n; if (src[15] || bits == -1 || bits > 120) n = 4; else if (src[14] || bits > 112) n = 3; else n = 2; n = decoct(src+12, n, tp, sizeof tmp - (tp - tmp)); if (n == 0) { errno = EMSGSIZE; return (NULL); } tp += strlen(tp); break; } tp += SPRINTF((tp, "%x", words[i])); } /* Was it a trailing run of 0x00's? */ if (best.base != -1 && (best.base + best.len) == (NS_IN6ADDRSZ / NS_INT16SZ)) *tp++ = ':'; *tp = '\0'; if (bits != -1) tp += SPRINTF((tp, "/%u", bits)); /* * Check for overflow, copy, and we're done. */ if ((size_t)(tp - tmp) > size) { errno = EMSGSIZE; return (NULL); } strcpy(dst, tmp); return (dst); } /*! \file */