Upstream tarball 10170
[amule.git] / src / IPFilter.cpp
blobbad25315c3bd3c2c29df3e58ba01e44cded591c8
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2008 Merkur ( devs@emule-project.net / http://www.emule-project.net )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include <wx/stdpaths.h> // Needed for GetDataDir
28 #include "IPFilter.h" // Interface declarations.
29 #include "Preferences.h" // Needed for thePrefs
30 #include "amule.h" // Needed for theApp
31 #include "Statistics.h" // Needed for theStats
32 #include "HTTPDownload.h" // Needed for CHTTPDownloadThread
33 #include "Logger.h" // Needed for AddDebugLogLineM
34 #include <common/Format.h> // Needed for CFormat
35 #include <common/StringFunctions.h> // Needed for CSimpleTokenizer
36 #include <common/FileFunctions.h> // Needed for UnpackArchive
37 #include <common/TextFile.h> // Needed for CTextFile
38 #include "ThreadScheduler.h" // Needed for CThreadScheduler and CThreadTask
39 #include "ClientList.h" // Needed for CClientList
40 #include "ServerList.h" // Needed for CServerList
41 #include <common/Macros.h> // Needed for DEBUG_ONLY()
42 #include "RangeMap.h" // Needed for CRangeMap
43 #include "ServerConnect.h" // Needed for ConnectToAnyServer()
44 #include "DownloadQueue.h" // Needed for theApp->downloadqueue
47 ////////////////////////////////////////////////////////////
48 // CIPFilterEvent
50 BEGIN_DECLARE_EVENT_TYPES()
51 DECLARE_EVENT_TYPE(MULE_EVT_IPFILTER_LOADED, -1)
52 END_DECLARE_EVENT_TYPES()
54 DEFINE_EVENT_TYPE(MULE_EVT_IPFILTER_LOADED)
57 class CIPFilterEvent : public wxEvent
59 public:
60 CIPFilterEvent(CIPFilter::RangeIPs rangeIPs, CIPFilter::RangeLengths rangeLengths, CIPFilter::RangeNames rangeNames)
61 : wxEvent(-1, MULE_EVT_IPFILTER_LOADED)
63 // Physically copy the vectors, this will hopefully resize them back to their needed capacity.
64 m_rangeIPs = rangeIPs;
65 m_rangeLengths = rangeLengths;
66 // This one is usually empty, and should always be swapped, not copied.
67 std::swap(m_rangeNames, rangeNames);
70 /** @see wxEvent::Clone */
71 virtual wxEvent* Clone() const {
72 return new CIPFilterEvent(*this);
75 CIPFilter::RangeIPs m_rangeIPs;
76 CIPFilter::RangeLengths m_rangeLengths;
77 CIPFilter::RangeNames m_rangeNames;
81 typedef void (wxEvtHandler::*MuleIPFilterEventFunction)(CIPFilterEvent&);
83 //! Event-handler for completed hashings of new shared files and partfiles.
84 #define EVT_MULE_IPFILTER_LOADED(func) \
85 DECLARE_EVENT_TABLE_ENTRY(MULE_EVT_IPFILTER_LOADED, -1, -1, \
86 (wxObjectEventFunction) (wxEventFunction) \
87 wxStaticCastEvent(MuleIPFilterEventFunction, &func), (wxObject*) NULL),
90 ////////////////////////////////////////////////////////////
91 // Thread task for loading the ipfilter.dat files.
93 /**
94 * This task loads the two ipfilter.dat files, a task that
95 * can take quite a while on a slow system with a large dat-
96 * file.
98 class CIPFilterTask : public CThreadTask
100 public:
101 CIPFilterTask(wxEvtHandler* owner, bool block = false)
102 : CThreadTask(wxT("Load IPFilter"), wxEmptyString, ETP_Critical),
103 m_storeDescriptions(false),
104 m_owner(owner)
106 if (block) {
107 LockBlockDuringUpdate();
111 void LockBlockDuringUpdate() { m_blockDuringUpdate.Lock(); }
112 void UnlockBlockDuringUpdate() { m_blockDuringUpdate.Unlock(); }
114 private:
115 void Entry()
117 // Block thread while there is still an update going on
118 LockBlockDuringUpdate();
119 AddLogLineN(_("Loading IP filters 'ipfilter.dat' and 'ipfilter_static.dat'."));
120 if ( !LoadFromFile(theApp->ConfigDir + wxT("ipfilter.dat")) &&
121 thePrefs::UseIPFilterSystem() ) {
122 // Load from system wide IP filter file
123 wxStandardPathsBase &spb(wxStandardPaths::Get());
124 #ifdef __WXMSW__
125 wxString dataDir(spb.GetPluginsDir());
126 #elif defined(__WXMAC__)
127 wxString dataDir(spb.GetDataDir());
128 #else
129 wxString dataDir(spb.GetDataDir().BeforeLast(wxT('/')) + wxT("/amule"));
130 #endif
131 wxString systemwideFile(JoinPaths(dataDir,wxT("ipfilter.dat")));
132 LoadFromFile(systemwideFile);
136 LoadFromFile(theApp->ConfigDir + wxT("ipfilter_static.dat"));
138 uint8 accessLevel = thePrefs::GetIPFilterLevel();
139 uint32 size = m_result.size();
140 // Reserve a little more so we don't have to resize the vector later.
141 // (Map ranges can exist that have to be stored in several parts.)
142 // Extra memory will be freed in the end.
143 m_rangeIPs.reserve(size + 1000);
144 m_rangeLengths.reserve(size + 1000);
145 if (m_storeDescriptions) {
146 m_rangeNames.reserve(size + 1000);
148 for (IPMap::iterator it = m_result.begin(); it != m_result.end(); ++it) {
149 if (it->AccessLevel < accessLevel) {
150 // Calculate range "length"
151 // (which is included-end - start and thus length - 1)
152 // Encoding:
153 // 0 - 0x7fff same
154 // 0x8000 - 0xffff 0xfff - 0x07ffffff
155 // that means: remove msb, shift left by 12 bit, add 0xfff
156 // so it can cover 8 consecutive class A nets
157 // larger ranges (or theoretical ranges with uneven ends) have to be split
158 uint32 startIP = it.keyStart();
159 uint32 realLength = it.keyEnd() - it.keyStart() + 1;
160 while (realLength) {
161 m_rangeIPs.push_back(startIP);
162 uint32 curLength = realLength;
163 uint16 pushLength;
164 if (realLength <= 0x8000) {
165 pushLength = realLength - 1;
166 } else {
167 if (curLength >= 0x08000000) {
168 // range to big, limit
169 curLength = 0x08000000;
170 } else {
171 // cut off LSBs
172 curLength &= 0x07FFF000;
174 pushLength = ((curLength - 1) >> 12) | 0x8000;
176 m_rangeLengths.push_back(pushLength);
177 #ifdef __DEBUG__
178 if (m_storeDescriptions) {
179 m_rangeNames.push_back(it->Description);
181 #endif
182 realLength -= curLength;
183 if (realLength) {
184 AddDebugLogLineN(logIPFilter, CFormat(wxT("Split range %s - %s %04X"))
185 % KadIPToString(startIP) % KadIPToString(it.keyEnd()) % realLength );
187 startIP += curLength;
191 AddDebugLogLineN(logIPFilter, CFormat(wxT("Ranges in map: %d blocked ranges %d")) % size % m_rangeIPs.size());
193 CIPFilterEvent evt(m_rangeIPs, m_rangeLengths, m_rangeNames);
194 wxPostEvent(m_owner, evt);
198 * This structure is used to contain the range-data in the rangemap.
200 struct rangeObject
202 bool operator==( const rangeObject& other ) const {
203 return AccessLevel == other.AccessLevel;
206 // Since descriptions are only used for debugging messages, there
207 // is no need to keep them in memory when running a non-debug build.
208 #ifdef __DEBUG__
209 //! Contains the user-description of the range.
210 wxString Description;
211 #endif
213 //! The AccessLevel for this filter.
214 uint8 AccessLevel;
217 //! The is the type of map used to store the IPs.
218 typedef CRangeMap<rangeObject, uint32> IPMap;
220 bool m_storeDescriptions;
222 // the generated filter
223 CIPFilter::RangeIPs m_rangeIPs;
224 CIPFilter::RangeLengths m_rangeLengths;
225 CIPFilter::RangeNames m_rangeNames;
227 wxEvtHandler* m_owner;
228 // temporary map for filter generation
229 IPMap m_result;
230 // Mutex to block thread during active update
231 wxMutex m_blockDuringUpdate;
234 * Helper function.
236 * @param IPstart The start of the IP-range.
237 * @param IPend The end of the IP-range, must be less than or equal to IPstart.
238 * @param AccessLevel The AccessLevel of this range.
239 * @param Description The assosiated description of this range.
240 * @return true if the range was added, false if it was discarded.
242 * This function inserts the specified range into the IPMap. Invalid
243 * ranges where the AccessLevel is not within the range 0..255, or
244 * where IPEnd < IPstart not inserted.
246 bool AddIPRange(uint32 IPStart, uint32 IPEnd, uint16 AccessLevel, const wxString& DEBUG_ONLY(Description))
248 if (AccessLevel < 256) {
249 if (IPStart <= IPEnd) {
250 rangeObject item;
251 item.AccessLevel = AccessLevel;
252 #ifdef __DEBUG__
253 if (m_storeDescriptions) {
254 item.Description = Description;
256 #endif
258 m_result.insert(IPStart, IPEnd, item);
260 return true;
264 return false;
269 * Helper function.
271 * @param str A string representation of an IP-range in the format "<ip>-<ip>".
272 * @param ipA The target of the first IP in the range.
273 * @param ipB The target of the second IP in the range.
274 * @return True if the parsing succeded, false otherwise (results will be invalid).
276 * The IPs returned by this function are in host order, not network order.
278 bool ReadIPRange(const wxString &str, uint32& ipA, uint32& ipB)
280 wxString first = str.BeforeFirst(wxT('-'));
281 wxString second = str.Mid(first.Len() + 1);
283 bool result = StringIPtoUint32(first, ipA) && StringIPtoUint32(second, ipB);
285 // StringIPtoUint32 saves the ip in anti-host order, but in order
286 // to be able to make relational comparisons, we need to convert
287 // it back to host-order.
288 ipA = wxUINT32_SWAP_ALWAYS(ipA);
289 ipB = wxUINT32_SWAP_ALWAYS(ipB);
291 return result;
296 * Helper-function for processing the PeerGuardian format.
298 * @return True if the line was valid, false otherwise.
300 * This function will correctly parse files that follow the folllowing
301 * format for specifying IP-ranges (whitespace is optional):
302 * <IPStart> - <IPEnd> , <AccessLevel> , <Description>
304 bool ProcessPeerGuardianLine(const wxString& sLine)
306 CSimpleTokenizer tkz(sLine, wxT(','));
308 wxString first = tkz.next();
309 wxString second = tkz.next();
310 wxString third = tkz.remaining().Strip(wxString::both);
312 // If there were less than two tokens, fail
313 if (tkz.tokenCount() != 2) {
314 return false;
317 // Convert string IP's to host order IP numbers
318 uint32 IPStart = 0;
319 uint32 IPEnd = 0;
321 // This will also fail if the line is commented out
322 if (!ReadIPRange(first, IPStart, IPEnd)) {
323 return false;
326 // Second token is Access Level, default is 0.
327 unsigned long AccessLevel = 0;
328 if (!second.Strip(wxString::both).ToULong(&AccessLevel) || AccessLevel >= 255) {
329 return false;
332 // Add the filter
333 return AddIPRange(IPStart, IPEnd, AccessLevel, third);
338 * Helper-function for processing the AntiP2P format.
340 * @return True if the line was valid, false otherwise.
342 * This function will correctly parse files that follow the folllowing
343 * format for specifying IP-ranges (whitespace is optional):
344 * <Description> : <IPStart> - <IPEnd>
346 bool ProcessAntiP2PLine(const wxString& sLine)
348 // remove spaces from the left and right.
349 const wxString line = sLine.Strip(wxString::leading);
351 // Extract description (first) and IP-range (second) form the line
352 int pos = line.Find(wxT(':'), true);
353 if (pos == -1) {
354 return false;
357 wxString Description = line.Left(pos).Strip(wxString::trailing);
358 wxString IPRange = line.Right(line.Len() - pos - 1);
360 // Convert string IP's to host order IP numbers
361 uint32 IPStart = 0;
362 uint32 IPEnd = 0;
364 if (!ReadIPRange(IPRange ,IPStart, IPEnd)) {
365 return false;
368 // Add the filter
369 return AddIPRange(IPStart, IPEnd, 0, Description);
374 * Loads a IP-list from the specified file, can be text or zip.
376 * @return True if the file was loaded, false otherwise.
378 int LoadFromFile(const wxString& file)
380 const CPath path = CPath(file);
382 if (!path.FileExists() || TestDestroy()) {
383 return 0;
386 #ifdef __DEBUG__
387 m_storeDescriptions = theLogger.IsEnabled(logIPFilter);
388 #endif
390 const wxChar* ipfilter_files[] = {
391 wxT("ipfilter.dat"),
392 wxT("guardian.p2p"),
393 wxT("guarding.p2p"),
394 NULL
397 // Try to unpack the file, might be an archive
399 if (UnpackArchive(path, ipfilter_files).second != EFT_Text) {
400 AddLogLineM(true,
401 CFormat(_("Failed to load ipfilter.dat file '%s', unknown format encountered.")) % file);
402 return 0;
405 int filtercount = 0;
406 int discardedCount = 0;
408 CTextFile readFile;
409 if (readFile.Open(path, CTextFile::read)) {
410 // Function pointer-type of the parse-functions we can use
411 typedef bool (CIPFilterTask::*ParseFunc)(const wxString&);
413 ParseFunc func = NULL;
415 while (!readFile.Eof()) {
416 if (TestDestroy()) {
417 return 0;
420 wxString line = readFile.GetNextLine();
422 // Comments and empty lines are ignored as soon as possible
423 if (!line.IsEmpty() && !line.StartsWith(wxT("#"))) {
424 bool processed_ok = false;
426 if (func) {
427 processed_ok = (*this.*func)(line);
428 } else {
429 if (processed_ok = ProcessPeerGuardianLine(line)) {
430 func = &CIPFilterTask::ProcessPeerGuardianLine;
431 } else if (processed_ok = ProcessAntiP2PLine(line)) {
432 func = &CIPFilterTask::ProcessAntiP2PLine;
436 if (processed_ok) {
437 filtercount++;
438 } else {
439 // It may be a padded comment or line with just spaces.
440 line = line.Strip(wxString::both);
441 if (!line.IsEmpty() && !line.StartsWith(wxT("#"))) {
442 discardedCount++;
443 AddDebugLogLineM(false, logIPFilter, wxT("Invalid line found while reading ipfilter file: ") + line);
448 } else {
449 AddLogLineM(true, CFormat(_("Failed to load ipfilter.dat file '%s', could not open file.")) % file);
450 return 0;
453 AddLogLineM(false,
454 ( CFormat(wxPLURAL("Loaded %u IP-range from '%s'.", "Loaded %u IP-ranges from '%s'.", filtercount)) % filtercount % file )
455 + wxT(" ") +
456 ( CFormat(wxPLURAL("%u malformed line was discarded.", "%u malformed lines were discarded.", discardedCount)) % discardedCount )
459 return filtercount;
464 ////////////////////////////////////////////////////////////
465 // CIPFilter
468 BEGIN_EVENT_TABLE(CIPFilter, wxEvtHandler)
469 EVT_MULE_IPFILTER_LOADED(CIPFilter::OnIPFilterEvent)
470 END_EVENT_TABLE()
475 * This function creates a text-file containing the specified text,
476 * but only if the file does not already exist.
478 static bool CreateDummyFile(const wxString& filename, const wxString& text)
480 // Create template files
481 if (!wxFileExists(filename)) {
482 CTextFile file;
484 if (file.Open(filename, CTextFile::write)) {
485 file.WriteLine(text);
486 return true;
489 return false;
493 CIPFilter::CIPFilter() :
494 m_ready(false),
495 m_startKADWhenReady(false),
496 m_connectToAnyServerWhenReady(false),
497 m_ipFilterTask(NULL)
499 // Setup dummy files for the curious user.
500 const wxString normalDat = theApp->ConfigDir + wxT("ipfilter.dat");
501 const wxString normalMsg = wxString()
502 << wxT("# This file is used by aMule to store ipfilter lists downloaded\n")
503 << wxT("# through the auto-update functionality. Do not save ipfilter-\n")
504 << wxT("# ranges here that should not be overwritten by aMule.\n");
506 if (CreateDummyFile(normalDat, normalMsg)) {
507 // redownload if user deleted file
508 thePrefs::SetLastHTTPDownloadURL(HTTP_IPFilter, wxEmptyString);
511 const wxString staticDat = theApp->ConfigDir + wxT("ipfilter_static.dat");
512 const wxString staticMsg = wxString()
513 << wxT("# This file is used to store ipfilter-ranges that should\n")
514 << wxT("# not be overwritten by aMule. If you wish to keep a custom\n")
515 << wxT("# set of ipfilter-ranges that take precedence over ipfilter-\n")
516 << wxT("# ranges aquired through the auto-update functionality, then\n")
517 << wxT("# place them in this file. aMule will not change this file.");
519 CreateDummyFile(staticDat, staticMsg);
521 if (thePrefs::IPFilterAutoLoad() && !thePrefs::IPFilterURL().IsEmpty()) {
523 // We want to update the filter on startup.
524 // If we create a CThreadTask now it will be the first to be processed.
525 // But if we start a download thread first other thread tasks started meanwhile
526 // will be processed first, and can take a long time (like AICH hashing).
527 // During this time filter won't be active, and thus networks won't be started.
529 // So start a new task now and block it until update is finished.
530 // Then unblock it.
532 m_ipFilterTask = new CIPFilterTask(this, true);
533 CThreadScheduler::AddTask(m_ipFilterTask);
534 Update(thePrefs::IPFilterURL());
535 } else {
536 Reload();
541 void CIPFilter::Reload()
543 // We keep the current filter till the new one has been loaded.
544 CThreadScheduler::AddTask(new CIPFilterTask(this));
548 uint32 CIPFilter::BanCount() const
550 wxMutexLocker lock(m_mutex);
552 return m_rangeIPs.size();
556 bool CIPFilter::IsFiltered(uint32 IPTest, bool isServer)
558 if ((!thePrefs::IsFilteringClients() && !isServer) || (!thePrefs::IsFilteringServers() && isServer)) {
559 return false;
561 if (!m_ready) {
562 // Somebody connected before we even started the networks.
563 // Filter is not up yet, so block him.
564 AddDebugLogLineN(logIPFilter, CFormat(wxT("Filtered IP %s because filter isn't ready yet.")) % Uint32toStringIP(IPTest));
565 if (isServer) {
566 theStats::AddFilteredServer();
567 } else {
568 theStats::AddFilteredClient();
570 return true;
572 wxMutexLocker lock(m_mutex);
573 // The IP needs to be in host order
574 uint32 ip = wxUINT32_SWAP_ALWAYS(IPTest);
575 int imin = 0;
576 int imax = m_rangeIPs.size() - 1;
577 int i;
578 bool found = false;
579 while (imin <= imax) {
580 i = (imin + imax) / 2;
581 uint32 curIP = m_rangeIPs[i];
582 if (curIP <= ip) {
583 uint32 curLength = m_rangeLengths[i];
584 if (curLength >= 0x8000) {
585 curLength = ((curLength & 0x7fff) << 12) + 0xfff;
587 if (curIP + curLength >= ip) {
588 found = true;
589 break;
592 if (curIP > ip) {
593 imax = i - 1;
594 } else {
595 imin = i + 1;
598 if (found) {
599 AddDebugLogLineN(logIPFilter, CFormat(wxT("Filtered IP %s%s")) % Uint32toStringIP(IPTest)
600 % (i < (int)m_rangeNames.size() ? (wxT(" (") + m_rangeNames[i] + wxT(")")) : wxString(wxEmptyString)));
601 if (isServer) {
602 theStats::AddFilteredServer();
603 } else {
604 theStats::AddFilteredClient();
606 return true;
608 return false;
612 void CIPFilter::Update(const wxString& strURL)
614 if (!strURL.IsEmpty()) {
615 m_URL = strURL;
617 wxString filename = theApp->ConfigDir + wxT("ipfilter.download");
618 wxString oldfilename = theApp->ConfigDir + wxT("ipfilter.dat");
619 CHTTPDownloadThread *downloader = new CHTTPDownloadThread(m_URL, filename, oldfilename, HTTP_IPFilter);
621 downloader->Create();
622 downloader->Run();
627 void CIPFilter::DownloadFinished(uint32 result)
629 if (result == HTTP_Success) {
630 // download succeeded. proceed with ipfilter loading
631 wxString newDat = theApp->ConfigDir + wxT("ipfilter.download");
632 wxString oldDat = theApp->ConfigDir + wxT("ipfilter.dat");
634 if (wxFileExists(oldDat) && !wxRemoveFile(oldDat)) {
635 AddLogLineC(CFormat(_("Failed to remove %s file, aborting update.")) % wxT("ipfilter.dat"));
636 result = HTTP_Error;
637 } else if (!wxRenameFile(newDat, oldDat)) {
638 AddLogLineC(CFormat(_("Failed to rename new %s file, aborting update.")) % wxT("ipfilter.dat"));
639 result = HTTP_Error;
640 } else {
641 AddLogLineN(CFormat(_("Successfully updated %s")) % wxT("ipfilter.dat"));
643 } else if (result == HTTP_Skipped) {
644 AddLogLineN(CFormat(_("Skipped download of %s, because requested file is not newer.")) % wxT("ipfilter.dat"));
645 } else {
646 AddLogLineC(CFormat(_("Failed to download %s from %s")) % wxT("ipfilter.dat") % m_URL);
649 if (m_ipFilterTask) {
650 // If we updated during startup, there is already a task waiting to load the filter.
651 // Trigger it to continue.
652 m_ipFilterTask->UnlockBlockDuringUpdate();
653 m_ipFilterTask = NULL;
654 // reload on success, or if filter is not up yet (shouldn't happen)
655 } else if (result == HTTP_Success || !m_ready) {
656 // Reload both ipfilter files
657 Reload();
662 void CIPFilter::OnIPFilterEvent(CIPFilterEvent& evt)
665 wxMutexLocker lock(m_mutex);
666 std::swap(m_rangeIPs, evt.m_rangeIPs);
667 std::swap(m_rangeLengths, evt.m_rangeLengths);
668 std::swap(m_rangeNames, evt.m_rangeNames);
669 m_ready = true;
671 if (theApp->IsOnShutDown()) {
672 return;
674 AddLogLineN(_("IP filter is ready"));
676 if (thePrefs::IsFilteringClients()) {
677 theApp->clientlist->FilterQueues();
679 if (thePrefs::IsFilteringServers()) {
680 theApp->serverlist->FilterServers();
682 // Now start networks we didn't start earlier
683 if (m_connectToAnyServerWhenReady || m_startKADWhenReady) {
684 AddLogLineC(_("Connecting"));
686 if (m_connectToAnyServerWhenReady) {
687 m_connectToAnyServerWhenReady = false;
688 theApp->serverconnect->ConnectToAnyServer();
690 if (m_startKADWhenReady) {
691 m_startKADWhenReady = false;
692 theApp->StartKad();
694 if (thePrefs::GetSrcSeedsOn()) {
695 theApp->downloadqueue->LoadSourceSeeds();
699 // File_checked_for_headers