Example #1
0
 private Message sendAXFR(Message query) throws IOException {
   Name qname = query.getQuestion().getName();
   ZoneTransferIn xfrin = ZoneTransferIn.newAXFR(qname, address, tsig);
   xfrin.setTimeout((int) (getTimeout() / 1000));
   xfrin.setLocalAddress(localAddress);
   try {
     xfrin.run();
   } catch (ZoneTransferException e) {
     throw new WireParseException(e.getMessage());
   }
   List records = xfrin.getAXFR();
   Message response = new Message(query.getHeader().getID());
   response.getHeader().setFlag(Flags.AA);
   response.getHeader().setFlag(Flags.QR);
   response.addRecord(query.getQuestion(), Section.QUESTION);
   Iterator it = records.iterator();
   while (it.hasNext()) response.addRecord((Record) it.next(), Section.ANSWER);
   return response;
 }
Example #2
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;
 }
Example #3
0
 private int uniqueID(Message m) {
   Record r = m.getQuestion();
   return (((r.getName().hashCode() & 0xFFFF) << 16)
       + (r.getType() + hashCode() << 8)
       + (hashCode() & 0xFF));
 }
Example #4
0
  /**
   * 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);
    }
  }
Example #5
0
  /**
   * 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);
  }