Jump to content

Recommended Posts

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.

Expand  

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
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
Posted
  On 11/1/2016 at 12:51 PM, 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).

Expand  

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 ;)

Posted

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

 

  Reveal hidden contents

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)

Posted (edited)
  On 11/1/2016 at 6:15 PM, jchd said:

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

 

Expand  

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
Posted

Ah I see. Thanks for detail information.

  Reveal hidden contents

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)

  • 3 months later...
  • 1 year later...
Posted

Signature beginning:
Please remember: "AutoIt"..... *  Wondering who uses AutoIt and what it can be used for ? * Forum Rules *
ADO.au3 UDF * POP3.au3 UDF * XML.au3 UDF * IE on Windows 11 * How to ask ChatGPT for AutoIt Codefor other useful stuff click the following button:

  Reveal hidden contents

Signature last update: 2023-04-24

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
×
×
  • Create New...