1// Copyright 2003-2005 Arthur van Hoff, Rick Blair 2// Licensed under Apache License version 2.0 3// Original license LGPL 4 5package javax.jmdns.impl.tasks; 6 7import java.util.HashSet; 8import java.util.Set; 9import java.util.Timer; 10import java.util.logging.Level; 11import java.util.logging.Logger; 12 13import javax.jmdns.impl.DNSIncoming; 14import javax.jmdns.impl.DNSOutgoing; 15import javax.jmdns.impl.DNSQuestion; 16import javax.jmdns.impl.DNSRecord; 17import javax.jmdns.impl.JmDNSImpl; 18import 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 */ 23public 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}