2 // This file is part of the aMule Project.
4 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2011 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
29 #include "IPFilter.h" // Interface declarations.
30 #include "IPFilterScanner.h" // Interface for flexer
31 #include "Preferences.h" // Needed for thePrefs
32 #include "amule.h" // Needed for theApp
33 #include "Statistics.h" // Needed for theStats
34 #include "HTTPDownload.h" // Needed for CHTTPDownloadThread
35 #include "Logger.h" // Needed for AddDebugLogLine{C,N}
36 #include <common/Format.h> // Needed for CFormat
37 #include <common/StringFunctions.h> // Needed for CSimpleTokenizer
38 #include <common/FileFunctions.h> // Needed for UnpackArchive
39 #include <common/TextFile.h> // Needed for CTextFile
40 #include "ThreadScheduler.h" // Needed for CThreadScheduler and CThreadTask
41 #include "ClientList.h" // Needed for CClientList
42 #include "ServerList.h" // Needed for CServerList
43 #include <common/Macros.h> // Needed for DEBUG_ONLY()
44 #include "RangeMap.h" // Needed for CRangeMap
45 #include "ServerConnect.h" // Needed for ConnectToAnyServer()
46 #include "DownloadQueue.h" // Needed for theApp->downloadqueue
49 ////////////////////////////////////////////////////////////
52 BEGIN_DECLARE_EVENT_TYPES()
53 DECLARE_EVENT_TYPE(MULE_EVT_IPFILTER_LOADED
, -1)
54 END_DECLARE_EVENT_TYPES()
56 DEFINE_EVENT_TYPE(MULE_EVT_IPFILTER_LOADED
)
59 class CIPFilterEvent
: public wxEvent
62 CIPFilterEvent(CIPFilter::RangeIPs rangeIPs
, CIPFilter::RangeLengths rangeLengths
, CIPFilter::RangeNames rangeNames
)
63 : wxEvent(-1, MULE_EVT_IPFILTER_LOADED
)
65 // Physically copy the vectors, this will hopefully resize them back to their needed capacity.
66 m_rangeIPs
= rangeIPs
;
67 m_rangeLengths
= rangeLengths
;
68 // This one is usually empty, and should always be swapped, not copied.
69 std::swap(m_rangeNames
, rangeNames
);
72 /** @see wxEvent::Clone */
73 virtual wxEvent
* Clone() const {
74 return new CIPFilterEvent(*this);
77 CIPFilter::RangeIPs m_rangeIPs
;
78 CIPFilter::RangeLengths m_rangeLengths
;
79 CIPFilter::RangeNames m_rangeNames
;
83 typedef void (wxEvtHandler::*MuleIPFilterEventFunction
)(CIPFilterEvent
&);
85 //! Event-handler for completed hashings of new shared files and partfiles.
86 #define EVT_MULE_IPFILTER_LOADED(func) \
87 DECLARE_EVENT_TABLE_ENTRY(MULE_EVT_IPFILTER_LOADED, -1, -1, \
88 (wxObjectEventFunction) (wxEventFunction) \
89 wxStaticCastEvent(MuleIPFilterEventFunction, &func), (wxObject*) NULL),
92 ////////////////////////////////////////////////////////////
93 // Thread task for loading the ipfilter.dat files.
96 * This task loads the two ipfilter.dat files, a task that
97 * can take quite a while on a slow system with a large dat-
100 class CIPFilterTask
: public CThreadTask
103 CIPFilterTask(wxEvtHandler
* owner
)
104 : CThreadTask(wxT("Load IPFilter"), wxEmptyString
, ETP_Critical
),
105 m_storeDescriptions(false),
113 AddLogLineN(_("Loading IP filters 'ipfilter.dat' and 'ipfilter_static.dat'."));
114 if ( !LoadFromFile(theApp
->ConfigDir
+ wxT("ipfilter.dat")) &&
115 thePrefs::UseIPFilterSystem() ) {
116 // Load from system wide IP filter file
117 wxStandardPathsBase
&spb(wxStandardPaths::Get());
119 wxString
dataDir(spb
.GetPluginsDir());
120 #elif defined(__WXMAC__)
121 wxString
dataDir(spb
.GetDataDir());
123 wxString
dataDir(spb
.GetDataDir().BeforeLast(wxT('/')) + wxT("/amule"));
125 wxString
systemwideFile(JoinPaths(dataDir
,wxT("ipfilter.dat")));
126 LoadFromFile(systemwideFile
);
130 LoadFromFile(theApp
->ConfigDir
+ wxT("ipfilter_static.dat"));
132 uint8 accessLevel
= thePrefs::GetIPFilterLevel();
133 uint32 size
= m_result
.size();
134 // Reserve a little more so we don't have to resize the vector later.
135 // (Map ranges can exist that have to be stored in several parts.)
136 // Extra memory will be freed in the end.
137 m_rangeIPs
.reserve(size
+ 1000);
138 m_rangeLengths
.reserve(size
+ 1000);
139 if (m_storeDescriptions
) {
140 m_rangeNames
.reserve(size
+ 1000);
142 for (IPMap::iterator it
= m_result
.begin(); it
!= m_result
.end(); ++it
) {
143 if (it
->AccessLevel
< accessLevel
) {
144 // Calculate range "length"
145 // (which is included-end - start and thus length - 1)
148 // 0x8000 - 0xffff 0xfff - 0x07ffffff
149 // that means: remove msb, shift left by 12 bit, add 0xfff
150 // so it can cover 8 consecutive class A nets
151 // larger ranges (or theoretical ranges with uneven ends) have to be split
152 uint32 startIP
= it
.keyStart();
153 uint32 realLength
= it
.keyEnd() - it
.keyStart() + 1;
155 std::string
* descp
= 0;
158 m_rangeIPs
.push_back(startIP
);
159 uint32 curLength
= realLength
;
161 if (realLength
<= 0x8000) {
162 pushLength
= realLength
- 1;
164 if (curLength
>= 0x08000000) {
165 // range to big, limit
166 curLength
= 0x08000000;
169 curLength
&= 0x07FFF000;
171 pushLength
= ((curLength
- 1) >> 12) | 0x8000;
173 m_rangeLengths
.push_back(pushLength
);
175 if (m_storeDescriptions
) {
176 // std::string has no ref counting, so swap it
177 // (it's used so we need half the space than wxString with wide chars)
179 // we split the range so we have to duplicate it
180 m_rangeNames
.push_back(*descp
);
182 // push back empty string and swap
183 m_rangeNames
.push_back(std::string());
184 descp
= & * m_rangeNames
.rbegin();
185 std::swap(*descp
, it
->Description
);
189 realLength
-= curLength
;
190 startIP
+= curLength
;
194 // Numbers are probably different:
195 // - ranges from map that are not blocked because of their level are not added to the table
196 // - some ranges from the map have to be split for the table
197 AddDebugLogLineN(logIPFilter
, CFormat(wxT("Ranges in map: %d blocked ranges in table: %d")) % size
% m_rangeIPs
.size());
199 CIPFilterEvent
evt(m_rangeIPs
, m_rangeLengths
, m_rangeNames
);
200 wxPostEvent(m_owner
, evt
);
204 * This structure is used to contain the range-data in the rangemap.
208 bool operator==( const rangeObject
& other
) const {
209 return AccessLevel
== other
.AccessLevel
;
212 // Since descriptions are only used for debugging messages, there
213 // is no need to keep them in memory when running a non-debug build.
215 //! Contains the user-description of the range.
216 std::string Description
;
219 //! The AccessLevel for this filter.
223 //! The is the type of map used to store the IPs.
224 typedef CRangeMap
<rangeObject
, uint32
> IPMap
;
226 bool m_storeDescriptions
;
228 // the generated filter
229 CIPFilter::RangeIPs m_rangeIPs
;
230 CIPFilter::RangeLengths m_rangeLengths
;
231 CIPFilter::RangeNames m_rangeNames
;
233 wxEvtHandler
* m_owner
;
234 // temporary map for filter generation
240 * @param IPstart The start of the IP-range.
241 * @param IPend The end of the IP-range, must be less than or equal to IPstart.
242 * @param AccessLevel The AccessLevel of this range.
243 * @param Description The assosiated description of this range.
244 * @return true if the range was added, false if it was discarded.
246 * This function inserts the specified range into the IPMap. Invalid
247 * ranges where the AccessLevel is not within the range 0..255, or
248 * where IPEnd < IPstart not inserted.
250 bool AddIPRange(uint32 IPStart
, uint32 IPEnd
, uint16 AccessLevel
, const char* DEBUG_ONLY(Description
))
252 if (AccessLevel
< 256) {
253 if (IPStart
<= IPEnd
) {
255 item
.AccessLevel
= AccessLevel
;
257 if (m_storeDescriptions
) {
258 item
.Description
= Description
;
262 m_result
.insert(IPStart
, IPEnd
, item
);
273 * Loads a IP-list from the specified file, can be text or zip.
275 * @return True if the file was loaded, false otherwise.
277 int LoadFromFile(const wxString
& file
)
279 const CPath path
= CPath(file
);
281 if (!path
.FileExists() || TestDestroy()) {
286 m_storeDescriptions
= theLogger
.IsEnabled(logIPFilter
);
289 const wxChar
* ipfilter_files
[] = {
296 // Try to unpack the file, might be an archive
298 if (UnpackArchive(path
, ipfilter_files
).second
!= EFT_Text
) {
299 AddLogLineC(CFormat(_("Failed to load ipfilter.dat file '%s', unknown format encountered.")) % file
);
306 if (readFile
.Open(path
.GetRaw())) {
308 yyiprestart(readFile
.fp());
311 uint32 IPAccessLevel
= 0;
312 char * IPDescription
;
314 uint32 time1
= GetTickCountFullRes();
316 while (yyiplex(IPStart
, IPEnd
, IPAccessLevel
, IPDescription
)) {
317 AddIPRange(IPStart
, IPEnd
, IPAccessLevel
, IPDescription
);
321 uint32 time2
= GetTickCountFullRes();
322 AddDebugLogLineN(logIPFilter
, CFormat(wxT("time for lexer: %.3f")) % ((time2
-time1
) / 1000.0));
325 AddLogLineC(CFormat(_("Failed to load ipfilter.dat file '%s', could not open file.")) % file
);
329 wxString msg
= CFormat(wxPLURAL("Loaded %u IP-range from '%s'.", "Loaded %u IP-ranges from '%s'.", filtercount
)) % filtercount
% file
;
331 msg
<< wxT(" ") << ( CFormat(wxPLURAL("%u malformed line was discarded.", "%u malformed lines were discarded.", yyip_Bad
)) % yyip_Bad
);
340 ////////////////////////////////////////////////////////////
344 BEGIN_EVENT_TABLE(CIPFilter
, wxEvtHandler
)
345 EVT_MULE_IPFILTER_LOADED(CIPFilter::OnIPFilterEvent
)
351 * This function creates a text-file containing the specified text,
352 * but only if the file does not already exist.
354 static bool CreateDummyFile(const wxString
& filename
, const wxString
& text
)
356 // Create template files
357 if (!wxFileExists(filename
)) {
360 if (file
.Open(filename
, CTextFile::write
)) {
361 file
.WriteLine(text
);
369 CIPFilter::CIPFilter() :
371 m_startKADWhenReady(false),
372 m_connectToAnyServerWhenReady(false)
374 // Setup dummy files for the curious user.
375 const wxString normalDat
= theApp
->ConfigDir
+ wxT("ipfilter.dat");
376 const wxString normalMsg
= wxString()
377 << wxT("# This file is used by aMule to store ipfilter lists downloaded\n")
378 << wxT("# through the auto-update functionality. Do not save ipfilter-\n")
379 << wxT("# ranges here that should not be overwritten by aMule.\n");
381 if (CreateDummyFile(normalDat
, normalMsg
)) {
382 // redownload if user deleted file
383 thePrefs::SetLastHTTPDownloadURL(HTTP_IPFilter
, wxEmptyString
);
386 const wxString staticDat
= theApp
->ConfigDir
+ wxT("ipfilter_static.dat");
387 const wxString staticMsg
= wxString()
388 << wxT("# This file is used to store ipfilter-ranges that should\n")
389 << wxT("# not be overwritten by aMule. If you wish to keep a custom\n")
390 << wxT("# set of ipfilter-ranges that take precedence over ipfilter-\n")
391 << wxT("# ranges aquired through the auto-update functionality, then\n")
392 << wxT("# place them in this file. aMule will not change this file.");
394 CreateDummyFile(staticDat
, staticMsg
);
396 // First load currently available filter, so network connect is possible right after
397 // (in case filter download takes some time).
399 // Check if update should be done only after that.
400 m_updateAfterLoading
= thePrefs::IPFilterAutoLoad() && !thePrefs::IPFilterURL().IsEmpty();
404 void CIPFilter::Reload()
406 // We keep the current filter till the new one has been loaded.
407 CThreadScheduler::AddTask(new CIPFilterTask(this));
411 uint32
CIPFilter::BanCount() const
413 wxMutexLocker
lock(m_mutex
);
415 return m_rangeIPs
.size();
419 bool CIPFilter::IsFiltered(uint32 IPTest
, bool isServer
)
421 if ((!thePrefs::IsFilteringClients() && !isServer
) || (!thePrefs::IsFilteringServers() && isServer
)) {
425 // Somebody connected before we even started the networks.
426 // Filter is not up yet, so block him.
427 AddDebugLogLineN(logIPFilter
, CFormat(wxT("Filtered IP %s because filter isn't ready yet.")) % Uint32toStringIP(IPTest
));
429 theStats::AddFilteredServer();
431 theStats::AddFilteredClient();
435 wxMutexLocker
lock(m_mutex
);
436 // The IP needs to be in host order
437 uint32 ip
= wxUINT32_SWAP_ALWAYS(IPTest
);
439 int imax
= m_rangeIPs
.size() - 1;
442 while (imin
<= imax
) {
443 i
= (imin
+ imax
) / 2;
444 uint32 curIP
= m_rangeIPs
[i
];
446 uint32 curLength
= m_rangeLengths
[i
];
447 if (curLength
>= 0x8000) {
448 curLength
= ((curLength
& 0x7fff) << 12) + 0xfff;
450 if (curIP
+ curLength
>= ip
) {
462 AddDebugLogLineN(logIPFilter
, CFormat(wxT("Filtered IP %s%s")) % Uint32toStringIP(IPTest
)
463 % (i
< (int)m_rangeNames
.size() ? (wxT(" (") + wxString(char2unicode(m_rangeNames
[i
].c_str())) + wxT(")"))
464 : wxString(wxEmptyString
)));
466 theStats::AddFilteredServer();
468 theStats::AddFilteredClient();
476 void CIPFilter::Update(const wxString
& strURL
)
478 if (!strURL
.IsEmpty()) {
481 wxString filename
= theApp
->ConfigDir
+ wxT("ipfilter.download");
482 wxString oldfilename
= theApp
->ConfigDir
+ wxT("ipfilter.dat");
483 CHTTPDownloadThread
*downloader
= new CHTTPDownloadThread(m_URL
, filename
, oldfilename
, HTTP_IPFilter
, true, true);
485 downloader
->Create();
491 void CIPFilter::DownloadFinished(uint32 result
)
493 wxString datName
= wxT("ipfilter.dat");
494 if (result
== HTTP_Success
) {
495 // download succeeded. proceed with ipfilter loading
496 wxString newDat
= theApp
->ConfigDir
+ wxT("ipfilter.download");
497 wxString oldDat
= theApp
->ConfigDir
+ datName
;
499 if (wxFileExists(oldDat
) && !wxRemoveFile(oldDat
)) {
500 AddLogLineC(CFormat(_("Failed to remove %s file, aborting update.")) % datName
);
502 } else if (!wxRenameFile(newDat
, oldDat
)) {
503 AddLogLineC(CFormat(_("Failed to rename new %s file, aborting update.")) % datName
);
506 AddLogLineN(CFormat(_("Successfully updated %s")) % datName
);
508 } else if (result
== HTTP_Skipped
) {
509 AddLogLineN(CFormat(_("Skipped download of %s, because requested file is not newer.")) % datName
);
511 AddLogLineC(CFormat(_("Failed to download %s from %s")) % datName
% m_URL
);
514 if (result
== HTTP_Success
) {
515 // Reload both ipfilter files on success
521 void CIPFilter::OnIPFilterEvent(CIPFilterEvent
& evt
)
524 wxMutexLocker
lock(m_mutex
);
525 std::swap(m_rangeIPs
, evt
.m_rangeIPs
);
526 std::swap(m_rangeLengths
, evt
.m_rangeLengths
);
527 std::swap(m_rangeNames
, evt
.m_rangeNames
);
530 if (theApp
->IsOnShutDown()) {
533 AddLogLineN(_("IP filter is ready"));
535 if (thePrefs::IsFilteringClients()) {
536 theApp
->clientlist
->FilterQueues();
538 if (thePrefs::IsFilteringServers()) {
539 theApp
->serverlist
->FilterServers();
541 // Now start networks we didn't start earlier
542 if (m_connectToAnyServerWhenReady
|| m_startKADWhenReady
) {
543 AddLogLineC(_("Connecting"));
545 if (m_connectToAnyServerWhenReady
) {
546 m_connectToAnyServerWhenReady
= false;
547 theApp
->serverconnect
->ConnectToAnyServer();
549 if (m_startKADWhenReady
) {
550 m_startKADWhenReady
= false;
553 theApp
->ShowConnectionState(true); // update connect button
554 if (thePrefs::GetSrcSeedsOn()) {
555 theApp
->downloadqueue
->LoadSourceSeeds();
557 // Trigger filter update if configured
558 if (m_updateAfterLoading
) {
559 m_updateAfterLoading
= false;
560 Update(thePrefs::IPFilterURL());
564 // File_checked_for_headers