Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

"""Check if the domains is reported as malicious in Quad9 database"""

import logging
from typing import Tuple
from urllib.parse import urlparse

import requests
Expand All @@ -14,8 +12,6 @@

from ..dns_responses import malicious_detector_response

logger = logging.getLogger(__name__)


class Quad9MaliciousDetector(classes.ObservableAnalyzer):
"""Check if a domain is malicious by Quad9 public resolver.
Expand All @@ -40,7 +36,7 @@ def run(self):
if self.observable_classification == self.ObservableTypes.URL:
observable = urlparse(self.observable_name).hostname

quad9_answer, timeout = self._quad9_dns_query(observable)
quad9_answer = self._quad9_dns_query(observable)
# if Quad9 has not an answer the site could be malicious
if not quad9_answer:
# Google dns request
Expand All @@ -50,38 +46,35 @@ def run(self):
if google_answer:
return malicious_detector_response(self.observable_name, True)

return malicious_detector_response(self.observable_name, False, timeout)
return malicious_detector_response(self.observable_name, False)

def _quad9_dns_query(self, observable) -> Tuple[bool, bool]:
def _quad9_dns_query(self, observable) -> bool:
"""Perform a DNS query with Quad9 service, return True if Quad9 answer the
DNS query with a non-empty response.

:param observable: domain to resolve
:type observable: str
:return: True in case of answer for the DNS query else False.
:rtype: bool
"""
answer_found = False
timeout = False
params = {"name": observable}

quad9_response = requests.get(
self.QUAD9_URL, headers=self.HEADERS, params=params
)
if quad9_response.status_code == 503:
msg = (
"503 status code! "
"It may be normal for this service to"
" happen from time to time"
)
logger.info(msg)
self.report.errors.append(msg)
timeout = True
return answer_found, timeout
quad9_response.raise_for_status()
answer_found = bool(quad9_response.json().get("Answer", None))

return answer_found, timeout
# sometimes it can respond with 503, I suppose to avoid DoS.
# In 1k requests just 20 fails and at least with 30 requests between 2 failures
# with 2 or 3 attemps the analyzer should get the data
attempt_number = 3
for attempt in range(0, attempt_number):
try:
quad9_response = requests.get(
self.QUAD9_URL, headers=self.HEADERS, params=params, timeout=10
)
except requests.exceptions.ConnectionError as exception:
# if the last attempt fails, raise an error
if attempt == attempt_number - 1:
raise exception
else:
quad9_response.raise_for_status()
break

return bool(quad9_response.json().get("Answer", None))

def _google_dns_query(self, observable) -> bool:
"""Perform a DNS query with Google service, return True if Google answer the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@
# See the file 'LICENSE' for copying permission.

"""Quad9 DNS resolutions"""

from urllib.parse import urlparse

import requests

from api_app.analyzers_manager import classes
from api_app.analyzers_manager.exceptions import AnalyzerRunException
from tests.mock_utils import MockUpResponse, if_mock_connections, patch

from ..dns_responses import dns_resolver_response
Expand All @@ -20,23 +18,33 @@ class Quad9DNSResolver(classes.ObservableAnalyzer):
query_type: str

def run(self):
try:
observable = self.observable_name
# for URLs we are checking the relative domain
if self.observable_classification == "url":
observable = urlparse(self.observable_name).hostname

headers = {"Accept": "application/dns-json"}
url = "https://dns.quad9.net:5053/dns-query"
params = {"name": observable, "type": self.query_type}

quad9_response = requests.get(url, headers=headers, params=params)
quad9_response.raise_for_status()
resolutions = quad9_response.json().get("Answer", [])
except requests.RequestException:
raise AnalyzerRunException(
"an error occurred during the connection to Quad9"
)
observable = self.observable_name
# for URLs we are checking the relative domain
if self.observable_classification == "url":
observable = urlparse(self.observable_name).hostname

url = "https://dns.quad9.net:5053/dns-query"
headers = {"Accept": "application/dns-json"}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be class attributes (and url should be named base_url if i remember correctly) so that the healthcheck could work automatically

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made the var a class attr. I checked the code about the health check and should work unfortunately a head request to the url get a 415 as response (I also tried removing the port or just the base url ans didn't work, always a > 400 status code).

params = {"name": observable, "type": self.query_type}

# sometimes it can respond with 503, I suppose to avoid DoS.
# In 1k requests just 20 fails and at least with 30 requests between 2 failures
# with 2 or 3 attemps the analyzer should get the data
attempt_number = 3
for attempt in range(0, attempt_number):
try:
quad9_response = requests.get(
url, headers=headers, params=params, timeout=10
)
except requests.exceptions.ConnectionError as exception:
# if the last attempt fails, raise an error
if attempt == attempt_number - 1:
raise exception
else:
quad9_response.raise_for_status()
break

resolutions = quad9_response.json().get("Answer", [])

return dns_resolver_response(self.observable_name, resolutions)

Expand Down