Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ClusterM/clovershell-client.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com>2017-04-11 23:38:06 +0300
committerAlexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com>2017-04-11 23:38:06 +0300
commit33c060c65554ad95b27c160525f4667ae803a93c (patch)
treef146cfeb4375c05d50773386d9e27f586bddeab0
parent92b21d8c46c84a853874639bb612c8cebd919894 (diff)
Real Telnet
-rw-r--r--CloverShell/ClovershellConnection.cs123
-rw-r--r--CloverShell/ExecConnection.cs2
-rw-r--r--CloverShell/ShellConnection.cs54
-rw-r--r--Program.cs6
4 files changed, 135 insertions, 50 deletions
diff --git a/CloverShell/ClovershellConnection.cs b/CloverShell/ClovershellConnection.cs
index 00c24c0..ae247c9 100644
--- a/CloverShell/ClovershellConnection.cs
+++ b/CloverShell/ClovershellConnection.cs
@@ -32,7 +32,7 @@ namespace com.clusterrr.clovershell
byte[] lastPingResponse = null;
DateTime lastAliveTime;
public delegate void OnClovershellConnected();
- public event OnClovershellConnected OnConnected = delegate{};
+ public event OnClovershellConnected OnConnected = delegate { };
internal enum ClovershellCommand
{
@@ -236,13 +236,14 @@ namespace com.clusterrr.clovershell
epReader = device.OpenEndpointReader((ReadEndpointID)inEndp, 65536);
epWriter = device.OpenEndpointWriter((WriteEndpointID)outEndp);
Debug.WriteLine("clovershell connected");
- // Kill all other serrions and drop all output
+ // Kill all other sessions and drop all output
killAll();
var body = new byte[65536];
int len;
while (epReader.Read(body, 50, out len) == ErrorCode.Ok) ;
epReader.ReadBufferSize = 65536;
epReader.DataReceived += epReader_DataReceived;
+ epReader.ReadThreadPriority = ThreadPriority.AboveNormal;
epReader.DataReceivedEnabled = true;
lastAliveTime = DateTime.Now;
online = true;
@@ -250,7 +251,7 @@ namespace com.clusterrr.clovershell
while (device.mUsbRegistry.IsAlive)
{
Thread.Sleep(100);
- if ((IdleTime.TotalSeconds >= 5) && (Ping() < 0))
+ if ((IdleTime.TotalSeconds >= 10) && (Ping() < 0))
throw new ClovershellException("no answer from device");
}
break;
@@ -261,7 +262,7 @@ namespace com.clusterrr.clovershell
}
catch (ClovershellException ex)
{
- Debug.WriteLine(ex.Message);
+ Debug.WriteLine(ex.Message + ex.StackTrace);
break;
}
}
@@ -301,19 +302,41 @@ namespace com.clusterrr.clovershell
if (!online) throw new ClovershellException("no clovershell connection, make sure your NES Mini connected, turned on and clovershell mod installed");
}
+ public void Disconnect()
+ {
+ try
+ {
+ if (device != null)
+ device.Close();
+ }
+ catch { }
+ }
+
void epReader_DataReceived(object sender, EndpointDataEventArgs e)
{
- var cmd = (ClovershellCommand)e.Buffer[0];
- var arg = e.Buffer[1];
- var len = e.Buffer[2] | (e.Buffer[3] * 0x100);
- proceedPacket(cmd, arg, e.Buffer, 4, len);
+#if VERY_DEBUG
+ Debug.WriteLine("<-[CLV] " + BitConverter.ToString(e.Buffer, 0, e.Count));
+#endif
+ int pos = 0;
+ int count = e.Count;
+ while (count > 0)
+ {
+ var cmd = (ClovershellCommand)e.Buffer[pos];
+ var arg = e.Buffer[pos + 1];
+ var len = e.Buffer[pos + 2] | (e.Buffer[pos + 3] * 0x100);
+ proceedPacket(cmd, arg, e.Buffer, pos + 4, len);
+ count -= len + 4;
+ pos += len + 4;
+ }
}
void proceedPacket(ClovershellCommand cmd, byte arg, byte[] data, int pos, int len)
{
if (len < 0)
len = data.Length;
- //Debug.WriteLine(string.Format("cmd={0}, arg={1:X2}, len={2}", cmd, arg, len));
+#if VERY_DEBUG
+ Debug.WriteLine(string.Format("<-[CLV] cmd={0}, arg={1:X2}, len={2}, data={3}", cmd, arg, len, BitConverter.ToString(data, pos, len)));
+#endif
lastAliveTime = DateTime.Now;
switch (cmd)
{
@@ -368,42 +391,52 @@ namespace com.clusterrr.clovershell
throw new ClovershellException("kill all exec: write error");
}
- internal void writeUsb(ClovershellCommand cmd, byte arg, byte[] data = null, int l = -1)
+ internal void writeUsb(ClovershellCommand cmd, byte arg, byte[] data = null, int pos = 0, int l = -1)
{
- if (!online) throw new ClovershellException("NES Mini is offline");
- var len = (l >= 0) ? l : ((data != null) ? data.Length : 0);
- var buff = new byte[len + 4];
- buff[0] = (byte)cmd;
- buff[1] = arg;
- buff[2] = (byte)(len & 0xFF);
- buff[3] = (byte)((len >> 8) & 0xFF);
- if (data != null)
- Array.Copy(data, 0, buff, 4, len);
- int tLen = 0;
- int pos = 0;
- len += 4;
- int repeats = 0;
- while (pos < len)
+ if (!IsOnline) throw new ClovershellException("NES Mini is offline");
+ if (epWriter == null) return;
+ lock (epWriter)
{
- var res = epWriter.Write(buff, pos, len, 1000, out tLen);
- pos += tLen;
- len -= tLen;
- if (res != ErrorCode.Ok)
+ var len = (l >= 0) ? l : ((data != null) ? (data.Length - pos) : 0);
+#if VERY_DEBUG
+ Debug.WriteLine(string.Format("->[CLV] cmd={0}, arg={1:X2}, len={2}, data={3}", cmd, arg, len, data != null ? BitConverter.ToString(data, pos, len) : ""));
+#endif
+ var buff = new byte[len + 4];
+ buff[0] = (byte)cmd;
+ buff[1] = arg;
+ buff[2] = (byte)(len & 0xFF);
+ buff[3] = (byte)((len >> 8) & 0xFF);
+ if (data != null)
+ Array.Copy(data, pos, buff, 4, len);
+ int tLen = 0;
+ pos = 0;
+ len += 4;
+ int repeats = 0;
+ while (pos < len)
{
- if (repeats >= 3) break;
- repeats++;
- Thread.Sleep(100);
+ var res = epWriter.Write(buff, pos, len, 1000, out tLen);
+#if VERY_DEBUG
+ Debug.WriteLine("->[CLV] " + BitConverter.ToString(buff, pos, len));
+#endif
+ pos += tLen;
+ len -= tLen;
+ if (res != ErrorCode.Ok)
+ {
+ if (repeats >= 3) break;
+ repeats++;
+ Thread.Sleep(100);
+ }
}
+ if (len > 0)
+ throw new ClovershellException("write error");
}
- if (len > 0)
- throw new ClovershellException("write error");
}
void shellListenerThreadLoop(object o)
{
+ var server = o as TcpListener;
try
{
- var server = o as TcpListener;
while (true)
{
while (!server.Pending()) Thread.Sleep(100);
@@ -419,7 +452,7 @@ namespace com.clusterrr.clovershell
{
Thread.Sleep(50);
t++;
- if (t >= 20)
+ if (t >= 50)
throw new ClovershellException("shell request timeout");
}
}
@@ -429,8 +462,10 @@ namespace com.clusterrr.clovershell
}
catch (ClovershellException ex)
{
+ Debug.WriteLine(ex.Message + ex.StackTrace);
+ if (connection.socket.Connected)
+ connection.socket.Send(Encoding.ASCII.GetBytes("Error: " + ex.Message));
connection.Dispose();
- Debug.WriteLine("Error: " + ex.Message + ex.StackTrace);
}
}
}
@@ -440,7 +475,11 @@ namespace com.clusterrr.clovershell
}
catch (ClovershellException ex)
{
- Debug.WriteLine(ex.Message);
+ Debug.WriteLine(ex.Message + ex.StackTrace);
+ }
+ finally
+ {
+ server.Stop();
}
shellEnabled = false;
}
@@ -467,7 +506,7 @@ namespace com.clusterrr.clovershell
{
try
{
- var connection = (from c in pendingExecConnections where c.command == command select c).First();
+ var connection = (from c in pendingExecConnections where c.command == command select c).Last();
pendingExecConnections.Remove(connection);
//Debug.WriteLine("Executing: " + command);
connection.id = arg;
@@ -560,6 +599,7 @@ namespace com.clusterrr.clovershell
}
public int Ping()
{
+ if (!IsOnline) throw new ClovershellException("NES Mini is offline");
var rnd = new Random();
var data = new byte[4];
rnd.NextBytes(data);
@@ -580,14 +620,13 @@ namespace com.clusterrr.clovershell
{
var stdOut = new MemoryStream();
Execute(command, null, stdOut, null, timeout, throwOnNonZero);
- var buff = new byte[stdOut.Length];
- stdOut.Seek(0, SeekOrigin.Begin);
- stdOut.Read(buff, 0, buff.Length);
+ var buff = stdOut.ToArray();
return Encoding.UTF8.GetString(buff).Trim();
}
public int Execute(string command, Stream stdin = null, Stream stdout = null, Stream stderr = null, int timeout = 0, bool throwOnNonZero = false)
{
+ if (!IsOnline) throw new ClovershellException("NES Mini is offline");
if (throwOnNonZero && stderr == null)
stderr = new MemoryStream();
using (var c = new ExecConnection(this, command, stdin, stdout, stderr))
@@ -601,7 +640,7 @@ namespace com.clusterrr.clovershell
{
Thread.Sleep(50);
t++;
- if (t >= 20)
+ if (t >= 50)
throw new ClovershellException("exec request timeout");
}
while (!c.finished)
diff --git a/CloverShell/ExecConnection.cs b/CloverShell/ExecConnection.cs
index 913d07b..2e68281 100644
--- a/CloverShell/ExecConnection.cs
+++ b/CloverShell/ExecConnection.cs
@@ -56,7 +56,7 @@ namespace com.clusterrr.clovershell
{
l = stdin.Read(buffer, 0, buffer.Length);
if (l > 0)
- connection.writeUsb(ClovershellConnection.ClovershellCommand.CMD_EXEC_STDIN, (byte)id, buffer, l);
+ connection.writeUsb(ClovershellConnection.ClovershellCommand.CMD_EXEC_STDIN, (byte)id, buffer, 0, l);
else
break;
LastDataTime = DateTime.Now;
diff --git a/CloverShell/ShellConnection.cs b/CloverShell/ShellConnection.cs
index c1be436..abd4eba 100644
--- a/CloverShell/ShellConnection.cs
+++ b/CloverShell/ShellConnection.cs
@@ -11,7 +11,7 @@ namespace com.clusterrr.clovershell
internal class ShellConnection : IDisposable
{
public readonly ClovershellConnection connection;
- Socket socket;
+ internal Socket socket;
internal int id;
internal Thread shellConnectionThread;
@@ -20,6 +20,9 @@ namespace com.clusterrr.clovershell
this.connection = connection;
this.socket = socket;
id = -1;
+ socket.Send(new byte[] { 0xFF, 0xFD, 0x03 }); // Do Suppress Go Ahead
+ socket.Send(new byte[] { 0xFF, 0xFB, 0x03 }); // Will Suppress Go Ahead
+ socket.Send(new byte[] { 0xFF, 0xFB, 0x01 }); // Will Echo
}
internal void shellConnectionLoop()
@@ -31,7 +34,47 @@ namespace com.clusterrr.clovershell
{
var l = socket.Receive(buff);
if (l > 0)
- connection.writeUsb(ClovershellConnection.ClovershellCommand.CMD_SHELL_IN, (byte)id, buff, l);
+ {
+ int start = 0;
+ int pos = 0;
+ do
+ {
+ if ((pos + 1 < l) && (buff[pos] == '\r') && (buff[pos + 1] == '\n')) // New line?
+ {
+ // Hey, dot not send \r\n! I'll cut it to \n
+ buff[pos] = (byte)'\n';
+ connection.writeUsb(ClovershellConnection.ClovershellCommand.CMD_SHELL_IN, (byte)id, buff, start, pos - start + 1);
+ pos += 2;
+ start = pos;
+ }
+ else if ((pos + 1 < l) && (buff[pos] == 0xFF)) // Telnet command?
+ {
+ if (buff[pos + 1] == 0xFF) // Or just 0xFF...
+ {
+ connection.writeUsb(ClovershellConnection.ClovershellCommand.CMD_SHELL_IN, (byte)id, buff, start, pos - start + 1);
+ pos += 2;
+ start = pos;
+ }
+ else if (pos + 2 < l)
+ {
+ if (pos - start > 0)
+ connection.writeUsb(ClovershellConnection.ClovershellCommand.CMD_SHELL_IN, (byte)id, buff, start, pos - start);
+ var cmd = buff[pos + 1]; // Telnet command code
+ var opt = buff[pos + 2]; // Telnet option code
+#if VERY_DEBUG
+ Debug.WriteLine(string.Format("Telnet command: CMD={0:X2} ARG={1:X2}", cmd, opt));
+#endif
+ pos += 3;
+ start = pos;
+ }
+ }
+ else pos++; // No, moving to next character
+ if ((pos == l) && (l - start > 0)) // End of packet
+ {
+ connection.writeUsb(ClovershellConnection.ClovershellCommand.CMD_SHELL_IN, (byte)id, buff, start, l - start);
+ }
+ } while (pos < l);
+ }
else
break;
}
@@ -39,8 +82,11 @@ namespace com.clusterrr.clovershell
catch (ThreadAbortException)
{
}
- catch (ClovershellException)
+ catch (ClovershellException ex)
{
+ Debug.WriteLine(ex.Message + ex.StackTrace);
+ if (socket.Connected)
+ socket.Send(Encoding.ASCII.GetBytes("Error: " + ex.Message));
}
finally
{
@@ -50,7 +96,7 @@ namespace com.clusterrr.clovershell
socket.Close();
connection.shellConnections[id] = null;
}
-
+
public void Dispose()
{
if (shellConnectionThread != null)
diff --git a/Program.cs b/Program.cs
index 72782a3..a5fca4c 100644
--- a/Program.cs
+++ b/Program.cs
@@ -44,8 +44,8 @@ namespace com.clusterrr.clovershell
nes.ShellPort = ushort.Parse(args[1]);
nes.ShellEnabled = true;
nes.AutoReconnect = true;
- Console.WriteLine("Started shell server on port {0}.", nes.ShellPort);
- Console.WriteLine("Connect to it using terminal client (raw mode, no local echo).");
+ Console.WriteLine("Started shell server on telnet://127.0.0.1:{0}.", nes.ShellPort);
+ Console.WriteLine("Connect to it using telnet client.");
Console.WriteLine("Press ENTER to stop.");
Console.ReadLine();
result = 0;
@@ -90,7 +90,7 @@ namespace com.clusterrr.clovershell
else stderr = Console.OpenStandardError();
var s = DateTime.Now;
result = nes.Execute(args[1], stdin, stdout, stderr);
- Console.Error.WriteLine("Done in {0}ms. Exit code: {1}", (int)(DateTime.Now - s).TotalMilliseconds, result);
+ //Console.Error.WriteLine("Done in {0}ms. Exit code: {1}", (int)(DateTime.Now - s).TotalMilliseconds, result);
break;
case "pull":
if (args.Length < 2)