Stun Server  Compliant with the latest RFCs including 5389, 5769, and 5780
discover the local host's own external IP address
testclientlogic.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 #include "unittest.h"
22 
23 #include "testmessagehandler.h"
24 
25 #include "testclientlogic.h"
26 
27 
29 {
30  HRESULT hr = S_OK;
31 
32  ChkA(Test1());
33 
34  printf("Testing detection for DirectMapping\n");
36 
37  printf("Testing detection for EndpointIndependent mapping\n");
39 
40  printf("Testing detection for AddressDependentMapping\n");
42 
43  printf("Testing detection for AddressAndPortDependentMapping\n");
45 
46  printf("Testing detection for EndpointIndependentFiltering\n");
48 
49  printf("Testing detection for AddressDependentFiltering\n");
51 
52  printf("Testing detection for AddressAndPortDependentFiltering\n");
54 
55 
56 
57 Cleanup:
58  return hr;
59 }
60 
62 {
63  HRESULT hr = S_OK;
64  CStunMessageReader reader;
66 
67  state = reader.AddBytes(spMsg->GetData(), spMsg->GetSize());
69 
71 
72  reader.GetTransactionId(pTransId);
73 
74  ChkIfA(false == IsTransactionIdValid(*pTransId), E_FAIL);
75 
76 Cleanup:
77  return hr;
78 }
79 
81 {
82  HRESULT hr = S_OK;
83 
84  CStunMessageBuilder builder;
85  builder.GetStream().Attach(spMsg, true);
86 
87  ChkA(builder.AddBindingResponseHeader(true));
88  ChkA(builder.AddTransactionId(transid));
89  ChkA(builder.AddXorMappedAddress(addrMapped));
90  ChkA(builder.FixLengthField());
91 Cleanup:
92  return hr;
93 }
94 
96 {
97  HRESULT hr = S_OK;
98  if (addrDest.IsSameIP_and_Port(_addrServerPP))
99  {
100  *pAddrMapped = _addrMappedPP;
101  }
102  else if (addrDest.IsSameIP_and_Port(_addrServerPA))
103  {
104  *pAddrMapped = _addrMappedPA;
105  }
106  else if (addrDest.IsSameIP_and_Port(_addrServerAP))
107  {
108  *pAddrMapped = _addrMappedAP;
109  }
110  else if (addrDest.IsSameIP_and_Port(_addrServerAA))
111  {
112  *pAddrMapped = _addrMappedAA;
113  }
114  else
115  {
116  ASSERT(false);
117  hr = E_FAIL;
118  }
119 
120  return hr;
121 }
122 
124 {
125  if (addrDest.IsSameIP_and_Port(_addrServerPP))
126  {
127  return RolePP;
128  }
129  else if (addrDest.IsSameIP_and_Port(_addrServerPA))
130  {
131  return RolePA;
132  }
133  else if (addrDest.IsSameIP_and_Port(_addrServerAP))
134  {
135  return RoleAP;
136  }
137  else if (addrDest.IsSameIP_and_Port(_addrServerAA))
138  {
139  return RoleAA;
140  }
141  ASSERT(false);
142  return RolePP;
143 
144 }
145 
146 
148 {
149  HRESULT hr = S_OK;
150 
151  CSocketAddress addrMapped(0x22222222, 6000);
152 
153  _addrServerPP = CSocketAddress(0xaaaaaaaa, 1001);
154  _addrServerPA = CSocketAddress(0xaaaaaaaa, 1002);
155  _addrServerAP = CSocketAddress(0xbbbbbbbb, 1001);
156  _addrServerAA = CSocketAddress(0xbbbbbbbb, 1002);
157 
158  _tsa.set[RolePP].fValid = true;
160  _tsa.set[RolePA].fValid = true;
162  _tsa.set[RoleAP].fValid = true;
164  _tsa.set[RoleAA].fValid = true;
166 
167 
168  _addrLocal = CSocketAddress(0x33333333, 7000);
169 
170  _addrMappedPP = addrMapped;
171  _addrMappedPA = addrMapped;
172  _addrMappedAP = addrMapped;
173  _addrMappedAA = addrMapped;
174 
175  _spClientLogic = boost::shared_ptr<CStunClientLogic>(new CStunClientLogic());
176 
177 
178  switch (behavior)
179  {
180  case DirectMapping:
181  {
186  break;
187  }
188 
190  {
191  break;
192  }
193 
195  {
196  _addrMappedAP.SetPort(6002);
197  _addrMappedAA.SetPort(6002);
198  break;
199  }
200 
202  {
203  _addrMappedPA.SetPort(6001);
204  _addrMappedAP.SetPort(6002);
205  _addrMappedAA.SetPort(6003);
206  break;
207  }
208 
209  default:
210  {
211  ChkA(E_FAIL);
212  }
213  }
214 
215  switch (filtering)
216  {
219  {
220  _fAllowChangeRequestPA = true;
221  _fAllowChangeRequestAA = true;
222  break;
223  }
224 
226  {
227  _fAllowChangeRequestPA = true;
228  _fAllowChangeRequestAA = false;
229  break;
230  }
231 
233  {
234  _fAllowChangeRequestPA = false;
235  _fAllowChangeRequestAA = false;
236  break;
237  }
238  default:
239  {
240  ChkA(E_FAIL);
241  }
242  }
243 
244 Cleanup:
245  return hr;
246 }
247 
248 
249 HRESULT CTestClientLogic::TestBehaviorAndFiltering(bool fBehaviorTest, NatBehavior behavior, bool fFilteringTest, NatFiltering filtering)
250 {
251  HRESULT hr = S_OK;
252  StunClientLogicConfig config;
253  HRESULT hrRet;
254  uint32_t time = 0;
256  CRefCountedBuffer spMsgResponse(new CBuffer(MAX_STUN_MESSAGE_SIZE));
257  SocketRole outputRole;
258  CSocketAddress addrDummy;
259 
260  StunMessageIn stunmsgIn;
261  StunMessageOut stunmsgOut;
262 
263 
264  CSocketAddress addrDest;
265  CSocketAddress addrMapped;
266  CSocketAddress addrServerResponse; // what address the fake server responded back on
267  StunClientResults results;
268  StunTransactionId transid={};
269  //std::string strAddr;
270 
271 
272  ChkA(CommonInit(behavior, filtering));
273 
274 
275  config.addrServer = _addrServerPP;
276  config.fBehaviorTest = fBehaviorTest;
277  config.fFilteringTest = fFilteringTest;
278  config.timeoutSeconds = 5;
279  config.uMaxAttempts = 10;
280 
281  ChkA(_spClientLogic->Initialize(config));
282 
283  while (true)
284  {
285  CStunMessageReader reader;
286 
287  bool fDropMessage = false;
288 
289  time += 1000;
290 
291  hrRet = _spClientLogic->GetNextMessage(spMsgOut, &addrDest, time);
292  if (hrRet == E_STUNCLIENT_STILL_WAITING)
293  {
294  //printf("GetNextMessage returned 'still waiting'\n");
295  continue;
296  }
297 
298  if (hrRet == E_STUNCLIENT_RESULTS_READY)
299  {
300  //printf("GetNextMessage returned 'results ready'\n");
301  break;
302  }
303 
304  //addrDest.ToString(&strAddr);
305  //printf("Client is sending stun packet to %s\n", strAddr.c_str());
306 
307  ChkA(GetMappedAddressForDestinationAddress(addrDest, &addrMapped));
308 
309  //addrMapped.ToString(&strAddr);
310  //printf("Server is receiving stun packet from %s\n", strAddr.c_str());
311 
312  ChkA(ValidateBindingRequest(spMsgOut, &transid));
313 
314  // --------------------------------------------------
315 
316  reader.AddBytes(spMsgOut->GetData(), spMsgOut->GetSize());
317 
319 
320  // Simulate sending the binding request and getting a response back
321  stunmsgIn.socketrole = GetSocketRoleForDestinationAddress(addrDest);
322  stunmsgIn.addrLocal = addrDest;
323  stunmsgIn.addrRemote = addrMapped;
324  stunmsgIn.fConnectionOriented = false;
325  stunmsgIn.pReader = &reader;
326 
327  stunmsgOut.socketrole = (SocketRole)-1; // intentionally setting it wrong
328  stunmsgOut.addrDest = addrDummy; // we don't care what address the server sent back to
329  stunmsgOut.spBufferOut = spMsgResponse;
330  spMsgResponse->SetSize(0);
331 
332  ChkA(::CStunRequestHandler::ProcessRequest(stunmsgIn, stunmsgOut, &_tsa, NULL));
333 
334  // simulate the message coming back
335 
336  // make sure we got something!
337  outputRole = stunmsgOut.socketrole;
338  ChkIfA(::IsValidSocketRole(outputRole)==false, E_FAIL);
339 
340  ChkIfA(spMsgResponse->GetSize() == 0, E_FAIL);
341 
342  addrServerResponse = _tsa.set[stunmsgOut.socketrole].addr;
343 
344  // --------------------------------------------------
345 
346 
347  //addrServerResponse.ToString(&strAddr);
348  //printf("Server is sending back from %s\n", strAddr.c_str());
349 
350  // if the request went to PP, but came back from AA or AP, then it's likely a filtering test
351  // decide if we need to drop the response
352 
353  fDropMessage = ( addrDest.IsSameIP_and_Port(_addrServerPP) &&
354  ( ((outputRole == RoleAA) && (_fAllowChangeRequestAA==false)) ||
355  ((outputRole == RolePA) && (_fAllowChangeRequestPA==false))
356  )
357  );
358 
359 
360  //{
361  // CStunMessageReader::ReaderParseState state;
362  // CStunMessageReader readerDebug;
363  // state = readerDebug.AddBytes(spMsgResponse->GetData(), spMsgResponse->GetSize());
364  // if (state != CStunMessageReader::BodyValidated)
365  // {
366  // printf("Error - response from server doesn't look valid");
367  // }
368  // else
369  // {
370  // CSocketAddress addr;
371  // readerDebug.GetMappedAddress(&addr);
372  // addr.ToString(&strAddr);
373  // printf("Response from server indicates our mapped address is %s\n", strAddr.c_str());
374  // }
375  //}
376 
377  if (fDropMessage == false)
378  {
379  ChkA(_spClientLogic->ProcessResponse(spMsgResponse, addrServerResponse, _addrLocal));
380  }
381  }
382 
383  // now validate the results
384  results.Init(); // zero it out
385  _spClientLogic->GetResults(&results);
386 
387  ChkIfA(results.behavior != behavior, E_UNEXPECTED);
388 
389 Cleanup:
390 
391  return hr;
392 
393 }
394 
395 
397 {
398  HRESULT hr = S_OK;
399  HRESULT hrTmp = 0;
400  CStunClientLogic clientlogic;
404  StunClientResults results;
405  StunTransactionId transid;
406 
407  CSocketAddress addrDest;
408 
409  CSocketAddress addrServerPP = CSocketAddress(0xaaaaaaaa, 1001);
410  CSocketAddress addrLocal = CSocketAddress(0xdddddddd, 4444);
411  CSocketAddress addrMapped = CSocketAddress(0xeeeeeeee, 5555);
412 
413  config.addrServer = addrServerPP;
414  config.fBehaviorTest = false;
415  config.fFilteringTest = false;
416  config.timeoutSeconds = 10;
417  config.uMaxAttempts = 2;
418  config.fTimeoutIsInstant = false;
419 
420  ChkA(clientlogic.Initialize(config));
421 
422  ChkA(clientlogic.GetNextMessage(spMsgOut, &addrDest, 0));
423 
424  // we expect to get back a message for the serverPP
425  ChkIfA(addrDest.IsSameIP_and_Port(addrServerPP)==false, E_UNEXPECTED);
426 
427  // check to make sure out timeout logic appears to work
428  hrTmp = clientlogic.GetNextMessage(spMsgOut, &addrDest, 1);
430 
431  // now we should get a dupe of what we had before
432  ChkA(clientlogic.GetNextMessage(spMsgOut, &addrDest, 11000));
433 
434  // the message should be a binding request
435  ChkA(ValidateBindingRequest(spMsgOut, &transid));
436 
437  // now let's generate a response
438  ChkA(GenerateBindingResponseMessage(addrMapped, transid, spMsgIn));
439  ChkA(clientlogic.ProcessResponse(spMsgIn, addrServerPP, addrLocal));
440 
441  // results should be ready
442  hrTmp = clientlogic.GetNextMessage(spMsgOut, &addrDest, 12000);
444 
445  ChkA(clientlogic.GetResults(&results));
446 
447  // results should have a successful binding result
448  ChkIfA(results.fBindingTestSuccess==false, E_UNEXPECTED);
449  ChkIfA(results.fIsDirect, E_UNEXPECTED);
450  ChkIfA(results.addrMapped.IsSameIP_and_Port(addrMapped)==false, E_UNEXPECTED);
451  ChkIfA(results.addrLocal.IsSameIP_and_Port(addrLocal)==false, E_UNEXPECTED);
452 
453 Cleanup:
454  return hr;
455 }
456 
const uint32_t MAX_STUN_MESSAGE_SIZE
Definition: stuntypes.h:178
CSocketAddress _addrMappedAP
#define S_OK
Definition: hresult.h:46
#define ASSERT(expr)
bool IsValidSocketRole(SocketRole sr)
Definition: socketrole.h:31
CSocketAddress _addrMappedPA
CSocketAddress _addrServerAA
uint16_t GetMessageType()
Definition: stunreader.cpp:839
HRESULT Initialize(StunClientLogicConfig &config)
#define E_STUNCLIENT_STILL_WAITING
bool IsSameIP_and_Port(const CSocketAddress &other) const
#define E_STUNCLIENT_RESULTS_READY
SocketRole socketrole
HRESULT TestBehaviorAndFiltering(bool fBehaviorTest, NatBehavior behavior, bool fFilteringTest, NatFiltering filtering)
HRESULT FixLengthField()
HRESULT AddXorMappedAddress(const CSocketAddress &addr)
TransportAddressSet _tsa
SocketRole socketrole
void GetTransactionId(StunTransactionId *pTransId)
Definition: stunreader.cpp:825
HRESULT GetNextMessage(CRefCountedBuffer &spMsg, CSocketAddress *pAddrDest, uint32_t timeCurrentMilliseconds)
CSocketAddress _addrServerAP
CSocketAddress addr
NatBehavior behavior
ReaderParseState AddBytes(const uint8_t *pData, uint32_t size)
Definition: stunreader.cpp:750
#define E_UNEXPECTED
Definition: hresult.h:48
Definition: buffer.h:27
CSocketAddress addrRemote
What local IP address the message was received on (useful if the socket binded to INADDR_ANY) ...
CSocketAddress addrLocal
which socket id did the message arrive on
void SetPort(uint16_t)
HRESULT AddTransactionId(const StunTransactionId &transid)
Definition: stunbuilder.cpp:86
SocketRole GetSocketRoleForDestinationAddress(const CSocketAddress &addrDest)
CSocketAddress addrMapped
HRESULT AddBindingResponseHeader(bool fSuccess)
Definition: stunbuilder.cpp:81
ReaderParseState GetState()
Definition: stunreader.cpp:820
SocketRole
Definition: socketrole.h:22
int32_t HRESULT
Definition: hresult.h:22
boost::shared_ptr< CStunClientLogic > _spClientLogic
CSocketAddress _addrServerPP
CSocketAddress addrDest
CSocketAddress addrLocal
bool IsTransactionIdValid(StunTransactionId &transid)
Definition: stunutils.cpp:26
CStunMessageReader * pReader
the address of the node that sent us the message
CSocketAddress _addrMappedPP
#define E_FAIL
Definition: hresult.h:56
CSocketAddress _addrLocal
HRESULT ProcessResponse(CRefCountedBuffer &spMsg, CSocketAddress &addrRemote, CSocketAddress &addrLocal)
CSocketAddress _addrServerPA
HRESULT GetResults(StunClientResults *pResults)
static HRESULT ProcessRequest(const StunMessageIn &msgIn, StunMessageOut &msgOut, TransportAddressSet *pAddressSet, IStunAuth *pAuth)
#define ChkA(expr)
Definition: chkmacros.h:73
HRESULT GenerateBindingResponseMessage(const CSocketAddress &addrMapped, const StunTransactionId &transid, CRefCountedBuffer &spMsg)
HRESULT GetMappedAddressForDestinationAddress(const CSocketAddress &addrDest, CSocketAddress *pAddrMapped)
CSocketAddress _addrMappedAA
HRESULT CommonInit(NatBehavior behavior, NatFiltering filtering)
NatBehavior
TransportAddress set[4]
CRefCountedBuffer spBufferOut
boost::shared_ptr< CBuffer > CRefCountedBuffer
Definition: buffer.h:65
NatFiltering
void Attach(CRefCountedBuffer &buffer, bool fForWriting)
Definition: datastream.cpp:55
#define ChkIfA(expr, hrerror)
Definition: chkmacros.h:84
CDataStream & GetStream()
CSocketAddress addrServer
bool fConnectionOriented
reader containing a valid stun message
HRESULT ValidateBindingRequest(CRefCountedBuffer &spMsg, StunTransactionId *pTransId)