1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=4 sw=2 sts=2 et cin: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "mozilla/net/DNS.h"
8 
9 #include "mozilla/ArrayUtils.h"
10 #include "mozilla/Assertions.h"
11 #include "mozilla/mozalloc.h"
12 #include "mozilla/StaticPrefs_network.h"
13 #include "nsContentUtils.h"
14 #include "nsString.h"
15 #include <string.h>
16 
17 #ifdef XP_WIN
18 #  include "ws2tcpip.h"
19 #endif
20 
21 namespace mozilla {
22 namespace net {
23 
inet_ntop_internal(int af,const void * src,char * dst,socklen_t size)24 const char* inet_ntop_internal(int af, const void* src, char* dst,
25                                socklen_t size) {
26 #ifdef XP_WIN
27   if (af == AF_INET) {
28     struct sockaddr_in s;
29     memset(&s, 0, sizeof(s));
30     s.sin_family = AF_INET;
31     memcpy(&s.sin_addr, src, sizeof(struct in_addr));
32     int result = getnameinfo((struct sockaddr*)&s, sizeof(struct sockaddr_in),
33                              dst, size, nullptr, 0, NI_NUMERICHOST);
34     if (result == 0) {
35       return dst;
36     }
37   } else if (af == AF_INET6) {
38     struct sockaddr_in6 s;
39     memset(&s, 0, sizeof(s));
40     s.sin6_family = AF_INET6;
41     memcpy(&s.sin6_addr, src, sizeof(struct in_addr6));
42     int result = getnameinfo((struct sockaddr*)&s, sizeof(struct sockaddr_in6),
43                              dst, size, nullptr, 0, NI_NUMERICHOST);
44     if (result == 0) {
45       return dst;
46     }
47   }
48   return nullptr;
49 #else
50   return inet_ntop(af, src, dst, size);
51 #endif
52 }
53 
54 // Copies the contents of a PRNetAddr to a NetAddr.
55 // Does not do a ptr safety check!
PRNetAddrToNetAddr(const PRNetAddr * prAddr,NetAddr * addr)56 void PRNetAddrToNetAddr(const PRNetAddr* prAddr, NetAddr* addr) {
57   if (prAddr->raw.family == PR_AF_INET) {
58     addr->inet.family = AF_INET;
59     addr->inet.port = prAddr->inet.port;
60     addr->inet.ip = prAddr->inet.ip;
61   } else if (prAddr->raw.family == PR_AF_INET6) {
62     addr->inet6.family = AF_INET6;
63     addr->inet6.port = prAddr->ipv6.port;
64     addr->inet6.flowinfo = prAddr->ipv6.flowinfo;
65     memcpy(&addr->inet6.ip, &prAddr->ipv6.ip, sizeof(addr->inet6.ip.u8));
66     addr->inet6.scope_id = prAddr->ipv6.scope_id;
67   }
68 #if defined(XP_UNIX)
69   else if (prAddr->raw.family == PR_AF_LOCAL) {
70     addr->local.family = AF_LOCAL;
71     memcpy(addr->local.path, prAddr->local.path, sizeof(addr->local.path));
72   }
73 #endif
74 }
75 
76 extern "C" {
77 // Rust bindings
78 
moz_netaddr_get_family(const NetAddr * addr)79 uint16_t moz_netaddr_get_family(const NetAddr* addr) {
80   return addr->raw.family;
81 }
82 
moz_netaddr_get_network_order_ip(const NetAddr * addr)83 uint32_t moz_netaddr_get_network_order_ip(const NetAddr* addr) {
84   return addr->inet.ip;
85 }
86 
moz_netaddr_get_ipv6(const NetAddr * addr)87 uint8_t const* moz_netaddr_get_ipv6(const NetAddr* addr) {
88   return addr->inet6.ip.u8;
89 }
90 
moz_netaddr_get_network_order_port(const NetAddr * addr)91 uint16_t moz_netaddr_get_network_order_port(const NetAddr* addr) {
92   if (addr->raw.family == PR_AF_INET) {
93     return addr->inet.port;
94   }
95   if (addr->raw.family == PR_AF_INET6) {
96     return addr->inet6.port;
97   }
98   return 0;
99 }
100 
101 }  // extern "C"
102 
103 // Copies the contents of a NetAddr to a PRNetAddr.
104 // Does not do a ptr safety check!
NetAddrToPRNetAddr(const NetAddr * addr,PRNetAddr * prAddr)105 void NetAddrToPRNetAddr(const NetAddr* addr, PRNetAddr* prAddr) {
106   if (addr->raw.family == AF_INET) {
107     prAddr->inet.family = PR_AF_INET;
108     prAddr->inet.port = addr->inet.port;
109     prAddr->inet.ip = addr->inet.ip;
110   } else if (addr->raw.family == AF_INET6) {
111     prAddr->ipv6.family = PR_AF_INET6;
112     prAddr->ipv6.port = addr->inet6.port;
113     prAddr->ipv6.flowinfo = addr->inet6.flowinfo;
114     memcpy(&prAddr->ipv6.ip, &addr->inet6.ip, sizeof(addr->inet6.ip.u8));
115     prAddr->ipv6.scope_id = addr->inet6.scope_id;
116   }
117 #if defined(XP_UNIX)
118   else if (addr->raw.family == AF_LOCAL) {
119     prAddr->local.family = PR_AF_LOCAL;
120     memcpy(prAddr->local.path, addr->local.path, sizeof(addr->local.path));
121   }
122 #elif defined(XP_WIN)
123   else if (addr->raw.family == AF_LOCAL) {
124     prAddr->local.family = PR_AF_LOCAL;
125     memcpy(prAddr->local.path, addr->local.path, sizeof(addr->local.path));
126   }
127 #endif
128 }
129 
ToStringBuffer(char * buf,uint32_t bufSize) const130 bool NetAddr::ToStringBuffer(char* buf, uint32_t bufSize) const {
131   const NetAddr* addr = this;
132   if (addr->raw.family == AF_INET) {
133     if (bufSize < INET_ADDRSTRLEN) {
134       return false;
135     }
136     struct in_addr nativeAddr = {};
137     nativeAddr.s_addr = addr->inet.ip;
138     return !!inet_ntop_internal(AF_INET, &nativeAddr, buf, bufSize);
139   }
140   if (addr->raw.family == AF_INET6) {
141     if (bufSize < INET6_ADDRSTRLEN) {
142       return false;
143     }
144     struct in6_addr nativeAddr = {};
145     memcpy(&nativeAddr.s6_addr, &addr->inet6.ip, sizeof(addr->inet6.ip.u8));
146     return !!inet_ntop_internal(AF_INET6, &nativeAddr, buf, bufSize);
147   }
148 #if defined(XP_UNIX)
149   if (addr->raw.family == AF_LOCAL) {
150     if (bufSize < sizeof(addr->local.path)) {
151       // Many callers don't bother checking our return value, so
152       // null-terminate just in case.
153       if (bufSize > 0) {
154         buf[0] = '\0';
155       }
156       return false;
157     }
158 
159     // Usually, the size passed to memcpy should be the size of the
160     // destination. Here, we know that the source is no larger than the
161     // destination, so using the source's size is always safe, whereas
162     // using the destination's size may cause us to read off the end of the
163     // source.
164     memcpy(buf, addr->local.path, sizeof(addr->local.path));
165     return true;
166   }
167 #endif
168   return false;
169 }
170 
ToString() const171 nsCString NetAddr::ToString() const {
172   nsCString out;
173   out.SetLength(kNetAddrMaxCStrBufSize);
174   if (ToStringBuffer(out.BeginWriting(), kNetAddrMaxCStrBufSize)) {
175     out.SetLength(strlen(out.BeginWriting()));
176     return out;
177   }
178   return ""_ns;
179 }
180 
IsLoopbackAddr() const181 bool NetAddr::IsLoopbackAddr() const {
182   if (IsLoopBackAddressWithoutIPv6Mapping()) {
183     return true;
184   }
185   const NetAddr* addr = this;
186   if (addr->raw.family != AF_INET6) {
187     return false;
188   }
189 
190   return IPv6ADDR_IS_V4MAPPED(&addr->inet6.ip) &&
191          IPv6ADDR_V4MAPPED_TO_IPADDR(&addr->inet6.ip) == htonl(INADDR_LOOPBACK);
192 }
193 
IsLoopBackAddressWithoutIPv6Mapping() const194 bool NetAddr::IsLoopBackAddressWithoutIPv6Mapping() const {
195   const NetAddr* addr = this;
196   if (addr->raw.family == AF_INET) {
197     // Consider 127.0.0.1/8 as loopback
198     uint32_t ipv4Addr = ntohl(addr->inet.ip);
199     return (ipv4Addr >> 24) == 127;
200   }
201 
202   return addr->raw.family == AF_INET6 && IPv6ADDR_IS_LOOPBACK(&addr->inet6.ip);
203 }
204 
IsLoopbackHostname(const nsACString & aAsciiHost)205 bool IsLoopbackHostname(const nsACString& aAsciiHost) {
206   // If the user has configured to proxy localhost addresses don't consider them
207   // to be secure
208   if (StaticPrefs::network_proxy_allow_hijacking_localhost() &&
209       !StaticPrefs::network_proxy_testing_localhost_is_secure_when_hijacked()) {
210     return false;
211   }
212 
213   nsAutoCString host;
214   nsContentUtils::ASCIIToLower(aAsciiHost, host);
215 
216   return host.EqualsLiteral("localhost") ||
217          StringEndsWith(host, ".localhost"_ns);
218 }
219 
HostIsIPLiteral(const nsACString & aAsciiHost)220 bool HostIsIPLiteral(const nsACString& aAsciiHost) {
221   NetAddr addr;
222   return NS_SUCCEEDED(addr.InitFromString(aAsciiHost));
223 }
224 
IsIPAddrAny() const225 bool NetAddr::IsIPAddrAny() const {
226   if (this->raw.family == AF_INET) {
227     if (this->inet.ip == htonl(INADDR_ANY)) {
228       return true;
229     }
230   } else if (this->raw.family == AF_INET6) {
231     if (IPv6ADDR_IS_UNSPECIFIED(&this->inet6.ip)) {
232       return true;
233     }
234     if (IPv6ADDR_IS_V4MAPPED(&this->inet6.ip) &&
235         IPv6ADDR_V4MAPPED_TO_IPADDR(&this->inet6.ip) == htonl(INADDR_ANY)) {
236       return true;
237     }
238   }
239   return false;
240 }
241 
NetAddr(const PRNetAddr * prAddr)242 NetAddr::NetAddr(const PRNetAddr* prAddr) { PRNetAddrToNetAddr(prAddr, this); }
243 
InitFromString(const nsACString & aString,uint16_t aPort)244 nsresult NetAddr::InitFromString(const nsACString& aString, uint16_t aPort) {
245   PRNetAddr prAddr{};
246   memset(&prAddr, 0, sizeof(PRNetAddr));
247   if (PR_StringToNetAddr(PromiseFlatCString(aString).get(), &prAddr) !=
248       PR_SUCCESS) {
249     return NS_ERROR_FAILURE;
250   }
251 
252   PRNetAddrToNetAddr(&prAddr, this);
253 
254   if (this->raw.family == PR_AF_INET) {
255     this->inet.port = PR_htons(aPort);
256   } else if (this->raw.family == PR_AF_INET6) {
257     this->inet6.port = PR_htons(aPort);
258   }
259   return NS_OK;
260 }
261 
IsIPAddrV4() const262 bool NetAddr::IsIPAddrV4() const { return this->raw.family == AF_INET; }
263 
IsIPAddrV4Mapped() const264 bool NetAddr::IsIPAddrV4Mapped() const {
265   if (this->raw.family == AF_INET6) {
266     return IPv6ADDR_IS_V4MAPPED(&this->inet6.ip);
267   }
268   return false;
269 }
270 
isLocalIPv4(uint32_t networkEndianIP)271 static bool isLocalIPv4(uint32_t networkEndianIP) {
272   uint32_t addr32 = ntohl(networkEndianIP);
273   return addr32 >> 24 == 0x0A ||    // 10/8 prefix (RFC 1918).
274          addr32 >> 20 == 0xAC1 ||   // 172.16/12 prefix (RFC 1918).
275          addr32 >> 16 == 0xC0A8 ||  // 192.168/16 prefix (RFC 1918).
276          addr32 >> 16 == 0xA9FE;    // 169.254/16 prefix (Link Local).
277 }
278 
IsIPAddrLocal() const279 bool NetAddr::IsIPAddrLocal() const {
280   const NetAddr* addr = this;
281 
282   // IPv4 RFC1918 and Link Local Addresses.
283   if (addr->raw.family == AF_INET) {
284     return isLocalIPv4(addr->inet.ip);
285   }
286   // IPv6 Unique and Link Local Addresses.
287   // or mapped IPv4 addresses
288   if (addr->raw.family == AF_INET6) {
289     uint16_t addr16 = ntohs(addr->inet6.ip.u16[0]);
290     if (addr16 >> 9 == 0xfc >> 1 ||    // fc00::/7 Unique Local Address.
291         addr16 >> 6 == 0xfe80 >> 6) {  // fe80::/10 Link Local Address.
292       return true;
293     }
294     if (IPv6ADDR_IS_V4MAPPED(&addr->inet6.ip)) {
295       return isLocalIPv4(IPv6ADDR_V4MAPPED_TO_IPADDR(&addr->inet6.ip));
296     }
297   }
298 
299   // Not an IPv4/6 local address.
300   return false;
301 }
302 
IsIPAddrShared() const303 bool NetAddr::IsIPAddrShared() const {
304   const NetAddr* addr = this;
305 
306   // IPv4 RFC6598.
307   if (addr->raw.family == AF_INET) {
308     uint32_t addr32 = ntohl(addr->inet.ip);
309     if (addr32 >> 22 == 0x644 >> 2) {  // 100.64/10 prefix (RFC 6598).
310       return true;
311     }
312   }
313 
314   // Not an IPv4 shared address.
315   return false;
316 }
317 
GetPort(uint16_t * aResult) const318 nsresult NetAddr::GetPort(uint16_t* aResult) const {
319   uint16_t port;
320   if (this->raw.family == PR_AF_INET) {
321     port = this->inet.port;
322   } else if (this->raw.family == PR_AF_INET6) {
323     port = this->inet6.port;
324   } else {
325     return NS_ERROR_NOT_INITIALIZED;
326   }
327 
328   *aResult = ntohs(port);
329   return NS_OK;
330 }
331 
operator ==(const NetAddr & other) const332 bool NetAddr::operator==(const NetAddr& other) const {
333   if (this->raw.family != other.raw.family) {
334     return false;
335   }
336   if (this->raw.family == AF_INET) {
337     return (this->inet.port == other.inet.port) &&
338            (this->inet.ip == other.inet.ip);
339   }
340   if (this->raw.family == AF_INET6) {
341     return (this->inet6.port == other.inet6.port) &&
342            (this->inet6.flowinfo == other.inet6.flowinfo) &&
343            (memcmp(&this->inet6.ip, &other.inet6.ip, sizeof(this->inet6.ip)) ==
344             0) &&
345            (this->inet6.scope_id == other.inet6.scope_id);
346 #if defined(XP_UNIX)
347   }
348   if (this->raw.family == AF_LOCAL) {
349     return strncmp(this->local.path, other.local.path,
350                    ArrayLength(this->local.path));
351 #endif
352   }
353   return false;
354 }
355 
operator <(const NetAddr & other) const356 bool NetAddr::operator<(const NetAddr& other) const {
357   if (this->raw.family != other.raw.family) {
358     return this->raw.family < other.raw.family;
359   }
360   if (this->raw.family == AF_INET) {
361     if (this->inet.ip == other.inet.ip) {
362       return this->inet.port < other.inet.port;
363     }
364     return this->inet.ip < other.inet.ip;
365   }
366   if (this->raw.family == AF_INET6) {
367     int cmpResult =
368         memcmp(&this->inet6.ip, &other.inet6.ip, sizeof(this->inet6.ip));
369     if (cmpResult) {
370       return cmpResult < 0;
371     }
372     if (this->inet6.port != other.inet6.port) {
373       return this->inet6.port < other.inet6.port;
374     }
375     return this->inet6.flowinfo < other.inet6.flowinfo;
376   }
377   return false;
378 }
379 
AddrInfo(const nsACString & host,const PRAddrInfo * prAddrInfo,bool disableIPv4,bool filterNameCollision,const nsACString & cname)380 AddrInfo::AddrInfo(const nsACString& host, const PRAddrInfo* prAddrInfo,
381                    bool disableIPv4, bool filterNameCollision,
382                    const nsACString& cname)
383     : mHostName(host), mCanonicalName(cname) {
384   MOZ_ASSERT(prAddrInfo,
385              "Cannot construct AddrInfo with a null prAddrInfo pointer!");
386   const uint32_t nameCollisionAddr = htonl(0x7f003535);  // 127.0.53.53
387 
388   PRNetAddr tmpAddr;
389   void* iter = nullptr;
390   do {
391     iter = PR_EnumerateAddrInfo(iter, prAddrInfo, 0, &tmpAddr);
392     bool addIt = iter && (!disableIPv4 || tmpAddr.raw.family != PR_AF_INET) &&
393                  (!filterNameCollision || tmpAddr.raw.family != PR_AF_INET ||
394                   (tmpAddr.inet.ip != nameCollisionAddr));
395     if (addIt) {
396       NetAddr elem(&tmpAddr);
397       mAddresses.AppendElement(elem);
398     }
399   } while (iter);
400 }
401 
AddrInfo(const nsACString & host,const nsACString & cname,DNSResolverType aResolverType,unsigned int aTRRType,nsTArray<NetAddr> && addresses)402 AddrInfo::AddrInfo(const nsACString& host, const nsACString& cname,
403                    DNSResolverType aResolverType, unsigned int aTRRType,
404                    nsTArray<NetAddr>&& addresses)
405     : mHostName(host),
406       mCanonicalName(cname),
407       mResolverType(aResolverType),
408       mTRRType(aTRRType),
409       mAddresses(std::move(addresses)) {}
410 
AddrInfo(const nsACString & host,DNSResolverType aResolverType,unsigned int aTRRType,nsTArray<NetAddr> && addresses,uint32_t aTTL)411 AddrInfo::AddrInfo(const nsACString& host, DNSResolverType aResolverType,
412                    unsigned int aTRRType, nsTArray<NetAddr>&& addresses,
413                    uint32_t aTTL)
414     : ttl(aTTL),
415       mHostName(host),
416       mCanonicalName(),
417       mResolverType(aResolverType),
418       mTRRType(aTRRType),
419       mAddresses(std::move(addresses)) {}
420 
421 // deep copy constructor
AddrInfo(const AddrInfo * src)422 AddrInfo::AddrInfo(const AddrInfo* src) {
423   mHostName = src->mHostName;
424   mCanonicalName = src->mCanonicalName;
425   ttl = src->ttl;
426   mResolverType = src->mResolverType;
427   mTRRType = src->mTRRType;
428   mTrrFetchDuration = src->mTrrFetchDuration;
429   mTrrFetchDurationNetworkOnly = src->mTrrFetchDurationNetworkOnly;
430 
431   mAddresses = src->mAddresses.Clone();
432 }
433 
434 AddrInfo::~AddrInfo() = default;
435 
SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const436 size_t AddrInfo::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const {
437   size_t n = mallocSizeOf(this);
438   n += mHostName.SizeOfExcludingThisIfUnshared(mallocSizeOf);
439   n += mCanonicalName.SizeOfExcludingThisIfUnshared(mallocSizeOf);
440   n += mAddresses.ShallowSizeOfExcludingThis(mallocSizeOf);
441   return n;
442 }
443 
444 }  // namespace net
445 }  // namespace mozilla
446