inet_ntop.c (6354B)
1 /* 2 * Copyright (C) 1996-2022 Internet Software Consortium. 3 * 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM 9 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 10 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 11 * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, 12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING 13 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 14 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 15 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 * 17 * SPDX-License-Identifier: ISC 18 */ 19 /* 20 * Original code by Paul Vixie. "curlified" by Gisle Vanem. 21 */ 22 23 #include "../curl_setup.h" 24 25 #ifndef HAVE_INET_NTOP 26 27 #ifdef HAVE_SYS_PARAM_H 28 #include <sys/param.h> 29 #endif 30 #ifdef HAVE_NETINET_IN_H 31 #include <netinet/in.h> 32 #endif 33 #ifdef HAVE_ARPA_INET_H 34 #include <arpa/inet.h> 35 #endif 36 37 #include "inet_ntop.h" 38 39 #define IN6ADDRSZ 16 40 /* #define INADDRSZ 4 */ 41 #define INT16SZ 2 42 43 /* 44 * If USE_IPV6 is disabled, we still want to parse IPv6 addresses, so make 45 * sure we have _some_ value for AF_INET6 without polluting our fake value 46 * everywhere. 47 */ 48 #if !defined(USE_IPV6) && !defined(AF_INET6) 49 #define AF_INET6 (AF_INET + 1) 50 #endif 51 52 /* 53 * Format an IPv4 address, more or less like inet_ntop(). 54 * 55 * Returns `dst' (as a const) 56 * Note: 57 * - uses no statics 58 * - takes an unsigned char* not an in_addr as input 59 */ 60 static char *inet_ntop4(const unsigned char *src, char *dst, size_t size) 61 { 62 char tmp[sizeof("255.255.255.255")]; 63 size_t len; 64 65 DEBUGASSERT(size >= 16); 66 67 /* this sprintf() does not overflow the buffer. Avoids snprintf to work more 68 widely. Avoids the msnprintf family to work as a curlx function. */ 69 (void)(sprintf)(tmp, "%d.%d.%d.%d", 70 ((int)((unsigned char)src[0])) & 0xff, 71 ((int)((unsigned char)src[1])) & 0xff, 72 ((int)((unsigned char)src[2])) & 0xff, 73 ((int)((unsigned char)src[3])) & 0xff); 74 75 len = strlen(tmp); 76 if(len == 0 || len >= size) { 77 #ifdef USE_WINSOCK 78 CURL_SETERRNO(WSAEINVAL); 79 #else 80 CURL_SETERRNO(ENOSPC); 81 #endif 82 return NULL; 83 } 84 strcpy(dst, tmp); 85 return dst; 86 } 87 88 /* 89 * Convert IPv6 binary address into presentation (printable) format. 90 */ 91 static char *inet_ntop6(const unsigned char *src, char *dst, size_t size) 92 { 93 /* 94 * Note that int32_t and int16_t need only be "at least" large enough 95 * to contain a value of the specified size. On some systems, like 96 * Crays, there is no such thing as an integer variable with 16 bits. 97 * Keep this in mind if you think this function should have been coded 98 * to use pointer overlays. All the world's not a VAX. 99 */ 100 char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; 101 char *tp; 102 struct { 103 int base; 104 int len; 105 } best, cur; 106 unsigned int words[IN6ADDRSZ / INT16SZ]; 107 int i; 108 109 /* Preprocess: 110 * Copy the input (bytewise) array into a wordwise array. 111 * Find the longest run of 0x00's in src[] for :: shorthanding. 112 */ 113 memset(words, '\0', sizeof(words)); 114 for(i = 0; i < IN6ADDRSZ; i++) 115 words[i/2] |= ((unsigned int)src[i] << ((1 - (i % 2)) << 3)); 116 117 best.base = -1; 118 cur.base = -1; 119 best.len = 0; 120 cur.len = 0; 121 122 for(i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { 123 if(words[i] == 0) { 124 if(cur.base == -1) { 125 cur.base = i; cur.len = 1; 126 } 127 else 128 cur.len++; 129 } 130 else if(cur.base != -1) { 131 if(best.base == -1 || cur.len > best.len) 132 best = cur; 133 cur.base = -1; 134 } 135 } 136 if((cur.base != -1) && (best.base == -1 || cur.len > best.len)) 137 best = cur; 138 if(best.base != -1 && best.len < 2) 139 best.base = -1; 140 /* Format the result. */ 141 tp = tmp; 142 for(i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { 143 /* Are we inside the best run of 0x00's? */ 144 if(best.base != -1 && i >= best.base && i < (best.base + best.len)) { 145 if(i == best.base) 146 *tp++ = ':'; 147 continue; 148 } 149 150 /* Are we following an initial run of 0x00s or any real hex? 151 */ 152 if(i) 153 *tp++ = ':'; 154 155 /* Is this address an encapsulated IPv4? 156 */ 157 if(i == 6 && best.base == 0 && 158 (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { 159 if(!inet_ntop4(src + 12, tp, sizeof(tmp) - (tp - tmp))) { 160 return NULL; 161 } 162 tp += strlen(tp); 163 break; 164 } 165 else { 166 /* Lower-case digits. Can't use the set from mprintf.c since this 167 needs to work as a curlx function */ 168 static const unsigned char ldigits[] = "0123456789abcdef"; 169 170 unsigned int w = words[i]; 171 /* output lowercase 16bit hex number but ignore leading zeroes */ 172 if(w & 0xf000) 173 *tp++ = ldigits[(w & 0xf000) >> 12]; 174 if(w & 0xff00) 175 *tp++ = ldigits[(w & 0x0f00) >> 8]; 176 if(w & 0xfff0) 177 *tp++ = ldigits[(w & 0x00f0) >> 4]; 178 *tp++ = ldigits[(w & 0x000f)]; 179 } 180 } 181 182 /* Was it a trailing run of 0x00's? 183 */ 184 if(best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ)) 185 *tp++ = ':'; 186 *tp++ = '\0'; 187 188 /* Check for overflow, copy, and we are done. 189 */ 190 if((size_t)(tp - tmp) > size) { 191 #ifdef USE_WINSOCK 192 CURL_SETERRNO(WSAEINVAL); 193 #else 194 CURL_SETERRNO(ENOSPC); 195 #endif 196 return NULL; 197 } 198 strcpy(dst, tmp); 199 return dst; 200 } 201 202 /* 203 * Convert a network format address to presentation format. 204 * 205 * Returns pointer to presentation format address (`buf'). 206 * Returns NULL on error and errno set with the specific 207 * error, EAFNOSUPPORT or ENOSPC. 208 * 209 * On Windows we store the error in the thread errno, not in the Winsock error 210 * code. This is to avoid losing the actual last Winsock error. When this 211 * function returns NULL, check errno not SOCKERRNO. 212 */ 213 char *curlx_inet_ntop(int af, const void *src, char *buf, size_t size) 214 { 215 switch(af) { 216 case AF_INET: 217 return inet_ntop4((const unsigned char *)src, buf, size); 218 case AF_INET6: 219 return inet_ntop6((const unsigned char *)src, buf, size); 220 default: 221 CURL_SETERRNO(SOCKEAFNOSUPPORT); 222 return NULL; 223 } 224 } 225 #endif /* HAVE_INET_NTOP */