Home | History | Annotate | Download | only in tasks
      1 // Copyright 2003-2005 Arthur van Hoff, Rick Blair
      2 // Licensed under Apache License version 2.0
      3 // Original license LGPL
      4 
      5 package javax.jmdns.impl.tasks;
      6 
      7 import java.util.HashSet;
      8 import java.util.Set;
      9 import java.util.Timer;
     10 import java.util.logging.Level;
     11 import java.util.logging.Logger;
     12 
     13 import javax.jmdns.impl.DNSIncoming;
     14 import javax.jmdns.impl.DNSOutgoing;
     15 import javax.jmdns.impl.DNSQuestion;
     16 import javax.jmdns.impl.DNSRecord;
     17 import javax.jmdns.impl.JmDNSImpl;
     18 import javax.jmdns.impl.constants.DNSConstants;
     19 
     20 /**
     21  * The Responder sends a single answer for the specified service infos and for the host name.
     22  */
     23 public class Responder extends DNSTask {
     24     static Logger             logger = Logger.getLogger(Responder.class.getName());
     25 
     26     /**
     27      *
     28      */
     29     private final DNSIncoming _in;
     30 
     31     /**
     32      *
     33      */
     34     private final boolean     _unicast;
     35 
     36     public Responder(JmDNSImpl jmDNSImpl, DNSIncoming in, int port) {
     37         super(jmDNSImpl);
     38         this._in = in;
     39         this._unicast = (port != DNSConstants.MDNS_PORT);
     40     }
     41 
     42     /*
     43      * (non-Javadoc)
     44      * @see javax.jmdns.impl.tasks.DNSTask#getName()
     45      */
     46     @Override
     47     public String getName() {
     48         return "Responder(" + (this.getDns() != null ? this.getDns().getName() : "") + ")";
     49     }
     50 
     51     /*
     52      * (non-Javadoc)
     53      * @see java.lang.Object#toString()
     54      */
     55     @Override
     56     public String toString() {
     57         return super.toString() + " incomming: " + _in;
     58     }
     59 
     60     /*
     61      * (non-Javadoc)
     62      * @see javax.jmdns.impl.tasks.DNSTask#start(java.util.Timer)
     63      */
     64     @Override
     65     public void start(Timer timer) {
     66         // According to draft-cheshire-dnsext-multicastdns.txt chapter "7 Responding":
     67         // We respond immediately if we know for sure, that we are the only one who can respond to the query.
     68         // In all other cases, we respond within 20-120 ms.
     69         //
     70         // According to draft-cheshire-dnsext-multicastdns.txt chapter "6.2 Multi-Packet Known Answer Suppression":
     71         // We respond after 20-120 ms if the query is truncated.
     72 
     73         boolean iAmTheOnlyOne = true;
     74         for (DNSQuestion question : _in.getQuestions()) {
     75             if (logger.isLoggable(Level.FINEST)) {
     76                 logger.finest(this.getName() + "start() question=" + question);
     77             }
     78             iAmTheOnlyOne = question.iAmTheOnlyOne(this.getDns());
     79             if (!iAmTheOnlyOne) {
     80                 break;
     81             }
     82         }
     83         int delay = (iAmTheOnlyOne && !_in.isTruncated()) ? 0 : DNSConstants.RESPONSE_MIN_WAIT_INTERVAL + JmDNSImpl.getRandom().nextInt(DNSConstants.RESPONSE_MAX_WAIT_INTERVAL - DNSConstants.RESPONSE_MIN_WAIT_INTERVAL + 1) - _in.elapseSinceArrival();
     84         if (delay < 0) {
     85             delay = 0;
     86         }
     87         if (logger.isLoggable(Level.FINEST)) {
     88             logger.finest(this.getName() + "start() Responder chosen delay=" + delay);
     89         }
     90         if (!this.getDns().isCanceling() && !this.getDns().isCanceled()) {
     91             timer.schedule(this, delay);
     92         }
     93     }
     94 
     95     @Override
     96     public void run() {
     97         this.getDns().respondToQuery(_in);
     98 
     99         // We use these sets to prevent duplicate records
    100         Set<DNSQuestion> questions = new HashSet<DNSQuestion>();
    101         Set<DNSRecord> answers = new HashSet<DNSRecord>();
    102 
    103         if (this.getDns().isAnnounced()) {
    104             try {
    105                 // Answer questions
    106                 for (DNSQuestion question : _in.getQuestions()) {
    107                     if (logger.isLoggable(Level.FINER)) {
    108                         logger.finer(this.getName() + "run() JmDNS responding to: " + question);
    109                     }
    110                     // for unicast responses the question must be included
    111                     if (_unicast) {
    112                         // out.addQuestion(q);
    113                         questions.add(question);
    114                     }
    115 
    116                     question.addAnswers(this.getDns(), answers);
    117                 }
    118 
    119                 // remove known answers, if the ttl is at least half of the correct value. (See Draft Cheshire chapter 7.1.).
    120                 long now = System.currentTimeMillis();
    121                 for (DNSRecord knownAnswer : _in.getAnswers()) {
    122                     if (knownAnswer.isStale(now)) {
    123                         answers.remove(knownAnswer);
    124                         if (logger.isLoggable(Level.FINER)) {
    125                             logger.finer(this.getName() + "JmDNS Responder Known Answer Removed");
    126                         }
    127                     }
    128                 }
    129 
    130                 // respond if we have answers
    131                 if (!answers.isEmpty()) {
    132                     if (logger.isLoggable(Level.FINER)) {
    133                         logger.finer(this.getName() + "run() JmDNS responding");
    134                     }
    135                     DNSOutgoing out = new DNSOutgoing(DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA, !_unicast, _in.getSenderUDPPayload());
    136                     out.setId(_in.getId());
    137                     for (DNSQuestion question : questions) {
    138                         if (question != null) {
    139                             out = this.addQuestion(out, question);
    140                         }
    141                     }
    142                     for (DNSRecord answer : answers) {
    143                         if (answer != null) {
    144                             out = this.addAnswer(out, _in, answer);
    145 
    146                         }
    147                     }
    148                     if (!out.isEmpty()) this.getDns().send(out);
    149                 }
    150                 // this.cancel();
    151             } catch (Throwable e) {
    152                 logger.log(Level.WARNING, this.getName() + "run() exception ", e);
    153                 this.getDns().close();
    154             }
    155         }
    156     }
    157 }