nopasaran 0.2.96__tar.gz → 0.2.98__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nopasaran-0.2.96 → nopasaran-0.2.98}/PKG-INFO +1 -1
- nopasaran-0.2.98/nopasaran/tools/http_1_socket_server.py +148 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/tools/https_1_socket_server.py +41 -75
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran.egg-info/PKG-INFO +1 -1
- {nopasaran-0.2.96 → nopasaran-0.2.98}/setup.py +1 -1
- nopasaran-0.2.96/nopasaran/tools/http_1_socket_server.py +0 -214
- {nopasaran-0.2.96 → nopasaran-0.2.98}/LICENSE +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/README.md +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/__init__.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/__main__.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/controllers/__init__.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/controllers/controller.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/controllers/factory.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/controllers/protocol.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/decorators.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/definitions/__init__.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/definitions/commands.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/definitions/control_channel.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/definitions/events.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/definitions/transitions.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/errors/__init__.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/errors/parsing_error.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/http_2_utils.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/interpreters/__init__.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/interpreters/action_interpreter.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/interpreters/condition_interpreter.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/interpreters/interpreter.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/interpreters/transition_interpreter.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/machines/__init__.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/machines/action_queue.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/machines/state_machine.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/parsers/__init__.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/parsers/interpreter_parser.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/parsers/state_machine_parser.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/__init__.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/__init__.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/action_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/certificate_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/client_echo_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/control_channel_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/data_channel_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/data_manipulation.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/dns_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/event_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/http_1_request_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/http_1_response_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/http_2_client_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/http_2_server_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/http_simple_client_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/https_1_request_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/https_1_response_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/icmp_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/io_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/ip_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/nested_machine_utils.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/probing_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/replay_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/server_echo_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/signaling_primitive.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/tcp_dns_request_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/tcp_dns_response_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/tcp_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/timing_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/tls_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/udp_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/condition_primitives/__init__.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/condition_primitives/condition_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/condition_primitives/variable_comparisons.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/transition_primitives/__init__.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/transition_primitives/assignment_transitions.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/transition_primitives/transition_primitives.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/sniffers/__init__.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/sniffers/sniffer.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/tools/__init__.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/tools/checks.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/tools/echo_socket_server.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/tools/http_2_overwrite.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/tools/http_2_socket_base.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/tools/http_2_socket_client.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/tools/http_2_socket_server.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/tools/tcp_dns_socket_server.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/utils.py +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran.egg-info/SOURCES.txt +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran.egg-info/dependency_links.txt +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran.egg-info/entry_points.txt +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran.egg-info/requires.txt +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran.egg-info/top_level.txt +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/setup.cfg +0 -0
- {nopasaran-0.2.96 → nopasaran-0.2.98}/tests/__init__.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: nopasaran
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.98
|
4
4
|
Summary: NoPASARAN is an advanced network tool designed to detect, fingerprint, and locate network middleboxes in a unified framework.
|
5
5
|
Home-page: https://github.com/BenIlies/NoPASARAN
|
6
6
|
Author: Ilies Benhabbour
|
@@ -0,0 +1,148 @@
|
|
1
|
+
import socket
|
2
|
+
import select
|
3
|
+
import threading
|
4
|
+
import time
|
5
|
+
from nopasaran.definitions.events import EventNames
|
6
|
+
|
7
|
+
class HTTP1SocketServer:
|
8
|
+
"""
|
9
|
+
A simple HTTP/1.1 server using sockets.
|
10
|
+
"""
|
11
|
+
|
12
|
+
def __init__(self):
|
13
|
+
self.routes = {}
|
14
|
+
self.request_received = None
|
15
|
+
self.received_request_data = None
|
16
|
+
self.sock = None
|
17
|
+
self.client_socket = None
|
18
|
+
self.TIMEOUT = 5.0
|
19
|
+
|
20
|
+
def handle_client_connection(self, client_socket, timeout):
|
21
|
+
"""
|
22
|
+
Handle a client connection by processing the incoming request and sending the appropriate response.
|
23
|
+
"""
|
24
|
+
client_socket.setblocking(False)
|
25
|
+
ready_to_read, _, _ = select.select([client_socket], [], [], timeout)
|
26
|
+
|
27
|
+
if not ready_to_read:
|
28
|
+
client_socket.close()
|
29
|
+
return None, EventNames.TIMEOUT.name
|
30
|
+
|
31
|
+
try:
|
32
|
+
request = client_socket.recv(4096)
|
33
|
+
except socket.timeout:
|
34
|
+
client_socket.close()
|
35
|
+
return None, EventNames.TIMEOUT.name
|
36
|
+
except ConnectionResetError as e:
|
37
|
+
client_socket.close()
|
38
|
+
return str(e), EventNames.ERROR.name
|
39
|
+
|
40
|
+
if not request:
|
41
|
+
client_socket.close()
|
42
|
+
return None, EventNames.TIMEOUT.name
|
43
|
+
|
44
|
+
request_str = request.decode('utf-8', errors='ignore')
|
45
|
+
|
46
|
+
# Extract request line and headers
|
47
|
+
headers_end_index = request_str.find("\r\n\r\n")
|
48
|
+
headers_part = request_str[:headers_end_index] if headers_end_index != -1 else request_str
|
49
|
+
request_line = headers_part.split("\r\n")[0]
|
50
|
+
|
51
|
+
try:
|
52
|
+
method, path, _ = request_line.split(" ", 2)
|
53
|
+
except ValueError:
|
54
|
+
method, path = "GET", "/"
|
55
|
+
|
56
|
+
route_key = (path, method)
|
57
|
+
route_info_list = self.routes.get(route_key)
|
58
|
+
|
59
|
+
if route_info_list:
|
60
|
+
response = ""
|
61
|
+
for route_info in route_info_list:
|
62
|
+
response_body = route_info.get('body', '')
|
63
|
+
status_code = route_info.get('status', 200)
|
64
|
+
headers = route_info.get('headers', [])
|
65
|
+
|
66
|
+
response_part = f"HTTP/1.1 {status_code} OK\r\n"
|
67
|
+
for header_name, header_value in headers:
|
68
|
+
response_part += f"{header_name}: {header_value}\r\n"
|
69
|
+
response_part += f"\r\n{response_body}\r\n\r\n"
|
70
|
+
|
71
|
+
response += response_part
|
72
|
+
|
73
|
+
client_socket.sendall(response.encode())
|
74
|
+
else:
|
75
|
+
response_body = 'NoPASARAN HTTP/1.1 Server'
|
76
|
+
status_code = 404
|
77
|
+
response = f"HTTP/1.1 {status_code} Not Found\r\nContent-Length: {len(response_body)}\r\n\r\n{response_body}"
|
78
|
+
client_socket.sendall(response.encode())
|
79
|
+
|
80
|
+
client_socket.close()
|
81
|
+
|
82
|
+
self.received_request_data = request
|
83
|
+
|
84
|
+
if self.request_received:
|
85
|
+
with self.request_received:
|
86
|
+
self.request_received.notify_all()
|
87
|
+
|
88
|
+
return request, EventNames.REQUEST_RECEIVED.name
|
89
|
+
|
90
|
+
def wait_for_request(self, port, timeout):
|
91
|
+
"""
|
92
|
+
Wait for an HTTP request or timeout.
|
93
|
+
|
94
|
+
Args:
|
95
|
+
port (int): The port to run the server on.
|
96
|
+
timeout (int): The timeout duration in seconds.
|
97
|
+
|
98
|
+
Returns:
|
99
|
+
Tuple[bytes or None, str]: The raw received request data or None if a timeout occurs, and the event name.
|
100
|
+
"""
|
101
|
+
server_address = ('', port)
|
102
|
+
self.request_received = threading.Condition()
|
103
|
+
self.received_request_data = None
|
104
|
+
|
105
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket:
|
106
|
+
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
107
|
+
server_socket.bind(server_address)
|
108
|
+
server_socket.listen(1)
|
109
|
+
server_socket.setblocking(False)
|
110
|
+
|
111
|
+
start_time = time.time()
|
112
|
+
|
113
|
+
while True:
|
114
|
+
elapsed_time = time.time() - start_time
|
115
|
+
if elapsed_time >= timeout:
|
116
|
+
return None, EventNames.TIMEOUT.name
|
117
|
+
|
118
|
+
ready_to_read, _, _ = select.select([server_socket], [], [], timeout - elapsed_time)
|
119
|
+
|
120
|
+
if ready_to_read:
|
121
|
+
client_socket, _ = server_socket.accept()
|
122
|
+
return self.handle_client_connection(client_socket, timeout - elapsed_time)
|
123
|
+
|
124
|
+
def start(self, host, port):
|
125
|
+
"""
|
126
|
+
Start the HTTP/1.1 server to receive HTTP/1.1 requests.
|
127
|
+
"""
|
128
|
+
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
129
|
+
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
130
|
+
self.sock.bind((host, port))
|
131
|
+
self.sock.listen(5)
|
132
|
+
|
133
|
+
return EventNames.SERVER_STARTED.name, f"Server successfully started at {host}:{port}."
|
134
|
+
|
135
|
+
def close(self):
|
136
|
+
"""Close the HTTP/1.1 connection and clean up resources"""
|
137
|
+
try:
|
138
|
+
if self.client_socket:
|
139
|
+
self.client_socket.close()
|
140
|
+
if self.sock:
|
141
|
+
self.sock.close()
|
142
|
+
|
143
|
+
self.client_socket = None
|
144
|
+
self.sock = None
|
145
|
+
|
146
|
+
return EventNames.CONNECTION_ENDING.name
|
147
|
+
except Exception as e:
|
148
|
+
return EventNames.CONNECTION_ENDING.name
|
@@ -30,13 +30,10 @@ class HTTPS1SocketServer:
|
|
30
30
|
def generate_and_load_cert(self, identifier):
|
31
31
|
cert_path, key_path = self.generate_self_signed_cert(identifier)
|
32
32
|
|
33
|
-
# Check if paths are valid
|
34
33
|
if not cert_path or not os.path.exists(cert_path):
|
35
34
|
raise FileNotFoundError(f"[HTTPS1SocketServer] Certificate file not found at {cert_path}")
|
36
35
|
if not key_path or not os.path.exists(key_path):
|
37
36
|
raise FileNotFoundError(f"[HTTPS1SocketServer] Key file not found at {key_path}")
|
38
|
-
|
39
|
-
# Optional: Ensure they're not empty (some systems fail silently on write)
|
40
37
|
if os.path.getsize(cert_path) == 0:
|
41
38
|
raise ValueError(f"[HTTPS1SocketServer] Certificate file is empty at {cert_path}")
|
42
39
|
if os.path.getsize(key_path) == 0:
|
@@ -50,7 +47,6 @@ class HTTPS1SocketServer:
|
|
50
47
|
self._cert_path = cert_path
|
51
48
|
self._key_path = key_path
|
52
49
|
|
53
|
-
|
54
50
|
def generate_self_signed_cert(self, identifier):
|
55
51
|
key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
56
52
|
|
@@ -60,11 +56,10 @@ class HTTPS1SocketServer:
|
|
60
56
|
|
61
57
|
alt_names = [x509.DNSName(identifier)]
|
62
58
|
try:
|
63
|
-
# Try to interpret as IP address
|
64
59
|
import ipaddress
|
65
60
|
alt_names.append(x509.IPAddress(ipaddress.ip_address(identifier)))
|
66
61
|
except ValueError:
|
67
|
-
pass
|
62
|
+
pass
|
68
63
|
|
69
64
|
cert = (
|
70
65
|
x509.CertificateBuilder()
|
@@ -102,16 +97,18 @@ class HTTPS1SocketServer:
|
|
102
97
|
self.sock.bind((host, port))
|
103
98
|
self.sock.listen(5)
|
104
99
|
return EventNames.SERVER_STARTED.name, f"HTTPS server started at {host}:{port}"
|
105
|
-
|
106
|
-
|
107
|
-
|
108
100
|
|
109
101
|
def handle_client_connection(self, tls_socket):
|
110
|
-
|
102
|
+
try:
|
103
|
+
request = tls_socket.recv(4096)
|
104
|
+
if not request:
|
105
|
+
return # Client closed connection or sent nothing
|
106
|
+
except socket.timeout:
|
107
|
+
return # Timed out waiting for client data
|
108
|
+
|
111
109
|
request_str = request.decode("utf-8", errors="ignore")
|
112
110
|
self.received_request_data = request
|
113
111
|
|
114
|
-
# Parse request line
|
115
112
|
headers_end_index = request_str.find("\r\n\r\n")
|
116
113
|
headers_part = request_str[:headers_end_index] if headers_end_index != -1 else request_str
|
117
114
|
request_line = headers_part.split("\r\n")[0]
|
@@ -147,51 +144,12 @@ class HTTPS1SocketServer:
|
|
147
144
|
with self.request_received:
|
148
145
|
self.request_received.notify_all()
|
149
146
|
|
150
|
-
def receive_test_frames(self):
|
151
|
-
if not self.sock:
|
152
|
-
return EventNames.ERROR.name, "Server not started", None
|
153
|
-
|
154
|
-
requests_received = []
|
155
|
-
start_time = time.time()
|
156
|
-
self.sock.setblocking(False)
|
157
|
-
|
158
|
-
while True:
|
159
|
-
if time.time() - start_time > self.TIMEOUT:
|
160
|
-
if not requests_received:
|
161
|
-
return EventNames.TIMEOUT.name, "Timeout with no request", None
|
162
|
-
break
|
163
|
-
|
164
|
-
try:
|
165
|
-
ready_to_read, _, _ = select.select([self.sock], [], [], 0.5)
|
166
|
-
if ready_to_read:
|
167
|
-
client_sock, _ = self.sock.accept()
|
168
|
-
tls_sock = self.context.wrap_socket(client_sock, server_side=True)
|
169
|
-
tls_sock.settimeout(1.0)
|
170
|
-
try:
|
171
|
-
self.handle_client_connection(tls_sock)
|
172
|
-
req_str = self.received_request_data.decode("utf-8", errors="ignore")
|
173
|
-
requests_received.append(req_str)
|
174
|
-
except Exception as e:
|
175
|
-
return EventNames.ERROR.name, f"TLS error: {e}", None
|
176
|
-
except Exception as e:
|
177
|
-
return EventNames.ERROR.name, str(e), None
|
178
|
-
|
179
|
-
return EventNames.RECEIVED_REQUESTS.name, f"Received {len(requests_received)} HTTPS requests.", str(requests_received)
|
180
|
-
|
181
147
|
def wait_for_request(self, port, timeout):
|
182
148
|
"""
|
183
149
|
Wait for a single HTTPS request with TLS handshake and user-defined timeout.
|
184
|
-
|
185
|
-
Args:
|
186
|
-
port (int): The port to bind and listen on.
|
187
|
-
timeout (int): Timeout duration in seconds.
|
188
|
-
|
189
|
-
Returns:
|
190
|
-
Tuple[bytes or None, str]: The raw request data (or None if timed out) and an event name.
|
191
150
|
"""
|
192
151
|
self.received_request_data = None
|
193
|
-
self.request_received = None
|
194
|
-
|
152
|
+
self.request_received = None
|
195
153
|
context = self.context
|
196
154
|
start_time = time.time()
|
197
155
|
|
@@ -207,36 +165,44 @@ class HTTPS1SocketServer:
|
|
207
165
|
return None, EventNames.TIMEOUT.name
|
208
166
|
|
209
167
|
try:
|
210
|
-
|
168
|
+
remaining = timeout - elapsed
|
169
|
+
ready, _, _ = select.select([server_socket], [], [], remaining)
|
211
170
|
if ready:
|
212
171
|
client_sock, _ = server_socket.accept()
|
213
|
-
|
214
|
-
|
215
|
-
|
172
|
+
client_sock.settimeout(2.0) # Important: Timeout for TLS handshake and recv
|
173
|
+
try:
|
174
|
+
with context.wrap_socket(client_sock, server_side=True) as tls_sock:
|
175
|
+
tls_sock.settimeout(2.0) # Also set after TLS handshake
|
216
176
|
self.handle_client_connection(tls_sock)
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
177
|
+
if self.received_request_data:
|
178
|
+
return self.received_request_data, EventNames.REQUEST_RECEIVED.name
|
179
|
+
else:
|
180
|
+
return None, EventNames.TIMEOUT.name
|
181
|
+
except (ssl.SSLError, socket.timeout):
|
182
|
+
return None, EventNames.TIMEOUT.name
|
183
|
+
except Exception:
|
184
|
+
return None, EventNames.ERROR.name
|
185
|
+
except Exception:
|
186
|
+
return None, EventNames.ERROR.name
|
222
187
|
|
223
188
|
def close(self):
|
224
|
-
if self.client_socket:
|
225
|
-
self.client_socket.close()
|
226
|
-
if self.sock:
|
227
|
-
self.sock.close()
|
228
|
-
self.client_socket = None
|
229
|
-
self.sock = None
|
230
|
-
|
231
189
|
try:
|
232
|
-
if self.
|
233
|
-
|
234
|
-
if self.
|
235
|
-
|
236
|
-
except Exception as e:
|
237
|
-
return EventNames.ERROR.name, f"Certificate cleanup error: {str(e)}"
|
190
|
+
if self.client_socket:
|
191
|
+
self.client_socket.close()
|
192
|
+
if self.sock:
|
193
|
+
self.sock.close()
|
238
194
|
|
239
|
-
|
195
|
+
self.client_socket = None
|
196
|
+
self.sock = None
|
240
197
|
|
241
|
-
|
198
|
+
try:
|
199
|
+
if self._cert_path and os.path.exists(self._cert_path):
|
200
|
+
os.remove(self._cert_path)
|
201
|
+
if self._key_path and os.path.exists(self._key_path):
|
202
|
+
os.remove(self._key_path)
|
203
|
+
except Exception as e:
|
204
|
+
return EventNames.ERROR.name, f"Certificate cleanup error: {str(e)}"
|
242
205
|
|
206
|
+
return EventNames.CONNECTION_ENDING.name
|
207
|
+
except Exception:
|
208
|
+
return EventNames.CONNECTION_ENDING.name
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: nopasaran
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.98
|
4
4
|
Summary: NoPASARAN is an advanced network tool designed to detect, fingerprint, and locate network middleboxes in a unified framework.
|
5
5
|
Home-page: https://github.com/BenIlies/NoPASARAN
|
6
6
|
Author: Ilies Benhabbour
|
@@ -13,7 +13,7 @@ with open(requirements_file, "r") as f:
|
|
13
13
|
# Version will automatically be updated when pushed on the main branch
|
14
14
|
setup(
|
15
15
|
name="nopasaran",
|
16
|
-
version='0.2.
|
16
|
+
version='0.2.98',
|
17
17
|
author="Ilies Benhabbour",
|
18
18
|
author_email="ilies.benhabbour@kaust.edu.sa",
|
19
19
|
description="NoPASARAN is an advanced network tool designed to detect, fingerprint, and locate network middleboxes in a unified framework.",
|
@@ -1,214 +0,0 @@
|
|
1
|
-
import socket
|
2
|
-
import select
|
3
|
-
import threading
|
4
|
-
import time
|
5
|
-
from nopasaran.definitions.events import EventNames
|
6
|
-
|
7
|
-
class HTTP1SocketServer:
|
8
|
-
"""
|
9
|
-
A simple HTTP/1.1 server using sockets.
|
10
|
-
"""
|
11
|
-
|
12
|
-
def __init__(self):
|
13
|
-
self.routes = {}
|
14
|
-
self.request_received = None
|
15
|
-
self.received_request_data = None
|
16
|
-
self.sock = None
|
17
|
-
self.client_socket = None
|
18
|
-
self.TIMEOUT = 5.0
|
19
|
-
|
20
|
-
def handle_client_connection(self, client_socket):
|
21
|
-
"""
|
22
|
-
Handle a client connection by processing the incoming request and sending the appropriate response.
|
23
|
-
"""
|
24
|
-
request = client_socket.recv(4096)
|
25
|
-
request_str = request.decode('utf-8')
|
26
|
-
# Extract request line and headers
|
27
|
-
headers_end_index = request_str.find("\r\n\r\n")
|
28
|
-
headers_part = request_str[:headers_end_index] if headers_end_index != -1 else request_str
|
29
|
-
request_line = headers_part.split("\r\n")[0]
|
30
|
-
method, path, _ = request_line.split(" ", 2)
|
31
|
-
|
32
|
-
route_key = (path, method)
|
33
|
-
route_info_list = self.routes.get(route_key)
|
34
|
-
|
35
|
-
if route_info_list:
|
36
|
-
response = ""
|
37
|
-
for route_info in route_info_list:
|
38
|
-
response_body = route_info.get('body', '')
|
39
|
-
status_code = route_info.get('status', 200)
|
40
|
-
headers = route_info.get('headers', [])
|
41
|
-
|
42
|
-
# Construct each part of the response
|
43
|
-
response_part = f"HTTP/1.1 {status_code} OK\r\n"
|
44
|
-
for header_name, header_value in headers:
|
45
|
-
response_part += f"{header_name}: {header_value}\r\n"
|
46
|
-
response_part += f"\r\n{response_body}\r\n\r\n" # Double CRLF to separate responses
|
47
|
-
|
48
|
-
# Append the response part to the full response
|
49
|
-
response += response_part
|
50
|
-
|
51
|
-
# Send the full combined response to the client
|
52
|
-
client_socket.sendall(response.encode())
|
53
|
-
|
54
|
-
else:
|
55
|
-
response_body = 'NoPASARAN HTTP/1.1 Server'
|
56
|
-
status_code = 404
|
57
|
-
headers = []
|
58
|
-
|
59
|
-
# Construct the HTTP response
|
60
|
-
response = f"HTTP/1.1 {status_code} OK\r\n"
|
61
|
-
for header_name, header_value in headers:
|
62
|
-
response += f"{header_name}: {header_value}\r\n"
|
63
|
-
response += f"\r\n{response_body}"
|
64
|
-
|
65
|
-
# Send response to client
|
66
|
-
client_socket.sendall(response.encode())
|
67
|
-
|
68
|
-
client_socket.close()
|
69
|
-
|
70
|
-
# Store the raw received request data
|
71
|
-
self.received_request_data = request
|
72
|
-
|
73
|
-
# Notify that a request has been received
|
74
|
-
if self.request_received:
|
75
|
-
with self.request_received:
|
76
|
-
self.request_received.notify_all()
|
77
|
-
|
78
|
-
def wait_for_request(self, port, timeout):
|
79
|
-
"""
|
80
|
-
Wait for an HTTP request or timeout.
|
81
|
-
|
82
|
-
Args:
|
83
|
-
port (int): The port to run the server on.
|
84
|
-
timeout (int): The timeout duration in seconds.
|
85
|
-
|
86
|
-
Returns:
|
87
|
-
Tuple[bytes, str]: The raw received request data or None if a timeout occurs, and the event name.
|
88
|
-
"""
|
89
|
-
server_address = ('', port)
|
90
|
-
self.request_received = threading.Condition()
|
91
|
-
self.received_request_data = None
|
92
|
-
|
93
|
-
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket:
|
94
|
-
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
95
|
-
server_socket.bind(server_address)
|
96
|
-
server_socket.listen(1)
|
97
|
-
server_socket.setblocking(False)
|
98
|
-
|
99
|
-
# Initialize the timeout timer
|
100
|
-
start_time = time.time()
|
101
|
-
|
102
|
-
while True:
|
103
|
-
# Check if the timeout has elapsed
|
104
|
-
elapsed_time = time.time() - start_time
|
105
|
-
if elapsed_time > timeout:
|
106
|
-
return None, EventNames.TIMEOUT.name
|
107
|
-
|
108
|
-
# Use select to wait for a connection with a timeout
|
109
|
-
ready_to_read, _, _ = select.select([server_socket], [], [], timeout - elapsed_time)
|
110
|
-
|
111
|
-
if ready_to_read:
|
112
|
-
client_socket, _ = server_socket.accept()
|
113
|
-
self.handle_client_connection(client_socket)
|
114
|
-
return self.received_request_data, EventNames.REQUEST_RECEIVED.name
|
115
|
-
|
116
|
-
def start(self, host, port):
|
117
|
-
"""
|
118
|
-
Start the HTTP/1.1 server to receive HTTP/1.1 requests.
|
119
|
-
"""
|
120
|
-
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
121
|
-
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
122
|
-
self.sock.bind((host, port))
|
123
|
-
self.sock.listen(5)
|
124
|
-
|
125
|
-
return EventNames.SERVER_STARTED.name, f"Server successfully started at {host}:{port}."
|
126
|
-
|
127
|
-
def receive_test_frames(self):
|
128
|
-
"""
|
129
|
-
Listen for HTTP/1.1 requests until a timeout occurs, then process all received requests.
|
130
|
-
|
131
|
-
Returns:
|
132
|
-
Tuple[str, str, str]: (event_name, message, received_data)
|
133
|
-
- event_name: The type of event that occurred (from EventNames)
|
134
|
-
- TIMEOUT: Timeout occurred before all requests were received
|
135
|
-
- REJECTED: Received a 4xx or 5xx status code
|
136
|
-
- RECEIVED_REQUESTS: All requests have been received
|
137
|
-
- ERROR: Error occurred while receiving requests
|
138
|
-
- message: A descriptive message about what happened
|
139
|
-
- received_data: String representation of the received requests or None
|
140
|
-
"""
|
141
|
-
if not self.sock:
|
142
|
-
return EventNames.ERROR.name, "Server not started", None
|
143
|
-
|
144
|
-
requests_received = []
|
145
|
-
start_time = time.time()
|
146
|
-
|
147
|
-
# Make the server socket non-blocking for accepting connections
|
148
|
-
self.sock.setblocking(False)
|
149
|
-
|
150
|
-
# Keep accepting connections until we hit a timeout
|
151
|
-
while True:
|
152
|
-
# Check for overall timeout
|
153
|
-
elapsed_time = time.time() - start_time
|
154
|
-
if elapsed_time > self.TIMEOUT:
|
155
|
-
if len(requests_received) == 0:
|
156
|
-
return EventNames.TIMEOUT.name, f"Timeout after {self.TIMEOUT}s and received no requests.", None
|
157
|
-
else:
|
158
|
-
break # We've received some requests, so consider it done
|
159
|
-
|
160
|
-
try:
|
161
|
-
# Try to accept a new connection
|
162
|
-
ready_to_read, _, _ = select.select([self.sock], [], [], 0.5)
|
163
|
-
|
164
|
-
if ready_to_read:
|
165
|
-
client_socket, _ = self.sock.accept()
|
166
|
-
client_socket.settimeout(0.5)
|
167
|
-
|
168
|
-
try:
|
169
|
-
data = client_socket.recv(4096)
|
170
|
-
|
171
|
-
if data:
|
172
|
-
# Parse the HTTP/1.1 request
|
173
|
-
request_str = data.decode('utf-8', errors='ignore')
|
174
|
-
requests_received.append(request_str)
|
175
|
-
|
176
|
-
# Check for error status codes
|
177
|
-
if "HTTP/1.1 5" in request_str or "HTTP/1.1 4" in request_str:
|
178
|
-
try:
|
179
|
-
status_line = request_str.split("\r\n")[0]
|
180
|
-
status_code = status_line.split(" ")[1]
|
181
|
-
client_socket.close()
|
182
|
-
return EventNames.REJECTED.name, f"Received {status_code} status code.", request_str
|
183
|
-
except (IndexError, ValueError):
|
184
|
-
client_socket.close()
|
185
|
-
return EventNames.REJECTED.name, "Received 4xx or 5xx status code.", request_str
|
186
|
-
finally:
|
187
|
-
client_socket.close()
|
188
|
-
except socket.timeout:
|
189
|
-
continue
|
190
|
-
except Exception as e:
|
191
|
-
return EventNames.ERROR.name, f"Error while receiving requests: {str(e)}", \
|
192
|
-
str(requests_received) if requests_received else None
|
193
|
-
|
194
|
-
# If we get here, we've either collected all requests or hit the timeout
|
195
|
-
if len(requests_received) == 0:
|
196
|
-
return EventNames.TIMEOUT.name, "No requests received before timeout.", None
|
197
|
-
|
198
|
-
return EventNames.RECEIVED_REQUESTS.name, f"Received {len(requests_received)} HTTP/1.1 requests.", str(requests_received)
|
199
|
-
|
200
|
-
def close(self):
|
201
|
-
"""Close the HTTP/1.1 connection and clean up resources"""
|
202
|
-
try:
|
203
|
-
if self.client_socket:
|
204
|
-
self.client_socket.close()
|
205
|
-
if self.sock:
|
206
|
-
self.sock.close()
|
207
|
-
|
208
|
-
# Clear references
|
209
|
-
self.client_socket = None
|
210
|
-
self.sock = None
|
211
|
-
|
212
|
-
return EventNames.CONNECTION_CLOSED.name
|
213
|
-
except Exception as e:
|
214
|
-
return EventNames.CONNECTION_CLOSED.name
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/action_primitives.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/data_manipulation.py
RENAMED
File without changes
|
{nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/dns_primitives.py
RENAMED
File without changes
|
{nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/event_primitives.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/icmp_primitives.py
RENAMED
File without changes
|
{nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/io_primitives.py
RENAMED
File without changes
|
{nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/ip_primitives.py
RENAMED
File without changes
|
{nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/nested_machine_utils.py
RENAMED
File without changes
|
{nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/probing_primitives.py
RENAMED
File without changes
|
{nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/replay_primitives.py
RENAMED
File without changes
|
File without changes
|
{nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/signaling_primitive.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/tcp_primitives.py
RENAMED
File without changes
|
{nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/timing_primitives.py
RENAMED
File without changes
|
{nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/tls_primitives.py
RENAMED
File without changes
|
{nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/udp_primitives.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/transition_primitives/__init__.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|