Stun Server  Compliant with the latest RFCs including 5389, 5769, and 5780
discover the local host's own external IP address
server.cpp
Go to the documentation of this file.
1 /*
2  Copyright 2011 John Selbie
3 
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7 
8  http://www.apache.org/licenses/LICENSE-2.0
9 
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15 */
16 
17 
18 #include "commonincludes.hpp"
19 #include "stuncore.h"
20 #include "stunsocket.h"
21 #include "stunsocketthread.h"
22 #include "server.h"
23 #include "ratelimiter.h"
24 
25 
26 
28 fHasPP(false),
29 fHasPA(false),
30 fHasAP(false),
31 fHasAA(false),
32 fMultiThreadedMode(false),
33 fTCP(false),
34 nMaxConnections(0), // zero means default
35 fEnableDosProtection(false)
36 {
37  ;
38 }
39 
40 
41 
43 _arrSockets() // zero-init
44 {
45  ;
46 }
47 
49 {
50  Shutdown();
51 }
52 
53 HRESULT CStunServer::AddSocket(TransportAddressSet* pTSA, SocketRole role, const CSocketAddress& addrListen, const CSocketAddress& addrAdvertise)
54 {
55  HRESULT hr = S_OK;
56 
58 
59  Chk(_arrSockets[role].UDPInit(addrListen, role));
60  ChkA(_arrSockets[role].EnablePktInfoOption(true));
61 
62 
63 #ifdef DEBUG
64  {
65  CSocketAddress addrLocal = _arrSockets[role].GetLocalAddress();
66 
67  // addrListen is the address we asked the socket to listen on via a call to bind()
68  // addrLocal is the socket address returned by getsockname after the socket is binded
69 
70  // I can't think of any case where addrListen != addrLocal
71  // the ports will be different if addrListen.GetPort() is 0, but that
72  // should never happen.
73 
74  // but if the assert below fails, I want to know about it
75  ASSERT(addrLocal.IsSameIP_and_Port(addrListen));
76  }
77 #endif
78 
79  pTSA->set[role].fValid = true;
80  if (addrAdvertise.IsIPAddressZero() == false)
81  {
82  // set the TSA for this socket to what the configuration wants us to advertise this address for in ORIGIN and OTHER address attributes
83  pTSA->set[role].addr = addrAdvertise;
84  pTSA->set[role].addr.SetPort(addrListen.GetPort()); // use the original port
85  }
86  else
87  {
88  pTSA->set[role].addr = addrListen; // use the socket's IP and port (OK if this is INADDR_ANY)
89  }
90 
91 Cleanup:
92  return hr;
93 }
94 
96 {
97  HRESULT hr = S_OK;
98  int socketcount = 0;
100  TransportAddressSet tsa = {};
101  boost::shared_ptr<RateLimiter> spLimiter;
102 
103  // cleanup any thing that's going on now
104  Shutdown();
105 
106  // optional code: create an authentication provider and initialize it here (if you want authentication)
107  // set the _spAuth member to reference it
108  // Chk(CYourAuthProvider::CreateInstanceNoInit(&_spAuth));
109 
110  // Create the sockets and initialize the TSA thing
111  if (config.fHasPP)
112  {
113  Chk(AddSocket(&tsa, RolePP, config.addrPP, config.addrPrimaryAdvertised));
114  socketcount++;
115  }
116 
117  if (config.fHasPA)
118  {
119  Chk(AddSocket(&tsa, RolePA, config.addrPA, config.addrPrimaryAdvertised));
120  socketcount++;
121  }
122 
123  if (config.fHasAP)
124  {
125  Chk(AddSocket(&tsa, RoleAP, config.addrAP, config.addrAlternateAdvertised));
126  socketcount++;
127  }
128 
129  if (config.fHasAA)
130  {
131  Chk(AddSocket(&tsa, RoleAA, config.addrAA, config.addrAlternateAdvertised));
132  socketcount++;
133  }
134 
135  ChkIf(socketcount == 0, E_INVALIDARG);
136 
137  if (config.fEnableDosProtection)
138  {
139  Logging::LogMsg(LL_DEBUG, "Creating rate limiter for ddos protection\n");
140  // hard coding to 25000 ip addresses
141  spLimiter = boost::shared_ptr<RateLimiter>(new RateLimiter(25000, config.fMultiThreadedMode));
142  }
143 
144  if (config.fMultiThreadedMode == false)
145  {
146  Logging::LogMsg(LL_DEBUG, "Configuring single threaded mode\n");
147 
148  // create one thread for all the sockets
149  CStunSocketThread* pThread = new CStunSocketThread();
150  ChkIf(pThread==NULL, E_OUTOFMEMORY);
151 
152  _threads.push_back(pThread);
153 
154  Chk(pThread->Init(_arrSockets, &tsa, _spAuth, (SocketRole)-1, spLimiter));
155  }
156  else
157  {
158  Logging::LogMsg(LL_DEBUG, "Configuring multi-threaded mode\n");
159 
160  // one thread for every socket
161  CStunSocketThread* pThread = NULL;
162  for (size_t index = 0; index < ARRAYSIZE(_arrSockets); index++)
163  {
164  if (_arrSockets[index].IsValid())
165  {
166  SocketRole rolePrimaryRecv = _arrSockets[index].GetRole();
167  ASSERT(rolePrimaryRecv == (SocketRole)index);
168  pThread = new CStunSocketThread();
169  ChkIf(pThread==NULL, E_OUTOFMEMORY);
170  _threads.push_back(pThread);
171  Chk(pThread->Init(_arrSockets, &tsa, _spAuth, rolePrimaryRecv, spLimiter));
172  }
173  }
174  }
175 
176 
177 Cleanup:
178 
179  if (FAILED(hr))
180  {
181  Shutdown();
182  }
183 
184  return hr;
185 
186 }
187 
189 {
190  size_t len;
191 
192  Stop();
193 
194  // release the sockets and the thread
195 
196  for (size_t index = 0; index < ARRAYSIZE(_arrSockets); index++)
197  {
198  _arrSockets[index].Close();
199  }
200 
201  len = _threads.size();
202  for (size_t index = 0; index < len; index++)
203  {
204  CStunSocketThread* pThread = _threads[index];
205  delete pThread;
206  _threads[index] = NULL;
207  }
208  _threads.clear();
209 
211 
212  return S_OK;
213 }
214 
215 
216 
218 {
219  HRESULT hr = S_OK;
220  size_t len = _threads.size();
221 
222  ChkIfA(len == 0, E_UNEXPECTED);
223 
224  for (size_t index = 0; index < len; index++)
225  {
226  CStunSocketThread* pThread = _threads[index];
227  if (pThread != NULL)
228  {
229  // set the "exit flag" that each thread looks at when it wakes up from waiting
230  ChkA(pThread->Start());
231  }
232  }
233 
234 Cleanup:
235  if (FAILED(hr))
236  {
237  Stop();
238  }
239 
240  return hr;
241 }
242 
244 {
245 
246 
247  size_t len = _threads.size();
248 
249  for (size_t index = 0; index < len; index++)
250  {
251  CStunSocketThread* pThread = _threads[index];
252  if (pThread != NULL)
253  {
254  // set the "exit flag" that each thread looks at when it wakes up from waiting
255  pThread->SignalForStop(false);
256  }
257  }
258 
259 
260  for (size_t index = 0; index < len; index++)
261  {
262  CStunSocketThread* pThread = _threads[index];
263 
264  // Post a bunch of empty buffers to get the threads unblocked from whatever socket call they are on
265  // In multi-threaded mode, this may wake up a different thread. But that's ok, since all threads start and stop together
266  if (pThread != NULL)
267  {
268  pThread->SignalForStop(true);
269  }
270  }
271 
272  for (size_t index = 0; index < len; index++)
273  {
274  CStunSocketThread* pThread = _threads[index];
275 
276  if (pThread != NULL)
277  {
278  pThread->WaitForStopAndClose();
279  }
280  }
281 
282 
283  return S_OK;
284 }
285 
286 
287 
288 
#define S_OK
Definition: hresult.h:46
CStunServer()
Definition: server.cpp:42
#define ASSERT(expr)
bool IsValidSocketRole(SocketRole sr)
Definition: socketrole.h:31
const uint32_t LL_DEBUG
Definition: logger.h:24
HRESULT AddSocket(TransportAddressSet *pTSA, SocketRole role, const CSocketAddress &addrListen, const CSocketAddress &addrAdvertise)
Definition: server.cpp:53
void Close()
Definition: stunsocket.cpp:41
bool IsSameIP_and_Port(const CSocketAddress &other) const
#define Chk(expr)
Definition: chkmacros.h:53
void LogMsg(uint32_t level, const char *pszFormat,...)
Definition: logger.cpp:44
~CStunServer()
Definition: server.cpp:48
CSocketAddress addr
HRESULT Init(CStunSocket *arrayOfFourSockets, TransportAddressSet *pTSA, IStunAuth *pAuth, SocketRole rolePrimaryRecv, boost::shared_ptr< RateLimiter > &_spRateLimiter)
CSocketAddress addrAlternateAdvertised
Definition: server.h:49
uint16_t GetPort() const
#define ARRAYSIZE(arr)
#define ChkIf(expr, hrerror)
Definition: chkmacros.h:63
std::vector< CStunSocketThread * > _threads
Definition: server.h:65
#define E_UNEXPECTED
Definition: hresult.h:48
CStunSocket _arrSockets[4]
Definition: server.h:63
CRefCountedPtr< IStunAuth > _spAuth
Definition: server.h:72
HRESULT Shutdown()
Definition: server.cpp:188
void SetPort(uint16_t)
CSocketAddress addrPA
Definition: server.h:44
HRESULT Stop()
Definition: server.cpp:243
SocketRole GetRole() const
Definition: stunsocket.cpp:98
CSocketAddress addrAA
Definition: server.h:46
SocketRole
Definition: socketrole.h:22
void ReleaseAndClear()
int32_t HRESULT
Definition: hresult.h:22
bool fMultiThreadedMode
Definition: server.h:38
#define E_INVALIDARG
Definition: hresult.h:51
#define FAILED(hr)
Definition: hresult.h:29
#define ChkA(expr)
Definition: chkmacros.h:73
#define E_OUTOFMEMORY
Definition: hresult.h:50
CSocketAddress addrPrimaryAdvertised
Definition: server.h:48
HRESULT Start()
Definition: server.cpp:217
bool fEnableDosProtection
Definition: server.h:51
TransportAddress set[4]
CSocketAddress addrAP
Definition: server.h:45
bool IsIPAddressZero() const
HRESULT SignalForStop(bool fPostMessages)
#define ChkIfA(expr, hrerror)
Definition: chkmacros.h:84
const CSocketAddress & GetLocalAddress() const
Definition: stunsocket.cpp:87
HRESULT Initialize(const CStunServerConfig &config)
Definition: server.cpp:95
CSocketAddress addrPP
Definition: server.h:43