/*
* File : NatCheckerServer.java
* Created : 12 oct. 2003 19:05:09
* By : Olivier
*
* Azureus - a Java Bittorrent client
*
* 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.
*
* 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 ( see the LICENSE file ).
*
* 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
*/
package org.gudy.azureus2.core3.ipchecker.natchecker;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import org.gudy.azureus2.core3.logging.*;
import org.gudy.azureus2.core3.util.*;
import com.aelitis.azureus.core.networkmanager.*;
import com.aelitis.azureus.core.networkmanager.impl.TransportHelper;
import com.aelitis.azureus.core.networkmanager.impl.http.HTTPNetworkManager;
import com.aelitis.azureus.core.networkmanager.impl.tcp.TCPNetworkManager;
import com.aelitis.azureus.core.peermanager.messaging.*;
import com.aelitis.azureus.core.peermanager.messaging.azureus.*;
/**
*
*
*/
public class NatCheckerServer extends AEThread {
private static final LogIDs LOGID = LogIDs.NET;
private static final String incoming_handshake = "NATCHECK_HANDSHAKE";
private final InetAddress bind_ip;
private boolean bind_ip_set;
private final String check;
private final boolean http_test;
private ServerSocket server;
private volatile boolean bContinue = true;
private final boolean use_incoming_router;
private NetworkManager.ByteMatcher matcher;
public
NatCheckerServer(
InetAddress _bind_ip,
int _port,
String _check,
boolean _http_test )
throws Exception
{
super("Nat Checker Server");
bind_ip = _bind_ip;
check = _check;
http_test = _http_test;
if ( http_test ){
HTTPNetworkManager net_man = HTTPNetworkManager.getSingleton();
if ( net_man.isHTTPListenerEnabled()){
use_incoming_router = _port == net_man.getHTTPListeningPortNumber();
}else{
use_incoming_router = false;
}
if ( use_incoming_router ){
if ( !net_man.isEffectiveBindAddress( bind_ip )){
net_man.setExplicitBindAddress( bind_ip );
bind_ip_set = true;
}
}
}else{
TCPNetworkManager net_man = TCPNetworkManager.getSingleton();
if ( net_man.isTCPListenerEnabled()){
use_incoming_router = _port == net_man.getTCPListeningPortNumber();
}else{
use_incoming_router = false;
}
if ( use_incoming_router ) {
if ( !net_man.isEffectiveBindAddress( bind_ip )){
net_man.setExplicitBindAddress( bind_ip );
bind_ip_set = true;
}
//test port and currently-configured listening port are the same,
//so register for incoming connection routing
matcher = new NetworkManager.ByteMatcher() {
public int matchThisSizeOrBigger(){ return( maxSize()); }
public int maxSize() { return incoming_handshake.getBytes().length; }
public int minSize(){ return maxSize(); }
public Object matches( TransportHelper transport, ByteBuffer to_compare, int port ) {
int old_limit = to_compare.limit();
to_compare.limit( to_compare.position() + maxSize() );
boolean matches = to_compare.equals( ByteBuffer.wrap( incoming_handshake.getBytes() ) );
to_compare.limit( old_limit ); //restore buffer structure
return matches?"":null;
}
public Object minMatches( TransportHelper transport, ByteBuffer to_compare, int port ) { return( matches( transport, to_compare, port )); }
public byte[][] getSharedSecrets(){ return( null ); }
public int getSpecificPort(){return( -1 );
}
};
NetworkManager.getSingleton().requestIncomingConnectionRouting(
matcher,
new NetworkManager.RoutingListener() {
public void
connectionRouted(
NetworkConnection connection,
Object routing_data )
{
if (Logger.isEnabled())
Logger.log(new LogEvent(LOGID, "Incoming connection from ["
+ connection + "] successfully routed to NAT CHECKER"));
try{
ByteBuffer msg = getMessage();
Transport transport = connection.getTransport();
long start = SystemTime.getCurrentTime();
while( msg.hasRemaining()){
transport.write( new ByteBuffer[]{ msg }, 0, 1 );
if ( msg.hasRemaining()){
long now = SystemTime.getCurrentTime();
if ( now < start ){
start = now;
}else{
if ( now - start > 30000 ){
throw( new Exception( "Timeout" ));
}
}
Thread.sleep( 50 );
}
}
}catch( Throwable t ) {
Debug.out( "Nat check write failed", t );
}
connection.close( null );
}
public boolean
autoCryptoFallback()
{
return( true );
}
},
new MessageStreamFactory() {
public MessageStreamEncoder createEncoder() { return new AZMessageEncoder(AZMessageEncoder.PADDING_MODE_NONE); /* unused */}
public MessageStreamDecoder createDecoder() { return new AZMessageDecoder(); /* unused */}
});
}
if (Logger.isEnabled())
Logger.log(new LogEvent(LOGID, "NAT tester using central routing for "
+ "server socket"));
}
if ( !use_incoming_router ){
//different port than already listening on, start new listen server
try{
server = new ServerSocket(); //unbound
server.setReuseAddress( true ); //set SO_REUSEADDR
InetSocketAddress address;
if( bind_ip != null ) {
address = new InetSocketAddress( bind_ip, _port );
}else {
address = new InetSocketAddress( _port );
}
server.bind( address );
if (Logger.isEnabled()) Logger.log(new LogEvent(LOGID, "NAT tester server socket bound to " +address ));
}catch(Exception e) {
Logger.log(new LogEvent(LOGID, "NAT tester failed to setup listener socket", e ));
throw( e );
}
}
}
protected ByteBuffer
getMessage()
throws IOException
{
Map map = new HashMap();
map.put( "check", check );
byte[] map_bytes = BEncoder.encode( map );
ByteBuffer msg = ByteBuffer.allocate( 4 + map_bytes.length );
msg.putInt( map_bytes.length );
msg.put( map_bytes );
msg.flip();
return( msg );
}
public void runSupport() {
while(bContinue) {
try {
if (use_incoming_router) {
//just NOOP loop sleep while waiting for routing
Thread.sleep(20);
}
else {
//listen for accept
Socket sck = server.accept();
try{
sck.getOutputStream().write( getMessage().array());
sck.close();
sck = null;
}finally{
if ( sck != null ){
try{
sck.close();
}catch( Throwable e ){
}
}
}
}
} catch(Exception e) {
//Debug.printStackTrace(e);
bContinue = false;
}
}
}
public void stopIt() {
bContinue = false;
if( use_incoming_router ) {
if ( http_test ){
if ( bind_ip_set ){
HTTPNetworkManager.getSingleton().clearExplicitBindAddress();
}
}else{
NetworkManager.getSingleton().cancelIncomingConnectionRouting( matcher );
if ( bind_ip_set ){
TCPNetworkManager.getSingleton().clearExplicitBindAddress();
}
}
}
else if( server != null ) {
try {
server.close();
}
catch(Throwable t) { t.printStackTrace(); }
}
}
}