private void ProcessPacketError(uint size, uint opcode, uint nIP, ushort nUDPPort, Exception ex) { string strName = string.Empty; ED2KServer pServer = MuleApplication.Instance.ServerList.GetServerByIPUDP(nIP, nUDPPort); if (pServer != null) { strName = " (" + pServer.ServerName + ")"; } MpdUtilities.DebugLogWarning(false, string.Format("Error: Failed to process server UDP packet from {0}:{1}{2} opcode=0x{3} size={4} - {5}", MpdUtilities.IP2String(nIP), nUDPPort, strName, opcode, size, ex.Message), ex); }
public virtual int DecryptReceivedClient(byte[] pbyBufIn, int nBufLen, out byte[] ppbyBufOut, uint dwIP, out uint nReceiverVerifyKey, out uint nSenderVerifyKey) { int nResult = nBufLen; ppbyBufOut = pbyBufIn; nReceiverVerifyKey = 0; nSenderVerifyKey = 0; if (nResult <= CRYPT_HEADER_WITHOUTPADDING /*|| !MuleApplication.Instance.Preference.IsClientCryptLayerSupported()*/) { return(nResult); } switch (pbyBufIn[0]) { case MuleConstants.PROTOCOL_EMULEPROT: case MuleConstants.PROTOCOL_KADEMLIAPACKEDPROT: case MuleConstants.PROTOCOL_KADEMLIAHEADER: case MuleConstants.PROTOCOL_UDPRESERVEDPROT1: case MuleConstants.PROTOCOL_UDPRESERVEDPROT2: case MuleConstants.PROTOCOL_PACKEDPROT: return(nResult); // no encrypted packet (see description on top) } // might be an encrypted packet, try to decrypt RC4Key keyReceiveKey = null; uint dwValue = 0; // check the marker bit which type this packet could be and which key to test first, this is only an indicator since old clients have it set random // see the header for marker bits explanation byte byCurrentTry = (byte)(((pbyBufIn[0] & 0x03) == 3) ? 1 : (pbyBufIn[0] & 0x03)); byte byTries; if (MuleApplication.Instance.KadEngine.Preference == null) { // if kad never run, no point in checking anything except for ed2k encryption byTries = 1; byCurrentTry = 1; } else { byTries = 3; } bool bKadRecvKeyUsed = false; bool bKad = false; do { byTries--; MD5 md5 = MD5.Create(); byte[] rawHash = null; if (byCurrentTry == 0) { // kad packet with NodeID as key bKad = true; bKadRecvKeyUsed = false; if (MuleApplication.Instance.KadEngine.Preference != null) { byte[] achKeyData = new byte[18]; Array.Copy(MuleApplication.Instance.KadEngine.Preference.KadID.Bytes, 0, achKeyData, 0, 16); Array.Copy(pbyBufIn, 1, achKeyData, 16, 2); // random key part sent from remote client rawHash = md5.ComputeHash(achKeyData); } } else if (byCurrentTry == 1) { // ed2k packet bKad = false; bKadRecvKeyUsed = false; byte[] achKeyData = new byte[23]; MpdUtilities.Md4Cpy(achKeyData, MuleApplication.Instance.Preference.UserHash); achKeyData[20] = MAGICVALUE_UDP; Array.Copy(BitConverter.GetBytes(dwIP), 0, achKeyData, 16, 4); Array.Copy(pbyBufIn, 1, achKeyData, 21, 2); // random key part sent from remote client rawHash = md5.ComputeHash(achKeyData); } else if (byCurrentTry == 2) { // kad packet with ReceiverKey as key bKad = true; bKadRecvKeyUsed = true; if (MuleApplication.Instance.KadEngine.Preference != null) { byte[] achKeyData = new byte[6]; Array.Copy(BitConverter.GetBytes(MuleApplication.Instance.KadEngine.Preference.GetUDPVerifyKey(dwIP)), achKeyData, 4); Array.Copy(pbyBufIn, 1, achKeyData, 4, 2); // random key part sent from remote client rawHash = md5.ComputeHash(achKeyData); } } else { Debug.Assert(false); } MuleUtilities.RC4CreateKey(rawHash, 16, ref keyReceiveKey, true); byte[] outBuf = new byte[4]; MuleUtilities.RC4Crypt(pbyBufIn, 3, outBuf, 0, 4, keyReceiveKey); dwIP = BitConverter.ToUInt32(outBuf, 0); byCurrentTry = (byte)((byCurrentTry + 1) % 3); } while (dwValue != MAGICVALUE_UDP_SYNC_CLIENT && byTries > 0); // try to decrypt as ed2k as well as kad packet if needed (max 3 rounds) if (dwValue == MAGICVALUE_UDP_SYNC_CLIENT) { // yup this is an encrypted packet // debugoutput notices // the following cases are "allowed" but shouldn't happen given that there is only our implementation yet if (bKad && (pbyBufIn[0] & 0x01) != 0) { MpdUtilities.DebugLog( string.Format("Received obfuscated UDP packet from clientIP: {0} with wrong key marker bits (kad packet, ed2k bit)", MpdUtilities.IP2String(dwIP))); } else if (bKad && !bKadRecvKeyUsed && (pbyBufIn[0] & 0x02) != 0) { MpdUtilities.DebugLog( string.Format("Received obfuscated UDP packet from clientIP: {0} with wrong key marker bits (kad packet, nodeid key, recvkey bit)", MpdUtilities.IP2String(dwIP))); } else if (bKad && bKadRecvKeyUsed && (pbyBufIn[0] & 0x02) == 0) { MpdUtilities.DebugLog( string.Format("Received obfuscated UDP packet from clientIP: {0} with wrong key marker bits (kad packet, recvkey key, nodeid bit)", MpdUtilities.IP2String(dwIP))); } byte byPadLen; byte[] outBuf = new byte[1]; MuleUtilities.RC4Crypt(pbyBufIn, 7, outBuf, 0, 1, keyReceiveKey); byPadLen = outBuf[0]; nResult -= CRYPT_HEADER_WITHOUTPADDING; if (nResult <= byPadLen) { MpdUtilities.DebugLogError( string.Format("Invalid obfuscated UDP packet from clientIP: {0}, Paddingsize ({1}) larger than received bytes", MpdUtilities.IP2String(dwIP), byPadLen)); return(nBufLen); // pass through, let the Receivefunction do the errorhandling on this junk } if (byPadLen > 0) { MuleUtilities.RC4Crypt(null, null, byPadLen, keyReceiveKey); } nResult -= byPadLen; if (bKad) { if (nResult <= 8) { MpdUtilities.DebugLogError( string.Format("Obfuscated Kad packet with mismatching size (verify keys missing) received from clientIP: {0}", MpdUtilities.IP2String(dwIP))); return(nBufLen); // pass through, let the Receivefunction do the errorhandling on this junk; } // read the verify keys outBuf = new byte[4]; MuleUtilities.RC4Crypt(pbyBufIn, (int)(CRYPT_HEADER_WITHOUTPADDING + byPadLen), outBuf, 0, 4, keyReceiveKey); nReceiverVerifyKey = BitConverter.ToUInt32(outBuf, 0); MuleUtilities.RC4Crypt(pbyBufIn, (int)(CRYPT_HEADER_WITHOUTPADDING + byPadLen + 4), outBuf, 0, 4, keyReceiveKey); nSenderVerifyKey = BitConverter.ToUInt32(outBuf, 0); nResult -= 8; } ppbyBufOut = new byte[nResult]; Array.Copy(pbyBufIn, (nBufLen - nResult), ppbyBufOut, 0, nResult); MuleUtilities.RC4Crypt(ppbyBufOut, ppbyBufOut, (uint)nResult, keyReceiveKey); MuleApplication.Instance.Statistics.AddDownDataOverheadCrypt((uint)(nBufLen - nResult)); //DEBUG_ONLY( MpdUtilities.DebugLog(("Received obfuscated UDP packet from clientIP: %s, Key: %s, RKey: %u, SKey: %u"), MpdUtilities.IP2String(dwIP), bKad ? (bKadRecvKeyUsed ? ("ReceiverKey") : ("NodeID")) : ("UserHash") // , nReceiverVerifyKey != 0 ? *nReceiverVerifyKey : 0, nSenderVerifyKey != 0 ? *nSenderVerifyKey : 0) ); return(nResult); // done } else { MpdUtilities.DebugLogWarning( string.Format("Obfuscated packet expected but magicvalue mismatch on UDP packet from clientIP: {0}, Possible RecvKey: {1}", MpdUtilities.IP2String(dwIP), MuleApplication.Instance.KadEngine.Preference.GetUDPVerifyKey(dwIP))); return(nBufLen); // pass through, let the Receivefunction do the errorhandling on this junk } }
public override int Receive(byte[] lpBuf, int offset, int nBufLen, SocketFlags nFlags) { obfuscationBytesReceived_ = base.Receive(lpBuf, offset, nBufLen, nFlags); fullReceive_ = obfuscationBytesReceived_ == (uint)nBufLen; if (obfuscationBytesReceived_ == SOCKET_ERROR || obfuscationBytesReceived_ <= 0) { return(obfuscationBytesReceived_); } switch (streamCryptState_) { case StreamCryptStateEnum.ECS_NONE: // disabled, just pass it through return(obfuscationBytesReceived_); case StreamCryptStateEnum.ECS_PENDING: case StreamCryptStateEnum.ECS_PENDING_SERVER: Debug.Assert(false); MpdUtilities.DebugLogError(("CEncryptedStreamSocket Received data before sending on outgoing connection")); streamCryptState_ = StreamCryptStateEnum.ECS_NONE; return(obfuscationBytesReceived_); case StreamCryptStateEnum.ECS_UNKNOWN: { int nRead = 1; bool bNormalHeader = false; switch (lpBuf[offset]) { case MuleConstants.PROTOCOL_EDONKEYPROT: case MuleConstants.PROTOCOL_PACKEDPROT: case MuleConstants.PROTOCOL_EMULEPROT: bNormalHeader = true; break; } if (!bNormalHeader) { StartNegotiation(false); int nNegRes = Negotiate(lpBuf, offset + nRead, obfuscationBytesReceived_ - nRead); if (nNegRes == (-1)) { return(0); } nRead += nNegRes; if (nRead != obfuscationBytesReceived_) { // this means we have more data then the current negotiation step required (or there is a bug) and this should never happen // (note: even if it just finished the handshake here, there still can be no data left, since the other client didnt received our response yet) MpdUtilities.DebugLogError(("CEncryptedStreamSocket: Client %s sent more data then expected while negotiating, disconnecting (1)"), DbgGetIPString()); OnError(Convert.ToInt32(EMSocketErrorCodeEnum.ERR_ENCRYPTION)); } return(0); } else { // doesn't seems to be encrypted streamCryptState_ = StreamCryptStateEnum.ECS_NONE; // if we require an encrypted connection, cut the connection here. This shouldn't happen that often // at least with other up-to-date eMule clients because they check for incompability before connecting if possible if (MuleApplication.Instance.Preference.IsClientCryptLayerRequired) { // TODO: Remove me when i have been solved // Even if the Require option is enabled, we currently have to accept unencrypted connection which are made // for lowid/firewall checks from servers and other from us selected client. Otherwise, this option would // always result in a lowid/firewalled status. This is of course not nice, but we can't avoid this walkarround // untill servers and kad completely support encryption too, which will at least for kad take a bit // only exception is the .ini option ClientCryptLayerRequiredStrict which will even ignore test connections // Update: New server now support encrypted callbacks IPEndPoint remote = RemoteEndPoint as IPEndPoint; uint address = BitConverter.ToUInt32(remote.Address.GetAddressBytes(), 0); if (MuleApplication.Instance.Preference.IsClientCryptLayerRequiredStrict || (!MuleApplication.Instance.ServerConnect.AwaitingTestFromIP(address) && !MuleApplication.Instance.ClientList.IsKadFirewallCheckIP(address))) { MpdUtilities.AddDebugLogLine(EDebugLogPriority.DLP_DEFAULT, false, ("Rejected incoming connection because Obfuscation was required but not used %s"), DbgGetIPString()); OnError(Convert.ToInt32(EMSocketErrorCodeEnum.ERR_ENCRYPTION_NOTALLOWED)); return(0); } else { MpdUtilities.AddDebugLogLine(EDebugLogPriority.DLP_DEFAULT, false, ("Incoming unencrypted firewallcheck connection permitted despite RequireEncryption setting - %s"), DbgGetIPString()); } } return(obfuscationBytesReceived_); // buffer was unchanged, we can just pass it through } } case StreamCryptStateEnum.ECS_ENCRYPTING: // basic obfuscation enabled and set, so decrypt and pass along MuleUtilities.RC4Crypt(lpBuf, offset, lpBuf, offset, Convert.ToUInt32(obfuscationBytesReceived_), rc4ReceiveKey_); return(obfuscationBytesReceived_); case StreamCryptStateEnum.ECS_NEGOTIATING: { int nRead = Negotiate(lpBuf, offset, obfuscationBytesReceived_); if (nRead == (-1)) { return(0); } else if (nRead != obfuscationBytesReceived_ && streamCryptState_ != StreamCryptStateEnum.ECS_ENCRYPTING) { // this means we have more data then the current negotiation step required (or there is a bug) and this should never happen MpdUtilities.DebugLogError(("CEncryptedStreamSocket: Client %s sent more data then expected while negotiating, disconnecting (2)"), DbgGetIPString()); OnError(Convert.ToInt32(EMSocketErrorCodeEnum.ERR_ENCRYPTION)); return(0); } else if (nRead != (uint)obfuscationBytesReceived_ && streamCryptState_ == StreamCryptStateEnum.ECS_ENCRYPTING) { // we finished the handshake and if we this was an outgoing connection it is allowed (but strange and unlikely) that the client sent payload MpdUtilities.DebugLogWarning(("CEncryptedStreamSocket: Client %s has finished the handshake but also sent payload on a outgoing connection"), DbgGetIPString()); Array.Copy(lpBuf, offset + nRead, lpBuf, offset + 0, obfuscationBytesReceived_ - nRead); return(obfuscationBytesReceived_ - nRead); } else { return(0); } } default: Debug.Assert(false); return(obfuscationBytesReceived_); } }