// // This file is part of the aMule Project. // // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org ) // Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net ) // // Any parts of this program derived from the xMule, lMule or eMule project, // or contributed by third-party developers are copyrighted by their // respective authors. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA // #include // Needed for GetDataDir #include #include "IPFilter.h" // Interface declarations. #include "IPFilterScanner.h" // Interface for flexer #include "Preferences.h" // Needed for thePrefs #include "amule.h" // Needed for theApp #include "Statistics.h" // Needed for theStats #include "HTTPDownload.h" // Needed for CHTTPDownloadThread #include "Logger.h" // Needed for AddDebugLogLine{C,N} #include // Needed for CFormat #include // Needed for CSimpleTokenizer #include // Needed for UnpackArchive #include // Needed for CTextFile #include "ThreadScheduler.h" // Needed for CThreadScheduler and CThreadTask #include "ClientList.h" // Needed for CClientList #include "ServerList.h" // Needed for CServerList #include // Needed for DEBUG_ONLY() #include "RangeMap.h" // Needed for CRangeMap #include "ServerConnect.h" // Needed for ConnectToAnyServer() #include "DownloadQueue.h" // Needed for theApp->downloadqueue //////////////////////////////////////////////////////////// // CIPFilterEvent BEGIN_DECLARE_EVENT_TYPES() DECLARE_EVENT_TYPE(MULE_EVT_IPFILTER_LOADED, -1) END_DECLARE_EVENT_TYPES() DEFINE_EVENT_TYPE(MULE_EVT_IPFILTER_LOADED) class CIPFilterEvent : public wxEvent { public: CIPFilterEvent(CIPFilter::RangeIPs rangeIPs, CIPFilter::RangeLengths rangeLengths, CIPFilter::RangeNames rangeNames) : wxEvent(-1, MULE_EVT_IPFILTER_LOADED) { // Physically copy the vectors, this will hopefully resize them back to their needed capacity. m_rangeIPs = rangeIPs; m_rangeLengths = rangeLengths; // This one is usually empty, and should always be swapped, not copied. std::swap(m_rangeNames, rangeNames); } /** @see wxEvent::Clone */ virtual wxEvent* Clone() const { return new CIPFilterEvent(*this); } CIPFilter::RangeIPs m_rangeIPs; CIPFilter::RangeLengths m_rangeLengths; CIPFilter::RangeNames m_rangeNames; }; typedef void (wxEvtHandler::*MuleIPFilterEventFunction)(CIPFilterEvent&); //! Event-handler for completed hashings of new shared files and partfiles. #define EVT_MULE_IPFILTER_LOADED(func) \ DECLARE_EVENT_TABLE_ENTRY(MULE_EVT_IPFILTER_LOADED, -1, -1, \ (wxObjectEventFunction) (wxEventFunction) \ wxStaticCastEvent(MuleIPFilterEventFunction, &func), (wxObject*) NULL), //////////////////////////////////////////////////////////// // Thread task for loading the ipfilter.dat files. /** * This task loads the two ipfilter.dat files, a task that * can take quite a while on a slow system with a large dat- * file. */ class CIPFilterTask : public CThreadTask { public: CIPFilterTask(wxEvtHandler* owner) : CThreadTask(wxT("Load IPFilter"), wxEmptyString, ETP_Critical), m_storeDescriptions(false), m_owner(owner) { } private: void Entry() { AddLogLineN(_("Loading IP filters 'ipfilter.dat' and 'ipfilter_static.dat'.")); if ( !LoadFromFile(theApp->ConfigDir + wxT("ipfilter.dat")) && thePrefs::UseIPFilterSystem() ) { // Load from system wide IP filter file wxStandardPathsBase &spb(wxStandardPaths::Get()); #ifdef __WXMSW__ wxString dataDir(spb.GetPluginsDir()); #elif defined(__WXMAC__) wxString dataDir(spb.GetDataDir()); #else wxString dataDir(spb.GetDataDir().BeforeLast(wxT('/')) + wxT("/amule")); #endif wxString systemwideFile(JoinPaths(dataDir,wxT("ipfilter.dat"))); LoadFromFile(systemwideFile); } LoadFromFile(theApp->ConfigDir + wxT("ipfilter_static.dat")); uint8 accessLevel = thePrefs::GetIPFilterLevel(); uint32 size = m_result.size(); // Reserve a little more so we don't have to resize the vector later. // (Map ranges can exist that have to be stored in several parts.) // Extra memory will be freed in the end. m_rangeIPs.reserve(size + 1000); m_rangeLengths.reserve(size + 1000); if (m_storeDescriptions) { m_rangeNames.reserve(size + 1000); } for (IPMap::iterator it = m_result.begin(); it != m_result.end(); ++it) { if (it->AccessLevel < accessLevel) { // Calculate range "length" // (which is included-end - start and thus length - 1) // Encoding: // 0 - 0x7fff same // 0x8000 - 0xffff 0xfff - 0x07ffffff // that means: remove msb, shift left by 12 bit, add 0xfff // so it can cover 8 consecutive class A nets // larger ranges (or theoretical ranges with uneven ends) have to be split uint32 startIP = it.keyStart(); uint32 realLength = it.keyEnd() - it.keyStart() + 1; #ifdef __DEBUG__ std::string * descp = 0; #endif while (realLength) { m_rangeIPs.push_back(startIP); uint32 curLength = realLength; uint16 pushLength; if (realLength <= 0x8000) { pushLength = realLength - 1; } else { if (curLength >= 0x08000000) { // range to big, limit curLength = 0x08000000; } else { // cut off LSBs curLength &= 0x07FFF000; } pushLength = ((curLength - 1) >> 12) | 0x8000; } m_rangeLengths.push_back(pushLength); #ifdef __DEBUG__ if (m_storeDescriptions) { // std::string has no ref counting, so swap it // (it's used so we need half the space than wxString with wide chars) if (descp) { // we split the range so we have to duplicate it m_rangeNames.push_back(*descp); } else { // push back empty string and swap m_rangeNames.push_back(std::string()); descp = & * m_rangeNames.rbegin(); std::swap(*descp, it->Description); } } #endif realLength -= curLength; startIP += curLength; } } } // Numbers are probably different: // - ranges from map that are not blocked because of their level are not added to the table // - some ranges from the map have to be split for the table AddDebugLogLineN(logIPFilter, CFormat(wxT("Ranges in map: %d blocked ranges in table: %d")) % size % m_rangeIPs.size()); CIPFilterEvent evt(m_rangeIPs, m_rangeLengths, m_rangeNames); wxPostEvent(m_owner, evt); } /** * This structure is used to contain the range-data in the rangemap. */ struct rangeObject { bool operator==( const rangeObject& other ) const { return AccessLevel == other.AccessLevel; } // Since descriptions are only used for debugging messages, there // is no need to keep them in memory when running a non-debug build. #ifdef __DEBUG__ //! Contains the user-description of the range. std::string Description; #endif //! The AccessLevel for this filter. uint8 AccessLevel; }; //! The is the type of map used to store the IPs. typedef CRangeMap IPMap; bool m_storeDescriptions; // the generated filter CIPFilter::RangeIPs m_rangeIPs; CIPFilter::RangeLengths m_rangeLengths; CIPFilter::RangeNames m_rangeNames; wxEvtHandler* m_owner; // temporary map for filter generation IPMap m_result; /** * Helper function. * * @param IPstart The start of the IP-range. * @param IPend The end of the IP-range, must be less than or equal to IPstart. * @param AccessLevel The AccessLevel of this range. * @param Description The assosiated description of this range. * @return true if the range was added, false if it was discarded. * * This function inserts the specified range into the IPMap. Invalid * ranges where the AccessLevel is not within the range 0..255, or * where IPEnd < IPstart not inserted. */ bool AddIPRange(uint32 IPStart, uint32 IPEnd, uint16 AccessLevel, const char* DEBUG_ONLY(Description)) { if (AccessLevel < 256) { if (IPStart <= IPEnd) { rangeObject item; item.AccessLevel = AccessLevel; #ifdef __DEBUG__ if (m_storeDescriptions) { item.Description = Description; } #endif m_result.insert(IPStart, IPEnd, item); return true; } } return false; } /** * Loads a IP-list from the specified file, can be text or zip. * * @return True if the file was loaded, false otherwise. **/ int LoadFromFile(const wxString& file) { const CPath path = CPath(file); if (!path.FileExists() || TestDestroy()) { return 0; } #ifdef __DEBUG__ m_storeDescriptions = theLogger.IsEnabled(logIPFilter); #endif const wxChar* ipfilter_files[] = { wxT("ipfilter.dat"), wxT("guardian.p2p"), wxT("guarding.p2p"), NULL }; // Try to unpack the file, might be an archive if (UnpackArchive(path, ipfilter_files).second != EFT_Text) { AddLogLineC(CFormat(_("Failed to load ipfilter.dat file '%s', unknown format encountered.")) % file); return 0; } int filtercount = 0; yyip_Bad = 0; wxFFile readFile; if (readFile.Open(path.GetRaw())) { yyip_Line = 1; yyiprestart(readFile.fp()); uint32 IPStart = 0; uint32 IPEnd = 0; uint32 IPAccessLevel = 0; char * IPDescription; #ifdef __DEBUG__ uint32 time1 = GetTickCountFullRes(); #endif while (yyiplex(IPStart, IPEnd, IPAccessLevel, IPDescription)) { AddIPRange(IPStart, IPEnd, IPAccessLevel, IPDescription); filtercount++; } #ifdef __DEBUG__ uint32 time2 = GetTickCountFullRes(); AddDebugLogLineN(logIPFilter, CFormat(wxT("time for lexer: %.3f")) % ((time2-time1) / 1000.0)); #endif } else { AddLogLineC(CFormat(_("Failed to load ipfilter.dat file '%s', could not open file.")) % file); return 0; } wxString msg = CFormat(wxPLURAL("Loaded %u IP-range from '%s'.", "Loaded %u IP-ranges from '%s'.", filtercount)) % filtercount % file; if (yyip_Bad) { msg << wxT(" ") << ( CFormat(wxPLURAL("%u malformed line was discarded.", "%u malformed lines were discarded.", yyip_Bad)) % yyip_Bad ); } AddLogLineN(msg); return filtercount; } }; //////////////////////////////////////////////////////////// // CIPFilter BEGIN_EVENT_TABLE(CIPFilter, wxEvtHandler) EVT_MULE_IPFILTER_LOADED(CIPFilter::OnIPFilterEvent) END_EVENT_TABLE() /** * This function creates a text-file containing the specified text, * but only if the file does not already exist. */ static bool CreateDummyFile(const wxString& filename, const wxString& text) { // Create template files if (!wxFileExists(filename)) { CTextFile file; if (file.Open(filename, CTextFile::write)) { file.WriteLine(text); return true; } } return false; } CIPFilter::CIPFilter() : m_ready(false), m_startKADWhenReady(false), m_connectToAnyServerWhenReady(false) { // Setup dummy files for the curious user. const wxString normalDat = theApp->ConfigDir + wxT("ipfilter.dat"); const wxString normalMsg = wxString() << wxT("# This file is used by aMule to store ipfilter lists downloaded\n") << wxT("# through the auto-update functionality. Do not save ipfilter-\n") << wxT("# ranges here that should not be overwritten by aMule.\n"); if (CreateDummyFile(normalDat, normalMsg)) { // redownload if user deleted file thePrefs::SetLastHTTPDownloadURL(HTTP_IPFilter, wxEmptyString); } const wxString staticDat = theApp->ConfigDir + wxT("ipfilter_static.dat"); const wxString staticMsg = wxString() << wxT("# This file is used to store ipfilter-ranges that should\n") << wxT("# not be overwritten by aMule. If you wish to keep a custom\n") << wxT("# set of ipfilter-ranges that take precedence over ipfilter-\n") << wxT("# ranges aquired through the auto-update functionality, then\n") << wxT("# place them in this file. aMule will not change this file."); CreateDummyFile(staticDat, staticMsg); // First load currently available filter, so network connect is possible right after // (in case filter download takes some time). Reload(); // Check if update should be done only after that. m_updateAfterLoading = thePrefs::IPFilterAutoLoad() && !thePrefs::IPFilterURL().IsEmpty(); } void CIPFilter::Reload() { // We keep the current filter till the new one has been loaded. CThreadScheduler::AddTask(new CIPFilterTask(this)); } uint32 CIPFilter::BanCount() const { wxMutexLocker lock(m_mutex); return m_rangeIPs.size(); } bool CIPFilter::IsFiltered(uint32 IPTest, bool isServer) { if ((!thePrefs::IsFilteringClients() && !isServer) || (!thePrefs::IsFilteringServers() && isServer)) { return false; } if (!m_ready) { // Somebody connected before we even started the networks. // Filter is not up yet, so block him. AddDebugLogLineN(logIPFilter, CFormat(wxT("Filtered IP %s because filter isn't ready yet.")) % Uint32toStringIP(IPTest)); if (isServer) { theStats::AddFilteredServer(); } else { theStats::AddFilteredClient(); } return true; } wxMutexLocker lock(m_mutex); // The IP needs to be in host order uint32 ip = wxUINT32_SWAP_ALWAYS(IPTest); int imin = 0; int imax = m_rangeIPs.size() - 1; int i; bool found = false; while (imin <= imax) { i = (imin + imax) / 2; uint32 curIP = m_rangeIPs[i]; if (curIP <= ip) { uint32 curLength = m_rangeLengths[i]; if (curLength >= 0x8000) { curLength = ((curLength & 0x7fff) << 12) + 0xfff; } if (curIP + curLength >= ip) { found = true; break; } } if (curIP > ip) { imax = i - 1; } else { imin = i + 1; } } if (found) { AddDebugLogLineN(logIPFilter, CFormat(wxT("Filtered IP %s%s")) % Uint32toStringIP(IPTest) % (i < (int)m_rangeNames.size() ? (wxT(" (") + wxString(char2unicode(m_rangeNames[i].c_str())) + wxT(")")) : wxString(wxEmptyString))); if (isServer) { theStats::AddFilteredServer(); } else { theStats::AddFilteredClient(); } return true; } return false; } void CIPFilter::Update(const wxString& strURL) { if (!strURL.IsEmpty()) { m_URL = strURL; wxString filename = theApp->ConfigDir + wxT("ipfilter.download"); wxString oldfilename = theApp->ConfigDir + wxT("ipfilter.dat"); CHTTPDownloadThread *downloader = new CHTTPDownloadThread(m_URL, filename, oldfilename, HTTP_IPFilter, true, true); downloader->Create(); downloader->Run(); } } void CIPFilter::DownloadFinished(uint32 result) { wxString datName = wxT("ipfilter.dat"); if (result == HTTP_Success) { // download succeeded. proceed with ipfilter loading wxString newDat = theApp->ConfigDir + wxT("ipfilter.download"); wxString oldDat = theApp->ConfigDir + datName; if (wxFileExists(oldDat) && !wxRemoveFile(oldDat)) { AddLogLineC(CFormat(_("Failed to remove %s file, aborting update.")) % datName); result = HTTP_Error; } else if (!wxRenameFile(newDat, oldDat)) { AddLogLineC(CFormat(_("Failed to rename new %s file, aborting update.")) % datName); result = HTTP_Error; } else { AddLogLineN(CFormat(_("Successfully updated %s")) % datName); } } else if (result == HTTP_Skipped) { AddLogLineN(CFormat(_("Skipped download of %s, because requested file is not newer.")) % datName); } else { AddLogLineC(CFormat(_("Failed to download %s from %s")) % datName % m_URL); } if (result == HTTP_Success) { // Reload both ipfilter files on success Reload(); } } void CIPFilter::OnIPFilterEvent(CIPFilterEvent& evt) { { wxMutexLocker lock(m_mutex); std::swap(m_rangeIPs, evt.m_rangeIPs); std::swap(m_rangeLengths, evt.m_rangeLengths); std::swap(m_rangeNames, evt.m_rangeNames); m_ready = true; } if (theApp->IsOnShutDown()) { return; } AddLogLineN(_("IP filter is ready")); if (thePrefs::IsFilteringClients()) { theApp->clientlist->FilterQueues(); } if (thePrefs::IsFilteringServers()) { theApp->serverlist->FilterServers(); } // Now start networks we didn't start earlier if (m_connectToAnyServerWhenReady || m_startKADWhenReady) { AddLogLineC(_("Connecting")); } if (m_connectToAnyServerWhenReady) { m_connectToAnyServerWhenReady = false; theApp->serverconnect->ConnectToAnyServer(); } if (m_startKADWhenReady) { m_startKADWhenReady = false; theApp->StartKad(); } theApp->ShowConnectionState(true); // update connect button if (thePrefs::GetSrcSeedsOn()) { theApp->downloadqueue->LoadSourceSeeds(); } // Trigger filter update if configured if (m_updateAfterLoading) { m_updateAfterLoading = false; Update(thePrefs::IPFilterURL()); } } // File_checked_for_headers