/* * Copyright (c) 2011-2012 ICM Uniwersytet Warszawski All rights reserved. * See LICENCE file for licensing information. * * Derived from the code copyrighted and licensed as follows: * * Copyright (c) Members of the EGEE Collaboration. 2004. * See http://www.eu-egee.org/partners/ for details on the copyright * holders. * * 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 eu.emi.security.authn.x509.helpers.proxy; /** * Helpers for IP addresses comparison. * Mostly the code from Tigran's ipmatcher library (GNU license). * * @author Tigran Mkrtchyan * @author K. Benedyczak */ public class IPAddressHelper { private static final int IPv4_FULL_MASK = 32; private static final int IPv6_FULL_MASK = 128; private static final int IPv6_HALF_MASK = 64; /** * Tests whether the ipAddress is within the address space defined by * the ipAddressWithNetmask. * * @param ipBytes * The IP address bytes to compare against the address * space. * @param ipAddressWithNetmask * The 8 (IPv4) or 32 (IPv6) byte array containing in the * first half the base IP address bytes and in the second * half the netmask bytes. * @return true if ip matches subnet. */ public static boolean isWithinAddressSpace(byte[] ipBytes, byte[] ipAddressWithNetmask) { if (!(ipAddressWithNetmask.length == 8 && ipBytes.length == 4) && !(ipAddressWithNetmask.length == 32 && ipBytes.length == 16)) { throw new IllegalArgumentException( "IP address and IP address-netmask length mismatch, should be either (4 and 8) or (16 and 32), actual lengths were: " + ipBytes.length + " and " + ipAddressWithNetmask.length + "."); } if (ipBytes.length == 4) { int mask = getCidrNetmask(4, ipAddressWithNetmask, 4); /* * IPv4 can be represented as a 32 bit ints. */ int ipAsInt = getInt(ipBytes, 0); int netAsInt = getInt(ipAddressWithNetmask, 0); return (ipAsInt ^ netAsInt) >> (IPv4_FULL_MASK - mask) == 0; } /** * IPv6 can be represented as two 64 bit longs. * * We evaluate second long only if bitmask bigger than 64. The * second longs are created only if needed as it turned to be * the slowest part. */ long ipAsLong0 = getLong(ipBytes, 0); long netAsLong0 = getLong(ipAddressWithNetmask, 0); int mask = getCidrNetmask(16, ipAddressWithNetmask, 16); if (mask > 64) { long ipAsLong1 = getLong(ipBytes, 8); long netAsLong1 = getLong(ipAddressWithNetmask, 8); return (ipAsLong0 == netAsLong0) & (ipAsLong1 ^ netAsLong1) >> (IPv6_FULL_MASK - mask) == 0; } return (ipAsLong0 ^ netAsLong0) >> (IPv6_HALF_MASK - mask) == 0; } /** * Returns the big-endian {@code long} value whose byte representation * is the 8 bytes of <code>bytes</code> staring <code>offset</code>. * * @param bytes * @param offset * @return long value */ private static long getLong(byte[] bytes, int offset) { return (bytes[offset] & 0xFFL) << 56 | (bytes[offset + 1] & 0xFFL) << 48 | (bytes[offset + 2] & 0xFFL) << 40 | (bytes[offset + 3] & 0xFFL) << 32 | (bytes[offset + 4] & 0xFFL) << 24 | (bytes[offset + 5] & 0xFFL) << 16 | (bytes[offset + 6] & 0xFFL) << 8 | (bytes[offset + 7] & 0xFFL); } /** * Returns the big-endian {@code int} value whose byte representation is * the 4 bytes of <code>bytes</code> staring <code>offset</code>. * * @param bytes * @param offset * @return int value */ private static int getInt(byte[] bytes, int offset) { return (bytes[offset + 0] & 0xFF) << 24 | (bytes[offset + 1] & 0xFF) << 16 | (bytes[offset + 2] & 0xFF) << 8 | (bytes[offset + 3] & 0xFF); } private static int getCidrNetmask(int size, byte[] netmask, int offset) { int ret = 0; for (int i=0; i<size; i++) { if (netmask[i+offset] != -1) //-1 == 255 unsigned { int maskByteReversed = (~(netmask[i+offset]))&0xff; int bitPfx = Integer.numberOfLeadingZeros(maskByteReversed)-24; return ret+bitPfx; } else ret += 8; } return ret; } }