Skip to content

Commit c9982d1

Browse files
committed
Introduction of the --dns-delay arg and logic.
Indeed, before this patch, user couldn't define the delay they want to apply between each DNS queries.
1 parent c94a50f commit c9982d1

File tree

7 files changed

+217
-10
lines changed

7 files changed

+217
-10
lines changed

PyFunceble/cli/entry_points/pyfunceble/argsparser.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ def parse_args(
8383
):
8484
raise self.error("--max-workers must be a positive digit.")
8585

86+
if namespace.dns__delay is not None and namespace.dns__delay < 0:
87+
raise self.error("--dns-delay must be zero or a positive digit.")
88+
8689
if namespace.cli_decoding__adblock and namespace.cli_decoding__wildcard:
8790
raise self.error("--adblock and --wildcard are incompatible.")
8891

PyFunceble/cli/entry_points/pyfunceble/cli.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,17 @@ def get_dns_control_group_data() -> List[Tuple[List[str], dict]]:
536536
% get_configured_value("dns.trust_server"),
537537
},
538538
),
539+
(
540+
[
541+
"--dns-delay",
542+
],
543+
{
544+
"dest": "dns.delay",
545+
"type": float,
546+
"help": "Sets the delay (in seconds) to apply between each DNS\n "
547+
"queries.\n %s" % get_configured_value("dns.delay"),
548+
},
549+
),
539550
]
540551

541552

PyFunceble/data/infrastructure/.PyFunceble_production.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,11 @@ dns:
243243
# Available: UDP | TCP | HTTPS | TLS
244244
protocol: UDP
245245

246+
# Sets the delay (seconds) to apply between each queries.
247+
# When a value > 0.00000 is given, we will wait for the given amount of
248+
# seconds between each queries.
249+
delay: 0.0
250+
246251
# Not Implemented yet. Reserved for future usage and implementation.
247252
share_logs: False
248253

PyFunceble/query/dns/query_tool.py

Lines changed: 73 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,9 @@ class DNSQueryTool:
8383
STD_TIMEOUT: float = 5.0
8484
STD_FOLLOW_NAMESERVER_ORDER: bool = True
8585
STD_TRUST_SERVER: bool = False
86+
STD_DELAY: float = 0.0
8687

8788
SUPPORTED_PROTOCOL: List[str] = ["TCP", "UDP", "HTTPS", "TLS"]
88-
BREAKOFF: float = 0.2
8989

9090
value2rdata_type: Dict[int, str] = {
9191
x.value: x.name for x in dns.rdatatype.RdataType
@@ -102,6 +102,7 @@ class DNSQueryTool:
102102
_preferred_protocol: str = "UDP"
103103
_query_timeout: float = 5.0
104104
_trust_server: bool = False
105+
_delay: float = 0.0
105106

106107
dns_name: Optional[str] = None
107108

@@ -115,6 +116,7 @@ def __init__(
115116
follow_nameserver_order: Optional[bool] = None,
116117
preferred_protocol: Optional[str] = None,
117118
trust_server: Optional[bool] = None,
119+
delay: Optional[bool] = None,
118120
) -> None:
119121
if nameservers is not None:
120122
self.nameservers.set_nameservers(nameservers)
@@ -136,6 +138,11 @@ def __init__(
136138
else:
137139
self.guess_and_set_trust_server()
138140

141+
if delay is not None:
142+
self.delay = delay
143+
else:
144+
self.guess_and_set_delay()
145+
139146
def prepare_query(func): # pylint: disable=no-self-argument
140147
"""
141148
Prepare the query after running the decorated method.
@@ -562,6 +569,50 @@ def set_preferred_protocol(self, value: str) -> "DNSQueryTool":
562569

563570
return self
564571

572+
@property
573+
def delay(self) -> float:
574+
"""
575+
Provides the current state of the :code:`_delay` attribute.
576+
"""
577+
578+
return self._delay
579+
580+
@delay.setter
581+
@update_lookup_record
582+
def delay(self, value: Union[int, float]) -> None:
583+
"""
584+
Sets the delay to apply.
585+
586+
:param value:
587+
The delay to apply.
588+
589+
:raise TypeError:
590+
When the given :code:`value` is not a :py:class:`float`
591+
nor :py:class.`int`.
592+
:raise ValueError:
593+
When the given :code:`value` is not a positive.
594+
"""
595+
596+
if not isinstance(value, (float, int)):
597+
raise TypeError(f"<value> should be {float} or {int}, {type(value)} given.")
598+
599+
if value < 0:
600+
raise ValueError(f"<value> should be positive, {value} given.")
601+
602+
self._delay = float(value)
603+
604+
def set_delay(self, value: Union[int, float]) -> "DNSQueryTool":
605+
"""
606+
Sets the delay to apply.
607+
608+
:param value:
609+
The delay to apply.
610+
"""
611+
612+
self.delay = value
613+
614+
return self
615+
565616
def guess_and_set_preferred_protocol(self) -> "DNSQueryTool":
566617
"""
567618
Try to guess and set the preferred procol.
@@ -612,6 +663,19 @@ def guess_and_set_trust_server(self) -> "DNSQueryTool":
612663

613664
return self
614665

666+
def guess_and_set_delay(self) -> "DNSQueryTool":
667+
"""
668+
Try to guess and set the delay to apply.
669+
"""
670+
671+
if PyFunceble.facility.ConfigLoader.is_already_loaded():
672+
if PyFunceble.storage.CONFIGURATION.dns.delay:
673+
self.delay = PyFunceble.storage.CONFIGURATION.dns.delay
674+
else:
675+
self.delay = self.STD_DELAY
676+
else:
677+
self.delay = self.STD_DELAY
678+
615679
def guess_all_settings(
616680
self,
617681
) -> "DNSQueryTool": # pragma: no cover ## Method themselves are more important
@@ -750,10 +814,10 @@ def tcp(
750814
"Unsuccessfully queried information of %r from %r. Sleeping %fs.",
751815
self.subject,
752816
nameserver,
753-
self.BREAKOFF,
817+
self.delay,
754818
)
755819

756-
time.sleep(self.BREAKOFF)
820+
time.sleep(self.delay)
757821

758822
return ListHelper(result).remove_duplicates().subject
759823

@@ -822,10 +886,10 @@ def udp(
822886
"Unsuccessfully queried information of %r from %r. Sleeping %fs.",
823887
self.subject,
824888
nameserver,
825-
self.BREAKOFF,
889+
self.delay,
826890
)
827891

828-
time.sleep(self.BREAKOFF)
892+
time.sleep(self.delay)
829893

830894
return ListHelper(result).remove_duplicates().subject
831895

@@ -888,10 +952,10 @@ def https(
888952
"Unsuccessfully queried information of %r from %r. Sleeping %fs.",
889953
self.subject,
890954
nameserver,
891-
self.BREAKOFF,
955+
self.delay,
892956
)
893957

894-
time.sleep(self.BREAKOFF)
958+
time.sleep(self.delay)
895959

896960
return ListHelper(result).remove_duplicates().subject
897961

@@ -965,10 +1029,10 @@ def tls(
9651029
"Unsuccessfully queried information of %r from %r. Sleeping %fs.",
9661030
self.subject,
9671031
nameserver,
968-
self.BREAKOFF,
1032+
self.delay,
9691033
)
9701034

971-
time.sleep(self.BREAKOFF)
1035+
time.sleep(self.delay)
9721036

9731037
return ListHelper(result).remove_duplicates().subject
9741038

docs/configuration/dns.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,12 @@
7171
Otherwise, when the trust mode is disabled, when the first read DNS server
7272
gives us a negative response (without any error), we still ask all other
7373
DNS servers that were given or found.
74+
75+
:code:`dns[delay]`
76+
""""""""""""""""""
77+
78+
**Type:** :code:`float`
79+
80+
**Default value:** :code:`0.0`
81+
82+
**Description:** Sets the delay to apply between each DNS query.

docs/usage/terminal.rst

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ Test sources
4646

4747
This argument takes one or more values separated by spaces.
4848

49-
Test one or more :code:`$DOMAIN`s,
49+
Test one or more :code:`$DOMAIN`s,
5050
5151
.. code-block:: console
5252
@@ -634,6 +634,25 @@ Activates or disable the trust mode.
634634
a negative response - without error - we still consolidate by
635635
checking all given/found server.
636636

637+
------
638+
639+
640+
.. _dns-delay:
641+
642+
:code:`--dns-delay`
643+
"""""""""""""""""""
644+
645+
.. versionadded:: 4.0.0
646+
647+
Sets the delay to apply between each DNS query.
648+
649+
**Default value:** :code:`0.0`
650+
651+
.. note::
652+
When greater that :code:`0.0`, a delay will be applied between each DNS
653+
query.
654+
655+
Otherwise, if equal to `0.0`, no delay will be applied.
637656

638657
------
639658

@@ -1468,6 +1487,11 @@ Global overview
14681487
checking all given/found server.
14691488

14701489
Configured value: False
1490+
--dns-delay DNS__DELAY
1491+
Sets the delay (in seconds) to apply between each DNS
1492+
queries.
1493+
1494+
Configured value: 0.0
14711495

14721496
Databases:
14731497
--inactive-db Activates or disables the usage of a 'database' to

tests/query/dnss/test_query_tool.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,97 @@ def test_guess_and_set_preferred_protocol_none(self) -> None:
692692

693693
self.assertEqual(expected, actual)
694694

695+
def test_set_delay(self) -> None:
696+
"""
697+
Tests the method which let us set the delay.
698+
"""
699+
700+
given = 0.48
701+
expected = 0.48
702+
703+
self.query_tool.set_delay(given)
704+
actual = self.query_tool.delay
705+
706+
self.assertEqual(expected, actual)
707+
708+
def test_set_delay_through_init(self) -> None:
709+
"""
710+
Tests the method which let us set the delay.
711+
712+
In this test we check that the transfert of the delay
713+
through the constructor is working.
714+
"""
715+
716+
given = 0.55
717+
718+
query_tool = DNSQueryTool(delay=given)
719+
720+
expected = 0.55
721+
actual = query_tool.delay
722+
723+
self.assertEqual(expected, actual)
724+
725+
def test_set_delay_not_float(self) -> None:
726+
"""
727+
Tests the method which let us set the delay for the case
728+
that the given delay is not a float or int.
729+
"""
730+
731+
given = ["Hello", "World"]
732+
733+
self.assertRaises(TypeError, lambda: self.query_tool.set_delay(given))
734+
735+
def test_guess_and_set_delay(self) -> None:
736+
"""
737+
Tests the method which let us guess and set the delay between each
738+
queries.
739+
740+
In this test, we check the case that the given delay is set to a value
741+
greater than 0.
742+
"""
743+
744+
config_loader = ConfigLoader()
745+
config_loader.set_custom_config({"dns": {"delay": 0.45}}).start()
746+
747+
self.query_tool.guess_and_set_delay()
748+
749+
expected = 0.45
750+
actual = self.query_tool.delay
751+
752+
self.assertEqual(expected, actual)
753+
754+
def test_guess_and_set_delay_less_than_zero(self) -> None:
755+
"""
756+
Tests the method which let us guess and set the delay between each
757+
queries.
758+
759+
In this test, we check the case that the given delay is set to a value
760+
less than 0.
761+
"""
762+
763+
config_loader = ConfigLoader()
764+
config_loader.set_custom_config({"dns": {"delay": -3.0}}).start()
765+
766+
self.assertRaises(ValueError, lambda: self.query_tool.guess_and_set_delay())
767+
768+
def test_guess_and_set_delay_none(self) -> None:
769+
"""
770+
Tests the method which let us guess and set the delay between each
771+
queries.
772+
773+
In this test, we check the case that the given delay is set to None.
774+
"""
775+
776+
config_loader = ConfigLoader()
777+
config_loader.set_custom_config({"dns": {"delay": None}}).start()
778+
779+
self.query_tool.guess_and_set_delay()
780+
781+
expected = self.query_tool.STD_DELAY
782+
actual = self.query_tool.delay
783+
784+
self.assertEqual(expected, actual)
785+
695786
def test_get_lookup_record(self) -> None:
696787
"""
697788
Tests the method which let us get the lookup record.

0 commit comments

Comments
 (0)