Package org.apache.lenya.ac.impl

Source Code of org.apache.lenya.ac.impl.AbstractIPRange

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
*  contributor license agreements.  See the NOTICE file distributed with
*  this work for additional information regarding copyright ownership.
*  The ASF 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.
*
*/

/* $Id: AbstractIPRange.java 531479 2007-04-23 14:21:35Z andreas $  */

package org.apache.lenya.ac.impl;

import java.io.File;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;

import org.apache.avalon.framework.logger.Logger;
import org.apache.lenya.ac.AccessControlException;
import org.apache.lenya.ac.IPRange;
import org.apache.lenya.ac.ItemManager;
import org.apache.lenya.ac.Machine;
import org.apache.lenya.net.InetAddressUtil;

/**
* <p>
* A range of IP addresses, expressed by a network address and a subnet mask.
* </p>
* <p>
* Note: this class does not enforce that the network address and the subnet mask have the same size
* (i.e. either both IPv4 or both IPv6 addresses). If the the network address and subnet mask have
* different sizes, the range does not contain any hosts, that is {@link #contains(Machine)} will
* always return <code>false</code>.
* </p>
*/
public abstract class AbstractIPRange extends AbstractGroupable implements IPRange {
    /*
     * FIXME by zisch@dals.ch: Fixed this class for IPv6. However there are still some general
     * flaws, partly coming from the IPRange interface. A redesign of (Abstract/File)IPRange and
     * it's helper class org.apache.lenya.net.InetAddressUtil would be a good idea. Some problems of
     * this implementation are:
     *  - The whole initialization seems flawed. Objects can be in an unitialized state and the
     * class seems not to be aware of this.
     *  - Network-address and -mask can be set independently. Therefore it cannot be enforced that
     * these have the same size (i.e. that both are IPv4 or both are IPv6). This shows up in
     * InetAddressUtil.contains(...), where in a case of mismatch there is no good way to inform the
     * user about the problem. This should be done once when the AbstractIPRange object is
     * initialized.
     *  - Unless this functionality would be needed by other parts of Lenya or external software
     * (which seems not to be the case ;-), InetAddressUtil should be removed (resp. deprecated)
     * altogether, because it's mostly an internal implementation detail of AbstractIPRange.
     * AbstractIPRange should implement the contains(...)-method internally to make use of the fact
     * that the network- addresses and -masks validity and compatibility has already been checked
     * when setting these. (Once the above problems have been fixed. ;-)
     *  - Especially for IPv6 it would be nice to have the possibility to specify the netmask as the
     * number of bits (as in "::1/128" or "127.0.0.1/24").
     *
     * FIXME II (from the previous version): why are we in the business of implementing IP ranges??
     */

    /**
     * Ctor.
     * Initializes the the IP range with the local host (127.0.0.1/24 for IPv4, ::1/128 for IPv6).
     * @param itemManager The item manager.
     * @param logger The logger.
     */
    public AbstractIPRange(ItemManager itemManager, Logger logger) {
        super(itemManager, logger);
        try {
            this.networkAddress = InetAddress.getLocalHost();
            byte[] mask = null;
            int masklen = this.networkAddress.getAddress().length;
            if (masklen == 4) {
                /* IPv4: */
                /*
                 * FIXME? by zisch@dals.ch: Should this be { -1, 0, 0, 0 }??
                 */
                mask = new byte[] { -1, -1, -1, 0 };
            } else {
                /* IPv6 (and others ;-): */
                mask = new byte[masklen];
                Arrays.fill(mask, (byte) -1);
            }
            this.subnetMask = InetAddress.getByAddress(mask);
        } catch (UnknownHostException ignore) {
            /*
             * FIXME? by zisch@dals.ch: Is it safe to ignore the exception and just leave the
             * IPRange uninitialized!?
             */
        }
    }

    /**
     * Ctor.
     * @param itemManager The item manager.
     * @param logger The logger.
     * @param id The IP range ID.
     */
    public AbstractIPRange(ItemManager itemManager, Logger logger, String id) {
        super(itemManager, logger);
        setId(id);
    }

    private File configurationDirectory;

    /**
     * Returns the configuration directory.
     * @return A file object.
     */
    public File getConfigurationDirectory() {
        return this.configurationDirectory;
    }

    protected void setConfigurationDirectory(File _configurationDirectory) {
        this.configurationDirectory = _configurationDirectory;
    }

    /**
     * Save the IP range
     * @throws AccessControlException if the save failed
     */
    public abstract void save() throws AccessControlException;

    /**
     * Delete an IP range
     * @throws AccessControlException if the delete failed
     */
    public void delete() throws AccessControlException {
        removeFromAllGroups();
    }

    private InetAddress networkAddress;

    /**
     * Sets the network address. This method accepts numeric IPv4 addresses like
     * <code>"129.168.0.32"</code>, numeric IPv6 addresses like
     * <code>"1080::8:800:200C:417A"</code> as well as hostnames (if DNS resolution is available)
     * like <code>"localhost"</code> or <code>"www.apache.com"</code>.
     * @param address a <code>String</code> like <code>"192.168.0.32"</code>,
     *            <code>"::1"</code>, ...
     * @throws AccessControlException when the conversion of the <code>String</code> to an
     *             <code>InetAddress</code> failed
     * @see #setNetworkAddress(byte[])
     */
    public void setNetworkAddress(String address) throws AccessControlException {
        try {
            this.networkAddress = InetAddress.getByName(address);
        } catch (UnknownHostException e) {
            throw new AccessControlException("Failed to convert address [" + address + "]: ", e);
        }
    }

    /**
     * Sets the network address. The method accepts numeric IPv4 addresses (specified by byte arrays
     * of length 4) or IPv6 addresses (specified by byte arrays of length 16).
     * @param address a byte array of the length 4 or 16
     * @throws AccessControlException when the conversion of the byte array to an InetAddress
     *             failed.
     * @see #setNetworkAddress(String)
     */
    public void setNetworkAddress(byte[] address) throws AccessControlException {
        try {
            this.networkAddress = InetAddress.getByAddress(address);
        } catch (UnknownHostException e) {
            throw new AccessControlException("Failed to convert address [" + addr2string(address)
                    + "]: ", e);
        }
    }

    /**
     * Returns the network address.
     * @return an <code>InetAddress</code> representing the network address
     */
    public InetAddress getNetworkAddress() {
        return this.networkAddress;
    }

    private InetAddress subnetMask;

    /**
     * Sets the subnet mask. See {@link #setNetworkAddress(String)} for the allowed formats of the
     * <code>mask</code> string. (However, the hostname format will usually not be of much use for
     * setting the mask.)
     * <p>
     * Only valid subnet masks are accepted, for which the binary representation is a sequence of
     * 1-bits followed by a sequence of 0-bits. For example <code>"255.128.0.0"</code> is valid
     * while <code>"255.128.0.1"</code> is not.
     * @param mask a <code>String</code> like <code>"255.255.255.0"</code>
     * @throws AccessControlException when the conversion of the String to an
     *             <code>InetAddress</code> failed.
     * @see #setSubnetMask(byte[])
     */
    public void setSubnetMask(String mask) throws AccessControlException {
        try {
            /* use setSubnetMask(...) to check the mask-format: */
            setSubnetMask(InetAddress.getByName(mask).getAddress());
        } catch (final UnknownHostException e) {
            throw new AccessControlException("Failed to convert mask [" + mask + "]: ", e);
        }

    }

    /**
     * Sets the subnet mask.
     * <p>
     * Only valid subnet masks are accepted, for which the binary representation is a sequence of
     * 1-bits followed by a sequence of 0-bits. For example <code>{ 255, 128, 0, 0 }</code> is
     * valid while <code>{ 255, 128, 0, 1 }</code> is not.
     * @param mask A byte array of the length 4.
     * @throws AccessControlException when the conversion of the byte array to an InetAddress
     *             failed.
     * @see #setSubnetMask(String)
     */
    public void setSubnetMask(byte[] mask) throws AccessControlException {
        /*
         * check for correct netmask (i.e. any number of 1-bits followed by 0-bits filling the right
         * part of the mask) ...
         *
         * FIXME: This "algorithm" is rather unelegant. There should be a better way to do it! ;-)
         */
        if (getLogger().isDebugEnabled()) {
            getLogger().debug("CHECK_NETMASK: check " + addr2string(mask));
        }
        int i = 0;
        CHECK_NETMASK: while (i < mask.length) {
            int b = mask[i++] & 0xff;
            /* the initial byte(s) must be 255: */
            if (b != 0xff) {
                /* first byte != 255, test all possibilities: */
                if (getLogger().isDebugEnabled()) {
                    getLogger().debug("CHECK_NETMASK: first byte != 255: idx: " + (i - 1)
                            + ", mask[idx]: 0x" + b);
                }
                /* check if 0: */
                if (b == 0) {
                    break CHECK_NETMASK;
                }
                for (int tst = 0xfe; tst != 0; tst = (tst << 1) & 0xff) {
                    getLogger().debug("CHECK_NETMASK: tst == 0x" + Integer.toHexString(tst));
                    if (b == tst) {
                        break CHECK_NETMASK;
                    }
                }
                /*
                 * Invalid byte found, i.e. one which is not element of { 11111111, 11111110,
                 * 11111100, 11111000, ..., 00000000 }
                 */
                throw new AccessControlException("Invalid byte in mask [" + addr2string(mask) + "]");
            }
        }
        /* the remaining byte(s) (if any) must be 0: */
        while (++i < mask.length) {
            if (mask[i] != 0) {
                /*
                 * Invalid byte found, i.e. some non-zero byte right of the first non-zero byte.
                 */
                throw new AccessControlException("Invalid non-zero byte in mask ["
                        + addr2string(mask) + "]");
            }
        }

        /* convert the checked mask to InetAddress: */
        try {
            this.subnetMask = InetAddress.getByAddress(mask);
        } catch (final UnknownHostException e) {
            throw new AccessControlException(
                    "Failed to convert mask [" + addr2string(mask) + "]: ", e);
        }
    }

    /**
     * Returns the subnet mask.
     * @return An InetAddress value.
     */
    public InetAddress getSubnetMask() {
        return this.subnetMask;
    }

    /**
     * Checks if a network address / subnet mask combination describes a valid subnet.
     * @param networkAddress The network address.
     * @param subnetMask The subnet mask.
     * @return A boolean value.
     * @deprecated This method is currently not implemented, probably not necessary.and could be
     *             removed in the future. Therefore it should not be used.
     */
    public static boolean isValidSubnet(InetAddress networkAddress, InetAddress subnetMask) {
        /*
         * FIXME? by zisch@dals.ch: Is this method really necessary (what for?) and (if so)
         * shouldn't it be an internal (private) utility-method??
         */
        // TODO implement class
        return false;
    }

    /**
     * Checks if this IP range contains a certain machine.
     * <p>
     * Note: if the network address and the subnet mask of this IP range have different sizes (i.e.
     * one is IPv4 and one is IPv6), this method will always return <code>false</code>, no matter
     * what machine has been specified!
     * <p>
     * Further, if the machine address and the IP range (i.e. network address and subnet mask) have
     * different sizes, the method will return <code>false</code>. (In other words: an IPv4 range
     * never contains an IPv6 address and the other way round.)
     * <p>
     * Note that the above can lead to confusion. For example the local subnet in IPv4 (
     * <code>127.0.0.0/8</code>) will <b>not </b> contain the localhost in IPv6 (
     * <code>::1</code>), and the localhost in IPv4 (<code>127.0.0.1</code>) will <b>not </b>
     * be contained in the local subnet in IPv6 (<code>::1/128</code>).
     * @param machine the machine to check for
     * @return a boolean value
     * @see InetAddressUtil#contains
     */
    public boolean contains(Machine machine) {
        /*
         * FIXME? by zisch@dals.ch: Maybe some mapping between IPv4/v6 should be done here, p.e. for
         * the localhost (see the javdoc comment above)? (I'm not a TCP/IP-guru, so I'm not sure
         * about this. ;-)
         */
        getLogger().debug("Checking IP range: [" + getId() + "]");
        InetAddressUtil util = new InetAddressUtil(getLogger());
        return util.contains(this.networkAddress, this.subnetMask, machine.getAddress());
    }

    /**
     * Format the specified numeric IP address.
     * @param addr the raw numeric IP address
     * @return the formatted address
     */
    private static String addr2string(byte[] addr) {
        StringBuffer buf = new StringBuffer();
        if (addr.length > 4) {
            /* IPv6-format if more than 4 bytes: */
            for (int i = 0; i < addr.length; i++) {
                if (i > 0 && (i & 1) == 0) {
                    buf.append(':');
                }
                String hex = Integer.toHexString(addr[i] & 0xff);
                if (hex.length() == 1) {
                    buf.append('0');
                }
                buf.append(hex);
            }
        } else {
            /* IPv4-format: */
            for (int i = 0; i < addr.length; i++) {
                if (i > 0) {
                    buf.append('.');
                }
                buf.append(addr[i] & 0xff);
            }
        }
        return buf.toString();
    }
}
TOP

Related Classes of org.apache.lenya.ac.impl.AbstractIPRange

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.