1
|
<?php
|
2
|
/*
|
3
|
* pfblockerng.inc
|
4
|
*
|
5
|
* part of pfSense (https://www.pfsense.org)
|
6
|
* Copyright (c) 2015-2025 Rubicon Communications, LLC (Netgate)
|
7
|
* Copyright (c) 2015-2024 BBcan177@gmail.com
|
8
|
* All rights reserved.
|
9
|
*
|
10
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
11
|
* you may not use this file except in compliance with the License.
|
12
|
* You may obtain a copy of the License at
|
13
|
*
|
14
|
* http://www.apache.org/licenses/LICENSE-2.0
|
15
|
*
|
16
|
* Unless required by applicable law or agreed to in writing, software
|
17
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
18
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
19
|
* See the License for the specific language governing permissions and
|
20
|
* limitations under the License.
|
21
|
*/
|
22
|
|
23
|
require_once('config.lib.inc');
|
24
|
require_once('util.inc');
|
25
|
require_once('functions.inc');
|
26
|
require_once('pkg-utils.inc');
|
27
|
require_once('pfsense-utils.inc');
|
28
|
require_once('globals.inc');
|
29
|
require_once('services.inc');
|
30
|
require_once('service-utils.inc');
|
31
|
if (file_exists('/usr/local/pkg/pfblockerng/pfblockerng_extra.inc')) {
|
32
|
require_once('/usr/local/pkg/pfblockerng/pfblockerng_extra.inc'); // 'include functions' not yet merged into pfSense
|
33
|
}
|
34
|
|
35
|
global $g, $pfb;
|
36
|
if (!is_array($pfb)) {
|
37
|
$pfb = array();
|
38
|
}
|
39
|
|
40
|
// Folders
|
41
|
$pfb['dbdir'] = "{$g['vardb_path']}/pfblockerng";
|
42
|
$pfb['aliasdir'] = "{$g['vardb_path']}/aliastables";
|
43
|
$pfb['logdir'] = "{$g['varlog_path']}/pfblockerng";
|
44
|
$pfb['etdir'] = "{$pfb['dbdir']}/ET";
|
45
|
$pfb['nativedir'] = "{$pfb['dbdir']}/native";
|
46
|
$pfb['denydir'] = "{$pfb['dbdir']}/deny";
|
47
|
$pfb['matchdir'] = "{$pfb['dbdir']}/match";
|
48
|
$pfb['permitdir'] = "{$pfb['dbdir']}/permit";
|
49
|
$pfb['origdir'] = "{$pfb['dbdir']}/original";
|
50
|
$pfb['dnsdir'] = "{$pfb['dbdir']}/dnsbl";
|
51
|
$pfb['dnsorigdir'] = "{$pfb['dbdir']}/dnsblorig";
|
52
|
$pfb['dnsalias'] = "{$pfb['dbdir']}/dnsblalias";
|
53
|
$pfb['geoipshare'] = '/usr/local/share/GeoIP';
|
54
|
$pfb['ccdir'] = '/usr/local/share/GeoIP/cc';
|
55
|
$pfb['ccdir_tmp'] = '/tmp/geoip_cc';
|
56
|
$pfb['dnsbl_tmp'] = '/tmp/dnsbl_tmp';
|
57
|
$pfb['dnsbl_tmpdir'] = '/tmp/DNSBL_TMP';
|
58
|
|
59
|
// Application Paths
|
60
|
$pfb['grep'] = '/usr/bin/grep';
|
61
|
$pfb['ggrep'] = '/usr/local/bin/ggrep'; // textproc/gnugrep
|
62
|
$pfb['awk'] = '/usr/bin/awk';
|
63
|
$pfb['cut'] = '/usr/bin/cut';
|
64
|
$pfb['sed'] = '/usr/bin/sed';
|
65
|
$pfb['cat'] = '/bin/cat';
|
66
|
$pfb['ls'] = '/bin/ls';
|
67
|
$pfb['pfctl'] = '/sbin/pfctl';
|
68
|
|
69
|
// Folder Array
|
70
|
$pfb['folder_array'] = array( "{$pfb['dbdir']}", "{$pfb['logdir']}", "{$pfb['ccdir']}", "{$pfb['origdir']}", "{$pfb['nativedir']}",
|
71
|
"{$pfb['denydir']}", "{$pfb['matchdir']}","{$pfb['permitdir']}", "{$pfb['aliasdir']}",
|
72
|
"{$pfb['dnsdir']}", "{$pfb['dnsorigdir']}", "{$pfb['dnsalias']}");
|
73
|
|
74
|
// Files
|
75
|
$pfb['log'] = "{$pfb['logdir']}/pfblockerng.log";
|
76
|
$pfb['dnslog'] = "{$pfb['logdir']}/dnsbl.log";
|
77
|
$pfb['dnsreplylog'] = "{$pfb['logdir']}/dns_reply.log";
|
78
|
$pfb['ip_blocklog'] = "{$pfb['logdir']}/ip_block.log";
|
79
|
$pfb['ip_permitlog'] = "{$pfb['logdir']}/ip_permit.log";
|
80
|
$pfb['ip_matchlog'] = "{$pfb['logdir']}/ip_match.log";
|
81
|
$pfb['unilog'] = "{$pfb['logdir']}/unified.log";
|
82
|
$pfb['errlog'] = "{$pfb['logdir']}/error.log";
|
83
|
$pfb['pyerrlog'] = "{$pfb['logdir']}/py_error.log";
|
84
|
$pfb['extraslog'] = "{$pfb['logdir']}/extras.log";
|
85
|
$pfb['dnsbl_parse_err'] = "{$pfb['logdir']}/dnsbl_parsed_error.log";
|
86
|
|
87
|
$pfb['master'] = "{$pfb['dbdir']}/masterfile";
|
88
|
$pfb['supptxt'] = "{$pfb['dbdir']}/pfbsuppression.txt";
|
89
|
$pfb['dnsbl_supptxt'] = "{$pfb['dbdir']}/pfbdnsblsuppression.txt";
|
90
|
$pfb['geoip_isos'] = "{$pfb['dbdir']}/geoip.txt";
|
91
|
$pfb['dnsbl_info'] = '/var/unbound/pfb_py_dnsbl.sqlite';
|
92
|
$pfb['dnsbl_resolver'] = '/var/unbound/pfb_py_resolver.sqlite';
|
93
|
$pfb['dnsbl_cache'] = '/var/unbound/pfb_py_cache.sqlite';
|
94
|
$pfb['asn_cache'] = "{$pfb['dbdir']}/asn_cache.sqlite";
|
95
|
$pfb['ip_cache'] = "{$pfb['dbdir']}/ip_cache.sqlite";
|
96
|
|
97
|
$pfb['script'] = '/usr/local/pkg/pfblockerng/pfblockerng.sh';
|
98
|
$pfb['feeds'] = '/usr/local/www/pfblockerng/pfblockerng_feeds.json';
|
99
|
$pfb['aliasarchive'] = '/usr/local/etc/aliastables.tar.bz2';
|
100
|
|
101
|
$pfb['dnsbl_tld_txt'] = "{$pfb['dnsdir']}/DNSBL_TLD.txt";
|
102
|
$pfb['dnsbl_tld_data'] = '/usr/local/pkg/pfblockerng/dnsbl_tld';
|
103
|
$pfb['dnsbl_conf'] = '/var/unbound/pfb_dnsbl_lighty.conf';
|
104
|
$pfb['dnsbl_cert'] = '/var/unbound/dnsbl_cert.pem';
|
105
|
$pfb['unbound_py_conf'] = '/var/unbound/pfb_unbound.ini';
|
106
|
$pfb['unbound_py_wh'] = '/var/unbound/pfb_py_whitelist.txt';
|
107
|
$pfb['unbound_py_data'] = '/var/unbound/pfb_py_data.txt';
|
108
|
$pfb['unbound_py_zone'] = '/var/unbound/pfb_py_zone.txt';
|
109
|
$pfb['unbound_py_ss'] = '/var/unbound/pfb_py_ss.txt';
|
110
|
$pfb['unbound_py_count']= '/var/unbound/pfb_py_count';
|
111
|
|
112
|
$pfb['dnsbl_safesearch'] = '/usr/local/pkg/pfblockerng/pfb_dnsbl.safesearch.conf';
|
113
|
$pfb['dnsbl_youtube_restrict'] = '/usr/local/pkg/pfblockerng/pfb_dnsbl.youtube_restrict.conf';
|
114
|
$pfb['dnsbl_youtube_restrictmoderate'] = '/usr/local/pkg/pfblockerng/pfb_dnsbl.youtube_restrictmoderate.conf';
|
115
|
$pfb['dnsbl_doh'] = '/usr/local/pkg/pfblockerng/pfb_dnsbl.doh.conf';
|
116
|
|
117
|
// tmp files
|
118
|
$pfb['geoip_tmp'] = '/tmp/pfb_continent';
|
119
|
$pfb['ip_unlock'] = '/tmp/ip_unlock';
|
120
|
|
121
|
$pfb['dnsbl_tld_remove'] = '/tmp/dnsbl_tld_remove';
|
122
|
$pfb['dnsbl_add'] = '/tmp/dnsbl_add';
|
123
|
$pfb['dnsbl_add_zone'] = '/tmp/dnsbl_add_zone';
|
124
|
$pfb['dnsbl_add_data'] = '/tmp/dnsbl_add_data';
|
125
|
$pfb['dnsbl_remove'] = '/tmp/dnsbl_remove';
|
126
|
$pfb['dnsbl_remove_zone'] = '/tmp/dnsbl_remove_zone';
|
127
|
$pfb['dnsbl_remove_data'] = '/tmp/dnsbl_remove_data';
|
128
|
$pfb['dnsbl_unlock'] = '/tmp/dnsbl_unlock';
|
129
|
$pfb['states_tmp'] = '/tmp/pfb_states';
|
130
|
|
131
|
// Unbound files and folders
|
132
|
$pfb['dnsbl_file'] = '/var/unbound/pfb_dnsbl'; // Filename Extension not referenced
|
133
|
$pfb['dnsbldir'] = '/var/unbound';
|
134
|
|
135
|
// Array definitions
|
136
|
$pfb['continents'] = array ( 'Top Spammers' => 'pfB_Top',
|
137
|
'Africa' => 'pfB_Africa',
|
138
|
'Antarctica' => 'pfB_Antarctica',
|
139
|
'Asia' => 'pfB_Asia',
|
140
|
'Europe' => 'pfB_Europe',
|
141
|
'North America' => 'pfB_NAmerica',
|
142
|
'Oceania' => 'pfB_Oceania',
|
143
|
'South America' => 'pfB_SAmerica',
|
144
|
'Proxy and Satellite' => 'pfB_PS'
|
145
|
);
|
146
|
|
147
|
$pfb['continent_list'] = array_flip(array('pfB_Africa', 'pfB_Antarctica', 'pfB_Asia', 'pfB_Europe', 'pfB_NAmerica', 'pfB_Oceania', 'pfB_SAmerica', 'pfB_Top'));
|
148
|
|
149
|
// Base rule array
|
150
|
$pfb['base_rule_reg'] = array('ipprotocol' => 'inet');
|
151
|
|
152
|
// Floating rules, base rule array
|
153
|
$pfb['base_rule_float'] = array('quick' => 'yes', 'floating' => 'yes', 'ipprotocol' => 'inet');
|
154
|
|
155
|
// Define Arrays for managing the IP mastefile
|
156
|
foreach (array('existing', 'actual') as $pftype) {
|
157
|
$pfb[$pftype]['match'] = array();
|
158
|
$pfb[$pftype]['permit'] = array();
|
159
|
$pfb[$pftype]['deny'] = array();
|
160
|
$pfb[$pftype]['native'] = array();
|
161
|
$pfb[$pftype]['dnsbl'] = array();
|
162
|
}
|
163
|
|
164
|
// Default cURL options
|
165
|
$pfb['curl_defaults'] = array( CURLOPT_USERAGENT => 'pfSense/pfBlockerNG cURL download agent-' . system_get_uniqueid(),
|
166
|
CURLOPT_SSL_CIPHER_LIST => 'TLSv1.3, TLSv1.2',
|
167
|
CURLOPT_FOLLOWLOCATION => true,
|
168
|
CURLOPT_SSL_VERIFYPEER => true,
|
169
|
CURLOPT_SSL_VERIFYHOST => true,
|
170
|
CURLOPT_FRESH_CONNECT => true,
|
171
|
CURLOPT_FILETIME => true,
|
172
|
CURLOPT_TCP_NODELAY => true,
|
173
|
CURLOPT_CONNECTTIMEOUT => 15,
|
174
|
CURLOPT_AUTOREFERER => true,
|
175
|
CURLOPT_MAXREDIRS => 10,
|
176
|
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_NONE,
|
177
|
CURLOPT_FORBID_REUSE => true,
|
178
|
CURLOPT_SSL_ENABLE_ALPN => true,
|
179
|
CURLOPT_SSL_ENABLE_NPN => true,
|
180
|
);
|
181
|
|
182
|
// RFC7231 HTTP response codes
|
183
|
$pfb['rfc7231'] = array(
|
184
|
1 => 'Unsupported Protocol', 2 => 'Early Initilization failed', 3 => 'Malformed URL',
|
185
|
4 => 'Requested feature not found', 5 => 'Could not Resolve Proxy', 6 => 'Could not Resolve Host',
|
186
|
7 => 'Failed to connect', 8 => 'Failed to parse data', 9 => 'Denied access',
|
187
|
10 => 'FTP failed', 11 => 'FTP password failure', 12 => 'FTP Accept timeout',
|
188
|
13 => 'FTP PASV failure', 14 => 'FTP 227 Format failure', 15 => 'FTP Host lookup failure',
|
189
|
16 => 'HTTP2 framing layer failure', 17 => 'FTP Could not set transfer mode', 18 => 'File Transfer failure',
|
190
|
19 => 'FTP weird reply', 20 => 'Obsolete error', 21 => 'FTP quote command error',
|
191
|
22 => 'HTTP Returned Error code', 23 => 'Write Error', 24 => 'Obsolete error',
|
192
|
25 => 'FTP failted starting upload', 26 => 'Read Error', 27 => 'Memory allocation request error',
|
193
|
28 => 'Operation timed out', 29 => 'Obsolete error', 30 => 'FTP Port command failure',
|
194
|
31 => 'FTP REST failure', 32 => 'Obsolete error', 33 => 'Range Request failure',
|
195
|
34 => 'HTTP Post error', 35 => 'SSL Connect error', 37 => 'Could not read file',
|
196
|
38 => 'LDAP Cannot bind', 39 => 'LDAP Search failed', 40 => 'Obsolete error',
|
197
|
41 => 'Function not found', 42 => 'Aborted by callback', 43 => 'Bad parameter',
|
198
|
44 => 'Obsolete error', 45 => 'Interface failure', 46 => 'Obsolete error',
|
199
|
47 => 'Too many redirects', 48 => 'Unknown option', 49 => 'Setopt Syntax error',
|
200
|
50 => 'Obsolete error', 51 => 'Obsolete error', 52 => 'Curl got nothing',
|
201
|
53 => 'SSL Engine not found', 54 => 'SSL Engine failure', 55 => 'Failed sending data',
|
202
|
56 => 'Failure receving data', 57 => 'Obsolete error', 58 => 'Local client certificate error',
|
203
|
59 => 'Cipher error', 60 => 'SSL certificate error', 61 => 'Transfer encoding error',
|
204
|
62 => 'Obsolete error', 63 => 'Maximum file size exceeded', 64 => 'FTP SSL level failure',
|
205
|
65 => 'Rewinding operation failed', 66 => 'SSL Engine failure', 67 => 'Login Denied',
|
206
|
68 => 'TFTP server not found', 69 => 'TFTP permission error', 70 => 'Out of disk space on server',
|
207
|
71 => 'Illegal TFTP operation', 72 => 'Unknown TFTP transfer ID', 73 => 'File already exists',
|
208
|
74 => 'TFTP no such user', 75 => 'Obsolete error', 76 => 'Obsolete error',
|
209
|
77 => 'SSL CA certificate error', 78 => 'Remote file not found', 79 => 'SSH connection failure',
|
210
|
80 => 'SSH connection failure', 81 => 'Socket is not ready', 82 => 'CRL Bad File error',
|
211
|
83 => 'SSL Issurer failure', 84 => 'FTP PRET failure', 85 => 'RTSP CSeq number error',
|
212
|
86 => 'RTSP Session mismatch', 87 => 'FTP Bad file list', 88 => 'Chunk callback error',
|
213
|
89 => 'No connection available', 90 => 'Pin Key match failure', 91 => 'Invalid Certificate status',
|
214
|
92 => 'HTTP/2 framing error', 93 => 'Recursive API call error', 94 => 'Authentication function error',
|
215
|
95 => 'HTTP/3 layer error',
|
216
|
96 => 'QUIC connect error', 97 => 'Proxy handshake error', 98 => 'SSL Client certificate required',
|
217
|
99 => 'Unrecoveranle Poll error',
|
218
|
|
219
|
100 => '100 Continue', 101 => '101 Switching Protocols', 102 => '102 Processing',
|
220
|
|
221
|
200 => '200 OK', 201 => '201 Created', 202 => '202 Accepted',
|
222
|
203 => '203 Non-Authoritative Info', 204 => '204 No Content', 205 => '205 Reset Content',
|
223
|
206 => '206 Partial Content', 207 => '207 Multi-Status', 208 => '208 Already Reported',
|
224
|
226 => '226 IM Used',
|
225
|
|
226
|
300 => '300 Multiple Choices', 301 => '301 Moved Permanently', 302 => '302 Found',
|
227
|
303 => '303 See Other', 304 => '304 Not Modified', 305 => '305 Use Proxy',
|
228
|
306 => '306 Switch Proxy', 307 => '307 Temporary Redirect', 308 => '308 Permanent Redirect',
|
229
|
|
230
|
400 => '400 Bad Request', 401 => '401 Unauthorized', 402 => '402 Payment Required',
|
231
|
403 => '403 Forbidden', 404 => '404 Not Found', 405 => '405 Method Not Allowed',
|
232
|
406 => '406 Not Acceptable', 407 => '407 Proxy Authentication Required', 408 => '408 Request Timeout',
|
233
|
409 => '409 Conflict', 410 => '410 Gone', 411 => '411 Length Required',
|
234
|
412 => '412 Precondition Failed', 413 => '413 Request Entity Too Large', 414 => '414 Request-URI Too Long',
|
235
|
415 => '415 Unsupported Media Type', 416 => '416 Requested Range Not Satisfiable', 417 => '417 Expectation Failed',
|
236
|
418 => '418 Im a teapot', 419 => '419 Authentication Timeout', 420 => '420 Method Failure',
|
237
|
421 => '421 Misdirected Request', 422 => '422 Unprocessable Entity', 423 => '423 Locked',
|
238
|
424 => '424 Failed Dependency', 426 => '426 Upgrade Required', 428 => '428 Precondition Required',
|
239
|
429 => '429 Too Many Requests', 431 => '431 Request Header Fields Large', 440 => '440 Login Timeout',
|
240
|
444 => '444 No Response', 449 => '449 Retry With', 450 => '450 Blocked Windows Parental Controls',
|
241
|
451 => '451 Unavailable Legal Reasons', 494 => '494 Request Header too Large', 495 => '495 Cert Error',
|
242
|
496 => '496 No Cert', 497 => '497 HTTP to HTTPS', 498 => '498 Token expired/invalid',
|
243
|
499 => '499 Client Closed Request',
|
244
|
|
245
|
500 => '500 Internal Server Error', 501 => '501 Not Implemented', 502 => '502 Bad Gateway',
|
246
|
503 => '503 Service Unavailable', 504 => '504 Gateway Timeout', 505 => '505 HTTP Version Not Supported',
|
247
|
506 => '506 Variant Also Negotiates', 507 => '507 Insufficient Storage', 508 => '508 Loop Detected',
|
248
|
509 => '509 Bandwidth Limit Exceeded', 510 => '510 Not Extended', 511 => '511 Network Authentication Required',
|
249
|
521 => '521 Web Server is down', 598 => '598 Network read timeout error', 599 => '599 Network connect timeout error',
|
250
|
|
251
|
520 => 'CF 520 Unknown Error', 521 => 'CF 521 Web Server is Down', 522 => 'CF 522 Connection Timed Out',
|
252
|
523 => 'CF 523 Origin is Unreachable', 524 => 'CF 524 A Timeout Occured', 525 => 'CF 525 SSL Handshake Failed',
|
253
|
526 => 'CF 526 Invalid SSL Certificate',527 => 'CF 527 Railgun Error'
|
254
|
);
|
255
|
|
256
|
// File download Mime-Types
|
257
|
$pfb['mime_types'] = array_flip(array( 'inode/x-empty',
|
258
|
'text/x-file',
|
259
|
'text/plain',
|
260
|
'text/html',
|
261
|
'text/xml',
|
262
|
'text/csv',
|
263
|
'application/csv',
|
264
|
'application/json',
|
265
|
'application/x-ndjson',
|
266
|
'application/x-tar',
|
267
|
'application/gzip',
|
268
|
'application/x-gzip',
|
269
|
'application/x-bzip2',
|
270
|
'application/zip'));
|
271
|
|
272
|
// pfb_filter constants
|
273
|
define('PFB_FILTER_HTML', 1);
|
274
|
define('PFB_FILTER_URL', 2);
|
275
|
define('PFB_FILTER_WORD', 3);
|
276
|
define('PFB_FILTER_WORD_DOT', 4);
|
277
|
define('PFB_FILTER_TLD', 5);
|
278
|
define('PFB_FILTER_DOMAIN', 6);
|
279
|
define('PFB_FILTER_HOSTNAME', 7);
|
280
|
define('PFB_FILTER_IPV4', 8);
|
281
|
define('PFB_FILTER_IP', 9);
|
282
|
define('PFB_FILTER_ALPHA', 10);
|
283
|
define('PFB_FILTER_ALNUM', 11);
|
284
|
define('PFB_FILTER_NUM', 12);
|
285
|
define('PFB_FILTER_CSV', 13);
|
286
|
define('PFB_FILTER_CSV_WHOIS', 14);
|
287
|
define('PFB_FILTER_CSV_CRON', 15);
|
288
|
define('PFB_FILTER_FILE_MIME_COMPARE', 16);
|
289
|
define('PFB_FILTER_FILE_MIME', 17);
|
290
|
define('PFB_FILTER_FILE_MIME_COMPRESSED', 18);
|
291
|
define('PFB_FILTER_ATYPE', 19);
|
292
|
define('PFB_FILTER_HEX_COLOR', 20);
|
293
|
define('PFB_FILTER_ON_OFF', 21);
|
294
|
|
295
|
// Function to filter/sanitize user input
|
296
|
function pfb_filter($input, $type, $reference='Unknown', $default='', $escape=FALSE) {
|
297
|
global $pfb;
|
298
|
|
299
|
$header = "\n PFB_FILTER - {$type} | {$reference} [ NOW ]";
|
300
|
|
301
|
$return_type = $default;
|
302
|
if (in_array($type, array(PFB_FILTER_URL, PFB_FILTER_FILE_MIME_COMPARE, PFB_FILTER_FILE_MIME, PFB_FILTER_FILE_MIME_COMPRESSED))) {
|
303
|
$return_type = FALSE;
|
304
|
}
|
305
|
|
306
|
if (!in_array($type, array(PFB_FILTER_ON_OFF, PFB_FILTER_NUM))) {
|
307
|
if (empty($input) || is_null($input)) {
|
308
|
return $return_type;
|
309
|
}
|
310
|
}
|
311
|
|
312
|
// Check for control characters
|
313
|
if (is_array($input)) {
|
314
|
foreach ($input as $vline) {
|
315
|
if (preg_match("/[\p{C}]+/", $vline)) {
|
316
|
pfb_logger("{$header} Control characters found [ " . htmlspecialchars($vline) . " ]", 6);
|
317
|
return $return_type;
|
318
|
}
|
319
|
}
|
320
|
}
|
321
|
else {
|
322
|
if (preg_match("/[\p{C}]+/", $input)) {
|
323
|
pfb_logger("{$header} Control characters found [ " . htmlspecialchars($input) . " ]", 6);
|
324
|
return $return_type;
|
325
|
}
|
326
|
}
|
327
|
|
328
|
$result = FALSE;
|
329
|
switch ($type) {
|
330
|
case PFB_FILTER_HTML:
|
331
|
$result = htmlspecialchars(trim($input));
|
332
|
break;
|
333
|
case PFB_FILTER_URL:
|
334
|
// Validate URL input
|
335
|
$is_RSYNC = FALSE;
|
336
|
if (strpos($input, '::') !== FALSE && !$escape) {
|
337
|
$rsync = explode('::', $input);
|
338
|
if (count($rsync) == 2 && !empty($rsync[0]) && filter_var($rsync[0], FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)) {
|
339
|
$is_RSYNC = TRUE;
|
340
|
}
|
341
|
}
|
342
|
|
343
|
if ($is_RSYNC || filter_var($input, FILTER_VALIDATE_URL)) {
|
344
|
if ($is_RSYNC) {
|
345
|
$data = array('host' => $rsync[0], 'path' => $rsync[1]);
|
346
|
} else {
|
347
|
$data = parse_url($input);
|
348
|
if (!in_array($data['scheme'], array('http', 'https', 'rsync', 'ftp'))) {
|
349
|
pfb_logger("{$header} Invalid URL Scheme [ " . htmlspecialchars($input) . " ]", 6);
|
350
|
return FALSE;
|
351
|
}
|
352
|
}
|
353
|
|
354
|
$validate_list = '';
|
355
|
if (!$data || !is_array($data)) {
|
356
|
pfb_logger("{$header} Invalid URL (missing data) [ " . htmlspecialchars($input) . " ]", 6);
|
357
|
return FALSE;
|
358
|
} elseif (!isset($data['host']) || empty($data['host'])) {
|
359
|
pfb_logger("{$header} Invalid URL (missing hostname) [ " . htmlspecialchars($input) . " ]", 6);
|
360
|
return FALSE;
|
361
|
} elseif (is_ipaddr($data['host'])) {
|
362
|
$validate_list = array(array('type' => 'IP', 'data' => $data['host']));
|
363
|
} else {
|
364
|
$validate_list = resolve_host_addresses("{$data['host']}.");
|
365
|
}
|
366
|
|
367
|
$pfsense_configured = FALSE;
|
368
|
if (!empty($validate_list) && is_array($validate_list)) {
|
369
|
foreach ($validate_list as $validate) {
|
370
|
if ($validate['type'] == 'CNAME' && !empty($validate['data'])) {
|
371
|
|
372
|
if (is_ipaddr($validate['data'])) {
|
373
|
$cname_list = resolve_host_addresses($validate['data']);
|
374
|
} else {
|
375
|
$cname_list = resolve_host_addresses("{$validate['data']}.");
|
376
|
}
|
377
|
|
378
|
if (!empty($cname_list) && is_array($cname_list)) {
|
379
|
foreach ($cname_list as $cname) {
|
380
|
if (!empty($cname['data']) && is_ipaddr_configured($cname['data'])) {
|
381
|
$pfsense_configured = TRUE;
|
382
|
break 2;
|
383
|
}
|
384
|
}
|
385
|
}
|
386
|
}
|
387
|
if (!empty($validate['data']) && is_ipaddr_configured($validate['data'])) {
|
388
|
$pfsense_configured = TRUE;
|
389
|
break;
|
390
|
}
|
391
|
}
|
392
|
}
|
393
|
else {
|
394
|
// Cannot resolve host
|
395
|
pfb_logger("{$header} Invalid URL (cannot resolve) [ " . htmlspecialchars($input) . " ]", 6);
|
396
|
return FALSE;
|
397
|
}
|
398
|
|
399
|
$path = pathinfo($data['path'], PATHINFO_DIRNAME);
|
400
|
|
401
|
// Validate only pfSense URLS (localfile check and Alerts Tab refresh)
|
402
|
if ($escape) {
|
403
|
if ($pfsense_configured ||
|
404
|
($data['host'] == '127.0.0.1') ||
|
405
|
($data['host'] == '::1')) {
|
406
|
|
407
|
// Allow '/usr/local/www'
|
408
|
if ($path == '/') {
|
409
|
return TRUE;
|
410
|
}
|
411
|
}
|
412
|
|
413
|
if ($reference == 'alerts refresh') {
|
414
|
pfb_logger("{$header} Invalid URL (alerts tab) [ " . htmlspecialchars($input) . " ]", 6);
|
415
|
}
|
416
|
return FALSE;
|
417
|
}
|
418
|
|
419
|
// pfSense URL
|
420
|
if (($pfsense_configured) ||
|
421
|
($data['host'] == '127.0.0.1') ||
|
422
|
($data['host'] == '::1')) {
|
423
|
|
424
|
// Allow '/usr/local/www'
|
425
|
if ($path == '/') {
|
426
|
return TRUE;
|
427
|
}
|
428
|
pfb_logger("{$header} Invalid URL (not allowed) [ " . htmlspecialchars($input) . " ]", 6);
|
429
|
return FALSE;
|
430
|
}
|
431
|
|
432
|
// all other remote URLs no path validation
|
433
|
return TRUE;
|
434
|
}
|
435
|
|
436
|
// Local file path validation
|
437
|
else {
|
438
|
$path = pathinfo($input, PATHINFO_DIRNAME) . '/';
|
439
|
$allowed_path = array_flip(array( '/var/db/pfblockerng/',
|
440
|
'/var/db/pfblockerng/deny/',
|
441
|
'/var/db/pfblockerng/permit/',
|
442
|
'/var/db/pfblockerng/match/',
|
443
|
'/var/db/pfblockerng/native/',
|
444
|
'/var/db/pfblockerng/original/',
|
445
|
'/var/db/pfblockerng/dnsbl/',
|
446
|
'/var/db/pfblockerng/dnsblorig/',
|
447
|
'/var/db/pfblockerng/ET/',
|
448
|
'/var/db/pfblockerng/ut1/',
|
449
|
'/var/db/pfblockerng/shallalist/',
|
450
|
'/usr/local/www/',
|
451
|
'/usr/local/share/GeoIP/',
|
452
|
'/usr/local/share/GeoIP/cc/' ));
|
453
|
if (isset($allowed_path[$path])) {
|
454
|
return TRUE;
|
455
|
}
|
456
|
}
|
457
|
pfb_logger("\n[PFB_FILTER - {$type}] Invalid URL (not allowed2) [ " . htmlspecialchars($input) . " ]", 6);
|
458
|
return FALSE;
|
459
|
break;
|
460
|
case PFB_FILTER_WORD:
|
461
|
// Validate for 'Any word character (letter, number, underscore)'
|
462
|
if (!preg_match("/\W/", $input)) {
|
463
|
$result = htmlspecialchars($input);
|
464
|
}
|
465
|
break;
|
466
|
case PFB_FILTER_WORD_DOT:
|
467
|
// Validate for '(letter, number, underscore, dash) dot (letter, number, underscore, dash)'
|
468
|
if (preg_match("/^[a-zA-Z0-9_\-]+\.{1}[a-zA-Z0-9_\-]+$/", $input)) {
|
469
|
$result = htmlspecialchars($input);
|
470
|
}
|
471
|
break;
|
472
|
case PFB_FILTER_TLD:
|
473
|
// Validate TLD
|
474
|
if (preg_match("/^[a-zA-Z0-9_\.\-]+$/", $input)) {
|
475
|
$result = htmlspecialchars($input);
|
476
|
}
|
477
|
break;
|
478
|
case PFB_FILTER_DOMAIN:
|
479
|
// Validate domain
|
480
|
if ((strpos($input, '.') !== FALSE) && // Exclude no dots
|
481
|
(strpos($input, '..') === FALSE) && // Exclude double dot
|
482
|
(strlen($input) < 255) && // Validate length of domain (Max 255 chars)
|
483
|
(pfb_validate_domain_labels($input) !== FALSE) && // Validate length of labels (Max of 63 chars)
|
484
|
(preg_match("/^[a-zA-Z0-9_\.\-]+$/", $input))) { // Exclude any other characters
|
485
|
$result = TRUE;
|
486
|
}
|
487
|
if ($result) {
|
488
|
$result = htmlspecialchars($input);
|
489
|
}
|
490
|
break;
|
491
|
case PFB_FILTER_HOSTNAME:
|
492
|
// Validate hostname
|
493
|
if (is_hostname($input)) {
|
494
|
$result = htmlspecialchars($input);
|
495
|
}
|
496
|
break;
|
497
|
case PFB_FILTER_IPV4:
|
498
|
// Validate IPv4
|
499
|
if (is_ipaddrv4($input)) {
|
500
|
$result = htmlspecialchars($input);
|
501
|
}
|
502
|
break;
|
503
|
case PFB_FILTER_IP:
|
504
|
// Validate any IP address v4/v6
|
505
|
if (is_ipaddr($input)) {
|
506
|
$result = htmlspecialchars($input);
|
507
|
}
|
508
|
break;
|
509
|
case PFB_FILTER_ALPHA:
|
510
|
// Validate input is alphabetic only
|
511
|
if (ctype_alpha($input)) {
|
512
|
$result = htmlspecialchars($input);
|
513
|
}
|
514
|
break;
|
515
|
case PFB_FILTER_ALNUM:
|
516
|
// Validate input is alphanumeric only
|
517
|
if (ctype_alnum($input)) {
|
518
|
$result = htmlspecialchars($input);
|
519
|
}
|
520
|
break;
|
521
|
case PFB_FILTER_NUM:
|
522
|
// Validate input is number only
|
523
|
if (preg_match("/^[0-9]+$/", $input)) {
|
524
|
$result = htmlspecialchars($input);
|
525
|
}
|
526
|
break;
|
527
|
case PFB_FILTER_CSV:
|
528
|
// Validate CSV string
|
529
|
if (preg_match("/^[a-zA-Z0-9,_-]+$/", $input)) {
|
530
|
$result = htmlspecialchars($input);
|
531
|
}
|
532
|
break;
|
533
|
case PFB_FILTER_CSV_WHOIS:
|
534
|
// Validate Whoisconvert string
|
535
|
if (preg_match("/^[a-zA-Z0-9,\._\-]+$/", $input)) {
|
536
|
$result = htmlspecialchars($input);
|
537
|
}
|
538
|
break;
|
539
|
case PFB_FILTER_CSV_CRON:
|
540
|
// Validate CSV string (cron hour setting)
|
541
|
if ($input == '*' || preg_match("/^[0-9,]+$/", $input)) {
|
542
|
$result = htmlspecialchars($input);
|
543
|
}
|
544
|
break;
|
545
|
case PFB_FILTER_FILE_MIME_COMPARE:
|
546
|
// Validate mime-type
|
547
|
// $input [0] path/file, [1] mime-type to be validated against
|
548
|
if (isset($retval)) {
|
549
|
unset($retval);
|
550
|
}
|
551
|
if (isset($output)) {
|
552
|
unset($output);
|
553
|
}
|
554
|
if (!file_exists($input[0])) {
|
555
|
pfb_logger("{$header} Invalid Mime-type (file missing): [" . htmlspecialchars($input[0]) . "|" . htmlspecialchars($input[1]) . "]", 2);
|
556
|
return FALSE;
|
557
|
}
|
558
|
exec("/usr/bin/file -b --mime-type " . escapeshellarg($input[0]) . " 2>&1", $output, $retval);
|
559
|
if ($retval != 0 || empty($output[0]) || $output[0] !== $input[1]) {
|
560
|
pfb_logger("{$header} Invalid Mime-type: [" . htmlspecialchars($input[0]) . "|" . htmlspecialchars($input[1]) . "]", 2);
|
561
|
return FALSE;
|
562
|
}
|
563
|
return TRUE;
|
564
|
break;
|
565
|
case PFB_FILTER_FILE_MIME:
|
566
|
// Validate File Mime-types
|
567
|
// $input [0] path/file escaped, [1] path/file [2] URL
|
568
|
|
569
|
if (isset($retval)) {
|
570
|
unset($retval);
|
571
|
}
|
572
|
if (isset($output)) {
|
573
|
unset($output);
|
574
|
}
|
575
|
if (!file_exists($input[1])) {
|
576
|
pfb_logger("{$header} Downloaded file not found: [" . htmlspecialchars($input[0]) . "|" . htmlspecialchars($input[1]) . "]", 2);
|
577
|
return FALSE;
|
578
|
}
|
579
|
exec("/usr/bin/file -b --mime-type {$input[0]} 2>&1", $output, $retval);
|
580
|
if ($retval != 0 || empty($output[0]) || !isset($pfb['mime_types'][$output[0]])) {
|
581
|
|
582
|
// Exceptions
|
583
|
$hostname = parse_url($input[2], PHP_URL_HOST);
|
584
|
if ($output[0] == 'text/x-asm' &&
|
585
|
($hostname == 'easylist-downloads.adblockplus.org' || $hostname == 'easylist.to' )) {
|
586
|
$output[0] = 'text/plain';
|
587
|
}
|
588
|
elseif ($output[0] == 'application/octet-stream' && $hostname == 'ipinfo.io') {
|
589
|
$output[0] = 'text/plain';
|
590
|
}
|
591
|
else {
|
592
|
pfb_logger("\n[PFB_FILTER - {$type}] Failed or invalid Mime Type: [" . htmlspecialchars($output[0]) . "|" . htmlspecialchars($retval) . "]", 2);
|
593
|
unlink_if_exists($input[1]);
|
594
|
return FALSE;
|
595
|
}
|
596
|
}
|
597
|
$result = htmlspecialchars($output[0]);
|
598
|
break;
|
599
|
case PFB_FILTER_FILE_MIME_COMPRESSED:
|
600
|
// Validate File Mime-types in compressed files
|
601
|
// $input [0] path/file escaped, [1] path/file [2] URL
|
602
|
if (isset($retval)) {
|
603
|
unset($retval);
|
604
|
}
|
605
|
if (isset($output)) {
|
606
|
unset($output);
|
607
|
}
|
608
|
if (!file_exists($input[1])) {
|
609
|
pfb_logger("{$header} Downloaded file not found: [" . htmlspecialchars($input[0]) . "|" . htmlspecialchars($input[1]) . "]", 2);
|
610
|
return FALSE;
|
611
|
}
|
612
|
exec("/usr/bin/file -bZ --mime-type {$input[0]} 2>&1", $output, $retval);
|
613
|
if ($retval != 0 || empty($output[0]) || !isset($pfb['mime_types'][$output[0]])) {
|
614
|
pfb_logger("{$header} Failed or invalid Mime Type Compressed: [" . htmlspecialchars($output[0]) . "|" . htmlspecialchars($retval) . "]", 2);
|
615
|
unlink_if_exists($input[1]);
|
616
|
return FALSE;
|
617
|
}
|
618
|
$result = htmlspecialchars($output[0]);
|
619
|
break;
|
620
|
case PFB_FILTER_ATYPE:
|
621
|
// Validate atype entry in category_edit.php
|
622
|
if (preg_match("/^[a-zA-Z0-9\.|_]+$/", $input)) {
|
623
|
$result = htmlspecialchars($input);
|
624
|
}
|
625
|
break;
|
626
|
case PFB_FILTER_HEX_COLOR:
|
627
|
// Alerts Tab - Hex code validation
|
628
|
if ($input == 'none' || preg_match("/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/", $input)) {
|
629
|
$result = htmlspecialchars($input);
|
630
|
}
|
631
|
break;
|
632
|
case PFB_FILTER_ON_OFF:
|
633
|
// Validate on or off
|
634
|
if ($input == 'on' || $input == '') {
|
635
|
$result = htmlspecialchars($input);
|
636
|
} else {
|
637
|
pfb_logger("{$header} Invalid on/off [ " . htmlspecialchars($input) . " ]", 6);
|
638
|
}
|
639
|
break;
|
640
|
default:
|
641
|
pfb_logger("{$header} Type invalid [ " . htmlspecialchars($input) . " ]", 6);
|
642
|
break;
|
643
|
}
|
644
|
|
645
|
if ($result && $escape) {
|
646
|
$result = escapeshellarg($result);
|
647
|
}
|
648
|
|
649
|
// Log validation errors
|
650
|
if (!empty($input) && empty($result) &&
|
651
|
!in_array($type, array(PFB_FILTER_URL, PFB_FILTER_FILE_MIME_COMPARE, PFB_FILTER_FILE_MIME, PFB_FILTER_FILE_MIME_COMPRESSED, PFB_FILTER_ON_OFF, PFB_FILTER_NUM))) {
|
652
|
|
653
|
// Exceptions
|
654
|
if (in_array($reference, array( 'DNSBL_Download'))) {
|
655
|
//
|
656
|
} else {
|
657
|
pfb_logger("{$header} Failed validation [ " . htmlspecialchars($input) . " ]", 6);
|
658
|
}
|
659
|
}
|
660
|
|
661
|
return $result == FALSE ? $default : $result;
|
662
|
}
|
663
|
|
664
|
|
665
|
// Validate length of labels (Max of 63 chars)
|
666
|
function pfb_validate_domain_labels($input) {
|
667
|
|
668
|
$labels = explode('.', $input);
|
669
|
if (!empty($labels) && is_array($labels)) {
|
670
|
foreach ($labels as $label) {
|
671
|
if (strlen($label) > 63) {
|
672
|
return FALSE;
|
673
|
}
|
674
|
}
|
675
|
return TRUE;
|
676
|
}
|
677
|
return FALSE;
|
678
|
}
|
679
|
|
680
|
|
681
|
// [ $pfb ] pfBlockerNG global array. This needs to be called to get the updated settings.
|
682
|
function pfb_global() {
|
683
|
global $g, $pfb;
|
684
|
|
685
|
// Create folders if not exist.
|
686
|
foreach ($pfb['folder_array'] as $folder) {
|
687
|
safe_mkdir("{$folder}", 0755);
|
688
|
}
|
689
|
|
690
|
// Reload config.xml to get any recent changes
|
691
|
config_read_file(false, true);
|
692
|
|
693
|
// General variables
|
694
|
$pfb['config'] = config_get_path('installedpackages/pfblockerng/config/0', []);
|
695
|
$pfb['ipconfig'] = config_get_path('installedpackages/pfblockerngipsettings/config/0', []);
|
696
|
$pfb['dnsblconfig'] = config_get_path('installedpackages/pfblockerngdnsblsettings/config/0', []);
|
697
|
$pfb['blconfig'] = config_get_path('installedpackages/pfblockerngblacklist', []);
|
698
|
$pfb['config_global'] = config_get_path('installedpackages/pfblockerngglobal', []);
|
699
|
|
700
|
$pfb['enable'] = $pfb['config']['enable_cb']; // Enable/Disable of pfBlockerNG
|
701
|
$pfb['keep'] = $pfb['config']['pfb_keep']; // Keep blocklists on pfBlockerNG Disable
|
702
|
$pfb['interval'] = $pfb['config']['pfb_interval'] ?: '1'; // Hour cycle for scheduler
|
703
|
|
704
|
// Validate Cron settings
|
705
|
if (!is_numeric($pfb['interval'])) {
|
706
|
$pfb['interval'] = '1';
|
707
|
}
|
708
|
foreach (array( 'pfb_min' => 'min', // User defined CRON start minute
|
709
|
'pfb_hour' => 'hour', // Start hour of the scheduler
|
710
|
'pfb_dailystart' => '24hour' // Start hour of the 'Once a day' schedule
|
711
|
) as $conf_value => $pfb_value) {
|
712
|
|
713
|
$pfb_variable = $pfb['config'][$conf_value] ?: '0';
|
714
|
if (!is_numeric($pfb_variable)) {
|
715
|
$pfb[$pfb_value] = '0';
|
716
|
} else {
|
717
|
$pfb[$pfb_value] = $pfb_variable;
|
718
|
}
|
719
|
}
|
720
|
|
721
|
$pfb['supp'] = $pfb['ipconfig']['suppression']; // Enable Suppression
|
722
|
$pfb['cc'] = $pfb['ipconfig']['database_cc']; // Disable Country database CRON updates
|
723
|
$pfb['maxmind_locale'] = $pfb['ipconfig']['maxmind_locale'] ?: 'en'; // MaxMind Localized Language setting
|
724
|
$pfb['asn_reporting'] = $pfb['ipconfig']['asn_reporting'] ?: 'disabled'; // ASN Reporting
|
725
|
$pfb['asn_token'] = $pfb['ipconfig']['asn_token'] ?: ''; // ASN Token (IPinfo)
|
726
|
|
727
|
$pfb['maxmind_account'] = pfb_filter($pfb['ipconfig']['maxmind_account'], PFB_FILTER_WORD, 'pfb_global') ?: ''; // Maxmind Account ID
|
728
|
$pfb['maxmind_key'] = pfb_filter($pfb['ipconfig']['maxmind_key'], PFB_FILTER_WORD, 'pfb_global') ?: ''; // Maxmind License Key
|
729
|
|
730
|
$pfb['dnsbl'] = $pfb['dnsblconfig']['pfb_dnsbl']; // Enabled state of DNSBL
|
731
|
$pfb['dnsbl_vip_type'] = $pfb['dnsblconfig']['pfb_dnsvip_type'] ?: 'ipalias'; // Virtual IP type
|
732
|
|
733
|
$pfb['dnsbl_vip_vhid'] = isset($pfb['dnsblconfig']['pfb_dnsvip_vhid']) ? $pfb['dnsblconfig']['pfb_dnsvip_vhid'] : ''; // Virtual IP Carp VHID
|
734
|
$pfb['dnsbl_vip_base'] = isset($pfb['dnsblconfig']['pfb_dnsvip_base']) ? $pfb['dnsblconfig']['pfb_dnsvip_base'] : ''; // Virtual IP Carp advbase
|
735
|
$pfb['dnsbl_vip_skew'] = isset($pfb['dnsblconfig']['pfb_dnsvip_skew']) ? $pfb['dnsblconfig']['pfb_dnsvip_skew'] : ''; // Virtual IP Carp skew
|
736
|
$pfb['dnsbl_vip_pass'] = isset($pfb['dnsblconfig']['pfb_dnsvip_pass']) ? $pfb['dnsblconfig']['pfb_dnsvip_pass'] : ''; // Virtual IP Carp password (if required)
|
737
|
|
738
|
$pfb['dnsbl_iface'] = $pfb['dnsblconfig']['dnsbl_interface']?: 'lo0'; // VIP Local Interface setting
|
739
|
$pfb['dnsbl_vip'] = $pfb['dnsblconfig']['pfb_dnsvip'] ?: ''; // Virtual IP local address
|
740
|
$pfb['dnsbl_v6'] = $pfb['dnsblconfig']['pfb_dnsblv6'] ?: ''; // Enable/Disable DNSBL IPv6
|
741
|
$pfb['dnsbl_port'] = $pfb['dnsblconfig']['pfb_dnsport']; // Lighttpd web server http port setting
|
742
|
$pfb['dnsbl_port_ssl'] = $pfb['dnsblconfig']['pfb_dnsport_ssl']; // Lighttpd web server https port setting
|
743
|
$pfb['dnsbl_alexa'] = $pfb['dnsblconfig']['alexa_enable']; // TOP1M whitelist
|
744
|
$pfb['dnsbl_alexatype'] = $pfb['dnsblconfig']['alexa_type'] ?: 'tranco'; // TOP1M type (Tranco, Alexa or Cisco)
|
745
|
$pfb['dnsbl_res_cache'] = $pfb['dnsblconfig']['pfb_cache']; // DNSBL Option to backup/restore Resolver cache
|
746
|
$pfb['dnsbl_sync'] = $pfb['dnsblconfig']['pfb_dnsbl_sync']; // Live Updates to Resolver without a Reload
|
747
|
$pfb['dnsbl_global_log']= $pfb['dnsblconfig']['global_log'] ?: ''; // Global Logging/Blocking mode
|
748
|
|
749
|
$pfb['dnsbl_mode'] = $pfb['dnsblconfig']['dnsbl_mode']; // DNSBL Mode (Unbound/python mode)
|
750
|
$pfb['dnsbl_py_reply'] = $pfb['dnsblconfig']['pfb_py_reply']; // DNSBL Resolver python DNS Reply logging
|
751
|
$pfb['dnsbl_py_block'] = $pfb['dnsblconfig']['pfb_py_block']; // DNSBL Resolver python blocking mode
|
752
|
$pfb['dnsbl_hsts'] = $pfb['dnsblconfig']['pfb_hsts']; // DNSBL Resolver python block HSTS via Null Block mode
|
753
|
$pfb['dnsbl_idn'] = $pfb['dnsblconfig']['pfb_idn']; // DNSBL Resolver python block IDN domains
|
754
|
$pfb['dnsbl_regex'] = $pfb['dnsblconfig']['pfb_regex']; // DNSBL Resolver python regex
|
755
|
$pfb['dnsbl_regex_list']= $pfb['dnsblconfig']['pfb_regex_list']; // DNSBL Resolver python regex list
|
756
|
$pfb['dnsbl_cname'] = $pfb['dnsblconfig']['pfb_cname']; // DNSBL Resolver python CNAME Validation
|
757
|
$pfb['dnsbl_pytld'] = $pfb['dnsblconfig']['pfb_pytld']; // DNSBL Resolver python TLD Allow option
|
758
|
$pfb['dnsbl_py_nolog'] = $pfb['dnsblconfig']['pfb_py_nolog']; // DNSBL Resolver python - Log events via DNSBL Webserver vs python
|
759
|
$pfb['dnsbl_noaaaa'] = $pfb['dnsblconfig']['pfb_noaaaa']; // DNSBL Resolver python no AAAA
|
760
|
$pfb['dnsbl_noaaaa_list']=$pfb['dnsblconfig']['pfb_noaaaa_list']; // DNSBL Resolver python no AAAA list
|
761
|
|
762
|
$pfb['dnsbl_gp'] = $pfb['dnsblconfig']['pfb_gp']; // DNSBL Resolver python - DNSBL Bypass
|
763
|
$pfb['dnsbl_gp_bypass_list'] = $pfb['dnsblconfig']['pfb_gp_bypass_list']; // DNSBL Resolver python - List of Local IPs to bypass DNSBL
|
764
|
|
765
|
// DNSBL Resolver mode (Unbound/Python)
|
766
|
$pfb['dnsbl_py_blacklist'] = FALSE;
|
767
|
if ($pfb['dnsbl_mode'] == 'dnsbl_python' && $pfb['dnsbl_py_block'] == 'on') {
|
768
|
$pfb['dnsbl_py_blacklist'] = TRUE;
|
769
|
}
|
770
|
|
771
|
// SafeSearch
|
772
|
$pfb['safesearch_enable'] = config_get_path('installedpackages/pfblockerngsafesearch/safesearch_enable', 'Disable');
|
773
|
$pfb['safesearch_youtube'] = config_get_path('installedpackages/pfblockerngsafesearch/safesearch_youtube', 'Disable');
|
774
|
$pfb['safesearch_doh'] = config_get_path('installedpackages/pfblockerngsafesearch/safesearch_doh', 'Disable');
|
775
|
$pfb['safesearch_doh_list'] = explode(',', config_get_path('installedpackages/pfblockerngsafesearch/safesearch_doh_list', ''));
|
776
|
|
777
|
// DNSBL SafeSearch
|
778
|
$pfb['dnsbl_safe_search'] = FALSE;
|
779
|
if ($pfb['safesearch_enable'] !== 'Disable' ||
|
780
|
$pfb['safesearch_youtube'] !== 'Disable' ||
|
781
|
$pfb['safesearch_doh'] !== 'Disable') {
|
782
|
$pfb['dnsbl_safe_search'] = TRUE;
|
783
|
}
|
784
|
|
785
|
// External DNS Server for TLD drill and CNAME Queries
|
786
|
$pfb['extdns'] = pfb_filter($pfb['config_global']['pfbextdns'], PFB_FILTER_IPV4, 'pfb_global', '8.8.8.8');
|
787
|
|
788
|
// Unbound chroot cmd
|
789
|
$pfb['chroot_cmd'] = "/usr/sbin/chroot -u unbound -g unbound / /usr/local/sbin/unbound-control -c {$g['unbound_chroot_path']}/unbound.conf";
|
790
|
|
791
|
// Define SQLite3 parameters
|
792
|
$pfb['sqlite_timeout'] = 100000;
|
793
|
|
794
|
// Max daily download failure threshold (default to '0' unlimited failures)
|
795
|
$pfb['skipfeed'] = $pfb['config']['skipfeed'] != '' ? $pfb['config']['skipfeed'] : 0;
|
796
|
|
797
|
if (config_path_enabled('unbound')) {
|
798
|
$pfb['unbound_state'] = 'on';
|
799
|
} else {
|
800
|
$pfb['unbound_state'] = '';
|
801
|
}
|
802
|
|
803
|
// cURL - system proxy server setttings, if configured
|
804
|
if (!empty(config_get_path('system/proxyurl'))) {
|
805
|
$pfb['curl_defaults'][CURLOPT_PROXY] = config_get_path('system/proxyurl');
|
806
|
if (!empty(config_get_path('system/proxyport'))) {
|
807
|
$pfb['curl_defaults'][CURLOPT_PROXYPORT] = config_get_path('system/proxyport');
|
808
|
}
|
809
|
if (!empty(config_get_path('system/proxyuser')) && !empty(config_get_path('system/proxypass'))) {
|
810
|
$pfb['curl_defaults'][CURLOPT_PROXYAUTH] = 'CURLAUTH_ANY | CURLAUTH_ANYSAFE';
|
811
|
$pfb['curl_defaults'][CURLOPT_PROXYUSERPWD] = config_get_path('system/proxyuser') . ':' . config_get_path('system/proxypass');
|
812
|
}
|
813
|
}
|
814
|
else {
|
815
|
$pfb['curl_defaults'][CURLOPT_TCP_FASTOPEN] = true;
|
816
|
}
|
817
|
|
818
|
// Set pfBlockerNG to disabled on 're-install'
|
819
|
if (isset($pfb['install']) && $pfb['install']) {
|
820
|
$pfb['enable'] = $pfb['dnsbl'] = '';
|
821
|
$pfb['install'] = FALSE;
|
822
|
}
|
823
|
}
|
824
|
pfb_global();
|
825
|
|
826
|
// Function to get pfBlockerNG package version
|
827
|
function pfb_pkg_ver() {
|
828
|
$pkg_ver = 'v??';
|
829
|
foreach (config_get_path('installedpackages/package', []) as $pkg_info_data) {
|
830
|
if (strpos($pkg_info_data['name'], 'pfBlockerNG') !== FALSE) {
|
831
|
$pkg_ver = 'v' . $pkg_info_data['version'];
|
832
|
break;
|
833
|
}
|
834
|
}
|
835
|
return $pkg_ver;
|
836
|
}
|
837
|
|
838
|
|
839
|
// Firewall Filter Service
|
840
|
function pfb_filter_service() {
|
841
|
|
842
|
$rc = array();
|
843
|
$rc['file'] = 'pfb_filter.sh';
|
844
|
$rc['start'] = <<<EOF
|
845
|
|
846
|
# Check if pfBlockerNG is enabled
|
847
|
pfbcheck="\$(/usr/local/sbin/read_xml_tag.sh string installedpackages/pfblockerng/config/enable_cb)"
|
848
|
if [ "\${pfbcheck}" != 'on' ]; then
|
849
|
exit
|
850
|
fi
|
851
|
|
852
|
# Ensure all processes are stopped
|
853
|
rc_stop
|
854
|
|
855
|
# clog is not required for pfSense 2.5 and above
|
856
|
filter_type='tail_pfb'
|
857
|
if [ -e "/usr/local/sbin/clog_pfb" ]; then
|
858
|
filter_type='clog_pfb'
|
859
|
fi
|
860
|
|
861
|
# Compare php/php_pfb versions
|
862
|
php_path='/usr/local/bin'
|
863
|
|
864
|
phpver="\$(/usr/bin/stat \${php_path}/php | cut -d '/' -f1)"
|
865
|
if [ -e "\${php_path}/php_pfb" ]; then
|
866
|
phppfbver="\$(/usr/bin/stat \${php_path}/php_pfb | cut -d '/' -f1)"
|
867
|
else
|
868
|
phppfbver=''
|
869
|
fi
|
870
|
|
871
|
# Create new hard link for php_pfb on version mismatch
|
872
|
if [ "\${phpver}" != "\${phppfbver}" ]; then
|
873
|
if [ -e "\${php_path}/php_pfb" ]; then
|
874
|
tmpfile="\$(/usr/bin/mktemp /tmp/pfb_php.XXXXXX)"
|
875
|
/bin/mv "\${php_path}/php_pfb" "\${tmpfile}"
|
876
|
fi
|
877
|
|
878
|
/bin/ln "\${php_path}/php" "\${php_path}/php_pfb"
|
879
|
fi
|
880
|
|
881
|
# Start pfBlockerNG Firewall filter Daemon
|
882
|
if [ -e '/var/log/filter.log' ]; then
|
883
|
/usr/bin/logger -p daemon.info -t "\${filter_type}" "[pfBlockerNG] Firewall Filter Service started"
|
884
|
|
885
|
if [ "\${filter_type}" == 'clog_pfb' ]; then
|
886
|
/usr/local/sbin/clog_pfb -f /var/log/filter.log | /usr/local/bin/php_pfb -f /usr/local/pkg/pfblockerng/pfblockerng.inc filterlog &
|
887
|
else
|
888
|
/usr/bin/tail_pfb -n0 -F /var/log/filter.log | /usr/local/bin/php_pfb -f /usr/local/pkg/pfblockerng/pfblockerng.inc filterlog &
|
889
|
fi
|
890
|
fi
|
891
|
|
892
|
EOF;
|
893
|
$rc['stop'] = <<<EOF
|
894
|
|
895
|
# clog is not required for pfSense 2.5 and above
|
896
|
filter_type='tail_pfb'
|
897
|
if [ -e "/usr/local/sbin/clog_pfb" ]; then
|
898
|
filter_type='clog_pfb'
|
899
|
fi
|
900
|
|
901
|
# Terminate pfBlockerNG Firewall filter Daemon (clog) and filter Daemon, if found
|
902
|
/usr/bin/logger -p daemon.info -t "\${filter_type}" "[pfBlockerNG] Firewall Filter Service stopped"
|
903
|
/usr/bin/logger -p daemon.info -t php_pfb "[pfBlockerNG] filterlog daemon stopped"
|
904
|
pidnum="\$(/bin/ps -wax | /usr/bin/grep '[c]log_pfb -f /var/log/filter.log\|[t]ail_pfb -n0 -F /var/log/filter.log\|[p]fblockerng.inc filterlog' | /usr/bin/awk '{print \$1}')"
|
905
|
if [ ! -z "\${pidnum}" ]; then
|
906
|
for i in \${pidnum}; do
|
907
|
/bin/kill -9 "\${i}"
|
908
|
done
|
909
|
fi
|
910
|
|
911
|
EOF;
|
912
|
write_rcfile($rc);
|
913
|
}
|
914
|
|
915
|
|
916
|
// DNSBL Service
|
917
|
function pfb_dnsbl_service() {
|
918
|
|
919
|
$rc = array();
|
920
|
$rc['file'] = 'pfb_dnsbl.sh';
|
921
|
$rc['start'] = <<<EOF
|
922
|
|
923
|
# Check if DNSBL is enabled
|
924
|
dnsblcheck="\$(/usr/local/sbin/read_xml_tag.sh string installedpackages/pfblockerngdnsblsettings/config/pfb_dnsbl)"
|
925
|
if [ "\${dnsblcheck}" != 'on' ]; then
|
926
|
exit
|
927
|
fi
|
928
|
|
929
|
# Ensure all processes are stopped
|
930
|
rc_stop
|
931
|
|
932
|
# Start DNSBL Lighttpd webserver (Unbound/Python mode) and DNSBL HTTPS Daemon (Unbound mode only)
|
933
|
if [ -e '/var/unbound/pfb_dnsbl_lighty.conf' ]; then
|
934
|
/usr/bin/logger -p daemon.info -t lighttpd_pfb "[pfBlockerNG] DNSBL Webserver started"
|
935
|
/usr/local/sbin/lighttpd_pfb -f /var/unbound/pfb_dnsbl_lighty.conf
|
936
|
fi
|
937
|
|
938
|
# Check if Unbound mode is enabled
|
939
|
unbound_mode="\$(/usr/local/sbin/read_xml_tag.sh string installedpackages/pfblockerngdnsblsettings/config/dnsbl_mode)"
|
940
|
|
941
|
# Start DNSBL Resolver queries Daemon
|
942
|
if [ "\${unbound_mode}" == 'dnsbl_unbound' ]; then
|
943
|
/usr/local/bin/php -f /usr/local/pkg/pfblockerng/pfblockerng.inc queries &
|
944
|
elif [ ! -e '/var/unbound/pfb_unbound.py' ]; then
|
945
|
/usr/bin/logger -p daemon.err -t php_pfb "[pfBlockerNG] DNSBL missing python script - forced reload required"
|
946
|
fi
|
947
|
|
948
|
EOF;
|
949
|
$rc['stop'] = <<<EOF
|
950
|
|
951
|
# Terminate DNSBL Lighttpd webserver, if found
|
952
|
/usr/bin/logger -p daemon.info -t lighttpd_pfb "[pfBlockerNG] DNSBL Webserver stopped"
|
953
|
pidnum="\$(/bin/pgrep lighttpd_pfb)"
|
954
|
if [ ! -z "\${pidnum}" ]; then
|
955
|
/usr/bin/killall lighttpd_pfb
|
956
|
fi
|
957
|
|
958
|
# Terminate DNSBL queries Daemon, if found
|
959
|
pidnum="\$(/bin/ps -wax | /usr/bin/grep '[p]fblockerng.inc queries' | /usr/bin/awk '{print \$1}')"
|
960
|
if [ ! -z "\${pidnum}" ]; then
|
961
|
for i in \${pidnum}; do
|
962
|
/bin/kill -9 "\${i}"
|
963
|
done
|
964
|
fi
|
965
|
|
966
|
EOF;
|
967
|
write_rcfile($rc);
|
968
|
}
|
969
|
|
970
|
|
971
|
// Create Firewall filter service
|
972
|
if (!file_exists('/usr/local/etc/rc.d/pfb_filter.sh')) {
|
973
|
pfb_filter_service();
|
974
|
}
|
975
|
|
976
|
// Create DNSBL service
|
977
|
if (!file_exists('/usr/local/etc/rc.d/pfb_dnsbl.sh')) {
|
978
|
pfb_dnsbl_service();
|
979
|
}
|
980
|
|
981
|
// clog is not required for pfSense 2.5 and above
|
982
|
if (substr(trim(file_get_contents('/etc/version')), 0, 3) > '2.5' && file_exists('/usr/local/sbin/clog_pfb')) {
|
983
|
unlink_if_exists('/usr/local/sbin/clog_pfb');
|
984
|
unlink_if_exists('/usr/bin/tail_pfb');
|
985
|
link('/usr/bin/tail', '/usr/bin/tail_pfb');
|
986
|
restart_service('pfb_filter');
|
987
|
}
|
988
|
|
989
|
// Commandline arguments for daemons
|
990
|
if (isset($argv[1])) {
|
991
|
|
992
|
// DNSBL Lighttpd HTTPS daemon (Collects HTTPS events from the Lighttpd dnsbl_error.log)
|
993
|
if ($argv[1] == 'dnsbl') {
|
994
|
ignore_user_abort(TRUE);
|
995
|
set_time_limit(0);
|
996
|
pfb_daemon_dnsbl();
|
997
|
exit;
|
998
|
}
|
999
|
|
1000
|
// DNSBL Lighttpd HTTP daemon (Collects HTTP events from the index.php script)
|
1001
|
elseif ($argv[1] == 'index') {
|
1002
|
ignore_user_abort(TRUE);
|
1003
|
set_time_limit(0);
|
1004
|
pfb_daemon_dnsbl_index();
|
1005
|
exit;
|
1006
|
}
|
1007
|
|
1008
|
// DNSBL daemon to monitor Resolver queries and manage SQLite3 database
|
1009
|
elseif ($argv[1] == 'queries') {
|
1010
|
ignore_user_abort(TRUE);
|
1011
|
set_time_limit(0);
|
1012
|
pfb_daemon_queries();
|
1013
|
exit;
|
1014
|
}
|
1015
|
|
1016
|
// IP filter daemon to convert filter.log to ip_block|ip_permit|ip_match log format
|
1017
|
elseif ($argv[1] == 'filterlog') {
|
1018
|
ignore_user_abort(TRUE);
|
1019
|
set_time_limit(0);
|
1020
|
if (!file_exists('/var/log/filter.log')) {
|
1021
|
log_error('[pfBlockerNG] pfSense Firewall log missing');
|
1022
|
exit;
|
1023
|
}
|
1024
|
pfb_daemon_filterlog();
|
1025
|
exit;
|
1026
|
}
|
1027
|
}
|
1028
|
|
1029
|
|
1030
|
// Function to convert string to lowercase (Not for comment line section)
|
1031
|
function pfb_strtolower($line) {
|
1032
|
if (strpos($line, '#') === FALSE) {
|
1033
|
return trim(strtolower($line));
|
1034
|
}
|
1035
|
return trim($line);
|
1036
|
}
|
1037
|
|
1038
|
|
1039
|
// Function to decode alias custom entry box.
|
1040
|
// Default (False, True): Return as string with comments
|
1041
|
function pfbng_text_area_decode($text, $mode=FALSE, $type=TRUE, $idn=FALSE) {
|
1042
|
|
1043
|
if ($mode) {
|
1044
|
$custom = array();
|
1045
|
}
|
1046
|
|
1047
|
$customlist = explode("\r\n", base64_decode($text));
|
1048
|
if (!empty($customlist)) {
|
1049
|
foreach ($customlist as $line) {
|
1050
|
if (substr(trim($line), 0, 1) != '#' && !empty($line)) {
|
1051
|
if ($idn && !ctype_print($line)) {
|
1052
|
$line_old = $line;
|
1053
|
// Convert encodings to UTF-8
|
1054
|
$line = mb_convert_encoding($line, 'UTF-8',
|
1055
|
mb_detect_encoding($line, 'UTF-8, ASCII, ISO-8859-1'));
|
1056
|
if (strpos($line, '#') !== FALSE) {
|
1057
|
$tmpline = preg_split('/(?=#)/', $line);
|
1058
|
if (substr($tmpline[0], 0, 1) == '.') {
|
1059
|
// idn_to_ascii() returns empty string if it starts with '.'
|
1060
|
$tmpline[0] = idn_to_ascii(ltrim($tmpline[0], '.'));
|
1061
|
if (!empty($tmpline[0])) {
|
1062
|
$tmpline[0] = '.' . $tmpline[0];
|
1063
|
}
|
1064
|
} else {
|
1065
|
$tmpline[0] = idn_to_ascii($tmpline[0]);
|
1066
|
}
|
1067
|
if (empty($tmpline[0])) {
|
1068
|
$log = "\nError converting IDN line '{$line_old}'\n";
|
1069
|
pfb_logger($log, 2);
|
1070
|
continue;
|
1071
|
}
|
1072
|
$line = implode(' ', $tmpline);
|
1073
|
} else {
|
1074
|
if (substr($line, 0, 1) == '.') {
|
1075
|
$line = idn_to_ascii(ltrim($line, '.'));
|
1076
|
if (!empty($line)) {
|
1077
|
$line = '.' . $line;
|
1078
|
}
|
1079
|
} else {
|
1080
|
$line = idn_to_ascii($line);
|
1081
|
}
|
1082
|
if (empty($line)) {
|
1083
|
$log = "\nError converting IDN line '{$line_old}'\n";
|
1084
|
pfb_logger($log, 2);
|
1085
|
continue;
|
1086
|
}
|
1087
|
}
|
1088
|
}
|
1089
|
// '#' commentline found
|
1090
|
if (strpos($line, '#') !== FALSE) {
|
1091
|
if ($mode) {
|
1092
|
if ($type) {
|
1093
|
// Split line into two elements (array)
|
1094
|
$custom[] = array_map('pfb_strtolower', preg_split('/(?=#)/', $line));
|
1095
|
} else {
|
1096
|
// Remove commentline
|
1097
|
$custom[] = trim(strtolower(strstr($line, '#', TRUE)));
|
1098
|
}
|
1099
|
} else {
|
1100
|
// Remove commentline
|
1101
|
$custom .= trim(strtolower(strstr($line, '#', TRUE))) . "\n";
|
1102
|
}
|
1103
|
}
|
1104
|
|
1105
|
// No '#' commentline found
|
1106
|
else {
|
1107
|
$line = trim(strtolower($line));
|
1108
|
|
1109
|
if ($mode) {
|
1110
|
if ($type) {
|
1111
|
$custom[][0] = $line;
|
1112
|
} else {
|
1113
|
$custom[] = $line;
|
1114
|
}
|
1115
|
} else {
|
1116
|
$custom .= "{$line}\n";
|
1117
|
}
|
1118
|
}
|
1119
|
}
|
1120
|
}
|
1121
|
return $custom;
|
1122
|
}
|
1123
|
}
|
1124
|
|
1125
|
|
1126
|
// Manage log files line limit
|
1127
|
function pfb_log_mgmt() {
|
1128
|
global $g, $pfb;
|
1129
|
pfb_global();
|
1130
|
|
1131
|
$chroot_folder = '/var/unbound';
|
1132
|
|
1133
|
foreach (array( 'log', 'errlog', 'extraslog', 'ip_blocklog', 'ip_permitlog', 'ip_matchlog',
|
1134
|
'dnslog', 'dnsbl_parse_err', 'dnsreplylog', 'unilog') as $logtype) {
|
1135
|
|
1136
|
// Max lines in Log file
|
1137
|
$logmax = pfb_filter($pfb['config']['log_max_' . $logtype], PFB_FILTER_NUM, 'pfb_log_mgmt', 20000);
|
1138
|
|
1139
|
if ($logmax != 'nolimit' && file_exists($pfb[$logtype])) {
|
1140
|
if ($logtype == 'dnslog' || $logtype == 'dnsreplylog' || $logtype == 'unilog') {
|
1141
|
|
1142
|
// Set DNSBL python logfile permissions using chroot folder
|
1143
|
if (is_dir("{$chroot_folder}/var/log/pfblockerng")) {
|
1144
|
$final_log_file = "{$chroot_folder}{$pfb[$logtype]}";
|
1145
|
$temp = tempnam("{$chroot_folder}/var/log/pfblockerng", 'pfb_log_');
|
1146
|
} else {
|
1147
|
$final_log_file = $pfb[$logtype];
|
1148
|
$temp = tempnam("{$g['tmp_path']}/", 'pfb_log_');
|
1149
|
}
|
1150
|
|
1151
|
if (file_exists($final_log_file)) {
|
1152
|
exec("/usr/bin/tail -n " . escapeshellarg($logmax) . " " . escapeshellarg($final_log_file) . " > " . escapeshellarg($temp));
|
1153
|
@chown($temp, 'unbound');
|
1154
|
@chgrp($temp, 'unbound');
|
1155
|
exec("/bin/mv -f " . escapeshellarg($temp) . " " . escapeshellarg($final_log_file));
|
1156
|
}
|
1157
|
}
|
1158
|
else {
|
1159
|
$temp = tempnam("{$g['tmp_path']}/", 'pfb_log_');
|
1160
|
exec("/usr/bin/tail -n " . escapeshellarg($logmax) . " {$pfb[$logtype]} > " . escapeshellarg($temp));
|
1161
|
exec("/bin/mv -f " . escapeshellarg($temp) . " {$pfb[$logtype]}");
|
1162
|
}
|
1163
|
unlink_if_exists($temp);
|
1164
|
}
|
1165
|
}
|
1166
|
}
|
1167
|
|
1168
|
|
1169
|
// Record log messsages to pfBlockerNG log file and/or error log file.
|
1170
|
function pfb_logger($log, $logtype) {
|
1171
|
global $g, $pfb;
|
1172
|
|
1173
|
$now = date('m/j/y H:i:s', time());
|
1174
|
|
1175
|
// Only log timestamp if new
|
1176
|
if (strpos($log, 'NOW') !== FALSE) {
|
1177
|
$elog = str_replace('NOW', $now, "{$log}"); // Always report timestamp to errorlog
|
1178
|
if ($now == $pfb['pnow']) {
|
1179
|
$log = str_replace(' [ NOW ]', '', "{$log}");
|
1180
|
} else {
|
1181
|
$log = str_replace('NOW', $now, "{$log}");
|
1182
|
}
|
1183
|
$pfb['pnow'] = "{$now}";
|
1184
|
}
|
1185
|
else {
|
1186
|
$elog = "{$log} [ {$now} ]";
|
1187
|
}
|
1188
|
|
1189
|
switch ($logtype) {
|
1190
|
|
1191
|
// Print to pfBlockerNG log
|
1192
|
case 1:
|
1193
|
@file_put_contents("{$pfb['log']}", "{$log}", FILE_APPEND);
|
1194
|
break;
|
1195
|
|
1196
|
// Print to pfBlockerNG log and Error log
|
1197
|
case 2:
|
1198
|
@file_put_contents("{$pfb['log']}", "{$log}", FILE_APPEND);
|
1199
|
@file_put_contents("{$pfb['errlog']}", "{$elog}", FILE_APPEND);
|
1200
|
break;
|
1201
|
|
1202
|
// Print to Extras log
|
1203
|
case 3:
|
1204
|
@file_put_contents("{$pfb['extraslog']}", "{$log}", FILE_APPEND);
|
1205
|
break;
|
1206
|
|
1207
|
// Print to screen and Extras log
|
1208
|
case 4:
|
1209
|
if (!$g['pfblockerng_install'] && !$pfb['extras_update']) {
|
1210
|
print "{$log}";
|
1211
|
}
|
1212
|
@file_put_contents("{$pfb['extraslog']}", "{$log}", FILE_APPEND);
|
1213
|
break;
|
1214
|
|
1215
|
// Print to debugger
|
1216
|
case 5:
|
1217
|
@file_put_contents("/tmp/pfb_debug", "{$now} | {$elog}", FILE_APPEND);
|
1218
|
break;
|
1219
|
|
1220
|
// Print to Error log
|
1221
|
case 6:
|
1222
|
@file_put_contents("{$pfb['errlog']}", "{$elog}", FILE_APPEND);
|
1223
|
break;
|
1224
|
default:
|
1225
|
break;
|
1226
|
}
|
1227
|
}
|
1228
|
|
1229
|
|
1230
|
// Record failed IP/DNSBL Feed parse errors
|
1231
|
function pfb_parsed_fail($header, $line='', $oline, $logfile) {
|
1232
|
|
1233
|
$line = $line ?: 'null';
|
1234
|
$now = date('m/j/y H:i:s', time());
|
1235
|
|
1236
|
$log = "{$now},{$header},{$line},{$oline}";
|
1237
|
@file_put_contents("{$logfile}", "{$log}", FILE_APPEND);
|
1238
|
}
|
1239
|
|
1240
|
|
1241
|
// Determine 'list' details
|
1242
|
function pfb_determine_list_detail($list='', $header='', $confconfig='', $key='') {
|
1243
|
global $pfb, $pfbarr;
|
1244
|
$pfbarr = array();
|
1245
|
|
1246
|
switch($list) {
|
1247
|
case 'Deny_Both':
|
1248
|
case 'Deny_Inbound':
|
1249
|
case 'Deny_Outbound':
|
1250
|
case 'Alias_Deny':
|
1251
|
$pfbarr = array('adv' => TRUE, 'folder' => "{$pfb['denydir']}", 'orig' => "{$pfb['origdir']}", 'reuse' => "{$pfb['reuse']}");
|
1252
|
break;
|
1253
|
case 'unbound':
|
1254
|
$pfbarr = array('adv' => FALSE, 'folder' => "{$pfb['dnsdir']}", 'orig' => "{$pfb['dnsorigdir']}", 'reuse' => "{$pfb['reuse_dnsbl']}");
|
1255
|
break;
|
1256
|
case 'Permit_Both':
|
1257
|
case 'Permit_Inbound':
|
1258
|
case 'Permit_Outbound':
|
1259
|
case 'Alias_Permit':
|
1260
|
$pfbarr = array('adv' => FALSE, 'folder' => "{$pfb['permitdir']}", 'orig' => "{$pfb['origdir']}", 'reuse' => "{$pfb['reuse']}");
|
1261
|
break;
|
1262
|
case 'Match_Both':
|
1263
|
case 'Match_Inbound':
|
1264
|
case 'Match_Outbound':
|
1265
|
case 'Alias_Match':
|
1266
|
$pfbarr = array('adv' => FALSE, 'folder' => "{$pfb['matchdir']}", 'orig' => "{$pfb['origdir']}", 'reuse' => "{$pfb['reuse']}");
|
1267
|
break;
|
1268
|
case 'Alias_Native':
|
1269
|
$pfbarr = array('adv' => FALSE, 'folder' => "{$pfb['nativedir']}", 'orig' => "{$pfb['origdir']}", 'reuse' => "{$pfb['reuse']}");
|
1270
|
break;
|
1271
|
}
|
1272
|
|
1273
|
// Collect proper alias table description (alias only vs autorules)
|
1274
|
if (strpos($list, 'Alias') !== FALSE) {
|
1275
|
$pfbarr['descr'] = '';
|
1276
|
} else {
|
1277
|
$pfbarr['descr'] = ' Auto ';
|
1278
|
}
|
1279
|
|
1280
|
// Determine length of header to format log output
|
1281
|
$tabtype = strlen($header);
|
1282
|
if ($tabtype > 27) {
|
1283
|
$pfbarr['logtab'] = '';
|
1284
|
} elseif ($tabtype > 19) {
|
1285
|
$pfbarr['logtab'] = "\t";
|
1286
|
} elseif ($tabtype > 11) {
|
1287
|
$pfbarr['logtab'] = "\t\t";
|
1288
|
} elseif ($tabtype < 4) {
|
1289
|
$pfbarr['logtab'] = "\t\t\t\t";
|
1290
|
} else {
|
1291
|
$pfbarr['logtab'] = "\t\t\t";
|
1292
|
}
|
1293
|
|
1294
|
// Configure autoports/protocol and auto destination if required.
|
1295
|
if (!empty($confconfig) && is_array(config_get_path("installedpackages/{$confconfig}/config/{$key}"))) {
|
1296
|
|
1297
|
$conf_config = config_get_path("installedpackages/{$confconfig}/config/{$key}");
|
1298
|
$autotype = array( 'autoports' => 'aliasports', 'autoaddr' => 'aliasaddr');
|
1299
|
|
1300
|
foreach (array('_out', '_in') as $dir) {
|
1301
|
|
1302
|
$pfbarr['aproto' . $dir] = $conf_config['autoproto' . $dir];
|
1303
|
$pfbarr['anot' . $dir] = $conf_config['autonot' . $dir];
|
1304
|
$pfbarr['aaddrnot' . $dir] = $conf_config['autoaddrnot' . $dir];
|
1305
|
$pfbarr['agateway' . $dir] = $conf_config['agateway' . $dir];
|
1306
|
|
1307
|
foreach ($autotype as $akey => $atype) {
|
1308
|
if ($conf_config[$akey . $dir] == 'on') {
|
1309
|
foreach (config_get_path('aliases/alias', []) as $palias) {
|
1310
|
if ($palias['name'] == $conf_config[$atype . $dir]) {
|
1311
|
if (!empty($palias['address'])) {
|
1312
|
$dalias = "{$atype}{$dir}";
|
1313
|
switch($akey) {
|
1314
|
case 'autoports':
|
1315
|
$ctype = "aports{$dir}";
|
1316
|
$pfbarr[$ctype] = $conf_config[$dalias];
|
1317
|
break;
|
1318
|
case 'autoaddr':
|
1319
|
$ctype = "aaddr{$dir}";
|
1320
|
$pfbarr[$ctype] = $conf_config[$dalias];
|
1321
|
break;
|
1322
|
}
|
1323
|
}
|
1324
|
}
|
1325
|
}
|
1326
|
}
|
1327
|
}
|
1328
|
}
|
1329
|
}
|
1330
|
|
1331
|
// Force 'Alias Native' setting to any Alias with 'Advanced Inbound/Outbound -Invert src/dst' settings.
|
1332
|
// This will bypass Deduplication and Reputation features.
|
1333
|
if ($pfbarr['aaddrnot_in'] == 'on' || $pfbarr['aaddrnot_out'] == 'on') {
|
1334
|
$pfbarr['adv'] = FALSE;
|
1335
|
$pfbarr['folder'] = "{$pfb['nativedir']}";
|
1336
|
}
|
1337
|
|
1338
|
return $pfbarr;
|
1339
|
}
|
1340
|
|
1341
|
|
1342
|
// Determine if cron task requires updating
|
1343
|
function pfblockerng_cron_exists($pfb_cmd, $pfb_min, $pfb_hour, $pfb_mday, $pfb_wday) {
|
1344
|
foreach (config_get_path('cron/item', []) as $item) {
|
1345
|
if (strpos($item['command'], $pfb_cmd) !== FALSE) {
|
1346
|
if ($item['command'] != $pfb_cmd) {
|
1347
|
return FALSE;
|
1348
|
}
|
1349
|
if ($item['minute'] != $pfb_min) {
|
1350
|
return FALSE;
|
1351
|
}
|
1352
|
if ($item['mday'] != $pfb_mday) {
|
1353
|
return FALSE;
|
1354
|
}
|
1355
|
if ($item['wday'] != $pfb_wday) {
|
1356
|
return FALSE;
|
1357
|
}
|
1358
|
if ($pfb_hour == 'random' && $item['hour'] != '') {
|
1359
|
// MaxMind/Blacklist hour is randomized. Skip comparison.
|
1360
|
return TRUE;
|
1361
|
}
|
1362
|
if ($item['hour'] != $pfb_hour) {
|
1363
|
return FALSE;
|
1364
|
}
|
1365
|
return TRUE;
|
1366
|
}
|
1367
|
}
|
1368
|
return FALSE;
|
1369
|
}
|
1370
|
|
1371
|
|
1372
|
// Calculate the cron task base hour setting
|
1373
|
function pfb_cron_base_hour($freq) {
|
1374
|
global $pfb;
|
1375
|
|
1376
|
switch($freq) {
|
1377
|
case 'Disabled':
|
1378
|
case 1:
|
1379
|
case '01hour':
|
1380
|
$j = 23; $k = 1;
|
1381
|
break;
|
1382
|
case 2:
|
1383
|
case '02hours':
|
1384
|
$j = 11; $k = 2;
|
1385
|
break;
|
1386
|
case 3:
|
1387
|
case '03hours':
|
1388
|
$j = 7; $k = 3;
|
1389
|
break;
|
1390
|
case 4:
|
1391
|
case '04hours':
|
1392
|
$j = 5; $k = 4;
|
1393
|
break;
|
1394
|
case 6:
|
1395
|
case '06hours':
|
1396
|
$j = 3; $k = 6;
|
1397
|
break;
|
1398
|
case 8:
|
1399
|
case '08hours':
|
1400
|
$j = 2; $k = 8;
|
1401
|
break;
|
1402
|
case 12:
|
1403
|
case '12hours':
|
1404
|
$j = 1; $k = 12;
|
1405
|
break;
|
1406
|
case 24:
|
1407
|
return array($pfb['24hour']);
|
1408
|
break;
|
1409
|
default:
|
1410
|
$pfb['interval'] = 1;
|
1411
|
return [];
|
1412
|
}
|
1413
|
|
1414
|
$shour = intval(substr($pfb['hour'], 0, 2));
|
1415
|
$sch = strval($shour);
|
1416
|
|
1417
|
for ($i=0; $i < $j; $i++) {
|
1418
|
$shour += $k;
|
1419
|
if ($shour >= 24) {
|
1420
|
$shour -= 24;
|
1421
|
}
|
1422
|
$sch .= ',' . strval($shour);
|
1423
|
}
|
1424
|
|
1425
|
$sch = explode(',', $sch);
|
1426
|
sort($sch);
|
1427
|
return $sch;
|
1428
|
}
|
1429
|
|
1430
|
|
1431
|
// Collect 'gateway(s)' and 'gateway group(s)' for Adv. In/Outbound customizations
|
1432
|
function pfb_get_gateways() {
|
1433
|
$gateway = array();
|
1434
|
$gateway['default'] = 'default';
|
1435
|
|
1436
|
foreach (config_get_path('gateways/gateway_item', []) as $item) {
|
1437
|
$gateway[$item['name']] = $item['name'];
|
1438
|
}
|
1439
|
foreach (config_get_path('gateways/gateway_group', []) as $item) {
|
1440
|
$gateway[$item['name']] = $item['name'];
|
1441
|
}
|
1442
|
|
1443
|
return $gateway;
|
1444
|
}
|
1445
|
|
1446
|
|
1447
|
// Collect all Interfaces for General Tab and DNSBL Firewall Permit Rule
|
1448
|
function pfb_build_if_list($show_wan=FALSE, $show_groups=FALSE) {
|
1449
|
$pfb_interfaces = array();
|
1450
|
|
1451
|
foreach (get_configured_interface_with_descr() as $ifent => $ifdesc) {
|
1452
|
if ($show_wan || $ifent != 'wan') {
|
1453
|
$pfb_interfaces[$ifent] = $ifdesc;
|
1454
|
}
|
1455
|
}
|
1456
|
|
1457
|
if ($show_groups) {
|
1458
|
foreach (config_get_path('ifgroups/ifgroupentry', []) as $ifgen) {
|
1459
|
$pfb_interfaces[$ifgen['ifname']] = $ifgen['ifname'];
|
1460
|
}
|
1461
|
}
|
1462
|
|
1463
|
if (ipsec_enabled()) {
|
1464
|
$pfb_interfaces['enc0'] = 'IPsec';
|
1465
|
}
|
1466
|
|
1467
|
if (config_get_path('openvpn/openvpn-server') || config_get_path('openvpn/openvpn-client')) {
|
1468
|
$pfb_interfaces['openvpn'] = 'OpenVPN';
|
1469
|
}
|
1470
|
|
1471
|
if (config_get_path('l2tp/mode') == 'server') {
|
1472
|
$pfb_interfaces['l2tp'] = 'L2TP VPN';
|
1473
|
}
|
1474
|
|
1475
|
if (function_exists('is_wg_enabled') && is_wg_enabled()) {
|
1476
|
$pfb_interfaces['wireguard'] = 'WireGuard';
|
1477
|
}
|
1478
|
|
1479
|
return $pfb_interfaces;
|
1480
|
}
|
1481
|
|
1482
|
|
1483
|
// Create suppression file from suppression list
|
1484
|
function pfb_create_suppression_file() {
|
1485
|
global $pfb;
|
1486
|
|
1487
|
$v4suppression = pfbng_text_area_decode($pfb['ipconfig']['v4suppression'], FALSE, TRUE);
|
1488
|
if (!empty($v4suppression)) {
|
1489
|
@file_put_contents("{$pfb['supptxt']}", $v4suppression, LOCK_EX);
|
1490
|
} else {
|
1491
|
unlink_if_exists("{$pfb['supptxt']}");
|
1492
|
}
|
1493
|
}
|
1494
|
|
1495
|
|
1496
|
// Function to update DNSBL aliases and widget stats
|
1497
|
function dnsbl_alias_update($mode, $alias, $pfbfolder, $lists_dnsbl_current, $alias_cnt) {
|
1498
|
global $pfb;
|
1499
|
|
1500
|
if ($mode == 'update') {
|
1501
|
|
1502
|
// Create master alias file
|
1503
|
if ($lists_dnsbl_current != '') {
|
1504
|
$pfb_output = @fopen("{$pfb['dnsalias']}/{$alias}", 'w');
|
1505
|
foreach ($lists_dnsbl_current as $clist) {
|
1506
|
if (($handle = @fopen("{$pfbfolder}/{$clist}.txt", 'r')) !== FALSE) {
|
1507
|
while (($line = @fgets($handle)) !== FALSE) {
|
1508
|
@fwrite($pfb_output, $line);
|
1509
|
}
|
1510
|
}
|
1511
|
@fclose($handle);
|
1512
|
}
|
1513
|
@fclose($pfb_output);
|
1514
|
}
|
1515
|
|
1516
|
// Update DNSBL alias statistics
|
1517
|
$dns_now = date('M j H:i:s', time());
|
1518
|
$pfbfound = FALSE;
|
1519
|
|
1520
|
if (!empty($pfb['dnsbl_info_stats'])) {
|
1521
|
foreach ($pfb['dnsbl_info_stats'] as $key => $line) {
|
1522
|
|
1523
|
// Update existing alias stats
|
1524
|
if ($line['groupname'] == "{$alias}") {
|
1525
|
$pfbfound = TRUE;
|
1526
|
$pfb['dnsbl_info_stats'][$key]['timestamp'] = "{$dns_now}";
|
1527
|
$pfb['dnsbl_info_stats'][$key]['entries'] = "{$alias_cnt}";
|
1528
|
break;
|
1529
|
}
|
1530
|
}
|
1531
|
}
|
1532
|
|
1533
|
if (!$pfbfound) {
|
1534
|
$pfb['dnsbl_info_stats'][] = array ( 'groupname' => $alias, 'timestamp' => $dns_now, 'entries' => $alias_cnt, 'counter' => 0);
|
1535
|
}
|
1536
|
}
|
1537
|
elseif ($mode == 'disabled') {
|
1538
|
// Record disabled alias statistics
|
1539
|
$pfbfound = FALSE;
|
1540
|
if (!empty($pfb['dnsbl_info_stats'])) {
|
1541
|
foreach ($pfb['dnsbl_info_stats'] as $line) {
|
1542
|
if ($line['groupname'] == "{$alias}") {
|
1543
|
$pfbfound = TRUE;
|
1544
|
break;
|
1545
|
}
|
1546
|
}
|
1547
|
}
|
1548
|
|
1549
|
if (!$pfbfound) {
|
1550
|
$dns_now = date('M j H:i:s', time());
|
1551
|
$pfb['dnsbl_info_stats'][] = array ('groupname' => $alias, 'timestamp' => $dns_now, 'entries' => 'disabled', 'counter' => 0);
|
1552
|
}
|
1553
|
}
|
1554
|
}
|
1555
|
|
1556
|
|
1557
|
// Function to save DNSBL Group statistics
|
1558
|
function dnsbl_save_stats() {
|
1559
|
global $pfb;
|
1560
|
|
1561
|
// Save group statistics to SQLite3 database (Remove any feeds that are not referenced)
|
1562
|
$db_update = $db_delete = '';
|
1563
|
pfb_logger("\nSaving DNSBL statistics...", 1);
|
1564
|
|
1565
|
// Collect existing SQL group names
|
1566
|
$sql_groupnames = array();
|
1567
|
$db_handle = pfb_open_sqlite(1, 'Collect Group');
|
1568
|
if ($db_handle) {
|
1569
|
$result = $db_handle->query("SELECT * FROM dnsbl;");
|
1570
|
if ($result) {
|
1571
|
while ($res = $result->fetchArray(SQLITE3_ASSOC)) {
|
1572
|
$sql_groupnames[$res['groupname']] = '';
|
1573
|
}
|
1574
|
}
|
1575
|
}
|
1576
|
pfb_close_sqlite($db_handle);
|
1577
|
|
1578
|
// Compare SQL database Group names to latest Group names
|
1579
|
if (!empty($pfb['dnsbl_info_stats'])) {
|
1580
|
|
1581
|
$db_handle = pfb_open_sqlite(1, 'Save DNSBL stats');
|
1582
|
if ($db_handle) {
|
1583
|
|
1584
|
foreach ($pfb['dnsbl_info_stats'] as $group) {
|
1585
|
|
1586
|
// Keep row
|
1587
|
$pfb_delete = FALSE;
|
1588
|
if (in_array($group['groupname'], $pfb['alias_dnsbl_all'])) {
|
1589
|
|
1590
|
// Update existing row
|
1591
|
if (isset($sql_groupnames[$group['groupname']])) {
|
1592
|
$db_update = "UPDATE dnsbl SET timestamp = :timestamp, entries = :entries"
|
1593
|
. " WHERE groupname = :groupname;\n";
|
1594
|
}
|
1595
|
|
1596
|
// Add new row
|
1597
|
else {
|
1598
|
$db_update = "INSERT INTO dnsbl (groupname, timestamp, entries, counter)"
|
1599
|
. " VALUES (:groupname, :timestamp, :entries, 0);\n";
|
1600
|
}
|
1601
|
}
|
1602
|
|
1603
|
// Remove row
|
1604
|
else {
|
1605
|
$db_update = "DELETE FROM dnsbl WHERE groupname = :groupname;\n";
|
1606
|
$pfb_delete = TRUE;
|
1607
|
}
|
1608
|
|
1609
|
if (is_numeric($group['entries'])) {
|
1610
|
$group['groupname'] = pfb_filter($group['groupname'], PFB_FILTER_HTML, 'dnsbl_save_stats');
|
1611
|
|
1612
|
$stmt = $db_handle->prepare($db_update);
|
1613
|
if ($stmt) {
|
1614
|
$stmt->bindValue(':groupname', $group['groupname'], SQLITE3_TEXT);
|
1615
|
if (!$pfb_delete) {
|
1616
|
$group['timestamp'] = pfb_filter($group['timestamp'], PFB_FILTER_HTML, 'dnsbl_save_stats');
|
1617
|
|
1618
|
$stmt->bindValue(':timestamp', $group['timestamp'], SQLITE3_TEXT);
|
1619
|
$stmt->bindValue(':entries', $group['entries'], SQLITE3_TEXT);
|
1620
|
}
|
1621
|
$stmt->execute();
|
1622
|
}
|
1623
|
}
|
1624
|
}
|
1625
|
}
|
1626
|
pfb_close_sqlite($db_handle);
|
1627
|
}
|
1628
|
else {
|
1629
|
$db_delete = 'DROP TABLE dnsbl;';
|
1630
|
$db_handle = pfb_open_sqlite(1, 'Delete table');
|
1631
|
if ($db_handle) {
|
1632
|
$db_handle->exec("BEGIN TRANSACTION;"
|
1633
|
. "{$db_delete}"
|
1634
|
. "END TRANSACTION;");
|
1635
|
}
|
1636
|
pfb_close_sqlite($db_handle);
|
1637
|
}
|
1638
|
pfb_logger(" completed [ NOW ]", 1);
|
1639
|
}
|
1640
|
|
1641
|
|
1642
|
// Function to create DNSBL Lighttpd configuration file
|
1643
|
function pfb_create_lighttpd() {
|
1644
|
global $pfb;
|
1645
|
|
1646
|
$lighttpd_bind = $pfb['dnsbl_iface'] != 'lo0' ? "127.0.0.1" : "{$pfb['dnsbl_vip']}";
|
1647
|
$lighttpd_port = $pfb['dnsbl_iface'] != 'lo0' ? "{$pfb['dnsbl_port']}" : '80';
|
1648
|
|
1649
|
$pfb_conf = '';
|
1650
|
$pfb_conf = <<<EOF
|
1651
|
#
|
1652
|
#pfBlockerNG DNSBL Lighttpd configuration file
|
1653
|
#
|
1654
|
server.tag = "pfBlockerNG DNSBL"
|
1655
|
server.bind = "{$lighttpd_bind}"
|
1656
|
server.port = "{$lighttpd_port}"
|
1657
|
server.event-handler = "freebsd-kqueue"
|
1658
|
server.network-backend = "freebsd-sendfile"
|
1659
|
server.dir-listing = "disable"
|
1660
|
server.document-root = "/usr/local/www/pfblockerng/www/"
|
1661
|
server.max-request-size = "1"
|
1662
|
server.pid-file = "/var/run/dnsbl.pid"
|
1663
|
|
1664
|
EOF;
|
1665
|
|
1666
|
if (!$pfb['dnsbl_py_blacklist'] || $pfb['dnsbl_py_nolog'] == 'on') {
|
1667
|
$pfb_conf .= <<<EOF
|
1668
|
server.errorlog = "|/usr/local/bin/php -f /usr/local/pkg/pfblockerng/pfblockerng.inc dnsbl"
|
1669
|
|
1670
|
EOF;
|
1671
|
}
|
1672
|
|
1673
|
if (file_exists('/usr/local/lib/lighttpd/mod_openssl.so')) {
|
1674
|
if (!$pfb['dnsbl_py_blacklist'] || $pfb['dnsbl_py_nolog'] == 'on') {
|
1675
|
$pfb_conf .= 'server.modules = ( "mod_access", "mod_auth", "mod_accesslog", "mod_fastcgi", "mod_rewrite", "mod_openssl" )';
|
1676
|
} else {
|
1677
|
$pfb_conf .= 'server.modules = ( "mod_auth", "mod_fastcgi", "mod_rewrite", "mod_openssl" )';
|
1678
|
}
|
1679
|
} else {
|
1680
|
if (!$pfb['dnsbl_py_blacklist'] || $pfb['dnsbl_py_nolog'] == 'on') {
|
1681
|
$pfb_conf .= 'server.modules = ( "mod_access", "mod_auth", "mod_accesslog", "mod_fastcgi", "mod_rewrite" )';
|
1682
|
} else {
|
1683
|
$pfb_conf .= 'server.modules = ( "mod_auth", "mod_fastcgi", "mod_rewrite" )';
|
1684
|
}
|
1685
|
}
|
1686
|
|
1687
|
$pfb_conf .= <<<EOF
|
1688
|
|
1689
|
index-file.names = ( "index.php" )
|
1690
|
mimetype.assign = ( ".html" => "text/html", ".gif" => "image/gif" )
|
1691
|
url.access-deny = ( "~", ".inc" )
|
1692
|
fastcgi.server = ( ".php" => ( "localhost" => ( "socket" => "/var/run/php-fpm.socket", "broken-scriptfilename" => "enable" ) ) )
|
1693
|
|
1694
|
EOF;
|
1695
|
if (!$pfb['dnsbl_py_blacklist'] || $pfb['dnsbl_py_nolog'] == 'on') {
|
1696
|
|
1697
|
$pfb_conf .= <<<EOF
|
1698
|
debug.log-condition-handling = "enable"
|
1699
|
accesslog.use-syslog = "disable"
|
1700
|
accesslog.format = "INDEX!%r!%V!%h!%{Referer}i * %r * %{User-Agent}i"
|
1701
|
accesslog.filename = "|/usr/local/bin/php -f /usr/local/pkg/pfblockerng/pfblockerng.inc index"
|
1702
|
|
1703
|
EOF;
|
1704
|
}
|
1705
|
// Lighttpd v1.4.58+ conditional error log with 'ssl.verifyclient.activate' to collect the domain name
|
1706
|
$lighty_ver = exec('/usr/local/sbin/lighttpd -v 2>&1');
|
1707
|
if ((!$pfb['dnsbl_py_blacklist'] || $pfb['dnsbl_py_nolog'] == 'on') &&
|
1708
|
(strpos($lighty_ver, 'lighttpd/1.4.58') !== FALSE ||
|
1709
|
strpos($lighty_ver, 'lighttpd/1.4.59') !== FALSE ||
|
1710
|
strpos($lighty_ver, 'lighttpd/1.4.60') !== FALSE ||
|
1711
|
strpos($lighty_ver, 'lighttpd/1.4.61') !== FALSE ||
|
1712
|
strpos($lighty_ver, 'lighttpd/1.4.67') !== FALSE ||
|
1713
|
strpos($lighty_ver, 'lighttpd/1.5') !== FALSE)) {
|
1714
|
|
1715
|
$pfb_conf .= <<<EOF
|
1716
|
ssl.verifyclient.activate = "enable"
|
1717
|
|
1718
|
EOF;
|
1719
|
}
|
1720
|
|
1721
|
$pfb_conf .= <<<EOF
|
1722
|
|
1723
|
\$HTTP["scheme"] == "http" {
|
1724
|
url.rewrite-once = ( ".*" => "/index.php" )
|
1725
|
}
|
1726
|
|
1727
|
\$HTTP["remoteip"] =~ ".*" {
|
1728
|
|
1729
|
EOF;
|
1730
|
|
1731
|
if ($pfb['dnsbl_iface'] != 'lo0') {
|
1732
|
$pfb_conf .= <<<EOF
|
1733
|
|
1734
|
\$SERVER["socket"] == "127.0.0.1:{$pfb['dnsbl_port_ssl']}" {
|
1735
|
ssl.engine = "enable"
|
1736
|
ssl.pemfile = "/var/unbound/dnsbl_cert.pem"
|
1737
|
ssl.dh-file = "/etc/dh-parameters.4096"
|
1738
|
ssl.ec-curve = "secp384r1"
|
1739
|
ssl.honor-cipher-order = "enable"
|
1740
|
ssl.openssl.ssl-conf-cmd = ("MinProtocol" => "TLSv1.2",
|
1741
|
"Options" => "-ServerPreference",
|
1742
|
"CipherString" => "EECDH+AESGCM:AES256+EECDH:CHACHA20:!SHA1:!SHA256:!SHA384")
|
1743
|
}
|
1744
|
|
1745
|
\$SERVER["socket"] == "{$pfb['dnsbl_vip']}:80" {
|
1746
|
#
|
1747
|
}
|
1748
|
|
1749
|
EOF;
|
1750
|
}
|
1751
|
$pfb_conf .= <<<EOF
|
1752
|
|
1753
|
\$SERVER["socket"] == "{$pfb['dnsbl_vip']}:443" {
|
1754
|
ssl.engine = "enable"
|
1755
|
ssl.pemfile = "/var/unbound/dnsbl_cert.pem"
|
1756
|
ssl.dh-file = "/etc/dh-parameters.4096"
|
1757
|
ssl.ec-curve = "secp384r1"
|
1758
|
ssl.honor-cipher-order = "enable"
|
1759
|
ssl.openssl.ssl-conf-cmd = ("MinProtocol" => "TLSv1.2",
|
1760
|
"Options" => "-ServerPreference",
|
1761
|
"CipherString" => "EECDH+AESGCM:AES256+EECDH:CHACHA20:!SHA1:!SHA256:!SHA384")
|
1762
|
}
|
1763
|
|
1764
|
EOF;
|
1765
|
if ($pfb['dnsbl_iface'] != 'lo0') {
|
1766
|
$pfb_conf .= <<<EOF
|
1767
|
|
1768
|
\$SERVER["socket"] == "[::1]:{$pfb['dnsbl_port']}" {
|
1769
|
#
|
1770
|
}
|
1771
|
|
1772
|
\$SERVER["socket"] == "[::1]:{$pfb['dnsbl_port_ssl']}" {
|
1773
|
ssl.engine = "enable"
|
1774
|
ssl.pemfile = "/var/unbound/dnsbl_cert.pem"
|
1775
|
ssl.dh-file = "/etc/dh-parameters.4096"
|
1776
|
ssl.ec-curve = "secp384r1"
|
1777
|
ssl.honor-cipher-order = "enable"
|
1778
|
ssl.openssl.ssl-conf-cmd = ("MinProtocol" => "TLSv1.2",
|
1779
|
"Options" => "-ServerPreference",
|
1780
|
"CipherString" => "EECDH+AESGCM:AES256+EECDH:CHACHA20:!SHA1:!SHA256:!SHA384")
|
1781
|
}
|
1782
|
|
1783
|
EOF;
|
1784
|
}
|
1785
|
if ($pfb['dnsbl_v6'] == 'on') {
|
1786
|
$pfb_conf .= <<<EOF
|
1787
|
|
1788
|
\$SERVER["socket"] == "[::{$pfb['dnsbl_vip']}]:80" {
|
1789
|
#
|
1790
|
}
|
1791
|
|
1792
|
\$SERVER["socket"] == "[::{$pfb['dnsbl_vip']}]:443" {
|
1793
|
ssl.engine = "enable"
|
1794
|
ssl.pemfile = "/var/unbound/dnsbl_cert.pem"
|
1795
|
ssl.dh-file = "/etc/dh-parameters.4096"
|
1796
|
ssl.ec-curve = "secp384r1"
|
1797
|
ssl.honor-cipher-order = "enable"
|
1798
|
ssl.openssl.ssl-conf-cmd = ("MinProtocol" => "TLSv1.2",
|
1799
|
"Options" => "-ServerPreference",
|
1800
|
"CipherString" => "EECDH+AESGCM:AES256+EECDH:CHACHA20:!SHA1:!SHA256:!SHA384")
|
1801
|
}
|
1802
|
|
1803
|
EOF;
|
1804
|
}
|
1805
|
$pfb_conf .= <<<EOF
|
1806
|
|
1807
|
\$HTTP["host"] =~ ".*" {
|
1808
|
url.rewrite-once = ( ".*" => "/index.php" )
|
1809
|
}
|
1810
|
}
|
1811
|
|
1812
|
EOF;
|
1813
|
return $pfb_conf;
|
1814
|
}
|
1815
|
|
1816
|
|
1817
|
// Function to create DNSBL SSL certificate
|
1818
|
function pfb_create_dnsbl_cert() {
|
1819
|
global $pfb, $cert_strict_values;
|
1820
|
|
1821
|
$cert = array();
|
1822
|
$cert['refid'] = uniqid();
|
1823
|
$cert['descr'] = sprintf(gettext("pfBlockerNG DNSBL (%s)"), $cert['refid']);
|
1824
|
$cert_hostname = config_get_path('system/hostname') . "-pfBNG-DNSBL-{$cert['refid']}";
|
1825
|
|
1826
|
$dn = array(
|
1827
|
'organizationName' => "pfBlockerNG DNSBL Self-Signed Certificate",
|
1828
|
'commonName' => $cert_hostname,
|
1829
|
'subjectAltName' => "DNS:{$cert_hostname}");
|
1830
|
|
1831
|
$old_err_level = error_reporting(0); /* otherwise openssl_ functions throw warnings directly to a page screwing menu tab */
|
1832
|
if (!cert_create($cert, null, 2048, $cert_strict_values['max_server_cert_lifetime'] ?: 398, $dn, 'self-signed', 'sha256')) {
|
1833
|
while ($ssl_err = openssl_error_string()) {
|
1834
|
log_error(sprintf(gettext("Error creating pfBlockerNG DNSBL Certificate: openssl library returns: %s"), $ssl_err));
|
1835
|
}
|
1836
|
error_reporting($old_err_level);
|
1837
|
return null;
|
1838
|
}
|
1839
|
error_reporting($old_err_level);
|
1840
|
$privatekey = base64_decode($cert['prv']);
|
1841
|
$publickey = base64_decode($cert['crt']);
|
1842
|
|
1843
|
@file_put_contents("{$pfb['dnsbl_cert']}", "{$privatekey}{$publickey}", LOCK_EX);
|
1844
|
}
|
1845
|
|
1846
|
|
1847
|
// Create DNSBL VIP and NAT rules, lighttpd conf and services
|
1848
|
function pfb_create_dnsbl($mode) {
|
1849
|
global $pfb;
|
1850
|
pfb_global();
|
1851
|
|
1852
|
// Reload config.xml to get any recent changes
|
1853
|
config_read_file(false, true);
|
1854
|
|
1855
|
$new_nat = $new_vip = $pfb_ex_nat = $pfb_ex_vip = $dnsbl_ex_nat = $dnsbl_ex_vip = array();
|
1856
|
$pfb['dnsbl_vip_changed'] = $pfbupdate = FALSE;
|
1857
|
$iface = escapeshellarg(get_real_interface($pfb['dnsbl_iface']));
|
1858
|
|
1859
|
if ((!empty($pfb['dnsbl_port']) && !empty($pfb['dnsbl_port_ssl']) && !empty($pfb['dnsbl_vip']) && $mode == 'enabled') || $mode == 'disabled') {
|
1860
|
|
1861
|
// DNSBL NAT rules generation
|
1862
|
$pfbfound = FALSE;
|
1863
|
// Collect existing pfSense NAT rules
|
1864
|
foreach (config_get_path('nat/rule', []) as $ex_nat) {
|
1865
|
if (strpos($ex_nat['descr'], 'pfB DNSBL') !== FALSE) {
|
1866
|
// Collect DNSBL NAT rules
|
1867
|
$dnsbl_ex_nat[] = $ex_nat;
|
1868
|
$pfbfound = TRUE;
|
1869
|
} else {
|
1870
|
// Collect all 'other' NAT rules
|
1871
|
$pfb_ex_nat[] = $ex_nat;
|
1872
|
}
|
1873
|
}
|
1874
|
|
1875
|
if ($mode == 'enabled') {
|
1876
|
|
1877
|
// Generate new DNSBL NAT per DNSBL listening ports except for 'localhost' interface setting.
|
1878
|
$dnsbl_new_nat = array();
|
1879
|
if ($pfb['dnsbl_iface'] != 'lo0') {
|
1880
|
$selected_ports = array("{$pfb['dnsbl_port']}" => '80', "{$pfb['dnsbl_port_ssl']}" => '443');
|
1881
|
foreach ($selected_ports as $port => $lport) {
|
1882
|
|
1883
|
$dnsbl_new_nat[] = array ( 'source' => array('any' => ''),
|
1884
|
'destination' => array('address' => "{$pfb['dnsbl_vip']}", 'port' => "{$lport}"),
|
1885
|
'protocol' => 'tcp',
|
1886
|
'target' => '127.0.0.1',
|
1887
|
'local-port' => "{$port}",
|
1888
|
'interface' => "{$pfb['dnsbl_iface']}",
|
1889
|
'descr' => 'pfB DNSBL - DO NOT EDIT',
|
1890
|
'associated-rule-id' => 'pass',
|
1891
|
'natreflection' => 'purenat'
|
1892
|
);
|
1893
|
|
1894
|
// Add DNSBL IPv6 NAT Rules
|
1895
|
if ($pfb['dnsbl_v6'] == 'on') {
|
1896
|
$dnsbl_new_nat[] = array ( 'source' => array('any' => ''),
|
1897
|
'destination' => array('address' => "::{$pfb['dnsbl_vip']}",
|
1898
|
'port' => "{$lport}"),
|
1899
|
'protocol' => 'tcp',
|
1900
|
'target' => '::1',
|
1901
|
'local-port' => "{$port}",
|
1902
|
'interface' => "{$pfb['dnsbl_iface']}",
|
1903
|
'ipprotocol' => 'inet6',
|
1904
|
'descr' => 'pfB DNSBL - DO NOT EDIT',
|
1905
|
'associated-rule-id' => 'pass',
|
1906
|
'natreflection' => 'purenat'
|
1907
|
);
|
1908
|
}
|
1909
|
}
|
1910
|
}
|
1911
|
|
1912
|
// Compare existing to new and if they are not identical update
|
1913
|
if ($dnsbl_ex_nat !== $dnsbl_new_nat) {
|
1914
|
$pfbupdate = TRUE;
|
1915
|
$new_nat = array_merge($pfb_ex_nat, $dnsbl_new_nat);
|
1916
|
} else {
|
1917
|
$new_nat = array_merge($pfb_ex_nat, $dnsbl_ex_nat);
|
1918
|
}
|
1919
|
} else {
|
1920
|
$new_nat = array_merge($pfb_ex_nat, $new_nat);
|
1921
|
// Update when DNSBL NAT found but is now disabled.
|
1922
|
if ($pfbfound) {
|
1923
|
$pfbupdate = TRUE;
|
1924
|
}
|
1925
|
}
|
1926
|
|
1927
|
// DNSBL VIP generation
|
1928
|
$dnsbl_new_vip = array();
|
1929
|
$dnsbl_new_vip[0] = array();
|
1930
|
$dnsbl_new_vip[0]['interface'] = "{$pfb['dnsbl_iface']}";
|
1931
|
$dnsbl_new_vip[0]['descr'] = 'pfB DNSBL - DO NOT EDIT';
|
1932
|
$dnsbl_new_vip[0]['type'] = 'single';
|
1933
|
$dnsbl_new_vip[0]['subnet_bits'] = '32';
|
1934
|
$dnsbl_new_vip[0]['subnet'] = "{$pfb['dnsbl_vip']}";
|
1935
|
|
1936
|
if ($pfb['dnsbl_vip_type'] == 'carp') {
|
1937
|
$dnsbl_new_vip[0]['mode'] = 'carp';
|
1938
|
$dnsbl_new_vip[0]['vhid'] = "{$pfb['dnsbl_vip_vhid']}";
|
1939
|
$dnsbl_new_vip[0]['advskew'] = "{$pfb['dnsbl_vip_skew']}";
|
1940
|
$dnsbl_new_vip[0]['advbase'] = "{$pfb['dnsbl_vip_base']}";
|
1941
|
$dnsbl_new_vip[0]['password'] = "{$pfb['dnsbl_vip_pass']}";
|
1942
|
} else {
|
1943
|
$dnsbl_new_vip[0]['mode'] = 'ipalias';
|
1944
|
}
|
1945
|
|
1946
|
// Add DNSBL IPv6 VIP
|
1947
|
if ($pfb['dnsbl_v6'] == 'on') {
|
1948
|
$dnsbl_new_vip[1] = array();
|
1949
|
$dnsbl_new_vip[1]['interface'] = "{$pfb['dnsbl_iface']}";
|
1950
|
$dnsbl_new_vip[1]['descr'] = 'pfB DNSBL - DO NOT EDIT';
|
1951
|
$dnsbl_new_vip[1]['type'] = 'single';
|
1952
|
$dnsbl_new_vip[1]['subnet_bits'] = '128';
|
1953
|
$dnsbl_new_vip[1]['subnet'] = "::{$pfb['dnsbl_vip']}";
|
1954
|
|
1955
|
if ($pfb['dnsbl_vip_type'] == 'carp') {
|
1956
|
$dnsbl_new_vip[1]['mode'] = 'carp';
|
1957
|
$dnsbl_new_vip[1]['vhid'] = "{$pfb['dnsbl_vip_vhid']}";
|
1958
|
$dnsbl_new_vip[1]['advskew'] = "{$pfb['dnsbl_vip_skew']}";
|
1959
|
$dnsbl_new_vip[1]['advbase'] = "{$pfb['dnsbl_vip_base']}";
|
1960
|
$dnsbl_new_vip[1]['password'] = "{$pfb['dnsbl_vip_pass']}";
|
1961
|
} else {
|
1962
|
$dnsbl_new_vip[1]['mode'] = 'ipalias';
|
1963
|
}
|
1964
|
}
|
1965
|
|
1966
|
$vip_count = 0;
|
1967
|
$pfbfound = FALSE;
|
1968
|
// Collect existing pfSense VIPs
|
1969
|
foreach (config_get_path('virtualip/vip', []) as $ex_vip) {
|
1970
|
if (strpos($ex_vip['descr'], 'pfB DNSBL') !== FALSE) {
|
1971
|
// Collect DNSBL VIP
|
1972
|
$dnsbl_ex_vip[] = $ex_vip;
|
1973
|
$pfbfound = TRUE;
|
1974
|
$vip_count++;
|
1975
|
} else {
|
1976
|
// Collect all 'other' VIPs
|
1977
|
$pfb_ex_vip[] = $ex_vip;
|
1978
|
}
|
1979
|
}
|
1980
|
|
1981
|
if (isset($dnsbl_ex_vip[0]) && !isset($dnsbl_ex_vip[0]['uniqid'])) {
|
1982
|
$dnsbl_new_vip[0]['uniqid'] = uniqid();
|
1983
|
}
|
1984
|
if (isset($dnsbl_ex_vip[1]) && !isset($dnsbl_ex_vip[1]['uniqid'])) {
|
1985
|
$dnsbl_new_vip[1]['uniqid'] = uniqid();
|
1986
|
}
|
1987
|
|
1988
|
if ($mode == 'enabled') {
|
1989
|
// Compare existing to new and if they are not identical update
|
1990
|
if ($dnsbl_ex_vip !== $dnsbl_new_vip) {
|
1991
|
$pfb['dnsbl_vip_changed'] = TRUE;
|
1992
|
$pfbupdate = TRUE;
|
1993
|
$new_vip = array_merge($pfb_ex_vip, $dnsbl_new_vip);
|
1994
|
} else {
|
1995
|
$new_vip = array_merge($pfb_ex_vip, $dnsbl_ex_vip);
|
1996
|
}
|
1997
|
} else {
|
1998
|
$new_vip = array_merge($pfb_ex_vip, $new_vip);
|
1999
|
// Update when DNSBL NAT found but is now disabled.
|
2000
|
if ($pfbfound) {
|
2001
|
$pfbupdate = TRUE;
|
2002
|
}
|
2003
|
}
|
2004
|
|
2005
|
// Compare previous Lighttpd conf
|
2006
|
$pfb_lighty_conf = pfb_create_lighttpd();
|
2007
|
$pfb_lighty_conf_ex = @file_get_contents($pfb['dnsbl_conf']);
|
2008
|
if ($pfb_lighty_conf !== $pfb_lighty_conf_ex) {
|
2009
|
$pfbupdate = TRUE;
|
2010
|
@file_put_contents($pfb['dnsbl_conf'], $pfb_lighty_conf, LOCK_EX);
|
2011
|
$log = "\nSaving new DNSBL web server configuration to port [ {$pfb['dnsbl_port']} and {$pfb['dnsbl_port_ssl']} ]";
|
2012
|
pfb_logger("{$log}", 1);
|
2013
|
}
|
2014
|
|
2015
|
// Validate DNSBL VIP address(es)
|
2016
|
$result = array();
|
2017
|
foreach (array("inet {$pfb['dnsbl_vip']}", "inet6 ::{$pfb['dnsbl_vip']}") as $g_vip) {
|
2018
|
$g_vip = escapeshellarg($g_vip);
|
2019
|
exec("/sbin/ifconfig {$iface} | {$pfb['grep']} {$g_vip} 2>&1", $result, $retval);
|
2020
|
}
|
2021
|
if (count($result) != $vip_count) {
|
2022
|
$pfbupdate = TRUE;
|
2023
|
}
|
2024
|
|
2025
|
// Update config.xml, if changes required
|
2026
|
if ($pfbupdate && $pfb['dnsbl'] == 'on') {
|
2027
|
config_set_path('nat/rule', $new_nat);
|
2028
|
config_set_path('virtualip/vip', $new_vip);
|
2029
|
write_config('pfBlockerNG: saving DNSBL changes');
|
2030
|
}
|
2031
|
|
2032
|
if ($mode == 'enabled' && $pfbupdate) {
|
2033
|
|
2034
|
// Execute ifconfig to enable VIP address
|
2035
|
if (!empty($iface) && !empty($pfb['dnsbl_vip'])) {
|
2036
|
|
2037
|
if (is_service_running('pfb_dnsbl')) {
|
2038
|
pfb_logger("\nStop Service DNSBL", 1);
|
2039
|
stop_service('pfb_dnsbl');
|
2040
|
}
|
2041
|
|
2042
|
foreach (array("{$pfb['dnsbl_vip']}", "::{$pfb['dnsbl_vip']}") as $vip) {
|
2043
|
|
2044
|
$mask = '32';
|
2045
|
$inet = 'inet';
|
2046
|
if (strpos($vip, '::') !== FALSE) {
|
2047
|
$mask = '128';
|
2048
|
$inet = 'inet6';
|
2049
|
}
|
2050
|
|
2051
|
$g_vip = escapeshellarg("{$inet} {$vip}");
|
2052
|
$vip = escapeshellarg($vip);
|
2053
|
|
2054
|
// Clear any existing VIP
|
2055
|
$result = exec("/sbin/ifconfig {$iface} | {$pfb['grep']} {$g_vip} 2>&1");
|
2056
|
if (!empty($result)) {
|
2057
|
exec("/sbin/ifconfig {$iface} {$inet} {$vip} -alias 2>&1");
|
2058
|
}
|
2059
|
|
2060
|
if ($inet == 'inet6' && $pfb['dnsbl_v6'] != 'on') {
|
2061
|
break;
|
2062
|
}
|
2063
|
|
2064
|
// Configure VIP type (ipalias or carp)
|
2065
|
if ($pfb['dnsbl_vip_type'] == 'carp') {
|
2066
|
$vhid = escapeshellarg("vhid {$pfb['dnsbl_vip_vhid']}");
|
2067
|
$advbase = escapeshellarg("advbase {$pfb['dnsbl_vip_base']}");
|
2068
|
$advskew = escapeshellarg("advskew {$pfb['dnsbl_vip_skew']}");
|
2069
|
if (config_path_enabled('', 'virtualip_carp_maintenancemode')) {
|
2070
|
$advskew = 'advskew 254';
|
2071
|
}
|
2072
|
|
2073
|
$password = '';
|
2074
|
if (!empty($pfb['dnsbl_vip_pass'])) {
|
2075
|
$password = 'pass ' . escapeshellarg(addslashes(str_replace(' ', '', $pfb['dnsbl_vip_pass'])));
|
2076
|
}
|
2077
|
|
2078
|
exec("/sbin/ifconfig {$iface} {$inet} {$vhid} {$advskew} {$advbase} {$password} 2>&1");
|
2079
|
exec("/sbin/ifconfig {$iface} {$inet} '{$vip}/{$mask}' alias {$vhid} 2>&1");
|
2080
|
}
|
2081
|
else {
|
2082
|
exec("/sbin/ifconfig {$iface} {$inet} '{$vip}/{$mask}' alias 2>&1");
|
2083
|
}
|
2084
|
|
2085
|
// Validate 'tentative' interface state
|
2086
|
for ($i=10; $i > 0; $i--) {
|
2087
|
$result = exec("/sbin/ifconfig {$iface} | {$pfb['grep']} {$g_vip} | {$pfb['grep']} 'tentative' 2>&1");
|
2088
|
if (!empty($result)) {
|
2089
|
pfb_logger('.', 1);
|
2090
|
usleep(500000);
|
2091
|
} else {
|
2092
|
break;
|
2093
|
}
|
2094
|
}
|
2095
|
}
|
2096
|
|
2097
|
$log = "\nVIP address(es) configured";
|
2098
|
pfb_logger("{$log}", 1);
|
2099
|
$pfb['filter_configure'] = TRUE;
|
2100
|
} else {
|
2101
|
$log = "DNSBL ifconfig error : Interface:{$iface}, VIP:{$pfb['dnsbl_iface']}\n";
|
2102
|
pfb_logger("{$log}", 1);
|
2103
|
}
|
2104
|
}
|
2105
|
}
|
2106
|
|
2107
|
// Save settings, restart services as required
|
2108
|
if ($mode == 'enabled') {
|
2109
|
|
2110
|
// Create DNSBL SSL certificate
|
2111
|
if (!file_exists("{$pfb['dnsbl_cert']}")) {
|
2112
|
pfb_create_dnsbl_cert();
|
2113
|
|
2114
|
$log = "\nNew DNSBL certificate created";
|
2115
|
pfb_logger("{$log}", 1);
|
2116
|
}
|
2117
|
|
2118
|
if ($pfbupdate || !is_service_running('pfb_dnsbl')) {
|
2119
|
|
2120
|
// Remove any existing and create link for DNSBL lighttpd executable
|
2121
|
unlink_if_exists('/usr/local/sbin/lighttpd_pfb');
|
2122
|
link('/usr/local/sbin/lighttpd', '/usr/local/sbin/lighttpd_pfb');
|
2123
|
|
2124
|
$log = "\nRestarting DNSBL Service";
|
2125
|
pfb_logger("{$log}", 1);
|
2126
|
restart_service('pfb_dnsbl');
|
2127
|
}
|
2128
|
}
|
2129
|
else {
|
2130
|
if (is_service_running('pfb_dnsbl')) {
|
2131
|
pfb_logger("Stop Service DNSBL\n", 1);
|
2132
|
stop_service('pfb_dnsbl');
|
2133
|
}
|
2134
|
|
2135
|
// Remove DNSBL VIP address
|
2136
|
if (!empty($iface) && !empty($pfb['dnsbl_vip'])) {
|
2137
|
foreach (array("{$pfb['dnsbl_vip']}" => 'inet', "::{$pfb['dnsbl_vip']}" => 'inet6') as $vip => $inet) {
|
2138
|
|
2139
|
$g_vip = escapeshellarg("{$inet} {$vip}");
|
2140
|
$vip = escapeshellarg($vip);
|
2141
|
|
2142
|
$result = exec("/sbin/ifconfig {$iface} | {$pfb['grep']} {$g_vip} 2>&1");
|
2143
|
if (!empty($result)) {
|
2144
|
exec("/sbin/ifconfig {$iface} {$inet} {$vip} -alias 2>&1");
|
2145
|
$pfb['filter_configure'] = TRUE;
|
2146
|
}
|
2147
|
|
2148
|
// Validate 'tentative' interface state
|
2149
|
for ($i=10; $i > 0; $i--) {
|
2150
|
$result = exec("/sbin/ifconfig {$iface} | {$pfb['grep']} {$g_vip} | {$pfb['grep']} 'tentative' 2>&1");
|
2151
|
if (!empty($result)) {
|
2152
|
pfb_logger('.', 1);
|
2153
|
usleep(500000);
|
2154
|
} else {
|
2155
|
break;
|
2156
|
}
|
2157
|
}
|
2158
|
}
|
2159
|
}
|
2160
|
}
|
2161
|
}
|
2162
|
|
2163
|
|
2164
|
// Define DNSBL Unbound include settings (config.xml)
|
2165
|
function pfb_unbound_dnsbl($mode) {
|
2166
|
global $g, $pfb;
|
2167
|
pfb_global();
|
2168
|
|
2169
|
// Reload config.xml to get any recent changes
|
2170
|
config_read_file(false, true);
|
2171
|
|
2172
|
$pfbupdate = FALSE;
|
2173
|
$unbound_include = "server:include: {$pfb['dnsbl_file']}.*conf";
|
2174
|
|
2175
|
// Collect Unbound custom option pfSense conf line
|
2176
|
$pfb['unboundconfig'] = config_get_path('unbound/custom_options');
|
2177
|
|
2178
|
if (!empty($pfb['unboundconfig'])) {
|
2179
|
$unbound_custom = base64_decode($pfb['unboundconfig']);
|
2180
|
} else {
|
2181
|
$unbound_custom = '';
|
2182
|
}
|
2183
|
|
2184
|
// Determine if DNSBL include line exists
|
2185
|
if (!empty($unbound_custom)) {
|
2186
|
// Append DNSBL Unbound pfSense conf integration
|
2187
|
if (!strstr($unbound_custom, 'pfb_dnsbl.*conf')) {
|
2188
|
if ($mode == 'enabled') {
|
2189
|
if (!$pfb['dnsbl_py_blacklist']) {
|
2190
|
$pfbupdate = TRUE;
|
2191
|
$unbound_custom .= "\n{$unbound_include}";
|
2192
|
$log = "\nAdding DNSBL Unbound mode (Resolver adv. setting)";
|
2193
|
}
|
2194
|
|
2195
|
// To be removed when SafeSearch CNAME python mode has been fixed
|
2196
|
elseif ($pfb['safesearch_enable'] !== 'Disable') {
|
2197
|
$pfbupdate = TRUE;
|
2198
|
$unbound_custom .= "\n{$unbound_include}";
|
2199
|
$log = "\nAdding DNSBL SafeSearch CNAME mode (Resolver adv. setting)";
|
2200
|
}
|
2201
|
}
|
2202
|
}
|
2203
|
else {
|
2204
|
// Remove DNSBL Unbound pfSense conf integration when disabled
|
2205
|
// or when DNSBL python mode is enabled but not when SafeSearch is enabled
|
2206
|
if ($mode == 'disabled' || $pfb['dnsbl_py_blacklist']) {
|
2207
|
$custom = explode ("\n", $unbound_custom);
|
2208
|
foreach ($custom as $key => $line) {
|
2209
|
if (strpos($line, 'pfb_dnsbl.*conf') !== FALSE) {
|
2210
|
$pfbupdate = TRUE;
|
2211
|
if (!$pfb['dnsbl_py_blacklist']) {
|
2212
|
$log = "\nRemoving DNSBL SafeSearch mode (Resolver adv. setting)";
|
2213
|
unset($custom[$key]);
|
2214
|
}
|
2215
|
|
2216
|
// To be removed when SafeSearch CNAME python mode has been fixed
|
2217
|
elseif ($pfb['safesearch_enable'] !== 'Disable') {
|
2218
|
//
|
2219
|
}
|
2220
|
|
2221
|
else {
|
2222
|
$log = "\nRemoving DNSBL Unbound mode and/or DNSBL SafeSearch CNAME mode (Resolver adv. setting)";
|
2223
|
unset($custom[$key]);
|
2224
|
}
|
2225
|
}
|
2226
|
}
|
2227
|
$unbound_custom = implode("\n", $custom);
|
2228
|
}
|
2229
|
}
|
2230
|
}
|
2231
|
else {
|
2232
|
// Add DNSBL Unbound pfSense conf integration
|
2233
|
if ($mode == 'enabled') {
|
2234
|
if (!$pfb['dnsbl_py_blacklist']) {
|
2235
|
$pfbupdate = TRUE;
|
2236
|
$unbound_custom = "{$unbound_include}";
|
2237
|
$log = "\nAdding DNSBL Unbound mode (Resolver adv. setting)";
|
2238
|
}
|
2239
|
|
2240
|
// To be removed when SafeSearch CNAME python mode has been fixed
|
2241
|
elseif ($pfb['safesearch_enable'] !== 'Disable') {
|
2242
|
$pfbupdate = TRUE;
|
2243
|
$unbound_custom = "{$unbound_include}";
|
2244
|
$log = "\nAdding DNSBL SafeSearch CNAME mode (Resolver adv. setting)";
|
2245
|
}
|
2246
|
}
|
2247
|
}
|
2248
|
|
2249
|
// Remove the previous include line, see Bug #6603
|
2250
|
$custom = explode ("\n", $unbound_custom);
|
2251
|
foreach ($custom as $key => $line) {
|
2252
|
if (strpos($line, 'pfb_dnsbl.conf') !== FALSE) {
|
2253
|
$pfbupdate = TRUE;
|
2254
|
$log .= "\nDNSBL - Removing previous DNSBL Unbound custom option\n";
|
2255
|
unset($custom[$key]);
|
2256
|
}
|
2257
|
}
|
2258
|
$unbound_custom = implode("\n", $custom);
|
2259
|
|
2260
|
// Update config.xml, if changes required
|
2261
|
if ($pfbupdate) {
|
2262
|
pfb_logger("{$log}", 1);
|
2263
|
$unbound_custom = base64_encode(str_replace("\r\n", "\n", $unbound_custom));
|
2264
|
$pfb['unboundconfig'] = "{$unbound_custom}";
|
2265
|
config_set_path('unbound/custom_options', $pfb['unboundconfig']);
|
2266
|
write_config('pfBlockerNG: saving Unbound custom options');
|
2267
|
}
|
2268
|
|
2269
|
// Modify unbound.conf file as required
|
2270
|
if (file_exists("{$pfb['dnsbldir']}/unbound.conf")) {
|
2271
|
$conf = file("{$pfb['dnsbldir']}/unbound.conf");
|
2272
|
if (empty($conf)) {
|
2273
|
pfb_logger("\nDNS Resolver configuration file missing or empty, Exiting!", 1);
|
2274
|
}
|
2275
|
|
2276
|
$unbound = FALSE; // Unbound mode
|
2277
|
$unbound_py = FALSE; // Unbound python mode
|
2278
|
|
2279
|
$u_update = FALSE;
|
2280
|
$u_msg = '';
|
2281
|
foreach ($conf as $key => $line) {
|
2282
|
|
2283
|
if (empty($line)) {
|
2284
|
continue;
|
2285
|
}
|
2286
|
|
2287
|
elseif (strpos($line, 'pfb_dnsbl.*conf') !== FALSE) {
|
2288
|
if ($mode == 'enabled') {
|
2289
|
if (!$pfb['dnsbl_py_blacklist']) {
|
2290
|
$unbound = TRUE;
|
2291
|
}
|
2292
|
|
2293
|
// To be removed when SafeSearch CNAME python mode has been fixed
|
2294
|
elseif ($pfb['safesearch_enable'] !== 'Disable') {
|
2295
|
$unbound = TRUE;
|
2296
|
}
|
2297
|
|
2298
|
else {
|
2299
|
$u_update = TRUE;
|
2300
|
$u_msg .= " Removed DNSBL SafeSearch mode\n";
|
2301
|
unset($conf[$key]);
|
2302
|
}
|
2303
|
} else {
|
2304
|
$u_update = TRUE;
|
2305
|
$u_msg .= " Removed DNSBL Unbound mode\n";
|
2306
|
unset($conf[$key]);
|
2307
|
}
|
2308
|
}
|
2309
|
|
2310
|
elseif (strpos($line, 'module-config:') !== FALSE) {
|
2311
|
if ($mode == 'enabled' && $pfb['dnsbl_mode'] == 'dnsbl_python') {
|
2312
|
if (strpos($line, 'module-config: "python') !== FALSE) {
|
2313
|
$unbound_py = TRUE;
|
2314
|
} else {
|
2315
|
$u_update = TRUE;
|
2316
|
$u_msg .= " Added DNSBL Unbound Python mode\n";
|
2317
|
$conf[$key] = str_replace('module-config: "', 'module-config: "python ', $line);
|
2318
|
}
|
2319
|
}
|
2320
|
else {
|
2321
|
// Only remove python module if script is 'pfb_unbound'
|
2322
|
if (strpos($line, 'module-config: "python') !== FALSE && in_array("python-script: pfb_unbound.py\n", $conf)) {
|
2323
|
$u_update = TRUE;
|
2324
|
$u_msg .= " Removed DNSBL Unbound Python mode\n";
|
2325
|
$conf[$key] = str_replace('module-config: "python ', 'module-config: "', $line);
|
2326
|
}
|
2327
|
}
|
2328
|
}
|
2329
|
|
2330
|
// Remove any DNSBL VIPs added to unbound.conf on 'disable'
|
2331
|
elseif ((strpos($line, 'interface:') !== FALSE) && ($mode == 'disabled') &&
|
2332
|
!empty($pfb['dnsbl_vip']) && (strpos($line, $pfb['dnsbl_vip']) !== FALSE)) {
|
2333
|
$u_update = TRUE;
|
2334
|
$u_msg .= " Removed DNSBL VIP from Unbound Interface settings\n";
|
2335
|
unset($conf[$key]);
|
2336
|
}
|
2337
|
|
2338
|
elseif (strpos($line, 'python-script: pfb_unbound.py') !== FALSE) {
|
2339
|
if ($mode == 'enabled' && $pfb['dnsbl_mode'] == 'dnsbl_python') {
|
2340
|
$unbound_py = TRUE;
|
2341
|
} else {
|
2342
|
$u_update = TRUE;
|
2343
|
$u_msg .= " Removed DNSBL Unbound Python mode script\n";
|
2344
|
unset($conf[$key-1]); // remove 'python:' line above
|
2345
|
unset($conf[$key]);
|
2346
|
}
|
2347
|
}
|
2348
|
}
|
2349
|
|
2350
|
// Add Unbound include line
|
2351
|
if (!$unbound && $mode == 'enabled') {
|
2352
|
if (!$pfb['dnsbl_py_blacklist']) {
|
2353
|
$u_update = TRUE;
|
2354
|
$u_msg .= " Added DNSBL Unbound mode\n";
|
2355
|
$conf[] = "\nserver:include: {$pfb['dnsbl_file']}.*conf\n";
|
2356
|
}
|
2357
|
|
2358
|
// To be removed when SafeSearch CNAME python mode has been fixed
|
2359
|
elseif ($pfb['safesearch_enable'] !== 'Disable') {
|
2360
|
$u_update = TRUE;
|
2361
|
$u_msg .= " Added DNSBL SafeSearch CNAME mode\n";
|
2362
|
$conf[] = "\nserver:include: {$pfb['dnsbl_file']}.*conf\n";
|
2363
|
}
|
2364
|
}
|
2365
|
|
2366
|
// Add python script line
|
2367
|
if (!$unbound_py && $mode == 'enabled' && $pfb['dnsbl_mode'] == 'dnsbl_python') {
|
2368
|
$u_update = TRUE;
|
2369
|
$u_msg .= " Added DNSBL Unbound Python mode script\n";
|
2370
|
$conf[] = "\npython:\npython-script: pfb_unbound.py\n";
|
2371
|
}
|
2372
|
|
2373
|
if ($mode == 'enabled' && $pfb['dnsbl_mode'] == 'dnsbl_python') {
|
2374
|
if (!file_exists("{$g['unbound_chroot_path']}/pfb_unbound.py")) {
|
2375
|
@copy("/usr/local/pkg/pfblockerng/pfb_unbound.py", "{$g['unbound_chroot_path']}/pfb_unbound.py");
|
2376
|
}
|
2377
|
if (!file_exists("{$g['unbound_chroot_path']}/pfb_unbound_include.inc")) {
|
2378
|
@copy("/usr/local/pkg/pfblockerng/pfb_unbound_include.inc", "{$g['unbound_chroot_path']}/pfb_unbound_include.inc");
|
2379
|
}
|
2380
|
if (!file_exists("{$g['unbound_chroot_path']}/pfb_py_hsts.txt")) {
|
2381
|
@copy("/usr/local/pkg/pfblockerng/pfb_py_hsts.txt", "{$g['unbound_chroot_path']}/pfb_py_hsts.txt");
|
2382
|
}
|
2383
|
} else {
|
2384
|
unlink_if_exists("{$g['unbound_chroot_path']}/pfb_unbound.py");
|
2385
|
unlink_if_exists("{$g['unbound_chroot_path']}/pfb_unbound_include.inc");
|
2386
|
unlink_if_exists("{$g['unbound_chroot_path']}/pfb_py_hsts.txt");
|
2387
|
}
|
2388
|
|
2389
|
// Save changes to unbound.conf
|
2390
|
if ($u_update) {
|
2391
|
pfb_logger("\nDNS Resolver ( {$mode} ) unbound.conf modifications:\n{$u_msg}", 1);
|
2392
|
@file_put_contents("{$pfb['dnsbldir']}/unbound.tmp", $conf, LOCK_EX);
|
2393
|
@chown("{$pfb['dnsbldir']}/unbound.tmp", 'unbound');
|
2394
|
@chgrp("{$pfb['dnsbldir']}/unbound.tmp", 'unbound');
|
2395
|
return TRUE;
|
2396
|
}
|
2397
|
}
|
2398
|
else {
|
2399
|
pfb_logger("\n\n*** [ Unbound.conf file missing. Exiting! ] ***\n\n", 1);
|
2400
|
}
|
2401
|
|
2402
|
return FALSE;
|
2403
|
}
|
2404
|
|
2405
|
|
2406
|
// Create DNSBL Whitelist
|
2407
|
function pfb_unbound_python_whitelist($mode='') {
|
2408
|
global $pfb;
|
2409
|
pfb_global();
|
2410
|
|
2411
|
$dnsbl_whitelist = '';
|
2412
|
$dnsbl_white = pfbng_text_area_decode($pfb['dnsblconfig']['suppression'], TRUE, FALSE, TRUE);
|
2413
|
if (!empty($dnsbl_white)) {
|
2414
|
foreach ($dnsbl_white as $key => $line) {
|
2415
|
if (!empty($line)) {
|
2416
|
if (substr($line, 0, 4) == 'www.') {
|
2417
|
$line = substr($line, 4);
|
2418
|
}
|
2419
|
|
2420
|
// Minimize the python whitelist queries to the smallest tld segment count
|
2421
|
if (!isset($tld_segments)) {
|
2422
|
$tld_segments = (substr_count($line, '.') +1);
|
2423
|
}
|
2424
|
$tld_segments = @min((array((substr_count($line, '.') +1), $tld_segments) ?: 1));
|
2425
|
|
2426
|
if (substr($line, 0, 1) == '.') {
|
2427
|
$line = ltrim($line, '.');
|
2428
|
$dnsbl_whitelist .= "{$line},1\n";
|
2429
|
} else {
|
2430
|
$dnsbl_whitelist .= "{$line},0\n";
|
2431
|
}
|
2432
|
}
|
2433
|
}
|
2434
|
}
|
2435
|
|
2436
|
if ($mode == 'alerts') {
|
2437
|
@file_put_contents($pfb['unbound_py_wh'], $dnsbl_whitelist, LOCK_EX);
|
2438
|
} else {
|
2439
|
return $dnsbl_whitelist;
|
2440
|
}
|
2441
|
}
|
2442
|
|
2443
|
|
2444
|
// Unbound python configuration file
|
2445
|
function pfb_unbound_python($mode) {
|
2446
|
global $pfb;
|
2447
|
pfb_global();
|
2448
|
|
2449
|
// Reload config.xml to get any recent changes
|
2450
|
config_read_file(false, true);
|
2451
|
|
2452
|
$pfbpython = FALSE;
|
2453
|
|
2454
|
// Ensure log file permissions are set as 'unbound:unbound'
|
2455
|
foreach (array('dnsbl.log', 'dns_reply.log', 'unified.log') as $logfile) {
|
2456
|
|
2457
|
if (!file_exists("{$pfb['logdir']}/{$logfile}")) {
|
2458
|
touch("{$pfb['logdir']}/{$logfile}");
|
2459
|
}
|
2460
|
|
2461
|
@chown("{$pfb['logdir']}/{$logfile}", 'unbound');
|
2462
|
@chgrp("{$pfb['logdir']}/{$logfile}", 'unbound');
|
2463
|
}
|
2464
|
|
2465
|
// Add python settings to DNS Resolver configuration
|
2466
|
$python_enable = 'off';
|
2467
|
if ($mode == 'enabled' && $pfb['dnsbl_mode'] == 'dnsbl_python') {
|
2468
|
$python_enable = 'on';
|
2469
|
|
2470
|
if (!config_path_enabled('unbound', 'python') ||
|
2471
|
config_get_path('unbound/python_script') != 'pfb_unbound') {
|
2472
|
|
2473
|
config_set_path('unbound/python', '');
|
2474
|
config_set_path('unbound/python_order', 'pre_validator');
|
2475
|
config_set_path('unbound/python_script', 'pfb_unbound');
|
2476
|
|
2477
|
$pfbpython = TRUE;
|
2478
|
$log = 'Added DNSBL Unbound python integration settings';
|
2479
|
pfb_logger("\n{$log}", 1);
|
2480
|
write_config("pfBlockerNG: {$log}");
|
2481
|
}
|
2482
|
|
2483
|
// If DNSBL python blocking mode enabled
|
2484
|
if ($pfb['dnsbl_py_blacklist']) {
|
2485
|
|
2486
|
// Create DNSBL Whitelist
|
2487
|
$dnsbl_whitelist = pfb_unbound_python_whitelist();
|
2488
|
|
2489
|
// Compare previous DNSBL Whitelist to new Whitelist
|
2490
|
$pfb_py_whitelist_ex = @file_get_contents($pfb['unbound_py_wh']);
|
2491
|
if ($dnsbl_whitelist !== $pfb_py_whitelist_ex) {
|
2492
|
$pfbpython = TRUE;
|
2493
|
@file_put_contents($pfb['unbound_py_wh'], $dnsbl_whitelist, LOCK_EX);
|
2494
|
}
|
2495
|
}
|
2496
|
|
2497
|
// Remove previous whitelist and reload
|
2498
|
elseif (file_exists($pfb['unbound_py_wh'])) {
|
2499
|
unlink_if_exists($pfb['unbound_py_wh']);
|
2500
|
$pfbpython = TRUE;
|
2501
|
}
|
2502
|
|
2503
|
if (!isset($tld_segments)) {
|
2504
|
$tld_segments = '1';
|
2505
|
}
|
2506
|
|
2507
|
$python_ipv6 = 'off';
|
2508
|
if ($pfb['dnsbl_v6'] == 'on') {
|
2509
|
$python_ipv6 = 'on';
|
2510
|
}
|
2511
|
|
2512
|
$python_reply = 'off';
|
2513
|
if ($pfb['dnsbl_py_reply'] == 'on') {
|
2514
|
$python_reply = 'on';
|
2515
|
}
|
2516
|
|
2517
|
$python_blocking = 'off';
|
2518
|
if ($pfb['dnsbl_py_blacklist']) {
|
2519
|
$python_blocking = 'on';
|
2520
|
}
|
2521
|
|
2522
|
$python_hsts = 'off';
|
2523
|
if ($pfb['dnsbl_hsts'] == 'on') {
|
2524
|
$python_hsts = 'on';
|
2525
|
}
|
2526
|
|
2527
|
$python_idn = 'off';
|
2528
|
if ($pfb['dnsbl_idn'] == 'on') {
|
2529
|
$python_idn = 'on';
|
2530
|
}
|
2531
|
|
2532
|
$python_cname = 'off';
|
2533
|
if ($pfb['dnsbl_cname'] == 'on') {
|
2534
|
$python_cname = 'on';
|
2535
|
}
|
2536
|
|
2537
|
$python_control = 'off';
|
2538
|
if ($pfb['dnsbl_control'] == 'on') {
|
2539
|
$python_control = 'on';
|
2540
|
}
|
2541
|
|
2542
|
$python_noaaaa = 'off';
|
2543
|
if ($pfb['dnsbl_noaaaa'] == 'on') {
|
2544
|
$python_noaaaa = 'on';
|
2545
|
}
|
2546
|
|
2547
|
$python_tld = 'off';
|
2548
|
if ($pfb['dnsbl_pytld'] == 'on') {
|
2549
|
$python_tlds = '';
|
2550
|
if (!empty($pfb['dnsblconfig']['pfb_pytlds_gtld'])) {
|
2551
|
$python_tld = 'on';
|
2552
|
$python_tlds = $pfb['dnsblconfig']['pfb_pytlds_gtld'];
|
2553
|
}
|
2554
|
if (!empty($pfb['dnsblconfig']['pfb_pytlds_cctld'])) {
|
2555
|
$python_tld = 'on';
|
2556
|
$python_tlds .= ',' . $pfb['dnsblconfig']['pfb_pytlds_cctld'];
|
2557
|
}
|
2558
|
if (!empty($pfb['dnsblconfig']['pfb_pytlds_itld'])) {
|
2559
|
$python_tld = 'on';
|
2560
|
$python_tlds .= ',' . $pfb['dnsblconfig']['pfb_pytlds_itld'];
|
2561
|
}
|
2562
|
if (!empty($pfb['dnsblconfig']['pfb_pytlds_bgtld'])) {
|
2563
|
$python_tld = 'on';
|
2564
|
$python_tlds .= ',' . $pfb['dnsblconfig']['pfb_pytlds_bgtld'];
|
2565
|
}
|
2566
|
$python_tlds = ltrim($python_tlds, ',');
|
2567
|
}
|
2568
|
|
2569
|
$python_nolog = 'off';
|
2570
|
if ($pfb['dnsbl_py_nolog'] == 'on') {
|
2571
|
$python_nolog = 'on';
|
2572
|
}
|
2573
|
|
2574
|
$now = date('m/j/y H:i:s', time());
|
2575
|
|
2576
|
$pfb_py_conf = <<<EOF
|
2577
|
; pfBlockerNG DNSBL Unbound python configuration file
|
2578
|
; pfb_unbound.ini [ File created: {$now} ]
|
2579
|
[MAIN]
|
2580
|
dnsbl_ipv4 = {$pfb['dnsbl_vip']}
|
2581
|
python_enable = {$python_enable}
|
2582
|
python_ipv6 = {$python_ipv6}
|
2583
|
python_reply = {$python_reply}
|
2584
|
python_blocking = {$python_blocking}
|
2585
|
python_hsts = {$python_hsts}
|
2586
|
python_idn = {$python_idn}
|
2587
|
python_tld_seg = {$tld_segments}
|
2588
|
python_tld = {$python_tld}
|
2589
|
python_tlds = {$python_tlds}
|
2590
|
python_nolog = {$python_nolog}
|
2591
|
python_cname = {$python_cname}
|
2592
|
python_control = {$python_control}
|
2593
|
|
2594
|
EOF;
|
2595
|
if ($pfb['dnsbl_regex'] == 'on' && isset($pfb['dnsbl_regex_list']) && !empty($pfb['dnsbl_regex_list'])) {
|
2596
|
$regex = '';
|
2597
|
$regex_list = pfbng_text_area_decode($pfb['dnsbl_regex_list'], TRUE, TRUE, FALSE);
|
2598
|
if (!empty($regex_list)) {
|
2599
|
$counter = 1;
|
2600
|
$key_index = array();
|
2601
|
foreach ($regex_list as $key => $list) {
|
2602
|
if (!isset($list[1])) {
|
2603
|
$list[1] = "Regex_{$counter}";
|
2604
|
} else {
|
2605
|
$list[1] = trim(ltrim($list[1], '#'));
|
2606
|
$list[1] = preg_replace("/\W/", '', str_replace(' ', '_', $list[1]));
|
2607
|
}
|
2608
|
|
2609
|
// Check if key exists
|
2610
|
if (!isset($key_index[$list[1]])) {
|
2611
|
$regex .= "{$list[1]} = {$list[0]}\n";
|
2612
|
$key_index[$list[1]] = '';
|
2613
|
} else {
|
2614
|
$regex .= "{$list[1]}_{$counter} = {$list[0]}\n";
|
2615
|
$key_index["{$list[1]}_{$counter}"] = '';
|
2616
|
}
|
2617
|
$counter++;
|
2618
|
}
|
2619
|
$pfb_py_conf .= <<<EOF
|
2620
|
|
2621
|
[REGEX]
|
2622
|
{$regex}
|
2623
|
EOF;
|
2624
|
}
|
2625
|
}
|
2626
|
|
2627
|
if ($pfb['dnsbl_noaaaa'] == 'on' && isset($pfb['dnsbl_noaaaa_list']) && !empty($pfb['dnsbl_noaaaa_list'])) {
|
2628
|
$noaaaa = '';
|
2629
|
$noaaaa_list = pfbng_text_area_decode($pfb['dnsbl_noaaaa_list'], TRUE, TRUE, TRUE);
|
2630
|
if (!empty($noaaaa_list)) {
|
2631
|
foreach ($noaaaa_list as $key => $list) {
|
2632
|
if (substr($list[0], 0, 1) == '.') {
|
2633
|
$list[0] = ltrim($list[0], '.');
|
2634
|
$noaaaa .= "{$key} = {$list[0]},1\n";
|
2635
|
} else {
|
2636
|
$noaaaa .= "{$key} = {$list[0]},0\n";
|
2637
|
}
|
2638
|
}
|
2639
|
$pfb_py_conf .= <<<EOF
|
2640
|
|
2641
|
[noAAAA]
|
2642
|
{$noaaaa}
|
2643
|
EOF;
|
2644
|
}
|
2645
|
}
|
2646
|
|
2647
|
if ($pfb['dnsbl_gp'] == 'on' && isset($pfb['dnsbl_gp_bypass_list']) && !empty($pfb['dnsbl_gp_bypass_list'])) {
|
2648
|
$gp_bypass = '';
|
2649
|
$gp_bypass_list = pfbng_text_area_decode($pfb['dnsbl_gp_bypass_list'], TRUE, TRUE, FALSE);
|
2650
|
if (!empty($gp_bypass_list)) {
|
2651
|
foreach ($gp_bypass_list as $key => $list) {
|
2652
|
$gp_bypass .= "{$key} = {$list[0]}\n";
|
2653
|
}
|
2654
|
$pfb_py_conf .= <<<EOF
|
2655
|
|
2656
|
[GP_Bypass_List]
|
2657
|
{$gp_bypass}
|
2658
|
EOF;
|
2659
|
}
|
2660
|
}
|
2661
|
|
2662
|
// Compare previous ini file to new ini (bypass file timestamp string)
|
2663
|
$pfb_py_conf_ex = @file_get_contents($pfb['unbound_py_conf']);
|
2664
|
$pfb_py_conf_ex2 = preg_replace("/File created:.* ]/", "File created: {$now} ]", $pfb_py_conf_ex);
|
2665
|
|
2666
|
if ($pfb_py_conf !== $pfb_py_conf_ex2) {
|
2667
|
$pfbpython = TRUE;
|
2668
|
@file_put_contents($pfb['unbound_py_conf'], $pfb_py_conf, LOCK_EX);
|
2669
|
}
|
2670
|
}
|
2671
|
|
2672
|
else {
|
2673
|
$mode = 'disabled';
|
2674
|
|
2675
|
// Remove python settings from DNS Resolver configuration
|
2676
|
if (config_path_enabled('unbound', 'python') && config_get_path('unbound/python_script') == 'pfb_unbound') {
|
2677
|
config_del_path('unbound/python');
|
2678
|
config_set_path('unbound/python_order', '');
|
2679
|
config_set_path('unbound/python_script', '');
|
2680
|
|
2681
|
$log = 'Removing DNSBL Unbound python integration settings';
|
2682
|
pfb_logger("\n{$log}", 1);
|
2683
|
write_config("pfBlockerNG: {$log}");
|
2684
|
}
|
2685
|
}
|
2686
|
|
2687
|
// Mount lib and bin folders
|
2688
|
$base_py = '/usr/local';
|
2689
|
|
2690
|
$log = '';
|
2691
|
foreach (array('/bin', '/lib') as $dir) {
|
2692
|
$validate = exec("/sbin/mount | {$pfb['grep']} " . escapeshellarg("{$pfb['dnsbldir']}{$base_py}{$dir}") . " 2>&1");
|
2693
|
|
2694
|
if ($mode == 'enabled' && empty($validate)) {
|
2695
|
if (!is_dir("{$pfb['dnsbldir']}{$base_py}{$dir}")) {
|
2696
|
$log .= "\n Creating: {$pfb['dnsbldir']}{$base_py}{$dir}";
|
2697
|
safe_mkdir("{$pfb['dnsbldir']}{$base_py}{$dir}");
|
2698
|
}
|
2699
|
$pfbpython = TRUE;
|
2700
|
$log .= "\n Mounting: /usr/local{$dir}";
|
2701
|
exec("/sbin/mount_nullfs -o ro " . escapeshellarg("/usr/local{$dir}") . ' '
|
2702
|
. escapeshellarg("{$pfb['dnsbldir']}{$base_py}{$dir}") . " 2>&1", $output, $retval);
|
2703
|
if ($retval != 0) {
|
2704
|
$log .= "\n Failed to mount [ /usr/local{$dir} ] to [ {$pfb['dnsbldir']}{$base_py}{$dir} ]!";
|
2705
|
}
|
2706
|
}
|
2707
|
}
|
2708
|
|
2709
|
if (!empty($log)) {
|
2710
|
pfb_logger("\nAdding DNSBL Unbound python mounts:{$log}\n", 1);
|
2711
|
}
|
2712
|
|
2713
|
// Set flag to unmount python folders after the next reboot is completed to avoid crashing Unbound
|
2714
|
if ($mode == 'disabled') {
|
2715
|
$pfb['dnsbl_python_unmount'] = TRUE;
|
2716
|
}
|
2717
|
return $pfbpython;
|
2718
|
}
|
2719
|
|
2720
|
|
2721
|
// Unbound python unmount
|
2722
|
function pfb_unbound_python_unmount() {
|
2723
|
global $pfb;
|
2724
|
|
2725
|
// Unmount lib and bin folders
|
2726
|
$base_py = '/usr/local';
|
2727
|
|
2728
|
$log = '';
|
2729
|
foreach (array('/bin', '/lib') as $dir) {
|
2730
|
$validate = exec("/sbin/mount | {$pfb['grep']} " . escapeshellarg("{$pfb['dnsbldir']}{$base_py}{$dir}") . " 2>&1");
|
2731
|
if (!empty($validate)) {
|
2732
|
$log .= "\n Unmounting: /usr/local{$dir}";
|
2733
|
exec("/sbin/umount -t nullfs " . escapeshellarg("{$pfb['dnsbldir']}{$base_py}{$dir}") . " 2>&1", $output, $retval);
|
2734
|
if ($retval != 0) {
|
2735
|
$log .= "\n Failed to unmount [ {$pfb['dnsbldir']}{$base_py}{$dir} ]!";
|
2736
|
}
|
2737
|
}
|
2738
|
|
2739
|
foreach (array( "/usr/local{$dir}", '/usr/local', '/usr') as $folder) {
|
2740
|
if (is_dir("{$pfb['dnsbldir']}{$folder}")) {
|
2741
|
$log .= "\n Removing: {$pfb['dnsbldir']}{$folder}";
|
2742
|
@rmdir("{$pfb['dnsbldir']}{$folder}");
|
2743
|
}
|
2744
|
|
2745
|
// Delete remaining subfolders on next loop
|
2746
|
if ($dir == '/bin') {
|
2747
|
break;
|
2748
|
}
|
2749
|
}
|
2750
|
}
|
2751
|
|
2752
|
if (!empty($log)) {
|
2753
|
pfb_logger("\nRemoving DNSBL Unbound python mounts:{$log}\n", 1);
|
2754
|
}
|
2755
|
}
|
2756
|
|
2757
|
|
2758
|
// Search for TLD match
|
2759
|
function tld_search($tld, $dparts, $j, $k) {
|
2760
|
global $tlds;
|
2761
|
|
2762
|
$tld_query = implode('.', array_slice($dparts, -$j, $j, TRUE));
|
2763
|
if (isset($tlds[$tld][$tld_query])) {
|
2764
|
return implode('.', array_slice($dparts, -$k, $k, TRUE));
|
2765
|
}
|
2766
|
return NULL;
|
2767
|
}
|
2768
|
|
2769
|
|
2770
|
// Function to determine if each Domain is a Sub-Domain ('transparent' zone) or a whole Domain ('redirect' zone)
|
2771
|
function tld_analysis() {
|
2772
|
global $pfb, $tlds;
|
2773
|
|
2774
|
if (!file_exists("{$pfb['dnsbl_file']}.raw")) {
|
2775
|
pfb_logger("\n\nTLD Analysis not required.", 1);
|
2776
|
return;
|
2777
|
}
|
2778
|
|
2779
|
pfb_logger("\nTLD:\n", 1);
|
2780
|
|
2781
|
$domain_cnt = 0;
|
2782
|
$pfb_found = FALSE; // Flag to determine if TLD 'redirect' zones found
|
2783
|
|
2784
|
rmdir_recursive("{$pfb['dnsbl_tmpdir']}");
|
2785
|
if (!$pfb['dnsbl_py_blacklist']) {
|
2786
|
safe_mkdir("{$pfb['dnsbl_tmpdir']}");
|
2787
|
}
|
2788
|
unlink_if_exists("{$pfb['dnsbl_file']}.tsp");
|
2789
|
unlink_if_exists("{$pfb['dnsbl_tld_txt']}.*");
|
2790
|
unlink_if_exists("{$pfb['dnsbl_tld_remove']}.tsp");
|
2791
|
unlink_if_exists("{$pfb['dnsbl_tld_remove']}");
|
2792
|
unlink_if_exists("{$pfb['dnsbl_tmp']}.sup");
|
2793
|
unlink_if_exists("{$pfb['dnsbl_tmp']}.adup");
|
2794
|
|
2795
|
// Master TLD Domain list
|
2796
|
if (($t_handle = @fopen("{$pfb['dnsbl_tld_data']}", 'r')) !== FALSE) {
|
2797
|
while (($line = @fgets($t_handle)) !== FALSE) {
|
2798
|
$line = rtrim($line, "\x00..\x1F");
|
2799
|
$tld = substr($line, strrpos($line, '.') + 1);
|
2800
|
|
2801
|
if (!empty($tld)) {
|
2802
|
if (!is_array($tlds[$tld])) {
|
2803
|
$tlds[$tld] = array();
|
2804
|
}
|
2805
|
$tlds[$tld][$line] = '';
|
2806
|
}
|
2807
|
}
|
2808
|
@fclose($t_handle);
|
2809
|
} else {
|
2810
|
pfb_logger("\n ** TLD Master data file missing. Terminating TLD **\n", 1);
|
2811
|
return;
|
2812
|
}
|
2813
|
|
2814
|
// DNSBL python - create file handles for data, zone, and remove files
|
2815
|
if ($pfb['dnsbl_py_blacklist']) {
|
2816
|
$p_data = @fopen("{$pfb['unbound_py_data']}.raw", 'w');
|
2817
|
$p_zone = @fopen("{$pfb['unbound_py_zone']}.raw", 'w');
|
2818
|
$p_tsp = @fopen("{$pfb['dnsbl_tld_remove']}", 'w');
|
2819
|
|
2820
|
if ((get_resource_type($p_data) != 'stream') ||
|
2821
|
(get_resource_type($p_zone) != 'stream') ||
|
2822
|
(get_resource_type($p_tsp) != 'stream')) {
|
2823
|
|
2824
|
pfb_logger("\nFailed to create DNSBL python data|zone|remove file handles! Exiting\n", 1);
|
2825
|
foreach (array($p_data, $p_zone, $p_tsp) as $handlex) {
|
2826
|
if ($handlex) {
|
2827
|
@fclose($handlex);
|
2828
|
}
|
2829
|
}
|
2830
|
return;
|
2831
|
}
|
2832
|
}
|
2833
|
|
2834
|
// Collect TLD Blacklist(s). If configured the whole TLD will be blocked
|
2835
|
$tld_blacklist = pfbng_text_area_decode($pfb['dnsblconfig']['tldblacklist'], TRUE, FALSE, TRUE);
|
2836
|
if (!empty($tld_blacklist)) {
|
2837
|
$tld_blacklist = array_flip($tld_blacklist);
|
2838
|
}
|
2839
|
|
2840
|
// Collect TLD Whitelist(s). If configured, create a 'static local-zone' Resolver entry (Not required for python mode blocking)
|
2841
|
$whitelist = array();
|
2842
|
if (!$pfb['dnsbl_py_blacklist']) {
|
2843
|
$whitelist = pfbng_text_area_decode($pfb['dnsblconfig']['tldwhitelist'], TRUE, FALSE, TRUE);
|
2844
|
$tld_whitelist = array();
|
2845
|
}
|
2846
|
|
2847
|
$extdns_esc = escapeshellarg("@{$pfb['extdns']}");
|
2848
|
|
2849
|
if (!empty($tld_blacklist) && !empty($whitelist)) {
|
2850
|
foreach ($whitelist as $domain) {
|
2851
|
|
2852
|
// Use user-defined IP address
|
2853
|
if (strpos($domain, '|') !== FALSE) {
|
2854
|
list($domain, $resolved_host) = array_map('trim', explode('|', $domain));
|
2855
|
}
|
2856
|
|
2857
|
// Resolve Domain IP address
|
2858
|
else {
|
2859
|
$domain_esc = escapeshellarg($domain);
|
2860
|
$resolved_host = exec("/usr/bin/drill {$extdns_esc} {$domain_esc} | grep -v '^;\|^\$' | head -1 | cut -f5 2>&1");
|
2861
|
}
|
2862
|
|
2863
|
$tld = '';
|
2864
|
if (strpos($domain, '.') !== FALSE && is_ipaddr($resolved_host)) {
|
2865
|
$dparts = explode('.', $domain);
|
2866
|
$dcnt = count($dparts);
|
2867
|
$tld = end($dparts);
|
2868
|
|
2869
|
for ($i=($dcnt-1); $i > 0; $i--) {
|
2870
|
$d_query = implode('.', array_slice($dparts, -$i, $i, TRUE));
|
2871
|
if (isset($tlds[$tld][$d_query])) {
|
2872
|
$tld = $d_query;
|
2873
|
break;
|
2874
|
}
|
2875
|
}
|
2876
|
}
|
2877
|
|
2878
|
$resolved_host = pfb_filter($resolved_host, PFB_FILTER_IP, 'tld_analysis');
|
2879
|
if (!empty($tld) && !empty($resolved_host)) {
|
2880
|
if (!is_array($tld_whitelist[$tld])) {
|
2881
|
$tld_whitelist[$tld] = array();
|
2882
|
}
|
2883
|
$tld_whitelist[$tld][] = array($domain, $resolved_host);
|
2884
|
pfb_logger(" TLD Whitelist {$domain}|{$resolved_host}\n", 1);
|
2885
|
} elseif (!empty($resolved_host)) {
|
2886
|
pfb_logger("\n TLD Whitelist - Missing data | {$domain} | {$resolved_host} |\n", 1);
|
2887
|
}
|
2888
|
}
|
2889
|
}
|
2890
|
|
2891
|
// Process TLD Blacklist(s). If configured the whole TLD will be blocked
|
2892
|
if (!empty($tld_blacklist)) {
|
2893
|
|
2894
|
$tld_list = '';
|
2895
|
$tld_cnt = 0;
|
2896
|
$tld_segments = 0;
|
2897
|
pfb_logger(" Blocking full TLD/Sub-Domain(s)... |", 1);
|
2898
|
|
2899
|
foreach ($tld_blacklist as $tld => $key) {
|
2900
|
|
2901
|
unset($tld_blacklist[$tld]); // Remove old entry
|
2902
|
$tld = trim($tld, '.'); // Remove any leading/trailing dots
|
2903
|
$tld_blacklist[$tld] = ''; // Add new TLD entry
|
2904
|
|
2905
|
// DNSBL python - TLD Blacklist (Set logging type to enabled '1')
|
2906
|
if ($pfb['dnsbl_py_blacklist']) {
|
2907
|
$tld_cnt++;
|
2908
|
$pfb_found = TRUE;
|
2909
|
$tld_segments = @max((array((substr_count($tld, '.') +1), $tld_segments) ?: 1));
|
2910
|
|
2911
|
pfb_logger("{$tld}|", 1);
|
2912
|
@fwrite($p_zone, ",{$tld},,1,DNSBL_TLD,DNSBL_TLD\n");
|
2913
|
|
2914
|
// Add TLD to remove file
|
2915
|
@fwrite($p_tsp, ".{$tld},,\n");
|
2916
|
|
2917
|
// Collect List of TLDs and save to DNSBL folder
|
2918
|
$tld_list .= ",{$tld},,\n";
|
2919
|
|
2920
|
// Remove any 'TLD Blacklists' from the 'TLD master list'
|
2921
|
if (isset($tlds[$tld])) {
|
2922
|
unset($tlds[$tld]);
|
2923
|
}
|
2924
|
continue;
|
2925
|
}
|
2926
|
|
2927
|
// (Unbound mode only. Cannot have duplicate zones defined
|
2928
|
elseif (!empty($pfb['safesearch_tlds']) && isset($pfb['safesearch_tlds'][$tld])) {
|
2929
|
pfb_logger("\n{$tld}(Removed due to SafeSearch conflict)", 1);
|
2930
|
continue;
|
2931
|
}
|
2932
|
|
2933
|
$dnsbl_file = "{$pfb['dnsbl_tmpdir']}/DNSBL_{$tld}.txt";
|
2934
|
if (!file_exists($dnsbl_file)) {
|
2935
|
|
2936
|
$tld_cnt++;
|
2937
|
$pfb_found = TRUE;
|
2938
|
$tld_segments = @max((array((substr_count($tld, '.') +1), $tld_segments) ?: 1));
|
2939
|
|
2940
|
// If a 'TLD Whitelist' exists, use 'static local-zone'
|
2941
|
if (isset($tld_whitelist[$tld])) {
|
2942
|
|
2943
|
pfb_logger("{$tld}(static)|", 1);
|
2944
|
$dnsbl_line = "local-zone: \"{$tld}\" \"static\"\n";
|
2945
|
$whitelist = $tld_whitelist[$tld];
|
2946
|
|
2947
|
foreach ($whitelist as $list) {
|
2948
|
$ip_type = is_ipaddr($list[1]);
|
2949
|
$ip_esc = escapeshellarg($list[0]);
|
2950
|
|
2951
|
switch ($ip_type) {
|
2952
|
case 4:
|
2953
|
$dnsbl_line .= "local-data: \"{$list[0]} A {$list[1]}\"\n";
|
2954
|
$tld_list .= "{$tld} | {$list[0]} A {$list[1]}\n";
|
2955
|
if ($pfb['dnsbl_v6'] == 'on') {
|
2956
|
$r = exec("/usr/bin/drill {$extdns_esc} AAAA {$ip_esc} | grep -v '^;\|^\$' | head -1 | cut -f5 2>&1");
|
2957
|
if (is_ipaddrv6($r)) {
|
2958
|
$dnsbl_line .= "local-data: \"{$list[0]} AAAA {$r}\"\n";
|
2959
|
$tld_list .= "{$list[0]} AAAA {$r}\"\n";
|
2960
|
}
|
2961
|
}
|
2962
|
break;
|
2963
|
case 6:
|
2964
|
$r = exec("/usr/bin/drill {$extdns_esc} A {$ip_esc} | grep -v '^;\|^\$' | head -1 | cut -f5 2>&1");
|
2965
|
if (is_ipaddrv4($r)) {
|
2966
|
$dnsbl_line .= "local-data: \"{$list[0]} A {$r}\"\n";
|
2967
|
$tld_list .= "{$tld} | {$list[0]} A {$r}\"\n";
|
2968
|
}
|
2969
|
$dnsbl_line .= "local-data: \"{$list[0]} AAAA {$list[1]}\"\n";
|
2970
|
$tld_list .= "{$tld} | {$list[0]} AAAA {$list[1]}\"\n";
|
2971
|
break;
|
2972
|
default:
|
2973
|
break;
|
2974
|
}
|
2975
|
}
|
2976
|
}
|
2977
|
|
2978
|
// Create 'redirect' zone for whole TLD
|
2979
|
else {
|
2980
|
pfb_logger("{$tld}|", 1);
|
2981
|
|
2982
|
$ipv6_dnsbl = '';
|
2983
|
if ($pfb['dnsbl_v6'] == 'on') {
|
2984
|
$ipv6_dnsbl = " local-data: \"{$tld} 60 IN AAAA ::{$pfb['dnsbl_vip']}\"";
|
2985
|
}
|
2986
|
$dnsbl_line = "local-zone: \"{$tld}\" redirect local-data: \"{$tld} 60 IN A {$pfb['dnsbl_vip']}\"{$ipv6_dnsbl}\n";
|
2987
|
|
2988
|
// Collect List of TLDs and save to DNSBL folder
|
2989
|
$tld_list .= "{$tld}\n";
|
2990
|
}
|
2991
|
|
2992
|
@file_put_contents($dnsbl_file, $dnsbl_line, LOCK_EX);
|
2993
|
|
2994
|
// Add TLD to remove file (To be removed from 'transparent' zone)
|
2995
|
@file_put_contents("{$pfb['dnsbl_tld_remove']}.tsp", ".{$tld} 60\n", FILE_APPEND | LOCK_EX);
|
2996
|
|
2997
|
// Remove any 'TLD Blacklists' from the 'TLD master list'
|
2998
|
if (isset($tlds[$tld])) {
|
2999
|
unset($tlds[$tld]);
|
3000
|
}
|
3001
|
}
|
3002
|
}
|
3003
|
|
3004
|
// Save a list of TLDs in DNSBL folder (DNSBL total line count verification)
|
3005
|
if (!empty($tld_list)) {
|
3006
|
@file_put_contents("{$pfb['dnsbl_tld_txt']}", $tld_list, LOCK_EX);
|
3007
|
|
3008
|
// Add 'TLD' to Alias/Feeds array
|
3009
|
if (!is_array($pfb['tld_update']['DNSBL_TLD'])) {
|
3010
|
$pfb['tld_update']['DNSBL_TLD'] = array();
|
3011
|
}
|
3012
|
|
3013
|
$pfb['tld_update']['DNSBL_TLD']['feeds'] = array('DNSBL_TLD');
|
3014
|
$pfb['tld_update']['DNSBL_TLD']['count'] = $tld_cnt;
|
3015
|
$pfb['alias_dnsbl_all'][] = 'DNSBL_TLD';
|
3016
|
}
|
3017
|
else {
|
3018
|
unlink_if_exists("{$pfb['dnsbl_tld_txt']}");
|
3019
|
}
|
3020
|
pfb_logger(" completed\n", 1);
|
3021
|
}
|
3022
|
else {
|
3023
|
unlink_if_exists("{$pfb['dnsbl_tld_txt']}");
|
3024
|
}
|
3025
|
|
3026
|
// Collect TLD Exclusion list and remove any 'TLD Exclusions' from the 'TLD master list'
|
3027
|
$exclusion = pfbng_text_area_decode($pfb['dnsblconfig']['tldexclusion'], TRUE, FALSE, TRUE);
|
3028
|
$tld_exclusion = array();
|
3029
|
if (!empty($exclusion)) {
|
3030
|
foreach ($exclusion as $key => $exclude) {
|
3031
|
$exclude = trim($exclude, '.'); // Remove any leading/trailing dots
|
3032
|
|
3033
|
// Collect exclusion
|
3034
|
if (strpos($exclude, '.') !== FALSE) {
|
3035
|
$tld_exclusion[$exclude] = '';
|
3036
|
}
|
3037
|
|
3038
|
// Remove Exclusion from TLDS array
|
3039
|
if (isset($tlds[$exclude])) {
|
3040
|
unset($tlds[$exclude]);
|
3041
|
}
|
3042
|
}
|
3043
|
}
|
3044
|
|
3045
|
pfb_logger("TLD analysis", 1);
|
3046
|
|
3047
|
// [ $pfb['dnsbl_file']}.tsp ] Final DNSBL output file (using 'transparent' zone)
|
3048
|
// [ $pfb['dnsbl_tld_remove'] ] File of Sub-Domains to be removed (from 'redirect' zone)
|
3049
|
|
3050
|
// DNSBL Unbound: Analyse DNSBL: 1) 'redirect' zone for whole Domain 2) 'transparent' zone only
|
3051
|
// DNSBL python: Analyse DNSBL into local and zone files
|
3052
|
|
3053
|
if (($fhandle = @fopen("{$pfb['dnsbl_file']}.raw", 'r')) !== FALSE) {
|
3054
|
while (($line = @fgets($fhandle)) !== FALSE) {
|
3055
|
if (empty($line)) {
|
3056
|
continue;
|
3057
|
}
|
3058
|
|
3059
|
// Display progress indicator
|
3060
|
if ($domain_cnt % 100000 == 0) {
|
3061
|
// Memory limitation exceeded for 'redirect' zones
|
3062
|
if ($domain_cnt >= $pfb['domain_max_cnt']) {
|
3063
|
pfb_logger('x', 1);
|
3064
|
} else {
|
3065
|
pfb_logger('.', 1);
|
3066
|
}
|
3067
|
}
|
3068
|
|
3069
|
// DNSBL python blocking mode
|
3070
|
if ($pfb['dnsbl_py_blacklist']) {
|
3071
|
$eparts = explode(',', $line, 3);
|
3072
|
$domain = $eparts[1];
|
3073
|
$dparts = explode('.', $domain);
|
3074
|
$dcnt = count($dparts);
|
3075
|
$tld = end($dparts);
|
3076
|
$d_info = $eparts[2]; // Logging Type/Header/Alias group details
|
3077
|
$dfound = '';
|
3078
|
}
|
3079
|
|
3080
|
// DNSBL Unbound blocking mode
|
3081
|
else {
|
3082
|
$eparts = explode(' ', str_replace('"', '', $line), 3);
|
3083
|
$domain = $eparts[1];
|
3084
|
$s_info = trim($eparts[2]);
|
3085
|
|
3086
|
if ($pfb['dnsbl_v6'] == 'on') {
|
3087
|
// Determine if DNSBL Logging is disabled and switch to '::0'
|
3088
|
if (strpos($s_info, ' A 0.0.0.0') !== FALSE) {
|
3089
|
$s_info6 = str_replace(' A 0.0.0.0', ' AAAA ::0', $s_info);
|
3090
|
} else {
|
3091
|
$s_info6 = str_replace(' A ', ' AAAA ::', $s_info);
|
3092
|
}
|
3093
|
}
|
3094
|
|
3095
|
$dparts = explode('.', $domain);
|
3096
|
$dcnt = count($dparts);
|
3097
|
$tld = end($dparts);
|
3098
|
$dfound = '';
|
3099
|
}
|
3100
|
|
3101
|
// Determine if TLD exists in TLD Blacklist (skip for DNSBL python)
|
3102
|
if (!$pfb['dnsbl_py_blacklist'] && !empty($tld_blacklist)) {
|
3103
|
|
3104
|
// Determine minimum 'tld level' for loop efficiency
|
3105
|
$min_cnt = @min(array($tld_segments, ($dcnt -1)));
|
3106
|
|
3107
|
for ($i=1; $i <= $min_cnt; $i++) {
|
3108
|
$d_query = implode('.', array_slice($dparts, -$i, $i, TRUE));
|
3109
|
if (isset($tld_blacklist[$d_query])) {
|
3110
|
continue 2; // Whole TLD being blocked
|
3111
|
}
|
3112
|
}
|
3113
|
}
|
3114
|
|
3115
|
if ($domain_cnt <= $pfb['domain_max_cnt']) {
|
3116
|
|
3117
|
// Search TLD master list (Levels 1-4)
|
3118
|
// If Domain is a Sub-Domain, create 'transparent' zone. Otherwise create 'redirect' zone
|
3119
|
switch($dcnt) {
|
3120
|
case ($dcnt > 5):
|
3121
|
break;
|
3122
|
case '5':
|
3123
|
$dfound = tld_search($tld, $dparts, 4, 5);
|
3124
|
break;
|
3125
|
case '4':
|
3126
|
$dfound = tld_search($tld, $dparts, 3, 4);
|
3127
|
break;
|
3128
|
case '3':
|
3129
|
$dfound = tld_search($tld, $dparts, 2, 3);
|
3130
|
break;
|
3131
|
case '2':
|
3132
|
$dfound = implode('.', array_slice($dparts, -2, 2, TRUE));
|
3133
|
break;
|
3134
|
}
|
3135
|
}
|
3136
|
|
3137
|
// If Domain is in the TLD Exclusion(s), use 'transparent zone'
|
3138
|
if (!empty($domain) && isset($tld_exclusion[$domain])) {
|
3139
|
$dfound = '';
|
3140
|
}
|
3141
|
|
3142
|
// Create 'redirect' zone for Domain
|
3143
|
if (!empty($dfound)) {
|
3144
|
$pfb_found = TRUE;
|
3145
|
|
3146
|
// DNSBL python blocking mode
|
3147
|
if ($pfb['dnsbl_py_blacklist']) {
|
3148
|
@fwrite($p_zone, ",{$dfound},{$d_info}");
|
3149
|
|
3150
|
// TLD remove files - See below for description
|
3151
|
@fwrite($p_tsp, ".{$dfound},,\n");
|
3152
|
}
|
3153
|
else {
|
3154
|
$ipv6_dnsbl = '';
|
3155
|
if ($pfb['dnsbl_v6'] == 'on') {
|
3156
|
$ipv6_dnsbl = " local-data: \"{$dfound} {$s_info6}\"";
|
3157
|
}
|
3158
|
$domain_line = "local-zone: \"{$dfound}\" redirect local-data: \"{$dfound} {$s_info}\"{$ipv6_dnsbl}\n";
|
3159
|
@file_put_contents("{$pfb['dnsbl_file']}.tsp", $domain_line, FILE_APPEND | LOCK_EX);
|
3160
|
|
3161
|
// Add Domain to remove file for [ 1- 'redirect zone' Domains 2- Unbound memory domains ]
|
3162
|
// This removes any of these domains and sub-domains
|
3163
|
@file_put_contents("{$pfb['dnsbl_tld_remove']}", ".{$dfound} 60\n\"{$dfound} 60\n", FILE_APPEND | LOCK_EX);
|
3164
|
|
3165
|
// Add Domain to remove file for 'transparent zone' domains
|
3166
|
// This removes any of these sub-domains
|
3167
|
@file_put_contents("{$pfb['dnsbl_tld_remove']}.tsp", ".{$dfound} 60\n", FILE_APPEND | LOCK_EX);
|
3168
|
}
|
3169
|
}
|
3170
|
|
3171
|
// Create 'transparent zone' for Sub-Domain
|
3172
|
else {
|
3173
|
// DNSBL python blocking mode
|
3174
|
if ($pfb['dnsbl_py_blacklist']) {
|
3175
|
@fwrite($p_data, ",{$domain},{$d_info}");
|
3176
|
}
|
3177
|
else {
|
3178
|
if (!empty($tld)) {
|
3179
|
$dnsbl_file = "{$pfb['dnsbl_tmpdir']}/DNSBL_{$tld}.txt";
|
3180
|
|
3181
|
// Create a temp file for each TLD. w/ 'transparent' header followed by each 'local-data' line
|
3182
|
if (!file_exists($dnsbl_file)) {
|
3183
|
$dnsbl_header = "local-zone: \"{$tld}\" \"transparent\"\n";
|
3184
|
@file_put_contents($dnsbl_file, $dnsbl_header, LOCK_EX);
|
3185
|
}
|
3186
|
|
3187
|
$ipv6_dnsbl = '';
|
3188
|
if ($pfb['dnsbl_v6'] == 'on') {
|
3189
|
$ipv6_dnsbl = " local-data: \"{$domain} {$s_info6}\"";
|
3190
|
}
|
3191
|
$domain_line = "local-data: \"{$domain} {$s_info}\"{$ipv6_dnsbl}\n";
|
3192
|
@file_put_contents($dnsbl_file, $domain_line, FILE_APPEND | LOCK_EX);
|
3193
|
}
|
3194
|
else {
|
3195
|
$oline = htmlentities($line);
|
3196
|
pfb_logger("\nDebug: Missing TLD: {$oline}", 1);
|
3197
|
}
|
3198
|
}
|
3199
|
}
|
3200
|
|
3201
|
// Increment Domain counter
|
3202
|
$domain_cnt++;
|
3203
|
}
|
3204
|
}
|
3205
|
|
3206
|
foreach (array($fhandle, $p_data, $p_zone, $p_tsp) as $handlex) {
|
3207
|
if ($handlex) {
|
3208
|
@fclose($handlex);
|
3209
|
}
|
3210
|
}
|
3211
|
unset($tlds, $tld_blacklist, $tld_exclusion);
|
3212
|
|
3213
|
// TLD 'redirect zones' found. Finalize TLD function
|
3214
|
if ($pfb_found) {
|
3215
|
$log = " completed [ NOW ]\n";
|
3216
|
// Print TLD exceedance error message
|
3217
|
if ($domain_cnt >= $pfb['domain_max_cnt']) {
|
3218
|
$log .= "\n ** TLD Domain count exceeded. [ {$pfb['domain_max_cnt']} ] All subsequent Domains listed as-is **\n\n";
|
3219
|
}
|
3220
|
$log .= "TLD finalize";
|
3221
|
pfb_logger("{$log}", 1);
|
3222
|
|
3223
|
// Execute Domain De-duplication
|
3224
|
if ($pfb['dnsbl_py_blacklist']) {
|
3225
|
exec("{$pfb['script']} domaintldpy >> {$pfb['log']} 2>&1");
|
3226
|
}
|
3227
|
else {
|
3228
|
// Create a csv list of 'recently updated' DNSBL Feeds, as ordered by User
|
3229
|
$dnsbl_feeds = '';
|
3230
|
if (!empty($pfb['tld_update'])) {
|
3231
|
foreach ($pfb['tld_update'] as $alias => $data) {
|
3232
|
foreach ($data['feeds'] as $feed) {
|
3233
|
$dnsbl_feeds .= "{$feed},";
|
3234
|
}
|
3235
|
}
|
3236
|
}
|
3237
|
|
3238
|
if (!empty(pfb_filter($dnsbl_feeds, PFB_FILTER_CSV, 'tld_analysis'))) {
|
3239
|
exec("{$pfb['script']} domaintld x x x {$dnsbl_feeds} >> {$pfb['log']} 2>&1");
|
3240
|
} else {
|
3241
|
pfb_logger("\nFailed to create list of DNSBL Feeds", 1);
|
3242
|
}
|
3243
|
}
|
3244
|
pfb_logger("\nTLD finalize... completed [ NOW ]\n", 1);
|
3245
|
|
3246
|
// Update DNSBL Alias and Widget Stats
|
3247
|
if (!empty($pfb['tld_update'])) {
|
3248
|
foreach ($pfb['tld_update'] as $alias => $data) {
|
3249
|
|
3250
|
// Create Alias summary file for each DNSBL Alias
|
3251
|
$lists_dnsbl_current = array();
|
3252
|
foreach ($data['feeds'] as $feed) {
|
3253
|
$lists_dnsbl_current[] = "{$feed}";
|
3254
|
}
|
3255
|
dnsbl_alias_update('update', $alias, $pfb['dnsdir'], $lists_dnsbl_current, $data['count']);
|
3256
|
}
|
3257
|
}
|
3258
|
}
|
3259
|
else {
|
3260
|
pfb_logger(" no changes\n", 1);
|
3261
|
}
|
3262
|
|
3263
|
// Save DNSBL Alias statistics
|
3264
|
dnsbl_save_stats();
|
3265
|
|
3266
|
if ($pfb['dnsbl_py_blacklist'] && file_exists("{$pfb['dnsbl_file']}.raw")) {
|
3267
|
unlink_if_exists("{$pfb['dnsbl_file']}.raw");
|
3268
|
}
|
3269
|
}
|
3270
|
|
3271
|
// Function to Start Unbound
|
3272
|
function pfb_stop_start_unbound($type) {
|
3273
|
global $g, $pfb;
|
3274
|
|
3275
|
$final = array();
|
3276
|
if (file_exists("{$g['varrun_path']}/unbound.pid")) {
|
3277
|
pfb_logger("\nStopping Unbound Resolver", 1);
|
3278
|
sigkillbypid("{$g['varrun_path']}/unbound.pid", 'TERM');
|
3279
|
}
|
3280
|
|
3281
|
// If unbound is still running, wait up to 30 seconds for it to terminate.
|
3282
|
for ($i=1; $i <= 30; $i++) {
|
3283
|
if (is_process_running('unbound')) {
|
3284
|
pfb_logger('.', 1);
|
3285
|
sleep(1);
|
3286
|
} else {
|
3287
|
pfb_logger("\nUnbound stopped in {$i} sec.", 1);
|
3288
|
break;
|
3289
|
}
|
3290
|
}
|
3291
|
|
3292
|
// Add/Remove additional python mounts
|
3293
|
if (file_exists('/var/unbound/pfb_unbound_include.inc')) {
|
3294
|
$g['pfblockerng_include_verbose'] = TRUE;
|
3295
|
pfb_logger("\nAdditional mounts{$type}:", 1);
|
3296
|
require_once('/var/unbound/pfb_unbound_include.inc');
|
3297
|
unset($g['pfblockerng_include_verbose']);
|
3298
|
}
|
3299
|
|
3300
|
// Remove Unbound python mounts
|
3301
|
if ($pfb['dnsbl_python_unmount']) {
|
3302
|
pfb_unbound_python_unmount();
|
3303
|
}
|
3304
|
|
3305
|
pfb_logger("\nStarting Unbound Resolver", 1);
|
3306
|
exec("/usr/local/sbin/unbound -c /var/unbound/unbound.conf 2>&1", $final['result'], $final['retval']);
|
3307
|
return $final;
|
3308
|
}
|
3309
|
|
3310
|
|
3311
|
// Reload Resolver
|
3312
|
function pfb_reload_unbound($mode, $cache=FALSE, $pfbpython=FALSE) {
|
3313
|
global $g, $pfb;
|
3314
|
|
3315
|
$final = array();
|
3316
|
$type = '';
|
3317
|
if ($mode == 'enabled' && $pfb['dnsbl_py_blacklist']) {
|
3318
|
$type = ' (DNSBL python)';
|
3319
|
}
|
3320
|
|
3321
|
if (!$pfb['dnsbl_py_blacklist'] && file_exists("{$pfb['dnsbl_file']}.raw")) {
|
3322
|
@rename("{$pfb['dnsbl_file']}.raw", "{$pfb['dnsbl_file']}.conf");
|
3323
|
}
|
3324
|
|
3325
|
$cache_dumpfile = tempnam('/var/tmp/', 'unbound_cache_');
|
3326
|
if ($mode == 'enabled' && is_process_running('unbound') && !$pfb['dnsbl_python_unmount'] && !$pfbpython) {
|
3327
|
|
3328
|
$log = "\nReloading Unbound Resolver{$type}";
|
3329
|
pfb_logger($log, 1);
|
3330
|
|
3331
|
if ($cache && $pfb['dnsbl_res_cache'] == 'on') {
|
3332
|
exec("{$pfb['chroot_cmd']} dump_cache > " . escapeshellarg($cache_dumpfile) . " 2>&1");
|
3333
|
pfb_logger('.', 1);
|
3334
|
}
|
3335
|
}
|
3336
|
|
3337
|
$final = pfb_stop_start_unbound($type);
|
3338
|
pfb_logger('.', 1);
|
3339
|
|
3340
|
if ($final['retval'] != 0) {
|
3341
|
|
3342
|
@copy("{$pfb['dnsbldir']}/unbound.conf", "{$pfb['dnsbldir']}/unbound.conf.error");
|
3343
|
if ($mode == 'enabled') {
|
3344
|
if (!$pfb['dnsbl_py_blacklist']) {
|
3345
|
$log = "\nDNSBL {$mode} FAIL - restoring Unbound conf *** Fix error(s) and a Force Reload required! ***\n";
|
3346
|
|
3347
|
// Try to restore previous DNSBL database
|
3348
|
if (file_exists("{$pfb['dnsbl_file']}.bk")) {
|
3349
|
@rename("{$pfb['dnsbl_file']}.bk", "{$pfb['dnsbl_file']}.conf");
|
3350
|
}
|
3351
|
|
3352
|
// Wipe DNSBL database
|
3353
|
else {
|
3354
|
$log .= ' Restore previous database Failed!';
|
3355
|
unlink_if_exists("{$pfb['dnsbl_file']}.conf");
|
3356
|
touch("{$pfb['dnsbl_file']}.conf");
|
3357
|
|
3358
|
// Restore previous unbound.conf
|
3359
|
if (file_exists("{$pfb['dnsbldir']}/unbound.bk")) {
|
3360
|
@rename("{$pfb['dnsbldir']}/unbound.bk", "{$pfb['dnsbldir']}/unbound.conf");
|
3361
|
}
|
3362
|
}
|
3363
|
}
|
3364
|
else {
|
3365
|
$log = "\nDNSBL {$mode} FAIL *** Fix error(s) and a Force Reload required! ***\n";
|
3366
|
if (file_exists("{$pfb['dnsbldir']}/unbound.bk")) {
|
3367
|
@rename("{$pfb['dnsbldir']}/unbound.bk", "{$pfb['dnsbldir']}/unbound.conf");
|
3368
|
}
|
3369
|
}
|
3370
|
pfb_logger("{$log}", 2);
|
3371
|
}
|
3372
|
else {
|
3373
|
$log = "\nDNSBL {$mode} - Unbound conf update FAIL *** Fix error(s) and a Force Reload required! ***\n";
|
3374
|
pfb_logger("{$log}", 2);
|
3375
|
}
|
3376
|
|
3377
|
$log = htmlspecialchars(implode("\n", $final['result']));
|
3378
|
pfb_logger("\n\n====================\n\n{$log}\n\n====================\n\n", 2);
|
3379
|
$final = pfb_stop_start_unbound($type);
|
3380
|
}
|
3381
|
|
3382
|
// Confirm that Resolver is running
|
3383
|
if (is_process_running('unbound')) {
|
3384
|
pfb_logger('.', 1);
|
3385
|
|
3386
|
// $final['result'] will be appended with previous result above
|
3387
|
exec("{$pfb['chroot_cmd']} status 2>&1", $final['result'], $final['retval']);
|
3388
|
pfb_logger('.', 1);
|
3389
|
if (preg_grep("/is running.../", $final['result'])) {
|
3390
|
pfb_logger(" completed [ NOW ]", 1);
|
3391
|
|
3392
|
// Restore Resolver cache
|
3393
|
if ($cache && $pfb['dnsbl_res_cache'] == 'on' && file_exists($cache_dumpfile) && filesize($cache_dumpfile) > 0) {
|
3394
|
exec("{$pfb['chroot_cmd']} load_cache < " . escapeshellarg($cache_dumpfile) . " 2>&1");
|
3395
|
$log = "\nResolver cache restored [ NOW ]";
|
3396
|
pfb_logger($log, 1);
|
3397
|
}
|
3398
|
}
|
3399
|
else {
|
3400
|
$log = htmlspecialchars(implode("\n", $final['result']));
|
3401
|
pfb_logger(" Not completed. [ NOW ]\n{$log}\n", 1);
|
3402
|
}
|
3403
|
}
|
3404
|
else {
|
3405
|
$log = htmlspecialchars(implode("\n", $final['result']));
|
3406
|
pfb_logger(" Not completed. [ NOW ]\n{$log}\n", 1);
|
3407
|
}
|
3408
|
unlink_if_exists($cache_dumpfile);
|
3409
|
}
|
3410
|
|
3411
|
|
3412
|
// Function to clear Unbound/DNSBL work files
|
3413
|
function pfb_unbound_clear_work_files() {
|
3414
|
global $pfb;
|
3415
|
|
3416
|
foreach (array( $pfb['dnsbl_cache'],
|
3417
|
"{$pfb['dnsbldir']}/unbound.bk",
|
3418
|
"{$pfb['dnsbldir']}/unbound.tmp",
|
3419
|
"{$pfb['dnsbl_file']}.bk",
|
3420
|
"{$pfb['dnsbl_file']}.tsp",
|
3421
|
"{$pfb['dnsbl_file']}.sync",
|
3422
|
"/tmp/dnsbl_remove*",
|
3423
|
"/tmp/dnsbl_add*",
|
3424
|
"/tmp/dnsbl_tld*",
|
3425
|
"{$pfb['unbound_py_data']}.raw",
|
3426
|
"{$pfb['unbound_py_zone']}.raw",
|
3427
|
"/var/tmp/unbound_cache_*") as $remove) {
|
3428
|
unlink_if_exists($remove);
|
3429
|
}
|
3430
|
}
|
3431
|
|
3432
|
|
3433
|
// Load new DNSBL updates to Unbound Resolver
|
3434
|
function pfb_update_unbound($mode, $pfbupdate, $pfbpython) {
|
3435
|
global $g, $pfb;
|
3436
|
|
3437
|
if ($mode == 'enabled') {
|
3438
|
$ext = '.bk';
|
3439
|
} else {
|
3440
|
$ext = '.*'; // Remove all DNSBL Unbound files
|
3441
|
}
|
3442
|
|
3443
|
// Execute TLD analysis, if configured
|
3444
|
if ($pfb['enable'] == 'on' && $pfb['dnsbl'] == 'on' && !$pfb['save']) {
|
3445
|
if ($pfb['dnsbl_tld']) {
|
3446
|
tld_analysis();
|
3447
|
} else {
|
3448
|
unlink_if_exists("{$pfb['dnsbl_tld_txt']}");
|
3449
|
}
|
3450
|
}
|
3451
|
|
3452
|
// Create file marker to disable DNSBL Queries daemon to avoid unbound-control collisions
|
3453
|
touch("{$pfb['dnsbl_file']}.sync");
|
3454
|
|
3455
|
// Marker file(s) to instruct Unbound to be reloaded
|
3456
|
if ($pfb['reuse_dnsbl'] == 'on' ||
|
3457
|
file_exists("{$pfb['dnsbl_file']}.reload") ||
|
3458
|
file_exists("{$pfb['dnsbl_unlock']}")) {
|
3459
|
|
3460
|
$pfbupdate = TRUE;
|
3461
|
unlink_if_exists("{$pfb['dnsbl_file']}.reload");
|
3462
|
unlink_if_exists("{$pfb['dnsbl_unlock']}");
|
3463
|
unlink_if_exists("{$pfb['dnsbl_unlock']}.data");
|
3464
|
}
|
3465
|
|
3466
|
// Backup existing unbound.conf and rename new unbound.conf file
|
3467
|
if (file_exists("{$pfb['dnsbldir']}/unbound.tmp")) {
|
3468
|
@copy("{$pfb['dnsbldir']}/unbound.conf", "{$pfb['dnsbldir']}/unbound.bk");
|
3469
|
@rename("{$pfb['dnsbldir']}/unbound.tmp", "{$pfb['dnsbldir']}/unbound.conf");
|
3470
|
}
|
3471
|
|
3472
|
// When pfBlockerNG is disabled and 'keep blocklists' is disabled.
|
3473
|
if ($pfb['enable'] == '' && $pfb['keep'] == '' && !$pfb['install']) {
|
3474
|
unlink_if_exists("{$pfb['dnsbl_file']}{$ext}");
|
3475
|
}
|
3476
|
|
3477
|
// Disable DNSBL
|
3478
|
if (($pfb['enable'] != 'on' || $pfb['dnsbl'] != 'on') && !$pfb['install']) {
|
3479
|
|
3480
|
pfb_reload_unbound('disabled', FALSE, $pfbpython);
|
3481
|
if (is_service_running('pfb_dnsbl')) {
|
3482
|
pfb_logger("\nStop Service DNSBL", 1);
|
3483
|
stop_service('pfb_dnsbl');
|
3484
|
}
|
3485
|
pfb_unbound_clear_work_files();
|
3486
|
|
3487
|
// Unmount Unbound python 'lib/bin' folders after Unbound has been reloaded with the python integration enabled
|
3488
|
if ($pfb['dnsbl_python_unmount']) {
|
3489
|
pfb_unbound_python_unmount();
|
3490
|
unset($pfb['dnsbl_python_unmount']);
|
3491
|
}
|
3492
|
pfb_logger("\nDNSBL is disabled\n", 1);
|
3493
|
return;
|
3494
|
}
|
3495
|
|
3496
|
// Load new DNSBL updates
|
3497
|
if (is_service_running('unbound')) {
|
3498
|
|
3499
|
// 'Live sync' new DNSBL updates utilizing unbound-control
|
3500
|
if (!$pfb['dnsbl_py_blacklist'] && $pfb['dnsbl_sync'] && !$pfbpython &&
|
3501
|
file_exists("{$pfb['dnsbl_file']}.conf") && filesize("{$pfb['dnsbl_file']}.conf") > 0) {
|
3502
|
|
3503
|
$sync_fail = FALSE;
|
3504
|
|
3505
|
pfb_logger("\nResolver Live Sync analysis", 1);
|
3506
|
exec("{$pfb['script']} dnsbl_livesync >> {$pfb['log']} 2>&1");
|
3507
|
pfb_logger(" completed [ NOW ]", 1);
|
3508
|
|
3509
|
$ucsync = array(array( 'dnsbl_remove_zone', 'local_zones_remove', 'Remove local-zone(s)' ),
|
3510
|
array( 'dnsbl_remove_data', 'local_datas_remove', 'Remove local-data(s)' ),
|
3511
|
array( 'dnsbl_add_zone', 'local_zones', 'Add local-zone(s)' ),
|
3512
|
array( 'dnsbl_add_data', 'local_datas', 'Add local-data(s)' ));
|
3513
|
|
3514
|
// Disable zone updates when TLD is disabled
|
3515
|
if (!$pfb['dnsbl_tld']) {
|
3516
|
unset($ucsync[0], $ucsync[2]);
|
3517
|
}
|
3518
|
|
3519
|
pfb_logger("\nResolver Live Sync finalizing:", 1);
|
3520
|
foreach ($ucsync as $skey => $sync) {
|
3521
|
$file = $pfb[$sync[0]];
|
3522
|
|
3523
|
if (filesize("{$file}") > 0) {
|
3524
|
$result = array();
|
3525
|
exec("{$pfb['chroot_cmd']} {$sync[1]} < {$file}", $result, $retval);
|
3526
|
$result = implode("\n", $result);
|
3527
|
$log = "\n\t{$sync[2]}:\t\t{$result}";
|
3528
|
}
|
3529
|
else {
|
3530
|
$log = "\n\t{$sync[2]}:\t\tno changes";
|
3531
|
}
|
3532
|
pfb_logger("{$log}", 1);
|
3533
|
|
3534
|
if (!$sync_fail && !empty($retval)) {
|
3535
|
$sync_fail = TRUE;
|
3536
|
}
|
3537
|
}
|
3538
|
|
3539
|
if ($sync_fail) {
|
3540
|
pfb_logger("\nResolver Live Sync ... FAILED!", 1);
|
3541
|
pfb_reload_unbound('reload', FALSE, $pfbpython);
|
3542
|
}
|
3543
|
}
|
3544
|
|
3545
|
// Do a full Reload of Unbound
|
3546
|
else {
|
3547
|
pfb_reload_unbound($mode, TRUE, $pfbpython);
|
3548
|
}
|
3549
|
}
|
3550
|
|
3551
|
// Start Unbound Service with new DNSBL Updates
|
3552
|
else {
|
3553
|
pfb_reload_unbound($mode, FALSE, $pfbpython);
|
3554
|
}
|
3555
|
|
3556
|
if ($pfbpython) {
|
3557
|
$log = "\nRestarting DNSBL Service (DNSBL python)";
|
3558
|
pfb_logger("{$log}", 1);
|
3559
|
restart_service('pfb_dnsbl');
|
3560
|
}
|
3561
|
|
3562
|
$dnsbl_cnt = exec("/bin/cat {$pfb['dnsdir']}/*.txt | {$pfb['grep']} -c ^ 2>&1");
|
3563
|
|
3564
|
// Unbound blocking mode enabled
|
3565
|
if (!$pfb['dnsbl_py_blacklist']) {
|
3566
|
$final_cnt = exec("{$pfb['grep']} -v '\"transparent\"\|\"static\"' {$pfb['dnsbl_file']}.conf | {$pfb['grep']} -c ^ 2>&1");
|
3567
|
if ($final_cnt == $dnsbl_cnt) {
|
3568
|
$log = "\nDNSBL update [ {$final_cnt} | PASSED ]... completed [ NOW ]";
|
3569
|
} else {
|
3570
|
$log = "\n*** DNSBL update [ {$final_cnt} ] [ {$dnsbl_cnt} ] ... OUT OF SYNC ! *** [ NOW ]";
|
3571
|
}
|
3572
|
pfb_logger("{$log}", 1);
|
3573
|
}
|
3574
|
|
3575
|
// Python blocking mode enabled
|
3576
|
else {
|
3577
|
$tld_cnt = @file_get_contents($pfb['unbound_py_count']);
|
3578
|
$dnsbl_cnt = $dnsbl_cnt - $tld_cnt;
|
3579
|
$final_cnt = exec("/usr/bin/find {$pfb['unbound_py_data']} {$pfb['unbound_py_zone']} -type f 2>/dev/null | xargs cat | {$pfb['grep']} -c ^ 2>&1");
|
3580
|
if ($final_cnt == $dnsbl_cnt) {
|
3581
|
$log = "\nDNSBL update [ {$final_cnt} | PASSED ]... completed [ NOW ]";
|
3582
|
} else {
|
3583
|
$log = "\n*** DNSBL update [ {$final_cnt} ] [ {$dnsbl_cnt} ] ... OUT OF SYNC ! *** [ NOW ]";
|
3584
|
}
|
3585
|
pfb_logger("{$log}", 1);
|
3586
|
}
|
3587
|
|
3588
|
// DEBUG Live Sync
|
3589
|
if (!$pfb['dnsbl_py_blacklist'] && $pfb['enable'] == 'on' && $pfb['dnsbl'] == 'on' && $pfb['dnsbl_sync'] && !$pfbupdate && !$pfbpython && !$sync_fail) {
|
3590
|
pfb_logger("\n\nDNSBL DEBUG", 1);
|
3591
|
$datacnt = $zonecnt = 0;
|
3592
|
|
3593
|
exec("{$pfb['chroot_cmd']} list_local_data | {$pfb['grep']} '{$pfb['dnsbl_vip']}\|0\.0\.0\.0\$' | {$pfb['grep']} -v 'AAAA' | {$pfb['grep']} -c ^ 2>&1", $datacnt, $retval);
|
3594
|
$datacnt = implode($datacnt);
|
3595
|
pfb_logger('.', 1);
|
3596
|
|
3597
|
if ($pfb['dnsbl_tld']) {
|
3598
|
exec("{$pfb['chroot_cmd']} list_local_zones | {$pfb['grep']} \"redirect\" | {$pfb['grep']} -c ^ 2>&1", $zonecnt, $retval);
|
3599
|
pfb_logger('.', 1);
|
3600
|
$zonecnt = implode($zonecnt);
|
3601
|
|
3602
|
$tldcnt = array('0');
|
3603
|
if (file_exists('/var/db/pfblockerng/dnsbl/DNSBL_TLD.txt')) {
|
3604
|
exec("{$pfb['grep']} -c ' A \| AAAA ' /var/db/pfblockerng/dnsbl/DNSBL_TLD.txt 2>&1", $tldcnt, $retval);
|
3605
|
}
|
3606
|
$tldcnt = implode($tldcnt);
|
3607
|
$datacnt = $datacnt + $tldcnt;
|
3608
|
}
|
3609
|
pfb_logger("[ Data(s): {$datacnt}\tZone(s): {$zonecnt} | NOW ]", 1);
|
3610
|
}
|
3611
|
|
3612
|
// Clear work files
|
3613
|
pfb_unbound_clear_work_files();
|
3614
|
pfb_logger("\n------------------------------------------------------------------------", 1);
|
3615
|
}
|
3616
|
|
3617
|
|
3618
|
// Process TOP1M database
|
3619
|
function pfblockerng_top1m() {
|
3620
|
global $pfb;
|
3621
|
|
3622
|
if (empty($pfb['dnsbl_alexa_inc'])) {
|
3623
|
pfb_logger("\n TOP1M: No TLD Inclusions found.\n", 1);
|
3624
|
return;
|
3625
|
}
|
3626
|
|
3627
|
// Array of TLDs to include in Whitelist
|
3628
|
$pfb_include = explode(',', $pfb['dnsbl_alexa_inc']);
|
3629
|
if (!empty($pfb_include)) {
|
3630
|
$pfb_include = array_flip($pfb_include);
|
3631
|
}
|
3632
|
|
3633
|
$linecnt = $x = 0;
|
3634
|
pfb_logger(" Building TOP1M Whitelist [", 1);
|
3635
|
|
3636
|
if (($handle = @fopen("{$pfb['dbdir']}/top-1m.csv", 'r')) !== FALSE) {
|
3637
|
$pfb_output = @fopen("{$pfb['dbdir']}/pfbalexawhitelist.txt", 'w');
|
3638
|
while (($line = @fgets($handle)) !== FALSE) {
|
3639
|
|
3640
|
if (strpos($line, '.') === FALSE || strpos($line, ',') === FALSE || empty($line)) {
|
3641
|
continue;
|
3642
|
}
|
3643
|
|
3644
|
// Display progress indicator
|
3645
|
if ($linecnt % 100000 == 0) {
|
3646
|
pfb_logger('.', 1);
|
3647
|
}
|
3648
|
|
3649
|
// Collect Domain TLD
|
3650
|
$csvline = str_getcsv($line);
|
3651
|
$tld = substr($csvline[1], strrpos($csvline[1], '.') + 1);
|
3652
|
|
3653
|
if (isset($pfb_include[$tld])) {
|
3654
|
// Whitelist both 'www.example.com' and 'example.com'
|
3655
|
if (substr($csvline[1], 0, 4) == 'www.') {
|
3656
|
$csvline[1] = substr($csvline[1], 4);
|
3657
|
}
|
3658
|
$x++;
|
3659
|
|
3660
|
// Create three whitelist options per TOP1M whitelisted Domain
|
3661
|
if ($pfb['dnsbl_py_blacklist']) {
|
3662
|
@fwrite($pfb_output, ".{$csvline[1]},,\n,{$csvline[1]},,\n,www.{$csvline[1]},,\n");
|
3663
|
} else {
|
3664
|
@fwrite($pfb_output, ".{$csvline[1]} 60\n\"{$csvline[1]} 60\n\"www.{$csvline[1]} 60\n");
|
3665
|
}
|
3666
|
}
|
3667
|
|
3668
|
if ($x >= $pfb['dnsbl_alexa_cnt']) {
|
3669
|
break;
|
3670
|
}
|
3671
|
$linecnt++;
|
3672
|
}
|
3673
|
pfb_logger("] [ Parsed {$linecnt} lines | Found {$x} of {$pfb['dnsbl_alexa_cnt']} ]...", 1);
|
3674
|
}
|
3675
|
else {
|
3676
|
$log = "\nTOP1M conversion Failed. File: top-1m.csv, not found...";
|
3677
|
pfb_logger("{$log}", 2);
|
3678
|
}
|
3679
|
|
3680
|
if ($handle) {
|
3681
|
@fclose($handle);
|
3682
|
}
|
3683
|
if ($pfb_output) {
|
3684
|
@fclose($pfb_output);
|
3685
|
}
|
3686
|
|
3687
|
// Remove Top1M update file marker
|
3688
|
unlink_if_exists("{$pfb['dbdir']}/top-1m.update");
|
3689
|
}
|
3690
|
|
3691
|
|
3692
|
// Function to remove any leading zeros in octets and to exclude private/reserved addresses.
|
3693
|
function sanitize_ipaddr($ipaddr, $custom, $pfbcidr) {
|
3694
|
global $pfb;
|
3695
|
|
3696
|
list ($subnet, $mask) = explode('/', $ipaddr);
|
3697
|
$iparr = explode('.', $subnet);
|
3698
|
|
3699
|
foreach ($iparr as $key => $octet) {
|
3700
|
// Remove any leading zeros in octets
|
3701
|
if ($octet == 0) {
|
3702
|
$ip[$key] = 0;
|
3703
|
} else {
|
3704
|
$ip[$key] = ltrim($octet, '0');
|
3705
|
}
|
3706
|
|
3707
|
if ($key == 3) {
|
3708
|
// If mask is not defined and 4th octet is '0', set mask to '24'
|
3709
|
if ($octet == 0 && empty($mask)) {
|
3710
|
$mask = 24;
|
3711
|
}
|
3712
|
|
3713
|
// If mask is '24', force 4th octet to '0'
|
3714
|
if ($mask == 24 && $octet != 0) {
|
3715
|
$ip[$key] = 0;
|
3716
|
}
|
3717
|
}
|
3718
|
}
|
3719
|
|
3720
|
$mask = str_replace('32', '', $mask); // Strip '/32' mask
|
3721
|
$ip_final = implode('.', $ip);
|
3722
|
|
3723
|
// Exclude private/reserved IPs (bypass exclusion for custom lists)
|
3724
|
if ($pfb['supp'] == 'on' && !$custom) {
|
3725
|
|
3726
|
// Remove 'loopback' and '0.0.0.0' IPs
|
3727
|
if ($ip[0] == 127 || $ip[0] == 0 || empty($ip[0])) {
|
3728
|
return;
|
3729
|
}
|
3730
|
|
3731
|
// Advanced IPv4 Tunable (Set CIDR Block size limit)
|
3732
|
if ($pfbcidr != 'Disabled' && !empty($mask) && $mask < $pfbcidr) {
|
3733
|
pfb_logger("\n Suppression CIDR Limit: {$ip_final}/{$mask}", 1);
|
3734
|
$mask = '32';
|
3735
|
}
|
3736
|
|
3737
|
if ($mask > 32) {
|
3738
|
$mask = '';
|
3739
|
}
|
3740
|
|
3741
|
if (!filter_var($ip_final, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== FALSE) {
|
3742
|
return;
|
3743
|
}
|
3744
|
}
|
3745
|
|
3746
|
if (!empty($mask)) {
|
3747
|
return "{$ip_final}/{$mask}";
|
3748
|
}
|
3749
|
return "{$ip_final}";
|
3750
|
}
|
3751
|
|
3752
|
|
3753
|
// Validate IPv4 IP addresses
|
3754
|
function validate_ipv4($ipaddr) {
|
3755
|
if (strpos($ipaddr, '/') !== FALSE) {
|
3756
|
return is_subnetv4($ipaddr);
|
3757
|
}
|
3758
|
return is_ipaddrv4($ipaddr);
|
3759
|
}
|
3760
|
|
3761
|
|
3762
|
// Validate IPv6 IP addresses
|
3763
|
function validate_ipv6($ipaddr) {
|
3764
|
if (strpos($ipaddr, '/') !== FALSE) {
|
3765
|
return is_subnetv6($ipaddr);
|
3766
|
}
|
3767
|
return is_ipaddrv6($ipaddr);
|
3768
|
}
|
3769
|
|
3770
|
|
3771
|
// Validate IP addresses
|
3772
|
function validate_ip($ipaddr) {
|
3773
|
if (strpos($ipaddr, '/') !== FALSE) {
|
3774
|
return is_subnet($ipaddr);
|
3775
|
}
|
3776
|
return is_ipaddr($ipaddr);
|
3777
|
}
|
3778
|
|
3779
|
|
3780
|
// Function to check for loopback addresses (IPv4 range: 127.0.0.0/8, excluding IPv6)
|
3781
|
function FILTER_FLAG_NO_LOOPBACK_RANGE($value) {
|
3782
|
// http://www.php.net/manual/en/filter.filters.flags.php
|
3783
|
return filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? $value : (((ip2long($value) & 0xff000000) == 0x7f000000) ? FALSE : $value);
|
3784
|
}
|
3785
|
|
3786
|
|
3787
|
// Explode IP for evaluations
|
3788
|
function ip_explode($ip) {
|
3789
|
|
3790
|
$ix1 = '';
|
3791
|
$ix = explode('.', $ip);
|
3792
|
foreach ($ix as $key => $octet) {
|
3793
|
if ($key != 3) {
|
3794
|
$ix1 .= "{$octet}.";
|
3795
|
}
|
3796
|
}
|
3797
|
array_unshift($ix, $ip);
|
3798
|
$ix[] = "{$ix1}0/24";
|
3799
|
$ix[] = "{$ix1}";
|
3800
|
|
3801
|
return $ix;
|
3802
|
}
|
3803
|
|
3804
|
|
3805
|
// Determine the header which Alerted an IP address and return the header name
|
3806
|
function find_reported_header($ip, $pfbfolder, $geoip=FALSE) {
|
3807
|
global $pfb;
|
3808
|
|
3809
|
// Find exact IP match
|
3810
|
$q_ip = escapeshellarg(str_replace('.', '\.', "^{$ip}"));
|
3811
|
$query = exec("{$pfb['grep']} -s {$q_ip} {$pfbfolder} 2>&1");
|
3812
|
if (!empty($query)) {
|
3813
|
$rx = pfb_parse_query($query);
|
3814
|
return $rx;
|
3815
|
}
|
3816
|
else {
|
3817
|
$v4_type = FALSE;
|
3818
|
if (substr_count($ip, ':') > 1) {
|
3819
|
$query = strstr($ip, ':', TRUE); // IPv6 Prefix
|
3820
|
$query_esc = escapeshellarg("^{$query}:");
|
3821
|
} else {
|
3822
|
$query = strstr($ip, '.', TRUE); // IPv4 Octet #1
|
3823
|
$query_esc = escapeshellarg("^{$query}\.");
|
3824
|
$v4_type = TRUE;
|
3825
|
}
|
3826
|
|
3827
|
$result = array();
|
3828
|
if (!$geoip) {
|
3829
|
exec("{$pfb['grep']} -s {$query_esc} {$pfbfolder} 2>&1", $result);
|
3830
|
} else {
|
3831
|
$geoip_list = "Africa\|Antarctica\|Asia\|Europe\|North_America\|South_America\|Oceania\|Proxy_and_Satellite\|Top_Spammers";
|
3832
|
exec("{$pfb['grep']} -s {$query_esc} {$pfb['ccdir']}/*.txt | {$pfb['grep']} -v '{$geoip_list}' 2>&1", $result);
|
3833
|
}
|
3834
|
|
3835
|
$cidrs = array();
|
3836
|
if (!empty($result)) {
|
3837
|
foreach ($result as $line) {
|
3838
|
$rx = pfb_parse_query($line);
|
3839
|
|
3840
|
// Collect all CIDRs for analysis if Alert is from a CIDR
|
3841
|
if (strpos($rx[1], '/') !== FALSE) {
|
3842
|
$cidrs[] = $rx;
|
3843
|
}
|
3844
|
}
|
3845
|
}
|
3846
|
|
3847
|
if (isset($result)) {
|
3848
|
unset($result);
|
3849
|
}
|
3850
|
|
3851
|
// Determine which CIDR alerted the IP address
|
3852
|
if (!empty($cidrs)) {
|
3853
|
foreach ($cidrs as $line) {
|
3854
|
$validate = FALSE;
|
3855
|
if ($v4_type) {
|
3856
|
list($addr, $mask) = explode('/', $line[1]);
|
3857
|
$mask = (0xffffffff << (32 - $mask)) & 0xffffffff;
|
3858
|
$validate = ((ip2long($ip) & $mask) == (ip2long($addr) & $mask));
|
3859
|
}
|
3860
|
else {
|
3861
|
/* Normalize IPv6 prefix to its start address to avoid PHP errors
|
3862
|
* https://redmine.pfsense.org/issues/14256
|
3863
|
*/
|
3864
|
list($prefix, $length) = explode('/', $line[1]);
|
3865
|
$prefix = gen_subnetv6($prefix, $length);
|
3866
|
$subnet = "{$prefix}/{$length}";
|
3867
|
|
3868
|
$validate = (Net_IPv6::isInNetmask($ip, $subnet));
|
3869
|
}
|
3870
|
|
3871
|
// Return header on CIDR match
|
3872
|
if ($validate) {
|
3873
|
unset($cidrs);
|
3874
|
return $line;
|
3875
|
}
|
3876
|
}
|
3877
|
unset($cidrs);
|
3878
|
}
|
3879
|
}
|
3880
|
|
3881
|
if (isset($result)) {
|
3882
|
unset($result);
|
3883
|
}
|
3884
|
return array('Unknown', 'Unknown');
|
3885
|
}
|
3886
|
|
3887
|
|
3888
|
// Function to download feeds
|
3889
|
function pfb_download($list_url, $file_dwn, $pflex=FALSE, $header, $format, $logtype, $vtype='', $timeout=300, $type='', $username='', $password='', $srcint=FALSE) {
|
3890
|
global $pfb;
|
3891
|
$http_status = '';
|
3892
|
$elog = ">> {$pfb['log']} 2>&1";
|
3893
|
|
3894
|
// Remove any leading/trailing whitespace
|
3895
|
$list_url = trim($list_url);
|
3896
|
|
3897
|
// Re-evaluate URL
|
3898
|
if ($format == 'whois') {
|
3899
|
if (empty(pfb_filter($list_url, PFB_FILTER_DOMAIN, 'pfb_download'))) {
|
3900
|
pfb_logger("\n Failed", 2);
|
3901
|
return FALSE;
|
3902
|
}
|
3903
|
}
|
3904
|
elseif ($format == 'asn') {
|
3905
|
if (empty(pfb_filter($list_url, PFB_FILTER_ALNUM, 'pfb_download'))) {
|
3906
|
pfb_logger("\n Failed", 2);
|
3907
|
return FALSE;
|
3908
|
}
|
3909
|
}
|
3910
|
elseif (!pfb_filter($list_url, PFB_FILTER_URL, 'pfb_download_failure')) {
|
3911
|
pfb_logger("\n Failed", 2);
|
3912
|
return FALSE;
|
3913
|
}
|
3914
|
|
3915
|
// Cron update function for md5 comparison
|
3916
|
if ($type == 'md5') {
|
3917
|
pfb_logger("\t\t\t\t( md5 feed )\t\t", 1);
|
3918
|
}
|
3919
|
|
3920
|
$file_dwn_esc = escapeshellarg("{$file_dwn}.raw");
|
3921
|
$file_org_esc = escapeshellarg("{$file_dwn}.orig");
|
3922
|
$list_url_esc = escapeshellarg("{$list_url}");
|
3923
|
$header_esc = escapeshellarg("{$header}");
|
3924
|
|
3925
|
$file_download = trim("{$file_dwn_esc}", "'");
|
3926
|
$orig_download = trim("{$file_org_esc}", "'");
|
3927
|
$list_download = trim("{$list_url_esc}", "'");
|
3928
|
$head_download = trim("{$header_esc}", "'");
|
3929
|
|
3930
|
$md5_download = trim(escapeshellarg("{$file_dwn}.md5.raw"), "'");
|
3931
|
|
3932
|
// If the Cron update function 'md5 comparison' generated an md5 file, re-utilize instead of downloading twice
|
3933
|
if (file_exists("{$md5_download}")) {
|
3934
|
$list_download = "{$md5_download}";
|
3935
|
pfb_logger(' ( md5 feed ) ', 1);
|
3936
|
}
|
3937
|
|
3938
|
// Download RSYNC format
|
3939
|
if ($format == 'rsync') {
|
3940
|
$result = exec("/usr/local/bin/rsync --timeout=5 {$list_url_esc} {$file_dwn_esc}");
|
3941
|
if ($result == 0) {
|
3942
|
$http_status = '200';
|
3943
|
} else {
|
3944
|
$log = "\n RSYNC Failed...\n";
|
3945
|
pfb_logger("{$log}", "{$logtype}");
|
3946
|
return FALSE;
|
3947
|
}
|
3948
|
}
|
3949
|
elseif ($format == 'whois' || $format == 'asn') {
|
3950
|
// Convert a Domain name/AS into its respective IP addresses
|
3951
|
exec("{$pfb['script']} whoisconvert {$header_esc} {$vtype} {$list_url_esc} {$elog}");
|
3952
|
return TRUE;
|
3953
|
}
|
3954
|
else {
|
3955
|
// Determine if URL is a pfSense localfile
|
3956
|
$localfile = FALSE;
|
3957
|
if (pfb_filter("{$list_download}", PFB_FILTER_URL, 'pfb_download', '', TRUE)) {
|
3958
|
$localfile = TRUE;
|
3959
|
}
|
3960
|
|
3961
|
// Download localfile format
|
3962
|
if ($localfile) {
|
3963
|
$file_data = @file_get_contents("{$list_download}");
|
3964
|
if ($file_data === FALSE) {
|
3965
|
$error = error_get_last();
|
3966
|
$log = "\n[ {$header} ] {$error['message']}\n";
|
3967
|
pfb_logger("{$log}", "{$logtype}");
|
3968
|
return FALSE;
|
3969
|
} else {
|
3970
|
// Save original downloaded file
|
3971
|
@file_put_contents("{$file_download}", $file_data, LOCK_EX);
|
3972
|
$http_status = '200';
|
3973
|
}
|
3974
|
}
|
3975
|
|
3976
|
// Download using cURL
|
3977
|
else {
|
3978
|
$remote_stamp = -1;
|
3979
|
if (($fhandle = @fopen("{$file_download}", 'w')) !== FALSE) {
|
3980
|
if (!($ch = curl_init("{$list_download}"))) {
|
3981
|
$log = "\nFailed to create cURL resource... Exiting...\n";
|
3982
|
pfb_logger("{$log}", "{$logtype}");
|
3983
|
return FALSE;
|
3984
|
}
|
3985
|
|
3986
|
curl_setopt_array($ch, $pfb['curl_defaults']); // Load curl default settings
|
3987
|
curl_setopt($ch, CURLOPT_FILE, $fhandle); // Add $fhandle setting to cURL
|
3988
|
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); // Set cURL download timeout
|
3989
|
curl_setopt($ch, CURLOPT_ENCODING, 'gzip'); // Request 'gzip' encoding from server if available
|
3990
|
if ($srcint) {
|
3991
|
curl_setopt($ch, CURLOPT_INTERFACE, $srcint); // Use a specific interface when downloading lists
|
3992
|
pfb_logger("\nList: {$header} will be downloaded via interface: {$srcint}\n", 1);
|
3993
|
}
|
3994
|
|
3995
|
if (!empty($username) && !empty($password)) {
|
3996
|
curl_setopt($ch, CURLOPT_USERPWD, "{$username}:{$password}");
|
3997
|
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
3998
|
}
|
3999
|
|
4000
|
// Attempt 3 Downloads before failing.
|
4001
|
for ($retries = 1; $retries <= 3; $retries++) {
|
4002
|
if (curl_exec($ch)) {
|
4003
|
// Collect remote timestamp.
|
4004
|
$raw_filetime = curl_getinfo($ch, CURLINFO_FILETIME);
|
4005
|
if ($raw_filetime == -1) {
|
4006
|
$remote_stamp = -1;
|
4007
|
} elseif (!empty(pfb_filter($raw_filetime, PFB_FILTER_NUM, 'pfb_download - remote timestamp'))) {
|
4008
|
$remote_stamp = $raw_filetime;
|
4009
|
}
|
4010
|
break; // Break on success
|
4011
|
}
|
4012
|
|
4013
|
$curl_error = curl_errno($ch);
|
4014
|
if ($logtype != 3) {
|
4015
|
pfb_logger(" cURL Error: {$curl_error} [ NOW ]\n", 1);
|
4016
|
} else {
|
4017
|
pfb_logger(" {$header}\t\tcURL Error: {$curl_error} [ NOW ]\n\n", 3);
|
4018
|
}
|
4019
|
|
4020
|
/* 'Flex' Downgrade cURL errors - [ 35 - GET_SERVER_HELLO:sslv3 ]
|
4021
|
[ 51 - NO alternative certificate ]
|
4022
|
[ 60 - Local Issuer Certificate Subject ] */
|
4023
|
|
4024
|
// Allow downgrade of cURL settings 'Flex' after 1st failure, if user configured.
|
4025
|
if ($retries == 1 && $pflex && in_array($curl_error, array( '35', '51', '60'))) {
|
4026
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
4027
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
4028
|
curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, 'TLSv1.3, TLSv1.2, TLSv1.1, TLSv1, SSLv3');
|
4029
|
$log = "\n Downgrading SSL settings (Flex)";
|
4030
|
pfb_logger("{$log}", "{$logtype}");
|
4031
|
}
|
4032
|
else {
|
4033
|
if ($retries == 3) {
|
4034
|
$log = curl_error($ch) . " |{$head_download}|{$list_download}| Retry [{$retries}] in 5 seconds...\n";
|
4035
|
} else {
|
4036
|
$log = curl_error($ch) . " Retry [{$retries}] in 5 seconds...\n";
|
4037
|
}
|
4038
|
pfb_logger("{$log}", "{$logtype}");
|
4039
|
sleep(5);
|
4040
|
pfb_logger('.', "{$logtype}");
|
4041
|
}
|
4042
|
}
|
4043
|
|
4044
|
// Collect RFC7231 http status code
|
4045
|
$http_status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
|
4046
|
|
4047
|
if (isset($pfb['rfc7231'][$http_status])) {
|
4048
|
if ($logtype < 3) {
|
4049
|
pfb_logger(". {$pfb['rfc7231'][$http_status]}", 2);
|
4050
|
} else {
|
4051
|
pfb_logger(" {$file_dwn}\t\t{$pfb['rfc7231'][$http_status]}\n", $logtype);
|
4052
|
}
|
4053
|
} else {
|
4054
|
pfb_logger(". Unknown Failure Code [{$http_status}]", $logtype);
|
4055
|
}
|
4056
|
if ($ch) {
|
4057
|
curl_close($ch);
|
4058
|
}
|
4059
|
}
|
4060
|
if ($fhandle) {
|
4061
|
@fclose($fhandle);
|
4062
|
}
|
4063
|
}
|
4064
|
}
|
4065
|
|
4066
|
// Cron update function for md5 comparison
|
4067
|
if ($type == 'md5') {
|
4068
|
if ($http_status == '200') {
|
4069
|
return TRUE;
|
4070
|
}
|
4071
|
return FALSE;
|
4072
|
}
|
4073
|
|
4074
|
// Remove any downloaded files with md5 extension
|
4075
|
unlink_if_exists("{$md5_download}");
|
4076
|
|
4077
|
// '304 not modified' - Utilize previously downloaded file if available
|
4078
|
if ($http_status == '304' && file_exists("{$orig_download}")) {
|
4079
|
return TRUE;
|
4080
|
}
|
4081
|
|
4082
|
if (file_exists($file_download) && ($http_status == '200' || $http_status == '221' || $http_status == '226')) {
|
4083
|
|
4084
|
if (isset($retval)) {
|
4085
|
unset($retval);
|
4086
|
}
|
4087
|
|
4088
|
// Validate File Mime-type
|
4089
|
$file_type = pfb_filter(array("{$file_dwn_esc}", "{$file_download}", "{$list_download}"), PFB_FILTER_FILE_MIME, 'pfb_download');
|
4090
|
if (!$file_type) {
|
4091
|
return FALSE;
|
4092
|
}
|
4093
|
|
4094
|
// Create update file markers on new downloads available
|
4095
|
switch($type) {
|
4096
|
case 'geoip':
|
4097
|
touch("{$pfb['dbdir']}/geoip.update");
|
4098
|
break;
|
4099
|
case 'top1m':
|
4100
|
touch("{$pfb['dbdir']}/top-1m.update");
|
4101
|
break;
|
4102
|
case 'asn':
|
4103
|
touch("{$pfb['dbdir']}/asn.update");
|
4104
|
break;
|
4105
|
default:
|
4106
|
break;
|
4107
|
}
|
4108
|
|
4109
|
// Set downloaded file timestamp to remote timestamp
|
4110
|
if (isset($remote_stamp) && $remote_stamp != -1) {
|
4111
|
@touch("{$file_download}", $remote_stamp);
|
4112
|
}
|
4113
|
|
4114
|
// Decompress file if required
|
4115
|
if ($file_type == 'application/x-gzip' || $file_type == 'application/gzip') {
|
4116
|
if ($type == 'geoip') {
|
4117
|
// Extras - MaxMind downloads
|
4118
|
exec("/usr/bin/tar -xzf {$file_dwn_esc} --strip=1 -C {$pfb['geoipshare']} >/dev/null 2>&1");
|
4119
|
unlink_if_exists($file_dwn_esc);
|
4120
|
return TRUE;
|
4121
|
}
|
4122
|
elseif ($type == 'asn') {
|
4123
|
exec("/usr/bin/gunzip -c {$file_dwn_esc} > {$header_esc}", $output, $retval);
|
4124
|
|
4125
|
// Update ASN Lookup table definitions
|
4126
|
exec("{$pfb['script']} asn_table >> {$logtype} 2>&1");
|
4127
|
|
4128
|
unlink_if_exists($file_dwn_esc);
|
4129
|
return TRUE;
|
4130
|
}
|
4131
|
elseif ($type == 'blacklist') {
|
4132
|
// Extras - Blacklist downloads
|
4133
|
@rename("{$file_download}", strstr("{$file_download}", '.raw', TRUE));
|
4134
|
$file_esc = trim(escapeshellarg("{$file_dwn}"), "'");
|
4135
|
$filename = basename("{$file_esc}", '.tar.gz');
|
4136
|
|
4137
|
if (!empty(pfb_filter($filename, PFB_FILTER_WORD, 'pfb_download category'))) {
|
4138
|
rmdir_recursive("{$pfb['dbdir']}/{$filename}/");
|
4139
|
safe_mkdir("{$pfb['dbdir']}/{$filename}/");
|
4140
|
|
4141
|
// Extract Blacklist categories from sub-folders into a single folder structure
|
4142
|
$cmd = "--include='*domains' -s',.*/\\(.*\\)/\\(.*\\)/domains,{$filename}_\\1_\\2,' -s',.*/\\(.*\\)/domains,{$filename}_\\1,'";
|
4143
|
$filename_esc = escapeshellarg("{$pfb['dbdir']}/{$filename}/");
|
4144
|
exec("/usr/bin/tar -xf " . escapeshellarg("{$file_dwn}") . " {$cmd} -C {$filename_esc} >/dev/null 2>&1");
|
4145
|
|
4146
|
// Rename any Category files with dashes
|
4147
|
$verifydir = "{$pfb['dbdir']}/{$filename}";
|
4148
|
if (is_dir("{$verifydir}")) {
|
4149
|
$list = glob("{$verifydir}/{$filename}*");
|
4150
|
if (is_array($list) && !empty($list)) {
|
4151
|
foreach ($list as $verify) {
|
4152
|
if (strpos($verify, '-') !== FALSE) {
|
4153
|
rename($verify, str_replace('-', '_', $verify));
|
4154
|
}
|
4155
|
}
|
4156
|
}
|
4157
|
}
|
4158
|
|
4159
|
// Create update file indicator for update process
|
4160
|
touch("{$pfb['dbdir']}/{$filename}/{$filename}.update");
|
4161
|
}
|
4162
|
else {
|
4163
|
pfb_logger("\n Invalid filename [{$filename}]", 1);
|
4164
|
return FALSE;
|
4165
|
}
|
4166
|
}
|
4167
|
else {
|
4168
|
if (!pfb_filter(array("{$file_dwn_esc}", "{$file_download}", "{$list_download}"), PFB_FILTER_FILE_MIME_COMPRESSED, 'pfb_download')) {
|
4169
|
return FALSE;
|
4170
|
}
|
4171
|
pfb_logger('.', 1);
|
4172
|
exec("/usr/bin/gunzip -c {$file_dwn_esc} > {$file_org_esc}", $output, $retval);
|
4173
|
}
|
4174
|
}
|
4175
|
elseif ($file_type == 'application/x-bzip2') {
|
4176
|
if (!pfb_filter(array("{$file_dwn_esc}", "{$file_download}", "{$list_download}"), PFB_FILTER_FILE_MIME_COMPRESSED, 'pfb_download')) {
|
4177
|
return FALSE;
|
4178
|
}
|
4179
|
pfb_logger('.', 1);
|
4180
|
exec("/usr/bin/bzip2 -dkc {$file_dwn_esc} > {$file_org_esc}", $output, $retval);
|
4181
|
}
|
4182
|
elseif ($file_type == 'application/zip') {
|
4183
|
|
4184
|
// Extras - MaxMind/TOP1M downloads
|
4185
|
if ($type == 'geoip' || $type == 'top1m') {
|
4186
|
// Determine if Zip contains multiple files
|
4187
|
exec("/usr/bin/tar -tf {$file_dwn_esc} 2>&1", $archive_count, $retval);
|
4188
|
if ($archive_count[0] == 'tar: Failed to set default locale') {
|
4189
|
unset($archive_count[0]);
|
4190
|
}
|
4191
|
if (count($archive_count) > 1) {
|
4192
|
exec("/usr/bin/tar -xf {$file_dwn_esc} --strip=1 -C {$header_esc} >/dev/null 2>&1");
|
4193
|
} else {
|
4194
|
exec("/usr/bin/tar -xOf {$file_dwn_esc} > {$header_esc}");
|
4195
|
}
|
4196
|
return TRUE;
|
4197
|
}
|
4198
|
|
4199
|
pfb_logger('.', 1);
|
4200
|
|
4201
|
/* TODO: FIX - Bypass ZIP Compression file mime type validation due to incompatability with ZIP files
|
4202
|
if (!pfb_filter(array("{$file_dwn_esc}", "{$file_download}", "{$list_download}"), PFB_FILTER_FILE_MIME_COMPRESSED, 'pfb_download')) {
|
4203
|
return FALSE;
|
4204
|
}
|
4205
|
*/
|
4206
|
|
4207
|
// Check if ZIP archive contains xlsx files
|
4208
|
$xlsxtest = exec("/usr/bin/tar -tf {$file_dwn_esc}");
|
4209
|
if (strpos($xlsxtest, '.xlsx') !== FALSE) {
|
4210
|
unlink_if_exists("{$orig_download}");
|
4211
|
exec("{$pfb['script']} xlsx {$header_esc} {$elog}");
|
4212
|
if (file_exists("{$orig_download}")) {
|
4213
|
$retval = 0;
|
4214
|
}
|
4215
|
} else {
|
4216
|
pfb_logger('.', 1);
|
4217
|
// Process ZIP file (SFS and hpHosts workaround)
|
4218
|
exec("/usr/bin/tar -xOf {$file_dwn_esc} | /usr/bin/sed 's/,[[:space:]]/; /g' | /usr/bin/tr ',' '\n' > {$file_org_esc}", $output, $retval);
|
4219
|
}
|
4220
|
|
4221
|
// TODO: Validate file contents after extraction (to be removed once ZIP compression file mime type is fixed above)
|
4222
|
if (!pfb_filter(array("{$file_org_esc}", "{$file_download}", "{$list_download}"), PFB_FILTER_FILE_MIME, 'pfb_download')) {
|
4223
|
unlink_if_exists("{$file_org_esc}");
|
4224
|
return FALSE;
|
4225
|
}
|
4226
|
}
|
4227
|
elseif ($file_type == 'application/x-7z-compressed') {
|
4228
|
if (!pfb_filter(array("{$file_dwn_esc}", "{$file_download}", "{$list_download}"), PFB_FILTER_FILE_MIME_COMPRESSED, 'pfb_download')) {
|
4229
|
return FALSE;
|
4230
|
}
|
4231
|
pfb_logger('.', 1);
|
4232
|
exec("/usr/local/bin/7z e -so {$file_dwn_esc} > {$file_org_esc}", $output, $retval);
|
4233
|
}
|
4234
|
else {
|
4235
|
// Uncompressed file format.
|
4236
|
if ($type == 'geoip' || $type == 'asn') {
|
4237
|
// Extras - MaxMind/TOP1M/ASN downloads
|
4238
|
@rename("{$file_download}", "{$head_download}");
|
4239
|
return TRUE;
|
4240
|
}
|
4241
|
elseif ($type == 'blacklist') {
|
4242
|
$retval = 0;
|
4243
|
}
|
4244
|
else {
|
4245
|
// Rename file to 'orig' format
|
4246
|
@rename("{$file_download}", "{$orig_download}");
|
4247
|
$retval = 0;
|
4248
|
}
|
4249
|
}
|
4250
|
|
4251
|
if ($retval == 0) {
|
4252
|
// Set downloaded file timestamp to remote timestamp
|
4253
|
if (isset($remote_stamp) && $remote_stamp != -1) {
|
4254
|
@touch("{$orig_download}", $remote_stamp);
|
4255
|
}
|
4256
|
|
4257
|
// Process Emerging Threats IQRisk if required
|
4258
|
if (strpos($list_url, 'iprepdata.txt') !== FALSE) {
|
4259
|
exec("{$pfb['script']} et {$header_esc} x x x x x {$pfb['etblock']} {$pfb['etmatch']} {$elog}");
|
4260
|
}
|
4261
|
return TRUE;
|
4262
|
}
|
4263
|
else {
|
4264
|
$log = " Decompression Failed\n";
|
4265
|
pfb_logger("{$log}", 2);
|
4266
|
return FALSE;
|
4267
|
}
|
4268
|
}
|
4269
|
else {
|
4270
|
// Download failed
|
4271
|
unlink_if_exists("{$file_download}");
|
4272
|
}
|
4273
|
return FALSE;
|
4274
|
}
|
4275
|
|
4276
|
|
4277
|
// Determine reason for download failure
|
4278
|
function pfb_download_failure($alias, $header, $pfbfolder, $list_url, $format, $vtype) {
|
4279
|
global $pfb;
|
4280
|
$pfbfound = FALSE;
|
4281
|
|
4282
|
// Re-evaluate URL
|
4283
|
if ($format == 'whois') {
|
4284
|
if (empty(pfb_filter($list_url, PFB_FILTER_DOMAIN, 'pfb_download_failure'))) {
|
4285
|
pfb_logger("\n Invalid WHOIS. Terminating Download! [ {$list_url} ]\n", 1);
|
4286
|
}
|
4287
|
}
|
4288
|
elseif ($format == 'asn') {
|
4289
|
if (empty(pfb_filter($list_url, PFB_FILTER_ALNUM, 'pfb_download_failure'))) {
|
4290
|
pfb_logger("\n Invalid ASN. Terminating Download! [ {$list_url} ]\n", 1);
|
4291
|
}
|
4292
|
}
|
4293
|
elseif (!pfb_filter($list_url, PFB_FILTER_URL, 'pfb_download_failure')) {
|
4294
|
pfb_logger("\n Invalid URL. Terminating Download! [ {$list_url} ]\n", 1);
|
4295
|
}
|
4296
|
else {
|
4297
|
// Determine if URL is a localfile
|
4298
|
$localfile = FALSE;
|
4299
|
if (pfb_filter($list_url, PFB_FILTER_URL, 'pfb_download_failure', '', TRUE)) {
|
4300
|
$localfile = TRUE;
|
4301
|
}
|
4302
|
|
4303
|
// Log FAILED downloads and check if firewall or Snort/Suricata is blocking host
|
4304
|
$log = "\n\n [ {$alias} - {$header} ] Download FAIL [ NOW ]\n";
|
4305
|
pfb_logger("{$log}", 2);
|
4306
|
|
4307
|
// Only perform these checks if they are not 'localfiles'
|
4308
|
if ($localfile) {
|
4309
|
$log = " Local File Failure\n";
|
4310
|
pfb_logger("{$log}", 2);
|
4311
|
}
|
4312
|
else {
|
4313
|
// Determine if Firewall/IPS/DNSBL is blocking download.
|
4314
|
$data = parse_url("{$list_url}");
|
4315
|
|
4316
|
$validate_list = array();
|
4317
|
if (is_ipaddr($data['host'])) {
|
4318
|
$validate_list = array(array('type' => 'IP', 'data' => $data['host']));
|
4319
|
} else {
|
4320
|
$validate_list = resolve_host_addresses($data['host']);
|
4321
|
}
|
4322
|
|
4323
|
$validate_list_final = array();
|
4324
|
if (!empty($validate_list) && is_array($validate_list)) {
|
4325
|
foreach ($validate_list as $validate) {
|
4326
|
if ($validate['type'] == 'CNAME') {
|
4327
|
$cname_list = resolve_host_addresses($validate['data']);
|
4328
|
if (!empty($cname_list) && is_array($cname_list)) {
|
4329
|
foreach ($cname_list as $cname) {
|
4330
|
if (!empty($cname['data'])) {
|
4331
|
$validate_list_final[$cname['data']] = "Host:{$data['host']} | CNAME:{$cname['host']}";
|
4332
|
}
|
4333
|
}
|
4334
|
}
|
4335
|
}
|
4336
|
elseif (!empty($validate['data'])) {
|
4337
|
$validate_list_final[$validate['data']] = $data['host'];
|
4338
|
}
|
4339
|
}
|
4340
|
}
|
4341
|
else {
|
4342
|
pfb_logger("\n Cannot Resolve Host:{$data['host']}", 1);
|
4343
|
}
|
4344
|
|
4345
|
if (!empty($validate_list_final)) {
|
4346
|
foreach ($validate_list_final as $ip => $host) {
|
4347
|
if (is_ipaddr($ip)) {
|
4348
|
|
4349
|
// Query Firewall aliastables
|
4350
|
$result = find_reported_header($ip, "{$pfbfolder}/*", FALSE);
|
4351
|
if (!empty($result) && $result[0] != 'Unknown') {
|
4352
|
$log = " [ {$ip} ] Firewall IP block found in: [ {$result[0]} | {$result[1]} ] for HOST:{$host}!\n";
|
4353
|
pfb_logger("{$log}", 2);
|
4354
|
$pfbfound = TRUE;
|
4355
|
}
|
4356
|
|
4357
|
// Determine if Host is listed in DNSBL
|
4358
|
if ($ip == $pfb['dnsbl_vip'] || $ip == "::{$pfb['dnsbl_vip']}" || $ip == '0.0.0.0') {
|
4359
|
$log = " [ {$host} ] Domain blocked via DNSBL!\n";
|
4360
|
pfb_logger("{$log}", 2);
|
4361
|
$pfbfound = TRUE;
|
4362
|
}
|
4363
|
|
4364
|
// Query Snort/Suricata snort2c IP block table
|
4365
|
$ip_esc = escapeshellarg($ip);
|
4366
|
$result = substr(exec("{$pfb['pfctl']} -t snort2c -T test {$ip_esc} 2>&1"), 0, 1);
|
4367
|
if ($result > 0) {
|
4368
|
$log = " [ {$ip} ] IDS IP block found for HOST:{$host}!\n";
|
4369
|
pfb_logger("{$log}", 2);
|
4370
|
$pfbfound = TRUE;
|
4371
|
}
|
4372
|
}
|
4373
|
else {
|
4374
|
pfb_logger("\nInvalid IP or NXDOMAIN found for HOST:{$host}", 2);
|
4375
|
$pfbfound = TRUE;
|
4376
|
}
|
4377
|
}
|
4378
|
}
|
4379
|
|
4380
|
if (!$pfbfound) {
|
4381
|
$log = " DNSBL, Firewall, and IDS (Legacy mode only) are not blocking download.\n";
|
4382
|
pfb_logger("{$log}", 2);
|
4383
|
}
|
4384
|
}
|
4385
|
}
|
4386
|
|
4387
|
// Call function to get all previous download fails
|
4388
|
pfb_failures();
|
4389
|
|
4390
|
// On download failure, create file marker for subsequent download attempts. ('0' no download failure threshold)
|
4391
|
if ($pfb['skipfeed'] == 0 || $pfb['failed'][$header] <= $pfb['skipfeed']) {
|
4392
|
touch("{$pfbfolder}/{$header}.fail");
|
4393
|
return;
|
4394
|
}
|
4395
|
|
4396
|
unlink_if_exists("{$pfbfolder}/{$header}.fail");
|
4397
|
return;
|
4398
|
}
|
4399
|
|
4400
|
|
4401
|
// Collect all previously failed daily download notices
|
4402
|
function pfb_failures() {
|
4403
|
global $pfb;
|
4404
|
$pfb['failed'] = array();
|
4405
|
|
4406
|
if (file_exists("{$pfb['errlog']}")) {
|
4407
|
$today_date = date('m/j/y', time());
|
4408
|
exec("{$pfb['grep']} 'FAIL' {$pfb['errlog']} | {$pfb['grep']} {$today_date}", $results);
|
4409
|
if (!empty($results)) {
|
4410
|
foreach ($results as $result) {
|
4411
|
$header = explode(' ', $result);
|
4412
|
$pfb['failed'][$header[4]] += 1;
|
4413
|
}
|
4414
|
}
|
4415
|
}
|
4416
|
return;
|
4417
|
}
|
4418
|
|
4419
|
|
4420
|
// Convert unique Alias details (via ascii table number) and return a 10 digit tracker ID
|
4421
|
function pfb_tracker($alias, $int, $text) {
|
4422
|
global $pfb;
|
4423
|
|
4424
|
$pfbtracker = 0;
|
4425
|
$real_int = get_real_interface($int);
|
4426
|
$ipaddr = get_interface_ip($int);
|
4427
|
|
4428
|
if (is_ipaddrv4($ipaddr)) {
|
4429
|
$ipaddr = ip2long32($ipaddr);
|
4430
|
$subnet = find_interface_subnet($real_int);
|
4431
|
}
|
4432
|
else {
|
4433
|
$ipaddr = get_interface_ipv6($real_int);
|
4434
|
$subnet = find_interface_subnetv6($real_int);
|
4435
|
}
|
4436
|
|
4437
|
$search = array( '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' );
|
4438
|
$replace = array( 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'zero' );
|
4439
|
$line = "{$alias}{$int}{$text}{$real_int}{$ipaddr}{$subnet}";
|
4440
|
$line = str_replace($search, $replace, $line);
|
4441
|
|
4442
|
for ($i = 0; $i < strlen($line); $i++) {
|
4443
|
$pfbtracker += @ord($line[$i]);
|
4444
|
}
|
4445
|
|
4446
|
$pfbtracker = '177' . str_pad($pfbtracker, 7, '0', STR_PAD_LEFT);
|
4447
|
if (strlen($pfbtracker) > 10) {
|
4448
|
$pfbtracker = substr($pfbtracker, 0, 10);
|
4449
|
}
|
4450
|
|
4451
|
// If duplicate Tracker ID found, pre-define a Tracker ID (Starts at 1700000010)
|
4452
|
if (in_array($pfbtracker, $pfb['trackerids'])) {
|
4453
|
$pfbtracker = ($pfb['last_trackerid'] + 1);
|
4454
|
|
4455
|
// Increment prefix (digits 1&2) and reset Last_tracker ID after 10 digits
|
4456
|
if (strlen($pfbtracker) > 10) {
|
4457
|
$tracker_prefix = substr($pfbtracker, 0, 2);
|
4458
|
$pfbtracker = ($tracker_prefix + 1) . '00000010';
|
4459
|
}
|
4460
|
$pfb['last_trackerid'] = $pfbtracker;
|
4461
|
}
|
4462
|
|
4463
|
$pfb['trackerids'][] = $pfbtracker;
|
4464
|
return (int)$pfbtracker;
|
4465
|
}
|
4466
|
|
4467
|
|
4468
|
// Define firewall rule settings
|
4469
|
function pfb_firewall_rule($action, $pfb_alias, $vtype, $pfb_log, $agateway_in='default', $agateway_out='default',
|
4470
|
$aaddrnot_in='', $adest_in='', $aports_in='', $aproto_in='', $anot_in='',
|
4471
|
$aaddrnot_out='', $asrc_out='', $aports_out='', $aproto_out='', $anot_out='') {
|
4472
|
|
4473
|
global $pfb;
|
4474
|
$rule = array();
|
4475
|
|
4476
|
switch ($action) {
|
4477
|
case 'Deny_Both':
|
4478
|
case 'Deny_Outbound':
|
4479
|
$rule = $pfb['base_rule'];
|
4480
|
$rule['type'] = "{$pfb['deny_action_outbound']}";
|
4481
|
if ($vtype == '_v6') {
|
4482
|
$rule['ipprotocol'] = 'inet6';
|
4483
|
}
|
4484
|
if ($pfb['float'] == 'on') {
|
4485
|
$rule['direction'] = 'any';
|
4486
|
}
|
4487
|
$rule['descr'] = "{$pfb_alias}{$pfb['suffix']}";
|
4488
|
if (!empty($asrc_out)) {
|
4489
|
$rule['source'] = array('address' => "{$asrc_out}");
|
4490
|
} else {
|
4491
|
$rule['source'] = array('any' => '');
|
4492
|
}
|
4493
|
if (!empty($asrc_out) && $anot_out == 'on') {
|
4494
|
$rule['source']['not'] = '';
|
4495
|
}
|
4496
|
if (!empty($aports_out)) {
|
4497
|
$rule['destination'] = array('address' => "{$pfb_alias}", 'port' => "{$aports_out}");
|
4498
|
} else {
|
4499
|
$rule['destination'] = array('address' => "{$pfb_alias}");
|
4500
|
}
|
4501
|
if ($aaddrnot_out == 'on') {
|
4502
|
$rule['destination']['not'] = '';
|
4503
|
}
|
4504
|
if (!empty($aproto_out)) {
|
4505
|
$rule['protocol'] = "{$aproto_out}";
|
4506
|
}
|
4507
|
if ($pfb['global_log'] == 'on' || $pfb_log == 'enabled') {
|
4508
|
$rule['log'] = '';
|
4509
|
}
|
4510
|
if (!empty($agateway_out) && $agateway_out != 'default') {
|
4511
|
$rule['gateway'] = "{$agateway_out}";
|
4512
|
if ($pfb['float'] == 'on') {
|
4513
|
$rule['direction'] = 'out';
|
4514
|
}
|
4515
|
}
|
4516
|
$rule['created'] = array('time' => (int)microtime(true), 'username' => 'Auto');
|
4517
|
$pfb['deny_outbound'][] = $rule;
|
4518
|
if ($action != 'Deny_Both') {
|
4519
|
break;
|
4520
|
}
|
4521
|
case 'Deny_Inbound':
|
4522
|
$rule = $pfb['base_rule'];
|
4523
|
$rule['type'] = "{$pfb['deny_action_inbound']}";
|
4524
|
if ($vtype == '_v6') {
|
4525
|
$rule['ipprotocol'] = 'inet6';
|
4526
|
}
|
4527
|
if ($pfb['float'] == 'on') {
|
4528
|
$rule['direction'] = 'any';
|
4529
|
}
|
4530
|
$rule['descr'] = "{$pfb_alias}{$pfb['suffix']}";
|
4531
|
$rule['source'] = array('address' => "{$pfb_alias}");
|
4532
|
if ($aaddrnot_in == 'on') {
|
4533
|
$rule['source']['not'] = '';
|
4534
|
}
|
4535
|
if (!empty($adest_in) && !empty($aports_in)) {
|
4536
|
$rule['destination'] = array('address' => "{$adest_in}", 'port' => "{$aports_in}");
|
4537
|
} elseif (!empty($adest_in) && empty($aports_in)) {
|
4538
|
$rule['destination'] = array('address' => "{$adest_in}");
|
4539
|
} elseif (empty($adest_in) && !empty($aports_in)) {
|
4540
|
$rule['destination'] = array('any' => '', 'port' => "{$aports_in}");
|
4541
|
} else {
|
4542
|
$rule['destination'] = array('any' => '');
|
4543
|
}
|
4544
|
if (!empty($adest_in) && $anot_in == 'on') {
|
4545
|
$rule['destination']['not'] = '';
|
4546
|
}
|
4547
|
if (!empty($aproto_in)) {
|
4548
|
$rule['protocol'] = "{$aproto_in}";
|
4549
|
}
|
4550
|
if ($pfb['global_log'] == 'on' || $pfb_log == 'enabled') {
|
4551
|
$rule['log'] = '';
|
4552
|
}
|
4553
|
if (!empty($agateway_in) && $agateway_in != 'default') {
|
4554
|
$rule['gateway'] = "{$agateway_in}";
|
4555
|
if ($pfb['float'] == 'on') {
|
4556
|
$rule['direction'] = 'in';
|
4557
|
}
|
4558
|
}
|
4559
|
$rule['created'] = array('time' => (int)microtime(true), 'username' => 'Auto');
|
4560
|
$pfb['deny_inbound'][] = $rule;
|
4561
|
break;
|
4562
|
case 'Permit_Both':
|
4563
|
case 'Permit_Outbound':
|
4564
|
$rule = $pfb['base_rule'];
|
4565
|
$rule['type'] = 'pass';
|
4566
|
if ($vtype == '_v6') {
|
4567
|
$rule['ipprotocol'] = 'inet6';
|
4568
|
}
|
4569
|
if ($pfb['float'] == 'on') {
|
4570
|
$rule['direction'] = 'any';
|
4571
|
}
|
4572
|
$rule['descr'] = "{$pfb_alias}{$pfb['suffix']}";
|
4573
|
if (!empty($asrc_out)) {
|
4574
|
$rule['source'] = array('address' => "{$asrc_out}");
|
4575
|
} else {
|
4576
|
$rule['source'] = array('any' => '');
|
4577
|
}
|
4578
|
if (!empty($asrc_out) && $anot_out == 'on') {
|
4579
|
$rule['source']['not'] = '';
|
4580
|
}
|
4581
|
if (!empty($aports_out)) {
|
4582
|
$rule['destination'] = array('address' => "{$pfb_alias}", 'port' => "{$aports_out}");
|
4583
|
} else {
|
4584
|
$rule['destination'] = array('address' => "{$pfb_alias}");
|
4585
|
}
|
4586
|
if ($aaddrnot_out == 'on') {
|
4587
|
$rule['destination']['not'] = '';
|
4588
|
}
|
4589
|
if (!empty($aproto_out)) {
|
4590
|
$rule['protocol'] = "{$aproto_out}";
|
4591
|
}
|
4592
|
if ($pfb['global_log'] == 'on' || $pfb_log == 'enabled') {
|
4593
|
$rule['log'] = '';
|
4594
|
}
|
4595
|
if (!empty($agateway_out) && $agateway_out != 'default') {
|
4596
|
$rule['gateway'] = "{$agateway_out}";
|
4597
|
if ($pfb['float'] == 'on') {
|
4598
|
$rule['direction'] = 'out';
|
4599
|
}
|
4600
|
}
|
4601
|
$rule['created'] = array('time' => (int)microtime(true), 'username' => 'Auto');
|
4602
|
$pfb['permit_outbound'][] = $rule;
|
4603
|
if ($action != 'Permit_Both') {
|
4604
|
break;
|
4605
|
}
|
4606
|
case 'Permit_Inbound':
|
4607
|
$rule = $pfb['base_rule'];
|
4608
|
$rule['type'] = 'pass';
|
4609
|
if ($vtype == '_v6') {
|
4610
|
$rule['ipprotocol'] = 'inet6';
|
4611
|
}
|
4612
|
if ($pfb['float'] == 'on') {
|
4613
|
$rule['direction'] = 'any';
|
4614
|
}
|
4615
|
$rule['descr'] = "{$pfb_alias}{$pfb['suffix']}";
|
4616
|
$rule['source'] = array('address' => "{$pfb_alias}");
|
4617
|
if ($aaddrnot_in == 'on') {
|
4618
|
$rule['source']['not'] = '';
|
4619
|
}
|
4620
|
if (!empty($adest_in) && !empty($aports_in)) {
|
4621
|
$rule['destination'] = array('address' => "{$adest_in}", 'port' => "{$aports_in}");
|
4622
|
} elseif (!empty($adest_in) && empty($aports_in)) {
|
4623
|
$rule['destination'] = array('address' => "{$adest_in}");
|
4624
|
} elseif (empty($adest_in) && !empty($aports_in)) {
|
4625
|
$rule['destination'] = array('any' => '', 'port' => "{$aports_in}");
|
4626
|
} else {
|
4627
|
$rule['destination'] = array('any' => '');
|
4628
|
}
|
4629
|
if (!empty($adest_in) && $anot_in == 'on') {
|
4630
|
$rule['destination']['not'] = '';
|
4631
|
}
|
4632
|
if (!empty($aproto_in)) {
|
4633
|
$rule['protocol'] = "{$aproto_in}";
|
4634
|
}
|
4635
|
if ($pfb['global_log'] == 'on' || $pfb_log == 'enabled') {
|
4636
|
$rule['log'] = '';
|
4637
|
}
|
4638
|
if (!empty($agateway_in) && $agateway_in != 'default') {
|
4639
|
$rule['gateway'] = "{$agateway_in}";
|
4640
|
if ($pfb['float'] == 'on') {
|
4641
|
$rule['direction'] = 'in';
|
4642
|
}
|
4643
|
}
|
4644
|
$rule['created'] = array('time' => (int)microtime(true), 'username' => 'Auto');
|
4645
|
$pfb['permit_inbound'][] = $rule;
|
4646
|
break;
|
4647
|
case 'Match_Both':
|
4648
|
case 'Match_Outbound':
|
4649
|
$rule = $pfb['base_rule_float'];
|
4650
|
$rule['type'] = 'match';
|
4651
|
if ($vtype == '_v6') {
|
4652
|
$rule['ipprotocol'] = 'inet6';
|
4653
|
}
|
4654
|
$rule['direction'] = 'any';
|
4655
|
$rule['descr'] = "{$pfb_alias}{$pfb['suffix']}";
|
4656
|
if (!empty($asrc_out)) {
|
4657
|
$rule['source'] = array('address' => "{$asrc_out}");
|
4658
|
} else {
|
4659
|
$rule['source'] = array('any' => '');
|
4660
|
}
|
4661
|
if (!empty($asrc_out) && $anot_out == 'on') {
|
4662
|
$rule['source']['not'] = '';
|
4663
|
}
|
4664
|
if (!empty($aports_out)) {
|
4665
|
$rule['destination'] = array('address' => "{$pfb_alias}", 'port' => "{$aports_out}");
|
4666
|
} else {
|
4667
|
$rule['destination'] = array('address' => "{$pfb_alias}");
|
4668
|
}
|
4669
|
if ($aaddrnot_out == 'on') {
|
4670
|
$rule['destination']['not'] = '';
|
4671
|
}
|
4672
|
if (!empty($aproto_out)) {
|
4673
|
$rule['protocol'] = "{$aproto_out}";
|
4674
|
}
|
4675
|
if ($pfb['global_log'] == 'on' || $pfb_log == 'enabled') {
|
4676
|
$rule['log'] = '';
|
4677
|
}
|
4678
|
if (!empty($agateway_out) && $agateway_out != 'default') {
|
4679
|
$rule['gateway'] = "{$agateway_out}";
|
4680
|
$rule['direction'] = 'out';
|
4681
|
}
|
4682
|
$rule['created'] = array('time' => (int)microtime(true), 'username' => 'Auto');
|
4683
|
$pfb['match_outbound'][] = $rule;
|
4684
|
if ($action != 'Match_Both') {
|
4685
|
break;
|
4686
|
}
|
4687
|
case 'Match_Inbound':
|
4688
|
$rule = $pfb['base_rule_float'];
|
4689
|
$rule['type'] = 'match';
|
4690
|
if ($vtype == '_v6') {
|
4691
|
$rule['ipprotocol'] = 'inet6';
|
4692
|
}
|
4693
|
$rule['direction'] = 'any';
|
4694
|
$rule['descr'] = "{$pfb_alias}{$pfb['suffix']}";
|
4695
|
$rule['source'] = array('address' => "{$pfb_alias}");
|
4696
|
if ($aaddrnot_in == 'on') {
|
4697
|
$rule['source']['not'] = '';
|
4698
|
}
|
4699
|
if (!empty($adest_in) && !empty($aports_in)) {
|
4700
|
$rule['destination'] = array('address' => "{$adest_in}", 'port' => "{$aports_in}");
|
4701
|
} elseif (!empty($adest_in) && empty($aports_in)) {
|
4702
|
$rule['destination'] = array('address' => "{$adest_in}");
|
4703
|
} elseif (empty($adest_in) && !empty($aports_in)) {
|
4704
|
$rule['destination'] = array('any' => '', 'port' => "{$aports_in}");
|
4705
|
} else {
|
4706
|
$rule['destination'] = array('any' => '');
|
4707
|
}
|
4708
|
if (!empty($adest_in) && $anot_in == 'on') {
|
4709
|
$rule['destination']['not'] = '';
|
4710
|
}
|
4711
|
if (!empty($aproto_in)) {
|
4712
|
$rule['protocol'] = "{$aproto_in}";
|
4713
|
}
|
4714
|
if ($pfb['global_log'] == 'on' || $pfb_log == 'enabled') {
|
4715
|
$rule['log'] = '';
|
4716
|
}
|
4717
|
if (!empty($agateway_in) && $agateway_in != 'default') {
|
4718
|
$rule['gateway'] = "{$agateway_in}";
|
4719
|
$rule['direction'] = 'in';
|
4720
|
}
|
4721
|
$rule['created'] = array('time' => (int)microtime(true), 'username' => 'Auto');
|
4722
|
$pfb['match_inbound'][] = $rule;
|
4723
|
break;
|
4724
|
}
|
4725
|
return;
|
4726
|
}
|
4727
|
|
4728
|
|
4729
|
// Archive IP aliastables and DNSBL database. ( Ramdisk installations only )
|
4730
|
function pfb_aliastables($mode) {
|
4731
|
global $g, $pfb;
|
4732
|
|
4733
|
$msg = '';
|
4734
|
$earlyshellcmd = '/usr/local/pkg/pfblockerng/pfblockerng.sh aliastables';
|
4735
|
|
4736
|
// Reload config.xml to get any recent changes
|
4737
|
config_read_file(false, true);
|
4738
|
|
4739
|
$a_earlyshellcmd = config_get_path('system/earlyshellcmd', []);
|
4740
|
$a_shellcmdsettings = config_get_path('installedpackages/shellcmdsettings/config', []);
|
4741
|
|
4742
|
// Only execute function if Ramdisks are used.
|
4743
|
if (config_path_enabled('system', 'use_mfs_tmpvar')) {
|
4744
|
|
4745
|
// Archive aliastable folder
|
4746
|
if ($mode == 'update') {
|
4747
|
pfb_logger("\nArchiving Aliastable folder", 1);
|
4748
|
|
4749
|
$files_to_backup = '';
|
4750
|
$files = glob("{{$pfb['aliasdir']}/pfB_*.txt,{$pfb['dnsbl_file']}.conf,/var/unbound/pfb_unbound*,/var/unbound/pfb_py_*}", GLOB_BRACE);
|
4751
|
if (!empty($files)) {
|
4752
|
$files_to_backup = implode(' ', array_map('escapeshellarg', array_filter($files)));
|
4753
|
}
|
4754
|
|
4755
|
// Archive IP Aliastables/Unbound DNSBL Database as required.
|
4756
|
if (!empty($files_to_backup)) {
|
4757
|
exec("/usr/bin/tar -jcvf {$pfb['aliasarchive']} {$files_to_backup} >/dev/null 2>&1");
|
4758
|
pfb_logger("\nArchiving selected pfBlockerNG files.\n", 1);
|
4759
|
} else {
|
4760
|
pfb_logger("\nNo Files to archive.\n", 1);
|
4761
|
}
|
4762
|
}
|
4763
|
|
4764
|
// Check conf file for earlyshellcmd/shellcmd package settings
|
4765
|
elseif ($mode == 'conf') {
|
4766
|
|
4767
|
// Add earlyshellcmd settings
|
4768
|
if (!empty($a_earlyshellcmd)) {
|
4769
|
if (!preg_grep("/pfblockerng.sh aliastables/", $a_earlyshellcmd)) {
|
4770
|
$a_earlyshellcmd[] = "{$earlyshellcmd}";
|
4771
|
$msg = "\n** Adding earlyshellcmd settings **\n";
|
4772
|
}
|
4773
|
}
|
4774
|
else {
|
4775
|
$a_earlyshellcmd = "{$earlyshellcmd}";
|
4776
|
$msg = "\n** Adding earlyshellcmd settings **\n";
|
4777
|
}
|
4778
|
|
4779
|
// Add shellcmd package settings
|
4780
|
$found = FALSE;
|
4781
|
if (!empty($a_shellcmdsettings)) {
|
4782
|
foreach ($a_shellcmdsettings as $key => $shellcmd) {
|
4783
|
if (strpos($shellcmd['cmd'], 'pfblockerng.sh aliastables') !== FALSE) {
|
4784
|
$found = TRUE;
|
4785
|
break;
|
4786
|
}
|
4787
|
}
|
4788
|
}
|
4789
|
|
4790
|
if (!$found) {
|
4791
|
$add = array( 'cmd' => $earlyshellcmd,
|
4792
|
'cmdtype' => 'earlyshellcmd',
|
4793
|
'description' => 'pfBlockerNG earlyshellcmd. DO NOT EDIT/DELETE!');
|
4794
|
|
4795
|
$a_shellcmdsettings[] = $add;
|
4796
|
$msg .= "\n** Adding shellcmd package settings **\n";
|
4797
|
}
|
4798
|
}
|
4799
|
}
|
4800
|
else {
|
4801
|
// Remove aliastables archive if found
|
4802
|
if (file_exists("{$pfb['aliasarchive']}")) {
|
4803
|
unlink_if_exists("{$pfb['aliasarchive']}");
|
4804
|
}
|
4805
|
|
4806
|
// Remove earlyshellcmd settings
|
4807
|
if (!empty($a_earlyshellcmd)) {
|
4808
|
if (preg_grep("/pfblockerng.sh aliastables/", $a_earlyshellcmd)) {
|
4809
|
$a_earlyshellcmd = preg_grep("/pfblockerng.sh aliastables/", $a_earlyshellcmd, PREG_GREP_INVERT);
|
4810
|
$msg = "\n** Removing earlyshellcmd settings **\n";
|
4811
|
}
|
4812
|
}
|
4813
|
|
4814
|
// Remove shellcmd package settings
|
4815
|
if (!empty($a_shellcmdsettings)) {
|
4816
|
foreach ($a_shellcmdsettings as $key => $shellcmd) {
|
4817
|
if (strpos($shellcmd['cmd'], 'pfblockerng.sh aliastables') !== FALSE) {
|
4818
|
unset($a_shellcmdsettings[$key]);
|
4819
|
$msg .= "\n** Removing shellcmd package settings**\n";
|
4820
|
}
|
4821
|
}
|
4822
|
}
|
4823
|
}
|
4824
|
|
4825
|
if (!empty($msg)) {
|
4826
|
pfb_logger("{$msg}", 1);
|
4827
|
config_set_path('system/earlyshellcmd', $a_earlyshellcmd);
|
4828
|
config_set_path('installedpackages/shellcmdsettings/config', $a_shellcmdsettings);
|
4829
|
write_config('pfBlockerNG: saving earlyshellcmd');
|
4830
|
}
|
4831
|
}
|
4832
|
|
4833
|
|
4834
|
// Collect pfBlockerNG rule names and Tracker IDs
|
4835
|
function pfb_filterrules() {
|
4836
|
global $pfb;
|
4837
|
|
4838
|
$rule_list = array();
|
4839
|
$rule_list['id'] = array();
|
4840
|
$rule_list['other'] = array();
|
4841
|
$rule_list['int'] = array();
|
4842
|
|
4843
|
exec("{$pfb['pfctl']} -vvsr 2>&1", $results);
|
4844
|
if (!empty($results)) {
|
4845
|
foreach ($results as $result) {
|
4846
|
if (substr($result, 0, 1) == '@') {
|
4847
|
|
4848
|
$type = strstr(ltrim(strstr($result, ' ', FALSE), ' '), ' ', TRUE);
|
4849
|
if (in_array($type, array('block', 'pass', 'match'))) {
|
4850
|
|
4851
|
// Since pfSense CE 2.6 and pfSense Plus 22.01, pf rules use an 'ridentifier' string
|
4852
|
if (strrpos($result, 'ridentifier') !== FALSE) {
|
4853
|
$id_begin_delim = 'ridentifier ';
|
4854
|
$id_end_delim = ' ';
|
4855
|
} elseif (strpos($result, '(') !== FALSE && strpos($result, ')') !== FALSE) {
|
4856
|
$id_begin_delim = '(';
|
4857
|
$id_end_delim = ')';
|
4858
|
} else {
|
4859
|
continue;
|
4860
|
}
|
4861
|
|
4862
|
// Get the rule ID
|
4863
|
$id_begin_offset = strpos($result, $id_begin_delim) + strlen($id_begin_delim);
|
4864
|
$id_end_offset = strpos($result, $id_end_delim, $id_begin_offset);
|
4865
|
|
4866
|
if ($id_end_offset !== FALSE) {
|
4867
|
$id_length = $id_end_offset - $id_begin_offset;
|
4868
|
} else {
|
4869
|
$id_length = strlen($result) - $id_begin_offset;
|
4870
|
}
|
4871
|
$id = substr($result, $id_begin_offset, $id_length);
|
4872
|
if (!is_numeric($id)) {
|
4873
|
continue;
|
4874
|
}
|
4875
|
|
4876
|
// Add the rule to the list
|
4877
|
if (strpos($result, ' <pfB_') !== FALSE) {
|
4878
|
$descr = ltrim(stristr($result, '<pfb_', FALSE), '<');
|
4879
|
$descr = strstr($descr, ':', TRUE);
|
4880
|
$type = strstr(ltrim(strstr($result, ' ', FALSE), ' '), ' ', TRUE);
|
4881
|
if ($type == 'match') {
|
4882
|
$type = 'unkn(%u)';
|
4883
|
}
|
4884
|
|
4885
|
if (!is_array($rule_list[$id])) {
|
4886
|
$rule_list[$id] = array();
|
4887
|
}
|
4888
|
|
4889
|
$rule_list['id'][] = $id;
|
4890
|
$rule_list[$id]['name'] = $descr;
|
4891
|
$rule_list[$id]['type'] = $type;
|
4892
|
|
4893
|
$int = strstr(ltrim(strstr($result, ' on ', FALSE), ' on '), ' ', TRUE);
|
4894
|
if (!empty($int)) {
|
4895
|
$rule_list['int'][$int] = '';
|
4896
|
}
|
4897
|
}
|
4898
|
else {
|
4899
|
// All other non-pfBlockerNG Tracker IDs
|
4900
|
$rule_list['other'][$id] = '';
|
4901
|
}
|
4902
|
}
|
4903
|
}
|
4904
|
}
|
4905
|
}
|
4906
|
return $rule_list;
|
4907
|
}
|
4908
|
|
4909
|
|
4910
|
// Function to remove existing firewall states for IPs that are have been recently added to IP block/reject aliastables
|
4911
|
function pfb_remove_states() {
|
4912
|
global $pfb;
|
4913
|
|
4914
|
$log = "\n===[ Kill States ]==================================================\n";
|
4915
|
pfb_logger("{$log}", 1);
|
4916
|
|
4917
|
$pfb_tables = array();
|
4918
|
// Collect all 'pfB_' and 'pfb_' rules that are 'Block/Reject' and do not have bypass states enabled
|
4919
|
foreach (config_get_path('aliases/alias', []) as $alias) {
|
4920
|
if ($alias['type'] == 'urltable' && strpos($alias['name'], 'pfB_') !== FALSE && strpos($alias['descr'], '[s]') === FALSE) {
|
4921
|
foreach (config_get_path('filter/rule', []) as $rule) {
|
4922
|
if ($alias['name'] === $rule['source']['address'] || $alias['name'] === $rule['destination']['address']) {
|
4923
|
|
4924
|
if ($rule['type'] == 'block' ||
|
4925
|
$rule['type'] == 'reject' ||
|
4926
|
strpos($rule['descr'], '[ks]') !== FALSE) {
|
4927
|
|
4928
|
if (isset($rule['source']['address']) && !isset($rule['source']['not'])) {
|
4929
|
$pfb_tables[] = $rule['source']['address'];
|
4930
|
}
|
4931
|
elseif (isset($rule['destination']['address']) && !isset($rule['destination']['not'])) {
|
4932
|
$pfb_tables[] = $rule['destination']['address'];
|
4933
|
}
|
4934
|
}
|
4935
|
}
|
4936
|
}
|
4937
|
}
|
4938
|
}
|
4939
|
$pfb_tables = array_unique($pfb_tables);
|
4940
|
|
4941
|
// List of IPs to suppress
|
4942
|
$pfb_local = $pfb_localsub = array();
|
4943
|
|
4944
|
// Collect IPv4 Suppression list IPs
|
4945
|
$v4suppression = pfbng_text_area_decode($pfb['ipconfig']['v4suppression'], TRUE, FALSE);
|
4946
|
foreach ($v4suppression as $line) {
|
4947
|
if (strpos($line, '/32') != FALSE) {
|
4948
|
$pfb_local[] = str_replace('/32', '', $line);
|
4949
|
}
|
4950
|
|
4951
|
// Convert '/24' CIDRs
|
4952
|
$pfb_suppcidr = array();
|
4953
|
if (strpos($line, '/24') !== FALSE && is_ipaddrv4($line)) {
|
4954
|
$pfb_suppcidr = subnetv4_expand($line);
|
4955
|
$pfb_local = array_merge($pfb_local, $pfb_suppcidr);
|
4956
|
}
|
4957
|
}
|
4958
|
|
4959
|
if (!empty($pfb_local)) {
|
4960
|
$pfb_local = array_flip($pfb_local);
|
4961
|
}
|
4962
|
|
4963
|
// Collect Interface list that have pfB Rules assigned
|
4964
|
$data = pfb_filterrules();
|
4965
|
$pfb_int = $data['int'] ?: array();
|
4966
|
|
4967
|
// Collect local IPs
|
4968
|
$data = pfb_collect_localip();
|
4969
|
if (!empty($data[0])) {
|
4970
|
$pfb_local = array_merge($pfb_local, $data[0]);
|
4971
|
}
|
4972
|
$pfb_localsub = $data[1] ?: array();
|
4973
|
|
4974
|
// Collect DNS servers to suppress
|
4975
|
$pfb_dnsservers = get_dns_servers();
|
4976
|
if (!empty($pfb_dnsservers)) {
|
4977
|
$pfb_local = array_merge($pfb_local, $pfb_dnsservers);
|
4978
|
}
|
4979
|
|
4980
|
// Remove any duplicate IPs
|
4981
|
if (!empty($pfb_local)) {
|
4982
|
$pfb_local = array_flip(array_unique($pfb_local));
|
4983
|
}
|
4984
|
|
4985
|
// Collect any 'Permit' Customlist IPs to suppress
|
4986
|
$custom_supp = array();
|
4987
|
foreach (array('pfblockernglistsv4', 'pfblockernglistsv6') as $ip_type) {
|
4988
|
foreach (config_get_path("installedpackages{$ip_type}/config", []) as $list) {
|
4989
|
if (!empty($list['custom']) && strpos($list['action'], 'Permit_') !== FALSE) {
|
4990
|
$custom = pfbng_text_area_decode($list['custom'], TRUE, FALSE);
|
4991
|
$custom_supp = array_merge($custom_supp, $custom);
|
4992
|
}
|
4993
|
}
|
4994
|
}
|
4995
|
|
4996
|
$custom_supp = array_unique(array_filter($custom_supp));
|
4997
|
// Append '/32' CIDR as required
|
4998
|
foreach ($custom_supp as &$custom) {
|
4999
|
if (strpos($custom, '/') === FALSE) {
|
5000
|
$custom = $custom . '/32';
|
5001
|
}
|
5002
|
}
|
5003
|
|
5004
|
// Collect firewall states and save to temp file
|
5005
|
exec("{$pfb['pfctl']} -s state > {$pfb['states_tmp']} 2>&1");
|
5006
|
|
5007
|
$state_count = $all_states = 0;
|
5008
|
$states = array();
|
5009
|
$states[4] = array();
|
5010
|
$states[6] = array();
|
5011
|
|
5012
|
if (($s_handle = @fopen("{$pfb['states_tmp']}", 'r')) !== FALSE) {
|
5013
|
while (($sline = @fgets($s_handle)) !== FALSE) {
|
5014
|
|
5015
|
$all_states++;
|
5016
|
|
5017
|
// SAMPLE : em0 udp 93.15.36.22:6881 -> 192.168.0.3:681 MULTIPLE:MULTIPLE
|
5018
|
// SAMPLE : pppoe0 udp 35.170.3.40:57197 (192.168.0.45:681) -> 22.41.123.206:1001 MULTIPLE:MULTIPLE
|
5019
|
// SAMPLE : em0 tcp 2001:65c:1398:101:124[443] <- 2001:170:2f:3e:a4c4:7b23:fe5f:b36e[52725] FIN_WAIT_2:FIN_WAIT_2
|
5020
|
|
5021
|
if (!empty($sline)) {
|
5022
|
|
5023
|
$detail = array_filter(explode(' ', $sline));
|
5024
|
|
5025
|
// Validate states for pfB Interfaces only
|
5026
|
if (!isset($pfb_int[$detail[0]])) {
|
5027
|
continue;
|
5028
|
}
|
5029
|
|
5030
|
$count = count($detail);
|
5031
|
if ($count == 6) {
|
5032
|
$orig_s_ip = $detail[2];
|
5033
|
}
|
5034
|
elseif ($count == 7) {
|
5035
|
$orig_s_ip = $detail[5];
|
5036
|
}
|
5037
|
else {
|
5038
|
continue; // Unknown state line
|
5039
|
}
|
5040
|
}
|
5041
|
else {
|
5042
|
continue;
|
5043
|
}
|
5044
|
|
5045
|
$ip_version = 4;
|
5046
|
|
5047
|
// Strip IPv6 port
|
5048
|
if (strpos($orig_s_ip, '[') !== FALSE) {
|
5049
|
list($s_ip, $s_port) = explode('[', $orig_s_ip);
|
5050
|
$ip_version = 6;
|
5051
|
}
|
5052
|
|
5053
|
// Strip IPv4 port
|
5054
|
elseif (strpos($orig_s_ip, ':') !== FALSE && substr_count($orig_s_ip, ':') == 1) {
|
5055
|
list($s_ip, $s_port) = explode(':', $orig_s_ip);
|
5056
|
$ip_version = 4;
|
5057
|
}
|
5058
|
|
5059
|
// No port listed
|
5060
|
else {
|
5061
|
$s_ip = $orig_s_ip;
|
5062
|
$s_port = '';
|
5063
|
|
5064
|
if (is_ipaddrv6($s_ip)) {
|
5065
|
$ip_version = 6;
|
5066
|
}
|
5067
|
}
|
5068
|
|
5069
|
// Exclude local and reserved IPs (Validate unique IPs only once)
|
5070
|
if (!isset($states[$ip_version][$s_ip])) {
|
5071
|
|
5072
|
if ($ip_version == 4) {
|
5073
|
if (isset($pfb_local[$s_ip]) ||
|
5074
|
pfb_local_ip($s_ip, $pfb_localsub) ||
|
5075
|
is_private_ip($s_ip) ||
|
5076
|
substr($s_ip, 0, 2) == '0.' ||
|
5077
|
substr($s_ip, 0, 4) == '127.' ||
|
5078
|
substr($s_ip, 0, 3) >= 224) {
|
5079
|
continue;
|
5080
|
}
|
5081
|
}
|
5082
|
else {
|
5083
|
if (isset($pfb_local[$s_ip]) ||
|
5084
|
pfb_local_ip($s_ip, $pfb_localsub) ||
|
5085
|
!filter_var($s_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) ||
|
5086
|
!filter_var($s_ip, FILTER_CALLBACK, array('options' => 'FILTER_FLAG_NO_LOOPBACK_RANGE'))) {
|
5087
|
continue;
|
5088
|
}
|
5089
|
}
|
5090
|
|
5091
|
// Exclude any 'Permit' Customlist IPs
|
5092
|
foreach ($custom_supp as $custom) {
|
5093
|
if (ip_in_subnet($s_ip, $custom)) {
|
5094
|
continue;
|
5095
|
}
|
5096
|
}
|
5097
|
}
|
5098
|
|
5099
|
$state = rtrim($sline, "\x00..\x1F");
|
5100
|
$state_count++;
|
5101
|
|
5102
|
// Collect IP for state removal verification
|
5103
|
if (!is_array($states[$ip_version][$s_ip])) {
|
5104
|
$states[$ip_version][$s_ip] = array();
|
5105
|
}
|
5106
|
|
5107
|
if (!in_array($state, $states[$ip_version][$s_ip])) {
|
5108
|
$states[$ip_version][$s_ip][] = $state;
|
5109
|
}
|
5110
|
}
|
5111
|
}
|
5112
|
else {
|
5113
|
pfb_logger("\n No Firewall States found", 1);
|
5114
|
}
|
5115
|
|
5116
|
if ($s_handle) {
|
5117
|
@fclose($s_handle);
|
5118
|
}
|
5119
|
unlink_if_exists("{$pfb['states_tmp']}");
|
5120
|
unset($pfb_local, $pfb_localsub, $custom_supp);
|
5121
|
|
5122
|
$pfbfound = FALSE;
|
5123
|
foreach ($states as $ip_version => $details) {
|
5124
|
|
5125
|
if (!empty($details)) {
|
5126
|
$log = "\nFirewall state(s) validation for [ " . count($details) . " ] IPv{$ip_version} address(es)...";
|
5127
|
pfb_logger("{$log}", 1);
|
5128
|
ksort($details, SORT_NATURAL);
|
5129
|
}
|
5130
|
|
5131
|
foreach ($details as $s_ip => $state) {
|
5132
|
foreach ($pfb_tables as $s_table) {
|
5133
|
|
5134
|
// Compare IP version and aliastable type
|
5135
|
if ($ip_version == 4 && strpos($s_table, '_v4') !== FALSE ||
|
5136
|
$ip_version == 6 && strpos($s_table, '_v6') !== FALSE) {
|
5137
|
|
5138
|
$s_table_esc = escapeshellarg($s_table);
|
5139
|
$s_ip_esc = escapeshellarg($s_ip);
|
5140
|
$result = substr(exec("{$pfb['pfctl']} -t {$s_table_esc} -T test {$s_ip_esc} 2>&1"), 0, 1);
|
5141
|
if ($result > 0) {
|
5142
|
|
5143
|
$pfbfound = TRUE;
|
5144
|
$log = "\n\n\t[ {$s_table} ] Removed " . count($state) . " state(s) for [ {$s_ip} ]\n\n";
|
5145
|
pfb_logger("{$log}", 1);
|
5146
|
|
5147
|
foreach ($state as $line) {
|
5148
|
pfb_logger("\t\t{$line}\n", 1);
|
5149
|
}
|
5150
|
|
5151
|
// Kill all state entries originating from $s_ip
|
5152
|
exec("{$pfb['pfctl']} -k {$s_ip_esc} 2>&1");
|
5153
|
|
5154
|
// Kill all state entries to the target $s_ip
|
5155
|
exec("{$pfb['pfctl']} -k 0.0.0.0/0 -k {$s_ip_esc} 2>&1");
|
5156
|
|
5157
|
break;
|
5158
|
}
|
5159
|
}
|
5160
|
}
|
5161
|
}
|
5162
|
}
|
5163
|
unset($states, $pfb_tables);
|
5164
|
|
5165
|
if ($pfbfound) {
|
5166
|
pfb_logger("\n======================================================================\n", 1);
|
5167
|
} else {
|
5168
|
pfb_logger("\nNo matching states found\n\n======================================================================\n", 1);
|
5169
|
}
|
5170
|
}
|
5171
|
|
5172
|
|
5173
|
// For subnet addresses - Determine if alert host 'dest' is within a local IP range.
|
5174
|
function pfb_local_ip($subnet, $pfb_localsub) {
|
5175
|
|
5176
|
if (!empty($pfb_localsub)) {
|
5177
|
foreach ($pfb_localsub as $line) {
|
5178
|
if (ip_in_subnet($subnet, $line)) {
|
5179
|
return TRUE;
|
5180
|
}
|
5181
|
}
|
5182
|
}
|
5183
|
return FALSE;
|
5184
|
}
|
5185
|
|
5186
|
|
5187
|
// Collect local IP addresses
|
5188
|
function pfb_collect_localip() {
|
5189
|
$pfb_local = $pfb_localsub = array();
|
5190
|
|
5191
|
// Collect gateway IP addresses for inbound/outbound list matching
|
5192
|
$int_gateway = get_interfaces_with_gateway();
|
5193
|
if (isset($int_gateway)) {
|
5194
|
foreach ($int_gateway as $gateway) {
|
5195
|
$pfb_local[] = get_interface_ip($gateway) ?: 'Disabled';
|
5196
|
}
|
5197
|
}
|
5198
|
|
5199
|
// Collect virtual IP aliases for inbound/outbound list matching
|
5200
|
foreach (config_get_path('virtualip/vip', []) as $list) {
|
5201
|
if (!empty($list['subnet']) && !empty($list['subnet_bits']) && is_subnet("{$list['subnet']}/{$list['subnet_bits']}")) {
|
5202
|
if (is_ipaddrv4($list['subnet'])) {
|
5203
|
if ($list['subnet_bits'] >= 24) {
|
5204
|
$pfb_local = array_merge(subnetv4_expand("{$list['subnet']}/{$list['subnet_bits']}"), $pfb_local);
|
5205
|
} else {
|
5206
|
$pfb_localsub[] = "{$list['subnet']}/{$list['subnet_bits']}";
|
5207
|
}
|
5208
|
}
|
5209
|
elseif (is_ipaddrv6($list['subnet'])) {
|
5210
|
$pfb_localsub[] = gen_subnetv6("{$list['subnet']}", "{$list['subnet_bits']}");
|
5211
|
}
|
5212
|
}
|
5213
|
elseif (is_ipaddr($list['subnet'])) {
|
5214
|
$pfb_local[] = "{$list['subnet']}";
|
5215
|
}
|
5216
|
}
|
5217
|
|
5218
|
// Collect NAT IP addresses for inbound/outbound list matching
|
5219
|
foreach (config_get_path('nat/rule', []) as $natent) {
|
5220
|
$pfb_local[] = $natent['target'];
|
5221
|
}
|
5222
|
|
5223
|
// Collect 1:1 NAT IP addresses for inbound/outbound list matching
|
5224
|
foreach (config_get_path('nat/onetoone', []) as $onetoone) {
|
5225
|
$pfb_local[] = $onetoone['source']['address'];
|
5226
|
}
|
5227
|
|
5228
|
// Convert any 'Firewall Aliases' to IP address format
|
5229
|
$aliases = config_get_path('aliases/alias', []);
|
5230
|
if (is_array($aliases)) {
|
5231
|
for ($cnt = 0; $cnt <= count($pfb_local); $cnt++) {
|
5232
|
foreach ($aliases as $i=> $alias) {
|
5233
|
if (isset($alias['name']) && isset($pfb_local[$cnt])) {
|
5234
|
if ($alias['name'] == $pfb_local[$cnt]) {
|
5235
|
$pfb_local[$cnt] = $alias['address'];
|
5236
|
}
|
5237
|
}
|
5238
|
}
|
5239
|
}
|
5240
|
}
|
5241
|
|
5242
|
// Collect all interface addresses for inbound/outbound list matching
|
5243
|
foreach (config_get_path('interfaces', []) as $int) {
|
5244
|
if ($int['ipaddr'] != 'dhcp') {
|
5245
|
if (!empty($int['ipaddr']) && !empty($int['subnet']) && is_subnet("{$int['ipaddr']}/{$int['subnet']}")) {
|
5246
|
if (is_ipaddrv4($int['ipaddr'])) {
|
5247
|
if ($int['subnet'] >= 24) {
|
5248
|
$pfb_local = array_merge(subnetv4_expand("{$int['ipaddr']}/{$int['subnet']}"), $pfb_local);
|
5249
|
} else {
|
5250
|
$pfb_localsub[] = "{$int['ipaddr']}/{$int['subnet']}";
|
5251
|
}
|
5252
|
}
|
5253
|
elseif (is_ipaddrv6($int['ipaddr'])) {
|
5254
|
$pfb_localsub[] = gen_subnetv6("{$int['ipaddr']}", "{$int['subnet']}");
|
5255
|
}
|
5256
|
}
|
5257
|
elseif (is_ipaddr($int['ipaddr'])) {
|
5258
|
$pfb_local[] = "{$int['ipaddr']}";
|
5259
|
}
|
5260
|
}
|
5261
|
}
|
5262
|
|
5263
|
// Remove any duplicate IPs
|
5264
|
if (!empty($pfb_local)) {
|
5265
|
$pfb_local = array_flip(array_filter(array_unique($pfb_local)));
|
5266
|
}
|
5267
|
$pfb_localsub = array_unique($pfb_localsub);
|
5268
|
|
5269
|
return array($pfb_local, $pfb_localsub);
|
5270
|
}
|
5271
|
|
5272
|
|
5273
|
// Collect local hostnames
|
5274
|
function pfb_collect_localhosts() {
|
5275
|
global $g, $pfb;
|
5276
|
|
5277
|
// Collect DHCP hostnames/IPs
|
5278
|
$local_hosts = array();
|
5279
|
|
5280
|
// Collect configured pfSense interfaces
|
5281
|
$pf_int = get_configured_ip_addresses();
|
5282
|
if (isset($pf_int)) {
|
5283
|
$local_hosts = array_merge($local_hosts, array_flip(array_filter($pf_int)));
|
5284
|
}
|
5285
|
$pf_int = get_configured_ipv6_addresses();
|
5286
|
if (isset($pf_int)) {
|
5287
|
$local_hosts = array_merge($local_hosts, array_flip(array_filter($pf_int)));
|
5288
|
}
|
5289
|
|
5290
|
// Collect dynamic DHCP hostnames/IPs
|
5291
|
$leasesfile = "{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases";
|
5292
|
|
5293
|
$end = $hostname = '';
|
5294
|
if (file_exists("{$leasesfile}")) {
|
5295
|
if (($l_handle = @fopen("{$leasesfile}", 'r')) !== FALSE) {
|
5296
|
while (($line = @fgets($l_handle)) !== FALSE) {
|
5297
|
if (strpos($line, '{') !== FALSE) {
|
5298
|
$end = FALSE;
|
5299
|
$data = explode(' ', $line);
|
5300
|
$ip = $data[1];
|
5301
|
}
|
5302
|
if (strpos($line, 'client-hostname') !== FALSE) {
|
5303
|
$data = explode(' ', $line);
|
5304
|
$hostname = trim(str_replace(array('"', ';'), '', $data[3]));
|
5305
|
}
|
5306
|
if (strpos($line, '}') !== FALSE) {
|
5307
|
$end = TRUE;
|
5308
|
}
|
5309
|
if ($end) {
|
5310
|
if (!empty($ip)) {
|
5311
|
$ip = pfb_filter($ip, PFB_FILTER_IP, 'Collect dynamic DHCP hostnames');
|
5312
|
}
|
5313
|
|
5314
|
if (!empty($hostname)) {
|
5315
|
$hostname = pfb_filter($hostname, PFB_FILTER_HOSTNAME, 'Collect dynamic DHCP hostnames');
|
5316
|
}
|
5317
|
|
5318
|
if (!empty($ip) && !empty($hostname)) {
|
5319
|
$local_hosts[$ip] = $hostname;
|
5320
|
}
|
5321
|
$ip = $hostname = '';
|
5322
|
}
|
5323
|
}
|
5324
|
}
|
5325
|
}
|
5326
|
|
5327
|
// Collect static DHCP hostnames/IPs
|
5328
|
foreach (config_get_path('dhcpd', []) as $dhcp) {
|
5329
|
if (isset($dhcp['staticmap']) && is_array($dhcp['staticmap'])) {
|
5330
|
foreach ($dhcp['staticmap'] as $smap) {
|
5331
|
$local_hosts[$smap['ipaddr']] = strtolower("{$smap['hostname']}");
|
5332
|
}
|
5333
|
}
|
5334
|
}
|
5335
|
|
5336
|
// Collect static DHCPv6 hostnames/IPs
|
5337
|
foreach (config_get_path('dhcpdv6', []) as $dhcpv6) {
|
5338
|
if (isset($dhcpv6['staticmap']) && is_array($dhcpv6['staticmap'])) {
|
5339
|
foreach ($dhcpv6['staticmap'] as $smap) {
|
5340
|
$local_hosts[$smap['ipaddrv6']] = strtolower("{$smap['hostname']}");
|
5341
|
}
|
5342
|
}
|
5343
|
}
|
5344
|
|
5345
|
// Collect Unbound Host overrides
|
5346
|
$hosts = config_get_path('unbound/hosts', []);
|
5347
|
foreach ($hosts as $host) {
|
5348
|
$local_hosts[$host['ip']] = strtolower("{$host['descr']}");
|
5349
|
}
|
5350
|
|
5351
|
// Collect NAT IP addresses by Target:Port
|
5352
|
foreach (config_get_path('nat/rule', []) as $natent) {
|
5353
|
$local_hosts["{$natent['target']}:{$natent['local-port']}"] = strtolower("{$natent['descr']}");
|
5354
|
}
|
5355
|
|
5356
|
// Collect virtual IP aliases
|
5357
|
foreach (config_get_path('virtualip/vip', []) as $list) {
|
5358
|
if (!empty($list['subnet']) && !empty($list['subnet_bits'])) {
|
5359
|
|
5360
|
// Use pfSense hostname for DNSBL vip
|
5361
|
if ($list['subnet'] == $pfb['dnsbl_vip']) {
|
5362
|
$list['descr'] = config_get_path('system/hostname', 'pfSense') . '.' . (config_get_path('system/domain', 'localdomain'));
|
5363
|
}
|
5364
|
$local_hosts[$list['subnet']] = strtolower("{$list['descr']}");
|
5365
|
}
|
5366
|
}
|
5367
|
|
5368
|
// Add localhost hostname
|
5369
|
if (!isset($local_hosts['127.0.0.1'])) {
|
5370
|
$local_hosts['127.0.0.1'] = strtolower((config_get_path('system/hostname', 'pfSense')) . '.' . (config_get_path('system/domain', 'localdomain')));
|
5371
|
}
|
5372
|
|
5373
|
return $local_hosts;
|
5374
|
}
|
5375
|
|
5376
|
|
5377
|
// Firewall filter.log parser daemon
|
5378
|
function pfb_daemon_filterlog() {
|
5379
|
global $pfb;
|
5380
|
|
5381
|
// ASN Reporting - cached setting
|
5382
|
if ($pfb['asn_reporting'] != 'disabled') {
|
5383
|
switch ($pfb['asn_reporting']) {
|
5384
|
case '1hour':
|
5385
|
$asn_cache = '-1 hour';
|
5386
|
break;
|
5387
|
case '4hour':
|
5388
|
$asn_cache = '-4 hours';
|
5389
|
break;
|
5390
|
case '12hour':
|
5391
|
$asn_cache = '-12 hours';
|
5392
|
break;
|
5393
|
case '24hour':
|
5394
|
$asn_cache = '-24 hours';
|
5395
|
break;
|
5396
|
case 'week':
|
5397
|
$asn_cache = '-1 week';
|
5398
|
break;
|
5399
|
default:
|
5400
|
$asn_cache = '-24 hours';
|
5401
|
}
|
5402
|
}
|
5403
|
|
5404
|
// Application paths
|
5405
|
if (file_exists('/usr/local/bin/mmdblookup') && file_exists("{$pfb['geoipshare']}/GeoLite2-Country.mmdb")) {
|
5406
|
$pathgeoip = "/usr/local/bin/mmdblookup -f {$pfb['geoipshare']}/GeoLite2-Country.mmdb -i";
|
5407
|
} else {
|
5408
|
$pathgeoip = '';
|
5409
|
}
|
5410
|
|
5411
|
// Proofpoint ET IQRisk header name reference
|
5412
|
$et_header = config_get_path('installedpackages/pfblockerngreputation/config/0/et_header', '');
|
5413
|
$et_enabled = TRUE;
|
5414
|
if (empty($et_header)) {
|
5415
|
$et_enabled = FALSE;
|
5416
|
}
|
5417
|
|
5418
|
$p_entry = '';
|
5419
|
$line_cnt = 0;
|
5420
|
$rule_list = $rule_list['other'] = array();
|
5421
|
|
5422
|
if (file_exists('/usr/local/sbin/clog_pfb')) {
|
5423
|
|
5424
|
// Parse full filter.log on first run, otherwise only parse new filter.log events
|
5425
|
if (!file_exists($pfb['ip_blocklog']) && !file_exists($pfb['ip_permitlog']) && !file_exists($pfb['ip_matchlog'])) {
|
5426
|
$filter_cnt = 0;
|
5427
|
} else {
|
5428
|
$filter_cnt = max( exec("{$pfb['grep']} -c ^ /var/log/filter.log 2>&1") -1, 0) ?: 0;
|
5429
|
}
|
5430
|
$skip_cnt = $filter_cnt;
|
5431
|
}
|
5432
|
else {
|
5433
|
$skip_cnt = $filter_cnt = 0;
|
5434
|
}
|
5435
|
|
5436
|
/* filter.log reference: URL: https://docs.netgate.com/pfsense/en/latest/monitoring/logs/raw-filter-format.html
|
5437
|
|
5438
|
$line -> $f
|
5439
|
|
5440
|
[ BSD format ] [ syslog format ]
|
5441
|
[0][1][2] [1] = Date/timestamp
|
5442
|
[5] [7] = Event Details
|
5443
|
|
5444
|
$f[5] -> $d
|
5445
|
|
5446
|
[0] = Rule number
|
5447
|
[1] = Sub-rule number
|
5448
|
[2] = Anchor
|
5449
|
[3] = Tracker ID
|
5450
|
[4] = Real Interface
|
5451
|
[5] = Reason
|
5452
|
[6] = Action
|
5453
|
[7] = Direction
|
5454
|
[8] = IP version
|
5455
|
[9] = IP Specific data
|
5456
|
|
5457
|
IPv4 IPv6
|
5458
|
[10] [] =
|
5459
|
[11] [] =
|
5460
|
[12] [] =
|
5461
|
[13] [] =
|
5462
|
[14] [] =
|
5463
|
[15] [13] = Protocol ID
|
5464
|
[16] [12] = Protocol
|
5465
|
[17] [] =
|
5466
|
[18] [15] = SRC IP
|
5467
|
[19] [16] = DST IP
|
5468
|
[20] [17] = SRC Port
|
5469
|
[21] [18] = DST Port
|
5470
|
[22] [] =
|
5471
|
[23] [20] = TCP Protocol Flags
|
5472
|
|
5473
|
Final output reference:
|
5474
|
[0] = Date/Timestamp
|
5475
|
[1] = Rulenum
|
5476
|
[2] = Real Interface
|
5477
|
[3] = Friendly Interface name
|
5478
|
[4] = Action
|
5479
|
[5] = Version
|
5480
|
[6] = Protocol ID
|
5481
|
[7] = Protocol
|
5482
|
[8] = SRC IP
|
5483
|
[9] = DST IP
|
5484
|
[10] = SRC Port
|
5485
|
[11] = DST Port
|
5486
|
[12] = Direction
|
5487
|
[13] = GeoIP code
|
5488
|
[14] = IP Alias Name
|
5489
|
[15] = IP evaluated
|
5490
|
[16] = Feed Name
|
5491
|
[17] = gethostbyaddr resolved hostname
|
5492
|
[18] = Client Hostname
|
5493
|
[19] = Duplicate ID indicator */
|
5494
|
|
5495
|
// Disable pfctl Tracker ID lookup on too many lookup failures
|
5496
|
$max_retries = 0;
|
5497
|
|
5498
|
if (($s_handle = @fopen('php://stdin', 'r')) !== FALSE) {
|
5499
|
syslog(LOG_NOTICE, '[pfBlockerNG] filterlog daemon started');
|
5500
|
while (!feof($s_handle)) {
|
5501
|
$line = @fgets($s_handle);
|
5502
|
|
5503
|
// Only parse new filter events
|
5504
|
if ($filter_cnt > 0 && $skip_cnt > 0) {
|
5505
|
$skip_cnt--;
|
5506
|
continue;
|
5507
|
}
|
5508
|
|
5509
|
$log_type = 'BSD';
|
5510
|
$f_pos = 5;
|
5511
|
if (substr($line, 0, 1) == '<') {
|
5512
|
$log_type = 'syslog';
|
5513
|
$f_pos = 7;
|
5514
|
}
|
5515
|
|
5516
|
// Remove any '^M' characters
|
5517
|
$line = htmlspecialchars(rtrim($line, "\x00..\x1F"));
|
5518
|
|
5519
|
$f = explode(' ', $line);
|
5520
|
|
5521
|
// Remove double space for single date entry nuance
|
5522
|
if ($log_type == 'BSD' && empty($f[1])) {
|
5523
|
array_splice($f, 1, 1);
|
5524
|
}
|
5525
|
$d = explode(',', $f[$f_pos]);
|
5526
|
|
5527
|
// Attempt to find the Tracker ID, if not found wait 5secs for filter_reload
|
5528
|
$other = FALSE;
|
5529
|
if (!empty($d[3])) {
|
5530
|
for ($retries = 1; $retries <= 5; $retries++) {
|
5531
|
|
5532
|
// Skip known non-pfBlockerNG Tracker IDs
|
5533
|
if (isset($rule_list['other'][$d[3]])) {
|
5534
|
$other = TRUE;
|
5535
|
break;
|
5536
|
}
|
5537
|
|
5538
|
// Break on pfBlockerNG rule Tracker ID and Rule type (block|permit|match)
|
5539
|
// If the user switched a manual rule from one type to another, the Tracker ID will stay the same
|
5540
|
// So this comparison will refresh the data on changes
|
5541
|
if (isset($rule_list[$d[3]]) && $rule_list[$d[3]]['type'] == $d[6]) {
|
5542
|
break;
|
5543
|
}
|
5544
|
|
5545
|
// Collect updated pfctl Rule data, Host and local IP data
|
5546
|
else {
|
5547
|
$rule_list = pfb_filterrules();
|
5548
|
$data = pfb_collect_localip();
|
5549
|
$pfb_local = $data[0] ?: array();
|
5550
|
$pfb_localsub = $data[1] ?: array();
|
5551
|
|
5552
|
$local_hosts = pfb_collect_localhosts();
|
5553
|
}
|
5554
|
|
5555
|
if ($retries < 5 && $max_retries < 30) {
|
5556
|
$max_retries++;
|
5557
|
sleep(5);
|
5558
|
} else {
|
5559
|
$other = TRUE;
|
5560
|
}
|
5561
|
}
|
5562
|
|
5563
|
if ($other) {
|
5564
|
continue;
|
5565
|
}
|
5566
|
|
5567
|
// Duplicate entry comparison: "Tracker ID/Action/SRC IP/DST IP/DST Port"
|
5568
|
$dup_entry = '+';
|
5569
|
if ($d[8] == 4 && "{$d[3]}{$d[6]}{$d[18]}{$d[19]}{$d[21]}" == $p_entry) {
|
5570
|
$dup_entry = '-';
|
5571
|
} elseif ($d[8] == 6 && "{$d[3]}{$d[6]}{$d[15]}{$d[16]}{$d[18]}" == $p_entry) {
|
5572
|
$dup_entry = '-';
|
5573
|
}
|
5574
|
|
5575
|
if ($dup_entry == '+') {
|
5576
|
|
5577
|
$int = convert_real_interface_to_friendly_descr($d[4]);
|
5578
|
$pfb_alias = pfb_filter($rule_list[$d[3]]['name'], PFB_FILTER_WORD, 'pfb_daemon_filterlog', 'Unknown');
|
5579
|
|
5580
|
// Action setting variables
|
5581
|
if ($d[6] == 'block') {
|
5582
|
$folder = "{$pfb['denydir']}/* {$pfb['nativedir']}/*";
|
5583
|
$iplog = "{$pfb['ip_blocklog']}";
|
5584
|
$l_type = 'Block';
|
5585
|
}
|
5586
|
elseif ($d[6] == 'pass') {
|
5587
|
$folder = "{$pfb['permitdir']}/* {$pfb['nativedir']}/*";
|
5588
|
$iplog = "{$pfb['ip_permitlog']}";
|
5589
|
$l_type = 'Permit';
|
5590
|
}
|
5591
|
elseif ($d[6] == 'unkn(%u)') {
|
5592
|
$d[6] = 'match';
|
5593
|
$folder = "{$pfb['matchdir']}/* {$pfb['nativedir']}/*";
|
5594
|
$iplog = "{$pfb['ip_matchlog']}";
|
5595
|
$l_type = 'Match';
|
5596
|
}
|
5597
|
|
5598
|
if ($d[8] == 4) {
|
5599
|
$srcip = $d[18] ?: 'Unknown';
|
5600
|
$dstip = $d[19] ?: 'Unknown';
|
5601
|
$tcp_flags = $d[16] == 'tcp' ? $d[23] : ''; // Keep protocol flags for TCP only
|
5602
|
} else {
|
5603
|
$srcip = $d[15] ?: 'Unknown';
|
5604
|
$dstip = $d[16] ?: 'Unknown';
|
5605
|
$tcp_flags = $d[12] == 'tcp' ? $d[20] : '';
|
5606
|
}
|
5607
|
|
5608
|
// Determine if DST IP or SRC IP is the external host
|
5609
|
if (isset($pfb_local[$dstip]) || pfb_local_ip($dstip, $pfb_localsub)) {
|
5610
|
$dir = 'in';
|
5611
|
$host = $srcip;
|
5612
|
$client = $dstip;
|
5613
|
$port = $d[21];
|
5614
|
} else {
|
5615
|
$dir = 'out';
|
5616
|
$host = $dstip;
|
5617
|
$client = $srcip;
|
5618
|
$port = $d[20];
|
5619
|
}
|
5620
|
|
5621
|
if (!is_ipaddr($host)) {
|
5622
|
continue;
|
5623
|
}
|
5624
|
if (!is_ipaddr($client)) {
|
5625
|
continue;
|
5626
|
}
|
5627
|
if (!is_port($port)) {
|
5628
|
$port = '';
|
5629
|
}
|
5630
|
|
5631
|
switch (TRUE) {
|
5632
|
case isset($local_hosts["{$client}:{$port}"]) && !empty($local_hosts["{$client}:{$port}"]):
|
5633
|
$hostname = $local_hosts["{$client}:{$port}"];
|
5634
|
break;
|
5635
|
case isset($local_hosts[$client]) && !empty($local_hosts[$client]):
|
5636
|
$hostname = $local_hosts[$client];
|
5637
|
break;
|
5638
|
default:
|
5639
|
$hostname = 'Unknown';
|
5640
|
}
|
5641
|
|
5642
|
$ip_cache = FALSE;
|
5643
|
$db_handle = pfb_open_sqlite(7, 'Query ip cache');
|
5644
|
if ($db_handle) {
|
5645
|
$db_update = "SELECT * FROM ipcache WHERE host = :host;";
|
5646
|
$stmt = $db_handle->prepare($db_update);
|
5647
|
if ($stmt) {
|
5648
|
$stmt->bindValue(':host', $host, SQLITE3_TEXT);
|
5649
|
$result = $stmt->execute();
|
5650
|
if ($result) {
|
5651
|
$ip_cache = $result->fetchArray(SQLITE3_ASSOC);
|
5652
|
}
|
5653
|
}
|
5654
|
}
|
5655
|
pfb_close_sqlite($db_handle);
|
5656
|
|
5657
|
if (!$ip_cache) {
|
5658
|
|
5659
|
// Find the header which alerted this host
|
5660
|
$geoip_folder = FALSE;
|
5661
|
$geoip_validate = substr($pfb_alias, 0, -3);
|
5662
|
if (isset($pfb['continent_list'][$geoip_validate])) {
|
5663
|
$geoip_folder = TRUE;
|
5664
|
}
|
5665
|
$pfb_query = find_reported_header($host, $folder, $geoip_folder);
|
5666
|
|
5667
|
// Report specific ET IQRisk details
|
5668
|
if ($et_enabled && strpos($pfb_query[0], "{$et_header}") !== FALSE) {
|
5669
|
$ET_orig = $pfb_query;
|
5670
|
$pfb_query = find_reported_header($host, "{$pfb['etdir']}/*", FALSE);
|
5671
|
|
5672
|
// ET IQRisk category is unknown.
|
5673
|
if ($pfb_query[1] == 'Unknown') {
|
5674
|
$pfb_query = $ET_orig;
|
5675
|
}
|
5676
|
else {
|
5677
|
// Prepend ET Header name
|
5678
|
$pfb_query[0] = "{$et_header}:{$pfb_query[0]}";
|
5679
|
}
|
5680
|
}
|
5681
|
|
5682
|
// Determine GeoIP isocode of host
|
5683
|
if (!empty($pathgeoip)) {
|
5684
|
$geoip = exec("{$pathgeoip} " . escapeshellarg($host) . " country iso_code | grep -v '^\$' | cut -d '\"' -f2 2>&1");
|
5685
|
$geoip = pfb_filter($geoip, PFB_FILTER_ALPHA, 'pfb_daemon_filterlog', 'Unk');
|
5686
|
}
|
5687
|
else {
|
5688
|
$geoip = 'Unk';
|
5689
|
}
|
5690
|
|
5691
|
$resolved_host = gethostbyaddr($host) ?: 'Unknown';
|
5692
|
if ($host == $resolved_host || $resolved_host == 'Unknown') {
|
5693
|
$resolved_host = 'Unknown';
|
5694
|
}
|
5695
|
|
5696
|
// Save entry to IP cache
|
5697
|
$db_update = "INSERT into ipcache ( host, q0, q1, geoip, resolved_host ) VALUES ( :host, :q0, :q1, :geoip, :resolved_host )";
|
5698
|
$db_handle = pfb_open_sqlite(7, 'Add to IP cache');
|
5699
|
if ($db_handle) {
|
5700
|
$pfb_host = pfb_filter($host, PFB_FILTER_HTML, 'pfb_daemon_filterlog');
|
5701
|
$pfb_query[0] = pfb_filter($pfb_query[0], PFB_FILTER_HTML, 'pfb_daemon_filterlog');
|
5702
|
$pfb_query[1] = pfb_filter($pfb_query[1], PFB_FILTER_HTML, 'pfb_daemon_filterlog');
|
5703
|
$geoip = pfb_filter($geoip, PFB_FILTER_HTML, 'pfb_daemon_filterlog');
|
5704
|
$resolved_host = pfb_filter($resolved_host, PFB_FILTER_HOSTNAME, 'pfb_daemon_filterlog', 'Unknown');
|
5705
|
|
5706
|
$stmt = $db_handle->prepare($db_update);
|
5707
|
if ($stmt) {
|
5708
|
$stmt->bindValue(':host', $host, SQLITE3_TEXT);
|
5709
|
$stmt->bindValue(':q0', $pfb_query[0], SQLITE3_TEXT);
|
5710
|
$stmt->bindValue(':q1', $pfb_query[1], SQLITE3_TEXT);
|
5711
|
$stmt->bindValue(':geoip', $geoip, SQLITE3_TEXT);
|
5712
|
$stmt->bindValue(':resolved_host', $resolved_host, SQLITE3_TEXT);
|
5713
|
$stmt->execute();
|
5714
|
}
|
5715
|
}
|
5716
|
pfb_close_sqlite($db_handle);
|
5717
|
}
|
5718
|
|
5719
|
// Use cached entries
|
5720
|
else {
|
5721
|
$host = htmlspecialchars($ip_cache['host']) ?: 'Unknown';
|
5722
|
$pfb_query = array();
|
5723
|
$pfb_query[0] = htmlspecialchars($ip_cache['q0']) ?: 'Unknown';
|
5724
|
$pfb_query[1] = htmlspecialchars($ip_cache['q1']) ?: 'Unknown';
|
5725
|
$geoip = htmlspecialchars($ip_cache['geoip']) ?: 'Unknown';
|
5726
|
$resolved_host = htmlspecialchars($ip_cache['resolved_host']) ?: 'Unknown';
|
5727
|
}
|
5728
|
|
5729
|
// Modify host for ASN query
|
5730
|
if ($d[8] == 4) {
|
5731
|
// For ASN, query for IP Network address (x.x.x.0)
|
5732
|
$ix = ip_explode($host);
|
5733
|
$ip = "{$ix['6']}0";
|
5734
|
} else {
|
5735
|
// For ASN, query for IP Network address with /48 subnet
|
5736
|
$ip = gen_subnetv6($host, 48);
|
5737
|
}
|
5738
|
$ip = pfb_filter($ip, PFB_FILTER_IP, 'pfb_daemon_filterlog');
|
5739
|
|
5740
|
// Determine ASN number of host
|
5741
|
$asn = 'null';
|
5742
|
if (($pfb['asn_reporting'] != 'disabled' || !empty($pfb['asn_token'])) && !empty($ip)) {
|
5743
|
|
5744
|
$is_asn_cache = FALSE;
|
5745
|
$db_handle = pfb_open_sqlite(5, 'Query ASN cache');
|
5746
|
if ($db_handle) {
|
5747
|
|
5748
|
// Clear cached entries older than defined cache setting
|
5749
|
$db_update = "DELETE FROM asncache WHERE timestamp <= datetime('now', 'localtime', :asn_cache)";
|
5750
|
$stmt = $db_handle->prepare($db_update);
|
5751
|
if ($stmt) {
|
5752
|
$stmt->bindValue(':asn_cache', $asn_cache, SQLITE3_TEXT);
|
5753
|
$stmt->execute();
|
5754
|
}
|
5755
|
|
5756
|
// Query for cached Host IP
|
5757
|
$db_update = "SELECT * FROM asncache WHERE host = :host;";
|
5758
|
$stmt = $db_handle->prepare($db_update);
|
5759
|
if ($stmt) {
|
5760
|
$stmt->bindValue(':host', $ip, SQLITE3_TEXT);
|
5761
|
$result = $stmt->execute();
|
5762
|
if ($result) {
|
5763
|
$is_asn_cache = $result->fetchArray(SQLITE3_ASSOC);
|
5764
|
}
|
5765
|
}
|
5766
|
}
|
5767
|
pfb_close_sqlite($db_handle);
|
5768
|
|
5769
|
// If Host IP is not in ASN cache, collect ASN from ASN database
|
5770
|
if (!$is_asn_cache) {
|
5771
|
$asn = exec("{$pfb['script']} iptoasn " . escapeshellarg($ip) . " 2>&1");
|
5772
|
|
5773
|
// Convert any IDN to ASCII
|
5774
|
$asn_final = '';
|
5775
|
if (!ctype_print($asn)) {
|
5776
|
if (strpos($asn, '|') !== FALSE) {
|
5777
|
$asn_ex = explode('|', $asn);
|
5778
|
if (is_array($asn_ex) && !empty($asn_ex)) {
|
5779
|
foreach ($asn_ex as $asn_element) {
|
5780
|
if (strpos($asn_element, ':') !== FALSE) {
|
5781
|
$asn_element_ex = explode(':', $asn_element);
|
5782
|
if (is_array($asn_element_ex) && !empty($asn_element_ex)) {
|
5783
|
if (!ctype_print($asn_element_ex[1])) {
|
5784
|
$asn_element_ex[1] = mb_convert_encoding($asn_element_ex[1], 'UTF-8',
|
5785
|
mb_detect_encoding($asn_element_ex[1], 'UTF-8, ASCII, ISO-8859-1'));
|
5786
|
$asn_element_ex[1] = idn_to_ascii($asn_element_ex[1]);
|
5787
|
}
|
5788
|
$asn_final .= "{$asn_element_ex[0]}:{$asn_element_ex[1]}|";
|
5789
|
}
|
5790
|
}
|
5791
|
}
|
5792
|
}
|
5793
|
}
|
5794
|
}
|
5795
|
else {
|
5796
|
$asn_final = $asn;
|
5797
|
}
|
5798
|
$asn = pfb_filter($asn_final, PFB_FILTER_HTML, 'pfb_daemon_filterlog - asn');
|
5799
|
|
5800
|
if (empty($asn) || strpos($asn, 'ASN:') === FALSE) {
|
5801
|
$asn = 'Unknown';
|
5802
|
}
|
5803
|
|
5804
|
// Save entry to ASN cache
|
5805
|
else {
|
5806
|
$db_update = "INSERT into asncache ( asn, host, timestamp ) VALUES ( :asn, :host, datetime('now', 'localtime'))";
|
5807
|
$db_handle = pfb_open_sqlite(5, 'Add to ASN cache');
|
5808
|
if ($db_handle) {
|
5809
|
$stmt = $db_handle->prepare($db_update);
|
5810
|
if ($stmt) {
|
5811
|
$asn = "|{$asn}";
|
5812
|
$stmt->bindValue(':asn', $asn, SQLITE3_TEXT);
|
5813
|
$stmt->bindValue(':host', $ip, SQLITE3_TEXT);
|
5814
|
$stmt->execute();
|
5815
|
}
|
5816
|
}
|
5817
|
pfb_close_sqlite($db_handle);
|
5818
|
}
|
5819
|
}
|
5820
|
|
5821
|
// Use cached ASN Entry
|
5822
|
else {
|
5823
|
$asn = htmlspecialchars($is_asn_cache['asn']) ?: 'Unknown';
|
5824
|
}
|
5825
|
}
|
5826
|
|
5827
|
// Log: "Date timestamp/Tracker ID/Interface/Interface Name/Action/IP Version"
|
5828
|
if ($log_type == 'BSD') {
|
5829
|
$log = "{$f[0]} {$f[1]} {$f[2]},{$d[3]},{$d[4]},{$int},{$d[6]},{$d[8]},";
|
5830
|
} else {
|
5831
|
$ts = date('M j H:i:s', strtotime($f[1]));
|
5832
|
$log = "{$ts},{$d[3]},{$d[4]},{$int},{$d[6]},{$d[8]},";
|
5833
|
}
|
5834
|
|
5835
|
// Details: "Direction/GeoIP/Aliasname/IP evaluated/Feed Name/Resolved Hostname/Client Hostname/ASN/Duplicate status"
|
5836
|
$details = "{$dir},{$geoip},{$pfb_alias},{$pfb_query[1]},{$pfb_query[0]},{$resolved_host},{$hostname},{$asn}";
|
5837
|
|
5838
|
// Reverse Match text
|
5839
|
if ($d[6] == 'match') {
|
5840
|
$d[6] = 'unkn(%u)';
|
5841
|
}
|
5842
|
|
5843
|
if ($d[8] == 4) {
|
5844
|
// Previous: "Tracker ID/Action/SRC IP/DST IP/DST Port"
|
5845
|
$p_entry = "{$d[3]}{$d[6]}{$d[18]}{$d[19]}{$d[21]}";
|
5846
|
$d[16] = str_replace('TCP', 'TCP-', strtoupper($d[16]), $d[16]) . $tcp_flags;
|
5847
|
|
5848
|
// Log: "Protocol ID/Protocol/SRC IP/DST IP/SRC Port/DST Port"
|
5849
|
$log .= "{$d[15]},{$d[16]},{$d[18]},{$d[19]},{$d[20]},{$d[21]}";
|
5850
|
}
|
5851
|
else {
|
5852
|
$p_entry = "{$d[3]}{$d[6]}{$d[15]}{$d[16]}{$d[18]}";
|
5853
|
$d[12] = str_replace('TCP', 'TCP-', strtoupper($d[12]), $d[12]) . $tcp_flags;
|
5854
|
$log .= "{$d[13]},{$d[12]},{$d[15]},{$d[16]},{$d[17]},{$d[18]}";
|
5855
|
}
|
5856
|
}
|
5857
|
@file_put_contents("{$iplog}", "{$log},{$details},{$dup_entry}\n", FILE_APPEND | LOCK_EX);
|
5858
|
|
5859
|
// Write to Unified Log
|
5860
|
if ($dup_entry == '+') {
|
5861
|
@file_put_contents("{$pfb['unilog']}", "{$l_type},{$log},{$details},{$dup_entry}\n", FILE_APPEND | LOCK_EX);
|
5862
|
}
|
5863
|
}
|
5864
|
}
|
5865
|
}
|
5866
|
else {
|
5867
|
log_error('[pfBlockerNG] filterlog - Failed to read STDIN');
|
5868
|
}
|
5869
|
if ($s_handle) {
|
5870
|
@fclose($s_handle);
|
5871
|
}
|
5872
|
}
|
5873
|
|
5874
|
|
5875
|
// Function to parse grep output and return Aliasname and IP fields
|
5876
|
function pfb_parse_query($line) {
|
5877
|
$rx = explode('.txt:', $line);
|
5878
|
$rx[0] = ltrim(strrchr($rx[0], '/'), '/');
|
5879
|
return $rx;
|
5880
|
}
|
5881
|
|
5882
|
|
5883
|
// Function to output Alias/Feed name string
|
5884
|
function pfb_parse_line($line) {
|
5885
|
$match = strstr($line, ':local', TRUE);
|
5886
|
if (strpos($match, '.txt') !== FALSE) {
|
5887
|
$match = strstr($match, '.txt', TRUE);
|
5888
|
}
|
5889
|
$match = substr($match, strrpos($match, '/') + 1);
|
5890
|
return $match;
|
5891
|
}
|
5892
|
|
5893
|
|
5894
|
// Functon to find which DNSBL Feed/Groupname blocked this event
|
5895
|
function pfb_dnsbl_parse($mode='daemon', $domain, $src_ip, $req_agent) {
|
5896
|
global $pfb;
|
5897
|
|
5898
|
$dnsbl_cache = FALSE;
|
5899
|
$db_handle = pfb_open_sqlite(4, 'Query cache');
|
5900
|
if ($db_handle) {
|
5901
|
$db_update = "SELECT * FROM dnsblcache WHERE domain = :domain;";
|
5902
|
$stmt = $db_handle->prepare($db_update);
|
5903
|
if ($stmt) {
|
5904
|
$stmt->bindValue(':domain', $domain, SQLITE3_TEXT);
|
5905
|
$result = $stmt->execute();
|
5906
|
if ($result) {
|
5907
|
$dnsbl_cache = $result->fetchArray(SQLITE3_ASSOC);
|
5908
|
}
|
5909
|
}
|
5910
|
}
|
5911
|
pfb_close_sqlite($db_handle);
|
5912
|
|
5913
|
// If domain is not in DNSBL cache, query for blocked domain details
|
5914
|
if (!$dnsbl_cache) {
|
5915
|
$o_domain = $domain; // Store original domain for CNAME query
|
5916
|
|
5917
|
while (TRUE) {
|
5918
|
|
5919
|
$domainparse = str_replace('.', '\.', $domain);
|
5920
|
if ($pfb['dnsbl_py_blacklist']) {
|
5921
|
$dquery_esc = escapeshellarg(",{$domainparse},,");
|
5922
|
$pfb_feed = exec("{$pfb['grep']} -shm1 {$dquery_esc} {$pfb['unbound_py_data']} 2>&1");
|
5923
|
} else {
|
5924
|
$dquery_esc = escapeshellarg(" \"{$domainparse} 60");
|
5925
|
$pfb_feed = pfb_parse_line(exec("{$pfb['grep']} -sHm1 {$dquery_esc} {$pfb['dnsdir']}/*.txt 2>&1"));
|
5926
|
}
|
5927
|
$pfb_group = $pfb_mode = $pfb_final = 'Unknown';
|
5928
|
|
5929
|
// Exact Domain match found
|
5930
|
if (!empty($pfb_feed)) {
|
5931
|
|
5932
|
if ($pfb['dnsbl_py_blacklist']) {
|
5933
|
list($dummy, $pfb_final, $empty_placeholder, $log_type, $pfb_feed, $pfb_group) = explode(',', $pfb_feed);
|
5934
|
$pfb_mode = 'DNSBL';
|
5935
|
}
|
5936
|
else {
|
5937
|
$pfb_group = pfb_parse_line(exec("{$pfb['grep']} -sHm1 {$dquery_esc} {$pfb['dnsalias']}/* 2>&1"));
|
5938
|
|
5939
|
// Determine DNSBL Type
|
5940
|
$pfb_mode = 'DNSBL';
|
5941
|
$ip = gethostbyname("dnsbl.test.{$domain}");
|
5942
|
if ($ip == $pfb['dnsbl_vip'] || $ip == "::{$pfb['dnsbl_vip']}" || $ip == '0.0.0.0') {
|
5943
|
$pfb_mode = 'TLD';
|
5944
|
}
|
5945
|
$pfb_final = $domain;
|
5946
|
}
|
5947
|
}
|
5948
|
|
5949
|
else {
|
5950
|
$dparts = explode('.', $domain);
|
5951
|
if (!$pfb['dnsbl_py_blacklist']) {
|
5952
|
unset($dparts[0]);
|
5953
|
}
|
5954
|
$dcnt = count($dparts);
|
5955
|
|
5956
|
for ($i=0; $i <= $dcnt; $i++) {
|
5957
|
|
5958
|
$domainparse = str_replace('.', '\.', implode('.', $dparts));
|
5959
|
$domainparse_esc = escapeshellarg("^{$domainparse}$");
|
5960
|
if (!$pfb['dnsbl_py_blacklist']) {
|
5961
|
$dquery_esc = escapeshellarg(" \"{$domainparse} 60");
|
5962
|
|
5963
|
// Determine if TLD exists in TLD Blacklist
|
5964
|
if (file_exists("{$pfb['dnsbl_tld_txt']}")) {
|
5965
|
exec("/usr/bin/grep -l {$domainparse_esc} {$pfb['dnsbl_tld_txt']} 2>&1", $match);
|
5966
|
if (!empty($match[0])) {
|
5967
|
$pfb_group = $pfb_feed = $pfb_mode = 'DNSBL_TLD';
|
5968
|
$pfb_final = $domainparse;
|
5969
|
break;
|
5970
|
}
|
5971
|
}
|
5972
|
}
|
5973
|
|
5974
|
if ($pfb['dnsbl_py_blacklist']) {
|
5975
|
$dquery_esc = escapeshellarg(",{$domainparse},,");
|
5976
|
$pfb_feed = exec("{$pfb['grep']} -shm1 {$dquery_esc} {$pfb['unbound_py_zone']} 2>&1");
|
5977
|
|
5978
|
// Collect Alias Group name
|
5979
|
if (!empty($pfb_feed)) {
|
5980
|
list($dummy, $d_found, $empty_placeholder, $log_type, $pfb_feed, $pfb_group) = explode(',', $pfb_feed);
|
5981
|
|
5982
|
$pfb_final = str_replace('\.', '.', $domainparse);
|
5983
|
$pfb_mode = 'TLD';
|
5984
|
if ($pfb_feed == 'DNSBL_TLD') {
|
5985
|
$pfb_mode = 'DNSBL_TLD';
|
5986
|
}
|
5987
|
break;
|
5988
|
}
|
5989
|
}
|
5990
|
else {
|
5991
|
$pfb_feed = pfb_parse_line(exec("{$pfb['grep']} -sHm1 {$dquery_esc} {$pfb['dnsdir']}/*.txt 2>&1"));
|
5992
|
|
5993
|
// Collect Alias Group name
|
5994
|
if (!empty($pfb_feed)) {
|
5995
|
$pfb_group = pfb_parse_line(exec("{$pfb['grep']} -sHm1 {$dquery_esc} {$pfb['dnsalias']}/* 2>&1"));
|
5996
|
$pfb_mode = 'TLD';
|
5997
|
$pfb_final = str_replace('\.', '.', $domainparse);
|
5998
|
break;
|
5999
|
}
|
6000
|
}
|
6001
|
unset($dparts[$i]);
|
6002
|
}
|
6003
|
}
|
6004
|
|
6005
|
// Query for CNAME(s)
|
6006
|
if (empty($pfb_feed)) {
|
6007
|
if (!isset($cnames)) {
|
6008
|
$domain_esc = escapeshellarg($domain);
|
6009
|
$extdns_esc = escapeshellarg("@{$pfb['extdns']}");
|
6010
|
exec("/usr/bin/drill {$domain_esc} {$extdns_esc} | /usr/bin/awk '/CNAME/ {sub(\"\.\$\", \"\", \$5); print \$5;}'", $cnames);
|
6011
|
if (is_array($cnames) && !empty($cnames)) {
|
6012
|
foreach ($cnames as $key => $cname) {
|
6013
|
// Remove invalid CNAMES
|
6014
|
if (empty(pfb_filter($cname, PFB_FILTER_DOMAIN, 'pfb_dnsbl_parse'))) {
|
6015
|
unset($cnames[$key]);
|
6016
|
continue;
|
6017
|
}
|
6018
|
}
|
6019
|
$cname_cnt = count($cnames);
|
6020
|
}
|
6021
|
}
|
6022
|
if (is_array($cnames) && !empty($cnames)) {
|
6023
|
$domain = array_shift($cnames);
|
6024
|
}
|
6025
|
|
6026
|
if ($cname_cnt == 0) {
|
6027
|
$domain = $o_domain; // No CNAME match found, revert back to original domain
|
6028
|
break;
|
6029
|
}
|
6030
|
$cname_cnt--;
|
6031
|
}
|
6032
|
else {
|
6033
|
break;
|
6034
|
}
|
6035
|
}
|
6036
|
$pfb_feed = $pfb_feed ?: 'Unknown';
|
6037
|
|
6038
|
if (isset($cnames)) {
|
6039
|
unset($cnames);
|
6040
|
if ($pfb_feed != 'Unknown') {
|
6041
|
$pfb_mode = "{$pfb_mode}-CNAME";
|
6042
|
}
|
6043
|
}
|
6044
|
|
6045
|
// Save entry to DNSBL cache
|
6046
|
$db_update = "INSERT into dnsblcache ( type, domain, groupname, final, feed ) VALUES ( :type, :domain, :groupname, :final, :feed )";
|
6047
|
$db_handle = pfb_open_sqlite(4, 'Add to DNSBL cache');
|
6048
|
if ($db_handle) {
|
6049
|
$domain = pfb_filter($domain, PFB_FILTER_HTML, 'pfb_dnsbl_parse');
|
6050
|
$pfb_group = pfb_filter($pfb_group, PFB_FILTER_HTML, 'pfb_dnsbl_parse');
|
6051
|
$pfb_final = pfb_filter($pfb_final, PFB_FILTER_HTML, 'pfb_dnsbl_parse');
|
6052
|
$pfb_feed = pfb_filter($pfb_feed, PFB_FILTER_HTML, 'pfb_dnsbl_parse');
|
6053
|
|
6054
|
$stmt = $db_handle->prepare($db_update);
|
6055
|
if ($stmt) {
|
6056
|
$stmt->bindValue(':type', $pfb_mode, SQLITE3_TEXT);
|
6057
|
$stmt->bindValue(':domain', $domain, SQLITE3_TEXT);
|
6058
|
$stmt->bindValue(':groupname', $pfb_group, SQLITE3_TEXT);
|
6059
|
$stmt->bindValue(':final', $pfb_final, SQLITE3_TEXT);
|
6060
|
$stmt->bindValue(':feed', $pfb_feed, SQLITE3_TEXT);
|
6061
|
$stmt->execute();
|
6062
|
}
|
6063
|
}
|
6064
|
pfb_close_sqlite($db_handle);
|
6065
|
}
|
6066
|
|
6067
|
// Use cached entries
|
6068
|
else {
|
6069
|
$pfb_mode = htmlspecialchars($dnsbl_cache['type']) ?: 'Unknown';
|
6070
|
$pfb_group = htmlspecialchars($dnsbl_cache['groupname']) ?: 'Unknown';
|
6071
|
$pfb_final = htmlspecialchars($dnsbl_cache['final']) ?: 'Unknown';
|
6072
|
$pfb_feed = htmlspecialchars($dnsbl_cache['feed']) ?: 'Unknown';
|
6073
|
}
|
6074
|
|
6075
|
if ($mode == 'daemon') {
|
6076
|
$details = "{$domain},{$src_ip},{$req_agent},{$pfb_mode},{$pfb_group},{$pfb_final},{$pfb_feed}";
|
6077
|
return array ($pfb_group, $details);
|
6078
|
} else {
|
6079
|
return array ('pfb_mode' => $pfb_mode, 'pfb_group' => $pfb_group, 'pfb_final' => $pfb_final, 'pfb_feed' => $pfb_feed);
|
6080
|
}
|
6081
|
}
|
6082
|
|
6083
|
|
6084
|
// DNSBL Lighttpd 'dnsbl_error.log' conditional log parser
|
6085
|
function pfb_daemon_dnsbl() {
|
6086
|
global $pfb;
|
6087
|
|
6088
|
if (($h_handle = @fopen('php://stdin', 'r')) !== FALSE) {
|
6089
|
syslog(LOG_NOTICE, '[pfBlockerNG] DNSBL parser daemon started');
|
6090
|
|
6091
|
$chk_ver = FALSE;
|
6092
|
$lighty_47 = FALSE;
|
6093
|
$lighty_58 = FALSE;
|
6094
|
$lighty_59 = FALSE;
|
6095
|
$checkpos = 3; // Pre Lighttpd v1.4.47
|
6096
|
|
6097
|
while (!feof($h_handle)) {
|
6098
|
$pfb_buffer = @fgets($h_handle);
|
6099
|
|
6100
|
if (!$chk_ver) {
|
6101
|
|
6102
|
// Lighttpd v1.4.58+ conditional error log with 'ssl.verifyclient.activate' to collect the domain name
|
6103
|
if (!$lighty_47 && strpos($pfb_buffer, 'lighttpd/1.4.58') !== FALSE) {
|
6104
|
$chk_ver = TRUE;
|
6105
|
$lighty_58 = TRUE;
|
6106
|
continue;
|
6107
|
}
|
6108
|
|
6109
|
// Lighttpd v1.4.59+ syntax changes to IP line
|
6110
|
elseif (!$lighty_47 &&
|
6111
|
(strpos($pfb_buffer, 'lighttpd/1.4.59') !== FALSE ||
|
6112
|
strpos($pfb_buffer, 'lighttpd/1.4.6') !== FALSE ||
|
6113
|
strpos($pfb_buffer, 'lighttpd/1.4.7') !== FALSE ||
|
6114
|
strpos($pfb_buffer, 'lighttpd/1.5') !== FALSE)) {
|
6115
|
$chk_ver = TRUE;
|
6116
|
$lighty_59 = TRUE;
|
6117
|
continue;
|
6118
|
}
|
6119
|
|
6120
|
// Lighttpd v1.4.47 uses mod_openssl with a different conditional log formatting
|
6121
|
elseif (strpos($pfb_buffer, 'global/HTTPscheme') !== FALSE) {
|
6122
|
$lighty_47 = TRUE;
|
6123
|
$checkpos = 1;
|
6124
|
continue;
|
6125
|
}
|
6126
|
}
|
6127
|
|
6128
|
if ($lighty_58 || $lighty_59) {
|
6129
|
|
6130
|
// Verify HTTP["remoteip"]
|
6131
|
if (empty($src_ip)) {
|
6132
|
if ($lighty_58) {
|
6133
|
if (strpos($pfb_buffer, '["re') !== FALSE) {
|
6134
|
$src_ip = strstr($pfb_buffer, ') compare', TRUE);
|
6135
|
$src_ip = ltrim(strstr($src_ip, '] (', FALSE), '] (');
|
6136
|
$src_ip = (filter_var($src_ip, FILTER_VALIDATE_IP) !== FALSE) ? $src_ip : '';
|
6137
|
}
|
6138
|
} else {
|
6139
|
if (strpos($pfb_buffer, '["re') !== FALSE && strpos($pfb_buffer, ' compare to ') !== FALSE) {
|
6140
|
$src_ip = strstr($pfb_buffer, ' compare to ', FALSE);
|
6141
|
$src_ip = trim(ltrim($src_ip, ' compare to '));
|
6142
|
$src_ip = (filter_var($src_ip, FILTER_VALIDATE_IP) !== FALSE) ? $src_ip : '';
|
6143
|
}
|
6144
|
}
|
6145
|
continue;
|
6146
|
}
|
6147
|
|
6148
|
if (!empty($src_ip) && strpos($pfb_buffer, "SSL: ") === FALSE) {
|
6149
|
continue;
|
6150
|
}
|
6151
|
if (empty($domain) && strpos($pfb_buffer, "SSL: can't verify client without ssl.") !== FALSE) {
|
6152
|
$domain = str_replace(' server name ', '', strstr(trim($pfb_buffer), ' server name ', FALSE));
|
6153
|
}
|
6154
|
|
6155
|
if (!empty($domain) && !empty($src_ip)) {
|
6156
|
|
6157
|
$domain = pfb_filter($domain, PFB_FILTER_DOMAIN, 'pfb_daemon_dnsbl');
|
6158
|
$src_ip = pfb_filter($src_ip, PFB_FILTER_IP, 'pfb_daemon_dnsbl');
|
6159
|
if (!empty($domain) && !empty($src_ip)) {
|
6160
|
|
6161
|
// URL/Referer/URI/Agent String not available for HTTPS events
|
6162
|
$req_agent = 'Unknown';
|
6163
|
$type = 'DNSBL-HTTPS';
|
6164
|
|
6165
|
pfb_log_event($type, $domain, $src_ip, $req_agent);
|
6166
|
$domain = $src_ip = '';
|
6167
|
}
|
6168
|
}
|
6169
|
}
|
6170
|
else {
|
6171
|
// Parse only HTTP["xxx"] log lines
|
6172
|
if (strpos($pfb_buffer, 'HTTP[') === FALSE) {
|
6173
|
continue;
|
6174
|
}
|
6175
|
|
6176
|
// Verify only HTTPS lines
|
6177
|
if (strpos($pfb_buffer, '( https') !== FALSE) {
|
6178
|
if ($lighty_47) {
|
6179
|
continue;
|
6180
|
}
|
6181
|
$checkpos = 0;
|
6182
|
}
|
6183
|
|
6184
|
// Verify HTTP["remoteip"]
|
6185
|
if ($checkpos == 1 && strpos($pfb_buffer, '["re') !== FALSE) {
|
6186
|
$src_ip = strstr($pfb_buffer, ' ) compare', TRUE);
|
6187
|
$src_ip = ltrim(strstr($src_ip, '] ( ', FALSE), '] ( ');
|
6188
|
$src_ip = (filter_var($src_ip, FILTER_VALIDATE_IP) !== FALSE) ? $src_ip : '';
|
6189
|
}
|
6190
|
|
6191
|
// Verify HTTP["host"]
|
6192
|
elseif ($checkpos == 2 && strpos($pfb_buffer, '["ho') !== FALSE) {
|
6193
|
$lighty_47 = FALSE;
|
6194
|
$domain = strstr($pfb_buffer, ' ) compare', TRUE);
|
6195
|
$domain = ltrim(strstr($domain, '] ( ', FALSE), '] ( ');
|
6196
|
|
6197
|
// URL/Referer/URI/Agent String not available for HTTPS events
|
6198
|
$req_agent = 'Unknown';
|
6199
|
$type = 'DNSBL-HTTPS';
|
6200
|
|
6201
|
// Log event and Increment SQLite DNSBL Group counter
|
6202
|
if (!empty($domain) && !empty($src_ip)) {
|
6203
|
|
6204
|
$domain = pfb_filter($domain, PFB_FILTER_DOMAIN, 'pfb_daemon_dnsbl');
|
6205
|
$src_ip = pfb_filter($src_ip, PFB_FILTER_IP, 'pfb_daemon_dnsbl');
|
6206
|
if (!empty($domain) && !empty($src_ip)) {
|
6207
|
|
6208
|
// URL/Referer/URI/Agent String not available for HTTPS events
|
6209
|
$req_agent = 'Unknown';
|
6210
|
$type = 'DNSBL-HTTPS';
|
6211
|
|
6212
|
pfb_log_event($type, $domain, $src_ip, $req_agent);
|
6213
|
$domain = $src_ip = '';
|
6214
|
}
|
6215
|
}
|
6216
|
}
|
6217
|
$checkpos++;
|
6218
|
}
|
6219
|
}
|
6220
|
}
|
6221
|
else {
|
6222
|
log_error('[pfBlockerNG] DNSBL conditional log parser - Failed to open handle');
|
6223
|
}
|
6224
|
@fclose($h_handle);
|
6225
|
}
|
6226
|
|
6227
|
|
6228
|
// DNSBL Lighttpd 'index.php' event parser
|
6229
|
function pfb_daemon_dnsbl_index() {
|
6230
|
global $pfb;
|
6231
|
|
6232
|
// Replace any [',' or '|' ] in HTTP_REFERER, REQUEST_URI or HTTP_USER_AGENT fields
|
6233
|
// Replace placeholder characters [ '!' and '*' ] to [ ',' and '|' ]
|
6234
|
$p1 = array( ',', '!', '|', ' * ' );
|
6235
|
$p2 = array( '', ',', '--', '|' );
|
6236
|
|
6237
|
if (($i_handle = @fopen('php://stdin', 'r')) !== FALSE) {
|
6238
|
while (!feof($i_handle)) {
|
6239
|
$pfb_buffer = @fgets($i_handle);
|
6240
|
$pfb_buffer = str_replace($p1, $p2, $pfb_buffer);
|
6241
|
|
6242
|
if (substr($pfb_buffer, 0, 6) == 'INDEX,' && substr_count($pfb_buffer, ',') == 4) {
|
6243
|
$csvline = str_getcsv($pfb_buffer, ',', '', '"');
|
6244
|
|
6245
|
// Determine blocked domain type (Full, 1x1 or JS)
|
6246
|
if (isset($csvline[1]) && !empty($csvline[1])) {
|
6247
|
$request = strstr($csvline[1], '/', FALSE);
|
6248
|
$request = strstr($request, ' ', TRUE);
|
6249
|
|
6250
|
if (strlen($request) < 2) {
|
6251
|
$type = 'DNSBL-Full';
|
6252
|
}
|
6253
|
else {
|
6254
|
if (pathinfo($request, PATHINFO_EXTENSION) == 'js') {
|
6255
|
$type = 'DNSBL-JS';
|
6256
|
} else {
|
6257
|
$type = 'DNSBL-1x1';
|
6258
|
}
|
6259
|
}
|
6260
|
}
|
6261
|
else {
|
6262
|
$type = 'DNSBL-Unknown';
|
6263
|
}
|
6264
|
|
6265
|
$csvline[2] = pfb_filter($csvline[2], PFB_FILTER_DOMAIN, 'pfb_daemon_dnsbl_index');
|
6266
|
$csvline[3] = pfb_filter($csvline[3], PFB_FILTER_IP, 'pfb_daemon_dnsbl_index');
|
6267
|
$csvline[4] = pfb_filter($csvline[4], PFB_FILTER_HTML, 'pfb_daemon_dnsbl_index');
|
6268
|
}
|
6269
|
else {
|
6270
|
continue;
|
6271
|
}
|
6272
|
|
6273
|
// Log event and increment SQLite DNSBL Group counter
|
6274
|
if (!empty($csvline[2]) && !empty($csvline[3])) {
|
6275
|
pfb_log_event($type, $csvline[2], $csvline[3], $csvline[4]);
|
6276
|
}
|
6277
|
}
|
6278
|
}
|
6279
|
else {
|
6280
|
log_error('[pfBlockerNG] DNSBL index event parser - Failed to open handle');
|
6281
|
}
|
6282
|
@fclose($i_handle);
|
6283
|
}
|
6284
|
|
6285
|
|
6286
|
// Function to create/open SQLite3 database(s)
|
6287
|
function pfb_open_sqlite($table, $message) {
|
6288
|
global $pfb;
|
6289
|
|
6290
|
if ($table == 1) {
|
6291
|
$database = $pfb['dnsbl_info'];
|
6292
|
$db_table = 'dnsbl';
|
6293
|
$db_create = "CREATE TABLE IF NOT EXISTS dnsbl ( groupname TEXT, timestamp TEXT, entries INTEGER, counter INTEGER );";
|
6294
|
} elseif ($table == 2) {
|
6295
|
$database = $pfb['dnsbl_resolver'];
|
6296
|
$db_table = 'lastevent';
|
6297
|
$db_create = "CREATE TABLE IF NOT EXISTS lastevent ( row INTEGER, groupname TEXT, entry TEXT, details TEXT );";
|
6298
|
} elseif ($table == 3) {
|
6299
|
$database = $pfb['dnsbl_resolver'];
|
6300
|
$db_table = 'resolver';
|
6301
|
$db_create = "CREATE TABLE IF NOT EXISTS resolver ( row INTEGER, totalqueries INTEGER, queries INTEGER );";
|
6302
|
} elseif ($table == 4) {
|
6303
|
$database = $pfb['dnsbl_cache'];
|
6304
|
$db_table = 'dnsblcache';
|
6305
|
$db_create = "CREATE TABLE IF NOT EXISTS dnsblcache ( type TEXT, domain TEXT, groupname TEXT, final TEXT, feed TEXT );";
|
6306
|
} elseif ($table == 5) {
|
6307
|
$database = $pfb['asn_cache'];
|
6308
|
$db_table = 'asncache';
|
6309
|
$db_create = "CREATE TABLE IF NOT EXISTS asncache ( asn TEXT, host TEXT, timestamp TEXT );";
|
6310
|
} elseif ($table == 6) {
|
6311
|
$database = $pfb['dnsbl_resolver'];
|
6312
|
$db_table = 'stats';
|
6313
|
$db_create = "CREATE TABLE IF NOT EXISTS lastclear ( row INTEGER, lastipclear TEXT, lastdnsblclear TEXT );";
|
6314
|
} elseif ($table == 7) {
|
6315
|
$database = $pfb['ip_cache'];
|
6316
|
$db_table = 'ipcache';
|
6317
|
$db_create = "CREATE TABLE IF NOT EXISTS ipcache ( host TEXT, q0 TEXT, q1 TEXT, geoip TEXT, resolved_host TEXT );";
|
6318
|
}
|
6319
|
|
6320
|
try {
|
6321
|
$db_handle = new SQLite3($database);
|
6322
|
$db_handle->busyTimeout("{$pfb['sqlite_timeout']}");
|
6323
|
}
|
6324
|
catch (Exception $e) {
|
6325
|
@file_put_contents($pfb['errlog'], "\nDNSBL_SQL: Failed to open DB - {$message}", FILE_APPEND | LOCK_EX);
|
6326
|
|
6327
|
try {
|
6328
|
$db_handle = new SQLite3($database);
|
6329
|
$db_handle->busyTimeout("{$pfb['sqlite_timeout']}");
|
6330
|
}
|
6331
|
catch (Exception $e) {
|
6332
|
@file_put_contents($pfb['errlog'], "\nDNSBL_SQL: Failed to open DB 2nd attempt - {$message}", FILE_APPEND | LOCK_EX);
|
6333
|
}
|
6334
|
}
|
6335
|
|
6336
|
if ($db_handle) {
|
6337
|
|
6338
|
// Validate database integrity
|
6339
|
$validate = $pfb_validate = FALSE;
|
6340
|
try {
|
6341
|
$validate = $db_handle->query("PRAGMA integrity_check;");
|
6342
|
}
|
6343
|
catch (Exception $e) {
|
6344
|
@file_put_contents($pfb['errlog'], "\nDNSBL_SQL: Failed to validate database - {$message}", FILE_APPEND | LOCK_EX);
|
6345
|
}
|
6346
|
|
6347
|
if ($validate) {
|
6348
|
while ($res = $validate->fetchArray(SQLITE3_ASSOC)) {
|
6349
|
if ($res['integrity_check'] == 'ok') {
|
6350
|
$pfb_validate = TRUE;
|
6351
|
}
|
6352
|
}
|
6353
|
}
|
6354
|
|
6355
|
if (!$pfb_validate) {
|
6356
|
log_error("[pfBlockerNG] DNSBL SQLite3 database [ {$db_table} ] corrupt. Table deletion/re-creation completed.");
|
6357
|
@copy("{$database}", "{$database}.invalid");
|
6358
|
$db_create = "DROP TABLE {$db_table}; {$db_create}";
|
6359
|
}
|
6360
|
|
6361
|
try {
|
6362
|
$db_handle->exec("BEGIN TRANSACTION; PRAGMA journal_mode = delete;"
|
6363
|
. "{$db_create}"
|
6364
|
. "END TRANSACTION;");
|
6365
|
}
|
6366
|
catch (Exception $e) {
|
6367
|
@file_put_contents($pfb['errlog'], "\nDNSBL_SQL: Database failure - {$message}", FILE_APPEND | LOCK_EX);
|
6368
|
return;
|
6369
|
}
|
6370
|
|
6371
|
if ($table <= 4 && file_exists($database)) {
|
6372
|
@chown($database, 'unbound');
|
6373
|
@chgrp($database, 'unbound');
|
6374
|
}
|
6375
|
return $db_handle;
|
6376
|
}
|
6377
|
return FALSE;
|
6378
|
}
|
6379
|
|
6380
|
|
6381
|
// Function to close SQLite3 database
|
6382
|
function pfb_close_sqlite($db_handle) {
|
6383
|
if (!empty($db_handle)) {
|
6384
|
$db_handle->close();
|
6385
|
unset($db_handle);
|
6386
|
}
|
6387
|
}
|
6388
|
|
6389
|
|
6390
|
// Function to Log event, Increment SQLite 'dnsbl' database and save last event to SQLite 'lastevent' Database
|
6391
|
function pfb_log_event($type, $domain, $src_ip, $req_agent) {
|
6392
|
global $pfb;
|
6393
|
|
6394
|
$datereq = date('M j H:i:s', time());
|
6395
|
$req_agent = str_replace("'", '', $req_agent);
|
6396
|
|
6397
|
// Collect lastevent without saving new event
|
6398
|
$result = pfb_dnsbl_lastevent('', "{$domain}{$src_ip}", '');
|
6399
|
$p_entry = $result['entry'];
|
6400
|
$details = $result['details'];
|
6401
|
|
6402
|
// Duplicate entry comparison: "Domain/SRC IP"
|
6403
|
if ("{$domain}{$src_ip}" == $p_entry) {
|
6404
|
$dup_entry = '-';
|
6405
|
$pfb_group = $result['groupname'];
|
6406
|
}
|
6407
|
|
6408
|
// If not duplicate entry, determine TLD type, Group Name and Feed name
|
6409
|
else {
|
6410
|
$dup_entry = '+';
|
6411
|
$data = pfb_dnsbl_parse('daemon', $domain, $src_ip, $req_agent);
|
6412
|
$pfb_group = $data[0];
|
6413
|
$details = $data[1];
|
6414
|
$req_agent = $data[2];
|
6415
|
|
6416
|
// Save new lastevent
|
6417
|
pfb_dnsbl_lastevent($pfb_group, "{$domain}{$src_ip}", $details);
|
6418
|
}
|
6419
|
|
6420
|
$log = "{$type},{$datereq},{$details},{$dup_entry}\n";
|
6421
|
@file_put_contents($pfb['dnslog'], "{$log}", FILE_APPEND | LOCK_EX);
|
6422
|
|
6423
|
// Write to Unified Log
|
6424
|
@file_put_contents($pfb['unilog'], "{$log}", FILE_APPEND | LOCK_EX);
|
6425
|
|
6426
|
// Increment DNSBL Widget counter
|
6427
|
if (!empty($pfb_group)) {
|
6428
|
|
6429
|
$db_handle = pfb_open_sqlite(1, 'Increment Counter');
|
6430
|
if ($db_handle) {
|
6431
|
|
6432
|
$pfb_group = pfb_filter($pfb_group, PFB_FILTER_HTML, 'pfb_log_event');
|
6433
|
$db_update = "UPDATE dnsbl SET counter = counter + 1 WHERE groupname = :pfb_group";
|
6434
|
|
6435
|
$stmt = $db_handle->prepare($db_update);
|
6436
|
if ($stmt) {
|
6437
|
$stmt->bindValue(':pfb_group', $pfb_group, SQLITE3_TEXT);
|
6438
|
$stmt->execute();
|
6439
|
}
|
6440
|
}
|
6441
|
pfb_close_sqlite($db_handle);
|
6442
|
}
|
6443
|
}
|
6444
|
|
6445
|
|
6446
|
// Function to 1) Collect lastevent and 2) Save lastevent to SQLite 'lastevent' Database
|
6447
|
function pfb_dnsbl_lastevent($p_group, $p_entry, $p_details) {
|
6448
|
global $pfb;
|
6449
|
|
6450
|
$db_update = '';
|
6451
|
$final = array();
|
6452
|
|
6453
|
$db_handle = pfb_open_sqlite(2, 'LastEvent');
|
6454
|
if ($db_handle) {
|
6455
|
$result = $db_handle->query("SELECT * FROM lastevent WHERE row = 0;");
|
6456
|
if ($result) {
|
6457
|
$final = $result->fetchArray(SQLITE3_ASSOC);
|
6458
|
}
|
6459
|
}
|
6460
|
pfb_close_sqlite($db_handle);
|
6461
|
|
6462
|
// Collect or update existing row
|
6463
|
if (!empty($final)) {
|
6464
|
|
6465
|
// Only collect lastevent entry
|
6466
|
if (empty($p_group)) {
|
6467
|
;
|
6468
|
}
|
6469
|
|
6470
|
// Update lastevent entry
|
6471
|
else {
|
6472
|
$db_update = "UPDATE lastevent SET groupname=:p_group, entry=:p_entry, details=:p_details WHERE row = 0";
|
6473
|
}
|
6474
|
}
|
6475
|
|
6476
|
// Add new lastevent entry
|
6477
|
else {
|
6478
|
$db_update = "INSERT into lastevent (row, groupname, entry, details) VALUES (0, :p_group, :p_entry, :p_details )";
|
6479
|
}
|
6480
|
|
6481
|
if (!empty($db_update)) {
|
6482
|
$db_handle = pfb_open_sqlite(2, 'LastEvent');
|
6483
|
if ($db_handle) {
|
6484
|
|
6485
|
$p_group = pfb_filter($p_group, PFB_FILTER_HTML, 'pfb_dnsbl_lastevent');
|
6486
|
$p_entry = pfb_filter($p_entry, PFB_FILTER_HTML, 'pfb_dnsbl_lastevent');
|
6487
|
$p_details = pfb_filter($p_details, PFB_FILTER_HTML, 'pfb_dnsbl_lastevent');
|
6488
|
|
6489
|
$stmt = $db_handle->prepare($db_update);
|
6490
|
if ($stmt) {
|
6491
|
$stmt->bindValue(':p_group', $p_group, SQLITE3_TEXT);
|
6492
|
$stmt->bindValue(':p_entry', $p_entry, SQLITE3_TEXT);
|
6493
|
$stmt->bindValue(':p_details', $p_details, SQLITE3_TEXT);
|
6494
|
$stmt->execute();
|
6495
|
}
|
6496
|
}
|
6497
|
pfb_close_sqlite($db_handle);
|
6498
|
}
|
6499
|
return $final;
|
6500
|
}
|
6501
|
|
6502
|
|
6503
|
// Function to collect and update the Unbound Resolver query/pid entries into SQLite3 database
|
6504
|
function pfb_daemon_queries() {
|
6505
|
global $g, $pfb;
|
6506
|
|
6507
|
$sleep_freq = config_get_path('installedpackages/pfblockerngglobal/widget-dnsblquery', 5);
|
6508
|
|
6509
|
$nice = '/usr/bin/nice -n20';
|
6510
|
$unbound_pid = "{$g['varrun_path']}/unbound.pid";
|
6511
|
|
6512
|
while (TRUE) {
|
6513
|
sleep($sleep_freq);
|
6514
|
|
6515
|
// If 'Live Sync' file marker exists skip DNSBL Queries daemon to avoid unbound-control collisions
|
6516
|
if (platform_booting() || $g['pfblockerng_install'] || file_exists("{$pfb['dnsbl_file']}.sync")) {
|
6517
|
continue;
|
6518
|
}
|
6519
|
|
6520
|
// Collect Unbound Resolver pid
|
6521
|
$pid = exec("{$nice} /usr/bin/pgrep -anx 'unbound' 2>&1");
|
6522
|
if (!empty($pid)) {
|
6523
|
$output = exec("{$pfb['chroot_cmd']} stats_noreset | {$nice} {$pfb['grep']} 'total.num.queries=' | cut -d '=' -f2 2>&1");
|
6524
|
$query = intval($output);
|
6525
|
if (!is_numeric($query)) {
|
6526
|
$query = '';
|
6527
|
}
|
6528
|
|
6529
|
// On an Unbound Reload, the query stat is reset, therefore reuse previous query value
|
6530
|
if ($query < $p_query) {
|
6531
|
$t_query = $query;
|
6532
|
$query = $query + $p_query;
|
6533
|
$pid = '';
|
6534
|
}
|
6535
|
}
|
6536
|
|
6537
|
// On initial daemon load
|
6538
|
if (empty($p_pid)) {
|
6539
|
$p_pid = $pid;
|
6540
|
continue;
|
6541
|
}
|
6542
|
|
6543
|
$pfb_found = FALSE;
|
6544
|
$stats = array();
|
6545
|
$stats['totalqueries'] = 0;
|
6546
|
$stats['queries'] = 0;
|
6547
|
|
6548
|
$db_handle = pfb_open_sqlite(3, 'Resolver collect queries');
|
6549
|
if ($db_handle) {
|
6550
|
$result = $db_handle->query("SELECT * FROM resolver WHERE row = 0;");
|
6551
|
if ($result) {
|
6552
|
while ($res = $result->fetchArray(SQLITE3_ASSOC)) {
|
6553
|
$stats = $res;
|
6554
|
$pfb_found = TRUE;
|
6555
|
}
|
6556
|
}
|
6557
|
|
6558
|
// Create new row
|
6559
|
if (!$pfb_found) {
|
6560
|
$db_update = "INSERT INTO resolver ( row, totalqueries, queries ) VALUES ( 0, 0, 0 );";
|
6561
|
$db_handle->exec("BEGIN TRANSACTION;"
|
6562
|
. "{$db_update}"
|
6563
|
. "END TRANSACTION;");
|
6564
|
}
|
6565
|
}
|
6566
|
pfb_close_sqlite($db_handle);
|
6567
|
|
6568
|
// If Unbound Resolver pid has changed, clear SQLite database 'queries' entry, and update 'totalqueries/pid' entries
|
6569
|
if ($pfb_found && $pid != $p_pid) {
|
6570
|
$totalqueries = ($stats['totalqueries'] ?: 0) + ($query ?: 0);
|
6571
|
pfBlockerNG_clearsqlite('update_totalqueries', $totalqueries);
|
6572
|
}
|
6573
|
else {
|
6574
|
if ($query != '' && $query != $p_query && is_numeric($query)) {
|
6575
|
|
6576
|
// Update existing row
|
6577
|
$db_handle = pfb_open_sqlite(3, 'Widget update queries');
|
6578
|
if ($db_handle) {
|
6579
|
$db_update = "UPDATE resolver SET queries = :query WHERE row = 0";
|
6580
|
$stmt = $db_handle->prepare($db_update);
|
6581
|
if ($stmt) {
|
6582
|
$stmt->bindValue(':query', $query, SQLITE3_INTEGER);
|
6583
|
$stmt->execute();
|
6584
|
}
|
6585
|
}
|
6586
|
pfb_close_sqlite($db_handle);
|
6587
|
}
|
6588
|
}
|
6589
|
|
6590
|
$p_pid = $pid;
|
6591
|
if (isset($t_query)) {
|
6592
|
$p_query = $t_query;
|
6593
|
unset($t_query);
|
6594
|
} else {
|
6595
|
$p_query = $query;
|
6596
|
}
|
6597
|
}
|
6598
|
}
|
6599
|
|
6600
|
|
6601
|
// Read logfile in realtime (livetail)
|
6602
|
// Reference: http://stackoverflow.com/questions/3218895/php-how-to-read-a-file-live-that-is-constantly-being-written-to
|
6603
|
function pfb_livetail($logfile, $mode) {
|
6604
|
global $pfb;
|
6605
|
|
6606
|
if (!file_exists("{$logfile}")) {
|
6607
|
touch("{$logfile}");
|
6608
|
}
|
6609
|
|
6610
|
$len = @filesize("{$logfile}"); // Start at EOF
|
6611
|
$lastpos_old = $pfb_output = '';
|
6612
|
|
6613
|
if ($mode == 'view') {
|
6614
|
// Start at EOF ( - 15000)
|
6615
|
if ($len > 15000) {
|
6616
|
$lastpos = ($len - 15000);
|
6617
|
} else {
|
6618
|
$lastpos = 0;
|
6619
|
}
|
6620
|
}
|
6621
|
else {
|
6622
|
$lastpos = $len;
|
6623
|
}
|
6624
|
|
6625
|
while (TRUE) {
|
6626
|
usleep(300000); //0.3s
|
6627
|
clearstatcache(false, "{$logfile}");
|
6628
|
$len = @filesize("{$logfile}");
|
6629
|
|
6630
|
if ($len < $lastpos) {
|
6631
|
$lastpos = $len; // File deleted or reset
|
6632
|
}
|
6633
|
else {
|
6634
|
$f = @fopen("{$logfile}", 'rb+');
|
6635
|
if ($f === false) {
|
6636
|
break;
|
6637
|
}
|
6638
|
@fseek($f, $lastpos);
|
6639
|
|
6640
|
while (!feof($f)) {
|
6641
|
$pfb_buffer = @fread($f, 2048);
|
6642
|
$pfb_output .= str_replace( array ("\r", "\")"), '', $pfb_buffer);
|
6643
|
// Refresh on new lines only. This allows Scrolling.
|
6644
|
if ($lastpos != $lastpos_old) {
|
6645
|
pfbupdate_output($pfb_output);
|
6646
|
}
|
6647
|
$lastpos_old = $lastpos;
|
6648
|
ob_flush();
|
6649
|
flush();
|
6650
|
}
|
6651
|
|
6652
|
$lastpos = @ftell($f);
|
6653
|
if ($f) {
|
6654
|
@fclose($f);
|
6655
|
}
|
6656
|
|
6657
|
// Capture remaining output
|
6658
|
if ($mode != 'view' && strpos($pfb_output, 'UPDATE PROCESS ENDED') !== FALSE) {
|
6659
|
$f = @fopen($pfb['log'], 'rb');
|
6660
|
@fseek($f, $lastpos);
|
6661
|
$pfb_buffer = @fread($f, 2048);
|
6662
|
$pfb_output .= str_replace( "\r", '', $pfb_buffer);
|
6663
|
pfbupdate_output($pfb_output);
|
6664
|
clearstatcache(false, $pfb['log']);
|
6665
|
ob_flush();
|
6666
|
flush();
|
6667
|
if ($f) {
|
6668
|
@fclose($f);
|
6669
|
}
|
6670
|
|
6671
|
// Call log mgmt function
|
6672
|
pfb_log_mgmt();
|
6673
|
break;
|
6674
|
}
|
6675
|
}
|
6676
|
}
|
6677
|
}
|
6678
|
|
6679
|
|
6680
|
// Load/convert Feeds (w/alternative aliasname(s), if user-configured) and return as array
|
6681
|
function convert_feeds_json() {
|
6682
|
global $pfb;
|
6683
|
|
6684
|
$aconfig = config_get_path('installedpackages/pfblockerngglobal', []);
|
6685
|
$pfb['feeds_list'] = $merge_feeds = $feed_info = array();
|
6686
|
|
6687
|
$feed_info_raw = json_decode(@file_get_contents("{$pfb['feeds']}"), TRUE);
|
6688
|
if (json_last_error() !== JSON_ERROR_NONE || !is_array($feed_info_raw)) {
|
6689
|
return array('blank' => '');
|
6690
|
}
|
6691
|
|
6692
|
$feed_count = array();
|
6693
|
foreach ($feed_info_raw as $type => $info) {
|
6694
|
|
6695
|
if (!is_array($info) || (isset($info[0]) && $info[0] == '*')) {
|
6696
|
continue;
|
6697
|
}
|
6698
|
|
6699
|
$feed_count[$type] = 0;
|
6700
|
foreach ($info as $aliasname => $data) {
|
6701
|
$l_aliasname = strtolower($aliasname);
|
6702
|
|
6703
|
$feed_count[$type] = $feed_count[$type] + count($data['feeds']);
|
6704
|
foreach ($data['feeds'] as $key => $feed) {
|
6705
|
|
6706
|
// Remove discontinued Feeds
|
6707
|
if (isset($feed['status']) && $feed['status'] == 'discontinued') {
|
6708
|
unset($data['feeds'][$key]);
|
6709
|
$feed_count[$type]--;
|
6710
|
continue;
|
6711
|
}
|
6712
|
|
6713
|
if (isset($feed['alternate'])) {
|
6714
|
foreach ($feed['alternate'] as $alternate) {
|
6715
|
$feed_count[$type]++;
|
6716
|
}
|
6717
|
}
|
6718
|
}
|
6719
|
|
6720
|
if (!array_key_exists($type, $pfb['feeds_list'])) {
|
6721
|
$pfb['feeds_list'][$type] = array();
|
6722
|
}
|
6723
|
if (!array_key_exists($type, $feed_info)) {
|
6724
|
$feed_info[$type] = array();
|
6725
|
}
|
6726
|
|
6727
|
// Use alternative Aliasname(s) and/or merge multiple aliasname Feeds together (if user configured)
|
6728
|
if (!empty($aconfig['feed_' . $l_aliasname])) {
|
6729
|
|
6730
|
$alt_feed = $aconfig['feed_' . $l_aliasname];
|
6731
|
$pfb['feeds_list'][$type][$aliasname] = $alt_feed; // Global list of all known Feed aliasnames
|
6732
|
|
6733
|
if (!is_array($merge_feeds[$alt_feed])) {
|
6734
|
$merge_feeds[$alt_feed] = array();
|
6735
|
}
|
6736
|
$merge_feeds[$alt_feed] = array_merge( $merge_feeds[$alt_feed], (array)$data['feeds'] );
|
6737
|
|
6738
|
if (!isset($feed_info[$type][$alt_feed])) {
|
6739
|
|
6740
|
// Modify 'info' and 'description' fields to reference user-defined aliasname
|
6741
|
foreach (array('info', 'description') as $atype) {
|
6742
|
$match = strpos($data[$atype], $aliasname);
|
6743
|
if ($match !== FALSE) {
|
6744
|
$data[$atype] = substr_replace($data[$atype], $aconfig['feed_' . $l_aliasname], $match, strlen($aliasname));
|
6745
|
}
|
6746
|
}
|
6747
|
$feed_info[$type][$aconfig['feed_' . $l_aliasname]] = $data;
|
6748
|
}
|
6749
|
$feed_info[$type][$alt_feed]['feeds'] = $merge_feeds[$alt_feed];
|
6750
|
}
|
6751
|
else {
|
6752
|
$pfb['feeds_list'][$type][$aliasname] = $aliasname;
|
6753
|
$feed_info[$type][$aliasname] = $data;
|
6754
|
}
|
6755
|
}
|
6756
|
}
|
6757
|
$feed_info['count'] = $feed_count;
|
6758
|
return $feed_info;
|
6759
|
}
|
6760
|
|
6761
|
|
6762
|
// Define Alerts Tab 'default GET request' (Top row)
|
6763
|
function pfb_alerts_default_page() {
|
6764
|
global $pfb;
|
6765
|
|
6766
|
if (isset($pfb['config_global']) &&
|
6767
|
isset($pfb['config_global']['pfbpageload'])) {
|
6768
|
switch($pfb['config_global']['pfbpageload']) {
|
6769
|
case 'dnsbl_stat':
|
6770
|
return '?view=dnsbl_stat';
|
6771
|
case 'dnsbl_reply_stat':
|
6772
|
return '?view=dnsbl_reply_stat';
|
6773
|
case 'ip_block_stat':
|
6774
|
return '?view=ip_block_stat';
|
6775
|
case 'ip_permit_stat':
|
6776
|
return '?view=ip_permit_stat';
|
6777
|
case 'ip_match_stat':
|
6778
|
return '?view=ip_match_stat';
|
6779
|
case 'reply':
|
6780
|
return '?view=reply';
|
6781
|
case 'unified':
|
6782
|
return '?view=unified';
|
6783
|
}
|
6784
|
}
|
6785
|
return '';
|
6786
|
}
|
6787
|
|
6788
|
|
6789
|
// Clear IP Alias Packet Counts (widget)
|
6790
|
function pfBlockerNG_clearip() {
|
6791
|
global $pfb;
|
6792
|
|
6793
|
exec("{$pfb['pfctl']} -z 2>&1");
|
6794
|
|
6795
|
/* TODO: Clear only pfB counters
|
6796
|
$pfb_tables = array();
|
6797
|
exec("{$pfb['pfctl']} -sTables | {$pfb['grep']} 'pfB_' 2>&1", $pfb_tables);
|
6798
|
if (!empty($pfb_tables)) {
|
6799
|
foreach ($pfb_tables as $table) {
|
6800
|
exec("{$pfb['pfctl']} -t {$table} -T zero");
|
6801
|
}
|
6802
|
}
|
6803
|
*/
|
6804
|
}
|
6805
|
|
6806
|
|
6807
|
// Clear DNSBL SQLite database statistics/queries as required
|
6808
|
function pfBlockerNG_clearsqlite($mode, $totalqueries = 0) {
|
6809
|
global $g, $pfb;
|
6810
|
|
6811
|
// Format of todo array: database, error message, SQLite command
|
6812
|
$todo = array();
|
6813
|
|
6814
|
// Clear SQLite database 'queries' entry and update totalqueries (+queries), if Unbound Resolver PID changed (Reload)
|
6815
|
if ($mode == 'update_totalqueries') {
|
6816
|
$todo[] = array(3, 'Clear Resolver queries', 'UPDATE resolver SET totalqueries = :totalqueries, queries = 0 WHERE row = 0;');
|
6817
|
}
|
6818
|
elseif ($mode == 'clearip') {
|
6819
|
$lastipclear = date('M j H:i:s', time());
|
6820
|
$todo[] = array(6, 'Reset IP last clear timestamp', 'UPDATE lastclear SET lastipclear = :lastipclear WHERE row = 0;');
|
6821
|
}
|
6822
|
elseif ($mode == 'cleardnsbl') {
|
6823
|
$lastdnsblclear = date('M j H:i:s', time());
|
6824
|
$todo[] = array(1, 'Clear Widget counters', 'UPDATE dnsbl SET counter = 0;');
|
6825
|
$todo[] = array(3, 'Clear Resolver queries', 'UPDATE resolver SET totalqueries = 0, queries = 0;');
|
6826
|
$todo[] = array(6, 'Reset DNSBL last clear timestamp', 'UPDATE lastclear SET lastdnsblclear = :lastdnsblclear WHERE row = 0;');
|
6827
|
}
|
6828
|
|
6829
|
// Clear Unbound Resolver statistics
|
6830
|
if ($mode != 'clearip' && is_process_running('unbound')) {
|
6831
|
exec("{$pfb['chroot_cmd']} flush_stats 2>&1");
|
6832
|
}
|
6833
|
|
6834
|
if (!empty($todo)) {
|
6835
|
foreach ($todo as $data) {
|
6836
|
$db_handle = pfb_open_sqlite($data[0], $data[1]);
|
6837
|
|
6838
|
if ($db_handle) {
|
6839
|
if ($mode == 'update_totalqueries') {
|
6840
|
if (is_numeric($totalqueries)) {
|
6841
|
$stmt = $db_handle->prepare($data[2]);
|
6842
|
if ($stmt) {
|
6843
|
$stmt->bindValue(':totalqueries', $totalqueries, SQLITE3_INTEGER);
|
6844
|
$stmt->execute();
|
6845
|
}
|
6846
|
}
|
6847
|
}
|
6848
|
elseif ($mode == 'clearip') {
|
6849
|
$stmt = $db_handle->prepare($data[2]);
|
6850
|
if ($stmt) {
|
6851
|
$stmt->bindValue(':lastipclear', $lastipclear, SQLITE3_TEXT);
|
6852
|
$stmt->execute();
|
6853
|
}
|
6854
|
}
|
6855
|
elseif ($mode == 'cleardnsbl') {
|
6856
|
$stmt = $db_handle->prepare($data[2]);
|
6857
|
if ($stmt) {
|
6858
|
$stmt->bindValue(':lastdnsblclear', $lastdnsblclear, SQLITE3_TEXT);
|
6859
|
$stmt->execute();
|
6860
|
}
|
6861
|
}
|
6862
|
else {
|
6863
|
$db_handle->exec("BEGIN TRANSACTION;"
|
6864
|
. "{$data[2]}"
|
6865
|
. "END TRANSACTION;");
|
6866
|
}
|
6867
|
}
|
6868
|
pfb_close_sqlite($db_handle);
|
6869
|
}
|
6870
|
}
|
6871
|
}
|
6872
|
|
6873
|
|
6874
|
// Function to read/lock/unlock IP/Domains from Aliastables/DNSBL (Called via Alerts Page)
|
6875
|
function pfb_unlock($mode, $type, $remove='', $r_type='', $filename_unlock) {
|
6876
|
global $pfb;
|
6877
|
|
6878
|
if ($type == 'ip') {
|
6879
|
$filename = $pfb['ip_unlock'];
|
6880
|
} elseif ($type == 'dnsbl') {
|
6881
|
$filename = $pfb['dnsbl_unlock'];
|
6882
|
} elseif ($type == 'dnsbl_data') {
|
6883
|
$filename = "{$pfb['dnsbl_unlock']}.data";
|
6884
|
} else {
|
6885
|
return;
|
6886
|
}
|
6887
|
|
6888
|
if ($mode == 'read') {
|
6889
|
$filename_unlock = array();
|
6890
|
if (($handle = @fopen("{$filename}", 'r')) !== FALSE) {
|
6891
|
while (($line = @fgetcsv($handle)) !== FALSE) {
|
6892
|
if (!empty($line)) {
|
6893
|
$filename_unlock[$line[0]] = $line[1];
|
6894
|
}
|
6895
|
}
|
6896
|
}
|
6897
|
if ($handle) {
|
6898
|
@fclose($handle);
|
6899
|
}
|
6900
|
|
6901
|
if (empty($filename_unlock)) {
|
6902
|
unlink_if_exists("{$filename}");
|
6903
|
}
|
6904
|
return $filename_unlock;
|
6905
|
}
|
6906
|
elseif ($mode == 'dnsbl_data') {
|
6907
|
$filename_unlock = '';
|
6908
|
$data = array();
|
6909
|
if (($handle = @fopen("{$filename}", 'r')) !== FALSE) {
|
6910
|
while (($line = @fgets($handle)) !== FALSE) {
|
6911
|
if (strpos($line, $remove) !== FALSE) {
|
6912
|
$data = explode(',', $line);
|
6913
|
} else {
|
6914
|
$filename_unlock .= "{$line}";
|
6915
|
}
|
6916
|
}
|
6917
|
}
|
6918
|
if ($handle) {
|
6919
|
@fclose($handle);
|
6920
|
}
|
6921
|
|
6922
|
if (empty($filename_unlock)) {
|
6923
|
unlink_if_exists("{$filename}");
|
6924
|
} else {
|
6925
|
@file_put_contents("{$filename}", "{$filename_unlock}", LOCK_EX);
|
6926
|
}
|
6927
|
return $data;
|
6928
|
}
|
6929
|
elseif ($mode == 'unlock' && isset($filename_unlock[$remove])) {
|
6930
|
return;
|
6931
|
}
|
6932
|
elseif (empty($remove)) {
|
6933
|
return;
|
6934
|
}
|
6935
|
|
6936
|
// Add/Remove IP/Domain in unlock file
|
6937
|
if (($pfb_output = @fopen("{$filename}", 'w')) !== FALSE) {
|
6938
|
foreach ($filename_unlock as $key => $line) {
|
6939
|
|
6940
|
// 'Remove locked IP/Domains' or 'Add existing unlocked IP/Domain' in unlock file
|
6941
|
if ($mode == 'unlock' || ($mode == 'lock' && $key != $remove)) {
|
6942
|
@fwrite($pfb_output, "{$key},{$line}\n");
|
6943
|
}
|
6944
|
}
|
6945
|
|
6946
|
// Add IP/Domain to unlock file
|
6947
|
if ($mode == 'unlock') {
|
6948
|
$filename_unlock[$remove] = $r_type;
|
6949
|
@fwrite($pfb_output, "{$remove},{$r_type}\n");
|
6950
|
}
|
6951
|
}
|
6952
|
if ($pfb_output) {
|
6953
|
@fclose($pfb_output);
|
6954
|
}
|
6955
|
|
6956
|
if (empty($filename_unlock)) {
|
6957
|
unlink_if_exists("{$filename}");
|
6958
|
}
|
6959
|
}
|
6960
|
|
6961
|
|
6962
|
// Function to clear pfBlockerNG folder/files
|
6963
|
function pfb_clear_contents() {
|
6964
|
global $pfb;
|
6965
|
|
6966
|
unlink_if_exists("{$pfb['dbdir']}/masterfile");
|
6967
|
unlink_if_exists("{$pfb['dbdir']}/mastercat");
|
6968
|
unlink_if_exists("{$pfb['supptxt']}");
|
6969
|
unlink_if_exists("{$pfb['dnsbl_supptxt']}");
|
6970
|
unlink_if_exists("{$pfb['dnsbl_info']}");
|
6971
|
unlink_if_exists("{$pfb['dnsbl_resolver']}");
|
6972
|
unlink_if_exists("{$pfb['dnsbl_cache']}");
|
6973
|
unlink_if_exists("/var/tmp/unbound_cache_*");
|
6974
|
unlink_if_exists("{$pfb['asn_cache']}");
|
6975
|
unlink_if_exists("{$pfb['ip_cache']}");
|
6976
|
rmdir_recursive("{$pfb['origdir']}");
|
6977
|
rmdir_recursive("{$pfb['matchdir']}");
|
6978
|
rmdir_recursive("{$pfb['permitdir']}");
|
6979
|
rmdir_recursive("{$pfb['denydir']}");
|
6980
|
rmdir_recursive("{$pfb['nativedir']}");
|
6981
|
rmdir_recursive("{$pfb['etdir']}");
|
6982
|
rmdir_recursive("{$pfb['dnsdir']}");
|
6983
|
rmdir_recursive("{$pfb['dnsorigdir']}");
|
6984
|
rmdir_recursive("{$pfb['dnsalias']}");
|
6985
|
}
|
6986
|
|
6987
|
|
6988
|
// Main pfBlockerNG function
|
6989
|
function sync_package_pfblockerng($cron='') {
|
6990
|
global $g, $pfb, $pfbarr;
|
6991
|
pfb_global();
|
6992
|
|
6993
|
$pfb['conf_mod'] = FALSE; // Flag to check for mods to the config.xml file. ('$pfb_config' array to hold changes)
|
6994
|
$pfb['filter_configure'] = FALSE; // Flag to call filter_configure once
|
6995
|
|
6996
|
// Detect boot process or package installation
|
6997
|
if (platform_booting() || $g['pfblockerng_install']) {
|
6998
|
// Create DNSBL NAT, VIP, Lighttpd service and certs if required on reboot.
|
6999
|
if ($pfb['dnsbl'] == 'on') {
|
7000
|
pfb_create_dnsbl('enabled');
|
7001
|
}
|
7002
|
$log = 'Sync terminated during boot process.';
|
7003
|
pfb_logger("\n{$log}\nUPDATE PROCESS ENDED [ NOW ]\n", 1);
|
7004
|
log_error("[pfBlockerNG] {$log}");
|
7005
|
return;
|
7006
|
}
|
7007
|
|
7008
|
// Reloads existing lists without downloading new lists when defined 'on'
|
7009
|
$pfb['reuse'] = $pfb['config']['pfb_reuse'];
|
7010
|
$pfb['reuse_dnsbl'] = '';
|
7011
|
|
7012
|
// Define update process (update or reload)
|
7013
|
switch ($cron) {
|
7014
|
case 'noupdates':
|
7015
|
// Force update - Set 'save' variable when 'No updates' found.
|
7016
|
$pfb['save'] = TRUE;
|
7017
|
break;
|
7018
|
case 'cron':
|
7019
|
if ($pfb['reuse'] == 'on') {
|
7020
|
$pfb['reuse_dnsbl'] = 'on';
|
7021
|
unlink_if_exists("{$pfb['dbdir']}/masterfile");
|
7022
|
unlink_if_exists("{$pfb['dbdir']}/mastercat");
|
7023
|
}
|
7024
|
break;
|
7025
|
case 'updatednsbl':
|
7026
|
$pfb['reuse'] = '';
|
7027
|
$pfb['reuse_dnsbl'] = 'on';
|
7028
|
$pfb['updatednsbl'] = TRUE;
|
7029
|
break;
|
7030
|
case 'updateip':
|
7031
|
$pfb['reuse'] = 'on';
|
7032
|
$pfb['reuse_dnsbl'] = '';
|
7033
|
unlink_if_exists("{$pfb['dbdir']}/masterfile");
|
7034
|
unlink_if_exists("{$pfb['dbdir']}/mastercat");
|
7035
|
break;
|
7036
|
}
|
7037
|
|
7038
|
// Start of pfBlockerNG logging to 'pfblockerng.log'
|
7039
|
if ($pfb['enable'] == 'on' && !$pfb['save']) {
|
7040
|
$log = " UPDATE PROCESS START [ " . pfb_pkg_ver() . " ] [ NOW ]\n";
|
7041
|
pfb_logger("{$log}", 1);
|
7042
|
} else {
|
7043
|
if ($cron != 'noupdates') {
|
7044
|
$log = "\n**Saving configuration [ NOW ]**\n";
|
7045
|
pfb_logger("{$log}", 1);
|
7046
|
}
|
7047
|
}
|
7048
|
|
7049
|
// Call function for Ramdisk processes.
|
7050
|
pfb_aliastables('conf');
|
7051
|
|
7052
|
// If table limit not defined, set default to 2M
|
7053
|
if (empty(config_get_path('system/maximumtableentries'))) {
|
7054
|
config_set_path('system/maximumtableentries', '2000000');
|
7055
|
write_config('pfBlockerNG: save max Firewall table entries limit', false);
|
7056
|
}
|
7057
|
$pfb['table_limit'] = config_get_path('system/maximumtableentries');
|
7058
|
|
7059
|
// Collect local web gui configuration
|
7060
|
$pfb['weblocal'] = config_get_path('system/webgui/protocol', 'http');
|
7061
|
$pfb['port'] = config_get_path('system/webgui/port');
|
7062
|
if (empty($pfb['port'])) {
|
7063
|
if (config_get_path('system/webgui/protocol') == 'http') {
|
7064
|
$pfb['port'] = '80';
|
7065
|
} else {
|
7066
|
$pfb['port'] = '443';
|
7067
|
}
|
7068
|
}
|
7069
|
$pfb['weblocal'] .= "://127.0.0.1:{$pfb['port']}/pfblockerng/pfblockerng.php";
|
7070
|
|
7071
|
// Define Inbound/Outbound action is not user selected.
|
7072
|
$pfb['deny_action_inbound'] = $pfb['ipconfig']['inbound_deny_action'] ?: 'block';
|
7073
|
$pfb['deny_action_outbound'] = $pfb['ipconfig']['outbound_deny_action'] ?: 'reject';
|
7074
|
|
7075
|
$pfb['float'] = $pfb['ipconfig']['enable_float']; // Enable/Disable floating autorules
|
7076
|
$pfb['dup'] = $pfb['ipconfig']['enable_dup']; // Enable remove of duplicate IPs utilizing grepcidr
|
7077
|
$pfb['agg'] = $pfb['ipconfig']['enable_agg']; // Enable aggregation of CIDRs
|
7078
|
$pfb['order'] = $pfb['ipconfig']['pass_order']; // Order of the autorules
|
7079
|
$pfb['global_log'] = $pfb['ipconfig']['enable_log']; // Enable Global IP logging
|
7080
|
$pfb['suffix'] = $pfb['ipconfig']['autorule_suffix']; // Suffix used for autorules
|
7081
|
$pfb['kstates'] = $pfb['ipconfig']['killstates']; // Firewall states removal
|
7082
|
|
7083
|
$pfb['ip_ph'] = pfb_filter($pfb['ipconfig']['ip_placeholder'], PFB_FILTER_IPV4, 'Placeholder IP Address', '127.1.7.7'); // Placeholder IP Address
|
7084
|
|
7085
|
// DNSBL settings
|
7086
|
$pfb['dnsbl_ip'] = $pfb['dnsblconfig']['action'] ?: 'Disabled'; // Enable/Disable IP blocking from DNSBL lists
|
7087
|
$pfb['dnsbl_rule'] = $pfb['dnsblconfig']['pfb_dnsbl_rule'] ?: 'Disabled'; // Auto create a Floating Pass Rule for other Lan subnets
|
7088
|
$pfb['dnsbl_alexa_cnt'] = $pfb['dnsblconfig']['alexa_count'] ?: '1000'; // TOP1M whitelist domain setting
|
7089
|
$pfb['dnsbl_alexa_inc'] = $pfb['dnsblconfig']['alexa_inclusion']?: ''; // TOP1M TLDs inclusions for whitelisting
|
7090
|
$pfb['dnsbl_tld'] = $pfb['dnsblconfig']['pfb_tld']; // Enable TLD Function
|
7091
|
$pfb['dnsbl_control'] = $pfb['dnsblconfig']['pfb_control'] ?: ''; // Python Control integration
|
7092
|
|
7093
|
// Validate pfB Script variable
|
7094
|
$pfb['dnsbl_alexa_inc'] = pfb_filter($pfb['dnsbl_alexa_inc'], PFB_FILTER_CSV, 'Validate pfB Script variable');
|
7095
|
|
7096
|
// Reputation config variables
|
7097
|
$pfb['config_rep'] = config_get_path('installedpackages/pfblockerngreputation/config/0', []);
|
7098
|
|
7099
|
// Validate pfB Script variables
|
7100
|
foreach (array(
|
7101
|
'enable_rep' => 'rep', // Enable/Disable 'Max' Reputation
|
7102
|
'enable_pdup' => 'prep', // Enable/Disable 'pRep' Reputation
|
7103
|
'enable_dedup' => 'drep', // Enable/Disable 'dRep' Reputation
|
7104
|
'et_update' => 'etupdate', // Perform a Force Update on ET categories
|
7105
|
'ccwhite' => 'ccwhite', // Action for whitelist Country category
|
7106
|
'ccblack' => 'ccblack', // Action for blacklist Country category
|
7107
|
'etblock' => 'etblock', // Emerging Threats IQRisk block categories
|
7108
|
'etmatch' => 'etmatch', // Emerging Threats IQRisk match categories
|
7109
|
'p24_max_var' => 'max', // 'Max' variable setting for Reputation
|
7110
|
'p24_dmax_var' => 'dmax', // 'dMax' variable setting for Reputation
|
7111
|
'p24_pmax_var' => 'pmax', // 'pMax' variable setting for Reputation
|
7112
|
'ccexclude' => 'ccexclude' // List of Countries to whitelist
|
7113
|
) as $conf_value => $pfb_value) {
|
7114
|
|
7115
|
$pfb_variable = $pfb['config_rep'][$conf_value] ?: 'x';
|
7116
|
if (empty(pfb_filter($pfb_variable, PFB_FILTER_CSV, 'Validate pfB Script variables'))) {
|
7117
|
$pfb[$pfb_value] = 'x';
|
7118
|
} else {
|
7119
|
$pfb[$pfb_value] = $pfb_variable;
|
7120
|
}
|
7121
|
}
|
7122
|
|
7123
|
// Starting variable to skip Reputation functions, if no changes are required
|
7124
|
$pfb['repcheck'] = FALSE;
|
7125
|
// $pfb['save'] is used to determine if user pressed "save" button to avoid collision with CRON.
|
7126
|
|
7127
|
// For 'script' calls using exec() (used to shorten length of line)
|
7128
|
$elog = ">> {$pfb['log']} 2>&1";
|
7129
|
|
7130
|
|
7131
|
#################################
|
7132
|
# Configure ARRAYS #
|
7133
|
#################################
|
7134
|
|
7135
|
$new_aliases = array(); // An array of aliases (full details)
|
7136
|
$new_aliases_list = array(); // An array of alias names
|
7137
|
$pfb_alias_lists = array(); // An array of aliases that have updated lists via CRON/force update. ('Reputation' disabled)
|
7138
|
$pfb_alias_lists_all = array(); // An array of all active aliases. ('Reputation' enabled)
|
7139
|
|
7140
|
$ip_types = array( 'pfblockernglistsv4' => '_v4', 'pfblockernglistsv6' => '_v6');
|
7141
|
$cont_types = array( 'countries4' => '_v4', 'countries6' => '_v6');
|
7142
|
|
7143
|
#################################
|
7144
|
# Tracker IDs #
|
7145
|
#################################
|
7146
|
|
7147
|
$pfb['trackerids'] = array(); // An array of pfBlockerNG Firewall rule Tracker IDs.
|
7148
|
$pfb['last_trackerid'] = 1700000009; // Pre-defined 'starting' Tracker ID (Only used if duplicates found)
|
7149
|
|
7150
|
|
7151
|
#########################################
|
7152
|
# Configure Rule Suffix #
|
7153
|
#########################################
|
7154
|
|
7155
|
// Discover if any rules are autorules (If no autorules found, $pfb['autorules'] is FALSE, skip rules re-order )
|
7156
|
// To configure auto rule suffix. pfBlockerNG must be disabled to change suffix and to avoid duplicate rules
|
7157
|
$pfb['autorules'] = FALSE;
|
7158
|
$action = array('Deny_Both', 'Deny_Inbound', 'Deny_Outbound', 'Match_Both', 'Match_Inbound',
|
7159
|
'Match_Outbound', 'Permit_Both', 'Permit_Inbound', 'Permit_Outbound');
|
7160
|
|
7161
|
foreach ($pfb['continents'] as $continent => $pfb_alias) {
|
7162
|
$cont_key = 'pfblockerng' . strtolower(str_replace(' ', '', $continent));
|
7163
|
if (!empty(config_get_path("installedpackages/{$cont_key}/config"))) {
|
7164
|
$continent_config = config_get_path("installedpackages/{$cont_key}/config/0");
|
7165
|
if ($continent_config['action'] != 'Disabled' && in_array($continent_config['action'], $action)) {
|
7166
|
$pfb['autorules'] = TRUE;
|
7167
|
break;
|
7168
|
}
|
7169
|
}
|
7170
|
}
|
7171
|
|
7172
|
if (!$pfb['autorules']) {
|
7173
|
foreach ($ip_types as $ip_type => $vtype) {
|
7174
|
foreach(config_get_path("installedpackages/{$ip_type}/config", []) as $list) {
|
7175
|
if ($list['action'] != 'Disabled' && in_array($list['action'], $action)) {
|
7176
|
$pfb['autorules'] = TRUE;
|
7177
|
break;
|
7178
|
}
|
7179
|
}
|
7180
|
}
|
7181
|
}
|
7182
|
|
7183
|
// Check if DNSBL auto permit rule or DNSBL 'Auto Deny' rules for DNSBL IPs are defined
|
7184
|
if (!empty($pfb['dnsblconfig']['dnsbl_allow_int']) || strpos($pfb['dnsblconfig']['action'], 'Deny_') !== FALSE) {
|
7185
|
$pfb['autorules'] = TRUE;
|
7186
|
}
|
7187
|
|
7188
|
// Configure auto rule suffix.
|
7189
|
$pfbfound = FALSE;
|
7190
|
$pfb_suffix_match = '';
|
7191
|
foreach (config_get_path('filter/rule', []) as $rule) {
|
7192
|
|
7193
|
// Query for previous IPv4 pfBlockerNG 'alias type' aliasnames which are not in the new '_v4' suffix format
|
7194
|
foreach (array('source', 'destination') as $rtype) {
|
7195
|
if (substr($rule[$rtype]['address'], 0, 4) == 'pfB_' &&
|
7196
|
substr($rule[$rtype]['address'], -3) != '_v4' &&
|
7197
|
$rule['ipprotocol'] == 'inet') {
|
7198
|
$pfb['autorules'] = TRUE; // Set flag to re-configure Firewall rules and add missing '_v4' suffix
|
7199
|
}
|
7200
|
}
|
7201
|
|
7202
|
// Collect any pre-existing suffix
|
7203
|
if (!empty($pfb_suffix_match) && preg_match('/^pfB_\w+(\s.*)/', $rule['descr'], $pfb_suffix_real)) {
|
7204
|
$pfb_suffix_match = $pfb_suffix_real[1];
|
7205
|
}
|
7206
|
}
|
7207
|
|
7208
|
// Change suffix only if no pfB rules found and autorules are enabled.
|
7209
|
if ($pfb['autorules'] && !$pfbfound) {
|
7210
|
switch ($pfb['suffix']) {
|
7211
|
case 'autorule':
|
7212
|
$pfb['suffix'] = ' auto rule';
|
7213
|
break;
|
7214
|
case 'standard':
|
7215
|
$pfb['suffix'] = '';
|
7216
|
break;
|
7217
|
case 'ar':
|
7218
|
$pfb['suffix'] = ' AR';
|
7219
|
break;
|
7220
|
}
|
7221
|
} else {
|
7222
|
$pfb['suffix'] = '';
|
7223
|
if ($pfb['autorules']) {
|
7224
|
$pfb['suffix'] = $pfb_suffix_match; // Use existing suffix match
|
7225
|
}
|
7226
|
}
|
7227
|
|
7228
|
|
7229
|
#########################################################
|
7230
|
# Configure INBOUND/OUTBOUND INTERFACES #
|
7231
|
#########################################################
|
7232
|
|
7233
|
// Collect pfSense interface order
|
7234
|
$ifaces = pfb_build_if_list(TRUE, FALSE);
|
7235
|
|
7236
|
foreach (array('inbound', 'outbound') as $type) {
|
7237
|
$pfb["{$type}_interfaces"] = $pfb["{$type}_floating"] = array();
|
7238
|
|
7239
|
if (!empty($pfb['ipconfig']["{$type}_interface"])) {
|
7240
|
$interfaces = explode(',', $pfb['ipconfig']["{$type}_interface"]);
|
7241
|
|
7242
|
// CSV string for 'pfB_' match rules
|
7243
|
$pfb["{$type}_floating"] = ltrim(implode(',', $interfaces), ',');
|
7244
|
|
7245
|
// Assign base rule/interfaces
|
7246
|
if ($pfb['float'] == 'on') {
|
7247
|
$pfb['base_rule'] = $pfb['base_rule_float'];
|
7248
|
$pfb["{$type}_interfaces"] = explode(' ', $pfb["{$type}_floating"]);
|
7249
|
} else {
|
7250
|
$pfb['base_rule'] = $pfb['base_rule_reg'];
|
7251
|
$pfb["{$type}_interfaces"] = $interfaces;
|
7252
|
}
|
7253
|
}
|
7254
|
}
|
7255
|
|
7256
|
// Determine max Domain count available for DNSBL TLD analysis (Avoid Unbound memory exhaustion)
|
7257
|
$pfs_memory = (round(get_single_sysctl('hw.physmem') / (1024*1024)) ?: 1000);
|
7258
|
|
7259
|
$pfb['pfs_mem'] = [
|
7260
|
'0' => '100000',
|
7261
|
'1500' => '150000',
|
7262
|
'2000' => '200000',
|
7263
|
'2500' => '250000',
|
7264
|
'3000' => '400000',
|
7265
|
'4000' => '600000',
|
7266
|
'5000' => '1000000',
|
7267
|
'6000' => '1500000',
|
7268
|
'7000' => '2000000',
|
7269
|
'8000' => '2500000',
|
7270
|
'12000' => '3000000',
|
7271
|
'16000' => '4000000',
|
7272
|
'32000' => '8000000'
|
7273
|
];
|
7274
|
|
7275
|
if ($pfb['dnsbl_py_blacklist']) {
|
7276
|
array_walk($pfb['pfs_mem'], function (&$value) {
|
7277
|
$value = $value * 3;
|
7278
|
});
|
7279
|
}
|
7280
|
|
7281
|
foreach ($pfb['pfs_mem'] as $pfb_mem => $domain_max) {
|
7282
|
if ($pfs_memory >= $pfb_mem) {
|
7283
|
$pfb['domain_max_cnt'] = $domain_max;
|
7284
|
}
|
7285
|
}
|
7286
|
|
7287
|
|
7288
|
#################################################
|
7289
|
# Clear Removed Lists from Masterfiles #
|
7290
|
#################################################
|
7291
|
|
7292
|
$pfb['sync_master'] = TRUE; // Process to keep IP Masterfiles in sync with valid Lists from config.conf file
|
7293
|
$pfb['remove'] = FALSE; // Flag to execute pfctl and rules ordering or reload of DNSBL domains
|
7294
|
$pfb['summary'] = FALSE; // Execute final summary as a list was removed
|
7295
|
|
7296
|
// Don't execute this function when pfBlockerNG is disabled and 'keep blocklists' is enabled.
|
7297
|
if ($pfb['enable'] == '' && $pfb['keep'] == 'on') {
|
7298
|
$pfb['sync_master'] = FALSE;
|
7299
|
}
|
7300
|
|
7301
|
if ($pfb['sync_master']) {
|
7302
|
|
7303
|
// Find all enabled Continents lists
|
7304
|
foreach ($pfb['continents'] as $continent => $pfb_alias) {
|
7305
|
$cont_key = 'pfblockerng' . strtolower(str_replace(' ', '', $continent));
|
7306
|
if (!empty(config_get_path("installedpackages/{$cont_key}/config")) && $pfb['enable'] == 'on') {
|
7307
|
$continent_config = config_get_path("installedpackages/{$cont_key}/config/0");
|
7308
|
if ($continent_config['action'] != 'Disabled') {
|
7309
|
foreach ($cont_types as $c_type => $vtype) {
|
7310
|
if (!empty($continent_config[$c_type])) {
|
7311
|
|
7312
|
// Force 'Alias Native' setting to any Alias with 'Advanced Inbound/Outbound -Invert src/dst' settings.
|
7313
|
// This will bypass Deduplication and Reputation features.
|
7314
|
if ($continent_config['autoaddrnot_in'] == 'on' ||
|
7315
|
$continent_config['autoaddrnot_out'] == 'on') {
|
7316
|
$pfb['existing']['native'][] = "{$pfb_alias}{$vtype}";
|
7317
|
}
|
7318
|
else {
|
7319
|
if (strpos($continent_config['action'], 'Match') !== FALSE) {
|
7320
|
$pfb['existing']['match'][] = "{$pfb_alias}{$vtype}";
|
7321
|
}
|
7322
|
elseif (strpos($continent_config['action'], 'Permit') !== FALSE) {
|
7323
|
$pfb['existing']['permit'][] = "{$pfb_alias}{$vtype}";
|
7324
|
}
|
7325
|
elseif (strpos($continent_config['action'], 'Deny') !== FALSE) {
|
7326
|
$pfb['existing']['deny'][] = "{$pfb_alias}{$vtype}";
|
7327
|
}
|
7328
|
elseif ($continent_config['action'] == 'Alias_Native') {
|
7329
|
$pfb['existing']['native'][] = "{$pfb_alias}{$vtype}";
|
7330
|
}
|
7331
|
}
|
7332
|
}
|
7333
|
}
|
7334
|
}
|
7335
|
}
|
7336
|
}
|
7337
|
|
7338
|
// Find all enabled IPv4/IPv6 lists and DNSBL lists
|
7339
|
// Find all enabled IPv4 'Custom List' header names and check if 'Emerging Threats Update' and 'Custom List Update' needs force updating
|
7340
|
$list_types = array( 'pfblockernglistsv4' => '_v4',
|
7341
|
'pfblockernglistsv6' => '_v6',
|
7342
|
'pfblockerngdnsbl' => '_v4'
|
7343
|
);
|
7344
|
|
7345
|
$pfb_invalid = FALSE;
|
7346
|
foreach ($list_types as $ltype => $vtype) {
|
7347
|
$lists = array();
|
7348
|
|
7349
|
$type_config = config_get_path("installedpackages/{$ltype}/config");
|
7350
|
if (!empty($type_config) && $pfb['enable'] == 'on') {
|
7351
|
foreach ($type_config as $list) {
|
7352
|
|
7353
|
// If only the 'customlist' is defined. Remove the 'List row' data.
|
7354
|
if (isset($list['row']) && empty($list['row'][0]['url'])) {
|
7355
|
unset($list['row']);
|
7356
|
}
|
7357
|
|
7358
|
if (!empty($list['custom'])) {
|
7359
|
$list['row'][] = array( 'header' => "{$list['aliasname']}_custom",
|
7360
|
'custom' => $list['custom'],
|
7361
|
'state' => 'Enabled',
|
7362
|
'url' => 'custom'
|
7363
|
);
|
7364
|
}
|
7365
|
$lists[] = $list;
|
7366
|
}
|
7367
|
}
|
7368
|
|
7369
|
// ADD DNSBL IP
|
7370
|
if ($ltype == 'pfblockernglistsv4' && $pfb['enable'] == 'on' && $pfb['dnsbl'] == 'on' && $pfb['dnsbl_ip'] != 'Disabled') {
|
7371
|
$list = array();
|
7372
|
$list['action'] = "{$pfb['dnsbl_ip']}";
|
7373
|
$list['row'][] = array('format' => 'auto',
|
7374
|
'state' => 'Enabled',
|
7375
|
'url' => "{$pfb['dbdir']}/DNSBLIP{$vtype}.txt",
|
7376
|
'header' => 'DNSBLIP');
|
7377
|
$lists[] = $list;
|
7378
|
}
|
7379
|
|
7380
|
if (!empty($lists)) {
|
7381
|
foreach ($lists as $key => $list) {
|
7382
|
|
7383
|
// Remove any spaces or special characters in existing Aliasnames
|
7384
|
if (preg_match("/\W/", $list['aliasname'])) {
|
7385
|
$pfb_invalid = TRUE;
|
7386
|
$list['aliasname'] = preg_replace("/\W/", '', $list['aliasname']);
|
7387
|
config_set_path("installedpackages/{$ltype}/config/{$key}/aliasname", $list['aliasname']);
|
7388
|
}
|
7389
|
|
7390
|
if (isset($list['row']) && $list['action'] != 'Disabled') {
|
7391
|
|
7392
|
// Force 'Alias Native' setting to any Alias with 'Advanced Inbound/Outbound -Invert src/dst' settings.
|
7393
|
// This will bypass Deduplication and Reputation features.
|
7394
|
if ($list['action'] != 'unbound' && ($list['autoaddrnot_in'] == 'on' ||
|
7395
|
$list['autoaddrnot_out'] == 'on')) {
|
7396
|
$list['action'] = 'Alias_Native';
|
7397
|
}
|
7398
|
|
7399
|
foreach ($list['row'] as $hkey => $row) {
|
7400
|
|
7401
|
// Remove any spaces or special characters in existing Header names
|
7402
|
if (preg_match("/\W/", $row['header'])) {
|
7403
|
$pfb_invalid = TRUE;
|
7404
|
$row['header'] = preg_replace("/\W/", '', $row['header']);
|
7405
|
config_set_path("installedpackages/{$ltype}/config/{$key}/row/{$hkey}/header", $row['header']);
|
7406
|
}
|
7407
|
|
7408
|
if ($ltype == 'pfblockerngdnsbl') {
|
7409
|
$header = "{$row['header']}";
|
7410
|
} else {
|
7411
|
$header = "{$row['header']}{$vtype}";
|
7412
|
}
|
7413
|
|
7414
|
// Collect enabled lists
|
7415
|
if (!empty($row['url']) && $row['state'] != 'Disabled') {
|
7416
|
if (strpos($list['action'], 'Match') !== FALSE) {
|
7417
|
$pfb['existing']['match'][] = "{$header}";
|
7418
|
}
|
7419
|
elseif (strpos($list['action'], 'Permit') !== FALSE) {
|
7420
|
$pfb['existing']['permit'][] = "{$header}";
|
7421
|
}
|
7422
|
elseif (strpos($list['action'], 'Deny') !== FALSE) {
|
7423
|
$pfb['existing']['deny'][] = "{$header}";
|
7424
|
}
|
7425
|
elseif ($list['action'] == 'Alias_Native') {
|
7426
|
$pfb['existing']['native'][] = "{$header}";
|
7427
|
}
|
7428
|
elseif ($list['action'] == 'unbound') {
|
7429
|
$pfb['existing']['dnsbl'][] = "{$header}";
|
7430
|
}
|
7431
|
}
|
7432
|
}
|
7433
|
}
|
7434
|
}
|
7435
|
}
|
7436
|
}
|
7437
|
|
7438
|
// Save any existing Alias/Header names that have spaces or special characters
|
7439
|
if ($pfb_invalid) {
|
7440
|
write_config('pfBlockerNG: Remove spaces/special characters in Alias/Header names');
|
7441
|
}
|
7442
|
|
7443
|
// If 'TLD' enabled and TLD Blacklists are defined, add to enabled DNSBL lists
|
7444
|
if ($pfb['dnsbl_tld']) {
|
7445
|
$tld_blacklist = pfbng_text_area_decode($pfb['dnsblconfig']['tldblacklist'], TRUE, FALSE, TRUE);
|
7446
|
if (!empty($tld_blacklist)) {
|
7447
|
$pfb['existing']['dnsbl'][] = 'DNSBL_TLD';
|
7448
|
}
|
7449
|
}
|
7450
|
|
7451
|
// Add 'Reputation - ccwhite Action' if found
|
7452
|
if ($pfb['ccwhite'] == 'match' && file_exists("{$pfb['matchdir']}/matchdedup_v4.txt")) {
|
7453
|
$pfb['existing']['match'][] = 'matchdedup_v4';
|
7454
|
}
|
7455
|
|
7456
|
// Add enabled 'DNSBL Blacklist categories'
|
7457
|
if (isset($pfb['blconfig']) &&
|
7458
|
$pfb['blconfig']['blacklist_enable'] != 'Disable' &&
|
7459
|
!empty($pfb['blconfig']['blacklist_selected'])) {
|
7460
|
|
7461
|
$selected = array_flip(explode(',', $pfb['blconfig']['blacklist_selected'])) ?: array();
|
7462
|
foreach ($pfb['blconfig']['item'] as $item) {
|
7463
|
|
7464
|
if (isset($selected[$item['xml']]) && !empty($item['selected'])) {
|
7465
|
$categories = explode(',', $item['selected']) ?: array();
|
7466
|
foreach ($categories as $category) {
|
7467
|
if (!empty($category)) {
|
7468
|
$category = str_replace('-', '_', $category);
|
7469
|
$pfb['existing']['dnsbl'][] = "{$item['title']}_{$category}";
|
7470
|
}
|
7471
|
}
|
7472
|
}
|
7473
|
}
|
7474
|
}
|
7475
|
|
7476
|
// Collect all .txt file names for each list type
|
7477
|
$list_types = array( 'match' => $pfb['matchdir'], 'permit' => $pfb['permitdir'], 'deny' => $pfb['denydir'],
|
7478
|
'native' => $pfb['nativedir'], 'dnsbl' => $pfb['dnsdir']);
|
7479
|
|
7480
|
// Collect all previouly downloaded filename headers
|
7481
|
foreach ($list_types as $pftype => $pfbfolder) {
|
7482
|
|
7483
|
$pfb_files = glob("{$pfbfolder}/*.txt");
|
7484
|
foreach ($pfb_files as $pfb_list) {
|
7485
|
$pfb['actual'][$pftype][] = basename($pfb_list, '.txt');
|
7486
|
}
|
7487
|
|
7488
|
$results = array_diff($pfb['actual'][$pftype], $pfb['existing'][$pftype]);
|
7489
|
|
7490
|
// Remove any DNSBL Orig files that are not referenced
|
7491
|
if ($pftype == 'dnsbl') {
|
7492
|
$orig_list = glob("{$pfb['dnsorigdir']}/*.orig");
|
7493
|
if (!empty($orig_list) && is_array($orig_list)) {
|
7494
|
$orig_list_final = array();
|
7495
|
foreach ($orig_list as $pfb_orig) {
|
7496
|
$orig_list_final[] = basename($pfb_orig, '.orig');
|
7497
|
}
|
7498
|
|
7499
|
$results_flip = array_flip($pfb['actual'][$pftype]);
|
7500
|
if (!empty($results_flip) && is_array($results_flip)) {
|
7501
|
foreach ($orig_list_final as $orig) {
|
7502
|
if (!isset($results_flip[$orig])) {
|
7503
|
unlink_if_exists("{$pfb['dnsorigdir']}/{$orig}.*");
|
7504
|
}
|
7505
|
}
|
7506
|
}
|
7507
|
}
|
7508
|
}
|
7509
|
|
7510
|
if (empty($results)) {
|
7511
|
continue; // No changes required
|
7512
|
}
|
7513
|
|
7514
|
$f_result = implode(',', $results);
|
7515
|
if (empty(pfb_filter($f_result, PFB_FILTER_CSV, 'Failed to complete sync of Feeds'))) {
|
7516
|
pfb_logger("\nFailed to complete sync of Feeds!", 1);
|
7517
|
continue;
|
7518
|
}
|
7519
|
|
7520
|
$log = "\n[ Removing '{$pftype}' \tList(s) : {$f_result} ]";
|
7521
|
pfb_logger("{$log}", 1);
|
7522
|
|
7523
|
// Process to remove lists from IP Masterfile/DB folder if they are not referenced any longer
|
7524
|
switch ($pftype) {
|
7525
|
case 'deny':
|
7526
|
// Script to Remove un-associated List(s)
|
7527
|
exec("{$pfb['script']} remove x x x {$f_result} {$elog}");
|
7528
|
$pfb['summary'] = $pfb['remove'] = TRUE;
|
7529
|
break;
|
7530
|
case 'match':
|
7531
|
case 'permit':
|
7532
|
case 'native':
|
7533
|
foreach ($results as $pfb_result) {
|
7534
|
if (!empty(pfb_filter($pfb_result, PFB_FILTER_WORD, "Remove un-associated List - {$pftype}"))) {
|
7535
|
unlink_if_exists("{$pfbfolder}/{$pfb_result}.txt");
|
7536
|
unlink_if_exists("{$pfb['origdir']}/{$pfb_result}.*");
|
7537
|
} else {
|
7538
|
pfb_logger("\nFailed to remove Native Feed [$pfb_result}]", 1);
|
7539
|
}
|
7540
|
}
|
7541
|
$pfb['summary'] = $pfb['remove'] = TRUE;
|
7542
|
break;
|
7543
|
case 'dnsbl':
|
7544
|
foreach ($results as $pfb_result) {
|
7545
|
if (!empty(pfb_filter($pfb_result, PFB_FILTER_WORD, "Remove un-associated List - {$pftype}"))) {
|
7546
|
unlink_if_exists("{$pfb['dnsorigdir']}/{$pfb_result}.*");
|
7547
|
} else {
|
7548
|
pfb_logger("\nFailed to remove DNSBL Feed [{$pfb_result}]", 1);
|
7549
|
}
|
7550
|
}
|
7551
|
|
7552
|
rmdir_recursive("{$pfb['dnsdir']}");
|
7553
|
safe_mkdir("{$pfb['dnsdir']}");
|
7554
|
|
7555
|
pfb_logger("\n ** DNSBL Changes found, Reloading...\n", 1);
|
7556
|
$pfb['reuse_dnsbl'] = 'on';
|
7557
|
break;
|
7558
|
}
|
7559
|
|
7560
|
// Allow rebuilding of changed Alias to purge 'SKIP' Lists (when pfBlockerNG is enabled)
|
7561
|
if ($pfb['enable'] == 'on' && $pftype != 'dnsbl') {
|
7562
|
foreach ($ip_types as $ltype => $vtype) {
|
7563
|
foreach ($results as $removed_header) {
|
7564
|
foreach (config_get_path("installedpackages/{$ltype}/config", []) as $list) {
|
7565
|
if (!empty($list['row'])) {
|
7566
|
foreach ($list['row'] as $row) {
|
7567
|
$removed = rtrim($removed_header, ',');
|
7568
|
if ($row['header'] == $removed) {
|
7569
|
$pfb['summary'] = $pfb['remove'] = TRUE;
|
7570
|
|
7571
|
// Add Alias to update array
|
7572
|
$pfb_alias_lists[] = "pfB_{$list['aliasname']}{$vtype}";
|
7573
|
$pfb_alias_lists_all[] = "pfB_{$list['aliasname']}{$vtype}";
|
7574
|
}
|
7575
|
}
|
7576
|
}
|
7577
|
}
|
7578
|
}
|
7579
|
}
|
7580
|
}
|
7581
|
}
|
7582
|
}
|
7583
|
|
7584
|
#########################################################
|
7585
|
# Clear Match/Pass/ET/Original Files/Folders #
|
7586
|
#########################################################
|
7587
|
|
7588
|
// When pfBlockerNG is Disabled and 'Keep Blocklists' is Disabled.
|
7589
|
if ($pfb['enable'] == '' && $pfb['keep'] == '' && !$pfb['install']) {
|
7590
|
$log = "\n Removing DB Files/Folders \n";
|
7591
|
pfb_logger("{$log}", 1);
|
7592
|
pfb_clear_contents();
|
7593
|
}
|
7594
|
|
7595
|
#################################################
|
7596
|
# Create IP Suppression Txt File #
|
7597
|
#################################################
|
7598
|
|
7599
|
if ($pfb['enable'] == 'on' && $pfb['supp'] == 'on') {
|
7600
|
pfb_create_suppression_file();
|
7601
|
}
|
7602
|
|
7603
|
#########################################
|
7604
|
# DNSBL - Processes #
|
7605
|
#########################################
|
7606
|
|
7607
|
if (!$pfb['save']) {
|
7608
|
$log = "\n===[ DNSBL Process ]================================================\n";
|
7609
|
pfb_logger("{$log}", 1);
|
7610
|
}
|
7611
|
|
7612
|
// Define DNSBL arrays and variables
|
7613
|
$pfb['alias_dnsbl_all'] = array(); // Array of all DNSBL aliases
|
7614
|
$pfb['tld_update'] = array(); // Array of all DNSBL Aliases/Feeds used for TLD Function
|
7615
|
$pfb['domain_update'] = FALSE; // Flag to signal update Unbound
|
7616
|
$pfb['updateip'] = FALSE; // Flag to signal updates to DNSBL IP lists
|
7617
|
|
7618
|
$dnsbl_error = FALSE;
|
7619
|
if ($pfb['enable'] == 'on' && $pfb['dnsbl'] == 'on' && !$pfb['save']) {
|
7620
|
|
7621
|
// Terminate if DNSBL VIP is empty
|
7622
|
if (empty($pfb['dnsbl_vip']) || empty($pfb['dnsbl_port']) || empty($pfb['dnsbl_port_ssl'])) {
|
7623
|
$log = "\n\n===[ DNSBL Virtual IP and/or Ports are not defined. Exiting ]======\n";
|
7624
|
pfb_logger("{$log}", 1);
|
7625
|
$dnsbl_error = TRUE;
|
7626
|
}
|
7627
|
}
|
7628
|
|
7629
|
if ($pfb['enable'] == 'on' && $pfb['dnsbl'] == 'on' && !$pfb['save'] && !$dnsbl_error) {
|
7630
|
if ((config_get_path('installedpackages/pfblockerngdnsbl/config') != null) ||
|
7631
|
(config_get_path('installedpackages/pfblockerngblacklist/blacklist_enable') == 'Enable')) {
|
7632
|
|
7633
|
$dnsbl_missing = FALSE;
|
7634
|
|
7635
|
// Collect existing DNSBL group statistics
|
7636
|
// SQLite3 Database format [ group name, updated timestamp, total domain count, total blocked count ]
|
7637
|
|
7638
|
$pfb['dnsbl_info_stats'] = array();
|
7639
|
if (file_exists("{$pfb['dnsbl_info']}")) {
|
7640
|
pfb_logger("\n Loading DNSBL Statistics...", 1);
|
7641
|
|
7642
|
$db_handle = pfb_open_sqlite(1, 'Reading DNSBL database');
|
7643
|
if ($db_handle) {
|
7644
|
$result = $db_handle->query("SELECT * FROM dnsbl;");
|
7645
|
if ($result) {
|
7646
|
while ($res = $result->fetchArray(SQLITE3_ASSOC)) {
|
7647
|
$pfb['dnsbl_info_stats'][] = $res;
|
7648
|
}
|
7649
|
}
|
7650
|
}
|
7651
|
else {
|
7652
|
pfb_logger(" FAILED", 1);
|
7653
|
unlink_if_exists("{$pfb['dnsbl_info']}");
|
7654
|
$dnsbl_missing = TRUE;
|
7655
|
}
|
7656
|
|
7657
|
pfb_close_sqlite($db_handle);
|
7658
|
pfb_logger(" completed", 1);
|
7659
|
}
|
7660
|
else {
|
7661
|
$dnsbl_missing = TRUE;
|
7662
|
}
|
7663
|
|
7664
|
if ($pfb['dnsbl_py_blacklist']) {
|
7665
|
|
7666
|
// When DNSBL python blocking mode is enabled, zone should not exist, and data should exist
|
7667
|
if (!$pfb['dnsbl_tld']) {
|
7668
|
if (file_exists($pfb['unbound_py_zone'])) {
|
7669
|
$dnsbl_missing = TRUE;
|
7670
|
}
|
7671
|
if (!file_exists($pfb['unbound_py_data'])) {
|
7672
|
$dnsbl_missing = TRUE;
|
7673
|
}
|
7674
|
}
|
7675
|
|
7676
|
// When DNSBL python blocking mode is enabled, atleast one 'data or zone' file must exist
|
7677
|
else {
|
7678
|
$found = FALSE;
|
7679
|
if (file_exists($pfb['unbound_py_data'])) {
|
7680
|
$found = TRUE;
|
7681
|
}
|
7682
|
if (file_exists($pfb['unbound_py_zone'])) {
|
7683
|
$found = TRUE;
|
7684
|
}
|
7685
|
if (!$found) {
|
7686
|
$dnsbl_missing = TRUE;
|
7687
|
}
|
7688
|
}
|
7689
|
}
|
7690
|
|
7691
|
// Check if DNSBL database is missing, when DNSBL python blocking mode is not enabled
|
7692
|
if (!$pfb['dnsbl_py_blacklist'] && !file_exists("{$pfb['dnsbl_file']}.conf")) {
|
7693
|
$dnsbl_missing = TRUE;
|
7694
|
}
|
7695
|
|
7696
|
if ($dnsbl_missing) {
|
7697
|
$log = "\n Missing DNSBL stats and/or Unbound DNSBL files - Rebuilding\n";
|
7698
|
pfb_logger("{$log}", 1);
|
7699
|
$pfb['reuse_dnsbl'] = 'on';
|
7700
|
touch("{$pfb['dnsbl_file']}.reload");
|
7701
|
}
|
7702
|
|
7703
|
// SafeSearch
|
7704
|
pfb_logger("\n Loading DNSBL SafeSearch...", 1);
|
7705
|
$safesearch_hosts = array();
|
7706
|
$pfb['safesearch_tlds'] = array();
|
7707
|
$safesearch_types = array( array( 'safesearch_enable', 'Enable', 'safesearch'),
|
7708
|
array( 'safesearch_youtube', 'Strict', 'youtube_restrict'),
|
7709
|
array( 'safesearch_youtube', 'Moderate', 'youtube_restrictmoderate'),
|
7710
|
array( 'safesearch_doh', 'Enable', 'doh')
|
7711
|
);
|
7712
|
|
7713
|
$safesearch_update = $pfb_found = FALSE;
|
7714
|
$CNAME_LOG = '';
|
7715
|
|
7716
|
// Remove deprecated firefox conf file
|
7717
|
if (file_exists("{$pfb['dnsbldir']}/pfb_dnsbl.firefoxdoh.conf")) {
|
7718
|
unlink_if_exists("{$pfb['dnsbldir']}/pfb_dnsbl.firefoxdoh.conf");
|
7719
|
$safesearch_update = TRUE;
|
7720
|
}
|
7721
|
|
7722
|
foreach ($safesearch_types as $safe_type) {
|
7723
|
if ($pfb[$safe_type[0]] == $safe_type[1]) {
|
7724
|
$pfb_found = TRUE;
|
7725
|
|
7726
|
// SafeSearch DoH validation (Add/remove comment '#' to DoH entries)
|
7727
|
if ($safe_type[0] == 'safesearch_doh') {
|
7728
|
$doh_file = file($pfb["dnsbl_{$safe_type[2]}"]);
|
7729
|
if (!empty($doh_file)) {
|
7730
|
$doh_file_final = '';
|
7731
|
foreach ($doh_file as $host) {
|
7732
|
if (strpos($host, '#') !== FALSE) {
|
7733
|
$host = ltrim(str_replace('#', '', $host));
|
7734
|
}
|
7735
|
|
7736
|
$doh_found = FALSE;
|
7737
|
if (!empty($pfb['safesearch_doh_list'])) {
|
7738
|
foreach ($pfb['safesearch_doh_list'] as $doh) {
|
7739
|
if (strpos($host, $doh) !== FALSE) {
|
7740
|
$doh_found = TRUE;
|
7741
|
break;
|
7742
|
}
|
7743
|
}
|
7744
|
}
|
7745
|
if (!$doh_found) {
|
7746
|
$doh_file_final .= "# {$host}";
|
7747
|
} else {
|
7748
|
$doh_file_final .= "{$host}";
|
7749
|
}
|
7750
|
}
|
7751
|
if (!empty($doh_file_final)) {
|
7752
|
@file_put_contents($pfb["dnsbl_{$safe_type[2]}"], $doh_file_final, LOCK_EX);
|
7753
|
}
|
7754
|
}
|
7755
|
}
|
7756
|
|
7757
|
if ($pfb['dnsbl_py_blacklist']) {
|
7758
|
|
7759
|
// Python Mode determine if user manually entered these SafeSearch CNAMES to avoid duplicate zone errors
|
7760
|
if ($safe_type[0] == 'safesearch_enable') {
|
7761
|
$DDG = $PIX = $ss_cname = FALSE;
|
7762
|
if (file_exists('/var/unbound/unbound.conf')) {
|
7763
|
$pfb_py_conf_ex = @file_get_contents('/var/unbound/unbound.conf');
|
7764
|
if (strstr($pfb_py_conf_ex, 'duckduckgo.com')) {
|
7765
|
$CNAME_LOG = "\n *** Found manual Resolver entry for duckduckgo.com. This manual entry should be removed in future!\n";
|
7766
|
$DDG = TRUE;
|
7767
|
}
|
7768
|
if (strstr($pfb_py_conf_ex, 'pixabay.com')) {
|
7769
|
$CNAME_LOG .= "\n *** Found manual Resolver entry for pixabay.com. This manual entry should be removed in future!\n";
|
7770
|
$PIX = TRUE;
|
7771
|
}
|
7772
|
}
|
7773
|
$ss_ex = @file_get_contents("{$pfb['dnsbldir']}/pfb_dnsbl.{$safe_type[2]}.conf");
|
7774
|
}
|
7775
|
unlink_if_exists("{$pfb['dnsbldir']}/pfb_dnsbl.{$safe_type[2]}.conf"); // Remove Unbound mode file
|
7776
|
|
7777
|
$safesearch_file = file($pfb["dnsbl_{$safe_type[2]}"]);
|
7778
|
if (!empty($safesearch_file)) {
|
7779
|
foreach ($safesearch_file as $host) {
|
7780
|
|
7781
|
if (substr($host, 0, 1) == '#') {
|
7782
|
continue;
|
7783
|
}
|
7784
|
|
7785
|
$line = str_replace('"', '', strstr($host, '"', False));
|
7786
|
$host_ip = trim(str_replace('A ', '', strstr($line, 'A ', FALSE)));
|
7787
|
$domain = strstr($line, ' ', TRUE);
|
7788
|
if (substr($domain, 0, 4) == 'www.') {
|
7789
|
$domain = substr($domain, 4);
|
7790
|
}
|
7791
|
|
7792
|
if (strpos($line, ' A ') !== FALSE) {
|
7793
|
$safesearch_hosts[$domain]['A'] = $host_ip;
|
7794
|
} elseif (strpos($line, ' AAAA ') !== FALSE) {
|
7795
|
$safesearch_hosts[$domain]['AAAA'] = $host_ip;
|
7796
|
|
7797
|
# CNAME SafeSearch is not compatible in Python Mode, use Unbound mode for now
|
7798
|
} elseif (strpos($line, ' CNAME ') !== FALSE) {
|
7799
|
$ss_cname = TRUE;
|
7800
|
if (!$DDG && strpos($line, 'duckduckgo.com') !== FALSE) {
|
7801
|
@file_put_contents("{$pfb['dnsbldir']}/pfb_dnsbl.{$safe_type[2]}.conf", "{$host}", FILE_APPEND);
|
7802
|
}
|
7803
|
if (!$PIX && strpos($line, 'pixabay.com') !== FALSE) {
|
7804
|
@file_put_contents("{$pfb['dnsbldir']}/pfb_dnsbl.{$safe_type[2]}.conf", "{$host}", FILE_APPEND);
|
7805
|
}
|
7806
|
# $cname = trim(str_replace('CNAME ', '', strstr($line, 'CNAME', FALSE)));
|
7807
|
# $safesearch_hosts[$domain]['A'] = 'cname';
|
7808
|
# $safesearch_hosts[$domain]['AAAA'] = $cname;
|
7809
|
} elseif (strpos($line, 'always_nxdomain') !== FALSE) {
|
7810
|
$safesearch_hosts[$domain]['nxdomain'] = '';
|
7811
|
}
|
7812
|
}
|
7813
|
}
|
7814
|
|
7815
|
if ($safe_type[2] == 'safesearch' && ($ss_cname && $ss_ex !== @file_get_contents("{$pfb['dnsbldir']}/pfb_dnsbl.{$safe_type[2]}.conf")) ||
|
7816
|
(($DDG || $PIX) && !$ss_cname)) {
|
7817
|
$safesearch_update = TRUE;
|
7818
|
}
|
7819
|
}
|
7820
|
else {
|
7821
|
unlink_if_exists("/var/unbound/pfb_py_ss.*"); // Remove Python mode file
|
7822
|
$ss_cname = FALSE;
|
7823
|
|
7824
|
// Determine if user manually entered these SafeSearch CNAMES to avoid duplicate zone errors
|
7825
|
if ($safe_type[0] == 'safesearch_enable') {
|
7826
|
|
7827
|
// Step one - See if DDG and PIX are manually configured
|
7828
|
$DDG = $PIX = FALSE;
|
7829
|
if (file_exists('/var/unbound/unbound.conf')) {
|
7830
|
$pfb_py_conf_ex = @file_get_contents('/var/unbound/unbound.conf');
|
7831
|
if (strstr($pfb_py_conf_ex, 'duckduckgo.com')) {
|
7832
|
$CNAME_LOG = "\n *** Found manual Resolver entry for duckduckgo.com. This manual entry should be removed in future!\n";
|
7833
|
$DDG = TRUE;
|
7834
|
}
|
7835
|
if (strstr($pfb_py_conf_ex, 'pixabay.com')) {
|
7836
|
$CNAME_LOG .= "\n *** Found manual Resolver entry for pixabay.com. This manual entry should be removed in future!\n";
|
7837
|
$PIX = TRUE;
|
7838
|
}
|
7839
|
}
|
7840
|
|
7841
|
// Step two - If they are configured rebuild SafeSearch file
|
7842
|
$safesearch_file = file($pfb["dnsbl_{$safe_type[2]}"]);
|
7843
|
$ss_lines = '';
|
7844
|
if (!empty($safesearch_file)) {
|
7845
|
foreach ($safesearch_file as $host) {
|
7846
|
if ($DDG && strpos($host, 'duckduckgo.com') !== FALSE) {
|
7847
|
$ss_lines .= "# {$host}";
|
7848
|
$ss_cname = TRUE;
|
7849
|
}
|
7850
|
elseif ($PIX && strpos($host, 'pixabay.com') !== FALSE) {
|
7851
|
$ss_lines .= "# {$host}";
|
7852
|
$ss_cname = TRUE;
|
7853
|
}
|
7854
|
else {
|
7855
|
$ss_lines .= "{$host}";
|
7856
|
}
|
7857
|
}
|
7858
|
}
|
7859
|
|
7860
|
// Step three - Compare previous SafeSearch file to new and update if changes are found
|
7861
|
if ($ss_cname) {
|
7862
|
if (file_exists("{$pfb['dnsbldir']}/pfb_dnsbl.{$safe_type[2]}.conf")) {
|
7863
|
$ss_lines_ex = @file_get_contents("{$pfb['dnsbldir']}/pfb_dnsbl.{$safe_type[2]}.conf");
|
7864
|
|
7865
|
if ($ss_lines !== $ss_lines_ex) {
|
7866
|
@file_put_contents("{$pfb['dnsbldir']}/pfb_dnsbl.{$safe_type[2]}.conf", $ss_lines, LOCK_EX);
|
7867
|
$safesearch_update = TRUE;
|
7868
|
}
|
7869
|
}
|
7870
|
else {
|
7871
|
@file_put_contents("{$pfb['dnsbldir']}/pfb_dnsbl.{$safe_type[2]}.conf", $ss_lines, LOCK_EX);
|
7872
|
$safesearch_update = TRUE;
|
7873
|
}
|
7874
|
}
|
7875
|
}
|
7876
|
|
7877
|
// Copy file if not exists or has been updated
|
7878
|
if (!file_exists("{$pfb['dnsbldir']}/pfb_dnsbl.{$safe_type[2]}.conf") ||
|
7879
|
@md5_file($pfb["dnsbl_{$safe_type[2]}"]) !==
|
7880
|
@md5_file("{$pfb['dnsbldir']}/pfb_dnsbl.{$safe_type[2]}.conf")) {
|
7881
|
|
7882
|
// Temp Workaround for Duplicate CNAME Zone issue:
|
7883
|
if (!$ss_cname) {
|
7884
|
@copy($pfb["dnsbl_{$safe_type[2]}"], "{$pfb['dnsbldir']}/pfb_dnsbl.{$safe_type[2]}.conf");
|
7885
|
$safesearch_update = TRUE;
|
7886
|
}
|
7887
|
}
|
7888
|
|
7889
|
// Collect SafeSearch domains for wildcard whitelisting
|
7890
|
exec("cut -d '\"' -f2 {$pfb['dnsbldir']}/pfb_dnsbl.{$safe_type[2]}.conf | cut -d ' ' -f1", $safesearch_hosts);
|
7891
|
|
7892
|
// Collect all Domains/TLDs for 'TLD Blacklist' validation (Unbound mode only. Cannot have duplicate zones)
|
7893
|
if (!$pfb['dnsbl_py_blacklist'] && !empty($safesearch_hosts)) {
|
7894
|
foreach ($safesearch_hosts as $host) {
|
7895
|
$safesearch_tlds[$host] = '';
|
7896
|
for ($i= substr_count($host, '.'); $i > 0; $i--) {
|
7897
|
$host = ltrim(strstr($host, '.', FALSE), '.');
|
7898
|
$pfb['safesearch_tlds'][$host] = '';
|
7899
|
}
|
7900
|
}
|
7901
|
}
|
7902
|
}
|
7903
|
}
|
7904
|
elseif (file_exists("{$pfb['dnsbldir']}/pfb_dnsbl.{$safe_type[2]}.conf")) {
|
7905
|
unlink("{$pfb['dnsbldir']}/pfb_dnsbl.{$safe_type[2]}.conf");
|
7906
|
$safesearch_update = TRUE;
|
7907
|
}
|
7908
|
}
|
7909
|
|
7910
|
// Python mode create a CSV list of SafeSearch hosts
|
7911
|
if ($pfb['dnsbl_py_blacklist'] && !empty($safesearch_hosts)) {
|
7912
|
foreach ($safesearch_hosts as $host => $data) {
|
7913
|
if (isset($data['nxdomain'])) {
|
7914
|
$line = "{$host},nxdomain,nxdomain\n";
|
7915
|
} else {
|
7916
|
$line = "{$host}," . ($data['A'] ?: '') . ',' . ($data['AAAA'] ?: '') . "\n"
|
7917
|
. "www.{$host}," . ($data['A'] ?: '') . ',' . ($data['AAAA'] ?: '') . "\n";
|
7918
|
}
|
7919
|
@file_put_contents('/var/unbound/pfb_py_ss.raw', $line, FILE_APPEND);
|
7920
|
}
|
7921
|
|
7922
|
// Copy file if not exists or has been updated
|
7923
|
if (!file_exists("{$pfb['unbound_py_ss']}") ||
|
7924
|
@md5_file("{$pfb['unbound_py_ss']}") !==
|
7925
|
@md5_file('/var/unbound/pfb_py_ss.raw')) {
|
7926
|
@rename('/var/unbound/pfb_py_ss.raw', "{$pfb['unbound_py_ss']}");
|
7927
|
$safesearch_update = TRUE;
|
7928
|
touch("{$pfb['dnsbl_file']}.reload");
|
7929
|
}
|
7930
|
unlink_if_exists('/var/unbound/pfb_py_ss.raw');
|
7931
|
}
|
7932
|
else {
|
7933
|
unlink_if_exists("/var/unbound/pfb_py_ss.*");
|
7934
|
}
|
7935
|
|
7936
|
// Wildcard whitelist SafeSearch domains (Unbound mode only)
|
7937
|
$pfb_whitelist = '';
|
7938
|
if (!$pfb['dnsbl_py_blacklist'] && !empty($safesearch_hosts)) {
|
7939
|
$safesearch_hosts = array_unique($safesearch_hosts);
|
7940
|
foreach ($safesearch_hosts as $line) {
|
7941
|
if (!empty($line)) {
|
7942
|
|
7943
|
// Remove 'www.' prefix
|
7944
|
if (substr($line, 0, 4) == 'www.') {
|
7945
|
$line = substr($line, 4);
|
7946
|
}
|
7947
|
|
7948
|
if ($pfb['dnsbl_py_blacklist']) {
|
7949
|
$pfb_whitelist .= ".{$line},,\n,{$line},,\n";
|
7950
|
} else {
|
7951
|
$pfb_whitelist .= ".{$line} 60\n\"{$line} 60\n";
|
7952
|
}
|
7953
|
}
|
7954
|
}
|
7955
|
}
|
7956
|
|
7957
|
$s_log = ' disabled';
|
7958
|
if ($pfb_found) {
|
7959
|
$s_log = ' enabled';
|
7960
|
}
|
7961
|
pfb_logger("{$s_log}{$CNAME_LOG}", 1);
|
7962
|
|
7963
|
// Collect Whitelist, create string, and save to file (for grep -vF -f cmd)
|
7964
|
pfb_logger("\n Loading DNSBL Whitelist...", 1);
|
7965
|
$pfb_white = pfbng_text_area_decode($pfb['dnsblconfig']['suppression'], TRUE, FALSE, TRUE);
|
7966
|
if (!empty($pfb_white)) {
|
7967
|
foreach ($pfb_white as $line) {
|
7968
|
if (!empty($line)) {
|
7969
|
$wildcard = FALSE;
|
7970
|
if (substr($line, 0, 1) == '.') {
|
7971
|
$line = ltrim($line, '.');
|
7972
|
$wildcard = TRUE;
|
7973
|
}
|
7974
|
|
7975
|
// Remove 'www.' prefix
|
7976
|
if (substr($line, 0, 4) == 'www.') {
|
7977
|
$line = substr($line, 4);
|
7978
|
}
|
7979
|
|
7980
|
if ($wildcard) {
|
7981
|
if ($pfb['dnsbl_py_blacklist']) {
|
7982
|
$pfb_whitelist .= ".{$line},,\n,{$line},,\n";
|
7983
|
} else {
|
7984
|
$pfb_whitelist .= ".{$line} 60\n\"{$line} 60\n";
|
7985
|
}
|
7986
|
} else {
|
7987
|
if ($pfb['dnsbl_py_blacklist']) {
|
7988
|
$pfb_whitelist .= ",{$line},,\n,www.{$line},,\n";
|
7989
|
} else {
|
7990
|
$pfb_whitelist .= "\"{$line} 60\n\"www.{$line} 60\n";
|
7991
|
}
|
7992
|
}
|
7993
|
}
|
7994
|
}
|
7995
|
}
|
7996
|
|
7997
|
// Added due to SWC Feed
|
7998
|
if ($pfb['dnsbl_py_blacklist']) {
|
7999
|
$pfb_whitelist .= ",localhost.localdomain,,\n";
|
8000
|
} else {
|
8001
|
$pfb_whitelist .= "\"localhost.localdomain 60\n";
|
8002
|
}
|
8003
|
|
8004
|
@file_put_contents("{$pfb['dnsbl_supptxt']}", $pfb_whitelist, LOCK_EX);
|
8005
|
pfb_logger(" completed", 1);
|
8006
|
|
8007
|
if (!$pfb['dnsbl_py_blacklist'] && $safesearch_update) {
|
8008
|
$log = "\n DNSBL - SafeSearch changes found - Rebuilding!\n";
|
8009
|
pfb_logger("{$log}", 1);
|
8010
|
$pfb['reuse_dnsbl'] = 'on';
|
8011
|
touch("{$pfb['dnsbl_file']}.reload");
|
8012
|
}
|
8013
|
|
8014
|
// Call TOP1M whitelist process
|
8015
|
if ($pfb['dnsbl_alexa'] == 'on') {
|
8016
|
pfb_logger("\n Loading TOP1M Whitelist...", 1);
|
8017
|
|
8018
|
// Check if TOP1M database exists
|
8019
|
if (!file_exists("{$pfb['dbdir']}/top-1m.csv")) {
|
8020
|
// Check if TOP1M download already in progress
|
8021
|
exec('/bin/ps -wax', $result_cron);
|
8022
|
if (!preg_grep("/pfblockerng[.]php\s+al/", $result_cron)) {
|
8023
|
$log = "\n TOP1M Database downloading ( approx 21MB ) ... Please wait ...\n";
|
8024
|
pfb_logger("{$log}", 1);
|
8025
|
exec('/usr/local/bin/php /usr/local/www/pfblockerng/pfblockerng.php al');
|
8026
|
}
|
8027
|
else {
|
8028
|
$log = "\n TOP1M download already in process...\n";
|
8029
|
pfb_logger("{$log}", 1);
|
8030
|
}
|
8031
|
}
|
8032
|
|
8033
|
// Process TOP1M database
|
8034
|
if (!file_exists("{$pfb['dbdir']}/pfbalexawhitelist.txt") ||
|
8035
|
file_exists("{$pfb['dbdir']}/top-1m.update")) {
|
8036
|
pfblockerng_top1m();
|
8037
|
$pfb['reuse_dnsbl'] = 'on';
|
8038
|
touch("{$pfb['dnsbl_file']}.reload");
|
8039
|
pfb_logger("\n DNSBL - TOP1M changes found - Rebuilding!\n", 1);
|
8040
|
}
|
8041
|
pfb_logger(" completed", 1);
|
8042
|
}
|
8043
|
pfb_logger("\n", 1);
|
8044
|
|
8045
|
// List of invalid Domains to skip parsed failed logging function
|
8046
|
$dnsbl_skip = array_flip (array('broadcasthost',
|
8047
|
'local',
|
8048
|
'localhost',
|
8049
|
'<pre>',
|
8050
|
'Vault',
|
8051
|
'Site',
|
8052
|
'list',
|
8053
|
'::1',
|
8054
|
':',
|
8055
|
'ip6-localhost',
|
8056
|
'ip6-localnet',
|
8057
|
'ip6-mcastprefix',
|
8058
|
'ip6-allnodes',
|
8059
|
'ip6-allrouters',
|
8060
|
'ip6-allhosts'
|
8061
|
));
|
8062
|
|
8063
|
// List of Alienvault OTX Indicator Types
|
8064
|
$alienvault_types = array_flip(array('domain', 'hostname', 'URL'));
|
8065
|
|
8066
|
// Collect feeds and custom list configuration and format into one array ($lists).
|
8067
|
$lists = array();
|
8068
|
|
8069
|
// Add DNSBL Category to '$lists array'
|
8070
|
if (isset($pfb['blconfig']) &&
|
8071
|
$pfb['blconfig']['blacklist_enable'] != 'Disable' &&
|
8072
|
!empty($pfb['blconfig']['blacklist_selected'])) {
|
8073
|
|
8074
|
$bl_count = 0;
|
8075
|
$bl_validate = FALSE;
|
8076
|
|
8077
|
$selected = array_flip(explode(',', $pfb['blconfig']['blacklist_selected'])) ?: array();
|
8078
|
foreach ($pfb['blconfig']['item'] as $item) {
|
8079
|
$type = "{$item['xml']}";
|
8080
|
|
8081
|
if (isset($selected[$type]) && !empty($item['selected'])) {
|
8082
|
|
8083
|
$bl_count++;
|
8084
|
$categories = explode(',', $item['selected']) ?: array();
|
8085
|
|
8086
|
$list = array();
|
8087
|
$list['aliasname'] = "{$item['title']}";
|
8088
|
$list['action'] = 'unbound';
|
8089
|
$list['logging'] = $pfb['blconfig']['blacklist_logging'] ?: 'enabled';
|
8090
|
$list['filter_alexa'] = '';
|
8091
|
|
8092
|
$feedname = strtolower($item['title']);
|
8093
|
$update_flag = "{$pfb['dbdir']}/{$feedname}/{$feedname}.update";
|
8094
|
|
8095
|
foreach ($categories as $category) {
|
8096
|
|
8097
|
// Replace dash to underscore in Header name
|
8098
|
$category = str_replace('-', '_', $category);
|
8099
|
|
8100
|
if (!empty($category)) {
|
8101
|
$list['row'][] = array( 'format' => 'auto',
|
8102
|
'state' => 'Enabled',
|
8103
|
'url' => "{$pfb['dbdir']}/{$type}/{$type}_{$category}",
|
8104
|
'header' => "{$item['title']}_{$category}"
|
8105
|
);
|
8106
|
|
8107
|
// If update available set Update flag for each selected Category
|
8108
|
if (file_exists("{$update_flag}")) {
|
8109
|
touch("{$pfb['dnsdir']}/{$item['title']}_{$category}.update");
|
8110
|
}
|
8111
|
}
|
8112
|
}
|
8113
|
unlink_if_exists("{$update_flag}");
|
8114
|
|
8115
|
if (isset($list['row'])) {
|
8116
|
$lists[] = $list;
|
8117
|
}
|
8118
|
|
8119
|
// Check if Blacklist database has not been previously downloaded
|
8120
|
if (!is_dir("{$pfb['dbdir']}/{$type}") ||
|
8121
|
is_dir("{$pfb['dbdir']}/{$type}") && (count(scandir("{$pfb['dbdir']}/{$type}")) <= 2)) {
|
8122
|
if (!is_array($bl_validate)) {
|
8123
|
$bl_validate = array();
|
8124
|
}
|
8125
|
$bl_validate[$type] = $item['size'];
|
8126
|
}
|
8127
|
}
|
8128
|
}
|
8129
|
|
8130
|
// Download Blacklist databases that are not previously downloaded
|
8131
|
if ($bl_validate) {
|
8132
|
|
8133
|
// Create commandline arguments for download script
|
8134
|
$bl_string = $bl_sources = '';
|
8135
|
foreach ($bl_validate as $type => $size) {
|
8136
|
$bl_string .= ",{$type}";
|
8137
|
$bl_sources .= " {$type} (~{$size}MB) |";
|
8138
|
}
|
8139
|
|
8140
|
if (!empty(pfb_filter($bl_string, PFB_FILTER_CSV, 'Blacklist database commandline arguments'))) {
|
8141
|
$bl_string = ltrim($bl_string, ',');
|
8142
|
$bl_sources = rtrim($bl_sources, ' |');
|
8143
|
|
8144
|
// Check if Blacklist download already in progress
|
8145
|
exec('/bin/ps -wax', $result_cron);
|
8146
|
if (!preg_grep("/pfblockerng[.]php\s+?(bl|bls)/", $result_cron)) {
|
8147
|
|
8148
|
$log = "\nDownloading Blacklist Database(s) [{$bl_sources} ] ... Please wait ...\n";
|
8149
|
pfb_logger("{$log}", 1);
|
8150
|
exec("/usr/local/bin/php /usr/local/www/pfblockerng/pfblockerng.php bls {$bl_string} 2>&1", $pfb_return);
|
8151
|
|
8152
|
if (is_array($pfb_return)) {
|
8153
|
foreach ($pfb_return as $key => $return_output) {
|
8154
|
|
8155
|
pfb_logger("{$return_output}\n", 1);
|
8156
|
|
8157
|
// On download failure, remove associated Blacklist category configuration
|
8158
|
if (strpos($return_output, 'Failed') !== FALSE) {
|
8159
|
unset($lists[$key]);
|
8160
|
}
|
8161
|
}
|
8162
|
}
|
8163
|
}
|
8164
|
else {
|
8165
|
$log = "\nBlacklist Database download already in process... Try again later...\n";
|
8166
|
pfb_logger("{$log}", 1);
|
8167
|
|
8168
|
// Remove Blacklist Category updates until database download is completed
|
8169
|
while ($bl_count != 0) {
|
8170
|
array_pop($lists);
|
8171
|
$bl_count--;
|
8172
|
}
|
8173
|
}
|
8174
|
}
|
8175
|
else {
|
8176
|
pfb_logger("\n Invalid Blacklist arguments [{$bl_string}]", 1);
|
8177
|
}
|
8178
|
}
|
8179
|
else {
|
8180
|
pfb_logger(" Blacklist database(s) ... exists.\n", 1);
|
8181
|
}
|
8182
|
}
|
8183
|
|
8184
|
foreach (config_get_path('installedpackages/pfblockerngdnsbl/config', []) as $list) {
|
8185
|
// If only the 'customlist' is defined. Remove the 'List row' data.
|
8186
|
if (isset($list['row']) && empty($list['row'][0]['url'])) {
|
8187
|
unset($list['row']);
|
8188
|
}
|
8189
|
|
8190
|
if (!empty($list['custom'])) {
|
8191
|
$list['row'][] = array( 'header' => "{$list['aliasname']}_custom",
|
8192
|
'custom' => $list['custom'],
|
8193
|
'state' => 'Enabled',
|
8194
|
'url' => 'custom'
|
8195
|
);
|
8196
|
}
|
8197
|
|
8198
|
// Move DNSBL Group to primary position before the Blacklist settings
|
8199
|
if ($list['order'] == 'primary') {
|
8200
|
$list_primary = array();
|
8201
|
$list_primary[] = $list;
|
8202
|
$lists = array_merge($list_primary, $lists);
|
8203
|
} else {
|
8204
|
$lists[] = $list;
|
8205
|
}
|
8206
|
}
|
8207
|
|
8208
|
foreach ($lists as $list) {
|
8209
|
|
8210
|
// Reset variables once per alias
|
8211
|
$lists_dnsbl_current = array(); // Array of all active Lists in current alias
|
8212
|
$pfb['aliasupdate'] = FALSE; // Flag to signal changes to alias
|
8213
|
$pfb['domain_clear'] = FALSE; // Flag to signal no Aliases defined or all Aliases disabled.
|
8214
|
$alias_cnt = 0;
|
8215
|
|
8216
|
if ($list['action'] != 'Disabled' && isset($list['row'])) {
|
8217
|
$alias = "DNSBL_{$list['aliasname']}";
|
8218
|
if (empty(pfb_filter($alias, PFB_FILTER_WORD, 'DNSBL - Processes'))) {
|
8219
|
pfb_logger("\n Invalid Aliasname:{$list['aliasname']}, *skipping*", 1);
|
8220
|
continue;
|
8221
|
}
|
8222
|
$pfb['alias_dnsbl_all'][] = "{$alias}";
|
8223
|
|
8224
|
foreach ($list['row'] as $key => $row) {
|
8225
|
if (!empty($row['url']) && $row['state'] != 'Disabled') {
|
8226
|
|
8227
|
// Empty header field validation check
|
8228
|
$header = pfb_filter($row['header'], PFB_FILTER_WORD, 'DNSBL - Processes');
|
8229
|
if (empty($header)) {
|
8230
|
$log = "\n[ {$row['url']} ]{$logtab} Header Field cannot be empty. *Skipping* \n";
|
8231
|
pfb_logger("{$log}", 2);
|
8232
|
continue;
|
8233
|
}
|
8234
|
$header_esc = escapeshellarg($header);
|
8235
|
|
8236
|
$liteparser = FALSE; // Minimal DNSBL Parser
|
8237
|
$rev_format = FALSE; // Host style format is reversed
|
8238
|
$domain_data_ip = array(); // Array of IPs found in feed
|
8239
|
$domain_data = ''; // List of Domains found in feed
|
8240
|
|
8241
|
// If row is a custom_list, set flag.
|
8242
|
if (isset($row['custom'])) {
|
8243
|
$custom = TRUE;
|
8244
|
} else {
|
8245
|
$custom = FALSE;
|
8246
|
}
|
8247
|
|
8248
|
// Global DNSBL Logging/Blocking mode
|
8249
|
if (!empty($pfb['dnsbl_global_log'])) {
|
8250
|
$list['logging'] = $pfb['dnsbl_global_log'];
|
8251
|
}
|
8252
|
|
8253
|
// Force Unbound mode Logging for 'disabled_log' to 'enabled'
|
8254
|
if (!$pfb['dnsbl_py_blacklist'] && $list['logging'] == 'disabled_log') {
|
8255
|
$list['logging'] = 'enabled';
|
8256
|
}
|
8257
|
|
8258
|
// If Null Blocking mode is selected, use '0.0.0.0|::0', otherwise utilize DNSBL WebServer/DNSBL VIP
|
8259
|
if ($list['logging'] == 'disabled') {
|
8260
|
$sinkhole_type4 = '0.0.0.0';
|
8261
|
$sinkhole_type6 = '::0';
|
8262
|
$logging_type = '2'; // Null Blocking no logging
|
8263
|
}
|
8264
|
elseif ($list['logging'] == 'disabled_log') {
|
8265
|
$logging_type = '0'; // Null Blocking logging
|
8266
|
} else {
|
8267
|
$sinkhole_type4 = "{$pfb['dnsbl_vip']}";
|
8268
|
$sinkhole_type6 = "::{$pfb['dnsbl_vip']}";
|
8269
|
$logging_type = '1'; // DNSBL VIP logging
|
8270
|
}
|
8271
|
|
8272
|
// Determine 'list' details (return array $pfbarr)
|
8273
|
pfb_determine_list_detail($list['action'], $header, 'pfblockerngdnsblsettings', '0');
|
8274
|
$pfbadv = $pfbarr['adv'];
|
8275
|
$pfbfolder = $pfbarr['folder'];
|
8276
|
$pfborig = $pfbarr['orig'];
|
8277
|
$pfbreuse = $pfbarr['reuse'];
|
8278
|
$logtab = $pfbarr['logtab'];
|
8279
|
|
8280
|
if (file_exists("{$pfbfolder}/{$header}.txt") &&
|
8281
|
!file_exists("{$pfbfolder}/{$header}.update") &&
|
8282
|
!file_exists("{$pfbfolder}/{$header}.fail") &&
|
8283
|
$pfbreuse == '') {
|
8284
|
|
8285
|
|
8286
|
if ($row['state'] == 'Hold') {
|
8287
|
$log = "\n[ {$header} ]{$logtab} static hold. [ NOW ]";
|
8288
|
} else {
|
8289
|
$log = "\n[ {$header} ]{$logtab} exists. [ NOW ]";
|
8290
|
}
|
8291
|
pfb_logger("{$log}", 1);
|
8292
|
|
8293
|
// Collect existing list stats
|
8294
|
$lists_dnsbl_all[] = "{$row['header']}.txt";
|
8295
|
$lists_dnsbl_current[] = "{$row['header']}";
|
8296
|
$file_esc = escapeshellarg("{$pfbfolder}/{$header}.txt");
|
8297
|
$list_cnt = exec("{$pfb['grep']} -c ^ {$file_esc}");
|
8298
|
$alias_cnt = $alias_cnt + $list_cnt;
|
8299
|
}
|
8300
|
else {
|
8301
|
if ($pfbreuse == 'on' && file_exists("{$pfborig}/{$header}.orig")) {
|
8302
|
$log = "\n[ {$header} ]{$logtab} Reload [ NOW ]";
|
8303
|
} else {
|
8304
|
$log = "\n[ {$header} ]{$logtab} Downloading update [ NOW ]";
|
8305
|
}
|
8306
|
pfb_logger("{$log}", 1);
|
8307
|
$file_dwn = "{$pfborig}/{$header}";
|
8308
|
|
8309
|
if (!$custom) {
|
8310
|
pfb_logger(' .', 1);
|
8311
|
|
8312
|
// Allow cURL SSL downgrade 'Flex' if user configured.
|
8313
|
$pflex = FALSE;
|
8314
|
if ($row['state'] == 'Flex') {
|
8315
|
$pflex = TRUE;
|
8316
|
}
|
8317
|
|
8318
|
// Determine if list needs to be downloaded or reuse previously downloaded file.
|
8319
|
if ($pfbreuse == 'on' && file_exists("{$file_dwn}.orig")) {
|
8320
|
// File exists/reuse
|
8321
|
pfb_logger(' completed .', 1);
|
8322
|
} else {
|
8323
|
// Download file
|
8324
|
if (!pfb_download($row['url'], $file_dwn, $pflex, $header,
|
8325
|
$row['format'], 1, '', '', '', '', '', $srcint)) {
|
8326
|
|
8327
|
// Determine reason for download failure
|
8328
|
pfb_download_failure($alias, $header, $pfbfolder, $row['url'], $row['format'], '');
|
8329
|
|
8330
|
// Utilize previously download file (If 'fail' marker exists)
|
8331
|
if (file_exists("{$pfbfolder}/{$header}.fail") &&
|
8332
|
file_exists("{$file_dwn}.orig")) {
|
8333
|
pfb_logger("\n Restoring previously downloaded file\n ", 2);
|
8334
|
} else {
|
8335
|
continue;
|
8336
|
}
|
8337
|
}
|
8338
|
else {
|
8339
|
// Clear any previous download fail marker
|
8340
|
unlink_if_exists("{$pfbfolder}/{$header}.fail");
|
8341
|
}
|
8342
|
}
|
8343
|
}
|
8344
|
else {
|
8345
|
// Collect custom list data.
|
8346
|
$custom_list = pfbng_text_area_decode($row['custom'], FALSE, TRUE, TRUE);
|
8347
|
@file_put_contents("{$file_dwn}.orig", $custom_list, LOCK_EX);
|
8348
|
unset($custom_list);
|
8349
|
$liteparser = TRUE;
|
8350
|
}
|
8351
|
|
8352
|
// Variables for Easylists
|
8353
|
$easylist = $validate_header = FALSE;
|
8354
|
$e_replace = array( '||', '.^', '^' );
|
8355
|
|
8356
|
$run_once = $csv_parser = FALSE;
|
8357
|
$csv_type = '';
|
8358
|
$ipcount = $ip_cnt = 0;
|
8359
|
|
8360
|
// Parse downloaded file for Domain names
|
8361
|
if (($fhandle = @fopen("{$file_dwn}.orig", 'r')) !== FALSE) {
|
8362
|
if (($dhandle = @fopen("{$pfbfolder}/{$header}.bk", 'w')) !== FALSE) {
|
8363
|
while (($line = @fgets($fhandle)) !== FALSE) {
|
8364
|
|
8365
|
// Collect original line
|
8366
|
$oline = $line;
|
8367
|
|
8368
|
// Validate EasyList/AdBlock/uBlock/ADGuard Feeds
|
8369
|
if (!$validate_header) {
|
8370
|
if (strpos($line, '[Adblock Plus ') !== FALSE ||
|
8371
|
strpos($line, '[Adblock Plus]') !== FALSE ||
|
8372
|
strpos($line, '[uBlock Origin') !== FALSE ||
|
8373
|
strpos($line, '! Title: AdGuard') !== FALSE) {
|
8374
|
$easylist = $validate_header = TRUE;
|
8375
|
continue;
|
8376
|
}
|
8377
|
elseif (substr($line, 0, 1) === '!') {
|
8378
|
continue;
|
8379
|
}
|
8380
|
else {
|
8381
|
$validate_header = TRUE;
|
8382
|
}
|
8383
|
}
|
8384
|
|
8385
|
// Remove any '^M' characters
|
8386
|
if (strpos($line, "\r") !== FALSE) {
|
8387
|
$line = rtrim($line, "\x00..\x1F");
|
8388
|
}
|
8389
|
|
8390
|
// Remove invalid charaters
|
8391
|
$line = trim($line, " \t\n\r\0\x0B\xC2\xA0");
|
8392
|
|
8393
|
if ($easylist) {
|
8394
|
if (substr($line, 0, 2) !== '||' ||
|
8395
|
substr($line, -1) !== '^' ||
|
8396
|
strpos($line, '$') !== FALSE ||
|
8397
|
strpos($line, '*') !== FALSE ||
|
8398
|
strpos($line, '/') !== FALSE) {
|
8399
|
continue;
|
8400
|
}
|
8401
|
|
8402
|
$lite = TRUE;
|
8403
|
$line = str_replace($e_replace, '', $line);
|
8404
|
}
|
8405
|
else {
|
8406
|
// If 'tab' character found, replace with whitespace
|
8407
|
if (strpos($line, "\x09") !== FALSE) {
|
8408
|
$line = str_replace("\x09", ' ', $line);
|
8409
|
}
|
8410
|
|
8411
|
// If '%20' found, remove.
|
8412
|
if (strpos($line, '%20') !== FALSE) {
|
8413
|
$line = str_replace('%20', '', $line);
|
8414
|
}
|
8415
|
|
8416
|
// Remove comment lines and special format considerations
|
8417
|
if (substr($line, 0, 1) == '#') {
|
8418
|
// Exit (hpHosts) when end of domain names found.
|
8419
|
if (strpos($line, 'Append critical updates below') !== FALSE) {
|
8420
|
break;
|
8421
|
}
|
8422
|
|
8423
|
// Spamhaus format validation
|
8424
|
if (strpos($line, 'The Spamhaus Project Ltd') !== FALSE) {
|
8425
|
$rev_format = TRUE;
|
8426
|
}
|
8427
|
|
8428
|
if ($line == '#family,type,url,status,first_seen,'
|
8429
|
. 'first_active,last_active,last_update') {
|
8430
|
$csv_type = 'h3x';
|
8431
|
$csv_parser = TRUE;
|
8432
|
}
|
8433
|
continue;
|
8434
|
}
|
8435
|
|
8436
|
// Remove slash comment lines
|
8437
|
if (substr($line, 0, 2) == '//') {
|
8438
|
continue;
|
8439
|
}
|
8440
|
|
8441
|
// Remove any 'End of line' comments (Some contains commas)
|
8442
|
if (strpos($line, ' #') !== FALSE) {
|
8443
|
$line = strstr($line, ' #', TRUE);
|
8444
|
}
|
8445
|
|
8446
|
// Convert CSV line into array
|
8447
|
if ($csv_parser) {
|
8448
|
$csvline = str_getcsv($line, ',', '', '"');
|
8449
|
}
|
8450
|
elseif (!$run_once) {
|
8451
|
if (substr_count($line, ',') >= 2) {
|
8452
|
$csvline = str_getcsv($line, ',', '', '"');
|
8453
|
$csv_parser = TRUE;
|
8454
|
}
|
8455
|
$run_once = TRUE;
|
8456
|
}
|
8457
|
}
|
8458
|
|
8459
|
// Remove blank lines
|
8460
|
if (empty($line)) {
|
8461
|
continue;
|
8462
|
}
|
8463
|
|
8464
|
// CSV parser
|
8465
|
if ($csv_parser) {
|
8466
|
|
8467
|
$csv_found = FALSE;
|
8468
|
$csv_count = count($csvline);
|
8469
|
|
8470
|
switch ($csv_type) {
|
8471
|
case 'pt':
|
8472
|
if ($csv_count == 8) {
|
8473
|
if (strpos($csvline[1], ' ') !== FALSE) {
|
8474
|
$line = str_replace(' ', '', $csvline[1]);
|
8475
|
} else {
|
8476
|
$line = $csvline[1];
|
8477
|
}
|
8478
|
$csv_found = TRUE;
|
8479
|
}
|
8480
|
break;
|
8481
|
case 'bbc':
|
8482
|
if ($csv_count == 4) {
|
8483
|
$line = $csvline[0];
|
8484
|
$csv_found = TRUE;
|
8485
|
}
|
8486
|
break;
|
8487
|
case 'h3x':
|
8488
|
if ($csv_count == 8) {
|
8489
|
$line = $csvline[2];
|
8490
|
if (strpos($line, 'btc://') !== FALSE) {
|
8491
|
continue 2;
|
8492
|
}
|
8493
|
$csv_found = TRUE;
|
8494
|
}
|
8495
|
break;
|
8496
|
case 'otx':
|
8497
|
if ($csv_count == 3) {
|
8498
|
if (isset($alienvault_types[$csvline[0]])) {
|
8499
|
$line = $csvline[1];
|
8500
|
$csv_found = TRUE;
|
8501
|
} else {
|
8502
|
continue 2;
|
8503
|
}
|
8504
|
}
|
8505
|
break;
|
8506
|
case 'pon':
|
8507
|
if ($csv_count == 9) {
|
8508
|
$line = $csvline[2];
|
8509
|
$csv_found = TRUE;
|
8510
|
|
8511
|
// Collect additional IP csv entry
|
8512
|
if (is_ipaddrv4($csvline[0]) &&
|
8513
|
$pfb['dnsbl_ip'] != 'Disabled') {
|
8514
|
$parsed = sanitize_ipaddr($line, $custom, 'Disabled');
|
8515
|
if (validate_ipv4($parsed)) {
|
8516
|
$domain_data_ip[] = $parsed;
|
8517
|
$pfb['updateip'] = TRUE;
|
8518
|
$ipcount++;
|
8519
|
}
|
8520
|
}
|
8521
|
}
|
8522
|
break;
|
8523
|
case 'et':
|
8524
|
if ($csv_count == 3) {
|
8525
|
$line = $csvline[0];
|
8526
|
$csv_found = TRUE;
|
8527
|
}
|
8528
|
break;
|
8529
|
default:
|
8530
|
|
8531
|
// Parse Phishtank Feed
|
8532
|
if (strpos($line, 'phish_id,url,'
|
8533
|
. 'phish_detail_url') !== FALSE) {
|
8534
|
$csv_type = 'pt';
|
8535
|
continue 2;
|
8536
|
}
|
8537
|
|
8538
|
// Parse Bambenek Consulting Feed
|
8539
|
elseif (strpos($csvline[3], 'osint.'
|
8540
|
. 'bambenekconsulting.com') !== FALSE) {
|
8541
|
$csv_type = 'bbc';
|
8542
|
$line = $csvline[0];
|
8543
|
$csv_found = TRUE;
|
8544
|
$liteparser = TRUE;
|
8545
|
}
|
8546
|
|
8547
|
// Parse Alienvault OTX pulse Feed
|
8548
|
elseif ($line == 'Indicator type,Indicator,'
|
8549
|
. 'Description') {
|
8550
|
$csv_type = 'otx';
|
8551
|
$liteparser = FALSE;
|
8552
|
continue 2;
|
8553
|
}
|
8554
|
|
8555
|
// Parse Ponomocup Feed
|
8556
|
elseif (strpos($csvline[0], 'timestamp') !== FALSE) {
|
8557
|
$csv_type = 'pon';
|
8558
|
$liteparser = TRUE;
|
8559
|
continue 2;
|
8560
|
}
|
8561
|
|
8562
|
// Parse Proofpoint/ET IQRisk IPRep Feed
|
8563
|
elseif ($line == 'domain, category, score') {
|
8564
|
$csv_type = 'et';
|
8565
|
$liteparser = TRUE;
|
8566
|
continue 2;
|
8567
|
}
|
8568
|
|
8569
|
// Reset variables for CSV determination
|
8570
|
else {
|
8571
|
$csv_parser = $run_once = FALSE;
|
8572
|
}
|
8573
|
break;
|
8574
|
}
|
8575
|
|
8576
|
// Record Failed CSV Parse event
|
8577
|
if (!$csv_found || empty($csv_type)) {
|
8578
|
pfb_parsed_fail($header, '', $oline, $pfb['dnsbl_parse_err']);
|
8579
|
continue;
|
8580
|
}
|
8581
|
}
|
8582
|
$line = trim($line);
|
8583
|
|
8584
|
if (!$easylist) {
|
8585
|
|
8586
|
// Typical Host Feed format - Remove characters before space
|
8587
|
if (!$rev_format && strpos($line, ' ') !== FALSE) {
|
8588
|
$line = trim(strstr($line, ' ', FALSE));
|
8589
|
}
|
8590
|
|
8591
|
// Remove characters after space
|
8592
|
if (strpos($line, ' ') !== FALSE) {
|
8593
|
$line = strstr($line, ' ', TRUE);
|
8594
|
}
|
8595
|
|
8596
|
// Determine if line contains only an alpha-numeric Domain name
|
8597
|
if (!$liteparser) {
|
8598
|
|
8599
|
$lite = FALSE;
|
8600
|
if (strpos($line, '.') !== FALSE &&
|
8601
|
ctype_alnum(str_replace('.', '', $line))) {
|
8602
|
$lite = TRUE;
|
8603
|
}
|
8604
|
}
|
8605
|
else {
|
8606
|
$lite = TRUE;
|
8607
|
}
|
8608
|
}
|
8609
|
|
8610
|
if (!$lite) {
|
8611
|
|
8612
|
// If 'http|https|telnet|ftp://' found, remove
|
8613
|
if (strpos($line, '://') !== FALSE) {
|
8614
|
$line = substr($line, strpos($line, '://') + 3);
|
8615
|
}
|
8616
|
|
8617
|
// If '/' character found, remove characters after '/'
|
8618
|
if (strpos($line, '/') !== FALSE) {
|
8619
|
$line = strstr($line, '/', TRUE);
|
8620
|
}
|
8621
|
|
8622
|
// If '#' character found, remove characters after '#'
|
8623
|
if (strpos($line, '#') !== FALSE) {
|
8624
|
$line = strstr($line, '#', TRUE);
|
8625
|
}
|
8626
|
|
8627
|
// If '?' character found, remove characters after '?'
|
8628
|
if (strpos($line, '?') !== FALSE) {
|
8629
|
$line = strstr($line, '?', TRUE);
|
8630
|
}
|
8631
|
|
8632
|
// If special characters found, parse line for host
|
8633
|
if (strpos($line, ';') !== FALSE) {
|
8634
|
$host = parse_url($line);
|
8635
|
if (isset($host['host'])) {
|
8636
|
$line = $host['host'];
|
8637
|
} else {
|
8638
|
$line = strstr($line, ';', TRUE);
|
8639
|
}
|
8640
|
}
|
8641
|
|
8642
|
// Remove any Port numbers at end of line
|
8643
|
if (strpos($line, ':') !== FALSE) {
|
8644
|
$line = preg_replace("/:[0-9]{1,5}$/", '', $line);
|
8645
|
}
|
8646
|
}
|
8647
|
$line = trim($line);
|
8648
|
|
8649
|
// Collect any IPs found in domain feed
|
8650
|
if (is_ipaddrv4($line)) {
|
8651
|
if ($pfb['dnsbl_ip'] != 'Disabled') {
|
8652
|
$parsed = sanitize_ipaddr($line, $custom, 'Disabled');
|
8653
|
if (validate_ipv4($parsed)) {
|
8654
|
$domain_data_ip[] = $parsed;
|
8655
|
$pfb['updateip'] = TRUE;
|
8656
|
$ipcount++;
|
8657
|
}
|
8658
|
}
|
8659
|
continue;
|
8660
|
}
|
8661
|
|
8662
|
// Convert IDN (Unicode domains) to ASCII (punycode)
|
8663
|
if (!ctype_print($line)) {
|
8664
|
|
8665
|
// Convert encodings to UTF-8
|
8666
|
$line = mb_convert_encoding($line, 'UTF-8',
|
8667
|
mb_detect_encoding($line, 'UTF-8, ASCII, ISO-8859-1'));
|
8668
|
|
8669
|
$log = "\n IDN converted: [ {$line} ]\t";
|
8670
|
$line = idn_to_ascii($line);
|
8671
|
if (!empty($line)) {
|
8672
|
pfb_logger("{$log} [ {$line} ]", 1);
|
8673
|
}
|
8674
|
else {
|
8675
|
// Record failed parsed line
|
8676
|
pfb_parsed_fail($header, '', $oline, $pfb['dnsbl_parse_err']);
|
8677
|
continue;
|
8678
|
}
|
8679
|
}
|
8680
|
|
8681
|
// Remove leading/trailing dots
|
8682
|
$line = trim(trim($line), '.');
|
8683
|
|
8684
|
// Domain Validation
|
8685
|
if (empty(pfb_filter($line, PFB_FILTER_DOMAIN, 'DNSBL_Download'))) {
|
8686
|
|
8687
|
// Reset lite parser
|
8688
|
$liteparser = FALSE;
|
8689
|
|
8690
|
// Skip yHost '@' prefixed lines
|
8691
|
if (substr($line, 0, 1) == '@') {
|
8692
|
continue;
|
8693
|
}
|
8694
|
|
8695
|
// Log invalid Domains
|
8696
|
if (!isset($dnsbl_skip[$line])) {
|
8697
|
pfb_parsed_fail($header, $line, $oline, $pfb['dnsbl_parse_err']);
|
8698
|
}
|
8699
|
continue;
|
8700
|
}
|
8701
|
|
8702
|
// For DNSBL python, save domain and Logging type
|
8703
|
if ($pfb['dnsbl_py_blacklist']) {
|
8704
|
$domain_data = ',' . strtolower($line)
|
8705
|
. ",,{$logging_type},{$header},{$alias}\n";
|
8706
|
}
|
8707
|
else {
|
8708
|
$ipv6_dnsbl = "\n";
|
8709
|
if ($pfb['dnsbl_v6'] == 'on' && !$pfb['dnsbl_tld']) {
|
8710
|
$ipv6_dnsbl = " local-data: \"" . strtolower($line)
|
8711
|
. " 60 IN AAAA {$sinkhole_type6}\"\n";
|
8712
|
}
|
8713
|
$domain_data = "local-data: \"" . strtolower($line)
|
8714
|
. " 60 IN A {$sinkhole_type4}\"{$ipv6_dnsbl}";
|
8715
|
}
|
8716
|
@fwrite($dhandle, $domain_data);
|
8717
|
}
|
8718
|
}
|
8719
|
if ($dhandle) {
|
8720
|
@fclose($dhandle);
|
8721
|
}
|
8722
|
}
|
8723
|
if ($fhandle) {
|
8724
|
@fclose($fhandle);
|
8725
|
}
|
8726
|
if (isset($csvline)) {
|
8727
|
unset($csvline);
|
8728
|
}
|
8729
|
|
8730
|
// Remove duplicates and save any IPs found in domain feed
|
8731
|
if (!empty($domain_data_ip)) {
|
8732
|
$domain_data_ip = implode("\n", array_unique($domain_data_ip)) . "\n";
|
8733
|
@file_put_contents("{$pfbfolder}/{$header}_v4.ip", $domain_data_ip, LOCK_EX);
|
8734
|
$ip_cnt = exec("{$pfb['grep']} -c ^ " . escapeshellarg("{$pfbfolder}/{$header}_v4.ip"));
|
8735
|
}
|
8736
|
else {
|
8737
|
// Remove previous IP feed
|
8738
|
unlink_if_exists("{$pfbfolder}/{$header}_v4.ip");
|
8739
|
}
|
8740
|
|
8741
|
// Validate feed with Unbound-checkconf
|
8742
|
if (!empty($domain_data)) {
|
8743
|
$conf = "server:\n";
|
8744
|
$conf .= "chroot: {$pfb['dnsbldir']}\n";
|
8745
|
$conf .= "username: \"unbound\"\n";
|
8746
|
$conf .= "directory: \"{$pfb['dnsbldir']}\"\n";
|
8747
|
$conf .= "pidfile: \"/var/run/unbound.pid\"\n";
|
8748
|
$conf .= "server:include: {$pfbfolder}/{$header}.bk";
|
8749
|
@file_put_contents("{$pfb['dnsbldir']}/check.conf", $conf, LOCK_EX);
|
8750
|
|
8751
|
pfb_logger(".\n", 1);
|
8752
|
|
8753
|
// Bypass TOP1M whitelist, if user configured
|
8754
|
$pfb_alexa = 'Disabled';
|
8755
|
if ($pfb['dnsbl_alexa'] == 'on' &&
|
8756
|
$list['filter_alexa'] == 'on' &&
|
8757
|
file_exists("{$pfb['dbdir']}/pfbalexawhitelist.txt")) {
|
8758
|
$pfb_alexa = 'on';
|
8759
|
}
|
8760
|
|
8761
|
// DNSBL python requires a different deduplication process
|
8762
|
$dup_mode = '';
|
8763
|
if ($pfb['dnsbl_py_blacklist']) {
|
8764
|
$dup_mode = 'python';
|
8765
|
}
|
8766
|
|
8767
|
// Call script to process DNSBL 'De-Duplication / Whitelisting / TOP1M Whitelisting'
|
8768
|
exec("{$pfb['script']} dnsbl_scrub {$header_esc} {$pfb_alexa} {$dup_mode} {$elog}");
|
8769
|
|
8770
|
if ($ip_cnt > 0) {
|
8771
|
pfb_logger(" IPv4 count={$ip_cnt}\n", 1);
|
8772
|
}
|
8773
|
|
8774
|
if (!$pfb['dnsbl_py_blacklist']) {
|
8775
|
$result = array();
|
8776
|
exec("/usr/local/sbin/unbound-checkconf {$pfb['dnsbldir']}/check.conf 2>&1", $result);
|
8777
|
} else {
|
8778
|
$result = array('unbound-checkconf: no errors');
|
8779
|
}
|
8780
|
unlink_if_exists("{$pfb['dnsbldir']}/check.conf");
|
8781
|
}
|
8782
|
else {
|
8783
|
$log = "\n No Domains Found! Ensure only domain based Feeds are used for DNSBL!\n";
|
8784
|
pfb_logger("{$log}", 1);
|
8785
|
|
8786
|
// Copy downloaded file to /tmp for debugging
|
8787
|
$ts = date('M_j', time());
|
8788
|
@copy("{$file_dwn}.orig", "/tmp/Error_{$header}_{$ts}.orig");
|
8789
|
|
8790
|
unlink_if_exists("{$pfbfolder}/{$header}.bk");
|
8791
|
$result = array('unbound-checkconf: no errors');
|
8792
|
}
|
8793
|
|
8794
|
// If parse error found, use previously downloaded file if available
|
8795
|
if (!$pfb['dnsbl_py_blacklist'] && !preg_grep("/unbound-checkconf: no errors/", $result)) {
|
8796
|
unlink_if_exists("{$pfbfolder}/{$header}.bk");
|
8797
|
|
8798
|
pfb_logger("\n DNSBL FAIL - Skipped! Use previous data, if found:\n", 2);
|
8799
|
$log = htmlspecialchars(implode("\n", $result));
|
8800
|
pfb_logger("{$log}\n", 1);
|
8801
|
|
8802
|
// Create failed marker file
|
8803
|
touch("{$pfbfolder}/{$header}.fail");
|
8804
|
}
|
8805
|
|
8806
|
// Save DNSBL feed info for next steps
|
8807
|
$pfb['domain_update'] = $pfb['aliasupdate'] = $pfb['summary'] = TRUE;
|
8808
|
$lists_dnsbl_all[] = "{$row['header']}.txt";
|
8809
|
$lists_dnsbl_current[] = "{$row['header']}";
|
8810
|
|
8811
|
// Rename newly downloaded file to final location
|
8812
|
if (file_exists("{$pfbfolder}/{$header}.bk")) {
|
8813
|
@rename("{$pfbfolder}/{$header}.bk", "{$pfbfolder}/{$header}.txt");
|
8814
|
}
|
8815
|
|
8816
|
// Create empty placeholder file
|
8817
|
if (!file_exists("{$pfbfolder}/{$header}.txt")) {
|
8818
|
touch("{$pfbfolder}/{$header}.txt");
|
8819
|
}
|
8820
|
|
8821
|
$list_cnt = exec("{$pfb['grep']} -c ^ " . escapeshellarg("{$pfbfolder}/{$header}.txt"));
|
8822
|
$alias_cnt = $alias_cnt + $list_cnt;
|
8823
|
|
8824
|
// Remove update file indicator
|
8825
|
unlink_if_exists("{$pfbfolder}/{$header}.update");
|
8826
|
}
|
8827
|
}
|
8828
|
}
|
8829
|
|
8830
|
// If changes found update DNSBL alias and TLD disabled, call function to update DNSBL alias
|
8831
|
if ($pfb['aliasupdate'] && !$pfb['dnsbl_tld']) {
|
8832
|
dnsbl_alias_update('update', $alias, $pfbfolder, $lists_dnsbl_current, $alias_cnt);
|
8833
|
}
|
8834
|
|
8835
|
// Collect Alias/Feeds for post TLD function
|
8836
|
if ($pfb['dnsbl_tld']) {
|
8837
|
if (!is_array($pfb['tld_update'][$alias])) {
|
8838
|
$pfb['tld_update'][$alias] = array();
|
8839
|
}
|
8840
|
$pfb['tld_update'][$alias]['feeds'] = $lists_dnsbl_current;
|
8841
|
$pfb['tld_update'][$alias]['count'] = $alias_cnt;
|
8842
|
}
|
8843
|
}
|
8844
|
else {
|
8845
|
dnsbl_alias_update('disabled', $alias, '', '', '');
|
8846
|
}
|
8847
|
}
|
8848
|
|
8849
|
}
|
8850
|
|
8851
|
// Remove any unused DNSBL aliases
|
8852
|
$daliases = glob("{$pfb['dnsalias']}/*");
|
8853
|
if (!empty($daliases)) {
|
8854
|
foreach ($daliases as $dlist) {
|
8855
|
if (!in_array(basename($dlist), $pfb['alias_dnsbl_all'])) {
|
8856
|
unlink_if_exists ("{$dlist}");
|
8857
|
}
|
8858
|
}
|
8859
|
}
|
8860
|
|
8861
|
// Add DNSBL Python options to widget statistics
|
8862
|
if ($pfb['dnsbl_mode'] == 'dnsbl_python') {
|
8863
|
if ($pfb['dnsbl_idn'] == 'on') {
|
8864
|
$idn_cnt = 1;
|
8865
|
$pfb['alias_dnsbl_all'][] = 'DNSBL_IDN';
|
8866
|
dnsbl_alias_update('update', 'DNSBL_IDN', '', '', $idn_cnt);
|
8867
|
}
|
8868
|
if ($pfb['dnsbl_pytld'] == 'on') {
|
8869
|
$pytld_cnt = 0;
|
8870
|
foreach (array('gtld', 'cctld', 'itld', 'bgtld') as $pytld) {
|
8871
|
if (isset($pfb['dnsblconfig']['pfb_pytlds_' . $pytld]) && !empty($pfb['dnsblconfig']['pfb_pytlds_' . $pytld])) {
|
8872
|
$p_cnt = count(explode(',', $pfb['dnsblconfig']['pfb_pytlds_' . $pytld]));
|
8873
|
if (is_numeric($p_cnt) && $p_cnt > 0) {
|
8874
|
$pytld_cnt += $p_cnt;
|
8875
|
}
|
8876
|
}
|
8877
|
}
|
8878
|
|
8879
|
$pfb['alias_dnsbl_all'][] = 'DNSBL_TLD_Allow';
|
8880
|
dnsbl_alias_update('update', 'DNSBL_TLD_Allow', '', '', $pytld_cnt);
|
8881
|
}
|
8882
|
if ($pfb['dnsbl_regex'] == 'on') {
|
8883
|
$regex_cnt = 0;
|
8884
|
if (isset($pfb['dnsbl_regex_list'])) {
|
8885
|
$regex_cnt = count(pfbng_text_area_decode($pfb['dnsbl_regex_list'], TRUE, FALSE, FALSE)) ?: 0;
|
8886
|
}
|
8887
|
$pfb['alias_dnsbl_all'][] = 'DNSBL_Regex';
|
8888
|
dnsbl_alias_update('update', 'DNSBL_Regex', '', '', $regex_cnt);
|
8889
|
}
|
8890
|
}
|
8891
|
|
8892
|
// Save DNSBL Alias statistics (Not for TLD mode)
|
8893
|
if ($pfb['domain_update'] && !$pfb['dnsbl_tld']) {
|
8894
|
dnsbl_save_stats();
|
8895
|
}
|
8896
|
}
|
8897
|
|
8898
|
// Collect all DNSBL IP feeds (IPv4 only) into DNSBLIP_v4.txt
|
8899
|
if ($pfb['dnsbl_ip'] != 'Disabled' && ($pfb['updateip'] || !file_exists("{$pfb['dbdir']}/DNSBLIP_v4.txt"))) {
|
8900
|
|
8901
|
$dnsbl_ip = glob("{$pfb['dnsdir']}/*_v4.ip");
|
8902
|
if (!empty($dnsbl_ip)) {
|
8903
|
$pfb_ips = @fopen("{$pfb['dbdir']}/DNSBLIP_v4.txt", 'w');
|
8904
|
foreach ($dnsbl_ip as $d_ip) {
|
8905
|
if (($handle = @fopen("{$d_ip}", 'r')) !== FALSE) {
|
8906
|
while (($line = @fgets($handle)) !== FALSE) {
|
8907
|
@fwrite($pfb_ips, $line);
|
8908
|
}
|
8909
|
}
|
8910
|
if ($handle) {
|
8911
|
@fclose($handle);
|
8912
|
}
|
8913
|
}
|
8914
|
if ($pfb_ips) {
|
8915
|
@fclose($pfb_ips);
|
8916
|
}
|
8917
|
}
|
8918
|
|
8919
|
// Add empty placeholder IP
|
8920
|
else {
|
8921
|
@file_put_contents("{$pfb['dbdir']}/DNSBLIP_v4.txt", "{$pfb['ip_ph']}\n", LOCK_EX);
|
8922
|
}
|
8923
|
unlink_if_exists($pfb['ip_cache']);
|
8924
|
touch("{$pfb['denydir']}/DNSBLIP_v4.update");
|
8925
|
}
|
8926
|
|
8927
|
// Remove DNSBL IP feed, if disabled
|
8928
|
if ($pfb['dnsbl_ip'] == 'Disabled') {
|
8929
|
unlink_if_exists("{$pfb['dbdir']}/DNSBLIP_v4.txt");
|
8930
|
unlink_if_exists("{$pfb['denydir']}/DNSBLIP_v4.*");
|
8931
|
}
|
8932
|
|
8933
|
#########################################
|
8934
|
# UPDATE Unbound DNS Database #
|
8935
|
#########################################
|
8936
|
|
8937
|
if ($pfb['domain_update']) {
|
8938
|
if (!empty($lists_dnsbl_all)) {
|
8939
|
pfb_logger("\n------------------------------------------------------------------------\n", 1);
|
8940
|
|
8941
|
pfb_logger('Assembling DNSBL database...', 1);
|
8942
|
unlink_if_exists("{$pfb['dnsbl_file']}.raw");
|
8943
|
$pfb_output = @fopen("{$pfb['dnsbl_file']}.raw", 'w');
|
8944
|
foreach ($lists_dnsbl_all as $current_list) {
|
8945
|
if (($handle = @fopen("{$pfb['dnsdir']}/{$current_list}", 'r')) !== FALSE) {
|
8946
|
while (($line = @fgets($handle)) !== FALSE) {
|
8947
|
@fwrite($pfb_output, $line);
|
8948
|
}
|
8949
|
}
|
8950
|
if ($handle) {
|
8951
|
@fclose($handle);
|
8952
|
}
|
8953
|
}
|
8954
|
if ($pfb_output) {
|
8955
|
@fclose($pfb_output);
|
8956
|
}
|
8957
|
pfb_logger("... completed [ NOW ]", 1);
|
8958
|
|
8959
|
// DNSBL Python blocking mode, if TLD is not enabled
|
8960
|
if ($pfb['dnsbl_py_blacklist'] && !$pfb['dnsbl_tld']) {
|
8961
|
unlink_if_exists($pfb['unbound_py_data']);
|
8962
|
unlink_if_exists($pfb['unbound_py_zone']);
|
8963
|
unlink_if_exists($pfb['unbound_py_count']);
|
8964
|
rename("{$pfb['dnsbl_file']}.raw", $pfb['unbound_py_data']);
|
8965
|
}
|
8966
|
}
|
8967
|
|
8968
|
else {
|
8969
|
$log = "\nDNSBL not Updated!\n";
|
8970
|
pfb_logger("{$log}", 1);
|
8971
|
}
|
8972
|
}
|
8973
|
else {
|
8974
|
if ($pfb['enable'] == 'on' && $pfb['dnsbl'] == 'on') {
|
8975
|
|
8976
|
// When DNSBL is enabled and no Aliases are defined, or all Aliases are Disabled
|
8977
|
if (empty($lists_dnsbl_all) && !$pfb['save']) {
|
8978
|
pfb_logger("\nClearing all DNSBL Feeds", 1);
|
8979
|
$pfb['domain_clear'] = TRUE;
|
8980
|
|
8981
|
// Clear out Unbound pfb_dnsbl.conf file
|
8982
|
if (!$pfb['dnsbl_py_blacklist']) {
|
8983
|
$pfb_output = @fopen("{$pfb['dnsbl_file']}.conf", 'w');
|
8984
|
@fwrite($pfb_output, '');
|
8985
|
@fclose($pfb_output);
|
8986
|
}
|
8987
|
|
8988
|
// Remove DNSBL Python files
|
8989
|
else {
|
8990
|
unlink_if_exists($pfb['unbound_py_data']);
|
8991
|
unlink_if_exists($pfb['unbound_py_zone']);
|
8992
|
unlink_if_exists($pfb['unbound_py_wh']);
|
8993
|
unlink_if_exists($pfb['unbound_py_count']);
|
8994
|
}
|
8995
|
}
|
8996
|
}
|
8997
|
else {
|
8998
|
foreach (array("{$pfb['dnsbl_file']}.conf", $pfb['unbound_py_data'], $pfb['unbound_py_zone']) as $pfb_file) {
|
8999
|
if (file_exists($pfb_file)) {
|
9000
|
$pfb['domain_clear'] = TRUE;
|
9001
|
@unlink($pfb_file);
|
9002
|
}
|
9003
|
}
|
9004
|
}
|
9005
|
}
|
9006
|
|
9007
|
#################################
|
9008
|
# UNBOUND INTEGRATION #
|
9009
|
#################################
|
9010
|
|
9011
|
$pfbupdate = $pfbpython = FALSE;
|
9012
|
if ($pfb['enable'] == 'on' && $pfb['dnsbl'] == 'on' && $pfb['unbound_state'] == 'on') {
|
9013
|
$mode = 'enabled';
|
9014
|
}
|
9015
|
elseif (($pfb['enable'] == '' || $pfb['dnsbl'] == '') && !$pfb['install']) {
|
9016
|
$mode = 'disabled';
|
9017
|
}
|
9018
|
|
9019
|
// Modify Unbound python configuration and mount lib/bin folders, as required
|
9020
|
$pfbpython = pfb_unbound_python($mode);
|
9021
|
|
9022
|
// Modify Unbound.conf file, as required
|
9023
|
$pfbupdate = pfb_unbound_dnsbl($mode);
|
9024
|
|
9025
|
// Modify DNSBL NAT and VIP and lighttpd web server conf, as required.
|
9026
|
pfb_create_dnsbl($mode);
|
9027
|
|
9028
|
// Load new DNSBL updates to Unbound Resolver, as required
|
9029
|
if ($pfb['domain_update'] || $pfbupdate || $pfbpython ||$pfb['domain_clear'] || $safesearch_update) {
|
9030
|
|
9031
|
// Create backup of existing DNSBL Unbound database
|
9032
|
if (!$pfb['dnsbl_py_blacklist'] && file_exists("{$pfb['dnsbl_file']}.conf")) {
|
9033
|
@copy("{$pfb['dnsbl_file']}.conf", "{$pfb['dnsbl_file']}.bk");
|
9034
|
}
|
9035
|
|
9036
|
pfb_update_unbound($mode, $pfbupdate, $pfbpython);
|
9037
|
}
|
9038
|
|
9039
|
|
9040
|
#################################
|
9041
|
# Assign Countries #
|
9042
|
#################################
|
9043
|
|
9044
|
if (!$pfb['save']) {
|
9045
|
$log = "\n\n===[ GeoIP Process ]============================================\n";
|
9046
|
pfb_logger("{$log}", 1);
|
9047
|
}
|
9048
|
|
9049
|
// Download MaxMind Databases if not found
|
9050
|
$maxmind_verify = FALSE;
|
9051
|
if (!empty($pfb['maxmind_key']) && !empty($pfb['maxmind_account'])) {
|
9052
|
|
9053
|
$maxmind_verify = TRUE;
|
9054
|
if (!file_exists("{$pfb['geoipshare']}/GeoLite2-Country.mmdb") ||
|
9055
|
!file_exists("{$pfb['geoipshare']}/GeoLite2-Country-Blocks-IPv4.csv") ||
|
9056
|
!file_exists("{$pfb['dbdir']}/geoip.txt") ||
|
9057
|
!file_exists("{$pfb['ccdir']}/Top_Spammers_v4.info")) {
|
9058
|
|
9059
|
// Check if MaxMind download already in progress
|
9060
|
exec('/bin/ps -wax', $result_cron);
|
9061
|
if (!preg_grep("/pfblockerng[.]php\s+dc/", $result_cron)) {
|
9062
|
$log = "\nMaxMind Database downloading and processing ( approx 4MB ) ... Please wait ...\n";
|
9063
|
pfb_logger("{$log}", 1);
|
9064
|
exec("/usr/local/bin/php /usr/local/www/pfblockerng/pfblockerng.php dc >> {$pfb['log']} 2>&1");
|
9065
|
restart_service('pfb_filter');
|
9066
|
}
|
9067
|
else {
|
9068
|
$log = "\nMaxMind download already in process...\n";
|
9069
|
pfb_logger("{$log}", 1);
|
9070
|
}
|
9071
|
}
|
9072
|
}
|
9073
|
|
9074
|
$maxmind_run_once = TRUE;
|
9075
|
foreach ($pfb['continents'] as $continent => $pfb_alias) {
|
9076
|
$cont_key = 'pfblockerng' . strtolower(str_replace(' ', '', $continent));
|
9077
|
if (!empty(config_get_path("installedpackages/{$cont_key}/config"))) {
|
9078
|
$continent_config = config_get_path("installedpackages/{$cont_key}/config/0");
|
9079
|
$cc_name = 'pfblockerng' . strtolower(str_replace(' ', '', $continent));
|
9080
|
if (isset($continent_config['action']) && $continent_config['action'] != 'Disabled' && $pfb['enable'] == 'on') {
|
9081
|
|
9082
|
// Maxmind License Key verification and user notification
|
9083
|
if ($maxmind_run_once && !$maxmind_verify) {
|
9084
|
$mmsg = 'MaxMind now requires a License Key! Review the IP tab: MaxMind settings for more information.';
|
9085
|
pfb_logger("\n\nURGENT:\n {$mmsg}\n", 1);
|
9086
|
file_notice('pfBlockerNG MaxMind', $mmsg, 'pfBlockerNG', '/pfblockerng/pfblockerng_ip.php', 2);
|
9087
|
$maxmind_run_once = FALSE;
|
9088
|
}
|
9089
|
|
9090
|
$urlvalue = ''; // Firewall: Aliases value field
|
9091
|
|
9092
|
// Determine if Continent lists require action (IPv4 and IPv6)
|
9093
|
foreach ($cont_types as $c_type => $vtype) {
|
9094
|
|
9095
|
$cc_alias = "{$pfb_alias}{$vtype}";
|
9096
|
|
9097
|
// Determine 'list' details (return array $pfbarr)
|
9098
|
pfb_determine_list_detail($continent_config['action'], "{$cc_alias}", $cc_name, '0');
|
9099
|
$pfbadv = $pfbarr['adv'];
|
9100
|
$pfbdescr = $pfbarr['descr'];
|
9101
|
$pfbfolder = $pfbarr['folder'];
|
9102
|
$pfborig = $pfbarr['orig'];
|
9103
|
$logtab = $pfbarr['logtab'];
|
9104
|
|
9105
|
if (!empty($continent_config[$c_type])) {
|
9106
|
|
9107
|
// Collect selected GeoIP ISOs
|
9108
|
if (($pfb_output = @fopen("{$pfb['geoip_tmp']}", 'w')) !== FALSE) {
|
9109
|
foreach (explode(',', $continent_config[$c_type]) as $iso) {
|
9110
|
|
9111
|
$urlvalue .= "{$iso},";
|
9112
|
$isofile = "{$pfb['ccdir']}/{$iso}{$vtype}.txt";
|
9113
|
if (($handle = @fopen("{$isofile}", 'r')) !== FALSE) {
|
9114
|
while (($line = @fgets($handle)) !== FALSE) {
|
9115
|
@fwrite($pfb_output, $line);
|
9116
|
}
|
9117
|
}
|
9118
|
else {
|
9119
|
pfb_logger("\nCould not open ISO [ {$iso}{$vtype} ]\n", 1);
|
9120
|
}
|
9121
|
if ($handle) {
|
9122
|
@fclose($handle);
|
9123
|
}
|
9124
|
}
|
9125
|
}
|
9126
|
else {
|
9127
|
pfb_logger("\n[ {$cc_alias} ] Could not create GeoIP file handle\n", 1);
|
9128
|
}
|
9129
|
if ($pfb_output) {
|
9130
|
@fclose($pfb_output);
|
9131
|
}
|
9132
|
|
9133
|
// Collect md5 of new Continent data
|
9134
|
$continent = 'md5_0';
|
9135
|
if (file_exists("{$pfb['geoip_tmp']}")) {
|
9136
|
$continent = @md5_file("{$pfb['geoip_tmp']}");
|
9137
|
}
|
9138
|
|
9139
|
// Collect md5 of existing Continent data
|
9140
|
$continent_ex = 'md5_1';
|
9141
|
if (file_exists("{$pfborig}/{$cc_alias}.orig")) {
|
9142
|
$continent_ex = @md5_file("{$pfborig}/{$cc_alias}.orig");
|
9143
|
}
|
9144
|
|
9145
|
// Check if pfBlockerNG pfctl Continent tables are empty (pfBlockerNG was disabled w/ "keep", then re-enabled)
|
9146
|
$pfctlck = exec("{$pfb['pfctl']} -vvsTables | {$pfb['grep']} -A1 {$cc_alias} | {$pfb['awk']} '/Addresses/ {s+=\$2}; END {print s}'");
|
9147
|
|
9148
|
if (empty($pfctlck) && file_exists("{$pfbfolder}/{$cc_alias}.txt")) {
|
9149
|
@copy("{$pfbfolder}/{$cc_alias}.txt", "{$pfb['aliasdir']}/{$cc_alias}.txt");
|
9150
|
// Collect updated alias lists ('Reputation' disabled)
|
9151
|
$pfb_alias_lists[] = "{$cc_alias}";
|
9152
|
}
|
9153
|
|
9154
|
// Collect active alias lists (Used for pfctl update when 'Reputation' is enabled).
|
9155
|
$pfb_alias_lists_all[] = "{$cc_alias}";
|
9156
|
|
9157
|
// Compare existing (original file) and new Continent data
|
9158
|
if ($continent == $continent_ex && !empty($pfctlck)
|
9159
|
&& file_exists("{$pfbfolder}/{$cc_alias}.txt") && $pfb['reuse'] == ''
|
9160
|
&& !file_exists("{$pfb['dbdir']}/geoip.update")) {
|
9161
|
if (!$pfb['save']) {
|
9162
|
$log = "\n[ {$cc_alias} ]{$logtab} exists. [ NOW ]";
|
9163
|
pfb_logger("{$log}", 1);
|
9164
|
}
|
9165
|
} else {
|
9166
|
// Do not proceed with changes on user 'save'
|
9167
|
if (!$pfb['save']) {
|
9168
|
$log = "\n[ {$cc_alias} ]{$logtab} Changes found... Updating\n";
|
9169
|
pfb_logger("{$log}", 1);
|
9170
|
|
9171
|
// Execute Reputation functions, when changes are found.
|
9172
|
if ($pfbadv && $vtype == '_v4') {
|
9173
|
$pfb['repcheck'] = TRUE;
|
9174
|
}
|
9175
|
|
9176
|
// Collect updated alias lists ('Reputation' disabled)
|
9177
|
$pfb_alias_lists[] = "{$cc_alias}";
|
9178
|
|
9179
|
if ($continent != 'md5_0') {
|
9180
|
@rename("{$pfb['geoip_tmp']}", "{$pfborig}/{$cc_alias}.orig");
|
9181
|
@copy("{$pfborig}/{$cc_alias}.orig", "{$pfbfolder}/{$cc_alias}.txt");
|
9182
|
|
9183
|
// Call Aggregate process
|
9184
|
if ($pfb['agg'] == 'on' && $vtype == '_v4') {
|
9185
|
exec("{$pfb['script']} cidr_aggregate {$cc_alias} {$pfbfolder} {$elog}");
|
9186
|
}
|
9187
|
|
9188
|
// Call Duplication process
|
9189
|
if ($pfb['dup'] == 'on' && $vtype == '_v4' && $pfbadv) {
|
9190
|
exec("{$pfb['script']} continent {$cc_alias} {$elog}");
|
9191
|
}
|
9192
|
|
9193
|
// Save Continent data to aliastable folder
|
9194
|
@copy("{$pfbfolder}/{$cc_alias}.txt", "{$pfb['aliasdir']}/{$cc_alias}.txt");
|
9195
|
}
|
9196
|
|
9197
|
// Check if file exists and is > 0 in size and save alias file
|
9198
|
$file_chk = 0;
|
9199
|
$cont_chk = "{$pfbfolder}/{$cc_alias}.txt";
|
9200
|
if (file_exists($cont_chk) && @filesize($cont_chk) > 0) {
|
9201
|
$file_chk = exec("{$pfb['grep']} -cv '^#\|^\$' {$cont_chk}");
|
9202
|
}
|
9203
|
|
9204
|
if ($file_chk <= 1) {
|
9205
|
if ($vtype == '_v6') {
|
9206
|
$p_ip = "::{$pfb['ip_ph']}";
|
9207
|
} else {
|
9208
|
$p_ip = $pfb['ip_ph'];
|
9209
|
}
|
9210
|
|
9211
|
@file_put_contents("{$pfbfolder}/{$cc_alias}.txt", "{$p_ip}\n", LOCK_EX);
|
9212
|
@copy("{$pfbfolder}/{$cc_alias}.txt", "{$pfb['aliasdir']}/{$cc_alias}.txt");
|
9213
|
$log = "[ {$cc_alias} ] Found no unique IPs, adding '{$p_ip}' to avoid empty file\n";
|
9214
|
pfb_logger("{$log}", 1);
|
9215
|
}
|
9216
|
}
|
9217
|
}
|
9218
|
|
9219
|
if (file_exists("{$pfbfolder}/{$cc_alias}.txt")) {
|
9220
|
// Create alias config
|
9221
|
$new_aliases_list[] = "{$cc_alias}";
|
9222
|
$new_aliases[] = array( 'name' => "{$cc_alias}",
|
9223
|
'url' => "{$pfb['weblocal']}?pfb={$cc_alias}",
|
9224
|
'updatefreq' => '32',
|
9225
|
'address' => '',
|
9226
|
'descr' => "pfBlockerNG {$pfbdescr} GeoIP Alias [ {$urlvalue} ]",
|
9227
|
'type' => 'urltable',
|
9228
|
'detail' => 'DO NOT EDIT THIS ALIAS'
|
9229
|
);
|
9230
|
|
9231
|
// Define firewall rule settings
|
9232
|
pfb_firewall_rule($continent_config['action'], $cc_alias, $vtype, $continent_config['aliaslog'],
|
9233
|
$pfbarr['agateway_in'], $pfbarr['agateway_out'], $pfbarr['aaddrnot_in'], $pfbarr['aaddr_in'],
|
9234
|
$pfbarr['aports_in'], $pfbarr['aproto_in'], $pfbarr['anot_in'], $pfbarr['aaddrnot_out'],
|
9235
|
$pfbarr['aaddr_out'], $pfbarr['aports_out'], $pfbarr['aproto_out'], $pfbarr['anot_out']);
|
9236
|
}
|
9237
|
else {
|
9238
|
// unlink Continent list
|
9239
|
unlink_if_exists("{$pfb['aliasdir']}/{$cc_alias}.txt");
|
9240
|
}
|
9241
|
}
|
9242
|
}
|
9243
|
}
|
9244
|
}
|
9245
|
}
|
9246
|
|
9247
|
// Remove temp file
|
9248
|
unlink_if_exists("{$pfb['geoip_tmp']}");
|
9249
|
|
9250
|
#################################################
|
9251
|
# Download and Collect IPv4/IPv6 lists #
|
9252
|
#################################################
|
9253
|
|
9254
|
// IPv4 REGEX Definitions
|
9255
|
$pfb['range'] = '/((?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))-((?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))/';
|
9256
|
$pfb['ipv4'] = '/(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)(?:\/(?:\d|[12]\d|3[0-2])\b)?/';
|
9257
|
|
9258
|
// IPv6 REGEX Definitions - Reference: http://labs.spritelink.net/regex
|
9259
|
$pfb['ipv6'] = '/(?:(?:(?:[[:xdigit:]]{1,4}:){7}(?:[[:xdigit:]]{1,4}|:))|(?:(?:[[:xdigit:]]{1,4}:){6}(?::[[:xdigit:]]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[[:xdigit:]]{1,4}:){5}(?:(?:(?::[[:xdigit:]]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[[:xdigit:]]{1,4}:){4}(?:(?:(?::[[:xdigit:]]{1,4}){1,3})|(?:(?::[[:xdigit:]]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[[:xdigit:]]{1,4}:){3}(?:(?:(?::[[:xdigit:]]{1,4}){1,4})|(?:(?::[[:xdigit:]]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[[:xdigit:]]{1,4}:){2}(?:(?:(?::[[:xdigit:]]{1,4}){1,5})|(?:(?::[[:xdigit:]]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[[:xdigit:]]{1,4}:){1}(?:(?:(?::[[:xdigit:]]{1,4}){1,6})|(?:(?::[[:xdigit:]]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[[:xdigit:]]{1,4}){1,7})|(?:(?::[[:xdigit:]]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?(?:\/(?:1(?:2[0-8]|[01]\d)|[1-9]?\d)\b)?/';
|
9260
|
|
9261
|
if ($pfb['enable'] == 'on' && !$pfb['save']) {
|
9262
|
|
9263
|
$pfb['supp_update'] = FALSE;
|
9264
|
$runonce_v4 = $runonce_v6 = TRUE;
|
9265
|
$lists = array();
|
9266
|
|
9267
|
// Collect lists and custom list configuration and format into one array ($lists).
|
9268
|
foreach ($ip_types as $ip_type => $vtype) {
|
9269
|
foreach (config_get_path("installedpackages/{$ip_type}/config", []) as $key => $list) {
|
9270
|
if (!is_array($list)) {
|
9271
|
$list = array();
|
9272
|
}
|
9273
|
if (!is_array($list['row'])) {
|
9274
|
$list['row'] = array();
|
9275
|
}
|
9276
|
|
9277
|
$list['vtype'] = "{$vtype}"; // Collect list IP type
|
9278
|
$list['key'] = "{$key}"; // Collect list array key location
|
9279
|
|
9280
|
// If only the 'customlist' is defined. Remove the 'List row' data.
|
9281
|
if (empty(array_get_path($list, 'row/0/url'))) {
|
9282
|
unset($list['row']);
|
9283
|
}
|
9284
|
|
9285
|
if (!empty($list['custom'])) {
|
9286
|
array_init_path($list, 'row');
|
9287
|
$list['row'][] = array( 'header' => "{$list['aliasname']}_custom",
|
9288
|
'custom' => $list['custom'],
|
9289
|
'state' => 'Enabled',
|
9290
|
'url' => 'custom'
|
9291
|
);
|
9292
|
}
|
9293
|
$lists[] = $list;
|
9294
|
}
|
9295
|
|
9296
|
// Add DNSBLIP, if configured (IPv4 only)
|
9297
|
if ($pfb['dnsbl'] == 'on' && $pfb['dnsbl_ip'] != 'Disabled' && $vtype == '_v4') {
|
9298
|
|
9299
|
$list = array( 'aliasname' => 'DNSBLIP',
|
9300
|
'vtype' => "{$vtype}",
|
9301
|
'key' => 0,
|
9302
|
'dnsblip' => '',
|
9303
|
'action' => "{$pfb['dnsbl_ip']}",
|
9304
|
);
|
9305
|
|
9306
|
$list['row'][] = array( 'format' => 'auto',
|
9307
|
'state' => 'Enabled',
|
9308
|
'url' => "{$pfb['dbdir']}/DNSBLIP{$vtype}.txt",
|
9309
|
'header' => 'DNSBLIP');
|
9310
|
$lists[] = $list;
|
9311
|
}
|
9312
|
}
|
9313
|
|
9314
|
$maxmind_run_once = $asn_run_once = TRUE;
|
9315
|
foreach ($lists as $list) {
|
9316
|
if ($runonce_v4 && $list['vtype'] == '_v4') {
|
9317
|
$runonce_v4 = FALSE;
|
9318
|
$log = "\n\n===[ IPv4 Process ]=================================================\n";
|
9319
|
pfb_logger("{$log}", 1);
|
9320
|
} elseif ($runonce_v6 && $list['vtype'] == '_v6') {
|
9321
|
$runonce_v6 = FALSE;
|
9322
|
$log = "\n\n===[ IPv6 Process ]=================================================\n";
|
9323
|
pfb_logger("{$log}", 1);
|
9324
|
}
|
9325
|
|
9326
|
if ($list['action'] != 'Disabled' && isset($list['row'])) {
|
9327
|
$alias = "pfB_{$list['aliasname']}{$list['vtype']}"; // Capture Alias name
|
9328
|
if (empty(pfb_filter($alias, PFB_FILTER_WORD, 'Download and Collect IPv4/IPv6 lists'))) {
|
9329
|
pfb_logger("\n Invalid Aliasname:{$list['aliasname']}{$list['vtype']}, *skipping*", 1);
|
9330
|
continue;
|
9331
|
}
|
9332
|
|
9333
|
foreach ($list['row'] as $row) {
|
9334
|
if (!empty($row['url']) && $row['state'] != 'Disabled') {
|
9335
|
$header = "{$row['header']}{$list['vtype']}"; // Capture Header/Label name
|
9336
|
if (empty(pfb_filter($header, PFB_FILTER_WORD, 'Download and Collect IPv4/IPv6 lists'))) {
|
9337
|
pfb_logger("\n Invalid Aliasname:{$list['aliasname']}{$list['vtype']} | Header:{$row['header']}{$list['vtype']}, *skipping*", 1);
|
9338
|
continue;
|
9339
|
}
|
9340
|
$header_esc = escapeshellarg($header);
|
9341
|
|
9342
|
// If row is a custom_list, set flag.
|
9343
|
if (isset($row['custom'])) {
|
9344
|
$custom = TRUE;
|
9345
|
} else {
|
9346
|
$custom = FALSE;
|
9347
|
}
|
9348
|
|
9349
|
// Maxmind License Key verification
|
9350
|
if ($maxmind_run_once && $row['format'] == 'geoip') {
|
9351
|
$mmsg = 'MaxMind now requires an Account ID and License Key! Review the IP tab: MaxMind settings for more information.';
|
9352
|
if (empty($pfb['maxmind_key']) || empty($pfb['maxmind_account'])) {
|
9353
|
pfb_logger("\n\nURGENT:\n {$mmsg}.\n", 1);
|
9354
|
file_notice('pfBlockerNG MaxMind', $mmsg, 'pfBlockerNG', '/pfblockerng/pfblockerng_ip.php', 2);
|
9355
|
}
|
9356
|
$maxmind_run_once = FALSE;
|
9357
|
}
|
9358
|
|
9359
|
// IPinfo ASN Token verification
|
9360
|
if ($asn_run_once && $row['format'] == 'asn') {
|
9361
|
$mmsg = 'To utilize the ASN functionality, you must register for a free IPinfo Account. Review IP Tab for more information.';
|
9362
|
if (empty($pfb['asn_token'])) {
|
9363
|
pfb_logger($mmsg, $logtype);
|
9364
|
file_notice('pfBlockerNG ASN', $mmsg, 'pfBlockerNG', '/pfblockerng/pfblockerng_ip.php', 2);
|
9365
|
}
|
9366
|
$asn_run_once = FALSE;
|
9367
|
}
|
9368
|
|
9369
|
// IPv4 Advanced Tunables
|
9370
|
$pfbcidr = 'Disabled';
|
9371
|
if (isset($list['suppression_cidr']) && $list['suppression_cidr'] != 'Disabled' && is_numeric($list['suppression_cidr'])) {
|
9372
|
$pfbcidr = "{$list['suppression_cidr']}";
|
9373
|
}
|
9374
|
|
9375
|
// cURL Source Interface (sets CURLOPT_INTERFACE)
|
9376
|
$srcint = $list['srcint'] ?: FALSE;
|
9377
|
|
9378
|
// IP v4/6 Advanced Tunable - (Pre/Post Script processing)
|
9379
|
$pfb_script_pre = FALSE;
|
9380
|
if (isset($list['script_pre']) && !empty($list['script_pre'])) {
|
9381
|
$script_pre = basename($list['script_pre']);
|
9382
|
if (file_exists("/usr/local/pkg/pfblockerng/{$script_pre}")) {
|
9383
|
$pfb_script_pre = "/usr/local/pkg/pfblockerng/{$script_pre}";
|
9384
|
}
|
9385
|
}
|
9386
|
|
9387
|
$pfb_script_post = FALSE;
|
9388
|
if (isset($list['script_post']) && !empty($list['script_post'])) {
|
9389
|
$script_post = basename($list['script_post']);
|
9390
|
if (file_exists("/usr/local/pkg/pfblockerng/{$script_post}")) {
|
9391
|
$pfb_script_post = "/usr/local/pkg/pfblockerng/{$script_post}";
|
9392
|
}
|
9393
|
}
|
9394
|
|
9395
|
// Determine 'list' details (return array $pfbarr)
|
9396
|
if (isset($list['dnsblip'])) {
|
9397
|
$list_type = 'pfblockerngdnsblsettings';
|
9398
|
} else {
|
9399
|
$list_type = "{$ip_type}";
|
9400
|
}
|
9401
|
|
9402
|
pfb_determine_list_detail($list['action'], $header, $list_type, $list['key']);
|
9403
|
$pfbadv = $pfbarr['adv'];
|
9404
|
$pfbfolder = $pfbarr['folder'];
|
9405
|
$pfborig = $pfbarr['orig'];
|
9406
|
$pfbreuse = $pfbarr['reuse'];
|
9407
|
$logtab = $pfbarr['logtab'];
|
9408
|
|
9409
|
// Collect active alias list (Used for pfctl update when 'Reputation' is enabled.
|
9410
|
$pfb_alias_lists_all[] = "{$alias}";
|
9411
|
|
9412
|
// Set update flags on new downloads available for GeoIP and ASN
|
9413
|
if ($row['format'] == 'geoip' && file_exists("{$pfb['dbdir']}/geoip.update")) {
|
9414
|
touch("{$pfbfolder}/{$header}.update");
|
9415
|
}
|
9416
|
if ($row['format'] == 'asn' && file_exists("{$pfb['dbdir']}/asn.update")) {
|
9417
|
touch("{$pfbfolder}/{$header}.update");
|
9418
|
}
|
9419
|
|
9420
|
if (file_exists("{$pfbfolder}/{$header}.txt") &&
|
9421
|
!file_exists("{$pfbfolder}/{$header}.update") &&
|
9422
|
!file_exists("{$pfbfolder}/{$header}.fail") &&
|
9423
|
$pfbreuse == '') {
|
9424
|
|
9425
|
if ($row['state'] == 'Hold') {
|
9426
|
$log = "\n[ {$header} ]{$logtab} static hold. [ NOW ]";
|
9427
|
} else {
|
9428
|
$log = "\n[ {$header} ]{$logtab} exists. [ NOW ]";
|
9429
|
}
|
9430
|
pfb_logger("{$log}", 1);
|
9431
|
}
|
9432
|
else {
|
9433
|
if ($pfbreuse == 'on' && file_exists("{$pfborig}/{$header}.orig")) {
|
9434
|
$log = "\n[ {$header} ]{$logtab} Reload [ NOW ]";
|
9435
|
} else {
|
9436
|
$log = "\n[ {$header} ]{$logtab} Downloading update [ NOW ]";
|
9437
|
}
|
9438
|
pfb_logger("{$log}", 1);
|
9439
|
$file_dwn = "{$pfborig}/{$header}";
|
9440
|
|
9441
|
// Force 'Alias Native' setting to any Alias with 'Advanced Inbound/Outbound -Invert src/dst' settings.
|
9442
|
// This will bypass Deduplication and Reputation features.
|
9443
|
if ($pfbarr['aaddrnot_in'] == 'on' || $pfbarr['aaddrnot_out'] == 'on') {
|
9444
|
pfb_logger("Using Alias Native\n", 1);
|
9445
|
}
|
9446
|
|
9447
|
if (!$custom) {
|
9448
|
pfb_logger(' .', 1);
|
9449
|
|
9450
|
// Allow cURL SSL downgrade 'Flex' if user configured.
|
9451
|
$pflex = FALSE;
|
9452
|
if ($row['state'] == 'Flex') {
|
9453
|
$pflex = TRUE;
|
9454
|
}
|
9455
|
|
9456
|
// Adjust 'geoip' format to GeoIP path location
|
9457
|
if ($row['format'] == 'geoip') {
|
9458
|
if (strpos($row['url'], ' ') !== FALSE) {
|
9459
|
$row['url'] = strstr($row['url'], ' ', TRUE);
|
9460
|
}
|
9461
|
if (!empty(pfb_filter($row['url'], PFB_FILTER_WORD, 'Adjust geoip format to GeoIP path location'))) {
|
9462
|
$row['url'] = "/usr/local/share/GeoIP/cc/{$row['url']}{$list['vtype']}.txt";
|
9463
|
} else {
|
9464
|
$row['url'] = '';
|
9465
|
}
|
9466
|
}
|
9467
|
|
9468
|
// Remove 'whois' source field description
|
9469
|
elseif ($row['format'] == 'asn') {
|
9470
|
if (strpos($row['url'], ' ') !== FALSE) {
|
9471
|
$row['url'] = strstr($row['url'], ' ', TRUE);
|
9472
|
}
|
9473
|
}
|
9474
|
|
9475
|
// Determine if list needs to be downloaded or reuse previously downloaded file.
|
9476
|
if ($pfbreuse == 'on' && file_exists("{$file_dwn}.orig")) {
|
9477
|
// File exists/reuse
|
9478
|
|
9479
|
// Process Emerging Threats IQRisk if required
|
9480
|
if (strpos($row['url'], 'iprepdata.txt') !== FALSE) {
|
9481
|
if (file_exists("{$file_dwn}.raw")) {
|
9482
|
$file_dwn_esc = escapeshellarg("{$file_dwn}.raw");
|
9483
|
$file_org_esc = escapeshellarg("{$file_dwn}.orig");
|
9484
|
exec("/usr/bin/gunzip -c {$file_dwn_esc} > {$file_org_esc}");
|
9485
|
}
|
9486
|
exec("{$pfb['script']} et {$header_esc} x x x x x {$pfb['etblock']} {$pfb['etmatch']} {$elog}");
|
9487
|
}
|
9488
|
}
|
9489
|
else {
|
9490
|
// Download list
|
9491
|
if (!pfb_download($row['url'], $file_dwn, $pflex, $header, $row['format'],
|
9492
|
1, $list['vtype'], '', '', '', '', $srcint)) {
|
9493
|
|
9494
|
// Determine reason for download failure
|
9495
|
pfb_download_failure($alias, $header, $pfbfolder, $row['url'], $row['format'], $list['vtype']);
|
9496
|
|
9497
|
// Utilize previously download file (If 'fail' marker exists)
|
9498
|
if (file_exists("{$pfbfolder}/{$header}.fail") &&
|
9499
|
file_exists("{$file_dwn}.orig")) {
|
9500
|
pfb_logger("\n Restoring previously downloaded file contents...", 2);
|
9501
|
}
|
9502
|
else {
|
9503
|
if ($pfbadv) {
|
9504
|
// Script to Remove failed lists from masterfile
|
9505
|
exec("{$pfb['script']} remove x x x {$header_esc} {$elog}");
|
9506
|
}
|
9507
|
continue;
|
9508
|
}
|
9509
|
}
|
9510
|
else {
|
9511
|
// Clear any previous download fail marker
|
9512
|
unlink_if_exists("{$pfbfolder}/{$header}.fail");
|
9513
|
pfb_logger('.', 1);
|
9514
|
}
|
9515
|
}
|
9516
|
pfb_logger(' completed .', 1);
|
9517
|
}
|
9518
|
else {
|
9519
|
if ($list['whois_convert'] == 'on') {
|
9520
|
// Process Domain/AS based custom list
|
9521
|
$custom_list = str_replace("\n", ',', pfbng_text_area_decode($list['custom'], FALSE, TRUE, TRUE));
|
9522
|
if (!empty(pfb_filter($custom_list, PFB_FILTER_CSV_WHOIS, 'Process Domain/AS based custom list'))) {
|
9523
|
exec("{$pfb['script']} whoisconvert {$header_esc} {$list['vtype']} {$custom_list} {$elog}");
|
9524
|
} else {
|
9525
|
pfb_logger("\nFailed to process customlist [AS/Whois convert | " . htmlspecialchars($custom_list) . " ]", 1);
|
9526
|
}
|
9527
|
}
|
9528
|
else {
|
9529
|
// Process IP based custom list
|
9530
|
$custom_list = pfbng_text_area_decode($list['custom'], FALSE, TRUE, FALSE);
|
9531
|
@file_put_contents("{$file_dwn}.orig", $custom_list, LOCK_EX);
|
9532
|
}
|
9533
|
pfb_logger(' . completed .', 1);
|
9534
|
}
|
9535
|
|
9536
|
$ip_data = ''; // IPs collected from feed
|
9537
|
$parse_fail = 0; // Failed parsed lines from feed
|
9538
|
pfb_logger('.', 1);
|
9539
|
|
9540
|
// Set 'auto' format for all lists, except for lists that require 'regex' parsing.
|
9541
|
if ($row['format'] == 'regex') {
|
9542
|
$pftype = 'regex';
|
9543
|
}
|
9544
|
else {
|
9545
|
$url = pathinfo($row['url']);
|
9546
|
|
9547
|
// Strip any text after '?'
|
9548
|
if (strpos($url['extension'], '?') !== FALSE) {
|
9549
|
$url['extension'] = strstr($url['extension'], '?', TRUE);
|
9550
|
}
|
9551
|
|
9552
|
// Determine if list is an IBlock list
|
9553
|
if (strpos($url['dirname'], 'iblocklist') !== FALSE) {
|
9554
|
$url['extension'] = 'iblock';
|
9555
|
}
|
9556
|
|
9557
|
// Use 'regex' IP parser for non-standard IP lists.
|
9558
|
if (in_array($url['extension'], array('html', 'htm', 'php', 'aspx', 'cgi', 'csv', 'rules', ''))) {
|
9559
|
$pftype = 'regex';
|
9560
|
} else {
|
9561
|
$pftype = 'auto';
|
9562
|
}
|
9563
|
}
|
9564
|
|
9565
|
// IPv4/6 Advanced Tunable - (Pre Script processing)
|
9566
|
if ($pfb_script_pre && file_exists("{$pfb_script_pre}")) {
|
9567
|
pfb_logger("\nExecuting pre-script: {$list['script_pre']}\n", 1);
|
9568
|
$file_dwn_esc = escapeshellarg("{$file_dwn}.orig");
|
9569
|
@copy("{$file_dwn}.orig", "{$file_dwn}.orig.pre"); // Save original file for restoration
|
9570
|
exec("{$pfb_script_pre} {$file_dwn_esc} {$list['vtype']} {$elog}");
|
9571
|
}
|
9572
|
|
9573
|
if (($fhandle = @fopen("{$file_dwn}.orig", 'r')) !== FALSE) {
|
9574
|
while (($line = @fgets($fhandle)) !== FALSE) {
|
9575
|
// Record original line for regex matching, if required.
|
9576
|
$oline = $line;
|
9577
|
|
9578
|
// Remove any leading/trailing whitespaces
|
9579
|
$line = trim($line);
|
9580
|
|
9581
|
// Remove commentlines and blank lines
|
9582
|
if (substr($line, 0, 1) == '#' || empty($line)) {
|
9583
|
continue;
|
9584
|
}
|
9585
|
|
9586
|
$parse_error = FALSE;
|
9587
|
if ($list['vtype'] == '_v4' && $pftype == 'auto') {
|
9588
|
|
9589
|
// IBlock - parser sample ( JKS Media, LLC:4.53.2.12-4.53.2.15 )
|
9590
|
// Remove leading domain name details
|
9591
|
if (strpos($line, '-') !== FALSE && strpos($line, ':') !== FALSE) {
|
9592
|
$line = str_replace(':', '', strstr($line, ':', FALSE));
|
9593
|
}
|
9594
|
|
9595
|
// If 'space' character found, remove characters after space
|
9596
|
if (strpos($line, ' ') !== FALSE) {
|
9597
|
$line = strstr($line, ' ', TRUE);
|
9598
|
}
|
9599
|
|
9600
|
// If '#' character found, remove characters after '#'
|
9601
|
if (strpos($line, '#') !== FALSE) {
|
9602
|
$line = str_replace('#', '', strstr($line, '#', TRUE));
|
9603
|
}
|
9604
|
|
9605
|
// Remove any leading/trailing whitespaces
|
9606
|
$line = trim($line);
|
9607
|
|
9608
|
// Range parser
|
9609
|
if (strpos($line, '-') !== FALSE) {
|
9610
|
$matches = explode('-', $line);
|
9611
|
if (count($matches) == 2) {
|
9612
|
$a_cidr = ip_range_to_subnet_array($matches[0],$matches[1]);
|
9613
|
if (!empty($a_cidr)) {
|
9614
|
foreach ($a_cidr as $cidr) {
|
9615
|
$cidr = sanitize_ipaddr($cidr, $custom, $pfbcidr);
|
9616
|
if (!empty($cidr)) {
|
9617
|
if (validate_ipv4($cidr)) {
|
9618
|
$ip_data .= $cidr . "\n";
|
9619
|
}
|
9620
|
else {
|
9621
|
$parse_error = TRUE;
|
9622
|
}
|
9623
|
}
|
9624
|
}
|
9625
|
if (!$parse_error) {
|
9626
|
continue;
|
9627
|
}
|
9628
|
}
|
9629
|
}
|
9630
|
else {
|
9631
|
$parse_error = TRUE;
|
9632
|
}
|
9633
|
}
|
9634
|
|
9635
|
if (!$parse_error) {
|
9636
|
// Single address parser
|
9637
|
$parsed = sanitize_ipaddr($line, $custom, $pfbcidr);
|
9638
|
if (validate_ipv4($parsed)) {
|
9639
|
$ip_data .= $parsed . "\n";
|
9640
|
continue;
|
9641
|
}
|
9642
|
else {
|
9643
|
$parse_error = TRUE;
|
9644
|
}
|
9645
|
}
|
9646
|
}
|
9647
|
|
9648
|
if ($list['vtype'] == '_v4' && ($pftype == 'regex' || $parse_error)) {
|
9649
|
|
9650
|
// Use regex as last alternative.
|
9651
|
|
9652
|
if (strpos($oline, '-') !== FALSE && strpos($oline, '.') !== FALSE) {
|
9653
|
// Network range 192.168.0.0-192.168.0.254
|
9654
|
if (preg_match($pfb['range'], $oline, $matches)) {
|
9655
|
$a_cidr = ip_range_to_subnet_array($matches[1], $matches[2]);
|
9656
|
if (!empty($a_cidr)) {
|
9657
|
foreach ($a_cidr as $cidr) {
|
9658
|
$cidr = sanitize_ipaddr($cidr, $custom, $pfbcidr);
|
9659
|
if (validate_ipv4($cidr)) {
|
9660
|
$ip_data .= $cidr . "\n";
|
9661
|
}
|
9662
|
else {
|
9663
|
$parse_fail++;
|
9664
|
}
|
9665
|
}
|
9666
|
}
|
9667
|
continue;
|
9668
|
}
|
9669
|
}
|
9670
|
|
9671
|
// IPv4/CIDR format 192.168.0.0 | 192.168.0.0/16
|
9672
|
if (preg_match_all($pfb['ipv4'], $oline, $matches)) {
|
9673
|
$matches = array_unique($matches[0]);
|
9674
|
foreach ($matches as $match) {
|
9675
|
|
9676
|
// Workaround to skip cloudflare error pages
|
9677
|
if (strpos($oline, 'cf-footer-item') === FALSE) {
|
9678
|
$parsed = sanitize_ipaddr($match, $custom, $pfbcidr);
|
9679
|
if (validate_ipv4($parsed)) {
|
9680
|
$ip_data .= $parsed . "\n";
|
9681
|
}
|
9682
|
}
|
9683
|
}
|
9684
|
continue;
|
9685
|
}
|
9686
|
}
|
9687
|
|
9688
|
if ($list['vtype'] == '_v6') {
|
9689
|
// Auto IPv6 parser
|
9690
|
if ($pftype == 'auto') {
|
9691
|
if (strpos($line, ':') !== FALSE) {
|
9692
|
|
9693
|
// Remove any comments
|
9694
|
if (strpos($line, '#') !== FALSE) {
|
9695
|
$line = str_replace('#', '', strstr($line, '#', TRUE));
|
9696
|
}
|
9697
|
|
9698
|
if (validate_ipv6($line)) {
|
9699
|
$ip_data .= $line . "\n";
|
9700
|
continue;
|
9701
|
}
|
9702
|
}
|
9703
|
}
|
9704
|
|
9705
|
// Range parser
|
9706
|
if (strpos($line, '-') !== FALSE && strpos($line, ':') !== FALSE) {
|
9707
|
$matches = explode('-', $line);
|
9708
|
if (count($matches) == 2) {
|
9709
|
$a_cidr = ip_range_to_subnet_array($matches[0],$matches[1]);
|
9710
|
if (!empty($a_cidr)) {
|
9711
|
foreach ($a_cidr as $cidr) {
|
9712
|
if (!empty($cidr)) {
|
9713
|
if (validate_ipv6($cidr)) {
|
9714
|
$ip_data .= $cidr . "\n";
|
9715
|
}
|
9716
|
else {
|
9717
|
$parse_error = TRUE;
|
9718
|
}
|
9719
|
}
|
9720
|
}
|
9721
|
}
|
9722
|
if (!$parse_error) {
|
9723
|
continue;
|
9724
|
}
|
9725
|
}
|
9726
|
else {
|
9727
|
$parse_error = TRUE;
|
9728
|
}
|
9729
|
}
|
9730
|
|
9731
|
// IPv6 Regex parser
|
9732
|
if (preg_match_all($pfb['ipv6'], $oline, $matches)) {
|
9733
|
$matches = array_unique($matches[0]);
|
9734
|
foreach ($matches as $match) {
|
9735
|
|
9736
|
// Remove any comments
|
9737
|
if (strpos($match, '#') !== FALSE) {
|
9738
|
$match = str_replace('#', '', strstr($match, '#', TRUE));
|
9739
|
}
|
9740
|
|
9741
|
// Workaround to skip cloudflare error pages
|
9742
|
if (strpos($oline, 'cf-footer-item') === FALSE) {
|
9743
|
if (validate_ipv6($match)) {
|
9744
|
$ip_data .= $match . "\n";
|
9745
|
}
|
9746
|
}
|
9747
|
}
|
9748
|
}
|
9749
|
}
|
9750
|
}
|
9751
|
|
9752
|
// Check for parse failures
|
9753
|
if (!empty($line) && !preg_match('/[a-zA-Z,;|\"\'?]/', $line)) {
|
9754
|
$parse_fail++;
|
9755
|
$log = "[!] Parse Errors [ {$parse_fail} ]\n";
|
9756
|
pfb_logger("{$log}", 2);
|
9757
|
}
|
9758
|
}
|
9759
|
if ($fhandle) {
|
9760
|
@fclose($fhandle);
|
9761
|
}
|
9762
|
pfb_logger("\n", 1);
|
9763
|
|
9764
|
// IP v4/6 Advanced Tunable - (Post Script processing)
|
9765
|
if ($pfb_script_post && file_exists("{$pfb_script_post}")) {
|
9766
|
pfb_logger("\nExecuting post-script: {$list['script_pre']}\n", 1);
|
9767
|
$file_org_esc = escapeshellarg("{$file_dwn}.orig");
|
9768
|
@copy("{$file_dwn}.orig", "{$file_dwn}.orig.post"); // Save original file for restoration
|
9769
|
exec("{$pfb_script_post} {$file_org_esc} {$list['vtype']} {$elog}");
|
9770
|
}
|
9771
|
|
9772
|
if (!$custom) {
|
9773
|
// Check to see if list actually failed download or has no IPs listed.
|
9774
|
$file_chk = '';
|
9775
|
if (file_exists("{$file_dwn}.orig") && @filesize("{$file_dwn}.orig") > 0) {
|
9776
|
$file_org_esc = escapeshellarg("{$file_dwn}.orig");
|
9777
|
$file_chk = exec("{$pfb['grep']} -cv '^#\|^\$' {$file_org_esc}");
|
9778
|
}
|
9779
|
|
9780
|
if ($file_chk == 0) {
|
9781
|
if ($list['vtype'] == '_v6') {
|
9782
|
$p_ip = "::{$pfb['ip_ph']}";
|
9783
|
} else {
|
9784
|
$p_ip = $pfb['ip_ph'];
|
9785
|
}
|
9786
|
|
9787
|
$ip_data = "{$p_ip}\n";
|
9788
|
$log = " Empty file, Adding '{$p_ip}' to avoid download failure.\n";
|
9789
|
pfb_logger("{$log}", 1);
|
9790
|
}
|
9791
|
}
|
9792
|
|
9793
|
if (!empty($ip_data)) {
|
9794
|
// Save List to '.txt' format in appropriate folder
|
9795
|
@file_put_contents("{$pfbfolder}/{$header}.txt", "{$ip_data}", LOCK_EX);
|
9796
|
|
9797
|
// Call 'shell script' functions (Deny Actions only)
|
9798
|
if ($pfbadv && $list['vtype'] == '_v4') {
|
9799
|
$args = '';
|
9800
|
// Call Process255
|
9801
|
if ($pfb['dup'] == 'on' || $pfb['agg'] == 'on') {
|
9802
|
$args = '_255';
|
9803
|
}
|
9804
|
// Call Aggregate process
|
9805
|
if ($pfb['agg'] == 'on') {
|
9806
|
$args .= '_agg';
|
9807
|
}
|
9808
|
// Call Reputation Max process
|
9809
|
if ($pfb['rep'] == 'on') {
|
9810
|
$args .= '_rep';
|
9811
|
}
|
9812
|
// Call Duplication process
|
9813
|
if ($pfb['dup'] == 'on') {
|
9814
|
$args .= '_dup';
|
9815
|
}
|
9816
|
if (!empty($args)) {
|
9817
|
exec("{$pfb['script']} {$args} {$header_esc} {$pfb['max']} {$pfb['drep']} {$pfb['ccexclude']} {$pfb['ccwhite']} {$pfb['ccblack']} {$elog}");
|
9818
|
}
|
9819
|
}
|
9820
|
|
9821
|
if (!$pfbadv && $list['vtype'] == '_v4') {
|
9822
|
// Call Aggregate process
|
9823
|
if ($pfb['agg'] == 'on') {
|
9824
|
exec("{$pfb['script']} cidr_aggregate {$header_esc} {$pfbfolder} {$elog}");
|
9825
|
}
|
9826
|
}
|
9827
|
|
9828
|
// Collect updated alias lists ('Reputation' disabled)
|
9829
|
$pfb_alias_lists[] = "{$alias}";
|
9830
|
|
9831
|
if ($pfbadv && $list['vtype'] == '_v4') {
|
9832
|
// Execute Reputation functions, when changes are found.
|
9833
|
$pfb['repcheck'] = TRUE;
|
9834
|
|
9835
|
// Enable suppression process due to updates
|
9836
|
if ($pfb['supp'] == 'on') {
|
9837
|
$pfb['supp_update'] = TRUE;
|
9838
|
}
|
9839
|
}
|
9840
|
} else {
|
9841
|
if (!$custom) {
|
9842
|
$log = "[ {$alias} {$header} ] No IPs found! Ensure only IP based Feeds are used! ]\n";
|
9843
|
} else {
|
9844
|
$log = "[ {$alias} {$header} ] Custom List: No IPs found! Ensure only IP based Feeds are used! ]\n";
|
9845
|
}
|
9846
|
pfb_logger("{$log}", 1);
|
9847
|
}
|
9848
|
unset($ip_data);
|
9849
|
|
9850
|
// Remove update file indicator
|
9851
|
unlink_if_exists("{$pfbfolder}/{$header}.update");
|
9852
|
|
9853
|
// Restore Original Downloaded file after Post Script function
|
9854
|
if (file_exists("{$file_dwn}.orig.post")) {
|
9855
|
@rename("{$file_dwn}.orig.post", "{$file_dwn}.orig");
|
9856
|
}
|
9857
|
// Restore Original Downloaded file after Pre Script function
|
9858
|
if (file_exists("{$file_dwn}.orig.pre")) {
|
9859
|
@rename("{$file_dwn}.orig.pre", "{$file_dwn}.orig");
|
9860
|
}
|
9861
|
}
|
9862
|
}
|
9863
|
}
|
9864
|
}
|
9865
|
}
|
9866
|
}
|
9867
|
|
9868
|
// Remove database update file markers
|
9869
|
unlink_if_exists("{$pfb['dbdir']}/geoip.update");
|
9870
|
unlink_if_exists("{$pfb['dbdir']}/asn.update");
|
9871
|
|
9872
|
#################################
|
9873
|
# REPUTATION PROCESSES #
|
9874
|
#################################
|
9875
|
|
9876
|
// IP Reputation processes (pMax and dMax)
|
9877
|
if ($pfb['prep'] == 'on' && $pfb['repcheck'] && !$pfb['save'] && $pfb['enable'] == 'on') {
|
9878
|
// Script to run prep process
|
9879
|
exec("{$pfb['script']} pmax x {$pfb['pmax']} {$elog}");
|
9880
|
}
|
9881
|
if ($pfb['drep'] == 'on' && $pfb['repcheck'] && !$pfb['save'] && $pfb['enable'] == 'on') {
|
9882
|
// Script to run drep process
|
9883
|
exec("{$pfb['script']} dmax x {$pfb['dmax']} {$pfb['drep']} {$pfb['ccexclude']} {$pfb['ccwhite']} {$pfb['ccblack']} {$elog}");
|
9884
|
}
|
9885
|
|
9886
|
#################################################
|
9887
|
# CONFIGURE ALIASES AND FIREWALL RULES #
|
9888
|
#################################################
|
9889
|
|
9890
|
foreach ($ip_types as $ip_type => $vtype) {
|
9891
|
$lists = config_get_path("installedpackages/{$ip_type}/config");
|
9892
|
|
9893
|
// Add DNSBLIP, if configured (IPv4 only)
|
9894
|
if ($pfb['dnsbl'] == 'on' && $pfb['dnsbl_ip'] != 'Disabled' && $vtype == '_v4') {
|
9895
|
|
9896
|
$list = array( 'aliasname' => 'DNSBLIP',
|
9897
|
'vtype' => "{$vtype}",
|
9898
|
'key' => 0,
|
9899
|
'dnsblip' => '',
|
9900
|
'action' => "{$pfb['dnsbl_ip']}",
|
9901
|
'aliaslog' => "{$pfb['dnsblconfig']['aliaslog']}");
|
9902
|
|
9903
|
$list['row'][] = array( 'format' => 'auto',
|
9904
|
'state' => 'Enabled',
|
9905
|
'url' => "{$pfb['dbdir']}/DNSBLIP{$vtype}.txt",
|
9906
|
'header' => 'DNSBLIP');
|
9907
|
$lists[] = $list;
|
9908
|
}
|
9909
|
|
9910
|
if (!empty($lists) && $pfb['enable'] == 'on') {
|
9911
|
$pfbrunonce = TRUE;
|
9912
|
foreach ($lists as $key => $list) {
|
9913
|
$alias = "pfB_{$list['aliasname']}{$vtype}";
|
9914
|
if (empty(pfb_filter($alias, PFB_FILTER_WORD, 'Configure Aliases and Firewall Rules'))) {
|
9915
|
pfb_logger("\n Invalid Aliasname:{$list['aliasname']}{$vtype}, *skipping*", 1);
|
9916
|
continue;
|
9917
|
}
|
9918
|
$alias_esc = escapeshellarg($alias);
|
9919
|
|
9920
|
// Skip any Alias that are 'enabled' but Lists/customlists are not defined.
|
9921
|
if (empty($list['row'][0]['url']) && empty($list['custom'])) {
|
9922
|
exec("{$pfb['pfctl']} -t {$alias_esc} -T kill 2>&1", $result);
|
9923
|
continue;
|
9924
|
}
|
9925
|
|
9926
|
if (isset($list['dnsblip'])) {
|
9927
|
$list_type = 'pfblockerngdnsblsettings';
|
9928
|
} else {
|
9929
|
$list_type = "{$ip_type}";
|
9930
|
}
|
9931
|
|
9932
|
// Determine 'list' details (return array $pfbarr)
|
9933
|
pfb_determine_list_detail($list['action'], '', $list_type, $key);
|
9934
|
$pfbadv = $pfbarr['adv'];
|
9935
|
$pfbdescr = $pfbarr['descr'];
|
9936
|
$pfbfolder = $pfbarr['folder'];
|
9937
|
|
9938
|
// Only Save aliases that have been updated.
|
9939
|
// When 'Reputation' is used, all aliases need to be updated.
|
9940
|
$final_alias = array();
|
9941
|
if ($pfb['drep'] == 'on' || $pfb['prep'] == 'on') {
|
9942
|
if (!empty($pfb_alias_lists_all)) {
|
9943
|
$final_alias = array_unique($pfb_alias_lists_all);
|
9944
|
}
|
9945
|
}
|
9946
|
else {
|
9947
|
if (!empty($pfb_alias_lists)) {
|
9948
|
$final_alias = array_unique($pfb_alias_lists);
|
9949
|
}
|
9950
|
}
|
9951
|
|
9952
|
if ($list['action'] != 'Disabled') {
|
9953
|
$pfbupdate = FALSE;
|
9954
|
$alias_ips = ''; // IP Collection of all Lists in the Alias
|
9955
|
$urlvalue = ''; // Firewall: Aliases value field
|
9956
|
|
9957
|
if (isset($list['row'])) {
|
9958
|
foreach ($list['row'] as $row) {
|
9959
|
if (!empty($row['url']) && $row['state'] != 'Disabled') {
|
9960
|
|
9961
|
$header = "{$row['header']}{$vtype}";
|
9962
|
if (empty(pfb_filter($header, PFB_FILTER_WORD, 'Configure Aliases and Firewall Rules'))) {
|
9963
|
pfb_logger("\n Invalid Aliasname:{$list['aliasname']}{$vtype} | Header:{$row['header']}{$vtype}, *skipping*", 1);
|
9964
|
continue;
|
9965
|
}
|
9966
|
$header_esc = escapeshellarg($header);
|
9967
|
$urlvalue .= "{$header},";
|
9968
|
|
9969
|
$pfctlck = exec("{$pfb['pfctl']} -vvsTables | {$pfb['grep']} -A1 {$alias_esc} | {$pfb['awk']} '/Addresses/ {s+=\$2}; END {print s}'");
|
9970
|
|
9971
|
// Update alias if list file exists and its been updated or if the alias URL table is empty.
|
9972
|
if (file_exists("{$pfbfolder}/{$header}.txt") && (in_array($alias, $final_alias) || empty($pfctlck))) {
|
9973
|
// Script to run suppression process (print header only)
|
9974
|
if ($pfbrunonce && $pfb['supp'] == 'on' && $vtype == '_v4' && $pfb['supp_update']) {
|
9975
|
exec("{$pfb['script']} suppress suppressheader {$elog}");
|
9976
|
$pfbrunonce = FALSE;
|
9977
|
}
|
9978
|
// Script to run suppression process (body)
|
9979
|
if ($pfb['supp'] == 'on' && $vtype == '_v4' && $pfb['supp_update'] && $pfbadv) {
|
9980
|
if ($pfb['dup'] == 'on') {
|
9981
|
exec("{$pfb['script']} suppress {$header_esc} {$pfbfolder} on {$elog}");
|
9982
|
} else {
|
9983
|
exec("{$pfb['script']} suppress {$header_esc} {$pfbfolder} off {$elog}");
|
9984
|
}
|
9985
|
}
|
9986
|
$alias_ips .= file_get_contents("{$pfbfolder}/{$header}.txt");
|
9987
|
$pfbupdate = TRUE;
|
9988
|
}
|
9989
|
}
|
9990
|
}
|
9991
|
}
|
9992
|
|
9993
|
// check custom network list
|
9994
|
$aliasname = "{$list['aliasname']}_custom{$vtype}";
|
9995
|
|
9996
|
// Update alias if list file exists and its been updated or if the alias URL table is empty.
|
9997
|
$alias_esc = escapeshellarg($alias);
|
9998
|
$pfctlck = exec("{$pfb['pfctl']} -vvsTables | {$pfb['grep']} -A1 {$alias_esc} | {$pfb['awk']} '/Addresses/ {s+=\$2}; END {print s}'");
|
9999
|
if (!empty($list['custom'])) {
|
10000
|
$urlvalue .= "{$aliasname},";
|
10001
|
if (file_exists("{$pfbfolder}/{$aliasname}.txt") && in_array($alias, $final_alias) ||
|
10002
|
file_exists("{$pfbfolder}/{$aliasname}.txt") && empty($pfctlck)) {
|
10003
|
$alias_ips .= file_get_contents("{$pfbfolder}/{$aliasname}.txt");
|
10004
|
$pfbupdate = TRUE;
|
10005
|
}
|
10006
|
}
|
10007
|
|
10008
|
// Determine validity of alias URL tables/rules. ie: Don't create empty URL tables or aliases
|
10009
|
if (empty($alias_ips) && empty($pfctlck)) {
|
10010
|
unlink_if_exists("{$pfb['aliasdir']}/{$alias}.txt");
|
10011
|
}
|
10012
|
else {
|
10013
|
// Save only aliases that have been updated.
|
10014
|
if ($pfbupdate) {
|
10015
|
@file_put_contents("{$pfb['aliasdir']}/{$alias}.txt", $alias_ips, LOCK_EX);
|
10016
|
}
|
10017
|
|
10018
|
// Add '[s]' to Alias descriptions (Bypass States removal feature)
|
10019
|
$adescr = "pfBlockerNG {$pfbdescr} Alias";
|
10020
|
if ($list['stateremoval'] == 'disabled') {
|
10021
|
$adescr = "pfBlockerNG {$pfbdescr} Alias [s]";
|
10022
|
}
|
10023
|
|
10024
|
// Create alias
|
10025
|
$new_aliases_list[] = "{$alias}";
|
10026
|
$new_aliases[] = array( 'name' => "{$alias}",
|
10027
|
'url' => "{$pfb['weblocal']}?pfb={$alias}",
|
10028
|
'updatefreq' => '32',
|
10029
|
'address' => '',
|
10030
|
'descr' => "{$adescr} [ {$urlvalue} ]",
|
10031
|
'type' => 'urltable',
|
10032
|
'detail' => 'DO NOT EDIT THIS ALIAS'
|
10033
|
);
|
10034
|
|
10035
|
// Define firewall rule settings
|
10036
|
pfb_firewall_rule($list['action'], $alias, $vtype, $list['aliaslog'], $pfbarr['agateway_in'], $pfbarr['agateway_out'],
|
10037
|
$pfbarr['aaddrnot_in'], $pfbarr['aaddr_in'], $pfbarr['aports_in'], $pfbarr['aproto_in'], $pfbarr['anot_in'],
|
10038
|
$pfbarr['aaddrnot_out'], $pfbarr['aaddr_out'], $pfbarr['aports_out'], $pfbarr['aproto_out'], $pfbarr['anot_out']);
|
10039
|
}
|
10040
|
}
|
10041
|
else {
|
10042
|
// unlink previous pfblockerNG alias list
|
10043
|
unlink_if_exists("{$pfb['aliasdir']}/{$alias}.txt");
|
10044
|
}
|
10045
|
}
|
10046
|
}
|
10047
|
}
|
10048
|
// Clear variables
|
10049
|
$alias_ips = '';
|
10050
|
|
10051
|
// Define DNSBL VIP Ports alias
|
10052
|
if ($pfb['dnsbl_rule'] != 'Disabled' && !empty($pfb['dnsbl_port']) && !empty($pfb['dnsbl_port_ssl'])
|
10053
|
&& !empty($pfb['dnsblconfig']['dnsbl_allow_int']) && isset($pfb['dnsbl_vip'])) {
|
10054
|
|
10055
|
$new_aliases_list[] = 'pfB_DNSBL_Ports';
|
10056
|
$new_aliases[] = array( 'name' => 'pfB_DNSBL_Ports',
|
10057
|
'address' => $pfb['dnsbl_iface'] != 'lo0' ? "{$pfb['dnsbl_port']} {$pfb['dnsbl_port_ssl']}" : '80 443',
|
10058
|
'descr' => 'pfBlockerNG DNSBL VIP Ports',
|
10059
|
'type' => 'port',
|
10060
|
'detail' => 'DO NOT EDIT THIS PORT||DO NOT EDIT THIS PORT'
|
10061
|
);
|
10062
|
|
10063
|
if ($pfb['dnsbl_v6'] == 'on') {
|
10064
|
$new_aliases_list[] = 'pfB_DNSBL_VIPs';
|
10065
|
$new_aliases[] = array( 'name' => 'pfB_DNSBL_VIPs',
|
10066
|
'address' => "{$pfb['dnsbl_vip']} ::{$pfb['dnsbl_vip']}",
|
10067
|
'descr' => 'pfBlockerNG DNSBL VIPs',
|
10068
|
'type' => 'host',
|
10069
|
'detail' => 'DO NOT EDIT THIS HOST||DO NOT EDIT THIS HOST'
|
10070
|
);
|
10071
|
}
|
10072
|
}
|
10073
|
|
10074
|
#########################################
|
10075
|
# UPDATE pfSense ALIAS TABLES #
|
10076
|
#########################################
|
10077
|
|
10078
|
// Reload config.xml to get any recent changes
|
10079
|
config_read_file(false, true);
|
10080
|
|
10081
|
$exist_aliases = config_get_path('aliases/alias', []);
|
10082
|
foreach ($exist_aliases as $cbalias) {
|
10083
|
|
10084
|
if (substr($cbalias['name'], 0, 4) == 'pfB_') {
|
10085
|
// Remove unreferenced pfB aliastable files
|
10086
|
if (!in_array($cbalias['name'], $new_aliases_list)) {
|
10087
|
unlink_if_exists("{$pfb['aliasdir']}/{$cbalias['name']}.*");
|
10088
|
}
|
10089
|
}
|
10090
|
else {
|
10091
|
$new_aliases[] = $cbalias;
|
10092
|
}
|
10093
|
}
|
10094
|
|
10095
|
// Update config.xml, if changes required
|
10096
|
if ($exist_aliases !== $new_aliases) {
|
10097
|
config_set_path('aliases/alias', $new_aliases);
|
10098
|
write_config('pfBlockerNG: saving Aliases');
|
10099
|
}
|
10100
|
unset($new_aliases, $exist_aliases);
|
10101
|
|
10102
|
#########################
|
10103
|
# Assign Rules #
|
10104
|
#########################
|
10105
|
|
10106
|
// Only execute if autorules are defined or if an alias has been removed.
|
10107
|
if ($pfb['autorules'] || $pfb['enable'] == '' || $pfb['remove']) {
|
10108
|
$message = '';
|
10109
|
if (!empty($pfb['deny_inbound']) || !empty($pfb['permit_inbound']) || !empty($pfb['match_inbound'])) {
|
10110
|
if (empty($pfb['inbound_interfaces'])) {
|
10111
|
$message = " Unable to apply rules. Inbound interface option not configured.";
|
10112
|
}
|
10113
|
}
|
10114
|
if (!empty($pfb['deny_outbound']) || !empty($pfb['permit_outbound']) || !empty($pfb['match_outbound'])) {
|
10115
|
if (empty($pfb['outbound_interfaces'])) {
|
10116
|
$message .= "\n Unable to apply rules. Outbound interface option not configured.";
|
10117
|
}
|
10118
|
}
|
10119
|
|
10120
|
if (empty($message)) {
|
10121
|
$new_rules = $permit_rules = $match_rules = $other_rules = $fpermit_rules = $fmatch_rules = $fother_rules = array();
|
10122
|
|
10123
|
// Reload config.xml to get any recent changes
|
10124
|
config_read_file(false, true);
|
10125
|
|
10126
|
// New vs old rules array comparison
|
10127
|
$orig_rules_nocreated = $new_rules_nocreated = array();
|
10128
|
|
10129
|
// Collect all existing rules
|
10130
|
$rules = config_get_path('filter/rule', []);
|
10131
|
|
10132
|
// Collect existing pfSense rules 'pass', 'match' and 'other' pfSense rules into new arrays.
|
10133
|
if (!empty($rules)) {
|
10134
|
foreach ($rules as $rule) {
|
10135
|
|
10136
|
// Remove all existing rules that start with 'pfB_' in the Rule Description
|
10137
|
if (substr($rule['descr'], 0, 4) != 'pfB_') {
|
10138
|
|
10139
|
// Upgrade previous IPv4 pfBlockerNG 'alias type' aliasnames to new '_v4' suffix format
|
10140
|
foreach (array('source', 'destination') as $rtype) {
|
10141
|
if (substr($rule[$rtype]['address'], 0, 4) == 'pfB_' &&
|
10142
|
substr($rule[$rtype]['address'], -3) != '_v4' &&
|
10143
|
$rule['ipprotocol'] == 'inet') {
|
10144
|
|
10145
|
// Add '_v4' suffix
|
10146
|
$rule[$rtype]['address'] = "{$rule[$rtype]['address']}_v4";
|
10147
|
}
|
10148
|
}
|
10149
|
|
10150
|
// Floating rules collection 'Floating Pass/Match', balance to 'other'
|
10151
|
if ($pfb['float'] == 'on') {
|
10152
|
if ($pfb['order'] == 'order_0' && $rule['floating'] == 'yes') {
|
10153
|
$fother_rules[] = $rule;
|
10154
|
}
|
10155
|
else {
|
10156
|
if ($rule['type'] == 'pass' && $rule['floating'] == 'yes') {
|
10157
|
$fpermit_rules[] = $rule;
|
10158
|
} elseif ($rule['type'] == 'match' && $rule['floating'] == 'yes') {
|
10159
|
$fmatch_rules[] = $rule;
|
10160
|
} elseif ($rule['floating'] == 'yes') {
|
10161
|
$fother_rules[] = $rule;
|
10162
|
} else {
|
10163
|
$other_rules[] = $rule;
|
10164
|
}
|
10165
|
}
|
10166
|
} else {
|
10167
|
// Collect only 'selected inbound and outbound interfaces'. balance to 'other'
|
10168
|
if (in_array($rule['interface'], $pfb['inbound_interfaces']) ||
|
10169
|
in_array($rule['interface'], $pfb['outbound_interfaces'])) {
|
10170
|
// Floating rules 'off'. Collect 'floating other', pass, balance to 'other'
|
10171
|
if ($rule['floating'] == 'yes') {
|
10172
|
$fother_rules[] = $rule;
|
10173
|
} elseif ($rule['type'] == 'pass' || isset($rule['associated-rule-id'])) {
|
10174
|
if ($pfb['order'] == 'order_0') {
|
10175
|
$other_rules[] = $rule;
|
10176
|
} else {
|
10177
|
$permit_rules[] = $rule;
|
10178
|
}
|
10179
|
} else {
|
10180
|
$other_rules[] = $rule;
|
10181
|
}
|
10182
|
} else {
|
10183
|
if ($rule['floating'] == 'yes') {
|
10184
|
$fother_rules[] = $rule;
|
10185
|
} else {
|
10186
|
$other_rules[] = $rule;
|
10187
|
}
|
10188
|
}
|
10189
|
}
|
10190
|
}
|
10191
|
|
10192
|
// Remove 'created' tag
|
10193
|
if (isset($rule['created'])) {
|
10194
|
unset($rule['created']);
|
10195
|
}
|
10196
|
$orig_rules_nocreated[] = $rule;
|
10197
|
}
|
10198
|
}
|
10199
|
|
10200
|
#################################################################################
|
10201
|
# IP FIREWALL RULES ORDER #
|
10202
|
# ORDER 0 | pfB (p/m/b/r) | All other | #
|
10203
|
# ORDER 1 | pfSense (p/m) | pfB (p/m) | pfB (b/r) | pfSense (b/r) #
|
10204
|
# ORDER 2 | pfB (p/m) | pfSense (p/m) | pfB (b/r) | pfSense (b/r) #
|
10205
|
# ORDER 3 | pfB (p/m) | pfB (b/r) | pfSense (p/m) | pfSense (b/r) #
|
10206
|
# ORDER 4 | pfB (p/m) | pfB (b/r) | pfSense (b/r) | pfSense (p/m) #
|
10207
|
#################################################################################
|
10208
|
|
10209
|
|
10210
|
if ($pfb['float'] == '' && $pfb['order'] == 'order_1' && !empty($fother_rules)) {
|
10211
|
foreach ($fother_rules as $cb_rules) {
|
10212
|
$new_rules[] = $cb_rules;
|
10213
|
}
|
10214
|
}
|
10215
|
if ($pfb['float'] == 'on' && $pfb['order'] == 'order_1') {
|
10216
|
foreach (array($fpermit_rules, $fmatch_rules) as $rtype) {
|
10217
|
if (!empty($rtype)) {
|
10218
|
foreach ($rtype as $cb_rules) {
|
10219
|
$new_rules[] = $cb_rules;
|
10220
|
}
|
10221
|
}
|
10222
|
}
|
10223
|
}
|
10224
|
|
10225
|
// Define DNSBL 'Floating' pass rule for selected 'OPT' segments to be able to access the LAN DNSBL VIP
|
10226
|
if ($pfb['enable'] == 'on' && $pfb['dnsbl'] == 'on' && $pfb['dnsbl_rule'] != 'Disabled'
|
10227
|
&& !empty($pfb['dnsbl_port']) && !empty($pfb['dnsbl_port_ssl'])
|
10228
|
&& !empty($pfb['dnsblconfig']['dnsbl_allow_int']) && isset($pfb['dnsbl_vip'])) {
|
10229
|
|
10230
|
$rule = $pfb['base_rule_float'];
|
10231
|
$rule['tracker'] = pfb_tracker('pfB_DNSBL_Ping', '', '');
|
10232
|
$rule['type'] = 'pass';
|
10233
|
$rule['direction'] = 'any';
|
10234
|
$rule['interface'] = "{$pfb['dnsblconfig']['dnsbl_allow_int']}";
|
10235
|
$rule['ipprotocol'] = ($pfb['dnsbl_v6'] == 'on' ? $rule['ipprotocol'] = 'inet46' : $rule['ipprotocol'] = 'inet');
|
10236
|
$rule['descr'] = "pfB_DNSBL_Ping{$pfb['suffix']}";
|
10237
|
$rule['protocol'] = 'icmp';
|
10238
|
$rule['icmptype'] = 'echoreq';
|
10239
|
$rule['source'] = array('any' => '');
|
10240
|
$rule['destination'] = array('address' => ($pfb['dnsbl_v6'] == 'on' ? 'pfB_DNSBL_VIPs' : $pfb['dnsbl_vip']));
|
10241
|
$rule['created'] = array('time' => (int)microtime(true), 'username' => 'Auto');
|
10242
|
$new_rules[] = $rule;
|
10243
|
|
10244
|
$rule = $pfb['base_rule_float'];
|
10245
|
$rule['tracker'] = pfb_tracker('pfB_DNSBL_Permit', '', '');
|
10246
|
$rule['type'] = 'pass';
|
10247
|
$rule['direction'] = 'any';
|
10248
|
$rule['interface'] = "{$pfb['dnsblconfig']['dnsbl_allow_int']}";
|
10249
|
$rule['ipprotocol'] = ($pfb['dnsbl_v6'] == 'on' ? $rule['ipprotocol'] = 'inet46' : $rule['ipprotocol'] = 'inet');
|
10250
|
$rule['descr'] = "pfB_DNSBL_Permit{$pfb['suffix']}";
|
10251
|
$rule['protocol'] = 'tcp/udp';
|
10252
|
$rule['source'] = array('any' => '');
|
10253
|
$rule['destination'] = array('address' => ($pfb['dnsbl_v6'] == 'on' ? 'pfB_DNSBL_VIPs' : $pfb['dnsbl_vip']),
|
10254
|
'port' => 'pfB_DNSBL_Ports');
|
10255
|
$rule['created'] = array('time' => (int)microtime(true), 'username' => 'Auto');
|
10256
|
$new_rules[] = $rule;
|
10257
|
}
|
10258
|
|
10259
|
// Define inbound interface rules
|
10260
|
if (!empty($pfb['inbound_interfaces'])) {
|
10261
|
$pfbrunonce = TRUE;
|
10262
|
foreach ($pfb['inbound_interfaces'] as $inbound_interface) {
|
10263
|
if ($pfb['order'] == 'order_1' && !empty($permit_rules)) {
|
10264
|
foreach ($permit_rules as $cb_rules) {
|
10265
|
if ($cb_rules['interface'] == $inbound_interface) {
|
10266
|
$new_rules[] = $cb_rules;
|
10267
|
}
|
10268
|
}
|
10269
|
}
|
10270
|
if (!empty($pfb['permit_inbound'])) {
|
10271
|
foreach ($pfb['permit_inbound'] as $cb_rules) {
|
10272
|
$cb_rules['interface'] = $inbound_interface;
|
10273
|
$cb_rules['tracker'] = pfb_tracker($cb_rules['descr'], $inbound_interface, 'permit_in');
|
10274
|
$new_rules[] = $cb_rules;
|
10275
|
}
|
10276
|
}
|
10277
|
// Match inbound rules defined as floating only.
|
10278
|
if ($pfbrunonce && !empty($pfb['match_inbound'])) {
|
10279
|
foreach ($pfb['match_inbound'] as $cb_rules) {
|
10280
|
$cb_rules['interface'] = $pfb['inbound_floating'];
|
10281
|
$cb_rules['tracker'] = pfb_tracker($cb_rules['descr'], $inbound_interface, 'match_in');
|
10282
|
$new_rules[] = $cb_rules;
|
10283
|
$pfbrunonce = FALSE;
|
10284
|
}
|
10285
|
}
|
10286
|
if ($pfb['order'] == 'order_2') {
|
10287
|
foreach (array($fpermit_rules, $fmatch_rules) as $rtype) {
|
10288
|
if (!empty($rtype)) {
|
10289
|
foreach ($rtype as $cb_rules) {
|
10290
|
$new_rules[] = $cb_rules;
|
10291
|
}
|
10292
|
}
|
10293
|
}
|
10294
|
if (!empty($permit_rules)) {
|
10295
|
foreach ($permit_rules as $cb_rules) {
|
10296
|
if ($cb_rules['interface'] == $inbound_interface) {
|
10297
|
$new_rules[] = $cb_rules;
|
10298
|
}
|
10299
|
}
|
10300
|
}
|
10301
|
}
|
10302
|
if (!empty($pfb['deny_inbound'])) {
|
10303
|
foreach ($pfb['deny_inbound'] as $cb_rules) {
|
10304
|
$cb_rules['interface'] = $inbound_interface;
|
10305
|
$cb_rules['tracker'] = pfb_tracker($cb_rules['descr'], $inbound_interface, 'deny_in');
|
10306
|
$new_rules[] = $cb_rules;
|
10307
|
}
|
10308
|
}
|
10309
|
}
|
10310
|
}
|
10311
|
|
10312
|
// Define outbound interface rules
|
10313
|
if (!empty($pfb['outbound_interfaces'])) {
|
10314
|
$pfbrunonce = TRUE;
|
10315
|
foreach ($pfb['outbound_interfaces'] as $outbound_interface) {
|
10316
|
if ($pfb['order'] == 'order_1' && !empty($permit_rules)) {
|
10317
|
foreach ($permit_rules as $cb_rules) {
|
10318
|
if ($cb_rules['interface'] == $outbound_interface) {
|
10319
|
$new_rules[] = $cb_rules;
|
10320
|
}
|
10321
|
}
|
10322
|
}
|
10323
|
if (!empty($pfb['permit_outbound'])) {
|
10324
|
foreach ($pfb['permit_outbound'] as $cb_rules) {
|
10325
|
$cb_rules['interface'] = $outbound_interface;
|
10326
|
$cb_rules['tracker'] = pfb_tracker($cb_rules['descr'], $outbound_interface, 'permit_out');
|
10327
|
$new_rules[] = $cb_rules;
|
10328
|
}
|
10329
|
}
|
10330
|
// Match outbound rules defined as floating only.
|
10331
|
if ($pfbrunonce && !empty($pfb['match_outbound'])) {
|
10332
|
foreach ($pfb['match_outbound'] as $cb_rules) {
|
10333
|
$cb_rules['interface'] = $pfb['outbound_floating'];
|
10334
|
$cb_rules['tracker'] = pfb_tracker($cb_rules['descr'], $outbound_interface, 'match_out');
|
10335
|
$new_rules[] = $cb_rules;
|
10336
|
$pfbrunonce = FALSE;
|
10337
|
}
|
10338
|
}
|
10339
|
if ($pfb['order'] == 'order_2' && !empty($permit_rules)) {
|
10340
|
foreach ($permit_rules as $cb_rules) {
|
10341
|
if ($cb_rules['interface'] == $outbound_interface) {
|
10342
|
$new_rules[] = $cb_rules;
|
10343
|
}
|
10344
|
}
|
10345
|
}
|
10346
|
if (!empty($pfb['deny_outbound'])) {
|
10347
|
foreach ($pfb['deny_outbound'] as $cb_rules) {
|
10348
|
$cb_rules['interface'] = $outbound_interface;
|
10349
|
$cb_rules['tracker'] = pfb_tracker($cb_rules['descr'], $outbound_interface, 'deny_out');
|
10350
|
$new_rules[] = $cb_rules;
|
10351
|
}
|
10352
|
}
|
10353
|
}
|
10354
|
}
|
10355
|
|
10356
|
if ($pfb['float'] == 'on' && in_array($pfb['order'], array('order_0', 'order_3', 'order_4'))) {
|
10357
|
if ($pfb['order'] != 'order_3') {
|
10358
|
$rule_order = array($fother_rules, $fpermit_rules, $fmatch_rules);
|
10359
|
} else {
|
10360
|
$rule_order = array($fpermit_rules, $fmatch_rules, $fother_rules);
|
10361
|
}
|
10362
|
foreach ($rule_order as $rtype) {
|
10363
|
if (!empty($rtype)) {
|
10364
|
foreach ($rtype as $cb_rules) {
|
10365
|
$new_rules[] = $cb_rules;
|
10366
|
}
|
10367
|
}
|
10368
|
}
|
10369
|
}
|
10370
|
if ($pfb['float'] == 'on' && in_array($pfb['order'], array('order_1', 'order_2')) && !empty($fother_rules)) {
|
10371
|
foreach ($fother_rules as $cb_rules) {
|
10372
|
$new_rules[] = $cb_rules;
|
10373
|
}
|
10374
|
}
|
10375
|
if ($pfb['float'] == '' && $pfb['order'] != 'order_1' && !empty($fother_rules)) {
|
10376
|
foreach ($fother_rules as $cb_rules) {
|
10377
|
$new_rules[] = $cb_rules;
|
10378
|
}
|
10379
|
}
|
10380
|
if ($pfb['order'] == 'order_4' && !empty($other_rules)) {
|
10381
|
foreach ($other_rules as $cb_rules) {
|
10382
|
$new_rules[] = $cb_rules;
|
10383
|
}
|
10384
|
}
|
10385
|
if ($pfb['order'] == 'order_4' && !empty($permit_rules)) {
|
10386
|
foreach ($permit_rules as $cb_rules) {
|
10387
|
$new_rules[] = $cb_rules;
|
10388
|
}
|
10389
|
}
|
10390
|
if ($pfb['order'] == 'order_3' && !empty($permit_rules)) {
|
10391
|
foreach ($permit_rules as $cb_rules) {
|
10392
|
$new_rules[] = $cb_rules;
|
10393
|
}
|
10394
|
}
|
10395
|
if ($pfb['order'] != 'order_4' && !empty($other_rules)) {
|
10396
|
foreach ($other_rules as $cb_rules) {
|
10397
|
$new_rules[] = $cb_rules;
|
10398
|
}
|
10399
|
}
|
10400
|
|
10401
|
unset($pfb['permit_inbound'], $pfb['permit_outbound'], $pfb['deny_inbound'],
|
10402
|
$pfb['deny_outbound'], $pfb['match_inbound'], $pfb['match_outbound']);
|
10403
|
unset($cb_rules, $other_rules, $fother_rules, $permit_rules, $fpermit_rules, $match_rules, $fmatch_rules);
|
10404
|
|
10405
|
// Remove 'created' tag (New vs old rules array comparison)
|
10406
|
foreach ($new_rules as $rule) {
|
10407
|
if (isset($rule['created'])) {
|
10408
|
unset($rule['created']);
|
10409
|
}
|
10410
|
$new_rules_nocreated[] = $rule;
|
10411
|
}
|
10412
|
|
10413
|
// Update config.xml, if changes required
|
10414
|
if ($orig_rules_nocreated != $new_rules_nocreated) {
|
10415
|
config_set_path('filter/rule', $new_rules);
|
10416
|
write_config('pfBlockerNG: saving Firewall rules');
|
10417
|
}
|
10418
|
}
|
10419
|
else {
|
10420
|
$log = "\n\n{$message}\n";
|
10421
|
pfb_logger("{$log}", 1);
|
10422
|
}
|
10423
|
}
|
10424
|
|
10425
|
#################################
|
10426
|
# pfSense Integration #
|
10427
|
#################################
|
10428
|
|
10429
|
// If 'Rule Changes' are found, utilize the 'filter_configure()' function, if not, utilize 'pfctl replace' command
|
10430
|
if ($pfb['autorules'] && $orig_rules_nocreated != $new_rules_nocreated || $pfb['enable'] == '' || $pfb['remove']) {
|
10431
|
|
10432
|
if (!$pfb['save']) {
|
10433
|
$log = "\n===[ Aliastables / Rules ]================================\n\n";
|
10434
|
pfb_logger("{$log}", 1);
|
10435
|
|
10436
|
$log = "Firewall rule changes found, applying Filter Reload\n";
|
10437
|
syslog(LOG_NOTICE, "[pfBlockerNG] {$log}");
|
10438
|
pfb_logger("{$log}", 1);
|
10439
|
}
|
10440
|
|
10441
|
// Remove all pfB aliastables
|
10442
|
exec("{$pfb['pfctl']} -s Tables | {$pfb['grep']} '^pfB_'", $pfb_tables);
|
10443
|
if (isset($pfb_tables)) {
|
10444
|
foreach ($pfb_tables as $pfb_table) {
|
10445
|
$pfb_table_esc = escapeshellarg($pfb_table);
|
10446
|
exec("{$pfb['pfctl']} -t {$pfb_table_esc} -T kill 2>&1", $result);
|
10447
|
}
|
10448
|
}
|
10449
|
|
10450
|
$pfb['filter_configure'] = TRUE; // Set flag for filter_configure which will create the pfctl tables
|
10451
|
|
10452
|
// Call function for Ramdisk processes.
|
10453
|
pfb_aliastables('update');
|
10454
|
}
|
10455
|
else {
|
10456
|
// Don't execute on user 'save'
|
10457
|
if (!$pfb['save']) {
|
10458
|
$log = "\n\n===[ Aliastables / Rules ]==========================================\n\n";
|
10459
|
pfb_logger("{$log}", 1);
|
10460
|
|
10461
|
$log = "No changes to Firewall rules, skipping Filter Reload\n";
|
10462
|
syslog(LOG_NOTICE, "[pfBlockerNG] {$log}");
|
10463
|
pfb_logger("{$log}", 1);
|
10464
|
|
10465
|
// Remove Alerts IP unlock file and force Reload of all Aliastables
|
10466
|
if (file_exists("{$pfb['ip_unlock']}")) {
|
10467
|
unlink_if_exists("{$pfb['ip_unlock']}");
|
10468
|
$pfb['repcheck'] = TRUE;
|
10469
|
}
|
10470
|
|
10471
|
// Only Save Aliases that have been updated.
|
10472
|
// When 'Reputation' is used, all aliases need to be updated when any alias has been updated.
|
10473
|
$final_alias = array();
|
10474
|
if ($pfb['repcheck'] && ($pfb['drep'] == 'on' || $pfb['prep'] == 'on')) {
|
10475
|
if (!empty($pfb_alias_lists_all)) {
|
10476
|
$final_alias = array_unique($pfb_alias_lists_all);
|
10477
|
}
|
10478
|
} else {
|
10479
|
if (!empty($pfb_alias_lists)) {
|
10480
|
$final_alias = array_unique($pfb_alias_lists);
|
10481
|
}
|
10482
|
}
|
10483
|
|
10484
|
if (!empty($final_alias)) {
|
10485
|
foreach ($final_alias as $final) {
|
10486
|
$log = "\n Updating: {$final}\n";
|
10487
|
pfb_logger("{$log}", 1);
|
10488
|
$result = '';
|
10489
|
if (file_exists("{$pfb['aliasdir']}/{$final}.txt")) {
|
10490
|
$final_esc = escapeshellarg($final);
|
10491
|
$final_path_esc = escapeshellarg("{$pfb['aliasdir']}/{$final}.txt");
|
10492
|
exec("{$pfb['pfctl']} -t {$final_esc} -T replace -f {$final_path_esc} 2>&1", $result);
|
10493
|
$log = implode($result);
|
10494
|
} else {
|
10495
|
$log = "Aliastable file not found\n";
|
10496
|
}
|
10497
|
pfb_logger("{$log}", 1);
|
10498
|
}
|
10499
|
pfb_logger("\n", 1);
|
10500
|
|
10501
|
// Call function for Ramdisk processes.
|
10502
|
pfb_aliastables('update');
|
10503
|
} else {
|
10504
|
$log = "No Changes to Aliases, Skipping pfctl Update\n";
|
10505
|
pfb_logger("{$log}", 1);
|
10506
|
}
|
10507
|
}
|
10508
|
}
|
10509
|
unset($rules, $new_rules, $orig_rules_nocreated, $new_rules_nocreated);
|
10510
|
|
10511
|
|
10512
|
#################################
|
10513
|
# SAVE CONFIGURATION #
|
10514
|
#################################
|
10515
|
|
10516
|
// Uncheck reusing existing downloads check box
|
10517
|
if (!$pfb['save'] && $pfb['enable'] == 'on' && $pfb['config']['pfb_reuse'] == 'on') {
|
10518
|
$pfb_config['installedpackages']['pfblockerng']['config'][0]['pfb_reuse'] = '';
|
10519
|
$pfb['conf_mod'] = TRUE;
|
10520
|
}
|
10521
|
|
10522
|
// Only save config.xml, if changes are found.
|
10523
|
if ($pfb['conf_mod'] && isset($pfb_config)) {
|
10524
|
pfb_logger("\nSaving config changes", 1);
|
10525
|
|
10526
|
// Reload config.xml to get any recent changes and merge/save changes.
|
10527
|
config_read_file(false, true);
|
10528
|
config_set_path('', array_replace_recursive(config_get_path(''), $pfb_config));
|
10529
|
write_config('pfBlockerNG: save settings');
|
10530
|
|
10531
|
pfb_logger("... completed", 1);
|
10532
|
}
|
10533
|
|
10534
|
|
10535
|
// Query NAT Rules for previous IPv4 pfBlockerNG aliasnames which are not in the new '_v4' suffix format
|
10536
|
$pfb_found = FALSE;
|
10537
|
config_read_file(false, true);
|
10538
|
|
10539
|
foreach (config_get_path('nat/rule', []) as $key => $ex_nat) {
|
10540
|
foreach (array('source', 'destination') as $rtype) {
|
10541
|
if (isset($ex_nat[$rtype]['address']) && substr($ex_nat[$rtype]['address'], 0, 4) == 'pfB_') {
|
10542
|
$pfb_suffix = substr($ex_nat[$rtype]['address'], -3);
|
10543
|
|
10544
|
if ($pfb_suffix == '_v6') {
|
10545
|
continue;
|
10546
|
}
|
10547
|
|
10548
|
// Add '_v4' suffix if missing
|
10549
|
elseif ($pfb_suffix != '_v4') {
|
10550
|
config_set_path("nat/rule/{$key}/{$rtype}/address", "{$ex_nat[$rtype]['address']}_v4");
|
10551
|
$pfb_found = TRUE;
|
10552
|
}
|
10553
|
}
|
10554
|
}
|
10555
|
}
|
10556
|
|
10557
|
if ($pfb_found) {
|
10558
|
write_config("pfBlockerNG: update NAT rule(s) aliasnames to include '_v4' suffix");
|
10559
|
}
|
10560
|
|
10561
|
#################################
|
10562
|
# Call filter_configure once #
|
10563
|
#################################
|
10564
|
|
10565
|
$log = '';
|
10566
|
if ($pfb['filter_configure']) {
|
10567
|
|
10568
|
// Remove any IPs in Alerts unlock feature
|
10569
|
unlink_if_exists("{$pfb['ip_unlock']}");
|
10570
|
|
10571
|
// Remove IP Cache file
|
10572
|
unlink_if_exists($pfb['ip_cache']);
|
10573
|
|
10574
|
require_once('filter.inc');
|
10575
|
filter_configure();
|
10576
|
|
10577
|
// Stop/Restart pfb_filter service for new rule changes
|
10578
|
if ($pfb['enable'] == '') {
|
10579
|
if (is_service_running('pfb_filter')) {
|
10580
|
$log = 'Stopping firewall filter daemon';
|
10581
|
stop_service('pfb_filter');
|
10582
|
}
|
10583
|
}
|
10584
|
else {
|
10585
|
$log = 'Restarting firewall filter daemon';
|
10586
|
restart_service('pfb_filter');
|
10587
|
}
|
10588
|
}
|
10589
|
else {
|
10590
|
// Stop/Start Firewall filter daemon
|
10591
|
if ($pfb['enable'] == '' && is_service_running('pfb_filter')) {
|
10592
|
$log = 'Stopping firewall filter daemon';
|
10593
|
stop_service('pfb_filter');
|
10594
|
}
|
10595
|
elseif ($pfb['enable'] == 'on' && !is_service_running('pfb_filter')) {
|
10596
|
$log = 'Starting firewall filter daemon';
|
10597
|
start_service('pfb_filter');
|
10598
|
}
|
10599
|
}
|
10600
|
|
10601
|
if (!empty($log)) {
|
10602
|
pfb_logger("\n\n** {$log} **\n", 1);
|
10603
|
syslog(LOG_NOTICE, "[pfBlockerNG] {$log}");
|
10604
|
}
|
10605
|
|
10606
|
|
10607
|
#################################
|
10608
|
# KILL STATES #
|
10609
|
#################################
|
10610
|
|
10611
|
if (!$pfb['save'] && $pfb['kstates'] && !$pfb['filter_configure']) {
|
10612
|
pfb_remove_states();
|
10613
|
}
|
10614
|
|
10615
|
|
10616
|
#########################################
|
10617
|
# XMLRPC - sync process #
|
10618
|
#########################################
|
10619
|
|
10620
|
if (!platform_booting() && !$g['pfblockerng_install']) {
|
10621
|
pfblockerng_sync_on_changes();
|
10622
|
}
|
10623
|
|
10624
|
#########################################
|
10625
|
# Define/Apply CRON Jobs #
|
10626
|
#########################################
|
10627
|
|
10628
|
// Replace CRON job with any user changes to $pfb_min
|
10629
|
if ($pfb['enable'] == 'on' && $pfb['interval'] != 'Disabled') {
|
10630
|
// Define pfBlockerNG CRON job
|
10631
|
$pfb_cmd = "/usr/local/bin/php /usr/local/www/pfblockerng/pfblockerng.php cron >> {$pfb['log']} 2>&1";
|
10632
|
// $pfb['min'] ( User defined variable. Variable defined at start of script )
|
10633
|
|
10634
|
// Define CRON hour (CRON interval & start hour)
|
10635
|
if ($pfb['interval'] == 1) {
|
10636
|
$pfb_hour = '*';
|
10637
|
} elseif ($pfb['interval'] == 24) {
|
10638
|
$pfb_hour = $pfb['24hour'];
|
10639
|
} else {
|
10640
|
$pfb_hour = implode(',', pfb_cron_base_hour($pfb['interval']));
|
10641
|
}
|
10642
|
|
10643
|
if ($pfb_hour == 0 || !empty(pfb_filter($pfb_hour, PFB_FILTER_CSV_CRON, 'Define pfBlockerNG CRON job'))) {
|
10644
|
$pfb_mday = '*';
|
10645
|
$pfb_month = '*';
|
10646
|
$pfb_wday = '*';
|
10647
|
$pfb_who = 'root';
|
10648
|
|
10649
|
// Determine if CRON job requires updating
|
10650
|
if (!pfblockerng_cron_exists($pfb_cmd, $pfb['min'], $pfb_hour, $pfb_mday, $pfb_wday)) {
|
10651
|
install_cron_job('pfblockerng.php cron', false);
|
10652
|
install_cron_job($pfb_cmd, true, $pfb['min'], $pfb_hour, $pfb_mday, $pfb_month, $pfb_wday, $pfb_who);
|
10653
|
}
|
10654
|
}
|
10655
|
else {
|
10656
|
pfb_logger("\n Failed to create pfBlockerNG cron task [{$pfb_hour}]", 1);
|
10657
|
}
|
10658
|
}
|
10659
|
else {
|
10660
|
// Clear any existing pfBlockerNG CRON jobs
|
10661
|
install_cron_job('pfblockerng.php cron', false);
|
10662
|
}
|
10663
|
|
10664
|
// Define pfBlockerNG MaxMind/TOP1M/ASN Cron job
|
10665
|
if ($pfb['enable'] == 'on') {
|
10666
|
$pfb_gcmd = "/usr/local/bin/php /usr/local/www/pfblockerng/pfblockerng.php dcc >> {$pfb['extraslog']} 2>&1";
|
10667
|
// CRON hour is randomized between 0-23 Hour to minimize effect on Servers
|
10668
|
$pfb_gmin = '0';
|
10669
|
$pfb_ghour = rand(0,23);
|
10670
|
$pfb_gmday = '*';
|
10671
|
$pfb_gmonth = '*';
|
10672
|
$pfb_gwday = '*';
|
10673
|
$pfb_gwho = 'root';
|
10674
|
|
10675
|
// Determine if CRON job requires updating
|
10676
|
if (!pfblockerng_cron_exists($pfb_gcmd, $pfb_gmin, 'random', $pfb_gmday, $pfb_gwday)) {
|
10677
|
install_cron_job('pfblockerng.php dcc', false);
|
10678
|
install_cron_job($pfb_gcmd, true, $pfb_gmin, $pfb_ghour, $pfb_gmday, $pfb_gmonth, $pfb_gwday, $pfb_gwho);
|
10679
|
}
|
10680
|
}
|
10681
|
else {
|
10682
|
// Clear any existing pfBlockerNG CRON jobs
|
10683
|
install_cron_job('pfblockerng.php dcc', false);
|
10684
|
}
|
10685
|
|
10686
|
|
10687
|
// Define pfBlockerNG Blacklist CRON job
|
10688
|
if ($pfb['enable'] == 'on' && $pfb['dnsbl'] == 'on' && $pfb['blconfig'] &&
|
10689
|
$pfb['blconfig']['blacklist_enable'] != 'Disable' &&
|
10690
|
$pfb['blconfig']['blacklist_freq'] != 'Never' &&
|
10691
|
!empty($pfb['blconfig']['blacklist_selected']) &&
|
10692
|
isset($pfb['blconfig']['item'])) {
|
10693
|
|
10694
|
$bl_string = '';
|
10695
|
$selected = array_flip(explode(',', $pfb['blconfig']['blacklist_selected'])) ?: array();
|
10696
|
foreach ($pfb['blconfig']['item'] as $item) {
|
10697
|
if (isset($selected[$item['xml']]) && !empty($item['selected'])) {
|
10698
|
$bl_string .= ",{$item['xml']}";
|
10699
|
}
|
10700
|
}
|
10701
|
$bl_string = pfb_filter(ltrim($bl_string, ','), PFB_FILTER_CSV, 'Define pfBlockerNG Blacklist CRON job');
|
10702
|
|
10703
|
if (!empty($bl_string)) {
|
10704
|
$pfb_bcmd = "/usr/local/bin/php /usr/local/www/pfblockerng/pfblockerng.php bl {$bl_string} >> {$pfb['extraslog']} 2>&1";
|
10705
|
|
10706
|
$pfb_bmin = '0';
|
10707
|
$pfb_bhour = rand(0,23);
|
10708
|
$pfb_bmday = '*';
|
10709
|
$pfb_bmonth = '*';
|
10710
|
$pfb_bwday = ($pfb['blconfig']['blacklist_freq'] == 'Weekly' ? '7' : '*');
|
10711
|
$pfb_bwho = 'root';
|
10712
|
|
10713
|
// Determine if CRON job requires updating
|
10714
|
if (!pfblockerng_cron_exists($pfb_bcmd, $pfb_bmin, 'random', $pfb_bmday, $pfb_bwday)) {
|
10715
|
install_cron_job('pfblockerng.php bl', false);
|
10716
|
install_cron_job($pfb_bcmd, true, $pfb_bmin, $pfb_bhour, $pfb_bmday, $pfb_bmonth, $pfb_bwday, $pfb_bwho);
|
10717
|
}
|
10718
|
}
|
10719
|
else {
|
10720
|
pfb_logger("\n Failed to create pfBlockerNG Blacklist cron task [{$bl_string}]", 1);
|
10721
|
}
|
10722
|
}
|
10723
|
else {
|
10724
|
// Clear any existing pfBlockerNG Blacklist CRON jobs
|
10725
|
install_cron_job('pfblockerng.php bl', false);
|
10726
|
}
|
10727
|
|
10728
|
// Define pfBlockerNG clear [ dnsbl and/or IP ] counter CRON job
|
10729
|
foreach (array( 'clearip', 'cleardnsbl') as $type) {
|
10730
|
$pfb_cmd = "/usr/local/bin/php /usr/local/www/pfblockerng/pfblockerng.php {$type} >/dev/null 2>&1";
|
10731
|
if (config_get_path("installedpackages/pfblockerngglobal/widget-{$type}") !== null) {
|
10732
|
if (config_get_path("installedpackages/pfblockerngglobal/widget-{$type}") != 'never') {
|
10733
|
$pfb_day = '*';
|
10734
|
if (config_get_path("installedpackages/pfblockerngglobal/widget-{$type}") == 'weekly') {
|
10735
|
$pfb_day = '7';
|
10736
|
}
|
10737
|
|
10738
|
// Remove unreferenced 'daily' or 'weekly' cron job
|
10739
|
$pfb_other = ($pfb_day == '*') ? '7' : '*';
|
10740
|
if (pfblockerng_cron_exists($pfb_cmd, '0', '0', '*', $pfb_other)) {
|
10741
|
install_cron_job("pfblockerng.php {$type}", false);
|
10742
|
}
|
10743
|
|
10744
|
if (!pfblockerng_cron_exists($pfb_cmd, '0', '0', '*', $pfb_day)) {
|
10745
|
install_cron_job($pfb_cmd, true, '0', '0', '*', '*', $pfb_day, 'root');
|
10746
|
}
|
10747
|
}
|
10748
|
else {
|
10749
|
if (pfblockerng_cron_exists($pfb_cmd, '0', '0', '*', '*')) {
|
10750
|
install_cron_job("pfblockerng.php {$type}", false);
|
10751
|
}
|
10752
|
if (pfblockerng_cron_exists($pfb_cmd, '0', '0', '*', '7')) {
|
10753
|
install_cron_job("pfblockerng.php {$type}", false);
|
10754
|
}
|
10755
|
}
|
10756
|
}
|
10757
|
else {
|
10758
|
if (pfblockerng_cron_exists($pfb_cmd, '0', '0', '*', '*')) {
|
10759
|
install_cron_job("pfblockerng.php {$type}", false);
|
10760
|
}
|
10761
|
if (pfblockerng_cron_exists($pfb_cmd, '0', '0', '*', '7')) {
|
10762
|
install_cron_job("pfblockerng.php {$type}", false);
|
10763
|
}
|
10764
|
}
|
10765
|
}
|
10766
|
|
10767
|
#################################
|
10768
|
# FINAL REPORTING #
|
10769
|
#################################
|
10770
|
|
10771
|
// Only run with CRON or Force invoked process
|
10772
|
if ((!$pfb['save'] && $pfb['repcheck'] && $pfb['enable'] == 'on') || $pfb['summary']) {
|
10773
|
// Script to run final script processes.
|
10774
|
if ($pfb['dup'] == 'on') {
|
10775
|
exec("{$pfb['script']} closing on {$elog}");
|
10776
|
}
|
10777
|
}
|
10778
|
|
10779
|
if ($pfb['enable'] == 'on' && !$pfb['save'] || $pfb['summary']) {
|
10780
|
$log = "\n UPDATE PROCESS ENDED [ NOW ]\n";
|
10781
|
pfb_logger("{$log}", 1);
|
10782
|
}
|
10783
|
}
|
10784
|
|
10785
|
|
10786
|
// Function to De-Install pfBlockerNG
|
10787
|
function pfblockerng_php_pre_deinstall_command() {
|
10788
|
require_once('config.inc');
|
10789
|
global $g, $pfb;
|
10790
|
|
10791
|
// Set these two variables to disable pfBlockerNG on de-install
|
10792
|
$pfb['save'] = $pfb['install'] = TRUE;
|
10793
|
|
10794
|
update_status("Removing pfBlockerNG...");
|
10795
|
sync_package_pfblockerng();
|
10796
|
|
10797
|
// Maintain pfBlockerNG settings and database files if $pfb['keep'] is ON.
|
10798
|
if ($pfb['keep'] != 'on') {
|
10799
|
update_status(" Removing all customizations/data...");
|
10800
|
// Remove pfBlockerNG log and DB folder
|
10801
|
rmdir_recursive("{$pfb['dbdir']}");
|
10802
|
rmdir_recursive("{$pfb['logdir']}");
|
10803
|
|
10804
|
// Remove all pfB aliastables
|
10805
|
exec("{$pfb['pfctl']} -s Tables | {$pfb['grep']} '^pfB_'", $pfb_tables);
|
10806
|
if (isset($pfb_tables)) {
|
10807
|
foreach ($pfb_tables as $pfb_table) {
|
10808
|
$pfb_table_esc = escapeshellarg($pfb_table);
|
10809
|
exec("{$pfb['pfctl']} -t {$pfb_table_esc} -T kill 2>&1", $result);
|
10810
|
}
|
10811
|
}
|
10812
|
|
10813
|
// Remove aliastables archive and earlyshellcmd if found.
|
10814
|
@unlink_if_exists("{$pfb['aliasarchive']}");
|
10815
|
if (config_get_path('system/earlyshellcmd') !== null) {
|
10816
|
$a_earlyshellcmd = config_get_path('system/earlyshellcmd', '');
|
10817
|
if (preg_grep("/pfblockerng.sh aliastables/", $a_earlyshellcmd)) {
|
10818
|
config_set_path('system/earlyshellcmd',
|
10819
|
preg_grep("/pfblockerng.sh aliastables/", $a_earlyshellcmd, PREG_GREP_INVERT));
|
10820
|
}
|
10821
|
}
|
10822
|
foreach (config_get_path('installedpackages/shellcmdsettings/config', []) as $key => $shellcmd) {
|
10823
|
if (strpos($shellcmd['cmd'], 'pfblockerng.sh aliastables') !== FALSE) {
|
10824
|
config_del_path("installedpackages/shellcmdsettings/config/{$key}");
|
10825
|
}
|
10826
|
}
|
10827
|
|
10828
|
// Remove settings from config.xml
|
10829
|
pfb_remove_config_settings();
|
10830
|
|
10831
|
unlink_if_exists("{$pfb['dnsbl_conf']}");
|
10832
|
unlink_if_exists("{$pfb['dnsbl_cert']}");
|
10833
|
unlink_if_exists("{$pfb['aliasarchive']}");
|
10834
|
unlink_if_exists("{$pfb['dnsbl_info']}");
|
10835
|
unlink_if_exists("{$pfb['dnsbl_resolver']}");
|
10836
|
|
10837
|
unlink_if_exists("{$g['unbound_chroot_path']}/pfb_unbound.py");
|
10838
|
unlink_if_exists("{$g['unbound_chroot_path']}/pfb_unbound_include.inc");
|
10839
|
unlink_if_exists("{$g['unbound_chroot_path']}/pfb_py_hsts.txt");
|
10840
|
|
10841
|
// Remove widget (code from Snort deinstall)
|
10842
|
$pfb['widgets'] = config_get_path('widgets/sequence', '');
|
10843
|
if (!empty($pfb['widgets'])) {
|
10844
|
$widgetlist = explode(',', $pfb['widgets']);
|
10845
|
foreach ($widgetlist as $key => $widget) {
|
10846
|
if (strpos($widget, 'pfblockerng') !== FALSE) {
|
10847
|
unset($widgetlist[$key]);
|
10848
|
}
|
10849
|
}
|
10850
|
config_set_path('widgets/sequence', implode(',', $widgetlist));
|
10851
|
write_config('pfBlockerNG: Remove widget', false);
|
10852
|
}
|
10853
|
}
|
10854
|
else {
|
10855
|
update_status(" All customizations/data will be retained...");
|
10856
|
}
|
10857
|
|
10858
|
unlink_if_exists('/usr/local/sbin/lighttpd_pfb');
|
10859
|
unlink_if_exists('/usr/local/bin/php_pfb');
|
10860
|
unlink_if_exists('/usr/local/sbin/clog_pfb');
|
10861
|
unlink_if_exists('/usr/bin/tail_pfb');
|
10862
|
unlink_if_exists('/usr/local/etc/rc.d/pfb_filter.sh');
|
10863
|
unlink_if_exists('/usr/local/etc/rc.d/pfb_dnsbl.sh');
|
10864
|
|
10865
|
unlink_if_exists("{$pfb['dnsbl_cache']}");
|
10866
|
unlink_if_exists("/var/tmp/unbound_cache_*");
|
10867
|
unlink_if_exists("{$pfb['ip_cache']}");
|
10868
|
|
10869
|
// Remove incorrect xml setting
|
10870
|
config_del_path('installedpackages/pfblockerngantartica');
|
10871
|
|
10872
|
update_status(" done.\n");
|
10873
|
}
|
10874
|
|
10875
|
|
10876
|
// Remove settings from config.xml
|
10877
|
function pfb_remove_config_settings() {
|
10878
|
global $pfb;
|
10879
|
|
10880
|
foreach (array( 'pfblockerng',
|
10881
|
'pfblockerngglobal',
|
10882
|
'pfblockerngsync',
|
10883
|
'pfblockerngreputation',
|
10884
|
'pfblockerngipsettings',
|
10885
|
'pfblockernglistsv4',
|
10886
|
'pfblockernglistsv6',
|
10887
|
'pfblockerngdnsbl',
|
10888
|
'pfblockerngdnsblsettings',
|
10889
|
'pfblockerngsafesearch',
|
10890
|
'pfblockerngblacklist',
|
10891
|
'pfblockerngafrica',
|
10892
|
'pfblockerngantarctica',
|
10893
|
'pfblockerngasia',
|
10894
|
'pfblockerngeurope',
|
10895
|
'pfblockerngnorthamerica',
|
10896
|
'pfblockerngoceania',
|
10897
|
'pfblockerngsouthamerica',
|
10898
|
'pfblockerngtopspammers',
|
10899
|
'pfblockerngproxyandsatellite' ) as $type) {
|
10900
|
|
10901
|
config_del_path("installedpackages/{$type}");
|
10902
|
}
|
10903
|
}
|
10904
|
|
10905
|
|
10906
|
/* Uses XMLRPC to synchronize the changes to a remote node */
|
10907
|
function pfblockerng_sync_on_changes() {
|
10908
|
// Create array of sync settings and exit if sync is disabled.
|
10909
|
$pfb_sync = config_get_path('installedpackages/pfblockerngsync/config/0', []);
|
10910
|
if (!empty($pfb_sync)) {
|
10911
|
if ($pfb_sync['varsynconchanges'] == 'disabled' || empty($pfb_sync['varsynconchanges'])) {
|
10912
|
return;
|
10913
|
}
|
10914
|
$synctimeout = $pfb_sync['varsynctimeout'] ?: 150;
|
10915
|
} else {
|
10916
|
return;
|
10917
|
}
|
10918
|
|
10919
|
pfb_logger("\n===[ XMLRPC Sync ]===================================================\n", 1);
|
10920
|
syslog(LOG_NOTICE, '[pfBlockerNG] XMLRPC sync is starting.');
|
10921
|
|
10922
|
if (config_get_path('installedpackages/pfblockerngsync/config') != null) {
|
10923
|
switch ($pfb_sync['varsynconchanges']) {
|
10924
|
case 'manual':
|
10925
|
if (isset($pfb_sync['row'])) {
|
10926
|
$rs = $pfb_sync['row'];
|
10927
|
} else {
|
10928
|
log_error('[pfBlockerNG] Manual XMLRPC sync is enabled but there are no replication targets configured.');
|
10929
|
return;
|
10930
|
}
|
10931
|
break;
|
10932
|
case 'auto':
|
10933
|
$hasync = config_get_path('hasync');
|
10934
|
if ($hasync != null) {
|
10935
|
$system_carp = $hasync;
|
10936
|
$rs[0]['varsyncipaddress'] = $system_carp['synchronizetoip'];
|
10937
|
$rs[0]['varsyncusername'] = $system_carp['username'];
|
10938
|
$rs[0]['varsyncpassword'] = $system_carp['password'];
|
10939
|
$rs[0]['varsyncdestinenable'] = FALSE;
|
10940
|
|
10941
|
// XMLRPC sync is currently only supported over connections using the same protocol and port as this system
|
10942
|
if (config_get_path('system/webgui/protocol') == 'http') {
|
10943
|
$rs[0]['varsyncprotocol'] = 'http';
|
10944
|
$rs[0]['varsyncport'] = config_get_path('system/webgui/port', '80');
|
10945
|
} else {
|
10946
|
$rs[0]['varsyncprotocol'] = 'https';
|
10947
|
$rs[0]['varsyncport'] = config_get_path('system/webgui/port', '443');
|
10948
|
}
|
10949
|
|
10950
|
if (empty($system_carp['synchronizetoip'])) {
|
10951
|
log_error('[pfBlockerNG] Auto XMLRPC sync is enabled but there is no sync IP address configured.');
|
10952
|
return;
|
10953
|
} else {
|
10954
|
$rs[0]['varsyncdestinenable'] = TRUE;
|
10955
|
}
|
10956
|
} else {
|
10957
|
log_error('[pfBlockerNG] Auto XMLRPC sync is enabled but there are no replication targets configured.');
|
10958
|
return;
|
10959
|
}
|
10960
|
break;
|
10961
|
default:
|
10962
|
return;
|
10963
|
break;
|
10964
|
}
|
10965
|
if (isset($rs)) {
|
10966
|
foreach ($rs as $sh) {
|
10967
|
// Only sync enabled replication targets
|
10968
|
if ($sh['varsyncdestinenable']) {
|
10969
|
$sync_to_ip = $sh['varsyncipaddress'];
|
10970
|
$port = $sh['varsyncport'];
|
10971
|
$password = $sh['varsyncpassword'];
|
10972
|
$protocol = $sh['varsyncprotocol'];
|
10973
|
$username = $sh['varsyncusername'] ?: 'admin';
|
10974
|
|
10975
|
$validate = TRUE;
|
10976
|
$error = '| ';
|
10977
|
|
10978
|
if (empty($password)) {
|
10979
|
$error .= 'Password parameter missing. | ';
|
10980
|
$validate = FALSE;
|
10981
|
}
|
10982
|
if (!is_ipaddr($sync_to_ip) && !is_hostname($sync_to_ip) && !is_domain($sync_to_ip)) {
|
10983
|
$error .= 'Mis-configured Target IP/Host address. | ';
|
10984
|
$validate = FALSE;
|
10985
|
}
|
10986
|
if (!is_port($port)) {
|
10987
|
$error .= 'Mis-configured Target Port setting. |';
|
10988
|
$validate = FALSE;
|
10989
|
}
|
10990
|
|
10991
|
if ($validate) {
|
10992
|
pfb_logger("\n Sync with [ {$protocol}://{$sync_to_ip}:{$port} ] ...", 1);
|
10993
|
$success = pfblockerng_do_xmlrpc_sync($sync_to_ip, $port, $protocol, $username, $password, $synctimeout);
|
10994
|
|
10995
|
if ($success) {
|
10996
|
pfb_logger(" done.\n", 1);
|
10997
|
syslog(LOG_NOTICE, "[pfBlockerNG] XMLRPC sync to [ {$sync_to_ip}:{port} ] completed successfully.");
|
10998
|
} else {
|
10999
|
pfb_logger(" Failed!\n", 1);
|
11000
|
}
|
11001
|
} else {
|
11002
|
pfb_logger(" terminated due to the following error(s): {$error}", 1);
|
11003
|
log_error("[pfBlockerNG] XMLRPC sync to [ {$sync_to_ip}:{port} ] terminated due to the following error(s): {$error}");
|
11004
|
}
|
11005
|
}
|
11006
|
}
|
11007
|
}
|
11008
|
}
|
11009
|
pfb_logger("\n======================================================================\n", 1);
|
11010
|
}
|
11011
|
|
11012
|
|
11013
|
/* Do the actual XMLRPC sync */
|
11014
|
function pfblockerng_do_xmlrpc_sync($sync_to_ip, $port, $protocol, $username, $password, $synctimeout) {
|
11015
|
global $g;
|
11016
|
$success = TRUE;
|
11017
|
|
11018
|
// Take care of IPv6 literal address
|
11019
|
if (is_ipaddrv6($sync_to_ip)) {
|
11020
|
$sync_to_ip = "[{$sync_to_ip}]";
|
11021
|
}
|
11022
|
|
11023
|
/* xml will hold the sections to sync */
|
11024
|
$xml = array();
|
11025
|
|
11026
|
// If User Disabled, remove 'General/IP/DNSBL Tab Customizations' from Sync
|
11027
|
if (config_get_path('installedpackages/pfblockerngsync/config/0/syncinterfaces') != 'on') {
|
11028
|
if (config_get_path('installedpackages/pfblockerng') != null) {
|
11029
|
$xml['pfblockerng'] = config_get_path('installedpackages/pfblockerng');
|
11030
|
}
|
11031
|
if (config_get_path('installedpackages/pfblockerngipsettings') != null) {
|
11032
|
$xml['pfblockerngipsettings'] = config_get_path('installedpackages/pfblockerngipsettings');
|
11033
|
}
|
11034
|
if (config_get_path('installedpackages/pfblockerngdnsblsettings') != null) {
|
11035
|
$xml['pfblockerngdnsblsettings']= config_get_path('installedpackages/pfblockerngdnsblsettings');
|
11036
|
|
11037
|
// Increase CARP Advskew value, see https://redmine.pfsense.org/issues/11964
|
11038
|
if (isset($xml['pfblockerngdnsblsettings']['config'][0]['pfb_dnsvip_skew'])) {
|
11039
|
$advskew = intval($xml['pfblockerngdnsblsettings']['config'][0]['pfb_dnsvip_skew']);
|
11040
|
$advskew += 100;
|
11041
|
if ($advskew > 254) {
|
11042
|
$advskew = 254;
|
11043
|
}
|
11044
|
$xml['pfblockerngdnsblsettings']['config'][0]['pfb_dnsvip_skew'] = $advskew;
|
11045
|
}
|
11046
|
}
|
11047
|
}
|
11048
|
|
11049
|
if (config_get_path('installedpackages/pfblockernglistsv4') !== null)
|
11050
|
$xml['pfblockernglistsv4'] = config_get_path('installedpackages/pfblockernglistsv4');
|
11051
|
if (config_get_path('installedpackages/pfblockernglistsv6') !== null)
|
11052
|
$xml['pfblockernglistsv6'] = config_get_path('installedpackages/pfblockernglistsv6');
|
11053
|
if (config_get_path('installedpackages/pfblockerngreputation') !== null)
|
11054
|
$xml['pfblockerngreputation'] = config_get_path('installedpackages/pfblockerngreputation');
|
11055
|
if (config_get_path('installedpackages/pfblockerngtopspammers') !== null)
|
11056
|
$xml['pfblockerngtopspammers'] = config_get_path('installedpackages/pfblockerngtopspammers');
|
11057
|
if (config_get_path('installedpackages/pfblockerngafrica') !== null)
|
11058
|
$xml['pfblockerngafrica'] = config_get_path('installedpackages/pfblockerngafrica');
|
11059
|
if (config_get_path('installedpackages/pfblockerngantarctica') !== null)
|
11060
|
$xml['pfblockerngantarctica'] = config_get_path('installedpackages/pfblockerngantarctica');
|
11061
|
if (config_get_path('installedpackages/pfblockerngasia') !== null)
|
11062
|
$xml['pfblockerngasia'] = config_get_path('installedpackages/pfblockerngasia');
|
11063
|
if (config_get_path('installedpackages/pfblockerngeurope') !== null)
|
11064
|
$xml['pfblockerngeurope'] = config_get_path('installedpackages/pfblockerngeurope');
|
11065
|
if (config_get_path('installedpackages/pfblockerngnorthamerica') !== null)
|
11066
|
$xml['pfblockerngnorthamerica'] = config_get_path('installedpackages/pfblockerngnorthamerica');
|
11067
|
if (config_get_path('installedpackages/pfblockerngoceania') !== null)
|
11068
|
$xml['pfblockerngoceania'] = config_get_path('installedpackages/pfblockerngoceania');
|
11069
|
if (config_get_path('installedpackages/pfblockerngsouthamerica') !== null)
|
11070
|
$xml['pfblockerngsouthamerica'] = config_get_path('installedpackages/pfblockerngsouthamerica');
|
11071
|
if (config_get_path('installedpackages/pfblockerngproxyandsatellite') !== null)
|
11072
|
$xml['pfblockerngproxyandsatellite'] = config_get_path('installedpackages/pfblockerngproxyandsatellite');
|
11073
|
if (config_get_path('installedpackages/pfblockerngdnsbl') !== null)
|
11074
|
$xml['pfblockerngdnsbl'] = config_get_path('installedpackages/pfblockerngdnsbl');
|
11075
|
if (config_get_path('installedpackages/pfblockerngblacklist') !== null)
|
11076
|
$xml['pfblockerngblacklist'] = config_get_path('installedpackages/pfblockerngblacklist');
|
11077
|
if (config_get_path('installedpackages/pfblockerngglobal') !== null)
|
11078
|
$xml['pfblockerngglobal'] = config_get_path('installedpackages/pfblockerngglobal');
|
11079
|
if (config_get_path('installedpackages/pfblockerngsafesearch') !== null)
|
11080
|
$xml['pfblockerngsafesearch'] = config_get_path('installedpackages/pfblockerngsafesearch');
|
11081
|
|
11082
|
|
11083
|
// Execute applicable XMLRPC code as per pfSense version
|
11084
|
if (substr(trim(file_get_contents('/etc/version')), 0, 3) < '2.4') {
|
11085
|
|
11086
|
require_once('xmlrpc.inc');
|
11087
|
require_once('xmlrpc_client.inc');
|
11088
|
|
11089
|
$url = "{$protocol}://{$sync_to_ip}";
|
11090
|
|
11091
|
/* assemble xmlrpc payload */
|
11092
|
$params = array(XML_RPC_encode($password), XML_RPC_encode($xml));
|
11093
|
|
11094
|
/* set a few variables needed for sync code borrowed from filter.inc */
|
11095
|
$msg = new XML_RPC_Message('pfsense.merge_installedpackages_section_xmlrpc', $params);
|
11096
|
$cli = new XML_RPC_Client('/xmlrpc.php', $url, $port);
|
11097
|
$cli->setCredentials($username, $password);
|
11098
|
if ($g['debug']) {
|
11099
|
$cli->setDebug(1);
|
11100
|
}
|
11101
|
|
11102
|
/* send our XMLRPC message and timeout after defined sync timeout value */
|
11103
|
$resp = $cli->send($msg, $synctimeout);
|
11104
|
|
11105
|
if (!$resp) {
|
11106
|
log_error("[pfBlockerNG] XMLRPC communications error occurred while attempting sync with {$url}:{$port}.");
|
11107
|
file_notice('pfBlockerNG Sync settings', $error, 'pfBlockerNG', '/pfblockerng/pfblockerng_sync.php', 2);
|
11108
|
$success = FALSE;
|
11109
|
} elseif ($resp->faultCode()) {
|
11110
|
$cli->setDebug(1);
|
11111
|
$resp = $cli->send($msg, $synctimeout);
|
11112
|
log_error("[pfBlockerNG] XMLRPC errors syncing with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString());
|
11113
|
file_notice('pfBlockerNG Sync settings', $error, 'pfBlockerNG', '/pfblockerng/pfblockerng_sync.php', 2);
|
11114
|
$success = FALSE;
|
11115
|
}
|
11116
|
return $success;
|
11117
|
}
|
11118
|
else {
|
11119
|
require_once('xmlrpc_client.inc');
|
11120
|
|
11121
|
// xmlrpc cannot encode NULL objects/arrays
|
11122
|
foreach ($xml as $xmlkey => $xmlvalue) {
|
11123
|
if (gettype($xmlvalue) == 'NULL') {
|
11124
|
$xml[$xmlkey] = array();
|
11125
|
}
|
11126
|
}
|
11127
|
|
11128
|
$synctimeout = intval($synctimeout);
|
11129
|
$rpc_client = new pfsense_xmlrpc_client();
|
11130
|
$rpc_client->setConnectionData($sync_to_ip, $port, $username, $password, $protocol);
|
11131
|
$resp = $rpc_client->xmlrpc_method('merge_installedpackages_section', $xml, $synctimeout);
|
11132
|
|
11133
|
if (!isset($resp)) {
|
11134
|
return FALSE;
|
11135
|
} else {
|
11136
|
return TRUE;
|
11137
|
}
|
11138
|
}
|
11139
|
}
|