Skip to content

Commit 53d0ade

Browse files
committed
Issue #24:blocking IP ranges
1 parent 2be2e7d commit 53d0ade

File tree

6 files changed

+535
-10
lines changed

6 files changed

+535
-10
lines changed

README.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ IP Address Blocking
33

44
Allows you to block IP addresses.
55
This module restores the lost functionality from the Drupal core.
6-
It also comes with a number of improvements.
6+
It also comes with a significant number of improvements.
77

88
Options available:
99

@@ -18,6 +18,23 @@ Options available:
1818
- use the "AbuseIPDB Report" module (https://backdropcms.org/project/abuseipdb_report) to send
1919
manual or automatic reports to the [AbuseIPDB](https://www.abuseipdb.com/) database.
2020

21+
---
22+
**New in version 1.0.19 – IP range blocking (CIDR support)**
23+
24+
The module now supports blocking entire IP ranges in addition to individual IP addresses.
25+
26+
Administrators can block:
27+
28+
- IPv4 CIDR ranges (e.g. 192.168.10.0/24)
29+
- IPv6 CIDR ranges (e.g. 2001:db8::/32)
30+
- Explicit ranges (e.g. 10.0.0.5-10.0.0.25)
31+
32+
A new "Ranges" tab is available at "admin/config/people/ip-blocking/ranges"
33+
34+
Blocked ranges are evaluated in addition to single IP blocks.
35+
36+
---
37+
2138
Installation
2239
------------
2340
Install this module using the official Backdrop CMS instructions at https://backdropcms.org/guide/modules

css/ip_blocking.admin.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* Admin form adjustment for desktop screen */
22
@media screen and (min-width: 48rem) {
3+
#ip-blocking-ranges-form > div,
34
#ip-blocking-form > div {
45
border: 2px solid #d0d0d0;
56
border-radius: 0.25rem;
@@ -10,15 +11,18 @@
1011
padding: 1rem 1rem 0;
1112
}
1213

14+
#ip-blocking-ranges-form > div p,
1315
#ip-blocking-form > div p {
1416
flex: 0 0 100%;
1517
margin: 0;
1618
}
1719

20+
#ip-blocking-ranges-form .form-item input,
1821
#ip-blocking-form .form-item input {
1922
margin-inline-end: 2rem;
2023
}
2124

25+
#edit-range-label,
2226
#edit-ip {
2327
width: 24rem;
2428
}
@@ -27,6 +31,7 @@
2731
width: 35rem;
2832
}
2933

34+
#ip-blocking-ranges-form .form-actions,
3035
#ip-blocking-form .form-actions {
3136
margin-inline-start: auto;
3237
align-self: center;
@@ -37,6 +42,11 @@
3742
margin-inline-end: 0;
3843
}
3944

45+
#ip-blocking-ranges-form .form-submit {
46+
margin-block-start: 0.22rem;
47+
margin-inline-end: 0;
48+
}
49+
4050
table td {
4151
min-width: 8rem;
4252
max-width: 34rem;

ip_blocking.admin.inc

Lines changed: 200 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,6 @@ function ip_blocking_form($form, &$form_state, $default_ip) {
8888
'#description' => t('Enter a valid IP address.'),
8989
'#prefix' => '<p>' . t('The IP addresses listed below are blocked from this site. Blocked addresses will be completely denied access to the site and will instead see a short message explaining the situation or a 404 error page (of your choice using the "Settings" tab above).') .
9090
'<br>' . t('From the "Operations" column you can unblock any IP and check its status in AbuseIPDB (opens in a new tab).') . '</p>',
91-
92-
9391
);
9492
$form['reason'] = array(
9593
'#title' => t('Reason for blocking (optional)'),
@@ -332,3 +330,203 @@ function ip_blocking_orphaned_table_form_submit($form, &$form_state) {
332330
backdrop_set_message($message);
333331
$form_state['redirect'] = 'admin/config/people/ip-blocking';
334332
}
333+
334+
function ip_blocking_ranges_page() {
335+
$config = config('ip_blocking.settings');
336+
$number_of_items = (int) $config->get('number_of_items');
337+
if ($number_of_items <= 0) {
338+
$number_of_items = 50;
339+
}
340+
341+
$header = array(
342+
array('data' => t('Range'), 'field' => 'range_label'),
343+
array('data' => t('Time'), 'field' => 'time', 'sort' => 'desc'),
344+
array('data' => t('Blocker'), 'field' => 'uid'),
345+
array('data' => t('Reason'), 'field' => 'reason'),
346+
array('data' => t('Operations')),
347+
);
348+
349+
$rows = array();
350+
351+
if (db_table_exists('blocked_ip_ranges')) {
352+
$select = db_select('blocked_ip_ranges', 'r')
353+
->extend('PagerDefault')
354+
->extend('TableSort');
355+
356+
$select->fields('r', array('rid', 'range_label', 'reason', 'uid', 'time'))
357+
->limit($number_of_items)
358+
->orderByHeader($header);
359+
360+
$results = $select->execute();
361+
362+
foreach ($results as $r) {
363+
$time = !empty($r->time) ? format_date($r->time, 'short') : t('N/A');
364+
365+
if (!empty($r->uid)) {
366+
if (defined('ANTISCAN_MODULE_UID') && $r->uid == ANTISCAN_MODULE_UID) {
367+
$username = t('Antiscan module');
368+
}
369+
else {
370+
$account = user_load($r->uid);
371+
$username = $account ? $account->name : t('N/A');
372+
}
373+
}
374+
else {
375+
$username = t('N/A');
376+
}
377+
378+
$ops = l(
379+
t('unblock'),
380+
"admin/config/people/ip-blocking/unblock_range/{$r->rid}",
381+
array('attributes' => array('title' => t('Unblock this range'))))
382+
. '&nbsp;|&nbsp;' .
383+
l(t('check'), "https://www.abuseipdb.com/check-block/{$r->range_label}",
384+
array('attributes' => array('target' => '_blank', 'title' => 'Check IP range status in AbuseIPDB')));
385+
386+
$rows[] = array(
387+
check_plain($r->range_label),
388+
$time,
389+
check_plain($username),
390+
check_plain($r->reason),
391+
$ops,
392+
);
393+
}
394+
}
395+
396+
$build = array();
397+
$build['range_form'] = backdrop_get_form('ip_blocking_ranges_form');
398+
399+
$output = backdrop_render($build);
400+
$output .= theme('table', array('header' => $header, 'rows' => $rows));
401+
$output .= theme('pager');
402+
403+
return $output;
404+
}
405+
406+
function ip_blocking_ranges_form($form, &$form_state) {
407+
$form['#attached']['css'] = array(
408+
backdrop_get_path('module', 'ip_blocking') . '/css/ip_blocking.admin.css',
409+
);
410+
411+
$form['range_label'] = array(
412+
'#title' => t('Add IP range to blocked list'),
413+
'#type' => 'textfield',
414+
'#size' => 48,
415+
'#maxlength' => 64,
416+
'#default_value' => '',
417+
'#description' => t('Enter a CIDR (e.g. 1.2.3.0/24 or 2001:db8::/32)')
418+
. '<br>'
419+
. t('or an explicit range (e.g. 1.2.3.4-1.2.3.9).'),
420+
'#prefix' => '<p>' . t('The IP ranges listed below are blocked from this site. Blocked IP ranges will be completely denied access to the site and will instead see a short message explaining the situation or a 404 error page (of your choice using the "Settings" tab above).') .
421+
'<br>' . t('From the "Operations" column you can unblock any IP range and check its status in AbuseIPDB (opens in a new tab).') . '</p>',
422+
);
423+
424+
$form['reason'] = array(
425+
'#title' => t('Reason for blocking (optional)'),
426+
'#type' => 'textfield',
427+
'#size' => 52,
428+
'#maxlength' => 255,
429+
'#default_value' => '',
430+
'#description' => t('Short description of the reason for blocking this IP range, e.g: "DDoS subnet".'),
431+
);
432+
433+
$form['actions'] = array('#type' => 'actions');
434+
$form['actions']['submit'] = array(
435+
'#type' => 'submit',
436+
'#value' => t('Add'),
437+
);
438+
439+
$form['#validate'][] = 'ip_blocking_ranges_form_validate';
440+
$form['#submit'][] = 'ip_blocking_ranges_form_submit';
441+
442+
return $form;
443+
}
444+
445+
function ip_blocking_ranges_form_validate($form, &$form_state) {
446+
$range_label = trim($form_state['values']['range_label']);
447+
$parsed = ip_blocking_parse_range($range_label);
448+
if (!$parsed) {
449+
form_set_error('range_label', t('Enter a valid CIDR or IP range.'));
450+
return;
451+
}
452+
453+
$my_ip = ip_address();
454+
$my_info = ip_blocking_ip_to_bin($my_ip);
455+
if ($my_info && $my_info['family'] == $parsed['family']) {
456+
if (strcmp($parsed['start_bin'], $my_info['bin']) <= 0 && strcmp($parsed['end_bin'], $my_info['bin']) >= 0) {
457+
form_set_error('range_label', t('You can not block your own IP address.'));
458+
return;
459+
}
460+
}
461+
462+
if (!db_table_exists('blocked_ip_ranges')) {
463+
form_set_error('range_label', t('Range blocking is not available (missing database table). Run update.php.'));
464+
return;
465+
}
466+
467+
$exists = db_query(
468+
"SELECT 1 FROM {blocked_ip_ranges} WHERE range_label = :range_label",
469+
array(':range_label' => $parsed['label'])
470+
)->fetchField();
471+
472+
if ($exists) {
473+
form_set_error('range_label', t('This IP range is already blocked.'));
474+
return;
475+
}
476+
477+
$form_state['ip_blocking_parsed_range'] = $parsed;
478+
}
479+
480+
function ip_blocking_ranges_form_submit($form, &$form_state) {
481+
global $user;
482+
483+
$parsed = $form_state['ip_blocking_parsed_range'];
484+
$reason = trim($form_state['values']['reason']);
485+
486+
db_insert('blocked_ip_ranges')->fields(array(
487+
'ip_family' => (int) $parsed['family'],
488+
'ip_start' => $parsed['start_bin'],
489+
'ip_end' => $parsed['end_bin'],
490+
'range_label' => $parsed['label'],
491+
'reason' => $reason,
492+
'uid' => (int) $user->uid,
493+
'time' => time(),
494+
'type' => 'admin_form',
495+
))->execute();
496+
497+
backdrop_set_message(t('The range %range has been blocked.', array('%range' => $parsed['label'])));
498+
$form_state['redirect'] = 'admin/config/people/ip-blocking/ranges';
499+
}
500+
501+
function ip_blocking_unblock_range($form, &$form_state, $rid) {
502+
$row = db_query(
503+
"SELECT rid, range_label FROM {blocked_ip_ranges} WHERE rid = :rid",
504+
array(':rid' => (int) $rid)
505+
)->fetchAssoc();
506+
507+
if (!$row) {
508+
backdrop_set_message(t('Range not found.'), 'error');
509+
$form_state['redirect'] = 'admin/config/people/ip-blocking/ranges';
510+
return array();
511+
}
512+
513+
$form['rid'] = array('#type' => 'value', '#value' => (int) $row['rid']);
514+
$form['#submit'][] = 'ip_blocking_unblock_range_submit';
515+
516+
return confirm_form(
517+
$form,
518+
t('Are you sure you want to unblock %range?', array('%range' => $row['range_label'])),
519+
'admin/config/people/ip-blocking/ranges',
520+
t('This action cannot be undone.'),
521+
t('Unblock'),
522+
t('Cancel')
523+
);
524+
}
525+
526+
function ip_blocking_unblock_range_submit($form, &$form_state) {
527+
$rid = (int) $form_state['values']['rid'];
528+
db_delete('blocked_ip_ranges')->condition('rid', $rid)->execute();
529+
530+
backdrop_set_message(t('The range has been unblocked.'));
531+
$form_state['redirect'] = 'admin/config/people/ip-blocking/ranges';
532+
}

ip_blocking.info

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ package = Spam control
44
backdrop = 1.x
55
type = module
66
configure = admin/config/people/ip-blocking
7-
version = 1.0.18
7+
version = 1.0.19

0 commit comments

Comments
 (0)