-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
180 additions
and
133 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# https://editorconfig.org/ | ||
|
||
root = true | ||
|
||
[*] | ||
indent_style = space | ||
indent_size = 4 | ||
insert_final_newline = true | ||
trim_trailing_whitespace = true | ||
end_of_line = lf | ||
charset = utf-8 | ||
|
||
# Docstrings and comments use max_line_length = 79 | ||
[*.py] | ||
max_line_length = 119 | ||
|
||
# Use 2 spaces for the HTML files | ||
[*.html] | ||
indent_size = 2 | ||
|
||
# The JSON files contain newlines inconsistently | ||
[*.json] | ||
indent_size = 2 | ||
insert_final_newline = ignore |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
// Use IntelliSense to learn about possible attributes. | ||
// Hover to view descriptions of existing attributes. | ||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||
"version": "0.2.0", | ||
"configurations": [{ | ||
"name": "Python: Current File", | ||
"type": "python", | ||
"request": "launch", | ||
"program": "${file}", | ||
"console": "integratedTerminal" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"python.pythonPath": "/usr/bin/python3" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,131 +1,130 @@ | ||
#!/usr/bin/env python | ||
# -*- coding: utf-8 -*- | ||
|
||
import concurrent.futures | ||
import base64 | ||
import io | ||
from struct import unpack | ||
import re | ||
|
||
import requests | ||
from requests import ConnectTimeout | ||
from requests import ReadTimeout | ||
from tabulate import tabulate | ||
import maxminddb | ||
|
||
from libs.log import logger | ||
|
||
reader = maxminddb.open_database("GeoLite2-Country.mmdb") | ||
|
||
def parse_resolvers(content): | ||
result = re.findall(r"^##.+?(?P<resolver>.+$)(?P<description>(\n|.)+?)(?P<stamp>^sdns.+)", | ||
content, re.M) | ||
if result is None: | ||
return None | ||
|
||
resolvers = [] | ||
for r in result: | ||
# Skip sdns:// | ||
|
||
stamp = r[3][7:] | ||
|
||
# FIX Padding. | ||
stamp += "=" * ((4 - len(stamp) % 4) % 4) | ||
decoded_stamp = base64.urlsafe_b64decode(stamp) | ||
|
||
stream = io.BytesIO(decoded_stamp) | ||
# https://github.com/jedisct1/dnscrypt-proxy/wiki/stamps | ||
|
||
flag = unpack("B", stream.read(1))[0] | ||
|
||
# Parse DNS-over-HTTPS only. | ||
if flag != 0x02: | ||
continue | ||
|
||
resolver = {} | ||
|
||
resolver["name"] = r[0] | ||
resolver["ip_address"] = "" | ||
|
||
props = unpack("Q", stream.read(8))[0] | ||
|
||
_len = unpack("B", stream.read(1))[0] | ||
if _len != 0: | ||
# can be empty. | ||
ip_address = stream.read(_len) | ||
resolver["ip_address"] = ip_address.decode() | ||
|
||
# https://github.com/jedisct1/dnscrypt-proxy/blob/master/vendor/github.com/jedisct1/go-dnsstamps/dnsstamps.go#L159 | ||
while True: | ||
vlen = unpack("B", stream.read(1))[0] | ||
_len = vlen & (~0x80) | ||
if _len > 0: | ||
hashes = stream.read(_len) | ||
|
||
if (vlen & 0x80) != 0x80: | ||
break | ||
|
||
_len = unpack("B", stream.read(1))[0] | ||
host = None | ||
if _len != 0: | ||
host = stream.read(_len) | ||
|
||
_len = unpack("B", stream.read(1))[0] | ||
path = None | ||
if _len != 0: | ||
path = stream.read(_len) | ||
|
||
resolver["url"] = f"https://{host.decode()}{path.decode()}" | ||
resolvers.append(resolver) | ||
|
||
return resolvers | ||
|
||
|
||
def test_resolver(resolver): | ||
logger.debug(f"Querying {resolver['name']}") | ||
try: | ||
params = { | ||
"name": "dl.google.com" | ||
} | ||
r = requests.get(resolver["url"], params=params, timeout=2) | ||
resolver["latency"] = r.elapsed.total_seconds() * 1000 | ||
|
||
for answer in r.json()["Answer"]: | ||
if answer["type"] == 1: | ||
ip = answer["data"] | ||
country = reader.get(ip) | ||
resolver["google"] = f"{ip}({country['country']['iso_code']})" | ||
break | ||
except (ConnectTimeout, ReadTimeout): | ||
resolver["latency"] = "timeout" | ||
return resolver | ||
|
||
|
||
def main(): | ||
content = open("public-resolvers.md", encoding="utf-8").read() | ||
resolvers = parse_resolvers(content) | ||
|
||
ipv4_resolvers = [] | ||
for resolver in resolvers: | ||
ip_address = resolver["ip_address"] | ||
if len(ip_address): | ||
# Ignore ipv6 | ||
if ip_address[0] == "[": | ||
continue | ||
ipv4_resolvers.append(resolver) | ||
|
||
result = [] | ||
with concurrent.futures.ThreadPoolExecutor() as executor: | ||
future_list = {executor.submit(test_resolver, resolver): resolver for resolver in ipv4_resolvers} | ||
for future in concurrent.futures.as_completed(future_list): | ||
try: | ||
if future.result(): | ||
result.append(future.result()) | ||
except Exception as exc: | ||
pass | ||
|
||
print(tabulate(result, headers = "keys", tablefmt="github")) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() | ||
#!/usr/bin/env python3 | ||
# -*- coding: utf-8 -*- | ||
|
||
import concurrent.futures | ||
import base64 | ||
import io | ||
from struct import unpack | ||
import re | ||
|
||
import requests | ||
from requests import ConnectTimeout | ||
from requests import ReadTimeout | ||
from tabulate import tabulate | ||
import maxminddb | ||
|
||
from libs.log import logger | ||
|
||
reader = maxminddb.open_database("GeoLite2-Country.mmdb") | ||
|
||
def parse_resolvers(content): | ||
result = re.findall(r"^##.+?(?P<resolver>.+$)(?P<description>(\n|.)+?)(?P<stamp>^sdns.+)", | ||
content, re.M) | ||
if result is None: | ||
return None | ||
|
||
resolvers = [] | ||
for r in result: | ||
# Skip sdns:// | ||
stamp = r[3][7:] | ||
|
||
# FIX Padding. | ||
stamp += "=" * ((4 - len(stamp) % 4) % 4) | ||
decoded_stamp = base64.urlsafe_b64decode(stamp) | ||
|
||
stream = io.BytesIO(decoded_stamp) | ||
# https://github.com/jedisct1/dnscrypt-proxy/wiki/stamps | ||
|
||
flag = unpack("B", stream.read(1))[0] | ||
|
||
# Parse DNS-over-HTTPS only. | ||
if flag != 0x02: | ||
continue | ||
|
||
resolver = {} | ||
|
||
resolver["name"] = r[0] | ||
resolver["ip_address"] = "" | ||
|
||
props = unpack("Q", stream.read(8))[0] | ||
|
||
_len = unpack("B", stream.read(1))[0] | ||
if _len != 0: | ||
# can be empty. | ||
ip_address = stream.read(_len) | ||
resolver["ip_address"] = ip_address.decode() | ||
|
||
# https://github.com/jedisct1/dnscrypt-proxy/blob/master/vendor/github.com/jedisct1/go-dnsstamps/dnsstamps.go#L159 | ||
while True: | ||
vlen = unpack("B", stream.read(1))[0] | ||
_len = vlen & (~0x80) | ||
if _len > 0: | ||
hashes = stream.read(_len) | ||
|
||
if (vlen & 0x80) != 0x80: | ||
break | ||
|
||
_len = unpack("B", stream.read(1))[0] | ||
host = None | ||
if _len != 0: | ||
host = stream.read(_len) | ||
|
||
_len = unpack("B", stream.read(1))[0] | ||
path = None | ||
if _len != 0: | ||
path = stream.read(_len) | ||
|
||
resolver["url"] = f"https://{host.decode()}{path.decode()}" | ||
resolvers.append(resolver) | ||
|
||
return resolvers | ||
|
||
|
||
def test_resolver(resolver): | ||
logger.debug(f"Querying {resolver['name']}") | ||
try: | ||
params = { | ||
"name": "dl.google.com" | ||
} | ||
r = requests.get(resolver["url"], params=params, timeout=2) | ||
resolver["latency(ms)"] = int(r.elapsed.total_seconds() * 1000) | ||
|
||
for answer in r.json()["Answer"]: | ||
if answer["type"] == 1: | ||
ip = answer["data"] | ||
country = reader.get(ip) | ||
resolver["google"] = f"{ip}({country['country']['iso_code']})" | ||
break | ||
except (ConnectTimeout, ReadTimeout): | ||
resolver["latency(ms)"] = "timeout" | ||
return resolver | ||
|
||
|
||
def main(): | ||
content = open("public-resolvers.md", encoding="utf-8").read() | ||
resolvers = parse_resolvers(content) | ||
|
||
ipv4_resolvers = [] | ||
for resolver in resolvers: | ||
ip_address = resolver["ip_address"] | ||
if len(ip_address): | ||
# Ignore ipv6 | ||
if ip_address[0] == "[": | ||
continue | ||
ipv4_resolvers.append(resolver) | ||
|
||
result = [] | ||
with concurrent.futures.ThreadPoolExecutor() as executor: | ||
future_list = {executor.submit(test_resolver, resolver): resolver for resolver in ipv4_resolvers} | ||
for future in concurrent.futures.as_completed(future_list): | ||
try: | ||
if future.result(): | ||
result.append(future.result()) | ||
except Exception as exc: | ||
pass | ||
|
||
print(tabulate(result, headers = "keys", tablefmt="github")) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |