From c7a1fb4141d2da4e4095fe82f8f0e84a03297162 Mon Sep 17 00:00:00 2001 From: Bigsk Date: Sat, 4 Jun 2022 19:25:08 +0800 Subject: [PATCH] First Commit --- ntp_daemon.py | 283 ++++++++++++++++++++++++++++++++++++++++++++++++ pool.txt | 123 +++++++++++++++++++++ requirement.txt | 3 + 3 files changed, 409 insertions(+) create mode 100644 ntp_daemon.py create mode 100644 pool.txt create mode 100644 requirement.txt diff --git a/ntp_daemon.py b/ntp_daemon.py new file mode 100644 index 0000000..d1e24b1 --- /dev/null +++ b/ntp_daemon.py @@ -0,0 +1,283 @@ +import socket, struct, threading, time, json, logging + +# flask, requests +import flask, requests + +RECORD_UPDATE_SWITCH = True +API_LISTEN = ("0.0.0.0", 5000) +INTERVAL = 60 * 60 +DOMAIN = "" +A_ID = "" +A_KEY = "" + +app = flask.Flask("NTP") +log = logging.getLogger('werkzeug') +log.disabled = True + +if RECORD_UPDATE_SWITCH: + # alibabacloud_alidns20150109==2.0.2 + from alibabacloud_alidns20150109.client import Client as Alidns20150109Client + from alibabacloud_tea_openapi import models as open_api_models + from alibabacloud_alidns20150109 import models as alidns_20150109_models + from alibabacloud_tea_util import models as util_models + + class Domain: + def __init__(self): + pass + + @staticmethod + def create_client( + access_key_id: str, + access_key_secret: str, + ) -> Alidns20150109Client: + config = open_api_models.Config( + access_key_id=access_key_id, + access_key_secret=access_key_secret + ) + config.endpoint = f'alidns.cn-hangzhou.aliyuncs.com' + return Alidns20150109Client(config) + + @staticmethod + def get_records(): + client = Domain.create_client(A_ID, A_KEY) + describe_domain_records_request = alidns_20150109_models.DescribeDomainRecordsRequest( + domain_name=DOMAIN + ) + runtime = util_models.RuntimeOptions() + try: + return True, eval(str(client.describe_domain_records_with_options(describe_domain_records_request, runtime)))["body"]["DomainRecords"]["Record"] + except Exception as error: + return False, error + + @staticmethod + def delete_record(rr) -> None: + client = Domain.create_client(A_ID, A_KEY) + delete_sub_domain_records_request = alidns_20150109_models.DeleteSubDomainRecordsRequest( + domain_name=DOMAIN, + rr=rr + ) + runtime = util_models.RuntimeOptions() + try: + return True, eval(str(client.describe_domain_records_with_options(client.delete_sub_domain_records_with_options(delete_sub_domain_records_request, runtime)))) + except Exception as error: + return False, error + + @staticmethod + def add_record(rr, type, value, line) -> None: + client = Domain.create_client(A_ID, A_KEY) + add_domain_record_request = alidns_20150109_models.AddDomainRecordRequest( + domain_name=DOMAIN, + rr=rr, + type=type, + value=value, + line=line + ) + runtime = util_models.RuntimeOptions() + try: + return True, eval(str(client.add_domain_record_with_options(add_domain_record_request, runtime))) + except Exception as error: + return False, error + +def is_ipv4(ip): + try: + socket.inet_pton(socket.AF_INET, ip) + except AttributeError: + try: + socket.inet_aton(ip) + except socket.error: + return False + return ip.count('.') == 3 + except socket.error: + return False + return True + + +def is_ipv6(ip): + try: + socket.inet_pton(socket.AF_INET6, ip) + except socket.error: + return False + return True + +def ip_type(ip): + if is_ipv4(ip) and not is_ipv6(ip): + return 4 + elif is_ipv6(ip) and not is_ipv4(ip): + return 6 + else: + return 0 + +def ip_area(ip): + type = ip_type(ip) + if type == 4 or type == 6: + info = requests.get(f"https://api.ghink.net/ip/zh_cn/v{type}/{ip}").json() + if info["code"] != 20000: + return "default" + info = info["content"] + if type == 4: + if info["pro"] == "": + return "oversea" + elif "教育" in info["addr"] or "学院" in info["addr"] or "大学" in info["addr"]: + return "edu" + elif "电信" in info["addr"]: + return "telecom" + elif "移动" in info["addr"] or "移通" in info["addr"]: + return "mobile" + elif "联通" in info["addr"]: + return "unicom" + else: + return "default" + elif type == 6: + if info["pro"] == "" and info["city"] == "": + return "oversea" + elif "教育" in info["addr"] or "学院" in info["addr"] or "大学" in info["addr"]: + return "edu" + elif "电信" in info["addr"]: + return "telecom" + elif "移动" in info["addr"] or "移通" in info["addr"]: + return "mobile" + elif "联通" in info["addr"]: + return "unicom" + else: + return "default" + else: + return "default" + +def ntp(server): + type = ip_type(server) + if type == 4: + client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + elif type == 6: + client = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) + else: + client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + data = ("\x1b" + 47 * "\0").encode() + try: + client.sendto(data, (server, 123)) + except socket.gaierror: + return False + data, address = client.recvfrom(1024) + if data: + t = struct.unpack('!12I', data)[10] + t -= 2208988800 + return t + else: + return False + +def check(server): + global active + if ntp(server): + if server not in active: + active.append(server) + else: + if server in active: + del active[active.index(server)] + +def update_status(): + global active, servers + # Read Server Pool + servers = [] + with open("pool.txt", "r") as fb: + servers = [s.strip() for s in fb.readlines()] + # Duplicate Removal + temp = [] + for s in servers: + if s not in temp: + temp.append(s) + servers = temp + with open("pool.txt", "w+") as fb: + for s in servers: + fb.write(f"{s}\n") + # Check + for s in active: + thread = threading.Thread(target=check, args=(s, ), name=s) + thread.start() + thread.join(1) + with open("pool.txt", "r") as fb: + for line in fb.readlines(): + thread = threading.Thread(target=check, args=(line.strip(), ), name=line.strip()) + thread.start() + thread.join(1) + +def update_status_daemon(): + while True: + update_status() + time.sleep(INTERVAL) + +def update_records(): + ok, records = Domain.get_records() + if ok: + for r in records: + if r["Value"] not in servers: + ok, msg = Domain.delete_record(r["Value"]) + if not ok: + print(msg) + for s in active: + flag = True + for r in records: + if r["Value"] == s: + flag = False + if flag: + ok, msg = Domain.add_record("@", "A" if ip_type(s) == 4 else "AAAA", s, ip_area(s)) + if not ok and "The maximum number of 10 records is exceeded" not in str(msg) and "The DNS record already exists." not in str(msg): + print(msg) + else: + print(records) + +def update_records_daemon(): + while True: + update_records() + time.sleep(10) + +@app.route("/update") +def update_api(): + update_thread = threading.Thread(target=update_status, name="Update From API For Status") + update_thread.daemon = True + update_thread.start() + if RECORD_UPDATE_SWITCH: + update_thread = threading.Thread(target=update_records, name="Update From API For Records") + update_thread.daemon = True + update_thread.start() + return json.dumps( + { + "message": "Success", + "content": None + } + ), 200, {"content-type": "application/json"} + +@app.route("/list") +def list_api(): + return json.dumps( + { + "message": "Success", + "content": active + } + ), 200, {"content-type": "application/json"} + +if __name__ == '__main__': + active = [] + servers = [] + + update_status_daemon_thread = threading.Thread(target=update_status_daemon, name="Update Daemon For Status") + update_status_daemon_thread.daemon = True + update_status_daemon_thread.start() + if RECORD_UPDATE_SWITCH: + update_status_daemon_thread = threading.Thread(target=update_records_daemon, name="Update Daemon For Records") + update_status_daemon_thread.daemon = True + update_status_daemon_thread.start() + + api_daemon = threading.Thread(target=app.run, args=API_LISTEN, name="API Daemon") + api_daemon.daemon = True + api_daemon.start() + + while True: + if input() == "exit": + break + print(f"Report {time.ctime(time.time())}:") + print(f"{len(active)} Servers Active, They are:") + for i in range(len(active)): + if not (i+1) % 5: + print(active[i]) + else: + print(active[i], end=" ") + print("", end="" if not len(active) % 5 or not len(active) else "\n") \ No newline at end of file diff --git a/pool.txt b/pool.txt new file mode 100644 index 0000000..e284302 --- /dev/null +++ b/pool.txt @@ -0,0 +1,123 @@ +103.11.143.248 +120.25.115.20 +116.13.10.10 +203.107.6.88 +202.112.29.82 +120.25.108.11 +182.92.12.11 +85.199.214.100 +101.6.6.172 +216.218.254.202 +66.228.42.59 +211.233.40.78 +158.69.48.97 +203.114.74.17 +2402:f000:1:416:101:6:6:172 +2001:250:380A:5::10 +2001:da8:9000::81 +2001:da8:9000::130 +185.209.85.222 +94.130.49.186 +84.16.67.12 +78.46.102.180 +193.182.111.143 +202.118.1.81 +108.59.2.24 +94.237.64.20 +219.216.128.25 +119.28.183.184 +162.159.200.123 +111.230.189.174 +178.215.228.24 +119.28.206.193 +193.182.111.12 +193.182.111.142 +45.77.144.44 +167.172.70.21 +192.33.214.47 +171.66.97.126 +213.206.184.75 +104.194.8.227 +69.197.128.202 +178.63.9.212 +135.125.169.44 +61.239.100.196 +45.125.1.20 +142.147.88.111 +149.202.91.39 +147.182.158.78 +185.216.231.84 +161.53.78.71 +152.115.59.242 +192.48.105.15 +95.215.175.2 +103.147.22.149 +168.61.215.74 +129.250.35.251 +128.4.24.98 +202.118.1.130 +129.250.35.250 +95.216.192.15 +133.243.238.243 +103.104.28.105 +45.132.84.104 +103.13.114.65 +203.99.62.214 +161.200.192.4 +58.176.194.96 +162.159.200.1 +202.12.97.45 +110.170.126.102 +121.174.142.81 +17.253.116.253 +202.28.93.5 +194.0.5.123 +80.241.0.72 +192.46.210.39 +139.199.214.202 +203.147.59.17 +23.106.249.200 +202.28.116.236 +13.209.84.50 +203.131.222.11 +212.138.72.43 +17.253.84.253 +212.138.72.41 +95.216.144.226 +64.227.167.110 +202.21.176.62 +202.65.114.202 +103.38.215.205 +212.26.18.43 +203.113.174.44 +36.91.114.86 +223.113.120.195 +223.113.97.98 +223.65.211.46 +36.154.179.82 +223.113.103.191 +58.220.133.132 +114.67.237.130 +114.67.103.73 +140.143.99.185 +111.230.50.201 +106.75.185.63 +119.29.26.206 +120.197.116.202 +202.112.31.197 +149.129.123.30 +106.186.122.232 +106.187.100.179 +133.100.11.8 +106.247.248.106 +202.73.57.107 +128.199.134.40 +218.186.3.36 +188.166.245.58 +208.53.158.34 +131.188.3.220 +131.188.3.223 +166.111.8.28 +166.111.8.29 +2402:f000:1:801::8:28 +2402:f000:1:801::8:29 diff --git a/requirement.txt b/requirement.txt new file mode 100644 index 0000000..3412608 --- /dev/null +++ b/requirement.txt @@ -0,0 +1,3 @@ +flask +alibabacloud_alidns20150109==2.0.2 +requests \ No newline at end of file