📄 natpmpdeviceimpl.java
字号:
/*
* Created on 12-Jun-2006
* Created by Marc Colosimo
* Copyright (C) 2004, 2005, 2006 Aelitis, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* AELITIS, SAS au capital de 46,603.30 euros
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
*
* Connection class for NAT-PMP (Port Mapping Protocol) Devices
*
* @see <http://files.dns-sd.org/draft-cheshire-nat-pmp.txt>
* Tested with <https://www.grc.com/x/portprobe=6881>
*
* This code is ugly, but it works.
*
* Some assumptions:
* - The NAT device will be at xxx.xxx.xxx.1
*
* This needs to be threaded.
* - It could take upto 2 minutes to timeout during any request
* - We need to listen for address changes (using link-local multicast?)!
* - We need to request the mapping again before it expires
*
* Some hints and to dos:
* - The draft spec says that the device could set max lease life time
* to be less than requested. this should be checked.
* - Need to make something to renew port mappings - recommend that
* the client SHOULD begin trying to renew the mapping halfway to *
* expiry time, like DHCP
* - Need to listen for public address changes
*
* Version 0.1b
*/
package com.aelitis.net.natpmp.impl;
import java.net.*;
import com.aelitis.azureus.core.util.NetUtils;
import com.aelitis.net.natpmp.NATPMPDeviceAdapter;
import com.aelitis.net.natpmp.NatPMPDevice;
/**
*
* Main class
*
* */
public class NatPMPDeviceImpl implements NatPMPDevice
{
static final int NATMAP_VER = 0;
static final int NATMAP_PORT = 5351;
static final int NATMAP_RESPONSE_MASK = 128;
static final int NATMAP_INIT_RETRY = 250; // ms
static final int NATMAP_MAX_RETRY = 2250; // gives us three tries
// lease life in seconds
// 24 hours
static final int NATMAP_DEFAULT_LEASE = 60*60*24;
// link-local multicast address - for address changes
// not implemented
static final String NATMAP_LLM = "224.0.0.1";
// Opcodes used for ..
static final byte NATOp_AddrRequest = 0; // Ask for a NAT-PMP device
static final byte NATOp_MapUDP = 1; // Map a UDP Port
static final byte NATOp_MapTCP = 2; // Map a TCP Port
/* Length of Requests in bytes */
static final int NATAddrRequest = 2;
static final int NATPortMapRequestLen = 4 * 3; // 4 bytes by 3
/* Length of Replies in Bytes */
static final int NATAddrReplyLen = 4 * 3;
static final int NATPortMapReplyLen = 4 * 4;
/* Current Result Codes */
static final int NATResultSuccess = 0;
static final int NATResultUnsupportedVer = 1;
/**
* Not Authorized/Refused
* (e.g. box supports mapping, but user has turned feature off)
**/
static final int NATResultNotAuth = 2;
/**
* Network Failure
* (e.g. NAT box itself has not obtained a DHCP lease)
**/
static final int NATResultNetFailure = 3;
static final int NATResultNoResc = 4; // Out of resources
static final int NATResultUnsupportedOp = 5; // Unsupported opcode
/* Instance specific globals */
String current_router_address = "?";
InetAddress hostInet; // Our address
InetAddress natPriInet; // NAT's private (interal) address
InetAddress natPubInet; // NAT's public address
NetworkInterface networkInterface; // natPriInet network interface
InetAddress llmInet;
boolean nat_pmp_found = false;
int nat_epoch = 0; // This gets updated each request
private NATPMPDeviceAdapter adapter;
/**
* Singleton creation
**/
private static NatPMPDeviceImpl NatPMPDeviceSingletonRef;
public static synchronized NatPMPDeviceImpl
getSingletonObject(NATPMPDeviceAdapter adapter) throws Exception {
if (NatPMPDeviceSingletonRef == null)
NatPMPDeviceSingletonRef = new NatPMPDeviceImpl(adapter);
return NatPMPDeviceSingletonRef;
}
private
NatPMPDeviceImpl(
NATPMPDeviceAdapter _adapter)
throws Exception
{
adapter = _adapter;
hostInet = NetUtils.getLocalHost();
checkRouterAddress();
}
protected void
checkRouterAddress()
throws Exception
{
String natAddr = adapter.getRouterAddress().trim();
if ( natAddr.length() == 0 ){
natAddr = convertHost2RouterAddress(hostInet);
}
if ( natAddr.equals( current_router_address )){
return;
}
current_router_address = natAddr;
log("Using Router IP: " + natAddr);
natPriInet = InetAddress.getByName(natAddr);
networkInterface = NetworkInterface.getByInetAddress( natPriInet );
}
/**
* Send a request and wait for reply
* This class should be threaded!!!
*
* This sends to the default NATPMP_PORT.
*
* @param dstInet destination address (should be the private NAT address)
* @param dstPkt packet to send
* @param recBuf byte buffer big enough to hold received
**/
public DatagramPacket sendNATMsg(InetAddress dstInet, DatagramPacket dstPkt, byte[] recBuf) throws Exception {
int retryInterval = NATMAP_INIT_RETRY;
boolean recRep = false;
DatagramSocket skt = new DatagramSocket();
skt.connect( dstInet, NATMAP_PORT );
skt.setSoTimeout( NATMAP_INIT_RETRY );
skt.send(dstPkt); // how do we know we hit something?
DatagramPacket recPkt = new DatagramPacket(recBuf, recBuf.length);
// We have several tries at this (like 3)
while ( !recRep && (retryInterval < NATMAP_MAX_RETRY) ) {
try {
skt.receive(recPkt);
recRep = true;
} catch (SocketTimeoutException ste) {
//log("Timed Out!");
//log( ste.getMessage() );
// sleep before trying again
// this.sleep(retryInterval);
Thread.sleep(retryInterval); // not sleeping?!?
// increase retry interval
retryInterval += (retryInterval * 2);
}
}
if ( !recRep ){
throw( new PortUnreachableException());
}
// check recRep for true!!!
return recPkt;
}
/**
* Try to connect with a NAT-PMP device.
* This could take sometime.
*
* @return true if it found one
**/
public boolean connect() throws Exception {
checkRouterAddress();
try{
// Send NAT request to find out if it is PMP happy
byte reqBuf[] = {NATMAP_VER, NATOp_AddrRequest};
DatagramPacket dstPkt = new DatagramPacket(reqBuf, reqBuf.length);
byte recBuf[] = new byte[NATAddrReplyLen];
DatagramPacket recPkt = sendNATMsg(natPriInet, dstPkt, recBuf);
int recVer = unsigned8ByteArrayToInt( recBuf, 0 );
int recOp = unsigned8ByteArrayToInt( recBuf, 1 );
int recErr = unsigned16ByteArrayToInt( recBuf, 2 );
int recEpoch = unsigned32ByteArrayToInt( recBuf, 4 );
String recPubAddr = unsigned8ByteArrayToInt( recBuf, 8 ) + "." +
unsigned8ByteArrayToInt( recBuf, 9 ) + "." +
unsigned8ByteArrayToInt( recBuf, 10 ) + "." +
unsigned8ByteArrayToInt( recBuf, 11 );
/* set the global NAT public address */
natPubInet = InetAddress.getByName(recPubAddr);
/* set the global NAT Epoch time (in seconds) */
nat_epoch = recEpoch;
if (recErr != 0)
throw( new Exception("NAT-PMP connection error: " + recErr) );
log("Err: " +recErr);
log("Uptime: " + recEpoch);
log("Public Address: " + recPubAddr);
/**
* TO DO:
* Set up listner for announcements from the device for
* address changes (public address changes)
**/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -