Stun Server  Compliant with the latest RFCs including 5389, 5769, and 5780
discover the local host's own external IP address
stunclientlogic.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 "stunclientlogic.h"
22 
23 
24 #include "stunclienttests.h"
25 #include "stunclientlogic.h"
26 
27 
29 {
30  Init();
31 }
32 
34 {
35  CSocketAddress addrZero;
36 
37  fBindingTestSuccess = false;
38  fIsDirect = false;
39  fHasOtherAddress = false; // set to true if the basic binding request got an "other adddress" back from the server
40  fBehaviorTestSuccess = false;
42  fFilteringTestSuccess = false;
44  fGotTest2Response = false;
45  fGotTest3Response = false;
46 
47  addrLocal = addrZero;
48  addrMapped = addrZero;
49  addrPA = addrZero;
50  addrAP = addrZero;
51  addrAA = addrZero;
52  addrMappingAP = addrZero;
53  addrMappingAA = addrZero;
54 
55  errorBitmask = 0;
56 }
57 
59 _fInitialized(false),
60 _timeLastMessageSent(0),
61 _sendCount(0),
62 _nTestIndex(0)
63 {
64 
65 }
66 
67 
69 {
70  HRESULT hr = S_OK;
71 
72  // Don't ever try to "re-use" a CStunClientLogic instance after it's already been used. Create a new instance after
73  // putting a test cycle into motion.
74  // Too much code in the tests expects everything in a clean state
76 
77 
79  ChkIfA(config.addrServer.GetPort() == 0, E_INVALIDARG);
80 
81 
82  _config = config;
83 
84 
85  _fInitialized = true;
86 
87  if (_config.fTimeoutIsInstant)
88  {
89  _config.timeoutSeconds = 0;
90  }
91  else if (_config.timeoutSeconds == 0)
92  {
93  _config.timeoutSeconds = 3; // default to waiting for 3 seconds
94  }
95 
96 
97  if (_config.uMaxAttempts <= 0)
98  {
99  _config.uMaxAttempts = 2;
100  }
101 
102  _nTestIndex = 0;
103 
104  _testlist.clear();
105 
106  // always add the binding test to start
107  _test1.Init(&_config, &_results);
108  _testlist.push_back(&_test1);
109 
110 
111  if (_config.fFilteringTest)
112  {
113  _testFiltering2.Init(&_config, &_results);
114  _testlist.push_back(&_testFiltering2);
115 
116  _testFiltering3.Init(&_config, &_results);
118  _testlist.push_back(&_testFiltering3);
119  }
120 
121  if (_config.fBehaviorTest)
122  {
123  _testBehavior2.Init(&_config, &_results);
124  _testlist.push_back(&_testBehavior2);
125 
126  _testBehavior3.Init(&_config, &_results);
128  _testlist.push_back(&_testBehavior3);
129  }
130 
131  _fPreCheckRunOnTest = false;
132 
134 
135 Cleanup:
136  return hr;
137 }
138 
139 HRESULT CStunClientLogic::GetNextMessage(CRefCountedBuffer& spMsg, CSocketAddress* pAddrDest, uint32_t timeCurrentMilliseconds)
140 {
141  HRESULT hr = S_OK;
142  uint32_t diff = 0;
143  IStunClientTest* pCurrentTest = NULL;
144  bool fReadyToReturn = false;
145 
146  ChkIfA(_fInitialized == false, E_FAIL);
147  ChkIfA(spMsg->GetAllocatedSize() == 0, E_INVALIDARG);
148  ChkIfA(pAddrDest == NULL, E_INVALIDARG);
149 
150  // clients should pass in the expected size
151  ChkIfA(spMsg->GetAllocatedSize() < MAX_STUN_MESSAGE_SIZE, E_INVALIDARG);
152 
153 
154  while (fReadyToReturn==false)
155  {
156  if (_nTestIndex >= _testlist.size())
157  {
158  hr = E_STUNCLIENT_RESULTS_READY; // no more tests to run
159  break;
160  }
161 
162  pCurrentTest = _testlist[_nTestIndex];
163 
164  if (_fPreCheckRunOnTest==false)
165  {
166  // give the test an early chance to complete before sending a message (based on results of previous test)
167  pCurrentTest->PreRunCheck();
168  _fPreCheckRunOnTest = true;
169  }
170 
171 
172  // has this test completed or is it in a state in which it can't run?
173  if (pCurrentTest->IsCompleted() || pCurrentTest->IsReadyToRun()==false)
174  {
175  // increment to the next test
176  _nTestIndex++;
177  _sendCount = 0;
178  _fPreCheckRunOnTest = false;
179 
180  continue;
181  }
182 
183  // Have we waited long enough for a response
184  diff = (timeCurrentMilliseconds - _timeLastMessageSent) / 1000; // convert from milliseconds to seconds
185  if ((diff < _config.timeoutSeconds) && (_sendCount != 0))
186  {
188  break;
189  }
190 
191  // have we exceeded the retry count
193  {
194  // notify the test that it has timed out
195  // this should put it in the completed state (and we increment to next test on next loop)
196  pCurrentTest->NotifyTimeout();
197  ASSERT(pCurrentTest->IsCompleted());
198  continue;
199  }
200 
201  // ok - we are ready to go fetch a message
202  hr = pCurrentTest->GetMessage(spMsg, pAddrDest);
203  ASSERT(SUCCEEDED(hr));
204  if (FAILED(hr))
205  {
206  break;
207  }
208 
209  // success
210  _sendCount++;
211  _timeLastMessageSent = timeCurrentMilliseconds;
212  fReadyToReturn = true;
213  hr = S_OK;
214  }
215 Cleanup:
216  return hr;
217 }
218 
220 {
221  HRESULT hr = S_OK;
222  IStunClientTest* pCurrentTest = NULL;
223 
224  ChkIfA(_fInitialized == false, E_FAIL);
225  ChkIfA(spMsg->GetSize() == 0, E_INVALIDARG);
227 
228  pCurrentTest = _testlist[_nTestIndex];
229 
230  // passing a response to a test that is already completed ??
231  ChkIfA(pCurrentTest->IsCompleted(), E_UNEXPECTED);
232 
233  hr = pCurrentTest->ProcessResponse(spMsg, addrRemote, addrLocal);
234  // this likely puts the test into the completed state
235  // A subsequent call to GetNextMessage will invoke the next tset
236 
237 Cleanup:
238  return hr;
239 }
240 
242 {
243  HRESULT hr=S_OK;
244  ChkIfA(pResults == NULL, E_INVALIDARG);
245  *pResults = _results;
246 Cleanup:
247  return hr;
248 }
249 
const uint32_t MAX_STUN_MESSAGE_SIZE
Definition: stuntypes.h:178
#define S_OK
Definition: hresult.h:46
#define ASSERT(expr)
CSocketAddress addrAP
CBasicBindingTest _test1
HRESULT Initialize(StunClientLogicConfig &config)
#define E_STUNCLIENT_STILL_WAITING
CBehaviorTest _testBehavior2
#define E_STUNCLIENT_RESULTS_READY
CFilteringTest _testFiltering2
HRESULT GetNextMessage(CRefCountedBuffer &spMsg, CSocketAddress *pAddrDest, uint32_t timeCurrentMilliseconds)
uint16_t GetPort() const
NatBehavior behavior
virtual void NotifyTimeout()=0
#define ChkIf(expr, hrerror)
Definition: chkmacros.h:63
#define E_UNEXPECTED
Definition: hresult.h:48
#define SUCCEEDED(hr)
Definition: hresult.h:28
virtual bool IsCompleted()=0
CSocketAddress addrMapped
int32_t HRESULT
Definition: hresult.h:22
CFilteringTest _testFiltering3
CSocketAddress addrLocal
virtual bool IsReadyToRun()=0
virtual HRESULT Init(StunClientLogicConfig *pConfig, StunClientResults *pResults)
#define E_INVALIDARG
Definition: hresult.h:51
std::vector< IStunClientTest * > _testlist
#define E_FAIL
Definition: hresult.h:56
virtual HRESULT GetMessage(CRefCountedBuffer &spMsg, CSocketAddress *pAddrDest)=0
HRESULT ProcessResponse(CRefCountedBuffer &spMsg, CSocketAddress &addrRemote, CSocketAddress &addrLocal)
#define FAILED(hr)
Definition: hresult.h:29
HRESULT GetResults(StunClientResults *pResults)
CSocketAddress addrMappingAA
void RunAsTest3(bool fSetAsTest3)
void RunAsTest3(bool fSetAsTest3)
NatFiltering filtering
CSocketAddress addrPA
boost::shared_ptr< CBuffer > CRefCountedBuffer
Definition: buffer.h:65
StunClientLogicConfig _config
StunClientResults _results
uint32_t _timeLastMessageSent
bool IsIPAddressZero() const
CSocketAddress addrMappingAP
#define ChkIfA(expr, hrerror)
Definition: chkmacros.h:84
CSocketAddress addrServer
virtual void PreRunCheck()=0
CSocketAddress addrAA
CBehaviorTest _testBehavior3