Use libnatpmp for port mapping. rewrite the upnp/natpmp manager.
NOTE: this will break the xpjets build until SoftwareElves or a volunteer patches the xcode project file to make a libnatpmp library just like was done for libminiupnp.
This commit is contained in:
parent
c7dbca7a75
commit
73ee11f233
|
@ -10,6 +10,7 @@ transmission_cli_SOURCES = transmissioncli.c
|
|||
transmission_cli_LDADD = \
|
||||
$(top_builddir)/libtransmission/libtransmission.a \
|
||||
$(top_builddir)/third-party/libevent/libevent.la \
|
||||
$(top_builddir)/third-party/libnatpmp/libnatpmp.a \
|
||||
$(top_builddir)/third-party/miniupnp/libminiupnp.a \
|
||||
$(OPENSSL_LIBS) $(PTHREAD_LIBS) -lm
|
||||
|
||||
|
|
|
@ -339,7 +339,7 @@ int main( int argc, char ** argv )
|
|||
for( i = 0; i < 10; i++ )
|
||||
{
|
||||
hstat = tr_handleStatus( h );
|
||||
if( TR_NAT_TRAVERSAL_DISABLED == hstat->natTraversalStatus )
|
||||
if( TR_NAT_TRAVERSAL_UNMAPPED == hstat->natTraversalStatus )
|
||||
{
|
||||
/* Port mappings were deleted */
|
||||
break;
|
||||
|
|
|
@ -208,6 +208,7 @@ AC_CONFIG_FILES([Makefile
|
|||
libtransmission/Makefile
|
||||
third-party/Makefile
|
||||
third-party/miniupnp/Makefile
|
||||
third-party/libnatpmp/Makefile
|
||||
macosx/Makefile
|
||||
wx/Makefile
|
||||
wx/images/Makefile
|
||||
|
|
|
@ -29,6 +29,7 @@ COMMON_LDADD = \
|
|||
./libdaemon.a \
|
||||
$(top_builddir)/libtransmission/libtransmission.a \
|
||||
$(top_builddir)/third-party/miniupnp/libminiupnp.a \
|
||||
$(top_builddir)/third-party/libnatpmp/libnatpmp.a \
|
||||
$(top_builddir)/third-party/libevent/libevent.la \
|
||||
$(OPENSSL_LIBS) $(PTHREAD_LIBS) -lm
|
||||
|
||||
|
|
|
@ -648,7 +648,7 @@ timerfunc( int fd UNUSED, short event UNUSED, void * arg UNUSED )
|
|||
if( !stillmore )
|
||||
{
|
||||
hs = tr_handleStatus( gl_handle );
|
||||
if( TR_NAT_TRAVERSAL_DISABLED != hs->natTraversalStatus )
|
||||
if( TR_NAT_TRAVERSAL_UNMAPPED != hs->natTraversalStatus )
|
||||
{
|
||||
stillmore = 1;
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@ transmission_LDADD = \
|
|||
$(top_builddir)/libtransmission/libtransmission.a \
|
||||
$(top_builddir)/third-party/libevent/libevent.la \
|
||||
$(top_builddir)/third-party/miniupnp/libminiupnp.a \
|
||||
$(top_builddir)/third-party/libnatpmp/libnatpmp.a \
|
||||
$(GTK_LIBS) \
|
||||
$(OPENSSL_LIBS) \
|
||||
$(PTHREAD_LIBS) -lm
|
||||
|
|
|
@ -971,6 +971,9 @@ smsg_torall( enum ipc_msg id, benc_val_t * val SHUTUP, int64_t tag,
|
|||
simpleresp( con, tag, IPC_MSG_OK );
|
||||
}
|
||||
|
||||
#define TR_NAT_TRAVERSAL_IS_DISABLED( st ) \
|
||||
( TR_NAT_TRAVERSAL_UNMAPPED == (st) || TR_NAT_TRAVERSAL_UNMAPPING == (st) )
|
||||
|
||||
static void
|
||||
smsg_pref( enum ipc_msg id, benc_val_t * val SHUTUP, int64_t tag, void * arg )
|
||||
{
|
||||
|
|
|
@ -1,822 +1,185 @@
|
|||
/******************************************************************************
|
||||
* $Id$
|
||||
/*
|
||||
* This file Copyright (C) 2007 Charles Kerr <charles@rebelbase.com>
|
||||
*
|
||||
* Copyright (c) 2006 Transmission authors and contributors
|
||||
* 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.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
* $Id:$
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h> /* strerror */
|
||||
|
||||
#ifdef __BEOS__
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <libnatpmp/natpmp.h>
|
||||
|
||||
#include "transmission.h"
|
||||
#include "natpmp.h"
|
||||
#include "net.h"
|
||||
#include "platform.h" /* tr_getDefaultRoute() */
|
||||
#include "shared.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define PMP_PORT 5351
|
||||
#define PMP_INITIAL_DELAY 250 /* ms, 1/4 second */
|
||||
#define PMP_TOTAL_DELAY 120000 /* ms, 2 minutes */
|
||||
#define PMP_VERSION 0
|
||||
#define PMP_OPCODE_GETIP 0
|
||||
#define PMP_OPCODE_ADDUDP 1
|
||||
#define PMP_OPCODE_ADDTCP 2
|
||||
#define PMP_LIFETIME 3600 /* secs, one hour */
|
||||
#define PMP_RESULT_OK 0
|
||||
#define PMP_RESULT_BADVERS 1
|
||||
#define PMP_RESULT_REFUSED 2
|
||||
#define PMP_RESULT_NETDOWN 3
|
||||
#define PMP_RESULT_NOMEM 4
|
||||
#define PMP_RESULT_BADOPCODE 5
|
||||
#define LIFETIME_SECS 3600
|
||||
|
||||
#define PMP_OPCODE_FROM_RESPONSE( op ) ( 0x80 ^ (op) )
|
||||
#define PMP_OPCODE_TO_RESPONSE( op ) ( 0x80 | (op) )
|
||||
#define PMP_OPCODE_IS_RESPONSE( op ) ( 0x80 & (op) )
|
||||
#define PMP_TOBUF16( buf, num ) ( *( (uint16_t *) (buf) ) = htons( (num) ) )
|
||||
#define PMP_TOBUF32( buf, num ) ( *( (uint32_t *) (buf) ) = htonl( (num) ) )
|
||||
#define PMP_FROMBUF16( buf ) ( htons( *( (uint16_t *) (buf) ) ) )
|
||||
#define PMP_FROMBUF32( buf ) ( htonl( *( (uint32_t *) (buf) ) ) )
|
||||
#define KEY "Port Mapping (NAT-PMP): "
|
||||
|
||||
typedef struct tr_natpmp_uptime_s
|
||||
typedef enum
|
||||
{
|
||||
time_t when;
|
||||
uint32_t uptime;
|
||||
} tr_natpmp_uptime_t;
|
||||
|
||||
typedef struct tr_natpmp_req_s
|
||||
{
|
||||
unsigned int adding : 1;
|
||||
unsigned int nobodyhome : 1;
|
||||
unsigned int tmpfail : 1;
|
||||
int fd;
|
||||
int delay;
|
||||
uint64_t retry;
|
||||
uint64_t timeout;
|
||||
int askport;
|
||||
int gotport;
|
||||
} tr_natpmp_req_t;
|
||||
|
||||
TR_NATPMP_IDLE,
|
||||
TR_NATPMP_ERR,
|
||||
TR_NATPMP_RECV_PUB,
|
||||
TR_NATPMP_SEND_MAP,
|
||||
TR_NATPMP_RECV_MAP,
|
||||
TR_NATPMP_SEND_UNMAP,
|
||||
TR_NATPMP_RECV_UNMAP
|
||||
}
|
||||
tr_natpmp_state;
|
||||
|
||||
struct tr_natpmp
|
||||
{
|
||||
#define PMP_STATE_IDLE 1
|
||||
#define PMP_STATE_ADDING 2
|
||||
#define PMP_STATE_DELETING 3
|
||||
#define PMP_STATE_MAPPED 4
|
||||
#define PMP_STATE_FAILED 5
|
||||
#define PMP_STATE_NOBODYHOME 6
|
||||
#define PMP_STATE_TMPFAIL 7
|
||||
char state;
|
||||
unsigned int active : 1;
|
||||
unsigned int mapped : 1;
|
||||
struct in_addr dest;
|
||||
int newport;
|
||||
int mappedport;
|
||||
uint64_t renew;
|
||||
tr_natpmp_req_t * req;
|
||||
tr_natpmp_uptime_t uptime;
|
||||
int mcastfd;
|
||||
int port;
|
||||
int isMapped;
|
||||
time_t renewTime;
|
||||
tr_natpmp_state state;
|
||||
natpmp_t natpmp;
|
||||
};
|
||||
|
||||
typedef struct tr_natpmp_parse_s
|
||||
/**
|
||||
***
|
||||
**/
|
||||
|
||||
static void
|
||||
logVal( const char * func, int ret )
|
||||
{
|
||||
unsigned int tmpfail : 1;
|
||||
uint32_t seconds;
|
||||
uint16_t port;
|
||||
uint32_t lifetime;
|
||||
}
|
||||
tr_natpmp_parse_t;
|
||||
|
||||
static void
|
||||
unmap( tr_natpmp * pmp );
|
||||
static int
|
||||
checktime( tr_natpmp_uptime_t * uptime, uint32_t seen );
|
||||
static void
|
||||
killsock( int * fd );
|
||||
static tr_natpmp_req_t *
|
||||
newreq( int adding, struct in_addr addr, int port );
|
||||
static void
|
||||
killreq( tr_natpmp_req_t ** req );
|
||||
static void
|
||||
resetreq( tr_natpmp_req_t * req );
|
||||
static tr_tristate_t
|
||||
pulsereq( tr_natpmp * req );
|
||||
static int
|
||||
sendreq( tr_natpmp_req_t * req );
|
||||
static int
|
||||
mcastsetup();
|
||||
static void
|
||||
mcastpulse( tr_natpmp * pmp );
|
||||
static tr_tristate_t
|
||||
parseresponse( uint8_t * buf, int len, int port, tr_natpmp_parse_t * parse );
|
||||
|
||||
tr_natpmp *
|
||||
tr_natpmpInit()
|
||||
{
|
||||
tr_natpmp * pmp;
|
||||
|
||||
pmp = calloc( 1, sizeof( *pmp ) );
|
||||
if( NULL == pmp )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pmp->state = PMP_STATE_IDLE;
|
||||
pmp->mcastfd = -1;
|
||||
|
||||
if( tr_getDefaultRoute( &pmp->dest ) || INADDR_ANY == pmp->dest.s_addr )
|
||||
{
|
||||
pmp->dest.s_addr = INADDR_NONE;
|
||||
}
|
||||
|
||||
if( INADDR_NONE == pmp->dest.s_addr )
|
||||
{
|
||||
tr_dbg( "nat-pmp device is unknown" );
|
||||
}
|
||||
if( ret==NATPMP_TRYAGAIN )
|
||||
tr_dbg( KEY "%s returned 'try again'", func );
|
||||
else if( ret >= 0 )
|
||||
tr_dbg( KEY "%s returned success (%d)", func, ret );
|
||||
else
|
||||
{
|
||||
char addrstr[INET_ADDRSTRLEN];
|
||||
tr_netNtop( &pmp->dest, addrstr, sizeof( addrstr ) );
|
||||
tr_dbg( "nat-pmp device is %s", addrstr );
|
||||
}
|
||||
tr_err( KEY "%s returned error %d, errno is %d (%s)", func, ret, errno, strerror(errno) );
|
||||
}
|
||||
|
||||
return pmp;
|
||||
struct tr_natpmp*
|
||||
tr_natpmpInit( void )
|
||||
{
|
||||
struct tr_natpmp * nat = tr_new0( struct tr_natpmp, 1 );
|
||||
int val;
|
||||
|
||||
val = initnatpmp( &nat->natpmp );
|
||||
logVal( "initnatpmp", val );
|
||||
val = sendpublicaddressrequest( &nat->natpmp );
|
||||
logVal( "sendpublicaddressrequest", val );
|
||||
|
||||
nat->state = val < 0 ? TR_NATPMP_ERR : TR_NATPMP_RECV_PUB;
|
||||
nat->port = -1;
|
||||
return nat;
|
||||
}
|
||||
|
||||
void
|
||||
tr_natpmpStart( tr_natpmp * pmp )
|
||||
tr_natpmpClose( tr_natpmp * nat )
|
||||
{
|
||||
if( !pmp->active )
|
||||
{
|
||||
tr_inf( "starting nat-pmp" );
|
||||
pmp->active = 1;
|
||||
if( 0 > pmp->mcastfd )
|
||||
{
|
||||
pmp->mcastfd = mcastsetup();
|
||||
}
|
||||
}
|
||||
}
|
||||
assert( !nat->isMapped );
|
||||
assert( ( nat->state == TR_NATPMP_IDLE ) || ( nat->state == TR_NATPMP_ERR ) );
|
||||
|
||||
void
|
||||
tr_natpmpStop( tr_natpmp * pmp )
|
||||
{
|
||||
if( pmp->active )
|
||||
{
|
||||
tr_inf( "stopping nat-pmp" );
|
||||
pmp->active = 0;
|
||||
killsock( &pmp->mcastfd );
|
||||
unmap( pmp );
|
||||
}
|
||||
closenatpmp( &nat->natpmp );
|
||||
tr_free( nat );
|
||||
}
|
||||
|
||||
int
|
||||
tr_natpmpStatus( tr_natpmp * pmp )
|
||||
tr_natpmpPulse( struct tr_natpmp * nat, int port, int isEnabled )
|
||||
{
|
||||
int ret;
|
||||
|
||||
if( !pmp->active )
|
||||
|
||||
if( nat->state == TR_NATPMP_RECV_PUB )
|
||||
{
|
||||
ret = ( PMP_STATE_DELETING == pmp->state ?
|
||||
TR_NAT_TRAVERSAL_UNMAPPING : TR_NAT_TRAVERSAL_DISABLED );
|
||||
natpmpresp_t response;
|
||||
const int val = readnatpmpresponseorretry( &nat->natpmp, &response );
|
||||
logVal( "readnatpmpresponseorretry", val );
|
||||
if( val >= 0 ) {
|
||||
tr_inf( KEY "found public address %s", inet_ntoa( response.publicaddress.addr ) );
|
||||
nat->state = TR_NATPMP_IDLE;
|
||||
} else if( val != NATPMP_TRYAGAIN ) {
|
||||
nat->state = TR_NATPMP_ERR;
|
||||
}
|
||||
}
|
||||
else if( pmp->mapped )
|
||||
|
||||
if( ( nat->state == TR_NATPMP_IDLE ) || ( nat->state == TR_NATPMP_ERR ) )
|
||||
{
|
||||
if( nat->isMapped && ( !isEnabled || ( nat->port != port ) ) )
|
||||
nat->state = TR_NATPMP_SEND_UNMAP;
|
||||
}
|
||||
|
||||
if( nat->state == TR_NATPMP_SEND_UNMAP )
|
||||
{
|
||||
const int val = sendnewportmappingrequest( &nat->natpmp, NATPMP_PROTOCOL_TCP, nat->port, nat->port, 0 );
|
||||
logVal( "sendnewportmappingrequest", val );
|
||||
nat->state = val < 0 ? TR_NATPMP_ERR : TR_NATPMP_RECV_UNMAP;
|
||||
}
|
||||
|
||||
if( nat->state == TR_NATPMP_RECV_UNMAP )
|
||||
{
|
||||
natpmpresp_t resp;
|
||||
const int val = readnatpmpresponseorretry( &nat->natpmp, &resp );
|
||||
logVal( "readnatpmpresponseorretry", val );
|
||||
if( val >= 0 ) {
|
||||
tr_inf( KEY "port %d has been unmapped.", nat->port );
|
||||
nat->state = TR_NATPMP_IDLE;
|
||||
nat->port = -1;
|
||||
nat->isMapped = 0;
|
||||
} else if( val != NATPMP_TRYAGAIN ) {
|
||||
nat->state = TR_NATPMP_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
if( nat->state == TR_NATPMP_IDLE )
|
||||
{
|
||||
if( isEnabled && !nat->isMapped )
|
||||
nat->state = TR_NATPMP_SEND_MAP;
|
||||
|
||||
else if( nat->isMapped && time(NULL) <= nat->renewTime )
|
||||
nat->state = TR_NATPMP_SEND_MAP;
|
||||
}
|
||||
|
||||
if( nat->state == TR_NATPMP_SEND_MAP )
|
||||
{
|
||||
const int val = sendnewportmappingrequest( &nat->natpmp, NATPMP_PROTOCOL_TCP, port, port, LIFETIME_SECS );
|
||||
logVal( "sendnewportmappingrequest", val );
|
||||
nat->state = val < 0 ? TR_NATPMP_ERR : TR_NATPMP_RECV_MAP;
|
||||
}
|
||||
|
||||
if( nat->state == TR_NATPMP_RECV_MAP )
|
||||
{
|
||||
natpmpresp_t resp;
|
||||
const int val = readnatpmpresponseorretry( &nat->natpmp, &resp );
|
||||
logVal( "readnatpmpresponseorretry", val );
|
||||
if( val >= 0 ) {
|
||||
nat->state = TR_NATPMP_IDLE;
|
||||
nat->isMapped = 1;
|
||||
nat->renewTime = time( NULL ) + LIFETIME_SECS;
|
||||
nat->port = resp.newportmapping.privateport;
|
||||
tr_inf( KEY "port %d mapped successfully", nat->port );
|
||||
} else if( val != NATPMP_TRYAGAIN ) {
|
||||
nat->state = TR_NATPMP_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
if( nat->state == TR_NATPMP_ERR )
|
||||
ret = TR_NAT_TRAVERSAL_ERROR;
|
||||
else if( ( nat->state == TR_NATPMP_IDLE ) && ( nat->isMapped ) )
|
||||
ret = TR_NAT_TRAVERSAL_MAPPED;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch( pmp->state )
|
||||
{
|
||||
case PMP_STATE_IDLE:
|
||||
case PMP_STATE_ADDING:
|
||||
case PMP_STATE_DELETING:
|
||||
ret = TR_NAT_TRAVERSAL_MAPPING;
|
||||
break;
|
||||
case PMP_STATE_FAILED:
|
||||
case PMP_STATE_TMPFAIL:
|
||||
ret = TR_NAT_TRAVERSAL_ERROR;
|
||||
break;
|
||||
case PMP_STATE_NOBODYHOME:
|
||||
ret = TR_NAT_TRAVERSAL_NOTFOUND;
|
||||
break;
|
||||
case PMP_STATE_MAPPED:
|
||||
default:
|
||||
/* if pmp->state is PMP_STATE_MAPPED then pmp->mapped
|
||||
should be true */
|
||||
assert( 0 );
|
||||
ret = TR_NAT_TRAVERSAL_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
else if( ( nat->state == TR_NATPMP_IDLE ) && ( !nat->isMapped ) )
|
||||
ret = TR_NAT_TRAVERSAL_UNMAPPED;
|
||||
else if( ( nat->state == TR_NATPMP_SEND_MAP ) || ( nat->state == TR_NATPMP_RECV_MAP ) )
|
||||
ret = TR_NAT_TRAVERSAL_MAPPING;
|
||||
else if( ( nat->state == TR_NATPMP_SEND_UNMAP ) || ( nat->state == TR_NATPMP_RECV_UNMAP ) )
|
||||
ret = TR_NAT_TRAVERSAL_UNMAPPING;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
tr_natpmpForwardPort( tr_natpmp * pmp, int port )
|
||||
{
|
||||
tr_inf( "nat-pmp set port %i", port );
|
||||
pmp->newport = port;
|
||||
}
|
||||
|
||||
void
|
||||
tr_natpmpRemoveForwarding( tr_natpmp * pmp )
|
||||
{
|
||||
tr_inf( "nat-pmp unset port" );
|
||||
pmp->newport = -1;
|
||||
unmap( pmp );
|
||||
}
|
||||
|
||||
void
|
||||
tr_natpmpClose( tr_natpmp * pmp )
|
||||
{
|
||||
/* try to send at least one delete request if we have a port mapping */
|
||||
tr_natpmpStop( pmp );
|
||||
tr_natpmpPulse( pmp, NULL );
|
||||
|
||||
killreq( &pmp->req );
|
||||
free( pmp );
|
||||
}
|
||||
|
||||
void
|
||||
tr_natpmpPulse( tr_natpmp * pmp, int * publicPort )
|
||||
{
|
||||
if( 0 <= pmp->mcastfd )
|
||||
{
|
||||
mcastpulse( pmp );
|
||||
}
|
||||
|
||||
if( NULL != publicPort )
|
||||
{
|
||||
*publicPort = -1;
|
||||
}
|
||||
|
||||
if( pmp->active || PMP_STATE_DELETING == pmp->state )
|
||||
{
|
||||
switch( pmp->state )
|
||||
{
|
||||
case PMP_STATE_IDLE:
|
||||
case PMP_STATE_TMPFAIL:
|
||||
if( 0 < pmp->newport )
|
||||
{
|
||||
tr_dbg( "nat-pmp state %s -> add with port %i",
|
||||
( PMP_STATE_IDLE == pmp->state ? "idle" : "err" ),
|
||||
pmp->newport );
|
||||
pmp->state = PMP_STATE_ADDING;
|
||||
}
|
||||
break;
|
||||
|
||||
case PMP_STATE_ADDING:
|
||||
if( NULL == pmp->req )
|
||||
{
|
||||
if( 0 >= pmp->newport )
|
||||
{
|
||||
tr_dbg( "nat-pmp state add -> idle, no port" );
|
||||
pmp->state = PMP_STATE_IDLE;
|
||||
}
|
||||
else if( INADDR_NONE == pmp->dest.s_addr )
|
||||
{
|
||||
tr_dbg( "nat-pmp state add -> fail, no default route" );
|
||||
pmp->state = PMP_STATE_FAILED;
|
||||
}
|
||||
else
|
||||
{
|
||||
pmp->req = newreq( 1, pmp->dest, pmp->newport );
|
||||
if( NULL == pmp->req )
|
||||
{
|
||||
pmp->state = PMP_STATE_FAILED;
|
||||
tr_dbg( "nat-pmp state add -> fail on req init" );
|
||||
}
|
||||
}
|
||||
}
|
||||
if( PMP_STATE_ADDING == pmp->state )
|
||||
{
|
||||
switch( pulsereq( pmp ) )
|
||||
{
|
||||
case TR_NET_ERROR:
|
||||
if( pmp->req->nobodyhome )
|
||||
{
|
||||
pmp->state = PMP_STATE_NOBODYHOME;
|
||||
tr_dbg( "nat-pmp state add -> nobodyhome on pulse" );
|
||||
}
|
||||
else if( pmp->req->tmpfail )
|
||||
{
|
||||
pmp->state = PMP_STATE_TMPFAIL;
|
||||
tr_dbg( "nat-pmp state add -> err on pulse" );
|
||||
if( pmp->req->askport == pmp->newport )
|
||||
{
|
||||
pmp->newport = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pmp->state = PMP_STATE_FAILED;
|
||||
tr_dbg( "nat-pmp state add -> fail on pulse" );
|
||||
}
|
||||
killreq( &pmp->req );
|
||||
break;
|
||||
case TR_NET_OK:
|
||||
pmp->mappedport = pmp->req->gotport;
|
||||
if( pmp->mappedport != pmp->newport &&
|
||||
pmp->newport == pmp->req->askport )
|
||||
{
|
||||
pmp->newport = pmp->req->gotport;
|
||||
}
|
||||
killreq( &pmp->req );
|
||||
pmp->state = PMP_STATE_MAPPED;
|
||||
pmp->mapped = 1;
|
||||
tr_dbg( "nat-pmp state add -> mapped with port %i",
|
||||
pmp->mappedport);
|
||||
tr_inf( "nat-pmp mapped port %i", pmp->mappedport );
|
||||
if( NULL != publicPort )
|
||||
{
|
||||
*publicPort = pmp->mappedport;
|
||||
}
|
||||
break;
|
||||
case TR_NET_WAIT:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PMP_STATE_DELETING:
|
||||
if( NULL == pmp->req )
|
||||
{
|
||||
assert( 0 < pmp->mappedport );
|
||||
pmp->req = newreq( 0, pmp->dest, pmp->mappedport );
|
||||
if( NULL == pmp->req )
|
||||
{
|
||||
pmp->state = PMP_STATE_FAILED;
|
||||
tr_dbg( "nat-pmp state del -> fail on req init" );
|
||||
}
|
||||
}
|
||||
if( PMP_STATE_DELETING == pmp->state )
|
||||
{
|
||||
switch( pulsereq( pmp ) )
|
||||
{
|
||||
case TR_NET_ERROR:
|
||||
if( pmp->req->nobodyhome )
|
||||
{
|
||||
pmp->mapped = 0;
|
||||
pmp->state = PMP_STATE_NOBODYHOME;
|
||||
tr_dbg( "nat-pmp state del -> nobodyhome on pulse" );
|
||||
}
|
||||
else if( pmp->req->tmpfail )
|
||||
{
|
||||
pmp->mapped = 0;
|
||||
pmp->state = PMP_STATE_TMPFAIL;
|
||||
tr_dbg( "nat-pmp state del -> err on pulse" );
|
||||
pmp->mappedport = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
pmp->state = PMP_STATE_FAILED;
|
||||
tr_dbg( "nat-pmp state del -> fail on pulse" );
|
||||
}
|
||||
killreq( &pmp->req );
|
||||
break;
|
||||
case TR_NET_OK:
|
||||
tr_dbg( "nat-pmp state del -> idle with port %i",
|
||||
pmp->req->askport);
|
||||
tr_inf( "nat-pmp unmapped port %i",
|
||||
pmp->req->askport );
|
||||
pmp->mapped = 0;
|
||||
pmp->mappedport = -1;
|
||||
killreq( &pmp->req );
|
||||
pmp->state = PMP_STATE_IDLE;
|
||||
break;
|
||||
case TR_NET_WAIT:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PMP_STATE_MAPPED:
|
||||
if( pmp->newport != pmp->mappedport )
|
||||
{
|
||||
tr_dbg( "nat-pmp state mapped -> del, port from %i to %i",
|
||||
pmp->mappedport, pmp->newport );
|
||||
pmp->state = PMP_STATE_DELETING;
|
||||
}
|
||||
else if( tr_date() > pmp->renew )
|
||||
{
|
||||
pmp->state = PMP_STATE_ADDING;
|
||||
tr_dbg( "nat-pmp state mapped -> add for renewal" );
|
||||
}
|
||||
break;
|
||||
|
||||
case PMP_STATE_FAILED:
|
||||
case PMP_STATE_NOBODYHOME:
|
||||
break;
|
||||
|
||||
default:
|
||||
assert( 0 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
unmap( tr_natpmp * pmp )
|
||||
{
|
||||
switch( pmp->state )
|
||||
{
|
||||
case PMP_STATE_IDLE:
|
||||
break;
|
||||
case PMP_STATE_ADDING:
|
||||
if( NULL == pmp->req )
|
||||
{
|
||||
pmp->state = PMP_STATE_IDLE;
|
||||
tr_dbg( "nat-pmp state add -> idle" );
|
||||
}
|
||||
else
|
||||
{
|
||||
pmp->mappedport = pmp->req->gotport;
|
||||
killreq( &pmp->req );
|
||||
pmp->state = PMP_STATE_DELETING;
|
||||
tr_dbg( "nat-pmp state add -> del" );
|
||||
}
|
||||
break;
|
||||
case PMP_STATE_DELETING:
|
||||
break;
|
||||
case PMP_STATE_MAPPED:
|
||||
pmp->state = PMP_STATE_DELETING;
|
||||
tr_dbg( "nat-pmp state mapped -> del" );
|
||||
break;
|
||||
case PMP_STATE_FAILED:
|
||||
case PMP_STATE_NOBODYHOME:
|
||||
case PMP_STATE_TMPFAIL:
|
||||
break;
|
||||
default:
|
||||
assert( 0 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
checktime( tr_natpmp_uptime_t * uptime, uint32_t cursecs )
|
||||
{
|
||||
time_t now;
|
||||
int ret;
|
||||
uint32_t estimated;
|
||||
|
||||
now = time( NULL );
|
||||
ret = 0;
|
||||
if( 0 < uptime->when )
|
||||
{
|
||||
estimated = ( ( now - uptime->when ) * 7 / 8 ) + uptime->uptime;
|
||||
if( estimated > cursecs )
|
||||
{
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
|
||||
uptime->when = now;
|
||||
uptime->uptime = cursecs;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
killsock( int * fd )
|
||||
{
|
||||
if( 0 <= *fd )
|
||||
{
|
||||
tr_netClose( *fd );
|
||||
*fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static tr_natpmp_req_t *
|
||||
newreq( int adding, struct in_addr addr, int port )
|
||||
{
|
||||
tr_natpmp_req_t * ret;
|
||||
|
||||
ret = calloc( 1, sizeof( *ret ) );
|
||||
if( NULL == ret )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->fd = tr_netOpenUDP( &addr, htons( PMP_PORT ), 1 );
|
||||
if( 0 > ret->fd )
|
||||
{
|
||||
free( ret );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->adding = adding;
|
||||
ret->askport = port;
|
||||
ret->gotport = port;
|
||||
resetreq( ret );
|
||||
if( sendreq( ret ) )
|
||||
{
|
||||
killreq( &ret );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
killreq( tr_natpmp_req_t ** req )
|
||||
{
|
||||
if( NULL != *req )
|
||||
{
|
||||
killsock( &(*req)->fd );
|
||||
free( *req );
|
||||
*req = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
resetreq( tr_natpmp_req_t * req )
|
||||
{
|
||||
uint64_t now;
|
||||
|
||||
now = tr_date();
|
||||
req->delay = PMP_INITIAL_DELAY;
|
||||
req->retry = now;
|
||||
req->timeout = now + PMP_TOTAL_DELAY;
|
||||
}
|
||||
|
||||
static tr_tristate_t
|
||||
pulsereq( tr_natpmp * pmp )
|
||||
{
|
||||
tr_natpmp_req_t * req = pmp->req;
|
||||
struct sockaddr_in sin;
|
||||
uint8_t buf[16];
|
||||
int res;
|
||||
uint64_t now;
|
||||
tr_tristate_t ret;
|
||||
tr_natpmp_parse_t parse;
|
||||
|
||||
now = tr_date();
|
||||
/* check for timeout */
|
||||
if( now >= req->timeout )
|
||||
{
|
||||
tr_dbg( "nat-pmp request timed out" );
|
||||
req->nobodyhome = 1;
|
||||
return TR_NET_ERROR;
|
||||
}
|
||||
/* send another request if it's been long enough */
|
||||
if( now >= req->retry && sendreq( req ) )
|
||||
{
|
||||
return TR_NET_ERROR;
|
||||
}
|
||||
|
||||
/* check for incoming packets */
|
||||
res = tr_netRecvFrom( req->fd, buf, sizeof( buf ), &sin );
|
||||
if( TR_NET_BLOCK & res )
|
||||
{
|
||||
return TR_NET_WAIT;
|
||||
}
|
||||
else if( TR_NET_CLOSE & res )
|
||||
{
|
||||
if( ECONNRESET == sockerrno || ECONNREFUSED == sockerrno )
|
||||
{
|
||||
tr_dbg( "nat-pmp not supported by device" );
|
||||
req->nobodyhome = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_inf( "error reading nat-pmp response (%s)", strerror( sockerrno ) );
|
||||
}
|
||||
return TR_NET_ERROR;
|
||||
}
|
||||
|
||||
/* parse the packet */
|
||||
tr_dbg( "nat-pmp read %i byte response", res );
|
||||
ret = parseresponse( buf, res, req->askport, &parse );
|
||||
req->tmpfail = parse.tmpfail;
|
||||
/* check for device reset */
|
||||
if( checktime( &pmp->uptime, parse.seconds ) )
|
||||
{
|
||||
pmp->renew = 0;
|
||||
tr_inf( "detected nat-pmp device reset" );
|
||||
resetreq( req );
|
||||
ret = TR_NET_WAIT;
|
||||
}
|
||||
if( TR_NET_OK == ret && req->adding )
|
||||
{
|
||||
if( req->askport != parse.port )
|
||||
{
|
||||
tr_dbg( "nat-pmp received %i for public port instead of %i",
|
||||
parse.port, req->askport );
|
||||
req->gotport = parse.port;
|
||||
}
|
||||
tr_dbg( "nat-pmp set renew to half of %u", parse.lifetime );
|
||||
pmp->renew = now + ( parse.lifetime / 2 * 1000 );
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
sendreq( tr_natpmp_req_t * req )
|
||||
{
|
||||
uint8_t buf[12];
|
||||
int res;
|
||||
|
||||
memset( buf, 0, sizeof( buf ) );
|
||||
buf[0] = PMP_VERSION;
|
||||
buf[1] = PMP_OPCODE_ADDTCP;
|
||||
PMP_TOBUF16( buf + 4, req->askport );
|
||||
if( req->adding )
|
||||
{
|
||||
PMP_TOBUF16( buf + 6, req->askport );
|
||||
PMP_TOBUF32( buf + 8, PMP_LIFETIME );
|
||||
}
|
||||
|
||||
res = tr_netSend( req->fd, buf, sizeof( buf ) );
|
||||
if( TR_NET_CLOSE & res && EHOSTUNREACH == sockerrno )
|
||||
{
|
||||
res = TR_NET_BLOCK;
|
||||
}
|
||||
if( TR_NET_CLOSE & res )
|
||||
{
|
||||
tr_err( "failed to send nat-pmp request (%s)", strerror( sockerrno ) );
|
||||
return 1;
|
||||
}
|
||||
else if( !( TR_NET_BLOCK & res ) )
|
||||
{
|
||||
/* XXX is it all right to assume the entire thing is written? */
|
||||
req->retry = tr_date() + req->delay;
|
||||
req->delay *= 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mcastsetup()
|
||||
{
|
||||
int fd;
|
||||
struct in_addr addr;
|
||||
|
||||
addr.s_addr = inet_addr( PMP_MCAST_ADDR );
|
||||
fd = tr_netMcastOpen( PMP_PORT, &addr );
|
||||
if( 0 > fd )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
tr_dbg( "nat-pmp create multicast socket %i", fd );
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void
|
||||
mcastpulse( tr_natpmp * pmp )
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
uint8_t buf[16];
|
||||
int res;
|
||||
char dbgstr[INET_ADDRSTRLEN];
|
||||
tr_natpmp_parse_t parse;
|
||||
|
||||
res = tr_netRecvFrom( pmp->mcastfd, buf, sizeof( buf ), &sin );
|
||||
if( TR_NET_BLOCK & res )
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if( TR_NET_CLOSE & res )
|
||||
{
|
||||
tr_err( "error reading nat-pmp multicast message" );
|
||||
killsock( &pmp->mcastfd );
|
||||
return;
|
||||
}
|
||||
|
||||
tr_netNtop( &sin.sin_addr, dbgstr, sizeof( dbgstr ) );
|
||||
tr_dbg( "nat-pmp read %i byte multicast packet from %s", res, dbgstr );
|
||||
|
||||
if( pmp->dest.s_addr != sin.sin_addr.s_addr )
|
||||
{
|
||||
tr_dbg( "nat-pmp ignoring multicast packet from unknown host %s",
|
||||
dbgstr );
|
||||
return;
|
||||
}
|
||||
|
||||
if( TR_NET_OK == parseresponse( buf, res, -1, &parse ) )
|
||||
{
|
||||
if( checktime( &pmp->uptime, parse.seconds ) )
|
||||
{
|
||||
pmp->renew = 0;
|
||||
tr_inf( "detected nat-pmp device reset" );
|
||||
if( NULL != pmp->req )
|
||||
{
|
||||
resetreq( pmp->req );
|
||||
}
|
||||
}
|
||||
if( PMP_STATE_NOBODYHOME == pmp->state )
|
||||
{
|
||||
tr_dbg( "nat-pmp state notfound -> idle" );
|
||||
pmp->state = PMP_STATE_IDLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static tr_tristate_t
|
||||
parseresponse( uint8_t * buf, int len, int port, tr_natpmp_parse_t * parse )
|
||||
{
|
||||
int version, respopcode, opcode, wantedopcode, rescode, privport;
|
||||
|
||||
memset( parse, 0, sizeof( *parse ) );
|
||||
|
||||
if( 8 > len )
|
||||
{
|
||||
tr_err( "read truncated %i byte nat-pmp response packet", len );
|
||||
return TR_NET_ERROR;
|
||||
}
|
||||
|
||||
/* parse the first 8 bytes: version, opcode, and result code */
|
||||
version = buf[0];
|
||||
respopcode = buf[1];
|
||||
opcode = PMP_OPCODE_FROM_RESPONSE( respopcode );
|
||||
wantedopcode = ( 0 < port ? PMP_OPCODE_ADDTCP : PMP_OPCODE_GETIP );
|
||||
rescode = PMP_FROMBUF16( buf + 2 );
|
||||
|
||||
if( PMP_VERSION != version )
|
||||
{
|
||||
tr_err( "unknown nat-pmp version %hhu", buf[0] );
|
||||
return TR_NET_ERROR;
|
||||
}
|
||||
if( !PMP_OPCODE_IS_RESPONSE( respopcode ) )
|
||||
{
|
||||
tr_dbg( "nat-pmp ignoring request packet" );
|
||||
return TR_NET_WAIT;
|
||||
}
|
||||
if( wantedopcode != opcode )
|
||||
{
|
||||
tr_err( "unknown nat-pmp opcode %hhu", opcode );
|
||||
return TR_NET_ERROR;
|
||||
}
|
||||
|
||||
switch( rescode )
|
||||
{
|
||||
case PMP_RESULT_OK:
|
||||
break;
|
||||
case PMP_RESULT_REFUSED:
|
||||
tr_err( "nat-pmp mapping failed: refused/unauthorized/disabled" );
|
||||
parse->tmpfail = 1;
|
||||
return TR_NET_ERROR;
|
||||
case PMP_RESULT_NETDOWN:
|
||||
tr_err( "nat-pmp mapping failed: network down" );
|
||||
parse->tmpfail = 1;
|
||||
return TR_NET_ERROR;
|
||||
case PMP_RESULT_NOMEM:
|
||||
tr_err( "nat-pmp mapping refused: insufficient resources" );
|
||||
parse->tmpfail = 1;
|
||||
return TR_NET_ERROR;
|
||||
default:
|
||||
tr_err( "nat-pmp mapping refused: unknown result code: %hu",
|
||||
rescode );
|
||||
return TR_NET_ERROR;
|
||||
}
|
||||
|
||||
parse->seconds = PMP_FROMBUF32( buf + 4 );
|
||||
if( PMP_OPCODE_ADDTCP == opcode )
|
||||
{
|
||||
if( 16 > len )
|
||||
{
|
||||
tr_err( "read truncated %i byte nat-pmp response packet", len );
|
||||
return TR_NET_ERROR;
|
||||
}
|
||||
privport = PMP_FROMBUF16( buf + 8 );
|
||||
parse->port = PMP_FROMBUF16( buf + 10 );
|
||||
parse->lifetime = PMP_FROMBUF32( buf + 12 );
|
||||
|
||||
if( port != privport )
|
||||
{
|
||||
tr_dbg( "nat-pmp ignoring message for port %i, expected port %i",
|
||||
privport, port );
|
||||
return TR_NET_WAIT;
|
||||
}
|
||||
}
|
||||
|
||||
return TR_NET_OK;
|
||||
}
|
||||
|
|
|
@ -1,41 +1,22 @@
|
|||
/******************************************************************************
|
||||
/*
|
||||
* This file Copyright (C) 2007 Charles Kerr <charles@rebelbase.com>
|
||||
*
|
||||
* 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$
|
||||
*
|
||||
* Copyright (c) 2006 Transmission authors and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
*/
|
||||
|
||||
#ifndef TR_NATPMP_H
|
||||
#define TR_NATPMP_H 1
|
||||
|
||||
typedef struct tr_natpmp tr_natpmp;
|
||||
|
||||
tr_natpmp * tr_natpmpInit();
|
||||
void tr_natpmpStart( tr_natpmp * );
|
||||
void tr_natpmpStop( tr_natpmp * );
|
||||
int tr_natpmpStatus( tr_natpmp * );
|
||||
void tr_natpmpForwardPort( tr_natpmp *, int );
|
||||
void tr_natpmpRemoveForwarding( tr_natpmp * );
|
||||
void tr_natpmpPulse( tr_natpmp *, int * );
|
||||
tr_natpmp * tr_natpmpInit( void );
|
||||
void tr_natpmpClose( tr_natpmp * );
|
||||
|
||||
#define PMP_MCAST_ADDR "224.0.0.1"
|
||||
int tr_natpmpPulse( tr_natpmp *, int port, int isEnabled );
|
||||
|
||||
#endif
|
||||
|
|
|
@ -145,43 +145,6 @@ tr_netOpenTCP( const struct in_addr * addr, tr_port_t port, int priority )
|
|||
return tr_netOpen( addr, port, SOCK_STREAM, priority );
|
||||
}
|
||||
|
||||
int
|
||||
tr_netOpenUDP( const struct in_addr * addr, tr_port_t port, int priority )
|
||||
{
|
||||
return tr_netOpen( addr, port, SOCK_DGRAM, priority );
|
||||
}
|
||||
|
||||
#ifdef IP_ADD_MEMBERSHIP
|
||||
int tr_netMcastOpen( int port, const struct in_addr * addr )
|
||||
{
|
||||
int fd;
|
||||
struct ip_mreq req;
|
||||
|
||||
fd = tr_netBindUDP( port );
|
||||
if( 0 > fd )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset( &req, 0, sizeof( req ) );
|
||||
req.imr_multiaddr.s_addr = addr->s_addr;
|
||||
req.imr_interface.s_addr = htonl( INADDR_ANY );
|
||||
if( setsockopt( fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&req, sizeof ( req ) ) )
|
||||
{
|
||||
tr_err( "Couldn't join multicast group (%s)", strerror( sockerrno ) );
|
||||
tr_netClose( fd );
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
#else /* IP_ADD_MEMBERSHIP */
|
||||
int tr_netMcastOpen( int port UNUSED, const struct in_addr * addr UNUSED )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif /* IP_ADD_MEMBERSHIP */
|
||||
|
||||
static int
|
||||
tr_netBind( int port, int type )
|
||||
{
|
||||
|
@ -201,27 +164,9 @@ tr_netBind( int port, int type )
|
|||
setsockopt( s, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, sizeof( optval ) );
|
||||
#endif
|
||||
|
||||
#ifdef SO_REUSEPORT
|
||||
if( SOCK_DGRAM == type )
|
||||
{
|
||||
optval = 1;
|
||||
setsockopt( s, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof( optval ) );
|
||||
}
|
||||
#endif
|
||||
|
||||
memset( &sock, 0, sizeof( sock ) );
|
||||
sock.sin_family = AF_INET;
|
||||
|
||||
/* Leopard closes a SO_REUSEADDR + INADDR_ANY hole, so we can't
|
||||
* use INADDR_ANY when binding for nat-pmp. For details, refer to
|
||||
* http://www.uwsg.indiana.edu/hypermail/linux/kernel/9902.1/0828.html .
|
||||
* This can probably be done cleaner, but since we're only using SOCK_DGRAM
|
||||
* for nat-pmp, this quick fix should work. */
|
||||
if ( SOCK_DGRAM == type )
|
||||
sock.sin_addr.s_addr = inet_addr( PMP_MCAST_ADDR );
|
||||
else
|
||||
sock.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
sock.sin_addr.s_addr = INADDR_ANY;
|
||||
sock.sin_port = htons( port );
|
||||
|
||||
if( bind( s, (struct sockaddr *) &sock,
|
||||
|
@ -241,13 +186,6 @@ tr_netBindTCP( int port )
|
|||
return tr_netBind( port, SOCK_STREAM );
|
||||
}
|
||||
|
||||
int
|
||||
tr_netBindUDP( int port )
|
||||
{
|
||||
return tr_netBind( port, SOCK_DGRAM );
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
tr_netAccept( int b, struct in_addr * addr, tr_port_t * port )
|
||||
{
|
||||
|
|
|
@ -69,7 +69,6 @@ int tr_netResolve( const char *, struct in_addr * );
|
|||
**********************************************************************/
|
||||
int tr_netOpenTCP ( const struct in_addr * addr, tr_port_t port, int priority );
|
||||
int tr_netOpenUDP ( const struct in_addr * addr, tr_port_t port, int priority );
|
||||
int tr_netMcastOpen( int port, const struct in_addr * addr );
|
||||
int tr_netBindTCP ( int port );
|
||||
int tr_netBindUDP ( int port );
|
||||
int tr_netAccept ( int s, struct in_addr *, tr_port_t * );
|
||||
|
|
|
@ -443,381 +443,3 @@ tr_getTorrentsDirectory( void )
|
|||
init = 1;
|
||||
return buf;
|
||||
}
|
||||
|
||||
/***
|
||||
**** SOCKETS
|
||||
***/
|
||||
|
||||
#ifdef BSD
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h> /* struct in_addr */
|
||||
#include <sys/sysctl.h>
|
||||
#include <net/route.h>
|
||||
|
||||
static uint8_t *
|
||||
getroute( int * buflen );
|
||||
static int
|
||||
parseroutes( uint8_t * buf, int len, struct in_addr * addr );
|
||||
|
||||
int
|
||||
tr_getDefaultRoute( struct in_addr * addr )
|
||||
{
|
||||
uint8_t * buf;
|
||||
int len;
|
||||
|
||||
buf = getroute( &len );
|
||||
if( NULL == buf )
|
||||
{
|
||||
tr_err( "failed to get default route (BSD)" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
len = parseroutes( buf, len, addr );
|
||||
free( buf );
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
#ifndef SA_SIZE
|
||||
#define ROUNDUP( a, size ) \
|
||||
( ( (a) & ( (size) - 1 ) ) ? ( 1 + ( (a) | ( (size) - 1 ) ) ) : (a) )
|
||||
#define SA_SIZE( sap ) \
|
||||
( sap->sa_len ? ROUNDUP( (sap)->sa_len, sizeof( u_long ) ) : \
|
||||
sizeof( u_long ) )
|
||||
#endif /* !SA_SIZE */
|
||||
#define NEXT_SA( sap ) \
|
||||
(struct sockaddr *) ( (caddr_t) (sap) + ( SA_SIZE( (sap) ) ) )
|
||||
|
||||
static uint8_t *
|
||||
getroute( int * buflen )
|
||||
{
|
||||
int mib[6];
|
||||
size_t len;
|
||||
uint8_t * buf;
|
||||
|
||||
mib[0] = CTL_NET;
|
||||
mib[1] = PF_ROUTE;
|
||||
mib[2] = 0;
|
||||
mib[3] = AF_INET;
|
||||
mib[4] = NET_RT_FLAGS;
|
||||
mib[5] = RTF_GATEWAY;
|
||||
|
||||
if( sysctl( mib, 6, NULL, &len, NULL, 0 ) )
|
||||
{
|
||||
if( ENOENT != errno )
|
||||
{
|
||||
tr_err( "sysctl net.route.0.inet.flags.gateway failed (%s)",
|
||||
strerror( sockerrno ) );
|
||||
}
|
||||
*buflen = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buf = malloc( len );
|
||||
if( NULL == buf )
|
||||
{
|
||||
*buflen = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if( sysctl( mib, 6, buf, &len, NULL, 0 ) )
|
||||
{
|
||||
tr_err( "sysctl net.route.0.inet.flags.gateway failed (%s)",
|
||||
strerror( sockerrno ) );
|
||||
free( buf );
|
||||
*buflen = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*buflen = len;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int
|
||||
parseroutes( uint8_t * buf, int len, struct in_addr * addr )
|
||||
{
|
||||
uint8_t * end;
|
||||
struct rt_msghdr * rtm;
|
||||
struct sockaddr * sa;
|
||||
struct sockaddr_in * sin;
|
||||
int ii;
|
||||
struct in_addr dest, gw;
|
||||
|
||||
end = buf + len;
|
||||
while( end > buf + sizeof( *rtm ) )
|
||||
{
|
||||
rtm = (struct rt_msghdr *) buf;
|
||||
buf += rtm->rtm_msglen;
|
||||
if( end >= buf )
|
||||
{
|
||||
dest.s_addr = INADDR_NONE;
|
||||
gw.s_addr = INADDR_NONE;
|
||||
sa = (struct sockaddr *) ( rtm + 1 );
|
||||
|
||||
for( ii = 0; ii < RTAX_MAX && (uint8_t *) sa < buf; ii++ )
|
||||
{
|
||||
if( buf < (uint8_t *) NEXT_SA( sa ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if( rtm->rtm_addrs & ( 1 << ii ) )
|
||||
{
|
||||
if( AF_INET == sa->sa_family )
|
||||
{
|
||||
sin = (struct sockaddr_in *) sa;
|
||||
switch( ii )
|
||||
{
|
||||
case RTAX_DST:
|
||||
dest = sin->sin_addr;
|
||||
break;
|
||||
case RTAX_GATEWAY:
|
||||
gw = sin->sin_addr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
sa = NEXT_SA( sa );
|
||||
}
|
||||
}
|
||||
|
||||
if( INADDR_ANY == dest.s_addr && INADDR_NONE != gw.s_addr )
|
||||
{
|
||||
*addr = gw;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#elif defined( linux ) || defined( __linux ) || defined( __linux__ )
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
#define SEQNUM 195909
|
||||
|
||||
static int
|
||||
getsock( void );
|
||||
static uint8_t *
|
||||
getroute( int fd, unsigned int * buflen );
|
||||
static int
|
||||
parseroutes( uint8_t * buf, unsigned int len, struct in_addr * addr );
|
||||
|
||||
int
|
||||
tr_getDefaultRoute( struct in_addr * addr )
|
||||
{
|
||||
int fd, ret;
|
||||
unsigned int len;
|
||||
uint8_t * buf;
|
||||
|
||||
ret = 1;
|
||||
fd = getsock();
|
||||
if( 0 <= fd )
|
||||
{
|
||||
while( ret )
|
||||
{
|
||||
buf = getroute( fd, &len );
|
||||
if( NULL == buf )
|
||||
{
|
||||
break;
|
||||
}
|
||||
ret = parseroutes( buf, len, addr );
|
||||
free( buf );
|
||||
}
|
||||
close( fd );
|
||||
}
|
||||
|
||||
if( ret )
|
||||
{
|
||||
tr_err( "failed to get default route (Linux)" );
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
getsock( void )
|
||||
{
|
||||
int fd, flags;
|
||||
struct
|
||||
{
|
||||
struct nlmsghdr nlh;
|
||||
struct rtgenmsg rtg;
|
||||
} req;
|
||||
struct sockaddr_nl snl;
|
||||
|
||||
fd = socket( PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE );
|
||||
if( 0 > fd )
|
||||
{
|
||||
tr_err( "failed to create routing socket (%s)", strerror( sockerrno ) );
|
||||
return -1;
|
||||
}
|
||||
|
||||
flags = fcntl( fd, F_GETFL );
|
||||
if( 0 > flags || 0 > fcntl( fd, F_SETFL, O_NONBLOCK | flags ) )
|
||||
{
|
||||
tr_err( "failed to set socket nonblocking (%s)", strerror( sockerrno ) );
|
||||
close( fd );
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset( &snl, 0, sizeof(snl) );
|
||||
snl.nl_family = AF_NETLINK;
|
||||
|
||||
memset( &req, 0, sizeof(req) );
|
||||
req.nlh.nlmsg_len = NLMSG_LENGTH( sizeof( req.rtg ) );
|
||||
req.nlh.nlmsg_type = RTM_GETROUTE;
|
||||
req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
|
||||
req.nlh.nlmsg_seq = SEQNUM;
|
||||
req.nlh.nlmsg_pid = 0;
|
||||
req.rtg.rtgen_family = AF_INET;
|
||||
|
||||
if( 0 > sendto( fd, &req, sizeof( req ), 0,
|
||||
(struct sockaddr *) &snl, sizeof( snl ) ) )
|
||||
{
|
||||
tr_err( "failed to write to routing socket (%s)", strerror( sockerrno ) );
|
||||
close( fd );
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static uint8_t *
|
||||
getroute( int fd, unsigned int * buflen )
|
||||
{
|
||||
void * buf;
|
||||
unsigned int len;
|
||||
ssize_t res;
|
||||
struct sockaddr_nl snl;
|
||||
socklen_t slen;
|
||||
|
||||
len = 8192;
|
||||
buf = calloc( 1, len );
|
||||
if( NULL == buf )
|
||||
{
|
||||
*buflen = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
slen = sizeof( snl );
|
||||
memset( &snl, 0, slen );
|
||||
res = recvfrom( fd, buf, len, 0, (struct sockaddr *) &snl, &slen );
|
||||
if( 0 > res )
|
||||
{
|
||||
if( EAGAIN != sockerrno )
|
||||
{
|
||||
tr_err( "failed to read from routing socket (%s)",
|
||||
strerror( sockerrno ) );
|
||||
}
|
||||
free( buf );
|
||||
*buflen = 0;
|
||||
return NULL;
|
||||
}
|
||||
if( slen < sizeof( snl ) || AF_NETLINK != snl.nl_family )
|
||||
{
|
||||
tr_err( "bad address" );
|
||||
free( buf );
|
||||
*buflen = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if( 0 == snl.nl_pid )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*buflen = res;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int
|
||||
parseroutes( uint8_t * buf, unsigned int len, struct in_addr * addr )
|
||||
{
|
||||
struct nlmsghdr * nlm;
|
||||
struct nlmsgerr * nle;
|
||||
struct rtmsg * rtm;
|
||||
struct rtattr * rta;
|
||||
int rtalen;
|
||||
struct in_addr gw, dst;
|
||||
|
||||
nlm = ( struct nlmsghdr * ) buf;
|
||||
while( NLMSG_OK( nlm, len ) )
|
||||
{
|
||||
gw.s_addr = INADDR_ANY;
|
||||
dst.s_addr = INADDR_ANY;
|
||||
if( NLMSG_ERROR == nlm->nlmsg_type )
|
||||
{
|
||||
nle = (struct nlmsgerr *) NLMSG_DATA( nlm );
|
||||
if( NLMSG_LENGTH( NLMSG_ALIGN( sizeof( struct nlmsgerr ) ) ) >
|
||||
nlm->nlmsg_len )
|
||||
{
|
||||
tr_err( "truncated netlink error" );
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_err( "netlink error (%s)", strerror( nle->error ) );
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else if( RTM_NEWROUTE == nlm->nlmsg_type && SEQNUM == nlm->nlmsg_seq &&
|
||||
getpid() == (pid_t) nlm->nlmsg_pid &&
|
||||
NLMSG_LENGTH( sizeof( struct rtmsg ) ) <= nlm->nlmsg_len )
|
||||
{
|
||||
rtm = NLMSG_DATA( nlm );
|
||||
rta = RTM_RTA( rtm );
|
||||
rtalen = RTM_PAYLOAD( nlm );
|
||||
|
||||
while( RTA_OK( rta, rtalen ) )
|
||||
{
|
||||
if( sizeof( struct in_addr ) <= RTA_PAYLOAD( rta ) )
|
||||
{
|
||||
switch( rta->rta_type )
|
||||
{
|
||||
case RTA_GATEWAY:
|
||||
memcpy( &gw, RTA_DATA( rta ), sizeof( gw ) );
|
||||
break;
|
||||
case RTA_DST:
|
||||
memcpy( &dst, RTA_DATA( rta ), sizeof( dst ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
rta = RTA_NEXT( rta, rtalen );
|
||||
}
|
||||
}
|
||||
|
||||
if( INADDR_NONE != gw.s_addr && INADDR_ANY != gw.s_addr &&
|
||||
INADDR_ANY == dst.s_addr )
|
||||
{
|
||||
*addr = gw;
|
||||
return 0;
|
||||
}
|
||||
|
||||
nlm = NLMSG_NEXT( nlm, len );
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#else /* not BSD or Linux */
|
||||
|
||||
int
|
||||
tr_getDefaultRoute( struct in_addr * addr UNUSED )
|
||||
{
|
||||
tr_inf( "don't know how to get default route on this platform" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -41,8 +41,4 @@ void tr_lockLock ( tr_lock * );
|
|||
void tr_lockUnlock ( tr_lock * );
|
||||
int tr_lockHave ( const tr_lock * );
|
||||
|
||||
struct in_addr; /* forward declaration to calm gcc down */
|
||||
int
|
||||
tr_getDefaultRoute( struct in_addr * addr );
|
||||
|
||||
#endif
|
||||
|
|
|
@ -22,20 +22,15 @@
|
|||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "transmission.h"
|
||||
#include "handshake.h"
|
||||
#include "natpmp.h"
|
||||
#include "net.h"
|
||||
#include "peer-io.h"
|
||||
#include "peer-mgr.h"
|
||||
#include "platform.h"
|
||||
#include "shared.h"
|
||||
#include "trevent.h"
|
||||
#include "upnp.h"
|
||||
|
@ -43,223 +38,62 @@
|
|||
|
||||
struct tr_shared
|
||||
{
|
||||
tr_handle * h;
|
||||
tr_timer * pulseTimer;
|
||||
tr_handle * h;
|
||||
tr_timer * pulseTimer;
|
||||
|
||||
/* Incoming connections */
|
||||
int publicPort;
|
||||
int bindPort;
|
||||
int bindSocket;
|
||||
|
||||
/* NAT-PMP/UPnP */
|
||||
/* port forwarding */
|
||||
int isEnabled;
|
||||
int publicPort;
|
||||
tr_nat_traversal_status natStatus;
|
||||
tr_upnp * upnp;
|
||||
tr_natpmp * natpmp;
|
||||
tr_upnp * upnp;
|
||||
|
||||
int isShuttingDown;
|
||||
};
|
||||
|
||||
/***********************************************************************
|
||||
* Local prototypes
|
||||
**********************************************************************/
|
||||
static int SharedLoop( void * );
|
||||
static void SetPublicPort( tr_shared *, int );
|
||||
static void AcceptPeers( tr_shared * );
|
||||
#define NATKEY "Port Mapping: "
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
/***********************************************************************
|
||||
* tr_sharedInit
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
tr_shared * tr_sharedInit( tr_handle * h )
|
||||
static const char*
|
||||
getNatStateStr( int state )
|
||||
{
|
||||
tr_shared * s = calloc( 1, sizeof( tr_shared ) );
|
||||
|
||||
s->h = h;
|
||||
s->publicPort = -1;
|
||||
s->bindPort = -1;
|
||||
s->bindSocket = -1;
|
||||
s->natpmp = tr_natpmpInit();
|
||||
s->upnp = tr_upnpInit();
|
||||
s->pulseTimer = tr_timerNew( h, SharedLoop, s, 500 );
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_sharedClose
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
void tr_sharedClose( tr_shared * s )
|
||||
{
|
||||
tr_timerFree( &s->pulseTimer );
|
||||
|
||||
tr_netClose( s->bindSocket );
|
||||
tr_natpmpClose( s->natpmp );
|
||||
tr_upnpClose( s->upnp );
|
||||
free( s );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_sharedSetPort
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
void tr_sharedSetPort( tr_shared * s, int port )
|
||||
{
|
||||
#ifdef BEOS_NETSERVER
|
||||
/* BeOS net_server seems to be unable to set incoming connections
|
||||
* to non-blocking. Too bad. */
|
||||
return;
|
||||
#endif
|
||||
|
||||
tr_globalLock( s->h );
|
||||
|
||||
if( port == s->bindPort )
|
||||
switch( state )
|
||||
{
|
||||
tr_globalUnlock( s->h );
|
||||
return;
|
||||
}
|
||||
s->bindPort = port;
|
||||
|
||||
/* Close the previous accept socket, if any */
|
||||
if( s->bindSocket > -1 )
|
||||
{
|
||||
tr_netClose( s->bindSocket );
|
||||
case TR_NAT_TRAVERSAL_MAPPING: return "mapping";
|
||||
case TR_NAT_TRAVERSAL_MAPPED: return "mapped";
|
||||
case TR_NAT_TRAVERSAL_UNMAPPING: return "unmapping";
|
||||
case TR_NAT_TRAVERSAL_UNMAPPED: return "unmapped";
|
||||
case TR_NAT_TRAVERSAL_ERROR: return "error";
|
||||
}
|
||||
|
||||
/* Create the new one */
|
||||
s->bindSocket = tr_netBindTCP( port );
|
||||
|
||||
/* Notify the trackers */
|
||||
SetPublicPort( s, port );
|
||||
|
||||
/* XXX should handle failure here in a better way */
|
||||
if( s->bindSocket < 0 )
|
||||
{
|
||||
/* Remove the forwarding for the old port */
|
||||
tr_natpmpRemoveForwarding( s->natpmp );
|
||||
tr_upnpRemoveForwarding( s->upnp );
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_inf( "Bound listening port %d", port );
|
||||
listen( s->bindSocket, 5 );
|
||||
/* Forward the new port */
|
||||
tr_natpmpForwardPort( s->natpmp, port );
|
||||
tr_upnpForwardPort( s->upnp, port );
|
||||
}
|
||||
|
||||
tr_globalUnlock( s->h );
|
||||
return "notfound";
|
||||
}
|
||||
|
||||
int
|
||||
tr_sharedGetPublicPort( const tr_shared * s )
|
||||
{
|
||||
return s->publicPort;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* tr_sharedTraversalEnable, tr_sharedTraversalStatus
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
void tr_sharedTraversalEnable( tr_shared * s, int enable )
|
||||
{
|
||||
if( enable )
|
||||
{
|
||||
tr_natpmpStart( s->natpmp );
|
||||
tr_upnpStart( s->upnp );
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_natpmpStop( s->natpmp );
|
||||
tr_upnpStop( s->upnp );
|
||||
}
|
||||
}
|
||||
|
||||
int tr_sharedTraversalStatus( const tr_shared * s )
|
||||
{
|
||||
const int statuses[] = {
|
||||
TR_NAT_TRAVERSAL_MAPPED,
|
||||
TR_NAT_TRAVERSAL_MAPPING,
|
||||
TR_NAT_TRAVERSAL_UNMAPPING,
|
||||
TR_NAT_TRAVERSAL_ERROR,
|
||||
TR_NAT_TRAVERSAL_NOTFOUND,
|
||||
TR_NAT_TRAVERSAL_DISABLED,
|
||||
-1,
|
||||
};
|
||||
int natpmp, upnp, ii;
|
||||
|
||||
natpmp = tr_natpmpStatus( s->natpmp );
|
||||
upnp = tr_upnpStatus( s->upnp );
|
||||
|
||||
for( ii = 0; 0 <= statuses[ii]; ii++ )
|
||||
{
|
||||
if( statuses[ii] == natpmp || statuses[ii] == upnp )
|
||||
{
|
||||
return statuses[ii];
|
||||
}
|
||||
}
|
||||
|
||||
assert( 0 );
|
||||
|
||||
return TR_NAT_TRAVERSAL_ERROR;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* Local functions
|
||||
**********************************************************************/
|
||||
|
||||
/***********************************************************************
|
||||
* SharedLoop
|
||||
**********************************************************************/
|
||||
static int
|
||||
SharedLoop( void * vs )
|
||||
{
|
||||
int newPort;
|
||||
tr_shared * s = vs;
|
||||
|
||||
tr_globalLock( s->h );
|
||||
|
||||
/* NAT-PMP and UPnP pulses */
|
||||
newPort = -1;
|
||||
tr_natpmpPulse( s->natpmp, &newPort );
|
||||
if( 0 < newPort && newPort != s->publicPort )
|
||||
SetPublicPort( s, newPort );
|
||||
tr_upnpPulse( s->upnp );
|
||||
|
||||
/* Handle incoming connections */
|
||||
AcceptPeers( s );
|
||||
|
||||
tr_globalUnlock( s->h );
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* SetPublicPort
|
||||
**********************************************************************/
|
||||
static void SetPublicPort( tr_shared * s, int port )
|
||||
{
|
||||
tr_handle * h = s->h;
|
||||
tr_torrent * tor;
|
||||
|
||||
s->publicPort = port;
|
||||
|
||||
for( tor = h->torrentList; tor; tor = tor->next )
|
||||
tr_torrentChangeMyPort( tor );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* AcceptPeers
|
||||
***********************************************************************
|
||||
* Check incoming connections and add the peers to our local list
|
||||
**********************************************************************/
|
||||
|
||||
static void
|
||||
AcceptPeers( tr_shared * s )
|
||||
natPulse( tr_shared * s )
|
||||
{
|
||||
tr_nat_traversal_status status;
|
||||
const int port = s->publicPort;
|
||||
const int isEnabled = s->isEnabled && !s->isShuttingDown;
|
||||
|
||||
status = tr_natpmpPulse( s->natpmp, port, isEnabled );
|
||||
if( status == TR_NAT_TRAVERSAL_ERROR )
|
||||
status = tr_upnpPulse( s->upnp, port, isEnabled );
|
||||
if( status != s->natStatus ) {
|
||||
tr_inf( NATKEY "mapping state changed from '%s' to '%s'", getNatStateStr(s->natStatus), getNatStateStr(status) );
|
||||
s->natStatus = status;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
checkForIncomingPeers( tr_shared * s )
|
||||
{
|
||||
for( ;; )
|
||||
{
|
||||
|
@ -277,3 +111,86 @@ AcceptPeers( tr_shared * s )
|
|||
tr_peerMgrAddIncoming( s->h->peerMgr, &addr, port, socket );
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
sharedPulse( void * vshared )
|
||||
{
|
||||
int keepPulsing = 1;
|
||||
tr_shared * shared = vshared;
|
||||
|
||||
natPulse( shared );
|
||||
|
||||
if( !shared->isShuttingDown )
|
||||
{
|
||||
checkForIncomingPeers( shared );
|
||||
}
|
||||
else if( ( shared->natStatus == TR_NAT_TRAVERSAL_ERROR ) || ( shared->natStatus == TR_NAT_TRAVERSAL_UNMAPPED ) )
|
||||
{
|
||||
tr_dbg( NATKEY "port mapping shut down" );
|
||||
shared->h->shared = NULL;
|
||||
tr_netClose( shared->bindSocket );
|
||||
tr_natpmpClose( shared->natpmp );
|
||||
tr_upnpClose( shared->upnp );
|
||||
tr_free( shared );
|
||||
keepPulsing = 0;
|
||||
}
|
||||
|
||||
return keepPulsing;
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
tr_shared *
|
||||
tr_sharedInit( tr_handle * h )
|
||||
{
|
||||
tr_shared * s = tr_new0( tr_shared, 1 );
|
||||
|
||||
s->h = h;
|
||||
s->publicPort = -1;
|
||||
s->bindPort = -1;
|
||||
s->bindSocket = -1;
|
||||
s->natpmp = tr_natpmpInit();
|
||||
s->upnp = tr_upnpInit();
|
||||
s->pulseTimer = tr_timerNew( h, sharedPulse, s, 500 );
|
||||
s->isEnabled = 0;
|
||||
s->natStatus = TR_NAT_TRAVERSAL_UNMAPPED;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void
|
||||
tr_sharedShuttingDown( tr_shared * s )
|
||||
{
|
||||
s->isShuttingDown = 1;
|
||||
}
|
||||
|
||||
void
|
||||
tr_sharedSetPort( tr_shared * s, int port )
|
||||
{
|
||||
tr_torrent * tor;
|
||||
|
||||
s->publicPort = port;
|
||||
|
||||
for( tor = s->h->torrentList; tor; tor = tor->next )
|
||||
tr_torrentChangeMyPort( tor );
|
||||
}
|
||||
|
||||
int
|
||||
tr_sharedGetPublicPort( const tr_shared * s )
|
||||
{
|
||||
return s->publicPort;
|
||||
}
|
||||
|
||||
void
|
||||
tr_sharedTraversalEnable( tr_shared * s, int isEnabled )
|
||||
{
|
||||
s->isEnabled = isEnabled;
|
||||
}
|
||||
|
||||
int
|
||||
tr_sharedTraversalStatus( const tr_shared * s )
|
||||
{
|
||||
return s->natStatus;
|
||||
}
|
||||
|
|
|
@ -29,33 +29,11 @@
|
|||
|
||||
typedef struct tr_shared tr_shared;
|
||||
|
||||
/***********************************************************************
|
||||
* tr_sharedInit, tr_sharedClose
|
||||
***********************************************************************
|
||||
* Starts / stops a thread to handle running things that are shared
|
||||
* among the torrents: NAT-PMP/UPnP, incoming connections, peer choking
|
||||
**********************************************************************/
|
||||
tr_shared * tr_sharedInit ( tr_handle * );
|
||||
void tr_sharedClose ( tr_shared * );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_sharedSetPort
|
||||
***********************************************************************
|
||||
* Changes the port for incoming connections. tr_sharedGetPublicPort
|
||||
* should be called with the shared lock held.
|
||||
**********************************************************************/
|
||||
void tr_sharedSetPort ( tr_shared *, int port );
|
||||
int tr_sharedGetPublicPort ( const tr_shared * s );
|
||||
|
||||
/***********************************************************************
|
||||
* tr_sharedTraversalEnable, tr_sharedTraversalStatus
|
||||
***********************************************************************
|
||||
* Enables/disables and retrieves the status of NAT traversal. Should
|
||||
* be called with the shared lock held.
|
||||
**********************************************************************/
|
||||
void tr_sharedTraversalEnable ( tr_shared *, int enable );
|
||||
int tr_sharedTraversalStatus ( const tr_shared * );
|
||||
|
||||
tr_shared* tr_sharedInit ( tr_handle * );
|
||||
void tr_sharedShuttingDown ( tr_shared * );
|
||||
void tr_sharedSetPort ( tr_shared *, int publicPort );
|
||||
void tr_sharedTraversalEnable ( tr_shared *, int isEnabled );
|
||||
int tr_sharedGetPublicPort ( const tr_shared * s );
|
||||
int tr_sharedTraversalStatus ( const tr_shared * );
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -324,6 +324,7 @@ tr_closeImpl( void * vh )
|
|||
tr_handle * h = vh;
|
||||
tr_torrent * t;
|
||||
|
||||
tr_sharedShuttingDown( h->shared );
|
||||
tr_trackerShuttingDown( h );
|
||||
|
||||
for( t=h->torrentList; t!=NULL; t=t->next )
|
||||
|
@ -334,13 +335,6 @@ tr_closeImpl( void * vh )
|
|||
tr_rcClose( h->upload );
|
||||
tr_rcClose( h->download );
|
||||
|
||||
tr_natTraversalEnable( h, 0 );
|
||||
while( tr_handleStatus( h )->natTraversalStatus != TR_NAT_TRAVERSAL_DISABLED )
|
||||
tr_wait( 100 );
|
||||
|
||||
tr_sharedClose( h->shared );
|
||||
tr_fdClose();
|
||||
|
||||
h->isClosed = TRUE;
|
||||
}
|
||||
|
||||
|
@ -366,6 +360,7 @@ tr_close( tr_handle * h )
|
|||
while( h->events && !deadlineReached( deadline ) )
|
||||
tr_wait( 100 );
|
||||
|
||||
tr_fdClose( );
|
||||
tr_statsClose( h );
|
||||
tr_lockFree( h->lock );
|
||||
free( h->tag );
|
||||
|
|
|
@ -750,20 +750,24 @@ struct tr_msg_list
|
|||
uint8_t level;
|
||||
time_t when;
|
||||
char * message;
|
||||
const char * file;
|
||||
int line;
|
||||
struct tr_msg_list * next;
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TR_NAT_TRAVERSAL_MAPPING,
|
||||
TR_NAT_TRAVERSAL_MAPPED,
|
||||
TR_NAT_TRAVERSAL_UNMAPPING,
|
||||
TR_NAT_TRAVERSAL_UNMAPPED,
|
||||
TR_NAT_TRAVERSAL_ERROR,
|
||||
}
|
||||
tr_nat_traversal_status;
|
||||
|
||||
struct tr_handle_status
|
||||
{
|
||||
#define TR_NAT_TRAVERSAL_MAPPING 1
|
||||
#define TR_NAT_TRAVERSAL_MAPPED 2
|
||||
#define TR_NAT_TRAVERSAL_NOTFOUND 3
|
||||
#define TR_NAT_TRAVERSAL_ERROR 4
|
||||
#define TR_NAT_TRAVERSAL_UNMAPPING 5
|
||||
#define TR_NAT_TRAVERSAL_DISABLED 6
|
||||
#define TR_NAT_TRAVERSAL_IS_DISABLED( st ) \
|
||||
( TR_NAT_TRAVERSAL_DISABLED == (st) || TR_NAT_TRAVERSAL_UNMAPPING == (st) )
|
||||
int natTraversalStatus;
|
||||
tr_nat_traversal_status natTraversalStatus;
|
||||
int publicPort;
|
||||
};
|
||||
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
* $Id$
|
||||
*/
|
||||
|
||||
#include <stdio.h> /* printf */
|
||||
#include <assert.h>
|
||||
#include <stdio.h> /* snprintf */
|
||||
|
||||
#include <miniupnp/miniwget.h>
|
||||
#include <miniupnp/miniupnpc.h>
|
||||
|
@ -18,18 +19,31 @@
|
|||
|
||||
#include "transmission.h"
|
||||
#include "internal.h"
|
||||
#include "shared.h"
|
||||
#include "utils.h"
|
||||
#include "upnp.h"
|
||||
|
||||
#define KEY "Port Mapping (UPNP): "
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TR_UPNP_IDLE,
|
||||
TR_UPNP_ERR,
|
||||
TR_UPNP_DISCOVER,
|
||||
TR_UPNP_MAP,
|
||||
TR_UPNP_UNMAP
|
||||
}
|
||||
tr_upnp_state;
|
||||
|
||||
struct tr_upnp
|
||||
{
|
||||
struct UPNPUrls urls;
|
||||
struct IGDdatas data;
|
||||
int port;
|
||||
char lanaddr[16];
|
||||
unsigned int isForwarding : 1;
|
||||
unsigned int isEnabled : 1;
|
||||
unsigned int isMapped;
|
||||
unsigned int hasDiscovered : 1;
|
||||
tr_upnp_state state;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -47,7 +61,9 @@ tr_upnpInit( void )
|
|||
void
|
||||
tr_upnpClose( tr_upnp * handle )
|
||||
{
|
||||
tr_upnpStop( handle );
|
||||
assert( !handle->isMapped );
|
||||
assert( ( handle->state == TR_UPNP_IDLE ) || ( handle->state == TR_UPNP_ERR ) );
|
||||
|
||||
if( handle->hasDiscovered )
|
||||
FreeUPNPUrls( &handle->urls );
|
||||
tr_free( handle );
|
||||
|
@ -57,91 +73,89 @@ tr_upnpClose( tr_upnp * handle )
|
|||
***
|
||||
**/
|
||||
|
||||
void
|
||||
tr_upnpStart( tr_upnp * handle )
|
||||
int
|
||||
tr_upnpPulse( tr_upnp * handle, int port, int isEnabled )
|
||||
{
|
||||
if( !handle->hasDiscovered )
|
||||
int ret;
|
||||
|
||||
if( handle->state == TR_UPNP_IDLE )
|
||||
{
|
||||
if( !handle->hasDiscovered )
|
||||
handle->state = TR_UPNP_DISCOVER;
|
||||
}
|
||||
|
||||
if( handle->state == TR_UPNP_DISCOVER )
|
||||
{
|
||||
struct UPNPDev * devlist = upnpDiscover( 2000, NULL );
|
||||
if( UPNP_GetValidIGD( devlist, &handle->urls, &handle->data, handle->lanaddr, sizeof(handle->lanaddr))) {
|
||||
tr_dbg( "UPNP: Found Internet Gateway Device '%s'", handle->urls.controlURL );
|
||||
tr_dbg( "UPNP: Local LAN IP Address is '%s'", handle->lanaddr );
|
||||
tr_inf( KEY "found Internet Gateway Device '%s'", handle->urls.controlURL );
|
||||
tr_inf( KEY "local LAN IP Address is '%s'", handle->lanaddr );
|
||||
handle->state = TR_UPNP_IDLE;
|
||||
handle->hasDiscovered = 1;
|
||||
} else {
|
||||
handle->state = TR_UPNP_ERR;
|
||||
}
|
||||
freeUPNPDevlist( devlist );
|
||||
handle->hasDiscovered = 1;
|
||||
}
|
||||
|
||||
handle->isEnabled = 1;
|
||||
if( handle->state == TR_UPNP_IDLE )
|
||||
{
|
||||
if( handle->isMapped && ( !isEnabled || ( handle->port != port ) ) )
|
||||
handle->state = TR_UPNP_UNMAP;
|
||||
}
|
||||
|
||||
if( handle->port >= 0 )
|
||||
if( handle->state == TR_UPNP_UNMAP )
|
||||
{
|
||||
char portStr[16];
|
||||
snprintf( portStr, sizeof(portStr), "%d", handle->port );
|
||||
handle->isForwarding = ( handle->urls.controlURL != NULL ) &&
|
||||
( handle->data.servicetype != NULL ) &&
|
||||
( UPNP_AddPortMapping( handle->urls.controlURL,
|
||||
handle->data.servicetype,
|
||||
portStr, portStr, handle->lanaddr,
|
||||
"Transmission", "TCP" ) );
|
||||
|
||||
tr_inf( "UPNP: Port Forwarding via '%s', service '%s'. (local address: %s:%d)",
|
||||
handle->urls.controlURL, handle->data.servicetype, handle->lanaddr, handle->port );
|
||||
tr_inf( "UPNP: Port Forwarding Enabled? %s", (handle->isForwarding?"Yes":"No") );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
tr_upnpRemoveForwarding ( tr_upnp * handle )
|
||||
{
|
||||
handle->port = -1;
|
||||
|
||||
if( handle->isForwarding )
|
||||
{
|
||||
char portStr[16];
|
||||
snprintf( portStr, sizeof(portStr), "%d", handle->port );
|
||||
|
||||
UPNP_DeletePortMapping( handle->urls.controlURL,
|
||||
handle->data.servicetype,
|
||||
portStr, "TCP" );
|
||||
tr_dbg( "Stopping port forwarding of '%s', service '%s'",
|
||||
tr_dbg( KEY "stopping port forwarding of '%s', service '%s'",
|
||||
handle->urls.controlURL, handle->data.servicetype );
|
||||
|
||||
handle->isForwarding = FALSE;
|
||||
handle->isMapped = 0;
|
||||
handle->state = TR_UPNP_IDLE;
|
||||
handle->port = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
tr_upnpForwardPort( tr_upnp * handle, int publicPort )
|
||||
{
|
||||
tr_upnpRemoveForwarding( handle ); /* remove the old forwarding */
|
||||
|
||||
handle->port = publicPort;
|
||||
|
||||
if( handle->isEnabled )
|
||||
tr_upnpStart( handle );
|
||||
}
|
||||
|
||||
void
|
||||
tr_upnpStop( tr_upnp * handle )
|
||||
{
|
||||
tr_upnpRemoveForwarding( handle );
|
||||
handle->isEnabled = 0;
|
||||
}
|
||||
|
||||
int
|
||||
tr_upnpStatus( tr_upnp * handle )
|
||||
{
|
||||
if( !handle->isEnabled )
|
||||
return TR_NAT_TRAVERSAL_DISABLED;
|
||||
|
||||
if( !handle->isForwarding )
|
||||
return TR_NAT_TRAVERSAL_ERROR;
|
||||
|
||||
return TR_NAT_TRAVERSAL_MAPPED;
|
||||
}
|
||||
|
||||
void
|
||||
tr_upnpPulse( tr_upnp * handle UNUSED )
|
||||
{
|
||||
/* no-op */
|
||||
|
||||
if( handle->state == TR_UPNP_IDLE )
|
||||
{
|
||||
if( isEnabled && !handle->isMapped )
|
||||
handle->state = TR_UPNP_MAP;
|
||||
}
|
||||
|
||||
if( handle->state == TR_UPNP_MAP )
|
||||
{
|
||||
char portStr[16];
|
||||
snprintf( portStr, sizeof(portStr), "%d", port );
|
||||
handle->isMapped = ( handle->urls.controlURL != NULL ) &&
|
||||
( handle->data.servicetype != NULL ) &&
|
||||
( UPNP_AddPortMapping( handle->urls.controlURL,
|
||||
handle->data.servicetype,
|
||||
portStr, portStr, handle->lanaddr,
|
||||
"Transmission", "TCP" ) );
|
||||
tr_inf( KEY "port forwarding via '%s', service '%s'. (local address: %s:%d)",
|
||||
handle->urls.controlURL, handle->data.servicetype, handle->lanaddr, handle->port );
|
||||
if( handle->isMapped ) {
|
||||
tr_inf( KEY "port forwarding successful!" );
|
||||
handle->port = port;
|
||||
handle->state = TR_UPNP_IDLE;
|
||||
} else {
|
||||
tr_err( KEY "port forwarding failed" );
|
||||
handle->port = -1;
|
||||
handle->state = TR_UPNP_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
if( handle->state == TR_UPNP_ERR )
|
||||
ret = TR_NAT_TRAVERSAL_ERROR;
|
||||
else if( ( handle->state == TR_UPNP_IDLE ) && handle->isMapped )
|
||||
ret = TR_NAT_TRAVERSAL_MAPPED;
|
||||
else if( ( handle->state == TR_UPNP_IDLE ) && !handle->isMapped )
|
||||
ret = TR_NAT_TRAVERSAL_UNMAPPED;
|
||||
else if( handle->state == TR_UPNP_MAP )
|
||||
ret = TR_NAT_TRAVERSAL_MAPPING;
|
||||
else if( handle->state == TR_UPNP_UNMAP )
|
||||
ret = TR_NAT_TRAVERSAL_UNMAPPING;
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -1,26 +1,14 @@
|
|||
/******************************************************************************
|
||||
/*
|
||||
* This file Copyright (C) 2007 Charles Kerr <charles@rebelbase.com>
|
||||
*
|
||||
* 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$
|
||||
*
|
||||
* Copyright (c) 2006 Transmission authors and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
*/
|
||||
|
||||
#ifndef TR_UPNP_H
|
||||
#define TR_UPNP_H 1
|
||||
|
@ -28,12 +16,7 @@
|
|||
typedef struct tr_upnp tr_upnp;
|
||||
|
||||
tr_upnp * tr_upnpInit ( void );
|
||||
void tr_upnpStart ( tr_upnp * );
|
||||
void tr_upnpStop ( tr_upnp * );
|
||||
int tr_upnpStatus ( tr_upnp * );
|
||||
void tr_upnpForwardPort ( tr_upnp *, int );
|
||||
void tr_upnpRemoveForwarding ( tr_upnp * );
|
||||
void tr_upnpPulse ( tr_upnp * );
|
||||
void tr_upnpClose ( tr_upnp * );
|
||||
int tr_upnpPulse ( tr_upnp *, int port, int isEnabled );
|
||||
|
||||
#endif
|
||||
|
|
|
@ -187,7 +187,8 @@ tr_asprintf( char **strp, const char *fmt, ...)
|
|||
return ret;
|
||||
}
|
||||
|
||||
void tr_msg( int level, const char * fmt, ... )
|
||||
void
|
||||
tr_msg( const char * file, int line, int level, const char * fmt, ... )
|
||||
{
|
||||
FILE * fp;
|
||||
|
||||
|
@ -222,6 +223,8 @@ void tr_msg( int level, const char * fmt, ... )
|
|||
newmsg->level = level;
|
||||
newmsg->when = time( NULL );
|
||||
newmsg->message = text;
|
||||
newmsg->file = file;
|
||||
newmsg->line = line;
|
||||
|
||||
*messageQueueTail = newmsg;
|
||||
messageQueueTail = &newmsg->next;
|
||||
|
|
|
@ -32,10 +32,10 @@
|
|||
|
||||
void tr_msgInit( void );
|
||||
|
||||
#define tr_err( a... ) tr_msg( TR_MSG_ERR, ## a )
|
||||
#define tr_inf( a... ) tr_msg( TR_MSG_INF, ## a )
|
||||
#define tr_dbg( a... ) tr_msg( TR_MSG_DBG, ## a )
|
||||
void tr_msg ( int level, const char * msg, ... );
|
||||
#define tr_err( a... ) tr_msg( __FILE__, __LINE__, TR_MSG_ERR, ## a )
|
||||
#define tr_inf( a... ) tr_msg( __FILE__, __LINE__, TR_MSG_INF, ## a )
|
||||
#define tr_dbg( a... ) tr_msg( __FILE__, __LINE__, TR_MSG_DBG, ## a )
|
||||
void tr_msg ( const char * file, int line, int level, const char * msg, ... );
|
||||
FILE* tr_getLog( void );
|
||||
|
||||
char* tr_getLogTimeStr( char * buf, int buflen );
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
SUBDIRS = libevent miniupnp
|
||||
SUBDIRS = \
|
||||
libevent \
|
||||
miniupnp \
|
||||
libnatpmp
|
||||
|
||||
EXTRA_DIST = \
|
||||
macosx-libevent-config.h
|
||||
|
|
Loading…
Reference in New Issue