Commit 49a3a992 authored by Romain Courteaud's avatar Romain Courteaud

Report PTR record

Allow empty ptr
parent f2c18185
...@@ -27,6 +27,7 @@ from .dns import ( ...@@ -27,6 +27,7 @@ from .dns import (
getDomainIpDict, getDomainIpDict,
reportDnsQuery, reportDnsQuery,
packDns, packDns,
reverseIp,
) )
from .domain import ( from .domain import (
queryWhois, queryWhois,
...@@ -135,8 +136,8 @@ def filterWarningStatus( ...@@ -135,8 +136,8 @@ def filterWarningStatus(
for i in range(len(status_dict["dns_query"]) - 1, -1, -1): for i in range(len(status_dict["dns_query"]) - 1, -1, -1):
state = status_dict["dns_query"][i]["response"] state = status_dict["dns_query"][i]["response"]
if state == "": if state == "":
if status_dict["dns_query"][i]["rdtype"] in ("MX", "TXT"): if status_dict["dns_query"][i]["rdtype"] in ("MX", "TXT", "PTR"):
# No MX/TXT is allowed # No MX/TXT/PTR is allowed
# XXX report empty SPF! # XXX report empty SPF!
del status_dict["dns_query"][i] del status_dict["dns_query"][i]
else: else:
...@@ -334,6 +335,16 @@ class WebBot: ...@@ -334,6 +335,16 @@ class WebBot:
self._db, status_id, resolver_ip_list, domain_list, "TXT", timeout self._db, status_id, resolver_ip_list, domain_list, "TXT", timeout
) )
# Query PTR record
getDomainIpDict(
self._db,
status_id,
resolver_ip_list,
[reverseIp(x) for x in server_ip_dict.keys()],
"PTR",
timeout,
)
# Check TCP port for the list of IP found # Check TCP port for the list of IP found
# XXX For now, check http/https only # XXX For now, check http/https only
server_ip_list = [x for x in server_ip_dict.keys()] server_ip_list = [x for x in server_ip_dict.keys()]
...@@ -582,6 +593,26 @@ class WebBot: ...@@ -582,6 +593,26 @@ class WebBot:
} }
) )
# Report PTR
# Report list of DNS query
query = reportDnsQuery(
self._db,
domain=[reverseIp(x) for x in server_ip_dict.keys()],
resolver_ip=resolver_ip_list,
rdtype=["PTR"],
)
for dns_change in query.dicts().iterator():
# XXX Duplicated code
result_dict["dns_query"].append(
{
"domain": dns_change["domain"],
"rdtype": dns_change["rdtype"],
"resolver_ip": dns_change["resolver_ip"],
"date": rfc822(dns_change["status"]),
"response": dns_change["response"],
}
)
# Report the list of CDN status # Report the list of CDN status
query = reportNetwork( query = reportNetwork(
self._db, self._db,
...@@ -662,6 +693,7 @@ class WebBot: ...@@ -662,6 +693,7 @@ class WebBot:
# Confirm that redirection url are checked # Confirm that redirection url are checked
if network_change["status_code"] in (301, 302, 303): if network_change["status_code"] in (301, 302, 303):
# XXX check full url
redirect_url = getRootUrl( redirect_url = getRootUrl(
network_change["http_header_dict"]["Location"] network_change["http_header_dict"]["Location"]
) )
......
...@@ -21,6 +21,7 @@ from dns import ( ...@@ -21,6 +21,7 @@ from dns import (
resolver as dns_resolver, resolver as dns_resolver,
name as dns_name, name as dns_name,
exception as dns_exception, exception as dns_exception,
reversename as dns_reversename,
) )
from .network import logNetwork from .network import logNetwork
from peewee import fn from peewee import fn
...@@ -102,9 +103,13 @@ def buildResolver(resolver_ip, timeout): ...@@ -102,9 +103,13 @@ def buildResolver(resolver_ip, timeout):
return resolver return resolver
def reverseIp(ip):
return dns_reversename.from_address(ip)
def queryDNS(db, status_id, resolver_ip, domain_text, rdtype, timeout=TIMEOUT): def queryDNS(db, status_id, resolver_ip, domain_text, rdtype, timeout=TIMEOUT):
# only A (and AAAA) has address property # only A (and AAAA) has address property
assert rdtype in ["A", "MX", "TXT"], rdtype assert rdtype in ["A", "MX", "TXT", "PTR"], rdtype
resolver = buildResolver(resolver_ip, timeout) resolver = buildResolver(resolver_ip, timeout)
try: try:
...@@ -115,7 +120,9 @@ def queryDNS(db, status_id, resolver_ip, domain_text, rdtype, timeout=TIMEOUT): ...@@ -115,7 +120,9 @@ def queryDNS(db, status_id, resolver_ip, domain_text, rdtype, timeout=TIMEOUT):
else ( else (
x.exchange.derelativize(domain_text).to_text()[:-1] x.exchange.derelativize(domain_text).to_text()[:-1]
if (rdtype == "MX") if (rdtype == "MX")
else x.to_text() else (
x.to_text()[:-1] if (rdtype == "PTR") else x.to_text()
)
) )
) )
for x in resolver.query( for x in resolver.query(
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
import unittest import unittest
from surykatka.bot import WebBot from surykatka.bot import WebBot
import mock import mock
from test_dns import MockAnswerA, MockAnswerMX, MockAnswerTXT from test_dns import MockAnswerA, MockAnswerMX, MockAnswerTXT, MockAnswerPTR
from test_domain import MockAnswer as MockWhoisAnswer from test_domain import MockAnswer as MockWhoisAnswer
import surykatka.dns import surykatka.dns
...@@ -160,6 +160,7 @@ class SurykatkaBotTestCase(unittest.TestCase): ...@@ -160,6 +160,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
[MockAnswerA("1.2.3.4")], [MockAnswerA("1.2.3.4")],
[MockAnswerMX("")], [MockAnswerMX("")],
[MockAnswerTXT("")], [MockAnswerTXT("")],
[MockAnswerPTR("example.org")],
] ]
mock_create_default_context.return_value.wrap_socket.return_value.getpeercert.side_effect = [ mock_create_default_context.return_value.wrap_socket.return_value.getpeercert.side_effect = [
b"", b"",
...@@ -179,7 +180,7 @@ class SurykatkaBotTestCase(unittest.TestCase): ...@@ -179,7 +180,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
bot.iterateLoop() bot.iterateLoop()
assert mock_whois.call_count == 1 assert mock_whois.call_count == 1
assert mock_query.call_count == 4 assert mock_query.call_count == 5
assert mock_socket.call_count == 4 assert mock_socket.call_count == 4
assert mock_create_default_context.call_count == 1 assert mock_create_default_context.call_count == 1
assert mock_request.call_count == 2 assert mock_request.call_count == 2
...@@ -199,6 +200,7 @@ class SurykatkaBotTestCase(unittest.TestCase): ...@@ -199,6 +200,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
[ [
(resolver_ip, "example.org", "A"), (resolver_ip, "example.org", "A"),
(resolver_ip, "example.org", "MX"), (resolver_ip, "example.org", "MX"),
(resolver_ip, "4.3.2.1.in-addr.arpa.", "PTR"),
(resolver_ip, "example.org", "TXT"), (resolver_ip, "example.org", "TXT"),
], ],
) )
...@@ -268,6 +270,8 @@ class SurykatkaBotTestCase(unittest.TestCase): ...@@ -268,6 +270,8 @@ class SurykatkaBotTestCase(unittest.TestCase):
[MockAnswerMX("")], [MockAnswerMX("")],
[MockAnswerTXT("")], [MockAnswerTXT("")],
[MockAnswerTXT("")], [MockAnswerTXT("")],
[MockAnswerPTR("example.org.")],
[MockAnswerPTR("example.org.")],
] ]
mock_create_default_context.return_value.wrap_socket.return_value.getpeercert.side_effect = [ mock_create_default_context.return_value.wrap_socket.return_value.getpeercert.side_effect = [
b"", b"",
...@@ -282,7 +286,7 @@ class SurykatkaBotTestCase(unittest.TestCase): ...@@ -282,7 +286,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
bot.iterateLoop() bot.iterateLoop()
assert mock_whois.call_count == 1 assert mock_whois.call_count == 1
assert mock_query.call_count == 8 assert mock_query.call_count == 10
assert mock_socket.call_count == 4 assert mock_socket.call_count == 4
assert mock_create_default_context.call_count == 1 assert mock_create_default_context.call_count == 1
assert mock_request.call_count == 2 assert mock_request.call_count == 2
...@@ -307,6 +311,8 @@ class SurykatkaBotTestCase(unittest.TestCase): ...@@ -307,6 +311,8 @@ class SurykatkaBotTestCase(unittest.TestCase):
(resolver_ip_2, "example.org", "A"), (resolver_ip_2, "example.org", "A"),
(resolver_ip, "example.org", "MX"), (resolver_ip, "example.org", "MX"),
(resolver_ip_2, "example.org", "MX"), (resolver_ip_2, "example.org", "MX"),
(resolver_ip, "4.3.2.1.in-addr.arpa.", "PTR"),
(resolver_ip_2, "4.3.2.1.in-addr.arpa.", "PTR"),
(resolver_ip, "example.org", "TXT"), (resolver_ip, "example.org", "TXT"),
(resolver_ip_2, "example.org", "TXT"), (resolver_ip_2, "example.org", "TXT"),
], ],
...@@ -375,6 +381,7 @@ class SurykatkaBotTestCase(unittest.TestCase): ...@@ -375,6 +381,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
[MockAnswerMX("")], [MockAnswerMX("")],
[MockAnswerTXT("")], [MockAnswerTXT("")],
[MockAnswerTXT("")], [MockAnswerTXT("")],
[MockAnswerPTR("example.org.")],
] ]
mock_create_default_context.return_value.wrap_socket.return_value.getpeercert.side_effect = [ mock_create_default_context.return_value.wrap_socket.return_value.getpeercert.side_effect = [
b"", b"",
...@@ -396,7 +403,7 @@ class SurykatkaBotTestCase(unittest.TestCase): ...@@ -396,7 +403,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
bot.iterateLoop() bot.iterateLoop()
assert mock_whois.call_count == 1 assert mock_whois.call_count == 1
assert mock_query.call_count == 7 assert mock_query.call_count == 8
assert mock_socket.call_count == 5 assert mock_socket.call_count == 5
assert mock_create_default_context.call_count == 2 assert mock_create_default_context.call_count == 2
assert mock_request.call_count == 4 assert mock_request.call_count == 4
...@@ -420,6 +427,7 @@ class SurykatkaBotTestCase(unittest.TestCase): ...@@ -420,6 +427,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
(resolver_ip, domain_2, "A"), (resolver_ip, domain_2, "A"),
(resolver_ip, domain_1, "MX"), (resolver_ip, domain_1, "MX"),
(resolver_ip, domain_2, "MX"), (resolver_ip, domain_2, "MX"),
(resolver_ip, "4.3.2.1.in-addr.arpa.", "PTR"),
(resolver_ip, domain_1, "TXT"), (resolver_ip, domain_1, "TXT"),
(resolver_ip, domain_2, "TXT"), (resolver_ip, domain_2, "TXT"),
], ],
...@@ -488,6 +496,8 @@ class SurykatkaBotTestCase(unittest.TestCase): ...@@ -488,6 +496,8 @@ class SurykatkaBotTestCase(unittest.TestCase):
[MockAnswerA("1.2.3.4"), MockAnswerA("1.2.3.5"),], [MockAnswerA("1.2.3.4"), MockAnswerA("1.2.3.5"),],
[MockAnswerMX("")], [MockAnswerMX("")],
[MockAnswerTXT("")], [MockAnswerTXT("")],
[MockAnswerPTR(domain)],
[MockAnswerPTR(domain)],
] ]
mock_create_default_context.return_value.wrap_socket.return_value.getpeercert.side_effect = [ mock_create_default_context.return_value.wrap_socket.return_value.getpeercert.side_effect = [
b"", b"",
...@@ -509,7 +519,7 @@ class SurykatkaBotTestCase(unittest.TestCase): ...@@ -509,7 +519,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
bot.iterateLoop() bot.iterateLoop()
assert mock_whois.call_count == 1 assert mock_whois.call_count == 1
assert mock_query.call_count == 4 assert mock_query.call_count == 6
assert mock_socket.call_count == 8 assert mock_socket.call_count == 8
assert mock_create_default_context.call_count == 2 assert mock_create_default_context.call_count == 2
assert mock_request.call_count == 4 assert mock_request.call_count == 4
...@@ -534,6 +544,8 @@ class SurykatkaBotTestCase(unittest.TestCase): ...@@ -534,6 +544,8 @@ class SurykatkaBotTestCase(unittest.TestCase):
[ [
(resolver_ip, domain, "A"), (resolver_ip, domain, "A"),
(resolver_ip, domain, "MX"), (resolver_ip, domain, "MX"),
(resolver_ip, "4.3.2.1.in-addr.arpa.", "PTR"),
(resolver_ip, "5.3.2.1.in-addr.arpa.", "PTR"),
(resolver_ip, domain, "TXT"), (resolver_ip, domain, "TXT"),
], ],
) )
...@@ -606,6 +618,7 @@ class SurykatkaBotTestCase(unittest.TestCase): ...@@ -606,6 +618,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
[MockAnswerMX("")], [MockAnswerMX("")],
[MockAnswerTXT("")], [MockAnswerTXT("")],
[MockAnswerTXT("")], [MockAnswerTXT("")],
[MockAnswerTXT(domain)],
] ]
mock_create_default_context.return_value.wrap_socket.return_value.getpeercert.side_effect = [ mock_create_default_context.return_value.wrap_socket.return_value.getpeercert.side_effect = [
b"", b"",
...@@ -627,7 +640,7 @@ class SurykatkaBotTestCase(unittest.TestCase): ...@@ -627,7 +640,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
bot.iterateLoop() bot.iterateLoop()
assert mock_whois.call_count == 1 assert mock_whois.call_count == 1
assert mock_query.call_count == 7 assert mock_query.call_count == 8
assert mock_socket.call_count == 5 assert mock_socket.call_count == 5
assert mock_create_default_context.call_count == 2 assert mock_create_default_context.call_count == 2
assert mock_request.call_count == 4 assert mock_request.call_count == 4
...@@ -651,6 +664,7 @@ class SurykatkaBotTestCase(unittest.TestCase): ...@@ -651,6 +664,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
(resolver_ip, sub_domain, "A"), (resolver_ip, sub_domain, "A"),
(resolver_ip, domain, "MX"), (resolver_ip, domain, "MX"),
(resolver_ip, sub_domain, "MX"), (resolver_ip, sub_domain, "MX"),
(resolver_ip, "4.3.2.1.in-addr.arpa.", "PTR"),
(resolver_ip, domain, "TXT"), (resolver_ip, domain, "TXT"),
(resolver_ip, sub_domain, "TXT"), (resolver_ip, sub_domain, "TXT"),
], ],
...@@ -721,6 +735,7 @@ class SurykatkaBotTestCase(unittest.TestCase): ...@@ -721,6 +735,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
[MockAnswerA("1.2.3.4")], [MockAnswerA("1.2.3.4")],
[MockAnswerMX("")], [MockAnswerMX("")],
[MockAnswerTXT("")], [MockAnswerTXT("")],
[MockAnswerPTR(domain)],
] ]
mock_create_default_context.return_value.wrap_socket.return_value.getpeercert.side_effect = [ mock_create_default_context.return_value.wrap_socket.return_value.getpeercert.side_effect = [
b"", b"",
...@@ -735,7 +750,7 @@ class SurykatkaBotTestCase(unittest.TestCase): ...@@ -735,7 +750,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
bot.iterateLoop() bot.iterateLoop()
assert mock_whois.call_count == 1 assert mock_whois.call_count == 1
assert mock_query.call_count == 4 assert mock_query.call_count == 5
assert mock_socket.call_count == 4 assert mock_socket.call_count == 4
assert mock_create_default_context.call_count == 1 assert mock_create_default_context.call_count == 1
assert mock_request.call_count == 2 assert mock_request.call_count == 2
...@@ -758,6 +773,7 @@ class SurykatkaBotTestCase(unittest.TestCase): ...@@ -758,6 +773,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
(resolver_ip, "example.org", "A"), (resolver_ip, "example.org", "A"),
(resolver_ip, sub_domain, "A"), (resolver_ip, sub_domain, "A"),
(resolver_ip, sub_domain, "MX"), (resolver_ip, sub_domain, "MX"),
(resolver_ip, "4.3.2.1.in-addr.arpa.", "PTR"),
(resolver_ip, sub_domain, "TXT"), (resolver_ip, sub_domain, "TXT"),
], ],
) )
...@@ -821,6 +837,7 @@ class SurykatkaBotTestCase(unittest.TestCase): ...@@ -821,6 +837,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
[MockAnswerA("1.2.3.4")], [MockAnswerA("1.2.3.4")],
[MockAnswerMX("")], [MockAnswerMX("")],
[MockAnswerTXT("")], [MockAnswerTXT("")],
[MockAnswerPTR("example.org.")],
] ]
mock_create_default_context.return_value.wrap_socket.return_value.getpeercert.side_effect = [ mock_create_default_context.return_value.wrap_socket.return_value.getpeercert.side_effect = [
b"", b"",
...@@ -834,7 +851,7 @@ class SurykatkaBotTestCase(unittest.TestCase): ...@@ -834,7 +851,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
bot.iterateLoop() bot.iterateLoop()
assert mock_whois.call_count == 1 assert mock_whois.call_count == 1
assert mock_query.call_count == 4 assert mock_query.call_count == 5
assert mock_socket.call_count == 4 assert mock_socket.call_count == 4
assert mock_create_default_context.call_count == 1 assert mock_create_default_context.call_count == 1
assert mock_request.call_count == 3 assert mock_request.call_count == 3
...@@ -856,6 +873,7 @@ class SurykatkaBotTestCase(unittest.TestCase): ...@@ -856,6 +873,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
[ [
(resolver_ip, domain, "A"), (resolver_ip, domain, "A"),
(resolver_ip, domain, "MX"), (resolver_ip, domain, "MX"),
(resolver_ip, "4.3.2.1.in-addr.arpa.", "PTR"),
(resolver_ip, domain, "TXT"), (resolver_ip, domain, "TXT"),
], ],
) )
......
...@@ -29,6 +29,7 @@ from surykatka.dns import ( ...@@ -29,6 +29,7 @@ from surykatka.dns import (
queryDNS, queryDNS,
getReachableResolverList, getReachableResolverList,
getDomainIpDict, getDomainIpDict,
reverseIp,
) )
from surykatka.status import logStatus from surykatka.status import logStatus
import mock import mock
...@@ -53,11 +54,31 @@ class MockAnswerTXT(object): ...@@ -53,11 +54,31 @@ class MockAnswerTXT(object):
return self.text return self.text
class MockAnswerPTR(MockAnswerTXT):
def __init__(self, text):
super().__init__(text)
class SurykatkaDNSTestCase(unittest.TestCase): class SurykatkaDNSTestCase(unittest.TestCase):
def setUp(self): def setUp(self):
self.db = LogDB(":memory:") self.db = LogDB(":memory:")
self.db.createTables() self.db.createTables()
################################################
# reverseIp
################################################
def test_reverseIp(self):
with mock.patch(
"surykatka.dns.dns_reversename.from_address"
) as mock_from_address:
mock_from_address.return_value = "1.foo.bar."
result = reverseIp("1")
assert mock_from_address.call_count == 1
mock_from_address.assert_called_with("1")
assert result == "1.foo.bar."
################################################ ################################################
# expandDomainList # expandDomainList
################################################ ################################################
...@@ -354,6 +375,31 @@ class SurykatkaDNSTestCase(unittest.TestCase): ...@@ -354,6 +375,31 @@ class SurykatkaDNSTestCase(unittest.TestCase):
assert self.db.DnsChange.get().status_id == status_id assert self.db.DnsChange.get().status_id == status_id
assert result == ['"foo=bar"', '"v=spf1 -all"'] assert result == ['"foo=bar"', '"v=spf1 -all"']
def test_queryDNS_PTR(self):
resolver_ip = "127.0.0.1"
domain = "example.org"
rdtype = "PTR"
status_id = logStatus(self.db, "foo")
with mock.patch(
"surykatka.dns.dns_resolver.Resolver.query"
) as mock_query:
mock_query.return_value = [MockAnswerPTR("foo.bar.")]
result = queryDNS(self.db, status_id, resolver_ip, domain, rdtype)
assert mock_query.call_count == 1
mock_query.assert_called_with(
domain, rdtype, raise_on_no_answer=False
)
assert self.db.DnsChange.select().count() == 1
assert self.db.DnsChange.get().resolver_ip == resolver_ip
assert self.db.DnsChange.get().domain == domain
assert self.db.DnsChange.get().rdtype == rdtype
assert self.db.DnsChange.get().response == "foo.bar"
assert self.db.DnsChange.get().status_id == status_id
assert result == ["foo.bar"]
def test_queryDNS_rejectRdtype(self): def test_queryDNS_rejectRdtype(self):
resolver_ip = "127.0.0.1" resolver_ip = "127.0.0.1"
domain = "example.org" domain = "example.org"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment