static HRESULT UnpackData(IInStream *inStream, const CResource &resource, bool lzxMode, CByteBuffer &buf, Byte *digest) { size_t size = (size_t)resource.UnpackSize; if (size != resource.UnpackSize) return E_OUTOFMEMORY; buf.Free(); buf.SetCapacity(size); CSequentialOutStreamImp2 *outStreamSpec = new CSequentialOutStreamImp2(); CMyComPtr<ISequentialOutStream> outStream = outStreamSpec; outStreamSpec->Init((Byte *)buf, size); CUnpacker unpacker; return unpacker.Unpack(inStream, resource, lzxMode, outStream, NULL, digest); }
void CServer::ProcessClientPacket(CNetChunk *pPacket) { int ClientID = pPacket->m_ClientID; CUnpacker Unpacker; Unpacker.Reset(pPacket->m_pData, pPacket->m_DataSize); // unpack msgid and system flag int Msg = Unpacker.GetInt(); int Sys = Msg&1; Msg >>= 1; if(Unpacker.Error()) return; if(Sys) { // system message if(Msg == NETMSG_INFO) { if(m_aClients[ClientID].m_State == CClient::STATE_AUTH) { char aVersion[64]; str_copy(aVersion, Unpacker.GetString(CUnpacker::SANITIZE_CC), 64); bool CustClt = str_comp(aVersion, GameServer()->NetVersionCust()) == 0; dbg_msg("es", "%s client connected!", CustClt?"cust":"vanilla"); if(!CustClt && str_comp(aVersion, GameServer()->NetVersion()) != 0) { // wrong version char aReason[256]; str_format(aReason, sizeof(aReason), "Wrong version. Server is running '%s' and client '%s'", GameServer()->NetVersion(), aVersion); m_NetServer.Drop(ClientID, aReason); return; } const char *pPassword = Unpacker.GetString(CUnpacker::SANITIZE_CC); if(g_Config.m_Password[0] != 0 && str_comp(g_Config.m_Password, pPassword) != 0) { // wrong password m_NetServer.Drop(ClientID, "Wrong password"); return; } m_aClients[ClientID].m_State = CClient::STATE_CONNECTING; m_aClients[ClientID].m_CustClt = CustClt; SendMap(ClientID); } } else if(Msg == NETMSG_REQUEST_MAP_DATA) { int Chunk = Unpacker.GetInt(); int ChunkSize = 1024-128; int Offset = Chunk * ChunkSize; int Last = 0; // drop faulty map data requests if(Chunk < 0 || Offset > m_CurrentMapSize) return; if(Offset+ChunkSize >= m_CurrentMapSize) { ChunkSize = m_CurrentMapSize-Offset; if(ChunkSize < 0) ChunkSize = 0; Last = 1; } CMsgPacker Msg(NETMSG_MAP_DATA); Msg.AddInt(Last); Msg.AddInt(m_CurrentMapCrc); Msg.AddInt(Chunk); Msg.AddInt(ChunkSize); Msg.AddRaw(&m_pCurrentMapData[Offset], ChunkSize); SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID, true); if(g_Config.m_Debug) { char aBuf[256]; str_format(aBuf, sizeof(aBuf), "sending chunk %d with size %d", Chunk, ChunkSize); Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "server", aBuf); } } else if(Msg == NETMSG_READY) { if(m_aClients[ClientID].m_State == CClient::STATE_CONNECTING) { char aAddrStr[NETADDR_MAXSTRSIZE]; net_addr_str(m_NetServer.ClientAddr(ClientID), aAddrStr, sizeof(aAddrStr), true); char aBuf[256]; str_format(aBuf, sizeof(aBuf), "player is ready. ClientID=%x addr=%s", ClientID, aAddrStr); Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf); m_aClients[ClientID].m_State = CClient::STATE_READY; GameServer()->OnClientConnected(ClientID); SendConnectionReady(ClientID); } } else if(Msg == NETMSG_ENTERGAME) { if(m_aClients[ClientID].m_State == CClient::STATE_READY && GameServer()->IsClientReady(ClientID)) { char aAddrStr[NETADDR_MAXSTRSIZE]; net_addr_str(m_NetServer.ClientAddr(ClientID), aAddrStr, sizeof(aAddrStr), true); char aBuf[256]; str_format(aBuf, sizeof(aBuf), "player has entered the game. ClientID=%x addr=%s", ClientID, aAddrStr); Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); m_aClients[ClientID].m_State = CClient::STATE_INGAME; GameServer()->OnClientEnter(ClientID); } } else if(Msg == NETMSG_INPUT) { CClient::CInput *pInput; int64 TagTime; m_aClients[ClientID].m_LastAckedSnapshot = Unpacker.GetInt(); int IntendedTick = Unpacker.GetInt(); int Size = Unpacker.GetInt(); // check for errors if(Unpacker.Error() || Size/4 > MAX_INPUT_SIZE) return; if(m_aClients[ClientID].m_LastAckedSnapshot > 0) m_aClients[ClientID].m_SnapRate = CClient::SNAPRATE_FULL; if(m_aClients[ClientID].m_Snapshots.Get(m_aClients[ClientID].m_LastAckedSnapshot, &TagTime, 0, 0) >= 0) m_aClients[ClientID].m_Latency = (int)(((time_get()-TagTime)*1000)/time_freq()); // add message to report the input timing // skip packets that are old if(IntendedTick > m_aClients[ClientID].m_LastInputTick) { int TimeLeft = ((TickStartTime(IntendedTick)-time_get())*1000) / time_freq(); CMsgPacker Msg(NETMSG_INPUTTIMING); Msg.AddInt(IntendedTick); Msg.AddInt(TimeLeft); SendMsgEx(&Msg, 0, ClientID, true); } m_aClients[ClientID].m_LastInputTick = IntendedTick; pInput = &m_aClients[ClientID].m_aInputs[m_aClients[ClientID].m_CurrentInput]; if(IntendedTick <= Tick()) IntendedTick = Tick()+1; pInput->m_GameTick = IntendedTick; for(int i = 0; i < Size/4; i++) pInput->m_aData[i] = Unpacker.GetInt(); mem_copy(m_aClients[ClientID].m_LatestInput.m_aData, pInput->m_aData, MAX_INPUT_SIZE*sizeof(int)); m_aClients[ClientID].m_CurrentInput++; m_aClients[ClientID].m_CurrentInput %= 200; // call the mod with the fresh input data if(m_aClients[ClientID].m_State == CClient::STATE_INGAME) GameServer()->OnClientDirectInput(ClientID, m_aClients[ClientID].m_LatestInput.m_aData); } else if(Msg == NETMSG_RCON_CMD) { const char *pCmd = Unpacker.GetString(); if(Unpacker.Error() == 0 && m_aClients[ClientID].m_Authed) { char aBuf[256]; str_format(aBuf, sizeof(aBuf), "ClientID=%d rcon='%s'", ClientID, pCmd); Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf); m_RconClientID = ClientID; m_RconAuthLevel = m_aClients[ClientID].m_Authed; Console()->SetAccessLevel(m_aClients[ClientID].m_Authed == AUTHED_ADMIN ? IConsole::ACCESS_LEVEL_ADMIN : IConsole::ACCESS_LEVEL_MOD); Console()->ExecuteLineFlag(pCmd, CFGFLAG_SERVER); Console()->SetAccessLevel(IConsole::ACCESS_LEVEL_ADMIN); m_RconClientID = IServer::RCON_CID_SERV; m_RconAuthLevel = AUTHED_ADMIN; } } else if(Msg == NETMSG_RCON_AUTH) { const char *pPw; Unpacker.GetString(); // login name, not used pPw = Unpacker.GetString(CUnpacker::SANITIZE_CC); if(Unpacker.Error() == 0) { if(g_Config.m_SvRconPassword[0] == 0 && g_Config.m_SvRconModPassword[0] == 0) { SendRconLine(ClientID, "No rcon password set on server. Set sv_rcon_password and/or sv_rcon_mod_password to enable the remote console."); } else if(g_Config.m_SvRconPassword[0] && str_comp(pPw, g_Config.m_SvRconPassword) == 0) { CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS); Msg.AddInt(1); //authed Msg.AddInt(1); //cmdlist SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true); m_aClients[ClientID].m_Authed = AUTHED_ADMIN; int SendRconCmds = Unpacker.GetInt(); if(Unpacker.Error() == 0 && SendRconCmds) m_aClients[ClientID].m_pRconCmdToSend = Console()->FirstCommandInfo(IConsole::ACCESS_LEVEL_ADMIN, CFGFLAG_SERVER); SendRconLine(ClientID, "Admin authentication successful. Full remote console access granted."); char aBuf[256]; str_format(aBuf, sizeof(aBuf), "ClientID=%d authed (admin)", ClientID); Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); } else if(g_Config.m_SvRconModPassword[0] && str_comp(pPw, g_Config.m_SvRconModPassword) == 0) { CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS); Msg.AddInt(1); //authed Msg.AddInt(1); //cmdlist SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true); m_aClients[ClientID].m_Authed = AUTHED_MOD; int SendRconCmds = Unpacker.GetInt(); if(Unpacker.Error() == 0 && SendRconCmds) m_aClients[ClientID].m_pRconCmdToSend = Console()->FirstCommandInfo(IConsole::ACCESS_LEVEL_MOD, CFGFLAG_SERVER); SendRconLine(ClientID, "Moderator authentication successful. Limited remote console access granted."); char aBuf[256]; str_format(aBuf, sizeof(aBuf), "ClientID=%d authed (moderator)", ClientID); Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); } else if(g_Config.m_SvRconMaxTries) { m_aClients[ClientID].m_AuthTries++; char aBuf[128]; str_format(aBuf, sizeof(aBuf), "Wrong password %d/%d.", m_aClients[ClientID].m_AuthTries, g_Config.m_SvRconMaxTries); SendRconLine(ClientID, aBuf); if(m_aClients[ClientID].m_AuthTries >= g_Config.m_SvRconMaxTries) { if(!g_Config.m_SvRconBantime) m_NetServer.Drop(ClientID, "Too many remote console authentication tries"); else m_ServerBan.BanAddr(m_NetServer.ClientAddr(ClientID), g_Config.m_SvRconBantime*60, "Too many remote console authentication tries"); } } else { SendRconLine(ClientID, "Wrong password."); } } } else if(Msg == NETMSG_PING) { CMsgPacker Msg(NETMSG_PING_REPLY); SendMsgEx(&Msg, 0, ClientID, true); } else { if(g_Config.m_Debug) { char aHex[] = "0123456789ABCDEF"; char aBuf[512]; for(int b = 0; b < pPacket->m_DataSize && b < 32; b++) { aBuf[b*3] = aHex[((const unsigned char *)pPacket->m_pData)[b]>>4]; aBuf[b*3+1] = aHex[((const unsigned char *)pPacket->m_pData)[b]&0xf]; aBuf[b*3+2] = ' '; aBuf[b*3+3] = 0; } char aBufMsg[256]; str_format(aBufMsg, sizeof(aBufMsg), "strange message ClientID=%d msg=%d data_size=%d", ClientID, Msg, pPacket->m_DataSize); Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "server", aBufMsg); Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "server", aBuf); } } }
void CServer::ProcessClientPacket(CNetChunk *pPacket) { int ClientId = pPacket->m_ClientID; NETADDR Addr; CUnpacker Unpacker; Unpacker.Reset(pPacket->m_pData, pPacket->m_DataSize); // unpack msgid and system flag int Msg = Unpacker.GetInt(); int Sys = Msg&1; Msg >>= 1; if(Unpacker.Error()) return; if(m_aClients[ClientId].m_State == CClient::STATE_AUTH) { if(Sys && Msg == NETMSG_INFO) { char aVersion[64]; const char *pPassword; str_copy(aVersion, Unpacker.GetString(), 64); if(str_comp(aVersion, GameServer()->NetVersion()) != 0) { // OH F**K! wrong version, drop him char aReason[256]; str_format(aReason, sizeof(aReason), "wrong version. server is running '%s' and client '%s'.", GameServer()->NetVersion(), aVersion); m_NetServer.Drop(ClientId, aReason); return; } str_copy(m_aClients[ClientId].m_aName, Unpacker.GetString(), MAX_NAME_LENGTH); str_copy(m_aClients[ClientId].m_aClan, Unpacker.GetString(), MAX_CLANNAME_LENGTH); pPassword = Unpacker.GetString(); if(g_Config.m_Password[0] != 0 && str_comp(g_Config.m_Password, pPassword) != 0) { // wrong password m_NetServer.Drop(ClientId, "wrong password"); return; } m_aClients[ClientId].m_State = CClient::STATE_CONNECTING; SendMap(ClientId); } } else { if(Sys) { // system message if(Msg == NETMSG_REQUEST_MAP_DATA) { int Chunk = Unpacker.GetInt(); int ChunkSize = 1024-128; int Offset = Chunk * ChunkSize; int Last = 0; // drop faulty map data requests if(Chunk < 0 || Offset > m_CurrentMapSize) return; if(Offset+ChunkSize >= m_CurrentMapSize) { ChunkSize = m_CurrentMapSize-Offset; if(ChunkSize < 0) ChunkSize = 0; Last = 1; } CMsgPacker Msg(NETMSG_MAP_DATA); Msg.AddInt(Last); Msg.AddInt(m_CurrentMapSize); Msg.AddInt(ChunkSize); Msg.AddRaw(&m_pCurrentMapData[Offset], ChunkSize); SendMsgEx(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientId, true); if(g_Config.m_Debug) dbg_msg("server", "sending chunk %d with size %d", Chunk, ChunkSize); } else if(Msg == NETMSG_READY) { if(m_aClients[ClientId].m_State == CClient::STATE_CONNECTING) { Addr = m_NetServer.ClientAddr(ClientId); dbg_msg("server", "player is ready. ClientId=%x ip=%d.%d.%d.%d", ClientId, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]); m_aClients[ClientId].m_State = CClient::STATE_READY; GameServer()->OnClientConnected(ClientId); } } else if(Msg == NETMSG_ENTERGAME) { if(m_aClients[ClientId].m_State == CClient::STATE_READY) { Addr = m_NetServer.ClientAddr(ClientId); dbg_msg("server", "player has entered the game. ClientId=%x ip=%d.%d.%d.%d", ClientId, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]); m_aClients[ClientId].m_State = CClient::STATE_INGAME; GameServer()->OnClientEnter(ClientId); } } else if(Msg == NETMSG_INPUT) { CClient::CInput *pInput; int64 TagTime; m_aClients[ClientId].m_LastAckedSnapshot = Unpacker.GetInt(); int IntendedTick = Unpacker.GetInt(); int Size = Unpacker.GetInt(); // check for errors if(Unpacker.Error() || Size/4 > MAX_INPUT_SIZE) return; if(m_aClients[ClientId].m_LastAckedSnapshot > 0) m_aClients[ClientId].m_SnapRate = CClient::SNAPRATE_FULL; if(m_aClients[ClientId].m_Snapshots.Get(m_aClients[ClientId].m_LastAckedSnapshot, &TagTime, 0, 0) >= 0) m_aClients[ClientId].m_Latency = (int)(((time_get()-TagTime)*1000)/time_freq()); // add message to report the input timing // skip packets that are old if(IntendedTick > m_aClients[ClientId].m_LastInputTick) { int TimeLeft = ((TickStartTime(IntendedTick)-time_get())*1000) / time_freq(); CMsgPacker Msg(NETMSG_INPUTTIMING); Msg.AddInt(IntendedTick); Msg.AddInt(TimeLeft); SendMsgEx(&Msg, 0, ClientId, true); } m_aClients[ClientId].m_LastInputTick = IntendedTick; pInput = &m_aClients[ClientId].m_aInputs[m_aClients[ClientId].m_CurrentInput]; if(IntendedTick <= Tick()) IntendedTick = Tick()+1; pInput->m_GameTick = IntendedTick; for(int i = 0; i < Size/4; i++) pInput->m_aData[i] = Unpacker.GetInt(); mem_copy(m_aClients[ClientId].m_LatestInput.m_aData, pInput->m_aData, MAX_INPUT_SIZE*sizeof(int)); m_aClients[ClientId].m_CurrentInput++; m_aClients[ClientId].m_CurrentInput %= 200; // call the mod with the fresh input data if(m_aClients[ClientId].m_State == CClient::STATE_INGAME) GameServer()->OnClientDirectInput(ClientId, m_aClients[ClientId].m_LatestInput.m_aData); } else if(Msg == NETMSG_RCON_CMD) { const char *pCmd = Unpacker.GetString(); if(Unpacker.Error() == 0 && m_aClients[ClientId].m_Authed) { dbg_msg("server", "ClientId=%d rcon='%s'", ClientId, pCmd); Console()->ExecuteLine(pCmd); } } else if(Msg == NETMSG_RCON_AUTH) { const char *pPw; Unpacker.GetString(); // login name, not used pPw = Unpacker.GetString(); if(Unpacker.Error() == 0) { if(g_Config.m_SvRconPassword[0] == 0) { SendRconLine(ClientId, "No rcon password set on server. Set sv_rcon_password to enable the remote console."); } else if(str_comp(pPw, g_Config.m_SvRconPassword) == 0) { CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS); Msg.AddInt(1); SendMsgEx(&Msg, MSGFLAG_VITAL, ClientId, true); m_aClients[ClientId].m_Authed = 1; SendRconLine(ClientId, "Authentication successful. Remote console access granted."); dbg_msg("server", "ClientId=%d authed", ClientId); } else { SendRconLine(ClientId, "Wrong password."); } } } else if(Msg == NETMSG_PING) { CMsgPacker Msg(NETMSG_PING_REPLY); SendMsgEx(&Msg, 0, ClientId, true); } else { char aHex[] = "0123456789ABCDEF"; char aBuf[512]; for(int b = 0; b < pPacket->m_DataSize && b < 32; b++) { aBuf[b*3] = aHex[((const unsigned char *)pPacket->m_pData)[b]>>4]; aBuf[b*3+1] = aHex[((const unsigned char *)pPacket->m_pData)[b]&0xf]; aBuf[b*3+2] = ' '; aBuf[b*3+3] = 0; } dbg_msg("server", "strange message ClientId=%d msg=%d data_size=%d", ClientId, Msg, pPacket->m_DataSize); dbg_msg("server", "%s", aBuf); } } else { // game message if(m_aClients[ClientId].m_State >= CClient::STATE_READY) GameServer()->OnMessage(Msg, &Unpacker, ClientId); } }
// connection-less msg packet without token-support void CNetServer::OnPreConnMsg(NETADDR &Addr, CNetPacketConstruct &Packet) { bool IsCtrl = Packet.m_Flags&NET_PACKETFLAG_CONTROL; int CtrlMsg = m_RecvUnpacker.m_Data.m_aChunkData[0]; // log flooding //TODO: remove if (g_Config.m_Debug) { int64 Now = time_get(); if (Now - m_TimeNumConAttempts > time_freq()) // reset m_NumConAttempts = 0; m_NumConAttempts++; if (m_NumConAttempts > 100) { dbg_msg("security", "flooding detected"); m_TimeNumConAttempts = Now; m_NumConAttempts = 0; } } if (IsCtrl && CtrlMsg == NET_CTRLMSG_CONNECT) { if (g_Config.m_SvVanillaAntiSpoof && g_Config.m_Password[0] == '\0') { // detect flooding int64 Now = time_get(); if(Now <= m_VConnFirst + time_freq()) { m_VConnNum++; } else { m_VConnHighLoad = m_VConnNum > g_Config.m_SvVanConnPerSecond; m_VConnNum = 1; m_VConnFirst = Now; } bool Flooding = m_VConnNum > g_Config.m_SvVanConnPerSecond || m_VConnHighLoad; if (g_Config.m_Debug && Flooding) { dbg_msg("security", "vanilla connection flooding detected"); } // simulate accept SendControl(Addr, NET_CTRLMSG_CONNECTACCEPT, NULL, 0, NET_SECURITY_TOKEN_UNSUPPORTED); // Begin vanilla compatible token handshake // The idea is to pack a security token in the gametick // parameter of NETMSG_SNAPEMPTY. The Client then will // return the token/gametick in NETMSG_INPUT, allowing // us to validate the token. // https://github.com/eeeee/ddnet/commit/b8e40a244af4e242dc568aa34854c5754c75a39a // Before we can send NETMSG_SNAPEMPTY, the client needs // to load a map, otherwise it might crash. The map // should be as small as is possible and directly available // to the client. Therefore a dummy map is sent in the same // packet. To reduce the traffic we'll fallback to a default // map if there are too many connection attempts at once. // send mapchange + map data + con_ready + 3 x empty snap (with token) CMsgPacker MapChangeMsg(NETMSG_MAP_CHANGE); if (Flooding) { // Fallback to dm1 MapChangeMsg.AddString("dm1", 0); MapChangeMsg.AddInt(0xf2159e6e); MapChangeMsg.AddInt(5805); } else { // dummy map MapChangeMsg.AddString("dummy", 0); MapChangeMsg.AddInt(DummyMapCrc); MapChangeMsg.AddInt(sizeof(g_aDummyMapData)); } CMsgPacker MapDataMsg(NETMSG_MAP_DATA); if (Flooding) { // send empty map data to keep 0.6.4 support MapDataMsg.AddInt(1); // last chunk MapDataMsg.AddInt(0); // crc MapDataMsg.AddInt(0); // chunk index MapDataMsg.AddInt(0); // map size MapDataMsg.AddRaw(NULL, 0); // map data } else { // send dummy map data MapDataMsg.AddInt(1); // last chunk MapDataMsg.AddInt(DummyMapCrc); // crc MapDataMsg.AddInt(0); // chunk index MapDataMsg.AddInt(sizeof(g_aDummyMapData)); // map size MapDataMsg.AddRaw(g_aDummyMapData, sizeof(g_aDummyMapData)); // map data } CMsgPacker ConReadyMsg(NETMSG_CON_READY); CMsgPacker SnapEmptyMsg(NETMSG_SNAPEMPTY); SECURITY_TOKEN SecurityToken = GetVanillaToken(Addr); SnapEmptyMsg.AddInt((int)SecurityToken); SnapEmptyMsg.AddInt((int)SecurityToken + 1); // send all chunks/msgs in one packet const CMsgPacker *Msgs[] = {&MapChangeMsg, &MapDataMsg, &ConReadyMsg, &SnapEmptyMsg, &SnapEmptyMsg, &SnapEmptyMsg}; SendMsgs(Addr, Msgs, 6); } else { // accept client directly SendControl(Addr, NET_CTRLMSG_CONNECTACCEPT, NULL, 0, NET_SECURITY_TOKEN_UNSUPPORTED); TryAcceptClient(Addr, NET_SECURITY_TOKEN_UNSUPPORTED); } } else if(!IsCtrl && g_Config.m_SvVanillaAntiSpoof && g_Config.m_Password[0] == '\0') { CNetChunkHeader h; unsigned char *pData = Packet.m_aChunkData; pData = h.Unpack(pData); CUnpacker Unpacker; Unpacker.Reset(pData, h.m_Size); int Msg = Unpacker.GetInt() >> 1; if (Msg == NETMSG_INPUT) { SECURITY_TOKEN SecurityToken = Unpacker.GetInt(); if (SecurityToken == GetVanillaToken(Addr)) { if (g_Config.m_Debug) dbg_msg("security", "new client (vanilla handshake)"); // try to accept client skipping auth state TryAcceptClient(Addr, NET_SECURITY_TOKEN_UNSUPPORTED, true); } else if (g_Config.m_Debug) dbg_msg("security", "invalid token (vanilla handshake)"); } else { if (g_Config.m_Debug) { dbg_msg("security", "invalid preconn msg %d", Msg); } } }
STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback) { COM_TRY_BEGIN bool allFilesMode = (numItems == (UInt32)(Int32)-1); if (allFilesMode) numItems = _db.SortedItems.Size() + _numXmlItems + _db.VirtualRoots.Size() + _numIgnoreItems; if (numItems == 0) return S_OK; UInt32 i; UInt64 totalSize = 0; for (i = 0; i < numItems; i++) { UInt32 index = allFilesMode ? i : indices[i]; if (index < _db.SortedItems.Size()) { int streamIndex = _db.Items[_db.SortedItems[index]].StreamIndex; if (streamIndex >= 0) { const CStreamInfo &si = _db.DataStreams[streamIndex]; totalSize += si.Resource.UnpackSize; } } else { index -= _db.SortedItems.Size(); if (index < (UInt32)_numXmlItems) totalSize += _xmls[index].Data.Size(); } } RINOK(extractCallback->SetTotal(totalSize)); UInt64 currentTotalPacked = 0; UInt64 currentTotalUnPacked = 0; UInt64 currentItemUnPacked, currentItemPacked; int prevSuccessStreamIndex = -1; CUnpacker unpacker; CLocalProgress *lps = new CLocalProgress; CMyComPtr<ICompressProgressInfo> progress = lps; lps->Init(extractCallback, false); for (i = 0; i < numItems; currentTotalUnPacked += currentItemUnPacked, currentTotalPacked += currentItemPacked) { currentItemUnPacked = 0; currentItemPacked = 0; lps->InSize = currentTotalPacked; lps->OutSize = currentTotalUnPacked; RINOK(lps->SetCur()); UInt32 index = allFilesMode ? i : indices[i]; i++; Int32 askMode = testMode ? NExtract::NAskMode::kTest : NExtract::NAskMode::kExtract; CMyComPtr<ISequentialOutStream> realOutStream; RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); if (index >= _db.SortedItems.Size()) { if (!testMode && !realOutStream) continue; RINOK(extractCallback->PrepareOperation(askMode)); index -= _db.SortedItems.Size(); if (index < (UInt32)_numXmlItems) { const CByteBuffer &data = _xmls[index].Data; currentItemUnPacked = data.Size(); if (realOutStream) { RINOK(WriteStream(realOutStream, (const Byte *)data, data.Size())); realOutStream.Release(); } } RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); continue; } const CItem &item = _db.Items[_db.SortedItems[index]]; int streamIndex = item.StreamIndex; if (streamIndex < 0) { if (!testMode && !realOutStream) continue; RINOK(extractCallback->PrepareOperation(askMode)); realOutStream.Release(); RINOK(extractCallback->SetOperationResult(_db.ItemHasStream(item) ? NExtract::NOperationResult::kDataError : NExtract::NOperationResult::kOK)); continue; } const CStreamInfo &si = _db.DataStreams[streamIndex]; currentItemUnPacked = si.Resource.UnpackSize; currentItemPacked = si.Resource.PackSize; if (!testMode && !realOutStream) continue; RINOK(extractCallback->PrepareOperation(askMode)); Int32 opRes = NExtract::NOperationResult::kOK; if (streamIndex != prevSuccessStreamIndex || realOutStream) { Byte digest[kHashSize]; const CVolume &vol = _volumes[si.PartNumber]; HRESULT res = unpacker.Unpack(vol.Stream, si.Resource, vol.Header.IsLzxMode(), realOutStream, progress, digest); if (res == S_OK) { if (memcmp(digest, si.Hash, kHashSize) == 0) prevSuccessStreamIndex = streamIndex; else opRes = NExtract::NOperationResult::kCRCError; } else if (res == S_FALSE) opRes = NExtract::NOperationResult::kDataError; else return res; } realOutStream.Release(); RINOK(extractCallback->SetOperationResult(opRes)); } return S_OK; COM_TRY_END }