public static byte[] MessageTransmissionHandler(Message message) { byte[] textBytes = new byte[0]; if (!String.IsNullOrEmpty(message.Text)) { textBytes = Encoding.Unicode.GetBytes(message.Text); } byte[] dataBytes = new byte[0]; if (message.Data != null) { dataBytes = message.Data; } byte[] messageBytes = ByteHelper.ConcatinateArray(BitConverter.GetBytes(message.From), new byte[] { (byte)message.Type }); messageBytes = ByteHelper.ConcatinateArray(messageBytes, dataBytes, textBytes); return(messageBytes); }
public static byte[] CreateNonMutualContactStatusMessage(int userId, bool relationshipTo, bool relationshipFrom) { UserObj user; if (_users.Any(x => x.ID == userId)) { user = _users.Find(x => x.ID == userId); } else { user = DatabaseCommunication.ReadUser(userId); } byte[] dataBytes; dataBytes = ByteHelper.ConcatinateArray(BitConverter.GetBytes(user.ID), new byte[] { (byte)((relationshipTo ? 64 : 0) + (relationshipFrom ? 32 : 0) + user.Name.Length) }, Encoding.Unicode.GetBytes(user.Name)); return(dataBytes); }
private static string CreateMac(byte[] messageBytes, string sharedSecret) { return(ByteHelper.GetHashString(ByteHelper.ConcatinateArray(ByteHelper.GetHashBytes(messageBytes), Convert.FromBase64String(sharedSecret)))); }
public static byte[] RemoveSignatureAndMac(byte[] bytes) { bytes = ByteHelper.SubArray(bytes, SignatureByteLength + ByteHelper.HashByteLength); // Remove the signature, the version number and the MAC. return(bytes); }
/// <summary> /// This is a threaded method that keeps looping while _online is true. /// It will receive UDP messages on the UdpClient's port number and forward them to the OnPacketReceived event. /// </summary> public void ReceivePacket() // Threaded looping method. { while (_online) { try { IPEndPoint remoteSender = new IPEndPoint(IPAddress.Any, 0); byte[] receivedBytes = _client.Receive(ref remoteSender); if (receivedBytes != null && receivedBytes.Length != 0) { if (receivedBytes.Length == ByteHelper.HashByteLength + 2 && receivedBytes[0] == 0xCE && receivedBytes[receivedBytes.Length - 1] == 0xCE) { if (_messageSendingControlList.Count != 0) { // The received message is a ACK message. string hash = OpenAck(receivedBytes); ControlledPacket packet = _messageSendingControlList.Find(x => x.Hash == hash); if (packet != null) { _messageSendingControlList.Remove(packet); } } } else if (receivedBytes.Length == 1 && receivedBytes[0] == 0xEC) { // Fire an OnEtherConnectionReply event. OnEtherConnectionReply(null); } else { // Send back an ACK packet. string hash = ByteHelper.GetHashString(receivedBytes); byte[] ackBytes = CreateAck(hash); _client.Send(ackBytes, ackBytes.Length, remoteSender); // Check if the message is a duplicate. if (!_messageReceivingControlList.Any(x => x == hash)) { // Add the message's hash to a list so we don't react on the same message twice. _messageReceivingControlList.Add(hash); if (_messageReceivingControlList.Count > 5) // Only keep the latest 5 messages. { _messageReceivingControlList.RemoveAt(0); } // Fire an OnPacketReceived event. PacketReceivedEventArgs args = new PacketReceivedEventArgs(); args.Sender = remoteSender; args.Data = receivedBytes; OnPacketReceived(args); } } } } catch (SocketException ex) { if (ex.SocketErrorCode != SocketError.TimedOut) { System.Diagnostics.Debug.WriteLine("### " + _threadPacketListener.Name + " has crashed:"); System.Diagnostics.Debug.WriteLine("### " + ex.Message); System.Diagnostics.Debug.WriteLine("### " + ex.ToString()); break; } else { continue; } } } }
/// <summary> /// Create an ACK packet from a base64 hash string. /// </summary> /// <param name="hash">Base64 hash string to be used.</param> protected byte[] CreateAck(string hash) { byte[] ackTag = new byte[] { 0xCE }; // 0xCE = 206 byte[] ackBytes = ByteHelper.ConcatinateArray(ackTag, Convert.FromBase64String(hash), ackTag); return(ackBytes); }
public static void SendAllContacts(int userId) { Dictionary <int, byte> contacts = DatabaseCommunication.GetAllContacts(userId); foreach (KeyValuePair <int, byte> contact in contacts) { byte[] contactStatusMessageBytes; if (contact.Value == 0x03) // 0b00000011 { contactStatusMessageBytes = CreateMutualContactStatusMessage(contact.Key); } else { contactStatusMessageBytes = CreateNonMutualContactStatusMessage(contact.Key, ByteHelper.CheckBitCodeIndex(contact.Value, 0), ByteHelper.CheckBitCodeIndex(contact.Value, 1)); } MessageToUser(userId, ChatTwo_Protocol.MessageType.ContactStatus, contactStatusMessageBytes); } }
public static void MessageReceivedHandler(object sender, PacketReceivedEventArgs args) { if (!DatabaseCommunication.Active) #if DEBUG { throw new NotImplementedException("Database connection was not active and a reply for this have not been implemented yet."); } // Need to add a simple debug message here, but this works as a great breakpoint until then. // Also need to make some kind of error message I can send back to the client. #else { return; } #endif if (args.Data[0] == 0x92) { string sharedSecret; // Position of the Type byte is 30 (SignatureByteLength + MacByteLength + TimezByteLength + UserIdByteLength). ChatTwo_Protocol.MessageType type = (ChatTwo_Protocol.MessageType)args.Data[ChatTwo_Protocol.SignatureByteLength + ByteHelper.HashByteLength + 4 + 4]; // Position of the UserID bytes is 26 (SignatureByteLength + MacByteLength + TimezByteLength) with a length of 4. int userId = ByteHelper.ToInt32(args.Data, ChatTwo_Protocol.SignatureByteLength + ByteHelper.HashByteLength + 4); if (type == ChatTwo_Protocol.MessageType.CreateUser) { sharedSecret = ChatTwo_Protocol.DefaultSharedSecret; } else if (type == ChatTwo_Protocol.MessageType.Login) { #if DEBUG byte[] test = ByteHelper.SubArray(args.Data, ChatTwo_Protocol.SignatureByteLength + ByteHelper.HashByteLength + 4); #endif // Don't take the Timez as part of the sharedSecret. This is mostly because of a problem I have in the client where I make the sharedSecrt before I add the Timez. sharedSecret = ByteHelper.GetHashString(ByteHelper.SubArray(args.Data, ChatTwo_Protocol.SignatureByteLength + ByteHelper.HashByteLength + 4)); } else { if (!_users.Any(x => x.ID == userId)) { return; // This is mostly to prevent clients with a connection to a previces server instants from crashing the server. Need to fix this. } sharedSecret = _users.Find(x => x.ID == userId).Secret; } if (ChatTwo_Protocol.ValidateMac(args.Data, sharedSecret)) { Message message = ChatTwo_Protocol.MessageReceivedHandler(args); switch (message.Type) { case ChatTwo_Protocol.MessageType.CreateUser: { string passwordHash = Convert.ToBase64String(message.Data, 0, ByteHelper.HashByteLength); string username = Encoding.Unicode.GetString(ByteHelper.SubArray(message.Data, ByteHelper.HashByteLength)); if (username.Length < 1 || username.Length > 30) { // Username is too short or too long. MessageToIp(message.Ip, ChatTwo_Protocol.MessageType.CreateUserReply, new byte[] { 0x02 }); return; } bool worked = DatabaseCommunication.CreateUser(username, passwordHash); if (!worked) { // Some error prevented the user from being created. Best guess is that a user with that name already exist. MessageToIp(message.Ip, ChatTwo_Protocol.MessageType.CreateUserReply, new byte[] { 0x01 }); return; } // Uesr creation was successful! MessageToIp(message.Ip, ChatTwo_Protocol.MessageType.CreateUserReply, new byte[] { 0x00 }); break; } case ChatTwo_Protocol.MessageType.Login: { string passwordHash = Convert.ToBase64String(message.Data, 0, ByteHelper.HashByteLength); string username = Encoding.Unicode.GetString(ByteHelper.SubArray(message.Data, ByteHelper.HashByteLength)); UserObj user = DatabaseCommunication.LoginUser(username, passwordHash); if (user == null) { // Have to send back a LoginReply message here with a "wrong username/password" error. _tempUsers.Add(message.Ip, sharedSecret); MessageToIp(message.Ip, ChatTwo_Protocol.MessageType.LoginReply, new byte[] { 0x01 }); return; } if (_users.Any(x => x.ID == user.ID)) { // Have to send back a LoginReply message here with a "User is already online" error. _tempUsers.Add(message.Ip, sharedSecret); MessageToIp(message.Ip, ChatTwo_Protocol.MessageType.LoginReply, new byte[] { 0x02 }); return; } user.Secret = sharedSecret; user.Socket = message.Ip; user.Online = true; _users.Add(user); MessageToUser(user.ID, ChatTwo_Protocol.MessageType.LoginReply, ByteHelper.ConcatinateArray(new byte[] { 0x00 }, BitConverter.GetBytes(user.ID)), user.Name); UserConnect(user); break; } case ChatTwo_Protocol.MessageType.Status: { UserObj user = _users.Find(x => x.ID == message.From); if (user != null) // If the user is not found, don't do anything. (Can this happen?) { if (!IPEndPoint.Equals(user.Socket, message.Ip)) { user.Socket = message.Ip; // Message all contacts of the user with the new IP change. TellMutualContactsAboutUserStatusChange(user.ID); } DatabaseCommunication.UpdateUser(user.ID, user.Socket); } break; } case ChatTwo_Protocol.MessageType.ContactRequest: { string username = Encoding.Unicode.GetString(message.Data); UserObj user = DatabaseCommunication.LookupUser(username); if (user == null) { // Have to send back a ContactRequestReply message here with a "No user with that name" error. MessageToUser(message.From, ChatTwo_Protocol.MessageType.ContactRequestReply, new byte[] { 0x01 }); return; } if (user.ID == message.From) { // Have to send back a ContactRequestReply message here with a "You can't add your self" error. MessageToUser(message.From, ChatTwo_Protocol.MessageType.ContactRequestReply, new byte[] { 0x02 }); return; } bool added = DatabaseCommunication.AddContact(message.From, user.ID); if (!added) { // Have to send back a ContactRequestReply message here with a "User is already a contact" error. MessageToUser(message.From, ChatTwo_Protocol.MessageType.ContactRequestReply, new byte[] { 0x03 }); return; } MessageToUser(message.From, ChatTwo_Protocol.MessageType.ContactRequestReply, new byte[] { 0x00 }); //if (_users.Any(x => x.ID == user.ID)) // TellContactsAboutUserStatusChange(message.From, true); // Need to figure this out. break; } } } #if DEBUG else { throw new NotImplementedException("Could not validate the MAC of the received message."); } // Need to add a simple debug message here, but this works as a great breakpoint until then. #endif } #if DEBUG else { throw new NotImplementedException("Could not validate the signature of the received message. The signature was \"0x" + args.Data[0] + "\" but only \"0x92\" is allowed."); } // Need to add a simple debug message here, but this works as a great breakpoint until then. #endif }