예제 #1
0
        /// <summary>
        /// Send an outgoing packet
        /// </summary>
        /// <returns>The send.</returns>
        /// <param name="outgoing">Outgoing.</param>
        /// <param name="address">Address.</param>
        /// <param name="port">Port.</param>
        public void Send(DNSOutgoing outgoing, IPAddress address = null, int port = Constants.MDNS_PORT)
        {
            if (address == null)
            {
                address = Constants.MDNS_ADDRESS;
            }

            byte[] packet = outgoing.Packet();
            if (packet.Length > Constants.MAX_MSG_ABSOLUTE)
            {
                Console.WriteLine("Dropping {0} over-sized packet ({1} bytes packet",
                                  outgoing, packet.Length);
                return;
            }
            foreach (Socket s in this.respondSockets)
            {
                if (this.globalDone)
                {
                    return;
                }
                int sent = s.SendTo(packet, new IPEndPoint(address, port));
                if (sent != packet.Length)
                {
                    Console.WriteLine("!!! sent {0} out of {1} bytes to {2}",
                                      sent, packet.Length, s);
                }
            }
        }
예제 #2
0
        /// <summary>
        /// Checks the network for a unique service name, modifying the
        /// ServiceInfo passed in if it is not unique.
        /// </summary>
        /// <param name="info">Info.</param>
        /// <param name="allowNameChange">If set to <c>true</c> allow name change.</param>
        public void CheckService(ServiceInfo info, bool allowNameChange = false)
        {
            // This is kind of funky because of the subtype based tests
            // need to make subtypes a first class citizen
            string serviceName = Utilities.ServiceTypeName(info.Name);

            if (!info.Type.EndsWith(serviceName, StringComparison.CurrentCulture))
            {
                throw new BadTypeInNameException("CheckService");
            }

            string instanceName       = info.Name.Substring(0, serviceName.Length - 1);
            int    nextInstanceNumber = 2;

            long now      = Utilities.CurrentTimeMilliseconds();
            long nextTime = now;
            int  i        = 0;

            while (i < 3)
            {
                // Check for a name conflict
                while (this.Cache.CurrentEntryWithNameAndAlias(info.Type, info.Name) != null)
                {
                    if (!allowNameChange)
                    {
                        throw new NonUniqueNameException("CheckService");
                    }

                    info.Name = String.Format("{0}-{1}.{2}",
                                              instanceName,
                                              nextInstanceNumber,
                                              info.Type);
                    nextInstanceNumber += 1;
                    Utilities.ServiceTypeName(info.Name);
                    nextTime = now;
                    i        = 0;
                }

                if (now < nextTime)
                {
                    Wait(nextTime - now);
                    now = Utilities.CurrentTimeMilliseconds();
                    continue;
                }

                DNSOutgoing outgoing = new DNSOutgoing((ushort)QueryFlags.Query | (ushort)Flags.AA);
                //this.debug = outgoing;
                outgoing.AddQuestion(new DNSQuestion(info.Type, DNSType.PTR, DNSClass.IN));
                outgoing.AddAuthorativeAnswer(new DNSPointer(
                                                  info.Type, DNSType.PTR, DNSClass.IN, Constants.DNS_TTL, info.Name
                                                  ));
                Send(outgoing);
                i++;
                nextTime += Timing.Check;
            }
        }
예제 #3
0
        /// <summary>
        /// Unregister a service.
        /// </summary>
        /// <param name="info">Info.</param>
        public void UnregisterService(ServiceInfo info)
        {
            string nameLower = info.Type.ToLower();

            if (this.services.ContainsKey(nameLower))
            {
                this.services.Remove(nameLower);
                if (this.serviceTypes[info.Type] > 0)
                {
                    this.serviceTypes[info.Type] -= 1;
                }
                else
                {
                    this.serviceTypes.Remove(info.Type);
                }
            }

            long now      = Utilities.CurrentTimeMilliseconds();
            long nextTime = now;
            int  i        = 0;

            while (i < 3)
            {
                if (now < nextTime)
                {
                    Wait(nextTime - now);
                    now = Utilities.CurrentTimeMilliseconds();
                    continue;
                }
                DNSOutgoing outgoing = new DNSOutgoing((ushort)QueryFlags.Response | (ushort)Flags.AA);
                outgoing.AddAnswerAtTime(
                    new DNSPointer(info.Type, DNSType.PTR, DNSClass.IN, 0, info.Name), 0);
                outgoing.AddAnswerAtTime(
                    new DNSService(info.Name, DNSType.SRV, DNSClass.IN, 0,
                                   info.Priority, info.Weight, info.Port, info.Name), 0);

                outgoing.AddAnswerAtTime(
                    new DNSText(info.Name, DNSType.TXT, DNSClass.IN, 0, info.Text), 0);
                if (info.Address != null)
                {
                    outgoing.AddAnswerAtTime(
                        new DNSAddress(info.Server, DNSType.A, DNSClass.IN, 0,
                                       Utilities.AddressToBytes(info.Address)), 0);
                }

                Send(outgoing);
                i++;
                nextTime += Timing.Unregister;
            }
        }
예제 #4
0
        /// <summary>
        /// Registers service information to the network with a default TTL
        // of 60 seconds.Zeroconf will then respond to requests for
        // information for that service.The name of the service may be
        // changed if needed to make it unique on the network.
        /// </summary>
        /// <param name="info">Info.</param>
        /// <param name="ttl">Ttl.</param>
        /// <param name="allowNameChange">If set to <c>true</c> allow name change.</param>
        public void RegisterService(ServiceInfo info, ushort ttl = Constants.DNS_TTL,
                                    bool allowNameChange         = false)
        {
            CheckService(info, allowNameChange);

            this.services[info.Name.ToLower()] = info;
            if (this.serviceTypes.ContainsKey(info.Type))
            {
                this.serviceTypes[info.Type] += 1;
            }
            else
            {
                this.serviceTypes[info.Type] = 1;
            }

            long now      = Utilities.CurrentTimeMilliseconds();
            long nextTime = now;
            int  i        = 0;

            while (i < 3)
            {
                if (now < nextTime)
                {
                    Wait(nextTime - now);
                    now = Utilities.CurrentTimeMilliseconds();
                    continue;
                }
                DNSOutgoing outgoing = new DNSOutgoing((ushort)QueryFlags.Response | (ushort)Flags.AA);
                outgoing.AddAnswerAtTime(
                    new DNSPointer(info.Type, DNSType.PTR, DNSClass.IN, ttl, info.Name), 0);
                outgoing.AddAnswerAtTime(
                    new DNSService(info.Name, DNSType.SRV, DNSClass.IN, ttl,
                                   info.Priority, info.Weight, info.Port, info.Server), 0);

                outgoing.AddAnswerAtTime(
                    new DNSText(info.Name, DNSType.TXT, DNSClass.IN, ttl, info.Text), 0);
                if (info.Address != null)
                {
                    outgoing.AddAnswerAtTime(
                        new DNSAddress(info.Server, DNSType.A, DNSClass.IN, ttl,
                                       Utilities.AddressToBytes(info.Address)), 0);
                }

                Send(outgoing);
                i++;
                nextTime += Timing.Register;
            }
        }
예제 #5
0
        public void Run()
        {
            this.zc.AddListener(this, new DNSQuestion(this.type,
                                                      DNSType.PTR, DNSClass.IN));

            long now;

            while (true)
            {
                now = Utilities.CurrentTimeMilliseconds();
                if (this.eventArgs.Count == 0 && this.nextTime > now)
                {
                    this.zc.Wait(this.nextTime - now);
                }
                if (this.zc.Done || this.done)
                {
                    return;
                }
                now = Utilities.CurrentTimeMilliseconds();
                if (this.nextTime <= now)
                {
                    DNSOutgoing outgoing = new DNSOutgoing((ushort)QueryFlags.Query);
                    outgoing.AddQuestion(new DNSQuestion(this.type, DNSType.PTR, DNSClass.IN));
                    foreach (DNSRecord record in this.services.Values)
                    {
                        if (!record.IsStale(now))
                        {
                            outgoing.AddAnswerAtTime(record, now);
                        }
                    }
                    this.zc.Send(outgoing);
                    this.nextTime = now + this.delay;
                    this.delay    = Math.Min(20 * 1000, this.delay * 2);
                }

                if (this.eventArgs.Count > 0 && !this.zc.Done)
                {
                    Delegates.OnChangeEventArgs args = this.eventArgs.Dequeue();
                    this._handlersToCall(this.zc, args);
                }
            }
        }
예제 #6
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);
        }
예제 #7
0
        /// <summary>
        /// Deal with incoming query packets.  Provides a response if
        /// possible.
        /// </summary>
        /// <param name="msg">Message.</param>
        /// <param name="address">Address.</param>
        /// <param name="port">Port.</param>
        public void HandleQuery(DNSIncoming msg, IPAddress address, int port)
        {
            DNSOutgoing outgoing = null;

            // Support unicast client responses
            if (port != Constants.MDNS_PORT)
            {
                outgoing = new DNSOutgoing((ushort)QueryFlags.Response | (ushort)Flags.AA,
                                           multicast: false);
                foreach (DNSQuestion question in msg.Questions)
                {
                    outgoing.AddQuestion(question);
                }
            }

            foreach (DNSQuestion question in msg.Questions)
            {
                if (question.Type == DNSType.PTR)
                {
                    if (question.Name == "_services._dns-sd._udp.local.")
                    {
                        foreach (string stype in this.serviceTypes.Keys)
                        {
                            if (outgoing == null)
                            {
                                outgoing = new DNSOutgoing((ushort)QueryFlags.Response | (ushort)Flags.AA);
                            }
                            outgoing.AddAnswer(msg, new DNSPointer(
                                                   "_services._dns-sd._udp.local.", DNSType.PTR,
                                                   DNSClass.IN, Constants.DNS_TTL, stype));
                        }
                    }

                    foreach (ServiceInfo service in this.services.Values)
                    {
                        if (question.Name == service.Type)
                        {
                            if (outgoing == null)
                            {
                                outgoing = new DNSOutgoing((ushort)QueryFlags.Response | (ushort)Flags.AA);
                            }
                            outgoing.AddAnswer(msg, new DNSPointer(
                                                   service.Type, DNSType.PTR,
                                                   DNSClass.IN, Constants.DNS_TTL, service.Name));
                        }
                    }
                }
                else
                {
                    if (outgoing == null)
                    {
                        outgoing = new DNSOutgoing((ushort)QueryFlags.Response | (ushort)Flags.AA);
                    }

                    // Answer A record queries for any service addresses we know
                    if (question.Type == DNSType.A || question.Type == DNSType.ANY)
                    {
                        foreach (ServiceInfo s in this.services.Values)
                        {
                            if (s.Server == question.Name.ToLower())
                            {
                                outgoing.AddAnswer(msg, new DNSAddress(
                                                       question.Name, DNSType.A,
                                                       DNSClass.IN | DNSClass.UNIQUE,
                                                       Constants.DNS_TTL,
                                                       Utilities.AddressToBytes(s.Address)));
                            }
                        }
                    }

                    if (!this.services.ContainsKey(question.Name.ToLower()))
                    {
                        continue;
                    }

                    ServiceInfo service = this.services[question.Name.ToLower()];

                    if (question.Type == DNSType.SRV || question.Type == DNSType.ANY)
                    {
                        outgoing.AddAnswer(msg, new DNSService(
                                               question.Name, DNSType.SRV, DNSClass.IN | DNSClass.UNIQUE,
                                               Constants.DNS_TTL, service.Priority, service.Weight,
                                               service.Port, service.Server));
                    }
                    if (question.Type == DNSType.TXT || question.Type == DNSType.ANY)
                    {
                        outgoing.AddAnswer(msg, new DNSText(
                                               question.Name, DNSType.TXT, DNSClass.IN | DNSClass.UNIQUE,
                                               Constants.DNS_TTL, service.Text));
                    }
                    if (question.Type == DNSType.SRV)
                    {
                        outgoing.AddAdditionalAnswer(new DNSAddress(
                                                         service.Server, DNSType.A, DNSClass.IN | DNSClass.UNIQUE,
                                                         Constants.DNS_TTL, Utilities.AddressToBytes(service.Address)));
                    }
                }
            }
            if (outgoing != null && outgoing.Answers.Count > 0)
            {
                outgoing.ID = msg.ID;
                Send(outgoing, address, port);
            }
        }