Java tutorial
// Copyright (C) 2011 VeriSign, Inc. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 // USA package com.verisignlabs.dnssec.cl; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.ListIterator; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.xbill.DNS.Master; import org.xbill.DNS.NSEC3PARAMRecord; import org.xbill.DNS.NSEC3Record; import org.xbill.DNS.Name; import org.xbill.DNS.Record; import org.xbill.DNS.Section; import org.xbill.DNS.Type; import org.xbill.DNS.utils.base32; import com.verisignlabs.dnssec.security.RecordComparator; /** * This class forms the command line implementation of a zone file normalizer. * That is, a tool to rewrite zones in a consistent, comparable format. * * @author David Blacka (original) * @author $Author: davidb $ * @version $Revision: 2218 $ */ public class ZoneFormat extends CLBase { private CLIState state; /** * This is a small inner class used to hold all of the command line option * state. */ protected static class CLIState extends CLIStateBase { public String file; public boolean assignNSEC3; public CLIState() { super("jdnssec-zoneformat [..options..] zonefile"); } protected void setupOptions(Options opts) { opts.addOption("N", "nsec3", false, "attempt to determine the original ownernames for NSEC3 RRs."); } protected void processOptions(CommandLine cli) throws ParseException { if (cli.hasOption('N')) assignNSEC3 = true; String[] cl_args = cli.getArgs(); if (cl_args.length < 1) { System.err.println("error: must specify a zone file"); usage(); } file = cl_args[0]; } } private static List<Record> readZoneFile(String filename) throws IOException { Master master = new Master(filename); List<Record> res = new ArrayList<Record>(); Record r = null; while ((r = master.nextRecord()) != null) { // Normalize each record by round-tripping it through canonical wire line // format. Mostly this just lowercases names that are subject to it. byte[] wire = r.toWireCanonical(); Record canon_record = Record.fromWire(wire, Section.ANSWER); res.add(canon_record); } return res; } private static void formatZone(List<Record> zone) { // Put the zone into a consistent (name and RR type) order. RecordComparator cmp = new RecordComparator(); Collections.sort(zone, cmp); for (Record r : zone) { System.out.println(r.toString()); } } private static void determineNSEC3Owners(List<Record> zone) throws NoSuchAlgorithmException { // Put the zone into a consistent (name and RR type) order. Collections.sort(zone, new RecordComparator()); // first, find the NSEC3PARAM record -- this is an inefficient linear // search, although it should be near the head of the list. NSEC3PARAMRecord nsec3param = null; HashMap<String, String> map = new HashMap<String, String>(); base32 b32 = new base32(base32.Alphabet.BASE32HEX, false, true); Name zonename = null; for (Record r : zone) { if (r.getType() == Type.SOA) { zonename = r.getName(); continue; } if (r.getType() == Type.NSEC3PARAM) { nsec3param = (NSEC3PARAMRecord) r; break; } } // If we couldn't determine a zone name, we have an issue. if (zonename == null) return; // If there wasn't one, we have nothing to do. if (nsec3param == null) return; // Next pass, calculate a mapping between ownernames and hashnames Name last_name = null; for (Record r : zone) { if (r.getName().equals(last_name)) continue; if (r.getType() == Type.NSEC3) continue; Name n = r.getName(); byte[] hash = nsec3param.hashName(n); String hashname = b32.toString(hash); map.put(hashname, n.toString().toLowerCase()); last_name = n; // inefficiently create hashes for the possible ancestor ENTs for (int i = zonename.labels() + 1; i < n.labels(); ++i) { Name parent = new Name(n, n.labels() - i); byte[] parent_hash = nsec3param.hashName(parent); String parent_hashname = b32.toString(parent_hash); if (!map.containsKey(parent_hashname)) { map.put(parent_hashname, parent.toString().toLowerCase()); } } } // Final pass, assign the names if we can for (ListIterator<Record> i = zone.listIterator(); i.hasNext();) { Record r = i.next(); if (r.getType() != Type.NSEC3) continue; NSEC3Record nsec3 = (NSEC3Record) r; String hashname = nsec3.getName().getLabelString(0).toLowerCase(); String ownername = (String) map.get(hashname); NSEC3Record new_nsec3 = new NSEC3Record(nsec3.getName(), nsec3.getDClass(), nsec3.getTTL(), nsec3.getHashAlgorithm(), nsec3.getFlags(), nsec3.getIterations(), nsec3.getSalt(), nsec3.getNext(), nsec3.getTypes(), ownername); i.set(new_nsec3); } } public void execute() throws IOException, NoSuchAlgorithmException { List<Record> z = readZoneFile(state.file); if (state.assignNSEC3) determineNSEC3Owners(z); formatZone(z); } public static void main(String[] args) { ZoneFormat tool = new ZoneFormat(); tool.state = new CLIState(); tool.run(tool.state, args); } }