Что нового

[Сеть, интернет] Получение внешнего IP-адреса по протоколу STUN

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
AutoIt: 3.3.6.1+
Версия: 1.0

Категория: Интернет

Описание: Про протокол STUN можно прочитать здесь: Способы обхода NAT. Метод STUN. Описание протокола и здесь: RFC5389.

Долгое время для определения внешнего IP-адреса пользовался функцией из этой темы Your public IP (STUN protocol). Почитав про протокол STUN, решил модифицировать функцию trancexx`а, добавив Magic Cookie, что дает возможность получать и обрабатывать ответ не только с атрибутом MAPPED-ADDRESS, но и с XOR-MAPPED-ADDRESS. В итоге получилась практически новая функция.

Код/Пример:
Код:
Opt('MustDeclareVars', 1)

Global $sIP, $iTimer = TimerInit()

$sIP = _STUN_GetMyIP()
ConsoleWrite(@error & @TAB & $sIP & @TAB & TimerDiff($iTimer) & @LF)

; #FUNCTION# ====================================================================================================
; Name...........: _STUN_GetMyIP
; Description....: Получает внешний IP-адрес компьютера по протоколу STUN.
; Syntax.........: _STUN_GetMyIP()
; Parameters.....: Нет параметров.
; Return values..: Success - внешний IP-адрес компьютора.
;                  Failure - пустая строка и флаг @error:
;                            1 - ошибка UDPStartup();
;                            2 - ни один из 15 STUN-серверов не вернул корректного ответа.
; Author.........: madmasles, первоначальный вариант: trancexx (http://www.autoitscript.com/forum/topic/161713-your-public-ip-stun-protocol/)
; Remarks........: Функция работает на много быстрее, чем штатная _GetIP().
;                  Информация о протоколе STUN:
;                  http://svitter.info/?p=442
;                  http://tools.ietf.org/html/rfc5389
; Link...........: http://autoit-script.ru/index.php?topic=19340.0
; Example........: Есть
; ===============================================================================================================
Func _STUN_GetMyIP()
;~ 	http://svitter.info/?p=442		read about STUN
	Local Const $STUN_BINDING_RESPONSE = 257, $STUN_FAMILY_IPV4 = 1, $STUN_MAPPED_ADDRESS = 256, $STUN_XOR_MAPPED_ADDRESS = 8192, _
			$a_Data[5][2] = [[28],[33, '.'],[18, '.'],[164, '.'],[66]]
;~ 			Hex Magic Cookie 21 12  A4 42
;~ 			Dec Magic Cookie 33 18 164 66
	Local $a_Server[17][2] = [[16],['stun.voxgratia.org', 3478],['stun.l.google.com', 19302],['stun1.l.google.com', 19302], _
			['stun2.l.google.com', 19302],['stun3.l.google.com', 19302],['stun4.l.google.com', 19302],['stun.ideasip.com', 3478], _
			['stun.iptel.org', 3478],['stun.rixtelecom.se', 3478],['stun.schlund.de', 3478],['stun.voiparound.com', 3478], _
			['stun.voipbuster.com', 3478],['stun.voipstunt.com', 3478],['stun.ekiga.net', 3478],['stun.sipgate.net', 10000], _
			['stun.voxgratia.org', 3478]], $a_Socket[3], $i_Timer, $b_Recv, $i_XOR, $s_MyIP

	UDPStartup()
	If @error Then Return SetError(1, 0, '')
	For $i = 1 To $a_Server[0][0]
		If $a_Socket[0] Then UDPCloseSocket($a_Socket)
		$a_Server[$i][0] = TCPNameToIP($a_Server[$i][0])
		If @error Then ContinueLoop
		$a_Socket = UDPOpen($a_Server[$i][0], $a_Server[$i][1])
		If Not $a_Socket[0] Then ContinueLoop
;~ 		header: 0001(Binding Request) 0000(Length) 2112A442(Magic Cookie) 12 byte random ID (All header size = 20 byte)
		If UDPSend($a_Socket, Binary('0x000100002112A442') & BinaryMid(Binary(Random()), 3, 6) & BinaryMid(Binary(Random()), 3, 6)) <> 20 Then ContinueLoop
		If $i = 1 Then ContinueLoop ; never working first server, I don't know why?
		$i_Timer = TimerInit()
		Do
			$b_Recv = UDPRecv($a_Socket, 88, 1)
			If @error Then ExitLoop
			If $b_Recv Then ExitLoop
			Sleep(10)
		Until TimerDiff($i_Timer) > 1000
		If (Not $b_Recv) Or (BinaryLen($b_Recv) < 32) Then ContinueLoop
		If BinaryMid($b_Recv, 1, 2) <> $STUN_BINDING_RESPONSE Then ContinueLoop
		If BinaryMid($b_Recv, 26, 1) <> $STUN_FAMILY_IPV4 Then ContinueLoop
		Switch BinaryMid($b_Recv, 21, 2);attribute
			Case $STUN_MAPPED_ADDRESS
				$i_XOR = 0
			Case $STUN_XOR_MAPPED_ADDRESS
				$i_XOR = 1
			Case Else
				$b_Recv = 0
				ContinueLoop
		EndSwitch
		For $j = 1 To 4
			If $i_XOR Then
				$s_MyIP &= BitXOR(BinaryMid($b_Recv, $a_Data[0][0] + $j, 1), $a_Data[$j][0]) & $a_Data[$j][1]
			Else
				$s_MyIP &= Int(BinaryMid($b_Recv, $a_Data[0][0] + $j, 1)) & $a_Data[$j][1]
			EndIf
		Next
		ExitLoop
	Next
	If $a_Socket[0] Then UDPCloseSocket($a_Socket)
	UDPShutdown()
	If $s_MyIP Then Return $s_MyIP
	Return SetError(2, 0, '')
EndFunc   ;==>_STUN_GetMyIP


Источник: autoit-script.ru
Автор: madmasles
 
Автор
madmasles

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
Вариант с другим способом получения IP-адреса из ответа.
Код:
Opt('MustDeclareVars', 1)

Global $sIP, $iTimer = TimerInit()

$sIP = _STUN_GetMyIP_Ex()
ConsoleWrite(@error & @TAB & $sIP & @TAB & TimerDiff($iTimer) & @LF)

; #FUNCTION# ====================================================================================================
; Name...........: _STUN_GetMyIP_Ex
; Description....: Получает внешний IP-адрес компьютора по протоколу STUN.
; Syntax.........: _STUN_GetMyIP_Ex()
; Parameters.....: Нет параметров.
; Return values..: Success - внешний IP-адрес компьютора.
;                  Failure - пустая строка и флаг @error:
;                            1 - ошибка UDPStartup();
;                            2 - ни один из 15 STUN-серверов не вернул корректного ответа.
; Author.........: madmasles, первоначальный вариант: trancexx (http://www.autoitscript.com/forum/topic/161713-your-public-ip-stun-protocol/)
; Remarks........: Функция работает на много быстрее, чем штатная _GetIP().
;                  Информация о протоколе STUN:
;                  http://svitter.info/?p=442
;                  http://tools.ietf.org/html/rfc5389
; Link...........:
; Example........: Есть
; ===============================================================================================================
Func _STUN_GetMyIP_Ex()
	Local Const $STUN_BINDING_RESPONSE = 257, $STUN_FAMILY_IPV4 = 1, $STUN_MAPPED_ADDRESS = 256, $STUN_XOR_MAPPED_ADDRESS = 8192, _
			$STUN_MAGIC_COOKIE = 1118048801 ;Magic Cookie 0x42A41221 local

	Local $a_Server[17][2] = [[16],['stun.voxgratia.org', 3478],['stun.l.google.com', 19302],['stun1.l.google.com', 19302], _
			['stun2.l.google.com', 19302],['stun3.l.google.com', 19302],['stun4.l.google.com', 19302],['stun.ideasip.com', 3478], _
			['stun.iptel.org', 3478],['stun.rixtelecom.se', 3478],['stun.schlund.de', 3478],['stun.voiparound.com', 3478], _
			['stun.voipbuster.com', 3478],['stun.voipstunt.com', 3478],['stun.ekiga.net', 3478],['stun.sipgate.net', 10000], _
			['stun.voxgratia.org', 3478]], $a_Socket[3], $i_Timer, $b_Recv, $b_IP, $a_Res, $s_MyIP

	UDPStartup()
	If @error Then Return SetError(1, 0, '')
	For $i = 1 To $a_Server[0][0]
		If $a_Socket[0] Then UDPCloseSocket($a_Socket)
		$a_Server[$i][0] = TCPNameToIP($a_Server[$i][0])
		If @error Then ContinueLoop
		$a_Socket = UDPOpen($a_Server[$i][0], $a_Server[$i][1])
		If Not $a_Socket[0] Then ContinueLoop
		If UDPSend($a_Socket, Binary('0x000100002112A442') & BinaryMid(Binary(Random()), 3, 6) & BinaryMid(Binary(Random()), 3, 6)) <> 20 Then ContinueLoop
		If $i = 1 Then ContinueLoop ; never working first server, I don't know why?
		$i_Timer = TimerInit()
		Do
			$b_Recv = UDPRecv($a_Socket, 88, 1)
			If @error Then ExitLoop
			If $b_Recv Then ExitLoop
			Sleep(10)
		Until TimerDiff($i_Timer) > 1000
		If (Not $b_Recv) Or (BinaryLen($b_Recv) < 32) Then ContinueLoop
		If BinaryMid($b_Recv, 1, 2) <> $STUN_BINDING_RESPONSE Then ContinueLoop
		If BinaryMid($b_Recv, 26, 1) <> $STUN_FAMILY_IPV4 Then ContinueLoop
		Switch BinaryMid($b_Recv, 21, 2);attribute
			Case $STUN_MAPPED_ADDRESS
				$b_IP = BinaryMid($b_Recv, 29, 4)
			Case $STUN_XOR_MAPPED_ADDRESS
				$b_IP = BitXOR(BinaryMid($b_Recv, 29, 4), $STUN_MAGIC_COOKIE)
			Case Else
				$b_Recv = 0
				ContinueLoop
		EndSwitch
		$a_Res = DllCall('Ws2_32.dll', 'str', 'inet_ntoa', 'ulong', $b_IP)
		If (@error) Or ($a_Res[0] = '0.0.0.0') Then ContinueLoop
		$s_MyIP = $a_Res[0]
		ExitLoop
	Next
	If $a_Socket[0] Then UDPCloseSocket($a_Socket)
	UDPShutdown()
	If $s_MyIP Then Return $s_MyIP
	Return SetError(2, 0, '')
EndFunc   ;==>_STUN_GetMyIP_Ex
 
Верх