com.offbynull.portmapper.natpmp.TcpMappingNatPmpRequest.java Source code

Java tutorial

Introduction

Here is the source code for com.offbynull.portmapper.natpmp.TcpMappingNatPmpRequest.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.natpmp;

import java.nio.ByteBuffer;
import org.apache.commons.lang3.Validate;

/**
 * Represents a NAT-PMP TCP mapping request. From the RFC:
 * <pre>
 * 3.3.  Requesting a Mapping
 * 
 *    To create a mapping, the client sends a UDP packet to port 5351 of
 *    the gateway's internal IP address with the following format:
 * 
 *     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
 *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *    | Vers = 0      | OP = x        | Reserved                      |
 *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *    | Internal Port                 | Suggested External Port       |
 *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *    | Requested Port Mapping Lifetime in Seconds                    |
 *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * 
 *    Opcodes supported:
 *    1 - Map UDP
 *    2 - Map TCP
 * 
 *    The Reserved field MUST be set to zero on transmission and MUST be
 *    ignored on reception.
 * 
 *    The Ports and Lifetime are transmitted in the traditional network
 *    byte order (i.e., most significant byte first).
 * 
 *    The Internal Port is set to the local port on which the client is
 *    listening.
 * 
 *    If the client would prefer to have a high-numbered "anonymous"
 *    external port assigned, then it should set the Suggested External
 *    Port to zero, which indicates to the gateway that it should allocate
 *    a high-numbered port of its choosing.  If the client would prefer
 *    instead to have the mapped external port be the same as its local
 *    internal port if possible (e.g., a web server listening on port 80
 *    that would ideally like to have external port 80), then it should set
 *    the Suggested External Port to the desired value.  However, the
 *    gateway is not obliged to assign the port suggested, and may choose
 *    not to, either for policy reasons (e.g., port 80 is reserved and
 *    clients may not request it) or because that port has already been
 *    assigned to some other client.  Because of this, some product
 *    developers have questioned the value of having the Suggested External
 *    Port field at all.  The reason is for failure recovery.  Most low-
 *    cost home NAT gateways do not record temporary port mappings in
 *    persistent storage, so if the gateway crashes or is rebooted, all the
 *    mappings are lost.  A renewal packet is formatted identically to an
 *    initial mapping request packet, except that for renewals the client
 *    sets the Suggested External Port field to the port the gateway
 *    actually assigned, rather than the port the client originally wanted.
 * 
 *    When a freshly rebooted NAT gateway receives a renewal packet from a
 *    client, it appears to the gateway just like an ordinary initial
 *    request for a port mapping, except that in this case the Suggested
 *    External Port is likely to be one that the NAT gateway *is* willing
 *    to allocate (it allocated it to this client right before the reboot,
 *    so it should presumably be willing to allocate it again).  This
 *    improves the stability of external ports across NAT gateway restarts.
 * 
 *    The RECOMMENDED Port Mapping Lifetime is 7200 seconds (two hours).
 * 
 * ...
 * 
 * 3.4.  Destroying a Mapping
 * 
 *    A mapping may be destroyed in a variety of ways.  If a client fails
 *    to renew a mapping, then at the time its lifetime expires, the
 *    mapping MUST be automatically deleted.  In the common case where the
 *    gateway device is a combined DHCP server and NAT gateway, when a
 *    client's DHCP address lease expires, the gateway device MAY
 *    automatically delete any mappings belonging to that client.
 *    Otherwise, a new client being assigned the same IP address could
 *    receive unexpected inbound UDP packets or inbound TCP connection
 *    requests that it did not ask for and does not want.
 * 
 *    A client MAY also send an explicit packet to request deletion of a
 *    mapping that is no longer needed.  A client requests explicit
 *    deletion of a mapping by sending a message to the NAT gateway
 *    requesting the mapping, with the Requested Lifetime in Seconds set to
 *    zero.  The Suggested External Port MUST be set to zero by the client
 *    on sending, and MUST be ignored by the gateway on reception.
 * 
 * ...
 * 
 *    A client can request the explicit deletion of all its UDP or TCP
 *    mappings by sending the same deletion request to the NAT gateway with
 *    the external port, internal port, and lifetime set to zero.  A client
 *    MAY choose to do this when it first acquires a new IP address in
 *    order to protect itself from port mappings that were performed by a
 *    previous owner of the IP address.  After receiving such a deletion
 *    request, the gateway MUST delete all its UDP or TCP port mappings
 *    (depending on the opcode).  The gateway responds to such a deletion
 *    request with a response as described above, with the internal port
 *    set to zero.  If the gateway is unable to delete a port mapping, for
 *    example, because the mapping was manually configured by the
 *    administrator, the gateway MUST still delete as many port mappings as
 *    possible, but respond with a non-zero result code.  The exact result
 *    code to return depends on the cause of the failure.  If the gateway
 *    is able to successfully delete all port mappings as requested, it
 *    MUST respond with a result code of zero.
 * </pre>
 * @author Kasra Faghihi
 */
public final class TcpMappingNatPmpRequest extends NatPmpRequest {
    private int internalPort;
    private int suggestedExternalPort;
    private long lifetime;

    /**
     * Construct a {@link TcpMappingNatPmpRequest} object.
     * @param internalPort internal port
     * @param suggestedExternalPort suggested external port ({@code 0} for no preference)
     * @param lifetime desired lifetime of mapping ({@code 0} to destroy mapping)
     * @throws IllegalArgumentException if {@code internalPort < 1 || > 65535}, or if {@code suggestedExternalPort < 0 || > 65535}, or if
     * {@code lifetime < 0 || > 0xFFFFFFFFL}
     */
    public TcpMappingNatPmpRequest(int internalPort, int suggestedExternalPort, long lifetime) {
        super(2);

        Validate.inclusiveBetween(1, 65535, internalPort);
        Validate.inclusiveBetween(0, 65535, suggestedExternalPort);
        Validate.inclusiveBetween(0L, 0xFFFFFFFFL, lifetime);

        this.internalPort = internalPort;
        this.suggestedExternalPort = suggestedExternalPort;
        this.lifetime = lifetime;
    }

    @Override
    protected void dumpOpCodeSpecificInformation(ByteBuffer dst) {
        dst.putShort((short) 0);
        dst.putShort((short) internalPort);
        dst.putShort((short) suggestedExternalPort);
        dst.putInt((int) lifetime);
    }

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

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

    /**
     * Get lifetime.
     * @return lifetime
     */
    public long getLifetime() {
        return lifetime;
    }
}