This repository has been archived by the owner on Oct 28, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
/
Copy pathUPnP.cpp
174 lines (160 loc) · 4.57 KB
/
UPnP.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// Aleth: Ethereum C++ client, tools and libraries.
// Copyright 2014-2019 Aleth Authors.
// Licensed under the GNU General Public License, Version 3.
/// @file
/// UPnP port forwarding support.
#include "UPnP.h"
#include <string.h>
#if ETH_MINIUPNPC
#include <miniupnpc/miniwget.h>
#include <miniupnpc/miniupnpc.h>
#include <miniupnpc/upnpcommands.h>
#endif
#include <libdevcore/Exceptions.h>
#include <libdevcore/CommonIO.h>
#include <libdevcore/Log.h>
using namespace std;
using namespace dev;
using namespace dev::p2p;
UPnP::UPnP()
{
#if ETH_MINIUPNPC
m_urls = make_shared<UPNPUrls>();
m_data = make_shared<IGDdatas>();
m_ok = false;
struct UPNPDev* devlist;
struct UPNPDev* dev;
char* descXML;
int descXMLsize = 0;
int upnperror = 0;
memset(m_urls.get(), 0, sizeof(struct UPNPUrls));
memset(m_data.get(), 0, sizeof(struct IGDdatas));
#if MINIUPNPC_API_VERSION >= 14
devlist = upnpDiscover(2000, NULL/*multicast interface*/, NULL/*minissdpd socket path*/, 0/*sameport*/, 0/*ipv6*/, 2/*ttl*/, &upnperror);
#else
devlist = upnpDiscover(2000, NULL/*multicast interface*/, NULL/*minissdpd socket path*/, 0/*sameport*/, 0/*ipv6*/, &upnperror);
#endif
if (devlist)
{
dev = devlist;
while (dev)
{
if (strstr (dev->st, "InternetGatewayDevice"))
break;
dev = dev->pNext;
}
if (!dev)
dev = devlist; /* defaulting to first device */
cnote << "UPnP device:" << dev->descURL << "[st:" << dev->st << "]";
#if MINIUPNPC_API_VERSION >= 16
int responsecode = 200;
descXML = (char*)miniwget(dev->descURL, &descXMLsize, 0, &responsecode);
#elif MINIUPNPC_API_VERSION >= 9
descXML = (char*)miniwget(dev->descURL, &descXMLsize, 0);
#else
descXML = (char*)miniwget(dev->descURL, &descXMLsize);
#endif
if (descXML)
{
parserootdesc(descXML, descXMLsize, m_data.get());
free(descXML);
#if MINIUPNPC_API_VERSION >= 9
GetUPNPUrls (m_urls.get(), m_data.get(), dev->descURL, 0);
#else
GetUPNPUrls (m_urls.get(), m_data.get(), dev->descURL);
#endif
m_ok = true;
}
freeUPNPDevlist(devlist);
}
else
#endif
{
cnote << "UPnP device not found.";
BOOST_THROW_EXCEPTION(NoUPnPDevice());
}
}
UPnP::~UPnP()
{
auto r = m_reg;
for (auto i: r)
removeRedirect(i);
}
string UPnP::externalIP()
{
#if ETH_MINIUPNPC
char addr[16];
if (!UPNP_GetExternalIPAddress(m_urls->controlURL, m_data->first.servicetype, addr))
return addr;
#endif
return "0.0.0.0";
}
int UPnP::addRedirect(char const* _addr, int _port)
{
(void)_addr;
(void)_port;
#if ETH_MINIUPNPC
if (m_urls->controlURL[0] == '\0')
{
cwarn << "UPnP::addRedirect() called without proper initialisation?";
return -1;
}
// Try direct mapping first (port external, port internal).
char port_str[16];
char ext_port_str[16];
sprintf(port_str, "%d", _port);
if (!UPNP_AddPortMapping(m_urls->controlURL, m_data->first.servicetype, port_str, port_str, _addr, "ethereum", "TCP", NULL, NULL))
return _port;
// Failed - now try (random external, port internal) and cycle up to 10 times.
srand(static_cast<unsigned int>(time(nullptr)));
for (unsigned i = 0; i < 10; ++i)
{
_port = rand() % (32768 - 1024) + 1024;
sprintf(ext_port_str, "%d", _port);
if (!UPNP_AddPortMapping(m_urls->controlURL, m_data->first.servicetype, ext_port_str, port_str, _addr, "ethereum", "TCP", NULL, NULL))
return _port;
}
// Failed. Try asking the router to give us a free external port.
if (UPNP_AddPortMapping(m_urls->controlURL, m_data->first.servicetype, port_str, NULL, _addr, "ethereum", "TCP", NULL, NULL))
// Failed. Exit.
return 0;
// We got mapped, but we don't know which ports we got mapped to. Now to find...
unsigned num = 0;
UPNP_GetPortMappingNumberOfEntries(m_urls->controlURL, m_data->first.servicetype, &num);
for (unsigned i = 0; i < num; ++i)
{
char extPort[16];
char intClient[16];
char intPort[6];
char protocol[4];
char desc[80];
char enabled[4];
char rHost[64];
char duration[16];
UPNP_GetGenericPortMappingEntry(m_urls->controlURL, m_data->first.servicetype, toString(i).c_str(), extPort, intClient, intPort, protocol, desc, enabled, rHost, duration);
if (string("ethereum") == desc)
{
m_reg.insert(atoi(extPort));
return atoi(extPort);
}
}
cerr << "ERROR: Mapped port not found." << endl;
#endif
return 0;
}
void UPnP::removeRedirect(int _port)
{
(void)_port;
#if ETH_MINIUPNPC
char port_str[16];
printf("TB : upnp_rem_redir (%d)\n", _port);
if (m_urls->controlURL[0] == '\0')
{
printf("TB : the init was not done !\n");
return;
}
sprintf(port_str, "%d", _port);
UPNP_DeletePortMapping(m_urls->controlURL, m_data->first.servicetype, port_str, "TCP", NULL);
m_reg.erase(_port);
#endif
}