source: branches/2.0x/libtransmission/upnp.c @ 10910

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

(2.0x) backport r10900 to 2.0x to fix a port forwarding issue

  • 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 10910 2010-06-30 06:07:06Z 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.