• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/ap/gpl/transmission/transmission-2.73/libtransmission/
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: upnp.c 12958 2011-10-09 14:51:13Z jordan $
11 */
12
13#include <assert.h>
14#include <errno.h>
15
16#ifdef SYSTEM_MINIUPNP
17  #include <miniupnpc/miniupnpc.h>
18  #include <miniupnpc/upnpcommands.h>
19#else
20  #include <miniupnp/miniupnpc.h>
21  #include <miniupnp/upnpcommands.h>
22#endif
23
24#ifdef SYS_DARWIN
25  #define HAVE_MINIUPNP_16 1
26#endif
27
28#include "transmission.h"
29#include "port-forwarding.h"
30#include "session.h"
31#include "upnp.h"
32#include "utils.h"
33
34static const char *
35getKey( void ) { return _( "Port Forwarding (UPnP)" ); }
36
37typedef enum
38{
39    TR_UPNP_IDLE,
40    TR_UPNP_ERR,
41    TR_UPNP_DISCOVER,
42    TR_UPNP_MAP,
43    TR_UPNP_UNMAP
44}
45tr_upnp_state;
46
47struct tr_upnp
48{
49    bool               hasDiscovered;
50    struct UPNPUrls    urls;
51    struct IGDdatas    data;
52    int                port;
53    char               lanaddr[16];
54    unsigned int       isMapped;
55    tr_upnp_state      state;
56};
57
58/**
59***
60**/
61
62tr_upnp*
63tr_upnpInit( void )
64{
65    tr_upnp * ret = tr_new0( tr_upnp, 1 );
66
67    ret->state = TR_UPNP_DISCOVER;
68    ret->port = -1;
69    return ret;
70}
71
72void
73tr_upnpClose( tr_upnp * handle )
74{
75    assert( !handle->isMapped );
76    assert( ( handle->state == TR_UPNP_IDLE )
77          || ( handle->state == TR_UPNP_ERR )
78          || ( handle->state == TR_UPNP_DISCOVER ) );
79
80    if( handle->hasDiscovered )
81        FreeUPNPUrls( &handle->urls );
82    tr_free( handle );
83}
84
85/**
86***  Wrappers for miniupnpc functions
87**/
88
89static struct UPNPDev *
90tr_upnpDiscover( int msec )
91{
92    int err = 0;
93    struct UPNPDev * ret = NULL;
94
95#if defined(HAVE_MINIUPNP_16)
96    ret = upnpDiscover( msec, NULL, NULL, 0, 0, &err );
97#elif defined(HAVE_MINIUPNP_15)
98    ret = upnpDiscover( msec, NULL, NULL, 0 );
99#else
100    ret = UPNPCOMMAND_UNKNOWN_ERROR;
101#endif
102
103    if( ret != UPNPCOMMAND_SUCCESS )
104        tr_ndbg( getKey( ), "upnpDiscover failed (errno %d - %s)", err, tr_strerror( err ) );
105
106    return ret;
107}
108
109static int
110tr_upnpGetSpecificPortMappingEntry( tr_upnp * handle, const char * proto )
111{
112    int err;
113    char intClient[16];
114    char intPort[16];
115    char portStr[16];
116
117    *intClient = '\0';
118    *intPort = '\0';
119
120    tr_snprintf( portStr, sizeof( portStr ), "%d", (int)handle->port );
121
122#if defined(HAVE_MINIUPNP_16)
123    err = UPNP_GetSpecificPortMappingEntry( handle->urls.controlURL, handle->data.first.servicetype, portStr, proto, intClient, intPort, NULL, NULL, NULL );
124#elif defined(HAVE_MINIUPNP_15)
125    err = UPNP_GetSpecificPortMappingEntry( handle->urls.controlURL, handle->data.first.servicetype, portStr, proto, intClient, intPort );
126#else
127    err = UPNPCOMMAND_UNKNOWN_ERROR;
128#endif
129
130    return err;
131}
132
133static int
134tr_upnpAddPortMapping( const tr_upnp * handle, const char * proto, tr_port port, const char * desc )
135{
136    int err;
137    const int old_errno = errno;
138    char portStr[16];
139    errno = 0;
140
141    tr_snprintf( portStr, sizeof( portStr ), "%d", (int)port );
142
143#if defined(HAVE_MINIUPNP_16)
144    err = UPNP_AddPortMapping( handle->urls.controlURL, handle->data.first.servicetype, portStr, portStr, handle->lanaddr, desc, proto, NULL, NULL );
145#elif defined(HAVE_MINIUPNP_15)
146    err = UPNP_AddPortMapping( handle->urls.controlURL, handle->data.first.servicetype, portStr, portStr, handle->lanaddr, desc, proto, NULL );
147#else
148    err = UPNPCOMMAND_UNKNOWN_ERROR;
149#endif
150
151    if( err )
152        tr_ndbg( getKey( ), "%s Port forwarding failed with error %d (errno %d - %s)", proto, err, errno, tr_strerror( errno ) );
153
154    errno = old_errno;
155    return err;
156}
157
158static void
159tr_upnpDeletePortMapping( const tr_upnp * handle, const char * proto, tr_port port )
160{
161    char portStr[16];
162
163    tr_snprintf( portStr, sizeof( portStr ), "%d", (int)port );
164
165    UPNP_DeletePortMapping( handle->urls.controlURL,
166                            handle->data.first.servicetype,
167                            portStr, proto, NULL );
168}
169
170/**
171***
172**/
173
174enum
175{
176  UPNP_IGD_NONE = 0,
177  UPNP_IGD_VALID_CONNECTED = 1,
178  UPNP_IGD_VALID_NOT_CONNECTED = 2,
179  UPNP_IGD_INVALID = 3
180};
181
182int
183tr_upnpPulse( tr_upnp * handle,
184              int       port,
185              int       isEnabled,
186              int       doPortCheck )
187{
188    int ret;
189
190    if( isEnabled && ( handle->state == TR_UPNP_DISCOVER ) )
191    {
192        struct UPNPDev * devlist;
193
194        devlist = tr_upnpDiscover( 2000 );
195
196        errno = 0;
197        if( UPNP_GetValidIGD( devlist, &handle->urls, &handle->data,
198                             handle->lanaddr, sizeof( handle->lanaddr ) ) == UPNP_IGD_VALID_CONNECTED )
199        {
200            tr_ninf( getKey( ), _(
201                         "Found Internet Gateway Device \"%s\"" ),
202                     handle->urls.controlURL );
203            tr_ninf( getKey( ), _(
204                         "Local Address is \"%s\"" ), handle->lanaddr );
205            handle->state = TR_UPNP_IDLE;
206            handle->hasDiscovered = 1;
207        }
208        else
209        {
210            handle->state = TR_UPNP_ERR;
211            tr_ndbg(
212                 getKey( ), "UPNP_GetValidIGD failed (errno %d - %s)",
213                errno,
214                tr_strerror( errno ) );
215            tr_ndbg(
216                getKey( ),
217                "If your router supports UPnP, please make sure UPnP is enabled!" );
218        }
219        freeUPNPDevlist( devlist );
220    }
221
222    if( handle->state == TR_UPNP_IDLE )
223    {
224        if( handle->isMapped && ( !isEnabled || ( handle->port != port ) ) )
225            handle->state = TR_UPNP_UNMAP;
226    }
227
228    if( isEnabled && handle->isMapped && doPortCheck )
229    {
230        if( ( tr_upnpGetSpecificPortMappingEntry( handle, "TCP" ) != UPNPCOMMAND_SUCCESS ) ||
231            ( tr_upnpGetSpecificPortMappingEntry( handle, "UDP" ) != UPNPCOMMAND_SUCCESS ) )
232        {
233            tr_ninf( getKey( ), _( "Port %d isn't forwarded" ), handle->port );
234            handle->isMapped = false;
235        }
236    }
237
238    if( handle->state == TR_UPNP_UNMAP )
239    {
240        tr_upnpDeletePortMapping( handle, "TCP", handle->port );
241        tr_upnpDeletePortMapping( handle, "UDP", handle->port );
242
243        tr_ninf( getKey( ),
244                 _( "Stopping port forwarding through \"%s\", service \"%s\"" ),
245                 handle->urls.controlURL, handle->data.first.servicetype );
246
247        handle->isMapped = 0;
248        handle->state = TR_UPNP_IDLE;
249        handle->port = -1;
250    }
251
252    if( handle->state == TR_UPNP_IDLE )
253    {
254        if( isEnabled && !handle->isMapped )
255            handle->state = TR_UPNP_MAP;
256    }
257
258    if( handle->state == TR_UPNP_MAP )
259    {
260        int  err_tcp = -1;
261        int  err_udp = -1;
262        errno = 0;
263
264        if( !handle->urls.controlURL || !handle->data.first.servicetype )
265            handle->isMapped = 0;
266        else
267        {
268            char desc[64];
269            tr_snprintf( desc, sizeof( desc ), "%s at %d", TR_NAME, port );
270
271            err_tcp = tr_upnpAddPortMapping( handle, "TCP", port, desc );
272            err_udp = tr_upnpAddPortMapping( handle, "UDP", port, desc );
273
274            handle->isMapped = !err_tcp | !err_udp;
275        }
276        tr_ninf( getKey( ),
277                 _( "Port forwarding through \"%s\", service \"%s\". (local address: %s:%d)" ),
278                 handle->urls.controlURL, handle->data.first.servicetype,
279                 handle->lanaddr, port );
280        if( handle->isMapped )
281        {
282            tr_ninf( getKey( ), "%s", _( "Port forwarding successful!" ) );
283            handle->port = port;
284            handle->state = TR_UPNP_IDLE;
285        }
286        else
287        {
288            tr_ndbg( getKey( ), "If your router supports UPnP, please make sure UPnP is enabled!" );
289            handle->port = -1;
290            handle->state = TR_UPNP_ERR;
291        }
292    }
293
294    switch( handle->state )
295    {
296        case TR_UPNP_DISCOVER:
297            ret = TR_PORT_UNMAPPED; break;
298
299        case TR_UPNP_MAP:
300            ret = TR_PORT_MAPPING; break;
301
302        case TR_UPNP_UNMAP:
303            ret = TR_PORT_UNMAPPING; break;
304
305        case TR_UPNP_IDLE:
306            ret = handle->isMapped ? TR_PORT_MAPPED
307                  : TR_PORT_UNMAPPED; break;
308
309        default:
310            ret = TR_PORT_ERROR; break;
311    }
312
313    return ret;
314}
315
316