1*eabc0478Schristos /* $NetBSD: inet_ntop.c,v 1.2 2024/08/18 20:47:14 christos Exp $ */ 2897be3a4Schristos 3897be3a4Schristos /* 4897be3a4Schristos * Copyright (C) 2004, 2005, 2007, 2009 Internet Systems Consortium, Inc. ("ISC") 5897be3a4Schristos * Copyright (C) 1996-2001 Internet Software Consortium. 6897be3a4Schristos * 7897be3a4Schristos * Permission to use, copy, modify, and/or distribute this software for any 8897be3a4Schristos * purpose with or without fee is hereby granted, provided that the above 9897be3a4Schristos * copyright notice and this permission notice appear in all copies. 10897be3a4Schristos * 11897be3a4Schristos * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 12897be3a4Schristos * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 13897be3a4Schristos * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 14897be3a4Schristos * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 15897be3a4Schristos * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 16897be3a4Schristos * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17897be3a4Schristos * PERFORMANCE OF THIS SOFTWARE. 18897be3a4Schristos */ 19897be3a4Schristos 20897be3a4Schristos /*! \file */ 21897be3a4Schristos 22897be3a4Schristos #if defined(LIBC_SCCS) && !defined(lint) 23897be3a4Schristos static char rcsid[] = 24897be3a4Schristos "Id: inet_ntop.c,v 1.21 2009/07/17 23:47:41 tbox Exp "; 25897be3a4Schristos #endif /* LIBC_SCCS and not lint */ 26897be3a4Schristos 27897be3a4Schristos #include <config.h> 28897be3a4Schristos 29897be3a4Schristos #include <errno.h> 30897be3a4Schristos #include <stdio.h> 31897be3a4Schristos #include <string.h> 32897be3a4Schristos 33897be3a4Schristos #include <isc/net.h> 34897be3a4Schristos #include <isc/print.h> 35897be3a4Schristos 36897be3a4Schristos #define NS_INT16SZ 2 37897be3a4Schristos #define NS_IN6ADDRSZ 16 38897be3a4Schristos 39897be3a4Schristos /* 40897be3a4Schristos * WARNING: Don't even consider trying to compile this on a system where 41897be3a4Schristos * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. 42897be3a4Schristos */ 43897be3a4Schristos 44897be3a4Schristos static const char *inet_ntop4(const unsigned char *src, char *dst, 45897be3a4Schristos size_t size); 46897be3a4Schristos 47897be3a4Schristos #ifdef AF_INET6 48897be3a4Schristos static const char *inet_ntop6(const unsigned char *src, char *dst, 49897be3a4Schristos size_t size); 50897be3a4Schristos #endif 51897be3a4Schristos const char *isc_net_ntop(int af, const void *src, char *dst, size_t size); 52897be3a4Schristos 53897be3a4Schristos /*! char * 54897be3a4Schristos * isc_net_ntop(af, src, dst, size) 55897be3a4Schristos * convert a network format address to presentation format. 56897be3a4Schristos * \return 57897be3a4Schristos * pointer to presentation format address (`dst'), or NULL (see errno). 58897be3a4Schristos * \author 59897be3a4Schristos * Paul Vixie, 1996. 60897be3a4Schristos */ 61897be3a4Schristos const char * 62897be3a4Schristos isc_net_ntop(int af, const void *src, char *dst, size_t size) 63897be3a4Schristos { 64897be3a4Schristos switch (af) { 65897be3a4Schristos case AF_INET: 66897be3a4Schristos return (inet_ntop4(src, dst, size)); 67897be3a4Schristos #ifdef AF_INET6 68897be3a4Schristos case AF_INET6: 69897be3a4Schristos return (inet_ntop6(src, dst, size)); 70897be3a4Schristos #endif 71897be3a4Schristos default: 72897be3a4Schristos errno = EAFNOSUPPORT; 73897be3a4Schristos return (NULL); 74897be3a4Schristos } 75897be3a4Schristos /* NOTREACHED */ 76897be3a4Schristos } 77897be3a4Schristos 78897be3a4Schristos /*! const char * 79897be3a4Schristos * inet_ntop4(src, dst, size) 80897be3a4Schristos * format an IPv4 address 81897be3a4Schristos * \return 82897be3a4Schristos * `dst' (as a const) 83897be3a4Schristos * \note 84897be3a4Schristos * (1) uses no statics 85897be3a4Schristos * \note 86897be3a4Schristos * (2) takes a unsigned char* not an in_addr as input 87897be3a4Schristos * \author 88897be3a4Schristos * Paul Vixie, 1996. 89897be3a4Schristos */ 90897be3a4Schristos static const char * 91897be3a4Schristos inet_ntop4(const unsigned char *src, char *dst, size_t size) 92897be3a4Schristos { 93*eabc0478Schristos #define fmt "%u.%u.%u.%u" 94897be3a4Schristos char tmp[sizeof("255.255.255.255")]; 95897be3a4Schristos int len; 96897be3a4Schristos 97897be3a4Schristos len = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]); 98897be3a4Schristos if (len < 0 || (size_t)len >= size) 99897be3a4Schristos { 100897be3a4Schristos errno = ENOSPC; 101897be3a4Schristos return (NULL); 102897be3a4Schristos } 103897be3a4Schristos memcpy(dst, tmp, 1 + len); 104897be3a4Schristos 105897be3a4Schristos return (dst); 106897be3a4Schristos } 107897be3a4Schristos 108897be3a4Schristos /*! const char * 109897be3a4Schristos * isc_inet_ntop6(src, dst, size) 110897be3a4Schristos * convert IPv6 binary address into presentation (printable) format 111897be3a4Schristos * \author 112897be3a4Schristos * Paul Vixie, 1996. 113897be3a4Schristos */ 114897be3a4Schristos #ifdef AF_INET6 115897be3a4Schristos static const char * 116897be3a4Schristos inet_ntop6(const unsigned char *src, char *dst, size_t size) 117897be3a4Schristos { 118897be3a4Schristos /* 119897be3a4Schristos * Note that int32_t and int16_t need only be "at least" large enough 120897be3a4Schristos * to contain a value of the specified size. On some systems, like 121897be3a4Schristos * Crays, there is no such thing as an integer variable with 16 bits. 122897be3a4Schristos * Keep this in mind if you think this function should have been coded 123897be3a4Schristos * to use pointer overlays. All the world's not a VAX. 124897be3a4Schristos */ 125897be3a4Schristos char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")], *tp; 126897be3a4Schristos struct { int base, len; } best, cur; 127897be3a4Schristos unsigned int words[NS_IN6ADDRSZ / NS_INT16SZ]; 128897be3a4Schristos int i; 129897be3a4Schristos 130897be3a4Schristos /* 131897be3a4Schristos * Preprocess: 132897be3a4Schristos * Copy the input (bytewise) array into a wordwise array. 133897be3a4Schristos * Find the longest run of 0x00's in src[] for :: shorthanding. 134897be3a4Schristos */ 135897be3a4Schristos memset(words, '\0', sizeof(words)); 136897be3a4Schristos for (i = 0; i < NS_IN6ADDRSZ; i++) 137897be3a4Schristos words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); 138897be3a4Schristos best.base = -1; 139897be3a4Schristos cur.base = -1; 140897be3a4Schristos best.len = cur.len = 0; 141897be3a4Schristos for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { 142897be3a4Schristos if (words[i] == 0) { 143897be3a4Schristos if (cur.base == -1) 144897be3a4Schristos cur.base = i, cur.len = 1; 145897be3a4Schristos else 146897be3a4Schristos cur.len++; 147897be3a4Schristos } else { 148897be3a4Schristos if (cur.base != -1) { 149897be3a4Schristos if (best.base == -1 || cur.len > best.len) 150897be3a4Schristos best = cur; 151897be3a4Schristos cur.base = -1; 152897be3a4Schristos } 153897be3a4Schristos } 154897be3a4Schristos } 155897be3a4Schristos if (cur.base != -1) { 156897be3a4Schristos if (best.base == -1 || cur.len > best.len) 157897be3a4Schristos best = cur; 158897be3a4Schristos } 159897be3a4Schristos if (best.base != -1 && best.len < 2) 160897be3a4Schristos best.base = -1; 161897be3a4Schristos 162897be3a4Schristos /* 163897be3a4Schristos * Format the result. 164897be3a4Schristos */ 165897be3a4Schristos tp = tmp; 166897be3a4Schristos for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { 167897be3a4Schristos /* Are we inside the best run of 0x00's? */ 168897be3a4Schristos if (best.base != -1 && i >= best.base && 169897be3a4Schristos i < (best.base + best.len)) { 170897be3a4Schristos if (i == best.base) 171897be3a4Schristos *tp++ = ':'; 172897be3a4Schristos continue; 173897be3a4Schristos } 174897be3a4Schristos /* Are we following an initial run of 0x00s or any real hex? */ 175897be3a4Schristos if (i != 0) 176897be3a4Schristos *tp++ = ':'; 177897be3a4Schristos /* Is this address an encapsulated IPv4? */ 178897be3a4Schristos if (i == 6 && best.base == 0 && (best.len == 6 || 179897be3a4Schristos (best.len == 7 && words[7] != 0x0001) || 180897be3a4Schristos (best.len == 5 && words[5] == 0xffff))) { 181897be3a4Schristos if (!inet_ntop4(src+12, tp, 182897be3a4Schristos sizeof(tmp) - (tp - tmp))) 183897be3a4Schristos return (NULL); 184897be3a4Schristos tp += strlen(tp); 185897be3a4Schristos break; 186897be3a4Schristos } 187897be3a4Schristos tp += snprintf(tp, sizeof(tmp) - (tp - tmp), "%x", words[i]); 188897be3a4Schristos } 189897be3a4Schristos /* Was it a trailing run of 0x00's? */ 190897be3a4Schristos if (best.base != -1 && (best.base + best.len) == 191897be3a4Schristos (NS_IN6ADDRSZ / NS_INT16SZ)) 192897be3a4Schristos *tp++ = ':'; 193897be3a4Schristos *tp++ = '\0'; 194897be3a4Schristos 195897be3a4Schristos /* 196897be3a4Schristos * Check for overflow, copy, and we're done. 197897be3a4Schristos */ 198897be3a4Schristos if ((size_t)(tp - tmp) > size) { 199897be3a4Schristos errno = ENOSPC; 200897be3a4Schristos return (NULL); 201897be3a4Schristos } 202897be3a4Schristos memcpy(dst, tmp, (size_t)(tp - tmp)); 203897be3a4Schristos return (dst); 204897be3a4Schristos } 205897be3a4Schristos #endif /* AF_INET6 */ 206