DHTTransportContact rendezvous,
final DHTTransportUDPContact target,
Map originator_client_data,
boolean no_tunnel )
{
AESemaphore wait_sem = new AESemaphore( "DHTNatPuncher::sendPunch" );
Object[] wait_data = new Object[]{ target, wait_sem, new Integer(0)};
try{
try{
punch_mon.enter();
oustanding_punches.add( wait_data );
}finally{
punch_mon.exit();
}
Map request = new HashMap();
request.put("type", new Long( RT_PUNCH_REQUEST ));
request.put("target", target.getAddress().toString().getBytes());
if ( originator_client_data != null ){
if ( no_tunnel ){
originator_client_data.put( "_notunnel", new Long(1));
}
request.put( "client_data", originator_client_data );
}
// for a message payload (i.e. no_tunnel) we double the initiator timeout to give
// more chance for reasonable size messages to get through as they have to go through
// 2 xfer processes
Map response = sendRequest( rendezvous, request, no_tunnel?TRANSFER_TIMEOUT*2:TRANSFER_TIMEOUT );
if ( response == null ){
return( null );
}
if (((Long)response.get( "type" )).intValue() == RT_PUNCH_REPLY ){
int result = ((Long)response.get("ok")).intValue();
trace( "received " + ( no_tunnel?"message":"punch") + " reply: " + (result==0?"failed":"ok" ));
if ( result == 1 ){
// pick up port changes from the rendezvous
Long indirect_port = (Long)response.get( "port" );
if ( indirect_port != null ){
int transport_port = indirect_port.intValue();
if ( transport_port != 0 ){
InetSocketAddress existing_address = target.getTransportAddress();
if ( transport_port != existing_address.getPort()){
target.setTransportAddress(
new InetSocketAddress(existing_address.getAddress(), transport_port ));
}
}
}
if ( !no_tunnel ){
// ping the target a few times to try and establish a tunnel
UTTimerEvent event =
timer.addPeriodicEvent(
3000,
new UTTimerEventPerformer()
{
private int pings = 1;
public void
perform(
UTTimerEvent event )
{
if ( pings > 3 ){
event.cancel();
return;
}
pings++;
if ( sendTunnelOutbound( target )){
event.cancel();
}
}
});
if ( sendTunnelOutbound( target )){
event.cancel();
}
// give the other end a few seconds to kick off some tunnel events to us
if ( wait_sem.reserve(10000)){
event.cancel();
}
}