source: trunk/libtransmission/upnp.c @ 14525

Last change on this file since 14525 was 14525, checked in by mikedld, 6 years ago

Fix some issues revealed by coverity

  • Property svn:keywords set to Date Rev Author Id
File size: 9.1 KB
Line 
1/*
2 * This file Copyright (C) 2007-2014 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 * $Id: upnp.c 14525 2015-05-09 08:37:55Z mikedld $
8 */
9
10#include <assert.h>
11#include <errno.h>
12
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
20
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"
27
28static const char *
29getKey (void) { return _("Port Forwarding (UPnP)"); }
30
31typedef enum
32{
33    TR_UPNP_IDLE,
34    TR_UPNP_ERR,
35    TR_UPNP_DISCOVER,
36    TR_UPNP_MAP,
37    TR_UPNP_UNMAP
38}
39tr_upnp_state;
40
41struct tr_upnp
42{
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;
50};
51
52/**
53***
54**/
55
56tr_upnp*
57tr_upnpInit (void)
58{
59    tr_upnp * ret = tr_new0 (tr_upnp, 1);
60
61    ret->state = TR_UPNP_DISCOVER;
62    ret->port = -1;
63    return ret;
64}
65
66void
67tr_upnpClose (tr_upnp * handle)
68{
69    assert (!handle->isMapped);
70    assert ((handle->state == TR_UPNP_IDLE)
71          || (handle->state == TR_UPNP_ERR)
72          || (handle->state == TR_UPNP_DISCOVER));
73
74    if (handle->hasDiscovered)
75        FreeUPNPUrls (&handle->urls);
76    tr_free (handle);
77}
78
79/**
80***  Wrappers for miniupnpc functions
81**/
82
83static struct UPNPDev *
84tr_upnpDiscover (int msec)
85{
86  struct UPNPDev * ret;
87  bool have_err;
88
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
97
98  if (have_err)
99    tr_logAddNamedDbg (getKey (), "upnpDiscover failed (errno %d - %s)", errno, tr_strerror (errno));
100
101  return ret;
102}
103
104static int
105tr_upnpGetSpecificPortMappingEntry (tr_upnp * handle, const char * proto)
106{
107    int err;
108    char intClient[16];
109    char intPort[16];
110    char portStr[16];
111
112    *intClient = '\0';
113    *intPort = '\0';
114
115    tr_snprintf (portStr, sizeof(portStr), "%d", (int)handle->port);
116
117#if (MINIUPNPC_API_VERSION >= 10) /* adds remoteHost arg */
118
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*/);
129
130#elif (MINIUPNPC_API_VERSION >= 8) /* adds desc, enabled and leaseDuration args */
131
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*/);
141
142#else
143
144    err = UPNP_GetSpecificPortMappingEntry (handle->urls.controlURL,
145                                            handle->data.first.servicetype,
146                                            portStr,
147                                            proto,
148                                            intClient,
149                                            intPort);
150
151#endif
152
153    return err;
154}
155
156static int
157tr_upnpAddPortMapping (const tr_upnp * handle, const char * proto, tr_port port, const char * desc)
158{
159    int err;
160    const int old_errno = errno;
161    char portStr[16];
162    errno = 0;
163
164    tr_snprintf (portStr, sizeof (portStr), "%d", (int)port);
165
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
171
172    if (err)
173        tr_logAddNamedDbg (getKey (), "%s Port forwarding failed with error %d (errno %d - %s)", proto, err, errno, tr_strerror (errno));
174
175    errno = old_errno;
176    return err;
177}
178
179static void
180tr_upnpDeletePortMapping (const tr_upnp * handle, const char * proto, tr_port port)
181{
182    char portStr[16];
183
184    tr_snprintf (portStr, sizeof (portStr), "%d", (int)port);
185
186    UPNP_DeletePortMapping (handle->urls.controlURL,
187                            handle->data.first.servicetype,
188                            portStr, proto, NULL);
189}
190
191/**
192***
193**/
194
195enum
196{
197  UPNP_IGD_NONE = 0,
198  UPNP_IGD_VALID_CONNECTED = 1,
199  UPNP_IGD_VALID_NOT_CONNECTED = 2,
200  UPNP_IGD_INVALID = 3
201};
202
203int
204tr_upnpPulse (tr_upnp * handle,
205              int       port,
206              int       isEnabled,
207              int       doPortCheck)
208{
209    int ret;
210
211    if (isEnabled && (handle->state == TR_UPNP_DISCOVER))
212    {
213        struct UPNPDev * devlist;
214
215        devlist = tr_upnpDiscover (2000);
216
217        errno = 0;
218        if (UPNP_GetValidIGD (devlist, &handle->urls, &handle->data,
219                             handle->lanaddr, sizeof (handle->lanaddr)) == UPNP_IGD_VALID_CONNECTED)
220        {
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 = true;
228        }
229        else
230        {
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!");
239        }
240        freeUPNPDevlist (devlist);
241    }
242
243    if (handle->state == TR_UPNP_IDLE)
244    {
245        if (handle->isMapped && (!isEnabled || (handle->port != port)))
246            handle->state = TR_UPNP_UNMAP;
247    }
248
249    if (isEnabled && handle->isMapped && doPortCheck)
250    {
251        if ((tr_upnpGetSpecificPortMappingEntry (handle, "TCP") != UPNPCOMMAND_SUCCESS) ||
252          (tr_upnpGetSpecificPortMappingEntry (handle, "UDP") != UPNPCOMMAND_SUCCESS))
253        {
254            tr_logAddNamedInfo (getKey (), _("Port %d isn't forwarded"), handle->port);
255            handle->isMapped = false;
256        }
257    }
258
259    if (handle->state == TR_UPNP_UNMAP)
260    {
261        tr_upnpDeletePortMapping (handle, "TCP", handle->port);
262        tr_upnpDeletePortMapping (handle, "UDP", handle->port);
263
264        tr_logAddNamedInfo (getKey (),
265                 _("Stopping port forwarding through \"%s\", service \"%s\""),
266                 handle->urls.controlURL, handle->data.first.servicetype);
267
268        handle->isMapped = 0;
269        handle->state = TR_UPNP_IDLE;
270        handle->port = -1;
271    }
272
273    if (handle->state == TR_UPNP_IDLE)
274    {
275        if (isEnabled && !handle->isMapped)
276            handle->state = TR_UPNP_MAP;
277    }
278
279    if (handle->state == TR_UPNP_MAP)
280    {
281        int  err_tcp = -1;
282        int  err_udp = -1;
283        errno = 0;
284
285        if (!handle->urls.controlURL)
286            handle->isMapped = 0;
287        else
288        {
289            char desc[64];
290            tr_snprintf (desc, sizeof (desc), "%s at %d", TR_NAME, port);
291
292            err_tcp = tr_upnpAddPortMapping (handle, "TCP", port, desc);
293            err_udp = tr_upnpAddPortMapping (handle, "UDP", port, desc);
294
295            handle->isMapped = !err_tcp | !err_udp;
296        }
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)
302        {
303            tr_logAddNamedInfo (getKey (), "%s", _("Port forwarding successful!"));
304            handle->port = port;
305            handle->state = TR_UPNP_IDLE;
306        }
307        else
308        {
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;
312        }
313    }
314
315    switch (handle->state)
316    {
317        case TR_UPNP_DISCOVER:
318            ret = TR_PORT_UNMAPPED; break;
319
320        case TR_UPNP_MAP:
321            ret = TR_PORT_MAPPING; break;
322
323        case TR_UPNP_UNMAP:
324            ret = TR_PORT_UNMAPPING; break;
325
326        case TR_UPNP_IDLE:
327            ret = handle->isMapped ? TR_PORT_MAPPED
328                  : TR_PORT_UNMAPPED; break;
329
330        default:
331            ret = TR_PORT_ERROR; break;
332    }
333
334    return ret;
335}
336
Note: See TracBrowser for help on using the repository browser.