Upstream tarball 10059
[amule.git] / src / IPFilter.cpp
blob557e1348e945d170528b4cae330812d041d4563b
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()
44 ////////////////////////////////////////////////////////////
45 // CIPFilterEvent
47 BEGIN_DECLARE_EVENT_TYPES()
48 DECLARE_EVENT_TYPE(MULE_EVT_IPFILTER_LOADED, -1)
49 END_DECLARE_EVENT_TYPES()
51 DEFINE_EVENT_TYPE(MULE_EVT_IPFILTER_LOADED)
54 class CIPFilterEvent : public wxEvent
56 public:
57 CIPFilterEvent(CIPFilter::IPMap& result)
58 : wxEvent(-1, MULE_EVT_IPFILTER_LOADED)
60 // Avoid needles copying
61 std::swap(result, m_result);
64 /** @see wxEvent::Clone */
65 virtual wxEvent* Clone() const {
66 return new CIPFilterEvent(*this);
69 CIPFilter::IPMap m_result;
73 typedef void (wxEvtHandler::*MuleIPFilterEventFunction)(CIPFilterEvent&);
75 //! Event-handler for completed hashings of new shared files and partfiles.
76 #define EVT_MULE_IPFILTER_LOADED(func) \
77 DECLARE_EVENT_TABLE_ENTRY(MULE_EVT_IPFILTER_LOADED, -1, -1, \
78 (wxObjectEventFunction) (wxEventFunction) \
79 wxStaticCastEvent(MuleIPFilterEventFunction, &func), (wxObject*) NULL),
82 ////////////////////////////////////////////////////////////
83 // Thread task for loading the ipfilter.dat files.
85 /**
86 * This task loads the two ipfilter.dat files, a task that
87 * can take quite a while on a slow system with a large dat-
88 * file.
90 class CIPFilterTask : public CThreadTask
92 public:
93 CIPFilterTask(wxEvtHandler* owner)
94 : CThreadTask(wxT("Load IPFilter"), wxEmptyString, ETP_Critical),
95 m_storeDescriptions(false),
96 m_owner(owner)
100 void Entry() {
101 wxStandardPathsBase &spb(wxStandardPaths::Get());
102 #ifdef __WXMSW__
103 wxString dataDir(spb.GetPluginsDir());
104 #elif defined(__WXMAC__)
105 wxString dataDir(spb.GetDataDir());
106 #else
107 wxString dataDir(spb.GetDataDir().BeforeLast(wxT('/')) + wxT("/amule"));
108 #endif
109 wxString systemwideFile(JoinPaths(dataDir,wxT("ipfilter.dat")));
111 AddLogLineM(false, _("Loading IP-filters 'ipfilter.dat' and 'ipfilter_static.dat'."));
112 if ( !LoadFromFile(theApp->ConfigDir + wxT("ipfilter.dat")) &&
113 thePrefs::UseIPFilterSystem() ) {
114 LoadFromFile(systemwideFile);
118 LoadFromFile(theApp->ConfigDir + wxT("ipfilter_static.dat"));
120 CIPFilterEvent evt(m_result);
121 wxPostEvent(m_owner, evt);
123 private:
125 bool m_storeDescriptions;
128 * Helper function.
130 * @param IPstart The start of the IP-range.
131 * @param IPend The end of the IP-range, must be less than or equal to IPstart.
132 * @param AccessLevel The AccessLevel of this range.
133 * @param Description The assosiated description of this range.
134 * @return true if the range was added, false if it was discarded.
136 * This function inserts the specified range into the IPMap. Invalid
137 * ranges where the AccessLevel is not within the range 0..255, or
138 * where IPEnd < IPstart not inserted.
140 bool AddIPRange(uint32 IPStart, uint32 IPEnd, uint16 AccessLevel, const wxString& DEBUG_ONLY(Description))
142 if (AccessLevel < 256) {
143 if (IPStart <= IPEnd) {
144 CIPFilter::rangeObject item;
145 item.AccessLevel = AccessLevel;
146 #ifdef __DEBUG__
147 if (m_storeDescriptions) {
148 item.Description = Description;
150 #endif
152 m_result.insert(IPStart, IPEnd, item);
154 return true;
158 return false;
163 * Helper function.
165 * @param str A string representation of an IP-range in the format "<ip>-<ip>".
166 * @param ipA The target of the first IP in the range.
167 * @param ipB The target of the second IP in the range.
168 * @return True if the parsing succeded, false otherwise (results will be invalid).
170 * The IPs returned by this function are in host order, not network order.
172 bool m_inet_atoh(const wxString &str, uint32& ipA, uint32& ipB)
174 wxString first = str.BeforeFirst(wxT('-'));
175 wxString second = str.Mid(first.Len() + 1);
177 bool result = StringIPtoUint32(first, ipA) && StringIPtoUint32(second, ipB);
179 // StringIPtoUint32 saves the ip in anti-host order, but in order
180 // to be able to make relational comparisons, we need to convert
181 // it back to host-order.
182 ipA = wxUINT32_SWAP_ALWAYS(ipA);
183 ipB = wxUINT32_SWAP_ALWAYS(ipB);
185 return result;
190 * Helper-function for processing the PeerGuardian format.
192 * @return True if the line was valid, false otherwise.
194 * This function will correctly parse files that follow the folllowing
195 * format for specifying IP-ranges (whitespace is optional):
196 * <IPStart> - <IPEnd> , <AccessLevel> , <Description>
198 bool ProcessPeerGuardianLine(const wxString& sLine)
200 CSimpleTokenizer tkz(sLine, wxT(','));
202 wxString first = tkz.next();
203 wxString second = tkz.next();
204 wxString third = tkz.remaining().Strip(wxString::both);
206 // If there were less than two tokens, fail
207 if (tkz.tokenCount() != 2) {
208 return false;
211 // Convert string IP's to host order IP numbers
212 uint32 IPStart = 0;
213 uint32 IPEnd = 0;
215 // This will also fail if the line is commented out
216 if (!m_inet_atoh(first, IPStart, IPEnd)) {
217 return false;
220 // Second token is Access Level, default is 0.
221 unsigned long AccessLevel = 0;
222 if (!second.Strip(wxString::both).ToULong(&AccessLevel) || AccessLevel >= 255) {
223 return false;
226 // Add the filter
227 return AddIPRange(IPStart, IPEnd, AccessLevel, third);
232 * Helper-function for processing the AntiP2P format.
234 * @return True if the line was valid, false otherwise.
236 * This function will correctly parse files that follow the folllowing
237 * format for specifying IP-ranges (whitespace is optional):
238 * <Description> : <IPStart> - <IPEnd>
240 bool ProcessAntiP2PLine(const wxString& sLine)
242 // remove spaces from the left and right.
243 const wxString line = sLine.Strip(wxString::leading);
245 // Extract description (first) and IP-range (second) form the line
246 int pos = line.Find(wxT(':'), true);
247 if (pos == -1) {
248 return false;
251 wxString Description = line.Left(pos).Strip(wxString::trailing);
252 wxString IPRange = line.Right(line.Len() - pos - 1);
254 // Convert string IP's to host order IP numbers
255 uint32 IPStart = 0;
256 uint32 IPEnd = 0;
258 if (!m_inet_atoh(IPRange ,IPStart, IPEnd)) {
259 return false;
262 // Add the filter
263 return AddIPRange(IPStart, IPEnd, 0, Description);
268 * Loads a IP-list from the specified file, can be text or zip.
270 * @return True if the file was loaded, false otherwise.
272 int LoadFromFile(const wxString& file)
274 const CPath path = CPath(file);
276 if (!path.FileExists() /* || TestDestroy() (see CIPFilter::Reload()) */) {
277 return 0;
280 #ifdef __DEBUG__
281 m_storeDescriptions = theLogger.IsEnabled(logIPFilter);
282 #endif
284 const wxChar* ipfilter_files[] = {
285 wxT("ipfilter.dat"),
286 wxT("guardian.p2p"),
287 wxT("guarding.p2p"),
288 NULL
291 // Try to unpack the file, might be an archive
293 if (UnpackArchive(path, ipfilter_files).second != EFT_Text) {
294 AddLogLineM(true,
295 CFormat(_("Failed to load ipfilter.dat file '%s', unknown format encountered.")) % file);
296 return 0;
299 int filtercount = 0;
300 int discardedCount = 0;
302 CTextFile readFile;
303 if (readFile.Open(path, CTextFile::read)) {
304 // Function pointer-type of the parse-functions we can use
305 typedef bool (CIPFilterTask::*ParseFunc)(const wxString&);
307 ParseFunc func = NULL;
309 while (!readFile.Eof()) {
310 wxString line = readFile.GetNextLine();
312 /* See CIPFilter::Reload()
313 if (TestDestroy()) {
314 return 0;
315 } else */ if (func && (*this.*func)(line)) {
316 filtercount++;
317 } else if (ProcessPeerGuardianLine(line)) {
318 func = &CIPFilterTask::ProcessPeerGuardianLine;
319 filtercount++;
320 } else if (ProcessAntiP2PLine(line)) {
321 func = &CIPFilterTask::ProcessAntiP2PLine;
322 filtercount++;
323 } else {
324 // Comments and empty lines are ignored
325 line = line.Strip(wxString::both);
327 if (!line.IsEmpty() && !line.StartsWith(wxT("#"))) {
328 discardedCount++;
329 AddDebugLogLineM(false, logIPFilter, wxT("Invalid line found while reading ipfilter file: ") + line);
333 } else {
334 AddLogLineM(true, CFormat(_("Failed to load ipfilter.dat file '%s', could not open file.")) % file);
335 return 0;
338 AddLogLineM(false,
339 ( CFormat(wxPLURAL("Loaded %u IP-range from '%s'.", "Loaded %u IP-ranges from '%s'.", filtercount)) % filtercount % file )
340 + wxT(" ") +
341 ( CFormat(wxPLURAL("%u malformed line was discarded.", "%u malformed lines were discarded.", discardedCount)) % discardedCount )
344 return filtercount;
347 private:
348 wxEvtHandler* m_owner;
349 CIPFilter::IPMap m_result;
353 ////////////////////////////////////////////////////////////
354 // CIPFilter
357 BEGIN_EVENT_TABLE(CIPFilter, wxEvtHandler)
358 EVT_MULE_IPFILTER_LOADED(CIPFilter::OnIPFilterEvent)
359 END_EVENT_TABLE()
364 * This function creates a text-file containing the specified text,
365 * but only if the file does not already exist.
367 void CreateDummyFile(const wxString& filename, const wxString& text)
369 // Create template files
370 if (!wxFileExists(filename)) {
371 CTextFile file;
373 if (file.Open(filename, CTextFile::write)) {
374 file.WriteLine(text);
380 CIPFilter::CIPFilter()
382 // Setup dummy files for the curious user.
383 const wxString normalDat = theApp->ConfigDir + wxT("ipfilter.dat");
384 const wxString normalMsg = wxString()
385 << wxT("# This file is used by aMule to store ipfilter lists downloaded\n")
386 << wxT("# through the auto-update functionality. Do not save ipfilter-\n")
387 << wxT("# ranges here that should not be overwritten by aMule.\n");
389 CreateDummyFile(normalDat, normalMsg);
391 const wxString staticDat = theApp->ConfigDir + wxT("ipfilter_static.dat");
392 const wxString staticMsg = wxString()
393 << wxT("# This file is used to store ipfilter-ranges that should\n")
394 << wxT("# not be overwritten by aMule. If you wish to keep a custom\n")
395 << wxT("# set of ipfilter-ranges that take precedence over ipfilter-\n")
396 << wxT("# ranges aquired through the auto-update functionality, then\n")
397 << wxT("# place them in this file. aMule will not change this file.");
399 CreateDummyFile(staticDat, staticMsg);
401 Reload();
405 void CIPFilter::Reload()
407 // We keep the current filter till the new one has been loaded.
408 //CThreadScheduler::AddTask(new CIPFilterTask(this));
410 // This procedure cannot be run as a task,
411 // wxArchiveFSHandler::FindFirst() will eventually call wxExecute(),
412 // and this can only be done from the main task.
414 // This way, We call the Entry() routine manually and comment out the
415 // calls to TestDestroy().
416 CIPFilterTask ipf_task(this);
417 ipf_task.Entry();
421 uint32 CIPFilter::BanCount() const
423 wxMutexLocker lock(m_mutex);
425 return m_iplist.size();
429 bool CIPFilter::IsFiltered(uint32 IPTest, bool isServer)
431 if ((thePrefs::IsFilteringClients() && !isServer) || (thePrefs::IsFilteringServers() && isServer)) {
432 wxMutexLocker lock(m_mutex);
434 // The IP needs to be in host order
435 IPMap::iterator it = m_iplist.find_range(wxUINT32_SWAP_ALWAYS(IPTest));
437 if (it != m_iplist.end()) {
438 if (it->AccessLevel < thePrefs::GetIPFilterLevel()) {
439 #ifdef __DEBUG__
440 AddDebugLogLineM(false, logIPFilter, wxString(wxT("Filtered IP (AccLvl: ")) << (long)it->AccessLevel << wxT("): ")
441 << Uint32toStringIP(IPTest) << wxT(" (") << it->Description + wxT(")"));
442 #endif
444 if (isServer) {
445 theStats::AddFilteredServer();
446 } else {
447 theStats::AddFilteredClient();
449 return true;
454 return false;
458 void CIPFilter::Update(const wxString& strURL)
460 if (!strURL.IsEmpty()) {
461 m_URL = strURL;
463 wxString filename = theApp->ConfigDir + wxT("ipfilter.download");
464 CHTTPDownloadThread *downloader = new CHTTPDownloadThread(m_URL, filename, theApp->ConfigDir + wxT("ipfilter.dat"), HTTP_IPFilter);
466 downloader->Create();
467 downloader->Run();
472 void CIPFilter::DownloadFinished(uint32 result)
474 if (result == HTTP_Success) {
475 // download succeeded. proceed with ipfilter loading
476 wxString newDat = theApp->ConfigDir + wxT("ipfilter.download");
477 wxString oldDat = theApp->ConfigDir + wxT("ipfilter.dat");
479 if (wxFileExists(oldDat)) {
480 if (!wxRemoveFile(oldDat)) {
481 AddLogLineC(CFormat(_("Failed to remove %s file, aborting update.")) % wxT("ipfilter.dat"));
482 return;
486 if (!wxRenameFile(newDat, oldDat)) {
487 AddLogLineC(CFormat(_("Failed to rename new %s file, aborting update.")) % wxT("ipfilter.dat"));
488 return;
491 // Reload both ipfilter files
492 Reload();
493 AddLogLineN(CFormat(_("Successfully updated %s")) % wxT("ipfilter.dat"));
494 } else if (result == HTTP_Skipped) {
495 AddLogLineN(CFormat(_("Skipped download of %s, because requested file is not newer.")) % wxT("ipfilter.dat"));
496 } else {
497 AddLogLineC(CFormat(_("Failed to download %s from %s")) % wxT("ipfilter.dat") % m_URL);
502 void CIPFilter::OnIPFilterEvent(CIPFilterEvent& evt)
505 wxMutexLocker lock(m_mutex);
506 std::swap(m_iplist, evt.m_result);
509 if (thePrefs::IsFilteringClients()) {
510 theApp->clientlist->FilterQueues();
512 if (thePrefs::IsFilteringServers()) {
513 theApp->serverlist->FilterServers();
517 // File_checked_for_headers