transmission 2.83
[tomato.git] / release / src-rt-6.x.4708 / router / transmission / libtransmission / upnp.c
blob14b2f25c7f93ab8db39ed1ae141aec46b1a9f507
1 /*
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 $
8 */
10 #include <assert.h>
11 #include <errno.h>
13 #ifdef SYSTEM_MINIUPNP
14 #include <miniupnpc/miniupnpc.h>
15 #include <miniupnpc/upnpcommands.h>
16 #else
17 #include <miniupnp/miniupnpc.h>
18 #include <miniupnp/upnpcommands.h>
19 #endif
21 #include "transmission.h"
22 #include "log.h"
23 #include "port-forwarding.h"
24 #include "session.h"
25 #include "upnp.h"
26 #include "utils.h"
28 static const char *
29 getKey (void) { return _("Port Forwarding (UPnP)"); }
31 typedef enum
33 TR_UPNP_IDLE,
34 TR_UPNP_ERR,
35 TR_UPNP_DISCOVER,
36 TR_UPNP_MAP,
37 TR_UPNP_UNMAP
39 tr_upnp_state;
41 struct tr_upnp
43 bool hasDiscovered;
44 struct UPNPUrls urls;
45 struct IGDdatas data;
46 int port;
47 char lanaddr[16];
48 unsigned int isMapped;
49 tr_upnp_state state;
52 /**
53 ***
54 **/
56 tr_upnp*
57 tr_upnpInit (void)
59 tr_upnp * ret = tr_new0 (tr_upnp, 1);
61 ret->state = TR_UPNP_DISCOVER;
62 ret->port = -1;
63 return ret;
66 void
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);
76 tr_free (handle);
79 /**
80 *** Wrappers for miniupnpc functions
81 **/
83 static struct UPNPDev *
84 tr_upnpDiscover (int msec)
86 struct UPNPDev * ret;
87 bool have_err;
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;
93 #else
94 ret = upnpDiscover (msec, NULL, NULL, 0);
95 have_err = ret == NULL;
96 #endif
98 if (have_err)
99 tr_logAddNamedDbg (getKey (), "upnpDiscover failed (errno %d - %s)", errno, tr_strerror (errno));
101 return ret;
104 static int
105 tr_upnpGetSpecificPortMappingEntry (tr_upnp * handle, const char * proto)
107 int err;
108 char intClient[16];
109 char intPort[16];
110 char portStr[16];
112 *intClient = '\0';
113 *intPort = '\0';
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,
121 portStr,
122 proto,
123 NULL /*remoteHost*/,
124 intClient,
125 intPort,
126 NULL /*desc*/,
127 NULL /*enabled*/,
128 NULL /*duration*/);
130 #elif (MINIUPNPC_API_VERSION >= 8) /* adds desc, enabled and leaseDuration args */
132 err = UPNP_GetSpecificPortMappingEntry (handle->urls.controlURL,
133 handle->data.first.servicetype,
134 portStr,
135 proto,
136 intClient,
137 intPort,
138 NULL /*desc*/,
139 NULL /*enabled*/,
140 NULL /*duration*/);
142 #else
144 err = UPNP_GetSpecificPortMappingEntry (handle->urls.controlURL,
145 handle->data.first.servicetype,
146 portStr,
147 proto,
148 intClient,
149 intPort);
151 #endif
153 return err;
156 static int
157 tr_upnpAddPortMapping (const tr_upnp * handle, const char * proto, tr_port port, const char * desc)
159 int err;
160 const int old_errno = errno;
161 char portStr[16];
162 errno = 0;
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);
168 #else
169 err = UPNP_AddPortMapping (handle->urls.controlURL, handle->data.first.servicetype, portStr, portStr, handle->lanaddr, desc, proto, NULL);
170 #endif
172 if (err)
173 tr_logAddNamedDbg (getKey (), "%s Port forwarding failed with error %d (errno %d - %s)", proto, err, errno, tr_strerror (errno));
175 errno = old_errno;
176 return err;
179 static void
180 tr_upnpDeletePortMapping (const tr_upnp * handle, const char * proto, tr_port port)
182 char portStr[16];
184 tr_snprintf (portStr, sizeof (portStr), "%d", (int)port);
186 UPNP_DeletePortMapping (handle->urls.controlURL,
187 handle->data.first.servicetype,
188 portStr, proto, NULL);
195 enum
197 UPNP_IGD_NONE = 0,
198 UPNP_IGD_VALID_CONNECTED = 1,
199 UPNP_IGD_VALID_NOT_CONNECTED = 2,
200 UPNP_IGD_INVALID = 3
204 tr_upnpPulse (tr_upnp * handle,
205 int port,
206 int isEnabled,
207 int doPortCheck)
209 int ret;
211 if (isEnabled && (handle->state == TR_UPNP_DISCOVER))
213 struct UPNPDev * devlist;
215 devlist = tr_upnpDiscover (2000);
217 errno = 0;
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;
229 else
231 handle->state = TR_UPNP_ERR;
232 tr_logAddNamedDbg (
233 getKey (), "UPNP_GetValidIGD failed (errno %d - %s)",
234 errno,
235 tr_strerror (errno));
236 tr_logAddNamedDbg (
237 getKey (),
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;
270 handle->port = -1;
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)
281 int err_tcp = -1;
282 int err_udp = -1;
283 errno = 0;
285 if (!handle->urls.controlURL || !handle->data.first.servicetype)
286 handle->isMapped = 0;
287 else
289 char desc[64];
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!"));
304 handle->port = port;
305 handle->state = TR_UPNP_IDLE;
307 else
309 tr_logAddNamedDbg (getKey (), "If your router supports UPnP, please make sure UPnP is enabled!");
310 handle->port = -1;
311 handle->state = TR_UPNP_ERR;
315 switch (handle->state)
317 case TR_UPNP_DISCOVER:
318 ret = TR_PORT_UNMAPPED; break;
320 case TR_UPNP_MAP:
321 ret = TR_PORT_MAPPING; break;
323 case TR_UPNP_UNMAP:
324 ret = TR_PORT_UNMAPPING; break;
326 case TR_UPNP_IDLE:
327 ret = handle->isMapped ? TR_PORT_MAPPED
328 : TR_PORT_UNMAPPED; break;
330 default:
331 ret = TR_PORT_ERROR; break;
334 return ret;