source: trunk/libtransmission/natpmp.c

Last change on this file was 14532, checked in by mikedld, 5 years ago

Add more booleans to the picture

  • Property svn:keywords set to Date Rev Author Id
File size: 6.7 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: natpmp.c 14532 2015-05-31 22:13:31Z mikedld $
8 */
9
10#include <errno.h>
11#include <time.h>
12#include <inttypes.h>
13
14#include <event2/util.h> /* evutil_inet_ntop () */
15
16#define ENABLE_STRNATPMPERR
17#include "natpmp.h"
18
19#include "transmission.h"
20#include "natpmp_local.h"
21#include "log.h"
22#include "net.h" /* tr_netCloseSocket */
23#include "port-forwarding.h"
24#include "utils.h"
25
26#define LIFETIME_SECS 3600
27#define COMMAND_WAIT_SECS 8
28
29static const char *
30getKey (void) { return _("Port Forwarding (NAT-PMP)"); }
31
32typedef enum
33{
34    TR_NATPMP_IDLE,
35    TR_NATPMP_ERR,
36    TR_NATPMP_DISCOVER,
37    TR_NATPMP_RECV_PUB,
38    TR_NATPMP_SEND_MAP,
39    TR_NATPMP_RECV_MAP,
40    TR_NATPMP_SEND_UNMAP,
41    TR_NATPMP_RECV_UNMAP
42}
43tr_natpmp_state;
44
45struct tr_natpmp
46{
47    bool              has_discovered;
48    bool              is_mapped;
49
50    tr_port           public_port;
51    tr_port           private_port;
52
53    time_t            renew_time;
54    time_t            command_time;
55    tr_natpmp_state   state;
56    natpmp_t          natpmp;
57};
58
59/**
60***
61**/
62
63static void
64logVal (const char * func,
65        int          ret)
66{
67    if (ret == NATPMP_TRYAGAIN)
68        return;
69    if (ret >= 0)
70        tr_logAddNamedInfo (getKey (), _("%s succeeded (%d)"), func, ret);
71    else
72        tr_logAddNamedDbg (
73             getKey (),
74            "%s failed. Natpmp returned %d (%s); errno is %d (%s)",
75            func, ret, strnatpmperr (ret), errno, tr_strerror (errno));
76}
77
78struct tr_natpmp*
79tr_natpmpInit (void)
80{
81    struct tr_natpmp * nat;
82
83    nat = tr_new0 (struct tr_natpmp, 1);
84    nat->state = TR_NATPMP_DISCOVER;
85    nat->public_port = 0;
86    nat->private_port = 0;
87    nat->natpmp.s = TR_BAD_SOCKET; /* socket */
88    return nat;
89}
90
91void
92tr_natpmpClose (tr_natpmp * nat)
93{
94    if (nat)
95    {
96        closenatpmp (&nat->natpmp);
97        tr_free (nat);
98    }
99}
100
101static bool
102canSendCommand (const struct tr_natpmp * nat)
103{
104    return tr_time () >= nat->command_time;
105}
106
107static void
108setCommandTime (struct tr_natpmp * nat)
109{
110    nat->command_time = tr_time () + COMMAND_WAIT_SECS;
111}
112
113int
114tr_natpmpPulse (struct tr_natpmp * nat, tr_port private_port, bool is_enabled, tr_port * public_port)
115{
116    int ret;
117
118    if (is_enabled && (nat->state == TR_NATPMP_DISCOVER))
119    {
120        int val = initnatpmp (&nat->natpmp, 0, 0);
121        logVal ("initnatpmp", val);
122        val = sendpublicaddressrequest (&nat->natpmp);
123        logVal ("sendpublicaddressrequest", val);
124        nat->state = val < 0 ? TR_NATPMP_ERR : TR_NATPMP_RECV_PUB;
125        nat->has_discovered = true;
126        setCommandTime (nat);
127    }
128
129    if ((nat->state == TR_NATPMP_RECV_PUB) && canSendCommand (nat))
130    {
131        natpmpresp_t response;
132        const int val = readnatpmpresponseorretry (&nat->natpmp, &response);
133        logVal ("readnatpmpresponseorretry", val);
134        if (val >= 0)
135        {
136            char str[128];
137            evutil_inet_ntop (AF_INET, &response.pnu.publicaddress.addr, str, sizeof (str));
138            tr_logAddNamedInfo (getKey (), _("Found public address \"%s\""), str);
139            nat->state = TR_NATPMP_IDLE;
140        }
141        else if (val != NATPMP_TRYAGAIN)
142        {
143            nat->state = TR_NATPMP_ERR;
144        }
145    }
146
147    if ((nat->state == TR_NATPMP_IDLE) || (nat->state == TR_NATPMP_ERR))
148    {
149        if (nat->is_mapped && (!is_enabled || (nat->private_port != private_port)))
150            nat->state = TR_NATPMP_SEND_UNMAP;
151    }
152
153    if ((nat->state == TR_NATPMP_SEND_UNMAP) && canSendCommand (nat))
154    {
155        const int val = sendnewportmappingrequest (&nat->natpmp, NATPMP_PROTOCOL_TCP,
156                                                   nat->private_port,
157                                                   nat->public_port,
158                                                   0);
159        logVal ("sendnewportmappingrequest", val);
160        nat->state = val < 0 ? TR_NATPMP_ERR : TR_NATPMP_RECV_UNMAP;
161        setCommandTime (nat);
162    }
163
164    if (nat->state == TR_NATPMP_RECV_UNMAP)
165    {
166        natpmpresp_t resp;
167        const int val = readnatpmpresponseorretry (&nat->natpmp, &resp);
168        logVal ("readnatpmpresponseorretry", val);
169        if (val >= 0)
170        {
171            const int private_port = resp.pnu.newportmapping.privateport;
172
173            tr_logAddNamedInfo (getKey (), _("no longer forwarding port %d"), private_port);
174
175            if (nat->private_port == private_port)
176            {
177                nat->private_port = 0;
178                nat->public_port = 0;
179                nat->state = TR_NATPMP_IDLE;
180                nat->is_mapped = false;
181            }
182        }
183        else if (val != NATPMP_TRYAGAIN)
184        {
185            nat->state = TR_NATPMP_ERR;
186        }
187    }
188
189    if (nat->state == TR_NATPMP_IDLE)
190    {
191        if (is_enabled && !nat->is_mapped && nat->has_discovered)
192            nat->state = TR_NATPMP_SEND_MAP;
193
194        else if (nat->is_mapped && tr_time () >= nat->renew_time)
195            nat->state = TR_NATPMP_SEND_MAP;
196    }
197
198    if ((nat->state == TR_NATPMP_SEND_MAP) && canSendCommand (nat))
199    {
200        const int val = sendnewportmappingrequest (&nat->natpmp, NATPMP_PROTOCOL_TCP, private_port, private_port, LIFETIME_SECS);
201        logVal ("sendnewportmappingrequest", val);
202        nat->state = val < 0 ? TR_NATPMP_ERR : TR_NATPMP_RECV_MAP;
203        setCommandTime (nat);
204    }
205
206    if (nat->state == TR_NATPMP_RECV_MAP)
207    {
208        natpmpresp_t resp;
209        const int    val = readnatpmpresponseorretry (&nat->natpmp, &resp);
210        logVal ("readnatpmpresponseorretry", val);
211        if (val >= 0)
212        {
213            nat->state = TR_NATPMP_IDLE;
214            nat->is_mapped = true;
215            nat->renew_time = tr_time () + (resp.pnu.newportmapping.lifetime / 2);
216            nat->private_port = resp.pnu.newportmapping.privateport;
217            nat->public_port = resp.pnu.newportmapping.mappedpublicport;
218            tr_logAddNamedInfo (getKey (), _("Port %d forwarded successfully"), nat->private_port);
219        }
220        else if (val != NATPMP_TRYAGAIN)
221        {
222            nat->state = TR_NATPMP_ERR;
223        }
224    }
225
226    switch (nat->state)
227    {
228        case TR_NATPMP_IDLE:
229            *public_port = nat->public_port;
230            return nat->is_mapped ? TR_PORT_MAPPED : TR_PORT_UNMAPPED;
231            break;
232
233        case TR_NATPMP_DISCOVER:
234            ret = TR_PORT_UNMAPPED; break;
235
236        case TR_NATPMP_RECV_PUB:
237        case TR_NATPMP_SEND_MAP:
238        case TR_NATPMP_RECV_MAP:
239            ret = TR_PORT_MAPPING; break;
240
241        case TR_NATPMP_SEND_UNMAP:
242        case TR_NATPMP_RECV_UNMAP:
243            ret = TR_PORT_UNMAPPING; break;
244
245        default:
246            ret = TR_PORT_ERROR; break;
247    }
248    return ret;
249}
250
Note: See TracBrowser for help on using the repository browser.