source: trunk/libtransmission/upnp.c @ 10900

Last change on this file since 10900 was 10900, checked in by charles, 11 years ago

(trunk libT) don't try to port forward through your cablebox just because it's hooked up to the local network...

  • Property svn:keywords set to Date Rev Author Id
File size: 6.6 KB
Line 
1/*
2 * This file Copyright (C) 2007-2010 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 10900 2010-06-27 15:29:23Z charles $
11 */
12
13#include <assert.h>
14#include <errno.h>
15
16#include <miniupnp/miniupnpc.h>
17#include <miniupnp/upnpcommands.h>
18
19#include "transmission.h"
20#include "port-forwarding.h"
21#include "session.h"
22#include "upnp.h"
23#include "utils.h"
24
25static const char *
26getKey( void ) { return _( "Port Forwarding (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;
37
38struct tr_upnp
39{
40    tr_bool            hasDiscovered;
41    struct UPNPUrls    urls;
42    struct IGDdatas    data;
43    int                port;
44    char               lanaddr[16];
45    unsigned int       isMapped;
46    tr_upnp_state      state;
47};
48
49/**
50***
51**/
52
53tr_upnp*
54tr_upnpInit( void )
55{
56    tr_upnp * ret = tr_new0( tr_upnp, 1 );
57
58    ret->state = TR_UPNP_DISCOVER;
59    ret->port = -1;
60    return ret;
61}
62
63void
64tr_upnpClose( tr_upnp * handle )
65{
66    assert( !handle->isMapped );
67    assert( ( handle->state == TR_UPNP_IDLE )
68          || ( handle->state == TR_UPNP_ERR )
69          || ( handle->state == TR_UPNP_DISCOVER ) );
70
71    if( handle->hasDiscovered )
72        FreeUPNPUrls( &handle->urls );
73    tr_free( handle );
74}
75
76/**
77***
78**/
79
80enum
81{
82  UPNP_IGD_NONE = 0,
83  UPNP_IGD_VALID_CONNECTED = 1,
84  UPNP_IGD_VALID_NOT_CONNECTED = 2,
85  UPNP_IGD_INVALID = 3
86};
87
88int
89tr_upnpPulse( tr_upnp * handle,
90              int       port,
91              int       isEnabled,
92              int       doPortCheck )
93{
94    int ret;
95
96    if( isEnabled && ( handle->state == TR_UPNP_DISCOVER ) )
97    {
98        struct UPNPDev * devlist;
99        errno = 0;
100        devlist = upnpDiscover( 2000, NULL, NULL, 0 );
101        if( devlist == NULL )
102        {
103            tr_ndbg(
104                 getKey( ), "upnpDiscover failed (errno %d - %s)", errno,
105                tr_strerror( errno ) );
106        }
107        errno = 0;
108        if( UPNP_GetValidIGD( devlist, &handle->urls, &handle->data,
109                             handle->lanaddr, sizeof( handle->lanaddr ) ) == UPNP_IGD_VALID_CONNECTED )
110        {
111            tr_ninf( getKey( ), _(
112                         "Found Internet Gateway Device \"%s\"" ),
113                     handle->urls.controlURL );
114            tr_ninf( getKey( ), _(
115                         "Local Address is \"%s\"" ), handle->lanaddr );
116            handle->state = TR_UPNP_IDLE;
117            handle->hasDiscovered = 1;
118        }
119        else
120        {
121            handle->state = TR_UPNP_ERR;
122            tr_ndbg(
123                 getKey( ), "UPNP_GetValidIGD failed (errno %d - %s)",
124                errno,
125                tr_strerror( errno ) );
126            tr_ndbg(
127                getKey( ),
128                "If your router supports UPnP, please make sure UPnP is enabled!" );
129        }
130        freeUPNPDevlist( devlist );
131    }
132
133    if( handle->state == TR_UPNP_IDLE )
134    {
135        if( handle->isMapped && ( !isEnabled || ( handle->port != port ) ) )
136            handle->state = TR_UPNP_UNMAP;
137    }
138
139    if( isEnabled && handle->isMapped && doPortCheck )
140    {
141        char portStr[8];
142        char intPort[8];
143        char intClient[16];
144        int i;
145
146        tr_snprintf( portStr, sizeof( portStr ), "%d", handle->port );
147        i = UPNP_GetSpecificPortMappingEntry( handle->urls.controlURL,
148                                              handle->data.first.servicetype, portStr,
149                                              "TCP", intClient, intPort );
150        if( i != UPNPCOMMAND_SUCCESS )
151        {
152            tr_ninf( getKey( ), _( "Port %d isn't forwarded" ), handle->port );
153            handle->isMapped = FALSE;
154        }
155    }
156
157    if( handle->state == TR_UPNP_UNMAP )
158    {
159        char portStr[16];
160        tr_snprintf( portStr, sizeof( portStr ), "%d", handle->port );
161        UPNP_DeletePortMapping( handle->urls.controlURL,
162                                handle->data.first.servicetype,
163                                portStr, "TCP", NULL );
164        tr_ninf( getKey( ),
165                 _(
166                     "Stopping port forwarding through \"%s\", service \"%s\"" ),
167                 handle->urls.controlURL, handle->data.first.servicetype );
168        handle->isMapped = 0;
169        handle->state = TR_UPNP_IDLE;
170        handle->port = -1;
171    }
172
173    if( handle->state == TR_UPNP_IDLE )
174    {
175        if( isEnabled && !handle->isMapped )
176            handle->state = TR_UPNP_MAP;
177    }
178
179    if( handle->state == TR_UPNP_MAP )
180    {
181        int  err = -1;
182        errno = 0;
183
184        if( !handle->urls.controlURL || !handle->data.first.servicetype )
185            handle->isMapped = 0;
186        else
187        {
188            char portStr[16];
189            char desc[64];
190            tr_snprintf( portStr, sizeof( portStr ), "%d", port );
191            tr_snprintf( desc, sizeof( desc ), "%s at %d", TR_NAME, port );
192            err = UPNP_AddPortMapping( handle->urls.controlURL,
193                                       handle->data.first.servicetype,
194                                       portStr, portStr, handle->lanaddr,
195                                       desc, "TCP", NULL );
196            handle->isMapped = !err;
197        }
198        tr_ninf( getKey( ),
199                 _(
200                     "Port forwarding through \"%s\", service \"%s\".  (local address: %s:%d)" ),
201                 handle->urls.controlURL, handle->data.first.servicetype,
202                 handle->lanaddr, port );
203        if( handle->isMapped )
204        {
205            tr_ninf( getKey( ), "%s", _( "Port forwarding successful!" ) );
206            handle->port = port;
207            handle->state = TR_UPNP_IDLE;
208        }
209        else
210        {
211            tr_ndbg(
212                 getKey( ),
213                "Port forwarding failed with error %d (errno %d - %s)", err,
214                errno, tr_strerror( errno ) );
215            tr_ndbg(
216                getKey( ),
217                "If your router supports UPnP, please make sure UPnP is enabled!" );
218            handle->port = -1;
219            handle->state = TR_UPNP_ERR;
220        }
221    }
222
223    switch( handle->state )
224    {
225        case TR_UPNP_DISCOVER:
226            ret = TR_PORT_UNMAPPED; break;
227
228        case TR_UPNP_MAP:
229            ret = TR_PORT_MAPPING; break;
230
231        case TR_UPNP_UNMAP:
232            ret = TR_PORT_UNMAPPING; break;
233
234        case TR_UPNP_IDLE:
235            ret = handle->isMapped ? TR_PORT_MAPPED
236                  : TR_PORT_UNMAPPED; break;
237
238        default:
239            ret = TR_PORT_ERROR; break;
240    }
241
242    return ret;
243}
244
Note: See TracBrowser for help on using the repository browser.