Что нового

[Сеть, интернет] Автоматизация работы с telnet

Redline

AutoIT Гуру
Сообщения
506
Репутация
375
Связка из двух функций, для автоматизации telnet-соединений, все построено на принципе ожидания определенной строки/символа.
Первая - это сценарий для работы с устройством (может меняться в зависимости от нужд и типа хоста)
Вторая - непосредственно работа с telnet
Функции не универсальны, при необходимости их можно переделать под конкретные условия.
Код:
$sIp = '123.123.123.123'	; IP-адрес хоста
$sLogin = 'user'			; логин
$sPass = 'passwd'			; пароль
$sForWait = ''
$sOut = ''

$sOutput = _scenario($sIp, $sLogin, $sPass)
Switch @error
	Case 0
		MsgBox(0, 'Done', 'Сценарий выполнен успешно' & @CRLF & 'листинг работы в консоли')
		ConsoleWrite($sOutput & @CRLF)
	Case 1
		MsgBox(16, 'Error', 'Ошибка службы TCP')
	Case 2
		MsgBox(16, 'Error', 'Ошибка при создании соединения')
	Case 3
		MsgBox(16, 'Error', 'Устройство не отвечает')
	Case 4
		MsgBox(16, 'Error', 'Вышло время при ожидании данной строки: ' & $sForWait & @CRLF & 'Листинг при ожидании:' & @CRLF & $sOut)
EndSwitch

Func _scenario($sInIp, $sInLogin, $sInPass)
	TCPStartup()
	If @error Then Return SetError(1)				; ошибка службы
	$socket = TCPConnect($sInIp, 23)				; создаем сокет с хостом по 23 порту
	If @error Then
		TCPCloseSocket($socket)
		TCPShutdown()
		Return SetError(2)							; ошибка при создании сокета
	EndIf
	$sForWait = ':'
	$sStream = _StartListener($socket, $sForWait)	; ждем приглашение ':' для ввода логина
	If @error = 1 Then
		TCPCloseSocket($socket)
		TCPShutdown()
		Return SetError(3)							; хост не отвечает
	ElseIf @error = 2 Then
		TCPCloseSocket($socket)
		TCPShutdown()
		Return SetError(4)							; вышло время ожидания строки от хоста
	EndIf
	TCPSend($socket, $sInLogin & @CRLF)				; отсылаем логин
	$sForWait = ':'
	$sStream = _StartListener($socket, $sForWait)	; ждем приглашение ':' для ввода пароля
	If @error = 1 Then
		TCPCloseSocket($socket)
		TCPShutdown()
		Return SetError(3)							; хост не отвечает
	ElseIf @error = 2 Then
		TCPCloseSocket($socket)
		TCPShutdown()
		Return SetError(4)							; вышло время ожидания строки от хоста
	EndIf
	TCPSend($socket, $sInPass & @CRLF)				; отсылаем пароль
	$sForWait = '>'
	$sStream = _StartListener($socket, $sForWait)	; ждем '>' для ввода команд
	If @error = 1 Then
		TCPCloseSocket($socket)
		TCPShutdown()
		Return SetError(3)							; хост не отвечает
	ElseIf @error = 2 Then
		TCPCloseSocket($socket)
		TCPShutdown()
		Return SetError(4)							; вышло время ожидания строки от хоста
	EndIf
	TCPSend($socket, 'command' & @CRLF)				; отсылаем команду
	$sForWait = '>'
	$sStream = _StartListener($socket, $sForWait)	; ждем '>' после окончания выполнения команды
	If @error = 1 Then
		TCPCloseSocket($socket)
		TCPShutdown()
		Return SetError(3)							; хост не отвечает
	ElseIf @error = 2 Then
		TCPCloseSocket($socket)
		TCPShutdown()
		Return SetError(4)							; вышло время ожидания строки от хоста
	EndIf
	TCPCloseSocket($socket)
	TCPShutdown()
	Return $sStream									; выдача листинга
EndFunc

Func _StartListener($sock, $sWait, $iTimer = 30000)
	$iTimer = Int($iTimer)												; перевод времени таймера из строчнойй формы в цифровую
	$sOut = ''
	$hTimer = TimerInit()												; инициирование таймера, для ограничения времени ожидания ответов
	While 1
		$sRcv = TCPRecv($sock, 10000)									; чтение данных с порта
		If StringLen($sRcv) <> 0 And StringInStr($sRcv, $sWait) Then	; если пришли данные, и они содержат искомую строку - выход с выводом
			$sOut = $sRcv
			Return $sOut
		ElseIf StringLen($sRcv) <> 0 Then								; если пришли данные, но они не содержат искомую строку - ждем
			$hTimer = TimerInit()										; заново инициируем таймер уже для ожидания нужной строки
			$sOut = $sRcv
			If StringInStr($sRcv, '--More--') Then						; пришла строка "--More--" (при многостраничной выдаче)
				TCPSend($sock, ' ')										; отсылаем пробел для "пролистывания"
				$hTimer = TimerInit()									; заново инициируем таймер
			EndIf
			Do															; чтения данных с порта в цикле, до прихода нужной строки
				$sRcv = TCPRecv($sock, 10000)
				If StringLen($sRcv) <> 0 Then
					If StringInStr($sRcv, '--More--') Then				; пришла строка "--More--" (при многостраничной выдаче)
						TCPSend($sock, ' ')								; отсылаем пробел для "пролистывания"
						$hTimer = TimerInit()							; заново инициируем таймер
					EndIf
					$sOut &= $sRcv
				EndIf
				Sleep(5)
				If TimerDiff($hTimer) > $iTimer Then Return SetError(2)	; время ожидания строки вышло - выход с ошибкой
			Until StringInStr($sRcv, $sWait)
			Return $sOut												; нормальный выход с выводом
		EndIf
		Sleep(50)
		If TimerDiff($hTimer) > $iTimer Then Return SetError(1)			; время ожидания какого-либо ответа от хоста вышло - выход с ошибкой
	WEnd
EndFunc
 

sind00

Новичок
Сообщения
9
Репутация
0
Спасибо за скрипт. Все вроде бы хорошо, спокойно работаю со своим роутером через telnet и еще некоторыми железками, кое-что под себе подправил, проблем не ощущал.

Но, есть одна уж больно хитрая железка, при попытке залезть на нее вместо заветных "login:", TCPRecv получает "яэяэ яэ#яэ'яэ$", при этом через telnet.exe, putty.exe - прекрасно работает. В чем может быть дело?
 

joiner

Модератор
Локальный модератор
Сообщения
3,557
Репутация
628
sind00
это в листинге такие кракозябры?
можно использовать _WinAPI_OemToChar из Winapiex.au3
 

sind00

Новичок
Сообщения
9
Репутация
0
Да это в листинге. Подключил Winapiex.au3, функция _WinAPI_OemToChar. Теперь результат немножко другой " ¤ ¤ ¤# ¤' ¤$".
 
Автор
Redline

Redline

AutoIT Гуру
Сообщения
506
Репутация
375
Мне что-то подобное в первой строке шлет cisco "яыяыяэяэ", но следующая строка идет уже с приглашением ввода логина. Я так понимаю это строка инициализации соединения или подтверждения, в общем это служебная строка для создания сеанса.
Попробуйте после получения этой строки послать в ответ пустую строку или пробел. Еще можно поставить WireShark и посмотреть какими пакетами обменивается putty и сравнить со скриптом, может это как-то поможет.

Ясно что скрипт не может заменить полноценный терминал.
Не раз писал, что для автоматизации telnet/ssh и прочего лучше всего использовать SecureCRT, т.к. в нем есть возможность написания скриптов на perl/VBS.
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
почти OffTopic:
Пацаны, а вы в детстве в MUDы играли?
Возмите любого клиента, тот же самый zMUD, например, и посмотрите.
Не perl/VBS, конечно, но весьма.
 

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
Подскажите, подключаюсь к модему, а в ответе получаю много мусора и служебных символов с кодами от 0 до 31. (на форуме их не отобразишь). Как лучше всего почистить это?
Код:
78яыGettingStarted1.GeneralSetup3.LANSetup4.InternetAccessSetupAdvancedApplications11.RemoteNodeSetup12.StaticRoutingSetup15.NATSetupAdvancedManagement21.FilterSetConfiguration22.SNMPConfiguration23.SystemPassword24.SystemMaintenance25.IPRoutingPolicySetup26.ScheduleSetup99.ExitEnterMenuSelectionNumber:Copyright(c)1994-2003ZyXELCommunicationsCorp.Prestige650R-E1MainMenu


Добавлено:
Сообщение автоматически объединено:

Вот что в консоле


Добавлено:
Сообщение автоматически объединено:

Вот как это видно в терминале
 

sind00

Новичок
Сообщения
9
Репутация
0
Redline, спасибо, что отозвались.

Проанализировал данные Wireshark.
Следующая картина при подключении через стандартный cmd, telnet.exe windows:

--------- handshake tcp start ---------

PC ----> TCP SYN ----> Router

PC <---- TCP ACK+SYN <----- Router

PC -----> TCP ACK ------> Router

--------- handshake tcp end ---------

--------- telnet initiation start ------------

PC <------ ACK+Push (Do Terminal Type, Do Terminal Speed, Do X Display Location....) <------- Router

PC ------> ACK+Push (Will Terminal Type, Will Negotiate About Window Size) ------> Router

... ну еще куча пакетов согласования....

Так, как наш скрипт не является протоколом сеансового уровня telnet, то он не понимает, что от него хочет роутер, когда отправляет эму ACK+Push (Do Terminal Type, Do Terminal Speed, Do X Display Location....). Поэтому на этом пакете весь процесс останавливается, скрипт в тупую ждет "login:" а на все запросы касательно инициализации telnet сессии эму пофиг.

Собственно говоря вопрос, можно ли каким то образом заслать в сокет пакет соответствующей структуры? Может быть есть готовые функции по работе с telnet?

ПС. Отсылание пробела, ентера не помогает.
 
Автор
Redline

Redline

AutoIT Гуру
Сообщения
506
Репутация
375
sind00
Можно еще тупо слать все в окно telnet, если нужно сделать интерактивную автоматизацию(переход по условиям), то придется считывать данные из консольного окна, о том как это делается на форуме есть примеры.
Код:
Run('cmd.exe')
Sleep(1000)
Send('telnet 127.0.0.1{Enter}')
Sleep(1000)
Send('user{Enter}')
Sleep(1000)
Send('pass{Enter}')
Sleep(1000)
Send('quit{Enter}')

И все таки советую посмотреть в сторону SecureCRT, там все довольно просто, вот примерный листинг:
Код:
...
objTab.Session.Connect("/telnet 127.0.0.1")
objTab.Screen.WaitForString "Login:"
objTab.Screen.Send "user" & vbcr
objTab.Screen.WaitForString "Password:"
objTab.Screen.Send "pass" & vbcr
objTab.Screen.WaitForString ">"
objTab.Screen.Send "command" & vbcr
...
 

sind00

Новичок
Сообщения
9
Репутация
0
Redline
Уже подумывал о варианте посылать в окно с telnet. Если других вариантов не найдется, наверное так и сделаю.
SecureCRT конечно же пользуюсь, но вот VBS, perl к сожалению не знаю. + суть моего скрипта это GUI с табами, на которых по 10 окон edit, при заполнении которых генерируется шаблон конфигурации и отправляется на оборудование. На autoit только описание GUI со всеми проверками edit регулярными выражениями и циклами, комментариями и объяснениями заняло 300 строк... :stars:
 

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
Redline

В примере к SecureCRT обращение идет к COM объектам? или это синтаксис языка?

Если это COM то вы sind00 сможете сделать тоже самое и на Autoit
 
Автор
Redline

Redline

AutoIT Гуру
Сообщения
506
Репутация
375
inververs
К сожалению внешнего входа в COM нет. А идея была отличная!

sind00
Можно создать файл конфигурации с помощью AutoIt, им же запускать SecureCRT с параметрами (тут pdf по написанию скриптов, раздел "Passing Arguments to Scripts"), в параметрах передать файл скрипта.vbs, файл полученной конфигурации, ip устройства, имя/пароль. Правда если конфигурация создается "на лету" и зависит от конкретного ответа устройства, то так просто не получится.

Если интересно, то могу помочь со скриптами для CRT, есть нормально работающие, чуть переделать под вашу задачу и все. Но только в личку, т.к. это выходит за рамки темы и этого форума.
 

vlad55_03

Новичок
Сообщения
1
Репутация
0
Связка из двух функций, для автоматизации telnet-соединений, все построено на принципе ожидания определенной строки/символа.
Первая - это сценарий для работы с устройством (может меняться в зависимости от нужд и типа хоста)
Вторая - непосредственно работа с telnet

Вот что-то подобное ищу под свою задачу...
Пробую Ваш скрипт... Но у меня php7 и соответственно выкатывает почти на каждой строке ошибку.
Сейчас изучаю ошибку на строке 24 Вашего кода:
Код:
 If @error Then Return SetError(1)                ;

Ошибка такая выводится:
Код:
PHP Parse error:  syntax error, unexpected '@', expecting '(' in /usr/local/www/test.php on line 76

Буду благодарен за ответ и подсказку.
 

alexnasa

Новичок
Сообщения
50
Репутация
0
Спасибо, дорогой автор! Я добавил чуть строчек и Cisco теперь ОК любые задачи CLI выполняет!

Например перезагружает POE порт и камеру в нём (Циско WS-C3560-48PS)
Код:
Return SetError(4)                          ; вышло время ожидания строки от хоста
    EndIf
    TCPSend($socket, 'enable' & @CRLF)             ; отсылаем команду
      Sleep(500)
       TCPSend($socket, 'cisco123' & @CRLF)             ; отсылаем команду
      Sleep(500)
      TCPSend($socket, 'conf t' & @CRLF)          ; отсылаем команду
      Sleep(500)     
        TCPSend($socket, 'interface fastEthernet 0/3' & @CRLF)             ; отсылаем команду
        Sleep(500)
      TCPSend($socket, 'shutdown' & @CRLF)          ; отсылаем команду
      Sleep(9000)
      TCPSend($socket, 'no shutdown' & @CRLF)          ; отсылаем команду
      Sleep(500)
      TCPSend($socket, 'exit' & @CRLF)             ; отсылаем команду
        Sleep(500)
      TCPSend($socket, 'exit' & @CRLF)          ; отсылаем команду
      Sleep(5000)
      TCPSend($socket, 'exit' & @CRLF)          ; отсылаем команду
    $sForWait = '>'
    $sStream = _StartListener($socket, $sForWait)   ; ждем '>' после окончания выполнения команды
 
Последнее редактирование:
Верх