Upstream tarball 10086
[amule.git] / src / IPFilter.cpp
blob7937b87e8fbd7b64f14d0032cf9bbf99ddafeba7
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()
46 ////////////////////////////////////////////////////////////
47 // CIPFilterEvent
49 BEGIN_DECLARE_EVENT_TYPES()
50 DECLARE_EVENT_TYPE(MULE_EVT_IPFILTER_LOADED, -1)
51 END_DECLARE_EVENT_TYPES()
53 DEFINE_EVENT_TYPE(MULE_EVT_IPFILTER_LOADED)
56 class CIPFilterEvent : public wxEvent
58 public:
59 CIPFilterEvent(CIPFilter::RangeIPs rangeIPs, CIPFilter::RangeLengths rangeLengths, CIPFilter::RangeNames rangeNames)
60 : wxEvent(-1, MULE_EVT_IPFILTER_LOADED)
62 // Physically copy the vectors, this will hopefully resize them back to their needed capacity.
63 m_rangeIPs = rangeIPs;
64 m_rangeLengths = rangeLengths;
65 // This one is usually empty, and should always be swapped, not copied.
66 std::swap(m_rangeNames, rangeNames);
69 /** @see wxEvent::Clone */
70 virtual wxEvent* Clone() const {
71 return new CIPFilterEvent(*this);
74 CIPFilter::RangeIPs m_rangeIPs;
75 CIPFilter::RangeLengths m_rangeLengths;
76 CIPFilter::RangeNames m_rangeNames;
80 typedef void (wxEvtHandler::*MuleIPFilterEventFunction)(CIPFilterEvent&);
82 //! Event-handler for completed hashings of new shared files and partfiles.
83 #define EVT_MULE_IPFILTER_LOADED(func) \
84 DECLARE_EVENT_TABLE_ENTRY(MULE_EVT_IPFILTER_LOADED, -1, -1, \
85 (wxObjectEventFunction) (wxEventFunction) \
86 wxStaticCastEvent(MuleIPFilterEventFunction, &func), (wxObject*) NULL),
89 ////////////////////////////////////////////////////////////
90 // Thread task for loading the ipfilter.dat files.
92 /**
93 * This task loads the two ipfilter.dat files, a task that
94 * can take quite a while on a slow system with a large dat-
95 * file.
97 class CIPFilterTask : public CThreadTask
99 public:
100 CIPFilterTask(wxEvtHandler* owner)
101 : CThreadTask(wxT("Load IPFilter"), wxEmptyString, ETP_Critical),
102 m_storeDescriptions(false),
103 m_owner(owner)
107 private:
108 void Entry() {
109 wxStandardPathsBase &spb(wxStandardPaths::Get());
110 #ifdef __WXMSW__
111 wxString dataDir(spb.GetPluginsDir());
112 #elif defined(__WXMAC__)
113 wxString dataDir(spb.GetDataDir());
114 #else
115 wxString dataDir(spb.GetDataDir().BeforeLast(wxT('/')) + wxT("/amule"));
116 #endif
117 wxString systemwideFile(JoinPaths(dataDir,wxT("ipfilter.dat")));
119 AddLogLineN(_("Loading IP filters 'ipfilter.dat' and 'ipfilter_static.dat'."));
120 if ( !LoadFromFile(theApp->ConfigDir + wxT("ipfilter.dat")) &&
121 thePrefs::UseIPFilterSystem() ) {
122 LoadFromFile(systemwideFile);
126 LoadFromFile(theApp->ConfigDir + wxT("ipfilter_static.dat"));
128 uint8 accessLevel = thePrefs::GetIPFilterLevel();
129 uint32 size = m_result.size();
130 // Reserve a little more so we don't have to resize the vector later.
131 // (Map ranges can exist that have to be stored in several parts.)
132 // Extra memory will be freed in the end.
133 m_rangeIPs.reserve(size + 1000);
134 m_rangeLengths.reserve(size + 1000);
135 if (m_storeDescriptions) {
136 m_rangeNames.reserve(size + 1000);
138 for (IPMap::iterator it = m_result.begin(); it != m_result.end(); ++it) {
139 if (it->AccessLevel < accessLevel) {
140 // Calculate range "length"
141 // (which is included-end - start and thus length - 1)
142 // Encoding:
143 // 0 - 0x7fff same
144 // 0x8000 - 0xffff 0xfff - 0x07ffffff
145 // that means: remove msb, shift left by 12 bit, add 0xfff
146 // so it can cover 8 consecutive class A nets
147 // larger ranges (or theoretical ranges with uneven ends) have to be split
148 uint32 startIP = it.keyStart();
149 uint32 realLength = it.keyEnd() - it.keyStart() + 1;
150 while (realLength) {
151 m_rangeIPs.push_back(startIP);
152 uint32 curLength = realLength;
153 uint16 pushLength;
154 if (realLength <= 0x8000) {
155 pushLength = realLength - 1;
156 } else {
157 if (curLength >= 0x08000000) {
158 // range to big, limit
159 curLength = 0x08000000;
160 } else {
161 // cut off LSBs
162 curLength &= 0x07FFF000;
164 pushLength = ((curLength - 1) >> 12) | 0x8000;
166 m_rangeLengths.push_back(pushLength);
167 #ifdef __DEBUG__
168 if (m_storeDescriptions) {
169 m_rangeNames.push_back(it->Description);
171 #endif
172 realLength -= curLength;
173 if (realLength) {
174 AddDebugLogLineN(logIPFilter, CFormat(wxT("Split range %s - %s %04X"))
175 % KadIPToString(startIP) % KadIPToString(it.keyEnd()) % realLength );
177 startIP += curLength;
181 AddDebugLogLineN(logIPFilter, CFormat(wxT("Ranges in map: %d blocked ranges %d")) % size % m_rangeIPs.size());
183 CIPFilterEvent evt(m_rangeIPs, m_rangeLengths, m_rangeNames);
184 wxPostEvent(m_owner, evt);
188 * This structure is used to contain the range-data in the rangemap.
190 struct rangeObject
192 bool operator==( const rangeObject& other ) const {
193 return AccessLevel == other.AccessLevel;
196 // Since descriptions are only used for debugging messages, there
197 // is no need to keep them in memory when running a non-debug build.
198 #ifdef __DEBUG__
199 //! Contains the user-description of the range.
200 wxString Description;
201 #endif
203 //! The AccessLevel for this filter.
204 uint8 AccessLevel;
207 //! The is the type of map used to store the IPs.
208 typedef CRangeMap<rangeObject, uint32> IPMap;
210 bool m_storeDescriptions;
212 // the generated filter
213 CIPFilter::RangeIPs m_rangeIPs;
214 CIPFilter::RangeLengths m_rangeLengths;
215 CIPFilter::RangeNames m_rangeNames;
217 wxEvtHandler* m_owner;
218 // temporary map for filter generation
219 IPMap m_result;
222 * Helper function.
224 * @param IPstart The start of the IP-range.
225 * @param IPend The end of the IP-range, must be less than or equal to IPstart.
226 * @param AccessLevel The AccessLevel of this range.
227 * @param Description The assosiated description of this range.
228 * @return true if the range was added, false if it was discarded.
230 * This function inserts the specified range into the IPMap. Invalid
231 * ranges where the AccessLevel is not within the range 0..255, or
232 * where IPEnd < IPstart not inserted.
234 bool AddIPRange(uint32 IPStart, uint32 IPEnd, uint16 AccessLevel, const wxString& DEBUG_ONLY(Description))
236 if (AccessLevel < 256) {
237 if (IPStart <= IPEnd) {
238 rangeObject item;
239 item.AccessLevel = AccessLevel;
240 #ifdef __DEBUG__
241 if (m_storeDescriptions) {
242 item.Description = Description;
244 #endif
246 m_result.insert(IPStart, IPEnd, item);
248 return true;
252 return false;
257 * Helper function.
259 * @param str A string representation of an IP-range in the format "<ip>-<ip>".
260 * @param ipA The target of the first IP in the range.
261 * @param ipB The target of the second IP in the range.
262 * @return True if the parsing succeded, false otherwise (results will be invalid).
264 * The IPs returned by this function are in host order, not network order.
266 bool m_inet_atoh(const wxString &str, uint32& ipA, uint32& ipB)
268 wxString first = str.BeforeFirst(wxT('-'));
269 wxString second = str.Mid(first.Len() + 1);
271 bool result = StringIPtoUint32(first, ipA) && StringIPtoUint32(second, ipB);
273 // StringIPtoUint32 saves the ip in anti-host order, but in order
274 // to be able to make relational comparisons, we need to convert
275 // it back to host-order.
276 ipA = wxUINT32_SWAP_ALWAYS(ipA);
277 ipB = wxUINT32_SWAP_ALWAYS(ipB);
279 return result;
284 * Helper-function for processing the PeerGuardian format.
286 * @return True if the line was valid, false otherwise.
288 * This function will correctly parse files that follow the folllowing
289 * format for specifying IP-ranges (whitespace is optional):
290 * <IPStart> - <IPEnd> , <AccessLevel> , <Description>
292 bool ProcessPeerGuardianLine(const wxString& sLine)
294 CSimpleTokenizer tkz(sLine, wxT(','));
296 wxString first = tkz.next();
297 wxString second = tkz.next();
298 wxString third = tkz.remaining().Strip(wxString::both);
300 // If there were less than two tokens, fail
301 if (tkz.tokenCount() != 2) {
302 return false;
305 // Convert string IP's to host order IP numbers
306 uint32 IPStart = 0;
307 uint32 IPEnd = 0;
309 // This will also fail if the line is commented out
310 if (!m_inet_atoh(first, IPStart, IPEnd)) {
311 return false;
314 // Second token is Access Level, default is 0.
315 unsigned long AccessLevel = 0;
316 if (!second.Strip(wxString::both).ToULong(&AccessLevel) || AccessLevel >= 255) {
317 return false;
320 // Add the filter
321 return AddIPRange(IPStart, IPEnd, AccessLevel, third);
326 * Helper-function for processing the AntiP2P format.
328 * @return True if the line was valid, false otherwise.
330 * This function will correctly parse files that follow the folllowing
331 * format for specifying IP-ranges (whitespace is optional):
332 * <Description> : <IPStart> - <IPEnd>
334 bool ProcessAntiP2PLine(const wxString& sLine)
336 // remove spaces from the left and right.
337 const wxString line = sLine.Strip(wxString::leading);
339 // Extract description (first) and IP-range (second) form the line
340 int pos = line.Find(wxT(':'), true);
341 if (pos == -1) {
342 return false;
345 wxString Description = line.Left(pos).Strip(wxString::trailing);
346 wxString IPRange = line.Right(line.Len() - pos - 1);
348 // Convert string IP's to host order IP numbers
349 uint32 IPStart = 0;
350 uint32 IPEnd = 0;
352 if (!m_inet_atoh(IPRange ,IPStart, IPEnd)) {
353 return false;
356 // Add the filter
357 return AddIPRange(IPStart, IPEnd, 0, Description);
362 * Loads a IP-list from the specified file, can be text or zip.
364 * @return True if the file was loaded, false otherwise.
366 int LoadFromFile(const wxString& file)
368 const CPath path = CPath(file);
370 if (!path.FileExists() || TestDestroy()) {
371 return 0;
374 #ifdef __DEBUG__
375 m_storeDescriptions = theLogger.IsEnabled(logIPFilter);
376 #endif
378 const wxChar* ipfilter_files[] = {
379 wxT("ipfilter.dat"),
380 wxT("guardian.p2p"),
381 wxT("guarding.p2p"),
382 NULL
385 // Try to unpack the file, might be an archive
387 if (UnpackArchive(path, ipfilter_files).second != EFT_Text) {
388 AddLogLineM(true,
389 CFormat(_("Failed to load ipfilter.dat file '%s', unknown format encountered.")) % file);
390 return 0;
393 int filtercount = 0;
394 int discardedCount = 0;
396 CTextFile readFile;
397 if (readFile.Open(path, CTextFile::read)) {
398 // Function pointer-type of the parse-functions we can use
399 typedef bool (CIPFilterTask::*ParseFunc)(const wxString&);
401 ParseFunc func = NULL;
403 while (!readFile.Eof()) {
404 wxString line = readFile.GetNextLine();
406 if (TestDestroy()) {
407 return 0;
408 } else if (func && (*this.*func)(line)) {
409 filtercount++;
410 } else if (ProcessPeerGuardianLine(line)) {
411 func = &CIPFilterTask::ProcessPeerGuardianLine;
412 filtercount++;
413 } else if (ProcessAntiP2PLine(line)) {
414 func = &CIPFilterTask::ProcessAntiP2PLine;
415 filtercount++;
416 } else {
417 // Comments and empty lines are ignored
418 line = line.Strip(wxString::both);
420 if (!line.IsEmpty() && !line.StartsWith(wxT("#"))) {
421 discardedCount++;
422 AddDebugLogLineM(false, logIPFilter, wxT("Invalid line found while reading ipfilter file: ") + line);
426 } else {
427 AddLogLineM(true, CFormat(_("Failed to load ipfilter.dat file '%s', could not open file.")) % file);
428 return 0;
431 AddLogLineM(false,
432 ( CFormat(wxPLURAL("Loaded %u IP-range from '%s'.", "Loaded %u IP-ranges from '%s'.", filtercount)) % filtercount % file )
433 + wxT(" ") +
434 ( CFormat(wxPLURAL("%u malformed line was discarded.", "%u malformed lines were discarded.", discardedCount)) % discardedCount )
437 return filtercount;
442 ////////////////////////////////////////////////////////////
443 // CIPFilter
446 BEGIN_EVENT_TABLE(CIPFilter, wxEvtHandler)
447 EVT_MULE_IPFILTER_LOADED(CIPFilter::OnIPFilterEvent)
448 END_EVENT_TABLE()
453 * This function creates a text-file containing the specified text,
454 * but only if the file does not already exist.
456 void CreateDummyFile(const wxString& filename, const wxString& text)
458 // Create template files
459 if (!wxFileExists(filename)) {
460 CTextFile file;
462 if (file.Open(filename, CTextFile::write)) {
463 file.WriteLine(text);
469 CIPFilter::CIPFilter() :
470 m_ready(false),
471 m_startKADWhenReady(false),
472 m_connectToAnyServerWhenReady(false)
475 // Setup dummy files for the curious user.
476 const wxString normalDat = theApp->ConfigDir + wxT("ipfilter.dat");
477 const wxString normalMsg = wxString()
478 << wxT("# This file is used by aMule to store ipfilter lists downloaded\n")
479 << wxT("# through the auto-update functionality. Do not save ipfilter-\n")
480 << wxT("# ranges here that should not be overwritten by aMule.\n");
482 CreateDummyFile(normalDat, normalMsg);
484 const wxString staticDat = theApp->ConfigDir + wxT("ipfilter_static.dat");
485 const wxString staticMsg = wxString()
486 << wxT("# This file is used to store ipfilter-ranges that should\n")
487 << wxT("# not be overwritten by aMule. If you wish to keep a custom\n")
488 << wxT("# set of ipfilter-ranges that take precedence over ipfilter-\n")
489 << wxT("# ranges aquired through the auto-update functionality, then\n")
490 << wxT("# place them in this file. aMule will not change this file.");
492 CreateDummyFile(staticDat, staticMsg);
494 if (thePrefs::IPFilterAutoLoad() && !thePrefs::IPFilterURL().IsEmpty()) {
495 Update(thePrefs::IPFilterURL());
496 } else {
497 Reload();
502 void CIPFilter::Reload()
504 // We keep the current filter till the new one has been loaded.
505 CThreadScheduler::AddTask(new CIPFilterTask(this));
509 uint32 CIPFilter::BanCount() const
511 wxMutexLocker lock(m_mutex);
513 return m_rangeIPs.size();
517 bool CIPFilter::IsFiltered(uint32 IPTest, bool isServer)
519 if ((!thePrefs::IsFilteringClients() && !isServer) || (!thePrefs::IsFilteringServers() && isServer)) {
520 return false;
522 if (!m_ready) {
523 // Somebody connected before we even started the networks.
524 // Filter is not up yet, so block him.
525 AddDebugLogLineN(logIPFilter, CFormat(wxT("Filtered IP %s because filter isn't ready yet.")) % Uint32toStringIP(IPTest));
526 return true;
528 wxMutexLocker lock(m_mutex);
529 // The IP needs to be in host order
530 uint32 ip = wxUINT32_SWAP_ALWAYS(IPTest);
531 int imin = 0;
532 int imax = m_rangeIPs.size() - 1;
533 int i;
534 bool found = false;
535 while (imin <= imax) {
536 i = (imin + imax) / 2;
537 uint32 curIP = m_rangeIPs[i];
538 if (curIP <= ip) {
539 uint32 curLength = m_rangeLengths[i];
540 if (curLength >= 0x8000) {
541 curLength = ((curLength & 0x7fff) << 12) + 0xfff;
543 if (curIP + curLength >= ip) {
544 found = true;
545 break;
548 if (curIP > ip) {
549 imax = i - 1;
550 } else {
551 imin = i + 1;
554 if (found) {
555 AddDebugLogLineN(logIPFilter, CFormat(wxT("Filtered IP %s%s")) % Uint32toStringIP(IPTest)
556 % (i < (int)m_rangeNames.size() ? (wxT(" (") + m_rangeNames[i] + wxT(")")) : wxString(wxEmptyString)));
557 if (isServer) {
558 theStats::AddFilteredServer();
559 } else {
560 theStats::AddFilteredClient();
562 return true;
564 return false;
568 void CIPFilter::Update(const wxString& strURL)
570 if (!strURL.IsEmpty()) {
571 m_URL = strURL;
573 wxString filename = theApp->ConfigDir + wxT("ipfilter.download");
574 CHTTPDownloadThread *downloader = new CHTTPDownloadThread(m_URL, filename, theApp->ConfigDir + wxT("ipfilter.dat"), HTTP_IPFilter);
576 downloader->Create();
577 downloader->Run();
582 void CIPFilter::DownloadFinished(uint32 result)
584 if (result == HTTP_Success) {
585 // download succeeded. proceed with ipfilter loading
586 wxString newDat = theApp->ConfigDir + wxT("ipfilter.download");
587 wxString oldDat = theApp->ConfigDir + wxT("ipfilter.dat");
589 if (wxFileExists(oldDat)) {
590 if (!wxRemoveFile(oldDat)) {
591 AddLogLineC(CFormat(_("Failed to remove %s file, aborting update.")) % wxT("ipfilter.dat"));
592 return;
596 if (!wxRenameFile(newDat, oldDat)) {
597 AddLogLineC(CFormat(_("Failed to rename new %s file, aborting update.")) % wxT("ipfilter.dat"));
598 return;
601 AddLogLineN(CFormat(_("Successfully updated %s")) % wxT("ipfilter.dat"));
602 } else if (result == HTTP_Skipped) {
603 AddLogLineN(CFormat(_("Skipped download of %s, because requested file is not newer.")) % wxT("ipfilter.dat"));
604 } else {
605 AddLogLineC(CFormat(_("Failed to download %s from %s")) % wxT("ipfilter.dat") % m_URL);
608 // reload on success, or if we reloaded on startup and download failed
609 if (result == HTTP_Success || !m_ready) {
610 // Reload both ipfilter files
611 Reload();
616 void CIPFilter::OnIPFilterEvent(CIPFilterEvent& evt)
619 wxMutexLocker lock(m_mutex);
620 std::swap(m_rangeIPs, evt.m_rangeIPs);
621 std::swap(m_rangeLengths, evt.m_rangeLengths);
622 std::swap(m_rangeNames, evt.m_rangeNames);
623 m_ready = true;
625 if (theApp->IsOnShutDown()) {
626 return;
628 AddLogLineN(_("IP filter is ready"));
630 if (thePrefs::IsFilteringClients()) {
631 theApp->clientlist->FilterQueues();
633 if (thePrefs::IsFilteringServers()) {
634 theApp->serverlist->FilterServers();
636 // Now start networks we didn't start earlier
637 if (m_connectToAnyServerWhenReady || m_startKADWhenReady) {
638 AddLogLineC(_("Connecting"));
640 if (m_connectToAnyServerWhenReady) {
641 m_connectToAnyServerWhenReady = false;
642 theApp->serverconnect->ConnectToAnyServer();
644 if (m_startKADWhenReady) {
645 m_startKADWhenReady = false;
646 theApp->StartKad();
650 // File_checked_for_headers