j0kky

STUN UDF

6 posts in this topic

#1 ·  Posted (edited)

Hi guysssss,

have you ever heard of STUN?! No?

When you are under a NAT and you try to connect to an online server, you make the request using an internal port and your local IP.

But when your request arrives to the router, it uses its own port list and it enstablishes the connection to the final server using a different port (which has a different port number, but it is associated to your internal port) and the public (external) IP. So, the server responds to your request using the couple external IP\port assigned by the router.

Well, STUN is a protocol that permits you to know your external IP but, more interesting, your external port associated to your internal port.

But, which is the point?

Client/Server connections don't require to know these informations, but what about P2P connection?

STUN UDF is the second step (the first was my winsock UDF, which is used in this script) to UDP Hole Punching technique:

Quote

UDP hole punching establishes connectivity between two hosts communicating across one or more network address translators.

NAT traversal techniques are typically required for client-to-client networking applications on the Internet involving hosts connected in private networks, especially in peer-to-peer, Direct Client-to-Client (DCC) and Voice over Internet Protocol (VoIP) deployments.

Here you are the function:

; #FUNCTION# ========================================================================================================================
; Name...........: _STUN
; Description ...: Makes a STUN request and processes the response
; Syntax.........: _STUN($iLocalPort, $iStunServer = "", $iStunPort = "")
; Parameters ....: $iLocalPort - The local port to be tested
;                  |1 : 65535
;                  $iStunServer - name of the STUN server [Default = ""]
;                  $iStunPort - STUN server port, used only if $iStunServer <> ""
;                  |1 : 65535 - [Default = 3478]
; Return values .: On success it returns an array:
;                  |[0] - The external IP address
;                  |[1] - The external port
;                  On failure it returns -1 and sets @error to non zero:
;                  |-1 - internal error
;                  |-2 - missing DLL (Ws2_32.dll)
;                  |-3 - Unable to connect to internet
;                  |-4 - $iLocalPort is not a valid port
;                  |-5 - can't bind to $iLocalPort, try a different port
;                  |-6 - problem with servers
; Remarks .......: This function is used by a client to know which external port/IP correspond to the indicated internal port.
;                  By default it is used a STUN server list composed by:
;                  stun.l.google.com:19302, stun1.l.google.com:19302, stun.ekiga.net:3478, stun.ideasip.com:3478, stun.schlund.de:3478
;                  If $iStunServer is indicated, the first element of the list is replaced by $iStunServer:$iStunPort
; Author ........: j0kky
; Modified ......: 1.0.0
; Links .........: STUN RFC 5389:   https://tools.ietf.org/html/rfc5389
; ====================================================================================================================================
#include "winsock.au3"
Func _STUN($iLocalPort, $iStunServer = "", $iStunPort = "")
    $iLocalPort = Int($iLocalPort)
    If ($iLocalPort < 1) Or ($iLocalPort > 65535) Then Return SetError(-4, 0, -1)

    $iExtended = 0
    If $iStunServer = Default Then $iStunServer = ""
    $iStunServer = String($iStunServer)
    If $iStunServer Then
        $iStunServer = StringRegExpReplace($iStunServer, "^http.?://", "")
        If StringRight($iStunServer, 1) = "/" Then $iStunServer = StringTrimRight($iStunServer, 1)
        If Not StringRegExp($iStunServer, ".+\..{2,}") Then
            $iStunServer = "" ; invalid parameter
            $iExtended = -1
        EndIf
    EndIf

    $hWs2 = DllOpen("Ws2_32.dll")
    If @error Then Return SetError(-2, $iExtended, -1)

    $tHeader = DllStructCreate("ushort MessageType; ushort MessageLenght; ulong MagicCookie; byte TransactionID[12]")
    $aRet = DllCall($hWs2, "ushort", "htons", "ushort", 0x0001)
    If @error Then
        DllClose($hWs2)
        Return SetError(-1, $iExtended, -1)
    Else
        DllStructSetData($tHeader, "MessageType", $aRet[0]) ;MessageType
    EndIf
    DllStructSetData($tHeader, "MessageLenght", 0x0000) ;MessageLenght
    $aRet = DllCall($hWs2, "ulong", "htonl", "ulong", 0x2112A442)
    If @error Then
        DllClose($hWs2)
        Return SetError(-1, $iExtended, -1)
    Else
        DllStructSetData($tHeader, "MagicCookie", $aRet[0]) ;MagicCookie
    EndIf
    For $i = 1 To 12
        DllStructSetData($tHeader, "TransactionID", Random(0x00, 0xff, 1), $i) ;TransactionID
    Next

    _UDPStartup()
    If @error Then
        DllClose($hWs2)
        Return SetError(-1, $iExtended, -1)
    EndIf

    $sLocalIP = _GetIPs()
    If @error Then
        _UDPShutdown()
        DllClose($hWs2)
        Return SetError(-3, $iExtended, -1)
    EndIf
    $sLocalIP = $sLocalIP[0]

    If $iStunServer Then
        $sFirstServer = $iStunServer
        If $iStunPort Then
            $nFirstPort = $iStunPort
        Else
            $nFirstPort = 3478
        EndIf
    Else
        $sFirstServer = "stun.l.google.com"
        $nFirstPort = 19302
    EndIf
    Local $aStunServer[5][2] = [[$sFirstServer, $nFirstPort], ["stun1.l.google.com", 19302], ["stun.ekiga.net", 3478], ["stun.ideasip.com", 3478], ["stun.schlund.de", 3478]]

    $nBindSocket = _UDPBind($sLocalIP, $iLocalPort)
    If @error Then Return SetError(-5, $iExtended, -1)

    For $j = 0 To 4
        $iError = 0
        $sServerName = $aStunServer[$j][0]
        $sServerIP = _TCPNameToIP($sServerName)
        If @error Then $iError = -6

        If Not $iError Then
            ;RFC indicates that $nRcMaxValue should be equal to 7 and $nRm should be equal to 16,
            ;but, following those rules, if the server doesn't respond quickly, the script is blocked for more than 1 minute
            $tByteHeader = DllStructCreate("byte[" & DllStructGetSize($tHeader) & "]", DllStructGetPtr($tHeader))
            Local $nRTO = 500, $hTime = TimerInit(), $nRTOTotal = 0, $nRc = 0, $nRcMaxValue = 2, $aRecv = "", $nRm = 2
            Do
                If ($nRc < $nRcMaxValue) And (TimerDiff($hTime) >= $nRTOTotal) Then
                    _UDPSendTo($sServerIP, $aStunServer[$j][1], DllStructGetData($tByteHeader, 1), $nBindSocket)
                    If @error Then $iError = -1
                    $nRTOTotal += $nRTO * (2 ^ $nRc)
                    $nRc += 1
                    If $nRc = $nRcMaxValue Then $hTime = TimerInit()
                EndIf

                $aRecv = _UDPRecvFrom($nBindSocket, 2048, 1)
                If @error Then $iError = -1
                Sleep(100)
            Until ($aRecv <> -1) Or (($nRc = $nRcMaxValue) And (TimerDiff($hTime) >= $nRTO * $nRm))
            If Not IsArray($aRecv) Then $iError = -1
        EndIf

        If Not $iError Then
            $dResponse = $aRecv[0]
            $aRet = DllCall($hWs2, "ushort", "ntohs", "ushort", BinaryMid($dResponse, 3, 2)) ;MessageLenght
            If @error Then
                $iError = -1
            Else
                $nMessageLenght = $aRet[0]
            EndIf
            If Not ($nMessageLenght > 0) Then $iError = -6
        EndIf

        If Not $iError Then
            $dResponse = BinaryMid($dResponse, 1, $nMessageLenght + 20) ;message lenght plus header size
            $aRet = DllCall($hWs2, "ushort", "ntohs", "ushort", BinaryMid($dResponse, 1, 2)) ;MessageType
            If @error Then
                $iError = -1
            Else
                $nMessageType = $aRet[0]
                If Not (($nMessageType = 0x0101) Or ($nMessageType = 0x0111)) Then $iError = -6
            EndIf
        EndIf

        If Not $iError Then
            $nByteNumber = 1
            If $nMessageType = 0x0101 Then ;Binding success response
                Do
                    $iError = 0
                    $aRet = DllCall($hWs2, "ushort", "ntohs", "ushort", BinaryMid($dResponse, 20 + $nByteNumber, 2)) ;Attribute Type
                    If @error Then
                        $iError = -1
                    Else
                        $nAttributeType = $aRet[0]
                    EndIf

                    If Not $iError And (($nAttributeType = 0x0001) Or ($nAttributeType = 0x0020)) Then ;MAPPED-ADDRESS or XOR-MAPPED-ADDRESS
                        $aRet = DllCall($hWs2, "ushort", "ntohs", "ushort", BinaryMid($dResponse, 24 + $nByteNumber, 2)) ;Family
                        If @error Then
                            $iError = -1
                        Else
                            $nFamily = $aRet[0]
                        EndIf
                    EndIf

                    If Not $iError Then
                        Local $aResult[2]
                        If $nAttributeType = 0x0020 Then ;XOR-MAPPED-ADDRESS
                            $aRet = DllCall($hWs2, "ushort", "ntohs", "ushort", BinaryMid($dResponse, 26 + $nByteNumber, 2)) ;Port
                            If @error Then
                                $iError = -1
                            Else
                                $aResult[1] = BitXOR($aRet[0], 0x2112) ;Xored with the most significant 16 bit of Magic Cookie
                            EndIf

                            If Not $iError Then
                                If $nFamily = 0x0001 Then ;IPv4
                                    $dNBOIP = BinaryMid($dResponse, 28 + $nByteNumber, 4)
                                    $dNBOCookie = Binary("0x" & Hex(0x2112A442))
                                    $tNBOXoredIP = DllStructCreate("byte[4]")
                                    For $i = 1 To 4
                                        DllStructSetData($tNBOXoredIP, 1, BitXOR(BinaryMid($dNBOIP, $i, 1), BinaryMid($dNBOCookie, $i, 1)), $i)
                                    Next

                                    $aRet = DllCall($hWs2, "ptr", "inet_ntoa", "ulong", Int(DllStructGetData($tNBOXoredIP, 1)))
                                    If @error Then
                                        $iError = -1
                                    ElseIf $aRet[0] = Null Then
                                        $iError = -6
                                    Else
                                        $aResult[0] = DllStructGetData(DllStructCreate("char[15]", $aRet[0]), 1) ;IP address xored with Magic Cookie
                                    EndIf
                                Else ;IPv6
                                    $dNBOIP = BinaryMid($dResponse, 28 + $nByteNumber, 16)
                                    $dNBOConcatenation = BinaryMid($dResponse, 5, 16)

                                    $tNBOXoredIP = DllStructCreate("byte[16]")
                                    For $i = 1 To 16
                                        DllStructSetData($tNBOXoredIP, 1, BitXOR(BinaryMid($dNBOIP, $i, 1), BinaryMid($dNBOConcatenation, $i, 1)), $i)
                                    Next

                                    $tIP = DllStructCreate("char[46]")
                                    $aRet = DllCall($hWs2, "ptr", "inet_ntop", _
                                            "int", 23, "ptr", DllStructGetPtr($tNBOXoredIP), "ptr", DllStructGetPtr($tIP), "ULONG_PTR", DllStructGetSize($tIP))
                                    If @error Then
                                        $iError = -1
                                    ElseIf $aRet[0] = Null Then
                                        $iError = -6
                                    Else
                                        $aResult[0] = DllStructGetData($tIP, 1) ;IP address
                                    EndIf
                                EndIf
                            EndIf
                            ExitLoop 2
                        ElseIf $nAttributeType = 0x0001 Then ;MAPPED-ADDRESS
                            $aRet = DllCall($hWs2, "ushort", "ntohs", "ushort", BinaryMid($dResponse, 26 + $nByteNumber, 2)) ;Port
                            If @error Then
                                $iError = -1
                            Else
                                $aResult[1] = $aRet[0]
                            EndIf
                            If Not $iError Then
                                If $nFamily = 0x0001 Then ;IPv4
                                    $aRet = DllCall($hWs2, "ptr", "inet_ntoa", "ulong", BinaryMid($dResponse, 28 + $nByteNumber, 4))
                                    If @error Then
                                        $iError = -1
                                    ElseIf $aRet[0] = Null Then
                                        $iError = -6
                                    Else
                                        $aResult[0] = DllStructGetData(DllStructCreate("char[15]", $aRet[0]), 1) ;IP address
                                    EndIf
                                Else ;IPv6
                                    $tNBOIP = DllStructCreate("byte[16]")
                                    DllStructSetData($tNBOIP, 1, BinaryMid($dResponse, 28 + $nByteNumber, 16))
                                    $tIP = DllStructCreate("char[46]")
                                    $aRet = DllCall($hWs2, "ptr", "inet_ntop", _
                                            "int", 23, "ptr", DllStructGetPtr($tNBOIP), "ptr", DllStructGetPtr($tIP), "ULONG_PTR", DllStructGetSize($tIP))
                                    If @error Then
                                        $iError = -1
                                    ElseIf $aRet[0] = Null Then
                                        $iError = -6
                                    Else
                                        $aResult[0] = DllStructGetData($tIP, 1) ;IP address
                                    EndIf
                                EndIf
                            EndIf
                            ExitLoop 2
                        Else
                            $aRet = DllCall($hWs2, "ushort", "ntohs", "ushort", BinaryMid($dResponse, 22 + $nByteNumber, 2)) ;Attribute Lenght
                            If @error Then
                                $iError = -1
                            Else
                                $nAttributeLenght = $aRet[0]
                                If Not (Mod($nAttributeLenght, 4) = 0) Then $nAttributeLenght += 4 - Mod($nAttributeLenght, 4) ;padding
                                $nByteNumber += 4 + $nAttributeLenght ;TLV
                            EndIf
                        EndIf
                    EndIf
                Until $nByteNumber > $nMessageLenght
            Else ;0x0111 = Binding error response
                $iError = -6
            EndIf
        EndIf
    Next

    _UDPCloseSocket($nBindSocket)
    _UDPShutdown()
    DllClose($hWs2)
    If $iError Then $aResult = -1
    If $iStunServer And ($iStunServer <> $sServerName) Then $iExtended = -2
    Return SetError($iError, $iExtended, $aResult)
EndFunc   ;==>_STUN

 

Please test it (especially if you have an IPv6 interface) and report every bug you can try :)

 

STUN.zip

Edited by j0kky
3 people like this

Share this post


Link to post
Share on other sites



#2 ·  Posted (edited)

Haven't tested yet, but it looks like a solid piece of work, potentially quite useful, thanks.:)

RE. your winsock.au3, these functions may help make your WSA error reporting a bit more accessible (just a thought).

Edited by RTFC

Share this post


Link to post
Share on other sites
17 minutes ago, RTFC said:

Haven't tested yet, but it looks like a solid piece of work, potentially quite useful, thanks.:)

RE. your winsock.au3, these functions may help make your WSA error reporting a bit more accessible (just a thought).

Thank you for the feedback! 

Talking about WSA error reporting: winsock UDF is planned to be used in simple "If @error then" statement, so it needs to be outputted just an error code number. Anyway who use winsock UDF can freely associate that error code to an explanation using your functions ;)

Share this post


Link to post
Share on other sites

Isn' it essentially the same STUN protocol UDF found here :

 


This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.
Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe here
RegExp tutorial: enough to get started
PCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta.

SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.
SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.
An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.
SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)
A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!
SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt)

Share this post


Link to post
Share on other sites

#5 ·  Posted (edited)

4 hours ago, jchd said:

Isn' it essentially the same STUN protocol UDF found here :

 

Short story: No! That script just uses STUN protocol to retrieve public IPv4 IP.

Long story: thanks to the implementation of winsock UDF I can retrieve the external port too, which is necessary to UDP Hole Punching; furthermore my UDF can parse XOR-MAPPED-ADDRESS attribute (used by newest STUN servers) and it can retrieve also IPv6 address (trancexx script needs some other code lines to parse IPv6). Other minor advantages are the possibility of using a customized STUN server name and a strongest error handling.

Edited by j0kky

Share this post


Link to post
Share on other sites

Ah I see. Thanks for detail information.


This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.
Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe here
RegExp tutorial: enough to get started
PCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta.

SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.
SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.
An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.
SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)
A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!
SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now

  • Similar Content

    • Turtlix21
      TCP, UDP problem with receiving data
      By Turtlix21
      I've a problem with receiving data on my PC by TCP and UDP. I can send data to another computer, but can't receive it.
      I'm not shure where is the problem (on PC or on "another comupers")
      This is client code:
      ;TCP client TCPStartup() $socket = TCPConnect("192.168.0.18", 7777) ;try to connect to server and save number of socket If $socket = -1 Then ;if $socket = -1 then error MsgBox(16, "Error:", "Can't connect to server") EndIf $sendedBytes = TCPSend($socket, "nothing here :)") ;send message to connected socket If $sendedBytes = 0 Then ;if receiving data TCPSend(...) = 0 then error MsgBox(16, "Error", "Can't send message") EndIf TCPCloseSocket($socket) TCPShutdown() and server code:
      ;TCP server TCPStartup() $mainsocket = TCPListen("192.168.0.18", 7777) ;making main receiving socket While 1 ;receiving loop $acceptedSocket = TCPAccept($mainsocket) ;possible connection to accept If $acceptedSocket <> -1 Then $receivedData = TCPRecv($acceptedSocket, 1024) ;if main socket is connected then receive message MsgBox(64, "Received message!", "Message: " & $receivedData) TCPCloseSocket($acceptedSocket) ;close open connecion EndIf WEnd TCPShutdown()  
      When server is on PC I can't receive any messages from any other computers.
      When client is on PC I can receive messages on any another computer.
      I tried to turn off antiviruses and windows firewall but it did't change anything
       
      EDIT:
      Error code from client from TCPConnect is 10060 (connection time out) and from TCPSend: 10038
      I've found something about 10060 error code, Microsoft says "Connection timed out. A connection attempt failed because the connected party did not properly respond after a period of time, or the established connection failed because the connected host has failed to respond."
      So it means that the problem is with time of response but it makes no sense couse "another computer" is Virtual Machine with bridge internet connection from PC (pings beetwen PC  and VM are lower than 1ms.
      About 10038 error code microsoft says that this is problem with socket and it actually makes sense.
       
    • topten
      Parental Filter
      By topten
      Hi I have an idea of creating a parental filter (freeware) , which will be protecting children from navigating to adult websites.  I want to ask, what is your opinion, how to do it best way?
      How to catch the traffic from the very beginning?
      For example if a user is starting IE or Firefox, or if he has some alternate BLABLA-browser - how can I catch the url before actual navigation to website, so that my script could first check if the website is ok, and then if it decides that the website is not ok, just block connection to this IP.
       
      Looking forward to your opinions, Great thanx in advance!
    • AoRaToS
      s!mpL3 LAN Messenger
      By AoRaToS
      I started working on this program in the summer then I stopped cause I faced some problems I couldn't overcome back then. Now that I've practiced more and have become a better scripter/programmer I'm releasing the program to the public to get some opinions. I know it's not a new concept but it's the first program I started besides some small stuff I did just for practice! I won't post the source code yet because it's still under construction, although I'm sure I've posted early stages of the code with bugs in the past in some topic...
      What I wanted was a simple, small, serverless program that would work without installation cause I wanted it for where I work, so I ended up with this!
       
      I have attached some images of various versions, also visit the forum thread.
       
      The package includes s!mpL3 LAN Messenger, the License Agreement and the change log.
       
      Current version 2.9.8.8! [30/09/2015]
       
      Check the Change Log below!
       
       
      http://www.autoitscript.com/forum/index.php?showtopic=88782
       
       
       
      Read the license before using this software.
       
    • shaggy89
      UDP send to all online users [revoked]
      By shaggy89
      Hi all,
      Im hoping some could help me out.
      I'm using an IM client that sends messages via UDP.
      I was wondering is there a way I could make a button with a pre canned message to send to all online users automatically E.g "about to get coffee place your oder" send to all online
      Saves sending to one person at a time.
       
      Cheers
      Shaggy
       
      Edit ::::
       
      After looking into it more,  there are scripts on the forum that do the job i need all i have to do edit them to my needs
       
      Thanks anyways
       
    • Graeme
      UDP function not working with new AutoIt
      By Graeme
      Hi,
      Recently I upgraded my AutoIt and since then this script has caused problems.
      Func MonitorOpenVPNTunnel ($UDPLogReceiver) If $UDPLogReceiver[0] <> 0 Then $received = UDPRecv($UDPLogReceiver,16000) If @error And (@error <> 10040) Then InternalErrorMsgBox("UDPRecv",@error) Could any experienced user of UDP tell me if there is some change in the UDPRecv function that could explain an error coming up as soon as I run the compiled program.
      This is the code that makes the $UDPLogReceiver
      $UDPLogPort = Random(20000,65535,1) $UDPLogReceiver = UDPBind("127.0.0.1",$UDPLogPort) Could there be something new in UDPBind that would cause a problem?
      Any fresh inspiration very welcome.
      Blessings
      Graeme