First Commit
This commit is contained in:
parent
058045ec95
commit
c7a1fb4141
283
ntp_daemon.py
Normal file
283
ntp_daemon.py
Normal file
@ -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")
|
123
pool.txt
Normal file
123
pool.txt
Normal file
@ -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
|
3
requirement.txt
Normal file
3
requirement.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
flask
|
||||||
|
alibabacloud_alidns20150109==2.0.2
|
||||||
|
requests
|
Reference in New Issue
Block a user