//called when receiving any message void handleServerMessage(IAsyncResult result) { //get length of data in buffer int receivedCount; try { receivedCount = connection.Client.EndReceive(result); } catch { //if there was any issue reading the server text, ignore the message (what else can we do?) return; } //0 bytes received means the server disconnected if (receivedCount == 0) { this.Disconnect(); return; } //list of bytes which aren't telnet sequences //ultimately, this will be the original buffer minus any telnet messages from the server List <string> telnetMessages; List <byte> contentBytes = this.telnetParser.HandleAndRemoveTelnetBytes(this.buffer, receivedCount, out telnetMessages); //report any telnet sequences seen to the caller Application.Current.Dispatcher.BeginInvoke(new Action(delegate { foreach (string telnetMessage in telnetMessages) { //fire the "received a server message" event this.telnetMessage(telnetMessage); } })); //now we've filtered-out and responded accordingly to any telnet data. //next, convert the actual MUD content of the message from ASCII to Unicode string message = AsciiDecoder.AsciiToUnicode(contentBytes.ToArray(), contentBytes.Count); //run the following on the main thread so that calling code doesn't have to think about threading if (this.serverMessage != null) { Application.Current.Dispatcher.BeginInvoke(new Action(delegate { //pass the message to the mudTranslator to parse any ANSI control sequences (colors!) List <MUDTextRun> runs = this.ansiColorParser.Translate(message); //fire the "received a server message" event with the runs to be displayed this.serverMessage(runs); })); } //now that we're done with this message, listen for the next message connection.Client.BeginReceive(this.buffer, 0, this.buffer.Length, SocketFlags.None, new AsyncCallback(this.handleServerMessage), null); }
public List <byte> HandleAndRemoveTelnetBytes(byte[] buffer, int receivedCount, out List <string> telnetMessages) { //list to hold a report of any telnet control sequences received or sent telnetMessages = new List <string>(); //list to hold any bytes which aren't telnet bytes (which will be most of the bytes) List <byte> contentBytes = new List <byte>(); //we'll scan for telnet control sequences. anything NOT a telnet control sequence will be added to the contentBytes list for later processing. int currentIndex = 0; while (currentIndex < receivedCount) { //search for an IAC, which may signal the beginning of a telnet message while (currentIndex < receivedCount && buffer[currentIndex] != (byte)Telnet.InterpretAsCommand) { contentBytes.Add(buffer[currentIndex]); currentIndex++; } //if at the end of the data, stop. otherwise we've encountered an IAC and there should be at least one more byte here if (++currentIndex == receivedCount) { break; } //read the next byte byte secondByte = buffer[currentIndex]; //if another IAC, then this was just sequence IAC IAC, which is the escape sequence to represent byte value 255 (=IAC) in the content stream if (secondByte == (byte)Telnet.InterpretAsCommand) { //write byte value 255 to the content stream and move on contentBytes.Add(secondByte); } //otherwise we have a "real" telnet sequence, where the second byte is a command or negotiation else { //start building a string representation of this message, to be reported to the caller //caller might want to show this info to the user always, or optionally for debugging purposes StringBuilder stringVersionOfMessage = new StringBuilder(); //also build a string version of the response (if any) StringBuilder stringVersionOfResponse = new StringBuilder(); //DO if (secondByte == (byte)Telnet.DO) { stringVersionOfMessage.Append("DO "); //what are we being told to do? currentIndex++; if (currentIndex == receivedCount) { break; } byte thirdByte = buffer[currentIndex]; stringVersionOfMessage.Append(interpretByteAsTelnet(thirdByte)); //if NAWS (negotiate about window size) if (thirdByte == (byte)Telnet.NAWS) { //on connection, we offered to negotiate about window size. so this is a "go ahead and negotiate" response. //so then, send information about client window size per the NAWS protocol //we're lieing to server by telling it a ridiculously large size, so that it won't do line breaking or paging for us (annoying!) stringVersionOfResponse.Append(this.sendTelnetBytes( (byte)Telnet.SubnegotiationBegin, (byte)31, 254, 254, 254, 254, (byte)Telnet.InterpretAsCommand, (byte)Telnet.SubnegotiationEnd)); } //everything else the server might ask us to do is unsupported by us else { stringVersionOfMessage.Append(interpretByteAsTelnet(thirdByte)); //sorry, i won't do whatever "that thing you said to do" was stringVersionOfResponse.Append(this.sendTelnetBytes((byte)Telnet.InterpretAsCommand, (byte)Telnet.WONT, thirdByte)); } } //DONT else if (secondByte == (byte)Telnet.DONT) { stringVersionOfMessage.Append("DONT "); currentIndex++; if (currentIndex == receivedCount) { break; } byte thirdByte = buffer[currentIndex]; stringVersionOfMessage.Append(interpretByteAsTelnet(thirdByte)); //whatever you want me to stop doing, that's no problem because i wasn't going to do it anyway stringVersionOfResponse.Append(this.sendTelnetBytes((byte)Telnet.WONT, thirdByte)); } //WILL else if (secondByte == (byte)Telnet.WILL) { stringVersionOfMessage.Append("WILL "); //find out what the server is willing to do currentIndex++; if (currentIndex == receivedCount) { break; } byte thirdByte = buffer[currentIndex]; stringVersionOfMessage.Append(interpretByteAsTelnet(thirdByte)); //anything the server offers to do for us, we'll tell it not to because we don't know what it is stringVersionOfResponse.Append((this.sendTelnetBytes((byte)Telnet.DONT, thirdByte))); } //WONT else if (secondByte == (byte)Telnet.WONT) { stringVersionOfMessage.Append("WONT "); //find out what the server is NOT willing to do currentIndex++; if (currentIndex == receivedCount) { break; } byte thirdByte = buffer[currentIndex]; stringVersionOfMessage.Append(interpretByteAsTelnet(thirdByte)); //because we haven't asked the server to DO anything, should not expect to receive any WONT //however if we do receive a WONT, respond with a DONT to confirm that the server can go ahead and NOT do that thing it doesn't want to do stringVersionOfResponse.Append(this.sendTelnetBytes((byte)Telnet.DONT, thirdByte)); } //subnegotiations else if (secondByte == (byte)Telnet.SubnegotiationBegin) { stringVersionOfMessage.Append("SB "); List <byte> subnegotiationBytes = new List <byte>(); //read until an IAC followed by an SE while (currentIndex < receivedCount - 1 && !(buffer[currentIndex] == (byte)Telnet.InterpretAsCommand && buffer[currentIndex] == (byte)Telnet.SubnegotiationEnd)) { subnegotiationBytes.Add(buffer[currentIndex]); currentIndex++; } byte[] subnegotiationBytesArray = subnegotiationBytes.ToArray(); //append the content of the subnegotiation to the incoming message report string stringVersionOfMessage.Append(AsciiDecoder.AsciiToUnicode(subnegotiationBytesArray, subnegotiationBytes.Count)); //append the subnegotiation end stringVersionOfMessage.Append(" SE"); } //any other telnet message else { //try to convert it to a known message via the enum defined above stringVersionOfMessage.Append(interpretByteAsTelnet(secondByte)); } //report the control sequence we found, if any string messageToReport = stringVersionOfMessage.ToString(); if (!string.IsNullOrEmpty(messageToReport)) { telnetMessages.Add("RECV: " + messageToReport.ToString()); } //report the response message sent, if any string responseToReport = stringVersionOfResponse.ToString(); if (!string.IsNullOrEmpty(responseToReport)) { telnetMessages.Add("SEND: " + stringVersionOfResponse.ToString()); } } //move up to the next byte in the data currentIndex++; } return(contentBytes); }