RRsetIterator(Zone zone, DomainFactory factory) { this.zone = zone; this.factory = factory; this.domainIter = zone.iterator(); if (domainIter == null) { throw new RuntimeException("Null Domain iterator"); } String rootString = zone.getRootDomain(); this.originNode = getDomainResource(rootString); if (originNode.getRRset(Type.SOA) == null) { throw new RuntimeException("Zone " + rootString + " missing SOA record"); } if (originNode.getRRset(Type.NS) == null) { throw new RuntimeException("Zone " + rootString + " missing NS rrset"); } List<RRset> sets = originNode.getAllRRsets(); this.current = new RRset[sets.size()]; for (int j = 2, k = 0; k < sets.size(); k++) { RRset rrset = sets.get(k); int type = rrset.getType(); if (type == Type.SOA) { current[0] = rrset; } else if (type == Type.NS) { current[1] = rrset; } else { current[j++] = rrset; } } }
/** * Verify the generated signatures. * * @param zonename the origin name of the zone. * @param records a list of {@link org.xbill.DNS.Record}s. * @param keypairs a list of keypairs used the sign the zone. * @return true if all of the signatures validated. */ private static boolean verifySigs( Name zonename, List<Record> records, List<DnsKeyPair> keypairs) { boolean secure = true; DnsSecVerifier verifier = new DnsSecVerifier(); for (DnsKeyPair pair : keypairs) { verifier.addTrustedKey(pair); } verifier.setVerifyAllSigs(true); List<RRset> rrsets = SignUtils.assembleIntoRRsets(records); for (RRset rrset : rrsets) { // skip unsigned rrsets. if (!rrset.sigs().hasNext()) continue; int result = verifier.verify(rrset, null); if (result != DNSSEC.Secure) { log.fine("Signatures did not verify for RRset: (" + result + "): " + rrset); secure = false; } } return secure; }
private static RRset createRRset(Record... records) { RRset rrset = new RRset(); for (Record r : records) { rrset.addRR(r); } return rrset; }
/** * 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 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; }
@SuppressWarnings("unchecked") public void execute() throws Exception { // Read in the zone List<Record> records = ZoneUtils.readZoneFile(state.inputfile, null); if (records == null || records.size() == 0) { System.err.println("error: empty RRset file"); state.usage(); } // Construct the RRset. Complain if the records in the input file // consist of more than one RRset. RRset rrset = null; for (Record r : records) { // skip RRSIGs if (r.getType() == Type.RRSIG || r.getType() == Type.SIG) { continue; } // Handle the first record. if (rrset == null) { rrset = new RRset(); rrset.addRR(r); continue; } // Ensure that the remaining records all belong to the same rrset. if (rrset.getName().equals(r.getName()) && rrset.getType() == r.getType() && rrset.getDClass() == r.getDClass()) { rrset.addRR(r); } else { System.err.println("Records do not all belong to the same RRset."); state.usage(); } } if (rrset.size() == 0) { System.err.println("No records found in inputfile."); state.usage(); } // Load the key pairs. if (state.keyFiles.length == 0) { System.err.println("error: at least one keyfile must be specified"); state.usage(); } List<DnsKeyPair> keypairs = getKeys(state.keyFiles, 0, state.keyDirectory); // Make sure that all the keypairs have the same name. // This will be used as the zone name, too. Name keysetName = null; for (DnsKeyPair pair : keypairs) { if (keysetName == null) { keysetName = pair.getDNSKEYName(); continue; } if (!pair.getDNSKEYName().equals(keysetName)) { System.err.println("Keys do not all have the same name."); state.usage(); } } // default the output file, if not set. if (state.outputfile == null && !state.inputfile.equals("-")) { state.outputfile = state.inputfile + ".signed"; } JCEDnsSecSigner signer = new JCEDnsSecSigner(); List<RRSIGRecord> sigs = signer.signRRset(rrset, keypairs, state.start, state.expire); for (RRSIGRecord s : sigs) { rrset.addRR(s); } // write out the signed RRset List<Record> signed_records = new ArrayList<Record>(); for (Iterator<Record> i = rrset.rrs(); i.hasNext(); ) { signed_records.add(i.next()); } for (Iterator<Record> i = rrset.sigs(); i.hasNext(); ) { signed_records.add(i.next()); } // write out the signed zone ZoneUtils.writeZoneFile(signed_records, state.outputfile); if (state.verifySigs) { log.fine("verifying generated signatures"); boolean res = verifySigs(keysetName, signed_records, keypairs); if (res) { System.out.println("Generated signatures verified"); // log.info("Generated signatures verified"); } else { System.out.println("Generated signatures did not verify."); // log.warn("Generated signatures did not verify."); } } }
/** * 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 */ @SuppressWarnings("unused") 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; 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; }
/** {@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 question = request.getQuestion(); if (question == null || question.getDClass() != DClass.IN) { throw new DNSException(DNSError.newError(Rcode.NOTIMP)); } Record queryRecord = request.getQuestion(); Name name = queryRecord.getName(); int type = queryRecord.getType(); if (LOGGER.isDebugEnabled()) { StringBuilder builder = new StringBuilder("Recieved Query Request:"); builder.append("\r\n\tName: " + name.toString()); builder.append("\r\n\tType: " + type); builder.append("\r\n\tDClass: " + queryRecord.getDClass()); LOGGER.debug(builder.toString()); } Collection<Record> lookupRecords = null; switch (question.getType()) { 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<Record>(); Iterator<Record> 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<Record>(); Iterator<Record> iter = set.rrs(); while (iter.hasNext()) lookupRecords.add(iter.next()); } break; } case Type.ANY: { Collection<Record> genRecs = processGenericANYRecordRequest(name.toString()); RRset certRecs = processCERTRecordRequest(name.toString()); if (genRecs != null || certRecs != null) { lookupRecords = new ArrayList<Record>(); if (genRecs != null) lookupRecords.addAll(genRecs); if (certRecs != null) { Iterator<Record> iter = certRecs.rrs(); while (iter.hasNext()) lookupRecords.add(iter.next()); } } break; } default: { LOGGER.debug("Query Type " + type + " not implemented"); throw new DNSException( DNSError.newError(Rcode.NOTIMP), "Query Type " + type + " 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<Record> 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; }
@Override public DnsResponse lookupRecords(final Record query) { final Name name = query.getName(); final int type = query.getType(); final Cache cache = new Cache(); Lookup aLookup = new Lookup(name, type); aLookup.setCache(cache); Record[] found = aLookup.run(); List<Record> queriedrrs = Arrays.asList(found != null ? found : new Record[] {}); List<Name> cnames = (List<Name>) (aLookup.getAliases().length > 0 ? Arrays.asList(aLookup.getAliases()) : Lists.newArrayList()); List<Record> answer = Lists.newArrayList(); List<Record> authority = Lists.newArrayList(); List<Record> additional = Lists.newArrayList(); for (Name cnameRec : cnames) { SetResponse sr = cache.lookupRecords(cnameRec, Type.CNAME, Credibility.ANY); if (sr != null && sr.isSuccessful() && sr.answers() != null) { for (RRset result : sr.answers()) { Iterator rrs = result.rrs(false); if (rrs != null) { for (Object record : ImmutableSet.copyOf(rrs)) { answer.add((Record) record); } } } } } for (Record queriedRec : queriedrrs) { SetResponse sr = cache.lookupRecords(queriedRec.getName(), queriedRec.getType(), Credibility.ANY); if (sr != null && sr.isSuccessful() && sr.answers() != null) { for (RRset result : sr.answers()) { Iterator rrs = result.rrs(false); if (rrs != null) { for (Object record : ImmutableSet.copyOf(rrs)) { answer.add((Record) record); } } } } } for (Record aRec : queriedrrs) { List<Record> nsRecs = lookupNSRecords(aRec.getName(), cache); for (Record nsRec : nsRecs) { authority.add(nsRec); Lookup nsLookup = new Lookup(((NSRecord) nsRec).getTarget(), Type.A); nsLookup.setCache(cache); Record[] nsAnswers = nsLookup.run(); if (nsAnswers != null) { additional.addAll(Arrays.asList(nsAnswers)); } } } return DnsResponse.forName(query.getName()) .recursive() .withAuthority(authority) .withAdditional(additional) .answer(answer); }