1 package com.verisign.cl;
3 import java.io.BufferedReader;
4 import java.io.FileReader;
5 import java.io.IOException;
8 import org.xbill.DNS.*;
10 import com.verisign.tat.dnssec.CaptiveValidator;
11 import com.verisign.tat.dnssec.SecurityStatus;
13 public class DNSSECReconciler {
16 * Invoke with java -jar dnssecreconciler.jar server=127.0.0.1 \
17 * query_file=queries.txt dnskey_query=net dnskey_query=edu
19 private CaptiveValidator validator;
20 private SimpleResolver resolver;
22 private BufferedReader queryStream;
23 private Set<Name> zoneNames;
28 public String queryFile;
29 public String dnskeyFile;
30 public List<String> dnskeyNames;
33 validator = new CaptiveValidator();
37 * Convert a query line of the form: <qname> <qtype> <flags> to a request
41 * @return A query message
42 * @throws TextParseException
43 * @throws NameTooLongException
45 private Message queryFromString(String query_line)
46 throws TextParseException, NameTooLongException {
48 String[] tokens = query_line.split("[ \t]+");
54 if (tokens.length < 1)
56 qname = Name.fromString(tokens[0]);
57 if (!qname.isAbsolute()) {
58 qname = Name.concatenate(qname, Name.root);
61 for (int i = 1; i < tokens.length; i++) {
62 if (tokens[i].startsWith("+")) {
63 // For now, we ignore flags as uninteresting
67 int type = Type.value(tokens[i]);
72 int cls = DClass.value(tokens[i]);
85 Message query = Message
86 .newQuery(Record.newRecord(qname, qtype, qclass));
92 * Fetch the next query from either the command line or the query file
94 * @return a query Message, or null if the query list is exhausted
97 private Message nextQuery() throws IOException {
99 Message res = queryFromString(query);
104 if (queryStream == null && queryFile != null) {
105 queryStream = new BufferedReader(new FileReader(queryFile));
108 if (queryStream != null) {
109 String line = queryStream.readLine();
114 return queryFromString(line);
122 * Figure out the correct zone from the query by comparing the qname to the
123 * list of trusted DNSKEY owner names.
126 * @return a zone name
127 * @throws IOException
129 private Name zoneFromQuery(Message query) throws IOException {
131 if (zoneNames == null) {
132 zoneNames = new HashSet<Name>();
133 for (String key : validator.listTrustedKeys()) {
134 String[] components = key.split("/");
135 Name keyname = Name.fromString(components[0]);
136 if (! keyname.isAbsolute()) {
137 keyname = Name.concatenate(keyname, Name.root);
139 zoneNames.add(keyname);
143 Name qname = query.getQuestion().getName();
144 for (Name n : zoneNames) {
145 if (qname.subdomain(n)) {
153 public void execute() throws IOException {
154 // Configure our resolver
155 resolver = new SimpleResolver(server);
156 resolver.setEDNS(0, 4096, Flags.DO, null);
158 // Prime the validator
159 if (dnskeyFile != null) {
160 validator.addTrustedKeysFromFile(dnskeyFile);
162 for (String name : dnskeyNames) {
163 Message query = queryFromString(name + " DNSKEY");
164 Message response = resolver.send(query);
165 validator.addTrustedKeysFromResponse(response);
169 // Log our set of trusted keys
170 for (String key : validator.listTrustedKeys()) {
171 System.out.println("Trusted Key: " + key);
174 // Iterate over all queries
175 Message query = nextQuery();
177 while (query != null) {
178 Message response = resolver.send(query);
179 if (response == null) {
183 Name zone = zoneFromQuery(query);
184 byte result = validator.validateMessage(response, zone.toString());
187 case SecurityStatus.BOGUS:
188 case SecurityStatus.INVALID:
189 System.out.println("BOGUS Answer:");
190 System.out.println("Query: " + query.getQuestion());
191 System.out.println("Response:\n" + response);
192 for (String err : validator.getErrorList()) {
193 System.out.println("Error: " + err);
195 System.out.println("");
197 case SecurityStatus.INSECURE:
198 case SecurityStatus.INDETERMINATE:
199 case SecurityStatus.UNCHECKED:
200 System.out.println("Insecure Answer:");
201 System.out.println("Query: " + query.getQuestion());
202 System.out.println("Response:\n" + response);
203 for (String err : validator.getErrorList()) {
204 System.out.println("Error: " + err);
207 case SecurityStatus.SECURE:
215 private static void usage() {
217 .println("usage: java -jar dnssecreconiler.jar [..options..]");
218 System.err.println(" server: the DNS server to query.");
219 System.err.println(" query: a name [type [flags]] string.");
221 .println(" query_file: a list of queries, one query per line.");
223 .println(" dnskey_file: a file containing DNSKEY RRs to trust.");
225 .println(" dnskey_query: query 'server' for DNSKEY at given name to trust, may repeat");
228 public static void main(String[] argv) {
230 DNSSECReconciler dr = new DNSSECReconciler();
233 // Parse the command line options
234 for (String arg : argv) {
236 if (arg.indexOf('=') < 0) {
237 System.err.println("Unrecognized option: " + arg);
242 String[] split_arg = arg.split("=", 2);
243 String opt = split_arg[0];
244 String optarg = split_arg[1];
246 if (opt.equals("server")) {
248 } else if (opt.equals("query")) {
250 } else if (opt.equals("query_file")) {
251 dr.queryFile = optarg;
252 } else if (opt.equals("dnskey_file")) {
253 dr.dnskeyFile = optarg;
254 } else if (opt.equals("dnskey_query")) {
255 if (dr.dnskeyNames == null) {
256 dr.dnskeyNames = new ArrayList<String>();
258 dr.dnskeyNames.add(optarg);
260 System.err.println("Unrecognized option: " + opt);
266 // Check for minimum usage
267 if (dr.server == null) {
268 System.err.println("'server' must be specified");
272 if (dr.query == null && dr.queryFile == null) {
274 .println("Either 'query' or 'query_file' must be specified");
278 if (dr.dnskeyFile == null && dr.dnskeyNames == null) {
280 .println("Either 'dnskey_file' or 'dnskey_query' must be specified");
288 } catch (Exception e) {