source: trunk/libtransmission/upnp.c @ 9868

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

happy new year!

  • Property svn:keywords set to Date Rev Author Id
File size: 6.4 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 9868 2010-01-04 21:00:47Z 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
80int
81tr_upnpPulse( tr_upnp * handle,
82              int       port,
83              int       isEnabled,
84              int       doPortCheck )
85{
86    int ret;
87
88    if( isEnabled && ( handle->state == TR_UPNP_DISCOVER ) )
89    {
90        struct UPNPDev * devlist;
91        errno = 0;
92        devlist = upnpDiscover( 2000, NULL, NULL, 0 );
93        if( devlist == NULL )
94        {
95            tr_ndbg(
96                 getKey( ), "upnpDiscover failed (errno %d - %s)", errno,
97                tr_strerror( errno ) );
98        }
99        errno = 0;
100        if( UPNP_GetValidIGD( devlist, &handle->urls, &handle->data,
101                             handle->lanaddr, sizeof( handle->lanaddr ) ) )
102        {
103            tr_ninf( getKey( ), _(
104                         "Found Internet Gateway Device \"%s\"" ),
105                     handle->urls.controlURL );
106            tr_ninf( getKey( ), _(
107                         "Local Address is \"%s\"" ), handle->lanaddr );
108            handle->state = TR_UPNP_IDLE;
109            handle->hasDiscovered = 1;
110        }
111        else
112        {
113            handle->state = TR_UPNP_ERR;
114            tr_ndbg(
115                 getKey( ), "UPNP_GetValidIGD failed (errno %d - %s)",
116                errno,
117                tr_strerror( errno ) );
118            tr_ndbg(
119                getKey( ),
120                "If your router supports UPnP, please make sure UPnP is enabled!" );
121        }
122        freeUPNPDevlist( devlist );
123    }
124
125    if( handle->state == TR_UPNP_IDLE )
126    {
127        if( handle->isMapped && ( !isEnabled || ( handle->port != port ) ) )
128            handle->state = TR_UPNP_UNMAP;
129    }
130
131    if( isEnabled && handle->isMapped && doPortCheck )
132    {
133        char portStr[8];
134        char intPort[8];
135        char intClient[16];
136        int i;
137
138        tr_snprintf( portStr, sizeof( portStr ), "%d", handle->port );
139        i = UPNP_GetSpecificPortMappingEntry( handle->urls.controlURL,
140                                              handle->data.servicetype, portStr,
141                                              "TCP", intClient, intPort );
142        if( i != UPNPCOMMAND_SUCCESS )
143        {
144            tr_ninf( getKey( ), _( "Port %d isn't forwarded" ), handle->port );
145            handle->isMapped = FALSE;
146        }
147    }
148
149    if( handle->state == TR_UPNP_UNMAP )
150    {
151        char portStr[16];
152        tr_snprintf( portStr, sizeof( portStr ), "%d", handle->port );
153        UPNP_DeletePortMapping( handle->urls.controlURL,
154                                handle->data.servicetype,
155                                portStr, "TCP", NULL );
156        tr_ninf( getKey( ),
157                 _(
158                     "Stopping port forwarding through \"%s\", service \"%s\"" ),
159                 handle->urls.controlURL, handle->data.servicetype );
160        handle->isMapped = 0;
161        handle->state = TR_UPNP_IDLE;
162        handle->port = -1;
163    }
164
165    if( handle->state == TR_UPNP_IDLE )
166    {
167        if( isEnabled && !handle->isMapped )
168            handle->state = TR_UPNP_MAP;
169    }
170
171    if( handle->state == TR_UPNP_MAP )
172    {
173        int  err = -1;
174        errno = 0;
175
176        if( !handle->urls.controlURL || !handle->data.servicetype )
177            handle->isMapped = 0;
178        else
179        {
180            char portStr[16];
181            char desc[64];
182            tr_snprintf( portStr, sizeof( portStr ), "%d", port );
183            tr_snprintf( desc, sizeof( desc ), "%s at %d", TR_NAME, port );
184            err = UPNP_AddPortMapping( handle->urls.controlURL,
185                                       handle->data.servicetype,
186                                       portStr, portStr, handle->lanaddr,
187                                       desc, "TCP", NULL );
188            handle->isMapped = !err;
189        }
190        tr_ninf( getKey( ),
191                 _(
192                     "Port forwarding through \"%s\", service \"%s\".  (local address: %s:%d)" ),
193                 handle->urls.controlURL, handle->data.servicetype,
194                 handle->lanaddr, port );
195        if( handle->isMapped )
196        {
197            tr_ninf( getKey( ), "%s", _( "Port forwarding successful!" ) );
198            handle->port = port;
199            handle->state = TR_UPNP_IDLE;
200        }
201        else
202        {
203            tr_ndbg(
204                 getKey( ),
205                "Port forwarding failed with error %d (errno %d - %s)", err,
206                errno, tr_strerror( errno ) );
207            tr_ndbg(
208                getKey( ),
209                "If your router supports UPnP, please make sure UPnP is enabled!" );
210            handle->port = -1;
211            handle->state = TR_UPNP_ERR;
212        }
213    }
214
215    switch( handle->state )
216    {
217        case TR_UPNP_DISCOVER:
218            ret = TR_PORT_UNMAPPED; break;
219
220        case TR_UPNP_MAP:
221            ret = TR_PORT_MAPPING; break;
222
223        case TR_UPNP_UNMAP:
224            ret = TR_PORT_UNMAPPING; break;
225
226        case TR_UPNP_IDLE:
227            ret = handle->isMapped ? TR_PORT_MAPPED
228                  : TR_PORT_UNMAPPED; break;
229
230        default:
231            ret = TR_PORT_ERROR; break;
232    }
233
234    return ret;
235}
236
Note: See TracBrowser for help on using the repository browser.