public SIGBase( Name name, int type, int dclass, long ttl, int covered, int alg, long origttl, Date expire, Date timeSigned, int footprint, Name signer, byte[] signature) { super(name, type, dclass, ttl); Type.check(covered); checkU8("alg", alg); checkU8("labels", labels); TTL.check(origttl); checkU16("footprint", footprint); this.covered = covered; this.alg = alg; this.labels = name.labels(); this.origttl = origttl; this.expire = expire; this.timeSigned = timeSigned; this.footprint = footprint; if (!signer.isAbsolute()) throw new RelativeNameException(signer); this.signer = signer; this.signature = signature; }
/** * Builds a new Record from its textual representation * * @param name The owner name of the record. * @param type The record's type. * @param dclass The record's class. * @param ttl The record's time to live. * @param st A tokenizer containing the textual representation of the rdata. * @param origin The default origin to be appended to relative domain names. * @return The new record * @throws IOException The text format was invalid. */ public static Record fromString( Name name, int type, int dclass, long ttl, Tokenizer st, Name origin) throws IOException { Record rec; if (!name.isAbsolute()) throw new RelativeNameException(name); Type.check(type); DClass.check(dclass); TTL.check(ttl); Tokenizer.Token t = st.get(); if (t.type == Tokenizer.IDENTIFIER && t.value.equals("\\#")) { int length = st.getUInt16(); byte[] data = st.getHex(); if (data == null) { data = new byte[0]; } if (length != data.length) throw st.exception("invalid unknown RR encoding: " + "length mismatch"); DNSInput in = new DNSInput(data); return newRecord(name, type, dclass, ttl, length, in); } st.unget(); rec = getEmptyRecord(name, type, dclass, ttl, true); rec.rdataFromString(st, origin); t = st.get(); if (t.type != Tokenizer.EOL && t.type != Tokenizer.EOF) { throw st.exception("unexpected tokens at end of record"); } return rec; }
/** * Determines if two Records are identical. This compares the name, type, class, and rdata (with * names canonicalized). The TTLs are not compared. * * @param arg The record to compare to * @return true if the records are equal, false otherwise. */ public boolean equals(Object arg) { if (arg == null || !(arg instanceof Record)) return false; Record r = (Record) arg; if (type != r.type || dclass != r.dclass || !name.equals(r.name)) return false; byte[] array1 = rdataToWireCanonical(); byte[] array2 = r.rdataToWireCanonical(); return Arrays.equals(array1, array2); }
/** * Creates a new empty record, with the given parameters. * * @param name The owner name of the record. * @param type The record's type. * @param dclass The record's class. * @param ttl The record's time to live. * @return An object of a subclass of Record */ public static Record newRecord(Name name, int type, int dclass, long ttl) { if (!name.isAbsolute()) throw new RelativeNameException(name); Type.check(type); DClass.check(dclass); TTL.check(ttl); return getEmptyRecord(name, type, dclass, ttl, false); }
Record(Name name, int type, int dclass, long ttl) { if (!name.isAbsolute()) throw new RelativeNameException(name); Type.check(type); DClass.check(dclass); TTL.check(ttl); this.name = name; this.type = type; this.dclass = dclass; this.ttl = ttl; }
/** * Creates an array containing fields of the SIG record and the RRsets to be signed/verified. * * @param sig The SIG record used to sign/verify the rrset. * @param rrset The data to be signed/verified. * @return The data to be cryptographically signed or verified. */ public static byte[] digestRRset(SIGRecord sig, RRset rrset) { DataByteOutputStream out = new DataByteOutputStream(); digestSIG(out, sig); int size = rrset.size(); byte[][] records = new byte[size][]; Iterator it = rrset.rrs(); Name name = rrset.getName(); Name wild = null; if (name.labels() > sig.getLabels()) wild = name.wild(name.labels() - sig.getLabels()); while (it.hasNext()) { Record rec = (Record) it.next(); if (wild != null) rec = rec.withName(wild); records[--size] = rec.toWireCanonical(); } Arrays.sort(records); for (int i = 0; i < records.length; i++) out.writeArray(records[i]); return out.toByteArray(); }
void rrToWire(DataByteOutputStream out, Compression c, boolean canonical) { if (signature == null) return; out.writeShort(covered); out.writeByte(alg); out.writeByte(labels); out.writeInt(origttl); out.writeInt((int) (expire.getTime() / 1000)); out.writeInt((int) (timeSigned.getTime() / 1000)); out.writeShort(footprint); signer.toWire(out, null, canonical); out.writeArray(signature); }
Record rdataFromString(Name name, short dclass, int ttl, MyStringTokenizer st, Name origin) throws TextParseException { SIGRecord rec = new SIGRecord(name, dclass, ttl); rec.covered = Type.value(st.nextToken()); rec.alg = Byte.parseByte(st.nextToken()); rec.labels = Byte.parseByte(st.nextToken()); rec.origttl = TTL.parseTTL(st.nextToken()); rec.expire = parseDate(st.nextToken()); rec.timeSigned = parseDate(st.nextToken()); rec.footprint = (short) Integer.parseInt(st.nextToken()); rec.signer = Name.fromString(st.nextToken(), origin); if (st.hasMoreTokens()) rec.signature = base64.fromString(st.remainingTokens()); return rec; }
void toWire(DNSOutput out, int section, Compression c) { name.toWire(out, c); out.writeU16(type); out.writeU16(dclass); if (section == Section.QUESTION) return; out.writeU32(ttl); int lengthPosition = out.current(); out.writeU16(0); /* until we know better */ rrToWire(out, c, false); int rrlength = out.current() - lengthPosition - 2; out.save(); out.jump(lengthPosition); out.writeU16(rrlength); out.restore(); }
/** * Creates a new record, with the given parameters. * * @param name The owner name of the record. * @param type The record's type. * @param dclass The record's class. * @param ttl The record's time to live. * @param length The length of the record's data. * @param data The rdata of the record, in uncompressed DNS wire format. Only the first length * bytes are used. */ public static Record newRecord( Name name, int type, int dclass, long ttl, int length, byte[] data) { if (!name.isAbsolute()) throw new RelativeNameException(name); Type.check(type); DClass.check(dclass); TTL.check(ttl); DNSInput in; if (data != null) in = new DNSInput(data); else in = null; try { return newRecord(name, type, dclass, ttl, length, in); } catch (IOException e) { return null; } }
private void toWireCanonical(DNSOutput out, boolean noTTL) { name.toWireCanonical(out); out.writeU16(type); out.writeU16(dclass); if (noTTL) { out.writeU32(0); } else { out.writeU32(ttl); } int lengthPosition = out.current(); out.writeU16(0); /* until we know better */ rrToWire(out, null, true); int rrlength = out.current() - lengthPosition - 2; out.save(); out.jump(lengthPosition); out.writeU16(rrlength); out.restore(); }
/** * Compares this Record to another Object. * * @param o The Object to be compared. * @return The value 0 if the argument is a record equivalent to this record; a value less than 0 * if the argument is less than this record in the canonical ordering, and a value greater * than 0 if the argument is greater than this record in the canonical ordering. The canonical * ordering is defined to compare by name, class, type, and rdata. * @throws ClassCastException if the argument is not a Record. */ public int compareTo(Object o) { Record arg = (Record) o; if (this == arg) return (0); int n = name.compareTo(arg.name); if (n != 0) return (n); n = dclass - arg.dclass; if (n != 0) return (n); n = type - arg.type; if (n != 0) return (n); byte[] rdata1 = rdataToWireCanonical(); byte[] rdata2 = arg.rdataToWireCanonical(); for (int i = 0; i < rdata1.length && i < rdata2.length; i++) { n = (rdata1[i] & 0xFF) - (rdata2[i] & 0xFF); if (n != 0) return (n); } return (rdata1.length - rdata2.length); }
/** * Creates an SIG Record from the given data * * @param covered The RRset type covered by this signature * @param alg The cryptographic algorithm of the key that generated the signature * @param origttl The original TTL of the RRset * @param expire The time at which the signature expires * @param timeSigned The time at which this signature was generated * @param footprint The footprint/key id of the signing key. * @param signer The owner of the signing key * @param signature Binary data representing the signature */ public SIGRecord( Name name, short dclass, int ttl, int covered, int alg, int origttl, Date expire, Date timeSigned, int footprint, Name signer, byte[] signature) { this(name, dclass, ttl); this.covered = (short) covered; this.alg = (byte) alg; this.labels = name.labels(); this.origttl = origttl; this.expire = expire; this.timeSigned = timeSigned; this.footprint = (short) footprint; this.signer = signer; this.signature = signature; }
/* Checks that a name is absolute */ static Name checkName(String field, Name name) { if (!name.isAbsolute()) throw new RelativeNameException(name); return name; }
/** * Creates a new record identical to the current record, but with a different name. This is most * useful for replacing the name of a wildcard record. */ public Record withName(Name name) { if (!name.isAbsolute()) throw new RelativeNameException(name); Record rec = cloneRecord(); rec.name = name; return rec; }
/** * Adds all data from a Message into the Cache. Each record is added with the appropriate * credibility, and negative answers are cached as such. * * @param in The Message to be added * @see Message */ public void addMessage(Message in) { boolean isAuth = in.getHeader().getFlag(Flags.AA); Name qname = in.getQuestion().getName(); Name curname = qname; short qtype = in.getQuestion().getType(); short qclass = in.getQuestion().getDClass(); byte cred; short rcode = in.getHeader().getRcode(); boolean haveAnswer = false; boolean completed = false; boolean restart = false; RRset[] answers, auth, addl; if (rcode != Rcode.NOERROR && rcode != Rcode.NXDOMAIN) return; if (secure) { Cache c = new Cache(dclass); c.addMessage(in); verifyRecords(c); return; } answers = in.getSectionRRsets(Section.ANSWER); for (int i = 0; i < answers.length; i++) { if (answers[i].getDClass() != qclass) continue; short type = answers[i].getType(); Name name = answers[i].getName(); cred = getCred(Section.ANSWER, isAuth); if (type == Type.CNAME && name.equals(curname)) { CNAMERecord cname; addRRset(answers[i], cred); cname = (CNAMERecord) answers[i].first(); curname = cname.getTarget(); restart = true; haveAnswer = true; } else if (type == Type.DNAME && curname.subdomain(name)) { DNAMERecord dname; addRRset(answers[i], cred); dname = (DNAMERecord) answers[i].first(); try { curname = curname.fromDNAME(dname); } catch (NameTooLongException e) { break; } restart = true; haveAnswer = true; } else if ((type == qtype || qtype == Type.ANY) && name.equals(curname)) { addRRset(answers[i], cred); completed = true; haveAnswer = true; } if (restart) { restart = false; i = 0; } } auth = in.getSectionRRsets(Section.AUTHORITY); if (!completed) { /* This is a negative response or a referral. */ RRset soa = null, ns = null; for (int i = 0; i < auth.length; i++) { if (auth[i].getType() == Type.SOA && curname.subdomain(auth[i].getName())) soa = auth[i]; else if (auth[i].getType() == Type.NS && curname.subdomain(auth[i].getName())) ns = auth[i]; } short cachetype = (rcode == Rcode.NXDOMAIN) ? (short) 0 : qtype; if (soa != null || ns == null) { /* Negative response */ cred = getCred(Section.AUTHORITY, isAuth); SOARecord soarec = null; if (soa != null) soarec = (SOARecord) soa.first(); addNegative(curname, cachetype, soarec, cred); /* NXT records are not cached yet. */ } else { /* Referral response */ cred = getCred(Section.AUTHORITY, isAuth); addRRset(ns, cred); } } addl = in.getSectionRRsets(Section.ADDITIONAL); for (int i = 0; i < addl.length; i++) { short type = addl[i].getType(); if (type != Type.A && type != Type.AAAA && type != Type.A6) continue; /* XXX check the name */ Name name = addl[i].getName(); cred = getCred(Section.ADDITIONAL, isAuth); addRRset(addl[i], cred); } }
/** Finds all matching sets or something that causes the lookup to stop. */ protected Object findSets(Name name, short type) { Object bestns = null; Object o; Name tname; int labels; int olabels; int tlabels; if (!name.subdomain(origin)) return null; labels = name.labels(); olabels = origin.labels(); for (tlabels = olabels; tlabels <= labels; tlabels++) { if (tlabels == olabels) tname = origin; else if (tlabels == labels) tname = name; else tname = new Name(name, labels - tlabels); TypeMap nameInfo = findName(tname); if (nameInfo == null) continue; /* If this is an ANY lookup, return everything. */ if (tlabels == labels && type == Type.ANY) return nameInfo.getAll(); /* Look for an NS */ if (tlabels > olabels || isCache) { o = nameInfo.get(Type.NS); if (o != null) { if (isCache) bestns = o; else return o; } } /* If this is the name, look for the actual type. */ if (tlabels == labels) { o = nameInfo.get(type); if (o != null) return o; } /* If this is the name, look for a CNAME */ if (tlabels == labels) { o = nameInfo.get(Type.CNAME); if (o != null) return o; } /* Look for a DNAME, unless this is the actual name */ if (tlabels < labels) { o = nameInfo.get(Type.DNAME); if (o != null) return o; } /* * If this is the name and this is a cache, look for an * NXDOMAIN entry. */ if (tlabels == labels && isCache) { o = nameInfo.get((short) 0); if (o != null) return o; } /* * If this is the name and we haven't matched anything, * just return the name. */ if (tlabels == labels) return nameInfo; } if (bestns == null) return null; else return bestns; }
/** * Determines if two Records could be part of the same RRset. This compares the name, type, and * class of the Records; the ttl and rdata are not compared. */ public boolean sameRRset(Record rec) { return (getRRsetType() == rec.getRRsetType() && dclass == rec.dclass && name.equals(rec.name)); }
/** * Looks up Records in the Cache. This follows CNAMEs and handles negatively cached data. * * @param name The name to look up * @param type The type to look up * @param minCred The minimum acceptable credibility * @return A SetResponse object * @see SetResponse * @see Credibility */ public SetResponse lookupRecords(Name name, short type, byte minCred) { SetResponse cr = null; boolean verbose = Options.check("verbosecache"); Object o = lookup(name, type); if (verbose) logLookup(name, type, "Starting"); if (o == null || o == NXRRSET) { /* * The name exists, but the type was not found. Or, the * name does not exist and no parent does either. Punt. */ if (verbose) logLookup(name, type, "no information found"); return SetResponse.ofType(SetResponse.UNKNOWN); } Object[] objects; if (o instanceof Element) objects = new Object[] {o}; else objects = (Object[]) o; int nelements = 0; for (int i = 0; i < objects.length; i++) { Element element = (Element) objects[i]; if (element.expired()) { if (verbose) { logLookup(name, type, element.toString()); logLookup(name, type, "expired: ignoring"); } removeSet(name, type, element); objects[i] = null; } else if (element.credibility < minCred) { if (verbose) { logLookup(name, type, element.toString()); logLookup(name, type, "not credible: ignoring"); } objects[i] = null; } else { nelements++; } } if (nelements == 0) { /* We have data, but can't use it. Punt. */ if (verbose) logLookup(name, type, "no useful data found"); return SetResponse.ofType(SetResponse.UNKNOWN); } /* * We have something at the name. It could be the answer, * a CNAME, DNAME, or NS, or a negative cache entry. * * Ignore wildcards, since it's pretty unlikely that any will be * cached. The occasional extra query is easily balanced by the * reduced number of lookups. */ for (int i = 0; i < objects.length; i++) { if (objects[i] == null) continue; Element element = (Element) objects[i]; if (verbose) logLookup(name, type, element.toString()); RRset rrset = null; if (element instanceof PositiveElement) rrset = ((PositiveElement) element).rrset; /* Is this a negatively cached entry? */ if (rrset == null) { /* * If this is an NXDOMAIN entry, return NXDOMAIN. */ if (element.getType() == 0) { if (verbose) logLookup(name, type, "NXDOMAIN"); return SetResponse.ofType(SetResponse.NXDOMAIN); } /* * If we're not looking for type ANY, return NXRRSET. * Otherwise ignore this. */ if (type != Type.ANY) { if (verbose) logLookup(name, type, "NXRRSET"); return SetResponse.ofType(SetResponse.NXRRSET); } else { if (verbose) logLookup(name, type, "ANY query; " + "ignoring NXRRSET"); continue; } } short rtype = rrset.getType(); Name rname = rrset.getName(); if (name.equals(rname)) { if (type != Type.CNAME && type != Type.ANY && rtype == Type.CNAME) { if (verbose) logLookup(name, type, "cname"); return new SetResponse(SetResponse.CNAME, rrset); } else if (type != Type.NS && type != Type.ANY && rtype == Type.NS) { if (verbose) logLookup(name, type, "exact delegation"); return new SetResponse(SetResponse.DELEGATION, rrset); } else { if (verbose) logLookup(name, type, "exact match"); if (cr == null) cr = new SetResponse(SetResponse.SUCCESSFUL); cr.addRRset(rrset); } } else if (name.subdomain(rname)) { if (rtype == Type.DNAME) { if (verbose) logLookup(name, type, "dname"); return new SetResponse(SetResponse.DNAME, rrset); } else if (rtype == Type.NS) { if (verbose) logLookup(name, type, "parent delegation"); return new SetResponse(SetResponse.DELEGATION, rrset); } else { if (verbose) logLookup(name, type, "ignoring rrset (" + rname + " " + Type.string(rtype) + ")"); } } else { if (verbose) logLookup(name, type, "ignoring rrset (" + rname + " " + Type.string(rtype) + ")"); } } /* * As far as I can tell, the only legitimate time cr will be null is * if we queried for ANY and only saw negative responses, but not an * NXDOMAIN. Return UNKNOWN. */ if (cr == null && type == Type.ANY) return SetResponse.ofType(SetResponse.UNKNOWN); else if (cr == null) throw new IllegalStateException( "looking up (" + name + " " + Type.string(type) + "): " + "cr == null."); return cr; }