forked from matt1/gandi-ddns
-
Notifications
You must be signed in to change notification settings - Fork 0
/
gandi-ddns.py
156 lines (118 loc) · 4.48 KB
/
gandi-ddns.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
from __future__ import print_function
try:
from xmlrpc import client as xmlrpclient
from urllib.request import urlopen
import configparser
except ImportError:
from urllib2 import urlopen
import xmlrpclib as xmlrpclient
import ConfigParser as configparser
import sys
import os
import socket
# Used to cache the zone_id for future calls
zone_id = None
# name of the configuration file.
# If the full path is not given, gandi-ddns.py will check for this file in
# its current directory
config_file = "config.txt"
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
def get_zone_id(config, section):
""" Get the gandi.net ID for the current zone version"""
global zone_id
# If we've not already got the zone ID, get it
if zone_id is None:
# Get domain info then check for a zone
domain_info = api.domain.info(config.get(section, "apikey"),
config.get(section, "domain"))
current_zone_id = domain_info['zone_id']
if current_zone_id == 'None':
print('No zone - make sure domain is set to use \
gandi.net name servers.')
sys.exit(1)
zone_id = current_zone_id
return zone_id
def get_zone_ip(config, section):
"""Get the current IP from the A record in the DNS zone """
current_zone = api.domain.zone.record.list(config.get(section, "apikey"),
get_zone_id(config, section), 0)
ip = '0.0.0.0'
# There may be more than one A record - we're interested in one with
# the specific name (typically @ but could be sub domain)
for d in current_zone:
if d['type'] == 'A'and d['name'] == config.get(section, "a_name"):
ip = d['value']
return ip
def get_ip():
""" Get external IP """
try:
# Could be any service that just gives us a simple raw
# ASCII IP address (not HTML etc)
result = urlopen("https://api.ipify.org", timeout=3).read()
except Exception:
print('Unable to external IP address.')
sys.exit(2)
return result.decode()
def change_zone_ip(config, section, new_ip):
""" Change the zone record to the new IP """
a_name = config.get(section, "a_name")
apikey = config.get(section, "apikey")
ttl = int(config.get(section, "ttl"))
zone_id = get_zone_id(config, section)
zone_record = {'name': a_name, 'value': new_ip, 'ttl': ttl, 'type': 'A'}
new_zone_ver = api.domain.zone.version.new(apikey, zone_id)
# clear old A record (defaults to previous verison's
api.domain.zone.record.delete(apikey, zone_id, new_zone_ver,
{'type': 'A', 'name': a_name})
# Add in new A record
api.domain.zone.record.add(apikey, zone_id, new_zone_ver, zone_record)
# Set new zone version as the active zone
api.domain.zone.version.set(apikey, zone_id, new_zone_ver)
default_config = """[local]
# gandi.net API (Production) key
apikey = <CHANGE ME>
# Domain
domain = <CHANGE ME>
# A-record name
a_name = @
# TTL (seconds = 5 mintes to 30 days)
ttl = 900
# Production API
api = https://rpc.gandi.net/xmlrpc/
# Host which IP should be changed
host = localhost
"""
def read_config(config_path):
""" Open the configuration file or create it if it doesn't exists """
if not os.path.exists(config_path):
with open(config_path, "w") as f:
f.write(default_config)
return None
cfg = configparser.ConfigParser()
cfg.read(config_path)
return cfg
def main():
global api, zone_id
path = config_file
if not path.startswith('/'):
path = os.path.join(SCRIPT_DIR, path)
config = read_config(path)
if not config:
sys.exit("please fill in the 'config.txt' file")
for section in config.sections():
api = xmlrpclient.ServerProxy(config.get(section, "api"), verbose=False)
zone_ip = get_zone_ip(config, section).strip()
current_ip = socket.gethostbyname(config.get(section, "host"))
if current_ip == '127.0.0.1':
current_ip = get_ip()
if (zone_ip.strip() == current_ip.strip()):
sys.exit()
else:
print('DNS Mistmatch detected: A-record: ',
zone_ip, ' WAN IP: ', current_ip)
change_zone_ip(config, section, current_ip)
zone_id = None
zone_ip = get_zone_ip(config, section)
print('DNS A record update complete - set to ', zone_ip)
if __name__ == "__main__":
main()