source: branches/1.5x/libtransmission/port-forwarding.c @ 7847

Last change on this file since 7847 was 7847, checked in by jhujhiti, 12 years ago

(1.5x libT) backport r7839 (stop using ipv4-mapped listeners)

  • Property svn:keywords set to Date Rev Author Id
File size: 7.5 KB
Line 
1/*
2 * This file Copyright (C) 2008-2009 Charles Kerr <charles@transmissionbt.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 *
10 * $Id: port-forwarding.c 7847 2009-02-08 00:29:20Z jhujhiti $
11 */
12
13#include <errno.h>
14#include <string.h>
15#include <stdio.h>
16
17#include <sys/types.h>
18
19#include "transmission.h"
20#include "session.h"
21#include "natpmp.h"
22#include "net.h"
23#include "peer-io.h"
24#include "peer-mgr.h"
25#include "port-forwarding.h"
26#include "torrent.h"
27#include "trevent.h"
28#include "upnp.h"
29#include "utils.h"
30
31static const char *
32getKey( void ) { return _( "Port Forwarding" ); }
33
34struct tr_shared
35{
36    tr_bool               isEnabled;
37    tr_bool               isShuttingDown;
38
39    tr_port_forwarding    natpmpStatus;
40    tr_port_forwarding    upnpStatus;
41
42    tr_bool               shouldChange;
43    tr_socketList       * bindSockets;
44    tr_port               publicPort;
45
46    tr_timer            * pulseTimer;
47
48    tr_upnp             * upnp;
49    tr_natpmp           * natpmp;
50    tr_session          * session;
51};
52
53/***
54****
55***/
56
57static const char*
58getNatStateStr( int state )
59{
60    switch( state )
61    {
62        /* we're in the process of trying to set up port forwarding */
63        case TR_PORT_MAPPING:
64            return _( "Starting" );
65
66        /* we've successfully forwarded the port */
67        case TR_PORT_MAPPED:
68            return _( "Forwarded" );
69
70        /* we're cancelling the port forwarding */
71        case TR_PORT_UNMAPPING:
72            return _( "Stopping" );
73
74        /* the port isn't forwarded */
75        case TR_PORT_UNMAPPED:
76            return _( "Not forwarded" );
77
78        case TR_PORT_ERROR:
79            return "???";
80    }
81
82    return "notfound";
83}
84
85static void
86natPulse( tr_shared * s )
87{
88    const tr_port port = s->publicPort;
89    const int     isEnabled = s->isEnabled && !s->isShuttingDown;
90    int           oldStatus;
91    int           newStatus;
92
93    oldStatus = tr_sharedTraversalStatus( s );
94    s->natpmpStatus = tr_natpmpPulse( s->natpmp, port, isEnabled );
95    s->upnpStatus = tr_upnpPulse( s->upnp, port, isEnabled );
96    newStatus = tr_sharedTraversalStatus( s );
97
98    if( newStatus != oldStatus )
99        tr_ninf( getKey( ), _( "State changed from \"%1$s\" to \"%2$s\"" ),
100                getNatStateStr( oldStatus ),
101                getNatStateStr( newStatus ) );
102}
103
104/*
105 * Callbacks for socket list
106 */
107static void
108closeCb( int * const socket,
109         tr_address * const addr,
110         void * const userData )
111{
112    tr_shared * s = ( tr_shared * )userData;
113    if( *socket >= 0 )
114    {
115        tr_ninf( getKey( ), _( "Closing port %d on %s" ), s->publicPort,
116                tr_ntop_non_ts( addr ) );
117        tr_netClose( *socket );
118    }
119}
120
121static void
122acceptCb( int        * const socket,
123          tr_address * const addr UNUSED,
124          void       * const userData )
125{
126    tr_shared * s = ( tr_shared * )userData;
127    tr_address clientAddr;
128    tr_port clientPort;
129    int clientSocket;
130    clientSocket = tr_netAccept( s->session, *socket, &clientAddr, &clientPort );
131    if( clientSocket > 0 )
132    {
133        tr_deepLog( __FILE__, __LINE__, NULL,
134                   "New INCOMING connection %d (%s)",
135                   clientSocket, tr_peerIoAddrStr( &clientAddr, clientPort ) );
136       
137        tr_peerMgrAddIncoming( s->session->peerMgr, &clientAddr, clientPort,
138                              clientSocket );
139    }
140}
141
142static void
143bindCb( int * const socket,
144        tr_address * const addr,
145        void * const userData )
146{
147    tr_shared * s = ( tr_shared * )userData;
148    *socket = tr_netBindTCP( addr, s->publicPort, FALSE );
149    if( *socket >= 0 )
150    {
151        tr_ninf( getKey( ),
152                _( "Opened port %d on %s to listen for incoming peer connections" ),
153                s->publicPort, tr_ntop_non_ts( addr ) );
154        listen( *socket, 10 );
155    }
156    else
157    {
158        tr_nerr( getKey( ),
159                _(
160                  "Couldn't open port %d on %s to listen for incoming peer connections (errno %d - %s)" ),
161                s->publicPort, tr_ntop_non_ts( addr ), errno, tr_strerror( errno ) );
162    }
163}
164
165static void
166incomingPeersPulse( tr_shared * s )
167{
168    tr_bool allPaused;
169   
170    if( s->shouldChange )
171    {
172        tr_socketListForEach( s->bindSockets, &closeCb, s );
173        s->shouldChange = FALSE;
174        if( s->publicPort > 0 )
175            tr_socketListForEach( s->bindSockets, &bindCb, s );
176    }
177   
178    allPaused = tr_sessionGetActiveTorrentCount( s->session ) == 0;
179   
180    /* if we have any running torrents, check for new incoming peer connections */
181    /* (jhujhiti):
182     * This has been changed from a loop that will end when the listener queue
183     * is exhausted to one that will only check for one connection at a time.
184     * I think it unlikely that we get many more than one connection in the
185     * time between pulses (currently one second). However, just to be safe,
186     * I have increased the length of the listener queue from 5 to 10
187     * (see acceptCb() above). */
188    if( !allPaused )
189        tr_socketListForEach( s->bindSockets, &acceptCb, s );
190}
191
192static int
193sharedPulse( void * vshared )
194{
195    tr_bool keepPulsing = 1;
196    tr_shared * shared = vshared;
197
198    natPulse( shared );
199
200    if( !shared->isShuttingDown )
201    {
202        incomingPeersPulse( shared );
203    }
204    else
205    {
206        tr_ninf( getKey( ), _( "Stopped" ) );
207        tr_timerFree( &shared->pulseTimer );
208        tr_socketListForEach( shared->bindSockets, &closeCb, shared );
209        tr_socketListFree( shared->bindSockets );
210        tr_natpmpClose( shared->natpmp );
211        tr_upnpClose( shared->upnp );
212        shared->session->shared = NULL;
213        tr_free( shared );
214        keepPulsing = 0;
215    }
216
217    return keepPulsing;
218}
219
220/***
221****
222***/
223
224static tr_socketList *
225setupBindSockets( tr_port port )
226{
227    tr_bool hasIPv6 = tr_net_hasIPv6( port );
228    tr_socketList * socks = NULL;
229    if( hasIPv6 )
230        socks = tr_socketListNew( &tr_in6addr_any );
231
232    if( socks )
233        tr_socketListAppend( socks, &tr_inaddr_any );
234    else
235        socks = tr_socketListNew( &tr_inaddr_any );
236    return socks; /* Because the dryer gremlins won't */
237}
238
239tr_shared *
240tr_sharedInit( tr_session  * session,
241               tr_bool       isEnabled,
242               tr_port       publicPort )
243{
244    tr_shared * s = tr_new0( tr_shared, 1 );
245
246    s->session      = session;
247    s->publicPort   = publicPort;
248    s->shouldChange = TRUE;
249    s->bindSockets  = setupBindSockets( publicPort );
250    s->shouldChange = TRUE;
251    s->natpmp       = tr_natpmpInit( );
252    s->upnp         = tr_upnpInit( );
253    s->pulseTimer   = tr_timerNew( session, sharedPulse, s, 1000 );
254    s->isEnabled    = isEnabled;
255    s->upnpStatus   = TR_PORT_UNMAPPED;
256    s->natpmpStatus = TR_PORT_UNMAPPED;
257
258    return s;
259}
260
261void
262tr_sharedShuttingDown( tr_shared * s )
263{
264    s->isShuttingDown = 1;
265}
266
267void
268tr_sharedSetPort( tr_shared * s, tr_port  port )
269{
270    tr_torrent * tor = NULL;
271
272    s->publicPort   = port;
273    s->shouldChange = TRUE;
274
275    while( ( tor = tr_torrentNext( s->session, tor ) ) )
276        tr_torrentChangeMyPort( tor );
277}
278
279tr_port
280tr_sharedGetPeerPort( const tr_shared * s )
281{
282    return s->publicPort;
283}
284
285void
286tr_sharedTraversalEnable( tr_shared * s, tr_bool isEnabled )
287{
288    s->isEnabled = isEnabled;
289}
290
291tr_bool
292tr_sharedTraversalIsEnabled( const tr_shared * s )
293{
294    return s->isEnabled;
295}
296
297int
298tr_sharedTraversalStatus( const tr_shared * s )
299{
300    return MAX( s->natpmpStatus, s->upnpStatus );
301}
302
Note: See TracBrowser for help on using the repository browser.