2 // This file is part of the aMule Project.
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 )
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
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.
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
43 ////////////////////////////////////////////////////////////
46 BEGIN_DECLARE_EVENT_TYPES()
47 DECLARE_EVENT_TYPE(MULE_EVT_IPFILTER_LOADED
, -1)
48 END_DECLARE_EVENT_TYPES()
50 DEFINE_EVENT_TYPE(MULE_EVT_IPFILTER_LOADED
)
53 class CIPFilterEvent
: public wxEvent
56 CIPFilterEvent(CIPFilter::IPMap
& result
)
57 : wxEvent(-1, MULE_EVT_IPFILTER_LOADED
)
59 // Avoid needles copying
60 std::swap(result
, m_result
);
63 /** @see wxEvent::Clone */
64 virtual wxEvent
* Clone() const {
65 return new CIPFilterEvent(*this);
68 CIPFilter::IPMap m_result
;
72 typedef void (wxEvtHandler::*MuleIPFilterEventFunction
)(CIPFilterEvent
&);
74 //! Event-handler for completed hashings of new shared files and partfiles.
75 #define EVT_MULE_IPFILTER_LOADED(func) \
76 DECLARE_EVENT_TABLE_ENTRY(MULE_EVT_IPFILTER_LOADED, -1, -1, \
77 (wxObjectEventFunction) (wxEventFunction) \
78 wxStaticCastEvent(MuleIPFilterEventFunction, &func), (wxObject*) NULL),
81 ////////////////////////////////////////////////////////////
82 // Thread task for loading the ipfilter.dat files.
85 * This task loads the two ipfilter.dat files, a task that
86 * can take quite a while on a slow system with a large dat-
89 class CIPFilterTask
: public CThreadTask
92 CIPFilterTask(wxEvtHandler
* owner
)
93 : CThreadTask(wxT("Load IPFilter"), wxEmptyString
, ETP_Critical
),
94 m_storeDescriptions(false),
100 wxStandardPathsBase
&spb(wxStandardPaths::Get());
102 wxString
dataDir(spb
.GetPluginsDir());
103 #elif defined(__WXMAC__)
104 wxString
dataDir(spb
.GetDataDir());
106 wxString
dataDir(spb
.GetDataDir().BeforeLast(wxT('/')) + wxT("/amule"));
108 wxString
systemwideFile(JoinPaths(dataDir
,wxT("ipfilter.dat")));
110 AddLogLineM(false, _("Loading IP-filters 'ipfilter.dat' and 'ipfilter_static.dat'."));
111 if ( !LoadFromFile(theApp
->ConfigDir
+ wxT("ipfilter.dat")) &&
112 thePrefs::UseIPFilterSystem() ) {
113 LoadFromFile(systemwideFile
);
117 LoadFromFile(theApp
->ConfigDir
+ wxT("ipfilter_static.dat"));
119 CIPFilterEvent
evt(m_result
);
120 wxPostEvent(m_owner
, evt
);
124 bool m_storeDescriptions
;
129 * @param IPstart The start of the IP-range.
130 * @param IPend The end of the IP-range, must be less than or equal to IPstart.
131 * @param AccessLevel The AccessLevel of this range.
132 * @param Description The assosiated description of this range.
133 * @return true if the range was added, false if it was discarded.
135 * This function inserts the specified range into the IPMap. Invalid
136 * ranges where the AccessLevel is not within the range 0..255, or
137 * where IPEnd < IPstart not inserted.
139 bool AddIPRange(uint32 IPStart
, uint32 IPEnd
, uint16 AccessLevel
, const wxString
& Description
)
141 if (AccessLevel
< 256) {
142 if (IPStart
<= IPEnd
) {
143 CIPFilter::rangeObject item
;
144 item
.AccessLevel
= AccessLevel
;
146 if (m_storeDescriptions
) {
147 item
.Description
= Description
;
151 m_result
.insert(IPStart
, IPEnd
, item
);
164 * @param str A string representation of an IP-range in the format "<ip>-<ip>".
165 * @param ipA The target of the first IP in the range.
166 * @param ipB The target of the second IP in the range.
167 * @return True if the parsing succeded, false otherwise (results will be invalid).
169 * The IPs returned by this function are in host order, not network order.
171 bool m_inet_atoh(const wxString
&str
, uint32
& ipA
, uint32
& ipB
)
173 wxString first
= str
.BeforeFirst(wxT('-'));
174 wxString second
= str
.Mid(first
.Len() + 1);
176 bool result
= StringIPtoUint32(first
, ipA
) && StringIPtoUint32(second
, ipB
);
178 // StringIPtoUint32 saves the ip in anti-host order, but in order
179 // to be able to make relational comparisons, we need to convert
180 // it back to host-order.
181 ipA
= wxUINT32_SWAP_ALWAYS(ipA
);
182 ipB
= wxUINT32_SWAP_ALWAYS(ipB
);
189 * Helper-function for processing the PeerGuardian format.
191 * @return True if the line was valid, false otherwise.
193 * This function will correctly parse files that follow the folllowing
194 * format for specifying IP-ranges (whitespace is optional):
195 * <IPStart> - <IPEnd> , <AccessLevel> , <Description>
197 bool ProcessPeerGuardianLine(const wxString
& sLine
)
199 CSimpleTokenizer
tkz(sLine
, wxT(','));
201 wxString first
= tkz
.next();
202 wxString second
= tkz
.next();
203 wxString third
= tkz
.remaining().Strip(wxString::both
);
205 // If there were less than two tokens, fail
206 if (tkz
.tokenCount() != 2) {
210 // Convert string IP's to host order IP numbers
214 // This will also fail if the line is commented out
215 if (!m_inet_atoh(first
, IPStart
, IPEnd
)) {
219 // Second token is Access Level, default is 0.
220 unsigned long AccessLevel
= 0;
221 if (!second
.Strip(wxString::both
).ToULong(&AccessLevel
) || AccessLevel
>= 255) {
226 return AddIPRange(IPStart
, IPEnd
, AccessLevel
, third
);
231 * Helper-function for processing the AntiP2P format.
233 * @return True if the line was valid, false otherwise.
235 * This function will correctly parse files that follow the folllowing
236 * format for specifying IP-ranges (whitespace is optional):
237 * <Description> : <IPStart> - <IPEnd>
239 bool ProcessAntiP2PLine(const wxString
& sLine
)
241 // remove spaces from the left and right.
242 const wxString line
= sLine
.Strip(wxString::leading
);
244 // Extract description (first) and IP-range (second) form the line
245 int pos
= line
.Find(wxT(':'), true);
250 wxString Description
= line
.Left(pos
).Strip(wxString::trailing
);
251 wxString IPRange
= line
.Right(line
.Len() - pos
- 1);
253 // Convert string IP's to host order IP numbers
257 if (!m_inet_atoh(IPRange
,IPStart
, IPEnd
)) {
262 return AddIPRange(IPStart
, IPEnd
, 0, Description
);
267 * Loads a IP-list from the specified file, can be text or zip.
269 * @return True if the file was loaded, false otherwise.
271 int LoadFromFile(const wxString
& file
)
273 const CPath path
= CPath(file
);
275 if (!path
.FileExists() /* || TestDestroy() (see CIPFilter::Reload()) */) {
280 m_storeDescriptions
= theLogger
.IsEnabled(logIPFilter
);
283 const wxChar
* ipfilter_files
[] = {
290 // Try to unpack the file, might be an archive
292 if (UnpackArchive(path
, ipfilter_files
).second
!= EFT_Text
) {
294 CFormat(_("Failed to load ipfilter.dat file '%s', unknown format encountered.")) % file
);
299 int discardedCount
= 0;
302 if (readFile
.Open(path
, CTextFile::read
)) {
303 // Function pointer-type of the parse-functions we can use
304 typedef bool (CIPFilterTask::*ParseFunc
)(const wxString
&);
306 ParseFunc func
= NULL
;
308 while (!readFile
.Eof()) {
309 wxString line
= readFile
.GetNextLine();
311 /* See CIPFilter::Reload()
314 } else */ if (func
&& (*this.*func
)(line
)) {
316 } else if (ProcessPeerGuardianLine(line
)) {
317 func
= &CIPFilterTask::ProcessPeerGuardianLine
;
319 } else if (ProcessAntiP2PLine(line
)) {
320 func
= &CIPFilterTask::ProcessAntiP2PLine
;
323 // Comments and empty lines are ignored
324 line
= line
.Strip(wxString::both
);
326 if (!line
.IsEmpty() && !line
.StartsWith(wxT("#"))) {
328 AddDebugLogLineM(false, logIPFilter
, wxT("Invalid line found while reading ipfilter file: ") + line
);
333 AddLogLineM(true, CFormat(_("Failed to load ipfilter.dat file '%s', could not open file.")) % file
);
338 ( CFormat(wxPLURAL("Loaded %u IP-range from '%s'.", "Loaded %u IP-ranges from '%s'.", filtercount
)) % filtercount
% file
)
340 ( CFormat(wxPLURAL("%u malformed line was discarded.", "%u malformed lines were discarded.", discardedCount
)) % discardedCount
)
347 wxEvtHandler
* m_owner
;
348 CIPFilter::IPMap m_result
;
352 ////////////////////////////////////////////////////////////
356 BEGIN_EVENT_TABLE(CIPFilter
, wxEvtHandler
)
357 EVT_MULE_IPFILTER_LOADED(CIPFilter::OnIPFilterEvent
)
363 * This function creates a text-file containing the specified text,
364 * but only if the file does not already exist.
366 void CreateDummyFile(const wxString
& filename
, const wxString
& text
)
368 // Create template files
369 if (!wxFileExists(filename
)) {
372 if (file
.Open(filename
, CTextFile::write
)) {
373 file
.WriteLine(text
);
379 CIPFilter::CIPFilter()
381 // Setup dummy files for the curious user.
382 const wxString normalDat
= theApp
->ConfigDir
+ wxT("ipfilter.dat");
383 const wxString normalMsg
= wxString()
384 << wxT("# This file is used by aMule to store ipfilter lists downloaded\n")
385 << wxT("# through the auto-update functionality. Do not save ipfilter-\n")
386 << wxT("# ranges here that should not be overwritten by aMule.\n");
388 CreateDummyFile(normalDat
, normalMsg
);
390 const wxString staticDat
= theApp
->ConfigDir
+ wxT("ipfilter_static.dat");
391 const wxString staticMsg
= wxString()
392 << wxT("# This file is used to store ipfilter-ranges that should\n")
393 << wxT("# not be overwritten by aMule. If you wish to keep a custom\n")
394 << wxT("# set of ipfilter-ranges that take precedence over ipfilter-\n")
395 << wxT("# ranges aquired through the auto-update functionality, then\n")
396 << wxT("# place them in this file. aMule will not change this file.");
398 CreateDummyFile(staticDat
, staticMsg
);
404 void CIPFilter::Reload()
406 // We keep the current filter till the new one has been loaded.
407 //CThreadScheduler::AddTask(new CIPFilterTask(this));
409 // This procedure cannot be run as a task,
410 // wxArchiveFSHandler::FindFirst() will eventually call wxExecute(),
411 // and this can only be done from the main task.
413 // This way, We call the Entry() routine manually and comment out the
414 // calls to TestDestroy().
415 CIPFilterTask
ipf_task(this);
420 uint32
CIPFilter::BanCount() const
422 wxMutexLocker
lock(m_mutex
);
424 return m_iplist
.size();
428 bool CIPFilter::IsFiltered(uint32 IPTest
, bool isServer
)
430 if ((thePrefs::IsFilteringClients() && !isServer
) || (thePrefs::IsFilteringServers() && isServer
)) {
431 wxMutexLocker
lock(m_mutex
);
433 // The IP needs to be in host order
434 IPMap::iterator it
= m_iplist
.find_range(wxUINT32_SWAP_ALWAYS(IPTest
));
436 if (it
!= m_iplist
.end()) {
437 if (it
->AccessLevel
< thePrefs::GetIPFilterLevel()) {
439 AddDebugLogLineM(false, logIPFilter
, wxString(wxT("Filtered IP (AccLvl: ")) << (long)it
->AccessLevel
<< wxT("): ")
440 << Uint32toStringIP(IPTest
) << wxT(" (") << it
->Description
+ wxT(")"));
444 theStats::AddFilteredServer();
446 theStats::AddFilteredClient();
457 void CIPFilter::Update(const wxString
& strURL
)
459 if (!strURL
.IsEmpty()) {
462 wxString filename
= theApp
->ConfigDir
+ wxT("ipfilter.download");
463 CHTTPDownloadThread
*downloader
= new CHTTPDownloadThread(m_URL
, filename
, theApp
->ConfigDir
+ wxT("ipfilter.dat"), HTTP_IPFilter
);
465 downloader
->Create();
471 void CIPFilter::DownloadFinished(uint32 result
)
473 if (result
== HTTP_Success
) {
474 // download succeeded. proceed with ipfilter loading
475 wxString newDat
= theApp
->ConfigDir
+ wxT("ipfilter.download");
476 wxString oldDat
= theApp
->ConfigDir
+ wxT("ipfilter.dat");
478 if (wxFileExists(oldDat
)) {
479 if (!wxRemoveFile(oldDat
)) {
480 AddLogLineC(CFormat(_("Failed to remove %s file, aborting update.")) % wxT("ipfilter.dat"));
485 if (!wxRenameFile(newDat
, oldDat
)) {
486 AddLogLineC(CFormat(_("Failed to rename new %s file, aborting update.")) % wxT("ipfilter.dat"));
490 // Reload both ipfilter files
492 AddLogLineN(CFormat(_("Successfully updated %s")) % wxT("ipfilter.dat"));
493 } else if (result
== HTTP_Skipped
) {
494 AddLogLineN(CFormat(_("Skipped download of %s, because requested file is not newer.")) % wxT("ipfilter.dat"));
496 AddLogLineC(CFormat(_("Failed to download %s from %s")) % wxT("ipfilter.dat") % m_URL
);
501 void CIPFilter::OnIPFilterEvent(CIPFilterEvent
& evt
)
504 wxMutexLocker
lock(m_mutex
);
505 std::swap(m_iplist
, evt
.m_result
);
508 if (thePrefs::IsFilteringClients()) {
509 theApp
->clientlist
->FilterQueues();
511 if (thePrefs::IsFilteringServers()) {
512 theApp
->serverlist
->FilterServers();
516 // File_checked_for_headers