source: trunk/libtransmission/upnp.c @ 5208

Last change on this file since 5208 was 5208, checked in by charles, 14 years ago

more i18n strings work

  • Property svn:keywords set to Date Rev Author Id
File size: 5.3 KB
Line 
1/*
2 * This file Copyright (C) 2007-2008 Charles Kerr <charles@rebelbase.com>
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 5208 2008-03-06 21:59:00Z charles $
11 */
12
13#include <assert.h>
14#include <errno.h>
15#include <stdio.h> /* snprintf */
16
17#include <miniupnp/miniwget.h>
18#include <miniupnp/miniupnpc.h>
19#include <miniupnp/upnpcommands.h>
20
21#include "transmission.h"
22#include "internal.h"
23#include "shared.h"
24#include "utils.h"
25#include "upnp.h"
26
27typedef enum
28{
29    TR_UPNP_IDLE,
30    TR_UPNP_ERR,
31    TR_UPNP_DISCOVER,
32    TR_UPNP_MAP,
33    TR_UPNP_UNMAP
34}
35tr_upnp_state;
36
37struct tr_upnp
38{
39    struct UPNPUrls urls;
40    struct IGDdatas data;
41    int port;
42    char lanaddr[16];
43    unsigned int isMapped;
44    unsigned int hasDiscovered : 1;
45    tr_upnp_state state;
46};
47
48/**
49***
50**/
51
52tr_upnp*
53tr_upnpInit( void )
54{
55    tr_upnp * ret = tr_new0( tr_upnp, 1 );
56    ret->state = TR_UPNP_DISCOVER;
57    ret->port = -1;
58    return ret;
59}
60
61void
62tr_upnpClose( tr_upnp * handle )
63{
64    assert( !handle->isMapped );
65    assert( ( handle->state == TR_UPNP_IDLE )
66         || ( handle->state == TR_UPNP_ERR )
67         || ( handle->state == TR_UPNP_DISCOVER ) );
68
69    if( handle->hasDiscovered )
70        FreeUPNPUrls( &handle->urls );
71    tr_free( handle );
72}
73
74/**
75***
76**/
77
78int
79tr_upnpPulse( tr_upnp * handle, int port, int isEnabled )
80{
81    int ret;
82
83    if( isEnabled && ( handle->state == TR_UPNP_DISCOVER ) )
84    {
85        struct UPNPDev * devlist;
86        errno = 0;
87        devlist = upnpDiscover( 2000, NULL, NULL );
88        if( devlist == NULL ) {
89            tr_err( _( "Port Mapping (UPnP): upnpDiscover returned NULL (errno %d - %s)" ), errno, tr_strerror(errno) );
90        }
91        errno = 0;
92        if( UPNP_GetValidIGD( devlist, &handle->urls, &handle->data, handle->lanaddr, sizeof(handle->lanaddr))) {
93            tr_inf( _( "Port Mapping (UPnP): Found Internet Gateway Device '%s'" ), handle->urls.controlURL );
94            tr_inf( _( "Port Mapping (UPnP): Local LAN IP Address is '%s'" ), handle->lanaddr );
95            handle->state = TR_UPNP_IDLE;
96            handle->hasDiscovered = 1;
97        } else {
98            handle->state = TR_UPNP_ERR;
99            tr_err( _( "Port Mapping (UPnP): UPNP_GetValidIGD failed.  (errno %d - %s)" ), errno, tr_strerror(errno) );
100            tr_err( _( "Port Mapping (UPnP): If your router supports UPnP, please make sure UPnP is enabled!" ) );
101        }
102        freeUPNPDevlist( devlist );
103    }
104
105    if( handle->state == TR_UPNP_IDLE )
106    {
107        if( handle->isMapped && ( !isEnabled || ( handle->port != port ) ) )
108            handle->state = TR_UPNP_UNMAP;
109    }
110
111    if( handle->state == TR_UPNP_UNMAP )
112    {
113        char portStr[16];
114        snprintf( portStr, sizeof(portStr), "%d", handle->port );
115        UPNP_DeletePortMapping( handle->urls.controlURL,
116                                handle->data.servicetype,
117                                portStr, "TCP" );
118        tr_dbg( "Port Mapping (UPnP): Stopping port forwarding of '%s', service '%s'",
119                handle->urls.controlURL, handle->data.servicetype );
120        handle->isMapped = 0;
121        handle->state = TR_UPNP_IDLE;
122        handle->port = -1;
123    }
124
125    if( handle->state == TR_UPNP_IDLE )
126    {
127        if( isEnabled && !handle->isMapped )
128            handle->state = TR_UPNP_MAP;
129    }
130
131    if( handle->state == TR_UPNP_MAP )
132    {
133        int err = -1;
134        char portStr[16];
135        snprintf( portStr, sizeof(portStr), "%d", port );
136        errno = 0;
137
138        if( !handle->urls.controlURL || !handle->data.servicetype )
139            handle->isMapped = 0;
140        else {
141            err = UPNP_AddPortMapping( handle->urls.controlURL,
142                                       handle->data.servicetype,
143                                       portStr, portStr, handle->lanaddr,
144                                       "Transmission", "TCP" );
145            handle->isMapped = !err;
146        }
147        tr_inf( _( "Port Mapping (UPnP): Port forwarding via '%s', service '%s'.  (local address: %s:%d)" ),
148                handle->urls.controlURL, handle->data.servicetype, handle->lanaddr, port );
149        if( handle->isMapped ) {
150            tr_inf( _( "Port Mapping (UPnP): Port forwarding successful!" ) );
151            handle->port = port;
152            handle->state = TR_UPNP_IDLE;
153        } else {
154            tr_err( _( "Port Mapping (UPnP): Port forwarding failed with err %d (%d - %s)" ), err, errno, tr_strerror(errno) );
155            tr_err( _( "Port Mapping (UPnP): If your router supports UPnP, please make sure UPnP is enabled!" ) );
156            handle->port = -1;
157            handle->state = TR_UPNP_ERR;
158        }
159    }
160
161    switch( handle->state )
162    {
163        case TR_UPNP_DISCOVER: ret = TR_NAT_TRAVERSAL_UNMAPPED; break;
164        case TR_UPNP_MAP:      ret = TR_NAT_TRAVERSAL_MAPPING; break;
165        case TR_UPNP_UNMAP:    ret = TR_NAT_TRAVERSAL_UNMAPPING; break;
166        case TR_UPNP_IDLE:     ret = handle->isMapped ? TR_NAT_TRAVERSAL_MAPPED
167                                                      : TR_NAT_TRAVERSAL_UNMAPPED; break;
168        default:               ret = TR_NAT_TRAVERSAL_ERROR; break;
169    }
170
171    return ret;
172}
Note: See TracBrowser for help on using the repository browser.