This repository has been archived on 2025-12-03. You can view files and clone it, but cannot push or open issues or pull requests.
cloudflare-ddns/cloudflare-ddns.py
2020-08-05 13:30:33 -04:00

128 lines
4.1 KiB
Python
Executable File

import requests, json, sys, os
import time, traceback
PATH = os.getcwd() + "/"
version = float(str(sys.version_info[0]) + "." + str(sys.version_info[1]))
if(version < 3.5):
raise Exception("This script requires Python 3.5+")
with open(PATH + "config.json") as config_file:
config = json.loads(config_file.read())
def getIPs():
a = requests.get("https://api.ipify.org?format=json").json().get("ip")
aaaa = requests.get("https://api6.ipify.org?format=json").json().get("ip")
ips = []
if(a.find(".") > -1):
ips.append({
"type": "A",
"ip": a
})
if(aaaa.find(":") > -1):
ips.append({
"type": "AAAA",
"ip": aaaa
})
return ips
def commitRecord(ip):
stale_record_ids = []
for c in config["cloudflare"]:
subdomains = c["subdomains"]
response = cf_api("zones/" + c['zone_id'], "GET", c)
base_domain_name = response["result"]["name"]
for subdomain in subdomains:
exists = False
record = {
"type": ip["type"],
"name": subdomain,
"content": ip["ip"],
"proxied": c["proxied"]
}
list = cf_api(
"zones/" + c['zone_id'] + "/dns_records?per_page=100&type=" + ip["type"], "GET", c)
full_subdomain = base_domain_name
if subdomain:
full_subdomain = subdomain + "." + full_subdomain
dns_id = ""
for r in list["result"]:
if (r["name"] == full_subdomain):
exists = True
if (r["content"] != ip["ip"]):
if (dns_id == ""):
dns_id = r["id"]
else:
stale_record_ids.append(r["id"])
if(exists == False):
print("Adding new record " + str(record))
response = cf_api(
"zones/" + c['zone_id'] + "/dns_records", "POST", c, {}, record)
elif(dns_id != ""):
# Only update if the record content is different
print("Updating record " + str(record))
response = cf_api(
"zones/" + c['zone_id'] + "/dns_records/" + dns_id, "PUT", c, {}, record)
# Delete duplicate, stale records
for identifier in stale_record_ids:
print("Deleting stale record " + str(identifier))
response = cf_api(
"zones/" + c['zone_id'] + "/dns_records/" + identifier, "DELETE", c)
return True
def cf_api(endpoint, method, config, headers={}, data=False):
api_token = config['authentication']['api_token']
if api_token != '' and api_token != 'api_token_here':
headers = {
"Authorization": "Bearer " + api_token,
**headers
}
else:
headers = {
"X-Auth-Email": config['authentication']['api_key']['account_email'],
"X-Auth-Key": config['authentication']['api_key']['api_key'],
}
if(data == False):
response = requests.request(
method, "https://api.cloudflare.com/client/v4/" + endpoint, headers=headers)
else:
response = requests.request(
method, "https://api.cloudflare.com/client/v4/" + endpoint, headers=headers, json=data)
return response.json()
def every(delay, task):
next_time = time.time() + delay
while True:
time.sleep(max(0, next_time - time.time()))
try:
task()
except Exception:
traceback.print_exc()
# in production code you might want to have this instead of course:
# logger.exception("Problem while executing repetitive task.")
# skip tasks if we are behind schedule:
next_time += (time.time() - next_time) // delay * delay + delay
def updateIPs():
for ip in getIPs():
print("Checking " + ip["type"] + " records")
commitRecord(ip)
if(len(sys.argv) > 1):
if(sys.argv[1] == "--repeat"):
import threading
threading.Thread(target=lambda: every(60*15, updateIPs)).start()
updateIPs()