I spend a week with my code, I try to make a proxy capable of handling http and https requests but I can't do it with https. Here is my server code :
Server.py
import socket
import threading
import time
import ssl
class Proxy:
def __init__(self):
self.blocked_urls = []
self.cache = {}
self.cache_expiry = {} # Store timestamp for cache expiry
self.cache_lock = threading.Lock()
self.lock = threading.Lock()
def handle_client(self, client_socket):
# Receive data from the client
request = client_socket.recv(1024)
start_time = time.time() # Start timer
if request.startswith(b'CONNECT'):
self.handle_https(client_socket, request)
else:
self.handle_http(client_socket, request)
end_time = time.time() # End timer
duration = end_time - start_time
print("[*] Request duration: {:.6f} seconds".format(duration))
def handle_http(self, client_socket, request):
# Extract domain from the HTTP request
lines = request.split(b'\r\n')
host_header = next((line for line in lines if line.startswith(b'Host:')), None)
if host_header:
domain = host_header.split(b' ')[1].decode()
else:
return
if domain in self.blocked_urls:
print("[*] URL Blocked: {}".format(domain))
client_socket.close()
return
# Check if the response is in cache and not expired
with self.cache_lock:
if domain in self.cache and time.time() < self.cache_expiry[domain]:
print("[*] Serving from cache: {}".format(domain))
response = self.cache[domain]
else:
# Connect to the remote server specified in the HTTP request
remote_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
remote_socket.connect((domain, 80))
# Send the HTTP request to the remote server
remote_socket.send(request)
# Receive the response from the remote server
start_time = time.time() # Start timer for response time
response = remote_socket.recv(4096)
end_time = time.time() # End timer for response time
duration = end_time - start_time
print("[*] Response received in {:.6f} seconds".format(duration))
# Cache the response
self.cache[domain] = response
# Set expiry time to 60 seconds
self.cache_expiry[domain] = time.time() + 60
# Close the remote socket
remote_socket.close()
# Calculate bandwidth used
bandwidth = len(response) / duration # Bytes per second
print("[*] Bandwidth used: {:.2f} bytes/second".format(bandwidth))
# Send the response back to the client
client_socket.send(response)
# Close client socket
client_socket.close()
def handle_https(self, client_socket, request):
# Extract domain from the HTTP request
lines = request.split(b'\r\n')
host_header = next((line for line in lines if line.startswith(b'Host:')), None)
if host_header:
domain = host_header.split(b' ')[1].decode()
else:
return
if domain in self.blocked_urls:
print("[*] URL Blocked: {}".format(domain))
client_socket.close()
return
domain_without_port = domain.replace(":443", "")
# Connect to the remote server specified in the HTTPS request
remote_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
remote_socket.connect((domain_without_port, 443)) # HTTPS typically uses port 443
# Wrap the socket with SSL context
ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE
remote_ssl_socket = ssl_context.wrap_socket(remote_socket, server_hostname=domain)
# Send the CONNECT request to the remote server
remote_ssl_socket.send(request)
# Receive the response from the remote server
response = remote_ssl_socket.recv(4096)
# Send the response back to the client
client_socket.send(response)
# Relay data between client and server
while True:
data = client_socket.recv(4096)
if not data:
break
remote_ssl_socket.send(data)
response = remote_ssl_socket.recv(4096)
if not response:
break
client_socket.send(response)
# Close the sockets
remote_ssl_socket.close()
client_socket.close()
def proxy_server(self, bind_ip, bind_port):
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((bind_ip, bind_port))
server.listen(5)
print("[*] Listening on {}:{}".format(bind_ip, bind_port))
print("[*] List of commands :")
print(" -b 'URLadresse' => block URL")
print(" -u 'URLadresse' => unblock URL")
while True:
client, addr = server.accept()
print("[*] Accepted connection from: {}:{}".format(addr[0], addr[1]))
# Create a thread to handle the client
client_handler = threading.Thread(target=self.handle_client, args=(client,))
client_handler.start()
def user_input_thread(self):
while True:
user_input = input()
user_input_split = user_input.split(" ")
if user_input_split and '-b' == user_input_split[0]:
with self.lock:
if len(user_input_split) > 1:
self.blocked_urls.append(user_input_split[1])
print("* Adresse ajoutée à la liste des URL bloquées.")
else:
print("* Veuillez spécifier une URL à bloquer.")
elif user_input_split and '-u' == user_input_split[0]:
with self.lock:
if len(user_input_split) > 1:
if user_input_split[1] in self.blocked_urls:
self.blocked_urls.remove(user_input_split[1])
print("* Adresse retirée de la liste des URL bloquées.")
else:
print("* L'adresse spécifiée n'est pas dans la liste des URL bloquées.")
else:
print("* Veuillez spécifier une URL à débloquer.")
else:
print("* Commande inconnue")
def run(self):
bind_ip = '0.0.0.0' # Listen on all network interfaces
bind_port = 8081 # Listening port
self.proxy_server(bind_ip, bind_port)
if __name__ == "__main__":
proxy = Proxy()
threading.Thread(target=proxy.user_input_thread).start() # Start the user input thread
proxy.run()
I'm testing with this client code:
import socket
def test_handle_https(proxy_host, proxy_port):
# Create a socket connection to the proxy server
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((proxy_host, proxy_port))
# Send a sample HTTPS request using the CONNECT method
domain = "www.example.com"
port = 443
https_request = f'CONNECT {domain}:{port} HTTP/1.1\r\nHost: {domain}:{port}\r\n\r\n'
client_socket.sendall(https_request.encode())
# Receive and print the response
response = client_socket.recv(4096)
print(response.decode())
# Close the client socket
client_socket.close()
if __name__ == "__main__":
proxy_host = '127.0.0.1' # Proxy server IP address
proxy_port = 8081 # Proxy server port
test_handle_https(proxy_host, proxy_port)
but I've this answer on my client side :
HTTP/1.1 400 Bad Request
Content-Type: text/html
Content-Length: 349
Connection: close
Date: Sat, 02 Mar 2024 15:17:47 GMT
Server: ECSF (nyd/D119)
I don't know what to do, I don't undestand where the error comes from