first commit
This commit is contained in:
10
example_config.json
Normal file
10
example_config.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ip_server":"https://api.ipify.org",
|
||||
"dns_endpoint":"https://dns.eu.ovhapis.com/nic/update",
|
||||
"username":"username",
|
||||
"password":"password",
|
||||
"domains":[
|
||||
"example.com"
|
||||
],
|
||||
"delay":3600
|
||||
}
|
||||
45
readme.md
Normal file
45
readme.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# VYDNS
|
||||
|
||||
A simple DynDNS v2 client written in Python, compatible with providers such as OVH DynHost.
|
||||
|
||||
## Features
|
||||
|
||||
- Periodically checks the public IP address
|
||||
- Updates DNS records only when the IP changes
|
||||
- Uses a local cache to avoid unnecessary updates
|
||||
- No external dependencies except `requests` module
|
||||
|
||||
## Usage
|
||||
|
||||
Run the script with Python. It will periodically:
|
||||
|
||||
- Query a web endpoint that returns the public IP in plain text
|
||||
- Compare it with the cached IP
|
||||
- If the IP has not changed, sleep for the configured delay
|
||||
- If the IP has changed, update the DNS records and refresh the cache
|
||||
- If no cache exists, create it and perform an initial update
|
||||
|
||||
## Configuration
|
||||
|
||||
The script relies on a JSON configuration file named `config.json` located in the working directory.
|
||||
|
||||
It must contain the following keys:
|
||||
|
||||
- `ip_server`: URL of the endpoint used to retrieve the public IP (must return plain text)
|
||||
- `dns_endpoint`: DynDNS v2 API endpoint of the provider
|
||||
- `username`: DynDNS account username
|
||||
- `password`: DynDNS account password
|
||||
- `domains`: list of domain names to update
|
||||
- `delay`: interval in seconds between checks
|
||||
|
||||
You can find an example configuration file in this repo.
|
||||
**Warning:** the password is stored in plain text. Changing config file permissions is recommended.
|
||||
|
||||
## Supported DynDNS providers
|
||||
|
||||
To this day, these DynDNS providers have been found to work well with the script:
|
||||
- OVH
|
||||
|
||||
## License
|
||||
|
||||
This script and the provided examples are under the MIT license.
|
||||
90
vydns.py
Normal file
90
vydns.py
Normal file
@@ -0,0 +1,90 @@
|
||||
import requests
|
||||
import os
|
||||
import json
|
||||
import ipaddress
|
||||
import time
|
||||
def recreate_cache(ip_server:str):
|
||||
print("[VYDNS] Obtaining public IP using server '"+ip_server+"'")
|
||||
ip=""
|
||||
try:
|
||||
ip=requests.get(ip_server,timeout=10).text.strip()
|
||||
ipaddress.ip_address(ip)
|
||||
except Exception as e:
|
||||
print("[VYDNS] Error: counldn't obtain IP or IP isn't valid. Exception: "+str(e))
|
||||
exit()
|
||||
print("[VYDNS] "+ip)
|
||||
cache={"ip":ip}
|
||||
with open("cache.json","w") as f:
|
||||
json.dump(cache,f)
|
||||
print("[VYDNS] Succesfully obtained IP.")
|
||||
return ip
|
||||
def obtain_ip(ip_server:str):
|
||||
print("[VYDNS] Obtaining public IP using server '"+ip_server+"'")
|
||||
ip=""
|
||||
try:
|
||||
ip=requests.get(ip_server,timeout=10).text.strip()
|
||||
ipaddress.ip_address(ip)
|
||||
except Exception as e:
|
||||
print("[VYDNS] Error: counldn't obtain IP or IP isn't valid. Exception: "+str(e))
|
||||
exit()
|
||||
print("[VYDNS] "+ip)
|
||||
print("[VYDNS] Succesfully obtained IP.")
|
||||
return ip
|
||||
def update_ip(ip:str,dns_endpoint:str,domains:str,username:str,password:str):
|
||||
print("[VYDNS] Updating "+str(len(domains))+" domains:")
|
||||
for d in domains:
|
||||
print("[VYDNS] Updating '"+d+"' :")
|
||||
params={
|
||||
"system":"dyndns",
|
||||
"hostname":d,
|
||||
"myip":ip
|
||||
}
|
||||
r=requests.get(dns_endpoint,params=params,auth=(username,password),timeout=10)
|
||||
print(r.text)
|
||||
if "good" in r.text or "nochg" in r.text:
|
||||
print("[VYDNS] Update OK.")
|
||||
else:
|
||||
print("[VYDNS] Update failed.")
|
||||
conffile={}
|
||||
if os.path.exists("config.json"):
|
||||
conffile=json.load(open("config.json","r"))
|
||||
else:
|
||||
print("[VYDNS] Error: config file doesn't exist.")
|
||||
exit()
|
||||
if ("ip_server" not in conffile) or ("dns_endpoint" not in conffile) or ("username" not in conffile) or ("password" not in conffile) or ("domains" not in conffile) or ("delay" not in conffile):
|
||||
print("[VYDNS] Error: config file isn't formatted as expected.")
|
||||
exit()
|
||||
if (conffile["ip_server"]=="") or (conffile["dns_endpoint"]=="") or (conffile["username"]=="") or (conffile["password"]=="") or (conffile["delay"]<=0):
|
||||
print("[VYDNS] Error: one of the key is empty or invalid.")
|
||||
exit()
|
||||
if (conffile["domains"]==[]):
|
||||
print("[VYDNS] Error: no domains provided.")
|
||||
exit()
|
||||
while True:
|
||||
obtained_ip=""
|
||||
if not os.path.exists("cache.json"):
|
||||
print("[VYDNS] Cache file doesn't exist, recreating it.")
|
||||
obtained_ip=recreate_cache(conffile["ip_server"])
|
||||
update_ip(obtained_ip,conffile["dns_endpoint"],conffile["domains"],conffile["username"],conffile["password"])
|
||||
print("[VYDNS] Sleeping for "+str(conffile["delay"])+" seconds.")
|
||||
time.sleep(conffile["delay"])
|
||||
continue
|
||||
else:
|
||||
print("[VYDNS] Cache file exist, obtaining current IP for comparaison.")
|
||||
obtained_ip=obtain_ip(conffile["ip_server"])
|
||||
with open("cache.json","r") as f:
|
||||
cachefile=json.load(f)
|
||||
if cachefile["ip"]==obtained_ip:
|
||||
print("[VYDNS] According to cache file, IP didn't change.")
|
||||
print("[VYDNS] Sleeping for "+str(conffile["delay"])+" seconds.")
|
||||
time.sleep(conffile["delay"])
|
||||
continue
|
||||
else:
|
||||
print("[VYDNS] According to cache file, IP did changed.")
|
||||
update_ip(obtained_ip,conffile["dns_endpoint"],conffile["domains"],conffile["username"],conffile["password"])
|
||||
cache={"ip":obtained_ip}
|
||||
with open("cache.json","w") as f:
|
||||
json.dump(cache,f)
|
||||
print("[VYDNS] Sleeping for "+str(conffile["delay"])+" seconds.")
|
||||
time.sleep(conffile["delay"])
|
||||
continue
|
||||
Reference in New Issue
Block a user