void CTCPServer::InitTSA(TransportAddressSet* pTSA, SocketRole role, bool fValid, const CSocketAddress& addrListen, const CSocketAddress& addrAdvertise) { if (fValid == false) { pTSA->set[role].fValid = false; pTSA->set[role].addr = CSocketAddress(); } else { pTSA->set[role].fValid = true; if (addrAdvertise.IsIPAddressZero()) { pTSA->set[role].addr = addrListen; } else { pTSA->set[role].addr = addrAdvertise; pTSA->set[role].addr.SetPort(addrListen.GetPort()); } } }
HRESULT CStunThreadMessageHandler::ProcessBindingRequest(CStunMessageReader& reader) { HRESULT hrTmp; bool fRequestHasPaddingAttribute = false; SocketRole socketOutput = _message.localSocket; StunChangeRequestAttribute changerequest = {}; bool fSendOtherAddress = false; bool fSendOriginAddress = false; SocketRole socketOther; CSocketAddress addrOrigin; CSocketAddress addrOther; CStunMessageBuilder builder; uint16_t paddingSize = 0; bool fLegacyFormat = false; // set to true if the client appears to be rfc3489 based instead of based on rfc 5789 _spResponseBuffer->SetSize(0); builder.GetStream().Attach(_spResponseBuffer, true); fLegacyFormat = reader.IsMessageLegacyFormat(); // check for an alternate response port // check for padding attribute (todo - figure out how to inject padding into the response) // check for a change request and validate we can do it. If so, set _socketOutput. If not, fill out _error and return. // determine if we have an "other" address to notify the caller about // did the request come with a padding request if (SUCCEEDED(reader.GetPaddingAttributeSize(&paddingSize))) { // todo - figure out how we're going to get the MTU size of the outgoing interface fRequestHasPaddingAttribute = true; } // as per 5780, section 6.1, If the Request contained a PADDING attribute... // "If the Request also contains the RESPONSE-PORT attribute the server MUST return an error response of type 400." if (_fRequestHasResponsePort && fRequestHasPaddingAttribute) { _error.errorcode = STUN_ERROR_BADREQUEST; return E_FAIL; } // handle change request logic and figure out what "other-address" attribute is going to be if (SUCCEEDED(reader.GetChangeRequest(&changerequest))) { if (changerequest.fChangeIP) { socketOutput = SocketRoleSwapIP(socketOutput); } if(changerequest.fChangePort) { socketOutput = SocketRoleSwapPort(socketOutput); } // IsValidSocketRole just validates the enum, not whether or not we can send on it ASSERT(IsValidSocketRole(socketOutput)); // now, make sure we have the ability to send from another socket if (_spStunResponder->HasAddress(socketOutput) == false) { // send back an error. We're being asked to respond using another address that we don't have a socket for _error.errorcode = STUN_ERROR_BADREQUEST; return E_FAIL; } } // If we're only working one socket, then that's ok, we just don't send back an "other address" unless we have all four sockets confgiured // now here's a problem. If we binded to "INADDR_ANY", all of the sockets will have "0.0.0.0" for an address (same for IPV6) // So we effectively can't send back "other address" if don't really know our own IP address // Fortunately, recvfromex and the ioctls on the socket allow address discovery a bit better fSendOtherAddress = (_spStunResponder->HasAddress(RolePP) && _spStunResponder->HasAddress(RolePA) && _spStunResponder->HasAddress(RoleAP) && _spStunResponder->HasAddress(RoleAA)); if (fSendOtherAddress) { socketOther = SocketRoleSwapIP(SocketRoleSwapPort(_message.localSocket)); hrTmp = _spStunResponder->GetSocketAddressForRole(socketOther, &addrOther); ASSERT(SUCCEEDED(hrTmp)); // so if our ip address is "0.0.0.0", disable this attribute fSendOtherAddress = (SUCCEEDED(hrTmp) && (addrOther.IsIPAddressZero()==false)); } // What's our address origin? VERIFY(SUCCEEDED(_spStunResponder->GetSocketAddressForRole(socketOutput, &addrOrigin))); if (addrOrigin.IsIPAddressZero()) { // Since we're sending back from the IP address we received on, we can just use the address the message came in on // Otherwise, we don't actually know it if (socketOutput == _message.localSocket) { addrOrigin = _message.localAddr; } } fSendOriginAddress = (false == addrOrigin.IsIPAddressZero()); // Success - we're all clear to build the response _socketOutput = socketOutput; _spResponseBuffer->SetSize(0); builder.GetStream().Attach(_spResponseBuffer, true); builder.AddHeader(StunMsgTypeBinding, StunMsgClassSuccessResponse); builder.AddTransactionId(_transid); builder.AddMappedAddress(_message.remoteAddr); if (fLegacyFormat == false) { builder.AddXorMappedAddress(_message.remoteAddr); } if (fSendOriginAddress) { builder.AddResponseOriginAddress(addrOrigin); } if (fSendOtherAddress) { builder.AddOtherAddress(addrOther, fLegacyFormat); // pass true to send back CHANGED-ADDRESS, otherwise, pass false to send back OTHER-ADDRESS } // finally - if we're supposed to have a message integrity attribute as a result of authorization, add it at the very end if (_integrity.fSendWithIntegrity) { if (_integrity.fUseLongTerm == false) { builder.AddMessageIntegrityShortTerm(_integrity.szPassword); } else { builder.AddMessageIntegrityLongTerm(_integrity.szUser, _integrity.szRealm, _integrity.szPassword); } } builder.FixLengthField(); return S_OK; }
// This test validates that the EnablePktInfoOption set on a socket allows us to get at the destination IP address for incoming packets // This is needed so that we can correctly insert an origin address into responses from the server // Otherwise, the server doesn't have a way of knowing which interface a packet arrived on when it's listening on INADDR_ANY (all available addresses) HRESULT CTestRecvFromEx::DoTest(bool fIPV6) { // create a couple of sockets for send/recv HRESULT hr = S_OK; CSocketAddress addrAny(0,0); // INADDR_ANY, random port sockaddr_in6 addrAnyIPV6 = {}; uint16_t portRecv = 0; CRefCountedStunSocket spSocketSend, spSocketRecv; fd_set set = {}; CSocketAddress addrDestForSend; CSocketAddress addrDestOnRecv; CSocketAddress addrSrcOnRecv; CSocketAddress addrSrc; CSocketAddress addrDst; char ch = 'x'; sockaddr_storage addrDummy; socklen_t addrlength; int ret; timeval tv = {}; if (fIPV6) { addrAnyIPV6.sin6_family = AF_INET6; addrAny = CSocketAddress(addrAnyIPV6); } // create two sockets listening on INADDR_ANY. One for sending and one for receiving ChkA(CStunSocket::Create(addrAny, RolePP, &spSocketSend)); ChkA(CStunSocket::Create(addrAny, RolePP, &spSocketRecv)); spSocketRecv->EnablePktInfoOption(true); portRecv = spSocketRecv->GetLocalAddress().GetPort(); // now send to localhost if (fIPV6) { sockaddr_in6 addr6 = {}; addr6.sin6_family = AF_INET6; ::inet_pton(AF_INET6, "::1", &(addr6.sin6_addr)); addrDestForSend = CSocketAddress(addr6); } else { sockaddr_in addr4 = {}; addr4.sin_family = AF_INET; ::inet_pton(AF_INET, "127.0.0.1", &(addr4.sin_addr)); addrDestForSend = CSocketAddress(addr4); } addrDestForSend.SetPort(portRecv); // flush out any residual packets that might be buffered up on recv socket ret = -1; do { addrlength = sizeof(addrDummy); ret = ::recvfrom(spSocketRecv->GetSocketHandle(), &ch, sizeof(ch), MSG_DONTWAIT, (sockaddr*)&addrDummy, &addrlength); } while (ret >= 0); // now send some data to ourselves ret = sendto(spSocketSend->GetSocketHandle(), &ch, sizeof(ch), 0, addrDestForSend.GetSockAddr(), addrDestForSend.GetSockAddrLength()); ChkIfA(ret <= 0, E_UNEXPECTED); // now wait for the data to arrive FD_ZERO(&set); FD_SET(spSocketRecv->GetSocketHandle(), &set); tv.tv_sec = 3; ret = select(spSocketRecv->GetSocketHandle()+1, &set, NULL, NULL, &tv); ChkIfA(ret <= 0, E_UNEXPECTED); ret = ::recvfromex(spSocketRecv->GetSocketHandle(), &ch, 1, MSG_DONTWAIT, &addrSrcOnRecv, &addrDestOnRecv); ChkIfA(ret <= 0, E_UNEXPECTED); ChkIfA(addrSrcOnRecv.IsIPAddressZero(), E_UNEXPECTED); ChkIfA(addrDestOnRecv.IsIPAddressZero(), E_UNEXPECTED); Cleanup: return hr; }