master
Raw Download raw file
  1#!/usr/bin/python
  2
  3import ConfigParser
  4import requests
  5import optparse
  6import sys
  7
  8
  9# This is a command-line script to update a hover.com DNS record with your
 10# current public-facing IP address. (think dyndns) Run it like so:
 11# ./dynhover.py -u USERNAME -p PASSWORD DOMAIN
 12# or create a config file like this:
 13#
 14# [hover]
 15# username=USERNAME
 16# password=PASSWORD
 17#
 18# and run it like this:
 19# ./dynhover.py -c PATH_TO_CONF DOMAIN
 20
 21
 22class HoverException(Exception):
 23    pass
 24
 25
 26class HoverAPI(object):
 27    def __init__(self, username, password):
 28        params = {"username": username, "password": password}
 29        r = requests.post("https://www.hover.com/api/login", params=params)
 30        if not r.ok or "hoverauth" not in r.cookies:
 31            raise HoverException(r)
 32        self.cookies = {"hoverauth": r.cookies["hoverauth"]}
 33    def call(self, method, resource, data=None):
 34        url = "https://www.hover.com/api/{0}".format(resource)
 35        r = requests.request(method, url, data=data, cookies=self.cookies)
 36        if not r.ok:
 37            raise HoverException(r)
 38        if r.content:
 39            body = r.json()
 40            if "succeeded" not in body or body["succeeded"] is not True:
 41                raise HoverException(body)
 42            return body
 43
 44
 45def get_public_ip():
 46    return requests.get("http://api.exip.org/?call=ip").content
 47
 48
 49def update_dns(username, password, fqdn):
 50    try:
 51        client = HoverAPI(username, password)
 52    except HoverException as e:
 53        raise HoverException("Authentication failed")
 54    dns = client.call("get", "dns")
 55    dns_id = None
 56    for domain in dns["domains"]:
 57        if fqdn == domain["domain_name"]:
 58            fqdn = "@.{domain_name}".format(**domain)
 59        for entry in domain["entries"]:
 60            if entry["type"] != "A": continue
 61            if "{0}.{1}".format(entry["name"], domain["domain_name"]) == fqdn:
 62                dns_id = entry["id"]
 63                break
 64    if dns_id is None:
 65        raise HoverException("No DNS record found for {0}".format(fqdn))
 66
 67    my_ip = get_public_ip()
 68
 69    response = client.call("put", "dns/{0}".format(dns_id), {"content": my_ip})
 70    
 71    if "succeeded" not in response or response["succeeded"] is not True:
 72        raise HoverException(response)
 73    
 74
 75def main():
 76    usage = "usage: %prog (-c CONF|-u USERNAME -p PASSWORD) DOMAIN"
 77    description = "Update a hover.com DNS record with the current IP of this machine."
 78    parser = optparse.OptionParser(usage=usage, description=description)
 79    parser.add_option("-c", "--conf", default=None, help="The conf file that contains your username and password")
 80    parser.add_option("-u", "--username", default=None, help="Your hover.com username")
 81    parser.add_option("-p", "--password", default=None, help="Your hover.com password")
 82    (options, args) = parser.parse_args()
 83    
 84    if len(args) < 1:
 85        parser.error("You must specify a domain")
 86    
 87    domain = args[0]
 88    
 89    def get_conf(filename):
 90        config = ConfigParser.ConfigParser()
 91        config.read(filename)
 92        items = dict(config.items("hover"))
 93        return items["username"], items["password"]
 94
 95    if options.conf is None:
 96        if not all((options.username, options.password)):
 97            parser.error("You must specifiy either a conf file, or a username and password")
 98        else:
 99            username, password = options.username, options.password
100    else:
101        username, password = get_conf(options.conf)
102
103    update_dns(username, password, domain)
104
105
106if __name__ == "__main__":
107    try:
108        main()
109    except HoverException as e:
110        print "Unable to update DNS: {0}".format(e)
111        sys.exit(1)
112    sys.exit(0)