Add blacklist feature
This commit is contained in:
parent
3adb47efde
commit
b10d070701
76
neomi.py
76
neomi.py
|
@ -1,5 +1,6 @@
|
||||||
import configparser
|
import configparser
|
||||||
import enum
|
import enum
|
||||||
|
import ipaddress
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import select
|
import select
|
||||||
|
@ -12,6 +13,7 @@ import urllib.parse
|
||||||
|
|
||||||
class default_config: None
|
class default_config: None
|
||||||
|
|
||||||
|
default_config.blacklist_file = pathlib.Path(os.environ['HOME']) / 'gopher_blacklist'
|
||||||
default_config.charset = 'utf-8'
|
default_config.charset = 'utf-8'
|
||||||
default_config.fallback_mimetype = 'application/octet-stream'
|
default_config.fallback_mimetype = 'application/octet-stream'
|
||||||
default_config.gopher_root = pathlib.Path(os.environ['HOME']) / 'gopher'
|
default_config.gopher_root = pathlib.Path(os.environ['HOME']) / 'gopher'
|
||||||
|
@ -33,6 +35,12 @@ def die(message, status = 1):
|
||||||
error(message)
|
error(message)
|
||||||
sys.exit(status)
|
sys.exit(status)
|
||||||
|
|
||||||
|
# log(message)
|
||||||
|
# Print a log message to stdout
|
||||||
|
def log(message):
|
||||||
|
program_name = os.path.basename(sys.argv[0])
|
||||||
|
print('%s: %s' % (program_name, message))
|
||||||
|
|
||||||
# A base for Exeptions that are used with one argument and that return a string that incorporates said argument
|
# A base for Exeptions that are used with one argument and that return a string that incorporates said argument
|
||||||
class OneArgumentException(Exception):
|
class OneArgumentException(Exception):
|
||||||
def __init__(self, argument):
|
def __init__(self, argument):
|
||||||
|
@ -433,11 +441,11 @@ def send_header(sock, protocol, status, mimetype, *, config):
|
||||||
elif status == Status.error:
|
elif status == Status.error:
|
||||||
# Technically -2 means "Try again later", but there is no code for "server blew up"
|
# Technically -2 means "Try again later", but there is no code for "server blew up"
|
||||||
header = b'--2\r\n'
|
header = b'--2\r\n'
|
||||||
|
|
||||||
elif protocol == Protocol.gopher:
|
elif protocol == Protocol.gopher:
|
||||||
# Gopher has no header
|
# Gopher has no header
|
||||||
header = b''
|
header = b''
|
||||||
|
|
||||||
else:
|
else:
|
||||||
unreachable()
|
unreachable()
|
||||||
|
|
||||||
|
@ -457,7 +465,7 @@ def send_binaryfile(sock, reader, protocol, *, config):
|
||||||
left = buffer_max
|
left = buffer_max
|
||||||
|
|
||||||
buffer.append(byte)
|
buffer.append(byte)
|
||||||
|
|
||||||
# If there was something left in the buffer, flush it
|
# If there was something left in the buffer, flush it
|
||||||
if len(buffer) != 0:
|
if len(buffer) != 0:
|
||||||
sock.sendall(buffer)
|
sock.sendall(buffer)
|
||||||
|
@ -494,7 +502,7 @@ def send_textfile(sock, reader, protocol, *, config):
|
||||||
|
|
||||||
# Signal end of text
|
# Signal end of text
|
||||||
sock.sendall(b'.\r\n')
|
sock.sendall(b'.\r\n')
|
||||||
|
|
||||||
else:
|
else:
|
||||||
unreachable()
|
unreachable()
|
||||||
|
|
||||||
|
@ -601,6 +609,56 @@ class Threads_controller:
|
||||||
with self.threads_lock:
|
with self.threads_lock:
|
||||||
self.threads_amount -= 1
|
self.threads_amount -= 1
|
||||||
|
|
||||||
|
class IPParseError(OneArgumentException):
|
||||||
|
text = 'Error parsing IP: %s'
|
||||||
|
|
||||||
|
# read_blacklist(blacklist_file) → blacklist
|
||||||
|
# Reads the contents of the blacklist file into a form usable by ip_in_ranges()
|
||||||
|
def read_blacklist(blacklist_file):
|
||||||
|
try:
|
||||||
|
file = open(str(blacklist_file), 'r')
|
||||||
|
except FileNotFoundError:
|
||||||
|
return []
|
||||||
|
|
||||||
|
lines = file.read().split('\n')
|
||||||
|
file.close()
|
||||||
|
|
||||||
|
blacklist = []
|
||||||
|
for line in lines:
|
||||||
|
# Comment handling
|
||||||
|
if '#' in line:
|
||||||
|
line = line[:line.index('#')]
|
||||||
|
|
||||||
|
# Remove surrounding whitespace
|
||||||
|
line = line.strip()
|
||||||
|
|
||||||
|
# If an empty line, skip
|
||||||
|
if line == '':
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
ip_range = ipaddress.ip_network(line)
|
||||||
|
except ValueError:
|
||||||
|
raise IPParseError('Invalid format: ' + line)
|
||||||
|
|
||||||
|
blacklist.append(ip_range)
|
||||||
|
|
||||||
|
return blacklist
|
||||||
|
|
||||||
|
# ip_in_ranges(ip, ip_ranges) → in_rages
|
||||||
|
# Checks whether an ip address is in given ranges
|
||||||
|
def ip_in_ranges(ip, ip_ranges):
|
||||||
|
try:
|
||||||
|
ip = ipaddress.ip_address(ip)
|
||||||
|
except ValueError:
|
||||||
|
raise IPParseError('Invalid format: ' + line)
|
||||||
|
|
||||||
|
for ip_range in ip_ranges:
|
||||||
|
if ip in ip_range:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
# listen(config) → (Never returns)
|
# listen(config) → (Never returns)
|
||||||
# Binds itself to all interfaces on designated port and listens on incoming connections
|
# Binds itself to all interfaces on designated port and listens on incoming connections
|
||||||
# Spawns worker threads to handle the connections
|
# Spawns worker threads to handle the connections
|
||||||
|
@ -625,6 +683,9 @@ def listen(config):
|
||||||
# Create a controller object for the worker threads
|
# Create a controller object for the worker threads
|
||||||
threads_controller = Threads_controller()
|
threads_controller = Threads_controller()
|
||||||
|
|
||||||
|
# Read blacklist of addresses
|
||||||
|
blacklist = read_blacklist(config.blacklist_file)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
# Wait for listening sockets to get activity
|
# Wait for listening sockets to get activity
|
||||||
events = listening.poll()
|
events = listening.poll()
|
||||||
|
@ -635,6 +696,13 @@ def listen(config):
|
||||||
# Accept and handle the connection
|
# Accept and handle the connection
|
||||||
conn, addr = s.accept()
|
conn, addr = s.accept()
|
||||||
|
|
||||||
|
# Check if connection is from a blacklisted IP address
|
||||||
|
if ip_in_ranges(addr[0], blacklist):
|
||||||
|
# It was, skip event
|
||||||
|
conn.close()
|
||||||
|
log('Connection from blacklisted address %s' % addr[0])
|
||||||
|
continue
|
||||||
|
|
||||||
# Set timeout for socket
|
# Set timeout for socket
|
||||||
conn.settimeout(config.socket_timeout)
|
conn.settimeout(config.socket_timeout)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue