nopasaran 0.2.95__tar.gz → 0.2.97__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.95 → nopasaran-0.2.97}/PKG-INFO +1 -1
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/action_primitives.py +6 -1
- nopasaran-0.2.97/nopasaran/primitives/action_primitives/tcp_dns_request_primitives.py +29 -0
- nopasaran-0.2.97/nopasaran/primitives/action_primitives/tcp_dns_response_primitives.py +40 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/tools/http_1_socket_server.py +2 -75
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/tools/https_1_socket_server.py +41 -75
- nopasaran-0.2.97/nopasaran/tools/tcp_dns_socket_server.py +170 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/utils.py +76 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran.egg-info/PKG-INFO +1 -1
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran.egg-info/SOURCES.txt +3 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran.egg-info/requires.txt +1 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/setup.py +1 -1
- {nopasaran-0.2.95 → nopasaran-0.2.97}/LICENSE +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/README.md +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/__init__.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/__main__.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/controllers/__init__.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/controllers/controller.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/controllers/factory.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/controllers/protocol.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/decorators.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/definitions/__init__.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/definitions/commands.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/definitions/control_channel.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/definitions/events.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/definitions/transitions.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/errors/__init__.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/errors/parsing_error.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/http_2_utils.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/interpreters/__init__.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/interpreters/action_interpreter.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/interpreters/condition_interpreter.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/interpreters/interpreter.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/interpreters/transition_interpreter.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/machines/__init__.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/machines/action_queue.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/machines/state_machine.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/parsers/__init__.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/parsers/interpreter_parser.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/parsers/state_machine_parser.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/__init__.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/__init__.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/certificate_primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/client_echo_primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/control_channel_primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/data_channel_primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/data_manipulation.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/dns_primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/event_primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/http_1_request_primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/http_1_response_primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/http_2_client_primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/http_2_server_primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/http_simple_client_primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/https_1_request_primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/https_1_response_primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/icmp_primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/io_primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/ip_primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/nested_machine_utils.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/probing_primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/replay_primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/server_echo_primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/signaling_primitive.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/tcp_primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/timing_primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/tls_primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/udp_primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/condition_primitives/__init__.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/condition_primitives/condition_primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/condition_primitives/variable_comparisons.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/transition_primitives/__init__.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/transition_primitives/assignment_transitions.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/transition_primitives/transition_primitives.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/sniffers/__init__.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/sniffers/sniffer.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/tools/__init__.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/tools/checks.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/tools/echo_socket_server.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/tools/http_2_overwrite.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/tools/http_2_socket_base.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/tools/http_2_socket_client.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/tools/http_2_socket_server.py +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran.egg-info/dependency_links.txt +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran.egg-info/entry_points.txt +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran.egg-info/top_level.txt +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/setup.cfg +0 -0
- {nopasaran-0.2.95 → nopasaran-0.2.97}/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.97
|
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
|
{nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/action_primitives.py
RENAMED
@@ -25,6 +25,9 @@ from nopasaran.primitives.action_primitives.client_echo_primitives import Client
|
|
25
25
|
from nopasaran.primitives.action_primitives.probing_primitives import PortProbingPrimitives
|
26
26
|
from nopasaran.primitives.action_primitives.replay_primitives import ReplayPrimitives
|
27
27
|
from nopasaran.primitives.action_primitives.http_simple_client_primitives import HTTPSimpleClientPrimitives
|
28
|
+
from nopasaran.primitives.action_primitives.tcp_dns_request_primitives import TCPDNSRequestPrimitives
|
29
|
+
from nopasaran.primitives.action_primitives.tcp_dns_response_primitives import TCPDNSResponsePrimitives
|
30
|
+
|
28
31
|
|
29
32
|
class ActionPrimitives(Primitives):
|
30
33
|
"""
|
@@ -56,5 +59,7 @@ class ActionPrimitives(Primitives):
|
|
56
59
|
ClientEchoPrimitives,
|
57
60
|
PortProbingPrimitives,
|
58
61
|
ReplayPrimitives,
|
59
|
-
HTTPSimpleClientPrimitives
|
62
|
+
HTTPSimpleClientPrimitives,
|
63
|
+
TCPDNSResponsePrimitives,
|
64
|
+
TCPDNSRequestPrimitives
|
60
65
|
]
|
@@ -0,0 +1,29 @@
|
|
1
|
+
from nopasaran.decorators import parsing_decorator
|
2
|
+
from nopasaran.definitions.events import EventNames
|
3
|
+
import nopasaran.utils as utils
|
4
|
+
|
5
|
+
class TCPDNSRequestPrimitives:
|
6
|
+
@staticmethod
|
7
|
+
@parsing_decorator(input_args=4, output_args=1)
|
8
|
+
def make_tcp_dns_query(inputs, outputs, state_machine):
|
9
|
+
"""
|
10
|
+
Make a DNS query over TCP with user-defined domain and query type.
|
11
|
+
inputs: [domain, query_type, server_ip, server_port]
|
12
|
+
outputs: [dns_response_dict]
|
13
|
+
"""
|
14
|
+
domain = state_machine.get_variable_value(inputs[0])
|
15
|
+
query_type = state_machine.get_variable_value(inputs[1])
|
16
|
+
server_ip = state_machine.get_variable_value(inputs[2])
|
17
|
+
server_port = int(state_machine.get_variable_value(inputs[3]))
|
18
|
+
|
19
|
+
# Call utility function with provided parameters
|
20
|
+
result = utils.send_tcp_dns_query(server_ip, server_port, domain, query_type)
|
21
|
+
|
22
|
+
# Store result and trigger events accordingly
|
23
|
+
if not result or result.get("response") is None:
|
24
|
+
state_machine.set_variable_value(outputs[0], {"received": None})
|
25
|
+
state_machine.trigger_event(EventNames.REQUEST_ERROR.name)
|
26
|
+
else:
|
27
|
+
state_machine.set_variable_value(outputs[0], {"received": result})
|
28
|
+
state_machine.trigger_event(EventNames.RESPONSE_RECEIVED.name)
|
29
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
from nopasaran.decorators import parsing_decorator
|
2
|
+
from nopasaran.tools.tcp_dns_socket_server import TCPDNSSocketServer
|
3
|
+
|
4
|
+
class TCPDNSResponsePrimitives:
|
5
|
+
|
6
|
+
@staticmethod
|
7
|
+
@parsing_decorator(input_args=0, output_args=1)
|
8
|
+
def create_tcp_dns_server(inputs, outputs, state_machine):
|
9
|
+
server = TCPDNSSocketServer()
|
10
|
+
state_machine.set_variable_value(outputs[0], server)
|
11
|
+
|
12
|
+
@staticmethod
|
13
|
+
@parsing_decorator(input_args=2, output_args=0)
|
14
|
+
def start_tcp_dns_server(inputs, outputs, state_machine):
|
15
|
+
server = state_machine.get_variable_value(inputs[0])
|
16
|
+
port = int(state_machine.get_variable_value(inputs[1]))
|
17
|
+
server.start(port)
|
18
|
+
|
19
|
+
@staticmethod
|
20
|
+
@parsing_decorator(input_args=4, output_args=1)
|
21
|
+
def wait_and_respond_tcp_dns_query(inputs, outputs, state_machine):
|
22
|
+
"""
|
23
|
+
Wait for a DNS query and respond based on provided spec.
|
24
|
+
Inputs: [server_instance, port, timeout, response_spec]
|
25
|
+
Outputs: [result_dict]
|
26
|
+
Example response_spec:
|
27
|
+
{"type": "CNAME", "value": "safe.com", "qname": "blocked.com."}
|
28
|
+
"""
|
29
|
+
server = state_machine.get_variable_value(inputs[0])
|
30
|
+
timeout = int(state_machine.get_variable_value(inputs[2]))
|
31
|
+
response_spec = state_machine.get_variable_value(inputs[3]) # can be None
|
32
|
+
|
33
|
+
result, _ = server.wait_for_query(timeout, response_spec)
|
34
|
+
state_machine.set_variable_value(outputs[0], result)
|
35
|
+
|
36
|
+
@staticmethod
|
37
|
+
@parsing_decorator(input_args=1, output_args=0)
|
38
|
+
def close_tcp_dns_server(inputs, outputs, state_machine):
|
39
|
+
server = state_machine.get_variable_value(inputs[0])
|
40
|
+
server.close()
|
@@ -124,79 +124,6 @@ class HTTP1SocketServer:
|
|
124
124
|
|
125
125
|
return EventNames.SERVER_STARTED.name, f"Server successfully started at {host}:{port}."
|
126
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
127
|
def close(self):
|
201
128
|
"""Close the HTTP/1.1 connection and clean up resources"""
|
202
129
|
try:
|
@@ -209,6 +136,6 @@ class HTTP1SocketServer:
|
|
209
136
|
self.client_socket = None
|
210
137
|
self.sock = None
|
211
138
|
|
212
|
-
return EventNames.
|
139
|
+
return EventNames.CONNECTION_ENDING.name
|
213
140
|
except Exception as e:
|
214
|
-
return EventNames.
|
141
|
+
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
|
@@ -0,0 +1,170 @@
|
|
1
|
+
import socket
|
2
|
+
import struct
|
3
|
+
import select
|
4
|
+
import time
|
5
|
+
from dnslib import DNSRecord, RR, QTYPE, A, CNAME, MX, TXT, NS, SOA, PTR, AAAA, SRV, DS, RRSIG, NSEC, DNSKEY
|
6
|
+
from nopasaran.definitions.events import EventNames
|
7
|
+
|
8
|
+
class TCPDNSSocketServer:
|
9
|
+
def __init__(self):
|
10
|
+
self.sock = None
|
11
|
+
|
12
|
+
def start(self, port):
|
13
|
+
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
14
|
+
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
15
|
+
self.sock.bind(('', port))
|
16
|
+
self.sock.listen(5)
|
17
|
+
return EventNames.SERVER_STARTED.name, f"TCP DNS server started on port {port}"
|
18
|
+
|
19
|
+
def wait_for_query(self, timeout, response_spec=None):
|
20
|
+
timeout = float(timeout)
|
21
|
+
start_time = time.time()
|
22
|
+
self.sock.setblocking(False)
|
23
|
+
|
24
|
+
print(f"[Server] Waiting for connections on port {self.sock.getsockname()[1]} with timeout {timeout} seconds")
|
25
|
+
|
26
|
+
while True:
|
27
|
+
remaining_time = timeout - (time.time() - start_time)
|
28
|
+
print(f"[Server] Remaining time: {remaining_time:.2f} seconds")
|
29
|
+
if remaining_time <= 0:
|
30
|
+
print("[Server] Timeout reached with no connection.")
|
31
|
+
return {"received": None}, EventNames.TIMEOUT.name
|
32
|
+
|
33
|
+
ready, _, _ = select.select([self.sock], [], [], remaining_time)
|
34
|
+
if ready:
|
35
|
+
print("[Server] Socket is ready, accepting...")
|
36
|
+
client_sock, client_addr = self.sock.accept()
|
37
|
+
print(f"[Server] Accepted connection from {client_addr}")
|
38
|
+
|
39
|
+
try:
|
40
|
+
# Set client socket read timeout
|
41
|
+
client_sock.settimeout(5)
|
42
|
+
length_data = client_sock.recv(2)
|
43
|
+
print(f"[Server] Received length_data: {length_data}")
|
44
|
+
|
45
|
+
if len(length_data) < 2:
|
46
|
+
print("[Server] Incomplete length_data")
|
47
|
+
return {"received": None}, EventNames.ERROR.name
|
48
|
+
|
49
|
+
expected_length = struct.unpack("!H", length_data)[0]
|
50
|
+
print(f"[Server] Expecting {expected_length} bytes of query data")
|
51
|
+
|
52
|
+
request_data = b""
|
53
|
+
receive_start_time = time.time()
|
54
|
+
receive_timeout = 5 # seconds
|
55
|
+
|
56
|
+
while len(request_data) < expected_length:
|
57
|
+
# Check elapsed time to prevent infinite waiting
|
58
|
+
if time.time() - receive_start_time > receive_timeout:
|
59
|
+
print("[Server] Timeout while receiving DNS query data")
|
60
|
+
return {"received": None}, EventNames.TIMEOUT.name
|
61
|
+
|
62
|
+
try:
|
63
|
+
chunk = client_sock.recv(expected_length - len(request_data))
|
64
|
+
if not chunk:
|
65
|
+
print("[Server] Connection closed before full query received")
|
66
|
+
return {"received": None}, EventNames.ERROR.name
|
67
|
+
request_data += chunk
|
68
|
+
print(f"[Server] Received {len(request_data)}/{expected_length} bytes")
|
69
|
+
except socket.timeout:
|
70
|
+
print("[Server] Socket recv() timed out")
|
71
|
+
return {"received": None}, EventNames.TIMEOUT.name
|
72
|
+
|
73
|
+
if not request_data:
|
74
|
+
print("[Server] No request data received")
|
75
|
+
return {"received": None}, EventNames.ERROR.name
|
76
|
+
|
77
|
+
print("[Server] Parsing DNS query...")
|
78
|
+
parsed_query = DNSRecord.parse(request_data)
|
79
|
+
print(f"[Server] Parsed query: {parsed_query.toZone()}")
|
80
|
+
|
81
|
+
print("[Server] Building DNS response...")
|
82
|
+
response = self.build_response(parsed_query, response_spec)
|
83
|
+
|
84
|
+
print("[Server] Sending DNS response...")
|
85
|
+
self.send_dns_response(client_sock, response)
|
86
|
+
print("[Server] DNS response sent successfully.")
|
87
|
+
|
88
|
+
return {
|
89
|
+
"received": parsed_query.toZone(),
|
90
|
+
"client_address": client_addr
|
91
|
+
}, EventNames.REQUEST_RECEIVED.name
|
92
|
+
|
93
|
+
finally:
|
94
|
+
print("[Server] Closing client socket")
|
95
|
+
client_sock.close()
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
def build_response(self, query_record, response_spec=None):
|
100
|
+
qname = str(query_record.q.qname)
|
101
|
+
qtype = query_record.q.qtype
|
102
|
+
response_qname = response_spec.get("qname") if response_spec and response_spec.get("qname") else qname
|
103
|
+
response_type = response_spec.get("type").upper() if response_spec and response_spec.get("type") else QTYPE[qtype].name
|
104
|
+
response_value = response_spec.get("value") if response_spec else None
|
105
|
+
|
106
|
+
response = query_record.reply()
|
107
|
+
|
108
|
+
handlers = {
|
109
|
+
"A": lambda: A(response_value or "127.0.0.1"),
|
110
|
+
"CNAME": lambda: CNAME(response_value or response_qname),
|
111
|
+
"MX": lambda: MX(response_value or f"mail.{response_qname}", preference=10),
|
112
|
+
"TXT": lambda: TXT(response_value or f"dummy record for {response_qname}"),
|
113
|
+
"NS": lambda: NS(response_value or f"ns1.{response_qname}"),
|
114
|
+
"SOA": lambda: SOA(response_value or f"ns1.{response_qname}", f"admin.{response_qname}", (2024051801, 3600, 3600, 3600, 3600)),
|
115
|
+
"PTR": lambda: PTR(response_value or f"ptr.{response_qname}"),
|
116
|
+
"AAAA": lambda: AAAA(response_value or "::1"),
|
117
|
+
"SRV": lambda: self._parse_srv(response_value or f"service.{response_qname},80,0,0"),
|
118
|
+
"DS": lambda: DS(12345, 1, 1, bytes(response_value or f"abcdef{response_qname}", 'utf-8')),
|
119
|
+
"RRSIG": lambda: RRSIG(1, 1, 0, 3600, 0, 0, 0, response_value or f"signer.{response_qname}", b"signature"),
|
120
|
+
"NSEC": lambda: NSEC(response_value or f"next.{response_qname}", []),
|
121
|
+
"DNSKEY": lambda: DNSKEY(256, 3, 8, bytes(response_value or f"publickey{response_qname}", 'utf-8')),
|
122
|
+
"ANY": lambda: A(response_value or "127.0.0.1")
|
123
|
+
}
|
124
|
+
|
125
|
+
handler = handlers.get(response_type)
|
126
|
+
if not handler:
|
127
|
+
print(f"[Server Error] No handler found for response_type: {response_type}")
|
128
|
+
return query_record.reply()
|
129
|
+
|
130
|
+
reverse_qtype = {QTYPE[k]: k for k in QTYPE if isinstance(k, int)}
|
131
|
+
rtype = reverse_qtype.get(response_type)
|
132
|
+
if rtype is None:
|
133
|
+
print(f"[Server Error] Unsupported response_type: {response_type}")
|
134
|
+
return query_record.reply()
|
135
|
+
|
136
|
+
try:
|
137
|
+
print(f"[Server] Calling handler for type {response_type}")
|
138
|
+
rdata = handler()
|
139
|
+
print(f"[Server] Handler produced rdata: {rdata}")
|
140
|
+
except Exception as e:
|
141
|
+
print(f"[Server Error] Handler for {response_type} failed: {e}")
|
142
|
+
return query_record.reply()
|
143
|
+
|
144
|
+
response.add_answer(RR(rname=response_qname, rtype=rtype, rclass=1, ttl=60, rdata=rdata))
|
145
|
+
return response
|
146
|
+
|
147
|
+
|
148
|
+
|
149
|
+
def _parse_srv(self, value):
|
150
|
+
try:
|
151
|
+
target, port, priority, weight = value.split(",")
|
152
|
+
return SRV(int(priority), int(weight), int(port), target)
|
153
|
+
except Exception:
|
154
|
+
return SRV(0, 0, 80, "service.example.com")
|
155
|
+
|
156
|
+
def send_dns_response(self, client_sock, dns_record):
|
157
|
+
response_bytes = dns_record.pack()
|
158
|
+
length_prefix = struct.pack("!H", len(response_bytes))
|
159
|
+
try:
|
160
|
+
client_sock.sendall(length_prefix + response_bytes)
|
161
|
+
return EventNames.RESPONSE_SENT.name
|
162
|
+
except Exception as e:
|
163
|
+
print(f"Failed to send DNS response: {e}")
|
164
|
+
return EventNames.ERROR.name
|
165
|
+
|
166
|
+
def close(self):
|
167
|
+
if self.sock:
|
168
|
+
self.sock.close()
|
169
|
+
self.sock = None
|
170
|
+
return EventNames.CONNECTION_CLOSED.name
|
@@ -4,6 +4,9 @@ import random
|
|
4
4
|
import socket
|
5
5
|
import select
|
6
6
|
import ssl
|
7
|
+
import struct
|
8
|
+
from dnslib import DNSRecord, QTYPE
|
9
|
+
|
7
10
|
|
8
11
|
|
9
12
|
from scapy.all import IP, TCP, UDP, ICMP, Raw
|
@@ -391,3 +394,76 @@ def group_ports(ports):
|
|
391
394
|
return result
|
392
395
|
|
393
396
|
|
397
|
+
|
398
|
+
|
399
|
+
def send_tcp_dns_query(server_ip, server_port, domain, query_type="A"):
|
400
|
+
dnsatypes = {
|
401
|
+
1: "A", 2: "NS", 5: "CNAME", 6: "SOA", 12: "PTR", 15: "MX",
|
402
|
+
16: "TXT", 28: "AAAA", 33: "SRV", 43: "DS",
|
403
|
+
46: "RRSIG", 47: "NSEC", 48: "DNSKEY", 255: "ANY"
|
404
|
+
}
|
405
|
+
|
406
|
+
result = {"query": None, "response": None, "error": None}
|
407
|
+
supported_types = ', '.join([f"{v}({k})" for k, v in dnsatypes.items()])
|
408
|
+
lookup_by_name = {v.upper(): k for k, v in dnsatypes.items()}
|
409
|
+
query_type_str = str(query_type).strip().upper()
|
410
|
+
|
411
|
+
print(f"[Debug] Received query_type: {query_type_str}")
|
412
|
+
|
413
|
+
# Validate query type
|
414
|
+
if query_type_str.isdigit():
|
415
|
+
qtype = int(query_type_str)
|
416
|
+
if qtype not in dnsatypes:
|
417
|
+
result["error"] = f"Unsupported numeric query type: {query_type}.\nSupported types are: {supported_types}"
|
418
|
+
print("[Error]", result["error"])
|
419
|
+
return result
|
420
|
+
else:
|
421
|
+
qtype = lookup_by_name.get(query_type_str)
|
422
|
+
if qtype is None:
|
423
|
+
result["error"] = f"Unsupported string query type: {query_type}.\nSupported types are: {supported_types}"
|
424
|
+
print("[Error]", result["error"])
|
425
|
+
return result
|
426
|
+
|
427
|
+
try:
|
428
|
+
print(f"[Debug] Building DNS query for domain {domain} with type {query_type_str} ({qtype})")
|
429
|
+
dns_query = DNSRecord.question(domain, qtype=query_type_str)
|
430
|
+
dns_query.header.id = random.randint(0, 65535)
|
431
|
+
query_packet = dns_query.pack()
|
432
|
+
result["query"] = dns_query.toZone()
|
433
|
+
|
434
|
+
print("[Debug] Opening socket")
|
435
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
436
|
+
sock.bind(('', 0))
|
437
|
+
sock.settimeout(2)
|
438
|
+
|
439
|
+
print(f"[Debug] Connecting to {server_ip}:{server_port}")
|
440
|
+
sock.connect((server_ip, server_port))
|
441
|
+
|
442
|
+
length_prefix = struct.pack("!H", len(query_packet))
|
443
|
+
print("[Debug] Sending DNS query")
|
444
|
+
sock.sendall(length_prefix + query_packet)
|
445
|
+
|
446
|
+
print("[Debug] Waiting for response")
|
447
|
+
length_data = sock.recv(2)
|
448
|
+
if len(length_data) < 2:
|
449
|
+
result["error"] = "Incomplete length prefix"
|
450
|
+
print("[Error]", result["error"])
|
451
|
+
return result
|
452
|
+
|
453
|
+
expected_length = struct.unpack("!H", length_data)[0]
|
454
|
+
response_data = b""
|
455
|
+
while len(response_data) < expected_length:
|
456
|
+
chunk = sock.recv(expected_length - len(response_data))
|
457
|
+
if not chunk:
|
458
|
+
break
|
459
|
+
response_data += chunk
|
460
|
+
|
461
|
+
print("[Debug] Received response data")
|
462
|
+
parsed_response = DNSRecord.parse(response_data)
|
463
|
+
result["response"] = parsed_response.to_dict()
|
464
|
+
return result
|
465
|
+
|
466
|
+
except Exception as e:
|
467
|
+
result["error"] = f"Exception occurred: {str(e)}"
|
468
|
+
print("[Error]", result["error"])
|
469
|
+
return result
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: nopasaran
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.97
|
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
|
@@ -60,6 +60,8 @@ nopasaran/primitives/action_primitives/probing_primitives.py
|
|
60
60
|
nopasaran/primitives/action_primitives/replay_primitives.py
|
61
61
|
nopasaran/primitives/action_primitives/server_echo_primitives.py
|
62
62
|
nopasaran/primitives/action_primitives/signaling_primitive.py
|
63
|
+
nopasaran/primitives/action_primitives/tcp_dns_request_primitives.py
|
64
|
+
nopasaran/primitives/action_primitives/tcp_dns_response_primitives.py
|
63
65
|
nopasaran/primitives/action_primitives/tcp_primitives.py
|
64
66
|
nopasaran/primitives/action_primitives/timing_primitives.py
|
65
67
|
nopasaran/primitives/action_primitives/tls_primitives.py
|
@@ -81,4 +83,5 @@ nopasaran/tools/http_2_socket_base.py
|
|
81
83
|
nopasaran/tools/http_2_socket_client.py
|
82
84
|
nopasaran/tools/http_2_socket_server.py
|
83
85
|
nopasaran/tools/https_1_socket_server.py
|
86
|
+
nopasaran/tools/tcp_dns_socket_server.py
|
84
87
|
tests/__init__.py
|
@@ -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.97',
|
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.",
|
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/data_manipulation.py
RENAMED
File without changes
|
{nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/dns_primitives.py
RENAMED
File without changes
|
{nopasaran-0.2.95 → nopasaran-0.2.97}/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.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/icmp_primitives.py
RENAMED
File without changes
|
{nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/io_primitives.py
RENAMED
File without changes
|
{nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/ip_primitives.py
RENAMED
File without changes
|
{nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/nested_machine_utils.py
RENAMED
File without changes
|
{nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/probing_primitives.py
RENAMED
File without changes
|
{nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/replay_primitives.py
RENAMED
File without changes
|
File without changes
|
{nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/signaling_primitive.py
RENAMED
File without changes
|
{nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/tcp_primitives.py
RENAMED
File without changes
|
{nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/timing_primitives.py
RENAMED
File without changes
|
{nopasaran-0.2.95 → nopasaran-0.2.97}/nopasaran/primitives/action_primitives/tls_primitives.py
RENAMED
File without changes
|
{nopasaran-0.2.95 → nopasaran-0.2.97}/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.95 → nopasaran-0.2.97}/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
|