io.servicetalk.utils.internal.NetworkUtils Maven / Gradle / Ivy
Show all versions of servicetalk-utils-internal Show documentation
/*
* Copyright © 2018, 2021-2022 Apple Inc. and the ServiceTalk project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.servicetalk.utils.internal;
import static io.servicetalk.buffer.api.CharSequences.indexOf;
/**
* Network-related utilities.
*
* This class borrowed some of its methods from
* NetUtil class
* which was part of Netty.
*/
public final class NetworkUtils {
private NetworkUtils() {
// no instances
}
/**
* Takes a string and parses it to see if it is a valid IPV4 address.
*
* @param ip the IP-address to validate
* @return true, if the string represents an IPV4 address in dotted notation, false otherwise.
*/
public static boolean isValidIpV4Address(final CharSequence ip) {
return isValidIpV4Address(ip, 0, ip.length());
}
/**
* Takes a string and parses it to see if it is a valid IPV4 address.
*
* @param ip the IP-address to validate
* @param from the index in {@code ip} to start validation (inclusive).
* @param toExclusive the index in {@code ip} to end validation (exclusive).
* @return true, if the string represents an IPV4 address in dotted notation, false otherwise.
*/
private static boolean isValidIpV4Address(final CharSequence ip, int from, int toExclusive) {
int len = toExclusive - from;
int i;
return len <= 15 && len >= 7 &&
(i = indexOf(ip, '.', from + 1)) > 0 && isValidIpV4Word(ip, from, i) &&
(i = indexOf(ip, '.', from = i + 2)) > 0 && isValidIpV4Word(ip, from - 1, i) &&
(i = indexOf(ip, '.', from = i + 2)) > 0 && isValidIpV4Word(ip, from - 1, i) &&
isValidIpV4Word(ip, i + 1, toExclusive);
}
/**
* Takes a string and parses it to see if it is a valid IPV6 address.
*
* @param ip the IP-address to validate
* @return true, if the string represents an IPV6 address
*/
public static boolean isValidIpV6Address(final CharSequence ip) {
int end = ip.length();
if (end < 2) {
return false;
}
// strip "[]"
int start;
char c = ip.charAt(0);
if (c == '[') {
end--;
if (ip.charAt(end) != ']') {
// must have a close ]
return false;
}
start = 1;
c = ip.charAt(1);
} else {
start = 0;
}
int colons;
int compressBegin;
if (c == ':') {
// an IPv6 address can start with "::" or with a number
if (ip.charAt(start + 1) != ':') {
return false;
}
colons = 2;
compressBegin = start;
start += 2;
} else {
colons = 0;
compressBegin = -1;
}
int wordLen = 0;
loop:
for (int i = start; i < end; i++) {
c = ip.charAt(i);
if (isValidHexChar(c)) {
if (wordLen < 4) {
wordLen++;
continue;
}
return false;
}
switch (c) {
case ':':
if (colons > 7) {
return false;
}
if (ip.charAt(i - 1) == ':') {
if (compressBegin >= 0) {
return false;
}
compressBegin = i - 1;
} else {
wordLen = 0;
}
colons++;
break;
case '.':
// case for the last 32-bits represented as IPv4 x:x:x:x:x:x:d.d.d.d
// check a normal case (6 single colons)
if (compressBegin < 0 && colons != 6 ||
// a special case ::1:2:3:4:5:d.d.d.d allows 7 colons with an
// IPv4 ending, otherwise 7 :'s is bad
(colons == 7 && compressBegin >= start || colons > 7)) {
return false;
}
// Verify this address is of the correct structure to contain an IPv4 address.
// It must be IPv4-Mapped or IPv4-Compatible
// (see https://tools.ietf.org/html/rfc4291#section-2.5.5).
final int ipv4Start = i - wordLen;
int j = ipv4Start - 2; // index of character before the previous ':'.
if (isValidIPv4MappedChar(ip.charAt(j))) {
if (!isValidIPv4MappedChar(ip.charAt(j - 1)) ||
!isValidIPv4MappedChar(ip.charAt(j - 2)) ||
!isValidIPv4MappedChar(ip.charAt(j - 3))) {
return false;
}
j -= 5;
}
for (; j >= start; --j) {
final char tmpChar = ip.charAt(j);
if (tmpChar != '0' && tmpChar != ':') {
return false;
}
}
// 7 - is minimum IPv4 address length
int ipv4End = indexOf(ip, '%', ipv4Start + 7);
if (ipv4End < 0) {
ipv4End = end;
}
return isValidIpV4Address(ip, ipv4Start, ipv4End);
case '%':
// strip the interface name/index after the percent sign
end = i;
break loop;
default:
return false;
}
}
// normal case without compression
if (compressBegin < 0) {
return colons == 7 && wordLen > 0;
}
return compressBegin + 2 == end ||
// 8 colons is valid only if compression in start or end
wordLen > 0 && (colons < 8 || compressBegin <= start);
}
private static boolean isValidIpV4Word(final CharSequence word, final int from, final int toExclusive) {
final int len = toExclusive - from;
final char c0;
final char c1;
final char c2;
if (len < 1 || len > 3 || (c0 = word.charAt(from)) < '0') {
return false;
}
if (len == 3) {
return (c1 = word.charAt(from + 1)) >= '0' &&
(c2 = word.charAt(from + 2)) >= '0' &&
(c0 <= '1' && c1 <= '9' && c2 <= '9' ||
c0 == '2' && c1 <= '5' && (c2 <= '5' || c1 < '5' && c2 <= '9'));
}
return c0 <= '9' && (len == 1 || isValidNumericChar(word.charAt(from + 1)));
}
private static boolean isValidHexChar(final char c) {
return c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f';
}
private static boolean isValidNumericChar(final char c) {
return c >= '0' && c <= '9';
}
private static boolean isValidIPv4MappedChar(final char c) {
return c == 'f' || c == 'F';
}
}