michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/net/DNS.h" michael@0: michael@0: #include "mozilla/Assertions.h" michael@0: #include "mozilla/mozalloc.h" michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include michael@0: michael@0: #ifdef XP_WIN michael@0: #include "ws2tcpip.h" michael@0: #endif michael@0: michael@0: namespace mozilla { michael@0: namespace net { michael@0: michael@0: const char *inet_ntop_internal(int af, const void *src, char *dst, socklen_t size) michael@0: { michael@0: #ifdef XP_WIN michael@0: if (af == AF_INET) { michael@0: struct sockaddr_in s; michael@0: memset(&s, 0, sizeof(s)); michael@0: s.sin_family = AF_INET; michael@0: memcpy(&s.sin_addr, src, sizeof(struct in_addr)); michael@0: int result = getnameinfo((struct sockaddr *)&s, sizeof(struct sockaddr_in), michael@0: dst, size, nullptr, 0, NI_NUMERICHOST); michael@0: if (result == 0) { michael@0: return dst; michael@0: } michael@0: } michael@0: else if (af == AF_INET6) { michael@0: struct sockaddr_in6 s; michael@0: memset(&s, 0, sizeof(s)); michael@0: s.sin6_family = AF_INET6; michael@0: memcpy(&s.sin6_addr, src, sizeof(struct in_addr6)); michael@0: int result = getnameinfo((struct sockaddr *)&s, sizeof(struct sockaddr_in6), michael@0: dst, size, nullptr, 0, NI_NUMERICHOST); michael@0: if (result == 0) { michael@0: return dst; michael@0: } michael@0: } michael@0: return nullptr; michael@0: #else michael@0: return inet_ntop(af, src, dst, size); michael@0: #endif michael@0: } michael@0: michael@0: // Copies the contents of a PRNetAddr to a NetAddr. michael@0: // Does not do a ptr safety check! michael@0: void PRNetAddrToNetAddr(const PRNetAddr *prAddr, NetAddr *addr) michael@0: { michael@0: if (prAddr->raw.family == PR_AF_INET) { michael@0: addr->inet.family = AF_INET; michael@0: addr->inet.port = prAddr->inet.port; michael@0: addr->inet.ip = prAddr->inet.ip; michael@0: } michael@0: else if (prAddr->raw.family == PR_AF_INET6) { michael@0: addr->inet6.family = AF_INET6; michael@0: addr->inet6.port = prAddr->ipv6.port; michael@0: addr->inet6.flowinfo = prAddr->ipv6.flowinfo; michael@0: memcpy(&addr->inet6.ip, &prAddr->ipv6.ip, sizeof(addr->inet6.ip.u8)); michael@0: addr->inet6.scope_id = prAddr->ipv6.scope_id; michael@0: } michael@0: #if defined(XP_UNIX) michael@0: else if (prAddr->raw.family == PR_AF_LOCAL) { michael@0: addr->local.family = AF_LOCAL; michael@0: memcpy(addr->local.path, prAddr->local.path, sizeof(addr->local.path)); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: // Copies the contents of a NetAddr to a PRNetAddr. michael@0: // Does not do a ptr safety check! michael@0: void NetAddrToPRNetAddr(const NetAddr *addr, PRNetAddr *prAddr) michael@0: { michael@0: if (addr->raw.family == AF_INET) { michael@0: prAddr->inet.family = PR_AF_INET; michael@0: prAddr->inet.port = addr->inet.port; michael@0: prAddr->inet.ip = addr->inet.ip; michael@0: } michael@0: else if (addr->raw.family == AF_INET6) { michael@0: prAddr->ipv6.family = PR_AF_INET6; michael@0: prAddr->ipv6.port = addr->inet6.port; michael@0: prAddr->ipv6.flowinfo = addr->inet6.flowinfo; michael@0: memcpy(&prAddr->ipv6.ip, &addr->inet6.ip, sizeof(addr->inet6.ip.u8)); michael@0: prAddr->ipv6.scope_id = addr->inet6.scope_id; michael@0: } michael@0: #if defined(XP_UNIX) michael@0: else if (addr->raw.family == AF_LOCAL) { michael@0: prAddr->local.family = PR_AF_LOCAL; michael@0: memcpy(prAddr->local.path, addr->local.path, sizeof(addr->local.path)); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: bool NetAddrToString(const NetAddr *addr, char *buf, uint32_t bufSize) michael@0: { michael@0: if (addr->raw.family == AF_INET) { michael@0: if (bufSize < INET_ADDRSTRLEN) { michael@0: return false; michael@0: } michael@0: struct in_addr nativeAddr = {}; michael@0: nativeAddr.s_addr = addr->inet.ip; michael@0: return !!inet_ntop_internal(AF_INET, &nativeAddr, buf, bufSize); michael@0: } michael@0: else if (addr->raw.family == AF_INET6) { michael@0: if (bufSize < INET6_ADDRSTRLEN) { michael@0: return false; michael@0: } michael@0: struct in6_addr nativeAddr = {}; michael@0: memcpy(&nativeAddr.s6_addr, &addr->inet6.ip, sizeof(addr->inet6.ip.u8)); michael@0: return !!inet_ntop_internal(AF_INET6, &nativeAddr, buf, bufSize); michael@0: } michael@0: #if defined(XP_UNIX) michael@0: else if (addr->raw.family == AF_LOCAL) { michael@0: if (bufSize < sizeof(addr->local.path)) { michael@0: // Many callers don't bother checking our return value, so michael@0: // null-terminate just in case. michael@0: if (bufSize > 0) { michael@0: buf[0] = '\0'; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: // Usually, the size passed to memcpy should be the size of the michael@0: // destination. Here, we know that the source is no larger than the michael@0: // destination, so using the source's size is always safe, whereas michael@0: // using the destination's size may cause us to read off the end of the michael@0: // source. michael@0: memcpy(buf, addr->local.path, sizeof(addr->local.path)); michael@0: return true; michael@0: } michael@0: #endif michael@0: return false; michael@0: } michael@0: michael@0: bool IsLoopBackAddress(const NetAddr *addr) michael@0: { michael@0: if (addr->raw.family == AF_INET) { michael@0: return (addr->inet.ip == htonl(INADDR_LOOPBACK)); michael@0: } michael@0: else if (addr->raw.family == AF_INET6) { michael@0: if (IPv6ADDR_IS_LOOPBACK(&addr->inet6.ip)) { michael@0: return true; michael@0: } else if (IPv6ADDR_IS_V4MAPPED(&addr->inet6.ip) && michael@0: IPv6ADDR_V4MAPPED_TO_IPADDR(&addr->inet6.ip) == htonl(INADDR_LOOPBACK)) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool IsIPAddrAny(const NetAddr *addr) michael@0: { michael@0: if (addr->raw.family == AF_INET) { michael@0: if (addr->inet.ip == htonl(INADDR_ANY)) { michael@0: return true; michael@0: } michael@0: } michael@0: else if (addr->raw.family == AF_INET6) { michael@0: if (IPv6ADDR_IS_UNSPECIFIED(&addr->inet6.ip)) { michael@0: return true; michael@0: } else if (IPv6ADDR_IS_V4MAPPED(&addr->inet6.ip) && michael@0: IPv6ADDR_V4MAPPED_TO_IPADDR(&addr->inet6.ip) == htonl(INADDR_ANY)) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool IsIPAddrV4Mapped(const NetAddr *addr) michael@0: { michael@0: if (addr->raw.family == AF_INET6) { michael@0: return IPv6ADDR_IS_V4MAPPED(&addr->inet6.ip); michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool IsIPAddrLocal(const NetAddr *addr) michael@0: { michael@0: MOZ_ASSERT(addr); michael@0: michael@0: // IPv4 RFC1918 and Link Local Addresses. michael@0: if (addr->raw.family == AF_INET) { michael@0: uint32_t addr32 = ntohl(addr->inet.ip); michael@0: if (addr32 >> 24 == 0x0A || // 10/8 prefix (RFC 1918). michael@0: addr32 >> 20 == 0xAC1 || // 172.16/12 prefix (RFC 1918). michael@0: addr32 >> 16 == 0xC0A8 || // 192.168/16 prefix (RFC 1918). michael@0: addr32 >> 16 == 0xA9FE) { // 169.254/16 prefix (Link Local). michael@0: return true; michael@0: } michael@0: } michael@0: // IPv6 Unique and Link Local Addresses. michael@0: if (addr->raw.family == AF_INET6) { michael@0: uint16_t addr16 = ntohs(addr->inet6.ip.u16[0]); michael@0: if (addr16 >> 9 == 0xfc >> 1 || // fc00::/7 Unique Local Address. michael@0: addr16 >> 6 == 0xfe80 >> 6) { // fe80::/10 Link Local Address. michael@0: return true; michael@0: } michael@0: } michael@0: // Not an IPv4/6 local address. michael@0: return false; michael@0: } michael@0: michael@0: nsresult michael@0: GetPort(const NetAddr *aAddr, uint16_t *aResult) michael@0: { michael@0: uint16_t port; michael@0: if (aAddr->raw.family == PR_AF_INET) { michael@0: port = aAddr->inet.port; michael@0: } else if (aAddr->raw.family == PR_AF_INET6) { michael@0: port = aAddr->inet6.port; michael@0: } else { michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: michael@0: *aResult = ntohs(port); michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: NetAddr::operator == (const NetAddr& other) const michael@0: { michael@0: if (this->raw.family != other.raw.family) { michael@0: return false; michael@0: } else if (this->raw.family == AF_INET) { michael@0: return (this->inet.port == other.inet.port) && michael@0: (this->inet.ip == other.inet.ip); michael@0: } else if (this->raw.family == AF_INET6) { michael@0: return (this->inet6.port == other.inet6.port) && michael@0: (this->inet6.flowinfo == other.inet6.flowinfo) && michael@0: (memcmp(&this->inet6.ip, &other.inet6.ip, michael@0: sizeof(this->inet6.ip)) == 0) && michael@0: (this->inet6.scope_id == other.inet6.scope_id); michael@0: #if defined(XP_UNIX) michael@0: } else if (this->raw.family == AF_LOCAL) { michael@0: return PL_strncmp(this->local.path, other.local.path, michael@0: ArrayLength(this->local.path)); michael@0: #endif michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: michael@0: michael@0: NetAddrElement::NetAddrElement(const PRNetAddr *prNetAddr) michael@0: { michael@0: PRNetAddrToNetAddr(prNetAddr, &mAddress); michael@0: } michael@0: michael@0: NetAddrElement::NetAddrElement(const NetAddrElement& netAddr) michael@0: { michael@0: mAddress = netAddr.mAddress; michael@0: } michael@0: michael@0: NetAddrElement::~NetAddrElement() michael@0: { michael@0: } michael@0: michael@0: AddrInfo::AddrInfo(const char *host, const PRAddrInfo *prAddrInfo, michael@0: bool disableIPv4, const char *cname) michael@0: { michael@0: MOZ_ASSERT(prAddrInfo, "Cannot construct AddrInfo with a null prAddrInfo pointer!"); michael@0: michael@0: Init(host, cname); michael@0: PRNetAddr tmpAddr; michael@0: void *iter = nullptr; michael@0: do { michael@0: iter = PR_EnumerateAddrInfo(iter, prAddrInfo, 0, &tmpAddr); michael@0: if (iter && (!disableIPv4 || tmpAddr.raw.family != PR_AF_INET)) { michael@0: NetAddrElement *addrElement = new NetAddrElement(&tmpAddr); michael@0: mAddresses.insertBack(addrElement); michael@0: } michael@0: } while (iter); michael@0: } michael@0: michael@0: AddrInfo::AddrInfo(const char *host, const char *cname) michael@0: { michael@0: Init(host, cname); michael@0: } michael@0: michael@0: AddrInfo::~AddrInfo() michael@0: { michael@0: NetAddrElement *addrElement; michael@0: while ((addrElement = mAddresses.popLast())) { michael@0: delete addrElement; michael@0: } michael@0: moz_free(mHostName); michael@0: moz_free(mCanonicalName); michael@0: } michael@0: michael@0: void michael@0: AddrInfo::Init(const char *host, const char *cname) michael@0: { michael@0: MOZ_ASSERT(host, "Cannot initialize AddrInfo with a null host pointer!"); michael@0: michael@0: size_t hostlen = strlen(host); michael@0: mHostName = static_cast(moz_xmalloc(hostlen + 1)); michael@0: memcpy(mHostName, host, hostlen + 1); michael@0: if (cname) { michael@0: size_t cnameLen = strlen(cname); michael@0: mCanonicalName = static_cast(moz_xmalloc(cnameLen + 1)); michael@0: memcpy(mCanonicalName, cname, cnameLen + 1); michael@0: } michael@0: else { michael@0: mCanonicalName = nullptr; michael@0: } michael@0: } michael@0: michael@0: void michael@0: AddrInfo::AddAddress(NetAddrElement *address) michael@0: { michael@0: MOZ_ASSERT(address, "Cannot add the address to an uninitialized list"); michael@0: michael@0: mAddresses.insertBack(address); michael@0: } michael@0: michael@0: size_t michael@0: AddrInfo::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const michael@0: { michael@0: size_t n = mallocSizeOf(this); michael@0: n += mallocSizeOf(mHostName); michael@0: n += mallocSizeOf(mCanonicalName); michael@0: n += mAddresses.sizeOfExcludingThis(mallocSizeOf); michael@0: return n; michael@0: } michael@0: michael@0: } // namespace dns michael@0: } // namespace mozilla