/// <summary> Add a question to the message.</summary> internal void AddQuestion(DNSQuestion rec) { if (numAnswers > 0 || numAuthorities > 0 || numAdditionals > 0) { // TODO is this the right exception? throw new Exception("Questions must be added before answers"); } numQuestions++; WriteQuestion(rec); }
/// <summary> Add a listener for a question. The listener will receive updates /// of answers to the question as they arrive, or from the cache if they /// are already available. /// </summary> internal virtual void AddListener(IDNSListener listener, DNSQuestion question) { long now = (DateTime.Now.Ticks - 621355968000000000) / 10000; // add the new listener lock (this) { listeners.Add(listener); } // report existing matched records if (question != null) { for (DNSCache.CacheNode i = cache.find(question.name); i != null; i = i.Next) { DNSRecord c = (DNSRecord)i.Value; if (question.IsAnsweredBy(c) && !c.IsExpired(now)) { listener.UpdateRecord(this, now, c); } } } }
public virtual void start() { // According to draft-cheshire-dnsext-multicastdns.txt // chapter "8 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 "7.2 Multi-Packet Known Answer Suppression": // We respond after 20-120 ms if the query is truncated. bool iAmTheOnlyOne = true; foreach (DNSEntry entry in in_Renamed.questions) { if (entry is DNSQuestion) { DNSQuestion q = (DNSQuestion)entry; logger.Debug("start() question=" + q); iAmTheOnlyOne &= (q.type == DNSConstants.TYPE_SRV || q.type == DNSConstants.TYPE_TXT || q.type == DNSConstants.TYPE_A || q.type == DNSConstants.TYPE_AAAA || Enclosing_Instance.localHost.Name.ToUpper().Equals(q.name.ToUpper()) || Enclosing_Instance.services.Contains(q.name.ToLower())); if (!iAmTheOnlyOne) { break; } } } int delay = (iAmTheOnlyOne && !in_Renamed.Truncated)?0:DNSConstants.RESPONSE_MIN_WAIT_INTERVAL + random.Next(DNSConstants.RESPONSE_MAX_WAIT_INTERVAL - DNSConstants.RESPONSE_MIN_WAIT_INTERVAL + 1) - in_Renamed.ElapsedSinceArrival(); if (delay < 0) { delay = 0; } logger.Debug("start() Responder chosen delay=" + delay); // TODO: check this Enclosing_Instance.Timer = new Timer(new TimerCallback(this.Run), null, delay, 0); }
public void Run(object state) { lock (Enclosing_Instance.IOLock) { if (Enclosing_Instance.PlannedAnswer == in_Renamed) { Enclosing_Instance.PlannedAnswer = null; } // We use these sets to prevent duplicate records // FIXME - This should be moved into DNSOutgoing // TODO: check these for compatibility SupportClass.HashSetSupport questions = new SupportClass.HashSetSupport(); SupportClass.HashSetSupport answers = new SupportClass.HashSetSupport(); if (Enclosing_Instance.State == DNSState.ANNOUNCED) { try { long now = (DateTime.Now.Ticks - 621355968000000000) / 10000; long expirationTime = now + 1; //=now+DNSConstants.KNOWN_ANSWER_TTL; bool isUnicast = (port != DNSConstants.MDNS_PORT); // Answer questions foreach (DNSEntry entry in in_Renamed.questions) { if (entry is DNSQuestion) { DNSQuestion q = (DNSQuestion)entry; // for unicast responses the question must be included if (isUnicast) { //out.addQuestion(q); questions.Add(q); } int type = q.type; if (type == DNSConstants.TYPE_ANY || type == DNSConstants.TYPE_SRV) { // I ama not sure of why there is a special case here [PJYF Oct 15 2004] if (Enclosing_Instance.localHost.Name.ToUpper().Equals(q.Name.ToUpper())) { // type = DNSConstants.TYPE_A; DNSRecord answer = Enclosing_Instance.localHost.DNS4AddressRecord; if (answer != null) { answers.Add(answer); } answer = Enclosing_Instance.localHost.DNS6AddressRecord; if (answer != null) { answers.Add(answer); } type = DNSConstants.TYPE_IGNORE; } else if (Enclosing_Instance.serviceTypes.Contains(q.Name.ToLower())) { type = DNSConstants.TYPE_PTR; } } switch (type) { case DNSConstants.TYPE_A: { // Answer a query for a domain name //out = addAnswer( in, addr, port, out, host ); DNSRecord answer = Enclosing_Instance.localHost.DNS4AddressRecord; if (answer != null) { answers.Add(answer); } break; } case DNSConstants.TYPE_AAAA: { // Answer a query for a domain name DNSRecord answer = Enclosing_Instance.localHost.DNS6AddressRecord; if (answer != null) { answers.Add(answer); } break; } case DNSConstants.TYPE_PTR: { // Answer a query for services of a given type // find matching services foreach (ServiceInfo info in Enclosing_Instance.services.Values) { if (info.State == DNSState.ANNOUNCED) { if (q.name.ToUpper().Equals(info.type.ToUpper())) { DNSRecord answer = Enclosing_Instance.localHost.DNS4AddressRecord; if (answer != null) { answers.Add(answer); } answer = Enclosing_Instance.localHost.DNS6AddressRecord; if (answer != null) { answers.Add(answer); } answers.Add(new Pointer(info.type, DNSConstants.TYPE_PTR, DNSConstants.CLASS_IN, DNSConstants.DNS_TTL, info.QualifiedName)); answers.Add(new Service(info.QualifiedName, DNSConstants.TYPE_SRV, DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, DNSConstants.DNS_TTL, info.priority, info.weight, info.port, Enclosing_Instance.localHost.Name)); answers.Add(new Text(info.QualifiedName, DNSConstants.TYPE_TXT, DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, DNSConstants.DNS_TTL, info.text)); } } } if (q.name.ToUpper().Equals("_services._mdns._udp.local.".ToUpper())) { foreach (String s in Enclosing_Instance.serviceTypes.Values) { answers.Add(new Pointer("_services._mdns._udp.local.", DNSConstants.TYPE_PTR, DNSConstants.CLASS_IN, DNSConstants.DNS_TTL, s)); } } break; } case DNSConstants.TYPE_SRV: case DNSConstants.TYPE_ANY: case DNSConstants.TYPE_TXT: { ServiceInfo info = (ServiceInfo)Enclosing_Instance.services[q.name.ToLower()]; if (info != null && info.State == DNSState.ANNOUNCED) { DNSRecord answer = Enclosing_Instance.localHost.DNS4AddressRecord; if (answer != null) { answers.Add(answer); } answer = Enclosing_Instance.localHost.DNS6AddressRecord; if (answer != null) { answers.Add(answer); } answers.Add(new Pointer(info.type, DNSConstants.TYPE_PTR, DNSConstants.CLASS_IN, DNSConstants.DNS_TTL, info.QualifiedName)); answers.Add(new Service(info.QualifiedName, DNSConstants.TYPE_SRV, DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, DNSConstants.DNS_TTL, info.priority, info.weight, info.port, Enclosing_Instance.localHost.Name)); answers.Add(new Text(info.QualifiedName, DNSConstants.TYPE_TXT, DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, DNSConstants.DNS_TTL, info.text)); } break; } default: { //Console.WriteLine("JmDNSResponder.unhandled query:"+q); break; } } } } // remove known answers, if the ttl is at least half of // the correct value. (See Draft Cheshire chapter 7.1.). foreach (DNSRecord knownAnswer in in_Renamed.answers) { bool tempBoolean; tempBoolean = answers.Contains(knownAnswer); answers.Remove(knownAnswer); if (knownAnswer.ttl > DNSConstants.DNS_TTL / 2 && tempBoolean) { logger.Debug("JmDNS Responder Known Answer Removed"); } } // responde if we have answers if (answers.Count != 0) { logger.Debug("run() JmDNS responding"); DNSOutgoing out_Renamed = null; if (isUnicast) { out_Renamed = new DNSOutgoing(DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA, false); } foreach (DNSQuestion question in questions) { out_Renamed.AddQuestion(question); } foreach (DNSRecord answer in answers) { out_Renamed = Enclosing_Instance.AddAnswer(in_Renamed, addr, port, out_Renamed, answer); } Enclosing_Instance.Send(out_Renamed); } // TODO: do we need this? //cancel(); } catch (Exception e) { logger.Warn("run() exception ", e); Enclosing_Instance.Close(); } } } }
internal void WriteQuestion(DNSQuestion question) { WriteName(question.name); WriteShort(question.type); WriteShort(question.clazz); }
/// <summary> Add a listener for a question. The listener will receive updates /// of answers to the question as they arrive, or from the cache if they /// are already available. /// </summary> internal virtual void AddListener(IDNSListener listener, DNSQuestion question) { long now = (DateTime.Now.Ticks - 621355968000000000) / 10000; // add the new listener lock (this) { listeners.Add(listener); } // report existing matched records if (question != null) { for (DNSCache.CacheNode i = cache.find(question.name); i != null; i = i.Next) { DNSRecord c = (DNSRecord) i.Value; if (question.IsAnsweredBy(c) && !c.IsExpired(now)) { listener.UpdateRecord(this, now, c); } } } }
/// <summary> Parse a message from a datagram packet.</summary> internal DNSIncoming(SupportClass.PacketSupport packet) { this.packet = packet; this.data = SupportClass.ToSByteArray(packet.Data); this.len = packet.Length; // TODO: will this always be 0 in .NET?? this.off = 0; this.questions = ArrayList.ReadOnly(new ArrayList()); this.answers = ArrayList.ReadOnly(new ArrayList()); this.receivedTime = (DateTime.Now.Ticks - 621355968000000000) / 10000; try { id = ReadUnsignedShort(); flags = ReadUnsignedShort(); numQuestions = ReadUnsignedShort(); numAnswers = ReadUnsignedShort(); numAuthorities = ReadUnsignedShort(); numAdditionals = ReadUnsignedShort(); // parse questions if (numQuestions > 0) { questions = ArrayList.Synchronized(new ArrayList(numQuestions)); for (int i = 0; i < numQuestions; i++) { DNSQuestion question = new DNSQuestion(ReadName(), ReadUnsignedShort(), ReadUnsignedShort()); questions.Add(question); } } // parse answers int n = numAnswers + numAuthorities + numAdditionals; if (n > 0) { answers = ArrayList.Synchronized(new ArrayList(n)); for (int i = 0; i < n; i++) { string domain = ReadName(); int type = ReadUnsignedShort(); int clazz = ReadUnsignedShort(); int ttl = ReadInt(); int len = ReadUnsignedShort(); int end = off + len; DNSRecord rec = null; switch (type) { case DNSConstants.TYPE_A: // IPv4 case DNSConstants.TYPE_AAAA: // IPv6 FIXME [PJYF Oct 14 2004] This has not been tested rec = new Address(domain, type, clazz, ttl, ReadBytes(off, len)); break; case DNSConstants.TYPE_CNAME: case DNSConstants.TYPE_PTR: rec = new Pointer(domain, type, clazz, ttl, ReadName()); break; case DNSConstants.TYPE_TXT: rec = new Text(domain, type, clazz, ttl, ReadBytes(off, len)); break; case DNSConstants.TYPE_SRV: rec = new Service(domain, type, clazz, ttl, ReadUnsignedShort(), ReadUnsignedShort(), ReadUnsignedShort(), ReadName()); break; case DNSConstants.TYPE_HINFO: // Maybe we should do something with those break; default: logger.Debug("DNSIncoming() unknown type:" + type); break; } if (rec != null) { // Add a record, if we were able to create one. answers.Add(rec); } else { // Addjust the numbers for the skipped record if (answers.Count < numAnswers) { numAnswers--; } else if (answers.Count < numAnswers + numAuthorities) { numAuthorities--; } else if (answers.Count < numAnswers + numAuthorities + numAdditionals) { numAdditionals--; } } off = end; } } } catch (IOException e) { logger.Warn("DNSIncoming() dump " + Print(true) + "\n exception ", e); throw e; } }