// REMIND: Oops, this shouldn't be public! /// <summary> JmDNS callback to update a DNS record.</summary> public virtual void UpdateRecord(mDNS jmdns, long now, DNSRecord rec) { if ((rec != null) && !rec.IsExpired(now)) { switch (rec.type) { case DNSConstants.TYPE_A: // IPv4 case DNSConstants.TYPE_AAAA: // IPv6 FIXME [PJYF Oct 14 2004] This has not been tested if (rec.name.Equals(server)) { addr = ((Address)rec).IPAddress; } break; case DNSConstants.TYPE_SRV: if (rec.name.Equals(QualifiedName)) { Service srv = (Service)rec; server = srv.server; port = srv.port; weight = srv.weight; priority = srv.priority; addr = null; // changed to use getCache() instead - jeffs // updateRecord(jmdns, now, (DNSRecord)jmdns.cache.get(server, TYPE_A, CLASS_IN)); UpdateRecord(jmdns, now, (DNSRecord)jmdns.Cache.get_Renamed(server, DNSConstants.TYPE_A, DNSConstants.CLASS_IN)); } break; case DNSConstants.TYPE_TXT: if (rec.name.Equals(QualifiedName)) { Text txt = (Text)rec; text = txt.text; } break; } // Future Design Pattern // This is done, to notify the wait loop in method // JmDNS.getServiceInfo(type, name, timeout); if (HasData && dns != null) { dns.HandleServiceResolved(this); dns = null; } lock (this) { Monitor.PulseAll(this); } } }
// Remind: Method updateRecord should receive a better name. /// <summary> Notify all listeners that a record was updated.</summary> internal virtual void UpdateRecord(long now, DNSRecord rec) { // We do not want to block the entire DNS while we are updating the record for each listener (service info) IList listenerList = null; lock (this) { listenerList = new ArrayList(listeners); } foreach (IDNSListener listener in listenerList) { listener.UpdateRecord(this, now, rec); } if (rec.type == DNSConstants.TYPE_PTR || rec.type == DNSConstants.TYPE_SRV) { IList serviceListenerList = null; lock (this) { serviceListenerList = (IList)serviceListeners[rec.name.ToLower()]; // Iterate on a copy in case listeners will modify it if (serviceListenerList != null) { serviceListenerList = new ArrayList(serviceListenerList); } } if (serviceListenerList != null) { bool expired = rec.IsExpired(now); string type = rec.Name; string name = ((Pointer)rec).Alias; // DNSRecord old = (DNSRecord)services.get(name.toLowerCase()); if (!expired) { // new record ServiceEvent event_Renamed = new ServiceEvent(this, type, ToUnqualifiedName(type, name), null); foreach (IServiceListener listener in serviceListenerList) { listener.ServiceAdded(this, event_Renamed); } } else { // expire record ServiceEvent event_Renamed = new ServiceEvent(this, type, ToUnqualifiedName(type, name), null); foreach (IServiceListener listener in serviceListenerList) { listener.ServiceRemoved(this, event_Renamed); } } } } }
/// <summary> Add an answer to the message.</summary> internal void AddAnswer(DNSRecord rec, long now) { if (numAuthorities > 0 || numAdditionals > 0) { // TODO is this the right exception? throw new Exception("Questions must be added before answers"); } if (rec != null) { if ((now == 0) || !rec.IsExpired(now)) { WriteRecord(rec, now); numAnswers++; } } }
/// <summary> Generate a possibly unique name for a service using the information we /// have in the cache. /// /// </summary> /// <returns> returns true, if the name of the service info had to be changed. /// </returns> private bool makeServiceNameUnique(ServiceInfo info) { string originalQualifiedName = info.QualifiedName; long now = (DateTime.Now.Ticks - 621355968000000000) / 10000; bool collision; do { collision = false; // Check for collision in cache for (DNSCache.CacheNode j = cache.find(info.QualifiedName.ToLower()); j != null; j = j.Next) { DNSRecord a = (DNSRecord)j.Value; if ((a.type == DNSConstants.TYPE_SRV) && !a.IsExpired(now)) { Service s = (Service)a; if (s.port != info.port || !s.server.Equals(localHost.Name)) { logger.Debug("makeServiceNameUnique() JmDNS.makeServiceNameUnique srv collision:" + a + " s.server=" + s.server + " " + localHost.Name + " equals:" + (s.server.Equals(localHost.Name))); info.SetName(IncrementName(info.getName())); collision = true; break; } } } // Check for collision with other service infos published by JmDNS object selfService = services[info.QualifiedName.ToLower()]; if (selfService != null && selfService != info) { info.SetName(IncrementName(info.getName())); collision = true; } }while (collision); return(!(originalQualifiedName.Equals(info.QualifiedName))); }
/// <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> Handle an incoming response. Cache answers, and pass them on to /// the appropriate questions. /// </summary> internal void HandleResponse(DNSIncoming msg) { long now = (DateTime.Now.Ticks - 621355968000000000) / 10000; bool hostConflictDetected = false; bool serviceConflictDetected = false; for (int recIdx = 0; recIdx < msg.answers.Count; recIdx++) { DNSRecord rec = (DNSRecord)msg.answers[recIdx]; bool isInformative = false; bool expired = rec.IsExpired(now); // update the cache DNSRecord c = (DNSRecord)cache.get_Renamed(rec); if (c != null) { if (expired) { isInformative = true; cache.remove(c); } else { c.ResetTTL(rec); rec = c; msg.answers[recIdx] = c; // put back into collection } } else { if (!expired) { isInformative = true; cache.add(rec); } } switch (rec.type) { case DNSConstants.TYPE_PTR: // handle _mdns._udp records if (rec.Name.IndexOf("._mdns._udp.") >= 0) { if (!expired && rec.name.StartsWith("_services._mdns._udp.")) { isInformative = true; RegisterServiceType(((Pointer)rec).alias); } continue; } RegisterServiceType(rec.name); break; } if ((rec.GetEntryType() == DNSConstants.TYPE_A) || (rec.GetEntryType() == DNSConstants.TYPE_AAAA)) { hostConflictDetected |= rec.HandleResponse(this); } else { serviceConflictDetected |= rec.HandleResponse(this); } // notify the listeners if (isInformative) { UpdateRecord(now, rec); } } if (hostConflictDetected || serviceConflictDetected) { new Prober(this).start(); } }
/// <summary> Add an answer to the message.</summary> internal void AddAnswer(DNSRecord rec, long now) { if (numAuthorities > 0 || numAdditionals > 0) { // TODO is this the right exception? throw new Exception("Questions must be added before answers"); } if (rec != null) { if ((now == 0) || !rec.IsExpired(now)) { WriteRecord(rec, now); numAnswers++; } } }
// Remind: Method updateRecord should receive a better name. /// <summary> Notify all listeners that a record was updated.</summary> internal virtual void UpdateRecord(long now, DNSRecord rec) { // We do not want to block the entire DNS while we are updating the record for each listener (service info) IList listenerList = null; lock (this) { listenerList = new ArrayList(listeners); } foreach (IDNSListener listener in listenerList) { listener.UpdateRecord(this, now, rec); } if (rec.type == DNSConstants.TYPE_PTR || rec.type == DNSConstants.TYPE_SRV) { IList serviceListenerList = null; lock (this) { serviceListenerList = (IList) serviceListeners[rec.name.ToLower()]; // Iterate on a copy in case listeners will modify it if (serviceListenerList != null) serviceListenerList = new ArrayList(serviceListenerList); } if (serviceListenerList != null) { bool expired = rec.IsExpired(now); string type = rec.Name; string name = ((Pointer) rec).Alias; // DNSRecord old = (DNSRecord)services.get(name.toLowerCase()); if (!expired) { // new record ServiceEvent event_Renamed = new ServiceEvent(this, type, ToUnqualifiedName(type, name), null); foreach (IServiceListener listener in serviceListenerList) { listener.ServiceAdded(this, event_Renamed); } } else { // expire record ServiceEvent event_Renamed = new ServiceEvent(this, type, ToUnqualifiedName(type, name), null); foreach (IServiceListener listener in serviceListenerList) { listener.ServiceRemoved(this, event_Renamed); } } } } }
// REMIND: Oops, this shouldn't be public! /// <summary> JmDNS callback to update a DNS record.</summary> public virtual void UpdateRecord(mDNS jmdns, long now, DNSRecord rec) { if ((rec != null) && !rec.IsExpired(now)) { switch (rec.type) { case DNSConstants.TYPE_A: // IPv4 case DNSConstants.TYPE_AAAA: // IPv6 FIXME [PJYF Oct 14 2004] This has not been tested if (rec.name.Equals(server)) { addr = ((Address) rec).IPAddress; } break; case DNSConstants.TYPE_SRV: if (rec.name.Equals(QualifiedName)) { Service srv = (Service) rec; server = srv.server; port = srv.port; weight = srv.weight; priority = srv.priority; addr = null; // changed to use getCache() instead - jeffs // updateRecord(jmdns, now, (DNSRecord)jmdns.cache.get(server, TYPE_A, CLASS_IN)); UpdateRecord(jmdns, now, (DNSRecord) jmdns.Cache.get_Renamed(server, DNSConstants.TYPE_A, DNSConstants.CLASS_IN)); } break; case DNSConstants.TYPE_TXT: if (rec.name.Equals(QualifiedName)) { Text txt = (Text) rec; text = txt.text; } break; } // Future Design Pattern // This is done, to notify the wait loop in method // JmDNS.getServiceInfo(type, name, timeout); if (HasData && dns != null) { dns.HandleServiceResolved(this); dns = null; } lock (this) { Monitor.PulseAll(this); } } }