source: trunk/libtransmission/upnp.c @ 12918

Last change on this file since 12918 was 12593, checked in by livings124, 10 years ago

#4387 Update miniupnpc to 1.6

  • Property svn:keywords set to Date Rev Author Id
File size: 7.5 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: upnp.c 12593 2011-07-26 01:36:30Z jordan $
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    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, 0, &errno );
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
145        tr_snprintf( portStr, sizeof( portStr ), "%d", handle->port );
146        if( UPNP_GetSpecificPortMappingEntry( handle->urls.controlURL, handle->data.first.servicetype,
147            portStr, "TCP", intClient, intPort, NULL, NULL, NULL ) != UPNPCOMMAND_SUCCESS  ||
148            UPNP_GetSpecificPortMappingEntry( handle->urls.controlURL, handle->data.first.servicetype,
149            portStr, "UDP", intClient, intPort, NULL, NULL, NULL ) != UPNPCOMMAND_SUCCESS )
150        {
151            tr_ninf( getKey( ), _( "Port %d isn't forwarded" ), handle->port );
152            handle->isMapped = false;
153        }
154    }
155
156    if( handle->state == TR_UPNP_UNMAP )
157    {
158        char portStr[16];
159        tr_snprintf( portStr, sizeof( portStr ), "%d", handle->port );
160        UPNP_DeletePortMapping( handle->urls.controlURL,
161                                handle->data.first.servicetype,
162                                portStr, "TCP", NULL );
163        UPNP_DeletePortMapping( handle->urls.controlURL,
164                                handle->data.first.servicetype,
165                                portStr, "UDP", NULL );
166        tr_ninf( getKey( ),
167                 _(
168                     "Stopping port forwarding through \"%s\", service \"%s\"" ),
169                 handle->urls.controlURL, handle->data.first.servicetype );
170        handle->isMapped = 0;
171        handle->state = TR_UPNP_IDLE;
172        handle->port = -1;
173    }
174
175    if( handle->state == TR_UPNP_IDLE )
176    {
177        if( isEnabled && !handle->isMapped )
178            handle->state = TR_UPNP_MAP;
179    }
180
181    if( handle->state == TR_UPNP_MAP )
182    {
183        int  err_tcp = -1;
184        int  err_udp = -1;
185        errno = 0;
186
187        if( !handle->urls.controlURL || !handle->data.first.servicetype )
188            handle->isMapped = 0;
189        else
190        {
191            char portStr[16];
192            char desc[64];
193            const int prev_errno = errno;
194            tr_snprintf( portStr, sizeof( portStr ), "%d", port );
195            tr_snprintf( desc, sizeof( desc ), "%s at %d", TR_NAME, port );
196
197            errno = 0;
198            err_tcp = UPNP_AddPortMapping( handle->urls.controlURL,
199                                       handle->data.first.servicetype,
200                                       portStr, portStr, handle->lanaddr,
201                                       desc, "TCP", NULL, NULL );
202            if( err_tcp )
203                tr_ndbg( getKey( ), "TCP Port forwarding failed with error %d (errno %d - %s)",
204                         err_tcp, errno, tr_strerror( errno ) );
205
206            errno = 0;
207            err_udp = UPNP_AddPortMapping( handle->urls.controlURL,
208                                       handle->data.first.servicetype,
209                                       portStr, portStr, handle->lanaddr,
210                                       desc, "UDP", NULL, NULL );
211            if( err_udp )
212                tr_ndbg( getKey( ), "UDP Port forwarding failed with error %d (errno %d - %s)",
213                         err_udp, errno, tr_strerror( errno ) );
214
215            errno = prev_errno;
216            handle->isMapped = !err_tcp | !err_udp;
217        }
218        tr_ninf( getKey( ),
219                 _( "Port forwarding through \"%s\", service \"%s\". (local address: %s:%d)" ),
220                 handle->urls.controlURL, handle->data.first.servicetype,
221                 handle->lanaddr, port );
222        if( handle->isMapped )
223        {
224            tr_ninf( getKey( ), "%s", _( "Port forwarding successful!" ) );
225            handle->port = port;
226            handle->state = TR_UPNP_IDLE;
227        }
228        else
229        {
230            tr_ndbg( getKey( ), "If your router supports UPnP, please make sure UPnP is enabled!" );
231            handle->port = -1;
232            handle->state = TR_UPNP_ERR;
233        }
234    }
235
236    switch( handle->state )
237    {
238        case TR_UPNP_DISCOVER:
239            ret = TR_PORT_UNMAPPED; break;
240
241        case TR_UPNP_MAP:
242            ret = TR_PORT_MAPPING; break;
243
244        case TR_UPNP_UNMAP:
245            ret = TR_PORT_UNMAPPING; break;
246
247        case TR_UPNP_IDLE:
248            ret = handle->isMapped ? TR_PORT_MAPPED
249                  : TR_PORT_UNMAPPED; break;
250
251        default:
252            ret = TR_PORT_ERROR; break;
253    }
254
255    return ret;
256}
257
Note: See TracBrowser for help on using the repository browser.