Stun Server  Compliant with the latest RFCs including 5389, 5769, and 5780
discover the local host's own external IP address
stunclienttests.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 
19 #include "commonincludes.hpp"
20 #include "stuncore.h"
21 
22 #include "stunclientlogic.h"
23 #include "stunclienttests.h"
24 
25 
26 
28 _fInit(false),
29 _pConfig(NULL),
30 _pResults(NULL),
31 _fCompleted(false),
32 _transid() // zero-init
33 {
34  ;
35 }
36 
38 {
39  HRESULT hr = S_OK;
40 
41  ChkIfA(pConfig == NULL, E_INVALIDARG);
42  ChkIfA(pResults == NULL, E_INVALIDARG);
43 
44  _fInit = true;
45  _pConfig = pConfig;
46  _pResults = pResults;
47  _fCompleted = false;
48  memset(&_transid, 0, sizeof(_transid));
49 
50 Cleanup:
51  return hr;
52 }
53 
55 {
56  builder.AddBindingRequestHeader();
57 
59  {
60  builder.AddTransactionId(_transid);
61  }
62  else
63  {
65  }
66  return S_OK;
67 }
68 
70 {
71  HRESULT hr = S_OK;
73  StunTransactionId transid;
74  int cmp = 0;
75 
76  readerstate = reader.AddBytes(spMsg->GetData(), spMsg->GetSize());
77 
78  hr = (readerstate == CStunMessageReader::BodyValidated) ? S_OK : E_FAIL;
79  if (FAILED(hr))
80  {
81  Logging::LogMsg(LL_DEBUG, "BasicReaderValidation - body parsing failed");
82  }
83  else
84  {
85  reader.GetTransactionId(&transid);
86  cmp = memcmp(transid.id, _transid.id, sizeof(_transid));
87  hr = (cmp == 0) ? S_OK : E_FAIL;
88  if (FAILED(hr))
89  {
90  Logging::LogMsg(LL_DEBUG, "BasicReaderValidation - transaction id comparison failed");
91  }
92  }
93 
94  return hr;
95 }
96 
97 
98 
100 {
101  return _fCompleted;
102 }
103 
105 {
106  return;
107 }
108 
109 
110 
111 // ----------------------------------------------------------------------------------
112 
113 
114 
115 
117 {
118  // the binding test can always be run if it hasn't already been run
119  return (_fCompleted == false);
120 }
121 
123 {
124  StunChangeRequestAttribute attribChangeRequest = {};
125 
126  HRESULT hr = S_OK;
127  ASSERT(spMsg->GetAllocatedSize() > 0);
128  ASSERT(pAddrDest);
129  ASSERT(_fInit);
130 
131  CStunMessageBuilder builder;
132  builder.GetStream().Attach(spMsg, true);
133 
134  Chk(StartBindingRequest(builder));
135 
136  builder.AddChangeRequest(attribChangeRequest); // adding a blank CHANGE-REQUEST, because a JSTUN server will not respond if the request doesn't have one
137 
138  builder.FixLengthField();
139 
140  *pAddrDest = _pConfig->addrServer;
141 
142 
143 Cleanup:
144  return hr;
145 }
146 
148 {
149  HRESULT hr = S_OK;
150  CStunMessageReader reader;
151  CSocketAddress addrMapped;
152  CSocketAddress addrOther;
153  bool fHasOtherAddress = false;
154 
155  Chk(BasicReaderValidation(spMsg, reader));
156 
157  hr = reader.GetXorMappedAddress(&addrMapped);
158  if (FAILED(hr))
159  {
160  hr = reader.GetMappedAddress(&addrMapped);
161  }
162  Chk(hr); // again drop the message if we can't parse the binding response
163 
164  fHasOtherAddress = SUCCEEDED(reader.GetOtherAddress(&addrOther));
165 
166  // ok, we got a response. So we are done
167  _fCompleted = true;
169  _pResults->fIsDirect = addrLocal.IsSameIP_and_Port(addrMapped);
170  _pResults->addrLocal = addrLocal;
171  _pResults->addrMapped = addrMapped;
172  _pResults->fHasOtherAddress = fHasOtherAddress;
173 
174 
175  if (fHasOtherAddress)
176  {
177  _pResults->addrAA = addrOther;
178 
180  _pResults->addrPA.SetPort(addrOther.GetPort());
181 
182  _pResults->addrAP = addrOther;
184 
186  {
187  char sz[100];
188  addrOther.ToStringBuffer(sz, 100);
189  Logging::LogMsg(LL_DEBUG, "Other address is %s\n",sz);
190  }
191 
192  }
193 
194 Cleanup:
195  return hr;
196 }
197 
199 {
200  // we timed out - mark the request as failed and get out of here
201  _fCompleted = true;
202  _pResults->fBindingTestSuccess = false; // should already be false
203 }
204 
205 // ----------------------------------------------------------------------------------
206 
207 
208 
210 {
211  this->_fIsTest3 = false;
212 }
213 
215 {
216 
217  if (_fIsTest3 == false)
218  {
219  // we don't need to run BehaviorTest2 or BehaviorTest3 if we know we are direct
221  {
222  _fCompleted = true;
225  }
226  }
227 }
228 
230 {
231  // we can run if the CBasicBindingTest succeeded and we have an "other" address
233 
234  if (_fIsTest3)
235  {
236  // check to see that test2 succeeded before allowing test3 to run
237  fRet = (fRet && (_pResults->addrMappingAP.IsIPAddressZero() == false));
238  }
239 
240  return fRet;
241 
242 }
243 
245 {
246  HRESULT hr = S_OK;
247  ASSERT(spMsg->GetAllocatedSize() > 0);
248  ASSERT(pAddrDest);
249 
250  StunChangeRequestAttribute attribChangeRequest = {};
251 
252  CStunMessageBuilder builder;
253  builder.GetStream().Attach(spMsg, true);
254  StartBindingRequest(builder);
255 
256  builder.AddChangeRequest(attribChangeRequest); // adding a blank CHANGE-REQUEST, because a JSTUN server will not respond if the request doesn't have one
257 
258  builder.FixLengthField();
259 
260  if (_fIsTest3 == false)
261  {
262  Logging::LogMsg(LL_DEBUG, "Preparing message for behavior test #2 (destination=AP)");
263  *pAddrDest = _pResults->addrAP;
264  }
265  else
266  {
267  Logging::LogMsg(LL_DEBUG, "Preparing message for behavior test #2 (destination=AA)");
268  *pAddrDest = _pResults->addrAA;
269  }
270 
271  return hr;
272 }
273 
275 {
276  HRESULT hr = S_OK;
277  CStunMessageReader reader;
278  CSocketAddress addrMapped;
279 
280 
281  Chk(BasicReaderValidation(spMsg, reader));
282 
283  hr = reader.GetXorMappedAddress(&addrMapped);
284  if (FAILED(hr))
285  {
286  hr = reader.GetMappedAddress(&addrMapped);
287  }
288  Chk(hr); // again drop the message if we can't parse the binding response
289 
290  _fCompleted = true;
291 
292 
293  if (_fIsTest3)
294  {
295  _pResults->addrMappingAA = addrMapped;
297 
298  if (addrMapped.IsSameIP_and_Port(_pResults->addrMappingAP))
299  {
301  }
302  else
303  {
305  }
306  }
307  else
308  {
309  _pResults->addrMappingAP = addrMapped;
310  if (addrMapped.IsSameIP_and_Port(_pResults->addrMapped))
311  {
314  }
315  }
316 
317 Cleanup:
318  return hr;
319 }
320 
322 {
323  // the behavior test fails if it never got a response
324  _fCompleted = true;
326 }
327 
328 void CBehaviorTest::RunAsTest3(bool fSetAsTest3)
329 {
330  _fIsTest3 = fSetAsTest3;
331 }
332 
333 
334 
335 // ----------------------------------------------------------------------------------
336 
337 
339 {
340  _fIsTest3 = false;
341 }
342 
343 
345 {
346  // if the binding test detected "direct", there's nothing for us to do except declare this as "endpoint indedepent"
347  if (_fIsTest3 == false)
348  {
350  {
351  _fCompleted = true;
354  }
355  }
356 }
357 
358 
360 {
361  // we can run if the CBasicBindingTest succeeded and we have an "other" address
363 
364  return fRet;
365 }
366 
368 {
369  CStunMessageBuilder builder;
371 
372 
373  builder.GetStream().Attach(spMsg, true);
374 
375  StartBindingRequest(builder);
376 
377  *pAddrDest = _pConfig->addrServer;
378 
379  if (_fIsTest3 == false)
380  {
381  Logging::LogMsg(LL_DEBUG, "Preparing message for filtering test #2 (ChangeRequest=AA)");
382  change.fChangeIP = true;
383  change.fChangePort = true;
384  builder.AddChangeRequest(change);
385  }
386  else
387  {
388  Logging::LogMsg(LL_DEBUG, "Preparing message for filtering test #3 (ChangeRequest=PA)");
389  change.fChangeIP = false;
390  change.fChangePort = true;
391  builder.AddChangeRequest(change);
392  }
393 
394  builder.FixLengthField();
395 
396 
397  return S_OK;
398 }
399 
400 
402 {
403  HRESULT hr = S_OK;
404  CStunMessageReader reader;
405 
406 
407  Chk(BasicReaderValidation(spMsg, reader));
408 
409  // BasicReaderValidation will check the transaction ID!
410  // we don't really care what's in the response other than if the transactionID is correct
411 
412  _fCompleted = true;
413 
414  if (_fIsTest3)
415  {
419  }
420  else
421  {
422  // if we got a response back from the other IP and Port, then we have independent filtering
426  }
427 
428 
429 Cleanup:
430  return hr;
431 }
432 
434 {
435  // in the filtering test, it's expected to not get a response for test2 or test3
436  _fCompleted = true;
437 
438  // if we didn't get a response in test3, that implies we never got a response in test2 (because we don't run test3 if test2 got a response)
439  if (_fIsTest3)
440  {
443  }
444 
445 }
446 
447 void CFilteringTest::RunAsTest3(bool fSetAsTest3)
448 {
449  _fIsTest3 = fSetAsTest3;
450 }
451 
#define S_OK
Definition: hresult.h:46
#define ASSERT(expr)
CSocketAddress addrAP
const uint32_t LL_DEBUG
Definition: logger.h:24
HRESULT AddRandomTransactionId(StunTransactionId *pTransId)
Definition: stunbuilder.cpp:93
HRESULT ProcessResponse(CRefCountedBuffer &spMsg, CSocketAddress &addrRemote, CSocketAddress &addrLocal)
bool IsSameIP_and_Port(const CSocketAddress &other) const
#define Chk(expr)
Definition: chkmacros.h:53
HRESULT FixLengthField()
void LogMsg(uint32_t level, const char *pszFormat,...)
Definition: logger.cpp:44
HRESULT AddBindingRequestHeader()
Definition: stunbuilder.cpp:76
void GetTransactionId(StunTransactionId *pTransId)
Definition: stunreader.cpp:825
HRESULT StartBindingRequest(CStunMessageBuilder &builder)
virtual bool IsCompleted()
HRESULT GetMappedAddress(CSocketAddress *pAddress)
Definition: stunreader.cpp:505
uint16_t GetPort() const
HRESULT ProcessResponse(CRefCountedBuffer &spMsg, CSocketAddress &addrRemote, CSocketAddress &addrLocal)
NatBehavior behavior
ReaderParseState AddBytes(const uint8_t *pData, uint32_t size)
Definition: stunreader.cpp:750
HRESULT GetMessage(CRefCountedBuffer &spMsg, CSocketAddress *pAddrDest)
HRESULT GetOtherAddress(CSocketAddress *pAddress)
Definition: stunreader.cpp:513
#define SUCCEEDED(hr)
Definition: hresult.h:28
void SetPort(uint16_t)
HRESULT AddTransactionId(const StunTransactionId &transid)
Definition: stunbuilder.cpp:86
virtual void PreRunCheck()
HRESULT GetMessage(CRefCountedBuffer &spMsg, CSocketAddress *pAddrDest)
CSocketAddress addrMapped
int32_t HRESULT
Definition: hresult.h:22
StunClientLogicConfig * _pConfig
HRESULT ToStringBuffer(char *pszAddrBytes, size_t length) const
CSocketAddress addrLocal
virtual HRESULT Init(StunClientLogicConfig *pConfig, StunClientResults *pResults)
bool IsTransactionIdValid(StunTransactionId &transid)
Definition: stunutils.cpp:26
HRESULT AddChangeRequest(const StunChangeRequestAttribute &changeAttrib)
#define E_INVALIDARG
Definition: hresult.h:51
uint8_t id[STUN_TRANSACTION_ID_LENGTH]
Definition: stuntypes.h:154
#define E_FAIL
Definition: hresult.h:56
#define FAILED(hr)
Definition: hresult.h:29
CSocketAddress addrMappingAA
void RunAsTest3(bool fSetAsTest3)
uint32_t GetLogLevel()
Definition: logger.cpp:33
void RunAsTest3(bool fSetAsTest3)
HRESULT GetMessage(CRefCountedBuffer &spMsg, CSocketAddress *pAddrDest)
NatFiltering filtering
CSocketAddress addrPA
boost::shared_ptr< CBuffer > CRefCountedBuffer
Definition: buffer.h:65
HRESULT BasicReaderValidation(CRefCountedBuffer &spMsg, CStunMessageReader &reader)
HRESULT ProcessResponse(CRefCountedBuffer &spMsg, CSocketAddress &addrRemote, CSocketAddress &addrLocal)
void Attach(CRefCountedBuffer &buffer, bool fForWriting)
Definition: datastream.cpp:55
bool IsIPAddressZero() const
CSocketAddress addrMappingAP
#define ChkIfA(expr, hrerror)
Definition: chkmacros.h:84
CDataStream & GetStream()
StunTransactionId _transid
HRESULT GetXorMappedAddress(CSocketAddress *pAddress)
Definition: stunreader.cpp:529
StunClientResults * _pResults
CSocketAddress addrServer
CSocketAddress addrAA