source: trunk/libtransmission/upnp.c @ 4251

Last change on this file since 4251 was 4251, checked in by charles, 15 years ago

upgrade to miniupnp-20071213

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