These are the source code files for the Ruby -vs- PHP Showcase: nsupdate script Read the full story in my blog.
<?php # $Id: nsupdate.php,v 1.2 2005/12/26 18:20:38 chip Exp $ # $Source: /home/chip/src/web-nsupdate/RCS/nsupdate.php,v $ # # Copyright 2005, Chip Rosenthal <chip@unicom.com>. # See software license at <http://www.unicom.com/sw/license.html> # for terms of use and distribution. # # # *** Edit this to point to where your definitions file is installed. # require_once("/usr/local/lib/web-nsupdate/nsupdate-defs.php"); /** * Template to generate command script for nsupdate(8). */ $NSUPATE_COMMAND_TEMPLATE = 'server {$p_hostinfo["nameserver"]} zone $p_domain update delete $p_host update add $p_host {$p_hostinfo["ttl"]} A $p_addr send '; /** * Web page with form for manual entry. */ $NSUPDATE_MANUAL_FORM = '<html> <head> <title>web-nsupdate: Manual Entry</title> </head> <body> <h1>web-nsupdate: Manual Entry</h1> <form method="get"> <table border="0" cellspaceing="0" cellpadding="3"> <tr> <td><label for="host">Host Name:</label></td> <td><input type="text" name="host" /></td> </tr> <tr> <td><label for="addr">Host Address:</label></td> <td><input type="text" name="addr" /></td> </tr> <tr> <td><label for="key">Password:</label></td> <td><input type="password" name="key" /></td> </tr> <tr> <td> </td> <td> <input type="hidden" name="verbose" value="1" /> <input type="submit" /> <input type="reset" /> </td> </tr> </table> </form> <hr> <p style="font-size:smaller">© 2005 <a href="http://www.unicom.com/sw/">Unicom Systems Development</a></p> </body> </html> '; /** * Template for web page with success resposne for manual update. */ $NSUPATE_MANUAL_RESPONSE = '<html> <head> <title>web-nsupdate: Manual Update Successful</title> </head> <body> <h1>web-nsupdate: Manual Update Successful</h1> <p>Host <i>{$p_host}</i> has been assigned address <i>$p_addr</i>.</p> <hr> <p style="font-size:smaller">© 2005 <a href="http://www.unicom.com/sw/">Unicom Systems Development</a></p> </body> </html> '; _EOT_; /** * Retrieve information from $Hosts_Table[] for a specified host. * @param $p_host Name of the host to lookup. * @return Array (key/value pairs) of information on the host. * The host info is a keyed array with the following items * defined: key, nskey, nameserver, ttl. * Values not specified for the host are defined to the default. * An error is raised if the host is not defined. */ function get_hostinfo($p_host) { global $Hosts_Table; if (empty($Hosts_Table[$p_host])) { trigger_error("Host \"$p_host\" unknown.", E_USER_ERROR); } $p_hostinfo = $Hosts_Table[$p_host]; if (empty($p_hostinfo["nskey"])) { $p_hostinfo["nskey"] = DEFAULT_NSKEY; } if (empty($p_hostinfo["nameserver"])) { $p_hostinfo["nameserver"] = DEFAULT_NAMESERVER; } if (empty($p_hostinfo["ttl"])) { $p_hostinfo["ttl"] = DEFAULT_TTL; } return $p_hostinfo; } /** * Validate an authorization key for a host. * @param $p_hostinfo Host information array. * @param $p_key Key to validate. * @return Nothing. * An error is raised if validation fails. */ function validate_host($p_hostinfo, $p_key) { if ($p_hostinfo["key"] != $p_key) { trigger_error("Permission denied.", E_USER_ERROR); } } /** * Extract the domain name name portion from a fully qualified host name. * @param $p_host The fully qualified host name. * @return The extracted domain name. * An error is raised if the extraction fails. */ function extract_domain_from_hostname($p_host) { $pos = strpos($p_host, "."); if ($pos === FALSE) { trigger_error("Cannot extract domain from hostname.", E_USER_ERROR); } return substr($p_host, $pos+1); } /** * Write data to a file. * @param $filename Name of the file to create. * @param $data The data to write. * @return Number of characters written. * An error is raised if any of the operations fail. */ function file_write_string($filename, $data) { $fh = fopen($filename, "w"); if (!$fh) { trigger_error("fopen() failed.", E_USER_ERROR); } $rc = fwrite($fh, $data); if ($rc === FALSE) { trigger_error("fwrite() failed.", E_USER_ERROR); } if (!fclose($fh)) { trigger_error("fclose() failed.", E_USER_ERROR); } return $rc; } ######################################################################## # # Main execution begins here. # # # Retrieve input from user. # $p_host = $_REQUEST['host']; if (empty($p_host)) { # If host not specified, present form for manual data entry. echo($NSUPDATE_MANUAL_FORM); exit(0); } $p_addr = $_REQUEST['addr']; if (empty($p_addr)) { trigger_error("Required parameter \"addr\" not specified.", E_USER_ERROR); } $p_key = $_REQUEST['key']; if (empty($p_key)) { trigger_error("Required parameter \"key\" not specified.", E_USER_ERROR); } # # Lookup this host and validate the password. # $p_hostinfo = get_hostinfo($p_host); validate_host($p_hostinfo, $p_key); $p_domain = extract_domain_from_hostname($p_host); # # Generate a command script for nsupdate(8). # $tmpfname = tempnam("", "nsupdate."); if (!$tmpfname) { trigger_error("Error generating temporary file name.", E_USER_ERROR); } eval("\$fcontent = \"$NSUPATE_COMMAND_TEMPLATE\";"); file_write_string($tmpfname, $fcontent); # # Run the nsupdate(8) command. # $rc = system("nsupdate -k {$p_hostinfo['nskey']} $tmpfname 2>&1", $ex); unlink($tmpfname); if ($rc === FALSE || $ex != 0) { trigger_error("nsupdate command failed.", E_USER_ERROR); } # # Normally we exit quietly on success. # If we were running manually, send an HTML response. # if ($_REQUEST['verbose']) { eval("echo(\"" . addslashes($NSUPATE_MANUAL_RESPONSE) . "\");"); } ?>
#!/usr/bin/env ruby require 'yaml' # http://yaml.org/ require 'cgi' # http://www.ruby-doc.org/stdlib/libdoc/cgi/rdoc/ # # Ruby CGI showcase # Copyright (c) 2006 Christian W. Zuckschwerdt <zany@triq.net> # based on nsupdate.php, Copyright 2005, Chip Rosenthal <chip@unicom.com>. # # # *** Edit this to point to where your config file is installed. # config_file = '/home/zany/workspace/ruby-nsupdate/nsupdate.yml' @config = YAML.load_file config_file # # Template to generate command script for nsupdate(8). # NSUPATE_COMMAND_TEMPLATE = <<'EOT' "server #{hostinfo['nameserver']} zone #{domain} update delete #{host} update add #{host} #{hostinfo['ttl']} A #{addr} send" EOT # # Web page with form for manual entry. # NSUPDATE_MANUAL_FORM = <<'EOT' <html> <head> <title>web-nsupdate: Manual Entry</title> </head> <body> <h1>web-nsupdate: Manual Entry</h1> <form method="get"> <table border="0" cellspaceing="0" cellpadding="3"> <tr> <td><label for="host">Host Name:</label></td> <td><input type="text" name="host" /></td> </tr> <tr> <td><label for="addr">Host Address:</label></td> <td><input type="text" name="addr" /></td> </tr> <tr> <td><label for="key">Password:</label></td> <td><input type="password" name="key" /></td> </tr> <tr> <td> </td> <td> <input type="hidden" name="verbose" value="1" /> <input type="submit" /> <input type="reset" /> </td> </tr> </table> </form> <hr> <p style="font-size:smaller"> based on work © 2005 <a href="http://www.unicom.com/sw/">Unicom Systems Development</a></p> </body> </html> EOT # # Template for web page with success response for manual update. # NSUPATE_MANUAL_RESPONSE = <<'EOT' "<html> <head> <title>web-nsupdate: Manual Update Successful</title> </head> <body> <h1>web-nsupdate: Manual Update Successful</h1> <p>Host <i>#{host}</i> has been assigned address <i>#{addr}</i>.</p> <hr> <p style='font-size:smaller'>based on work © 2005 <a href='http://www.unicom.com/sw/'>Unicom Systems Development</a></p> </body> </html>" EOT # # Retrieve information from @config for a specified host. # - host Name of the host to lookup. # Returns Hash (key/value pairs) of information on the host. # The host info is a keyed with the following items # defined: key, nskey, nameserver, ttl. # Values not specified for the host are defined to the default. # An error is raised if the host is not defined. # def get_hostinfo(host) return unless @config.include? 'defaults' return unless @config.include? host defaults = @config['defaults'] || {} defaults.merge @config[host] end # # Validate an authorization key for a host. # - hostinfo Host information array. # - key Key to validate. # Returns true/false. # # def validate_host(hostinfo, key) hostinfo["key"] == key end # # Extract the domain name name portion from a fully qualified host name. # - host The fully qualified host name. # Returns The extracted domain name. # # def domain_from_hostname(host) host[host.index('.')+1..-1] rescue nil end ######################################################################## # # Main execution begins here. # cgi = CGI.new # # Retrieve input from user. # if (host = "#{cgi['host']}").empty? # If host not specified, present form for manual data entry. puts NSUPDATE_MANUAL_FORM exit 0 end if (addr = "#{cgi['addr']}").empty? raise "Required parameter \"addr\" not specified." end if (key = "#{cgi['key']}").empty? raise "Required parameter \"key\" not specified." end # # Lookup this host and validate the password. # raise "Host #{host} unknown." unless hostinfo = get_hostinfo(host) raise "Permission denied." unless validate_host(hostinfo, key) domain = domain_from_hostname(host) raise "Cannot extract domain from hostname." unless domain # # Generate a command script for nsupdate(8). # content = eval NSUPATE_COMMAND_TEMPLATE # # Run the nsupdate(8) command. # IO.popen ("nsupdate -k #{hostinfo['nskey']}", 'w') { |io| io.puts content } #raise "nsupdate command failed." unless rc # # Normally we exit quietly on success. # If we were running manually, send an HTML response. # if (cgi['verbose']) eval NSUPATE_MANUAL_RESPONSE else "OK" end