2 * This file Copyright (C) 2007-2010 Mnemosyne LLC
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.
10 * $Id: upnp.c 10900 2010-06-27 15:29:23Z charles $
16 #include <miniupnp/miniupnpc.h>
17 #include <miniupnp/upnpcommands.h>
19 #include "transmission.h"
20 #include "port-forwarding.h"
26 getKey( void ) { return _( "Port Forwarding (UPnP)" ); }
40 tr_bool hasDiscovered
;
45 unsigned int isMapped
;
56 tr_upnp
* ret
= tr_new0( tr_upnp
, 1 );
58 ret
->state
= TR_UPNP_DISCOVER
;
64 tr_upnpClose( tr_upnp
* handle
)
66 assert( !handle
->isMapped
);
67 assert( ( handle
->state
== TR_UPNP_IDLE
)
68 || ( handle
->state
== TR_UPNP_ERR
)
69 || ( handle
->state
== TR_UPNP_DISCOVER
) );
71 if( handle
->hasDiscovered
)
72 FreeUPNPUrls( &handle
->urls
);
83 UPNP_IGD_VALID_CONNECTED
= 1,
84 UPNP_IGD_VALID_NOT_CONNECTED
= 2,
89 tr_upnpPulse( tr_upnp
* handle
,
96 if( isEnabled
&& ( handle
->state
== TR_UPNP_DISCOVER
) )
98 struct UPNPDev
* devlist
;
100 devlist
= upnpDiscover( 2000, NULL
, NULL
, 0 );
101 if( devlist
== NULL
)
104 getKey( ), "upnpDiscover failed (errno %d - %s)", errno
,
105 tr_strerror( errno
) );
108 if( UPNP_GetValidIGD( devlist
, &handle
->urls
, &handle
->data
,
109 handle
->lanaddr
, sizeof( handle
->lanaddr
) ) == UPNP_IGD_VALID_CONNECTED
)
111 tr_ninf( getKey( ), _(
112 "Found Internet Gateway Device \"%s\"" ),
113 handle
->urls
.controlURL
);
114 tr_ninf( getKey( ), _(
115 "Local Address is \"%s\"" ), handle
->lanaddr
);
116 handle
->state
= TR_UPNP_IDLE
;
117 handle
->hasDiscovered
= 1;
121 handle
->state
= TR_UPNP_ERR
;
123 getKey( ), "UPNP_GetValidIGD failed (errno %d - %s)",
125 tr_strerror( errno
) );
128 "If your router supports UPnP, please make sure UPnP is enabled!" );
130 freeUPNPDevlist( devlist
);
133 if( handle
->state
== TR_UPNP_IDLE
)
135 if( handle
->isMapped
&& ( !isEnabled
|| ( handle
->port
!= port
) ) )
136 handle
->state
= TR_UPNP_UNMAP
;
139 if( isEnabled
&& handle
->isMapped
&& doPortCheck
)
146 tr_snprintf( portStr
, sizeof( portStr
), "%d", handle
->port
);
147 i
= UPNP_GetSpecificPortMappingEntry( handle
->urls
.controlURL
,
148 handle
->data
.first
.servicetype
, portStr
,
149 "TCP", intClient
, intPort
);
150 if( i
!= UPNPCOMMAND_SUCCESS
)
152 tr_ninf( getKey( ), _( "Port %d isn't forwarded" ), handle
->port
);
153 handle
->isMapped
= FALSE
;
157 if( handle
->state
== TR_UPNP_UNMAP
)
160 tr_snprintf( portStr
, sizeof( portStr
), "%d", handle
->port
);
161 UPNP_DeletePortMapping( handle
->urls
.controlURL
,
162 handle
->data
.first
.servicetype
,
163 portStr
, "TCP", NULL
);
166 "Stopping port forwarding through \"%s\", service \"%s\"" ),
167 handle
->urls
.controlURL
, handle
->data
.first
.servicetype
);
168 handle
->isMapped
= 0;
169 handle
->state
= TR_UPNP_IDLE
;
173 if( handle
->state
== TR_UPNP_IDLE
)
175 if( isEnabled
&& !handle
->isMapped
)
176 handle
->state
= TR_UPNP_MAP
;
179 if( handle
->state
== TR_UPNP_MAP
)
184 if( !handle
->urls
.controlURL
|| !handle
->data
.first
.servicetype
)
185 handle
->isMapped
= 0;
190 tr_snprintf( portStr
, sizeof( portStr
), "%d", port
);
191 tr_snprintf( desc
, sizeof( desc
), "%s at %d", TR_NAME
, port
);
192 err
= UPNP_AddPortMapping( handle
->urls
.controlURL
,
193 handle
->data
.first
.servicetype
,
194 portStr
, portStr
, handle
->lanaddr
,
196 handle
->isMapped
= !err
;
200 "Port forwarding through \"%s\", service \"%s\". (local address: %s:%d)" ),
201 handle
->urls
.controlURL
, handle
->data
.first
.servicetype
,
202 handle
->lanaddr
, port
);
203 if( handle
->isMapped
)
205 tr_ninf( getKey( ), "%s", _( "Port forwarding successful!" ) );
207 handle
->state
= TR_UPNP_IDLE
;
213 "Port forwarding failed with error %d (errno %d - %s)", err
,
214 errno
, tr_strerror( errno
) );
217 "If your router supports UPnP, please make sure UPnP is enabled!" );
219 handle
->state
= TR_UPNP_ERR
;
223 switch( handle
->state
)
225 case TR_UPNP_DISCOVER
:
226 ret
= TR_PORT_UNMAPPED
; break;
229 ret
= TR_PORT_MAPPING
; break;
232 ret
= TR_PORT_UNMAPPING
; break;
235 ret
= handle
->isMapped
? TR_PORT_MAPPED
236 : TR_PORT_UNMAPPED
; break;
239 ret
= TR_PORT_ERROR
; break;