< Previous by Date Date Index Next by Date >
  Thread Index Next in Thread >

[reSIProcate] STUN over TCP



My company has modified 1.9.6 to do STUN over TCP. I am cross-posting this to return-devel and resiprocate-devel because the two files we changed are in resip/stack, but I figure changes involving STUN might interest the reTurn folks as well. We enclosed our changes in the conditional compilatoin flag AYLUS_CHANGE_STUN_OVER_TCP, which should be removed if/when this code is ever incorporated into the codebase. Also, because we only act as a STUN server, we #if 0'ed the code that processes STUN responses. No guarantees about the disabled code. Diff files attached (ConnectionBase.cxx and ConnectionBase.hxx changes from 1.9.6).

-John Gregg

Index: ConnectionBase.cxx
===================================================================
--- ConnectionBase.cxx	(revision 27529)
+++ ConnectionBase.cxx	(working copy)
@@ -33,12 +33,20 @@
 #include <osc/StateChanges.h>
 #endif
 
+#if AYLUS_CHANGE_STUN_OVER_TCP
+#include "rutil/stun/Stun.hxx"
+#endif
+
 using namespace resip;
 
 #define RESIPROCATE_SUBSYSTEM Subsystem::TRANSPORT
 
 char 
-ConnectionBase::connectionStates[ConnectionBase::MAX][32] = { "NewMessage", "ReadingHeaders", "PartialBody" };
+ConnectionBase::connectionStates[ConnectionBase::MAX][32] = { "NewMessage", "ReadingHeaders", "PartialBody",
+#if AYLUS_CHANGE_STUN_OVER_TCP
+   "PartialStunRequest"
+#endif
+};
 
 #ifndef RESIP_SIP_MSG_MAX_BYTES
 #define RESIP_SIP_MSG_MAX_BYTES  10485760
@@ -64,6 +72,9 @@
      mBuffer(0),
      mBufferPos(0),
      mBufferSize(0),
+#if AYLUS_CHANGE_STUN_OVER_TCP
+     mStunLength(0),
+#endif
      mWsFrameExtractor(messageSizeMax),
      mLastUsed(Timer::getTimeMs()),
      mConnState(NewMessage)
@@ -130,6 +141,69 @@
    return mWho.mFlowKey;
 }
 
+#if AYLUS_CHANGE_STUN_OVER_TCP
+
+
+// Cut and pasted from UdpTransport::processRxParse()
+void
+ConnectionBase::ProcessStunRequest()
+{
+    bool changePort = false;
+    bool changeIp = false;
+
+    StunAddress4 myAddr;
+    const sockaddr_in& bi = (const sockaddr_in&) transport()->boundInterface();
+    myAddr.addr = ntohl(bi.sin_addr.s_addr);
+    myAddr.port = ntohs(bi.sin_port);
+
+    StunAddress4 from; // packet source
+    const sockaddr_in& fi = (const sockaddr_in&) who().getSockaddr();
+    from.addr = ntohl(fi.sin_addr.s_addr);
+    from.port = ntohs(fi.sin_port);
+
+    StunMessage resp;
+    StunAddress4 dest;
+    StunAtrString hmacPassword;
+    hmacPassword.sizeValue = 0;
+
+    StunAddress4 secondary;
+    secondary.port = 0;
+    secondary.addr = 0;
+
+    bool ok = stunServerProcessMsg( mBuffer, mStunLength, // input buffer
+                                    from,  // packet source
+                                    secondary, // not used
+                                    myAddr, // address to fill into response
+                                    myAddr, // not used
+                                    &resp, // stun response
+                                    &dest, // where to send response
+                                    &hmacPassword, // not used
+                                    &changePort, // not used
+                                    &changeIp, // not used
+                                    true ); // logging
+
+    if (ok)
+    {
+        DebugLog(<<"Got TCP STUN request. Sending response...");
+        char* response = new char[STUN_MAX_MESSAGE_SIZE];
+        size_t rlen = stunEncodeMessage( resp,
+                                         response,
+                                         STUN_MAX_MESSAGE_SIZE,
+                                         hmacPassword,
+                                         false );
+
+        std::auto_ptr<SendData> respSendData(new SendData(who(), response, rlen));
+
+        transport()->send(respSendData);
+    }
+    else
+    {
+        ErrLog(<< "Failed to parse STUN request");
+    }
+}
+
+#endif
+
 bool
 ConnectionBase::preparseNewBytes(int bytesRead)
 {
@@ -141,6 +215,9 @@
    {
       case NewMessage:
       {
+#if AYLUS_CHANGE_STUN_OVER_TCP
+          char * buffer = mBuffer + mBufferPos;
+#endif
          if (strncmp(mBuffer + mBufferPos, Symbols::CRLFCRLF, 4) == 0)
          {
             DebugLog(<< "Got incoming double-CRLF keepalive (aka ping).");
@@ -176,6 +253,113 @@
             }
          }
 
+#if AYLUS_CHANGE_STUN_OVER_TCP
+         // AYLUS_CHANGE: STUN processing BEGIN
+         // Cut/pasted from UdpTransport.cxx
+
+#if 0
+
+         // We don't need to worry about STUN responses (yet)
+
+         // this must be a STUN response (or garbage)
+         else if ((bytesRead >= 4) && buffer[0] == 1 && buffer[1] == 1)
+         {
+             StunMessage resp;
+             memset(&resp, 0, sizeof(StunMessage));
+
+             if (stunParseMessage(buffer, len, resp, false))
+             {
+                 in_addr sin_addr;
+                 // Use XorMappedAddress if present - if not use MappedAddress
+                 if(resp.hasXorMappedAddress)
+                 {
+                     UInt16 id16 = resp.msgHdr.id.octet[0]<<8
+                         | resp.msgHdr.id.octet[1];
+                     UInt32 id32 = resp.msgHdr.id.octet[0]<<24
+                         | resp.msgHdr.id.octet[1]<<16
+                         | resp.msgHdr.id.octet[2]<<8
+                         | resp.msgHdr.id.octet[3];
+                     resp.xorMappedAddress.ipv4.port = resp.xorMappedAddress.ipv4.port^id16;
+                     resp.xorMappedAddress.ipv4.addr = resp.xorMappedAddress.ipv4.addr^id32;
+
+#if defined(WIN32)
+                     sin_addr.S_un.S_addr = htonl(resp.xorMappedAddress.ipv4.addr);
+#else
+                     sin_addr.s_addr = htonl(resp.xorMappedAddress.ipv4.addr);
+#endif
+                     mStunMappedAddress = Tuple(sin_addr,resp.xorMappedAddress.ipv4.port, UDP);
+                     mStunSuccess = true;
+                 }
+                 else if(resp.hasMappedAddress)
+                 {
+#if defined(WIN32)
+                     sin_addr.S_un.S_addr = htonl(resp.mappedAddress.ipv4.addr);
+#else
+                     sin_addr.s_addr = htonl(resp.mappedAddress.ipv4.addr);
+#endif
+
+                     mStunMappedAddress = Tuple(sin_addr,resp.mappedAddress.ipv4.port, UDP);
+                     mStunSuccess = true;
+                 }
+             }
+             return false;
+         }
+#endif // 0
+
+         // this must be a STUN request (or garbage)
+         else if ((bytesRead >= 4) && buffer[0] == 0 && buffer[1] == 1)
+         {
+             mStunLength = ntohs(*((uint16_t *) (&buffer[2])));
+             mStunLength += 20; // total, including header.
+
+             DebugLog(<< "STUN request, mStunLength " << mStunLength << ", bytesRead " << bytesRead);
+
+             if (bytesRead + (int) mBufferPos >= (int) mStunLength)
+             {
+                 // good to go
+
+                 ProcessStunRequest();
+
+                 int overHang = bytesRead - mStunLength;
+
+                 mConnState = NewMessage;
+
+                 if (overHang > 0)
+                 {
+                     size_t size = overHang*3/2;
+                     if (size < ConnectionBase::ChunkSize)
+                     {
+                         size = ConnectionBase::ChunkSize;
+                     }
+                     char* newBuffer = MsgHeaderScanner::allocateBuffer((int)size);
+                     memcpy(newBuffer, mBuffer + mStunLength, overHang);
+                     delete [] mBuffer;
+                     
+                     mBuffer = newBuffer;
+                     mBufferPos = 0;
+                     mBufferSize = size;
+                     mStunLength = 0;
+                  
+                     DebugLog (<< "Extra bytes after message: " << overHang);
+                     DebugLog (<< Data(mBuffer, overHang));
+                     
+                     bytesRead = overHang;
+                     goto start;
+                 }
+
+                 mStunLength = 0;
+             }
+             else
+             {
+                 // need more bytes.
+                 mBufferPos += bytesRead;
+                 mConnState = PartialStunRequest;
+             }
+
+             break;
+         }
+#endif // AYLUS_CHANGE_STUN_OVER_TCP
+
          assert(mTransport);
          mMessage = new SipMessage(mTransport);
          
@@ -448,6 +632,56 @@
          }
          break;
       }
+
+#if AYLUS_CHANGE_STUN_OVER_TCP
+
+   case PartialStunRequest:
+       {
+           DebugLog(<< "STUN request, mStunLength " << mStunLength << ", bytesRead " << bytesRead << ", mBufferPos " << mBufferPos);
+           if (bytesRead + mBufferPos >= mStunLength)
+           {
+               // good to go
+
+               ProcessStunRequest();
+
+               int overHang = bytesRead - mStunLength;
+
+               mConnState = NewMessage;
+
+               if (overHang > 0)
+               {
+                   size_t size = overHang*3/2;
+                   if (size < ConnectionBase::ChunkSize)
+                   {
+                       size = ConnectionBase::ChunkSize;
+                   }
+                   char* newBuffer = MsgHeaderScanner::allocateBuffer((int)size);
+                   memcpy(newBuffer, mBuffer + mStunLength, overHang);
+                   delete [] mBuffer;
+                   mBuffer = newBuffer;
+                   mBufferPos = 0;
+                   mBufferSize = size;
+                   mStunLength = 0;
+                  
+                   DebugLog (<< "Extra bytes after message: " << overHang);
+                   DebugLog (<< Data(mBuffer, overHang));
+                     
+                   bytesRead = overHang;
+                   goto start;
+               }
+               mStunLength = 0;
+           }
+           else
+           {
+               // need more bytes.
+               mBufferPos += bytesRead;
+               mConnState = PartialStunRequest;
+           }
+       }
+       break;
+
+#endif // AYLUS_CHANGE_STUN_OVER_TCP
+
       case PartialBody:
       {
          size_t contentLength = 0;
Index: ConnectionBase.hxx
===================================================================
--- ConnectionBase.hxx	(revision 27529)
+++ ConnectionBase.hxx	(working copy)
@@ -12,6 +12,8 @@
 #include "resip/stack/WsFrameExtractor.hxx"
 #include "resip/stack/Cookie.hxx"
 
+#define AYLUS_CHANGE_STUN_OVER_TCP 1
+
 namespace osc
 {
    class Stack;
@@ -61,6 +63,9 @@
          NewMessage = 0,
          ReadingHeaders,
          PartialBody,
+#if AYLUS_CHANGE_STUN_OVER_TCP
+         PartialStunRequest,
+#endif
          SigComp, // This indicates that incoming bytes are compressed.
          WebSocket,
          MAX
@@ -76,6 +81,9 @@
       } TransmissionFormat;
 
       ConnState getCurrentState() const { return mConnState; }
+#if AYLUS_CHANGE_STUN_OVER_TCP
+    void ProcessStunRequest();
+#endif
       bool preparseNewBytes(int bytesRead);
       bool wsProcessHandshake(int bytesRead, bool &dropConnection);
       bool wsProcessData(int bytesRead);
@@ -121,6 +129,9 @@
       char* mBuffer;
       size_t mBufferPos;
       size_t mBufferSize;
+#if AYLUS_CHANGE_STUN_OVER_TCP
+      size_t mStunLength;
+#endif
       WsFrameExtractor mWsFrameExtractor;
 
       static char connectionStates[MAX][32];