From 697b3c67727485172253c1ec523e450e5c39fdb9 Mon Sep 17 00:00:00 2001 From: jrandom Date: Sun, 9 May 2004 07:16:04 +0000 Subject: [PATCH] SAM .net lib work in progress - dm and firerabbit --- .../src/SAM.NET/SAM.NET.Test/AssemblyInfo.cs | 58 ++++ .../src/SAM.NET/SAM.NET.Test/SAM.NET.Test.cs | 52 ++++ .../src/SAM.NET/SAM.NET/AssemblyInfo.cs | 58 ++++ .../sam/csharp/src/SAM.NET/SAM.NET/SAM.NET.cs | 271 ++++++++++++++++++ 4 files changed, 439 insertions(+) create mode 100644 apps/sam/csharp/src/SAM.NET/SAM.NET.Test/AssemblyInfo.cs create mode 100644 apps/sam/csharp/src/SAM.NET/SAM.NET.Test/SAM.NET.Test.cs create mode 100644 apps/sam/csharp/src/SAM.NET/SAM.NET/AssemblyInfo.cs create mode 100644 apps/sam/csharp/src/SAM.NET/SAM.NET/SAM.NET.cs diff --git a/apps/sam/csharp/src/SAM.NET/SAM.NET.Test/AssemblyInfo.cs b/apps/sam/csharp/src/SAM.NET/SAM.NET.Test/AssemblyInfo.cs new file mode 100644 index 000000000..9f89a3282 --- /dev/null +++ b/apps/sam/csharp/src/SAM.NET/SAM.NET.Test/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/apps/sam/csharp/src/SAM.NET/SAM.NET.Test/SAM.NET.Test.cs b/apps/sam/csharp/src/SAM.NET/SAM.NET.Test/SAM.NET.Test.cs new file mode 100644 index 000000000..bffc90912 --- /dev/null +++ b/apps/sam/csharp/src/SAM.NET/SAM.NET.Test/SAM.NET.Test.cs @@ -0,0 +1,52 @@ +using System; +using System.Net; +using System.Threading; +using System.Text; +using System.Collections; + +namespace SAM.NET +{ + class SAMTester + { + [STAThread] + static void Main(string[] args) + { + new SAMTester(); + } + public SAMTester () + { + SAMConnection connection1 = new SAMConnection(IPAddress.Parse("127.0.0.1"),7656); + SAMSession session1 = new SAMSession(connection1,SAM.NET.SamSocketType.Stream,"alice"); + + SAMConnection connection2 = new SAMConnection(IPAddress.Parse("127.0.0.1"),7656); + SAMSession session2 = new SAMSession(connection2,SAM.NET.SamSocketType.Stream,"bob"); + + SAMStream stream1 = new SAMStream(connection1,session1,233); + stream1.Connect(session2.getKey()); + + //Wait till we are connected to destination + while (!stream1.isConnected) + Thread.Sleep(1000); + + //Send some bytes + stream1.Write(Encoding.ASCII.GetBytes(DateTime.Now.ToLongTimeString() + "Hi!!!!!!")); + + //Wait till a stream magically appears on the other side + while (session2.getStreams().Count == 0) Thread.Sleep(1000); + + Thread.Sleep(1000); + while (true) {} + foreach (SAMStream stream in session2.getStreams().Values) + { + Console.WriteLine("Text received on " + stream.getID() + " at " + DateTime.Now.ToLongTimeString()); + Console.WriteLine(Encoding.ASCII.GetString(stream.ReadToEnd())); + stream.Close(); + } + while (true) {} + + stream1.Close(); + connection1.Close(); + connection2.Close(); + } + } +} diff --git a/apps/sam/csharp/src/SAM.NET/SAM.NET/AssemblyInfo.cs b/apps/sam/csharp/src/SAM.NET/SAM.NET/AssemblyInfo.cs new file mode 100644 index 000000000..9f89a3282 --- /dev/null +++ b/apps/sam/csharp/src/SAM.NET/SAM.NET/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/apps/sam/csharp/src/SAM.NET/SAM.NET/SAM.NET.cs b/apps/sam/csharp/src/SAM.NET/SAM.NET/SAM.NET.cs new file mode 100644 index 000000000..96ff4e09b --- /dev/null +++ b/apps/sam/csharp/src/SAM.NET/SAM.NET/SAM.NET.cs @@ -0,0 +1,271 @@ +using System; +using System.Net.Sockets; +using System.Text; +using System.Net; +using System.IO; +using System.Collections; +using System.Threading; + +namespace SAM.NET +{ + public enum SamSocketType + { + Stream, + Datagram, + Raw + } + + public class SAMConnection + { + private const string propertyMinVersion = "1.0"; + private const string propertyMaxVersion = "1.0"; + + private Socket _sock; + private NetworkStream _sockStream; + private StreamReader _sockStreamIn; + private StreamWriter _sockStreamOut; + + public SAMConnection(IPAddress routerIP, int port) + { + _sock = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); + IPEndPoint rEP = new IPEndPoint(routerIP,port); + _sock.Connect(rEP); + _sockStream = new NetworkStream(_sock); + _sockStreamIn = new StreamReader(_sockStream); + _sockStreamOut = new StreamWriter(_sockStream); + try + { + sendVersion(propertyMinVersion,propertyMinVersion); + } + catch (Exception e) + { + _sock.Close(); + throw (new Exception("No SAM for you :(")); + } + + } + + private void sendVersion(string min, string max) + { + _sockStreamOut.WriteLine("HELLO VERSION MIN=" + propertyMinVersion + " MAX=" + propertyMaxVersion); + _sockStreamOut.Flush(); + Hashtable response = SAMUtil.parseKeyValues(_sockStreamIn.ReadLine(),2); + if (response["RESULT"].ToString() != "OK") throw (new Exception("Version mismatch")); + } + + public StreamWriter getOutputStream() + { + return _sockStreamOut; + } + + public StreamReader getInputStream() + { + return _sockStreamIn; + } + + public NetworkStream getStream() + { + return _sockStream; + } + + public void Close() + { + _sock.Close(); + } + } + + /* + * Creating a SAMSession object will automatically: + * 1) create a sesion on SAM + * 1) query for the base64key + * 2) start a listening thread to catch all stream commands + */ + public class SAMSession + { + private Hashtable _streams; + private string _sessionKey; + + public SAMSession (SAMConnection connection, SamSocketType type, string destination) + { + _streams = new Hashtable(); + StreamWriter writer = connection.getOutputStream(); + StreamReader reader = connection.getInputStream(); + writer.WriteLine("SESSION CREATE STYLE=STREAM DESTINATION=" + destination); + writer.Flush(); + Hashtable response = SAMUtil.parseKeyValues(reader.ReadLine(),2); + if (response["RESULT"].ToString() != "OK") + { + throw (new Exception(response["MESSAGE"].ToString())); + } + else + { + writer.WriteLine("NAMING LOOKUP NAME=ME"); + writer.Flush(); + response = SAMUtil.parseKeyValues(reader.ReadLine(),2); + _sessionKey = response["VALUE"].ToString(); + SAMSessionListener listener = new SAMSessionListener(connection,this,_streams); + new Thread(new ThreadStart(listener.startListening)).Start(); + } + } + public void addStream(SAMStream stream) + { + _streams.Add(stream.getID(),stream); + } + public string getKey() + { + return _sessionKey; + } + public Hashtable getStreams() + { + return _streams; + } + } + + public class SAMSessionListener + { + private Hashtable _streams; + private SAMConnection _connection; + private SAMSession _session; + private bool stayAlive = true; + + public SAMSessionListener(SAMConnection connection,SAMSession session, Hashtable streams) + { + _streams = streams; + _connection = connection; + _session = session; + } + public void startListening() + { + StreamReader reader = _connection.getInputStream(); + while (stayAlive) + { + string response = reader.ReadLine(); + if (response.StartsWith("STREAM STATUS")) + { + Hashtable values = SAMUtil.parseKeyValues(response,2); + SAMStream theStream = (SAMStream)_streams[int.Parse(values["ID"].ToString())]; + if (theStream != null) theStream.ReceivedStatus(values); + } + if (response.StartsWith("STREAM CONNECTED")) + { + Hashtable values = SAMUtil.parseKeyValues(response,2); + SAMStream theStream = (SAMStream)_streams[int.Parse(values["ID"].ToString())]; + if (theStream != null) theStream.isConnected = true; + } + if (response.StartsWith("STREAM RECEIVED")) + { + Hashtable values = SAMUtil.parseKeyValues(response,2); + int streamID = int.Parse(values["ID"].ToString()); + SAMStream theStream = (SAMStream)_streams[streamID]; + if (theStream == null) new SAMStream(_connection,_session,streamID); + theStream = (SAMStream)_streams[streamID]; + theStream.ReceivedData(int.Parse(values["SIZE"].ToString())); + } + if (response.StartsWith("STREAM CLOSE")) + { + Hashtable values = SAMUtil.parseKeyValues(response,2); + SAMStream theStream = (SAMStream)_streams[int.Parse(values["ID"].ToString())]; + if (theStream != null) theStream.isConnected = false; + } + } + } + } + + public class SAMStream + { + private int _ID; + private byte[] _data; + private int _position=0; + private int _size=0; + private SAMSession _session; + private SAMConnection _connection; + public bool isConnected=false; + + public SAMStream (SAMConnection connection,SAMSession session, int ID) + { + _data = new byte[100000]; //FIXME: change to non-static structure for storing stream data + _ID = ID; + _connection = connection; + _session = session; + _session.addStream(this); + } + + public void Connect(string destination) + { + StreamWriter writer = _connection.getOutputStream(); + writer.WriteLine("STREAM CONNECT ID=" + _ID.ToString() + " DESTINATION=" + destination); + writer.Flush(); + } + + public void ReceivedData(int size) //FIXME: WTF is going on when reading the payload here? All zeros and way too many of them. + { + NetworkStream stream = _connection.getStream(); + int bytesRead = stream.Read(_data,_size,size); + _size = _size + bytes; + } + + public void ReceivedStatus(Hashtable response) + { + if (response["RESULT"].ToString() != "OK") + { + throw (new Exception(response["RESULT"].ToString())); + } + else + { + isConnected = true; + } + } + + public int getID() {return _ID;} + + public bool DataAvailable() + { + return _position != _size; + } + + public void Write(byte[] buf) + { + NetworkStream stream = _connection.getStream(); + int sent = 0; + while (sent < buf.Length) + { + int toSend = Math.Min(buf.Length - sent,32768); + string header = "STREAM SEND ID=" + _ID.ToString() + " SIZE=" + toSend.ToString() + "\n"; + byte[] headerbytes = Encoding.ASCII.GetBytes(header); + stream.Write(headerbytes,0,headerbytes.Length); + stream.Write(buf,sent,toSend); + sent = sent + toSend; + } + } + + public byte[] ReadToEnd() + { + byte[] ret = new byte[_size - _position]; + Array.Copy(_data,_position,ret,0,_size - _position); + _position = _size; + return ret; + } + + public void Close() + { + StreamWriter writer = _connection.getOutputStream(); + writer.WriteLine("STREAM CLOSE " + _ID.ToString()); + writer.Flush(); + } + } + + public class SAMUtil + { + public static Hashtable parseKeyValues(string str, int startingWord) + { + Hashtable hash = new Hashtable(); + string strTruncated = string.Join(" ",str.Split(' '),startingWord,str.Split(' ').Length - startingWord); + string[] sets = strTruncated.Split('=',' '); + for (int i=0; i