Advertisement
EmilySamantha80

Minimalist Telnet client with scripting capabilities

Aug 22nd, 2016
286
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 15.97 KB | None | 0 0
  1. // Title:  Minimalist telnet client with scripting capabilities
  2. // Author: Emily Heiner
  3. // Date:   2016-08-22
  4. // This code is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported license.
  5. // You are free to share and adapt this code for any purposes, commerical and non-commercial,
  6. // as long as appropriate credit is given, you indicate if changes were made, and you share your
  7. // modified code. License details can be found at https://creativecommons.org/licenses/by-sa/3.0/
  8.  
  9. using System;
  10. using System.Text;
  11. using System.Net.Sockets;
  12. using System.Text.RegularExpressions;
  13.  
  14. /// <summary>
  15. /// Minimalist telnet implementation
  16. /// </summary>
  17. namespace ESH.Telnet
  18. {
  19.     /// <summary>
  20.     /// Telnet protocol verbs
  21.     /// </summary>
  22.     public enum TelnetVerbs
  23.     {
  24.         WILL = 251,
  25.         WONT = 252,
  26.         DO = 253,
  27.         DONT = 254,
  28.         IAC = 255
  29.     }
  30.  
  31.     /// <summary>
  32.     /// Telnet protocol options
  33.     /// </summary>
  34.     public enum TelnetOptions
  35.     {
  36.         SGA = 3
  37.     }
  38.  
  39.     /// <summary>
  40.     /// Minimalist telnet client with scripting capabilities
  41.     /// </summary>
  42.     public class TelnetClient
  43.     {
  44.         private TcpClient _Client;
  45.         /// <summary>
  46.         /// The underlying TcpClinet object
  47.         /// </summary>
  48.         public TcpClient Client { get { return this._Client; } }
  49.  
  50.         private string _Host;
  51.         /// <summary>
  52.         /// Host name/ip of the telnet server
  53.         /// </summary>
  54.         public string Host { get { return _Host; } }
  55.  
  56.         private int _Port;
  57.         /// <summary>
  58.         /// Port number of the telnet server
  59.         /// </summary>
  60.         public int Port { get { return _Port; } }
  61.  
  62.         /// <summary>
  63.         /// Internal variable used for while reading commands coming in from the server.
  64.         /// </summary>
  65.         private int _ReadTimeoutMs { get; set; }
  66.  
  67.  
  68.         private StringBuilder _ResponseBuffer;
  69.         /// <summary>
  70.         /// Holds the response from the server.
  71.         /// </summary>
  72.         public string ResponseBuffer
  73.         {
  74.             get
  75.             {
  76.                 if (this._ResponseBuffer == null)
  77.                 {
  78.                     return string.Empty;
  79.                 }
  80.                 return this._ResponseBuffer.ToString();
  81.             }
  82.         }
  83.  
  84.         /// <summary>
  85.         /// Initializes a new telnet client.
  86.         /// </summary>
  87.         /// <param name="host">Host name/ip</param>
  88.         /// <param name="port">Port number</param>
  89.         public TelnetClient(string host, int port = 23)
  90.         {
  91.             this._Client = new TcpClient();
  92.             this._Host = host;
  93.             this._Port = port;
  94.             this._ReadTimeoutMs = 1000;
  95.             this._ResponseBuffer = new StringBuilder();
  96.         }
  97.  
  98.         /// <summary>
  99.         /// Connect to the server.
  100.         /// </summary>
  101.         public void Connect()
  102.         {
  103.             this._Client.Connect(this.Host, this.Port);
  104.         }
  105.  
  106.         /// <summary>
  107.         /// Disconnect from the server.
  108.         /// </summary>
  109.         public void Disconnect()
  110.         {
  111.             this._Client.Close();
  112.         }
  113.  
  114.         /// <summary>
  115.         /// Returns whether or not the client is connected to the server.
  116.         /// NOTE: This function is currently not reliable. It does not detect disconnects if the server closes the connection.
  117.         /// </summary>
  118.         public bool IsConnected
  119.         {
  120.             get
  121.             {
  122.                 // We can't use TcpClient.Connected to detect connection state reliably. It lies and says it's still connected sometimes.
  123.  
  124.                 // Find out if the client is still connected to the server.
  125.                 // This will catch server side disconnects.
  126.                 if (this._Client.Client.Poll(0, SelectMode.SelectRead))
  127.                 {
  128.                     byte[] buff = new byte[1];
  129.                     if (this._Client.Client.Receive(buff, SocketFlags.Peek) == 0)
  130.                     {
  131.                         // Server has disconnected
  132.                         return false;
  133.                     }
  134.                 }
  135.  
  136.                 // Check if the server has disconnected or has never connected
  137.                 if (!this._Client.Connected)
  138.                 {
  139.                     return false;
  140.                 }
  141.  
  142.                 // Connection is good
  143.                 return true;
  144.             }
  145.         }
  146.  
  147.         /// <summary>
  148.         /// Overly simplified login prompt detection. Shows how to use the scripting capabilities.
  149.         /// </summary>
  150.         /// <param name="username">Username to connect to the server</param>
  151.         /// <param name="password">Password to connect to the server</param>
  152.         /// <param name="promptTimeoutMs">How long to wait for a response from the server</param>
  153.         /// <param name="buffer">String buffer to store the response from the server</param>
  154.         /// <param name="clearBufferAfterRead">Whether or not to clear the buffer after reading the response from the server</param>
  155.         public void Login(string username, string password, int promptTimeoutMs, out string buffer, bool clearBufferAfterRead = true)
  156.         {
  157.             bool result = false;
  158.             string tmpBuffer;
  159.             buffer = "";
  160.  
  161.             if (!this.IsConnected)
  162.             {
  163.                 this.Connect();
  164.             }
  165.  
  166.             result = this.WaitForConnect(promptTimeoutMs);
  167.             if (!result)
  168.                 throw new Exception("Failed to connect to server.");
  169.  
  170.             result = WaitFor(":", promptTimeoutMs, out tmpBuffer);
  171.             if (!result)
  172.                 throw new Exception("Failed to log in! No login prompt.");
  173.             WriteLine(username);
  174.             buffer += tmpBuffer;
  175.  
  176.             result = WaitFor(":", promptTimeoutMs, out tmpBuffer);
  177.             if (!result)
  178.                 throw new Exception("Failed to log in! No password prompt.");
  179.             WriteLine(password);
  180.             buffer += tmpBuffer;
  181.  
  182.             if (clearBufferAfterRead)
  183.             {
  184.                 this.ClearResponseBuffer();
  185.             }
  186.             return;
  187.         }
  188.  
  189.         /// <summary>
  190.         /// Take the contents of the response buffer, and then clear it.
  191.         /// </summary>
  192.         /// <returns></returns>
  193.         public string TakeResponeBuffer()
  194.         {
  195.             string buffer = this.ResponseBuffer;
  196.             this.ClearResponseBuffer();
  197.             return buffer;
  198.         }
  199.  
  200.         /// <summary>
  201.         /// Clear the text response buffer
  202.         /// </summary>
  203.         public void ClearResponseBuffer()
  204.         {
  205.             this._ResponseBuffer = new StringBuilder();
  206.         }
  207.  
  208.         /// <summary>
  209.         /// Write the specified text to the server.
  210.         /// A newline character is appended to the text.
  211.         /// </summary>
  212.         /// <param name="text"></param>
  213.         public void WriteLine(string text)
  214.         {
  215.             this.Write(text + "\n");
  216.         }
  217.  
  218.         /// <summary>
  219.         /// Write the specified text to the server.
  220.         /// </summary>
  221.         /// <param name="text">Text to write</param>
  222.         public void Write(string text)
  223.         {
  224.             if (!this.IsConnected)
  225.             {
  226.                 throw new SocketException((int)SocketError.NotConnected);
  227.             }
  228.             byte[] buf = System.Text.ASCIIEncoding.ASCII.GetBytes(text.Replace("\0xFF", "\0xFF\0xFF"));
  229.             _Client.GetStream().Write(buf, 0, buf.Length);
  230.         }
  231.  
  232.         /// <summary>
  233.         /// Read and parse a byte from the receive stream, if there is anything to receive.
  234.         /// </summary>
  235.         /// <returns></returns>
  236.         private int ReadByte()
  237.         {
  238.             if (!this.IsConnected)
  239.             {
  240.                 throw new SocketException((int)SocketError.NotConnected);
  241.             }
  242.  
  243.             int charCode = -1;
  244.             if (_Client.Available > 0)
  245.             {
  246.                 charCode = _Client.GetStream().ReadByte();
  247.                 charCode = this.ParseTelnetResponseByte(charCode);
  248.                 if (charCode > 0)
  249.                 {
  250.                     this._ResponseBuffer.Append((char)charCode);
  251.                 }
  252.             }
  253.             return charCode;
  254.         }
  255.  
  256.         /// <summary>
  257.         /// Parse a byte of the response from the server.
  258.         /// </summary>
  259.         /// <param name="charCode">Character code of the response byte</param>
  260.         /// <returns></returns>
  261.         private int ParseTelnetResponseByte(int charCode)
  262.         {
  263.             DateTime timeout = DateTime.Now.AddMilliseconds(this._ReadTimeoutMs);
  264.  
  265.             switch (charCode)
  266.             {
  267.                 case -1:
  268.                     break;
  269.                 case (int)TelnetVerbs.IAC:
  270.                     // interpret as command
  271.                     int inputVerb = -1;
  272.                     timeout = DateTime.Now.AddMilliseconds(this._ReadTimeoutMs);
  273.                     while (DateTime.Now < timeout)
  274.                     {
  275.                         if (_Client.Available == 0)
  276.                         {
  277.                             System.Threading.Thread.Sleep(10);
  278.                             continue;
  279.                         }
  280.                         inputVerb = _Client.GetStream().ReadByte();
  281.                         if (inputVerb >= 0)
  282.                         {
  283.                             break;
  284.                         }
  285.                     }
  286.                     if (inputVerb == -1) break;
  287.                     switch (inputVerb)
  288.                     {
  289.                         case (int)TelnetVerbs.IAC:
  290.                             // Literal IAC = 255 escaped, so append char 255 to string
  291.                             return -1;
  292.                         case (int)TelnetVerbs.DO:
  293.                         case (int)TelnetVerbs.DONT:
  294.                         case (int)TelnetVerbs.WILL:
  295.                         case (int)TelnetVerbs.WONT:
  296.                             // Reply to all commands with "WONT", unless it is SGA (suppres go ahead)
  297.                             int inputOption = -1;
  298.                             timeout = DateTime.Now.AddMilliseconds(this._ReadTimeoutMs);
  299.                             while (DateTime.Now < timeout)
  300.                             {
  301.                                 inputOption = _Client.GetStream().ReadByte();
  302.                                 if (inputOption >= 0)
  303.                                 {
  304.                                     break;
  305.                                 }
  306.                                 System.Threading.Thread.Sleep(10);
  307.                             }
  308.                             if (inputOption == -1) break;
  309.                             _Client.GetStream().WriteByte((byte)TelnetVerbs.IAC);
  310.                             if (inputOption == (int)TelnetOptions.SGA)
  311.                             {
  312.                                 _Client.GetStream().WriteByte(inputVerb == (int)TelnetVerbs.DO ? (byte)TelnetVerbs.WILL : (byte)TelnetVerbs.DO);
  313.                             }
  314.                             else
  315.                             {
  316.                                 _Client.GetStream().WriteByte(inputVerb == (int)TelnetVerbs.DO ? (byte)TelnetVerbs.WONT : (byte)TelnetVerbs.DONT);
  317.                             }
  318.                             _Client.GetStream().WriteByte((byte)inputOption);
  319.                             break;
  320.                         default:
  321.                             break;
  322.                     }
  323.                     break;
  324.                 default:
  325.                     return charCode;
  326.  
  327.             }
  328.             return -1;
  329.         }
  330.  
  331.         /// <summary>
  332.         /// Wait for the specified response from the server.
  333.         /// </summary>
  334.         /// <param name="regex">The text to wait for</param>
  335.         /// <param name="timeoutMs">Wait timeout in ms</param>
  336.         /// <param name="buffer">Holds text received while waiting</param>
  337.         /// <param name="clearBufferAfterRead">Whether or not to clear the buffer after reading the response from the server</param>
  338.         /// <returns></returns>
  339.         public bool WaitFor(string text, int timeoutMs, out string buffer, bool clearBufferAfterRead = true)
  340.         {
  341.             var timeout = DateTime.Now.AddMilliseconds(timeoutMs);
  342.             bool result = false;
  343.             while (DateTime.Now < timeout)
  344.             {
  345.                 this.ReadByte();
  346.                 if (this.ResponseBuffer.Contains(text))
  347.                 {
  348.                     result = true;
  349.                     break;
  350.                 }
  351.             }
  352.             buffer = this.ResponseBuffer;
  353.             if (clearBufferAfterRead)
  354.             {
  355.                 this.ClearResponseBuffer();
  356.             }
  357.             return result;
  358.         }
  359.  
  360.  
  361.         /// <summary>
  362.         /// Wait for the specified response from the server.
  363.         /// </summary>
  364.         /// <param name="regex">The regex to wait for</param>
  365.         /// <param name="timeoutMs">Wait timeout in ms</param>
  366.         /// <param name="buffer">Holds text received while waiting</param>
  367.         /// <param name="clearBufferAfterRead">Whether or not to clear the buffer after reading the response from the server</param>
  368.         /// <returns></returns>
  369.         public bool WaitFor(Regex regex, int timeoutMs, out string buffer, bool clearBufferAfterRead = true)
  370.         {
  371.             var timeout = DateTime.Now.AddMilliseconds(timeoutMs);
  372.             bool result = false;
  373.             while (DateTime.Now < timeout)
  374.             {
  375.                 this.ReadByte();
  376.                 if (regex.IsMatch(this.ResponseBuffer))
  377.                 {
  378.                     result = true;
  379.                     break;
  380.                 }
  381.             }
  382.             buffer = this.ResponseBuffer;
  383.             if (clearBufferAfterRead)
  384.             {
  385.                 this.ClearResponseBuffer();
  386.             }
  387.             return result;
  388.         }
  389.  
  390.         /// <summary>
  391.         /// Wait for the specified response from the server.
  392.         /// The response buffer is never cleared after this function.
  393.         /// </summary>
  394.         /// <param name="text">The text to wait for</param>
  395.         /// <param name="timeoutMs">Wait timeout in ms</param>
  396.         /// <returns></returns>
  397.         public bool WaitFor(string text, int timeoutMs)
  398.         {
  399.             string buffer;
  400.             return this.WaitFor(text, timeoutMs, out buffer, false);
  401.         }
  402.  
  403.         /// <summary>
  404.         /// Wait for the specified response from the server.
  405.         /// The response buffer is never cleared after this function.
  406.         /// </summary>
  407.         /// <param name="regex">The regex to wait for</param>
  408.         /// <param name="timeoutMs">Wait timeout in ms</param>
  409.         /// <returns></returns>
  410.         public bool WaitFor(Regex regex, int timeoutMs)
  411.         {
  412.             string buffer;
  413.             return this.WaitFor(regex, timeoutMs, out buffer, false);
  414.         }
  415.  
  416.         /// <summary>
  417.         /// Wait for the server to disconnect from the client.
  418.         /// </summary>
  419.         /// <param name="timeoutMs">Wait timeout in ms</param>
  420.         /// <returns></returns>
  421.         public bool WaitForDisconnect(int timeoutMs)
  422.         {
  423.             var timeout = DateTime.Now.AddMilliseconds(timeoutMs);
  424.             while (DateTime.Now < timeout)
  425.             {
  426.                 if (!this.IsConnected)
  427.                 {
  428.                     return true;
  429.                 }
  430.             }
  431.             return false;
  432.         }
  433.  
  434.         /// <summary>
  435.         /// Wait for a connection to the server to be established
  436.         /// </summary>
  437.         /// <param name="timeoutMs">Wait timeout in ms</param>
  438.         /// <returns></returns>
  439.         public bool WaitForConnect(int timeoutMs)
  440.         {
  441.             var timeout = DateTime.Now.AddMilliseconds(timeoutMs);
  442.             while (DateTime.Now < timeout)
  443.             {
  444.                 if (this.IsConnected)
  445.                 {
  446.                     return true;
  447.                 }
  448.             }
  449.             return false;
  450.         }
  451.     }
  452. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement