Ruby -vs- PHP Showcase: nsupdate script

These are the source code files for the Ruby -vs- PHP Showcase: nsupdate script Read the full story in my blog.

Source codes

Config files

#!/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>&nbsp;</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 &copy; 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 &copy; 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