| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| |
| #include <algorithm> |
| #include <cstdlib> |
| #include <iterator> |
| #include <map> |
| |
| #include <fcntl.h> |
| #include <netdb.h> |
| #include <net/if.h> |
| #include <netinet/in.h> |
| #include <arpa/inet.h> |
| |
| #include <string.h> |
| |
| #include "net_util.h" |
| |
| namespace net { |
| |
| #ifndef INET6_ADDRSTRLEN /* for non IPv6 machines */ |
| #define INET6_ADDRSTRLEN 46 |
| #endif |
| |
| bool ParseIPLiteralToNumber(const std::string& ip_literal, |
| IPAddressNumber* ip_number) { |
| char buf[sizeof(struct in6_addr)]; |
| int size = sizeof(struct in_addr); |
| int mode = AF_INET; |
| if (ip_literal.find(':') != std::string::npos) { |
| mode = AF_INET6; |
| size = sizeof(struct in6_addr); |
| } |
| if (inet_pton(mode, ip_literal.c_str(), buf) != 1) { |
| return false; |
| } |
| ip_number->resize(size); |
| for (int i = 0; i < size; i++) { |
| (*ip_number)[i] = buf[i]; |
| } |
| return true; |
| } |
| |
| IPAddressNumber ConvertIPv4NumberToIPv6Number( |
| const IPAddressNumber& ipv4_number) { |
| // IPv4-mapped addresses are formed by: |
| // <80 bits of zeros> + <16 bits of ones> + <32-bit IPv4 address>. |
| IPAddressNumber ipv6_number; |
| ipv6_number.reserve(16); |
| ipv6_number.insert(ipv6_number.end(), 10, 0); |
| ipv6_number.push_back(0xFF); |
| ipv6_number.push_back(0xFF); |
| ipv6_number.insert(ipv6_number.end(), ipv4_number.begin(), ipv4_number.end()); |
| return ipv6_number; |
| } |
| |
| bool ParseCIDRBlock(const std::string& cidr_literal, |
| IPAddressNumber* ip_number, |
| size_t* prefix_length_in_bits) { |
| // We expect CIDR notation to match one of these two templates: |
| // <IPv4-literal> "/" <number of bits> |
| // <IPv6-literal> "/" <number of bits> |
| |
| std::vector<std::string> parts; |
| size_t split = cidr_literal.find('/'); |
| if (split == std::string::npos) |
| return false; |
| parts.push_back(cidr_literal.substr(0, split)); |
| parts.push_back(cidr_literal.substr(split + 1)); |
| if (parts[1].find('/') != std::string::npos) |
| return false; |
| |
| // Parse the IP address. |
| if (!ParseIPLiteralToNumber(parts[0], ip_number)) |
| return false; |
| |
| // Parse the prefix length. |
| int number_of_bits = atoi(parts[1].c_str()); |
| |
| // Make sure the prefix length is in a valid range. |
| if (number_of_bits < 0 || |
| number_of_bits > static_cast<int>(ip_number->size() * 8)) |
| return false; |
| |
| *prefix_length_in_bits = static_cast<size_t>(number_of_bits); |
| return true; |
| } |
| |
| bool IPNumberMatchesPrefix(const IPAddressNumber& ip_number, |
| const IPAddressNumber& ip_prefix, |
| size_t prefix_length_in_bits) { |
| // Both the input IP address and the prefix IP address should be |
| // either IPv4 or IPv6. |
| |
| // In case we have an IPv6 / IPv4 mismatch, convert the IPv4 addresses to |
| // IPv6 addresses in order to do the comparison. |
| if (ip_number.size() != ip_prefix.size()) { |
| if (ip_number.size() == 4) { |
| return IPNumberMatchesPrefix(ConvertIPv4NumberToIPv6Number(ip_number), |
| ip_prefix, prefix_length_in_bits); |
| } |
| return IPNumberMatchesPrefix(ip_number, |
| ConvertIPv4NumberToIPv6Number(ip_prefix), |
| 96 + prefix_length_in_bits); |
| } |
| |
| // Otherwise we are comparing two IPv4 addresses, or two IPv6 addresses. |
| // Compare all the bytes that fall entirely within the prefix. |
| int num_entire_bytes_in_prefix = prefix_length_in_bits / 8; |
| for (int i = 0; i < num_entire_bytes_in_prefix; ++i) { |
| if (ip_number[i] != ip_prefix[i]) |
| return false; |
| } |
| |
| // In case the prefix was not a multiple of 8, there will be 1 byte |
| // which is only partially masked. |
| int remaining_bits = prefix_length_in_bits % 8; |
| if (remaining_bits != 0) { |
| unsigned char mask = 0xFF << (8 - remaining_bits); |
| int i = num_entire_bytes_in_prefix; |
| if ((ip_number[i] & mask) != (ip_prefix[i] & mask)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } // namespace net |