2 * This file Copyright (C) 2007-2014 Mnemosyne LLC
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
7 * $Id: upnp.c 14262 2014-04-27 19:31:10Z jordan $
13 #ifdef SYSTEM_MINIUPNP
14 #include <miniupnpc/miniupnpc.h>
15 #include <miniupnpc/upnpcommands.h>
17 #include <miniupnp/miniupnpc.h>
18 #include <miniupnp/upnpcommands.h>
21 #include "transmission.h"
23 #include "port-forwarding.h"
29 getKey (void) { return _("Port Forwarding (UPnP)"); }
48 unsigned int isMapped
;
59 tr_upnp
* ret
= tr_new0 (tr_upnp
, 1);
61 ret
->state
= TR_UPNP_DISCOVER
;
67 tr_upnpClose (tr_upnp
* handle
)
69 assert (!handle
->isMapped
);
70 assert ((handle
->state
== TR_UPNP_IDLE
)
71 || (handle
->state
== TR_UPNP_ERR
)
72 || (handle
->state
== TR_UPNP_DISCOVER
));
74 if (handle
->hasDiscovered
)
75 FreeUPNPUrls (&handle
->urls
);
80 *** Wrappers for miniupnpc functions
83 static struct UPNPDev
*
84 tr_upnpDiscover (int msec
)
89 #if (MINIUPNPC_API_VERSION >= 8) /* adds ipv6 and error args */
90 int err
= UPNPDISCOVER_SUCCESS
;
91 ret
= upnpDiscover (msec
, NULL
, NULL
, 0, 0, &err
);
92 have_err
= err
!= UPNPDISCOVER_SUCCESS
;
94 ret
= upnpDiscover (msec
, NULL
, NULL
, 0);
95 have_err
= ret
== NULL
;
99 tr_logAddNamedDbg (getKey (), "upnpDiscover failed (errno %d - %s)", errno
, tr_strerror (errno
));
105 tr_upnpGetSpecificPortMappingEntry (tr_upnp
* handle
, const char * proto
)
115 tr_snprintf (portStr
, sizeof(portStr
), "%d", (int)handle
->port
);
117 #if (MINIUPNPC_API_VERSION >= 10) /* adds remoteHost arg */
119 err
= UPNP_GetSpecificPortMappingEntry (handle
->urls
.controlURL
,
120 handle
->data
.first
.servicetype
,
130 #elif (MINIUPNPC_API_VERSION >= 8) /* adds desc, enabled and leaseDuration args */
132 err
= UPNP_GetSpecificPortMappingEntry (handle
->urls
.controlURL
,
133 handle
->data
.first
.servicetype
,
144 err
= UPNP_GetSpecificPortMappingEntry (handle
->urls
.controlURL
,
145 handle
->data
.first
.servicetype
,
157 tr_upnpAddPortMapping (const tr_upnp
* handle
, const char * proto
, tr_port port
, const char * desc
)
160 const int old_errno
= errno
;
164 tr_snprintf (portStr
, sizeof (portStr
), "%d", (int)port
);
166 #if (MINIUPNPC_API_VERSION >= 8)
167 err
= UPNP_AddPortMapping (handle
->urls
.controlURL
, handle
->data
.first
.servicetype
, portStr
, portStr
, handle
->lanaddr
, desc
, proto
, NULL
, NULL
);
169 err
= UPNP_AddPortMapping (handle
->urls
.controlURL
, handle
->data
.first
.servicetype
, portStr
, portStr
, handle
->lanaddr
, desc
, proto
, NULL
);
173 tr_logAddNamedDbg (getKey (), "%s Port forwarding failed with error %d (errno %d - %s)", proto
, err
, errno
, tr_strerror (errno
));
180 tr_upnpDeletePortMapping (const tr_upnp
* handle
, const char * proto
, tr_port port
)
184 tr_snprintf (portStr
, sizeof (portStr
), "%d", (int)port
);
186 UPNP_DeletePortMapping (handle
->urls
.controlURL
,
187 handle
->data
.first
.servicetype
,
188 portStr
, proto
, NULL
);
198 UPNP_IGD_VALID_CONNECTED
= 1,
199 UPNP_IGD_VALID_NOT_CONNECTED
= 2,
204 tr_upnpPulse (tr_upnp
* handle
,
211 if (isEnabled
&& (handle
->state
== TR_UPNP_DISCOVER
))
213 struct UPNPDev
* devlist
;
215 devlist
= tr_upnpDiscover (2000);
218 if (UPNP_GetValidIGD (devlist
, &handle
->urls
, &handle
->data
,
219 handle
->lanaddr
, sizeof (handle
->lanaddr
)) == UPNP_IGD_VALID_CONNECTED
)
221 tr_logAddNamedInfo (getKey (), _(
222 "Found Internet Gateway Device \"%s\""),
223 handle
->urls
.controlURL
);
224 tr_logAddNamedInfo (getKey (), _(
225 "Local Address is \"%s\""), handle
->lanaddr
);
226 handle
->state
= TR_UPNP_IDLE
;
227 handle
->hasDiscovered
= 1;
231 handle
->state
= TR_UPNP_ERR
;
233 getKey (), "UPNP_GetValidIGD failed (errno %d - %s)",
235 tr_strerror (errno
));
238 "If your router supports UPnP, please make sure UPnP is enabled!");
240 freeUPNPDevlist (devlist
);
243 if (handle
->state
== TR_UPNP_IDLE
)
245 if (handle
->isMapped
&& (!isEnabled
|| (handle
->port
!= port
)))
246 handle
->state
= TR_UPNP_UNMAP
;
249 if (isEnabled
&& handle
->isMapped
&& doPortCheck
)
251 if ((tr_upnpGetSpecificPortMappingEntry (handle
, "TCP") != UPNPCOMMAND_SUCCESS
) ||
252 (tr_upnpGetSpecificPortMappingEntry (handle
, "UDP") != UPNPCOMMAND_SUCCESS
))
254 tr_logAddNamedInfo (getKey (), _("Port %d isn't forwarded"), handle
->port
);
255 handle
->isMapped
= false;
259 if (handle
->state
== TR_UPNP_UNMAP
)
261 tr_upnpDeletePortMapping (handle
, "TCP", handle
->port
);
262 tr_upnpDeletePortMapping (handle
, "UDP", handle
->port
);
264 tr_logAddNamedInfo (getKey (),
265 _("Stopping port forwarding through \"%s\", service \"%s\""),
266 handle
->urls
.controlURL
, handle
->data
.first
.servicetype
);
268 handle
->isMapped
= 0;
269 handle
->state
= TR_UPNP_IDLE
;
273 if (handle
->state
== TR_UPNP_IDLE
)
275 if (isEnabled
&& !handle
->isMapped
)
276 handle
->state
= TR_UPNP_MAP
;
279 if (handle
->state
== TR_UPNP_MAP
)
285 if (!handle
->urls
.controlURL
|| !handle
->data
.first
.servicetype
)
286 handle
->isMapped
= 0;
290 tr_snprintf (desc
, sizeof (desc
), "%s at %d", TR_NAME
, port
);
292 err_tcp
= tr_upnpAddPortMapping (handle
, "TCP", port
, desc
);
293 err_udp
= tr_upnpAddPortMapping (handle
, "UDP", port
, desc
);
295 handle
->isMapped
= !err_tcp
| !err_udp
;
297 tr_logAddNamedInfo (getKey (),
298 _("Port forwarding through \"%s\", service \"%s\". (local address: %s:%d)"),
299 handle
->urls
.controlURL
, handle
->data
.first
.servicetype
,
300 handle
->lanaddr
, port
);
301 if (handle
->isMapped
)
303 tr_logAddNamedInfo (getKey (), "%s", _("Port forwarding successful!"));
305 handle
->state
= TR_UPNP_IDLE
;
309 tr_logAddNamedDbg (getKey (), "If your router supports UPnP, please make sure UPnP is enabled!");
311 handle
->state
= TR_UPNP_ERR
;
315 switch (handle
->state
)
317 case TR_UPNP_DISCOVER
:
318 ret
= TR_PORT_UNMAPPED
; break;
321 ret
= TR_PORT_MAPPING
; break;
324 ret
= TR_PORT_UNMAPPING
; break;
327 ret
= handle
->isMapped
? TR_PORT_MAPPED
328 : TR_PORT_UNMAPPED
; break;
331 ret
= TR_PORT_ERROR
; break;