Package net.tomp2p.nat

Source Code of net.tomp2p.nat.NATUtils

/*
* Copyright 2011 Thomas Bocek
*
* Licensed 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.
*/

package net.tomp2p.nat;

import java.io.IOException;
import java.net.InetAddress;

import javax.xml.parsers.ParserConfigurationException;

import net.tomp2p.natpmp.Gateway;
import net.tomp2p.natpmp.MapRequestMessage;
import net.tomp2p.natpmp.NatPmpDevice;
import net.tomp2p.natpmp.NatPmpException;
import net.tomp2p.natpmp.ResultCode;

import org.bitlet.weupnp.GatewayDevice;
import org.bitlet.weupnp.GatewayDiscover;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;

/**
* This class is used to do automatic port forwarding. It maps with PMP und UPNP
* and also unmaps them. It creates a shutdown hook in case the user exits the
* application without a proper shutdown.
*
* @author Thomas Bocek
*/
public class NATUtils {
  private static final Logger LOGGER = LoggerFactory.getLogger(NATUtils.class);

  // UPNP
  private GatewayDevice gatewayDevice;
  private int externalPortTCP = -1;
  private int externalPortUDP = -1;
  // NAT-PMP
  private NatPmpDevice pmpDevice;

  private final Object shutdownLock = new Object();

  /**
   * Constructor.
   */
  public NATUtils() {
    shutdownHookEnabled();
  }

  /**
   * Maps with the PMP protocol
   * (http://en.wikipedia.org/wiki/NAT_Port_Mapping_Protocol). One of the
   * drawbacks of this protocol is that it needs to know the IP of the router.
   * To get the IP of the router in Java, we need to use netstat and parse the
   * output.
   *
   * @param internalPortUDP
   *            The UDP internal port
   * @param internalPortTCP
   *            The TCP internal port
   * @param externalPortUDP
   *            The UDP external port
   * @param externalPortTCP
   *            The TCP external port
   * @return True, if the mapping was successful (i.e. the router supports
   *         PMP)
   * @throws NatPmpException
   *             the router does not supports PMP
   */
  public boolean mapPMP(final int internalPortUDP, final int internalPortTCP, final int externalPortUDP,
          final int externalPortTCP) throws NatPmpException {
    InetAddress gateway = Gateway.getIP();
    pmpDevice = new NatPmpDevice(gateway);
    MapRequestMessage mapTCP = new MapRequestMessage(true, internalPortTCP, externalPortTCP, Integer.MAX_VALUE,
            null);
    MapRequestMessage mapUDP = new MapRequestMessage(false, internalPortUDP, externalPortUDP, Integer.MAX_VALUE,
            null);
    pmpDevice.enqueueMessage(mapTCP);
    pmpDevice.enqueueMessage(mapUDP);
    pmpDevice.waitUntilQueueEmpty();
    // UDP is nice to have, but we need TCP
    return mapTCP.getResultCode() == ResultCode.Success;
  }

  /**
   * Maps with UPNP protocol
   * (http://en.wikipedia.org/wiki/Internet_Gateway_Device_Protocol). Since
   * this uses broadcasting to discover routers, no calling the external
   * program netstat is necessary.
   *
   * @param internalHost
   *            The internal host to map the ports to
   * @param internalPortUDP
   *            The UDP internal port
   * @param internalPortTCP
   *            The TCP internal port
   * @param externalPortUDP
   *            The UDP external port
   * @param externalPortTCP
   *            The TCP external port
   * @return True, if at least one mapping was successful (i.e. the router
   *         supports UPNP)
   * @throws IOException
   *             Exception
   * @throws SAXException
   * @throws ParserConfigurationException
   */
  public boolean mapUPNP(final String internalHost, final int internalPortUDP, final int internalPortTCP,
          final int externalPortUDP, final int externalPortTCP) throws IOException, SAXException,
          ParserConfigurationException {
    // -1 sets the default timeout to 1500 ms

    if (gatewayDevice != null) {
      gatewayDevice.deletePortMapping(this.externalPortTCP, "TCP");
      gatewayDevice.deletePortMapping(this.externalPortUDP, "UDP");
    }
    GatewayDiscover discover = new GatewayDiscover();
    discover.discover();
    gatewayDevice = discover.getValidGateway();

    if (gatewayDevice == null) {
      LOGGER.info("no UPNP device found");
      return false;
    }

    this.externalPortTCP = externalPortTCP;
    this.externalPortUDP = externalPortUDP;

    boolean mapTCP = gatewayDevice.addPortMapping(externalPortTCP, internalPortTCP, internalHost, "TCP",
            "TomP2P mapping TCP");
    boolean mapUDP = gatewayDevice.addPortMapping(externalPortUDP, internalPortUDP, internalHost, "UDP",
            "TomP2P mapping UDP");

    if (mapTCP && mapUDP) {
      return true;
    } else {
      if (!mapTCP) {
        LOGGER.warn("UPNP TCP mapping did failed");
      }
      if (!mapUDP) {
        LOGGER.warn("UPNP UDP mapping did failed");
      }
      return false;
    }
  }

  /**
   * Unmap the device that has been mapped previously. Used during shutdown.
   *
   * @throws SAXException
   * @throws IOException
   */
  private void unmapUPNP() throws SAXException, IOException {
    if (gatewayDevice != null) {
      try {
        boolean unmapTCP = gatewayDevice.deletePortMapping(this.externalPortTCP, "TCP");
        boolean unmapUDP = gatewayDevice.deletePortMapping(this.externalPortUDP, "UDP");
        if (!unmapTCP) {
          LOGGER.warn("UPNP TCP unmapping did failed");
        }
        if (!unmapUDP) {
          LOGGER.warn("UPNP UDP unmapping did failed");
        }
      } finally {
        gatewayDevice = null;
      }
    }
  }

  /**
   * Registers a shutdownhook to clean the NAT mapping. If this is not called,
   * then the mapping may stay until the router is rebooted.
   */
  private void shutdownHookEnabled() {
    // The shutdown hook simply runs the shutdown method.
    Thread t = new Thread(new Runnable() {
      public void run() {
        shutdown();
      }
    }, "TomP2P:NATUtils:ShutdownHook");
    Runtime.getRuntime().addShutdownHook(t);
  }

  /**
   * Since shutdown is also called from the shutdown hook, it might get called
   * twice. Thus, this method deregister NAT mappings only once. If it already
   * has been called, this method does nothing.
   */
  public void shutdown() {
    synchronized (shutdownLock) {
      try {
        unmapUPNP();
      } catch (Exception e) {
        LOGGER.error("UPNP UDP unmapping did failed", e);
      }
      if (pmpDevice != null) {
        pmpDevice.shutdown();
        pmpDevice = null;
      }
    }
  }
}
TOP

Related Classes of net.tomp2p.nat.NATUtils

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.