Windows automate telnet

2019-03-14 18:49发布

问题:

I would like to run a set of commands that would typically be run in telnet(from c#).

For example I would like to run the following

using System;
using System.Diagnostics;

namespace InteractWithConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            ProcessStartInfo cmdStartInfo = new ProcessStartInfo();
            cmdStartInfo.FileName = @"C:\Windows\System32\cmd.exe";
            cmdStartInfo.RedirectStandardOutput = true;
            cmdStartInfo.RedirectStandardError = true;
            cmdStartInfo.RedirectStandardInput = true;
            cmdStartInfo.UseShellExecute = false;
            cmdStartInfo.CreateNoWindow = true;

            Process cmdProcess = new Process();
            cmdProcess.StartInfo = cmdStartInfo;
            cmdProcess.ErrorDataReceived += cmd_Error;
            cmdProcess.OutputDataReceived += cmd_DataReceived;
            cmdProcess.EnableRaisingEvents = true;
            cmdProcess.Start();
            cmdProcess.BeginOutputReadLine();
            cmdProcess.BeginErrorReadLine();

            cmdProcess.StandardInput.WriteLine("telnet telehack.com");
            int milliseconds = 2000;
            System.Threading.Thread.Sleep(milliseconds);
            cmdProcess.StandardInput.WriteLine("exit");

            cmdProcess.StandardInput.WriteLine("exit");
            cmdProcess.WaitForExit();
        }

        static void cmd_DataReceived(object sender, DataReceivedEventArgs e)
        {
            Console.WriteLine(e.Data);
        }

        static void cmd_Error(object sender, DataReceivedEventArgs e)
        {
            Console.WriteLine(e.Data);
        }
    }
}

and keep telnet open to run subsequent commands. For example for the question above I would like to run and receive the following output, but I don't receive any of the telnet output. It doesn't receive any output. This is related.

telnet telehack.com
> Connected to TELEHACK port 53

It is 2:33 pm on Tuesday, September 1, 2015 in Mountain View, California, USA.
There are 31 local users. There are 24906 hosts on the network.

May the command line live forever.

Command, one of the following:
  ?           ac          advent      basic       cal         calc
  ching       clear       clock       cowsay      date        echo
  eliza       factor      figlet      finger      fnord       geoip
  help        hosts       ipaddr      joke        login       md5
  morse       newuser     notes       octopus     phoon       pig
  ping        primes      privacy     rain        rand        rfc
  rig         roll        rot13       sleep       starwars    traceroute
  units       uptime      usenet      users       uumap       uupath
  uuplot      weather     when        zc          zork        zrun
.calc
calc>2+2
> 4

回答1:

Based on comments I understand that you can use actual telnet protocol implementation instead of calling to telnet.exe, so

Form1.cs

using MinimalisticTelnet;
using System;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace Telnet
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private MinimalisticTelnet.TelnetConnection _tc;

        private void Form1_Load(object sender, EventArgs e)
        {
            _tc = new TelnetConnection("telehack.com", 23);
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            ProcessOutput();
        }

        private void btnSendCommand_Click(object sender, EventArgs e)
        {
            if (_tc.IsConnected)
            {
                _tc.WriteLine(tbCommand.Text.Trim());
                tbCommand.Clear();
                tbCommand.Focus();
                ProcessOutput();
            }
        }

        private void ProcessOutput()
        {
            if (!_tc.IsConnected)
                return;

            var s = _tc.Read();
            s = Regex.Replace(s, @"\x1b\[([0-9,A-Z]{1,2}(;[0-9]{1,2})?(;[0-9]{3})?)?[m|K]?", "");
            tbOutput.AppendText(s);
        }
    }
}

TelnetInterface.cs

// minimalistic telnet implementation
// conceived by Tom Janssens on 2007/06/06  for codeproject
//
// http://www.corebvba.be



using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;

namespace MinimalisticTelnet
{
    enum Verbs
    {
        WILL = 251,
        WONT = 252,
        DO = 253,
        DONT = 254,
        IAC = 255
    }

    enum Options
    {
        SGA = 3
    }

    class TelnetConnection
    {
        TcpClient tcpSocket;

        int TimeOutMs = 100;

        public TelnetConnection(string Hostname, int Port)
        {
            tcpSocket = new TcpClient(Hostname, Port);

        }

        public string Login(string Username, string Password, int LoginTimeOutMs)
        {
            int oldTimeOutMs = TimeOutMs;
            TimeOutMs = LoginTimeOutMs;
            string s = Read();
            if (!s.TrimEnd().EndsWith(":"))
                throw new Exception("Failed to connect : no login prompt");
            WriteLine(Username);

            s += Read();
            if (!s.TrimEnd().EndsWith(":"))
                throw new Exception("Failed to connect : no password prompt");
            WriteLine(Password);

            s += Read();
            TimeOutMs = oldTimeOutMs;
            return s;
        }

        public void WriteLine(string cmd)
        {
            Write(cmd + Environment.NewLine);
        }

        public void Write(string cmd)
        {
            if (!tcpSocket.Connected) return;
            byte[] buf = System.Text.ASCIIEncoding.ASCII.GetBytes(cmd.Replace("\0xFF", "\0xFF\0xFF"));
            tcpSocket.GetStream().Write(buf, 0, buf.Length);
        }

        public string Read()
        {
            if (!tcpSocket.Connected) return null;
            StringBuilder sb = new StringBuilder();
            do
            {
                ParseTelnet(sb);
                System.Threading.Thread.Sleep(TimeOutMs);
            } while (tcpSocket.Available > 0);
            return sb.ToString();
        }

        public bool IsConnected
        {
            get { return tcpSocket.Connected; }
        }

        void ParseTelnet(StringBuilder sb)
        {
            while (tcpSocket.Available > 0)
            {
                int input = tcpSocket.GetStream().ReadByte();
                switch (input)
                {
                    case -1:
                        break;
                    case (int)Verbs.IAC:
                        // interpret as command
                        int inputverb = tcpSocket.GetStream().ReadByte();
                        if (inputverb == -1) break;
                        switch (inputverb)
                        {
                            case (int)Verbs.IAC:
                                //literal IAC = 255 escaped, so append char 255 to string
                                sb.Append(inputverb);
                                break;
                            case (int)Verbs.DO:
                            case (int)Verbs.DONT:
                            case (int)Verbs.WILL:
                            case (int)Verbs.WONT:
                                // reply to all commands with "WONT", unless it is SGA (suppres go ahead)
                                int inputoption = tcpSocket.GetStream().ReadByte();
                                if (inputoption == -1) break;
                                tcpSocket.GetStream().WriteByte((byte)Verbs.IAC);
                                if (inputoption == (int)Options.SGA)
                                    tcpSocket.GetStream().WriteByte(inputverb == (int)Verbs.DO ? (byte)Verbs.WILL : (byte)Verbs.DO);
                                else
                                    tcpSocket.GetStream().WriteByte(inputverb == (int)Verbs.DO ? (byte)Verbs.WONT : (byte)Verbs.DONT);
                                tcpSocket.GetStream().WriteByte((byte)inputoption);
                                break;
                            default:
                                break;
                        }
                        break;
                    default:
                        sb.Append((char)input);
                        break;
                }
            }
        }
    }
}

This is Windows Forms app with 2 textboxes and 1 button and a timer (interval is 1000ms). I've used code from CodeProject (linked in original question) with some changes to make it actually work.



回答2:

Coding this may be hard. However, there are free tools out there for Telnet scripting, see Expect for one. If you have an C# application then perhaps your code could generate the Expect script and then run Expect?



回答3:

Sending the information to telnet seems like it should be straightforward; use SendKeys(). The question becomes how to capture the output.

I found this YouTube video that should help: https://www.youtube.com/watch?v=BDTCviA-5M8

The solution in the video doesn't really address keeping the session open. I'm a bit outside my knowledge area here, but I believe you can start telnet in a worker thread, send commands to it with SendKeys(), capture the output as described in the video, then parse it.

Does that sufficiently resolve the requirement?