Changeset 4092


Ignore:
Timestamp:
Dec 8, 2007, 7:34:15 PM (14 years ago)
Author:
charles
Message:

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.

Location:
trunk
Files:
22 edited

Legend:

Unmodified
Added
Removed
  • trunk/cli/Makefile.am

    r3731 r4092  
    1111    $(top_builddir)/libtransmission/libtransmission.a \
    1212    $(top_builddir)/third-party/libevent/libevent.la \
     13    $(top_builddir)/third-party/libnatpmp/libnatpmp.a \
    1314    $(top_builddir)/third-party/miniupnp/libminiupnp.a \
    1415    $(OPENSSL_LIBS) $(PTHREAD_LIBS) -lm
  • trunk/cli/transmissioncli.c

    r3974 r4092  
    340340    {
    341341        hstat = tr_handleStatus( h );
    342         if( TR_NAT_TRAVERSAL_DISABLED == hstat->natTraversalStatus )
     342        if( TR_NAT_TRAVERSAL_UNMAPPED == hstat->natTraversalStatus )
    343343        {
    344344            /* Port mappings were deleted */
  • trunk/configure.ac

    r4070 r4092  
    209209                 third-party/Makefile
    210210                 third-party/miniupnp/Makefile
     211                 third-party/libnatpmp/Makefile
    211212                 macosx/Makefile
    212213                 wx/Makefile
  • trunk/daemon/Makefile.am

    r3731 r4092  
    3030    $(top_builddir)/libtransmission/libtransmission.a \
    3131    $(top_builddir)/third-party/miniupnp/libminiupnp.a \
     32    $(top_builddir)/third-party/libnatpmp/libnatpmp.a \
    3233    $(top_builddir)/third-party/libevent/libevent.la \
    3334    $(OPENSSL_LIBS) $(PTHREAD_LIBS) -lm
  • trunk/daemon/torrents.c

    r3735 r4092  
    649649        {
    650650            hs = tr_handleStatus( gl_handle );
    651             if( TR_NAT_TRAVERSAL_DISABLED != hs->natTraversalStatus )
     651            if( TR_NAT_TRAVERSAL_UNMAPPED != hs->natTraversalStatus )
    652652            {
    653653                stillmore = 1;
  • trunk/gtk/Makefile.am

    r3993 r4092  
    5959    $(top_builddir)/third-party/libevent/libevent.la \
    6060    $(top_builddir)/third-party/miniupnp/libminiupnp.a \
     61    $(top_builddir)/third-party/libnatpmp/libnatpmp.a \
    6162    $(GTK_LIBS) \
    6263    $(OPENSSL_LIBS) \
  • trunk/gtk/ipc.c

    r3401 r4092  
    972972}
    973973
     974#define TR_NAT_TRAVERSAL_IS_DISABLED( st ) \
     975    ( TR_NAT_TRAVERSAL_UNMAPPED == (st) || TR_NAT_TRAVERSAL_UNMAPPING == (st) )
     976
    974977static void
    975978smsg_pref( enum ipc_msg id, benc_val_t * val SHUTUP, int64_t tag, void * arg )
  • trunk/libtransmission/natpmp.c

    r4029 r4092  
    1 /******************************************************************************
    2  * $Id$
     1/*
     2 * This file Copyright (C) 2007 Charles Kerr <charles@rebelbase.com>
    33 *
    4  * Copyright (c) 2006 Transmission authors and contributors
     4 * This file is licensed by the GPL version 2.  Works owned by the
     5 * Transmission project are granted a special exemption to clause 2(b)
     6 * so that the bulk of its code can remain under the MIT license.
     7 * This exemption does not extend to derived works not owned by
     8 * the Transmission project.
    59 *
    6  * Permission is hereby granted, free of charge, to any person obtaining a
    7  * copy of this software and associated documentation files (the "Software"),
    8  * to deal in the Software without restriction, including without limitation
    9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
    10  * and/or sell copies of the Software, and to permit persons to whom the
    11  * Software is furnished to do so, subject to the following conditions:
    12  *
    13  * The above copyright notice and this permission notice shall be included in
    14  * all copies or substantial portions of the Software.
    15  *
    16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    22  * DEALINGS IN THE SOFTWARE.
    23  *****************************************************************************/
     10 * $Id:$
     11 */
    2412
    2513#include <assert.h>
    2614#include <errno.h>
    27 #include <stdlib.h>
    28 #include <string.h>
    2915#include <time.h>
     16#include <inttypes.h>
     17#include <string.h> /* strerror */
    3018
    31 #ifdef __BEOS__
    32     #include <netdb.h>
    33 #endif
     19#include <sys/socket.h>
     20#include <netinet/in.h>
     21#include <arpa/inet.h>
    3422
    35 #include <sys/types.h>
     23#include <libnatpmp/natpmp.h>
    3624
    3725#include "transmission.h"
    3826#include "natpmp.h"
    39 #include "net.h"
    40 #include "platform.h" /* tr_getDefaultRoute() */
     27#include "shared.h"
    4128#include "utils.h"
    4229
    43 #define PMP_PORT                5351
    44 #define PMP_INITIAL_DELAY       250     /* ms, 1/4 second */
    45 #define PMP_TOTAL_DELAY         120000  /* ms, 2 minutes */
    46 #define PMP_VERSION             0
    47 #define PMP_OPCODE_GETIP        0
    48 #define PMP_OPCODE_ADDUDP       1
    49 #define PMP_OPCODE_ADDTCP       2
    50 #define PMP_LIFETIME            3600    /* secs, one hour */
    51 #define PMP_RESULT_OK           0
    52 #define PMP_RESULT_BADVERS      1
    53 #define PMP_RESULT_REFUSED      2
    54 #define PMP_RESULT_NETDOWN      3
    55 #define PMP_RESULT_NOMEM        4
    56 #define PMP_RESULT_BADOPCODE    5
     30#define LIFETIME_SECS 3600
    5731
    58 #define PMP_OPCODE_FROM_RESPONSE( op )  ( 0x80 ^ (op) )
    59 #define PMP_OPCODE_TO_RESPONSE( op )    ( 0x80 | (op) )
    60 #define PMP_OPCODE_IS_RESPONSE( op )    ( 0x80 & (op) )
    61 #define PMP_TOBUF16( buf, num ) ( *( (uint16_t *) (buf) ) = htons( (num) ) )
    62 #define PMP_TOBUF32( buf, num ) ( *( (uint32_t *) (buf) ) = htonl( (num) ) )
    63 #define PMP_FROMBUF16( buf )    ( htons( *( (uint16_t *) (buf) ) ) )
    64 #define PMP_FROMBUF32( buf )    ( htonl( *( (uint32_t *) (buf) ) ) )
     32#define KEY "Port Mapping (NAT-PMP): "
    6533
    66 typedef struct tr_natpmp_uptime_s
     34typedef enum
    6735{
    68     time_t   when;
    69     uint32_t uptime;
    70 } tr_natpmp_uptime_t;
    71 
    72 typedef struct tr_natpmp_req_s
    73 {
    74     unsigned int         adding : 1;
    75     unsigned int         nobodyhome : 1;
    76     unsigned int         tmpfail : 1;
    77     int                  fd;
    78     int                  delay;
    79     uint64_t             retry;
    80     uint64_t             timeout;
    81     int                  askport;
    82     int                  gotport;
    83 } tr_natpmp_req_t;
    84 
     36    TR_NATPMP_IDLE,
     37    TR_NATPMP_ERR,
     38    TR_NATPMP_RECV_PUB,
     39    TR_NATPMP_SEND_MAP,
     40    TR_NATPMP_RECV_MAP,
     41    TR_NATPMP_SEND_UNMAP,
     42    TR_NATPMP_RECV_UNMAP
     43}
     44tr_natpmp_state;
     45   
    8546struct tr_natpmp
    8647{
    87 #define PMP_STATE_IDLE          1
    88 #define PMP_STATE_ADDING        2
    89 #define PMP_STATE_DELETING      3
    90 #define PMP_STATE_MAPPED        4
    91 #define PMP_STATE_FAILED        5
    92 #define PMP_STATE_NOBODYHOME    6
    93 #define PMP_STATE_TMPFAIL       7
    94     char               state;
    95     unsigned int       active : 1;
    96     unsigned int       mapped : 1;
    97     struct in_addr     dest;
    98     int                newport;
    99     int                mappedport;
    100     uint64_t           renew;
    101     tr_natpmp_req_t *  req;
    102     tr_natpmp_uptime_t uptime;
    103     int                mcastfd;
     48    int port;
     49    int isMapped;
     50    time_t renewTime;
     51    tr_natpmp_state state;
     52    natpmp_t natpmp;
    10453};
    10554
    106 typedef struct tr_natpmp_parse_s
    107 {
    108     unsigned int tmpfail : 1;
    109     uint32_t     seconds;
    110     uint16_t     port;
    111     uint32_t     lifetime;
    112 }
    113 tr_natpmp_parse_t;
     55/**
     56***
     57**/
    11458
    11559static void
    116 unmap( tr_natpmp * pmp );
    117 static int
    118 checktime( tr_natpmp_uptime_t * uptime, uint32_t seen );
    119 static void
    120 killsock( int * fd );
    121 static tr_natpmp_req_t *
    122 newreq( int adding, struct in_addr addr, int port );
    123 static void
    124 killreq( tr_natpmp_req_t ** req );
    125 static void
    126 resetreq( tr_natpmp_req_t * req );
    127 static tr_tristate_t
    128 pulsereq( tr_natpmp * req );
    129 static int
    130 sendreq( tr_natpmp_req_t * req );
    131 static int
    132 mcastsetup();
    133 static void
    134 mcastpulse( tr_natpmp * pmp );
    135 static tr_tristate_t
    136 parseresponse( uint8_t * buf, int len, int port, tr_natpmp_parse_t * parse );
     60logVal( const char * func, int ret )
     61{
     62    if( ret==NATPMP_TRYAGAIN )
     63        tr_dbg( KEY "%s returned 'try again'", func );
     64    else if( ret >= 0 )
     65        tr_dbg( KEY "%s returned success (%d)", func, ret );
     66    else
     67        tr_err( KEY "%s returned error %d, errno is %d (%s)", func, ret, errno, strerror(errno) );
     68}
    13769
    138 tr_natpmp *
    139 tr_natpmpInit()
     70struct tr_natpmp*
     71tr_natpmpInit( void )
    14072{
    141     tr_natpmp * pmp;
     73    struct tr_natpmp * nat = tr_new0( struct tr_natpmp, 1 );
     74    int val;
    14275
    143     pmp = calloc( 1, sizeof( *pmp ) );
    144     if( NULL == pmp )
    145     {
    146         return NULL;
    147     }
     76    val = initnatpmp( &nat->natpmp );
     77    logVal( "initnatpmp", val );
     78    val = sendpublicaddressrequest( &nat->natpmp );
     79    logVal( "sendpublicaddressrequest", val );
    14880
    149     pmp->state       = PMP_STATE_IDLE;
    150     pmp->mcastfd     = -1;
    151 
    152     if( tr_getDefaultRoute( &pmp->dest ) || INADDR_ANY == pmp->dest.s_addr )
    153     {
    154         pmp->dest.s_addr = INADDR_NONE;
    155     }
    156 
    157     if( INADDR_NONE == pmp->dest.s_addr )
    158     {
    159         tr_dbg( "nat-pmp device is unknown" );
    160     }
    161     else
    162     {
    163         char addrstr[INET_ADDRSTRLEN];
    164         tr_netNtop( &pmp->dest, addrstr, sizeof( addrstr ) );
    165         tr_dbg( "nat-pmp device is %s", addrstr );
    166     }
    167 
    168     return pmp;
     81    nat->state = val < 0 ? TR_NATPMP_ERR : TR_NATPMP_RECV_PUB;
     82    nat->port = -1;
     83    return nat;
    16984}
    17085
    17186void
    172 tr_natpmpStart( tr_natpmp * pmp )
     87tr_natpmpClose( tr_natpmp * nat )
    17388{
    174     if( !pmp->active )
    175     {
    176         tr_inf( "starting nat-pmp" );
    177         pmp->active = 1;
    178         if( 0 > pmp->mcastfd )
    179         {
    180             pmp->mcastfd = mcastsetup();
    181         }
    182     }
    183 }
     89    assert( !nat->isMapped );
     90    assert( ( nat->state == TR_NATPMP_IDLE ) || ( nat->state == TR_NATPMP_ERR ) );
    18491
    185 void
    186 tr_natpmpStop( tr_natpmp * pmp )
    187 {
    188     if( pmp->active )
    189     {
    190         tr_inf( "stopping nat-pmp" );
    191         pmp->active = 0;
    192         killsock( &pmp->mcastfd );
    193         unmap( pmp );
    194     }
     92    closenatpmp( &nat->natpmp );
     93    tr_free( nat );
    19594}
    19695
    19796int
    198 tr_natpmpStatus( tr_natpmp * pmp )
     97tr_natpmpPulse( struct tr_natpmp * nat, int port, int isEnabled )
    19998{
    20099    int ret;
    201    
    202     if( !pmp->active )
     100
     101    if( nat->state == TR_NATPMP_RECV_PUB )
    203102    {
    204         ret = ( PMP_STATE_DELETING == pmp->state ?
    205                 TR_NAT_TRAVERSAL_UNMAPPING : TR_NAT_TRAVERSAL_DISABLED );
    206     }
    207     else if( pmp->mapped )
    208     {
    209         ret = TR_NAT_TRAVERSAL_MAPPED;
    210     }
    211     else
    212     {
    213         switch( pmp->state )
    214         {
    215             case PMP_STATE_IDLE:
    216             case PMP_STATE_ADDING:
    217             case PMP_STATE_DELETING:
    218                 ret = TR_NAT_TRAVERSAL_MAPPING;
    219                 break;
    220             case PMP_STATE_FAILED:
    221             case PMP_STATE_TMPFAIL:
    222                 ret = TR_NAT_TRAVERSAL_ERROR;
    223                 break;
    224             case PMP_STATE_NOBODYHOME:
    225                 ret = TR_NAT_TRAVERSAL_NOTFOUND;
    226                 break;
    227             case PMP_STATE_MAPPED:
    228             default:
    229                 /* if pmp->state is PMP_STATE_MAPPED then pmp->mapped
    230                    should be true */
    231                 assert( 0 );
    232                 ret = TR_NAT_TRAVERSAL_ERROR;
    233                 break;
     103        natpmpresp_t response;
     104        const int val = readnatpmpresponseorretry( &nat->natpmp, &response );
     105        logVal( "readnatpmpresponseorretry", val );
     106        if( val >= 0 ) {
     107            tr_inf( KEY "found public address %s", inet_ntoa( response.publicaddress.addr ) );
     108            nat->state = TR_NATPMP_IDLE;
     109        } else if( val != NATPMP_TRYAGAIN ) {
     110            nat->state = TR_NATPMP_ERR;
    234111        }
    235112    }
    236113
    237     return ret;
    238 }
    239 
    240 void
    241 tr_natpmpForwardPort( tr_natpmp * pmp, int port )
    242 {
    243     tr_inf( "nat-pmp set port %i", port );
    244     pmp->newport = port;
    245 }
    246 
    247 void
    248 tr_natpmpRemoveForwarding( tr_natpmp * pmp )
    249 {
    250     tr_inf( "nat-pmp unset port" );
    251     pmp->newport = -1;
    252     unmap( pmp );
    253 }
    254 
    255 void
    256 tr_natpmpClose( tr_natpmp * pmp )
    257 {
    258     /* try to send at least one delete request if we have a port mapping */
    259     tr_natpmpStop( pmp );
    260     tr_natpmpPulse( pmp, NULL );
    261 
    262     killreq( &pmp->req );
    263     free( pmp );
    264 }
    265 
    266 void
    267 tr_natpmpPulse( tr_natpmp * pmp, int * publicPort )
    268 {
    269     if( 0 <= pmp->mcastfd )
     114    if( ( nat->state == TR_NATPMP_IDLE ) || ( nat->state == TR_NATPMP_ERR ) )
    270115    {
    271         mcastpulse( pmp );
     116        if( nat->isMapped && ( !isEnabled || ( nat->port != port ) ) )
     117            nat->state = TR_NATPMP_SEND_UNMAP;
    272118    }
    273119
    274     if( NULL != publicPort )
     120    if( nat->state == TR_NATPMP_SEND_UNMAP )
    275121    {
    276         *publicPort = -1;
     122        const int val = sendnewportmappingrequest( &nat->natpmp, NATPMP_PROTOCOL_TCP, nat->port, nat->port, 0 );
     123        logVal( "sendnewportmappingrequest", val );
     124        nat->state = val < 0 ? TR_NATPMP_ERR : TR_NATPMP_RECV_UNMAP;
    277125    }
    278126
    279     if( pmp->active || PMP_STATE_DELETING == pmp->state )
     127    if( nat->state == TR_NATPMP_RECV_UNMAP )
    280128    {
    281         switch( pmp->state )
    282         {
    283             case PMP_STATE_IDLE:
    284             case PMP_STATE_TMPFAIL:
    285                 if( 0 < pmp->newport )
    286                 {
    287                     tr_dbg( "nat-pmp state %s -> add with port %i",
    288                             ( PMP_STATE_IDLE == pmp->state ? "idle" : "err" ),
    289                             pmp->newport );
    290                     pmp->state = PMP_STATE_ADDING;
    291                 }
    292                 break;
    293 
    294             case PMP_STATE_ADDING:
    295                 if( NULL == pmp->req )
    296                 {
    297                     if( 0 >= pmp->newport )
    298                     {
    299                         tr_dbg( "nat-pmp state add -> idle, no port" );
    300                         pmp->state = PMP_STATE_IDLE;
    301                     }
    302                     else if( INADDR_NONE == pmp->dest.s_addr )
    303                     {
    304                         tr_dbg( "nat-pmp state add -> fail, no default route" );
    305                         pmp->state = PMP_STATE_FAILED;
    306                     }
    307                     else
    308                     {
    309                         pmp->req = newreq( 1, pmp->dest, pmp->newport );
    310                         if( NULL == pmp->req )
    311                         {
    312                             pmp->state = PMP_STATE_FAILED;
    313                             tr_dbg( "nat-pmp state add -> fail on req init" );
    314                         }
    315                     }
    316                 }
    317                 if( PMP_STATE_ADDING == pmp->state )
    318                 {
    319                     switch( pulsereq( pmp ) )
    320                     {
    321                         case TR_NET_ERROR:
    322                             if( pmp->req->nobodyhome )
    323                             {
    324                                 pmp->state = PMP_STATE_NOBODYHOME;
    325                                 tr_dbg( "nat-pmp state add -> nobodyhome on pulse" );
    326                             }
    327                             else if( pmp->req->tmpfail )
    328                             {
    329                                 pmp->state = PMP_STATE_TMPFAIL;
    330                                 tr_dbg( "nat-pmp state add -> err on pulse" );
    331                                 if( pmp->req->askport == pmp->newport )
    332                                 {
    333                                     pmp->newport = 0;
    334                                 }
    335                             }
    336                             else
    337                             {
    338                                 pmp->state = PMP_STATE_FAILED;
    339                                 tr_dbg( "nat-pmp state add -> fail on pulse" );
    340                             }
    341                             killreq( &pmp->req );
    342                             break;
    343                         case TR_NET_OK:
    344                             pmp->mappedport = pmp->req->gotport;
    345                             if( pmp->mappedport != pmp->newport &&
    346                                 pmp->newport == pmp->req->askport )
    347                             {
    348                                 pmp->newport = pmp->req->gotport;
    349                             }
    350                             killreq( &pmp->req );
    351                             pmp->state = PMP_STATE_MAPPED;
    352                             pmp->mapped = 1;
    353                             tr_dbg( "nat-pmp state add -> mapped with port %i",
    354                                     pmp->mappedport);
    355                             tr_inf( "nat-pmp mapped port %i", pmp->mappedport );
    356                             if( NULL != publicPort )
    357                             {
    358                                 *publicPort = pmp->mappedport;
    359                             }
    360                             break;
    361                         case TR_NET_WAIT:
    362                             break;
    363                     }
    364                 }
    365                 break;
    366 
    367             case PMP_STATE_DELETING:
    368                 if( NULL == pmp->req )
    369                 {
    370                     assert( 0 < pmp->mappedport );
    371                     pmp->req = newreq( 0, pmp->dest, pmp->mappedport );
    372                     if( NULL == pmp->req )
    373                     {
    374                         pmp->state = PMP_STATE_FAILED;
    375                         tr_dbg( "nat-pmp state del -> fail on req init" );
    376                     }
    377                 }
    378                 if( PMP_STATE_DELETING == pmp->state )
    379                 {
    380                     switch( pulsereq( pmp ) )
    381                     {
    382                         case TR_NET_ERROR:
    383                             if( pmp->req->nobodyhome )
    384                             {
    385                                 pmp->mapped = 0;
    386                                 pmp->state = PMP_STATE_NOBODYHOME;
    387                                 tr_dbg( "nat-pmp state del -> nobodyhome on pulse" );
    388                             }
    389                             else if( pmp->req->tmpfail )
    390                             {
    391                                 pmp->mapped = 0;
    392                                 pmp->state = PMP_STATE_TMPFAIL;
    393                                 tr_dbg( "nat-pmp state del -> err on pulse" );
    394                                 pmp->mappedport = -1;
    395                             }
    396                             else
    397                             {
    398                                 pmp->state = PMP_STATE_FAILED;
    399                                 tr_dbg( "nat-pmp state del -> fail on pulse" );
    400                             }
    401                             killreq( &pmp->req );
    402                             break;
    403                         case TR_NET_OK:
    404                             tr_dbg( "nat-pmp state del -> idle with port %i",
    405                                     pmp->req->askport);
    406                             tr_inf( "nat-pmp unmapped port %i",
    407                                     pmp->req->askport );
    408                             pmp->mapped = 0;
    409                             pmp->mappedport = -1;
    410                             killreq( &pmp->req );
    411                             pmp->state = PMP_STATE_IDLE;
    412                             break;
    413                         case TR_NET_WAIT:
    414                             break;
    415                     }
    416                 }
    417                 break;
    418 
    419             case PMP_STATE_MAPPED:
    420                 if( pmp->newport != pmp->mappedport )
    421                 {
    422                     tr_dbg( "nat-pmp state mapped -> del, port from %i to %i",
    423                             pmp->mappedport, pmp->newport );
    424                     pmp->state = PMP_STATE_DELETING;
    425                 }
    426                 else if( tr_date() > pmp->renew )
    427                 {
    428                     pmp->state = PMP_STATE_ADDING;
    429                     tr_dbg( "nat-pmp state mapped -> add for renewal" );
    430                 }
    431                 break;
    432 
    433             case PMP_STATE_FAILED:
    434             case PMP_STATE_NOBODYHOME:
    435                 break;
    436 
    437             default:
    438                 assert( 0 );
    439                 break;
    440         }
    441     }
    442 }
    443 
    444 void
    445 unmap( tr_natpmp * pmp )
    446 {
    447     switch( pmp->state )
    448     {
    449         case PMP_STATE_IDLE:
    450             break;
    451         case PMP_STATE_ADDING:
    452             if( NULL == pmp->req )
    453             {
    454                 pmp->state = PMP_STATE_IDLE;
    455                 tr_dbg( "nat-pmp state add -> idle" );
    456             }
    457             else
    458             {
    459                 pmp->mappedport = pmp->req->gotport;
    460                 killreq( &pmp->req );
    461                 pmp->state = PMP_STATE_DELETING;
    462                 tr_dbg( "nat-pmp state add -> del" );
    463             }
    464             break;
    465         case PMP_STATE_DELETING:
    466             break;
    467         case PMP_STATE_MAPPED:
    468             pmp->state = PMP_STATE_DELETING;
    469             tr_dbg( "nat-pmp state mapped -> del" );
    470             break;
    471         case PMP_STATE_FAILED:
    472         case PMP_STATE_NOBODYHOME:
    473         case PMP_STATE_TMPFAIL:
    474             break;
    475         default:
    476             assert( 0 );
    477             break;
    478     }
    479 }
    480 
    481 static int
    482 checktime( tr_natpmp_uptime_t * uptime, uint32_t cursecs )
    483 {
    484     time_t   now;
    485     int      ret;
    486     uint32_t estimated;
    487 
    488     now = time( NULL );
    489     ret = 0;
    490     if( 0 < uptime->when )
    491     {
    492         estimated = ( ( now - uptime->when ) * 7 / 8 ) + uptime->uptime;
    493         if( estimated > cursecs )
    494         {
    495             ret = 1;
     129        natpmpresp_t resp;
     130        const int val = readnatpmpresponseorretry( &nat->natpmp, &resp );
     131        logVal( "readnatpmpresponseorretry", val );
     132        if( val >= 0 ) {
     133            tr_inf( KEY "port %d has been unmapped.", nat->port );
     134            nat->state = TR_NATPMP_IDLE;
     135            nat->port = -1;
     136            nat->isMapped = 0;
     137        } else if( val != NATPMP_TRYAGAIN ) {
     138            nat->state = TR_NATPMP_ERR;
    496139        }
    497140    }
    498141
    499     uptime->when   = now;
    500     uptime->uptime = cursecs;
     142    if( nat->state == TR_NATPMP_IDLE )
     143    {
     144        if( isEnabled && !nat->isMapped )
     145            nat->state = TR_NATPMP_SEND_MAP;
    501146
    502     return ret;
    503 }
    504 
    505 static void
    506 killsock( int * fd )
    507 {
    508     if( 0 <= *fd )
    509     {
    510         tr_netClose( *fd );
    511         *fd = -1;
    512     }
    513 }
    514 
    515 static tr_natpmp_req_t *
    516 newreq( int adding, struct in_addr addr, int port )
    517 {
    518     tr_natpmp_req_t * ret;
    519 
    520     ret = calloc( 1, sizeof( *ret ) );
    521     if( NULL == ret )
    522     {
    523         return NULL;
     147        else if( nat->isMapped && time(NULL) <= nat->renewTime )
     148            nat->state = TR_NATPMP_SEND_MAP;
    524149    }
    525150
    526     ret->fd = tr_netOpenUDP( &addr, htons( PMP_PORT ), 1 );
    527     if( 0 > ret->fd )
     151    if( nat->state == TR_NATPMP_SEND_MAP )
    528152    {
    529         free( ret );
    530         return NULL;
     153        const int val = sendnewportmappingrequest( &nat->natpmp, NATPMP_PROTOCOL_TCP, port, port, LIFETIME_SECS );
     154        logVal( "sendnewportmappingrequest", val );
     155        nat->state = val < 0 ? TR_NATPMP_ERR : TR_NATPMP_RECV_MAP;
    531156    }
    532157
    533     ret->adding  = adding;
    534     ret->askport = port;
    535     ret->gotport = port;
    536     resetreq( ret );
    537     if( sendreq( ret ) )
     158    if( nat->state == TR_NATPMP_RECV_MAP )
    538159    {
    539         killreq( &ret );
    540         return NULL;
    541     }
    542 
    543     return ret;
    544 }
    545 
    546 static void
    547 killreq( tr_natpmp_req_t ** req )
    548 {
    549     if( NULL != *req )
    550     {
    551         killsock( &(*req)->fd );
    552         free( *req );
    553         *req = NULL;
    554     }
    555 }
    556 
    557 static void
    558 resetreq( tr_natpmp_req_t * req )
    559 {
    560     uint64_t now;
    561 
    562     now          = tr_date();
    563     req->delay   = PMP_INITIAL_DELAY;
    564     req->retry   = now;
    565     req->timeout = now + PMP_TOTAL_DELAY;
    566 }
    567 
    568 static tr_tristate_t
    569 pulsereq( tr_natpmp * pmp )
    570 {
    571     tr_natpmp_req_t  * req = pmp->req;
    572     struct sockaddr_in sin;
    573     uint8_t            buf[16];
    574     int                res;
    575     uint64_t           now;
    576     tr_tristate_t      ret;
    577     tr_natpmp_parse_t  parse;
    578 
    579     now = tr_date();
    580     /* check for timeout */
    581     if( now >= req->timeout )
    582     {
    583         tr_dbg( "nat-pmp request timed out" );
    584         req->nobodyhome = 1;
    585         return TR_NET_ERROR;
    586     }
    587     /* send another request  if it's been long enough */
    588     if( now >= req->retry && sendreq( req ) )
    589     {
    590         return TR_NET_ERROR;
    591     }
    592 
    593     /* check for incoming packets */
    594     res = tr_netRecvFrom( req->fd, buf, sizeof( buf ), &sin );
    595     if( TR_NET_BLOCK & res )
    596     {
    597         return TR_NET_WAIT;
    598     }
    599     else if( TR_NET_CLOSE & res )
    600     {
    601         if( ECONNRESET == sockerrno || ECONNREFUSED == sockerrno )
    602         {
    603             tr_dbg( "nat-pmp not supported by device" );
    604             req->nobodyhome = 1;
    605         }
    606         else
    607         {
    608             tr_inf( "error reading nat-pmp response (%s)", strerror( sockerrno ) );
    609         }
    610         return TR_NET_ERROR;
    611     }
    612 
    613     /* parse the packet */
    614     tr_dbg( "nat-pmp read %i byte response", res );
    615     ret = parseresponse( buf, res, req->askport, &parse );
    616     req->tmpfail = parse.tmpfail;
    617     /* check for device reset */
    618     if( checktime( &pmp->uptime, parse.seconds ) )
    619     {
    620         pmp->renew = 0;
    621         tr_inf( "detected nat-pmp device reset" );
    622         resetreq( req );
    623         ret = TR_NET_WAIT;
    624     }
    625     if( TR_NET_OK == ret && req->adding )
    626     {
    627         if( req->askport != parse.port )
    628         {
    629             tr_dbg( "nat-pmp received %i for public port instead of %i",
    630                     parse.port, req->askport );
    631             req->gotport = parse.port;
    632         }
    633         tr_dbg( "nat-pmp set renew to half of %u", parse.lifetime );
    634         pmp->renew = now + ( parse.lifetime / 2 * 1000 );
    635     }
    636 
    637     return ret;
    638 }
    639 
    640 static int
    641 sendreq( tr_natpmp_req_t * req )
    642 {
    643     uint8_t buf[12];
    644     int res;
    645 
    646     memset( buf, 0, sizeof( buf ) );
    647     buf[0] = PMP_VERSION;
    648     buf[1] = PMP_OPCODE_ADDTCP;
    649     PMP_TOBUF16( buf + 4, req->askport );
    650     if( req->adding )
    651     {
    652         PMP_TOBUF16( buf + 6, req->askport );
    653         PMP_TOBUF32( buf + 8, PMP_LIFETIME );
    654     }
    655 
    656     res = tr_netSend( req->fd, buf, sizeof( buf ) );
    657     if( TR_NET_CLOSE & res && EHOSTUNREACH == sockerrno )
    658     {
    659         res = TR_NET_BLOCK;
    660     }
    661     if( TR_NET_CLOSE & res )
    662     {
    663         tr_err( "failed to send nat-pmp request (%s)", strerror( sockerrno ) );
    664         return 1;
    665     }
    666     else if( !( TR_NET_BLOCK & res ) )
    667     {
    668         /* XXX is it all right to assume the entire thing is written? */
    669         req->retry  = tr_date() + req->delay;
    670         req->delay *= 2;
    671     }
    672     return 0;
    673 }
    674 
    675 static int
    676 mcastsetup()
    677 {
    678     int fd;
    679     struct in_addr addr;
    680 
    681     addr.s_addr = inet_addr( PMP_MCAST_ADDR );
    682     fd = tr_netMcastOpen( PMP_PORT, &addr );
    683     if( 0 > fd )
    684     {
    685         return -1;
    686     }
    687 
    688     tr_dbg( "nat-pmp create multicast socket %i", fd );
    689 
    690     return fd;
    691 }
    692 
    693 static void
    694 mcastpulse( tr_natpmp * pmp )
    695 {
    696     struct sockaddr_in sin;
    697     uint8_t            buf[16];
    698     int                res;
    699     char               dbgstr[INET_ADDRSTRLEN];
    700     tr_natpmp_parse_t  parse;
    701 
    702     res = tr_netRecvFrom( pmp->mcastfd, buf, sizeof( buf ), &sin );
    703     if( TR_NET_BLOCK & res )
    704     {
    705         return;
    706     }
    707     else if( TR_NET_CLOSE & res )
    708     {
    709         tr_err( "error reading nat-pmp multicast message" );
    710         killsock( &pmp->mcastfd );
    711         return;
    712     }
    713 
    714     tr_netNtop( &sin.sin_addr, dbgstr, sizeof( dbgstr ) );
    715     tr_dbg( "nat-pmp read %i byte multicast packet from %s", res, dbgstr );
    716 
    717     if( pmp->dest.s_addr != sin.sin_addr.s_addr )
    718     {
    719         tr_dbg( "nat-pmp ignoring multicast packet from unknown host %s",
    720                 dbgstr );
    721         return;
    722     }
    723 
    724     if( TR_NET_OK == parseresponse( buf, res, -1, &parse ) )
    725     {
    726         if( checktime( &pmp->uptime, parse.seconds ) )
    727         {
    728             pmp->renew = 0;
    729             tr_inf( "detected nat-pmp device reset" );
    730             if( NULL != pmp->req )
    731             {
    732                 resetreq( pmp->req );
    733             }
    734         }
    735         if( PMP_STATE_NOBODYHOME == pmp->state )
    736         {
    737             tr_dbg( "nat-pmp state notfound -> idle" );
    738             pmp->state = PMP_STATE_IDLE;
    739         }
    740     }
    741 }
    742 
    743 static tr_tristate_t
    744 parseresponse( uint8_t * buf, int len, int port, tr_natpmp_parse_t * parse )
    745 {
    746     int version, respopcode, opcode, wantedopcode, rescode, privport;
    747 
    748     memset( parse, 0, sizeof( *parse ) );
    749 
    750     if( 8 > len )
    751     {
    752         tr_err( "read truncated %i byte nat-pmp response packet", len );
    753         return TR_NET_ERROR;
    754     }
    755 
    756     /* parse the first 8 bytes: version, opcode, and result code */
    757     version      = buf[0];
    758     respopcode   = buf[1];
    759     opcode       = PMP_OPCODE_FROM_RESPONSE( respopcode );
    760     wantedopcode = ( 0 < port ? PMP_OPCODE_ADDTCP : PMP_OPCODE_GETIP );
    761     rescode      = PMP_FROMBUF16( buf + 2 );
    762 
    763     if( PMP_VERSION != version )
    764     {
    765         tr_err( "unknown nat-pmp version %hhu", buf[0] );
    766         return TR_NET_ERROR;
    767     }
    768     if( !PMP_OPCODE_IS_RESPONSE( respopcode ) )
    769     {
    770         tr_dbg( "nat-pmp ignoring request packet" );
    771         return TR_NET_WAIT;
    772     }
    773     if( wantedopcode != opcode )
    774     {
    775         tr_err( "unknown nat-pmp opcode %hhu", opcode );
    776         return TR_NET_ERROR;
    777     }
    778 
    779     switch( rescode )
    780     {
    781         case PMP_RESULT_OK:
    782             break;
    783         case PMP_RESULT_REFUSED:
    784             tr_err( "nat-pmp mapping failed: refused/unauthorized/disabled" );
    785             parse->tmpfail = 1;
    786             return TR_NET_ERROR;
    787         case PMP_RESULT_NETDOWN:
    788             tr_err( "nat-pmp mapping failed: network down" );
    789             parse->tmpfail = 1;
    790             return TR_NET_ERROR;
    791         case PMP_RESULT_NOMEM:
    792             tr_err( "nat-pmp mapping refused: insufficient resources" );
    793             parse->tmpfail = 1;
    794             return TR_NET_ERROR;
    795         default:
    796             tr_err( "nat-pmp mapping refused: unknown result code: %hu",
    797                     rescode );
    798             return TR_NET_ERROR;
    799     }
    800 
    801     parse->seconds = PMP_FROMBUF32( buf + 4 );
    802     if( PMP_OPCODE_ADDTCP == opcode )
    803     {
    804         if( 16 > len )
    805         {
    806             tr_err( "read truncated %i byte nat-pmp response packet", len );
    807             return TR_NET_ERROR;
    808         }
    809         privport        = PMP_FROMBUF16( buf + 8 );
    810         parse->port     = PMP_FROMBUF16( buf + 10 );
    811         parse->lifetime = PMP_FROMBUF32( buf + 12 );
    812 
    813         if( port != privport )
    814         {
    815             tr_dbg( "nat-pmp ignoring message for port %i, expected port %i",
    816                     privport, port );
    817             return TR_NET_WAIT;
     160        natpmpresp_t resp;
     161        const int val = readnatpmpresponseorretry( &nat->natpmp, &resp );
     162        logVal( "readnatpmpresponseorretry", val );
     163        if( val >= 0 ) {
     164            nat->state = TR_NATPMP_IDLE;
     165            nat->isMapped = 1;
     166            nat->renewTime = time( NULL ) + LIFETIME_SECS;
     167            nat->port = resp.newportmapping.privateport;
     168            tr_inf( KEY "port %d mapped successfully", nat->port );
     169        } else if( val != NATPMP_TRYAGAIN ) {
     170            nat->state = TR_NATPMP_ERR;
    818171        }
    819172    }
    820173
    821     return TR_NET_OK;
     174    if( nat->state == TR_NATPMP_ERR )
     175        ret = TR_NAT_TRAVERSAL_ERROR;
     176    else if( ( nat->state == TR_NATPMP_IDLE ) &&  ( nat->isMapped ) )
     177        ret = TR_NAT_TRAVERSAL_MAPPED;
     178    else if( ( nat->state == TR_NATPMP_IDLE ) &&  ( !nat->isMapped ) )
     179        ret = TR_NAT_TRAVERSAL_UNMAPPED;
     180    else if( ( nat->state == TR_NATPMP_SEND_MAP ) || ( nat->state == TR_NATPMP_RECV_MAP ) )
     181        ret = TR_NAT_TRAVERSAL_MAPPING;
     182    else if( ( nat->state == TR_NATPMP_SEND_UNMAP ) || ( nat->state == TR_NATPMP_RECV_UNMAP ) )
     183        ret = TR_NAT_TRAVERSAL_UNMAPPING;
     184    return ret;
    822185}
  • trunk/libtransmission/natpmp.h

    r4029 r4092  
    1 /******************************************************************************
     1/*
     2 * This file Copyright (C) 2007 Charles Kerr <charles@rebelbase.com>
     3 *
     4 * This file is licensed by the GPL version 2.  Works owned by the
     5 * Transmission project are granted a special exemption to clause 2(b)
     6 * so that the bulk of its code can remain under the MIT license.
     7 * This exemption does not extend to derived works not owned by
     8 * the Transmission project.
     9 *
    210 * $Id$
    3  *
    4  * Copyright (c) 2006 Transmission authors and contributors
    5  *
    6  * Permission is hereby granted, free of charge, to any person obtaining a
    7  * copy of this software and associated documentation files (the "Software"),
    8  * to deal in the Software without restriction, including without limitation
    9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
    10  * and/or sell copies of the Software, and to permit persons to whom the
    11  * Software is furnished to do so, subject to the following conditions:
    12  *
    13  * The above copyright notice and this permission notice shall be included in
    14  * all copies or substantial portions of the Software.
    15  *
    16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    22  * DEALINGS IN THE SOFTWARE.
    23  *****************************************************************************/
     11 */
    2412
    2513#ifndef TR_NATPMP_H
     
    2816typedef struct tr_natpmp tr_natpmp;
    2917
    30 tr_natpmp * tr_natpmpInit();
    31 void        tr_natpmpStart( tr_natpmp * );
    32 void        tr_natpmpStop( tr_natpmp * );
    33 int         tr_natpmpStatus( tr_natpmp * );
    34 void        tr_natpmpForwardPort( tr_natpmp *, int );
    35 void        tr_natpmpRemoveForwarding( tr_natpmp * );
    36 void        tr_natpmpPulse( tr_natpmp *, int * );
     18tr_natpmp * tr_natpmpInit( void );
    3719void        tr_natpmpClose( tr_natpmp * );
    38 
    39 #define PMP_MCAST_ADDR "224.0.0.1"
     20int         tr_natpmpPulse( tr_natpmp *, int port, int isEnabled );
    4021
    4122#endif
  • trunk/libtransmission/net.c

    r4029 r4092  
    146146}
    147147
    148 int
    149 tr_netOpenUDP( const struct in_addr * addr, tr_port_t port, int priority )
    150 {
    151     return tr_netOpen( addr, port, SOCK_DGRAM, priority );
    152 }
    153 
    154 #ifdef IP_ADD_MEMBERSHIP
    155 int tr_netMcastOpen( int port, const struct in_addr * addr )
    156 {
    157     int fd;
    158     struct ip_mreq req;
    159 
    160     fd = tr_netBindUDP( port );
    161     if( 0 > fd )
    162     {
    163         return -1;
    164     }
    165 
    166     memset( &req, 0, sizeof( req ) );
    167     req.imr_multiaddr.s_addr = addr->s_addr;
    168     req.imr_interface.s_addr = htonl( INADDR_ANY );
    169     if( setsockopt( fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&req, sizeof ( req ) ) )
    170     {
    171         tr_err( "Couldn't join multicast group (%s)", strerror( sockerrno ) );
    172         tr_netClose( fd );
    173         return -1;
    174     }
    175 
    176     return fd;
    177 }
    178 #else /* IP_ADD_MEMBERSHIP */
    179 int tr_netMcastOpen( int port UNUSED, const struct in_addr * addr UNUSED )
    180 {
    181     return -1;
    182 }
    183 #endif /* IP_ADD_MEMBERSHIP */
    184 
    185148static int
    186149tr_netBind( int port, int type )
     
    202165#endif
    203166
    204 #ifdef SO_REUSEPORT
    205     if( SOCK_DGRAM == type )
    206     {
    207         optval = 1;
    208         setsockopt( s, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof( optval ) );
    209     }
    210 #endif
    211 
    212167    memset( &sock, 0, sizeof( sock ) );
    213168    sock.sin_family      = AF_INET;
    214 
    215     /* Leopard closes a SO_REUSEADDR + INADDR_ANY hole, so we can't
    216      * use INADDR_ANY when binding for nat-pmp. For details, refer to
    217      * http://www.uwsg.indiana.edu/hypermail/linux/kernel/9902.1/0828.html .
    218      * This can probably be done cleaner, but since we're only using SOCK_DGRAM
    219      * for nat-pmp, this quick fix should work. */
    220     if ( SOCK_DGRAM == type )
    221         sock.sin_addr.s_addr = inet_addr( PMP_MCAST_ADDR );
    222     else
    223         sock.sin_addr.s_addr = INADDR_ANY;
    224 
     169    sock.sin_addr.s_addr = INADDR_ANY;
    225170    sock.sin_port        = htons( port );
    226171
     
    241186    return tr_netBind( port, SOCK_STREAM );
    242187}
    243 
    244 int
    245 tr_netBindUDP( int port )
    246 {
    247     return tr_netBind( port, SOCK_DGRAM );
    248 }
    249 
    250188
    251189int
  • trunk/libtransmission/net.h

    r3707 r4092  
    7070int  tr_netOpenTCP  ( const struct in_addr * addr, tr_port_t port, int priority );
    7171int  tr_netOpenUDP  ( const struct in_addr * addr, tr_port_t port, int priority );
    72 int  tr_netMcastOpen( int port, const struct in_addr * addr );
    7372int  tr_netBindTCP  ( int port );
    7473int  tr_netBindUDP  ( int port );
  • trunk/libtransmission/platform.c

    r4089 r4092  
    444444    return buf;
    445445}
    446 
    447 /***
    448 ****  SOCKETS
    449 ***/
    450 
    451 #ifdef BSD
    452 
    453 #include <sys/types.h>
    454 #include <sys/socket.h>
    455 #include <netinet/in.h>
    456 #include <arpa/inet.h>
    457 #include <netinet/in.h> /* struct in_addr */
    458 #include <sys/sysctl.h>
    459 #include <net/route.h>
    460 
    461 static uint8_t *
    462 getroute( int * buflen );
    463 static int
    464 parseroutes( uint8_t * buf, int len, struct in_addr * addr );
    465 
    466 int
    467 tr_getDefaultRoute( struct in_addr * addr )
    468 {
    469     uint8_t * buf;
    470     int len;
    471 
    472     buf = getroute( &len );
    473     if( NULL == buf )
    474     {
    475         tr_err( "failed to get default route (BSD)" );
    476         return 1;
    477     }
    478 
    479     len = parseroutes( buf, len, addr );
    480     free( buf );
    481 
    482     return len;
    483 }
    484 
    485 #ifndef SA_SIZE
    486 #define ROUNDUP( a, size ) \
    487     ( ( (a) & ( (size) - 1 ) ) ? ( 1 + ( (a) | ( (size) - 1 ) ) ) : (a) )
    488 #define SA_SIZE( sap ) \
    489     ( sap->sa_len ? ROUNDUP( (sap)->sa_len, sizeof( u_long ) ) : \
    490                     sizeof( u_long ) )
    491 #endif /* !SA_SIZE */
    492 #define NEXT_SA( sap ) \
    493     (struct sockaddr *) ( (caddr_t) (sap) + ( SA_SIZE( (sap) ) ) )
    494 
    495 static uint8_t *
    496 getroute( int * buflen )
    497 {
    498     int     mib[6];
    499     size_t  len;
    500     uint8_t * buf;
    501 
    502     mib[0] = CTL_NET;
    503     mib[1] = PF_ROUTE;
    504     mib[2] = 0;
    505     mib[3] = AF_INET;
    506     mib[4] = NET_RT_FLAGS;
    507     mib[5] = RTF_GATEWAY;
    508 
    509     if( sysctl( mib, 6, NULL, &len, NULL, 0 ) )
    510     {
    511         if( ENOENT != errno )
    512         {
    513             tr_err( "sysctl net.route.0.inet.flags.gateway failed (%s)",
    514                     strerror( sockerrno ) );
    515         }
    516         *buflen = 0;
    517         return NULL;
    518     }
    519 
    520     buf = malloc( len );
    521     if( NULL == buf )
    522     {
    523         *buflen = 0;
    524         return NULL;
    525     }
    526 
    527     if( sysctl( mib, 6, buf, &len, NULL, 0 ) )
    528     {
    529         tr_err( "sysctl net.route.0.inet.flags.gateway failed (%s)",
    530                 strerror( sockerrno ) );
    531         free( buf );
    532         *buflen = 0;
    533         return NULL;
    534     }
    535 
    536     *buflen = len;
    537 
    538     return buf;
    539 }
    540 
    541 static int
    542 parseroutes( uint8_t * buf, int len, struct in_addr * addr )
    543 {
    544     uint8_t            * end;
    545     struct rt_msghdr   * rtm;
    546     struct sockaddr    * sa;
    547     struct sockaddr_in * sin;
    548     int                  ii;
    549     struct in_addr       dest, gw;
    550 
    551     end = buf + len;
    552     while( end > buf + sizeof( *rtm ) )
    553     {
    554         rtm = (struct rt_msghdr *) buf;
    555         buf += rtm->rtm_msglen;
    556         if( end >= buf )
    557         {
    558             dest.s_addr = INADDR_NONE;
    559             gw.s_addr   = INADDR_NONE;
    560             sa = (struct sockaddr *) ( rtm + 1 );
    561 
    562             for( ii = 0; ii < RTAX_MAX && (uint8_t *) sa < buf; ii++ )
    563             {
    564                 if( buf < (uint8_t *) NEXT_SA( sa ) )
    565                 {
    566                     break;
    567                 }
    568 
    569                 if( rtm->rtm_addrs & ( 1 << ii ) )
    570                 {
    571                     if( AF_INET == sa->sa_family )
    572                     {
    573                         sin = (struct sockaddr_in *) sa;
    574                         switch( ii )
    575                         {
    576                             case RTAX_DST:
    577                                 dest = sin->sin_addr;
    578                                 break;
    579                             case RTAX_GATEWAY:
    580                                 gw = sin->sin_addr;
    581                                 break;
    582                         }
    583                     }
    584                     sa = NEXT_SA( sa );
    585                 }
    586             }
    587 
    588             if( INADDR_ANY == dest.s_addr && INADDR_NONE != gw.s_addr )
    589             {
    590                 *addr = gw;
    591                 return 0;
    592             }
    593         }
    594     }
    595 
    596     return 1;
    597 }
    598 
    599 #elif defined( linux ) || defined( __linux ) || defined( __linux__ )
    600 
    601 #include <linux/types.h>
    602 #include <linux/netlink.h>
    603 #include <linux/rtnetlink.h>
    604 
    605 #define SEQNUM 195909
    606 
    607 static int
    608 getsock( void );
    609 static uint8_t *
    610 getroute( int fd, unsigned int * buflen );
    611 static int
    612 parseroutes( uint8_t * buf, unsigned int len, struct in_addr * addr );
    613 
    614 int
    615 tr_getDefaultRoute( struct in_addr * addr )
    616 {
    617     int fd, ret;
    618     unsigned int len;
    619     uint8_t * buf;
    620 
    621     ret = 1;
    622     fd = getsock();
    623     if( 0 <= fd )
    624     {
    625         while( ret )
    626         {
    627             buf = getroute( fd, &len );
    628             if( NULL == buf )
    629             {
    630                 break;
    631             }
    632             ret = parseroutes( buf, len, addr );
    633             free( buf );
    634         }
    635         close( fd );
    636     }
    637 
    638     if( ret )
    639     {
    640         tr_err( "failed to get default route (Linux)" );
    641     }
    642 
    643     return ret;
    644 }
    645 
    646 static int
    647 getsock( void )
    648 {
    649     int fd, flags;
    650     struct
    651     {
    652         struct nlmsghdr nlh;
    653         struct rtgenmsg rtg;
    654     } req;
    655     struct sockaddr_nl snl;
    656 
    657     fd = socket( PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE );
    658     if( 0 > fd )
    659     {
    660         tr_err( "failed to create routing socket (%s)", strerror( sockerrno ) );
    661         return -1;
    662     }
    663 
    664     flags = fcntl( fd, F_GETFL );
    665     if( 0 > flags || 0 > fcntl( fd, F_SETFL, O_NONBLOCK | flags ) )
    666     {
    667         tr_err( "failed to set socket nonblocking (%s)", strerror( sockerrno ) );
    668         close( fd );
    669         return -1;
    670     }
    671 
    672     memset( &snl, 0, sizeof(snl) );
    673     snl.nl_family = AF_NETLINK;
    674 
    675     memset( &req, 0, sizeof(req) );
    676     req.nlh.nlmsg_len = NLMSG_LENGTH( sizeof( req.rtg ) );
    677     req.nlh.nlmsg_type = RTM_GETROUTE;
    678     req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
    679     req.nlh.nlmsg_seq = SEQNUM;
    680     req.nlh.nlmsg_pid = 0;
    681     req.rtg.rtgen_family = AF_INET;
    682 
    683     if( 0 > sendto( fd, &req, sizeof( req ), 0,
    684                     (struct sockaddr *) &snl, sizeof( snl ) ) )
    685     {
    686         tr_err( "failed to write to routing socket (%s)", strerror( sockerrno ) );
    687         close( fd );
    688         return -1;
    689     }
    690 
    691     return fd;
    692 }
    693 
    694 static uint8_t *
    695 getroute( int fd, unsigned int * buflen )
    696 {
    697     void             * buf;
    698     unsigned int       len;
    699     ssize_t            res;
    700     struct sockaddr_nl snl;
    701     socklen_t          slen;
    702 
    703     len = 8192;
    704     buf = calloc( 1, len );
    705     if( NULL == buf )
    706     {
    707         *buflen = 0;
    708         return NULL;
    709     }
    710 
    711     for( ;; )
    712     {
    713         slen = sizeof( snl );
    714         memset( &snl, 0, slen );
    715         res = recvfrom( fd, buf, len, 0, (struct sockaddr *) &snl, &slen );
    716         if( 0 > res )
    717         {
    718             if( EAGAIN != sockerrno )
    719             {
    720                 tr_err( "failed to read from routing socket (%s)",
    721                         strerror( sockerrno ) );
    722             }
    723             free( buf );
    724             *buflen = 0;
    725             return NULL;
    726         }
    727         if( slen < sizeof( snl ) || AF_NETLINK != snl.nl_family )
    728         {
    729             tr_err( "bad address" );
    730             free( buf );
    731             *buflen = 0;
    732             return NULL;
    733         }
    734 
    735         if( 0 == snl.nl_pid )
    736         {
    737             break;
    738         }
    739     }
    740 
    741     *buflen = res;
    742 
    743     return buf;
    744 }
    745 
    746 static int
    747 parseroutes( uint8_t * buf, unsigned int len, struct in_addr * addr )
    748 {
    749     struct nlmsghdr * nlm;
    750     struct nlmsgerr * nle;
    751     struct rtmsg    * rtm;
    752     struct rtattr   * rta;
    753     int               rtalen;
    754     struct in_addr    gw, dst;
    755 
    756     nlm = ( struct nlmsghdr * ) buf;
    757     while( NLMSG_OK( nlm, len ) )
    758     {
    759         gw.s_addr = INADDR_ANY;
    760         dst.s_addr = INADDR_ANY;
    761         if( NLMSG_ERROR == nlm->nlmsg_type )
    762         {
    763             nle = (struct nlmsgerr *) NLMSG_DATA( nlm );
    764             if( NLMSG_LENGTH( NLMSG_ALIGN( sizeof( struct nlmsgerr ) ) ) >
    765                 nlm->nlmsg_len )
    766             {
    767                 tr_err( "truncated netlink error" );
    768             }
    769             else
    770             {
    771                 tr_err( "netlink error (%s)", strerror( nle->error ) );
    772             }
    773             return 1;
    774         }
    775         else if( RTM_NEWROUTE == nlm->nlmsg_type && SEQNUM == nlm->nlmsg_seq &&
    776                  getpid() == (pid_t) nlm->nlmsg_pid &&
    777                  NLMSG_LENGTH( sizeof( struct rtmsg ) ) <= nlm->nlmsg_len )
    778         {
    779             rtm = NLMSG_DATA( nlm );
    780             rta = RTM_RTA( rtm );
    781             rtalen = RTM_PAYLOAD( nlm );
    782 
    783             while( RTA_OK( rta, rtalen ) )
    784             {
    785                 if( sizeof( struct in_addr ) <= RTA_PAYLOAD( rta ) )
    786                 {
    787                     switch( rta->rta_type )
    788                     {
    789                         case RTA_GATEWAY:
    790                             memcpy( &gw, RTA_DATA( rta ), sizeof( gw ) );
    791                             break;
    792                         case RTA_DST:
    793                             memcpy( &dst, RTA_DATA( rta ), sizeof( dst ) );
    794                             break;
    795                     }
    796                 }
    797                 rta = RTA_NEXT( rta, rtalen );
    798             }
    799         }
    800 
    801         if( INADDR_NONE != gw.s_addr && INADDR_ANY != gw.s_addr &&
    802             INADDR_ANY == dst.s_addr )
    803         {
    804             *addr = gw;
    805             return 0;
    806         }
    807 
    808         nlm = NLMSG_NEXT( nlm, len );
    809     }
    810 
    811     return 1;
    812 }
    813 
    814 #else /* not BSD or Linux */
    815 
    816 int
    817 tr_getDefaultRoute( struct in_addr * addr UNUSED )
    818 {
    819     tr_inf( "don't know how to get default route on this platform" );
    820     return 1;
    821 }
    822 
    823 #endif
  • trunk/libtransmission/platform.h

    r4043 r4092  
    4242int          tr_lockHave       ( const tr_lock * );
    4343
    44 struct in_addr; /* forward declaration to calm gcc down */
    45 int
    46 tr_getDefaultRoute( struct in_addr * addr );
    47 
    4844#endif
  • trunk/libtransmission/shared.c

    r3796 r4092  
    2323 *****************************************************************************/
    2424
    25 #include <assert.h>
    26 #include <stdlib.h>
    2725#include <string.h>
    2826#include <stdio.h>
     
    3129
    3230#include "transmission.h"
    33 #include "handshake.h"
    3431#include "natpmp.h"
    3532#include "net.h"
    36 #include "peer-io.h"
    3733#include "peer-mgr.h"
    38 #include "platform.h"
    3934#include "shared.h"
    4035#include "trevent.h"
     
    4439struct tr_shared
    4540{
    46     tr_handle    * h;
    47     tr_timer     * pulseTimer;
     41    tr_handle * h;
     42    tr_timer  * pulseTimer;
    4843
    4944    /* Incoming connections */
    50     int publicPort;
    5145    int bindPort;
    5246    int bindSocket;
    5347
    54     /* NAT-PMP/UPnP */
     48    /* port forwarding */
     49    int isEnabled;
     50    int publicPort;
     51    tr_nat_traversal_status natStatus;
     52    tr_upnp * upnp;
    5553    tr_natpmp  * natpmp;
    56     tr_upnp    * upnp;
     54
     55    int isShuttingDown;
    5756};
    5857
    59 /***********************************************************************
    60  * Local prototypes
    61  **********************************************************************/
    62 static int SharedLoop( void * );
    63 static void SetPublicPort( tr_shared *, int );
    64 static void AcceptPeers( tr_shared * );
     58#define NATKEY "Port Mapping: "
    6559
     60/***
     61****
     62***/
    6663
    67 /***********************************************************************
    68  * tr_sharedInit
    69  ***********************************************************************
    70  *
    71  **********************************************************************/
    72 tr_shared * tr_sharedInit( tr_handle * h )
     64static const char*
     65getNatStateStr( int state )
    7366{
    74     tr_shared * s = calloc( 1, sizeof( tr_shared ) );
     67    switch( state )
     68    {
     69        case TR_NAT_TRAVERSAL_MAPPING:   return "mapping";
     70        case TR_NAT_TRAVERSAL_MAPPED:    return "mapped";
     71        case TR_NAT_TRAVERSAL_UNMAPPING: return "unmapping";
     72        case TR_NAT_TRAVERSAL_UNMAPPED:  return "unmapped";
     73        case TR_NAT_TRAVERSAL_ERROR:     return "error";
     74    }
    7575
    76     s->h          = h;
    77     s->publicPort = -1;
    78     s->bindPort   = -1;
    79     s->bindSocket = -1;
    80     s->natpmp     = tr_natpmpInit();
    81     s->upnp       = tr_upnpInit();
    82     s->pulseTimer   = tr_timerNew( h, SharedLoop, s, 500 );
    83 
    84     return s;
     76    return "notfound";
    8577}
    8678
    87 /***********************************************************************
    88  * tr_sharedClose
    89  ***********************************************************************
    90  *
    91  **********************************************************************/
    92 void tr_sharedClose( tr_shared * s )
     79static void
     80natPulse( tr_shared * s )
    9381{
    94     tr_timerFree( &s->pulseTimer );
     82    tr_nat_traversal_status status;
     83    const int port = s->publicPort;
     84    const int isEnabled = s->isEnabled && !s->isShuttingDown;
    9585
    96     tr_netClose( s->bindSocket );
    97     tr_natpmpClose( s->natpmp );
    98     tr_upnpClose( s->upnp );
    99     free( s );
    100 }
    101 
    102 /***********************************************************************
    103  * tr_sharedSetPort
    104  ***********************************************************************
    105  *
    106  **********************************************************************/
    107 void tr_sharedSetPort( tr_shared * s, int port )
    108 {
    109 #ifdef BEOS_NETSERVER
    110     /* BeOS net_server seems to be unable to set incoming connections
    111      * to non-blocking. Too bad. */
    112     return;
    113 #endif
    114 
    115     tr_globalLock( s->h );
    116 
    117     if( port == s->bindPort )
    118     {
    119         tr_globalUnlock( s->h );
    120         return;
    121     }
    122     s->bindPort = port;
    123 
    124     /* Close the previous accept socket, if any */
    125     if( s->bindSocket > -1 )
    126     {
    127         tr_netClose( s->bindSocket );
    128     }
    129 
    130     /* Create the new one */
    131     s->bindSocket = tr_netBindTCP( port );
    132 
    133     /* Notify the trackers */
    134     SetPublicPort( s, port );
    135 
    136     /* XXX should handle failure here in a better way */
    137     if( s->bindSocket < 0 )
    138     {
    139         /* Remove the forwarding for the old port */
    140         tr_natpmpRemoveForwarding( s->natpmp );
    141         tr_upnpRemoveForwarding( s->upnp );
    142     }
    143     else
    144     {
    145         tr_inf( "Bound listening port %d", port );
    146         listen( s->bindSocket, 5 );
    147         /* Forward the new port */
    148         tr_natpmpForwardPort( s->natpmp, port );
    149         tr_upnpForwardPort( s->upnp, port );
    150     }
    151 
    152     tr_globalUnlock( s->h );
    153 }
    154 
    155 int
    156 tr_sharedGetPublicPort( const tr_shared * s )
    157 {
    158     return s->publicPort;
    159 }
    160 
    161 /***********************************************************************
    162  * tr_sharedTraversalEnable, tr_sharedTraversalStatus
    163  ***********************************************************************
    164  *
    165  **********************************************************************/
    166 void tr_sharedTraversalEnable( tr_shared * s, int enable )
    167 {
    168     if( enable )
    169     {
    170         tr_natpmpStart( s->natpmp );
    171         tr_upnpStart( s->upnp );
    172     }
    173     else
    174     {
    175         tr_natpmpStop( s->natpmp );
    176         tr_upnpStop( s->upnp );
     86    status = tr_natpmpPulse( s->natpmp, port, isEnabled );
     87    if( status == TR_NAT_TRAVERSAL_ERROR )
     88        status = tr_upnpPulse( s->upnp, port, isEnabled );
     89    if( status != s->natStatus ) {
     90        tr_inf( NATKEY "mapping state changed from '%s' to '%s'", getNatStateStr(s->natStatus), getNatStateStr(status) );
     91        s->natStatus = status;
    17792    }
    17893}
    17994
    180 int tr_sharedTraversalStatus( const tr_shared * s )
    181 {
    182     const int statuses[] = {
    183         TR_NAT_TRAVERSAL_MAPPED,
    184         TR_NAT_TRAVERSAL_MAPPING,
    185         TR_NAT_TRAVERSAL_UNMAPPING,
    186         TR_NAT_TRAVERSAL_ERROR,
    187         TR_NAT_TRAVERSAL_NOTFOUND,
    188         TR_NAT_TRAVERSAL_DISABLED,
    189         -1,
    190     };
    191     int natpmp, upnp, ii;
    192 
    193     natpmp = tr_natpmpStatus( s->natpmp );
    194     upnp = tr_upnpStatus( s->upnp );
    195 
    196     for( ii = 0; 0 <= statuses[ii]; ii++ )
    197     {
    198         if( statuses[ii] == natpmp || statuses[ii] == upnp )
    199         {
    200             return statuses[ii];
    201         }
    202     }
    203 
    204     assert( 0 );
    205 
    206     return TR_NAT_TRAVERSAL_ERROR;
    207 
    208 }
    209 
    210 
    211 /***********************************************************************
    212  * Local functions
    213  **********************************************************************/
    214 
    215 /***********************************************************************
    216  * SharedLoop
    217  **********************************************************************/
    218 static int
    219 SharedLoop( void * vs )
    220 {
    221     int newPort;
    222     tr_shared * s = vs;
    223 
    224     tr_globalLock( s->h );
    225 
    226     /* NAT-PMP and UPnP pulses */
    227     newPort = -1;
    228     tr_natpmpPulse( s->natpmp, &newPort );
    229     if( 0 < newPort && newPort != s->publicPort )
    230         SetPublicPort( s, newPort );
    231     tr_upnpPulse( s->upnp );
    232 
    233     /* Handle incoming connections */
    234     AcceptPeers( s );
    235 
    236     tr_globalUnlock( s->h );
    237 
    238     return TRUE;
    239 }
    240 
    241 /***********************************************************************
    242  * SetPublicPort
    243  **********************************************************************/
    244 static void SetPublicPort( tr_shared * s, int port )
    245 {
    246     tr_handle * h = s->h;
    247     tr_torrent * tor;
    248 
    249     s->publicPort = port;
    250 
    251     for( tor = h->torrentList; tor; tor = tor->next )
    252         tr_torrentChangeMyPort( tor );
    253 }
    254 
    255 /***********************************************************************
    256  * AcceptPeers
    257  ***********************************************************************
    258  * Check incoming connections and add the peers to our local list
    259  **********************************************************************/
    260 
    26195static void
    262 AcceptPeers( tr_shared * s )
     96checkForIncomingPeers( tr_shared * s )
    26397{
    26498    for( ;; )
     
    278112    }
    279113}
     114
     115static int
     116sharedPulse( void * vshared )
     117{
     118    int keepPulsing = 1;
     119    tr_shared * shared = vshared;
     120
     121    natPulse( shared );
     122
     123    if( !shared->isShuttingDown )
     124    {
     125        checkForIncomingPeers( shared );
     126    }
     127    else if( ( shared->natStatus == TR_NAT_TRAVERSAL_ERROR ) || ( shared->natStatus == TR_NAT_TRAVERSAL_UNMAPPED ) )
     128    {
     129        tr_dbg( NATKEY "port mapping shut down" );
     130        shared->h->shared = NULL;
     131        tr_netClose( shared->bindSocket );
     132        tr_natpmpClose( shared->natpmp );
     133        tr_upnpClose( shared->upnp );
     134        tr_free( shared );
     135        keepPulsing = 0;
     136    }
     137
     138    return keepPulsing;
     139}
     140
     141/***
     142****
     143***/
     144
     145tr_shared *
     146tr_sharedInit( tr_handle * h )
     147{
     148    tr_shared * s = tr_new0( tr_shared, 1 );
     149
     150    s->h            = h;
     151    s->publicPort   = -1;
     152    s->bindPort     = -1;
     153    s->bindSocket   = -1;
     154    s->natpmp       = tr_natpmpInit();
     155    s->upnp         = tr_upnpInit();
     156    s->pulseTimer   = tr_timerNew( h, sharedPulse, s, 500 );
     157    s->isEnabled    = 0;
     158    s->natStatus    = TR_NAT_TRAVERSAL_UNMAPPED;
     159
     160    return s;
     161}
     162
     163void
     164tr_sharedShuttingDown( tr_shared * s )
     165{
     166    s->isShuttingDown = 1;
     167}
     168
     169void
     170tr_sharedSetPort( tr_shared * s, int port )
     171{
     172    tr_torrent * tor;
     173
     174    s->publicPort = port;
     175
     176    for( tor = s->h->torrentList; tor; tor = tor->next )
     177        tr_torrentChangeMyPort( tor );
     178}
     179
     180int
     181tr_sharedGetPublicPort( const tr_shared * s )
     182{
     183    return s->publicPort;
     184}
     185
     186void
     187tr_sharedTraversalEnable( tr_shared * s, int isEnabled )
     188{
     189    s->isEnabled = isEnabled;
     190}
     191
     192int
     193tr_sharedTraversalStatus( const tr_shared * s )
     194{
     195    return s->natStatus;
     196}
  • trunk/libtransmission/shared.h

    r3254 r4092  
    3030typedef struct tr_shared tr_shared;
    3131
    32 /***********************************************************************
    33  * tr_sharedInit, tr_sharedClose
    34  ***********************************************************************
    35  * Starts / stops a thread to handle running things that are shared
    36  * among the torrents: NAT-PMP/UPnP, incoming connections, peer choking
    37  **********************************************************************/
    38 tr_shared * tr_sharedInit           ( tr_handle * );
    39 void        tr_sharedClose          ( tr_shared * );
    40 
    41 /***********************************************************************
    42  * tr_sharedSetPort
    43  ***********************************************************************
    44  * Changes the port for incoming connections.  tr_sharedGetPublicPort
    45  * should be called with the shared lock held.
    46  **********************************************************************/
    47 void         tr_sharedSetPort         ( tr_shared *, int port );
    48 int          tr_sharedGetPublicPort   ( const tr_shared * s );
    49 
    50 /***********************************************************************
    51  * tr_sharedTraversalEnable, tr_sharedTraversalStatus
    52  ***********************************************************************
    53  * Enables/disables and retrieves the status of NAT traversal.  Should
    54  * be called with the shared lock held.
    55  **********************************************************************/
    56 void         tr_sharedTraversalEnable ( tr_shared *, int enable );
    57 int          tr_sharedTraversalStatus ( const tr_shared * );
    58 
     32tr_shared* tr_sharedInit            ( tr_handle * );
     33void       tr_sharedShuttingDown    ( tr_shared * );
     34void       tr_sharedSetPort         ( tr_shared *, int publicPort );
     35void       tr_sharedTraversalEnable ( tr_shared *, int isEnabled );
     36int        tr_sharedGetPublicPort   ( const tr_shared * s );
     37int        tr_sharedTraversalStatus ( const tr_shared * );
    5938
    6039#endif
    61 
  • trunk/libtransmission/transmission.c

    r4008 r4092  
    325325    tr_torrent * t;
    326326
     327    tr_sharedShuttingDown( h->shared );
    327328    tr_trackerShuttingDown( h );
    328329
     
    335336    tr_rcClose( h->download );
    336337   
    337     tr_natTraversalEnable( h, 0 );
    338     while( tr_handleStatus( h )->natTraversalStatus != TR_NAT_TRAVERSAL_DISABLED )
    339         tr_wait( 100 );
    340 
    341     tr_sharedClose( h->shared );
    342     tr_fdClose();
    343 
    344338    h->isClosed = TRUE;
    345339}
     
    367361        tr_wait( 100 );
    368362
     363    tr_fdClose( );
    369364    tr_statsClose( h );
    370365    tr_lockFree( h->lock );
  • trunk/libtransmission/transmission.h

    r4002 r4092  
    751751    time_t               when;
    752752    char               * message;
     753    const char         * file;
     754    int                  line;
    753755    struct tr_msg_list * next;
    754756};
    755757
     758typedef enum
     759{
     760    TR_NAT_TRAVERSAL_MAPPING,
     761    TR_NAT_TRAVERSAL_MAPPED,
     762    TR_NAT_TRAVERSAL_UNMAPPING,
     763    TR_NAT_TRAVERSAL_UNMAPPED,
     764    TR_NAT_TRAVERSAL_ERROR,
     765}
     766tr_nat_traversal_status;
     767
    756768struct tr_handle_status
    757769{
    758 #define TR_NAT_TRAVERSAL_MAPPING        1
    759 #define TR_NAT_TRAVERSAL_MAPPED         2
    760 #define TR_NAT_TRAVERSAL_NOTFOUND       3
    761 #define TR_NAT_TRAVERSAL_ERROR          4
    762 #define TR_NAT_TRAVERSAL_UNMAPPING      5
    763 #define TR_NAT_TRAVERSAL_DISABLED       6
    764 #define TR_NAT_TRAVERSAL_IS_DISABLED( st ) \
    765   ( TR_NAT_TRAVERSAL_DISABLED == (st) || TR_NAT_TRAVERSAL_UNMAPPING == (st) )
    766     int natTraversalStatus;
     770    tr_nat_traversal_status natTraversalStatus;
    767771    int publicPort;
    768772};
  • trunk/libtransmission/upnp.c

    r3815 r4092  
    1111 */
    1212
    13 #include <stdio.h> /* printf */
     13#include <assert.h>
     14#include <stdio.h> /* snprintf */
    1415
    1516#include <miniupnp/miniwget.h>
     
    1920#include "transmission.h"
    2021#include "internal.h"
     22#include "shared.h"
    2123#include "utils.h"
    2224#include "upnp.h"
     25
     26#define KEY "Port Mapping (UPNP): "
     27
     28typedef enum
     29{
     30    TR_UPNP_IDLE,
     31    TR_UPNP_ERR,
     32    TR_UPNP_DISCOVER,
     33    TR_UPNP_MAP,
     34    TR_UPNP_UNMAP
     35}
     36tr_upnp_state;
    2337
    2438struct tr_upnp
     
    2842    int port;
    2943    char lanaddr[16];
    30     unsigned int isForwarding : 1;
    31     unsigned int isEnabled : 1;
     44    unsigned int isMapped;
    3245    unsigned int hasDiscovered : 1;
     46    tr_upnp_state state;
    3347};
    3448
     
    4862tr_upnpClose( tr_upnp * handle )
    4963{
    50     tr_upnpStop( handle );
     64    assert( !handle->isMapped );
     65    assert( ( handle->state == TR_UPNP_IDLE ) || ( handle->state == TR_UPNP_ERR ) );
     66
    5167    if( handle->hasDiscovered )
    5268        FreeUPNPUrls( &handle->urls );
     
    5874**/
    5975
    60 void
    61 tr_upnpStart( tr_upnp * handle )
     76int
     77tr_upnpPulse( tr_upnp * handle, int port, int isEnabled )
    6278{
    63     if( !handle->hasDiscovered )
     79    int ret;
     80
     81    if( handle->state == TR_UPNP_IDLE )
     82    {
     83        if( !handle->hasDiscovered )
     84            handle->state = TR_UPNP_DISCOVER;
     85    }
     86
     87    if( handle->state == TR_UPNP_DISCOVER )
    6488    {
    6589        struct UPNPDev * devlist = upnpDiscover( 2000, NULL );
    6690        if( UPNP_GetValidIGD( devlist, &handle->urls, &handle->data, handle->lanaddr, sizeof(handle->lanaddr))) {
    67             tr_dbg( "UPNP: Found Internet Gateway Device '%s'", handle->urls.controlURL );
    68             tr_dbg( "UPNP: Local LAN IP Address is '%s'", handle->lanaddr );
     91            tr_inf( KEY "found Internet Gateway Device '%s'", handle->urls.controlURL );
     92            tr_inf( KEY "local LAN IP Address is '%s'", handle->lanaddr );
     93            handle->state = TR_UPNP_IDLE;
     94            handle->hasDiscovered = 1;
     95        } else {
     96            handle->state = TR_UPNP_ERR;
    6997        }
    7098        freeUPNPDevlist( devlist );
    71         handle->hasDiscovered = 1;
    7299    }
    73100
    74     handle->isEnabled = 1;
     101    if( handle->state == TR_UPNP_IDLE )
     102    {
     103        if( handle->isMapped && ( !isEnabled || ( handle->port != port ) ) )
     104            handle->state = TR_UPNP_UNMAP;
     105    }
    75106
    76     if( handle->port >= 0 )
     107    if( handle->state == TR_UPNP_UNMAP )
    77108    {
    78109        char portStr[16];
    79110        snprintf( portStr, sizeof(portStr), "%d", handle->port );
    80         handle->isForwarding = ( handle->urls.controlURL != NULL ) &&
    81                                ( handle->data.servicetype != NULL ) &&
    82                                ( UPNP_AddPortMapping( handle->urls.controlURL,
    83                                                       handle->data.servicetype,
    84                                                       portStr, portStr, handle->lanaddr,
    85                                                       "Transmission", "TCP" ) );
    86 
    87         tr_inf( "UPNP: Port Forwarding via '%s', service '%s'.  (local address: %s:%d)",
    88                 handle->urls.controlURL, handle->data.servicetype, handle->lanaddr, handle->port );
    89         tr_inf( "UPNP: Port Forwarding Enabled?  %s", (handle->isForwarding?"Yes":"No") );
    90     }
    91 }
    92 
    93 void
    94 tr_upnpRemoveForwarding ( tr_upnp * handle )
    95 {
    96     handle->port = -1;
    97 
    98     if( handle->isForwarding )
    99     {
    100         char portStr[16];
    101         snprintf( portStr, sizeof(portStr), "%d", handle->port );
    102 
    103111        UPNP_DeletePortMapping( handle->urls.controlURL,
    104112                                handle->data.servicetype,
    105113                                portStr, "TCP" );
    106         tr_dbg( "Stopping port forwarding of '%s', service '%s'",
     114        tr_dbg( KEY "stopping port forwarding of '%s', service '%s'",
    107115                handle->urls.controlURL, handle->data.servicetype );
     116        handle->isMapped = 0;
     117        handle->state = TR_UPNP_IDLE;
     118        handle->port = -1;
     119    }
    108120
    109         handle->isForwarding = FALSE;
     121    if( handle->state == TR_UPNP_IDLE )
     122    {
     123        if( isEnabled && !handle->isMapped )
     124            handle->state = TR_UPNP_MAP;
    110125    }
     126
     127    if( handle->state == TR_UPNP_MAP )
     128    {
     129        char portStr[16];
     130        snprintf( portStr, sizeof(portStr), "%d", port );
     131        handle->isMapped = ( handle->urls.controlURL != NULL ) &&
     132                           ( handle->data.servicetype != NULL ) &&
     133                           ( UPNP_AddPortMapping( handle->urls.controlURL,
     134                                                  handle->data.servicetype,
     135                                                  portStr, portStr, handle->lanaddr,
     136                                                  "Transmission", "TCP" ) );
     137        tr_inf( KEY "port forwarding via '%s', service '%s'.  (local address: %s:%d)",
     138                handle->urls.controlURL, handle->data.servicetype, handle->lanaddr, handle->port );
     139        if( handle->isMapped ) {
     140            tr_inf( KEY "port forwarding successful!" );
     141            handle->port = port;
     142            handle->state = TR_UPNP_IDLE;
     143        } else {
     144            tr_err( KEY "port forwarding failed" );
     145            handle->port = -1;
     146            handle->state = TR_UPNP_ERR;
     147        }
     148    }
     149
     150    if( handle->state == TR_UPNP_ERR )
     151        ret = TR_NAT_TRAVERSAL_ERROR;
     152    else if( ( handle->state == TR_UPNP_IDLE ) && handle->isMapped )
     153        ret = TR_NAT_TRAVERSAL_MAPPED;
     154    else if( ( handle->state == TR_UPNP_IDLE ) && !handle->isMapped )
     155        ret = TR_NAT_TRAVERSAL_UNMAPPED;
     156    else if( handle->state == TR_UPNP_MAP )
     157        ret = TR_NAT_TRAVERSAL_MAPPING;
     158    else if( handle->state == TR_UPNP_UNMAP )
     159        ret = TR_NAT_TRAVERSAL_UNMAPPING;
     160    return ret;
    111161}
    112 
    113 void
    114 tr_upnpForwardPort( tr_upnp * handle, int publicPort )
    115 {
    116     tr_upnpRemoveForwarding( handle ); /* remove the old forwarding */
    117 
    118     handle->port = publicPort;
    119 
    120     if( handle->isEnabled )
    121         tr_upnpStart( handle );
    122 }
    123 
    124 void
    125 tr_upnpStop( tr_upnp * handle )
    126 {
    127     tr_upnpRemoveForwarding( handle );
    128     handle->isEnabled = 0;
    129 }
    130 
    131 int
    132 tr_upnpStatus( tr_upnp * handle )
    133 {
    134     if( !handle->isEnabled )
    135         return TR_NAT_TRAVERSAL_DISABLED;
    136 
    137     if( !handle->isForwarding )
    138         return TR_NAT_TRAVERSAL_ERROR;
    139 
    140     return TR_NAT_TRAVERSAL_MAPPED;
    141 }
    142 
    143 void
    144 tr_upnpPulse( tr_upnp * handle UNUSED )
    145 {
    146     /* no-op */
    147 }
  • trunk/libtransmission/upnp.h

    r3731 r4092  
    1 /******************************************************************************
     1/*
     2 * This file Copyright (C) 2007 Charles Kerr <charles@rebelbase.com>
     3 *
     4 * This file is licensed by the GPL version 2.  Works owned by the
     5 * Transmission project are granted a special exemption to clause 2(b)
     6 * so that the bulk of its code can remain under the MIT license.
     7 * This exemption does not extend to derived works not owned by
     8 * the Transmission project.
     9 *
    210 * $Id$
    3  *
    4  * Copyright (c) 2006 Transmission authors and contributors
    5  *
    6  * Permission is hereby granted, free of charge, to any person obtaining a
    7  * copy of this software and associated documentation files (the "Software"),
    8  * to deal in the Software without restriction, including without limitation
    9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
    10  * and/or sell copies of the Software, and to permit persons to whom the
    11  * Software is furnished to do so, subject to the following conditions:
    12  *
    13  * The above copyright notice and this permission notice shall be included in
    14  * all copies or substantial portions of the Software.
    15  *
    16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    22  * DEALINGS IN THE SOFTWARE.
    23  *****************************************************************************/
     11 */
    2412
    2513#ifndef TR_UPNP_H
     
    2917
    3018tr_upnp * tr_upnpInit             ( void );
    31 void      tr_upnpStart            ( tr_upnp * );
    32 void      tr_upnpStop             ( tr_upnp * );
    33 int       tr_upnpStatus           ( tr_upnp * );
    34 void      tr_upnpForwardPort      ( tr_upnp *, int );
    35 void      tr_upnpRemoveForwarding ( tr_upnp * );
    36 void      tr_upnpPulse            ( tr_upnp * );
    3719void      tr_upnpClose            ( tr_upnp * );
     20int       tr_upnpPulse            ( tr_upnp *, int port, int isEnabled );
    3821
    3922#endif
  • trunk/libtransmission/utils.c

    r3995 r4092  
    188188}
    189189
    190 void tr_msg( int level, const char * fmt, ... )
     190void
     191tr_msg( const char * file, int line, int level, const char * fmt, ... )
    191192{
    192193    FILE * fp;
     
    223224                newmsg->when = time( NULL );
    224225                newmsg->message = text;
     226                newmsg->file = file;
     227                newmsg->line = line;
    225228
    226229                *messageQueueTail = newmsg;
  • trunk/libtransmission/utils.h

    r3991 r4092  
    3333void tr_msgInit( void );
    3434
    35 #define tr_err( a... ) tr_msg( TR_MSG_ERR, ## a )
    36 #define tr_inf( a... ) tr_msg( TR_MSG_INF, ## a )
    37 #define tr_dbg( a... ) tr_msg( TR_MSG_DBG, ## a )
    38 void tr_msg  ( int level, const char * msg, ... );
     35#define tr_err( a... ) tr_msg( __FILE__, __LINE__, TR_MSG_ERR, ## a )
     36#define tr_inf( a... ) tr_msg( __FILE__, __LINE__, TR_MSG_INF, ## a )
     37#define tr_dbg( a... ) tr_msg( __FILE__, __LINE__, TR_MSG_DBG, ## a )
     38void tr_msg  ( const char * file, int line, int level, const char * msg, ... );
    3939FILE* tr_getLog( void );
    4040
  • trunk/third-party/Makefile.am

    r3731 r4092  
    1 SUBDIRS = libevent miniupnp
     1SUBDIRS = \
     2    libevent \
     3    miniupnp \
     4    libnatpmp
    25
    36EXTRA_DIST = \
Note: See TracChangeset for help on using the changeset viewer.