support multiple domains in update script; get rid of usernames
authorRalf Jung <post@ralfj.de>
Tue, 4 Mar 2014 20:19:40 +0000 (21:19 +0100)
committerRalf Jung <post@ralfj.de>
Tue, 4 Mar 2014 20:19:40 +0000 (21:19 +0100)
client.py
dyn-nsupdate.cpp
update

index a4d41fa86ac646da3008a89fddc1b652927ba1ac..d32542b1e3e6b8ba579d52fabfbc0fbd4583cac5 100755 (executable)
--- a/client.py
+++ b/client.py
@@ -3,24 +3,32 @@ import urllib2, socket, urllib, sys
 
 # configuration variables
 server = 'ns.ralfj.de'
-user = 'yourusername'
+domains = ['your.domain.ralfj.de'] # list of domains to update
 password = 'yourpassword'
-domain = 'your.domain.ralfj.de'
 # END of configuration variables
 
-# check if the domain is already mapped to our current IP
-myip = urllib2.urlopen('https://'+server+'/checkip').read().strip()
-currip = socket.gethostbyname(domain)
-if myip == currip:
-       # nothing to do
-       sys.exit(0)
+def update_domain(domain):
+       '''Update the given domain, using the global server, user, password. Returns True on success, False on failure.'''
+       # check if the domain is already mapped to our current IP
+       myip = urllib2.urlopen('https://'+server+'/checkip').read().strip()
+       domainip = socket.gethostbyname(domain)
+       if myip == domainip:
+               # nothing to do
+               return True
 
-# we need to update the IP
-result = urllib2.urlopen('https://'+server+'/update?user='+urllib.quote(user)+'&password='+urllib.quote(password)+'&domain='+urllib.quote(domain)+'&ip='+urllib.quote(myip)).read().strip()
-if 'good '+myip == result: 
-       # nothing to do, all went all right
-       sys.exit(0)
+       # we need to update the IP
+       result = urllib2.urlopen('https://'+server+'/update?password='+urllib.quote(password)+'&domain='+urllib.quote(domain)+'&ip='+urllib.quote(myip)).read().strip()
+       if 'good '+myip == result: 
+               # all went all right
+               return True
+       else:
+               # Something went wrong
+               print "Unexpected answer from server",server,"while updating",domain,"to",myip
+               print result
+               return False
 
-# there was an error :(
-print result
-sys.exit(1)
+exitcode = 0
+for domain in domains:
+       if not update_domain(domain):
+               exitcode = 1
+sys.exit(exitcode)
index 25381cff5d09219af2cf9c5a5209a83534907eb8..fd804d0057eb6990dfc3f2cf958743a0dc0ade0a 100644 (file)
@@ -9,6 +9,7 @@
 #include <boost/iostreams/stream.hpp>
 
 using namespace boost;
+typedef property_tree::ptree::path_type path;
 
 static void write(int fd, const char *str)
 {
@@ -23,26 +24,31 @@ static void write(int fd, const char *str)
 int main(int argc, const char ** argv)
 {
        static const regex regex_ip("\\d{1,3}.\\d{1,3}.\\d{1,3}.\\d{1,3}");
-       static const regex regex_user("[a-zA-Z]+");
+       static const regex regex_password("[a-zA-Z0-9.:;,_-]+");
+       static const regex regex_domain("[a-zA-Z0-9.]+");
        
        
-       if (argc != 5) {
-               std::cerr << "Usage: " << argv[0] << " <username> <password> <domain> <IP address>" << std::endl;
+       if (argc != 4) {
+               std::cerr << "Usage: " << argv[0] << " <domain> <password> <IP address>" << std::endl;
                return 1;
        }
        
-       /* Obtain and validate inpit */
-       std::string user = argv[1];
+       /* Obtain input */
+       std::string domain = argv[1];
        std::string password = argv[2];
-       std::string domain = argv[3];
-       std::string ip = argv[4];
+       std::string ip = argv[3];
        
+       /* Validate input */
        if (!regex_match(ip, regex_ip)) {
                std::cerr << "Invalid IP address " << ip << "." << std::endl;
                exit(1);
        }
-       if (!regex_match(user, regex_user)) {
-               std::cerr << "Invalid username " << user << "." << std::endl;
+       if (!regex_match(domain, regex_domain)) {
+               std::cerr << "Invalid domain " << domain << "." << std::endl;
+               exit(1);
+       }
+       if (!regex_match(password, regex_password)) {
+               std::cerr << "Invalid password " << password << "." << std::endl;
                exit(1);
        }
        
@@ -51,14 +57,10 @@ int main(int argc, const char ** argv)
        property_tree::ini_parser::read_ini(CONFIG_FILE, config);
        std::string nsupdate = config.get<std::string>("nsupdate");
        
-       /* Check username, password, domain */
-       optional<std::string> correct_password = config.get_optional<std::string>(user+".password");
+       /* Given the domain, check whether the password matches */
+       optional<std::string> correct_password = config.get_optional<std::string>(path(domain+"/password", '/'));
        if (!correct_password || *correct_password != password) {
-               std::cerr << "Username or password incorrect." << std::endl;
-               exit(1);
-       }
-       if (config.get<std::string>(user+".domain") != domain) {
-               std::cerr << "Domain incorrect." << std::endl;
+               std::cerr << "Password incorrect." << std::endl;
                exit(1);
        }
        
@@ -77,7 +79,7 @@ int main(int argc, const char ** argv)
        }
        if (child_pid == 0) {
                /* We're in the child */
-               /* Close write end, use read and as stdin */
+               /* Close write end, use read end as stdin */
                close(pipe_ends[1]);
                if (dup2(pipe_ends[0], fileno(stdin)) < 0) {
                        std::cerr << "There was an error redirecting stdin." << std::endl;
diff --git a/update b/update
index 1c149beb96ced210726811d871fea42b33f3f9ba..d5fbca39c945b90ed5ad5292ad341b68587d8cf4 100755 (executable)
--- a/update
+++ b/update
@@ -7,32 +7,23 @@ print "Content-Type: text/plain"
 print ""
 
 # get input
-if "user" not in form or "password" not in form or "domain" not in form or "ip" not in form:
-    print "Mandatory argument missing: You must supply all of 'user', 'password', 'domain', 'ip'"
+if "password" not in form or "domain" not in form or "ip" not in form:
+    print "Mandatory argument missing: You must supply all of 'password', 'domain', 'ip'"
     sys.exit()
 
 ip = form["ip"].value
 domain = form["domain"].value
-user = form["user"].value
 password = form["password"].value
 
 # run update program
-p = subprocess.Popen(["/var/lib/named/dyn-nsupdate", user, password, domain, ip], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+p = subprocess.Popen(["/var/lib/named/dyn-nsupdate", domain, password, ip], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 (stdout, stderr) = p.communicate()
-if stdout:
-       print "Unexpected stdout from dyn-nsupdate:"
-       print stdout
-       sys.exit()
 
 # check what it did
-if p.returncode == 1:
+if p.returncode or stderr or stdout:
        # error :/
-       print "There was an error while updating the DNS:"
-       print stderr
+       print "There was an error while updating the DNS: Return code %d" % p.returncode
+       if stdout: print stdout
+       if stderr: print stderr
 else:
-       # all right!
-       if p.returncode or stderr:
-               print "Unexpected stderr from dyn-nsupdate:"
-               print stderr
-               sys.exit()
        print "good",ip