13742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// Copyright 2003-2005 Arthur van Hoff, Rick Blair 23742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// Licensed under Apache License version 2.0 33742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// Original license LGPL 43742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 53742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanpackage javax.jmdns.impl.tasks; 63742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 73742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.HashSet; 83742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.Set; 93742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.Timer; 103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.logging.Level; 113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.logging.Logger; 123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.DNSIncoming; 143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.DNSOutgoing; 153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.DNSQuestion; 163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.DNSRecord; 173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.JmDNSImpl; 183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.constants.DNSConstants; 193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman/** 213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman * The Responder sends a single answer for the specified service infos and for the host name. 223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman */ 233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanpublic class Responder extends DNSTask { 243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman static Logger logger = Logger.getLogger(Responder.class.getName()); 253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman /** 273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman * 283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman */ 293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman private final DNSIncoming _in; 303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman /** 323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman * 333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman */ 343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman private final boolean _unicast; 353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman public Responder(JmDNSImpl jmDNSImpl, DNSIncoming in, int port) { 373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman super(jmDNSImpl); 383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman this._in = in; 393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman this._unicast = (port != DNSConstants.MDNS_PORT); 403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman /* 433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman * (non-Javadoc) 443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman * @see javax.jmdns.impl.tasks.DNSTask#getName() 453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman */ 463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman @Override 473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman public String getName() { 483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman return "Responder(" + (this.getDns() != null ? this.getDns().getName() : "") + ")"; 493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman /* 523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman * (non-Javadoc) 533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman * @see java.lang.Object#toString() 543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman */ 553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman @Override 563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman public String toString() { 573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman return super.toString() + " incomming: " + _in; 583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman /* 613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman * (non-Javadoc) 623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman * @see javax.jmdns.impl.tasks.DNSTask#start(java.util.Timer) 633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman */ 643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman @Override 653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman public void start(Timer timer) { 663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman // According to draft-cheshire-dnsext-multicastdns.txt chapter "7 Responding": 673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman // We respond immediately if we know for sure, that we are the only one who can respond to the query. 683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman // In all other cases, we respond within 20-120 ms. 693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman // 703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman // According to draft-cheshire-dnsext-multicastdns.txt chapter "6.2 Multi-Packet Known Answer Suppression": 713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman // We respond after 20-120 ms if the query is truncated. 723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman boolean iAmTheOnlyOne = true; 743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman for (DNSQuestion question : _in.getQuestions()) { 753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (logger.isLoggable(Level.FINEST)) { 763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman logger.finest(this.getName() + "start() question=" + question); 773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman iAmTheOnlyOne = question.iAmTheOnlyOne(this.getDns()); 793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (!iAmTheOnlyOne) { 803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman break; 813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 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(); 843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (delay < 0) { 853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman delay = 0; 863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (logger.isLoggable(Level.FINEST)) { 883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman logger.finest(this.getName() + "start() Responder chosen delay=" + delay); 893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (!this.getDns().isCanceling() && !this.getDns().isCanceled()) { 913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman timer.schedule(this, delay); 923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman @Override 963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman public void run() { 973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman this.getDns().respondToQuery(_in); 983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman // We use these sets to prevent duplicate records 1003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman Set<DNSQuestion> questions = new HashSet<DNSQuestion>(); 1013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman Set<DNSRecord> answers = new HashSet<DNSRecord>(); 1023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 1033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (this.getDns().isAnnounced()) { 1043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman try { 1053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman // Answer questions 1063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman for (DNSQuestion question : _in.getQuestions()) { 1073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (logger.isLoggable(Level.FINER)) { 1083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman logger.finer(this.getName() + "run() JmDNS responding to: " + question); 1093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 1103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman // for unicast responses the question must be included 1113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (_unicast) { 1123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman // out.addQuestion(q); 1133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman questions.add(question); 1143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 1153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 1163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman question.addAnswers(this.getDns(), answers); 1173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 1183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 1193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman // remove known answers, if the ttl is at least half of the correct value. (See Draft Cheshire chapter 7.1.). 1203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman long now = System.currentTimeMillis(); 1213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman for (DNSRecord knownAnswer : _in.getAnswers()) { 1223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (knownAnswer.isStale(now)) { 1233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman answers.remove(knownAnswer); 1243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (logger.isLoggable(Level.FINER)) { 1253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman logger.finer(this.getName() + "JmDNS Responder Known Answer Removed"); 1263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 1273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 1283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 1293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 1303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman // respond if we have answers 1313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (!answers.isEmpty()) { 1323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (logger.isLoggable(Level.FINER)) { 1333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman logger.finer(this.getName() + "run() JmDNS responding"); 1343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 1353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman DNSOutgoing out = new DNSOutgoing(DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA, !_unicast, _in.getSenderUDPPayload()); 1363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman out.setId(_in.getId()); 1373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman for (DNSQuestion question : questions) { 1383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (question != null) { 1393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman out = this.addQuestion(out, question); 1403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 1413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 1423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman for (DNSRecord answer : answers) { 1433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (answer != null) { 1443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman out = this.addAnswer(out, _in, answer); 1453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman 1463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 1473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 1483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman if (!out.isEmpty()) this.getDns().send(out); 1493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 1503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman // this.cancel(); 1513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } catch (Throwable e) { 1523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman logger.log(Level.WARNING, this.getName() + "run() exception ", e); 1533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman this.getDns().close(); 1543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 1553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 1563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman } 1573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman}