source: trunk/libtransmission/natpmp.c @ 11709

Last change on this file since 11709 was 11709, checked in by jordan, 11 years ago

Update the copyright year in the source code comments.

The Berne Convention says that the copyright year is moot, so instead of adding another year to each file as in previous years, I've removed the year altogether from the source code comments in libtransmission, gtk, qt, utils, daemon, and cli.

Juliusz's copyright notice in tr-dht and Johannes' copyright notice in tr-lpd have been left alone; it didn't seem appropriate to modify them.

  • Property svn:keywords set to Date Rev Author Id
File size: 6.8 KB
Line 
1/*
2 * This file Copyright (C) Mnemosyne LLC
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: natpmp.c 11709 2011-01-19 13:48:47Z jordan $
11 */
12
13#include <errno.h>
14#include <time.h>
15#include <inttypes.h>
16
17#define ENABLE_STRNATPMPERR
18#include <libnatpmp/natpmp.h>
19
20#include "transmission.h"
21#include "natpmp.h"
22#include "net.h" /* inet_ntoa() */
23#include "port-forwarding.h"
24#include "utils.h"
25
26#define LIFETIME_SECS 3600
27#define COMMAND_WAIT_SECS 8
28
29static const char *
30getKey( void ) { return _( "Port Forwarding (NAT-PMP)" ); }
31
32typedef enum
33{
34    TR_NATPMP_IDLE,
35    TR_NATPMP_ERR,
36    TR_NATPMP_DISCOVER,
37    TR_NATPMP_RECV_PUB,
38    TR_NATPMP_SEND_MAP,
39    TR_NATPMP_RECV_MAP,
40    TR_NATPMP_SEND_UNMAP,
41    TR_NATPMP_RECV_UNMAP
42}
43tr_natpmp_state;
44
45struct tr_natpmp
46{
47    tr_bool           has_discovered;
48    tr_bool           is_mapped;
49
50    tr_port           public_port;
51    tr_port           private_port;
52
53    time_t            renew_time;
54    time_t            command_time;
55    tr_natpmp_state   state;
56    natpmp_t          natpmp;
57};
58
59/**
60***
61**/
62
63static void
64logVal( const char * func,
65        int          ret )
66{
67    if( ret == NATPMP_TRYAGAIN )
68        return;
69    if( ret >= 0 )
70        tr_ninf( getKey( ), _( "%s succeeded (%d)" ), func, ret );
71    else
72        tr_ndbg(
73             getKey( ),
74            "%s failed. Natpmp returned %d (%s); errno is %d (%s)",
75            func, ret, strnatpmperr( ret ), errno, tr_strerror( errno ) );
76}
77
78struct tr_natpmp*
79tr_natpmpInit( void )
80{
81    struct tr_natpmp * nat;
82
83    nat = tr_new0( struct tr_natpmp, 1 );
84    nat->state = TR_NATPMP_DISCOVER;
85    nat->public_port = 0;
86    nat->private_port = 0;
87    nat->natpmp.s = -1; /* socket */
88    return nat;
89}
90
91void
92tr_natpmpClose( tr_natpmp * nat )
93{
94    if( nat )
95    {
96        if( nat->natpmp.s >= 0 )
97            tr_netCloseSocket( nat->natpmp.s );
98        tr_free( nat );
99    }
100}
101
102static int
103canSendCommand( const struct tr_natpmp * nat )
104{
105    return tr_time( ) >= nat->command_time;
106}
107
108static void
109setCommandTime( struct tr_natpmp * nat )
110{
111    nat->command_time = tr_time( ) + COMMAND_WAIT_SECS;
112}
113
114int
115tr_natpmpPulse( struct tr_natpmp * nat, tr_port private_port, tr_bool is_enabled, tr_port * public_port )
116{
117    int ret;
118
119    if( is_enabled && ( nat->state == TR_NATPMP_DISCOVER ) )
120    {
121        int val = initnatpmp( &nat->natpmp );
122        logVal( "initnatpmp", val );
123        val = sendpublicaddressrequest( &nat->natpmp );
124        logVal( "sendpublicaddressrequest", val );
125        nat->state = val < 0 ? TR_NATPMP_ERR : TR_NATPMP_RECV_PUB;
126        nat->has_discovered = TRUE;
127        setCommandTime( nat );
128    }
129
130    if( ( nat->state == TR_NATPMP_RECV_PUB ) && canSendCommand( nat ) )
131    {
132        natpmpresp_t response;
133        const int val = readnatpmpresponseorretry( &nat->natpmp, &response );
134        logVal( "readnatpmpresponseorretry", val );
135        if( val >= 0 )
136        {
137            tr_ninf( getKey( ), _( "Found public address \"%s\"" ),
138                     inet_ntoa( response.pnu.publicaddress.addr ) );
139            nat->state = TR_NATPMP_IDLE;
140        }
141        else if( val != NATPMP_TRYAGAIN )
142        {
143            nat->state = TR_NATPMP_ERR;
144        }
145    }
146
147    if( ( nat->state == TR_NATPMP_IDLE ) || ( nat->state == TR_NATPMP_ERR ) )
148    {
149        if( nat->is_mapped && ( !is_enabled || ( nat->private_port != private_port ) ) )
150            nat->state = TR_NATPMP_SEND_UNMAP;
151    }
152
153    if( ( nat->state == TR_NATPMP_SEND_UNMAP ) && canSendCommand( nat ) )
154    {
155        const int val = sendnewportmappingrequest( &nat->natpmp, NATPMP_PROTOCOL_TCP,
156                                                   nat->private_port,
157                                                   nat->public_port,
158                                                   0 );
159        logVal( "sendnewportmappingrequest", val );
160        nat->state = val < 0 ? TR_NATPMP_ERR : TR_NATPMP_RECV_UNMAP;
161        setCommandTime( nat );
162    }
163
164    if( nat->state == TR_NATPMP_RECV_UNMAP )
165    {
166        natpmpresp_t resp;
167        const int val = readnatpmpresponseorretry( &nat->natpmp, &resp );
168        logVal( "readnatpmpresponseorretry", val );
169        if( val >= 0 )
170        {
171            const int private_port = resp.pnu.newportmapping.privateport;
172
173            tr_ninf( getKey( ), _( "no longer forwarding port %d" ), private_port );
174
175            if( nat->private_port == private_port )
176            {
177                nat->private_port = 0;
178                nat->public_port = 0;
179                nat->state = TR_NATPMP_IDLE;
180                nat->is_mapped = FALSE;
181            }
182        }
183        else if( val != NATPMP_TRYAGAIN )
184        {
185            nat->state = TR_NATPMP_ERR;
186        }
187    }
188
189    if( nat->state == TR_NATPMP_IDLE )
190    {
191        if( is_enabled && !nat->is_mapped && nat->has_discovered )
192            nat->state = TR_NATPMP_SEND_MAP;
193
194        else if( nat->is_mapped && tr_time( ) >= nat->renew_time )
195            nat->state = TR_NATPMP_SEND_MAP;
196    }
197
198    if( ( nat->state == TR_NATPMP_SEND_MAP ) && canSendCommand( nat ) )
199    {
200        const int val = sendnewportmappingrequest( &nat->natpmp, NATPMP_PROTOCOL_TCP, private_port, private_port, LIFETIME_SECS );
201        logVal( "sendnewportmappingrequest", val );
202        nat->state = val < 0 ? TR_NATPMP_ERR : TR_NATPMP_RECV_MAP;
203        setCommandTime( nat );
204    }
205
206    if( nat->state == TR_NATPMP_RECV_MAP )
207    {
208        natpmpresp_t resp;
209        const int    val = readnatpmpresponseorretry( &nat->natpmp, &resp );
210        logVal( "readnatpmpresponseorretry", val );
211        if( val >= 0 )
212        {
213            nat->state = TR_NATPMP_IDLE;
214            nat->is_mapped = TRUE;
215            nat->renew_time = tr_time( ) + ( resp.pnu.newportmapping.lifetime / 2 );
216            nat->private_port = resp.pnu.newportmapping.privateport;
217            nat->public_port = resp.pnu.newportmapping.mappedpublicport;
218            tr_ninf( getKey( ), _( "Port %d forwarded successfully" ), nat->private_port );
219        }
220        else if( val != NATPMP_TRYAGAIN )
221        {
222            nat->state = TR_NATPMP_ERR;
223        }
224    }
225
226    switch( nat->state )
227    {
228        case TR_NATPMP_IDLE:
229            *public_port = nat->public_port;
230            return nat->is_mapped ? TR_PORT_MAPPED : TR_PORT_UNMAPPED;
231            break;
232
233        case TR_NATPMP_DISCOVER:
234            ret = TR_PORT_UNMAPPED; break;
235
236        case TR_NATPMP_RECV_PUB:
237        case TR_NATPMP_SEND_MAP:
238        case TR_NATPMP_RECV_MAP:
239            ret = TR_PORT_MAPPING; break;
240
241        case TR_NATPMP_SEND_UNMAP:
242        case TR_NATPMP_RECV_UNMAP:
243            ret = TR_PORT_UNMAPPING; break;
244
245        default:
246            ret = TR_PORT_ERROR; break;
247    }
248    return ret;
249}
250
Note: See TracBrowser for help on using the repository browser.