source: trunk/libtransmission/upnp.c @ 4154

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

take pea_'s suggestion of not sending out natpmp/upnp discover messages until port forwarding is enabled

  • Property svn:keywords set to Date Rev Author Id
File size: 4.9 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 4154 2007-12-13 18:56: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 ) || ( handle->state == TR_UPNP_ERR ) );
69
70    if( handle->hasDiscovered )
71        FreeUPNPUrls( &handle->urls );
72    tr_free( handle );
73}
74
75/**
76***
77**/
78
79int
80tr_upnpPulse( tr_upnp * handle, int port, int isEnabled )
81{
82    int ret;
83
84    if( isEnabled && ( handle->state == TR_UPNP_DISCOVER ) )
85    {
86        struct UPNPDev * devlist;
87        errno = 0;
88        devlist = upnpDiscover( 2000, NULL );
89        if( devlist == NULL ) {
90            tr_err( KEY "upnpDiscover returned NULL (errno %d - %s)", errno, strerror(errno) );
91        }
92        errno = 0;
93        if( UPNP_GetValidIGD( devlist, &handle->urls, &handle->data, handle->lanaddr, sizeof(handle->lanaddr))) {
94            tr_inf( KEY "found Internet Gateway Device '%s'", handle->urls.controlURL );
95            tr_inf( KEY "local LAN IP Address is '%s'", handle->lanaddr );
96            handle->state = TR_UPNP_IDLE;
97            handle->hasDiscovered = 1;
98        } else {
99            handle->state = TR_UPNP_ERR;
100            tr_err( KEY "UPNP_GetValidIGD failed.  (errno %d - %s)", errno, strerror(errno) );
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( KEY "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        char portStr[16];
134        snprintf( portStr, sizeof(portStr), "%d", port );
135        errno = 0;
136        handle->isMapped = ( handle->urls.controlURL != NULL ) && 
137                           ( handle->data.servicetype != NULL ) &&
138                           ( UPNP_AddPortMapping( handle->urls.controlURL,
139                                                  handle->data.servicetype,
140                                                  portStr, portStr, handle->lanaddr,
141                                                  "Transmission", "TCP" ) );
142        tr_inf( KEY "port forwarding via '%s', service '%s'.  (local address: %s:%d)",
143                handle->urls.controlURL, handle->data.servicetype, handle->lanaddr, handle->port );
144        if( handle->isMapped ) {
145            tr_inf( KEY "port forwarding successful!" );
146            handle->port = port;
147            handle->state = TR_UPNP_IDLE;
148        } else {
149            tr_err( KEY "port forwarding failed (errno %d - %s)", errno, strerror(errno) );
150            handle->port = -1;
151            handle->state = TR_UPNP_ERR;
152        }
153    }
154
155    switch( handle->state )
156    {
157        case TR_UPNP_DISCOVER: ret = TR_NAT_TRAVERSAL_UNMAPPED; break;
158        case TR_UPNP_MAP:      ret = TR_NAT_TRAVERSAL_MAPPING; break;
159        case TR_UPNP_UNMAP:    ret = TR_NAT_TRAVERSAL_UNMAPPING; break;
160        case TR_UPNP_IDLE:     ret = handle->isMapped ? TR_NAT_TRAVERSAL_MAPPED
161                                                      : TR_NAT_TRAVERSAL_UNMAPPED; break;
162        default:               ret = TR_NAT_TRAVERSAL_ERROR; break;
163    }
164
165    return ret;
166}
Note: See TracBrowser for help on using the repository browser.