public void AddAnswerAtTime(DNSRecord record, long now) { if (now == 0 || !record.IsExpired(now)) { this.Answers.Add(new Tuple <DNSRecord, long>(record, now)); } }
/// <summary> /// Deal with incoming response packets. All answers /// are held in the cache, and listeners are notified. /// </summary> /// <param name="msg">Message.</param> public void HandleResponse(DNSIncoming msg) { long now = Utilities.CurrentTimeMilliseconds(); foreach (DNSRecord record in msg.Answers) { bool expired = record.IsExpired(now); if (this.Cache.Entries().Contains(record)) { if (expired) { this.Cache.Remove(record); } else { DNSRecord entry = (DNSRecord)this.Cache.Get(record); if (entry != null) { entry.ResetTTL(record); } } } else { this.Cache.Add(record); } } foreach (DNSRecord record in msg.Answers) { UpdateRecord(now, record); } }
public void AddAnswer(DNSIncoming incoming, DNSRecord record) { if (!record.SuppressedBy(incoming)) { AddAnswerAtTime(record, 0); } }
/// <summary> /// Used to notify listeners of new information that has updated // a record. /// </summary> /// <param name="now">Now.</param> /// <param name="record">Record.</param> public void UpdateRecord(long now, DNSRecord record) { foreach (IServiceListener l in this.listeners) { l.UpdateRecord(this, now, record); } NotifyAll(); }
public string ToString(DNSRecord other) { long remainingTime = this.GetRemainingTTL(Utilities.CurrentTimeMilliseconds()); String arg = String.Format("{0}/{1},{2}", this.TTL, remainingTime, other); return(base.ToString("record", arg)); }
/// <summary> /// Returns a bytearray containing the packet's bytes /// /// No further parts should be added to the packet once this /// is done. /// </summary> /// <returns>The packet.</returns> public byte[] Packet() { int OverrunAnswers = 0; int OverrunAuthorities = 0; int OverrunAdditionals = 0; if (this.State != State.Finished) { // Reserve space for header fields this.bew.Write(new byte[12]); foreach (DNSQuestion question in this.Questions) { WriteQuestion(question); } foreach (Tuple <DNSRecord, long> entry in this.Answers) { DNSRecord answer = entry.Item1; long time = entry.Item2; OverrunAnswers += WriteRecord(answer, time); } foreach (DNSRecord authority in this.Authorities) { OverrunAuthorities += WriteRecord(authority, 0); } foreach (DNSRecord additional in this.Additionals) { OverrunAdditionals += WriteRecord(additional, 0); } this.State = State.Finished; // Write header fields this.bew.BaseStream.Position = 0; if (this.Multicast) { this.bew.Write((UInt16)0); } else { this.bew.Write(this.ID); } this.bew.Write(this.Flags); this.bew.Write((UInt16)this.Questions.Count); this.bew.Write((UInt16)(this.Answers.Count - OverrunAnswers)); this.bew.Write((UInt16)(this.Authorities.Count - OverrunAuthorities)); this.bew.Write((UInt16)(this.Additionals.Count - OverrunAdditionals)); } return(this.ms.ToArray()); }
/// <summary> /// Callback invoked by Zeroconf when new information arrives. /// Updates information required by browser in the Zeroconf cache. /// </summary> /// <param name="zc">Zc.</param> /// <param name="now">Now.</param> /// <param name="record">Record.</param> public void UpdateRecord(Zeroconf zc, long now, DNSRecord record) { if (record.Type == DNSType.PTR && record.Name == this.type) { bool expired = record.IsExpired(now); string alias = ((DNSPointer)record).Alias; string serviceKey = alias.ToLower(); DNSRecord oldRecord; bool success = this.services.TryGetValue(serviceKey, out oldRecord); if (!success && !expired) { this.services[serviceKey] = record; this.eventArgs.Enqueue( new Delegates.OnChangeEventArgs(this.type, alias, ServiceStateChange.Added) ); } else if (!expired) { oldRecord.ResetTTL(record); } else { this.services.Remove(serviceKey); this.eventArgs.Enqueue( new Delegates.OnChangeEventArgs(this.type, alias, ServiceStateChange.Removed) ); return; } long expires = record.GetExpirationTime(75); if (expires < this.nextTime) { this.nextTime = expires; } } }
/// <summary> /// Returns true if another record has same name, type and class, /// and if its TTL is at least half of this record's /// </summary> /// <returns><c>true</c>, if by answer was suppresseded, <c>false</c> otherwise.</returns> /// <param name="other">Other.</param> public bool SuppressedByAnswer(DNSRecord other) { return(this == other && other.TTL > (this.TTL / 2)); }
/// <summary> /// Sets this record's TTL and created time to that of /// another record /// </summary> /// <param name="other">Other.</param> public void ResetTTL(DNSRecord other) { this.Created = other.Created; this.TTL = other.TTL; }
/// <summary> /// Check wether question is answered by record /// </summary> /// <returns>Returns <c>true</c> if question is answered by record, /// <c>false</c> otherwise/</returns> /// <param name="record">Record.</param> public bool AnsweredBy(DNSRecord record) { return(base.Class == record.Class && (base.Type == record.Type || base.Type == DNSType.ANY) && base.Name == record.Name); }
public bool Request(Zeroconf zc, long timeout) { long now = Utilities.CurrentTimeMilliseconds(); long delay = Timing.Listener; long next = now + delay; long last = now + timeout; List <Tuple <DNSType, DNSClass> > recordTypesForCheckCache = new List <Tuple <DNSType, DNSClass> >() { new Tuple <DNSType, DNSClass>(DNSType.SRV, DNSClass.IN), new Tuple <DNSType, DNSClass>(DNSType.TXT, DNSClass.IN), }; if (this.Server != null) { recordTypesForCheckCache.Add(new Tuple <DNSType, DNSClass>(DNSType.A, DNSClass.IN)); } foreach (Tuple <DNSType, DNSClass> record_type in recordTypesForCheckCache) { DNSRecord cached = (DNSRecord)zc.Cache.GetByDetails(this.Name, record_type.Item1, record_type.Item2); if (cached != null) { this.UpdateRecord(zc, now, cached); } } if (this.Server != null || this.Address != null || this.Text != null) { return(true); } try { zc.AddListener(this, new DNSQuestion(this.Name, DNSType.ANY, DNSClass.IN)); while (this.Server == null || this.Address == null || this.Text == null) { if (last <= now) { return(false); } if (next <= now) { DNSOutgoing outgoing = new DNSOutgoing((ushort)QueryFlags.Query); outgoing.AddQuestion( new DNSQuestion(this.Name, DNSType.SRV, DNSClass.IN)); outgoing.AddAnswerAtTime( (DNSService)zc.Cache.GetByDetails(this.Name, DNSType.SRV, DNSClass.IN), now ); outgoing.AddQuestion( new DNSQuestion(this.Name, DNSType.TXT, DNSClass.IN)); outgoing.AddAnswerAtTime( (DNSText)zc.Cache.GetByDetails(this.Name, DNSType.TXT, DNSClass.IN), now ); if (this.Server != null) { outgoing.AddQuestion( new DNSQuestion(this.Name, DNSType.A, DNSClass.IN)); outgoing.AddAnswerAtTime( (DNSAddress)zc.Cache.GetByDetails(this.Name, DNSType.A, DNSClass.IN), now ); } zc.Send(outgoing); next = now + delay; delay *= 2; } zc.Wait(Math.Min(next, last) - now); now = Utilities.CurrentTimeMilliseconds(); } } finally { zc.RemoveListener(this); } return(true); }
public void AddAuthorativeAnswer(DNSRecord record) { this.Authorities.Add(record); }
/// <summary> /// Writes a record (answer, authoritative answer, additional) to /// the packet /// </summary> /// <param name="record">Record.</param> /// <param name="now">Now.</param> public int WriteRecord(DNSRecord record, long now) { if (this.State == State.Finished) { return(1); } long StartSize = this.bew.BaseStream.Length; this.bew.WriteName(record.Name); this.bew.Write((ushort)record.Type); if (record.Unique && this.Multicast) { this.bew.Write((ushort)((ushort)record.Class | (ushort)DNSClass.UNIQUE)); } else { this.bew.Write((ushort)record.Class); } if (now == 0) { this.bew.Write(record.TTL); } else { this.bew.Write((uint)record.GetRemainingTTL(now)); } // Reserve space for payload size this.bew.Write((UInt16)0); // Save old position long index = this.bew.BaseStream.Position; // Write payload record.Write(this.bew); long payloadSize = this.bew.BaseStream.Position - index; // Rewind to payload size position this.bew.BaseStream.Position -= (payloadSize + sizeof(UInt16)); this.bew.Write((UInt16)payloadSize); // Forward to end of stream this.bew.BaseStream.Position += payloadSize; // Need to add headerlength (12) for this check if ((this.bew.BaseStream.Length + 12) > Constants.MAX_MSG_ABSOLUTE) { byte[] newData = new byte[StartSize]; Array.Copy(this.ms.ToArray(), newData, StartSize); this.ms = new MemoryStream(newData); this.bew = new BigEndianWriter(this.ms); this.State = State.Finished; return(1); } return(0); }
/// <summary> /// Adds the additional answer. /// /// From: RFC 6763, DNS-Based Service Discovery, February 2013 /// /// 12. DNS Additional Record Generation /// /// DNS has an efficiency feature whereby a DNS server may place /// additional records in the additional section of the DNS message. /// These additional records are records that the client did not /// explicitly request, but the server has reasonable grounds to expect /// that the client might request them shortly, so including them can /// save the client from having to issue additional queries. /// /// This section recommends which additional records SHOULD be generated /// to improve network efficiency, for both Unicast and Multicast DNS-SD /// responses. /// /// 12.1. PTR Records /// /// When including a DNS-SD Service Instance Enumeration or Selective /// Instance Enumeration (subtype) PTR record in a response packet, the /// server/responder SHOULD include the following additional records: /// /// o The SRV record(s) named in the PTR rdata. /// o The TXT record(s) named in the PTR rdata. /// o All address records (type "A" and "AAAA") named in the SRV rdata. /// /// 12.2. SRV Records /// /// When including an SRV record in a response packet, the /// server/responder SHOULD include the following additional records: /// /// o All address records(type "A" and "AAAA") named in the SRV rdata. /// </summary> /// <param name="record">Record.</param> public void AddAdditionalAnswer(DNSRecord record) { this.Additionals.Add(record); }