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

Java tutorial

Introduction

Here is the source code for com.offbynull.portmapper.pcp.MapPcpRequest.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.ByteBufferUtils;
import com.offbynull.portmapper.common.NetworkUtils;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import org.apache.commons.lang3.Validate;

/**
 * Represents a MAP PCP request. From the RFC:
 * <pre>
 *    The following diagram shows the format of the Opcode-specific
 *    information in a request for the MAP Opcode.
 * 
 *       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
 *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *      |                                                               |
 *      |                 Mapping Nonce (96 bits)                       |
 *      |                                                               |
 *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *      |   Protocol    |          Reserved (24 bits)                   |
 *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *      |        Internal Port          |    Suggested External Port    |
 *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *      |                                                               |
 *      |           Suggested External IP Address (128 bits)            |
 *      |                                                               |
 *      |                                                               |
 *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * 
 *                        Figure 9: MAP Opcode Request
 * 
 *    These fields are described below:
 * 
 *    Requested lifetime (in common header):  Requested lifetime of this
 *       mapping, in seconds.  The value 0 indicates "delete".
 * 
 *    Mapping Nonce:  Random value chosen by the PCP client.  See
 *       Section 11.2, "Generating a MAP Request".  Zero is a legal value
 *       (but unlikely, occurring in roughly one in 2^96 requests).
 * 
 *    Protocol:  Upper-layer protocol associated with this Opcode.  Values
 *       are taken from the IANA protocol registry [proto_numbers].  For
 *       example, this field contains 6 (TCP) if the Opcode is intended to
 *       create a TCP mapping.  This field contains 17 (UDP) if the Opcode
 *       is intended to create a UDP mapping.  The value 0 has a special
 *       meaning for 'all protocols'.
 * 
 *    Reserved:  24 reserved bits, MUST be sent as 0 and MUST be ignored
 *       when received.
 * 
 *    Internal Port:  Internal port for the mapping.  The value 0 indicates
 *       'all ports', and is legal when the lifetime is zero (a delete
 *       request), if the protocol does not use 16-bit port numbers, or the
 *       client is requesting 'all ports'.  If the protocol is zero
 *       (meaning 'all protocols'), then internal port MUST be zero on
 *       transmission and MUST be ignored on reception.
 * 
 *    Suggested External Port:  Suggested external port for the mapping.
 *       This is useful for refreshing a mapping, especially after the PCP
 *       server loses state.  If the PCP client does not know the external
 *       port, or does not have a preference, it MUST use 0.
 * 
 *    Suggested External IP Address:  Suggested external IPv4 or IPv6
 *       address.  This is useful for refreshing a mapping, especially
 *       after the PCP server loses state.  If the PCP client does not know
 *       the external address, or does not have a preference, it MUST use
 *       the address-family-specific all-zeros address (see Section 5).
 * 
 *    The internal address for the request is the source IP address of the
 *    PCP request message itself, unless the THIRD_PARTY option is used.
 * </pre>
 * @author Kasra Faghihi
 */
public final class MapPcpRequest extends PcpRequest {

    private ByteBuffer mappingNonce;
    private int protocol;
    private int internalPort;
    private int suggestedExternalPort;
    private InetAddress suggestedExternalIpAddress;

    /**
     * Constructs a {@link MapPcpRequest} object.
     * @param mappingNonce random value used to map requests to responses
     * @param protocol IANA protocol number ({@code 0} is valid, see Javadoc header)
     * @param internalPort internal port ({@code 0} is valid, see Javadoc header)
     * @param suggestedExternalPort suggested external port ({@code 0} for no preference)
     * @param suggestedExternalIpAddress suggested external IP address ({@code ::} for no preference)
     * @param lifetime requested lifetime in seconds
     * @param options PCP options to use
     * @throws NullPointerException if any argument is {@code null} or contains {@code null}
     * @throws IllegalArgumentException if any numeric argument is negative, or if {@code protocol > 255}, or if
     * {@code internalPort > 65535}, or if {@code suggestedExternalPort > 65535}, or if {@code mappingNonce} does not have {@code 12} bytes
     * remaining, or if {@code protocol == 0} but {@code internalPort != 0}, or if {@code internalPort == 0} but {@code lifetime != 0}
     */
    public MapPcpRequest(ByteBuffer mappingNonce, int protocol, int internalPort, int suggestedExternalPort,
            InetAddress suggestedExternalIpAddress, long lifetime, PcpOption... options) {
        super(1, lifetime, options);

        Validate.notNull(mappingNonce);
        Validate.isTrue(mappingNonce.remaining() == 12);
        Validate.inclusiveBetween(0, 255, protocol);
        Validate.inclusiveBetween(0, 65535, internalPort);
        Validate.inclusiveBetween(0, 65535, suggestedExternalPort);
        Validate.notNull(suggestedExternalIpAddress);

        if (protocol == 0) {
            Validate.isTrue(internalPort == 0);
        }

        if (internalPort == 0) {
            Validate.isTrue(super.getLifetime() == 0L);
        }

        this.mappingNonce = ByteBufferUtils.copyContents(mappingNonce).asReadOnlyBuffer();
        this.protocol = protocol;
        this.internalPort = internalPort;
        this.suggestedExternalPort = suggestedExternalPort;
        this.suggestedExternalIpAddress = suggestedExternalIpAddress; // for any ipv4 must be ::ffff:0:0, for any ipv6 must be ::
    }

    @Override
    protected void dumpOpCodeSpecificInformation(ByteBuffer dst) {
        dst.put(mappingNonce.asReadOnlyBuffer());
        dst.put((byte) protocol);

        for (int i = 0; i < 3; i++) { // reserved block
            dst.put((byte) 0);
        }

        dst.putShort((short) internalPort);
        dst.putShort((short) suggestedExternalPort);
        dst.put(NetworkUtils.convertToIpv6Array(suggestedExternalIpAddress));
    }

    /**
     * Get nonce.
     * @return nonce (read-only buffer)
     */
    public ByteBuffer getMappingNonce() {
        return mappingNonce.asReadOnlyBuffer();
    }

    /**
     * Get IANA protocol number.
     * @return IANA protocol number
     */
    public int getProtocol() {
        return protocol;
    }

    /**
     * Get internal port number.
     * @return internal port number
     */
    public int getInternalPort() {
        return internalPort;
    }

    /**
     * Get suggested external port number.
     * @return suggested external port number
     */
    public int getSuggestedExternalPort() {
        return suggestedExternalPort;
    }

    /**
     * Get suggested external IP address.
     * @return suggested external IP address
     */
    public InetAddress getSuggestedExternalIpAddress() {
        return suggestedExternalIpAddress;
    }
}