Java tutorial
/* * dnssecjava - a DNSSEC validating stub resolver for Java * Copyright (c) 2013-2015 Ingo Bauersachs * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.jitsi.dnssec.unbound.rpl; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.text.ParseException; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.TreeMap; import org.jitsi.dnssec.SRRset; import org.jitsi.dnssec.SecurityStatus; import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormat; import org.xbill.DNS.DClass; import org.xbill.DNS.DNSSEC.Algorithm; import org.xbill.DNS.DNSSEC; import org.xbill.DNS.Flags; import org.xbill.DNS.Master; import org.xbill.DNS.Message; import org.xbill.DNS.Name; import org.xbill.DNS.RRSIGRecord; import org.xbill.DNS.Rcode; import org.xbill.DNS.Record; import org.xbill.DNS.Section; import org.xbill.DNS.TextParseException; import org.xbill.DNS.Type; /** * Parser for the RPL unit-test files of unbound. */ public class RplParser { private final InputStream data; private List<String> algoStrings = new ArrayList<String>(); private enum ParseState { Zero, Server, ENTRY_BEGIN, STEP_QUERY, STEP_CHECK_ANSWER } public RplParser(InputStream data) { this.data = data; for (Field f : Algorithm.class.getFields()) { this.algoStrings.add(f.getName()); } } public Rpl parse() throws ParseException, IOException { BufferedReader r = new BufferedReader(new InputStreamReader(data)); String line; ParseState state = ParseState.Zero; Rpl rpl = new Rpl(); Message m = null; int section = -1; int step = -1; Check check = null; while ((line = r.readLine()) != null) { // comment or empty if (line.equals("") || line.startsWith(";")) { continue; } switch (state) { case Zero: if (line.startsWith("server:")) { state = ParseState.Server; } else if (line.startsWith("SCENARIO_BEGIN")) { rpl.scenario = line.substring(line.indexOf(" ")); rpl.replays = new LinkedList<Message>(); rpl.checks = new TreeMap<Integer, Check>(); } else if (line.startsWith("ENTRY_BEGIN")) { state = ParseState.ENTRY_BEGIN; m = new Message(); } else if (line.startsWith("STEP")) { String[] data = line.split("\\s"); step = Integer.parseInt(data[1]); m = new Message(); r.readLine(); if (data[2].equals("QUERY")) { state = ParseState.STEP_QUERY; check = new Check(); } else if (data[2].equals("CHECK_ANSWER")) { state = ParseState.STEP_CHECK_ANSWER; } } break; case Server: if (line.matches("\\s*trust-anchor:.*")) { SRRset rrset = new SRRset(); rrset.setSecurityStatus(SecurityStatus.SECURE); rrset.addRR(parseRecord(line.substring(line.indexOf("\"") + 1, line.length() - 1))); rpl.trustAnchors.add(rrset); } else if (line.matches("\\s*val-override-date:.*")) { rpl.date = DateTime.parse(line.substring(line.indexOf("\"") + 1, line.length() - 2), DateTimeFormat.forPattern("yyyyMMddHHmmss")); } else if (line.matches("\\s*val-nsec3-keysize-iterations:.*")) { String[] data = line.substring(line.indexOf("\"") + 1, line.length() - 1).split("\\s"); if (data.length % 2 != 0) { throw new ParseException("val-nsec3-keysize-iterations invalid", 0); } rpl.nsec3iterations = new TreeMap<Integer, Integer>(); for (int i = 0; i < data.length; i += 2) { rpl.nsec3iterations.put(Integer.parseInt(data[i]), Integer.parseInt(data[i + 1])); } } else if (line.matches("\\s*val-digest-preference:.*")) { rpl.digestPreference = line.substring(line.indexOf("\"") + 1, line.length() - 1); } else if (line.startsWith("CONFIG_END")) { state = ParseState.Zero; } break; case ENTRY_BEGIN: case STEP_CHECK_ANSWER: case STEP_QUERY: if (line.startsWith("MATCH") || line.startsWith("ADJUST")) { // ignore } else if (line.startsWith("REPLY")) { String[] flags = line.split("\\s"); if (state != ParseState.STEP_QUERY) { m.getHeader().setRcode(Rcode.value(flags[flags.length - 1])); } for (int i = 1; i < flags.length - (state == ParseState.STEP_QUERY ? 0 : 1); i++) { if (flags[i].equals("DO")) { // set on the resolver, not on the message } else { int flag = Flags.value(flags[i]); if (flag > -1) { m.getHeader().setFlag(flag); } else { throw new ParseException(flags[i] + ": not a Flag", i); } } } } else if (line.startsWith("SECTION QUESTION")) { section = Section.QUESTION; } else if (line.startsWith("SECTION ANSWER")) { section = Section.ANSWER; } else if (line.startsWith("SECTION AUTHORITY")) { section = Section.AUTHORITY; } else if (line.startsWith("SECTION ADDITIONAL")) { section = Section.ADDITIONAL; } else if (line.startsWith("ENTRY_END")) { if (state == ParseState.ENTRY_BEGIN) { rpl.replays.add(m); } else if (state == ParseState.STEP_CHECK_ANSWER) { check.response = m; rpl.checks.put(step, check); check = null; } else if (state == ParseState.STEP_QUERY) { check.query = m; } m = null; state = ParseState.Zero; } else { Record rec; if (section == Section.QUESTION) { rec = parseQuestion(line); } else { rec = parseRecord(line); } m.addRecord(rec, section); } break; } } return rpl; } private Record parseRecord(String line) throws IOException { try { Master ma = new Master(new ByteArrayInputStream(line.getBytes()), null, 3600); Record r = ma.nextRecord(); if (r.getType() == Type.RRSIG) { RRSIGRecord rr = (RRSIGRecord) r; // unbound directly uses the DER format for DSA signatures // instead of the format specified in rfc2536#section-3 if (rr.getAlgorithm() == Algorithm.DSA && rr.getSignature().length > 41) { Method DSASignaturetoDNS = DNSSEC.class.getDeclaredMethod("DSASignaturetoDNS", byte[].class, int.class); DSASignaturetoDNS.setAccessible(true); byte[] signature = (byte[]) DSASignaturetoDNS.invoke(null, rr.getSignature(), 0); RRSIGRecord fixed = new RRSIGRecord(rr.getName(), rr.getDClass(), rr.getTTL(), rr.getTypeCovered(), rr.getAlgorithm(), rr.getOrigTTL(), rr.getExpire(), rr.getTimeSigned(), rr.getFootprint(), rr.getSigner(), signature); Field f = getField(RRSIGRecord.class, "labels"); f.setAccessible(true); f.set(fixed, rr.getLabels()); r = fixed; } } return r; } catch (Exception ex) { if (ex.getMessage() != null && ex.getMessage().contains("expected an integer")) { String[] data = line.split("\\s"); StringBuilder sb = new StringBuilder(); for (int i = 0; i < data.length; i++) { if (this.algoStrings.contains(data[i])) { sb.append(Algorithm.value(data[i])); } else { sb.append(data[i]); } sb.append(' '); } return parseRecord(sb.toString()); } else { throw new IOException(line, ex); } } } private static Field getField(Class<?> clazz, String fieldName) throws NoSuchFieldException { try { return clazz.getDeclaredField(fieldName); } catch (NoSuchFieldException e) { Class<?> superClass = clazz.getSuperclass(); if (superClass == null) { throw e; } else { return getField(superClass, fieldName); } } } private Record parseQuestion(String line) throws TextParseException { String[] temp = line.replaceAll("\\s+", " ").split(" "); if (Type.value(temp[2]) == -1) { System.out.println(temp[2]); } return Record.newRecord(Name.fromString(temp[0]), Type.value(temp[2]), DClass.value(temp[1])); } }