Skip to content

Commit 82b6689

Browse files
committed
Feature : AlephDNS
1 parent 66a53af commit 82b6689

File tree

5 files changed

+180
-0
lines changed

5 files changed

+180
-0
lines changed

setup.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ testing =
7979
black
8080
isort
8181
flake8
82+
aiodns
8283
mqtt =
8384
aiomqtt<=0.1.3
8485
certifi

src/aleph/sdk/conf.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ class Settings(BaseSettings):
3333

3434
CODE_USES_SQUASHFS: bool = which("mksquashfs") is not None # True if command exists
3535

36+
# Dns resolver
37+
IPFS_DOMAINS = "ipfs.public.aleph.sh"
38+
PROGRAM_DOMAINS = "program.public.aleph.sh"
39+
ROOT_DOMAIN = "static.public.aleph.sh"
40+
RESOLVERS = ["1.1.1.1", "8.8.8.8"]
41+
3642
class Config:
3743
env_prefix = "ALEPH_"
3844
case_sensitive = False

src/aleph/sdk/domain.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import aiodns
2+
import re
3+
from .conf import settings
4+
from typing import Optional
5+
from aleph.sdk.exceptions import DomainConfigurationError
6+
7+
8+
class AlephDNS:
9+
def __init__(self):
10+
self.resolver = aiodns.DNSResolver(servers=settings.RESOLVERS)
11+
self.fqdn_matcher = re.compile(r"https?://?")
12+
13+
async def query(self, name: str, query_type: str):
14+
try:
15+
return await self.resolver.query(name, query_type)
16+
except Exception as e:
17+
print(e)
18+
return None
19+
20+
def url_to_domain(self, url):
21+
return self.fqdn_matcher.sub("", url).strip().strip("/")
22+
23+
async def get_ipv6_address(self, url: str):
24+
domain = self.url_to_domain(url)
25+
ipv6 = []
26+
query = await self.query(domain, "AAAA")
27+
if query:
28+
for entry in query:
29+
ipv6.append(entry.host)
30+
return ipv6
31+
32+
async def get_dnslink(self, url: str):
33+
domain = self.url_to_domain(url)
34+
query = await self.query(f"_dnslink.{domain}", "TXT")
35+
if query is not None and len(query) > 0:
36+
return query[0].text
37+
38+
async def get_control(self, url: str):
39+
domain = self.url_to_domain(url)
40+
query = await self.query(f"_control.{domain}", "TXT")
41+
if query is not None and len(query) > 0:
42+
return query[0].text
43+
44+
async def check_domain_configured(self, domain, _type, owner):
45+
try:
46+
print("Check...", _type)
47+
return await self.check_domain(domain, _type, owner)
48+
except Exception as error:
49+
raise DomainConfigurationError(error)
50+
51+
async def check_domain(self, url: str, _type: str, owner: Optional[str] = None):
52+
# if _type.lower() == 'ipfs':
53+
return await self.check_ipfs_domain(url, _type, owner)
54+
# elif _type.lower() == 'program':
55+
# pass
56+
57+
async def check_ipfs_domain(
58+
self, url: str, _type: str, owner: Optional[str] = None
59+
):
60+
status = {"cname": True, "owner_proof": False}
61+
62+
_type = _type.lower()
63+
domain = self.url_to_domain(url)
64+
65+
if _type == "ipfs":
66+
status["delegation"] = False
67+
68+
# check1: CNAME value should be ipfs or program
69+
res = await self.query(domain, "CNAME")
70+
if _type.lower() == "ipfs":
71+
expected_value = settings.IPFS_DOMAINS
72+
else:
73+
expected_value = settings.PROGRAM_DOMAINS
74+
75+
assert_error = (
76+
f"CNAME record not found: {domain}",
77+
f"Create a CNAME record for {domain} with values {expected_value}",
78+
status,
79+
)
80+
81+
assert res is not None, assert_error
82+
assert hasattr(res, "cname"), assert_error
83+
84+
assert_error = (
85+
f"{domain} should have a valid CNAME value, {res.cname} provided",
86+
f"Create a CNAME record for {domain} with values {expected_value}",
87+
status,
88+
)
89+
assert res.cname in expected_value, assert_error
90+
status["cname"] = True
91+
92+
if _type.lower() == "ipfs":
93+
# check2: CNAME value of _dnslink.__custom_domain__
94+
# should be _dnslink.__custom_domain__.static.public.aleph.sh
95+
res = await self.query(f"_dnslink.{domain}", "CNAME")
96+
97+
expected_value = f"_dnslink.{domain}.{settings.ROOT_DOMAIN}"
98+
assert_error = (
99+
f"CNAME record not found: _dnslink.{domain}",
100+
f"Create a CNAME record for _dnslink.{domain} with value: {expected_value}",
101+
status,
102+
)
103+
104+
assert res is not None, assert_error
105+
assert hasattr(res, "cname"), assert_error
106+
assert res.cname == expected_value, assert_error
107+
status["delegation"] = True
108+
109+
# check3: TXT value of _control.__custom_domain__ should be the address of the owner
110+
owner_address = await self.get_control(domain)
111+
assert_error = (
112+
f"TXT record not found: _control.{domain}",
113+
f'Create a TXT record for _control.{domain} with value = "owner address"',
114+
status,
115+
)
116+
assert owner_address is not None, assert_error
117+
118+
if owner is not None:
119+
assert owner_address == owner, (
120+
f"Owner address mismatch, got: {owner} expected: {owner_address}",
121+
f"",
122+
status,
123+
)
124+
status["owner_proof"] = True
125+
126+
return status

src/aleph/sdk/exceptions.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,8 @@ class FileTooLarge(Exception):
5050
"""
5151

5252
pass
53+
54+
55+
class DomainConfigurationError(Exception):
56+
"Raised when the domain checks are not satisfied"
57+
pass

tests/unit/test_domains.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import pytest
2+
import asyncio
3+
4+
from aleph.sdk.domain import AlephDNS
5+
6+
7+
@pytest.mark.asyncio
8+
async def test_url_to_domain():
9+
alephdns = AlephDNS()
10+
domain = alephdns.url_to_domain("https://aleph.im")
11+
query = await alephdns.query(domain, "A")
12+
assert query is not None
13+
assert len(query) > 0
14+
assert hasattr(query[0], "host")
15+
16+
17+
@pytest.mark.asyncio
18+
async def test_get_ipv6_adress():
19+
alephdns = AlephDNS()
20+
url = "https://aleph.im"
21+
ipv6_address = await alephdns.get_ipv6_address(url)
22+
assert ipv6_address is not None
23+
assert len(ipv6_address) > 0
24+
assert ":" in ipv6_address[0]
25+
26+
27+
@pytest.mark.asyncio
28+
async def test_dnslink():
29+
alephdns = AlephDNS()
30+
url = "https://aleph.im"
31+
dnslink = await alephdns.get_dnslink(url)
32+
assert dnslink is not None
33+
34+
35+
"""
36+
@pytest.mark.asyncio
37+
async def test_cname():
38+
alephdns = AlephDNS()
39+
url = 'https://custom_domain_test.aleph.sh'
40+
check = await alephdns.custom_domain_check(url)
41+
assert check is not None
42+
"""

0 commit comments

Comments
 (0)