/*
* Copyright (c) 2008-2012, Hazel Bilisim Ltd. All Rights Reserved.
*
* 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.
*/
package com.hazelcast.util;
import java.util.Collection;
import java.util.LinkedList;
public final class AddressUtil {
private AddressUtil() {
}
public static boolean matchAnyInterface(final String address, final Collection<String> interfaces) {
if (interfaces == null || interfaces.size() == 0) return false;
for (final String interfaceMask : interfaces) {
if (matchInterface(address, interfaceMask)) {
return true;
}
}
return false;
}
public static boolean matchInterface(final String address, final String interfaceMask) {
final AddressMatcher mask;
try {
mask = getAddressMatcher(interfaceMask);
} catch (Exception e) {
return false;
}
return mask.match(address);
}
public static AddressHolder getAddressHolder(String address) {
return getAddressHolder(address, -1);
}
public static AddressHolder getAddressHolder(final String address, int defaultPort) {
final int indexBracketStart = address.indexOf('[');
final int indexBracketEnd = address.indexOf(']', indexBracketStart);
final int indexColon = address.indexOf(':');
final int lastIndexColon = address.lastIndexOf(':');
String host;
int port = defaultPort;
String scopeId = null;
if (indexColon > -1 && lastIndexColon > indexColon) {
// IPv6
if (indexBracketStart == 0 && indexBracketEnd > indexBracketStart
&& lastIndexColon == indexBracketEnd + 1) {
host = address.substring(indexBracketStart + 1, indexBracketEnd);
port = Integer.parseInt(address.substring(lastIndexColon + 1));
} else {
host = address;
}
final int indexPercent = host.indexOf('%');
if (indexPercent != -1) {
scopeId = host.substring(indexPercent + 1);
host = host.substring(0, indexPercent);
}
} else if (indexColon > 0 && indexColon == lastIndexColon) {
host = address.substring(0, indexColon);
port = Integer.parseInt(address.substring(indexColon + 1));
} else {
host = address;
}
return new AddressHolder(host, port, scopeId);
}
public static boolean isIpAddress(String address) {
try {
return getAddressMatcher(address) != null;
} catch (InvalidAddressException e) {
return false;
}
}
public static AddressMatcher getAddressMatcher(String host) {
final String address = getAddressHolder(host).address;
final AddressMatcher matcher;
final int indexColon = address.indexOf(':');
final int lastIndexColon = address.lastIndexOf(':');
final int indexDot = address.indexOf('.');
final int lastIndexDot = address.lastIndexOf('.');
if (indexColon > -1 && lastIndexColon > indexColon) {
if (indexDot == -1) {
matcher = new Ip6AddressMatcher();
parseIpv6(matcher, address);
} else {
// IPv4 mapped IPv6
if (indexDot >= lastIndexDot) {
throw new InvalidAddressException(address);
}
final int lastIndexColon2 = address.lastIndexOf(':');
final String host2 = address.substring(lastIndexColon2 + 1);
matcher = new Ip4AddressMatcher();
parseIpv4(matcher, host2);
}
} else if (indexDot > -1 && lastIndexDot > indexDot && indexColon == -1) {
// IPv4
matcher = new Ip4AddressMatcher();
parseIpv4(matcher, address);
} else {
throw new InvalidAddressException(address);
}
return matcher;
}
private static void parseIpv4(AddressMatcher matcher, String address) {
final String[] parts = address.split("\\.");
if (parts.length != 4) {
throw new InvalidAddressException(address);
}
matcher.setAddress(parts);
}
private static void parseIpv6(AddressMatcher matcher, String address) {
if (address.indexOf('%') > -1) {
String[] parts = address.split("\\%");
address = parts[0];
}
final String[] parts = address.split("((?<=:)|(?=:))");
final LinkedList<String> ipString = new LinkedList<String>();
int count = 0;
int mark = -1;
for (int i = 0; i < parts.length; i++) {
String part = parts[i];
String nextPart = i < parts.length - 1 ? parts[i + 1] : null;
if ("".equals(part)) {
continue;
}
if (":".equals(part) && ":".equals(nextPart)) {
if (mark != -1) {
throw new InvalidAddressException(address);
}
mark = count;
} else if (!":".equals(part)) {
count++;
ipString.add(part);
}
}
if (mark > -1) {
final int remaining = (8 - count);
for (int i = 0; i < remaining; i++) {
ipString.add((i + mark), "0");
}
}
if (ipString.size() != 8) {
throw new InvalidAddressException(address);
}
matcher.setAddress(ipString.toArray(new String[0]));
}
// ----------------- UTILITY CLASSES ------------------
public static class AddressHolder {
public final String address;
public final String scopeId;
public final int port;
public AddressHolder(final String address, final int port,
final String scopeId) {
this.address = address;
this.scopeId = scopeId;
this.port = port;
}
}
/**
* http://docs.oracle.com/javase/1.5.0/docs/guide/net/ipv6_guide/index.html
*/
public static abstract class AddressMatcher {
protected final String[] address;
protected AddressMatcher(final String[] address) {
this.address = address;
}
public abstract boolean isIPv4();
public abstract boolean isIPv6();
public abstract void setAddress(String ip[]);
protected final boolean match(final String[] mask, String[] input, int radix) {
if (input != null && mask != null) {
for (int i = 0; i < mask.length; i++) {
if (!doMatch(mask[i], input[i], radix)) {
return false;
}
}
return true;
}
return false;
}
protected final boolean doMatch(final String mask, String input, int radix) {
final int dashIndex = mask.indexOf('-');
final int ipa = Integer.parseInt(input, radix);
if ("*".equals(mask)) {
return true;
} else if (dashIndex != -1) {
final int start = Integer.parseInt(mask.substring(0, dashIndex).trim(), radix);
final int end = Integer.parseInt(mask.substring(dashIndex + 1).trim(), radix);
if (ipa >= start && ipa <= end) {
return true;
}
} else {
final int x = Integer.parseInt(mask, radix);
if (x == ipa) {
return true;
}
}
return false;
}
public abstract String getAddress();
public abstract boolean match(AddressMatcher matcher);
public boolean match(final String address) {
try {
return match(getAddressMatcher(address));
} catch (Exception e) {
return false;
}
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append(getClass().getSimpleName());
sb.append('{');
sb.append(getAddress());
sb.append('}');
return sb.toString();
}
}
static class Ip4AddressMatcher extends AddressMatcher {
public Ip4AddressMatcher() {
super(new String[4]); // d.d.d.d
}
public boolean isIPv4() {
return true;
}
public boolean isIPv6() {
return false;
}
public void setAddress(String ip[]) {
for (int i = 0; i < ip.length; i++) {
this.address[i] = ip[i];
}
}
public boolean match(final AddressMatcher matcher) {
if (matcher.isIPv6()) return false;
final String[] mask = this.address;
final String[] input = ((Ip4AddressMatcher) matcher).address;
return match(mask, input, 10);
}
public String getAddress() {
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < address.length; i++) {
sb.append(address[i]);
if (i != address.length - 1) {
sb.append('.');
}
}
return sb.toString();
}
}
static class Ip6AddressMatcher extends AddressMatcher {
public Ip6AddressMatcher() {
super(new String[8]); // x:x:x:x:x:x:x:x%s
}
public boolean isIPv4() {
return false;
}
public boolean isIPv6() {
return true;
}
public void setAddress(String ip[]) {
for (int i = 0; i < ip.length; i++) {
this.address[i] = ip[i];
}
}
public boolean match(final AddressMatcher matcher) {
if (matcher.isIPv4()) return false;
final Ip6AddressMatcher a = (Ip6AddressMatcher) matcher;
final String[] mask = this.address;
final String[] input = a.address;
return match(mask, input, 16);
}
public String getAddress() {
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < address.length; i++) {
sb.append(address[i]);
if (i != address.length - 1) {
sb.append(':');
}
}
return sb.toString();
}
}
public static class InvalidAddressException extends IllegalArgumentException {
public InvalidAddressException(final String s) {
super("Illegal IP address format: " + s);
}
}
}