/** Converts the RRSIG/SIG Record to a String */ public String rdataToString() { StringBuffer sb = new StringBuffer(); if (signature != null) { sb.append(Type.string(covered)); sb.append(" "); sb.append(alg); sb.append(" "); sb.append(labels); sb.append(" "); sb.append(origttl); sb.append(" "); if (Options.check("multiline")) sb.append("(\n\t"); sb.append(FormattedTime.format(expire)); sb.append(" "); sb.append(FormattedTime.format(timeSigned)); sb.append(" "); sb.append(footprint); sb.append(" "); sb.append(signer); if (Options.check("multiline")) { sb.append("\n"); sb.append(base64.formatString(signature, 64, "\t", true)); } else { sb.append(" "); sb.append(base64.toString(signature)); } } return sb.toString(); }
private Message parseMessage(byte[] b) throws WireParseException { try { return (new Message(b)); } catch (IOException e) { if (Options.check("verbose")) e.printStackTrace(); if (!(e instanceof WireParseException)) e = new WireParseException("Error parsing message"); throw (WireParseException) e; } }
private static void addSearch(String search, List list) { Name name; if (Options.check("verbose")) System.out.println("adding search " + search); try { name = Name.fromString(search, Name.root); } catch (TextParseException e) { return; } if (list.contains(name)) return; list.add(name); }
/** Converts a Record into a String representation */ public String toString() { StringBuffer sb = new StringBuffer(); sb.append(name); if (sb.length() < 8) sb.append("\t"); if (sb.length() < 16) sb.append("\t"); sb.append("\t"); if (Options.check("BINDTTL")) sb.append(TTL.format(ttl)); else sb.append(ttl); sb.append("\t"); if (dclass != DClass.IN || !Options.check("noPrintIN")) { sb.append(DClass.string(dclass)); sb.append("\t"); } sb.append(Type.string(type)); String rdata = rrToString(); if (!rdata.equals("")) { sb.append("\t"); sb.append(rdata); } return sb.toString(); }
/* * Receive an exception. If the resolution has been completed, * do nothing. Otherwise make progress. */ public void handleException(Object id, Exception e) { if (Options.check("verbose")) System.err.println("ExtendedResolver: got " + e); synchronized (this) { outstanding--; if (done) return; int n; for (n = 0; n < inprogress.length; n++) if (inprogress[n] == id) break; /* If we don't know what this is, do nothing. */ if (n == inprogress.length) return; boolean startnext = false; boolean waiting = false; /* * If this is the first response from server n, * we should start sending queries to server n + 1. */ if (sent[n] == 1 && n < resolvers.length - 1) startnext = true; if (e instanceof InterruptedIOException) { /* Got a timeout; resend */ if (sent[n] < retries) send(n); if (thrown == null) thrown = e; } else if (e instanceof SocketException) { /* * Problem with the socket; don't resend * on it */ if (thrown == null || thrown instanceof InterruptedIOException) thrown = e; } else { /* * Problem with the response; don't resend * on the same socket. */ thrown = e; } if (done) return; if (startnext) send(n + 1); if (done) return; if (outstanding == 0) { /* * If we're done and this is synchronous, * wake up the blocking thread. */ done = true; if (listener == null) { notifyAll(); return; } } if (!done) return; } /* If we're done and this is asynchronous, call the callback. */ if (!(thrown instanceof Exception)) thrown = new RuntimeException(thrown.getMessage()); listener.handleException(this, (Exception) thrown); }
/* * Receive a response. If the resolution hasn't been completed, * either wake up the blocking thread or call the callback. */ public void receiveMessage(Object id, Message m) { if (Options.check("verbose")) System.err.println("ExtendedResolver: " + "received message"); synchronized (this) { if (done) return; response = m; done = true; if (listener == null) { notifyAll(); return; } } listener.receiveMessage(this, response); }
/** * Converts the generate specification to a string containing the corresponding $GENERATE * statement. */ public String toString() { StringBuffer sb = new StringBuffer(); sb.append("$GENERATE "); sb.append(start + "-" + end); if (step > 1) sb.append("/" + step); sb.append(" "); sb.append(namePattern + " "); sb.append(ttl + " "); if (dclass != DClass.IN || !Options.check("noPrintIN")) sb.append(DClass.string(dclass) + " "); sb.append(Type.string(type) + " "); sb.append(rdataPattern + " "); return sb.toString(); }
private static final Record getTypedObject(int type) { if (type < 0 || type > knownRecords.length) return unknownRecord.getObject(); if (knownRecords[type] != null) return knownRecords[type]; /* Construct the class name by putting the type before "Record". */ String s = Record.class.getPackage().getName() + "." + Type.string(type).replace('-', '_') + "Record"; try { Class c = Class.forName(s); Constructor m = c.getDeclaredConstructor(emptyClassArray); knownRecords[type] = (Record) m.newInstance(emptyObjectArray); } catch (ClassNotFoundException e) { /* This is normal; do nothing */ } catch (Exception e) { if (Options.check("verbose")) System.err.println(e); } if (knownRecords[type] == null) knownRecords[type] = unknownRecord.getObject(); return knownRecords[type]; }
/** Converts to a String */ public String toString() { StringBuffer sb = toStringNoData(); if (key != null || (flags & (FLAG_NOKEY)) == (FLAG_NOKEY)) { if (!Options.check("nohex")) { sb.append("0x"); sb.append(Integer.toHexString(flags & 0xFFFF)); } else sb.append(flags & 0xFFFF); sb.append(" "); sb.append(proto); sb.append(" "); sb.append(alg); if (key != null) { sb.append(" (\n"); sb.append(base64.formatString(key, 64, "\t", true)); sb.append(" ; key_tag= "); sb.append(getFootprint()); } } return sb.toString(); }
/** * Creates a Zone by performing a zone transfer to the specified host. All records that do not * belong in the Zone are added to the specified Cache. * * @see Cache * @see Master */ public Zone(Name zone, short dclass, String remote, Cache cache) throws IOException { super(false); origin = zone; this.dclass = dclass; type = SECONDARY; Resolver res = new SimpleResolver(remote); Record rec = Record.newRecord(zone, Type.AXFR, dclass); Message query = Message.newQuery(rec); Message response = res.send(query); Record[] recs = response.getSectionArray(Section.ANSWER); for (int i = 0; i < recs.length; i++) { if (!recs[i].getName().subdomain(origin)) { if (Options.check("verbose")) System.err.println(recs[i].getName() + "is not in zone " + origin); continue; } addRecord(recs[i]); } if (cache != null) { recs = response.getSectionArray(Section.ADDITIONAL); for (int i = 0; i < recs.length; i++) cache.addRecord(recs[i], Credibility.GLUE, recs); } validate(); }
/** * 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; }
private static void addServer(String server, List list) { if (list.contains(server)) return; if (Options.check("verbose")) System.out.println("adding server " + server); list.add(server); }
/** * Sends a message to a single server and waits for a response. No checking is done to ensure that * the response is associated with the query. * * @param query The query to send. * @return The response. * @throws IOException An error occurred while sending or receiving. */ public Message send(Message query) throws IOException { if (Options.check("verbose")) System.err.println( "Sending to " + address.getAddress().getHostAddress() + ":" + address.getPort()); if (query.getHeader().getOpcode() == Opcode.QUERY) { Record question = query.getQuestion(); if (question != null && question.getType() == Type.AXFR) return sendAXFR(query); } query = (Message) query.clone(); applyEDNS(query); if (tsig != null) { tsig.apply(query, null); } byte[] out = query.toWire(Message.MAXLENGTH); int udpSize = maxUDPSize(query); boolean tcp = false; long endTime = System.currentTimeMillis() + timeoutValue; do { byte[] b_in; if (useTCP || out.length > udpSize) { tcp = true; } if (tcp) { b_in = TCPClient.sendrecv(localAddress, address, out, endTime); } else { b_in = UDPClient.sendrecv(localAddress, address, out, udpSize, endTime); } /* * Check that the response is long enough. */ if (b_in.length < Header.LENGTH) { throw new WireParseException("invalid DNS header - " + "too short"); } /* * Check that the response ID matches the query ID. We want * to check this before actually parsing the message, so that * if there's a malformed response that's not ours, it * doesn't confuse us. */ int id = ((b_in[0] & 0xFF) << 8) + (b_in[1] & 0xFF); int qid = query.getHeader().getID(); if (id != qid) { String error = "invalid message id: expected " + qid + "; got id " + id; if (tcp) { throw new WireParseException(error); } else { if (Options.check("verbose")) { System.err.println(error); } continue; } } Message response = parseMessage(b_in); verifyTSIG(query, response, b_in, tsig); if (!tcp && !ignoreTruncation && response.getHeader().getFlag(Flags.TC)) { tcp = true; continue; } return response; } while (true); }
private void verifyTSIG(Message query, Message response, byte[] b, TSIG tsig) { if (tsig == null) return; int error = tsig.verify(response, b, query.getTSIG()); if (Options.check("verbose")) System.err.println("TSIG verify: " + Rcode.TSIGstring(error)); }