/// <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); } } }
/// <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; } }
/// <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; } }
/// <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; } }
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); } } }
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); }
/// <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); } }