blob: 069a78941e160fe4883223a6e11a0c4d8040b45d [file] [log] [blame]
//Copyright 2003-2005 Arthur van Hoff, Rick Blair
//Licensed under Apache License version 2.0
//Original license LGPL
package javax.jmdns.impl.tasks;
import java.util.HashSet;
import java.util.Set;
import java.util.Timer;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.jmdns.impl.DNSIncoming;
import javax.jmdns.impl.DNSOutgoing;
import javax.jmdns.impl.DNSQuestion;
import javax.jmdns.impl.DNSRecord;
import javax.jmdns.impl.JmDNSImpl;
import javax.jmdns.impl.constants.DNSConstants;
/**
* The Responder sends a single answer for the specified service infos and for the host name.
*/
public class Responder extends DNSTask
{
static Logger logger = Logger.getLogger(Responder.class.getName());
/**
*
*/
private DNSIncoming _in;
/**
*
*/
private boolean _unicast;
public Responder(JmDNSImpl jmDNSImpl, DNSIncoming in, int port)
{
super(jmDNSImpl);
this._in = in;
this._unicast = (port != DNSConstants.MDNS_PORT);
}
/*
* (non-Javadoc)
*
* @see javax.jmdns.impl.tasks.DNSTask#getName()
*/
@Override
public String getName()
{
return "Responder(" + (this.getDns() != null ? this.getDns().getName() : "") + ")";
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
return super.toString() + " incomming: " + _in;
}
/*
* (non-Javadoc)
*
* @see javax.jmdns.impl.tasks.DNSTask#start(java.util.Timer)
*/
@Override
public void start(Timer timer)
{
// According to draft-cheshire-dnsext-multicastdns.txt chapter "7 Responding":
// We respond immediately if we know for sure, that we are the only one who can respond to the query.
// In all other cases, we respond within 20-120 ms.
//
// According to draft-cheshire-dnsext-multicastdns.txt chapter "6.2 Multi-Packet Known Answer Suppression":
// We respond after 20-120 ms if the query is truncated.
boolean iAmTheOnlyOne = true;
for (DNSQuestion question : _in.getQuestions())
{
if (logger.isLoggable(Level.FINEST))
{
logger.finest(this.getName() + "start() question=" + question);
}
iAmTheOnlyOne = question.iAmTheOnlyOne(this.getDns());
if (!iAmTheOnlyOne)
{
break;
}
}
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();
if (delay < 0)
{
delay = 0;
}
if (logger.isLoggable(Level.FINEST))
{
logger.finest(this.getName() + "start() Responder chosen delay=" + delay);
}
if (!this.getDns().isCanceling() && !this.getDns().isCanceled())
{
timer.schedule(this, delay);
}
}
@Override
public void run()
{
this.getDns().respondToQuery(_in);
// We use these sets to prevent duplicate records
Set<DNSQuestion> questions = new HashSet<DNSQuestion>();
Set<DNSRecord> answers = new HashSet<DNSRecord>();
if (this.getDns().isAnnounced())
{
try
{
// Answer questions
for (DNSQuestion question : _in.getQuestions())
{
if (logger.isLoggable(Level.FINER))
{
logger.finer(this.getName() + "run() JmDNS responding to: " + question);
}
// for unicast responses the question must be included
if (_unicast)
{
// out.addQuestion(q);
questions.add(question);
}
question.addAnswers(this.getDns(), answers);
}
// remove known answers, if the ttl is at least half of the correct value. (See Draft Cheshire chapter 7.1.).
long now = System.currentTimeMillis();
for (DNSRecord knownAnswer : _in.getAnswers())
{
if (knownAnswer.isStale(now))
{
answers.remove(knownAnswer);
if (logger.isLoggable(Level.FINER))
{
logger.finer(this.getName() + "JmDNS Responder Known Answer Removed");
}
}
}
// respond if we have answers
if (answers.size() != 0)
{
if (logger.isLoggable(Level.FINER))
{
logger.finer(this.getName() + "run() JmDNS responding");
}
DNSOutgoing out = new DNSOutgoing(DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA, !_unicast, _in.getSenderUDPPayload());
out.setId(_in.getId());
for (DNSQuestion question : questions)
{
out = this.addQuestion(out, question);
}
for (DNSRecord answer : answers)
{
out = this.addAnswer(out, _in, answer);
}
if (!out.isEmpty())
this.getDns().send(out);
}
// this.cancel();
}
catch (Throwable e)
{
logger.log(Level.WARNING, "run() exception ", e);
this.getDns().close();
}
}
}
}