com.offbynull.portmapper.pcp.FilterPcpOption.java Source code

Java tutorial

Introduction

Here is the source code for com.offbynull.portmapper.pcp.FilterPcpOption.java

Source

/*
 * Copyright (c) 2013-2014, Kasra Faghihi, All rights reserved.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3.0 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.
 */
package com.offbynull.portmapper.pcp;

import com.offbynull.portmapper.common.NetworkUtils;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.BufferUnderflowException; // NOPMD Javadoc not recognized (fixed in latest PMD but maven plugin has to catch up)
import java.nio.ByteBuffer;
import org.apache.commons.lang3.Validate;

/**
 * Represents a FILTER PCP option. From the RFC:
 * <pre>
The FILTER option is formatted as follows:
     
   0                   1                   2                   3
   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  | Option Code=3 |  Reserved     |   Option Length=20            |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |    Reserved   | Prefix Length |      Remote Peer Port         |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                                                               |
  |               Remote Peer IP remotePeerIpAddress (128 bits)               |
  |                                                               |
  |                                                               |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     
                   Figure 15: FILTER Option Layout
     
These fields are described below:
     
Reserved:  8 reserved bits, MUST be sent as 0 and MUST be ignored
   when received.
     
Prefix Length:  indicates how many bits of the IPv4 or IPv6 remotePeerIpAddress
   are relevant for this filter.  The value 0 indicates "no filter",
   and will remove all previous filters.  See below for detail.
     
Remote Peer Port:  the port number of the remote peer.  The value 0
   indicates "all ports".
     
Remote Peer IP remotePeerIpAddress:  The IP remotePeerIpAddress of the remote peer.
     
   Option Name: FILTER
   Number: 3
   Purpose: specifies a filter for incoming packets
   Valid for Opcodes: MAP
   Length: 20 octets
   May appear in: request.  May appear in response only if it
   appeared in the associated request.
   Maximum occurrences: as many as fit within maximum PCP message
   size
 </pre>
 * @author Kasra Faghihi
 */
public final class FilterPcpOption extends PcpOption {
    private int prefixLength;
    private int remotePeerPort;
    private InetAddress remotePeerIpAddress;

    /**
     * Constructs a {@link FilterPcpOption} by parsing a buffer.
     * @param buffer buffer containing PCP option data
     * @throws NullPointerException if any argument is {@code null}
     * @throws BufferUnderflowException if not enough data is available in {@code buffer}
     * @throws IllegalArgumentException if option code is not {@code 3}, or if the field {@code prefixLength > 128}
     */
    public FilterPcpOption(ByteBuffer buffer) {
        super(buffer);

        Validate.isTrue(super.getCode() == 3);

        buffer.get(); // reserved
        prefixLength = buffer.get() & 0xFF;
        remotePeerPort = buffer.getShort() & 0xFFFF;

        Validate.inclusiveBetween(0, 128, prefixLength); // 0 indicates 'no filter'
        Validate.inclusiveBetween(0, 65535, remotePeerPort); // 0 indicates 'all ports', should never trigger

        byte[] addrArr = new byte[16];
        buffer.get(addrArr);
        try {
            remotePeerIpAddress = InetAddress.getByAddress(addrArr);
        } catch (UnknownHostException uhe) {
            throw new IllegalStateException(uhe); // should never happen
        }
    }

    /**
     * Constructs a {@link FilterPcpOption}.
     * @param prefixLength prefix length ({@code 0} = no filter}
     * @param remotePeerPort remote peer port ({@code 0} = all ports)
     * @param remotePeerIpAddress remote peer IP remotePeerIpAddress
     * @throws NullPointerException if any argument is {@code null}
     * @throws IllegalArgumentException if {@code prefixLength < 0 || > 128}, or if {@code remotePeerPort < 0 || > 65535} 
     */
    public FilterPcpOption(int prefixLength, int remotePeerPort, InetAddress remotePeerIpAddress) {
        super(3, toDataSection(prefixLength, remotePeerPort, remotePeerIpAddress));

        this.prefixLength = prefixLength;
        this.remotePeerPort = remotePeerPort;
        this.remotePeerIpAddress = remotePeerIpAddress;
    }

    private static ByteBuffer toDataSection(int prefixLength, int remotePeerPort, InetAddress remotePeerIpAddress) {
        Validate.inclusiveBetween(0, 128, prefixLength); // 0 indicates 'no filter'
        Validate.inclusiveBetween(0, 65535, remotePeerPort); // 0 indicates 'all ports'
        Validate.notNull(remotePeerIpAddress);

        ByteBuffer buffer = ByteBuffer.allocate(20);
        buffer.put((byte) 0); // reserved
        buffer.put((byte) prefixLength);
        buffer.putShort((short) remotePeerPort);
        buffer.put(NetworkUtils.convertToIpv6Array(remotePeerIpAddress));

        return buffer;
    }

    /**
     * Get the prefix length.
     * @return prefix length
     */
    public int getPrefixLength() {
        return prefixLength;
    }

    /**
     * Get the remote peer port.
     * @return remote peer port
     */
    public int getRemotePeerPort() {
        return remotePeerPort;
    }

    /**
     * Get the remote IP address.
     * @return remote IP address
     */
    public InetAddress getRemotePeerIpAddress() {
        return remotePeerIpAddress;
    }

}