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.
Files changed (90) hide show
  1. {nopasaran-0.2.96 → nopasaran-0.2.98}/PKG-INFO +1 -1
  2. nopasaran-0.2.98/nopasaran/tools/http_1_socket_server.py +148 -0
  3. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/tools/https_1_socket_server.py +41 -75
  4. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran.egg-info/PKG-INFO +1 -1
  5. {nopasaran-0.2.96 → nopasaran-0.2.98}/setup.py +1 -1
  6. nopasaran-0.2.96/nopasaran/tools/http_1_socket_server.py +0 -214
  7. {nopasaran-0.2.96 → nopasaran-0.2.98}/LICENSE +0 -0
  8. {nopasaran-0.2.96 → nopasaran-0.2.98}/README.md +0 -0
  9. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/__init__.py +0 -0
  10. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/__main__.py +0 -0
  11. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/controllers/__init__.py +0 -0
  12. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/controllers/controller.py +0 -0
  13. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/controllers/factory.py +0 -0
  14. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/controllers/protocol.py +0 -0
  15. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/decorators.py +0 -0
  16. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/definitions/__init__.py +0 -0
  17. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/definitions/commands.py +0 -0
  18. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/definitions/control_channel.py +0 -0
  19. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/definitions/events.py +0 -0
  20. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/definitions/transitions.py +0 -0
  21. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/errors/__init__.py +0 -0
  22. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/errors/parsing_error.py +0 -0
  23. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/http_2_utils.py +0 -0
  24. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/interpreters/__init__.py +0 -0
  25. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/interpreters/action_interpreter.py +0 -0
  26. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/interpreters/condition_interpreter.py +0 -0
  27. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/interpreters/interpreter.py +0 -0
  28. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/interpreters/transition_interpreter.py +0 -0
  29. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/machines/__init__.py +0 -0
  30. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/machines/action_queue.py +0 -0
  31. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/machines/state_machine.py +0 -0
  32. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/parsers/__init__.py +0 -0
  33. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/parsers/interpreter_parser.py +0 -0
  34. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/parsers/state_machine_parser.py +0 -0
  35. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/__init__.py +0 -0
  36. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/__init__.py +0 -0
  37. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/action_primitives.py +0 -0
  38. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/certificate_primitives.py +0 -0
  39. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/client_echo_primitives.py +0 -0
  40. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/control_channel_primitives.py +0 -0
  41. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/data_channel_primitives.py +0 -0
  42. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/data_manipulation.py +0 -0
  43. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/dns_primitives.py +0 -0
  44. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/event_primitives.py +0 -0
  45. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/http_1_request_primitives.py +0 -0
  46. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/http_1_response_primitives.py +0 -0
  47. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/http_2_client_primitives.py +0 -0
  48. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/http_2_server_primitives.py +0 -0
  49. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/http_simple_client_primitives.py +0 -0
  50. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/https_1_request_primitives.py +0 -0
  51. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/https_1_response_primitives.py +0 -0
  52. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/icmp_primitives.py +0 -0
  53. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/io_primitives.py +0 -0
  54. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/ip_primitives.py +0 -0
  55. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/nested_machine_utils.py +0 -0
  56. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/probing_primitives.py +0 -0
  57. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/replay_primitives.py +0 -0
  58. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/server_echo_primitives.py +0 -0
  59. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/signaling_primitive.py +0 -0
  60. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/tcp_dns_request_primitives.py +0 -0
  61. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/tcp_dns_response_primitives.py +0 -0
  62. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/tcp_primitives.py +0 -0
  63. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/timing_primitives.py +0 -0
  64. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/tls_primitives.py +0 -0
  65. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/action_primitives/udp_primitives.py +0 -0
  66. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/condition_primitives/__init__.py +0 -0
  67. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/condition_primitives/condition_primitives.py +0 -0
  68. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/condition_primitives/variable_comparisons.py +0 -0
  69. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/primitives.py +0 -0
  70. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/transition_primitives/__init__.py +0 -0
  71. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/transition_primitives/assignment_transitions.py +0 -0
  72. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/primitives/transition_primitives/transition_primitives.py +0 -0
  73. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/sniffers/__init__.py +0 -0
  74. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/sniffers/sniffer.py +0 -0
  75. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/tools/__init__.py +0 -0
  76. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/tools/checks.py +0 -0
  77. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/tools/echo_socket_server.py +0 -0
  78. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/tools/http_2_overwrite.py +0 -0
  79. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/tools/http_2_socket_base.py +0 -0
  80. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/tools/http_2_socket_client.py +0 -0
  81. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/tools/http_2_socket_server.py +0 -0
  82. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/tools/tcp_dns_socket_server.py +0 -0
  83. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran/utils.py +0 -0
  84. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran.egg-info/SOURCES.txt +0 -0
  85. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran.egg-info/dependency_links.txt +0 -0
  86. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran.egg-info/entry_points.txt +0 -0
  87. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran.egg-info/requires.txt +0 -0
  88. {nopasaran-0.2.96 → nopasaran-0.2.98}/nopasaran.egg-info/top_level.txt +0 -0
  89. {nopasaran-0.2.96 → nopasaran-0.2.98}/setup.cfg +0 -0
  90. {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.96
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 # it's not an IP, skip adding IPAddress
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
- request = tls_socket.recv(4096)
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
- ready, _, _ = select.select([server_socket], [], [], timeout - elapsed)
168
+ remaining = timeout - elapsed
169
+ ready, _, _ = select.select([server_socket], [], [], remaining)
211
170
  if ready:
212
171
  client_sock, _ = server_socket.accept()
213
- with context.wrap_socket(client_sock, server_side=True) as tls_sock:
214
- tls_sock.settimeout(1.0)
215
- try:
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
- return self.received_request_data, EventNames.REQUEST_RECEIVED.name
218
- except Exception as e:
219
- return None, EventNames.ERROR.name
220
- except Exception as e:
221
- return None, EventNames.ERROR.name
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._cert_path and os.path.exists(self._cert_path):
233
- os.remove(self._cert_path)
234
- if self._key_path and os.path.exists(self._key_path):
235
- os.remove(self._key_path)
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
- return EventNames.CONNECTION_CLOSED.name
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.96
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.96',
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