Handle IPv4-mapped addresses in IP classification functions.

This change ensures that functions like IPIsLoopback, IPIsLinkLocal,
IPIsPrivateNetwork, and IPIsSharedNetwork correctly handle
IPv4-mapped IPv6 addresses by normalizing them before classification.

Bug: webrtc:479635018
Change-Id: I3a99f44e090d0cc0d2e1e7b55b0bfb75be78315f
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/462842
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#47406}
diff --git a/rtc_base/ip_address.cc b/rtc_base/ip_address.cc
index 179bc37..007f13e 100644
--- a/rtc_base/ip_address.cc
+++ b/rtc_base/ip_address.cc
@@ -229,12 +229,13 @@
 }
 
 bool IPIsPrivateNetwork(const IPAddress& ip) {
-  switch (ip.family()) {
+  IPAddress normalized = ip.Normalized();
+  switch (normalized.family()) {
     case AF_INET: {
-      return IPIsPrivateNetworkV4(ip);
+      return IPIsPrivateNetworkV4(normalized);
     }
     case AF_INET6: {
-      return IPIsPrivateNetworkV6(ip);
+      return IPIsPrivateNetworkV6(normalized);
     }
   }
   return false;
@@ -246,8 +247,9 @@
 }
 
 bool IPIsSharedNetwork(const IPAddress& ip) {
-  if (ip.family() == AF_INET) {
-    return IPIsSharedNetworkV4(ip);
+  IPAddress normalized = ip.Normalized();
+  if (normalized.family() == AF_INET) {
+    return IPIsSharedNetworkV4(normalized);
   }
   return false;
 }
@@ -324,12 +326,13 @@
 }
 
 bool IPIsLoopback(const IPAddress& ip) {
-  switch (ip.family()) {
+  IPAddress normalized = ip.Normalized();
+  switch (normalized.family()) {
     case AF_INET: {
-      return IPIsLoopbackV4(ip);
+      return IPIsLoopbackV4(normalized);
     }
     case AF_INET6: {
-      return IPIsLoopbackV6(ip);
+      return IPIsLoopbackV6(normalized);
     }
   }
   return false;
@@ -345,12 +348,13 @@
 }
 
 size_t HashIP(const IPAddress& ip) {
-  switch (ip.family()) {
+  IPAddress normalized = ip.Normalized();
+  switch (normalized.family()) {
     case AF_INET: {
-      return ip.ipv4_address().s_addr;
+      return normalized.ipv4_address().s_addr;
     }
     case AF_INET6: {
-      in6_addr v6addr = ip.ipv6_address();
+      in6_addr v6addr = normalized.ipv6_address();
       const uint32_t* v6_as_ints =
           reinterpret_cast<const uint32_t*>(&v6addr.s6_addr);
       return v6_as_ints[0] ^ v6_as_ints[1] ^ v6_as_ints[2] ^ v6_as_ints[3];
@@ -483,12 +487,13 @@
 }
 
 bool IPIsLinkLocal(const IPAddress& ip) {
-  switch (ip.family()) {
+  IPAddress normalized = ip.Normalized();
+  switch (normalized.family()) {
     case AF_INET: {
-      return IPIsLinkLocalV4(ip);
+      return IPIsLinkLocalV4(normalized);
     }
     case AF_INET6: {
-      return IPIsLinkLocalV6(ip);
+      return IPIsLinkLocalV6(normalized);
     }
   }
   return false;
diff --git a/rtc_base/ip_address_unittest.cc b/rtc_base/ip_address_unittest.cc
index 1e91727..38b625b 100644
--- a/rtc_base/ip_address_unittest.cc
+++ b/rtc_base/ip_address_unittest.cc
@@ -634,6 +634,28 @@
   EXPECT_TRUE(IPIsAny(IPAddress(kIPv4MappedAnyAddr)));
 }
 
+TEST(IPAddressTest, TestMappedLocalAddresses) {
+  // IPv4 loopback: 127.0.0.1
+  IPAddress v4_loopback(0x7f000001);
+  // IPv4-mapped IPv6 loopback: ::ffff:127.0.0.1
+  IPAddress mapped_loopback = v4_loopback.AsIPv6Address();
+  EXPECT_TRUE(IPIsLoopback(mapped_loopback));
+
+  // IPv4 private: 192.168.1.1
+  IPAddress v4_private;
+  EXPECT_TRUE(IPFromString("192.168.1.1", &v4_private));
+  // IPv4-mapped IPv6 private: ::ffff:192.168.1.1
+  IPAddress mapped_private = v4_private.AsIPv6Address();
+  EXPECT_TRUE(IPIsPrivate(mapped_private));
+
+  // IPv4 link-local: 169.254.1.1
+  IPAddress v4_linklocal;
+  EXPECT_TRUE(IPFromString("169.254.1.1", &v4_linklocal));
+  // IPv4-mapped IPv6 link-local: ::ffff:169.254.1.1
+  IPAddress mapped_linklocal = v4_linklocal.AsIPv6Address();
+  EXPECT_TRUE(IPIsLinkLocal(mapped_linklocal));
+}
+
 TEST(IPAddressTest, TestIsEui64) {
   IPAddress addr;
   EXPECT_TRUE(IPFromString(kIPv6EuiAddrString, &addr));