@@ 0,0 1,162 @@
+#!/usr/bin/env python3
+#
+# Update GoDaddy DNS "A" Record.
+#
+# usage: godaddy_ddns.py [-h] [--version] [--ip IP] [--key KEY]
+# [--secret SECRET] [--ttl TTL] [--force]
+# hostname
+#
+# positional arguments:
+# hostname DNS fully-qualified host name with an 'A' record. If the hostname consists of only a domain name
+# (i.e., it contains only one period), the record for '@' is updated.
+#
+# optional arguments:
+# -h, --help show this help message and exit
+# --version show program's version number and exit
+# --ip IP DNS Address (defaults to public WAN address from http://ipv4.icanhazip.com/)
+# --key KEY GoDaddy production key
+# --secret SECRET GoDaddy production secret
+# --ttl TTL DNS TTL.
+# --force force update of GoDaddy DNS record even if DNS query indicates that record is already correct
+#
+# GoDaddy customers can obtain values for the KEY and SECRET arguments by creating a production key at
+# https://developer.godaddy.com/keys/.
+#
+# Note that command line arguments may be specified in a FILE, one to a line, by instead giving
+# the argument "%FILE". For security reasons, it is particularly recommended to supply the
+# KEY and SECRET arguments in such a file, rather than directly on the command line:
+#
+# Create a file named, e.g., `godaddy-ddns.config` with the content:
+# MY.FULLY.QUALIFIED.HOSTNAME.COM
+# --key
+# MY-KEY-FROM-GODADDY
+# --secret
+# MY-SECRET-FROM-GODADDY
+#
+# Then just invoke `godaddy-ddns %godaddy-ddns.config`
+
+prog='godaddy-ddns'
+version='0.4'
+author='Carl Edman (CarlEdman@gmail.com)'
+
+import sys, json, argparse, socket
+
+if sys.version_info > (3,):
+ from urllib.request import urlopen, Request
+ from urllib.error import URLError, HTTPError
+else:
+ from urllib2 import urlopen, Request
+ from urllib2 import URLError, HTTPError
+
+parser = argparse.ArgumentParser(description='Update GoDaddy DNS "A" Record.', fromfile_prefix_chars='%', epilog= \
+'''GoDaddy customers can obtain values for the KEY and SECRET arguments by creating a production key at
+https://developer.godaddy.com/keys/.
+
+Note that command line arguments may be specified in a FILE, one to a line, by instead giving
+the argument "%FILE". For security reasons, it is particularly recommended to supply the
+KEY and SECRET arguments in such a file, rather than directly on the command line.''')
+
+parser.add_argument('--version', action='version',
+ version='{} {}'.format(prog, version))
+
+parser.add_argument('hostname', type=str,
+ help='DNS fully-qualified host name with an A record. If the hostname consists of only a domain name (i.e., it contains only one period), the record for @ is updated.')
+
+parser.add_argument('--ip', type=str, default=None,
+ help='IPv4 address to write to DNS record (defaults to public WAN address from http://ipv4.icanhazip.com/)')
+
+parser.add_argument('--key', type=str, default='',
+ help='GoDaddy production key')
+
+parser.add_argument('--secret', type=str, default='',
+ help='GoDaddy production secret')
+
+parser.add_argument('--ttl', type=int, default=3600,
+ help='DNS TTL.')
+
+parser.add_argument('--force', type=bool, default=False,
+ help='force update of GoDaddy DNS record even if DNS query indicates that record is already correct.')
+
+args = parser.parse_args()
+
+def main():
+ hostnames = args.hostname.split('.')
+ if len(hostnames)<2:
+ msg = 'Hostname "{}" is not a fully-qualified host name of form "HOST.DOMAIN.TOP".'.format(args.hostname)
+ raise Exception(msg)
+ elif len(hostnames)<3:
+ hostnames.insert(0,'@')
+
+ if not args.ip:
+ try:
+ with urlopen("https://ipv4.icanhazip.com/") as f: resp=f.read()
+ if sys.version_info > (3,): resp = resp.decode('utf-8')
+ args.ip = resp.strip()
+ except URLError:
+ msg = 'Unable to automatically obtain IP address from http://ipv4.icanhazip.com/.'
+ raise Exception(msg)
+
+ ipslist = args.ip.split(",")
+ for ipsiter in ipslist:
+ ips = ipsiter.split('.')
+ if len(ips)!=4 or \
+ not ips[0].isdigit() or not ips[1].isdigit() or not ips[2].isdigit() or not ips[3].isdigit() or \
+ int(ips[0])>255 or int(ips[1])>255 or int(ips[2])>255 or int(ips[3])>255:
+ msg = '"{}" is not valid IP address.'.format(ips)
+ raise Exception(msg)
+
+ if not args.force and len(ipslist)==1:
+ try:
+ dnsaddr = socket.gethostbyname(args.hostname)
+ if ipslist[0] == dnsaddr:
+ msg = '{} already has IP address {}.'.format(args.hostname, dnsaddr)
+ raise Exception(msg)
+ except:
+ pass
+
+ url = 'https://api.godaddy.com/v1/domains/{}/records/A/{}'.format('.'.join(hostnames[1:]),hostnames[0])
+ data = json.dumps([ { "data": ip, "ttl": args.ttl, "name": hostnames[0], "type": "A" } for ip in ipslist])
+ if sys.version_info > (3,): data = data.encode('utf-8')
+ req = Request(url, method='PUT', data=data)
+
+ req.add_header("Content-Type","application/json")
+ req.add_header("Accept","application/json")
+ if args.key and args.secret:
+ req.add_header("Authorization", "sso-key {}:{}".format(args.key,args.secret))
+
+ try:
+ with urlopen(req) as f: resp = f.read()
+ if sys.version_info > (3,): resp = resp.decode('utf-8')
+ # resp = json.loads(resp)
+ except HTTPError as e:
+ if e.code==400:
+ msg = 'Unable to set IP address: GoDaddy API URL ({}) was malformed.'.format(req.full_url)
+ elif e.code==401:
+ if args.key and args.secret:
+ msg = '''Unable to set IP address: --key or --secret option incorrect.
+Correct values can be obtained from from https://developer.godaddy.com/keys/ and are ideally placed in a % file.'''
+ else:
+ msg = '''Unable to set IP address: --key or --secret option missing.
+Correct values can be obtained from from https://developer.godaddy.com/keys/ and are ideally placed in a % file.'''
+ elif e.code==403:
+ msg = '''Unable to set IP address: customer identified by --key and --secret options denied permission.
+Correct values can be obtained from from https://developer.godaddy.com/keys/ and are ideally placed in a % file.'''
+ elif e.code==404:
+ msg = 'Unable to set IP address: {} not found at GoDaddy.'.format(args.hostname)
+ elif e.code==422:
+ msg = 'Unable to set IP address: "{}" has invalid domain or lacks A record.'.format(args.hostname)
+ elif e.code==429:
+ msg = 'Unable to set IP address: too many requests to GoDaddy within brief period.'
+ elif e.code==503:
+ msg = 'Unable to set IP address: "{}" is unavailable.'.format(args.hostname)
+ else:
+ msg = 'Unable to set IP address: GoDaddy API failure because "{}".'.format(e.reason)
+ raise Exception(msg)
+ except URLError as e:
+ msg = 'Unable to set IP address: GoDaddy API failure because "{}".'.format(e.reason)
+ raise Exception(msg)
+
+ print('IP address for {} set to {}.'.format(args.hostname,args.ip))
+
+if __name__ == '__main__':
+ main()