From a1084d2beab383fb40d2201c083b64e260d800c0 Mon Sep 17 00:00:00 2001 From: Alexander Eckart Date: Sat, 4 May 2013 15:57:04 +0200 Subject: [PATCH 01/23] added ClientServerLib Project (not tested) --- .gitignore | 2 + ClientServerLib/ClientServerLib.sln | 20 + ClientServerLib/ClientServerLib/CClient.cs | 257 ++ .../ClientServerLib/CConnection.cs | 145 + ClientServerLib/ClientServerLib/CServer.cs | 162 + .../ClientServerLib/ClientServerLib.csproj | 60 + .../ClientServerLib/DeffieHellman/BigInt.cs | 2974 +++++++++++++++++ .../DeffieHellman/DeffieHellman.cs | 178 + .../DeffieHellman/StrongNumberProvider.cs | 33 + .../Properties/AssemblyInfo.cs | 36 + ClientServerLib/ClientServerLib/Structs.cs | 17 + 11 files changed, 3884 insertions(+) create mode 100644 ClientServerLib/ClientServerLib.sln create mode 100644 ClientServerLib/ClientServerLib/CClient.cs create mode 100644 ClientServerLib/ClientServerLib/CConnection.cs create mode 100644 ClientServerLib/ClientServerLib/CServer.cs create mode 100644 ClientServerLib/ClientServerLib/ClientServerLib.csproj create mode 100644 ClientServerLib/ClientServerLib/DeffieHellman/BigInt.cs create mode 100644 ClientServerLib/ClientServerLib/DeffieHellman/DeffieHellman.cs create mode 100644 ClientServerLib/ClientServerLib/DeffieHellman/StrongNumberProvider.cs create mode 100644 ClientServerLib/ClientServerLib/Properties/AssemblyInfo.cs create mode 100644 ClientServerLib/ClientServerLib/Structs.cs diff --git a/.gitignore b/.gitignore index ca5aa552..a61b7628 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ Output/Songs *.Cache /GstreamerHelper/bin/* /VocaluxeLib/bin +/ClientServerLib\ClientServerLib/ClientServerLib.csproj.user +/ClientServerLib\ClientServerLib\bin\Debug/ClientServerLib.dll diff --git a/ClientServerLib/ClientServerLib.sln b/ClientServerLib/ClientServerLib.sln new file mode 100644 index 00000000..7ebf309a --- /dev/null +++ b/ClientServerLib/ClientServerLib.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual C# Express 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClientServerLib", "ClientServerLib\ClientServerLib.csproj", "{EC5167E4-CCFB-4CEE-987C-CF816F1062A3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/ClientServerLib/ClientServerLib/CClient.cs b/ClientServerLib/ClientServerLib/CClient.cs new file mode 100644 index 00000000..75de55f3 --- /dev/null +++ b/ClientServerLib/ClientServerLib/CClient.cs @@ -0,0 +1,257 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; + + +namespace ClientServerLib +{ + public class CClient + { + private bool doConnect; + private bool connected; + private bool running; + private int bufferLength = 8192; + + private Thread clientThread; + private IPEndPoint serverEndPoint; + private string ip; + private int port; + + private CConnection connection; + private List requests; + private List responses; + + public bool Connected + { + get { return connected; } + } + + public bool LoggedIn + { + get { return connection.LoggedIn; } + set { connection.LoggedIn = value; } + } + + public CClient() + { + doConnect = false; + connected = false; + running = true; + requests = new List(); + responses = new List(); + + connection = new CConnection(new TcpClient(), -1); + + clientThread = new Thread(new ThreadStart(Runner)); + clientThread.Start(); + } + + public void Connect(string IP = "127.0.0.1", int Port = 3000) + { + if (!doConnect) + { + ip = IP; + port = Port; + doConnect = true; + } + } + + public void Disconnect() + { + if (doConnect) + { + doConnect = false; + + lock (requests) + { + requests.Clear(); + } + + lock (responses) + { + responses.Clear(); + } + } + } + + public void Close() + { + Disconnect(); + running = false; + clientThread.Join(100); + } + + public void SendMessage(byte[] Message, ResponseCallback Callback) + { + SRequest req = new SRequest(); + req.Command = Message; + req.Callback = Callback; + req.Response = null; + + lock (requests) + { + requests.Add(req); + } + } + + private void Runner() + { + while (running) + { + if (!connected && doConnect) + { + try + { + serverEndPoint = new IPEndPoint(IPAddress.Parse(ip), port); + connection.TcpClient.Connect(serverEndPoint); + connected = true; + } + catch + { + connected = false; + for (int i = 0; i < 500; i++) + { + if (doConnect) + Thread.Sleep(10); + else + break; + } + + } + } + + if (connected && !doConnect) + { + try + { + connection.TcpClient.Close(); + } + catch + { + } + + connected = false; + } + + if (doConnect && connected) + { + try + { + Communicate(); + } + catch + { + connected = false; + } + + } + } + } + + private void Communicate() + { + NetworkStream clientStream = connection.TcpClient.GetStream(); + + byte[] data = new byte[bufferLength]; + int bytesRead; + + if (!connection.KeySet) + { + bytesRead = 0; + + try + { + bytesRead = clientStream.Read(data, 0, bufferLength); + } + catch + { + return; + } + + if (bytesRead == 0) + return; + + byte[] serverParam = new byte[bytesRead]; + Array.Copy(data, serverParam, bytesRead); + byte[] clientResponse = connection.CreateClientKey(serverParam); + clientStream.Write(clientResponse, 0, clientResponse.Length); + clientStream.Flush(); + } + else + { + lock (requests) + { + while (requests.Count > 0) + { + SRequest res = HandleRequest(requests[0]); + responses.Add(res); + requests.RemoveAt(0); + } + } + + lock (responses) + { + while (responses.Count > 0) + { + try + { + responses[0].Callback(responses[0].Response); + } + finally + { + responses.RemoveAt(0); + } + } + } + + } + } + + private SRequest HandleRequest(SRequest request) + { + SRequest response = new SRequest(); + response.Command = request.Command; + response.Callback = request.Callback; + response.Response = null; + + NetworkStream clientStream = connection.TcpClient.GetStream(); + + byte[] encrypted = connection.Encrypt(request.Command); + + clientStream.Write(encrypted, 0, encrypted.Length); + clientStream.Flush(); + + byte[] data = new byte[bufferLength]; + int bytesRead = 0; + + try + { + bytesRead = clientStream.Read(data, 0, bufferLength); + } + catch + { + return response; + } + + if (bytesRead == 0) + return response; + + int messageLength = BitConverter.ToInt32(data, 0); + while (messageLength > bytesRead) + { + Array.Resize(ref data, data.Length + bufferLength); + bytesRead += clientStream.Read(data, data.Length - bufferLength, bufferLength); + } + + encrypted = new byte[bytesRead]; + Array.Copy(data, encrypted, bytesRead); + response.Response = connection.Decrypt(encrypted); + + return response; + } + } +} diff --git a/ClientServerLib/ClientServerLib/CConnection.cs b/ClientServerLib/ClientServerLib/CConnection.cs new file mode 100644 index 00000000..50aa3460 --- /dev/null +++ b/ClientServerLib/ClientServerLib/CConnection.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Sockets; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +using DiffieHellman; + +namespace ClientServerLib +{ + internal class CConnection + { + private bool keySet; + private CDiffieHellman dh; + private UTF8Encoding encoder; + private int connectionID; + + public TcpClient TcpClient; + public bool LoggedIn; + + public bool KeySet + { + get { return keySet; } + } + + public int ConnectionID + { + get { return connectionID; } + } + + public CConnection(TcpClient client, int ConnectionID) + { + TcpClient = client; + keySet = false; + LoggedIn = false; + connectionID = ConnectionID; + encoder = new UTF8Encoding(); + } + + public byte[] GetKeyParams() + { + if (dh == null) + dh = new CDiffieHellman(256).GenerateRequest(); + + return encoder.GetBytes(dh.ToString()); + } + + public byte[] CreateClientKey(byte[] serverParam) + { + if (serverParam == null) + return null; + + dh = new CDiffieHellman(256).GenerateResponse( + encoder.GetString(serverParam, 0, serverParam.Length)); + + keySet = true; + return encoder.GetBytes(dh.ToString()); + } + + public void CreateServerKey(byte[] clientResponse) + { + if (dh == null) + return; + + dh.HandleResponse(encoder.GetString(clientResponse, 0, clientResponse.Length)); + keySet = true; + } + + public byte[] Encrypt(byte[] Data) + { + if (Data == null || !keySet) + return Data; + + using (Aes aes = Aes.Create()) + { + aes.Key = dh.Key; + aes.GenerateIV(); + + byte[] dataLength = BitConverter.GetBytes(Data.Length); + + using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV)) + using (var memoryStream = new MemoryStream()) + using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write)) + { + cryptoStream.Write(Data, 0, Data.Length); + cryptoStream.FlushFinalBlock(); + + byte[] encrypted = memoryStream.ToArray(); + + int len = 4 + aes.IV.Length + dataLength.Length + encrypted.Length; + byte[] messageLength = BitConverter.GetBytes(len); + + MemoryStream stream = new MemoryStream(); + stream.Write(messageLength, 0, messageLength.Length); + stream.Write(aes.IV, 0, aes.IV.Length); + stream.Write(dataLength, 0, dataLength.Length); + stream.Write(encrypted, 0, encrypted.Length); + + return stream.ToArray(); + } + } + } + + public byte[] Decrypt(byte[] Data) + { + if (Data == null || !keySet) + return Data; + + if (Data.Length < 25) + return null; + + int messageLength = BitConverter.ToInt32(Data, 0); + if (messageLength > Data.Length) + return null; + + byte[] IV = new byte[16]; + Array.Copy(Data, 4, IV, 0, 16); + int dataLength = BitConverter.ToInt32(Data, 20); + + using (Aes aes = Aes.Create()) + { + aes.Key = dh.Key; + aes.IV = IV; + + byte[] encrypted = new byte[messageLength - 24]; + Array.Copy(Data, 24, encrypted, 0, encrypted.Length); + + using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV)) + using (var memoryStream = new MemoryStream()) + using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Write)) + { + cryptoStream.Write(encrypted, 0, encrypted.Length); + cryptoStream.FlushFinalBlock(); + cryptoStream.Close(); + byte[] decrypted = memoryStream.ToArray(); + byte[] message = new byte[dataLength]; + Array.Copy(decrypted, message, dataLength); + return message; + } + } + } + } +} diff --git a/ClientServerLib/ClientServerLib/CServer.cs b/ClientServerLib/ClientServerLib/CServer.cs new file mode 100644 index 00000000..24e54557 --- /dev/null +++ b/ClientServerLib/ClientServerLib/CServer.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; + +namespace ClientServerLib +{ + public class CServer + { + private TcpListener tcpListener; + private Thread listenThread; + private int port; + private int bufferLength = 8192; + private bool running; + + private HandleRequest requestCallback; + + private Dictionary clients; + private Queue ids; + + public CServer(HandleRequest RequestCallback, int Port = 3000) + { + port = Port; + running = false; + requestCallback = RequestCallback; + + clients = new Dictionary(); + ids = new Queue(1000); + + for (int i = 0; i < 1000; i++) + ids.Enqueue(i); + } + + public void Start() + { + if (!running) + { + running = true; + + tcpListener = new TcpListener(IPAddress.Any, port); + + listenThread = new Thread(new ThreadStart(ListenForClients)); + listenThread.Start(); + } + } + + public void Stop() + { + if (running) + { + running = false; + tcpListener.Stop(); + Thread.Sleep(100); + listenThread.Join(); + + lock (clients) + { + int[] cIDs = new int[clients.Count]; + clients.Keys.CopyTo(cIDs, 0); + + foreach (int id in cIDs) + { + clients.Remove(id); + ids.Enqueue(id); + } + } + } + } + + private void ListenForClients() + { + tcpListener.Start(); + + while (running) + { + //wait for new client + TcpClient client = tcpListener.AcceptTcpClient(); + + int id = ids.Dequeue(); + CConnection connection = new CConnection(client, id); + + lock (clients) + { + clients.Add(id, connection); + } + + Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientCom)); + + NetworkStream clientStream = client.GetStream(); + byte[] serverParams = connection.GetKeyParams(); + clientStream.Write(serverParams, 0, serverParams.Length); + clientStream.Flush(); + + clientThread.Start(connection); + } + } + + private void HandleClientCom(object client) + { + CConnection connection = (CConnection)client; + TcpClient tcpClient = (TcpClient)connection.TcpClient; + NetworkStream clientStream = tcpClient.GetStream(); + + byte[] data = new byte[bufferLength]; + int bytesRead; + + while (running) + { + bytesRead = 0; + + try + { + bytesRead = clientStream.Read(data, 0, bufferLength); + } + catch + { + break; + } + + if (bytesRead == 0) + break; + + if (!connection.KeySet) + { + byte[] clientResponse = new byte[bytesRead]; + Array.Copy(data, clientResponse, bytesRead); + connection.CreateServerKey(clientResponse); + } + else + { + //receive message + int messageLength = BitConverter.ToInt32(data, 0); + while (messageLength > bytesRead) + { + Array.Resize(ref data, data.Length + bufferLength); + bytesRead += clientStream.Read(data, data.Length - bufferLength, bufferLength); + } + + byte[] message = new byte[bytesRead]; + Array.Copy(data, message, bytesRead); + byte[] decrypted = connection.Decrypt(message); + + byte[] answer = HandleClientMessage(decrypted, connection); + + //send message + byte[] encrypted = connection.Encrypt(answer); + + clientStream.Write(encrypted, 0, encrypted.Length); + clientStream.Flush(); + } + } + } + + private byte[] HandleClientMessage(byte[] Message, CConnection connection) + { + return requestCallback(connection.ConnectionID, Message); + } + } +} diff --git a/ClientServerLib/ClientServerLib/ClientServerLib.csproj b/ClientServerLib/ClientServerLib/ClientServerLib.csproj new file mode 100644 index 00000000..4a958128 --- /dev/null +++ b/ClientServerLib/ClientServerLib/ClientServerLib.csproj @@ -0,0 +1,60 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {EC5167E4-CCFB-4CEE-987C-CF816F1062A3} + Library + Properties + ClientServerLib + ClientServerLib + v4.0 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ClientServerLib/ClientServerLib/DeffieHellman/BigInt.cs b/ClientServerLib/ClientServerLib/DeffieHellman/BigInt.cs new file mode 100644 index 00000000..4908c600 --- /dev/null +++ b/ClientServerLib/ClientServerLib/DeffieHellman/BigInt.cs @@ -0,0 +1,2974 @@ +//************************************************************************************ +// BigInteger Class Version 1.03 +// +// Copyright (c) 2002 Chew Keong TAN +// All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, provided that the above +// copyright notice(s) and this permission notice appear in all copies of +// the Software and that both the above copyright notice(s) and this +// permission notice appear in supporting documentation. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +// OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +// HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL +// INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING +// FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +// NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +// WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// +// Disclaimer +// ---------- +// Although reasonable care has been taken to ensure the correctness of this +// implementation, this code should never be used in any application without +// proper verification and testing. I disclaim all liability and responsibility +// to any person or entity with respect to any loss or damage caused, or alleged +// to be caused, directly or indirectly, by the use of this BigInteger class. +// +// Comments, bugs and suggestions to +// (http://www.codeproject.com/csharp/biginteger.asp) +// +// +// Overloaded Operators +, -, *, /, %, >>, <<, ==, !=, >, <, >=, <=, &, |, ^, ++, --, ~ +// +// Features +// -------- +// 1) Arithmetic operations involving large signed integers (2's complement). +// 2) Primality test using Fermat little theorm, Rabin Miller's method, +// Solovay Strassen's method and Lucas strong pseudoprime. +// 3) Modulo exponential with Barrett's reduction. +// 4) Inverse modulo. +// 5) Pseudo prime generation. +// 6) Co-prime generation. +// +// +// Known Problem +// ------------- +// This pseudoprime passes my implementation of +// primality test but failed in JDK's isProbablePrime test. +// +// byte[] pseudoPrime1 = { (byte)0x00, +// (byte)0x85, (byte)0x84, (byte)0x64, (byte)0xFD, (byte)0x70, (byte)0x6A, +// (byte)0x9F, (byte)0xF0, (byte)0x94, (byte)0x0C, (byte)0x3E, (byte)0x2C, +// (byte)0x74, (byte)0x34, (byte)0x05, (byte)0xC9, (byte)0x55, (byte)0xB3, +// (byte)0x85, (byte)0x32, (byte)0x98, (byte)0x71, (byte)0xF9, (byte)0x41, +// (byte)0x21, (byte)0x5F, (byte)0x02, (byte)0x9E, (byte)0xEA, (byte)0x56, +// (byte)0x8D, (byte)0x8C, (byte)0x44, (byte)0xCC, (byte)0xEE, (byte)0xEE, +// (byte)0x3D, (byte)0x2C, (byte)0x9D, (byte)0x2C, (byte)0x12, (byte)0x41, +// (byte)0x1E, (byte)0xF1, (byte)0xC5, (byte)0x32, (byte)0xC3, (byte)0xAA, +// (byte)0x31, (byte)0x4A, (byte)0x52, (byte)0xD8, (byte)0xE8, (byte)0xAF, +// (byte)0x42, (byte)0xF4, (byte)0x72, (byte)0xA1, (byte)0x2A, (byte)0x0D, +// (byte)0x97, (byte)0xB1, (byte)0x31, (byte)0xB3, +// }; +// +// +// Change Log +// ---------- +// 1) September 23, 2002 (Version 1.03) +// - Fixed operator- to give correct data length. +// - Added Lucas sequence generation. +// - Added Strong Lucas Primality test. +// - Added integer square root method. +// - Added setBit/unsetBit methods. +// - New isProbablePrime() method which do not require the +// confident parameter. +// +// 2) August 29, 2002 (Version 1.02) +// - Fixed bug in the exponentiation of negative numbers. +// - Faster modular exponentiation using Barrett reduction. +// - Added getBytes() method. +// - Fixed bug in ToHexString method. +// - Added overloading of ^ operator. +// - Faster computation of Jacobi symbol. +// +// 3) August 19, 2002 (Version 1.01) +// - Big integer is stored and manipulated as unsigned integers (4 bytes) instead of +// individual bytes this gives significant performance improvement. +// - Updated Fermat's Little Theorem test to use a^(p-1) mod p = 1 +// - Added isProbablePrime method. +// - Updated documentation. +// +// 4) August 9, 2002 (Version 1.0) +// - Initial Release. +// +// +// References +// [1] D. E. Knuth, "Seminumerical Algorithms", The Art of Computer Programming Vol. 2, +// 3rd Edition, Addison-Wesley, 1998. +// +// [2] K. H. Rosen, "Elementary Number Theory and Its Applications", 3rd Ed, +// Addison-Wesley, 1993. +// +// [3] B. Schneier, "Applied Cryptography", 2nd Ed, John Wiley & Sons, 1996. +// +// [4] A. Menezes, P. van Oorschot, and S. Vanstone, "Handbook of Applied Cryptography", +// CRC Press, 1996, www.cacr.math.uwaterloo.ca/hac +// +// [5] A. Bosselaers, R. Govaerts, and J. Vandewalle, "Comparison of Three Modular +// Reduction Functions," Proc. CRYPTO'93, pp.175-186. +// +// [6] R. Baillie and S. S. Wagstaff Jr, "Lucas Pseudoprimes", Mathematics of Computation, +// Vol. 35, No. 152, Oct 1980, pp. 1391-1417. +// +// [7] H. C. Williams, "Édouard Lucas and Primality Testing", Canadian Mathematical +// Society Series of Monographs and Advance Texts, vol. 22, John Wiley & Sons, New York, +// NY, 1998. +// +// [8] P. Ribenboim, "The new book of prime number records", 3rd edition, Springer-Verlag, +// New York, NY, 1995. +// +// [9] M. Joye and J.-J. Quisquater, "Efficient computation of full Lucas sequences", +// Electronics Letters, 32(6), 1996, pp 537-538. +// +//************************************************************************************ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace DiffieHellman +{ + internal class BigInteger : IDisposable + { + // maximum length of the BigInteger in uint (4 bytes) + // change this to suit the required level of precision. + + private const int maxLength = 50; + + // primes smaller than 2000 to test the generated prime number + + public static readonly int[] primesBelow2000 = { + 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, + 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, + 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, + 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, + 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, + 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, + 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, + 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, + 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, + 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, + 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, + 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, + 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, + 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, + 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, + 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, + 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, + 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, + 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, + 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999 }; + + + private uint[] data = null; // stores bytes from the Big Integer + public int dataLength; // number of actual chars used + + + //*********************************************************************** + // Constructor (Default value for BigInteger is 0 + //*********************************************************************** + + public BigInteger() + { + data = new uint[maxLength]; + dataLength = 1; + } + + + //*********************************************************************** + // Constructor (Default value provided by long) + //*********************************************************************** + + public BigInteger(long value) + { + data = new uint[maxLength]; + long tempVal = value; + + // copy bytes from long to BigInteger without any assumption of + // the length of the long datatype + + dataLength = 0; + while (value != 0 && dataLength < maxLength) + { + data[dataLength] = (uint)(value & 0xFFFFFFFF); + value >>= 32; + dataLength++; + } + + if (tempVal > 0) // overflow check for +ve value + { + if (value != 0 || (data[maxLength - 1] & 0x80000000) != 0) + throw (new ArithmeticException("Positive overflow in constructor.")); + } + else if (tempVal < 0) // underflow check for -ve value + { + if (value != -1 || (data[dataLength - 1] & 0x80000000) == 0) + throw (new ArithmeticException("Negative underflow in constructor.")); + } + + if (dataLength == 0) + dataLength = 1; + } + + + //*********************************************************************** + // Constructor (Default value provided by ulong) + //*********************************************************************** + + public BigInteger(ulong value) + { + data = new uint[maxLength]; + + // copy bytes from ulong to BigInteger without any assumption of + // the length of the ulong datatype + + dataLength = 0; + while (value != 0 && dataLength < maxLength) + { + data[dataLength] = (uint)(value & 0xFFFFFFFF); + value >>= 32; + dataLength++; + } + + if (value != 0 || (data[maxLength - 1] & 0x80000000) != 0) + throw (new ArithmeticException("Positive overflow in constructor.")); + + if (dataLength == 0) + dataLength = 1; + } + + + + //*********************************************************************** + // Constructor (Default value provided by BigInteger) + //*********************************************************************** + + public BigInteger(BigInteger bi) + { + data = new uint[maxLength]; + + dataLength = bi.dataLength; + + for (int i = 0; i < dataLength; i++) + data[i] = bi.data[i]; + } + + + //*********************************************************************** + // Constructor (Default value provided by a string of digits of the + // specified base) + // + // Example (base 10) + // ----------------- + // To initialize "a" with the default value of 1234 in base 10 + // BigInteger a = new BigInteger("1234", 10) + // + // To initialize "a" with the default value of -1234 + // BigInteger a = new BigInteger("-1234", 10) + // + // Example (base 16) + // ----------------- + // To initialize "a" with the default value of 0x1D4F in base 16 + // BigInteger a = new BigInteger("1D4F", 16) + // + // To initialize "a" with the default value of -0x1D4F + // BigInteger a = new BigInteger("-1D4F", 16) + // + // Note that string values are specified in the + // format. + // + //*********************************************************************** + + public BigInteger(string value, int radix) + { + BigInteger multiplier = new BigInteger(1); + BigInteger result = new BigInteger(); + value = (value.ToUpper()).Trim(); + int limit = 0; + + if (value[0] == '-') + limit = 1; + + for (int i = value.Length - 1; i >= limit; i--) + { + int posVal = (int)value[i]; + + if (posVal >= '0' && posVal <= '9') + posVal -= '0'; + else if (posVal >= 'A' && posVal <= 'Z') + posVal = (posVal - 'A') + 10; + else + posVal = 9999999; // arbitrary large + + + if (posVal >= radix) + throw (new ArithmeticException("Invalid string in constructor.")); + else + { + if (value[0] == '-') + posVal = -posVal; + + result = result + (multiplier * posVal); + + if ((i - 1) >= limit) + multiplier = multiplier * radix; + } + } + + if (value[0] == '-') // negative values + { + if ((result.data[maxLength - 1] & 0x80000000) == 0) + throw (new ArithmeticException("Negative underflow in constructor.")); + } + else // positive values + { + if ((result.data[maxLength - 1] & 0x80000000) != 0) + throw (new ArithmeticException("Positive overflow in constructor.")); + } + + data = new uint[maxLength]; + for (int i = 0; i < result.dataLength; i++) + data[i] = result.data[i]; + + dataLength = result.dataLength; + } + + + //*********************************************************************** + // Constructor (Default value provided by an array of bytes) + // + // The lowest index of the input byte array (i.e [0]) should contain the + // most significant byte of the number, and the highest index should + // contain the least significant byte. + // + // E.g. + // To initialize "a" with the default value of 0x1D4F in base 16 + // byte[] temp = { 0x1D, 0x4F }; + // BigInteger a = new BigInteger(temp) + // + // Note that this method of initialization does not allow the + // sign to be specified. + // + //*********************************************************************** + + public BigInteger(byte[] inData) + { + dataLength = inData.Length >> 2; + + int leftOver = inData.Length & 0x3; + if (leftOver != 0) // length not multiples of 4 + dataLength++; + + + if (dataLength > maxLength) + throw (new ArithmeticException("Byte overflow in constructor.")); + + data = new uint[maxLength]; + + for (int i = inData.Length - 1, j = 0; i >= 3; i -= 4, j++) + { + data[j] = (uint)((inData[i - 3] << 24) + (inData[i - 2] << 16) + + (inData[i - 1] << 8) + inData[i]); + } + + if (leftOver == 1) + data[dataLength - 1] = (uint)inData[0]; + else if (leftOver == 2) + data[dataLength - 1] = (uint)((inData[0] << 8) + inData[1]); + else if (leftOver == 3) + data[dataLength - 1] = (uint)((inData[0] << 16) + (inData[1] << 8) + inData[2]); + + + while (dataLength > 1 && data[dataLength - 1] == 0) + dataLength--; + + //Console.WriteLine("Len = " + dataLength); + } + + + //*********************************************************************** + // Constructor (Default value provided by an array of bytes of the + // specified length.) + //*********************************************************************** + + public BigInteger(byte[] inData, int inLen) + { + dataLength = inLen >> 2; + + int leftOver = inLen & 0x3; + if (leftOver != 0) // length not multiples of 4 + dataLength++; + + if (dataLength > maxLength || inLen > inData.Length) + throw (new ArithmeticException("Byte overflow in constructor.")); + + + data = new uint[maxLength]; + + for (int i = inLen - 1, j = 0; i >= 3; i -= 4, j++) + { + data[j] = (uint)((inData[i - 3] << 24) + (inData[i - 2] << 16) + + (inData[i - 1] << 8) + inData[i]); + } + + if (leftOver == 1) + data[dataLength - 1] = (uint)inData[0]; + else if (leftOver == 2) + data[dataLength - 1] = (uint)((inData[0] << 8) + inData[1]); + else if (leftOver == 3) + data[dataLength - 1] = (uint)((inData[0] << 16) + (inData[1] << 8) + inData[2]); + + + if (dataLength == 0) + dataLength = 1; + + while (dataLength > 1 && data[dataLength - 1] == 0) + dataLength--; + + //Console.WriteLine("Len = " + dataLength); + } + + + //*********************************************************************** + // Constructor (Default value provided by an array of unsigned integers) + //********************************************************************* + + public BigInteger(uint[] inData) + { + dataLength = inData.Length; + + if (dataLength > maxLength) + throw (new ArithmeticException("Byte overflow in constructor.")); + + data = new uint[maxLength]; + + for (int i = dataLength - 1, j = 0; i >= 0; i--, j++) + data[j] = inData[i]; + + while (dataLength > 1 && data[dataLength - 1] == 0) + dataLength--; + + //Console.WriteLine("Len = " + dataLength); + } + + + //*********************************************************************** + // Overloading of the typecast operator. + // For BigInteger bi = 10; + //*********************************************************************** + + public static implicit operator BigInteger(long value) + { + return (new BigInteger(value)); + } + + public static implicit operator BigInteger(ulong value) + { + return (new BigInteger(value)); + } + + public static implicit operator BigInteger(int value) + { + return (new BigInteger((long)value)); + } + + public static implicit operator BigInteger(uint value) + { + return (new BigInteger((ulong)value)); + } + + + //*********************************************************************** + // Overloading of addition operator + //*********************************************************************** + + public static BigInteger operator +(BigInteger bi1, BigInteger bi2) + { + BigInteger result = new BigInteger(); + + result.dataLength = (bi1.dataLength > bi2.dataLength) ? bi1.dataLength : bi2.dataLength; + + long carry = 0; + for (int i = 0; i < result.dataLength; i++) + { + long sum = (long)bi1.data[i] + (long)bi2.data[i] + carry; + carry = sum >> 32; + result.data[i] = (uint)(sum & 0xFFFFFFFF); + } + + if (carry != 0 && result.dataLength < maxLength) + { + result.data[result.dataLength] = (uint)(carry); + result.dataLength++; + } + + while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) + result.dataLength--; + + + // overflow check + int lastPos = maxLength - 1; + if ((bi1.data[lastPos] & 0x80000000) == (bi2.data[lastPos] & 0x80000000) && + (result.data[lastPos] & 0x80000000) != (bi1.data[lastPos] & 0x80000000)) + { + throw (new ArithmeticException()); + } + + return result; + } + + + //*********************************************************************** + // Overloading of the unary ++ operator + //*********************************************************************** + + public static BigInteger operator ++(BigInteger bi1) + { + BigInteger result = new BigInteger(bi1); + + long val, carry = 1; + int index = 0; + + while (carry != 0 && index < maxLength) + { + val = (long)(result.data[index]); + val++; + + result.data[index] = (uint)(val & 0xFFFFFFFF); + carry = val >> 32; + + index++; + } + + if (index > result.dataLength) + result.dataLength = index; + else + { + while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) + result.dataLength--; + } + + // overflow check + int lastPos = maxLength - 1; + + // overflow if initial value was +ve but ++ caused a sign + // change to negative. + + if ((bi1.data[lastPos] & 0x80000000) == 0 && + (result.data[lastPos] & 0x80000000) != (bi1.data[lastPos] & 0x80000000)) + { + throw (new ArithmeticException("Overflow in ++.")); + } + return result; + } + + + //*********************************************************************** + // Overloading of subtraction operator + //*********************************************************************** + + public static BigInteger operator -(BigInteger bi1, BigInteger bi2) + { + BigInteger result = new BigInteger(); + + result.dataLength = (bi1.dataLength > bi2.dataLength) ? bi1.dataLength : bi2.dataLength; + + long carryIn = 0; + for (int i = 0; i < result.dataLength; i++) + { + long diff; + + diff = (long)bi1.data[i] - (long)bi2.data[i] - carryIn; + result.data[i] = (uint)(diff & 0xFFFFFFFF); + + if (diff < 0) + carryIn = 1; + else + carryIn = 0; + } + + // roll over to negative + if (carryIn != 0) + { + for (int i = result.dataLength; i < maxLength; i++) + result.data[i] = 0xFFFFFFFF; + result.dataLength = maxLength; + } + + // fixed in v1.03 to give correct datalength for a - (-b) + while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) + result.dataLength--; + + // overflow check + + int lastPos = maxLength - 1; + if ((bi1.data[lastPos] & 0x80000000) != (bi2.data[lastPos] & 0x80000000) && + (result.data[lastPos] & 0x80000000) != (bi1.data[lastPos] & 0x80000000)) + { + throw (new ArithmeticException()); + } + + return result; + } + + + //*********************************************************************** + // Overloading of the unary -- operator + //*********************************************************************** + + public static BigInteger operator --(BigInteger bi1) + { + BigInteger result = new BigInteger(bi1); + + long val; + bool carryIn = true; + int index = 0; + + while (carryIn && index < maxLength) + { + val = (long)(result.data[index]); + val--; + + result.data[index] = (uint)(val & 0xFFFFFFFF); + + if (val >= 0) + carryIn = false; + + index++; + } + + if (index > result.dataLength) + result.dataLength = index; + + while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) + result.dataLength--; + + // overflow check + int lastPos = maxLength - 1; + + // overflow if initial value was -ve but -- caused a sign + // change to positive. + + if ((bi1.data[lastPos] & 0x80000000) != 0 && + (result.data[lastPos] & 0x80000000) != (bi1.data[lastPos] & 0x80000000)) + { + throw (new ArithmeticException("Underflow in --.")); + } + + return result; + } + + + //*********************************************************************** + // Overloading of multiplication operator + //*********************************************************************** + + public static BigInteger operator *(BigInteger bi1, BigInteger bi2) + { + int lastPos = maxLength - 1; + bool bi1Neg = false, bi2Neg = false; + + // take the absolute value of the inputs + try + { + if ((bi1.data[lastPos] & 0x80000000) != 0) // bi1 negative + { + bi1Neg = true; bi1 = -bi1; + } + if ((bi2.data[lastPos] & 0x80000000) != 0) // bi2 negative + { + bi2Neg = true; bi2 = -bi2; + } + } + catch (Exception) { } + + BigInteger result = new BigInteger(); + + // multiply the absolute values + try + { + for (int i = 0; i < bi1.dataLength; i++) + { + if (bi1.data[i] == 0) continue; + + ulong mcarry = 0; + for (int j = 0, k = i; j < bi2.dataLength; j++, k++) + { + // k = i + j + ulong val = ((ulong)bi1.data[i] * (ulong)bi2.data[j]) + + (ulong)result.data[k] + mcarry; + + result.data[k] = (uint)(val & 0xFFFFFFFF); + mcarry = (val >> 32); + } + + if (mcarry != 0) + result.data[i + bi2.dataLength] = (uint)mcarry; + } + } + catch (Exception) + { + throw (new ArithmeticException("Multiplication overflow.")); + } + + + result.dataLength = bi1.dataLength + bi2.dataLength; + if (result.dataLength > maxLength) + result.dataLength = maxLength; + + while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) + result.dataLength--; + + // overflow check (result is -ve) + if ((result.data[lastPos] & 0x80000000) != 0) + { + if (bi1Neg != bi2Neg && result.data[lastPos] == 0x80000000) // different sign + { + // handle the special case where multiplication produces + // a max negative number in 2's complement. + + if (result.dataLength == 1) + return result; + else + { + bool isMaxNeg = true; + for (int i = 0; i < result.dataLength - 1 && isMaxNeg; i++) + { + if (result.data[i] != 0) + isMaxNeg = false; + } + + if (isMaxNeg) + return result; + } + } + + throw (new ArithmeticException("Multiplication overflow.")); + } + + // if input has different signs, then result is -ve + if (bi1Neg != bi2Neg) + return -result; + + return result; + } + + + + //*********************************************************************** + // Overloading of unary << operators + //*********************************************************************** + + public static BigInteger operator <<(BigInteger bi1, int shiftVal) + { + BigInteger result = new BigInteger(bi1); + result.dataLength = ShiftLeft(result.data, shiftVal); + + return result; + } + + + // least significant bits at lower part of buffer + + private static int ShiftLeft(uint[] buffer, int shiftVal) + { + int shiftAmount = 32; + int bufLen = buffer.Length; + + while (bufLen > 1 && buffer[bufLen - 1] == 0) + bufLen--; + + for (int count = shiftVal; count > 0; ) + { + if (count < shiftAmount) + shiftAmount = count; + + //Console.WriteLine("shiftAmount = {0}", shiftAmount); + + ulong carry = 0; + for (int i = 0; i < bufLen; i++) + { + ulong val = ((ulong)buffer[i]) << shiftAmount; + val |= carry; + + buffer[i] = (uint)(val & 0xFFFFFFFF); + carry = val >> 32; + } + + if (carry != 0) + { + if (bufLen + 1 <= buffer.Length) + { + buffer[bufLen] = (uint)carry; + bufLen++; + } + } + count -= shiftAmount; + } + return bufLen; + } + + + //*********************************************************************** + // Overloading of unary >> operators + //*********************************************************************** + + public static BigInteger operator >>(BigInteger bi1, int shiftVal) + { + BigInteger result = new BigInteger(bi1); + result.dataLength = ShiftRight(result.data, shiftVal); + + + if ((bi1.data[maxLength - 1] & 0x80000000) != 0) // negative + { + for (int i = maxLength - 1; i >= result.dataLength; i--) + result.data[i] = 0xFFFFFFFF; + + uint mask = 0x80000000; + for (int i = 0; i < 32; i++) + { + if ((result.data[result.dataLength - 1] & mask) != 0) + break; + + result.data[result.dataLength - 1] |= mask; + mask >>= 1; + } + result.dataLength = maxLength; + } + + return result; + } + + + private static int ShiftRight(uint[] buffer, int shiftVal) + { + int shiftAmount = 32; + int invShift = 0; + int bufLen = buffer.Length; + + while (bufLen > 1 && buffer[bufLen - 1] == 0) + bufLen--; + + //Console.WriteLine("bufLen = " + bufLen + " buffer.Length = " + buffer.Length); + + for (int count = shiftVal; count > 0; ) + { + if (count < shiftAmount) + { + shiftAmount = count; + invShift = 32 - shiftAmount; + } + + //Console.WriteLine("shiftAmount = {0}", shiftAmount); + + ulong carry = 0; + for (int i = bufLen - 1; i >= 0; i--) + { + ulong val = ((ulong)buffer[i]) >> shiftAmount; + val |= carry; + + carry = ((ulong)buffer[i]) << invShift; + buffer[i] = (uint)(val); + } + + count -= shiftAmount; + } + + while (bufLen > 1 && buffer[bufLen - 1] == 0) + bufLen--; + + return bufLen; + } + + + //*********************************************************************** + // Overloading of the NOT operator (1's complement) + //*********************************************************************** + + public static BigInteger operator ~(BigInteger bi1) + { + BigInteger result = new BigInteger(bi1); + + for (int i = 0; i < maxLength; i++) + result.data[i] = (uint)(~(bi1.data[i])); + + result.dataLength = maxLength; + + while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) + result.dataLength--; + + return result; + } + + + //*********************************************************************** + // Overloading of the NEGATE operator (2's complement) + //*********************************************************************** + + public static BigInteger operator -(BigInteger bi1) + { + // handle neg of zero separately since it'll cause an overflow + // if we proceed. + + if (bi1.dataLength == 1 && bi1.data[0] == 0) + return (new BigInteger()); + + BigInteger result = new BigInteger(bi1); + + // 1's complement + for (int i = 0; i < maxLength; i++) + result.data[i] = (uint)(~(bi1.data[i])); + + // add one to result of 1's complement + long val, carry = 1; + int index = 0; + + while (carry != 0 && index < maxLength) + { + val = (long)(result.data[index]); + val++; + + result.data[index] = (uint)(val & 0xFFFFFFFF); + carry = val >> 32; + + index++; + } + + if ((bi1.data[maxLength - 1] & 0x80000000) == (result.data[maxLength - 1] & 0x80000000)) + throw (new ArithmeticException("Overflow in negation.\n")); + + result.dataLength = maxLength; + + while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) + result.dataLength--; + return result; + } + + + //*********************************************************************** + // Overloading of equality operator + //*********************************************************************** + + public static bool operator ==(BigInteger bi1, BigInteger bi2) + { + return bi1.Equals(bi2); + } + + + public static bool operator !=(BigInteger bi1, BigInteger bi2) + { + return !(bi1.Equals(bi2)); + } + + + public override bool Equals(object o) + { + BigInteger bi = (BigInteger)o; + + if (this.dataLength != bi.dataLength) + return false; + + for (int i = 0; i < this.dataLength; i++) + { + if (this.data[i] != bi.data[i]) + return false; + } + return true; + } + + + public override int GetHashCode() + { + return this.ToString().GetHashCode(); + } + + + //*********************************************************************** + // Overloading of inequality operator + //*********************************************************************** + + public static bool operator >(BigInteger bi1, BigInteger bi2) + { + int pos = maxLength - 1; + + // bi1 is negative, bi2 is positive + if ((bi1.data[pos] & 0x80000000) != 0 && (bi2.data[pos] & 0x80000000) == 0) + return false; + + // bi1 is positive, bi2 is negative + else if ((bi1.data[pos] & 0x80000000) == 0 && (bi2.data[pos] & 0x80000000) != 0) + return true; + + // same sign + int len = (bi1.dataLength > bi2.dataLength) ? bi1.dataLength : bi2.dataLength; + for (pos = len - 1; pos >= 0 && bi1.data[pos] == bi2.data[pos]; pos--) ; + + if (pos >= 0) + { + if (bi1.data[pos] > bi2.data[pos]) + return true; + return false; + } + return false; + } + + + public static bool operator <(BigInteger bi1, BigInteger bi2) + { + int pos = maxLength - 1; + + // bi1 is negative, bi2 is positive + if ((bi1.data[pos] & 0x80000000) != 0 && (bi2.data[pos] & 0x80000000) == 0) + return true; + + // bi1 is positive, bi2 is negative + else if ((bi1.data[pos] & 0x80000000) == 0 && (bi2.data[pos] & 0x80000000) != 0) + return false; + + // same sign + int len = (bi1.dataLength > bi2.dataLength) ? bi1.dataLength : bi2.dataLength; + for (pos = len - 1; pos >= 0 && bi1.data[pos] == bi2.data[pos]; pos--) ; + + if (pos >= 0) + { + if (bi1.data[pos] < bi2.data[pos]) + return true; + return false; + } + return false; + } + + + public static bool operator >=(BigInteger bi1, BigInteger bi2) + { + return (bi1 == bi2 || bi1 > bi2); + } + + + public static bool operator <=(BigInteger bi1, BigInteger bi2) + { + return (bi1 == bi2 || bi1 < bi2); + } + + + //*********************************************************************** + // Private function that supports the division of two numbers with + // a divisor that has more than 1 digit. + // + // Algorithm taken from [1] + //*********************************************************************** + + private static void MultiByteDivide(BigInteger bi1, BigInteger bi2, + BigInteger outQuotient, BigInteger outRemainder) + { + uint[] result = new uint[maxLength]; + + int remainderLen = bi1.dataLength + 1; + uint[] remainder = new uint[remainderLen]; + + uint mask = 0x80000000; + uint val = bi2.data[bi2.dataLength - 1]; + int shift = 0, resultPos = 0; + + while (mask != 0 && (val & mask) == 0) + { + shift++; mask >>= 1; + } + + //Console.WriteLine("shift = {0}", shift); + //Console.WriteLine("Before bi1 Len = {0}, bi2 Len = {1}", bi1.dataLength, bi2.dataLength); + + for (int i = 0; i < bi1.dataLength; i++) + remainder[i] = bi1.data[i]; + ShiftLeft(remainder, shift); + bi2 = bi2 << shift; + + /* + Console.WriteLine("bi1 Len = {0}, bi2 Len = {1}", bi1.dataLength, bi2.dataLength); + Console.WriteLine("dividend = " + bi1 + "\ndivisor = " + bi2); + for(int q = remainderLen - 1; q >= 0; q--) + Console.Write("{0:x2}", remainder[q]); + Console.WriteLine(); + */ + + int j = remainderLen - bi2.dataLength; + int pos = remainderLen - 1; + + ulong firstDivisorByte = bi2.data[bi2.dataLength - 1]; + ulong secondDivisorByte = bi2.data[bi2.dataLength - 2]; + + int divisorLen = bi2.dataLength + 1; + uint[] dividendPart = new uint[divisorLen]; + + while (j > 0) + { + ulong dividend = ((ulong)remainder[pos] << 32) + (ulong)remainder[pos - 1]; + //Console.WriteLine("dividend = {0}", dividend); + + ulong q_hat = dividend / firstDivisorByte; + ulong r_hat = dividend % firstDivisorByte; + + //Console.WriteLine("q_hat = {0:X}, r_hat = {1:X}", q_hat, r_hat); + + bool done = false; + while (!done) + { + done = true; + + if (q_hat == 0x100000000 || + (q_hat * secondDivisorByte) > ((r_hat << 32) + remainder[pos - 2])) + { + q_hat--; + r_hat += firstDivisorByte; + + if (r_hat < 0x100000000) + done = false; + } + } + + for (int h = 0; h < divisorLen; h++) + dividendPart[h] = remainder[pos - h]; + + BigInteger kk = new BigInteger(dividendPart); + BigInteger ss = bi2 * (long)q_hat; + + //Console.WriteLine("ss before = " + ss); + while (ss > kk) + { + q_hat--; + ss -= bi2; + //Console.WriteLine(ss); + } + BigInteger yy = kk - ss; + + //Console.WriteLine("ss = " + ss); + //Console.WriteLine("kk = " + kk); + //Console.WriteLine("yy = " + yy); + + for (int h = 0; h < divisorLen; h++) + remainder[pos - h] = yy.data[bi2.dataLength - h]; + + /* + Console.WriteLine("dividend = "); + for(int q = remainderLen - 1; q >= 0; q--) + Console.Write("{0:x2}", remainder[q]); + Console.WriteLine("\n************ q_hat = {0:X}\n", q_hat); + */ + + result[resultPos++] = (uint)q_hat; + + pos--; + j--; + } + + outQuotient.dataLength = resultPos; + int y = 0; + for (int x = outQuotient.dataLength - 1; x >= 0; x--, y++) + outQuotient.data[y] = result[x]; + for (; y < maxLength; y++) + outQuotient.data[y] = 0; + + while (outQuotient.dataLength > 1 && outQuotient.data[outQuotient.dataLength - 1] == 0) + outQuotient.dataLength--; + + if (outQuotient.dataLength == 0) + outQuotient.dataLength = 1; + + outRemainder.dataLength = ShiftRight(remainder, shift); + + for (y = 0; y < outRemainder.dataLength; y++) + outRemainder.data[y] = remainder[y]; + for (; y < maxLength; y++) + outRemainder.data[y] = 0; + } + + + //*********************************************************************** + // Private function that supports the division of two numbers with + // a divisor that has only 1 digit. + //*********************************************************************** + + private static void SingleByteDivide(BigInteger bi1, BigInteger bi2, + BigInteger outQuotient, BigInteger outRemainder) + { + uint[] result = new uint[maxLength]; + int resultPos = 0; + + // copy dividend to reminder + for (int i = 0; i < maxLength; i++) + outRemainder.data[i] = bi1.data[i]; + outRemainder.dataLength = bi1.dataLength; + + while (outRemainder.dataLength > 1 && outRemainder.data[outRemainder.dataLength - 1] == 0) + outRemainder.dataLength--; + + ulong divisor = (ulong)bi2.data[0]; + int pos = outRemainder.dataLength - 1; + ulong dividend = (ulong)outRemainder.data[pos]; + + //Console.WriteLine("divisor = " + divisor + " dividend = " + dividend); + //Console.WriteLine("divisor = " + bi2 + "\ndividend = " + bi1); + + if (dividend >= divisor) + { + ulong quotient = dividend / divisor; + result[resultPos++] = (uint)quotient; + + outRemainder.data[pos] = (uint)(dividend % divisor); + } + pos--; + + while (pos >= 0) + { + //Console.WriteLine(pos); + + dividend = ((ulong)outRemainder.data[pos + 1] << 32) + (ulong)outRemainder.data[pos]; + ulong quotient = dividend / divisor; + result[resultPos++] = (uint)quotient; + + outRemainder.data[pos + 1] = 0; + outRemainder.data[pos--] = (uint)(dividend % divisor); + //Console.WriteLine(">>>> " + bi1); + } + + outQuotient.dataLength = resultPos; + int j = 0; + for (int i = outQuotient.dataLength - 1; i >= 0; i--, j++) + outQuotient.data[j] = result[i]; + for (; j < maxLength; j++) + outQuotient.data[j] = 0; + + while (outQuotient.dataLength > 1 && outQuotient.data[outQuotient.dataLength - 1] == 0) + outQuotient.dataLength--; + + if (outQuotient.dataLength == 0) + outQuotient.dataLength = 1; + + while (outRemainder.dataLength > 1 && outRemainder.data[outRemainder.dataLength - 1] == 0) + outRemainder.dataLength--; + } + + + //*********************************************************************** + // Overloading of division operator + //*********************************************************************** + + public static BigInteger operator /(BigInteger bi1, BigInteger bi2) + { + BigInteger quotient = new BigInteger(); + BigInteger remainder = new BigInteger(); + + int lastPos = maxLength - 1; + bool divisorNeg = false, dividendNeg = false; + + if ((bi1.data[lastPos] & 0x80000000) != 0) // bi1 negative + { + bi1 = -bi1; + dividendNeg = true; + } + if ((bi2.data[lastPos] & 0x80000000) != 0) // bi2 negative + { + bi2 = -bi2; + divisorNeg = true; + } + + if (bi1 < bi2) + { + return quotient; + } + + else + { + if (bi2.dataLength == 1) + SingleByteDivide(bi1, bi2, quotient, remainder); + else + MultiByteDivide(bi1, bi2, quotient, remainder); + + if (dividendNeg != divisorNeg) + return -quotient; + + return quotient; + } + } + + + //*********************************************************************** + // Overloading of modulus operator + //*********************************************************************** + + public static BigInteger operator %(BigInteger bi1, BigInteger bi2) + { + BigInteger quotient = new BigInteger(); + BigInteger remainder = new BigInteger(bi1); + + int lastPos = maxLength - 1; + bool dividendNeg = false; + + if ((bi1.data[lastPos] & 0x80000000) != 0) // bi1 negative + { + bi1 = -bi1; + dividendNeg = true; + } + if ((bi2.data[lastPos] & 0x80000000) != 0) // bi2 negative + bi2 = -bi2; + + if (bi1 < bi2) + { + return remainder; + } + + else + { + if (bi2.dataLength == 1) + SingleByteDivide(bi1, bi2, quotient, remainder); + else + MultiByteDivide(bi1, bi2, quotient, remainder); + + if (dividendNeg) + return -remainder; + + return remainder; + } + } + + + //*********************************************************************** + // Overloading of bitwise AND operator + //*********************************************************************** + + public static BigInteger operator &(BigInteger bi1, BigInteger bi2) + { + BigInteger result = new BigInteger(); + + int len = (bi1.dataLength > bi2.dataLength) ? bi1.dataLength : bi2.dataLength; + + for (int i = 0; i < len; i++) + { + uint sum = (uint)(bi1.data[i] & bi2.data[i]); + result.data[i] = sum; + } + + result.dataLength = maxLength; + + while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) + result.dataLength--; + + return result; + } + + + //*********************************************************************** + // Overloading of bitwise OR operator + //*********************************************************************** + + public static BigInteger operator |(BigInteger bi1, BigInteger bi2) + { + BigInteger result = new BigInteger(); + + int len = (bi1.dataLength > bi2.dataLength) ? bi1.dataLength : bi2.dataLength; + + for (int i = 0; i < len; i++) + { + uint sum = (uint)(bi1.data[i] | bi2.data[i]); + result.data[i] = sum; + } + + result.dataLength = maxLength; + + while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) + result.dataLength--; + + return result; + } + + + //*********************************************************************** + // Overloading of bitwise XOR operator + //*********************************************************************** + + public static BigInteger operator ^(BigInteger bi1, BigInteger bi2) + { + BigInteger result = new BigInteger(); + + int len = (bi1.dataLength > bi2.dataLength) ? bi1.dataLength : bi2.dataLength; + + for (int i = 0; i < len; i++) + { + uint sum = (uint)(bi1.data[i] ^ bi2.data[i]); + result.data[i] = sum; + } + + result.dataLength = maxLength; + + while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) + result.dataLength--; + + return result; + } + + + //*********************************************************************** + // Returns max(this, bi) + //*********************************************************************** + + public BigInteger Max(BigInteger bi) + { + if (this > bi) + return (new BigInteger(this)); + else + return (new BigInteger(bi)); + } + + + //*********************************************************************** + // Returns min(this, bi) + //*********************************************************************** + + public BigInteger Min(BigInteger bi) + { + if (this < bi) + return (new BigInteger(this)); + else + return (new BigInteger(bi)); + + } + + + //*********************************************************************** + // Returns the absolute value + //*********************************************************************** + + public BigInteger Abs() + { + if ((this.data[maxLength - 1] & 0x80000000) != 0) + return (-this); + else + return (new BigInteger(this)); + } + + + //*********************************************************************** + // Returns a string representing the BigInteger in base 10. + //*********************************************************************** + + public override string ToString() + { + return ToString(10); + } + + + //*********************************************************************** + // Returns a string representing the BigInteger in sign-and-magnitude + // format in the specified radix. + // + // Example + // ------- + // If the value of BigInteger is -255 in base 10, then + // ToString(16) returns "-FF" + // + //*********************************************************************** + + public string ToString(int radix) + { + if (radix < 2 || radix > 36) + throw (new ArgumentException("Radix must be >= 2 and <= 36")); + + string charSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + string result = ""; + + BigInteger a = this; + + bool negative = false; + if ((a.data[maxLength - 1] & 0x80000000) != 0) + { + negative = true; + try + { + a = -a; + } + catch (Exception) { } + } + + BigInteger quotient = new BigInteger(); + BigInteger remainder = new BigInteger(); + BigInteger biRadix = new BigInteger(radix); + + if (a.dataLength == 1 && a.data[0] == 0) + result = "0"; + else + { + while (a.dataLength > 1 || (a.dataLength == 1 && a.data[0] != 0)) + { + SingleByteDivide(a, biRadix, quotient, remainder); + + if (remainder.data[0] < 10) + result = remainder.data[0] + result; + else + result = charSet[(int)remainder.data[0] - 10] + result; + + a = quotient; + } + if (negative) + result = "-" + result; + } + + return result; + } + + + //*********************************************************************** + // Returns a hex string showing the contains of the BigInteger + // + // Examples + // ------- + // 1) If the value of BigInteger is 255 in base 10, then + // ToHexString() returns "FF" + // + // 2) If the value of BigInteger is -255 in base 10, then + // ToHexString() returns ".....FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01", + // which is the 2's complement representation of -255. + // + //*********************************************************************** + + public string ToHexString() + { + string result = data[dataLength - 1].ToString("X"); + + for (int i = dataLength - 2; i >= 0; i--) + { + result += data[i].ToString("X8"); + } + + return result; + } + + + + //*********************************************************************** + // Modulo Exponentiation + //*********************************************************************** + + public BigInteger ModPow(BigInteger exp, BigInteger n) + { + if ((exp.data[maxLength - 1] & 0x80000000) != 0) + throw (new ArithmeticException("Positive exponents only.")); + + BigInteger resultNum = 1; + BigInteger tempNum; + bool thisNegative = false; + + if ((this.data[maxLength - 1] & 0x80000000) != 0) // negative this + { + tempNum = -this % n; + thisNegative = true; + } + else + tempNum = this % n; // ensures (tempNum * tempNum) < b^(2k) + + if ((n.data[maxLength - 1] & 0x80000000) != 0) // negative n + n = -n; + + // calculate constant = b^(2k) / m + BigInteger constant = new BigInteger(); + + int i = n.dataLength << 1; + constant.data[i] = 0x00000001; + constant.dataLength = i + 1; + + constant = constant / n; + int totalBits = exp.BitCount(); + int count = 0; + + // perform squaring and multiply exponentiation + for (int pos = 0; pos < exp.dataLength; pos++) + { + uint mask = 0x01; + //Console.WriteLine("pos = " + pos); + + for (int index = 0; index < 32; index++) + { + if ((exp.data[pos] & mask) != 0) + resultNum = BarrettReduction(resultNum * tempNum, n, constant); + + mask <<= 1; + + tempNum = BarrettReduction(tempNum * tempNum, n, constant); + + + if (tempNum.dataLength == 1 && tempNum.data[0] == 1) + { + if (thisNegative && (exp.data[0] & 0x1) != 0) //odd exp + return -resultNum; + return resultNum; + } + count++; + if (count == totalBits) + break; + } + } + + if (thisNegative && (exp.data[0] & 0x1) != 0) //odd exp + return -resultNum; + + return resultNum; + } + + + + //*********************************************************************** + // Fast calculation of modular reduction using Barrett's reduction. + // Requires x < b^(2k), where b is the base. In this case, base is + // 2^32 (uint). + // + // Reference [4] + //*********************************************************************** + + private BigInteger BarrettReduction(BigInteger x, BigInteger n, BigInteger constant) + { + int k = n.dataLength, + kPlusOne = k + 1, + kMinusOne = k - 1; + + BigInteger q1 = new BigInteger(); + + // q1 = x / b^(k-1) + for (int i = kMinusOne, j = 0; i < x.dataLength; i++, j++) + q1.data[j] = x.data[i]; + q1.dataLength = x.dataLength - kMinusOne; + if (q1.dataLength <= 0) + q1.dataLength = 1; + + + BigInteger q2 = q1 * constant; + BigInteger q3 = new BigInteger(); + + // q3 = q2 / b^(k+1) + for (int i = kPlusOne, j = 0; i < q2.dataLength; i++, j++) + q3.data[j] = q2.data[i]; + q3.dataLength = q2.dataLength - kPlusOne; + if (q3.dataLength <= 0) + q3.dataLength = 1; + + + // r1 = x mod b^(k+1) + // i.e. keep the lowest (k+1) words + BigInteger r1 = new BigInteger(); + int lengthToCopy = (x.dataLength > kPlusOne) ? kPlusOne : x.dataLength; + for (int i = 0; i < lengthToCopy; i++) + r1.data[i] = x.data[i]; + r1.dataLength = lengthToCopy; + + + // r2 = (q3 * n) mod b^(k+1) + // partial multiplication of q3 and n + + BigInteger r2 = new BigInteger(); + for (int i = 0; i < q3.dataLength; i++) + { + if (q3.data[i] == 0) continue; + + ulong mcarry = 0; + int t = i; + for (int j = 0; j < n.dataLength && t < kPlusOne; j++, t++) + { + // t = i + j + ulong val = ((ulong)q3.data[i] * (ulong)n.data[j]) + + (ulong)r2.data[t] + mcarry; + + r2.data[t] = (uint)(val & 0xFFFFFFFF); + mcarry = (val >> 32); + } + + if (t < kPlusOne) + r2.data[t] = (uint)mcarry; + } + r2.dataLength = kPlusOne; + while (r2.dataLength > 1 && r2.data[r2.dataLength - 1] == 0) + r2.dataLength--; + + r1 -= r2; + if ((r1.data[maxLength - 1] & 0x80000000) != 0) // negative + { + BigInteger val = new BigInteger(); + val.data[kPlusOne] = 0x00000001; + val.dataLength = kPlusOne + 1; + r1 += val; + } + + while (r1 >= n) + r1 -= n; + + return r1; + } + + + //*********************************************************************** + // Returns gcd(this, bi) + //*********************************************************************** + + public BigInteger Gcd(BigInteger bi) + { + BigInteger x; + BigInteger y; + + if ((data[maxLength - 1] & 0x80000000) != 0) // negative + x = -this; + else + x = this; + + if ((bi.data[maxLength - 1] & 0x80000000) != 0) // negative + y = -bi; + else + y = bi; + + BigInteger g = y; + + while (x.dataLength > 1 || (x.dataLength == 1 && x.data[0] != 0)) + { + g = x; + x = y % x; + y = g; + } + + return g; + } + + + //*********************************************************************** + // Populates "this" with the specified amount of random bits + //*********************************************************************** + + public void GenRandomBits(int bits, StrongNumberProvider rand) + { + int dwords = bits >> 5; + int remBits = bits & 0x1F; + + if (remBits != 0) + dwords++; + + if (dwords > maxLength) + throw (new ArithmeticException("Number of required bits > maxLength.")); + + for (int i = 0; i < dwords; i++) + data[i] = (uint)rand.NextUInt32(); + + for (int i = dwords; i < maxLength; i++) + data[i] = 0; + + if (remBits != 0) + { + uint mask = (uint)(0x01 << (remBits - 1)); + data[dwords - 1] |= mask; + + mask = (uint)(0xFFFFFFFF >> (32 - remBits)); + data[dwords - 1] &= mask; + } + else + data[dwords - 1] |= 0x80000000; + + dataLength = dwords; + + if (dataLength == 0) + dataLength = 1; + } + + + //*********************************************************************** + // Returns the position of the most significant bit in the BigInteger. + // + // Eg. The result is 0, if the value of BigInteger is 0...0000 0000 + // The result is 1, if the value of BigInteger is 0...0000 0001 + // The result is 2, if the value of BigInteger is 0...0000 0010 + // The result is 2, if the value of BigInteger is 0...0000 0011 + // + //*********************************************************************** + + public int BitCount() + { + while (dataLength > 1 && data[dataLength - 1] == 0) + dataLength--; + + uint value = data[dataLength - 1]; + uint mask = 0x80000000; + int bits = 32; + + while (bits > 0 && (value & mask) == 0) + { + bits--; + mask >>= 1; + } + bits += ((dataLength - 1) << 5); + + return bits; + } + + + //*********************************************************************** + // Probabilistic prime test based on Fermat's little theorem + // + // for any a < p (p does not divide a) if + // a^(p-1) mod p != 1 then p is not prime. + // + // Otherwise, p is probably prime (pseudoprime to the chosen base). + // + // Returns + // ------- + // True if "this" is a pseudoprime to randomly chosen + // bases. The number of chosen bases is given by the "confidence" + // parameter. + // + // False if "this" is definitely NOT prime. + // + // Note - this method is fast but fails for Carmichael numbers except + // when the randomly chosen base is a factor of the number. + // + //*********************************************************************** + + public bool FermatLittleTest(int confidence) + { + BigInteger thisVal; + if ((this.data[maxLength - 1] & 0x80000000) != 0) // negative + thisVal = -this; + else + thisVal = this; + + if (thisVal.dataLength == 1) + { + // test small numbers + if (thisVal.data[0] == 0 || thisVal.data[0] == 1) + return false; + else if (thisVal.data[0] == 2 || thisVal.data[0] == 3) + return true; + } + + if ((thisVal.data[0] & 0x1) == 0) // even numbers + return false; + + int bits = thisVal.BitCount(); + BigInteger a = new BigInteger(); + BigInteger p_sub1 = thisVal - (new BigInteger(1)); + StrongNumberProvider rand = new StrongNumberProvider(); + + for (int round = 0; round < confidence; round++) + { + bool done = false; + + while (!done) // generate a < n + { + int testBits = 0; + + // make sure "a" has at least 2 bits + while (testBits < 2) + testBits = rand.NextInt() * bits; + + a.GenRandomBits(testBits, rand); + + int byteLen = a.dataLength; + + // make sure "a" is not 0 + if (byteLen > 1 || (byteLen == 1 && a.data[0] != 1)) + done = true; + } + + // check whether a factor exists (fix for version 1.03) + BigInteger gcdTest = a.Gcd(thisVal); + if (gcdTest.dataLength == 1 && gcdTest.data[0] != 1) + return false; + + // calculate a^(p-1) mod p + BigInteger expResult = a.ModPow(p_sub1, thisVal); + + int resultLen = expResult.dataLength; + + // is NOT prime is a^(p-1) mod p != 1 + + if (resultLen > 1 || (resultLen == 1 && expResult.data[0] != 1)) + { + //Console.WriteLine("a = " + a.ToString()); + return false; + } + } + + return true; + } + + + //*********************************************************************** + // Probabilistic prime test based on Rabin-Miller's + // + // for any p > 0 with p - 1 = 2^s * t + // + // p is probably prime (strong pseudoprime) if for any a < p, + // 1) a^t mod p = 1 or + // 2) a^((2^j)*t) mod p = p-1 for some 0 <= j <= s-1 + // + // Otherwise, p is composite. + // + // Returns + // ------- + // True if "this" is a strong pseudoprime to randomly chosen + // bases. The number of chosen bases is given by the "confidence" + // parameter. + // + // False if "this" is definitely NOT prime. + // + //*********************************************************************** + + public bool RabinMillerTest(int confidence) + { + BigInteger thisVal; + if ((this.data[maxLength - 1] & 0x80000000) != 0) // negative + thisVal = -this; + else + thisVal = this; + + if (thisVal.dataLength == 1) + { + // test small numbers + if (thisVal.data[0] == 0 || thisVal.data[0] == 1) + return false; + else if (thisVal.data[0] == 2 || thisVal.data[0] == 3) + return true; + } + + if ((thisVal.data[0] & 0x1) == 0) // even numbers + return false; + + + // calculate values of s and t + BigInteger p_sub1 = thisVal - (new BigInteger(1)); + int s = 0; + + for (int index = 0; index < p_sub1.dataLength; index++) + { + uint mask = 0x01; + + for (int i = 0; i < 32; i++) + { + if ((p_sub1.data[index] & mask) != 0) + { + index = p_sub1.dataLength; // to break the outer loop + break; + } + mask <<= 1; + s++; + } + } + + BigInteger t = p_sub1 >> s; + + int bits = thisVal.BitCount(); + BigInteger a = new BigInteger(); + StrongNumberProvider rand = new StrongNumberProvider(); + + for (int round = 0; round < confidence; round++) + { + bool done = false; + + while (!done) // generate a < n + { + int testBits = 0; + + // make sure "a" has at least 2 bits + while (testBits < 2) + testBits = (int)(rand.NextSingle() * bits); + + a.GenRandomBits(testBits, rand); + + int byteLen = a.dataLength; + + // make sure "a" is not 0 + if (byteLen > 1 || (byteLen == 1 && a.data[0] != 1)) + done = true; + } + + // check whether a factor exists (fix for version 1.03) + BigInteger gcdTest = a.Gcd(thisVal); + if (gcdTest.dataLength == 1 && gcdTest.data[0] != 1) + return false; + + BigInteger b = a.ModPow(t, thisVal); + + bool result = false; + + if (b.dataLength == 1 && b.data[0] == 1) // a^t mod p = 1 + result = true; + + for (int j = 0; result == false && j < s; j++) + { + if (b == p_sub1) // a^((2^j)*t) mod p = p-1 for some 0 <= j <= s-1 + { + result = true; + break; + } + + b = (b * b) % thisVal; + } + + if (result == false) + return false; + } + return true; + } + + + //*********************************************************************** + // Probabilistic prime test based on Solovay-Strassen (Euler Criterion) + // + // p is probably prime if for any a < p (a is not multiple of p), + // a^((p-1)/2) mod p = J(a, p) + // + // where J is the Jacobi symbol. + // + // Otherwise, p is composite. + // + // Returns + // ------- + // True if "this" is a Euler pseudoprime to randomly chosen + // bases. The number of chosen bases is given by the "confidence" + // parameter. + // + // False if "this" is definitely NOT prime. + // + //*********************************************************************** + + public bool SolovayStrassenTest(int confidence) + { + BigInteger thisVal; + if ((this.data[maxLength - 1] & 0x80000000) != 0) // negative + thisVal = -this; + else + thisVal = this; + + if (thisVal.dataLength == 1) + { + // test small numbers + if (thisVal.data[0] == 0 || thisVal.data[0] == 1) + return false; + else if (thisVal.data[0] == 2 || thisVal.data[0] == 3) + return true; + } + + if ((thisVal.data[0] & 0x1) == 0) // even numbers + return false; + + + int bits = thisVal.BitCount(); + BigInteger a = new BigInteger(); + BigInteger p_sub1 = thisVal - 1; + BigInteger p_sub1_shift = p_sub1 >> 1; + + StrongNumberProvider rand = new StrongNumberProvider(); + + for (int round = 0; round < confidence; round++) + { + bool done = false; + + while (!done) // generate a < n + { + int testBits = 0; + + // make sure "a" has at least 2 bits + while (testBits < 2) + testBits = (int)(rand.NextSingle() * bits); + + a.GenRandomBits(testBits, rand); + + int byteLen = a.dataLength; + + // make sure "a" is not 0 + if (byteLen > 1 || (byteLen == 1 && a.data[0] != 1)) + done = true; + } + + // check whether a factor exists (fix for version 1.03) + BigInteger gcdTest = a.Gcd(thisVal); + if (gcdTest.dataLength == 1 && gcdTest.data[0] != 1) + return false; + + // calculate a^((p-1)/2) mod p + + BigInteger expResult = a.ModPow(p_sub1_shift, thisVal); + if (expResult == p_sub1) + expResult = -1; + + // calculate Jacobi symbol + BigInteger jacob = Jacobi(a, thisVal); + + //Console.WriteLine("a = " + a.ToString(10) + " b = " + thisVal.ToString(10)); + //Console.WriteLine("expResult = " + expResult.ToString(10) + " Jacob = " + jacob.ToString(10)); + + // if they are different then it is not prime + if (expResult != jacob) + return false; + } + + return true; + } + + + //*********************************************************************** + // Implementation of the Lucas Strong Pseudo Prime test. + // + // Let n be an odd number with gcd(n,D) = 1, and n - J(D, n) = 2^s * d + // with d odd and s >= 0. + // + // If Ud mod n = 0 or V2^r*d mod n = 0 for some 0 <= r < s, then n + // is a strong Lucas pseudoprime with parameters (P, Q). We select + // P and Q based on Selfridge. + // + // Returns True if number is a strong Lucus pseudo prime. + // Otherwise, returns False indicating that number is composite. + //*********************************************************************** + + public bool LucasStrongTest() + { + BigInteger thisVal; + if ((this.data[maxLength - 1] & 0x80000000) != 0) // negative + thisVal = -this; + else + thisVal = this; + + if (thisVal.dataLength == 1) + { + // test small numbers + if (thisVal.data[0] == 0 || thisVal.data[0] == 1) + return false; + else if (thisVal.data[0] == 2 || thisVal.data[0] == 3) + return true; + } + + if ((thisVal.data[0] & 0x1) == 0) // even numbers + return false; + + return LucasStrongTestHelper(thisVal); + } + + + private bool LucasStrongTestHelper(BigInteger thisVal) + { + // Do the test (selects D based on Selfridge) + // Let D be the first element of the sequence + // 5, -7, 9, -11, 13, ... for which J(D,n) = -1 + // Let P = 1, Q = (1-D) / 4 + + long D = 5, sign = -1, dCount = 0; + bool done = false; + + while (!done) + { + int Jresult = BigInteger.Jacobi(D, thisVal); + + if (Jresult == -1) + done = true; // J(D, this) = 1 + else + { + if (Jresult == 0 && Math.Abs(D) < thisVal) // divisor found + return false; + + if (dCount == 20) + { + // check for square + BigInteger root = thisVal.Sqrt(); + if (root * root == thisVal) + return false; + } + + //Console.WriteLine(D); + D = (Math.Abs(D) + 2) * sign; + sign = -sign; + } + dCount++; + } + + long Q = (1 - D) >> 2; + + BigInteger p_add1 = thisVal + 1; + int s = 0; + + for (int index = 0; index < p_add1.dataLength; index++) + { + uint mask = 0x01; + + for (int i = 0; i < 32; i++) + { + if ((p_add1.data[index] & mask) != 0) + { + index = p_add1.dataLength; // to break the outer loop + break; + } + mask <<= 1; + s++; + } + } + + BigInteger t = p_add1 >> s; + + // calculate constant = b^(2k) / m + // for Barrett Reduction + BigInteger constant = new BigInteger(); + + int nLen = thisVal.dataLength << 1; + constant.data[nLen] = 0x00000001; + constant.dataLength = nLen + 1; + + constant = constant / thisVal; + + BigInteger[] lucas = LucasSequenceHelper(1, Q, t, thisVal, constant, 0); + bool isPrime = false; + + if ((lucas[0].dataLength == 1 && lucas[0].data[0] == 0) || + (lucas[1].dataLength == 1 && lucas[1].data[0] == 0)) + { + // u(t) = 0 or V(t) = 0 + isPrime = true; + } + + for (int i = 1; i < s; i++) + { + if (!isPrime) + { + // doubling of index + lucas[1] = thisVal.BarrettReduction(lucas[1] * lucas[1], thisVal, constant); + lucas[1] = (lucas[1] - (lucas[2] << 1)) % thisVal; + + //lucas[1] = ((lucas[1] * lucas[1]) - (lucas[2] << 1)) % thisVal; + + if ((lucas[1].dataLength == 1 && lucas[1].data[0] == 0)) + isPrime = true; + } + + lucas[2] = thisVal.BarrettReduction(lucas[2] * lucas[2], thisVal, constant); //Q^k + } + + + if (isPrime) // additional checks for composite numbers + { + // If n is prime and gcd(n, Q) == 1, then + // Q^((n+1)/2) = Q * Q^((n-1)/2) is congruent to (Q * J(Q, n)) mod n + + BigInteger g = thisVal.Gcd(Q); + if (g.dataLength == 1 && g.data[0] == 1) // gcd(this, Q) == 1 + { + if ((lucas[2].data[maxLength - 1] & 0x80000000) != 0) + lucas[2] += thisVal; + + BigInteger temp = (Q * BigInteger.Jacobi(Q, thisVal)) % thisVal; + if ((temp.data[maxLength - 1] & 0x80000000) != 0) + temp += thisVal; + + if (lucas[2] != temp) + isPrime = false; + } + } + + return isPrime; + } + + + //*********************************************************************** + // Determines whether a number is probably prime, using the Rabin-Miller's + // test. Before applying the test, the number is tested for divisibility + // by primes < 2000 + // + // Returns true if number is probably prime. + //*********************************************************************** + + public bool IsProbablePrime(int confidence) + { + BigInteger thisVal; + if ((this.data[maxLength - 1] & 0x80000000) != 0) // negative + thisVal = -this; + else + thisVal = this; + + + // test for divisibility by primes < 2000 + for (int p = 0; p < primesBelow2000.Length; p++) + { + BigInteger divisor = primesBelow2000[p]; + + if (divisor >= thisVal) + break; + + BigInteger resultNum = thisVal % divisor; + if (resultNum.IntValue() == 0) + { + /* + Console.WriteLine("Not prime! Divisible by {0}\n", + primesBelow2000[p]); + */ + return false; + } + } + + if (thisVal.RabinMillerTest(confidence)) + return true; + else + { + //Console.WriteLine("Not prime! Failed primality test\n"); + return false; + } + } + + + //*********************************************************************** + // Determines whether this BigInteger is probably prime using a + // combination of base 2 strong pseudoprime test and Lucas strong + // pseudoprime test. + // + // The sequence of the primality test is as follows, + // + // 1) Trial divisions are carried out using prime numbers below 2000. + // if any of the primes divides this BigInteger, then it is not prime. + // + // 2) Perform base 2 strong pseudoprime test. If this BigInteger is a + // base 2 strong pseudoprime, proceed on to the next step. + // + // 3) Perform strong Lucas pseudoprime test. + // + // Returns True if this BigInteger is both a base 2 strong pseudoprime + // and a strong Lucas pseudoprime. + // + // For a detailed discussion of this primality test, see [6]. + // + //*********************************************************************** + + public bool IsProbablePrime() + { + BigInteger thisVal; + if ((this.data[maxLength - 1] & 0x80000000) != 0) // negative + thisVal = -this; + else + thisVal = this; + + if (thisVal.dataLength == 1) + { + // test small numbers + if (thisVal.data[0] == 0 || thisVal.data[0] == 1) + return false; + else if (thisVal.data[0] == 2 || thisVal.data[0] == 3) + return true; + } + + if ((thisVal.data[0] & 0x1) == 0) // even numbers + return false; + + + // test for divisibility by primes < 2000 + for (int p = 0; p < primesBelow2000.Length; p++) + { + BigInteger divisor = primesBelow2000[p]; + + if (divisor >= thisVal) + break; + + BigInteger resultNum = thisVal % divisor; + if (resultNum.IntValue() == 0) + { + //Console.WriteLine("Not prime! Divisible by {0}\n", + // primesBelow2000[p]); + + return false; + } + } + + // Perform BASE 2 Rabin-Miller Test + + // calculate values of s and t + BigInteger p_sub1 = thisVal - (new BigInteger(1)); + int s = 0; + + for (int index = 0; index < p_sub1.dataLength; index++) + { + uint mask = 0x01; + + for (int i = 0; i < 32; i++) + { + if ((p_sub1.data[index] & mask) != 0) + { + index = p_sub1.dataLength; // to break the outer loop + break; + } + mask <<= 1; + s++; + } + } + + BigInteger t = p_sub1 >> s; + + int bits = thisVal.BitCount(); + BigInteger a = 2; + + // b = a^t mod p + BigInteger b = a.ModPow(t, thisVal); + bool result = false; + + if (b.dataLength == 1 && b.data[0] == 1) // a^t mod p = 1 + result = true; + + for (int j = 0; result == false && j < s; j++) + { + if (b == p_sub1) // a^((2^j)*t) mod p = p-1 for some 0 <= j <= s-1 + { + result = true; + break; + } + + b = (b * b) % thisVal; + } + + // if number is strong pseudoprime to base 2, then do a strong lucas test + if (result) + result = LucasStrongTestHelper(thisVal); + + return result; + } + + + + //*********************************************************************** + // Returns the lowest 4 bytes of the BigInteger as an int. + //*********************************************************************** + + public int IntValue() + { + return (int)data[0]; + } + + + //*********************************************************************** + // Returns the lowest 8 bytes of the BigInteger as a long. + //*********************************************************************** + + public long LongValue() + { + long val = 0; + + val = (long)data[0]; + try + { // exception if maxLength = 1 + val |= (long)data[1] << 32; + } + catch (Exception) + { + if ((data[0] & 0x80000000) != 0) // negative + val = (int)data[0]; + } + + return val; + } + + + //*********************************************************************** + // Computes the Jacobi Symbol for a and b. + // Algorithm adapted from [3] and [4] with some optimizations + //*********************************************************************** + + public static int Jacobi(BigInteger a, BigInteger b) + { + // Jacobi defined only for odd integers + if ((b.data[0] & 0x1) == 0) + throw (new ArgumentException("Jacobi defined only for odd integers.")); + + if (a >= b) a %= b; + if (a.dataLength == 1 && a.data[0] == 0) return 0; // a == 0 + if (a.dataLength == 1 && a.data[0] == 1) return 1; // a == 1 + + if (a < 0) + { + if ((((b - 1).data[0]) & 0x2) == 0) //if( (((b-1) >> 1).data[0] & 0x1) == 0) + return Jacobi(-a, b); + else + return -Jacobi(-a, b); + } + + int e = 0; + for (int index = 0; index < a.dataLength; index++) + { + uint mask = 0x01; + + for (int i = 0; i < 32; i++) + { + if ((a.data[index] & mask) != 0) + { + index = a.dataLength; // to break the outer loop + break; + } + mask <<= 1; + e++; + } + } + + BigInteger a1 = a >> e; + + int s = 1; + if ((e & 0x1) != 0 && ((b.data[0] & 0x7) == 3 || (b.data[0] & 0x7) == 5)) + s = -1; + + if ((b.data[0] & 0x3) == 3 && (a1.data[0] & 0x3) == 3) + s = -s; + + if (a1.dataLength == 1 && a1.data[0] == 1) + return s; + else + return (s * Jacobi(b % a1, a1)); + } + + + + //*********************************************************************** + // Generates a positive BigInteger that is probably prime. + //*********************************************************************** + + public static BigInteger GenPseudoPrime(int bits, int confidence, StrongNumberProvider rand) + { + BigInteger result = new BigInteger(); + bool done = false; + + while (!done) + { + result.GenRandomBits(bits, rand); + result.data[0] |= 0x01; // make it odd + + // prime test + done = result.IsProbablePrime(confidence); + } + return result; + } + + + //*********************************************************************** + // Generates a random number with the specified number of bits such + // that gcd(number, this) = 1 + //*********************************************************************** + + public BigInteger GenCoPrime(int bits, StrongNumberProvider rand) + { + bool done = false; + BigInteger result = new BigInteger(); + + while (!done) + { + result.GenRandomBits(bits, rand); + //Console.WriteLine(result.ToString(16)); + + // gcd test + BigInteger g = result.Gcd(this); + if (g.dataLength == 1 && g.data[0] == 1) + done = true; + } + + return result; + } + + + //*********************************************************************** + // Returns the modulo inverse of this. Throws ArithmeticException if + // the inverse does not exist. (i.e. gcd(this, modulus) != 1) + //*********************************************************************** + + public BigInteger ModInverse(BigInteger modulus) + { + BigInteger[] p = { 0, 1 }; + BigInteger[] q = new BigInteger[2]; // quotients + BigInteger[] r = { 0, 0 }; // remainders + + int step = 0; + + BigInteger a = modulus; + BigInteger b = this; + + while (b.dataLength > 1 || (b.dataLength == 1 && b.data[0] != 0)) + { + BigInteger quotient = new BigInteger(); + BigInteger remainder = new BigInteger(); + + if (step > 1) + { + BigInteger pval = (p[0] - (p[1] * q[0])) % modulus; + p[0] = p[1]; + p[1] = pval; + } + + if (b.dataLength == 1) + SingleByteDivide(a, b, quotient, remainder); + else + MultiByteDivide(a, b, quotient, remainder); + + /* + Console.WriteLine(quotient.dataLength); + Console.WriteLine("{0} = {1}({2}) + {3} p = {4}", a.ToString(10), + b.ToString(10), quotient.ToString(10), remainder.ToString(10), + p[1].ToString(10)); + */ + + q[0] = q[1]; + r[0] = r[1]; + q[1] = quotient; r[1] = remainder; + + a = b; + b = remainder; + + step++; + } + + if (r[0].dataLength > 1 || (r[0].dataLength == 1 && r[0].data[0] != 1)) + throw (new ArithmeticException("No inverse!")); + + BigInteger result = ((p[0] - (p[1] * q[0])) % modulus); + + if ((result.data[maxLength - 1] & 0x80000000) != 0) + result += modulus; // get the least positive modulus + + return result; + } + + + //*********************************************************************** + // Returns the value of the BigInteger as a byte array. The lowest + // index contains the MSB. + //*********************************************************************** + + public byte[] GetBytes() + { + int numBits = BitCount(); + + int numBytes = numBits >> 3; + if ((numBits & 0x7) != 0) + numBytes++; + + byte[] result = new byte[numBytes]; + + //Console.WriteLine(result.Length); + + int pos = 0; + uint tempVal, val = data[dataLength - 1]; + + if ((tempVal = (val >> 24 & 0xFF)) != 0) + result[pos++] = (byte)tempVal; + if ((tempVal = (val >> 16 & 0xFF)) != 0) + result[pos++] = (byte)tempVal; + if ((tempVal = (val >> 8 & 0xFF)) != 0) + result[pos++] = (byte)tempVal; + if ((tempVal = (val & 0xFF)) != 0) + result[pos++] = (byte)tempVal; + + for (int i = dataLength - 2; i >= 0; i--, pos += 4) + { + val = data[i]; + result[pos + 3] = (byte)(val & 0xFF); + val >>= 8; + result[pos + 2] = (byte)(val & 0xFF); + val >>= 8; + result[pos + 1] = (byte)(val & 0xFF); + val >>= 8; + result[pos] = (byte)(val & 0xFF); + } + + return result; + } + + + //*********************************************************************** + // Sets the value of the specified bit to 1 + // The Least Significant Bit position is 0. + //*********************************************************************** + + public void SetBit(uint bitNum) + { + uint bytePos = bitNum >> 5; // divide by 32 + byte bitPos = (byte)(bitNum & 0x1F); // get the lowest 5 bits + + uint mask = (uint)1 << bitPos; + this.data[bytePos] |= mask; + + if (bytePos >= this.dataLength) + this.dataLength = (int)bytePos + 1; + } + + + //*********************************************************************** + // Sets the value of the specified bit to 0 + // The Least Significant Bit position is 0. + //*********************************************************************** + + public void UnsetBit(uint bitNum) + { + uint bytePos = bitNum >> 5; + + if (bytePos < this.dataLength) + { + byte bitPos = (byte)(bitNum & 0x1F); + + uint mask = (uint)1 << bitPos; + uint mask2 = 0xFFFFFFFF ^ mask; + + this.data[bytePos] &= mask2; + + if (this.dataLength > 1 && this.data[this.dataLength - 1] == 0) + this.dataLength--; + } + } + + + //*********************************************************************** + // Returns a value that is equivalent to the integer square root + // of the BigInteger. + // + // The integer square root of "this" is defined as the largest integer n + // such that (n * n) <= this + // + //*********************************************************************** + + public BigInteger Sqrt() + { + uint numBits = (uint)this.BitCount(); + + if ((numBits & 0x1) != 0) // odd number of bits + numBits = (numBits >> 1) + 1; + else + numBits = (numBits >> 1); + + uint bytePos = numBits >> 5; + byte bitPos = (byte)(numBits & 0x1F); + + uint mask; + + BigInteger result = new BigInteger(); + if (bitPos == 0) + mask = 0x80000000; + else + { + mask = (uint)1 << bitPos; + bytePos++; + } + result.dataLength = (int)bytePos; + + for (int i = (int)bytePos - 1; i >= 0; i--) + { + while (mask != 0) + { + // guess + result.data[i] ^= mask; + + // undo the guess if its square is larger than this + if ((result * result) > this) + result.data[i] ^= mask; + + mask >>= 1; + } + mask = 0x80000000; + } + return result; + } + + + //*********************************************************************** + // Returns the k_th number in the Lucas Sequence reduced modulo n. + // + // Uses index doubling to speed up the process. For example, to calculate V(k), + // we maintain two numbers in the sequence V(n) and V(n+1). + // + // To obtain V(2n), we use the identity + // V(2n) = (V(n) * V(n)) - (2 * Q^n) + // To obtain V(2n+1), we first write it as + // V(2n+1) = V((n+1) + n) + // and use the identity + // V(m+n) = V(m) * V(n) - Q * V(m-n) + // Hence, + // V((n+1) + n) = V(n+1) * V(n) - Q^n * V((n+1) - n) + // = V(n+1) * V(n) - Q^n * V(1) + // = V(n+1) * V(n) - Q^n * P + // + // We use k in its binary expansion and perform index doubling for each + // bit position. For each bit position that is set, we perform an + // index doubling followed by an index addition. This means that for V(n), + // we need to update it to V(2n+1). For V(n+1), we need to update it to + // V((2n+1)+1) = V(2*(n+1)) + // + // This function returns + // [0] = U(k) + // [1] = V(k) + // [2] = Q^n + // + // Where U(0) = 0 % n, U(1) = 1 % n + // V(0) = 2 % n, V(1) = P % n + //*********************************************************************** + + public static BigInteger[] LucasSequence(BigInteger P, BigInteger Q, + BigInteger k, BigInteger n) + { + if (k.dataLength == 1 && k.data[0] == 0) + { + BigInteger[] result = new BigInteger[3]; + + result[0] = 0; result[1] = 2 % n; result[2] = 1 % n; + return result; + } + + // calculate constant = b^(2k) / m + // for Barrett Reduction + BigInteger constant = new BigInteger(); + + int nLen = n.dataLength << 1; + constant.data[nLen] = 0x00000001; + constant.dataLength = nLen + 1; + + constant = constant / n; + + // calculate values of s and t + int s = 0; + + for (int index = 0; index < k.dataLength; index++) + { + uint mask = 0x01; + + for (int i = 0; i < 32; i++) + { + if ((k.data[index] & mask) != 0) + { + index = k.dataLength; // to break the outer loop + break; + } + mask <<= 1; + s++; + } + } + + BigInteger t = k >> s; + + //Console.WriteLine("s = " + s + " t = " + t); + return LucasSequenceHelper(P, Q, t, n, constant, s); + } + + + //*********************************************************************** + // Performs the calculation of the kth term in the Lucas Sequence. + // For details of the algorithm, see reference [9]. + // + // k must be odd. i.e LSB == 1 + //*********************************************************************** + + private static BigInteger[] LucasSequenceHelper(BigInteger P, BigInteger Q, + BigInteger k, BigInteger n, + BigInteger constant, int s) + { + BigInteger[] result = new BigInteger[3]; + + if ((k.data[0] & 0x00000001) == 0) + throw (new ArgumentException("Argument k must be odd.")); + + int numbits = k.BitCount(); + uint mask = (uint)0x1 << ((numbits & 0x1F) - 1); + + // v = v0, v1 = v1, u1 = u1, Q_k = Q^0 + + BigInteger v = 2 % n, Q_k = 1 % n, + v1 = P % n, u1 = Q_k; + bool flag = true; + + for (int i = k.dataLength - 1; i >= 0; i--) // iterate on the binary expansion of k + { + //Console.WriteLine("round"); + while (mask != 0) + { + if (i == 0 && mask == 0x00000001) // last bit + break; + + if ((k.data[i] & mask) != 0) // bit is set + { + // index doubling with addition + + u1 = (u1 * v1) % n; + + v = ((v * v1) - (P * Q_k)) % n; + v1 = n.BarrettReduction(v1 * v1, n, constant); + v1 = (v1 - ((Q_k * Q) << 1)) % n; + + if (flag) + flag = false; + else + Q_k = n.BarrettReduction(Q_k * Q_k, n, constant); + + Q_k = (Q_k * Q) % n; + } + else + { + // index doubling + u1 = ((u1 * v) - Q_k) % n; + + v1 = ((v * v1) - (P * Q_k)) % n; + v = n.BarrettReduction(v * v, n, constant); + v = (v - (Q_k << 1)) % n; + + if (flag) + { + Q_k = Q % n; + flag = false; + } + else + Q_k = n.BarrettReduction(Q_k * Q_k, n, constant); + } + + mask >>= 1; + } + mask = 0x80000000; + } + + // at this point u1 = u(n+1) and v = v(n) + // since the last bit always 1, we need to transform u1 to u(2n+1) and v to v(2n+1) + + u1 = ((u1 * v) - Q_k) % n; + v = ((v * v1) - (P * Q_k)) % n; + if (flag) + flag = false; + else + Q_k = n.BarrettReduction(Q_k * Q_k, n, constant); + + Q_k = (Q_k * Q) % n; + + + for (int i = 0; i < s; i++) + { + // index doubling + u1 = (u1 * v) % n; + v = ((v * v) - (Q_k << 1)) % n; + + if (flag) + { + Q_k = Q % n; + flag = false; + } + else + Q_k = n.BarrettReduction(Q_k * Q_k, n, constant); + } + + result[0] = u1; + result[1] = v; + result[2] = Q_k; + + return result; + } + + #region IDisposable Members + + public void Dispose() + { + data = new uint[0]; + data = null; + GC.Collect(); + GC.Collect(); + } + + #endregion + } +} diff --git a/ClientServerLib/ClientServerLib/DeffieHellman/DeffieHellman.cs b/ClientServerLib/ClientServerLib/DeffieHellman/DeffieHellman.cs new file mode 100644 index 00000000..5d66c08d --- /dev/null +++ b/ClientServerLib/ClientServerLib/DeffieHellman/DeffieHellman.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Security.Cryptography; + +namespace DiffieHellman +{ + /// + /// Represents the Diffie-Hellman algorithm. + /// + public class CDiffieHellman : IDisposable + { + #region - Util - + static StrongNumberProvider _strongRng = new StrongNumberProvider(); + #endregion + + #region - Fields - + /// + /// The number of bits to generate. + /// + private int bits = 256; + + /// + /// The shared prime. + /// + BigInteger prime; + /// + /// The shared base. + /// + BigInteger g; + /// + /// The private prime. + /// + BigInteger mine; + + /// + /// The final key. + /// + byte[] key; + /// + /// The string representation/packet. + /// + string representation; + #endregion + + #region - Properties - + /// + /// Gets the final key to use for encryption. + /// + public byte[] Key + { + get { return key; } + } + #endregion + + #region - Ctor - + public CDiffieHellman() + { + } + + public CDiffieHellman(int bits) + { + this.bits = bits; + } + + ~CDiffieHellman() + { + Dispose(); + } + #endregion + + #region - Implementation Methods - + #region Flow + /// + /// Generates a request packet. + /// + /// + public CDiffieHellman GenerateRequest() + { + // Generate the parameters. + prime = BigInteger.GenPseudoPrime(bits, 30, _strongRng); + mine = BigInteger.GenPseudoPrime(bits, 30, _strongRng); + g = (BigInteger)5; + + // Gemerate the string. + StringBuilder rep = new StringBuilder(); + rep.Append(prime.ToString(36)); + rep.Append("|"); + rep.Append(g.ToString(36)); + rep.Append("|"); + + // Generate the send BigInt. + using (BigInteger send = g.ModPow(mine, prime)) + { + rep.Append(send.ToString(36)); + } + + representation = rep.ToString(); + return this; + } + + /// + /// Generate a response packet. + /// + /// The string representation of the request. + /// + public CDiffieHellman GenerateResponse(string request) + { + string[] parts = request.Split('|'); + + // Generate the would-be fields. + using (BigInteger prime = new BigInteger(parts[0], 36)) + using (BigInteger g = new BigInteger(parts[1], 36)) + using (BigInteger mine = BigInteger.GenPseudoPrime(bits, 30, _strongRng)) + { + // Generate the key. + using (BigInteger given = new BigInteger(parts[2], 36)) + using (BigInteger key = given.ModPow(mine, prime)) + { + this.key = key.GetBytes(); + } + // Generate the response. + using (BigInteger send = g.ModPow(mine, prime)) + { + this.representation = send.ToString(36); + } + } + + return this; + } + + /// + /// Generates the key after a response is received. + /// + /// The string representation of the response. + public void HandleResponse(string response) + { + // Get the response and modpow it with the stored prime. + using (BigInteger given = new BigInteger(response, 36)) + using (BigInteger key = given.ModPow(mine, prime)) + { + this.key = key.GetBytes(); + } + Dispose(); + } + #endregion + + public override string ToString() + { + return representation; + } + #endregion + + #region IDisposable Members + /// + /// Ends the calculation. The key will still be available. + /// + public void Dispose() + { + if (!Object.ReferenceEquals(prime, null)) + prime.Dispose(); + if (!Object.ReferenceEquals(mine, null)) + mine.Dispose(); + if (!Object.ReferenceEquals(g, null)) + g.Dispose(); + + prime = null; + mine = null; + g = null; + + representation = null; + GC.Collect(); + GC.Collect(); + } + + #endregion + } +} diff --git a/ClientServerLib/ClientServerLib/DeffieHellman/StrongNumberProvider.cs b/ClientServerLib/ClientServerLib/DeffieHellman/StrongNumberProvider.cs new file mode 100644 index 00000000..720a1414 --- /dev/null +++ b/ClientServerLib/ClientServerLib/DeffieHellman/StrongNumberProvider.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Security.Cryptography; + +namespace DiffieHellman +{ + internal class StrongNumberProvider + { + private static RNGCryptoServiceProvider csp = new RNGCryptoServiceProvider(); + + public uint NextUInt32() + { + byte[] res = new byte[4]; + csp.GetBytes(res); + return BitConverter.ToUInt32(res, 0); + } + + public int NextInt() + { + byte[] res = new byte[4]; + csp.GetBytes(res); + return BitConverter.ToInt32(res, 0); + } + + public Single NextSingle() + { + float numerator = NextUInt32(); + float denominator = uint.MaxValue; + return numerator / denominator; + } + } +} diff --git a/ClientServerLib/ClientServerLib/Properties/AssemblyInfo.cs b/ClientServerLib/ClientServerLib/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..64cd33c7 --- /dev/null +++ b/ClientServerLib/ClientServerLib/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die mit einer Assembly verknüpft sind. +[assembly: AssemblyTitle("ClientServerLib")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ClientServerLib")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("53f89645-1948-420f-b64a-7dc705ca8820")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ClientServerLib/ClientServerLib/Structs.cs b/ClientServerLib/ClientServerLib/Structs.cs new file mode 100644 index 00000000..fa22e2a2 --- /dev/null +++ b/ClientServerLib/ClientServerLib/Structs.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace ClientServerLib +{ + public delegate void ResponseCallback(byte[] Response); + public delegate byte[] HandleRequest(int ConnectionID, byte[] Message); + + public struct SRequest + { + public byte[] Command; + public byte[] Response; + public ResponseCallback Callback; + } +} From 7346ce5f110d21c4df68813cc96ab90858b58ec9 Mon Sep 17 00:00:00 2001 From: Alexander Eckart Date: Sat, 4 May 2013 15:58:35 +0200 Subject: [PATCH 02/23] adjusted .gitignore --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index a61b7628..9cd3ff64 100644 --- a/.gitignore +++ b/.gitignore @@ -39,5 +39,5 @@ Output/Songs *.Cache /GstreamerHelper/bin/* /VocaluxeLib/bin -/ClientServerLib\ClientServerLib/ClientServerLib.csproj.user -/ClientServerLib\ClientServerLib\bin\Debug/ClientServerLib.dll +/ClientServerLib/ClientServerLib/ClientServerLib.csproj.user +/ClientServerLib/ClientServerLib/bin/Debug/ClientServerLib.dll From 40c470c184341130930acd88639615e3bf7f48f1 Mon Sep 17 00:00:00 2001 From: Alexander Eckart Date: Sat, 4 May 2013 16:53:36 +0200 Subject: [PATCH 03/23] added Client Test Form --- .gitignore | 1 + ClientServerLib/ClientServerLib.sln | 22 ++ ClientServerLib/ClientServerLib/CClient.cs | 21 +- ClientServerLib/ClientServerLib/Structs.cs | 1 + .../ClientTest/ClientTest.Designer.cs | 204 ++++++++++++++++++ ClientServerLib/ClientTest/ClientTest.cs | 76 +++++++ ClientServerLib/ClientTest/ClientTest.csproj | 93 ++++++++ ClientServerLib/ClientTest/ClientTest.resx | 123 +++++++++++ ClientServerLib/ClientTest/Program.cs | 21 ++ .../ClientTest/Properties/AssemblyInfo.cs | 36 ++++ .../Properties/Resources.Designer.cs | 71 ++++++ .../ClientTest/Properties/Resources.resx | 117 ++++++++++ .../Properties/Settings.Designer.cs | 30 +++ .../ClientTest/Properties/Settings.settings | 7 + .../ClientTest/bin/Debug/ClientTest.exe | Bin 0 -> 11776 bytes .../bin/Debug/ClientTest.vshost.exe | Bin 0 -> 11600 bytes 16 files changed, 822 insertions(+), 1 deletion(-) create mode 100644 ClientServerLib/ClientTest/ClientTest.Designer.cs create mode 100644 ClientServerLib/ClientTest/ClientTest.cs create mode 100644 ClientServerLib/ClientTest/ClientTest.csproj create mode 100644 ClientServerLib/ClientTest/ClientTest.resx create mode 100644 ClientServerLib/ClientTest/Program.cs create mode 100644 ClientServerLib/ClientTest/Properties/AssemblyInfo.cs create mode 100644 ClientServerLib/ClientTest/Properties/Resources.Designer.cs create mode 100644 ClientServerLib/ClientTest/Properties/Resources.resx create mode 100644 ClientServerLib/ClientTest/Properties/Settings.Designer.cs create mode 100644 ClientServerLib/ClientTest/Properties/Settings.settings create mode 100644 ClientServerLib/ClientTest/bin/Debug/ClientTest.exe create mode 100644 ClientServerLib/ClientTest/bin/Debug/ClientTest.vshost.exe diff --git a/.gitignore b/.gitignore index 9cd3ff64..3fb7c02c 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ Output/Songs /VocaluxeLib/bin /ClientServerLib/ClientServerLib/ClientServerLib.csproj.user /ClientServerLib/ClientServerLib/bin/Debug/ClientServerLib.dll +/ClientServerLib/ClientTest/bin/Debug/ClientServerLib.dll diff --git a/ClientServerLib/ClientServerLib.sln b/ClientServerLib/ClientServerLib.sln index 7ebf309a..396e5921 100644 --- a/ClientServerLib/ClientServerLib.sln +++ b/ClientServerLib/ClientServerLib.sln @@ -3,16 +3,38 @@ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual C# Express 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClientServerLib", "ClientServerLib\ClientServerLib.csproj", "{EC5167E4-CCFB-4CEE-987C-CF816F1062A3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClientTest", "ClientTest\ClientTest.csproj", "{48A825C6-728F-4EBF-B98E-3D4665B3BC9E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.Debug|x86.ActiveCfg = Debug|Any CPU {EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.Release|Any CPU.ActiveCfg = Release|Any CPU {EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.Release|Any CPU.Build.0 = Release|Any CPU + {EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.Release|x86.ActiveCfg = Release|Any CPU + {48A825C6-728F-4EBF-B98E-3D4665B3BC9E}.Debug|Any CPU.ActiveCfg = Debug|x86 + {48A825C6-728F-4EBF-B98E-3D4665B3BC9E}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {48A825C6-728F-4EBF-B98E-3D4665B3BC9E}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {48A825C6-728F-4EBF-B98E-3D4665B3BC9E}.Debug|x86.ActiveCfg = Debug|x86 + {48A825C6-728F-4EBF-B98E-3D4665B3BC9E}.Debug|x86.Build.0 = Debug|x86 + {48A825C6-728F-4EBF-B98E-3D4665B3BC9E}.Release|Any CPU.ActiveCfg = Release|x86 + {48A825C6-728F-4EBF-B98E-3D4665B3BC9E}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {48A825C6-728F-4EBF-B98E-3D4665B3BC9E}.Release|Mixed Platforms.Build.0 = Release|x86 + {48A825C6-728F-4EBF-B98E-3D4665B3BC9E}.Release|x86.ActiveCfg = Release|x86 + {48A825C6-728F-4EBF-B98E-3D4665B3BC9E}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ClientServerLib/ClientServerLib/CClient.cs b/ClientServerLib/ClientServerLib/CClient.cs index 75de55f3..162b2955 100644 --- a/ClientServerLib/ClientServerLib/CClient.cs +++ b/ClientServerLib/ClientServerLib/CClient.cs @@ -24,6 +24,7 @@ namespace ClientServerLib private CConnection connection; private List requests; private List responses; + private OnConnectionChanged onConnectionChanged; public bool Connected { @@ -50,10 +51,12 @@ namespace ClientServerLib clientThread.Start(); } - public void Connect(string IP = "127.0.0.1", int Port = 3000) + public void Connect(string IP = "127.0.0.1", int Port = 3000, OnConnectionChanged OnConnectionChanged = null) { if (!doConnect) { + if (OnConnectionChanged != null) + onConnectionChanged = OnConnectionChanged; ip = IP; port = Port; doConnect = true; @@ -109,10 +112,12 @@ namespace ClientServerLib serverEndPoint = new IPEndPoint(IPAddress.Parse(ip), port); connection.TcpClient.Connect(serverEndPoint); connected = true; + RaiseOnConnectionChanged(); } catch { connected = false; + RaiseOnConnectionChanged(); for (int i = 0; i < 500; i++) { if (doConnect) @@ -135,6 +140,7 @@ namespace ClientServerLib } connected = false; + RaiseOnConnectionChanged(); } if (doConnect && connected) @@ -146,6 +152,7 @@ namespace ClientServerLib catch { connected = false; + RaiseOnConnectionChanged(); } } @@ -253,5 +260,17 @@ namespace ClientServerLib return response; } + + private void RaiseOnConnectionChanged() + { + if (onConnectionChanged == null) + return; + + try + { + onConnectionChanged(connected); + } + catch {} + } } } diff --git a/ClientServerLib/ClientServerLib/Structs.cs b/ClientServerLib/ClientServerLib/Structs.cs index fa22e2a2..d3f3c41f 100644 --- a/ClientServerLib/ClientServerLib/Structs.cs +++ b/ClientServerLib/ClientServerLib/Structs.cs @@ -7,6 +7,7 @@ namespace ClientServerLib { public delegate void ResponseCallback(byte[] Response); public delegate byte[] HandleRequest(int ConnectionID, byte[] Message); + public delegate void OnConnectionChanged(bool Connected); public struct SRequest { diff --git a/ClientServerLib/ClientTest/ClientTest.Designer.cs b/ClientServerLib/ClientTest/ClientTest.Designer.cs new file mode 100644 index 00000000..a0dc68e9 --- /dev/null +++ b/ClientServerLib/ClientTest/ClientTest.Designer.cs @@ -0,0 +1,204 @@ +namespace ClientTest +{ + partial class ClientTest + { + /// + /// Erforderliche Designervariable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Verwendete Ressourcen bereinigen. + /// + /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Vom Windows Form-Designer generierter Code + + /// + /// Erforderliche Methode für die Designerunterstützung. + /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. + /// + private void InitializeComponent() + { + this.lbServerIP = new System.Windows.Forms.Label(); + this.lbPort = new System.Windows.Forms.Label(); + this.tbServerIP = new System.Windows.Forms.TextBox(); + this.tbPort = new System.Windows.Forms.TextBox(); + this.statusStrip1 = new System.Windows.Forms.StatusStrip(); + this.lbConnectionStatus = new System.Windows.Forms.ToolStripStatusLabel(); + this.lbConnectionStatusText = new System.Windows.Forms.ToolStripStatusLabel(); + this.btConnect = new System.Windows.Forms.Button(); + this.panel1 = new System.Windows.Forms.Panel(); + this.tbDataSending = new System.Windows.Forms.TextBox(); + this.lbDateSending = new System.Windows.Forms.Label(); + this.lbDataReceiving = new System.Windows.Forms.Label(); + this.tbDataReceiving = new System.Windows.Forms.TextBox(); + this.statusStrip1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // lbServerIP + // + this.lbServerIP.AutoSize = true; + this.lbServerIP.Location = new System.Drawing.Point(3, 0); + this.lbServerIP.Name = "lbServerIP"; + this.lbServerIP.Size = new System.Drawing.Size(54, 13); + this.lbServerIP.TabIndex = 0; + this.lbServerIP.Text = "Server IP:"; + // + // lbPort + // + this.lbPort.AutoSize = true; + this.lbPort.Location = new System.Drawing.Point(3, 26); + this.lbPort.Name = "lbPort"; + this.lbPort.Size = new System.Drawing.Size(29, 13); + this.lbPort.TabIndex = 1; + this.lbPort.Text = "Port:"; + // + // tbServerIP + // + this.tbServerIP.Location = new System.Drawing.Point(85, -3); + this.tbServerIP.Name = "tbServerIP"; + this.tbServerIP.Size = new System.Drawing.Size(86, 20); + this.tbServerIP.TabIndex = 2; + this.tbServerIP.Text = "127.0.0.1"; + // + // tbPort + // + this.tbPort.Location = new System.Drawing.Point(85, 23); + this.tbPort.Name = "tbPort"; + this.tbPort.Size = new System.Drawing.Size(86, 20); + this.tbPort.TabIndex = 3; + this.tbPort.Text = "3000"; + // + // statusStrip1 + // + this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.lbConnectionStatus, + this.lbConnectionStatusText}); + this.statusStrip1.Location = new System.Drawing.Point(0, 528); + this.statusStrip1.Name = "statusStrip1"; + this.statusStrip1.Size = new System.Drawing.Size(1008, 22); + this.statusStrip1.TabIndex = 4; + this.statusStrip1.Text = "statusStrip1"; + // + // lbConnectionStatus + // + this.lbConnectionStatus.Name = "lbConnectionStatus"; + this.lbConnectionStatus.Size = new System.Drawing.Size(107, 17); + this.lbConnectionStatus.Text = "Connection Status:"; + // + // lbConnectionStatusText + // + this.lbConnectionStatusText.Name = "lbConnectionStatusText"; + this.lbConnectionStatusText.Size = new System.Drawing.Size(79, 17); + this.lbConnectionStatusText.Text = "Disconnected"; + // + // btConnect + // + this.btConnect.Location = new System.Drawing.Point(6, 49); + this.btConnect.Name = "btConnect"; + this.btConnect.Size = new System.Drawing.Size(165, 23); + this.btConnect.TabIndex = 5; + this.btConnect.Text = "Connect"; + this.btConnect.UseVisualStyleBackColor = true; + this.btConnect.Click += new System.EventHandler(this.btConnect_Click); + // + // panel1 + // + this.panel1.Controls.Add(this.lbServerIP); + this.panel1.Controls.Add(this.btConnect); + this.panel1.Controls.Add(this.tbServerIP); + this.panel1.Controls.Add(this.tbPort); + this.panel1.Controls.Add(this.lbPort); + this.panel1.Location = new System.Drawing.Point(822, 12); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(174, 513); + this.panel1.TabIndex = 6; + // + // tbDataSending + // + this.tbDataSending.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tbDataSending.Location = new System.Drawing.Point(12, 25); + this.tbDataSending.Multiline = true; + this.tbDataSending.Name = "tbDataSending"; + this.tbDataSending.Size = new System.Drawing.Size(804, 120); + this.tbDataSending.TabIndex = 7; + // + // lbDateSending + // + this.lbDateSending.AutoSize = true; + this.lbDateSending.Location = new System.Drawing.Point(12, 9); + this.lbDateSending.Name = "lbDateSending"; + this.lbDateSending.Size = new System.Drawing.Size(75, 13); + this.lbDateSending.TabIndex = 6; + this.lbDateSending.Text = "Data Sending:"; + // + // lbDataReceiving + // + this.lbDataReceiving.AutoSize = true; + this.lbDataReceiving.Location = new System.Drawing.Point(12, 197); + this.lbDataReceiving.Name = "lbDataReceiving"; + this.lbDataReceiving.Size = new System.Drawing.Size(84, 13); + this.lbDataReceiving.TabIndex = 8; + this.lbDataReceiving.Text = "Data Receiving:"; + // + // tbDataReceiving + // + this.tbDataReceiving.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tbDataReceiving.Location = new System.Drawing.Point(12, 213); + this.tbDataReceiving.Multiline = true; + this.tbDataReceiving.Name = "tbDataReceiving"; + this.tbDataReceiving.Size = new System.Drawing.Size(804, 120); + this.tbDataReceiving.TabIndex = 9; + // + // ClientTest + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1008, 550); + this.Controls.Add(this.tbDataReceiving); + this.Controls.Add(this.lbDataReceiving); + this.Controls.Add(this.lbDateSending); + this.Controls.Add(this.tbDataSending); + this.Controls.Add(this.panel1); + this.Controls.Add(this.statusStrip1); + this.Name = "ClientTest"; + this.Text = "ClientTest"; + this.statusStrip1.ResumeLayout(false); + this.statusStrip1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label lbServerIP; + private System.Windows.Forms.Label lbPort; + private System.Windows.Forms.TextBox tbServerIP; + private System.Windows.Forms.TextBox tbPort; + private System.Windows.Forms.StatusStrip statusStrip1; + private System.Windows.Forms.ToolStripStatusLabel lbConnectionStatus; + private System.Windows.Forms.ToolStripStatusLabel lbConnectionStatusText; + private System.Windows.Forms.Button btConnect; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.TextBox tbDataSending; + private System.Windows.Forms.Label lbDateSending; + private System.Windows.Forms.Label lbDataReceiving; + private System.Windows.Forms.TextBox tbDataReceiving; + } +} + diff --git a/ClientServerLib/ClientTest/ClientTest.cs b/ClientServerLib/ClientTest/ClientTest.cs new file mode 100644 index 00000000..4fc0f9ba --- /dev/null +++ b/ClientServerLib/ClientTest/ClientTest.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Net; +using System.Text; +using System.Windows.Forms; + +using ClientServerLib; + +namespace ClientTest +{ + public partial class ClientTest : Form + { + CClient client; + + public ClientTest() + { + client = new CClient(); + + InitializeComponent(); + } + + protected override void OnClosing(CancelEventArgs e) + { + client.Close(); + base.OnClosing(e); + } + + private void btConnect_Click(object sender, EventArgs e) + { + if (!client.Connected) + Connect(); + else + Disconnect(); + } + + private void Connect() + { + int port = 3000; + if (!int.TryParse(tbPort.Text, out port)) + { + MessageBox.Show("Error parsing port number", "Port Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + IPAddress ip; + if (!IPAddress.TryParse(tbServerIP.Text, out ip)) + { + MessageBox.Show("Error parsing ip address", "IP Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + lbConnectionStatusText.Text = "Connecting..."; + client.Connect(tbServerIP.Text, port, OnConnectionChanged); + } + + private void Disconnect() + { + client.Disconnect(); + } + + private void OnConnectionChanged(bool Connected) + { + if (Connected) + lbConnectionStatusText.Text = "Connected"; + else + { + client.Disconnect(); + lbConnectionStatusText.Text = "Disconnected"; + } + } + } +} diff --git a/ClientServerLib/ClientTest/ClientTest.csproj b/ClientServerLib/ClientTest/ClientTest.csproj new file mode 100644 index 00000000..a86fe023 --- /dev/null +++ b/ClientServerLib/ClientTest/ClientTest.csproj @@ -0,0 +1,93 @@ + + + + Debug + x86 + 8.0.30703 + 2.0 + {48A825C6-728F-4EBF-B98E-3D4665B3BC9E} + WinExe + Properties + ClientTest + ClientTest + v4.0 + Client + 512 + + + x86 + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + x86 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + Form + + + ClientTest.cs + + + + + ClientTest.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + {EC5167E4-CCFB-4CEE-987C-CF816F1062A3} + ClientServerLib + + + + + \ No newline at end of file diff --git a/ClientServerLib/ClientTest/ClientTest.resx b/ClientServerLib/ClientTest/ClientTest.resx new file mode 100644 index 00000000..174ebc72 --- /dev/null +++ b/ClientServerLib/ClientTest/ClientTest.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/ClientServerLib/ClientTest/Program.cs b/ClientServerLib/ClientTest/Program.cs new file mode 100644 index 00000000..f93a6318 --- /dev/null +++ b/ClientServerLib/ClientTest/Program.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; + +namespace ClientTest +{ + static class Program + { + /// + /// Der Haupteinstiegspunkt für die Anwendung. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new ClientTest()); + } + } +} diff --git a/ClientServerLib/ClientTest/Properties/AssemblyInfo.cs b/ClientServerLib/ClientTest/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..d5de0107 --- /dev/null +++ b/ClientServerLib/ClientTest/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die mit einer Assembly verknüpft sind. +[assembly: AssemblyTitle("ClientTest")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ClientTest")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("0bde3ff1-7eb7-4b60-9813-32ff42c0ceff")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ClientServerLib/ClientTest/Properties/Resources.Designer.cs b/ClientServerLib/ClientTest/Properties/Resources.Designer.cs new file mode 100644 index 00000000..af9fcd25 --- /dev/null +++ b/ClientServerLib/ClientTest/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.18034 +// +// Änderungen an dieser Datei können fehlerhaftes Verhalten verursachen und gehen verloren, wenn +// der Code neu generiert wird. +// +//------------------------------------------------------------------------------ + +namespace ClientTest.Properties +{ + + + /// + /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + /// + // Diese Klasse wurde von der StronglyTypedResourceBuilder-Klasse + // über ein Tool wie ResGen oder Visual Studio automatisch generiert. + // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen + // mit der Option /str erneut aus, oder erstellen Sie Ihr VS-Projekt neu. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ClientTest.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle + /// Ressourcenlookups, die diese stark typisierte Ressourcenklasse verwenden. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/ClientServerLib/ClientTest/Properties/Resources.resx b/ClientServerLib/ClientTest/Properties/Resources.resx new file mode 100644 index 00000000..af7dbebb --- /dev/null +++ b/ClientServerLib/ClientTest/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/ClientServerLib/ClientTest/Properties/Settings.Designer.cs b/ClientServerLib/ClientTest/Properties/Settings.Designer.cs new file mode 100644 index 00000000..19867065 --- /dev/null +++ b/ClientServerLib/ClientTest/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.18034 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ClientTest.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/ClientServerLib/ClientTest/Properties/Settings.settings b/ClientServerLib/ClientTest/Properties/Settings.settings new file mode 100644 index 00000000..39645652 --- /dev/null +++ b/ClientServerLib/ClientTest/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/ClientServerLib/ClientTest/bin/Debug/ClientTest.exe b/ClientServerLib/ClientTest/bin/Debug/ClientTest.exe new file mode 100644 index 0000000000000000000000000000000000000000..f1ed2a579ff0f03394e92b42dfb729ab5c45e937 GIT binary patch literal 11776 zcmeHNe{dZ2b${R0>FzAc!a7^BWn*qF+w}ZHXUX#KSfHOa!oP%17IEUSy}Db;i&wkH z?w&10Ly!YBAxT?23`snlX#)+@aVMc>$}~`h)C4jqX-#2Tro%7<)53I`h5#*T2a-7b zyzlPb?a6A&O#fmgEAQL)`@Qe`{@gw9|I7naLqsv$zy3ARlX&vgE#YqlGY~hV|EhtW zn0I;QlVacHmBS|uS1mgBxT8&|dCjtHPaV}&r(~&yrS>1psS|cV&n{R{w>oe=G(faZ z)X*y{?z=6Fc7+r%U(6?BrKHdOaW9@K?i08QXZa`j&`I*3&0F&~LQ@KqlK7 zc(8Qkn^1MbMxO@!7~UzY+7=cZ6C zwXY}oyJnJO0;P{%T1!+E)i%G0TBl?d6V*Xw9W83Fp#JWZ8f-oAmN$>+0t6NX;b*-e#Fj1D*NI9S{ zA8}Euc%}tDJusCSmaMx$$t(kB-5r!cf7VfRW(7aod8~T_qw9WOS$r3ULQKh|nI^?m z@w^LD1S||ExLx-dQl^<osK>ofPPb z% z?W;j&qG5{y&;MTbygprJ709u?1d)9|@aWg6X2X1Cnk$a+v0f~J%Ldpp!^RNi9;nH* z$?&LGjIMIO%KExlaPI;hQ+zZb(T&XI!YHmpxt@yK&RjSH+QI1j3d)61+)d0y&j9UY z6r}(vJzOVI84UvfT!}VT6jF>%vv+$lY>_@&2Y4o6aiUA?Exw$ImS%S=D0^Fl<+KP? zX?ul{-kM(E?qIE*6=t*jsS4q2WE0FZ4$BoN$6%r*y{sw-^PGW55N4!T%nIVW34%zI zTqFqBHhnn#t18FLbdEB$ORd?Cnb{IL zmL^}1IF>!rg%U0OJ@hR`ZL>1X1K^SP5`T0)61Q*iE6A$N>Ow3nRar3Gc{q{Bz^r>{C@1wiIblzmGjL(o$H=$I_n2|m>%S>uj zrkOI$qxt%DM^!_Z=WJnD!6;*f`Y0wN>K+|v%wHZhL|P59RnQP-EU~dD@u*`0iwD{g zcYDFI`+UBf1pUC3X&&$Pz%^{PoP*6HV-7a=>l|zzpi#EEmAj?Bs@a^1b0pGiW~h&$ z*{a~aj<%dd*M}|Vxu0HO@AgxdRDHUo$~QBY*n27(&UhpcOV!n!(PUK==C25&@Ul{# zw@jq#)AOo=Fw?mw;#X?zoi%ZpSx#M={Bfkyc*QD5VIp0x{s1MY3dDTp=|~`{I6Ny5 zGj9m9mnN@80`YoSDVn6})mLUmVt%=9OgO)Ygk{EDb(7xa1);I;Ug`!I>Tz#D{WF8I z6P9Fdl}|Z8)f>b9&Fq!jj6WjTPcUi%4mYEk!xgONaIaN!c(hb=c-99TXL$`3GyY7I zcsPGpr`g^o6Y@8X{XWj_$^z-z7c^&l<%}fPp_aIw!2RNn-u<4y^Bit7H|2WzMD~#G z+9fBiyJhTe}J;~KWrwK?0)e;d@klIfKkz01-|o}-x?)S=R- zY2^3nQ^WQt-Rc_MwoTiT-?Fu1N9Sg}efy48*9I$Do-v_^(Q~?o(wDEHo__wvIe${~ z?#cXnX;;8UClcnM40s?X#2jb*7HE7q!<=NQ(w%Nj)tsLfak zRvpgKw-)*DBCGZs9>j>GCqOS{S+8l2vNA4|Pu$-0h?vJAKU&jCI43jOgBu4V!WiZ^ z96k>b#s!Nd&Si%yK3_?F*7duhF33JkIvvB}eHJ-4&|`EScF)sa0REh=!0)}n6LoY! zJSgJyoM6qE@}Ov-t;%`mACT~*gl8qZB;m_|ak{;xt0qogs3`)Tm+&tod_}^aNtlSS zv{k|`2}`k&Se)*c@avLuQNk;+%ds=`0h$u0Rkby>aq0uCqp4aA_ye_=4Q{~$U4EIIE;_>qM5iSuan`h=Qj zpe?{t^d4#wr+tZIsPh*RZlam~2(W>kg0z92lkjJNacW8)LoGL88*8A0NtTWSHq+T8 z>wi&lE&}r9k0yUqEu=NjkH&^L;h9K3wWLKX~12|QNTSCjwmO9YZ7`&0r=OHlYkc_{Gnn3|0ksg_+zQ_ zQ)%^)gmpD1Xh`Lp?^)U?QqVansRxrwL^G&w1=M=69MsbRwN0p?o|n|ulAjfuF%sXG z)bC0!3cV(&b4$0xZ-VSYpCVf@wRk1r3N^P&>Unxf?1pSeQfKM1*oPX;1k~%utd>@` zvfWvFTT<--^`6KfgilHMtb{KB=ICXKzbfII68;L5a&f-!lQ@b^I_3JuZuqgzc6IDe9O5u9%)?uI7E zeOA1dctyM*-cG!ROjz^0V4au6hw$^N%t{lj$$x>>P062#H^tuMd%%w-|69B-++>bE zgw9nVly4+sicr3noUgRdOG%FM2Bez!X);A?={foZWobG6fs)01W4K;;JDalWvpx6wL*XVUC z2wwZcOVDA7R`ZU{!Hcd*p|vNSM{S=JE>9JCvG60TbWh^R_-gEV^4hiEE|g6DCh9W{ z-SUQY*URdYI)%prH!2u%?4s^?hVBxM4W8UE!Q)Sf5j6^rFXW#+=-}ubdfp4DY@cn7 z8RI2K^Kh`Co?;Ql7m0^1do@=Nm2Wq!f_=u#?zWu?ml%eUN`fJ_U$eAv-61D9SAhSLegz-cgg9Gp`QT79PN zq6etL8}(3I3#C2|kNHy+P}FbWJShci=3}UR-$~6H*9$ZVmWRYpx_lVaWuu~vju^BI z&*0kW<&0>bHj0$YQNIm?LqLaY$D?6T9IJ~^61w*EvH)rE|kfXUPWn#^t^7IW~r}R zp-*dONyquig*7S@L`S&_vM_8C9pC?D@0P2sWg{(0)?2mR)9HMDCNCt1$}mL z$}z@I&LJ9h@NP1pMH>_57|ei*yIF-9v^*Uv2ags5uREya#E9VgpECXJBrKq*^UOXZryV*e zTsqM>tem_e$%WWo#SjZ~z;kRS9MC-)9O@|)u(!CCHER&B@mL_d<9=X&$>g?CuH+Wcf_>VQ zUGk(#Psy`$s7~mz-_g$SQZZy3mannT_V+=legI1cNqzQTMuxS~L99oUl^iM=;*S@Hv_he>op7aRg$U{|qfie9 zf$ao+${~D4xG|I???(?{qF841ypbkLPZsuB=t& z_}i&08Ta*s9`wSH?u_985m3ICJnvMFHRUfcE`F>FzRpBWHra?#MNT*O;r-meUSSdT zK;AR(%+e4!WK$8e1DfS7xQFD^rV_Y$iBsnQaMpE5g}#QT3w?{mCCz*fm<5{&8kPR2 zX-MibzhX@t75rLH27IgVjx7uxIU9WLXdFbIRX&#Y!Lx-w9re}Q7b8FGLdag=6hN~jzvvTj*8Rv5aCyo1*{vkiROnGBu(C2UKVxGCfbIq+Jn0T zK3FrfqSl3HwXJa+ z)v%=&u(Zgpx4k99!~ro8e4)iFok08W^(QrRu5sp}T9r~W=M(IjpNX1OW2#Y!CD58U zhn{&jDe#8K*AYQ`_ZxY~cI`1Q8?MZyf?;Rf}C8o}&qTp~~ zJIN0D$vBG%z~Sm;N{^;y9*q$SBMZ6!MOJDOD3eMJaZ;3qg-Lv^YMgndp`mf+*~Xb4 zBqcL7^8y#=`lU&=Ml>W-GcThdc)Xw_FW!9o?#5T@cCtK~^qcgm|9D-t{p|#rxgs@l znVG4XcO>(DpLpC?{5c!f*Zah;h{rs-Z?hb(Y8$=-PX2wbZdLtj_nWd@F+2GFYU#>n&?ojKX>2r z=U)Ee@plIvx?+DYwXh)-gS>y|(GeUa%+jQOblmVNgx^B{r428hM=LEpYLDJ=baMOF zqw@S25oC*nQMjaUBW8HL$haC()z9$%s#J52_u0l=A;y<1haEK#faFK2a2DN@hGxC*hTS%;u?dfmHl$ zLYq7A{gnN19tQ(EK4owoVLO=mc=>FX_47}{1)RV5L|JLg-0eYl)SrJpskWP4z1g+f zE^T_yLoV9SCuI{YS2233t3KQA-KYP0*Ugg?rg}Q~v9%RTcB`sed7Hl*b+sNI-rcsn zRmFEJtAIC1OYdr((%sgZZ(2~dOAEf!sBmDpU9Ba@+Ue#`;!BC!HW6;-ZFzfQr{+#% zPj|Gc6Pjg=;WKNvkI86orK)9Tg9Uu%!)Ju5jCdPqRW0mhU9J14BJa~#typZ`=zH*- zl8aA}WA?|)XH%=RLu^;immI_lXmA|;&Jyy}3quY*k(v6q?tV!xZl>}u8ALF+VryLMXDlF^gL2O^YlOfy|QXq0raagJ=l)Hhb;zH4K-2vFFy dG3+4_JhDE4SGWH=_LWVaz}$bE^8dR6{{x#+)&c+k literal 0 HcmV?d00001 diff --git a/ClientServerLib/ClientTest/bin/Debug/ClientTest.vshost.exe b/ClientServerLib/ClientTest/bin/Debug/ClientTest.vshost.exe new file mode 100644 index 0000000000000000000000000000000000000000..bb84a51ac4f20534146c09f2fd9c928d15a903b2 GIT binary patch literal 11600 zcmeG?2~<)?tG75|5OPOMcC{DsnWb(K|p-9Gz zCYX{WA(Jm;dIfQqiJ};SrLL}G(n&hl8zFy;j>apOj_;bS1<^27Oc^0Hz(7W(Czb%1 z@VUW90w!d%4!)s8@lWO`4L-o%@Fz33`3PxI|9bMyD%lrO3DBh`b_0^65V}%-gn|b^ zWm*RtcF<41tL^XP%x>1H-pWw-?KO@iBWB8={z?S^v{7pXzI!Jo%+8WXB|Jbhzz%$;+6FHiiU_~N3I z3EN^9UtHuj{7z%|8vGH<%)(m1Ea7^jMXH#Y>ya2$DiaMPF|c+I&NvSYVbqF9*~#r#ILj8dWjm251tb7C6J%*T7omHQ z|3QZ`M<(G55b{zd>0OBcT-q z$5D_+!Bh&aq+kUFn<&Vjy~Gre83lc5nD5M>aGT=)?fZgOM>nV`Kqv zI0bDf=!~2J||60WgZ0+&DGHhBHk2136`;HfS!lVrq~L;Qp?LLw4#2#K5zlOj)1BCy1d77#r{JpDO<77O@Xp{xhn!4xZ$h!p@= zFfDSIN{Pg1K}r~3)_Zst+KZ6#Bz!U1wTDViV=OmGAajRFq+hp^({qI>UpIiLVv=~h zxay+kDH5kh_;K;yXzG;G*Tj7I0^&~_{e-b1Ng|izo-6pOSGO>vq9h3^M-Ep;2n2i? z;VA;Ob9u6_h6EBx&>#pUI*0gkg>gyTxL$JaT8kGU;HGf+ao-Tzcp`=5x@Rq2jC5KA zDL81CbnCa%R49SU_|be|v6omoM|<(PaYE7-c~ay>L?^|?aX&MN4jh;n*m;M|z$BDt zLV?wpRH>I1yLLe*EkY6~5>Aj35>FW2!};IZ6RZg`l4U+2WpPr3Jg{aVFJ1yBKdala z!{Q|bH-_8;-f}`H`;N<(`ErFZ0s_H?nELq^^AiFst^h{IAPxchk}HJW2r40D)L?1| zN5+-NKyKu|BH5O9=>_$oO%{WM1RNkt$|LX>+U#yGW4SL{W#Dq<^-?pWTs_=s z+sMi))7<${{lWc@(;pS&e4xt(nf|K_4fiC?yA3+_%vvY*l!4@4iv}=BM|c$3*(8Ejv=a+B_=Ss*JEQXuIqpixQ3a_4(W> zyEKm#xtQrM)b=Fwn2Ghv>;fV#EMt&fQn_Nx)Omd`sf+@qw7IY%8d=~wU}z94&^xT# z?O1hMy$gBFV<=2Rx4X%UFBOAX2ZDn_3e*V(5c0klQtL7p%59=S80yz;986D8eMaINL%0wc8G%|*e$MEHmK74Q% z!Aa2qJ`Z*(ODar2$dFuS7_I+z60R6Fg7-`wLHR_mTT!wUQ^u&61Y`_zfv7uESG-PI zL=b$z5K9F^02Rvd;_$Xby>8#V-GB3vXN;}hekVw2b}%EC!sUQGG$K1h+L_tfM2e!P zOCtHUjw8EKEOAUULMMT4SGq-(nO(Kd^R74+p{XEA@0kR39#JP2L9k*3WE=-K98<(} zqe}k)-;w}E#jg-2r+wZ^?F0lS_7$G&N8TMKx*>Foia|&WS+)ZhjyMosg(Ghi3MD_V zlt8Ha0ra7)2BA8})Ao*tk&JOg@{U+nBp*Xj>4k4odB9b z6pKVuFB2+W3WHWIV5QK`MKb6of?7waV3?3D4|tM6FFxSozD0@ZgTuNz{_IdRDFbAO z)M2bA&`X3r0!GVV7PvbAj0da~>bA%RB0vJogpW-A5a@S}5mS7nfE2jYc(OeJxZnXV z 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 Date: Sat, 4 May 2013 17:20:43 +0200 Subject: [PATCH 04/23] modified controller class to support more than one controller --- .../ClientServerLib/ClientServerLib.csproj | 4 +- .../ClientTest/bin/Debug/ClientTest.exe | Bin 11776 -> 0 bytes .../bin/Debug/ClientTest.vshost.exe | Bin 11600 -> 0 bytes Vocaluxe/Base/CController.cs | 79 +++++++++++++++--- 4 files changed, 69 insertions(+), 14 deletions(-) delete mode 100644 ClientServerLib/ClientTest/bin/Debug/ClientTest.exe delete mode 100644 ClientServerLib/ClientTest/bin/Debug/ClientTest.vshost.exe diff --git a/ClientServerLib/ClientServerLib/ClientServerLib.csproj b/ClientServerLib/ClientServerLib/ClientServerLib.csproj index 4a958128..90c0aa91 100644 --- a/ClientServerLib/ClientServerLib/ClientServerLib.csproj +++ b/ClientServerLib/ClientServerLib/ClientServerLib.csproj @@ -17,7 +17,7 @@ true full false - bin\Debug\ + bin\ DEBUG;TRACE prompt 4 @@ -25,7 +25,7 @@ pdbonly true - bin\Release\ + bin\ TRACE prompt 4 diff --git a/ClientServerLib/ClientTest/bin/Debug/ClientTest.exe b/ClientServerLib/ClientTest/bin/Debug/ClientTest.exe deleted file mode 100644 index f1ed2a579ff0f03394e92b42dfb729ab5c45e937..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11776 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 diff --git a/ClientServerLib/ClientTest/bin/Debug/ClientTest.vshost.exe b/ClientServerLib/ClientTest/bin/Debug/ClientTest.vshost.exe deleted file mode 100644 index bb84a51ac4f20534146c09f2fd9c928d15a903b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11600 zcmeG?2~<)?tG75|5OPOMcC{DsnWb(K|p-9Gz zCYX{WA(Jm;dIfQqiJ};SrLL}G(n&hl8zFy;j>apOj_;bS1<^27Oc^0Hz(7W(Czb%1 z@VUW90w!d%4!)s8@lWO`4L-o%@Fz33`3PxI|9bMyD%lrO3DBh`b_0^65V}%-gn|b^ zWm*RtcF<41tL^XP%x>1H-pWw-?KO@iBWB8={z?S^v{7pXzI!Jo%+8WXB|Jbhzz%$;+6FHiiU_~N3I z3EN^9UtHuj{7z%|8vGH<%)(m1Ea7^jMXH#Y>ya2$DiaMPF|c+I&NvSYVbqF9*~#r#ILj8dWjm251tb7C6J%*T7omHQ z|3QZ`M<(G55b{zd>0OBcT-q z$5D_+!Bh&aq+kUFn<&Vjy~Gre83lc5nD5M>aGT=)?fZgOM>nV`Kqv zI0bDf=!~2J||60WgZ0+&DGHhBHk2136`;HfS!lVrq~L;Qp?LLw4#2#K5zlOj)1BCy1d77#r{JpDO<77O@Xp{xhn!4xZ$h!p@= zFfDSIN{Pg1K}r~3)_Zst+KZ6#Bz!U1wTDViV=OmGAajRFq+hp^({qI>UpIiLVv=~h zxay+kDH5kh_;K;yXzG;G*Tj7I0^&~_{e-b1Ng|izo-6pOSGO>vq9h3^M-Ep;2n2i? z;VA;Ob9u6_h6EBx&>#pUI*0gkg>gyTxL$JaT8kGU;HGf+ao-Tzcp`=5x@Rq2jC5KA zDL81CbnCa%R49SU_|be|v6omoM|<(PaYE7-c~ay>L?^|?aX&MN4jh;n*m;M|z$BDt zLV?wpRH>I1yLLe*EkY6~5>Aj35>FW2!};IZ6RZg`l4U+2WpPr3Jg{aVFJ1yBKdala z!{Q|bH-_8;-f}`H`;N<(`ErFZ0s_H?nELq^^AiFst^h{IAPxchk}HJW2r40D)L?1| zN5+-NKyKu|BH5O9=>_$oO%{WM1RNkt$|LX>+U#yGW4SL{W#Dq<^-?pWTs_=s z+sMi))7<${{lWc@(;pS&e4xt(nf|K_4fiC?yA3+_%vvY*l!4@4iv}=BM|c$3*(8Ejv=a+B_=Ss*JEQXuIqpixQ3a_4(W> zyEKm#xtQrM)b=Fwn2Ghv>;fV#EMt&fQn_Nx)Omd`sf+@qw7IY%8d=~wU}z94&^xT# z?O1hMy$gBFV<=2Rx4X%UFBOAX2ZDn_3e*V(5c0klQtL7p%59=S80yz;986D8eMaINL%0wc8G%|*e$MEHmK74Q% z!Aa2qJ`Z*(ODar2$dFuS7_I+z60R6Fg7-`wLHR_mTT!wUQ^u&61Y`_zfv7uESG-PI zL=b$z5K9F^02Rvd;_$Xby>8#V-GB3vXN;}hekVw2b}%EC!sUQGG$K1h+L_tfM2e!P zOCtHUjw8EKEOAUULMMT4SGq-(nO(Kd^R74+p{XEA@0kR39#JP2L9k*3WE=-K98<(} zqe}k)-;w}E#jg-2r+wZ^?F0lS_7$G&N8TMKx*>Foia|&WS+)ZhjyMosg(Ghi3MD_V zlt8Ha0ra7)2BA8})Ao*tk&JOg@{U+nBp*Xj>4k4odB9b z6pKVuFB2+W3WHWIV5QK`MKb6of?7waV3?3D4|tM6FFxSozD0@ZgTuNz{_IdRDFbAO z)M2bA&`X3r0!GVV7PvbAj0da~>bA%RB0vJogpW-A5a@S}5mS7nfE2jYc(OeJxZnXV z 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 _Controller; + + private static List _KeysPool; + private static List _MousePool; public static void Init() { - _Controller = new CWiiMote(); + _Controller = new List(); + _KeysPool = new List(); + _MousePool = new List(); + + _Controller.Add(new CWiiMote()); - _Controller.Init(); + foreach (IController controller in _Controller) + { + controller.Init(); + } } public static void Close() { - _Controller.Close(); - _Controller = null; + foreach (IController controller in _Controller) + { + controller.Close(); + } + + _Controller = new List(); } public static void Connect() { - _Controller.Connect(); + foreach (IController controller in _Controller) + { + controller.Connect(); + } } public static void Disconnect() { - _Controller.Disconnect(); + foreach (IController controller in _Controller) + { + controller.Disconnect(); + } } public static bool IsConnected() { - return _Controller.IsConnected(); + //return _Controller.IsConnected(); + return false; //should be changed } public static void Update() { - _Controller.Update(); + foreach (IController controller in _Controller) + { + controller.Update(); + + SKeyEvent ke = new SKeyEvent(); + while (controller.PollKeyEvent(ref ke)) + { + _KeysPool.Add(ke); + } + + SMouseEvent me = new SMouseEvent(); + while (controller.PollMouseEvent(ref me)) + { + _MousePool.Add(me); + } + } + } public static bool PollKeyEvent(ref SKeyEvent keyEvent) { - return _Controller.PollKeyEvent(ref keyEvent); + if (_KeysPool.Count > 0) + { + keyEvent = _KeysPool[0]; + _KeysPool.RemoveAt(0); + return true; + } + return false; } public static bool PollMouseEvent(ref SMouseEvent mouseEvent) { - return _Controller.PollMouseEvent(ref mouseEvent); + if (_MousePool.Count > 0) + { + mouseEvent = _MousePool[0]; + _MousePool.RemoveAt(0); + return true; + } + return false; } public static void SetRumble(float duration) { - _Controller.SetRumble(duration); + foreach (IController controller in _Controller) + { + controller.SetRumble(duration); + } } } } \ No newline at end of file From 7f7185367210f88142a75adc86d3ceedb1b30858 Mon Sep 17 00:00:00 2001 From: Alexander Eckart Date: Sat, 4 May 2013 19:01:09 +0200 Subject: [PATCH 05/23] added Server/CVocaluxeServer.cs --- .gitignore | 5 ++ ClientServerLib/ClientServerLib/CClient.cs | 48 ++++++++++++-- ClientServerLib/ClientServerLib/Structs.cs | 3 + .../ClientTest/ClientTest.Designer.cs | 37 +++++++++++ ClientServerLib/ClientTest/ClientTest.cs | 60 ++++++++++++++++-- Output/ClientServerLib.dll | Bin 0 -> 36864 bytes Vocaluxe/Base/CConfig.cs | 4 ++ Vocaluxe/Base/Server/CVocaluxeServer.cs | 36 +++++++++++ Vocaluxe/Program.cs | 12 +++- Vocaluxe/Vocaluxe.csproj | 4 ++ 10 files changed, 199 insertions(+), 10 deletions(-) create mode 100644 Output/ClientServerLib.dll create mode 100644 Vocaluxe/Base/Server/CVocaluxeServer.cs diff --git a/.gitignore b/.gitignore index 3fb7c02c..0e90224b 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,8 @@ Output/Songs /ClientServerLib/ClientServerLib/ClientServerLib.csproj.user /ClientServerLib/ClientServerLib/bin/Debug/ClientServerLib.dll /ClientServerLib/ClientTest/bin/Debug/ClientServerLib.dll +/ClientServerLib/ClientServerLib/bin/ClientServerLib.dll +/ClientServerLib/ClientTest/bin/Debug/ClientTest.exe +/ClientServerLib/ClientTest/bin/Debug/ClientTest.vshost.exe +/ClientServerLib/ClientTest/bin/Release/ClientTest.vshost.exe +/ClientServerLib/ClientTest/bin/Release/ClientTest.vshost.exe.manifest diff --git a/ClientServerLib/ClientServerLib/CClient.cs b/ClientServerLib/ClientServerLib/CClient.cs index 162b2955..41347cf3 100644 --- a/ClientServerLib/ClientServerLib/CClient.cs +++ b/ClientServerLib/ClientServerLib/CClient.cs @@ -25,6 +25,8 @@ namespace ClientServerLib private List requests; private List responses; private OnConnectionChanged onConnectionChanged; + private OnSend onSend; + private OnReceived onReceived; public bool Connected { @@ -51,12 +53,17 @@ namespace ClientServerLib clientThread.Start(); } - public void Connect(string IP = "127.0.0.1", int Port = 3000, OnConnectionChanged OnConnectionChanged = null) + public void Connect(string IP = "127.0.0.1", int Port = 3000, + OnConnectionChanged OnConnectionChanged = null, + OnSend OnSend = null, + OnReceived OnReceived = null) { if (!doConnect) { - if (OnConnectionChanged != null) - onConnectionChanged = OnConnectionChanged; + onConnectionChanged = OnConnectionChanged; + onSend = OnSend; + onReceived = OnReceived; + ip = IP; port = Port; doConnect = true; @@ -114,7 +121,7 @@ namespace ClientServerLib connected = true; RaiseOnConnectionChanged(); } - catch + catch (Exception e) { connected = false; RaiseOnConnectionChanged(); @@ -134,6 +141,7 @@ namespace ClientServerLib try { connection.TcpClient.Close(); + connection = new CConnection(new TcpClient(), -1); } catch { @@ -184,7 +192,10 @@ namespace ClientServerLib byte[] serverParam = new byte[bytesRead]; Array.Copy(data, serverParam, bytesRead); + RaiseOnReceived(serverParam); + byte[] clientResponse = connection.CreateClientKey(serverParam); + RaiseOnSend(clientResponse); clientStream.Write(clientResponse, 0, clientResponse.Length); clientStream.Flush(); } @@ -206,7 +217,8 @@ namespace ClientServerLib { try { - responses[0].Callback(responses[0].Response); + if (responses[0].Callback != null) + responses[0].Callback(responses[0].Response); } finally { @@ -227,6 +239,7 @@ namespace ClientServerLib NetworkStream clientStream = connection.TcpClient.GetStream(); + RaiseOnSend(request.Command); byte[] encrypted = connection.Encrypt(request.Command); clientStream.Write(encrypted, 0, encrypted.Length); @@ -257,6 +270,7 @@ namespace ClientServerLib encrypted = new byte[bytesRead]; Array.Copy(data, encrypted, bytesRead); response.Response = connection.Decrypt(encrypted); + RaiseOnReceived(response.Response); return response; } @@ -272,5 +286,29 @@ namespace ClientServerLib } catch {} } + + private void RaiseOnSend(byte[] Message) + { + if (onSend == null) + return; + + try + { + onSend(Message); + } + catch { } + } + + private void RaiseOnReceived(byte[] Message) + { + if (onReceived == null) + return; + + try + { + onReceived(Message); + } + catch { } + } } } diff --git a/ClientServerLib/ClientServerLib/Structs.cs b/ClientServerLib/ClientServerLib/Structs.cs index d3f3c41f..9e1309ed 100644 --- a/ClientServerLib/ClientServerLib/Structs.cs +++ b/ClientServerLib/ClientServerLib/Structs.cs @@ -7,7 +7,10 @@ namespace ClientServerLib { public delegate void ResponseCallback(byte[] Response); public delegate byte[] HandleRequest(int ConnectionID, byte[] Message); + public delegate void OnConnectionChanged(bool Connected); + public delegate void OnSend(byte[] Message); + public delegate void OnReceived(byte[] Message); public struct SRequest { diff --git a/ClientServerLib/ClientTest/ClientTest.Designer.cs b/ClientServerLib/ClientTest/ClientTest.Designer.cs index a0dc68e9..c04db48e 100644 --- a/ClientServerLib/ClientTest/ClientTest.Designer.cs +++ b/ClientServerLib/ClientTest/ClientTest.Designer.cs @@ -41,6 +41,9 @@ this.lbDateSending = new System.Windows.Forms.Label(); this.lbDataReceiving = new System.Windows.Forms.Label(); this.tbDataReceiving = new System.Windows.Forms.TextBox(); + this.tbPassword = new System.Windows.Forms.TextBox(); + this.lbPassword = new System.Windows.Forms.Label(); + this.btLogin = new System.Windows.Forms.Button(); this.statusStrip1.SuspendLayout(); this.panel1.SuspendLayout(); this.SuspendLayout(); @@ -114,6 +117,9 @@ // // panel1 // + this.panel1.Controls.Add(this.btLogin); + this.panel1.Controls.Add(this.lbPassword); + this.panel1.Controls.Add(this.tbPassword); this.panel1.Controls.Add(this.lbServerIP); this.panel1.Controls.Add(this.btConnect); this.panel1.Controls.Add(this.tbServerIP); @@ -162,6 +168,34 @@ this.tbDataReceiving.Size = new System.Drawing.Size(804, 120); this.tbDataReceiving.TabIndex = 9; // + // tbPassword + // + this.tbPassword.Location = new System.Drawing.Point(85, 113); + this.tbPassword.Name = "tbPassword"; + this.tbPassword.Size = new System.Drawing.Size(86, 20); + this.tbPassword.TabIndex = 6; + this.tbPassword.Text = "vocaluxe"; + // + // lbPassword + // + this.lbPassword.AutoSize = true; + this.lbPassword.Location = new System.Drawing.Point(3, 116); + this.lbPassword.Name = "lbPassword"; + this.lbPassword.Size = new System.Drawing.Size(56, 13); + this.lbPassword.TabIndex = 7; + this.lbPassword.Text = "Password:"; + // + // btLogin + // + this.btLogin.Enabled = false; + this.btLogin.Location = new System.Drawing.Point(6, 139); + this.btLogin.Name = "btLogin"; + this.btLogin.Size = new System.Drawing.Size(165, 23); + this.btLogin.TabIndex = 8; + this.btLogin.Text = "Login"; + this.btLogin.UseVisualStyleBackColor = true; + this.btLogin.Click += new System.EventHandler(this.btLogin_Click); + // // ClientTest // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -199,6 +233,9 @@ private System.Windows.Forms.Label lbDateSending; private System.Windows.Forms.Label lbDataReceiving; private System.Windows.Forms.TextBox tbDataReceiving; + private System.Windows.Forms.Button btLogin; + private System.Windows.Forms.Label lbPassword; + private System.Windows.Forms.TextBox tbPassword; } } diff --git a/ClientServerLib/ClientTest/ClientTest.cs b/ClientServerLib/ClientTest/ClientTest.cs index 4fc0f9ba..3ed470ee 100644 --- a/ClientServerLib/ClientTest/ClientTest.cs +++ b/ClientServerLib/ClientTest/ClientTest.cs @@ -37,6 +37,11 @@ namespace ClientTest Disconnect(); } + private void btLogin_Click(object sender, EventArgs e) + { + + } + private void Connect() { int port = 3000; @@ -54,7 +59,7 @@ namespace ClientTest } lbConnectionStatusText.Text = "Connecting..."; - client.Connect(tbServerIP.Text, port, OnConnectionChanged); + client.Connect(tbServerIP.Text, port, OnConnectionChanged, OnSend, OnReceived); } private void Disconnect() @@ -65,12 +70,59 @@ namespace ClientTest private void OnConnectionChanged(bool Connected) { if (Connected) - lbConnectionStatusText.Text = "Connected"; + { + this.Invoke((MethodInvoker)delegate + { + lbConnectionStatusText.Text = "Connected"; + btConnect.Text = "Disconnect"; + btLogin.Enabled = true; + }); + } else { - client.Disconnect(); - lbConnectionStatusText.Text = "Disconnected"; + this.Invoke((MethodInvoker)delegate + { + client.Disconnect(); + lbConnectionStatusText.Text = "Disconnected"; + btConnect.Text = "Connect"; + btLogin.Text = "Login"; + btLogin.Enabled = false; + }); } } + + private void OnSend(byte[] Message) + { + if (Message == null) + return; + + string text = String.Empty; + foreach (byte b in Message) + { + text += b.ToString() + " "; + } + + this.Invoke((MethodInvoker)delegate + { + tbDataSending.Text = text; + }); + } + + private void OnReceived(byte[] Message) + { + if (Message == null) + return; + + string text = String.Empty; + foreach (byte b in Message) + { + text += b.ToString() + " "; + } + + this.Invoke((MethodInvoker)delegate + { + tbDataReceiving.Text = text; + }); + } } } diff --git a/Output/ClientServerLib.dll b/Output/ClientServerLib.dll new file mode 100644 index 0000000000000000000000000000000000000000..b762cd237b2e059b224f9817881eb2d6a14ba335 GIT binary patch literal 36864 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 l?y~!pXf}?n$MFB(_vF&_W` literal 0 HcmV?d00001 diff --git a/Vocaluxe/Base/CConfig.cs b/Vocaluxe/Base/CConfig.cs index baf4b93c..73c65f55 100644 --- a/Vocaluxe/Base/CConfig.cs +++ b/Vocaluxe/Base/CConfig.cs @@ -112,6 +112,10 @@ namespace Vocaluxe.Base public static EWebcamLib WebcamLib = EWebcamLib.OpenCV; public static SWebcamConfig WebcamConfig; + // Server + public static EOffOn Server = EOffOn.TR_CONFIG_ON; + public static int ServerPort = 3000; + //Lists to save parameters and values private static readonly List _Params = new List(); private static readonly List _Values = new List(); diff --git a/Vocaluxe/Base/Server/CVocaluxeServer.cs b/Vocaluxe/Base/Server/CVocaluxeServer.cs new file mode 100644 index 00000000..b4fd8e2a --- /dev/null +++ b/Vocaluxe/Base/Server/CVocaluxeServer.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using ClientServerLib; +using VocaluxeLib.Menu; + +namespace Vocaluxe.Base.Server +{ + static class CVocaluxeServer + { + private static CServer _Server; + + public static void Init() + { + _Server = new CServer(RequestHandler, CConfig.ServerPort); + } + + public static void Start() + { + if (CConfig.Server == EOffOn.TR_CONFIG_ON) + _Server.Start(); + } + + public static void Close() + { + _Server.Stop(); + } + + private static byte[] RequestHandler(int ConnectionID, byte[] Message) + { + return null; + } + } +} diff --git a/Vocaluxe/Program.cs b/Vocaluxe/Program.cs index 5f204724..ea384764 100644 --- a/Vocaluxe/Program.cs +++ b/Vocaluxe/Program.cs @@ -26,6 +26,7 @@ using System.Windows.Forms; using Vocaluxe.Base; using System.Runtime.ExceptionServices; using Vocaluxe.Base.Font; +using Vocaluxe.Base.Server; namespace Vocaluxe { @@ -183,6 +184,13 @@ namespace Vocaluxe Application.DoEvents(); + // Init Server + CLog.StartBenchmark(0, "Init Server"); + CVocaluxeServer.Init(); + CLog.StopBenchmark(0, "Init Server"); + + Application.DoEvents(); + // Init Input CLog.StartBenchmark(0, "Init Input"); CController.Init(); @@ -210,6 +218,7 @@ namespace Vocaluxe // Start Main Loop _SplashScreen.Close(); + CVocaluxeServer.Start(); CDraw.MainLoop(); } @@ -219,7 +228,7 @@ namespace Vocaluxe // Unloading try { - CLog.CloseAll(); + CVocaluxeServer.Close(); CController.Close(); CSound.RecordCloseAll(); CSound.CloseAllStreams(); @@ -227,6 +236,7 @@ namespace Vocaluxe CDraw.Unload(); CDataBase.CloseConnections(); CWebcam.Close(); + CLog.CloseAll(); } catch (Exception) {} Environment.Exit(Environment.ExitCode); diff --git a/Vocaluxe/Vocaluxe.csproj b/Vocaluxe/Vocaluxe.csproj index 46d4c11c..0c75048d 100644 --- a/Vocaluxe/Vocaluxe.csproj +++ b/Vocaluxe/Vocaluxe.csproj @@ -152,6 +152,9 @@ ..\Output\AForge.Video.DirectShow.dll False + + ..\ClientServerLib\ClientServerLib\bin\ClientServerLib.dll + ..\Output\Community.CsharpSqlite.dll @@ -197,6 +200,7 @@ + Form From 1147d612fe6aeac99d46796776b0a3ad5005da52 Mon Sep 17 00:00:00 2001 From: Alexander Eckart Date: Sat, 4 May 2013 20:28:32 +0200 Subject: [PATCH 06/23] added login function --- ClientServerLib/ClientServerLib/CClient.cs | 6 -- .../ClientServerLib/CConnection.cs | 2 - ClientServerLib/ClientServerLib/CServer.cs | 37 ++++--- .../ClientServerLib/ClientServerLib.csproj | 2 + .../ClientServerLib/Vocaluxe/CCommands.cs | 99 ++++++++++++++++++ .../Vocaluxe/VocaluxeStructs.cs | 10 ++ .../ClientTest/ClientTest.Designer.cs | 2 +- ClientServerLib/ClientTest/ClientTest.cs | 39 +++++++ Output/ClientServerLib.dll | Bin 36864 -> 38912 bytes Vocaluxe/Base/CConfig.cs | 1 + Vocaluxe/Base/Server/CClientHandler.cs | 18 ++++ Vocaluxe/Base/Server/CVocaluxeServer.cs | 55 +++++++++- Vocaluxe/Vocaluxe.csproj | 1 + 13 files changed, 249 insertions(+), 23 deletions(-) create mode 100644 ClientServerLib/ClientServerLib/Vocaluxe/CCommands.cs create mode 100644 ClientServerLib/ClientServerLib/Vocaluxe/VocaluxeStructs.cs create mode 100644 Vocaluxe/Base/Server/CClientHandler.cs diff --git a/ClientServerLib/ClientServerLib/CClient.cs b/ClientServerLib/ClientServerLib/CClient.cs index 41347cf3..fe8b4a58 100644 --- a/ClientServerLib/ClientServerLib/CClient.cs +++ b/ClientServerLib/ClientServerLib/CClient.cs @@ -33,12 +33,6 @@ namespace ClientServerLib get { return connected; } } - public bool LoggedIn - { - get { return connection.LoggedIn; } - set { connection.LoggedIn = value; } - } - public CClient() { doConnect = false; diff --git a/ClientServerLib/ClientServerLib/CConnection.cs b/ClientServerLib/ClientServerLib/CConnection.cs index 50aa3460..900a4366 100644 --- a/ClientServerLib/ClientServerLib/CConnection.cs +++ b/ClientServerLib/ClientServerLib/CConnection.cs @@ -18,7 +18,6 @@ namespace ClientServerLib private int connectionID; public TcpClient TcpClient; - public bool LoggedIn; public bool KeySet { @@ -34,7 +33,6 @@ namespace ClientServerLib { TcpClient = client; keySet = false; - LoggedIn = false; connectionID = ConnectionID; encoder = new UTF8Encoding(); } diff --git a/ClientServerLib/ClientServerLib/CServer.cs b/ClientServerLib/ClientServerLib/CServer.cs index 24e54557..37f44139 100644 --- a/ClientServerLib/ClientServerLib/CServer.cs +++ b/ClientServerLib/ClientServerLib/CServer.cs @@ -73,28 +73,39 @@ namespace ClientServerLib private void ListenForClients() { tcpListener.Start(); + TcpClient client; while (running) { //wait for new client - TcpClient client = tcpListener.AcceptTcpClient(); - - int id = ids.Dequeue(); - CConnection connection = new CConnection(client, id); - - lock (clients) + try { - clients.Add(id, connection); + client = tcpListener.AcceptTcpClient(); + } + catch + { + client = null; } - Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientCom)); + if (client != null) + { + int id = ids.Dequeue(); + CConnection connection = new CConnection(client, id); - NetworkStream clientStream = client.GetStream(); - byte[] serverParams = connection.GetKeyParams(); - clientStream.Write(serverParams, 0, serverParams.Length); - clientStream.Flush(); + lock (clients) + { + clients.Add(id, connection); + } - clientThread.Start(connection); + Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientCom)); + + NetworkStream clientStream = client.GetStream(); + byte[] serverParams = connection.GetKeyParams(); + clientStream.Write(serverParams, 0, serverParams.Length); + clientStream.Flush(); + + clientThread.Start(connection); + } } } diff --git a/ClientServerLib/ClientServerLib/ClientServerLib.csproj b/ClientServerLib/ClientServerLib/ClientServerLib.csproj index 90c0aa91..b9a347bb 100644 --- a/ClientServerLib/ClientServerLib/ClientServerLib.csproj +++ b/ClientServerLib/ClientServerLib/ClientServerLib.csproj @@ -48,6 +48,8 @@ + + Q#q$Ahr%Iy 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°t~YNY7f^LDWS(wq2^oMA*CI2^_U{LZhG(fx!_r+W}>6 zFt1J{RDA<9;DA*;aabA*2AtSakEOJoMB3h?NDdXUYN|t33r|b)skL8iJrf}?dRtNS zFW{8@MIQL?yMON(yp`)Z3elTW|NAYUo?CU`iRnMt{=)Dp|911K`#;_Hdz=36zklY2 zJMR3CAN=jv#;t$z7eoL0(378eVEZ3`;Lk9{dYV5 z_5bj%%FmA#o_e{w{&O$?$*o_1^w6Kq-~E?gICtlZ|MKjEZ`}B5{BOSTKmND>b+Z47 zfAjDEpD!%@H-EZ%`|$7ltC3r4zx(k^^S2lO=R<|)uEWPqoNLUVKhta+pE}o=ZZ=Of zW}9cuHfN^ho3o7*ryBFen~mSS|DL-W=gu@|P8>Ty#EH|Vwly9&NBD_TQ)kbU>fC&D zYIYl+zb-@{`3IHwz~d)WYbcKs`gU^JSGsh4fV`U!MR0OZUq%@_!h0}%SM(O+Y`tR7 zE5(t%drR+$H^;ZfTUN!J;!*oQuBVHlSK~>J4vq46TWw!jTgolGuGEubAZ#Bj!yy^} zeLA07?Je=_;rKKAcFNVwrCPkX9N%7!x0F@mc-3mSt(+C_1(!?dh?52QWn|Jf*D)9a z2|F9qvu{vw)si@?9p)UtJ5YjG8cai_9{j1tU+hY(N|=HRb|nwvy|q+gyNSXr6OOCB z(dB89Yc73Y7MreXMXG+`WP4;JhNtXen*C){7-A#VwH%c5jcqLRBw1#DGv3536mvrJ; z&(=$2%H?{2)=Q!`m}i5ot@eZdVy#{(^~4R{5gT+9G_()5Mu z?f2D^*ct!Umnsz{8|h2+pjQa2?bE@3x|2iqBI_>?LSC}Sq3sMXVM!J8?sI;PfA;w&AKBRXqf4C=KX#fvf%0S9e(le^c@iyeB_{ywMvt9%X$c z7yZjF)`fX!OYC8|yD zBB_jfkP%Fbf-NZ%Vlue9Nt$}Ra6+@(lQ%Jv$Vw3lQ-rAu6%$u1!;}|y!e3KEKtb1NK|AKlRLh#%B~x2?nLkZT6Y3EC5l$*YCsN^!1Z^)yy;8D;Mzl@~ zY@qb)Vdg6&;IbdPP9v!*3^X=NwJzOMVzE}25|~d3SyZTN_a_4s@IdL|_+DX_TMX`{ zIox$1hK9mKf}148dU*7(Ccth{BH=ex&OqYosS;IUen4GXfY$2nNOhzKFB*Hbdef49 zx~a)zL!oz&SZ(h`+4NE0GYD5zts~VMz163wiAux|=VK}hsMTU@U|#A(#%wr^u%R!U zPb~_#s;eP`{5=fP3G2hY5|S8l+RbvKh?xQf+^13!tnoTH5Ba$k2;fyvKP%9@lq7)p z!O~y~7)mhEL2?G>k}5}tWf2A1`iwx72DtF#`hwjeQ-}^1D|_eWPJi;?<8$*T&dfEV zAp27xUgJc6stw=_v^?4gTNtIN2ApSwqP#Ad*kJtX5AlEc!|#1C7Kwf^3}}hz7*ak6 zV5z7&0DK4Ln_Ow7n~}5+lEY7$j3MsKGhlO4nP6-`)dQktVps{M6BoXmPLJjOO2=JW z_^P?#2DntD?SAmNG)?mjy-bqV6h2fEd-r-XN18&L6xbWv9)|U#1sPxHlayR|d58t{ zc;RcX@qu!6ZK;GCXS{F;s{RtMxB`LchNnb3zC_p~PNWzTp?vNqphl#7`*?875ic|0 zao*Z=sJHwImtSv9g)9SFX#04Dg4)L^$9%L+@~sn``qEr;sz&MNEZ1D{!E1Eq1TZqM zL6NjUy6}vK>cXErT!{WNQ>V|IJ$AB{7voUpQHdETqHqXpW%^d~uI z@K7Oo@E5M&{_BDNpBJJd|KQTE4*Y|K=>A{0_&d*@J$?2nSTR2Q3}PbTJ`Ttk(TLg& z%;u zQ`d}y6fzfaru#h_#EcJyD~!ip8n`-7pOQRGMML&`M$^yR^96gpSc2as*jufdUIvg~ zl!3(DAoUTST@OIvM+Rtm`+S`IwRH73DeG`h8DZk{BTn>V?vc`f?yZ#wO?Ajs6T+OI zx(1^)_CeC!gal0^KzF8-ys|FKk%hi0+d=Cx`MRQm0;6MC?A<+>tIc&DnQBTzfO+Iu z7dWm~>4+AGNtK}A^v|fst}IUSb3I6t_oMf`g0+!LY8tNu9)>PrJ=R7Cl}>< znvAbQ4#3b72wi#89TD8gBM1kUByfFC*G8=J1gn-La3ye+(YEH2I%Wm+87xas8Fawc zyAYgegsm&$nW4LqgC-Z9RID`UZ3t`D+7brt$)LA7dU~bpw98D~v@r`xgm_wiC&JYA zgOFU@eoc@mACWfJwf7*c((f_M13ila4(lA@Us69$)Nr+l*yTA+?7?OJ_S_t zr+`5XT~noG-uC1=t%UD&n8d=5blO8g_DApobTW<-$)>CLE89zJ(MdW}O^n>HYMw_< zekk_*%s_tyc&U#`&8skn3N|n{op7#5zYQf(fPMyYr839}d7$$HWg5#M&@@S5@+bik zsS^QzW#-5jB2n8PB9&Ciz)W-=a`~daa&ryj@^VL;Wh{h z{`@+EQ7}Vfdd~!*teY$iy`ej8D2U*E;t-5_7p|>9N5m43ViId+(FJLJ6#<yI5tUwq23h}q~x4r~BSpb&}fW5_?_P3vg z^)bI)7Qt8!GX~bqI?h+q$xORZym6T|(HB)T>1j7kmDa}XO)0Dy$ljDCPt~7d>KA<@ zBaZ-SDOjqO>$HD%cyWDTh>+F zaGNvdq#mYxp`g{d&c<6@ z6}o$QT-Dc5rO`@wq!gk|gK59x?vAOhO8Z_hW;1yel~lSqB9)WzJZ+&0gFEYf@=4!I zp0FtFyzaB8S5gngM#)r&sL$6TR1`m{x~xG_w6y+(pKHJ-7bJkNQY07RWWFrDSq_Ub zyLFP3HT9p57YoX@*r%w)LYiw)QOacwB}gvV^Rw1{7c4IYUNG;cP%h%d0W&5nR{T}- z*E)Z#=U5Y(^^;61kWKO8NP0ENXU&^@*1e0mZBTV``duaJ7rJg9ZNb3Zs67*E+2Th0 z;tFEUiyH&v3N^^(fL1ism>+=Qxl5<7fdc*#FWxHP^k&4!r`qN9X^fp4S-jO+_%E#X z=i}rnq4wk}aq@hqJ^4z&wn7cta#$r>RY^5o-0F2rm*pYI1B}X5o8*7!%*N?vtWku%<6@7Ju6Qxz2T$0=#fV?FzNElDT?v6{@U z#jclpElxg-J5HXBlh4G-S5rR1us48H?h89iL8LP^TF9_yfSD&v%f!w`T>CL_;kq)| zTC`EH7!b8syrI-j35!6sm_oJ)WG}s^Qw2*tnh`sTM!NM3kt3~jJ~e(VQi@7&Hh_a7 zzDZhW$_}k#qD0eXB0X3_;g)sudA30LR+~Yx5~bMHrBzJAx#gtX)5D~^M^oX&z37GI zYRwk9n6dY8D+fz8J#zNIPs z7)K&QSh7Ea9nZp!t5g5%iQX;-}eKSAb#_%#V&ms;e9HVto0mPF=n*X~XTX zuK3W{PWT;>H9Yx&+-4vUp&m9g-zm;Af(d)nHt*@85DRdF!@^&wtcBOf3r#NkHJS*9 z2d!JlBLjpov)@L%#zMR}x6u7NTS!o5KT4Tgju#JTgf4u`K-5K^(n%BUN|9TFB~Bg( z-qH8S%J0slZ><#^wQ>FfJ=@P6F0E#z+X$6?QR(~n^!>CGFv&Pr8zNtT5JNgHfA3D} z8IG6YH2B5z{apGEg*=t|e@NvhzmyqLzQ{JJkg2QEkmV^h6NTI$40e5#7d(n~afQXv ze)^5W2qKXb30f8~rY}>F4>wo&5b#)tFw}GJf4cJmi*WLm_RE7WWkhDmDpRJ_#Ww^H?hv+HDcCRRxtXspn-e{Khjwi!45inY2&xEuNMTC(TCN^< z!yqthSyWcYYnB0pN$XGC>e#x3?qto<1t?}TVfDzEv0Yb!{pGtxU9U-&d;I;-NC4pZ5cG);qvC755sU%fU~VIw_WWnzahD_<+WF zofYaHxM>HPDxzVXc5kqd#j4uI64zML&u^Ih@hvk!Vym>?<_YJLx#0=6GICWVMXTcP zlGyN3B87|Y*+2*Ep^?JM7GS_Odx-hR9Dzn_w*WvxPpCVrPp)GA%-i0>| zzYj8IH2=PsDoE{%TAi{xMjRpSiA!W8|Y{zAoh1Hi?6w+LG4Nu<4zNnh%;Y{5hAW<}Uy zz-&?L(66XR1fM#Cb}KF!j=$Cc2|d%IIO7y5P({X zfsn-{fc0*@*y&a-5JnF1P!K=s6MwS|${}v+62FzC0Z(eZhC3IQa~A6XiR`URQGm;s zNF_xPx0rb6u{{$#5r*vX-!y{UjYdXm*^uAdQyM}!n>BFQk{I4`d!pkEdoW2I$_#HW zXAi6YMrwPk^mz}Z(?^(5a`YG;d9SvZaz%$(Uq%A_?Gdamq7Ka6>qBR9ot}iHR!KET z^wyvmAtg`kFQBW^F!{n1Oc#K#{h+<^#!^Wy?rE{7?rD*qO>%JG%56*++VknyO`KbV zk=%0PNcN{hg5odEk{tK)f%;h;IM_UQZtBtIf!RCnKhFlk zliTil{M7u3lg({mq0x6X?wgvQy5sbz`Lk0q^Lyv#&z_ime7?EuW(s`xz`6UH$C_uG zr)HX)Zx!vgo}tGKZ^!L9_nR;-ANI$u^R@+H_yZezy<0)4IwjHW*$c)}U;sw+pU17XhN(2VA zk$B;!in5G1Sy5lNXTd1UfMTU`gca)%+m$OF-1cR#eAt<~w&$g;m;jkCuQxl;N-V}i z%Y6C$ohfof1O)Bt5WEO*J`lY?;F1{e3ueU{Nu)|D!h7;pdMo}ahp7alIHp>M3D z-Es0w7D=!Pe8DIRgd2SYdQ0iAy^QWGol|L`)ilk*=pdGjWW8+{27kH;Mg#$J7XA)4 z$)MHDMb$67WkW@>;3xwzUVK(}xB$$v{FD?E5WAX~eiFiU7Ia3qu*WPp&$^D`SNLc& ziUwMj5Mtsl)`v^p%v=dcM{OrTU8~1NBMO2ZkVRZ^PN#PQ6(K6{@oqWm>@+1*ZcRyT z=Z_?fj7~h$>uaw-mG42wgzB6-JFRG~&`eyhqq&&I)~3t}c)EoF%BPf=+4zKbuM*@ym|w zYI!}f$jVJs!QiQuZ*(c8d@e_W+>|oz7(f$qEGE6Sou{|6wvhCGu;h!sh&1x&BAnD+ z_8HZiWTZe+suy&RNop>~?XAY)(=Y=MU&kzpTLOELJ)U3(U)oW)Y+9pE!%cG~L!RQT zA^em^Wcw-W-KSFT;#cH_e+CB^lDT^ERA3uV6N}6rR$B(G202q>e)z-!fPlxP;Z*3Be2-o z92kRX7fT8U6`@#W8Tj0i0co0Ms0(ehmb=yJ@sxgmFmI z1H_}c9JUM(?@OE7egxc{VXfKFQ)kDMHn(CTGIMczR=JMGM;oQp)jIi~VJ^0rGh=7) zw4T*H7zt}vvm^DrrQs?=jhiq)Hi#UlJ75c=Z}$zii<2wlWfI6PhxQ{yKenR-E83FB zwnyVZT(X8dDfJ10*NXKpKj1snfH zC<LHL9;Xf6H<#eycTrlFMP-sOOIBw7CFI=6 ztWWRO&sd)&dW7vUL-E$Kl>T{LY(o?kJ3M8Cf%K)01>8J{2ynEqqpNMKhlLD6>$yb^ zdBu+&r}s#d%375d2GGX(z#tPV1=W3^0!6(u#gQkQ;>eecdSwZP&(~8XszA{7m{mwI z{v}3b{4~2(H1ZTRUk^z-euHsd&4PFYuiX5Bi}oYzxV=jV7VU}jTF=(nTYa>rX+s;k z0?lp^x4sxl_fG4D38u~z;pzkU3?EA5XZV&Z>dfHfA(V@CxbSg#1t#Bb!fw*nw z6CBTdojw++iCcvTcEK>$^F(aAvF4L|4;xE69*^ZBshFEhJE@(z;Rr_M$y#!y&i!l) z7SDSGq%le{CwN4A5%#;tVFhl9s#dTf4{(x5to{bZii zP>rbNkav|G@gqn_4cyn_5k@Y0m6c@oO)c8qkHyQjLHTF3#mh>yctw^vKEmUA1i15uZreIEop_u&@1M`{G(H;v9Tee{| zwrvXwY1+F5vvySP)+lZ7F|!c!r-`^d#^6CGqnb;SWQooZ@N@+XGq1(k*tYogOTK~! zMZdJBRKk9#VSj0ZZPG4ph}M?|JS@!cIx!{^QTr4h+@CsmqP)BXYBYosR`90XF3(klB_m0HK+;K`-nJqnie z^VAzouK7e{2l#?WU_gQef34vH7OP(}e+j zDMP_t!GL{VqvMzP4oYdC;OvNas$KA5)cXWkG@ovt03_w5wAzI|X@!}KskFno2Z^nQ z5G4{bD3n*FUAU8w0ITNNR5?O@W`#|M8jDFf%D_==4ebkz9&B>SAsgomK)NkC#2w+C z(HQ&}bYlc@8YOCR`-u;s&l1FV*Dw=m8Ik;DzY7OLTa^oBE>6Og{LL2&p#|!1UAo0} zU2-aet(A6OjltEQmV{;9p1p?Xd8LAW?6Es`h_7+fNxeUqxXqwBxfrYoHpgUwN zGb~VbTZ*dN0NFXcT#C6pjk&!OBbW3lNAA8feqSeEE>+M}_Wfz}{!X-9{g6Djx+%e3 zX@a{t3FK1Dy^3MgPKQj~A7HyzX-!*@3CBl?To`4{3mWBJQ(|Fs4X8$|m7fJoD@fB% z70BDxEypc3ErvLVmJ2m-Fxyl8xD8&&&SiAD zBQjCZGmD2c&h)2Y4i`C6(S@8B9%7AJ(hQv?_@uj0ZkusbV9&AaX$$*}Cb(OL@vo^0 zEz?3`zfMiCl3D7LV^BMkYq`{Sm)K+60XaYjc)!>WTrH?GdN@uEBU^mXgw_)<0o$eh z3QgxUu-eD=FHQyHdx3OzbxOL)n@tjYT)IC!;|p^kmit`z1_ft5#Q*Gqm0hJZwbtLd zo9*1XErs73_G6nFKfLbf@?usi_vo(C))n@s45m9u3_j&aA-i%Tn->94JJ35@*NFcr z+5thfyF9J(N|`A_SqUqcjJeIj<}$4-x@g=s$ZTLnp<;YD4lat}7t+b9!TNKNtV5-N zLG@L+$P7e8d(>!3n>MiaF_#jBu`Po+fzJhWrGGUMF&mG4m{nfzIsKn`4w#^|I; zgjL^{?f^zqU(n88+uo_CHJPnAD1Ibk=&eHW1l+tfEELVq%MBwn~Gt41xq8OncNVsNAc=Hv`do(RSMFZC{kP z6-h;ajR|gvwG?36w03X-=F?$=6EhDg?H+YCuN3?3Jw6yU*OfY~x2SXvXPSA!NWGUx zx7-mg&H~XMZQ`MW!YXmH-q*9&BhNwc@fw)q$Q6=Ar-xo=^Rg7%gi6k9_Y{4xCejA) z#W`07@&Q;q)BxXbklS_uFeGP;acB-P{tZZ$64|II6}z2OQS+?loGxwgIa&W6u@{@v zn5uAwr*%JXbuBqG9!Y@1Bcw3+a3&1uju+>{zA~u?R+cnTG!Lpv)|=&(b4TWp-*MJS zE8A%<`WD;GPP1rEu5sppvPr5978{B(D25_Cec!s&EJU~JdX3Hs_Lh(N-5Y!UV0_!? zmV3{hK6YZRx#y0#6U|feTHRjTNxARDIgSFKzni7+_o;|RC#Esp@C=PK{|$wx7B2E= zSXv5?qU6y+luX^-{N()UQ?}r4X%08f&7a%SxcBktxf3%VZJysVI=X#)RR6{&?X}x| zqoX@Vw_89OKe2sc$N1QeF{QL{x%fLav2$$p*!Eq!W_HeuPLJ>2HM?^=Yx&!!$Hzuz zW@nCVpWNA;*s*J7bYgth_VHu8jvbpC9osp+V|?P+&h67Xnmfn0k8a;RyK8E4bY^D9 z#Mth!$=wqZJGM{Gj8DzZOilsUr(U znAtHsHQgMYoE`Y3@;$=Qh=YQ$_4u*Qy!ZQnI{ zZ0C-h&6)9?+h>mLp4mPzzI*o=P&Rk&Y)(&)O@P&H- zcOM&@7@wLR8=u-eJ~lJHn;c^k6Em}8v*X8Rrl&@aO-(}x^z!t0lm4IBy<>WGY+`0= z8rQDbsU2fuyS5(#p*wfYj_z*G&Q6Vwj!#e0Cm?jkvC*B|r<%LQW=E&DH^;|!Oiu3_ zpP3mOo1NjJ>pKe3Z+A$YQ}k1fGpElyJ~wr?as0%i$G392{@CfWC#Tri(fEjNyE+!O zjx-3ju@Ob93(;WKSy5CeMCDLs6m2d-g^N6I&;zW~a7}Pi^1b+&#WyYRAqUQN&Gut0uOM zZX4e=#+aIjxCDru)KR$*={7NQl+Ce0;fISi{QlXgGk2dpb?2vMnrC#z|M2m%r$2el zF0G0F*%v=`NQsrTPSDr?s6YDpR6jS8?YsZ!J=?!<;h+DP_x#0QzyCk}Z~uJNvrJ2( zncq5kxOwu-QMK^s@3V=9797o*zscpw&MAL+k> zHI7SGTip#b;*Ah6%fgjzKYtHC(;vOY)grw=!ri5>@5LEVV-VV*~MZndv5oi*6c z@~I#}JVSdLly<-2t3jMvqMANQDQBZk5bK`@YIN#SyCzY916=xE(J4Mmad(u2Eu#ZOr z!0unfnuS*MG3*Qj%keK5ZZmv$=UIyNtKoc77U5_w{i9EBpM<*gZK?(%tGn0KSD_5` c@vr~=Bf>zd9}0@1(XoF-ZTfXn-ZlpQKXsO Date: Sun, 5 May 2013 17:03:12 +0200 Subject: [PATCH 10/23] added a simple android test application (using xamarin) --- .gitignore | 6 + ClientServerLib/AndroidRemote.sln | 20 ++ .../AndroidRemote/AndroidRemote.csproj | 92 +++++++++ ClientServerLib/AndroidRemote/MainActivity.cs | 143 +++++++++++++ .../Newtonsoft.Json.MonoDroid.dll | Bin 0 -> 325120 bytes .../AndroidRemote/Properties/AssemblyInfo.cs | 28 +++ .../Resources/AboutResources.txt | 44 ++++ .../Resources/Resource.designer.cs | 194 ++++++++++++++++++ .../AndroidRemote/Resources/drawable/Icon.png | Bin 0 -> 2574 bytes .../AndroidRemote/Resources/layout/Main.axml | 52 +++++ .../Resources/values/Strings.xml | 18 ++ 11 files changed, 597 insertions(+) create mode 100644 ClientServerLib/AndroidRemote.sln create mode 100644 ClientServerLib/AndroidRemote/AndroidRemote.csproj create mode 100644 ClientServerLib/AndroidRemote/MainActivity.cs create mode 100644 ClientServerLib/AndroidRemote/Newtonsoft.Json.MonoDroid.dll create mode 100644 ClientServerLib/AndroidRemote/Properties/AssemblyInfo.cs create mode 100644 ClientServerLib/AndroidRemote/Resources/AboutResources.txt create mode 100644 ClientServerLib/AndroidRemote/Resources/Resource.designer.cs create mode 100644 ClientServerLib/AndroidRemote/Resources/drawable/Icon.png create mode 100644 ClientServerLib/AndroidRemote/Resources/layout/Main.axml create mode 100644 ClientServerLib/AndroidRemote/Resources/values/Strings.xml diff --git a/.gitignore b/.gitignore index 1875ccf8..b0ac1e0d 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,9 @@ Output/Songs /ClientServerLib/ClientServerLib/bin/ClientServerLib.dll ClientTest.exe *.vshost.* +*.userprefs +AndroidRemote.dll.mdb +/ClientServerLib/AndroidRemote/bin/Newtonsoft.Json.MonoDroid.dll +/ClientServerLib/ClientServerLib/bin/Newtonsoft.Json.dll +/ClientServerLib/AndroidRemote/bin/AndroidRemote.dll +/ClientServerLib/ClientTest/bin/Debug/Newtonsoft.Json.dll diff --git a/ClientServerLib/AndroidRemote.sln b/ClientServerLib/AndroidRemote.sln new file mode 100644 index 00000000..eaf69974 --- /dev/null +++ b/ClientServerLib/AndroidRemote.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AndroidRemote", "AndroidRemote\AndroidRemote.csproj", "{D97EF6F3-7C3F-439F-B022-647948E206D2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D97EF6F3-7C3F-439F-B022-647948E206D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D97EF6F3-7C3F-439F-B022-647948E206D2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D97EF6F3-7C3F-439F-B022-647948E206D2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D97EF6F3-7C3F-439F-B022-647948E206D2}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = AndroidRemote.csproj + EndGlobalSection +EndGlobal diff --git a/ClientServerLib/AndroidRemote/AndroidRemote.csproj b/ClientServerLib/AndroidRemote/AndroidRemote.csproj new file mode 100644 index 00000000..e7b5397e --- /dev/null +++ b/ClientServerLib/AndroidRemote/AndroidRemote.csproj @@ -0,0 +1,92 @@ + + + + Debug + AnyCPU + 10.0.0 + 2.0 + {D97EF6F3-7C3F-439F-B022-647948E206D2} + {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Library + AndroidRemote + Resources + Assets + Resource + True + Resources\Resource.designer.cs + AndroidRemote + + + true + full + false + bin\ + DEBUG;MONO + prompt + 4 + false + None + + + none + true + bin\ + prompt + 4 + false + false + SdkOnly + MONO + + + + + + + + Newtonsoft.Json.MonoDroid.dll + + + + + + + + ClientServerLib\CClient.cs + + + ClientServerLib\CConnection.cs + + + ClientServerLib\Structs.cs + + + ClientServerLib\DeffieHellman\BigInt.cs + + + ClientServerLib\DeffieHellman\DeffieHellman.cs + + + ClientServerLib\DeffieHellman\StrongNumberProvider.cs + + + ClientServerLib\Vocaluxe\CCommands.cs + + + ClientServerLib\Vocaluxe\VocaluxeStructs.cs + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ClientServerLib/AndroidRemote/MainActivity.cs b/ClientServerLib/AndroidRemote/MainActivity.cs new file mode 100644 index 00000000..f2a1c89f --- /dev/null +++ b/ClientServerLib/AndroidRemote/MainActivity.cs @@ -0,0 +1,143 @@ +using System; + +using Android.App; +using Android.Content; +using Android.Runtime; +using Android.Views; +using Android.Widget; +using Android.OS; + +using ClientServerLib; +using Vocaluxe.Base.Server; + +namespace AndroidRemote +{ + [Activity (Label = "Vocaluxe Remote", MainLauncher = true)] + public class Activity1 : Activity + { + CClient client = new CClient(); + Button bConnect; + Button bLogin; + Button bUp; + Button bDown; + bool loggedIn; + + protected override void OnCreate (Bundle bundle) + { + base.OnCreate (bundle); + + loggedIn = false; + + // Set our view from the "main" layout resource + SetContentView (Resource.Layout.Main); + + bConnect = FindViewById