/* * This file Copyright (C) Mnemosyne LLC * * This file is licensed by the GPL version 2. Works owned by the * Transmission project are granted a special exemption to clause 2(b) * so that the bulk of its code can remain under the MIT license. * This exemption does not extend to derived works not owned by * the Transmission project. * * $Id: port-forwarding.c 13199 2012-02-04 01:28:15Z jordan $ */ #include #include #include #include #include "transmission.h" #include "natpmp_local.h" #include "net.h" #include "peer-mgr.h" #include "port-forwarding.h" #include "session.h" #include "torrent.h" #include "upnp.h" #include "utils.h" static const char * getKey( void ) { return _( "Port Forwarding" ); } struct tr_shared { bool isEnabled; bool isShuttingDown; bool doPortCheck; tr_port_forwarding natpmpStatus; tr_port_forwarding upnpStatus; tr_upnp * upnp; tr_natpmp * natpmp; tr_session * session; struct event * timer; }; /*** **** ***/ static const char* getNatStateStr( int state ) { switch( state ) { case TR_PORT_MAPPING: return _( "Starting" ); case TR_PORT_MAPPED: return _( "Forwarded" ); case TR_PORT_UNMAPPING: return _( "Stopping" ); case TR_PORT_UNMAPPED: return _( "Not forwarded" ); default: return "???"; } } static void natPulse( tr_shared * s, bool do_check ) { const tr_port private_peer_port = s->session->private_peer_port; const int is_enabled = s->isEnabled && !s->isShuttingDown; tr_port public_peer_port; int oldStatus; int newStatus; if( s->natpmp == NULL ) s->natpmp = tr_natpmpInit( ); if( s->upnp == NULL ) s->upnp = tr_upnpInit( ); oldStatus = tr_sharedTraversalStatus( s ); s->natpmpStatus = tr_natpmpPulse( s->natpmp, private_peer_port, is_enabled, &public_peer_port ); if( s->natpmpStatus == TR_PORT_MAPPED ) s->session->public_peer_port = public_peer_port; s->upnpStatus = tr_upnpPulse( s->upnp, private_peer_port, is_enabled, do_check ); newStatus = tr_sharedTraversalStatus( s ); if( newStatus != oldStatus ) tr_ninf( getKey( ), _( "State changed from \"%1$s\" to \"%2$s\"" ), getNatStateStr( oldStatus ), getNatStateStr( newStatus ) ); } static void set_evtimer_from_status( tr_shared * s ) { int sec=0, msec=0; /* when to wake up again */ switch( tr_sharedTraversalStatus( s ) ) { case TR_PORT_MAPPED: /* if we're mapped, everything is fine... check back in 20 minutes * to renew the port forwarding if it's expired */ s->doPortCheck = true; sec = 60 * 20; break; case TR_PORT_ERROR: /* some kind of an error. wait 60 seconds and retry */ sec = 60; break; default: /* in progress. pulse frequently. */ msec = 333000; break; } if( s->timer != NULL ) tr_timerAdd( s->timer, sec, msec ); } static void onTimer( int fd UNUSED, short what UNUSED, void * vshared ) { tr_shared * s = vshared; assert( s ); assert( s->timer ); /* do something */ natPulse( s, s->doPortCheck ); s->doPortCheck = false; /* set up the timer for the next pulse */ set_evtimer_from_status( s ); } /*** **** ***/ tr_shared * tr_sharedInit( tr_session * session ) { tr_shared * s = tr_new0( tr_shared, 1 ); s->session = session; s->isEnabled = false; s->upnpStatus = TR_PORT_UNMAPPED; s->natpmpStatus = TR_PORT_UNMAPPED; #if 0 if( isEnabled ) { s->timer = tr_new0( struct event, 1 ); evtimer_set( s->timer, onTimer, s ); tr_timerAdd( s->timer, 0, 333000 ); } #endif return s; } static void stop_timer( tr_shared * s ) { if( s->timer != NULL ) { event_free( s->timer ); s->timer = NULL; } } static void stop_forwarding( tr_shared * s ) { tr_ninf( getKey( ), "%s", _( "Stopped" ) ); natPulse( s, false ); tr_natpmpClose( s->natpmp ); s->natpmp = NULL; s->natpmpStatus = TR_PORT_UNMAPPED; tr_upnpClose( s->upnp ); s->upnp = NULL; s->upnpStatus = TR_PORT_UNMAPPED; stop_timer( s ); } void tr_sharedClose( tr_session * session ) { tr_shared * s = session->shared; s->isShuttingDown = true; stop_forwarding( s ); s->session->shared = NULL; tr_free( s ); } static void start_timer( tr_shared * s ) { s->timer = evtimer_new( s->session->event_base, onTimer, s ); set_evtimer_from_status( s ); } void tr_sharedTraversalEnable( tr_shared * s, bool isEnabled ) { if(( s->isEnabled = isEnabled )) start_timer( s ); else stop_forwarding( s ); } void tr_sharedPortChanged( tr_session * session ) { tr_shared * s = session->shared; if( s->isEnabled ) { stop_timer( s ); natPulse( s, false ); start_timer( s ); } } bool tr_sharedTraversalIsEnabled( const tr_shared * s ) { return s->isEnabled; } int tr_sharedTraversalStatus( const tr_shared * s ) { return MAX( s->natpmpStatus, s->upnpStatus ); }