Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Telnet processing packet agnostic. #98

Merged
merged 5 commits into from Sep 11, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 0 additions & 1 deletion MBBSEmu/Session/SessionBase.cs
Expand Up @@ -4,7 +4,6 @@
using MBBSEmu.Module;
using MBBSEmu.Server;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
Expand Down
44 changes: 44 additions & 0 deletions MBBSEmu/Session/Telnet/EnumIacOptions.cs
Expand Up @@ -2,20 +2,64 @@
{
/// <summary>
/// Enumerator for IAC Options that MBBSEmu will handle
///
/// https://www.iana.org/assignments/telnet-options/telnet-options.xhtml
/// </summary>
public enum EnumIacOptions : byte
{
BinaryTransmission = 0,
Echo = 1,
Reconnection = 2,
SuppressGoAhead = 3,
ApproximateMessageSizeNegotiation = 4,
Status = 5,
TimingMark = 6,
RemoteControlledTransAndEcho = 7,
OutputLineWidth = 8,
OutputPageSize = 9,
OutputCarrigeReturnDisposition = 10,
OutputHorizontalTabStops = 11,
OutputHorizontalTabDisposition = 12,
OutputFormfeedDisposition = 13,
OutputVerticalTabStops = 14,
OutputVerticalTabDisposition = 15,
OutputLineFeedDisposition = 16,
ExtendedAscii = 17,
Logout = 18,
ByteMacro = 19,
DataEntryTerminal = 20,
SUPDUP = 21,
SUPDUPOutput = 22,
SendLocation = 23,
TerminalType = 24,
EndOfRecord = 25,
TACACSUserIdentification = 26,
OutputMarking = 27,
TerminalLocationNumber = 28,
Telnet3270Regime = 29,
X_3Pad = 30,
NegotiateAboutWindowSize = 31,
TerminalSpeed = 32,
RemoteFlowControl = 33,
Linemode = 34,
XDisplayLocation = 35,
EnvironmentOption = 36,
AuthenticationOption = 37,
EncryptionOption = 38,
NewEnvironmentOption = 39,
TN3270E = 40,
XAUTH = 41,
CHARSET = 42,
TelnetRemoteSerialPort = 43,
ComPortControlOption = 44,
TelnetSuppressLocalEcho = 45,
TelnetStartTLS = 46,
KERMIT = 47,
SEND_URL = 48,
FORWARD_X = 49,
TELOPTPragmaLogon = 138,
TELOPT_SSPI_Logon = 139,
TELOPTPragmaHeartbeat = 140,
None = 0xFF
}
}
122 changes: 122 additions & 0 deletions MBBSEmu/Session/Telnet/IacFilter.cs
@@ -0,0 +1,122 @@
using NLog;
using System.IO;
using System;

namespace MBBSEmu.Session.Telnet
{
/// <summary>
/// Parses incoming client Telnet data and invokes the IacVerbReceived
/// event when a verb is received.
/// </summary>
public class IacFilter
{
private readonly ILogger _logger;

private const byte IAC = (byte) 0xFF;
paladine marked this conversation as resolved.
Show resolved Hide resolved

private const byte SB = (byte) 0xFA;
private const byte SE = (byte) 0xF0;

private enum ParseState {
Normal,
FoundIAC,
IACCommand,
SBStart,
SBValue,
SBIAC
}

// for jumbo frame size
private readonly MemoryStream _memoryStream = new MemoryStream(10000);
private ParseState _parseState = ParseState.Normal;
private EnumIacVerbs _currentVerb;

public class IacVerbReceivedEventArgs : EventArgs
{
public EnumIacVerbs Verb { get; set; }
public EnumIacOptions Option { get; set; }
}

public event EventHandler<IacVerbReceivedEventArgs> IacVerbReceived;

public IacFilter(ILogger logger)
{
_logger = logger;
}

public (byte[], int) ProcessIncomingClientData(byte[] clientData, int bytesReceived)
{
_memoryStream.SetLength(0);

for (int i = 0; i < bytesReceived; ++i)
paladine marked this conversation as resolved.
Show resolved Hide resolved
{
process(clientData[i]);
}

return (_memoryStream.GetBuffer(), (int)_memoryStream.Length);
}

private void process(byte b)
paladine marked this conversation as resolved.
Show resolved Hide resolved
{
switch (_parseState) {
case ParseState.Normal:
paladine marked this conversation as resolved.
Show resolved Hide resolved
if (b == IAC)
{
_parseState = ParseState.FoundIAC;
}
else
{
_memoryStream.WriteByte(b);
}
break;
case ParseState.FoundIAC:
if (b == SB)
{
_parseState = ParseState.SBStart;
}
else if (b == (byte)EnumIacVerbs.WILL || b == (byte)EnumIacVerbs.WONT || b == (byte)EnumIacVerbs.DO || b == (byte)EnumIacVerbs.DONT)
paladine marked this conversation as resolved.
Show resolved Hide resolved
{
_currentVerb = (EnumIacVerbs) b;
_parseState = ParseState.IACCommand;
}
else if (b == IAC)
{
// special escape sequence
_memoryStream.WriteByte(b);
}
else
{
_parseState = ParseState.Normal;
}
break;
case ParseState.IACCommand:
IacVerbReceived?.Invoke(this, new IacVerbReceivedEventArgs()
{
Verb = _currentVerb,
Option = (EnumIacOptions)b
});
_parseState = ParseState.Normal;
break;
case ParseState.SBStart:
_parseState = ParseState.SBValue;
break;
case ParseState.SBValue:
if (b == IAC)
{
_parseState = ParseState.SBIAC;
}
break;
case ParseState.SBIAC:
if (b == SE)
{
_parseState = ParseState.Normal;
}
else
{
_parseState = ParseState.SBValue;
}
break;
}
}
}
}
18 changes: 18 additions & 0 deletions MBBSEmu/Session/Telnet/IacResponse.cs
Expand Up @@ -27,5 +27,23 @@ public byte[] ToArray()
msOutput.WriteByte((byte)Option);
return msOutput.ToArray();
}

public override bool Equals(object obj)
{
return Equals(obj as IacResponse);
}

public bool Equals(IacResponse other)
{
if (other == null)
return false;

return (Verb == other.Verb && Option == other.Option);
}

public override int GetHashCode()
{
return Verb.GetHashCode() ^ Option.GetHashCode();
}
}
}