private void CreateUserHash() { for (int i = 0; i < 8; i++) { ushort random = MpdUtilities.GetRandomUInt16(); Array.Copy(BitConverter.GetBytes(random), 0, UserHash_, i * 2, 2); } // mark as emule client. that will be need in later version UserHash_[5] = 14; UserHash_[14] = 111; }
public virtual int EncryptSendServer(ref byte[] ppbyBuf, int nBufLen, uint dwBaseKey) { Debug.Assert(MuleApplication.Instance.Preference.IsServerCryptLayerUDPEnabled); Debug.Assert(dwBaseKey != 0); byte byPadLen = 0; // padding disabled for UDP currently int nCryptedLen = nBufLen + byPadLen + CRYPT_HEADER_WITHOUTPADDING; byte[] pachCryptedBuffer = new byte[nCryptedLen]; ushort nRandomKeyPart = MpdUtilities.GetRandomUInt16(); byte[] achKeyData = new byte[7]; Array.Copy(BitConverter.GetBytes(dwBaseKey), achKeyData, 4); achKeyData[4] = MAGICVALUE_UDP_CLIENTSERVER; Array.Copy(BitConverter.GetBytes(nRandomKeyPart), 0, achKeyData, 5, 2); MD5 md5 = MD5.Create(); byte[] rawHash = md5.ComputeHash(achKeyData); RC4Key keySendKey = null; MuleUtilities.RC4CreateKey(rawHash, 16, ref keySendKey, true); // create the semi random byte encryption header byte bySemiRandomNotProtocolMarker = 0; int i; for (i = 0; i < 128; i++) { bySemiRandomNotProtocolMarker = MpdUtilities.GetRandomUInt8(); if (bySemiRandomNotProtocolMarker != MuleConstants.PROTOCOL_EDONKEYPROT) // not allowed values { break; } } if (i >= 128) { // either we have _real_ bad luck or the randomgenerator is a bit messed up Debug.Assert(false); bySemiRandomNotProtocolMarker = 0x01; } uint dwMagicValue = MAGICVALUE_UDP_SYNC_SERVER; pachCryptedBuffer[0] = bySemiRandomNotProtocolMarker; Array.Copy(BitConverter.GetBytes(nRandomKeyPart), 0, pachCryptedBuffer, 1, 2); MuleUtilities.RC4Crypt(BitConverter.GetBytes(dwMagicValue), 0, pachCryptedBuffer, 3, 4, keySendKey); MuleUtilities.RC4Crypt(new byte[] { byPadLen }, 0, pachCryptedBuffer, 7, 1, keySendKey); Random rand = new Random(); for (int j = 0; j < byPadLen; j++) { byte byRand = (byte)rand.Next(255); // they actually dont really need to be random, but it doesn't hurts either MuleUtilities.RC4Crypt(new byte[] { byRand }, 0, pachCryptedBuffer, CRYPT_HEADER_WITHOUTPADDING + j, 1, keySendKey); } MuleUtilities.RC4Crypt(ppbyBuf, 0, pachCryptedBuffer, CRYPT_HEADER_WITHOUTPADDING + byPadLen, (uint)nBufLen, keySendKey); ppbyBuf = pachCryptedBuffer; MuleApplication.Instance.Statistics.AddUpDataOverheadCrypt((uint)(nCryptedLen - nBufLen)); return(nCryptedLen); }
private bool ProcessPacket(byte[] buffer, int offset, int size, uint opcode, uint nIP, ushort nUDPPort) { try { MuleApplication.Instance.Statistics.AddDownDataOverheadServer((uint)size); ED2KServer pServer = MuleApplication.Instance.ServerList.GetServerByIPUDP(nIP, nUDPPort, true); if (pServer != null) { pServer.ResetFailedCount(); } switch ((OperationCodeEnum)opcode) { case OperationCodeEnum.OP_GLOBSEARCHRES: { SafeMemFile data = MpdObjectManager.CreateSafeMemFile(buffer, size); // process all search result packets int iLeft; do { uint uResultCount = MuleApplication.Instance.SearchList.ProcessUDPSearchAnswer(data, true /*pServer.GetUnicodeSupport()*/, nIP, nUDPPort - 4); // check if there is another source packet iLeft = (int)(data.Length - data.Position); if (iLeft >= 2) { byte protocol = data.ReadUInt8(); iLeft--; if (protocol != MuleConstants.PROTOCOL_EDONKEYPROT) { data.Seek(-1, System.IO.SeekOrigin.Current); iLeft += 1; break; } byte opcode1 = data.ReadUInt8(); iLeft--; if (opcode1 != (byte)OperationCodeEnum.OP_GLOBSEARCHRES) { data.Seek(-2, System.IO.SeekOrigin.Current); iLeft += 2; break; } } }while (iLeft > 0); break; } case OperationCodeEnum.OP_GLOBFOUNDSOURCES: { SafeMemFile data = MpdObjectManager.CreateSafeMemFile(buffer, size); // process all source packets int iLeft; do { byte[] fileid = new byte[16]; data.ReadHash16(fileid); PartFile file = MuleApplication.Instance.DownloadQueue.GetFileByID(fileid); if (file != null) { file.AddSources(data, nIP, (ushort)(nUDPPort - 4), false); } else { // skip sources for that file uint count = data.ReadUInt8(); data.Seek(count * (4 + 2), System.IO.SeekOrigin.Current); } // check if there is another source packet iLeft = (int)(data.Length - data.Position); if (iLeft >= 2) { byte protocol = data.ReadUInt8(); iLeft--; if (protocol != MuleConstants.PROTOCOL_EDONKEYPROT) { data.Seek(-1, System.IO.SeekOrigin.Current); iLeft += 1; break; } byte opcode1 = data.ReadUInt8(); iLeft--; if (opcode1 != (byte)OperationCodeEnum.OP_GLOBFOUNDSOURCES) { data.Seek(-2, System.IO.SeekOrigin.Current); iLeft += 2; break; } } }while (iLeft > 0); break; } case OperationCodeEnum.OP_GLOBSERVSTATRES: { if (size < 12 || pServer == null) { return(true); } uint challenge = BitConverter.ToUInt32(buffer, 0); if (challenge != pServer.Challenge) { return(true); } if (pServer != null) { pServer.Challenge = 0; pServer.CryptPingReplyPending = false; uint tNow = MpdUtilities.Time(); Random rand = new Random(); // if we used Obfuscated ping, we still need to reset the time properly pServer.LastPingedTime = Convert.ToUInt32(tNow - (rand.Next() % MuleConstants.ONE_HOUR_SEC)); } uint cur_user = BitConverter.ToUInt32(buffer, 4); uint cur_files = BitConverter.ToUInt32(buffer, 8); uint cur_maxusers = 0; uint cur_softfiles = 0; uint cur_hardfiles = 0; uint uUDPFlags = 0; uint uLowIDUsers = 0; uint dwServerUDPKey = 0; ushort nTCPObfuscationPort = 0; ushort nUDPObfuscationPort = 0; if (size >= 16) { cur_maxusers = BitConverter.ToUInt32(buffer, 12); } if (size >= 24) { cur_softfiles = BitConverter.ToUInt32(buffer, 16); cur_hardfiles = BitConverter.ToUInt32(buffer, 20); } if (size >= 28) { uUDPFlags = BitConverter.ToUInt32(buffer, 24); } if (size >= 32) { uLowIDUsers = BitConverter.ToUInt32(buffer, 28); } if (size >= 40) { // TODO debug check if this packet was encrypted if it has a key nUDPObfuscationPort = BitConverter.ToUInt16(buffer, 32); nTCPObfuscationPort = BitConverter.ToUInt16(buffer, 34);; dwServerUDPKey = BitConverter.ToUInt32(buffer, 36); } if (pServer != null) { pServer.Ping = MpdUtilities.GetTickCount() - pServer.LastPinged; pServer.UserCount = cur_user; pServer.FileCount = cur_files; pServer.MaxUsers = cur_maxusers; pServer.SoftFiles = cur_softfiles; pServer.HardFiles = cur_hardfiles; pServer.ServerKeyUDP = dwServerUDPKey; pServer.ObfuscationPortTCP = nTCPObfuscationPort; pServer.ObfuscationPortUDP = nUDPObfuscationPort; // if the received UDP flags do not match any already stored UDP flags, // reset the server version string because the version (which was determined by last connecting to // that server) is most likely not accurat any longer. // this may also give 'false' results because we don't know the UDP flags when connecting to a server // with TCP. //if (pServer.GetUDPFlags() != uUDPFlags) // pServer.Version(_T = ""); pServer.UDPFlags = (ED2KServerUdpFlagsEnum)uUDPFlags; pServer.LowIDUsers = uLowIDUsers; pServer.SetLastDescPingedCount(false); if (pServer.LastDescPingedCount < 2) { // eserver 16.45+ supports a new OP_SERVER_DESC_RES answer, if the OP_SERVER_DESC_REQ contains a uint // challenge, the server returns additional info with OP_SERVER_DESC_RES. To properly distinguish the // old and new OP_SERVER_DESC_RES answer, the challenge has to be selected carefully. The first 2 bytes // of the challenge (in network byte order) MUST NOT be a valid string-len-int16! Packet packet1 = MuleApplication.Instance.NetworkObjectManager.CreatePacket(OperationCodeEnum.OP_SERVER_DESC_REQ, 4); uint uDescReqChallenge = ((uint)MpdUtilities.GetRandomUInt16() << 16) + MuleConstants.INV_SERV_DESC_LEN; // 0xF0FF = an 'invalid' string length. pServer.DescReqChallenge = uDescReqChallenge; Array.Copy(BitConverter.GetBytes(uDescReqChallenge), packet1.Buffer, 4); MuleApplication.Instance.Statistics.AddUpDataOverheadServer(packet1.Size); MuleApplication.Instance.ServerConnect.SendUDPPacket(packet1, pServer, true); } else { pServer.SetLastDescPingedCount(true); } } break; } case OperationCodeEnum.OP_SERVER_DESC_RES: { if (pServer == null) { return(true); } // old packet: <name_len 2><name name_len><desc_len 2 desc_en> // new packet: <challenge 4><taglist> // // NOTE: To properly distinguish between the two packets which are both useing the same opcode... // the first two bytes of <challenge> (in network byte order) have to be an invalid <name_len> at least. SafeMemFile srvinfo = MpdObjectManager.CreateSafeMemFile(buffer, size); if (size >= 8 && BitConverter.ToUInt16(buffer, 0) == MuleConstants.INV_SERV_DESC_LEN) { if (pServer.DescReqChallenge != 0 && BitConverter.ToUInt32(buffer, 0) == pServer.DescReqChallenge) { pServer.DescReqChallenge = 0; srvinfo.ReadUInt32(); // skip challenge uint uTags = srvinfo.ReadUInt32(); for (uint i = 0; i < uTags; i++) { Tag tag = MpdObjectManager.CreateTag(srvinfo, true /*pServer.GetUnicodeSupport()*/); if (tag.NameID == MuleConstants.ST_SERVERNAME && tag.IsStr) { pServer.ServerName = tag.Str; } else if (tag.NameID == MuleConstants.ST_DESCRIPTION && tag.IsStr) { pServer.Description = tag.Str; } else if (tag.NameID == MuleConstants.ST_DYNIP && tag.IsStr) { // Verify that we really received a DN. IPAddress address; if (!IPAddress.TryParse(tag.Str, out address) || address == IPAddress.None) { string strOldDynIP = pServer.DynIP; pServer.DynIP = tag.Str; // If a dynIP-server changed its address or, if this is the // first time we get the dynIP-address for a server which we // already have as non-dynIP in our list, we need to remove // an already available server with the same 'dynIP:port'. if (string.Compare(strOldDynIP, pServer.DynIP, true) != 0) { MuleApplication.Instance.ServerList.RemoveDuplicatesByAddress(pServer); } } } else if (tag.NameID == MuleConstants.ST_VERSION && tag.IsStr) { pServer.Version = tag.Str; } else if (tag.NameID == MuleConstants.ST_VERSION && tag.IsInt) { pServer.Version = string.Format("{0}.{1}", tag.Int >> 16, tag.Int & 0xFFFF); } } } else { // A server sent us a new server description packet (including a challenge) although we did not // ask for it. This may happen, if there are multiple servers running on the same machine with // multiple IPs. If such a server is asked for a description, the server will answer 2 times, // but with the same IP. } } else { string strName = srvinfo.ReadString(true /*pServer.GetUnicodeSupport()*/); string strDesc = srvinfo.ReadString(true /*pServer.GetUnicodeSupport()*/); pServer.Description = strDesc; pServer.ServerName = strName; } break; } default: return(false); } return(true); } catch (Exception error) { ProcessPacketError((uint)size, (uint)opcode, nIP, nUDPPort, error); if (opcode == (byte)OperationCodeEnum.OP_GLOBSEARCHRES || opcode == (byte)OperationCodeEnum.OP_GLOBFOUNDSOURCES) { return(true); } } return(false); }
public virtual int EncryptSendClient(ref byte[] ppbyBuf, int nBufLen, byte[] pachClientHashOrKadID, bool bKad, uint nReceiverVerifyKey, uint nSenderVerifyKey) { Debug.Assert(MuleApplication.Instance.PublicIP != 0 || bKad); Debug.Assert(MuleApplication.Instance.Preference.IsClientCryptLayerSupported); Debug.Assert(pachClientHashOrKadID != null || nReceiverVerifyKey != 0); Debug.Assert((nReceiverVerifyKey == 0 && nSenderVerifyKey == 0) || bKad); byte byPadLen = 0; // padding disabled for UDP currently int nCryptHeaderLen = byPadLen + CRYPT_HEADER_WITHOUTPADDING + (bKad ? 8 : 0); int nCryptedLen = nBufLen + nCryptHeaderLen; byte[] pachCryptedBuffer = new byte[nCryptedLen]; bool bKadRecKeyUsed = false; ushort nRandomKeyPart = MpdUtilities.GetRandomUInt16(); MD5 md5 = MD5.Create(); byte[] rawHash = null; if (bKad) { if ((pachClientHashOrKadID == null || MpdUtilities.IsNullMd4(pachClientHashOrKadID)) && nReceiverVerifyKey != 0) { bKadRecKeyUsed = true; byte[] achKeyData = new byte[6]; Array.Copy(BitConverter.GetBytes(nReceiverVerifyKey), achKeyData, 4); Array.Copy(BitConverter.GetBytes(nRandomKeyPart), 0, achKeyData, 4, 2); rawHash = md5.ComputeHash(achKeyData); //DEBUG_ONLY( DebugLog(_T("Creating obfuscated Kad packet encrypted by ReceiverKey (%u)"), nReceiverVerifyKey) ); } else if (pachClientHashOrKadID != null && !MpdUtilities.IsNullMd4(pachClientHashOrKadID)) { byte[] achKeyData = new byte[18]; MpdUtilities.Md4Cpy(achKeyData, pachClientHashOrKadID); Array.Copy(BitConverter.GetBytes(nRandomKeyPart), 0, achKeyData, 16, 2); rawHash = md5.ComputeHash(achKeyData); //DEBUG_ONLY( DebugLog(_T("Creating obfuscated Kad packet encrypted by Hash/NodeID %s"), md4str(pachClientHashOrKadID)) ); } else { ppbyBuf = null; Debug.Assert(false); return(nBufLen); } } else { byte[] achKeyData = new byte[23]; MpdUtilities.Md4Cpy(achKeyData, pachClientHashOrKadID); uint dwIP = (uint)MuleApplication.Instance.PublicIP; Array.Copy(BitConverter.GetBytes(dwIP), 0, achKeyData, 16, 4); Array.Copy(BitConverter.GetBytes(nRandomKeyPart), 0, achKeyData, 21, 2); achKeyData[20] = MAGICVALUE_UDP; rawHash = md5.ComputeHash(achKeyData); } RC4Key keySendKey = null; MuleUtilities.RC4CreateKey(rawHash, 16, ref keySendKey, true); // create the semi random byte encryption header byte bySemiRandomNotProtocolMarker = 0; int i; for (i = 0; i < 128; i++) { bySemiRandomNotProtocolMarker = MpdUtilities.GetRandomUInt8(); bySemiRandomNotProtocolMarker = (byte)(bKad ? (bySemiRandomNotProtocolMarker & 0xFE) : (bySemiRandomNotProtocolMarker | 0x01)); // set the ed2k/kad marker bit if (bKad) { bySemiRandomNotProtocolMarker = (byte)(bKadRecKeyUsed ? ((bySemiRandomNotProtocolMarker & 0xFE) | 0x02) : (bySemiRandomNotProtocolMarker & 0xFC)); // set the ed2k/kad and nodeid/reckey markerbit } else { bySemiRandomNotProtocolMarker = (byte)(bySemiRandomNotProtocolMarker | 0x01); // set the ed2k/kad marker bit } bool bOk = false; switch (bySemiRandomNotProtocolMarker) { // not allowed values case MuleConstants.PROTOCOL_EMULEPROT: case MuleConstants.PROTOCOL_KADEMLIAPACKEDPROT: case MuleConstants.PROTOCOL_KADEMLIAHEADER: case MuleConstants.PROTOCOL_UDPRESERVEDPROT1: case MuleConstants.PROTOCOL_UDPRESERVEDPROT2: case MuleConstants.PROTOCOL_PACKEDPROT: break; default: bOk = true; break; } if (bOk) { break; } } if (i >= 128) { // either we have _really_ bad luck or the randomgenerator is a bit messed up Debug.Assert(false); bySemiRandomNotProtocolMarker = 0x01; } uint dwMagicValue = MAGICVALUE_UDP_SYNC_CLIENT; pachCryptedBuffer[0] = bySemiRandomNotProtocolMarker; Array.Copy(BitConverter.GetBytes(nRandomKeyPart), 0, pachCryptedBuffer, 1, 2); MuleUtilities.RC4Crypt(BitConverter.GetBytes(dwMagicValue), 0, pachCryptedBuffer, 3, 4, keySendKey); MuleUtilities.RC4Crypt(BitConverter.GetBytes(byPadLen), 0, pachCryptedBuffer, 7, 1, keySendKey); Random rand = new Random(); for (int j = 0; j < byPadLen; j++) { byte byRand = (byte)rand.Next(255); // they actually dont really need to be random, but it doesn't hurts either MuleUtilities.RC4Crypt(BitConverter.GetBytes(byRand), 0, pachCryptedBuffer, CRYPT_HEADER_WITHOUTPADDING + j, 1, keySendKey); } if (bKad) { MuleUtilities.RC4Crypt(BitConverter.GetBytes(nReceiverVerifyKey), 0, pachCryptedBuffer, CRYPT_HEADER_WITHOUTPADDING + byPadLen, 4, keySendKey); MuleUtilities.RC4Crypt(BitConverter.GetBytes(nSenderVerifyKey), 0, pachCryptedBuffer, CRYPT_HEADER_WITHOUTPADDING + byPadLen + 4, 4, keySendKey); } MuleUtilities.RC4Crypt(ppbyBuf, 0, pachCryptedBuffer, nCryptHeaderLen, (uint)nBufLen, keySendKey); ppbyBuf = pachCryptedBuffer; MuleApplication.Instance.Statistics.AddUpDataOverheadCrypt((uint)(nCryptedLen - nBufLen)); return(nCryptedLen); }
public void ServerStats() { // Update the server list even if we are connected to Kademlia only. The idea is for both networks to keep // each other up to date.. Kad network can get you back into the ED2K network.. And the ED2K network can get // you back into the Kad network.. if (MuleApplication.Instance.ServerConnect.IsConnected && MuleApplication.Instance.ServerConnect.IsUDPSocketAvailable && servers_.Count > 0) { ED2KServer ping_server = GetNextStatServer(); if (ping_server == null) { return; } uint tNow = MpdUtilities.Time(); ED2KServer test = ping_server; while (ping_server.LastPingedTime != 0 && (tNow - ping_server.LastPingedTime) < MuleConstants.UDPSERVSTATREASKTIME) { ping_server = GetNextStatServer(); if (ping_server == test) { return; } } // IP-filter: We do not need to IP-filter any servers here, even dynIP-servers are not // needed to get filtered here. See also comments in 'CServerSocket::ConnectTo'. if (ping_server.FailedCount >= MuleApplication.Instance.Preference.DeadServerRetries) { RemoveServer(ping_server); return; } Random rand = new Random((int)tNow); ping_server.RealLastPingedTime = tNow; // this is not used to calcualte the next ping, but only to ensure a minimum delay for premature pings if (!ping_server.CryptPingReplyPending && (tNow - ping_server.LastPingedTime) >= MuleConstants.UDPSERVSTATREASKTIME && MuleApplication.Instance.PublicIP != 0 && MuleApplication.Instance.Preference.IsServerCryptLayerUDPEnabled) { // we try a obfsucation ping first and wait 20 seconds for an answer // if it doesn'T get responsed, we don't count it as error but continue with a normal ping ping_server.CryptPingReplyPending = true; int nPacketLen = 4 + (byte)(rand.Next() % 16); // max padding 16 bytes byte[] pRawPacket = new byte[nPacketLen]; int dwChallenge = (rand.Next() << 17) | (rand.Next() << 2) | (rand.Next() & 0x03); if (dwChallenge == 0) { dwChallenge++; } Array.Copy(BitConverter.GetBytes(dwChallenge), pRawPacket, 4); for (uint i = 4; i < nPacketLen; i++) // fillung up the remaining bytes with random data { pRawPacket[i] = (byte)rand.Next(); } ping_server.Challenge = (uint)dwChallenge; ping_server.LastPinged = MpdUtilities.GetTickCount(); ping_server.LastPingedTime = ((tNow - (uint)MuleConstants.UDPSERVSTATREASKTIME) + 20); // give it 20 seconds to respond MuleApplication.Instance.Statistics.AddUpDataOverheadServer((uint)nPacketLen); MuleApplication.Instance.ServerConnect.SendUDPPacket(null, ping_server, true, (ushort)(ping_server.Port + 12), pRawPacket, (uint)nPacketLen); } else if (ping_server.CryptPingReplyPending || MuleApplication.Instance.PublicIP == 0 || !MuleApplication.Instance.Preference.IsServerCryptLayerUDPEnabled) { // our obfsucation ping request was not answered, so probably the server doesn'T supports obfuscation // continue with a normal request ping_server.CryptPingReplyPending = false; Packet packet = MuleApplication.Instance.NetworkObjectManager.CreatePacket(OperationCodeEnum.OP_GLOBSERVSTATREQ, 4); int uChallenge = 0x55AA0000 + MpdUtilities.GetRandomUInt16(); ping_server.Challenge = (uint)uChallenge; Array.Copy(BitConverter.GetBytes(uChallenge), packet.Buffer, 4); ping_server.LastPinged = MpdUtilities.GetTickCount(); ping_server.LastPingedTime = (uint)(tNow - (rand.Next() % MuleConstants.ONE_HOUR_SEC)); ping_server.AddFailedCount(); MuleApplication.Instance.Statistics.AddUpDataOverheadServer(packet.Size); MuleApplication.Instance.ServerConnect.SendUDPPacket(packet, ping_server, true); } else { Debug.Assert(false); } } }