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")