/* Copyright (c) 2003-2004, Roger Dingledine * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. * Copyright (c) 2007-2018, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "lib/net/ipv6.h" #include "lib/net/ipv4.h" #include "lib/string/util_string.h" #include "lib/string/compat_string.h" #include "lib/string/compat_ctype.h" #include "lib/string/printf.h" #include "lib/string/scanf.h" #include "lib/log/util_bug.h" #ifdef HAVE_ARPA_INET_H #include #endif #include #include /** Given af==AF_INET and src a struct in_addr, or * af==AF_INET6 and src a struct in6_addr, try to format the * address and store it in the len-byte buffer dst. Returns * dst on success, NULL on failure. * * (Like inet_ntop(af,src,dst,len), but works on platforms that don't have it: * Tor sometimes needs to format ipv6 addresses even on platforms without ipv6 * support.) */ const char * tor_inet_ntop(int af, const void *src, char *dst, size_t len) { if (af == AF_INET) { if (tor_inet_ntoa(src, dst, len) < 0) return NULL; else return dst; } else if (af == AF_INET6) { const struct in6_addr *addr = src; char buf[64], *cp; int longestGapLen = 0, longestGapPos = -1, i, curGapPos = -1, curGapLen = 0; uint16_t words[8]; for (i = 0; i < 8; ++i) { words[i] = (((uint16_t)addr->s6_addr[2*i])<<8) + addr->s6_addr[2*i+1]; } if (words[0] == 0 && words[1] == 0 && words[2] == 0 && words[3] == 0 && words[4] == 0 && ((words[5] == 0 && words[6] && words[7]) || (words[5] == 0xffff))) { /* This is an IPv4 address. */ if (words[5] == 0) { tor_snprintf(buf, sizeof(buf), "::%d.%d.%d.%d", addr->s6_addr[12], addr->s6_addr[13], addr->s6_addr[14], addr->s6_addr[15]); } else { tor_snprintf(buf, sizeof(buf), "::%x:%d.%d.%d.%d", words[5], addr->s6_addr[12], addr->s6_addr[13], addr->s6_addr[14], addr->s6_addr[15]); } if ((strlen(buf) + 1) > len) /* +1 for \0 */ return NULL; strlcpy(dst, buf, len); return dst; } i = 0; while (i < 8) { if (words[i] == 0) { curGapPos = i++; curGapLen = 1; while (i<8 && words[i] == 0) { ++i; ++curGapLen; } if (curGapLen > longestGapLen) { longestGapPos = curGapPos; longestGapLen = curGapLen; } } else { ++i; } } if (longestGapLen<=1) longestGapPos = -1; cp = buf; for (i = 0; i < 8; ++i) { if (words[i] == 0 && longestGapPos == i) { if (i == 0) *cp++ = ':'; *cp++ = ':'; while (i < 8 && words[i] == 0) ++i; --i; /* to compensate for loop increment. */ } else { tor_snprintf(cp, sizeof(buf)-(cp-buf), "%x", (unsigned)words[i]); cp += strlen(cp); if (i != 7) *cp++ = ':'; } } *cp = '\0'; if ((strlen(buf) + 1) > len) /* +1 for \0 */ return NULL; strlcpy(dst, buf, len); return dst; } else { return NULL; } } /** Given af==AF_INET or af==AF_INET6, and a string src * encoding an IPv4 address or IPv6 address correspondingly, try to parse the * address and store the result in dst (which must have space for a * struct in_addr or a struct in6_addr, as appropriate). Return 1 on success, * 0 on a bad parse, and -1 on a bad af. * * (Like inet_pton(af,src,dst) but works on platforms that don't have it: Tor * sometimes needs to format ipv6 addresses even on platforms without ipv6 * support.) */ int tor_inet_pton(int af, const char *src, void *dst) { if (af == AF_INET) { return tor_inet_aton(src, dst); } else if (af == AF_INET6) { struct in6_addr *out = dst; uint16_t words[8]; int gapPos = -1, i, setWords=0; const char *dot = strchr(src, '.'); const char *eow; /* end of words. */ memset(words, 0xf8, sizeof(words)); if (dot == src) return 0; else if (!dot) eow = src+strlen(src); else { unsigned byte1,byte2,byte3,byte4; char more; for (eow = dot-1; eow > src && TOR_ISDIGIT(*eow); --eow) ; if (*eow != ':') return 0; ++eow; /* We use "scanf" because some platform inet_aton()s are too lax * about IPv4 addresses of the form "1.2.3" */ if (tor_sscanf(eow, "%3u.%3u.%3u.%3u%c", &byte1,&byte2,&byte3,&byte4,&more) != 4) return 0; if (byte1 > 255 || byte2 > 255 || byte3 > 255 || byte4 > 255) return 0; words[6] = (byte1<<8) | byte2; words[7] = (byte3<<8) | byte4; setWords += 2; } i = 0; while (src < eow) { if (i > 7) return 0; if (TOR_ISXDIGIT(*src)) { char *next; ssize_t len; long r = strtol(src, &next, 16); if (next == NULL || next == src) { /* The 'next == src' error case can happen on versions of openbsd * which treat "0xfoo" as an error, rather than as "0" followed by * "xfoo". */ return 0; } len = *next == '\0' ? eow - src : next - src; if (len > 4) return 0; if (len > 1 && !TOR_ISXDIGIT(src[1])) return 0; /* 0x is not valid */ tor_assert(r >= 0); tor_assert(r < 65536); words[i++] = (uint16_t)r; setWords++; src = next; if (*src != ':' && src != eow) return 0; ++src; } else if (*src == ':' && i > 0 && gapPos == -1) { gapPos = i; ++src; } else if (*src == ':' && i == 0 && src+1 < eow && src[1] == ':' && gapPos == -1) { gapPos = i; src += 2; } else { return 0; } } if (setWords > 8 || (setWords == 8 && gapPos != -1) || (setWords < 8 && gapPos == -1)) return 0; if (gapPos >= 0) { int nToMove = setWords - (dot ? 2 : 0) - gapPos; int gapLen = 8 - setWords; tor_assert(nToMove >= 0); memmove(&words[gapPos+gapLen], &words[gapPos], sizeof(uint16_t)*nToMove); memset(&words[gapPos], 0, sizeof(uint16_t)*gapLen); } for (i = 0; i < 8; ++i) { out->s6_addr[2*i ] = words[i] >> 8; out->s6_addr[2*i+1] = words[i] & 0xff; } return 1; } else { return -1; } }