Java library that maps ports on NAT-enabled routers (supported protocols: UPnP-IGD/NAT-PMP/PCP).
Java
Clone or download

README.md

Port Mapper

Portmapper logo

The Port Mapper project is a Java library that allows you to forward ports on NAT-enabled routers. Originally developed as part of the Peernetic project, Port Mapper has several distinct advantages over existing Java libraries that provide port forwarding functionality.

  • Tested on all major platforms: Android, Windows, Linux, and Mac
  • Supports UPnP-IGD (Universal Plug-and-Play Internet Gateway Device) -- both IGD v1.0 and IGD v2.0 services
  • Supports NAT-PMP (Network Address Traversal Port Mapping Protocol)
  • Supports PCP (Port Control Protocol)
  • Supports both IPv4 and IPv6
  • Fault-tolerant -- works around malformed responses and faulty devices
  • Light-weight -- very few third-party dependencies and easy to port to other languages

Table of Contents

Quick-start Guide

Port Mapper requires Java7 or later. In your Maven POM, add "portmapper" as a dependency.

<dependency>
    <groupId>com.offbynull.portmapper</groupId>
    <artifactId>portmapper</artifactId>
    <version>2.0.5</version>
</dependency>

The following example attempts to forward some external port (55555 preferred) to internal port 12345 on the first port forwarding device it finds.

// Start gateways
Gateway network = NetworkGateway.create();
Gateway process = ProcessGateway.create();
Bus networkBus = network.getBus();
Bus processBus = process.getBus();

// Discover port forwarding devices and take the first one found
List<PortMapper> mappers = PortMapperFactory.discover(networkBus, processBus);
PortMapper mapper = mappers.get(0);

// Map internal port 12345 to some external port (55555 preferred)
//
// IMPORTANT NOTE: Many devices prevent you from mapping ports that are <= 1024
// (both internal and external ports). Be mindful of this when choosing which
// ports you want to map.
MappedPort mappedPort = mapper.mapPort(PortType.TCP, 12345, 55555, 60);
System.out.println("Port mapping added: " + mappedPort);

// Refresh mapping half-way through the lifetime of the mapping (for example,
// if the mapping is available for 40 seconds, refresh it every 20 seconds)
while(!shutdown) {
    mappedPort = mapper.refreshPort(mappedPort, mappedPort.getLifetime() / 2L);
    System.out.println("Port mapping refreshed: " + mappedPort);
    Thread.sleep(mappedPort.getLifetime() * 1000L);
}

// Unmap port 12345
mapper.unmapPort(mappedPort);

// Stop gateways
networkBus.send(new KillNetworkRequest());
processBus.send(new KillProcessRequest()); // can kill this after discovery

FAQ

What if I want to discover only one type of port forwarding device?

You can use the identify method on PortMapper implementations directly.

List<UpnpIgdPortMapper> upnpIgdMappers = UpnpIgdPortMapper.identify(networkBus);
        
List<NatPmpPortMapper> natPmpMappers = NatPmpPortMapper.identify(networkBus, processBus, additionalIps);
        
List<PcpPortMapper> pcpMappers = PcpPortMapper.identify(networkBus, processBus, additionalIps);

How is this library considered light-weight?

Several reasons. The Port Mapper project

  1. has very few dependencies on third-party libraries.
  2. doesn't require any special parsing libraries (e.g. XML/SOAP/HTTP/etc..) -- all parsing is done as US-ASCII text.
  3. doesn't require any special networking libraries (e.g. Netty/MINA/etc..) -- all networking is done through standard NIO.
  4. doesn't make use of regular expressions.

Because of this, the code should be easily portable to other languages -- especially languages that don't have the same robust ecosystem that Java does.

How is this library considered fault-tolerant?

The Port Mapper project aims to be resilient when it comes to faulty responses, especially when using UPnP-IGD. The code

  1. parses XML as text, based on patterns/heuristics (works around issues such as invalid XML syntax/invalid XML structure/incorrect capitalization/etc..).
  2. attempts requests multiple times when the device responds with a failure (works around temporary network failure and other temporary hiccups that cause bad response codes).

How does this library discover NAT-PMP and PCP gateway devices?

Unfortunately, Java doesn't provide a built-in way to grab gateway addresses from the OS, nor does it allow you to do ICMP probing to find devices on path (e.g. set TTL to 1 and ping, the first device is very likely the gateway). As such, the Port Mapper project makes use of various OS-specific commands to find gateway addresses. You can find out which commands are used by looking through the source code.

Does this library support PCP authentication and/or UPnP-IGD device protection?

Not at this time. Support may be added in the future.

Does this library support unsolicited PCP ANNOUNCEs or UPnP eventing?

Not at this time. Version 1 did support unsolicited PCP ANNOUNCEs, but it has since been removed because no devices seem to support it. If you're worried about not being notified of lost mappings, just make sure you refresh more often so that you catch the problem early (e.g. every 5 or 10 minutes).

What alternatives are available?

Alternatives to Port Mapper include:

If you know of any other projects please let me know and I'll update this section.

Change Log

Template adapted from http://keepachangelog.com/

All notable changes to this project will be documented in this file. This project adheres to Semantic Versioning.

[Unreleased][unreleased]

[2.0.5] - 2016-11-18

  • FIXED: NetworkRunnable crash when getting IP addresses for inactive interface

[2.0.4] - 2016-07-26

  • FIXED: Race condition where some stdout output wouldn't be received from a process because it was sent just before termination
  • FIXED: Process output now include stderr output
  • FIXED: Attempt to filter out bad NAT-PMP/PCP gateway IP addresses -- loopback (e.g. 127.0.0.1), any address (e.g. 0.0.0.0), and multicast addresses are now filtered out
  • FIXED: PCP port signed/unsigned integer bug fixed
  • FIXED: NAT-PMP result code signed/unsigned integer bug fixed
  • OTHER: Logging tweaked for NetworkGateway and ProcessGateway

[2.0.3] - 2016-07-15

  • FIXED: \r\n newlines removed from UPnP-IGD request XMLs
  • FIXED: namespace and encoding style URLs fixed for UPnP-IGD request XMLs

[2.0.2] - 2016-07-06

  • OTHER: License updated from LGPL3 to Apache2

[2.0.1] - 2016-06-25

  • FIXED: Issue when getting bad unexpected HTTP version in response

[2.0.0] - 2016-01-17

  • CHANGED: Refactored API and backend
  • ADDED: UPnP-IGD IPv6 firewall service support
  • FIXED: Issue when scraping IPv4 address from output
  • FIXED: Issue with Macs getting SocketExceptions on PCP discovery
  • OTHER: Updated README
  • OTHER: New logo made in Inkscape

[1.0.0] - 2014-06-24

  • Initial release.