telnet で繋ぐと、サーバ側から問い合わせがあります。


telnet 接続時の最初の通信

[IAC][DO][TELOPT_TTYPE]
[IAC][DO][TELOPT_SPEED]
[IAC][DO][TELOPT_XDISPLOC]
[IAC][DO][TELOPT_NEW_ENVIRON]


これらの質問は、1パケットで送られてくるので、一気に応答する。
うん、TELOPT_SPEED 以外は答えるね!


→[IAC][WILL][TELOPT_TTYPE]
→[IAC][WONT][TELOPT_SPEED]
→[IAC][WILL][TELOPT_XDISPLOC]
→[IAC][WILL][TELOPT_NEW_ENVIRON]


…問い合わせに応えると…こっからあとも1パケットで再質問がきます。


[IAC][SB][TELOPT_XDISPLOC][001][IAC][SE]
[IAC][SB][TELOPT_NEW_ENVIRON][001][IAC][SE]
[IAC][SB][TELOPT_TTYPE][001][IAC][SE]


各質問を解析して、端末タイプは、vt100 と応える。
といいつつ、TELOPT_TTYPE にだけ vt100 とまともな回答を送る。


→[IAC][SB][TELOPT_DISPLOC][0,0,0,0][IAC][SE]
→[IAC][SB][TELOPT_NEW_ENVIRON][TELQUAL_IS][IAC][SE]
→[IAC][SB][TELOPT_TTYPE][vt100][IAC][SE]


そのあと、1パケットでいろいろ聞いてくるけど、それには君の言うとおりでいいよと答える。
つまり、[IAC][DO]...で届くのに[DO]を[WONT]に変えて、そのまま返信するだけです。
いいかげんなように見えて、それでいいからもう聞くな!という応答なので、これでネゴは終了。
3回目のやりとりで、端末の設定関係を終了するように持っていっています。


そうすると、通常の端末入力のやりとりになるので、オートパイロットの開始です。
最初に TTY_ECHO が来るのを、そのまま表示しているので3バイトは文字化けしてますが気にしないで(笑)
という、簡単なロジックで動作しています。
詳細は、ソースをみてね。


前に公開したのが、1回目のログインに無理失敗させて逃げるという仕様だったのが気になっていて、ちゃんと1回目のログインで成功するよう修正しました。





## telnet-robot.ps1
##
## telnet プロトコルでサーバに接続してコマンドを実行する。
##
## 引数: remoteHost ホスト名
## port 接続ポート
## cmdfile リモートホストで実行するコマンドリスト
## logfile 実行ログを保存するファイル名
## user ユーザ名
## passwd パスワード
param(
[string] $remoteHost = "ukikusa",
[int] $port = 23,
[string] $cmdfile = "c:\users\kotora\com.txt",
[string] $logfile = "c:\users\kotora\Temp.txt",
[string] $user = "user",
[string] $passwd = "password"
)

# Start Logging
###############################################################################
Start-Transcript -Path "$logfile"

# Main Loop
###############################################################################
try {
## ホストに TCP で接続
write-host "Connecting to $remoteHost on port $port`r`n"
$socket = new-object System.Net.Sockets.TcpClient($remoteHost, $port)
if($socket -eq $null) {
throw ("Could Not Connect")
}
write-host "Connected $remoteHost on port $port`r`n"

$stream = $socket.GetStream()
$writer = new-object System.IO.StreamWriter($stream)

$buffer = new-object System.Byte[] 1024
$nego = new-object System.Byte[] 1024
$encoding = new-object System.Text.UTF8Encoding

# Telnet Negotiation
$IAC = 255 # interpret as command:
$DONT = 254 # you are not to use option
$DO = 253 # please, you use option
$WONT = 252 # I won't use option
$WILL = 251 # I will use option
$SB = 250 # interpret as subnegotiation
$GA = 249 # you may reverse the line
$EL = 248 # erase the current line
$EC = 247 # erase the current character
$AYT = 246 # are you there
$AO = 245 # abort output--but let prog finish
$IP = 244 # interrupt process--permanently
$BREAK= 243 # break
$DM = 242 # data mark--for connect. cleaning
$NOP = 241 # nop
$SE = 240 # end of record (transparent mode)
$EOR = 239 # end of record (transparent mode)
$ABORT= 238 # Abort process
$SUSP = 237 # Suspend process
$xEOF = 236 # End of file: EOF is already used...

$TELOPT_BINARY = 0
$TELOPT_ECHO = 1
$TELOPT_SGA = 3
$TELOPT_NAWS = 4
$TELOPT_STATUS = 5
$TELOPT_NAOCRD = 10
$TELOPT_NAOFFD = 13
$TELOPT_TTYPE = 24
$TELOPT_TSPEED = 32
$TELOPT_XDISPLOC = 35
$TELOPT_NEW_ENVIRON = 39

$TELQUAL_IS = 0
$TELQUAL_SEND = 1
$TELQUAL_INFO = 2
$TELQUAL_REPLY = 2
$TELQUAL_NAME = 3

$CR = "`r`n"
$debug = 0
$retry = 1000
$input_wait = 1000

# Exsec comand list read
###########################################################################
$commands = Get-Content $cmdfile;

##
## Telnet Negitiation

# STAGE1:サーバにサブコマンドの受付を要求
###########################################################################
$rep = 0
for($r=0; $r -lt $retry; $r++) {
start-sleep -m 500
if ($stream.DataAvailable) {
$read = $stream.Read($buffer, 0, 1024)
if($buffer[0] -eq $IAC) {
if($debug -eq 1) {
write-host Recieve1: $CR
for($i=0; $i -lt $read; $i++) {
write-host $buffer[$i]
}
write-host Reply1: $CR
}
for($c=0; $buffer[$c] -ne 0; $c+=3) {
if($buffer[$c+1] -eq $DO) {
if($buffer[$c+2] -eq $TELOPT_TSPEED) {
$nego = $IAC,$WONT,$TELOPT_TSPEED
$stream.Write($nego, 0, $nego.Length)
} else {
$nego = $IAC,$WILL,$buffer[$c+2]
$stream.Write($nego, 0, $nego.Length)
}
$stream.Flush()
if($debug -eq 1) {
write-host $nego $CR
}
}
}
}
$r = $retry
$rep = 1
}
}
if($rep -eq 0) {
throw ("No response negoation`r`n")
}
# STAGE2:サブコマンドでこちらの端末設定を適当に送信
###########################################################################
$rep = 0
for($r=0; $r -lt $retry; $r++) {
start-sleep -m 500
if($stream.DataAvailable) {
$read = $stream.Read($buffer, 0, 1024)
if($buffer[0] -eq $IAC) {
if($debug -eq 1) {
write-host Recieve2: $CR
for($i=0; $i -lt $read; $i++) {
write-host $buffer[$i]
}
write-host Reply2: $CR
}
for($c=0; $buffer[$c] -ne 0; $c+=6) {
if($buffer[$c+1] -eq $SB) {
switch($buffer[$c+2]) {
$TELOPT_XDISPLOC {
# XDisplay じゃないので適当に設定
$nego = $IAC,$SB,$TELOPT_XDISPLOC,0,0,0,0,$IAC,$SE
$stream.Write($nego, 0, $nego.Length)
}
$TELOPT_NEW_ENVIRON {
$nego = $IAC,$SB,$TELOPT_NEW_ENVIRON,$TELQUAL_IS,$IAC,$SE
$stream.Write($nego, 0, $nego.Length)
}
$TELOPT_TTYPE {
# ターミナルタイプを vt100 に設定
$nego = $IAC,$SB,$TELOPT_TTYPE,00,118,116,49,48,48,$IAC,$SE
$stream.Write($nego, 0, $nego.Length)
}
}
}
$stream.Flush()
if($debug -eq 1) {
write-host $nego $CR
}
}
}
$r = $retry
$rep = 1
}
}
if($rep -eq 0) {
throw ("No response negoation`r`n")
}
# STAGE3:他の問い合わせに、全部問い合わせなくていいと回答
###########################################################################
$rep = 0
for($r=0; $r -lt $retry; $r++) {
start-sleep -m 500
if($stream.DataAvailable) {
$read = $stream.Read($buffer, 0, 1024)
if($buffer[0] -eq $IAC) {
if($debug -eq 1) {
write-host Recieve3: $CR
for($i=0; $i -lt $read; $i++) {
write-host $buffer[$i]
}
write-host Reply3: $CR
}
for($c=0; $buffer[$c] -ne 0; $c+=3) {
if($buffer[$c+1] -eq $DO) {
$nego = $IAC,$WONT,$buffer[$c+2]
$stream.Write($nego, 0, $nego.Length)
$stream.Flush()
if($debug -eq 1) {
write-host $nego $CR
}
}
}
}
$r = $retry
$rep = 1
}
}
if($rep -eq 0) {
throw ("No response negoation`r`n")
}
# ログインプロンプトにユーザとパスワードを送信
###########################################################################
$rep = 0
for($r=0; $r -lt $retry; $r++) {
start-sleep -m 500
if ($stream.DataAvailable) {
while($stream.DataAvailable) {
$read = $stream.Read($buffer, 0, 1024)
write-host -n ($encoding.GetString($buffer, 0, $read))
}
start-sleep -m $input_wait
$writer.WriteLine($user)
$writer.Flush()
$r = $retry
$rep = 1
}
}
if($rep -eq 0) {
throw ("No response negoation`r`n")
}
$rep = 0
for($r=0; $r -lt $retry; $r++) {
start-sleep -m 500
if ($stream.DataAvailable) {
while($stream.DataAvailable) {
$read = $stream.Read($buffer, 0, 1024)
write-host -n ($encoding.GetString($buffer, 0, $read))
}
start-sleep -m $input_wait
$writer.WriteLine($passwd)
$writer.Flush()
$r = $retry
$rep = 1
}
}
if($rep -eq 0) {
throw ("No response negoation`r`n")
}
# ここから実行したいコマンドを送信
###########################################################################
$rep = 0
for($i=0; $i -le $commands.Count; $i++) {
for($r=0; $r -lt $retry; $r++) {
start-sleep -m 500
if ($stream.DataAvailable) {
while($stream.DataAvailable) {
$read = $stream.Read($buffer, 0, 1024)
if($buffer[0] -eq $IAC) {
for($c=0; $c -lt $read; $c++) {
write-host $buffer[$c]
}
}
write-host -n ($encoding.GetString($buffer, 0, $read))
}
start-sleep -m $input_wait
$writer.WriteLine($commands[$i])
$writer.Flush()
$r = $retry
$rep = 1
}
}
if($rep -eq 0) {
throw ("No response negoation`r`n")
}
}
# ログアウト
###########################################################################
$rep = 0
for($r=0; $r -lt $retry; $r++) {
start-sleep -m 500
if ($stream.DataAvailable) {
while($stream.DataAvailable) {
$read = $stream.Read($buffer, 0, 1024)
write-host -n ($encoding.GetString($buffer, 0, $read))
}
start-sleep -m $input_wait
$writer.WriteLine("exit")
$writer.Flush()
$r = $retry
$rep = 1
}
}
if($rep -eq 0) {
throw ("No response negoation`r`n")
}
# ホストからの応答を受けて処理終了
###########################################################################
$rep = 0
for($r=0; $r -lt $retry; $r++) {
start-sleep -m 500
if ($stream.DataAvailable) {
while($stream.DataAvailable) {
$read = $stream.Read($buffer, 0, 1024)
write-host -n ($encoding.GetString($buffer, 0, $read))
}
start-sleep -m $input_wait
$writer.WriteLine("bye!") # 送信はなんでもOK!
$writer.Flush()
$r = $retry
$rep = 1
}
}
if($rep -eq 0) {
throw ("No response negoation`r`n")
}
}

# ERROR Routine(catch throw)
###############################################################################
catch {
write-host $error[0]
$dateTime = get-date
$errorOccurence = "Error occurred connecting to $remoteHost on $port at $dateTime"
write-host $errorOccurence
}

# Finalize
###############################################################################
finally {
write-host `r`n
write-host exit program.`r`n
$writer.Close()
$stream.Close()

stop-transcript
}