From 73ee11f233f7b7118b85553ec9c8d787f66d33dd Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Sat, 8 Dec 2007 19:34:15 +0000 Subject: [PATCH] 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. --- cli/Makefile.am | 1 + cli/transmissioncli.c | 2 +- configure.ac | 1 + daemon/Makefile.am | 1 + daemon/torrents.c | 2 +- gtk/Makefile.am | 1 + gtk/ipc.c | 3 + libtransmission/natpmp.c | 929 ++++++--------------------------- libtransmission/natpmp.h | 43 +- libtransmission/net.c | 64 +-- libtransmission/net.h | 1 - libtransmission/platform.c | 378 -------------- libtransmission/platform.h | 4 - libtransmission/shared.c | 327 +++++------- libtransmission/shared.h | 34 +- libtransmission/transmission.c | 9 +- libtransmission/transmission.h | 22 +- libtransmission/upnp.c | 162 +++--- libtransmission/upnp.h | 39 +- libtransmission/utils.c | 5 +- libtransmission/utils.h | 8 +- third-party/Makefile.am | 5 +- 22 files changed, 422 insertions(+), 1619 deletions(-) diff --git a/cli/Makefile.am b/cli/Makefile.am index ae8a2b7f4..1b8541bb6 100644 --- a/cli/Makefile.am +++ b/cli/Makefile.am @@ -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 diff --git a/cli/transmissioncli.c b/cli/transmissioncli.c index b047bbc3a..fe497b2a9 100644 --- a/cli/transmissioncli.c +++ b/cli/transmissioncli.c @@ -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; diff --git a/configure.ac b/configure.ac index 43e3658c0..57d567334 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 6af2ad701..67a215820 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -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 diff --git a/daemon/torrents.c b/daemon/torrents.c index 8eb4d831a..2d68bac63 100644 --- a/daemon/torrents.c +++ b/daemon/torrents.c @@ -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; } diff --git a/gtk/Makefile.am b/gtk/Makefile.am index a3afc42db..fabb09ed5 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -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 diff --git a/gtk/ipc.c b/gtk/ipc.c index f7ca40b6b..77900fbbf 100644 --- a/gtk/ipc.c +++ b/gtk/ipc.c @@ -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 ) { diff --git a/libtransmission/natpmp.c b/libtransmission/natpmp.c index 2b1806646..8ee7ec7d0 100644 --- a/libtransmission/natpmp.c +++ b/libtransmission/natpmp.c @@ -1,822 +1,185 @@ -/****************************************************************************** - * $Id$ +/* + * This file Copyright (C) 2007 Charles Kerr * - * 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 #include -#include -#include #include +#include +#include /* strerror */ -#ifdef __BEOS__ - #include -#endif +#include +#include +#include -#include +#include #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; -} diff --git a/libtransmission/natpmp.h b/libtransmission/natpmp.h index 4dab5abef..cfe0840e9 100644 --- a/libtransmission/natpmp.h +++ b/libtransmission/natpmp.h @@ -1,41 +1,22 @@ -/****************************************************************************** +/* + * This file Copyright (C) 2007 Charles Kerr + * + * 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 diff --git a/libtransmission/net.c b/libtransmission/net.c index 0c83d4feb..ebb0c62f1 100644 --- a/libtransmission/net.c +++ b/libtransmission/net.c @@ -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 ) { diff --git a/libtransmission/net.h b/libtransmission/net.h index de1ec59ca..ff2052bb3 100644 --- a/libtransmission/net.h +++ b/libtransmission/net.h @@ -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 * ); diff --git a/libtransmission/platform.c b/libtransmission/platform.c index c975a81bc..d151b1829 100644 --- a/libtransmission/platform.c +++ b/libtransmission/platform.c @@ -443,381 +443,3 @@ tr_getTorrentsDirectory( void ) init = 1; return buf; } - -/*** -**** SOCKETS -***/ - -#ifdef BSD - -#include -#include -#include -#include -#include /* struct in_addr */ -#include -#include - -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 -#include -#include - -#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 diff --git a/libtransmission/platform.h b/libtransmission/platform.h index 3c71da19a..b2b3efd45 100644 --- a/libtransmission/platform.h +++ b/libtransmission/platform.h @@ -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 diff --git a/libtransmission/shared.c b/libtransmission/shared.c index a91e82a4a..8f20ae136 100644 --- a/libtransmission/shared.c +++ b/libtransmission/shared.c @@ -22,20 +22,15 @@ * DEALINGS IN THE SOFTWARE. *****************************************************************************/ -#include -#include #include #include #include #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; +} diff --git a/libtransmission/shared.h b/libtransmission/shared.h index bd73d8b09..17af20c16 100644 --- a/libtransmission/shared.h +++ b/libtransmission/shared.h @@ -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 - diff --git a/libtransmission/transmission.c b/libtransmission/transmission.c index 5ff09e5f6..6c432381c 100644 --- a/libtransmission/transmission.c +++ b/libtransmission/transmission.c @@ -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 ); diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h index a7eaa5640..2db8cd1c4 100644 --- a/libtransmission/transmission.h +++ b/libtransmission/transmission.h @@ -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; }; diff --git a/libtransmission/upnp.c b/libtransmission/upnp.c index 4c713d532..0f262fcbb 100644 --- a/libtransmission/upnp.c +++ b/libtransmission/upnp.c @@ -10,7 +10,8 @@ * $Id$ */ -#include /* printf */ +#include +#include /* snprintf */ #include #include @@ -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; } diff --git a/libtransmission/upnp.h b/libtransmission/upnp.h index 1a8b7a901..88c1f0ae0 100644 --- a/libtransmission/upnp.h +++ b/libtransmission/upnp.h @@ -1,26 +1,14 @@ -/****************************************************************************** +/* + * This file Copyright (C) 2007 Charles Kerr + * + * 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 diff --git a/libtransmission/utils.c b/libtransmission/utils.c index 709accb77..494f727be 100644 --- a/libtransmission/utils.c +++ b/libtransmission/utils.c @@ -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; diff --git a/libtransmission/utils.h b/libtransmission/utils.h index 2070216ab..8c748ffae 100644 --- a/libtransmission/utils.h +++ b/libtransmission/utils.h @@ -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 ); diff --git a/third-party/Makefile.am b/third-party/Makefile.am index 845a40d83..cdb8c03a4 100644 --- a/third-party/Makefile.am +++ b/third-party/Makefile.am @@ -1,4 +1,7 @@ -SUBDIRS = libevent miniupnp +SUBDIRS = \ + libevent \ + miniupnp \ + libnatpmp EXTRA_DIST = \ macosx-libevent-config.h