#!/usr/bin/env python import uuid import datetime import urllib2 import urllib import httplib import ssl import socket import json import argparse import sys CURRENT_VERSION = "1.1-imanolbarba" DEBUG = 0 EXTERNAL_IP_URL = "https://ipv4.wtfismyip.com/text" TYPE_INFO = 0 TYPE_WARNING = 1 TYPE_ERROR = 2 TYPE_DEBUG = 3 BASH_YLW_TEXT = '\033[33m' BASH_RED_TEXT = '\033[31m' BASH_RESET_TEXT = '\033[0m' #MY_API_KEY = "AAAAAAAAAAAAAAAA" #Generates random UUID def genUUID(): return str(uuid.uuid4()) #Message logging function #TODO: log to file def msgLog(string,type = TYPE_INFO): time = str(datetime.datetime.now()) if(type == TYPE_ERROR): print "[" + time + "]: " + BASH_RED_TEXT + "ERROR: " + string + BASH_RESET_TEXT elif(type == TYPE_WARNING): print "[" + time + "]: " + BASH_YLW_TEXT + "WARNING: " + string + BASH_RESET_TEXT elif(type == TYPE_INFO): print "[" + time + "]: " + "INFO: " + string elif(type == TYPE_DEBUG): if(DEBUG): print "[" + time + "]: " + "DEBUG: " + string else: print string #External IP fetcher, expects plain text response def fetchIP(): text = urllib2.urlopen(EXTERNAL_IP_URL).read() return ''.join(text.split()) #remove whitespace class APIConn: APIKey = "" username = "" def __init__(self): self.APIKey = MY_API_KEY def sendCmd(self,cmd,args): conn = httplib.HTTPSConnection(host="api.dreamhost.com", port=443) sock = socket.create_connection((conn.host, conn.port), conn.timeout, conn.source_address) conn.sock = ssl.wrap_socket(sock, conn.key_file, conn.cert_file, ssl_version=ssl.PROTOCOL_TLSv1) msgLog("Connection with Dreamhost established",TYPE_DEBUG) apiParams = { "key" : self.APIKey, "unique_id" : genUUID(), "cmd" : cmd, "format" : "json" } if(len(args) != 0): apiParams.update(args) params = urllib.urlencode(apiParams) msgLog("Sending request with parameters " + params,TYPE_DEBUG) conn.request("POST","/",params) answer = conn.getresponse().read() msgLog("Received answer: " + answer, TYPE_DEBUG) conn.close() msgLog("Connection closed", TYPE_DEBUG) return answer def listDNS(apiconn): cmd = "dns-list_records" args = {} data = json.loads(apiconn.sendCmd(cmd,args)) records = {} for i in xrange(len(data["data"])): if(data["data"][i]["type"] == "A"): records[str(data["data"][i]["record"])] = str(data["data"][i]["value"]) msgLog("Returned records: " + str(records), TYPE_DEBUG) return records def updateDNS(apiconn,record,oldIP,newIP): removeDNS(apiconn,record,oldIP) addDNS(apiconn,record,newIP) msgLog("Successfully updated " + record,TYPE_INFO) def addDNS(apiconn,record,IP): cmd = "dns-add_record" args = {"record" : record , "type" : "A" , "value" : IP} data = json.loads(apiconn.sendCmd(cmd,args)) if(data["result"] != "success"): if(data["data"] == "no_such_zone"): msgLog("Server returned no_such_zone, this is a known issue in the Dreamhost API, go to your Panel and add a A record for your domain with the IP 0.0.0.0, then run " + sys.argv[0] + " --add " + record,TYPE_WARNING) msgLog("Error while adding record, server returned: " + data["data"],TYPE_ERROR) return msgLog("Successfully added " + record,TYPE_INFO) def removeDNS(apiconn,record,IP): cmd = "dns-remove_record" args = {"record" : record , "type" : "A" , "value" : IP} data = json.loads(apiconn.sendCmd(cmd,args)) if(data["result"] != "success"): msgLog("Error while removing old record, server returned: " + data["data"],TYPE_ERROR) return msgLog("Successfully removed " + record,TYPE_INFO) if(MY_API_KEY == ""): msgLog("Missing API Key. Please open the script and add your key in the MY_API_KEY variable",TYPE_ERROR) exit() api = APIConn() parser = argparse.ArgumentParser(description="Update Dreamhost DNS records") parser.add_argument('--verbose', action='store_true', dest="verbose", help="Enable debugging messages") parser.add_argument('--version', action='version', version=str("DreamDDNS v" + CURRENT_VERSION)) group = parser.add_mutually_exclusive_group() group.add_argument("--update", action="store", dest="update", help="Update DNS record RECORD", nargs=1) group.add_argument("--list", action="store_true", dest="list", help="List current DNS records") group.add_argument("--add", action="store", dest="add", help="Add DNS record RECORD", nargs=1) group.add_argument("--remove", action="store", dest="remove", help="Remove DNS record RECORD", nargs=1) if(len(sys.argv) == 1): parser.print_help() argv = parser.parse_args() if(argv.verbose): DEBUG = 1 if(argv.list): records = listDNS(api) for i in records: print i + ": " + records[i] elif(argv.update != None): targetRecord = argv.update[0] newIP = fetchIP() records = listDNS(api) recordFound = False for i in records: if(i == targetRecord): recordFound = True if(records[i] == newIP): msgLog(targetRecord + " is already up-to-date. No further action needed",TYPE_INFO) else: msgLog("Updating " + targetRecord + "...",TYPE_INFO) updateDNS(api,targetRecord,records[i],newIP) if(recordFound == False): msgLog("Record not found",TYPE_ERROR) elif(argv.add != None): targetRecord = argv.add[0] newIP = fetchIP() msgLog("Adding " + targetRecord + "...",TYPE_INFO) addDNS(api,targetRecord,newIP) elif(argv.remove != None): targetRecord = argv.remove[0] records = listDNS(api) recordFound = False for i in records: if(i == targetRecord): recordFound = True msgLog("Removing " + targetRecord + "...",TYPE_INFO) removeDNS(api,targetRecord,records[i]) if(recordFound == False): msgLog("Record not found",TYPE_ERROR)