OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
C4Network2UPnPWin32.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2012-2016, The OpenClonk Team and contributors
5  *
6  * Distributed under the terms of the ISC license; see accompanying file
7  * "COPYING" for details.
8  *
9  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
10  * See accompanying file "TRADEMARK" for details.
11  *
12  * To redistribute this file separately, substitute the full license texts
13  * for the above references.
14  */
15 /* Win32 implementation of a UPnP port mapper */
16 
17 #include "C4Include.h"
19 #include "network/C4Network2UPnP.h"
20 #include "C4Version.h"
21 
22 #ifdef __GNUC__
23 #pragma GCC diagnostic push
24 #pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
25 #pragma GCC diagnostic ignored "-Wredundant-decls"
26 #pragma GCC diagnostic ignored "-Wunknown-pragmas"
27 #endif
28 #include <natupnp.h>
29 #ifdef __GNUC__
30 #pragma GCC diagnostic pop
31 #endif
32 
33 #if defined(__MINGW32__) || defined(__MINGW64__)
34 // MinGW doesn't usually have these
35 extern "C" const CLSID CLSID_UPnPNAT = { 0xAE1E00AA, 0x3FD5, 0x403C, { 0x8A, 0x27, 0x2B, 0xBD, 0xC3, 0x0C, 0xD0, 0xE1 } };
36 extern "C" const IID IID_IUPnPNAT = { 0xB171C812, 0xCC76, 0x485A, { 0x94, 0xD8, 0xB6, 0xB3, 0xA2, 0x79, 0x4E, 0x99 } };
37 #endif
38 
39 namespace
40 {
41  static BSTR PROTO_UDP = ::SysAllocString(L"UDP");
42  static BSTR PROTO_TCP = ::SysAllocString(L"TCP");
43 
44  template<class T> inline void SafeRelease(T* &t)
45  {
46  if (t) t->Release();
47  t = nullptr;
48  }
49 }
50 
51 class C4Network2UPnPP
52 {
53 public:
55 
56  // NAT
57  IStaticPortMappingCollection *mappings;
58  std::set<IStaticPortMapping*> added_mappings;
59 
61  : MustReleaseCOM(false),
62  mappings(nullptr)
63  {}
64 
65  void AddMapping(C4Network2IOProtocol protocol, uint16_t intport, uint16_t extport);
66  void RemoveMapping(C4Network2IOProtocol protocol, uint16_t extport);
67  void ClearNatMappings();
68 };
69 
71  : p(new C4Network2UPnPP)
72 {
73  Log("UPnP init...");
74  // Make sure COM is available
75  if (FAILED(CoInitializeEx(0, COINIT_APARTMENTTHREADED)))
76  {
77  // Didn't work, don't do UPnP then
78  Log("UPnP fail (no COM).");
79  return;
80  }
81  p->MustReleaseCOM = true;
82 
83  // Get the NAT service
84  IUPnPNAT *nat = nullptr;
85  if (FAILED(CoCreateInstance(CLSID_UPnPNAT, nullptr, CLSCTX_INPROC_SERVER, IID_IUPnPNAT, reinterpret_cast<void**>(&nat))))
86  {
87  Log("UPnP fail (no service).");
88  return;
89  }
90 
91  // Fetch NAT mappings
92  for (int ctr = 0; ctr < 10; ++ctr)
93  {
94  // Usually it doesn't work on the first try, give Windows some time to query the IGD
95  if (SUCCEEDED(nat->get_StaticPortMappingCollection(&p->mappings)) && p->mappings)
96  {
97  LogF("UPnP: Got NAT port mapping table after %d tries", ctr+1);
98  break;
99  }
100  if (ctr == 2) Log(LoadResStr("IDS_MSG_UPNPHINT"));
101  Sleep(1000);
102  }
103 
104  SafeRelease(nat);
105 
106  if (!p->mappings) Log("UPnP fail (no mapping).");
107 }
108 
110 {
111  p->ClearNatMappings();
112  if (p->MustReleaseCOM)
113  {
114  // Decrement COM reference count
115  CoUninitialize();
116  }
117  delete p; p = nullptr;
118 }
119 
120 void C4Network2UPnP::AddMapping(C4Network2IOProtocol protocol, uint16_t intport, uint16_t extport)
121 {
122  p->AddMapping(protocol, intport, extport);
123 }
124 
126 {
127  p->ClearNatMappings();
128 }
129 
131 {
132  if (!mappings)
133  return;
134  for(IStaticPortMapping *mapping: added_mappings)
135  {
136  BSTR proto, client;
137  long intport, extport;
138  mapping->get_ExternalPort(&extport);
139  mapping->get_InternalPort(&intport);
140  mapping->get_InternalClient(&client);
141  mapping->get_Protocol(&proto);
142  if (SUCCEEDED(mappings->Remove(extport, proto)))
143  LogF("UPnP: Closed port %d->%s:%d (%s)", (int)extport, StdStrBuf(client).getData(), (int)intport, StdStrBuf(proto).getData());
144  ::SysFreeString(proto);
145  ::SysFreeString(client);
146  SafeRelease(mapping);
147  }
148  SafeRelease(mappings);
149 }
150 
151 void C4Network2UPnPP::AddMapping(C4Network2IOProtocol protocol, uint16_t intport, uint16_t extport)
152 {
153  if (mappings)
154  {
155  // Get (one of the) local host address(es)
156  char hostname[MAX_PATH];
157  hostent *host;
158  if (gethostname(hostname, MAX_PATH) == 0 && (host = gethostbyname(hostname)) != nullptr)
159  {
160  in_addr addr;
161  addr.s_addr = *(ULONG*)host->h_addr_list[0];
162 
163  BSTR description = ::SysAllocString(ADDL(C4ENGINECAPTION));
164  BSTR client = ::SysAllocString(GetWideChar(inet_ntoa(addr)));
165  IStaticPortMapping *mapping = nullptr;
166  if (SUCCEEDED(mappings->Add(extport, protocol == P_TCP ? PROTO_TCP : PROTO_UDP, intport, client, VARIANT_TRUE, description, &mapping)))
167  {
168  LogF("UPnP: Successfully opened port %d->%s:%d (%s)", extport, StdStrBuf(client).getData(), intport, protocol == P_TCP ? "TCP" : "UDP");
169  added_mappings.insert(mapping);
170  }
171  ::SysFreeString(description);
172  ::SysFreeString(client);
173  }
174  }
175 }
void RemoveMapping(C4Network2IOProtocol protocol, uint16_t extport)
StdStrBuf::wchar_t_holder GetWideChar(const char *utf8, bool double_null_terminate=false)
C4Network2IOProtocol
Definition: C4Network2IO.h:29
const char * LoadResStr(const char *id)
Definition: C4Language.h:83
void AddMapping(C4Network2IOProtocol protocol, uint16_t intport, uint16_t extport)
#define ADDL(s)
void AddMapping(enum C4Network2IOProtocol protocol, uint16_t intport, uint16_t extport)
bool Log(const char *szMessage)
Definition: C4Log.cpp:195
IStaticPortMappingCollection * mappings
std::set< IStaticPortMapping * > added_mappings
bool LogF(const char *strMessage,...)
Definition: C4Log.cpp:253