source: trunk/libtransmission/upnp.c @ 5221

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

lots more i18n string work -- making strings more consistent, folding redundant strings together, etc.

  • 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 5221 2008-03-07 20:48:36Z 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
27static const char * getKey( void ) { return _( "Port Mapping (UPnP)" ); }
28
29typedef enum
30{
31    TR_UPNP_IDLE,
32    TR_UPNP_ERR,
33    TR_UPNP_DISCOVER,
34    TR_UPNP_MAP,
35    TR_UPNP_UNMAP
36}
37tr_upnp_state;
38
39struct tr_upnp
40{
41    struct UPNPUrls urls;
42    struct IGDdatas data;
43    int port;
44    char lanaddr[16];
45    unsigned int isMapped;
46    unsigned int hasDiscovered : 1;
47    tr_upnp_state state;
48};
49
50/**
51***
52**/
53
54tr_upnp*
55tr_upnpInit( void )
56{
57    tr_upnp * ret = tr_new0( tr_upnp, 1 );
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, int port, int isEnabled )
82{
83    int ret;
84
85    if( isEnabled && ( handle->state == TR_UPNP_DISCOVER ) )
86    {
87        struct UPNPDev * devlist;
88        errno = 0;
89        devlist = upnpDiscover( 2000, NULL, NULL );
90        if( devlist == NULL ) {
91            tr_err( _( "%s: upnpDiscover returned NULL (errno %d - %s)" ), getKey(), errno, tr_strerror(errno) );
92        }
93        errno = 0;
94        if( UPNP_GetValidIGD( devlist, &handle->urls, &handle->data, handle->lanaddr, sizeof(handle->lanaddr))) {
95            tr_inf( _( "%s: Found Internet Gateway Device '%s'" ), getKey(), handle->urls.controlURL );
96            tr_inf( _( "%s: Local Address is '%s'" ), getKey(), handle->lanaddr );
97            handle->state = TR_UPNP_IDLE;
98            handle->hasDiscovered = 1;
99        } else {
100            handle->state = TR_UPNP_ERR;
101            tr_err( _( "%s: UPNP_GetValidIGD failed (errno %d - %s)" ), getKey(), errno, tr_strerror(errno) );
102            tr_err( _( "%s: If your router supports UPnP, please make sure UPnP is enabled!" ), getKey() );
103        }
104        freeUPNPDevlist( devlist );
105    }
106
107    if( handle->state == TR_UPNP_IDLE )
108    {
109        if( handle->isMapped && ( !isEnabled || ( handle->port != port ) ) )
110            handle->state = TR_UPNP_UNMAP;
111    }
112
113    if( handle->state == TR_UPNP_UNMAP )
114    {
115        char portStr[16];
116        snprintf( portStr, sizeof(portStr), "%d", handle->port );
117        UPNP_DeletePortMapping( handle->urls.controlURL,
118                                handle->data.servicetype,
119                                portStr, "TCP" );
120        tr_dbg( _( "%s: Stopping port forwarding of '%s', service '%s'" ),
121                getKey(), handle->urls.controlURL, handle->data.servicetype );
122        handle->isMapped = 0;
123        handle->state = TR_UPNP_IDLE;
124        handle->port = -1;
125    }
126
127    if( handle->state == TR_UPNP_IDLE )
128    {
129        if( isEnabled && !handle->isMapped )
130            handle->state = TR_UPNP_MAP;
131    }
132
133    if( handle->state == TR_UPNP_MAP )
134    {
135        int err = -1;
136        char portStr[16];
137        snprintf( portStr, sizeof(portStr), "%d", port );
138        errno = 0;
139
140        if( !handle->urls.controlURL || !handle->data.servicetype )
141            handle->isMapped = 0;
142        else {
143            err = UPNP_AddPortMapping( handle->urls.controlURL,
144                                       handle->data.servicetype,
145                                       portStr, portStr, handle->lanaddr,
146                                       "Transmission", "TCP" );
147            handle->isMapped = !err;
148        }
149        tr_inf( _( "%s: Port forwarding via '%s', service '%s'.  (local address: %s:%d)" ),
150                getKey(), handle->urls.controlURL, handle->data.servicetype, handle->lanaddr, port );
151        if( handle->isMapped ) {
152            tr_inf( _( "%s: Port forwarding successful!" ), getKey() );
153            handle->port = port;
154            handle->state = TR_UPNP_IDLE;
155        } else {
156            tr_err( _( "%s: Port forwarding failed with error %d (%d - %s)" ), getKey(), err, errno, tr_strerror(errno) );
157            tr_err( _( "%s: If your router supports UPnP, please make sure UPnP is enabled!" ), getKey() );
158            handle->port = -1;
159            handle->state = TR_UPNP_ERR;
160        }
161    }
162
163    switch( handle->state )
164    {
165        case TR_UPNP_DISCOVER: ret = TR_NAT_TRAVERSAL_UNMAPPED; break;
166        case TR_UPNP_MAP:      ret = TR_NAT_TRAVERSAL_MAPPING; break;
167        case TR_UPNP_UNMAP:    ret = TR_NAT_TRAVERSAL_UNMAPPING; break;
168        case TR_UPNP_IDLE:     ret = handle->isMapped ? TR_NAT_TRAVERSAL_MAPPED
169                                                      : TR_NAT_TRAVERSAL_UNMAPPED; break;
170        default:               ret = TR_NAT_TRAVERSAL_ERROR; break;
171    }
172
173    return ret;
174}
Note: See TracBrowser for help on using the repository browser.