• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

funilrys / PyFunceble / 5103569999

pending completion
5103569999

push

github-actions

funilrys
Fix linting issues.

11297 of 11853 relevant lines covered (95.31%)

11.42 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.3
/PyFunceble/checker/availability/base.py
1
"""
2
The tool to check the availability or syntax of domain, IP or URL.
3

4
::
5

6

7
    ██████╗ ██╗   ██╗███████╗██╗   ██╗███╗   ██╗ ██████╗███████╗██████╗ ██╗     ███████╗
8
    ██╔══██╗╚██╗ ██╔╝██╔════╝██║   ██║████╗  ██║██╔════╝██╔════╝██╔══██╗██║     ██╔════╝
9
    ██████╔╝ ╚████╔╝ █████╗  ██║   ██║██╔██╗ ██║██║     █████╗  ██████╔╝██║     █████╗
10
    ██╔═══╝   ╚██╔╝  ██╔══╝  ██║   ██║██║╚██╗██║██║     ██╔══╝  ██╔══██╗██║     ██╔══╝
11
    ██║        ██║   ██║     ╚██████╔╝██║ ╚████║╚██████╗███████╗██████╔╝███████╗███████╗
12
    ╚═╝        ╚═╝   ╚═╝      ╚═════╝ ╚═╝  ╚═══╝ ╚═════╝╚══════╝╚═════╝ ╚══════╝╚══════╝
13

14
Provides the base of all availability checker classes.
15

16
Author:
17
    Nissar Chababy, @funilrys, contactTATAfunilrysTODTODcom
18

19
Special thanks:
20
    https://pyfunceble.github.io/#/special-thanks
21

22
Contributors:
23
    https://pyfunceble.github.io/#/contributors
24

25
Project link:
26
    https://github.com/funilrys/PyFunceble
27

28
Project documentation:
29
    https://pyfunceble.readthedocs.io/en/dev/
30

31
Project homepage:
32
    https://pyfunceble.github.io/
33

34
License:
35
::
36

37

38
    Copyright 2017, 2018, 2019, 2020, 2022, 2023 Nissar Chababy
39

40
    Licensed under the Apache License, Version 2.0 (the "License");
41
    you may not use this file except in compliance with the License.
42
    You may obtain a copy of the License at
43

44
        http://www.apache.org/licenses/LICENSE-2.0
45

46
    Unless required by applicable law or agreed to in writing, software
47
    distributed under the License is distributed on an "AS IS" BASIS,
48
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
49
    See the License for the specific language governing permissions and
50
    limitations under the License.
51
"""
52

53
# pylint: disable=too-many-lines
54

55
import multiprocessing
56
from datetime import datetime
57
from typing import Dict, List, Optional
58

59
from sqlalchemy.orm import Session
60

61
import PyFunceble.checker.utils.whois
62
import PyFunceble.facility
63
import PyFunceble.factory
64
import PyFunceble.storage
65
from PyFunceble.checker.availability.extras.base import ExtraRuleHandlerBase
66
from PyFunceble.checker.availability.extras.dns import DNSRulesHandler
67
from PyFunceble.checker.availability.extras.etoxic import EToxicHandler
68
from PyFunceble.checker.availability.extras.rules import ExtraRulesHandler
69
from PyFunceble.checker.availability.extras.subject_switch import (
70
    SubjectSwitchRulesHandler,
71
)
72
from PyFunceble.checker.availability.params import AvailabilityCheckerParams
73
from PyFunceble.checker.availability.status import AvailabilityCheckerStatus
74
from PyFunceble.checker.base import CheckerBase
75
from PyFunceble.checker.syntax.domain import DomainSyntaxChecker
76
from PyFunceble.checker.syntax.ip import IPSyntaxChecker
77
from PyFunceble.checker.syntax.url import URLSyntaxChecker
78
from PyFunceble.converter.url2netloc import Url2Netloc
79
from PyFunceble.helpers.regex import RegexHelper
80
from PyFunceble.query.dns.query_tool import DNSQueryTool
81
from PyFunceble.query.http_status_code import HTTPStatusCode
82
from PyFunceble.query.netinfo.address import AddressInfo
83
from PyFunceble.query.netinfo.hostbyaddr import HostByAddrInfo
84
from PyFunceble.query.whois.query_tool import WhoisQueryTool
85

86

87
class AvailabilityCheckerBase(CheckerBase):
88
    """
89
    Provides the base of all our availability checker classes.
90

91
    :param str subject:
92
        Optional, The subject to work with.
93
    :param bool use_extra_rules:
94
        Optional, Activates/Disables the usage of our own set of extra rules.
95
    :param bool use_whois_lookup:
96
        Optional, Activates/Disables the usage of the WHOIS lookup to gather
97
        the status of the given :code:`subject`.
98
    :param bool use_dns_lookup:
99
        Optional, Activates/Disables the usage of the DNS lookup to gather the
100
        status of the given :code:`subject`.
101
    :param bool use_netinfo_lookup:
102
        Optional, Activates/Disables the usage of the network information
103
        lookup module to gather the status of the given :code:`subject`.
104
    :param bool use_http_code_lookup:
105
        Optional, Activates/Disables the usage of the HTTP status code lookup
106
        to gather the status of the given :code:`subject`.
107
    :param bool use_reputation_lookup:
108
        Optional, Activates/Disables the usage of the reputation dataset
109
        lookup to gather the status of the given :code:`subject`.
110
    :param bool do_syntax_check_first:
111
        Optional, Activates/Disables the check of the status before the actual
112
        status gathering.
113
    :param bool use_whois_db:
114
        Optional, Activates/Disable the usage of a local database to store the
115
        WHOIS datasets.
116
    """
117

118
    # pylint: disable=too-many-public-methods, too-many-instance-attributes
119

120
    STD_USE_EXTRA_RULES: bool = True
121
    STD_USE_WHOIS_LOOKUP: bool = True
122
    STD_USE_DNS_LOOKUP: bool = True
123
    STD_USE_NETINFO_LOOKUP: bool = True
124
    STD_USE_HTTP_CODE_LOOKUP: bool = True
125
    STD_USE_REPUTATION_LOOKUP: bool = False
126
    STD_USE_WHOIS_DB: bool = True
127

128
    dns_query_tool: Optional[DNSQueryTool] = None
129
    whois_query_tool: Optional[WhoisQueryTool] = None
130
    addressinfo_query_tool: Optional[AddressInfo] = None
131
    hostbyaddr_query_tool: Optional[HostByAddrInfo] = None
132
    http_status_code_query_tool: Optional[HTTPStatusCode] = None
133
    domain_syntax_checker: Optional[DomainSyntaxChecker] = None
134
    ip_syntax_checker: Optional[IPSyntaxChecker] = None
135
    url_syntax_checker: Optional[URLSyntaxChecker] = None
136
    extra_rules_handlers: Optional[List[ExtraRuleHandlerBase]] = None
137
    url2netloc: Optional[Url2Netloc] = None
138

139
    _use_extra_rules: bool = False
140
    _use_whois_lookup: bool = False
141
    _use_dns_lookup: bool = False
142
    _use_netinfo_lookup: bool = False
143
    _use_http_code_lookup: bool = False
144
    _use_reputation_lookup: bool = False
145
    _use_whois_db: bool = False
146

147
    status: Optional[AvailabilityCheckerStatus] = None
148
    params: Optional[AvailabilityCheckerParams] = None
149

150
    def __init__(
151
        self,
152
        subject: Optional[str] = None,
153
        *,
154
        use_extra_rules: Optional[bool] = None,
155
        use_whois_lookup: Optional[bool] = None,
156
        use_dns_lookup: Optional[bool] = None,
157
        use_netinfo_lookup: Optional[bool] = None,
158
        use_http_code_lookup: Optional[bool] = None,
159
        use_reputation_lookup: Optional[bool] = None,
160
        do_syntax_check_first: Optional[bool] = None,
161
        db_session: Optional[Session] = None,
162
        use_whois_db: Optional[bool] = None,
163
        use_collection: Optional[bool] = None,
164
    ) -> None:
165
        self.dns_query_tool = DNSQueryTool()
166
        self.whois_query_tool = WhoisQueryTool()
167
        self.addressinfo_query_tool = AddressInfo()
168
        self.hostbyaddr_query_tool = HostByAddrInfo()
169
        self.http_status_code_query_tool = HTTPStatusCode()
170
        self.domain_syntax_checker = DomainSyntaxChecker()
171
        self.ip_syntax_checker = IPSyntaxChecker()
172
        self.url_syntax_checker = URLSyntaxChecker()
173
        # WARNING: Put the aggressive one first!
174
        self.extra_rules_handlers = [
175
            SubjectSwitchRulesHandler(),
176
            DNSRulesHandler(),
177
            EToxicHandler(),
178
            ExtraRulesHandler(),
179
        ]
180
        self.db_session = db_session
181

182
        self.params = AvailabilityCheckerParams()
183

184
        self.status = AvailabilityCheckerStatus()
185
        self.status.params = self.params
186
        self.status.dns_lookup_record = self.dns_query_tool.lookup_record
187
        self.status.whois_lookup_record = self.whois_query_tool.lookup_record
188

189
        if use_extra_rules is not None:
190
            self.use_extra_rules = use_extra_rules
191
        else:
192
            self.guess_and_set_use_extra_rules()
193

194
        if use_whois_lookup is not None:
195
            self.use_whois_lookup = use_whois_lookup
196
        else:
197
            self.guess_and_set_use_whois_lookup()
198

199
        if use_dns_lookup is not None:
200
            self.use_dns_lookup = use_dns_lookup
201
        else:
202
            self.guess_and_set_dns_lookup()
203

204
        if use_netinfo_lookup is not None:
205
            self.use_netinfo_lookup = use_netinfo_lookup
206
        else:
207
            self.guess_and_set_use_netinfo_lookup()
208

209
        if use_http_code_lookup is not None:
210
            self.use_http_code_lookup = use_http_code_lookup
211
        else:
212
            self.guess_and_set_use_http_code_lookup()
213

214
        if use_reputation_lookup is not None:
215
            self.use_reputation_lookup = use_reputation_lookup
216
        else:
217
            self.guess_and_set_use_reputation_lookup()
218

219
        if use_whois_db is not None:
220
            self.use_whois_db = use_whois_db
221
        else:
222
            self.guess_and_set_use_whois_db()
223

224
        super().__init__(
225
            subject,
226
            do_syntax_check_first=do_syntax_check_first,
227
            db_session=db_session,
228
            use_collection=use_collection,
229
        )
230

231
    @property
232
    def use_extra_rules(self) -> bool:
233
        """
234
        Provides the current value of the :code:`_use_extra_rules` attribute.
235
        """
236

237
        return self._use_extra_rules
238

239
    @use_extra_rules.setter
240
    def use_extra_rules(self, value: bool) -> None:
241
        """
242
        Sets the value which authorizes the usage of the special rule.
243

244
        :param value:
245
            The value to set.
246

247
        :raise TypeError:
248
            When the given :code:`value` is not a :py:class:`bool`.
249
        """
250

251
        if not isinstance(value, bool):
252
            raise TypeError(f"<value> should be {bool}, {type(value)} given.")
253

254
        self._use_extra_rules = self.params.use_extra_rules = value
255

256
    def set_use_extra_rules(self, value: bool) -> "AvailabilityCheckerBase":
257
        """
258
        Sets the value which authorizes the usage of the special rule.
259

260
        :param value:
261
            The value to set.
262
        """
263

264
        self.use_extra_rules = value
265

266
        return self
267

268
    @property
269
    def use_whois_lookup(self) -> bool:
270
        """
271
        Provides the current value of the :code:`_use_whois_lookup` attribute.
272
        """
273

274
        return self._use_whois_lookup
275

276
    @use_whois_lookup.setter
277
    def use_whois_lookup(self, value: bool) -> None:
278
        """
279
        Sets the value which authorizes the usage of the WHOIS lookup.
280

281
        :param value:
282
            The value to set.
283

284
        :raise TypeError:
285
            When the given :code:`value` is not a :py:class:`bool`.
286
        """
287

288
        if not isinstance(value, bool):
289
            raise TypeError(f"<value> should be {bool}, {type(value)} given.")
290

291
        self._use_whois_lookup = self.params.use_whois_lookup = value
292

293
    def set_use_whois_lookup(self, value: bool) -> "AvailabilityCheckerBase":
294
        """
295
        Sets the value which authorizes the usage of the WHOIS lookup.
296

297
        :param value:
298
            The value to set.
299
        """
300

301
        self.use_whois_lookup = value
302

303
        return self
304

305
    @property
306
    def use_dns_lookup(self) -> bool:
307
        """
308
        Provides the current value of the :code:`_use_dns_lookup` attribute.
309
        """
310

311
        return self._use_dns_lookup
312

313
    @use_dns_lookup.setter
314
    def use_dns_lookup(self, value: bool) -> None:
315
        """
316
        Sets the value which authorizes the usage of the DNS Lookup.
317

318
        :param value:
319
            The value to set.
320

321
        :raise TypeError:
322
            When the given :code:`value` is not a :py:class:`bool`.
323
        """
324

325
        if not isinstance(value, bool):
326
            raise TypeError(f"<value> should be {bool}, {type(value)} given.")
327

328
        self._use_dns_lookup = self.params.use_dns_lookup = value
329

330
    def set_use_dns_lookup(self, value: bool) -> "AvailabilityCheckerBase":
331
        """
332
        Sets the value which authorizes the usage of the DNS Lookup.
333

334
        :param value:
335
            The value to set.
336
        """
337

338
        self.use_dns_lookup = value
339

340
        return self
341

342
    @property
343
    def use_netinfo_lookup(self) -> bool:
344
        """
345
        Provides the current value of the :code:`_use_netinfo_lookup` attribute.
346
        """
347

348
        return self._use_netinfo_lookup
349

350
    @use_netinfo_lookup.setter
351
    def use_netinfo_lookup(self, value: bool) -> None:
352
        """
353
        Sets the value which authorizes the usage of the network information
354
        lookup.
355

356
        :param value:
357
            The value to set.
358

359
        :raise TypeError:
360
            When the given :code:`value` is not a :py:class:`bool`.
361
        """
362

363
        if not isinstance(value, bool):
364
            raise TypeError(f"<value> should be {bool}, {type(value)} given.")
365

366
        self._use_netinfo_lookup = self.params.use_netinfo_lookup = value
367

368
    def set_use_netinfo_lookup(self, value: bool) -> "AvailabilityCheckerBase":
369
        """
370
        Sets the value which authorizes the usage of the network information
371
        lookup.
372

373
        :param value:
374
            The value to set.
375
        """
376

377
        self.use_netinfo_lookup = value
378

379
        return self
380

381
    @property
382
    def use_http_code_lookup(self) -> None:
383
        """
384
        Provides the current value of the :code:`_use_http_code_lookup` attribute.
385
        """
386

387
        return self._use_http_code_lookup
388

389
    @use_http_code_lookup.setter
390
    def use_http_code_lookup(self, value: bool) -> None:
391
        """
392
        Sets the value which authorizes the usage of the HTTP status code
393
        lookup.
394

395
        :param value:
396
            The value to set.
397

398
        :raise TypeError:
399
            When the given :code:`value` is not a :py:class:`bool`.
400
        """
401

402
        if not isinstance(value, bool):
403
            raise TypeError(f"<value> should be {bool}, {type(value)} given.")
404

405
        self._use_http_code_lookup = self.params.use_http_code_lookup = value
406

407
    def set_use_http_code_lookup(self, value: bool) -> "AvailabilityCheckerBase":
408
        """
409
        Sets the value which authorizes the usage of the HTTP status code
410
        lookup.
411

412
        :param value:
413
            The value to set.
414
        """
415

416
        self.use_http_code_lookup = value
417

418
        return self
419

420
    @property
421
    def use_reputation_lookup(self) -> bool:
422
        """
423
        Provides the current value of the :code:`_use_reputation_lookup` attribute.
424
        """
425

426
        return self._use_reputation_lookup
427

428
    @use_reputation_lookup.setter
429
    def use_reputation_lookup(self, value: bool) -> None:
430
        """
431
        Sets the value which authorizes the usage of the reputation
432
        lookup.
433

434
        :param value:
435
            The value to set.
436

437
        :raise TypeError:
438
            When the given :code:`value` is not a :py:class:`bool`.
439
        """
440

441
        if not isinstance(value, bool):
442
            raise TypeError(f"<value> should be {bool}, {type(value)} given.")
443

444
        self._use_reputation_lookup = self.params.use_reputation_lookup = value
445

446
    def set_use_reputation_lookup(self, value: bool) -> "AvailabilityCheckerBase":
447
        """
448
        Sets the value which authorizes the usage of the reputation
449
        lookup.
450

451
        :param value:
452
            The value to set.
453
        """
454

455
        self.use_reputation_lookup = value
456

457
        return self
458

459
    @property
460
    def use_whois_db(self) -> bool:
461
        """
462
        Provides the current value of the :code:`_use_whois_db` attribute.
463
        """
464

465
        return self._use_whois_db
466

467
    @use_whois_db.setter
468
    def use_whois_db(self, value: bool) -> None:
469
        """
470
        Sets the value which authorizes the usage of the WHOIS DB.
471

472
        :param value:
473
            The value to set.
474

475
        :param TypeError:
476
            When the given :code:`use_whois_db` is not a :py:class:`bool`.
477
        """
478

479
        if not isinstance(value, bool):
480
            raise TypeError(f"<value> should be {bool}, {type(value)} given.")
481

482
        self._use_whois_db = self.params.use_whois_db = value
483

484
    def set_use_whois_db(self, value: bool) -> "AvailabilityCheckerBase":
485
        """
486
        Sets the value which authorizes the usage of the WHOIS DB.
487

488
        :param value:
489
            The value to set.
490
        """
491

492
        self.use_whois_db = value
493

494
        return self
495

496
    def subject_propagator(self) -> "CheckerBase":
497
        """
498
        Propagate the currently set subject.
499

500
        .. warning::
501
            You are not invited to run this method directly.
502
        """
503

504
        self.dns_query_tool.set_subject(self.idna_subject)
505
        self.whois_query_tool.set_subject(self.idna_subject)
506
        self.addressinfo_query_tool.set_subject(self.idna_subject)
507
        self.hostbyaddr_query_tool.set_subject(self.idna_subject)
508
        self.http_status_code_query_tool.set_subject(self.idna_subject)
509

510
        self.domain_syntax_checker.subject = self.idna_subject
511
        self.ip_syntax_checker.subject = self.idna_subject
512
        self.url_syntax_checker.subject = self.idna_subject
513

514
        self.status = AvailabilityCheckerStatus()
515
        self.status.params = self.params
516
        self.status.dns_lookup_record = self.dns_query_tool.lookup_record
517
        self.status.whois_lookup_record = self.whois_query_tool.lookup_record
518

519
        return super().subject_propagator()
520

521
    def should_we_continue_test(self, status_post_syntax_checker: str) -> bool:
522
        """
523
        Checks if we are allowed to continue a standard testing.
524

525
        Rules:
526
            1. No status available yet. Continue to next test method.
527
            2. Status is/still INVALID. Continue to next test method.
528
            3. Above are False. Not allowed to continue to next test method.
529
        """
530

531
        if not self.status.status:
532
            return True
533

534
        if (
535
            status_post_syntax_checker == PyFunceble.storage.STATUS.invalid
536
            and self.status.status == PyFunceble.storage.STATUS.invalid
537
        ):
538
            return True
539

540
        return False
541

542
    def guess_and_set_use_extra_rules(self) -> "AvailabilityCheckerBase":
543
        """
544
        Try to guess and set the value of the :code:`use_extra_rules` attribute
545
        from the configuration file.
546
        """
547

548
        if PyFunceble.facility.ConfigLoader.is_already_loaded():
549
            self.use_extra_rules = PyFunceble.storage.CONFIGURATION.lookup.special
550
        else:
551
            self.use_extra_rules = self.STD_USE_EXTRA_RULES
552

553
        return self
554

555
    def guess_and_set_use_whois_lookup(self) -> "AvailabilityCheckerBase":
556
        """
557
        Try to guess and set the value of the :code:`use_whois` attribute
558
        from the configuration file.
559
        """
560

561
        if PyFunceble.facility.ConfigLoader.is_already_loaded():
562
            self.use_whois_lookup = PyFunceble.storage.CONFIGURATION.lookup.whois
563
        else:
564
            self.use_whois_lookup = self.STD_USE_WHOIS_LOOKUP
565

566
        return self
567

568
    def guess_and_set_dns_lookup(self) -> "AvailabilityCheckerBase":
569
        """
570
        Try to guess and set the value of the :code:`use_dns_lookup` attribute
571
        from the configuration file.
572
        """
573

574
        if PyFunceble.facility.ConfigLoader.is_already_loaded():
575
            self.use_dns_lookup = PyFunceble.storage.CONFIGURATION.lookup.dns
576
        else:
577
            self.use_dns_lookup = self.STD_USE_DNS_LOOKUP
578

579
        return self
580

581
    def guess_and_set_use_netinfo_lookup(self) -> "AvailabilityCheckerBase":
582
        """
583
        Try to guess and set the value of the :code:`use_netinfo_lookup` attribute
584
        from the configuration file.
585
        """
586

587
        if PyFunceble.facility.ConfigLoader.is_already_loaded():
588
            self.use_netinfo_lookup = PyFunceble.storage.CONFIGURATION.lookup.netinfo
589
        else:
590
            self.use_netinfo_lookup = self.STD_USE_NETINFO_LOOKUP
591

592
        return self
593

594
    def guess_and_set_use_http_code_lookup(self) -> "AvailabilityCheckerBase":
595
        """
596
        Try to guess and set the value of the :code:`use_http_code_lookup` attribute
597
        from the configuration file.
598
        """
599

600
        if PyFunceble.facility.ConfigLoader.is_already_loaded():
601
            self.use_http_code_lookup = (
602
                PyFunceble.storage.CONFIGURATION.lookup.http_status_code
603
            )
604
        else:
605
            self.use_http_code_lookup = self.STD_USE_HTTP_CODE_LOOKUP
606

607
        return self
608

609
    def guess_and_set_use_reputation_lookup(self) -> "AvailabilityCheckerBase":
610
        """
611
        Try to guess and set the value of the :code:`use_reputation_lookup` attribute
612
        from the configuration file.
613
        """
614

615
        if PyFunceble.facility.ConfigLoader.is_already_loaded():
616
            self.use_reputation_lookup = (
617
                PyFunceble.storage.CONFIGURATION.lookup.reputation
618
            )
619
        else:
620
            self.use_reputation_lookup = self.STD_USE_REPUTATION_LOOKUP
621

622
        return self
623

624
    def guess_and_set_use_whois_db(self) -> "AvailabilityCheckerBase":
625
        """
626
        Try to guess and set the value of the :code:`use_whois_db` attribute.
627
        """
628

629
        if PyFunceble.facility.ConfigLoader.is_already_loaded():
630
            self.use_whois_db = PyFunceble.storage.CONFIGURATION.cli_testing.whois_db
631
        else:
632
            self.use_whois_db = self.STD_USE_WHOIS_DB
633

634
    def guess_all_settings(
635
        self,
636
    ) -> "AvailabilityCheckerBase":  # pragma: no cover ## Method are more important
637
        """
638
        Try to guess all settings.
639
        """
640

641
        to_ignore = ["guess_all_settings"]
642

643
        for method in dir(self):
644
            if method in to_ignore or not method.startswith("guess_"):
645
                continue
646

647
            getattr(self, method)()
648

649
        return self
650

651
    def query_common_checker(self) -> "AvailabilityCheckerBase":
652
        """
653
        Queries the syntax checker.
654
        """
655

656
        PyFunceble.facility.Logger.info(
657
            "Started to check the syntax of %r", self.status.idna_subject
658
        )
659

660
        self.status.second_level_domain_syntax = (
661
            self.domain_syntax_checker.is_valid_second_level()
662
        )
663
        self.status.subdomain_syntax = self.domain_syntax_checker.is_valid_subdomain()
664
        self.status.domain_syntax = bool(self.status.subdomain_syntax) or bool(
665
            self.status.second_level_domain_syntax
666
        )
667

668
        self.status.ipv4_syntax = self.ip_syntax_checker.is_valid_v4()
669
        self.status.ipv6_syntax = self.ip_syntax_checker.is_valid_v6()
670
        self.status.ipv4_range_syntax = self.ip_syntax_checker.is_valid_v4_range()
671
        self.status.ipv6_range_syntax = self.ip_syntax_checker.is_valid_v6_range()
672
        self.status.ip_syntax = bool(self.status.ipv4_syntax or self.status.ipv6_syntax)
673
        self.status.url_syntax = self.url_syntax_checker.is_valid()
674

675
        PyFunceble.facility.Logger.info(
676
            "Finished to check the syntax of %r", self.status.idna_subject
677
        )
678

679
        return super().query_common_checker()
680

681
    @CheckerBase.ensure_subject_is_given
682
    def query_dns_record(self) -> Optional[Dict[str, Optional[List[str]]]]:
683
        """
684
        Tries to query the DNS record(s) of the given subject.
685

686
        .. versionchanged:: 4.1.0b8.dev
687
           Lookup order relative to actual subject.
688
        """
689

690
        PyFunceble.facility.Logger.info(
691
            "Started to try to query the DNS record of %r.",
692
            self.status.idna_subject,
693
        )
694

695
        result = {}
696

697
        if self.status.idna_subject != self.dns_query_tool.subject:
698
            self.domain_syntax_checker.set_subject(self.dns_query_tool.subject)
699
            self.ip_syntax_checker.set_subject(self.dns_query_tool.subject)
700

701
        if self.domain_syntax_checker.is_valid_subdomain():
702
            lookup_order = ["NS", "A", "AAAA", "CNAME", "DNAME"]
703
        elif self.ip_syntax_checker.is_valid():
704
            lookup_order = ["PTR"]
705
        else:
706
            lookup_order = ["NS", "CNAME", "A", "AAAA", "DNAME"]
707

708
        if lookup_order:
709
            for record_type in lookup_order:
710
                local_result = self.dns_query_tool.set_query_record_type(
711
                    record_type
712
                ).query()
713

714
                if local_result:
715
                    result[record_type] = local_result
716

717
                    break
718

719
        if self.status.idna_subject != self.dns_query_tool.subject:
720
            # Switch back subject because we don't want to break subsequential calls.
721
            self.domain_syntax_checker.set_subject(self.status.idna_subject)
722
            self.ip_syntax_checker.set_subject(self.status.idna_subject)
723

724
        PyFunceble.facility.Logger.debug("DNS Record:\n%r", result)
725

726
        PyFunceble.facility.Logger.info(
727
            "Finished to try to query the DNS record of %r",
728
            self.status.idna_subject,
729
        )
730

731
        return result
732

733
    def try_to_query_status_from_whois(
734
        self,
735
    ) -> "AvailabilityCheckerBase":
736
        """
737
        Tries to get and the status from the WHOIS record.
738

739
        .. warning::
740
            If the configuration is loaded, this method try to query from the
741
            best database first.
742

743
            If it's not found it will try to query it from the best WHOIS server
744
            then add it into the database (if the expiration date
745
            extraction is successful).
746

747
        .. note::
748
            The addition into the WHOIS database is only done if this method is
749
            running in a process with a name that does not starts with
750
            :code:`PyFunceble` (case sensitive).
751
        """
752

753
        PyFunceble.facility.Logger.info(
754
            "Started to try to query the status of %r from: WHOIS Lookup",
755
            self.status.idna_subject,
756
        )
757

758
        if (
759
            PyFunceble.facility.ConfigLoader.is_already_loaded() and self.use_whois_db
760
        ):  # pragma: no cover ## Not interesting enough to spend time on it.
761
            whois_object = PyFunceble.checker.utils.whois.get_whois_dataset_object(
762
                db_session=self.db_session
763
            )
764
            known_record = whois_object[self.subject]
765

766
            if known_record and not isinstance(known_record, dict):
767
                # Comes from DB engine.
768
                known_record = known_record.to_dict()
769

770
            if not known_record:
771
                # We assume that expired dataset are never saved into the
772
                # dataset.
773
                self.status.expiration_date = self.whois_query_tool.expiration_date
774
                self.status.registrar = self.whois_query_tool.lookup_record.registrar
775
                self.status.whois_record = self.whois_query_tool.lookup_record.record
776

777
                if (
778
                    self.status.expiration_date
779
                    and not multiprocessing.current_process().name.startswith(
780
                        PyFunceble.storage.PROJECT_NAME.lower()
781
                    )
782
                ):
783
                    whois_object.update(
784
                        {
785
                            "subject": self.subject,
786
                            "idna_subject": self.idna_subject,
787
                            "expiration_date": self.status.expiration_date,
788
                            "epoch": str(
789
                                datetime.strptime(
790
                                    self.status.expiration_date, "%d-%b-%Y"
791
                                ).timestamp()
792
                            ),
793
                        }
794
                    )
795
            else:
796
                self.status.expiration_date = known_record["expiration_date"]
797
                self.status.registrar = known_record["registrar"]
798
                self.status.whois_record = None
799
        else:
800
            self.status.expiration_date = self.whois_query_tool.expiration_date
801
            self.status.registrar = self.whois_query_tool.lookup_record.registrar
802
            self.status.whois_record = self.whois_query_tool.lookup_record.record
803

804
        if self.status.expiration_date:
805
            self.status.status = PyFunceble.storage.STATUS.up
806
            self.status.status_source = "WHOIS"
807

808
            PyFunceble.facility.Logger.info(
809
                "Could define the status of %r from: WHOIS Lookup",
810
                self.status.idna_subject,
811
            )
812

813
        PyFunceble.facility.Logger.info(
814
            "Finished to try to query the status of %r from: WHOIS Lookup",
815
            self.status.idna_subject,
816
        )
817

818
        return self
819

820
    def try_to_query_status_from_dns(self) -> "AvailabilityCheckerBase":
821
        """
822
        Tries to query the status from the DNS lookup.
823

824
        .. versionchanged:: 4.1.0b7.dev
825
           Logging return the correct subject.
826
        """
827

828
        PyFunceble.facility.Logger.info(
829
            "Started to try to query the status of %r from: DNS Lookup",
830
            self.dns_query_tool.subject,
831
        )
832

833
        lookup_result = self.query_dns_record()
834

835
        if lookup_result:
836
            self.status.dns_lookup = lookup_result
837
            self.status.status = PyFunceble.storage.STATUS.up
838
            self.status.status_source = "DNSLOOKUP"
839

840
            PyFunceble.facility.Logger.info(
841
                "Could define the status of %r from: DNS Lookup",
842
                self.dns_query_tool.subject,
843
            )
844

845
        PyFunceble.facility.Logger.info(
846
            "Finished to try to query the status of %r from: DNS Lookup",
847
            self.dns_query_tool.subject,
848
        )
849

850
        return self
851

852
    def try_to_query_status_from_netinfo(self) -> "AvailabilityCheckerBase":
853
        """
854
        Tries to query the status from the network information.
855
        """
856

857
        PyFunceble.facility.Logger.info(
858
            "Started to try to query the status of %r from: NETINFO Lookup",
859
            self.status.idna_subject,
860
        )
861

862
        if self.status.domain_syntax:
863
            lookup_result = self.addressinfo_query_tool.get_info()
864
        elif self.status.ip_syntax:
865
            lookup_result = self.hostbyaddr_query_tool.get_info()
866
        elif self.status.idna_subject.isdigit():
867
            lookup_result = None
868
        else:
869
            lookup_result = self.addressinfo_query_tool.get_info()
870

871
        if lookup_result:
872
            self.status.netinfo = lookup_result
873
            self.status.status = PyFunceble.storage.STATUS.up
874
            self.status.status_source = "NETINFO"
875

876
            PyFunceble.facility.Logger.info(
877
                "Could define the status of %r from: NETINFO Lookup",
878
                self.status.idna_subject,
879
            )
880

881
        PyFunceble.facility.Logger.info(
882
            "Finished to try to query the status of %r from: NETINFO Lookup",
883
            self.status.idna_subject,
884
        )
885

886
        return self
887

888
    def try_to_query_status_from_http_status_code(self) -> "AvailabilityCheckerBase":
889
        """
890
        Tries to query the status from the HTTP status code.
891
        """
892

893
        PyFunceble.facility.Logger.info(
894
            "Started to try to query the status of %r from: HTTP Status code Lookup",
895
            self.status.idna_subject,
896
        )
897

898
        if not self.status.url_syntax and not RegexHelper("[^a-z0-9._]").match(
899
            self.idna_subject, return_match=False
900
        ):
901
            # The regex is there because while testing for domain, sometime we
902
            # may see something like mailto:xxx@yyy.de
903

904
            self.http_status_code_query_tool.set_subject(
905
                f"http://{self.idna_subject}:80"
906
            )
907

908
        lookup_result = self.http_status_code_query_tool.get_status_code()
909

910
        if (
911
            lookup_result
912
            and lookup_result
913
            != self.http_status_code_query_tool.STD_UNKNOWN_STATUS_CODE
914
        ):
915
            self.status.http_status_code = lookup_result
916

917
            if (
918
                PyFunceble.facility.ConfigLoader.is_already_loaded()
919
            ):  # pragma: no cover ## Special behavior.
920
                dataset = PyFunceble.storage.HTTP_CODES
921
            else:
922
                dataset = PyFunceble.storage.STD_HTTP_CODES
923

924
            if (
925
                not self.status.status
926
                or self.status.status == PyFunceble.storage.STATUS.down
927
            ) and (
928
                self.status.http_status_code in dataset.list.up
929
                or self.status.http_status_code in dataset.list.potentially_up
930
            ):
931
                self.status.status = PyFunceble.storage.STATUS.up
932
                self.status.status_source = "HTTP CODE"
933

934
                PyFunceble.facility.Logger.info(
935
                    "Could define the status of %r from: HTTP Status code Lookup",
936
                    self.status.idna_subject,
937
                )
938
        else:
939
            self.status.http_status_code = None
940

941
        PyFunceble.facility.Logger.info(
942
            "Finished to try to query the status of %r from: HTTP Status code Lookup",
943
            self.status.idna_subject,
944
        )
945

946
        return self
947

948
    def try_to_query_status_from_syntax_lookup(self) -> "AvailabilityCheckerBase":
949
        """
950
        Tries to query the status from the syntax.
951
        """
952

953
        PyFunceble.facility.Logger.info(
954
            "Started to try to query the status of %r from: Syntax Lookup",
955
            self.status.idna_subject,
956
        )
957

958
        if (
959
            not self.status.domain_syntax
960
            and not self.status.ip_syntax
961
            and not self.status.url_syntax
962
        ):
963
            self.status.status = PyFunceble.storage.STATUS.invalid
964
            self.status.status_source = "SYNTAX"
965

966
            PyFunceble.facility.Logger.info(
967
                "Could define the status of %r from: Syntax Lookup",
968
                self.status.idna_subject,
969
            )
970

971
        PyFunceble.facility.Logger.info(
972
            "Finished to try to query the status of %r from: Syntax Lookup",
973
            self.status.idna_subject,
974
        )
975

976
        return self
977

978
    def try_to_query_status_from_reputation(self) -> "AvailabilityCheckerBase":
979
        """
980
        Tries to query the status from the reputation lookup.
981
        """
982

983
        raise NotImplementedError()
984

985
    def try_to_query_status_from_collection(self) -> "AvailabilityCheckerBase":
986
        """
987
        Tries to get and set the status from the Collection API.
988
        """
989

990
        PyFunceble.facility.Logger.info(
991
            "Started to try to query the status of %r from: Collection Lookup",
992
            self.status.idna_subject,
993
        )
994

995
        data = self.collection_query_tool.pull(self.idna_subject)
996

997
        if data and "status" in data:
998
            if (
999
                self.collection_query_tool.preferred_status_origin == "frequent"
1000
                and data["status"]["availability"]["frequent"]
1001
            ):
1002
                self.status.status = data["status"]["availability"]["frequent"]
1003
                self.status.status_source = "COLLECTION"
1004
            elif (
1005
                self.collection_query_tool.preferred_status_origin == "latest"
1006
                and data["status"]["availability"]["latest"]
1007
            ):
1008
                self.status.status = data["status"]["availability"]["latest"]["status"]
1009
                self.status.status_source = "COLLECTION"
1010
            elif (
1011
                self.collection_query_tool.preferred_status_origin == "recommended"
1012
                and data["status"]["availability"]["recommended"]
1013
            ):
1014
                self.status.status = data["status"]["availability"]["recommended"]
1015
                self.status.status_source = "COLLECTION"
1016

1017
            PyFunceble.facility.Logger.info(
1018
                "Could define the status of %r from: Collection Lookup",
1019
                self.status.idna_subject,
1020
            )
1021

1022
        PyFunceble.facility.Logger.info(
1023
            "Finished to try to query the status of %r from: Collection Lookup",
1024
            self.status.idna_subject,
1025
        )
1026

1027
        return self
1028

1029
    def try_to_query_status_from_extra_rules(self) -> "AvailabilityCheckerBase":
1030
        """
1031
        Tries to query the status from the extra rules.
1032
        """
1033

1034
        PyFunceble.facility.Logger.info(
1035
            "Started to try to query the status of %r from: Extra Rules Lookup",
1036
            self.status.idna_subject,
1037
        )
1038

1039
        if self.use_extra_rules:
1040
            for rule_handler in self.extra_rules_handlers:
1041
                rule_handler.set_status(self.status).start()
1042

1043
                if self.status.status_after_extra_rules:
1044
                    PyFunceble.facility.Logger.info(
1045
                        "Could define the status of %r from: Extra Rules Lookup",
1046
                        self.status.idna_subject,
1047
                    )
1048
                    break
1049

1050
        PyFunceble.facility.Logger.info(
1051
            "Finished to try to query the status of %r from: Extra Rules Lookup",
1052
            self.status.idna_subject,
1053
        )
1054

1055
        return self
1056

1057
    @CheckerBase.ensure_subject_is_given
1058
    @CheckerBase.update_status_date_after_query
1059
    def query_status(self) -> "AvailabilityCheckerBase":
1060
        """
1061
        Queries the status and for for more action.
1062
        """
1063

1064
        raise NotImplementedError()
1065

1066
    # pylint: disable=useless-super-delegation
1067
    def get_status(self) -> Optional[AvailabilityCheckerStatus]:
1068
        return super().get_status()
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc