source: trunk/libtransmission/upnp.c @ 4092

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

Use libnatpmp for port mapping. rewrite the upnp/natpmp manager.

NOTE: this will break the xpjets build until SoftwareElves? or a volunteer patches the xcode project file to make a libnatpmp library just like was done for libminiupnp.

  • Property svn:keywords set to Date Rev Author Id
File size: 4.5 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 4092 2007-12-08 19:34:15Z charles $
11 */
12
13#include <assert.h>
14#include <stdio.h> /* snprintf */
15
16#include <miniupnp/miniwget.h>
17#include <miniupnp/miniupnpc.h>
18#include <miniupnp/upnpcommands.h>
19
20#include "transmission.h"
21#include "internal.h"
22#include "shared.h"
23#include "utils.h"
24#include "upnp.h"
25
26#define KEY "Port Mapping (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    struct UPNPUrls urls;
41    struct IGDdatas data;
42    int port;
43    char lanaddr[16];
44    unsigned int isMapped;
45    unsigned int hasDiscovered : 1;
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    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 ) || ( handle->state == TR_UPNP_ERR ) );
66
67    if( handle->hasDiscovered )
68        FreeUPNPUrls( &handle->urls );
69    tr_free( handle );
70}
71
72/**
73***
74**/
75
76int
77tr_upnpPulse( tr_upnp * handle, int port, int isEnabled )
78{
79    int ret;
80
81    if( handle->state == TR_UPNP_IDLE )
82    {
83        if( !handle->hasDiscovered )
84            handle->state = TR_UPNP_DISCOVER;
85    }
86
87    if( handle->state == TR_UPNP_DISCOVER )
88    {
89        struct UPNPDev * devlist = upnpDiscover( 2000, NULL );
90        if( UPNP_GetValidIGD( devlist, &handle->urls, &handle->data, handle->lanaddr, sizeof(handle->lanaddr))) {
91            tr_inf( KEY "found Internet Gateway Device '%s'", handle->urls.controlURL );
92            tr_inf( KEY "local LAN IP Address is '%s'", handle->lanaddr );
93            handle->state = TR_UPNP_IDLE;
94            handle->hasDiscovered = 1;
95        } else {
96            handle->state = TR_UPNP_ERR;
97        }
98        freeUPNPDevlist( devlist );
99    }
100
101    if( handle->state == TR_UPNP_IDLE )
102    {
103        if( handle->isMapped && ( !isEnabled || ( handle->port != port ) ) )
104            handle->state = TR_UPNP_UNMAP;
105    }
106
107    if( handle->state == TR_UPNP_UNMAP )
108    {
109        char portStr[16];
110        snprintf( portStr, sizeof(portStr), "%d", handle->port );
111        UPNP_DeletePortMapping( handle->urls.controlURL,
112                                handle->data.servicetype,
113                                portStr, "TCP" );
114        tr_dbg( KEY "stopping port forwarding of '%s', service '%s'",
115                handle->urls.controlURL, handle->data.servicetype );
116        handle->isMapped = 0;
117        handle->state = TR_UPNP_IDLE;
118        handle->port = -1;
119    }
120
121    if( handle->state == TR_UPNP_IDLE )
122    {
123        if( isEnabled && !handle->isMapped )
124            handle->state = TR_UPNP_MAP;
125    }
126
127    if( handle->state == TR_UPNP_MAP )
128    {
129        char portStr[16];
130        snprintf( portStr, sizeof(portStr), "%d", port );
131        handle->isMapped = ( handle->urls.controlURL != NULL ) && 
132                           ( handle->data.servicetype != NULL ) &&
133                           ( UPNP_AddPortMapping( handle->urls.controlURL,
134                                                  handle->data.servicetype,
135                                                  portStr, portStr, handle->lanaddr,
136                                                  "Transmission", "TCP" ) );
137        tr_inf( KEY "port forwarding via '%s', service '%s'.  (local address: %s:%d)",
138                handle->urls.controlURL, handle->data.servicetype, handle->lanaddr, handle->port );
139        if( handle->isMapped ) {
140            tr_inf( KEY "port forwarding successful!" );
141            handle->port = port;
142            handle->state = TR_UPNP_IDLE;
143        } else {
144            tr_err( KEY "port forwarding failed" );
145            handle->port = -1;
146            handle->state = TR_UPNP_ERR;
147        }
148    }
149
150    if( handle->state == TR_UPNP_ERR )
151        ret = TR_NAT_TRAVERSAL_ERROR;
152    else if( ( handle->state == TR_UPNP_IDLE ) && handle->isMapped )
153        ret = TR_NAT_TRAVERSAL_MAPPED;
154    else if( ( handle->state == TR_UPNP_IDLE ) && !handle->isMapped )
155        ret = TR_NAT_TRAVERSAL_UNMAPPED;
156    else if( handle->state == TR_UPNP_MAP )
157        ret = TR_NAT_TRAVERSAL_MAPPING;
158    else if( handle->state == TR_UPNP_UNMAP )
159        ret = TR_NAT_TRAVERSAL_UNMAPPING;
160    return ret;
161}
Note: See TracBrowser for help on using the repository browser.