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
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 ////////////////////////////////////////////////////////////
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
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.
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-
98 class CIPFilterTask
: public CThreadTask
101 CIPFilterTask(wxEvtHandler
* owner
, bool block
= false)
102 : CThreadTask(wxT("Load IPFilter"), wxEmptyString
, ETP_Critical
),
103 m_storeDescriptions(false),
107 LockBlockDuringUpdate();
111 void LockBlockDuringUpdate() { m_blockDuringUpdate
.Lock(); }
112 void UnlockBlockDuringUpdate() { m_blockDuringUpdate
.Unlock(); }
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());
125 wxString
dataDir(spb
.GetPluginsDir());
126 #elif defined(__WXMAC__)
127 wxString
dataDir(spb
.GetDataDir());
129 wxString
dataDir(spb
.GetDataDir().BeforeLast(wxT('/')) + wxT("/amule"));
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)
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;
161 m_rangeIPs
.push_back(startIP
);
162 uint32 curLength
= realLength
;
164 if (realLength
<= 0x8000) {
165 pushLength
= realLength
- 1;
167 if (curLength
>= 0x08000000) {
168 // range to big, limit
169 curLength
= 0x08000000;
172 curLength
&= 0x07FFF000;
174 pushLength
= ((curLength
- 1) >> 12) | 0x8000;
176 m_rangeLengths
.push_back(pushLength
);
178 if (m_storeDescriptions
) {
179 m_rangeNames
.push_back(it
->Description
);
182 realLength
-= curLength
;
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.
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.
209 //! Contains the user-description of the range.
210 wxString Description
;
213 //! The AccessLevel for this filter.
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
230 // Mutex to block thread during active update
231 wxMutex m_blockDuringUpdate
;
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
) {
251 item
.AccessLevel
= AccessLevel
;
253 if (m_storeDescriptions
) {
254 item
.Description
= Description
;
258 m_result
.insert(IPStart
, IPEnd
, item
);
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
);
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) {
317 // Convert string IP's to host order IP numbers
321 // This will also fail if the line is commented out
322 if (!ReadIPRange(first
, IPStart
, IPEnd
)) {
326 // Second token is Access Level, default is 0.
327 unsigned long AccessLevel
= 0;
328 if (!second
.Strip(wxString::both
).ToULong(&AccessLevel
) || AccessLevel
>= 255) {
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);
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
364 if (!ReadIPRange(IPRange
,IPStart
, IPEnd
)) {
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()) {
387 m_storeDescriptions
= theLogger
.IsEnabled(logIPFilter
);
390 const wxChar
* ipfilter_files
[] = {
397 // Try to unpack the file, might be an archive
399 if (UnpackArchive(path
, ipfilter_files
).second
!= EFT_Text
) {
401 CFormat(_("Failed to load ipfilter.dat file '%s', unknown format encountered.")) % file
);
406 int discardedCount
= 0;
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()) {
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;
427 processed_ok
= (*this.*func
)(line
);
429 if (processed_ok
= ProcessPeerGuardianLine(line
)) {
430 func
= &CIPFilterTask::ProcessPeerGuardianLine
;
431 } else if (processed_ok
= ProcessAntiP2PLine(line
)) {
432 func
= &CIPFilterTask::ProcessAntiP2PLine
;
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("#"))) {
443 AddDebugLogLineM(false, logIPFilter
, wxT("Invalid line found while reading ipfilter file: ") + line
);
449 AddLogLineM(true, CFormat(_("Failed to load ipfilter.dat file '%s', could not open file.")) % file
);
454 ( CFormat(wxPLURAL("Loaded %u IP-range from '%s'.", "Loaded %u IP-ranges from '%s'.", filtercount
)) % filtercount
% file
)
456 ( CFormat(wxPLURAL("%u malformed line was discarded.", "%u malformed lines were discarded.", discardedCount
)) % discardedCount
)
464 ////////////////////////////////////////////////////////////
468 BEGIN_EVENT_TABLE(CIPFilter
, wxEvtHandler
)
469 EVT_MULE_IPFILTER_LOADED(CIPFilter::OnIPFilterEvent
)
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
)) {
484 if (file
.Open(filename
, CTextFile::write
)) {
485 file
.WriteLine(text
);
493 CIPFilter::CIPFilter() :
495 m_startKADWhenReady(false),
496 m_connectToAnyServerWhenReady(false),
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.
532 m_ipFilterTask
= new CIPFilterTask(this, true);
533 CThreadScheduler::AddTask(m_ipFilterTask
);
534 Update(thePrefs::IPFilterURL());
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
)) {
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
));
566 theStats::AddFilteredServer();
568 theStats::AddFilteredClient();
572 wxMutexLocker
lock(m_mutex
);
573 // The IP needs to be in host order
574 uint32 ip
= wxUINT32_SWAP_ALWAYS(IPTest
);
576 int imax
= m_rangeIPs
.size() - 1;
579 while (imin
<= imax
) {
580 i
= (imin
+ imax
) / 2;
581 uint32 curIP
= m_rangeIPs
[i
];
583 uint32 curLength
= m_rangeLengths
[i
];
584 if (curLength
>= 0x8000) {
585 curLength
= ((curLength
& 0x7fff) << 12) + 0xfff;
587 if (curIP
+ curLength
>= ip
) {
599 AddDebugLogLineN(logIPFilter
, CFormat(wxT("Filtered IP %s%s")) % Uint32toStringIP(IPTest
)
600 % (i
< (int)m_rangeNames
.size() ? (wxT(" (") + m_rangeNames
[i
] + wxT(")")) : wxString(wxEmptyString
)));
602 theStats::AddFilteredServer();
604 theStats::AddFilteredClient();
612 void CIPFilter::Update(const wxString
& strURL
)
614 if (!strURL
.IsEmpty()) {
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();
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"));
637 } else if (!wxRenameFile(newDat
, oldDat
)) {
638 AddLogLineC(CFormat(_("Failed to rename new %s file, aborting update.")) % wxT("ipfilter.dat"));
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"));
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
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
);
671 if (theApp
->IsOnShutDown()) {
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;
694 if (thePrefs::GetSrcSeedsOn()) {
695 theApp
->downloadqueue
->LoadSourceSeeds();
699 // File_checked_for_headers