seg/trabalho5/proxy.py

210 lines
5.8 KiB
Python
Raw Normal View History

2023-11-17 00:28:37 +00:00
import socket
import select
from threading import Thread
import time
2023-12-05 01:22:57 +00:00
import sys
2023-11-17 00:28:37 +00:00
import logging
import logging.handlers
2023-12-05 01:22:57 +00:00
from logging import config
2023-11-17 00:28:37 +00:00
CURRENT_THREADS = 0
MAX_THREADS = 10
BACKLOG = 50
2023-12-05 01:22:57 +00:00
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '%(levelname)s %(message)s'
},
},
'handlers': {
'stdout': {
'class': 'logging.StreamHandler',
'stream': sys.stdout,
'formatter': 'verbose',
},
'sys-logger6': {
'class': 'logging.handlers.SysLogHandler',
"address": ("127.0.0.1", 514),
'facility': "local6",
'formatter': 'verbose',
},
},
'loggers': {
'PythonProxy': {
'handlers': ['sys-logger6', 'stdout'],
'level': logging.DEBUG,
'propagate': True,
},
}
}
config.dictConfig(LOGGING)
2023-11-17 00:28:37 +00:00
class Logger:
_instance = None
def __init__(self):
if self._instance is not None:
raise Exception("Logger is a singleton!")
2023-11-17 00:28:37 +00:00
self.logger = logging.getLogger('PythonProxy')
2023-12-05 01:22:57 +00:00
self.logger.info('Initiating Proxy logger!')
2023-11-17 00:28:37 +00:00
def log(self, message:str):
2023-12-05 01:22:57 +00:00
self.logger.info(message)
2023-11-17 00:28:37 +00:00
def critical(self, message:str):
self.logger.critical(message)
@classmethod
def instance(self):
if self._instance is None:
self._instance = self()
return self._instance
class Server:
def __init__(self, host:str, port:int):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.bind((host, port))
self.sock.listen(BACKLOG)
print(f"Listening at: http://{host}:{port}")
def thread_check(self):
global CURRENT_THREADS, MAX_THREADS
while True:
if CURRENT_THREADS >= MAX_THREADS:
time.sleep(1)
else:
return
def start(self):
global CURRENT_THREADS
while True:
conn, client_addr = self.sock.accept()
self.thread_check()
CURRENT_THREADS += 1
thread = Thread(target = connectionHandle, args = (conn, client_addr, ))
CURRENT_THREADS -= 1
thread.start()
def __del__(self):
self.sock.close()
def is_valid_status_code(status_code:str):
valid_starts = [str(i) for i in range(5)]
if status_code.startswith(tuple(valid_starts)):
return True
return False
2023-11-17 00:28:37 +00:00
def connectionHandle(client_socket, client_address):
2023-11-17 00:28:37 +00:00
request = client_socket.recv(16 * 1024)
logger = Logger.instance()
if len(request) == 0:
client_socket.close()
return
try:
raw_request = request.decode()
except UnicodeDecodeError:
client_socket.close()
return
if "CONNECT" in raw_request:
client_socket.sendall(b"HTTP/1.1 200 Connection Established\r\n\r\n")
request = client_socket.recv(16 * 1024)
request_url = raw_request.split(' ')[1]
request_host = ""
request_port = 443 if 'https' in request_url else 80
if request_url.startswith('http'):
request_host = request_url.split('/')[2]
else:
request_host = request_url.split('/')[0]
if request_host.startswith('www'):
request_host = request_host[4:]
if ':' in request_host:
request_port = int(request_host.split(':')[1])
request_host = request_host.split(':')[0]
if "monitorando" in request_url:
body = "<HTML><HEAD><TITLE>403</TITLE><meta charset=\"UTF-8\"></HEAD> <BODY>Acesso não autorizado!</BODY> </HTML>"
client_socket.sendall(b"HTTP/1.1 403 Forbidden\r\n\r\n")
client_socket.sendall(body.encode())
client_socket.close()
2023-12-05 01:22:57 +00:00
logger.log(f"REQUEST [{client_address[0]}:{client_address[1]}] to [{request_host}:{request_port}] - 403")
2023-11-17 00:28:37 +00:00
return
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.connect((request_host, request_port))
server_socket.send(request)
2023-11-17 00:28:37 +00:00
while True:
triple = select.select([client_socket, server_socket], [], [], 20)[0]
2023-11-17 00:28:37 +00:00
if not len(triple):
break
try:
if server_socket in triple:
data = server_socket.recv(16 * 1024)
2023-11-17 00:28:37 +00:00
if not data:
break
try:
status_code = data.decode().split('\r\n')[0].split(' ')[1:]
2023-11-17 00:28:37 +00:00
status_code = ' '.join(status_code)
if is_valid_status_code(status_code):
logger.log(f"REQUEST [{client_address[0]}:{client_address[1]}] to [{request_host}:{request_port}] - {status_code}")
2023-11-17 00:28:37 +00:00
except UnicodeDecodeError:
2023-12-05 01:22:57 +00:00
logger.log(f"REQUEST [{client_address[0]}:{client_address[1]}] to [{request_host}:{request_port}]")
2023-11-17 00:28:37 +00:00
pass
client_socket.send(data)
2023-12-05 01:22:57 +00:00
if client_socket in triple:
data = client_socket.recv(16 * 1024)
if not data:
break
server_socket.send(data)
2023-11-17 00:28:37 +00:00
except ConnectionAbortedError:
break
server_socket.close()
2023-11-17 00:28:37 +00:00
client_socket.close()
2023-11-25 02:01:18 +00:00
def verify_code_integrity():
import hashlib
import sys
content = None
md5_hash = None
with open(sys.argv[0], 'rb') as f:
content = f.read()
with open('proxy.py.md5', 'r') as f:
md5_hash = f.read()
hash = hashlib.md5(content).hexdigest()
if hash != md5_hash:
print('ERROR: proxy.py has been tampered with!')
exit(1)
print('[Code Integrity] proxy.py is verified!')
2023-11-17 00:28:37 +00:00
if __name__ == '__main__':
2023-12-05 01:22:57 +00:00
# verify_code_integrity()
2023-11-25 02:01:18 +00:00
2023-12-05 01:22:57 +00:00
try:
ser = Server(host="0.0.0.0", port=8080)
ser.start()
except KeyboardInterrupt:
print("Shutting down...")
exit(0)