This source file includes following definitions.
- BaseForType
- DoFindIPv4Components
- IPv4ComponentToNumber
- DoIPv4AddressToNumber
- DoCanonicalizeIPv4Address
- DoParseIPv6
- CheckIPv6ComponentsSize
- IPv6HexComponentToNumber
- DoIPv6AddressToNumber
- ChooseIPv6ContractionRange
- DoCanonicalizeIPv6Address
- AppendIPv4Address
- AppendIPv6Address
- FindIPv4Components
- FindIPv4Components
- CanonicalizeIPAddress
- CanonicalizeIPAddress
- IPv4AddressToNumber
- IPv4AddressToNumber
- IPv6AddressToNumber
- IPv6AddressToNumber
#include "url/url_canon_ip.h"
#include <stdlib.h>
#include "base/basictypes.h"
#include "base/logging.h"
#include "url/url_canon_internal.h"
namespace url_canon {
namespace {
int BaseForType(SharedCharTypes type) {
switch (type) {
case CHAR_HEX:
return 16;
case CHAR_DEC:
return 10;
case CHAR_OCT:
return 8;
default:
return 0;
}
}
template<typename CHAR, typename UCHAR>
bool DoFindIPv4Components(const CHAR* spec,
const url_parse::Component& host,
url_parse::Component components[4]) {
if (!host.is_nonempty())
return false;
int cur_component = 0;
int cur_component_begin = host.begin;
int end = host.end();
for (int i = host.begin; ; i++) {
if (i >= end || spec[i] == '.') {
int component_len = i - cur_component_begin;
components[cur_component] =
url_parse::Component(cur_component_begin, component_len);
cur_component_begin = i + 1;
cur_component++;
if (component_len == 0 && (i < end || cur_component == 1))
return false;
if (i >= end)
break;
if (cur_component == 4) {
if (spec[i] == '.' && i + 1 == end)
break;
return false;
}
} else if (static_cast<UCHAR>(spec[i]) >= 0x80 ||
!IsIPv4Char(static_cast<unsigned char>(spec[i]))) {
return false;
}
}
while (cur_component < 4)
components[cur_component++] = url_parse::Component();
return true;
}
template<typename CHAR>
CanonHostInfo::Family IPv4ComponentToNumber(
const CHAR* spec,
const url_parse::Component& component,
uint32* number) {
SharedCharTypes base;
int base_prefix_len = 0;
if (spec[component.begin] == '0') {
if (component.len == 1) {
base = CHAR_DEC;
} else if (spec[component.begin + 1] == 'X' ||
spec[component.begin + 1] == 'x') {
base = CHAR_HEX;
base_prefix_len = 2;
} else {
base = CHAR_OCT;
base_prefix_len = 1;
}
} else {
base = CHAR_DEC;
}
while (base_prefix_len < component.len &&
spec[component.begin + base_prefix_len] == '0')
base_prefix_len++;
const int kMaxComponentLen = 16;
char buf[kMaxComponentLen + 1];
int dest_i = 0;
for (int i = component.begin + base_prefix_len; i < component.end(); i++) {
char input = static_cast<char>(spec[i]);
if (!IsCharOfType(input, base))
return CanonHostInfo::NEUTRAL;
if (dest_i < kMaxComponentLen)
buf[dest_i++] = input;
}
buf[dest_i] = '\0';
uint64 num = _strtoui64(buf, NULL, BaseForType(base));
if (num > kuint32max)
return CanonHostInfo::BROKEN;
*number = static_cast<uint32>(num);
return CanonHostInfo::IPV4;
}
template<typename CHAR>
CanonHostInfo::Family DoIPv4AddressToNumber(const CHAR* spec,
const url_parse::Component& host,
unsigned char address[4],
int* num_ipv4_components) {
url_parse::Component components[4];
if (!FindIPv4Components(spec, host, components))
return CanonHostInfo::NEUTRAL;
uint32 component_values[4];
int existing_components = 0;
bool broken = false;
for (int i = 0; i < 4; i++) {
if (components[i].len <= 0)
continue;
CanonHostInfo::Family family = IPv4ComponentToNumber(
spec, components[i], &component_values[existing_components]);
if (family == CanonHostInfo::BROKEN) {
broken = true;
} else if (family != CanonHostInfo::IPV4) {
return family;
}
existing_components++;
}
if (broken)
return CanonHostInfo::BROKEN;
for (int i = 0; i < existing_components - 1; i++) {
if (component_values[i] > kuint8max)
return CanonHostInfo::BROKEN;
address[i] = static_cast<unsigned char>(component_values[i]);
}
uint32 last_value = component_values[existing_components - 1];
for (int i = 3; i >= existing_components - 1; i--) {
address[i] = static_cast<unsigned char>(last_value);
last_value >>= 8;
}
if (last_value != 0)
return CanonHostInfo::BROKEN;
*num_ipv4_components = existing_components;
return CanonHostInfo::IPV4;
}
template<typename CHAR, typename UCHAR>
bool DoCanonicalizeIPv4Address(const CHAR* spec,
const url_parse::Component& host,
CanonOutput* output,
CanonHostInfo* host_info) {
host_info->family = IPv4AddressToNumber(
spec, host, host_info->address, &host_info->num_ipv4_components);
switch (host_info->family) {
case CanonHostInfo::IPV4:
host_info->out_host.begin = output->length();
AppendIPv4Address(host_info->address, output);
host_info->out_host.len = output->length() - host_info->out_host.begin;
return true;
case CanonHostInfo::BROKEN:
return true;
default:
return false;
}
}
struct IPv6Parsed {
void reset() {
num_hex_components = 0;
index_of_contraction = -1;
ipv4_component.reset();
}
url_parse::Component hex_components[8];
int num_hex_components;
int index_of_contraction;
url_parse::Component ipv4_component;
};
template<typename CHAR, typename UCHAR>
bool DoParseIPv6(const CHAR* spec,
const url_parse::Component& host,
IPv6Parsed* parsed) {
parsed->reset();
if (!host.is_nonempty())
return false;
int begin = host.begin;
int end = host.end();
int cur_component_begin = begin;
for (int i = begin; ; i++) {
bool is_colon = spec[i] == ':';
bool is_contraction = is_colon && i < end - 1 && spec[i + 1] == ':';
if (is_colon || i == end) {
int component_len = i - cur_component_begin;
if (component_len > 4)
return false;
if (component_len == 0) {
if (!((is_contraction && i == begin) || (i == end &&
parsed->index_of_contraction == parsed->num_hex_components)))
return false;
}
if (component_len > 0) {
if (parsed->num_hex_components >= 8)
return false;
parsed->hex_components[parsed->num_hex_components++] =
url_parse::Component(cur_component_begin, component_len);
}
}
if (i == end)
break;
if (is_contraction) {
if (parsed->index_of_contraction != -1)
return false;
parsed->index_of_contraction = parsed->num_hex_components;
++i;
}
if (is_colon) {
cur_component_begin = i + 1;
} else {
if (static_cast<UCHAR>(spec[i]) >= 0x80)
return false;
if (!IsHexChar(static_cast<unsigned char>(spec[i]))) {
if (IsIPv4Char(static_cast<unsigned char>(spec[i]))) {
parsed->ipv4_component = url_parse::Component(
cur_component_begin, end - cur_component_begin);
break;
} else {
return false;
}
}
}
}
return true;
}
bool CheckIPv6ComponentsSize(const IPv6Parsed& parsed,
int* out_num_bytes_of_contraction) {
int num_bytes_without_contraction = parsed.num_hex_components * 2;
if (parsed.ipv4_component.is_valid())
num_bytes_without_contraction += 4;
int num_bytes_of_contraction = 0;
if (parsed.index_of_contraction != -1) {
num_bytes_of_contraction = 16 - num_bytes_without_contraction;
if (num_bytes_of_contraction < 2)
num_bytes_of_contraction = 2;
}
if (num_bytes_without_contraction + num_bytes_of_contraction != 16)
return false;
*out_num_bytes_of_contraction = num_bytes_of_contraction;
return true;
}
template<typename CHAR>
uint16 IPv6HexComponentToNumber(const CHAR* spec,
const url_parse::Component& component) {
DCHECK(component.len <= 4);
char buf[5];
for (int i = 0; i < component.len; ++i)
buf[i] = static_cast<char>(spec[component.begin + i]);
buf[component.len] = '\0';
return static_cast<uint16>(_strtoui64(buf, NULL, 16));
}
template<typename CHAR, typename UCHAR>
bool DoIPv6AddressToNumber(const CHAR* spec,
const url_parse::Component& host,
unsigned char address[16]) {
int end = host.end();
if (!host.is_nonempty() || spec[host.begin] != '[' || spec[end - 1] != ']')
return false;
url_parse::Component ipv6_comp(host.begin + 1, host.len - 2);
IPv6Parsed ipv6_parsed;
if (!DoParseIPv6<CHAR, UCHAR>(spec, ipv6_comp, &ipv6_parsed))
return false;
int num_bytes_of_contraction;
if (!CheckIPv6ComponentsSize(ipv6_parsed, &num_bytes_of_contraction))
return false;
int cur_index_in_address = 0;
for (int i = 0; i <= ipv6_parsed.num_hex_components; ++i) {
if (i == ipv6_parsed.index_of_contraction) {
for (int j = 0; j < num_bytes_of_contraction; ++j)
address[cur_index_in_address++] = 0;
}
if (i != ipv6_parsed.num_hex_components) {
uint16 number = IPv6HexComponentToNumber<CHAR>(
spec, ipv6_parsed.hex_components[i]);
address[cur_index_in_address++] = (number & 0xFF00) >> 8;
address[cur_index_in_address++] = (number & 0x00FF);
}
}
if (ipv6_parsed.ipv4_component.is_valid()) {
int ignored_num_ipv4_components;
if (CanonHostInfo::IPV4 !=
IPv4AddressToNumber(spec,
ipv6_parsed.ipv4_component,
&address[cur_index_in_address],
&ignored_num_ipv4_components))
return false;
}
return true;
}
void ChooseIPv6ContractionRange(const unsigned char address[16],
url_parse::Component* contraction_range) {
url_parse::Component max_range;
url_parse::Component cur_range;
for (int i = 0; i < 16; i += 2) {
bool is_zero = (address[i] == 0 && address[i + 1] == 0);
if (is_zero) {
if (!cur_range.is_valid())
cur_range = url_parse::Component(i, 0);
cur_range.len += 2;
}
if (!is_zero || i == 14) {
if (cur_range.len > 2 && cur_range.len > max_range.len) {
max_range = cur_range;
}
cur_range.reset();
}
}
*contraction_range = max_range;
}
template<typename CHAR, typename UCHAR>
bool DoCanonicalizeIPv6Address(const CHAR* spec,
const url_parse::Component& host,
CanonOutput* output,
CanonHostInfo* host_info) {
if (!IPv6AddressToNumber(spec, host, host_info->address)) {
for (int i = host.begin; i < host.end(); i++) {
switch (spec[i]) {
case '[':
case ']':
case ':':
host_info->family = CanonHostInfo::BROKEN;
return true;
}
}
host_info->family = CanonHostInfo::NEUTRAL;
return false;
}
host_info->out_host.begin = output->length();
output->push_back('[');
AppendIPv6Address(host_info->address, output);
output->push_back(']');
host_info->out_host.len = output->length() - host_info->out_host.begin;
host_info->family = CanonHostInfo::IPV6;
return true;
}
}
void AppendIPv4Address(const unsigned char address[4], CanonOutput* output) {
for (int i = 0; i < 4; i++) {
char str[16];
_itoa_s(address[i], str, 10);
for (int ch = 0; str[ch] != 0; ch++)
output->push_back(str[ch]);
if (i != 3)
output->push_back('.');
}
}
void AppendIPv6Address(const unsigned char address[16], CanonOutput* output) {
url_parse::Component contraction_range;
ChooseIPv6ContractionRange(address, &contraction_range);
for (int i = 0; i <= 14;) {
DCHECK(i % 2 == 0);
if (i == contraction_range.begin && contraction_range.len > 0) {
if (i == 0)
output->push_back(':');
output->push_back(':');
i = contraction_range.end();
} else {
int x = address[i] << 8 | address[i + 1];
i += 2;
char str[5];
_itoa_s(x, str, 16);
for (int ch = 0; str[ch] != 0; ++ch)
output->push_back(str[ch]);
if (i < 16)
output->push_back(':');
}
}
}
bool FindIPv4Components(const char* spec,
const url_parse::Component& host,
url_parse::Component components[4]) {
return DoFindIPv4Components<char, unsigned char>(spec, host, components);
}
bool FindIPv4Components(const base::char16* spec,
const url_parse::Component& host,
url_parse::Component components[4]) {
return DoFindIPv4Components<base::char16, base::char16>(
spec, host, components);
}
void CanonicalizeIPAddress(const char* spec,
const url_parse::Component& host,
CanonOutput* output,
CanonHostInfo* host_info) {
if (DoCanonicalizeIPv4Address<char, unsigned char>(
spec, host, output, host_info))
return;
if (DoCanonicalizeIPv6Address<char, unsigned char>(
spec, host, output, host_info))
return;
}
void CanonicalizeIPAddress(const base::char16* spec,
const url_parse::Component& host,
CanonOutput* output,
CanonHostInfo* host_info) {
if (DoCanonicalizeIPv4Address<base::char16, base::char16>(
spec, host, output, host_info))
return;
if (DoCanonicalizeIPv6Address<base::char16, base::char16>(
spec, host, output, host_info))
return;
}
CanonHostInfo::Family IPv4AddressToNumber(const char* spec,
const url_parse::Component& host,
unsigned char address[4],
int* num_ipv4_components) {
return DoIPv4AddressToNumber<char>(spec, host, address, num_ipv4_components);
}
CanonHostInfo::Family IPv4AddressToNumber(const base::char16* spec,
const url_parse::Component& host,
unsigned char address[4],
int* num_ipv4_components) {
return DoIPv4AddressToNumber<base::char16>(
spec, host, address, num_ipv4_components);
}
bool IPv6AddressToNumber(const char* spec,
const url_parse::Component& host,
unsigned char address[16]) {
return DoIPv6AddressToNumber<char, unsigned char>(spec, host, address);
}
bool IPv6AddressToNumber(const base::char16* spec,
const url_parse::Component& host,
unsigned char address[16]) {
return DoIPv6AddressToNumber<base::char16, base::char16>(spec, host, address);
}
}