nopasaran 0.2.99__py3-none-any.whl → 0.2.101__py3-none-any.whl

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.
@@ -35,4 +35,5 @@ class EventNames(Enum):
35
35
  REJECTED = 24
36
36
  RESET_RECEIVED = 25
37
37
  RECEIVED_FRAMES = 26
38
- RECEIVED_REQUESTS = 27
38
+ RECEIVED_REQUESTS = 27
39
+ RESPONSE_SENT = 27
@@ -27,6 +27,8 @@ from nopasaran.primitives.action_primitives.replay_primitives import ReplayPrimi
27
27
  from nopasaran.primitives.action_primitives.http_simple_client_primitives import HTTPSimpleClientPrimitives
28
28
  from nopasaran.primitives.action_primitives.tcp_dns_request_primitives import TCPDNSRequestPrimitives
29
29
  from nopasaran.primitives.action_primitives.tcp_dns_response_primitives import TCPDNSResponsePrimitives
30
+ from nopasaran.primitives.action_primitives.udp_dns_request_primitives import UDPDNSRequestPrimitives
31
+ from nopasaran.primitives.action_primitives.udp_dns_response_primitives import UDPDNSResponsePrimitives
30
32
 
31
33
 
32
34
  class ActionPrimitives(Primitives):
@@ -60,6 +62,8 @@ class ActionPrimitives(Primitives):
60
62
  PortProbingPrimitives,
61
63
  ReplayPrimitives,
62
64
  HTTPSimpleClientPrimitives,
65
+ TCPDNSRequestPrimitives,
63
66
  TCPDNSResponsePrimitives,
64
- TCPDNSRequestPrimitives
67
+ UDPDNSRequestPrimitives,
68
+ UDPDNSResponsePrimitives
65
69
  ]
@@ -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 UDPDNSRequestPrimitives:
6
+ @staticmethod
7
+ @parsing_decorator(input_args=4, output_args=1)
8
+ def make_udp_dns_query(inputs, outputs, state_machine):
9
+ """
10
+ Make a DNS query over UDP 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_udp_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.udp_dns_socket_server import UDPDNSSocketServer
3
+
4
+ class UDPDNSResponsePrimitives:
5
+
6
+ @staticmethod
7
+ @parsing_decorator(input_args=0, output_args=1)
8
+ def create_udp_dns_server(inputs, outputs, state_machine):
9
+ server = UDPDNSSocketServer()
10
+ state_machine.set_variable_value(outputs[0], server)
11
+
12
+ @staticmethod
13
+ @parsing_decorator(input_args=2, output_args=0)
14
+ def start_udp_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_udp_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_udp_dns_server(inputs, outputs, state_machine):
39
+ server = state_machine.get_variable_value(inputs[0])
40
+ server.close()
@@ -4,6 +4,7 @@ import select
4
4
  import time
5
5
  from dnslib import DNSRecord, RR, QTYPE, A, CNAME, MX, TXT, NS, SOA, PTR, AAAA, SRV, DS, RRSIG, NSEC, DNSKEY
6
6
  from nopasaran.definitions.events import EventNames
7
+ import logging
7
8
 
8
9
  class TCPDNSSocketServer:
9
10
  def __init__(self):
@@ -14,6 +15,7 @@ class TCPDNSSocketServer:
14
15
  self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
15
16
  self.sock.bind(('', port))
16
17
  self.sock.listen(5)
18
+ logging.info(f"TCP DNS server started on port {port}")
17
19
  return EventNames.SERVER_STARTED.name, f"TCP DNS server started on port {port}"
18
20
 
19
21
  def wait_for_query(self, timeout, response_spec=None):
@@ -21,69 +23,66 @@ class TCPDNSSocketServer:
21
23
  start_time = time.time()
22
24
  self.sock.setblocking(False)
23
25
 
24
- print(f"[Server] Waiting for connections on port {self.sock.getsockname()[1]} with timeout {timeout} seconds")
26
+ logging.info(f"Waiting for connections on port {self.sock.getsockname()[1]} with timeout {timeout} seconds")
25
27
 
26
28
  while True:
27
29
  remaining_time = timeout - (time.time() - start_time)
28
- print(f"[Server] Remaining time: {remaining_time:.2f} seconds")
30
+ logging.debug(f"Remaining time: {remaining_time:.2f} seconds")
29
31
  if remaining_time <= 0:
30
- print("[Server] Timeout reached with no connection.")
32
+ logging.warning("Timeout reached with no connection.")
31
33
  return {"received": None}, EventNames.TIMEOUT.name
32
34
 
33
35
  ready, _, _ = select.select([self.sock], [], [], remaining_time)
34
36
  if ready:
35
- print("[Server] Socket is ready, accepting...")
36
37
  client_sock, client_addr = self.sock.accept()
37
- print(f"[Server] Accepted connection from {client_addr}")
38
+ logging.info(f"Accepted connection from {client_addr}")
38
39
 
39
40
  try:
40
- # Set client socket read timeout
41
41
  client_sock.settimeout(5)
42
42
  length_data = client_sock.recv(2)
43
- print(f"[Server] Received length_data: {length_data}")
43
+ logging.debug(f"Received length_data: {length_data}")
44
44
 
45
45
  if len(length_data) < 2:
46
- print("[Server] Incomplete length_data")
46
+ logging.error("Incomplete length_data")
47
47
  return {"received": None}, EventNames.ERROR.name
48
48
 
49
49
  expected_length = struct.unpack("!H", length_data)[0]
50
- print(f"[Server] Expecting {expected_length} bytes of query data")
50
+ logging.debug(f"Expecting {expected_length} bytes of query data")
51
51
 
52
52
  request_data = b""
53
53
  receive_start_time = time.time()
54
54
  receive_timeout = 5 # seconds
55
55
 
56
56
  while len(request_data) < expected_length:
57
- # Check elapsed time to prevent infinite waiting
58
57
  if time.time() - receive_start_time > receive_timeout:
59
- print("[Server] Timeout while receiving DNS query data")
58
+ logging.warning("Timeout while receiving DNS query data")
60
59
  return {"received": None}, EventNames.TIMEOUT.name
61
60
 
62
61
  try:
63
62
  chunk = client_sock.recv(expected_length - len(request_data))
64
63
  if not chunk:
65
- print("[Server] Connection closed before full query received")
64
+ logging.error("Connection closed before full query received")
66
65
  return {"received": None}, EventNames.ERROR.name
67
66
  request_data += chunk
68
- print(f"[Server] Received {len(request_data)}/{expected_length} bytes")
67
+ logging.debug(f"Received {len(request_data)}/{expected_length} bytes")
69
68
  except socket.timeout:
70
- print("[Server] Socket recv() timed out")
69
+ logging.warning("Socket recv() timed out")
71
70
  return {"received": None}, EventNames.TIMEOUT.name
72
71
 
73
72
  if not request_data:
74
- print("[Server] No request data received")
73
+ logging.error("No request data received")
75
74
  return {"received": None}, EventNames.ERROR.name
76
75
 
77
- print("[Server] Parsing DNS query...")
76
+ logging.debug("Parsing DNS query...")
78
77
  parsed_query = DNSRecord.parse(request_data)
79
- print(f"[Server] Parsed query: {parsed_query.toZone()}")
78
+ logging.info(f"Parsed query:\n{parsed_query.toZone()}")
80
79
 
81
- print("[Server] Building DNS response...")
80
+ logging.debug("Building DNS response...")
82
81
  response = self.build_response(parsed_query, response_spec)
83
82
 
84
- print("[Server] Sending DNS response...")
83
+ logging.debug("Sending DNS response...")
85
84
  self.send_dns_response(client_sock, response)
86
- print("[Server] DNS response sent successfully.")
85
+ logging.info("DNS response sent successfully.")
87
86
 
88
87
  return {
89
88
  "received": parsed_query.toZone(),
@@ -91,11 +90,9 @@ class TCPDNSSocketServer:
91
90
  }, EventNames.REQUEST_RECEIVED.name
92
91
 
93
92
  finally:
94
- print("[Server] Closing client socket")
93
+ logging.debug("Closing client socket")
95
94
  client_sock.close()
96
95
 
97
-
98
-
99
96
  def build_response(self, query_record, response_spec=None):
100
97
  qname = str(query_record.q.qname)
101
98
  qtype = query_record.q.qtype
@@ -124,33 +121,32 @@ class TCPDNSSocketServer:
124
121
 
125
122
  handler = handlers.get(response_type)
126
123
  if not handler:
127
- print(f"[Server Error] No handler found for response_type: {response_type}")
124
+ logging.warning(f"No handler for response_type: {response_type}")
128
125
  return query_record.reply()
129
126
 
130
127
  reverse_qtype = {QTYPE[k]: k for k in QTYPE if isinstance(k, int)}
131
128
  rtype = reverse_qtype.get(response_type)
132
129
  if rtype is None:
133
- print(f"[Server Error] Unsupported response_type: {response_type}")
130
+ logging.warning(f"Unsupported response_type: {response_type}")
134
131
  return query_record.reply()
135
132
 
136
133
  try:
137
- print(f"[Server] Calling handler for type {response_type}")
134
+ logging.debug(f"Calling handler for type {response_type}")
138
135
  rdata = handler()
139
- print(f"[Server] Handler produced rdata: {rdata}")
136
+ logging.debug(f"Handler produced rdata: {rdata}")
140
137
  except Exception as e:
141
- print(f"[Server Error] Handler for {response_type} failed: {e}")
138
+ logging.error(f"Handler for {response_type} failed: {e}", exc_info=True)
142
139
  return query_record.reply()
143
140
 
144
141
  response.add_answer(RR(rname=response_qname, rtype=rtype, rclass=1, ttl=60, rdata=rdata))
145
142
  return response
146
143
 
147
-
148
-
149
144
  def _parse_srv(self, value):
150
145
  try:
151
146
  target, port, priority, weight = value.split(",")
152
147
  return SRV(int(priority), int(weight), int(port), target)
153
148
  except Exception:
149
+ logging.warning(f"Failed to parse SRV value: '{value}', using default fallback")
154
150
  return SRV(0, 0, 80, "service.example.com")
155
151
 
156
152
  def send_dns_response(self, client_sock, dns_record):
@@ -160,11 +156,12 @@ class TCPDNSSocketServer:
160
156
  client_sock.sendall(length_prefix + response_bytes)
161
157
  return EventNames.RESPONSE_SENT.name
162
158
  except Exception as e:
163
- print(f"Failed to send DNS response: {e}")
159
+ logging.error(f"Failed to send DNS response: {e}", exc_info=True)
164
160
  return EventNames.ERROR.name
165
161
 
166
162
  def close(self):
167
163
  if self.sock:
168
164
  self.sock.close()
169
165
  self.sock = None
170
- return EventNames.CONNECTION_CLOSED.name
166
+ logging.info("TCP socket closed")
167
+ return EventNames.CONNECTION_ENDING.name
@@ -0,0 +1,120 @@
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
+ import logging
8
+
9
+ class UDPDNSSocketServer:
10
+ def __init__(self):
11
+ self.sock = None
12
+
13
+ def start(self, port):
14
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
15
+ self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
16
+ self.sock.bind(('', port))
17
+ logging.info(f"UDP DNS server started on port {port}")
18
+ return EventNames.SERVER_STARTED.name, f"UDP DNS server started on port {port}"
19
+
20
+ def wait_for_query(self, timeout, response_spec=None):
21
+ self.sock.settimeout(timeout)
22
+ logging.info(f"Waiting for UDP packets on port {self.sock.getsockname()[1]} with timeout {timeout} seconds")
23
+
24
+ try:
25
+ data, client_addr = self.sock.recvfrom(512) # max size of a DNS UDP packet
26
+ logging.debug(f"Received {len(data)} bytes from {client_addr}")
27
+
28
+ logging.debug("Parsing DNS query...")
29
+ parsed_query = DNSRecord.parse(data)
30
+ logging.info(f"Parsed query:\n{parsed_query.toZone()}")
31
+
32
+ logging.debug("Building DNS response...")
33
+ response = self.build_response(parsed_query, response_spec)
34
+
35
+ logging.debug("Sending DNS response...")
36
+ self.send_dns_response(client_addr, response)
37
+ logging.info("DNS response sent successfully.")
38
+
39
+ return {
40
+ "received": parsed_query.toZone(),
41
+ "client_address": client_addr
42
+ }, EventNames.REQUEST_RECEIVED.name
43
+
44
+ except socket.timeout:
45
+ logging.warning("Timeout reached with no packet received.")
46
+ return {"received": None}, EventNames.TIMEOUT.name
47
+ except Exception as e:
48
+ logging.error(f"Error while handling DNS query: {e}", exc_info=True)
49
+ return {"received": None}, EventNames.ERROR.name
50
+
51
+ def build_response(self, query_record, response_spec=None):
52
+ qname = str(query_record.q.qname)
53
+ qtype = query_record.q.qtype
54
+ response_qname = response_spec.get("qname") if response_spec and response_spec.get("qname") else qname
55
+ response_type = response_spec.get("type").upper() if response_spec and response_spec.get("type") else QTYPE[qtype].name
56
+ response_value = response_spec.get("value") if response_spec else None
57
+
58
+ response = query_record.reply()
59
+
60
+ handlers = {
61
+ "A": lambda: A(response_value or "127.0.0.1"),
62
+ "CNAME": lambda: CNAME(response_value or response_qname),
63
+ "MX": lambda: MX(response_value or f"mail.{response_qname}", preference=10),
64
+ "TXT": lambda: TXT(response_value or f"dummy record for {response_qname}"),
65
+ "NS": lambda: NS(response_value or f"ns1.{response_qname}"),
66
+ "SOA": lambda: SOA(response_value or f"ns1.{response_qname}", f"admin.{response_qname}", (2024051801, 3600, 3600, 3600, 3600)),
67
+ "PTR": lambda: PTR(response_value or f"ptr.{response_qname}"),
68
+ "AAAA": lambda: AAAA(response_value or "::1"),
69
+ "SRV": lambda: self._parse_srv(response_value or f"service.{response_qname},80,0,0"),
70
+ "DS": lambda: DS(12345, 1, 1, bytes(response_value or f"abcdef{response_qname}", 'utf-8')),
71
+ "RRSIG": lambda: RRSIG(1, 1, 0, 3600, 0, 0, 0, response_value or f"signer.{response_qname}", b"signature"),
72
+ "NSEC": lambda: NSEC(response_value or f"next.{response_qname}", []),
73
+ "DNSKEY": lambda: DNSKEY(256, 3, 8, bytes(response_value or f"publickey{response_qname}", 'utf-8')),
74
+ "ANY": lambda: A(response_value or "127.0.0.1")
75
+ }
76
+
77
+ handler = handlers.get(response_type)
78
+ if not handler:
79
+ logging.warning(f"No handler for response_type: {response_type}")
80
+ return query_record.reply()
81
+
82
+ reverse_qtype = {QTYPE[k]: k for k in QTYPE if isinstance(k, int)}
83
+ rtype = reverse_qtype.get(response_type)
84
+ if rtype is None:
85
+ logging.warning(f"Unsupported response_type: {response_type}")
86
+ return query_record.reply()
87
+
88
+ try:
89
+ logging.debug(f"Calling handler for type {response_type}")
90
+ rdata = handler()
91
+ logging.debug(f"Handler produced rdata: {rdata}")
92
+ except Exception as e:
93
+ logging.error(f"Handler for {response_type} failed: {e}", exc_info=True)
94
+ return query_record.reply()
95
+
96
+ response.add_answer(RR(rname=response_qname, rtype=rtype, rclass=1, ttl=60, rdata=rdata))
97
+ return response
98
+
99
+ def _parse_srv(self, value):
100
+ try:
101
+ target, port, priority, weight = value.split(",")
102
+ return SRV(int(priority), int(weight), int(port), target)
103
+ except Exception:
104
+ logging.warning(f"Failed to parse SRV value: '{value}', using default fallback")
105
+ return SRV(0, 0, 80, "service.example.com")
106
+
107
+ def send_dns_response(self, client_addr, dns_record):
108
+ try:
109
+ self.sock.sendto(dns_record.pack(), client_addr)
110
+ return EventNames.RESPONSE_SENT.name
111
+ except Exception as e:
112
+ logging.error(f"Failed to send DNS response to {client_addr}: {e}", exc_info=True)
113
+ return EventNames.ERROR.name
114
+
115
+ def close(self):
116
+ if self.sock:
117
+ self.sock.close()
118
+ self.sock = None
119
+ logging.info("UDP socket closed")
120
+ return EventNames.CONNECTION_ENDING.name
nopasaran/utils.py CHANGED
@@ -5,6 +5,7 @@ import socket
5
5
  import select
6
6
  import ssl
7
7
  import struct
8
+ import logging
8
9
  from dnslib import DNSRecord, QTYPE
9
10
 
10
11
 
@@ -399,7 +400,7 @@ def group_ports(ports):
399
400
  def send_tcp_dns_query(server_ip, server_port, domain, query_type="A"):
400
401
  dnsatypes = {
401
402
  1: "A", 2: "NS", 5: "CNAME", 6: "SOA", 12: "PTR", 15: "MX",
402
- 16: "TXT", 28: "AAAA", 33: "SRV", 43: "DS",
403
+ 16: "TXT", 28: "AAAA", 33: "SRV", 43: "DS",
403
404
  46: "RRSIG", 47: "NSEC", 48: "DNSKEY", 255: "ANY"
404
405
  }
405
406
 
@@ -408,46 +409,43 @@ def send_tcp_dns_query(server_ip, server_port, domain, query_type="A"):
408
409
  lookup_by_name = {v.upper(): k for k, v in dnsatypes.items()}
409
410
  query_type_str = str(query_type).strip().upper()
410
411
 
411
- print(f"[Debug] Received query_type: {query_type_str}")
412
+ logging.debug(f"Received query_type: {query_type_str}")
412
413
 
413
- # Validate query type
414
414
  if query_type_str.isdigit():
415
415
  qtype = int(query_type_str)
416
416
  if qtype not in dnsatypes:
417
417
  result["error"] = f"Unsupported numeric query type: {query_type}.\nSupported types are: {supported_types}"
418
- print("[Error]", result["error"])
418
+ logging.error(result["error"])
419
419
  return result
420
420
  else:
421
421
  qtype = lookup_by_name.get(query_type_str)
422
422
  if qtype is None:
423
423
  result["error"] = f"Unsupported string query type: {query_type}.\nSupported types are: {supported_types}"
424
- print("[Error]", result["error"])
424
+ logging.error(result["error"])
425
425
  return result
426
426
 
427
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)
428
+ logging.debug(f"Building DNS query for domain {domain} with type {query_type_str} ({qtype})")
429
+ dns_query = DNSRecord.question(domain, qtype=dnsatypes[qtype])
430
430
  dns_query.header.id = random.randint(0, 65535)
431
431
  query_packet = dns_query.pack()
432
432
  result["query"] = dns_query.toZone()
433
433
 
434
- print("[Debug] Opening socket")
434
+ logging.debug("Opening TCP socket")
435
435
  with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
436
- sock.bind(('', 0))
437
436
  sock.settimeout(2)
438
-
439
- print(f"[Debug] Connecting to {server_ip}:{server_port}")
437
+ logging.debug(f"Connecting to {server_ip}:{server_port}")
440
438
  sock.connect((server_ip, server_port))
441
439
 
442
440
  length_prefix = struct.pack("!H", len(query_packet))
443
- print("[Debug] Sending DNS query")
441
+ logging.debug("Sending DNS query")
444
442
  sock.sendall(length_prefix + query_packet)
445
443
 
446
- print("[Debug] Waiting for response")
444
+ logging.debug("Waiting for response")
447
445
  length_data = sock.recv(2)
448
446
  if len(length_data) < 2:
449
447
  result["error"] = "Incomplete length prefix"
450
- print("[Error]", result["error"])
448
+ logging.error(result["error"])
451
449
  return result
452
450
 
453
451
  expected_length = struct.unpack("!H", length_data)[0]
@@ -458,12 +456,65 @@ def send_tcp_dns_query(server_ip, server_port, domain, query_type="A"):
458
456
  break
459
457
  response_data += chunk
460
458
 
461
- print("[Debug] Received response data")
459
+ logging.debug("Received response data")
460
+ parsed_response = DNSRecord.parse(response_data)
461
+ result["response"] = parsed_response.to_dict()
462
+ return result
463
+
464
+ except Exception as e:
465
+ result["error"] = f"Exception occurred: {str(e)}"
466
+ logging.error("An error occurred while sending the TCP DNS query")
467
+ return result
468
+
469
+ def send_udp_dns_query(server_ip, server_port, domain, query_type="A"):
470
+ dnsatypes = {
471
+ 1: "A", 2: "NS", 5: "CNAME", 6: "SOA", 12: "PTR", 15: "MX",
472
+ 16: "TXT", 28: "AAAA", 33: "SRV", 43: "DS",
473
+ 46: "RRSIG", 47: "NSEC", 48: "DNSKEY", 255: "ANY"
474
+ }
475
+
476
+ result = {"query": None, "response": None, "error": None}
477
+ supported_types = ', '.join([f"{v}({k})" for k, v in dnsatypes.items()])
478
+ lookup_by_name = {v.upper(): k for k, v in dnsatypes.items()}
479
+ query_type_str = str(query_type).strip().upper()
480
+
481
+ logging.debug(f"Received query_type: {query_type_str}")
482
+
483
+ if query_type_str.isdigit():
484
+ qtype = int(query_type_str)
485
+ if qtype not in dnsatypes:
486
+ result["error"] = f"Unsupported numeric query type: {query_type}.\nSupported types are: {supported_types}"
487
+ logging.error(result["error"])
488
+ return result
489
+ else:
490
+ qtype = lookup_by_name.get(query_type_str)
491
+ if qtype is None:
492
+ result["error"] = f"Unsupported string query type: {query_type}.\nSupported types are: {supported_types}"
493
+ logging.error(result["error"])
494
+ return result
495
+
496
+ try:
497
+ logging.debug(f"Building DNS query for domain {domain} with type {query_type_str} ({qtype})")
498
+ dns_query = DNSRecord.question(domain, qtype=dnsatypes[qtype])
499
+ dns_query.header.id = random.randint(0, 65535)
500
+ query_packet = dns_query.pack()
501
+ result["query"] = dns_query.toZone()
502
+
503
+ logging.debug("Opening UDP socket")
504
+ with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
505
+ sock.settimeout(2)
506
+ logging.debug(f"Sending UDP DNS query to {server_ip}:{server_port}")
507
+ sock.sendto(query_packet, (server_ip, server_port))
508
+
509
+ logging.debug("Waiting for UDP response")
510
+ response_data, _ = sock.recvfrom(4096)
511
+
512
+ logging.debug("Received UDP response")
462
513
  parsed_response = DNSRecord.parse(response_data)
463
514
  result["response"] = parsed_response.to_dict()
464
515
  return result
465
516
 
466
517
  except Exception as e:
467
518
  result["error"] = f"Exception occurred: {str(e)}"
468
- print("[Error]", result["error"])
519
+ logging.error("An error occurred while sending the UDP DNS query")
469
520
  return result
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nopasaran
3
- Version: 0.2.99
3
+ Version: 0.2.101
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
@@ -2,7 +2,7 @@ nopasaran/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  nopasaran/__main__.py,sha256=9dY9M5gbwdR74rBezoSAvQPO3acAVHFA5Kov9KkrXO8,2461
3
3
  nopasaran/decorators.py,sha256=4QcUtZAMYZqNWwv7OsIAhHcec8C_MfxThA8kmUXw1Xo,2941
4
4
  nopasaran/http_2_utils.py,sha256=eQL8uG2USCN5ab2brrYtxcTnYk-g3s_b7v4bZ3YdA-Y,26324
5
- nopasaran/utils.py,sha256=MmRiaBkDX655jXTOCgVnCZwkRI8raDQysTaAL6P0BMM,14206
5
+ nopasaran/utils.py,sha256=eCmEOHO9XJEmlrhr-0wHfbO9zrvTCw4XO24dszjWQHA,16456
6
6
  nopasaran/controllers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  nopasaran/controllers/controller.py,sha256=kh6_ByvSMRiz8o8DgfR00WfylmClVZKdHe1OIcnltp8,7326
8
8
  nopasaran/controllers/factory.py,sha256=e1c6oea4ePiSZcQuAUn-515kjgmBIOqFXQ_78huEXHw,1287
@@ -10,7 +10,7 @@ nopasaran/controllers/protocol.py,sha256=d8PvU5CWFA94ORPgqqOBCanRfTHFhRna4zmrWef
10
10
  nopasaran/definitions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  nopasaran/definitions/commands.py,sha256=zcETXuMlOdiVaV5pE7G4UXCTZX9ntPI1Wcy4Whkrl8M,236
12
12
  nopasaran/definitions/control_channel.py,sha256=5qif19Txyccy841wvlruFVZsRd55_0UsPv5_VZbHg98,649
13
- nopasaran/definitions/events.py,sha256=Bf4VcTZ_zDqfH3bhckz-RJRo0gLakDn2QImCw567lN8,778
13
+ nopasaran/definitions/events.py,sha256=GmkLpfUDHGcJh-mW9a5ITx52dS2e_4DvijpHuqTI2DI,801
14
14
  nopasaran/definitions/transitions.py,sha256=eFyOeJM3-z_Rx-G4hNoycv1Jb4vBPyJLUQcMruhNSpg,264
15
15
  nopasaran/errors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  nopasaran/errors/parsing_error.py,sha256=pi0jMhbLU7v1kEPOka80Bh7KTCf060OiK7w0zV0GPB0,634
@@ -28,7 +28,7 @@ nopasaran/parsers/state_machine_parser.py,sha256=suV_1dtGe3tEdqccHL4WItariHCYLVH
28
28
  nopasaran/primitives/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
29
  nopasaran/primitives/primitives.py,sha256=Qgx56p1lCLuzFL-udz777f1ttUYakWluID7G5kpQJc8,371
30
30
  nopasaran/primitives/action_primitives/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
- nopasaran/primitives/action_primitives/action_primitives.py,sha256=JNYnN8BiWk8KuCMwcKbEGv-IYOycobJQ7sStZE4Ge98,3614
31
+ nopasaran/primitives/action_primitives/action_primitives.py,sha256=Pp8FzB6c9OdvfflMJAexDbqqRj0ax5ezIrdvCAYXHyQ,3887
32
32
  nopasaran/primitives/action_primitives/certificate_primitives.py,sha256=3PDUDTYkTvbnIW5anPuG4igNyPj6YReo4lw69JvJbB0,13480
33
33
  nopasaran/primitives/action_primitives/client_echo_primitives.py,sha256=2Qupem0N4miGePilnm0-nsVbZUUS6BpME2JhKlU-32E,3532
34
34
  nopasaran/primitives/action_primitives/control_channel_primitives.py,sha256=_OMgxLLtaK0vvobwhPvSAJhYCOgn4optcGhXOdgh1Q4,11281
@@ -56,6 +56,8 @@ nopasaran/primitives/action_primitives/tcp_dns_response_primitives.py,sha256=Ogf
56
56
  nopasaran/primitives/action_primitives/tcp_primitives.py,sha256=H4lpqeL5uhqqwC0RYrelO0BlrTocrfGl4dbd6FUi5FI,16909
57
57
  nopasaran/primitives/action_primitives/timing_primitives.py,sha256=hq4Q2_9nbL9ihf5TRAMRPWZuybqP4CXZxfstelAF6R8,980
58
58
  nopasaran/primitives/action_primitives/tls_primitives.py,sha256=UZ-bwYBDCVxPrZjodGTTdICYGjfA9WPymIAqaOeaZYI,6163
59
+ nopasaran/primitives/action_primitives/udp_dns_request_primitives.py,sha256=p-FsvWJUDTOI34oeuV46oPh8FEvT0LR23lc_oAwuNI8,1328
60
+ nopasaran/primitives/action_primitives/udp_dns_response_primitives.py,sha256=F93weT7OU6Qsu3g26k6Frdiclr67pLWQ2hBhcthOWFo,1657
59
61
  nopasaran/primitives/action_primitives/udp_primitives.py,sha256=SHYzi1FS4EEIssSwMEUOTyQlSnyung8iWR6LzJLrarM,7533
60
62
  nopasaran/primitives/condition_primitives/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
61
63
  nopasaran/primitives/condition_primitives/condition_primitives.py,sha256=-AQdtK_lQfy7Qo3CVVII8MKPKtE9nvea2fh8sDOjdlc,307
@@ -74,11 +76,12 @@ nopasaran/tools/http_2_socket_base.py,sha256=KDfkU4Nr_TXxqzp1-ZHec_UawfDZ5oIIj-J
74
76
  nopasaran/tools/http_2_socket_client.py,sha256=5mmc8FuT2dNVAihbMdvyytVwME_xSc7mez_jwU3YZOA,6256
75
77
  nopasaran/tools/http_2_socket_server.py,sha256=8W-bxmdoGyK5moxpC87V1EGuZ9jJpIKhokvZrDV84kk,5551
76
78
  nopasaran/tools/https_1_socket_server.py,sha256=ytLVYdH5je4L6ElCKKhhsbU_53gT6QF9l5ym7q5TMmo,8162
77
- nopasaran/tools/tcp_dns_socket_server.py,sha256=UKOgyhCXj-u9KJoP8qsQLbyyIkjOhyG7-_3GSP8e0DM,7908
79
+ nopasaran/tools/tcp_dns_socket_server.py,sha256=RHbeydTHZ5nIe3llGGX81BLcVSV0pkM38rhxJgn5Edw,7924
80
+ nopasaran/tools/udp_dns_socket_server.py,sha256=OlQYNfnoLP_A-H61qyfVMvXFK-dg0rmx8r-wnQYMKRk,5600
78
81
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
- nopasaran-0.2.99.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
80
- nopasaran-0.2.99.dist-info/METADATA,sha256=SEjkQMlAxalgmDx6zn_5ydkbwVVI5oCy2inxEkKZ3eI,5496
81
- nopasaran-0.2.99.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
82
- nopasaran-0.2.99.dist-info/entry_points.txt,sha256=LaOz5GlWuMLjzg4KOEB5OVTattCXVW6a4nSW-WQajCw,55
83
- nopasaran-0.2.99.dist-info/top_level.txt,sha256=60R1FzpprzU8iiJ1cBMNOA0F083_lYoctFo7pzOpMwY,16
84
- nopasaran-0.2.99.dist-info/RECORD,,
82
+ nopasaran-0.2.101.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
83
+ nopasaran-0.2.101.dist-info/METADATA,sha256=NxZQ06nU5zoDe6ZbfIDXS9kTogNamlBtO-2BVx6VHtg,5497
84
+ nopasaran-0.2.101.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
85
+ nopasaran-0.2.101.dist-info/entry_points.txt,sha256=LaOz5GlWuMLjzg4KOEB5OVTattCXVW6a4nSW-WQajCw,55
86
+ nopasaran-0.2.101.dist-info/top_level.txt,sha256=60R1FzpprzU8iiJ1cBMNOA0F083_lYoctFo7pzOpMwY,16
87
+ nopasaran-0.2.101.dist-info/RECORD,,