All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.maxmind.db.Networks Maven / Gradle / Ivy

package com.maxmind.db;

import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Stack;

/**
 * Instances of this class provide an iterator over the networks in a database.
 * The iterator will return a {@link DatabaseRecord} for each network.
 * 
 * @param  The type of data returned by the iterator.
 */
public final class Networks implements Iterator> {
    private final Reader reader;
    private final Stack nodes;
    private NetworkNode lastNode;
    private final boolean includeAliasedNetworks;
    private final ByteBuffer buffer; /* Stores the buffer for Next() calls */
    private final Class typeParameterClass;
    
    /**
     * Constructs a Networks instance.
     * @param reader The reader object.
     * @param includeAliasedNetworks The boolean to include aliased networks.
     * @param typeParameterClass The type of data returned by the iterator.
     * @throws ClosedDatabaseException Exception for a closed database.
     */
    Networks(Reader reader, boolean includeAliasedNetworks, Class typeParameterClass) 
        throws ClosedDatabaseException {
        this(reader, includeAliasedNetworks, new NetworkNode[]{}, typeParameterClass);
    }

    /**
     * Constructs a Networks instance.
     * @param reader The reader object.
     * @param includeAliasedNetworks The boolean to include aliased networks.
     * @param nodes The initial nodes array to start Networks iterator with.
     * @param typeParameterClass The type of data returned by the iterator.
     * @throws ClosedDatabaseException Exception for a closed database.
     */
    Networks(
            Reader reader,
            boolean includeAliasedNetworks,
            NetworkNode[] nodes,
            Class typeParameterClass)
        throws ClosedDatabaseException {
        this.reader = reader;
        this.includeAliasedNetworks = includeAliasedNetworks;
        this.buffer = reader.getBufferHolder().get();
        this.nodes = new Stack<>();
        this.typeParameterClass = typeParameterClass;
        for (NetworkNode node : nodes) {
            this.nodes.push(node);
        }
    }

    /**
     * Constructs a Networks instance with includeAliasedNetworks set to false by default.
     * @param reader The reader object.
     * @param typeParameterClass The type of data returned by the iterator.
     */
    Networks(Reader reader, Class typeParameterClass) throws ClosedDatabaseException {
        this(reader, false, typeParameterClass);
    }

    /**
     * Returns the next DataRecord.
     * @return The next DataRecord.
     * @throws NetworksIterationException An exception when iterating over the networks.
     */
    @Override
    public DatabaseRecord next() {
        try {
            T data = this.reader.resolveDataPointer(
                this.buffer, this.lastNode.pointer, this.typeParameterClass);

            byte[] ip = this.lastNode.ip;
            int prefixLength = this.lastNode.prefix;

            // We do this because uses of includeAliasedNetworks will get IPv4 networks
            // from the ::FFFF:0:0/96. We want to return the IPv4 form of the address
            // in that case.
            if (!this.includeAliasedNetworks && isInIpv4Subtree(ip)) {
                ip = Arrays.copyOfRange(ip, 12, ip.length);
                prefixLength -= 96;
            }

            // If the ip is in ipv6 form, drop the prefix manually
            // as InetAddress converts it to ipv4.
            InetAddress ipAddr = InetAddress.getByAddress(ip);
            if (ipAddr instanceof Inet4Address && ip.length > 4 && prefixLength > 96) {
                prefixLength -= 96;
            }

            return new DatabaseRecord<>(data, ipAddr, prefixLength);
        } catch (IOException e) {
            throw new NetworksIterationException(e);
        }
    }

    private boolean isInIpv4Subtree(byte[] ip) {
        if (ip.length != 16) {
            return false;
        }
        for (int i = 0; i < 12; i++) {
            if (ip[i] != 0) {
                return false;
            }
        }
        return true;
    }
    
    /**
    * hasNext prepares the next network for reading with the Network method. It
    * returns true if there is another network to be processed and false if there
    * are no more networks.
    * @return boolean True if there is another network to be processed.
    * @throws NetworksIterationException Exception while iterating over the networks.
    */
    @Override
    public boolean hasNext()  {
        while (!this.nodes.isEmpty()) {
            NetworkNode node = this.nodes.pop();

            // Next until we don't have data.
            while (node.pointer != this.reader.getMetadata().getNodeCount()) {
                // This skips IPv4 aliases without hardcoding the networks that the writer
                // currently aliases.
                if (!this.includeAliasedNetworks && this.reader.getIpv4Start() != 0
                        && node.pointer == this.reader.getIpv4Start()
                        && !isInIpv4Subtree(node.ip)) {
                    break;
                }

                if (node.pointer > this.reader.getMetadata().getNodeCount()) {
                    this.lastNode = node;
                    return true;
                }

                byte[] ipRight = Arrays.copyOf(node.ip, node.ip.length);
                if (ipRight.length <= (node.prefix >> 3)) {
                    throw new NetworksIterationException("Invalid search tree");
                }

                ipRight[node.prefix >> 3] |= 1 << (7 - (node.prefix % 8));

                try {
                    int rightPointer = this.reader.readNode(this.buffer, node.pointer, 1);
                    node.prefix++;

                    this.nodes.push(new NetworkNode(ipRight, node.prefix, rightPointer));
                    node.pointer = this.reader.readNode(this.buffer, node.pointer, 0);
                } catch (InvalidDatabaseException e) {
                    throw new NetworksIterationException(e);
                }
            }
        }
        return false;
    }

    static class NetworkNode {
        public byte[] ip;
        public int prefix;
        public int pointer;

        /**
         * Constructs a network node for internal use.
         * @param ip The ip address of the node.
         * @param prefix The prefix of the node.
         * @param pointer The node number
         */
        NetworkNode(byte[] ip, int prefix, int pointer) {
            this.ip = ip;
            this.prefix = prefix;
            this.pointer = pointer;
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy