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
|
|
|
|
2023-12-06 20:44:29 +00:00
|
|
|
CONNECTION_TIMEOUT_SEC = 10
|
2023-12-06 20:15:01 +00:00
|
|
|
BUFFER_SIZE = 32 * 1024
|
2023-11-17 00:28:37 +00:00
|
|
|
CURRENT_THREADS = 0
|
2023-12-06 20:22:04 +00:00
|
|
|
MAX_THREADS = 50
|
2023-11-17 00:28:37 +00:00
|
|
|
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):
|
2023-11-17 00:51:08 +00:00
|
|
|
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
|
|
|
|
2023-12-06 20:00:05 +00:00
|
|
|
def info(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)
|
2023-12-06 20:15:01 +00:00
|
|
|
print(f"Proxy executando em: http://{host}:{port}")
|
2023-11-17 00:28:37 +00:00
|
|
|
|
|
|
|
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
|
2023-12-06 20:44:29 +00:00
|
|
|
client_connection = ClientConnection(conn, client_addr)
|
|
|
|
thread = Thread(target = client_connection.handle_connection)
|
2023-11-17 00:28:37 +00:00
|
|
|
CURRENT_THREADS -= 1
|
|
|
|
thread.start()
|
|
|
|
|
|
|
|
def __del__(self):
|
|
|
|
self.sock.close()
|
|
|
|
|
2023-12-06 20:44:29 +00:00
|
|
|
class ClientConnection:
|
|
|
|
def __init__(self, client_socket, client_address):
|
|
|
|
self.client_socket = client_socket
|
|
|
|
self.client_addr = client_address[0]
|
|
|
|
self.client_port = client_address[1]
|
2023-11-17 00:28:37 +00:00
|
|
|
|
2023-12-06 20:44:29 +00:00
|
|
|
self.server_socket = None
|
2023-11-17 00:28:37 +00:00
|
|
|
|
2023-12-06 20:44:29 +00:00
|
|
|
def check_monitorando(self, request):
|
|
|
|
if b"monitorando" in request:
|
|
|
|
body = open('forbidden.html', 'r').read()
|
|
|
|
self.client_socket.sendall(b"HTTP/1.1 403 Forbidden\r\n\r\n")
|
|
|
|
self.client_socket.sendall(body.encode())
|
|
|
|
self.client_socket.close()
|
|
|
|
return True
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
def get_host_port(self, request):
|
|
|
|
request_url = request.split(' ')[1]
|
2023-12-06 20:22:04 +00:00
|
|
|
request_host = ""
|
2023-12-06 20:44:29 +00:00
|
|
|
request_port = 443 if request_url.startswith('https') else 80
|
2023-12-06 20:22:04 +00:00
|
|
|
|
2023-12-06 20:44:29 +00:00
|
|
|
# Remove protocolo do request
|
2023-12-06 20:22:04 +00:00
|
|
|
if request_url.startswith('http'):
|
2023-12-06 20:44:29 +00:00
|
|
|
request_host = request_url.split('//')[1]
|
2023-12-06 20:22:04 +00:00
|
|
|
else:
|
|
|
|
request_host = request_url.split('/')[0]
|
|
|
|
|
2023-12-06 20:44:29 +00:00
|
|
|
# Remove 'www' do request
|
2023-12-06 20:22:04 +00:00
|
|
|
if request_host.startswith('www'):
|
2023-12-06 20:44:29 +00:00
|
|
|
request_host = request_host.split('www.')[1]
|
2023-12-06 20:22:04 +00:00
|
|
|
|
2023-12-06 20:44:29 +00:00
|
|
|
# Remove porta do request e verifica se é um host com porta
|
2023-12-06 20:22:04 +00:00
|
|
|
if ':' in request_host:
|
|
|
|
request_port = int(request_host.split(':')[1])
|
|
|
|
request_host = request_host.split(':')[0]
|
|
|
|
|
2023-12-06 22:49:47 +00:00
|
|
|
request_host = request_host.split('/')[0]
|
|
|
|
|
2023-12-06 20:44:29 +00:00
|
|
|
return request_host, request_port
|
|
|
|
|
|
|
|
def handle_connection(self):
|
|
|
|
request = self.client_socket.recv(BUFFER_SIZE)
|
|
|
|
logger = Logger.instance()
|
|
|
|
|
|
|
|
# Verifica se o request é vazio, se for, fecha o socket
|
|
|
|
if len(request) == 0:
|
|
|
|
self.client_socket.close()
|
|
|
|
return
|
|
|
|
|
|
|
|
try:
|
|
|
|
# Tenta decodificar o request, se não conseguir, fecha o socket
|
|
|
|
raw_request = request.decode()
|
|
|
|
except UnicodeDecodeError:
|
|
|
|
self.client_socket.close()
|
2023-12-06 20:22:04 +00:00
|
|
|
return
|
|
|
|
|
2023-12-06 20:44:29 +00:00
|
|
|
# Verifica se é um request CONNECT
|
|
|
|
# Se for, retorna 200 Connection Established, e espera o request novamente
|
|
|
|
if "CONNECT" in raw_request:
|
|
|
|
self.client_socket.sendall(b"HTTP/1.1 200 Connection Established\r\n\r\n")
|
|
|
|
request = self.client_socket.recv(BUFFER_SIZE)
|
|
|
|
|
|
|
|
# Retorna host e porta do request
|
|
|
|
request_host, request_port = self.get_host_port(raw_request)
|
|
|
|
|
|
|
|
# Verifica se 'monitorando' está no request
|
|
|
|
if self.check_monitorando(request):
|
|
|
|
logger.info(f"REQUEST [{self.client_addr}:{self.client_port}] to [{request_host}:{request_port}] - 'Monitorando' - 403 Forbidden")
|
|
|
|
return
|
2023-11-17 00:28:37 +00:00
|
|
|
|
2023-12-06 20:44:29 +00:00
|
|
|
# Cria socket para o servidor
|
|
|
|
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
|
|
self.server_socket.connect((request_host, request_port))
|
|
|
|
self.server_socket.send(request)
|
|
|
|
|
|
|
|
'''
|
|
|
|
Enquanto houver dados para serem lidos, lê do socket do cliente e envia para o servidor
|
|
|
|
e lê do socket do servidor e envia para o cliente
|
|
|
|
|
|
|
|
Se não houver dados para serem lidos, fecha os sockets
|
|
|
|
|
|
|
|
Exemplo de fluxo:
|
|
|
|
proxy -> server
|
|
|
|
server -> proxy
|
|
|
|
proxy -> client
|
|
|
|
proxy -> client
|
|
|
|
proxy -> client
|
|
|
|
server -> proxy
|
|
|
|
'''
|
|
|
|
data = None
|
2023-12-06 20:22:04 +00:00
|
|
|
while True:
|
2023-12-06 20:44:29 +00:00
|
|
|
triple = select.select([self.client_socket, self.server_socket], [], [], CONNECTION_TIMEOUT_SEC)[0]
|
2023-12-06 20:22:04 +00:00
|
|
|
if not len(triple):
|
|
|
|
break
|
|
|
|
try:
|
2023-12-06 20:44:29 +00:00
|
|
|
if self.server_socket in triple:
|
|
|
|
data = self.server_socket.recv(BUFFER_SIZE)
|
2023-12-06 20:22:04 +00:00
|
|
|
if not data:
|
|
|
|
break
|
|
|
|
|
|
|
|
try:
|
|
|
|
status_code = data.decode().split('\r\n')[0].split(' ')[1:]
|
|
|
|
status_code = ' '.join(status_code)
|
|
|
|
if is_valid_status_code(status_code):
|
2023-12-06 20:44:29 +00:00
|
|
|
logger.info(f"REQUEST [{self.client_addr}:{self.client_port}] to [{request_host}:{request_port}] - {status_code}")
|
2023-12-06 20:22:04 +00:00
|
|
|
except UnicodeDecodeError:
|
2023-12-06 20:44:29 +00:00
|
|
|
logger.info(f"REQUEST [{self.client_addr}:{self.client_port}] to [{request_host}:{request_port}]")
|
2023-12-06 20:22:04 +00:00
|
|
|
pass
|
|
|
|
|
2023-12-06 20:44:29 +00:00
|
|
|
self.client_socket.send(data)
|
|
|
|
if self.client_socket in triple:
|
|
|
|
data = self.client_socket.recv(BUFFER_SIZE)
|
2023-12-06 20:22:04 +00:00
|
|
|
if not data:
|
|
|
|
break
|
|
|
|
|
2023-12-06 20:44:29 +00:00
|
|
|
self.server_socket.send(data)
|
2023-12-06 20:22:04 +00:00
|
|
|
except ConnectionAbortedError:
|
|
|
|
break
|
|
|
|
|
2023-12-06 20:44:29 +00:00
|
|
|
self.server_socket.close()
|
|
|
|
self.client_socket.close()
|
2023-11-17 00:28:37 +00:00
|
|
|
|
2023-12-06 20:22:04 +00:00
|
|
|
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
|
|
|
|
2023-12-06 20:22:04 +00:00
|
|
|
def check_code():
|
2023-11-25 02:01:18 +00:00
|
|
|
import hashlib
|
|
|
|
|
|
|
|
content = None
|
2023-12-06 20:00:05 +00:00
|
|
|
shas256_hash = None
|
2023-12-06 21:17:55 +00:00
|
|
|
logger = Logger.instance()
|
2023-11-25 02:01:18 +00:00
|
|
|
|
|
|
|
with open(sys.argv[0], 'rb') as f:
|
|
|
|
content = f.read()
|
|
|
|
|
2023-12-06 20:00:05 +00:00
|
|
|
with open('proxy.sha256sum', 'r') as f:
|
|
|
|
shas256_hash = f.read()
|
2023-11-25 02:01:18 +00:00
|
|
|
|
2023-12-06 20:00:05 +00:00
|
|
|
hash = hashlib.sha256(content).hexdigest()
|
2023-11-25 02:01:18 +00:00
|
|
|
|
2023-12-06 20:00:05 +00:00
|
|
|
if hash != shas256_hash:
|
2023-12-06 21:17:55 +00:00
|
|
|
logger.critical('ERRO: Código do proxy está diferente!')
|
2023-11-25 02:01:18 +00:00
|
|
|
exit(1)
|
|
|
|
|
2023-12-06 21:17:55 +00:00
|
|
|
logger.info('Arquivo do proxy verificado!')
|
2023-11-25 02:01:18 +00:00
|
|
|
|
2023-11-17 00:28:37 +00:00
|
|
|
if __name__ == '__main__':
|
2023-12-06 20:22:04 +00:00
|
|
|
check_code()
|
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:
|
2023-12-06 20:00:05 +00:00
|
|
|
print("Desligando proxy...")
|
2023-12-05 01:22:57 +00:00
|
|
|
exit(0)
|