Wednesday, October 06, 2010

Minimal Telnet Parsing in C#

Ever wonder what the minimal parsing is needed to do a telnet server?
While the socket/listen is easy stuff, and life for low session counts
is easy using a thread per session, the issue is, what do I need to do
to get to the point I can talk to my device. In this case a scangun
with a telnet client.

Telnet actually is a protocol. The PC Micro link is a good write-up
which wikipedia was so nice to provide a link to.

I google for a while, and could not find anything other than expensive .net libraries, or basic protocol documentation.

Here is the code, so you dont have to search any longer.

private void HandleClientComm(object client)
        {
            TcpClient tcpClient = (TcpClient)client;
            NetworkStream clientStream = tcpClient.GetStream();

            byte[] message = new byte[4096];
            byte[] cmd = new byte[4096];
            int cmdcnt;
            int bpos;
            int bytesRead;
           
            const int tmode_normal = 0;
            const int tmode_iac = 1;
            const int tmode_option = 2;
            const int tmode_do = 3;
            const int tmode_will = 4;
            int mode;
            byte thebyte;
            BarCodeClient bci;
            
            cmdcnt = 0;
            bpos = 0;
            mode = tmode_normal;

           bci = new BarCodeClient(client);
       
            while (true)
            {
                bytesRead = 0;

                try
                {
                    //blocks until a client sends a message
                    bytesRead = clientStream.Read(message, 0, 4096);
                }
                catch
                {
                    //a socket error has occured
                    break;
                }

                if (bytesRead == 0)
                {
                    //the client has disconnected from the server
                    break;
                }
               
                bpos = 0;
                while( (cmdcnt < 4096) && (bpos < bytesRead)){
                    thebyte = message[bpos++];
                    switch (mode)
                    {
                        case tmode_normal:
                            switch (thebyte)
                            {
                                case telnet_iac:
                                    mode = tmode_iac;
                                    continue;
                                case 0x0a: // LineFeed
                                    break;
                                case 0x0d:
                                    string thecmd = Encoding.ASCII.GetString(cmd,0,cmdcnt);
                                    if (thecmd.Length > 0)
                                    {
                                        bci.Cmd(thecmd);
                                    }
                                    cmdcnt = 0;
                                    continue;
                                default:
                                    break;
                            }
                            cmd[cmdcnt++] = thebyte; 
                            break;
                        case tmode_iac:
                            switch (thebyte)
                            {
                                case telnet_do:
                                     mode = tmode_do;
                                     break;
                                case telnet_se:  // End Subnegotiation
                                     mode = tmode_normal;
                                     bci.Cmd(""); // Let the lower level know to force a screen refresh
                                     break;
                                case telnet_sb:
                                    mode = tmode_option;
                                    break;
                                case telnet_will:
                                    mode = tmode_will;
                                    break;
                                default:
                                    mode = tmode_normal;
                                    break;
                            }
                            break;
                        case tmode_do:
                            switch (thebyte)
                            {
                                default:
                                    mode = tmode_normal;
                                    break;
                            }
                            break;
                        case tmode_will:
                            switch (thebyte)
                             {
                                 default:
                                     mode = tmode_normal;
                                     break;
                             }
                            break;
                        case tmode_option:
                            switch (thebyte)
                            {
                                default:
                                    mode = tmode_normal;
                                    break;
                            }
                            break;
                        }
                    }
                
            }

            tcpClient.Close();
        }

0 comments: