Beispiel #1
0
 Message(DNSInput in) throws IOException {
   this(new Header(in));
   boolean isUpdate = (header.getOpcode() == Opcode.UPDATE);
   boolean truncated = header.getFlag(Flags.TC);
   try {
     for (int i = 0; i < 4; i++) {
       int count = header.getCount(i);
       if (count > 0) sections[i] = new ArrayList(count);
       for (int j = 0; j < count; j++) {
         int pos = in.current();
         Record rec = Record.fromWire(in, i, isUpdate);
         sections[i].add(rec);
         if (i == Section.ADDITIONAL) {
           if (rec.getType() == Type.TSIG) tsigstart = pos;
           if (rec.getType() == Type.SIG) {
             SIGRecord sig = (SIGRecord) rec;
             if (sig.getTypeCovered() == 0) sig0start = pos;
           }
         }
       }
     }
   } catch (WireParseException e) {
     if (!truncated) throw e;
   }
   size = in.current();
 }
Beispiel #2
0
  private Record parseRR(MyStringTokenizer st, boolean useLast, Record last, Name origin)
      throws IOException {
    Name name;
    int ttl;
    short type, dclass;

    if (!useLast) name = new Name(st.nextToken(), origin);
    else name = last.getName();

    String s = st.nextToken();

    try {
      ttl = TTL.parseTTL(s);
      s = st.nextToken();
    } catch (NumberFormatException e) {
      if (!useLast || last == null) ttl = defaultTTL;
      else ttl = last.getTTL();
    }

    if ((dclass = DClass.value(s)) > 0) s = st.nextToken();
    else dclass = DClass.IN;

    if ((type = Type.value(s)) < 0) throw new IOException("Parse error");

    return Record.fromString(name, type, dclass, ttl, st, origin);
  }
Beispiel #3
0
 /**
  * Adds a record to the Zone
  *
  * @param r The record to be added
  * @see Record
  */
 public void addRecord(Record r) {
   Name name = r.getName();
   short type = r.getRRsetType();
   RRset rrset = (RRset) findExactSet(name, type);
   if (rrset == null) addSet(name, type, rrset = new RRset());
   rrset.addRR(r);
 }
Beispiel #4
0
  /* Returns the number of records not successfully rendered. */
  private int sectionToWire(DNSOutput out, int section, Compression c, int maxLength) {
    int n = sections[section].size();
    int pos = out.current();
    int rendered = 0;
    int skipped = 0;
    Record lastrec = null;

    for (int i = 0; i < n; i++) {
      Record rec = (Record) sections[section].get(i);
      if (section == Section.ADDITIONAL && rec instanceof OPTRecord) {
        skipped++;
        continue;
      }

      if (lastrec != null && !sameSet(rec, lastrec)) {
        pos = out.current();
        rendered = i;
      }
      lastrec = rec;
      rec.toWire(out, section, c);
      if (out.current() > maxLength) {
        out.jump(pos);
        return n - rendered + skipped;
      }
    }
    return skipped;
  }
  /**
   * 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);
 }
Beispiel #7
0
 /**
  * Determines if an RRset with the given name and type is already present in the given section.
  *
  * @see RRset
  * @see Section
  */
 public boolean findRRset(Name name, int type, int section) {
   if (sections[section] == null) return false;
   for (int i = 0; i < sections[section].size(); i++) {
     Record r = (Record) sections[section].get(i);
     if (r.getType() == type && name.equals(r.getName())) return true;
   }
   return false;
 }
Beispiel #8
0
  private final void maybeAddRecord(Record record) throws IOException {
    int rtype = record.getType();
    Name name = record.getName();

    if (rtype == Type.SOA && !name.equals(origin)) {
      throw new IOException("SOA owner " + name + " does not match zone origin " + origin);
    }
    if (name.subdomain(origin)) addRecord(record);
  }
Beispiel #9
0
 /**
  * Removes a record from the Zone
  *
  * @param r The record to be removed
  * @see Record
  */
 public void removeRecord(Record r) {
   Name name = r.getName();
   short type = r.getRRsetType();
   RRset rrset = (RRset) findExactSet(name, type);
   if (rrset != null) {
     rrset.deleteRR(r);
     if (rrset.size() == 0) removeSet(name, type, rrset);
   }
 }
Beispiel #10
0
 /**
  * Removes a record from the Zone
  *
  * @param r The record to be removed
  * @see Record
  */
 public void removeRecord(Record r) {
   Name name = r.getName();
   int rtype = r.getRRsetType();
   synchronized (this) {
     RRset rrset = findRRset(name, rtype);
     if (rrset == null) return;
     rrset.deleteRR(r);
     if (rrset.size() == 0) removeRRset(name, rtype);
   }
 }
 private static final Record getEmptyRecord(
     Name name, int type, int dclass, long ttl, boolean hasData) {
   Record rec;
   if (hasData) rec = getTypedObject(type).getObject();
   else rec = new EmptyRecord();
   rec.name = name;
   rec.type = type;
   rec.dclass = dclass;
   rec.ttl = ttl;
   return rec;
 }
Beispiel #12
0
 void toWire(DNSOutput out) {
   header.toWire(out);
   Compression c = new Compression();
   for (int i = 0; i < 4; i++) {
     if (sections[i] == null) continue;
     for (int j = 0; j < sections[i].size(); j++) {
       Record rec = (Record) sections[i].get(j);
       rec.toWire(out, i, c);
     }
   }
 }
Beispiel #13
0
 /**
  * Adds a Record to the Zone
  *
  * @param r The record to be added
  * @see Record
  */
 public void addRecord(Record r) {
   Name name = r.getName();
   int rtype = r.getRRsetType();
   synchronized (this) {
     RRset rrset = findRRset(name, rtype);
     if (rrset == null) {
       rrset = new RRset(r);
       addRRset(name, rrset);
     } else {
       rrset.addRR(r);
     }
   }
 }
  private static Record newRecord(
      Name name, int type, int dclass, long ttl, int length, DNSInput in) throws IOException {
    Record rec;
    int recstart;
    rec = getEmptyRecord(name, type, dclass, ttl, in != null);
    if (in != null) {
      if (in.remaining() < length) throw new WireParseException("truncated record");
      in.setActive(length);

      rec.rrFromWire(in);

      if (in.remaining() > 0) throw new WireParseException("invalid record length");
      in.clearActive();
    }
    return rec;
  }
  @Test
  public void itLogsServerErrors() throws Exception {
    header.setRcode(Rcode.REFUSED);

    Name name = Name.fromString("John Wayne.");
    Record question = Record.newRecord(name, 65530, 43210);

    Message query = Message.newQuery(question);

    Message response = mock(Message.class);
    when(response.getHeader()).thenReturn(header);
    when(response.getSectionArray(Section.ANSWER)).thenReturn(null);
    when(response.getQuestion()).thenReturn(question);

    when(nameServer.query(
            any(Message.class), any(InetAddress.class), any(DNSAccessRecord.Builder.class)))
        .thenThrow(new RuntimeException("Aw snap!"));

    FakeAbstractProtocol abstractProtocol = new FakeAbstractProtocol(client, query.toWire());
    abstractProtocol.setNameServer(nameServer);
    abstractProtocol.run();

    verify(accessLogger)
        .info(
            "144140678.000 qtype=DNS chi=192.168.23.45 ttms=345.123 xn=65535 fqdn=John\\032Wayne. type=TYPE65530 class=CLASS43210 rcode=SERVFAIL rtype=- rloc=\"-\" rdtl=- rerr=\"Server Error:RuntimeException:Aw snap!\" ttl=\"-\" ans=\"-\"");
  }
Beispiel #16
0
 /**
  * Asynchronously sends a message to a single server, registering a listener to receive a callback
  * on success or exception. Multiple asynchronous lookups can be performed in parallel. Since the
  * callback may be invoked before the function returns, external synchronization is necessary.
  *
  * @param query The query to send
  * @param listener The object containing the callbacks.
  * @return An identifier, which is also a parameter in the callback
  */
 public Object sendAsync(final Message query, final ResolverListener listener) {
   final Object id;
   synchronized (this) {
     id = new Integer(uniqueID++);
   }
   Record question = query.getQuestion();
   String qname;
   if (question != null) qname = question.getName().toString();
   else qname = "(none)";
   String name = this.getClass() + ": " + qname;
   Thread thread = new ResolveThread(this, query, id, listener);
   thread.setName(name);
   thread.setDaemon(true);
   thread.start();
   return id;
 }
Beispiel #17
0
 /**
  * Constructs and returns the next record in the expansion.
  *
  * @throws IOException The name or rdata was invalid after substitutions were performed.
  */
 public Record nextRecord() throws IOException {
   if (current > end) return null;
   String namestr = substitute(namePattern, current);
   Name name = Name.fromString(namestr, origin);
   String rdata = substitute(rdataPattern, current);
   current += step;
   return Record.fromString(name, type, dclass, ttl, rdata, origin);
 }
Beispiel #18
0
  private final void maybeAddRecord(Record record, Cache cache, Object source) throws IOException {
    int type = record.getType();
    Name name = record.getName();

    if (type == Type.SOA) {
      if (!name.equals(origin))
        throw new IOException("SOA owner " + name + " does not match zone origin " + origin);
      else {
        setOrigin(origin);
        dclass = record.getDClass();
      }
    }
    if (origin == null && type != Type.SOA)
      throw new IOException("non-SOA record seen at " + name + " with no origin set");
    if (name.subdomain(origin)) addRecord(record);
    else if (cache != null) cache.addRecord(record, Credibility.GLUE, source);
  }
Beispiel #19
0
 /**
  * Adds a record to the Cache.
  *
  * @param r The record to be added
  * @param cred The credibility of the record
  * @param o The source of the record (this could be a Message, for example)
  * @see Record
  */
 public void addRecord(Record r, byte cred, Object o) {
   Name name = r.getName();
   short type = r.getRRsetType();
   if (!Type.isRR(type)) return;
   boolean addrrset = false;
   Element element = (Element) findExactSet(name, type);
   if (element == null || cred > element.credibility) {
     RRset rrset = new RRset();
     rrset.addRR(r);
     addRRset(rrset, cred);
   } else if (cred == element.credibility) {
     if (element instanceof PositiveElement) {
       PositiveElement pe = (PositiveElement) element;
       pe.rrset.addRR(r);
     }
   }
 }
Beispiel #20
0
  /**
   * Converts the given section of the Message to a String.
   *
   * @see Section
   */
  public String sectionToString(int i) {
    if (i > 3) return null;

    StringBuffer sb = new StringBuffer();

    Record[] records = getSectionArray(i);
    for (int j = 0; j < records.length; j++) {
      Record rec = records[j];
      if (i == Section.QUESTION) {
        sb.append(";;\t" + rec.name);
        sb.append(", type = " + Type.string(rec.type));
        sb.append(", class = " + DClass.string(rec.dclass));
      } else if (rec.getType() == Type.OPT) continue;
      else sb.append(rec);
      sb.append("\n");
    }
    return sb.toString();
  }
  /**
   * 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);
  }
Beispiel #22
0
 /**
  * Constructs and returns all records in the expansion.
  *
  * @throws IOException The name or rdata of a record was invalid after substitutions were
  *     performed.
  */
 public Record[] expand() throws IOException {
   List list = new ArrayList();
   for (long i = start; i < end; i += step) {
     String namestr = substitute(namePattern, current);
     Name name = Name.fromString(namestr, origin);
     String rdata = substitute(rdataPattern, current);
     list.add(Record.fromString(name, type, dclass, ttl, rdata, origin));
   }
   return (Record[]) list.toArray(new Record[list.size()]);
 }
  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];
  }
Beispiel #24
0
  /**
   * 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();
  }
Beispiel #25
0
  /* Returns the number of records not successfully rendered. */
  private int sectionToWire(DNSOutput out, int section, Compression c, int maxLength) {
    int n = sections[section].size();
    int pos = out.current();
    int rendered = 0;
    Record lastrec = null;

    for (int i = 0; i < n; i++) {
      Record rec = (Record) sections[section].get(i);
      if (lastrec != null && !sameSet(rec, lastrec)) {
        pos = out.current();
        rendered = i;
      }
      lastrec = rec;
      rec.toWire(out, section, c);
      if (out.current() > maxLength) {
        out.jump(pos);
        return n - rendered;
      }
    }
    return 0;
  }
  @Test
  public void itLogsARecordQueries() throws Exception {
    header.setRcode(Rcode.NOERROR);

    Name name = Name.fromString("www.example.com.");
    Record question = Record.newRecord(name, Type.A, DClass.IN, 0L);
    Message query = Message.newQuery(question);

    query.getHeader().getRcode();

    byte[] queryBytes = query.toWire();

    whenNew(Message.class).withArguments(queryBytes).thenReturn(query);

    InetAddress resolvedAddress = Inet4Address.getByName("192.168.8.9");

    Record answer = new ARecord(name, DClass.IN, 3600L, resolvedAddress);
    Record[] answers = new Record[] {answer};

    Message response = mock(Message.class);
    when(response.getHeader()).thenReturn(header);
    when(response.getSectionArray(Section.ANSWER)).thenReturn(answers);
    when(response.getQuestion()).thenReturn(question);

    InetAddress client = Inet4Address.getByName("192.168.23.45");
    when(nameServer.query(
            any(Message.class), any(InetAddress.class), any(DNSAccessRecord.Builder.class)))
        .thenReturn(response);

    FakeAbstractProtocol abstractProtocol = new FakeAbstractProtocol(client, queryBytes);
    abstractProtocol.setNameServer(nameServer);

    abstractProtocol.run();

    verify(accessLogger)
        .info(
            "144140678.000 qtype=DNS chi=192.168.23.45 ttms=345.123 xn=65535 fqdn=www.example.com. type=A class=IN rcode=NOERROR rtype=- rloc=\"-\" rdtl=- rerr=\"-\" ttl=\"3600\" ans=\"192.168.8.9\"");
  }
Beispiel #27
0
 /**
  * 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();
 }
 /**
  * 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;
 }
Beispiel #29
0
 private static boolean sameSet(Record r1, Record r2) {
   return (r1.getRRsetType() == r2.getRRsetType()
       && r1.getDClass() == r2.getDClass()
       && r1.getName().equals(r2.getName()));
 }
 /**
  * Creates a new record identical to the current record, but with a different class and ttl. This
  * is most useful for dynamic update.
  */
 Record withDClass(int dclass, long ttl) {
   Record rec = cloneRecord();
   rec.dclass = dclass;
   rec.ttl = ttl;
   return rec;
 }