source: trunk/libtransmission/port-forwarding.c @ 8271

Last change on this file since 8271 was 8271, checked in by charles, 13 years ago

(trunk libT) #1993: Check if UPNP port is still open. Patch from KyleK

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