Stun Server  Compliant with the latest RFCs including 5389, 5769, and 5780
discover the local host's own external IP address
testmessagehandler.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 #include "commonincludes.hpp"
18 
19 #include "stuncore.h"
20 #include "unittest.h"
21 
22 #include "stunauth.h"
23 #include "testmessagehandler.h"
24 
25 
26 static const uint16_t c_portServerPrimary = 3478;
27 static const uint16_t c_portServerAlternate = 3479;
28 static const char* c_szIPServerPrimary = "1.2.3.4";
29 static const char* c_szIPServerAlternate = "1.2.3.5";
30 
31 static const char* c_szIPLocal = "2.2.2.2";
32 static const uint16_t c_portLocal = 2222;
33 
34 static const char* c_szIPMapped = "3.3.3.3";
35 static const uint16_t c_portMapped = 3333;
36 
37 
38 
40 {
41  const char* c_pszAuthorizedUser = "AuthorizedUser";
42 
43  pResponse->authCredMech = AuthCredShortTerm;
44 
45  if (0 == strcmp(pAuthAttributes->szUser, c_pszAuthorizedUser))
46  {
47  strcpy(pResponse->szPassword, "password");
48  pResponse->responseType = AllowConditional;
49  }
50  else if (pAuthAttributes->szUser[0] == '\0')
51  {
52  pResponse->responseType = Reject;
53  }
54  else
55  {
56  pResponse->responseType = Unauthorized;
57  }
58 
59  return S_OK;
60 }
61 
63 {
64  // go ahead and return a realm/nonce. It won't get used unless it's an error response
65  bool fIsKnownUser = false;
66 
67  strcpy(pResponse->szRealm, "MyRealm");
68  strcpy(pResponse->szNonce, "NewNonce");
69 
70  pResponse->authCredMech = AuthCredLongTerm;
71 
72  fIsKnownUser = !strcmp(pAuthAttributes->szUser, "AuthorizedUser");
73 
74  if ((pAuthAttributes->fMessageIntegrityPresent == false) || (fIsKnownUser == false))
75  {
76  pResponse->responseType = Unauthorized;
77  }
78  else
79  {
80  pResponse->responseType = AllowConditional;
81  strcpy(pResponse->szPassword, "password");
82  }
83 
84  return S_OK;
85 }
86 
87 
88 // ---------------------------------------------------------------------------------------------
89 
90 
91 
93 {
94  CMockAuthShort::CreateInstanceNoInit(_spAuthShort.GetPointerPointer());
95  CMockAuthLong::CreateInstanceNoInit(_spAuthLong.GetPointerPointer());
96 
97  ToAddr(c_szIPLocal, c_portLocal, &_addrLocal);
98  ToAddr(c_szIPMapped, c_portMapped, &_addrMapped);
99 
100  ToAddr(c_szIPServerPrimary, c_portServerPrimary, &_addrServerPP);
101  ToAddr(c_szIPServerPrimary, c_portServerAlternate, &_addrServerPA);
102  ToAddr(c_szIPServerAlternate, c_portServerPrimary, &_addrServerAP);
103  ToAddr(c_szIPServerAlternate, c_portServerAlternate, &_addrServerAA);
104 }
105 
106 
108 {
109  CRefCountedBuffer spBufferRequest;
110  CRefCountedBuffer spBufferResponse(new CBuffer(MAX_STUN_MESSAGE_SIZE));
111  StunMessageIn msgIn;
112  StunMessageOut msgOut;
113  CStunMessageReader reader;
114  CSocketAddress addrDest;
116  HRESULT hr = S_OK;
117 
118 
119  InitTransportAddressSet(tas, true, true, true, true);
120 
121  builderRequest.GetResult(&spBufferRequest);
122 
123  ChkIf(CStunMessageReader::BodyValidated != reader.AddBytes(spBufferRequest->GetData(), spBufferRequest->GetSize()), E_FAIL);
124 
125  msgIn.fConnectionOriented = false;
126  msgIn.addrLocal = _addrServerPP;
127  msgIn.pReader = &reader;
128  msgIn.socketrole = RolePP;
129  msgIn.addrRemote = _addrMapped;
130 
131  msgOut.spBufferOut = spBufferResponse;
132 
133  ChkA(CStunRequestHandler::ProcessRequest(msgIn, msgOut, &tas, pAuth));
134 
135  ChkIf(CStunMessageReader::BodyValidated != pReaderResponse->AddBytes(spBufferResponse->GetData(), spBufferResponse->GetSize()), E_FAIL);
136 
137 Cleanup:
138 
139  return hr;
140 
141 }
142 
143 void CTestMessageHandler::ToAddr(const char* pszIP, uint16_t port, CSocketAddress* pAddr)
144 {
145  sockaddr_in addr={};
146  int result;
147 
148  UNREFERENCED_VARIABLE(result);
149 
150  addr.sin_family = AF_INET;
151  addr.sin_port = htons(port);
152 
153  result = ::inet_pton(AF_INET, pszIP, &addr.sin_addr);
154 
155  ASSERT(result == 1);
156 
157  *pAddr = addr;
158 }
159 
160 
162 {
163  StunTransactionId transid;
165  builder.AddRandomTransactionId(&transid);
166  builder.FixLengthField();
167  return S_OK;
168 }
169 
171 {
172  HRESULT hr = S_OK;
173  CSocketAddress addrMapped;
174  CSocketAddress addrXorMapped;
175  HRESULT hrResult;
176 
177  hrResult = reader.GetXorMappedAddress(&addrXorMapped);
178 
179  if (SUCCEEDED(hrResult))
180  {
181  ChkIfA(false == addrExpected.IsSameIP_and_Port(addrXorMapped), E_FAIL);
182  ChkIfA(fLegacyOnly, E_FAIL); // legacy responses should not include XOR mapped
183  }
184  else
185  {
186  ChkIfA(fLegacyOnly==false, E_FAIL); // non-legacy responses should include XOR Mapped address
187  }
188 
189  ChkA(reader.GetMappedAddress(&addrMapped));
190  ChkIfA(false == addrExpected.IsSameIP_and_Port(addrMapped), E_FAIL);
191 
192 Cleanup:
193  return hr;
194 }
195 
197 {
198  HRESULT hr = S_OK;
199  CSocketAddress addr;
200 
201  ChkA(reader.GetResponseOriginAddress(&addr));
202  ChkIfA(false == addrExpected.IsSameIP_and_Port(addr), E_FAIL);
203 
204 Cleanup:
205  return hr;
206 }
207 
208 
209 
211 {
212  HRESULT hr = S_OK;
213  CSocketAddress addr;
214 
215  ChkA(reader.GetOtherAddress(&addr));
216  ChkIfA(false == addrExpected.IsSameIP_and_Port(addr), E_FAIL);
217 
218 Cleanup:
219  return hr;
220 }
221 
222 void CTestMessageHandler::InitTransportAddressSet(TransportAddressSet& tas, bool fRolePP, bool fRolePA, bool fRoleAP, bool fRoleAA)
223 {
224  CSocketAddress addrZero;
225 
226  tas.set[RolePP].fValid = fRolePP;
227  tas.set[RolePP].addr = fRolePP ? _addrServerPP : addrZero;
228 
229  tas.set[RolePA].fValid = fRolePA;
230  tas.set[RolePA].addr = fRolePA ? _addrServerPA : addrZero;
231 
232  tas.set[RoleAP].fValid = fRoleAP;
233  tas.set[RoleAP].addr = fRoleAP ? _addrServerAP : addrZero;
234 
235  tas.set[RoleAA].fValid = fRoleAA;
236  tas.set[RoleAA].addr = fRoleAA ? _addrServerAA : addrZero;
237 
238 }
239 
240 
241 
242 // Test1 - just do a basic binding request
244 {
245  HRESULT hr = S_OK;
246  CStunMessageBuilder builder;
247  CRefCountedBuffer spBuffer, spBufferOut(new CBuffer(MAX_STUN_MESSAGE_SIZE));
248  CStunMessageReader reader;
249  StunMessageIn msgIn;
250  StunMessageOut msgOut;
251  TransportAddressSet tas = {};
252 
253  InitTransportAddressSet(tas, true, true, true, true);
254 
255  ChkA(InitBindingRequest(builder));
256 
257 
258  Chk(builder.GetResult(&spBuffer));
259 
260  ChkIfA(CStunMessageReader::BodyValidated != reader.AddBytes(spBuffer->GetData(), spBuffer->GetSize()), E_FAIL);
261 
262  // a message send to the PP socket on the server from the
263  msgIn.socketrole = RolePP;
264  msgIn.addrRemote = _addrMapped;
265  msgIn.pReader = &reader;
266  msgIn.addrLocal = _addrServerPP;
267  msgIn.fConnectionOriented = false;
268 
269  spBuffer.reset();
270 
271  msgOut.spBufferOut = spBufferOut;
272  msgOut.socketrole = RoleAA; // deliberately wrong - so we can validate if it got changed to RolePP
273 
274  ChkA(CStunRequestHandler::ProcessRequest(msgIn, msgOut, &tas, NULL));
275 
276  reader.Reset();
277  ChkIfA(CStunMessageReader::BodyValidated != reader.AddBytes(spBufferOut->GetData(), spBufferOut->GetSize()), E_FAIL);
278 
279 
280  // validate that the message returned is a success response for a binding request
282  ChkIfA(reader.GetMessageType() != (uint16_t)StunMsgTypeBinding, E_FAIL);
283 
284 
285  // Validate that the message came from the server port we expected
286  // and that it's the same address the server set for the origin address
287  ChkIfA(msgOut.socketrole != RolePP, E_FAIL);
288  ChkA(ValidateResponseOriginAddress(reader, _addrServerPP));
289  ChkIfA(msgOut.addrDest.IsSameIP_and_Port(_addrMapped)==false, E_FAIL);
290 
291  // validate that the mapping was done correctly
292  ChkA(ValidateMappedAddress(reader, _addrMapped, false));
293 
294  ChkA(ValidateOtherAddress(reader, _addrServerAA));
295 
296 
297 Cleanup:
298  return hr;
299 }
300 
301 // Test2 - send a binding request to a duplex server instructing it to send back on it's alternate port and alternate IP to an alternate client port
303 {
304  HRESULT hr = S_OK;
305  CStunMessageBuilder builder;
306  CRefCountedBuffer spBuffer, spBufferOut(new CBuffer(MAX_STUN_MESSAGE_SIZE));
307  CStunMessageReader reader;
308  StunMessageIn msgIn;
309  StunMessageOut msgOut;
310  TransportAddressSet tas = {};
311  uint16_t responsePort = 2222;
312  StunChangeRequestAttribute changereq;
314  CSocketAddress addrDestExpected;
315 
316 
317  InitTransportAddressSet(tas, true, true, true, true);
318 
319 
320  InitBindingRequest(builder);
321 
322 
323  builder.AddResponsePort(responsePort);
324 
325  changereq.fChangeIP = true;
326  changereq.fChangePort = true;
327  builder.AddChangeRequest(changereq);
328  builder.AddResponsePort(responsePort);
329  builder.GetResult(&spBuffer);
330 
331  ChkIfA(CStunMessageReader::BodyValidated != reader.AddBytes(spBuffer->GetData(), spBuffer->GetSize()), E_FAIL);
332 
333  msgIn.fConnectionOriented = false;
334  msgIn.addrLocal = _addrServerPP;
335  msgIn.pReader = &reader;
336  msgIn.socketrole = RolePP;
337  msgIn.addrRemote = _addrMapped;
338 
339  msgOut.socketrole = RolePP; // deliberate initialized wrong
340  msgOut.spBufferOut = spBufferOut;
341 
342  ChkA(CStunRequestHandler::ProcessRequest(msgIn, msgOut, &tas, NULL));
343 
344  // parse the response
345  reader.Reset();
346  state = reader.AddBytes(spBufferOut->GetData(), spBufferOut->GetSize());
348 
349  // validate that the message was sent back from the AA
350  ChkIfA(msgOut.socketrole != RoleAA, E_FAIL);
351  // validate that the server though it was sending back from the AA
352  ChkA(ValidateResponseOriginAddress(reader, _addrServerAA));
353 
354  // validate that the message was sent to the response port requested
355  addrDestExpected = _addrMapped;
356  addrDestExpected.SetPort(responsePort);
357  ChkIfA(addrDestExpected.IsSameIP_and_Port(msgOut.addrDest)==false, E_FAIL);
358 
359  // validate that the binding response came back
360  ChkA(ValidateMappedAddress(reader, _addrMapped, false));
361 
362  // the "other" address is still AA (See RFC 3489 - section 8.1)
363  ChkA(ValidateOtherAddress(reader, _addrServerAA));
364 
365 
366 Cleanup:
367 
368  return hr;
369 }
370 
371 
372 
373 
374 
375 
376 // test simple authentication
378 {
379  CStunMessageBuilder builder1, builder2, builder3;
380  CStunMessageReader readerResponse;
381  uint16_t errorcode = 0;
382  HRESULT hr = S_OK;
383 
384 
385  // -----------------------------------------------------------------------
386  // simulate an authorized user making a request with a valid password
387 
388  ChkA(InitBindingRequest(builder1));
389  builder1.AddStringAttribute(STUN_ATTRIBUTE_USERNAME, "AuthorizedUser");
390  builder1.AddMessageIntegrityShortTerm("password");
391  builder1.FixLengthField();
392 
393  ChkA(SendHelper(builder1, &readerResponse, _spAuthShort));
394  ChkA(readerResponse.ValidateMessageIntegrityShort("password"));
395 
396 
397  // -----------------------------------------------------------------------
398  // simulate a user with a bad password
399  readerResponse.Reset();
400  InitBindingRequest(builder2);
401  builder2.AddStringAttribute(STUN_ATTRIBUTE_USERNAME, "WrongUser");
402  builder2.AddMessageIntegrityShortTerm("wrongpassword");
403  builder2.FixLengthField();
404 
405  ChkA(SendHelper(builder2, &readerResponse, _spAuthShort))
406 
407  errorcode = 0;
408  ChkA(readerResponse.GetErrorCode(&errorcode));
409  ChkIfA(errorcode != ::STUN_ERROR_UNAUTHORIZED, E_FAIL);
410 
411  // -----------------------------------------------------------------------
412  // simulate a client sending no credentials - we expect it to fire back with a 400/bad-request
413  readerResponse.Reset();
414  ChkA(InitBindingRequest(builder3));
415 
416  ChkA(SendHelper(builder3, &readerResponse, _spAuthShort));
417 
418  errorcode = 0;
419  ChkA(readerResponse.GetErrorCode(&errorcode));
420  ChkIfA(errorcode != ::STUN_ERROR_BADREQUEST, E_FAIL);
421 
422 Cleanup:
423  return hr;
424 
425 }
426 
427 
428 // test long-credential authentication
430 {
431  HRESULT hr=S_OK;
432  CStunMessageBuilder builder1, builder2;
433  CStunMessageReader readerResponse;
434  CSocketAddress addrMapped;
435  uint16_t errorcode = 0;
436  char szNonce[MAX_STUN_AUTH_STRING_SIZE+1];
437  char szRealm[MAX_STUN_AUTH_STRING_SIZE+1];
438 
439 
440  // -----------------------------------------------------------------------
441  // simulate a user making a request with no message integrity attribute (or username, or realm)
442  InitBindingRequest(builder1);
443  builder1.FixLengthField();
444 
445  ChkA(SendHelper(builder1, &readerResponse, _spAuthLong));
446 
447  Chk(readerResponse.GetErrorCode(&errorcode));
448 
450  ChkIf(errorcode != ::STUN_ERROR_UNAUTHORIZED, E_UNEXPECTED);
451 
452  readerResponse.GetStringAttributeByType(STUN_ATTRIBUTE_REALM, szRealm, ARRAYSIZE(szRealm));
453  readerResponse.GetStringAttributeByType(STUN_ATTRIBUTE_NONCE, szNonce, ARRAYSIZE(szNonce));
454 
455 
456  // --------------------------------------------------------------------------------
457  // now simulate the follow-up request
458  readerResponse.Reset();
459  InitBindingRequest(builder2);
460  builder2.AddNonce(szNonce);
461  builder2.AddRealm(szRealm);
462  builder2.AddUserName("AuthorizedUser");
463  builder2.AddMessageIntegrityLongTerm("AuthorizedUser", szRealm, "password");
464  builder2.FixLengthField();
465 
466  ChkA(SendHelper(builder2, &readerResponse, _spAuthLong));
467 
469 
470  // should have a mapped address
471  ChkA(readerResponse.GetMappedAddress(&addrMapped));
472 
473  // and the message integrity field should be valid
474  ChkA(readerResponse.ValidateMessageIntegrityLong("AuthorizedUser", szRealm, "password"));
475 
476 Cleanup:
477  return hr;
478 }
479 
480 
482 {
483 
484  HRESULT hr = S_OK;
485 
486  Chk(Test1());
487  Chk(Test2());
488  Chk(Test3());
489  Chk(Test4());
490 
491 Cleanup:
492  return hr;
493 }
494 
495 
const uint32_t MAX_STUN_MESSAGE_SIZE
Definition: stuntypes.h:178
AuthResponseType responseType
Definition: stunauth.h:54
#define S_OK
Definition: hresult.h:46
const uint16_t STUN_ATTRIBUTE_USERNAME
Definition: stuntypes.h:39
#define ASSERT(expr)
char szUser[MAX_STUN_AUTH_STRING_SIZE+1]
Definition: stunauth.h:29
HRESULT AddStringAttribute(uint16_t attribType, const char *pstr)
uint16_t GetMessageType()
Definition: stunreader.cpp:839
HRESULT AddRandomTransactionId(StunTransactionId *pTransId)
Definition: stunbuilder.cpp:93
HRESULT AddRealm(const char *pszRealm)
const uint16_t STUN_ERROR_UNAUTHORIZED
Definition: stuntypes.h:86
bool IsSameIP_and_Port(const CSocketAddress &other) const
HRESULT ValidateMessageIntegrityLong(const char *pszUser, const char *pszRealm, const char *pszPassword)
Definition: stunreader.cpp:282
static HRESULT CreateInstanceNoInit(X **ppInstance)
Definition: objectfactory.h:30
SocketRole socketrole
#define Chk(expr)
Definition: chkmacros.h:53
static const uint16_t c_portServerPrimary
HRESULT FixLengthField()
bool fMessageIntegrityPresent
Definition: stunauth.h:33
HRESULT ValidateMappedAddress(CStunMessageReader &reader, const CSocketAddress &addrExpected, bool fLegacyOnly)
SocketRole socketrole
static const char * c_szIPLocal
static const uint16_t c_portLocal
CSocketAddress addr
const uint16_t STUN_ATTRIBUTE_REALM
Definition: stuntypes.h:51
HRESULT GetMappedAddress(CSocketAddress *pAddress)
Definition: stunreader.cpp:505
AuthCredentialMechanism authCredMech
Definition: stunauth.h:55
#define UNREFERENCED_VARIABLE(unrefparam)
#define ARRAYSIZE(arr)
const uint16_t STUN_ATTRIBUTE_NONCE
Definition: stuntypes.h:52
#define ChkIf(expr, hrerror)
Definition: chkmacros.h:63
ReaderParseState AddBytes(const uint8_t *pData, uint32_t size)
Definition: stunreader.cpp:750
const uint16_t STUN_ERROR_BADREQUEST
Definition: stuntypes.h:85
#define E_UNEXPECTED
Definition: hresult.h:48
Definition: buffer.h:27
HRESULT GetOtherAddress(CSocketAddress *pAddress)
Definition: stunreader.cpp:513
HRESULT SendHelper(CStunMessageBuilder &builderRequest, CStunMessageReader *pReaderResponse, IStunAuth *pAuth)
CSocketAddress addrRemote
What local IP address the message was received on (useful if the socket binded to INADDR_ANY) ...
#define SUCCEEDED(hr)
Definition: hresult.h:28
CSocketAddress addrLocal
which socket id did the message arrive on
HRESULT InitBindingRequest(CStunMessageBuilder &builder)
static const uint16_t c_portMapped
char szPassword[MAX_STUN_AUTH_STRING_SIZE+1]
Definition: stunauth.h:57
const uint32_t MAX_STUN_AUTH_STRING_SIZE
Definition: stunauth.h:22
void SetPort(uint16_t)
HRESULT AddHeader(StunMessageType msgType, StunMessageClass msgClass)
Definition: stunbuilder.cpp:55
HRESULT AddMessageIntegrityLongTerm(const char *pszUserName, const char *pszRealm, const char *pszPassword)
void ToAddr(const char *pszIP, uint16_t port, CSocketAddress *pAddr)
HRESULT GetErrorCode(uint16_t *pErrorNumber)
Definition: stunreader.cpp:461
HRESULT ValidateMessageIntegrityShort(const char *pszPassword)
Definition: stunreader.cpp:277
char szNonce[MAX_STUN_AUTH_STRING_SIZE+1]
Definition: stunauth.h:59
HRESULT AddNonce(const char *pszNonce)
int32_t HRESULT
Definition: hresult.h:22
static const char * c_szIPServerAlternate
HRESULT AddResponsePort(uint16_t port)
CSocketAddress addrDest
HRESULT GetResult(CRefCountedBuffer *pspBuffer)
virtual HRESULT DoAuthCheck(AuthAttributes *pAuthAttributes, AuthResponse *pResponse)
HRESULT AddMessageIntegrityShortTerm(const char *pszPassword)
HRESULT AddChangeRequest(const StunChangeRequestAttribute &changeAttrib)
HRESULT ValidateResponseOriginAddress(CStunMessageReader &reader, const CSocketAddress &addrExpected)
CStunMessageReader * pReader
the address of the node that sent us the message
static const char * c_szIPServerPrimary
#define E_FAIL
Definition: hresult.h:56
StunMessageClass GetMessageClass()
Definition: stunreader.cpp:834
HRESULT ValidateOtherAddress(CStunMessageReader &reader, const CSocketAddress &addrExpected)
static HRESULT ProcessRequest(const StunMessageIn &msgIn, StunMessageOut &msgOut, TransportAddressSet *pAddressSet, IStunAuth *pAuth)
#define ChkA(expr)
Definition: chkmacros.h:73
static const char * c_szIPMapped
TransportAddress set[4]
CRefCountedBuffer spBufferOut
char szRealm[MAX_STUN_AUTH_STRING_SIZE+1]
Definition: stunauth.h:58
boost::shared_ptr< CBuffer > CRefCountedBuffer
Definition: buffer.h:65
HRESULT GetStringAttributeByType(uint16_t attributeType, char *pszValue, size_t size)
Definition: stunreader.cpp:565
static const uint16_t c_portServerAlternate
virtual HRESULT DoAuthCheck(AuthAttributes *pAuthAttributes, AuthResponse *pResponse)
#define ChkIfA(expr, hrerror)
Definition: chkmacros.h:84
HRESULT GetXorMappedAddress(CSocketAddress *pAddress)
Definition: stunreader.cpp:529
HRESULT AddUserName(const char *pszUserName)
bool fConnectionOriented
reader containing a valid stun message
void InitTransportAddressSet(TransportAddressSet &tas, bool fRolePP, bool fRolePA, bool fRoleAP, bool fRoleAA)
HRESULT GetResponseOriginAddress(CSocketAddress *pAddress)
Definition: stunreader.cpp:548