Click here to Skip to main content
15,915,093 members
Articles / Programming Languages / C#

Simple Client-server Interactions using C#

Rate me:
Please Sign up or sign in to vote.
4.63/5 (53 votes)
10 Apr 2012CPOL7 min read 959.7K   35.1K   183   171
Introduces a couple of cover classes to provide event-driven interaction with TCP/IP sockets and simple message protocols

Introduction

Sockets are an important part of the modern programmer's armory. To read this article, you are making extensive use of sockets – the article itself, and each image, come down a socket to your machine, and perhaps two, if you're reading this at work, behind a router. Communicating with other machines lets you spread computationally expensive tasks, share data and so on. Yet the Framework doesn't provide as good a toolkit as it does for, for example, Windows Forms (unless you are doing something 'standard' like making HTTP requests). If someone sends me some data, or connects to my server, I want an event to be thrown, just like if someone presses a button on my form.

There are also problems with networks, and specifically with TCP. Connections can be lost at any time, so an OnDisconnect event would be nice so that we can take appropriate action if this happens. (The appropriate action varies from a message box to cleaning up information stored on a server about the client who disappeared.) What I send as one message can arrive as several, or vice versa; while TCP guarantees that data arrives in the right order (if it arrives at all), it has no concept of individual messages.

I have written a small assembly which provides event-driven client-server communication over TCP (using the tools provided in System.Net.Sockets). It also specifies a simple message-based protocol so that you can keep your messages together, although you don't have to use it.

Being a Simple Client

A client is the term for a user who connects to a server, typically to request data. Your browser acts as a client while it downloads material from the Internet. For most problems, there is one server and many clients, and if you're writing an application which talks to existing servers, all you need to use is a client socket.

Creating a client socket with ordinary .NET functions is slightly messy, so I provide a cover. And once you have the socket, you need to create an instance of ClientInfo to get event-driven access to the data.

C#
// At the top of the file, you will always need
using System.Net.Sockets;
using RedCorona.Net;

class SimpleClient{
  ClientInfo client;
  void Start(){
    Socket sock = Sockets.CreateTCPSocket("www.myserver.com", 2345);
    client = new ClientInfo(sock, false); // Don't start receiving yet
    client.OnReadBytes += new ConnectionReadBytes(ReadData);
    client.BeginReceive();
  }

  void ReadData(ClientInfo ci, byte[] data, int len){
    Console.WriteLine("Received "+len+" bytes: "+
       System.Text.Encoding.UTF8.GetString(data, 0, len));
  }
}

Et voilà, you can receive data! (Assuming you have a server that sends you some, of course.) To send text data, use client.Send("text"). ClientInfo exposes the socket it is using, so to send binary data, use client.Socket.Send().

Messaged Communication

Receiving data with OnReadBytes doesn't guarantee your messages arrive in one piece, though. ClientInfo provides two ways to keep messages together, one suitable for simplistic text messaging and one suitable for arbitrary message-based communication.

Text Messages

Often, being able to pass text with a suitable end marker, often a new line, is all we need in an application. Similar things can then be done with data received like this as with command strings. To do this, assign a handler to the OnRead event:

C#
class TextClient{
  ClientInfo client;
  void Start(){
    Socket sock = Sockets.CreateTCPSocket("www.myserver.com", 2345);
    client = new ClientInfo(sock, false); // Don't start receiving yet
    client.OnRead += new ConnectionRead(ReadData);
    client.Delimiter = '\n';  // this is the default, shown for illustration
    client.BeginReceive();
  }

  void ReadData(ClientInfo ci, String text){
    Console.WriteLine("Received text message: "+text);
  }
}

Now, any data received will be interpreted as text (UTF8 text, to be precise), and ReadData() will be called whenever a newline is found. Note that if the data is not text (and is not valid UTF8), invalid characters will be replaced with '?' – so don't use this method unless you know you are always handling text.

Binary Messages

For more advanced applications, you may want to pass a byte stream (which can represent anything), but still keep the message together. This is typically done by sending the length first, and ClientInfo does the same thing. To use this protocol, you need to put the client into a messaged mode and use OnReadMessage():

C#
class MessageClient{
  ClientInfo client;
  void Start(){
    Socket sock = Sockets.CreateTCPSocket("www.myserver.com", 2345);
    client = new ClientInfo(sock, false); // Don't start receiving yet
    client.MessageMode = MessageMode.Length;
    client.OnReadMessage += new ConnectionRead(ReadData);
    client.BeginReceive();
  }

  void ReadData(ClientInfo ci, uint code, byte[] bytes, int len){
    Console.WriteLine("Received "+len+" bytes: "+
       System.Text.Encoding.UTF8.GetString(bytes, 0, len));
  }
}

In the current version, a 4-byte length parameter is sent before the data (big-endian); for example, a message containing the word "Bob" would have the byte form 00 00 00 03 42(B) 6F(o) 62(b). Obviously, the other end of the connection needs to be using the same protocol! You can send messages, in the same format, using client.SendMessage(code, bytes).

By now, you're probably wondering what that uint code is all about. As well as MessageType.Length, there is also a MessageType.CodeAndLength which lets you specify a 4-byte code to be sent with each message. (It is sent before the length.) The event handler in this type of an application will typically be something like:

C#
void ReadData(ClientInfo ci, uint code, byte[] bytes, int len){
  switch(code){
    case 0:
      Console.WriteLine("A message: "+
        System.Text.Encoding.UTF8.GetString(bytes, 0, len));
      break;
    case 1:
      Console.WriteLine("An error: "+
        System.Text.Encoding.UTF8.GetString(bytes, 0, len));
      break;
   // ... etc
  }
}

If I were to send the message "Bob" with the tag 0x12345678, its byte stream would be 12 34 56 78 00 00 00 03 42 6F 62.

Being a Server

As well as receiving and sending information (just like a client), a server has to keep track of who is connected to it. It is also useful to be able to broadcast messages, that is send them to every client currently connected (for example, a message indicating the server is about to be taken down for maintenance).

Below is a simple server class which simply 'bounces' messages back where they came from, unless they start with '!' in which case it broadcasts them.

C#
class SimpleServer{
  Server server;
  ClientInfo client;
  void Start(){
    server = new Server(2345, new ClientEvent(ClientConnect));
  }
  
  bool ClientConnect(Server serv, ClientInfo new_client){
    new_client.Delimiter = '\n';
    new_client.OnRead += new ConnectionRead(ReadData);
    return true; // allow this connection
  }

  void ReadData(ClientInfo ci, String text){
    Console.WriteLine("Received from "+ci.ID+": "+text);
    if(text[0] == '!')
     server.Broadcast(Encoding.UTF8.GetBytes(text));
    else ci.Send(text);
  }
}

As you can see, the Connect handler is passed a ClientInfo, which you can treat in the same way as a client (assign the appropriate OnReadXxx handler and MessageType). You can also reject a connection by returning false from Connect. In addition to Broadcast(), there is a BroadcastMessage() if you are using messaged communication.

ClientInfos are assigned an ID when they are created, controlled by the static NextID property. They will be unique within a running application unless you reset NextID at any time after creating one. A server application will often want to keep track of a client's transactions. You can do this in two ways:

  1. There is a Data property of ClientInfo, to which you can assign any data you like. The data is maintained until the ClientInfo is disposed of (guaranteed to be after the connection dies).
  2. You can use the value of client.ID as the key in a Hashtable or similar. If you do that, you should supply a Disconnect handler:
    C#
    void ConnectionClosed(ClientInfo ci){
      myHashtable.Remove(ci.ID);
    }

    ... to make sure the data is cleaned up.

Encryption

This library includes the ability to protect a socket, using the encryption algorithm I wrote about here. This can be turned on by passing a value for encryptionType in the ClientInfo constructor or setting the EncryptionType property before calling BeginReceive, and setting the DefaultEncryptionType property on the server:

C#
public void EncryptionExamples() {
        // Client, Method 1: call the full constructor
        ClientInfo encrypted1 = new ClientInfo(socket1, null, myReadBytesHandler,
                ClientDirection.Both, true, EncryptionType.ServerKey);
        // Client, Method 2: delay receiving and set EncryptionType
        ClientInfo encrypted2 = new ClientInfo(socket2, false);
        encrypted2.EncryptionType = EncryptionType.ServerRSAClientKey;
        encrypted2.OnReadBytes = myReadBytesHandler;
        encrypted2.BeginReceive();
        // Server: set DefaultEncryptionType
        server = new Server(2345, new ClientConnect(ClientConnect));
        server.DefaultEncryptionType = EncryptionType.ServerRSAClientKey;
}

There are three encryption modes supported. The first, None, performs no encryption, and is the default. EncryptionType.ServerKey means that the server sends a symmetric key for the connection when the client first connects; because the key is sent unencrypted, this is not very secure, but it does mean that the communication is not in plain text. The most secure method is ServerRSAClientKey: the server will send an RSA public key upon connection, and the client will generate a symmetric key and encrypt it with the RSA key before sending it to the server. This means the key is never visible to a third party and the connection is quite secure; to access the message you would need to break the encryption algorithm.

If you choose to use encrypted sockets, very little is different in your application code. However, in your server, you should respond to the ClientReady event, not ClientConnect, in most cases. ClientReady is called when key exchange is complete and a client is ready to send and receive data; attempting to send data to a client before this event will result in an exception. Similarly, if you want to send data through an encrypted client socket, you should respond to the OnReady event or check the EncryptionReady property before sending data.

History

  • December 2005: Initial version posted
  • April 2008: Updated with version 1.4, supporting encryption, multi-character delimiters and sending byte arrays
  • November 2011: Updated with version 1.6, including the ability to synchronise events to a UI control

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United Kingdom United Kingdom
I'm a recent graduate (MSci) from the University of Cambridge, no longer studying Geology. Programming is a hobby so I get to write all the cool things and not all the boring things Smile | :) . However I now have a job in which I have to do a bit of work with a computer too.

Comments and Discussions

 
GeneralRe: Encrypted sockets issues Pin
James Winterburn25-Sep-09 2:42
James Winterburn25-Sep-09 2:42 
GeneralRe: Encrypted sockets issues Pin
BobJanova25-Sep-09 3:23
BobJanova25-Sep-09 3:23 
Questionusing the code Pin
jnantes18-Aug-09 4:17
jnantes18-Aug-09 4:17 
AnswerRe: using the code [modified] Pin
BobJanova18-Aug-09 5:01
BobJanova18-Aug-09 5:01 
GeneralNetworkStream Flush Problem Pin
Abdullah_Hunzai30-Mar-09 1:28
Abdullah_Hunzai30-Mar-09 1:28 
Generalbehavior under load Pin
mitchmcc27-Mar-09 7:52
mitchmcc27-Mar-09 7:52 
GeneralRe: behavior under load Pin
BobJanova27-Mar-09 8:28
BobJanova27-Mar-09 8:28 
GeneralRe: behavior under load Pin
mitchmcc27-Mar-09 8:49
mitchmcc27-Mar-09 8:49 
I will have to see if I can send you any code. I am working on a project for a company, and obviously they own the rights to it. Still, I will check with my boss and explain the problem, and to ask if I can send something to you.

I can tell you that I created a simple instant message application that uses your encryption feature to send messages with a code. The messages are just text, although they are encrypted on the wire.

My most recent crash just occurred, and was in the sockets.cs function

public ClientInfo this[int id]

which is in the Server object.

The error I received was InvalidOperationException was unhandled; Collection was modified; enumeration operation may not execute.

The context was that of my 30 users (your ClientInfo) I had active at the time, one client connection had been closed, so I iterate over my list of clients and call SendMessage to notify all of the other IM users that one went away.

The actual error line pointed to

foreach (ClientInfo ci in Clients)

I have made changes to sockets.cs. I can include it below just in case you want to see what I have done. Some of the changes are cosmetic (sorry!), but you will see it catch some specific exceptions and generate a simple Application exception that my app can catch. I think 100% of these are really just SocketExceptions when a client is closing and someone is trying to send, etc.

Here is the modified code:

// Client-server helpers for TCP/IP
// ClientInfo: wrapper for a socket which throws
// Receive events, and allows for messaged communication (using
// a specified character as end-of-message)
// Server: simple TCP server that throws Connect events
// ByteBuilder: utility class to manage byte arrays built up
// in multiple transactions

// (C) Richard Smith 2005-8
// bobjanova@gmail.com
// You can use this for free and give it to people as much as you like
// as long as you leave a credit to me Smile | :) .

// Code to connect to a SOCKS proxy modified from
// http://www.thecodeproject.com/csharp/ZaSocks5Proxy.asp

// Define this symbol to include console output in various places

#define DEBUG

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Collections;
using System.Net.Sockets;
using System.Security.Cryptography;
using Netsalon.Cryptography;

[assembly: System.Reflection.AssemblyVersion("1.4.2008.0330")]

namespace Netsalon.Sockets
{



public delegate void ConnectionRead(ClientInfo ci, String text);
public delegate void ConnectionClosed(ClientInfo ci);
public delegate void ConnectionReadBytes(ClientInfo ci, byte[] bytes, int len);
public delegate void ConnectionReadMessage(ClientInfo ci, uint code, byte[] bytes, int len);
public delegate void ConnectionReadPartialMessage(ClientInfo ci, uint code, byte[] bytes, int start, int read, int sofar, int totallength);
public delegate void ConnectionNotify(ClientInfo ci);

public enum ClientDirection { In, Out, Left, Right, Both };
public enum MessageType { Unmessaged, EndMarker, Length, CodeAndLength };
// ServerDES: The server sends an encryption key on connect
// ServerRSAClientDES: The server sends an RSA public key, the client sends back a key
public enum EncryptionType { None, ServerKey, ServerRSAClientKey };


public class EncryptionUtils
{
static RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
public static byte[] GetRandomBytes(int length, bool addByte)
{
if (addByte && (length > 255))
throw new ArgumentException("Length must be 1 byte <256");
byte[] random = new byte[length + (addByte ? 1 : 0)];
rng.GetBytes(random);
if (addByte)
random[0] = (byte)length;

return random;
}
}

// OnReadBytes: catch the raw bytes as they arrive
// OnRead: for text ended with a marker character
// OnReadMessage: for binary info on a messaged client

//-----------------------------------------------------------------------
//
// c l a s s C l i e n t I n f o
//
//-----------------------------------------------------------------------

public class ClientInfo
{
internal Server server = null;
private Socket sock;
private String buffer;
public event ConnectionRead OnRead;
public event ConnectionClosed OnClose;
public event ConnectionReadBytes OnReadBytes;
public event ConnectionReadMessage OnReadMessage;
public event ConnectionReadPartialMessage OnPartialMessage;
public event ConnectionNotify OnReady;
public MessageType MessageType;
private ClientDirection dir;
int id;
bool alreadyclosed = false;
public static int NextID = 100;
//private ClientThread t;
public object Data = null;

// Encryption info
EncryptionType encType;
int encRead = 0, encStage, encExpected;
internal bool encComplete;
internal byte[] encKey;
internal RSAParameters encParams;

public EncryptionType EncryptionType
{
get { return encType; }
set
{
if (encStage != 0) throw new ArgumentException("Key exchange has already begun");
encType = value;
encComplete = encType == EncryptionType.None;
encExpected = -1;
}
}
public bool EncryptionReady { get { return encComplete; } }
internal ICryptoTransform encryptor, decryptor;
public ICryptoTransform Encryptor { get { return encryptor; } }
public ICryptoTransform Decryptor { get { return decryptor; } }

private string delim;
public const int BUFSIZE = 1024;
byte[] buf = new byte[BUFSIZE];
ByteBuilder bytes = new ByteBuilder(10);

byte[] msgheader = new byte[8];
byte headerread = 0;
bool wantingChecksum = true;

public string Delimiter
{
get { return delim; }
set { delim = value; }
}

public ClientDirection Direction { get { return dir; } }
public Socket Socket { get { return sock; } }
public Server Server { get { return server; } }
public int ID { get { return id; } }

public bool Closed
{
get { return !sock.Connected; }
}

public ClientInfo(Socket cl, bool StartNow) : this(cl, null, null, ClientDirection.Both, StartNow, EncryptionType.None) { }
//public ClientInfo(Socket cl, ConnectionRead read, ConnectionReadBytes readevt, ClientDirection d) : this(cl, read, readevt, d, true, EncryptionType.None) {}

public ClientInfo(Socket cl, ConnectionRead read, ConnectionReadBytes readevt, ClientDirection d, bool StartNow) : this(cl, read, readevt, d, StartNow, EncryptionType.None) { }

public ClientInfo(Socket cl, ConnectionRead read, ConnectionReadBytes readevt, ClientDirection d, bool StartNow, EncryptionType encryptionType)
{
sock = cl; buffer = ""; OnReadBytes = readevt; encType = encryptionType;
encStage = 0; encComplete = encType == EncryptionType.None;
OnRead = read;
MessageType = MessageType.EndMarker;
dir = d; delim = "\n";
id = NextID; // Assign each client an application-unique ID
unchecked { NextID++; }

//t = new ClientThread(this);
if (StartNow)
BeginReceive();
}

public void BeginReceive()
{
#if DEBUG
Console.WriteLine("BeginReceive: enter");
#endif
// t.t.Start();
sock.BeginReceive(buf, 0, BUFSIZE, 0, new AsyncCallback(ReadCallback), this);
}

public String Send(String text)
{
byte[] ba = Encoding.UTF8.GetBytes(text);
String s = "";
for (int i = 0; i < ba.Length; i++)
s += ba[i] + " ";
Send(ba);
return s;
}

public void SendMessage(uint code, byte[] bytes) { SendMessage(code, bytes, 0, bytes.Length); }
public void SendMessage(uint code, byte[] bytes, byte paramType) { SendMessage(code, bytes, paramType, bytes.Length); }
public void SendMessage(uint code, byte[] bytes, byte paramType, int len)
{
if (paramType > 0)
{
ByteBuilder b = new ByteBuilder(3);
b.AddParameter(bytes, paramType);
bytes = b.Read(0, b.Length);
len = bytes.Length;
}

lock (sock)
{
byte checksum = 0; byte[] ba;
switch (MessageType)
{
case MessageType.CodeAndLength:
Send(ba = UintToBytes(code));
for (int i = 0; i < 4; i++) checksum += ba[i];
Send(ba = IntToBytes(len));
for (int i = 0; i < 4; i++) checksum += ba[i];
if (encType != EncryptionType.None) Send(new byte[] { checksum });
break;
case MessageType.Length:
Send(ba = IntToBytes(len));
for (int i = 0; i < 4; i++) checksum += ba[i];
if (encType != EncryptionType.None) Send(new byte[] { checksum });
break;
}
Send(bytes, len);

if (encType != EncryptionType.None)
{
checksum = 0;
for (int i = 0; i < len; i++) checksum += bytes[i];
Send(new byte[] { checksum });
}
}

}
public void Send(byte[] bytes) { Send(bytes, bytes.Length); }
public void Send(byte[] bytes, int len)
{
if (!encComplete) throw new IOException("Key exchange is not yet completed");
if (encType != EncryptionType.None)
{
byte[] outbytes = new byte[len];
Encryptor.TransformBlock(bytes, 0, len, outbytes, 0);
bytes = outbytes;
//Console.Write("Encryptor IV: "); LogBytes(encryptor.Key, encryptor.IV.length);
}
#if DEBUG
Console.Write(ID + " Sent: "); LogBytes(bytes, len);
#endif
try
{
sock.Send(bytes, len, SocketFlags.None);
}
catch (SocketException e)
{
string errMsg = "ERROR: got Socket Exception errorCode " + e.ErrorCode.ToString() + " trying to send on socket";
Console.WriteLine(errMsg);
Console.WriteLine(e.StackTrace);

throw new ApplicationException(errMsg);
}
}

public bool MessageWaiting()
{
FillBuffer(sock);
return buffer.IndexOf(delim) >= 0;
}

public String Read()
{
//FillBuffer(sock);
int p = buffer.IndexOf(delim);
if (p >= 0)
{
String res = buffer.Substring(0, p);
buffer = buffer.Substring(p + delim.Length);
return res;
}
else return "";
}

private void FillBuffer(Socket sock)
{
byte[] buf = new byte[256];
int read;

while (sock.Available != 0)
{
read = sock.Receive(buf);
if (OnReadBytes != null) OnReadBytes(this, buf, read);
buffer += Encoding.UTF8.GetString(buf, 0, read);
}
}

void ReadCallback(IAsyncResult ar)
{
try
{
int read = sock.EndReceive(ar);
#if DEBUG
Console.WriteLine("Socket " + ID + " read " + read + " bytes");
#endif
if (read > 0)
{
DoRead(buf, read);
BeginReceive();
}
else
{
#if DEBUG
Console.WriteLine(ID + " zero byte read closure");
#endif
Close();
}
}
catch (SocketException e)
{
#if DEBUG
Console.WriteLine(ID + " socket exception closure: " + e);
#endif
Close();
}
catch (ObjectDisposedException)
{
#if DEBUG
Console.WriteLine(ID + " disposed exception closure");
#endif
Close();
}
}



internal void DoRead(byte[] buf, int read)
{
if (read > 0)
{
if (OnRead != null)
{ // Simple text mode
buffer += Encoding.UTF8.GetString(buf, 0, read);

while (buffer.IndexOf(delim) >= 0)
OnRead(this, Read());
}
}
ReadInternal(buf, read, false);
}

public static void LogBytes(byte[] buf, int len)
{
byte[] ba = new byte[len];
Array.Copy(buf, ba, len);
Console.WriteLine(ByteBuilder.FormatParameter(new Parameter(ba, ParameterType.Byte)));
}



void ReadInternal(byte[] buf, int read, bool alreadyEncrypted)
{
if ((!alreadyEncrypted) && (encType != EncryptionType.None))
{
if (encComplete)
{
#if DEBUG
Console.Write(ID + " Received: "); LogBytes(buf, read);
#endif
buf = decryptor.TransformFinalBlock(buf, 0, read);
#if DEBUG
Console.Write(ID + " Decrypted: "); LogBytes(buf, read);
#endif
}
else
{
// Client side key exchange
int ofs = 0;
if (encExpected < 0)
{
encStage++;
ofs++; read--; encExpected = buf[0]; // length of key to come
encKey = new byte[encExpected];
encRead = 0;
}
if (read >= encExpected)
{
Array.Copy(buf, ofs, encKey, encRead, encExpected);
int togo = read - encExpected;
encExpected = -1;
#if DEBUG
Console.WriteLine(ID + " Read encryption key: " + ByteBuilder.FormatParameter(new Parameter(encKey, ParameterType.Byte)));
#endif
if (server == null) ClientEncryptionTransferComplete();
else ServerEncryptionTransferComplete();
if (togo > 0)
{
byte[] newbuf = new byte[togo];
Array.Copy(buf, read + ofs - togo, newbuf, 0, togo);
ReadInternal(newbuf, togo, false);
}
}
else
{
Array.Copy(buf, ofs, encKey, encRead, read);
encExpected -= read; encRead += read;
}
return;
}
}

if ((!alreadyEncrypted) && (OnReadBytes != null)) OnReadBytes(this, buf, read);

if ((OnReadMessage != null) && (MessageType != MessageType.Unmessaged))
{
// Messaged mode
int copied;
uint code = 0;
switch (MessageType)
{
case MessageType.CodeAndLength:
case MessageType.Length:
int length;
if (MessageType == MessageType.Length)
{
copied = FillHeader(ref buf, 4, read);
if (headerread < 4) break;
length = GetInt(msgheader, 0, 4);
}
else
{
copied = FillHeader(ref buf, 8, read);
if (headerread < 8) break;
code = (uint)GetInt(msgheader, 0, 4);
length = GetInt(msgheader, 4, 4);
}
if (read == copied)
break;

// If encryption is on, the next byte is a checksum of the header
int ofs = 0;

if (wantingChecksum && (encType != EncryptionType.None))
{
byte checksum = buf[0];
ofs++;
wantingChecksum = false;
byte headersum = 0;

for (int i = 0; i < 8; i++)
headersum += msgheader[i];

if (checksum != headersum)
{
Close();
throw new IOException("Client ID " + ID + " header checksum failed! (was " + checksum + ", calculated " + headersum + ")");
}
}

bytes.Add(buf, ofs, read - ofs - copied);

if (encType != EncryptionType.None) length++; // checksum byte

// Now we know we are reading into the body of the message
#if DEBUG
Console.WriteLine(ID + " Added " + (read - ofs - copied) + " bytes, have " + bytes.Length + " of " + length);
#endif
if (OnPartialMessage != null) OnPartialMessage(this, code, buf, ofs, read - ofs - copied, bytes.Length, length);

if (bytes.Length >= length)
{
// A message was received!
headerread = 0;
wantingChecksum = true;
byte[] msg = bytes.Read(0, length);

if (encType != EncryptionType.None)
{
byte checksum = msg[length - 1], msgsum = 0;

for (int i = 0; i < length - 1; i++)
msgsum += msg[i];

if (checksum != msgsum)
{
Close();
throw new IOException("Content checksum failed! (was " + checksum + ", calculated " + msgsum + ")");
}

OnReadMessage(this, code, msg, length - 1);
}
else
OnReadMessage(this, code, msg, length);

// Don't forget to put the rest through the mill

int togo = bytes.Length - length;

if (togo > 0)
{
byte[] whatsleft = bytes.Read(length, togo);
bytes.Clear();
ReadInternal(whatsleft, whatsleft.Length, true);
}
else bytes.Clear();
}

//if(OnStatus != null) OnStatus(this, bytes.Length, length);
break;
}
}
}

int FillHeader(ref byte[] buf, int to, int read)
{
int copied = 0;
if (headerread < to)
{
// First copy the header into the header variable.
for (int i = 0; (i < read) && (headerread < to); i++, headerread++, copied++)
{
msgheader[headerread] = buf[i];
}
}
if (copied > 0)
{
// Take the header bytes off the 'message' section
byte[] newbuf = new byte[read - copied];
for (int i = 0; i < newbuf.Length; i++)
newbuf[i] = buf[i + copied];
buf = newbuf;
}
return copied;
}

internal ICryptoTransform MakeEncryptor() { return MakeCrypto(true); }
internal ICryptoTransform MakeDecryptor() { return MakeCrypto(false); }

internal ICryptoTransform MakeCrypto(bool encrypt)
{
if (encrypt) return new SimpleEncryptor(encKey);
else return new SimpleDecryptor(encKey);
}

void ServerEncryptionTransferComplete()
{
switch (encType)
{
case EncryptionType.None:
throw new ArgumentException("Should not have key exchange for unencrypted connection!");
case EncryptionType.ServerKey:
throw new ArgumentException("Should not have server-side key exchange for server keyed connection!");
case EncryptionType.ServerRSAClientKey:
// Symmetric key is in RSA-encoded encKey
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(encParams);
encKey = rsa.Decrypt(encKey, false);
#if DEBUG
Console.WriteLine("Symmetric key is: "); LogBytes(encKey, encKey.Length);
#endif
MakeEncoders();
server.KeyExchangeComplete(this);
break;
}
}

void ClientEncryptionTransferComplete()
{
// A part of the key exchange process has been completed, and the key is
// in encKey
switch (encType)
{
case EncryptionType.None:
throw new ArgumentException("Should not have key exchange for unencrypted connection!");
case EncryptionType.ServerKey:
// key for transfer is now in encKey, so all is good
MakeEncoders();
break;
case EncryptionType.ServerRSAClientKey:
// Stage 1: modulus; Stage 2: exponent
// When the exponent arrives, create a random DES key
// and send it
switch (encStage)
{
case 1: encParams.Modulus = encKey; break;
case 2:
encParams.Exponent = encKey;
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(encParams);
encKey = EncryptionUtils.GetRandomBytes(24, false);
byte[] send = GetLengthEncodedVector(rsa.Encrypt(encKey, false));
sock.Send(send);
MakeEncoders();
break;
}
break;
}
}

internal void MakeEncoders()
{
encryptor = MakeEncryptor();
decryptor = MakeDecryptor();
#if ORIG
if (OnReady != null)
OnReady(this);
encComplete = true;
#else
// MJM: the doc says that when OnReady is called, the app can start sending,
// but if encComplete is not set to true, it generates an exception. This looks
// like a bug to me, so I am trying this. It doesn't look like it will hurt anything.

encComplete = true;

if (OnReady != null)
OnReady(this);
#endif
}

public static byte[] GetLengthEncodedVector(byte[] from)
{
int l = from.Length;
if (l > 255) throw new ArgumentException("Cannot length encode more than 255");
byte[] to = new byte[l + 1];
to[0] = (byte)l;
Array.Copy(from, 0, to, 1, l);
return to;
}

public static int GetInt(byte[] ba, int from, int len)
{
int r = 0;
for (int i = 0; i < len; i++)
r += ba[from + i] << ((len - i - 1) * 8);
return r;
}

public static int[] GetIntArray(byte[] ba) { return GetIntArray(ba, 0, ba.Length); }
public static int[] GetIntArray(byte[] ba, int from, int len)
{
int[] res = new int[len / 4];
for (int i = 0; i < res.Length; i++)
{
res[i] = GetInt(ba, from + (i * 4), 4);
}
return res;
}

public static uint[] GetUintArray(byte[] ba)
{
uint[] res = new uint[ba.Length / 4];
for (int i = 0; i < res.Length; i++)
{
res[i] = (uint)GetInt(ba, i * 4, 4);
}
return res;
}

public static byte[] IntToBytes(int val) { return UintToBytes((uint)val); }
public static byte[] UintToBytes(uint val)
{
byte[] res = new byte[4];
for (int i = 3; i >= 0; i--)
{
res[i] = (byte)val; val >>= 8;
}
return res;
}

public static byte[] IntArrayToBytes(int[] val)
{
byte[] res = new byte[val.Length * 4];
for (int i = 0; i < val.Length; i++)
{
byte[] vb = IntToBytes(val[i]);
res[(i * 4)] = vb[0];
res[(i * 4) + 1] = vb[1];
res[(i * 4) + 2] = vb[2];
res[(i * 4) + 3] = vb[3];
}
return res;
}

public static byte[] UintArrayToBytes(uint[] val)
{
byte[] res = new byte[val.Length * 4];
for (uint i = 0; i < val.Length; i++)
{
byte[] vb = IntToBytes((int)val[i]);
res[(i * 4)] = vb[0];
res[(i * 4) + 1] = vb[1];
res[(i * 4) + 2] = vb[2];
res[(i * 4) + 3] = vb[3];
}
return res;
}

public static byte[] StringArrayToBytes(string[] val, Encoding e)
{
byte[][] baa = new byte[val.Length][];
int l = 0;
for (int i = 0; i < val.Length; i++) { baa[i] = e.GetBytes(val[i]); l += 4 + baa[i].Length; }
byte[] r = new byte[l + 4];
IntToBytes(val.Length).CopyTo(r, 0);
int ofs = 4;
for (int i = 0; i < baa.Length; i++)
{
IntToBytes(baa[i].Length).CopyTo(r, ofs); ofs += 4;
baa[i].CopyTo(r, ofs); ofs += baa[i].Length;
}
return r;
}

public static string[] GetStringArray(byte[] ba, Encoding e)
{
int l = GetInt(ba, 0, 4), ofs = 4;
string[] r = new string[l];
for (int i = 0; i < l; i++)
{
int thislen = GetInt(ba, ofs, 4); ofs += 4;
r[i] = e.GetString(ba, ofs, thislen); ofs += thislen;
}
return r;
}

public void Close()
{
if (!alreadyclosed)
{
if (server != null)
server.ClientClosed(this);

if (OnClose != null)
OnClose(this);

alreadyclosed = true;
#if DEBUG
Console.WriteLine("(ClientInfo) **closed client** at " + DateTime.Now.Ticks);
#endif
}

sock.Close();
}

} // end class ClientInfo


//-----------------------------------------------------------------------
//
// c l a s s S o c k e t s
//
//-----------------------------------------------------------------------

public class Sockets
{
// Socks proxy inspired by http://www.thecodeproject.com/csharp/ZaSocks5Proxy.asp
public static SocksProxy SocksProxy;
public static bool UseSocks = false;

public static Socket CreateTCPSocket(String address, int port) { return CreateTCPSocket(address, port, UseSocks, SocksProxy); }
public static Socket CreateTCPSocket(String address, int port, bool useSocks, SocksProxy proxy)
{
Socket sock;
if (useSocks) sock = ConnectToSocksProxy(proxy.host, proxy.port, address, port, proxy.username, proxy.password);
else
{
IPAddress host = Dns.GetHostByName(address).AddressList[0];
sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

try
{
sock.Connect(new IPEndPoint(host, port));
}
catch (SocketException e)
{
throw new ApplicationException("Socket connect failed, errno " + e.Message);
}
}
return sock;
}

private static string[] errorMsgs = {
"Operation completed successfully.",
"General SOCKS server failure.",
"Connection not allowed by ruleset.",
"Network unreachable.",
"Host unreachable.",
"Connection refused.",
"TTL expired.",
"Command not supported.",
"Address type not supported.",
"Unknown error."
};

public static Socket ConnectToSocksProxy(IPAddress proxyIP, int proxyPort, String destAddress, int destPort, string userName, string password)
{
byte[] request = new byte[257];
byte[] response = new byte[257];
ushort nIndex;

IPAddress destIP = null;

try { destIP = IPAddress.Parse(destAddress); }
catch { }

IPEndPoint proxyEndPoint = new IPEndPoint(proxyIP, proxyPort);

// open a TCP connection to SOCKS server...
Socket s;
s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
s.Connect(proxyEndPoint);
/* } catch(SocketException){
throw new SocketException(0, "Could not connect to proxy server.");
}*/

nIndex = 0;
request[nIndex++] = 0x05; // Version 5.
request[nIndex++] = 0x02; // 2 Authentication methods are in packet...
request[nIndex++] = 0x00; // NO AUTHENTICATION REQUIRED
request[nIndex++] = 0x02; // USERNAME/PASSWORD
// Send the authentication negotiation request...
s.Send(request, nIndex, SocketFlags.None);

// Receive 2 byte response...
int nGot = s.Receive(response, 2, SocketFlags.None);
if (nGot != 2)
throw new ConnectionException("Bad response received from proxy server.");

if (response[1] == 0xFF)
{ // No authentication method was accepted close the socket.
s.Close();
throw new ConnectionException("None of the authentication method was accepted by proxy server.");
}

byte[] rawBytes;

if (/*response[1]==0x02*/true)
{//Username/Password Authentication protocol
nIndex = 0;
request[nIndex++] = 0x05; // Version 5.

// add user name
request[nIndex++] = (byte)userName.Length;
rawBytes = Encoding.Default.GetBytes(userName);
rawBytes.CopyTo(request, nIndex);
nIndex += (ushort)rawBytes.Length;

// add password
request[nIndex++] = (byte)password.Length;
rawBytes = Encoding.Default.GetBytes(password);
rawBytes.CopyTo(request, nIndex);
nIndex += (ushort)rawBytes.Length;

// Send the Username/Password request
s.Send(request, nIndex, SocketFlags.None);
// Receive 2 byte response...
nGot = s.Receive(response, 2, SocketFlags.None);
if (nGot != 2)
throw new ConnectionException("Bad response received from proxy server.");
if (response[1] != 0x00)
throw new ConnectionException("Bad Usernaem/Password.");
}
// This version only supports connect command.
// UDP and Bind are not supported.

// Send connect request now...
nIndex = 0;
request[nIndex++] = 0x05; // version 5.
request[nIndex++] = 0x01; // command = connect.
request[nIndex++] = 0x00; // Reserve = must be 0x00

if (destIP != null)
{// Destination adress in an IP.
switch (destIP.AddressFamily)
{
case AddressFamily.InterNetwork:
// Address is IPV4 format
request[nIndex++] = 0x01;
rawBytes = destIP.GetAddressBytes();
rawBytes.CopyTo(request, nIndex);
nIndex += (ushort)rawBytes.Length;
break;
case AddressFamily.InterNetworkV6:
// Address is IPV6 format
request[nIndex++] = 0x04;
rawBytes = destIP.GetAddressBytes();
rawBytes.CopyTo(request, nIndex);
nIndex += (ushort)rawBytes.Length;
break;
}
}
else
{// Dest. address is domain name.
request[nIndex++] = 0x03; // Address is full-qualified domain name.
request[nIndex++] = Convert.ToByte(destAddress.Length); // length of address.
rawBytes = Encoding.Default.GetBytes(destAddress);
rawBytes.CopyTo(request, nIndex);
nIndex += (ushort)rawBytes.Length;
}

// using big-edian byte order
byte[] portBytes = BitConverter.GetBytes((ushort)destPort);
for (int i = portBytes.Length - 1; i >= 0; i--)
request[nIndex++] = portBytes[i];

// send connect request.
s.Send(request, nIndex, SocketFlags.None);
s.Receive(response); // Get variable length response...
if (response[1] != 0x00)
throw new ConnectionException(errorMsgs[response[1]]);
// Success Connected...
return s;
}

} // end class Sockets



public struct SocksProxy
{
public IPAddress host;
public ushort port;
public string username, password;

public SocksProxy(String hostname, ushort port, String username, String password)
{
this.port = port;
host = Dns.GetHostByName(hostname).AddressList[0];
this.username = username; this.password = password;
}
}



public class ConnectionException : Exception
{
public ConnectionException(string message) : base(message) { }
}



// Server code cribbed from Framework Help
public delegate bool ClientEvent(Server serv, ClientInfo new_client); // whether to accept the client




//-----------------------------------------------------------------------
//
// c l a s s S e r v e r
//
//-----------------------------------------------------------------------

public class Server
{
class ClientState
{
// To hold the state information about a client between transactions
internal Socket Socket = null;
internal const int BufferSize = 1024;
internal byte[] buffer = new byte[BufferSize];
internal StringBuilder sofar = new StringBuilder();

internal ClientState(Socket sock)
{
Socket = sock;
}
}

ArrayList clients = new ArrayList();
Socket ss;

public event ClientEvent Connect, ClientReady;
public IEnumerable Clients
{
get { return clients; }
}

public Socket ServerSocket
{
get { return ss; }
}

public ClientInfo this[int id]
{
get
{
foreach (ClientInfo ci in Clients)
if (ci.ID == id) return ci;

return null;
}
}

private EncryptionType encType;
public EncryptionType DefaultEncryptionType
{
get { return encType; }
set { encType = value; }
}

public int Port
{
get { return ((IPEndPoint)ss.LocalEndPoint).Port; }
}

public Server(int port) : this(port, null) { }

public Server(int port, ClientEvent connDel)
{
Connect = connDel;

#if DEBUG
Console.WriteLine("(Server) constructor called, port = " + port.ToString());
#endif
ss = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
ss.Bind(new IPEndPoint(IPAddress.Any, port));
ss.Listen(100);

// Start the accept process. When a connection is accepted, the callback
// must do this again to accept another connection
#if DEBUG
Console.WriteLine("(Server) calling Accept()");
#endif
ss.BeginAccept(new AsyncCallback(AcceptCallback), ss);
}

internal void ClientClosed(ClientInfo ci)
{
#if DEBUG
Console.WriteLine("(Server) closed client " + ci.ID.ToString() + " at " + DateTime.Now.Ticks);
#endif
clients.Remove(ci);
}

public void Broadcast(byte[] bytes)
{
foreach (ClientInfo ci in clients) ci.Send(bytes);
}

public void BroadcastMessage(uint code, byte[] bytes) { BroadcastMessage(code, bytes, 0); }
public void BroadcastMessage(uint code, byte[] bytes, byte paramType)
{
foreach (ClientInfo ci in clients) ci.SendMessage(code, bytes, paramType);
}

// ASYNC CALLBACK CODE
void AcceptCallback(IAsyncResult ar)
{
try
{
Socket server = (Socket)ar.AsyncState;
Socket cs = server.EndAccept(ar);

#if DEBUG
Console.WriteLine("(Server-AcceptCallback) enter");
#endif
// Start the thing listening again
server.BeginAccept(new AsyncCallback(AcceptCallback), server);

ClientInfo c = new ClientInfo(cs, null, null, ClientDirection.Both, false);
c.server = this;

// Allow the new client to be rejected by the application
if (Connect != null)
{
if (!Connect(this, c))
{
// Rejected
cs.Close();
return;
}
}

// Initiate key exchange
c.EncryptionType = encType;

switch (encType)
{
case EncryptionType.None:
KeyExchangeComplete(c);
break;

case EncryptionType.ServerKey:
c.encKey = GetSymmetricKey();
byte[] key = ClientInfo.GetLengthEncodedVector(c.encKey);
cs.Send(key);
c.MakeEncoders();
KeyExchangeComplete(c);
break;

case EncryptionType.ServerRSAClientKey:
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
RSAParameters p = rsa.ExportParameters(true);
cs.Send(ClientInfo.GetLengthEncodedVector(p.Modulus));
cs.Send(ClientInfo.GetLengthEncodedVector(p.Exponent));
c.encParams = p;
break;

default:
throw new ArgumentException("Unknown or unsupported encryption type " + encType);

} // end switch

clients.Add(c);
c.BeginReceive();
}

catch (ObjectDisposedException) { }
catch (SocketException)
{
throw new ApplicationException("Socket error in AcceptCallBack");
}
catch (Exception e)
{
Console.WriteLine(e);
}

} // end AcceptCallBack

protected virtual byte[] GetSymmetricKey()
{
return EncryptionUtils.GetRandomBytes(24, false);
}

internal void KeyExchangeComplete(ClientInfo ci)
{
// Key exchange is complete on this client. Client ready
// handlers may still force a close of the connection
if (ClientReady != null)
if (!ClientReady(this, ci)) ci.Close();
}

~Server()
{
Close();
}

public void Close()
{
ArrayList cl2 = new ArrayList();
foreach (ClientInfo c in clients) cl2.Add(c);
foreach (ClientInfo c in cl2) c.Close();

ss.Close();
}

} // end class Server



//-----------------------------------------------------------------------
//
// c l a s s B y t e B u i l d e r
//
//-----------------------------------------------------------------------


public class ByteBuilder : MarshalByRefObject
{
byte[][] data;
int packsize, used;

public int Length
{
get
{
int len = 0;
for (int i = 0; i < used; i++) len += data[i].Length;
return len;
}
}

public byte this[int i]
{
get { return Read(i, 1)[0]; }
}

public ByteBuilder() : this(10) { }
public ByteBuilder(int packsize)
{
this.packsize = packsize; used = 0;
data = new byte[packsize][];
}

public ByteBuilder(byte[] data)
{
packsize = 1;
used = 1;
this.data = new byte[][] { data };
}

public ByteBuilder(byte[] data, int len) : this(data, 0, len) { }
public ByteBuilder(byte[] data, int from, int len)
: this(1)
{
Add(data, from, len);
}
public void Add(byte[] moredata) { Add(moredata, 0, moredata.Length); }
public void Add(byte[] moredata, int from, int len)
{
//Console.WriteLine("Getting "+from+" to "+(from+len-1)+" of "+moredata.Length);
if (used < packsize)
{
data[used] = new byte[len];
for (int j = from; j < from + len; j++)
data[used][j - from] = moredata[j];
used++;
}
else
{
// Compress the existing items into the first array
byte[] newdata = new byte[Length + len];
int np = 0;
for (int i = 0; i < used; i++)
for (int j = 0; j < data[i].Length; j++)
newdata[np++] = data[i][j];
for (int j = from; j < from + len; j++)
newdata[np++] = moredata[j];
data[0] = newdata;
for (int i = 1; i < used; i++) data[i] = null;
used = 1;
}
}

public byte[] Read(int from, int len)
{
if (len == 0) return new byte[0];
byte[] res = new byte[len];
int done = 0, start = 0;

for (int i = 0; i < used; i++)
{
if ((start + data[i].Length) <= from)
{
start += data[i].Length; continue;
}
// Now we're in the data block
for (int j = 0; j < data[i].Length; j++)
{
if ((j + start) < from) continue;
res[done++] = data[i][j];
if (done == len) return res;
}
}

throw new ArgumentException("Datapoints " + from + " and " + (from + len) + " must be less than " + Length);
}

public void Clear()
{
used = 0;
for (int i = 0; i < used; i++) data[i] = null;
}

public Parameter GetParameter(ref int index)
{
Parameter res = new Parameter();
res.Type = Read(index++, 1)[0];
byte[] lenba = Read(index, 4);
index += 4;
int len = ClientInfo.GetInt(lenba, 0, 4);
res.content = Read(index, len);
index += len;
return res;
}

public void AddParameter(Parameter param) { AddParameter(param.content, param.Type); }
public void AddParameter(byte[] content, byte Type)
{
Add(new byte[] { Type });
Add(ClientInfo.IntToBytes(content.Length));
Add(content);
}

public static String FormatParameter(Parameter p)
{
switch (p.Type)
{
case ParameterType.Int:
int[] ia = ClientInfo.GetIntArray(p.content);
StringBuilder sb = new StringBuilder();
foreach (int i in ia) sb.Append(i + " ");
return sb.ToString();
case ParameterType.Uint:
ia = ClientInfo.GetIntArray(p.content);
sb = new StringBuilder();
foreach (int i in ia) sb.Append(i.ToString("X8") + " ");
return sb.ToString();
case ParameterType.String:
return Encoding.UTF8.GetString(p.content);
case ParameterType.StringArray:
string[] sa = ClientInfo.GetStringArray(p.content, Encoding.UTF8);
sb = new StringBuilder();
foreach (string s in sa) sb.Append(s + "; ");
return sb.ToString();
case ParameterType.Byte:
sb = new StringBuilder();
foreach (int b in p.content) sb.Append(b.ToString("X2") + " ");
return sb.ToString();
default: return "??";
}
}
}

[Serializable]
public struct Parameter
{
public byte Type;
public byte[] content;

public Parameter(byte[] content, byte type)
{
this.content = content; Type = type;
}
}

public struct ParameterType
{
public const byte Unparameterised = 0;
public const byte Int = 1;
public const byte Uint = 2;
public const byte String = 3;
public const byte Byte = 4;
public const byte StringArray = 5;
}
}

namespace Netsalon.Cryptography
{
// Cryptographic classes
public abstract class BaseCrypto : ICryptoTransform
{
public int InputBlockSize { get { return 1; } }
public int OutputBlockSize { get { return 1; } }
public bool CanTransformMultipleBlocks { get { return true; } }
public bool CanReuseTransform { get { return true; } }

protected byte[] key;
protected byte currentKey;
protected int done = 0, keyinx = 0;

protected BaseCrypto(byte[] key)
{
if (key.Length == 0) throw new ArgumentException("Must provide a key");
this.key = key;
currentKey = 0;
for (int i = 0; i < key.Length; i++) currentKey += key[i];
}

protected abstract byte DoByte(byte b);
public int TransformBlock(byte[] from, int frominx, int len, byte[] to, int toinx)
{
for (int i = 0; i < len; i++)
{
byte oldkey = currentKey;
to[toinx + i] = DoByte(from[frominx + i]);
#if DEBUG
// Console.WriteLine(" encrypting "+from[frominx + i]+" to "+to[toinx + i]+", key is "+oldkey);
#endif
BumpKey();
}
return len;
}
public byte[] TransformFinalBlock(byte[] from, int frominx, int len)
{
byte[] to = new byte[len];
TransformBlock(from, frominx, len, to, 0);
return to;
}
protected void BumpKey()
{
keyinx = (keyinx + 1) % key.Length;
currentKey = Multiply257(key[keyinx], currentKey);
}

protected static byte Multiply257(byte a, byte b)
{
return (byte)((((a + 1) * (b + 1)) % 257) - 1);
}

protected static byte[] complements = { 0, 128, 85, 192, 102, 42, 146, 224, 199, 179, 186, 149, 177, 201, 119, 240, 120, 99, 229, 89, 48, 221, 189, 74, 71, 88, 237, 100, 194, 59, 198, 248, 147, 188, 234, 49, 131, 114, 144, 44, 162, 152, 5, 110, 39, 94, 174, 165, 20, 35, 125, 172, 96, 118, 242, 178, 247, 225, 60, 29, 58, 227, 101, 252, 86, 73, 233, 222, 148, 245, 180, 24, 168, 65, 23, 185, 246, 200, 243, 150, 164, 209, 95, 204, 126, 2, 64, 183, 25, 19, 208, 175, 151, 215, 45, 82, 52, 138, 134, 17, 27, 62, 4, 214, 163, 176, 244, 187, 223, 249, 43, 217, 115, 123, 37, 112, 133, 158, 53, 14, 16, 157, 139, 113, 219, 50, 84, 254, 1, 171, 205, 36, 142, 116, 98, 239, 241, 202, 97, 122, 143, 218, 132, 140, 38, 212, 6, 32, 68, 11, 79, 92, 41, 251, 193, 228, 238, 121, 117, 203, 173, 210, 40, 104, 80, 47, 236, 230, 72, 191, 253, 129, 51, 160, 46, 91, 105, 12, 55, 9, 70, 232, 190, 87, 231, 75, 10, 107, 33, 22, 182, 169, 3, 154, 28, 197, 226, 195, 30, 8, 77, 13, 137, 159, 83, 130, 220, 235, 90, 81, 161, 216, 145, 250, 103, 93, 211, 111, 141, 124, 206, 21, 67, 108, 7, 57, 196, 61, 155, 18, 167, 184, 181, 66, 34, 207, 166, 26, 156, 135, 15, 136, 54, 78, 106, 69, 76, 56, 31, 109, 213, 153, 63, 170, 127, 255 };
protected static byte Complement257(byte b) { return complements[b]; }

public void Dispose() { } // for IDisposable
}

public class SimpleEncryptor : BaseCrypto
{
public SimpleEncryptor(byte[] key) : base(key) { }

protected override byte DoByte(byte b)
{
byte b2 = Multiply257((byte)(b + currentKey), currentKey);
currentKey = Multiply257((byte)(b + b2), currentKey);
return b2;
}
}
public class SimpleDecryptor : BaseCrypto
{
public SimpleDecryptor(byte[] key) : base(key) { }

protected override byte DoByte(byte b)
{
byte b2 = (byte)(Multiply257(b, Complement257(currentKey)) - currentKey);
currentKey = Multiply257((byte)(b + b2), currentKey);
return b2;
}
}

}

As soon as I can chat with my boss, I will ask about sending more code.
GeneralRe: behavior under load Pin
mitchmcc27-Mar-09 8:52
mitchmcc27-Mar-09 8:52 
GeneralRe: behavior under load Pin
mitchmcc27-Mar-09 9:03
mitchmcc27-Mar-09 9:03 
GeneralRe: behavior under load Pin
BobJanova27-Mar-09 9:35
BobJanova27-Mar-09 9:35 
GeneralRe: behavior under load Pin
mitchmcc27-Mar-09 9:55
mitchmcc27-Mar-09 9:55 
GeneralRe: behavior under load Pin
BobJanova27-Mar-09 10:35
BobJanova27-Mar-09 10:35 
GeneralRe: behavior under load Pin
BobJanova28-Mar-09 13:35
BobJanova28-Mar-09 13:35 
GeneralRe: behavior under load Pin
mitchmcc28-Mar-09 23:07
mitchmcc28-Mar-09 23:07 
GeneralRe: behavior under load Pin
mitchmcc29-Mar-09 6:40
mitchmcc29-Mar-09 6:40 
GeneralRe: behavior under load [modified] Pin
BobJanova29-Mar-09 7:28
BobJanova29-Mar-09 7:28 
Questionrace condition? Pin
mitchmcc18-Mar-09 3:27
mitchmcc18-Mar-09 3:27 
AnswerRe: race condition? Pin
BobJanova18-Mar-09 3:42
BobJanova18-Mar-09 3:42 
Generalnewbie questions Pin
mitchmcc16-Mar-09 2:58
mitchmcc16-Mar-09 2:58 
GeneralRe: newbie questions Pin
BobJanova16-Mar-09 3:34
BobJanova16-Mar-09 3:34 
GeneralRe: newbie questions Pin
mitchmcc16-Mar-09 3:48
mitchmcc16-Mar-09 3:48 
GeneralWell done Pin
Xmen Real 9-Mar-09 17:37
professional Xmen Real 9-Mar-09 17:37 
GeneralLocal connection is fine, but web connection errors. Pin
fullofevil5-Feb-09 10:41
fullofevil5-Feb-09 10:41 
GeneralRe: Local connection is fine, but web connection errors. Pin
BobJanova5-Feb-09 10:50
BobJanova5-Feb-09 10:50 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.