예제 #1
0
 public void AddAnswerAtTime(DNSRecord record, long now)
 {
     if (now == 0 || !record.IsExpired(now))
     {
         this.Answers.Add(new Tuple <DNSRecord, long>(record, now));
     }
 }
예제 #2
0
        /// <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);
            }
        }
예제 #3
0
 public void AddAnswer(DNSIncoming incoming, DNSRecord record)
 {
     if (!record.SuppressedBy(incoming))
     {
         AddAnswerAtTime(record, 0);
     }
 }
예제 #4
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();
 }
예제 #5
0
        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));
        }
예제 #6
0
        /// <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());
        }
예제 #7
0
        /// <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;
                }
            }
        }
예제 #8
0
 /// <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));
 }
예제 #9
0
 /// <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;
 }
예제 #10
0
 /// <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);
 }
예제 #11
0
        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);
        }
예제 #12
0
 public void AddAuthorativeAnswer(DNSRecord record)
 {
     this.Authorities.Add(record);
 }
예제 #13
0
        /// <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);
        }
예제 #14
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);
 }