diff --git a/java/dns/src/site/apt/releaseNotes.apt b/java/dns/src/site/apt/releaseNotes.apt
index e07a0a53b..70ccd9991 100644
--- a/java/dns/src/site/apt/releaseNotes.apt
+++ b/java/dns/src/site/apt/releaseNotes.apt
@@ -6,6 +6,39 @@
Greg Meyer
---
+{2.0.1}
+
+ Changes included with release 2.0.1
+
+ []
+
+ Enhancements
+
+ * N/A
+
+ []
+
+ Bug Fixes
+
+ * Switching log statements to out text displayable version of the DNS query type name (instead of the RFC int value).
+
+{2.0}
+
+ Changes included with release 2.0
+
+ []
+
+ Enhancements
+
+ * Updated to support using REST API for config service.
+
+ []
+
+ Bug Fixes
+
+ * N/A
+
+
{1.5.2}
Changes included with release 1.5.2
diff --git a/java/tags/dns-2.0.1/pom.xml b/java/tags/dns-2.0.1/pom.xml
new file mode 100644
index 000000000..f53dd27d1
--- /dev/null
+++ b/java/tags/dns-2.0.1/pom.xml
@@ -0,0 +1,660 @@
+
+
+ org.nhind
+ 4.0.0
+ dns
+ Direct Project DNS services
+ 2.0.1
+ Direct Project DNS services
+ 2010
+ http://api.nhindirect.org/x/www/api.nhindirect.org/java/site/dns/${project.version}
+
+
+ Greg Meyer
+ GM2552
+ gm2552@cerner.com
+
+ owner
+
+
+
+
+ The Direct Project
+ http://nhindirect.org
+
+
+ 2.0.4
+
+
+ http://code.google.com/p/nhin-d/source/browse/#hg/java/dns
+ scm:hg:https://nhin-d.googlecode.com/hg/nhin-d/java/dns
+
+
+ Google Code
+ http://code.google.com/p/nhin-d/issues/list
+
+
+
+ New BSD License
+ http://nhindirect.org/BSDLicense
+
+
+
+
+ com.google.inject
+ guice
+ 2.0
+
+
+ commons-logging
+ commons-logging
+ 1.1.1
+
+
+ commons-io
+ commons-io
+ 1.4
+
+
+ junit
+ junit
+ 3.8.2
+ test
+
+
+ bouncycastle
+ bcprov-jdk15
+ 140
+
+
+ dnsjava
+ dnsjava
+ 2.0.8
+
+
+ org.nhind
+ config-service-client
+ 2.0
+
+
+ org.springframework
+ spring-context
+
+
+
+
+ org.nhind
+ config-model
+ 1.1
+
+
+ org.nhind
+ direct-policy
+ 1.0.1
+
+
+ org.nhind
+ direct-common
+ 2.0
+
+
+ org.nhind
+ agent
+ 2.1
+
+
+ org.apache.james
+ apache-jsieve-mailet
+
+
+ javax.activation
+ activation
+
+
+
+
+ org.nhind
+ config-store
+ 2.0.0
+
+
+ org.hibernate
+ hibernate
+
+
+ org.hibernate
+ hibernate-core
+
+
+ test
+
+
+ org.apache.mina
+ mina-core
+ 1.0.2
+ test
+
+
+ org.mockito
+ mockito-all
+ 1.8.5
+ test
+
+
+ org.mortbay.jetty
+ jetty-servlet-tester
+ 6.1.14
+ test
+
+
+ javax.activation
+ activation
+
+
+
+
+
+
+
+ org.apache.maven.wagon
+ wagon-webdav
+ RELEASE
+
+
+ org.apache.maven.wagon
+ wagon-ssh-external
+ 1.0-beta-6
+
+
+ org.apache.maven.wagon
+ wagon-ssh
+ 1.0-beta-6
+
+
+
+
+ src/main/resources
+
+
+ lib
+ ${project.basedir}/lib
+
+
+
+
+ src/test/resources
+
+
+ lib
+ ${project.basedir}/lib
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ org.apache.maven.plugins
+ maven-jxr-plugin
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ copy
+ compile
+
+ copy
+
+
+
+
+ org.nhind
+ config-service
+ 2.2
+ war
+ true
+ config-service.war
+
+
+
+ ${project.basedir}/war
+
+ true
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+
+ testCompile
+
+ compile
+
+
+
+ true
+ true
+ true
+ UTF-8
+ 1.8
+ 1.8
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 2.0.3
+
+
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 2.2
+
+
+
+ test-jar
+
+
+
+
+
+ org.codehaus.mojo
+ appassembler-maven-plugin
+ 1.3.1
+
+
+ generate-jsw-scripts
+ package
+
+ ${project.build.directory}/appassembler
+ jsw/DirectDNSServer/lib
+
+ .sh
+
+ jsw/DirectDNSServer/lib
+ flat
+
+ 256M
+ 1024M
+
+ java.security.policy=conf/policy.all
+ org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog
+ org.apache.commons.logging.simplelog.defaultlog=info
+ org.nhindirect.dns.CertPolicyName=DNSCertPolicy
+
+
+
+
+ DirectDNSServer
+ org.nhindirect.dns.service.SimpleServiceRunner
+
+ -p
+ 53
+ -b
+ 0.0.0.0
+ -u
+ http://localhost:8081/config-service/ConfigurationService
+ -m
+ SERVER
+
+
+ jsw
+
+
+
+ jsw
+
+ linux-x86-32
+ linux-x86-64
+ windows-x86-32
+ solaris-x86-32
+ macosx-universal-32
+
+
+
+ set.default.REPO_DIR
+ lib
+
+
+ wrapper.logfile
+ logs/wrapper.txt
+
+
+
+
+
+
+
+
+
+ generate-daemons
+ create-repository
+
+
+
+
+ ${project.build.directory}/appassembler/app/DNSMgmtConsole
+ lib
+ flat
+ -Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog -Dorg.apache.commons.logging.simplelog.defaultlog=error
+
+
+
+ org.nhindirect.dns.tools.DNSManager
+ DNSMgmtConsole
+
+
+
+
+
+ assemble
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-antrun-plugin
+
+
+ create-logs
+ package
+
+
+
+
+
+
+
+
+ run
+
+
+
+ books
+ pre-site
+
+
+
+
+
+
+
+
+
+
+
+ run
+
+
+
+
+
+ org.apache.maven.doxia
+ doxia-maven-plugin
+ 1.1.3
+
+
+ pre-site
+
+ render-books
+
+
+
+
+
+
+ src/books/users-guide
+ src/books/users-guide.xml
+
+
+ xdoc
+
+
+
+
+
+
+
+ org.codehaus.plexus
+ plexus-utils
+ 1.5.12
+
+
+ org.apache.maven.doxia
+ doxia-module-confluence
+ 1.1.3
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 2.2
+
+
+ package
+
+ single
+
+
+ false
+ false
+ dnsServices-${project.version}
+
+ src/descriptors/distribution.xml
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-release-plugin
+ 2.0
+
+ scm:hg:https://nhin-d.googlecode.com/hg/nhin-d/java/tags
+
+
+
+ org.apache.maven.plugins
+ maven-site-plugin
+ 2.1.1
+
+
+ commons-httpclient
+ commons-httpclient
+ 3.1
+
+
+ commons-logging
+ commons-logging
+
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-site-plugin
+ 2.1.1
+
+
+ org.apache.maven.plugins
+ maven-project-info-reports-plugin
+ 2.2
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 2.6.1
+
+ -Xdoclint:none
+ UTF-8
+ UTF-8
+ true
+ true
+ true
+ 1.8
+ public
+
+ org.nhindirect.dns.tools.utils
+
+
+
+
+ org.apache.maven.plugins
+ maven-pmd-plugin
+
+ 1.8
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-report-plugin
+
+
+ org.apache.maven.plugins
+ maven-jxr-plugin
+
+
+ org.codehaus.mojo
+ clirr-maven-plugin
+
+ info
+
+
+
+ org.codehaus.mojo
+ findbugs-maven-plugin
+ 1.2
+
+ Max
+ ${project.basedir}/report/findbugs-exclude.xml
+
+
+
+ org.codehaus.mojo
+ taglist-maven-plugin
+
+
+ FIXME
+ TODO
+ WARN
+ @deprecated
+
+
+
+
+ com.atlassian.maven.plugins
+ maven-clover2-plugin
+ 3.0.2
+
+
+ ${project.basedir}/../licenses/clover.license
+
+
+
+
+
+
+
+ nhind-site
+ NHIN Direct API publication site
+ sftp://api.nhindirect.org/x/www/api.nhindirect.org/java/site/dns/${project.version}
+
+
+ sonatype-snapshot
+ Sonatype OSS Maven SNAPSHOT Repository
+ https://oss.sonatype.org/content/repositories/snapshots/
+ false
+
+
+ sonatype-release
+ Sonatype OSS Maven Release Repositor
+ https://oss.sonatype.org/service/local/staging/deploy/maven2/
+ false
+
+
+
diff --git a/java/tags/dns-2.0.1/report/findbugs-exclude.xml b/java/tags/dns-2.0.1/report/findbugs-exclude.xml
new file mode 100644
index 000000000..25a582583
--- /dev/null
+++ b/java/tags/dns-2.0.1/report/findbugs-exclude.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/java/tags/dns-2.0.1/src/books/users-guide.xml b/java/tags/dns-2.0.1/src/books/users-guide.xml
new file mode 100644
index 000000000..736e3be3a
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/books/users-guide.xml
@@ -0,0 +1,40 @@
+
+
+
+ users-guide
+ DNS Services Users Guide
+
+
+ preface
+ Preface
+
+
+ preface
+
+
+
+
+ deploy
+ Deployment Guide
+
+
+ deploy-intro
+
+
+ dns-serv-depl
+
+
+ dns-proto-config
+
+
+ dns-record-config
+
+
+ dns-godad-config
+
+
+
+
+
\ No newline at end of file
diff --git a/java/tags/dns-2.0.1/src/books/users-guide/deploy-intro.confluence b/java/tags/dns-2.0.1/src/books/users-guide/deploy-intro.confluence
new file mode 100644
index 000000000..696d07e64
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/books/users-guide/deploy-intro.confluence
@@ -0,0 +1,27 @@
+h1. About this Chapter
+
+This chapters describes the different deployment and configuration options for the DNS services.
+
+h2. Purpose and Overview
+
+The Direct Project Simple Health [Transport|http://wiki.directproject.org/Simple+Health+Transport] specification outlines requirements for using DNS to resolve public certificates for the purpose of message encryption. Although the requirements are not enforced with the MUST verbiage, the underlying goal is to define a universal and ubiquitous method to distribute and resolve public certificates.
+
+Several open source and commercial DNS services are already widely available, however they differ in both their support of the full DNS specification and the tooling available to configure and manage the DNS solution. The challenge is finding a solution the meets the needs of the domain both in terms of functional support and ease of use (good tooling). So why does the Direct Project provides its own DNS solution when are there already so many viable options. The simple answer is the use of the CERT record type. Investigation has shown that many popular, commercially available DNS services (both organizational and third party hosting, ex: GoDaddy) do not support CERT records. Conversely many of the open source services support a broader range of the DNS spec, however tooling and configuration support is limited. In some solutions, tooling is limited to editing a raw configuration file with a plain text editor. Text editor support is not a viable solution for large deployments where thousands of entries may exist.
+
+The Direct Project DNS services are not intended to be a one stop shop for all DNS needs, but to compliment existing DNS service and fill the functional gaps not provided by existing solutions. In practice, the functional abilities of the Direct DNS services are limited to meet a small number of use cases. Specifically they provide a simple solution to respond to public cert requests (and a few other request types) and tooling to manage certificate storage and DNS record entries. In addition the services not intended to host primary DNS zones; instead they are deployed in a sub zone that is generally intended only for Direct Project implementations.
+
+h2. Service Deployment
+
+DNS deployment consists of installing the services using operating system specific service installation methods and configuring the location of the DNS record store.
+
+* [DNS Service Deployment|./dns-serv-depl.html]
+
+h2. Service Configuration
+
+Configuration is broken into two logical part: configuring the DNS specific protocol parameters and configuring/managing DNS records. The latter configuration may be dependent on the DNS hosting solution of the primary domain name.
+
+* [DNS Protocol Configuration|./dns-proto-config.html]
+
+* [DNS Record Configuration|./dns-record-config.html]
+
+* [GoDaddy Domain Hosting|./dns-godad-config.html]
\ No newline at end of file
diff --git a/java/tags/dns-2.0.1/src/books/users-guide/dns-godad-config.confluence b/java/tags/dns-2.0.1/src/books/users-guide/dns-godad-config.confluence
new file mode 100644
index 000000000..97217ef9a
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/books/users-guide/dns-godad-config.confluence
@@ -0,0 +1,6 @@
+h1. Integration With GoDaddy
+
+[GoDaddy|http://www.godaddy.com] is a popular domain hosting service. You can use your existing GoDaddy domain to host a Direct message domain. Typically you will create a sub domain/zone from your registered domain and configure GoDaddy to use your DNS server as the name server. The following links describe creating a sub domain from your existing domain and how to configure GoDaddy to use your name server with the sub domain.
+
+* [Direct Project DNS Configuration Guide|http://wiki.directproject.org/DNS+Configuration+Guide]
+* [DNS Configuration On GoDaddy|http://wiki.directproject.org/Configuring+DNS+on+GoDaddy]
\ No newline at end of file
diff --git a/java/tags/dns-2.0.1/src/books/users-guide/dns-proto-config.confluence b/java/tags/dns-2.0.1/src/books/users-guide/dns-proto-config.confluence
new file mode 100644
index 000000000..ea1146891
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/books/users-guide/dns-proto-config.confluence
@@ -0,0 +1,22 @@
+h1. DNS Protocol Configuration
+
+The DNS server has several configuration parameters for tuning the DNS communication protocol. Some of the basic settings are configurable in the services configuration [file|./dns-serv-depl.html], however the service can be tuned at a more granular level using the configuration UI web tool. Settings in the configuration UI take precedence over those in the configuration file.
+
+h2. Protocol Configuration Parameters
+
+Protocol configuration parameters are set using the same section of the configuration UI web tool as SMTP server [settings|../../../gateway/4.0/users-guide/smtp-depl-wsconfig#Anchors].
+
+!images/dnsconfigsetting.png!
+
+
+||Setting||Description||
+|DNSServerBindings|The IP addressed on the local machine that the service will bind to. Multiple IP addresses are separated with a comma. The default is 0.0.0.0 which means the service will bind to all IP addresses on the machine (including the loopback address of 127.0.0.1)|
+|DNSServerPort|The IP port that the DNS server will use for listening for DNS queries. The default is 53. \\NOTE: Some operating systems may require the service to run with elevated account privileges to open ports in this range.|
+|DNSServerMaxRequestSize|The maximum size in bytes for an query request. The default is 16K.|
+|DNSServerMaxConnectionBacklog|The maximum number of connections that are in the IP socket accept backlog. Socket backlog is only relevant for TCP session based connections. Setting this value to high can overload the IP stack or result in DNS client timeouts. The default value is 64.|
+|DNSServerMaxActiveRequests|The maximum number of concurrent requests that can be processed by the server at any give time. Setting this value to high may result in overloading the system. Setting this value to low can limit throughput. The default is 64.|
+|DNSServerMaxOutstandingRequests|The maximum number of requests that can be accepted by the server, but not yet committed to a processing thread. Setting this value to high may result in DNS clients timing out due to outstanding requests waiting to long for a processing thread. The default is 16 requests.|
+|DNSServerMaxRequestBuffer|The maximum size request buffer size in bytes. The default value is 1024 bytes.|
+|DNSServerSendTimeout|The socket timeout in milliseconds for sending responses. Setting this value to high can result in performance degradation if multiple clients abandon their sessions. Setting this value to low can result in clients not receiving responses in high latency environments. The default value is 5000 milliseconds.|
+|DNSServerRecieveTimeout|The socket timeout in milliseconds for receiving or reading request. Setting this value to high can result in performance degradation if multiple clients abandon their sessions. Setting this value to low can result in the server not fully reading request data in high latency environments. The default value is 5000 milliseconds.|
+|DNSServerSocketCloseTimeout|The timeout in milliseconds for closing a socket connection. The default value is 5000 milliseconds.|
\ No newline at end of file
diff --git a/java/tags/dns-2.0.1/src/books/users-guide/dns-record-config.confluence b/java/tags/dns-2.0.1/src/books/users-guide/dns-record-config.confluence
new file mode 100644
index 000000000..9ea175ecc
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/books/users-guide/dns-record-config.confluence
@@ -0,0 +1,152 @@
+h1. DNS Record Configuration
+
+The primary purpose of the DNS service is to respond to DNS queries with appropriate DNS record responses. By design, the DNS service can store any type of DNS record defined by the DNS RFC, however it is tuned only to respond to a few types of records.
+
+The DNS service stores all DNS records in the Direct Project configurations services and accesses them over the web service interface. The the public facing DNS interface does not allow for records to be added or changed in anyway. Instead, the configuration UI tool is used to manage and maintain DNS records.
+
+h2. Managing Records
+
+The configuration UI and config management command console tools are used to add, update, and delete all record types.
+
+h3. Config UI
+
+The Configuration UI tool is used to managed DNS records in the web UI. To managed records load the Configuration UI tool is a browser, login, and press the DNS Entries button.
+
+!images/WSConfigDomainSearch.png!
+
+!images/WSConfigDNSHome.png!
+
+h3. Config Management Console
+
+*NOTE* The DNS Management console has been deprecated and all DNS configuration commands are now available in the Config Management Console.
+
+The Config Management console is command line tool for managing all configuration aspects of the DirectProject Java reference implementation including DNS records. To run the console:
+
+# Launch a command shell and navigate to the ConfigMgmtConsole/bin directory.
+# Run the command _ConfigMgmtConsole_ (./ConfigMgmtConsole for unix based systems).
+
+The console connects to the Direct Project configuration service and manages records using the configuration service proxy module. By default it uses the url _http://localhost:8081/config-service/ConfigurationService_, but can be overridden using the configURL command line parameter.
+
+{code}
+ ConfigMgmtConsole configURL http://confserver/config-service/ConfigurationService
+{code}
+
+*NOTE* The configURL must be the first parameter on the command line to change the configuration service URL.
+
+The management console can either take command directly on the command line or run interactively. If no arguments or commands are passed on the command line (with the exception of the configURL), then console runs interactively. For a list of supported commands, simple type _help all_ either as command line arguments or when running interactively.
+
+h3. SOA Records
+
+The [SOA|http://support.microsoft.com/kb/163971] (start of authority) record defines (amongst other attributes) the host that is the authoritative server for a DNS zone/domain. Each domain managed by the DNS server should have a SOA record associated with it.
+
+*Config UI*
+
+SOA records are added by entering the required SOA record information in the Config UI tool on the _DNS Resolver Configuration_ page.
+
+!images/WSConfigSOARecords.png!
+
+*Management Console*
+
+SOA records are added using either of the following commands:
+* DNS_SOA_ADD
+* DNS_SOA_ENSURE
+
+{code}
+ Add a new SOA dns record if an identical one does not exist.
+ domainname primarysourcedomain responsibleemail serialnumber ttl [refresh] [retry] [expire] [minimum]
+ domainname: The domain name of the name server that was the primary source for this zone
+ responsibleemail: Email mailbox of the hostmaster
+ serialnumber: Version number of the original copy of the zone.
+ ttl: time to live in seconds, 32bit int
+ [refresh]: Number of seconds before the zone should be refreshed.
+ [retry]: Number of seconds before failed refresh should be retried.
+ [expire]: Number of seconds before records should be expired if not refreshed
+ [minimum]: Minimum TTL for this zone.
+{code}
+
+h3. A Records
+
+A records maps a host name to an IP4 address.
+
+*Config UI*
+
+A records are added by entering the required A record information in the Config UI tool on the _DNS Resolver Configuration_ page.
+
+!images/WSConfigARecords.png!
+
+*Management Console*
+
+A records are added using either of the following commands:
+* DNS_ANAME_ADD
+* DNS_ANAME_ENSURE
+
+{code}
+ Add a new A dns record if an identical one does not exist.
+ hostname ipaddress ttl
+ hostname: host name for the record
+ ipaddress: IP address in dot notation
+ ttl: time to live in seconds, 32bit int
+{code}
+
+h3. MX Records
+
+MX records map a domain to one of more servers or message transfer agents for that domain. The records are returned are generally are cononical names which means they must have A or CNAME records in the DNS server.
+
+*Config UI*
+
+MX records are added by entering the required MX record information in the Config UI tool on the _DNS Resolver Configuration_ page.
+
+!images/WSConfigMXRecords.png!
+
+*Management Console*
+
+A records are added using either of the following commands:
+* DNS_MX_ADD
+* DNS_MX_ENSURE
+
+{code}
+ Adds a new MX dns record if an identical one does't already exist.
+ domainname exchange ttl [priority]
+ domainname: email domain name for the record
+ exchange: smtp server host name for the domain
+ ttl: time to live in seconds
+ [priority]: short value indicating preference of the record
+{code}
+
+h3. CERT Records
+[Cert|http://tools.ietf.org/html/rfc4398] records store a certificate with a DNS name entry. The Direct Project DNS server only stores x.509 public certificates. *NOTE* The DNS server does not automatically resolve organizational level certificate resolution as defined by the security and trust specification; it only responds with the exact matches to a DNS query. The DNS client is responsibly for sending an org level query if the server does not respond with certificate to user level certificate query.
+
+Unlike all other DNS record types, the DNS service uses the configuration services certificate store to find certificates. Certificates are added to the store using the certificates button in the configuration UI web tool. See the SMTP web deployment [guide|../../../gateway/4.0/users-guide/smtp-depl-wsconfig#PrivateCertStore] for instruction on importing certificates into the configuration UI tool. *NOTE* Even though certificates in your domain will have the private key stored with the certificates, the DNS service only returns public certificate information for CERT record queries.
+
+h2. Required DNS Configuration
+
+Typically your domain registration service will allow you to define an NS (name service) record that will point the server running your DNS service. Your DNS service is then responsible for handling all queries for your domain(s). Depending on your deployment, you will need the following minimal set of DNS records for your Direct messaging implementation:
+
+* An SOA record for each domain managed by your server.
+* An A record for each of you email servers.
+* An MX record for each of you email servers server a domain.
+* Either an org level certificate for each domain or individual certificates per email address (this should already be configured as part of you SMTP agent/gateway installation).
+
+h2. Policy Enforcement
+
+It is possible for multiple certificates to exist for a specific address of domain. This is especially true in circumstances when single use certificates are used (i.e. one certificate for encryption and one certificate for digital signatures). In these cases, it likely that you will not want to publish all certificates for a addresses or domains over DNS. The Direct DNS server allow you to filter published certificate using the Policy Enablement [module|../../../direct-policy/1.0/users-guide/index.html].
+
+The Direct DNS server's configuration file (conf/wrapper.conf) defines a system parameter named _org.nhindirect.dns.CertPolicyName_ with a default value of _DNSCertPolicy_. This policy name indicates the name of the policy that the server will use for filtering certificates. If this parameter does not exist or references a policy that does not exist in the system, the server will fall back to not filtering certificates.
+
+h3. Policy Configuration
+
+To configure a policy for certificate filtering, first define a policy using the appropriate [syntax|../../../direct-policy/1.0/users-guide/tools-simpleTextV1.html#Simple_Text_Lexicon_Version_I]. As an example, let's say you want to only publish certificates that assert the key encipherment bit (which is probably the primary use case for using policy enforcement in the DNS server. Below is the syntax for this policy:
+
+{code}
+(X509.TBS.EXTENSION.KeyUsage & 32) > 0
+{code}
+
+Now let's say this policy is saved in a file named KeyEnc.pol. To add this policy to the system, run the Management Console application and execute the following sample command.
+
+{code}
+IMPORTPOLICY DNSCertPolicy KeyEnc.pol
+{code}
+
+This will create a policy named _DNSCertPolicy_ which contains the policy defined in the file KeyEnc.pol. When the DNS server launches, it will look for a policy with the name _DNSCertPolicy_ (remember the policy name is configured with system parameter org.nhindirect.dns.CertPolicyName in the wrapper.conf file) in the system and will apply the policy to all certificates that queried.
+
+If you choose, you can change the name of the policy that you want the DNS server to use by changing the value of the _org.nhindirect.dns.CertPolicyName_ parameter in the wrapper.conf file.
\ No newline at end of file
diff --git a/java/tags/dns-2.0.1/src/books/users-guide/dns-serv-depl.confluence b/java/tags/dns-2.0.1/src/books/users-guide/dns-serv-depl.confluence
new file mode 100644
index 000000000..78ced12a1
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/books/users-guide/dns-serv-depl.confluence
@@ -0,0 +1,166 @@
+h1. DNS Service Deployment
+
+The DNS services are deployable on a number of different operating environments and can be launched either interactively (for debugging) or as a background service. Although each OS configures and runs background services differently, the DNS service package contains OS specific script files that normalize the deployment process.
+
+h2. Service Package Layout
+
+The DNS services are assembled and packaged as a zipped tar ball. In side the package, the services are bundled using in the following directory structure.
+
+{code}
+ +-- DirectDNSServer
+ +-- bin
+ +-- conf
+ +-- lib
+ +-- logs
+{code}
+
+*Directory Contents*
+
+* bin - Contains installation and service launching/shutdown scripts.
+* conf - Contains service configuration files.
+* lib - Contains the binary code packages.
+* logs - Contains service execution log files.
+
+h2. Service Installation
+
+Installation of the DNS services is slightly different for each operating system, however each case uses the same assembly. To install, first download the Direct Project [assembly|http://repo2.maven.org/maven2/org/nhind/direct-project-stock] and unpack the contents into the desired location using your archiver of choice (tar, WinZip, WinRar, File Roller, etc).
+
+h3. Windows Installation
+
+The DNS server runs as Windows service on Windows deployments. To install as a service:
+
+# Launch a command shell and navigate to the DirectDNSServer/bin directory.
+# Run the command _DirectDNSServer install_
+
+This will install as the server as a windows service that starts automatically on system boot up. You should be able to see the service in the Windows service control panel (services.msc).
+
+!images/winservices.png!
+
+To start the server, use one of the following methods:
+
+* Right click on the service in the service control panel and click _start_.
+* In a command shell, run the command _DirectDNSServer start_.
+
+To stop the server, use one of the following methods:
+
+* Right click on the service in the service control panel and click _stop_.
+* In a command shell, run the command _DirectDNSServer stop_.
+
+To uninstall the server:
+
+# Stop the service using of the method described above.
+# Launch a command shell and navigate to the DirectDNSServer/bin directory.
+# Run the command _DirectDNSServer remove_
+
+*NOTE:* The service runs as process named _wrapper-windows-x86-32.exe_. If for some reason the service hangs or will not stop, you can terminate it manually by killing this process.
+
+h3. Linux Installation
+
+The DNS server runs as a background process on Linux based systems and can be optionally configured to run as a service daemon. To start the service manually:
+
+# Open a terminal shell and navigate to the DirectDNSServer/bin directory.
+# Run the command _./DirectDNSServer start_
+
+*NOTE:* If you get an error of "Permissioned denied" you will need to set the executable flag on the script files:
+
+{code}
+ chmod +x DirectDNSServer
+ chmod +x wrapper-linux-x86-32 (or wrapper-linux-x86-64 depending if you are using a 64 or bit linux installation).
+{code}
+
+To stop the server:
+
+# Open a terminal shell and navigate to the DirectDNSServer/bin directory.
+# Run the command _DirectDNSServer stop_.
+
+You can also optionally configure the server as a service. There are different ways to do this depending on your linux distribution. On possibility is to create a script file in the /etc/init.d directory.
+
+Assuming you have deployed the server in the /opt directory and you are running Ubuntu, create the file /etc/init.d/DirectDNSServer using the editor of you choice paste the following content:
+
+{code}
+ # DirectDNSServer auto-start
+ #
+ # description: Auto-starts the DirectDNSServer
+
+ case $1 in
+ start)
+ sh /opt/DirectDNSServer/bin/DirectDNSServer start
+ ;;
+ stop)
+ sh /opt/DirectDNSServer/bin/DirectDNSServer stop
+ ;;
+ restart)
+ sh /opt/DirectDNSServer/bin/DirectDNSServer start
+ sh /opt/DirectDNSServer/bin/DirectDNSServer stop
+ ;;
+
+ esac
+ exit 0
+{code}
+
+Make the script executable using the following command:
+
+{code}
+sudo chmod 755 /etc/init.d/DirectDNSServer
+{code}
+
+You can then start the service by running the command:
+
+{code}
+service DirectDNSServer start
+{code}
+
+Conversely you can stop the service by running the command:
+
+{code}
+service DirectDNSServer stop
+{code}
+
+h3. Running Interactively
+
+For debugging or troubleshooting purposes, you may need to run the service interactively. Running interactively is the same across all platforms.
+
+# Open a terminal shell and navigate to the DirectDNSServer/bin directory.
+# Run the command _DirectDNSServer console_.
+
+The service will output all logging to the current console and the log file. To terminate the interactive service, simply press _CTRL+C_ (Control C).
+
+h2. Service Deployment Configuration
+
+The service deployment is configured using a file named _wrapper.conf_ found in the ./conf directory of the service's directory structure. The service script files read this configuration file to set runtime attributes such as classpath, logging locations and thresholds, JVM arguments, and application arguments. The service itself is just a plain Java application, but is wrapped by a series of deployment classes that are intialized and launched by the service script.
+
+The configuration file in most cases does not need a lot of modification, however there a few settings that will need adjustment depending on your deployment.
+
+{code}
+# Java Additional Parameters
+wrapper.java.additional.1=-Djava.security.policy=conf/policy.all
+wrapper.java.additional.2=-Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog
+wrapper.java.additional.3=-Dorg.apache.commons.logging.simplelog.defaultlog=info
+.
+.
+.
+# Application parameters. Add parameters as needed starting from 1
+wrapper.app.parameter.1=org.nhindirect.dns.service.SimpleServiceRunner
+wrapper.app.parameter.2=-p
+wrapper.app.parameter.3=53
+wrapper.app.parameter.4=-b
+wrapper.app.parameter.5=0.0.0.0
+wrapper.app.parameter.6=-u
+wrapper.app.parameter.7=http://localhost:8081/config-service/ConfigurationService
+wrapper.app.parameter.8=-m
+wrapper.app.parameter.9=SERVER
+{code}
+
+Typical settings that may need adjustment.
+
+||Setting||Description||
+|-Dorg.apache.commons.logging.simplelog.defaultlog|The logging threshold. Valid value: \\ \\fatal: Severe errors that cause premature termination\\error: Other runtime errors or unexpected conditions.\\warn: Use of deprecated APIs, poor use of API, 'almost' errors, other runtime situations that are undesirable or unexpected, but not necessarily "wrong".\\info (default): Interesting runtime events such as startup/shutdown.\\debug: Detailed information on flow of through the system.\\trace: Even more detailed information such as entering and exiting methods.|
+|wrapper.app.parameter.2=-p|The IP port that the DNS server will use for listening for DNS queries. The default is 53. \\NOTE: Some operating systems may require the service to run with elevated account privileges to open ports in this range.|
+|wrapper.app.parameter.4=-b|The IP addressed on the local machine that the service will bind to. Multiple IP addresses are separated with a comma. The default is 0.0.0.0 which means the service will bind to all IP addresses on the machine (including the loopback address of 127.0.0.1)|
+|wrapper.app.parameter.6=-u|This is the URL of the location of the DNS records. Generally this will be the URL of the Direct Project configuration web service.|
+
+Some of these parameters can be over ridden with settings in the configuration service. See the protocol [configuration|./dns-proto-config.html] for more details.
+
+h2. Service Logging
+
+The service logs are written to the file _wrapper.txt_ found in the ./logs directory of the service's directory structure. Logging threshold configuration is described in the previous section.
\ No newline at end of file
diff --git a/java/tags/dns-2.0.1/src/books/users-guide/images/WSConfigARecords.png b/java/tags/dns-2.0.1/src/books/users-guide/images/WSConfigARecords.png
new file mode 100644
index 000000000..0ee8bc906
Binary files /dev/null and b/java/tags/dns-2.0.1/src/books/users-guide/images/WSConfigARecords.png differ
diff --git a/java/tags/dns-2.0.1/src/books/users-guide/images/WSConfigDNSHome.png b/java/tags/dns-2.0.1/src/books/users-guide/images/WSConfigDNSHome.png
new file mode 100644
index 000000000..5668a7824
Binary files /dev/null and b/java/tags/dns-2.0.1/src/books/users-guide/images/WSConfigDNSHome.png differ
diff --git a/java/tags/dns-2.0.1/src/books/users-guide/images/WSConfigDomainSearch.png b/java/tags/dns-2.0.1/src/books/users-guide/images/WSConfigDomainSearch.png
new file mode 100644
index 000000000..1e50bc27e
Binary files /dev/null and b/java/tags/dns-2.0.1/src/books/users-guide/images/WSConfigDomainSearch.png differ
diff --git a/java/tags/dns-2.0.1/src/books/users-guide/images/WSConfigMXRecords.png b/java/tags/dns-2.0.1/src/books/users-guide/images/WSConfigMXRecords.png
new file mode 100644
index 000000000..825dc28d4
Binary files /dev/null and b/java/tags/dns-2.0.1/src/books/users-guide/images/WSConfigMXRecords.png differ
diff --git a/java/tags/dns-2.0.1/src/books/users-guide/images/WSConfigSOARecords.png b/java/tags/dns-2.0.1/src/books/users-guide/images/WSConfigSOARecords.png
new file mode 100644
index 000000000..419313ea1
Binary files /dev/null and b/java/tags/dns-2.0.1/src/books/users-guide/images/WSConfigSOARecords.png differ
diff --git a/java/tags/dns-2.0.1/src/books/users-guide/images/dnsconfigsetting.png b/java/tags/dns-2.0.1/src/books/users-guide/images/dnsconfigsetting.png
new file mode 100644
index 000000000..3f773620a
Binary files /dev/null and b/java/tags/dns-2.0.1/src/books/users-guide/images/dnsconfigsetting.png differ
diff --git a/java/tags/dns-2.0.1/src/books/users-guide/images/winservices.png b/java/tags/dns-2.0.1/src/books/users-guide/images/winservices.png
new file mode 100644
index 000000000..9cc7c1863
Binary files /dev/null and b/java/tags/dns-2.0.1/src/books/users-guide/images/winservices.png differ
diff --git a/java/tags/dns-2.0.1/src/books/users-guide/preface.apt b/java/tags/dns-2.0.1/src/books/users-guide/preface.apt
new file mode 100644
index 000000000..157e3daab
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/books/users-guide/preface.apt
@@ -0,0 +1,11 @@
+ -----
+ Introduction
+ -----
+ Greg Meyer
+ -----
+
+About this Document
+
+ This document describes how to implement and configure the Direct Project Java DNS server for certificate distribution.
+
+ * {{{./deploy-intro.html}Development Guide}} - This section describes how deploy and configure the DNS services.
\ No newline at end of file
diff --git a/java/tags/dns-2.0.1/src/descriptors/distribution.xml b/java/tags/dns-2.0.1/src/descriptors/distribution.xml
new file mode 100644
index 000000000..76b57cc85
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/descriptors/distribution.xml
@@ -0,0 +1,19 @@
+
+ assembly
+ DirectDNSServices
+
+ tar.gz
+
+
+
+ ${project.build.directory}/appassembler/jsw/DirectDNSServer
+ /DirectDNSServer
+
+
+ ${project.build.directory}/appassembler/app/DNSMgmtConsole
+ /DNSMgmtConsole
+
+
+
diff --git a/java/tags/dns-2.0.1/src/logs/wrapper.txt b/java/tags/dns-2.0.1/src/logs/wrapper.txt
new file mode 100644
index 000000000..e69de29bb
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/AbstractDNSStore.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/AbstractDNSStore.java
new file mode 100644
index 000000000..0d772823b
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/AbstractDNSStore.java
@@ -0,0 +1,268 @@
+package org.nhindirect.dns;
+
+import java.security.Security;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.nhindirect.policy.PolicyExpression;
+import org.nhindirect.policy.PolicyFilter;
+import org.xbill.DNS.DClass;
+import org.xbill.DNS.Flags;
+import org.xbill.DNS.Header;
+import org.xbill.DNS.InvalidTypeException;
+import org.xbill.DNS.Message;
+import org.xbill.DNS.Name;
+import org.xbill.DNS.Opcode;
+import org.xbill.DNS.RRset;
+import org.xbill.DNS.Rcode;
+import org.xbill.DNS.Record;
+import org.xbill.DNS.Section;
+import org.xbill.DNS.Type;
+
+public abstract class AbstractDNSStore implements DNSStore
+{
+ protected static final Log LOGGER = LogFactory.getFactory().getInstance(AbstractDNSStore.class);
+
+ protected static final String DNS_CERT_POLICY_NAME_VAR = "org.nhindirect.dns.CertPolicyName";
+
+ protected static final String DEFAULT_JCE_PROVIDER_STRING = "BC";
+ protected static final String JCE_PROVIDER_STRING_SYS_PARAM = "org.nhindirect.dns.JCEProviderName";
+
+ protected Map soaRecords = null;
+
+ protected PolicyFilter polFilter = null;
+ protected PolicyExpression polExpression = null;
+
+ static
+ {
+ Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
+ }
+
+ /**
+ * Gets the configured JCE crypto provider string for crypto operations. This is configured using the
+ * -Dorg.nhindirect.dns.JCEProviderName JVM parameters. If the parameter is not set or is empty,
+ * then the default string "BC" (BouncyCastle provider) is returned. By default the agent installs the BouncyCastle provider.
+ * @return The name of the JCE provider string.
+ */
+ public static String getJCEProviderName()
+ {
+ String retVal = System.getProperty(JCE_PROVIDER_STRING_SYS_PARAM);
+
+ if (retVal == null || retVal.isEmpty())
+ retVal = DEFAULT_JCE_PROVIDER_STRING;
+
+ return retVal;
+ }
+
+ /**
+ * Overrides the configured JCE crypto provider string. If the name is empty or null, the default string "BC" (BouncyCastle provider)
+ * is used.
+ * @param name The name of the JCE provider.
+ */
+ public static void setJCEProviderName(String name)
+ {
+ if (name == null || name.isEmpty())
+ System.setProperty(JCE_PROVIDER_STRING_SYS_PARAM, DEFAULT_JCE_PROVIDER_STRING);
+ else
+ System.setProperty(JCE_PROVIDER_STRING_SYS_PARAM, name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public Message get(Message request) throws DNSException
+ {
+ LOGGER.trace("get(Message) Entered");
+ /* for testing time out cases
+ try
+ {
+ Thread.sleep(1000000);
+ }
+ catch (Exception e)
+ {
+
+ }
+ */
+ if (request == null)
+ throw new DNSException(DNSError.newError(Rcode.FORMERR));
+
+ Header header = request.getHeader();
+ if (header.getFlag(Flags.QR) || header.getRcode() != Rcode.NOERROR)
+ throw new DNSException(DNSError.newError(Rcode.FORMERR));
+
+ if (header.getOpcode() != Opcode.QUERY)
+ throw new DNSException(DNSError.newError(Rcode.NOTIMP));
+
+ Record queryRecord = request.getQuestion();
+
+ if (queryRecord == null || queryRecord.getDClass() != DClass.IN)
+ {
+ throw new DNSException(DNSError.newError(Rcode.NOTIMP));
+ }
+
+ Name name = queryRecord.getName();
+ int type = queryRecord.getType();
+ String typeString = null;
+ try {
+ typeString = Type.string(type);
+ } catch(InvalidTypeException e) {
+ }
+
+ if (LOGGER.isDebugEnabled())
+ {
+ StringBuilder builder = new StringBuilder("Received Query Request:");
+ builder.append("\r\n\tName: " + name.toString());
+ builder.append("\r\n\tType: " + (typeString == null ? type : typeString));
+ builder.append("\r\n\tDClass: " + queryRecord.getDClass());
+ LOGGER.debug(builder.toString());
+ }
+
+ LOGGER.info("Process record for DNS request type " + (typeString == null ? type : typeString) + " and name " + name.toString());
+
+ Collection lookupRecords= null;
+ switch (type)
+ {
+ case Type.A:
+ case Type.MX:
+ case Type.SOA:
+ case Type.SRV:
+ case Type.NS:
+ case Type.CNAME:
+ {
+ try
+ {
+ final RRset set = processGenericRecordRequest(name.toString(), type);
+
+ if (set != null)
+ {
+ lookupRecords = new ArrayList();
+ Iterator iter = set.rrs();
+ while (iter.hasNext())
+ lookupRecords.add(iter.next());
+ }
+
+ }
+ catch (Exception e)
+ {
+ throw new DNSException(DNSError.newError(Rcode.SERVFAIL), "DNS service proxy call failed: " + e.getMessage(), e);
+ }
+ break;
+ }
+ case Type.CERT:
+ {
+ final RRset set = processCERTRecordRequest(name.toString());
+
+ if (set != null)
+ {
+ lookupRecords = new ArrayList();
+ Iterator iter = set.rrs();
+ while (iter.hasNext())
+ lookupRecords.add(iter.next());
+ }
+
+ break;
+ }
+ case Type.ANY:
+ {
+
+ Collection genRecs = processGenericANYRecordRequest(name.toString());
+ RRset certRecs = processCERTRecordRequest(name.toString());
+
+ if (genRecs != null || certRecs != null)
+ {
+ lookupRecords = new ArrayList();
+ if (genRecs != null)
+ lookupRecords.addAll(genRecs);
+
+ if (certRecs != null)
+ {
+ Iterator iter = certRecs.rrs();
+ while (iter.hasNext())
+ lookupRecords.add(iter.next());
+ }
+ }
+
+ break;
+ }
+ default:
+ {
+ LOGGER.debug("Query Type " + (typeString == null ? type : typeString) + " not implemented");
+ throw new DNSException(DNSError.newError(Rcode.NOTIMP), "Query Type " + (typeString == null ? type : typeString) + " not implemented");
+ }
+ }
+
+
+ if (lookupRecords == null || lookupRecords.size() == 0)
+ {
+ LOGGER.debug("No records found.");
+ return null;
+ }
+
+ final Message response = new Message(request.getHeader().getID());
+ response.getHeader().setFlag(Flags.QR);
+ if (request.getHeader().getFlag(Flags.RD))
+ response.getHeader().setFlag(Flags.RD);
+ response.addRecord(queryRecord, Section.QUESTION);
+
+
+ final Iterator iter = lookupRecords.iterator();
+ while (iter.hasNext())
+ response.addRecord(iter.next(), Section.ANSWER);
+
+ // we are authoritative only
+ response.getHeader().setFlag(Flags.AA);
+ // look for an SOA record
+ final Record soaRecord = checkForSoaRecord(name.toString());
+ if (soaRecord != null)
+ response.addRecord(soaRecord, Section.AUTHORITY);
+
+ LOGGER.trace("get(Message) Exit");
+
+ return response;
+ }
+
+ /**
+ * Processes all DNS requests except CERT records.
+ * @param name The record name.
+ * @param type The record type.
+ * @return Returns a set of record responses to the request.
+ * @throws DNSException
+ */
+ protected abstract RRset processGenericRecordRequest(String name, int type) throws DNSException;
+
+ /**
+ * Processes all DNS CERT requests.
+ * @param name The record name. In many cases this a email address.
+ * @return Returns a set of record responses to the request.
+ * @throws DNSException
+ */
+ protected abstract RRset processCERTRecordRequest(String name) throws DNSException;
+
+ protected abstract Collection processGenericANYRecordRequest(String name) throws DNSException;
+
+ protected abstract Record checkForSoaRecord(String questionName);
+
+ protected boolean isCertCompliantWithPolicy(X509Certificate cert)
+ {
+ // if no policy has been set, then always return true
+ if (this.polFilter == null)
+ return true;
+
+ try
+ {
+ return this.polFilter.isCompliant(cert, this.polExpression);
+ }
+ catch (Exception e)
+ {
+ LOGGER.warn("Error testing certificate for policy compliance. Default to compliant.", e);
+ return true;
+ }
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/ConfigServiceDNSStore.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/ConfigServiceDNSStore.java
new file mode 100644
index 000000000..29614e141
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/ConfigServiceDNSStore.java
@@ -0,0 +1,412 @@
+/*
+ Copyright (c) 2010, Direct Project
+ All rights reserved.
+
+ Authors:
+ Umesh Madan umeshma@microsoft.com
+ Chris Lomonico chris.lomonico@surescripts.com
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of The Direct Project (directproject.org) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAKey;
+
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.nhind.config.Certificate;
+import org.nhind.config.ConfigurationServiceProxy;
+import org.nhind.config.DnsRecord;
+import org.nhindirect.config.model.exceptions.CertificateConversionException;
+import org.nhindirect.config.model.utils.CertUtils;
+import org.nhindirect.dns.annotation.ConfigServiceURL;
+import org.nhindirect.policy.PolicyFilterFactory;
+import org.nhindirect.policy.PolicyLexicon;
+import org.nhindirect.policy.PolicyLexiconParser;
+import org.nhindirect.policy.PolicyLexiconParserFactory;
+import org.nhindirect.policy.x509.SignatureAlgorithmIdentifier;
+import org.xbill.DNS.CERTRecord;
+import org.xbill.DNS.DClass;
+import org.xbill.DNS.Name;
+import org.xbill.DNS.RRset;
+import org.xbill.DNS.Rcode;
+import org.xbill.DNS.Record;
+import org.xbill.DNS.Type;
+
+
+import com.google.inject.Inject;
+
+/**
+ * Implementation of the the {@link DNStore} interface that uses the Direct Project configuration web service to store
+ * DNS records.
+ * @author Greg Meyer
+ * @since 1.0
+ */
+public class ConfigServiceDNSStore extends AbstractDNSStore
+{
+ protected static final Log LOGGER = LogFactory.getFactory().getInstance(ConfigServiceDNSStore.class);
+
+ final ConfigurationServiceProxy proxy;
+
+ /**
+ * Creates a store using the provided URL to lookup DNS records in the configuration service.
+ * @param serviceURL The URL of the configuration service.
+ */
+ @Inject
+ public ConfigServiceDNSStore(@ConfigServiceURL URL serviceURL)
+ {
+ proxy = new ConfigurationServiceProxy(serviceURL.toString());
+
+ try
+ {
+ configCertPolicy();
+ }
+ catch (DNSException e)
+ {
+ throw new IllegalStateException(e);
+ }
+
+ }
+
+ /**
+ * Checks to see if a certificate policy has been configured.
+ */
+ protected void configCertPolicy() throws DNSException
+ {
+ // check to see if there is a certificate policy set
+ final String polName = System.getProperty(DNS_CERT_POLICY_NAME_VAR);
+ if (!StringUtils.isEmpty(polName))
+ {
+ InputStream inStream = null;
+ LOGGER.info("Certificate policy name " + polName + " has been configured.");
+ try
+ {
+ // get the policy by name
+ final org.nhind.config.CertPolicy policy = proxy.getPolicyByName(polName);
+ if (policy == null)
+ {
+ LOGGER.warn("Certificate policy " + polName + " could not be found in the system. Falling back to no policy.");
+ return;
+ }
+
+ // now compile the policy into an expression
+ final PolicyLexiconParser parser = PolicyLexiconParserFactory.getInstance(PolicyLexicon.valueOf(policy.getLexicon().getValue()));
+ inStream = new ByteArrayInputStream(policy.getPolicyData());
+ this.polExpression = parser.parse(inStream);
+
+ // now create the filter
+ this.polFilter = PolicyFilterFactory.getInstance();
+
+ }
+ catch (Exception e)
+ {
+ // it's OK if can't find the certificate policy that was configured, we'll just log a warning
+ // it's also OK if we can't download or parse the policy, but we need to log the error
+ LOGGER.warn("Error loading and compling certificate policy " + polName + ". Will fallback to no policy filter.", e);
+ }
+ finally
+ {
+ IOUtils.closeQuietly(inStream);
+ }
+ }
+ else
+ LOGGER.info("No certificate policy has been configured.");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected RRset processGenericRecordRequest(String name, int type) throws DNSException
+ {
+ DnsRecord records[];
+
+ try
+ {
+ records = proxy.getDNSByNameAndType(name, type);
+ }
+ catch (Exception e)
+ {
+ throw new DNSException(DNSError.newError(Rcode.SERVFAIL), "DNS service proxy call for DNS records failed: " + e.getMessage(), e);
+ }
+
+ if (records == null || records.length == 0)
+ return null;
+
+ RRset retVal = new RRset();
+ try
+ {
+ for (DnsRecord record : records)
+ {
+ Record rec = Record.newRecord(Name.fromString(record.getName()), record.getType(),
+ record.getDclass(), record.getTtl(), record.getData());
+
+ retVal.addRR(rec);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new DNSException(DNSError.newError(Rcode.SERVFAIL), "Failure while parsing generic record data: " + e.getMessage(), e);
+ }
+
+ return retVal;
+ }
+
+ @Override
+ protected Collection processGenericANYRecordRequest(String name) throws DNSException
+ {
+ DnsRecord records[];
+
+ try
+ {
+ records = proxy.getDNSByNameAndType(name, Type.ANY);
+ }
+ catch (Exception e)
+ {
+ throw new DNSException(DNSError.newError(Rcode.SERVFAIL), "DNS service proxy call for DNS records failed: " + e.getMessage(), e);
+ }
+
+ if (records == null || records.length == 0)
+ return null;
+
+ Collection retVal = new ArrayList();
+ try
+ {
+ for (DnsRecord record : records)
+ {
+ Record rec = Record.newRecord(Name.fromString(record.getName()), record.getType(),
+ record.getDclass(), record.getTtl(), record.getData());
+
+ retVal.add(rec);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new DNSException(DNSError.newError(Rcode.SERVFAIL), "Failure while parsing generic record data: " + e.getMessage(), e);
+ }
+
+ return retVal;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("unused")
+ @Override
+ protected RRset processCERTRecordRequest(String name) throws DNSException
+ {
+ if (name.endsWith("."))
+ name = name.substring(0, name.length() - 1);
+
+ Certificate[] certs;
+
+ // use the certificate configuration service
+ try
+ {
+ certs = proxy.getCertificatesForOwner(name, null);
+ }
+ catch (Exception e)
+ {
+ throw new DNSException(DNSError.newError(Rcode.SERVFAIL), "DNS service proxy call for certificates failed: " + e.getMessage(), e);
+ }
+
+ if (certs == null || certs.length == 0)
+ {
+ // unless the call above was for an org level cert, it will probably always fail because the
+ // "name" parameter has had all instances of "@" replaced with ".". The certificate service
+ // stores owners using "@".
+ // This is horrible, but try hitting the cert service replacing each "." with "@" one by one.
+ // Start at the beginning of the address because this is more than likely where the "@" character
+ // will be.
+ int previousIndex = 0;
+ int replaceIndex = 0;
+ while ((replaceIndex = name.indexOf(".", previousIndex)) > -1)
+ {
+ char[] chars = name.toCharArray();
+ chars[replaceIndex] = '@';
+ try
+ {
+ certs = proxy.getCertificatesForOwner(String.copyValueOf(chars), null);
+ }
+ catch (Exception e)
+ {
+ throw new DNSException(DNSError.newError(Rcode.SERVFAIL), "DNS service proxy call for certificates failed: " + e.getMessage(), e);
+ }
+ if (certs != null && certs.length > 0)
+ break;
+
+ if (replaceIndex >= (name.length() - 1))
+ break;
+
+ previousIndex = replaceIndex + 1;
+ }
+ }
+
+ if (certs == null || certs.length == 0)
+ return null;
+
+ if (!name.endsWith("."))
+ name += ".";
+
+ RRset retVal = new RRset();
+ try
+ {
+ for (Certificate cert : certs)
+ {
+ int certRecordType = CERTRecord.PKIX;
+ byte[] retData = null;
+
+ X509Certificate xCert = null;
+ try
+ {
+ // need to convert to cert container because this might be
+ // a certificate with wrapped private key data
+ final CertUtils.CertContainer cont = CertUtils.toCertContainer(cert.getData());
+ xCert = cont.getCert();
+ // check if this is a compliant certificate with the configured policy... if not, move on
+ if (!isCertCompliantWithPolicy(xCert))
+ continue;
+
+ retData = xCert.getEncoded();
+ }
+ catch (CertificateConversionException e)
+ {
+ // probably not a Certificate... might be a URL
+ }
+
+
+ if (xCert == null)
+ {
+ // see if it's a URL
+ try
+ {
+ retData = cert.getData();
+ URL url = new URL(new String(retData));
+ certRecordType = CERTRecord.URI;
+ }
+ catch (Exception e)
+ {
+ throw new DNSException(DNSError.newError(Rcode.SERVFAIL), "Failure while parsing CERT record data: " + e.getMessage(), e);
+ }
+ }
+
+ int keyTag = 0;
+ int alg = 0;
+ if (xCert != null && xCert.getPublicKey() instanceof RSAKey)
+ {
+ RSAKey key = (RSAKey)xCert.getPublicKey();
+ byte[] modulus = key.getModulus().toByteArray();
+
+ keyTag = (modulus[modulus.length - 2] << 8) & 0xFF00;
+
+ keyTag |= modulus[modulus.length - 1] & 0xFF;
+ if (xCert.getSigAlgOID().equalsIgnoreCase(SignatureAlgorithmIdentifier.SHA1RSA.getId())){
+ alg = 5; // RFC 4034 Appendix A.1
+ } else if (xCert.getSigAlgOID().equalsIgnoreCase(SignatureAlgorithmIdentifier.SHA256RSA.getId())){
+ alg = 8; // RFC 5702 3.1
+ } else if (xCert.getSigAlgOID().equalsIgnoreCase(SignatureAlgorithmIdentifier.SHA1DSA.getId())){
+ alg = 3; // RFC 4034 Appendix A.1
+ } else if (xCert.getSigAlgOID().equalsIgnoreCase(SignatureAlgorithmIdentifier.MD5RSA.getId())){
+ alg = 1; // RFC 4034 Appendix A.1
+ } else{
+ alg = 5;
+ }
+ }
+
+ CERTRecord rec = new CERTRecord(Name.fromString(name), DClass.IN, 86400L, certRecordType, keyTag,
+ alg /*public key alg, RFC 4034*/, retData);
+
+ retVal.addRR(rec);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new DNSException(DNSError.newError(Rcode.SERVFAIL), "Failure while parsing CERT record data: " + e.getMessage(), e);
+ }
+
+ // because of policy filtering, it's possible that we could have filtered out every cert
+ // resulting in an empty RR set
+ return (retVal.size() == 0) ? null : retVal;
+ }
+
+ /*
+ * Look for SOA records corresponding to the request
+ * TODO: Add cache coherency to SOA records?
+ */
+ @Override
+ protected synchronized Record checkForSoaRecord(String questionName)
+ {
+ if (!questionName.endsWith("."))
+ questionName += ".";
+
+ if (soaRecords == null)
+ {
+ DnsRecord[] getRecs = null;
+ // load all SOA records...
+ try
+ {
+ getRecs = proxy.getDNSByType(Type.SOA);
+
+ if (getRecs == null || getRecs.length == 0)
+ soaRecords = Collections.emptyMap();
+ else
+ {
+ soaRecords = new HashMap();
+
+ for (DnsRecord rec : getRecs)
+ {
+ Record newRec = Record.newRecord(Name.fromString(rec.getName()), Type.SOA,
+ rec.getDclass(), rec.getTtl(), rec.getData());
+
+ soaRecords.put(newRec.getName().toString(), newRec);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ LOGGER.error("Failed to load SOA records from config service.");
+ }
+ }
+
+ Record retVal = null;
+ if (soaRecords.size() > 0)
+ {
+ // look for the record by question name
+
+ retVal = soaRecords.get(questionName);
+ if (retVal == null)
+ {
+ // start taking apart the question name . by .
+ int index = -1;
+ while ((index = questionName.indexOf(".")) > 0 && index < (questionName.length() - 1))
+ {
+ questionName = questionName.substring(index + 1);
+ retVal = soaRecords.get(questionName);
+ if (retVal != null)
+ break;
+ }
+ }
+ }
+
+ return retVal;
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSError.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSError.java
new file mode 100644
index 000000000..7a0e1b5ea
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSError.java
@@ -0,0 +1,72 @@
+/*
+Copyright (c) 2010, NHIN Direct Project
+All rights reserved.
+
+Authors:
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the distribution. Neither the name of the The NHIN Direct Project (nhindirect.org).
+nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns;
+
+/**
+ * Container for a DNS error code.
+ * @author Greg Meyer
+ *
+ * @param The error type. Typically a long or integer describing an error code.
+ * @since 1.0
+ */
+public class DNSError
+{
+ /**
+ * Creates a new DNSError with an internal error code.
+ * @param The error Type. Typically a long or integer describing an error code.
+ * @param error The error describing the error condition.
+ * @return
+ */
+ public static DNSError newError(T error)
+ {
+ return new DNSError(error);
+ }
+
+ private final T error;
+
+ /**
+ * Constructs a new DNSError.
+ * @param error The error describing the error condition.
+ */
+ public DNSError(T error)
+ {
+ this.error = error;
+ }
+
+ /**
+ * Gets the internal error. The error is typically a long or integer describing an error code.
+ * @return The internal error.
+ */
+ public T getError()
+ {
+ return error;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString()
+ {
+ return error.toString();
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSException.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSException.java
new file mode 100644
index 000000000..ed930cf2f
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSException.java
@@ -0,0 +1,95 @@
+/*
+Copyright (c) 2010, NHIN Direct Project
+All rights reserved.
+
+Authors:
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the distribution. Neither the name of the The NHIN Direct Project (nhindirect.org).
+nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns;
+
+/**
+ * Exception for DNS server errors.
+ * @author Greg Meyer
+ * @since 1.0
+ */
+public class DNSException extends Exception
+{
+
+ static final long serialVersionUID = 1852021431664662496L;
+
+
+ private DNSError> error;
+
+ /**
+ * Construct an exception with an error message.
+ * @param error The error message.
+ */
+ public DNSException(String error)
+ {
+ super(error);
+ }
+
+ /**
+ * Construct an exception with a given DNS error.
+ * @param error The DNS error.
+ */
+ public DNSException(DNSError> error)
+ {
+ this(error, "");
+ }
+
+ /**
+ * Constructs an exception with a message and the DNS error.
+ * @param error The DNS error
+ * @param msg The exception message.
+ */
+ public DNSException(DNSError> error, String message)
+ {
+ this(error,message,null);
+ }
+
+ /**
+ * Constructs an exception with the DNS error and the exception that caused the error.
+ * @param error The DNS error.
+ * @param innerException The exception that caused the error.
+ */
+ public DNSException(DNSError> error, Exception innerException)
+ {
+ this(error, "", innerException);
+ }
+
+ /**
+ * Constructs an exception with the DNS error, a message, and the exception that caused the error.
+ * @param error The DNS error.
+ * @param msg The exception message.
+ * @param innerException The exception that caused the error.
+ */
+ public DNSException(DNSError> error, String message, Exception innerException)
+ {
+ super(message, innerException);
+ this.error = error;
+ }
+
+ /**
+ * Gets the internal DNSError.
+ * @return The internal DNSError.
+ */
+ public DNSError> getError()
+ {
+ return this.error;
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSResponder.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSResponder.java
new file mode 100644
index 000000000..f8eb00d36
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSResponder.java
@@ -0,0 +1,189 @@
+/*
+ Copyright (c) 2010, Direct Project
+ All rights reserved.
+
+ Authors:
+ Umesh Madan umeshma@microsoft.com
+ Chris Lomonico chris.lomonico@surescripts.com
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of The Direct Project (directproject.org) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns;
+
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.xbill.DNS.Flags;
+import org.xbill.DNS.Header;
+import org.xbill.DNS.Message;
+import org.xbill.DNS.Rcode;
+import org.xbill.DNS.Section;
+
+/**
+ * Abstract DNSResponder for DNS requests. It implements common methods for calling the DNS store and handling error conditions. Protocol specific
+ * (UDP, TCP, etc) messaging handling is implemented in concrete implementations.
+ * @author Greg Meyer
+ * @since 1.0
+ */
+public abstract class DNSResponder
+{
+ private static final Log LOGGER = LogFactory.getFactory().getInstance(DNSResponder.class);
+
+ protected DNSServerSettings settings;
+ protected DNSStore store;
+
+ /**
+ * Creates a DNS responder using the provided settings and DNS store. The responder will not handle requests
+ * until {@link #start()} is called.
+ * @param settings The DNS server settings.
+ * @param store The DNS store that holds the DNS record information.
+ * @throws DNSException
+ */
+ public DNSResponder(DNSServerSettings settings, DNSStore store) throws DNSException
+ {
+ this.settings = settings;
+ this.store = store;
+ }
+
+ /**
+ * Starts the responder. Concrete implementation bind their protocol specific handlers and start accepting DNS requests.
+ * @throws DNSException
+ */
+ public abstract void start() throws DNSException;
+
+ /**
+ * Stops the responder. The responder will not londer accept DNS requests after stop has been called.
+ * @throws DNSException
+ */
+ public abstract void stop() throws DNSException;
+
+ /**
+ * Processes a DNS request and returns a DNS response. The request is in raw DNS wire protocol format.
+ * @param rawMessage The raw DNS wire protocol format of the request.
+ * @return A response to the DNS request.
+ * @throws DNSException
+ */
+ public Message processRequest(byte[] rawMessage) throws DNSException
+ {
+ if (rawMessage == null || rawMessage.length == 0)
+ throw new DNSException(DNSError.newError(Rcode.FORMERR), "Message cannot be null or empty.");
+
+ Message msg;
+ try
+ {
+ msg = new Message(rawMessage);
+ }
+ catch (IOException e)
+ {
+ throw new DNSException(DNSError.newError(Rcode.FORMERR), "IO Exception reading raw message.", e);
+ }
+
+ return processRequest(msg);
+ }
+
+ /**
+ * Processes a DNS request and returns a DNS response.
+ * @param request The DNS request message.
+ * @return A response to the DNS request.
+ */
+ public Message processRequest(Message request)
+ {
+ if (request == null)
+ throw new IllegalArgumentException("Missing request. Request cannot be null.");
+
+ Message response;
+ try
+ {
+ response = store.get(request);
+ if (response == null || response.getHeader() == null)
+ {
+
+ response = processError(request, DNSError.newError(Rcode.NXDOMAIN));
+ }
+ else if (response.getHeader().getRcode() != Rcode.NOERROR)
+ response = processError(request, DNSError.newError(response.getHeader().getRcode()));
+ }
+ catch (DNSException e)
+ {
+ // don't log as an error if it's just a non implemented query type
+ if (!e.getError().getError().equals(Rcode.NOTIMP))
+ {
+ LOGGER.error("Error processing DNS request: " + e.getMessage(), e);
+ }
+ response = processError(request, e.getError());
+ }
+
+ return response;
+ }
+
+ /**
+ * Processes a DNS error condition and creates an appropriate DNS response.
+ * @param request The original DNS request.
+ * @param error The error condition that occured.
+ * @return A response to the DNS request.
+ */
+ protected Message processError(Message request, DNSError> error)
+ {
+ Message errorResponse = null;
+ try
+ {
+ Header respHeader = new Header(request.toWire());
+ Message response = new Message();
+ response.setHeader(respHeader);
+
+ for (int i = 0; i < 4; i++)
+ response.removeAllRecords(i);
+
+ response.addRecord(request.getQuestion(), Section.QUESTION);
+
+ response.getHeader().setFlag(Flags.QR);
+ if (request.getHeader().getFlag(Flags.RD))
+ response.getHeader().setFlag(Flags.RD);
+ respHeader.setRcode(Integer.parseInt(error.getError().toString()));
+
+ return response;
+ }
+ catch (IOException e) {}
+
+ return errorResponse;
+ }
+
+ /**
+ * Converts a raw DNS wire protocol format message to a Message structure.
+ * @param buffer The raw DNS wire protocol format.
+ * @return A Message object converted from the buffer.
+ * @throws DNSException
+ */
+ protected Message toMessage(byte[] buffer) throws DNSException
+ {
+ if (buffer.length <= 0 || buffer.length > settings.getMaxRequestSize())
+ throw new DNSException(DNSError.newError(Rcode.REFUSED), "Invalid request size " + buffer.length);
+
+ try
+ {
+ return new Message(buffer);
+ }
+ catch (IOException e)
+ {
+ throw new DNSException(DNSError.newError(Rcode.FORMERR), "Failed to deserialize raw byte message.");
+ }
+ }
+
+ /**
+ * Converts a Message object to a raw DNS wire format byte array.
+ * @param msg The message to convert.
+ * @return A byte array representing the raw DNS wire format of the message.
+ */
+ protected byte[] toBytes(Message msg)
+ {
+ return msg.toWire();
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSResponderTCP.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSResponderTCP.java
new file mode 100644
index 000000000..2c0d043d2
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSResponderTCP.java
@@ -0,0 +1,58 @@
+/*
+ Copyright (c) 2010, Direct Project
+ All rights reserved.
+
+ Authors:
+ Umesh Madan umeshma@microsoft.com
+ Chris Lomonico chris.lomonico@surescripts.com
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of The Direct Project (directproject.org) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns;
+
+/**
+ * TCP/IP implementation of the {@link DNSReponser) interface. The responder binds to the addresses and port
+ * provided in the {@link DNSServerSettings} object.
+ * @author Greg Meyer
+ * @since 1.0
+ */
+public class DNSResponderTCP extends DNSResponder
+{
+ private final DNSSocketServer socketServer;
+
+ /**
+ * {@inheritDoc}
+ */
+ public DNSResponderTCP(DNSServerSettings settings, DNSStore store) throws DNSException
+ {
+ super(settings, store);
+ socketServer = new TCPServer(settings, this);
+ }
+
+
+ /**
+ * {@inheritDoc}}
+ */
+ @Override
+ public void start() throws DNSException
+ {
+ socketServer.start();
+ }
+
+
+ /**
+ * {@inheritDoc}}
+ */
+ @Override
+ public void stop() throws DNSException
+ {
+ socketServer.stop();
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSResponderUDP.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSResponderUDP.java
new file mode 100644
index 000000000..f6bebdda0
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSResponderUDP.java
@@ -0,0 +1,58 @@
+/*
+ Copyright (c) 2010, Direct Project
+ All rights reserved.
+
+ Authors:
+ Umesh Madan umeshma@microsoft.com
+ Chris Lomonico chris.lomonico@surescripts.com
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of The Direct Project (directproject.org) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns;
+
+/**
+ * UDP/IP implementation of the {@link DNSReponser) interface. The responder binds to the addresses and port
+ * provided in the {@link DNSServerSettings} object.
+ * @author Greg Meyer
+ * @since 1.0
+ */
+public class DNSResponderUDP extends DNSResponder
+{
+ private final DNSSocketServer socketServer;
+
+ /**
+ * {@inheritDoc}}
+ */
+ public DNSResponderUDP(DNSServerSettings settings, DNSStore store) throws DNSException
+ {
+ super(settings, store);
+ socketServer = new UDPServer(settings, this);
+ }
+
+
+ /**
+ * {@inheritDoc}}
+ */
+ @Override
+ public void start() throws DNSException
+ {
+ socketServer.start();
+ }
+
+
+ /**
+ * {@inheritDoc}}
+ */
+ @Override
+ public void stop() throws DNSException
+ {
+ socketServer.stop();
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSServer.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSServer.java
new file mode 100644
index 000000000..113e97d5a
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSServer.java
@@ -0,0 +1,213 @@
+/*
+Copyright (c) 2010, NHIN Direct Project
+All rights reserved.
+
+Authors:
+ Umesh Madan umeshma@microsoft.com
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the distribution. Neither the name of the The NHIN Direct Project (nhindirect.org).
+nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+package org.nhindirect.dns;
+
+import java.lang.management.ManagementFactory;
+import java.util.UUID;
+
+import javax.management.JMException;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.StandardMBean;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.google.inject.Inject;
+
+/**
+ * The DNS server creates the UDP and TCP responders and manages their life cycles. DNS queries are delegated
+ * the responders which use the {@link DNSStore} to lookup entries.
+ *
+ * To run a server, an instance of a server is created followed by calling the {@link #start()} method.
+ * @author Greg Meyer
+ * @since 1.0
+ */
+public class DNSServer implements DNSServerMBean
+{
+ private static final Log LOGGER = LogFactory.getFactory().getInstance(DNSServer.class);
+
+ private DNSResponder tcpResponder;
+ private DNSResponder updResponder;
+ private CompositeData settingsData;
+ private final String dnsStoreImplName;
+
+ /**
+ * Create a new DNSServer
+ * @param store The storage medium of the DNS records.
+ * @param settings DNS server specific settings such as UDP/TCP ports, IP bindings, and thread tuning parameters.
+ */
+ @Inject
+ public DNSServer(DNSStore store, DNSServerSettings settings)
+ {
+ try
+ {
+ tcpResponder = new DNSResponderTCP(settings, store);
+ }
+ catch (DNSException e)
+ {
+ LOGGER.error("Failed to create TCP responder: " + e.getLocalizedMessage(), e);
+ }
+
+ try
+ {
+ updResponder = new DNSResponderUDP(settings, store);
+ }
+ catch (DNSException e)
+ {
+ LOGGER.error("Failed to create UDP responder: " + e.getLocalizedMessage(), e);
+ }
+
+ dnsStoreImplName = store.getClass().getName();
+
+ registerMBean(settings);
+ }
+
+ /**
+ * Register the MBean
+ */
+ private void registerMBean(DNSServerSettings settings)
+ {
+ String[] itemNames = {"Port", "Bind Address", "Max Request Size", "Max Outstanding Accepts", "Max Active Accepts", "Max Connection Backlog",
+ "Read Buffer Size", "Send Timeout", "Receive Timeout", "Socket Close Timeout"};
+
+ String[] itemDesc = {"Port", "Bind Address", "Max Request Size", "Max Outstanding Accepts", "Max Active Accepts", "Max Connection Backlog",
+ "Read Buffer Size", "Send Timeout", "Receive Timeout", "Socket Close Timeout"};
+
+ OpenType>[] types = {SimpleType.INTEGER, SimpleType.STRING, SimpleType.INTEGER, SimpleType.INTEGER, SimpleType.INTEGER, SimpleType.INTEGER, SimpleType.INTEGER,
+ SimpleType.INTEGER, SimpleType.INTEGER, SimpleType.INTEGER};
+
+ Object[] settingsValues = {settings.getPort(), settings.getBindAddress(), settings.getMaxRequestSize(), settings.getMaxOutstandingAccepts(),
+ settings.getMaxActiveRequests(), settings.getMaxConnectionBacklog(), settings.getReadBufferSize(), settings.getSendTimeout(),
+ settings.getReceiveTimeout(), settings.getSocketCloseTimeout()};
+
+ try
+ {
+ CompositeType settingsType = new CompositeType(DNSServerSettings.class.getSimpleName(), "DNS server settings.", itemNames, itemDesc, types);
+ settingsData = new CompositeDataSupport(settingsType, itemNames, settingsValues);
+ }
+ catch (OpenDataException e)
+ {
+ LOGGER.error("Failed to create settings composite type: " + e.getLocalizedMessage(), e);
+ return;
+ }
+
+
+ Class> clazz = this.getClass();
+ final StringBuilder objectNameBuilder = new StringBuilder(clazz.getPackage().getName());
+ objectNameBuilder.append(":type=").append(clazz.getSimpleName());
+ objectNameBuilder.append(",name=").append(UUID.randomUUID());
+
+ try
+ {
+ final StandardMBean mbean = new StandardMBean(this, DNSServerMBean.class);
+
+ final MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
+ mbeanServer.registerMBean(mbean, new ObjectName(objectNameBuilder.toString()));
+ }
+ catch (JMException e)
+ {
+ LOGGER.error("Unable to register the DNSServer MBean", e);
+ }
+ }
+
+ /**
+ * Starts the DNS server by initializing and launching the TCP and UDP listeners.
+ * @throws DNSException Thrown if the internal listeners could not be started.
+ */
+ public void start() throws DNSException
+ {
+ tcpResponder.start();
+ updResponder.start();
+ }
+
+ /**
+ * Stops the server and shuts down the TCP and UPD listeners.
+ * @throws DNSException Thrown if the internal listeners could not be stopped.
+ */
+ public void stop() throws DNSException
+ {
+ tcpResponder.stop();
+ updResponder.stop();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public CompositeData getServerSettings()
+ {
+
+ return settingsData;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void startServer()
+ {
+ LOGGER.info("Received request to start server.");
+ try
+ {
+ start();
+ }
+ catch (DNSException e)
+ {
+ LOGGER.error("Failed to start server: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void stopServer()
+ {
+ LOGGER.info("Received request to stop server.");
+ try
+ {
+ stop();
+ }
+ catch (DNSException e)
+ {
+ LOGGER.error("Failed to stop server: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getDNSStoreImplName()
+ {
+ return dnsStoreImplName;
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSServerFactory.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSServerFactory.java
new file mode 100644
index 000000000..9feaec4aa
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSServerFactory.java
@@ -0,0 +1,98 @@
+/*
+Copyright (c) 2010, NHIN Direct Project
+All rights reserved.
+
+Authors:
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the distribution. Neither the name of the The NHIN Direct Project (nhindirect.org).
+nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns;
+
+import java.net.URL;
+
+import org.nhindirect.dns.config.DNSServerConfig;
+import org.nhindirect.dns.module.DNSServerConfigModule;
+
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Provider;
+
+
+/**
+ * The DNSServerFactory is a bootstrapper for creating instances of the {@link DNSServer) based on configuration information. Configurations
+ * are loaded from a URL that may take the form of any addressable resource such as a file, HTTP resource, LDAP store, or database. Based on the
+ * URL protocol, an appropriate configuration loader and parser is instantiated which creates an injector used to provide instance of the DNSServer.
+ * Optionally specific configuration and {@link DNSStore} providers can be passed for specific object creation. This is generally useful
+ * for creating mock implementations for testing.
+ * @author Greg Meyer
+ *
+ * @since 1.0
+ */
+public class DNSServerFactory
+{
+ /**
+ * Creates an instance of a {@link DNSServer} using the configuration information stored at the configuration location.
+ * @param configLocation The URL of the configuration information. The URL may refer to any addressable resource.
+ * @return An initialized instance of a DNSServer.
+ * @throws DNSException Thrown if an error occurs while creating the DNSServer.
+ */
+ public synchronized static DNSServer createDNSServer(URL configLocation) throws DNSException
+ {
+ return createDNSServer(configLocation, null, null);
+ }
+
+ /**
+ * Creates an instance of a {@link DNSServer} using the configuration information stored at the configuration location. An
+ * optional {@link DNSStore> provider can be passed for initializing the server with a specific record store.
+ * @param configLocation The URL of the configuration information. The URL may refer to any addressable resource.
+ * @param dnsStore Optional provider that will create an instance of a specific {@link DNSStore} type. If this is null, the
+ * system will create a default store.
+ * @param settings Optional DNS server settings. Overridden by settings from the configuration service.
+ * @return An initialized instance of a DNSServer.
+ * @throws DNSException Thrown if an error occurs while creating the DNSServer.
+ */
+ public synchronized static DNSServer createDNSServer(URL configLocation, Provider dnsStore, Provider settings) throws DNSException
+ {
+ DNSServer retVal = null;
+
+ try
+ {
+ Injector agentInjector = buildServerInjector(configLocation, dnsStore, settings);
+ retVal = agentInjector.getInstance(DNSServer.class);
+
+ }
+ catch (Exception t)
+ {
+ // catch all
+ throw new DNSException(DNSError.newError(-1), "DNSServer creation failed: " + t.getMessage(), t);
+ }
+
+ return retVal;
+ }
+
+ /*
+ * Creates an injector for getting SmtpAgent instances
+ */
+ private static Injector buildServerInjector(URL configLocation, Provider storeProvider, Provider settings)
+ {
+ Injector configInjector = Guice.createInjector(DNSServerConfigModule.create(configLocation, storeProvider, settings));
+
+ DNSServerConfig config = configInjector.getInstance(DNSServerConfig.class);
+
+ return config.getServerInjector();
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSServerMBean.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSServerMBean.java
new file mode 100644
index 000000000..2928e6823
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSServerMBean.java
@@ -0,0 +1,50 @@
+/*
+ Copyright (c) 2010, Direct Project
+ All rights reserved.
+
+ Authors:
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of The Direct Project (directproject.org) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns;
+
+import javax.management.openmbean.CompositeData;
+
+/**
+ * MBean interface definition for monitoring and managing the DNS server.
+ * @author Greg Meyer
+ *
+ * @since 1.1.0
+ */
+public interface DNSServerMBean
+{
+
+ /**
+ * Gets the DNS server settings.
+ * @return The DNS server settings.
+ */
+ public CompositeData getServerSettings();
+
+ /**
+ * Initializes the DNS server socket listeners are begins accepting requests.
+ */
+ public void startServer();
+
+ /**
+ * Shutdown the DNS server socket listeners and stops accepting requests.
+ */
+ public void stopServer();
+
+ /**
+ * Gets the fully qualified class name of the DNS store.
+ * @return The fully qualified class name of the DNS store.
+ */
+ public String getDNSStoreImplName();
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSServerSettings.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSServerSettings.java
new file mode 100644
index 000000000..6258df0f8
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSServerSettings.java
@@ -0,0 +1,106 @@
+/*
+ Copyright (c) 2010, Direct Project
+ All rights reserved.
+
+ Authors:
+ Umesh Madan umeshma@microsoft.com
+ Chris Lomonico chris.lomonico@surescripts.com
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of The Direct Project (directproject.org) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns;
+
+
+/**
+ * Tuning parameters for the DNS server.
+ *
+ * @author Greg Meyer
+ * @author Umesh Madan
+ * @author Chris Lomonico
+ *
+ * @since 1.0
+ */
+public class DNSServerSettings extends SocketServerSettings
+{
+ private static final int DEFAULT_PORT = 53;
+ private static final String DEFAULT_BIND_ADDRESS = "0.0.0.0"; // bind to all adapters
+ public static final int DAFAULT_MAX_REQUEST_SIZE = 1024 * 16;
+
+ private int port;
+ private String bindAddress;
+ private int maxRequestSize;
+
+ /**
+ * Create default DNS server settings
+ */
+ public DNSServerSettings()
+ {
+ super();
+ port = DEFAULT_PORT;
+ bindAddress = DEFAULT_BIND_ADDRESS;
+ maxRequestSize = DAFAULT_MAX_REQUEST_SIZE;
+ }
+
+ /**
+ * Gets the IP port that the server will be listening on. The default is 53.
+ * @return The IP port that the server will be listening on.
+ */
+ public int getPort()
+ {
+ return port;
+ }
+
+ /**
+ * Sets the IP port that the server will be listening on.
+ * @param port The IP port that the server will be listening on.
+ *
+ */
+ public void setPort(int port)
+ {
+ this.port = port;
+ }
+
+ /**
+ * Gets the IP4 addresses that the server will be bound to. The string is comma delimited list of IP addresses. The default is 0.0.0.0
+ * which means that the server will bind to add IP addresses available on the local machine.
+ * @return The IP4 addresses that the server will be bound to.
+ */
+ public String getBindAddress()
+ {
+ return bindAddress;
+ }
+
+ /**
+ * Sets the IP4 addresses that the server will be bound to.
+ * @param bindAddress The IP4 addresses that the server will be bound to.
+ */
+ public void setBindAddress(String bindAddress)
+ {
+ this.bindAddress = bindAddress;
+ }
+
+ /**
+ * Gets the maximum size in bytes of a request. The default size is 16K.
+ * @return The maximum size in bytes of a request.
+ */
+ public int getMaxRequestSize()
+ {
+ return maxRequestSize;
+ }
+
+ /**
+ * Sets the maximum size in bytes of a request.
+ * @param maxRequestSize The maximum size in bytes of a request.
+ */
+ public void setMaxRequestSize(int maxRequestSize)
+ {
+ this.maxRequestSize = maxRequestSize;
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSSocketServer.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSSocketServer.java
new file mode 100644
index 000000000..e66c19bc6
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSSocketServer.java
@@ -0,0 +1,321 @@
+/*
+ Copyright (c) 2010, Direct Project
+ All rights reserved.
+
+ Authors:
+ Umesh Madan umeshma@microsoft.com
+ Chris Lomonico chris.lomonico@surescripts.com
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of The Direct Project (directproject.org) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns;
+
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.net.Socket;
+import java.util.UUID;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.management.JMException;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.StandardMBean;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * The socket server is an IP protocol agnostic server that manages threading/concurrency and message dispatching to the
+ * concrete socket implementation. It utilizes a "smart" thread pool for efficiently managing processing threads.
+ * @author Greg Meyer
+ * @since 1.0
+ */
+public abstract class DNSSocketServer implements DNSSocketServerMBean
+{
+ private static final Log LOGGER = LogFactory.getFactory().getInstance(DNSSocketServer.class);
+
+ protected final DNSServerSettings settings;
+ protected final DNSResponder responder;
+
+ protected ExecutorService socketAcceptService;
+ protected ThreadPoolExecutor dnsRequestService;
+
+ protected final AtomicBoolean running;
+
+ private long serverStartTime = Long.MAX_VALUE;
+ private volatile long rejectedCount = 0;
+ private volatile long requestCount = 0;
+ private TemporalCountBucket countBuckets[] = {new TemporalCountBucket(), new TemporalCountBucket(),
+ new TemporalCountBucket(), new TemporalCountBucket(), new TemporalCountBucket()};
+
+
+ /**
+ * Creates a socket server. The server will not start accepting messages until the {@link #start()} method is called.
+ * @param settings The server settings. The settings contain specific IP and socket configuration parameters.
+ * @param responsder The DNS responder that will handle lookups.
+ * @throws DNSException
+ */
+ public DNSSocketServer(DNSServerSettings settings, DNSResponder responsder) throws DNSException
+ {
+ running = new AtomicBoolean(false);
+
+ this.settings = settings;
+ this.responder = responsder;
+
+ // create the server socket
+ createServerSocket();
+ }
+
+ protected void registerMBean(Class> clazz)
+ {
+ final StringBuilder objectNameBuilder = new StringBuilder(clazz.getPackage().getName());
+ objectNameBuilder.append(":type=").append(clazz.getSimpleName());
+ objectNameBuilder.append(",name=").append(UUID.randomUUID());
+
+ try
+ {
+ final StandardMBean mbean = new StandardMBean(this, DNSSocketServerMBean.class);
+
+ final MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
+ mbeanServer.registerMBean(mbean, new ObjectName(objectNameBuilder.toString()));
+ }
+ catch (JMException e)
+ {
+ LOGGER.error("Unable to register the DNSSocketServer MBean", e);
+ }
+ }
+
+ /**
+ * Starts the socket server and initializes the dispatch threads. After this method has been called, the server will start accepting
+ * DNS requests.
+ * @throws DNSException
+ */
+ public void start() throws DNSException
+ {
+ if (running.get() != true)
+ {
+ // create the accept thread
+ running.set(true);
+
+ dnsRequestService = new ThreadPoolExecutor(0, settings.getMaxActiveRequests(),
+ 120L, TimeUnit.SECONDS, new SynchronousQueue());
+
+
+ socketAcceptService = Executors.newSingleThreadExecutor();
+ socketAcceptService.execute(getSocketAcceptTask());
+
+ serverStartTime = System.currentTimeMillis();
+ }
+ else
+ LOGGER.info("Start requested, but socket server is already running.");
+
+ }
+
+ /**
+ * Shuts down the socket server and terminates the server from accepting additional requests. The server attempts to gracefully
+ * shutdown the processing threads and gives currently running processing threads a chance to finish.
+ * @throws DNSException
+ */
+ public void stop() throws DNSException
+ {
+ running.set(false);
+
+ socketAcceptService.shutdown();
+
+ dnsRequestService.shutdown();
+
+ }
+
+ protected void waitForGracefulStop()
+ {
+ try
+ {
+ socketAcceptService.awaitTermination(10, TimeUnit.SECONDS);
+ }
+ catch (InterruptedException e) {/* no op */}
+
+ try
+ {
+ dnsRequestService.awaitTermination(10, TimeUnit.SECONDS);
+ }
+ catch (InterruptedException e) {/* no op */}
+ }
+
+ /**
+ * Creates and initializes the socket implementation that will accept incoming requests.
+ * @throws DNSException
+ */
+ public abstract void createServerSocket() throws DNSException;
+
+ /**
+ * Gets the Runnable task that will be responsible for accepting connections. This task
+ * is placed in a single thread, so it should loop until the running flag is set to false.
+ * @return The Runnable task that will be responsible for accepting connections
+ */
+ public abstract Runnable getSocketAcceptTask();
+
+ /**
+ * Gets the Runnable task that will process a DNS request. Each accepted request will create a new instance
+ * of the Runnable task and run it in its own thread.
+ * @param s An arbitrary parameter passed to the Runnable task. This parameter generally contain the DNS request information.
+ * This may be the TCP socket from the accept() server socket call or a UDP datagram packet.
+ * @return The Runnable task that will process a DNS request
+ */
+ public abstract Runnable getDNSRequestTask(Object s);
+
+ /**
+ * Submits the DNS request to a runnable task.
+ * @param s An arbitrary parameter passed to the Runnable task. This parameter generally contain the DNS request information.
+ * This may be the TCP socket from the accept() server socket call or a UDP datagram packet.
+ */
+ protected void submitDNSRequest(Object s)
+ {
+
+ updateCountMetrics();
+ boolean executed = false;
+ if (dnsRequestService.getActiveCount() < settings.getMaxActiveRequests())
+ {
+ try
+ {
+ dnsRequestService.execute(getDNSRequestTask(s));
+ executed = true;
+ }
+ catch (RejectedExecutionException e)
+ {
+ LOGGER.warn("Rejecting DNS request: " + e.getMessage());
+ /* no-op, but use logic below to indicate that it was rejected */
+ }
+ }
+
+ if (!executed)
+ {
+ ++rejectedCount;
+ // just close the socket... we're too busy to handle anything
+ try
+ {
+ if (s instanceof Socket)
+ ((Socket)s).close();
+ }
+ catch (IOException e) {}
+ }
+ }
+
+ private void updateCountMetrics()
+ {
+ ++requestCount;
+ long curTime = System.currentTimeMillis();
+ int bucketIndex = (int)((curTime / 1000) % 5);
+
+ synchronized (countBuckets)
+ {
+ TemporalCountBucket bucket = countBuckets[bucketIndex];
+ bucket.increment(curTime);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Long getUptime()
+ {
+ if (running.get() == false || serverStartTime == Long.MAX_VALUE)
+ return -1L;
+ else
+ return (System.currentTimeMillis() - serverStartTime);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Long getRejectedRequestCount()
+ {
+ return rejectedCount;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Long getResourceRequestCount()
+ {
+ return requestCount;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getResourceRequestLoad()
+ {
+ // this is an approximation over the last 5 seconds converted to requests per second
+ long curTime = System.currentTimeMillis();
+ int numTransactions = 0;
+ synchronized (countBuckets)
+ {
+ for (TemporalCountBucket bucket : countBuckets)
+ {
+ numTransactions += bucket.getCount(curTime);
+ }
+ }
+
+ // can get a little better accuracy if we take into consideration that the last full second of the 5 second time range has not yet passed
+ double div = 4.0 + ((curTime % 1000) / 1000);
+
+ int aveTransLoad = (int)((double)(numTransactions) / (div));
+
+ return aveTransLoad + "/sec";
+ }
+
+ /*
+ * class used to hold request count within a 1 second time range
+ */
+ private static class TemporalCountBucket
+ {
+ private volatile long count = 0;
+ private volatile long firstAddedTime = 0;
+
+ /*
+ * increment the access count... if the access time is outside
+ * of the 5 second time range, then reset the counter
+ */
+ public synchronized void increment(long accessTime)
+ {
+ if ((accessTime - firstAddedTime) > 5000)
+ {
+ // round down to nearest 1000
+ firstAddedTime = accessTime - (accessTime % 1000);
+ count = 1;
+ }
+ else
+ ++count;
+ }
+
+ /*
+ * get the access count... if the access time is outside of the
+ * 5 second range, then return 0
+ */
+ public synchronized long getCount(long accessTime)
+ {
+ if (firstAddedTime == 0)
+ return 0;
+
+ return ((accessTime - firstAddedTime) > 5000) ? 0 : count;
+ }
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSSocketServerMBean.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSSocketServerMBean.java
new file mode 100644
index 000000000..0638c655d
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSSocketServerMBean.java
@@ -0,0 +1,68 @@
+/*
+ Copyright (c) 2010, Direct Project
+ All rights reserved.
+
+ Authors:
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of The Direct Project (directproject.org) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns;
+
+/**
+ * MBean interface definition for monitoring and managing a DNS socket server.
+ * @author Greg Meyer
+ *
+ * @since 1.1.0
+ */
+public interface DNSSocketServerMBean
+{
+ /**
+ * Gets the number of DNS requests received by the server.
+ * @return The number of DNS requests received by the server.
+ */
+ public Long getResourceRequestCount();
+
+ /**
+ * Gets the request load of the server. Load is returned in number of requests per second averaged over the last 5 seconds.
+ * @return The request load of the server.
+ */
+ public String getResourceRequestLoad();
+
+ /**
+ * Gets the time in milliseconds that the server has been running since its last start.
+ * @return The time in milliseconds that the server has been running since its last start.
+ */
+ public Long getUptime();
+
+ /**
+ * Gets the number of requests that returned without error. NXDOMAIN statuses qualify as successful requests.
+ * @return The number of requests that returned without error.
+ */
+ public Long getSuccessfulRequestCount();
+
+ /**
+ * Gets the number of requests that resulted in an error.
+ * @return The number of requests that resulted in an error.
+ */
+ public Long getErrorRequestCount();
+
+ /**
+ * Gets the number of requests that returned no records without error.
+ * @return The number of request that returned no records without error.
+ */
+ public Long getMissedRequestCount();
+
+ /**
+ * Gets the number of requests that were rejected by the server due to being to busy. A high number of rejected requests indicates that the server
+ * should be reconfigured to accept a higher load.
+ * @return The number of requests that were rejected by the server due to being to busy.
+ */
+ public Long getRejectedRequestCount();
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSStore.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSStore.java
new file mode 100644
index 000000000..360a2f411
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/DNSStore.java
@@ -0,0 +1,43 @@
+/*
+ Copyright (c) 2010, Direct Project
+ All rights reserved.
+
+ Authors:
+ Umesh Madan umeshma@microsoft.com
+ Chris Lomonico chris.lomonico@surescripts.com
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of The Direct Project (directproject.org) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns;
+
+import org.xbill.DNS.Message;
+
+import com.google.inject.ImplementedBy;
+
+/**
+ * A DNSStore encapsulates the physical medium that stores DNS records such as a files, databases, or web services. A store processes DNS requests,
+ * executes the lookup or operational request (such as zone transfers) logic, and returns an appropriate response.
+ * @author Greg Meyer
+ * @author Umesh Madan
+ *
+ * @since 1.0
+ */
+@ImplementedBy(ConfigServiceDNSStore.class)
+public interface DNSStore
+{
+ /**
+ * Processes a lookup request for DNS records.
+ * @param dnsMsg The DSN request message.
+ * @return The DNS response message. Returns null for lookup requests if a matching record cannot be found.
+ * @throws DNSException Thrown is the request fails due to sever failure such as illegal request parameters or
+ * a failure accessing the physical record medium.
+ */
+ public Message get(Message dnsMsg) throws DNSException;
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/ProxyDNSStore.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/ProxyDNSStore.java
new file mode 100644
index 000000000..480689ac5
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/ProxyDNSStore.java
@@ -0,0 +1,154 @@
+/*
+ Copyright (c) 2010, Direct Project
+ All rights reserved.
+
+ Authors:
+ Umesh Madan umeshma@microsoft.com
+ Chris Lomonico chris.lomonico@surescripts.com
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of The Direct Project (directproject.org) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns;
+
+import java.io.IOException;
+import java.net.UnknownHostException;
+import java.util.Collection;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.xbill.DNS.ExtendedResolver;
+import org.xbill.DNS.Message;
+import org.xbill.DNS.ResolverConfig;
+
+/**
+ * Proxy DNS store that delegates all requests to another set of DNS servers.
+ * The store defaults to using port 53 and the machine's configured DNS servers.
+ * @author Greg Meyer
+ *
+ * @since 1.0
+ */
+public class ProxyDNSStore implements DNSStore
+{
+ private static final int DEFAULT_RESOLVER_PORT = 53;
+
+ private final String[] servers;
+ private final int port;
+
+ protected static final Log LOGGER = LogFactory.getFactory().getInstance(ProxyDNSStore.class);
+
+ /**
+ * Creates a default proxy store.
+ */
+ public ProxyDNSStore()
+ {
+ this(DEFAULT_RESOLVER_PORT);
+ }
+
+ /**
+ * Creates a proxy store delegating requests to the provided port.
+ * @param port The IP port to use when calling the proxy DNS server.
+ */
+ public ProxyDNSStore(int port)
+ {
+ this(null, DEFAULT_RESOLVER_PORT);
+ }
+
+ /**
+ * Creates a proxy using the the provided servers for delegating requests.
+ * @param servers A collections of IP4 addresses (as strings) that the proxy will delegate request to.
+ */
+ public ProxyDNSStore(Collection servers)
+ {
+ this(servers, DEFAULT_RESOLVER_PORT);
+ }
+
+ /**
+ * Creates a proxy using the provided servers and port for delegating requests.
+ * @param servers A collections of IP4 addresses (as strings) that the proxy will delegate request to.
+ * @param port The IP port to use when calling the proxy DNS server.
+ */
+ public ProxyDNSStore(Collection servers, int port)
+ {
+ if (servers == null || servers.size() == 0)
+ {
+
+ String[] configedServers = ResolverConfig.getCurrentConfig().servers();
+
+ if (configedServers != null)
+ {
+ this.servers = configedServers;
+ }
+ else
+ this.servers = null;
+ }
+ else
+ {
+ this.servers = new String[servers.size()];
+ servers.toArray(this.servers);
+ }
+
+ this.port = port;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Message get(Message dnsMsg) throws DNSException
+ {
+ ExtendedResolver resolver = createExResolver(servers, port, 2, 2000);
+ // try UPD first
+
+ Message response = null;
+ try
+ {
+ response = resolver.send(dnsMsg);
+ }
+ catch (IOException e)
+ {
+ /* no-op */
+ }
+
+ if (response == null)
+ {
+ // try TCP
+ resolver.setTCP(true);
+ try
+ {
+ response = resolver.send(dnsMsg);
+ }
+ catch (IOException e)
+ {
+ /* no-op */
+ }
+ }
+
+ return response;
+ }
+
+ /*
+ * Create the resolver that will do the DNS requests.
+ */
+ private ExtendedResolver createExResolver(String[] servers, int port, int retries, int timeout)
+ {
+ ExtendedResolver retVal = null;
+ try
+ {
+ retVal = new ExtendedResolver(servers);
+ retVal.setRetries(retries);
+ retVal.setTimeout(timeout);
+ }
+ catch (UnknownHostException e)
+ {
+ LOGGER.warn("Proxy store resolver could not be created: " + e.getMessage(), e);
+ }
+ return retVal;
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/RESTServiceDNSStore.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/RESTServiceDNSStore.java
new file mode 100644
index 000000000..8badb99dd
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/RESTServiceDNSStore.java
@@ -0,0 +1,384 @@
+package org.nhindirect.dns;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAKey;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.http.client.HttpClient;
+import org.nhind.config.rest.CertificateService;
+import org.nhind.config.rest.DNSService;
+import org.nhind.config.rest.impl.DefaultCertPolicyService;
+import org.nhind.config.rest.impl.DefaultCertificateService;
+import org.nhind.config.rest.impl.DefaultDNSService;
+import org.nhind.config.rest.CertPolicyService;
+import org.nhindirect.common.rest.ServiceSecurityManager;
+import org.nhindirect.config.model.DNSRecord;
+import org.nhindirect.config.model.Certificate;
+import org.nhindirect.config.model.exceptions.CertificateConversionException;
+import org.nhindirect.config.model.utils.CertUtils;
+import org.nhindirect.dns.annotation.ConfigServiceURL;
+import org.nhindirect.policy.PolicyFilterFactory;
+import org.nhindirect.policy.PolicyLexiconParser;
+import org.nhindirect.policy.PolicyLexiconParserFactory;
+import org.nhindirect.policy.x509.SignatureAlgorithmIdentifier;
+import org.xbill.DNS.CERTRecord;
+import org.xbill.DNS.DClass;
+import org.xbill.DNS.Name;
+import org.xbill.DNS.RRset;
+import org.xbill.DNS.Rcode;
+import org.xbill.DNS.Record;
+import org.xbill.DNS.Type;
+
+import com.google.inject.Inject;
+
+public class RESTServiceDNSStore extends AbstractDNSStore
+{
+ protected final CertificateService certService;
+ protected final CertPolicyService certPolicyService;
+ protected final DNSService dnsService;
+
+ @Inject
+ public RESTServiceDNSStore(@ConfigServiceURL String serviceURL, HttpClient httpClient, ServiceSecurityManager securityManager)
+ {
+ certService = new DefaultCertificateService(serviceURL, httpClient, securityManager);
+ certPolicyService = new DefaultCertPolicyService(serviceURL, httpClient, securityManager);
+ dnsService = new DefaultDNSService(serviceURL, httpClient, securityManager);
+
+ try
+ {
+ configCertPolicy();
+ }
+ catch (DNSException e)
+ {
+ throw new IllegalStateException(e);
+ }
+
+ }
+
+ /**
+ * Checks to see if a certificate policy has been configured.
+ */
+ protected void configCertPolicy() throws DNSException
+ {
+ // check to see if there is a certificate policy set
+ final String polName = System.getProperty(DNS_CERT_POLICY_NAME_VAR);
+ if (!StringUtils.isEmpty(polName))
+ {
+ InputStream inStream = null;
+ LOGGER.info("Certificate policy name " + polName + " has been configured.");
+ try
+ {
+ // get the policy by name
+ final org.nhindirect.config.model.CertPolicy policy = certPolicyService.getPolicyByName(polName);
+ if (policy == null)
+ {
+ LOGGER.warn("Certificate policy " + polName + " could not be found in the system. Falling back to no policy.");
+ return;
+ }
+
+ // now compile the policy into an expression
+ final PolicyLexiconParser parser = PolicyLexiconParserFactory.getInstance(policy.getLexicon());
+ inStream = new ByteArrayInputStream(policy.getPolicyData());
+ this.polExpression = parser.parse(inStream);
+
+ // now create the filter
+ this.polFilter = PolicyFilterFactory.getInstance();
+
+ }
+ catch (Exception e)
+ {
+ // it's OK if can't find the certificate policy that was configured, we'll just log a warning
+ // it's also OK if we can't download or parse the policy, but we need to log the error
+ LOGGER.warn("Error loading and compling certificate policy " + polName + ". Will fallback to no policy filter.", e);
+ }
+ finally
+ {
+ IOUtils.closeQuietly(inStream);
+ }
+ }
+ else
+ LOGGER.info("No certificate policy has been configured.");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected RRset processGenericRecordRequest(String name, int type) throws DNSException
+ {
+ Collection records;
+
+ try
+ {
+ records = dnsService.getDNSRecord(type, name);
+ }
+ catch (Exception e)
+ {
+ throw new DNSException(DNSError.newError(Rcode.SERVFAIL), "DNS service proxy call for DNS records failed: " + e.getMessage(), e);
+ }
+
+ if (records == null || records.size() == 0)
+ return null;
+
+ RRset retVal = new RRset();
+ try
+ {
+ for (DNSRecord record : records)
+ {
+ Record rec = Record.newRecord(Name.fromString(record.getName()), record.getType(),
+ record.getDclass(), record.getTtl(), record.getData());
+
+ retVal.addRR(rec);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new DNSException(DNSError.newError(Rcode.SERVFAIL), "Failure while parsing generic record data: " + e.getMessage(), e);
+ }
+
+ return retVal;
+ }
+
+ @Override
+ protected Collection processGenericANYRecordRequest(String name) throws DNSException
+ {
+ Collection records;
+
+ try
+ {
+ records = dnsService.getDNSRecord(Type.ANY, name);
+ }
+ catch (Exception e)
+ {
+ throw new DNSException(DNSError.newError(Rcode.SERVFAIL), "DNS service proxy call for DNS records failed: " + e.getMessage(), e);
+ }
+
+ if (records == null || records.size() == 0)
+ return null;
+
+ Collection retVal = new ArrayList();
+ try
+ {
+ for (DNSRecord record : records)
+ {
+ Record rec = Record.newRecord(Name.fromString(record.getName()), record.getType(),
+ record.getDclass(), record.getTtl(), record.getData());
+
+ retVal.add(rec);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new DNSException(DNSError.newError(Rcode.SERVFAIL), "Failure while parsing generic record data: " + e.getMessage(), e);
+ }
+
+ return retVal;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("unused")
+ @Override
+ protected RRset processCERTRecordRequest(String name) throws DNSException
+ {
+ if (name.endsWith("."))
+ name = name.substring(0, name.length() - 1);
+
+ Collection certs;
+
+ // use the certificate configuration service
+ try
+ {
+ certs = certService.getCertificatesByOwner(name);
+ }
+ catch (Exception e)
+ {
+ throw new DNSException(DNSError.newError(Rcode.SERVFAIL), "DNS service proxy call for certificates failed: " + e.getMessage(), e);
+ }
+
+ if (certs == null || certs.size() == 0)
+ {
+ // unless the call above was for an org level cert, it will probably always fail because the
+ // "name" parameter has had all instances of "@" replaced with ".". The certificate service
+ // stores owners using "@".
+ // This is horrible, but try hitting the cert service replacing each "." with "@" one by one.
+ // Start at the beginning of the address because this is more than likely where the "@" character
+ // will be.
+ int previousIndex = 0;
+ int replaceIndex = 0;
+ while ((replaceIndex = name.indexOf(".", previousIndex)) > -1)
+ {
+ char[] chars = name.toCharArray();
+ chars[replaceIndex] = '@';
+ try
+ {
+ certs = certService.getCertificatesByOwner(String.copyValueOf(chars));
+ }
+ catch (Exception e)
+ {
+ throw new DNSException(DNSError.newError(Rcode.SERVFAIL), "DNS service proxy call for certificates failed: " + e.getMessage(), e);
+ }
+ if (certs != null && certs.size() > 0)
+ break;
+
+ if (replaceIndex >= (name.length() - 1))
+ break;
+
+ previousIndex = replaceIndex + 1;
+ }
+ }
+
+ if (certs == null || certs.size() == 0)
+ return null;
+
+ if (!name.endsWith("."))
+ name += ".";
+
+ RRset retVal = new RRset();
+ try
+ {
+ for (Certificate cert : certs)
+ {
+ int certRecordType = CERTRecord.PKIX;
+ byte[] retData = null;
+
+ X509Certificate xCert = null;
+ try
+ {
+ // need to convert to cert container because this might be
+ // a certificate with wrapped private key data
+ final CertUtils.CertContainer cont = CertUtils.toCertContainer(cert.getData());
+ xCert = cont.getCert();
+ // check if this is a compliant certificate with the configured policy... if not, move on
+ if (!isCertCompliantWithPolicy(xCert))
+ continue;
+
+ retData = xCert.getEncoded();
+ }
+ catch (CertificateConversionException e)
+ {
+ // probably not a Certificate... might be a URL
+ }
+
+
+ if (xCert == null)
+ {
+ // see if it's a URL
+ try
+ {
+ retData = cert.getData();
+ URL url = new URL(new String(retData));
+ certRecordType = CERTRecord.URI;
+ }
+ catch (Exception e)
+ {
+ throw new DNSException(DNSError.newError(Rcode.SERVFAIL), "Failure while parsing CERT record data: " + e.getMessage(), e);
+ }
+ }
+
+ int keyTag = 0;
+ int alg = 0;
+ if (xCert != null && xCert.getPublicKey() instanceof RSAKey)
+ {
+ RSAKey key = (RSAKey)xCert.getPublicKey();
+ byte[] modulus = key.getModulus().toByteArray();
+
+ keyTag = (modulus[modulus.length - 2] << 8) & 0xFF00;
+
+ keyTag |= modulus[modulus.length - 1] & 0xFF;
+ if (xCert.getSigAlgOID().equalsIgnoreCase(SignatureAlgorithmIdentifier.SHA1RSA.getId())){
+ alg = 5; // RFC 4034 Appendix A.1
+ } else if (xCert.getSigAlgOID().equalsIgnoreCase(SignatureAlgorithmIdentifier.SHA256RSA.getId())){
+ alg = 8; // RFC 5702 3.1
+ } else if (xCert.getSigAlgOID().equalsIgnoreCase(SignatureAlgorithmIdentifier.SHA1DSA.getId())){
+ alg = 3; // RFC 4034 Appendix A.1
+ } else if (xCert.getSigAlgOID().equalsIgnoreCase(SignatureAlgorithmIdentifier.MD5RSA.getId())){
+ alg = 1; // RFC 4034 Appendix A.1
+ } else{
+ alg = 5;
+ }
+ }
+
+ CERTRecord rec = new CERTRecord(Name.fromString(name), DClass.IN, 86400L, certRecordType, keyTag,
+ alg /*public key alg, RFC 4034*/, retData);
+
+ retVal.addRR(rec);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new DNSException(DNSError.newError(Rcode.SERVFAIL), "Failure while parsing CERT record data: " + e.getMessage(), e);
+ }
+
+ // because of policy filtering, it's possible that we could have filtered out every cert
+ // resulting in an empty RR set
+ return (retVal.size() == 0) ? null : retVal;
+ }
+
+ @Override
+ protected synchronized Record checkForSoaRecord(String questionName)
+ {
+ if (!questionName.endsWith("."))
+ questionName += ".";
+
+ if (soaRecords == null)
+ {
+ Collection getRecs = null;
+ // load all SOA records...
+ try
+ {
+ getRecs = dnsService.getDNSRecord(Type.SOA, "");
+
+ if (getRecs == null || getRecs.size() == 0)
+ soaRecords = Collections.emptyMap();
+ else
+ {
+ soaRecords = new HashMap();
+
+ for (DNSRecord rec : getRecs)
+ {
+ Record newRec = Record.newRecord(Name.fromString(rec.getName()), Type.SOA,
+ rec.getDclass(), rec.getTtl(), rec.getData());
+
+ soaRecords.put(newRec.getName().toString(), newRec);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ LOGGER.error("Failed to load SOA records from config service.");
+ }
+ }
+
+ Record retVal = null;
+ if (soaRecords.size() > 0)
+ {
+ // look for the record by question name
+
+ retVal = soaRecords.get(questionName);
+ if (retVal == null)
+ {
+ // start taking apart the question name . by .
+ int index = -1;
+ while ((index = questionName.indexOf(".")) > 0 && index < (questionName.length() - 1))
+ {
+ questionName = questionName.substring(index + 1);
+ retVal = soaRecords.get(questionName);
+ if (retVal != null)
+ break;
+ }
+ }
+ }
+
+ return retVal;
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/SocketServerSettings.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/SocketServerSettings.java
new file mode 100644
index 000000000..ce1a5831a
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/SocketServerSettings.java
@@ -0,0 +1,206 @@
+/*
+ Copyright (c) 2010, Direct Project
+ All rights reserved.
+
+ Authors:
+ Umesh Madan umeshma@microsoft.com
+ Chris Lomonico chris.lomonico@surescripts.com
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of The Direct Project (directproject.org) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns;
+
+/**
+ * Tuning parameters for the DNS server socket connections. Tuning parameters are important to maximize the performance
+ * of the DNS server or to prevent the server machine from becoming over loaded.
+ *
+ * @author Greg Meyer
+ * @author Umesh Madan
+ * @author Chris Lomonico
+ *
+ * @since 1.0
+ */
+public class SocketServerSettings
+{
+
+ private static final int DEFAULT_MAX_CONNECTION_BACKLOG = 64;
+ private static final int DEFAULT_MAX_ACTIVE_REQUESTS = 64;
+ private static final int DEFAULT_MAX_OUTSTANDING_ACCEPTS = 16;
+ private static final int DEFAULT_READ_BUFFER_SIZE = 1024;
+ private static final int DEFAULT_SEND_TIMEOUT = 5000;
+ private static final int DEFAULT_RECEIVE_TIMEOUT = 50000;
+ private static final int DEFAULT_SOCKET_CLOSE_TIMEOUT = 5000;
+
+ private int maxOutstandingAccepts;
+ private int maxActiveRequests;
+ private int maxConnectionBacklog;
+ private int readBufferSize;
+ private int sendTimeout;
+ private int receiveTimeout;
+ private int socketCloseTimeout;
+
+ /**
+ * Creates a default set of socket parameters.
+ */
+ public SocketServerSettings()
+ {
+ maxOutstandingAccepts = DEFAULT_MAX_OUTSTANDING_ACCEPTS;
+ maxActiveRequests = DEFAULT_MAX_ACTIVE_REQUESTS;
+ maxConnectionBacklog = DEFAULT_MAX_CONNECTION_BACKLOG;
+ readBufferSize = DEFAULT_READ_BUFFER_SIZE;
+ sendTimeout = DEFAULT_SEND_TIMEOUT;
+ receiveTimeout = DEFAULT_RECEIVE_TIMEOUT;
+ socketCloseTimeout = DEFAULT_SOCKET_CLOSE_TIMEOUT;
+ }
+
+ /**
+ * Gets the maximum number of requests that can be accepted by the server, but not yet
+ * committed to a processing thread. Setting this value to high may result in DNS clients
+ * timing out due to outstanding requests waiting to long for a processing thread. The
+ * default is 16 requests.
+ * @return The maximum number of requests that can be excepted by the server, but not yet
+ * committed to a processing thread.
+ */
+ public int getMaxOutstandingAccepts()
+ {
+ return maxOutstandingAccepts;
+ }
+
+ /**
+ * Sets the maximum number of requests that can be accepted by the server, but not yet
+ * committed to a processing thread.
+ * @param maxOutstandingAccepts The maximum number of requests that can be accepted by the server, but not yet
+ * committed to a processing thread.
+ */
+ public void setMaxOutstandingAccepts(int maxOutstandingAccepts)
+ {
+ this.maxOutstandingAccepts = maxOutstandingAccepts;
+ }
+
+ /**
+ * Gets the maximum number of concurrent requests that can be processed by the server at any give time. Setting this
+ * value to high may result in overloading the system. Setting this value to low can limit throughput. The default
+ * 64.
+ * @return The maximum number of concurrent requests that can be processed by the server at any give time.
+ */
+ public int getMaxActiveRequests()
+ {
+ return maxActiveRequests;
+ }
+
+ /**
+ * Sets the maximum number of concurrent requests that can be processed by the server at any give time.
+ * @param maxActiveRequests The maximum number of concurrent requests that can be processed by the server at any give time.
+ */
+ public void setMaxActiveRequests(int maxActiveRequests)
+ {
+ this.maxActiveRequests = maxActiveRequests;
+ }
+
+ /**
+ * Gets the maximum number of connections that are in the IP socket accept backlog. Socket backlog is only relevant
+ * for TCP session based connections. Setting this value to high can overload the IP stack or result in DNS
+ * client timeouts. The default value is 64.
+ *
+ * @return The maximum number of connections that are in the IP socket accept backlog.
+ */
+ public int getMaxConnectionBacklog()
+ {
+ return maxConnectionBacklog;
+ }
+
+ /**
+ * Sets the maximum number of connections that are in the IP socket accept backlog.
+ * @param maxConnectionBacklog The maximum number of connections that are in the IP socket accept backlog.
+ */
+ public void setMaxConnectionBacklog(int maxConnectionBacklog)
+ {
+ this.maxConnectionBacklog = maxConnectionBacklog;
+ }
+
+ /**
+ * Gets the maximum size request buffer size in bytes. The default value is 1024 bytes.
+ * @return The maximum size request buffer size in bytes.
+ */
+ public int getReadBufferSize()
+ {
+ return readBufferSize;
+ }
+
+ /**
+ * Sets the maximum size request buffer size in bytes.
+ * @param readBufferSize The maximum size request buffer size in bytes.
+ */
+ public void setReadBufferSize(int readBufferSize)
+ {
+ this.readBufferSize = readBufferSize;
+ }
+
+ /**
+ * Gets the socket timeout in milliseconds for sending responses. Setting this value to high can
+ * result in performance degradation if multiple clients abandon their sessions. Setting this value
+ * to low can result in clients not receiving responses in high latency environments. The default value is
+ * 5000 milliseconds.
+ * @return The socket timeout in milliseconds for sending responses
+ */
+ public int getSendTimeout()
+ {
+ return sendTimeout;
+ }
+
+ /**
+ * Sets the socket timeout in milliseconds for sending responses.
+ * @param sendTimeout Sets the socket timeout in milliseconds for sending responses.
+ */
+ public void setSendTimeout(int sendTimeout)
+ {
+ this.sendTimeout = sendTimeout;
+ }
+
+ /**
+ * Gets the socket timeout in milliseconds for receiving or reading request. Setting this value to high can
+ * result in performance degradation if multiple clients abandon their sessions. Setting this value
+ * to low can result in the server not fully reading request data in high latency environments. The default value is
+ * 5000 milliseconds.
+ * @return The socket timeout in milliseconds for receiving or reading requests.
+ */
+ public int getReceiveTimeout()
+ {
+ return receiveTimeout;
+ }
+
+ /**
+ * Sets the socket timeout in milliseconds for receiving or reading request.
+ * @param receiveTimeout The socket timeout in milliseconds for receiving or reading request.
+ */
+ public void setReceiveTimeout(int receiveTimeout)
+ {
+ this.receiveTimeout = receiveTimeout;
+ }
+
+ /**
+ * Gets the timeout in milliseconds for closing a socket connection. The default value is 5000 milliseconds.
+ * @return The timeout in milliseconds for closing a socket connection.
+ */
+ public int getSocketCloseTimeout()
+ {
+ return socketCloseTimeout;
+ }
+
+ /**
+ * Sets the timeout in milliseconds for closing a socket connection. The default value is 5000 milliseconds.
+ * @param socketCloseTimeout The timeout in milliseconds for closing a socket connection.
+ */
+ public void setSocketCloseTimeout(int socketCloseTimeout)
+ {
+ this.socketCloseTimeout = socketCloseTimeout;
+ }
+
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/TCPServer.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/TCPServer.java
new file mode 100644
index 000000000..bde6b4154
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/TCPServer.java
@@ -0,0 +1,318 @@
+/*
+ Copyright (c) 2010, Direct Project
+ All rights reserved.
+
+ Authors:
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of The Direct Project (directproject.org) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.Inet4Address;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.xbill.DNS.Message;
+import org.xbill.DNS.Rcode;
+import org.xbill.DNS.Section;
+
+/**
+ * TCP socket server that handled DNS requests over TCP.
+ * @author Greg Meyer
+ * @since 1.0
+ */
+public class TCPServer extends DNSSocketServer
+{
+
+ private static final Log LOGGER = LogFactory.getFactory().getInstance(TCPServer.class);
+
+ private ServerSocket serverSocket;
+
+ private volatile long missCount = 0;
+ private volatile long errorCount = 0;
+ private volatile long successCount = 0;
+
+ /**
+ * Creates a TCP socket server. The server will not start accepting messages until the {@link #start()} method is called.
+ * @param settings The server settings. The settings contain specific IP and socket configuration parameters.
+ * @param responsder The DNS responder that will handle lookups.
+ * @throws DNSException
+ */
+ public TCPServer(DNSServerSettings settings, DNSResponder responder) throws DNSException
+ {
+ super(settings, responder);
+
+ registerMBean(this.getClass());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void start() throws DNSException
+ {
+ LOGGER.info("DNS TCP Server Starting");
+ super.start();
+
+ if (LOGGER.isInfoEnabled())
+ {
+ StringBuilder builder = new StringBuilder();
+ builder.append("DNS TCP Server Startup Complete\r\n\tBind Address: ").append(settings.getBindAddress());
+ builder.append("\r\n\tBind Port: ").append(settings.getPort());
+ LOGGER.info(builder.toString());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void stop() throws DNSException
+ {
+ super.stop();
+
+ try
+ {
+ serverSocket.close();
+ }
+ catch (IOException e) {/* no-op */}
+
+ waitForGracefulStop();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void createServerSocket() throws DNSException
+ {
+
+ try
+ {
+ serverSocket = new ServerSocket(settings.getPort(), settings.getMaxConnectionBacklog(),
+ Inet4Address.getByName(settings.getBindAddress()));
+
+ }
+ catch (Exception e)
+ {
+ throw new DNSException(null, "Failed to create TCP server socket: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Runnable getSocketAcceptTask()
+ {
+ return new AcceptTask();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Runnable getDNSRequestTask(Object s)
+ {
+ return new RequestTask((Socket)s);
+ }
+
+ /*
+ * Thread that accepts socket connections
+ */
+ private class AcceptTask implements Runnable
+ {
+ public void run()
+ {
+ while(running.get())
+ {
+
+ try
+ {
+ Socket s = serverSocket.accept();
+
+ s.setReceiveBufferSize(settings.getMaxRequestSize());
+ s.setSoTimeout(settings.getReceiveTimeout());
+ submitDNSRequest(s);
+ }
+ catch (Throwable e)
+ {
+ if (running.get())
+ {
+ LOGGER.error("DNS TCP server socket dropped:" + e.getMessage());
+ reconnect();
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * In the case that the server loses it connections, the accept socket needs to be re-established.
+ */
+ private void reconnect()
+ {
+ // socket may already be closed, but clean up to be thorough.
+ try
+ {
+ serverSocket.close();
+ } catch (IOException e) {/* no-op */}
+
+ serverSocket = null;
+ while (serverSocket == null && running.get())
+ {
+ try
+ {
+ createServerSocket();
+ LOGGER.error("DNS TCP server socket re-established");
+ }
+ catch (DNSException ex)
+ {
+ LOGGER.error("DNS TCP server socket failed to rebind. Trying again in 5 seconds.");
+
+ // the socket creation failed....
+ // sleep 5 seconds and come back around and try again
+ try
+ {
+ Thread.sleep(5000);
+ }
+ catch (InterruptedException iex) {/* no-op */}
+ }
+ }
+ }
+
+ /*
+ * Task that handles incoming tasks
+ */
+ private class RequestTask implements Runnable
+ {
+ private Socket requestSocket;
+
+ public RequestTask(Socket s)
+ {
+ requestSocket = s;
+ }
+
+ @SuppressWarnings("unused")
+ public void run()
+ {
+ Message response = null;
+ Message query = null;
+ int inLength;
+ DataInputStream dataIn;
+ DataOutputStream dataOut;
+ byte [] in;
+
+ try
+ {
+ InputStream is = null;
+ try
+ {
+ is = requestSocket.getInputStream();
+ dataIn = new DataInputStream(is);
+ inLength = dataIn.readUnsignedShort();
+ }
+ catch (Exception e)
+ {
+ // don't fill up my logs due to input stream errors that can happen
+ // from DOS attaches
+ try
+ {
+ requestSocket.close();
+ }
+ catch (IOException e2) {/* no-op */}
+ return;
+ }
+ in = new byte[inLength];
+ dataIn.readFully(in);
+
+ //LOGGER.info("Valid message... moving on.");
+
+ try
+ {
+ query = responder.toMessage(in);
+
+ response = responder.processRequest(query);
+ }
+ catch (DNSException e)
+ {
+ //LOGGER.info("TCP server error response.");
+ if (query != null)
+ response = responder.processError(query, e.getError());
+ }
+
+ if (response != null)
+ {
+ if (response.getRcode() == Rcode.NOERROR || response.getRcode() == Rcode.NXDOMAIN)
+ {
+ ++successCount;
+ if (response.getSectionArray(Section.ANSWER).length == 0)
+ ++missCount;
+ }
+ else
+ ++errorCount;
+
+ //LOGGER.info("Sending back valid response.");
+
+ dataOut = new DataOutputStream(requestSocket.getOutputStream());
+ byte[] writeBytes = response.toWire();
+ dataOut.writeShort(writeBytes.length);
+ dataOut.write(writeBytes);
+ }
+ else
+ ++errorCount;
+ }
+ catch (IOException e)
+ {
+ LOGGER.error("Wire/connection protocol error handing DNS request: " + e.getMessage(), e);
+ }
+ finally
+ {
+ try
+ {
+ requestSocket.close();
+ }
+ catch (IOException e) {/* no-op */}
+ }
+ }
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Long getMissedRequestCount()
+ {
+ return missCount;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Long getSuccessfulRequestCount()
+ {
+ return successCount;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Long getErrorRequestCount()
+ {
+ return errorCount;
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/UDPServer.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/UDPServer.java
new file mode 100644
index 000000000..4ed8df47a
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/UDPServer.java
@@ -0,0 +1,287 @@
+/*
+ Copyright (c) 2010, Direct Project
+ All rights reserved.
+
+ Authors:
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of The Direct Project (directproject.org) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+package org.nhindirect.dns;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.Inet4Address;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.xbill.DNS.Message;
+import org.xbill.DNS.Rcode;
+import org.xbill.DNS.Section;
+
+/**
+ * UDP socket server that handled DNS requests over UDP.
+ * @author Greg Meyer
+ * @since 1.0
+ */
+public class UDPServer extends DNSSocketServer
+{
+ private static final Log LOGGER = LogFactory.getFactory().getInstance(UDPServer.class);
+
+ private static final int MAX_WIRE_SIZE = 512;
+
+
+ private DatagramSocket serverSock;
+
+ private volatile long missCount = 0;
+ private volatile long errorCount = 0;
+ private volatile long successCount = 0;
+
+ /**
+ * Creates a UDP server that listens to datagram packets. The server will not start accepting messages until the {@link #start()} method is called.
+ * @param settings The server settings. The settings contain specific IP and socket configuration parameters.
+ * @param responsder The DNS responder that will handle lookups.
+ * @throws DNSException
+ */
+ public UDPServer(DNSServerSettings settings, DNSResponder responder) throws DNSException
+ {
+ super(settings, responder);
+
+ registerMBean(this.getClass());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void start() throws DNSException
+ {
+ LOGGER.info("DNS UPD Server Starting");
+ super.start();
+
+ if (LOGGER.isInfoEnabled())
+ {
+ StringBuilder builder = new StringBuilder();
+ builder.append("DNS UDP Server Startup Complete\r\n\tBind Address: ").append(settings.getBindAddress());
+ builder.append("\r\n\tBind Port: ").append(settings.getPort());
+ LOGGER.info(builder.toString());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void stop() throws DNSException
+ {
+ super.stop();
+
+ serverSock.close();
+
+ waitForGracefulStop();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void createServerSocket() throws DNSException
+ {
+
+ try
+ {
+ serverSock = new DatagramSocket(settings.getPort(), Inet4Address.getByName(settings.getBindAddress()));
+ serverSock.setReceiveBufferSize(settings.getMaxRequestSize());
+ serverSock.setSoTimeout(settings.getReceiveTimeout());
+ }
+ catch (Exception e)
+ {
+ throw new DNSException(null, "Failed to create UDP server socket: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Runnable getSocketAcceptTask()
+ {
+ return new ReceiveTask();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Runnable getDNSRequestTask(Object packet)
+ {
+ return new RequestTask((DatagramPacket)packet);
+ }
+
+ /*
+ * Task that listens for datagram packets
+ */
+ private class ReceiveTask implements Runnable
+ {
+ public void run()
+ {
+
+ while(running.get())
+ {
+
+ try
+ {
+ byte[] inBuffer = new byte[settings.getMaxRequestSize()];
+ DatagramPacket inPacket = new DatagramPacket(inBuffer, inBuffer.length);
+
+ serverSock.receive(inPacket);
+
+ submitDNSRequest(inPacket);
+ }
+ catch (Throwable e)
+ {
+ // udp has no state, so we can just call receive again
+ // unless it was closed
+ if (serverSock.isClosed() && running.get())
+ {
+ LOGGER.error("DNS UDP server socket dropped:" + e.getMessage());
+ reconnect();
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * In the event that the server drops its connection, we need to open up a new
+ * datagram socket to listen for datagram packets.
+ */
+ private void reconnect()
+ {
+ // socket may already be closed, but clean up to be thorough.
+ serverSock.close();
+
+ serverSock = null;
+ while (serverSock == null && running.get())
+ {
+ try
+ {
+ createServerSocket();
+ LOGGER.error("DNS UDP server socket re-established");
+ }
+ catch (DNSException ex)
+ {
+ LOGGER.error("DNS UDP server socket failed to rebind. Trying again in 5 seconds.");
+
+ // the socket creation failed....
+ // sleep 5 seconds and come back around and try again
+ try
+ {
+ Thread.sleep(5000);
+ }
+ catch (InterruptedException iex) {/* no-op */}
+ }
+ }
+ }
+
+ /*
+ * Task that handles DNS requests.
+ */
+ public class RequestTask implements Runnable
+ {
+ private DatagramPacket inPacket;
+
+ public RequestTask(DatagramPacket inPacket)
+ {
+ this.inPacket = inPacket;
+ }
+
+ @SuppressWarnings("unused")
+ public void run()
+ {
+ Message query = null;
+ Message response = null;
+ DatagramPacket outPacket = null;
+
+ try
+ {
+
+ try
+ {
+ //LOGGER.info("Got UDP DNS query. Translating");
+ query = responder.toMessage(inPacket.getData());
+ //LOGGER.info("Send UDP DNS query to service.");
+ response = responder.processRequest(query);
+ //LOGGER.info("UDP query returned from config service");
+ }
+ catch (DNSException e)
+ {
+ //LOGGER.info("Sending UDP error response");
+ if (query != null)
+ response = responder.processError(query, e.getError());
+ }
+
+ if (response != null)
+ {
+ if (response.getRcode() == Rcode.NOERROR || response.getRcode() == Rcode.NXDOMAIN)
+ {
+ ++successCount;
+ if (response.getSectionArray(Section.ANSWER).length == 0)
+ ++missCount;
+ }
+ else
+ ++errorCount;
+
+ byte[] writeBytes = response.toWire(MAX_WIRE_SIZE);
+ outPacket = new DatagramPacket(writeBytes,
+ writeBytes.length,
+ inPacket.getAddress(),
+ inPacket.getPort());
+
+ //LOGGER.info("Sending UDP query valid response");
+ serverSock.send(outPacket);
+ }
+ else
+ ++errorCount;
+ }
+ catch (IOException e)
+ {
+ LOGGER.error("Wire/connection protocol error handing DNS request: " + e.getMessage(), e);
+ }
+ }
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Long getMissedRequestCount()
+ {
+ return missCount;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Long getSuccessfulRequestCount()
+ {
+ return successCount;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Long getErrorRequestCount()
+ {
+ return errorCount;
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/annotation/ConfigServiceURL.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/annotation/ConfigServiceURL.java
new file mode 100644
index 000000000..71cbccf68
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/annotation/ConfigServiceURL.java
@@ -0,0 +1,41 @@
+/*
+Copyright (c) 2010, NHIN Direct Project
+All rights reserved.
+
+Authors:
+ Umesh Madan umeshma@microsoft.com
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the distribution. Neither the name of the The NHIN Direct Project (nhindirect.org).
+nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns.annotation;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.google.inject.BindingAnnotation;
+
+/**
+ * Guice annotation for the location of the configuration service.
+ * @author Greg Meyer
+ * @since 1.0
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target( {ElementType.FIELD, ElementType.PARAMETER})
+@BindingAnnotation
+public @interface ConfigServiceURL {}
\ No newline at end of file
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/annotation/package-info.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/annotation/package-info.java
new file mode 100644
index 000000000..d28d9a103
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/annotation/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Guice annotations.
+ */
+package org.nhindirect.dns.annotation;
\ No newline at end of file
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/config/DNSServerConfig.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/config/DNSServerConfig.java
new file mode 100644
index 000000000..47cc00394
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/config/DNSServerConfig.java
@@ -0,0 +1,40 @@
+/*
+Copyright (c) 2010, NHIN Direct Project
+All rights reserved.
+
+Authors:
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the distribution. Neither the name of the The NHIN Direct Project (nhindirect.org).
+nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns.config;
+
+import com.google.inject.Injector;
+
+/**
+ * The DNSServerConfig is responsible for loading configuration information from a URL and creating an injector that will subsequently
+ * be used to create instances of a {@link DNSServer}.
+ * @author Greg Meyer
+ *
+ * @since 1.0
+ */
+public interface DNSServerConfig
+{
+ /**
+ * Gets a Guide Injector that can be used to create {@link DNSServer} objects based on configuration information.
+ * @return A Guide Injector that can be used to create {@link DNSServer} objects.
+ */
+ public Injector getServerInjector();
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/config/RESTDNSServerConfig.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/config/RESTDNSServerConfig.java
new file mode 100644
index 000000000..0f0eb8e54
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/config/RESTDNSServerConfig.java
@@ -0,0 +1,115 @@
+package org.nhindirect.dns.config;
+
+import java.net.URL;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.client.HttpClient;
+import org.nhindirect.config.model.Setting;
+import org.nhind.config.rest.SettingService;
+import org.nhind.config.rest.impl.DefaultSettingService;
+import org.nhindirect.common.rest.ServiceSecurityManager;
+import org.nhindirect.dns.DNSServerSettings;
+import org.nhindirect.dns.DNSStore;
+import org.nhindirect.dns.module.DNSServerModule;
+import org.nhindirect.dns.provider.BasicDNSServerSettingsProvider;
+import org.nhindirect.dns.provider.ConfigServiceRESTDNSStoreProvider;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Provider;
+
+public class RESTDNSServerConfig implements DNSServerConfig
+{
+ private static final Log LOGGER = LogFactory.getFactory().getInstance(WSDNSServerConfig.class);
+
+ private static final String DNS_SERVER_BINDING = "DNSServerBindings";
+ private static final String DNS_SERVER_PORT = "DNSServerPort";
+
+ private Provider storeProvider;
+ private Provider settings;
+ private HttpClient httpClient;
+ private ServiceSecurityManager secMgr;
+
+ private final String configServiceLocation;
+ private final SettingService settingService;
+
+ public RESTDNSServerConfig(String configServiceLocation, HttpClient httpClient, ServiceSecurityManager secMgr,
+ Provider storeProvider, Provider settings)
+ {
+ this.storeProvider = storeProvider;
+ this.configServiceLocation = configServiceLocation;
+ this.settings = settings;
+ this.httpClient = httpClient;
+ this.secMgr = secMgr;
+
+ settingService = new DefaultSettingService(configServiceLocation, httpClient, secMgr);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Injector getServerInjector()
+ {
+ LOGGER.info("Looking up DNS server configuration info from location " + configServiceLocation);
+
+ Provider settingsProv = getServerSettings();
+
+ try
+ {
+ if (storeProvider == null)
+ storeProvider = new ConfigServiceRESTDNSStoreProvider(new URL(configServiceLocation), httpClient, secMgr);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("Conguration location is not a valid URL", e);
+ }
+ DNSServerModule module = DNSServerModule.create(storeProvider, settingsProv);
+
+ return Guice.createInjector(module);
+ }
+
+ /*
+ * Just use the basic settings provider for now. Will only allow setting the port and IP bindings.
+ */
+ private Provider getServerSettings()
+ {
+ String ipBindings = "";
+ int port = 0;
+
+ try
+ {
+ Setting setting = settingService.getSetting(DNS_SERVER_BINDING);
+ if (setting != null)
+ ipBindings = setting.getValue();
+
+ setting = settingService.getSetting(DNS_SERVER_PORT);
+ if (setting != null)
+ {
+ String sPort = setting.getValue();
+ try
+ {
+ port = Integer.parseInt(sPort);
+ }
+ catch (Exception e)
+ {
+ LOGGER.warn("Could not parse port setting " + port + " from configuration service");
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ LOGGER.warn("Could not get DNS setting from web service.");
+ }
+
+ if ((ipBindings == null || ipBindings.length() == 0) && port == 0 && settings != null)
+ {
+ LOGGER.info("Using DNS server settings from injected provider.");
+ return settings;
+ }
+
+ LOGGER.info("Using DNS server settings from configuration service.");
+ return new BasicDNSServerSettingsProvider(ipBindings, port);
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/config/WSDNSServerConfig.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/config/WSDNSServerConfig.java
new file mode 100644
index 000000000..ed7b9b044
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/config/WSDNSServerConfig.java
@@ -0,0 +1,137 @@
+/*
+ Copyright (c) 2010, Direct Project
+ All rights reserved.
+
+ Authors:
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of The Direct Project (directproject.org) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns.config;
+
+import java.net.URL;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.nhind.config.ConfigurationServiceProxy;
+import org.nhind.config.Setting;
+import org.nhindirect.dns.DNSServerSettings;
+import org.nhindirect.dns.DNSStore;
+import org.nhindirect.dns.module.DNSServerModule;
+import org.nhindirect.dns.provider.BasicDNSServerSettingsProvider;
+import org.nhindirect.dns.provider.ConfigServiceDNSStoreProvider;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Provider;
+
+/**
+ * Loads DNS server configuration settings from the configuration service to create the injector.
+ * @author Greg Meyer
+ *
+ * @since 1.0
+ */
+public class WSDNSServerConfig implements DNSServerConfig
+{
+ private static final Log LOGGER = LogFactory.getFactory().getInstance(WSDNSServerConfig.class);
+
+ private static final String DNS_SERVER_BINDING = "DNSServerBindings";
+ private static final String DNS_SERVER_PORT = "DNSServerPort";
+
+ private Provider storeProvider;
+ private Provider settings;
+
+
+ private final ConfigurationServiceProxy cfService;
+ private final URL configServiceLocation;
+
+ /**
+ * Construct and configuration component with the location of the configuration file and an optional provider for creating
+ * instances of the DNSServer.
+ * @param configServiceLocation The full path of the XML configuration file.
+ * @param storeProvider An option provider used for creating instances of the {@link DNSStore}. If the provider is
+ * null, a default provider is used.
+ */
+ public WSDNSServerConfig(URL configServiceLocation, Provider storeProvider, Provider settings)
+ {
+ this.storeProvider = storeProvider;
+ this.configServiceLocation = configServiceLocation;
+ this.settings = settings;
+
+ cfService = new ConfigurationServiceProxy(configServiceLocation.toExternalForm());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Injector getServerInjector()
+ {
+ LOGGER.info("Looking up DNS server configuration info from location " + configServiceLocation.toExternalForm());
+
+ Provider settingsProv = getServerSettings();
+
+ if (storeProvider == null)
+ storeProvider = new ConfigServiceDNSStoreProvider(configServiceLocation);
+
+ DNSServerModule module = DNSServerModule.create(storeProvider, settingsProv);
+
+ return Guice.createInjector(module);
+ }
+
+ /*
+ * Just use the basic settings provider for now. Will only allow setting the port and IP bindings.
+ */
+ private Provider getServerSettings()
+ {
+ String ipBindings = "";
+ int port = 0;
+
+ try
+ {
+ Setting[] settings = cfService.getSettingsByNames(new String[] {DNS_SERVER_BINDING, DNS_SERVER_PORT});
+
+ if (settings != null && settings.length > 0)
+ {
+ for (Setting setting : settings)
+ {
+ if (setting.getName().equalsIgnoreCase(DNS_SERVER_BINDING))
+ {
+ ipBindings = setting.getValue();
+ }
+ else if (setting.getName().equalsIgnoreCase(DNS_SERVER_PORT))
+ {
+ String sPort = setting.getValue();
+ try
+ {
+ port = Integer.parseInt(sPort);
+ }
+ catch (Exception e)
+ {
+ LOGGER.warn("Could not parse port setting " + port + " from configuration service");
+ }
+ }
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ LOGGER.warn("Could not get DNS setting from web service.");
+ }
+
+ if ((ipBindings == null || ipBindings.length() == 0) && port == 0 && settings != null)
+ {
+ LOGGER.info("Using DNS server settings from injected provider.");
+ return settings;
+ }
+
+ LOGGER.info("Using DNS server settings from configuration service.");
+ return new BasicDNSServerSettingsProvider(ipBindings, port);
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/config/package-info.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/config/package-info.java
new file mode 100644
index 000000000..816344d50
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/config/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Utility class used by Guice modules for getting DNS server configuration information.
+ */
+package org.nhindirect.dns.config;
\ No newline at end of file
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/module/DNSServerConfigModule.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/module/DNSServerConfigModule.java
new file mode 100644
index 000000000..898855031
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/module/DNSServerConfigModule.java
@@ -0,0 +1,92 @@
+/*
+Copyright (c) 2010, NHIN Direct Project
+All rights reserved.
+
+Authors:
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the distribution. Neither the name of the The NHIN Direct Project (nhindirect.org).
+nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns.module;
+
+import java.net.URL;
+
+import org.nhindirect.dns.DNSServerSettings;
+import org.nhindirect.dns.DNSStore;
+import org.nhindirect.dns.config.DNSServerConfig;
+import org.nhindirect.dns.provider.WSDNSServerConfigProvider;
+
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Provider;
+
+/**
+ * Guice module for generating a configuration provider based on the URL protocol.
+ * @author Greg Meyer
+ *
+ * @since 1.0
+ */
+public class DNSServerConfigModule extends AbstractModule
+{
+ private final URL configLocation;
+ private final Provider storeProvider;
+ private final Provider settings;
+
+ /**
+ * Creates a DNSServerConfigModule that is used by a Guice to create a {@link DNSServerConfig} object. The DNSServerConfig
+ * object is subsequently used to create {@link DNSServer} objects.
+ * @param configLocation The URL that the {@link DNSServerConfig} will use to lookup configuration information.
+ * @param storeProvider An optional {@link DNSStore} provider that will create instances of DNSStore objects.
+ * @param settings Optional DNS server settings. Overridden by settings from the configuration service.
+ * @return A configured Guice module used for create {@link DNSServerConfig} objects.
+ */
+ public static DNSServerConfigModule create(URL configLocation, Provider storeProvider, Provider settings)
+ {
+ return new DNSServerConfigModule(configLocation, storeProvider, settings);
+ }
+
+ /*
+ * Private constructor.
+ */
+ private DNSServerConfigModule(URL configLocation, Provider storeProvider, Provider settings)
+ {
+ this.configLocation = configLocation;
+ this.storeProvider = storeProvider;
+ this.settings = settings;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void configure()
+ {
+ Provider provider = null;
+
+ if (provider == null)
+ {
+ if (configLocation.getProtocol().equalsIgnoreCase("HTTP") || configLocation.getProtocol().equalsIgnoreCase("HTTPS"))
+ {
+ // web services based
+ provider = new WSDNSServerConfigProvider(configLocation, storeProvider, settings);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Configuration URL uses an unsupported protocol: " + configLocation.getProtocol());
+ }
+ }
+ bind(DNSServerConfig.class).toProvider(provider);
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/module/DNSServerModule.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/module/DNSServerModule.java
new file mode 100644
index 000000000..235d7836b
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/module/DNSServerModule.java
@@ -0,0 +1,112 @@
+/*
+ Copyright (c) 2010, Direct Project
+ All rights reserved.
+
+ Authors:
+ Umesh Madan umeshma@microsoft.com
+ Chris Lomonico chris.lomonico@surescripts.com
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of The Direct Project (directproject.org) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns.module;
+
+import java.net.URL;
+
+import org.nhindirect.dns.DNSServerSettings;
+import org.nhindirect.dns.DNSStore;
+import org.nhindirect.dns.annotation.ConfigServiceURL;
+import org.nhindirect.dns.provider.BasicDNSServerSettingsProvider;
+
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Provider;
+
+/**
+ * Guice module for configuring and creating {@link DNSServer} instances. Allows configuration either using just a URL to
+ * a configuration service, or more advanced options by using {@link Provider providers}.
+ * @author Greg Meyer
+ * @since 1.0
+ */
+public class DNSServerModule extends AbstractModule
+{
+ private final URL configServiceURL;
+ private final Provider dnsStore;
+ private final Provider settings;
+
+ /**
+ * Creates a module using just a configuration URL. The server is created using the ConfigServiceDNSStore class with
+ * default server settings.
+ * @param configServiceURL A URL that provides the location to the configuration service.
+ * @return A DNSServerModule used to create instances of the DNS server.
+ */
+ public static DNSServerModule create(URL configServiceURL)
+ {
+ if (configServiceURL == null)
+ throw new IllegalArgumentException("URL cannot be null.");
+
+ return new DNSServerModule(configServiceURL, null, null);
+ }
+
+ /**
+ * Creates a module using a specific {@link DNSStore} provider and default server settings.
+ * @param dnsStore A provider used to create instances of the {@link DNSStore}.
+ * @return A DNSServerModule used to create instances of the DNS server.
+ */
+ public static DNSServerModule create(Provider dnsStore)
+ {
+ if (dnsStore == null)
+ throw new IllegalArgumentException("dnsStore cannot be null.");
+
+ return new DNSServerModule(null, dnsStore, new BasicDNSServerSettingsProvider());
+ }
+
+ /**
+ * Creates a module using specific {@link DNSStore} and {@link DNSServerSettings} providers
+ * @param dnsStore A provider used to create instances of the {@link DNSStore}.
+ * @param settings A provider used to create instances of the {@link DNSServerSettings}.
+ * @return A DNSServerModule used to create instances of the DNS server.
+ */
+ public static DNSServerModule create(Provider dnsStore, Provider settings)
+ {
+ if (dnsStore == null)
+ throw new IllegalArgumentException("dnsStore cannot be null.");
+
+ if (settings == null)
+ settings = new BasicDNSServerSettingsProvider();
+
+ return new DNSServerModule(null, dnsStore, settings);
+ }
+
+ /*
+ * Private constructor.
+ */
+ private DNSServerModule(URL configServiceURL, Provider dnsStore, Provider settings)
+ {
+ this.configServiceURL = configServiceURL;
+ this.dnsStore = dnsStore;
+ this.settings = settings;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void configure()
+ {
+ if (configServiceURL != null)
+ this.bind(URL.class).annotatedWith(ConfigServiceURL.class).toInstance(configServiceURL);
+ else
+ {
+ this.bind(DNSStore.class).toProvider(dnsStore);
+ this.bind(DNSServerSettings.class).toProvider(settings);
+ }
+ }
+}
+
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/module/package-info.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/module/package-info.java
new file mode 100644
index 000000000..35edd3e72
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/module/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Guice modules for configuration and creating DNS server instances.
+ */
+package org.nhindirect.dns.module;
\ No newline at end of file
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/package-info.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/package-info.java
new file mode 100644
index 000000000..199e422e8
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Direct project DNS services and responders.
+ */
+package org.nhindirect.dns;
\ No newline at end of file
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/provider/AbstractConfigDNSStoreProvider.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/provider/AbstractConfigDNSStoreProvider.java
new file mode 100644
index 000000000..072146322
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/provider/AbstractConfigDNSStoreProvider.java
@@ -0,0 +1,29 @@
+package org.nhindirect.dns.provider;
+
+import java.net.URL;
+
+import org.nhindirect.dns.DNSStore;
+
+import com.google.inject.Provider;
+
+/**
+ * Abstract Guice provider for DNSStoreProviders that use the configuration service to retrieve runtime configuration information.
+ * @author Greg Meyer
+ *
+ * @since 1.0
+ */
+
+public abstract class AbstractConfigDNSStoreProvider implements Provider
+{
+ protected final URL configServiceURL;
+
+ /**
+ * Provider constructor.
+ * @param configServiceURL A URL to the location of the DNS configuration service.
+ */
+ public AbstractConfigDNSStoreProvider(URL configServiceURL)
+ {
+ this.configServiceURL = configServiceURL;
+ }
+
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/provider/BasicDNSServerSettingsProvider.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/provider/BasicDNSServerSettingsProvider.java
new file mode 100644
index 000000000..b6d656213
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/provider/BasicDNSServerSettingsProvider.java
@@ -0,0 +1,68 @@
+/*
+ Copyright (c) 2010, Direct Project
+ All rights reserved.
+
+ Authors:
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of The Direct Project (directproject.org) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns.provider;
+
+
+import org.nhindirect.dns.DNSServerSettings;
+
+import com.google.inject.Provider;
+
+/** Guice provider for configuring a minimal set of DNSServer settings.
+ * @author Greg Meyer
+ *
+ * @since 1.0
+ */
+public class BasicDNSServerSettingsProvider implements Provider
+{
+
+ private final String bindings;
+ private final int port;
+
+ /**
+ * Creates a provider using the default DNS server settings.
+ */
+ public BasicDNSServerSettingsProvider()
+ {
+ this(null, 0);
+ }
+
+ /**
+ * Creates a provider allowing the IP binding addresses and port to be overridden.
+ * @param bindings A comma delimited list of IP binding addresses.
+ * @param port The IP port that the server will use to listen for DNS requests.
+ */
+ public BasicDNSServerSettingsProvider(String bindings, int port)
+ {
+ this.bindings = bindings;
+ this.port = port;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public DNSServerSettings get()
+ {
+ DNSServerSettings settings = new DNSServerSettings();
+ if (port > 0)
+ settings.setPort(port);
+
+ if (bindings != null && !bindings.isEmpty())
+ settings.setBindAddress(bindings);
+
+ return settings;
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/provider/ConfigServiceDNSStoreProvider.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/provider/ConfigServiceDNSStoreProvider.java
new file mode 100644
index 000000000..4c7a32df2
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/provider/ConfigServiceDNSStoreProvider.java
@@ -0,0 +1,50 @@
+/*
+ Copyright (c) 2010, Direct Project
+ All rights reserved.
+
+ Authors:
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of The Direct Project (directproject.org) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns.provider;
+
+import java.net.URL;
+
+import org.nhindirect.dns.ConfigServiceDNSStore;
+import org.nhindirect.dns.DNSStore;
+
+
+/**
+ * Guice provider for creating {@link ConfigServiceDNSStore} instances.
+ * @author Greg Meyer
+ *
+ * @since 1.0
+ */
+public class ConfigServiceDNSStoreProvider extends AbstractConfigDNSStoreProvider
+{
+
+ /**
+ * Provider constructor.
+ * @param configServiceURL A URL to the location of the DNS configuration service.
+ */
+ public ConfigServiceDNSStoreProvider(URL configServiceURL)
+ {
+ super(configServiceURL);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public DNSStore get()
+ {
+ return new ConfigServiceDNSStore(configServiceURL);
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/provider/ConfigServiceRESTDNSStoreProvider.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/provider/ConfigServiceRESTDNSStoreProvider.java
new file mode 100644
index 000000000..7ad000bd0
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/provider/ConfigServiceRESTDNSStoreProvider.java
@@ -0,0 +1,36 @@
+package org.nhindirect.dns.provider;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.http.client.HttpClient;
+import org.nhindirect.common.rest.ServiceSecurityManager;
+import org.nhindirect.dns.DNSStore;
+import org.nhindirect.dns.RESTServiceDNSStore;
+
+public class ConfigServiceRESTDNSStoreProvider extends AbstractConfigDNSStoreProvider
+{
+ protected final HttpClient httpClient;
+ protected final ServiceSecurityManager secManager;
+
+ /**
+ * Provider constructor.
+ * @param configServiceURL A URL to the location of the DNS configuration service.
+ * @throws MalformedURLException
+ */
+ public ConfigServiceRESTDNSStoreProvider(URL configServiceURL, HttpClient httpClient, ServiceSecurityManager secManager)
+ {
+ super(configServiceURL);
+ this.httpClient = httpClient;
+ this.secManager = secManager;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public DNSStore get()
+ {
+ return new RESTServiceDNSStore(configServiceURL.toExternalForm(), httpClient, secManager);
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/provider/RESTDNSServerConfigProvider.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/provider/RESTDNSServerConfigProvider.java
new file mode 100644
index 000000000..99e61afa9
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/provider/RESTDNSServerConfigProvider.java
@@ -0,0 +1,38 @@
+package org.nhindirect.dns.provider;
+
+import org.apache.http.client.HttpClient;
+import org.nhindirect.common.rest.ServiceSecurityManager;
+import org.nhindirect.dns.DNSServerSettings;
+import org.nhindirect.dns.DNSStore;
+import org.nhindirect.dns.config.DNSServerConfig;
+import org.nhindirect.dns.config.RESTDNSServerConfig;
+
+import com.google.inject.Provider;
+
+public class RESTDNSServerConfigProvider implements Provider
+{
+ private final String configURL;
+ private final Provider storeProvider;
+ private final Provider settings;
+ private final HttpClient httpClient;
+ private final ServiceSecurityManager secMgr;
+
+ public RESTDNSServerConfigProvider(String configURL, HttpClient httpClient, ServiceSecurityManager secMgr,
+ Provider storeProvider, Provider settings)
+ {
+ this.configURL = configURL;
+ this.storeProvider = storeProvider;
+ this.settings = settings;
+ this.httpClient = httpClient;
+ this.secMgr = secMgr;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public DNSServerConfig get()
+ {
+ return new RESTDNSServerConfig(configURL, httpClient, secMgr, storeProvider, settings);
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/provider/WSDNSServerConfigProvider.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/provider/WSDNSServerConfigProvider.java
new file mode 100644
index 000000000..dc63e20c6
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/provider/WSDNSServerConfigProvider.java
@@ -0,0 +1,67 @@
+/*
+Copyright (c) 2010, NHIN Direct Project
+All rights reserved.
+
+Authors:
+ Umesh Madan umeshma@microsoft.com
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the distribution. Neither the name of the The NHIN Direct Project (nhindirect.org).
+nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns.provider;
+
+import java.net.URL;
+
+import org.nhindirect.dns.DNSServerSettings;
+import org.nhindirect.dns.DNSStore;
+import org.nhindirect.dns.config.DNSServerConfig;
+import org.nhindirect.dns.config.WSDNSServerConfig;
+
+import com.google.inject.Provider;
+
+/**
+ * Configuration provider for web service based configuration of the DNS Server.
+ * @author Greg Meyer
+ *
+ * @since 1.0
+ */
+public class WSDNSServerConfigProvider implements Provider
+{
+ private final URL configURL;
+ private final Provider storeProvider;
+ private final Provider settings;
+
+ /**
+ * Creates a provider with the location of the configuration service and an optional {@link DNSStore} provider.
+ * @param configURL A URL that contains the location of the configuration service.
+ * @param storeProvider An option provider used for creating specific instances of a {@link DNSStore}
+ * @param settings Optional DNS server settings. Overridden by settings from the configuration service.
+ */
+ public WSDNSServerConfigProvider(URL configURL, Provider storeProvider, Provider settings)
+ {
+ this.configURL = configURL;
+ this.storeProvider = storeProvider;
+ this.settings = settings;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public DNSServerConfig get()
+ {
+ return new WSDNSServerConfig(configURL, storeProvider, settings);
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/provider/package-info.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/provider/package-info.java
new file mode 100644
index 000000000..2799b19e3
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/provider/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Guice providers for creating DNS server configuration and server classes.
+ */
+package org.nhindirect.dns.provider;
\ No newline at end of file
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/service/DNSServerService.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/service/DNSServerService.java
new file mode 100644
index 000000000..6015ca20e
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/service/DNSServerService.java
@@ -0,0 +1,151 @@
+/*
+Copyright (c) 2010, NHIN Direct Project
+All rights reserved.
+
+Authors:
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the distribution. Neither the name of the The NHIN Direct Project (nhindirect.org).
+nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+package org.nhindirect.dns.service;
+
+import java.net.URL;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.nhindirect.dns.DNSException;
+import org.nhindirect.dns.DNSServer;
+import org.nhindirect.dns.DNSServerFactory;
+import org.nhindirect.dns.DNSServerSettings;
+import org.nhindirect.dns.DNSStore;
+import org.nhindirect.dns.provider.AbstractConfigDNSStoreProvider;
+import org.nhindirect.dns.provider.BasicDNSServerSettingsProvider;
+
+import com.google.inject.Provider;
+
+/**
+ * Service wrapper that instantiates and configures the DNS server.
+ * @author Greg Meyer
+ *
+ * @since 1.0
+ */
+public class DNSServerService
+{
+ protected static final String DNS_STORE_PROVIDER_VAR = "org.nhindirect.dns.DNSStoreProviderClass";
+
+ private static final Log LOGGER = LogFactory.getFactory().getInstance(DNSServerService.class);
+
+ protected final DNSServer server;
+
+ /**
+ * Creates the service wrapper with the location of the configuration service and server settings.
+ * @param configLocation URL with the location of the configuration service.
+ * @param settings Default settings for the server. Settings in the configuration service can over ride these settings.
+ * @throws DNSException
+ *
+ * @since 1.0
+ */
+ public DNSServerService(URL configLocation, DNSServerSettings settings) throws DNSException
+ {
+ LOGGER.info("Creating the DNSServer using configuration location " + configLocation.toExternalForm());
+
+
+ BasicDNSServerSettingsProvider settingsProv = new BasicDNSServerSettingsProvider(settings.getBindAddress(), settings.getPort());
+
+ Provider dnsStoreProvider = getDNSStoreProvider(configLocation);
+
+ server = DNSServerFactory.createDNSServer(configLocation, dnsStoreProvider, settingsProv);
+
+ LOGGER.info("DNS Server created. Starting server.");
+ server.start();
+
+ Runtime.getRuntime().addShutdownHook(new Thread()
+ {
+ public void run()
+ {
+ try
+ {
+ LOGGER.info("Shutdown hook detected. Intiate server shutdown.");
+ stopService();
+ }
+ catch (DNSException e) {/* no-op */}
+
+ }
+ });
+ }
+
+ /**
+ * Creates a {@link Provider} instance based on the system property
+ * org.nhindirect.dns.DNSStoreProviderClass. This property is the fully qualified class name
+ * of the provider. If the provider extends the {@link AbstractConfigDNSStoreProvider} class, then the configuration service
+ * location will be passed at construction time.
+ * @param configLocation The URL of the configuration service.
+ * @return A constructed instance of a provider. If the system cannot locate the Provider class specified by the
+ * org.nhindirect.dns.DNSStoreProviderClass system property of if the property does not exist, the method
+ * will return null.
+ *
+ */
+ @SuppressWarnings("unchecked")
+ protected Provider getDNSStoreProvider(URL configLocation)
+ {
+ Provider retVal = null;
+
+ // get the system property
+ String className = System.getProperty(DNS_STORE_PROVIDER_VAR);
+ if (className != null && !className.isEmpty())
+ {
+ try
+ {
+ Class> loadedClazz = DNSServerService.class.getClassLoader().loadClass(className);
+
+ if (AbstractConfigDNSStoreProvider.class.isAssignableFrom(loadedClazz))
+ {
+ // this provider takes a URL for the constructor
+ retVal = (Provider)loadedClazz.getConstructor(URL.class).newInstance(configLocation);
+ }
+ else
+ retVal = (Provider)loadedClazz.newInstance();
+
+ LOGGER.info("Loaded Provider class " + className + " for creating the DNSStore");
+ }
+ catch(Throwable e)
+ {
+ LOGGER.error("Could not load or construct instance of Provider class " + className + " A default " +
+ "provider will be used." , e);
+ }
+ }
+ else
+ LOGGER.info("A DNSStore provider class was not set. A default provider will be used.");
+
+ return retVal;
+ }
+
+ /**
+ * Stops and shutdown the service.
+ * @throws DNSException
+ *
+ * @since 1.0
+ */
+ public synchronized void stopService() throws DNSException
+ {
+ if (server != null)
+ {
+ LOGGER.info("Shutting down DNS server.");
+ server.stop();
+ }
+
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/service/SimpleServiceRunner.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/service/SimpleServiceRunner.java
new file mode 100644
index 000000000..73152155b
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/service/SimpleServiceRunner.java
@@ -0,0 +1,249 @@
+/*
+ Copyright (c) 2010, Direct Project
+ All rights reserved.
+
+ Authors:
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of The Direct Project (directproject.org) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns.service;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.net.Inet4Address;
+import java.net.URL;
+import java.security.Security;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.nhindirect.dns.DNSException;
+import org.nhindirect.dns.DNSServerSettings;
+
+
+/**
+ * Simple command line based application that launches the DNS server. Use the -help runtime parameter
+ * to see usage.
+ * @author Greg Meyer
+ * @since 1.0
+ */
+public class SimpleServiceRunner
+{
+ private static final Log LOGGER = LogFactory.getFactory().getInstance(SimpleServiceRunner.class);
+
+ private static final String MODE_STANDALONE = "STANDALONE";
+ private static final String MODE_SERVER = "SERVER";
+
+ private static int port;
+ private static String bind;
+ private static URL servURL;
+ private static String mode;
+
+ static
+ {
+ Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
+
+ port = 53;
+ bind = "0.0.0.0";
+ mode = MODE_STANDALONE;
+
+ try
+ {
+ servURL = new URL("http://localhost:8080/config-service/ConfigurationService");
+ }
+ catch (Exception e) {/* no-op */}
+ }
+
+ /**
+ * Main entry point into the application.
+ * @param argv Command line arguments.
+ */
+ public static void main(String[] argv)
+ {
+
+ // Check parameters
+ for (int i = 0; i < argv.length; i++)
+ {
+ String arg = argv[i];
+
+ // Options
+ if (!arg.startsWith("-"))
+ {
+ System.err.println("Error: Unexpected argument [" + arg + "]\n");
+ printUsage();
+ System.exit(-1);
+ }
+ else if (arg.equalsIgnoreCase("-p"))
+ {
+ if (i == argv.length - 1 || argv[i + 1].startsWith("-"))
+ {
+ System.err.println("Error: Missing port.");
+ System.exit(-1);
+ }
+
+ port = Integer.parseInt(argv[++i]);
+
+ }
+ else if (arg.equals("-b"))
+ {
+ if (i == argv.length - 1 || argv[i + 1].startsWith("-"))
+ {
+ System.err.println("Error: Missing bind IP address.");
+ System.exit(-1);
+ }
+ bind = argv[++i];
+
+ // validate its a valid IP addresses
+ String checkIP = "";
+ try
+ {
+ String[] ips = bind.split(",");
+ for (String ip : ips)
+ {
+ checkIP = ip;
+ Inet4Address.getByName(checkIP);
+ }
+ }
+ catch(Exception e)
+ {
+ System.err.println("Error in bind IP address " + checkIP + " : " + e.getMessage());
+ System.exit(-1);
+ }
+ }
+ else if (arg.equals("-u"))
+ {
+ if (i == argv.length - 1 || argv[i + 1].startsWith("-"))
+ {
+ System.err.println("Error: Missing service URL");
+ System.exit(-1);
+ }
+ String url = argv[++i];
+ try
+ {
+ servURL = new URL(url);
+ }
+ catch (Exception e)
+ {
+ System.err.println("Error in service URL parameter: " + e.getMessage());
+ System.exit(-1);
+ }
+ }
+ else if (arg.equals("-m"))
+ {
+ if (i == argv.length - 1 || argv[i + 1].startsWith("-"))
+ {
+ System.err.println("Error: Missing mode");
+ System.exit(-1);
+ }
+ mode = argv[++i];
+ if (!mode.equalsIgnoreCase(MODE_STANDALONE) && !mode.equalsIgnoreCase(MODE_SERVER))
+ {
+ System.err.println("Unknown mode: " + mode);
+ System.exit(-1);
+
+ }
+ }
+ else if (arg.equals("-help"))
+ {
+ printUsage();
+ System.exit(-1);
+ }
+ else
+ {
+ System.err.println("Error: Unknown argument " + arg + "\n");
+ printUsage();
+ System.exit(-1);
+ }
+ }
+
+ startAndRun();
+
+ if (mode.equalsIgnoreCase(MODE_STANDALONE))
+ System.exit(0);
+
+ }
+
+ /*
+ * Creates, intializes, and runs the server.
+ */
+ private static void startAndRun()
+ {
+ StringBuffer buffer = new StringBuffer("Starting DNS server. Settings:");
+ buffer.append("\r\n\tBind Addresses: ").append(bind);
+ buffer.append("\r\n\tListen Port: ").append(port);
+ buffer.append("\r\n\tService URL: ").append(servURL.toString());
+ LOGGER.info(buffer.toString() + "\n");
+
+
+ DNSServerService server = null;
+ try
+ {
+ DNSServerSettings settings = new DNSServerSettings();
+ settings.setPort(port);
+ settings.setBindAddress(bind);
+
+ server = new DNSServerService(servURL, settings);
+ }
+ catch (DNSException e)
+ {
+ LOGGER.error("Server failed to start: " + e.getMessage(), e);
+ return;
+ }
+
+ if (mode.equalsIgnoreCase(MODE_STANDALONE))
+ {
+ LOGGER.info("\r\nServer running.... Press Enter or Return to stop.");
+
+ InputStreamReader input = new InputStreamReader(System.in);
+ BufferedReader reader = new BufferedReader(input);
+
+ try
+ {
+ reader.readLine();
+
+ LOGGER.info("Shutting down server. Wait 5 seconds for cleanup.");
+
+ server.stopService();
+
+ Thread.sleep(5000);
+
+ LOGGER.info("Server stopped");
+ }
+ catch (Exception e)
+ {
+
+ }
+ }
+ else
+ LOGGER.info("\r\nServer running.");
+ }
+
+ /*
+ * Prints the command line usage.
+ */
+ private static void printUsage()
+ {
+ StringBuffer use = new StringBuffer();
+ use.append("Usage:\n");
+ use.append("java SimpleDNSServiceRunner (options)...\n\n");
+ use.append("options:\n");
+ use.append("-p port IP listener port.\n");
+ use.append(" Default: 53\n\n");
+ use.append("-b bind Comma limited list of IP addresses to bind to.\n");
+ use.append(" Default: 0.0.0.0 (All IP addresses on local machine)\n\n");
+ use.append("-u URL URL of DNS configuration service.\n");
+ use.append(" Default: http://localhost:8080/config-service/ConfigurationService\n\n");
+ use.append("-m mode Run mode: STANDALONE or SERVER.\n");
+ use.append(" Default: STANDALONE\n\n");
+
+
+ System.err.println(use);
+ }
+
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/service/package-info.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/service/package-info.java
new file mode 100644
index 000000000..ca50b190c
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/service/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Direct project DNS application/service wrappers and entry points.
+ */
+package org.nhindirect.dns.service;
\ No newline at end of file
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/CertCommands.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/CertCommands.java
new file mode 100644
index 000000000..db009b24c
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/CertCommands.java
@@ -0,0 +1,221 @@
+package org.nhindirect.dns.tools;
+
+
+import java.io.File;
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+
+import org.apache.commons.io.FileUtils;
+import org.nhind.config.ConfigurationServiceProxy;
+import org.nhind.config.EntityStatus;
+import org.nhindirect.dns.tools.printers.CertRecordPrinter;
+import org.nhindirect.dns.tools.printers.RecordPrinter;
+import org.nhindirect.dns.tools.utils.Command;
+import org.nhindirect.dns.tools.utils.StringArrayUtil;
+import org.nhindirect.dns.utils.CertUtils;
+import org.nhindirect.stagent.CryptoExtensions;
+import org.nhindirect.stagent.cert.X509CertificateEx;
+
+public class CertCommands
+{
+ private static final String LIST_CERTIFICATES_USAGE = "Lists certificates in the system";
+
+ private static final String LIST_EMAIL_CERTIFICATES_USAGE = "Lists certificates by a given email address or domain" +
+ "\r\n address" +
+ "\r\n\t address: The email address or domain to search for. Certificates are mathed on the subject alternative name field of legacy email address of the certificate";
+
+ private static final String IMPORT_PUBLIC_CERT_USAGE = "Imports a certificate that does not contain private key information" +
+ "\r\n certfile" +
+ "\r\n\t certfile: Fully qualified path and file name of the X509 certificate file. Place the file name in quotes (\"\") if there are spaces in the path or name.";
+
+ private static final String IMPORT_PRIVATE_CERT_USAGE = "Imports a certificate with a private key an optional passphrase. \r\n" +
+ "Files should be in pkcs12 format." +
+ "\r\n certfile [passphrase]" +
+ "\r\n\t certfile: Fully qualified path and file name of the pkcs12 certificate file. Place the file name in quotes (\"\") if there are spaces in the path or name." +
+ "\r\n\t [passphrase]: Optional passphrase to decrypt the pkcs12 file.";
+
+ private static final String ADD_IPKIX_CERT_USAGE = "Add an IPKIX record with a subject and URL. \r\n" +
+ "\r\n subject URL" +
+ "\r\n subject: email address or domain name" +
+ "\r\n\t URL: Fully qualified URL to certificate";
+
+ private static final String REMOVED_CERTIFICATE_USAGE = "Removes a certifacte from the system by owner." +
+ "\r\n owner" +
+ "\r\n\t owner: owner or URL of the certificate to be removed";
+
+
+ protected ConfigurationServiceProxy proxy;
+
+ protected RecordPrinter certPrinter;
+
+ public CertCommands(ConfigurationServiceProxy proxy)
+ {
+ this.proxy = proxy;
+
+ this.certPrinter = new CertRecordPrinter();
+ }
+
+ @Command(name = "ListCerts", usage = LIST_CERTIFICATES_USAGE)
+ public void listCerts(String[] args)
+ {
+ try
+ {
+ final org.nhind.config.Certificate[] certs = proxy.listCertificates(1, 1000, null);
+ if (certs == null || certs.length == 0)
+ System.out.println("No certificates found");
+ else
+ {
+ certPrinter.printRecords(Arrays.asList(certs));
+ }
+ }
+ catch (Exception e)
+ {
+ System.out.println("Failed to lookup certificates: " + e.getMessage());
+ }
+
+ }
+
+ @Command(name = "ListCertsByAddress", usage = LIST_EMAIL_CERTIFICATES_USAGE)
+ public void listCertsByAddress(String[] args)
+ {
+ String owner = StringArrayUtil.getRequiredValue(args, 0);
+
+ try
+ {
+ final org.nhind.config.Certificate[] certs = proxy.getCertificatesForOwner(owner, null);
+
+ if (certs == null || certs.length == 0)
+ System.out.println("No certificates found");
+ else
+ {
+ certPrinter.printRecords(Arrays.asList(certs));
+ }
+ }
+ catch (Exception e)
+ {
+ System.out.println("Failed to lookup certificates: " + e.getMessage());
+ }
+ }
+
+ @Command(name = "AddPublicCert", usage = IMPORT_PUBLIC_CERT_USAGE)
+ public void importPublicCert(String[] args)
+ {
+ final String fileLoc = StringArrayUtil.getRequiredValue(args, 0);
+ try
+ {
+ final X509Certificate cert = CertUtils.certFromFile(fileLoc);
+
+
+ final org.nhind.config.Certificate addCert = new org.nhind.config.Certificate();
+ addCert.setData(cert.getEncoded());
+ addCert.setOwner(CryptoExtensions.getSubjectAddress(cert));
+ addCert.setPrivateKey(false);
+ addCert.setStatus(EntityStatus.ENABLED);
+
+ proxy.addCertificates(new org.nhind.config.Certificate[] {addCert});
+ System.out.println("Successfully imported public certificate.");
+
+ }
+ catch (IOException e)
+ {
+ System.out.println("Error reading file " + fileLoc + " : " + e.getMessage());
+ return;
+ }
+ ///CLOVER:OFF
+ catch (Exception e)
+ {
+ System.out.println("Error importing certificate " + fileLoc + " : " + e.getMessage());
+ }
+ ///CLOVER:ON
+
+ }
+
+ @Command(name = "AddPrivateCert", usage = IMPORT_PRIVATE_CERT_USAGE)
+ public void importPrivateCert(String[] args)
+ {
+ final String fileLoc = StringArrayUtil.getRequiredValue(args, 0);
+ final String passPhrase = StringArrayUtil.getOptionalValue(args, 1, "");
+ try
+ {
+
+ final byte[] certBytes = FileUtils.readFileToByteArray(new File(fileLoc));
+
+ final byte[] insertBytes = (passPhrase == null || passPhrase.isEmpty()) ?
+ certBytes : CertUtils.pkcs12ToStrippedPkcs12(certBytes, passPhrase);
+
+ final X509Certificate cert = CertUtils.toX509Certificate(insertBytes);
+
+ org.nhind.config.Certificate addCert = new org.nhind.config.Certificate();
+ addCert.setData(certBytes);
+ addCert.setOwner(CryptoExtensions.getSubjectAddress(cert));
+ addCert.setPrivateKey(cert instanceof X509CertificateEx);
+ addCert.setStatus(EntityStatus.ENABLED);
+
+ proxy.addCertificates(new org.nhind.config.Certificate[] {addCert});
+ System.out.println("Successfully imported private certificate.");
+
+ }
+ catch (IOException e)
+ {
+ System.out.println("Error reading file " + fileLoc + " : " + e.getMessage());
+ return;
+ }
+ catch (Exception e)
+ {
+ System.out.println("Error importing certificate " + fileLoc + " : " + e.getMessage());
+ }
+ }
+
+ @Command(name = "AddIPKIXCert", usage = ADD_IPKIX_CERT_USAGE)
+ public void addIPKIXCert(String[] args)
+ {
+ final String owner = StringArrayUtil.getRequiredValue(args, 0);
+ final String URL = StringArrayUtil.getRequiredValue(args, 1);
+
+ try
+ {
+
+ org.nhind.config.Certificate addCert = new org.nhind.config.Certificate();
+ addCert.setData(URL.getBytes());
+ addCert.setOwner(owner);
+ addCert.setPrivateKey(false);
+ addCert.setStatus(EntityStatus.ENABLED);
+
+ proxy.addCertificates(new org.nhind.config.Certificate[] {addCert});
+ System.out.println("Successfully added IPKIX certificate URL.");
+
+
+ }
+ catch (Exception e)
+ {
+ System.out.println("Error add IPKIX URL: " + e.getMessage());
+ }
+ }
+
+ @Command(name = "RemoveCert", usage = REMOVED_CERTIFICATE_USAGE)
+ public void removeCert(String[] args)
+ {
+ final String owner = StringArrayUtil.getRequiredValue(args, 0);
+
+ try
+ {
+ proxy.removeCertificatesForOwner(owner);
+ System.out.println("Successfully removed certificate for owner." + owner);
+ }
+ catch (Exception e)
+ {
+ System.out.println("Error removing certificate for owner " + owner + " : " + e.getMessage());
+ }
+ }
+
+ public void setRecordPrinter(RecordPrinter printer)
+ {
+ this.certPrinter = printer;
+ }
+
+ public void setConfigurationProxy(ConfigurationServiceProxy proxy)
+ {
+ this.proxy = proxy;
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/DNSManager.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/DNSManager.java
new file mode 100644
index 000000000..ebb899894
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/DNSManager.java
@@ -0,0 +1,153 @@
+/*
+Copyright (c) 2010, NHIN Direct Project
+All rights reserved.
+
+Authors:
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the distribution. Neither the name of the The NHIN Direct Project (nhindirect.org).
+nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+package org.nhindirect.dns.tools;
+
+import java.net.URL;
+import java.util.Arrays;
+
+import org.nhind.config.ConfigurationServiceProxy;
+import org.nhindirect.dns.tools.utils.Commands;
+
+/**
+ * Command line tool for managing DNS entries in the Direct Project configuration service.
+ *
+ * The tool can either be run directly from the command prompt by passing arguments directly from
+ * the command line or can be run interactively by passing 0 parameters. The only exception is setting
+ * the URL to the configuration service. By default the manager uses "http://localhost:8081/config-service/ConfigurationService"
+ * as the config URL, but can be changed using the configURL command line parameters (it must be the first parameter on the
+ * command line).
+ *
+ * DNSManager configURL http://someserver:8081/config-service/ConfigurationService
+ * @author Greg Meyer
+ *
+ * @since 1.0
+ */
+public class DNSManager
+{
+ private static final String DEFAULT_CONFIG_URL = "http://localhost:8081/config-service/ConfigurationService";
+
+ private final Commands commands;
+
+ private static boolean exitOnEndCommands = true;
+
+ /**
+ * Application entry point.
+ * @param args Command line arguments.
+ *
+ * @since 1.0
+ */
+ public static void main(String[] args)
+ {
+ String[] passArgs = null;
+ String configURL = null;
+
+ // get the config URL if it exist
+ if (args.length > 1)
+ {
+ // check if the first argument is the config url
+ if (args[0].equalsIgnoreCase("configurl"))
+ {
+ //the next argument should be the config URL
+ configURL = args[1];
+ if (args.length > 2)
+ passArgs = (String[])Arrays.copyOfRange(args, 2, args.length);
+ else
+ passArgs = new String[0];
+
+ }
+ }
+
+ if (configURL == null)
+ {
+ configURL = DEFAULT_CONFIG_URL;
+ passArgs = args;
+ }
+
+ DNSManager manager = null;
+ try
+ {
+ manager = new DNSManager(new URL(configURL));
+ }
+ catch (Exception e)
+ {
+ System.err.println("Invalid config URL");
+ }
+
+ boolean runCommand = false;
+
+ if (manager != null)
+ {
+ runCommand = manager.run(passArgs);
+ }
+
+ if (exitOnEndCommands)
+ System.exit(runCommand ? 0 : -1);
+ }
+
+ /**
+ * Constructor with the location of the configuration service.
+ * @param configURL URL containing the locations of the configuration service.
+ *
+ * @since 1.0
+ */
+ public DNSManager(URL configURL)
+ {
+ ConfigurationServiceProxy proxy = new ConfigurationServiceProxy(configURL.toExternalForm());
+
+ commands = new Commands("DNS Management Console");
+ commands.register(new DNSRecordCommands(proxy));
+ commands.register(new CertCommands(proxy));
+
+ System.out.println("Configuration service URL: " + configURL.toExternalForm());
+
+ }
+
+ /**
+ * Either executes commands from the command line or runs the manager interactively.
+ * @param args Command arguments. If the arguments are empty, then the manager runs interactively.
+ * @return True if the command was run successfully. False otherwise.
+ *
+ * @since 1.0
+ */
+ public boolean run(String[] args)
+ {
+ if (args != null && args.length > 0)
+ {
+ return commands.run(args);
+ }
+
+ commands.runInteractive();
+ System.out.println("Shutting Down DNS Manager Console");
+ return true;
+ }
+
+ /**
+ * Determines if the application should exit when command processing is complete. It may be desirable to set this
+ * to false if calling from another application context. The default is true.
+ * @param exit True if the application should terminate on completing processing commands. False otherwise.
+ */
+ public static void setExitOnEndCommands(boolean exit)
+ {
+ exitOnEndCommands = exit;
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/DNSRecordCommands.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/DNSRecordCommands.java
new file mode 100644
index 000000000..a63cfe93c
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/DNSRecordCommands.java
@@ -0,0 +1,690 @@
+/*
+Copyright (c) 2010, NHIN Direct Project
+All rights reserved.
+
+Authors:
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the distribution. Neither the name of the The NHIN Direct Project (nhindirect.org).
+nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns.tools;
+
+import java.io.File;
+import java.rmi.RemoteException;
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.io.FileUtils;
+import org.bouncycastle.util.Arrays;
+import org.nhind.config.ConfigurationServiceProxy;
+import org.nhind.config.DnsRecord;
+import org.nhindirect.dns.tools.utils.Command;
+import org.nhindirect.dns.tools.utils.StringArrayUtil;
+import org.xbill.DNS.Record;
+import org.xbill.DNS.Section;
+import org.xbill.DNS.Type;
+
+/**
+ * Command definition and logic for managing DNS records. Commands are case-insensitive.
+ * @author Greg Meyer
+ *
+ * @since 1.0
+ */
+public class DNSRecordCommands
+{
+ private static final String IMPORT_MX_USAGE = "Import a new MX dns record from a binary file." +
+ "\r\n\tfilepath " +
+ "\r\n\t filePath: path to the MX record binary file. Can have any (or no extension)";
+
+ private static final String IMPORT_SOA_USAGE = "Import a new SOA dns record from a binary file." +
+ "\r\n\tfilepath " +
+ "\r\n\t filePath: path to the SOA record binary file. Can have any (or no extension)";
+
+ private static final String IMPORT_ADDRESS_USAGE = "Import a new A dns record from a binary file." +
+ "\r\n\tfilepath " +
+ "\r\n\t filePath: path to the A record binary file. Can have any (or no extension)";
+
+ private static final String ADD_MX_USAGE = "Add a new MX dns record." +
+ "\r\n" + DNSRecordParser.PARSE_MX_USAGE;
+
+ private static final String ENSURE_MX_USAGE = "Adds a new MX dns record if an identical one does't already exist. " +
+ "\r\n" + DNSRecordParser.PARSE_MX_USAGE;
+
+ private static final String ADD_SOA_USAGE = "Add a new SOA dns record." +
+ "\r\n" + DNSRecordParser.PARSE_SOA_USAGE;
+
+ private static final String ENSURE_SOA_USAGE = "Add a new SOA dns record if an identical one does not exist." +
+ "\r\n" + DNSRecordParser.PARSE_SOA_USAGE;
+
+ private static final String ADD_ANAME_USAGE = "Add a new ANAME dns record." +
+ "\r\n" + DNSRecordParser.PARSE_ANAME_USAGE;
+
+ private static final String ENSURE_ANAME_USAGE = "Add a new ANAME dns record if an identical one does not exist." +
+ "\r\n" + DNSRecordParser.PARSE_ANAME_USAGE;
+
+ private static final String REMOVE_MX_USAGE = "Remove an existing MX record by ID." +
+ "\r\n\trecordid" +
+ "\r\n\t recordid: record id to be removed from the database";
+
+
+ private static final String REMOVE_SOA_USAGE = "Remove an existing SOA record by ID." +
+ "\r\n\trecordid" +
+ "\r\nt\t recordid: record id to be removed from the database";
+
+
+ private static final String REMOVE_ANAME_USAGE = "Remove an existing ANAME record by ID." +
+ "\r\n\trecordid" +
+ "\r\n\t recordid: record id to be removed from the database";
+
+
+ private static final String GET_MX_USAGE = "Gets an existing MX record by ID." +
+ "\r\n\trecordid" +
+ "\r\n\t recordid: record id to be retrieved from the database";
+
+
+ private static final String GET_SOA_USAGE = "Gets an existing SOA record by ID." +
+ "\r\n\trecordid" +
+ "\r\n\t recordid: record id to be retrieved from the database";
+
+
+ private static final String GET_ANAME_USAGE = "Gets an existing ANAME record by ID." +
+ "\r\n\trecordid";
+
+ private static final String GET_ALL_USAGE = "Gets all records in the DNS store.";
+ private DNSRecordPrinter printer;
+ private DNSRecordParser parser;
+ private ConfigurationServiceProxy proxy;
+
+ /**
+ * Constructor that takes a reference to the configuration service proxy.
+ * @param proxy Configuration service proxy for accessing the configuration service.
+ *
+ * @since 1.0
+ */
+ public DNSRecordCommands(ConfigurationServiceProxy proxy)
+ {
+ parser = new DNSRecordParser();
+ printer = new DefaultDNSRecordPrinter();
+ this.proxy = proxy;
+ }
+
+ /*
+ * Convert a dnsjava record to a DnsRecord for use with the proxy.
+ */
+ private DnsRecord fromRecord(Record rec)
+ {
+ DnsRecord retVal = new DnsRecord();
+ retVal.setData(rec.rdataToWireCanonical());
+ retVal.setDclass(rec.getDClass());
+ retVal.setName(rec.getName().toString());
+ retVal.setTtl(rec.getTTL());
+ retVal.setType(rec.getType());
+
+ return retVal;
+ }
+
+ /*
+ * Loads a record from a file. Records are stored in raw wire format.
+ */
+ private DnsRecord loadAndVerifyDnsRecordFromBin(String path)
+ {
+ File recFile = new File(path);
+ if (!recFile.exists())
+ throw new IllegalArgumentException("Record file " + recFile.getAbsolutePath() + " not found");
+
+ Record rec = null;
+ try
+ {
+ byte[] wire = FileUtils.readFileToByteArray(recFile);
+
+ rec = Record.fromWire(wire, Section.ANSWER);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("Error reading file " + recFile.getAbsolutePath() + " : " + e.getMessage(), e);
+ }
+
+ return (rec != null) ? fromRecord(rec) : null;
+ }
+
+ /*
+ * Adds a DNS record to the configuration service.
+ */
+ private void addDNS(DnsRecord dnsRecord)
+ {
+ try
+ {
+ proxy.addDNS(new DnsRecord[] {dnsRecord});
+ System.out.println("Record added successfully.");
+ }
+ catch (RemoteException e)
+ {
+ throw new RuntimeException("Error adding DNS record: " + e.getMessage(), e);
+ }
+
+ }
+
+ /*
+ * Removed a DNS record from the service
+ */
+ private void removeDNS(long recordId)
+ {
+ try
+ {
+ proxy.removeDNSByRecordId(recordId);
+ System.out.println("Record removed successfully.");
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("Error accessing configuration service: " + e.getMessage(), e);
+ }
+ }
+
+ /*
+ * Imports a specific DNS record type from a file.
+ */
+ private void importRecord(String path, int type)
+ {
+ DnsRecord dnsRecord = loadAndVerifyDnsRecordFromBin(path);
+
+ if (dnsRecord.getType() != type)
+ {
+ throw new IllegalArgumentException("File " + path + " does not contain the requested record type");
+ }
+
+ addDNS(dnsRecord);
+ }
+
+ /**
+ * Imports an MX record from a file. The file contains the record in raw DNS wire format.
+ * @param args The first entry in the array contains the file path (required).
+ *
+ * @since 1.0
+ */
+ @Command(name = "Dns_MX_Import", usage = IMPORT_MX_USAGE)
+ public void mXImport(String[] args)
+ {
+ String path = StringArrayUtil.getRequiredValue(args, 0);
+ importRecord(path, Type.MX);
+ }
+
+ /**
+ * Imports an SOA record from a file. The file contains the record in raw DNS wire format.
+ * @param args The first entry in the array contains the file path (required).
+ *
+ * @since 1.0
+ */
+ @Command(name = "Dns_SOA_Import", usage = IMPORT_SOA_USAGE)
+ public void sOAImport(String[] args)
+ {
+ String path = StringArrayUtil.getRequiredValue(args, 0);
+ importRecord(path, Type.SOA);
+ }
+
+ /**
+ * Imports an A record from a file. The file contains the record in raw DNS wire format.
+ * @param args The first entry in the array contains the file path (required).
+ *
+ * @since 1.0
+ */
+ @Command(name = "Dns_ANAME_Import", usage = IMPORT_ADDRESS_USAGE)
+ public void importAddress(String[] args)
+ {
+ String path = StringArrayUtil.getRequiredValue(args, 0);
+ importRecord(path, Type.A);
+ }
+
+ /**
+ * Adds an MX records to the configuration service.
+ * @param args Contains the MX record attributes.
+ *
+ * @since 1.0
+ */
+ @Command(name = "Dns_MX_Add", usage = ADD_MX_USAGE)
+ public void addMX(String[] args)
+ {
+ DnsRecord record = fromRecord(parser.parseMX(args));
+
+ addDNS(record);
+ }
+
+ /**
+ * Adds an MX records to the configuration service only if the record does not exist.
+ * @param args Contains the MX record attributes.
+ *
+ * @since 1.0
+ */
+ @Command(name = "Dns_MX_Ensure", usage = ENSURE_MX_USAGE)
+ public void ensureMX(String[] args)
+ {
+ DnsRecord record = fromRecord(parser.parseMX(args));
+ if (!verifyIsUnique(record, false))
+ {
+ return;
+ }
+
+
+ addDNS(record);
+ }
+
+ /**
+ * Adds an SOA records to the configuration service.
+ * @param args Contains the SOA record attributes.
+ *
+ * @since 1.0
+ */
+ @Command(name = "Dns_SOA_Add", usage = ADD_SOA_USAGE)
+ public void addSOA(String[] args)
+ {
+ DnsRecord record = fromRecord(parser.parseSOA(args));
+
+ addDNS(record);
+ }
+
+ /**
+ * Adds an SOA records to the configuration service only if the record does not exist.
+ * @param args Contains the SOA record attributes.
+ *
+ * @since 1.0
+ */
+ @Command(name = "Dns_SOA_Ensure", usage = ENSURE_SOA_USAGE)
+ public void ensureSOA(String[] args)
+ {
+ DnsRecord record = fromRecord(parser.parseSOA(args));
+ if (!verifyIsUnique(record, false))
+ {
+ return;
+ }
+
+ addDNS(record);
+ }
+
+ /**
+ * Adds an A records to the configuration service.
+ * @param args Contains the A record attributes.
+ *
+ * @since 1.0
+ */
+ @Command(name = "Dns_ANAME_Add", usage = ADD_ANAME_USAGE)
+ public void addANAME(String[] args)
+ {
+ DnsRecord record = fromRecord(parser.parseANAME(args));
+ addDNS(record);
+ }
+
+
+ /**
+ * Adds an A records to the configuration service only if the record does not exist.
+ * @param args Contains the A record attributes.
+ *
+ * @since 1.0
+ */
+ @Command(name = "Dns_ANAME_Ensure", usage = ENSURE_ANAME_USAGE)
+ public void ensureANAME(String[] args)
+ {
+ DnsRecord record = fromRecord(parser.parseANAME(args));
+ if (!verifyIsUnique(record, false))
+ {
+ return;
+ }
+
+ addDNS(record);
+ }
+
+ /**
+ * Removes an MX record from the configuration service by record id.
+ * @param args The first entry in the array contains the record id (required).
+ *
+ * @since 1.0
+ */
+ @Command(name = "Dns_MX_Remove", usage = REMOVE_MX_USAGE)
+ public void removeMX(String[] args)
+ {
+ long recordID = Long.parseLong(StringArrayUtil.getRequiredValue(args, 0));
+ removeDNS(recordID);
+ }
+
+ /**
+ * Removes an SOA record from the configuration service by record id.
+ * @param args The first entry in the array contains the record id (required).
+ *
+ * @since 1.0
+ */
+ @Command(name = "Dns_SOA_Remove", usage = REMOVE_SOA_USAGE)
+ public void removeSOA(String[] args)
+ {
+ long recordID = Long.parseLong(StringArrayUtil.getRequiredValue(args, 0));
+ removeDNS(recordID);
+ }
+
+ /**
+ * Removes an A record from the configuration service by record id.
+ * @param args The first entry in the array contains the record id (required).
+ *
+ * @since 1.0
+ */
+ @Command(name = "Dns_ANAME_Remove", usage = REMOVE_ANAME_USAGE)
+ public void removeANAME(String[] args)
+ {
+ long recordID = Long.parseLong(StringArrayUtil.getRequiredValue(args, 0));
+ removeDNS(recordID);
+ }
+
+ /**
+ * Looks up an MX record by record id.
+ * @param args The first entry in the array contains the record id (required).
+ *
+ * @since 1.0
+ */
+ @Command(name = "Dns_MX_Get", usage = GET_MX_USAGE)
+ public void getMX(String[] args)
+ {
+ get(Long.parseLong(StringArrayUtil.getRequiredValue(args, 0)));
+ }
+
+ /**
+ * Looks up an SOA record by record id.
+ * @param args The first entry in the array contains the record id (required).
+ *
+ * @since 1.0
+ */
+ @Command(name = "Dns_SOA_Get", usage = GET_SOA_USAGE)
+ public void getSOA(String[] args)
+ {
+ get(Long.parseLong(StringArrayUtil.getRequiredValue(args, 0)));
+ }
+
+ /**
+ * Looks up an A record by record id.
+ * @param args The first entry in the array contains the record id (required).
+ *
+ * @since 1.0
+ */
+ @Command(name = "Dns_ANAME_Get", usage = GET_ANAME_USAGE)
+ public void getANAME(String[] args)
+ {
+ get(Long.parseLong(StringArrayUtil.getRequiredValue(args, 0)));
+ }
+
+ /**
+ * Retrieves and prints all records in the configuration store.
+ * @param args Empty
+ *
+ * @since 1.0
+ */
+ @Command(name= "Dns_Get_All", usage = GET_ALL_USAGE)
+ public void getAll(String[] args)
+ {
+ DnsRecord[] records = null;
+ try
+ {
+ records = proxy.getDNSByType(Type.ANY);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("Error accessing configuration service: " + e.getMessage(), e);
+ }
+
+ if (records == null || records.length == 0)
+ {
+ System.out.println("No records found");
+ }
+ else
+ print(records);
+ }
+
+ /*
+ * Gets and prints a record by record is
+ */
+ private void get(long recordID)
+ {
+ DnsRecord record = getRecord(recordID);
+ if (record != null)
+ printer.print(record);
+ }
+
+ /**
+ * Looks up all records for a given domain and any sub domains.
+ * @param args The first entry in the array contains the domain name (required).
+ *
+ * @since 1.0
+ */
+ @Command(name = "Dns_Match", usage = "Resolve all records for the given domain")
+ public void match(String[] args)
+ {
+ String domain = StringArrayUtil.getRequiredValue(args, 0);
+ DnsRecord[] records = null;
+ Pattern pattern = Pattern.compile(domain);
+ ArrayList matchedRecords = new ArrayList();
+ try
+ {
+ records = proxy.getDNSByType(Type.ANY);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("Error accessing configuration service: " + e.getMessage(), e);
+ }
+
+ if (records == null || records.length == 0)
+ {
+ System.out.println("No records found");
+ return;
+ }
+ else
+ {
+ for (DnsRecord record : records)
+ {
+ Matcher matcher = pattern.matcher(record.getName());
+ if (matcher.find())
+ {
+ matchedRecords.add(record);
+ }
+ }
+ }
+
+ if (matchedRecords.size() == 0)
+ {
+ System.out.println("No records found");
+ return;
+ }
+
+ print(matchedRecords.toArray(new DnsRecord[matchedRecords.size()]));
+ }
+
+ /**
+ * Looks up SOA records for a given domain.
+ * @param args The first entry in the array contains the domain name (required).
+ *
+ * @since 1.0
+ */
+ @Command(name = "Dns_SOA_Match", usage = "Resolve SOA records for the given domain")
+ public void matchSOA(String[] args)
+ {
+ match(StringArrayUtil.getRequiredValue(args, 0), Type.SOA);
+ }
+
+ /**
+ * Looks up A records for a given host name.
+ * @param args The first entry in the array contains the domain name (required).
+ *
+ * @since 1.0
+ */
+ @Command(name = "Dns_ANAME_Match", usage = "Resolve Address records for the given domain")
+ public void matchAName(String[] args)
+ {
+ match(StringArrayUtil.getRequiredValue(args, 0), Type.A);
+ }
+
+ /**
+ * Looks up MX records for a given domain.
+ * @param args The first entry in the array contains the domain name (required).
+ *
+ * @since 1.0
+ */
+ @Command(name = "Dns_MX_Match", usage = "Resolve MX records for the given domain")
+ public void matchMX(String[] args)
+ {
+ match(StringArrayUtil.getRequiredValue(args, 0), Type.MX);
+ }
+
+ /*
+ * gets records for a domain name and sub domains for a specific type of record
+ */
+ private void match(String domain, int type)
+ {
+ DnsRecord[] records = getRecords(domain, type);
+ if (records != null && records.length > 0)
+ print(records);
+ }
+
+ /*
+ * gets a record by record id
+ */
+ private DnsRecord getRecord(long recordID)
+ {
+ DnsRecord dr = null;
+ try
+ {
+ dr = proxy.getDNSByRecordId(recordID);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("Error accessing configuration service: " + e.getMessage(), e);
+ }
+
+ if (dr == null)
+ {
+ System.out.println("No record found matching id.");
+ }
+
+ return dr;
+ }
+
+ /*
+ * gets records by name and type
+ */
+ private DnsRecord[] getRecords(String domain, int type)
+ {
+ if (!domain.endsWith("."))
+ domain += ".";
+
+ DnsRecord[] records = null;
+ try
+ {
+ records = proxy.getDNSByNameAndType(domain, type);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("Error accessing configuration service: " + e.getMessage(), e);
+ }
+
+ if (records == null || records.length == 0)
+ {
+ System.out.println("No records found");
+ }
+ return records;
+ }
+
+ /*
+ * ensures that a record is unique in the configuration service
+ */
+ private boolean verifyIsUnique(DnsRecord record, boolean details)
+ {
+ DnsRecord existing = find(record);
+ if (existing != null)
+ {
+ System.out.println("Record already exists");
+
+ print(existing);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /*
+ * finds a specific record by name and type
+ */
+ private DnsRecord find(DnsRecord record)
+ {
+ DnsRecord[] existingRecords = null;
+ try
+ {
+ existingRecords = proxy.getDNSByNameAndType(record.getName(), record.getType());
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("Error accessing configuration service: " + e.getMessage(), e);
+ }
+
+ if (existingRecords == null || existingRecords.length == 0)
+ {
+ return null;
+ }
+
+ for (DnsRecord existingRecord : existingRecords)
+ if (Arrays.areEqual(record.getData(), existingRecord.getData()))
+ return existingRecord;
+
+ return null;
+ }
+
+ /*
+ * prints the contents of an array of records
+ */
+ private void print(DnsRecord[] records)
+ {
+ if (records != null)
+ {
+ for(DnsRecord record : records)
+ {
+ print(record);
+ System.out.println("\r\n-------------------------------------------");
+ }
+ }
+ }
+
+ /*
+ * prints the contents of a specific record
+ */
+ private void print(DnsRecord dnsRecord)
+ {
+ System.out.println("RecordID: " + dnsRecord.getId());
+
+
+ printer.print(dnsRecord);
+
+ }
+
+ /**
+ * Sets the printer that will be used to print record query responses.
+ * @param printer The printer that will be used to print record query responses.
+ */
+ public void setRecordPrinter(DNSRecordPrinter printer)
+ {
+ this.printer = printer;
+ }
+
+ /**
+ * Sets the printer that will be used to print record query responses.
+ * @param printer The printer that will be used to print record query responses.
+ */
+ public void setConfigurationProxy(ConfigurationServiceProxy proxy)
+ {
+ this.proxy = proxy;
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/DNSRecordParser.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/DNSRecordParser.java
new file mode 100644
index 000000000..3ebe9d048
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/DNSRecordParser.java
@@ -0,0 +1,163 @@
+/*
+Copyright (c) 2010, NHIN Direct Project
+All rights reserved.
+
+Authors:
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the distribution. Neither the name of the The NHIN Direct Project (nhindirect.org).
+nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns.tools;
+
+import java.net.InetAddress;
+
+import org.nhindirect.dns.tools.utils.StringArrayUtil;
+import org.xbill.DNS.ARecord;
+import org.xbill.DNS.DClass;
+import org.xbill.DNS.MXRecord;
+import org.xbill.DNS.Name;
+import org.xbill.DNS.SOARecord;
+
+/**
+ * Parses an array of strings into DNS records.
+ * @author Greg Meyer
+ *
+ * @since 1.0
+ */
+public class DNSRecordParser
+{
+ public static final String PARSE_ANAME_USAGE = " hostname ipaddress ttl [notes]" +
+ "\r\n\t hostname: host name for the record" +
+ "\r\n\t ipaddress: IP address in dot notation" +
+ "\r\n\t ttl: time to live in seconds, 32bit int";
+
+ public static final String PARSE_SOA_USAGE = " domainname primarysourcedomain responsibleemail serialnumber ttl [refresh] [retry] [expire] [minimum] [notes]" +
+ "\r\n\t domainname: The domain name of the name server that was the primary source for this zone" +
+ "\r\n\t responsibleemail: Email mailbox of the hostmaster" +
+ "\r\n\t serialnumber: Version number of the original copy of the zone." +
+ "\r\n\t ttl: time to live in seconds, 32bit int" +
+ "\r\n\t [refresh]: Number of seconds before the zone should be refreshed." +
+ "\r\n\t [retry]: Number of seconds before failed refresh should be retried." +
+ "\r\n\t [expire]: Number of seconds before records should be expired if not refreshed" +
+ "\r\n\t [minimum]: Minimum TTL for this zone.";
+
+ public static final String PARSE_MX_USAGE = " domainname exchange ttl [preference] [notes]" +
+ "\r\n\t domainname: email domain name for the record" +
+ "\r\n\t exchange: smtp server host name for the domain" +
+ "\r\n\t ttl: time to live in seconds" +
+ "\r\n\t [preference]: short value indicating preference of the record";
+
+ /**
+ * Default empty constructor
+ *
+ * @since 1.0
+ */
+ public DNSRecordParser()
+ {
+ }
+
+ /*
+ * converts a string to a dnsjava Name
+ */
+ private Name nameFromString(String str)
+ {
+ if (!str.endsWith("."))
+ str += ".";
+
+ try
+ {
+ return Name.fromString(str);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("Invalid DNS name");
+ }
+ }
+
+ /*
+ * converts a string to a InetAddress object
+ */
+ private InetAddress inetFromString(String str)
+ {
+ try
+ {
+ return InetAddress.getByName(str);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("Invalid ip address");
+ }
+ }
+
+ /**
+ * Converts A record configuration information to an ARecord
+ * @param args The A record configuration parameters.
+ * @return A DNS ARecord.
+ *
+ * @since 1.0
+ */
+ public ARecord parseANAME(String[] args)
+ {
+
+ String domainName = StringArrayUtil.getRequiredValue(args, 0);
+ String ipAddress = StringArrayUtil.getRequiredValue(args, 1);
+ int ttl = Integer.parseInt(StringArrayUtil.getRequiredValue(args, 2));
+
+ return new ARecord(nameFromString(domainName), DClass.IN, ttl, inetFromString(ipAddress));
+
+ }
+
+ /**
+ * Converts SAO record configuration information to an SOARecord
+ * @param args The SOA record configuration parameters.
+ * @return A DNS SAORecord.
+ *
+ * @since 1.0
+ */
+ public SOARecord parseSOA(String[] args)
+ {
+ String domainName = StringArrayUtil.getRequiredValue(args, 0);
+ String primarySourceDomain = StringArrayUtil.getRequiredValue(args, 1);
+ String responsibleEmail = StringArrayUtil.getRequiredValue(args, 2);
+ int serialNumber = Integer.parseInt(StringArrayUtil.getRequiredValue(args, 3));
+ int ttl = Integer.parseInt(StringArrayUtil.getRequiredValue(args, 4));
+
+ int refresh = Integer.parseInt(StringArrayUtil.getOptionalValue(args, 5, "0"));
+ int retry = Integer.parseInt(StringArrayUtil.getOptionalValue(args, 6, "0"));
+ int expire = Integer.parseInt(StringArrayUtil.getOptionalValue(args, 7, "0"));
+ int minimum = Integer.parseInt(StringArrayUtil.getOptionalValue(args, 8, "0"));
+
+ return new SOARecord(nameFromString(domainName), DClass.IN, ttl, nameFromString(primarySourceDomain),
+ nameFromString(responsibleEmail), serialNumber, refresh, retry, expire, minimum);
+
+ }
+
+ /**
+ * Converts MX record configuration information to an MXRecord
+ * @param args The MX record configuration parameters.
+ * @return A DNS MXRecord.
+ *
+ * @since 1.0
+ */
+ public MXRecord parseMX(String[] args)
+ {
+ String domainName = StringArrayUtil.getRequiredValue(args, 0);
+ String exchange = StringArrayUtil.getRequiredValue(args, 1);
+ int ttl = Integer.parseInt(StringArrayUtil.getRequiredValue(args, 2));
+ short pref = Short.parseShort(StringArrayUtil.getOptionalValue(args, 3, "0"));
+
+ return new MXRecord(nameFromString(domainName), DClass.IN, ttl, pref, nameFromString(exchange));
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/DNSRecordPrinter.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/DNSRecordPrinter.java
new file mode 100644
index 000000000..f04c52941
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/DNSRecordPrinter.java
@@ -0,0 +1,38 @@
+package org.nhindirect.dns.tools;
+
+import java.util.Collection;
+
+import org.nhind.config.DnsRecord;
+
+/**
+ * Interface for printing DNS records to an output Stream.
+ * @author Greg Meyer
+ *
+ * @since 1.0
+ */
+public interface DNSRecordPrinter
+{
+ /**
+ * Prints the contents of a collection of DNS records.
+ * @param records A collection of DNS records to print.
+ *
+ * @since 1.0
+ */
+ public void print(Collection records);
+
+ /**
+ * Prints the contents of an array of DNS records.
+ * @param records An array of DNS records to print.
+ *
+ * @since 1.0
+ */
+ public void print(DnsRecord[] records);
+
+ /**
+ * Prints the contents of a single DNS records.
+ * @param record DNS records to print.
+ *
+ * @since 1.0
+ */
+ public void print(DnsRecord record);
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/DefaultDNSRecordPrinter.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/DefaultDNSRecordPrinter.java
new file mode 100644
index 000000000..154535aae
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/DefaultDNSRecordPrinter.java
@@ -0,0 +1,267 @@
+/*
+Copyright (c) 2010, NHIN Direct Project
+All rights reserved.
+
+Authors:
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the distribution. Neither the name of the The NHIN Direct Project (nhindirect.org).
+nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns.tools;
+
+import java.io.PrintWriter;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.nhind.config.DnsRecord;
+import org.xbill.DNS.ARecord;
+import org.xbill.DNS.CERTRecord;
+import org.xbill.DNS.MXRecord;
+import org.xbill.DNS.Name;
+import org.xbill.DNS.Record;
+import org.xbill.DNS.SOARecord;
+import org.xbill.DNS.Type;
+import org.xbill.DNS.security.CERTConverter;
+
+/**
+ * Utility class for formatting and outputting the content of DNS records.
+ * @author Greg Meyer
+ *
+ * @since 1.0
+ */
+public class DefaultDNSRecordPrinter implements DNSRecordPrinter
+{
+ private final PrintWriter writer;
+
+ /**
+ * Default constructor. Create a writer that outputs to system console.
+ *
+ * @since 1.0
+ */
+ public DefaultDNSRecordPrinter()
+ {
+ writer = new PrintWriter(System.out);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void print(Collection records)
+ {
+ if (records == null || records.size() == 0)
+ {
+ writer.println("Empty record list");
+ return;
+ }
+
+ for (DnsRecord record : records)
+ {
+ print(record);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void print(DnsRecord[] records)
+ {
+ if (records == null || records.length == 0)
+ {
+ writer.println("Empty record array");
+ return;
+ }
+
+ print(Arrays.asList(records));
+ }
+
+ /*
+ * Converts a DNS record type to a string representation
+ */
+ private String typeToString(int type)
+ {
+ switch (type)
+ {
+ case Type.A:
+ return "A";
+
+ case Type.MX:
+ return "MX";
+
+ case Type.SOA:
+ return "SOA";
+
+ case Type.CERT:
+ return "CERT";
+
+ default:
+ return "Unknown";
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void print(DnsRecord record)
+ {
+ if (record == null)
+ {
+ writer.println("Null Resource Record");
+ return;
+ }
+
+ writer.println("-----------");
+ print("Record Name", record.getName());
+ print("Type", typeToString(record.getType()));
+ print("TTL", String.valueOf(record.getTtl()));
+ switch(record.getType())
+ {
+ default:
+ break;
+
+ case Type.A:
+ print((ARecord)toRecord(record));
+ break;
+
+ case Type.SOA:
+ print((SOARecord)toRecord(record));
+ break;
+
+ case Type.MX:
+ print((MXRecord)toRecord(record));
+ break;
+
+ case Type.CERT:
+ print((CERTRecord)toRecord(record));
+ break;
+ }
+
+ writer.flush();
+ }
+
+ /*
+ * converts a String to a DNS name
+ */
+ private Name nameFromString(String str)
+ {
+ if (!str.endsWith("."))
+ str += ".";
+
+ try
+ {
+ return Name.fromString(str);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException("Invalid DNS name");
+ }
+ }
+
+ /*
+ * converts a configuration service DnsRecord to a dnsjava Record
+ */
+ private Record toRecord(DnsRecord rec)
+ {
+ return Record.newRecord(nameFromString(rec.getName()), rec.getType(), rec.getDclass(), rec.getTtl(), rec.getData());
+ }
+
+ /*
+ * prints the A record specific fields
+ */
+ private void print(ARecord body)
+ {
+ if (body == null)
+ {
+ print("Null A Record Body");
+ return;
+ }
+
+ this.print("IPAddress", body.getAddress().getHostAddress());
+ }
+
+ /*
+ * prints the MX record specific fields
+ */
+ private void print(MXRecord body)
+ {
+ if (body == null)
+ {
+ print("Null MX Record Body");
+ return;
+ }
+
+ print("Access Exchage Server", body.getTarget().toString());
+ print("Priority", String.valueOf(body.getPriority()));
+ }
+
+ /*
+ * prints the SOA record specific fields
+ */
+ private void print(SOARecord soa)
+ {
+ if (soa == null)
+ {
+ print("Null SOA Record Body");
+ return;
+ }
+
+
+ print("DomainName", soa.getName().toString());
+ print("Primary Name Server", soa.getHost().toString());
+ print("Refresh", String.valueOf(soa.getRefresh()));
+ print("Retry", String.valueOf(soa.getRetry()));
+ print("Expire", String.valueOf(soa.getExpire()));
+ print("Minimum", String.valueOf(soa.getMinimum()));
+ }
+
+ /*
+ * prints the CERT record specific fields
+ */
+ private void print(CERTRecord certbody)
+ {
+ if (certbody == null)
+ {
+ print("Null CERT Record Body");
+ return;
+ }
+
+
+ Certificate cert = CERTConverter.parseRecord(certbody);
+ if (cert instanceof X509Certificate) // may not be an X509Cert
+ {
+ X509Certificate xcert = (X509Certificate)cert;
+ print("Certificate Subject", xcert.getSubjectDN().getName());
+ }
+ }
+
+
+ /*
+ * prints a name value pair
+ */
+ private void print(String name, String value)
+ {
+ writer.println(name + ": " + value);
+ }
+
+ /*
+ * prints a specific string message
+ */
+ private void print(String message)
+ {
+ writer.println(message);
+ }
+
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/package-info.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/package-info.java
new file mode 100644
index 000000000..71c2fb79b
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * DNS management tools and command sets.
+ */
+package org.nhindirect.dns.tools;
\ No newline at end of file
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/printers/AbstractRecordPrinter.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/printers/AbstractRecordPrinter.java
new file mode 100644
index 000000000..631140eba
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/printers/AbstractRecordPrinter.java
@@ -0,0 +1,139 @@
+package org.nhindirect.dns.tools.printers;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collection;
+
+
+public abstract class AbstractRecordPrinter implements RecordPrinter
+{
+ protected final Collection reportColumns;
+ protected final int tableWidth;
+
+ protected static class ReportColumn
+ {
+ protected final String header;
+ protected final int width;
+ protected final String fieldName;
+
+ public ReportColumn(String header, int width, String fieldName)
+ {
+ this.header = header;
+ this.width = width;
+ this.fieldName = fieldName;
+ }
+ }
+
+ public AbstractRecordPrinter(int tableWidth, Collection reportColumns)
+ {
+ this.tableWidth = tableWidth;
+ this.reportColumns = reportColumns;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void printRecord(T record)
+ {
+ printRecords(Arrays.asList(record));
+ }
+
+ @Override
+ public void printRecords(Collection records)
+ {
+ printHeader();
+
+ for (T record : records)
+ printRecordInternal(record);
+ }
+
+ protected void printRecordInternal(T record)
+ {
+ StringBuilder builder = new StringBuilder();
+
+ int cnt = 0;
+ for (ReportColumn column : reportColumns)
+ {
+
+
+ builder.append(" ");
+ String colValue = getColumnValue(column, record);
+ builder.append(colValue);
+ // pad the rest with spaces
+ int padSize = (column.width - 2 ) - colValue.length();
+ for (int i = 0; i < padSize; ++i)
+ builder.append(' ');
+
+ if (++cnt < reportColumns.size())
+ builder.append("|");
+ }
+
+ builder.append("\r\n");
+ for (int i = 0; i < tableWidth; ++i)
+ builder.append('-');
+
+ System.out.println(builder.toString());
+ }
+
+ protected String getColumnValue(ReportColumn column, T record)
+ {
+ // default is to get the field value by introspection using
+ // the field name
+ try
+ {
+ Method method = record.getClass().getDeclaredMethod("get" + column.fieldName);
+ Object obj = method.invoke(record);
+ return obj.toString();
+ }
+ catch (Exception e)
+ {
+ return "ERROR: " + e.getMessage();
+ }
+ }
+
+ protected void printHeader()
+ {
+ StringBuilder builder = new StringBuilder();
+
+ // top of header
+ for (int i = 0; i < tableWidth; ++i)
+ builder.append('-');
+
+ builder.append("\r\n|");
+
+ int cnt = 0;
+ int widthUsed = 0;
+ for (ReportColumn column : reportColumns)
+ {
+ int currentWidth = 0;
+ if (++cnt >= reportColumns.size())
+ currentWidth = tableWidth - widthUsed;
+ else
+ currentWidth = column.width;
+
+ // center the header
+ int padding = (currentWidth - column.header.length()) / 2;
+
+ // add pre padding
+ for (int i = 0; i < padding; ++i)
+ builder.append(' ');
+
+ // print header
+ builder.append(column.header);
+
+ // add post padding
+ for (int i = 0; i < (padding -1); ++i)
+ builder.append(' ');
+
+ builder.append("|");
+
+ widthUsed += currentWidth;
+ }
+
+ // end of header
+ builder.append("\r\n");
+ for (int i = 0; i < tableWidth; ++i)
+ builder.append('-');
+
+ System.out.println(builder.toString());
+ }
+}
\ No newline at end of file
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/printers/CertRecordPrinter.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/printers/CertRecordPrinter.java
new file mode 100644
index 000000000..719b36ddb
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/printers/CertRecordPrinter.java
@@ -0,0 +1,106 @@
+package org.nhindirect.dns.tools.printers;
+
+
+import java.net.URL;
+
+import java.security.cert.X509Certificate;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Locale;
+
+import org.nhindirect.dns.DNSException;
+import org.nhindirect.dns.utils.CertUtils;
+import org.nhindirect.stagent.cert.Thumbprint;
+
+public class CertRecordPrinter extends AbstractRecordPrinter
+{
+
+ protected static final SimpleDateFormat dateFormatter;
+
+ protected static final String CERT_NAME_COL = "Subject Name/URL";
+ protected static final String RECORD_TYPE_COL = "Record Type";
+ protected static final String PRIVATE_IND_COL = "Private Key";
+ protected static final String TP_NAME_COL = "Thumbprint";
+ protected static final String EXPIRES_COL = "Expires";
+
+ protected static final Collection REPORT_COLS;
+
+ static
+ {
+ REPORT_COLS = new ArrayList();
+
+ REPORT_COLS.add(new ReportColumn(CERT_NAME_COL, 55, "getCertificate"));
+ REPORT_COLS.add(new ReportColumn(RECORD_TYPE_COL, 11, "getCertificate"));
+ REPORT_COLS.add(new ReportColumn(PRIVATE_IND_COL, 12, "getCertificate"));
+ REPORT_COLS.add(new ReportColumn(TP_NAME_COL, 55, "getCertificate"));
+ REPORT_COLS.add(new ReportColumn(EXPIRES_COL, 15, "getCertificate"));
+
+
+ dateFormatter = new SimpleDateFormat("MMM d yyyy" , Locale.getDefault());
+ }
+
+
+ public CertRecordPrinter()
+ {
+ super(150, REPORT_COLS);
+ }
+
+ @SuppressWarnings("unused")
+ @Override
+ protected String getColumnValue(ReportColumn column, org.nhind.config.Certificate retCert)
+ {
+ String tpOrURL = null;
+ boolean isURL = false;
+
+ X509Certificate cert = null;
+
+ try
+ {
+ cert = CertUtils.toX509Certificate(retCert.getData());
+ tpOrURL = Thumbprint.toThumbprint(cert).toString();
+ }
+ catch (DNSException e)
+ {
+ // probably not an X509 CERT... might be a URL
+ }
+
+ if (tpOrURL == null)
+ {
+ try
+ {
+ tpOrURL = new String(retCert.getData());
+ URL url = new URL(tpOrURL);
+ isURL = true;
+ }
+ catch (Exception e)
+ {
+ // invalid URL
+ return "";
+ }
+ }
+
+
+
+ try
+ {
+ if (column.header.equals(CERT_NAME_COL))
+ return retCert.getOwner();
+ else if (column.header.equals(RECORD_TYPE_COL))
+ return (isURL) ? "IPKIX" : "PKIX";
+ else if (column.header.equals(TP_NAME_COL))
+ return isURL ? tpOrURL : Thumbprint.toThumbprint(cert).toString();
+ else if (column.header.equals(EXPIRES_COL))
+ return isURL ? "" : dateFormatter.format(cert.getNotAfter());
+ else if (column.header.equals(PRIVATE_IND_COL))
+ return retCert.isPrivateKey() ? "Y" : "N";
+ else
+ return super.getColumnValue(column, retCert);
+ }
+ catch (Exception e)
+ {
+ return "ERROR: " + e.getMessage();
+ }
+ }
+
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/printers/RecordPrinter.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/printers/RecordPrinter.java
new file mode 100644
index 000000000..f4bed70c3
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/printers/RecordPrinter.java
@@ -0,0 +1,10 @@
+package org.nhindirect.dns.tools.printers;
+
+import java.util.Collection;
+
+public interface RecordPrinter
+{
+ public void printRecord(T rec);
+
+ public void printRecords(Collection recs);
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/utils/Action.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/utils/Action.java
new file mode 100644
index 000000000..b08d57d55
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/utils/Action.java
@@ -0,0 +1,40 @@
+/*
+Copyright (c) 2010, NHIN Direct Project
+All rights reserved.
+
+Authors:
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the distribution. Neither the name of the The NHIN Direct Project (nhindirect.org).
+nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+package org.nhindirect.dns.tools.utils;
+
+/**
+ * Command interface for DNS manager.
+ * @author Greg Meyer
+ *
+ * @param Parameters that are sent to the command actions.
+ *
+ * @since 1.0
+ */
+public interface Action
+{
+ /**
+ * Executes a command action.
+ * @param param Parameters passed to the action.
+ */
+ public void doAction(T param);
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/utils/Command.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/utils/Command.java
new file mode 100644
index 000000000..e54b37ec0
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/utils/Command.java
@@ -0,0 +1,51 @@
+/*
+Copyright (c) 2010, NHIN Direct Project
+All rights reserved.
+
+Authors:
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the distribution. Neither the name of the The NHIN Direct Project (nhindirect.org).
+nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+package org.nhindirect.dns.tools.utils;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation that marks a class method as a executable command in the DNS configuration tool.
+ * @author Greg Meyer
+ *
+ * @since 1.0
+ */
+@Target( {ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Command
+{
+ /**
+ * The name of the command. The is the actual command name used in the tool.
+ * @return The name of the command.
+ */
+ String name();
+
+ /**
+ * Optional text that describes how the command is used.
+ * @return
+ */
+ String usage();
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/utils/CommandDef.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/utils/CommandDef.java
new file mode 100644
index 000000000..575c7acda
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/utils/CommandDef.java
@@ -0,0 +1,89 @@
+/*
+Copyright (c) 2010, NHIN Direct Project
+All rights reserved.
+
+Authors:
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the distribution. Neither the name of the The NHIN Direct Project (nhindirect.org).
+nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+package org.nhindirect.dns.tools.utils;
+
+import java.util.Locale;
+
+/**
+ * Defines the attributes of a command.
+ * @author Greg Meyer
+ *
+ */
+class CommandDef
+{
+ private String name;
+ private Action eval;
+ private CommandUsage usage;
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ public Action getEval()
+ {
+ return eval;
+ }
+
+ public void setEval(Action eval)
+ {
+ this.eval = eval;
+ }
+
+ public CommandUsage getUsage()
+ {
+ return usage;
+ }
+
+ public void setUsage(CommandUsage usage)
+ {
+ this.usage = usage;
+ }
+
+
+ boolean hasUsage()
+ {
+ return (usage != null);
+ }
+
+ void showUsage()
+ {
+ System.out.println(getName().toUpperCase(Locale.getDefault()));
+ if (usage != null)
+ {
+ System.out.println(usage.getUsage());
+ }
+
+ System.out.println();
+ }
+
+ void showCommand()
+ {
+ System.out.println(getName().toUpperCase(Locale.getDefault()));
+ }
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/utils/CommandUsage.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/utils/CommandUsage.java
new file mode 100644
index 000000000..475522c7f
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/utils/CommandUsage.java
@@ -0,0 +1,34 @@
+/*
+Copyright (c) 2010, NHIN Direct Project
+All rights reserved.
+
+Authors:
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the distribution. Neither the name of the The NHIN Direct Project (nhindirect.org).
+nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+package org.nhindirect.dns.tools.utils;
+
+/**
+ * Interface used for getting the text used to decribe a command.
+ * @author Greg Meyer
+ *
+ * @since 1.0
+ */
+public interface CommandUsage
+{
+ public String getUsage();
+}
diff --git a/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/utils/Commands.java b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/utils/Commands.java
new file mode 100644
index 000000000..089972d7e
--- /dev/null
+++ b/java/tags/dns-2.0.1/src/main/java/org/nhindirect/dns/tools/utils/Commands.java
@@ -0,0 +1,558 @@
+/*
+Copyright (c) 2010, NHIN Direct Project
+All rights reserved.
+
+Authors:
+ Greg Meyer gm2552@cerner.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the distribution. Neither the name of the The NHIN Direct Project (nhindirect.org).
+nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package org.nhindirect.dns.tools.utils;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.io.FileUtils;
+
+/**
+ * Main command and control class for the DNS configuration manager tool. Command classes are registered to this class, and
+ * commands marked with the {@link Command} annotation are mapped as runnable commands into the system.
+ * @author Greg Meyer
+ *
+ * @since 1.0
+ */
+public class Commands
+{
+ static final String[] EmptyArgs = new String[0];
+
+ private static final String SEARCH_USAGE = "Search for commands matching the given wildcard pattern" +
+ "\r\n\tpattern" +
+ "\r\n\tpattern: (optional) pattern, containing '*' wildcards";
+
+ private static final String HELP_USAGE = "Show help" +
+ "\r\nhelp ['all' | name]" +
+ "\r\n\tall: All commands" +
+ "\r\n\tname: This command name or names with this PREFIX" +
+ "\r\nsearch [pattern]" +
+ "\r\n" +
+ SEARCH_USAGE;
+
+ private static final String COMMANDS_USAGE = "List the commands available" +
+ "\r\ncommands [nameprefix]";
+
+ private static final String EXIT_USAGE = "Exit the application";
+
+ private static final String BATCH_USAGE = "Run a series of commands from a file" +
+ "\r\nEach command is on its own line. Comments begin with //" +
+ "\r\nfilepath [echo command (default true)]";
+
+
+ private final String appName;
+ private final List