source: trunk/libtransmission/upnp.c @ 13625

Last change on this file since 13625 was 13625, checked in by jordan, 9 years ago

Follow more common whitespace style conventions in the C code (libtransmission, daemon, utils, cli, gtk).

  • Property svn:keywords set to Date Rev Author Id
File size: 8.1 KB
Line 
1/*
2 * This file Copyright (C) Mnemosyne LLC
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 13625 2012-12-05 17:29:46Z jordan $
11 */
12
13#include <assert.h>
14#include <errno.h>
15
16#ifdef SYSTEM_MINIUPNP
17  #include <miniupnpc/miniupnpc.h>
18  #include <miniupnpc/upnpcommands.h>
19#else
20  #include <miniupnp/miniupnpc.h>
21  #include <miniupnp/upnpcommands.h>
22#endif
23
24#ifdef SYS_DARWIN
25  #define HAVE_MINIUPNP_16 1
26#endif
27
28#include "transmission.h"
29#include "port-forwarding.h"
30#include "session.h"
31#include "upnp.h"
32#include "utils.h"
33
34static const char *
35getKey (void) { return _ ("Port Forwarding (UPnP)"); }
36
37typedef enum
38{
39    TR_UPNP_IDLE,
40    TR_UPNP_ERR,
41    TR_UPNP_DISCOVER,
42    TR_UPNP_MAP,
43    TR_UPNP_UNMAP
44}
45tr_upnp_state;
46
47struct tr_upnp
48{
49    bool               hasDiscovered;
50    struct UPNPUrls    urls;
51    struct IGDdatas    data;
52    int                port;
53    char               lanaddr[16];
54    unsigned int       isMapped;
55    tr_upnp_state      state;
56};
57
58/**
59***
60**/
61
62tr_upnp*
63tr_upnpInit (void)
64{
65    tr_upnp * ret = tr_new0 (tr_upnp, 1);
66
67    ret->state = TR_UPNP_DISCOVER;
68    ret->port = -1;
69    return ret;
70}
71
72void
73tr_upnpClose (tr_upnp * handle)
74{
75    assert (!handle->isMapped);
76    assert ((handle->state == TR_UPNP_IDLE)
77          || (handle->state == TR_UPNP_ERR)
78          || (handle->state == TR_UPNP_DISCOVER));
79
80    if (handle->hasDiscovered)
81        FreeUPNPUrls (&handle->urls);
82    tr_free (handle);
83}
84
85/**
86***  Wrappers for miniupnpc functions
87**/
88
89static struct UPNPDev *
90tr_upnpDiscover (int msec)
91{
92    int err = 0;
93    struct UPNPDev * ret = NULL;
94
95#if defined (HAVE_MINIUPNP_16)
96    ret = upnpDiscover (msec, NULL, NULL, 0, 0, &err);
97#elif defined (HAVE_MINIUPNP_15)
98    ret = upnpDiscover (msec, NULL, NULL, 0);
99#else
100    ret = UPNPCOMMAND_UNKNOWN_ERROR;
101#endif
102
103    if (ret != UPNPCOMMAND_SUCCESS)
104        tr_ndbg (getKey (), "upnpDiscover failed (errno %d - %s)", err, tr_strerror (err));
105
106    return ret;
107}
108
109static int
110tr_upnpGetSpecificPortMappingEntry (tr_upnp * handle, const char * proto)
111{
112    int err;
113    char intClient[16];
114    char intPort[16];
115    char portStr[16];
116
117    *intClient = '\0';
118    *intPort = '\0';
119
120    tr_snprintf (portStr, sizeof (portStr), "%d", (int)handle->port);
121
122#if defined (HAVE_MINIUPNP_16)
123    err = UPNP_GetSpecificPortMappingEntry (handle->urls.controlURL, handle->data.first.servicetype, portStr, proto, intClient, intPort, NULL, NULL, NULL);
124#elif defined (HAVE_MINIUPNP_15)
125    err = UPNP_GetSpecificPortMappingEntry (handle->urls.controlURL, handle->data.first.servicetype, portStr, proto, intClient, intPort);
126#else
127    err = UPNPCOMMAND_UNKNOWN_ERROR;
128#endif
129
130    return err;
131}
132
133static int
134tr_upnpAddPortMapping (const tr_upnp * handle, const char * proto, tr_port port, const char * desc)
135{
136    int err;
137    const int old_errno = errno;
138    char portStr[16];
139    errno = 0;
140
141    tr_snprintf (portStr, sizeof (portStr), "%d", (int)port);
142
143#if defined (HAVE_MINIUPNP_16)
144    err = UPNP_AddPortMapping (handle->urls.controlURL, handle->data.first.servicetype, portStr, portStr, handle->lanaddr, desc, proto, NULL, NULL);
145#elif defined (HAVE_MINIUPNP_15)
146    err = UPNP_AddPortMapping (handle->urls.controlURL, handle->data.first.servicetype, portStr, portStr, handle->lanaddr, desc, proto, NULL);
147#else
148    err = UPNPCOMMAND_UNKNOWN_ERROR;
149#endif
150
151    if (err)
152        tr_ndbg (getKey (), "%s Port forwarding failed with error %d (errno %d - %s)", proto, err, errno, tr_strerror (errno));
153
154    errno = old_errno;
155    return err;
156}
157
158static void
159tr_upnpDeletePortMapping (const tr_upnp * handle, const char * proto, tr_port port)
160{
161    char portStr[16];
162
163    tr_snprintf (portStr, sizeof (portStr), "%d", (int)port);
164
165    UPNP_DeletePortMapping (handle->urls.controlURL,
166                            handle->data.first.servicetype,
167                            portStr, proto, NULL);
168}
169
170/**
171***
172**/
173
174enum
175{
176  UPNP_IGD_NONE = 0,
177  UPNP_IGD_VALID_CONNECTED = 1,
178  UPNP_IGD_VALID_NOT_CONNECTED = 2,
179  UPNP_IGD_INVALID = 3
180};
181
182int
183tr_upnpPulse (tr_upnp * handle,
184              int       port,
185              int       isEnabled,
186              int       doPortCheck)
187{
188    int ret;
189
190    if (isEnabled && (handle->state == TR_UPNP_DISCOVER))
191    {
192        struct UPNPDev * devlist;
193
194        devlist = tr_upnpDiscover (2000);
195
196        errno = 0;
197        if (UPNP_GetValidIGD (devlist, &handle->urls, &handle->data,
198                             handle->lanaddr, sizeof (handle->lanaddr)) == UPNP_IGD_VALID_CONNECTED)
199        {
200            tr_ninf (getKey (), _ (
201                         "Found Internet Gateway Device \"%s\""),
202                     handle->urls.controlURL);
203            tr_ninf (getKey (), _ (
204                         "Local Address is \"%s\""), handle->lanaddr);
205            handle->state = TR_UPNP_IDLE;
206            handle->hasDiscovered = 1;
207        }
208        else
209        {
210            handle->state = TR_UPNP_ERR;
211            tr_ndbg (
212                 getKey (), "UPNP_GetValidIGD failed (errno %d - %s)",
213                errno,
214                tr_strerror (errno));
215            tr_ndbg (
216                getKey (),
217                "If your router supports UPnP, please make sure UPnP is enabled!");
218        }
219        freeUPNPDevlist (devlist);
220    }
221
222    if (handle->state == TR_UPNP_IDLE)
223    {
224        if (handle->isMapped && (!isEnabled || (handle->port != port)))
225            handle->state = TR_UPNP_UNMAP;
226    }
227
228    if (isEnabled && handle->isMapped && doPortCheck)
229    {
230        if ((tr_upnpGetSpecificPortMappingEntry (handle, "TCP") != UPNPCOMMAND_SUCCESS) ||
231          (tr_upnpGetSpecificPortMappingEntry (handle, "UDP") != UPNPCOMMAND_SUCCESS))
232        {
233            tr_ninf (getKey (), _ ("Port %d isn't forwarded"), handle->port);
234            handle->isMapped = false;
235        }
236    }
237
238    if (handle->state == TR_UPNP_UNMAP)
239    {
240        tr_upnpDeletePortMapping (handle, "TCP", handle->port);
241        tr_upnpDeletePortMapping (handle, "UDP", handle->port);
242
243        tr_ninf (getKey (),
244                 _ ("Stopping port forwarding through \"%s\", service \"%s\""),
245                 handle->urls.controlURL, handle->data.first.servicetype);
246
247        handle->isMapped = 0;
248        handle->state = TR_UPNP_IDLE;
249        handle->port = -1;
250    }
251
252    if (handle->state == TR_UPNP_IDLE)
253    {
254        if (isEnabled && !handle->isMapped)
255            handle->state = TR_UPNP_MAP;
256    }
257
258    if (handle->state == TR_UPNP_MAP)
259    {
260        int  err_tcp = -1;
261        int  err_udp = -1;
262        errno = 0;
263
264        if (!handle->urls.controlURL || !handle->data.first.servicetype)
265            handle->isMapped = 0;
266        else
267        {
268            char desc[64];
269            tr_snprintf (desc, sizeof (desc), "%s at %d", TR_NAME, port);
270
271            err_tcp = tr_upnpAddPortMapping (handle, "TCP", port, desc);
272            err_udp = tr_upnpAddPortMapping (handle, "UDP", port, desc);
273
274            handle->isMapped = !err_tcp | !err_udp;
275        }
276        tr_ninf (getKey (),
277                 _ ("Port forwarding through \"%s\", service \"%s\". (local address: %s:%d)"),
278                 handle->urls.controlURL, handle->data.first.servicetype,
279                 handle->lanaddr, port);
280        if (handle->isMapped)
281        {
282            tr_ninf (getKey (), "%s", _ ("Port forwarding successful!"));
283            handle->port = port;
284            handle->state = TR_UPNP_IDLE;
285        }
286        else
287        {
288            tr_ndbg (getKey (), "If your router supports UPnP, please make sure UPnP is enabled!");
289            handle->port = -1;
290            handle->state = TR_UPNP_ERR;
291        }
292    }
293
294    switch (handle->state)
295    {
296        case TR_UPNP_DISCOVER:
297            ret = TR_PORT_UNMAPPED; break;
298
299        case TR_UPNP_MAP:
300            ret = TR_PORT_MAPPING; break;
301
302        case TR_UPNP_UNMAP:
303            ret = TR_PORT_UNMAPPING; break;
304
305        case TR_UPNP_IDLE:
306            ret = handle->isMapped ? TR_PORT_MAPPED
307                  : TR_PORT_UNMAPPED; break;
308
309        default:
310            ret = TR_PORT_ERROR; break;
311    }
312
313    return ret;
314}
315
Note: See TracBrowser for help on using the repository browser.