From: Apple Date: Wed, 15 Dec 2004 21:58:48 +0000 (+0000) Subject: mDNSResponder-87.tar.gz X-Git-Tag: v87^0 X-Git-Url: https://git.saurik.com/apple/mdnsresponder.git/commitdiff_plain/7f0064bd55e3fa98568d2c359429ff8a38b23a6c mDNSResponder-87.tar.gz --- diff --git a/Clients/DNS-SD.xcode/project.pbxproj b/Clients/DNS-SD.xcode/project.pbxproj index 46805b9..574ad8b 100644 --- a/Clients/DNS-SD.xcode/project.pbxproj +++ b/Clients/DNS-SD.xcode/project.pbxproj @@ -151,7 +151,7 @@ GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO; GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; GCC_WARN_UNKNOWN_PRAGMAS = NO; - HEADER_SEARCH_PATHS = ""; + HEADER_SEARCH_PATHS = ../mDNSShared; INSTALL_PATH = "$(HOME)/bin"; LIBRARY_SEARCH_PATHS = ""; OTHER_CFLAGS = ""; @@ -282,6 +282,7 @@ GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h"; GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; GCC_WARN_UNKNOWN_PRAGMAS = NO; + HEADER_SEARCH_PATHS = ../mDNSShared; INFOPLIST_FILE = "DNSServiceBrowser-Info.plist"; INSTALL_PATH = "$(USER_APPS_DIR)"; OTHER_CFLAGS = ""; @@ -396,7 +397,8 @@ GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h"; GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; GCC_WARN_UNKNOWN_PRAGMAS = NO; - INFOPLIST_FILE = "DNSServiceRegistration-Info.plist"; + HEADER_SEARCH_PATHS = ../mDNSShared; + INFOPLIST_FILE = "DNSServiceReg-Info.plist"; INSTALL_PATH = "$(USER_APPS_DIR)"; OTHER_CFLAGS = ""; OTHER_LDFLAGS = "-framework Foundation -framework AppKit"; @@ -436,7 +438,7 @@ CFBundleVersion 1.0.0d1 NSMainNibFile - DNSServiceRegistration + DNSServiceReg NSPrincipalClass NSApplication @@ -457,7 +459,7 @@ isa = PBXContainerItemProxy; proxyType = 1; remoteGlobalIDString = FF1E351206711B5C003DD5BC; - remoteInfo = DNSServiceRegistration; + remoteInfo = DNSServiceReg; }; FF1E351806711B6A003DD5BC = { isa = PBXTargetDependency; @@ -468,7 +470,7 @@ fileEncoding = 4; isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; - path = DNSServiceRegistration.m; + path = DNSServiceReg.m; refType = 4; sourceTree = ""; }; @@ -481,7 +483,7 @@ FF1E352506711BD6003DD5BC = { isa = PBXFileReference; lastKnownFileType = wrapper.nib; - path = DNSServiceRegistration.nib; + path = DNSServiceReg.nib; refType = 4; sourceTree = ""; }; diff --git a/Clients/DNSServiceBrowser.NET/App.ico b/Clients/DNSServiceBrowser.NET/App.ico new file mode 100755 index 0000000..3a5525f Binary files /dev/null and b/Clients/DNSServiceBrowser.NET/App.ico differ diff --git a/Clients/DNSServiceBrowser.NET/AssemblyInfo.cs b/Clients/DNSServiceBrowser.NET/AssemblyInfo.cs new file mode 100755 index 0000000..793362b --- /dev/null +++ b/Clients/DNSServiceBrowser.NET/AssemblyInfo.cs @@ -0,0 +1,89 @@ +/* + * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: AssemblyInfo.cs,v $ +Revision 1.1 2004/07/19 07:54:24 shersche +Initial revision + + +*/ + +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/Clients/DNSServiceBrowser.NET/DNSServiceBrowser.NET.csproj b/Clients/DNSServiceBrowser.NET/DNSServiceBrowser.NET.csproj new file mode 100755 index 0000000..d43cf10 --- /dev/null +++ b/Clients/DNSServiceBrowser.NET/DNSServiceBrowser.NET.csproj @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Clients/DNSServiceBrowser.NET/DNSServiceBrowser.cs b/Clients/DNSServiceBrowser.NET/DNSServiceBrowser.cs new file mode 100755 index 0000000..75bbc93 --- /dev/null +++ b/Clients/DNSServiceBrowser.NET/DNSServiceBrowser.cs @@ -0,0 +1,745 @@ +/* + * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: DNSServiceBrowser.cs,v $ +Revision 1.5 2004/09/21 16:26:58 shersche +Check to make sure browse list selected item is not null before resolving +Submitted by: prepin@gmail.com + +Revision 1.4 2004/09/13 19:38:17 shersche +Changed code to reflect namespace and type changes to dnssd.NET library + +Revision 1.3 2004/09/11 00:38:14 shersche +DNSService APIs now assume port in host format. Check for null text record in resolve callback. + +Revision 1.2 2004/07/22 23:15:25 shersche +Fix service names for teleport, tftp, and bootps + +Revision 1.1 2004/07/19 07:54:24 shersche +Initial revision + + + +*/ + +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using System.Data; +using System.Text; +using Apple.DNSSD; + +namespace DNSServiceBrowser_NET +{ + /// + /// Summary description for Form1. + /// + public class Form1 : System.Windows.Forms.Form + { + private System.Windows.Forms.ComboBox typeBox; + private System.Windows.Forms.ListBox browseList; + private ServiceRef browser = null; + private ServiceRef resolver = null; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + + // + // These delegates are invoked as a result of DNSService + // operation. + // + delegate void AddServiceCallback(BrowseData data); + delegate void RemoveServiceCallback(BrowseData data); + delegate void ResolveServiceCallback(ResolveData data); + + AddServiceCallback addServiceCallback; + RemoveServiceCallback removeServiceCallback; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.TextBox nameField; + private System.Windows.Forms.TextBox typeField; + private System.Windows.Forms.TextBox domainField; + private System.Windows.Forms.TextBox hostField; + private System.Windows.Forms.TextBox portField; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.ListBox serviceTextField; + ResolveServiceCallback resolveServiceCallback; + + public Form1() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + addServiceCallback = new AddServiceCallback(OnAddService); + removeServiceCallback = new RemoveServiceCallback(OnRemoveService); + resolveServiceCallback = new ResolveServiceCallback(OnResolveService); + + this.Load += new System.EventHandler(this.Form1_Load); + } + + private void Form1_Load(object sender, EventArgs e) + { + typeBox.SelectedItem = "_spike._tcp"; + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if (components != null) + { + components.Dispose(); + } + + if (browser != null) + { + browser.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.browseList = new System.Windows.Forms.ListBox(); + this.typeBox = new System.Windows.Forms.ComboBox(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.nameField = new System.Windows.Forms.TextBox(); + this.typeField = new System.Windows.Forms.TextBox(); + this.domainField = new System.Windows.Forms.TextBox(); + this.hostField = new System.Windows.Forms.TextBox(); + this.portField = new System.Windows.Forms.TextBox(); + this.label5 = new System.Windows.Forms.Label(); + this.serviceTextField = new System.Windows.Forms.ListBox(); + this.SuspendLayout(); + // + // browseList + // + this.browseList.Location = new System.Drawing.Point(8, 48); + this.browseList.Name = "browseList"; + this.browseList.Size = new System.Drawing.Size(488, 147); + this.browseList.TabIndex = 0; + this.browseList.SelectedIndexChanged += new System.EventHandler(this.listBox1_SelectedIndexChanged); + // + // typeBox + // + this.typeBox.Items.AddRange(new object[] + { + "_accessone._tcp", + "_accountedge._tcp", + "_actionitems._tcp", + "_addressbook._tcp", + "_aecoretech._tcp", + "_afpovertcp._tcp", + "_airport._tcp", + "_animolmd._tcp", + "_animobserver._tcp", + "_apple-sasl._tcp", + "_aquamon._tcp", + "_async._tcp", + "_auth._tcp", + "_beep._tcp", + "_bfagent._tcp", + "_bootps._udp", + "_bousg._tcp", + "_bsqdea._tcp", + "_cheat._tcp", + "_chess._tcp", + "_clipboard._tcp", + "_collection._tcp", + "_contactserver._tcp", + "_cvspserver._tcp", + "_cytv._tcp", + "_daap._tcp", + "_difi._tcp", + "_distcc._tcp", + "_dossier._tcp", + "_dpap._tcp", + "_earphoria._tcp", + "_ebms._tcp", + "_ebreg._tcp", + "_ecbyesfsgksc._tcp", + "_eheap._tcp", + "_embrace._tcp", + "_eppc._tcp", + "_eventserver._tcp", + "_exec._tcp", + "_facespan._tcp", + "_faxstfx._tcp", + "_fish._tcp", + "_fjork._tcp", + "_fmpro-internal._tcp", + "_ftp._tcp", + "_ftpcroco._tcp", + "_gbs-smp._tcp", + "_gbs-stp._tcp", + "_grillezvous._tcp", + "_h323._tcp", + "_http._tcp", + "_hotwayd._tcp", + "_hydra._tcp", + "_ica-networking._tcp", + "_ichalkboard._tcp", + "_ichat._tcp", + "_iconquer._tcp", + "_ifolder._tcp", + "_ilynx._tcp", + "_imap._tcp", + "_imidi._tcp", + "_ipbroadcaster._tcp", + "_ipp._tcp", + "_isparx._tcp", + "_ispq-vc._tcp", + "_ishare._tcp", + "_isticky._tcp", + "_istorm._tcp", + "_iwork._tcp", + "_lan2p._tcp", + "_ldap._tcp", + "_liaison._tcp", + "_login._tcp", + "_lontalk._tcp", + "_lonworks._tcp", + "_macfoh-remote._tcp", + "_macminder._tcp", + "_moneyworks._tcp", + "_mp3sushi._tcp", + "_mttp._tcp", + "_ncbroadcast._tcp", + "_ncdirect._tcp", + "_ncsyncserver._tcp", + "_net-assistant._tcp", + "_newton-dock._tcp", + "_nfs._udp", + "_nssocketport._tcp", + "_odabsharing._tcp", + "_omni-bookmark._tcp", + "_openbase._tcp", + "_p2pchat._udp", + "_pdl-datastream._tcp", + "_poch._tcp", + "_pop3._tcp", + "_postgresql._tcp", + "_presence._tcp", + "_printer._tcp", + "_ptp._tcp", + "_quinn._tcp", + "_raop._tcp", + "_rce._tcp", + "_realplayfavs._tcp", + "_rendezvouspong._tcp", + "_riousbprint._tcp", + "_rfb._tcp", + "_rtsp._tcp", + "_safarimenu._tcp", + "_sallingclicker._tcp", + "_scone._tcp", + "_sdsharing._tcp", + "_see._tcp", + "_seeCard._tcp", + "_serendipd._tcp", + "_servermgr._tcp", + "_shell._tcp", + "_shout._tcp", + "_shoutcast._tcp", + "_soap._tcp", + "_spike._tcp", + "_spincrisis._tcp", + "_spl-itunes._tcp", + "_spr-itunes._tcp", + "_ssh._tcp", + "_ssscreenshare._tcp", + "_strateges._tcp", + "_sge-exec._tcp", + "_sge-qmaster._tcp", + "_stickynotes._tcp", + "_sxqdea._tcp", + "_sybase-tds._tcp", + "_teamlist._tcp", + "_teleport._udp", + "_telnet._tcp", + "_tftp._udp", + "_ticonnectmgr._tcp", + "_tinavigator._tcp", + "_tryst._tcp", + "_upnp._tcp", + "_utest._tcp", + "_vue4rendercow._tcp", + "_webdav._tcp", + "_whamb._tcp", + "_wired._tcp", + "_workstation._tcp", + "_wormhole._tcp", + "_workgroup._tcp", + "_ws._tcp", + "_xserveraid._tcp", + "_xsync._tcp", + "_xtshapro._tcp" + }); + + this.typeBox.Location = new System.Drawing.Point(8, 16); + this.typeBox.Name = "typeBox"; + this.typeBox.Size = new System.Drawing.Size(192, 21); + this.typeBox.Sorted = true; + this.typeBox.TabIndex = 3; + this.typeBox.SelectedIndexChanged += new System.EventHandler(this.typeBox_SelectedIndexChanged); + // + // label1 + // + this.label1.Location = new System.Drawing.Point(8, 208); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(48, 16); + this.label1.TabIndex = 4; + this.label1.Text = "Name:"; + this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // label2 + // + this.label2.Location = new System.Drawing.Point(8, 240); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(48, 16); + this.label2.TabIndex = 5; + this.label2.Text = "Type:"; + this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // label3 + // + this.label3.Location = new System.Drawing.Point(8, 272); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(48, 16); + this.label3.TabIndex = 6; + this.label3.Text = "Domain:"; + this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // label4 + // + this.label4.Location = new System.Drawing.Point(8, 304); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(48, 16); + this.label4.TabIndex = 7; + this.label4.Text = "Host:"; + this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // nameField + // + this.nameField.Location = new System.Drawing.Point(56, 208); + this.nameField.Name = "nameField"; + this.nameField.ReadOnly = true; + this.nameField.Size = new System.Drawing.Size(168, 20); + this.nameField.TabIndex = 8; + this.nameField.Text = ""; + // + // typeField + // + this.typeField.Location = new System.Drawing.Point(56, 240); + this.typeField.Name = "typeField"; + this.typeField.ReadOnly = true; + this.typeField.Size = new System.Drawing.Size(168, 20); + this.typeField.TabIndex = 9; + this.typeField.Text = ""; + // + // domainField + // + this.domainField.Location = new System.Drawing.Point(56, 272); + this.domainField.Name = "domainField"; + this.domainField.ReadOnly = true; + this.domainField.Size = new System.Drawing.Size(168, 20); + this.domainField.TabIndex = 10; + this.domainField.Text = ""; + // + // hostField + // + this.hostField.Location = new System.Drawing.Point(56, 304); + this.hostField.Name = "hostField"; + this.hostField.ReadOnly = true; + this.hostField.Size = new System.Drawing.Size(168, 20); + this.hostField.TabIndex = 11; + this.hostField.Text = ""; + + // + // portField + // + this.portField.Location = new System.Drawing.Point(56, 336); + this.portField.Name = "portField"; + this.portField.ReadOnly = true; + this.portField.Size = new System.Drawing.Size(168, 20); + this.portField.TabIndex = 12; + this.portField.Text = ""; + // + // label5 + // + this.label5.Location = new System.Drawing.Point(8, 336); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(48, 16); + this.label5.TabIndex = 14; + this.label5.Text = "Port:"; + this.label5.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // serviceTextField + // + this.serviceTextField.HorizontalScrollbar = true; + this.serviceTextField.Location = new System.Drawing.Point(264, 208); + this.serviceTextField.Name = "serviceTextField"; + this.serviceTextField.Size = new System.Drawing.Size(232, 147); + this.serviceTextField.TabIndex = 16; + // + // Form1 + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(512, 365); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.serviceTextField, + this.label5, + this.portField, + this.hostField, + this.domainField, + this.typeField, + this.nameField, + this.label4, + this.label3, + this.label2, + this.label1, + this.typeBox, + this.browseList}); + this.Name = "Form1"; + this.Text = "DNSServices Browser"; + this.ResumeLayout(false); + + } + #endregion + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.Run(new Form1()); + } + // + // BrowseData + // + // This class is used to store data associated + // with a DNSService.Browse() operation + // + public class BrowseData + { + public int InterfaceIndex; + public String Name; + public String Type; + public String Domain; + + public override String + ToString() + { + return Name; + } + + public override bool + Equals(object other) + { + bool result = false; + + if (other != null) + { + if ((object) this == other) + { + result = true; + } + else if (other is BrowseData) + { + BrowseData otherBrowseData = (BrowseData) other; + + result = (this.Name == otherBrowseData.Name); + } + } + + return result; + } + + public override int + GetHashCode() + { + return Name.GetHashCode(); + } + }; + + + // + // ResolveData + // + // This class is used to store data associated + // with a DNSService.Resolve() operation + // + public class ResolveData + { + public int InterfaceIndex; + public String FullName; + public String HostName; + public int Port; + public Byte[] TxtRecord; + + public override String + ToString() + { + return FullName; + } + }; + + // + // Populate() + // + // Populate this form with data associated with a + // DNSService.Resolve() call + // + public void + Populate(BrowseData browseData, ResolveData resolveData) + { + nameField.Text = browseData.Name; + typeField.Text = browseData.Type; + domainField.Text = browseData.Domain; + hostField.Text = resolveData.HostName; + portField.Text = resolveData.Port.ToString(); + + serviceTextField.Items.Clear(); + + if (resolveData.TxtRecord != null) + { + for (int idx = 0; idx < TextRecord.GetCount(resolveData.TxtRecord); idx++) + { + String key; + Byte[] bytes; + + bytes = TextRecord.GetItemAtIndex(resolveData.TxtRecord, idx, out key); + + if (key.Length > 0) + { + String val = ""; + + if (bytes != null) + { + val = Encoding.ASCII.GetString(bytes, 0, bytes.Length); + } + + serviceTextField.Items.Add(key + "=" + val); + } + } + } + } + + // + // called when the type field changes + // + private void typeBox_SelectedIndexChanged(object sender, System.EventArgs e) + { + browseList.Items.Clear(); + + if (browser != null) + { + browser.Dispose(); + } + + nameField.Text = ""; + typeField.Text = ""; + domainField.Text = ""; + hostField.Text = ""; + portField.Text = ""; + serviceTextField.Items.Clear(); + + try + { + browser = DNSService.Browse(0, 0, typeBox.SelectedItem.ToString(), null, new DNSService.BrowseReply(OnBrowseReply)); + } + catch + { + MessageBox.Show("Browse Failed", "Error"); + Application.Exit(); + } + } + + private void listBox1_SelectedIndexChanged(object sender, System.EventArgs e) + { + if (resolver != null) + { + resolver.Dispose(); + } + + if (browseList.SelectedItem != null) + { + try + { + BrowseData data = (BrowseData) browseList.SelectedItem; + + resolver = DNSService.Resolve(0, 0, data.Name, data.Type, data.Domain, new DNSService.ResolveReply(OnResolveReply)); + } + catch + { + MessageBox.Show("Resolve Failed", "Error"); + Application.Exit(); + } + } + } + + // + // OnAddService + // + // This method is "Invoked" by OnBrowseReply. This call + // executes in the context of the main thread + // + private void OnAddService + ( + BrowseData data + ) + { + browseList.Items.Add(data); + browseList.Invalidate(); + } + + // + // OnRemoveService + // + // This method is "Invoked" by OnBrowseReply. This call + // executes in the context of the main thread + // + private void OnRemoveService + ( + BrowseData data + ) + { + browseList.Items.Remove(data); + browseList.Invalidate(); + } + + // + // OnResolveService + // + // This method is "Invoked" by OnResolveReply. This call + // executes in the context of the main thread + // + private void OnResolveService + ( + ResolveData data + ) + { + resolver.Dispose(); + resolver = null; + + Populate((BrowseData) browseList.SelectedItem, data); + } + + // + // OnBrowseReply + // + // This call is invoked by the DNSService core. It is + // executed in the context of a worker thread, not the + // main (GUI) thread. We create a BrowseData object + // and invoked the appropriate method in the GUI thread + // so we can update the UI + // + private void OnBrowseReply + ( + ServiceRef sdRef, + ServiceFlags flags, + int interfaceIndex, + ErrorCode errorCode, + String name, + String type, + String domain + ) + { + if (errorCode == ErrorCode.NoError) + { + BrowseData data = new BrowseData(); + + data.InterfaceIndex = interfaceIndex; + data.Name = name; + data.Type = type; + data.Domain = domain; + + if ((flags & ServiceFlags.Add) != 0) + { + Invoke(addServiceCallback, new Object[]{data}); + + } + else if ((flags == 0) || ((flags & ServiceFlags.MoreComing) != 0)) + { + Invoke(removeServiceCallback, new Object[]{data}); + } + } + else + { + MessageBox.Show("OnBrowseReply returned an error code: " + errorCode, "Error"); + } + } + + private void OnResolveReply + ( + ServiceRef sdRef, + ServiceFlags flags, + int interfaceIndex, + ErrorCode errorCode, + String fullName, + String hostName, + int port, + Byte[] txtRecord + ) + { + if (errorCode == ErrorCode.NoError) + { + ResolveData data = new ResolveData(); + + data.InterfaceIndex = interfaceIndex; + data.FullName = fullName; + data.HostName = hostName; + data.Port = port; + data.TxtRecord = txtRecord; + + Invoke(resolveServiceCallback, new Object[]{data}); + } + else + { + MessageBox.Show("OnResolveReply returned an error code: " + errorCode, "Error"); + } + } + } +} diff --git a/Clients/DNSServiceBrowser.NET/DNSServiceBrowser.resx b/Clients/DNSServiceBrowser.NET/DNSServiceBrowser.resx new file mode 100755 index 0000000..e5b5a11 --- /dev/null +++ b/Clients/DNSServiceBrowser.NET/DNSServiceBrowser.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Form1 + + \ No newline at end of file diff --git a/Clients/DNSServiceBrowser.m b/Clients/DNSServiceBrowser.m index f2a38c8..30db5a8 100755 --- a/Clients/DNSServiceBrowser.m +++ b/Clients/DNSServiceBrowser.m @@ -3,8 +3,6 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in diff --git a/Clients/DNSServiceReg-Info.plist b/Clients/DNSServiceReg-Info.plist new file mode 100644 index 0000000..2569968 --- /dev/null +++ b/Clients/DNSServiceReg-Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + DNS Service Registration + CFBundleGetInfoString + + CFBundleIconFile + + CFBundleIdentifier + com.apple.DNS_Service_Registration + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleShortVersionString + + CFBundleSignature + ???? + CFBundleVersion + 1.0.0d1 + NSMainNibFile + DNSServiceReg + NSPrincipalClass + NSApplication + + diff --git a/Clients/DNSServiceReg.m b/Clients/DNSServiceReg.m new file mode 100644 index 0000000..c4a3ee7 --- /dev/null +++ b/Clients/DNSServiceReg.m @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: DNSServiceReg.m,v $ +Revision 1.15 2004/06/05 02:01:08 cheshire +Move DNSServiceRegistration from mDNSMacOSX directory to Clients directory + +Revision 1.14 2004/03/04 19:20:23 cheshire +Remove invalid UTF-8 character + +Revision 1.13 2003/08/12 19:55:07 cheshire +Update to APSL 2.0 + + */ + +#include "dns_sd.h" + +@interface RegistrationController : NSObject +{ + IBOutlet NSTableColumn *typeColumn; + IBOutlet NSTableColumn *nameColumn; + IBOutlet NSTableColumn *portColumn; + IBOutlet NSTableColumn *domainColumn; + IBOutlet NSTableColumn *textColumn; + + IBOutlet NSTableView *serviceDisplayTable; + + IBOutlet NSTextField *serviceTypeField; + IBOutlet NSTextField *serviceNameField; + IBOutlet NSTextField *servicePortField; + IBOutlet NSTextField *serviceDomainField; + IBOutlet NSTextField *serviceTextField; + + NSMutableArray *srvtypeKeys; + NSMutableArray *srvnameKeys; + NSMutableArray *srvportKeys; + NSMutableArray *srvdomainKeys; + NSMutableArray *srvtextKeys; + + NSMutableDictionary *registeredDict; +} + +- (IBAction)registerService:(id)sender; +- (IBAction)unregisterService:(id)sender; + +- (IBAction)addNewService:(id)sender; +- (IBAction)removeSelected:(id)sender; + +@end + +void reg_reply + ( + DNSServiceRef sdRef, + DNSServiceFlags flags, + DNSServiceErrorType errorCode, + const char *name, + const char *regtype, + const char *domain, + void *context + ) +{ + // registration reply + printf("Got a reply from the server with error %d\n", errorCode); + return; +} + +static void myCFSocketCallBack(CFSocketRef cfs, CFSocketCallBackType CallBackType, CFDataRef address, const void *data, void *context) + { + DNSServiceProcessResult((DNSServiceRef)context); + } + +static void addDNSServiceRefToRunLoop(DNSServiceRef ref) + { + int s = DNSServiceRefSockFD(ref); + CFSocketContext myCFSocketContext = { 0, ref, NULL, NULL, NULL }; + CFSocketRef c = CFSocketCreateWithNative(kCFAllocatorDefault, s, kCFSocketReadCallBack, myCFSocketCallBack, &myCFSocketContext); + CFRunLoopSourceRef rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, c, 0); + CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); + CFRelease(rls); + } + + +@implementation RegistrationController + +- (void)registerDefaults +{ + NSMutableDictionary *regDict = [NSMutableDictionary dictionary]; + + NSArray *typeArray = [NSArray arrayWithObjects:@"_ftp._tcp.", @"_ssh._tcp.", @"_tftp._tcp.", @"_http._tcp.", @"_printer._tcp.", @"_afpovertcp._tcp.", nil]; + NSArray *nameArray = [NSArray arrayWithObjects:@"My ftp Server", @"My Computer", @"Testing Boot Image", @"A Web Server", @"Steve's Printer", @"Company AppleShare Server", nil]; + NSArray *portArray = [NSArray arrayWithObjects:@"21", @"22", @"69", @"80", @"515", @"548", nil]; + NSArray *domainArray = [NSArray arrayWithObjects:@"", @"", @"", @"", @"", @"", nil]; + NSArray *textArray = [NSArray arrayWithObjects:@"", @"", @"image=mybootimage", @"path=/index.html", @"rn=lpt1", @"Vol=Public", nil]; + + [regDict setObject:typeArray forKey:@"SrvTypeKeys"]; + [regDict setObject:nameArray forKey:@"SrvNameKeys"]; + [regDict setObject:portArray forKey:@"SrvPortKeys"]; + [regDict setObject:domainArray forKey:@"SrvDomainKeys"]; + [regDict setObject:textArray forKey:@"SrvTextKeys"]; + + [[NSUserDefaults standardUserDefaults] registerDefaults:regDict]; +} + +- (id)init +{ + srvtypeKeys = [[NSMutableArray array] retain]; //Define arrays for Type, Domain, and Name + srvnameKeys = [[NSMutableArray array] retain]; + srvportKeys = [[NSMutableArray array] retain]; + srvdomainKeys = [[NSMutableArray array] retain]; + srvtextKeys = [[NSMutableArray array] retain]; + + registeredDict = [[NSMutableDictionary alloc] init]; + + [self registerDefaults]; + return [super init]; +} + +- (void)awakeFromNib //BrowserController startup procedure +{ + [srvtypeKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvTypeKeys"]]; + [srvnameKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvNameKeys"]]; + [srvportKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvPortKeys"]]; + [srvdomainKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvDomainKeys"]]; + [srvtextKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvTextKeys"]]; + + [serviceDisplayTable reloadData]; //Reload (redraw) data in fields + +} + + + + - (IBAction)registerService:(id)sender +{ + int selectedRow = [serviceDisplayTable selectedRow]; + CFMachPortContext context; + DNSServiceRef dns_client; + + if (selectedRow < 0) { + return; + } + + NSString *key = [srvtypeKeys objectAtIndex:selectedRow]; + if ([registeredDict objectForKey:key]) { printf("Already registered\n"); return; } + + context.version = 1; + context.info = 0; + context.retain = NULL; + context.release = NULL; + context.copyDescription = NULL; + unsigned char txtbuffer[300]; + strncpy(txtbuffer+1, [[srvtextKeys objectAtIndex:selectedRow] UTF8String], sizeof(txtbuffer)-1); + txtbuffer[0] = strlen(txtbuffer+1); + + DNSServiceErrorType err = DNSServiceRegister + ( + &dns_client, 0, 0, + [[srvnameKeys objectAtIndex:selectedRow] UTF8String], + [key UTF8String], + [[srvdomainKeys objectAtIndex:selectedRow] UTF8String], + NULL, htons([[srvportKeys objectAtIndex:selectedRow] intValue]), + txtbuffer[0]+1, txtbuffer, + reg_reply, + nil + ); + if (err) + printf("DNSServiceRegister failed %d\n", err); + else + { + addDNSServiceRefToRunLoop(dns_client); + [registeredDict setObject:[NSNumber numberWithUnsignedInt:(unsigned int)dns_client] forKey:key]; + } +} + +- (IBAction)unregisterService:(id)sender +{ + int selectedRow = [serviceDisplayTable selectedRow]; + NSString *key = [srvtypeKeys objectAtIndex:selectedRow]; + + NSNumber *refPtr = [registeredDict objectForKey:key]; + DNSServiceRef ref = (DNSServiceRef)[refPtr unsignedIntValue]; + + if (ref) { + DNSServiceRefDeallocate(ref); + [registeredDict removeObjectForKey:key]; + } +} + +-(void)tableView:(NSTableView *)theTableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(int)row +{ + if (row<0) return; +} + +- (int)numberOfRowsInTableView:(NSTableView *)theTableView //Begin mandatory TableView methods +{ + return [srvtypeKeys count]; +} + +- (id)tableView:(NSTableView *)theTableView objectValueForTableColumn:(NSTableColumn *)theColumn row:(int)rowIndex +{ + if (theColumn == typeColumn) { + return [srvtypeKeys objectAtIndex:rowIndex]; + } + if (theColumn == nameColumn) { + return [srvnameKeys objectAtIndex:rowIndex]; + } + if (theColumn == portColumn) { + return [srvportKeys objectAtIndex:rowIndex]; + } + if (theColumn == domainColumn) { + return [srvdomainKeys objectAtIndex:rowIndex]; + } + if (theColumn == textColumn) { + return [srvtextKeys objectAtIndex:rowIndex]; + } + + return(0); +} //End of mandatory TableView methods + +- (IBAction)removeSelected:(id)sender +{ + // remove the selected row and force a refresh + + int selectedRow = [serviceDisplayTable selectedRow]; + + if (selectedRow) { + + [srvtypeKeys removeObjectAtIndex:selectedRow]; + [srvnameKeys removeObjectAtIndex:selectedRow]; + [srvportKeys removeObjectAtIndex:selectedRow]; + [srvdomainKeys removeObjectAtIndex:selectedRow]; + [srvtextKeys removeObjectAtIndex:selectedRow]; + + [[NSUserDefaults standardUserDefaults] setObject:srvtypeKeys forKey:@"SrvTypeKeys"]; + [[NSUserDefaults standardUserDefaults] setObject:srvnameKeys forKey:@"SrvNameKeys"]; + [[NSUserDefaults standardUserDefaults] setObject:srvportKeys forKey:@"SrvPortKeys"]; + [[NSUserDefaults standardUserDefaults] setObject:srvdomainKeys forKey:@"SrvDomainKeys"]; + [[NSUserDefaults standardUserDefaults] setObject:srvtextKeys forKey:@"SrvTextKeys"]; + + [serviceDisplayTable reloadData]; + } +} + +- (IBAction)addNewService:(id)sender +{ + // add new entries from the edit fields to the arrays for the defaults + + if ([[serviceTypeField stringValue] length] && [[serviceNameField stringValue] length] && [[serviceDomainField stringValue] length]&& [[servicePortField stringValue] length]) { + [srvtypeKeys addObject:[serviceTypeField stringValue]]; + [srvnameKeys addObject:[serviceNameField stringValue]]; + [srvportKeys addObject:[servicePortField stringValue]]; + [srvdomainKeys addObject:[serviceDomainField stringValue]]; + [srvtextKeys addObject:[serviceTextField stringValue]]; + + [[NSUserDefaults standardUserDefaults] setObject:srvtypeKeys forKey:@"SrvTypeKeys"]; + [[NSUserDefaults standardUserDefaults] setObject:srvnameKeys forKey:@"SrvNameKeys"]; + [[NSUserDefaults standardUserDefaults] setObject:srvportKeys forKey:@"SrvPortKeys"]; + [[NSUserDefaults standardUserDefaults] setObject:srvdomainKeys forKey:@"SrvDomainKeys"]; + [[NSUserDefaults standardUserDefaults] setObject:srvtextKeys forKey:@"SrvTextKeys"]; + + [serviceDisplayTable reloadData]; + } else { + NSBeep(); + } + +} + +@end + +int main(int argc, const char *argv[]) +{ + return NSApplicationMain(argc, argv); +} diff --git a/Clients/DNSServiceReg.nib/classes.nib b/Clients/DNSServiceReg.nib/classes.nib new file mode 100644 index 0000000..46f466c --- /dev/null +++ b/Clients/DNSServiceReg.nib/classes.nib @@ -0,0 +1,30 @@ +{ + IBClasses = ( + {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, + { + ACTIONS = { + addNewService = id; + registerService = id; + removeSelected = id; + unregisterService = id; + }; + CLASS = RegistrationController; + LANGUAGE = ObjC; + OUTLETS = { + domainColumn = NSTableColumn; + nameColumn = NSTableColumn; + portColumn = NSTableColumn; + serviceDisplayTable = NSTableView; + serviceDomainField = NSTextField; + serviceNameField = NSTextField; + servicePortField = NSTextField; + serviceTextField = NSTextField; + serviceTypeField = NSTextField; + textColumn = NSTableColumn; + typeColumn = NSTableColumn; + }; + SUPERCLASS = NSObject; + } + ); + IBVersion = 1; +} \ No newline at end of file diff --git a/Clients/DNSServiceReg.nib/info.nib b/Clients/DNSServiceReg.nib/info.nib new file mode 100644 index 0000000..9d2eb74 --- /dev/null +++ b/Clients/DNSServiceReg.nib/info.nib @@ -0,0 +1,22 @@ + + + + + IBDocumentLocation + 32 111 356 240 0 0 1152 746 + IBEditorPositions + + 29 + 103 609 252 44 0 0 1152 746 + + IBFramework Version + 273.0 + IBOpenObjects + + 243 + 21 + + IBSystem Version + 6C30 + + diff --git a/Clients/DNSServiceReg.nib/objects.nib b/Clients/DNSServiceReg.nib/objects.nib new file mode 100644 index 0000000..705e77d Binary files /dev/null and b/Clients/DNSServiceReg.nib/objects.nib differ diff --git a/Clients/DNSServiceRegistration-Info.plist b/Clients/DNSServiceRegistration-Info.plist deleted file mode 100644 index fafaa99..0000000 --- a/Clients/DNSServiceRegistration-Info.plist +++ /dev/null @@ -1,30 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - DNS Service Registration - CFBundleGetInfoString - - CFBundleIconFile - - CFBundleIdentifier - com.apple.DNS_Service_Registration - CFBundleInfoDictionaryVersion - 6.0 - CFBundlePackageType - APPL - CFBundleShortVersionString - - CFBundleSignature - ???? - CFBundleVersion - 1.0.0d1 - NSMainNibFile - DNSServiceRegistration - NSPrincipalClass - NSApplication - - diff --git a/Clients/DNSServiceRegistration.m b/Clients/DNSServiceRegistration.m deleted file mode 100644 index 3b50a07..0000000 --- a/Clients/DNSServiceRegistration.m +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - - Change History (most recent first): - -$Log: DNSServiceRegistration.m,v $ -Revision 1.15 2004/06/05 02:01:08 cheshire -Move DNSServiceRegistration from mDNSMacOSX directory to Clients directory - -Revision 1.14 2004/03/04 19:20:23 cheshire -Remove invalid UTF-8 character - -Revision 1.13 2003/08/12 19:55:07 cheshire -Update to APSL 2.0 - - */ - -#include "dns_sd.h" - -@interface RegistrationController : NSObject -{ - IBOutlet NSTableColumn *typeColumn; - IBOutlet NSTableColumn *nameColumn; - IBOutlet NSTableColumn *portColumn; - IBOutlet NSTableColumn *domainColumn; - IBOutlet NSTableColumn *textColumn; - - IBOutlet NSTableView *serviceDisplayTable; - - IBOutlet NSTextField *serviceTypeField; - IBOutlet NSTextField *serviceNameField; - IBOutlet NSTextField *servicePortField; - IBOutlet NSTextField *serviceDomainField; - IBOutlet NSTextField *serviceTextField; - - NSMutableArray *srvtypeKeys; - NSMutableArray *srvnameKeys; - NSMutableArray *srvportKeys; - NSMutableArray *srvdomainKeys; - NSMutableArray *srvtextKeys; - - NSMutableDictionary *registeredDict; -} - -- (IBAction)registerService:(id)sender; -- (IBAction)unregisterService:(id)sender; - -- (IBAction)addNewService:(id)sender; -- (IBAction)removeSelected:(id)sender; - -@end - -void reg_reply - ( - DNSServiceRef sdRef, - DNSServiceFlags flags, - DNSServiceErrorType errorCode, - const char *name, - const char *regtype, - const char *domain, - void *context - ) -{ - // registration reply - printf("Got a reply from the server with error %d\n", errorCode); - return; -} - -static void myCFSocketCallBack(CFSocketRef cfs, CFSocketCallBackType CallBackType, CFDataRef address, const void *data, void *context) - { - DNSServiceProcessResult((DNSServiceRef)context); - } - -static void addDNSServiceRefToRunLoop(DNSServiceRef ref) - { - int s = DNSServiceRefSockFD(ref); - CFSocketContext myCFSocketContext = { 0, ref, NULL, NULL, NULL }; - CFSocketRef c = CFSocketCreateWithNative(kCFAllocatorDefault, s, kCFSocketReadCallBack, myCFSocketCallBack, &myCFSocketContext); - CFRunLoopSourceRef rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, c, 0); - CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); - CFRelease(rls); - } - - -@implementation RegistrationController - -- (void)registerDefaults -{ - NSMutableDictionary *regDict = [NSMutableDictionary dictionary]; - - NSArray *typeArray = [NSArray arrayWithObjects:@"_ftp._tcp.", @"_ssh._tcp.", @"_tftp._tcp.", @"_http._tcp.", @"_printer._tcp.", @"_afpovertcp._tcp.", nil]; - NSArray *nameArray = [NSArray arrayWithObjects:@"My ftp Server", @"My Computer", @"Testing Boot Image", @"A Web Server", @"Steve's Printer", @"Company AppleShare Server", nil]; - NSArray *portArray = [NSArray arrayWithObjects:@"21", @"22", @"69", @"80", @"515", @"548", nil]; - NSArray *domainArray = [NSArray arrayWithObjects:@"", @"", @"", @"", @"", @"", nil]; - NSArray *textArray = [NSArray arrayWithObjects:@"", @"", @"image=mybootimage", @"path=/index.html", @"rn=lpt1", @"Vol=Public", nil]; - - [regDict setObject:typeArray forKey:@"SrvTypeKeys"]; - [regDict setObject:nameArray forKey:@"SrvNameKeys"]; - [regDict setObject:portArray forKey:@"SrvPortKeys"]; - [regDict setObject:domainArray forKey:@"SrvDomainKeys"]; - [regDict setObject:textArray forKey:@"SrvTextKeys"]; - - [[NSUserDefaults standardUserDefaults] registerDefaults:regDict]; -} - -- (id)init -{ - srvtypeKeys = [[NSMutableArray array] retain]; //Define arrays for Type, Domain, and Name - srvnameKeys = [[NSMutableArray array] retain]; - srvportKeys = [[NSMutableArray array] retain]; - srvdomainKeys = [[NSMutableArray array] retain]; - srvtextKeys = [[NSMutableArray array] retain]; - - registeredDict = [[NSMutableDictionary alloc] init]; - - [self registerDefaults]; - return [super init]; -} - -- (void)awakeFromNib //BrowserController startup procedure -{ - [srvtypeKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvTypeKeys"]]; - [srvnameKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvNameKeys"]]; - [srvportKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvPortKeys"]]; - [srvdomainKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvDomainKeys"]]; - [srvtextKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvTextKeys"]]; - - [serviceDisplayTable reloadData]; //Reload (redraw) data in fields - -} - - - - - (IBAction)registerService:(id)sender -{ - int selectedRow = [serviceDisplayTable selectedRow]; - CFMachPortContext context; - DNSServiceRef dns_client; - - if (selectedRow < 0) { - return; - } - - NSString *key = [srvtypeKeys objectAtIndex:selectedRow]; - if ([registeredDict objectForKey:key]) { printf("Already registered\n"); return; } - - context.version = 1; - context.info = 0; - context.retain = NULL; - context.release = NULL; - context.copyDescription = NULL; - unsigned char txtbuffer[300]; - strncpy(txtbuffer+1, [[srvtextKeys objectAtIndex:selectedRow] UTF8String], sizeof(txtbuffer)-1); - txtbuffer[0] = strlen(txtbuffer+1); - - DNSServiceErrorType err = DNSServiceRegister - ( - &dns_client, 0, 0, - [[srvnameKeys objectAtIndex:selectedRow] UTF8String], - [key UTF8String], - [[srvdomainKeys objectAtIndex:selectedRow] UTF8String], - NULL, htons([[srvportKeys objectAtIndex:selectedRow] intValue]), - txtbuffer[0]+1, txtbuffer, - reg_reply, - nil - ); - if (err) - printf("DNSServiceRegister failed %d\n", err); - else - { - addDNSServiceRefToRunLoop(dns_client); - [registeredDict setObject:[NSNumber numberWithUnsignedInt:(unsigned int)dns_client] forKey:key]; - } -} - -- (IBAction)unregisterService:(id)sender -{ - int selectedRow = [serviceDisplayTable selectedRow]; - NSString *key = [srvtypeKeys objectAtIndex:selectedRow]; - - NSNumber *refPtr = [registeredDict objectForKey:key]; - DNSServiceRef ref = (DNSServiceRef)[refPtr unsignedIntValue]; - - if (ref) { - DNSServiceRefDeallocate(ref); - [registeredDict removeObjectForKey:key]; - } -} - --(void)tableView:(NSTableView *)theTableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(int)row -{ - if (row<0) return; -} - -- (int)numberOfRowsInTableView:(NSTableView *)theTableView //Begin mandatory TableView methods -{ - return [srvtypeKeys count]; -} - -- (id)tableView:(NSTableView *)theTableView objectValueForTableColumn:(NSTableColumn *)theColumn row:(int)rowIndex -{ - if (theColumn == typeColumn) { - return [srvtypeKeys objectAtIndex:rowIndex]; - } - if (theColumn == nameColumn) { - return [srvnameKeys objectAtIndex:rowIndex]; - } - if (theColumn == portColumn) { - return [srvportKeys objectAtIndex:rowIndex]; - } - if (theColumn == domainColumn) { - return [srvdomainKeys objectAtIndex:rowIndex]; - } - if (theColumn == textColumn) { - return [srvtextKeys objectAtIndex:rowIndex]; - } - - return(0); -} //End of mandatory TableView methods - -- (IBAction)removeSelected:(id)sender -{ - // remove the selected row and force a refresh - - int selectedRow = [serviceDisplayTable selectedRow]; - - if (selectedRow) { - - [srvtypeKeys removeObjectAtIndex:selectedRow]; - [srvnameKeys removeObjectAtIndex:selectedRow]; - [srvportKeys removeObjectAtIndex:selectedRow]; - [srvdomainKeys removeObjectAtIndex:selectedRow]; - [srvtextKeys removeObjectAtIndex:selectedRow]; - - [[NSUserDefaults standardUserDefaults] setObject:srvtypeKeys forKey:@"SrvTypeKeys"]; - [[NSUserDefaults standardUserDefaults] setObject:srvnameKeys forKey:@"SrvNameKeys"]; - [[NSUserDefaults standardUserDefaults] setObject:srvportKeys forKey:@"SrvPortKeys"]; - [[NSUserDefaults standardUserDefaults] setObject:srvdomainKeys forKey:@"SrvDomainKeys"]; - [[NSUserDefaults standardUserDefaults] setObject:srvtextKeys forKey:@"SrvTextKeys"]; - - [serviceDisplayTable reloadData]; - } -} - -- (IBAction)addNewService:(id)sender -{ - // add new entries from the edit fields to the arrays for the defaults - - if ([[serviceTypeField stringValue] length] && [[serviceNameField stringValue] length] && [[serviceDomainField stringValue] length]&& [[servicePortField stringValue] length]) { - [srvtypeKeys addObject:[serviceTypeField stringValue]]; - [srvnameKeys addObject:[serviceNameField stringValue]]; - [srvportKeys addObject:[servicePortField stringValue]]; - [srvdomainKeys addObject:[serviceDomainField stringValue]]; - [srvtextKeys addObject:[serviceTextField stringValue]]; - - [[NSUserDefaults standardUserDefaults] setObject:srvtypeKeys forKey:@"SrvTypeKeys"]; - [[NSUserDefaults standardUserDefaults] setObject:srvnameKeys forKey:@"SrvNameKeys"]; - [[NSUserDefaults standardUserDefaults] setObject:srvportKeys forKey:@"SrvPortKeys"]; - [[NSUserDefaults standardUserDefaults] setObject:srvdomainKeys forKey:@"SrvDomainKeys"]; - [[NSUserDefaults standardUserDefaults] setObject:srvtextKeys forKey:@"SrvTextKeys"]; - - [serviceDisplayTable reloadData]; - } else { - NSBeep(); - } - -} - -@end - -int main(int argc, const char *argv[]) -{ - return NSApplicationMain(argc, argv); -} diff --git a/Clients/DNSServiceRegistration.nib/classes.nib b/Clients/DNSServiceRegistration.nib/classes.nib deleted file mode 100644 index 46f466c..0000000 --- a/Clients/DNSServiceRegistration.nib/classes.nib +++ /dev/null @@ -1,30 +0,0 @@ -{ - IBClasses = ( - {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, - { - ACTIONS = { - addNewService = id; - registerService = id; - removeSelected = id; - unregisterService = id; - }; - CLASS = RegistrationController; - LANGUAGE = ObjC; - OUTLETS = { - domainColumn = NSTableColumn; - nameColumn = NSTableColumn; - portColumn = NSTableColumn; - serviceDisplayTable = NSTableView; - serviceDomainField = NSTextField; - serviceNameField = NSTextField; - servicePortField = NSTextField; - serviceTextField = NSTextField; - serviceTypeField = NSTextField; - textColumn = NSTableColumn; - typeColumn = NSTableColumn; - }; - SUPERCLASS = NSObject; - } - ); - IBVersion = 1; -} \ No newline at end of file diff --git a/Clients/DNSServiceRegistration.nib/info.nib b/Clients/DNSServiceRegistration.nib/info.nib deleted file mode 100644 index 9d2eb74..0000000 --- a/Clients/DNSServiceRegistration.nib/info.nib +++ /dev/null @@ -1,22 +0,0 @@ - - - - - IBDocumentLocation - 32 111 356 240 0 0 1152 746 - IBEditorPositions - - 29 - 103 609 252 44 0 0 1152 746 - - IBFramework Version - 273.0 - IBOpenObjects - - 243 - 21 - - IBSystem Version - 6C30 - - diff --git a/Clients/DNSServiceRegistration.nib/objects.nib b/Clients/DNSServiceRegistration.nib/objects.nib deleted file mode 100644 index 705e77d..0000000 Binary files a/Clients/DNSServiceRegistration.nib/objects.nib and /dev/null differ diff --git a/Clients/ExplorerPlugin/About.cpp b/Clients/ExplorerPlugin/About.cpp new file mode 100644 index 0000000..37d3281 --- /dev/null +++ b/Clients/ExplorerPlugin/About.cpp @@ -0,0 +1,63 @@ +// About.cpp : implementation file +// + +#include "stdafx.h" +#include "ExplorerPlugin.h" +#include "About.h" + + +// CAbout dialog + +IMPLEMENT_DYNAMIC(CAbout, CDialog) +CAbout::CAbout(CWnd* pParent /*=NULL*/) + : CDialog(CAbout::IDD, pParent) +{ + // Initialize brush with the desired background color + m_bkBrush.CreateSolidBrush(RGB(255, 255, 255)); +} + +CAbout::~CAbout() +{ +} + +void CAbout::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Control(pDX, IDC_COMPONENT, m_componentCtrl); + DDX_Control(pDX, IDC_LEGAL, m_legalCtrl); +} + + +BEGIN_MESSAGE_MAP(CAbout, CDialog) +ON_WM_CTLCOLOR() +END_MESSAGE_MAP() + + +// CAbout message handlers +HBRUSH CAbout::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) +{ + switch (nCtlColor) + { + case CTLCOLOR_STATIC: + + if ( pWnd->GetDlgCtrlID() == IDC_COMPONENT ) + { + pDC->SetTextColor(RGB(64, 64, 64)); + } + else + { + pDC->SetTextColor(RGB(0, 0, 0)); + } + + pDC->SetBkColor(RGB(255, 255, 255)); + return (HBRUSH)(m_bkBrush.GetSafeHandle()); + + case CTLCOLOR_DLG: + + return (HBRUSH)(m_bkBrush.GetSafeHandle()); + + default: + + return CDialog::OnCtlColor(pDC, pWnd, nCtlColor); + } +} diff --git a/Clients/ExplorerPlugin/About.h b/Clients/ExplorerPlugin/About.h new file mode 100644 index 0000000..f4d0d7e --- /dev/null +++ b/Clients/ExplorerPlugin/About.h @@ -0,0 +1,27 @@ +#pragma once + +#include "Resource.h" +#include "afxwin.h" + +// CAbout dialog + +class CAbout : public CDialog +{ + DECLARE_DYNAMIC(CAbout) + +public: + CAbout(CWnd* pParent = NULL); // standard constructor + virtual ~CAbout(); + +// Dialog Data + enum { IDD = IDD_ABOUT }; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor); + DECLARE_MESSAGE_MAP() +public: + CStatic m_componentCtrl; + CStatic m_legalCtrl; + CBrush m_bkBrush; +}; diff --git a/Clients/ExplorerPlugin/ClassFactory.cpp b/Clients/ExplorerPlugin/ClassFactory.cpp new file mode 100644 index 0000000..b4e06c6 --- /dev/null +++ b/Clients/ExplorerPlugin/ClassFactory.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: ClassFactory.cpp,v $ +Revision 1.2 2004/07/13 21:24:21 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:34:59 rpantos +Move to Clients from mDNSWindows + +Revision 1.1 2004/01/30 03:01:56 bradley +Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. + +*/ + +#include "StdAfx.h" + +#include "DebugServices.h" + +#include "ExplorerBar.h" +#include "ExplorerPlugin.h" + +#include "ClassFactory.h" + +// MFC Debugging + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +//=========================================================================================================================== +// ClassFactory +//=========================================================================================================================== + +ClassFactory::ClassFactory( CLSID inCLSID ) +{ + mCLSIDObject = inCLSID; + mRefCount = 1; + ++gDLLRefCount; +} + +//=========================================================================================================================== +// ~ClassFactory +//=========================================================================================================================== + +ClassFactory::~ClassFactory( void ) +{ + check( gDLLRefCount > 0 ); + + --gDLLRefCount; +} + +#if 0 +#pragma mark - +#pragma mark == IUnknown methods == +#endif + +//=========================================================================================================================== +// QueryInterface +//=========================================================================================================================== + +STDMETHODIMP ClassFactory::QueryInterface( REFIID inID, LPVOID *outResult ) +{ + HRESULT err; + + check( outResult ); + + if( IsEqualIID( inID, IID_IUnknown ) ) + { + *outResult = this; + } + else if( IsEqualIID( inID, IID_IClassFactory ) ) + { + *outResult = (IClassFactory *) this; + } + else + { + *outResult = NULL; + err = E_NOINTERFACE; + goto exit; + } + + ( *( (LPUNKNOWN *) outResult ) )->AddRef(); + err = S_OK; + +exit: + return( err ); +} + +//=========================================================================================================================== +// AddRef +//=========================================================================================================================== + +STDMETHODIMP_( DWORD ) ClassFactory::AddRef( void ) +{ + return( ++mRefCount ); +} + +//=========================================================================================================================== +// Release +//=========================================================================================================================== + +STDMETHODIMP_( DWORD ) ClassFactory::Release( void ) +{ + DWORD count; + + count = --mRefCount; + if( count == 0 ) + { + delete this; + } + return( count ); +} + +#if 0 +#pragma mark - +#pragma mark == IClassFactory methods == +#endif + +//=========================================================================================================================== +// CreateInstance +//=========================================================================================================================== + +STDMETHODIMP ClassFactory::CreateInstance( LPUNKNOWN inUnknown, REFIID inID, LPVOID *outObject ) +{ + HRESULT err; + LPVOID obj; + + check( outObject ); + + obj = NULL; + *outObject = NULL; + require_action( !inUnknown, exit, err = CLASS_E_NOAGGREGATION ); + + // Create the object based on the CLSID. + + if( IsEqualCLSID( mCLSIDObject, CLSID_ExplorerBar ) ) + { + try + { + obj = new ExplorerBar(); + } + catch( ... ) + { + // Don't let exception escape. + } + require_action( obj, exit, err = E_OUTOFMEMORY ); + } + else + { + err = E_FAIL; + goto exit; + } + + // Query for the specified interface. Release the factory since QueryInterface retains it. + + err = ( (LPUNKNOWN ) obj )->QueryInterface( inID, outObject ); + ( (LPUNKNOWN ) obj )->Release(); + +exit: + return( err ); +} + +//=========================================================================================================================== +// LockServer +//=========================================================================================================================== + +STDMETHODIMP ClassFactory::LockServer( BOOL inLock ) +{ + DEBUG_UNUSED( inLock ); + + return( E_NOTIMPL ); +} diff --git a/Clients/ExplorerPlugin/ClassFactory.h b/Clients/ExplorerPlugin/ClassFactory.h new file mode 100644 index 0000000..76e4b5e --- /dev/null +++ b/Clients/ExplorerPlugin/ClassFactory.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: ClassFactory.h,v $ +Revision 1.2 2004/07/13 21:24:21 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:34:59 rpantos +Move to Clients from mDNSWindows + +Revision 1.1 2004/01/30 03:01:56 bradley +Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. + +*/ + +#ifndef __CLASS_FACTORY__ +#define __CLASS_FACTORY__ + +#include "StdAfx.h" + +//=========================================================================================================================== +// ClassFactory +//=========================================================================================================================== + +class ClassFactory : public IClassFactory +{ + protected: + + DWORD mRefCount; + CLSID mCLSIDObject; + + public: + + ClassFactory( CLSID inCLSID ); + ~ClassFactory( void ); + + // IUnknown methods + + STDMETHODIMP QueryInterface( REFIID inID, LPVOID *outResult ); + STDMETHODIMP_( DWORD ) AddRef( void ); + STDMETHODIMP_( DWORD ) Release( void ); + + // IClassFactory methods + + STDMETHODIMP CreateInstance( LPUNKNOWN inUnknown, REFIID inID, LPVOID *outObject ); + STDMETHODIMP LockServer( BOOL inLock ); +}; + +#endif // __CLASS_FACTORY__ diff --git a/Clients/ExplorerPlugin/ExplorerBar.cpp b/Clients/ExplorerPlugin/ExplorerBar.cpp new file mode 100644 index 0000000..1c71c9b --- /dev/null +++ b/Clients/ExplorerPlugin/ExplorerBar.cpp @@ -0,0 +1,681 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: ExplorerBar.cpp,v $ +Revision 1.3 2004/07/26 05:44:08 shersche +remove extraneous debug statement + +Revision 1.2 2004/07/13 21:24:21 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:34:59 rpantos +Move to Clients from mDNSWindows + +Revision 1.1 2004/01/30 03:01:56 bradley +Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. + +*/ + +#include "StdAfx.h" + +#include "comutil.h" +#include "ShObjIdl.h" + +#include "DebugServices.h" + +#include "Resource.h" + +#include "ExplorerBar.h" + +#include "About.h" +// MFC Debugging + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +//=========================================================================================================================== +// Constants +//=========================================================================================================================== +#define LENGTHOF(a) (sizeof(a) == sizeof(&*a)) ? 0 : (sizeof(a) / sizeof(*a)) +#define MIN_SIZE_X 10 +#define MIN_SIZE_Y 10 + +//=========================================================================================================================== +// ExplorerBar +//=========================================================================================================================== + +ExplorerBar::ExplorerBar( void ) +{ + ++gDLLRefCount; + + mRefCount = 1; + mSite = NULL; + mWebBrowser = NULL; + mParentWindow = NULL; + mFocus = FALSE; + mViewMode = 0; + mBandID = 0; +} + +//=========================================================================================================================== +// ~ExplorerBar +//=========================================================================================================================== + +ExplorerBar::~ExplorerBar( void ) +{ + if( mWebBrowser ) + { + mWebBrowser->Release(); + mWebBrowser = NULL; + } + if( mSite ) + { + mSite->Release(); + mSite = NULL; + } + + --gDLLRefCount; +} + +#if 0 +#pragma mark - +#pragma mark == IUnknown implementation == +#endif + +//=========================================================================================================================== +// QueryInterface +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::QueryInterface( REFIID inID, LPVOID *outResult ) +{ + HRESULT err; + + if( IsEqualIID( inID, IID_IUnknown ) ) // IUnknown + { + *outResult = this; + } + else if( IsEqualIID( inID, IID_IOleWindow ) ) // IOleWindow + { + *outResult = (IOleWindow *) this; + } + else if( IsEqualIID( inID, IID_IDockingWindow ) ) // IDockingWindow + { + *outResult = (IDockingWindow *) this; + } + else if( IsEqualIID( inID, IID_IDeskBand ) ) // IDeskBand + { + *outResult = (IDeskBand *) this; + } + else if( IsEqualIID( inID, IID_IInputObject ) ) // IInputObject + { + *outResult = (IInputObject *) this; + } + else if( IsEqualIID( inID, IID_IObjectWithSite ) ) // IObjectWithSite + { + *outResult = (IObjectWithSite *) this; + } + else if( IsEqualIID( inID, IID_IPersistStream ) ) // IPersistStream + { + *outResult = (IPersistStream *) this; + } + else if( IsEqualIID( inID, IID_IContextMenu ) ) // IContextMenu + { + *outResult = (IContextMenu *) this; + } + else + { + *outResult = NULL; + err = E_NOINTERFACE; + goto exit; + } + + ( *( (LPUNKNOWN *) outResult ) )->AddRef(); + err = S_OK; + +exit: + return( err ); +} + +//=========================================================================================================================== +// AddRef +//=========================================================================================================================== + +STDMETHODIMP_( DWORD ) ExplorerBar::AddRef( void ) +{ + return( ++mRefCount ); +} + +//=========================================================================================================================== +// Release +//=========================================================================================================================== + +STDMETHODIMP_( DWORD ) ExplorerBar::Release( void ) +{ + DWORD count; + + count = --mRefCount; + if( count == 0 ) + { + delete this; + } + return( count ); +} + +#if 0 +#pragma mark - +#pragma mark == IOleWindow implementation == +#endif + +//=========================================================================================================================== +// GetWindow +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::GetWindow( HWND *outWindow ) +{ + *outWindow = mWindow.GetSafeHwnd(); + return( S_OK ); +} + +//=========================================================================================================================== +// ContextSensitiveHelp +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::ContextSensitiveHelp( BOOL inEnterMode ) +{ + DEBUG_UNUSED( inEnterMode ); + + return( E_NOTIMPL ); +} + +#if 0 +#pragma mark - +#pragma mark == IDockingWindow implementation == +#endif + +//=========================================================================================================================== +// ShowDW +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::ShowDW( BOOL inShow ) +{ + if( mWindow.GetSafeHwnd() ) + { + mWindow.ShowWindow( inShow ? SW_SHOW : SW_HIDE ); + } + return( S_OK ); +} + +//=========================================================================================================================== +// CloseDW +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::CloseDW( DWORD inReserved ) +{ + DEBUG_UNUSED( inReserved ); + + ShowDW( FALSE ); + if( mWindow.GetSafeHwnd() ) + { + mWindow.SendMessage( WM_CLOSE ); + } + return( S_OK ); +} + +//=========================================================================================================================== +// ResizeBorderDW +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::ResizeBorderDW( LPCRECT inBorder, IUnknown *inPunkSite, BOOL inReserved ) +{ + DEBUG_UNUSED( inBorder ); + DEBUG_UNUSED( inPunkSite ); + DEBUG_UNUSED( inReserved ); + + return( E_NOTIMPL ); +} + +#if 0 +#pragma mark - +#pragma mark == IDeskBand implementation == +#endif + +//=========================================================================================================================== +// GetBandInfo +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::GetBandInfo( DWORD inBandID, DWORD inViewMode, DESKBANDINFO *outInfo ) +{ + HRESULT err; + + require_action( outInfo, exit, err = E_INVALIDARG ); + + mBandID = inBandID; + mViewMode = inViewMode; + + if( outInfo->dwMask & DBIM_MINSIZE ) + { + outInfo->ptMinSize.x = 100; + outInfo->ptMinSize.y = 100; + } + if( outInfo->dwMask & DBIM_MAXSIZE ) + { + // Unlimited max size. + + outInfo->ptMaxSize.x = -1; + outInfo->ptMaxSize.y = -1; + } + if( outInfo->dwMask & DBIM_INTEGRAL ) + { + outInfo->ptIntegral.x = 1; + outInfo->ptIntegral.y = 1; + } + if( outInfo->dwMask & DBIM_ACTUAL ) + { + outInfo->ptActual.x = 0; + outInfo->ptActual.y = 0; + } + if( outInfo->dwMask & DBIM_TITLE ) + { + CString s; + BOOL ok; + + ok = s.LoadString( IDS_NAME ); + require_action( ok, exit, err = kNoResourcesErr ); + + #ifdef UNICODE + lstrcpyn( outInfo->wszTitle, s, sizeof_array( outInfo->wszTitle ) ); + #else + DWORD nChars; + + nChars = MultiByteToWideChar( CP_ACP, 0, s, -1, outInfo->wszTitle, sizeof_array( outInfo->wszTitle ) ); + err = translate_errno( nChars > 0, (OSStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + #endif + } + if( outInfo->dwMask & DBIM_MODEFLAGS ) + { + outInfo->dwModeFlags = DBIMF_NORMAL | DBIMF_VARIABLEHEIGHT; + } + + // Force the default background color. + + outInfo->dwMask &= ~DBIM_BKCOLOR; + err = S_OK; + +exit: + return( err ); +} + +#if 0 +#pragma mark - +#pragma mark == IInputObject implementation == +#endif + +//=========================================================================================================================== +// UIActivateIO +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::UIActivateIO( BOOL inActivate, LPMSG inMsg ) +{ + DEBUG_UNUSED( inMsg ); + + if( inActivate ) + { + mWindow.SetFocus(); + } + return( S_OK ); +} + +//=========================================================================================================================== +// HasFocusIO +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::HasFocusIO( void ) +{ + if( mWindow.GetFocus()->GetSafeHwnd() == mWindow.GetSafeHwnd() ) + { + return( S_OK ); + } + return( S_FALSE ); +} + +//=========================================================================================================================== +// TranslateAcceleratorIO +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::TranslateAcceleratorIO( LPMSG inMsg ) +{ + DEBUG_UNUSED( inMsg ); + + return( S_FALSE ); +} + +#if 0 +#pragma mark - +#pragma mark == IObjectWithSite implementation == +#endif + +//=========================================================================================================================== +// SetSite +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::SetSite( IUnknown *inPunkSite ) +{ + AFX_MANAGE_STATE( AfxGetStaticModuleState() ); + + HRESULT err; + + // Release the old interfaces. + + if( mWebBrowser ) + { + mWebBrowser->Release(); + mWebBrowser = NULL; + } + if( mSite ) + { + mSite->Release(); + mSite = NULL; + } + + // A non-NULL site means we're setting the site. Otherwise, the site is being released (done above). + + if( !inPunkSite ) + { + err = S_OK; + goto exit; + } + + // Get the parent window. + + IOleWindow * oleWindow; + + mParentWindow = NULL; + err = inPunkSite->QueryInterface( IID_IOleWindow, (LPVOID *) &oleWindow ); + require( SUCCEEDED( err ), exit ); + + err = oleWindow->GetWindow( &mParentWindow ); + oleWindow->Release(); + require_noerr( err, exit ); + require_action( mParentWindow, exit, err = E_FAIL ); + + // Get the IInputObject interface. + + err = inPunkSite->QueryInterface( IID_IInputObjectSite, (LPVOID *) &mSite ); + require( SUCCEEDED( err ), exit ); + check( mSite ); + + // Get the IWebBrowser2 interface. + + IOleCommandTarget * oleCommandTarget; + + err = inPunkSite->QueryInterface( IID_IOleCommandTarget, (LPVOID *) &oleCommandTarget ); + require( SUCCEEDED( err ), exit ); + + IServiceProvider * serviceProvider; + + err = oleCommandTarget->QueryInterface( IID_IServiceProvider, (LPVOID *) &serviceProvider ); + oleCommandTarget->Release(); + require( SUCCEEDED( err ), exit ); + + err = serviceProvider->QueryService( SID_SWebBrowserApp, IID_IWebBrowser2, (LPVOID *) &mWebBrowser ); + serviceProvider->Release(); + require( SUCCEEDED( err ), exit ); + + // Create the main window. + + err = SetupWindow(); + require_noerr( err, exit ); + +exit: + return( err ); +} + +//=========================================================================================================================== +// GetSite +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::GetSite( REFIID inID, LPVOID *outResult ) +{ + HRESULT err; + + *outResult = NULL; + require_action( mSite, exit, err = E_FAIL ); + + err = mSite->QueryInterface( inID, outResult ); + +exit: + return( err ); +} + +#if 0 +#pragma mark - +#pragma mark == IPersistStream implementation == +#endif + +// +// IPersistStream implementation +// +// This is only supported to allow the desk band to be dropped on the desktop and to prevent multiple instances of +// the desk band from showing up in the context menu. This desk band doesn't actually persist any data. +// + +//=========================================================================================================================== +// GetClassID +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::GetClassID( LPCLSID outClassID ) +{ + *outClassID = CLSID_ExplorerBar; + return( S_OK ); +} + +//=========================================================================================================================== +// IsDirty +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::IsDirty( void ) +{ + return( S_FALSE ); +} + +//=========================================================================================================================== +// Load +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::Load( LPSTREAM inStream ) +{ + DEBUG_UNUSED( inStream ); + + return( S_OK ); +} + +//=========================================================================================================================== +// Save +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::Save( LPSTREAM inStream, BOOL inClearDirty ) +{ + DEBUG_UNUSED( inStream ); + DEBUG_UNUSED( inClearDirty ); + + return( S_OK ); +} + +//=========================================================================================================================== +// GetSizeMax +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::GetSizeMax( ULARGE_INTEGER *outSizeMax ) +{ + DEBUG_UNUSED( outSizeMax ); + + return( E_NOTIMPL ); +} + + +//=========================================================================================================================== +// QueryContextMenu +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::QueryContextMenu(HMENU hShellContextMenu, UINT iContextMenuFirstItem, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) +{ + DEBUG_UNUSED( idCmdLast ); + DEBUG_UNUSED( uFlags ); + + CMenu menubar; + + menubar.LoadMenu(IDR_CONTEXT_MENU); + CMenu * menu = menubar.GetSubMenu(0); + + CMenu shellmenu; + + shellmenu.Attach(hShellContextMenu); + + UINT iShellItem = iContextMenuFirstItem; //! remove plus one + UINT idShellCmd = idCmdFirst; + + int n = menu->GetMenuItemCount(); + + for (int i=0; iGetMenuItemInfo(i, &mii, TRUE); + + mii.wID = idShellCmd++; + + shellmenu.InsertMenuItem(iShellItem++, &mii, TRUE); + } + + shellmenu.Detach(); + + return n; +} + + +//=========================================================================================================================== +// GetCommandString +//=========================================================================================================================== + +// Not called for explorer bars +STDMETHODIMP ExplorerBar::GetCommandString(UINT idCmd, UINT uType, UINT* pwReserved, LPSTR pszName, UINT cchMax) +{ + DEBUG_UNUSED( idCmd ); + DEBUG_UNUSED( uType ); + DEBUG_UNUSED( pwReserved ); + DEBUG_UNUSED( pszName ); + DEBUG_UNUSED( cchMax ); + + return E_NOTIMPL; +} + +//=========================================================================================================================== +// InvokeCommand +//=========================================================================================================================== + +// The shell sends either strings or indexes +// IE never sends strings +// The indexes are into an array of my commands +// So the verb for the first command I added to the menu is always 0x0000 +// Here - because I don't have any submenus - +// I can treat the 'verb' as an menu item position +STDMETHODIMP ExplorerBar::InvokeCommand(LPCMINVOKECOMMANDINFO lpici) +{ + // IE doesn't send string commands + if (HIWORD(lpici->lpVerb) != 0) return 0; + + CAbout dlg; + + dlg.DoModal(); + + return S_OK; +} + +#if 0 +#pragma mark - +#pragma mark == Other == +#endif + +//=========================================================================================================================== +// SetupWindow +//=========================================================================================================================== + +OSStatus ExplorerBar::SetupWindow( void ) +{ + OSStatus err; + CWnd * window; + CRect rect; + CString s; + BOOL ok; + + window = CWnd::FromHandle( mParentWindow ); + check( window ); + window->GetClientRect( rect ); + + ok = s.LoadString( IDS_NAME ); + require_action( ok, exit, err = kNoResourcesErr ); + + ok = mWindow.Create( NULL, s, WS_CHILD | WS_VISIBLE, rect, window, 100 ) != 0; + require_action( ok, exit, err = kNoResourcesErr ); + + mWindow.SetOwner( this ); + err = kNoErr; + +exit: + return( err ); +} + +//=========================================================================================================================== +// GoToURL +//=========================================================================================================================== + +OSStatus ExplorerBar::GoToURL( const CString &inURL ) +{ + OSStatus err; + BSTR s; + VARIANT empty; + + s = inURL.AllocSysString(); + require_action( s, exit, err = kNoMemoryErr ); + + VariantInit( &empty ); + err = mWebBrowser->Navigate( s, &empty, &empty, &empty, &empty ); + SysFreeString( s ); + require_noerr( err, exit ); + +exit: + return( err ); +} diff --git a/Clients/ExplorerPlugin/ExplorerBar.h b/Clients/ExplorerPlugin/ExplorerBar.h new file mode 100644 index 0000000..fe58176 --- /dev/null +++ b/Clients/ExplorerPlugin/ExplorerBar.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: ExplorerBar.h,v $ +Revision 1.2 2004/07/13 21:24:21 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:34:59 rpantos +Move to Clients from mDNSWindows + +Revision 1.1 2004/01/30 03:01:56 bradley +Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. + +*/ + +#ifndef __EXPLORER_BAR__ +#define __EXPLORER_BAR__ + +#include "StdAfx.h" + +#include "ExplorerBarWindow.h" +#include "ExplorerPlugin.h" + +//=========================================================================================================================== +// ExplorerBar +//=========================================================================================================================== + +class ExplorerBar : public IDeskBand, + public IInputObject, + public IObjectWithSite, + public IPersistStream, + public IContextMenu +{ + protected: + + DWORD mRefCount; + IInputObjectSite * mSite; + IWebBrowser2 * mWebBrowser; + HWND mParentWindow; + BOOL mFocus; + DWORD mViewMode; + DWORD mBandID; + ExplorerBarWindow mWindow; + + public: + + ExplorerBar( void ); + ~ExplorerBar( void ); + + // IUnknown methods + + STDMETHODIMP QueryInterface( REFIID inID, LPVOID *outResult ); + STDMETHODIMP_( DWORD ) AddRef( void ); + STDMETHODIMP_( DWORD ) Release( void ); + + // IOleWindow methods + + STDMETHOD( GetWindow )( HWND *outWindow ); + STDMETHOD( ContextSensitiveHelp )( BOOL inEnterMode ); + + // IDockingWindow methods + + STDMETHOD( ShowDW )( BOOL inShow ); + STDMETHOD( CloseDW )( DWORD inReserved ); + STDMETHOD( ResizeBorderDW )( LPCRECT inBorder, IUnknown *inPunkSite, BOOL inReserved ); + + // IDeskBand methods + + STDMETHOD( GetBandInfo )( DWORD inBandID, DWORD inViewMode, DESKBANDINFO *outInfo ); + + // IInputObject methods + + STDMETHOD( UIActivateIO )( BOOL inActivate, LPMSG inMsg ); + STDMETHOD( HasFocusIO )( void ); + STDMETHOD( TranslateAcceleratorIO )( LPMSG inMsg ); + + // IObjectWithSite methods + + STDMETHOD( SetSite )( IUnknown *inPunkSite ); + STDMETHOD( GetSite )( REFIID inID, LPVOID *outResult ); + + // IPersistStream methods + + STDMETHOD( GetClassID )( LPCLSID outClassID ); + STDMETHOD( IsDirty )( void ); + STDMETHOD( Load )( LPSTREAM inStream ); + STDMETHOD( Save )( LPSTREAM inStream, BOOL inClearDirty ); + STDMETHOD( GetSizeMax )( ULARGE_INTEGER *outSizeMax ); + + // IContextMenu methods + + STDMETHOD( QueryContextMenu )( HMENU hContextMenu, UINT iContextMenuFirstItem, UINT idCmdFirst, UINT idCmdLast, UINT uFlags ); + STDMETHOD( GetCommandString )( UINT idCmd, UINT uType, UINT* pwReserved, LPSTR pszName, UINT cchMax ); + STDMETHOD( InvokeCommand )( LPCMINVOKECOMMANDINFO lpici ); + + // Other + + OSStatus SetupWindow( void ); + OSStatus GoToURL( const CString &inURL ); +}; + +#endif // __EXPLORER_BAR__ diff --git a/Clients/ExplorerPlugin/ExplorerBarWindow.cpp b/Clients/ExplorerPlugin/ExplorerBarWindow.cpp new file mode 100644 index 0000000..beb0b35 --- /dev/null +++ b/Clients/ExplorerPlugin/ExplorerBarWindow.cpp @@ -0,0 +1,840 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: ExplorerBarWindow.cpp,v $ +Revision 1.12 2004/10/26 00:56:03 cheshire +Use "#if 0" instead of commenting out code + +Revision 1.11 2004/10/18 23:49:17 shersche + Remove trailing dot from hostname, because some flavors of Windows have difficulty parsing hostnames with a trailing dot. +Bug #: 3841564 + +Revision 1.10 2004/09/02 02:18:58 cheshire +Minor textual cleanup to improve readability + +Revision 1.9 2004/09/02 02:11:56 cheshire + Fix incorrect testing of MoreComing flag + +Revision 1.8 2004/07/26 05:47:31 shersche +use TXTRecord APIs, fix bug in locating service to be removed + +Revision 1.7 2004/07/22 16:08:20 shersche +clean up debug print statements, re-enable code inadvertently commented out + +Revision 1.6 2004/07/22 05:27:20 shersche + Check to make sure error isn't WSAEWOULDBLOCK when canceling browse +Bug #: 3735827 + +Revision 1.5 2004/07/20 06:49:18 shersche +clean up socket handling code + +Revision 1.4 2004/07/13 21:24:21 rpantos +Fix for . + +Revision 1.3 2004/06/27 14:59:59 shersche +reference count service info to handle multi-homed hosts + +Revision 1.2 2004/06/23 16:09:34 shersche +Add the resolve DNSServiceRef to list of extant refs. This fixes the "doesn't resolve when double clicking" problem + +Submitted by: Scott Herscher + +Revision 1.1 2004/06/18 04:34:59 rpantos +Move to Clients from mDNSWindows + +Revision 1.5 2004/04/15 01:00:05 bradley +Removed support for automatically querying for A/AAAA records when resolving names. Platforms +without .local name resolving support will need to manually query for A/AAAA records as needed. + +Revision 1.4 2004/04/09 21:03:15 bradley +Changed port numbers to use network byte order for consistency with other platforms. + +Revision 1.3 2004/04/08 09:43:43 bradley +Changed callback calling conventions to __stdcall so they can be used with C# delegates. + +Revision 1.2 2004/02/21 04:36:19 bradley +Enable dot local name lookups now that the NSP is being installed. + +Revision 1.1 2004/01/30 03:01:56 bradley +Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. + +*/ + +#include "StdAfx.h" + +#include "CommonServices.h" +#include "DebugServices.h" +#include "WinServices.h" +#include "dns_sd.h" + +#include "ExplorerBar.h" +#include "LoginDialog.h" +#include "Resource.h" + +#include "ExplorerBarWindow.h" + +// MFC Debugging + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#if 0 +#pragma mark == Constants == +#endif + +//=========================================================================================================================== +// Constants +//=========================================================================================================================== + +// Control IDs + +#define IDC_EXPLORER_TREE 1234 + +// Private Messages + +#define WM_PRIVATE_SERVICE_EVENT ( WM_USER + 0x100 ) + +// TXT records + +#define kTXTRecordKeyPath "path" + +#if 0 +#pragma mark == Prototypes == +#endif + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +DEBUG_LOCAL int FindServiceArrayIndex( const ServiceInfoArray &inArray, const ServiceInfo &inService, int &outIndex ); + +#if 0 +#pragma mark == Message Map == +#endif + +//=========================================================================================================================== +// Message Map +//=========================================================================================================================== + +BEGIN_MESSAGE_MAP( ExplorerBarWindow, CWnd ) + ON_WM_CREATE() + ON_WM_DESTROY() + ON_WM_SIZE() + ON_NOTIFY( NM_DBLCLK, IDC_EXPLORER_TREE, OnDoubleClick ) + ON_MESSAGE( WM_PRIVATE_SERVICE_EVENT, OnServiceEvent ) +END_MESSAGE_MAP() + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// ExplorerBarWindow +//=========================================================================================================================== + +ExplorerBarWindow::ExplorerBarWindow( void ) +{ + mOwner = NULL; + mResolveServiceRef = NULL; +} + +//=========================================================================================================================== +// ~ExplorerBarWindow +//=========================================================================================================================== + +ExplorerBarWindow::~ExplorerBarWindow( void ) +{ + // +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// OnCreate +//=========================================================================================================================== + +int ExplorerBarWindow::OnCreate( LPCREATESTRUCT inCreateStruct ) +{ + AFX_MANAGE_STATE( AfxGetStaticModuleState() ); + + OSStatus err; + CRect rect; + CBitmap bitmap; + CString s; + + err = CWnd::OnCreate( inCreateStruct ); + require_noerr( err, exit ); + + GetClientRect( rect ); + mTree.Create( WS_TABSTOP | WS_VISIBLE | WS_CHILD | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_HASLINES | TVS_NOHSCROLL , rect, this, + IDC_EXPLORER_TREE ); + + + ServiceHandlerEntry * e; + + // Web Site Handler + + e = new ServiceHandlerEntry; + check( e ); + e->type = "_http._tcp"; + e->urlScheme = "http://"; + e->ref = NULL; + e->treeItem = NULL; + e->treeFirst = true; + e->obj = this; + e->needsLogin = false; + mServiceHandlers.Add( e ); + + s.LoadString( IDS_WEB_SITES ); + e->treeItem = mTree.InsertItem( s, 0, 0 ); + mTree.Expand( e->treeItem, TVE_EXPAND ); + + err = DNSServiceBrowse( &e->ref, 0, 0, e->type, NULL, BrowseCallBack, e ); + require_noerr( err, exit ); + + err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(e->ref), m_hWnd, WM_PRIVATE_SERVICE_EVENT, FD_READ|FD_CLOSE); + require_noerr( err, exit ); + + m_serviceRefs.push_back(e->ref); + + // FTP Site Handler + + e = new ServiceHandlerEntry; + check( e ); + e->type = "_ftp._tcp"; + e->urlScheme = "ftp://"; + e->ref = NULL; + e->treeItem = NULL; + e->treeFirst = true; + e->obj = this; + e->needsLogin = true; + mServiceHandlers.Add( e ); + + s.LoadString( IDS_FTP_SITES ); + e->treeItem = mTree.InsertItem( s, 0, 0 ); + mTree.Expand( e->treeItem, TVE_EXPAND ); + + err = DNSServiceBrowse( &e->ref, 0, 0, e->type, NULL, BrowseCallBack, e ); + require_noerr( err, exit ); + + err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(e->ref), m_hWnd, WM_PRIVATE_SERVICE_EVENT, FD_READ|FD_CLOSE); + require_noerr( err, exit ); + + m_serviceRefs.push_back(e->ref); + + m_imageList.Create(16, 16, ILC_COLORDDB, 1, 0); + bitmap.LoadBitmap(IDB_LOGO); + m_imageList.Add(&bitmap, (CBitmap*) NULL); + + mTree.SetImageList(&m_imageList, TVSIL_NORMAL); + +exit: + + // Cannot talk to the mDNSResponder service. Show the error message and exit (with kNoErr so they can see it). + if ( err != kNoErr ) + { + s.LoadString( IDS_MDNSRESPONDER_NOT_AVAILABLE ); + mTree.DeleteAllItems(); + mTree.InsertItem( s, 0, 0, TVI_ROOT, TVI_LAST ); + err = kNoErr; + } + + return( err ); +} + +//=========================================================================================================================== +// OnDestroy +//=========================================================================================================================== + +void ExplorerBarWindow::OnDestroy( void ) +{ + // Stop any resolves that may still be pending (shouldn't be any). + + StopResolve(); + + // Clean up the extant browses + while (m_serviceRefs.size() > 0) + { + // + // take the head of the list + // + DNSServiceRef ref = m_serviceRefs.front(); + + // + // Stop will remove it from the list + // + Stop( ref ); + } + + // Clean up the service handlers. + + int i; + int n; + + n = (int) mServiceHandlers.GetSize(); + for( i = 0; i < n; ++i ) + { + delete mServiceHandlers[ i ]; + } + + CWnd::OnDestroy(); +} + +//=========================================================================================================================== +// OnSize +//=========================================================================================================================== + +void ExplorerBarWindow::OnSize( UINT inType, int inX, int inY ) +{ + CWnd::OnSize( inType, inX, inY ); + mTree.MoveWindow( 0, 0, inX, inY ); +} + +//=========================================================================================================================== +// OnDoubleClick +//=========================================================================================================================== + +void ExplorerBarWindow::OnDoubleClick( NMHDR *inNMHDR, LRESULT *outResult ) +{ + HTREEITEM item; + ServiceInfo * service; + OSStatus err; + + DEBUG_UNUSED( inNMHDR ); + + item = mTree.GetSelectedItem(); + require( item, exit ); + + service = reinterpret_cast < ServiceInfo * > ( mTree.GetItemData( item ) ); + require_quiet( service, exit ); + + err = StartResolve( service ); + require_noerr( err, exit ); + +exit: + *outResult = 0; +} + + +//=========================================================================================================================== +// OnServiceEvent +//=========================================================================================================================== + +LONG +ExplorerBarWindow::OnServiceEvent(WPARAM inWParam, LPARAM inLParam) +{ + if (WSAGETSELECTERROR(inLParam) && !(HIWORD(inLParam))) + { + dlog( kDebugLevelError, "OnServiceEvent: window error\n" ); + } + else + { + SOCKET sock = (SOCKET) inWParam; + + // iterate thru list + ServiceRefList::iterator it; + + for (it = m_serviceRefs.begin(); it != m_serviceRefs.end(); it++) + { + DNSServiceRef ref = *it; + + check(ref != NULL); + + if ((SOCKET) DNSServiceRefSockFD(ref) == sock) + { + DNSServiceErrorType err; + + err = DNSServiceProcessResult(ref); + + if (err != 0) + { + CString s; + + s.LoadString( IDS_MDNSRESPONDER_NOT_AVAILABLE ); + mTree.DeleteAllItems(); + mTree.InsertItem( s, 0, 0, TVI_ROOT, TVI_LAST ); + + Stop(ref); + } + + break; + } + } + } + + return ( 0 ); +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// BrowseCallBack +//=========================================================================================================================== + +void DNSSD_API + ExplorerBarWindow::BrowseCallBack( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + const char * inType, + const char * inDomain, + void * inContext ) +{ + ServiceHandlerEntry * obj; + ServiceInfo * service; + OSStatus err; + + DEBUG_UNUSED( inRef ); + + obj = NULL; + service = NULL; + + require_noerr( inErrorCode, exit ); + obj = reinterpret_cast < ServiceHandlerEntry * > ( inContext ); + check( obj ); + check( obj->obj ); + + // + // set the UI to hold off on updates + // + obj->obj->mTree.SetRedraw(FALSE); + + try + { + service = new ServiceInfo; + require_action( service, exit, err = kNoMemoryErr ); + + err = UTF8StringToStringObject( inName, service->displayName ); + check_noerr( err ); + + service->name = strdup( inName ); + require_action( service->name, exit, err = kNoMemoryErr ); + + service->type = strdup( inType ); + require_action( service->type, exit, err = kNoMemoryErr ); + + service->domain = strdup( inDomain ); + require_action( service->domain, exit, err = kNoMemoryErr ); + + service->ifi = inInterfaceIndex; + service->handler = obj; + + service->refs = 1; + + if (inFlags & kDNSServiceFlagsAdd) obj->obj->OnServiceAdd (service); + else obj->obj->OnServiceRemove(service); + + service = NULL; + } + catch( ... ) + { + dlog( kDebugLevelError, "BrowseCallBack: exception thrown\n" ); + } + +exit: + // + // If no more coming, then update UI + // + if (obj && obj->obj && ((inFlags & kDNSServiceFlagsMoreComing) == 0)) + { + obj->obj->mTree.SetRedraw(TRUE); + obj->obj->mTree.Invalidate(); + } + + if( service ) + { + delete service; + } +} + +//=========================================================================================================================== +// OnServiceAdd +//=========================================================================================================================== + +LONG ExplorerBarWindow::OnServiceAdd( ServiceInfo * service ) +{ + ServiceHandlerEntry * handler; + int cmp; + int index; + + + check( service ); + handler = service->handler; + check( handler ); + + cmp = FindServiceArrayIndex( handler->array, *service, index ); + if( cmp == 0 ) + { + // Found a match so update the item. The index is index + 1 so subtract 1. + + index -= 1; + check( index < handler->array.GetSize() ); + + handler->array[ index ]->refs++; + + delete service; + } + else + { + HTREEITEM afterItem; + + // Insert the new item in sorted order. + + afterItem = ( index > 0 ) ? handler->array[ index - 1 ]->item : TVI_FIRST; + handler->array.InsertAt( index, service ); + service->item = mTree.InsertItem( service->displayName, handler->treeItem, afterItem ); + mTree.SetItemData( service->item, (DWORD_PTR) service ); + + // Make sure the item is visible if this is the first time a service was added. + + if( handler->treeFirst ) + { + handler->treeFirst = false; + mTree.EnsureVisible( service->item ); + } + } + return( 0 ); +} + +//=========================================================================================================================== +// OnServiceRemove +//=========================================================================================================================== + +LONG ExplorerBarWindow::OnServiceRemove( ServiceInfo * service ) +{ + ServiceHandlerEntry * handler; + int cmp; + int index; + + + check( service ); + handler = service->handler; + check( handler ); + + // Search to see if we know about this service instance. If so, remove it from the list. + + cmp = FindServiceArrayIndex( handler->array, *service, index ); + check( cmp == 0 ); + + if( cmp == 0 ) + { + // Possibly found a match remove the item. The index + // is index + 1 so subtract 1. + index -= 1; + check( index < handler->array.GetSize() ); + + if ( --handler->array[ index ]->refs == 0 ) + { + mTree.DeleteItem( handler->array[ index ]->item ); + delete handler->array[ index ]; + handler->array.RemoveAt( index ); + } + } + + delete service; + return( 0 ); +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// StartResolve +//=========================================================================================================================== + +OSStatus ExplorerBarWindow::StartResolve( ServiceInfo *inService ) +{ + OSStatus err; + + check( inService ); + + // Stop any current resolve that may be in progress. + + StopResolve(); + + // Resolve the service. + err = DNSServiceResolve( &mResolveServiceRef, 0, 0, + inService->name, inService->type, inService->domain, (DNSServiceResolveReply) ResolveCallBack, inService->handler ); + require_noerr( err, exit ); + + err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(mResolveServiceRef), m_hWnd, WM_PRIVATE_SERVICE_EVENT, FD_READ|FD_CLOSE); + require_noerr( err, exit ); + + m_serviceRefs.push_back(mResolveServiceRef); + +exit: + return( err ); +} + +//=========================================================================================================================== +// StopResolve +//=========================================================================================================================== + +void ExplorerBarWindow::StopResolve( void ) +{ + if( mResolveServiceRef ) + { + Stop( mResolveServiceRef ); + mResolveServiceRef = NULL; + } +} + +//=========================================================================================================================== +// ResolveCallBack +//=========================================================================================================================== + +void DNSSD_API + ExplorerBarWindow::ResolveCallBack( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inFullName, + const char * inHostName, + uint16_t inPort, + uint16_t inTXTSize, + const char * inTXT, + void * inContext ) +{ + ExplorerBarWindow * obj; + ServiceHandlerEntry * handler; + OSStatus err; + + DEBUG_UNUSED( inRef ); + DEBUG_UNUSED( inFlags ); + DEBUG_UNUSED( inErrorCode ); + DEBUG_UNUSED( inFullName ); + + require_noerr( inErrorCode, exit ); + handler = (ServiceHandlerEntry *) inContext; + check( handler ); + obj = handler->obj; + check( obj ); + + try + { + ResolveInfo * resolve; + int idx; + + dlog( kDebugLevelNotice, "resolved %s on ifi %d to %s\n", inFullName, inInterfaceIndex, inHostName ); + + // Stop resolving after the first good result. + + obj->StopResolve(); + + // Post a message to the main thread so it can handle it since MFC is not thread safe. + + resolve = new ResolveInfo; + require_action( resolve, exit, err = kNoMemoryErr ); + + UTF8StringToStringObject( inHostName, resolve->host ); + + // rdar://problem/3841564 + // + // strip trailing dot from hostname because some flavors of Windows + // have trouble parsing it. + + idx = resolve->host.ReverseFind('.'); + + if ((idx > 1) && ((resolve->host.GetLength() - 1) == idx)) + { + resolve->host.Delete(idx, 1); + } + + resolve->port = ntohs( inPort ); + resolve->ifi = inInterfaceIndex; + resolve->handler = handler; + + err = resolve->txt.SetData( inTXT, inTXTSize ); + check_noerr( err ); + + obj->OnResolve(resolve); + } + catch( ... ) + { + dlog( kDebugLevelError, "ResolveCallBack: exception thrown\n" ); + } + +exit: + return; +} + +//=========================================================================================================================== +// OnResolve +//=========================================================================================================================== + +LONG ExplorerBarWindow::OnResolve( ResolveInfo * resolve ) +{ + CString url; + uint8_t * path; + uint8_t pathSize; + char * pathPrefix; + CString username; + CString password; + + + check( resolve ); + + // Get login info if needed. + + if( resolve->handler->needsLogin ) + { + LoginDialog dialog; + + if( !dialog.GetLogin( username, password ) ) + { + goto exit; + } + } + + // If the HTTP TXT record is a "path=" entry, use it as the resource path. Otherwise, use "/". + + pathPrefix = ""; + if( strcmp( resolve->handler->type, "_http._tcp" ) == 0 ) + { + uint8_t * txtData; + uint16_t txtLen; + + resolve->txt.GetData( &txtData, &txtLen ); + + path = (uint8_t*) TXTRecordGetValuePtr(txtLen, txtData, kTXTRecordKeyPath, &pathSize); + + if (path == NULL) + { + path = (uint8_t*) ""; + pathSize = 1; + } + } + else + { + path = (uint8_t *) ""; + pathSize = 1; + } + + // Build the URL in the following format: + // + // [[:]@][] + + url.AppendFormat( TEXT( "%S" ), resolve->handler->urlScheme ); // URL Scheme + if( username.GetLength() > 0 ) + { + url.AppendFormat( TEXT( "%s" ), username ); // Username + if( password.GetLength() > 0 ) + { + url.AppendFormat( TEXT( ":%s" ), password ); // Password + } + url.AppendFormat( TEXT( "@" ) ); + } + + url += resolve->host; // Host + url.AppendFormat( TEXT( ":%d" ), resolve->port ); // :Port + url.AppendFormat( TEXT( "%S" ), pathPrefix ); // Path Prefix ("/" or empty). + url.AppendFormat( TEXT( "%.*S" ), (int) pathSize, (char *) path ); // Path (possibly empty). + + // Tell Internet Explorer to go to the URL. + + check( mOwner ); + mOwner->GoToURL( url ); + +exit: + delete resolve; + return( 0 ); +} + +//=========================================================================================================================== +// Stop +//=========================================================================================================================== +void ExplorerBarWindow::Stop( DNSServiceRef ref ) +{ + m_serviceRefs.remove( ref ); + + WSAAsyncSelect(DNSServiceRefSockFD( ref ), m_hWnd, WM_PRIVATE_SERVICE_EVENT, 0); + + DNSServiceRefDeallocate( ref ); +} + + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// FindServiceArrayIndex +//=========================================================================================================================== + +DEBUG_LOCAL int FindServiceArrayIndex( const ServiceInfoArray &inArray, const ServiceInfo &inService, int &outIndex ) +{ + int result; + int lo; + int hi; + int mid; + + result = -1; + mid = 0; + lo = 0; + hi = (int)( inArray.GetSize() - 1 ); + while( lo <= hi ) + { + mid = ( lo + hi ) / 2; + result = inService.displayName.CompareNoCase( inArray[ mid ]->displayName ); +#if 0 + if( result == 0 ) + { + result = ( (int) inService.ifi ) - ( (int) inArray[ mid ]->ifi ); + } +#endif + if( result == 0 ) + { + break; + } + else if( result < 0 ) + { + hi = mid - 1; + } + else + { + lo = mid + 1; + } + } + if( result == 0 ) + { + mid += 1; // Bump index so new item is inserted after matching item. + } + else if( result > 0 ) + { + mid += 1; + } + outIndex = mid; + return( result ); +} diff --git a/Clients/ExplorerPlugin/ExplorerBarWindow.h b/Clients/ExplorerPlugin/ExplorerBarWindow.h new file mode 100644 index 0000000..afa4e99 --- /dev/null +++ b/Clients/ExplorerPlugin/ExplorerBarWindow.h @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: ExplorerBarWindow.h,v $ +Revision 1.5 2004/07/26 05:47:31 shersche +use TXTRecord APIs, fix bug in locating service to be removed + +Revision 1.4 2004/07/20 06:49:18 shersche +clean up socket handling code + +Revision 1.3 2004/07/13 21:24:21 rpantos +Fix for . + +Revision 1.2 2004/06/27 14:59:59 shersche +reference count service info to handle multi-homed hosts + +Revision 1.1 2004/06/18 04:34:59 rpantos +Move to Clients from mDNSWindows + +Revision 1.3 2004/04/15 01:00:05 bradley +Removed support for automatically querying for A/AAAA records when resolving names. Platforms +without .local name resolving support will need to manually query for A/AAAA records as needed. + +Revision 1.2 2004/04/08 09:43:43 bradley +Changed callback calling conventions to __stdcall so they can be used with C# delegates. + +Revision 1.1 2004/01/30 03:01:56 bradley +Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. + +*/ + +#ifndef __EXPLORER_BAR_WINDOW__ +#define __EXPLORER_BAR_WINDOW__ + +#pragma once + +#include "afxtempl.h" + +#include "dns_sd.h" +#include + +//=========================================================================================================================== +// Structures +//=========================================================================================================================== + +// Forward Declarations + +struct ServiceHandlerEntry; +class ExplorerBarWindow; + +// ServiceInfo + +struct ServiceInfo +{ + CString displayName; + char * name; + char * type; + char * domain; + uint32_t ifi; + HTREEITEM item; + ServiceHandlerEntry * handler; + DWORD refs; + + ServiceInfo( void ) + { + item = NULL; + type = NULL; + domain = NULL; + handler = NULL; + } + + ~ServiceInfo( void ) + { + if( name ) + { + free( name ); + } + if( type ) + { + free( type ); + } + if( domain ) + { + free( domain ); + } + } +}; + +typedef CArray < ServiceInfo *, ServiceInfo * > ServiceInfoArray; + +// TextRecord + +struct TextRecord +{ + uint8_t * mData; + uint16_t mSize; + + TextRecord( void ) + { + mData = NULL; + mSize = 0; + } + + ~TextRecord( void ) + { + if( mData ) + { + free( mData ); + } + } + + void GetData( void *outData, uint16_t *outSize ) + { + if( outData ) + { + *( (void **) outData ) = mData; + } + if( outSize ) + { + *outSize = mSize; + } + } + + OSStatus SetData( const void *inData, uint16_t inSize ) + { + OSStatus err; + uint8_t * newData; + + newData = (uint8_t *) malloc( inSize ); + require_action( newData, exit, err = kNoMemoryErr ); + memcpy( newData, inData, inSize ); + + if( mData ) + { + free( mData ); + } + mData = newData; + mSize = inSize; + err = kNoErr; + + exit: + return( err ); + } +}; + +// ResolveInfo + +struct ResolveInfo +{ + CString host; + uint16_t port; + uint32_t ifi; + TextRecord txt; + ServiceHandlerEntry * handler; +}; + +// ServiceHandlerEntry + +struct ServiceHandlerEntry +{ + const char * type; + const char * urlScheme; + DNSServiceRef ref; + ServiceInfoArray array; + HTREEITEM treeItem; + bool treeFirst; + ExplorerBarWindow * obj; + bool needsLogin; + + ServiceHandlerEntry( void ) + { + type = NULL; + urlScheme = NULL; + ref = NULL; + treeItem = NULL; + treeFirst = true; + obj = NULL; + needsLogin = false; + } + + ~ServiceHandlerEntry( void ) + { + int i; + int n; + + n = (int) array.GetSize(); + for( i = 0; i < n; ++i ) + { + delete array[ i ]; + } + } +}; + +typedef CArray < ServiceHandlerEntry *, ServiceHandlerEntry * > ServiceHandlerArray; + +//=========================================================================================================================== +// ExplorerBarWindow +//=========================================================================================================================== + +class ExplorerBar; // Forward Declaration + +class ExplorerBarWindow : public CWnd +{ + protected: + + ExplorerBar * mOwner; + CTreeCtrl mTree; + + ServiceHandlerArray mServiceHandlers; + DNSServiceRef mResolveServiceRef; + + public: + + ExplorerBarWindow( void ); + virtual ~ExplorerBarWindow( void ); + + protected: + + // General + + afx_msg int OnCreate( LPCREATESTRUCT inCreateStruct ); + afx_msg void OnDestroy( void ); + afx_msg void OnSize( UINT inType, int inX, int inY ); + afx_msg void OnDoubleClick( NMHDR *inNMHDR, LRESULT *outResult ); + afx_msg LONG OnServiceEvent( WPARAM inWParam, LPARAM inLParam ); + + // Browsing + + static void DNSSD_API + BrowseCallBack( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + const char * inType, + const char * inDomain, + void * inContext ); + LONG OnServiceAdd( ServiceInfo * service ); + LONG OnServiceRemove( ServiceInfo * service ); + + // Resolving + + OSStatus StartResolve( ServiceInfo *inService ); + void StopResolve( void ); + + + void Stop( DNSServiceRef ref ); + + + static void DNSSD_API + ResolveCallBack( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inFullName, + const char * inHostName, + uint16_t inPort, + uint16_t inTXTSize, + const char * inTXT, + void * inContext ); + LONG OnResolve( ResolveInfo * resolve ); + + // Accessors + + public: + + ExplorerBar * GetOwner( void ) const { return( mOwner ); } + void SetOwner( ExplorerBar *inOwner ) { mOwner = inOwner; } + + DECLARE_MESSAGE_MAP() + private: + + typedef std::list< DNSServiceRef > ServiceRefList; + + ServiceRefList m_serviceRefs; + CImageList m_imageList; +}; + +#endif // __EXPLORER_BAR_WINDOW__ diff --git a/Clients/ExplorerPlugin/ExplorerPlugin.cpp b/Clients/ExplorerPlugin/ExplorerPlugin.cpp new file mode 100644 index 0000000..5de124d --- /dev/null +++ b/Clients/ExplorerPlugin/ExplorerPlugin.cpp @@ -0,0 +1,538 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: ExplorerPlugin.cpp,v $ +Revision 1.5 2004/09/15 10:33:54 shersche + Install XP toolbar button (8 bit mask) if running on XP platform, otherwise install 1 bit mask toolbar button +Bug #: 3721611 + +Revision 1.4 2004/07/13 21:24:21 rpantos +Fix for . + +Revision 1.3 2004/06/26 14:12:07 shersche +Register the toolbar button + +Revision 1.2 2004/06/24 20:09:39 shersche +Change text +Submitted by: herscher + +Revision 1.1 2004/06/18 04:34:59 rpantos +Move to Clients from mDNSWindows + +Revision 1.1 2004/01/30 03:01:56 bradley +Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. + +*/ + +#include "StdAfx.h" + +// The following 2 includes have to be in this order and INITGUID must be defined here, before including the file +// that specifies the GUID(s), and nowhere else. The reason for this is that initguid.h doesn't provide separate +// define and declare macros for GUIDs so you have to #define INITGUID in the single file where you want to define +// your GUID then in all the other files that just need the GUID declared, INITGUID must not be defined. + +#define INITGUID +#include +#include "ExplorerPlugin.h" + +#include +#include + +#include "CommonServices.h" +#include "DebugServices.h" + +#include "ClassFactory.h" +#include "Resource.h" + +// MFC Debugging + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#if 0 +#pragma mark == Prototypes == +#endif + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +// DLL Exports + +extern "C" BOOL WINAPI DllMain( HINSTANCE inInstance, DWORD inReason, LPVOID inReserved ); + +// MFC Support + +DEBUG_LOCAL OSStatus MFCDLLProcessAttach( HINSTANCE inInstance ); +DEBUG_LOCAL void MFCDLLProcessDetach( HINSTANCE inInstance ); +DEBUG_LOCAL void MFCDLLThreadDetach( HINSTANCE inInstance ); + +// Utilities + +DEBUG_LOCAL OSStatus RegisterServer( HINSTANCE inInstance, CLSID inCLSID, LPCTSTR inName ); +DEBUG_LOCAL OSStatus RegisterCOMCategory( CLSID inCLSID, CATID inCategoryID, BOOL inRegister ); + +#if 0 +#pragma mark == Globals == +#endif + +//=========================================================================================================================== +// Globals +//=========================================================================================================================== + +HINSTANCE gInstance = NULL; +int gDLLRefCount = 0; +CWinApp gApp; + +#if 0 +#pragma mark - +#pragma mark == DLL Exports == +#endif + +//=========================================================================================================================== +// DllMain +//=========================================================================================================================== + +BOOL WINAPI DllMain( HINSTANCE inInstance, DWORD inReason, LPVOID inReserved ) +{ + BOOL ok; + OSStatus err; + + DEBUG_UNUSED( inReserved ); + + ok = TRUE; + switch( inReason ) + { + case DLL_PROCESS_ATTACH: + gInstance = inInstance; + debug_initialize( kDebugOutputTypeWindowsEventLog, "DNSServices Bar", inInstance ); + debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelTrace ); + dlog( kDebugLevelTrace, "\nDllMain: process attach\n" ); + + err = MFCDLLProcessAttach( inInstance ); + ok = ( err == kNoErr ); + require_noerr( err, exit ); + break; + + case DLL_PROCESS_DETACH: + dlog( kDebugLevelTrace, "DllMain: process detach\n" ); + MFCDLLProcessDetach( inInstance ); + break; + + case DLL_THREAD_ATTACH: + dlog( kDebugLevelTrace, "DllMain: thread attach\n" ); + break; + + case DLL_THREAD_DETACH: + dlog( kDebugLevelTrace, "DllMain: thread detach\n" ); + MFCDLLThreadDetach( inInstance ); + break; + + default: + dlog( kDebugLevelTrace, "DllMain: unknown reason code (%d)\n",inReason ); + break; + } + +exit: + return( ok ); +} + +//=========================================================================================================================== +// DllCanUnloadNow +//=========================================================================================================================== + +STDAPI DllCanUnloadNow( void ) +{ + dlog( kDebugLevelTrace, "DllCanUnloadNow (refCount=%d)\n", gDLLRefCount ); + + return( gDLLRefCount == 0 ); +} + +//=========================================================================================================================== +// DllGetClassObject +//=========================================================================================================================== + +STDAPI DllGetClassObject( REFCLSID inCLSID, REFIID inIID, LPVOID *outResult ) +{ + HRESULT err; + BOOL ok; + ClassFactory * factory; + + dlog( kDebugLevelTrace, "DllGetClassObject\n" ); + + *outResult = NULL; + + // Check if the class ID is supported. + + ok = IsEqualCLSID( inCLSID, CLSID_ExplorerBar ); + require_action_quiet( ok, exit, err = CLASS_E_CLASSNOTAVAILABLE ); + + // Create the ClassFactory object. + + factory = NULL; + try + { + factory = new ClassFactory( inCLSID ); + } + catch( ... ) + { + // Do not let exception escape. + } + require_action( factory, exit, err = E_OUTOFMEMORY ); + + // Query for the specified interface. Release the factory since QueryInterface retains it. + + err = factory->QueryInterface( inIID, outResult ); + factory->Release(); + +exit: + return( err ); +} + +//=========================================================================================================================== +// DllRegisterServer +//=========================================================================================================================== + +STDAPI DllRegisterServer( void ) +{ + HRESULT err; + BOOL ok; + CString s; + + dlog( kDebugLevelTrace, "DllRegisterServer\n" ); + + ok = s.LoadString( IDS_NAME ); + require_action( ok, exit, err = E_UNEXPECTED ); + + err = RegisterServer( gInstance, CLSID_ExplorerBar, s ); + require_noerr( err, exit ); + + err = RegisterCOMCategory( CLSID_ExplorerBar, CATID_InfoBand, TRUE ); + require_noerr( err, exit ); + +exit: + return( err ); +} + +//=========================================================================================================================== +// DllUnregisterServer +//=========================================================================================================================== + +STDAPI DllUnregisterServer( void ) +{ + HRESULT err; + + dlog( kDebugLevelTrace, "DllUnregisterServer\n" ); + + err = RegisterCOMCategory( CLSID_ExplorerBar, CATID_InfoBand, FALSE ); + require_noerr( err, exit ); + +exit: + return( err ); +} + +#if 0 +#pragma mark - +#pragma mark == MFC Support == +#endif + +//=========================================================================================================================== +// MFCDLLProcessAttach +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus MFCDLLProcessAttach( HINSTANCE inInstance ) +{ + OSStatus err; + _AFX_THREAD_STATE * threadState; + AFX_MODULE_STATE * previousModuleState; + BOOL ok; + CWinApp * app; + + app = NULL; + + // Simulate what is done in dllmodul.cpp. + + threadState = AfxGetThreadState(); + check( threadState ); + previousModuleState = threadState->m_pPrevModuleState; + + ok = AfxWinInit( inInstance, NULL, TEXT( "" ), 0 ); + require_action( ok, exit, err = kUnknownErr ); + + app = AfxGetApp(); + require_action( ok, exit, err = kNotInitializedErr ); + + ok = app->InitInstance(); + require_action( ok, exit, err = kUnknownErr ); + + threadState->m_pPrevModuleState = previousModuleState; + threadState = NULL; + AfxInitLocalData( inInstance ); + err = kNoErr; + +exit: + if( err ) + { + if( app ) + { + app->ExitInstance(); + } + AfxWinTerm(); + } + if( threadState ) + { + threadState->m_pPrevModuleState = previousModuleState; + } + return( err ); +} + +//=========================================================================================================================== +// MFCDLLProcessDetach +//=========================================================================================================================== + +DEBUG_LOCAL void MFCDLLProcessDetach( HINSTANCE inInstance ) +{ + CWinApp * app; + + // Simulate what is done in dllmodul.cpp. + + app = AfxGetApp(); + if( app ) + { + app->ExitInstance(); + } + +#if( DEBUG ) + if( AfxGetModuleThreadState()->m_nTempMapLock != 0 ) + { + dlog( kDebugLevelWarning, "Warning: Temp map lock count non-zero (%ld).\n", AfxGetModuleThreadState()->m_nTempMapLock ); + } +#endif + + AfxLockTempMaps(); + AfxUnlockTempMaps( -1 ); + + // Terminate the library before destructors are called. + + AfxWinTerm(); + AfxTermLocalData( inInstance, TRUE ); +} + +//=========================================================================================================================== +// MFCDLLFinalize +//=========================================================================================================================== + +DEBUG_LOCAL void MFCDLLThreadDetach( HINSTANCE inInstance ) +{ + // Simulate what is done in dllmodul.cpp. + +#if( DEBUG ) + if( AfxGetModuleThreadState()->m_nTempMapLock != 0 ) + { + dlog( kDebugLevelWarning, "Warning: Temp map lock count non-zero (%ld).\n", AfxGetModuleThreadState()->m_nTempMapLock ); + } +#endif + + AfxLockTempMaps(); + AfxUnlockTempMaps( -1 ); + AfxTermThread( inInstance ); +} + +#if 0 +#pragma mark - +#pragma mark == Utilities == +#endif + +//=========================================================================================================================== +// RegisterServer +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus RegisterServer( HINSTANCE inInstance, CLSID inCLSID, LPCTSTR inName ) +{ + typedef struct RegistryBuilder RegistryBuilder; + struct RegistryBuilder + { + HKEY rootKey; + LPCTSTR subKey; + LPCTSTR valueName; + LPCTSTR data; + }; + + OSStatus err; + LPWSTR clsidWideString; + TCHAR clsidString[ 64 ]; + DWORD nChars; + size_t n; + size_t i; + HKEY key; + TCHAR keyName[ MAX_PATH ]; + TCHAR moduleName[ MAX_PATH ] = TEXT( "" ); + TCHAR data[ MAX_PATH ]; + RegistryBuilder entries[] = + { + { HKEY_CLASSES_ROOT, TEXT( "CLSID\\%s" ), NULL, inName }, + { HKEY_CLASSES_ROOT, TEXT( "CLSID\\%s\\InprocServer32" ), NULL, moduleName }, + { HKEY_CLASSES_ROOT, TEXT( "CLSID\\%s\\InprocServer32" ), TEXT( "ThreadingModel" ), TEXT( "Apartment" ) } + }; + DWORD size; + OSVERSIONINFO versionInfo; + + // Convert the CLSID to a string based on the encoding of this code (ANSI or Unicode). + + err = StringFromIID( inCLSID, &clsidWideString ); + require_noerr( err, exit ); + require_action( clsidWideString, exit, err = kNoMemoryErr ); + + #ifdef UNICODE + lstrcpyn( clsidString, clsidWideString, sizeof_array( clsidString ) ); + CoTaskMemFree( clsidWideString ); + #else + nChars = WideCharToMultiByte( CP_ACP, 0, clsidWideString, -1, clsidString, sizeof_array( clsidString ), NULL, NULL ); + err = translate_errno( nChars > 0, (OSStatus) GetLastError(), kUnknownErr ); + CoTaskMemFree( clsidWideString ); + require_noerr( err, exit ); + #endif + + // Register the CLSID entries. + + nChars = GetModuleFileName( inInstance, moduleName, sizeof_array( moduleName ) ); + err = translate_errno( nChars > 0, (OSStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + n = sizeof_array( entries ); + for( i = 0; i < n; ++i ) + { + wsprintf( keyName, entries[ i ].subKey, clsidString ); + err = RegCreateKeyEx( entries[ i ].rootKey, keyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL ); + require_noerr( err, exit ); + + size = (DWORD)( ( lstrlen( entries[ i ].data ) + 1 ) * sizeof( TCHAR ) ); + err = RegSetValueEx( key, entries[ i ].valueName, 0, REG_SZ, (LPBYTE) entries[ i ].data, size ); + RegCloseKey( key ); + require_noerr( err, exit ); + } + + // If running on NT, register the extension as approved. + + versionInfo.dwOSVersionInfoSize = sizeof( versionInfo ); + GetVersionEx( &versionInfo ); + if( versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ) + { + lstrcpyn( keyName, TEXT( "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved" ), sizeof_array( keyName ) ); + err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, keyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL ); + require_noerr( err, exit ); + + lstrcpyn( data, inName, sizeof_array( data ) ); + size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) ); + err = RegSetValueEx( key, clsidString, 0, REG_SZ, (LPBYTE) data, size ); + RegCloseKey( key ); + } + + // register toolbar button + lstrcpyn( keyName, TEXT( "SOFTWARE\\Microsoft\\Internet Explorer\\Extensions\\{7F9DB11C-E358-4ca6-A83D-ACC663939424}"), sizeof_array( keyName ) ); + err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, keyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL ); + require_noerr( err, exit ); + + lstrcpyn( data, L"Yes", sizeof_array( data ) ); + size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) ); + RegSetValueEx( key, L"Default Visible", 0, REG_SZ, (LPBYTE) data, size ); + + lstrcpyn( data, inName, sizeof_array( data ) ); + size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) ); + RegSetValueEx( key, L"ButtonText", 0, REG_SZ, (LPBYTE) data, size ); + + lstrcpyn( data, L"{E0DD6CAB-2D10-11D2-8F1A-0000F87ABD16}", sizeof_array( data ) ); + size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) ); + RegSetValueEx( key, L"CLSID", 0, REG_SZ, (LPBYTE) data, size ); + + lstrcpyn( data, clsidString, sizeof_array( data ) ); + size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) ); + RegSetValueEx( key, L"BandCLSID", 0, REG_SZ, (LPBYTE) data, size ); + + // check if we're running XP or later + if ( ( versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ) && + ( versionInfo.dwMajorVersion == 5 ) && + ( versionInfo.dwMinorVersion >= 1 ) ) + { + wsprintf( data, L"%s,%d", moduleName, IDI_BUTTON_XP ); + size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) ); + RegSetValueEx( key, L"Icon", 0, REG_SZ, (LPBYTE) data, size); + + wsprintf( data, L"%s,%d", moduleName, IDI_BUTTON_XP ); + size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) ); + RegSetValueEx( key, L"HotIcon", 0, REG_SZ, (LPBYTE) data, size); + } + else + { + wsprintf( data, L"%s,%d", moduleName, IDI_BUTTON_2K ); + size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) ); + RegSetValueEx( key, L"Icon", 0, REG_SZ, (LPBYTE) data, size); + + wsprintf( data, L"%s,%d", moduleName, IDI_BUTTON_2K ); + size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) ); + RegSetValueEx( key, L"HotIcon", 0, REG_SZ, (LPBYTE) data, size); + } + + RegCloseKey( key ); + +exit: + return( err ); +} + +//=========================================================================================================================== +// RegisterCOMCategory +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus RegisterCOMCategory( CLSID inCLSID, CATID inCategoryID, BOOL inRegister ) +{ + HRESULT err; + ICatRegister * cat; + + err = CoInitialize( NULL ); + require( SUCCEEDED( err ), exit ); + + err = CoCreateInstance( CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (LPVOID *) &cat ); + check( SUCCEEDED( err ) ); + if( SUCCEEDED( err ) ) + { + if( inRegister ) + { + err = cat->RegisterClassImplCategories( inCLSID, 1, &inCategoryID ); + check_noerr( err ); + } + else + { + err = cat->UnRegisterClassImplCategories( inCLSID, 1, &inCategoryID ); + check_noerr( err ); + } + cat->Release(); + } + CoUninitialize(); + +exit: + return( err ); +} diff --git a/Clients/ExplorerPlugin/ExplorerPlugin.def b/Clients/ExplorerPlugin/ExplorerPlugin.def new file mode 100644 index 0000000..e825180 --- /dev/null +++ b/Clients/ExplorerPlugin/ExplorerPlugin.def @@ -0,0 +1,43 @@ +; +; Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. +; +; @APPLE_LICENSE_HEADER_START@ +; +; This file contains Original Code and/or Modifications of Original Code +; as defined in and that are subject to the Apple Public Source License +; Version 2.0 (the 'License'). You may not use this file except in +; compliance with the License. Please obtain a copy of the License at +; http://www.opensource.apple.com/apsl/ and read it before using this +; file. +; +; The Original Code and all software distributed under the License are +; distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +; EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +; INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +; FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +; Please see the License for the specific language governing rights and +; limitations under the License. +; +; @APPLE_LICENSE_HEADER_END@ +; +; Change History (most recent first): +; +; $Log: ExplorerPlugin.def,v $ +; Revision 1.2 2004/07/13 21:24:21 rpantos +; Fix for . +; +; Revision 1.1 2004/06/18 04:34:59 rpantos +; Move to Clients from mDNSWindows +; +; Revision 1.1 2004/01/30 03:01:56 bradley +; Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. +; +; + +LIBRARY ExplorerPlugin + +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE diff --git a/Clients/ExplorerPlugin/ExplorerPlugin.h b/Clients/ExplorerPlugin/ExplorerPlugin.h new file mode 100644 index 0000000..0acca3f --- /dev/null +++ b/Clients/ExplorerPlugin/ExplorerPlugin.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: ExplorerPlugin.h,v $ +Revision 1.2 2004/07/13 21:24:21 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:34:59 rpantos +Move to Clients from mDNSWindows + +Revision 1.1 2004/01/30 03:01:56 bradley +Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. + +*/ + +//=========================================================================================================================== +// Globals +//=========================================================================================================================== + +// {9999A076-A9E2-4c99-8A2B-632FC9429223} +DEFINE_GUID(CLSID_ExplorerBar, +0x9999a076, 0xa9e2, 0x4c99, 0x8a, 0x2b, 0x63, 0x2f, 0xc9, 0x42, 0x92, 0x23); + +extern HINSTANCE gInstance; +extern int gDLLRefCount; diff --git a/Clients/ExplorerPlugin/ExplorerPlugin.rc b/Clients/ExplorerPlugin/ExplorerPlugin.rc new file mode 100644 index 0000000..7879e58 --- /dev/null +++ b/Clients/ExplorerPlugin/ExplorerPlugin.rc @@ -0,0 +1,227 @@ +// Microsoft Visual C++ generated resource script. +// +#include "Resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" +#include "WinVersRes.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "Resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "#include ""WinVersRes.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#define _AFX_NO_SPLITTER_RESOURCES\r\n" + "#define _AFX_NO_OLE_RESOURCES\r\n" + "#define _AFX_NO_TRACKER_RESOURCES\r\n" + "#define _AFX_NO_PROPERTY_RESOURCES\r\n" + "\r\n" + "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n" + "LANGUAGE 9, 1\r\n" + "#pragma code_page(1252)\r\n" + "#include ""afxres.rc"" // Standard components\r\n" + "#endif\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION MASTER_PROD_VERS + PRODUCTVERSION MASTER_PROD_VERS + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Apple Computer, Inc." + VALUE "FileDescription", "DNSServices Explorer Bar" + VALUE "FileVersion", MASTER_PROD_VERS_STR + VALUE "InternalName", "ExplorerPlugin.dll" + VALUE "LegalCopyright", "Copyright (C) 2003-2004 Apple Computer, Inc." + VALUE "OriginalFilename", "ExplorerPlugin.dll" + VALUE "ProductName", MASTER_PROD_NAME + VALUE "ProductVersion", MASTER_PROD_VERS_STR + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_LOGIN DIALOGEX 0, 0, 180, 89 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION +CAPTION "Login" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "Enter a username and password. Leave blank to use the default username and/or password.", + IDC_STATIC,8,8,156,16,NOT WS_GROUP + RTEXT "Username:",IDC_STATIC,10,34,36,8,NOT WS_GROUP + EDITTEXT IDC_LOGIN_USERNAME_TEXT,50,32,118,12,ES_AUTOHSCROLL + RTEXT "Password:",IDC_STATIC,10,50,36,8,NOT WS_GROUP + EDITTEXT IDC_LOGIN_PASSWORD_TEXT,50,48,118,12,ES_PASSWORD | + ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,129,70,44,14 + PUSHBUTTON "Cancel",IDCANCEL,77,70,44,14 +END + +IDD_ABOUT DIALOGEX 0, 0, 219, 87 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR +CAPTION "About Rendezvous" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL 119,IDC_STATIC,"Static",SS_BITMAP | SS_REALSIZEIMAGE,0,0, + 220,86 + CONTROL MASTER_PROD_VERS_STR3,IDC_COMPONENT,"Static", + SS_SIMPLE | WS_GROUP,92,10,122,8 + LTEXT "Copyright (c) 2004 Apple Computer, Inc. All rights reserved. Apple and the Apple logo are trademarks of Apple Computer, Inc., registered in the U.S. and other countries.", + IDC_LEGAL,92,31,123,50,0,WS_EX_RTLREADING +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_LOGIN, DIALOG + BEGIN + BOTTOMMARGIN, 62 + END + + IDD_ABOUT, DIALOG + BEGIN + BOTTOMMARGIN, 132 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_LOGO BITMAP "res\\logo.bmp" +IDB_ABOUT BITMAP "res\\about.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_BUTTON_2K ICON "res\\button-2k.ico" +IDI_BUTTON_XP ICON "res\\button-xp.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_CONTEXT_MENU MENU +BEGIN + POPUP "ContextMenu" + BEGIN + MENUITEM "About Rendezvous...", ID_Menu + MENUITEM SEPARATOR + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_NAME "Rendezvous" + IDS_WEB_SITES "Web Sites" + IDS_FTP_SITES "FTP Sites" + IDS_PRINTERS "Printers" + IDS_MDNSRESPONDER_NOT_AVAILABLE "Rendezvous Service Not Available" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#define _AFX_NO_SPLITTER_RESOURCES +#define _AFX_NO_OLE_RESOURCES +#define _AFX_NO_TRACKER_RESOURCES +#define _AFX_NO_PROPERTY_RESOURCES + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE 9, 1 +#pragma code_page(1252) +#include "afxres.rc" // Standard components +#endif + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Clients/ExplorerPlugin/ExplorerPlugin.vcproj b/Clients/ExplorerPlugin/ExplorerPlugin.vcproj new file mode 100644 index 0000000..62f37fc --- /dev/null +++ b/Clients/ExplorerPlugin/ExplorerPlugin.vcproj @@ -0,0 +1,279 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Clients/ExplorerPlugin/LoginDialog.cpp b/Clients/ExplorerPlugin/LoginDialog.cpp new file mode 100644 index 0000000..ccee81c --- /dev/null +++ b/Clients/ExplorerPlugin/LoginDialog.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: LoginDialog.cpp,v $ +Revision 1.2 2004/07/13 21:24:21 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:34:59 rpantos +Move to Clients from mDNSWindows + +Revision 1.1 2004/01/30 03:01:56 bradley +Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. + +*/ + +#include +#include + +#include "stdafx.h" + +#include "LoginDialog.h" + +// MFC Debugging + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +//=========================================================================================================================== +// Message Map +//=========================================================================================================================== + +BEGIN_MESSAGE_MAP( LoginDialog, CDialog ) +END_MESSAGE_MAP() + +//=========================================================================================================================== +// LoginDialog +//=========================================================================================================================== + +LoginDialog::LoginDialog( CWnd *inParent ) + : CDialog( LoginDialog::IDD, inParent ) +{ + // +} + +//=========================================================================================================================== +// OnInitDialog +//=========================================================================================================================== + +BOOL LoginDialog::OnInitDialog( void ) +{ + CDialog::OnInitDialog(); + return( TRUE ); +} + +//=========================================================================================================================== +// DoDataExchange +//=========================================================================================================================== + +void LoginDialog::DoDataExchange( CDataExchange *inDX ) +{ + CDialog::DoDataExchange( inDX ); +} + +//=========================================================================================================================== +// OnOK +//=========================================================================================================================== + +void LoginDialog::OnOK( void ) +{ + const CWnd * control; + + // Username + + control = GetDlgItem( IDC_LOGIN_USERNAME_TEXT ); + assert( control ); + if( control ) + { + control->GetWindowText( mUsername ); + } + + // Password + + control = GetDlgItem( IDC_LOGIN_PASSWORD_TEXT ); + assert( control ); + if( control ) + { + control->GetWindowText( mPassword ); + } + + CDialog::OnOK(); +} + +//=========================================================================================================================== +// GetLogin +//=========================================================================================================================== + +BOOL LoginDialog::GetLogin( CString &outUsername, CString &outPassword ) +{ + if( DoModal() == IDOK ) + { + outUsername = mUsername; + outPassword = mPassword; + return( TRUE ); + } + return( FALSE ); +} diff --git a/Clients/ExplorerPlugin/LoginDialog.h b/Clients/ExplorerPlugin/LoginDialog.h new file mode 100644 index 0000000..a89c3a6 --- /dev/null +++ b/Clients/ExplorerPlugin/LoginDialog.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: LoginDialog.h,v $ +Revision 1.2 2004/07/13 21:24:21 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:34:59 rpantos +Move to Clients from mDNSWindows + +Revision 1.1 2004/01/30 03:01:56 bradley +Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. + +*/ + +#ifndef __LOGIN_DIALOG__ +#define __LOGIN_DIALOG__ + +#pragma once + +#include "Resource.h" + +//=========================================================================================================================== +// LoginDialog +//=========================================================================================================================== + +class LoginDialog : public CDialog +{ + protected: + + CString mUsername; + CString mPassword; + + public: + + enum { IDD = IDD_LOGIN }; + + LoginDialog( CWnd *inParent = NULL ); + + virtual BOOL GetLogin( CString &outUsername, CString &outPassword ); + + protected: + + virtual BOOL OnInitDialog( void ); + virtual void DoDataExchange( CDataExchange *inDX ); + virtual void OnOK( void ); + + DECLARE_MESSAGE_MAP() +}; + +#endif // __LOGIN_DIALOG__ diff --git a/Clients/ExplorerPlugin/ReadMe.txt b/Clients/ExplorerPlugin/ReadMe.txt new file mode 100644 index 0000000..2fbe666 --- /dev/null +++ b/Clients/ExplorerPlugin/ReadMe.txt @@ -0,0 +1,9 @@ +The DNSServices Explorer Plugin is a vertical Explorer bar. It lets you browse for DNS-SD advertised services directly within Internet Explorer. + +This DLL needs to be registered to work. The Visual Studio project automatically registers the DLL after each successful build. If you need to manually register the DLL for some reason, you can execute the following line from the DOS command line prompt ("" is the actual parent path of the DLL): + +regsvr32.exe /s /c \ExplorerPlugin.dll + +For more information, see the Band Objects topic in the Microsoft Platform SDK documentation and check the following URL: + + diff --git a/Clients/ExplorerPlugin/Resource.h b/Clients/ExplorerPlugin/Resource.h new file mode 100644 index 0000000..2e56708 --- /dev/null +++ b/Clients/ExplorerPlugin/Resource.h @@ -0,0 +1,32 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by ExplorerPlugin.rc +// +#define IDS_NAME 106 +#define IDS_WEB_SITES 107 +#define IDS_FTP_SITES 108 +#define IDS_PRINTERS 109 +#define IDS_MDNSRESPONDER_NOT_AVAILABLE 110 +#define IDB_LOGO 115 +#define IDI_BUTTON_2K 115 +#define IDD_ABOUT 118 +#define IDI_BUTTON_XP 118 +#define IDB_ABOUT 119 +#define IDR_CONTEXT_MENU 120 +#define IDD_LOGIN 145 +#define IDC_COMPONENT 1001 +#define IDC_LEGAL 1002 +#define IDC_LOGIN_USERNAME_TEXT 1182 +#define IDC_LOGIN_PASSWORD_TEXT 1183 +#define ID_Menu 40001 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 119 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Clients/ExplorerPlugin/StdAfx.cpp b/Clients/ExplorerPlugin/StdAfx.cpp new file mode 100644 index 0000000..b97d864 --- /dev/null +++ b/Clients/ExplorerPlugin/StdAfx.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: StdAfx.cpp,v $ +Revision 1.2 2004/07/13 21:24:21 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:34:59 rpantos +Move to Clients from mDNSWindows + +Revision 1.1 2004/01/30 03:01:56 bradley +Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. + +*/ + +#include "StdAfx.h" diff --git a/Clients/ExplorerPlugin/StdAfx.h b/Clients/ExplorerPlugin/StdAfx.h new file mode 100644 index 0000000..ca625a1 --- /dev/null +++ b/Clients/ExplorerPlugin/StdAfx.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: StdAfx.h,v $ +Revision 1.2 2004/07/13 21:24:21 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:34:59 rpantos +Move to Clients from mDNSWindows + +Revision 1.1 2004/01/30 03:01:56 bradley +Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. + +*/ + +#ifndef __STDAFX__ +#define __STDAFX__ + +#pragma once + +#ifndef VC_EXTRALEAN +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers +#endif + +#include // MFC core and standard components +#include // MFC extensions +#include // MFC support for Internet Explorer 4 Common Controls +#ifndef _AFX_NO_AFXCMN_SUPPORT + #include // MFC support for Windows Common Controls +#endif + +#include +#include // MFC socket extensions + +#endif // __STDAFX__ diff --git a/Clients/ExplorerPlugin/res/about.bmp b/Clients/ExplorerPlugin/res/about.bmp new file mode 100644 index 0000000..ab5e430 Binary files /dev/null and b/Clients/ExplorerPlugin/res/about.bmp differ diff --git a/Clients/ExplorerPlugin/res/button-2k.ico b/Clients/ExplorerPlugin/res/button-2k.ico new file mode 100755 index 0000000..3699b44 Binary files /dev/null and b/Clients/ExplorerPlugin/res/button-2k.ico differ diff --git a/Clients/ExplorerPlugin/res/button-xp.ico b/Clients/ExplorerPlugin/res/button-xp.ico new file mode 100755 index 0000000..69d41dd Binary files /dev/null and b/Clients/ExplorerPlugin/res/button-xp.ico differ diff --git a/Clients/ExplorerPlugin/res/cold.ico b/Clients/ExplorerPlugin/res/cold.ico new file mode 100644 index 0000000..3699b44 Binary files /dev/null and b/Clients/ExplorerPlugin/res/cold.ico differ diff --git a/Clients/ExplorerPlugin/res/hot.ico b/Clients/ExplorerPlugin/res/hot.ico new file mode 100644 index 0000000..95b03d0 Binary files /dev/null and b/Clients/ExplorerPlugin/res/hot.ico differ diff --git a/Clients/ExplorerPlugin/res/logo.bmp b/Clients/ExplorerPlugin/res/logo.bmp new file mode 100644 index 0000000..f866bd7 Binary files /dev/null and b/Clients/ExplorerPlugin/res/logo.bmp differ diff --git a/Clients/Java/BrowserApp.java b/Clients/Java/BrowserApp.java index d3805ae..0b55fa5 100644 --- a/Clients/Java/BrowserApp.java +++ b/Clients/Java/BrowserApp.java @@ -1,40 +1,42 @@ /* * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ - * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - - Change History (most recent first): - -$Log: BrowserApp.java,v $ -Revision 1.3 2004/05/26 01:41:58 cheshire -Pass proper flags to DNSSD.enumerateDomains - -Revision 1.2 2004/04/30 21:53:34 rpantos -Change line endings for CVS. - -Revision 1.1 2004/04/30 16:29:35 rpantos -First checked in. - - BrowserApp demonstrates how to use DNSSD to browse for and resolve services. + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + BrowserApp demonstrates how to use DNS-SD to browse for and resolve services. To do: - display resolved TXTRecord @@ -75,7 +77,7 @@ class BrowserApp implements ListSelectionListener, ResolveListener try { domainBrowser = DNSSD.enumerateDomains( DNSSD.BROWSE_DOMAINS, 0, domainList); - servicesBrowser = DNSSD.browse( 0, 0, "_services._mdns._udp.", "", servicesList); + servicesBrowser = DNSSD.browse( 0, 0, "_services._dns-sd._udp.", "", servicesList); serviceBrowser = null; } catch ( Exception ex) { terminateWithException( ex); } @@ -142,7 +144,7 @@ class BrowserApp implements ListSelectionListener, ResolveListener if ( serviceBrowser != null) serviceBrowser.stop(); serviceList.removeAllElements(); - servicesBrowser = DNSSD.browse( 0, 0, "_services._mdns._udp.", "", servicesList); + servicesBrowser = DNSSD.browse( 0, 0, "_services._dns-sd._udp.", "", servicesList); } } else if ( e.getSource() == servicesPane && !e.getValueIsAdjusting()) diff --git a/Clients/Java/BrowserApp.manifest b/Clients/Java/BrowserApp.manifest new file mode 100644 index 0000000..7c392a1 --- /dev/null +++ b/Clients/Java/BrowserApp.manifest @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 +Main-Class: BrowserApp diff --git a/Clients/Java/DNSSDUnitTest.java b/Clients/Java/DNSSDUnitTest.java index e4523e1..786cab7 100644 --- a/Clients/Java/DNSSDUnitTest.java +++ b/Clients/Java/DNSSDUnitTest.java @@ -3,8 +3,6 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -25,6 +23,9 @@ Change History (most recent first): $Log: DNSSDUnitTest.java,v $ +Revision 1.4 2004/08/04 01:07:43 rpantos +Update unit test for . + Revision 1.3 2004/05/26 01:41:58 cheshire Pass proper flags to DNSSD.enumerateDomains @@ -67,19 +68,34 @@ class DNSSDUnitTest String a; txtRecord.set( "path", "~/names"); + txtRecord.set( "rw", (String) null); + txtRecord.set( "empty", ""); txtRecord.set( "ttl", "4"); byte[] rawBytes = txtRecord.getRawBytes(); System.out.println( ( new String( rawBytes, 0, rawBytes.length)) + " has count " + String.valueOf( txtRecord.size())); + System.out.println( txtRecord); boolean ttlPresent = txtRecord.contains( "ttl"); System.out.println( "ttl is present: " + ( ttlPresent ? "true" : "false")); boolean timeoutPresent = txtRecord.contains( "timeout"); System.out.println( "timeout is present: " + ( timeoutPresent ? "true" : "false")); - for ( int i=0; null != ( a = txtRecord.getKey( i)); i++) - System.out.println( "attr/val " + String.valueOf( i) + ": " + a + "," + txtRecord.getValueAsString( i)); + txtRecord.set( "path", "~/numbers"); + System.out.println( txtRecord); + + txtRecord.remove( "ttl"); + System.out.println( txtRecord); + + txtRecord.remove( "path"); + System.out.println( txtRecord); + + txtRecord.remove( "at"); + System.out.println( txtRecord); + + txtRecord.set( "rw", "1"); + System.out.println( txtRecord); } public void run() throws DNSSDException diff --git a/Clients/Java/SimpleChat.java b/Clients/Java/SimpleChat.java index 07f58d2..745b9a0 100644 --- a/Clients/Java/SimpleChat.java +++ b/Clients/Java/SimpleChat.java @@ -1,42 +1,45 @@ /* * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ - * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - - Change History (most recent first): - -$Log: SimpleChat.java,v $ -Revision 1.2 2004/04/30 21:53:35 rpantos -Change line endings for CVS. - -Revision 1.1 2004/04/30 16:29:35 rpantos -First checked in. + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SimpleChat is a simple peer-to-peer chat program that demonstrates - DNSSD registration, browsing, resolving and record-querying. + DNS-SD registration, browsing, resolving and record-querying. To do: - - remove TXTRecord tests - - remove diagnostic printf's - implement better coloring algorithm */ @@ -94,11 +97,7 @@ class SimpleChat implements ResolveListener, RegisterListener, QueryListener, browser = DNSSD.browse( 0, 0, kChatExampleRegType, "", new SwingBrowseListener( targetList)); - TXTRecord tRec = new TXTRecord(); - tRec.set( "name", "roger"); - tRec.set( "color", "blue"); - - registration = DNSSD.register( 0, 0, ourName, kChatExampleRegType, "", "", inSocket.getLocalPort(), tRec, this); + registration = DNSSD.register( 0, 0, ourName, kChatExampleRegType, "", "", inSocket.getLocalPort(), null, this); new ListenerThread( this, inSocket, dataPacket).start(); } @@ -155,7 +154,6 @@ class SimpleChat implements ResolveListener, RegisterListener, QueryListener, String serviceName, String regType, String domain) { ourName = serviceName; // might have been renamed on collision -System.out.println( "ServiceRegisterCallback() invoked as " + serviceName); } public void operationFailed( DNSSDService service, int errorCode) @@ -166,10 +164,6 @@ System.out.println( "ServiceRegisterCallback() invoked as " + serviceName); public void serviceResolved( DNSSDService resolver, int flags, int ifIndex, String fullName, String hostName, int port, TXTRecord txtRecord) { -String a; -for ( int i=0; null != ( a = txtRecord.getKey( i)); i++) - System.out.println( "attr/val " + String.valueOf( i) + ": " + a + "," + txtRecord.getValueAsString( i)); - buddyPort = port; try { // Start a record query to obtain IP address from hostname diff --git a/Clients/Java/SimpleChat.manifest b/Clients/Java/SimpleChat.manifest new file mode 100644 index 0000000..45c0202 --- /dev/null +++ b/Clients/Java/SimpleChat.manifest @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 +Main-Class: SimpleChat diff --git a/Clients/Java/SwingBrowseListener.java b/Clients/Java/SwingBrowseListener.java index 4855dde..78bfe4f 100644 --- a/Clients/Java/SwingBrowseListener.java +++ b/Clients/Java/SwingBrowseListener.java @@ -1,36 +1,40 @@ /* * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ - * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - - Change History (most recent first): - -$Log: SwingBrowseListener.java,v $ -Revision 1.2 2004/04/30 21:53:35 rpantos -Change line endings for CVS. - -Revision 1.1 2004/04/30 16:29:35 rpantos -First checked in. - + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ diff --git a/Clients/Java/SwingDomainListener.java b/Clients/Java/SwingDomainListener.java index 2c5840f..c6c380b 100644 --- a/Clients/Java/SwingDomainListener.java +++ b/Clients/Java/SwingDomainListener.java @@ -1,36 +1,40 @@ /* * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ - * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - - Change History (most recent first): - -$Log: SwingDomainListener.java,v $ -Revision 1.2 2004/04/30 21:53:35 rpantos -Change line endings for CVS. - -Revision 1.1 2004/04/30 16:29:35 rpantos -First checked in. - + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ diff --git a/Clients/Java/SwingQueryListener.java b/Clients/Java/SwingQueryListener.java index 4b58e67..5f2de1d 100644 --- a/Clients/Java/SwingQueryListener.java +++ b/Clients/Java/SwingQueryListener.java @@ -1,36 +1,40 @@ /* * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ - * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - - Change History (most recent first): - -$Log: SwingQueryListener.java,v $ -Revision 1.2 2004/04/30 21:53:35 rpantos -Change line endings for CVS. - -Revision 1.1 2004/04/30 16:29:35 rpantos -First checked in. - + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ diff --git a/Clients/Java/SwingResolveListener.java b/Clients/Java/SwingResolveListener.java index 1422a6e..f2be4b0 100644 --- a/Clients/Java/SwingResolveListener.java +++ b/Clients/Java/SwingResolveListener.java @@ -1,36 +1,40 @@ /* * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ - * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - - Change History (most recent first): - -$Log: SwingResolveListener.java,v $ -Revision 1.2 2004/06/14 23:46:55 cheshire -Recreate CVS state - -Revision 1.1 2004/06/14 23:46:26 cheshire -First checkin - + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ diff --git a/Clients/Java/nmakefile b/Clients/Java/nmakefile new file mode 100644 index 0000000..4c655d8 --- /dev/null +++ b/Clients/Java/nmakefile @@ -0,0 +1,106 @@ +# Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +# +# +# This Makefile builds .jar files for the DNS-SD Java sample apps. +# You must have the Java support installed. +# +# nmake with no arguments builds all production targets. +# 'nmake DEBUG=1' to build debugging targets. +# 'nmake clean' or 'nmake clean DEBUG=1' to delete prod/debug objects & targets +# +# To run nmake, you may need to set up your PATH correctly, using a script +# such as: "\Program Files\Microsoft Visual Studio .NET\Common7\tools\vsvars32.bat" +# +# The default location of the JDK is \javasdk. You can override this on the +# command line (e.g. 'nmake JDK=\j2dk1.4.2_03'). + +############################################################################ + +JDK = \javasdk + +CP = copy +RM = del /Q +RMDIR = rmdir /S /Q +JAVAC = $(JDK)\bin\javac +JAVAH = $(JDK)\bin\javah +JAR = $(JDK)\bin\jar + +# Set up diverging paths for debug vs. prod builds +DEBUG=0 +!if $(DEBUG) == 1 +JFLAGS = -g +OBJDIR = objects\debug +BUILDDIR = build\debug +!else +JFLAGS = +OBJDIR = objects\prod +BUILDDIR = build\prod +!endif + +SCOBJ = $(OBJDIR)\SimpleChat +BAOBJ = $(OBJDIR)\BrowserApp + +############################################################################# + +all: setup Java + +# 'setup' sets up the build directory structure the way we want +setup: + @if not exist objects mkdir objects + @if not exist build mkdir build + @if not exist $(OBJDIR) mkdir $(OBJDIR) + @if not exist $(SCOBJ) mkdir $(SCOBJ) + @if not exist $(BAOBJ) mkdir $(BAOBJ) + @if not exist $(BUILDDIR) mkdir $(BUILDDIR) + +# clean removes targets and objects +clean: + @if exist $(OBJDIR) $(RMDIR) $(OBJDIR) + @if exist $(BUILDDIR) $(RMDIR) $(BUILDDIR) + +############################################################################# + +Java: setup $(BUILDDIR)\SimpleChat.jar $(BUILDDIR)\BrowserApp.jar + @echo "Build complete" + +SIMPLECHATOBJ = $(SCOBJ)\SwingBrowseListener.class \ + $(SCOBJ)\SwingQueryListener.class \ + $(SCOBJ)\SimpleChat.class +SIMPLECHATMAN = SimpleChat.manifest + +$(BUILDDIR)\SimpleChat.jar: $(SIMPLECHATOBJ) $(SIMPLECHATMAN) + $(JAR) -cfm $@ $(SIMPLECHATMAN) -C $(SCOBJ) . + +BROWSERAPPOBJ = $(BAOBJ)\SwingResolveListener.class \ + $(BAOBJ)\BrowserApp.class +BROWSERAPPMAN = BrowserApp.manifest + +$(BUILDDIR)\BrowserApp.jar: $(BROWSERAPPOBJ) $(BROWSERAPPMAN) + $(JAR) -cfm $@ $(BROWSERAPPMAN) -C $(BAOBJ) . + +JAVASRC = . +.SUFFIXES : .java +{$(JAVASRC)}.java{$(BAOBJ)}.class: + $(JAVAC) -d $(BAOBJ) -classpath $(BAOBJ) $< +{$(JAVASRC)}.java{$(SCOBJ)}.class: + $(JAVAC) -d $(SCOBJ) -classpath $(SCOBJ) $< + diff --git a/Clients/Makefile b/Clients/Makefile index 11f4a83..709bd2b 100755 --- a/Clients/Makefile +++ b/Clients/Makefile @@ -20,6 +20,12 @@ # @APPLE_LICENSE_HEADER_END@ # # $Log: Makefile,v $ +# Revision 1.6 2004/09/24 21:15:26 cheshire +# Library "libmdns" misnamed; should be "libdns_sd" +# +# Revision 1.5 2004/09/02 17:32:45 cheshire +# Look for headers in ../mDNSShared before we go to /usr/include +# # Revision 1.4 2004/05/21 17:25:56 cheshire # Fixes to make sample client work on Linux # @@ -43,9 +49,9 @@ ############################################################################# -# If library /usr/lib/libmdns.* exists, then link it -ifneq "$(wildcard /usr/lib/libmdns.*)" "" -LIBS = -lmdns +# If library /usr/lib/libdns_sd.* exists, then link it +ifneq "$(wildcard /usr/lib/libdns_sd.*)" "" +LIBS = -ldns_sd else LIBS = endif @@ -59,4 +65,4 @@ build: mkdir build build/dns-sd: build dns-sd.c - cc $(filter %.c %.o, $+) $(LIBS) -o $@ + cc $(filter %.c %.o, $+) $(LIBS) -I../mDNSShared -o $@ diff --git a/Clients/PrinterSetupWizard/About.cpp b/Clients/PrinterSetupWizard/About.cpp new file mode 100644 index 0000000..6fda710 --- /dev/null +++ b/Clients/PrinterSetupWizard/About.cpp @@ -0,0 +1,31 @@ +// About.cpp : implementation file +// + +#include "stdafx.h" +#include "PrinterSetupWizardApp.h" +#include "About.h" + + +// CAbout dialog + +IMPLEMENT_DYNAMIC(CAbout, CDialog) +CAbout::CAbout(CWnd* pParent /*=NULL*/) + : CDialog(CAbout::IDD, pParent) +{ +} + +CAbout::~CAbout() +{ +} + +void CAbout::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); +} + + +BEGIN_MESSAGE_MAP(CAbout, CDialog) +END_MESSAGE_MAP() + + +// CAbout message handlers diff --git a/Clients/PrinterSetupWizard/About.h b/Clients/PrinterSetupWizard/About.h new file mode 100644 index 0000000..1d84b7f --- /dev/null +++ b/Clients/PrinterSetupWizard/About.h @@ -0,0 +1,21 @@ +#pragma once + + +// CAbout dialog + +class CAbout : public CDialog +{ + DECLARE_DYNAMIC(CAbout) + +public: + CAbout(CWnd* pParent = NULL); // standard constructor + virtual ~CAbout(); + +// Dialog Data + enum { IDD = IDD_DIALOG1 }; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + + DECLARE_MESSAGE_MAP() +}; diff --git a/Clients/PrinterSetupWizard/FirstPage.cpp b/Clients/PrinterSetupWizard/FirstPage.cpp new file mode 100644 index 0000000..c71faf5 --- /dev/null +++ b/Clients/PrinterSetupWizard/FirstPage.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: FirstPage.cpp,v $ +Revision 1.2 2004/07/13 20:15:04 shersche + Load large font name from resource +Bug #: 3726363 + +Revision 1.1 2004/06/18 04:36:57 rpantos +First checked in + + +*/ + +#include "stdafx.h" +#include "PrinterSetupWizardApp.h" +#include "FirstPage.h" + + +// CFirstPage dialog + +IMPLEMENT_DYNAMIC(CFirstPage, CPropertyPage) +CFirstPage::CFirstPage() + : CPropertyPage(CFirstPage::IDD) +{ + CString fontName; + + m_psp.dwFlags &= ~(PSP_HASHELP); + m_psp.dwFlags |= PSP_DEFAULT|PSP_HIDEHEADER; + + fontName.LoadString(IDS_LARGE_FONT); + + // create the large font + m_largeFont.CreateFont(-16, 0, 0, 0, + FW_BOLD, FALSE, FALSE, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, fontName); +} + +CFirstPage::~CFirstPage() +{ +} + +void CFirstPage::DoDataExchange(CDataExchange* pDX) +{ + CPropertyPage::DoDataExchange(pDX); + DDX_Control(pDX, IDC_GREETING, m_greeting); +} + + +BOOL +CFirstPage::OnSetActive() +{ + CPropertySheet* psheet = (CPropertySheet*) GetParent(); + + psheet->SetWizardButtons(PSWIZB_NEXT); + + m_greeting.SetFont(&m_largeFont); + + CString greetingText; + + greetingText.LoadString(IDS_GREETING); + m_greeting.SetWindowText(greetingText); + + return CPropertyPage::OnSetActive(); +} + + +BEGIN_MESSAGE_MAP(CFirstPage, CPropertyPage) +END_MESSAGE_MAP() + + +// CFirstPage message handlers diff --git a/Clients/PrinterSetupWizard/FirstPage.h b/Clients/PrinterSetupWizard/FirstPage.h new file mode 100644 index 0000000..5c23bdf --- /dev/null +++ b/Clients/PrinterSetupWizard/FirstPage.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: FirstPage.h,v $ +Revision 1.1 2004/06/18 04:36:57 rpantos +First checked in + + +*/ + +#pragma once +#include "afxwin.h" + + +// CFirstPage dialog + +class CFirstPage : public CPropertyPage +{ + DECLARE_DYNAMIC(CFirstPage) + +public: + CFirstPage(); + virtual ~CFirstPage(); + +// Dialog Data + enum { IDD = IDD_FIRST_PAGE }; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual BOOL OnSetActive(); + + + DECLARE_MESSAGE_MAP() + +private: + + CFont m_largeFont; + +public: + + CStatic m_greeting; +}; diff --git a/Clients/PrinterSetupWizard/FourthPage.cpp b/Clients/PrinterSetupWizard/FourthPage.cpp new file mode 100644 index 0000000..c4ccc52 --- /dev/null +++ b/Clients/PrinterSetupWizard/FourthPage.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: FourthPage.cpp,v $ +Revision 1.4 2004/07/13 20:15:04 shersche + Load large font name from resource +Bug #: 3726363 + +Revision 1.3 2004/07/12 06:59:03 shersche + Use resource strings for Yes/No +Bug #: 3723695 + +Revision 1.2 2004/06/26 23:27:12 shersche +support for installing multiple printers of the same name + +Revision 1.1 2004/06/18 04:36:57 rpantos +First checked in + + +*/ + +#include "stdafx.h" +#include "PrinterSetupWizardApp.h" +#include "PrinterSetupWizardSheet.h" +#include "FourthPage.h" + + +// CFourthPage dialog + +IMPLEMENT_DYNAMIC(CFourthPage, CPropertyPage) +CFourthPage::CFourthPage() + : CPropertyPage(CFourthPage::IDD), + m_initialized(false) +{ + CString fontName; + + m_psp.dwFlags &= ~(PSP_HASHELP); + m_psp.dwFlags |= PSP_DEFAULT|PSP_HIDEHEADER; + + fontName.LoadString(IDS_LARGE_FONT); + + // create the large font + m_largeFont.CreateFont(-16, 0, 0, 0, + FW_BOLD, FALSE, FALSE, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, fontName); +} + +CFourthPage::~CFourthPage() +{ +} + +void CFourthPage::DoDataExchange(CDataExchange* pDX) +{ + CPropertyPage::DoDataExchange(pDX); + DDX_Control(pDX, IDC_GOODBYE, m_goodbye); + DDX_Control(pDX, IDC_PRINTER_NAME, m_printerNameCtrl); + DDX_Control(pDX, IDC_PRINTER_MANUFACTURER, m_printerManufacturerCtrl); + DDX_Control(pDX, IDC_PRINTER_MODEL, m_printerModelCtrl); + DDX_Control(pDX, IDC_PRINTER_PORT, m_printerPortCtrl); + DDX_Control(pDX, IDC_PRINTER_DEFAULT, m_printerDefault); +} + + +BEGIN_MESSAGE_MAP(CFourthPage, CPropertyPage) +END_MESSAGE_MAP() + + +// CFourthPage message handlers +OSStatus +CFourthPage::OnInitPage() +{ + return kNoErr; +} + + +BOOL +CFourthPage::OnSetActive() +{ + CPrinterSetupWizardSheet * psheet; + CString goodbyeText; + Printer * printer; + CString defaultText; + + psheet = reinterpret_cast(GetParent()); + require_quiet( psheet, exit ); + + printer = psheet->GetSelectedPrinter(); + require_quiet( psheet, exit ); + + psheet->SetWizardButtons(PSWIZB_BACK|PSWIZB_FINISH); + + if (m_initialized == false) + { + m_initialized = true; + OnInitPage(); + } + + m_goodbye.SetFont(&m_largeFont); + + goodbyeText.LoadString(IDS_GOODBYE); + m_goodbye.SetWindowText(goodbyeText); + + m_printerNameCtrl.SetWindowText( printer->actualName ); + m_printerManufacturerCtrl.SetWindowText ( printer->manufacturer ); + m_printerModelCtrl.SetWindowText ( printer->model ); + m_printerPortCtrl.SetWindowText ( printer->portName ); + + if (printer->deflt) + { + defaultText.LoadString(IDS_YES); + } + else + { + defaultText.LoadString(IDS_NO); + } + + m_printerDefault.SetWindowText ( defaultText ); + +exit: + + return CPropertyPage::OnSetActive(); +} diff --git a/Clients/PrinterSetupWizard/FourthPage.h b/Clients/PrinterSetupWizard/FourthPage.h new file mode 100644 index 0000000..78bbf52 --- /dev/null +++ b/Clients/PrinterSetupWizard/FourthPage.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: FourthPage.h,v $ +Revision 1.1 2004/06/18 04:36:57 rpantos +First checked in + + +*/ + +#pragma once +#include "afxwin.h" + + +// CFourthPage dialog + +class CFourthPage : public CPropertyPage +{ + DECLARE_DYNAMIC(CFourthPage) + +public: + CFourthPage(); + virtual ~CFourthPage(); + +// Dialog Data + enum { IDD = IDD_FOURTH_PAGE }; + + virtual BOOL OnSetActive(); + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + + DECLARE_MESSAGE_MAP() + +private: + + OSStatus OnInitPage(); + CFont m_largeFont; + bool m_initialized; + + +public: + CStatic m_goodbye; +private: + CStatic m_printerNameCtrl; + CStatic m_printerManufacturerCtrl; + CStatic m_printerModelCtrl; + CStatic m_printerPortCtrl; + CStatic m_printerDefault; +}; diff --git a/Clients/PrinterSetupWizard/PrinterSetupWizard.ncb b/Clients/PrinterSetupWizard/PrinterSetupWizard.ncb new file mode 100644 index 0000000..9057eba --- /dev/null +++ b/Clients/PrinterSetupWizard/PrinterSetupWizard.ncb @@ -0,0 +1 @@ +Microsoft C/C++ MSF 7.00 diff --git a/Clients/PrinterSetupWizard/PrinterSetupWizard.rc b/Clients/PrinterSetupWizard/PrinterSetupWizard.rc new file mode 100644 index 0000000..f099a44 --- /dev/null +++ b/Clients/PrinterSetupWizard/PrinterSetupWizard.rc @@ -0,0 +1,346 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" +#include "WinVersRes.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Russian resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS) +#ifdef _WIN32 +LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT +#pragma code_page(1251) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "#include ""WinVersRes.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#define _AFX_NO_SPLITTER_RESOURCES\r\n" + "#define _AFX_NO_OLE_RESOURCES\r\n" + "#define _AFX_NO_TRACKER_RESOURCES\r\n" + "#define _AFX_NO_PROPERTY_RESOURCES\r\n" + "\r\n" + "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n" + "LANGUAGE 9, 1\r\n" + "#pragma code_page(1252)\r\n" + "#include ""res\\PrinterSetupWizard.rc2"" // non-Microsoft Visual C++ edited resources\r\n" + "#include ""afxres.rc"" // Standard components\r\n" + "#endif\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // Russian resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDR_MAINFRAME ICON "res\\Print.ico" +IDI_INFO ICON "res\\Info.ico" +IDI_PRINTER ICON "res\\NetworkPrinter.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_WATERMARK BITMAP "res\\watermark.bmp" +IDB_BANNER_ICON BITMAP "res\\banner_icon.bmp" +IDB_ABOUT BITMAP "res\\about.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUTBOX DIALOGEX 0, 0, 235, 55 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "About Printer Setup Wizard" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + ICON IDR_MAINFRAME,IDC_STATIC,11,17,20,20 + LTEXT "Printer Setup Wizard Version 1.0",IDC_STATIC,40,10,119, + 8,SS_NOPREFIX + LTEXT "Copyright (C) 2002",IDC_STATIC,40,25,119,8 + DEFPUSHBUTTON "OK",IDOK,178,7,50,16,WS_GROUP +END + +IDD_PRINTERSETUPWIZARD_DIALOG DIALOGEX 0, 0, 320, 200 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | + WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_APPWINDOW +CAPTION "Printer Setup Wizard" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,263,7,50,16 + PUSHBUTTON "Cancel",IDCANCEL,263,25,50,16 + PUSHBUTTON "Start Wizard",IDC_BUTTON1,100,86,109,35 +END + +IDD_SECOND_PAGE DIALOGEX 0, 0, 290, 154 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Rendezvous Printer Wizard" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "Shared Printers:",IDC_STATIC,3,0,171,8 + CONTROL "",IDC_BROWSE_LIST,"SysTreeView32",TVS_DISABLEDRAGDROP | + TVS_SHOWSELALWAYS | TVS_FULLROWSELECT | WS_BORDER | + WS_TABSTOP,2,11,286,142 +END + +IDD_FIRST_PAGE DIALOGEX 0, 0, 290, 199 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Rendezvous Printer Wizard" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "Welcome to the Rendezvous Printer Setup Wizard",IDC_GREETING, + 114,7,171,46 + LTEXT "Click next to continue.",IDC_STATIC,115,188,143,8 + LTEXT "This wizard helps you connect to a shared printer using Rendezvous. Make sure your printer is turned on and connected to your network.", + IDC_STATIC,146,60,139,62 + ICON IDI_INFO,IDC_STATIC,118,60,21,20,SS_REALSIZEIMAGE, + WS_EX_TRANSPARENT +END + +IDD_THIRD_PAGE DIALOGEX 0, 0, 290, 154 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Rendezvous Printer Wizard" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_PRINTER_MODEL,"SysListView32",LVS_REPORT | + LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_SORTASCENDING | + LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,110,58,178,76 + CONTROL "",IDC_PRINTER_MANUFACTURER,"SysListView32",LVS_REPORT | + LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_SORTASCENDING | + LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,2,58,105,76 + ICON IDI_PRINTER,IDC_PRINTER_IMAGE,1,0,21,20,SS_REALSIZEIMAGE + LTEXT "",IDC_PRINTER_NAME,40,5,173,8 + LTEXT "The Rendezvous Printer Wizard has auto-selected the following printer settings. Click 'Next' to continue installing this printer.", + IDC_PRINTER_SELECTION_TEXT,40,23,243,26 + CONTROL "Use this printer as the default printer", + IDC_DEFAULT_PRINTER,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,3,142,224,10 + PUSHBUTTON "Have Disk...",IDC_HAVE_DISK,238,140,50,14 +END + +IDD_FOURTH_PAGE DIALOGEX 0, 0, 290, 199 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Rendezvous Printer Wizard" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Completing the Rendezvous Printer Wizard",IDC_GOODBYE, + 116,7,171,27 + LTEXT "You have successfully completed the Rendezvous Printer Wizard. The printer has the following settings:", + IDC_STATIC,116,42,158,31 + LTEXT "Name:",IDC_STATIC,116,78,22,8 + LTEXT "Manufacturer:",IDC_STATIC,116,91,47,8 + LTEXT "Model:",IDC_STATIC,116,104,22,8 + LTEXT "Port:",IDC_STATIC,116,117,17,8 + LTEXT "Default:",IDC_STATIC,116,130,27,8 + LTEXT "",IDC_PRINTER_NAME,172,79,113,8 + LTEXT "",IDC_PRINTER_MANUFACTURER,172,91,113,8 + LTEXT "",IDC_PRINTER_MODEL,172,103,113,8 + LTEXT "",IDC_PRINTER_PORT,172,116,113,8 + LTEXT "",IDC_PRINTER_DEFAULT,172,130,113,8 + LTEXT "To close this wizard, click Finish.",IDC_STATIC,116,187, + 103,8 +END + +IDD_DIALOG1 DIALOGEX 0, 0, 265, 130 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Dialog" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL 138,IDC_STATIC,"Static",SS_BITMAP | SS_REALSIZEIMAGE,0,0, + 265,131 + LTEXT "Static",IDC_STATIC,77,12,19,8 + LTEXT "Static",IDC_STATIC,71,46,19,8 + LTEXT "Static",IDC_STATIC,71,89,19,8 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION MASTER_PROD_VERS + PRODUCTVERSION MASTER_PROD_VERS + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "Apple Computer, Inc." + VALUE "FileDescription", "Rendezvous Printer Wizard" + VALUE "FileVersion", MASTER_PROD_VERS_STR + VALUE "InternalName", "RendezvousPrinterWizard.exe" + VALUE "LegalCopyright", "Copyright (C) 2003-2004 Apple Computer, Inc." + VALUE "OriginalFilename", "RendezvousPrinterWizard.exe" + VALUE "ProductName", MASTER_PROD_NAME + VALUE "ProductVersion", MASTER_PROD_VERS_STR + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_SECOND_PAGE, DIALOG + BEGIN + BOTTOMMARGIN, 153 + END + + IDD_FIRST_PAGE, DIALOG + BEGIN + RIGHTMARGIN, 285 + BOTTOMMARGIN, 196 + END + + IDD_FOURTH_PAGE, DIALOG + BEGIN + RIGHTMARGIN, 285 + BOTTOMMARGIN, 197 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// RT_MANIFEST +// + +IDR_MANIFEST RT_MANIFEST "res\\PrinterSetupWizard.manifest" + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_ABOUTBOX "&About Printer Setup Wizard..." + IDS_GOODBYE "Completing the Rendezvous Printer Setup Wizard." + IDS_GREETING "Welcome to the Rendezvous Printer Setup Wizard" + IDS_BROWSE_TITLE "Browse for Rendezvous Printers" + IDS_BROWSE_SUBTITLE "Select the printer you want to use from the list below." + IDS_CAPTION "Rendezvous Printer Wizard" + IDS_GOODBYE_GOOD1 "You have successfully completed the Rendezvous Printer Wizard. The printer has the following settings:" + IDS_SEARCHING "Searching for printers..." + IDS_GOODBYTE_GOOD2 "To close this wizard, click Finish." + IDS_INSTALL_TITLE "Install Rendezvous Printer" + IDS_INSTALL_SUBTITLE "The manufacturer and model determine which printer software to use." +END + +STRINGTABLE +BEGIN + IDS_ERROR_SELECTING_PRINTER_TEXT + "There was an error selecting this printer." + IDS_ERROR_SELECTING_PRINTER_CAPTION "Error" + IDS_INSTALL_ERROR_CAPTION "Error" + IDS_INSTALL_ERROR_MESSAGE + "You do not have sufficient access to your computer to connect to the selected printer." + IDS_MANUFACTURER_HEADING "Manufacturer" + IDS_MODEL_HEADING "Model" + IDS_NO_RENDEZVOUS_PRINTERS "No Rendezvous Printers are available" + IDS_NO_MDNSRESPONDER_SERVICE_TEXT "Rendezvous Service is not available" + IDS_NO_MDNSRESPONDER_SERVICE_CAPTION "Error" + IDS_PRINTER_MATCH_GOOD "The Rendezvous Printer Wizard has auto-selected the following printer settings. Click 'Next' to continue installing this printer." + IDS_PRINTER_MATCH_BAD "The Rendezvous Printer Wizard cannot find a driver for this printer. Manually select from the list, or click 'Have Disk' if your printer came with an installation disk. " + IDS_YES "Yes" + IDS_NO "No" + IDS_LARGE_FONT "MS Sans Serif" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#define _AFX_NO_SPLITTER_RESOURCES +#define _AFX_NO_OLE_RESOURCES +#define _AFX_NO_TRACKER_RESOURCES +#define _AFX_NO_PROPERTY_RESOURCES + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE 9, 1 +#pragma code_page(1252) +#include "res\PrinterSetupWizard.rc2" // non-Microsoft Visual C++ edited resources +#include "afxres.rc" // Standard components +#endif + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Clients/PrinterSetupWizard/PrinterSetupWizard.vcproj b/Clients/PrinterSetupWizard/PrinterSetupWizard.vcproj new file mode 100644 index 0000000..280c398 --- /dev/null +++ b/Clients/PrinterSetupWizard/PrinterSetupWizard.vcproj @@ -0,0 +1,269 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Clients/PrinterSetupWizard/PrinterSetupWizardApp.cpp b/Clients/PrinterSetupWizard/PrinterSetupWizardApp.cpp new file mode 100644 index 0000000..295b4a9 --- /dev/null +++ b/Clients/PrinterSetupWizard/PrinterSetupWizardApp.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: PrinterSetupWizardApp.cpp,v $ +Revision 1.3 2004/07/13 21:24:23 rpantos +Fix for . + +Revision 1.2 2004/06/24 20:12:08 shersche +Clean up source code +Submitted by: herscher + +Revision 1.1 2004/06/18 04:36:57 rpantos +First checked in + + +*/ + +#include "stdafx.h" +#include "PrinterSetupWizardApp.h" +#include "PrinterSetupWizardSheet.h" +#include "DebugServices.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#endif + + +// CPrinterSetupWizardApp + +BEGIN_MESSAGE_MAP(CPrinterSetupWizardApp, CWinApp) + ON_COMMAND(ID_HELP, CWinApp::OnHelp) +END_MESSAGE_MAP() + + +// CPrinterSetupWizardApp construction + +CPrinterSetupWizardApp::CPrinterSetupWizardApp() +{ + // TODO: add construction code here, + // Place all significant initialization in InitInstance +} + + +// The one and only CPrinterSetupWizardApp object + +CPrinterSetupWizardApp theApp; + + +// CPrinterSetupWizardApp initialization + +BOOL CPrinterSetupWizardApp::InitInstance() +{ + // + // initialize the debugging framework + // + debug_initialize( kDebugOutputTypeWindowsDebugger, "PrinterSetupWizard", NULL ); + debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelTrace ); + + + // InitCommonControls() is required on Windows XP if an application + // manifest specifies use of ComCtl32.dll version 6 or later to enable + // visual styles. Otherwise, any window creation will fail. + InitCommonControls(); + + CWinApp::InitInstance(); + + AfxEnableControlContainer(); + + CPrinterSetupWizardSheet dlg(IDS_CAPTION); + + m_pMainWnd = &dlg; + + try + { + INT_PTR nResponse = dlg.DoModal(); + + if (nResponse == IDOK) + { + // TODO: Place code here to handle when the dialog is + // dismissed with OK + } + else if (nResponse == IDCANCEL) + { + // TODO: Place code here to handle when the dialog is + // dismissed with Cancel + } + } + catch (CPrinterSetupWizardSheet::WizardException & exc) + { + MessageBox(NULL, exc.text, exc.caption, MB_OK|MB_ICONEXCLAMATION); + } + + // Since the dialog has been closed, return FALSE so that we exit the + // application, rather than start the application's message pump. + return FALSE; +} diff --git a/Clients/PrinterSetupWizard/PrinterSetupWizardApp.h b/Clients/PrinterSetupWizard/PrinterSetupWizardApp.h new file mode 100644 index 0000000..aa3f233 --- /dev/null +++ b/Clients/PrinterSetupWizard/PrinterSetupWizardApp.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: PrinterSetupWizardApp.h,v $ +Revision 1.1 2004/06/18 04:36:57 rpantos +First checked in + + +*/ + +#pragma once + +#ifndef __AFXWIN_H__ + #error include 'stdafx.h' before including this file for PCH +#endif + +#include "resource.h" // main symbols + + +// CWiz97_3App: +// See Wiz97_3.cpp for the implementation of this class +// + +class CPrinterSetupWizardApp : public CWinApp +{ +public: + CPrinterSetupWizardApp(); + +// Overrides + public: + virtual BOOL InitInstance(); + +// Implementation + + DECLARE_MESSAGE_MAP() +}; + + +extern CPrinterSetupWizardApp theApp; diff --git a/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp b/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp new file mode 100644 index 0000000..02b2952 --- /dev/null +++ b/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp @@ -0,0 +1,1085 @@ +/* + * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: PrinterSetupWizardSheet.cpp,v $ +Revision 1.17 2004/10/12 18:02:53 shersche + Escape '/', '@', '"' characters in printui command. +Bug #: 3764873 + +Revision 1.16 2004/09/13 21:27:22 shersche + Pass the moreComing flag to OnAddPrinter and OnRemovePrinter callbacks +Bug #: 3796483 + +Revision 1.15 2004/09/11 05:59:06 shersche + Fix code that generates unique printer names based on currently installed printers +Bug #: 3785766 + +Revision 1.14 2004/09/02 01:57:58 cheshire + Fix incorrect testing of MoreComing flag + +Revision 1.13 2004/07/26 21:06:29 shersche + Removing trailing '.' in hostname +Bug #: 3739200 + +Revision 1.12 2004/07/13 21:24:23 rpantos +Fix for . + +Revision 1.11 2004/06/28 00:51:47 shersche +Move call to EnumPrinters out of browse callback into standalone function + +Revision 1.10 2004/06/27 23:06:47 shersche +code cleanup, make sure EnumPrinters returns non-zero value + +Revision 1.9 2004/06/27 15:49:31 shersche +clean up some cruft in the printer browsing code + +Revision 1.8 2004/06/27 08:04:51 shersche +copy selected printer to prevent printer being deleted out from under + +Revision 1.7 2004/06/26 23:27:12 shersche +support for installing multiple printers of the same name + +Revision 1.6 2004/06/26 21:22:39 shersche +handle spaces in file names + +Revision 1.5 2004/06/26 03:19:57 shersche +clean up warning messages + +Submitted by: herscher + +Revision 1.4 2004/06/25 02:26:52 shersche +Normalize key fields in text record entries +Submitted by: herscher + +Revision 1.3 2004/06/24 20:12:07 shersche +Clean up source code +Submitted by: herscher + +Revision 1.2 2004/06/23 17:58:21 shersche + eliminated memory leaks on exit + installation of a printer that is already installed results in a no-op +Bug #: 3701837, 3701926 +Submitted by: herscher + +Revision 1.1 2004/06/18 04:36:57 rpantos +First checked in + + +*/ + +#include "stdafx.h" +#include "PrinterSetupWizardApp.h" +#include "PrinterSetupWizardSheet.h" +#include "CommonServices.h" +#include "DebugServices.h" +#include "WinServices.h" +#include "About.h" +#include +#include +#include + +// unreachable code +#pragma warning(disable:4702) + + +#if( !TARGET_OS_WINDOWS_CE ) +# include +# include +#endif + +// Private Messages + +#define WM_SERVICE_EVENT ( WM_USER + 0x100 ) +#define WM_PROCESS_EVENT ( WM_USER + 0x101 ) + +// Service Types + +#define kPDLDataStreamServiceType "_pdl-datastream._tcp" +#define kLPRServiceType "_printer._tcp" +#define kIPPServiceType "_ipp._tcp" + + +// CPrinterSetupWizardSheet + +IMPLEMENT_DYNAMIC(CPrinterSetupWizardSheet, CPropertySheet) +CPrinterSetupWizardSheet::CPrinterSetupWizardSheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage) + :CPropertySheet(nIDCaption, pParentWnd, iSelectPage), + m_selectedPrinter(NULL) +{ + m_arrow = LoadCursor(0, IDC_ARROW); + m_wait = LoadCursor(0, IDC_APPSTARTING); + m_active = m_arrow; + + Init(); +} + + +CPrinterSetupWizardSheet::~CPrinterSetupWizardSheet() +{ + // + // rdar://problem/3701837 memory leaks + // + // Clean up the ServiceRef and printer list on exit + // + if (m_pdlBrowser != NULL) + { + DNSServiceRefDeallocate(m_pdlBrowser); + m_pdlBrowser = NULL; + } + + while (m_printerList.size() > 0) + { + Printer * printer = m_printerList.front(); + + m_printerList.pop_front(); + + delete printer; + } + + if (m_selectedPrinter != NULL) + { + delete m_selectedPrinter; + } +} + + +// ------------------------------------------------------ +// InstallEventHandler +// +// Installs an event handler for DNSService events. +// +int +CPrinterSetupWizardSheet::InstallEventHandler(EventHandler * handler) +{ + PrinterList::iterator iter; + + m_eventHandlerList.push_back(handler); + + iter = m_printerList.begin(); + + while (iter != m_printerList.end()) + { + Printer * printer = *iter++; + + handler->OnAddPrinter(printer, iter != m_printerList.end()); + } + + return kNoErr; +} + + +// ------------------------------------------------------ +// RemoveEventHandler +// +// Removes an event handler for DNSService events. +// +int +CPrinterSetupWizardSheet::RemoveEventHandler(EventHandler * handler) +{ + m_eventHandlerList.remove(handler); + + return kNoErr; +} + + + +// ------------------------------------------------------ +// SetSelectedPrinter +// +// Manages setting a printer as the printer to install. Stops +// any pending resolves. +// +OSStatus +CPrinterSetupWizardSheet::SetSelectedPrinter(Printer * printer) +{ + OSStatus err; + + // + // we only want one resolve going on at a time, so we check + // state of the m_selectedPrinter + // + if (m_selectedPrinter != NULL) + { + // + // if we're currently resolving, then stop the resolve + // + if (m_selectedPrinter->serviceRef) + { + err = StopResolve(m_selectedPrinter); + require_noerr(err, exit); + } + + delete m_selectedPrinter; + m_selectedPrinter = NULL; + } + + check( m_selectedPrinter == NULL ); + + try + { + m_selectedPrinter = new Printer; + } + catch (...) + { + m_selectedPrinter = NULL; + } + + require_action( m_selectedPrinter, exit, err = E_OUTOFMEMORY ); + + m_selectedPrinter->window = printer->window; + m_selectedPrinter->serviceRef = NULL; + m_selectedPrinter->item = NULL; + m_selectedPrinter->ifi = printer->ifi; + m_selectedPrinter->name = printer->name; + m_selectedPrinter->displayName = printer->displayName; + m_selectedPrinter->actualName = printer->actualName; + m_selectedPrinter->type = printer->type; + m_selectedPrinter->domain = printer->domain; + m_selectedPrinter->installed = printer->installed; + m_selectedPrinter->deflt = printer->deflt; + m_selectedPrinter->refs = 1; + + err = StartResolve(m_selectedPrinter); + require_noerr(err, exit); + +exit: + + return err; +} + + +// ------------------------------------------------------ +// InstallPrinter +// +// Installs a printer with Windows. +// +// NOTE: this works one of two ways, depending on whether +// there are drivers already installed for this printer. +// If there are, then we can just create a port with XcvData, +// and then call AddPrinter. If not, we use the printui.dll +// to install the printer. Actually installing drivers that +// are not currently installed is painful, and it's much +// easier and less error prone to just let printui.dll do +// the hard work for us. +// + +OSStatus +CPrinterSetupWizardSheet::InstallPrinter(Printer * printer) +{ + PRINTER_DEFAULTS printerDefaults = { NULL, NULL, SERVER_ACCESS_ADMINISTER }; + DWORD dwStatus; + DWORD cbInputData = 100; + PBYTE pOutputData = NULL; + DWORD cbOutputNeeded = 0; + PORT_DATA_1 portData; + HANDLE hXcv = NULL; + HANDLE hPrinter = NULL; + BOOL ok; + OSStatus err; + + check(printer != NULL); + check(printer->installed == false); + + ok = OpenPrinter(L",XcvMonitor Standard TCP/IP Port", &hXcv, &printerDefaults); + err = translate_errno( ok, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + // + // BUGBUG: MSDN said this is not required, but my experience shows it is required + // + try + { + pOutputData = new BYTE[cbInputData]; + } + catch (...) + { + pOutputData = NULL; + } + + require_action( pOutputData, exit, err = kNoMemoryErr ); + + // + // setup the port + // + ZeroMemory(&portData, sizeof(PORT_DATA_1)); + wcscpy(portData.sztPortName, printer->portName); + + portData.dwPortNumber = printer->portNumber; + portData.dwVersion = 1; + + portData.dwProtocol = PROTOCOL_RAWTCP_TYPE; + portData.cbSize = sizeof PORT_DATA_1; + portData.dwReserved = 0L; + + wcscpy(portData.sztQueue, printer->hostname); + wcscpy(portData.sztIPAddress, printer->hostname); + wcscpy(portData.sztHostAddress, printer->hostname); + + ok = XcvData(hXcv, L"AddPort", (PBYTE) &portData, sizeof(PORT_DATA_1), pOutputData, cbInputData, &cbOutputNeeded, &dwStatus); + err = translate_errno( ok, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + if (printer->driverInstalled) + { + PRINTER_INFO_2 pInfo; + + ZeroMemory(&pInfo, sizeof(pInfo)); + + pInfo.pPrinterName = printer->actualName.GetBuffer(); + pInfo.pServerName = NULL; + pInfo.pShareName = NULL; + pInfo.pPortName = printer->portName.GetBuffer(); + pInfo.pDriverName = printer->model.GetBuffer(); + pInfo.pComment = printer->model.GetBuffer(); + pInfo.pLocation = L""; + pInfo.pDevMode = NULL; + pInfo.pDevMode = NULL; + pInfo.pSepFile = L""; + pInfo.pPrintProcessor = L"winprint"; + pInfo.pDatatype = L"RAW"; + pInfo.pParameters = L""; + pInfo.pSecurityDescriptor = NULL; + pInfo.Attributes = PRINTER_ATTRIBUTE_QUEUED; + pInfo.Priority = 0; + pInfo.DefaultPriority = 0; + pInfo.StartTime = 0; + pInfo.UntilTime = 0; + + hPrinter = AddPrinter(NULL, 2, (LPBYTE) &pInfo); + err = translate_errno( hPrinter, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + } + else + { + DWORD dwResult; + HANDLE hThread; + unsigned threadID; + + + m_processFinished = false; + + // + // create the thread + // + hThread = (HANDLE) _beginthreadex_compat( NULL, 0, InstallPrinterThread, printer, 0, &threadID ); + err = translate_errno( hThread, (OSStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + // + // go modal + // + while (!m_processFinished) + { + MSG msg; + + GetMessage( &msg, m_hWnd, 0, 0 ); + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + // + // Wait until child process exits. + // + dwResult = WaitForSingleObject( hThread, INFINITE ); + err = translate_errno( dwResult == WAIT_OBJECT_0, errno_compat(), err = kUnknownErr ); + require_noerr( err, exit ); + } + + printer->installed = true; + + // + // if the user specified a default printer, set it + // + if (printer->deflt) + { + ok = SetDefaultPrinter(printer->actualName); + err = translate_errno( ok, errno_compat(), err = kUnknownErr ); + require_noerr( err, exit ); + } + +exit: + + if (hPrinter != NULL) + { + ClosePrinter(hPrinter); + } + + if (hXcv != NULL) + { + ClosePrinter(hXcv); + } + + if (pOutputData != NULL) + { + delete [] pOutputData; + } + + return err; +} + + +BEGIN_MESSAGE_MAP(CPrinterSetupWizardSheet, CPropertySheet) +ON_MESSAGE( WM_SERVICE_EVENT, OnServiceEvent ) +ON_MESSAGE( WM_PROCESS_EVENT, OnProcessEvent ) +ON_WM_SETCURSOR() +END_MESSAGE_MAP() + + +// ------------------------------------------------------ +// OnCommand +// +// Traps when the user hits Finish +// +BOOL CPrinterSetupWizardSheet::OnCommand(WPARAM wParam, LPARAM lParam) +{ + // + // Check if this is OK + // + if (wParam == ID_WIZFINISH) // If OK is hit... + { + OnOK(); + } + + return CPropertySheet::OnCommand(wParam, lParam); +} + + +// ------------------------------------------------------ +// OnInitDialog +// +// Initializes this Dialog object. We start the browse here, +// so that printers show up instantly when the user clicks +// the next button. +// +BOOL CPrinterSetupWizardSheet::OnInitDialog() +{ + OSStatus err; + + CPropertySheet::OnInitDialog(); + + // + // setup the DNS-SD browsing + // + err = DNSServiceBrowse( &m_pdlBrowser, 0, 0, kPDLDataStreamServiceType, NULL, OnBrowse, this ); + require_noerr( err, exit ); + + m_serviceRefList.push_back(m_pdlBrowser); + + err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(m_pdlBrowser), m_hWnd, WM_SERVICE_EVENT, FD_READ|FD_CLOSE); + require_noerr( err, exit ); + + LoadPrinterNames(); + +exit: + + if (err != kNoErr) + { + WizardException exc; + + exc.text.LoadString(IDS_NO_MDNSRESPONDER_SERVICE_TEXT); + exc.caption.LoadString(IDS_NO_MDNSRESPONDER_SERVICE_CAPTION); + + throw(exc); + } + + return TRUE; +} + + +// ------------------------------------------------------ +// OnSetCursor +// +// This is called when Windows wants to know what cursor +// to display. So we tell it. +// +BOOL +CPrinterSetupWizardSheet::OnSetCursor(CWnd * pWnd, UINT nHitTest, UINT message) +{ + DEBUG_UNUSED(pWnd); + DEBUG_UNUSED(nHitTest); + DEBUG_UNUSED(message); + + SetCursor(m_active); + return TRUE; +} + + +// ------------------------------------------------------ +// OnContextMenu +// +// This is not fully implemented yet. +// + +void +CPrinterSetupWizardSheet::OnContextMenu(CWnd * pWnd, CPoint pos) +{ + DEBUG_UNUSED(pWnd); + DEBUG_UNUSED(pos); + + CAbout dlg; + + dlg.DoModal(); +} + + +// ------------------------------------------------------ +// OnOK +// +// This is called when the user hits the "Finish" button +// +void +CPrinterSetupWizardSheet::OnOK() +{ + check ( m_selectedPrinter != NULL ); + + SetWizardButtons( PSWIZB_DISABLEDFINISH ); + + if ( InstallPrinter( m_selectedPrinter ) != kNoErr ) + { + CString caption; + CString message; + + caption.LoadString(IDS_INSTALL_ERROR_CAPTION); + message.LoadString(IDS_INSTALL_ERROR_MESSAGE); + + MessageBox(message, caption, MB_OK|MB_ICONEXCLAMATION); + } +} + + +// CPrinterSetupWizardSheet message handlers + +void CPrinterSetupWizardSheet::Init(void) +{ + AddPage(&m_pgFirst); + AddPage(&m_pgSecond); + AddPage(&m_pgThird); + AddPage(&m_pgFourth); + + m_psh.dwFlags &= (~PSH_HASHELP); + + m_psh.dwFlags |= PSH_WIZARD97|PSH_WATERMARK|PSH_HEADER; + m_psh.pszbmWatermark = MAKEINTRESOURCE(IDB_WATERMARK); + m_psh.pszbmHeader = MAKEINTRESOURCE(IDB_BANNER_ICON); + + m_psh.hInstance = AfxGetInstanceHandle(); + + SetWizardMode(); +} + + +LONG +CPrinterSetupWizardSheet::OnServiceEvent(WPARAM inWParam, LPARAM inLParam) +{ + if (WSAGETSELECTERROR(inLParam) && !(HIWORD(inLParam))) + { + dlog( kDebugLevelError, "OnServiceEvent: window error\n" ); + } + else + { + SOCKET sock = (SOCKET) inWParam; + + // iterate thru list + ServiceRefList::iterator begin = m_serviceRefList.begin(); + ServiceRefList::iterator end = m_serviceRefList.end(); + + while (begin != end) + { + DNSServiceRef ref = *begin++; + + check(ref != NULL); + + if ((SOCKET) DNSServiceRefSockFD(ref) == sock) + { + DNSServiceProcessResult(ref); + break; + } + } + } + + return ( 0 ); +} + + +LONG +CPrinterSetupWizardSheet::OnProcessEvent(WPARAM inWParam, LPARAM inLParam) +{ + DEBUG_UNUSED(inWParam); + DEBUG_UNUSED(inLParam); + + m_processFinished = true; + + return 0; +} + + +void DNSSD_API +CPrinterSetupWizardSheet::OnBrowse( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + const char * inType, + const char * inDomain, + void * inContext ) +{ + DEBUG_UNUSED(inRef); + + CPrinterSetupWizardSheet * self; + Printer * printer; + EventHandlerList::iterator it; + DWORD printerNameCount; + bool moreComing = (bool) (inFlags & kDNSServiceFlagsMoreComing); + + require_noerr( inErrorCode, exit ); + + self = reinterpret_cast ( inContext ); + require_quiet( self, exit ); + + printer = self->LookUp(inName); + + if (inFlags & kDNSServiceFlagsAdd) + { + OSStatus err; + + if (printer != NULL) + { + printer->refs++; + } + else + { + try + { + printer = new Printer; + } + catch (...) + { + printer = NULL; + } + + require_action( printer, exit, err = E_OUTOFMEMORY ); + + printer->window = self; + printer->ifi = inInterfaceIndex; + printer->name = inName; + err = UTF8StringToStringObject(inName, printer->displayName); + check_noerr( err ); + printer->actualName = printer->displayName; + + // + // Compare this name against printers that are already installed + // to avoid name clashes. Rename as necessary + // to come up with a unique name. + // + printerNameCount = 2; + + for (;;) + { + PrinterNameMap::iterator it; + + it = self->m_printerNames.find(printer->actualName); + + if (it != self->m_printerNames.end()) + { + printer->actualName.Format(L"%s (%d)", printer->displayName, printerNameCount); + } + else + { + break; + } + + printerNameCount++; + } + + printer->type = inType; + printer->domain = inDomain; + printer->installed = false; + printer->deflt = false; + printer->refs = 1; + + self->m_printerList.push_back( printer ); + + // + // now invoke event handlers for AddPrinter event + // + for (it = self->m_eventHandlerList.begin(); it != self->m_eventHandlerList.end(); it++) + { + EventHandler * handler = *it; + + handler->OnAddPrinter(printer, moreComing); + } + } + } + else + { + if ((printer != NULL) && (--printer->refs == 0)) + { + // + // now invoke event handlers for RemovePrinter event + // + for (it = self->m_eventHandlerList.begin(); it != self->m_eventHandlerList.end(); it++) + { + EventHandler * handler = *it; + + handler->OnRemovePrinter(printer, moreComing); + } + + self->m_printerList.remove(printer); + + // + // check to see if we've selected this printer + // + if (self->m_selectedPrinter == printer) + { + // + // this guy is being removed while we're resolving it...so let's + // stop the resolve + // + if (printer->serviceRef != NULL) + { + self->StopResolve(printer); + } + + self->m_selectedPrinter = NULL; + } + + delete printer; + } + } + +exit: + + return; +} + + +void DNSSD_API +CPrinterSetupWizardSheet::OnResolve( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inFullName, + const char * inHostName, + uint16_t inPort, + uint16_t inTXTSize, + const char * inTXT, + void * inContext ) +{ + DEBUG_UNUSED(inFullName); + DEBUG_UNUSED(inInterfaceIndex); + DEBUG_UNUSED(inFlags); + DEBUG_UNUSED(inRef); + + Printer * printer; + CPrinterSetupWizardSheet * self; + EventHandlerList::iterator it1; + EventHandlerList::iterator it2; + int idx; + OSStatus err; + + require_noerr( inErrorCode, exit ); + + printer = reinterpret_cast( inContext ); + require_quiet( printer, exit); + + self = printer->window; + require_quiet( self, exit ); + + err = self->StopResolve(printer); + require_noerr(err, exit); + + // + // hold on to the hostname... + // + err = UTF8StringToStringObject( inHostName, printer->hostname ); + require_noerr( err, exit ); + + // + // remove the trailing dot on hostname + // + idx = printer->hostname.ReverseFind('.'); + + if ((idx > 1) && ((printer->hostname.GetLength() - 1) == idx)) + { + printer->hostname.Delete(idx, 1); + } + + // + // hold on to the port + // + printer->portNumber = ntohs(inPort); + + // + // parse the text record. we create a stringlist of text record + // entries that can be interrogated later + // + while (inTXTSize) + { + char buf[256]; + + unsigned char num = *inTXT; + check( (int) num < inTXTSize ); + + memset(buf, 0, sizeof(buf)); + memcpy(buf, inTXT + 1, num); + + inTXTSize -= (num + 1); + inTXT += (num + 1); + + CString elem; + + err = UTF8StringToStringObject( buf, elem ); + require_noerr( err, exit ); + + int curPos = 0; + + CString key = elem.Tokenize(L"=", curPos); + CString val = elem.Tokenize(L"=", curPos); + + key.MakeLower(); + + if ((key == L"usb_mfg") || (key == L"usb_manufacturer")) + { + printer->usb_MFG = val; + } + else if ((key == L"usb_mdl") || (key == L"usb_model")) + { + printer->usb_MDL = val; + } + else if (key == L"description") + { + printer->description = val; + } + else if (key == L"product") + { + printer->product = val; + } + } + + // + // now invoke event handlers for Resolve event + // + it1 = self->m_eventHandlerList.begin(); + it2 = self->m_eventHandlerList.end(); + + while (it1 != it2) + { + EventHandler * handler = *it1++; + + handler->OnResolvePrinter(printer); + } + +exit: + + return; +} + + +OSStatus +CPrinterSetupWizardSheet::LoadPrinterNames() +{ + PBYTE buffer = NULL; + OSStatus err = 0; + + // + // rdar://problem/3701926 - Printer can't be installed twice + // + // First thing we want to do is make sure the printer isn't already installed. + // If the printer name is found, we'll try and rename it until we + // find a unique name + // + DWORD dwNeeded = 0, dwNumPrinters = 0; + + BOOL ok = EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &dwNeeded, &dwNumPrinters); + err = translate_errno( ok, errno_compat(), kUnknownErr ); + + if ((err == ERROR_INSUFFICIENT_BUFFER) && (dwNeeded > 0)) + { + try + { + buffer = new unsigned char[dwNeeded]; + } + catch (...) + { + buffer = NULL; + } + + require_action( buffer, exit, kNoMemoryErr ); + ok = EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, buffer, dwNeeded, &dwNeeded, &dwNumPrinters); + err = translate_errno( ok, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + for (DWORD index = 0; index < dwNumPrinters; index++) + { + PRINTER_INFO_4 * lppi4 = (PRINTER_INFO_4*) (buffer + index * sizeof(PRINTER_INFO_4)); + + m_printerNames[lppi4->pPrinterName] = lppi4->pPrinterName; + } + } + +exit: + + if (buffer != NULL) + { + delete [] buffer; + } + + return err; +} + + +OSStatus +CPrinterSetupWizardSheet::StartResolve(Printer * printer) +{ + OSStatus err; + + check( printer ); + + err = DNSServiceResolve( &printer->serviceRef, 0, 0, printer->name.c_str(), printer->type.c_str(), printer->domain.c_str(), (DNSServiceResolveReply) OnResolve, printer ); + require_noerr( err, exit); + + m_serviceRefList.push_back(printer->serviceRef); + + err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(printer->serviceRef), m_hWnd, WM_SERVICE_EVENT, FD_READ|FD_CLOSE); + require_noerr( err, exit ); + + // + // set the cursor to arrow+hourglass + // + m_active = m_wait; + SetCursor(m_active); + +exit: + + return err; +} + + +OSStatus +CPrinterSetupWizardSheet::StopResolve(Printer * printer) +{ + OSStatus err; + + check( printer ); + check( printer->serviceRef ); + + m_serviceRefList.remove( printer->serviceRef ); + + err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(printer->serviceRef), m_hWnd, 0, 0); + check(err == 0); + + DNSServiceRefDeallocate( printer->serviceRef ); + + printer->serviceRef = NULL; + + // + // set the cursor back to normal + // + m_active = m_arrow; + SetCursor(m_active); + + return kNoErr; +} + + +Printer* +CPrinterSetupWizardSheet::LookUp(const char * inName) +{ + PrinterList::iterator it1 = m_printerList.begin(); + PrinterList::iterator it2 = m_printerList.end(); + + while (it1 != it2) + { + Printer * printer = *it1++; + + if (printer->name == inName) + { + return printer; + } + } + + return NULL; +} + + +unsigned WINAPI +CPrinterSetupWizardSheet::InstallPrinterThread( LPVOID inParam ) +{ + check( inParam ); + + Printer * printer = (Printer*) inParam; + CString actualName; + CString command; + DWORD exitCode = 0; + DWORD dwResult; + OSStatus err; + STARTUPINFO si; + PROCESS_INFORMATION pi; + BOOL ok; + + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + ZeroMemory( &pi, sizeof(pi) ); + + // + // Escape '\', '@', '"' characters which seem to cause problems for printui + // + + actualName = printer->actualName; + + actualName.Replace(L"\\", L"\\\\"); + actualName.Replace(L"@", L"\\@"); + actualName.Replace(L"\"", L"\\\""); + + command.Format(L"rundll32.exe printui.dll,PrintUIEntry /if /b \"%s\" /f \"%s\" /r \"%s\" /m \"%s\"", (LPCTSTR) actualName, (LPCTSTR) printer->infFileName, (LPCTSTR) printer->portName, (LPCTSTR) printer->model); + + ok = CreateProcess(NULL, command.GetBuffer(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + err = translate_errno( ok, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + dwResult = WaitForSingleObject( pi.hProcess, INFINITE ); + translate_errno( dwResult == WAIT_OBJECT_0, errno_compat(), err = kUnknownErr ); + require_noerr( err, exit ); + + ok = GetExitCodeProcess( pi.hProcess, &exitCode ); + err = translate_errno( ok, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + // + // Close process and thread handles. + // + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread ); + +exit: + + // + // alert the main thread + // + printer->window->PostMessage( WM_PROCESS_EVENT, err, exitCode ); + + return 0; +} diff --git a/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.h b/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.h new file mode 100644 index 0000000..e1077aa --- /dev/null +++ b/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.h @@ -0,0 +1,207 @@ +/* + * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: PrinterSetupWizardSheet.h,v $ +Revision 1.4 2004/07/13 21:24:23 rpantos +Fix for . + +Revision 1.3 2004/06/28 00:51:47 shersche +Move call to EnumPrinters out of browse callback into standalone function + +Revision 1.2 2004/06/24 20:12:07 shersche +Clean up source code +Submitted by: herscher + +Revision 1.1 2004/06/18 04:36:57 rpantos +First checked in + + +*/ + +#pragma once + + +#include "firstpage.h" +#include "secondpage.h" +#include "thirdpage.h" +#include "fourthpage.h" +#include "UtilTypes.h" +#include "dns_sd.h" +#include +#include + +using namespace PrinterSetupWizard; + +// CPrinterSetupWizardSheet + +class CPrinterSetupWizardSheet : public CPropertySheet +{ + DECLARE_DYNAMIC(CPrinterSetupWizardSheet) + +public: + + struct WizardException + { + CString text; + CString caption; + }; + +public: + + CPrinterSetupWizardSheet(UINT nIDCaption, CWnd* pParentWnd = NULL, UINT iSelectPage = 0); + virtual ~CPrinterSetupWizardSheet(); + + int + InstallEventHandler(EventHandler * handler); + + int + RemoveEventHandler(EventHandler * handler); + + OSStatus + SetSelectedPrinter(Printer * printer); + + Printer* + GetSelectedPrinter(); + + OSStatus + LoadPrinterDriver(const CString & filename); + + HCURSOR + GetCursor(); + + // + // handles socket events for DNSService operations + // + virtual LONG + OnServiceEvent(WPARAM inWParam, LPARAM inLParam); + + // + // handles end of process event + // + virtual LONG + OnProcessEvent(WPARAM inWParam, LPARAM inLParam); + + virtual BOOL + OnCommand(WPARAM wParam, LPARAM lParam); + + virtual BOOL + OnInitDialog(); + + virtual BOOL + OnSetCursor(CWnd * pWnd, UINT nHitTest, UINT message); + + virtual void + OnContextMenu(CWnd * pWnd, CPoint pos); + + afx_msg void + OnOK(); + +protected: + DECLARE_MESSAGE_MAP() + CFirstPage m_pgFirst; + CSecondPage m_pgSecond; + CThirdPage m_pgThird; + CFourthPage m_pgFourth; + + static void DNSSD_API + OnBrowse( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + const char * inType, + const char * inDomain, + void * inContext ); + + static void DNSSD_API + OnResolve( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inFullName, + const char * inHostName, + uint16_t inPort, + uint16_t inTXTSize, + const char * inTXT, + void * inContext ); + + void Init(void); + +private: + + OSStatus + LoadPrinterNames(); + + OSStatus + StartResolve(Printer * printer); + + OSStatus + StopResolve(Printer * printer); + + Printer* + LookUp(const char * inName); + + OSStatus + InstallPrinter(Printer * printer); + + static unsigned WINAPI + InstallPrinterThread( LPVOID inParam ); + + + typedef std::list PrinterList; + typedef std::list EventHandlerList; + typedef std::list ServiceRefList; + typedef std::map PrinterNameMap; + + Printer * m_selectedPrinter; + + PrinterNameMap m_printerNames; + PrinterList m_printerList; + EventHandlerList m_eventHandlerList; + ServiceRefList m_serviceRefList; + + bool m_processFinished; + + HCURSOR m_active; + HCURSOR m_arrow; + HCURSOR m_wait; + + DNSServiceRef m_pdlBrowser; +}; + + +inline Printer* +CPrinterSetupWizardSheet::GetSelectedPrinter() +{ + return m_selectedPrinter; +} + + +inline HCURSOR +CPrinterSetupWizardSheet::GetCursor() +{ + return m_active; +} diff --git a/Clients/PrinterSetupWizard/ReadMe.txt b/Clients/PrinterSetupWizard/ReadMe.txt new file mode 100644 index 0000000..9b5eb41 --- /dev/null +++ b/Clients/PrinterSetupWizard/ReadMe.txt @@ -0,0 +1,94 @@ +================================================================================ + MICROSOFT FOUNDATION CLASS LIBRARY : Wiz97_3 Project Overview +=============================================================================== + +The application wizard has created this Wiz97_3 application for +you. This application not only demonstrates the basics of using the Microsoft +Foundation Classes but is also a starting point for writing your application. + +This file contains a summary of what you will find in each of the files that +make up your Wiz97_3 application. + +Wiz97_3.vcproj + This is the main project file for VC++ projects generated using an application wizard. + It contains information about the version of Visual C++ that generated the file, and + information about the platforms, configurations, and project features selected with the + application wizard. + +Wiz97_3.h + This is the main header file for the application. It includes other + project specific headers (including Resource.h) and declares the + CWiz97_3App application class. + +Wiz97_3.cpp + This is the main application source file that contains the application + class CWiz97_3App. + +Wiz97_3.rc + This is a listing of all of the Microsoft Windows resources that the + program uses. It includes the icons, bitmaps, and cursors that are stored + in the RES subdirectory. This file can be directly edited in Microsoft + Visual C++. Your project resources are in 1033. + +res\Wiz97_3.ico + This is an icon file, which is used as the application's icon. This + icon is included by the main resource file Wiz97_3.rc. + +res\Wiz97_3.rc2 + This file contains resources that are not edited by Microsoft + Visual C++. You should place all resources not editable by + the resource editor in this file. + +///////////////////////////////////////////////////////////////////////////// + +The application wizard creates one dialog class: +Wiz97_3Dlg.h, Wiz97_3Dlg.cpp - the dialog + These files contain your CWiz97_3Dlg class. This class defines + the behavior of your application's main dialog. The dialog's template is + in Wiz97_3.rc, which can be edited in Microsoft Visual C++. +///////////////////////////////////////////////////////////////////////////// + +Other Features: + +ActiveX Controls + The application includes support to use ActiveX controls. + +Printing and Print Preview support + The application wizard has generated code to handle the print, print setup, and print preview + commands by calling member functions in the CView class from the MFC library. +///////////////////////////////////////////////////////////////////////////// + +Other standard files: + +StdAfx.h, StdAfx.cpp + These files are used to build a precompiled header (PCH) file + named Wiz97_3.pch and a precompiled types file named StdAfx.obj. + +Resource.h + This is the standard header file, which defines new resource IDs. + Microsoft Visual C++ reads and updates this file. + +Wiz97_3.manifest + Application manifest files are used by Windows XP to describe an applications + dependency on specific versions of Side-by-Side assemblies. The loader uses this + information to load the appropriate assembly from the assembly cache or private + from the application. The Application manifest maybe included for redistribution + as an external .manifest file that is installed in the same folder as the application + executable or it may be included in the executable in the form of a resource. +///////////////////////////////////////////////////////////////////////////// + +Other notes: + +The application wizard uses "TODO:" to indicate parts of the source code you +should add to or customize. + +If your application uses MFC in a shared DLL, and your application is in a +language other than the operating system's current language, you will need +to copy the corresponding localized resources MFC70XXX.DLL from the Microsoft +Visual C++ CD-ROM under the Win\System directory to your computer's system or +system32 directory, and rename it to be MFCLOC.DLL. ("XXX" stands for the +language abbreviation. For example, MFC70DEU.DLL contains resources +translated to German.) If you don't do this, some of the UI elements of +your application will remain in the language of the operating system. + +///////////////////////////////////////////////////////////////////////////// diff --git a/Clients/PrinterSetupWizard/SecondPage.cpp b/Clients/PrinterSetupWizard/SecondPage.cpp new file mode 100644 index 0000000..abf4fa0 --- /dev/null +++ b/Clients/PrinterSetupWizard/SecondPage.cpp @@ -0,0 +1,347 @@ +/* + * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: SecondPage.cpp,v $ +Revision 1.3 2004/09/13 21:26:15 shersche + Use the moreComing flag to determine whether drawing should take place in OnAddPrinter and OnRemovePrinter callbacks +Bug #: 3796483 + +Revision 1.2 2004/06/26 03:19:57 shersche +clean up warning messages + +Submitted by: herscher + +Revision 1.1 2004/06/18 04:36:57 rpantos +First checked in + + +*/ + +#include "stdafx.h" +#include "PrinterSetupWizardApp.h" +#include "PrinterSetupWizardSheet.h" +#include "SecondPage.h" +#include "DebugServices.h" + +// local variable is initialize but not referenced +#pragma warning(disable:4189) + + +// CSecondPage dialog + +IMPLEMENT_DYNAMIC(CSecondPage, CPropertyPage) +CSecondPage::CSecondPage() + : CPropertyPage(CSecondPage::IDD) +{ + m_psp.dwFlags &= ~(PSP_HASHELP); + m_psp.dwFlags |= PSP_DEFAULT|PSP_USEHEADERTITLE|PSP_USEHEADERSUBTITLE; + + m_psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_BROWSE_TITLE); + m_psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_BROWSE_SUBTITLE); + + m_resolver = NULL; + m_emptyListItem = NULL; + m_initialized = false; + m_waiting = false; +} + +CSecondPage::~CSecondPage() +{ +} + + +void +CSecondPage::InitBrowseList() +{ + CPrinterSetupWizardSheet * psheet; + CString text; + + psheet = reinterpret_cast(GetParent()); + require_quiet( psheet, exit ); + + // + // load the no rendezvous printers message until something shows up in the browse list + // + text.LoadString(IDS_NO_RENDEZVOUS_PRINTERS); + + m_emptyListItem = m_browseList.InsertItem( text, 0, 0, NULL, TVI_FIRST ); + + // + // this will remove everything else in the list...we might be navigating + // back to this window, and the browse list might have changed since + // we last displayed it. + // + if ( m_emptyListItem ) + { + HTREEITEM item = m_browseList.GetNextVisibleItem( m_emptyListItem ); + + while ( item ) + { + m_browseList.DeleteItem( item ); + item = m_browseList.GetNextVisibleItem( m_emptyListItem ); + } + } + + // + // disable the next button until there's a printer to select + // + psheet->SetWizardButtons(PSWIZB_BACK); + + // + // disable the window until there's a printer to select + // + m_browseList.EnableWindow( FALSE ); + +exit: + + return; +} + + +void CSecondPage::DoDataExchange(CDataExchange* pDX) +{ + CPropertyPage::DoDataExchange(pDX); + DDX_Control(pDX, IDC_BROWSE_LIST, m_browseList); +} + + +afx_msg BOOL +CSecondPage::OnSetCursor(CWnd * pWnd, UINT nHitTest, UINT message) +{ + DEBUG_UNUSED(pWnd); + DEBUG_UNUSED(nHitTest); + DEBUG_UNUSED(message); + + CPrinterSetupWizardSheet * psheet; + + psheet = reinterpret_cast(GetParent()); + require_quiet( psheet, exit ); + + SetCursor(psheet->GetCursor()); + +exit: + + return TRUE; +} + + +BOOL +CSecondPage::OnSetActive() +{ + CString noPrinters; + CPrinterSetupWizardSheet * psheet; + + psheet = reinterpret_cast(GetParent()); + require_quiet( psheet, exit ); + + // + // initialize the browse list...this will remove everything currently + // in it, and add the no rendezvous printers item + // + InitBrowseList(); + + // + // this will invoke OnAddPrinter for all the printers that we have + // browsed + // + psheet->InstallEventHandler(this); + +exit: + + return CPropertyPage::OnSetActive(); +} + + +BOOL +CSecondPage::OnKillActive() +{ + CPrinterSetupWizardSheet * psheet; + + psheet = reinterpret_cast(GetParent()); + require_quiet( psheet, exit ); + + // + // we don't want our event handlers called when we don't have + // anywhere to put the data + // + psheet->RemoveEventHandler(this); + +exit: + + return CPropertyPage::OnKillActive(); +} + + +BEGIN_MESSAGE_MAP(CSecondPage, CPropertyPage) + ON_NOTIFY(TVN_SELCHANGED, IDC_BROWSE_LIST, OnTvnSelchangedBrowseList) + ON_WM_SETCURSOR() +END_MESSAGE_MAP() + + +// Printer::EventHandler implementation +void +CSecondPage::OnAddPrinter( + Printer * printer, + bool moreComing) +{ + check( IsWindow( m_hWnd ) ); + + m_browseList.SetRedraw(FALSE); + + printer->item = m_browseList.InsertItem(printer->displayName); + + m_browseList.SetItemData( printer->item, (DWORD_PTR) printer ); + + m_browseList.SortChildren(TVI_ROOT); + + // + // if the searching item is still in the list + // get rid of it + // + // note that order is important here. Insert the printer + // item before removing the placeholder so we always have + // an item in the list to avoid experiencing the bug + // in Microsoft's implementation of CTreeCtrl + // + if (m_emptyListItem != NULL) + { + m_browseList.DeleteItem(m_emptyListItem); + m_emptyListItem = NULL; + m_browseList.EnableWindow(TRUE); + } + + if (!moreComing) + { + m_browseList.SetRedraw(TRUE); + m_browseList.Invalidate(); + } +} + + +void +CSecondPage::OnRemovePrinter( + Printer * printer, + bool moreComing) +{ + check( IsWindow( m_hWnd ) ); + + m_browseList.SetRedraw(FALSE); + + // + // check to make sure if we're the only item in the control...i.e. + // the list size is 1. + // + if (m_browseList.GetCount() > 1) + { + // + // if we're not the only thing in the list, then + // simply remove it from the list + // + m_browseList.DeleteItem( printer->item ); + } + else + { + // + // if we're the only thing in the list, then redisplay + // it with the no rendezvous printers message + // + InitBrowseList(); + } + + if (!moreComing) + { + m_browseList.SetRedraw(TRUE); + m_browseList.Invalidate(); + } +} + + +void +CSecondPage::OnResolvePrinter( + Printer * printer) +{ + DEBUG_UNUSED(printer); + + check( IsWindow( m_hWnd ) ); + + CPrinterSetupWizardSheet * psheet = reinterpret_cast(GetParent()); + require_quiet( psheet, exit ); + + // + // setup the sheet to enable the next button if we've successfully + // resolved + // + psheet->SetWizardButtons( PSWIZB_BACK|PSWIZB_NEXT ); + +exit: + + return; +} + + +void CSecondPage::OnTvnSelchangedBrowseList(NMHDR *pNMHDR, LRESULT *pResult) +{ + LPNMTREEVIEW pNMTreeView = reinterpret_cast(pNMHDR); + CPrinterSetupWizardSheet * psheet; + int err = 0; + + HTREEITEM item = m_browseList.GetSelectedItem(); + require_quiet( item, exit ); + + psheet = reinterpret_cast(GetParent()); + require_action( psheet, exit, err = kUnknownErr ); + + Printer * printer; + + printer = reinterpret_cast(m_browseList.GetItemData( item ) ); + require_quiet( printer, exit ); + + // + // this call will trigger a resolve. When the resolve is complete, + // our OnResolve will be called. + // + err = psheet->SetSelectedPrinter(printer); + require_noerr( err, exit ); + + // + // setup the sheet to disable the next button until we've successfully + // resolved this printer + // + psheet->SetWizardButtons( PSWIZB_BACK ); + +exit: + + if (err != 0) + { + CString text; + CString caption; + + text.LoadString(IDS_ERROR_SELECTING_PRINTER_TEXT); + caption.LoadString(IDS_ERROR_SELECTING_PRINTER_CAPTION); + + MessageBox(text, caption, MB_OK|MB_ICONEXCLAMATION); + } + + *pResult = 0; +} diff --git a/Clients/PrinterSetupWizard/SecondPage.h b/Clients/PrinterSetupWizard/SecondPage.h new file mode 100644 index 0000000..45c55c9 --- /dev/null +++ b/Clients/PrinterSetupWizard/SecondPage.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: SecondPage.h,v $ +Revision 1.2 2004/09/13 21:23:42 shersche + Add moreComing argument to OnAddPrinter and OnRemovePrinter callbacks +Bug #: 3796483 + +Revision 1.1 2004/06/18 04:36:57 rpantos +First checked in + + +*/ + +#pragma once + +#include "PrinterSetupWizardSheet.h" +#include "CommonServices.h" +#include "UtilTypes.h" +#include "afxcmn.h" +#include "dns_sd.h" +#include "afxwin.h" +#include + +using namespace PrinterSetupWizard; + +// CSecondPage dialog + +class CSecondPage : public CPropertyPage, public EventHandler +{ + DECLARE_DYNAMIC(CSecondPage) + +public: + CSecondPage(); + virtual ~CSecondPage(); + +// Dialog Data + enum { IDD = IDD_SECOND_PAGE }; + + virtual void + OnAddPrinter( + Printer * printer, + bool moreComing); + + virtual void + OnRemovePrinter( + Printer * printer, + bool moreComing); + + virtual void + OnResolvePrinter( + Printer * printer); + +protected: + + void InitBrowseList(); + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + afx_msg BOOL OnSetCursor(CWnd * pWnd, UINT nHitTest, UINT message); + virtual BOOL OnSetActive(); + virtual BOOL OnKillActive(); + + DECLARE_MESSAGE_MAP() + +public: + + HTREEITEM m_emptyListItem; + bool m_selectOkay; + CTreeCtrl m_browseList; + DNSServiceRef m_resolver; + bool m_initialized; + bool m_waiting; + + afx_msg void OnTvnSelchangedBrowseList(NMHDR *pNMHDR, LRESULT *pResult); +}; diff --git a/Clients/PrinterSetupWizard/ThirdPage.cpp b/Clients/PrinterSetupWizard/ThirdPage.cpp new file mode 100644 index 0000000..6828f3c --- /dev/null +++ b/Clients/PrinterSetupWizard/ThirdPage.cpp @@ -0,0 +1,1224 @@ +/* + * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: ThirdPage.cpp,v $ +Revision 1.10 2004/10/11 22:55:34 shersche + Use the IP port number when deriving the printer port name. +Bug #: 3827624 + +Revision 1.9 2004/06/27 23:08:00 shersche +code cleanup, make sure EnumPrintDrivers returns non-zero value, ignore comments in inf files + +Revision 1.8 2004/06/27 08:06:45 shersche +Parse [Strings] section of inf file + +Revision 1.7 2004/06/26 04:00:05 shersche +fix warnings compiling in debug mode +Submitted by: herscher + +Revision 1.6 2004/06/26 03:19:57 shersche +clean up warning messages + +Submitted by: herscher + +Revision 1.5 2004/06/25 05:06:02 shersche +Trim whitespace from key/value pairs when parsing inf files +Submitted by: herscher + +Revision 1.4 2004/06/25 02:44:13 shersche +Tweaked code to handle Xerox Phaser printer identification +Submitted by: herscher + +Revision 1.3 2004/06/25 02:27:58 shersche +Do a CListCtrl::FindItem() before calling CListCtrl::SetItemState(). +Submitted by: herscher + +Revision 1.2 2004/06/23 18:09:23 shersche +Normalize tag names when parsing inf files. +Submitted by: herscher + +Revision 1.1 2004/06/18 04:36:58 rpantos +First checked in + + +*/ + +#include "stdafx.h" +#include "PrinterSetupWizardApp.h" +#include "PrinterSetupWizardSheet.h" +#include "ThirdPage.h" +#include "StdioFileEx.h" +#include +#include +#include + +// local variable is initialize but not referenced +#pragma warning(disable:4189) + + +// +// This is the printer description file that is shipped +// with Windows +// +#define kNTPrintFile L"inf\\ntprint.inf" + +// +// These are pre-defined names for Generic manufacturer and model +// +#define kGenericManufacturer L"Generic" +#define kGenericModel L"Generic / Text Only" + +// +// states for parsing ntprint.inf +// +enum PrinterParsingState +{ + Looking, + ParsingManufacturers, + ParsingModels, + ParsingStrings +}; + + +// CThirdPage dialog + +IMPLEMENT_DYNAMIC(CThirdPage, CPropertyPage) +CThirdPage::CThirdPage() + : CPropertyPage(CThirdPage::IDD), + m_initialized(false) +{ + m_psp.dwFlags &= ~(PSP_HASHELP); + m_psp.dwFlags |= PSP_DEFAULT|PSP_USEHEADERTITLE|PSP_USEHEADERSUBTITLE; + + m_psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_INSTALL_TITLE); + m_psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_INSTALL_SUBTITLE); +} + + +CThirdPage::~CThirdPage() +{ + // + // clean up all the printer manufacturers + // + while (m_manufacturers.size()) + { + Manufacturers::iterator iter = m_manufacturers.begin(); + + while (iter->second->models.size()) + { + Models::iterator it = iter->second->models.begin(); + + Model * model = *it; + + delete model; + + iter->second->models.erase(it); + } + + delete iter->second; + + m_manufacturers.erase(iter); + } +} + + +// ---------------------------------------------------- +// SelectMatch +// +// SelectMatch will do all the UI work associated with +// selected a manufacturer and model of printer. It also +// makes sure the printer object is update with the +// latest settings +// +// ---------------------------------------------------- +void +CThirdPage::SelectMatch(Printer * printer, Manufacturer * manufacturer, Model * model) +{ + LVFINDINFO info; + int nIndex; + + check( printer != NULL ); + check( manufacturer != NULL ); + check( model != NULL ); + + Manufacturers manufacturers; + manufacturers[manufacturer->name] = manufacturer; + + PopulateUI( manufacturers ); + + // + // select the manufacturer + // + info.flags = LVFI_STRING; + info.psz = manufacturer->name; + + nIndex = m_manufacturerListCtrl.FindItem(&info); + + if (nIndex != -1) + { + m_manufacturerListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED); + m_manufacturerListCtrl.EnsureVisible(nIndex, FALSE); + } + + // + // select the model + // + info.flags = LVFI_STRING; + info.psz = model->name; + + nIndex = m_modelListCtrl.FindItem(&info); + + if (nIndex != -1) + { + m_modelListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED); + m_modelListCtrl.EnsureVisible(nIndex, FALSE); + + m_modelListCtrl.SetFocus(); + } + + CopyPrinterSettings( printer, manufacturer, model ); +} + + +// -------------------------------------------------------- +// CopyPrinterSettings +// +// This function makes sure that the printer object has the +// latest settings from the manufacturer and model objects +// -------------------------------------------------------- + +void +CThirdPage::CopyPrinterSettings( Printer * printer, Manufacturer * manufacturer, Model * model ) +{ + printer->manufacturer = manufacturer->name; + printer->model = model->name; + printer->driverInstalled = model->driverInstalled; + printer->infFileName = model->infFileName; + printer->portName.Format(L"IP_%s.%d", static_cast(printer->hostname), printer->portNumber); +} + + +// ------------------------------------------------------ +// LoadPrintDriverDefsFromFile +// +// This function does all the heavy lifting in parsing inf +// files. It is called to parse both ntprint.inf, and driver +// files that might be shipped on a printer's installation +// disk +// +// The inf file is not totally parsed. I only want to determine +// the manufacturer and models that are involved. I leave it +// to printui.dll to actually copy the driver files to the +// right places. +// +// I was aiming to parse as little as I could so as not to +// duplicate the parsing code that is contained in Windows. There +// are no public APIs for parsing inf files. +// +// That part of the inf file that we're interested in has a fairly +// easy format. Tags are strings that are enclosed in brackets. +// We are only interested in [MANUFACTURERS] and models. +// +// The only potentially opaque thing about this function is the +// checkForDuplicateModels flag. The problem here is that ntprint.inf +// doesn't contain duplicate models, and it has hundreds of models +// listed. You wouldn't check for duplicates there. But oftentimes, +// loading different windows print driver files contain multiple +// entries for the same printer. You don't want the UI to display +// the same printer multiple times, so in that case, you would ask +// this function to check for multiple models. + +OSStatus +CThirdPage::LoadPrintDriverDefsFromFile(Manufacturers & manufacturers, const CString & filename, bool checkForDuplicateModels ) +{ + PrinterParsingState state = Looking; + Manufacturers::iterator iter = manufacturers.end(); + CStdioFileEx file; + CFileException feError; + CString s; + OSStatus err; + BOOL ok; + + typedef std::map StringMap; + + StringMap strings; + + ok = file.Open( filename, CFile::modeRead|CFile::typeText, &feError); + err = translate_errno( ok, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + check ( state == Looking ); + check ( iter == manufacturers.end() ); + + // + // first, parse the file looking for string sections + // + while (file.ReadString(s)) + { + // + // check for comment + // + if (s.Find(';') == 0) + { + continue; + } + + // + // check for tag + // + else if (s.Find('[') == 0) + { + // + // handle any capitalization issues here + // + CString tag = s; + + tag.MakeLower(); + + if (tag == L"[strings]") + { + state = ParsingStrings; + } + else + { + state = Looking; + } + } + else + { + switch (state) + { + case ParsingStrings: + { + int curPos = 0; + + if (s.GetLength() > 0) + { + CString key = s.Tokenize(L"=",curPos); + CString val = s.Tokenize(L"=",curPos); + + // + // get rid of all delimiters + // + val.Remove('"'); + + // + // and store it + // + strings[key] = val; + } + } + break; + } + } + } + + file.Close(); + + ok = file.Open( filename, CFile::modeRead|CFile::typeText, &feError); + err = translate_errno( ok, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + state = Looking; + + check ( iter == manufacturers.end() ); + + while (file.ReadString(s)) + { + // + // check for comment + // + if (s.Find(';') == 0) + { + continue; + } + + // + // check for tag + // + else if (s.Find('[') == 0) + { + // + // handle any capitalization issues here + // + CString tag = s; + + tag.MakeLower(); + + if (tag == L"[manufacturer]") + { + state = ParsingManufacturers; + } + else + { + // remove the leading and trailing delimiters + // + s.Remove('['); + s.Remove(']'); + + // check to see if this is a printer entry + // + iter = manufacturers.find(s); + + if (iter != manufacturers.end()) + { + state = ParsingModels; + } + else + { + state = Looking; + } + } + } + // + // only look at this if the line isn't empty, or + // if it isn't a comment + // + else if ((s.GetLength() > 0) && (s.Find(';') != 0)) + { + switch (state) + { + // + // if we're parsing manufacturers, then we will parse + // an entry of the form key=val, where key is a delimited + // string specifying a manufacturer name, and val is + // a tag that is used later in the file. the key is + // delimited by either '"' (quotes) or '%' (percent sign). + // + // the tag is used further down the file when models are + // declared. this allows multiple manufacturers to exist + // in a single inf file. + // + case ParsingManufacturers: + { + Manufacturer * manufacturer; + int curPos = 0; + + CString key = s.Tokenize(L"=",curPos); + CString val = s.Tokenize(L"=",curPos); + + try + { + manufacturer = new Manufacturer; + } + catch (...) + { + manufacturer = NULL; + } + + require_action( manufacturer, exit, err = kNoMemoryErr ); + + // + // if it's a variable, look it up + // + if (key.Find('%') == 0) + { + StringMap::iterator it; + + key.Remove('%'); + + it = strings.find(key); + + if (it != strings.end()) + { + key = it->second; + } + } + else + { + key.Remove('"'); + } + + val.TrimLeft(); + val.TrimRight(); + + // + // why is there no consistency in inf files? + // + if (val.GetLength() == 0) + { + val = key; + } + + // + // fix the manufacturer name if necessary + // + curPos = 0; + val = val.Tokenize(L",", curPos); + + manufacturer->name = NormalizeManufacturerName( key ); + manufacturer->tag = val; + + manufacturers[val] = manufacturer; + } + break; + + case ParsingModels: + { + check( iter != manufacturers.end() ); + + Model * model; + int curPos = 0; + + CString name = s.Tokenize(L"=",curPos); + CString description = s.Tokenize(L"=",curPos); + + name.Remove('"'); + name.Trim(); + description.Trim(); + + // + // If true, see if we've seen this guy before + // + if (checkForDuplicateModels == true) + { + if ( MatchModel( iter->second, name ) != NULL ) + { + continue; + } + } + + try + { + model = new Model; + } + catch (...) + { + model = NULL; + } + + require_action( model, exit, err = kNoMemoryErr ); + + model->infFileName = filename; + model->name = name; + model->driverInstalled = false; + + iter->second->models.push_back(model); + } + break; + + default: + { + // pay no attention if we are in any other state + } + break; + } + } + } + +exit: + + file.Close(); + + return (err); +} + + +// ------------------------------------------------------- +// LoadPrintDriverDefs +// +// This function is responsible for loading the print driver +// definitions of all print drivers that have been installed +// on this machine. +// ------------------------------------------------------- +OSStatus +CThirdPage::LoadPrintDriverDefs( Manufacturers & manufacturers ) +{ + BYTE * buffer = NULL; + DWORD bytesReceived = 0; + DWORD numPrinters = 0; + OSStatus err = 0; + BOOL ok; + + // + // like a lot of win32 calls, we call this first to get the + // size of the buffer we need. + // + EnumPrinterDrivers(NULL, L"all", 6, NULL, 0, &bytesReceived, &numPrinters); + + if (bytesReceived > 0) + { + try + { + buffer = new BYTE[bytesReceived]; + } + catch (...) + { + buffer = NULL; + } + + require_action( buffer, exit, err = kNoMemoryErr ); + + // + // this call gets the real info + // + ok = EnumPrinterDrivers(NULL, L"all", 6, buffer, bytesReceived, &bytesReceived, &numPrinters); + err = translate_errno( ok, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + DRIVER_INFO_6 * info = (DRIVER_INFO_6*) buffer; + + for (DWORD i = 0; i < numPrinters; i++) + { + Manufacturer * manufacturer; + Model * model; + CString name; + + // + // skip over anything that doesn't have a manufacturer field. This + // fixes a bug that I noticed that occurred after I installed + // ProComm. This program add a print driver with no manufacturer + // that screwed up this wizard. + // + if (info[i].pszMfgName == NULL) + { + continue; + } + + // + // look for manufacturer + // + Manufacturers::iterator iter; + + // + // save the name + // + name = NormalizeManufacturerName( info[i].pszMfgName ); + + iter = manufacturers.find(name); + + if (iter != manufacturers.end()) + { + manufacturer = iter->second; + } + else + { + try + { + manufacturer = new Manufacturer; + } + catch (...) + { + manufacturer = NULL; + } + + require_action( manufacturer, exit, err = kNoMemoryErr ); + + manufacturer->name = name; + + manufacturers[name] = manufacturer; + } + + // + // now look to see if we have already seen this guy. this could + // happen if we have already installed printers that are described + // in ntprint.inf. the extant drivers will show up in EnumPrinterDrivers + // but we have already loaded their info + // + // + if ( MatchModel( manufacturer, ConvertToModelName( info[i].pName ) ) == NULL ) + { + try + { + model = new Model; + } + catch (...) + { + model = NULL; + } + + require_action( model, exit, err = kNoMemoryErr ); + + model->name = info[i].pName; + model->driverInstalled = true; + + manufacturer->models.push_back(model); + } + } + } + +exit: + + if (buffer != NULL) + { + delete [] buffer; + } + + return err; +} + + +// ------------------------------------------------------ +// ConvertToManufacturerName +// +// This function is responsible for tweaking the +// name so that subsequent string operations won't fail because +// of capitalizations/different names for the same manufacturer +// (i.e. Hewlett-Packard/HP/Hewlett Packard) +// +CString +CThirdPage::ConvertToManufacturerName( const CString & name ) +{ + // + // first we're going to convert all the characters to lower + // case + // + CString lower = name; + lower.MakeLower(); + + // + // now we're going to check to see if the string says "hewlett-packard", + // because sometimes they refer to themselves as "hewlett-packard", and + // sometimes they refer to themselves as "hp". + // + if ( lower == L"hewlett-packard") + { + lower = "hp"; + } + + // + // tweak for Xerox Phaser, which doesn't announce itself + // as a xerox + // + else if ( lower.Find( L"phaser", 0 ) != -1 ) + { + lower = "xerox"; + } + + return lower; +} + + +// ------------------------------------------------------ +// ConvertToModelName +// +// This function is responsible for ensuring that subsequent +// string operations don't fail because of differing capitalization +// schemes and the like +// ------------------------------------------------------ + +CString +CThirdPage::ConvertToModelName( const CString & name ) +{ + // + // convert it to lowercase + // + CString lower = name; + lower.MakeLower(); + + return lower; +} + + +// ------------------------------------------------------ +// NormalizeManufacturerName +// +// This function is responsible for tweaking the manufacturer +// name so that there are no aliases for vendors +// +CString +CThirdPage::NormalizeManufacturerName( const CString & name ) +{ + CString normalized = name; + + // + // now we're going to check to see if the string says "hewlett-packard", + // because sometimes they refer to themselves as "hewlett-packard", and + // sometimes they refer to themselves as "hp". + // + if ( normalized == L"Hewlett-Packard") + { + normalized = "HP"; + } + + return normalized; +} + + +// ------------------------------------------------------- +// MatchPrinter +// +// This function is responsible for matching a printer +// to a list of manufacturers and models. It calls +// MatchManufacturer and MatchModel in turn. +// + +OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * printer) +{ + CString normalizedProductName; + Manufacturer * manufacturer = NULL; + Model * model = NULL; + bool found = false; + CString text; + OSStatus err = kNoErr; + + // + // first look to see if we have a usb_MFG descriptor + // + if (printer->usb_MFG.GetLength() > 0) + { + manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( printer->usb_MFG ) ); + } + + if ( manufacturer == NULL ) + { + printer->product.Remove('('); + printer->product.Remove(')'); + + manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( printer->product ) ); + } + + // + // if we found the manufacturer, then start looking for the model + // + if ( manufacturer != NULL ) + { + if (printer->usb_MDL.GetLength() > 0) + { + model = MatchModel ( manufacturer, ConvertToModelName ( printer->usb_MDL ) ); + } + + if ( model == NULL ) + { + printer->product.Remove('('); + printer->product.Remove(')'); + + model = MatchModel ( manufacturer, ConvertToModelName ( printer->product ) ); + } + + if ( model != NULL ) + { + SelectMatch(printer, manufacturer, model); + found = true; + } + } + + // + // display a message to the user based on whether we could match + // this printer + // + if (found) + { + text.LoadString(IDS_PRINTER_MATCH_GOOD); + } + else + { + text.LoadString(IDS_PRINTER_MATCH_BAD); + + // + // if there was any crud in this list from before, get rid of it now + // + m_modelListCtrl.DeleteAllItems(); + + // + // select the manufacturer if we found one + // + if (manufacturer != NULL) + { + LVFINDINFO info; + int nIndex; + + // + // select the manufacturer + // + info.flags = LVFI_STRING; + info.psz = manufacturer->name; + + nIndex = m_manufacturerListCtrl.FindItem(&info); + + if (nIndex != -1) + { + m_manufacturerListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED); + m_manufacturerListCtrl.EnsureVisible(nIndex, FALSE); + } + } + } + + m_printerSelectionText.SetWindowText(text); + + return err; +} + + +// ------------------------------------------------------ +// MatchManufacturer +// +// This function is responsible for finding a manufacturer +// object from a string name. It does a CString::Find, which +// is like strstr, so it doesn't have to do an exact match +// +// If it can't find a match, NULL is returned +// ------------------------------------------------------ + +Manufacturer* +CThirdPage::MatchManufacturer( Manufacturers & manufacturers, const CString & name) +{ + Manufacturers::iterator iter; + + for (iter = manufacturers.begin(); iter != manufacturers.end(); iter++) + { + // + // we're going to convert all the manufacturer names to lower case, + // so we match the name passed in. + // + CString lower = iter->second->name; + lower.MakeLower(); + + // + // now try and find the lowered string in the name passed in. + // + if (name.Find(lower) == 0) + { + return iter->second; + } + } + + return NULL; +} + + +// ------------------------------------------------------- +// MatchModel +// +// This function is responsible for matching a model from +// a name. It does a CString::Find(), which works like strstr, +// so it doesn't rely on doing an exact string match. +// + +Model* +CThirdPage::MatchModel(Manufacturer * manufacturer, const CString & name) +{ + Models::iterator iter; + + iter = manufacturer->models.begin(); + + for (iter = manufacturer->models.begin(); iter != manufacturer->models.end(); iter++) + { + Model * model = *iter; + + // + // convert the model name to lower case + // + CString lowered = model->name; + lowered.MakeLower(); + + if (lowered.Find( name ) != -1) + { + return model; + } + } + + return NULL; +} + + + +// ----------------------------------------------------------- +// OnInitPage +// +// This function is responsible for doing initialization that +// only occurs once during a run of the wizard +// + +OSStatus CThirdPage::OnInitPage() +{ + static const int bufferSize = 32768; + TCHAR windowsDirectory[bufferSize]; + CString header; + CString ntPrint; + OSStatus err; + BOOL ok; + + + // + // The CTreeCtrl widget automatically sends a selection changed + // message which initially we want to ignore, because the user + // hasn't selected anything + // + // this flag gets reset in the message handler. Every subsequent + // message gets handled. + // + + // + // we have to make sure that we only do this once. Typically, + // we would do this in something like OnInitDialog, but we don't + // have this in Wizards, because the window is a PropertySheet. + // We're considered fully initialized when we receive the first + // selection notice + // + header.LoadString(IDS_MANUFACTURER_HEADING); + m_manufacturerListCtrl.InsertColumn(0, header, LVCFMT_LEFT, 138); + m_manufacturerSelected = NULL; + + header.LoadString(IDS_MODEL_HEADING); + m_modelListCtrl.InsertColumn(0, header, LVCFMT_LEFT, 247); + m_modelSelected = NULL; + + // + // load printers from ntprint.inf + // + ok = GetWindowsDirectory( windowsDirectory, bufferSize ); + err = translate_errno( ok, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + ntPrint.Format(L"%s\\%s", windowsDirectory, kNTPrintFile); + err = LoadPrintDriverDefsFromFile( m_manufacturers, ntPrint, false ); + require_noerr(err, exit); + + // + // load printer drivers that have been installed on this machine + // + err = LoadPrintDriverDefs( m_manufacturers ); + require_noerr(err, exit); + +exit: + + return (err); +} + + +void CThirdPage::DoDataExchange(CDataExchange* pDX) +{ + CPropertyPage::DoDataExchange(pDX); + DDX_Control(pDX, IDC_PRINTER_MANUFACTURER, m_manufacturerListCtrl); + DDX_Control(pDX, IDC_PRINTER_MODEL, m_modelListCtrl); + DDX_Control(pDX, IDC_PRINTER_NAME, m_printerName); + DDX_Control(pDX, IDC_DEFAULT_PRINTER, m_defaultPrinterCtrl); + DDX_Control(pDX, IDC_PRINTER_SELECTION_TEXT, m_printerSelectionText); +} + + +// ---------------------------------------------------------- +// OnSetActive +// +// This function is called by MFC after the window has been +// activated. +// + +BOOL +CThirdPage::OnSetActive() +{ + CPrinterSetupWizardSheet * psheet; + Printer * printer; + + psheet = reinterpret_cast(GetParent()); + require_quiet( psheet, exit ); + + if ((m_manufacturerListCtrl.GetFirstSelectedItemPosition() != NULL) && + (m_modelListCtrl.GetFirstSelectedItemPosition() != NULL)) + { + psheet->SetWizardButtons( PSWIZB_BACK|PSWIZB_NEXT ); + } + else + { + psheet->SetWizardButtons( PSWIZB_BACK ); + } + + printer = psheet->GetSelectedPrinter(); + require_quiet( printer, exit ); + + // + // call OnInitPage once + // + if (!m_initialized) + { + OnInitPage(); + m_initialized = true; + } + + // + // update the UI with the printer name + // + m_printerName.SetWindowText(printer->displayName); + + // + // populate the list controls with the manufacturers and models + // from ntprint.inf + // + PopulateUI( m_manufacturers ); + + // + // and try and match the printer + // + MatchPrinter( m_manufacturers, printer ); + +exit: + + return CPropertyPage::OnSetActive(); +} + + +// ------------------------------------------------------- +// PopulateUI +// +// This function is called to populate the list of manufacturers +// +OSStatus +CThirdPage::PopulateUI(Manufacturers & manufacturers) +{ + Manufacturers::iterator iter; + + m_manufacturerListCtrl.DeleteAllItems(); + + for (iter = manufacturers.begin(); iter != manufacturers.end(); iter++) + { + int nIndex; + + Manufacturer * manufacturer = iter->second; + + nIndex = m_manufacturerListCtrl.InsertItem(0, manufacturer->name); + + m_manufacturerListCtrl.SetItemData(nIndex, (DWORD_PTR) manufacturer); + } + + return 0; +} + + +BEGIN_MESSAGE_MAP(CThirdPage, CPropertyPage) + ON_NOTIFY(LVN_ITEMCHANGED, IDC_PRINTER_MANUFACTURER, OnLvnItemchangedManufacturer) + ON_NOTIFY(LVN_ITEMCHANGED, IDC_PRINTER_MODEL, OnLvnItemchangedPrinterModel) + ON_BN_CLICKED(IDC_DEFAULT_PRINTER, OnBnClickedDefaultPrinter) + ON_BN_CLICKED(IDC_HAVE_DISK, OnBnClickedHaveDisk) +END_MESSAGE_MAP() + + +// CThirdPage message handlers +void CThirdPage::OnLvnItemchangedManufacturer(NMHDR *pNMHDR, LRESULT *pResult) +{ + LPNMLISTVIEW pNMLV = reinterpret_cast(pNMHDR); + + POSITION p = m_manufacturerListCtrl.GetFirstSelectedItemPosition(); + int nSelected = m_manufacturerListCtrl.GetNextSelectedItem(p); + + if (nSelected != -1) + { + m_manufacturerSelected = (Manufacturer*) m_manufacturerListCtrl.GetItemData(nSelected); + + m_modelListCtrl.SetRedraw(FALSE); + + m_modelListCtrl.DeleteAllItems(); + m_modelSelected = NULL; + + Models::iterator iter; + + for (iter = m_manufacturerSelected->models.begin(); iter != m_manufacturerSelected->models.end(); iter++) + { + Model * model = *iter; + + int nItem = m_modelListCtrl.InsertItem(0, model->name); + + m_modelListCtrl.SetItemData(nItem, (DWORD_PTR) model); + } + + m_modelListCtrl.SetRedraw(TRUE); + } + + *pResult = 0; +} + +void CThirdPage::OnLvnItemchangedPrinterModel(NMHDR *pNMHDR, LRESULT *pResult) +{ + LPNMLISTVIEW pNMLV = reinterpret_cast(pNMHDR); + + CPrinterSetupWizardSheet * psheet; + Printer * printer; + + psheet = reinterpret_cast(GetParent()); + require_quiet( psheet, exit ); + + printer = psheet->GetSelectedPrinter(); + require_quiet( printer, exit ); + + check ( m_manufacturerSelected ); + + POSITION p = m_modelListCtrl.GetFirstSelectedItemPosition(); + int nSelected = m_modelListCtrl.GetNextSelectedItem(p); + + if (nSelected != -1) + { + m_modelSelected = (Model*) m_modelListCtrl.GetItemData(nSelected); + + CopyPrinterSettings( printer, m_manufacturerSelected, m_modelSelected ); + + psheet->SetWizardButtons(PSWIZB_BACK|PSWIZB_NEXT); + } + else + { + psheet->SetWizardButtons(PSWIZB_BACK); + } + +exit: + + *pResult = 0; +} + + +void CThirdPage::OnBnClickedDefaultPrinter() +{ + CPrinterSetupWizardSheet * psheet; + Printer * printer; + + psheet = reinterpret_cast(GetParent()); + require_quiet( psheet, exit ); + + printer = psheet->GetSelectedPrinter(); + require_quiet( printer, exit ); + + printer->deflt = m_defaultPrinterCtrl.GetState() ? true : false; + +exit: + + return; +} + +void CThirdPage::OnBnClickedHaveDisk() +{ + CPrinterSetupWizardSheet * psheet; + Printer * printer; + + CFileDialog dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY|OFN_FILEMUSTEXIST, L"Setup Information (*.inf)|*.inf||", this); + + psheet = reinterpret_cast(GetParent()); + require_quiet( psheet, exit ); + + printer = psheet->GetSelectedPrinter(); + require_quiet( printer, exit ); + + if ( dlg.DoModal() == IDOK ) + { + Manufacturers manufacturers; + CString filename = dlg.GetPathName(); + + LoadPrintDriverDefsFromFile( manufacturers, filename, true ); + + PopulateUI( manufacturers ); + + MatchPrinter( manufacturers, printer ); + } + +exit: + + return; +} diff --git a/Clients/PrinterSetupWizard/ThirdPage.h b/Clients/PrinterSetupWizard/ThirdPage.h new file mode 100644 index 0000000..dfdb69d --- /dev/null +++ b/Clients/PrinterSetupWizard/ThirdPage.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: ThirdPage.h,v $ +Revision 1.1 2004/06/18 04:36:58 rpantos +First checked in + + +*/ + +#pragma once +#include "afxcmn.h" +#include "UtilTypes.h" +#include +#include +#include +#include +#include "afxwin.h" + + +// CThirdPage dialog + +class CThirdPage : public CPropertyPage +{ + DECLARE_DYNAMIC(CThirdPage) + +public: + CThirdPage(); + virtual ~CThirdPage(); + +// Dialog Data + enum { IDD = IDD_THIRD_PAGE }; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual BOOL OnSetActive(); + + DECLARE_MESSAGE_MAP() + +private: + + typedef std::map Manufacturers; + + // + // LoadPrintDriverDefsFromFile + // + // Parses INF file and populates manufacturers + // + OSStatus LoadPrintDriverDefsFromFile(Manufacturers & manufacturers, const CString & filename, bool checkForDuplicateModels ); + + // + // LoadPrintDriverDefs + // + // Loads extant print driver definitions + // + OSStatus LoadPrintDriverDefs(Manufacturers & manufacturers); + + // + // PopulateUI + // + // Load print driver defs into UI for browsing/selection + // + OSStatus PopulateUI(Manufacturers & manufacturers); + + // + // MatchPrinter + // + // Tries to match printer based on manufacturer and model + // + OSStatus MatchPrinter(Manufacturers & manufacturers, Printer * printer); + + // + // OnInitPage + // + // Called first time page is activated. + OSStatus OnInitPage(); + + // + // these functions will tweak the names so that everything is + // consistent + // + CString ConvertToManufacturerName( const CString & name ); + CString ConvertToModelName( const CString & name ); + CString NormalizeManufacturerName( const CString & name ); + + Manufacturer * MatchManufacturer( Manufacturers & manufacturer, const CString & name ); + Model * MatchModel( Manufacturer * manufacturer, const CString & name ); + void SelectMatch(Printer * printer, Manufacturer * manufacturer, Model * model); + void CopyPrinterSettings(Printer * printer, Manufacturer * manufacturer, Model * model); + + Manufacturers m_manufacturers; + + CListCtrl m_manufacturerListCtrl; + Manufacturer * m_manufacturerSelected; + + CListCtrl m_modelListCtrl; + Model * m_modelSelected; + + bool m_initialized; + +public: + + afx_msg void OnLvnItemchangedManufacturer(NMHDR *pNMHDR, LRESULT *pResult); + CStatic m_printerName; + afx_msg void OnLvnItemchangedPrinterModel(NMHDR *pNMHDR, LRESULT *pResult); + afx_msg void OnBnClickedDefaultPrinter(); +private: + CButton m_defaultPrinterCtrl; +public: + CStatic m_printerSelectionText; + afx_msg void OnBnClickedHaveDisk(); +}; diff --git a/Clients/PrinterSetupWizard/UtilTypes.h b/Clients/PrinterSetupWizard/UtilTypes.h new file mode 100644 index 0000000..f702ebc --- /dev/null +++ b/Clients/PrinterSetupWizard/UtilTypes.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: UtilTypes.h,v $ +Revision 1.4 2004/09/13 21:22:44 shersche + Add moreComing argument to OnAddPrinter and OnRemovePrinter callbacks +Bug #: 3796483 + +Revision 1.3 2004/06/26 23:27:12 shersche +support for installing multiple printers of the same name + +Revision 1.2 2004/06/25 02:25:59 shersche +Remove item field from manufacturer and model structures +Submitted by: herscher + +Revision 1.1 2004/06/18 04:36:58 rpantos +First checked in + + +*/ + +#pragma once + +#include +#include + +class CPrinterSetupWizardSheet; + +namespace PrinterSetupWizard +{ + struct Printer; + struct Manufacturer; + struct Model; + + typedef std::list TextRecord; + typedef std::list Models; + + struct Printer + { + DNSServiceRef serviceRef; + CPrinterSetupWizardSheet * window; + HTREEITEM item; + DWORD refs; + + // + // these are from the browse reply + // + uint32_t ifi; + std::string name; + CString displayName; + CString actualName; + std::string type; + std::string domain; + + // + // these are from the resolve + // + CString hostname; + unsigned short portNumber; + CString usb_MFG; + CString usb_MDL; + CString description; + CString product; + + // + // these are derived from the printer matching code + // + // if driverInstalled is false, then infFileName should + // have an absolute path to the printers inf file. this + // is used to install the printer from printui.dll + // + // if driverInstalled is true, then model is the name + // of the driver to use in AddPrinter + // + bool driverInstalled; + CString infFileName; + CString manufacturer; + CString model; + CString portName; + bool deflt; + + // + // state + // + bool installed; + }; + + + struct Manufacturer + { + CString name; + CString tag; + Models models; + }; + + + struct Model + { + bool driverInstalled; + CString infFileName; + CString name; + }; + + + class EventHandler + { + public: + + virtual void + OnAddPrinter( + Printer * printer, + bool moreComing) = 0; + + virtual void + OnRemovePrinter( + Printer * printer, + bool moreComing) = 0; + + virtual void + OnResolvePrinter( + Printer * printer) = 0; + }; +} diff --git a/Clients/PrinterSetupWizard/res/Info.ico b/Clients/PrinterSetupWizard/res/Info.ico new file mode 100644 index 0000000..1a53218 Binary files /dev/null and b/Clients/PrinterSetupWizard/res/Info.ico differ diff --git a/Clients/PrinterSetupWizard/res/NetworkPrinter.ico b/Clients/PrinterSetupWizard/res/NetworkPrinter.ico new file mode 100644 index 0000000..22130b3 Binary files /dev/null and b/Clients/PrinterSetupWizard/res/NetworkPrinter.ico differ diff --git a/Clients/PrinterSetupWizard/res/Print.ico b/Clients/PrinterSetupWizard/res/Print.ico new file mode 100644 index 0000000..22130b3 Binary files /dev/null and b/Clients/PrinterSetupWizard/res/Print.ico differ diff --git a/Clients/PrinterSetupWizard/res/PrinterSetupWizard.ico b/Clients/PrinterSetupWizard/res/PrinterSetupWizard.ico new file mode 100644 index 0000000..22130b3 Binary files /dev/null and b/Clients/PrinterSetupWizard/res/PrinterSetupWizard.ico differ diff --git a/Clients/PrinterSetupWizard/res/PrinterSetupWizard.manifest b/Clients/PrinterSetupWizard/res/PrinterSetupWizard.manifest new file mode 100644 index 0000000..90a067e --- /dev/null +++ b/Clients/PrinterSetupWizard/res/PrinterSetupWizard.manifest @@ -0,0 +1,22 @@ + + + +Your app description here + + + + + + diff --git a/Clients/PrinterSetupWizard/res/PrinterSetupWizard.rc2 b/Clients/PrinterSetupWizard/res/PrinterSetupWizard.rc2 new file mode 100644 index 0000000..27e049a --- /dev/null +++ b/Clients/PrinterSetupWizard/res/PrinterSetupWizard.rc2 @@ -0,0 +1,13 @@ +// +// Wiz97_3.RC2 - resources Microsoft Visual C++ does not edit directly +// + +#ifdef APSTUDIO_INVOKED +#error this file is not editable by Microsoft Visual C++ +#endif //APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// Add manually edited resources here... + +///////////////////////////////////////////////////////////////////////////// diff --git a/Clients/PrinterSetupWizard/res/Thumbs.db b/Clients/PrinterSetupWizard/res/Thumbs.db new file mode 100644 index 0000000..f9f6330 Binary files /dev/null and b/Clients/PrinterSetupWizard/res/Thumbs.db differ diff --git a/Clients/PrinterSetupWizard/res/about.bmp b/Clients/PrinterSetupWizard/res/about.bmp new file mode 100644 index 0000000..83e0bb0 Binary files /dev/null and b/Clients/PrinterSetupWizard/res/about.bmp differ diff --git a/Clients/PrinterSetupWizard/res/banner_icon.bmp b/Clients/PrinterSetupWizard/res/banner_icon.bmp new file mode 100644 index 0000000..8e62c35 Binary files /dev/null and b/Clients/PrinterSetupWizard/res/banner_icon.bmp differ diff --git a/Clients/PrinterSetupWizard/res/watermark.bmp b/Clients/PrinterSetupWizard/res/watermark.bmp new file mode 100644 index 0000000..e76046b Binary files /dev/null and b/Clients/PrinterSetupWizard/res/watermark.bmp differ diff --git a/Clients/PrinterSetupWizard/resource.h b/Clients/PrinterSetupWizard/resource.h new file mode 100644 index 0000000..8c2a6ca --- /dev/null +++ b/Clients/PrinterSetupWizard/resource.h @@ -0,0 +1,74 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by PrinterSetupWizard.rc +// +#define IDR_MANIFEST 1 +#define IDM_ABOUTBOX 0x0010 +#define IDD_ABOUTBOX 100 +#define IDS_ABOUTBOX 101 +#define IDD_PRINTERSETUPWIZARD_DIALOG 102 +#define IDS_GOODBYE 102 +#define IDS_GREETING 103 +#define IDS_BROWSE_TITLE 104 +#define IDS_BROWSE_SUBTITLE 105 +#define IDS_CAPTION 106 +#define IDD_FIRST_PAGE 107 +#define IDS_GOODBYE_GOOD1 107 +#define IDS_SEARCHING 108 +#define IDD_SECOND_PAGE 109 +#define IDS_GOODBYTE_GOOD2 109 +#define IDS_INSTALL_TITLE 110 +#define IDS_INSTALL_SUBTITLE 111 +#define IDS_ERROR_SELECTING_PRINTER_TEXT 112 +#define IDS_ERROR_SELECTING_PRINTER_CAPTION 113 +#define IDS_INSTALL_ERROR_CAPTION 114 +#define IDS_INSTALL_ERROR_MESSAGE 115 +#define IDS_MANUFACTURER_HEADING 116 +#define IDS_MODEL_HEADING 117 +#define IDS_NO_RENDEZVOUS_PRINTERS 118 +#define IDS_NO_MDNSRESPONDER_SERVICE_TEXT 119 +#define IDS_NO_MDNSRESPONDER_SERVICE_CAPTION 120 +#define IDS_PRINTER_MATCH_GOOD 121 +#define IDS_PRINTER_MATCH_BAD 122 +#define IDS_YES 123 +#define IDS_NO 124 +#define IDS_LARGE_FONT 125 +#define IDR_MAINFRAME 128 +#define IDB_BANNER_ICON 129 +#define IDD_THIRD_PAGE 130 +#define IDB_WATERMARK 131 +#define IDD_FOURTH_PAGE 132 +#define IDI_INFO 136 +#define IDB_ABOUT 138 +#define IDD_DIALOG1 139 +#define IDI_ICON2 141 +#define IDI_PRINTER 141 +#define IDC_BUTTON1 1000 +#define IDC_LIST1 1000 +#define IDC_BROWSE_LIST 1000 +#define IDC_RADIO1 1001 +#define IDC_COMBO1 1001 +#define IDC_RADIO2 1002 +#define IDC_GREETING 1003 +#define IDC_CHECK1 1016 +#define IDC_DEFAULT_PRINTER 1016 +#define IDC_PRINTER_IMAGE 1017 +#define IDC_PRINTER_NAME 1018 +#define IDC_PRINTER_MANUFACTURER 1019 +#define IDC_PRINTER_MODEL 1020 +#define IDC_GOODBYE 1021 +#define IDC_PRINTER_PORT 1022 +#define IDC_PRINTER_DEFAULT 1023 +#define IDC_PRINTER_SELECTION_TEXT 1024 +#define IDC_HAVE_DISK 1025 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 142 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1026 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Clients/PrinterSetupWizard/stdafx.cpp b/Clients/PrinterSetupWizard/stdafx.cpp new file mode 100644 index 0000000..53abf25 --- /dev/null +++ b/Clients/PrinterSetupWizard/stdafx.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: stdafx.cpp,v $ +Revision 1.1 2004/06/18 04:36:58 rpantos +First checked in + + +*/ + +#include "stdafx.h" + + diff --git a/Clients/PrinterSetupWizard/stdafx.h b/Clients/PrinterSetupWizard/stdafx.h new file mode 100644 index 0000000..160644b --- /dev/null +++ b/Clients/PrinterSetupWizard/stdafx.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: stdafx.h,v $ +Revision 1.1 2004/06/18 04:36:58 rpantos +First checked in + + +*/ + +#pragma once + +#ifndef VC_EXTRALEAN +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers +#endif + +// Modify the following defines if you have to target a platform prior to the ones specified below. +// Refer to MSDN for the latest info on corresponding values for different platforms. +#ifndef WINVER // Allow use of features specific to Windows 95 and Windows NT 4 or later. +#define WINVER 0x0400 // Change this to the appropriate value to target Windows 98 and Windows 2000 or later. +#endif + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows NT 4 or later. +#define _WIN32_WINNT 0x0400 // Change this to the appropriate value to target Windows 98 and Windows 2000 or later. +#endif + +#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later. +#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later. +#endif + +// Step 3: We want to see one image, but not a tile +#ifndef _WIN32_IE // Allow use of features specific to IE 4.0 or later. +#define _WIN32_IE 0x0500 // Change this to the appropriate value to target IE 5.0 or later. +#endif + +#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit + +// turns off MFC's hiding of some common and often safely ignored warning messages +#define _AFX_ALL_WARNINGS + +#include // MFC core and standard components +#include // MFC extensions +#include // MFC Automation classes + +#include // MFC support for Internet Explorer 4 Common Controls +#ifndef _AFX_NO_AFXCMN_SUPPORT +#include // MFC support for Windows Common Controls +#endif // _AFX_NO_AFXCMN_SUPPORT +#include + diff --git a/Clients/SimpleChat.NET/App.ico b/Clients/SimpleChat.NET/App.ico new file mode 100755 index 0000000..3a5525f Binary files /dev/null and b/Clients/SimpleChat.NET/App.ico differ diff --git a/Clients/SimpleChat.NET/AssemblyInfo.cs b/Clients/SimpleChat.NET/AssemblyInfo.cs new file mode 100755 index 0000000..a778420 --- /dev/null +++ b/Clients/SimpleChat.NET/AssemblyInfo.cs @@ -0,0 +1,90 @@ +/* + * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: AssemblyInfo.cs,v $ +Revision 1.1 2004/07/19 07:57:08 shersche +Initial revision + + + +*/ + +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/Clients/SimpleChat.NET/SimpleChat.NET.csproj b/Clients/SimpleChat.NET/SimpleChat.NET.csproj new file mode 100755 index 0000000..2165beb --- /dev/null +++ b/Clients/SimpleChat.NET/SimpleChat.NET.csproj @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Clients/SimpleChat.NET/SimpleChat.cs b/Clients/SimpleChat.NET/SimpleChat.cs new file mode 100755 index 0000000..fb841f2 --- /dev/null +++ b/Clients/SimpleChat.NET/SimpleChat.cs @@ -0,0 +1,757 @@ +/* + * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: SimpleChat.cs,v $ +Revision 1.5 2004/09/13 19:37:42 shersche +Change code to reflect namespace and type changes to dnssd.NET library + +Revision 1.4 2004/09/11 05:42:56 shersche +don't reset SelectedIndex in OnRemove + +Revision 1.3 2004/09/11 00:38:58 shersche +DNSService APIs now expect port in host format + +Revision 1.2 2004/07/19 22:08:53 shersche +Fixed rdata->int conversion problem in QueryRecordReply + +Revision 1.1 2004/07/19 07:57:08 shersche +Initial revision + + + +*/ + +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using System.Net; +using System.Net.Sockets; +using System.Data; +using System.Text; +using Apple.DNSSD; + +namespace SimpleChat.NET +{ + /// + /// Summary description for Form1. + /// + /// + + // + // PeerData + // + // Holds onto the information associated with a peer on the network + // + public class PeerData + { + public int InterfaceIndex; + public String Name; + public String Type; + public String Domain; + public IPAddress Address; + public int Port; + + public override String + ToString() + { + return Name; + } + + public override bool + Equals(object other) + { + bool result = false; + + if (other != null) + { + if ((object) this == other) + { + result = true; + } + else if (other is PeerData) + { + PeerData otherPeerData = (PeerData) other; + + result = (this.Name == otherPeerData.Name); + } + } + + return result; + } + + public override int + GetHashCode() + { + return Name.GetHashCode(); + } + }; + + // + // ResolveData + // + // Holds onto the information associated with the resolution + // of a DNSService + // + public class ResolveData + { + public int InterfaceIndex; + public String FullName; + public String HostName; + public int Port; + public Byte[] TxtRecord; + + public override String + ToString() + { + return FullName; + } + }; + + + // + // SocketStateObject + // + // Holds onto the data associated with an asynchronous + // socket operation + // + class SocketStateObject + { + public const int BUFFER_SIZE = 1024; + private Socket m_socket; + public byte[] m_buffer; + public bool m_complete; + public StringBuilder m_sb = new StringBuilder(); + + public SocketStateObject(Socket socket) + { + m_buffer = new byte[BUFFER_SIZE]; + m_complete = false; + m_socket = socket; + } + + public Socket + WorkSocket + { + get + { + return m_socket; + } + } + } + public class Form1 : System.Windows.Forms.Form + { + private System.Windows.Forms.ComboBox comboBox1; + private System.Windows.Forms.TextBox textBox2; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Label label1; + private ServiceRef registrar = null; + private ServiceRef browser = null; + private ServiceRef resolver = null; + private String myName; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + // + // These all of our callbacks. These are invoked in the context + // of the main (GUI) thread. The DNSService callbacks Invoke() + // them + delegate void RegisterServiceCallback(String name); + delegate void AddPeerCallback(PeerData data); + delegate void RemovePeerCallback(PeerData data); + delegate void ResolveServiceCallback(ResolveData data); + delegate void ResolveAddressCallback(System.Net.IPAddress address); + delegate void ReadMessageCallback(String data); + + RegisterServiceCallback registerServiceCallback; + AddPeerCallback addPeerCallback; + RemovePeerCallback removePeerCallback; + ResolveServiceCallback resolveServiceCallback; + ResolveAddressCallback resolveAddressCallback; + ReadMessageCallback readMessageCallback; + private System.Windows.Forms.RichTextBox richTextBox1; + + // + // The socket that we will be reading data from + // + Socket socket = null; + + // + // OnRegisterService + // + // The name that we are passed might be different than the + // name we called Register with. So we hold onto this name + // rather than the name we Register with. + // + // This is called (indirectly) from OnRegisterReply(). + // + private void + OnRegisterService + ( + String name + ) + { + myName = name; + } + + // + // OnAddPeer + // + // Called when DNSServices detects a new P2P Chat peer has + // joined. + // + // This is called (indirectly) from OnBrowseReply() + // + private void + OnAddPeer + ( + PeerData peer + ) + { + comboBox1.Items.Add(peer); + + if (comboBox1.Items.Count == 1) + { + comboBox1.SelectedIndex = 0; + } + } + + // + // OnRemovePeer + // + // Called when DNSServices detects a P2P peer has left + // the network + // + // This is called (indirectly) from OnBrowseReply() + // + private void + OnRemovePeer + ( + PeerData peer + ) + { + comboBox1.Items.Remove(peer); + } + + // + // OnResolveService + // + // Called when DNSServices has resolved a service. + // + // This is called (indirectly) from OnResolveService() + // + private void + OnResolveService + ( + ResolveData data + ) + { + resolver.Dispose(); + + PeerData peer = (PeerData) comboBox1.SelectedItem; + + peer.Port = data.Port; + + try + { + resolver = DNSService.QueryRecord(0, 0, data.HostName, /* ns_t_a */ 1, /* ns_t_c */ 1, new DNSService.QueryRecordReply(OnQueryRecordReply)); + } + catch + { + MessageBox.Show("QueryRecord Failed", "Error"); + Application.Exit(); + } + } + + // + // OnResolveAddress + // + // Called when DNSServices has finished a query operation + // + // This is called (indirectly) from OnQueryRecordReply() + // + private void + OnResolveAddress + ( + System.Net.IPAddress address + ) + { + resolver.Dispose(); + + PeerData peer = (PeerData) comboBox1.SelectedItem; + + peer.Address = address; + } + + // + // OnReadMessage + // + // Called when there is data to be read on a socket + // + // This is called (indirectly) from OnReadSocket() + // + private void + OnReadMessage + ( + String msg + ) + { + int rgb = 0; + + for (int i = 0; i < msg.Length && msg[i] != ':'; i++) + { + rgb = rgb ^ ((int) msg[i] << (i % 3 + 2) * 8); + } + + Color color = Color.FromArgb(rgb & 0x007F7FFF); + + richTextBox1.SelectionColor = color; + + richTextBox1.AppendText(msg + "\n"); + } + + // + // OnRegisterReply + // + // Called by DNSServices core as a result of DNSService.Register() + // call + // + // This is called from a worker thread by DNSService core. + // + private void + OnRegisterReply + ( + ServiceRef sdRef, + ServiceFlags flags, + ErrorCode errorCode, + String name, + String regtype, + String domain) + { + if (errorCode == ErrorCode.NoError) + { + Invoke(registerServiceCallback, new Object[]{name}); + } + else + { + MessageBox.Show("OnRegisterReply returned an error code " + errorCode, "Error"); + } + } + + + // + // OnBrowseReply + // + // Called by DNSServices core as a result of DNSService.Browse() + // call + // + // This is called from a worker thread by DNSService core. + // + private void + OnBrowseReply + ( + ServiceRef sdRef, + ServiceFlags flags, + int interfaceIndex, + ErrorCode errorCode, + String name, + String type, + String domain) + { + if (errorCode == ErrorCode.NoError) + { + PeerData peer = new PeerData(); + + peer.InterfaceIndex = interfaceIndex; + peer.Name = name; + peer.Type = type; + peer.Domain = domain; + peer.Address = null; + + if ((flags & ServiceFlags.Add) != 0) + { + Invoke(addPeerCallback, new Object[]{peer}); + } + else if ((flags == 0) || ((flags & ServiceFlags.MoreComing) != 0)) + { + Invoke(removePeerCallback, new Object[]{peer}); + } + } + else + { + MessageBox.Show("OnBrowseReply returned an error code " + errorCode, "Error"); + } + } + + // + // OnResolveReply + // + // Called by DNSServices core as a result of DNSService.Resolve() + // call + // + // This is called from a worker thread by DNSService core. + // + private void + OnResolveReply + ( + ServiceRef sdRef, + ServiceFlags flags, + int interfaceIndex, + ErrorCode errorCode, + String fullName, + String hostName, + int port, + Byte[] txtRecord + ) + { + if (errorCode == ErrorCode.NoError) + { + ResolveData data = new ResolveData(); + + data.InterfaceIndex = interfaceIndex; + data.FullName = fullName; + data.HostName = hostName; + data.Port = port; + data.TxtRecord = txtRecord; + + Invoke(resolveServiceCallback, new Object[]{data}); + } + else + { + MessageBox.Show("OnResolveReply returned an error code: " + errorCode, "Error"); + } + } + + // + // OnQueryRecordReply + // + // Called by DNSServices core as a result of DNSService.QueryRecord() + // call + // + // This is called from a worker thread by DNSService core. + // + private void + OnQueryRecordReply + ( + ServiceRef sdRef, + ServiceFlags flags, + int interfaceIndex, + ErrorCode errorCode, + String fullName, + int rrtype, + int rrclass, + Byte[] rdata, + int ttl + ) + { + if (errorCode == ErrorCode.NoError) + { + uint bits = BitConverter.ToUInt32(rdata, 0); + System.Net.IPAddress data = new System.Net.IPAddress(bits); + + Invoke(resolveAddressCallback, new Object[]{data}); + } + else + { + MessageBox.Show("OnQueryRecordReply returned an error code: " + errorCode, "Error"); + } + } + + // + // OnReadSocket + // + // Called by the .NET core when there is data to be read on a socket + // + // This is called from a worker thread by the .NET core + // + private void + OnReadSocket + ( + IAsyncResult ar + ) + { + SocketStateObject so = (SocketStateObject) ar.AsyncState; + Socket s = so.WorkSocket; + + try + { + if (s == null) + { + return; + } + + int read = s.EndReceive(ar); + + if (read > 0) + { + String msg = Encoding.UTF8.GetString(so.m_buffer, 0, read); + + Invoke(readMessageCallback, new Object[]{msg}); + } + + s.BeginReceive(so.m_buffer, 0, SocketStateObject.BUFFER_SIZE, 0, new AsyncCallback(OnReadSocket), so); + } + catch + { + } + } + + + public Form1() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + registerServiceCallback = new RegisterServiceCallback(OnRegisterService); + addPeerCallback = new AddPeerCallback(OnAddPeer); + removePeerCallback = new RemovePeerCallback(OnRemovePeer); + resolveServiceCallback = new ResolveServiceCallback(OnResolveService); + resolveAddressCallback = new ResolveAddressCallback(OnResolveAddress); + readMessageCallback = new ReadMessageCallback(OnReadMessage); + + this.Load += new System.EventHandler(this.Form1_Load); + + this.AcceptButton = button1; + } + + /// + /// Clean up any resources being used. + /// + protected override void + Dispose( bool disposing ) + { + if( disposing ) + { + if (components != null) + { + components.Dispose(); + } + + if (registrar != null) + { + registrar.Dispose(); + } + + if (browser != null) + { + browser.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.comboBox1 = new System.Windows.Forms.ComboBox(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.button1 = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.richTextBox1 = new System.Windows.Forms.RichTextBox(); + this.SuspendLayout(); + // + // comboBox1 + // + this.comboBox1.Anchor = ((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right); + this.comboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBox1.Location = new System.Drawing.Point(59, 208); + this.comboBox1.Name = "comboBox1"; + this.comboBox1.Size = new System.Drawing.Size(224, 21); + this.comboBox1.Sorted = true; + this.comboBox1.TabIndex = 5; + this.comboBox1.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged); + // + // textBox2 + // + this.textBox2.Anchor = ((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right); + this.textBox2.Location = new System.Drawing.Point(8, 248); + this.textBox2.Name = "textBox2"; + this.textBox2.ScrollBars = System.Windows.Forms.ScrollBars.Horizontal; + this.textBox2.Size = new System.Drawing.Size(192, 20); + this.textBox2.TabIndex = 2; + this.textBox2.Text = ""; + this.textBox2.TextChanged += new System.EventHandler(this.textBox2_TextChanged); + // + // button1 + // + this.button1.Anchor = (System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right); + this.button1.Enabled = false; + this.button1.Location = new System.Drawing.Point(208, 248); + this.button1.Name = "button1"; + this.button1.TabIndex = 3; + this.button1.Text = "Send"; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // label1 + // + this.label1.Anchor = (System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left); + this.label1.Location = new System.Drawing.Point(8, 210); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(48, 16); + this.label1.TabIndex = 4; + this.label1.Text = "Talk To:"; + // + // richTextBox1 + // + this.richTextBox1.Anchor = (((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right); + this.richTextBox1.Location = new System.Drawing.Point(8, 8); + this.richTextBox1.Name = "richTextBox1"; + this.richTextBox1.ReadOnly = true; + this.richTextBox1.Size = new System.Drawing.Size(272, 184); + this.richTextBox1.TabIndex = 1; + this.richTextBox1.Text = ""; + // + // Form1 + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(292, 273); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.richTextBox1, + this.label1, + this.button1, + this.textBox2, + this.comboBox1}); + this.Name = "Form1"; + this.Text = "SimpleChat.NET"; + this.ResumeLayout(false); + + } + #endregion + + private void Form1_Load(object sender, EventArgs e) + { + IPEndPoint localEP = new IPEndPoint(System.Net.IPAddress.Any, 0); + + // + // create the socket and bind to INADDR_ANY + // + socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + socket.Bind(localEP); + localEP = (IPEndPoint) socket.LocalEndPoint; + + // + // start asynchronous read + // + SocketStateObject so = new SocketStateObject(socket); + socket.BeginReceive(so.m_buffer, 0, SocketStateObject.BUFFER_SIZE, 0, new AsyncCallback(this.OnReadSocket), so); + + try + { + // + // start the register and browse operations + // + registrar = DNSService.Register(0, 0, System.Environment.UserName, "_p2pchat._udp", null, null, localEP.Port, null, new DNSService.RegisterReply(OnRegisterReply)); + browser = DNSService.Browse(0, 0, "_p2pchat._udp", null, new DNSService.BrowseReply(OnBrowseReply)); + } + catch + { + MessageBox.Show("DNSServices Not Available", "Error"); + Application.Exit(); + } + } + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.Run(new Form1()); + } + + // + // send the message to a peer + // + private void button1_Click(object sender, System.EventArgs e) + { + PeerData peer = (PeerData) comboBox1.SelectedItem; + + String message = myName + ": " + textBox2.Text; + + Byte[] bytes = Encoding.UTF8.GetBytes(message); + + UdpClient udpSocket = new UdpClient(peer.Address.ToString(), peer.Port); + + udpSocket.Send(bytes, bytes.Length); + + richTextBox1.SelectionColor = Color.Black; + + richTextBox1.AppendText(textBox2.Text + "\n"); + + textBox2.Text = ""; + } + + // + // called when typing in message box + // + private void textBox2_TextChanged(object sender, System.EventArgs e) + { + PeerData peer = (PeerData) comboBox1.SelectedItem; + + if ((peer.Address != null) && (textBox2.Text.Length > 0)) + { + button1.Enabled = true; + } + else + { + button1.Enabled = false; + } + } + + // + // called when peer target changes + // + /// + /// + /// + /// + private void comboBox1_SelectedIndexChanged(object sender, System.EventArgs e) + { + PeerData peer = (PeerData) comboBox1.SelectedItem; + + try + { + resolver = DNSService.Resolve(0, 0, peer.Name, peer.Type, peer.Domain, new DNSService.ResolveReply(OnResolveReply)); + } + catch + { + MessageBox.Show("Unable to Resolve service", "Error"); + Application.Exit(); + } + } + } +} diff --git a/Clients/SimpleChat.NET/SimpleChat.resx b/Clients/SimpleChat.NET/SimpleChat.resx new file mode 100755 index 0000000..e5b5a11 --- /dev/null +++ b/Clients/SimpleChat.NET/SimpleChat.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Form1 + + \ No newline at end of file diff --git a/Clients/dns-sd.c b/Clients/dns-sd.c index 1fb02d0..17c4a76 100644 --- a/Clients/dns-sd.c +++ b/Clients/dns-sd.c @@ -1,26 +1,40 @@ /* * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ - * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Formatting notes: * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion @@ -35,42 +49,39 @@ * understand why variable y is not of type "char*" just proves the point that poor code * layout leads people to unfortunate misunderstandings about how the C language really works.) - Change History (most recent first): - -$Log: dns-sd.c,v $ -Revision 1.7 2004/05/28 02:20:06 cheshire -If we allow dot or empty string for domain when resolving a service, -it should be a synonym for "local" - -Revision 1.6 2004/05/21 19:57:19 cheshire -Add -Q option to exercise DNSServiceQueryRecord() API call +To build this tool, copy and paste the following into a command line: -Revision 1.5 2004/05/21 17:39:27 cheshire -Include extra headers to fix Xcode build +OS X: +gcc dns-sd.c -o dns-sd -Revision 1.4 2004/05/21 17:25:56 cheshire -Fixes to make sample client work on Linux - -Revision 1.3 2004/04/06 22:02:06 cheshire -Also show interface id when showing browse add/remove events - -Revision 1.2 2004/03/25 05:40:56 cheshire -Changes from Marc Krochmal: Fix inconsistent use of space/tab - -Revision 1.1 2004/02/06 03:19:09 cheshire -Check in code to make command-line "dns-sd" testing tool +POSIX systems: +gcc dns-sd.c -o dns-sd -I../mDNSShared -ldns_sd +Windows: +cl dns-sd.c -I../mDNSShared -DNOT_HAVE_GETOPT -DNOT_HAVE_SETLINEBUF ws2_32.lib ..\mDNSWindows\DLL\Release\dnssd.lib +(may require that you run a Visual Studio script such as vsvars32.bat first) */ +#include "dns_sd.h" +#include #include // For stdout, stderr #include // For exit() -#include // For strlen(), strcpy(), bzero() -#include // For getopt() and optind +#include // For strlen(), strcpy(), bzero() #include // For errno, EINTR -#define BIND_8_COMPAT -#include // For T_HINFO, etc. +#include + +#ifdef _WIN32 +#include +typedef int pid_t; +#define getpid _getpid +#define strcasecmp _stricmp +#define snprintf _snprintf +#else #include // For struct timeval -#include +#include // For getopt() and optind +#include // For inet_addr() +#endif + //************************************************************************************************************* // Globals @@ -78,76 +89,199 @@ Check in code to make command-line "dns-sd" testing tool typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16; static int operation; -static DNSServiceRef client = NULL; +static DNSServiceRef client = NULL; +static DNSServiceRef client2 = NULL; static int num_printed; static char addtest = 0; static DNSRecordRef record = NULL; -static char myhinfo9[11] = "\003Mac\006OS 9.2"; +static char myhinfoW[14] = "\002PC\012Windows XP"; static char myhinfoX[ 9] = "\003Mac\004OS X"; static char updatetest[3] = "\002AA"; static char bigNULL[4096]; -#define LONG_TIME 0x4000000 +// Note: the select() implementation on Windows (Winsock2) fails with any timeout much larger than this +#define LONG_TIME 100000000 + static volatile int stopNow = 0; static volatile int timeOut = LONG_TIME; //************************************************************************************************************* // Supporting Utility Function +static uint16_t GetRRType(const char *s) + { + if (!strcasecmp(s, "A" )) return(kDNSServiceType_A); + else if (!strcasecmp(s, "NS" )) return(kDNSServiceType_NS); + else if (!strcasecmp(s, "MD" )) return(kDNSServiceType_MD); + else if (!strcasecmp(s, "MF" )) return(kDNSServiceType_MF); + else if (!strcasecmp(s, "CNAME" )) return(kDNSServiceType_CNAME); + else if (!strcasecmp(s, "SOA" )) return(kDNSServiceType_SOA); + else if (!strcasecmp(s, "MB" )) return(kDNSServiceType_MB); + else if (!strcasecmp(s, "MG" )) return(kDNSServiceType_MG); + else if (!strcasecmp(s, "MR" )) return(kDNSServiceType_MR); + else if (!strcasecmp(s, "NULL" )) return(kDNSServiceType_NULL); + else if (!strcasecmp(s, "WKS" )) return(kDNSServiceType_WKS); + else if (!strcasecmp(s, "PTR" )) return(kDNSServiceType_PTR); + else if (!strcasecmp(s, "HINFO" )) return(kDNSServiceType_HINFO); + else if (!strcasecmp(s, "MINFO" )) return(kDNSServiceType_MINFO); + else if (!strcasecmp(s, "MX" )) return(kDNSServiceType_MX); + else if (!strcasecmp(s, "TXT" )) return(kDNSServiceType_TXT); + else if (!strcasecmp(s, "RP" )) return(kDNSServiceType_RP); + else if (!strcasecmp(s, "AFSDB" )) return(kDNSServiceType_AFSDB); + else if (!strcasecmp(s, "X25" )) return(kDNSServiceType_X25); + else if (!strcasecmp(s, "ISDN" )) return(kDNSServiceType_ISDN); + else if (!strcasecmp(s, "RT" )) return(kDNSServiceType_RT); + else if (!strcasecmp(s, "NSAP" )) return(kDNSServiceType_NSAP); + else if (!strcasecmp(s, "NSAP_PTR")) return(kDNSServiceType_NSAP_PTR); + else if (!strcasecmp(s, "SIG" )) return(kDNSServiceType_SIG); + else if (!strcasecmp(s, "KEY" )) return(kDNSServiceType_KEY); + else if (!strcasecmp(s, "PX" )) return(kDNSServiceType_PX); + else if (!strcasecmp(s, "GPOS" )) return(kDNSServiceType_GPOS); + else if (!strcasecmp(s, "AAAA" )) return(kDNSServiceType_AAAA); + else if (!strcasecmp(s, "LOC" )) return(kDNSServiceType_LOC); + else if (!strcasecmp(s, "NXT" )) return(kDNSServiceType_NXT); + else if (!strcasecmp(s, "EID" )) return(kDNSServiceType_EID); + else if (!strcasecmp(s, "NIMLOC" )) return(kDNSServiceType_NIMLOC); + else if (!strcasecmp(s, "SRV" )) return(kDNSServiceType_SRV); + else if (!strcasecmp(s, "ATMA" )) return(kDNSServiceType_ATMA); + else if (!strcasecmp(s, "NAPTR" )) return(kDNSServiceType_NAPTR); + else if (!strcasecmp(s, "KX" )) return(kDNSServiceType_KX); + else if (!strcasecmp(s, "CERT" )) return(kDNSServiceType_CERT); + else if (!strcasecmp(s, "A6" )) return(kDNSServiceType_A6); + else if (!strcasecmp(s, "DNAME" )) return(kDNSServiceType_DNAME); + else if (!strcasecmp(s, "SINK" )) return(kDNSServiceType_SINK); + else if (!strcasecmp(s, "OPT" )) return(kDNSServiceType_OPT); + else if (!strcasecmp(s, "TKEY" )) return(kDNSServiceType_TKEY); + else if (!strcasecmp(s, "TSIG" )) return(kDNSServiceType_TSIG); + else if (!strcasecmp(s, "IXFR" )) return(kDNSServiceType_IXFR); + else if (!strcasecmp(s, "AXFR" )) return(kDNSServiceType_AXFR); + else if (!strcasecmp(s, "MAILB" )) return(kDNSServiceType_MAILB); + else if (!strcasecmp(s, "MAILA" )) return(kDNSServiceType_MAILA); + else if (!strcasecmp(s, "ANY" )) return(kDNSServiceType_ANY); + else return(atoi(s)); + } + //************************************************************************************************************* // Sample callback functions for each of the operation types static void printtimestamp(void) { - struct timeval tv; struct tm tm; + int ms; +#ifdef _WIN32 + SYSTEMTIME sysTime; + time_t uct = time(NULL); + tm = *localtime(&uct); + GetLocalTime(&sysTime); + ms = sysTime.wMilliseconds; +#else + struct timeval tv; gettimeofday(&tv, NULL); localtime_r((time_t*)&tv.tv_sec, &tm); - printf("%2d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec/1000); + ms = tv.tv_usec/1000; +#endif + printf("%2d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min, tm.tm_sec, ms); } #define DomainMsg(X) (((X) & kDNSServiceFlagsDefault) ? "(Default)" : \ ((X) & kDNSServiceFlagsAdd) ? "Added" : "Removed") -static void regdom_reply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interface, - DNSServiceErrorType errorCode, const char *replyDomain, void *context) +static const char *GetNextLabel(const char *cstr, char label[64]) { - (void)interface; // Unused - (void)errorCode; // Unused - (void)context; // Unused - printtimestamp(); - printf("Recommended Registration Domain %s %s", replyDomain, DomainMsg(flags)); - if (flags) printf(" Flags: %X", flags); - printf("\n"); + char *ptr = label; + while (*cstr && *cstr != '.') // While we have characters in the label... + { + char c = *cstr++; + if (c == '\\') + { + c = *cstr++; + if (isdigit(cstr[-1]) && isdigit(cstr[0]) && isdigit(cstr[1])) + { + int v0 = cstr[-1] - '0'; // then interpret as three-digit decimal + int v1 = cstr[ 0] - '0'; + int v2 = cstr[ 1] - '0'; + int val = v0 * 100 + v1 * 10 + v2; + if (val <= 255) { c = (char)val; cstr += 2; } // If valid three-digit decimal value, use it + } + } + *ptr++ = c; + if (ptr >= label+64) return(NULL); + } + if (*cstr) cstr++; // Skip over the trailing dot (if present) + *ptr++ = 0; + return(cstr); } -static void browsedom_reply(DNSServiceRef client, DNSServiceFlags flags, uint32_t interface, +static void DNSSD_API enum_reply(DNSServiceRef client, DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, const char *replyDomain, void *context) { + int labels = 0, depth = 0, i, initial = 0; + char text[64]; + const char *label[128]; + (void)client; // Unused - (void)interface; // Unused + (void)ifIndex; // Unused (void)errorCode; // Unused (void)context; // Unused + + if (!*replyDomain) return; + + // 1. Print the header + if (num_printed++ == 0) printf("Timestamp Recommended %s domain\n", operation == 'E' ? "Registration" : "Browsing"); printtimestamp(); - printf("Recommended Browsing Domain %s %s", replyDomain, DomainMsg(flags)); - if (flags) printf(" Flags: %X", flags); + printf("%-10s", DomainMsg(flags)); + printf("%-8s", (flags & kDNSServiceFlagsMoreComing) ? "(More)" : ""); + flags &= ~kDNSServiceFlagsMoreComing; + flags &= ~kDNSServiceFlagsAdd; + flags &= ~kDNSServiceFlagsDefault; + if (flags) printf("Flags: %4X ", flags); + else printf(" "); + + // 2. Count the labels + while (*replyDomain) + { + label[labels++] = replyDomain; + replyDomain = GetNextLabel(replyDomain, text); + } + + // 3. Decide if we're going to clump the last two or three labels (e.g. "apple.com", or "nicta.com.au") + if (labels >= 3 && replyDomain - label[labels-1] <= 3 && label[labels-1] - label[labels-2] <= 4) initial = 3; + else if (labels >= 2 && replyDomain - label[labels-1] <= 4) initial = 2; + else initial = 1; + labels -= initial; + + // 4. Print the initial one-, two- or three-label clump + for (i=0; i0) printf("."); + printf("%s", text); + } printf("\n"); + + // 5. Print the remainder of the hierarchy + for (depth=0; depth %s\n", text); + } } -static void browse_reply(DNSServiceRef client, DNSServiceFlags flags, uint32_t interface, DNSServiceErrorType errorCode, +static void DNSSD_API browse_reply(DNSServiceRef client, DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, const char *replyName, const char *replyType, const char *replyDomain, void *context) { char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv"; (void)client; // Unused - (void)interface; // Unused (void)errorCode; // Unused (void)context; // Unused if (num_printed++ == 0) printf("Timestamp A/R Flags if %-24s %-24s %s\n", "Domain", "Service Type", "Instance Name"); printtimestamp(); - printf("%s%6X%3d %-24s %-24s %s\n", op, flags, interface, replyDomain, replyType, replyName); + printf("%s%6X%3d %-24s %-24s %s\n", op, flags, ifIndex, replyDomain, replyType, replyName); } -static void resolve_reply(DNSServiceRef client, DNSServiceFlags flags, uint32_t interface, DNSServiceErrorType errorCode, +static void DNSSD_API resolve_reply(DNSServiceRef client, DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t opaqueport, uint16_t txtLen, const char *txtRecord, void *context) { const char *src = txtRecord; @@ -155,8 +289,9 @@ static void resolve_reply(DNSServiceRef client, DNSServiceFlags flags, uint32_t uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1]; (void)client; // Unused - (void)interface; // Unused + (void)ifIndex; // Unused (void)errorCode; // Unused + (void)txtLen; // Unused (void)context; // Unused printtimestamp(); @@ -195,7 +330,7 @@ static void resolve_reply(DNSServiceRef client, DNSServiceFlags flags, uint32_t static void myTimerCallBack(void) { - DNSServiceErrorType err; + DNSServiceErrorType err = kDNSServiceErr_Unknown; switch (operation) { @@ -204,11 +339,11 @@ static void myTimerCallBack(void) switch (addtest) { case 0: printf("Adding Test HINFO record\n"); - err = DNSServiceAddRecord(client, &record, 0, T_HINFO, sizeof(myhinfo9), &myhinfo9[0], 120); + err = DNSServiceAddRecord(client, &record, 0, kDNSServiceType_HINFO, sizeof(myhinfoW), &myhinfoW[0], 0); addtest = 1; break; case 1: printf("Updating Test HINFO record\n"); - err = DNSServiceUpdateRecord(client, record, 0, sizeof(myhinfoX), &myhinfoX[0], 120); + err = DNSServiceUpdateRecord(client, record, 0, sizeof(myhinfoX), &myhinfoX[0], 0); addtest = 2; break; case 2: printf("Removing Test HINFO record\n"); @@ -226,14 +361,14 @@ static void myTimerCallBack(void) updatetest[0] = 3 - updatetest[0]; updatetest[2] = updatetest[1]; printf("Updating Test TXT record to %c\n", updatetest[1]); - err = DNSServiceUpdateRecord(client, NULL, 0, 1+updatetest[0], &updatetest[0], 120); + err = DNSServiceUpdateRecord(client, NULL, 0, 1+updatetest[0], &updatetest[0], 0); } break; case 'N': { printf("Adding big NULL record\n"); - err = DNSServiceAddRecord(client, &record, 0, T_NULL, sizeof(bigNULL), &bigNULL[0], 120); + err = DNSServiceAddRecord(client, &record, 0, kDNSServiceType_NULL, sizeof(bigNULL), &bigNULL[0], 0); timeOut = LONG_TIME; } break; @@ -246,7 +381,7 @@ static void myTimerCallBack(void) } } -static void reg_reply(DNSServiceRef client, DNSServiceFlags flags, DNSServiceErrorType errorCode, +static void DNSSD_API reg_reply(DNSServiceRef client, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, void *context) { (void)client; // Unused @@ -264,17 +399,33 @@ static void reg_reply(DNSServiceRef client, DNSServiceFlags flags, DNSServiceErr if (operation == 'A' || operation == 'U' || operation == 'N') timeOut = 5; } -void qr_reply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, +static void DNSSD_API qr_reply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context) { - const unsigned char *rd = rdata; + char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv"; + const unsigned char *rd = rdata; + const unsigned char *end = (const unsigned char *) rdata + rdlen; char rdb[1000]; + char *p = rdb; + const char * const lim = rdb + sizeof(rdb); + + (void)sdRef; // Unused + (void)flags; // Unused + (void)ifIndex; // Unused + (void)errorCode;// Unused + (void)ttl; // Unused + (void)context; // Unused + switch (rrtype) { - case T_A: snprintf(rdb, sizeof(rdb), "%d.%d.%d.%d", rd[0], rd[1], rd[2], rd[3]); break; - default : snprintf(rdb, sizeof(rdb), "Unknown rdata: %d bytes", rdlen); break; + case kDNSServiceType_A: sprintf(rdb, "%d.%d.%d.%d", rd[0], rd[1], rd[2], rd[3]); break; + default : p += snprintf(p, lim-p, "%d bytes%s", rdlen, rdlen ? ":" : ""); + while (rd < end && p < lim) p += snprintf(p, lim-p, " %02X", *rd++); + break; } - printf("%-30s%4d%4d %s\n", fullname, rrtype, rrclass, rdb); + if (num_printed++ == 0) printf("Timestamp A/R Flags if %-30s%4s%4s Rdata\n", "Name", "T", "C"); + printtimestamp(); + printf("%s%6X%3d %-30s%4d%4d %s\n", op, flags, ifIndex, fullname, rrtype, rrclass, rdb); } //************************************************************************************************************* @@ -282,11 +433,14 @@ void qr_reply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceInde static void HandleEvents(void) { - int dns_sd_fd = DNSServiceRefSockFD(client); + int dns_sd_fd = client ? DNSServiceRefSockFD(client ) : -1; + int dns_sd_fd2 = client2 ? DNSServiceRefSockFD(client2) : -1; int nfds = dns_sd_fd + 1; fd_set readfds; struct timeval tv; int result; + + if (dns_sd_fd2 > dns_sd_fd) nfds = dns_sd_fd2 + 1; while (!stopNow) { @@ -296,7 +450,8 @@ static void HandleEvents(void) FD_ZERO(&readfds); // 2. Add the fd for our client(s) to the fd_set - FD_SET(dns_sd_fd, &readfds); + if (client ) FD_SET(dns_sd_fd , &readfds); + if (client2) FD_SET(dns_sd_fd2, &readfds); // 3. Set up the timeout. tv.tv_sec = timeOut; @@ -305,7 +460,10 @@ static void HandleEvents(void) result = select(nfds, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv); if (result > 0) { - if (FD_ISSET(dns_sd_fd, &readfds)) DNSServiceProcessResult(client); + DNSServiceErrorType err = kDNSServiceErr_NoError; + if (client && FD_ISSET(dns_sd_fd , &readfds)) err = DNSServiceProcessResult(client ); + else if (client2 && FD_ISSET(dns_sd_fd2, &readfds)) err = DNSServiceProcessResult(client2); + if (err) { fprintf(stderr, "DNSServiceProcessResult returned %d\n", err); stopNow = 1; } } else if (result == 0) myTimerCallBack(); @@ -317,24 +475,114 @@ static void HandleEvents(void) } } +static int getfirstoption( int argc, char **argv, const char *optstr, int *pOptInd) +// Return the recognized option in optstr and the option index of the next arg. +#if NOT_HAVE_GETOPT + { + int i; + for ( i=1; i < argc; i++) + { + if ( argv[i][0] == '-' && &argv[i][1] && + NULL != strchr( optstr, argv[i][1])) + { + *pOptInd = i + 1; + return argv[i][1]; + } + } + return -1; + } +#else + { + int operation = getopt(argc, (char * const *)argv, optstr); + *pOptInd = optind; + return operation; + } +#endif + +static void DNSSD_API MyRegisterRecordCallback(DNSServiceRef service, DNSRecordRef record, DNSServiceFlags flags, + DNSServiceErrorType errorCode, void * context) + { + char *name = (char *)context; + + (void)service; // Unused + (void)record; // Unused + (void)flags; // Unused + + printf("Got a reply for %s: ", name); + switch (errorCode) + { + case kDNSServiceErr_NoError: printf("Name now registered and active\n"); break; + case kDNSServiceErr_NameConflict: printf("Name in use, please choose another\n"); exit(-1); + default: printf("Error %d\n", errorCode); return; + } + } + +static DNSServiceErrorType RegisterProxyAddressRecord(DNSServiceRef *sdRef, const char *host, const char *ip) + { + unsigned long addr = inet_addr(ip); + DNSServiceErrorType err = DNSServiceCreateConnection(sdRef); + if (err) { fprintf(stderr, "DNSServiceCreateConnection returned %d\n", err); return(err); } + return(DNSServiceRegisterRecord(*sdRef, &record, kDNSServiceFlagsUnique, kDNSServiceInterfaceIndexAny, host, + kDNSServiceType_A, kDNSServiceClass_IN, sizeof(addr), &addr, 240, MyRegisterRecordCallback, (void*)host)); + } + +static DNSServiceErrorType RegisterService(DNSServiceRef *sdRef, + const char *nam, const char *typ, const char *dom, const char *host, const char *port, int argc, char **argv) + { + uint16_t PortAsNumber = atoi(port); + Opaque16 registerPort = { { PortAsNumber >> 8, PortAsNumber & 0xFF } }; + char txt[2048] = ""; + char *ptr = txt; + int i; + + if (nam[0] == '.' && nam[1] == 0) nam = ""; // We allow '.' on the command line as a synonym for empty string + if (dom[0] == '.' && dom[1] == 0) dom = ""; // We allow '.' on the command line as a synonym for empty string + + for (i = 0; i < argc; i++) + { + char *len = ptr++; + *len = strlen(argv[i]); + strcpy(ptr, argv[i]); + ptr += *len; + } + + printf("Registering Service %s.%s%s", nam, typ, dom); + if (host && *host) printf(" host %s", host); + printf(" port %s %s\n", port, txt); + return(DNSServiceRegister(sdRef, /* kDNSServiceFlagsAllowRemoteQuery */ 0, 0, nam, typ, dom, host, registerPort.NotAnInteger, ptr-txt, txt, reg_reply, NULL)); + } + int main(int argc, char **argv) { +#ifdef _WIN32 + const char kFilePathSep = '\\'; +#else + const char kFilePathSep = '/'; +#endif DNSServiceErrorType err; char *dom; + int optind; + const char *progname = strrchr(argv[0], kFilePathSep) ? strrchr(argv[0], kFilePathSep) + 1 : argv[0]; +#ifndef NOT_HAVE_SETLINEBUF setlinebuf(stdout); // Want to see lines as they appear, not block buffered +#endif if (argc < 2) goto Fail; // Minimum command line is the command name and one argument - operation = getopt(argc, (char * const *)argv, "EFBLQRAUNTMI"); + operation = getfirstoption( argc, argv, "EFBLQRPAUNTMI", &optind); if (operation == -1) goto Fail; switch (operation) { case 'E': printf("Looking for recommended registration domains:\n"); - err = DNSServiceEnumerateDomains(&client, kDNSServiceFlagsRegistrationDomains, 0, regdom_reply, NULL); + err = DNSServiceEnumerateDomains(&client, kDNSServiceFlagsRegistrationDomains, 0, enum_reply, NULL); break; case 'F': printf("Looking for recommended browsing domains:\n"); - err = DNSServiceEnumerateDomains(&client, kDNSServiceFlagsBrowseDomains, 0, browsedom_reply, NULL); + err = DNSServiceEnumerateDomains(&client, kDNSServiceFlagsBrowseDomains, 0, enum_reply, NULL); + //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "nicta.com.au.", NULL); + //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "rendezvous.nicta.com.au.", NULL); + //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "ibm.com.", NULL); + //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "dns-sd.ibm.com.", NULL); break; case 'B': if (argc < optind+1) goto Fail; @@ -352,37 +600,23 @@ int main(int argc, char **argv) break; case 'R': if (argc < optind+4) goto Fail; - { - char *nam = argv[optind+0]; - char *typ = argv[optind+1]; - char *dom = argv[optind+2]; - uint16_t PortAsNumber = atoi(argv[optind+3]); - Opaque16 registerPort = { { PortAsNumber >> 8, PortAsNumber & 0xFF } }; - char txt[2048]; - char *ptr = txt; - int i; - - if (nam[0] == '.' && nam[1] == 0) nam[0] = 0; // We allow '.' on the command line as a synonym for empty string - if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string - - for (i = optind+4; i < argc; i++) - { - char *len = ptr++; - *len = strlen(argv[i]); - strcpy(ptr, argv[i]); - ptr += *len; - } + err = RegisterService(&client, argv[optind+0], argv[optind+1], argv[optind+2], NULL, argv[optind+3], argc-(optind+4), argv+(optind+4)); + break; - printf("Registering Service %s.%s%s port %s %s\n", nam, typ, dom, argv[optind+3], txt); - err = DNSServiceRegister(&client, 0, 0, nam, typ, dom, NULL, registerPort.NotAnInteger, ptr-txt, txt, reg_reply, NULL); + case 'P': if (argc < optind+6) goto Fail; + err = RegisterProxyAddressRecord(&client2, argv[optind+4], argv[optind+5]); + if (err) break; + err = RegisterService(&client, argv[optind+0], argv[optind+1], argv[optind+2], argv[optind+4], argv[optind+3], argc-(optind+6), argv+(optind+6)); break; - } case 'Q': { + uint16_t rrtype, rrclass; + DNSServiceFlags flags = 0; if (argc < optind+1) goto Fail; - uint16_t rrtype = (argc <= optind+1) ? T_A : atoi(argv[optind+1]); - uint16_t rrclass = (argc <= optind+2) ? C_IN : atoi(argv[optind+2]); - err = DNSServiceQueryRecord(&client, 0, 0, argv[optind+0], rrtype, rrclass, qr_reply, NULL); + rrtype = (argc <= optind+1) ? kDNSServiceType_A : GetRRType(argv[optind+1]); + rrclass = (argc <= optind+2) ? kDNSServiceClass_IN : atoi(argv[optind+2]); + if (rrtype == kDNSServiceType_TXT || rrtype == kDNSServiceType_PTR) flags |= kDNSServiceFlagsLongLivedQuery; + err = DNSServiceQueryRecord(&client, flags, 0, argv[optind+0], rrtype, rrclass, qr_reply, NULL); break; } @@ -414,7 +648,7 @@ int main(int argc, char **argv) static const char TXT2[] = "\xD" "Fourth String" "\xC" "Fifth String" "\xC" "Sixth String"; printf("Registering Service Test._testdualtxt._tcp.local.\n"); err = DNSServiceRegister(&client, 0, 0, "Test", "_testdualtxt._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT1)-1, TXT1, reg_reply, NULL); - if (!err) err = DNSServiceAddRecord(client, &record, 0, T_TXT, sizeof(TXT2)-1, TXT2, 120); + if (!err) err = DNSServiceAddRecord(client, &record, 0, kDNSServiceType_TXT, sizeof(TXT2)-1, TXT2, 0); break; } @@ -424,7 +658,7 @@ int main(int argc, char **argv) static const char TXT[] = "\x09" "Test Data"; printf("Registering Service Test._testtxt._tcp.local.\n"); err = DNSServiceRegister(&client, 0, 0, "Test", "_testtxt._tcp.", "", NULL, registerPort.NotAnInteger, 0, NULL, reg_reply, NULL); - if (!err) err = DNSServiceUpdateRecord(client, NULL, 0, sizeof(TXT)-1, TXT, 120); + if (!err) err = DNSServiceUpdateRecord(client, NULL, 0, sizeof(TXT)-1, TXT, 0); break; } @@ -435,21 +669,23 @@ int main(int argc, char **argv) HandleEvents(); // Be sure to deallocate the DNSServiceRef when you're finished - DNSServiceRefDeallocate(client); + if (client ) DNSServiceRefDeallocate(client ); + if (client2) DNSServiceRefDeallocate(client2); return 0; Fail: - fprintf(stderr, "%s -E (Enumerate recommended registration domains)\n", argv[0]); - fprintf(stderr, "%s -F (Enumerate recommended browsing domains)\n", argv[0]); - fprintf(stderr, "%s -B (Browse for services instances)\n", argv[0]); - fprintf(stderr, "%s -L (Look up a service instance)\n", argv[0]); - fprintf(stderr, "%s -R [...] (Register a service)\n", argv[0]); - fprintf(stderr, "%s -Q (Generic query for any record type)\n", argv[0]); - fprintf(stderr, "%s -A (Test Adding/Updating/Deleting a record)\n", argv[0]); - fprintf(stderr, "%s -U (Test updating a TXT record)\n", argv[0]); - fprintf(stderr, "%s -N (Test adding a large NULL record)\n", argv[0]); - fprintf(stderr, "%s -T (Test creating a large TXT record)\n", argv[0]); - fprintf(stderr, "%s -M (Test creating a registration with multiple TXT records)\n", argv[0]); - fprintf(stderr, "%s -I (Test registering and then immediately updating TXT record)\n", argv[0]); + fprintf(stderr, "%s -E (Enumerate recommended registration domains)\n", progname); + fprintf(stderr, "%s -F (Enumerate recommended browsing domains)\n", progname); + fprintf(stderr, "%s -B (Browse for services instances)\n", progname); + fprintf(stderr, "%s -L (Look up a service instance)\n", progname); + fprintf(stderr, "%s -R [...] (Register a service)\n", progname); + fprintf(stderr, "%s -P [...] (Proxy)\n", progname); + fprintf(stderr, "%s -Q (Generic query for any record type)\n", progname); + fprintf(stderr, "%s -A (Test Adding/Updating/Deleting a record)\n", progname); + fprintf(stderr, "%s -U (Test updating a TXT record)\n", progname); + fprintf(stderr, "%s -N (Test adding a large NULL record)\n", progname); + fprintf(stderr, "%s -T (Test creating a large TXT record)\n", progname); + fprintf(stderr, "%s -M (Test creating a registration with multiple TXT records)\n", progname); + fprintf(stderr, "%s -I (Test registering and then immediately updating TXT record)\n", progname); return 0; } diff --git a/Makefile b/Makefile index 6535aa1..20adec5 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ include /Developer/Makefiles/pb_makefiles/platform.make -MVERS = "mDNSResponder-66.3" +MVERS = "mDNSResponder-87" install: cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild install OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) diff --git a/README.txt b/README.txt index fa70ecf..5f17866 100644 --- a/README.txt +++ b/README.txt @@ -141,3 +141,26 @@ instead of embedding their own mDNSResponder. See ReadMe.txt in the mDNSPosix directory for specific details of building an mDNSResponder on a POSIX Operating System. + + +Compiling on Older C Compilers +------------------------------ + +We go to some lengths to make the code portable, but //-style comments +are one of the modern conveniences we can't live without. + +If your C compiler doesn't understand these comments, you can transform +them into classical K&R /* style */ comments with a quick GREP +search-and-replace pattern. + +In BBEdit on the Mac: +1. Open the "Find" dialog window and make sure "Use Grep" is selected +2. Search For : ([^:])//(.*) +3. Replace With: \1/*\2 */ +4. Drag your mDNSResponder source code folder to the Multi-File search pane +5. Click "Replace All" + +For the more command-line oriented, cd into your mDNSResponder source code +directory and execute the following command (all one line): + +find mDNSResponder \( -name \*.c\* -or -name \*.h \) -exec sed -i .orig -e 's,^//\(.*\),/*\1 */,' -e '/\/\*/\!s,\([^:]\)//\(.*\),\1/*\2 */,' {} \; diff --git a/mDNSCore/DNSCommon.c b/mDNSCore/DNSCommon.c index 6e317a9..13319c1 100644 --- a/mDNSCore/DNSCommon.c +++ b/mDNSCore/DNSCommon.c @@ -3,8 +3,6 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -25,6 +23,135 @@ Change History (most recent first): $Log: DNSCommon.c,v $ +Revision 1.74 2004/12/09 22:49:15 ksekar + Wide-Area Goodbyes broken + +Revision 1.73 2004/12/07 22:49:06 cheshire + BIND doesn't like zero-length rdata + +Revision 1.72 2004/12/06 21:15:20 ksekar + mDNSResponder crashed in CheckServiceRegistrations + +Revision 1.71 2004/12/04 02:12:45 cheshire + mDNSResponder puts LargeCacheRecord on the stack + +Revision 1.70 2004/12/03 19:52:44 ksekar +Use PutResourceRecordTTLJumbo for putDeletionRecord() + +Revision 1.69 2004/12/03 07:20:50 ksekar + Wide-Area: Registration of large TXT record fails + +Revision 1.68 2004/11/24 00:10:43 cheshire + For unicast operations, verify that service types are legal + +Revision 1.67 2004/10/26 03:52:02 cheshire +Update checkin comments + +Revision 1.66 2004/10/23 01:16:00 cheshire + uDNS operations not always reliable on multi-homed hosts + +Revision 1.65 2004/10/20 02:15:09 cheshire +Add case in GetRRDisplayString() to display NS rdata + +Revision 1.64 2004/10/13 00:24:02 cheshire +Disable "array is too small to include a terminating null character" warning on Windows + +Revision 1.63 2004/10/10 06:57:14 cheshire +Change definition of "localdomain" to make code compile a little smaller + +Revision 1.62 2004/10/06 01:44:19 cheshire + Resolving too quickly sometimes returns stale TXT record + +Revision 1.61 2004/09/30 00:24:56 ksekar + Dynamically update default registration domains on config change + +Revision 1.60 2004/09/27 23:25:30 cheshire +Fix compiler warning: soa.serial is signed, not unsigned + +Revision 1.59 2004/09/27 22:53:45 ksekar +Fixed getLargeResourceRecord for SOA rdata. + +Revision 1.58 2004/09/25 02:41:39 cheshire + Deliver near-pending "remove" events before new "add" events + +Revision 1.57 2004/09/25 02:24:27 cheshire +Removed unused rr->UseCount + +Revision 1.56 2004/09/24 20:57:39 cheshire + Eliminate inappropriate casts that cause misaligned-address errors + +Revision 1.55 2004/09/17 01:08:48 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.54 2004/09/17 00:49:51 cheshire +Get rid of now-unused GetResourceRecord -- the correct (safe) routine to use +is GetLargeResourceRecord + +Revision 1.53 2004/09/17 00:31:51 cheshire +For consistency with ipv6, renamed rdata field 'ip' to 'ipv4' + +Revision 1.52 2004/09/17 00:19:10 cheshire +For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4 + +Revision 1.51 2004/09/16 02:29:39 cheshire +Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around +uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService + +Revision 1.50 2004/09/16 01:58:14 cheshire +Fix compiler warnings + +Revision 1.49 2004/09/14 23:42:35 cheshire + Need to seed random number generator from platform-layer data + +Revision 1.48 2004/09/14 23:27:46 cheshire +Fix compile errors + +Revision 1.47 2004/08/25 02:50:04 cheshire + Browses are no longer piggybacking on other browses +Make mDNSSameAddress() recognise that two mDNSAddrType_None addresses are necessarily equal + +Revision 1.46 2004/08/18 17:35:40 ksekar +: Feature #9586: Need support for Legacy NAT gateways + +Revision 1.45 2004/08/15 18:26:00 cheshire +Don't use strcpy() on "struct domainname" objects; use AssignDomainName() instead +(A "struct domainname" is a collection of packed pascal strings, not a C string.) + +Revision 1.44 2004/08/13 23:46:58 cheshire +"asyncronous" -> "asynchronous" + +Revision 1.43 2004/08/12 02:55:46 ksekar +Fix param order error moving putPrereqNameNotInUse from uDNS.c using +ustrcpy macro to DNSCommon.c using mDNSPlatformStrCopy(). + +Revision 1.42 2004/08/10 23:19:14 ksekar +: DNS Extension daemon for Wide Area Rendezvous +Moved routines/constants to allow extern access for garbage collection daemon + +Revision 1.41 2004/08/10 01:10:01 cheshire + Current method of doing subtypes causes name collisions +Minor revision from Roger Pantos + +Revision 1.40 2004/08/04 22:10:46 cheshire + Current method of doing subtypes causes name collisions +Change to use "._sub." instead of ".s." to mark subtypes. + +Revision 1.39 2004/07/13 21:24:24 rpantos +Fix for . + +Revision 1.38 2004/06/18 21:08:58 cheshire + Applications are registering invalid records +Attempts to create domain names like "www..apple.com." now logged to aid debugging + +Revision 1.37 2004/06/18 20:25:42 cheshire + Add a syslog message if someone tries to use "local.arpa". + +Revision 1.36 2004/06/18 19:09:59 cheshire + Current method of doing subtypes causes name collisions + Revision 1.35 2004/06/05 00:14:44 cheshire Fix signed/unsigned and other compiler warnings @@ -105,9 +232,9 @@ Revision 1.11 2004/02/03 22:35:34 cheshire : Should not allow empty string for resolve domain Revision 1.10 2004/02/03 19:47:36 ksekar -Added an asyncronous state machine mechanism to uDNS.c, including +Added an asynchronous state machine mechanism to uDNS.c, including calls to find the parent zone for a domain name. Changes include code -in repository previously dissabled via "#if 0 //incomplete". Codepath +in repository previously dissabled via "#if 0 incomplete". Codepath is currently unused, and will be called to create update records, etc. Revision 1.9 2004/01/27 20:15:22 cheshire @@ -142,7 +269,7 @@ Revision 1.1 2003/12/13 03:05:27 ksekar */ -// Set mDNS_InstantiateInlines to tell mDNSClientAPI.h to instantiate inline functions, if necessary +// Set mDNS_InstantiateInlines to tell mDNSEmbeddedAPI.h to instantiate inline functions, if necessary #define mDNS_InstantiateInlines 1 #include "DNSCommon.h" @@ -152,6 +279,9 @@ Revision 1.1 2003/12/13 03:05:27 ksekar // Otherwise, this generates warnings for the perfectly natural construct "while(1)" // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know #pragma warning(disable:4127) + // Disable "array is too small to include a terminating null character" warning + // -- domain labels have an initial length byte, not a terminating null character + #pragma warning(disable:4295) #endif // *************************************************************************** @@ -169,7 +299,7 @@ mDNSexport DNameListElem *mDNS_CopyDNameList(const DNameListElem *orig) { newelem = (DNameListElem*)mDNSPlatformMemAllocate(sizeof(DNameListElem)); if (!newelem) { LogMsg("ERROR: malloc"); return mDNSNULL; } - mDNSPlatformStrCopy(ptr->name.c, newelem->name.c); + AssignDomainName(newelem->name, ptr->name); newelem->next = copy; copy = newelem; } @@ -194,6 +324,19 @@ mDNSexport void mDNS_FreeDNameList(DNameListElem *list) #pragma mark - General Utility Functions #endif +// return true for RFC1918 private addresses +mDNSexport mDNSBool IsPrivateV4Addr(mDNSAddr *addr) + { + mDNSu8 *b; + + if (addr->type != mDNSAddrType_IPv4) return mDNSfalse; + b = addr->ip.v4.b; + + return ((b[0] == 10) || // 10/8 prefix + (b[0] == 172 && b[1] > 15 && b[1] < 32) || // 172.16/12 + (b[0] == 192 && b[1] == 168)); // 192.168/16 + } + mDNSexport const NetworkInterfaceInfo *GetFirstActiveInterface(const NetworkInterfaceInfo *intf) { while (intf && !intf->InterfaceActive) intf = intf->next; @@ -237,24 +380,27 @@ mDNSexport char *DNSTypeName(mDNSu16 rrtype) } } -mDNSexport char *GetRRDisplayString_rdb(mDNS *const m, const ResourceRecord *rr, RDataBody *rd) +mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *rr, RDataBody *rd, char *buffer) { - char *ptr = m->MsgBuffer; - mDNSu32 length = mDNS_snprintf(m->MsgBuffer, 79, "%4d %##s %s ", rr->rdlength, rr->name.c, DNSTypeName(rr->rrtype)); + char *ptr = buffer; + mDNSu32 length = mDNS_snprintf(buffer, 79, "%4d %##s %s ", rr->rdlength, rr->name.c, DNSTypeName(rr->rrtype)); switch (rr->rrtype) { - case kDNSType_A: mDNS_snprintf(m->MsgBuffer+length, 79-length, "%.4a", &rd->ip); break; + case kDNSType_A: mDNS_snprintf(buffer+length, 79-length, "%.4a", &rd->ipv4); break; + + case kDNSType_NS: // Same as PTR case kDNSType_CNAME:// Same as PTR - case kDNSType_PTR: mDNS_snprintf(m->MsgBuffer+length, 79-length, "%##s", &rd->name); break; + case kDNSType_PTR: mDNS_snprintf(buffer+length, 79-length, "%##s", rd->name.c); break; + case kDNSType_HINFO:// Display this the same as TXT (just show first string) - case kDNSType_TXT: mDNS_snprintf(m->MsgBuffer+length, 79-length, "%#s", rd->txt.c); break; - case kDNSType_AAAA: mDNS_snprintf(m->MsgBuffer+length, 79-length, "%.16a", &rd->ipv6); break; - case kDNSType_SRV: mDNS_snprintf(m->MsgBuffer+length, 79-length, "%##s", &rd->srv.target); break; - default: mDNS_snprintf(m->MsgBuffer+length, 79-length, "RDLen %d: %s", - rr->rdlength, rd->data); break; + case kDNSType_TXT: mDNS_snprintf(buffer+length, 79-length, "%#s", rd->txt.c); break; + + case kDNSType_AAAA: mDNS_snprintf(buffer+length, 79-length, "%.16a", &rd->ipv6); break; + case kDNSType_SRV: mDNS_snprintf(buffer+length, 79-length, "%##s", rd->srv.target.c); break; + default: mDNS_snprintf(buffer+length, 79-length, "RDLen %d: %s", rr->rdlength, rd->data); break; } - for (ptr = m->MsgBuffer; *ptr; ptr++) if (*ptr < ' ') *ptr='.'; - return(m->MsgBuffer); + for (ptr = buffer; *ptr; ptr++) if (*ptr < ' ') *ptr='.'; + return(buffer); } mDNSexport mDNSu32 mDNSRandom(mDNSu32 max) @@ -262,7 +408,12 @@ mDNSexport mDNSu32 mDNSRandom(mDNSu32 max) static mDNSu32 seed = 0; mDNSu32 mask = 1; - if (!seed) seed = (mDNSu32)mDNSPlatformTimeNow(); + if (!seed) + { + int i; + seed = mDNSPlatformRandomSeed(); // Pick an initial seed + for (i=0; i<100; i++) seed = seed * 21 + 1; // And mix it up a bit + } while (mask < max) mask = (mask << 1) | 1; do seed = seed * 21 + 1; while ((seed & mask) > max); return (seed & mask); @@ -274,6 +425,7 @@ mDNSexport mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2) { switch (ip1->type) { + case mDNSAddrType_None : return(mDNStrue); // Empty addresses have no data and are therefore always equal case mDNSAddrType_IPv4 : return(mDNSBool)(mDNSSameIPv4Address(ip1->ip.v4, ip2->ip.v4)); case mDNSAddrType_IPv6 : return(mDNSBool)(mDNSSameIPv6Address(ip1->ip.v6, ip2->ip.v6)); } @@ -285,7 +437,7 @@ mDNSexport mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip) { switch(ip->type) { - case mDNSAddrType_IPv4: return(mDNSBool)(ip->ip.v4.NotAnInteger == AllDNSLinkGroup.NotAnInteger); + case mDNSAddrType_IPv4: return(mDNSBool)(ip->ip.v4.NotAnInteger == AllDNSLinkGroupv4.NotAnInteger); case mDNSAddrType_IPv6: return(mDNSBool)(ip->ip.v6.l[0] == AllDNSLinkGroupv6.l[0] && ip->ip.v6.l[1] == AllDNSLinkGroupv6.l[1] && ip->ip.v6.l[2] == AllDNSLinkGroupv6.l[2] && @@ -429,13 +581,15 @@ mDNSexport mDNSu8 *AppendLiteralLabelString(domainname *const name, const char * // in the domainname bufer (i.e., the next byte after the terminating zero). // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes) // AppendDNSNameString returns mDNSNULL. -mDNSexport mDNSu8 *AppendDNSNameString(domainname *const name, const char *cstr) +mDNSexport mDNSu8 *AppendDNSNameString(domainname *const name, const char *cstring) { + const char *cstr = cstring; mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero) while (*cstr && ptr < lim) // While more characters, and space to put them... { mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go + if (*cstr == '.') { LogMsg("AppendDNSNameString: Illegal empty label in name \"%s\"", cstring); return(mDNSNULL); } while (*cstr && *cstr != '.' && ptr < lim) // While we have characters in the label... { mDNSu8 c = (mDNSu8)*cstr++; // Read the character @@ -605,15 +759,6 @@ mDNSexport void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], do hostlabel->c[0] = (mDNSu8)(ptr - &hostlabel->c[1]); } -#if MDNS_ENFORCE_SERVICE_TYPE_LENGTH -mDNSlocal mDNSBool AllowedServiceNameException(const mDNSu8 *const src) - { - if (SameDomainLabel(src, (mDNSu8*)"\x12_MacOSXDupSuppress")) return(mDNStrue); - LogMsg("Application protocol name %#s too long; see ", src); - return(mDNSfalse); - } -#endif - mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn, const domainlabel *name, const domainname *type, const domainname *const domain) { @@ -624,15 +769,31 @@ mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn, // In the case where there is no name (and ONLY in that case), // a single-label subtype is allowed as the first label of a three-part "type" - if (!name) + if (!name && type) { - const mDNSu8 *s2 = type->c + 1 + type->c[0]; - if (type->c[0] > 0 && type->c[0] < 0x40 && - s2[0] > 0 && s2[0] < 0x40 && - s2[1+s2[0]] > 0 && s2[1+s2[0]] < 0x40) + const mDNSu8 *s0 = type->c; + if (s0[0] && s0[0] < 0x40) // If legal first label (at least one character, and no more than 63) { - name = (domainlabel *)type; - type = (domainname *)s2; + const mDNSu8 * s1 = s0 + 1 + s0[0]; + if (s1[0] && s1[0] < 0x40) // and legal second label (at least one character, and no more than 63) + { + const mDNSu8 *s2 = s1 + 1 + s1[0]; + if (s2[0] && s2[0] < 0x40 && s2[1+s2[0]] == 0) // and we have three and only three labels + { + static const mDNSu8 SubTypeLabel[5] = "\x04_sub"; + src = s0; // Copy the first label + len = *src; + for (i=0; i <= len; i++) *dst++ = *src++; + for (i=0; i < (int)sizeof(SubTypeLabel); i++) *dst++ = SubTypeLabel[i]; + type = (domainname *)s1; + + // Special support for queries done by older versions of "Rendezvous Browser" + // For these queries, we retract the "._sub" we just added between the subtype and the main type + if (SameDomainName((domainname*)s0, (domainname*)"\x09_services\x07_dns-sd\x04_udp") || + SameDomainName((domainname*)s0, (domainname*)"\x09_services\x05_mdns\x04_udp")) + dst -= sizeof(SubTypeLabel); + } + } } } @@ -648,13 +809,11 @@ mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn, src = type->c; // Put the service type into the domain name len = *src; -#if MDNS_ENFORCE_SERVICE_TYPE_LENGTH - if (len < 2 || len > 15) - if (!AllowedServiceNameException(src)) // If length not legal, check our grandfather-exceptions list - { errormsg="Application protocol name must be underscore plus 1-14 characters"; goto fail; } -#else - if (len < 2 || len >= 0x40) { errormsg="Application protocol name should be underscore plus 1-14 characters"; goto fail; } -#endif + if (len < 2 || len >= 0x40 || (len > 15 && !SameDomainName(domain, (domainname*)"\x05" "local"))) + { + errormsg="Application protocol name must be underscore plus 1-14 characters. See "; + goto fail; + } if (src[1] != '_') { errormsg="Application protocol name must begin with underscore"; goto fail; } for (i=2; i<=len; i++) if (!mdnsIsLetter(src[i]) && !mdnsIsDigit(src[i]) && src[i] != '-' && src[i] != '_') @@ -665,13 +824,15 @@ mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn, if (!(len == 4 && src[1] == '_' && (((src[2] | 0x20) == 'u' && (src[3] | 0x20) == 'd') || ((src[2] | 0x20) == 't' && (src[3] | 0x20) == 'c')) && (src[4] | 0x20) == 'p')) - { errormsg="Service transport protocol name must be _udp or _tcp"; goto fail; } + { errormsg="Transport protocol name must be _udp or _tcp"; goto fail; } for (i=0; i<=len; i++) *dst++ = *src++; if (*src) { errormsg="Service type must have only two labels"; goto fail; } *dst = 0; if (!domain->c[0]) { errormsg="Service domain must be non-empty"; goto fail; } + if (SameDomainName(domain, (domainname*)"\x05" "local" "\x04" "arpa")) + { errormsg="Illegal domain \"local.arpa.\" Use \"local.\" (or empty string)"; goto fail; } dst = AppendDomainName(fqdn, domain); if (!dst) { errormsg="Service domain too long"; goto fail; } return(dst); @@ -864,6 +1025,14 @@ mDNSexport mDNSBool SameRData(const ResourceRecord *const r1, const ResourceReco } } +mDNSexport mDNSBool SameResourceRecord(ResourceRecord *r1, ResourceRecord *r2) + { + return (r1->namehash == r2->namehash && + r1->rrtype == r2->rrtype && + SameDomainName(&r1->name, &r2->name) && + SameRData(r1, r2)); + } + mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) { if (rr->InterfaceID && @@ -878,11 +1047,11 @@ mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr mDNSexport mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate) { - RDataBody *rd = &rr->rdata->u; + const RDataBody *rd = &rr->rdata->u; const domainname *const name = estimate ? &rr->name : mDNSNULL; switch (rr->rrtype) { - case kDNSType_A: return(sizeof(rd->ip)); + case kDNSType_A: return(sizeof(rd->ipv4)); case kDNSType_CNAME:// Same as PTR case kDNSType_NS: // Same as PTR case kDNSType_PTR: return(CompressedDomainNameLength(&rd->name, name)); @@ -903,6 +1072,11 @@ mDNSexport mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate mDNSexport mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd) { mDNSu16 len; + // Some (or perhaps all) versions of BIND named (name daemon) don't allow updates + // with zero-length rdata, so for consistency we don't allow them for mDNS either. + // Otherwise we risk having applications that work with mDNS but not with uDNS. + if (!rdlength) return(mDNSfalse); + switch(rrtype) { case kDNSType_A: return(rdlength == sizeof(mDNSv4Addr)); @@ -1067,6 +1241,15 @@ mDNSlocal mDNSu8 *putVal16(mDNSu8 *ptr, mDNSu16 val) return ptr + sizeof(mDNSOpaque16); } +mDNSlocal mDNSu8 *putVal32(mDNSu8 *ptr, mDNSu32 val) + { + ptr[0] = (mDNSu8)((val >> 24) & 0xFF); + ptr[1] = (mDNSu8)((val >> 16) & 0xFF); + ptr[2] = (mDNSu8)((val >> 8) & 0xFF); + ptr[3] = (mDNSu8)((val ) & 0xFF); + return ptr + sizeof(mDNSu32); + } + mDNSlocal mDNSu8 *putOptRData(mDNSu8 *ptr, const mDNSu8 *limit, ResourceRecord *rr) { int nput = 0; @@ -1088,15 +1271,13 @@ mDNSlocal mDNSu8 *putOptRData(mDNSu8 *ptr, const mDNSu8 *limit, ResourceRecord * ptr = putVal16(ptr, opt->OptData.llq.err); mDNSPlatformMemCopy(opt->OptData.llq.id, ptr, 8); // 8-byte id ptr += 8; - *(mDNSOpaque32 *)ptr = mDNSOpaque32fromIntVal(opt->OptData.llq.lease); - ptr += sizeof(mDNSOpaque32); + ptr = putVal32(ptr, opt->OptData.llq.lease); nput += sizeof(LLQOptData); } else if (opt->opt == kDNSOpt_Lease) { if (ptr + sizeof(mDNSs32) > limit) goto space_err; - *(mDNSOpaque32 *)ptr = mDNSOpaque32fromIntVal(opt->OptData.lease); - ptr += sizeof(mDNSs32); + ptr = putVal32(ptr, opt->OptData.lease); nput += sizeof(mDNSs32); } else { LogMsg("putOptRData - unknown option %d", opt->opt); return mDNSNULL; } @@ -1174,10 +1355,10 @@ mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNS return(mDNSNULL); } if (ptr + 4 > limit) return(mDNSNULL); - *ptr++ = rr->rdata->u.ip.b[0]; - *ptr++ = rr->rdata->u.ip.b[1]; - *ptr++ = rr->rdata->u.ip.b[2]; - *ptr++ = rr->rdata->u.ip.b[3]; + *ptr++ = rr->rdata->u.ipv4.b[0]; + *ptr++ = rr->rdata->u.ipv4.b[1]; + *ptr++ = rr->rdata->u.ipv4.b[2]; + *ptr++ = rr->rdata->u.ipv4.b[3]; return(ptr); case kDNSType_CNAME:// Same as PTR @@ -1212,16 +1393,10 @@ mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNS } } -mDNSexport mDNSu8 *PutResourceRecordTTL(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl) +mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit) { mDNSu8 *endofrdata; mDNSu16 actualLength; - const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData; - - // If we have a single large record to put in the packet, then we allow the packet to be up to 9K bytes, - // but in the normal case we try to keep the packets below 1500 to avoid IP fragmentation on standard Ethernet - if (msg->h.numAnswers || msg->h.numAuthorities || msg->h.numAdditionals) - limit = msg->data + NormalMaxDNSMessageData; if (rr->RecordType == kDNSRecordTypeUnregistered) { @@ -1287,6 +1462,90 @@ mDNSexport mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 return(ptr+4); } +// for dynamic updates +mDNSexport mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, const domainname *zone, mDNSOpaque16 zoneClass) + { + ptr = putDomainNameAsLabels(msg, ptr, limit, zone); + if (!ptr || ptr + 4 > limit) return mDNSNULL; // If we're out-of-space, return NULL + *ptr++ = (mDNSu8)(kDNSType_SOA >> 8); + *ptr++ = (mDNSu8)(kDNSType_SOA & 0xFF); + *ptr++ = zoneClass.b[0]; + *ptr++ = zoneClass.b[1]; + msg->h.mDNS_numZones++; + return ptr; + } + +// for dynamic updates +mDNSexport mDNSu8 *putPrereqNameNotInUse(domainname *name, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *end) + { + AuthRecord prereq; + + mDNSPlatformMemZero(&prereq, sizeof(AuthRecord)); + AssignDomainName(prereq.resrec.name, *name); + prereq.resrec.rrtype = kDNSQType_ANY; + prereq.resrec.rrclass = kDNSClass_NONE; + ptr = putEmptyResourceRecord(msg, ptr, end, &msg->h.mDNS_numPrereqs, &prereq); + return ptr; + } + +// for dynamic updates +mDNSexport mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr) + { + mDNSu16 origclass; + // deletion: specify record w/ TTL 0, class NONE + + origclass = rr->rrclass; + rr->rrclass = kDNSClass_NONE; + ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0); + rr->rrclass = origclass; + return ptr; + } + +// for dynamic updates +mDNSexport mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domainname *name) + { + const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;; + mDNSu16 class = kDNSQClass_ANY; + mDNSu16 rrtype = kDNSQType_ANY; + + ptr = putDomainNameAsLabels(msg, ptr, limit, name); + if (!ptr || ptr + 10 >= limit) return mDNSNULL; // If we're out-of-space, return mDNSNULL + ptr[0] = (mDNSu8)(rrtype >> 8); + ptr[1] = (mDNSu8)(rrtype & 0xFF); + ptr[2] = (mDNSu8)(class >> 8); + ptr[3] = (mDNSu8)(class & 0xFF); + ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl + ptr[8] = ptr[9] = 0; // zero rdlength/rdata + + msg->h.mDNS_numUpdates++; + return ptr + 10; + } + +// for dynamic updates +mDNSexport mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease) + { + AuthRecord rr; + ResourceRecord *opt = &rr.resrec; + rdataOpt *optRD; + + mDNSPlatformMemZero(&rr, sizeof(AuthRecord)); + opt->rdata = &rr.rdatastorage; + + opt->RecordType = kDNSRecordTypeKnownUnique; // to avoid warnings in other layers + opt->rrtype = kDNSType_OPT; + opt->rdlength = LEASE_OPT_SIZE; + opt->rdestimate = LEASE_OPT_SIZE; + + optRD = &rr.resrec.rdata->u.opt; + optRD->opt = kDNSOpt_Lease; + optRD->optlen = sizeof(mDNSs32); + optRD->OptData.lease = lease; + end = PutResourceRecordTTLJumbo(msg, end, &msg->h.numAdditionals, opt, 0); + if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL; } + + return end; + } + // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - @@ -1418,19 +1677,23 @@ mDNSexport const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 return(ptr + pktrdlength); } -mDNSexport const mDNSu8 *GetResourceRecord(mDNS *const m, const DNSMessage * const msg, const mDNSu8 *ptr, - const mDNSu8 * const end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, CacheRecord *rr, RData *RDataStorage) +mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage * const msg, const mDNSu8 *ptr, + const mDNSu8 *end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *largecr) { + CacheRecord *rr = &largecr->r; mDNSu16 pktrdlength; + + if (largecr == &m->rec && rr->resrec.RecordType) + LogMsg("GetLargeResourceRecord: m->rec appears to be already in use"); rr->next = mDNSNULL; rr->resrec.RecordType = RecordType; rr->NextInKAList = mDNSNULL; - rr->TimeRcvd = m->timenow; - rr->NextRequiredQuery = m->timenow; // Will be updated to the real value when we call SetNextCacheCheckTime() - rr->LastUsed = m->timenow; - rr->UseCount = 0; + rr->TimeRcvd = m ? m->timenow : 0; + rr->DelayDelivery = 0; + rr->NextRequiredQuery = m ? m->timenow : 0; // Will be updated to the real value when we call SetNextCacheCheckTime() + rr->LastUsed = m ? m->timenow : 0; rr->CRActiveQuestion = mDNSNULL; rr->UnansweredQueries = 0; rr->LastUnansweredTime= 0; @@ -1458,21 +1721,19 @@ mDNSexport const mDNSu8 *GetResourceRecord(mDNS *const m, const DNSMessage * con rr->resrec.RecordType |= kDNSRecordTypePacketUniqueMask; ptr += 10; if (ptr + pktrdlength > end) { debugf("GetResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); } + end = ptr + pktrdlength; // Adjust end to indicate the end of the rdata for this resource record - if (RDataStorage) - rr->resrec.rdata = RDataStorage; - else - { - rr->resrec.rdata = (RData*)&rr->rdatastorage; - rr->resrec.rdata->MaxRDLength = sizeof(RDataBody); - } + rr->resrec.rdata = (RData*)&rr->rdatastorage; + rr->resrec.rdata->MaxRDLength = MaximumRDSize; + + if (!RecordType) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr->resrec.name.c); switch (rr->resrec.rrtype) { - case kDNSType_A: rr->resrec.rdata->u.ip.b[0] = ptr[0]; - rr->resrec.rdata->u.ip.b[1] = ptr[1]; - rr->resrec.rdata->u.ip.b[2] = ptr[2]; - rr->resrec.rdata->u.ip.b[3] = ptr[3]; + case kDNSType_A: rr->resrec.rdata->u.ipv4.b[0] = ptr[0]; + rr->resrec.rdata->u.ipv4.b[1] = ptr[1]; + rr->resrec.rdata->u.ipv4.b[2] = ptr[2]; + rr->resrec.rdata->u.ipv4.b[3] = ptr[3]; break; case kDNSType_CNAME:// Same as PTR @@ -1506,16 +1767,16 @@ mDNSexport const mDNSu8 *GetResourceRecord(mDNS *const m, const DNSMessage * con //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.srv.target.c, pktrdlength); break; - case kDNSType_SOA: if (!getDomainName(msg, ptr, end, &rr->resrec.rdata->u.soa.mname) || - !getDomainName(msg, ptr, end, &rr->resrec.rdata->u.soa.rname)) - { debugf("GetResourceRecord: Malformed SOA RDATA mname/rname"); return mDNSNULL; } - if ((unsigned)(end - ptr) < 5 * sizeof(mDNSOpaque32)) - { debugf("GetResourceRecord: Malformed SOA RDATA"); return mDNSNULL; } - rr->resrec.rdata->u.soa.serial.NotAnInteger = ((mDNSOpaque32 *)ptr)->NotAnInteger; ptr += 4; - rr->resrec.rdata->u.soa.refresh.NotAnInteger = ((mDNSOpaque32 *)ptr)->NotAnInteger; ptr += 4; - rr->resrec.rdata->u.soa.retry.NotAnInteger = ((mDNSOpaque32 *)ptr)->NotAnInteger; ptr += 4; - rr->resrec.rdata->u.soa.expire.NotAnInteger = ((mDNSOpaque32 *)ptr)->NotAnInteger; ptr += 4; - rr->resrec.rdata->u.soa.min.NotAnInteger = ((mDNSOpaque32 *)ptr)->NotAnInteger; + case kDNSType_SOA: ptr = getDomainName(msg, ptr, end, &rr->resrec.rdata->u.soa.mname); + if (!ptr) { debugf("GetResourceRecord: Malformed SOA RDATA mname"); return mDNSNULL; } + ptr = getDomainName(msg, ptr, end, &rr->resrec.rdata->u.soa.rname); + if (!ptr) { debugf("GetResourceRecord: Malformed SOA RDATA rname"); return mDNSNULL; } + if (ptr + 0x14 != end) { debugf("GetResourceRecord: Malformed SOA RDATA"); return mDNSNULL; } + rr->resrec.rdata->u.soa.serial = (mDNSs32) ((mDNSs32)ptr[0x00] << 24 | (mDNSs32)ptr[0x01] << 16 | (mDNSs32)ptr[0x02] << 8 | ptr[0x03]); + rr->resrec.rdata->u.soa.refresh = (mDNSu32) ((mDNSu32)ptr[0x04] << 24 | (mDNSu32)ptr[0x05] << 16 | (mDNSu32)ptr[0x06] << 8 | ptr[0x07]); + rr->resrec.rdata->u.soa.retry = (mDNSu32) ((mDNSu32)ptr[0x08] << 24 | (mDNSu32)ptr[0x09] << 16 | (mDNSu32)ptr[0x0A] << 8 | ptr[0x0B]); + rr->resrec.rdata->u.soa.expire = (mDNSu32) ((mDNSu32)ptr[0x0C] << 24 | (mDNSu32)ptr[0x0D] << 16 | (mDNSu32)ptr[0x0E] << 8 | ptr[0x0F]); + rr->resrec.rdata->u.soa.min = (mDNSu32) ((mDNSu32)ptr[0x10] << 24 | (mDNSu32)ptr[0x11] << 16 | (mDNSu32)ptr[0x12] << 8 | ptr[0x13]); break; case kDNSType_OPT: getOptRdata(ptr, end, &rr->resrec, pktrdlength); break; @@ -1597,7 +1858,7 @@ mDNSexport const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mD #pragma mark - Packet Sending Functions #endif -mDNSlocal mStatus sendDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end, +mDNSexport mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end, mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport, int sd, uDNS_AuthInfo *authInfo) { mStatus status; @@ -1654,31 +1915,96 @@ mDNSlocal mStatus sendDNSMessage(const mDNS *const m, DNSMessage *const msg, mDN return(status); tcp_error: - LogMsg("sendDNSMessage: error sending message over tcp"); + LogMsg("mDNSSendDNSMessage: error sending message over tcp"); return mStatus_UnknownErr; - } -mDNSexport mStatus mDNSSendDNSMessage_tcp(const mDNS *const m, DNSMessage *const msg, mDNSu8 * end, int sd) - { - if (sd < 0) { LogMsg("mDNSSendDNSMessage_tcp: invalid desciptor %d", sd); return mStatus_UnknownErr; } - return sendDNSMessage(m, msg, end, mDNSInterface_Any, &zeroAddr, zeroIPPort, sd, mDNSNULL); - } +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - RR List Management & Task Management +#endif -mDNSexport mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 * end, - mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport) +mDNSexport void mDNS_Lock(mDNS *const m) { - return sendDNSMessage(m, msg, end, InterfaceID, dst, dstport, -1, mDNSNULL); + // MUST grab the platform lock FIRST! + mDNSPlatformLock(m); + + // Normally, mDNS_reentrancy is zero and so is mDNS_busy + // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too + // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one + // If mDNS_busy != mDNS_reentrancy that's a bad sign + if (m->mDNS_busy != m->mDNS_reentrancy) + LogMsg("mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + + // If this is an initial entry into the mDNSCore code, set m->timenow + // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set + if (m->mDNS_busy == 0) + { + if (m->timenow) + LogMsg("mDNS_Lock: m->timenow already set (%ld/%ld)", m->timenow, mDNSPlatformRawTime() + m->timenow_adjust); + m->timenow = mDNSPlatformRawTime() + m->timenow_adjust; + if (m->timenow == 0) m->timenow = 1; + } + else if (m->timenow == 0) + { + LogMsg("mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", m->mDNS_busy); + m->timenow = mDNSPlatformRawTime() + m->timenow_adjust; + if (m->timenow == 0) m->timenow = 1; + } + + if (m->timenow_last - m->timenow > 0) + { + m->timenow_adjust += m->timenow_last - m->timenow; + LogMsg("mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", m->timenow_last - m->timenow, m->timenow_adjust); + m->timenow = m->timenow_last; + } + m->timenow_last = m->timenow; + + // Increment mDNS_busy so we'll recognise re-entrant calls + m->mDNS_busy++; } -mDNSexport mStatus mDNSSendSignedDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 * end, - mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport, uDNS_AuthInfo *authInfo) +mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m) { - return sendDNSMessage(m, msg, end, InterfaceID, dst, dstport, -1, authInfo); + mDNSs32 e = m->timenow + 0x78000000; + if (m->mDNSPlatformStatus != mStatus_NoError || m->SleepState) return(e); + if (m->NewQuestions) + { + if (m->NewQuestions->DelayAnswering) e = m->NewQuestions->DelayAnswering; + else return(m->timenow); + } + if (m->NewLocalOnlyQuestions) return(m->timenow); + if (m->NewLocalOnlyRecords) return(m->timenow); + if (m->DiscardLocalOnlyRecords) return(m->timenow); + if (m->SuppressSending) return(m->SuppressSending); +#ifndef UNICAST_DISABLED + if (e - m->uDNS_info.nextevent > 0) e = m->uDNS_info.nextevent; +#endif + if (e - m->NextCacheCheck > 0) e = m->NextCacheCheck; + if (e - m->NextScheduledQuery > 0) e = m->NextScheduledQuery; + if (e - m->NextScheduledProbe > 0) e = m->NextScheduledProbe; + if (e - m->NextScheduledResponse > 0) e = m->NextScheduledResponse; + return(e); } -mDNSexport mStatus mDNSSendSignedDNSMessage_tcp(const mDNS *const m, DNSMessage *const msg, mDNSu8 * end, int sd, uDNS_AuthInfo *authInfo) +mDNSexport void mDNS_Unlock(mDNS *const m) { - if (sd < 0) { LogMsg("mDNSSendDNSMessage_tcp: invalid desciptor %d", sd); return mStatus_UnknownErr; } - return sendDNSMessage(m, msg, end, mDNSInterface_Any, &zeroAddr, zeroIPPort, sd, authInfo); + // Decrement mDNS_busy + m->mDNS_busy--; + + // Check for locking failures + if (m->mDNS_busy != m->mDNS_reentrancy) + LogMsg("mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + + // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow + if (m->mDNS_busy == 0) + { + m->NextScheduledEvent = GetNextScheduledEvent(m); + if (m->timenow == 0) LogMsg("mDNS_Unlock: ERROR! m->timenow aready zero"); + m->timenow = 0; + } + + // MUST release the platform lock LAST! + mDNSPlatformUnlock(m); } diff --git a/mDNSCore/DNSCommon.h b/mDNSCore/DNSCommon.h index 674bb7a..5466501 100644 --- a/mDNSCore/DNSCommon.h +++ b/mDNSCore/DNSCommon.h @@ -3,8 +3,6 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -25,6 +23,54 @@ Change History (most recent first): $Log: DNSCommon.h,v $ +Revision 1.28 2004/12/06 21:15:22 ksekar + mDNSResponder crashed in CheckServiceRegistrations + +Revision 1.27 2004/12/03 07:20:50 ksekar + Wide-Area: Registration of large TXT record fails + +Revision 1.26 2004/12/03 05:18:33 ksekar + mDNSResponder needs to return more specific TSIG errors + +Revision 1.25 2004/10/26 03:52:02 cheshire +Update checkin comments + +Revision 1.24 2004/10/23 01:16:00 cheshire + uDNS operations not always reliable on multi-homed hosts + +Revision 1.23 2004/10/03 23:18:58 cheshire +Move address comparison macros from DNSCommon.h to mDNSEmbeddedAPI.h + +Revision 1.22 2004/09/30 00:24:56 ksekar + Dynamically update default registration domains on config change + +Revision 1.21 2004/09/17 01:08:48 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.20 2004/09/17 00:49:51 cheshire +Get rid of now-unused GetResourceRecord -- the correct (safe) routine to use +is GetLargeResourceRecord + +Revision 1.19 2004/09/16 21:59:15 cheshire +For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr + +Revision 1.18 2004/09/16 02:29:39 cheshire +Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around +uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService + +Revision 1.17 2004/09/14 23:27:46 cheshire +Fix compile errors + +Revision 1.16 2004/08/13 23:46:58 cheshire +"asyncronous" -> "asynchronous" + +Revision 1.15 2004/08/10 23:19:14 ksekar +: DNS Extension daemon for Wide Area Rendezvous +Moved routines/constants to allow extern access for garbage collection daemon + Revision 1.14 2004/05/28 23:42:36 ksekar : Feature: DNS server->client notification on record changes (#7805) @@ -48,16 +94,16 @@ Basic Dynamic Update support via mDNS_Register (dissabled via UNICAST_REGISTRATION #define) Revision 1.7 2004/02/03 19:47:36 ksekar -Added an asyncronous state machine mechanism to uDNS.c, including +Added an asynchronous state machine mechanism to uDNS.c, including calls to find the parent zone for a domain name. Changes include code -in repository previously dissabled via "#if 0 //incomplete". Codepath +in repository previously dissabled via "#if 0 incomplete". Codepath is currently unused, and will be called to create update records, etc. Revision 1.6 2004/01/27 20:15:22 cheshire : Time to prune obsolete code for listening on port 53 Revision 1.5 2004/01/24 03:40:56 cheshire -Move mDNSAddrIsDNSMulticast() from DNSCommon.h to mDNSClientAPI.h so clients can use it +Move mDNSAddrIsDNSMulticast() from DNSCommon.h to mDNSEmbeddedAPI.h so embedded clients can use it Revision 1.4 2004/01/24 03:38:27 cheshire Fix minor syntactic error: Headers should use "extern" declarations, not "mDNSexport" @@ -77,7 +123,7 @@ Revision 1.1 2003/12/13 03:05:27 ksekar #ifndef __DNSCOMMON_H_ #define __DNSCOMMON_H_ -#include "mDNSClientAPI.h" +#include "mDNSEmbeddedAPI.h" #ifdef __cplusplus extern "C" { @@ -127,6 +173,13 @@ typedef enum kDNSFlag1_RC_NotZone = 0x0A } DNS_Flags; +typedef enum + { + TSIG_ErrBadSig = 16, + TSIG_ErrBadKey = 17, + TSIG_ErrBadTime = 18 + } TSIG_ErrorCode; + // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - @@ -138,41 +191,18 @@ extern mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf extern mDNSu32 mDNSRandom(mDNSu32 max); -#define mDNSSameIPv4Address(A,B) ((A).NotAnInteger == (B).NotAnInteger) -#define mDNSSameIPv6Address(A,B) ((A).l[0] == (B).l[0] && (A).l[1] == (B).l[1] && (A).l[2] == (B).l[2] && (A).l[3] == (B).l[3]) - -#define mDNSIPv4AddressIsZero(A) mDNSSameIPv4Address((A), zeroIPAddr) -#define mDNSIPv6AddressIsZero(A) mDNSSameIPv6Address((A), zerov6Addr) - -#define mDNSIPv4AddressIsOnes(A) mDNSSameIPv4Address((A), onesIPv4Addr) -#define mDNSIPv6AddressIsOnes(A) mDNSSameIPv6Address((A), onesIPv6Addr) - -#define mDNSAddressIsZero(X) ( \ - ((X)->type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsZero((X)->ip.v4)) || \ - ((X)->type == mDNSAddrType_IPv6 && mDNSIPv6AddressIsZero((X)->ip.v6)) ) - -#define mDNSAddressIsOnes(X) ( \ - ((X)->type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsOnes((X)->ip.v4)) || \ - ((X)->type == mDNSAddrType_IPv6 && mDNSIPv6AddressIsOnes((X)->ip.v6)) ) - -#define mDNSAddressIsValid(X) ( \ - ((X)->type == mDNSAddrType_IPv4) ? !(mDNSIPv4AddressIsZero((X)->ip.v4) || mDNSIPv4AddressIsOnes((X)->ip.v4)) : \ - ((X)->type == mDNSAddrType_IPv6) ? !(mDNSIPv6AddressIsZero((X)->ip.v6) || mDNSIPv6AddressIsOnes((X)->ip.v6)) : mDNSfalse) - - // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - #pragma mark - Domain Name Utility Functions #endif - + #define mdnsIsDigit(X) ((X) >= '0' && (X) <= '9') #define mDNSIsUpperCase(X) ((X) >= 'A' && (X) <= 'Z') #define mDNSIsLowerCase(X) ((X) >= 'a' && (X) <= 'z') #define mdnsIsLetter(X) (mDNSIsUpperCase(X) || mDNSIsLowerCase(X)) - - + #define mdnsValidHostChar(X, notfirst, notlast) (mdnsIsLetter(X) || mdnsIsDigit(X) || ((notfirst) && (notlast) && (X) == '-') ) extern mDNSu16 CompressedDomainNameLength(const domainname *const name, const domainname *parent); @@ -196,7 +226,8 @@ extern mDNSBool SameRData(const ResourceRecord *const r1, const ResourceRecord * extern mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q); - +extern mDNSBool SameResourceRecord(ResourceRecord *r1, ResourceRecord *r2); + extern mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate); #define GetRRDomainNameTarget(RR) ( \ @@ -221,13 +252,32 @@ extern mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg, mDNSu8 *ptr, c extern mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, ResourceRecord *rr); -extern mDNSu8 *PutResourceRecordTTL(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl); +// If we have a single large record to put in the packet, then we allow the packet to be up to 9K bytes, +// but in the normal case we try to keep the packets below 1500 to avoid IP fragmentation on standard Ethernet +extern mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit); + +#define PutResourceRecordTTL(msg, ptr, count, rr, ttl) PutResourceRecordTTLWithLimit((msg), (ptr), (count), (rr), (ttl), \ + ((msg)->h.numAnswers || (msg)->h.numAuthorities || (msg)->h.numAdditionals) ? (msg)->data + NormalMaxDNSMessageData : (msg)->data + AbsoluteMaxDNSMessageData) + +#define PutResourceRecordTTLJumbo(msg, ptr, count, rr, ttl) PutResourceRecordTTLWithLimit((msg), (ptr), (count), (rr), (ttl), \ + (msg)->data + AbsoluteMaxDNSMessageData) + extern mDNSu8 *PutResourceRecordCappedTTL(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 maxttl); extern mDNSu8 *putEmptyResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, mDNSu16 *count, const AuthRecord *rr); extern mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass); + +extern mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, const domainname *zone, mDNSOpaque16 zoneClass); + +extern mDNSu8 *putPrereqNameNotInUse(domainname *name, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *end); + +extern mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr); + +extern mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domainname *name); + +extern mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease); #define PutResourceRecord(MSG, P, C, RR) PutResourceRecordTTL((MSG), (P), (C), (RR), (RR)->rroriginalttl) @@ -250,8 +300,8 @@ extern const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *pt extern const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end); -extern const mDNSu8 *GetResourceRecord(mDNS *const m, const DNSMessage * const msg, const mDNSu8 *ptr, - const mDNSu8 * const end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, CacheRecord *rr, RData *RDataStorage); +extern const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage * const msg, const mDNSu8 *ptr, + const mDNSu8 * const end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *largecr); extern const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end); @@ -264,9 +314,6 @@ extern const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDNSu8 extern const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 *const end); -#define GetLargeResourceRecord(m, msg, p, e, i, t, L) \ - (((L)->r.rdatastorage.MaxRDLength = MaximumRDSize), GetResourceRecord((m), (msg), (p), (e), (i), (t), &(L)->r, (RData*)&(L)->r.rdatastorage)) - // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - @@ -274,11 +321,17 @@ extern const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 #pragma mark - Packet Sending Functions #endif -extern mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 * end, mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport); -extern mStatus mDNSSendDNSMessage_tcp(const mDNS *const m, DNSMessage *const msg, mDNSu8 * end, int sd); -extern mStatus mDNSSendSignedDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end, - mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport, uDNS_AuthInfo *authInfo); -extern mStatus mDNSSendSignedDNSMessage_tcp(const mDNS *const m, DNSMessage *const msg, mDNSu8 * end, int sd, uDNS_AuthInfo *authInfo); +extern mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end, + mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport, int sd, uDNS_AuthInfo *authInfo); + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - RR List Management & Task Management +#endif + +extern void mDNS_Lock(mDNS *const m); +extern void mDNS_Unlock(mDNS *const m); #ifdef __cplusplus } diff --git a/mDNSCore/DNSDigest.c b/mDNSCore/DNSDigest.c index 370e706..55e304c 100644 --- a/mDNSCore/DNSDigest.c +++ b/mDNSCore/DNSDigest.c @@ -3,8 +3,6 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -25,6 +23,29 @@ Change History (most recent first): $Log: DNSDigest.c,v $ +Revision 1.12 2004/12/03 07:20:50 ksekar + Wide-Area: Registration of large TXT record fails + +Revision 1.11 2004/12/02 01:10:27 cheshire +Fix to compile cleanly on 64-bit x86 + +Revision 1.10 2004/11/01 20:36:04 ksekar + mDNSResponder should not receive Keychain Notifications + +Revision 1.9 2004/10/26 09:00:12 cheshire +Save a few bytes by creating HMAC_MD5_AlgName as a C string instead of a 256-byte object + +Revision 1.8 2004/09/17 01:08:48 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.7 2004/08/15 18:36:38 cheshire +Don't use strcpy() and strlen() on "struct domainname" objects; +use AssignDomainName() and DomainNameLength() instead +(A "struct domainname" is a collection of packed pascal strings, not a C string.) + Revision 1.6 2004/06/02 00:17:46 ksekar Referenced original OpenSSL license headers in source file description. @@ -53,7 +74,7 @@ Support for TSIG signed dynamic updates. extern "C" { #endif -#include "mDNSClientAPI.h" +#include "mDNSEmbeddedAPI.h" #include "DNSCommon.h" // Disable certain benign warnings with Microsoft compilers @@ -661,7 +682,7 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num); * Time for some action:-) */ -int HASH_UPDATE (HASH_CTX *c, const void *data_, mDNSu32 len) +int HASH_UPDATE (HASH_CTX *c, const void *data_, unsigned long len) { const unsigned char *data=(const unsigned char *)data_; register HASH_LONG * p; @@ -1297,13 +1318,10 @@ mDNSexport mDNSs32 DNSDigest_Base64ToBin(const char *src, mDNSu8 *target, mDNSu3 #define HMAC_OPAD 0x5c #define MD5_LEN 16 -static domainname HMAC_MD5_AlgName = { { '\010', 'h', 'm', 'a', 'c', '-', 'm', 'd', '5', - '\007', 's', 'i', 'g', '-', 'a', 'l', 'g', - '\003', 'r', 'e', 'g', - '\003', 'i', 'n', 't', - '\0' } }; +#define HMAC_MD5_AlgName (*(const domainname*) "\010" "hmac-md5" "\007" "sig-alg" "\003" "reg" "\003" "int") + // Adapted from Appendix, RFC 2104 -mDNSexport void DNSDigest_ConstructHMACKey(uDNS_AuthInfo *info, mDNSu8 *key, mDNSu32 len) +mDNSexport void DNSDigest_ConstructHMACKey(uDNS_AuthInfo *info, const mDNSu8 *key, mDNSu32 len) { MD5_CTX k; mDNSu8 buf[MD5_LEN]; @@ -1354,11 +1372,10 @@ mDNSexport mDNSu8 *DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, mDNSu16 // Construct TSIG RR, digesting variables as apporpriate mDNSPlatformMemZero(&tsig, sizeof(AuthRecord)); mDNS_SetupResourceRecord(&tsig, mDNSNULL, 0, kDNSType_TSIG, 0, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL); - rdata = tsig.resrec.rdata->u.data; // key name - mDNSPlatformStrCopy(info->keyname.c, tsig.resrec.name.c); - MD5_Update(&c, info->keyname.c, mDNSPlatformStrLen(info->keyname.c)+1); + AssignDomainName(tsig.resrec.name, info->keyname); + MD5_Update(&c, info->keyname.c, DomainNameLength(&info->keyname)); // class tsig.resrec.rrclass = kDNSQClass_ANY; @@ -1370,9 +1387,9 @@ mDNSexport mDNSu8 *DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, mDNSu16 MD5_Update(&c, (mDNSu8 *)&tsig.resrec.rroriginalttl, sizeof(tsig.resrec.rroriginalttl)); // alg name - mDNSPlatformStrCopy(HMAC_MD5_AlgName.c, rdata); - len = mDNSPlatformStrLen(HMAC_MD5_AlgName.c) + 1; - rdata += len; + AssignDomainName(tsig.resrec.rdata->u.name, HMAC_MD5_AlgName); + len = DomainNameLength(&HMAC_MD5_AlgName); + rdata = tsig.resrec.rdata->u.data + len; MD5_Update(&c, HMAC_MD5_AlgName.c, len); // time @@ -1392,7 +1409,8 @@ mDNSexport mDNSu8 *DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, mDNSu16 // fudge buf = mDNSOpaque16fromIntVal(300); // 300 sec is fudge recommended in RFC 2485 - ((mDNSOpaque16 *)rdata)->NotAnInteger = buf.NotAnInteger; + rdata[0] = buf.b[0]; + rdata[1] = buf.b[1]; rdata += sizeof(mDNSOpaque16); MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); @@ -1411,19 +1429,21 @@ mDNSexport mDNSu8 *DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, mDNSu16 MD5_Final(digest, &c); // set remaining rdata fields - *(mDNSOpaque16 *)rdata = mDNSOpaque16fromIntVal(MD5_LEN); // MAC size + rdata[0] = (mDNSu8)((MD5_LEN >> 8) & 0xff); + rdata[1] = (mDNSu8)( MD5_LEN & 0xff); rdata += sizeof(mDNSOpaque16); mDNSPlatformMemCopy(digest, rdata, MD5_LEN); // MAC rdata += MD5_LEN; - ((mDNSOpaque16 *)rdata)->NotAnInteger = msg->h.id.NotAnInteger; // original ID - rdata += sizeof(mDNSOpaque16); - ((mDNSOpaque16 *)rdata)->NotAnInteger = 0; // no error - rdata += sizeof(mDNSOpaque16); - ((mDNSOpaque16 *)rdata)->NotAnInteger = 0; // other data len - rdata += sizeof(mDNSOpaque16); + rdata[0] = msg->h.id.b[0]; // original ID + rdata[1] = msg->h.id.b[1]; + rdata[2] = 0; // no error + rdata[3] = 0; + rdata[4] = 0; // other data len + rdata[5] = 0; + rdata += 6; tsig.resrec.rdlength = (mDNSu16)(rdata - tsig.resrec.rdata->u.data); - *end = PutResourceRecordTTL(msg, ptr, numAdditionals, &tsig.resrec, 0); + *end = PutResourceRecordTTLJumbo(msg, ptr, numAdditionals, &tsig.resrec, 0); if (!*end) { LogMsg("ERROR: DNSDigest_SignMessage - could not put TSIG"); return mDNSNULL; } // update num additionals @@ -1433,7 +1453,6 @@ mDNSexport mDNSu8 *DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, mDNSu16 return *end; } - #ifdef __cplusplus } diff --git a/mDNSCore/mDNS.c b/mDNSCore/mDNS.c index b5364c2..83f634b 100755 --- a/mDNSCore/mDNS.c +++ b/mDNSCore/mDNS.c @@ -1,10 +1,9 @@ -/* +/* -*- Mode: C; tab-width: 4 -*- + * * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -46,6 +45,369 @@ Change History (most recent first): $Log: mDNS.c,v $ +Revision 1.491 2004/12/11 01:52:11 cheshire + Support kDNSServiceFlagsAllowRemoteQuery for registering services too + +Revision 1.490 2004/12/10 20:06:25 cheshire + Reduce egregious stack space usage +Reduced SendDelayedUnicastResponse() stack frame from 9K to 112 bytes + +Revision 1.489 2004/12/10 20:03:43 cheshire + Reduce egregious stack space usage +Reduced mDNSCoreReceiveQuery() stack frame from 9K to 144 bytes + +Revision 1.488 2004/12/10 19:50:41 cheshire + Reduce egregious stack space usage +Reduced SendResponses() stack frame from 9K to 176 bytes + +Revision 1.487 2004/12/10 19:39:13 cheshire + Reduce egregious stack space usage +Reduced SendQueries() stack frame from 18K to 112 bytes + +Revision 1.486 2004/12/10 14:16:17 cheshire + Relax update rate limiting +We now allow an average rate of ten updates per minute. +Updates in excess of that are rate limited, but more gently than before. + +Revision 1.485 2004/12/10 02:09:24 cheshire + Modify default TTLs + +Revision 1.484 2004/12/09 03:15:40 ksekar + use _legacy instead of _default to find "empty string" browse domains + +Revision 1.483 2004/12/07 23:00:14 ksekar + DNSServiceRegisterRecord() can crash on deregistration: +Call RecordProbeFailure even if there is no record callback + +Revision 1.482 2004/12/07 22:49:06 cheshire + BIND doesn't like zero-length rdata + +Revision 1.481 2004/12/07 21:26:04 ksekar + DNSServiceRegisterRecord() can crash on deregistration + +Revision 1.480 2004/12/07 20:42:33 cheshire +Add explicit context parameter to mDNS_RemoveRecordFromService() + +Revision 1.479 2004/12/07 17:50:49 ksekar + BIND doesn't like zero-length rdata + +Revision 1.478 2004/12/06 21:15:22 ksekar + mDNSResponder crashed in CheckServiceRegistrations + +Revision 1.477 2004/12/04 02:12:45 cheshire + mDNSResponder puts LargeCacheRecord on the stack + +Revision 1.476 2004/11/29 23:34:31 cheshire +On platforms with coarse time resolutions, ORing time values with one to ensure they are non-zero +is crude, and effectively halves the time resolution. The more selective NonZeroTime() function +only nudges the time value to 1 if the interval calculation happens to result in the value zero. + +Revision 1.475 2004/11/29 23:13:31 cheshire + All unique records in a set should have the cache flush bit set +Additional check: Make sure we don't unnecessarily send packets containing only additionals. +(This could occur with multi-packet KA lists, if the answer and additionals were marked +by the query packet, and then the answer were later suppressed in a subsequent KA packet.) + +Revision 1.474 2004/11/29 17:18:12 cheshire +Remove "Unknown DNS packet type" message for update responses + +Revision 1.473 2004/11/25 01:57:52 cheshire + All unique records in a set should have the cache flush bit set + +Revision 1.472 2004/11/25 01:28:09 cheshire + Need to implement random delay for 'QU' unicast replies (and set cache flush bit too) + +Revision 1.471 2004/11/25 01:10:13 cheshire +Move code to add additional records to a subroutine called AddAdditionalsToResponseList() + +Revision 1.470 2004/11/24 21:54:44 cheshire + mDNSCore not receiving unicast responses properly + +Revision 1.469 2004/11/24 04:50:39 cheshire +Minor tidying + +Revision 1.468 2004/11/24 01:47:07 cheshire + DNSServiceRegisterRecord should call CallBack on success. + +Revision 1.467 2004/11/24 01:41:28 cheshire +Rename CompleteProbing() to AcknowledgeRecord() + +Revision 1.466 2004/11/23 21:08:07 ksekar +Don't use ID to demux multicast/unicast now that unicast uses random IDs + +Revision 1.465 2004/11/15 20:09:21 ksekar + Wide Area support for Add/Remove record + +Revision 1.464 2004/11/03 01:44:36 cheshire +Update debugging messages + +Revision 1.463 2004/10/29 02:38:48 cheshire +Fix Windows compile errors + +Revision 1.462 2004/10/28 19:21:07 cheshire +Guard against registering interface with zero InterfaceID + +Revision 1.461 2004/10/28 19:02:16 cheshire +Remove \n from LogMsg() call + +Revision 1.460 2004/10/28 03:24:40 cheshire +Rename m->CanReceiveUnicastOn as m->CanReceiveUnicastOn5353 + +Revision 1.459 2004/10/26 22:34:37 cheshire + Need to protect mDNSResponder from unbounded packet flooding + +Revision 1.458 2004/10/26 20:45:28 cheshire +Show mask in "invalid mask" message + +Revision 1.457 2004/10/26 06:28:36 cheshire +Now that we don't check IP TTL any more, remove associated log message + +Revision 1.456 2004/10/26 06:21:42 cheshire +Adjust mask validity check to allow an all-ones mask (for IPv6 ::1 loopback address) + +Revision 1.455 2004/10/26 06:11:40 cheshire +Add improved logging to aid in diagnosis of mDNSResponder crashed + +Revision 1.454 2004/10/23 01:16:00 cheshire + uDNS operations not always reliable on multi-homed hosts + +Revision 1.453 2004/10/22 20:52:06 ksekar + Create NAT port mappings for Long Lived Queries + +Revision 1.452 2004/10/20 01:50:40 cheshire + Cannot resolve non-local registrations using the mach API +Implemented ForceMCast mode for AuthRecords as well as for Questions + +Revision 1.451 2004/10/19 21:33:15 cheshire + Cannot resolve non-local registrations using the mach API +Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name +doesn't force multicast unless you set this flag to indicate explicitly that this is what you want + +Revision 1.450 2004/10/19 17:42:59 ksekar +Fixed compiler warnings for non-debug builds. + +Revision 1.449 2004/10/18 22:57:07 cheshire + Seen in console: Ignored apparent spoof mDNS Response with TTL 1 + +Revision 1.448 2004/10/16 00:16:59 cheshire + Replace IP TTL 255 check with local subnet source address check + +Revision 1.447 2004/10/15 00:51:21 cheshire + Seen in console: Ignored apparent spoof mDNS Response with TTL 1 + +Revision 1.446 2004/10/14 00:43:34 cheshire + Services continue to announce SRV and HINFO + +Revision 1.445 2004/10/12 21:07:09 cheshire +Set up m->p in mDNS_Init() before calling mDNSPlatformTimeInit() + +Revision 1.444 2004/10/11 17:54:16 ksekar +Changed hashtable pointer output from debugf to verbosedebugf. + +Revision 1.443 2004/10/10 07:05:45 cheshire +For consistency, use symbol "localdomain" instead of literal string + +Revision 1.442 2004/10/08 20:25:10 cheshire +Change of plan for -- we're not going to do that at this time + +Revision 1.441 2004/10/08 03:25:01 ksekar + domain enumeration should use LLQs + +Revision 1.440 2004/10/06 01:44:19 cheshire + Resolving too quickly sometimes returns stale TXT record + +Revision 1.439 2004/10/03 23:14:11 cheshire +Add "mDNSEthAddr" type and "zeroEthAddr" constant + +Revision 1.438 2004/09/29 23:07:04 cheshire +Patch from Pavel Repin to fix compile error on Windows + +Revision 1.437 2004/09/28 02:23:50 cheshire + Deliver near-pending "remove" events before new "add" events +Don't need to search the entire cache for nearly-expired records -- just the appropriate hash slot +For records with the cache flush bit set, defer the decision until the end of the packet + +Revision 1.436 2004/09/28 01:27:04 cheshire +Update incorrect log message + +Revision 1.435 2004/09/25 02:41:39 cheshire + Deliver near-pending "remove" events before new "add" events + +Revision 1.434 2004/09/25 02:32:06 cheshire +Update comments + +Revision 1.433 2004/09/25 02:24:27 cheshire +Removed unused rr->UseCount + +Revision 1.432 2004/09/24 21:35:17 cheshire + Browses are no longer piggybacking on other browses +TargetPort and TargetQID are allowed to be undefined if no question->Target is set + +Revision 1.431 2004/09/24 21:33:12 cheshire +Adjust comment + +Revision 1.430 2004/09/24 02:15:49 cheshire + Late conflicts don't send goodbye packets on other interfaces + +Revision 1.429 2004/09/24 00:20:21 cheshire + Any rrtype is a conflict for unique records + +Revision 1.428 2004/09/24 00:12:25 cheshire +Get rid of unused RRUniqueOrKnownUnique(RR) + +Revision 1.427 2004/09/23 20:44:11 cheshire + Reduce timeout before expiring records on failure + +Revision 1.426 2004/09/23 20:21:07 cheshire + Refine "immediate answer burst; restarting exponential backoff sequence" logic +Associate a unique sequence number with each received packet, and only increment the count of recent answer +packets if the packet sequence number for this answer record is not one we've already seen and counted. + +Revision 1.425 2004/09/23 20:14:38 cheshire +Rename "question->RecentAnswers" to "question->RecentAnswerPkts" + +Revision 1.424 2004/09/23 00:58:36 cheshire + Rate limiting interferes with updating TXT records + +Revision 1.423 2004/09/23 00:50:53 cheshire + Don't send a (DE) if a service is unregistered after wake from sleep + +Revision 1.422 2004/09/22 02:34:46 cheshire +Move definitions of default TTL times from mDNS.c to mDNSEmbeddedAPI.h + +Revision 1.421 2004/09/21 23:29:49 cheshire + DNSServiceResolve should delay sending packets + +Revision 1.420 2004/09/21 23:01:42 cheshire +Update debugf messages + +Revision 1.419 2004/09/21 19:51:14 cheshire +Move "Starting time value" message from mDNS.c to mDNSMacOSX/daemon.c + +Revision 1.418 2004/09/21 18:40:17 cheshire + Adjust default record TTLs + +Revision 1.417 2004/09/21 17:32:16 cheshire + Rate limiting imposed too soon + +Revision 1.416 2004/09/20 23:52:01 cheshire +CFSocket{Puma}.c renamed to mDNSMacOSX{Puma}.c + +Revision 1.415 2004/09/18 01:14:09 cheshire + Resolve() should not bother doing AAAA queries on machines with no IPv6 interfaces + +Revision 1.414 2004/09/18 01:06:48 cheshire +Add comments + +Revision 1.413 2004/09/17 01:08:48 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.412 2004/09/17 00:46:33 cheshire +mDNS_TimeNow should take const mDNS parameter + +Revision 1.411 2004/09/17 00:31:51 cheshire +For consistency with ipv6, renamed rdata field 'ip' to 'ipv4' + +Revision 1.410 2004/09/17 00:19:10 cheshire +For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4 + +Revision 1.409 2004/09/16 21:59:15 cheshire +For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr + +Revision 1.408 2004/09/16 21:36:36 cheshire + Fix unsafe use of mDNSPlatformTimeNow() +Changes to add necessary locking calls around unicast DNS operations + +Revision 1.407 2004/09/16 02:29:39 cheshire +Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around +uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService + +Revision 1.406 2004/09/16 01:58:14 cheshire +Fix compiler warnings + +Revision 1.405 2004/09/16 00:24:48 cheshire + Fix unsafe use of mDNSPlatformTimeNow() + +Revision 1.404 2004/09/15 21:44:11 cheshire + Randomize initial timenow_adjust value in mDNS_Init +Show time value in log to help diagnose errors + +Revision 1.403 2004/09/15 00:46:32 ksekar +Changed debugf to verbosedebugf in CheckCacheExpiration + +Revision 1.402 2004/09/14 23:59:55 cheshire + Randomize initial timenow_adjust value in mDNS_Init + +Revision 1.401 2004/09/14 23:27:46 cheshire +Fix compile errors + +Revision 1.400 2004/09/02 03:48:47 cheshire + Disable targeted unicast query support by default +1. New flag kDNSServiceFlagsAllowRemoteQuery to indicate we want to allow remote queries for this record +2. New field AllowRemoteQuery in AuthRecord structure +3. uds_daemon.c sets AllowRemoteQuery if kDNSServiceFlagsAllowRemoteQuery is set +4. mDNS.c only answers remote queries if AllowRemoteQuery is set + +Revision 1.399 2004/09/02 01:39:40 cheshire +For better readability, follow consistent convention that QR bit comes first, followed by OP bits + +Revision 1.398 2004/09/01 03:59:29 ksekar +: Conditionally compile out uDNS code on Windows + +Revision 1.397 2004/08/25 22:04:25 rpantos +Fix the standard Windows compile error. + +Revision 1.396 2004/08/25 00:37:27 ksekar +: Cleanup DynDNS hostname registration code + +Revision 1.395 2004/08/18 17:21:18 ksekar +Removed double-call of uDNS_AdvertiseInterface from mDNS_SetFQDNs() + +Revision 1.394 2004/08/14 03:22:41 cheshire + Dynamic DNS UI <-> mDNSResponder glue +Add GetUserSpecifiedDDNSName() routine +Convert ServiceRegDomain to domainname instead of C string +Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs + +Revision 1.393 2004/08/13 23:42:52 cheshire +Removed unused "zeroDomainNamePtr" + +Revision 1.392 2004/08/13 23:37:02 cheshire +Now that we do both uDNS and mDNS, global replace "uDNS_info.hostname" with +"uDNS_info.UnicastHostname" for clarity + +Revision 1.391 2004/08/13 23:25:00 cheshire +Now that we do both uDNS and mDNS, global replace "m->hostname" with +"m->MulticastHostname" for clarity + +Revision 1.390 2004/08/11 02:17:01 cheshire + Registering service with port number 0 should create a "No Such Service" record + +Revision 1.389 2004/08/10 23:19:14 ksekar +: DNS Extension daemon for Wide Area Rendezvous +Moved routines/constants to allow extern access for garbage collection daemon + +Revision 1.388 2004/07/30 17:40:06 ksekar +: TXT Record updates not available for wide-area services + +Revision 1.387 2004/07/26 22:49:30 ksekar +: Feature #9516: Need support for NATPMP in client + +Revision 1.386 2004/07/13 21:24:24 rpantos +Fix for . + +Revision 1.385 2004/06/18 19:09:59 cheshire + Current method of doing subtypes causes name collisions + +Revision 1.384 2004/06/15 04:31:23 cheshire +Make sure to clear m->CurrentRecord at the end of AnswerNewLocalOnlyQuestion() + +Revision 1.383 2004/06/11 00:04:59 cheshire + TTL must be greater than zero for DNSServiceRegisterRecord + Revision 1.382 2004/06/08 04:59:40 cheshire Tidy up wording -- log messages are already prefixed with "mDNSResponder", so don't need to repeat it @@ -201,7 +563,7 @@ Revision 1.335 2003/12/01 20:27:48 cheshire Display IPv6 addresses correctly (e.g. in log messages) on little-endian processors Revision 1.334 2003/11/20 22:59:53 cheshire -Changed runtime checks in mDNS.c to be compile-time checks in mDNSClientAPI.h +Changed runtime checks in mDNS.c to be compile-time checks in mDNSEmbeddedAPI.h Thanks to Bob Bradley for suggesting the ingenious compiler trick to make this work. Revision 1.333 2003/11/20 20:49:53 cheshire @@ -243,13 +605,13 @@ DeadvertiseInterface() -- they're internal private routines, not API routines. Revision 1.323 2003/11/14 20:59:08 cheshire Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h. -Best solution is just to combine mDNSClientAPI.h and mDNSPlatformFunctions.h into a single file. +Best solution is just to combine mDNSEmbeddedAPI.h and mDNSPlatformFunctions.h into a single file. Revision 1.322 2003/11/14 19:47:52 cheshire Define symbol MAX_ESCAPED_DOMAIN_NAME to indicate recommended buffer size for ConvertDomainNameToCString Revision 1.321 2003/11/14 19:18:34 cheshire -Move AssignDomainName macro to mDNSClientAPI.h to that client layers can use it too +Move AssignDomainName macro to mDNSEmbeddedAPI.h to that client layers can use it too Revision 1.320 2003/11/13 06:45:04 cheshire Fix compiler warning on certain compilers @@ -332,7 +694,7 @@ Revision 1.299 2003/09/03 02:33:09 cheshire Don't update m->NewQuestions until *after* CheckCacheExpiration(); Revision 1.298 2003/09/03 01:47:01 cheshire - Rendezvous services always in a state of flux + Services always in a state of flux Change mDNS_Reconfirm_internal() minimum timeout from 5 seconds to 45-60 seconds Revision 1.297 2003/08/29 19:44:15 cheshire @@ -410,7 +772,7 @@ Final expiration queries now only mark the question for sending on the particula pertaining to the record that's expiring. Revision 1.278 2003/08/18 22:53:37 cheshire - mDNSResponder divide by zero in mDNSPlatformTimeNow() + mDNSResponder divide by zero in mDNSPlatformRawTime() Revision 1.277 2003/08/18 19:05:44 cheshire UpdateRecord not working right @@ -441,7 +803,7 @@ We want to avoid touching the rdata pages, so we don't page them in. Revision 1.272 2003/08/14 19:29:04 cheshire Include cache records in SIGINFO output -Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSClientAPI.h so daemon.c can use them +Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSEmbeddedAPI.h so daemon.c can use them Revision 1.271 2003/08/14 02:17:05 cheshire Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord @@ -581,7 +943,7 @@ Revision 1.231 2003/07/18 00:06:37 cheshire To make code a little easier to read in GetRDLength(), search-and-replace "rr->rdata->u." with "rd->" Revision 1.230 2003/07/17 18:16:54 cheshire - Rendezvous services always in a state of flux + Services always in a state of flux In preparation for working on this, made some debugf messages a little more selective Revision 1.229 2003/07/17 17:35:04 cheshire @@ -984,7 +1346,7 @@ Fix some warnings Revision 1.118 2003/05/14 18:48:40 cheshire mDNSResponder should be smarter about reconfigurations More minor refinements: -CFSocket.c needs to do *all* its mDNS_DeregisterInterface calls before freeing memory +mDNSMacOSX.c needs to do *all* its mDNS_DeregisterInterface calls before freeing memory mDNS_DeregisterInterface revalidates cache record when *any* representative of an interface goes away Revision 1.117 2003/05/14 07:08:36 cheshire @@ -996,7 +1358,7 @@ and caused lots of extra network traffic. Now it only removes interfaces that have really gone, and only adds new ones that weren't there before. Revision 1.116 2003/05/14 06:51:56 cheshire - Rendezvous doesn't refresh server info if changed during sleep + mDNSResponder doesn't refresh server info if changed during sleep Revision 1.115 2003/05/14 06:44:31 cheshire Improve debugging message @@ -1214,7 +1576,7 @@ Revision 1.61 2002/09/20 01:05:24 cheshire Don't kill the Extras list in mDNS_DeregisterService() Revision 1.60 2002/09/19 23:47:35 cheshire -Added mDNS_RegisterNoSuchService() function for assertion of non-existance +Added mDNS_RegisterNoSuchService() function for assertion of non-existence of a particular named service Revision 1.59 2002/09/19 21:25:34 cheshire @@ -1232,7 +1594,6 @@ Merge in license terms from Quinn's copy, in preparation for Darwin release #define TEST_LOCALONLY_FOR_EVERYTHING 0 -//#include "mDNSClientAPI.h" // Defines the interface provided to the client layer above #include "DNSCommon.h" // Defines general DNS untility routines #include "uDNS.h" // Defines entry points into unicast-specific routines // Disable certain benign warnings with Microsoft compilers @@ -1257,15 +1618,15 @@ Merge in license terms from Quinn's copy, in preparation for Darwin release #endif mDNSexport const mDNSIPPort zeroIPPort = { { 0 } }; -mDNSexport const mDNSv4Addr zeroIPAddr = { { 0 } }; +mDNSexport const mDNSv4Addr zerov4Addr = { { 0 } }; mDNSexport const mDNSv6Addr zerov6Addr = { { 0 } }; +mDNSexport const mDNSEthAddr zeroEthAddr = { { 0 } }; mDNSexport const mDNSv4Addr onesIPv4Addr = { { 255, 255, 255, 255 } }; mDNSexport const mDNSv6Addr onesIPv6Addr = { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }; mDNSexport const mDNSAddr zeroAddr = { mDNSAddrType_None, {{{ 0 }}} }; mDNSexport const mDNSInterfaceID mDNSInterface_Any = 0; mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)-1; -mDNSexport const mDNSInterfaceID mDNSInterface_ForceMCast = (mDNSInterfaceID)-2; // Note that mDNSInterfaceMark is the same value as mDNSInterface_LocalOnly, but they are used in different contexts mDNSlocal const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)~0; @@ -1275,7 +1636,7 @@ mDNSlocal const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)~0; mDNSexport const mDNSIPPort UnicastDNSPort = { { UnicastDNSPortAsNumber >> 8, UnicastDNSPortAsNumber & 0xFF } }; mDNSexport const mDNSIPPort MulticastDNSPort = { { MulticastDNSPortAsNumber >> 8, MulticastDNSPortAsNumber & 0xFF } }; mDNSexport const mDNSv4Addr AllDNSAdminGroup = { { 239, 255, 255, 251 } }; -mDNSexport const mDNSv4Addr AllDNSLinkGroup = { { 224, 0, 0, 251 } }; +mDNSexport const mDNSv4Addr AllDNSLinkGroupv4 = { { 224, 0, 0, 251 } }; mDNSexport const mDNSv6Addr AllDNSLinkGroupv6 = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } }; mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 251 } } } }; mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } }; @@ -1283,27 +1644,28 @@ mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF mDNSexport const mDNSOpaque16 zeroID = { { 0, 0 } }; mDNSexport const mDNSOpaque16 QueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery, 0 } }; mDNSexport const mDNSOpaque16 ResponseFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA, 0 } }; -mDNSexport const mDNSOpaque16 UpdateReqFlags= { { kDNSFlag0_OP_Update | kDNSFlag0_QR_Query, 0 } }; -mDNSexport const mDNSOpaque16 UpdateRespFlags={ { kDNSFlag0_OP_Update | kDNSFlag0_QR_Response, 0 } }; -#define zeroDomainNamePtr ((domainname*)"") +mDNSexport const mDNSOpaque16 UpdateReqFlags= { { kDNSFlag0_QR_Query | kDNSFlag0_OP_Update, 0 } }; +mDNSexport const mDNSOpaque16 UpdateRespFlags={ { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update, 0 } }; // Any records bigger than this are considered 'large' records #define SmallRecordLimit 1024 -#define kDefaultTTLforUnique 240 -#define kDefaultTTLforShared (2*3600) - #define kMaxUpdateCredits 10 -#define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 50) +#define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 6) static const char *const mDNS_DomainTypeNames[] = { "_browse._dns-sd._udp.", "_default._browse._dns-sd._udp.", "_register._dns-sd._udp.", - "_default._register._dns-sd._udp." + "_default._register._dns-sd._udp.", + "_legacy._browse._dns-sd._udp." }; +#ifdef UNICAST_DISABLED +#define uDNS_IsActiveQuery(q, u) mDNSfalse +#endif + // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - @@ -1639,20 +2001,10 @@ mDNSlocal void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q) (ResourceRecordIsValidAnswer(RR) && \ ((RR)->resrec.InterfaceID == mDNSInterface_Any || (RR)->resrec.InterfaceID == (INTID))) -#define RRUniqueOrKnownUnique(RR) ((RR)->RecordType & (kDNSRecordTypeUnique | kDNSRecordTypeKnownUnique)) - #define DefaultProbeCountForTypeUnique ((mDNSu8)3) #define DefaultProbeCountForRecordType(X) ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0) -// For records that have *never* been announced on the wire, their AnnounceCount will be set to InitialAnnounceCount (10). -// When de-registering these records we do not need to send any goodbye packet because we never announced them in the first -// place. If AnnounceCount is less than InitialAnnounceCount that means we have announced them at least once, so a goodbye -// packet is needed. For this reason, if we ever reset AnnounceCount (e.g. after an interface change) we set it to -// ReannounceCount (9), not InitialAnnounceCount. If we were to reset AnnounceCount back to InitialAnnounceCount that would -// imply that the record had never been announced on the wire (which is false) and if the client were then to immediately -// deregister that record before it had a chance to announce, we'd fail to send its goodbye packet (which would be a bug). #define InitialAnnounceCount ((mDNSu8)10) -#define ReannounceCount ((mDNSu8)9) // Note that the announce intervals use exponential backoff, doubling each time. The probe intervals do not. // This means that because the announce interval is doubled after sending the first packet, the first @@ -1689,9 +2041,11 @@ mDNSlocal mDNSBool SameResourceRecordSignature(const ResourceRecord *const r1, c return(mDNSBool)(r1->rrtype == r2->rrtype && r1->rrclass == r2->rrclass && r1->namehash == r2->namehash && SameDomainName(&r1->name, &r2->name)); } -// PacketRRMatchesSignature behaves as SameResourceRecordSignature, except that types may differ if the -// authoratative record is in the probing state. We always send probes with the wildcard type kDNSQType_ANY, -// so a response of any type should match, even if it is not the type the client plans to use. +// PacketRRMatchesSignature behaves as SameResourceRecordSignature, except that types may differ if our +// authoratative record is unique (as opposed to shared). For unique records, we are supposed to have +// complete ownership of *all* types for this name, so *any* record type with the same name is a conflict. +// In addition, when probing we send our questions with the wildcard type kDNSQType_ANY, +// so a response of any type should match, even if it is not actually the type the client plans to use. mDNSlocal mDNSBool PacketRRMatchesSignature(const CacheRecord *const pktrr, const AuthRecord *const authrr) { if (!pktrr) { LogMsg("PacketRRMatchesSignature ERROR: pktrr is NULL"); return(mDNSfalse); } @@ -1699,7 +2053,7 @@ mDNSlocal mDNSBool PacketRRMatchesSignature(const CacheRecord *const pktrr, cons if (pktrr->resrec.InterfaceID && authrr->resrec.InterfaceID && pktrr->resrec.InterfaceID != authrr->resrec.InterfaceID) return(mDNSfalse); - if (authrr->resrec.RecordType != kDNSRecordTypeUnique && pktrr->resrec.rrtype != authrr->resrec.rrtype) return(mDNSfalse); + if (!(authrr->resrec.RecordType & kDNSRecordTypeUniqueMask) && pktrr->resrec.rrtype != authrr->resrec.rrtype) return(mDNSfalse); return(mDNSBool)(pktrr->resrec.rrclass == authrr->resrec.rrclass && pktrr->resrec.namehash == authrr->resrec.namehash && SameDomainName(&pktrr->resrec.name, &authrr->resrec.name)); } @@ -1741,7 +2095,7 @@ mDNSlocal void SetNextAnnounceProbeTime(mDNS *const m, const AuthRecord *const r { if (rr->resrec.RecordType == kDNSRecordTypeUnique) { - //LogMsg("ProbeCount %d Next %ld %s", rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, GetRRDisplayString(m, rr)); + //LogMsg("ProbeCount %d Next %ld %s", rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, ARDisplayString(m, rr)); if (m->NextScheduledProbe - (rr->LastAPTime + rr->ThisAPInterval) >= 0) m->NextScheduledProbe = (rr->LastAPTime + rr->ThisAPInterval); } @@ -1764,7 +2118,7 @@ mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr) // If we have no probe suppression time set, or it is in the past, set it now if (m->SuppressProbes == 0 || m->SuppressProbes - m->timenow < 0) { - m->SuppressProbes = (m->timenow + DefaultProbeIntervalForTypeUnique) | 1; + m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique); // If we already have a probe scheduled to go out sooner, then use that time to get better aggregation if (m->SuppressProbes - m->NextScheduledProbe >= 0) m->SuppressProbes = m->NextScheduledProbe; @@ -1804,12 +2158,12 @@ mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr) if (!target) debugf("SetTargetToHostName: Don't know how to set the target of rrtype %d", rr->resrec.rrtype); - if (target && SameDomainName(target, &m->hostname)) + if (target && SameDomainName(target, &m->MulticastHostname)) debugf("SetTargetToHostName: Target of %##s is already %##s", rr->resrec.name.c, target->c); - if (target && !SameDomainName(target, &m->hostname)) + if (target && !SameDomainName(target, &m->MulticastHostname)) { - AssignDomainName(*target, m->hostname); + AssignDomainName(*target, m->MulticastHostname); SetNewRData(&rr->resrec, mDNSNULL, 0); // If we're in the middle of probing this record, we need to start again, @@ -1820,19 +2174,18 @@ mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr) // If we've announced this record, we really should send a goodbye packet for the old rdata before // changing to the new rdata. However, in practice, we only do SetTargetToHostName for unique records, // so when we announce them we'll set the kDNSClass_UniqueRRSet and clear any stale data that way. - if (rr->AnnounceCount < InitialAnnounceCount && rr->resrec.RecordType == kDNSRecordTypeShared) + if (rr->RequireGoodbye && rr->resrec.RecordType == kDNSRecordTypeShared) debugf("Have announced shared record %##s (%s) at least once: should have sent a goodbye packet before updating", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); - if (rr->AnnounceCount < ReannounceCount) - rr->AnnounceCount = ReannounceCount; + rr->AnnounceCount = InitialAnnounceCount; + rr->RequireGoodbye = mDNSfalse; rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); InitializeLastAPTime(m,rr); } } -mDNSlocal void CompleteProbing(mDNS *const m, AuthRecord *const rr) +mDNSlocal void AcknowledgeRecord(mDNS *const m, AuthRecord *const rr) { - verbosedebugf("Probing for %##s (%s) complete", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); if (!rr->Acknowledged && rr->RecordCallback) { // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function @@ -1855,17 +2208,22 @@ mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) AuthRecord **p = &m->ResourceRecords; AuthRecord **d = &m->DuplicateRecords; AuthRecord **l = &m->LocalOnlyRecords; + + mDNSPlatformMemZero(&rr->uDNS_info, sizeof(uDNS_RegInfo)); + + if ((mDNSs32)rr->resrec.rroriginalttl <= 0) + { LogMsg("mDNS_Register_internal: TTL must be 1 - 0x7FFFFFFF %s", ARDisplayString(m, rr)); return(mStatus_BadParamErr); } #if TEST_LOCALONLY_FOR_EVERYTHING rr->resrec.InterfaceID = mDNSInterface_LocalOnly; #endif - // If the client has specified an explicit InterfaceID, - // then we do a multicast registration on that interface, even for unicast domains. - if (rr->resrec.InterfaceID || IsLocalDomain(&rr->resrec.name)) +#ifndef UNICAST_DISABLED + if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->ForceMCast || IsLocalDomain(&rr->resrec.name)) rr->uDNS_info.id = zeroID; else return uDNS_RegisterRecord(m, rr); - +#endif + while (*p && *p != rr) p=&(*p)->next; while (*d && *d != rr) d=&(*d)->next; while (*l && *l != rr) l=&(*l)->next; @@ -1917,6 +2275,7 @@ mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) // rr->Context = already set in mDNS_SetupResourceRecord // rr->RecordType = already set in mDNS_SetupResourceRecord // rr->HostTarget = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client +// rr->AllowRemoteQuery = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client // Make sure target is not uninitialized data, or we may crash writing debugging log messages if (rr->HostTarget && target) target->c[0] = 0; @@ -1924,11 +2283,13 @@ mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) rr->Acknowledged = mDNSfalse; rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); rr->AnnounceCount = InitialAnnounceCount; + rr->RequireGoodbye = mDNSfalse; rr->IncludeInProbe = mDNSfalse; rr->ImmedAnswer = mDNSNULL; + rr->ImmedUnicast = mDNSfalse; rr->ImmedAdditional = mDNSNULL; rr->SendRNow = mDNSNULL; - rr->v4Requester = zeroIPAddr; + rr->v4Requester = zerov4Addr; rr->v6Requester = zerov6Addr; rr->NextResponse = mDNSNULL; rr->NR_AnswerTo = mDNSNULL; @@ -1962,11 +2323,15 @@ mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) } if (!ValidateDomainName(&rr->resrec.name)) - { LogMsg("Attempt to register record with invalid name: %s", GetRRDisplayString(m, rr)); return(mStatus_Invalid); } + { LogMsg("Attempt to register record with invalid name: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); } + + // Some (or perhaps all) versions of BIND named (name daemon) don't allow updates with zero-length rdata. It's common for + // existing mDNS clients to create empty TXT records, so we silently change those to a TXT record containing a single empty string. + if (rr->resrec.rrtype == kDNSType_TXT && rr->resrec.rdlength == 0) { rr->resrec.rdlength = 1; rr->resrec.rdata->u.txt.c[0] = 0; } // Don't do this until *after* we've set rr->resrec.rdlength if (!ValidateRData(rr->resrec.rrtype, rr->resrec.rdlength, rr->resrec.rdata)) - { LogMsg("Attempt to register record with invalid rdata: %s", GetRRDisplayString(m, rr)); return(mStatus_Invalid); } + { LogMsg("Attempt to register record with invalid rdata: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); } rr->resrec.namehash = DomainNameHashValue(&rr->resrec.name); rr->resrec.rdatahash = RDataHashValue(rr->resrec.rdlength, &rr->resrec.rdata->u); @@ -2021,6 +2386,10 @@ mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) *p = rr; } } + + // For records that are not going to probe, acknowledge them right away + if (rr->resrec.RecordType != kDNSRecordTypeUnique) AcknowledgeRecord(m, rr); + return(mStatus_NoError); } @@ -2028,12 +2397,17 @@ mDNSlocal void RecordProbeFailure(mDNS *const m, const AuthRecord *const rr) { m->ProbeFailTime = m->timenow; m->NumFailedProbes++; - // If we've had ten or more probe failures, rate-limit to one every five seconds - // The result is ORed with 1 to make sure SuppressProbes is not accidentally set to zero - if (m->NumFailedProbes >= 10) + // If we've had fifteen or more probe failures, rate-limit to one every five seconds. + // If a bunch of hosts have all been configured with the same name, then they'll all + // conflict and run through the same series of names: name-2, name-3, name-4, etc., + // up to name-10. After that they'll start adding random increments in the range 1-100, + // so they're more likely to branch out in the available namespace and settle on a set of + // unique names quickly. If after five more tries the host is still conflicting, then we + // may have a serious problem, so we start rate-limiting so we don't melt down the network. + if (m->NumFailedProbes >= 15) { - m->SuppressProbes = (m->timenow + mDNSPlatformOneSecond * 5) | 1; - LogMsg("Excessive name conflicts (%d) for %##s (%s); rate limiting in effect", + m->SuppressProbes = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 5); + LogMsg("Excessive name conflicts (%lu) for %##s (%s); rate limiting in effect", m->NumFailedProbes, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); } } @@ -2059,8 +2433,10 @@ mDNSlocal mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, mDNSu8 RecordType = rr->resrec.RecordType; AuthRecord **p = &m->ResourceRecords; // Find this record in our list of active records - if (!rr->resrec.InterfaceID && !IsLocalDomain(&rr->resrec.name) && rr->uDNS_info.id.NotAnInteger) +#ifndef UNICAST_DISABLED + if (!(rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->ForceMCast || IsLocalDomain(&rr->resrec.name))) return uDNS_DeregisterRecord(m, rr); +#endif if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly) p = &m->LocalOnlyRecords; while (*p && *p != rr) p=&(*p)->next; @@ -2091,18 +2467,20 @@ mDNSlocal mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, dup->next = rr->next; // And then... rr->next = dup; // ... splice it in right after the record we're about to delete dup->resrec.RecordType = rr->resrec.RecordType; - dup->ProbeCount = rr->ProbeCount; - dup->AnnounceCount = rr->AnnounceCount; - dup->ImmedAnswer = rr->ImmedAnswer; - dup->ImmedAdditional = rr->ImmedAdditional; - dup->v4Requester = rr->v4Requester; - dup->v6Requester = rr->v6Requester; - dup->ThisAPInterval = rr->ThisAPInterval; - dup->AnnounceUntil = rr->AnnounceUntil; - dup->LastAPTime = rr->LastAPTime; - dup->LastMCTime = rr->LastMCTime; - dup->LastMCInterface = rr->LastMCInterface; - if (RecordType == kDNSRecordTypeShared) rr->AnnounceCount = InitialAnnounceCount; + dup->ProbeCount = rr->ProbeCount; + dup->AnnounceCount = rr->AnnounceCount; + dup->RequireGoodbye = rr->RequireGoodbye; + dup->ImmedAnswer = rr->ImmedAnswer; + dup->ImmedUnicast = rr->ImmedUnicast; + dup->ImmedAdditional = rr->ImmedAdditional; + dup->v4Requester = rr->v4Requester; + dup->v6Requester = rr->v6Requester; + dup->ThisAPInterval = rr->ThisAPInterval; + dup->AnnounceUntil = rr->AnnounceUntil; + dup->LastAPTime = rr->LastAPTime; + dup->LastMCTime = rr->LastMCTime; + dup->LastMCInterface = rr->LastMCInterface; + rr->RequireGoodbye = mDNSfalse; } } } @@ -2112,7 +2490,7 @@ mDNSlocal mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, p = &m->DuplicateRecords; while (*p && *p != rr) p=&(*p)->next; // If we found our record on the duplicate list, then make sure we don't send a goodbye for it - if (*p && RecordType == kDNSRecordTypeShared) rr->AnnounceCount = InitialAnnounceCount; + if (*p) rr->RequireGoodbye = mDNSfalse; if (*p) debugf("DNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); } @@ -2120,13 +2498,13 @@ mDNSlocal mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, { // No need to log an error message if we already know this is a potentially repeated deregistration if (drt != mDNS_Dereg_repeat) - debugf("mDNS_Deregister_internal: Record %p %##s (%s) not found in list", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + LogMsg("mDNS_Deregister_internal: Record %p %##s (%s) not found in list", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); return(mStatus_BadReferenceErr); } // If this is a shared record and we've announced it at least once, // we need to retract that announcement before we delete the record - if (RecordType == kDNSRecordTypeShared && rr->AnnounceCount < InitialAnnounceCount) + if (RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) { verbosedebugf("mDNS_Deregister_internal: Sending deregister for %##s (%s)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); rr->resrec.RecordType = kDNSRecordTypeDeregistering; @@ -2172,17 +2550,12 @@ mDNSlocal mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, // In this case the likely client action to the mStatus_MemFree message is to free the memory, // so any attempt to touch rr after this is likely to lead to a crash. m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback - if (RecordType == kDNSRecordTypeShared) - { - if (rr->RecordCallback) - rr->RecordCallback(m, rr, mStatus_MemFree); - } - else if (drt == mDNS_Dereg_conflict) + if (drt == mDNS_Dereg_conflict) { RecordProbeFailure(m, rr); - if (rr->RecordCallback) - rr->RecordCallback(m, rr, mStatus_NameConflict); + if (rr->RecordCallback) rr->RecordCallback(m, rr, mStatus_NameConflict); } + else if (rr->RecordCallback) rr->RecordCallback(m, rr, mStatus_MemFree); m->mDNS_reentrancy--; // Decrement to block mDNS API calls again } return(mStatus_NoError); @@ -2195,13 +2568,127 @@ mDNSlocal mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, #pragma mark - Packet Sending Functions #endif +mDNSlocal void AddRecordToResponseList(AuthRecord ***nrpp, AuthRecord *rr, AuthRecord *add) + { + if (rr->NextResponse == mDNSNULL && *nrpp != &rr->NextResponse) + { + **nrpp = rr; + // NR_AdditionalTo must point to a record with NR_AnswerTo set (and not NR_AdditionalTo) + // If 'add' does not meet this requirement, then follow its NR_AdditionalTo pointer to a record that does + // The referenced record will definitely be acceptable (by recursive application of this rule) + if (add && add->NR_AdditionalTo) add = add->NR_AdditionalTo; + rr->NR_AdditionalTo = add; + *nrpp = &rr->NextResponse; + } + debugf("AddRecordToResponseList: %##s (%s) already in list", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + } + +mDNSlocal void AddAdditionalsToResponseList(mDNS *const m, AuthRecord *ResponseRecords, AuthRecord ***nrpp, const mDNSInterfaceID InterfaceID) + { + AuthRecord *rr, *rr2; + for (rr=ResponseRecords; rr; rr=rr->NextResponse) // For each record we plan to put + { + // (Note: This is an "if", not a "while". If we add a record, we'll find it again + // later in the "for" loop, and we will follow further "additional" links then.) + if (rr->Additional1 && ResourceRecordIsValidInterfaceAnswer(rr->Additional1, InterfaceID)) + AddRecordToResponseList(nrpp, rr->Additional1, rr); + + if (rr->Additional2 && ResourceRecordIsValidInterfaceAnswer(rr->Additional2, InterfaceID)) + AddRecordToResponseList(nrpp, rr->Additional2, rr); + + // For SRV records, automatically add the Address record(s) for the target host + if (rr->resrec.rrtype == kDNSType_SRV) + for (rr2=m->ResourceRecords; rr2; rr2=rr2->next) // Scan list of resource records + if (RRTypeIsAddressType(rr2->resrec.rrtype) && // For all address records (A/AAAA) ... + ResourceRecordIsValidInterfaceAnswer(rr2, InterfaceID) && // ... which are valid for answer ... + rr->resrec.rdnamehash == rr2->resrec.namehash && // ... whose name is the name of the SRV target + SameDomainName(&rr->resrec.rdata->u.srv.target, &rr2->resrec.name)) + AddRecordToResponseList(nrpp, rr2, rr); + } + } + +mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const dest, const mDNSInterfaceID InterfaceID) + { + AuthRecord *rr; + AuthRecord *ResponseRecords = mDNSNULL; + AuthRecord **nrp = &ResponseRecords; + + // Make a list of all our records that need to be unicast to this destination + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + // If we find we can no longer unicast this answer, clear ImmedUnicast + if (rr->ImmedAnswer == mDNSInterfaceMark || + mDNSSameIPv4Address(rr->v4Requester, onesIPv4Addr) || + mDNSSameIPv6Address(rr->v6Requester, onesIPv6Addr) ) + rr->ImmedUnicast = mDNSfalse; + + if (rr->ImmedUnicast && rr->ImmedAnswer == InterfaceID) + if ((dest->type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->v4Requester, dest->ip.v4)) || + (dest->type == mDNSAddrType_IPv6 && mDNSSameIPv6Address(rr->v6Requester, dest->ip.v6))) + { + rr->ImmedAnswer = mDNSNULL; // Clear the state fields + rr->ImmedUnicast = mDNSfalse; + rr->v4Requester = zerov4Addr; + rr->v6Requester = zerov6Addr; + if (rr->NextResponse == mDNSNULL && nrp != &rr->NextResponse) // rr->NR_AnswerTo + { rr->NR_AnswerTo = (mDNSu8*)~0; *nrp = rr; nrp = &rr->NextResponse; } + } + } + + AddAdditionalsToResponseList(m, ResponseRecords, &nrp, InterfaceID); + + while (ResponseRecords) + { + mDNSu8 *responseptr = m->omsg.data; + mDNSu8 *newptr; + InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags); + + // Put answers in the packet + while (ResponseRecords && ResponseRecords->NR_AnswerTo) + { + rr = ResponseRecords; + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it + newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec); + rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state + if (!newptr && m->omsg.h.numAnswers) break; // If packet full, send it now + if (newptr) responseptr = newptr; + ResponseRecords = rr->NextResponse; + rr->NextResponse = mDNSNULL; + rr->NR_AnswerTo = mDNSNULL; + rr->NR_AdditionalTo = mDNSNULL; + rr->RequireGoodbye = mDNStrue; + } + + // Add additionals, if there's space + while (ResponseRecords && !ResponseRecords->NR_AnswerTo) + { + rr = ResponseRecords; + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it + newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAdditionals, &rr->resrec); + rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state + + if (newptr) responseptr = newptr; + if (newptr && m->omsg.h.numAnswers) rr->RequireGoodbye = mDNStrue; + else if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) rr->ImmedAnswer = mDNSInterfaceMark; + ResponseRecords = rr->NextResponse; + rr->NextResponse = mDNSNULL; + rr->NR_AnswerTo = mDNSNULL; + rr->NR_AdditionalTo = mDNSNULL; + } + + if (m->omsg.h.numAnswers) mDNSSendDNSMessage(m, &m->omsg, responseptr, mDNSInterface_Any, dest, MulticastDNSPort, -1, mDNSNULL); + } + } + mDNSlocal void CompleteDeregistration(mDNS *const m, AuthRecord *rr) { - // Setting AnnounceCount to InitialAnnounceCount signals mDNS_Deregister_internal() + // Clearing rr->RequireGoodbye signals mDNS_Deregister_internal() // that it should go ahead and immediately dispose of this registration - rr->resrec.RecordType = kDNSRecordTypeShared; - rr->AnnounceCount = InitialAnnounceCount; - mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); + rr->resrec.RecordType = kDNSRecordTypeShared; + rr->RequireGoodbye = mDNSfalse; + mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); // Don't touch rr after this } // NOTE: DiscardDeregistrations calls mDNS_Deregister_internal which can call a user callback, which may change @@ -2217,23 +2704,14 @@ mDNSlocal void DiscardDeregistrations(mDNS *const m) AuthRecord *rr = m->CurrentRecord; m->CurrentRecord = rr->next; if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) - CompleteDeregistration(m, rr); + CompleteDeregistration(m, rr); // Don't touch rr after this } } mDNSlocal void GrantUpdateCredit(AuthRecord *rr) { if (++rr->UpdateCredits >= kMaxUpdateCredits) rr->NextUpdateCredit = 0; - else rr->NextUpdateCredit = (rr->NextUpdateCredit + kUpdateCreditRefreshInterval) | 1; - } - -mDNSlocal mDNSBool HaveSentEntireRRSet(const mDNS *const m, const AuthRecord *const rr, mDNSInterfaceID InterfaceID) - { - // Try to find another member of this set that we're still planning to send on this interface - const AuthRecord *a; - for (a = m->ResourceRecords; a; a=a->next) - if (a->SendRNow == InterfaceID && a != rr && SameResourceRecordSignature(&a->resrec, &rr->resrec)) break; - return (a == mDNSNULL); // If no more members of this set found, then we should set the cache flush bit + else rr->NextUpdateCredit = NonZeroTime(rr->NextUpdateCredit + kUpdateCreditRefreshInterval); } // Note about acceleration of announcements to facilitate automatic coalescing of @@ -2261,6 +2739,22 @@ mDNSlocal void SendResponses(mDNS *const m) m->NextScheduledResponse = m->timenow + 0x78000000; + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->ImmedUnicast) + { + mDNSAddr v4 = { mDNSAddrType_IPv4, {{{0}}} }; + mDNSAddr v6 = { mDNSAddrType_IPv6, {{{0}}} }; + v4.ip.v4 = rr->v4Requester; + v6.ip.v6 = rr->v6Requester; + if (!mDNSIPv4AddressIsZero(rr->v4Requester)) SendDelayedUnicastResponse(m, &v4, rr->ImmedAnswer); + if (!mDNSIPv6AddressIsZero(rr->v6Requester)) SendDelayedUnicastResponse(m, &v6, rr->ImmedAnswer); + if (rr->ImmedUnicast) + { + LogMsg("SendResponses: ERROR: rr->ImmedUnicast still set: %s", ARDisplayString(m, rr)); + rr->ImmedUnicast = mDNSfalse; + } + } + // *** // *** 1. Setup: Set the SendRNow and ImmedAnswer fields to indicate which interface(s) the records need to be sent on // *** @@ -2354,7 +2848,7 @@ mDNSlocal void SendResponses(mDNS *const m) rr->LastMCInterface = rr->ImmedAnswer; } SetNextAnnounceProbeTime(m, rr); - //if (rr->SendRNow) LogMsg("%-15.4a %s", &rr->v4Requester, GetRRDisplayString(m, rr)); + //if (rr->SendRNow) LogMsg("%-15.4a %s", &rr->v4Requester, ARDisplayString(m, rr)); } // *** @@ -2366,10 +2860,9 @@ mDNSlocal void SendResponses(mDNS *const m) int numDereg = 0; int numAnnounce = 0; int numAnswer = 0; - DNSMessage response; - mDNSu8 *responseptr = response.data; + mDNSu8 *responseptr = m->omsg.data; mDNSu8 *newptr; - InitializeDNSMessage(&response.h, zeroID, ResponseFlags); + InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags); // First Pass. Look for: // 1. Deregistering records that need to send their goodbye packet @@ -2384,8 +2877,8 @@ mDNSlocal void SendResponses(mDNS *const m) { if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) { - newptr = PutResourceRecordTTL(&response, responseptr, &response.h.numAnswers, &rr->resrec, 0); - if (!newptr && response.h.numAnswers) break; + newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0); + if (!newptr && m->omsg.h.numAnswers) break; numDereg++; responseptr = newptr; } @@ -2394,31 +2887,30 @@ mDNSlocal void SendResponses(mDNS *const m) RData *OldRData = rr->resrec.rdata; mDNSu16 oldrdlength = rr->resrec.rdlength; // See if we should send a courtesy "goodbye" for the old data before we replace it. - // We compare with "InitialAnnounceCount-1" instead of "InitialAnnounceCount" because by the time - // we get to this place in this routine we've we've already decremented rr->AnnounceCount - if (ResourceRecordIsValidAnswer(rr) && rr->AnnounceCount < InitialAnnounceCount-1) + if (ResourceRecordIsValidAnswer(rr) && rr->RequireGoodbye) { - newptr = PutResourceRecordTTL(&response, responseptr, &response.h.numAnswers, &rr->resrec, 0); - if (!newptr && response.h.numAnswers) break; + newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0); + if (!newptr && m->omsg.h.numAnswers) break; numDereg++; responseptr = newptr; } // Now try to see if we can fit the update in the same packet (not fatal if we can't) SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); - if ((rr->resrec.RecordType & kDNSRecordTypeUniqueMask) && HaveSentEntireRRSet(m, rr, intf->InterfaceID)) + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it - newptr = PutResourceRecord(&response, responseptr, &response.h.numAnswers, &rr->resrec); + newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec); rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state if (newptr) responseptr = newptr; SetNewRData(&rr->resrec, OldRData, oldrdlength); } else { - if ((rr->resrec.RecordType & kDNSRecordTypeUniqueMask) && HaveSentEntireRRSet(m, rr, intf->InterfaceID)) + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it - newptr = PutResourceRecordTTL(&response, responseptr, &response.h.numAnswers, &rr->resrec, m->SleepState ? 0 : rr->resrec.rroriginalttl); + newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, m->SleepState ? 0 : rr->resrec.rroriginalttl); rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state - if (!newptr && response.h.numAnswers) break; + if (!newptr && m->omsg.h.numAnswers) break; + rr->RequireGoodbye = !m->SleepState; if (rr->LastAPTime == m->timenow) numAnnounce++; else numAnswer++; responseptr = newptr; } @@ -2433,47 +2925,56 @@ mDNSlocal void SendResponses(mDNS *const m) newptr = responseptr; for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->ImmedAdditional == intf->InterfaceID) - { - // Since additionals are optional, we clear ImmedAdditional anyway, even if we subsequently find it doesn't fit in the packet - rr->ImmedAdditional = mDNSNULL; - if (newptr && ResourceRecordIsValidAnswer(rr)) + if (ResourceRecordIsValidAnswer(rr)) { - if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + // If we have at least one answer already in the packet, then plan to add additionals too + mDNSBool SendAdditional = (m->omsg.h.numAnswers > 0); + + // If we're not planning to send any additionals, but this record is a unique one, then + // make sure we haven't already sent any other members of its RRSet -- if we have, then they + // will have had the cache flush bit set, so now we need to finish the job and send the rest. + if (!SendAdditional && (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)) { - // Try to find another member of this set that we're still planning to send on this interface const AuthRecord *a; for (a = m->ResourceRecords; a; a=a->next) - if (a->ImmedAdditional == intf->InterfaceID && SameResourceRecordSignature(&a->resrec, &rr->resrec)) break; - if (a == mDNSNULL) // If no more members of this set found - rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it + if (a->LastMCTime == m->timenow && + a->LastMCInterface == intf->InterfaceID && + SameResourceRecordSignature(&a->resrec, &rr->resrec)) { SendAdditional = mDNStrue; break; } } - newptr = PutResourceRecord(&response, newptr, &response.h.numAdditionals, &rr->resrec); - if (newptr) + if (!SendAdditional) // If we don't want to send this after all, + rr->ImmedAdditional = mDNSNULL; // then cancel its ImmedAdditional field + else if (newptr) // Else, try to add it if we can { - responseptr = newptr; - // If we successfully put this additional record in the packet, we record LastMCTime & LastMCInterface. - // This matters particularly in the case where we have more than one IPv6 (or IPv4) address, because otherwise, - // when we see our own multicast with the cache flush bit set, if we haven't set LastMCTime, then we'll get - // all concerned and re-announce our record again to make sure it doesn't get flushed from peer caches. - rr->LastMCTime = m->timenow; - rr->LastMCInterface = intf->InterfaceID; + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it + newptr = PutResourceRecord(&m->omsg, newptr, &m->omsg.h.numAdditionals, &rr->resrec); + rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state + if (newptr) + { + responseptr = newptr; + rr->ImmedAdditional = mDNSNULL; + rr->RequireGoodbye = mDNStrue; + // If we successfully put this additional record in the packet, we record LastMCTime & LastMCInterface. + // This matters particularly in the case where we have more than one IPv6 (or IPv4) address, because otherwise, + // when we see our own multicast with the cache flush bit set, if we haven't set LastMCTime, then we'll get + // all concerned and re-announce our record again to make sure it doesn't get flushed from peer caches. + rr->LastMCTime = m->timenow; + rr->LastMCInterface = intf->InterfaceID; + } } - rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state } - } - if (response.h.numAnswers > 0) // We *never* send a packet with only additionals in it + if (m->omsg.h.numAnswers > 0 || m->omsg.h.numAdditionals) { debugf("SendResponses: Sending %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s on %p", numDereg, numDereg == 1 ? "" : "s", numAnnounce, numAnnounce == 1 ? "" : "s", numAnswer, numAnswer == 1 ? "" : "s", - response.h.numAdditionals, response.h.numAdditionals == 1 ? "" : "s", intf->InterfaceID); - if (intf->IPv4Available) mDNSSendDNSMessage(m, &response, responseptr, intf->InterfaceID, &AllDNSLinkGroup_v4, MulticastDNSPort); - if (intf->IPv6Available) mDNSSendDNSMessage(m, &response, responseptr, intf->InterfaceID, &AllDNSLinkGroup_v6, MulticastDNSPort); - if (!m->SuppressSending) m->SuppressSending = (m->timenow + mDNSPlatformOneSecond/10) | 1; // OR with one to ensure non-zero - if (++pktcount >= 1000) - { LogMsg("SendResponses exceeded loop limit %d: giving up", pktcount); break; } + m->omsg.h.numAdditionals, m->omsg.h.numAdditionals == 1 ? "" : "s", intf->InterfaceID); + if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, &AllDNSLinkGroup_v4, MulticastDNSPort, -1, mDNSNULL); + if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, &AllDNSLinkGroup_v6, MulticastDNSPort, -1, mDNSNULL); + if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+9)/10); + if (++pktcount >= 1000) { LogMsg("SendResponses exceeded loop limit %d: giving up", pktcount); break; } // There might be more things to send on this interface, so go around one more time and try again. } else // Nothing more to send on this interface; go to next @@ -2499,20 +3000,24 @@ mDNSlocal void SendResponses(mDNS *const m) m->CurrentRecord = rr->next; if (rr->SendRNow) - { LogMsg("SendResponses: No active interface to send: %s", GetRRDisplayString(m, rr)); rr->SendRNow = mDNSNULL; } - - if (rr->NewRData) CompleteRDataUpdate(m,rr); // Update our rdata, clear the NewRData pointer, and return memory to the client + { LogMsg("SendResponses: No active interface to send: %s", ARDisplayString(m, rr)); rr->SendRNow = mDNSNULL; } - if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) - CompleteDeregistration(m, rr); - else + if (rr->ImmedAnswer) { - rr->ImmedAnswer = mDNSNULL; - rr->v4Requester = zeroIPAddr; - rr->v6Requester = zerov6Addr; + if (rr->NewRData) CompleteRDataUpdate(m,rr); // Update our rdata, clear the NewRData pointer, and return memory to the client + + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) + CompleteDeregistration(m, rr); // Don't touch rr after this + else + { + rr->ImmedAnswer = mDNSNULL; + rr->ImmedUnicast = mDNSfalse; + rr->v4Requester = zerov4Addr; + rr->v6Requester = zerov6Addr; + } } } - verbosedebugf("SendResponses: Next in %d ticks", m->NextScheduledResponse - m->timenow); + verbosedebugf("SendResponses: Next in %ld ticks", m->NextScheduledResponse - m->timenow); } // Calling CheckCacheExpiration() is an expensive operation because it has to look at the entire cache, @@ -2526,12 +3031,14 @@ mDNSlocal void SendResponses(mDNS *const m) // 4. Else, it is expiring and had an original TTL of ten seconds or less (includes explicit goodbye packets), // so allow at most 1/10 second lateness #define CacheCheckGracePeriod(RR) ( \ + ((RR)->DelayDelivery ) ? (mDNSPlatformOneSecond/10) : \ ((RR)->CRActiveQuestion == mDNSNULL ) ? (60 * mDNSPlatformOneSecond) : \ ((RR)->UnansweredQueries < MaxUnansweredQueries) ? (TicksTTL(rr)/50) : \ ((RR)->resrec.rroriginalttl > 10 ) ? (mDNSPlatformOneSecond) : (mDNSPlatformOneSecond/10)) // Note: MUST call SetNextCacheCheckTime any time we change: // rr->TimeRcvd +// rr->DelayDelivery // rr->resrec.rroriginalttl // rr->UnansweredQueries // rr->CRActiveQuestion @@ -2551,20 +3058,18 @@ mDNSlocal void SetNextCacheCheckTime(mDNS *const m, CacheRecord *const rr) if (m->NextCacheCheck - (rr->NextRequiredQuery + CacheCheckGracePeriod(rr)) > 0) m->NextCacheCheck = (rr->NextRequiredQuery + CacheCheckGracePeriod(rr)); + + if (rr->DelayDelivery) + if (m->NextCacheCheck - rr->DelayDelivery > 0) + m->NextCacheCheck = rr->DelayDelivery; } -#define kDefaultReconfirmTimeForNoAnswer ((mDNSu32)mDNSPlatformOneSecond * 45) +#define kDefaultReconfirmTimeForNoAnswer ((mDNSu32)mDNSPlatformOneSecond * 15) #define kDefaultReconfirmTimeForCableDisconnect ((mDNSu32)mDNSPlatformOneSecond * 5) #define kMinimumReconfirmTime ((mDNSu32)mDNSPlatformOneSecond * 5) mDNSlocal mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, mDNSu32 interval) { - if (!rr->resrec.InterfaceID && !IsLocalDomain(&rr->resrec.name)) - { - LogMsg("mDNS_Reconfirm_internal: Not implemented for unicast DNS"); - return mStatus_UnsupportedErr; - } - if (interval < kMinimumReconfirmTime) interval = kMinimumReconfirmTime; if (interval > 0x10000000) // Make sure interval doesn't overflow when we multiply by four below @@ -2579,7 +3084,7 @@ mDNSlocal mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, rr->resrec.rroriginalttl = interval * 4 / mDNSPlatformOneSecond; SetNextCacheCheckTime(m, rr); } - debugf("mDNS_Reconfirm_internal:%5ld ticks to go for %s", RRExpireTime(rr) - m->timenow, GetRRDisplayString(m, rr)); + debugf("mDNS_Reconfirm_internal:%5ld ticks to go for %s", RRExpireTime(rr) - m->timenow, CRDisplayString(m, rr)); return(mStatus_NoError); } @@ -2591,7 +3096,7 @@ mDNSlocal mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **queryptr, DNSQuestion *q, CacheRecord ***kalistptrptr, mDNSu32 *answerforecast) { - mDNSBool ucast = (q->LargeAnswers || q->ThisQInterval <= InitialQuestionInterval*2) && m->CanReceiveUnicast; + mDNSBool ucast = (q->LargeAnswers || q->ThisQInterval <= InitialQuestionInterval*2) && m->CanReceiveUnicastOn5353; mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); const mDNSu8 *const limit = query->data + NormalMaxDNSMessageData; mDNSu8 *newptr = putQuestion(query, *queryptr, limit, &q->qname, q->qtype, (mDNSu16)(q->qclass | ucbit)); @@ -2812,17 +3317,16 @@ mDNSlocal void SendQueries(mDNS *const m) for (q = m->Questions; q; q=q->next) if (q->Target.type && (q->SendQNow || TimeToSendThisQuestion(q, m->timenow))) { - DNSMessage query; - mDNSu8 *qptr = query.data; - const mDNSu8 *const limit = query.data + sizeof(query.data); - InitializeDNSMessage(&query.h, q->TargetQID, QueryFlags); - qptr = putQuestion(&query, qptr, limit, &q->qname, q->qtype, q->qclass); - mDNSSendDNSMessage(m, &query, qptr, mDNSInterface_Any, &q->Target, q->TargetPort); - q->ThisQInterval *= 2; - q->LastQTime = m->timenow; - q->LastQTxTime = m->timenow; - q->RecentAnswers = 0; - q->SendQNow = mDNSNULL; + mDNSu8 *qptr = m->omsg.data; + const mDNSu8 *const limit = m->omsg.data + sizeof(m->omsg.data); + InitializeDNSMessage(&m->omsg.h, q->TargetQID, QueryFlags); + qptr = putQuestion(&m->omsg, qptr, limit, &q->qname, q->qtype, q->qclass); + mDNSSendDNSMessage(m, &m->omsg, qptr, mDNSInterface_Any, &q->Target, q->TargetPort, -1, mDNSNULL); + q->ThisQInterval *= 2; + q->LastQTime = m->timenow; + q->LastQTxTime = m->timenow; + q->RecentAnswerPkts = 0; + q->SendQNow = mDNSNULL; m->ExpectUnicastResponse = m->timenow; } @@ -2870,8 +3374,8 @@ mDNSlocal void SendQueries(mDNS *const m) // then we consider it recent enough that we don't need to do an identical query ourselves. ExpireDupSuppressInfo(q->DupSuppress, m->timenow - q->ThisQInterval/2); - q->LastQTxTime = m->timenow; - q->RecentAnswers = 0; + q->LastQTxTime = m->timenow; + q->RecentAnswerPkts = 0; } // For all questions (not just the ones we're sending) check what the next scheduled event will be SetNextQueryTime(m,q); @@ -2917,7 +3421,7 @@ mDNSlocal void SendQueries(mDNS *const m) for (r2 = m->DuplicateRecords; r2; r2=r2->next) if (r2->resrec.RecordType == kDNSRecordTypeUnique && RecordIsLocalDuplicate(r2, rr)) r2->ProbeCount = 0; - CompleteProbing(m, rr); + AcknowledgeRecord(m, rr); } } } @@ -2927,7 +3431,7 @@ mDNSlocal void SendQueries(mDNS *const m) AuthRecord *rr = m->CurrentRecord; m->CurrentRecord = rr->next; if (rr->resrec.RecordType == kDNSRecordTypeUnique && rr->ProbeCount == 0) - CompleteProbing(m, rr); + AcknowledgeRecord(m, rr); } } @@ -2935,9 +3439,8 @@ mDNSlocal void SendQueries(mDNS *const m) while (intf) { AuthRecord *rr; - DNSMessage query; - mDNSu8 *queryptr = query.data; - InitializeDNSMessage(&query.h, zeroID, QueryFlags); + mDNSu8 *queryptr = m->omsg.data; + InitializeDNSMessage(&m->omsg.h, zeroID, QueryFlags); if (KnownAnswerList) verbosedebugf("SendQueries: KnownAnswerList set... Will continue from previous packet"); if (!KnownAnswerList) { @@ -2949,12 +3452,12 @@ mDNSlocal void SendQueries(mDNS *const m) for (q = m->Questions; q; q=q->next) if (q->SendQNow == intf->InterfaceID) { - debugf("SendQueries: %s question for %##s (%s) at %lu forecast total %lu", + debugf("SendQueries: %s question for %##s (%s) at %d forecast total %d", SuppressOnThisInterface(q->DupSuppress, intf) ? "Suppressing" : "Putting ", - q->qname.c, DNSTypeName(q->qtype), queryptr - query.data, queryptr + answerforecast - query.data); + q->qname.c, DNSTypeName(q->qtype), queryptr - m->omsg.data, queryptr + answerforecast - m->omsg.data); // If we're suppressing this question, or we successfully put it, update its SendQNow state if (SuppressOnThisInterface(q->DupSuppress, intf) || - BuildQuestion(m, &query, &queryptr, q, &kalistptr, &answerforecast)) + BuildQuestion(m, &m->omsg, &queryptr, q, &kalistptr, &answerforecast)) q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf); } @@ -2962,10 +3465,10 @@ mDNSlocal void SendQueries(mDNS *const m) for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->SendRNow == intf->InterfaceID) { - mDNSBool ucast = (rr->ProbeCount >= DefaultProbeCountForTypeUnique-1) && m->CanReceiveUnicast; + mDNSBool ucast = (rr->ProbeCount >= DefaultProbeCountForTypeUnique-1) && m->CanReceiveUnicastOn5353; mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); - const mDNSu8 *const limit = query.data + ((query.h.numQuestions) ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData); - mDNSu8 *newptr = putQuestion(&query, queryptr, limit, &rr->resrec.name, kDNSQType_ANY, (mDNSu16)(rr->resrec.rrclass | ucbit)); + const mDNSu8 *const limit = m->omsg.data + ((m->omsg.h.numQuestions) ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData); + mDNSu8 *newptr = putQuestion(&m->omsg, queryptr, limit, &rr->resrec.name, kDNSQType_ANY, (mDNSu16)(rr->resrec.rrclass | ucbit)); // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) mDNSu32 forecast = answerforecast + 12 + rr->resrec.rdestimate; if (newptr && newptr + forecast < limit) @@ -2979,7 +3482,7 @@ mDNSlocal void SendQueries(mDNS *const m) else { verbosedebugf("SendQueries: Retracting Question %##s (%s)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); - query.h.numQuestions--; + m->omsg.h.numQuestions--; } } } @@ -2989,10 +3492,10 @@ mDNSlocal void SendQueries(mDNS *const m) { CacheRecord *rr = KnownAnswerList; mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond; - mDNSu8 *newptr = PutResourceRecordTTL(&query, queryptr, &query.h.numAnswers, &rr->resrec, rr->resrec.rroriginalttl - SecsSinceRcvd); + mDNSu8 *newptr = PutResourceRecordTTL(&m->omsg, queryptr, &m->omsg.h.numAnswers, &rr->resrec, rr->resrec.rroriginalttl - SecsSinceRcvd); if (newptr) { - verbosedebugf("SendQueries: Put %##s (%s) at %lu - %lu", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), queryptr - query.data, newptr - query.data); + verbosedebugf("SendQueries: Put %##s (%s) at %d - %d", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), queryptr - m->omsg.data, newptr - m->omsg.data); queryptr = newptr; KnownAnswerList = rr->NextInKAList; rr->NextInKAList = mDNSNULL; @@ -3001,9 +3504,9 @@ mDNSlocal void SendQueries(mDNS *const m) { // If we ran out of space and we have more than one question in the packet, that's an error -- // we shouldn't have put more than one question if there was a risk of us running out of space. - if (query.h.numQuestions > 1) - LogMsg("SendQueries: Put %d answers; No more space for known answers", query.h.numAnswers); - query.h.flags.b[0] |= kDNSFlag0_TC; + if (m->omsg.h.numQuestions > 1) + LogMsg("SendQueries: Put %d answers; No more space for known answers", m->omsg.h.numAnswers); + m->omsg.h.flags.b[0] |= kDNSFlag0_TC; break; } } @@ -3011,24 +3514,24 @@ mDNSlocal void SendQueries(mDNS *const m) for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->IncludeInProbe) { - mDNSu8 *newptr = PutResourceRecord(&query, queryptr, &query.h.numAuthorities, &rr->resrec); + mDNSu8 *newptr = PutResourceRecord(&m->omsg, queryptr, &m->omsg.h.numAuthorities, &rr->resrec); rr->IncludeInProbe = mDNSfalse; if (newptr) queryptr = newptr; else LogMsg("SendQueries: How did we fail to have space for the Update record %##s (%s)?", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); } - if (queryptr > query.data) + if (queryptr > m->omsg.data) { - if ((query.h.flags.b[0] & kDNSFlag0_TC) && query.h.numQuestions > 1) - LogMsg("SendQueries: Should not have more than one question (%d) in a truncated packet\n", query.h.numQuestions); + if ((m->omsg.h.flags.b[0] & kDNSFlag0_TC) && m->omsg.h.numQuestions > 1) + LogMsg("SendQueries: Should not have more than one question (%d) in a truncated packet", m->omsg.h.numQuestions); debugf("SendQueries: Sending %d Question%s %d Answer%s %d Update%s on %p", - query.h.numQuestions, query.h.numQuestions == 1 ? "" : "s", - query.h.numAnswers, query.h.numAnswers == 1 ? "" : "s", - query.h.numAuthorities, query.h.numAuthorities == 1 ? "" : "s", intf->InterfaceID); - if (intf->IPv4Available) mDNSSendDNSMessage(m, &query, queryptr, intf->InterfaceID, &AllDNSLinkGroup_v4, MulticastDNSPort); - if (intf->IPv6Available) mDNSSendDNSMessage(m, &query, queryptr, intf->InterfaceID, &AllDNSLinkGroup_v6, MulticastDNSPort); - if (!m->SuppressSending) m->SuppressSending = (m->timenow + mDNSPlatformOneSecond/10) | 1; // OR with one to ensure non-zero + m->omsg.h.numQuestions, m->omsg.h.numQuestions == 1 ? "" : "s", + m->omsg.h.numAnswers, m->omsg.h.numAnswers == 1 ? "" : "s", + m->omsg.h.numAuthorities, m->omsg.h.numAuthorities == 1 ? "" : "s", intf->InterfaceID); + if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, queryptr, intf->InterfaceID, &AllDNSLinkGroup_v4, MulticastDNSPort, -1, mDNSNULL); + if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, queryptr, intf->InterfaceID, &AllDNSLinkGroup_v6, MulticastDNSPort, -1, mDNSNULL); + if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + mDNSPlatformOneSecond/10); if (++pktcount >= 1000) { LogMsg("SendQueries exceeded loop limit %d: giving up", pktcount); break; } // There might be more records left in the known answer list, or more questions to send @@ -3050,7 +3553,7 @@ mDNSlocal void SendQueries(mDNS *const m) AuthRecord *rr; for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->SendRNow) - { LogMsg("SendQueries: No active interface to send: %s", GetRRDisplayString(m, rr)); rr->SendRNow = mDNSNULL; } + { LogMsg("SendQueries: No active interface to send: %s", ARDisplayString(m, rr)); rr->SendRNow = mDNSNULL; } } } @@ -3067,8 +3570,11 @@ mDNSlocal void AnswerQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, C verbosedebugf("AnswerQuestionWithResourceRecord:%4lu %s TTL%6lu %##s (%s)", q->CurrentAnswers, AddRecord ? "Add" : "Rmv", rr->resrec.rroriginalttl, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + // Note: Use caution here. In the case of records with rr->DelayDelivery set, AnswerQuestionWithResourceRecord(... mDNStrue) + // may be called twice, once when the record is received, and again when it's time to notify local clients. + // If any counters or similar are added here, care must be taken to ensure that they are not double-incremented by this. + rr->LastUsed = m->timenow; - rr->UseCount++; if (ActiveQuestion(q) && rr->CRActiveQuestion != q) { if (!rr->CRActiveQuestion) m->rrcache_active++; // If not previously active, increment rrcache_active count @@ -3076,15 +3582,47 @@ mDNSlocal void AnswerQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, C SetNextCacheCheckTime(m, rr); } - // CAUTION: MUST NOT do anything more with q after calling q->Callback(), because the client's callback function - // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. - // Right now the only routines that call AnswerQuestionWithResourceRecord() are CacheRecordAdd(), CacheRecordRmv() - // and AnswerNewQuestion(), and all of them use the "m->CurrentQuestion" mechanism to protect against questions - // being deleted out from under them. + if (rr->DelayDelivery) return; // We'll come back later when CacheRecordDeferredAdd() calls us + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback if (q->QuestionCallback) q->QuestionCallback(m, q, &rr->resrec, AddRecord); m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + // CAUTION: MUST NOT do anything more with q after calling q->QuestionCallback(), because the client's callback function + // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. + // Right now the only routines that call AnswerQuestionWithResourceRecord() are CacheRecordAdd(), CacheRecordRmv() + // and AnswerNewQuestion(), and all of them use the "m->CurrentQuestion" mechanism to protect against questions + // being deleted out from under them. + } + +mDNSlocal void CacheRecordDeferredAdd(mDNS *const m, CacheRecord *rr) + { + rr->DelayDelivery = 0; + if (m->CurrentQuestion) LogMsg("CacheRecordDeferredAdd ERROR m->CurrentQuestion already set"); + m->CurrentQuestion = m->Questions; + while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) + { + DNSQuestion *q = m->CurrentQuestion; + m->CurrentQuestion = q->next; + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue); + } + m->CurrentQuestion = mDNSNULL; + } + +mDNSlocal mDNSs32 CheckForSoonToExpireRecords(mDNS *const m, const domainname *const name, const mDNSu32 namehash, const mDNSu32 slot) + { + const mDNSs32 threshhold = m->timenow + mDNSPlatformOneSecond; // See if there are any records expiring within one second + const mDNSs32 start = m->timenow - 0x10000000; + mDNSs32 delay = start; + CacheRecord *rr; + for (rr = m->rrcache_hash[slot]; rr; rr=rr->next) + if (rr->resrec.namehash == namehash && SameDomainName(&rr->resrec.name, name)) + if (threshhold - RRExpireTime(rr) >= 0) // If we have records about to expire within a second + if (delay - RRExpireTime(rr) < 0) // then delay until after they've been deleted + delay = RRExpireTime(rr); + if (delay - start > 0) return(delay ? delay : 1); // Make sure we return non-zero if we want to delay + else return(0); } // CacheRecordAdd is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call. @@ -3110,19 +3648,32 @@ mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr) // We must be at least at the eight-second interval to do this. If we're at the four-second interval, or less, // there's not much benefit accelerating because we will anyway send another query within a few seconds. // The first reset query is sent out randomized over the next four seconds to reduce possible synchronization between machines. - if (ActiveQuestion(q) && ++q->RecentAnswers >= 10 && - q->ThisQInterval > InitialQuestionInterval*16 && m->timenow - q->LastQTxTime < mDNSPlatformOneSecond) + if (q->LastAnswerPktNum != m->PktNum) { - LogMsg("CacheRecordAdd: %##s (%s) got immediate answer burst; restarting exponential backoff sequence", - q->qname.c, DNSTypeName(q->qtype)); - q->LastQTime = m->timenow - InitialQuestionInterval + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*4); - q->ThisQInterval = InitialQuestionInterval; - SetNextQueryTime(m,q); + q->LastAnswerPktNum = m->PktNum; + if (ActiveQuestion(q) && ++q->RecentAnswerPkts >= 10 && + q->ThisQInterval > InitialQuestionInterval*16 && m->timenow - q->LastQTxTime < mDNSPlatformOneSecond) + { + LogMsg("CacheRecordAdd: %##s (%s) got immediate answer burst; restarting exponential backoff sequence", + q->qname.c, DNSTypeName(q->qtype)); + q->LastQTime = m->timenow - InitialQuestionInterval + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*4); + q->ThisQInterval = InitialQuestionInterval; + SetNextQueryTime(m,q); + } } verbosedebugf("CacheRecordAdd %p %##s (%s) %lu", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), rr->resrec.rroriginalttl); q->CurrentAnswers++; if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++; if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++; + if (q->CurrentAnswers > 4000) + { + static int msgcount = 0; + if (msgcount++ < 10) + LogMsg("CacheRecordAdd: %##s (%s) has %d answers; shedding records to resist DOS attack", + q->qname.c, DNSTypeName(q->qtype), q->CurrentAnswers); + rr->resrec.rroriginalttl = 1; + rr->UnansweredQueries = MaxUnansweredQueries; + } AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue); // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord() } @@ -3179,7 +3730,9 @@ mDNSlocal void ReleaseCacheRR(mDNS *const m, CacheRecord *r) m->rrcache_totalused--; } -mDNSlocal void CheckCacheExpiration(mDNS *const m, mDNSu32 slot) +// Note: We want to be careful that we deliver all the CacheRecordRmv calls before delivering CacheRecordDeferredAdd calls +// The in-order nature of the cache lists ensures that all callbacks for old records are delivered before callbacks for newer records +mDNSlocal void CheckCacheExpiration(mDNS *const m, const mDNSu32 slot) { CacheRecord **rp = &(m->rrcache_hash[slot]); @@ -3193,7 +3746,7 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, mDNSu32 slot) if (m->timenow - event >= 0) // If expired, delete it { *rp = rr->next; // Cut it from the list - verbosedebugf("CheckCacheExpiration: Deleting %s", GetRRDisplayString(m, rr)); + verbosedebugf("CheckCacheExpiration: Deleting %s", CRDisplayString(m, rr)); if (rr->CRActiveQuestion) // If this record has one or more active questions, tell them it's going away { CacheRecordRmv(m, rr); @@ -3204,18 +3757,24 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, mDNSu32 slot) } else // else, not expired; see if we need to query { - if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries) + if (rr->DelayDelivery && rr->DelayDelivery - m->timenow > 0) + event = rr->DelayDelivery; + else { - if (m->timenow - rr->NextRequiredQuery < 0) // If not yet time for next query - event = rr->NextRequiredQuery; // then just record when we want the next query - else // else trigger our question to go out now + if (rr->DelayDelivery) CacheRecordDeferredAdd(m, rr); + if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries) { - // Set NextScheduledQuery to timenow so that SendQueries() will run. - // SendQueries() will see that we have records close to expiration, and send FEQs for them. - m->NextScheduledQuery = m->timenow; - // After sending the query we'll increment UnansweredQueries and call SetNextCacheCheckTime(), - // which will correctly update m->NextCacheCheck for us - event = m->timenow + 0x3FFFFFFF; + if (m->timenow - rr->NextRequiredQuery < 0) // If not yet time for next query + event = rr->NextRequiredQuery; // then just record when we want the next query + else // else trigger our question to go out now + { + // Set NextScheduledQuery to timenow so that SendQueries() will run. + // SendQueries() will see that we have records close to expiration, and send FEQs for them. + m->NextScheduledQuery = m->timenow; + // After sending the query we'll increment UnansweredQueries and call SetNextCacheCheckTime(), + // which will correctly update m->NextCacheCheck for us + event = m->timenow + 0x3FFFFFFF; + } } } if (m->NextCacheCheck - (event + CacheCheckGracePeriod(rr)) > 0) @@ -3223,7 +3782,7 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, mDNSu32 slot) rp = &rr->next; } } - if (m->rrcache_tail[slot] != rp) debugf("CheckCacheExpiration: Updating m->rrcache_tail[%d] from %p to %p", slot, m->rrcache_tail[slot], rp); + if (m->rrcache_tail[slot] != rp) verbosedebugf("CheckCacheExpiration: Updating m->rrcache_tail[%lu] from %p to %p", slot, m->rrcache_tail[slot], rp); m->rrcache_tail[slot] = rp; m->lock_rrcache = 0; } @@ -3233,7 +3792,7 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) mDNSBool ShouldQueryImmediately = mDNStrue; CacheRecord *rr; DNSQuestion *q = m->NewQuestions; // Grab the question we're going to answer - mDNSu32 slot = HashSlot(&q->qname); + const mDNSu32 slot = HashSlot(&q->qname); verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); @@ -3262,7 +3821,8 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) // If this record set is marked unique, then that means we can reasonably assume we have the whole set // -- we don't need to rush out on the network and query immediately to see if there are more answers out there - if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ShouldQueryImmediately = mDNSfalse; + if ((rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) || (q->ExpectUnique)) + ShouldQueryImmediately = mDNSfalse; q->CurrentAnswers++; if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++; if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++; @@ -3287,7 +3847,7 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) mDNSlocal void AnswerLocalOnlyQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, AuthRecord *rr, mDNSBool AddRecord) { // Indicate that we've given at least one positive answer for this record, so we should be prepared to send a goodbye for it - if (AddRecord) rr->AnnounceCount = InitialAnnounceCount - 1; + if (AddRecord) rr->RequireGoodbye = mDNStrue; m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback if (q->QuestionCallback) q->QuestionCallback(m, q, &rr->resrec, AddRecord); @@ -3301,9 +3861,10 @@ mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m) debugf("AnswerNewLocalOnlyQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - if (m->CurrentQuestion) LogMsg("AnswerNewQuestion ERROR m->CurrentQuestion already set"); + if (m->CurrentQuestion) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentQuestion already set"); m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted + if (m->CurrentRecord) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set"); m->CurrentRecord = m->LocalOnlyRecords; while (m->CurrentRecord && m->CurrentRecord != m->NewLocalOnlyRecords) { @@ -3318,6 +3879,7 @@ mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m) } m->CurrentQuestion = mDNSNULL; + m->CurrentRecord = mDNSNULL; } mDNSlocal void AnswerLocalOnlyQuestions(mDNS *const m, AuthRecord *rr, mDNSBool AddRecord) @@ -3332,7 +3894,7 @@ mDNSlocal void AnswerLocalOnlyQuestions(mDNS *const m, AuthRecord *rr, mDNSBool { debugf("AnswerLocalOnlyQuestions %p %##s (%s) %lu", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), rr->resrec.rroriginalttl); AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, AddRecord); - // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord() + // MUST NOT dereference q again after calling AnswerLocalOnlyQuestionWithResourceRecord() } } m->CurrentQuestion = mDNSNULL; @@ -3411,7 +3973,7 @@ mDNSlocal CacheRecord *GetFreeCacheRR(mDNS *const m, mDNSu16 RDLength) ReleaseCacheRR(m, rr); } } - if (m->rrcache_tail[slot] != rp) debugf("GetFreeCacheRR: Updating m->rrcache_tail[%d] from %p to %p", slot, m->rrcache_tail[slot], rp); + if (m->rrcache_tail[slot] != rp) verbosedebugf("GetFreeCacheRR: Updating m->rrcache_tail[%lu] from %p to %p", slot, m->rrcache_tail[slot], rp); m->rrcache_tail[slot] = rp; } #if MDNS_DEBUGMSGS @@ -3434,9 +3996,9 @@ mDNSlocal CacheRecord *GetFreeCacheRR(mDNS *const m, mDNSu16 RDLength) else m->rrcache_report += 100; } mDNSPlatformMemZero(r, sizeof(*r)); - r->resrec.rdata = (RData*)&r->rdatastorage; // By default, assume we're usually going to be using local storage + r->resrec.rdata = (RData*)&r->rdatastorage; // By default, assume we're usually going to be using local storage - if (RDLength > InlineCacheRDSize) // If RDLength is too big, allocate extra storage + if (RDLength > InlineCacheRDSize) // If RDLength is too big, allocate extra storage { r->resrec.rdata = (RData*)mDNSPlatformMemAllocate(sizeofRDataHeader + RDLength); if (r->resrec.rdata) r->resrec.rdata->MaxRDLength = r->resrec.rdlength = RDLength; @@ -3462,89 +4024,26 @@ mDNSlocal void PurgeCacheResourceRecord(mDNS *const m, CacheRecord *rr) SetNextCacheCheckTime(m, rr); } -mDNSlocal void mDNS_Lock(mDNS *const m) +mDNSexport mDNSs32 mDNS_TimeNow(const mDNS *const m) { - // MUST grab the platform lock FIRST! + mDNSs32 time; mDNSPlatformLock(m); - - // Normally, mDNS_reentrancy is zero and so is mDNS_busy - // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too - // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one - // If mDNS_busy != mDNS_reentrancy that's a bad sign - if (m->mDNS_busy != m->mDNS_reentrancy) - LogMsg("mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); - - // If this is an initial entry into the mDNSCore code, set m->timenow - // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set - if (m->mDNS_busy == 0) - { - if (m->timenow) - LogMsg("mDNS_Lock: m->timenow already set (%ld/%ld)", m->timenow, mDNSPlatformTimeNow() + m->timenow_adjust); - m->timenow = mDNSPlatformTimeNow() + m->timenow_adjust; - if (m->timenow == 0) m->timenow = 1; - } - else if (m->timenow == 0) - { - LogMsg("mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", m->mDNS_busy); - m->timenow = mDNSPlatformTimeNow() + m->timenow_adjust; - if (m->timenow == 0) m->timenow = 1; - } - - if (m->timenow_last - m->timenow > 0) + if (m->mDNS_busy) { - m->timenow_adjust += m->timenow_last - m->timenow; - LogMsg("mDNSPlatformTimeNow went backwards by %ld ticks; setting correction factor to %ld", m->timenow_last - m->timenow, m->timenow_adjust); - m->timenow = m->timenow_last; + LogMsg("mDNS_TimeNow called while holding mDNS lock. This is incorrect. Code protected by lock should just use m->timenow."); + if (!m->timenow) LogMsg("mDNS_TimeNow: m->mDNS_busy is %ld but m->timenow not set", m->mDNS_busy); } - m->timenow_last = m->timenow; - - // Increment mDNS_busy so we'll recognise re-entrant calls - m->mDNS_busy++; - } - -mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m) - { - mDNSs32 e = m->timenow + 0x78000000; - if (m->mDNSPlatformStatus != mStatus_NoError || m->SleepState) return(e); - if (m->NewQuestions) return(m->timenow); - if (m->NewLocalOnlyQuestions) return(m->timenow); - if (m->NewLocalOnlyRecords) return(m->timenow); - if (m->DiscardLocalOnlyRecords) return(m->timenow); - if (m->SuppressSending) return(m->SuppressSending); - if (e - m->uDNS_info.nextevent > 0) e = m->uDNS_info.nextevent; - if (e - m->NextCacheCheck > 0) e = m->NextCacheCheck; - if (e - m->NextScheduledQuery > 0) e = m->NextScheduledQuery; - if (e - m->NextScheduledProbe > 0) e = m->NextScheduledProbe; - if (e - m->NextScheduledResponse > 0) e = m->NextScheduledResponse; - return(e); - } - -mDNSlocal void mDNS_Unlock(mDNS *const m) - { - // Decrement mDNS_busy - m->mDNS_busy--; - // Check for locking failures - if (m->mDNS_busy != m->mDNS_reentrancy) - LogMsg("mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); - - // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow - if (m->mDNS_busy == 0) - { - m->NextScheduledEvent = GetNextScheduledEvent(m); - if (m->timenow == 0) LogMsg("mDNS_Unlock: ERROR! m->timenow aready zero"); - m->timenow = 0; - } - - // MUST release the platform lock LAST! + if (m->timenow) time = m->timenow; + else time = mDNSPlatformRawTime() + m->timenow_adjust; mDNSPlatformUnlock(m); + return(time); } mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) { mDNS_Lock(m); // Must grab lock before trying to read m->timenow - if (m->timenow - m->NextScheduledEvent >= 0) { int i; @@ -3567,7 +4066,11 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) } // 4. See if we can answer any of our new local questions from the cache - for (i=0; m->NewQuestions && i<1000; i++) AnswerNewQuestion(m); + for (i=0; m->NewQuestions && i<1000; i++) + { + if (m->NewQuestions->DelayAnswering && m->timenow - m->NewQuestions->DelayAnswering < 0) break; + AnswerNewQuestion(m); + } if (i >= 1000) debugf("mDNS_Execute: AnswerNewQuestion exceeded loop limit"); for (i=0; m->DiscardLocalOnlyRecords && i<1000; i++) DiscardLocalOnlyRecords(m); @@ -3636,7 +4139,9 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) // callback function should call mDNS_Execute() (and ignore the return value, which may already be stale // by the time it gets to the timer callback function). +#ifndef UNICAST_DISABLED uDNS_Execute(m); +#endif mDNS_Unlock(m); // Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value return(m->NextScheduledEvent); } @@ -3663,10 +4168,12 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleepstate) if (sleepstate) { - uDNS_SuspendLLQs(m); +#ifndef UNICAST_DISABLED + uDNS_Sleep(m); +#endif // Mark all the records we need to deregister and send them for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.RecordType == kDNSRecordTypeShared && rr->AnnounceCount < InitialAnnounceCount) + if (rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) rr->ImmedAnswer = mDNSInterfaceMark; SendResponses(m); } @@ -3676,15 +4183,16 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleepstate) mDNSu32 slot; CacheRecord *cr; - uDNS_RestartLLQs(m); - +#ifndef UNICAST_DISABLED + uDNS_Wake(m); +#endif // 1. Retrigger all our questions for (q = m->Questions; q; q=q->next) // Scan our list of questions if (ActiveQuestion(q)) { - q->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question - q->LastQTime = m->timenow - q->ThisQInterval; - q->RecentAnswers = 0; + q->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question + q->LastQTime = m->timenow - q->ThisQInterval; + q->RecentAnswerPkts = 0; ExpireDupSuppressInfo(q->DupSuppress, m->timenow); m->NextScheduledQuery = m->timenow; } @@ -3699,13 +4207,11 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleepstate) for (rr = m->ResourceRecords; rr; rr=rr->next) { if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique; - rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); - if (rr->AnnounceCount < ReannounceCount) - rr->AnnounceCount = ReannounceCount; - rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); + rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); + rr->AnnounceCount = InitialAnnounceCount; + rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); InitializeLastAPTime(m, rr); } - } mDNS_Unlock(m); @@ -3717,21 +4223,6 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleepstate) #pragma mark - Packet Reception Functions #endif -mDNSlocal void AddRecordToResponseList(AuthRecord ***nrpp, AuthRecord *rr, AuthRecord *add) - { - if (rr->NextResponse == mDNSNULL && *nrpp != &rr->NextResponse) - { - **nrpp = rr; - // NR_AdditionalTo must point to a record with NR_AnswerTo set (and not NR_AdditionalTo) - // If 'add' does not meet this requirement, then follow its NR_AdditionalTo pointer to a record that does - // The referenced record will definitely be acceptable (by recursive application of this rule) - if (add && add->NR_AdditionalTo) add = add->NR_AdditionalTo; - rr->NR_AdditionalTo = add; - *nrpp = &rr->NextResponse; - } - debugf("AddRecordToResponseList: %##s (%s) already in list", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); - } - #define MustSendRecord(RR) ((RR)->NR_AnswerTo || (RR)->NR_AdditionalTo) mDNSlocal mDNSu8 *GenerateUnicastResponse(const DNSMessage *const query, const mDNSu8 *const end, @@ -3913,17 +4404,16 @@ mDNSlocal void ResolveSimultaneousProbe(mDNS *const m, const DNSMessage *const q for (i = 0; i < query->h.numAuthorities; i++) { - LargeCacheRecord pkt; - ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, 0, &pkt); + ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, &m->rec); if (!ptr) break; - if (ResourceRecordAnswersQuestion(&pkt.r.resrec, q)) + if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q)) { FoundUpdate = mDNStrue; - if (PacketRRConflict(m, our, &pkt.r)) + if (PacketRRConflict(m, our, &m->rec.r)) { - int result = (int)our->resrec.rrclass - (int)pkt.r.resrec.rrclass; - if (!result) result = (int)our->resrec.rrtype - (int)pkt.r.resrec.rrtype; - if (!result) result = CompareRData(our, &pkt.r); + int result = (int)our->resrec.rrclass - (int)m->rec.r.resrec.rrclass; + if (!result) result = (int)our->resrec.rrtype - (int)m->rec.r.resrec.rrtype; + if (!result) result = CompareRData(our, &m->rec.r); switch (result) { case 1: debugf("ResolveSimultaneousProbe: %##s (%s): We won", our->resrec.name.c, DNSTypeName(our->resrec.rrtype)); @@ -3931,13 +4421,16 @@ mDNSlocal void ResolveSimultaneousProbe(mDNS *const m, const DNSMessage *const q case 0: break; case -1: debugf("ResolveSimultaneousProbe: %##s (%s): We lost", our->resrec.name.c, DNSTypeName(our->resrec.rrtype)); mDNS_Deregister_internal(m, our, mDNS_Dereg_conflict); - return; + goto exit; } } } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it } if (!FoundUpdate) debugf("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our->resrec.name.c, DNSTypeName(our->resrec.rrtype)); +exit: + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it } mDNSlocal CacheRecord *FindIdenticalRecordInCache(const mDNS *const m, ResourceRecord *pktrr) @@ -3950,25 +4443,22 @@ mDNSlocal CacheRecord *FindIdenticalRecordInCache(const mDNS *const m, ResourceR // ProcessQuery examines a received query to see if we have any answers to give mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, const mDNSu8 *const end, - const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, mDNSBool QueryWasMulticast, + const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, mDNSBool QueryWasMulticast, mDNSBool QueryWasLocalUnicast, DNSMessage *const response) { - AuthRecord *ResponseRecords = mDNSNULL; - AuthRecord **nrp = &ResponseRecords; - CacheRecord *ExpectedAnswers = mDNSNULL; // Records in our cache we expect to see updated - CacheRecord **eap = &ExpectedAnswers; - DNSQuestion *DupQuestions = mDNSNULL; // Our questions that are identical to questions in this packet - DNSQuestion **dqp = &DupQuestions; - mDNSs32 delayresponse = 0; - mDNSBool HaveUnicastAnswer = mDNSfalse; - const mDNSu8 *ptr = query->data; - mDNSu8 *responseptr = mDNSNULL; - AuthRecord *rr, *rr2; + AuthRecord *ResponseRecords = mDNSNULL; + AuthRecord **nrp = &ResponseRecords; + CacheRecord *ExpectedAnswers = mDNSNULL; // Records in our cache we expect to see updated + CacheRecord **eap = &ExpectedAnswers; + DNSQuestion *DupQuestions = mDNSNULL; // Our questions that are identical to questions in this packet + DNSQuestion **dqp = &DupQuestions; + mDNSs32 delayresponse = 0; + mDNSBool SendLegacyResponse = mDNSfalse; + const mDNSu8 *ptr = query->data; + mDNSu8 *responseptr = mDNSNULL; + AuthRecord *rr; int i; - // If TC flag is set, it means we should expect that additional known answers may be coming in another packet. - if (query->h.flags.b[0] & kDNSFlag0_TC) delayresponse = mDNSPlatformOneSecond; // Divided by 50 = 20ms - // *** // *** 1. Parse Question Section and mark potential answers // *** @@ -4004,7 +4494,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con { rr = m->CurrentRecord; m->CurrentRecord = rr->next; - if (ResourceRecordAnswersQuestion(&rr->resrec, &pktq)) + if (ResourceRecordAnswersQuestion(&rr->resrec, &pktq) && (QueryWasMulticast || QueryWasLocalUnicast || rr->AllowRemoteQuery)) { if (rr->resrec.RecordType == kDNSRecordTypeUnique) ResolveSimultaneousProbe(m, query, end, &pktq, rr); @@ -4012,9 +4502,9 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con { NumAnswersForThisQuestion++; // Notes: - // NR_AnswerTo pointing into query packet means "answer via unicast" - // (may also choose to do multicast as well) - // NR_AnswerTo == ~0 means "definitely answer via multicast" (can't downgrade to unicast later) + // NR_AnswerTo pointing into query packet means "answer via immediate legacy unicast" (may also choose to multicast as well) + // NR_AnswerTo == (mDNSu8*)~1 means "answer via delayed unicast" (to modern querier) + // NR_AnswerTo == (mDNSu8*)~0 means "definitely answer via multicast" (can't downgrade to unicast later) if (QuestionNeedsMulticastResponse) { // We only mark this question for sending if it is at least one second since the last time we multicast it @@ -4024,20 +4514,21 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con (rr->LastMCInterface != mDNSInterfaceMark && rr->LastMCInterface != InterfaceID)) rr->NR_AnswerTo = (mDNSu8*)~0; } - else if (!rr->NR_AnswerTo) rr->NR_AnswerTo = ptr; + else if (!rr->NR_AnswerTo) rr->NR_AnswerTo = LegacyQuery ? ptr : (mDNSu8*)~1; } } } + // If we couldn't answer this question, someone else might be able to, + // so use random delay on response to reduce collisions + if (NumAnswersForThisQuestion == 0) delayresponse = mDNSPlatformOneSecond; // Divided by 50 = 20ms + // We only do the following accelerated cache expiration processing and duplicate question suppression processing // for multicast queries with multicast responses. // For any query generating a unicast response we don't do this because we can't assume we will see the response if (QuestionNeedsMulticastResponse) { CacheRecord *rr; - // If we couldn't answer this question, someone else might be able to, - // so use random delay on response to reduce collisions - if (NumAnswersForThisQuestion == 0) delayresponse = mDNSPlatformOneSecond; // Divided by 50 = 20ms // Make a list indicating which of our own cache records we expect to see updated as a result of this query // Note: Records larger than 1K are not habitually multicast, so don't expect those to be updated @@ -4082,25 +4573,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con // *** // *** 3. Add additional records // *** - for (rr=ResponseRecords; rr; rr=rr->NextResponse) // For each record we plan to put - { - // (Note: This is an "if", not a "while". If we add a record, we'll find it again - // later in the "for" loop, and we will follow further "additional" links then.) - if (rr->Additional1 && ResourceRecordIsValidInterfaceAnswer(rr->Additional1, InterfaceID)) - AddRecordToResponseList(&nrp, rr->Additional1, rr); - - if (rr->Additional2 && ResourceRecordIsValidInterfaceAnswer(rr->Additional2, InterfaceID)) - AddRecordToResponseList(&nrp, rr->Additional2, rr); - - // For SRV records, automatically add the Address record(s) for the target host - if (rr->resrec.rrtype == kDNSType_SRV) - for (rr2=m->ResourceRecords; rr2; rr2=rr2->next) // Scan list of resource records - if (RRTypeIsAddressType(rr2->resrec.rrtype) && // For all address records (A/AAAA) ... - ResourceRecordIsValidInterfaceAnswer(rr2, InterfaceID) && // ... which are valid for answer ... - rr->resrec.rdnamehash == rr2->resrec.namehash && // ... whose name is the name of the SRV target - SameDomainName(&rr->resrec.rdata->u.srv.target, &rr2->resrec.name)) - AddRecordToResponseList(&nrp, rr2, rr); - } + AddAdditionalsToResponseList(m, ResponseRecords, &nrp, InterfaceID); // *** // *** 4. Parse Answer Section and cancel any records disallowed by Known-Answer list @@ -4108,26 +4581,25 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con for (i=0; ih.numAnswers; i++) // For each record in the query's answer section... { // Get the record... - LargeCacheRecord pkt; AuthRecord *rr; CacheRecord *ourcacherr; - ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt); + ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &m->rec); if (!ptr) goto exit; // See if this Known-Answer suppresses any of our currently planned answers for (rr=ResponseRecords; rr; rr=rr->NextResponse) - if (MustSendRecord(rr) && ShouldSuppressKnownAnswer(&pkt.r, rr)) + if (MustSendRecord(rr) && ShouldSuppressKnownAnswer(&m->rec.r, rr)) { rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; } // See if this Known-Answer suppresses any previously scheduled answers (for multi-packet KA suppression) for (rr=m->ResourceRecords; rr; rr=rr->next) { // If we're planning to send this answer on this interface, and only on this interface, then allow KA suppression - if (rr->ImmedAnswer == InterfaceID && ShouldSuppressKnownAnswer(&pkt.r, rr)) + if (rr->ImmedAnswer == InterfaceID && ShouldSuppressKnownAnswer(&m->rec.r, rr)) { if (srcaddr->type == mDNSAddrType_IPv4) { - if (mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = zeroIPAddr; + if (mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = zerov4Addr; } else if (srcaddr->type == mDNSAddrType_IPv6) { @@ -4135,9 +4607,10 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con } if (mDNSIPv4AddressIsZero(rr->v4Requester) && mDNSIPv6AddressIsZero(rr->v6Requester)) { - rr->ImmedAnswer = mDNSNULL; + rr->ImmedAnswer = mDNSNULL; + rr->ImmedUnicast = mDNSfalse; #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES - LogMsg("Suppressed after%4d: %s", m->timenow - rr->ImmedAnswerMarkTime, GetRRDisplayString(m, rr)); + LogMsg("Suppressed after%4d: %s", m->timenow - rr->ImmedAnswerMarkTime, ARDisplayString(m, rr)); #endif } } @@ -4145,7 +4618,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con // See if this Known-Answer suppresses any answers we were expecting for our cache records. We do this always, // even if the TC bit is not set (the TC bit will *not* be set in the *last* packet of a multi-packet KA list). - ourcacherr = FindIdenticalRecordInCache(m, &pkt.r.resrec); + ourcacherr = FindIdenticalRecordInCache(m, &m->rec.r.resrec); if (ourcacherr && ourcacherr->MPExpectingKA && m->timenow - ourcacherr->MPLastUnansweredQT < mDNSPlatformOneSecond) { ourcacherr->MPUnansweredKA++; @@ -4159,7 +4632,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con while (*eap) { CacheRecord *rr = *eap; - if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&pkt.r.resrec, &rr->resrec)) + if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &rr->resrec)) { *eap = rr->NextInKAList; rr->NextInKAList = mDNSNULL; } else eap = &rr->NextInKAList; } @@ -4171,11 +4644,12 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con while (*dqp) { DNSQuestion *q = *dqp; - if (ResourceRecordAnswersQuestion(&pkt.r.resrec, q)) + if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q)) { *dqp = q->NextInDQList; q->NextInDQList = mDNSNULL; } else dqp = &q->NextInDQList; } } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it } // *** @@ -4192,16 +4666,18 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con { if (rr->NR_AnswerTo) { - mDNSBool SendMulticastResponse = mDNSfalse; + mDNSBool SendMulticastResponse = mDNSfalse; // Send modern multicast response + mDNSBool SendUnicastResponse = mDNSfalse; // Send modern unicast response (not legacy unicast response) // If it's been a while since we multicast this, then send a multicast response for conflict detection, etc. if (m->timenow - (rr->LastMCTime + TicksTTL(rr)/4) >= 0) SendMulticastResponse = mDNStrue; // If the client insists on a multicast response, then we'd better send one - if (rr->NR_AnswerTo == (mDNSu8*)~0) SendMulticastResponse = mDNStrue; - else if (rr->NR_AnswerTo) HaveUnicastAnswer = mDNStrue; + if (rr->NR_AnswerTo == (mDNSu8*)~0) SendMulticastResponse = mDNStrue; + else if (rr->NR_AnswerTo == (mDNSu8*)~1) SendUnicastResponse = mDNStrue; + else if (rr->NR_AnswerTo) SendLegacyResponse = mDNStrue; - if (SendMulticastResponse) + if (SendMulticastResponse || SendUnicastResponse) { #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES rr->ImmedAnswerMarkTime = m->timenow; @@ -4209,13 +4685,11 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con m->NextScheduledResponse = m->timenow; // If we're already planning to send this on another interface, just send it on all interfaces if (rr->ImmedAnswer && rr->ImmedAnswer != InterfaceID) - { rr->ImmedAnswer = mDNSInterfaceMark; - debugf("ProcessQuery: %##s (%s) : Will send on all interfaces", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); - } else { rr->ImmedAnswer = InterfaceID; // Record interface to send it on + if (SendUnicastResponse) rr->ImmedUnicast = mDNStrue; if (srcaddr->type == mDNSAddrType_IPv4) { if (mDNSIPv4AddressIsZero(rr->v4Requester)) rr->v4Requester = srcaddr->ip.v4; @@ -4228,11 +4702,12 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con } } } - if (rr->resrec.RecordType == kDNSRecordTypeShared) - { - if (query->h.flags.b[0] & kDNSFlag0_TC) delayresponse = mDNSPlatformOneSecond * 20; // Divided by 50 = 400ms - else delayresponse = mDNSPlatformOneSecond; // Divided by 50 = 20ms - } + // If TC flag is set, it means we should expect that additional known answers may be coming in another packet, + // so we allow roughly half a second before deciding to reply (we've observed inter-packet delays of 100-200ms on 802.11) + // else, if record is a shared one, spread responses over 100ms to avoid implosion of simultaneous responses + // else, for a simple unique record reply, we can reply immediately; no need for delay + if (query->h.flags.b[0] & kDNSFlag0_TC) delayresponse = mDNSPlatformOneSecond * 20; // Divided by 50 = 400ms + else if (rr->resrec.RecordType == kDNSRecordTypeShared) delayresponse = mDNSPlatformOneSecond; // Divided by 50 = 20ms } else if (rr->NR_AdditionalTo && rr->NR_AdditionalTo->NR_AnswerTo == (mDNSu8*)~0) { @@ -4279,10 +4754,12 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con // *** // *** 8. If query is from a legacy client, or from a new client requesting a unicast reply, then generate a unicast response too // *** - if (HaveUnicastAnswer) + if (SendLegacyResponse) responseptr = GenerateUnicastResponse(query, end, InterfaceID, LegacyQuery, response, ResponseRecords); exit: + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + // *** // *** 9. Finally, clear our link chains ready for use next time // *** @@ -4311,7 +4788,7 @@ exit: rr->LastUnansweredTime = m->timenow; if (rr->UnansweredQueries > 1) debugf("ProcessQuery: (!TC) UAQ %lu MPQ %lu MPKA %lu %s", - rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, GetRRDisplayString(m, rr)); + rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr)); SetNextCacheCheckTime(m, rr); } @@ -4322,7 +4799,7 @@ exit: // Only show debugging message if this record was not about to expire anyway if (RRExpireTime(rr) - m->timenow > 4 * mDNSPlatformOneSecond) debugf("ProcessQuery: (Max) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s", - rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, GetRRDisplayString(m, rr)); + rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr)); mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer); } // Make a guess, based on the multi-packet query / known answer counts, whether we think we @@ -4343,7 +4820,7 @@ exit: // Only show debugging message if this record was not about to expire anyway if (RRExpireTime(rr) - m->timenow > 4 * mDNSPlatformOneSecond) debugf("ProcessQuery: (MPQ) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s", - rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, GetRRDisplayString(m, rr)); + rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr)); if (remain <= 60 * (mDNSu32)mDNSPlatformOneSecond) rr->UnansweredQueries++; // Treat this as equivalent to one definite unanswered query @@ -4371,16 +4848,44 @@ exit: return(responseptr); } +mDNSlocal mDNSBool AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr) + { + NetworkInterfaceInfo *intf; + + if (addr->type == mDNSAddrType_IPv4) + { + if (addr->ip.v4.b[0] == 169 && addr->ip.v4.b[1] == 254) return(mDNStrue); + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID) + if (((intf->ip.ip.v4.NotAnInteger ^ addr->ip.v4.NotAnInteger) & intf->mask.ip.v4.NotAnInteger) == 0) + return(mDNStrue); + } + + if (addr->type == mDNSAddrType_IPv6) + { + if (addr->ip.v6.b[0] == 0xFE && addr->ip.v6.b[1] == 0x80) return(mDNStrue); + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID) + if ((((intf->ip.ip.v6.l[0] ^ addr->ip.v6.l[0]) & intf->mask.ip.v6.l[0]) == 0) && + (((intf->ip.ip.v6.l[1] ^ addr->ip.v6.l[1]) & intf->mask.ip.v6.l[1]) == 0) && + (((intf->ip.ip.v6.l[2] ^ addr->ip.v6.l[2]) & intf->mask.ip.v6.l[2]) == 0) && + (((intf->ip.ip.v6.l[3] ^ addr->ip.v6.l[3]) & intf->mask.ip.v6.l[3]) == 0)) + return(mDNStrue); + } + + return(mDNSfalse); + } + mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, const mDNSInterfaceID InterfaceID) { - DNSMessage response; - mDNSu8 *responseend = mDNSNULL; + mDNSu8 *responseend = mDNSNULL; + mDNSBool QueryWasLocalUnicast = !mDNSAddrIsDNSMulticast(dstaddr) && AddressIsLocalSubnet(m, InterfaceID, srcaddr); - if (!InterfaceID) + if (!InterfaceID && mDNSAddrIsDNSMulticast(dstaddr)) { - LogMsg("Ignoring Query from %#-15a:%-5d to %#-15a:%-5d on 0x%.8X with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s", + LogMsg("Ignoring Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s (Multicast, but no InterfaceID)", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID, msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", @@ -4389,7 +4894,7 @@ mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, return; } - verbosedebugf("Received Query from %#-15a:%-5d to %#-15a:%-5d on 0x%.8X with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s", + verbosedebugf("Received Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID, msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", @@ -4397,16 +4902,16 @@ mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, msg->h.numAdditionals, msg->h.numAdditionals == 1 ? "" : "s"); responseend = ProcessQuery(m, msg, end, srcaddr, InterfaceID, - (srcport.NotAnInteger != MulticastDNSPort.NotAnInteger), mDNSAddrIsDNSMulticast(dstaddr), &response); + (srcport.NotAnInteger != MulticastDNSPort.NotAnInteger), mDNSAddrIsDNSMulticast(dstaddr), QueryWasLocalUnicast, &m->omsg); if (responseend) // If responseend is non-null, that means we built a unicast response packet { debugf("Unicast Response: %d Question%s, %d Answer%s, %d Additional%s to %#-15a:%d on %p/%ld", - response.h.numQuestions, response.h.numQuestions == 1 ? "" : "s", - response.h.numAnswers, response.h.numAnswers == 1 ? "" : "s", - response.h.numAdditionals, response.h.numAdditionals == 1 ? "" : "s", + m->omsg.h.numQuestions, m->omsg.h.numQuestions == 1 ? "" : "s", + m->omsg.h.numAnswers, m->omsg.h.numAnswers == 1 ? "" : "s", + m->omsg.h.numAdditionals, m->omsg.h.numAdditionals == 1 ? "" : "s", srcaddr, mDNSVal16(srcport), InterfaceID, srcaddr->type); - mDNSSendDNSMessage(m, &response, responseend, InterfaceID, srcaddr, srcport); + mDNSSendDNSMessage(m, &m->omsg, responseend, InterfaceID, srcaddr, srcport, -1, mDNSNULL); } } @@ -4416,53 +4921,39 @@ mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end, const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, - const mDNSInterfaceID InterfaceID, mDNSu8 ttl) + const mDNSInterfaceID InterfaceID) { - static mDNSu32 NumPktsAccepted = 0, NumPktsIgnored = 0; int i; const mDNSu8 *ptr = LocateAnswers(response, end); // We ignore questions (if any) in a DNS response packet CacheRecord *CacheFlushRecords = mDNSNULL; CacheRecord **cfp = &CacheFlushRecords; - + // All records in a DNS response packet are treated as equally valid statements of truth. If we want // to guard against spoof responses, then the only credible protection against that is cryptographic // security, e.g. DNSSEC., not worring about which section in the spoof packet contained the record int totalrecords = response->h.numAnswers + response->h.numAuthorities + response->h.numAdditionals; (void)srcaddr; // Currently used only for display in debugging message + (void)srcport; + (void)dstport; - verbosedebugf("Received Response from %#-15a addressed to %#-15a on %p TTL %d with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s", - srcaddr, dstaddr, InterfaceID, ttl, + verbosedebugf("Received Response from %#-15a addressed to %#-15a on %p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s", + srcaddr, dstaddr, InterfaceID, response->h.numQuestions, response->h.numQuestions == 1 ? ", " : "s,", response->h.numAnswers, response->h.numAnswers == 1 ? ", " : "s,", response->h.numAuthorities, response->h.numAuthorities == 1 ? "y, " : "ies,", response->h.numAdditionals, response->h.numAdditionals == 1 ? "" : "s"); - // TTL should be 255 - // In the case of overlayed subnets that aren't using RFC 3442, some packets may incorrectly - // go to the router first and then come back with a TTL of 254, so we allow that too. - // Anything lower than 254 is a pretty good sign of an off-net spoofing attack. - // Also, if we get a unicast response when we weren't expecting one, then we assume it is someone trying to spoof us - if (ttl < 254 || (!mDNSAddrIsDNSMulticast(dstaddr) && (mDNSu32)(m->timenow - m->ExpectUnicastResponse) > (mDNSu32)(mDNSPlatformOneSecond*2))) - { - mDNSBool ignoredlots = (++NumPktsIgnored > NumPktsAccepted + 10); - if (ignoredlots || NumPktsIgnored <= 10) - LogMsg("Ignored apparent spoof mDNS Response with TTL %d from %#-15a:%-5d to %#-15a:%-5d on %p with %2d Q %2d Ans %2d Auth %2d Add", - ttl, srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID, - response->h.numQuestions, response->h.numAnswers, response->h.numAuthorities, response->h.numAdditionals); - if (ignoredlots) - LogMsg("WARNING: Have ignored %lu packets out of %lu; this may indicate an error in the platform support layer.", - NumPktsIgnored, NumPktsIgnored + NumPktsAccepted); - return; - } - NumPktsAccepted++; + // If we get a unicast response when we weren't expecting one, then we assume it is someone trying to spoof us + if (!mDNSAddrIsDNSMulticast(dstaddr)) + if (!AddressIsLocalSubnet(m, InterfaceID, srcaddr) || (mDNSu32)(m->timenow - m->ExpectUnicastResponse) > (mDNSu32)(mDNSPlatformOneSecond*2)) + return; for (i = 0; i < totalrecords && ptr && ptr < end; i++) { - LargeCacheRecord pkt; const mDNSu8 RecordType = (mDNSu8)((i < response->h.numAnswers) ? kDNSRecordTypePacketAns : kDNSRecordTypePacketAdd); - ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, RecordType, &pkt); - if (!ptr) break; // Break out of the loop and clean up our CacheFlushRecords list before exiting + ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, RecordType, &m->rec); + if (!ptr) goto exit; // Break out of the loop and clean up our CacheFlushRecords list before exiting // 1. Check that this packet resource record does not conflict with any of ours if (m->CurrentRecord) LogMsg("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set"); @@ -4471,16 +4962,16 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, { AuthRecord *rr = m->CurrentRecord; m->CurrentRecord = rr->next; - if (PacketRRMatchesSignature(&pkt.r, rr)) // If interface, name, type (if verified) and class match... + if (PacketRRMatchesSignature(&m->rec.r, rr)) // If interface, name, type (if shared record) and class match... { - // ... check to see if rdata is identical - if (SameRData(&pkt.r.resrec, &rr->resrec)) + // ... check to see if type and rdata are identical + if (m->rec.r.resrec.rrtype == rr->resrec.rrtype && SameRData(&m->rec.r.resrec, &rr->resrec)) { // If the RR in the packet is identical to ours, just check they're not trying to lower the TTL on us - if (pkt.r.resrec.rroriginalttl >= rr->resrec.rroriginalttl/2 || m->SleepState) + if (m->rec.r.resrec.rroriginalttl >= rr->resrec.rroriginalttl/2 || m->SleepState) { // If we were planning to send on this -- and only this -- interface, then we don't need to any more - if (rr->ImmedAnswer == InterfaceID) rr->ImmedAnswer = mDNSNULL; + if (rr->ImmedAnswer == InterfaceID) { rr->ImmedAnswer = mDNSNULL; rr->ImmedUnicast = mDNSfalse; } } else { @@ -4488,78 +4979,77 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, else if (rr->ImmedAnswer != InterfaceID) { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; } } } - else + // else, the packet RR has different type or different rdata -- check to see if this is a conflict + else if (m->rec.r.resrec.rroriginalttl > 0 && PacketRRConflict(m, rr, &m->rec.r)) { - // else, the packet RR has different rdata -- check to see if this is a conflict - if (pkt.r.resrec.rroriginalttl > 0 && PacketRRConflict(m, rr, &pkt.r)) - { - debugf("mDNSCoreReceiveResponse: Our Record: %08X %08X %s", rr-> resrec.rdatahash, rr-> resrec.rdnamehash, GetRRDisplayString(m, rr)); - debugf("mDNSCoreReceiveResponse: Pkt Record: %08X %08X %s", pkt.r.resrec.rdatahash, pkt.r.resrec.rdnamehash, GetRRDisplayString(m, &pkt.r)); + debugf("mDNSCoreReceiveResponse: Our Record: %08lX %08lX %s", rr-> resrec.rdatahash, rr-> resrec.rdnamehash, ARDisplayString(m, rr)); + debugf("mDNSCoreReceiveResponse: Pkt Record: %08lX %08lX %s", m->rec.r.resrec.rdatahash, m->rec.r.resrec.rdnamehash, CRDisplayString(m, &m->rec.r)); - // If this record is marked DependentOn another record for conflict detection purposes, - // then *that* record has to be bumped back to probing state to resolve the conflict - while (rr->DependentOn) rr = rr->DependentOn; + // If this record is marked DependentOn another record for conflict detection purposes, + // then *that* record has to be bumped back to probing state to resolve the conflict + while (rr->DependentOn) rr = rr->DependentOn; - // If we've just whacked this record's ProbeCount, don't need to do it again - if (rr->ProbeCount <= DefaultProbeCountForTypeUnique) + // If we've just whacked this record's ProbeCount, don't need to do it again + if (rr->ProbeCount <= DefaultProbeCountForTypeUnique) + { + // If we'd previously verified this record, put it back to probing state and try again + if (rr->resrec.RecordType == kDNSRecordTypeVerified) { - // If we'd previously verified this record, put it back to probing state and try again - if (rr->resrec.RecordType == kDNSRecordTypeVerified) - { - debugf("mDNSCoreReceiveResponse: Reseting to Probing: %##s (%s)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); - rr->resrec.RecordType = kDNSRecordTypeUnique; - rr->ProbeCount = DefaultProbeCountForTypeUnique + 1; - rr->ThisAPInterval = DefaultAPIntervalForRecordType(kDNSRecordTypeUnique); - InitializeLastAPTime(m, rr); - RecordProbeFailure(m, rr); // Repeated late conflicts also cause us to back off to the slower probing rate - } - // If we're probing for this record, we just failed - else if (rr->resrec.RecordType == kDNSRecordTypeUnique) - { - debugf("mDNSCoreReceiveResponse: Will rename %##s (%s)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); - mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); - } - // We assumed this record must be unique, but we were wrong. - // (e.g. There are two mDNSResponders on the same machine giving - // different answers for the reverse mapping record.) - // This is simply a misconfiguration, and we don't try to recover from it. - else if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique) - { - debugf("mDNSCoreReceiveResponse: Unexpected conflict on %##s (%s) -- discarding our record", - rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); - mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); - } - else - debugf("mDNSCoreReceiveResponse: Unexpected record type %X %##s (%s)", - rr->resrec.RecordType, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + debugf("mDNSCoreReceiveResponse: Reseting to Probing: %##s (%s)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + rr->resrec.RecordType = kDNSRecordTypeUnique; + rr->ProbeCount = DefaultProbeCountForTypeUnique + 1; + rr->ThisAPInterval = DefaultAPIntervalForRecordType(kDNSRecordTypeUnique); + InitializeLastAPTime(m, rr); + RecordProbeFailure(m, rr); // Repeated late conflicts also cause us to back off to the slower probing rate + } + // If we're probing for this record, we just failed + else if (rr->resrec.RecordType == kDNSRecordTypeUnique) + { + debugf("mDNSCoreReceiveResponse: Will rename %##s (%s)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); + } + // We assumed this record must be unique, but we were wrong. + // (e.g. There are two mDNSResponders on the same machine giving + // different answers for the reverse mapping record.) + // This is simply a misconfiguration, and we don't try to recover from it. + else if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique) + { + debugf("mDNSCoreReceiveResponse: Unexpected conflict on %##s (%s) -- discarding our record", + rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); } + else + debugf("mDNSCoreReceiveResponse: Unexpected record type %X %##s (%s)", + rr->resrec.RecordType, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); } - // Else, matching signature, different rdata, but not a considered a conflict. - // If the packet record has the cache-flush bit set, then we check to see if we have to re-assert our record(s) - // to rescue them (see note about "multi-homing and bridged networks" at the end of this function). - else if ((pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && m->timenow - rr->LastMCTime > mDNSPlatformOneSecond/2) - { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; } } + // Else, matching signature, different type or rdata, but not a considered a conflict. + // If the packet record has the cache-flush bit set, then we check to see if we + // have any record(s) of the same type that we should re-assert to rescue them + // (see note about "multi-homing and bridged networks" at the end of this function). + else if (m->rec.r.resrec.rrtype == rr->resrec.rrtype) + if ((m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && m->timenow - rr->LastMCTime > mDNSPlatformOneSecond/2) + { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; } } } // 2. See if we want to add this packet resource record to our cache if (m->rrcache_size) // Only try to cache answers if we have a cache to put them in { - mDNSu32 slot = HashSlot(&pkt.r.resrec.name); + const mDNSu32 slot = HashSlot(&m->rec.r.resrec.name); CacheRecord *rr; // 2a. Check if this packet resource record is already in our cache for (rr = m->rrcache_hash[slot]; rr; rr=rr->next) { // If we found this exact resource record, refresh its TTL - if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&pkt.r.resrec, &rr->resrec)) + if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &rr->resrec)) { - if (pkt.r.resrec.rdlength > InlineCacheRDSize) + if (m->rec.r.resrec.rdlength > InlineCacheRDSize) verbosedebugf("Found record size %5d interface %p already in cache: %s", - pkt.r.resrec.rdlength, InterfaceID, GetRRDisplayString(m, &pkt.r)); + m->rec.r.resrec.rdlength, InterfaceID, CRDisplayString(m, &m->rec.r)); rr->TimeRcvd = m->timenow; - if (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) + if (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) { // If this packet record has the kDNSClass_UniqueRRSet flag set, then add it to our cache flushing list if (rr->NextInCFList == mDNSNULL && cfp != &rr->NextInCFList) @@ -4570,13 +5060,13 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, { DNSQuestion *q; for (q = m->Questions; q; q=q->next) if (ResourceRecordAnswersQuestion(&rr->resrec, q)) q->UniqueAnswers++; - rr->resrec.RecordType = pkt.r.resrec.RecordType; + rr->resrec.RecordType = m->rec.r.resrec.RecordType; } } - if (pkt.r.resrec.rroriginalttl > 0) + if (m->rec.r.resrec.rroriginalttl > 0) { - rr->resrec.rroriginalttl = pkt.r.resrec.rroriginalttl; + rr->resrec.rroriginalttl = m->rec.r.resrec.rroriginalttl; rr->UnansweredQueries = 0; rr->MPUnansweredQ = 0; rr->MPUnansweredKA = 0; @@ -4599,41 +5089,50 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // If packet resource record not in our cache, add it now // (unless it is just a deletion of a record we never had, in which case we don't care) - if (!rr && pkt.r.resrec.rroriginalttl > 0) + if (!rr && m->rec.r.resrec.rroriginalttl > 0) { - rr = GetFreeCacheRR(m, pkt.r.resrec.rdlength); - if (!rr) debugf("No cache space to add record for %#s", pkt.r.resrec.name.c); + rr = GetFreeCacheRR(m, m->rec.r.resrec.rdlength); + if (!rr) debugf("No cache space to add record for %#s", m->rec.r.resrec.name.c); else { RData *saveptr = rr->resrec.rdata; // Save the rr->resrec.rdata pointer - *rr = pkt.r; - rr->resrec.rdata = saveptr; // and then restore it after the structure assignment + *rr = m->rec.r; + rr->resrec.rdata = saveptr; // and then restore it after the structure assignment if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) { *cfp = rr; cfp = &rr->NextInCFList; } // If this is an oversized record with external storage allocated, copy rdata to external storage - if (pkt.r.resrec.rdlength > InlineCacheRDSize) - mDNSPlatformMemCopy(pkt.r.resrec.rdata, rr->resrec.rdata, sizeofRDataHeader + pkt.r.resrec.rdlength); + if (m->rec.r.resrec.rdlength > InlineCacheRDSize) + mDNSPlatformMemCopy(m->rec.r.resrec.rdata, rr->resrec.rdata, sizeofRDataHeader + m->rec.r.resrec.rdlength); rr->next = mDNSNULL; // Clear 'next' pointer *(m->rrcache_tail[slot]) = rr; // Append this record to tail of cache slot list m->rrcache_tail[slot] = &(rr->next); // Advance tail pointer m->rrcache_used[slot]++; - //debugf("Adding RR %##s to cache (%d)", pkt.r.name.c, m->rrcache_used); + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) // If marked unique, assume we may have + rr->DelayDelivery = m->timenow + mDNSPlatformOneSecond; // to delay delivery of this 'add' event + else + rr->DelayDelivery = CheckForSoonToExpireRecords(m, &rr->resrec.name, rr->resrec.namehash, slot); + //debugf("Adding RR %##s to cache (%d)", m->rec.r.name.c, m->rrcache_used); CacheRecordAdd(m, rr); // MUST do this AFTER CacheRecordAdd(), because that's what sets CRActiveQuestion for us SetNextCacheCheckTime(m, rr); } } } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it } +exit: + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + // If we've just received one or more records with their cache flush bits set, // then scan that cache slot to see if there are any old stale records we need to flush while (CacheFlushRecords) { CacheRecord *r1 = CacheFlushRecords, *r2; + const mDNSu32 slot = HashSlot(&r1->resrec.name); CacheFlushRecords = CacheFlushRecords->NextInCFList; r1->NextInCFList = mDNSNULL; - for (r2 = m->rrcache_hash[HashSlot(&r1->resrec.name)]; r2; r2=r2->next) + for (r2 = m->rrcache_hash[slot]; r2; r2=r2->next) if (SameResourceRecordSignature(&r1->resrec, &r2->resrec) && m->timenow - r2->TimeRcvd > mDNSPlatformOneSecond) { verbosedebugf("Cache flush %p X %p %##s (%s)", r1, r2, r2->resrec.name.c, DNSTypeName(r2->resrec.rrtype)); @@ -4653,20 +5152,40 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, r2->UnansweredQueries = MaxUnansweredQueries; SetNextCacheCheckTime(m, r2); } + if (r1->DelayDelivery) // If we were planning to delay delivery of this record, see if we still need to + { + r1->DelayDelivery = CheckForSoonToExpireRecords(m, &r1->resrec.name, r1->resrec.namehash, slot); + if (!r1->DelayDelivery) CacheRecordDeferredAdd(m, r1); + } } } -mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, +mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport, - const mDNSInterfaceID InterfaceID, mDNSu8 ttl) + const mDNSInterfaceID InterfaceID) { - const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery; - const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; - const mDNSu8 UpdateR = kDNSFlag0_OP_Update | kDNSFlag0_QR_Response; - const mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask); + DNSMessage *msg = (DNSMessage *)pkt; + const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery; + const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; + mDNSu8 QR_OP; + mDNSu8 *ptr = mDNSNULL; + +#ifndef UNICAST_DISABLED + mDNSIPPort NATPort = mDNSOpaque16fromIntVal(NATMAP_PORT); + const mDNSu8 UpdateR = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update; + if (srcport.NotAnInteger == NATPort.NotAnInteger) + { + mDNS_Lock(m); + uDNS_ReceiveNATMap(m, pkt, (mDNSu16)(end - (mDNSu8 *)pkt)); + mDNS_Unlock(m); + return; + } +#endif + if ((unsigned)(end - (mDNSu8 *)pkt) < sizeof(DNSMessageHeader)) { LogMsg("DNS Message too short"); return; } + QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask); // Read the integer parts which are in IETF byte-order (MSB first, LSB second) - mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions; + ptr = (mDNSu8 *)&msg->h.numQuestions; msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); @@ -4678,18 +5197,18 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNS // If we accept and try to process a packet with zero or all-ones source address, that could really mess things up if (!mDNSAddressIsValid(srcaddr)) { debugf("mDNSCoreReceive ignoring packet from %#a", srcaddr); return; } - if (dstaddr->type == mDNSAddrType_IPv4 && dstaddr->ip.v4.NotAnInteger != AllDNSLinkGroup.NotAnInteger && - (QR_OP == StdR || QR_OP == UpdateR ) && msg->h.id.NotAnInteger) - { - uDNS_ReceiveMsg(m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID, ttl); - return; - } - mDNS_Lock(m); + m->PktNum++; +#ifndef UNICAST_DISABLED + if (!mDNSAddressIsAllDNSLinkGroup(dstaddr) && (QR_OP == StdR || QR_OP == UpdateR)) + uDNS_ReceiveMsg(m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID); + // Note: mDNSCore also needs to get access to received unicast responses +#endif if (QR_OP == StdQ) mDNSCoreReceiveQuery (m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID); - else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID, ttl); - else LogMsg("Unknown DNS packet type %02X%02X from %#-15a:%-5d to %#-15a:%-5d on %p (ignored)", - msg->h.flags.b[0], msg->h.flags.b[1], srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID); + else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID); + else if (QR_OP != UpdateR) + LogMsg("Unknown DNS packet type %02X%02X from %#-15a:%-5d to %#-15a:%-5d on %p (ignored)", + msg->h.flags.b[0], msg->h.flags.b[1], srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID); // Packet reception often causes a change to the task list: // 1. Inbound queries can cause us to need to send responses @@ -4734,11 +5253,11 @@ mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, const DNSQuestion *const for (q = m->Questions; q; q=q->next) // Scan our list of questions if (q->DuplicateOf == question) // To see if any questions were referencing this as their duplicate { - q->ThisQInterval = question->ThisQInterval; - q->LastQTime = question->LastQTime; - q->RecentAnswers = 0; - q->DuplicateOf = FindDuplicateQuestion(m, q); - q->LastQTxTime = question->LastQTxTime; + q->ThisQInterval = question->ThisQInterval; + q->LastQTime = question->LastQTime; + q->RecentAnswerPkts = 0; + q->DuplicateOf = FindDuplicateQuestion(m, q); + q->LastQTxTime = question->LastQTxTime; SetNextQueryTime(m,q); } } @@ -4754,23 +5273,29 @@ mDNSlocal mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const que if (question->Target.type && !ValidQuestionTarget(question)) { - LogMsg("Warning! Target.type = %d port = %u (Client forgot to initialize before calling mDNS_StartQuery?)", + LogMsg("Warning! Target.type = %ld port = %u (Client forgot to initialize before calling mDNS_StartQuery?)", question->Target.type, mDNSVal16(question->TargetPort)); question->Target.type = mDNSAddrType_None; } - // If the client has specified an explicit InterfaceID, + if (!question->Target.type) // No question->Target specified, so clear TargetPort and TargetQID + { + question->TargetPort = zeroIPPort; + question->TargetQID = zeroID; + } + +#ifndef UNICAST_DISABLED + // If the client has specified 'kDNSServiceFlagsForceMulticast' // then we do a multicast query on that interface, even for unicast domains. - if (question->InterfaceID || IsLocalDomain(&question->qname)) + if (question->InterfaceID == mDNSInterface_LocalOnly || question->ForceMCast || IsLocalDomain(&question->qname)) question->uDNS_info.id = zeroID; else return uDNS_StartQuery(m, question); - - // The special interface ID "-2" means - // "do this query via multicast on all interfaces, even if it's apparently a unicast domain" - // After it's served its purpose by preventing a unicast query above, we now set it to mDNSInterface_Any. - if (question->InterfaceID == mDNSInterface_ForceMCast) - question->InterfaceID = mDNSInterface_Any; - +#else + question->uDNS_info.id = zeroID; +#endif // UNICAST_DISABLED + + //LogOperation("mDNS_StartQuery %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + if (m->rrcache_size == 0) // Can't do queries if we have no cache space allocated return(mStatus_NoCache); else @@ -4801,41 +5326,43 @@ mDNSlocal mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const que } } - // Note: In the case where we already have the answer to this question in our cache, that may be all the client - // wanted, and they may immediately cancel their question. In this case, sending an actual query on the wire would - // be a waste. For that reason, we schedule our first query to go out in half a second. If AnswerNewQuestion() finds - // that we have *no* relevant answers currently in our cache, then it will accelerate that to go out immediately. if (!ValidateDomainName(&question->qname)) { LogMsg("Attempt to start query with invalid qname %##s %s", question->qname.c, DNSTypeName(question->qtype)); return(mStatus_Invalid); } + // Note: In the case where we already have the answer to this question in our cache, that may be all the client + // wanted, and they may immediately cancel their question. In this case, sending an actual query on the wire would + // be a waste. For that reason, we schedule our first query to go out in half a second. If AnswerNewQuestion() finds + // that we have *no* relevant answers currently in our cache, then it will accelerate that to go out immediately. if (!m->RandomQueryDelay) m->RandomQueryDelay = 1 + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval); - question->next = mDNSNULL; - question->qnamehash = DomainNameHashValue(&question->qname); // MUST do this before FindDuplicateQuestion() - question->ThisQInterval = InitialQuestionInterval * 2; // MUST be > zero for an active question - question->LastQTime = m->timenow - m->RandomQueryDelay; // Avoid inter-machine synchronization - question->RecentAnswers = 0; - question->CurrentAnswers = 0; - question->LargeAnswers = 0; - question->UniqueAnswers = 0; - question->DuplicateOf = FindDuplicateQuestion(m, question); - question->NextInDQList = mDNSNULL; + question->next = mDNSNULL; + question->qnamehash = DomainNameHashValue(&question->qname); // MUST do this before FindDuplicateQuestion() + question->DelayAnswering = CheckForSoonToExpireRecords(m, &question->qname, question->qnamehash, HashSlot(&question->qname)); + question->ThisQInterval = InitialQuestionInterval * 2; // MUST be > zero for an active question + question->LastQTime = m->timenow - m->RandomQueryDelay; // Avoid inter-machine synchronization + question->LastAnswerPktNum = m->PktNum; + question->RecentAnswerPkts = 0; + question->CurrentAnswers = 0; + question->LargeAnswers = 0; + question->UniqueAnswers = 0; + question->DuplicateOf = FindDuplicateQuestion(m, question); + question->NextInDQList = mDNSNULL; for (i=0; iDupSuppress[i].InterfaceID = mDNSNULL; // question->InterfaceID must be already set by caller - question->SendQNow = mDNSNULL; - question->SendOnAll = mDNSfalse; - question->LastQTxTime = m->timenow; + question->SendQNow = mDNSNULL; + question->SendOnAll = mDNSfalse; + question->LastQTxTime = m->timenow; if (!question->DuplicateOf) - verbosedebugf("mDNS_StartQuery_internal: Question %##s %s %p (%p) started", - question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, question); + verbosedebugf("mDNS_StartQuery_internal: Question %##s %s %p %d (%p) started", + question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, question->LastQTime + question->ThisQInterval - m->timenow, question); else - verbosedebugf("mDNS_StartQuery_internal: Question %##s %s %p (%p) duplicate of (%p)", - question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, question, question->DuplicateOf); + verbosedebugf("mDNS_StartQuery_internal: Question %##s %s %p %d (%p) duplicate of (%p)", + question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, question->LastQTime + question->ThisQInterval - m->timenow, question, question->DuplicateOf); *q = question; if (question->InterfaceID == mDNSInterface_LocalOnly) @@ -4857,8 +5384,8 @@ mDNSlocal mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const ques CacheRecord *rr; DNSQuestion **q = &m->Questions; - if (IsActiveUnicastQuery(question, &m->uDNS_info)) return uDNS_StopQuery(m, question); - + if (uDNS_IsActiveQuery(question, &m->uDNS_info)) return uDNS_StopQuery(m, question); + if (question->InterfaceID == mDNSInterface_LocalOnly) q = &m->LocalOnlyQuestions; while (*q && *q != question) q=&(*q)->next; if (*q) *q = (*q)->next; @@ -4885,7 +5412,7 @@ mDNSlocal mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const ques for (q = m->Questions; q; q=q->next) // Scan our list of questions if (ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) break; - verbosedebugf("mDNS_StopQuery_internal: Cache RR %##s (%s) setting CRActiveQuestion to %X", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), q); + verbosedebugf("mDNS_StopQuery_internal: Cache RR %##s (%s) setting CRActiveQuestion to %p", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), q); rr->CRActiveQuestion = q; // Question used to be active; new value may or may not be null if (!q) m->rrcache_active--; // If no longer active, decrement rrcache_active count } @@ -4954,19 +5481,21 @@ mDNSexport mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr mDNSexport mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question, const domainname *const srv, const domainname *const domain, - const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context) + const mDNSInterfaceID InterfaceID, mDNSBool ForceMCast, mDNSQuestionCallback *Callback, void *Context) { question->InterfaceID = InterfaceID; question->Target = zeroAddr; question->qtype = kDNSType_PTR; question->qclass = kDNSClass_IN; + question->LongLived = mDNSfalse; + question->ExpectUnique = mDNSfalse; + question->ForceMCast = ForceMCast; question->QuestionCallback = Callback; question->QuestionContext = Context; if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) return(mStatus_BadParamErr); - // If the client has specified an explicit InterfaceID, - // then we do a multicast query on that interface, even for unicast domains. - if (question->InterfaceID || IsLocalDomain(&question->qname)) +#ifndef UNICAST_DISABLED + if (question->InterfaceID == mDNSInterface_LocalOnly || question->ForceMCast || IsLocalDomain(&question->qname)) { question->LongLived = mDNSfalse; question->uDNS_info.id = zeroID; @@ -4974,9 +5503,25 @@ mDNSexport mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question, } else { + mStatus status; + // Need to explicitly lock here, because mDNS_StartQuery does locking but uDNS_StartQuery does not + mDNS_Lock(m); question->LongLived = mDNStrue; - return uDNS_StartQuery(m, question); + status = uDNS_StartQuery(m, question); + mDNS_Unlock(m); + return(status); } +#else + return(mDNS_StartQuery(m, question)); +#endif // UNICAST_DISABLED + } + +mDNSlocal mDNSBool MachineHasActiveIPv6(mDNS *const m) + { + NetworkInterfaceInfo *intf; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->ip.type == mDNSAddrType_IPv6) return(mDNStrue); + return(mDNSfalse); } mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) @@ -4996,15 +5541,16 @@ mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const R AssignDomainName(query->qAv4.qname, answer->rdata->u.srv.target); query->qAv6.InterfaceID = answer->InterfaceID; AssignDomainName(query->qAv6.qname, answer->rdata->u.srv.target); - mDNS_StartQuery_internal(m, &query->qAv4); - mDNS_StartQuery_internal(m, &query->qAv6); + mDNS_StartQuery(m, &query->qAv4); + // Only do the AAAA query if this machine actually has IPv6 active + if (MachineHasActiveIPv6(m)) mDNS_StartQuery(m, &query->qAv6); } // If this is not our first answer, only re-issue the address query if the target host name has changed else if ((query->qAv4.InterfaceID != query->qSRV.InterfaceID && query->qAv4.InterfaceID != answer->InterfaceID) || !SameDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target)) { - mDNS_StopQuery_internal(m, &query->qAv4); - mDNS_StopQuery_internal(m, &query->qAv6); + mDNS_StopQuery(m, &query->qAv4); + if (query->qAv6.ThisQInterval >= 0) mDNS_StopQuery(m, &query->qAv6); if (SameDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target) && !PortChanged) { // If we get here, it means: @@ -5023,8 +5569,9 @@ mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const R AssignDomainName(query->qAv6.qname, answer->rdata->u.srv.target); } debugf("FoundServiceInfoSRV: Restarting address queries for %##s", query->qAv4.qname.c); - mDNS_StartQuery_internal(m, &query->qAv4); - mDNS_StartQuery_internal(m, &query->qAv6); + mDNS_StartQuery(m, &query->qAv4); + // Only do the AAAA query if this machine actually has IPv6 active + if (MachineHasActiveIPv6(m)) mDNS_StartQuery(m, &query->qAv6); } else if (query->ServiceInfoQueryCallback && query->GotADD && query->GotTXT && PortChanged) { @@ -5066,12 +5613,13 @@ mDNSlocal void FoundServiceInfoTXT(mDNS *const m, DNSQuestion *question, const R mDNSlocal void FoundServiceInfo(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) { ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; + //LogOperation("FoundServiceInfo %d %s", AddRecord, RRDisplayString(m, answer)); if (!AddRecord) return; if (answer->rrtype == kDNSType_A) { query->info->ip.type = mDNSAddrType_IPv4; - query->info->ip.ip.v4 = answer->rdata->u.ip; + query->info->ip.ip.v4 = answer->rdata->u.ipv4; } else if (answer->rrtype == kDNSType_AAAA) { @@ -5087,7 +5635,7 @@ mDNSlocal void FoundServiceInfo(mDNS *const m, DNSQuestion *question, const Reso query->GotADD = mDNStrue; query->info->InterfaceID = answer->InterfaceID; - verbosedebugf("FoundServiceInfo v%d: %##s GotTXT=%d", query->info->ip.type, query->info->name.c, query->GotTXT); + verbosedebugf("FoundServiceInfo v%ld: %##s GotTXT=%d", query->info->ip.type, query->info->name.c, query->GotTXT); // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's // callback function is allowed to do anything, including deleting this query and freeing its memory. @@ -5096,7 +5644,7 @@ mDNSlocal void FoundServiceInfo(mDNS *const m, DNSQuestion *question, const Reso if (++query->Answers >= 100) { if (answer->rrtype == kDNSType_A) - debugf("**** WARNING **** have given %lu answers for %##s (A) %.4a", query->Answers, query->qSRV.qname.c, &answer->rdata->u.ip); + debugf("**** WARNING **** have given %lu answers for %##s (A) %.4a", query->Answers, query->qSRV.qname.c, &answer->rdata->u.ipv4); else debugf("**** WARNING **** have given %lu answers for %##s (AAAA) %.16a", query->Answers, query->qSRV.qname.c, &answer->rdata->u.ipv6); } @@ -5120,6 +5668,9 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, AssignDomainName(query->qSRV.qname, info->name); query->qSRV.qtype = kDNSType_SRV; query->qSRV.qclass = kDNSClass_IN; + query->qSRV.LongLived = mDNSfalse; + query->qSRV.ExpectUnique = mDNStrue; + query->qSRV.ForceMCast = mDNSfalse; query->qSRV.QuestionCallback = FoundServiceInfoSRV; query->qSRV.QuestionContext = query; @@ -5129,6 +5680,9 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, AssignDomainName(query->qTXT.qname, info->name); query->qTXT.qtype = kDNSType_TXT; query->qTXT.qclass = kDNSClass_IN; + query->qTXT.LongLived = mDNSfalse; + query->qTXT.ExpectUnique = mDNStrue; + query->qTXT.ForceMCast = mDNSfalse; query->qTXT.QuestionCallback = FoundServiceInfoTXT; query->qTXT.QuestionContext = query; @@ -5138,6 +5692,9 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, query->qAv4.qname.c[0] = 0; query->qAv4.qtype = kDNSType_A; query->qAv4.qclass = kDNSClass_IN; + query->qAv4.LongLived = mDNSfalse; + query->qAv4.ExpectUnique = mDNStrue; + query->qAv4.ForceMCast = mDNSfalse; query->qAv4.QuestionCallback = FoundServiceInfo; query->qAv4.QuestionContext = query; @@ -5147,6 +5704,9 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, query->qAv6.qname.c[0] = 0; query->qAv6.qtype = kDNSType_AAAA; query->qAv6.qclass = kDNSClass_IN; + query->qAv6.LongLived = mDNSfalse; + query->qAv6.ExpectUnique = mDNStrue; + query->qAv6.ForceMCast = mDNSfalse; query->qAv6.QuestionCallback = FoundServiceInfo; query->qAv6.QuestionContext = query; @@ -5165,6 +5725,7 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, info->port = zeroIPPort; info->TXTlen = 0; + // We use mDNS_StartQuery_internal here because we're already holding the lock status = mDNS_StartQuery_internal(m, &query->qSRV); if (status == mStatus_NoError) status = mDNS_StartQuery_internal(m, &query->qTXT); if (status != mStatus_NoError) mDNS_StopResolveService(m, query); @@ -5176,13 +5737,14 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, mDNSexport void mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *query) { mDNS_Lock(m); - if (query->qSRV.ThisQInterval >= 0 || IsActiveUnicastQuery(&query->qSRV, &m->uDNS_info)) + // We use mDNS_StopQuery_internal here because we're already holding the lock + if (query->qSRV.ThisQInterval >= 0 || uDNS_IsActiveQuery(&query->qSRV, &m->uDNS_info)) mDNS_StopQuery_internal(m, &query->qSRV); - if (query->qTXT.ThisQInterval >= 0 || IsActiveUnicastQuery(&query->qTXT, &m->uDNS_info)) + if (query->qTXT.ThisQInterval >= 0 || uDNS_IsActiveQuery(&query->qTXT, &m->uDNS_info)) mDNS_StopQuery_internal(m, &query->qTXT); - if (query->qAv4.ThisQInterval >= 0 || IsActiveUnicastQuery(&query->qAv4, &m->uDNS_info)) + if (query->qAv4.ThisQInterval >= 0 || uDNS_IsActiveQuery(&query->qAv4, &m->uDNS_info)) mDNS_StopQuery_internal(m, &query->qAv4); - if (query->qAv6.ThisQInterval >= 0 || IsActiveUnicastQuery(&query->qAv6, &m->uDNS_info)) + if (query->qAv6.ThisQInterval >= 0 || uDNS_IsActiveQuery(&query->qAv6, &m->uDNS_info)) mDNS_StopQuery_internal(m, &query->qAv6); mDNS_Unlock(m); } @@ -5194,11 +5756,14 @@ mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, m question->Target = zeroAddr; question->qtype = kDNSType_PTR; question->qclass = kDNSClass_IN; + question->LongLived = mDNSfalse; + question->ExpectUnique = mDNSfalse; + question->ForceMCast = mDNSfalse; question->QuestionCallback = Callback; question->QuestionContext = Context; - if (DomainType > mDNS_DomainTypeRegistrationDefault) return(mStatus_BadParamErr); + if (DomainType > mDNS_DomainTypeBrowseLegacy) return(mStatus_BadParamErr); if (!MakeDomainNameFromDNSNameString(&question->qname, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr); - if (!dom) dom = (const domainname *)"\x5local"; + if (!dom) dom = &localdomain; if (!AppendDomainName(&question->qname, dom)) return(mStatus_BadParamErr); return(mDNS_StartQuery(m, question)); } @@ -5214,11 +5779,12 @@ mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, m mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID, mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, mDNSRecordCallback Callback, void *Context) { + mDNSPlatformMemZero(&rr->uDNS_info, sizeof(uDNS_RegInfo)); // Don't try to store a TTL bigger than we can represent in platform time units if (ttl > 0x7FFFFFFFUL / mDNSPlatformOneSecond) ttl = 0x7FFFFFFFUL / mDNSPlatformOneSecond; else if (ttl == 0) // And Zero TTL is illegal - ttl = kDefaultTTLforShared; + ttl = DefaultTTLforRRType(rrtype); // Field Group 1: Persistent metadata for Authoritative Records rr->Additional1 = mDNSNULL; @@ -5230,6 +5796,8 @@ mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mD rr->resrec.RecordType = RecordType; rr->HostTarget = mDNSfalse; + rr->AllowRemoteQuery = mDNSfalse; + rr->ForceMCast = mDNSfalse; // Field Group 2: Transient state for Authoritative Records (set in mDNS_Register_internal) // Field Group 3: Transient state for Cache Records (set in mDNS_Register_internal) @@ -5263,19 +5831,17 @@ mDNSexport mStatus mDNS_Register(mDNS *const m, AuthRecord *const rr) } mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newttl, - const mDNSu16 newrdlength, - RData *const newrdata, mDNSRecordUpdateCallback *Callback) + const mDNSu16 newrdlength, RData *const newrdata, mDNSRecordUpdateCallback *Callback) { - - if (!rr->resrec.InterfaceID && !IsLocalDomain(&rr->resrec.name)) - { - LogMsg("mDNS_Update: Not implemented for unicast DNS"); - return mStatus_UnsupportedErr; - } +#ifndef UNICAST_DISABLED + mDNSBool unicast = !(rr->resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(&rr->resrec.name)); +#else + mDNSBool unicast = mDNSfalse; +#endif if (!ValidateRData(rr->resrec.rrtype, newrdlength, newrdata)) - { LogMsg("Attempt to update record with invalid rdata: %s", GetRRDisplayString_rdb(m, &rr->resrec, &newrdata->u)); return(mStatus_Invalid); } - + { LogMsg("Attempt to update record with invalid rdata: %s", GetRRDisplayString_rdb(&rr->resrec, &newrdata->u, m->MsgBuffer)); return(mStatus_Invalid); } + mDNS_Lock(m); // If TTL is unspecified, leave TTL unchanged @@ -5283,7 +5849,7 @@ mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newt // If we already have an update queued up which has not gone through yet, // give the client a chance to free that memory - if (rr->NewRData) + if (!unicast && rr->NewRData) { RData *n = rr->NewRData; rr->NewRData = mDNSNULL; // Clear the NewRData pointer ... @@ -5294,7 +5860,9 @@ mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newt rr->NewRData = newrdata; rr->newrdlength = newrdlength; rr->UpdateCallback = Callback; - + + if (unicast) { mStatus status = uDNS_UpdateRecord(m, rr); mDNS_Unlock(m); return(status); } + if (rr->resrec.rroriginalttl == newttl && rr->resrec.rdlength == newrdlength && mDNSPlatformMemSame(rr->resrec.rdata->u.data, newrdata->u.data, newrdlength)) CompleteRDataUpdate(m, rr); else @@ -5302,8 +5870,7 @@ mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newt domainlabel name; domainname type, domain; DeconstructServiceName(&rr->resrec.name, &name, &type, &domain); - if (rr->AnnounceCount < ReannounceCount) - rr->AnnounceCount = ReannounceCount; + rr->AnnounceCount = InitialAnnounceCount; // iChat often does suprious record updates where no data has changed. For the _presence service type, using // name/value pairs, the mDNSPlatformMemSame() check above catches this and correctly suppresses the wasteful // update. For the _ichat service type, the XML encoding introduces spurious noise differences into the data @@ -5314,15 +5881,15 @@ mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newt InitializeLastAPTime(m, rr); while (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) GrantUpdateCredit(rr); if (!rr->UpdateBlocked && rr->UpdateCredits) rr->UpdateCredits--; - if (!rr->NextUpdateCredit) rr->NextUpdateCredit = (m->timenow + kUpdateCreditRefreshInterval) | 1; + if (!rr->NextUpdateCredit) rr->NextUpdateCredit = NonZeroTime(m->timenow + kUpdateCreditRefreshInterval); if (rr->AnnounceCount > rr->UpdateCredits + 1) rr->AnnounceCount = (mDNSu8)(rr->UpdateCredits + 1); if (rr->UpdateCredits <= 5) { - mDNSs32 delay = 1 << (5 - rr->UpdateCredits); - if (!rr->UpdateBlocked) rr->UpdateBlocked = (m->timenow + delay * mDNSPlatformOneSecond) | 1; - rr->LastAPTime = rr->UpdateBlocked; + mDNSs32 delay = 6 - rr->UpdateCredits; // Delay 1 second, then 2, then 3, etc. up to 6 seconds maximum + if (!rr->UpdateBlocked) rr->UpdateBlocked = NonZeroTime(m->timenow + delay * mDNSPlatformOneSecond); rr->ThisAPInterval *= 4; - LogMsg("Excessive update rate for %##s; delaying announcement by %d second%s", rr->resrec.name.c, delay, delay > 1 ? "s" : ""); + rr->LastAPTime = rr->UpdateBlocked - rr->ThisAPInterval; + LogMsg("Excessive update rate for %##s; delaying announcement by %ld second%s", rr->resrec.name.c, delay, delay > 1 ? "s" : ""); } rr->resrec.rroriginalttl = newttl; } @@ -5360,18 +5927,22 @@ mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary // Send dynamic update for non-linklocal IPv4 Addresses - uDNS_AdvertiseInterface(m, set); - mDNS_SetupResourceRecord(&set->RR_A, mDNSNULL, set->InterfaceID, kDNSType_A, kDefaultTTLforUnique, kDNSRecordTypeUnique, mDNS_HostNameCallback, set); - mDNS_SetupResourceRecord(&set->RR_PTR, mDNSNULL, set->InterfaceID, kDNSType_PTR, kDefaultTTLforUnique, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL); - mDNS_SetupResourceRecord(&set->RR_HINFO, mDNSNULL, set->InterfaceID, kDNSType_HINFO, kDefaultTTLforUnique, kDNSRecordTypeUnique, mDNSNULL, mDNSNULL); - + mDNS_SetupResourceRecord(&set->RR_A, mDNSNULL, set->InterfaceID, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnique, mDNS_HostNameCallback, set); + mDNS_SetupResourceRecord(&set->RR_PTR, mDNSNULL, set->InterfaceID, kDNSType_PTR, kHostNameTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL); + mDNS_SetupResourceRecord(&set->RR_HINFO, mDNSNULL, set->InterfaceID, kDNSType_HINFO, kHostNameTTL, kDNSRecordTypeUnique, mDNSNULL, mDNSNULL); + +#if ANSWER_REMOTE_HOSTNAME_QUERIES + set->RR_A .AllowRemoteQuery = mDNStrue; + set->RR_PTR .AllowRemoteQuery = mDNStrue; + set->RR_HINFO.AllowRemoteQuery = mDNStrue; +#endif // 1. Set up Address record to map from host name ("foo.local.") to IP address // 2. Set up reverse-lookup PTR record to map from our address back to our host name - AssignDomainName(set->RR_A.resrec.name, m->hostname); + AssignDomainName(set->RR_A.resrec.name, m->MulticastHostname); if (set->ip.type == mDNSAddrType_IPv4) { set->RR_A.resrec.rrtype = kDNSType_A; - set->RR_A.resrec.rdata->u.ip = set->ip.ip.v4; + set->RR_A.resrec.rdata->u.ipv4 = set->ip.ip.v4; // Note: This is reverse order compared to a normal dotted-decimal IP address mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", set->ip.ip.v4.b[3], set->ip.ip.v4.b[2], set->ip.ip.v4.b[1], set->ip.ip.v4.b[0]); @@ -5394,6 +5965,7 @@ mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) MakeDomainNameFromDNSNameString(&set->RR_PTR.resrec.name, buffer); set->RR_PTR.HostTarget = mDNStrue; // Tell mDNS that the target of this PTR is to be kept in sync with our host name + set->RR_PTR.ForceMCast = mDNStrue; // This PTR points to our dot-local name, so don't ever try to write it into a uDNS server set->RR_A.RRSet = &primary->RR_A; // May refer to self @@ -5403,7 +5975,7 @@ mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) if (m->HIHardware.c[0] > 0 && m->HISoftware.c[0] > 0 && m->HIHardware.c[0] + m->HISoftware.c[0] <= 254) { mDNSu8 *p = set->RR_HINFO.resrec.rdata->u.data; - AssignDomainName(set->RR_HINFO.resrec.name, m->hostname); + AssignDomainName(set->RR_HINFO.resrec.name, m->MulticastHostname); set->RR_HINFO.DependentOn = &set->RR_A; mDNSPlatformMemCopy(&m->HIHardware, p, 1 + (mDNSu32)m->HIHardware.c[0]); p += 1 + (int)p[0]; @@ -5438,56 +6010,34 @@ mDNSlocal void DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) if (set->RR_HINFO.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_HINFO, mDNS_Dereg_normal); } -mDNSlocal void GenerateFQDN(mDNS *const m, const char *domain, mDNSBool local) +mDNSexport void mDNS_SetFQDN(mDNS *const m) { - domainname newname; - mDNS_Lock(m); - - newname.c[0] = 0; - if (!AppendDomainLabel(&newname, &m->hostlabel)) LogMsg("ERROR: GenerateFQDN: Cannot create hostname"); - if (!AppendDNSNameString(&newname, domain)) LogMsg("ERROR: GenerateFQDN: Cannot create hostname"); - - if ((local && !SameDomainName(&m->hostname, &newname)) || - (!local && !SameDomainName(&m->uDNS_info.hostname, &newname))) - { - NetworkInterfaceInfo *intf; - AuthRecord *rr; - - if (local) m->hostname = newname; - else m->uDNS_info.hostname = newname; + domainname newmname; + NetworkInterfaceInfo *intf; + AuthRecord *rr; + newmname.c[0] = 0; - //!!!KRS for unicast, we can do the deadvertisements/new adverts in the same update message - // 1. Stop advertising our address records on all interfaces - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->Advertise) - local ? DeadvertiseInterface(m, intf) : uDNS_DeadvertiseInterface(m, intf); + if (!AppendDomainLabel(&newmname, &m->hostlabel)) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; } + if (!AppendLiteralLabelString(&newmname, "local")) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; } + if (SameDomainName(&m->MulticastHostname, &newmname)) { LogMsg("mDNS_SetFQDN - hostname unchanged"); return; } - // 2. Start advertising our address records using the new name - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->Advertise) - local ? AdvertiseInterface(m, intf) : uDNS_AdvertiseInterface(m, intf); + mDNS_Lock(m); + AssignDomainName(m->MulticastHostname, newmname); - // 3. Make sure that any SRV records (and the like) that reference our - // host name in their rdata get updated to reference this new host name - if (local) - { - for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->HostTarget) SetTargetToHostName(m, rr); - for (rr = m->DuplicateRecords; rr; rr=rr->next) if (rr->HostTarget) SetTargetToHostName(m, rr); - } - else uDNS_UpdateServiceTargets(m); - } - mDNS_Unlock(m); - } + // 1. Stop advertising our address records on all interfaces + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->Advertise) DeadvertiseInterface(m, intf); -mDNSexport void mDNS_GenerateFQDN(mDNS *const m) - { - GenerateFQDN(m, "local.", mDNStrue); - } + // 2. Start advertising our address records using the new name + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->Advertise) AdvertiseInterface(m, intf); -mDNSexport void mDNS_GenerateGlobalFQDN(mDNS *const m) - { - if (!m->uDNS_info.NameRegDomain[0]) return; - GenerateFQDN(m, m->uDNS_info.NameRegDomain, mDNSfalse); + // 3. Make sure that any SRV records (and the like) that reference our + // host name in their rdata get updated to reference this new host name + for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->HostTarget) SetTargetToHostName(m, rr); + for (rr = m->DuplicateRecords; rr; rr=rr->next) if (rr->HostTarget) SetTargetToHostName(m, rr); + + mDNS_Unlock(m); } mDNSexport void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result) @@ -5516,7 +6066,7 @@ mDNSexport void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStat else if (result == mStatus_NameConflict) { domainlabel oldlabel = m->hostlabel; - mDNSBool local = (IsLocalDomain(&rr->resrec.name) || rr->resrec.InterfaceID); + // 1. First give the client callback a chance to pick a new name if (m->MainCallback) { @@ -5531,21 +6081,17 @@ mDNSexport void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStat // 3. Generate the FQDNs from the hostlabel, // and make sure all SRV records, etc., are updated to reference our new hostname - if (!local) - { - //!!!KRS clean this up - char regdomain[MAX_ESCAPED_DOMAIN_NAME]; - ConvertDomainNameToCString_unescaped((domainname *)(rr->resrec.name.c + rr->resrec.name.c[0] + 1), regdomain); - mDNS_GenerateGlobalFQDN(m); - LogMsg("Host Name %s.%s already in use; new name %s.%s selected for this host", oldlabel.c, regdomain, m->hostlabel.c, regdomain); - } - else - { - mDNS_GenerateFQDN(m); - LogMsg("Link-Local Host Name %#s.local. already in use; new name %#s.local. selected for this host.", oldlabel.c, m->hostlabel.c); - } + mDNS_SetFQDN(m); + LogMsg("Local Hostname %#s.local already in use; will try %#s.local instead", oldlabel.c, m->hostlabel.c); + } + else if (result == mStatus_MemFree) + { + // .local hostnames do not require goodbyes - we ignore the MemFree (which is sent directly by + // mDNS_Deregister_internal), and allow the caller to deallocate immediately following mDNS_DeadvertiseInterface + debugf("mDNS_HostNameCallback: MemFree (ignored)"); } - else LogMsg("mDNS_HostNameCallback: Unknown error %d for registration of record %s", result, rr->resrec.name.c); + else + LogMsg("mDNS_HostNameCallback: Unknown error %ld for registration of record %s", result, rr->resrec.name.c); } mDNSlocal void UpdateInterfaceProtocols(mDNS *const m, NetworkInterfaceInfo *active) @@ -5565,6 +6111,13 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s { mDNSBool FirstOfType = mDNStrue; NetworkInterfaceInfo **p = &m->HostInterfaces; + + if (!set->InterfaceID) + { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with zero InterfaceID", &set->ip); return(mStatus_Invalid); } + + if (!mDNSAddressIsValidNonZero(&set->mask)) + { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with invalid mask %#a", &set->ip, &set->mask); return(mStatus_Invalid); } + mDNS_Lock(m); // Assume this interface will be active @@ -5616,9 +6169,9 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s for (q = m->Questions; q; q=q->next) // Scan our list of questions if (!q->InterfaceID || q->InterfaceID == set->InterfaceID) // If non-specific Q, or Q on this specific interface, { // then reactivate this question - q->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question - q->LastQTime = m->timenow - q->ThisQInterval; - q->RecentAnswers = 0; + q->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question + q->LastQTime = m->timenow - q->ThisQInterval; + q->RecentAnswerPkts = 0; if (ActiveQuestion(q)) m->NextScheduledQuery = m->timenow; } @@ -5628,10 +6181,9 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s if (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == set->InterfaceID) { if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique; - rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); - if (rr->AnnounceCount < ReannounceCount) - rr->AnnounceCount = ReannounceCount; - rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); + rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); + rr->AnnounceCount = InitialAnnounceCount; + rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); InitializeLastAPTime(m, rr); } } @@ -5717,11 +6269,8 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se } // If we were advertising on this interface, deregister those address and reverse-lookup records now - if (set->Advertise) - { - DeadvertiseInterface(m, set); - uDNS_DeadvertiseInterface(m, set); - } + if (set->Advertise) DeadvertiseInterface(m, set); + // If we have any cache records received on this interface that went away, then re-verify them. // In some versions of OS X the IPv6 address remains on an interface even when the interface is turned off, // giving the false impression that there's an active representative of this interface when there really isn't. @@ -5755,12 +6304,14 @@ mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus resu } #endif + // Only pass on the NoError acknowledgement for the SRV record (when it finishes probing) + if (result == mStatus_NoError && rr != &sr->RR_SRV) return; + // If we got a name conflict on either SRV or TXT, forcibly deregister this service, and record that we did that if (result == mStatus_NameConflict) { - sr->Conflict = mDNStrue; // Record that this service set had a conflict - sr->RR_PTR.AnnounceCount = InitialAnnounceCount; // Make sure we don't send a goodbye for the PTR record - mDNS_DeregisterService(m, sr); // Unlink the records from our list + sr->Conflict = mDNStrue; // Record that this service set had a conflict + mDNS_DeregisterService(m, sr); // Unlink the records from our list return; } @@ -5783,6 +6334,13 @@ mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus resu sr->ServiceCallback(m, sr, result); } +mDNSlocal void NSSCallback(mDNS *const m, AuthRecord *const rr, mStatus result) + { + ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext; + if (sr->ServiceCallback) + sr->ServiceCallback(m, sr, result); + } + // Note: // Name is first label of domain name (any dots in the name are actual dots, not label separators) // Type is service type (e.g. "_ipp._tcp.") @@ -5790,7 +6348,7 @@ mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus resu // We always register a TXT, even if it is empty (so that clients are not // left waiting forever looking for a nonexistent record.) // If the host parameter is mDNSNULL or the root domain (ASCII NUL), -// then the default host name (m->hostname1) is automatically used +// then the default host name (m->MulticastHostname) is automatically used mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, const domainlabel *const name, const domainname *const type, const domainname *const domain, const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen, @@ -5809,11 +6367,14 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, if (host && host->c[0]) sr->Host = *host; else sr->Host.c[0] = 0; + // If port number is zero, that means the client is really trying to do a RegisterNoSuchService + if (!port.NotAnInteger) return(mDNS_RegisterNoSuchService(m, &sr->RR_SRV, name, type, domain, mDNSNULL, mDNSInterface_Any, NSSCallback, sr)); + // Initialize the AuthRecord objects to sane values - mDNS_SetupResourceRecord(&sr->RR_ADV, mDNSNULL, InterfaceID, kDNSType_PTR, kDefaultTTLforShared, kDNSRecordTypeAdvisory, ServiceCallback, sr); - mDNS_SetupResourceRecord(&sr->RR_PTR, mDNSNULL, InterfaceID, kDNSType_PTR, kDefaultTTLforShared, kDNSRecordTypeShared, ServiceCallback, sr); - mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, InterfaceID, kDNSType_SRV, kDefaultTTLforUnique, kDNSRecordTypeUnique, ServiceCallback, sr); - mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, InterfaceID, kDNSType_TXT, kDefaultTTLforUnique, kDNSRecordTypeUnique, ServiceCallback, sr); + mDNS_SetupResourceRecord(&sr->RR_ADV, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeAdvisory, ServiceCallback, sr); + mDNS_SetupResourceRecord(&sr->RR_PTR, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, ServiceCallback, sr); + mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, ServiceCallback, sr); + mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, InterfaceID, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnique, ServiceCallback, sr); // If the client is registering an oversized TXT record, // it is the client's responsibility to alloate a ServiceRecordSet structure that is large enough for it @@ -5843,9 +6404,11 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, // already set the first label of the record name to the subtype being registered for (i=0; iSubTypes[i].resrec.name; - mDNS_SetupResourceRecord(&sr->SubTypes[i], mDNSNULL, InterfaceID, kDNSType_PTR, kDefaultTTLforShared, kDNSRecordTypeShared, ServiceCallback, sr); - if (ConstructServiceName(&sr->SubTypes[i].resrec.name, &s, type, domain) == mDNSNULL) return(mStatus_BadParamErr); + domainname st; + AssignDomainName(st, sr->SubTypes[i].resrec.name); + AppendDomainName(&st, type); + mDNS_SetupResourceRecord(&sr->SubTypes[i], mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, ServiceCallback, sr); + if (ConstructServiceName(&sr->SubTypes[i].resrec.name, mDNSNULL, &st, domain) == mDNSNULL) return(mStatus_BadParamErr); AssignDomainName(sr->SubTypes[i].resrec.rdata->u.name, sr->RR_SRV.resrec.name); sr->SubTypes[i].Additional1 = &sr->RR_SRV; sr->SubTypes[i].Additional2 = &sr->RR_TXT; @@ -5871,11 +6434,21 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, } sr->RR_TXT.DependentOn = &sr->RR_SRV; +#ifndef UNICAST_DISABLED // If the client has specified an explicit InterfaceID, // then we do a multicast registration on that interface, even for unicast domains. - if (!InterfaceID && !IsLocalDomain(&sr->RR_SRV.resrec.name)) - return uDNS_RegisterService(m, sr); - + if (!(InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(&sr->RR_SRV.resrec.name))) + { + mStatus status; + mDNS_Lock(m); + // Some (or perhaps all) versions of BIND named (name daemon) don't allow updates with zero-length rdata. + // (We have to duplicate this check here because uDNS_RegisterService() bypasses the usual mDNS_Register_internal() bottleneck) + if (!sr->RR_TXT.resrec.rdlength) { sr->RR_TXT.resrec.rdlength = 1; sr->RR_TXT.resrec.rdata->u.txt.c[0] = 0; } + status = uDNS_RegisterService(m, sr); + mDNS_Unlock(m); + return(status); + } +#endif mDNS_Lock(m); err = mDNS_Register_internal(m, &sr->RR_SRV); if (!err) err = mDNS_Register_internal(m, &sr->RR_TXT); @@ -5900,22 +6473,30 @@ mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord **e; mStatus status; - if (!sr->RR_SRV.resrec.InterfaceID && !IsLocalDomain(&sr->RR_SRV.resrec.name)) + extra->next = mDNSNULL; + mDNS_SetupResourceRecord(&extra->r, rdata, sr->RR_PTR.resrec.InterfaceID, extra->r.resrec.rrtype, ttl, kDNSRecordTypeUnique, ServiceCallback, sr); + AssignDomainName(extra->r.resrec.name, sr->RR_SRV.resrec.name); + +#ifndef UNICAST_DISABLED + if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(&sr->RR_SRV.resrec.name))) { - LogMsg("mDNS_AddRecordToService: Not implemented for unicast DNS"); - return mStatus_UnsupportedErr; + mDNS_Lock(m); + // Some (or perhaps all) versions of BIND named (name daemon) don't allow updates with zero-length rdata. + // (We have to duplicate this check here because uDNS_AddRecordToService() bypasses the usual mDNS_Register_internal() bottleneck) + if (extra->r.resrec.rrtype == kDNSType_TXT && extra->r.resrec.rdlength == 0) + { extra->r.resrec.rdlength = 1; extra->r.resrec.rdata->u.txt.c[0] = 0; } + status = uDNS_AddRecordToService(m, sr, extra); + mDNS_Unlock(m); + return status; } - +#endif + mDNS_Lock(m); e = &sr->Extras; while (*e) e = &(*e)->next; - // If TTL is unspecified, make it the same as the service's TXT and SRV default - if (ttl == 0) ttl = kDefaultTTLforUnique; + if (ttl == 0) ttl = kStandardTTL; - extra->next = mDNSNULL; - mDNS_SetupResourceRecord(&extra->r, rdata, sr->RR_PTR.resrec.InterfaceID, extra->r.resrec.rrtype, ttl, kDNSRecordTypeUnique, ServiceCallback, sr); - AssignDomainName(extra->r.resrec.name, sr->RR_SRV.resrec.name); extra->r.DependentOn = &sr->RR_SRV; debugf("mDNS_AddRecordToService adding record to %##s", extra->r.resrec.name.c); @@ -5926,17 +6507,11 @@ mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, return(status); } -mDNSexport mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra) +mDNSexport mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, mDNSRecordCallback MemFreeCallback, void *Context) { ExtraResourceRecord **e; mStatus status; - if (!sr->RR_SRV.resrec.InterfaceID && !IsLocalDomain(&sr->RR_SRV.resrec.name)) - { - LogMsg("mDNS_AddRecordToService: Not implemented for unicast DNS"); - return mStatus_UnsupportedErr; - } - mDNS_Lock(m); e = &sr->Extras; while (*e && *e != extra) e = &(*e)->next; @@ -5948,7 +6523,14 @@ mDNSexport mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet else { debugf("mDNS_RemoveRecordFromService removing record from %##s", extra->r.resrec.name.c); + extra->r.RecordCallback = MemFreeCallback; + extra->r.RecordContext = Context; *e = (*e)->next; +#ifndef UNICAST_DISABLED + if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(&sr->RR_SRV.resrec.name))) + status = uDNS_DeregisterRecord(m, &extra->r); + else +#endif status = mDNS_Deregister_internal(m, &extra->r, mDNS_Dereg_normal); } mDNS_Unlock(m); @@ -5998,17 +6580,27 @@ mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordS // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. mDNSexport mStatus mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr) { - if (!sr->RR_SRV.resrec.InterfaceID && !IsLocalDomain(&sr->RR_SRV.resrec.name)) - return uDNS_DeregisterService(m, sr); + // If port number is zero, that means this was actually registered using mDNS_RegisterNoSuchService() + if (!sr->RR_SRV.resrec.rdata->u.srv.port.NotAnInteger) return(mDNS_DeregisterNoSuchService(m, &sr->RR_SRV)); +#ifndef UNICAST_DISABLED + if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(&sr->RR_SRV.resrec.name))) + { + mStatus status; + mDNS_Lock(m); + status = uDNS_DeregisterService(m, sr); + mDNS_Unlock(m); + return(status); + } +#endif if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeUnregistered) { - debugf("Service set for %##s already deregistered", sr->RR_PTR.resrec.name.c); + debugf("Service set for %##s already deregistered", sr->RR_SRV.resrec.name.c); return(mStatus_BadReferenceErr); } else if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeDeregistering) { - debugf("Service set for %##s already in the process of deregistering", sr->RR_PTR.resrec.name.c); + debugf("Service set for %##s already in the process of deregistering", sr->RR_SRV.resrec.name.c); return(mStatus_NoError); } else @@ -6059,7 +6651,7 @@ mDNSexport mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const r const domainname *const host, const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context) { - mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_SRV, kDefaultTTLforUnique, kDNSRecordTypeUnique, Callback, Context); + mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, Callback, Context); if (ConstructServiceName(&rr->resrec.name, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr); rr->resrec.rdata->u.srv.priority = 0; rr->resrec.rdata->u.srv.weight = 0; @@ -6072,7 +6664,7 @@ mDNSexport mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const r mDNSexport mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname) { - mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_PTR, kDefaultTTLforShared, kDNSRecordTypeShared, mDNSNULL, mDNSNULL); + mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, mDNSNULL, mDNSNULL); if (!MakeDomainNameFromDNSNameString(&rr->resrec.name, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr); if (!MakeDomainNameFromDNSNameString(&rr->resrec.rdata->u.name, domname)) return(mStatus_BadParamErr); return(mDNS_Register(m, rr)); @@ -6109,19 +6701,21 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, mDNSBool AdvertiseLocalAddresses, mDNSCallback *Callback, void *Context) { mDNSu32 slot; - mDNSs32 timenow; - mStatus result = mDNSPlatformTimeInit(&timenow); - if (result != mStatus_NoError) return(result); + mDNSs32 timenow, timenow_adjust; + mStatus result; if (!rrcachestorage) rrcachesize = 0; m->p = p; m->KnownBugs = 0; - m->CanReceiveUnicast = mDNSfalse; // Assume we can't receive unicasts, unless platform layer tells us otherwise + m->CanReceiveUnicastOn5353 = mDNSfalse; // Assume we can't receive unicasts on 5353, unless platform layer tells us otherwise m->AdvertiseLocalAddresses = AdvertiseLocalAddresses; m->mDNSPlatformStatus = mStatus_Waiting; + m->UnicastPort4 = zeroIPPort; + m->UnicastPort6 = zeroIPPort; m->MainCallback = Callback; m->MainContext = Context; + m->rec.r.resrec.RecordType = 0; // For debugging: To catch and report locking failures m->mDNS_busy = 0; @@ -6132,9 +6726,14 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->lock_Records = 0; // Task Scheduling variables + result = mDNSPlatformTimeInit(); + if (result != mStatus_NoError) return(result); + timenow_adjust = (mDNSs32)mDNSRandom(0xFFFFFFFF); + timenow = mDNSPlatformRawTime() + timenow_adjust; + m->timenow = 0; // MUST only be set within mDNS_Lock/mDNS_Unlock section m->timenow_last = timenow; - m->timenow_adjust = 0; + m->timenow_adjust = timenow_adjust; m->NextScheduledEvent = timenow; m->SuppressSending = timenow; m->NextCacheCheck = timenow + 0x78000000; @@ -6143,6 +6742,7 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->NextScheduledResponse = timenow + 0x78000000; m->ExpectUnicastResponse = timenow + 0x78000000; m->RandomQueryDelay = 0; + m->PktNum = 0; m->SendDeregistrations = mDNSfalse; m->SendImmediateAnswers = mDNSfalse; m->SleepState = mDNSfalse; @@ -6171,7 +6771,7 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, // Fields below only required for mDNS Responder... m->hostlabel.c[0] = 0; m->nicelabel.c[0] = 0; - m->hostname.c[0] = 0; + m->MulticastHostname.c[0] = 0; m->HIHardware.c[0] = 0; m->HISoftware.c[0] = 0; m->ResourceRecords = mDNSNULL; @@ -6185,7 +6785,9 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->NumFailedProbes = 0; m->SuppressProbes = 0; +#ifndef UNICAST_DISABLED uDNS_Init(m); +#endif result = mDNSPlatformInit(m); return(result); @@ -6214,6 +6816,9 @@ mDNSexport void mDNS_Close(mDNS *const m) m->mDNS_shutdown = mDNStrue; +#ifndef UNICAST_DISABLED + uDNS_Close(m); +#endif rrcache_totalused = m->rrcache_totalused; for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) { @@ -6228,7 +6833,7 @@ mDNSexport void mDNS_Close(mDNS *const m) // Reset tail pointer back to empty state (not that it really matters on exit, but we'll do it anyway, for the sake of completeness) m->rrcache_tail[slot] = &m->rrcache_hash[slot]; } - debugf("mDNS_Close: RR Cache was using %ld records, %d active", rrcache_totalused, rrcache_active); + debugf("mDNS_Close: RR Cache was using %ld records, %lu active", rrcache_totalused, rrcache_active); if (rrcache_active != m->rrcache_active) LogMsg("*** ERROR *** rrcache_active %lu != m->rrcache_active %lu", rrcache_active, m->rrcache_active); @@ -6252,8 +6857,6 @@ mDNSexport void mDNS_Close(mDNS *const m) } } - uDNS_SuspendLLQs(m); - if (m->ResourceRecords) debugf("mDNS_Close: Sending final packets for deregistering records"); else debugf("mDNS_Close: No deregistering records remain"); diff --git a/mDNSCore/mDNSClientAPI.h b/mDNSCore/mDNSClientAPI.h deleted file mode 100755 index df404f7..0000000 --- a/mDNSCore/mDNSClientAPI.h +++ /dev/null @@ -1,2063 +0,0 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - - Change History (most recent first): - -$Log: mDNSClientAPI.h,v $ -Revision 1.177 2004/06/05 00:04:26 cheshire -: wide-area domains should be returned in reg. domain enumeration - -Revision 1.176 2004/06/04 08:58:29 ksekar -: Keychain integration for secure dynamic update - -Revision 1.175 2004/06/04 00:15:06 cheshire -Move misplaced brackets - -Revision 1.174 2004/06/03 23:30:16 cheshire -Remove extraneous blank lines and white space - -Revision 1.173 2004/06/03 03:09:58 ksekar -: Garbage Collection for Dynamic Updates - -Revision 1.172 2004/06/01 23:46:50 ksekar -: DynDNS: dynamically look up LLQ/Update ports - -Revision 1.171 2004/05/28 23:42:37 ksekar -: Feature: DNS server->client notification on record changes (#7805) - -Revision 1.170 2004/05/18 23:51:25 cheshire -Tidy up all checkin comments to use consistent "" format for bug numbers - -Revision 1.169 2004/05/13 04:54:20 ksekar -Unified list copy/free code. Added symetric list for - -Revision 1.168 2004/05/12 22:03:09 ksekar -Made GetSearchDomainList a true platform-layer call (declaration moved -from mDNSMacOSX.h to mDNSClientAPI.h), impelemted to return "local" -only on non-OSX platforms. Changed call to return a copy of the list -to avoid shared memory issues. Added a routine to free the list. - -Revision 1.167 2004/04/22 04:07:01 cheshire -Fix from Bob Bradley: Don't try to do inline functions on compilers that don't support it - -Revision 1.166 2004/04/22 03:15:56 cheshire -Fix use of "struct __attribute__((__packed__))" so it only applies on GCC >= 2.9 - -Revision 1.165 2004/04/22 03:05:28 cheshire -kDNSClass_ANY should be kDNSQClass_ANY - -Revision 1.164 2004/04/21 02:55:03 cheshire -Update comments describing 'InterfaceActive' field - -Revision 1.163 2004/04/21 02:49:11 cheshire -To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx' - -Revision 1.162 2004/04/15 00:51:28 bradley -Minor tweaks for Windows and C++ builds. Added casts for signed/unsigned integers and 64-bit pointers. -Prefix some functions with mDNS to avoid conflicts. Disable benign warnings on Microsoft compilers. - -Revision 1.161 2004/04/14 23:09:28 ksekar -Support for TSIG signed dynamic updates. - -Revision 1.160 2004/04/09 17:40:26 cheshire -Remove unnecessary "Multicast" field -- it duplicates the semantics of the existing McastTxRx field - -Revision 1.159 2004/04/09 16:37:15 cheshire -Suggestion from Bob Bradley: -Move NumCacheRecordsForInterfaceID() to DNSCommon.c so it's available to all platform layers - -Revision 1.158 2004/04/02 19:38:33 cheshire -Update comment about typical RR TTLs - -Revision 1.157 2004/04/02 19:35:53 cheshire -Add clarifying comments about legal mDNSInterfaceID values - -Revision 1.156 2004/04/02 19:19:48 cheshire -Add code to do optional logging of multi-packet KA list time intervals - -Revision 1.155 2004/03/24 00:29:45 ksekar -Make it safe to call StopQuery in a unicast question callback - -Revision 1.154 2004/03/20 01:05:49 cheshire -Test __LP64__ and __ILP64__ to compile properly on a wider range of 64-bit architectures - -Revision 1.153 2004/03/13 01:57:33 ksekar -: DynDNS: Dynamic update of service records - -Revision 1.152 2004/03/09 02:27:16 cheshire -Remove erroneous underscore in 'packed_struct' (makes no difference now, but might in future) - -Revision 1.151 2004/03/02 03:21:56 cheshire - Properly support "_services._dns-sd._udp" meta-queries - -Revision 1.150 2004/02/21 02:06:24 cheshire -Can't use anonymous unions -- they're non-standard and don't work on all compilers - -Revision 1.149 2004/02/06 23:04:19 ksekar -Basic Dynamic Update support via mDNS_Register (dissabled via -UNICAST_REGISTRATION #define) - -Revision 1.148 2004/02/03 19:47:36 ksekar -Added an asyncronous state machine mechanism to uDNS.c, including -calls to find the parent zone for a domain name. Changes include code -in repository previously dissabled via "#if 0 //incomplete". Codepath -is currently unused, and will be called to create update records, etc. - -Revision 1.147 2004/02/03 18:57:35 cheshire -Update comment for "IsLocalDomain()" - -Revision 1.146 2004/01/30 02:20:24 bradley -Map inline to __inline when building with Microsoft C compilers since they do not support C99 inline. - -Revision 1.145 2004/01/29 02:59:17 ksekar -Unicast DNS: Changed from a resource record oriented question/response -matching to packet based matching. New callback architecture allows -collections of records in a response to be processed differently -depending on the nature of the request, and allows the same structure -to be used for internal and client-driven queries with different processing needs. - -Revision 1.144 2004/01/28 20:20:45 ksekar -Unified ActiveQueries and ActiveInternalQueries lists, using a flag to -demux them. Check-in includes work-in-progress code, #ifdef'd out. - -Revision 1.143 2004/01/28 03:41:00 cheshire -: Need ability to do targeted queries as well as multicast queries - -Revision 1.142 2004/01/28 02:30:07 ksekar -Added default Search Domains to unicast browsing, controlled via -Networking sharing prefs pane. Stopped sending unicast messages on -every interface. Fixed unicast resolving via mach-port API. - -Revision 1.141 2004/01/27 20:15:22 cheshire -: Time to prune obsolete code for listening on port 53 - -Revision 1.140 2004/01/24 23:37:08 cheshire -At Kiren's suggestion, made functions to convert mDNSOpaque16s to/from integer values - -Revision 1.139 2004/01/24 08:46:26 bradley -Added InterfaceID<->Index platform interfaces since they are now used by all platforms for the DNS-SD APIs. - -Revision 1.138 2004/01/24 04:59:15 cheshire -Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again - -Revision 1.137 2004/01/24 03:40:56 cheshire -Move mDNSAddrIsDNSMulticast() from DNSCommon.h to mDNSClientAPI.h so clients can use it - -Revision 1.136 2004/01/24 03:38:27 cheshire -Fix minor syntactic error: Headers should use "extern" declarations, not "mDNSexport" - -Revision 1.135 2004/01/23 23:23:15 ksekar -Added TCP support for truncated unicast messages. - -Revision 1.134 2004/01/22 03:54:11 cheshire -Create special meta-interface 'mDNSInterface_ForceMCast' (-2), -which means "do this query via multicast, even if it's apparently a unicast domain" - -Revision 1.133 2004/01/22 03:48:41 cheshire -Make sure uDNS client doesn't accidentally use query ID zero - -Revision 1.132 2004/01/22 03:43:08 cheshire -Export constants like mDNSInterface_LocalOnly so that the client layers can use them - -Revision 1.131 2004/01/21 21:53:18 cheshire -: Don't try to receive unicast responses if we're not the first to bind to the UDP port - -Revision 1.130 2003/12/14 05:05:29 cheshire -Add comments explaining mDNS_Init_NoCache and mDNS_Init_ZeroCacheSize - -Revision 1.129 2003/12/13 03:05:27 ksekar -: DynDNS: Unicast query of service records - -Revision 1.128 2003/12/01 21:44:23 cheshire -Add mStatus_BadInterfaceErr = -65552 for consistency with dns_sd.h - -Revision 1.127 2003/12/01 18:26:37 cheshire -Also pack the OpaqueXX union types. Otherwise, on some systems, mDNSOpaque16 is four bytes! - -Revision 1.126 2003/12/01 18:23:48 cheshire -: Scalar size problem in mDNS code on some 64-bit architectures - -Revision 1.125 2003/11/22 00:18:27 cheshire -Add compile-time asserts to verify correct sizes of mDNSu32, mDNSOpaque16, etc. - -Revision 1.124 2003/11/20 22:59:54 cheshire -Changed runtime checks in mDNS.c to be compile-time checks in mDNSClientAPI.h -Thanks to Bob Bradley for suggesting the ingenious compiler trick to make this work. - -Revision 1.123 2003/11/20 22:53:01 cheshire -Add comment about MAX_ESCAPED_DOMAIN_LABEL - -Revision 1.122 2003/11/20 20:49:53 cheshire -Another fix from HP: Use packedstruct macro to ensure proper packing for on-the-wire packet structures - -Revision 1.121 2003/11/20 05:01:38 cheshire -Update comments; add explanation of Advertise/DontAdvertiseLocalAddresses - -Revision 1.120 2003/11/14 20:59:08 cheshire -Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h. -Best solution is just to combine mDNSClientAPI.h and mDNSPlatformFunctions.h into a single file. - -Revision 1.119 2003/11/14 19:47:52 cheshire -Define symbol MAX_ESCAPED_DOMAIN_NAME to indicate recommended buffer size for ConvertDomainNameToCString - -Revision 1.118 2003/11/14 19:18:34 cheshire -Move AssignDomainName macro to mDNSClientAPI.h to that client layers can use it too - -Revision 1.117 2003/11/08 23:32:24 cheshire -Gave name to anonymous struct, to avoid errors on certain compilers. -(Thanks to ramaprasad.kr@hp.com for reporting this.) - -Revision 1.116 2003/11/07 03:32:56 cheshire - mDNSResponder delivers answers in inconsistent order -This is the real fix. Checkin 1.312 was overly simplistic; Calling GetFreeCacheRR() can sometimes -purge records from the cache, causing tail pointer *rp to be stale on return. The correct fix is -to maintain a system-wide tail pointer for each cache slot, and then if neccesary GetFreeCacheRR() -can update this pointer, so that mDNSCoreReceiveResponse() appends records in the right place. - -Revision 1.115 2003/09/23 00:53:54 cheshire -NumFailedProbes should be unsigned - -Revision 1.114 2003/08/29 19:44:15 cheshire - Traffic reduction: Eliminate synchronized QUs when a new service appears -1. Use m->RandomQueryDelay to impose a random delay in the range 0-500ms on queries - that already have at least one unique answer in the cache -2. For these queries, go straight to QM, skipping QU - -Revision 1.113 2003/08/21 19:31:58 cheshire -Cosmetic: Swap order of fields - -Revision 1.112 2003/08/21 19:27:36 cheshire - Traffic reduction: No need to announce record for longer than TTL - -Revision 1.111 2003/08/21 02:21:50 cheshire - Efficiency: Reduce repeated queries - -Revision 1.110 2003/08/20 23:39:31 cheshire - Review syslog messages, and remove as appropriate - -Revision 1.109 2003/08/19 22:24:10 cheshire -Comment change - -Revision 1.108 2003/08/19 22:20:00 cheshire - Don't use IPv6 on interfaces that have a routable IPv4 address configured -More minor refinements - -Revision 1.107 2003/08/19 06:48:25 cheshire - Guard against excessive record updates -Each record starts with 10 UpdateCredits. -Every update consumes one UpdateCredit. -UpdateCredits are replenished at a rate of one one per minute, up to a maximum of 10. -As the number of UpdateCredits declines, the number of announcements is similarly scaled back. -When fewer than 5 UpdateCredits remain, the first announcement is also delayed by an increasing amount. - -Revision 1.106 2003/08/19 04:49:28 cheshire - Interaction between v4, v6 and dual-stack hosts not working quite right -1. A dual-stack host should only suppress its own query if it sees the same query from other hosts on BOTH IPv4 and IPv6. -2. When we see the first v4 (or first v6) member of a group, we re-trigger questions and probes on that interface. -3. When we see the last v4 (or v6) member of a group go away, we revalidate all the records received on that interface. - -Revision 1.105 2003/08/19 02:33:37 cheshire -Update comments - -Revision 1.104 2003/08/19 02:31:11 cheshire - mDNSResponder overenthusiastic with final expiration queries -Final expiration queries now only mark the question for sending on the particular interface -pertaining to the record that's expiring. - -Revision 1.103 2003/08/18 19:05:44 cheshire - UpdateRecord not working right -Added "newrdlength" field to hold new length of updated rdata - -Revision 1.102 2003/08/16 03:39:00 cheshire - InterfaceID -1 indicates "local only" - -Revision 1.101 2003/08/15 20:16:02 cheshire - mDNSResponder takes too much RPRVT -We want to avoid touching the rdata pages, so we don't page them in. -1. RDLength was stored with the rdata, which meant touching the page just to find the length. - Moved this from the RData to the ResourceRecord object. -2. To avoid unnecessarily touching the rdata just to compare it, - compute a hash of the rdata and store the hash in the ResourceRecord object. - -Revision 1.100 2003/08/14 19:29:04 cheshire - Include cache records in SIGINFO output -Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSClientAPI.h so daemon.c can use them - -Revision 1.99 2003/08/14 02:17:05 cheshire - Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord - -Revision 1.98 2003/08/12 19:56:23 cheshire -Update to APSL 2.0 - -Revision 1.97 2003/08/12 14:59:27 cheshire - Rate-limiting blocks some legitimate responses -When setting LastMCTime also record LastMCInterface. When checking LastMCTime to determine -whether to suppress the response, also check LastMCInterface to see if it matches. - -Revision 1.96 2003/08/12 13:57:04 cheshire - Improve cache performance -Changed the number of hash table slots from 37 to 499 - -Revision 1.95 2003/08/09 00:55:02 cheshire - mDNSResponder is taking 20-30% of the CPU -Don't scan the whole cache after every packet. - -Revision 1.94 2003/08/09 00:35:29 cheshire - -Revision 1.93 2003/08/08 18:55:48 cheshire - Guard against time going backwards - -Revision 1.92 2003/08/08 18:36:04 cheshire - Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug - -Revision 1.91 2003/08/06 21:33:39 cheshire -Fix compiler warnings on PocketPC 2003 (Windows CE) - -Revision 1.90 2003/08/06 20:30:17 cheshire -Add structure definition for rdataMX (not currently used, but good to have it for completeness) - -Revision 1.89 2003/08/06 18:58:19 cheshire -Update comments - -Revision 1.88 2003/07/24 23:45:44 cheshire -To eliminate compiler warnings, changed definition of mDNSBool from -"unsigned char" to "int", since "int" is in fact truly the type that C uses -for the result of comparison operators (a: Feature: New Rendezvous APIs (#7875) (mDNSResponder component) -Added error type for incompatibility between daemon and client versions - -Revision 1.85 2003/07/19 03:23:13 cheshire - mDNSResponder needs to receive and cache larger records - -Revision 1.84 2003/07/18 23:52:12 cheshire -To improve consistency of field naming, global search-and-replace: -NextProbeTime -> NextScheduledProbe -NextResponseTime -> NextScheduledResponse - -Revision 1.83 2003/07/18 00:29:59 cheshire - Remove mDNSResponder version from packet header and use HINFO record instead - -Revision 1.82 2003/07/17 17:35:04 cheshire - Rate-limit responses, to guard against packet flooding - -Revision 1.81 2003/07/16 05:01:36 cheshire -Add fields 'LargeAnswers' and 'ExpectUnicastResponse' in preparation for - Need to implement "unicast response" request, using top bit of qclass - -Revision 1.80 2003/07/15 01:55:12 cheshire - Need to implement service registration with subtypes - -Revision 1.79 2003/07/13 02:28:00 cheshire - SendResponses didn't all its responses -Delete all references to RRInterfaceActive -- it's now superfluous - -Revision 1.78 2003/07/13 01:47:53 cheshire -Fix one error and one warning in the Windows build - -Revision 1.77 2003/07/11 01:32:38 cheshire -Syntactic cleanup (no change to funcationality): Now that we only have one host name, -rename field "hostname1" to "hostname", and field "RR_A1" to "RR_A". - -Revision 1.76 2003/07/11 01:28:00 cheshire - No more local.arpa - -Revision 1.75 2003/07/02 21:19:45 cheshire - Update copyright notices, etc., in source code comments - -Revision 1.74 2003/07/02 02:41:23 cheshire - mDNSResponder needs to start with a smaller cache and then grow it as needed - -Revision 1.73 2003/06/10 04:24:39 cheshire - React when we observe other people query unsuccessfully for a record that's in our cache -Some additional refinements: -Don't try to do this for unicast-response queries -better tracking of Qs and KAs in multi-packet KA lists - -Revision 1.72 2003/06/10 01:46:27 cheshire -Add better comments explaining how these data structures are intended to be used from the client layer - -Revision 1.71 2003/06/07 06:45:05 cheshire - No need for multiple machines to all be sending the same queries - -Revision 1.70 2003/06/07 04:50:53 cheshire - React when we observe other people query unsuccessfully for a record that's in our cache - -Revision 1.69 2003/06/07 04:22:17 cheshire -Add MsgBuffer for error log and debug messages - -Revision 1.68 2003/06/07 01:46:38 cheshire - When query produces zero results, call mDNS_Reconfirm() on any antecedent records - -Revision 1.67 2003/06/07 01:22:14 cheshire - mDNSResponder needs an mDNS_Reconfirm() function - -Revision 1.66 2003/06/07 00:59:43 cheshire - Need some randomness to spread queries on the network - -Revision 1.65 2003/06/06 21:41:11 cheshire -For consistency, mDNS_StopQuery() should return an mStatus result, just like all the other mDNSCore routines - -Revision 1.64 2003/06/06 21:38:55 cheshire -Renamed 'NewData' as 'FreshData' (The data may not be new data, just a refresh of data that we -already had in our cache. This refreshes our TTL on the data, but the data itself stays the same.) - -Revision 1.63 2003/06/06 17:20:14 cheshire -For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass -(Global search-and-replace; no functional change to code execution.) - -Revision 1.62 2003/06/04 01:25:33 cheshire - Cannot perform multi-packet known-answer suppression messages -Display time interval between first and subsequent queries - -Revision 1.61 2003/06/03 05:02:16 cheshire - Duplicate registrations not handled as efficiently as they should be - -Revision 1.60 2003/05/31 00:09:49 cheshire - Add ability to discover what services are on a network - -Revision 1.59 2003/05/29 06:11:35 cheshire -: Report if there appear to be too many "Resolve" callbacks - -Revision 1.58 2003/05/29 05:48:06 cheshire -Minor fix for when generating printf warnings: mDNS_snprintf arguments are now 3,4 - -Revision 1.57 2003/05/26 03:21:27 cheshire -Tidy up address structure naming: -mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) -mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 -mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 - -Revision 1.56 2003/05/26 03:01:27 cheshire - sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead - -Revision 1.55 2003/05/26 00:47:30 cheshire -Comment clarification - -Revision 1.54 2003/05/24 16:39:48 cheshire - SendResponses also needs to handle multihoming better - -Revision 1.53 2003/05/23 02:15:37 cheshire -Fixed misleading use of the term "duplicate suppression" where it should have -said "known answer suppression". (Duplicate answer suppression is something -different, and duplicate question suppression is yet another thing, so the use -of the completely vague term "duplicate suppression" was particularly bad.) - -Revision 1.52 2003/05/22 02:29:22 cheshire - SendQueries needs to handle multihoming better -Complete rewrite of SendQueries. Works much better now :-) - -Revision 1.51 2003/05/21 20:14:55 cheshire -Fix comments and warnings - -Revision 1.50 2003/05/14 07:08:36 cheshire - mDNSResponder should be smarter about reconfigurations -Previously, when there was any network configuration change, mDNSResponder -would tear down the entire list of active interfaces and start again. -That was very disruptive, and caused the entire cache to be flushed, -and caused lots of extra network traffic. Now it only removes interfaces -that have really gone, and only adds new ones that weren't there before. - -Revision 1.49 2003/05/07 01:49:36 cheshire -Remove "const" in ConstructServiceName prototype - -Revision 1.48 2003/05/07 00:18:44 cheshire -Fix typo: "kDNSQClass_Mask" should be "kDNSClass_Mask" - -Revision 1.47 2003/05/06 00:00:46 cheshire - Rationalize naming of domainname manipulation functions - -Revision 1.46 2003/04/30 20:39:09 cheshire -Add comment - -Revision 1.45 2003/04/29 00:40:50 cheshire -Fix compiler warnings - -Revision 1.44 2003/04/26 02:41:56 cheshire - Change timenow from a local variable to a structure member - -Revision 1.43 2003/04/25 01:45:56 cheshire - mDNS_RegisterNoSuchService needs to include a host name - -Revision 1.42 2003/04/15 20:58:31 jgraessl - - Added a hash to lookup records in the cache. - -Revision 1.41 2003/04/15 18:09:13 jgraessl - - -Reviewed by: Stuart Cheshire -Added code to keep track of when the next cache item will expire so we can -call TidyRRCache only when necessary. - -Revision 1.40 2003/03/29 01:55:19 cheshire - mDNSResponder sometimes suffers false self-conflicts when it sees its own packets -Solution: Major cleanup of packet timing and conflict handling rules - -Revision 1.39 2003/03/27 03:30:55 cheshire - Name conflicts not handled properly, resulting in memory corruption, and eventual crash -Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback -Fixes: -1. Make mDNS_DeregisterInterface() safe to call from a callback -2. Make HostNameCallback() use mDNS_DeadvertiseInterface() instead - (it never really needed to deregister the interface at all) - -Revision 1.38 2003/03/15 04:40:36 cheshire -Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID" - -Revision 1.37 2003/03/14 21:34:11 cheshire - Can't setup and print to Lexmark PS printers via Airport Extreme -Increase size of cache rdata from 512 to 768 - -Revision 1.36 2003/03/05 03:38:35 cheshire - Bogus error message in console: died or deallocated, but no record of client can be found! -Fixed by leaving client in list after conflict, until client explicitly deallocates - -Revision 1.35 2003/02/21 02:47:54 cheshire - mDNSResponder needs performance improvements -Several places in the code were calling CacheRRActive(), which searched the entire -question list every time, to see if this cache resource record answers any question. -Instead, we now have a field "CRActiveQuestion" in the resource record structure - -Revision 1.34 2003/02/21 01:54:08 cheshire - mDNSResponder needs performance improvements -Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt") - -Revision 1.33 2003/02/20 06:48:32 cheshire - Xserve RAID needs to do interface-specific registrations -Reviewed by: Josh Graessley, Bob Bradley - -Revision 1.32 2003/01/31 03:35:59 cheshire - mDNSResponder sometimes fails to find the correct results -When there were *two* active questions in the list, they were incorrectly -finding *each other* and *both* being marked as duplicates of another question - -Revision 1.31 2003/01/29 02:46:37 cheshire -Fix for IPv6: -A physical interface is identified solely by its InterfaceID (not by IP and type). -On a given InterfaceID, mDNSCore may send both v4 and v6 multicasts. -In cases where the requested outbound protocol (v4 or v6) is not supported on -that InterfaceID, the platform support layer should simply discard that packet. - -Revision 1.30 2003/01/29 01:47:08 cheshire -Rename 'Active' to 'CRActive' or 'InterfaceActive' for improved clarity - -Revision 1.29 2003/01/28 05:23:43 cheshire - mDNSResponder sometimes fails to find the correct results -Add 'Active' flag for interfaces - -Revision 1.28 2003/01/28 01:35:56 cheshire -Revise comment about ThisQInterval to reflect new semantics - -Revision 1.27 2003/01/13 23:49:42 jgraessl -Merged changes for the following fixes in to top of tree: - computer name changes not handled properly - service name changes are not properly handled - announcements sent in pairs, failing chattiness test - -Revision 1.26 2002/12/23 22:13:28 jgraessl - -Reviewed by: Stuart Cheshire -Initial IPv6 support for mDNSResponder. - -Revision 1.25 2002/09/21 20:44:49 zarzycki -Added APSL info - -Revision 1.24 2002/09/19 23:47:35 cheshire -Added mDNS_RegisterNoSuchService() function for assertion of non-existance -of a particular named service - -Revision 1.23 2002/09/19 21:25:34 cheshire -mDNS_snprintf() doesn't need to be in a separate file - -Revision 1.22 2002/09/19 04:20:43 cheshire -Remove high-ascii characters that confuse some systems - -Revision 1.21 2002/09/17 01:06:35 cheshire -Change mDNS_AdvertiseLocalAddresses to be a parameter to mDNS_Init() - -Revision 1.20 2002/09/16 18:41:41 cheshire -Merge in license terms from Quinn's copy, in preparation for Darwin release - -*/ - -#ifndef __mDNSClientAPI_h -#define __mDNSClientAPI_h - -#include // stdarg.h is required for for va_list support for the mDNS_vsnprintf declaration -#include "mDNSDebug.h" - -#ifdef __cplusplus - extern "C" { -#endif - -// *************************************************************************** -// Function scope indicators - -// If you see "mDNSlocal" before a function name in a C file, it means the function is not callable outside this file -#ifndef mDNSlocal -#define mDNSlocal static -#endif -// If you see "mDNSexport" before a symbol in a C file, it means the symbol is exported for use by clients -// For every "mDNSexport" in a C file, there needs to be a corresponding "extern" declaration in some header file -// (When a C file #includes a header file, the "extern" declarations tell the compiler: -// "This symbol exists -- but not necessarily in this C file.") -#ifndef mDNSexport -#define mDNSexport -#endif - -// *************************************************************************** -// Structure packing macro - -// If we're not using GNUC, it's not fatal. -// Most compilers naturally pack the on-the-wire structures correctly anyway, so a plain "struct" is usually fine. -// In the event that structures are not packed correctly, mDNS_Init() will detect this and report an error, so the -// developer will know what's wrong, and can investigate what needs to be done on that compiler to provide proper packing. -#ifndef packedstruct - #if ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 9))) - #define packedstruct struct __attribute__((__packed__)) - #define packedunion union __attribute__((__packed__)) - #else - #define packedstruct struct - #define packedunion union - #endif -#endif - -// *************************************************************************** -#if 0 -#pragma mark - DNS Resource Record class and type constants -#endif - -typedef enum // From RFC 1035 - { - kDNSClass_IN = 1, // Internet - kDNSClass_CS = 2, // CSNET - kDNSClass_CH = 3, // CHAOS - kDNSClass_HS = 4, // Hesiod - kDNSClass_NONE = 254, // Used in DNS UPDATE [RFC 2136] - - kDNSClass_Mask = 0x7FFF,// Multicast DNS uses the bottom 15 bits to identify the record class... - kDNSClass_UniqueRRSet = 0x8000,// ... and the top bit indicates that all other cached records are now invalid - - kDNSQClass_ANY = 255, // Not a DNS class, but a DNS query class, meaning "all classes" - kDNSQClass_UnicastResponse = 0x8000 // Top bit set in a question means "unicast response acceptable" - } DNS_ClassValues; - -typedef enum // From RFC 1035 - { - kDNSType_A = 1, // 1 Address - kDNSType_NS, // 2 Name Server - kDNSType_MD, // 3 Mail Destination - kDNSType_MF, // 4 Mail Forwarder - kDNSType_CNAME, // 5 Canonical Name - kDNSType_SOA, // 6 Start of Authority - kDNSType_MB, // 7 Mailbox - kDNSType_MG, // 8 Mail Group - kDNSType_MR, // 9 Mail Rename - kDNSType_NULL, // 10 NULL RR - kDNSType_WKS, // 11 Well-known-service - kDNSType_PTR, // 12 Domain name pointer - kDNSType_HINFO, // 13 Host information - kDNSType_MINFO, // 14 Mailbox information - kDNSType_MX, // 15 Mail Exchanger - kDNSType_TXT, // 16 Arbitrary text string - - kDNSType_AAAA = 28, // 28 IPv6 address - kDNSType_SRV = 33, // 33 Service record - kDNSType_OPT = 41, // EDNS0 OPT record - kDNSType_TSIG = 250, // 250 Transaction Signature - - kDNSQType_ANY = 255 // Not a DNS type, but a DNS query type, meaning "all types" - } DNS_TypeValues; - -// *************************************************************************** -#if 0 -#pragma mark - Simple types -#endif - -// mDNS defines its own names for these common types to simplify portability across -// multiple platforms that may each have their own (different) names for these types. -typedef int mDNSBool; -typedef signed char mDNSs8; -typedef unsigned char mDNSu8; -typedef signed short mDNSs16; -typedef unsigned short mDNSu16; - -// says -// __LP64__ _LP64 -// These macros are defined, with value 1, if (and only if) the compilation is -// for a target where long int and pointer both use 64-bits and int uses 32-bit. -// says -// Macro Name __LP64__ Value 1 -// A quick Google search for "defined(__LP64__)" OR "#ifdef __LP64__" gives 2590 hits and -// a search for "#if __LP64__" gives only 12, so I think we'll go with the majority and use defined() -#if defined(_LP64) || defined(__LP64__) -typedef signed int mDNSs32; -typedef unsigned int mDNSu32; -#elif defined(_ILP64) || defined(__ILP64__) -typedef signed int32 mDNSs32; -typedef unsigned int32 mDNSu32; -#else -typedef signed long mDNSs32; -typedef unsigned long mDNSu32; -#endif - -// To enforce useful type checking, we make mDNSInterfaceID be a pointer to a dummy struct -// This way, mDNSInterfaceIDs can be assigned, and compared with each other, but not with other types -// Declaring the type to be the typical generic "void *" would lack this type checking -typedef struct mDNSInterfaceID_dummystruct { void *dummy; } *mDNSInterfaceID; - -// These types are for opaque two- and four-byte identifiers. -// The "NotAnInteger" fields of the unions allow the value to be conveniently passed around in a -// register for the sake of efficiency, and compared for equality or inequality, but don't forget -- -// just because it is in a register doesn't mean it is an integer. Operations like greater than, -// less than, add, multiply, increment, decrement, etc., are undefined for opaque identifiers, -// and if you make the mistake of trying to do those using the NotAnInteger field, then you'll -// find you get code that doesn't work consistently on big-endian and little-endian machines. -typedef packedunion { mDNSu8 b[2]; mDNSu16 NotAnInteger; } mDNSOpaque16; -typedef packedunion { mDNSu8 b[4]; mDNSu32 NotAnInteger; } mDNSOpaque32; -typedef packedunion { mDNSu8 b[16]; mDNSu16 w[8]; mDNSu32 l[4]; } mDNSOpaque128; - -typedef mDNSOpaque16 mDNSIPPort; // An IP port is a two-byte opaque identifier (not an integer) -typedef mDNSOpaque32 mDNSv4Addr; // An IP address is a four-byte opaque identifier (not an integer) -typedef mDNSOpaque128 mDNSv6Addr; // An IPv6 address is a 16-byte opaque identifier (not an integer) - -enum - { - mDNSAddrType_None = 0, - mDNSAddrType_IPv4 = 4, - mDNSAddrType_IPv6 = 6, - mDNSAddrType_Unknown = ~0 // Special marker value used in known answer list recording - }; - -typedef struct - { - mDNSs32 type; - union { mDNSv6Addr v6; mDNSv4Addr v4; } ip; - } mDNSAddr; - -enum { mDNSfalse = 0, mDNStrue = 1 }; - -#define mDNSNULL 0L - -enum - { - mStatus_Waiting = 1, - mStatus_NoError = 0, - - // mDNS return values are in the range FFFE FF00 (-65792) to FFFE FFFF (-65537) - // The top end of the range (FFFE FFFF) is used for error codes; - // the bottom end of the range (FFFE FF00) is used for non-error values; - - // Error codes: - mStatus_UnknownErr = -65537, // 0xFFFE FFFF - mStatus_NoSuchNameErr = -65538, - mStatus_NoMemoryErr = -65539, - mStatus_BadParamErr = -65540, - mStatus_BadReferenceErr = -65541, - mStatus_BadStateErr = -65542, - mStatus_BadFlagsErr = -65543, - mStatus_UnsupportedErr = -65544, - mStatus_NotInitializedErr = -65545, - mStatus_NoCache = -65546, - mStatus_AlreadyRegistered = -65547, - mStatus_NameConflict = -65548, - mStatus_Invalid = -65549, - // = -65550, - mStatus_Incompatible = -65551, - mStatus_BadInterfaceErr = -65552, - mStatus_Refused = -65553, - mStatus_NoSuchRecord = -65554, - mStatus_NoAuth = -65555, - // -65556 - -65789 currently unused - - // Non-error values: - mStatus_GrowCache = -65790, - mStatus_ConfigChanged = -65791, - mStatus_MemFree = -65792, // 0xFFFE FF00 - - // tcp connection status - mStatus_ConnectionPending = -65793, - mStatus_ConnectionFailed = -65794, - mStatus_ConnectionEstablished = -65795 - }; - -typedef mDNSs32 mStatus; - -// RFC 1034/1035 specify that a domain label consists of a length byte plus up to 63 characters -#define MAX_DOMAIN_LABEL 63 -typedef struct { mDNSu8 c[ 64]; } domainlabel; // One label: length byte and up to 63 characters - -// RFC 1034/1035 specify that a domain name, including length bytes, data bytes, and terminating zero, may be up to 255 bytes long -#define MAX_DOMAIN_NAME 255 -typedef struct { mDNSu8 c[256]; } domainname; // Up to 255 bytes of length-prefixed domainlabels - -typedef struct { mDNSu8 c[256]; } UTF8str255; // Null-terminated C string - -// The longest legal textual form of a DNS name is 1005 bytes, including the C-string terminating NULL at the end. -// Explanation: -// When a native domainname object is converted to printable textual form using ConvertDomainNameToCString(), -// non-printing characters are represented in the conventional DNS way, as '\ddd', where ddd is a three-digit decimal number. -// The longest legal domain name is 255 bytes, in the form of four labels as shown below: -// Length byte, 63 data bytes, length byte, 63 data bytes, length byte, 63 data bytes, length byte, 61 data bytes, zero byte. -// Each label is encoded textually as characters followed by a trailing dot. -// If every character has to be represented as a four-byte escape sequence, then this makes the maximum textual form four labels -// plus the C-string terminating NULL as shown below: -// 63*4+1 + 63*4+1 + 63*4+1 + 61*4+1 + 1 = 1005. -// Note that MAX_ESCAPED_DOMAIN_LABEL is not normally used: If you're only decoding a single label, escaping is usually not required. -// It is for domain names, where dots are used as label separators, that proper escaping is vital. -#define MAX_ESCAPED_DOMAIN_LABEL 254 -#define MAX_ESCAPED_DOMAIN_NAME 1005 - -// *************************************************************************** -#if 0 -#pragma mark - DNS Message structures -#endif - -#define mDNS_numZones numQuestions -#define mDNS_numPrereqs numAnswers -#define mDNS_numUpdates numAuthorities - -typedef packedstruct - { - mDNSOpaque16 id; - mDNSOpaque16 flags; - mDNSu16 numQuestions; - mDNSu16 numAnswers; - mDNSu16 numAuthorities; - mDNSu16 numAdditionals; - } DNSMessageHeader; - -// We can send and receive packets up to 9000 bytes (Ethernet Jumbo Frame size, if that ever becomes widely used) -// However, in the normal case we try to limit packets to 1500 bytes so that we don't get IP fragmentation on standard Ethernet -// 40 (IPv6 header) + 8 (UDP header) + 12 (DNS message header) + 1440 (DNS message body) = 1500 total -#define AbsoluteMaxDNSMessageData 8940 -#define NormalMaxDNSMessageData 1440 -typedef packedstruct - { - DNSMessageHeader h; // Note: Size 12 bytes - mDNSu8 data[AbsoluteMaxDNSMessageData]; // 40 (IPv6) + 8 (UDP) + 12 (DNS header) + 8940 (data) = 9000 - } DNSMessage; - -// *************************************************************************** -#if 0 -#pragma mark - Resource Record structures -#endif - -// Authoritative Resource Records: -// There are four basic types: Shared, Advisory, Unique, Known Unique - -// * Shared Resource Records do not have to be unique -// -- Shared Resource Records are used for DNS-SD service PTRs -// -- It is okay for several hosts to have RRs with the same name but different RDATA -// -- We use a random delay on responses to reduce collisions when all the hosts respond to the same query -// -- These RRs typically have moderately high TTLs (e.g. one hour) -// -- These records are announced on startup and topology changes for the benefit of passive listeners -// -- These records send a goodbye packet when deregistering -// -// * Advisory Resource Records are like Shared Resource Records, except they don't send a goodbye packet -// -// * Unique Resource Records should be unique among hosts within any given mDNS scope -// -- The majority of Resource Records are of this type -// -- If two entities on the network have RRs with the same name but different RDATA, this is a conflict -// -- Responses may be sent immediately, because only one host should be responding to any particular query -// -- These RRs typically have low TTLs (e.g. a few minutes) -// -- On startup and after topology changes, a host issues queries to verify uniqueness - -// * Known Unique Resource Records are treated like Unique Resource Records, except that mDNS does -// not have to verify their uniqueness because this is already known by other means (e.g. the RR name -// is derived from the host's IP or Ethernet address, which is already known to be a unique identifier). - -// Summary of properties of different record types: -// Probe? Does this record type send probes before announcing? -// Conflict? Does this record type react if we observe an apparent conflict? -// Goodbye? Does this record type send a goodbye packet on departure? -// -// Probe? Conflict? Goodbye? Notes -// Unregistered Should not appear in any list (sanity check value) -// Shared No No Yes e.g. Service PTR record -// Deregistering No No Yes Shared record about to announce its departure and leave the list -// Advisory No No No -// Unique Yes Yes No Record intended to be unique -- will probe to verify -// Verified Yes Yes No Record has completed probing, and is verified unique -// KnownUnique No Yes No Record is assumed by other means to be unique - -// Valid lifecycle of a record: -// Unregistered -> Shared -> Deregistering -(goodbye)-> Unregistered -// Unregistered -> Advisory -> Unregistered -// Unregistered -> Unique -(probe)-> Verified -> Unregistered -// Unregistered -> KnownUnique -> Unregistered - -// Each Authoritative kDNSRecordType has only one bit set. This makes it easy to quickly see if a record -// is one of a particular set of types simply by performing the appropriate bitwise masking operation. - -// Cache Resource Records (received from the network): -// There are four basic types: Answer, Unique Answer, Additional, Unique Additional -// Bit 7 (the top bit) of kDNSRecordType is always set for Cache Resource Records; always clear for Authoritative Resource Records -// Bit 6 (value 0x40) is set for answer records; clear for additional records -// Bit 5 (value 0x20) is set for records received with the kDNSClass_UniqueRRSet - -enum - { - kDNSRecordTypeUnregistered = 0x00, // Not currently in any list - kDNSRecordTypeDeregistering = 0x01, // Shared record about to announce its departure and leave the list - - kDNSRecordTypeUnique = 0x02, // Will become a kDNSRecordTypeVerified when probing is complete - - kDNSRecordTypeAdvisory = 0x04, // Like Shared, but no goodbye packet - kDNSRecordTypeShared = 0x08, // Shared means record name does not have to be unique -- use random delay on responses - - kDNSRecordTypeVerified = 0x10, // Unique means mDNS should check that name is unique (and then send immediate responses) - kDNSRecordTypeKnownUnique = 0x20, // Known Unique means mDNS can assume name is unique without checking - // For Dynamic Update records, Known Unique means the record must already exist on the server. - kDNSRecordTypeUniqueMask = (kDNSRecordTypeUnique | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique), - kDNSRecordTypeActiveMask = (kDNSRecordTypeAdvisory | kDNSRecordTypeShared | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique), - - kDNSRecordTypePacketAdd = 0x80, // Received in the Additional Section of a DNS Response - kDNSRecordTypePacketAddUnique = 0xA0, // Received in the Additional Section of a DNS Response with kDNSClass_UniqueRRSet set - kDNSRecordTypePacketAns = 0xC0, // Received in the Answer Section of a DNS Response - kDNSRecordTypePacketAnsUnique = 0xE0, // Received in the Answer Section of a DNS Response with kDNSClass_UniqueRRSet set - - kDNSRecordTypePacketAnsMask = 0x40, // True for PacketAns and PacketAnsUnique - kDNSRecordTypePacketUniqueMask = 0x20 // True for PacketAddUnique and PacketAnsUnique - }; - -typedef packedstruct { mDNSu16 priority; mDNSu16 weight; mDNSIPPort port; domainname target; } rdataSRV; -typedef packedstruct { mDNSu16 preference; domainname exchange; } rdataMX; -typedef packedstruct { domainname mname; domainname rname; mDNSOpaque32 serial; mDNSOpaque32 refresh; - mDNSOpaque32 retry; mDNSOpaque32 expire; mDNSOpaque32 min; } rdataSOA; - -typedef packedstruct - { - mDNSu16 vers; - mDNSu16 llqOp; - mDNSu16 err; - mDNSu8 id[8]; - mDNSu32 lease; - } LLQOptData; - -// NOTE: rdataOpt format may be repeated an arbitrary number of times in a single resource record -typedef packedstruct - { - mDNSu16 opt; - mDNSu16 optlen; - union { LLQOptData llq; mDNSu32 lease; } OptData; - } rdataOpt; - -// StandardAuthRDSize is 264 (256+8), which is large enough to hold a maximum-sized SRV record -// MaximumRDSize is 8K the absolute maximum we support (at least for now) -#define StandardAuthRDSize 264 -#define MaximumRDSize 8192 - -// InlineCacheRDSize is 64 -// Records received from the network with rdata this size or less have their rdata stored right in the CacheRecord object -// Records received from the network with rdata larger than this have additional storage allocated for the rdata -// A quick unscientific sample from a busy network at Apple with lots of machines revealed this: -// 1461 records in cache -// 292 were one-byte TXT records -// 136 were four-byte A records -// 184 were sixteen-byte AAAA records -// 780 were various PTR, TXT and SRV records from 12-64 bytes -// Only 69 records had rdata bigger than 64 bytes -#define InlineCacheRDSize 64 - -typedef union - { - mDNSu8 data[StandardAuthRDSize]; - mDNSv4Addr ip; // For 'A' record - mDNSv6Addr ipv6; // For 'AAAA' record - domainname name; // For PTR, NS, and CNAME records - UTF8str255 txt; // For TXT record - rdataSRV srv; // For SRV record - rdataMX mx; // For MX record - rdataSOA soa; // For SOA record - rdataOpt opt; // For eDNS0 opt record - } RDataBody; - -typedef struct - { - mDNSu16 MaxRDLength; // Amount of storage allocated for rdata (usually sizeof(RDataBody)) - RDataBody u; - } RData; -#define sizeofRDataHeader (sizeof(RData) - sizeof(RDataBody)) - -typedef struct AuthRecord_struct AuthRecord; -typedef struct CacheRecord_struct CacheRecord; -typedef struct ResourceRecord_struct ResourceRecord; -typedef struct DNSQuestion_struct DNSQuestion; -typedef struct mDNS_struct mDNS; -typedef struct mDNS_PlatformSupport_struct mDNS_PlatformSupport; - -// Note: Within an mDNSRecordCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Close(), mDNS_Execute() -typedef void mDNSRecordCallback(mDNS *const m, AuthRecord *const rr, mStatus result); - -// Note: -// Restrictions: An mDNSRecordUpdateCallback may not make any mDNS API calls. -// The intent of this callback is to allow the client to free memory, if necessary. -// The internal data structures of the mDNS code may not be in a state where mDNS API calls may be made safely. -typedef void mDNSRecordUpdateCallback(mDNS *const m, AuthRecord *const rr, RData *OldRData); - -struct ResourceRecord_struct - { - mDNSu8 RecordType; // See enum above - mDNSInterfaceID InterfaceID; // Set if this RR is specific to one interface - // For records received off the wire, InterfaceID is *always* set to the receiving interface - // For our authoritative records, InterfaceID is usually zero, except for those few records - // that are interface-specific (e.g. address records, especially linklocal addresses) - domainname name; - mDNSu16 rrtype; - mDNSu16 rrclass; - mDNSu32 rroriginalttl; // In seconds - mDNSu16 rdlength; // Size of the raw rdata, in bytes - mDNSu16 rdestimate; // Upper bound on size of rdata after name compression - mDNSu32 namehash; // Name-based (i.e. case insensitive) hash of name - mDNSu32 rdatahash; // 32-bit hash of the raw rdata - mDNSu32 rdnamehash; // Set if this rdata contains a domain name (e.g. PTR, SRV, CNAME etc.) - RData *rdata; // Pointer to storage for this rdata - }; - -enum - { - regState_FetchingZoneData = 1, // getting info - update not sent - regState_Pending = 2, // update sent, reply not received - regState_Registered = 3, // update sent, reply received - regState_DeregPending = 4, // dereg sent, reply not received - regState_DeregDeferred = 5, // dereg requested while in Pending state - send dereg AFTER registration is confirmed - regState_Cancelled = 6, // update not sent, reg. cancelled by client - regState_TargetChange = 7, // host name change update pending - regState_Unregistered = 8, // not in any list - regState_Refresh = 9 // outstanding refresh message - }; - -typedef mDNSu16 regState_t; - -typedef struct - { - regState_t state; - mDNSOpaque16 id; - domainname zone; // the zone that is updated - mDNSAddr ns; // primary name server for the record's zone !!!KRS not technically correct to cache longer than TTL - mDNSIPPort port; // port on which server accepts dynamic updates - mDNSBool add; // !!!KRS this should really be an enumerated state - struct uDNS_AuthInfo *AuthInfo; // authentication info (may be null) - mDNSBool lease; // dynamic update contains (should contain) lease option - mDNSs32 expire; // expiration of lease (-1 for static) - } uDNS_RegInfo; - -struct AuthRecord_struct - { - // For examples of how to set up this structure for use in mDNS_Register(), - // see mDNS_AdvertiseInterface() or mDNS_RegisterService(). - // Basically, resrec and persistent metadata need to be set up before calling mDNS_Register(). - // mDNS_SetupResourceRecord() is avaliable as a helper routine to set up most fields to sensible default values for you - - AuthRecord *next; // Next in list; first element of structure for efficiency reasons - ResourceRecord resrec; - uDNS_RegInfo uDNS_info; - - // Persistent metadata for Authoritative Records - AuthRecord *Additional1; // Recommended additional record to include in response - AuthRecord *Additional2; // Another additional - AuthRecord *DependentOn; // This record depends on another for its uniqueness checking - AuthRecord *RRSet; // This unique record is part of an RRSet - mDNSRecordCallback *RecordCallback; // Callback function to call for state changes - void *RecordContext; // Context parameter for the callback function - mDNSu8 HostTarget; // Set if the target of this record (PTR, CNAME, SRV, etc.) is our host name - - // Transient state for Authoritative Records - mDNSu8 Acknowledged; // Set if we've given the success callback to the client - mDNSu8 ProbeCount; // Number of probes remaining before this record is valid (kDNSRecordTypeUnique) - mDNSu8 AnnounceCount; // Number of announcements remaining (kDNSRecordTypeShared) - mDNSu8 IncludeInProbe; // Set if this RR is being put into a probe right now - mDNSInterfaceID ImmedAnswer; // Someone on this interface issued a query we need to answer (all-ones for all interfaces) -#if MDNS_LOG_ANSWER_SUPPRESSION_TIMES - mDNSs32 ImmedAnswerMarkTime; -#endif - mDNSInterfaceID ImmedAdditional; // Hint that we might want to also send this record, just to be helpful - mDNSInterfaceID SendRNow; // The interface this query is being sent on right now - mDNSv4Addr v4Requester; // Recent v4 query for this record, or all-ones if more than one recent query - mDNSv6Addr v6Requester; // Recent v6 query for this record, or all-ones if more than one recent query - AuthRecord *NextResponse; // Link to the next element in the chain of responses to generate - const mDNSu8 *NR_AnswerTo; // Set if this record was selected by virtue of being a direct answer to a question - AuthRecord *NR_AdditionalTo; // Set if this record was selected by virtue of being additional to another - mDNSs32 ThisAPInterval; // In platform time units: Current interval for announce/probe - mDNSs32 AnnounceUntil; // In platform time units: Creation time + TTL - mDNSs32 LastAPTime; // In platform time units: Last time we sent announcement/probe - mDNSs32 LastMCTime; // Last time we multicast this record (used to guard against packet-storm attacks) - mDNSInterfaceID LastMCInterface; // Interface this record was multicast on at the time LastMCTime was recorded - RData *NewRData; // Set if we are updating this record with new rdata - mDNSu16 newrdlength; // ... and the length of the new RData - mDNSRecordUpdateCallback *UpdateCallback; - mDNSu32 UpdateCredits; // Token-bucket rate limiting of excessive updates - mDNSs32 NextUpdateCredit; // Time next token is added to bucket - mDNSs32 UpdateBlocked; // Set if update delaying is in effect - - RData rdatastorage; // Normally the storage is right here, except for oversized records - // rdatastorage MUST be the last thing in the structure -- when using oversized AuthRecords, extra bytes - // are appended after the end of the AuthRecord, logically augmenting the size of the rdatastorage - // DO NOT ADD ANY MORE FIELDS HERE - }; - -struct CacheRecord_struct - { - CacheRecord *next; // Next in list; first element of structure for efficiency reasons - ResourceRecord resrec; - - // Transient state for Cache Records - CacheRecord *NextInKAList; // Link to the next element in the chain of known answers to send - mDNSs32 TimeRcvd; // In platform time units - mDNSs32 NextRequiredQuery; // In platform time units - mDNSs32 LastUsed; // In platform time units - mDNSu32 UseCount; // Number of times this RR has been used to answer a question - DNSQuestion *CRActiveQuestion; // Points to an active question referencing this answer - mDNSu32 UnansweredQueries; // Number of times we've issued a query for this record without getting an answer - mDNSs32 LastUnansweredTime; // In platform time units; last time we incremented UnansweredQueries - mDNSu32 MPUnansweredQ; // Multi-packet query handling: Number of times we've seen a query for this record - mDNSs32 MPLastUnansweredQT; // Multi-packet query handling: Last time we incremented MPUnansweredQ - mDNSu32 MPUnansweredKA; // Multi-packet query handling: Number of times we've seen this record in a KA list - mDNSBool MPExpectingKA; // Multi-packet query handling: Set when we increment MPUnansweredQ; allows one KA - CacheRecord *NextInCFList; // Set if this is in the list of records we just received with the cache flush bit set - - struct { mDNSu16 MaxRDLength; mDNSu8 data[InlineCacheRDSize]; } rdatastorage; // Storage for small records is right here - }; - -typedef struct - { - CacheRecord r; - mDNSu8 _extradata[MaximumRDSize-InlineCacheRDSize]; // Glue on the necessary number of extra bytes - } LargeCacheRecord; - -typedef struct NetworkInterfaceInfo_struct NetworkInterfaceInfo; - -typedef struct - { - AuthRecord RR_A; - mDNSBool registered; // True if a name for the interface is globally registered - domainname regname; // the name registered to the update server - } uDNS_NetworkInterfaceInfo; - -// A NetworkInterfaceInfo_struct serves two purposes: -// 1. It holds the address, PTR and HINFO records to advertise a given IP address on a given physical interface -// 2. It tells mDNSCore which physical interfaces are available; each physical interface has its own unique InterfaceID. -// Since there may be multiple IP addresses on a single physical interface, -// there may be multiple NetworkInterfaceInfo_structs with the same InterfaceID. -// In this case, to avoid sending the same packet n times, when there's more than one -// struct with the same InterfaceID, mDNSCore picks one member of the set to be the -// active representative of the set; all others have the 'InterfaceActive' flag unset. - -struct NetworkInterfaceInfo_struct - { - // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. - NetworkInterfaceInfo *next; - - mDNSBool InterfaceActive; // Set if interface is sending & receiving packets (see comment above) - mDNSBool IPv4Available; // If InterfaceActive, set if v4 available on this InterfaceID - mDNSBool IPv6Available; // If InterfaceActive, set if v6 available on this InterfaceID - - // Standard AuthRecords that every Responder host should have (one per active IP address) - AuthRecord RR_A; // 'A' or 'AAAA' (address) record for our ".local" name - AuthRecord RR_PTR; // PTR (reverse lookup) record - AuthRecord RR_HINFO; - - uDNS_NetworkInterfaceInfo uDNS_info; - - // Client API fields: The client must set up these fields *before* calling mDNS_RegisterInterface() - mDNSInterfaceID InterfaceID; // Identifies physical interface; MUST NOT be 0, -1, or -2 - mDNSAddr ip; // The IPv4 or IPv6 address to advertise - mDNSBool Advertise; // False if you are only searching on this interface - mDNSBool McastTxRx; // Send/Receive multicast on this { InterfaceID, address family } ? - }; - -typedef struct ExtraResourceRecord_struct ExtraResourceRecord; -struct ExtraResourceRecord_struct - { - ExtraResourceRecord *next; - AuthRecord r; - // Note: Add any additional fields *before* the AuthRecord in this structure, not at the end. - // In some cases clients can allocate larger chunks of memory and set r->rdata->MaxRDLength to indicate - // that this extra memory is available, which would result in any fields after the AuthRecord getting smashed - }; - -// Note: Within an mDNSServiceCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Close(), mDNS_Execute() -typedef struct ServiceRecordSet_struct ServiceRecordSet; -typedef void mDNSServiceCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result); -struct ServiceRecordSet_struct - { - // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. - // No fields need to be set up by the client prior to calling mDNS_RegisterService(); - // all required data is passed as parameters to that function. - ServiceRecordSet *next; - uDNS_RegInfo uDNS_info; - mDNSServiceCallback *ServiceCallback; - void *ServiceContext; - ExtraResourceRecord *Extras; // Optional list of extra AuthRecords attached to this service registration - mDNSu32 NumSubTypes; - AuthRecord *SubTypes; - mDNSBool Conflict; // Set if this record set was forcibly deregistered because of a conflict - domainname Host; // Set if this service record does not use the standard target host name - AuthRecord RR_ADV; // e.g. _services._dns-sd._udp.local. PTR _printer._tcp.local. - AuthRecord RR_PTR; // e.g. _printer._tcp.local. PTR Name._printer._tcp.local. - AuthRecord RR_SRV; // e.g. Name._printer._tcp.local. SRV 0 0 port target - AuthRecord RR_TXT; // e.g. Name._printer._tcp.local. TXT PrintQueueName - // Don't add any fields after AuthRecord RR_TXT. - // This is where the implicit extra space goes if we allocate a ServiceRecordSet containing an oversized RR_TXT record - }; - -// *************************************************************************** -#if 0 -#pragma mark - Question structures -#endif - -// We record the last eight instances of each duplicate query -// This gives us v4/v6 on each of Ethernet/AirPort and Firewire, and two free slots "for future expansion" -// If the host has more active interfaces that this it is not fatal -- duplicate question suppression will degrade gracefully. -// Since we will still remember the last eight, the busiest interfaces will still get the effective duplicate question suppression. -#define DupSuppressInfoSize 8 - -typedef struct - { - mDNSs32 Time; - mDNSInterfaceID InterfaceID; - mDNSs32 Type; // v4 or v6? - } DupSuppressInfo; - -typedef enum - { - LLQ_UnInit = 0, - LLQ_GetZoneInfo = 1, - LLQ_InitialRequest = 2, - LLQ_SecondaryRequest = 3, - LLQ_Established = 4, - LLQ_Refresh = 5, - LLQ_Retry = 6, - LLQ_Suspended = 7, - // safe to re-start LLQ before this point - LLQ_Static = 16, - LLQ_Poll = 17, - LLQ_Error = 18, - LLQ_Cancelled = 19 - } LLQ_State; - -typedef struct - { - LLQ_State state; - mDNSAddr servAddr; - mDNSIPPort servPort; - DNSQuestion *question; - mDNSu32 origLease; // seconds (relative) - mDNSs32 retry; // ticks (absolute) - mDNSs32 expire; // ticks (absolute) - mDNSs16 ntries; - mDNSu8 id[8]; - mDNSBool deriveRemovesOnResume; - } LLQ_Info; - -// LLQ constants -#define kDNSOpt_LLQ 1 //!!!KRS -#define kDNSOpt_Lease 2 //!!!KRS -#define kLLQ_Vers 0 // prerelease -#define kLLQ_DefLease 7200 // 2 hours -#define kUpdate_DefLease 7200 -#define kLLQ_MAX_TRIES 3 // retry an operation 3 times max -#define kLLQ_INIT_RESEND 2 // resend an un-ack'd packet after 2 seconds, then double for each additional -#define kLLQ_DEF_RETRY 1800 // retry a failed operation after 30 minutes -// LLQ Operation Codes -#define kLLQ_Setup 1 -#define kLLQ_Refresh 2 - -#define LLQ_OPT_SIZE (2 * sizeof(mDNSu16)) + sizeof(LLQOptData) -#define LEASE_OPT_SIZE (2 * sizeof(mDNSu16)) + sizeof(mDNSs32) - -// LLQ Errror Codes -enum - { - LLQErr_NoError = 0, - LLQErr_ServFull = 1, - LLQErr_Static = 2, - LLQErr_FormErr = 3, - LLQErr_NoSuchLLQ = 4, - LLQErr_BadVers = 5, - LLQErr_UnknownErr = 6 - }; - -typedef void (*InternalResponseHndlr)(mDNS *const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *internalContext); -typedef struct - { - mDNSOpaque16 id; - mDNSs32 timestamp; - mDNSBool internal; - InternalResponseHndlr responseCallback; // NULL if internal field is false - LLQ_Info *llq; // NULL for 1-shot queries - CacheRecord *knownAnswers; - void *context; - } uDNS_QuestionInfo; - -// Note: Within an mDNSQuestionCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Close(), mDNS_Execute() -typedef void mDNSQuestionCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord); -struct DNSQuestion_struct - { - // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. - DNSQuestion *next; - mDNSu32 qnamehash; - mDNSs32 LastQTime; // Last scheduled transmission of this Q on *all* applicable interfaces - mDNSs32 ThisQInterval; // LastQTime + ThisQInterval is the next scheduled transmission of this Q - // ThisQInterval > 0 for an active question; - // ThisQInterval = 0 for a suspended question that's still in the list - // ThisQInterval = -1 for a cancelled question that's been removed from the list - mDNSu32 RecentAnswers; // Number of answers since the last time we sent this query - mDNSu32 CurrentAnswers; // Number of records currently in the cache that answer this question - mDNSu32 LargeAnswers; // Number of answers with rdata > 1024 bytes - mDNSu32 UniqueAnswers; // Number of answers received with kDNSClass_UniqueRRSet bit set - DNSQuestion *DuplicateOf; - DNSQuestion *NextInDQList; - DupSuppressInfo DupSuppress[DupSuppressInfoSize]; - mDNSInterfaceID SendQNow; // The interface this query is being sent on right now - mDNSBool SendOnAll; // Set if we're sending this question on all active interfaces - mDNSs32 LastQTxTime; // Last time this Q was sent on one (but not necessarily all) interfaces - uDNS_QuestionInfo uDNS_info; - - // Client API fields: The client must set up these fields *before* calling mDNS_StartQuery() - mDNSInterfaceID InterfaceID; // Non-zero if you want to issue queries only on a single specific IP interface - mDNSAddr Target; // Non-zero if you want to direct queries to a specific unicast target address - mDNSIPPort TargetPort; // Must be set if Target is set - mDNSOpaque16 TargetQID; // Must be set if Target is set - domainname qname; - mDNSu16 qtype; - mDNSu16 qclass; - mDNSQuestionCallback *QuestionCallback; - void *QuestionContext; - mDNSBool LongLived; // Set by client for calls to mDNS_StartQuery to indicate LLQs to unicast layer. - // Set by mDNS.c in mDNS_StartBrowse. - }; - -typedef struct - { - // Client API fields: The client must set up name and InterfaceID *before* calling mDNS_StartResolveService() - // When the callback is invoked, ip, port, TXTlen and TXTinfo will have been filled in with the results learned from the network. - domainname name; - mDNSInterfaceID InterfaceID; // ID of the interface the response was received on - mDNSAddr ip; // Remote (destination) IP address where this service can be accessed - mDNSIPPort port; // Port where this service can be accessed - mDNSu16 TXTlen; - mDNSu8 TXTinfo[2048]; // Additional demultiplexing information (e.g. LPR queue name) - } ServiceInfo; - -// Note: Within an mDNSServiceInfoQueryCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Close(), mDNS_Execute() -typedef struct ServiceInfoQuery_struct ServiceInfoQuery; -typedef void mDNSServiceInfoQueryCallback(mDNS *const m, ServiceInfoQuery *query); -struct ServiceInfoQuery_struct - { - // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. - // No fields need to be set up by the client prior to calling mDNS_StartResolveService(); - // all required data is passed as parameters to that function. - // The ServiceInfoQuery structure memory is working storage for mDNSCore to discover the requested information - // and place it in the ServiceInfo structure. After the client has called mDNS_StopResolveService(), it may - // dispose of the ServiceInfoQuery structure while retaining the results in the ServiceInfo structure. - DNSQuestion qSRV; - DNSQuestion qTXT; - DNSQuestion qAv4; - DNSQuestion qAv6; - mDNSu8 GotSRV; - mDNSu8 GotTXT; - mDNSu8 GotADD; - mDNSu32 Answers; - ServiceInfo *info; - mDNSServiceInfoQueryCallback *ServiceInfoQueryCallback; - void *ServiceInfoQueryContext; - }; - -// *************************************************************************** -#if 0 -#pragma mark - Main mDNS object, used to hold all the mDNS state -#endif - -typedef void mDNSCallback(mDNS *const m, mStatus result); - -#define CACHE_HASH_SLOTS 499 - -enum - { - mDNS_KnownBug_PhantomInterfaces = 1 - }; - -typedef struct - { - mDNSs32 nextevent; - DNSQuestion *ActiveQueries; //!!!KRS this should be a hashtable (hash on messageID) - DNSQuestion *CurrentQuery; // pointer to ActiveQueries list being examined in a loop. Functions that remove - // elements from the ActiveQueries list must update this pointer (if non-NULL) as necessary. - //!!!KRS do the same for registration lists - ServiceRecordSet *ServiceRegistrations; - AuthRecord *RecordRegistrations; - mDNSu16 NextMessageID; - mDNSAddr Servers[32]; //!!!KRS this should be a dynamically allocated linked list - domainname hostname; // global name for dynamic registration of address records - char NameRegDomain[MAX_ESCAPED_DOMAIN_NAME]; - // domain in which above hostname is registered - // currently set by the platform layer at startup - // do not set if services / address records are not to be globally registered to an update server - // !!!KRS this must go away once we can learn the reg domain from the network or prefs - char ServiceRegDomain[MAX_ESCAPED_DOMAIN_NAME]; - // if set, all services that don't explicitly specify a domain upon registration will be - // registered in this domain. if not set, .local will be used by default - struct uDNS_AuthInfo *AuthInfoList; // list of domains required authentication for updates. !!!KRS this shoudl be a hashtable - } uDNS_GlobalInfo; - -struct mDNS_struct - { - // Internal state fields. These hold the main internal state of mDNSCore; - // the client layer needn't be concerned with them. - // No fields need to be set up by the client prior to calling mDNS_Init(); - // all required data is passed as parameters to that function. - - mDNS_PlatformSupport *p; // Pointer to platform-specific data of indeterminite size - mDNSu32 KnownBugs; - mDNSBool CanReceiveUnicast; - mDNSBool AdvertiseLocalAddresses; - mStatus mDNSPlatformStatus; - mDNSCallback *MainCallback; - void *MainContext; - - // For debugging: To catch and report locking failures - mDNSu32 mDNS_busy; // Incremented between mDNS_Lock/mDNS_Unlock section - mDNSu32 mDNS_reentrancy; // Incremented when calling a client callback - mDNSu8 mDNS_shutdown; // Set when we're shutting down, allows us to skip some unnecessary steps - mDNSu8 lock_rrcache; // For debugging: Set at times when these lists may not be modified - mDNSu8 lock_Questions; - mDNSu8 lock_Records; - char MsgBuffer[80]; // Temp storage used while building error log messages - - // Task Scheduling variables - mDNSs32 timenow; // The time that this particular activation of the mDNS code started - mDNSs32 timenow_last; // The time the last time we ran - mDNSs32 timenow_adjust; // Correction applied if we ever discover time went backwards - mDNSs32 NextScheduledEvent; // Derived from values below - mDNSs32 SuppressSending; // Don't send *any* packets during this time - mDNSs32 NextCacheCheck; // Next time to refresh cache record before it expires - mDNSs32 NextScheduledQuery; // Next time to send query in its exponential backoff sequence - mDNSs32 NextScheduledProbe; // Next time to probe for new authoritative record - mDNSs32 NextScheduledResponse; // Next time to send authoritative record(s) in responses - mDNSs32 ExpectUnicastResponse; // Set when we send a query with the kDNSQClass_UnicastResponse bit set - mDNSs32 RandomQueryDelay; // For de-synchronization of query packets on the wire - mDNSBool SendDeregistrations; // Set if we need to send deregistrations (immediately) - mDNSBool SendImmediateAnswers; // Set if we need to send answers (immediately -- or as soon as SuppressSending clears) - mDNSBool SleepState; // Set if we're sleeping (send no more packets) - - // These fields only required for mDNS Searcher... - DNSQuestion *Questions; // List of all registered questions, active and inactive - DNSQuestion *NewQuestions; // Fresh questions not yet answered from cache - DNSQuestion *CurrentQuestion; // Next question about to be examined in AnswerLocalQuestions() - DNSQuestion *LocalOnlyQuestions; // Questions with InterfaceID set to mDNSInterface_LocalOnly - DNSQuestion *NewLocalOnlyQuestions; // Fresh local-only questions not yet answered - mDNSu32 rrcache_size; // Total number of available cache entries - mDNSu32 rrcache_totalused; // Number of cache entries currently occupied - mDNSu32 rrcache_active; // Number of cache entries currently occupied by records that answer active questions - mDNSu32 rrcache_report; - CacheRecord *rrcache_free; - CacheRecord *rrcache_hash[CACHE_HASH_SLOTS]; - CacheRecord **rrcache_tail[CACHE_HASH_SLOTS]; - mDNSu32 rrcache_used[CACHE_HASH_SLOTS]; - - // Fields below only required for mDNS Responder... - domainlabel nicelabel; // Rich text label encoded using canonically precomposed UTF-8 - domainlabel hostlabel; // Conforms to RFC 1034 "letter-digit-hyphen" ARPANET host name rules - domainname hostname; // Host Name, e.g. "Foo.local." - UTF8str255 HIHardware; - UTF8str255 HISoftware; - AuthRecord *ResourceRecords; - AuthRecord *DuplicateRecords; // Records currently 'on hold' because they are duplicates of existing records - AuthRecord *LocalOnlyRecords; // Local records registered with InterfaceID set to mDNSInterface_LocalOnly - AuthRecord *NewLocalOnlyRecords; // Fresh local-only records not yet delivered to local-only questions - mDNSBool DiscardLocalOnlyRecords;// Set when we have "remove" events we need to deliver to local-only questions - AuthRecord *CurrentRecord; // Next AuthRecord about to be examined - NetworkInterfaceInfo *HostInterfaces; - mDNSs32 ProbeFailTime; - mDNSu32 NumFailedProbes; - mDNSs32 SuppressProbes; - - // unicast-specific data - uDNS_GlobalInfo uDNS_info; - }; - -// *************************************************************************** -#if 0 -#pragma mark - Useful Static Constants -#endif - -extern const mDNSIPPort zeroIPPort; -extern const mDNSv4Addr zeroIPAddr; -extern const mDNSv6Addr zerov6Addr; -extern const mDNSv4Addr onesIPv4Addr; -extern const mDNSv6Addr onesIPv6Addr; -extern const mDNSAddr zeroAddr; - -extern const mDNSInterfaceID mDNSInterface_Any; // Zero -extern const mDNSInterfaceID mDNSInterface_LocalOnly; // (mDNSInterfaceID)-1; -extern const mDNSInterfaceID mDNSInterface_ForceMCast; // (mDNSInterfaceID)-2; - -extern const mDNSIPPort UnicastDNSPort; -extern const mDNSIPPort MulticastDNSPort; -extern const mDNSv4Addr AllDNSAdminGroup; -extern const mDNSv4Addr AllDNSLinkGroup; -extern const mDNSv6Addr AllDNSLinkGroupv6; -extern const mDNSAddr AllDNSLinkGroup_v4; -extern const mDNSAddr AllDNSLinkGroup_v6; - -extern const mDNSOpaque16 zeroID; -extern const mDNSOpaque16 QueryFlags; -extern const mDNSOpaque16 ResponseFlags; -extern const mDNSOpaque16 UpdateReqFlags; -extern const mDNSOpaque16 UpdateRespFlags; - -// *************************************************************************** -#if 0 -#pragma mark - Inline functions -#endif - -#if (defined(_MSC_VER)) - #define mDNSinline static __inline -#elif ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 9))) - #define mDNSinline static inline -#endif - -// If we're not doing inline functions, then this header needs to have the extern declarations -#if !defined(mDNSinline) -extern mDNSu16 mDNSVal16(mDNSOpaque16 x); -extern mDNSu32 mDNSVal32(mDNSOpaque32 x); -extern mDNSOpaque16 mDNSOpaque16fromIntVal(mDNSu16 v); -extern mDNSOpaque32 mDNSOpaque32fromIntVal(mDNSu32 v); -#endif - -// If we're compiling the particular C file that instantiates our inlines, then we -// define "mDNSinline" (to empty string) so that we generate code in the following section -#if (!defined(mDNSinline) && mDNS_InstantiateInlines) -#define mDNSinline -#endif - -#ifdef mDNSinline - -mDNSinline mDNSu16 mDNSVal16(mDNSOpaque16 x) { return((mDNSu16)((mDNSu16)x.b[0] << 8 | (mDNSu16)x.b[1])); } -mDNSinline mDNSu32 mDNSVal32(mDNSOpaque32 x) { return((mDNSu32)((mDNSu32)x.b[0] << 24 | (mDNSu32)x.b[1] << 16 | (mDNSu32)x.b[2] << 8 | (mDNSu32)x.b[3])); } - -mDNSinline mDNSOpaque16 mDNSOpaque16fromIntVal(mDNSu16 v) - { - mDNSOpaque16 x; - x.b[0] = (mDNSu8)(v >> 8); - x.b[1] = (mDNSu8)(v & 0xFF); - return(x); - } - -mDNSinline mDNSOpaque32 mDNSOpaque32fromIntVal(mDNSu32 v) - { - mDNSOpaque32 x; - x.b[0] = (mDNSu8) (v >> 24) ; - x.b[1] = (mDNSu8)((v >> 16) & 0xFF); - x.b[2] = (mDNSu8)((v >> 8 ) & 0xFF); - x.b[3] = (mDNSu8)((v ) & 0xFF); - return x; - } - -#endif - -// *************************************************************************** -#if 0 -#pragma mark - Main Client Functions -#endif - -// Every client should call mDNS_Init, passing in storage for the mDNS object and the mDNS_PlatformSupport object. -// -// Clients that are only advertising services should use mDNS_Init_NoCache and mDNS_Init_ZeroCacheSize. -// Clients that plan to perform queries (mDNS_StartQuery, mDNS_StartBrowse, mDNS_StartResolveService, etc.) -// need to provide storage for the resource record cache, or the query calls will return 'mStatus_NoCache'. -// The rrcachestorage parameter is the address of memory for the resource record cache, and -// the rrcachesize parameter is the number of entries in the CacheRecord array passed in. -// (i.e. the size of the cache memory needs to be sizeof(CacheRecord) * rrcachesize). -// OS X 10.3 Panther uses an initial cache size of 64 entries, and then mDNSCore sends an -// mStatus_GrowCache message if it needs more. -// -// Most clients should use mDNS_Init_AdvertiseLocalAddresses. This causes mDNSCore to automatically -// create the correct address records for all the hosts interfaces. If you plan to advertise -// services being offered by the local machine, this is almost always what you want. -// There are two cases where you might use mDNS_Init_DontAdvertiseLocalAddresses: -// 1. A client-only device, that browses for services but doesn't advertise any of its own. -// 2. A proxy-registration service, that advertises services being offered by other machines, and takes -// the appropriate steps to manually create the correct address records for those other machines. -// In principle, a proxy-like registration service could manually create address records for its own machine too, -// but this would be pointless extra effort when using mDNS_Init_AdvertiseLocalAddresses does that for you. -// -// When mDNS has finished setting up the client's callback is called -// A client can also spin and poll the mDNSPlatformStatus field to see when it changes from mStatus_Waiting to mStatus_NoError -// -// Call mDNS_Close to tidy up before exiting -// -// Call mDNS_Register with a completed AuthRecord object to register a resource record -// If the resource record type is kDNSRecordTypeUnique (or kDNSknownunique) then if a conflicting resource record is discovered, -// the resource record's mDNSRecordCallback will be called with error code mStatus_NameConflict. The callback should deregister -// the record, and may then try registering the record again after picking a new name (e.g. by automatically appending a number). -// -// Call mDNS_StartQuery to initiate a query. mDNS will proceed to issue Multicast DNS query packets, and any time a response -// is received containing a record which matches the question, the DNSQuestion's mDNSAnswerCallback function will be called -// Call mDNS_StopQuery when no more answers are required -// -// Care should be taken on multi-threaded or interrupt-driven environments. -// The main mDNS routines call mDNSPlatformLock() on entry and mDNSPlatformUnlock() on exit; -// each platform layer needs to implement these appropriately for its respective platform. -// For example, if the support code on a particular platform implements timer callbacks at interrupt time, then -// mDNSPlatformLock/Unlock need to disable interrupts or do similar concurrency control to ensure that the mDNS -// code is not entered by an interrupt-time timer callback while in the middle of processing a client call. - -extern mStatus mDNS_Init (mDNS *const m, mDNS_PlatformSupport *const p, - CacheRecord *rrcachestorage, mDNSu32 rrcachesize, - mDNSBool AdvertiseLocalAddresses, - mDNSCallback *Callback, void *Context); -// See notes above on use of NoCache/ZeroCacheSize -#define mDNS_Init_NoCache mDNSNULL -#define mDNS_Init_ZeroCacheSize 0 -// See notes above on use of Advertise/DontAdvertiseLocalAddresses -#define mDNS_Init_AdvertiseLocalAddresses mDNStrue -#define mDNS_Init_DontAdvertiseLocalAddresses mDNSfalse -#define mDNS_Init_NoInitCallback mDNSNULL -#define mDNS_Init_NoInitCallbackContext mDNSNULL - -extern void mDNS_GrowCache (mDNS *const m, CacheRecord *storage, mDNSu32 numrecords); -extern void mDNS_Close (mDNS *const m); -extern mDNSs32 mDNS_Execute (mDNS *const m); - -extern mStatus mDNS_Register (mDNS *const m, AuthRecord *const rr); -extern mStatus mDNS_Update (mDNS *const m, AuthRecord *const rr, mDNSu32 newttl, - const mDNSu16 newrdlength, - RData *const newrdata, mDNSRecordUpdateCallback *Callback); -extern mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr); - -extern mStatus mDNS_StartQuery(mDNS *const m, DNSQuestion *const question); -extern mStatus mDNS_StopQuery (mDNS *const m, DNSQuestion *const question); -extern mStatus mDNS_Reconfirm (mDNS *const m, CacheRecord *const cacherr); -extern mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr); - -// *************************************************************************** -#if 0 -#pragma mark - Platform support functions that are accessible to the client layer too -#endif - -extern mDNSs32 mDNSPlatformOneSecond; -extern mDNSs32 mDNSPlatformTimeNow(void); - -// *************************************************************************** -#if 0 -#pragma mark - General utility and helper functions -#endif - -// mDNS_RegisterService is a single call to register the set of resource records associated with a given named service. -// -// mDNS_StartResolveService is single call which is equivalent to multiple calls to mDNS_StartQuery, -// to find the IP address, port number, and demultiplexing information for a given named service. -// As with mDNS_StartQuery, it executes asynchronously, and calls the ServiceInfoQueryCallback when the answer is -// found. After the service is resolved, the client should call mDNS_StopResolveService to complete the transaction. -// The client can also call mDNS_StopResolveService at any time to abort the transaction. -// -// mDNS_GetBrowseDomains is a special case of the mDNS_StartQuery call, where the resulting answers -// are a list of PTR records indicating (in the rdata) domains that are recommended for browsing. -// After getting the list of domains to browse, call mDNS_StopQuery to end the search. -// mDNS_GetDefaultBrowseDomain returns the name of the domain that should be highlighted by default. -// -// mDNS_GetRegistrationDomains and mDNS_GetDefaultRegistrationDomain are the equivalent calls to get the list -// of one or more domains that should be offered to the user as choices for where they may register their service, -// and the default domain in which to register in the case where the user has made no selection. - -extern void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID, - mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, mDNSRecordCallback Callback, void *Context); - -extern mStatus mDNS_RegisterService (mDNS *const m, ServiceRecordSet *sr, - const domainlabel *const name, const domainname *const type, const domainname *const domain, - const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen, - AuthRecord *SubTypes, mDNSu32 NumSubTypes, - const mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context); -extern mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl); -extern mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra); -extern mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordSet *const sr, const domainlabel *newname); -extern mStatus mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr); - -extern mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const rr, - const domainlabel *const name, const domainname *const type, const domainname *const domain, - const domainname *const host, - const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context); -#define mDNS_DeregisterNoSuchService mDNS_Deregister - -extern mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question, - const domainname *const srv, const domainname *const domain, - const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context); -#define mDNS_StopBrowse mDNS_StopQuery - -extern mStatus mDNS_StartResolveService(mDNS *const m, ServiceInfoQuery *query, ServiceInfo *info, mDNSServiceInfoQueryCallback *Callback, void *Context); -extern void mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *query); - -typedef enum - { - mDNS_DomainTypeBrowse = 0, - mDNS_DomainTypeBrowseDefault = 1, - mDNS_DomainTypeRegistration = 2, - mDNS_DomainTypeRegistrationDefault = 3 - } mDNS_DomainType; - -extern mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const domainname *dom, - const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context); -#define mDNS_StopGetDomains mDNS_StopQuery -extern mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname); -#define mDNS_StopAdvertiseDomains mDNS_Deregister - -// *************************************************************************** -#if 0 -#pragma mark - DNS name utility functions -#endif - -// In order to expose the full capabilities of the DNS protocol (which allows any arbitrary eight-bit values -// in domain name labels, including unlikely characters like ascii nulls and even dots) all the mDNS APIs -// work with DNS's native length-prefixed strings. For convenience in C, the following utility functions -// are provided for converting between C's null-terminated strings and DNS's length-prefixed strings. - -// Assignment -// A simple C structure assignment of a domainname can cause a protection fault by accessing unmapped memory, -// because that object is defined to be 256 bytes long, but not all domainname objects are truly the full size. -// This macro uses mDNSPlatformMemCopy() to make sure it only touches the actual bytes that are valid. -#define AssignDomainName(DST, SRC) mDNSPlatformMemCopy((SRC).c, (DST).c, DomainNameLength(&(SRC))) - -// Comparison functions -extern mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b); -extern mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2); -extern mDNSBool IsLocalDomain(const domainname *d); // returns true for domains that by default should be looked up using link-local multicast - -// Get total length of domain name, in native DNS format, including terminal root label -// (e.g. length of "com." is 5 (length byte, three data bytes, final zero) -extern mDNSu16 DomainNameLength(const domainname *const name); - -// Append functions to append one or more labels to an existing native format domain name: -// AppendLiteralLabelString adds a single label from a literal C string, with no escape character interpretation. -// AppendDNSNameString adds zero or more labels from a C string using conventional DNS dots-and-escaping interpretation -// AppendDomainLabel adds a single label from a native format domainlabel -// AppendDomainName adds zero or more labels from a native format domainname -extern mDNSu8 *AppendLiteralLabelString(domainname *const name, const char *cstr); -extern mDNSu8 *AppendDNSNameString (domainname *const name, const char *cstr); -extern mDNSu8 *AppendDomainLabel (domainname *const name, const domainlabel *const label); -extern mDNSu8 *AppendDomainName (domainname *const name, const domainname *const append); - -// Convert from null-terminated string to native DNS format: -// The DomainLabel form makes a single label from a literal C string, with no escape character interpretation. -// The DomainName form makes native format domain name from a C string using conventional DNS interpretation: -// dots separate labels, and within each label, '\.' represents a literal dot, '\\' represents a literal -// backslash and backslash with three decimal digits (e.g. \000) represents an arbitrary byte value. -extern mDNSBool MakeDomainLabelFromLiteralString(domainlabel *const label, const char *cstr); -extern mDNSu8 *MakeDomainNameFromDNSNameString (domainname *const name, const char *cstr); - -// Convert native format domainlabel or domainname back to C string format -// IMPORTANT: -// When using ConvertDomainLabelToCString, the target buffer must be MAX_ESCAPED_DOMAIN_LABEL (254) bytes long -// to guarantee there will be no buffer overrun. It is only safe to use a buffer shorter than this in rare cases -// where the label is known to be constrained somehow (for example, if the label is known to be either "_tcp" or "_udp"). -// Similarly, when using ConvertDomainNameToCString, the target buffer must be MAX_ESCAPED_DOMAIN_NAME (1005) bytes long. -// See definitions of MAX_ESCAPED_DOMAIN_LABEL and MAX_ESCAPED_DOMAIN_NAME for more detailed explanation. -extern char *ConvertDomainLabelToCString_withescape(const domainlabel *const name, char *cstr, char esc); -#define ConvertDomainLabelToCString_unescaped(D,C) ConvertDomainLabelToCString_withescape((D), (C), 0) -#define ConvertDomainLabelToCString(D,C) ConvertDomainLabelToCString_withescape((D), (C), '\\') -extern char *ConvertDomainNameToCString_withescape(const domainname *const name, char *cstr, char esc); -#define ConvertDomainNameToCString_unescaped(D,C) ConvertDomainNameToCString_withescape((D), (C), 0) -#define ConvertDomainNameToCString(D,C) ConvertDomainNameToCString_withescape((D), (C), '\\') - -extern void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], domainlabel *const hostlabel); - -extern mDNSu8 *ConstructServiceName(domainname *const fqdn, const domainlabel *name, const domainname *type, const domainname *const domain); -extern mDNSBool DeconstructServiceName(const domainname *const fqdn, domainlabel *const name, domainname *const type, domainname *const domain); - -// Note: Some old functions have been replaced by more sensibly-named versions. -// You can uncomment the hash-defines below if you don't want to have to change your source code right away. -// When updating your code, note that (unlike the old versions) *all* the new routines take the target object -// as their first parameter. -//#define ConvertCStringToDomainName(SRC,DST) MakeDomainNameFromDNSNameString((DST),(SRC)) -//#define ConvertCStringToDomainLabel(SRC,DST) MakeDomainLabelFromLiteralString((DST),(SRC)) -//#define AppendStringLabelToName(DST,SRC) AppendLiteralLabelString((DST),(SRC)) -//#define AppendStringNameToName(DST,SRC) AppendDNSNameString((DST),(SRC)) -//#define AppendDomainLabelToName(DST,SRC) AppendDomainLabel((DST),(SRC)) -//#define AppendDomainNameToName(DST,SRC) AppendDomainName((DST),(SRC)) - -// *************************************************************************** -#if 0 -#pragma mark - Other utility functions -#endif - -extern mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg); -extern mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...) IS_A_PRINTF_STYLE_FUNCTION(3,4); -extern mDNSu32 NumCacheRecordsForInterfaceID(const mDNS *const m, mDNSInterfaceID id); -extern char *DNSTypeName(mDNSu16 rrtype); -extern char *GetRRDisplayString_rdb(mDNS *const m, const ResourceRecord *rr, RDataBody *rd); -#define GetRRDisplayString(m, rr) GetRRDisplayString_rdb((m), &(rr)->resrec, &(rr)->resrec.rdata->u) -extern mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2); -extern void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText); - -// *************************************************************************** -#if 0 -#pragma mark - Authentication Support -#endif - -#define HMAC_LEN 64 -#define HMAC_IPAD 0x36 -#define HMAC_OPAD 0x5c -#define MD5_LEN 16 - -// padded keys for inned/outer hash rounds -typedef struct - { - mDNSu8 ipad[HMAC_LEN]; - mDNSu8 opad[HMAC_LEN]; - } HMAC_Key; - -// Internal data structure to maintain authentication information for an update domain -typedef struct uDNS_AuthInfo - { - domainname zone; - domainname keyname; - HMAC_Key key; - struct uDNS_AuthInfo *next; - } uDNS_AuthInfo; - -// Platform Support for computing MD5 -// mDNSPlatformUTC returns the time, in seconds, since Jan 1st 1970 UTC and is required for generating TSIG records - -extern mDNSs32 mDNSPlatformUTC(void); - -// Client Calls -// mDNS_UpdateDomainRequiresAuthentication tells the core to authenticate (via TSIG with an HMAC_MD5 hash) -// when dynamically updating a given zone (and its subdomains). The key used in authentication must be in -// domain name format. The shared secret must be a base64 encoded string with the base64 parameter set to -// true, or binary data with the base64 parameter set to false. The length is the size of the secret in -// bytes. (A minimum size of 16 bytes (128 bits) is recommended for an MD5 hash as per RFC 2485). -// The This routine is normally called once for each secure domain at startup, though it can be called at any time. - -// mDNS_ClearAuthenticationList clears from the core's internal structures all domains previously passed to -// mDNS_UpdateDomainRequiresAuthentication. - -extern mStatus mDNS_UpdateDomainRequiresAuthentication(mDNS *m, domainname *zone, domainname *key, - mDNSu8 *sharedSecret, mDNSu32 ssLen, mDNSBool base64); - -extern void mDNS_ClearAuthenticationList(mDNS *m); - -// Routines called by the core, exported by DNSDigest.c - -// Convert a base64 encoded key into a binary byte stream -extern mDNSs32 DNSDigest_Base64ToBin(const char *src, mDNSu8 *target, mDNSu32 targsize); - -// Convert an arbitrary binary key (of any length) into an HMAC key (stored in AuthInfo struct) -extern void DNSDigest_ConstructHMACKey(uDNS_AuthInfo *info, mDNSu8 *key, mDNSu32 len); - -// sign a DNS message. The message must be compete, with all values in network byte order. end points to the end -// of the message, and is modified by this routine. numAdditionals is a pointer to the number of additional -// records in HOST byte order, which is incremented upon successful completion of this routine. The function returns -// the new end pointer on success, and NULL on failure. -extern mDNSu8 *DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, mDNSu16 *numAdditionals, uDNS_AuthInfo *info); - -// MD5 hash function used by the core for signing TSIG records (impemented in DNSDigest.c) -extern mStatus DNSDigest_MD5(const DNSMessage *msg, mDNSu32 msglen, mDNSOpaque16 *digest); - -// *************************************************************************** -#if 0 -#pragma mark - PlatformSupport interface -#endif - -// This section defines the interface to the Platform Support layer. -// Normal client code should not use any of types defined here, or directly call any of the functions defined here. -// The definitions are placed here because sometimes clients do use these calls indirectly, via other supported client operations. -// For example, AssignDomainName is a macro defined using mDNSPlatformMemCopy() - -// Every platform support module must provide the following functions. -// mDNSPlatformInit() typically opens a communication endpoint, and starts listening for mDNS packets. -// When Setup is complete, the platform support layer calls mDNSCoreInitComplete(). -// mDNSPlatformSendUDP() sends one UDP packet -// When a packet is received, the PlatformSupport code calls mDNSCoreReceive() -// mDNSPlatformClose() tidies up on exit -// Note: mDNSPlatformMemAllocate/mDNSPlatformMemFree are only required for handling oversized resource records and unicast DNS. -// If your target platform has a well-defined specialized application, and you know that all the records it uses -// are InlineCacheRDSize or less, then you can just make a simple mDNSPlatformMemAllocate() stub that always returns -// NULL. InlineCacheRDSize is a compile-time constant, which is set by default to 64. If you need to handle records -// a little larger than this and you don't want to have to implement run-time allocation and freeing, then you -// can raise the value of this constant to a suitable value (at the expense of increased memory usage). -extern mStatus mDNSPlatformInit (mDNS *const m); -extern void mDNSPlatformClose (mDNS *const m); -extern mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, -mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport); - -extern void mDNSPlatformLock (const mDNS *const m); -extern void mDNSPlatformUnlock (const mDNS *const m); - -extern void mDNSPlatformStrCopy (const void *src, void *dst); -extern mDNSu32 mDNSPlatformStrLen (const void *src); -extern void mDNSPlatformMemCopy (const void *src, void *dst, mDNSu32 len); -extern mDNSBool mDNSPlatformMemSame (const void *src, const void *dst, mDNSu32 len); -extern void mDNSPlatformMemZero ( void *dst, mDNSu32 len); -extern void * mDNSPlatformMemAllocate (mDNSu32 len); -extern void mDNSPlatformMemFree (void *mem); -extern mStatus mDNSPlatformTimeInit (mDNSs32 *timenow); - -// Platform support modules should provide the following functions to map between opaque interface IDs -// and interface indexes in order to support the DNS-SD API. If your target platform does not support -// multiple interfaces and/or does not support the DNS-SD API, these functions can be empty. -extern mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(const mDNS *const m, mDNSu32 index); -extern mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(const mDNS *const m, mDNSInterfaceID id); - -// Every platform support module must provide the following functions if it is to support unicast DNS -// and Dynamic Update. -// All TCP socket operations implemented by the platform layer MUST NOT BLOCK. -// mDNSPlatformTCPConnect initiates a TCP connection with a peer, adding the socket descriptor to the -// main event loop. The return value indicates whether the connection succeeded, failed, or is pending -// (i.e. the call would block.) On return, the descriptor parameter is set to point to the connected socket. -// The TCPConnectionCallback is subsequently invoked when the connection -// completes (in which case the ConnectionEstablished parameter is true), or data is available for -// reading on the socket (indicated by the ConnectionEstablished parameter being false.) If the connection -// asyncronously fails, the TCPConnectionCallback should be invoked as usual, with the error being -// returned in subsequent calls to PlatformReadTCP or PlatformWriteTCP. (This allows for platforms -// with limited asyncronous error detection capabilities.) PlatformReadTCP and PlatformWriteTCP must -// return the number of bytes read/written, 0 if the call would block, and -1 if an error. -// PlatformTCPCloseConnection must close the connection to the peer and remove the descriptor from the -// event loop. CloseConnectin may be called at any time, including in a ConnectionCallback. - -typedef void (*TCPConnectionCallback)(int sd, void *context, mDNSBool ConnectionEstablished); -extern mStatus mDNSPlatformTCPConnect(const mDNSAddr *dst, mDNSOpaque16 dstport, mDNSInterfaceID InterfaceID, - TCPConnectionCallback callback, void *context, int *descriptor); -extern void mDNSPlatformTCPCloseConnection(int sd); -extern int mDNSPlatformReadTCP(int sd, void *buf, int buflen); -extern int mDNSPlatformWriteTCP(int sd, const char *msg, int len); - -// Platforms that support unicast browsing and dynamic update registration for clients who do not specify a domain -// in browse/registration calls must implement these routines to get the "default" browse/registration list. -// The Get() functions must return a linked list of DNameListElem structs, allocated via mDNSPlatformMemAllocate. -// Platforms may implement the Get() calls via the mDNS_CopyDNameList() helper routine. -// Callers should free lists obtained via the Get() calls with th mDNS_FreeDNameList routine, provided by the core. - -typedef struct DNameListElem - { - domainname name; - struct DNameListElem *next; - } DNameListElem; - -extern DNameListElem *mDNSPlatformGetSearchDomainList(void); -extern DNameListElem *mDNSPlatformGetRegDomainList(void); - -// Helper functions provided by the core -extern DNameListElem *mDNS_CopyDNameList(const DNameListElem *orig); -extern void mDNS_FreeDNameList(DNameListElem *list); - -// The core mDNS code provides these functions, for the platform support code to call at appropriate times -// -// mDNS_GenerateFQDN() is called once on startup (typically from mDNSPlatformInit()) -// and then again on each subsequent change of the dot-local host name. -// -// !!!KRS -// mDNS_GenerateGlobalFQDN() is called to register a domain name via Dynamic DNS update. It should be -// called on startup (after acquiring an IP address and DNS server) and on each change to the machine name -// or registration domain. The domain parameter is the domain in which the address record is to be registered, -// e.g. "mycompany.com". The full name is formed by appending this domain to the machine's host label. -// -// mDNS_RegisterInterface() is used by the platform support layer to inform mDNSCore of what -// physical and/or logical interfaces are available for sending and receiving packets. -// Typically it is called on startup for each available interface, but register/deregister may be -// called again later, on multiple occasions, to inform the core of interface configuration changes. -// If set->Advertise is set non-zero, then mDNS_RegisterInterface() also registers the standard -// resource records that should be associated with every publicised IP address/interface: -// -- Name-to-address records (A/AAAA) -// -- Address-to-name records (PTR) -// -- Host information (HINFO) -// IMPORTANT: The specified mDNSInterfaceID MUST NOT be 0, -1, or -2; these values have special meaning -// -// mDNS_SetDynamicRegistrationDomain is used to enable dynamic update registrations of address records -// in the specified domain. -// -// mDNS_RegisterDNS() is used by the platform support layer to provide the core with the addresses of -// available domain name servers for unicast queries/updates. RegisterDNS() should be called once for -// each name server, typically at startup, or when a new name server becomes available. DeregiterDNS() -// must be called whenever a registered name server becomes unavailable. DeregisterDNSList deregisters -// all registered servers. mDNS_DNSRegistered() returns true if one or more servers are registered in the core. -// -// mDNSCoreInitComplete() is called when the platform support layer is finished. -// Typically this is at the end of mDNSPlatformInit(), but may be later -// (on platforms like OT that allow asynchronous initialization of the networking stack). -// -// mDNSCoreReceive() is called when a UDP packet is received -// -// mDNSCoreMachineSleep() is called when the machine sleeps or wakes -// (This refers to heavyweight laptop-style sleep/wake that disables network access, -// not lightweight second-by-second CPU power management modes.) - -extern void mDNS_GenerateFQDN(mDNS *const m); -extern void mDNS_GenerateGlobalFQDN(mDNS *const m); -extern mStatus mDNS_RegisterInterface (mDNS *const m, NetworkInterfaceInfo *set); -extern void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set); -extern void mDNS_RegisterDNS(mDNS *const m, mDNSv4Addr *const dnsAddr); -extern void mDNS_DeregisterDNS(mDNS *const m, mDNSv4Addr *const dnsAddr); -extern void mDNS_DeregisterDNSList(mDNS *const m); -extern mDNSBool mDNS_DNSRegistered(mDNS *const m); -extern void mDNSCoreInitComplete(mDNS *const m, mStatus result); -extern void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, - const mDNSAddr *const srcaddr, const mDNSIPPort srcport, - const mDNSAddr *const dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, mDNSu8 ttl); -extern void mDNSCoreMachineSleep(mDNS *const m, mDNSBool wake); - -extern mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip); - -// *************************************************************************** -#if 0 -#pragma mark - Compile-Time assertion checks -#endif - -// Some C compiler cleverness. We can make the compiler check certain things for -// us, and report compile-time errors if anything is wrong. The usual way to do -// this would be to use a run-time "if" statement, but then you don't find out -// what's wrong until you run the software. This way, if the assertion condition -// is false, the array size is negative, and the complier complains immediately. - -struct mDNS_CompileTimeAssertionChecks - { - // Check that the compiler generated our on-the-wire packet format structure definitions - // properly packed, without adding padding bytes to align fields on 32-bit or 64-bit boundaries. - char assert0[(sizeof(rdataSRV) == 262 ) ? 1 : -1]; - char assert1[(sizeof(DNSMessageHeader) == 12 ) ? 1 : -1]; - char assert2[(sizeof(DNSMessage) == 12+AbsoluteMaxDNSMessageData) ? 1 : -1]; - char assert3[(sizeof(mDNSs8) == 1 ) ? 1 : -1]; - char assert4[(sizeof(mDNSu8) == 1 ) ? 1 : -1]; - char assert5[(sizeof(mDNSs16) == 2 ) ? 1 : -1]; - char assert6[(sizeof(mDNSu16) == 2 ) ? 1 : -1]; - char assert7[(sizeof(mDNSs32) == 4 ) ? 1 : -1]; - char assert8[(sizeof(mDNSu32) == 4 ) ? 1 : -1]; - char assert9[(sizeof(mDNSOpaque16) == 2 ) ? 1 : -1]; - char assertA[(sizeof(mDNSOpaque32) == 4 ) ? 1 : -1]; - char assertB[(sizeof(mDNSOpaque128) == 16 ) ? 1 : -1]; - }; - -// *************************************************************************** - -#ifdef __cplusplus - } -#endif - -#endif diff --git a/mDNSCore/mDNSDebug.h b/mDNSCore/mDNSDebug.h index bcb0851..8f95d42 100755 --- a/mDNSCore/mDNSDebug.h +++ b/mDNSCore/mDNSDebug.h @@ -3,8 +3,6 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -25,6 +23,9 @@ Change History (most recent first): $Log: mDNSDebug.h,v $ +Revision 1.24 2004/09/16 01:58:21 cheshire +Fix compiler warnings + Revision 1.23 2004/05/18 23:51:25 cheshire Tidy up all checkin comments to use consistent "" format for bug numbers @@ -88,6 +89,7 @@ Merge in license terms from Quinn's copy, in preparation for Darwin release // (If you edit the file here to turn on MDNS_DEBUGMSGS while you're debugging some code, be careful // not to accidentally check-in that change by mistake when you check in your other changes.) +//#undef MDNS_DEBUGMSGS //#define MDNS_DEBUGMSGS 2 // Set MDNS_CHECK_PRINTF_STYLE_FUNCTIONS to 1 to enable extra GCC compiler warnings @@ -136,8 +138,8 @@ extern void verbosedebugf_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1 // LogMsg is used even in shipping code, to write truly serious error messages to syslog (or equivalent) extern int mDNS_DebugMode; // If non-zero, LogMsg() writes to stderr instead of syslog extern void LogMsg(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); -extern void LogMsgIdent(const char *ident, const char *format, ...); -extern void LogMsgNoIdent(const char *format, ...); +extern void LogMsgIdent(const char *ident, const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(2,3); +extern void LogMsgNoIdent(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); // Set this symbol to 1 to do extra debug checks on malloc() and free() // Set this symbol to 2 to write a log message for every malloc() and free() diff --git a/mDNSCore/mDNSEmbeddedAPI.h b/mDNSCore/mDNSEmbeddedAPI.h new file mode 100755 index 0000000..ac0215d --- /dev/null +++ b/mDNSCore/mDNSEmbeddedAPI.h @@ -0,0 +1,2602 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + + NOTE: + If you're building an application that uses DNS Service Discovery + this is probably NOT the header file you're looking for. + In most cases you will want to use /usr/include/dns_sd.h instead. + + This header file defines the lowest level raw interface to mDNSCore, + which is appropriate *only* on tiny embedded systems where everything + runs in a single address space and memory is extremely constrained. + All the APIs here are malloc-free, which means that the caller is + responsible for passing in a pointer to the relevant storage that + will be used in the execution of that call, and (when called with + correct parameters) all the calls are guaranteed to succeed. There + is never a case where a call can suffer intermittent failures because + the implementation calls malloc() and sometimes malloc() returns NULL + because memory is so limited that no more is available. + This is primarily for devices that need to have precisely known fixed + memory requirements, with absolutely no uncertainty or run-time variation, + but that certainty comes at a cost of more difficult programming. + + For applications running on general-purpose desktop operating systems + (Mac OS, Linux, Solaris, Windows, etc.) the API you should use is + /usr/include/dns_sd.h, which defines the API by which multiple + independent client processes communicate their DNS Service Discovery + requests to a single "mdnsd" daemon running in the background. + + Even on platforms that don't run multiple independent processes in + multiple independent address spaces, you can still use the preferred + dns_sd.h APIs by linking in "dnssd_clientshim.c", which implements + the standard "dns_sd.h" API calls, allocates any required storage + using malloc(), and then calls through to the low-level malloc-free + mDNSCore routines defined here. This has the benefit that even though + you're running on a small embedded system with a single address space, + you can still use the exact same client C code as you'd use on a + general-purpose desktop system. + + + Change History (most recent first): + +$Log: mDNSEmbeddedAPI.h,v $ +Revision 1.260 2004/12/12 23:51:42 ksekar + Wide-area registrations should fallback to using DHCP hostname as target + +Revision 1.259 2004/12/11 20:55:29 ksekar + Clean up registration state machines + +Revision 1.258 2004/12/10 20:48:32 cheshire + Need to pick final EDNS numbers for LLQ and GC + +Revision 1.257 2004/12/10 02:09:23 cheshire + Modify default TTLs + +Revision 1.256 2004/12/09 03:15:40 ksekar + use _legacy instead of _default to find "empty string" browse domains + +Revision 1.255 2004/12/07 22:48:37 cheshire +Tidying + +Revision 1.254 2004/12/07 21:26:04 ksekar + DNSServiceRegisterRecord() can crash on deregistration + +Revision 1.253 2004/12/07 20:42:33 cheshire +Add explicit context parameter to mDNS_RemoveRecordFromService() + +Revision 1.252 2004/12/07 03:02:12 ksekar +Fixed comments, grouped unicast-specific routines together + +Revision 1.251 2004/12/06 21:15:22 ksekar + mDNSResponder crashed in CheckServiceRegistrations + +Revision 1.250 2004/12/04 02:12:45 cheshire + mDNSResponder puts LargeCacheRecord on the stack + +Revision 1.249 2004/12/03 05:18:33 ksekar + mDNSResponder needs to return more specific TSIG errors + +Revision 1.248 2004/12/02 20:03:48 ksekar + Rendezvous still publishes wide-area domains even after switching to a local subnet + +Revision 1.247 2004/12/01 20:57:19 ksekar + Wide Area Rendezvous must be split-DNS aware + +Revision 1.246 2004/11/29 23:26:32 cheshire +Added NonZeroTime() function, which usually returns the value given, with the exception +that if the value given is zero, it returns one instead. For timer values where zero is +used to mean "not set", this can be used to ensure that setting them to the result of an +interval computation (e.g. "now+interval") does not inadvertently result in a zero value. + +Revision 1.245 2004/11/25 01:28:09 cheshire + Need to implement random delay for 'QU' unicast replies (and set cache flush bit too) + +Revision 1.244 2004/11/24 22:00:59 cheshire +Move definition of mDNSAddressIsAllDNSLinkGroup() from mDNSMacOSX.c to mDNSEmbeddedAPI.h + +Revision 1.243 2004/11/23 22:43:53 cheshire +Tidy up code alignment + +Revision 1.242 2004/11/23 03:39:46 cheshire +Let interface name/index mapping capability live directly in JNISupport.c, +instead of having to call through to the daemon via IPC to get this information. + +Revision 1.241 2004/11/22 17:16:19 ksekar + Unicast services don't disappear when you disable all networking + +Revision 1.240 2004/11/19 02:32:43 ksekar +Wide-Area Rendezvous Security: Add LLQ-ID to events + +Revision 1.239 2004/11/15 20:09:23 ksekar + Wide Area support for Add/Remove record + +Revision 1.238 2004/11/12 03:16:48 rpantos +rdar://problem/3809541 Add mDNSPlatformGetInterfaceByName, mDNSPlatformGetInterfaceName + +Revision 1.237 2004/11/10 20:40:53 ksekar + LLQ mobility fragile on non-primary interface + +Revision 1.236 2004/11/01 20:36:11 ksekar + mDNSResponder should not receive Keychain Notifications + +Revision 1.235 2004/11/01 17:48:14 cheshire +Changed SOA serial number back to signed. RFC 1035 may describe it as "unsigned", but +it's wrong. The SOA serial is a modular counter, as explained in "DNS & BIND", page +137. Since C doesn't have a modular type, we used signed, C's closest approximation. + +Revision 1.234 2004/10/29 21:59:02 ksekar +SOA serial should be a unsigned integer, as per RFC 1035 + +Revision 1.233 2004/10/28 03:24:41 cheshire +Rename m->CanReceiveUnicastOn as m->CanReceiveUnicastOn5353 + +Revision 1.232 2004/10/26 06:20:23 cheshire +Add mDNSAddressIsValidNonZero() macro + +Revision 1.231 2004/10/26 06:11:41 cheshire +Add improved logging to aid in diagnosis of mDNSResponder crashed + +Revision 1.230 2004/10/26 03:52:02 cheshire +Update checkin comments + +Revision 1.229 2004/10/25 19:30:52 ksekar + Simplify dynamic host name structures + +Revision 1.228 2004/10/23 01:16:00 cheshire + uDNS operations not always reliable on multi-homed hosts + +Revision 1.227 2004/10/22 20:52:07 ksekar + Create NAT port mappings for Long Lived Queries + +Revision 1.226 2004/10/20 01:50:40 cheshire + Cannot resolve non-local registrations using the mach API +Implemented ForceMCast mode for AuthRecords as well as for Questions + +Revision 1.225 2004/10/19 21:33:17 cheshire + Cannot resolve non-local registrations using the mach API +Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name +doesn't force multicast unless you set this flag to indicate explicitly that this is what you want + +Revision 1.224 2004/10/16 00:16:59 cheshire + Replace IP TTL 255 check with local subnet source address check + +Revision 1.223 2004/10/15 23:00:17 ksekar + Need to update LLQs on location changes + +Revision 1.222 2004/10/12 02:49:20 ksekar + Clean up LLQ sleep/wake, error handling + +Revision 1.221 2004/10/10 06:57:15 cheshire +Change definition of "localdomain" to make code compile a little smaller + +Revision 1.220 2004/10/06 01:44:19 cheshire + Resolving too quickly sometimes returns stale TXT record + +Revision 1.219 2004/10/03 23:18:58 cheshire +Move address comparison macros from DNSCommon.h to mDNSEmbeddedAPI.h + +Revision 1.218 2004/10/03 23:14:12 cheshire +Add "mDNSEthAddr" type and "zeroEthAddr" constant + +Revision 1.217 2004/09/30 00:24:56 ksekar + Dynamically update default registration domains on config change + +Revision 1.216 2004/09/27 23:24:32 cheshire +Fix typo: SOA refresh interval is supposed to be unsigned + +Revision 1.215 2004/09/26 23:20:35 ksekar + Allow default registrations in multiple wide-area domains + +Revision 1.214 2004/09/25 02:41:39 cheshire + Deliver near-pending "remove" events before new "add" events + +Revision 1.213 2004/09/25 02:24:27 cheshire +Removed unused rr->UseCount + +Revision 1.212 2004/09/24 20:57:39 cheshire + Eliminate inappropriate casts that cause misaligned-address errors + +Revision 1.211 2004/09/24 20:33:22 cheshire +Remove unused DNSDigest_MD5 declaration + +Revision 1.210 2004/09/23 20:21:07 cheshire + Refine "immediate answer burst; restarting exponential backoff sequence" logic +Associate a unique sequence number with each received packet, and only increment the count of recent answer +packets if the packet sequence number for this answer record is not one we've already seen and counted. + +Revision 1.209 2004/09/23 20:14:39 cheshire +Rename "question->RecentAnswers" to "question->RecentAnswerPkts" + +Revision 1.208 2004/09/23 00:50:53 cheshire + Don't send a (DE) if a service is unregistered after wake from sleep + +Revision 1.207 2004/09/22 02:34:46 cheshire +Move definitions of default TTL times from mDNS.c to mDNSEmbeddedAPI.h + +Revision 1.206 2004/09/22 00:41:59 cheshire +Move tcp connection status codes into the legal range allocated for mDNS use + +Revision 1.205 2004/09/21 23:40:11 ksekar + mDNSResponder to return errors on NAT traversal failure + +Revision 1.204 2004/09/21 23:29:50 cheshire + DNSServiceResolve should delay sending packets + +Revision 1.203 2004/09/21 20:58:22 cheshire +Add ifname field to NetworkInterfaceInfo_struct + +Revision 1.202 2004/09/17 00:46:34 cheshire +mDNS_TimeNow should take const mDNS parameter + +Revision 1.201 2004/09/17 00:31:51 cheshire +For consistency with ipv6, renamed rdata field 'ip' to 'ipv4' + +Revision 1.200 2004/09/17 00:19:10 cheshire +For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4 + +Revision 1.199 2004/09/16 21:59:16 cheshire +For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr + +Revision 1.198 2004/09/16 21:36:36 cheshire + Fix unsafe use of mDNSPlatformTimeNow() +Changes to add necessary locking calls around unicast DNS operations + +Revision 1.197 2004/09/16 00:24:48 cheshire + Fix unsafe use of mDNSPlatformTimeNow() + +Revision 1.196 2004/09/14 23:42:35 cheshire + Need to seed random number generator from platform-layer data + +Revision 1.195 2004/09/14 23:27:46 cheshire +Fix compile errors + +Revision 1.194 2004/09/10 00:49:57 cheshire + Add error code kDNSServiceErr_Firewall, for future use + +Revision 1.193 2004/09/03 19:23:05 ksekar +: Need retransmission mechanism for wide-area service registrations + +Revision 1.192 2004/09/02 03:48:47 cheshire + Disable targeted unicast query support by default +1. New flag kDNSServiceFlagsAllowRemoteQuery to indicate we want to allow remote queries for this record +2. New field AllowRemoteQuery in AuthRecord structure +3. uds_daemon.c sets AllowRemoteQuery if kDNSServiceFlagsAllowRemoteQuery is set +4. mDNS.c only answers remote queries if AllowRemoteQuery is set + +Revision 1.191 2004/08/25 00:37:27 ksekar +: Cleanup DynDNS hostname registration code + +Revision 1.190 2004/08/18 17:35:41 ksekar +: Feature #9586: Need support for Legacy NAT gateways + +Revision 1.189 2004/08/14 03:22:41 cheshire + Dynamic DNS UI <-> mDNSResponder glue +Add GetUserSpecifiedDDNSName() routine +Convert ServiceRegDomain to domainname instead of C string +Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs + +Revision 1.188 2004/08/13 23:46:58 cheshire +"asyncronous" -> "asynchronous" + +Revision 1.187 2004/08/13 23:37:02 cheshire +Now that we do both uDNS and mDNS, global replace "uDNS_info.hostname" with +"uDNS_info.UnicastHostname" for clarity + +Revision 1.186 2004/08/13 23:25:00 cheshire +Now that we do both uDNS and mDNS, global replace "m->hostname" with +"m->MulticastHostname" for clarity + +Revision 1.185 2004/08/12 00:32:36 ksekar +: LLQ Refreshes never terminate if unanswered + +Revision 1.184 2004/08/11 17:09:31 cheshire +Add comment clarifying the applicability of these APIs + +Revision 1.183 2004/08/10 23:19:14 ksekar +: DNS Extension daemon for Wide Area Rendezvous +Moved routines/constants to allow extern access for garbage collection daemon + +Revision 1.182 2004/07/30 17:40:06 ksekar +: TXT Record updates not available for wide-area services + +Revision 1.181 2004/07/29 19:27:15 ksekar +NATPMP Support - minor fixes and cleanup + +Revision 1.180 2004/07/29 02:03:35 ksekar +Delete unused #define and structure field + +Revision 1.179 2004/07/26 22:49:30 ksekar +: Feature #9516: Need support for NATPMP in client + +Revision 1.178 2004/07/13 21:24:24 rpantos +Fix for . + +Revision 1.177 2004/06/05 00:04:26 cheshire +: wide-area domains should be returned in reg. domain enumeration + +Revision 1.176 2004/06/04 08:58:29 ksekar +: Keychain integration for secure dynamic update + +Revision 1.175 2004/06/04 00:15:06 cheshire +Move misplaced brackets + +Revision 1.174 2004/06/03 23:30:16 cheshire +Remove extraneous blank lines and white space + +Revision 1.173 2004/06/03 03:09:58 ksekar +: Garbage Collection for Dynamic Updates + +Revision 1.172 2004/06/01 23:46:50 ksekar +: DynDNS: dynamically look up LLQ/Update ports + +Revision 1.171 2004/05/28 23:42:37 ksekar +: Feature: DNS server->client notification on record changes (#7805) + +Revision 1.170 2004/05/18 23:51:25 cheshire +Tidy up all checkin comments to use consistent "" format for bug numbers + +Revision 1.169 2004/05/13 04:54:20 ksekar +Unified list copy/free code. Added symetric list for + +Revision 1.168 2004/05/12 22:03:09 ksekar +Made GetSearchDomainList a true platform-layer call (declaration moved +from mDNSMacOSX.h to mDNSEmbeddedAPI.h), implemented to return "local" +only on non-OSX platforms. Changed call to return a copy of the list +to avoid shared memory issues. Added a routine to free the list. + +Revision 1.167 2004/04/22 04:07:01 cheshire +Fix from Bob Bradley: Don't try to do inline functions on compilers that don't support it + +Revision 1.166 2004/04/22 03:15:56 cheshire +Fix use of "struct __attribute__((__packed__))" so it only applies on GCC >= 2.9 + +Revision 1.165 2004/04/22 03:05:28 cheshire +kDNSClass_ANY should be kDNSQClass_ANY + +Revision 1.164 2004/04/21 02:55:03 cheshire +Update comments describing 'InterfaceActive' field + +Revision 1.163 2004/04/21 02:49:11 cheshire +To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx' + +Revision 1.162 2004/04/15 00:51:28 bradley +Minor tweaks for Windows and C++ builds. Added casts for signed/unsigned integers and 64-bit pointers. +Prefix some functions with mDNS to avoid conflicts. Disable benign warnings on Microsoft compilers. + +Revision 1.161 2004/04/14 23:09:28 ksekar +Support for TSIG signed dynamic updates. + +Revision 1.160 2004/04/09 17:40:26 cheshire +Remove unnecessary "Multicast" field -- it duplicates the semantics of the existing McastTxRx field + +Revision 1.159 2004/04/09 16:37:15 cheshire +Suggestion from Bob Bradley: +Move NumCacheRecordsForInterfaceID() to DNSCommon.c so it's available to all platform layers + +Revision 1.158 2004/04/02 19:38:33 cheshire +Update comment about typical RR TTLs + +Revision 1.157 2004/04/02 19:35:53 cheshire +Add clarifying comments about legal mDNSInterfaceID values + +Revision 1.156 2004/04/02 19:19:48 cheshire +Add code to do optional logging of multi-packet KA list time intervals + +Revision 1.155 2004/03/24 00:29:45 ksekar +Make it safe to call StopQuery in a unicast question callback + +Revision 1.154 2004/03/20 01:05:49 cheshire +Test __LP64__ and __ILP64__ to compile properly on a wider range of 64-bit architectures + +Revision 1.153 2004/03/13 01:57:33 ksekar +: DynDNS: Dynamic update of service records + +Revision 1.152 2004/03/09 02:27:16 cheshire +Remove erroneous underscore in 'packed_struct' (makes no difference now, but might in future) + +Revision 1.151 2004/03/02 03:21:56 cheshire + Properly support "_services._dns-sd._udp" meta-queries + +Revision 1.150 2004/02/21 02:06:24 cheshire +Can't use anonymous unions -- they're non-standard and don't work on all compilers + +Revision 1.149 2004/02/06 23:04:19 ksekar +Basic Dynamic Update support via mDNS_Register (dissabled via +UNICAST_REGISTRATION #define) + +Revision 1.148 2004/02/03 19:47:36 ksekar +Added an asynchronous state machine mechanism to uDNS.c, including +calls to find the parent zone for a domain name. Changes include code +in repository previously dissabled via "#if 0 incomplete". Codepath +is currently unused, and will be called to create update records, etc. + +Revision 1.147 2004/02/03 18:57:35 cheshire +Update comment for "IsLocalDomain()" + +Revision 1.146 2004/01/30 02:20:24 bradley +Map inline to __inline when building with Microsoft C compilers since they do not support C99 inline. + +Revision 1.145 2004/01/29 02:59:17 ksekar +Unicast DNS: Changed from a resource record oriented question/response +matching to packet based matching. New callback architecture allows +collections of records in a response to be processed differently +depending on the nature of the request, and allows the same structure +to be used for internal and client-driven queries with different processing needs. + +Revision 1.144 2004/01/28 20:20:45 ksekar +Unified ActiveQueries and ActiveInternalQueries lists, using a flag to +demux them. Check-in includes work-in-progress code, #ifdef'd out. + +Revision 1.143 2004/01/28 03:41:00 cheshire +: Need ability to do targeted queries as well as multicast queries + +Revision 1.142 2004/01/28 02:30:07 ksekar +Added default Search Domains to unicast browsing, controlled via +Networking sharing prefs pane. Stopped sending unicast messages on +every interface. Fixed unicast resolving via mach-port API. + +Revision 1.141 2004/01/27 20:15:22 cheshire +: Time to prune obsolete code for listening on port 53 + +Revision 1.140 2004/01/24 23:37:08 cheshire +At Kiren's suggestion, made functions to convert mDNSOpaque16s to/from integer values + +Revision 1.139 2004/01/24 08:46:26 bradley +Added InterfaceID<->Index platform interfaces since they are now used by all platforms for the DNS-SD APIs. + +Revision 1.138 2004/01/24 04:59:15 cheshire +Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again + +Revision 1.137 2004/01/24 03:40:56 cheshire +Move mDNSAddrIsDNSMulticast() from DNSCommon.h to mDNSEmbeddedAPI.h so clients can use it + +Revision 1.136 2004/01/24 03:38:27 cheshire +Fix minor syntactic error: Headers should use "extern" declarations, not "mDNSexport" + +Revision 1.135 2004/01/23 23:23:15 ksekar +Added TCP support for truncated unicast messages. + +Revision 1.134 2004/01/22 03:54:11 cheshire +Create special meta-interface 'mDNSInterface_ForceMCast' (-2), +which means "do this query via multicast, even if it's apparently a unicast domain" + +Revision 1.133 2004/01/22 03:48:41 cheshire +Make sure uDNS client doesn't accidentally use query ID zero + +Revision 1.132 2004/01/22 03:43:08 cheshire +Export constants like mDNSInterface_LocalOnly so that the client layers can use them + +Revision 1.131 2004/01/21 21:53:18 cheshire +: Don't try to receive unicast responses if we're not the first to bind to the UDP port + +Revision 1.130 2003/12/14 05:05:29 cheshire +Add comments explaining mDNS_Init_NoCache and mDNS_Init_ZeroCacheSize + +Revision 1.129 2003/12/13 03:05:27 ksekar +: DynDNS: Unicast query of service records + +Revision 1.128 2003/12/01 21:44:23 cheshire +Add mStatus_BadInterfaceErr = -65552 for consistency with dns_sd.h + +Revision 1.127 2003/12/01 18:26:37 cheshire +Also pack the OpaqueXX union types. Otherwise, on some systems, mDNSOpaque16 is four bytes! + +Revision 1.126 2003/12/01 18:23:48 cheshire +: Scalar size problem in mDNS code on some 64-bit architectures + +Revision 1.125 2003/11/22 00:18:27 cheshire +Add compile-time asserts to verify correct sizes of mDNSu32, mDNSOpaque16, etc. + +Revision 1.124 2003/11/20 22:59:54 cheshire +Changed runtime checks in mDNS.c to be compile-time checks in mDNSEmbeddedAPI.h +Thanks to Bob Bradley for suggesting the ingenious compiler trick to make this work. + +Revision 1.123 2003/11/20 22:53:01 cheshire +Add comment about MAX_ESCAPED_DOMAIN_LABEL + +Revision 1.122 2003/11/20 20:49:53 cheshire +Another fix from HP: Use packedstruct macro to ensure proper packing for on-the-wire packet structures + +Revision 1.121 2003/11/20 05:01:38 cheshire +Update comments; add explanation of Advertise/DontAdvertiseLocalAddresses + +Revision 1.120 2003/11/14 20:59:08 cheshire +Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h. +Best solution is just to combine mDNSEmbeddedAPI.h and mDNSPlatformFunctions.h into a single file. + +Revision 1.119 2003/11/14 19:47:52 cheshire +Define symbol MAX_ESCAPED_DOMAIN_NAME to indicate recommended buffer size for ConvertDomainNameToCString + +Revision 1.118 2003/11/14 19:18:34 cheshire +Move AssignDomainName macro to mDNSEmbeddedAPI.h to that client layers can use it too + +Revision 1.117 2003/11/08 23:32:24 cheshire +Gave name to anonymous struct, to avoid errors on certain compilers. +(Thanks to ramaprasad.kr@hp.com for reporting this.) + +Revision 1.116 2003/11/07 03:32:56 cheshire + mDNSResponder delivers answers in inconsistent order +This is the real fix. Checkin 1.312 was overly simplistic; Calling GetFreeCacheRR() can sometimes +purge records from the cache, causing tail pointer *rp to be stale on return. The correct fix is +to maintain a system-wide tail pointer for each cache slot, and then if neccesary GetFreeCacheRR() +can update this pointer, so that mDNSCoreReceiveResponse() appends records in the right place. + +Revision 1.115 2003/09/23 00:53:54 cheshire +NumFailedProbes should be unsigned + +Revision 1.114 2003/08/29 19:44:15 cheshire + Traffic reduction: Eliminate synchronized QUs when a new service appears +1. Use m->RandomQueryDelay to impose a random delay in the range 0-500ms on queries + that already have at least one unique answer in the cache +2. For these queries, go straight to QM, skipping QU + +Revision 1.113 2003/08/21 19:31:58 cheshire +Cosmetic: Swap order of fields + +Revision 1.112 2003/08/21 19:27:36 cheshire + Traffic reduction: No need to announce record for longer than TTL + +Revision 1.111 2003/08/21 02:21:50 cheshire + Efficiency: Reduce repeated queries + +Revision 1.110 2003/08/20 23:39:31 cheshire + Review syslog messages, and remove as appropriate + +Revision 1.109 2003/08/19 22:24:10 cheshire +Comment change + +Revision 1.108 2003/08/19 22:20:00 cheshire + Don't use IPv6 on interfaces that have a routable IPv4 address configured +More minor refinements + +Revision 1.107 2003/08/19 06:48:25 cheshire + Guard against excessive record updates +Each record starts with 10 UpdateCredits. +Every update consumes one UpdateCredit. +UpdateCredits are replenished at a rate of one one per minute, up to a maximum of 10. +As the number of UpdateCredits declines, the number of announcements is similarly scaled back. +When fewer than 5 UpdateCredits remain, the first announcement is also delayed by an increasing amount. + +Revision 1.106 2003/08/19 04:49:28 cheshire + Interaction between v4, v6 and dual-stack hosts not working quite right +1. A dual-stack host should only suppress its own query if it sees the same query from other hosts on BOTH IPv4 and IPv6. +2. When we see the first v4 (or first v6) member of a group, we re-trigger questions and probes on that interface. +3. When we see the last v4 (or v6) member of a group go away, we revalidate all the records received on that interface. + +Revision 1.105 2003/08/19 02:33:37 cheshire +Update comments + +Revision 1.104 2003/08/19 02:31:11 cheshire + mDNSResponder overenthusiastic with final expiration queries +Final expiration queries now only mark the question for sending on the particular interface +pertaining to the record that's expiring. + +Revision 1.103 2003/08/18 19:05:44 cheshire + UpdateRecord not working right +Added "newrdlength" field to hold new length of updated rdata + +Revision 1.102 2003/08/16 03:39:00 cheshire + InterfaceID -1 indicates "local only" + +Revision 1.101 2003/08/15 20:16:02 cheshire + mDNSResponder takes too much RPRVT +We want to avoid touching the rdata pages, so we don't page them in. +1. RDLength was stored with the rdata, which meant touching the page just to find the length. + Moved this from the RData to the ResourceRecord object. +2. To avoid unnecessarily touching the rdata just to compare it, + compute a hash of the rdata and store the hash in the ResourceRecord object. + +Revision 1.100 2003/08/14 19:29:04 cheshire + Include cache records in SIGINFO output +Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSEmbeddedAPI.h so daemon.c can use them + +Revision 1.99 2003/08/14 02:17:05 cheshire + Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord + +Revision 1.98 2003/08/12 19:56:23 cheshire +Update to APSL 2.0 + +Revision 1.97 2003/08/12 14:59:27 cheshire + Rate-limiting blocks some legitimate responses +When setting LastMCTime also record LastMCInterface. When checking LastMCTime to determine +whether to suppress the response, also check LastMCInterface to see if it matches. + +Revision 1.96 2003/08/12 13:57:04 cheshire + Improve cache performance +Changed the number of hash table slots from 37 to 499 + +Revision 1.95 2003/08/09 00:55:02 cheshire + mDNSResponder is taking 20-30% of the CPU +Don't scan the whole cache after every packet. + +Revision 1.94 2003/08/09 00:35:29 cheshire + +Revision 1.93 2003/08/08 18:55:48 cheshire + Guard against time going backwards + +Revision 1.92 2003/08/08 18:36:04 cheshire + Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug + +Revision 1.91 2003/08/06 21:33:39 cheshire +Fix compiler warnings on PocketPC 2003 (Windows CE) + +Revision 1.90 2003/08/06 20:30:17 cheshire +Add structure definition for rdataMX (not currently used, but good to have it for completeness) + +Revision 1.89 2003/08/06 18:58:19 cheshire +Update comments + +Revision 1.88 2003/07/24 23:45:44 cheshire +To eliminate compiler warnings, changed definition of mDNSBool from +"unsigned char" to "int", since "int" is in fact truly the type that C uses +for the result of comparison operators (a: Feature: New DNS-SD APIs (#7875) (mDNSResponder component) +Added error type for incompatibility between daemon and client versions + +Revision 1.85 2003/07/19 03:23:13 cheshire + mDNSResponder needs to receive and cache larger records + +Revision 1.84 2003/07/18 23:52:12 cheshire +To improve consistency of field naming, global search-and-replace: +NextProbeTime -> NextScheduledProbe +NextResponseTime -> NextScheduledResponse + +Revision 1.83 2003/07/18 00:29:59 cheshire + Remove mDNSResponder version from packet header and use HINFO record instead + +Revision 1.82 2003/07/17 17:35:04 cheshire + Rate-limit responses, to guard against packet flooding + +Revision 1.81 2003/07/16 05:01:36 cheshire +Add fields 'LargeAnswers' and 'ExpectUnicastResponse' in preparation for + Need to implement "unicast response" request, using top bit of qclass + +Revision 1.80 2003/07/15 01:55:12 cheshire + Need to implement service registration with subtypes + +Revision 1.79 2003/07/13 02:28:00 cheshire + SendResponses didn't all its responses +Delete all references to RRInterfaceActive -- it's now superfluous + +Revision 1.78 2003/07/13 01:47:53 cheshire +Fix one error and one warning in the Windows build + +Revision 1.77 2003/07/11 01:32:38 cheshire +Syntactic cleanup (no change to funcationality): Now that we only have one host name, +rename field "hostname1" to "hostname", and field "RR_A1" to "RR_A". + +Revision 1.76 2003/07/11 01:28:00 cheshire + No more local.arpa + +Revision 1.75 2003/07/02 21:19:45 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.74 2003/07/02 02:41:23 cheshire + mDNSResponder needs to start with a smaller cache and then grow it as needed + +Revision 1.73 2003/06/10 04:24:39 cheshire + React when we observe other people query unsuccessfully for a record that's in our cache +Some additional refinements: +Don't try to do this for unicast-response queries +better tracking of Qs and KAs in multi-packet KA lists + +Revision 1.72 2003/06/10 01:46:27 cheshire +Add better comments explaining how these data structures are intended to be used from the client layer + +Revision 1.71 2003/06/07 06:45:05 cheshire + No need for multiple machines to all be sending the same queries + +Revision 1.70 2003/06/07 04:50:53 cheshire + React when we observe other people query unsuccessfully for a record that's in our cache + +Revision 1.69 2003/06/07 04:22:17 cheshire +Add MsgBuffer for error log and debug messages + +Revision 1.68 2003/06/07 01:46:38 cheshire + When query produces zero results, call mDNS_Reconfirm() on any antecedent records + +Revision 1.67 2003/06/07 01:22:14 cheshire + mDNSResponder needs an mDNS_Reconfirm() function + +Revision 1.66 2003/06/07 00:59:43 cheshire + Need some randomness to spread queries on the network + +Revision 1.65 2003/06/06 21:41:11 cheshire +For consistency, mDNS_StopQuery() should return an mStatus result, just like all the other mDNSCore routines + +Revision 1.64 2003/06/06 21:38:55 cheshire +Renamed 'NewData' as 'FreshData' (The data may not be new data, just a refresh of data that we +already had in our cache. This refreshes our TTL on the data, but the data itself stays the same.) + +Revision 1.63 2003/06/06 17:20:14 cheshire +For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass +(Global search-and-replace; no functional change to code execution.) + +Revision 1.62 2003/06/04 01:25:33 cheshire + Cannot perform multi-packet known-answer suppression messages +Display time interval between first and subsequent queries + +Revision 1.61 2003/06/03 05:02:16 cheshire + Duplicate registrations not handled as efficiently as they should be + +Revision 1.60 2003/05/31 00:09:49 cheshire + Add ability to discover what services are on a network + +Revision 1.59 2003/05/29 06:11:35 cheshire +: Report if there appear to be too many "Resolve" callbacks + +Revision 1.58 2003/05/29 05:48:06 cheshire +Minor fix for when generating printf warnings: mDNS_snprintf arguments are now 3,4 + +Revision 1.57 2003/05/26 03:21:27 cheshire +Tidy up address structure naming: +mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) +mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 +mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 + +Revision 1.56 2003/05/26 03:01:27 cheshire + sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead + +Revision 1.55 2003/05/26 00:47:30 cheshire +Comment clarification + +Revision 1.54 2003/05/24 16:39:48 cheshire + SendResponses also needs to handle multihoming better + +Revision 1.53 2003/05/23 02:15:37 cheshire +Fixed misleading use of the term "duplicate suppression" where it should have +said "known answer suppression". (Duplicate answer suppression is something +different, and duplicate question suppression is yet another thing, so the use +of the completely vague term "duplicate suppression" was particularly bad.) + +Revision 1.52 2003/05/22 02:29:22 cheshire + SendQueries needs to handle multihoming better +Complete rewrite of SendQueries. Works much better now :-) + +Revision 1.51 2003/05/21 20:14:55 cheshire +Fix comments and warnings + +Revision 1.50 2003/05/14 07:08:36 cheshire + mDNSResponder should be smarter about reconfigurations +Previously, when there was any network configuration change, mDNSResponder +would tear down the entire list of active interfaces and start again. +That was very disruptive, and caused the entire cache to be flushed, +and caused lots of extra network traffic. Now it only removes interfaces +that have really gone, and only adds new ones that weren't there before. + +Revision 1.49 2003/05/07 01:49:36 cheshire +Remove "const" in ConstructServiceName prototype + +Revision 1.48 2003/05/07 00:18:44 cheshire +Fix typo: "kDNSQClass_Mask" should be "kDNSClass_Mask" + +Revision 1.47 2003/05/06 00:00:46 cheshire + Rationalize naming of domainname manipulation functions + +Revision 1.46 2003/04/30 20:39:09 cheshire +Add comment + +Revision 1.45 2003/04/29 00:40:50 cheshire +Fix compiler warnings + +Revision 1.44 2003/04/26 02:41:56 cheshire + Change timenow from a local variable to a structure member + +Revision 1.43 2003/04/25 01:45:56 cheshire + mDNS_RegisterNoSuchService needs to include a host name + +Revision 1.42 2003/04/15 20:58:31 jgraessl + + Added a hash to lookup records in the cache. + +Revision 1.41 2003/04/15 18:09:13 jgraessl + + +Reviewed by: Stuart Cheshire +Added code to keep track of when the next cache item will expire so we can +call TidyRRCache only when necessary. + +Revision 1.40 2003/03/29 01:55:19 cheshire + mDNSResponder sometimes suffers false self-conflicts when it sees its own packets +Solution: Major cleanup of packet timing and conflict handling rules + +Revision 1.39 2003/03/27 03:30:55 cheshire + Name conflicts not handled properly, resulting in memory corruption, and eventual crash +Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback +Fixes: +1. Make mDNS_DeregisterInterface() safe to call from a callback +2. Make HostNameCallback() use mDNS_DeadvertiseInterface() instead + (it never really needed to deregister the interface at all) + +Revision 1.38 2003/03/15 04:40:36 cheshire +Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID" + +Revision 1.37 2003/03/14 21:34:11 cheshire + Can't setup and print to Lexmark PS printers via Airport Extreme +Increase size of cache rdata from 512 to 768 + +Revision 1.36 2003/03/05 03:38:35 cheshire + Bogus error message in console: died or deallocated, but no record of client can be found! +Fixed by leaving client in list after conflict, until client explicitly deallocates + +Revision 1.35 2003/02/21 02:47:54 cheshire + mDNSResponder needs performance improvements +Several places in the code were calling CacheRRActive(), which searched the entire +question list every time, to see if this cache resource record answers any question. +Instead, we now have a field "CRActiveQuestion" in the resource record structure + +Revision 1.34 2003/02/21 01:54:08 cheshire + mDNSResponder needs performance improvements +Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt") + +Revision 1.33 2003/02/20 06:48:32 cheshire + Xserve RAID needs to do interface-specific registrations +Reviewed by: Josh Graessley, Bob Bradley + +Revision 1.32 2003/01/31 03:35:59 cheshire + mDNSResponder sometimes fails to find the correct results +When there were *two* active questions in the list, they were incorrectly +finding *each other* and *both* being marked as duplicates of another question + +Revision 1.31 2003/01/29 02:46:37 cheshire +Fix for IPv6: +A physical interface is identified solely by its InterfaceID (not by IP and type). +On a given InterfaceID, mDNSCore may send both v4 and v6 multicasts. +In cases where the requested outbound protocol (v4 or v6) is not supported on +that InterfaceID, the platform support layer should simply discard that packet. + +Revision 1.30 2003/01/29 01:47:08 cheshire +Rename 'Active' to 'CRActive' or 'InterfaceActive' for improved clarity + +Revision 1.29 2003/01/28 05:23:43 cheshire + mDNSResponder sometimes fails to find the correct results +Add 'Active' flag for interfaces + +Revision 1.28 2003/01/28 01:35:56 cheshire +Revise comment about ThisQInterval to reflect new semantics + +Revision 1.27 2003/01/13 23:49:42 jgraessl +Merged changes for the following fixes in to top of tree: + computer name changes not handled properly + service name changes are not properly handled + announcements sent in pairs, failing chattiness test + +Revision 1.26 2002/12/23 22:13:28 jgraessl + +Reviewed by: Stuart Cheshire +Initial IPv6 support for mDNSResponder. + +Revision 1.25 2002/09/21 20:44:49 zarzycki +Added APSL info + +Revision 1.24 2002/09/19 23:47:35 cheshire +Added mDNS_RegisterNoSuchService() function for assertion of non-existence +of a particular named service + +Revision 1.23 2002/09/19 21:25:34 cheshire +mDNS_snprintf() doesn't need to be in a separate file + +Revision 1.22 2002/09/19 04:20:43 cheshire +Remove high-ascii characters that confuse some systems + +Revision 1.21 2002/09/17 01:06:35 cheshire +Change mDNS_AdvertiseLocalAddresses to be a parameter to mDNS_Init() + +Revision 1.20 2002/09/16 18:41:41 cheshire +Merge in license terms from Quinn's copy, in preparation for Darwin release + +*/ + +#ifndef __mDNSClientAPI_h +#define __mDNSClientAPI_h + +#include // stdarg.h is required for for va_list support for the mDNS_vsnprintf declaration +#include "mDNSDebug.h" + +#ifdef __cplusplus + extern "C" { +#endif + +// *************************************************************************** +// Function scope indicators + +// If you see "mDNSlocal" before a function name in a C file, it means the function is not callable outside this file +#ifndef mDNSlocal +#define mDNSlocal static +#endif +// If you see "mDNSexport" before a symbol in a C file, it means the symbol is exported for use by clients +// For every "mDNSexport" in a C file, there needs to be a corresponding "extern" declaration in some header file +// (When a C file #includes a header file, the "extern" declarations tell the compiler: +// "This symbol exists -- but not necessarily in this C file.") +#ifndef mDNSexport +#define mDNSexport +#endif + +// *************************************************************************** +// Structure packing macro + +// If we're not using GNUC, it's not fatal. +// Most compilers naturally pack the on-the-wire structures correctly anyway, so a plain "struct" is usually fine. +// In the event that structures are not packed correctly, mDNS_Init() will detect this and report an error, so the +// developer will know what's wrong, and can investigate what needs to be done on that compiler to provide proper packing. +#ifndef packedstruct + #if ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 9))) + #define packedstruct struct __attribute__((__packed__)) + #define packedunion union __attribute__((__packed__)) + #else + #define packedstruct struct + #define packedunion union + #endif +#endif + +// *************************************************************************** +#if 0 +#pragma mark - DNS Resource Record class and type constants +#endif + +typedef enum // From RFC 1035 + { + kDNSClass_IN = 1, // Internet + kDNSClass_CS = 2, // CSNET + kDNSClass_CH = 3, // CHAOS + kDNSClass_HS = 4, // Hesiod + kDNSClass_NONE = 254, // Used in DNS UPDATE [RFC 2136] + + kDNSClass_Mask = 0x7FFF,// Multicast DNS uses the bottom 15 bits to identify the record class... + kDNSClass_UniqueRRSet = 0x8000,// ... and the top bit indicates that all other cached records are now invalid + + kDNSQClass_ANY = 255, // Not a DNS class, but a DNS query class, meaning "all classes" + kDNSQClass_UnicastResponse = 0x8000 // Top bit set in a question means "unicast response acceptable" + } DNS_ClassValues; + +typedef enum // From RFC 1035 + { + kDNSType_A = 1, // 1 Address + kDNSType_NS, // 2 Name Server + kDNSType_MD, // 3 Mail Destination + kDNSType_MF, // 4 Mail Forwarder + kDNSType_CNAME, // 5 Canonical Name + kDNSType_SOA, // 6 Start of Authority + kDNSType_MB, // 7 Mailbox + kDNSType_MG, // 8 Mail Group + kDNSType_MR, // 9 Mail Rename + kDNSType_NULL, // 10 NULL RR + kDNSType_WKS, // 11 Well-known-service + kDNSType_PTR, // 12 Domain name pointer + kDNSType_HINFO, // 13 Host information + kDNSType_MINFO, // 14 Mailbox information + kDNSType_MX, // 15 Mail Exchanger + kDNSType_TXT, // 16 Arbitrary text string + + kDNSType_AAAA = 28, // 28 IPv6 address + kDNSType_SRV = 33, // 33 Service record + kDNSType_OPT = 41, // EDNS0 OPT record + kDNSType_TSIG = 250, // 250 Transaction Signature + + kDNSQType_ANY = 255 // Not a DNS type, but a DNS query type, meaning "all types" + } DNS_TypeValues; + +// *************************************************************************** +#if 0 +#pragma mark - Simple types +#endif + +// mDNS defines its own names for these common types to simplify portability across +// multiple platforms that may each have their own (different) names for these types. +typedef int mDNSBool; +typedef signed char mDNSs8; +typedef unsigned char mDNSu8; +typedef signed short mDNSs16; +typedef unsigned short mDNSu16; + +// says +// __LP64__ _LP64 +// These macros are defined, with value 1, if (and only if) the compilation is +// for a target where long int and pointer both use 64-bits and int uses 32-bit. +// says +// Macro Name __LP64__ Value 1 +// A quick Google search for "defined(__LP64__)" OR "#ifdef __LP64__" gives 2590 hits and +// a search for "#if __LP64__" gives only 12, so I think we'll go with the majority and use defined() +#if defined(_LP64) || defined(__LP64__) +typedef signed int mDNSs32; +typedef unsigned int mDNSu32; +#elif defined(_ILP64) || defined(__ILP64__) +typedef signed int32 mDNSs32; +typedef unsigned int32 mDNSu32; +#else +typedef signed long mDNSs32; +typedef unsigned long mDNSu32; +#endif + +// To enforce useful type checking, we make mDNSInterfaceID be a pointer to a dummy struct +// This way, mDNSInterfaceIDs can be assigned, and compared with each other, but not with other types +// Declaring the type to be the typical generic "void *" would lack this type checking +typedef struct mDNSInterfaceID_dummystruct { void *dummy; } *mDNSInterfaceID; + +// These types are for opaque two- and four-byte identifiers. +// The "NotAnInteger" fields of the unions allow the value to be conveniently passed around in a +// register for the sake of efficiency, and compared for equality or inequality, but don't forget -- +// just because it is in a register doesn't mean it is an integer. Operations like greater than, +// less than, add, multiply, increment, decrement, etc., are undefined for opaque identifiers, +// and if you make the mistake of trying to do those using the NotAnInteger field, then you'll +// find you get code that doesn't work consistently on big-endian and little-endian machines. +typedef packedunion { mDNSu8 b[ 2]; mDNSu16 NotAnInteger; } mDNSOpaque16; +typedef packedunion { mDNSu8 b[ 4]; mDNSu32 NotAnInteger; } mDNSOpaque32; +typedef packedunion { mDNSu8 b[ 6]; mDNSu16 w[3]; mDNSu32 l[1]; } mDNSOpaque48; +typedef packedunion { mDNSu8 b[16]; mDNSu16 w[8]; mDNSu32 l[4]; } mDNSOpaque128; + +typedef mDNSOpaque16 mDNSIPPort; // An IP port is a two-byte opaque identifier (not an integer) +typedef mDNSOpaque32 mDNSv4Addr; // An IP address is a four-byte opaque identifier (not an integer) +typedef mDNSOpaque128 mDNSv6Addr; // An IPv6 address is a 16-byte opaque identifier (not an integer) +typedef mDNSOpaque48 mDNSEthAddr; // An Ethernet address is a six-byte opaque identifier (not an integer) + +enum + { + mDNSAddrType_None = 0, + mDNSAddrType_IPv4 = 4, + mDNSAddrType_IPv6 = 6, + mDNSAddrType_Unknown = ~0 // Special marker value used in known answer list recording + }; + +typedef struct + { + mDNSs32 type; + union { mDNSv6Addr v6; mDNSv4Addr v4; } ip; + } mDNSAddr; + +enum { mDNSfalse = 0, mDNStrue = 1 }; + +#define mDNSNULL 0L + +enum + { + mStatus_Waiting = 1, + mStatus_NoError = 0, + + // mDNS return values are in the range FFFE FF00 (-65792) to FFFE FFFF (-65537) + // The top end of the range (FFFE FFFF) is used for error codes; + // the bottom end of the range (FFFE FF00) is used for non-error values; + + // Error codes: + mStatus_UnknownErr = -65537, // First value: 0xFFFE FFFF + mStatus_NoSuchNameErr = -65538, + mStatus_NoMemoryErr = -65539, + mStatus_BadParamErr = -65540, + mStatus_BadReferenceErr = -65541, + mStatus_BadStateErr = -65542, + mStatus_BadFlagsErr = -65543, + mStatus_UnsupportedErr = -65544, + mStatus_NotInitializedErr = -65545, + mStatus_NoCache = -65546, + mStatus_AlreadyRegistered = -65547, + mStatus_NameConflict = -65548, + mStatus_Invalid = -65549, + mStatus_Firewall = -65550, + mStatus_Incompatible = -65551, + mStatus_BadInterfaceErr = -65552, + mStatus_Refused = -65553, + mStatus_NoSuchRecord = -65554, + mStatus_NoAuth = -65555, + mStatus_NoSuchKey = -65556, + mStatus_NATTraversal = -65557, + mStatus_DblNAT = -65558, + mStatus_BadTime = -65559, + mStatus_BadSig = -65560, // while we define this per RFC 2845, BIND 9 returns Refused for bad/missing signatures + mStatus_BadKey = -65561, + + // -65562 to -65786 currently unused; available for allocation + + // tcp connection status + mStatus_ConnPending = -65787, + mStatus_ConnFailed = -65788, + mStatus_ConnEstablished = -65789, + + // Non-error values: + mStatus_GrowCache = -65790, + mStatus_ConfigChanged = -65791, + mStatus_MemFree = -65792 // Last value: 0xFFFE FF00 + + // mStatus_MemFree is the last legal mDNS error code, at the end of the range allocated for mDNS + }; + +typedef mDNSs32 mStatus; + +// RFC 1034/1035 specify that a domain label consists of a length byte plus up to 63 characters +#define MAX_DOMAIN_LABEL 63 +typedef struct { mDNSu8 c[ 64]; } domainlabel; // One label: length byte and up to 63 characters + +// RFC 1034/1035 specify that a domain name, including length bytes, data bytes, and terminating zero, may be up to 255 bytes long +#define MAX_DOMAIN_NAME 255 +typedef struct { mDNSu8 c[256]; } domainname; // Up to 255 bytes of length-prefixed domainlabels + +typedef struct { mDNSu8 c[256]; } UTF8str255; // Null-terminated C string + +// The longest legal textual form of a DNS name is 1005 bytes, including the C-string terminating NULL at the end. +// Explanation: +// When a native domainname object is converted to printable textual form using ConvertDomainNameToCString(), +// non-printing characters are represented in the conventional DNS way, as '\ddd', where ddd is a three-digit decimal number. +// The longest legal domain name is 255 bytes, in the form of four labels as shown below: +// Length byte, 63 data bytes, length byte, 63 data bytes, length byte, 63 data bytes, length byte, 61 data bytes, zero byte. +// Each label is encoded textually as characters followed by a trailing dot. +// If every character has to be represented as a four-byte escape sequence, then this makes the maximum textual form four labels +// plus the C-string terminating NULL as shown below: +// 63*4+1 + 63*4+1 + 63*4+1 + 61*4+1 + 1 = 1005. +// Note that MAX_ESCAPED_DOMAIN_LABEL is not normally used: If you're only decoding a single label, escaping is usually not required. +// It is for domain names, where dots are used as label separators, that proper escaping is vital. +#define MAX_ESCAPED_DOMAIN_LABEL 254 +#define MAX_ESCAPED_DOMAIN_NAME 1005 + +// Most records have a TTL of 75 minutes, so that their 80% cache-renewal query occurs once per hour. +// For records containing a hostname (in the name on the left, or in the rdata on the right), +// like A, AAAA, reverse-mapping PTR, and SRV, we use a two-minute TTL by default, because we don't want +// them to hang around for too long in the cache if the host in question crashes or otherwise goes away. +#define kStandardTTL (3600 * 100 / 80) +#define kHostNameTTL 120 + +#define DefaultTTLforRRType(X) (((X) == kDNSType_A || (X) == kDNSType_AAAA || (X) == kDNSType_SRV) ? kHostNameTTL : kStandardTTL) + +// *************************************************************************** +#if 0 +#pragma mark - DNS Message structures +#endif + +#define mDNS_numZones numQuestions +#define mDNS_numPrereqs numAnswers +#define mDNS_numUpdates numAuthorities + +typedef packedstruct + { + mDNSOpaque16 id; + mDNSOpaque16 flags; + mDNSu16 numQuestions; + mDNSu16 numAnswers; + mDNSu16 numAuthorities; + mDNSu16 numAdditionals; + } DNSMessageHeader; + +// We can send and receive packets up to 9000 bytes (Ethernet Jumbo Frame size, if that ever becomes widely used) +// However, in the normal case we try to limit packets to 1500 bytes so that we don't get IP fragmentation on standard Ethernet +// 40 (IPv6 header) + 8 (UDP header) + 12 (DNS message header) + 1440 (DNS message body) = 1500 total +#define AbsoluteMaxDNSMessageData 8940 +#define NormalMaxDNSMessageData 1440 +typedef packedstruct + { + DNSMessageHeader h; // Note: Size 12 bytes + mDNSu8 data[AbsoluteMaxDNSMessageData]; // 40 (IPv6) + 8 (UDP) + 12 (DNS header) + 8940 (data) = 9000 + } DNSMessage; + +// *************************************************************************** +#if 0 +#pragma mark - Resource Record structures +#endif + +// Authoritative Resource Records: +// There are four basic types: Shared, Advisory, Unique, Known Unique + +// * Shared Resource Records do not have to be unique +// -- Shared Resource Records are used for DNS-SD service PTRs +// -- It is okay for several hosts to have RRs with the same name but different RDATA +// -- We use a random delay on responses to reduce collisions when all the hosts respond to the same query +// -- These RRs typically have moderately high TTLs (e.g. one hour) +// -- These records are announced on startup and topology changes for the benefit of passive listeners +// -- These records send a goodbye packet when deregistering +// +// * Advisory Resource Records are like Shared Resource Records, except they don't send a goodbye packet +// +// * Unique Resource Records should be unique among hosts within any given mDNS scope +// -- The majority of Resource Records are of this type +// -- If two entities on the network have RRs with the same name but different RDATA, this is a conflict +// -- Responses may be sent immediately, because only one host should be responding to any particular query +// -- These RRs typically have low TTLs (e.g. a few minutes) +// -- On startup and after topology changes, a host issues queries to verify uniqueness + +// * Known Unique Resource Records are treated like Unique Resource Records, except that mDNS does +// not have to verify their uniqueness because this is already known by other means (e.g. the RR name +// is derived from the host's IP or Ethernet address, which is already known to be a unique identifier). + +// Summary of properties of different record types: +// Probe? Does this record type send probes before announcing? +// Conflict? Does this record type react if we observe an apparent conflict? +// Goodbye? Does this record type send a goodbye packet on departure? +// +// Probe? Conflict? Goodbye? Notes +// Unregistered Should not appear in any list (sanity check value) +// Shared No No Yes e.g. Service PTR record +// Deregistering No No Yes Shared record about to announce its departure and leave the list +// Advisory No No No +// Unique Yes Yes No Record intended to be unique -- will probe to verify +// Verified Yes Yes No Record has completed probing, and is verified unique +// KnownUnique No Yes No Record is assumed by other means to be unique + +// Valid lifecycle of a record: +// Unregistered -> Shared -> Deregistering -(goodbye)-> Unregistered +// Unregistered -> Advisory -> Unregistered +// Unregistered -> Unique -(probe)-> Verified -> Unregistered +// Unregistered -> KnownUnique -> Unregistered + +// Each Authoritative kDNSRecordType has only one bit set. This makes it easy to quickly see if a record +// is one of a particular set of types simply by performing the appropriate bitwise masking operation. + +// Cache Resource Records (received from the network): +// There are four basic types: Answer, Unique Answer, Additional, Unique Additional +// Bit 7 (the top bit) of kDNSRecordType is always set for Cache Resource Records; always clear for Authoritative Resource Records +// Bit 6 (value 0x40) is set for answer records; clear for additional records +// Bit 5 (value 0x20) is set for records received with the kDNSClass_UniqueRRSet + +enum + { + kDNSRecordTypeUnregistered = 0x00, // Not currently in any list + kDNSRecordTypeDeregistering = 0x01, // Shared record about to announce its departure and leave the list + + kDNSRecordTypeUnique = 0x02, // Will become a kDNSRecordTypeVerified when probing is complete + + kDNSRecordTypeAdvisory = 0x04, // Like Shared, but no goodbye packet + kDNSRecordTypeShared = 0x08, // Shared means record name does not have to be unique -- use random delay on responses + + kDNSRecordTypeVerified = 0x10, // Unique means mDNS should check that name is unique (and then send immediate responses) + kDNSRecordTypeKnownUnique = 0x20, // Known Unique means mDNS can assume name is unique without checking + // For Dynamic Update records, Known Unique means the record must already exist on the server. + kDNSRecordTypeUniqueMask = (kDNSRecordTypeUnique | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique), + kDNSRecordTypeActiveMask = (kDNSRecordTypeAdvisory | kDNSRecordTypeShared | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique), + + kDNSRecordTypePacketAdd = 0x80, // Received in the Additional Section of a DNS Response + kDNSRecordTypePacketAddUnique = 0x90, // Received in the Additional Section of a DNS Response with kDNSClass_UniqueRRSet set + kDNSRecordTypePacketAuth = 0xA0, // Received in the Authorities Section of a DNS Response + kDNSRecordTypePacketAuthUnique = 0xB0, // Received in the Authorities Section of a DNS Response with kDNSClass_UniqueRRSet set + kDNSRecordTypePacketAns = 0xC0, // Received in the Answer Section of a DNS Response + kDNSRecordTypePacketAnsUnique = 0xD0, // Received in the Answer Section of a DNS Response with kDNSClass_UniqueRRSet set + + kDNSRecordTypePacketAnsMask = 0x40, // True for PacketAns and PacketAnsUnique + kDNSRecordTypePacketUniqueMask = 0x10 // True for PacketAddUnique, PacketAnsUnique, PacketAuthUnique + }; + +typedef packedstruct { mDNSu16 priority; mDNSu16 weight; mDNSIPPort port; domainname target; } rdataSRV; +typedef packedstruct { mDNSu16 preference; domainname exchange; } rdataMX; +typedef packedstruct + { + domainname mname; + domainname rname; + mDNSs32 serial; // Modular counter; increases when zone changes + mDNSu32 refresh; // Time in seconds that a slave waits after successful replication of the database before it attempts replication again + mDNSu32 retry; // Time in seconds that a slave waits after an unsuccessful replication attempt before it attempts replication again + mDNSu32 expire; // Time in seconds that a slave holds on to old data while replication attempts remain unsuccessful + mDNSu32 min; // Nominally the minimum record TTL for this zone, in seconds; also used for negative caching. + } rdataSOA; + +typedef packedstruct + { + mDNSu16 vers; + mDNSu16 llqOp; + mDNSu16 err; + mDNSu8 id[8]; + mDNSu32 lease; + } LLQOptData; + +// NOTE: rdataOpt format may be repeated an arbitrary number of times in a single resource record +typedef packedstruct + { + mDNSu16 opt; + mDNSu16 optlen; + union { LLQOptData llq; mDNSu32 lease; } OptData; + } rdataOpt; + +// StandardAuthRDSize is 264 (256+8), which is large enough to hold a maximum-sized SRV record +// MaximumRDSize is 8K the absolute maximum we support (at least for now) +#define StandardAuthRDSize 264 +#define MaximumRDSize 8192 + +// InlineCacheRDSize is 64 +// Records received from the network with rdata this size or less have their rdata stored right in the CacheRecord object +// Records received from the network with rdata larger than this have additional storage allocated for the rdata +// A quick unscientific sample from a busy network at Apple with lots of machines revealed this: +// 1461 records in cache +// 292 were one-byte TXT records +// 136 were four-byte A records +// 184 were sixteen-byte AAAA records +// 780 were various PTR, TXT and SRV records from 12-64 bytes +// Only 69 records had rdata bigger than 64 bytes +#define InlineCacheRDSize 64 + +typedef union + { + mDNSu8 data[StandardAuthRDSize]; + mDNSv4Addr ipv4; // For 'A' record + mDNSv6Addr ipv6; // For 'AAAA' record + domainname name; // For PTR, NS, and CNAME records + UTF8str255 txt; // For TXT record + rdataSRV srv; // For SRV record + rdataMX mx; // For MX record + rdataSOA soa; // For SOA record + rdataOpt opt; // For eDNS0 opt record + } RDataBody; + +typedef struct + { + mDNSu16 MaxRDLength; // Amount of storage allocated for rdata (usually sizeof(RDataBody)) + RDataBody u; + } RData; +#define sizeofRDataHeader (sizeof(RData) - sizeof(RDataBody)) + +typedef struct AuthRecord_struct AuthRecord; +typedef struct CacheRecord_struct CacheRecord; +typedef struct ResourceRecord_struct ResourceRecord; +typedef struct DNSQuestion_struct DNSQuestion; +typedef struct mDNS_struct mDNS; +typedef struct mDNS_PlatformSupport_struct mDNS_PlatformSupport; +typedef struct NATTraversalInfo_struct NATTraversalInfo; + +// Note: Within an mDNSRecordCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Close(), mDNS_Execute() +typedef void mDNSRecordCallback(mDNS *const m, AuthRecord *const rr, mStatus result); + +// Note: +// Restrictions: An mDNSRecordUpdateCallback may not make any mDNS API calls. +// The intent of this callback is to allow the client to free memory, if necessary. +// The internal data structures of the mDNS code may not be in a state where mDNS API calls may be made safely. +typedef void mDNSRecordUpdateCallback(mDNS *const m, AuthRecord *const rr, RData *OldRData); + +struct ResourceRecord_struct + { + mDNSu8 RecordType; // See enum above + mDNSInterfaceID InterfaceID; // Set if this RR is specific to one interface + // For records received off the wire, InterfaceID is *always* set to the receiving interface + // For our authoritative records, InterfaceID is usually zero, except for those few records + // that are interface-specific (e.g. address records, especially linklocal addresses) + domainname name; + mDNSu16 rrtype; + mDNSu16 rrclass; + mDNSu32 rroriginalttl; // In seconds + mDNSu16 rdlength; // Size of the raw rdata, in bytes + mDNSu16 rdestimate; // Upper bound on size of rdata after name compression + mDNSu32 namehash; // Name-based (i.e. case insensitive) hash of name + mDNSu32 rdatahash; // 32-bit hash of the raw rdata + mDNSu32 rdnamehash; // Set if this rdata contains a domain name (e.g. PTR, SRV, CNAME etc.) + RData *rdata; // Pointer to storage for this rdata + }; + +// Unless otherwise noted, states may apply to either independent record registrations or service registrations +typedef enum + { + regState_FetchingZoneData = 1, // getting info - update not sent + regState_Pending = 2, // update sent, reply not received + regState_Registered = 3, // update sent, reply received + regState_DeregPending = 4, // dereg sent, reply not received + regState_DeregDeferred = 5, // dereg requested while in Pending state - send dereg AFTER registration is confirmed + regState_Cancelled = 6, // update not sent, reg. cancelled by client + regState_Unregistered = 8, // not in any list + regState_Refresh = 9, // outstanding refresh (or target change) message + regState_NATMap = 10, // establishing NAT port mapping or learning public address + regState_UpdatePending = 11, // update in flight as result of mDNS_Update call + regState_NoTarget = 12, // service registration pending registration of hostname (ServiceRegistrations only) + regState_ExtraQueued = 13 // extra record to be registered upon completion of service registration (RecordRegistrations only) + } regState_t; + +// context for both ServiceRecordSet and individual AuthRec structs +typedef struct + { + // registration/lease state + regState_t state; + mDNSBool lease; // dynamic update contains (should contain) lease option + mDNSs32 expire; // expiration of lease (-1 for static) + mDNSBool TestForSelfConflict; // on name conflict, check if we're just seeing our own orphaned records + + // identifier to match update request and response + mDNSOpaque16 id; + + // server info + domainname zone; // the zone that is updated + mDNSAddr ns; // primary name server for the record's zone !!!KRS not technically correct to cache longer than TTL + mDNSIPPort port; // port on which server accepts dynamic updates + + // NAT traversal context + NATTraversalInfo *NATinfo; // may be NULL + + // state for deferred operations + mDNSBool ClientCallbackDeferred; // invoke client callback on completion of pending operation(s) + mStatus DeferredStatus; // status to deliver when above flag is set + mDNSBool SRVUpdateDeferred; // do we need to change target or port once current operation completes? + mDNSBool LostTarget; // temporarily deregistered service because its target was deregistered + + // uDNS_UpdateRecord support fields + mDNSBool UpdateQueued; // Update the rdata once the current pending operation completes + RData *UpdateRData; // Pointer to new RData while a record update is in flight + mDNSu16 UpdateRDLen; // length of above field + mDNSRecordUpdateCallback *UpdateRDCallback; // client callback to free old rdata + } uDNS_RegInfo; + +struct AuthRecord_struct + { + // For examples of how to set up this structure for use in mDNS_Register(), + // see mDNS_AdvertiseInterface() or mDNS_RegisterService(). + // Basically, resrec and persistent metadata need to be set up before calling mDNS_Register(). + // mDNS_SetupResourceRecord() is avaliable as a helper routine to set up most fields to sensible default values for you + + AuthRecord *next; // Next in list; first element of structure for efficiency reasons + ResourceRecord resrec; + uDNS_RegInfo uDNS_info; + + // Persistent metadata for Authoritative Records + AuthRecord *Additional1; // Recommended additional record to include in response + AuthRecord *Additional2; // Another additional + AuthRecord *DependentOn; // This record depends on another for its uniqueness checking + AuthRecord *RRSet; // This unique record is part of an RRSet + mDNSRecordCallback *RecordCallback; // Callback function to call for state changes, and to free memory asynchronously on deregistration + void *RecordContext; // Context parameter for the callback function + mDNSu8 HostTarget; // Set if the target of this record (PTR, CNAME, SRV, etc.) is our host name + mDNSu8 AllowRemoteQuery; // Set if we allow hosts not on the local link to query this record + mDNSu8 ForceMCast; // Set by client to advertise solely via multicast, even for apparently unicast names + + // Transient state for Authoritative Records + mDNSu8 Acknowledged; // Set if we've given the success callback to the client + mDNSu8 ProbeCount; // Number of probes remaining before this record is valid (kDNSRecordTypeUnique) + mDNSu8 AnnounceCount; // Number of announcements remaining (kDNSRecordTypeShared) + mDNSu8 RequireGoodbye; // Set if this RR has been announced on the wire and will require a goodbye packet + mDNSu8 IncludeInProbe; // Set if this RR is being put into a probe right now + mDNSInterfaceID ImmedAnswer; // Someone on this interface issued a query we need to answer (all-ones for all interfaces) + mDNSu8 ImmedUnicast; // Set if we may send our response directly via unicast to the requester +#if MDNS_LOG_ANSWER_SUPPRESSION_TIMES + mDNSs32 ImmedAnswerMarkTime; +#endif + mDNSInterfaceID ImmedAdditional; // Hint that we might want to also send this record, just to be helpful + mDNSInterfaceID SendRNow; // The interface this query is being sent on right now + mDNSv4Addr v4Requester; // Recent v4 query for this record, or all-ones if more than one recent query + mDNSv6Addr v6Requester; // Recent v6 query for this record, or all-ones if more than one recent query + AuthRecord *NextResponse; // Link to the next element in the chain of responses to generate + const mDNSu8 *NR_AnswerTo; // Set if this record was selected by virtue of being a direct answer to a question + AuthRecord *NR_AdditionalTo; // Set if this record was selected by virtue of being additional to another + mDNSs32 ThisAPInterval; // In platform time units: Current interval for announce/probe + mDNSs32 AnnounceUntil; // In platform time units: Creation time + TTL + mDNSs32 LastAPTime; // In platform time units: Last time we sent announcement/probe + mDNSs32 LastMCTime; // Last time we multicast this record (used to guard against packet-storm attacks) + mDNSInterfaceID LastMCInterface; // Interface this record was multicast on at the time LastMCTime was recorded + RData *NewRData; // Set if we are updating this record with new rdata + mDNSu16 newrdlength; // ... and the length of the new RData + mDNSRecordUpdateCallback *UpdateCallback; + mDNSu32 UpdateCredits; // Token-bucket rate limiting of excessive updates + mDNSs32 NextUpdateCredit; // Time next token is added to bucket + mDNSs32 UpdateBlocked; // Set if update delaying is in effect + + RData rdatastorage; // Normally the storage is right here, except for oversized records + // rdatastorage MUST be the last thing in the structure -- when using oversized AuthRecords, extra bytes + // are appended after the end of the AuthRecord, logically augmenting the size of the rdatastorage + // DO NOT ADD ANY MORE FIELDS HERE + }; + +// Wrapper struct for Auth Records for higher-level code that cannot use the AuthRecord's ->next pointer field +typedef struct ARListElem + { + struct ARListElem *next; + AuthRecord ar; // Note: Must be last struct in field to accomodate oversized AuthRecords + } ARListElem; + +struct CacheRecord_struct + { + CacheRecord *next; // Next in list; first element of structure for efficiency reasons + ResourceRecord resrec; + + // Transient state for Cache Records + CacheRecord *NextInKAList; // Link to the next element in the chain of known answers to send + mDNSs32 TimeRcvd; // In platform time units + mDNSs32 DelayDelivery; // Set if we want to defer delivery of this answer to local clients + mDNSs32 NextRequiredQuery; // In platform time units + mDNSs32 LastUsed; // In platform time units + DNSQuestion *CRActiveQuestion; // Points to an active question referencing this answer + mDNSu32 UnansweredQueries; // Number of times we've issued a query for this record without getting an answer + mDNSs32 LastUnansweredTime; // In platform time units; last time we incremented UnansweredQueries + mDNSu32 MPUnansweredQ; // Multi-packet query handling: Number of times we've seen a query for this record + mDNSs32 MPLastUnansweredQT; // Multi-packet query handling: Last time we incremented MPUnansweredQ + mDNSu32 MPUnansweredKA; // Multi-packet query handling: Number of times we've seen this record in a KA list + mDNSBool MPExpectingKA; // Multi-packet query handling: Set when we increment MPUnansweredQ; allows one KA + CacheRecord *NextInCFList; // Set if this is in the list of records we just received with the cache flush bit set + + struct { mDNSu16 MaxRDLength; mDNSu8 data[InlineCacheRDSize]; } rdatastorage; // Storage for small records is right here + }; + +typedef struct + { + CacheRecord r; + mDNSu8 _extradata[MaximumRDSize-InlineCacheRDSize]; // Glue on the necessary number of extra bytes + } LargeCacheRecord; + +typedef struct uDNS_HostnameInfo + { + struct uDNS_HostnameInfo *next; + AuthRecord *ar; // registered address record + mDNSRecordCallback *StatusCallback; // callback to deliver success or error code to client layer + const void *StatusContext; // Client Context + } uDNS_HostnameInfo; + +typedef struct DNSServer + { + struct DNSServer *next; + mDNSAddr addr; + domainname domain; // name->server matching for "split dns" + } DNSServer; + +typedef struct NetworkInterfaceInfo_struct NetworkInterfaceInfo; + +// A NetworkInterfaceInfo_struct serves two purposes: +// 1. It holds the address, PTR and HINFO records to advertise a given IP address on a given physical interface +// 2. It tells mDNSCore which physical interfaces are available; each physical interface has its own unique InterfaceID. +// Since there may be multiple IP addresses on a single physical interface, +// there may be multiple NetworkInterfaceInfo_structs with the same InterfaceID. +// In this case, to avoid sending the same packet n times, when there's more than one +// struct with the same InterfaceID, mDNSCore picks one member of the set to be the +// active representative of the set; all others have the 'InterfaceActive' flag unset. + +struct NetworkInterfaceInfo_struct + { + // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. + NetworkInterfaceInfo *next; + + mDNSBool InterfaceActive; // Set if interface is sending & receiving packets (see comment above) + mDNSBool IPv4Available; // If InterfaceActive, set if v4 available on this InterfaceID + mDNSBool IPv6Available; // If InterfaceActive, set if v6 available on this InterfaceID + + // Standard AuthRecords that every Responder host should have (one per active IP address) + AuthRecord RR_A; // 'A' or 'AAAA' (address) record for our ".local" name + AuthRecord RR_PTR; // PTR (reverse lookup) record + AuthRecord RR_HINFO; + + // Client API fields: The client must set up these fields *before* calling mDNS_RegisterInterface() + mDNSInterfaceID InterfaceID; // Identifies physical interface; MUST NOT be 0, -1, or -2 + mDNSAddr ip; // The IPv4 or IPv6 address to advertise + mDNSAddr mask; + char ifname[16]; + mDNSBool Advertise; // False if you are only searching on this interface + mDNSBool McastTxRx; // Send/Receive multicast on this { InterfaceID, address family } ? + }; + +typedef struct ExtraResourceRecord_struct ExtraResourceRecord; +struct ExtraResourceRecord_struct + { + ExtraResourceRecord *next; + mDNSu32 ClientID; // Opaque ID field to be used by client to map an AddRecord call to a set of Extra records + AuthRecord r; + // Note: Add any additional fields *before* the AuthRecord in this structure, not at the end. + // In some cases clients can allocate larger chunks of memory and set r->rdata->MaxRDLength to indicate + // that this extra memory is available, which would result in any fields after the AuthRecord getting smashed + }; + +// Note: Within an mDNSServiceCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Close(), mDNS_Execute() +typedef struct ServiceRecordSet_struct ServiceRecordSet; +typedef void mDNSServiceCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result); +struct ServiceRecordSet_struct + { + // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. + // No fields need to be set up by the client prior to calling mDNS_RegisterService(); + // all required data is passed as parameters to that function. + ServiceRecordSet *next; + uDNS_RegInfo uDNS_info; + mDNSServiceCallback *ServiceCallback; + void *ServiceContext; + ExtraResourceRecord *Extras; // Optional list of extra AuthRecords attached to this service registration + mDNSu32 NumSubTypes; + AuthRecord *SubTypes; + mDNSBool Conflict; // Set if this record set was forcibly deregistered because of a conflict + domainname Host; // Set if this service record does not use the standard target host name + AuthRecord RR_ADV; // e.g. _services._dns-sd._udp.local. PTR _printer._tcp.local. + AuthRecord RR_PTR; // e.g. _printer._tcp.local. PTR Name._printer._tcp.local. + AuthRecord RR_SRV; // e.g. Name._printer._tcp.local. SRV 0 0 port target + AuthRecord RR_TXT; // e.g. Name._printer._tcp.local. TXT PrintQueueName + // Don't add any fields after AuthRecord RR_TXT. + // This is where the implicit extra space goes if we allocate a ServiceRecordSet containing an oversized RR_TXT record + }; + +// *************************************************************************** +#if 0 +#pragma mark - Question structures +#endif + +// We record the last eight instances of each duplicate query +// This gives us v4/v6 on each of Ethernet/AirPort and Firewire, and two free slots "for future expansion" +// If the host has more active interfaces that this it is not fatal -- duplicate question suppression will degrade gracefully. +// Since we will still remember the last eight, the busiest interfaces will still get the effective duplicate question suppression. +#define DupSuppressInfoSize 8 + +typedef struct + { + mDNSs32 Time; + mDNSInterfaceID InterfaceID; + mDNSs32 Type; // v4 or v6? + } DupSuppressInfo; + +typedef enum + { + // Setup states + LLQ_UnInit = 0, + LLQ_GetZoneInfo = 1, + LLQ_InitialRequest = 2, + LLQ_SecondaryRequest = 3, + LLQ_Refresh = 4, + LLQ_Retry = 5, + LLQ_Established = 6, + LLQ_Suspended = 7, + LLQ_SuspendDeferred = 8, // suspend once we get zone info + LLQ_SuspendedPoll = 9, // suspended from polling state + LLQ_NatMapWait = 10, + + // Established/error states + LLQ_Static = 16, + LLQ_Poll = 17, + LLQ_Error = 18, + LLQ_Cancelled = 19 + } LLQ_State; + +typedef struct + { + LLQ_State state; + mDNSAddr servAddr; + mDNSIPPort servPort; + DNSQuestion *question; + mDNSu32 origLease; // seconds (relative) + mDNSs32 retry; // ticks (absolute) + mDNSs32 expire; // ticks (absolute) + mDNSs16 ntries; + mDNSu8 id[8]; + mDNSBool deriveRemovesOnResume; + mDNSBool NATMap; // does this LLQ use the global LLQ NAT mapping? + } LLQ_Info; + +// LLQ constants +#define kDNSOpt_LLQ 1 +#define kDNSOpt_Lease 2 +#define kLLQ_Vers 0 // prerelease +#define kLLQ_DefLease 7200 // 2 hours +#define kUpdate_DefLease 7200 +#define kLLQ_MAX_TRIES 3 // retry an operation 3 times max +#define kLLQ_INIT_RESEND 2 // resend an un-ack'd packet after 2 seconds, then double for each additional +#define kLLQ_DEF_RETRY 1800 // retry a failed operation after 30 minutes +// LLQ Operation Codes +#define kLLQOp_Setup 1 +#define kLLQOp_Refresh 2 +#define kLLQOp_Event 3 + +#define LLQ_OPT_SIZE (2 * sizeof(mDNSu16)) + sizeof(LLQOptData) +#define LEASE_OPT_SIZE (2 * sizeof(mDNSu16)) + sizeof(mDNSs32) + +// LLQ Errror Codes +enum + { + LLQErr_NoError = 0, + LLQErr_ServFull = 1, + LLQErr_Static = 2, + LLQErr_FormErr = 3, + LLQErr_NoSuchLLQ = 4, + LLQErr_BadVers = 5, + LLQErr_UnknownErr = 6 + }; + +typedef void (*InternalResponseHndlr)(mDNS *const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *internalContext); +typedef struct + { + mDNSOpaque16 id; + mDNSBool internal; + InternalResponseHndlr responseCallback; // NULL if internal field is false + LLQ_Info *llq; // NULL for 1-shot queries + mDNSBool Answered; // have we received an answer (including NXDOMAIN) for this question? + CacheRecord *knownAnswers; + mDNSs32 RestartTime; // Mark when we restart a suspended query + void *context; + } uDNS_QuestionInfo; + +// Note: Within an mDNSQuestionCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Close(), mDNS_Execute() +typedef void mDNSQuestionCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord); +struct DNSQuestion_struct + { + // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. + DNSQuestion *next; + mDNSu32 qnamehash; + mDNSs32 DelayAnswering; // Set if we want to defer answering this question until the cache settles + mDNSs32 LastQTime; // Last scheduled transmission of this Q on *all* applicable interfaces + mDNSs32 ThisQInterval; // LastQTime + ThisQInterval is the next scheduled transmission of this Q + // ThisQInterval > 0 for an active question; + // ThisQInterval = 0 for a suspended question that's still in the list + // ThisQInterval = -1 for a cancelled question that's been removed from the list + mDNSs32 LastAnswerPktNum; // The sequence number of the last response packet containing an answer to this Q + mDNSu32 RecentAnswerPkts; // Number of answers since the last time we sent this query + mDNSu32 CurrentAnswers; // Number of records currently in the cache that answer this question + mDNSu32 LargeAnswers; // Number of answers with rdata > 1024 bytes + mDNSu32 UniqueAnswers; // Number of answers received with kDNSClass_UniqueRRSet bit set + DNSQuestion *DuplicateOf; + DNSQuestion *NextInDQList; + DupSuppressInfo DupSuppress[DupSuppressInfoSize]; + mDNSInterfaceID SendQNow; // The interface this query is being sent on right now + mDNSBool SendOnAll; // Set if we're sending this question on all active interfaces + mDNSs32 LastQTxTime; // Last time this Q was sent on one (but not necessarily all) interfaces + uDNS_QuestionInfo uDNS_info; + + // Client API fields: The client must set up these fields *before* calling mDNS_StartQuery() + mDNSInterfaceID InterfaceID; // Non-zero if you want to issue queries only on a single specific IP interface + mDNSAddr Target; // Non-zero if you want to direct queries to a specific unicast target address + mDNSIPPort TargetPort; // Must be set if Target is set + mDNSOpaque16 TargetQID; // Must be set if Target is set + domainname qname; + mDNSu16 qtype; + mDNSu16 qclass; + mDNSBool LongLived; // Set by client for calls to mDNS_StartQuery to indicate LLQs to unicast layer. + mDNSBool ExpectUnique; // Set by client if it's expecting unique RR(s) for this question, not shared RRs + mDNSBool ForceMCast; // Set by client to force mDNS query, even for apparently uDNS names + mDNSQuestionCallback *QuestionCallback; + void *QuestionContext; + }; + +typedef struct + { + // Client API fields: The client must set up name and InterfaceID *before* calling mDNS_StartResolveService() + // When the callback is invoked, ip, port, TXTlen and TXTinfo will have been filled in with the results learned from the network. + domainname name; + mDNSInterfaceID InterfaceID; // ID of the interface the response was received on + mDNSAddr ip; // Remote (destination) IP address where this service can be accessed + mDNSIPPort port; // Port where this service can be accessed + mDNSu16 TXTlen; + mDNSu8 TXTinfo[2048]; // Additional demultiplexing information (e.g. LPR queue name) + } ServiceInfo; + +// Note: Within an mDNSServiceInfoQueryCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Close(), mDNS_Execute() +typedef struct ServiceInfoQuery_struct ServiceInfoQuery; +typedef void mDNSServiceInfoQueryCallback(mDNS *const m, ServiceInfoQuery *query); +struct ServiceInfoQuery_struct + { + // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. + // No fields need to be set up by the client prior to calling mDNS_StartResolveService(); + // all required data is passed as parameters to that function. + // The ServiceInfoQuery structure memory is working storage for mDNSCore to discover the requested information + // and place it in the ServiceInfo structure. After the client has called mDNS_StopResolveService(), it may + // dispose of the ServiceInfoQuery structure while retaining the results in the ServiceInfo structure. + DNSQuestion qSRV; + DNSQuestion qTXT; + DNSQuestion qAv4; + DNSQuestion qAv6; + mDNSu8 GotSRV; + mDNSu8 GotTXT; + mDNSu8 GotADD; + mDNSu32 Answers; + ServiceInfo *info; + mDNSServiceInfoQueryCallback *ServiceInfoQueryCallback; + void *ServiceInfoQueryContext; + }; + +// *************************************************************************** +#if 0 +#pragma mark - NAT Traversal structures and constants +#endif + +#define NATMAP_INIT_RETRY (mDNSPlatformOneSecond / 4) // start at 250ms w/ exponential decay +#define NATMAP_MAX_RETRY mDNSPlatformOneSecond // back off to once per second +#define NATMAP_MAX_TRIES 3 // for max 3 tries +#define NATMAP_DEFAULT_LEASE (60 * 60) // lease life in seconds +#define NATMAP_VERS 0 +#define NATMAP_PORT 5351 +#define ADDR_REQUEST_PKTLEN 2 +#define ADDR_REPLY_PKTLEN 8 +#define PORTMAP_PKTLEN 12 +#define NATMAP_RESPONSE_MASK 0x80 + +typedef enum + { + NATOp_AddrRequest = 0, + NATOp_MapUDP = 1, + NATOp_MapTCP = 2 + } NATOp_t; + +enum + { + NATErr_None = 0, + NATErr_Vers = 1, + NATErr_Refused = 2, + NATErr_NetFail = 3, + NATErr_Res = 4, + NATErr_Opcode = 5 + }; + +typedef mDNSu16 NATErr_t; + +typedef enum + { + NATState_Init, + NATState_Request, + NATState_Established, + NATState_Legacy, + NATState_Error, + NATState_Refresh, + NATState_Deleted + } NATState_t; +// Note: we have no explicit "cancelled" state, where a service/interface is deregistered while we + // have an outstanding NAT request. This is conveyed by the "reg" pointer being set to NULL + +// Pass NULL for pkt on error (including timeout) +typedef void (*NATResponseHndlr)(NATTraversalInfo *n, mDNS *m, mDNSu8 *pkt, mDNSu16 len); + +struct NATTraversalInfo_struct + { + NATOp_t op; + NATResponseHndlr ReceiveResponse; + union { AuthRecord *RecordRegistration; ServiceRecordSet *ServiceRegistration; } reg; + mDNSIPPort PublicPort; + mDNSu8 request[PORTMAP_PKTLEN]; // buffer for request messages + int requestlen; // length of buffer used + mDNSs32 retry; // absolute time when we retry + mDNSs32 RetryInterval; // delta between time sent and retry + int ntries; + NATState_t state; + NATTraversalInfo *next; + }; + +// *************************************************************************** +#if 0 +#pragma mark - Main mDNS object, used to hold all the mDNS state +#endif + +typedef void mDNSCallback(mDNS *const m, mStatus result); + +#define CACHE_HASH_SLOTS 499 + +enum + { + mDNS_KnownBug_PhantomInterfaces = 1 + }; + +typedef struct + { + mDNSs32 nextevent; + DNSQuestion *ActiveQueries; //!!!KRS this should be a hashtable (hash on messageID) + DNSQuestion *CurrentQuery; // pointer to ActiveQueries list being examined in a loop. Functions that remove + // elements from the ActiveQueries list must update this pointer (if non-NULL) as necessary. + //!!!KRS do the same for registration lists + ServiceRecordSet *ServiceRegistrations; + AuthRecord *RecordRegistrations; + NATTraversalInfo *NATTraversals; + mDNSu16 NextMessageID; + DNSServer *Servers; // list of DNS servers + mDNSAddr Router; + mDNSAddr PrimaryIP; // Address of primary interface + mDNSAddr MappedPrimaryIP; // Cache of public address if PrimaryIP is behind a NAT + NATTraversalInfo *LLQNatInfo; // Nat port mapping to receive LLQ events + domainname ServiceRegDomain; // (going away w/ multi-user support) + struct uDNS_AuthInfo *AuthInfoList; // list of domains requiring authentication for updates. + uDNS_HostnameInfo *Hostnames; // List of registered hostnames + hostname metadata + DNSQuestion ReverseMap; // Reverse-map query to find static hostname for service target + mDNSBool ReverseMapActive; // Is above query active? + domainname StaticHostname; // Current answer to reverse-map query (above) + } uDNS_GlobalInfo; + +struct mDNS_struct + { + // Internal state fields. These hold the main internal state of mDNSCore; + // the client layer needn't be concerned with them. + // No fields need to be set up by the client prior to calling mDNS_Init(); + // all required data is passed as parameters to that function. + + mDNS_PlatformSupport *p; // Pointer to platform-specific data of indeterminite size + mDNSu32 KnownBugs; + mDNSBool CanReceiveUnicastOn5353; + mDNSBool AdvertiseLocalAddresses; + mStatus mDNSPlatformStatus; + mDNSIPPort UnicastPort4; + mDNSIPPort UnicastPort6; + mDNSCallback *MainCallback; + void *MainContext; + + // For debugging: To catch and report locking failures + mDNSu32 mDNS_busy; // Incremented between mDNS_Lock/mDNS_Unlock section + mDNSu32 mDNS_reentrancy; // Incremented when calling a client callback + mDNSu8 mDNS_shutdown; // Set when we're shutting down, allows us to skip some unnecessary steps + mDNSu8 lock_rrcache; // For debugging: Set at times when these lists may not be modified + mDNSu8 lock_Questions; + mDNSu8 lock_Records; + char MsgBuffer[80]; // Temp storage used while building error log messages + + // Task Scheduling variables + mDNSs32 timenow; // The time that this particular activation of the mDNS code started + mDNSs32 timenow_last; // The time the last time we ran + mDNSs32 timenow_adjust; // Correction applied if we ever discover time went backwards + mDNSs32 NextScheduledEvent; // Derived from values below + mDNSs32 SuppressSending; // Don't send *any* packets during this time + mDNSs32 NextCacheCheck; // Next time to refresh cache record before it expires + mDNSs32 NextScheduledQuery; // Next time to send query in its exponential backoff sequence + mDNSs32 NextScheduledProbe; // Next time to probe for new authoritative record + mDNSs32 NextScheduledResponse; // Next time to send authoritative record(s) in responses + mDNSs32 ExpectUnicastResponse; // Set when we send a query with the kDNSQClass_UnicastResponse bit set + mDNSs32 RandomQueryDelay; // For de-synchronization of query packets on the wire + mDNSs32 PktNum; // Unique sequence number assigned to each received packet + mDNSBool SendDeregistrations; // Set if we need to send deregistrations (immediately) + mDNSBool SendImmediateAnswers; // Set if we need to send answers (immediately -- or as soon as SuppressSending clears) + mDNSBool SleepState; // Set if we're sleeping (send no more packets) + + // These fields only required for mDNS Searcher... + DNSQuestion *Questions; // List of all registered questions, active and inactive + DNSQuestion *NewQuestions; // Fresh questions not yet answered from cache + DNSQuestion *CurrentQuestion; // Next question about to be examined in AnswerLocalQuestions() + DNSQuestion *LocalOnlyQuestions; // Questions with InterfaceID set to mDNSInterface_LocalOnly + DNSQuestion *NewLocalOnlyQuestions; // Fresh local-only questions not yet answered + mDNSu32 rrcache_size; // Total number of available cache entries + mDNSu32 rrcache_totalused; // Number of cache entries currently occupied + mDNSu32 rrcache_active; // Number of cache entries currently occupied by records that answer active questions + mDNSu32 rrcache_report; + CacheRecord *rrcache_free; + CacheRecord *rrcache_hash[CACHE_HASH_SLOTS]; + CacheRecord **rrcache_tail[CACHE_HASH_SLOTS]; + mDNSu32 rrcache_used[CACHE_HASH_SLOTS]; + + // Fields below only required for mDNS Responder... + domainlabel nicelabel; // Rich text label encoded using canonically precomposed UTF-8 + domainlabel hostlabel; // Conforms to RFC 1034 "letter-digit-hyphen" ARPANET host name rules + domainname MulticastHostname; // Fully Qualified "dot-local" Host Name, e.g. "Foo.local." + UTF8str255 HIHardware; + UTF8str255 HISoftware; + AuthRecord *ResourceRecords; + AuthRecord *DuplicateRecords; // Records currently 'on hold' because they are duplicates of existing records + AuthRecord *LocalOnlyRecords; // Local records registered with InterfaceID set to mDNSInterface_LocalOnly + AuthRecord *NewLocalOnlyRecords; // Fresh local-only records not yet delivered to local-only questions + mDNSBool DiscardLocalOnlyRecords;// Set when we have "remove" events we need to deliver to local-only questions + AuthRecord *CurrentRecord; // Next AuthRecord about to be examined + NetworkInterfaceInfo *HostInterfaces; + mDNSs32 ProbeFailTime; + mDNSu32 NumFailedProbes; + mDNSs32 SuppressProbes; + + // unicast-specific data + uDNS_GlobalInfo uDNS_info; + + // Fixed storage, to avoid creating large objects on the stack + DNSMessage imsg; // Incoming message received from wire + DNSMessage omsg; // Outgoing message we're building + LargeCacheRecord rec; // Resource Record extracted from received message + }; + +// *************************************************************************** +#if 0 +#pragma mark - Useful Static Constants +#endif + +extern const mDNSIPPort zeroIPPort; +extern const mDNSv4Addr zerov4Addr; +extern const mDNSv6Addr zerov6Addr; +extern const mDNSEthAddr zeroEthAddr; +extern const mDNSv4Addr onesIPv4Addr; +extern const mDNSv6Addr onesIPv6Addr; +extern const mDNSAddr zeroAddr; + +extern const mDNSInterfaceID mDNSInterface_Any; // Zero +extern const mDNSInterfaceID mDNSInterface_LocalOnly; // (mDNSInterfaceID)-1; + +extern const mDNSIPPort UnicastDNSPort; +extern const mDNSIPPort MulticastDNSPort; +extern const mDNSv4Addr AllDNSAdminGroup; +extern const mDNSv4Addr AllDNSLinkGroupv4; +extern const mDNSv6Addr AllDNSLinkGroupv6; +extern const mDNSAddr AllDNSLinkGroup_v4; +extern const mDNSAddr AllDNSLinkGroup_v6; + +extern const mDNSOpaque16 zeroID; +extern const mDNSOpaque16 QueryFlags; +extern const mDNSOpaque16 ResponseFlags; +extern const mDNSOpaque16 UpdateReqFlags; +extern const mDNSOpaque16 UpdateRespFlags; + +#define localdomain (*(const domainname *)"\x5local") + +// *************************************************************************** +#if 0 +#pragma mark - Inline functions +#endif + +#if (defined(_MSC_VER)) + #define mDNSinline static __inline +#elif ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 9))) + #define mDNSinline static inline +#endif + +// If we're not doing inline functions, then this header needs to have the extern declarations +#if !defined(mDNSinline) +extern mDNSu16 mDNSVal16(mDNSOpaque16 x); +extern mDNSu32 mDNSVal32(mDNSOpaque32 x); +extern mDNSOpaque16 mDNSOpaque16fromIntVal(mDNSu16 v); +extern mDNSOpaque32 mDNSOpaque32fromIntVal(mDNSu32 v); +#endif + +// If we're compiling the particular C file that instantiates our inlines, then we +// define "mDNSinline" (to empty string) so that we generate code in the following section +#if (!defined(mDNSinline) && mDNS_InstantiateInlines) +#define mDNSinline +#endif + +#ifdef mDNSinline + +mDNSinline mDNSs32 NonZeroTime(mDNSs32 t) { if (t) return(t); else return(1); } + +mDNSinline mDNSu16 mDNSVal16(mDNSOpaque16 x) { return((mDNSu16)((mDNSu16)x.b[0] << 8 | (mDNSu16)x.b[1])); } +mDNSinline mDNSu32 mDNSVal32(mDNSOpaque32 x) { return((mDNSu32)((mDNSu32)x.b[0] << 24 | (mDNSu32)x.b[1] << 16 | (mDNSu32)x.b[2] << 8 | (mDNSu32)x.b[3])); } + +mDNSinline mDNSOpaque16 mDNSOpaque16fromIntVal(mDNSu16 v) + { + mDNSOpaque16 x; + x.b[0] = (mDNSu8)(v >> 8); + x.b[1] = (mDNSu8)(v & 0xFF); + return(x); + } + +mDNSinline mDNSOpaque32 mDNSOpaque32fromIntVal(mDNSu32 v) + { + mDNSOpaque32 x; + x.b[0] = (mDNSu8) (v >> 24) ; + x.b[1] = (mDNSu8)((v >> 16) & 0xFF); + x.b[2] = (mDNSu8)((v >> 8 ) & 0xFF); + x.b[3] = (mDNSu8)((v ) & 0xFF); + return x; + } + +#endif + +// *************************************************************************** +#if 0 +#pragma mark - Main Client Functions +#endif + +// Every client should call mDNS_Init, passing in storage for the mDNS object and the mDNS_PlatformSupport object. +// +// Clients that are only advertising services should use mDNS_Init_NoCache and mDNS_Init_ZeroCacheSize. +// Clients that plan to perform queries (mDNS_StartQuery, mDNS_StartBrowse, mDNS_StartResolveService, etc.) +// need to provide storage for the resource record cache, or the query calls will return 'mStatus_NoCache'. +// The rrcachestorage parameter is the address of memory for the resource record cache, and +// the rrcachesize parameter is the number of entries in the CacheRecord array passed in. +// (i.e. the size of the cache memory needs to be sizeof(CacheRecord) * rrcachesize). +// OS X 10.3 Panther uses an initial cache size of 64 entries, and then mDNSCore sends an +// mStatus_GrowCache message if it needs more. +// +// Most clients should use mDNS_Init_AdvertiseLocalAddresses. This causes mDNSCore to automatically +// create the correct address records for all the hosts interfaces. If you plan to advertise +// services being offered by the local machine, this is almost always what you want. +// There are two cases where you might use mDNS_Init_DontAdvertiseLocalAddresses: +// 1. A client-only device, that browses for services but doesn't advertise any of its own. +// 2. A proxy-registration service, that advertises services being offered by other machines, and takes +// the appropriate steps to manually create the correct address records for those other machines. +// In principle, a proxy-like registration service could manually create address records for its own machine too, +// but this would be pointless extra effort when using mDNS_Init_AdvertiseLocalAddresses does that for you. +// +// When mDNS has finished setting up the client's callback is called +// A client can also spin and poll the mDNSPlatformStatus field to see when it changes from mStatus_Waiting to mStatus_NoError +// +// Call mDNS_Close to tidy up before exiting +// +// Call mDNS_Register with a completed AuthRecord object to register a resource record +// If the resource record type is kDNSRecordTypeUnique (or kDNSknownunique) then if a conflicting resource record is discovered, +// the resource record's mDNSRecordCallback will be called with error code mStatus_NameConflict. The callback should deregister +// the record, and may then try registering the record again after picking a new name (e.g. by automatically appending a number). +// Following deregistration, the RecordCallback will be called with result mStatus_MemFree to signal that it is safe to deallocate +// the record's storage (memory must be freed asynchronously to allow for goodbye packets and dynamic update deregistration). +// +// Call mDNS_StartQuery to initiate a query. mDNS will proceed to issue Multicast DNS query packets, and any time a response +// is received containing a record which matches the question, the DNSQuestion's mDNSAnswerCallback function will be called +// Call mDNS_StopQuery when no more answers are required +// +// Care should be taken on multi-threaded or interrupt-driven environments. +// The main mDNS routines call mDNSPlatformLock() on entry and mDNSPlatformUnlock() on exit; +// each platform layer needs to implement these appropriately for its respective platform. +// For example, if the support code on a particular platform implements timer callbacks at interrupt time, then +// mDNSPlatformLock/Unlock need to disable interrupts or do similar concurrency control to ensure that the mDNS +// code is not entered by an interrupt-time timer callback while in the middle of processing a client call. + +extern mStatus mDNS_Init (mDNS *const m, mDNS_PlatformSupport *const p, + CacheRecord *rrcachestorage, mDNSu32 rrcachesize, + mDNSBool AdvertiseLocalAddresses, + mDNSCallback *Callback, void *Context); +// See notes above on use of NoCache/ZeroCacheSize +#define mDNS_Init_NoCache mDNSNULL +#define mDNS_Init_ZeroCacheSize 0 +// See notes above on use of Advertise/DontAdvertiseLocalAddresses +#define mDNS_Init_AdvertiseLocalAddresses mDNStrue +#define mDNS_Init_DontAdvertiseLocalAddresses mDNSfalse +#define mDNS_Init_NoInitCallback mDNSNULL +#define mDNS_Init_NoInitCallbackContext mDNSNULL + +extern void mDNS_GrowCache (mDNS *const m, CacheRecord *storage, mDNSu32 numrecords); +extern void mDNS_Close (mDNS *const m); +extern mDNSs32 mDNS_Execute (mDNS *const m); + +extern mStatus mDNS_Register (mDNS *const m, AuthRecord *const rr); +extern mStatus mDNS_Update (mDNS *const m, AuthRecord *const rr, mDNSu32 newttl, + const mDNSu16 newrdlength, RData *const newrdata, mDNSRecordUpdateCallback *Callback); +extern mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr); + +extern mStatus mDNS_StartQuery(mDNS *const m, DNSQuestion *const question); +extern mStatus mDNS_StopQuery (mDNS *const m, DNSQuestion *const question); +extern mStatus mDNS_Reconfirm (mDNS *const m, CacheRecord *const cacherr); +extern mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr); +extern mDNSs32 mDNS_TimeNow(const mDNS *const m); + +// *************************************************************************** +#if 0 +#pragma mark - Platform support functions that are accessible to the client layer too +#endif + +extern mDNSs32 mDNSPlatformOneSecond; + +// *************************************************************************** +#if 0 +#pragma mark - General utility and helper functions +#endif + +// mDNS_RegisterService is a single call to register the set of resource records associated with a given named service. +// +// mDNS_StartResolveService is single call which is equivalent to multiple calls to mDNS_StartQuery, +// to find the IP address, port number, and demultiplexing information for a given named service. +// As with mDNS_StartQuery, it executes asynchronously, and calls the ServiceInfoQueryCallback when the answer is +// found. After the service is resolved, the client should call mDNS_StopResolveService to complete the transaction. +// The client can also call mDNS_StopResolveService at any time to abort the transaction. +// +// mDNS_AddRecordToService adds an additional record to a Service Record Set. This record may be deregistered +// via mDNS_RemoveRecordFromService, or by deregistering the service. mDNS_RemoveRecordFromService is passed a +// callback to free the memory associated with the extra RR when it is safe to do so. The ExtraResourceRecord +// object can be found in the record's context pointer. + +// mDNS_GetBrowseDomains is a special case of the mDNS_StartQuery call, where the resulting answers +// are a list of PTR records indicating (in the rdata) domains that are recommended for browsing. +// After getting the list of domains to browse, call mDNS_StopQuery to end the search. +// mDNS_GetDefaultBrowseDomain returns the name of the domain that should be highlighted by default. +// +// mDNS_GetRegistrationDomains and mDNS_GetDefaultRegistrationDomain are the equivalent calls to get the list +// of one or more domains that should be offered to the user as choices for where they may register their service, +// and the default domain in which to register in the case where the user has made no selection. + +extern void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID, + mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, mDNSRecordCallback Callback, void *Context); + +extern mStatus mDNS_RegisterService (mDNS *const m, ServiceRecordSet *sr, + const domainlabel *const name, const domainname *const type, const domainname *const domain, + const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen, + AuthRecord *SubTypes, mDNSu32 NumSubTypes, + const mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context); +extern mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl); +extern mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, mDNSRecordCallback MemFreeCallback, void *Context); +extern mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordSet *const sr, const domainlabel *newname); +extern mStatus mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr); + +extern mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const rr, + const domainlabel *const name, const domainname *const type, const domainname *const domain, + const domainname *const host, + const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context); +#define mDNS_DeregisterNoSuchService mDNS_Deregister + +extern mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question, + const domainname *const srv, const domainname *const domain, + const mDNSInterfaceID InterfaceID, mDNSBool ForceMCast, mDNSQuestionCallback *Callback, void *Context); +#define mDNS_StopBrowse mDNS_StopQuery + +extern mStatus mDNS_StartResolveService(mDNS *const m, ServiceInfoQuery *query, ServiceInfo *info, mDNSServiceInfoQueryCallback *Callback, void *Context); +extern void mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *query); + +typedef enum + { + mDNS_DomainTypeBrowse = 0, + mDNS_DomainTypeBrowseDefault = 1, + mDNS_DomainTypeRegistration = 2, + mDNS_DomainTypeRegistrationDefault = 3, + mDNS_DomainTypeBrowseLegacy = 4, + } mDNS_DomainType; + +extern mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const domainname *dom, + const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context); +#define mDNS_StopGetDomains mDNS_StopQuery +extern mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname); +#define mDNS_StopAdvertiseDomains mDNS_Deregister + +// *************************************************************************** +#if 0 +#pragma mark - DNS name utility functions +#endif + +// In order to expose the full capabilities of the DNS protocol (which allows any arbitrary eight-bit values +// in domain name labels, including unlikely characters like ascii nulls and even dots) all the mDNS APIs +// work with DNS's native length-prefixed strings. For convenience in C, the following utility functions +// are provided for converting between C's null-terminated strings and DNS's length-prefixed strings. + +// Assignment +// A simple C structure assignment of a domainname can cause a protection fault by accessing unmapped memory, +// because that object is defined to be 256 bytes long, but not all domainname objects are truly the full size. +// This macro uses mDNSPlatformMemCopy() to make sure it only touches the actual bytes that are valid. +#define AssignDomainName(DST, SRC) mDNSPlatformMemCopy((SRC).c, (DST).c, DomainNameLength(&(SRC))) + +// Comparison functions +extern mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b); +extern mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2); +extern mDNSBool IsLocalDomain(const domainname *d); // returns true for domains that by default should be looked up using link-local multicast + +// Get total length of domain name, in native DNS format, including terminal root label +// (e.g. length of "com." is 5 (length byte, three data bytes, final zero) +extern mDNSu16 DomainNameLength(const domainname *const name); + +// Append functions to append one or more labels to an existing native format domain name: +// AppendLiteralLabelString adds a single label from a literal C string, with no escape character interpretation. +// AppendDNSNameString adds zero or more labels from a C string using conventional DNS dots-and-escaping interpretation +// AppendDomainLabel adds a single label from a native format domainlabel +// AppendDomainName adds zero or more labels from a native format domainname +extern mDNSu8 *AppendLiteralLabelString(domainname *const name, const char *cstr); +extern mDNSu8 *AppendDNSNameString (domainname *const name, const char *cstr); +extern mDNSu8 *AppendDomainLabel (domainname *const name, const domainlabel *const label); +extern mDNSu8 *AppendDomainName (domainname *const name, const domainname *const append); + +// Convert from null-terminated string to native DNS format: +// The DomainLabel form makes a single label from a literal C string, with no escape character interpretation. +// The DomainName form makes native format domain name from a C string using conventional DNS interpretation: +// dots separate labels, and within each label, '\.' represents a literal dot, '\\' represents a literal +// backslash and backslash with three decimal digits (e.g. \000) represents an arbitrary byte value. +extern mDNSBool MakeDomainLabelFromLiteralString(domainlabel *const label, const char *cstr); +extern mDNSu8 *MakeDomainNameFromDNSNameString (domainname *const name, const char *cstr); + +// Convert native format domainlabel or domainname back to C string format +// IMPORTANT: +// When using ConvertDomainLabelToCString, the target buffer must be MAX_ESCAPED_DOMAIN_LABEL (254) bytes long +// to guarantee there will be no buffer overrun. It is only safe to use a buffer shorter than this in rare cases +// where the label is known to be constrained somehow (for example, if the label is known to be either "_tcp" or "_udp"). +// Similarly, when using ConvertDomainNameToCString, the target buffer must be MAX_ESCAPED_DOMAIN_NAME (1005) bytes long. +// See definitions of MAX_ESCAPED_DOMAIN_LABEL and MAX_ESCAPED_DOMAIN_NAME for more detailed explanation. +extern char *ConvertDomainLabelToCString_withescape(const domainlabel *const name, char *cstr, char esc); +#define ConvertDomainLabelToCString_unescaped(D,C) ConvertDomainLabelToCString_withescape((D), (C), 0) +#define ConvertDomainLabelToCString(D,C) ConvertDomainLabelToCString_withescape((D), (C), '\\') +extern char *ConvertDomainNameToCString_withescape(const domainname *const name, char *cstr, char esc); +#define ConvertDomainNameToCString_unescaped(D,C) ConvertDomainNameToCString_withescape((D), (C), 0) +#define ConvertDomainNameToCString(D,C) ConvertDomainNameToCString_withescape((D), (C), '\\') + +extern void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], domainlabel *const hostlabel); + +extern mDNSu8 *ConstructServiceName(domainname *const fqdn, const domainlabel *name, const domainname *type, const domainname *const domain); +extern mDNSBool DeconstructServiceName(const domainname *const fqdn, domainlabel *const name, domainname *const type, domainname *const domain); + +// Note: Some old functions have been replaced by more sensibly-named versions. +// You can uncomment the hash-defines below if you don't want to have to change your source code right away. +// When updating your code, note that (unlike the old versions) *all* the new routines take the target object +// as their first parameter. +//#define ConvertCStringToDomainName(SRC,DST) MakeDomainNameFromDNSNameString((DST),(SRC)) +//#define ConvertCStringToDomainLabel(SRC,DST) MakeDomainLabelFromLiteralString((DST),(SRC)) +//#define AppendStringLabelToName(DST,SRC) AppendLiteralLabelString((DST),(SRC)) +//#define AppendStringNameToName(DST,SRC) AppendDNSNameString((DST),(SRC)) +//#define AppendDomainLabelToName(DST,SRC) AppendDomainLabel((DST),(SRC)) +//#define AppendDomainNameToName(DST,SRC) AppendDomainName((DST),(SRC)) + +// *************************************************************************** +#if 0 +#pragma mark - Other utility functions and macros +#endif + +extern mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg); +extern mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...) IS_A_PRINTF_STYLE_FUNCTION(3,4); +extern mDNSu32 NumCacheRecordsForInterfaceID(const mDNS *const m, mDNSInterfaceID id); +extern char *DNSTypeName(mDNSu16 rrtype); +extern char *GetRRDisplayString_rdb(const ResourceRecord *rr, RDataBody *rd, char *buffer); +#define RRDisplayString(m, rr) GetRRDisplayString_rdb(rr, &(rr)->rdata->u, (m)->MsgBuffer) +#define ARDisplayString(m, rr) GetRRDisplayString_rdb(&(rr)->resrec, &(rr)->resrec.rdata->u, (m)->MsgBuffer) +#define CRDisplayString(m, rr) GetRRDisplayString_rdb(&(rr)->resrec, &(rr)->resrec.rdata->u, (m)->MsgBuffer) +extern mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2); +extern void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText); +extern mDNSBool IsPrivateV4Addr(mDNSAddr *addr); // returns true for RFC1918 private addresses + +#define mDNSSameIPv4Address(A,B) ((A).NotAnInteger == (B).NotAnInteger) +#define mDNSSameIPv6Address(A,B) ((A).l[0] == (B).l[0] && (A).l[1] == (B).l[1] && (A).l[2] == (B).l[2] && (A).l[3] == (B).l[3]) +#define mDNSSameEthAddress(A,B) ((A)->w[0] == (B)->w[0] && (A)->w[1] == (B)->w[1] && (A)->w[2] == (B)->w[2]) + +#define mDNSIPv4AddressIsZero(A) mDNSSameIPv4Address((A), zerov4Addr) +#define mDNSIPv6AddressIsZero(A) mDNSSameIPv6Address((A), zerov6Addr) + +#define mDNSIPv4AddressIsOnes(A) mDNSSameIPv4Address((A), onesIPv4Addr) +#define mDNSIPv6AddressIsOnes(A) mDNSSameIPv6Address((A), onesIPv6Addr) + +#define mDNSAddressIsAllDNSLinkGroup(X) ( \ + ((X)->type == mDNSAddrType_IPv4 && mDNSSameIPv4Address((X)->ip.v4, AllDNSLinkGroupv4)) || \ + ((X)->type == mDNSAddrType_IPv6 && mDNSSameIPv6Address((X)->ip.v6, AllDNSLinkGroupv6)) ) + +#define mDNSAddressIsZero(X) ( \ + ((X)->type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsZero((X)->ip.v4)) || \ + ((X)->type == mDNSAddrType_IPv6 && mDNSIPv6AddressIsZero((X)->ip.v6)) ) + +#define mDNSAddressIsValidNonZero(X) ( \ + ((X)->type == mDNSAddrType_IPv4 && !mDNSIPv4AddressIsZero((X)->ip.v4)) || \ + ((X)->type == mDNSAddrType_IPv6 && !mDNSIPv6AddressIsZero((X)->ip.v6)) ) + +#define mDNSAddressIsOnes(X) ( \ + ((X)->type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsOnes((X)->ip.v4)) || \ + ((X)->type == mDNSAddrType_IPv6 && mDNSIPv6AddressIsOnes((X)->ip.v6)) ) + +#define mDNSAddressIsValid(X) ( \ + ((X)->type == mDNSAddrType_IPv4) ? !(mDNSIPv4AddressIsZero((X)->ip.v4) || mDNSIPv4AddressIsOnes((X)->ip.v4)) : \ + ((X)->type == mDNSAddrType_IPv6) ? !(mDNSIPv6AddressIsZero((X)->ip.v6) || mDNSIPv6AddressIsOnes((X)->ip.v6)) : mDNSfalse) + + +// *************************************************************************** +#if 0 +#pragma mark - Authentication Support +#endif + +#define HMAC_LEN 64 +#define HMAC_IPAD 0x36 +#define HMAC_OPAD 0x5c +#define MD5_LEN 16 + +// padded keys for inned/outer hash rounds +typedef struct + { + mDNSu8 ipad[HMAC_LEN]; + mDNSu8 opad[HMAC_LEN]; + } HMAC_Key; + +// Internal data structure to maintain authentication information for an update domain +typedef struct uDNS_AuthInfo + { + domainname zone; + domainname keyname; + HMAC_Key key; + struct uDNS_AuthInfo *next; + } uDNS_AuthInfo; + +// Unicast DNS and Dynamic Update specific Client Calls +// +// mDNS_SetSecretForZone tells the core to authenticate (via TSIG with an HMAC_MD5 hash of the shared secret) +// when dynamically updating a given zone (and its subdomains). The key used in authentication must be in +// domain name format. The shared secret must be a base64 encoded string with the base64 parameter set to +// true, or binary data with the base64 parameter set to false. The length is the size of the secret in +// bytes. (A minimum size of 16 bytes (128 bits) is recommended for an MD5 hash as per RFC 2485). +// Calling this routine multiple times for a zone replaces previously entered values. Call with a NULL key +// to dissable authentication for the zone. + +extern mStatus mDNS_SetSecretForZone(mDNS *m, const domainname *zone, const domainname *key, const mDNSu8 *sharedSecret, mDNSu32 ssLen, mDNSBool base64); + +// Hostname/Unicast Interface Configuration + +// All hostnames advertised point to a single IP address, set via SetPrimaryInterfaceInfo. Invoking this routine +// updates all existing hostnames to point to the new address. + +// A hostname is added via AddDynDNSHostName, which points to the primary interface's IP address. + +// The status callback is invoked to convey success or failure codes - the callback should not modify the AuthRecord or free memory. +// Added hostnames may be removed (deregistered) via mDNS_RemoveDynDNSHostName. + +// Host domains added prior to specification of the primary interface address and computer name will be deferred until +// these values are initialized. + +// When routable V4 interfaces are added or removed, mDNS_UpdateLLQs should be called to re-estabish LLQs in case the +// destination address for events (i.e. the route) has changed. For performance reasons, the caller is responsible for +// batching changes, e.g. calling the routine only once if multiple interfaces are simultanously removed or added. + +// DNS servers used to resolve unicast queries are specified by mDNS_AddDNSServer, and may later be removed via mDNS_DeleteDNSServers. +// For "split" DNS configurations, in which queries for different domains are sent to different servers (e.g. VPN and external), +// a domain may be associated with a DNS server. For standard configurations, specify the root label (".") or NULL. + +extern void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext); +extern void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn); +extern void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *addr, const mDNSAddr *router); +extern void mDNS_UpdateLLQs(mDNS *m); +extern void mDNS_AddDNSServer(mDNS *const m, const mDNSAddr *dnsAddr, const domainname *domain); +extern void mDNS_DeleteDNSServers(mDNS *const m); + +// Routines called by the core, exported by DNSDigest.c + +// Convert a base64 encoded key into a binary byte stream +extern mDNSs32 DNSDigest_Base64ToBin(const char *src, mDNSu8 *target, mDNSu32 targsize); + +// Convert an arbitrary binary key (of any length) into an HMAC key (stored in AuthInfo struct) +extern void DNSDigest_ConstructHMACKey(uDNS_AuthInfo *info, const mDNSu8 *key, mDNSu32 len); + +// sign a DNS message. The message must be compete, with all values in network byte order. end points to the end +// of the message, and is modified by this routine. numAdditionals is a pointer to the number of additional +// records in HOST byte order, which is incremented upon successful completion of this routine. The function returns +// the new end pointer on success, and NULL on failure. +extern mDNSu8 *DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, mDNSu16 *numAdditionals, uDNS_AuthInfo *info); + +// *************************************************************************** +#if 0 +#pragma mark - PlatformSupport interface +#endif + +// This section defines the interface to the Platform Support layer. +// Normal client code should not use any of types defined here, or directly call any of the functions defined here. +// The definitions are placed here because sometimes clients do use these calls indirectly, via other supported client operations. +// For example, AssignDomainName is a macro defined using mDNSPlatformMemCopy() + +// Every platform support module must provide the following functions. +// mDNSPlatformInit() typically opens a communication endpoint, and starts listening for mDNS packets. +// When Setup is complete, the platform support layer calls mDNSCoreInitComplete(). +// mDNSPlatformSendUDP() sends one UDP packet +// When a packet is received, the PlatformSupport code calls mDNSCoreReceive() +// mDNSPlatformClose() tidies up on exit +// +// Note: mDNSPlatformMemAllocate/mDNSPlatformMemFree are only required for handling oversized resource records and unicast DNS. +// If your target platform has a well-defined specialized application, and you know that all the records it uses +// are InlineCacheRDSize or less, then you can just make a simple mDNSPlatformMemAllocate() stub that always returns +// NULL. InlineCacheRDSize is a compile-time constant, which is set by default to 64. If you need to handle records +// a little larger than this and you don't want to have to implement run-time allocation and freeing, then you +// can raise the value of this constant to a suitable value (at the expense of increased memory usage). +// +// USE CAUTION WHEN CALLING mDNSPlatformRawTime: The m->timenow_adjust correction factor needs to be added +// Generally speaking: +// Code that's protected by the main mDNS lock should just use the m->timenow value +// Code outside the main mDNS lock should use mDNS_TimeNow(m) to get properly adjusted time +// +// mDNSPlatformUTC returns the time, in seconds, since Jan 1st 1970 UTC and is required for generating TSIG records + +extern mStatus mDNSPlatformInit (mDNS *const m); +extern void mDNSPlatformClose (mDNS *const m); +extern mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end, +mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport); + +extern void mDNSPlatformLock (const mDNS *const m); +extern void mDNSPlatformUnlock (const mDNS *const m); + +extern void mDNSPlatformStrCopy (const void *src, void *dst); +extern mDNSu32 mDNSPlatformStrLen (const void *src); +extern void mDNSPlatformMemCopy (const void *src, void *dst, mDNSu32 len); +extern mDNSBool mDNSPlatformMemSame (const void *src, const void *dst, mDNSu32 len); +extern void mDNSPlatformMemZero ( void *dst, mDNSu32 len); +extern void * mDNSPlatformMemAllocate (mDNSu32 len); +extern void mDNSPlatformMemFree (void *mem); +extern mDNSu32 mDNSPlatformRandomSeed (void); +extern mStatus mDNSPlatformTimeInit (void); +extern mDNSs32 mDNSPlatformRawTime (void); +extern mDNSs32 mDNSPlatformUTC (void); + +// Platform support modules should provide the following functions to map between opaque interface IDs +// and interface indexes in order to support the DNS-SD API. If your target platform does not support +// multiple interfaces and/or does not support the DNS-SD API, these functions can be empty. +extern mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(const mDNS *const m, mDNSu32 index); +extern mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(const mDNS *const m, mDNSInterfaceID id); + +// Every platform support module must provide the following functions if it is to support unicast DNS +// and Dynamic Update. +// All TCP socket operations implemented by the platform layer MUST NOT BLOCK. +// mDNSPlatformTCPConnect initiates a TCP connection with a peer, adding the socket descriptor to the +// main event loop. The return value indicates whether the connection succeeded, failed, or is pending +// (i.e. the call would block.) On return, the descriptor parameter is set to point to the connected socket. +// The TCPConnectionCallback is subsequently invoked when the connection +// completes (in which case the ConnectionEstablished parameter is true), or data is available for +// reading on the socket (indicated by the ConnectionEstablished parameter being false.) If the connection +// asynchronously fails, the TCPConnectionCallback should be invoked as usual, with the error being +// returned in subsequent calls to PlatformReadTCP or PlatformWriteTCP. (This allows for platforms +// with limited asynchronous error detection capabilities.) PlatformReadTCP and PlatformWriteTCP must +// return the number of bytes read/written, 0 if the call would block, and -1 if an error. +// PlatformTCPCloseConnection must close the connection to the peer and remove the descriptor from the +// event loop. CloseConnectin may be called at any time, including in a ConnectionCallback. + +typedef void (*TCPConnectionCallback)(int sd, void *context, mDNSBool ConnectionEstablished); +extern mStatus mDNSPlatformTCPConnect(const mDNSAddr *dst, mDNSOpaque16 dstport, mDNSInterfaceID InterfaceID, + TCPConnectionCallback callback, void *context, int *descriptor); +extern void mDNSPlatformTCPCloseConnection(int sd); +extern int mDNSPlatformReadTCP(int sd, void *buf, int buflen); +extern int mDNSPlatformWriteTCP(int sd, const char *msg, int len); + +// Platforms that support unicast browsing and dynamic update registration for clients who do not specify a domain +// in browse/registration calls must implement these routines to get the "default" browse/registration list. +// The Get() functions must return a linked list of DNameListElem structs, allocated via mDNSPlatformMemAllocate. +// Platforms may implement the Get() calls via the mDNS_CopyDNameList() helper routine. +// Callers should free lists obtained via the Get() calls with th mDNS_FreeDNameList routine, provided by the core. + +typedef struct DNameListElem + { + domainname name; + struct DNameListElem *next; + } DNameListElem; + +extern DNameListElem *mDNSPlatformGetSearchDomainList(void); +extern DNameListElem *mDNSPlatformGetRegDomainList(void); + +// Helper functions provided by the core +extern DNameListElem *mDNS_CopyDNameList(const DNameListElem *orig); +extern void mDNS_FreeDNameList(DNameListElem *list); + +#ifdef _LEGACY_NAT_TRAVERSAL_ +// Support for legacy NAT traversal protocols, implemented by the platform layer and callable by the core. + +#define DYN_PORT_MIN 49152 // ephemeral port range +#define DYN_PORT_MAX 65535 +#define LEGACY_NATMAP_MAX_TRIES 4 // if our desired mapping is taken, how many times we try mapping to a random port + +extern mStatus LNT_GetPublicIP(mDNSOpaque32 *ip); +extern mStatus LNT_MapPort(mDNSIPPort priv, mDNSIPPort pub, mDNSBool tcp); +extern mStatus LNT_UnmapPort(mDNSIPPort PubPort, mDNSBool tcp); +#endif // _LEGACY_NAT_TRAVERSAL_ + +// The core mDNS code provides these functions, for the platform support code to call at appropriate times +// +// mDNS_SetFQDN() is called once on startup (typically from mDNSPlatformInit()) +// and then again on each subsequent change of the host name. +// +// mDNS_RegisterInterface() is used by the platform support layer to inform mDNSCore of what +// physical and/or logical interfaces are available for sending and receiving packets. +// Typically it is called on startup for each available interface, but register/deregister may be +// called again later, on multiple occasions, to inform the core of interface configuration changes. +// If set->Advertise is set non-zero, then mDNS_RegisterInterface() also registers the standard +// resource records that should be associated with every publicised IP address/interface: +// -- Name-to-address records (A/AAAA) +// -- Address-to-name records (PTR) +// -- Host information (HINFO) +// IMPORTANT: The specified mDNSInterfaceID MUST NOT be 0, -1, or -2; these values have special meaning +// mDNS_RegisterInterface does not result in the registration of global hostnames via dynamic update - +// see mDNS_SetPrimaryInterfaceInfo, mDNS_AddDynDNSHostName, etc. for this purpose. +// Note that the set may be deallocated immediately after it is deregistered via mDNS_DeegisterInterface. +// +// mDNS_RegisterDNS() is used by the platform support layer to provide the core with the addresses of +// available domain name servers for unicast queries/updates. RegisterDNS() should be called once for +// each name server, typically at startup, or when a new name server becomes available. DeregiterDNS() +// must be called whenever a registered name server becomes unavailable. DeregisterDNSList deregisters +// all registered servers. mDNS_DNSRegistered() returns true if one or more servers are registered in the core. +// +// mDNSCoreInitComplete() is called when the platform support layer is finished. +// Typically this is at the end of mDNSPlatformInit(), but may be later +// (on platforms like OT that allow asynchronous initialization of the networking stack). +// +// mDNSCoreReceive() is called when a UDP packet is received +// +// mDNSCoreMachineSleep() is called when the machine sleeps or wakes +// (This refers to heavyweight laptop-style sleep/wake that disables network access, +// not lightweight second-by-second CPU power management modes.) + +extern void mDNS_SetFQDN(mDNS *const m); +extern mStatus mDNS_RegisterInterface (mDNS *const m, NetworkInterfaceInfo *set); +extern void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set); +extern void mDNSCoreInitComplete(mDNS *const m, mStatus result); +extern void mDNSCoreReceive(mDNS *const m, void *const msg, const mDNSu8 *const end, + const mDNSAddr *const srcaddr, const mDNSIPPort srcport, + const mDNSAddr *const dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID); +extern void mDNSCoreMachineSleep(mDNS *const m, mDNSBool wake); + +extern mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip); + +// *************************************************************************** +#if 0 +#pragma mark - Compile-Time assertion checks +#endif + +// Some C compiler cleverness. We can make the compiler check certain things for +// us, and report compile-time errors if anything is wrong. The usual way to do +// this would be to use a run-time "if" statement, but then you don't find out +// what's wrong until you run the software. This way, if the assertion condition +// is false, the array size is negative, and the complier complains immediately. + +struct mDNS_CompileTimeAssertionChecks + { + // Check that the compiler generated our on-the-wire packet format structure definitions + // properly packed, without adding padding bytes to align fields on 32-bit or 64-bit boundaries. + char assert0[(sizeof(rdataSRV) == 262 ) ? 1 : -1]; + char assert1[(sizeof(DNSMessageHeader) == 12 ) ? 1 : -1]; + char assert2[(sizeof(DNSMessage) == 12+AbsoluteMaxDNSMessageData) ? 1 : -1]; + char assert3[(sizeof(mDNSs8) == 1 ) ? 1 : -1]; + char assert4[(sizeof(mDNSu8) == 1 ) ? 1 : -1]; + char assert5[(sizeof(mDNSs16) == 2 ) ? 1 : -1]; + char assert6[(sizeof(mDNSu16) == 2 ) ? 1 : -1]; + char assert7[(sizeof(mDNSs32) == 4 ) ? 1 : -1]; + char assert8[(sizeof(mDNSu32) == 4 ) ? 1 : -1]; + char assert9[(sizeof(mDNSOpaque16) == 2 ) ? 1 : -1]; + char assertA[(sizeof(mDNSOpaque32) == 4 ) ? 1 : -1]; + char assertB[(sizeof(mDNSOpaque128) == 16 ) ? 1 : -1]; + }; + +// *************************************************************************** + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/mDNSCore/uDNS.c b/mDNSCore/uDNS.c index 90fd363..b2f8f3f 100755 --- a/mDNSCore/uDNS.c +++ b/mDNSCore/uDNS.c @@ -3,8 +3,6 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -25,11 +23,327 @@ Change History (most recent first): $Log: uDNS.c,v $ +Revision 1.151 2004/12/13 21:45:08 ksekar +uDNS_DeregisterService should return NoError if called twice (to follow mDNS behavior expected by daemon layer) + +Revision 1.150 2004/12/13 20:42:41 ksekar +Fixed LogMsg + +Revision 1.149 2004/12/13 18:10:03 ksekar +Fixed LogMsg + +Revision 1.148 2004/12/13 01:18:04 ksekar +Fixed unused variable warning for non-debug builds + +Revision 1.147 2004/12/12 23:51:42 ksekar + Wide-area registrations should fallback to using DHCP hostname as target + +Revision 1.146 2004/12/12 23:30:40 ksekar + Extra RRs not properly unlinked when parent service registration fails + +Revision 1.145 2004/12/12 22:56:29 ksekar + Need to properly handle duplicate long-lived queries + +Revision 1.144 2004/12/11 20:55:29 ksekar + Clean up registration state machines + +Revision 1.143 2004/12/10 01:21:27 cheshire + Get rid of "LLQ Responses over TCP not currently supported" message + +Revision 1.142 2004/12/08 02:03:31 ksekar + Looping on NAT Traversal error - check for +NULL RR on error + +Revision 1.141 2004/12/07 01:39:28 cheshire +Don't fail if the same server is responsible for more than one domain +(e.g. the same DNS server may be responsible for both apple.com. and 17.in-addr.arpa.) + +Revision 1.140 2004/12/06 21:15:22 ksekar + mDNSResponder crashed in CheckServiceRegistrations + +Revision 1.139 2004/12/06 19:08:03 cheshire +Add clarifying comment -- CountLabels() excludes the final root label. + +Revision 1.138 2004/12/06 01:45:54 ksekar +Correct wording in LogMsg + +Revision 1.137 2004/12/03 20:40:35 ksekar + Looping on NAT Traversal error + +Revision 1.136 2004/12/03 07:20:50 ksekar + Wide-Area: Registration of large TXT record fails + +Revision 1.135 2004/12/03 05:18:33 ksekar + mDNSResponder needs to return more specific TSIG errors + +Revision 1.134 2004/12/02 20:03:49 ksekar + Rendezvous still publishes wide-area domains even after switching to a local subnet + +Revision 1.133 2004/12/02 18:37:52 ksekar + Registering with port number zero should not create a port mapping + +Revision 1.132 2004/12/01 20:57:19 ksekar + Wide Area Rendezvous must be split-DNS aware + +Revision 1.131 2004/12/01 19:59:27 cheshire + Crash in mDNSPlatformTCPConnect +If a TCP response has the TC bit set, don't respond by just trying another TCP connection + +Revision 1.130 2004/12/01 02:43:23 cheshire +Don't call StatusCallback if function pointer is null + +Revision 1.129 2004/11/30 23:51:06 cheshire +Remove double semicolons + +Revision 1.128 2004/11/25 01:48:30 ksekar + Logging into VPN does not trigger registration of address record + +Revision 1.127 2004/11/25 01:41:36 ksekar +Changed unnecessary LogMsgs to debugfs + +Revision 1.126 2004/11/23 23:54:17 ksekar + Wide-Area DNSServiceRegisterRecord() failures +can crash mDNSResponder + +Revision 1.125 2004/11/23 04:16:48 cheshire +Removed receiveMsg() routine. + +Revision 1.124 2004/11/23 04:06:51 cheshire +Get rid of floating point constant -- in a small embedded device, bringing in all +the floating point libraries just to halve an integer value is a bit too heavyweight. + +Revision 1.123 2004/11/22 17:16:20 ksekar + Unicast services don't disappear when you disable all networking + +Revision 1.122 2004/11/19 18:00:34 ksekar + Security: use random ID for one-shot unicast queries + +Revision 1.121 2004/11/19 04:24:08 ksekar + Security: Enforce a "window" on one-shot wide-area queries + +Revision 1.120 2004/11/19 02:32:43 ksekar + Wide-Area Rendezvous Security: Add LLQ-ID to events + +Revision 1.119 2004/11/18 23:21:24 ksekar + LLQ Security: Need to verify src port/address for LLQ handshake + +Revision 1.118 2004/11/18 22:58:37 ksekar +Removed old comment. + +Revision 1.117 2004/11/18 18:04:21 ksekar +Restore checkins lost due to repository disk failure: Update comments & + +Revision 1.xxx 2004/11/17 06:17:57 cheshire +Update comments to show correct SRV names: _dns-update._udp.. and _dns-llq._udp.. + +Revision 1.xxx 2004/11/17 00:45:28 ksekar + Result of putUpdateLease not error-checked + +Revision 1.116 2004/11/16 01:41:47 ksekar +Fixed typo in debugf + +Revision 1.115 2004/11/15 20:09:24 ksekar + Wide Area support for Add/Remove record + +Revision 1.114 2004/11/13 02:32:47 ksekar + LLQ mobility fragile on non-primary interface +- fixed incorrect state comparison in CheckQueries + +Revision 1.113 2004/11/13 02:29:52 ksekar + LLQ refreshes not reliable + +Revision 1.112 2004/11/11 20:45:14 ksekar + self-conflict test not compatible with some BIND servers + +Revision 1.111 2004/11/11 20:14:55 ksekar + Wide-Area registrations not deregistered on sleep + +Revision 1.110 2004/11/10 23:53:53 ksekar +Remove no longer relevant comment + +Revision 1.109 2004/11/10 20:40:53 ksekar + LLQ mobility fragile on non-primary interface + +Revision 1.108 2004/11/01 20:36:16 ksekar + mDNSResponder should not receive Keychain Notifications + +Revision 1.107 2004/10/26 06:11:41 cheshire +Add improved logging to aid in diagnosis of mDNSResponder crashed + +Revision 1.106 2004/10/26 03:52:03 cheshire +Update checkin comments + +Revision 1.105 2004/10/26 01:15:06 cheshire +Use "#if 0" instead of commenting out code + +Revision 1.104 2004/10/25 21:41:38 ksekar + wide-area name conflicts can cause crash + +Revision 1.103 2004/10/25 19:30:52 ksekar + Simplify dynamic host name structures + +Revision 1.102 2004/10/23 01:16:00 cheshire + uDNS operations not always reliable on multi-homed hosts + +Revision 1.101 2004/10/22 20:52:07 ksekar + Create NAT port mappings for Long Lived Queries + +Revision 1.100 2004/10/20 02:16:41 cheshire +Improve "could not confirm existence of NS record" error message +Don't call newRR->RecordCallback if it is NULL + +Revision 1.99 2004/10/19 21:33:18 cheshire + Cannot resolve non-local registrations using the mach API +Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name +doesn't force multicast unless you set this flag to indicate explicitly that this is what you want + +Revision 1.98 2004/10/16 00:16:59 cheshire + Replace IP TTL 255 check with local subnet source address check + +Revision 1.97 2004/10/15 23:00:18 ksekar + Need to update LLQs on location changes + +Revision 1.96 2004/10/12 23:30:44 ksekar + mDNSResponder needs to follow CNAME referrals + +Revision 1.95 2004/10/12 03:15:09 ksekar + mDNS_StartQuery shouldn't return transient no-server error + +Revision 1.94 2004/10/12 02:49:20 ksekar + Clean up LLQ sleep/wake, error handling + +Revision 1.93 2004/10/08 04:17:25 ksekar + Don't use DNS extensions if the server does not advertise required SRV record + +Revision 1.92 2004/10/08 03:54:35 ksekar + Refine unicast polling intervals + +Revision 1.91 2004/09/30 17:45:34 ksekar + lots of log messages: mDNS_SetPrimaryIP: IP address unchanged + +Revision 1.90 2004/09/25 00:22:13 ksekar + Crash in uDNS_RegisterService + +Revision 1.89 2004/09/24 19:14:53 cheshire +Remove unused "extern mDNS mDNSStorage" + +Revision 1.88 2004/09/23 20:48:15 ksekar +Clarify retransmission debugf messages. + +Revision 1.87 2004/09/22 00:41:59 cheshire +Move tcp connection status codes into the legal range allocated for mDNS use + +Revision 1.86 2004/09/21 23:40:11 ksekar + mDNSResponder to return errors on NAT traversal failure + +Revision 1.85 2004/09/21 22:38:27 ksekar + PrimaryIP type uninitialized + +Revision 1.84 2004/09/18 00:30:39 cheshire + Infinite loop in CheckServiceRegistrations + +Revision 1.83 2004/09/17 00:31:51 cheshire +For consistency with ipv6, renamed rdata field 'ip' to 'ipv4' + +Revision 1.82 2004/09/16 21:36:36 cheshire + Fix unsafe use of mDNSPlatformTimeNow() +Changes to add necessary locking calls around unicast DNS operations + +Revision 1.81 2004/09/16 02:29:39 cheshire +Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around +uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService + +Revision 1.80 2004/09/16 01:58:21 cheshire +Fix compiler warnings + +Revision 1.79 2004/09/16 00:24:48 cheshire + Fix unsafe use of mDNSPlatformTimeNow() + +Revision 1.78 2004/09/15 01:16:57 ksekar + mDNSResponder printing too many messages + +Revision 1.77 2004/09/14 23:27:47 cheshire +Fix compile errors + +Revision 1.76 2004/09/14 22:22:00 ksekar + Legacy browses broken against some BIND versions + +Revision 1.75 2004/09/03 19:23:05 ksekar +: Need retransmission mechanism for wide-area service registrations + +Revision 1.74 2004/09/02 17:49:04 ksekar +: 8A246: mDNSResponder crash while logging on restart +Fixed incorrect conversions, changed %s to %##s for all domain names. + +Revision 1.73 2004/09/02 01:39:40 cheshire +For better readability, follow consistent convention that QR bit comes first, followed by OP bits + +Revision 1.72 2004/09/01 03:59:29 ksekar +: Conditionally compile out uDNS code on Windows + +Revision 1.71 2004/08/27 17:51:53 ksekar +Replaced unnecessary LogMsg with debugf. + +Revision 1.70 2004/08/25 00:37:27 ksekar +: Cleanup DynDNS hostname registration code + +Revision 1.69 2004/08/18 17:35:41 ksekar +: Feature #9586: Need support for Legacy NAT gateways + +Revision 1.68 2004/08/14 03:22:41 cheshire + Dynamic DNS UI <-> mDNSResponder glue +Add GetUserSpecifiedDDNSName() routine +Convert ServiceRegDomain to domainname instead of C string +Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs + +Revision 1.67 2004/08/13 23:46:58 cheshire +"asyncronous" -> "asynchronous" + +Revision 1.66 2004/08/13 23:37:02 cheshire +Now that we do both uDNS and mDNS, global replace "uDNS_info.hostname" with +"uDNS_info.UnicastHostname" for clarity + +Revision 1.65 2004/08/13 23:12:32 cheshire +Don't use strcpy() and strlen() on "struct domainname" objects; +use AssignDomainName() and DomainNameLength() instead +(A "struct domainname" is a collection of packed pascal strings, not a C string.) + +Revision 1.64 2004/08/13 23:01:05 cheshire +Use platform-independent mDNSNULL instead of NULL + +Revision 1.63 2004/08/12 00:32:36 ksekar +: LLQ Refreshes never terminate if unanswered + +Revision 1.62 2004/08/10 23:19:14 ksekar +: DNS Extension daemon for Wide Area Rendezvous +Moved routines/constants to allow extern access for garbage collection daemon + +Revision 1.61 2004/07/30 17:40:06 ksekar +: TXT Record updates not available for wide-area services + +Revision 1.60 2004/07/29 19:40:05 ksekar +NATPMP Support - minor fixes and cleanup + +Revision 1.59 2004/07/29 19:27:15 ksekar +NATPMP Support - minor fixes and cleanup + +Revision 1.58 2004/07/27 07:35:38 shersche +fix syntax error, variables declared in the middle of a block + +Revision 1.57 2004/07/26 22:49:30 ksekar +: Feature #9516: Need support for NATPMP in client + +Revision 1.56 2004/07/26 19:14:44 ksekar +: 8A210: mDNSResponder crashed in startLLQHandshakeCallback + +Revision 1.55 2004/07/15 19:01:33 ksekar +: Check for incorrect time comparisons + Revision 1.54 2004/06/22 02:10:53 ksekar : Lighthouse failure causes packet flood to DNS Revision 1.53 2004/06/17 20:49:09 ksekar -: Tiger8A148: repeated crash of mDNSResponder while location cycling +: mDNSResponder crash while location cycling Revision 1.52 2004/06/17 01:13:11 ksekar : polling interval too short @@ -162,9 +476,9 @@ Revision 1.13 2004/02/03 22:15:01 ksekar Fixed nameToAddr error check: don't abort state machine on nxdomain error. Revision 1.12 2004/02/03 19:47:36 ksekar -Added an asyncronous state machine mechanism to uDNS.c, including +Added an asynchronous state machine mechanism to uDNS.c, including calls to find the parent zone for a domain name. Changes include code -in repository previously dissabled via "#if 0 //incomplete". Codepath +in repository previously dissabled via "#if 0 incomplete". Codepath is currently unused, and will be called to create update records, etc. Revision 1.11 2004/01/30 02:12:30 ksekar @@ -220,20 +534,12 @@ Revision 1.1 2003/12/13 03:05:27 ksekar #pragma warning(disable:4706) #endif -#ifndef NULL -#define NULL mDNSNULL -#endif // NULL - - -#define ustrcpy(d,s) mDNSPlatformStrCopy(s,d) // use strcpy(2) param ordering -#define ustrlen(s) mDNSPlatformStrLen(s) #define umalloc(x) mDNSPlatformMemAllocate(x) // short hands for common routines #define ufree(x) mDNSPlatformMemFree(x) #define ubzero(x,y) mDNSPlatformMemZero(x,y) #define umemcpy(x, y, l) mDNSPlatformMemCopy(y, x, l) // uses memcpy(2) arg ordering - -// Asyncronous operation types +// Asynchronous operation types typedef enum { @@ -269,28 +575,70 @@ typedef void AsyncOpCallback(mStatus err, mDNS *const m, void *info, const Async // read top-to-bottom.) mDNSlocal void hndlTruncatedAnswer(DNSQuestion *question, const mDNSAddr *src, mDNS *m); -mDNSlocal mStatus startGetZoneData(domainname *name, mDNS *m, mDNSBool findUpdatePort, mDNSBool findLLQPort, - AsyncOpCallback callback, void *callbackInfo); +mDNSlocal mStatus startGetZoneData(domainname *name, mDNS *m, mDNSBool findUpdatePort, mDNSBool findLLQPort, AsyncOpCallback callback, void *callbackInfo); mDNSlocal mDNSBool recvLLQResponse(mDNS *m, DNSMessage *msg, const mDNSu8 *end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSInterfaceID InterfaceID); mDNSlocal void sendRecordRegistration(mDNS *const m, AuthRecord *rr); mDNSlocal void SendServiceRegistration(mDNS *m, ServiceRecordSet *srs); +mDNSlocal void SendServiceDeregistration(mDNS *m, ServiceRecordSet *srs); +mDNSlocal void serviceRegistrationCallback(mStatus err, mDNS *const m, void *srsPtr, const AsyncOpResult *result); +mDNSlocal void SendRecordUpdate(mDNS *m, AuthRecord *rr, uDNS_RegInfo *info); +mDNSlocal mStatus RegisterService(mDNS *m, ServiceRecordSet *srs); +mDNSlocal void SuspendLLQs(mDNS *m, mDNSBool DeregisterActive); +mDNSlocal void RestartQueries(mDNS *m); +mDNSlocal void startLLQHandshake(mDNS *m, LLQ_Info *info, mDNSBool defer); +mDNSlocal void llqResponseHndlr(mDNS * const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *context); + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Temporary workaround +#endif + +// 17 Places in this file directly call mDNSPlatformTimeNow(), which is unsafe +// The platform function is now called mDNSPlatformRawTime(), and +// mDNSPlatformTimeNow() is defined here as a temporary workaround. +// This is a gross hack, and after this change has been tested for a while, +// all these calls should be replaced by simple references to m->timenow + +mDNSlocal mDNSs32 mDNSPlatformTimeNow(mDNS *m) + { + if (m->mDNS_busy && m->timenow) return(m->timenow); + LogMsg("ERROR: uDNS.c code executing without holding main mDNS lock"); + + // To get a quick and easy stack trace to find out *how* this routine + // is being called without holding main mDNS lock, uncomment the line below: + // *(long*)0=0; + + return(mDNS_TimeNow(m)); + } // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - General Utility Functions #endif +// CountLabels() returns number of labels in name, excluding final root label +// (e.g. for "apple.com." CountLabels returns 2.) +mDNSlocal int CountLabels(const domainname *d) + { + int count = 0; + const mDNSu8 *ptr; + + for (ptr = d->c; *ptr; ptr = ptr + ptr[0] + 1) count++; + return count; + } + mDNSlocal mDNSOpaque16 newMessageID(uDNS_GlobalInfo *u) { - // if NextMessageID is 0 (ininitialized) or 0xffff (reserved for TCP packets) reset to 1 - if (!u->NextMessageID || u->NextMessageID == (mDNSu16)~0) u->NextMessageID = 1; + static mDNSBool randomized = mDNSfalse; + + if (!randomized) { u->NextMessageID = mDNSRandom(~0); randomized = mDNStrue; } return mDNSOpaque16fromIntVal(u->NextMessageID++); } // unlink an AuthRecord from a linked list mDNSlocal mStatus unlinkAR(AuthRecord **list, AuthRecord *const rr) { - AuthRecord *rptr, *prev = NULL; + AuthRecord *rptr, *prev = mDNSNULL; for (rptr = *list; rptr; rptr = rptr->next) { @@ -298,7 +646,7 @@ mDNSlocal mStatus unlinkAR(AuthRecord **list, AuthRecord *const rr) { if (prev) prev->next = rptr->next; else *list = rptr->next; - rptr->next = NULL; + rptr->next = mDNSNULL; return mStatus_NoError; } prev = rptr; @@ -307,106 +655,154 @@ mDNSlocal mStatus unlinkAR(AuthRecord **list, AuthRecord *const rr) return mStatus_UnknownErr; } +mDNSlocal void unlinkSRS(uDNS_GlobalInfo *u, ServiceRecordSet *srs) + { + ServiceRecordSet **p; + for (p = &u->ServiceRegistrations; *p; p = &(*p)->next) + if (*p == srs) { *p = srs->next; srs->next = mDNSNULL; return; } + LogMsg("ERROR: unlinkSRS - SRS not found in ServiceRegistrations list"); + } + mDNSlocal void LinkActiveQuestion(uDNS_GlobalInfo *u, DNSQuestion *q) { - if (IsActiveUnicastQuery(q, u)) - { LogMsg("LinkActiveQuestion - %s (%d) already in list!", q->qname.c, q->qtype); return; } + if (uDNS_IsActiveQuery(q, u)) + { LogMsg("LinkActiveQuestion - %##s (%d) already in list!", q->qname.c, q->qtype); return; } q->next = u->ActiveQueries; u->ActiveQueries = q; } +mDNSlocal void SwapRData(mDNS *m, AuthRecord *rr, mDNSBool DeallocOld) + { + RData *oldrd = rr->resrec.rdata; + mDNSu16 oldrdlen = rr->resrec.rdlength; + + if (!rr->uDNS_info.UpdateRData) { LogMsg("SwapRData invoked with NULL UpdateRData field"); return; } + SetNewRData(&rr->resrec, rr->uDNS_info.UpdateRData, rr->uDNS_info.UpdateRDLen); + if (DeallocOld) + { + rr->uDNS_info.UpdateRData = mDNSNULL; // Clear the NewRData pointer ... + if (rr->uDNS_info.UpdateRDCallback) rr->uDNS_info.UpdateRDCallback(m, rr, oldrd); // ... and let the client know + } + else + { + rr->uDNS_info.UpdateRData = oldrd; + rr->uDNS_info.UpdateRDLen = oldrdlen; + } + } + +// set retry timestamp for record with exponential backoff +// (for service record sets, use RR_SRV as representative for time checks +mDNSlocal void SetRecordRetry(mDNS *const m, AuthRecord *rr) + { + rr->LastAPTime = mDNSPlatformTimeNow(m); + if (rr->ThisAPInterval < INIT_UCAST_POLL_INTERVAL) { rr->ThisAPInterval = INIT_UCAST_POLL_INTERVAL; return; } + if (rr->ThisAPInterval*2 <= MAX_UCAST_POLL_INTERVAL) { rr->ThisAPInterval *= 2; return; } + if (rr->ThisAPInterval != MAX_UCAST_POLL_INTERVAL) { rr->ThisAPInterval = MAX_UCAST_POLL_INTERVAL; } + } + // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - Name Server List Management #endif -mDNSexport void mDNS_RegisterDNS(mDNS *const m, mDNSv4Addr *const dnsAddr) +mDNSexport void mDNS_AddDNSServer(mDNS *const m, const mDNSAddr *addr, const domainname *d) { - //!!!KRS do this dynamically! uDNS_GlobalInfo *u = &m->uDNS_info; - int i; - - if (!dnsAddr->NotAnInteger) - { - LogMsg("ERROR: attempt to register DNS with IP address 0"); - return; - } + DNSServer *s, **p = &u->Servers; + + mDNS_Lock(m); + if (!d) d = (domainname *)""; - for (i = 0; i < 32; i++) - { - if (!u->Servers[i].ip.v4.NotAnInteger) - { - u->Servers[i].ip.v4.NotAnInteger = dnsAddr->NotAnInteger; - u->Servers[i].type = mDNSAddrType_IPv4; - return; - } - if (u->Servers[i].ip.v4.NotAnInteger == dnsAddr->NotAnInteger) - { - LogMsg("ERROR: mDNS_RegisterDNS - DNS already registered"); - return; - } - } - if (i == 32) { LogMsg("ERROR: mDNS_RegisterDNS - too many registered servers"); } + while (*p) // Check if we already have this {server,domain} pair registered + { + if (mDNSSameAddress(&(*p)->addr, addr) && SameDomainName(&(*p)->domain, d)) + LogMsg("Note: DNS Server %#a for domain %##s registered more than once", addr, d->c); + p=&(*p)->next; + } + // allocate, add to list + s = umalloc(sizeof(*s)); + if (!s) { LogMsg("Error: mDNS_AddDNSServer - malloc"); goto end; } + s->addr = *addr; + AssignDomainName(s->domain, *d); + s->next = mDNSNULL; + *p = s; + + end: + mDNS_Unlock(m); } -mDNSexport void mDNS_DeregisterDNS(mDNS *const m, mDNSv4Addr *const dnsAddr) +mDNSexport void mDNS_DeleteDNSServers(mDNS *const m) { - uDNS_GlobalInfo *u = &m->uDNS_info; - int i; + DNSServer *s; + mDNS_Lock(m); - if (!dnsAddr->NotAnInteger) - { - LogMsg("ERROR: attempt to deregister DNS with IP address 0"); - return; - } - - for (i = 0; i < 32; i++) - { - - if (u->Servers[i].ip.v4.NotAnInteger == dnsAddr->NotAnInteger) - { - u->Servers[i].ip.v4.NotAnInteger = 0; - return; - } - } - if (i == 32) { LogMsg("ERROR: mDNS_DeregisterDNS - no such DNS registered"); } - } + s = m->uDNS_info.Servers; + m->uDNS_info.Servers = mDNSNULL; + while (s) + { + DNSServer *tmp = s; + s = s->next; + ufree(tmp); + } -mDNSexport void mDNS_DeregisterDNSList(mDNS *const m) - { - ubzero(m->uDNS_info.Servers, 32 * sizeof(mDNSAddr)); + mDNS_Unlock(m); } -mDNSexport mDNSBool mDNS_DNSRegistered(mDNS *const m) - { - int i; - - for (i = 0; i < 32; i++) if (m->uDNS_info.Servers[i].ip.v4.NotAnInteger) return mDNStrue; - return mDNSfalse; - } - - // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - authorization management #endif +mDNSlocal uDNS_AuthInfo *GetAuthInfoForZone(const uDNS_GlobalInfo *u, const domainname *zone) + { + uDNS_AuthInfo *ptr; + while (zone->c[0]) + { + for (ptr = u->AuthInfoList; ptr; ptr = ptr->next) + if (SameDomainName(&ptr->zone, zone)) return(ptr); + zone = (const domainname *)(zone->c + 1 + zone->c[0]); + } + return mDNSNULL; + } + +mDNSlocal void DeleteAuthInfoForZone(uDNS_GlobalInfo *u, const domainname *zone) + { + uDNS_AuthInfo *ptr, *prev = mDNSNULL; + + for (ptr = u->AuthInfoList; ptr; ptr = ptr->next) + { + if (SameDomainName(&ptr->zone, zone)) + { + if (prev) prev->next = ptr->next; + else u->AuthInfoList = ptr->next; + ufree(ptr); + return; + } + prev = ptr; + } + } -mDNSexport mStatus mDNS_UpdateDomainRequiresAuthentication(mDNS *m, domainname *zone, domainname *key, - mDNSu8 *sharedSecret, mDNSu32 ssLen, mDNSBool base64) +mDNSexport mStatus mDNS_SetSecretForZone(mDNS *m, const domainname *zone, const domainname *key, const mDNSu8 *sharedSecret, mDNSu32 ssLen, mDNSBool base64) { uDNS_AuthInfo *info; mDNSu8 keybuf[1024]; mDNSs32 keylen; + uDNS_GlobalInfo *u = &m->uDNS_info; + mStatus status = mStatus_NoError; + + mDNS_Lock(m); + + if (GetAuthInfoForZone(u, zone)) DeleteAuthInfoForZone(u, zone); + if (!key) goto exit; info = (uDNS_AuthInfo*)umalloc(sizeof(uDNS_AuthInfo) + ssLen); - if (!info) { LogMsg("ERROR: umalloc"); return mStatus_NoMemoryErr; } + if (!info) { LogMsg("ERROR: umalloc"); status = mStatus_NoMemoryErr; goto exit; } ubzero(info, sizeof(uDNS_AuthInfo)); - ustrcpy(info->zone.c, zone->c); - ustrcpy(info->keyname.c, key->c); + AssignDomainName(info->zone, *zone); + AssignDomainName(info->keyname, *key); if (base64) { @@ -415,181 +811,836 @@ mDNSexport mStatus mDNS_UpdateDomainRequiresAuthentication(mDNS *m, domainname * { LogMsg("ERROR: mDNS_UpdateDomainRequiresAuthentication - could not convert shared secret from base64"); ufree(info); - return mStatus_UnknownErr; + status = mStatus_UnknownErr; + goto exit; } DNSDigest_ConstructHMACKey(info, keybuf, (mDNSu32)keylen); } else DNSDigest_ConstructHMACKey(info, sharedSecret, ssLen); // link into list - // !!!KRS this should be a hashtable since we must check if updates are required on each registration info->next = m->uDNS_info.AuthInfoList; m->uDNS_info.AuthInfoList = info; - return mStatus_NoError; +exit: + mDNS_Unlock(m); + return status; + } + + // *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - NAT Traversal +#endif + +mDNSlocal mDNSBool MapServicePort(mDNS *m) + { + uDNS_HostnameInfo *i; + + //!!!KRS this could be cached + for (i = m->uDNS_info.Hostnames; i; i= i->next) + if (i->ar->uDNS_info.NATinfo && i->ar->uDNS_info.NATinfo->state != NATState_Error) return mDNStrue; + + return mDNSfalse; + } + +mDNSlocal mDNSBool DomainContainsLabelString(const domainname *d, const char *str) + { + const domainlabel *l; + domainlabel buf; + + if (!MakeDomainLabelFromLiteralString(&buf, str)) return mDNSfalse; + + for (l = (const domainlabel *)d; l->c[0]; l = (const domainlabel *)(l->c + l->c[0]+1)) + if (SameDomainLabel(l->c, buf.c)) return mDNStrue; + return mDNSfalse; + } + +// allocate struct, link into global list, initialize +mDNSlocal NATTraversalInfo *AllocNATInfo(mDNS *const m, NATOp_t op, NATResponseHndlr callback) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + NATTraversalInfo *info = umalloc(sizeof(NATTraversalInfo)); + if (!info) { LogMsg("ERROR: malloc"); return mDNSNULL; } + ubzero(info, sizeof(NATTraversalInfo)); + info->next = u->NATTraversals; + u->NATTraversals = info; + info->retry = mDNSPlatformTimeNow(m) + NATMAP_INIT_RETRY; + info->op = op; + info->state = NATState_Init; + info->ReceiveResponse = callback; + info->PublicPort.NotAnInteger = 0; + return info; } -mDNSexport void mDNS_ClearAuthenticationList(mDNS *m) +// unlink from list, deallocate +mDNSlocal mDNSBool FreeNATInfo(mDNS *m, NATTraversalInfo *n) { - uDNS_AuthInfo *fptr, *ptr = m->uDNS_info.AuthInfoList; + NATTraversalInfo *ptr, *prev = mDNSNULL; + if (n == m->uDNS_info.LLQNatInfo) m->uDNS_info.LLQNatInfo = mDNSNULL; + ptr = m->uDNS_info.NATTraversals; while (ptr) { - fptr = ptr; + if (ptr == n) + { + if (prev) prev->next = ptr->next; + else m->uDNS_info.NATTraversals = ptr->next; + ufree(n); + return mDNStrue; + } + prev = ptr; ptr = ptr->next; - ufree(fptr); } - m->uDNS_info.AuthInfoList = NULL; + LogMsg("FreeNATInfo: NATTraversalInfo not found in list"); + return mDNSfalse; } -mDNSlocal uDNS_AuthInfo *GetAuthInfoForZone(const uDNS_GlobalInfo *u, const domainname *zone) +mDNSlocal void SendNATMsg(NATTraversalInfo *info, mDNS *m) { - uDNS_AuthInfo *ptr; - domainname *z; - mDNSu32 zoneLen, ptrZoneLen; + mStatus err; + mDNSAddr dst; + mDNSIPPort dstport; + uDNS_GlobalInfo *u = &m->uDNS_info; - zoneLen = ustrlen(zone->c); - for (ptr = u->AuthInfoList; ptr; ptr = ptr->next) + if (info->state != NATState_Request && info->state != NATState_Refresh) + { LogMsg("SendNATMsg: Bad state %d", info->state); return; } + + if (u->Router.ip.v4.NotAnInteger) { - z = &ptr->zone; - ptrZoneLen = ustrlen(z->c); - if (zoneLen < ptrZoneLen) continue; - // return info if zone ends in info->zone - if (mDNSPlatformMemSame(z->c, zone->c + (zoneLen - ptrZoneLen), ptrZoneLen)) return ptr; - } - return NULL; - } + // send msg if we have a router + dst.type = u->Router.type; + dst.ip.v4 = u->Router.ip.v4; + dstport = mDNSOpaque16fromIntVal(NATMAP_PORT); + err = mDNSPlatformSendUDP(m, info->request, info->request+info->requestlen, 0, &dst, dstport); + if (!err) (info->ntries++); // don't increment attempt counter if the send failed + } + // set retry + if (info->RetryInterval < NATMAP_INIT_RETRY) info->RetryInterval = NATMAP_INIT_RETRY; + else if (info->RetryInterval * 2 > NATMAP_MAX_RETRY) info->RetryInterval = NATMAP_MAX_RETRY; + else info->RetryInterval *= 2; + info->retry = mDNSPlatformTimeNow(m) + info->RetryInterval; + } - - - // *************************************************************************** -#if COMPILER_LIKES_PRAGMA_MARK -#pragma mark - host name and interface management -#endif - - -mDNSlocal void hostnameCallback(mDNS *const m, AuthRecord *const rr, mStatus result) +mDNSlocal void ReceiveNATAddrResponse(NATTraversalInfo *n, mDNS *m, mDNSu8 *pkt, mDNSu16 len) { - // note that the rr is already unlinked if result is non-zero + NATErr_t NatErr = NATErr_None; + mStatus err = mStatus_NoError; + AuthRecord *rr = mDNSNULL; + mDNSAddr addr; - if (result == mStatus_MemFree) return; - if (result == mStatus_NameConflict && rr->resrec.RecordType == kDNSRecordTypeUnique) + if (n->state != NATState_Request) { - // if we get a name conflict, make sure our name/addr isn't already registered by re-registering - rr->resrec.RecordType = kDNSRecordTypeKnownUnique; - uDNS_RegisterRecord(m, rr); - return; + LogMsg("ReceiveNATAddrResponse: bad state %d", n->state); + err = mStatus_UnknownErr; + goto end; } - - if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique) - // we've already tried to re-register. reset RecordType before returning RR to client + + rr = n->reg.RecordRegistration; + if (!rr) { - if (result == mStatus_NoSuchRecord) // name is advertised for some other address - result = mStatus_NameConflict; - rr->resrec.RecordType = kDNSRecordTypeUnique; + LogMsg("ReceiveNATAddrResponse: registration cancelled"); + err = mStatus_UnknownErr; + goto end; } - - if (!result) rr->resrec.RecordType = kDNSRecordTypeVerified; - if (result) - ((NetworkInterfaceInfo *)(rr->RecordContext))->uDNS_info.registered = mDNSfalse; - mDNS_HostNameCallback(m, rr, result); - } + addr.type = mDNSAddrType_IPv4; + addr.ip.v4 = rr->resrec.rdata->u.ipv4; -mDNSlocal void deadvertiseIfCallback(mDNS *const m, AuthRecord *const rr, mStatus err) - { - (void)m; // unused - - if (err == mStatus_MemFree) ufree(rr); - else LogMsg("deadvertiseIfCallback - error %s for record %s", err, rr->resrec.name.c); - } - - mDNSexport void uDNS_DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) - { - AuthRecord *copy; - AuthRecord *rr = &set->uDNS_info.RR_A; - - // NOTE: for compatibility w/ mDNS architecture, we make a copy of the address record before sending a - // goodbye, since mDNS does not send goodbyes for address records and expects the memory to be immediately - // freed - - if (set->uDNS_info.registered) - { - // copy resource record - copy = (AuthRecord*)umalloc(sizeof(AuthRecord)); // allocate storage - if (!copy) { LogMsg("ERROR: Malloc"); return; } - umemcpy(copy, rr, sizeof(AuthRecord)); // copy all fields - copy->resrec.rdata = ©->rdatastorage; // set rdata pointer - if (rr->resrec.rdata != &rr->rdatastorage) - { LogMsg("ERROR: uDNS_DeadvertiseInterface - expected local rdata storage. Aborting deregistration"); return; } - - // link copy into list - copy->next = m->uDNS_info.RecordRegistrations; - m->uDNS_info.RecordRegistrations = copy; - copy->RecordCallback = deadvertiseIfCallback; + if (!pkt) // timeout + { +#ifdef _LEGACY_NAT_TRAVERSAL_ + err = LNT_GetPublicIP(&addr.ip.v4); + if (err) goto end; + else n->state = NATState_Legacy; +#else + debugf("ReceiveNATAddrResponse: timeout"); + err = mStatus_NATTraversal; + goto end; +#endif // _LEGACY_NAT_TRAVERSAL_ + } + else + { + if (len < ADDR_REPLY_PKTLEN) + { + LogMsg("ReceiveNATAddrResponse: response too short (%d bytes)", len); + err = mStatus_NATTraversal; + goto end; + } + if (pkt[0] != NATMAP_VERS) + { + LogMsg("ReceiveNATAddrResponse: received version %d (expect version %d)", pkt[0], NATMAP_VERS); + err = mStatus_NATTraversal; + goto end; + } + if (pkt[1] != (NATOp_AddrRequest | NATMAP_RESPONSE_MASK)) + { + LogMsg("ReceiveNATAddrResponse: bad response code %d", pkt[1]); + err = mStatus_NATTraversal; + goto end; + } + NatErr = (NATErr_t)((NATErr_t)pkt[2] << 8 | (NATErr_t)pkt[3]); + if (NatErr) { LogMsg("ReceiveAddrResponse: received error %d", err); err = mStatus_NATTraversal; goto end; } - // unlink the original - unlinkAR(&m->uDNS_info.RecordRegistrations, rr); - rr->uDNS_info.state = regState_Unregistered; - set->uDNS_info.registered = mDNSfalse; - uDNS_DeregisterRecord(m, copy); + addr.ip.v4.b[0] = pkt[4]; + addr.ip.v4.b[1] = pkt[5]; + addr.ip.v4.b[2] = pkt[6]; + addr.ip.v4.b[3] = pkt[7]; + n->state = NATState_Established; + } + + if (IsPrivateV4Addr(&addr)) + { + LogMsg("ReceiveNATAddrResponse: Double NAT"); + err = mStatus_DblNAT; + goto end; + } + + end: + if (err) + { + FreeNATInfo(m, n); + if (rr) + { + rr->uDNS_info.NATinfo = mDNSNULL; + rr->uDNS_info.state = regState_Unregistered; // note that rr is not yet in global list + rr->RecordCallback(m, rr, mStatus_NATTraversal); + // note - unsafe to touch rr after callback + } + return; + } + else LogMsg("Received public IP address %d.%d.%d.%d from NAT.", addr.ip.v4.b[0], addr.ip.v4.b[1], addr.ip.v4.b[2], addr.ip.v4.b[3]); + rr->resrec.rdata->u.ipv4 = addr.ip.v4; // replace rdata w/ public address + uDNS_RegisterRecord(m, rr); + } + + +mDNSlocal void StartGetPublicAddr(mDNS *m, uDNS_HostnameInfo *hInfo) + { + mDNSu8 *msg; + uDNS_GlobalInfo *u = &m->uDNS_info; + + NATTraversalInfo *info = AllocNATInfo(m, NATOp_AddrRequest, ReceiveNATAddrResponse); + if (!info) { uDNS_RegisterRecord(m, hInfo->ar); return; } + hInfo->ar->uDNS_info.NATinfo = info; + info->reg.RecordRegistration = hInfo->ar; + info->state = NATState_Request; + + // format message + msg = info->request; + msg[0] = NATMAP_VERS; + msg[1] = NATOp_AddrRequest; + info->requestlen = ADDR_REQUEST_PKTLEN; + + if (!u->Router.ip.v4.NotAnInteger) + { + debugf("No router. Will retry NAT traversal in %ld ticks", NATMAP_INIT_RETRY); + return; } - else debugf("uDNS_DeadvertiseInterface - interface not registered"); - return; + + SendNATMsg(info, m); + } + + +mDNSlocal void RefreshNATMapping(NATTraversalInfo *n, mDNS *m) + { + n->state = NATState_Refresh; + n->RetryInterval = NATMAP_INIT_RETRY; + n->ntries = 0; + SendNATMsg(n, m); } -mDNSexport void uDNS_AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) +mDNSlocal void LLQNatMapComplete(mDNS *m) { - mDNSu8 *ip = set->ip.ip.v4.b; - AuthRecord *a = &set->uDNS_info.RR_A; - a->RecordContext = set; - if (set->ip.type != mDNSAddrType_IPv4 // non-v4 - || (ip[0] == 169 && ip[1] == 254) // link-local - || (ip[0] == 127 && ip[1] == 0 && ip[2] == 0 && ip[3] == 1)) // loopback - return; + uDNS_GlobalInfo *u = &m->uDNS_info; + LLQ_Info *llqInfo; + NATTraversalInfo *n = u->LLQNatInfo; - if (set->uDNS_info.registered && SameDomainName(&m->uDNS_info.hostname, &set->uDNS_info.regname)) - return; // already registered + if (!n) { LogMsg("Error: LLQNatMapComplete called with NULL LLQNatInfo"); return; } + if (n->state != NATState_Established && n->state != NATState_Legacy && n->state != NATState_Error) + { LogMsg("LLQNatMapComplete - bad nat state %d", n->state); return; } + + u->CurrentQuery = u->ActiveQueries; + while (u->CurrentQuery) + { + DNSQuestion *q = u->CurrentQuery; + u->CurrentQuery = u->CurrentQuery->next; + llqInfo = q->uDNS_info.llq; + if (q->LongLived && llqInfo->state == LLQ_NatMapWait) + { + if (n->state == NATState_Error) + { + llqInfo->NATMap = mDNSfalse; + llqInfo->question->uDNS_info.responseCallback = llqResponseHndlr; + llqInfo->state = LLQ_Poll; + llqInfo->question->LastQTime = mDNSPlatformTimeNow(m) - (2 * INIT_UCAST_POLL_INTERVAL); // trigger immediate poll + llqInfo->question->ThisQInterval = INIT_UCAST_POLL_INTERVAL; + } + else { llqInfo->state = LLQ_GetZoneInfo; startLLQHandshake(m, llqInfo, mDNSfalse); } + } + } + } - if (!m->uDNS_info.hostname.c[0]) +mDNSlocal void ReceivePortMapReply(NATTraversalInfo *n, mDNS *m, mDNSu8 *pkt, mDNSu16 len) + { + NATErr_t err; + ServiceRecordSet *srs; + mDNSIPPort priv, pktpriv, pub; + mDNSu32 lease; + mDNSBool deletion; + + if (n->state != NATState_Request && n->state != NATState_Refresh) + { LogMsg("ReceivePortMapReply: bad state %d", n->state); return; } + + deletion = !(n->request[8] | n->request[9] | n->request[10] | n->request[11]); // zero lease + if (deletion) { n->state = NATState_Deleted; return; } + // note that we explicitly ignore timeouts here + // we keep the context struct around so that the SendServiceDeregistration code can reference + // it to determine the public port to delete in the SRV record. + + srs = n->reg.ServiceRegistration; + if (!srs && n != m->uDNS_info.LLQNatInfo) { - // no hostname available - set->uDNS_info.registered = mDNSfalse; + debugf("ReceivePortMapReply: registration cancelled"); + FreeNATInfo(m, n); return; } - - set->uDNS_info.registered = mDNStrue; - ustrcpy(set->uDNS_info.regname.c, m->uDNS_info.hostname.c); - //!!!KRS temp ttl 1 - mDNS_SetupResourceRecord(a, mDNSNULL, 0, kDNSType_A, 1, kDNSRecordTypeShared /*Unique*/, hostnameCallback, set); //!!!KRS - - ustrcpy(a->resrec.name.c, m->uDNS_info.hostname.c); - a->resrec.rdata->u.ip = set->ip.ip.v4; - LogMsg("uDNS_AdvertiseInterface: advertising %s", m->uDNS_info.hostname.c); - - uDNS_RegisterRecord(m, a); + + priv = srs ? srs->RR_SRV.resrec.rdata->u.srv.port : m->UnicastPort4; + + if (!pkt) // timeout + { +#ifdef _LEGACY_NAT_TRAVERSAL_ + int ntries = 0; + mStatus err; + mDNSBool tcp = (srs && DomainContainsLabelString(&srs->RR_PTR.resrec.name, "_tcp")); + + pub = priv; // initially request priv == pub + while (1) + { + err = LNT_MapPort(priv, pub, tcp); + if (!err) + { + n->PublicPort = pub; + n->state = NATState_Legacy; + goto end; + } + else if (err != mStatus_AlreadyRegistered || ++ntries > LEGACY_NATMAP_MAX_TRIES) + { + n->state = NATState_Error; + goto end; + } + else + { + // the mapping we want is taken - try a random port + mDNSu16 RandPort = mDNSRandom(DYN_PORT_MAX - DYN_PORT_MIN) + DYN_PORT_MIN; + pub = mDNSOpaque16fromIntVal(RandPort); + } + } +#else + goto end; +#endif // _LEGACY_NAT_TRAVERSAL_ + } + + if (len < PORTMAP_PKTLEN) { LogMsg("ReceivePortMapReply: response too short (%d bytes)", len); goto end; } + if (pkt[0] != NATMAP_VERS) { LogMsg("ReceivePortMapReply: received version %d (expect version %d)", pkt[0], NATMAP_VERS); goto end; } + if (pkt[1] != (n->op | NATMAP_RESPONSE_MASK)) { LogMsg("ReceivePortMapReply: bad response code %d", pkt[1]); goto end; } + err = (NATErr_t)((NATErr_t)pkt[2] << 8 | (NATErr_t)pkt[3]); + if (err) { LogMsg("ReceivePortMapReply: received error %d", err); goto end; } + + pktpriv.b[0] = pkt[4]; + pktpriv.b[1] = pkt[5]; + pub.b[0] = pkt[6]; + pub.b[1] = pkt[7]; + + lease = (mDNSu32) ((mDNSu32)pkt[8] << 24 | (mDNSu32)pkt[9] << 16 | (mDNSu32)pkt[10] << 8 | pkt[11]); + if (lease > 0x70000000UL / mDNSPlatformOneSecond) + lease = 0x70000000UL / mDNSPlatformOneSecond; + + if (priv.NotAnInteger != pktpriv.NotAnInteger) + { LogMsg("ReceivePortMapReply: reply private port does not match requested private port"); goto end; } + + if (n->state == NATState_Refresh && pub.NotAnInteger != n->PublicPort.NotAnInteger) + LogMsg("ReceivePortMapReply: NAT refresh changed public port from %d to %d", mDNSVal16(n->PublicPort), mDNSVal16(pub)); + // !!!KRS we need to update the SRV here! + n->PublicPort = pub; + + n->retry = mDNSPlatformTimeNow(m) + ((mDNSs32)lease * mDNSPlatformOneSecond/2); // retry half way to expiration + + if (n->state == NATState_Refresh) { n->state = NATState_Established; return; } + n->state = NATState_Established; + + end: + if (n->state != NATState_Established && n->state != NATState_Legacy) + { + LogMsg("NAT Port Mapping: timeout"); + n->state = NATState_Error; + if (!srs) { LLQNatMapComplete(m); return; } + FreeNATInfo(m, n); + srs->uDNS_info.NATinfo = mDNSNULL; + unlinkSRS(&m->uDNS_info, srs); + srs->uDNS_info.state = regState_Unregistered; + srs->ServiceCallback(m, srs, mStatus_NATTraversal); + return; // note - unsafe to touch srs here + } + + LogMsg("Mapped private port %d to public port %d", mDNSVal16(priv), mDNSVal16(n->PublicPort)); + if (!srs) { LLQNatMapComplete(m); return; } + srs->uDNS_info.state = regState_FetchingZoneData; + startGetZoneData(&srs->RR_SRV.resrec.name, m, mDNStrue, mDNSfalse, serviceRegistrationCallback, srs); } +mDNSlocal void FormatPortMaprequest(NATTraversalInfo *info, mDNSIPPort port) + { + mDNSu8 *msg = info->request; + mDNSOpaque32 lease = mDNSOpaque32fromIntVal(NATMAP_DEFAULT_LEASE); + + msg[0] = NATMAP_VERS; + msg[1] = info->op; + msg[2] = 0; // reserved + msg[3] = 0; // reserved + msg[4] = port.b[0]; // private port + msg[5] = port.b[1]; + msg[6] = port.b[0]; // requested pub. port + msg[7] = port.b[1]; + + msg[8] = lease.b[0]; + msg[9] = lease.b[1]; + msg[10] = lease.b[2]; + msg[11] = lease.b[3]; + } -// *************************************************************************** +mDNSlocal void SendInitialPMapReq(mDNS *m, NATTraversalInfo *info) + { + if (!m->uDNS_info.Router.ip.v4.NotAnInteger) + { + debugf("No router. Will retry NAT traversal in %ld seconds", NATMAP_INIT_RETRY); + info->retry = mDNSPlatformTimeNow(m) + NATMAP_INIT_RETRY; + info->RetryInterval = NATMAP_INIT_RETRY; + return; + } + SendNATMsg(info, m); + return; + } + +mDNSlocal void StartNATPortMap(mDNS *m, ServiceRecordSet *srs) + { + NATOp_t op; + NATTraversalInfo *info; + + if (DomainContainsLabelString(&srs->RR_PTR.resrec.name, "_tcp")) op = NATOp_MapTCP; + else if (DomainContainsLabelString(&srs->RR_PTR.resrec.name, "_udp")) op = NATOp_MapUDP; + else { LogMsg("StartNATPortMap: could not determine transport protocol of service %##s", srs->RR_SRV.resrec.name.c); goto error; } + + info = AllocNATInfo(m, op, ReceivePortMapReply); + srs->uDNS_info.NATinfo = info; + info->reg.ServiceRegistration = srs; + info->state = NATState_Request; + info->requestlen = PORTMAP_PKTLEN; + + FormatPortMaprequest(info, srs->RR_SRV.resrec.rdata->u.srv.port); + SendInitialPMapReq(m, info); + return; + + error: + startGetZoneData(&srs->RR_SRV.resrec.name, m, mDNStrue, mDNSfalse, serviceRegistrationCallback, srs); + } + +mDNSlocal void DeleteNATPortMapping(mDNS *m, NATTraversalInfo *nat, ServiceRecordSet *srs) + { + if (nat->state == NATState_Established) // let other edge-case states expire for simplicity + { + // zero lease + nat->request[8] = 0; + nat->request[9] = 0; + nat->request[10] = 0 ; + nat->request[11] = 0; + nat->state = NATState_Request; + SendNATMsg(nat, m); + } +#ifdef _LEGACY_NAT_TRAVERSAL_ + else if (nat->state == NATState_Legacy) + { + mStatus err = mStatus_NoError; + mDNSBool tcp = srs ? DomainContainsLabelString(&srs->RR_PTR.resrec.name, "_tcp") : mDNSfalse; + err = LNT_UnmapPort(nat->PublicPort, tcp); + if (err) LogMsg("Legacy NAT Traversal - unmap request failed with error %ld", err); + } +#else + (void)srs; // unused +#endif // _LEGACY_NAT_TRAVERSAL_ + } + +mDNSlocal void StartLLQNatMap(mDNS *m) + { + NATTraversalInfo *info = AllocNATInfo(m, NATOp_MapUDP, ReceivePortMapReply); + uDNS_GlobalInfo *u = &m->uDNS_info; + + u->LLQNatInfo = info; + + info->reg.RecordRegistration = mDNSNULL; + info->reg.ServiceRegistration = mDNSNULL; + info->state = NATState_Request; + info->requestlen = PORTMAP_PKTLEN; + FormatPortMaprequest(info, m->UnicastPort4); + SendInitialPMapReq(m, info); + return; + } + +// if LLQ NAT context unreferenced, delete the mapping +mDNSlocal void CheckForUnreferencedLLQMapping(mDNS *m) + { + NATTraversalInfo *nat = m->uDNS_info.LLQNatInfo; + DNSQuestion *q; + + if (!nat) return; + + for (q = m->uDNS_info.ActiveQueries; q; q = q->next) + if (q->LongLived && q->uDNS_info.llq->NATMap) return; + + //to avoid race condition if we need to recreate before this finishes, we do one-shot deregistration + if (nat->state == NATState_Established || nat->state == NATState_Legacy) + DeleteNATPortMapping(m, nat, mDNSNULL); // for simplicity we allow other states to expire + FreeNATInfo(m, nat); // note: this clears the global LLQNatInfo pointer + } + + // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK -#pragma mark - Incoming Message Processing +#pragma mark - host name and interface management #endif -mDNSlocal mDNSBool sameResourceRecord(ResourceRecord *r1, ResourceRecord *r2) +// if we ever want to refine support for multiple hostnames, we can add logic matching service names to a particular hostname +// for now, we grab the first registered DynDNS name, if any, or a static name we learned via a reverse-map query +mDNSlocal mDNSBool GetServiceTarget(uDNS_GlobalInfo *u, AuthRecord *srv, domainname *dst) + { + uDNS_HostnameInfo *hi = u->Hostnames; + (void)srv; // unused + + dst->c[0] = 0; + while (hi) + { + if (hi->ar->uDNS_info.state == regState_Registered || hi->ar->uDNS_info.state == regState_Refresh) + { AssignDomainName(*dst, hi->ar->resrec.name); return mDNStrue; } + hi = hi->next; + } + + if (u->StaticHostname.c[0]) { AssignDomainName(*dst, u->StaticHostname); return mDNStrue; } + return mDNSfalse; + } + +mDNSlocal void UpdateSRV(mDNS *m, ServiceRecordSet *srs) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + ExtraResourceRecord *e; + domainname newtarget; + domainname *curtarget = &srs->RR_SRV.resrec.rdata->u.srv.target; + mDNSBool havetarget = GetServiceTarget(u, &srs->RR_SRV, &newtarget); + + if ((!havetarget && srs->uDNS_info.state == regState_NoTarget) || (havetarget && SameDomainName(curtarget, &newtarget))) return; // target unchanged + + switch(srs->uDNS_info.state) + { + case regState_FetchingZoneData: + case regState_Cancelled: + case regState_DeregPending: + case regState_DeregDeferred: + case regState_Unregistered: + case regState_NATMap: + case regState_ExtraQueued: + // In these states, the SRV has either not yet been registered (it will get up-to-date information when it is) + // or is in the process of, or has already been, deregistered + return; + + case regState_Pending: + case regState_Refresh: + case regState_UpdatePending: + // let the in-flight operation complete before updating + srs->uDNS_info.SRVUpdateDeferred = mDNStrue; + return; + + case regState_NoTarget: + // Service has not been registered due to lack of a target hostname + if (havetarget) SendServiceRegistration(m, srs); + return; + + case regState_Registered: + // target lost or changed. deregister service. upon completion, we'll look for a new target + // extra will be re-registed if the service is re-registered + for (e = srs->Extras; e; e = e->next) e->r.uDNS_info.state = regState_ExtraQueued; + + srs->uDNS_info.LostTarget = mDNStrue; + SendServiceDeregistration(m, srs); + return; + } + } + +mDNSlocal void UpdateSRVRecords(mDNS *m) + { + ServiceRecordSet *srs; + + for (srs = m->uDNS_info.ServiceRegistrations; srs; srs = srs->next) UpdateSRV(m, srs); + } + +mDNSlocal void HostnameCallback(mDNS *const m, AuthRecord *const rr, mStatus result) + { + uDNS_HostnameInfo *hi = (uDNS_HostnameInfo *)rr->RecordContext; + mDNSu8 *ip = rr->resrec.rdata->u.ipv4.b; + + if (result == mStatus_MemFree) + { + debugf("MemFree: %##s IP %d.%d.%d.%d", rr->resrec.name.c, ip[0], ip[1], ip[2], ip[3]); + if (hi) ufree(hi); + ufree(rr); + return; + } + + if (result == mStatus_NameConflict && rr->resrec.RecordType == kDNSRecordTypeUnique) + { + // if we get a name conflict, make sure our name/addr isn't already registered by re-registering + debugf("Name in use - retrying as type KnownUnique"); + rr->resrec.RecordType = kDNSRecordTypeKnownUnique; + uDNS_RegisterRecord(m, rr); + return; + } + + if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique) + { + // we've already tried to re-register. reset RecordType before returning RR to client + if (result == mStatus_NoSuchRecord) // name is advertised for some other address + result = mStatus_NameConflict; + } + + if (result) + { + // don't unlink or free - we can retry when we get a new address/router + LogMsg("HostnameCallback: Error %ld for registration of %##s IP %d.%d.%d.%d", result, rr->resrec.name.c, ip[0], ip[1], ip[2], ip[3]); + if (!hi) { ufree(rr); return; } + if (hi->ar->uDNS_info.state != regState_Unregistered) LogMsg("Error: HostnameCallback invoked with error code for record not in regState_Unregistered!"); + (const void *)rr->RecordContext = hi->StatusContext; + if (hi->StatusCallback) + hi->StatusCallback(m, rr, result); // client may NOT make API calls here + rr->RecordContext = (void *)hi; + return; + } + + // register any pending services that require a target + UpdateSRVRecords(m); + + // Deliver success to client + if (!hi) { LogMsg("HostnameCallback invoked with orphaned address record"); return; } + LogMsg("Registered hostname %##s IP %d.%d.%d.%d", rr->resrec.name.c, ip[0], ip[1], ip[2], ip[3]); + (const void *)rr->RecordContext = hi->StatusContext; + if (hi->StatusCallback) + hi->StatusCallback(m, rr, result); // client may NOT make API calls here + rr->RecordContext = (void *)hi; + } + +// register record or begin NAT traversal +mDNSlocal void AdvertiseHostname(mDNS *m, uDNS_HostnameInfo *h) + { + if (IsPrivateV4Addr(&m->uDNS_info.PrimaryIP)) + StartGetPublicAddr(m, h); + else + { + mDNSu8 *ip = m->uDNS_info.PrimaryIP.ip.v4.b; + LogMsg("Advertising %##s IP %d.%d.%d.%d", h->ar->resrec.name.c, ip[0], ip[1], ip[2], ip[3]); + uDNS_RegisterRecord(m, h->ar); + } + } + +mDNSlocal void FoundStaticHostname(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + const domainname *pktname = &answer->rdata->u.name; + domainname *storedname = &m->uDNS_info.StaticHostname; + (void)question; + + debugf("FoundStaticHostname: %##s -> %##s (%s)", question->qname.c, answer->rdata->u.name.c, AddRecord ? "added" : "removed"); + if (AddRecord && !SameDomainName(pktname, storedname)) + { + AssignDomainName(*storedname, *pktname); + UpdateSRVRecords(m); + } + else if (!AddRecord && SameDomainName(pktname, storedname)) + { + storedname->c[0] = 0; + UpdateSRVRecords(m); + } + } + +mDNSlocal void GetStaticHostname(mDNS *m) + { + char buf[MAX_ESCAPED_DOMAIN_NAME]; + DNSQuestion *q = &m->uDNS_info.ReverseMap; + mDNSu8 *ip = m->uDNS_info.PrimaryIP.ip.v4.b; + mStatus err; + + ubzero(q, sizeof(*q)); + + if (m->uDNS_info.ReverseMapActive) + { + uDNS_StopQuery(m, q); + m->uDNS_info.ReverseMapActive = mDNSfalse; + } + + if (!m->uDNS_info.PrimaryIP.ip.v4.NotAnInteger) return; + mDNS_snprintf(buf, MAX_ESCAPED_DOMAIN_NAME, "%d.%d.%d.%d.in-addr.arpa.", ip[3], ip[2], ip[1], ip[0]); + if (!MakeDomainNameFromDNSNameString(&q->qname, buf)) { LogMsg("Error: GetStaticHostname - bad name %s", buf); return; } + + q->InterfaceID = mDNSInterface_Any; + q->Target = zeroAddr; + q->qtype = kDNSType_PTR; + q->qclass = kDNSClass_IN; + q->LongLived = mDNSfalse; + q->ExpectUnique = mDNSfalse; + q->ForceMCast = mDNSfalse; + q->QuestionCallback = FoundStaticHostname; + q->QuestionContext = mDNSNULL; + + err = uDNS_StartQuery(m, q); + if (err) LogMsg("Error: GetStaticHostname - StartQuery returned error %d", err); + else m->uDNS_info.ReverseMapActive = mDNStrue; + } + +// Deregister hostnames and register new names for each host domain with the current global +// values for the hostlabel and primary IP address +mDNSlocal void UpdateHostnameRegistrations(mDNS *m) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + AuthRecord *new; + uDNS_HostnameInfo *i; + + for (i = u->Hostnames; i; i = i->next) + { + // only allocate new record if existing record is actually registered (i.e. it wasn't pending the hostname or IP being set) + if (i->ar->uDNS_info.state == regState_Unregistered) new = i->ar; + else + { + new = umalloc(sizeof(AuthRecord)); + if (!new) { LogMsg("ERROR: UpdateHostnameRegistration - malloc"); return; } + mDNS_SetupResourceRecord(new, mDNSNULL, 0, kDNSType_A, 1, kDNSRecordTypeUnique, HostnameCallback, i); + } + + // setup new record + AssignDomainName(new->resrec.name, i->ar->resrec.name); + new->resrec.rdata->u.ipv4 = u->PrimaryIP.ip.v4; + + if (i->ar->uDNS_info.state != regState_Unregistered) + { + // delete old record + i->ar->RecordContext = mDNSNULL; // clear backpointer to HostnameInfo + uDNS_DeregisterRecord(m, i->ar); + i->ar = new; + } + + // advertise new + AdvertiseHostname(m, i); + } + } + +mDNSexport void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext) { - return (r1->namehash == r2->namehash && - r1->rrtype == r2->rrtype && - SameDomainName(&r1->name, &r2->name) && - SameRData(r1, r2)); + uDNS_GlobalInfo *u = &m->uDNS_info; + uDNS_HostnameInfo *ptr, *new; + + mDNS_Lock(m); + + // check if domain already registered + for (ptr = u->Hostnames; ptr; ptr = ptr->next) + { + if (SameDomainName(fqdn, &ptr->ar->resrec.name)) + { LogMsg("Host Domain %##s already in list", fqdn->c); goto exit; } + } + + // allocate and format new address record + new = umalloc(sizeof(*new)); + if (new) new->ar = umalloc(sizeof(AuthRecord)); + if (!new || !new->ar) { LogMsg("ERROR: mDNS_AddDynDNSHostname - malloc"); goto exit; } + new->StatusCallback = StatusCallback; + new->StatusContext = StatusContext; + mDNS_SetupResourceRecord(new->ar, mDNSNULL, 0, kDNSType_A, 1, kDNSRecordTypeUnique, HostnameCallback, new); + AppendDomainName(&new->ar->resrec.name, fqdn); + new->next = u->Hostnames; + u->Hostnames = new; + if (u->PrimaryIP.ip.v4.NotAnInteger) + { + // only set RData if we have a valid IP + if (u->MappedPrimaryIP.ip.v4.NotAnInteger) new->ar->resrec.rdata->u.ipv4 = u->MappedPrimaryIP.ip.v4; //!!!KRS implement code that caches this + else new->ar->resrec.rdata->u.ipv4 = u->PrimaryIP.ip.v4; + AdvertiseHostname(m, new); + } + else new->ar->uDNS_info.state = regState_Unregistered; +exit: + mDNS_Unlock(m); } +mDNSexport void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + uDNS_HostnameInfo **ptr = &u->Hostnames; + + mDNS_Lock(m); + + while (*ptr && !SameDomainName(fqdn, &(*ptr)->ar->resrec.name)) ptr = &(*ptr)->next; + if (!*ptr) LogMsg("mDNS_RemoveDynDNSHostName: no such domainname %##s", fqdn->c); + else + { + uDNS_HostnameInfo *hi = *ptr; + *ptr = (*ptr)->next; // unlink + hi->ar->RecordContext = mDNSNULL; // about to free wrapper struct + if (hi->ar->uDNS_info.state != regState_Unregistered) uDNS_DeregisterRecord(m, hi->ar); + else { ufree(hi->ar); hi->ar = mDNSNULL; } + ufree(hi); + } + UpdateSRVRecords(m); + mDNS_Unlock(m); + } + +mDNSexport void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *addr, const mDNSAddr *router) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + mDNSBool AddrChanged, RouterChanged; + + if (addr && addr->type !=mDNSAddrType_IPv4) { LogMsg("mDNS_SetPrimaryInterfaceInfo passed non-V4 address. Discarding."); return; } + if (router && router->type !=mDNSAddrType_IPv4) { LogMsg("mDNS_SetPrimaryInterfaceInfo passed non-V4 address. Discarding."); return; } + mDNS_Lock(m); + + AddrChanged = addr ? (addr->ip.v4.NotAnInteger != u->PrimaryIP.ip.v4.NotAnInteger) : u->PrimaryIP.ip.v4.NotAnInteger; + RouterChanged = router ? (router->ip.v4.NotAnInteger != u->Router.ip.v4.NotAnInteger) : u->Router.ip.v4.NotAnInteger; + +#if MDNS_DEBUGMSGS + if (addr && (AddrChanged || RouterChanged)) + LogMsg("mDNS_SetPrimaryInterfaceInfo: address changed from %d.%d.%d.%d to %d.%d.%d.%d:%d", + u->PrimaryIP.ip.v4.b[0], u->PrimaryIP.ip.v4.b[1], u->PrimaryIP.ip.v4.b[2], u->PrimaryIP.ip.v4.b[3], + addr->ip.v4.b[0], addr->ip.v4.b[1], addr->ip.v4.b[2], addr->ip.v4.b[3], mDNSVal16(m->UnicastPort4)); +#endif // MDNS_DEBUGMSGS + + if (addr) u->PrimaryIP = *addr; + if (router) u->Router = *router; + else u->Router.ip.v4.NotAnInteger = 0; // setting router to zero indicates that nat mappings must be reestablished when router is reset + + if (AddrChanged) + { + if (addr) + { + UpdateHostnameRegistrations(m); + UpdateSRVRecords(m); + } + GetStaticHostname(m); // look up reverse map record to find any static hostnames for our IP address + } + + mDNS_Unlock(m); + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Incoming Message Processing +#endif + mDNSlocal mDNSBool kaListContainsAnswer(DNSQuestion *question, CacheRecord *rr) { CacheRecord *ptr; for (ptr = question->uDNS_info.knownAnswers; ptr; ptr = ptr->next) - if (sameResourceRecord(&ptr->resrec, &rr->resrec)) return mDNStrue; + if (SameResourceRecord(&ptr->resrec, &rr->resrec)) return mDNStrue; return mDNSfalse; } @@ -597,11 +1648,11 @@ mDNSlocal mDNSBool kaListContainsAnswer(DNSQuestion *question, CacheRecord *rr) mDNSlocal void removeKnownAnswer(DNSQuestion *question, CacheRecord *rr) { - CacheRecord *ptr, *prev = NULL; + CacheRecord *ptr, *prev = mDNSNULL; for (ptr = question->uDNS_info.knownAnswers; ptr; ptr = ptr->next) { - if (sameResourceRecord(&ptr->resrec, &rr->resrec)) + if (SameResourceRecord(&ptr->resrec, &rr->resrec)) { if (prev) prev->next = ptr->next; else question->uDNS_info.knownAnswers = ptr->next; @@ -616,7 +1667,7 @@ mDNSlocal void removeKnownAnswer(DNSQuestion *question, CacheRecord *rr) mDNSlocal void addKnownAnswer(DNSQuestion *question, const CacheRecord *rr) { - CacheRecord *newCR = NULL; + CacheRecord *newCR = mDNSNULL; mDNSu32 size; size = sizeof(CacheRecord) + rr->resrec.rdlength - InlineCacheRDSize; @@ -633,7 +1684,7 @@ mDNSlocal void deriveGoodbyes(mDNS * const m, DNSMessage *msg, const mDNSu8 *en { const mDNSu8 *ptr; int i; - CacheRecord *fptr, *ka, *cr, *answers = NULL, *prev = NULL; + CacheRecord *fptr, *ka, *cr, *answers = mDNSNULL, *prev = mDNSNULL; LargeCacheRecord *lcr; if (question != m->uDNS_info.CurrentQuery) { LogMsg("ERROR: deriveGoodbyes called without CurrentQuery set!"); return; } @@ -647,8 +1698,11 @@ mDNSlocal void deriveGoodbyes(mDNS * const m, DNSMessage *msg, const mDNSu8 *en ka = question->uDNS_info.knownAnswers; while (ka) { - debugf("deriving goodbye for %s", ka->resrec.name.c); + debugf("deriving goodbye for %##s", ka->resrec.name.c); + + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback question->QuestionCallback(m, question, &ka->resrec, mDNSfalse); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again if (question != m->uDNS_info.CurrentQuery) { debugf("deriveGoodbyes - question removed via callback. returning."); @@ -658,7 +1712,7 @@ mDNSlocal void deriveGoodbyes(mDNS * const m, DNSMessage *msg, const mDNSu8 *en ka = ka->next; ufree(fptr); } - question->uDNS_info.knownAnswers = NULL; + question->uDNS_info.knownAnswers = mDNSNULL; return; } @@ -684,14 +1738,16 @@ mDNSlocal void deriveGoodbyes(mDNS * const m, DNSMessage *msg, const mDNSu8 *en while (ka) { for (cr = answers; cr; cr = cr->next) - { if (sameResourceRecord(&ka->resrec, &cr->resrec)) break; } + { if (SameResourceRecord(&ka->resrec, &cr->resrec)) break; } if (!cr) { // record is in KA list but not answer list - remove from KA list if (prev) prev->next = ka->next; else question->uDNS_info.knownAnswers = ka->next; - debugf("deriving goodbye for %s", ka->resrec.name.c); + debugf("deriving goodbye for %##s", ka->resrec.name.c); + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback question->QuestionCallback(m, question, &ka->resrec, mDNSfalse); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again if (question != m->uDNS_info.CurrentQuery) { debugf("deriveGoodbyes - question removed via callback. returning."); @@ -715,7 +1771,7 @@ mDNSlocal void deriveGoodbyes(mDNS * const m, DNSMessage *msg, const mDNSu8 *en return; pkt_error: - LogMsg("ERROR: deriveGoodbyes - received malformed response to query for %s (%d)", + LogMsg("ERROR: deriveGoodbyes - received malformed response to query for %##s (%d)", question->qname.c, question->qtype); return; @@ -729,12 +1785,15 @@ mDNSlocal void pktResponseHndlr(mDNS * const m, DNSMessage *msg, const mDNSu8 * int i; LargeCacheRecord lcr; CacheRecord *cr = &lcr.r; - mDNSBool goodbye, inKAList; + mDNSBool goodbye, inKAList, followedCName = mDNSfalse; LLQ_Info *llqInfo = question->uDNS_info.llq; + domainname origname; if (question != m->uDNS_info.CurrentQuery) { LogMsg("ERROR: pktResponseHdnlr called without CurrentQuery ptr set!"); return; } - + + question->uDNS_info.Answered = mDNStrue; + ptr = LocateAnswers(msg, end); if (!ptr) goto pkt_error; @@ -744,32 +1803,53 @@ mDNSlocal void pktResponseHndlr(mDNS * const m, DNSMessage *msg, const mDNSu8 * if (!ptr) goto pkt_error; if (ResourceRecordAnswersQuestion(&cr->resrec, question)) { + if (cr->resrec.rrtype == kDNSType_CNAME) + { + if (followedCName) LogMsg("Error: multiple CNAME referals for question %##s", question->qname.c); + else + { + debugf("Following cname %##s -> %##s", question->qname.c, cr->resrec.rdata->u.name.c); + AssignDomainName(origname, question->qname); + AssignDomainName(question->qname, cr->resrec.rdata->u.name); + question->qnamehash = DomainNameHashValue(&question->qname); + followedCName = mDNStrue; + i = -1; // restart packet answer matching + ptr = LocateAnswers(msg, end); + continue; + } + } + goodbye = llq ? ((mDNSs32)cr->resrec.rroriginalttl == -1) : mDNSfalse; inKAList = kaListContainsAnswer(question, cr); if ((goodbye && !inKAList) || (!goodbye && inKAList)) continue; // list up to date if (!inKAList) addKnownAnswer(question, cr); if (goodbye) removeKnownAnswer(question, cr); + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback question->QuestionCallback(m, question, &cr->resrec, !goodbye); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again if (question != m->uDNS_info.CurrentQuery) { debugf("pktResponseHndlr - CurrentQuery changed by QuestionCallback - returning"); return; } } - else - { - LogMsg("unexpected answer: %s", cr->resrec.name.c); - } + else if (!followedCName || !SameDomainName(&cr->resrec.name, &origname)) + LogMsg("Question %##s type %d - unexpected answer %##s type %d", question->qname.c, question->qtype, cr->resrec.name.c, cr->resrec.rrtype); + } + + if (!llq || llqInfo->state == LLQ_Poll || llqInfo->deriveRemovesOnResume) + { + deriveGoodbyes(m, msg, end,question); + if (llq && llqInfo->deriveRemovesOnResume) llqInfo->deriveRemovesOnResume = mDNSfalse; } - if (llq && (llqInfo->state == LLQ_Poll || llqInfo->deriveRemovesOnResume)) - { deriveGoodbyes(m, msg, end,question); llqInfo->deriveRemovesOnResume = mDNSfalse; } - //!!!KRS should we derive goodbyes for non-LLQs? + // our interval may be set lower to recover from failures - now that we have an answer, fully back off retry + if (question->ThisQInterval < MAX_UCAST_POLL_INTERVAL) question->ThisQInterval = MAX_UCAST_POLL_INTERVAL; return; pkt_error: - LogMsg("ERROR: pktResponseHndlr - received malformed response to query for %s (%d)", + LogMsg("ERROR: pktResponseHndlr - received malformed response to query for %##s (%d)", question->qname.c, question->qtype); return; } @@ -786,134 +1866,248 @@ mDNSlocal void llqResponseHndlr(mDNS * const m, DNSMessage *msg, const mDNSu8 * pktResponseHndlr(m, msg, end, question, mDNStrue); } - - -mDNSlocal void unlinkSRS(uDNS_GlobalInfo *u, ServiceRecordSet *srs) +mDNSlocal mStatus ParseTSIGError(mDNS *m, const DNSMessage *msg, const mDNSu8 *end, const domainname *displayname) { - ServiceRecordSet *ptr, *prev = NULL; + LargeCacheRecord lcr; + const mDNSu8 *ptr; + mStatus err = mStatus_NoError; + int i; + + ptr = LocateAdditionals(msg, end); + if (!ptr) goto finish; - for (ptr = u->ServiceRegistrations; ptr; ptr = ptr->next) + for (i = 0; i < msg->h.numAdditionals; i++) { - if (ptr == srs) + ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr);; + if (!ptr) goto finish; + if (lcr.r.resrec.rrtype == kDNSType_TSIG) { - if (prev) prev->next = ptr->next; - else u->ServiceRegistrations = ptr->next; - ptr->next = NULL; - return; + mDNSu32 macsize; + mDNSu8 *rd = lcr.r.resrec.rdata->u.data; + mDNSu8 *rdend = rd + MaximumRDSize; + int alglen = DomainNameLength(&lcr.r.resrec.rdata->u.name); + + if (rd + alglen > rdend) goto finish; + rd += alglen; // algorithm name + if (rd + 6 > rdend) goto finish; + rd += 6; // 48-bit timestamp + if (rd + sizeof(mDNSOpaque16) > rdend) goto finish; + rd += sizeof(mDNSOpaque16); // fudge + if (rd + sizeof(mDNSOpaque16) > rdend) goto finish; + macsize = mDNSVal16(*(mDNSOpaque16 *)rd); + rd += sizeof(mDNSOpaque16); // MAC size + if (rd + macsize > rdend) goto finish; + rd += macsize; + if (rd + sizeof(mDNSOpaque16) > rdend) goto finish; + rd += sizeof(mDNSOpaque16); // orig id + if (rd + sizeof(mDNSOpaque16) > rdend) goto finish; + err = mDNSVal16(*(mDNSOpaque16 *)rd); // error code + + if (err == TSIG_ErrBadSig) { LogMsg("%##s: bad signature", displayname->c); err = mStatus_BadSig; } + else if (err == TSIG_ErrBadKey) { LogMsg("%##s: bad key", displayname->c); err = mStatus_BadKey; } + else if (err == TSIG_ErrBadTime) { LogMsg("%##s: bad time", displayname->c); err = mStatus_BadTime; } + else if (err) { LogMsg("%##s: unknown tsig error %d", err); err = mStatus_UnknownErr; } + goto finish; } - prev = ptr; } - LogMsg("ERROR: unlinkSRS - SRS not found in ServiceRegistrations list"); + + finish: + return err; } - -mDNSlocal mStatus checkUpdateResult(domainname *name, mDNSu8 rcode, const DNSMessage *msg) - { +mDNSlocal mStatus checkUpdateResult(domainname *displayname, mDNSu8 rcode, mDNS *m, const DNSMessage *msg, const mDNSu8 *end) + { (void)msg; // currently unused, needed for TSIG errors if (!rcode) return mStatus_NoError; else if (rcode == kDNSFlag1_RC_YXDomain) { - LogMsg("Name in use: %s", name->c); + LogMsg("name in use: %##s", displayname->c); return mStatus_NameConflict; } else if (rcode == kDNSFlag1_RC_Refused) { - LogMsg("Update %s refused", name->c); - return mStatus_Refused; + LogMsg("Update %##s refused", displayname->c); + return mStatus_Refused; } else if (rcode == kDNSFlag1_RC_NXRRSet) { - LogMsg("Reregister refusted (NXRRSET): %s", name->c); + LogMsg("Reregister refused (NXRRSET): %##s", displayname->c); return mStatus_NoSuchRecord; } else if (rcode == kDNSFlag1_RC_NotAuth) { - LogMsg("Permission denied (NOAUTH): %s", name->c); - return mStatus_NoAuth; + // TSIG errors should come with FmtErr as per RFC 2845, but BIND 9 sends them with NotAuth so we look here too + mStatus tsigerr = ParseTSIGError(m, msg, end, displayname); + if (!tsigerr) + { + LogMsg("Permission denied (NOAUTH): %##s", displayname->c); + return mStatus_UnknownErr; + } + else return tsigerr; } else if (rcode == kDNSFlag1_RC_FmtErr) { - LogMsg("Format Error: %s", name->c); - return mStatus_UnknownErr; - //!!!KRS need to parse message for TSIG errors + mStatus tsigerr = ParseTSIGError(m, msg, end, displayname); + if (!tsigerr) + { + LogMsg("Format Error: %##s", displayname->c); + return mStatus_UnknownErr; + } + else return tsigerr; } else { - LogMsg("Update %s failed with rcode %d", name->c, rcode); + LogMsg("Update %##s failed with rcode %d", displayname->c, rcode); return mStatus_UnknownErr; } } mDNSlocal void hndlServiceUpdateReply(mDNS * const m, ServiceRecordSet *srs, mStatus err) { - //!!!KRS make sure we're doing the right thing w/ MemFree + mDNSBool InvokeCallback = mDNSfalse; + AuthRecord *UpdateR = mDNSNULL; + uDNS_RegInfo *info = &srs->uDNS_info; + NATTraversalInfo *nat = srs->uDNS_info.NATinfo; + ExtraResourceRecord **e = &srs->Extras; - switch (srs->uDNS_info.state) + switch (info->state) { case regState_Pending: - case regState_Refresh: - if (err) + if (err == mStatus_NameConflict && !info->TestForSelfConflict) { - if (srs->uDNS_info.lease && err == mStatus_UnknownErr) - { - LogMsg("Re-trying update of service %s without lease option", srs->RR_SRV.resrec.name.c); - srs->uDNS_info.lease = mDNSfalse; - srs->uDNS_info.expire = -1; - SendServiceRegistration(m, srs); - return; - } - else - { - LogMsg("hndlServiceUpdateReply: Error %d returned for registration of %s", - err, srs->RR_SRV.resrec.name.c); - srs->uDNS_info.state = regState_Unregistered; - break; - } + info->TestForSelfConflict = mDNStrue; + debugf("checking for self-conflict of service %##s", srs->RR_SRV.resrec.name.c); + SendServiceRegistration(m, srs); + return; + } + else if (info->TestForSelfConflict) + { + info->TestForSelfConflict = mDNSfalse; + if (err == mStatus_NoSuchRecord) err = mStatus_NameConflict; // NoSuchRecord implies that our prereq was not met, so we actually have a name conflict + if (err) info->state = regState_Unregistered; + else info->state = regState_Registered; + InvokeCallback = mDNStrue; + break; + } + else if (err == mStatus_UnknownErr && info->lease) + { + LogMsg("Re-trying update of service %##s without lease option", srs->RR_SRV.resrec.name.c); + info->lease = mDNSfalse; + info->expire = -1; + SendServiceRegistration(m, srs); + return; } else { - if (srs->uDNS_info.state == regState_Refresh) - { - srs->uDNS_info.state = regState_Registered; - return; - } - srs->uDNS_info.state = regState_Registered; + if (err) { LogMsg("Error %ld for registration of service %##s", err, srs->RR_SRV.resrec.name.c); info->state = regState_Unregistered; } //!!!KRS make sure all structs will still get cleaned up when client calls DeregisterService with this state + else info->state = regState_Registered; + InvokeCallback = mDNStrue; break; - } + } + case regState_Refresh: + if (err) + { + LogMsg("Error %ld for refresh of service %##s", err, srs->RR_SRV.resrec.name.c); + InvokeCallback = mDNStrue; + info->state = regState_Unregistered; + } + else info->state = regState_Registered; + break; case regState_DeregPending: - if (err) LogMsg("hndlServiceUpdateReply: Error %d returned for dereg of %s", - err, srs->RR_SRV.resrec.name.c); - else err = mStatus_MemFree; + if (err) LogMsg("Error %ld for deregistration of service %##s", err, srs->RR_SRV.resrec.name.c); + if (info->LostTarget) + { + info->state = regState_NoTarget; + break; + } + err = mStatus_MemFree; + InvokeCallback = mDNStrue; + if (nat) + { + if (nat->state == NATState_Deleted) { FreeNATInfo(m, nat); info->NATinfo = mDNSNULL; } // deletion copmleted + else nat->reg.ServiceRegistration = mDNSNULL; // allow mapping deletion to continue + } + info->state = regState_Unregistered; break; case regState_DeregDeferred: - if (err) LogMsg("hndlServiceUpdateReply: Error %d received prior to deferred derigstration of %s", - err, srs->RR_SRV.resrec.name.c); - LogMsg("Performing deferred deregistration of %s", srs->RR_SRV.resrec.name.c); - uDNS_DeregisterService(m, srs); - return; - case regState_TargetChange: + if (err) { debugf("Error %ld received prior to deferred derigstration of %##s", err, srs->RR_SRV.resrec.name.c); } + debugf("Performing deferred deregistration of %##s", srs->RR_SRV.resrec.name.c); + info->state = regState_Registered; // benign to set even if we've got an error + SendServiceDeregistration(m, srs); + return; + case regState_UpdatePending: + // find the record being updated + UpdateR = &srs->RR_TXT; if (err) { - LogMsg("hdnlServiceUpdateReply: Error %d returned for host target update of %s", - err, srs->RR_SRV.resrec.name.c); - srs->uDNS_info.state = regState_Unregistered; - // !!!KRS we are leaving the ptr/txt records registered + LogMsg("hndlServiceUpdateReply: error updating resource record"); + UpdateR->uDNS_info.state = regState_Unregistered; + InvokeCallback = mDNStrue; // signal error via service callback } - else srs->uDNS_info.state = regState_Registered; + else + { + UpdateR->uDNS_info.state = regState_Registered; + SwapRData(m, UpdateR, mDNStrue); + } + info->state = regState_Registered; break; - default: - LogMsg("hndlServiceUpdateReply called for service %s in unexpected state %d with error %d. Unlinking.", - srs->RR_SRV.resrec.name.c, srs->uDNS_info.state, err); + case regState_FetchingZoneData: + case regState_Registered: + case regState_Cancelled: + case regState_Unregistered: + case regState_NATMap: + case regState_NoTarget: + case regState_ExtraQueued: + LogMsg("hndlServiceUpdateReply called for service %##s in unexpected state %d with error %ld. Unlinking.", + srs->RR_SRV.resrec.name.c, info->state, err); err = mStatus_UnknownErr; } - if (err) + if ((info->LostTarget || info->SRVUpdateDeferred) && (info->state == regState_NoTarget || info->state == regState_Registered)) { - unlinkSRS(&m->uDNS_info, srs); // name conflicts, force dereg, and errors - srs->uDNS_info.state = regState_Unregistered; + UpdateSRV(m, srs); + return; } - - srs->ServiceCallback(m, srs, err); + + while (*e) + { + uDNS_RegInfo *einfo = &(*e)->r.uDNS_info; + if (einfo->state == regState_ExtraQueued) + { + if (info->state == regState_Registered && !err) + { + // extra resource record queued for this service - copy zone info and register + AssignDomainName(einfo->zone, info->zone); + einfo->ns = info->ns; + einfo->port = info->port; + einfo->lease = info->lease; + sendRecordRegistration(m, &(*e)->r); + e = &(*e)->next; + } + else if (err && einfo->state != regState_Unregistered) + { + // unlink extra from list + einfo->state = regState_Unregistered; + *e = (*e)->next; + } + else e = &(*e)->next; + } + else e = &(*e)->next; + } + + srs->RR_SRV.ThisAPInterval = INIT_UCAST_POLL_INTERVAL - 1; // reset retry delay for future refreshes, dereg, etc. + if (srs->RR_TXT.uDNS_info.UpdateQueued) SendRecordUpdate(m, &srs->RR_TXT, &srs->uDNS_info); + + if (info->state == regState_Unregistered) unlinkSRS(&m->uDNS_info, srs); + + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + if (InvokeCallback) srs->ServiceCallback(m, srs, err); + else if (info->ClientCallbackDeferred) + { + info->ClientCallbackDeferred = mDNSfalse; + srs->ServiceCallback(m, srs, info->DeferredStatus); + } + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again // NOTE: do not touch structures after calling ServiceCallback } @@ -921,16 +2115,37 @@ mDNSlocal void hndlRecordUpdateReply(mDNS *m, AuthRecord *rr, mStatus err) { uDNS_GlobalInfo *u = &m->uDNS_info; + if (rr->uDNS_info.state == regState_UpdatePending) + { + if (err) + { + LogMsg("Update record failed for %##s (err %d)", rr->resrec.name.c, err); + rr->uDNS_info.state = regState_Unregistered; + } + else + { + debugf("Update record %##s - success", rr->resrec.name.c); + rr->uDNS_info.state = regState_Registered; + SwapRData(m, rr, mDNStrue); + } + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + if (rr->RecordCallback) rr->RecordCallback(m, rr, err); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + return; + } + if (rr->uDNS_info.state == regState_DeregPending) { - debugf("Received reply for deregister record %s type %d", rr->resrec.name.c, rr->resrec.rrtype); - if (err) LogMsg("ERROR: Deregistration of record %s type %s failed with error %d", + debugf("Received reply for deregister record %##s type %d", rr->resrec.name.c, rr->resrec.rrtype); + if (err) LogMsg("ERROR: Deregistration of record %##s type %d failed with error %ld", rr->resrec.name.c, rr->resrec.rrtype, err); - else err = mStatus_MemFree; + err = mStatus_MemFree; if (unlinkAR(&m->uDNS_info.RecordRegistrations, rr)) LogMsg("ERROR: Could not unlink resource record following deregistration"); rr->uDNS_info.state = regState_Unregistered; - rr->RecordCallback(m, rr, err); + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + if (rr->RecordCallback) rr->RecordCallback(m, rr, err); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again return; } @@ -938,13 +2153,13 @@ mDNSlocal void hndlRecordUpdateReply(mDNS *m, AuthRecord *rr, mStatus err) { if (err) { - LogMsg("Cancelling deferred deregistration record %s type %d due to registration error %d", + LogMsg("Cancelling deferred deregistration record %##s type %d due to registration error %ld", rr->resrec.name.c, rr->resrec.rrtype, err); unlinkAR(&m->uDNS_info.RecordRegistrations, rr); rr->uDNS_info.state = regState_Unregistered; return; } - LogMsg("Calling deferred deregistration of record %s type %d", + LogMsg("Calling deferred deregistration of record %##s type %d", rr->resrec.name.c, rr->resrec.rrtype); rr->uDNS_info.state = regState_Registered; uDNS_DeregisterRecord(m, rr); @@ -957,32 +2172,45 @@ mDNSlocal void hndlRecordUpdateReply(mDNS *m, AuthRecord *rr, mStatus err) { if (rr->uDNS_info.lease && err == mStatus_UnknownErr) { - LogMsg("Re-trying update of record %s without lease option", rr->resrec.name.c); + LogMsg("Re-trying update of record %##s without lease option", rr->resrec.name.c); rr->uDNS_info.lease = mDNSfalse; rr->uDNS_info.expire = -1; sendRecordRegistration(m, rr); return; } - LogMsg("Registration of record %s type %d failed with error %d", + LogMsg("Registration of record %##s type %d failed with error %ld", rr->resrec.name.c, rr->resrec.rrtype, err); unlinkAR(&u->RecordRegistrations, rr); rr->uDNS_info.state = regState_Unregistered; + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + if (rr->RecordCallback) rr->RecordCallback(m, rr, err); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + return; } else { + if (rr->uDNS_info.UpdateQueued) + { + debugf("%##s: sending queued update", rr->resrec.name.c); + rr->uDNS_info.state = regState_Registered; + SendRecordUpdate(m ,rr, &rr->uDNS_info); + return; + } if (rr->uDNS_info.state == regState_Refresh) rr->uDNS_info.state = regState_Registered; else { rr->uDNS_info.state = regState_Registered; - rr->RecordCallback(m, rr, err); + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + if (rr->RecordCallback) rr->RecordCallback(m, rr, err); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again } return; } } - LogMsg("Received unexpected response for record %s type %d, in state %d, with response error %d", + LogMsg("Received unexpected response for record %##s type %d, in state %d, with response error %ld", rr->resrec.name.c, rr->resrec.rrtype, rr->uDNS_info.state, err); } @@ -993,7 +2221,8 @@ mDNSlocal void SetUpdateExpiration(mDNS *m, DNSMessage *msg, const mDNSu8 *end, const mDNSu8 *ptr; int i; mDNSu32 lease = 0; - + mDNSs32 expire; + ptr = LocateAdditionals(msg, end); if (info->lease && (ptr = LocateAdditionals(msg, end))) @@ -1013,13 +2242,43 @@ mDNSlocal void SetUpdateExpiration(mDNS *m, DNSMessage *msg, const mDNSu8 *end, } if (lease > 0) - info->expire = (mDNSPlatformTimeNow() + (((mDNSs32)lease * mDNSPlatformOneSecond)) * 3/4); + { + expire = (mDNSPlatformTimeNow(m) + (((mDNSs32)lease * mDNSPlatformOneSecond)) * 3/4); + if (info->state == regState_UpdatePending) + // if updating individual record, the service record set may expire sooner + { if (expire - info->expire < 0) info->expire = expire; } + else info->expire = expire; + } else info->expire = -1; } +mDNSexport void uDNS_ReceiveNATMap(mDNS *m, mDNSu8 *pkt, mDNSu16 len) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + NATTraversalInfo *ptr, *cur; + NATOp_t op; + + // check length, version, opcode + if (len < ADDR_REPLY_PKTLEN && len < PORTMAP_PKTLEN) { LogMsg("NAT Traversal message too short (%d bytes)", len); return; } + if (pkt[0] != NATMAP_VERS) { LogMsg("Received NAT Traversal response with version %d (expect version %d)", pkt[0], NATMAP_VERS); return; } + op = pkt[1]; + if (!(op & NATMAP_RESPONSE_MASK)) { LogMsg("Received NAT Traversal message that is not a response (opcode %d)", op); return; } + + ptr = u->NATTraversals; + while (ptr) + { + cur = ptr; + ptr = ptr->next; + if ((cur->state == NATState_Request || cur->state == NATState_Refresh) && + (cur->op | NATMAP_RESPONSE_MASK) == op) + cur->ReceiveResponse(cur, m, pkt, len); // callback may delete "cur" + // specific request/reply matching logic handled by callback - we don't know if this was a match, so we don't break here + } + } + mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, - const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, mDNSu8 ttl) + const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID) { DNSQuestion *qptr; AuthRecord *rptr; @@ -1028,35 +2287,37 @@ mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNS uDNS_GlobalInfo *u = &m->uDNS_info; mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; - mDNSu8 UpdateR = kDNSFlag0_OP_Update | kDNSFlag0_QR_Response; + mDNSu8 UpdateR = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update; mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask); mDNSu8 rcode = (mDNSu8)(msg->h.flags.b[1] & kDNSFlag1_RC); - + + mDNSs32 timenow = mDNSPlatformTimeNow(m); + // unused - (void)srcaddr; - (void)srcport; (void)dstaddr; (void)dstport; - (void)ttl; (void)InterfaceID; if (QR_OP == StdR) { // !!!KRS we should to a table lookup here to see if it answers an LLQ or a 1-shot - if (recvLLQResponse(m, msg, end, srcaddr, srcport, InterfaceID)) return; + // LLQ Responses over TCP not currently supported + if (srcaddr && recvLLQResponse(m, msg, end, srcaddr, srcport, InterfaceID)) return; for (qptr = u->ActiveQueries; qptr; qptr = qptr->next) { //!!!KRS we should have a hashtable, hashed on message id if (qptr->uDNS_info.id.NotAnInteger == msg->h.id.NotAnInteger) { + if (timenow - (qptr->LastQTime + RESPONSE_WINDOW) > 0) + { LogMsg("uDNS_ReceiveMsg - response received after maximum allowed window. Discarding"); return; } if (msg->h.flags.b[0] & kDNSFlag0_TC) { hndlTruncatedAnswer(qptr, srcaddr, m); return; } else { u->CurrentQuery = qptr; qptr->uDNS_info.responseCallback(m, msg, end, qptr, qptr->uDNS_info.context); - u->CurrentQuery = NULL; + u->CurrentQuery = mDNSNULL; // Note: responseCallback can invalidate qptr return; } @@ -1069,7 +2330,7 @@ mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNS { if (sptr->uDNS_info.id.NotAnInteger == msg->h.id.NotAnInteger) { - err = checkUpdateResult(&sptr->RR_SRV.resrec.name, rcode, msg); + err = checkUpdateResult(&sptr->RR_SRV.resrec.name, rcode, m, msg, end); if (!err) SetUpdateExpiration(m, msg, end, &sptr->uDNS_info); hndlServiceUpdateReply(m, sptr, err); return; @@ -1079,7 +2340,7 @@ mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNS { if (rptr->uDNS_info.id.NotAnInteger == msg->h.id.NotAnInteger) { - err = checkUpdateResult(&rptr->resrec.name, rcode, msg); + err = checkUpdateResult(&rptr->resrec.name, rcode, m, msg, end); if (!err) SetUpdateExpiration(m, msg, end, &rptr->uDNS_info); hndlRecordUpdateReply(m, rptr, err); return; @@ -1089,28 +2350,35 @@ mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNS debugf("Received unexpected response: ID %d matches no active records", mDNSVal16(msg->h.id)); } - -mDNSlocal void receiveMsg(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, - const mDNSInterfaceID InterfaceID) - { - mDNSAddr *sa = NULL, *da = NULL; - mDNSIPPort sp, dp; - mDNSu8 ttl = 0; - - sp.NotAnInteger = 0; - dp.NotAnInteger = 0; - uDNS_ReceiveMsg(m, msg, end, sa, sp, da, dp, InterfaceID, ttl); - } - -//!!!KRS this should go away (don't just pick one randomly!) -mDNSlocal const mDNSAddr *getInitializedDNS(uDNS_GlobalInfo *u) +// lookup a DNS Server, matching by name in split-dns configurations. Result stored in addr parameter if successful +mDNSlocal mDNSBool GetServerForName(uDNS_GlobalInfo *u, const domainname *name, mDNSAddr *addr) { - int i; - for (i = 0; i < 32; i++) - if (u->Servers[i].ip.v4.NotAnInteger) return &u->Servers[i]; + DNSServer *curmatch = mDNSNULL, *p = u->Servers; + int i, ncount, scount, curmatchlen = -1; - return NULL; - } + *addr = zeroAddr; + ncount = name ? CountLabels(name) : 0; + while (p) + { + scount = CountLabels(&p->domain); + if (scount <= ncount && scount > curmatchlen) + { + // only inspect if server's domain is longer than current best match and shorter than the name itself + const domainname *tail = name; + for (i = 0; i < ncount - scount; i++) + tail = (domainname *)(tail->c + 1 + tail->c[0]); // find "tail" (scount labels) of name + if (SameDomainName(tail, &p->domain)) { curmatch = p; curmatchlen = scount; } + } + p = p->next; + } + + if (curmatch) + { + *addr = curmatch->addr; + return mDNStrue; + } + else return mDNSfalse; + } // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -1151,7 +2419,7 @@ mDNSlocal mDNSu8 *putLLQ(DNSMessage *const msg, mDNSu8 *ptr, DNSQuestion *questi if (includeQuestion) { ptr = putQuestion(msg, ptr, msg->data + AbsoluteMaxDNSMessageData, &question->qname, question->qtype, question->qclass); - if (!ptr) { LogMsg("ERROR: putLLQ - putQuestion"); return NULL; } + if (!ptr) { LogMsg("ERROR: putLLQ - putQuestion"); return mDNSNULL; } } // locate OptRR if it exists, set pointer to end // !!!KRS implement me @@ -1170,8 +2438,8 @@ mDNSlocal mDNSu8 *putLLQ(DNSMessage *const msg, mDNSu8 *ptr, DNSQuestion *questi optRD->opt = kDNSOpt_LLQ; optRD->optlen = sizeof(LLQOptData); umemcpy(&optRD->OptData.llq, data, sizeof(LLQOptData)); - ptr = PutResourceRecordTTL(msg, ptr, &msg->h.numAdditionals, opt, 0); - if (!ptr) { LogMsg("ERROR: putLLQ - PutResourceRecordTTL"); return NULL; } + ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.numAdditionals, opt, 0); + if (!ptr) { LogMsg("ERROR: putLLQ - PutResourceRecordTTLJumbo"); return mDNSNULL; } return ptr; } @@ -1193,7 +2461,7 @@ mDNSlocal mDNSBool getLLQAtIndex(mDNS *m, DNSMessage *msg, const mDNSu8 *end, LL { ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr); if (!ptr) return mDNSfalse; if (lcr.r.resrec.rrtype == kDNSType_OPT) break; } if (lcr.r.resrec.rrtype != kDNSType_OPT) return mDNSfalse; if (lcr.r.resrec.rdlength < (index + 1) * LLQ_OPT_SIZE) return mDNSfalse; // rdata too small - umemcpy(llq, (mDNSu8 *)&lcr.r.resrec.rdata->u.opt.OptData.llq + (index * sizeof(LLQOptData)), sizeof(LLQOptData)); + umemcpy(llq, (mDNSu8 *)&lcr.r.resrec.rdata->u.opt.OptData.llq + (index * sizeof(LLQOptData)), sizeof(LLQOptData)); // !!! Should convert to host byte order? return mDNStrue; } @@ -1204,12 +2472,12 @@ mDNSlocal void recvRefreshReply(mDNS *m, DNSMessage *msg, const mDNSu8 *end, DNS qInfo = q->uDNS_info.llq; if (!getLLQAtIndex(m, msg, end, &pktData, 0)) { LogMsg("ERROR recvRefreshReply - getLLQAtIndex"); return; } - if (pktData.llqOp != kLLQ_Refresh) return; + if (pktData.llqOp != kLLQOp_Refresh) return; if (!sameID(pktData.id, qInfo->id)) { LogMsg("recvRefreshReply - ID mismatch. Discarding"); return; } if (pktData.err != LLQErr_NoError) { LogMsg("recvRefreshReply: received error %d from server", pktData.err); return; } - qInfo->expire = mDNSPlatformTimeNow() + ((mDNSs32)pktData.lease * mDNSPlatformOneSecond); - qInfo->retry = qInfo->expire + ((mDNSs32)pktData.lease * mDNSPlatformOneSecond * 3/4); + qInfo->expire = mDNSPlatformTimeNow(m) + ((mDNSs32)pktData.lease * mDNSPlatformOneSecond); + qInfo->retry = qInfo->expire - ((mDNSs32)pktData.lease * mDNSPlatformOneSecond/2); qInfo->origLease = pktData.lease; qInfo->state = LLQ_Established; @@ -1222,19 +2490,22 @@ mDNSlocal void sendLLQRefresh(mDNS *m, DNSQuestion *q, mDNSu32 lease) LLQOptData llq; LLQ_Info *info = q->uDNS_info.llq; mStatus err; + mDNSs32 timenow; - if (info->state == kLLQ_Refresh && info->ntries >= kLLQ_MAX_TRIES) + timenow = mDNSPlatformTimeNow(m); + if ((info->state == LLQ_Refresh && info->ntries >= kLLQ_MAX_TRIES) || + info->expire - timenow < 0) { - LogMsg("sendLLQRefresh - %d failed attempts for llq %s", info->ntries, q->qname.c); + LogMsg("Unable to refresh LLQ %##s - will retry in %d minutes", q->qname.c, kLLQ_DEF_RETRY/60); info->state = LLQ_Retry; - info->retry = mDNSPlatformTimeNow() + kLLQ_DEF_RETRY * mDNSPlatformOneSecond; + info->retry = mDNSPlatformTimeNow(m) + kLLQ_DEF_RETRY * mDNSPlatformOneSecond; info->deriveRemovesOnResume = mDNStrue; return; //!!!KRS handle this - periodically try to re-establish } - + llq.vers = kLLQ_Vers; - llq.llqOp = kLLQ_Refresh; + llq.llqOp = kLLQOp_Refresh; llq.err = LLQErr_NoError; umemcpy(llq.id, info->id, 8); llq.lease = lease; @@ -1243,34 +2514,44 @@ mDNSlocal void sendLLQRefresh(mDNS *m, DNSQuestion *q, mDNSu32 lease) end = putLLQ(&msg, msg.data, q, &llq, mDNStrue); if (!end) { LogMsg("ERROR: sendLLQRefresh - putLLQ"); return; } - err = mDNSSendDNSMessage(m, &msg, end, q->InterfaceID, &info->servAddr, info->servPort); - if (err) LogMsg("ERROR: sendLLQRefresh - mDNSSendDNSMessage returned %d", err); + err = mDNSSendDNSMessage(m, &msg, end, mDNSInterface_Any, &info->servAddr, info->servPort, -1, mDNSNULL); + if (err) LogMsg("ERROR: sendLLQRefresh - mDNSSendDNSMessage returned %ld", err); if (info->state == LLQ_Established) info->ntries = 1; else info->ntries++; info->state = LLQ_Refresh; - q->LastQTime = mDNSPlatformTimeNow(); + q->LastQTime = timenow; info->retry = (info->expire - q->LastQTime) / 2; } -mDNSlocal void recvLLQEvent(mDNS *m, DNSQuestion *q, DNSMessage *msg, const mDNSu8 *end, const mDNSAddr *srcaddr, mDNSIPPort srcport, mDNSInterfaceID InterfaceID) +mDNSlocal mDNSBool recvLLQEvent(mDNS *m, DNSQuestion *q, DNSMessage *msg, const mDNSu8 *end, const mDNSAddr *srcaddr, mDNSIPPort srcport, mDNSInterfaceID InterfaceID) { DNSMessage ack; mDNSu8 *ackEnd = ack.data; mStatus err; + LLQOptData opt; + + (void)InterfaceID; // unused + + // find Opt RR, verify correct ID + if (!getLLQAtIndex(m, msg, end, &opt, 0)) { debugf("Pkt does not contain LLQ Opt"); return mDNSfalse; } + if (opt.llqOp != kLLQOp_Event) { LogMsg("recvLLQEvent - Bad LLQ Opcode %d", opt.llqOp); return mDNSfalse; } + if (!q->uDNS_info.llq) { LogMsg("Error: recvLLQEvent - question onject does not contain LLQ metadata"); return mDNSfalse; } + if (!sameID(opt.id, q->uDNS_info.llq->id)) { LogMsg("recvLLQEvent - incorrect ID. Discarding"); return mDNSfalse; } // invoke response handler m->uDNS_info.CurrentQuery = q; q->uDNS_info.responseCallback(m, msg, end, q, q->uDNS_info.context); - if (m->uDNS_info.CurrentQuery != q) return; + if (m->uDNS_info.CurrentQuery != q) return mDNStrue; // format and send ack InitializeDNSMessage(&ack.h, msg->h.id, ResponseFlags); ackEnd = putQuestion(&ack, ack.data, ack.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass); - if (!ackEnd) { LogMsg("ERROR: recvLLQEvent - putQuestion"); return; } - err = mDNSSendDNSMessage(m, &ack, ackEnd, InterfaceID, srcaddr, srcport); - if (err) LogMsg("ERROR: recvLLQEvent - mDNSSendDNSMessage returned %d", err); - } + if (!ackEnd) { LogMsg("ERROR: recvLLQEvent - putQuestion"); return mDNSfalse; } + err = mDNSSendDNSMessage(m, &ack, ackEnd, mDNSInterface_Any, srcaddr, srcport, -1, mDNSNULL); + if (err) LogMsg("ERROR: recvLLQEvent - mDNSSendDNSMessage returned %ld", err); + return mDNStrue; + } @@ -1280,13 +2561,14 @@ mDNSlocal void hndlChallengeResponseAck(mDNS *m, DNSMessage *pktMsg, const mDNSu if (llq->err) { LogMsg("hndlChallengeResponseAck - received error %d from server", llq->err); goto error; } if (!sameID(info->id, llq->id)) { LogMsg("hndlChallengeResponseAck - ID changed. discarding"); return; } // this can happen rarely (on packet loss + reordering) - info->expire = mDNSPlatformTimeNow() + ((mDNSs32)llq->lease * mDNSPlatformOneSecond); - info->retry = info->expire + ((mDNSs32)llq->lease * mDNSPlatformOneSecond * 3/4); + info->expire = mDNSPlatformTimeNow(m) + ((mDNSs32)llq->lease * mDNSPlatformOneSecond); + info->retry = info->expire - ((mDNSs32)llq->lease * mDNSPlatformOneSecond / 2); info->origLease = llq->lease; info->state = LLQ_Established; + q->uDNS_info.responseCallback = llqResponseHndlr; - llqResponseHndlr(m, pktMsg, end, q, NULL); + llqResponseHndlr(m, pktMsg, end, q, mDNSNULL); return; error: @@ -1300,11 +2582,11 @@ mDNSlocal void sendChallengeResponse(mDNS *m, DNSQuestion *q, LLQOptData *llq) mDNSu8 *responsePtr = response.data; mStatus err; LLQOptData llqBuf; - mDNSs32 timenow = mDNSPlatformTimeNow(); + mDNSs32 timenow = mDNSPlatformTimeNow(m); if (info->ntries++ == kLLQ_MAX_TRIES) { - LogMsg("sendChallengeResponse: %d failed attempts for LLQ %s. Will re-try in %d minutes", + LogMsg("sendChallengeResponse: %d failed attempts for LLQ %##s. Will re-try in %d minutes", kLLQ_MAX_TRIES, q->qname.c, kLLQ_DEF_RETRY / 60); info->state = LLQ_Retry; info->retry = timenow + (kLLQ_DEF_RETRY * mDNSPlatformOneSecond); @@ -1317,7 +2599,7 @@ mDNSlocal void sendChallengeResponse(mDNS *m, DNSQuestion *q, LLQOptData *llq) { llq = &llqBuf; llq->vers = kLLQ_Vers; - llq->llqOp = kLLQ_Setup; + llq->llqOp = kLLQOp_Setup; llq->err = LLQErr_NoError; umemcpy(llq->id, info->id, 8); llq->lease = info->origLease; @@ -1330,8 +2612,8 @@ mDNSlocal void sendChallengeResponse(mDNS *m, DNSQuestion *q, LLQOptData *llq) responsePtr = putLLQ(&response, responsePtr, q, llq, mDNSfalse); if (!responsePtr) { LogMsg("ERROR: sendChallengeResponse - putLLQ"); goto error; } - err = mDNSSendDNSMessage(m, &response, responsePtr, q->InterfaceID, &info->servAddr, info->servPort); - if (err) LogMsg("ERROR: sendChallengeResponse - mDNSSendDNSMessage returned %d", err); + err = mDNSSendDNSMessage(m, &response, responsePtr, mDNSInterface_Any, &info->servAddr, info->servPort, -1, mDNSNULL); + if (err) LogMsg("ERROR: sendChallengeResponse - mDNSSendDNSMessage returned %ld", err); // on error, we procede as normal and retry after the appropriate interval return; @@ -1345,43 +2627,43 @@ mDNSlocal void sendChallengeResponse(mDNS *m, DNSQuestion *q, LLQOptData *llq) mDNSlocal void hndlRequestChallenge(mDNS *m, DNSMessage *pktMsg, const mDNSu8 *end, LLQOptData *llq, DNSQuestion *q) { LLQ_Info *info = q->uDNS_info.llq; - mDNSs32 timenow = mDNSPlatformTimeNow(); + mDNSs32 timenow = mDNSPlatformTimeNow(m); switch(llq->err) { case LLQErr_NoError: break; case LLQErr_ServFull: - LogMsg("hndlRequestChallenge - received ServFull from server for LLQ %s. Retry in %d sec", q->qname.c, llq->lease); + LogMsg("hndlRequestChallenge - received ServFull from server for LLQ %##s. Retry in %lu sec", q->qname.c, llq->lease); info->retry = timenow + ((mDNSs32)llq->lease * mDNSPlatformOneSecond); info->state = LLQ_Retry; - simpleResponseHndlr(m, pktMsg, end, q, NULL); // get available answers + simpleResponseHndlr(m, pktMsg, end, q, mDNSNULL); // get available answers info->deriveRemovesOnResume = mDNStrue; case LLQErr_Static: info->state = LLQ_Static; - LogMsg("LLQ %s: static", q->qname.c); - simpleResponseHndlr(m, pktMsg, end, q, NULL); + LogMsg("LLQ %##s: static", q->qname.c); + simpleResponseHndlr(m, pktMsg, end, q, mDNSNULL); return; case LLQErr_FormErr: - LogMsg("ERROR: hndlRequestChallenge - received FormErr from server for LLQ %s", q->qname.c); + LogMsg("ERROR: hndlRequestChallenge - received FormErr from server for LLQ %##s", q->qname.c); goto error; case LLQErr_BadVers: LogMsg("ERROR: hndlRequestChallenge - received BadVers from server"); goto error; case LLQErr_UnknownErr: - LogMsg("ERROR: hndlRequestChallenge - received UnknownErr from server for LLQ %s", q->qname.c); + LogMsg("ERROR: hndlRequestChallenge - received UnknownErr from server for LLQ %##s", q->qname.c); goto error; default: - LogMsg("ERROR: hndlRequestChallenge - received invalid error %d for LLQ %s", llq->err, q->qname.c); + LogMsg("ERROR: hndlRequestChallenge - received invalid error %d for LLQ %##s", llq->err, q->qname.c); goto error; } if (info->origLease != llq->lease) - LogMsg("hndlRequestChallenge: requested lease %d, granted lease %d", info->origLease, llq->lease); + LogMsg("hndlRequestChallenge: requested lease %lu, granted lease %lu", info->origLease, llq->lease); // cache expiration in case we go to sleep before finishing setup info->origLease = llq->lease; info->expire = timenow + ((mDNSs32)llq->lease * mDNSPlatformOneSecond); - // update state and timestamp + // update state info->state = LLQ_SecondaryRequest; umemcpy(info->id, llq->id, 8); info->ntries = 0; // first attempt to send response @@ -1406,58 +2688,61 @@ mDNSlocal void recvSetupResponse(mDNS *m, DNSMessage *pktMsg, const mDNSu8 *end, (void)clientContext; // unused - if (rcode && rcode != kDNSFlag1_RC_NXDomain) - { - LogMsg("LLQ Setup for %s failed with rcode %d. Reverting to polling mode", q->qname.c, rcode); - info->state = LLQ_Poll; - q->uDNS_info.responseCallback = simpleResponseHndlr; - q->LastQTime = mDNSPlatformTimeNow(); - q->ThisQInterval = 1; - return; - } + if (rcode && rcode != kDNSFlag1_RC_NXDomain) goto poll; ptr = getQuestion(pktMsg, ptr, end, 0, &pktQuestion); - if (!ptr) { LogMsg("ERROR: recvSetupResponse - getQuestion"); goto error; } + if (!ptr) { LogMsg("ERROR: recvSetupResponse - getQuestion"); goto poll; } if (!SameDomainName(&q->qname, &pktQuestion.qname)) - { LogMsg("ERROR: recvSetupResponse - mismatched question in response for llq setup %s", q->qname.c); goto error; } + { LogMsg("ERROR: recvSetupResponse - mismatched question in response for llq setup %##s", q->qname.c); goto poll; } - if (!getLLQAtIndex(m, pktMsg, end, &llq, 0)) { LogMsg("ERROR: recvSetupResponse - GetLLQAtIndex"); goto error; } - if (llq.llqOp != kLLQ_Setup) { LogMsg("ERROR: recvSetupResponse - bad op %d", llq.llqOp); goto error; } - if (llq.vers != kLLQ_Vers) { LogMsg("ERROR: recvSetupResponse - bad vers %d", llq.vers); goto error; } + if (!getLLQAtIndex(m, pktMsg, end, &llq, 0)) { debugf("recvSetupResponse - GetLLQAtIndex"); goto poll; } + if (llq.llqOp != kLLQOp_Setup) { LogMsg("ERROR: recvSetupResponse - bad op %d", llq.llqOp); goto poll; } + if (llq.vers != kLLQ_Vers) { LogMsg("ERROR: recvSetupResponse - bad vers %d", llq.vers); goto poll; } if (info->state == LLQ_InitialRequest) { hndlRequestChallenge(m, pktMsg, end, &llq, q); return; } if (info->state == LLQ_SecondaryRequest) { hndlChallengeResponseAck(m, pktMsg, end, &llq, q); return; } LogMsg("recvSetupResponse - bad state %d", info->state); - - error: - info->state = LLQ_Error; + poll: + info->state = LLQ_Poll; + q->uDNS_info.responseCallback = llqResponseHndlr; + info->question->LastQTime = mDNSPlatformTimeNow(m) - (2 * INIT_UCAST_POLL_INTERVAL); // trigger immediate poll + info->question->ThisQInterval = INIT_UCAST_POLL_INTERVAL; } - - -mDNSlocal void startLLQHandshake(mDNS *m, LLQ_Info *info) +mDNSlocal void startLLQHandshake(mDNS *m, LLQ_Info *info, mDNSBool defer) { DNSMessage msg; mDNSu8 *end; LLQOptData llqData; DNSQuestion *q = info->question; - mStatus err; - mDNSs32 timenow = mDNSPlatformTimeNow(); + mStatus err = mStatus_NoError; + mDNSs32 timenow = mDNSPlatformTimeNow(m); + uDNS_GlobalInfo *u = &m->uDNS_info; - if (info->ntries++ == kLLQ_MAX_TRIES) + if (IsPrivateV4Addr(&u->PrimaryIP)) { - LogMsg("startLLQHandshake: %d failed attempts for LLQ %s. Will re-try in %d minutes", - kLLQ_MAX_TRIES, q->qname.c, kLLQ_DEF_RETRY / 60); - info->state = LLQ_Retry; - info->retry = timenow + (kLLQ_DEF_RETRY * mDNSPlatformOneSecond); - // !!!KRS give a callback error in these cases? - return; + if (!u->LLQNatInfo) + { + info->state = LLQ_NatMapWait; + StartLLQNatMap(m); + return; + } + if (u->LLQNatInfo->state == NATState_Error) goto poll; + if (u->LLQNatInfo->state != NATState_Established && u->LLQNatInfo->state != NATState_Legacy) + { info->state = LLQ_NatMapWait; info->NATMap = mDNStrue; return; } + info->NATMap = mDNStrue; // this llq references the global llq nat mapping + } + + if (info->ntries++ >= kLLQ_MAX_TRIES) + { + debugf("startLLQHandshake: %d failed attempts for LLQ %##s. Polling.", kLLQ_MAX_TRIES, q->qname.c, kLLQ_DEF_RETRY / 60); + goto poll; } // set llq rdata llqData.vers = kLLQ_Vers; - llqData.llqOp = kLLQ_Setup; + llqData.llqOp = kLLQOp_Setup; llqData.err = LLQErr_NoError; ubzero(llqData.id, 8); llqData.lease = kLLQ_DefLease; @@ -1471,10 +2756,13 @@ mDNSlocal void startLLQHandshake(mDNS *m, LLQ_Info *info) return; } - err = mDNSSendDNSMessage(m, &msg, end, q->InterfaceID, &info->servAddr, info->servPort); - if (err) LogMsg("ERROR: startLLQHandshake - mDNSSendDNSMessage returned %d", err); - // on error, we procede as normal and retry after the appropriate interval - + if (!defer) // if we are to defer, we simply set the retry timers so the request goes out in the future + { + err = mDNSSendDNSMessage(m, &msg, end, mDNSInterface_Any, &info->servAddr, info->servPort, -1, mDNSNULL); + if (err) LogMsg("ERROR: startLLQHandshake - mDNSSendDNSMessage returned %ld", err); + // on error, we procede as normal and retry after the appropriate interval + } + // update question/info state info->state = LLQ_InitialRequest; info->origLease = kLLQ_DefLease; @@ -1482,44 +2770,67 @@ mDNSlocal void startLLQHandshake(mDNS *m, LLQ_Info *info) q->LastQTime = timenow; q->uDNS_info.responseCallback = recvSetupResponse; q->uDNS_info.internal = mDNStrue; + return; + + poll: + info->question->uDNS_info.responseCallback = llqResponseHndlr; + info->state = LLQ_Poll; + info->question->LastQTime = mDNSPlatformTimeNow(m) - (2 * INIT_UCAST_POLL_INTERVAL); // trigger immediate poll + info->question->ThisQInterval = INIT_UCAST_POLL_INTERVAL; } // wrapper for startLLQHandshake, invoked by async op callback mDNSlocal void startLLQHandshakeCallback(mStatus err, mDNS *const m, void *llqInfo, const AsyncOpResult *result) { LLQ_Info *info = (LLQ_Info *)llqInfo; - const zoneData_t *zoneInfo = &result->zoneData; - - if (err) - { - LogMsg("ERROR: startLLQHandshakeCallback invoked with error code %d", err); - info->state = LLQ_Poll; - info->question->LastQTime = 0; // trigger immediate poll - info->question->ThisQInterval = INIT_UCAST_POLL_INTERVAL; - return; - } - + const zoneData_t *zoneInfo = mDNSNULL; + + // check state first to make sure it is OK to touch question object if (info->state == LLQ_Cancelled) { // StopQuery was called while we were getting the zone info - LogMsg("startLLQHandshake - LLQ Cancelled."); - info->question = NULL; // question may be deallocated + debugf("startLLQHandshake - LLQ Cancelled."); + info->question = mDNSNULL; // question may be deallocated ufree(info); return; } + if (!info->question) + { LogMsg("ERROR: startLLQHandshakeCallback invoked with NULL question"); goto error; } + if (info->state != LLQ_GetZoneInfo) - { - LogMsg("ERROR: startLLQHandshake - bad state %d", info->state); - return; - } + { LogMsg("ERROR: startLLQHandshake - bad state %d", info->state); goto error; } + + if (err) + { LogMsg("ERROR: startLLQHandshakeCallback invoked with error code %ld", err); goto poll; } + + if (!result) + { LogMsg("ERROR: startLLQHandshakeCallback invoked with NULL result and no error code"); goto error; } + zoneInfo = &result->zoneData; + + if (!zoneInfo->llqPort.NotAnInteger) + { debugf("LLQ port lookup failed - reverting to polling"); goto poll; } + // cache necessary zone data info->servAddr.type = zoneInfo->primaryAddr.type; info->servAddr.ip.v4.NotAnInteger = zoneInfo->primaryAddr.ip.v4.NotAnInteger; info->servPort.NotAnInteger = zoneInfo->llqPort.NotAnInteger; info->ntries = 0; - startLLQHandshake(m, info); + + if (info->state == LLQ_SuspendDeferred) info->state = LLQ_Suspended; + else startLLQHandshake(m, info, mDNSfalse); + return; + + poll: + info->question->uDNS_info.responseCallback = llqResponseHndlr; + info->state = LLQ_Poll; + info->question->LastQTime = mDNSPlatformTimeNow(m) - (2 * INIT_UCAST_POLL_INTERVAL); // trigger immediate poll + info->question->ThisQInterval = INIT_UCAST_POLL_INTERVAL; + return; + + error: + info->state = LLQ_Error; } mDNSlocal mStatus startLLQ(mDNS *m, DNSQuestion *question) @@ -1542,10 +2853,10 @@ mDNSlocal mStatus startLLQ(mDNS *m, DNSQuestion *question) err = startGetZoneData(&question->qname, m, mDNSfalse, mDNStrue, startLLQHandshakeCallback, info); if (err) { - LogMsg("ERROR: startLLQ - startGetZoneData returned %d", err); - info->question = NULL; + LogMsg("ERROR: startLLQ - startGetZoneData returned %ld", err); + info->question = mDNSNULL; ufree(info); - question->uDNS_info.llq = NULL; + question->uDNS_info.llq = mDNSNULL; return err; } @@ -1559,15 +2870,13 @@ mDNSlocal mDNSBool recvLLQResponse(mDNS *m, DNSMessage *msg, const mDNSu8 *end, uDNS_GlobalInfo *u = &m->uDNS_info; const mDNSu8 *ptr = msg->data; LLQ_Info *llqInfo; - + if (!msg->h.numQuestions) return mDNSfalse; ptr = getQuestion(msg, ptr, end, 0, &pktQ); if (!ptr) return mDNSfalse; pktQ.uDNS_info.id = msg->h.id; - // !!!KRS we should do a table lookup to quickly determine if this packet is for an LLQ - q = u->ActiveQueries; while (q) { @@ -1580,20 +2889,24 @@ mDNSlocal mDNSBool recvLLQResponse(mDNS *m, DNSMessage *msg, const mDNSu8 *end, { u->CurrentQuery = q; if (llqInfo->state == LLQ_Established || (llqInfo->state == LLQ_Refresh && msg->h.numAnswers)) - { recvLLQEvent(m, q, msg, end, srcaddr, srcport, InterfaceID); return mDNStrue; } - else if (msg->h.id.NotAnInteger != q->uDNS_info.id.NotAnInteger) - { q = q->next; continue; } - else if (llqInfo->state == LLQ_Refresh && msg->h.numAdditionals && !msg->h.numAnswers) - { recvRefreshReply(m, msg, end, q); return mDNStrue; } - else if (llqInfo->state < LLQ_Static) - { q->uDNS_info.responseCallback(m, msg, end, q, q->uDNS_info.context); return mDNStrue; } + { if (recvLLQEvent(m, q, msg, end, srcaddr, srcport, InterfaceID)) return mDNStrue; } + else if (msg->h.id.NotAnInteger == q->uDNS_info.id.NotAnInteger) + { + if (llqInfo->state == LLQ_Refresh && msg->h.numAdditionals && !msg->h.numAnswers) + { recvRefreshReply(m, msg, end, q); return mDNStrue; } + if (llqInfo->state < LLQ_Static) + { + if ((llqInfo->state != LLQ_InitialRequest && llqInfo->state != LLQ_SecondaryRequest) || mDNSSameAddress(srcaddr, &llqInfo->servAddr)) + { q->uDNS_info.responseCallback(m, msg, end, q, q->uDNS_info.context); return mDNStrue; } + } + } } q = q->next; } return mDNSfalse; } -mDNSexport mDNSBool IsActiveUnicastQuery(DNSQuestion *const question, uDNS_GlobalInfo *u) +mDNSexport mDNSBool uDNS_IsActiveQuery(DNSQuestion *const question, uDNS_GlobalInfo *u) { DNSQuestion *q; @@ -1601,8 +2914,8 @@ mDNSexport mDNSBool IsActiveUnicastQuery(DNSQuestion *const question, uDNS_Globa { if (q == question) { - if (!question->uDNS_info.id.NotAnInteger || question->InterfaceID || IsLocalDomain(&question->qname)) - LogMsg("Warning: Question %s in Active Unicast Query list with id %d, interfaceID %x", + if (!question->uDNS_info.id.NotAnInteger || question->InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(&question->qname)) + LogMsg("Warning: Question %##s in Active Unicast Query list with id %d, interfaceID %p", question->qname.c, question->uDNS_info.id.NotAnInteger, question->InterfaceID); return mDNStrue; } @@ -1612,7 +2925,7 @@ mDNSexport mDNSBool IsActiveUnicastQuery(DNSQuestion *const question, uDNS_Globa // stopLLQ happens IN ADDITION to stopQuery mDNSlocal void stopLLQ(mDNS *m, DNSQuestion *question) - { + { LLQ_Info *info = question->uDNS_info.llq; (void)m; // unused @@ -1626,30 +2939,33 @@ mDNSlocal void stopLLQ(mDNS *m, DNSQuestion *question) //!!!KRS should we unlink info<->question here? return; case LLQ_GetZoneInfo: - info->question = NULL; // remove ref to question, as it may be freed when we get called back from async op + case LLQ_SuspendDeferred: + info->question = mDNSNULL; // remove ref to question, as it may be freed when we get called back from async op info->state = LLQ_Cancelled; return; case LLQ_Established: case LLQ_Refresh: // refresh w/ lease 0 sendLLQRefresh(m, question, 0); - goto free_info; + goto end; default: debugf("stopLLQ - silently discarding LLQ in state %d", info->state); - goto free_info; + goto end; } - free_info: - info->question = NULL; + end: + if (info->NATMap) info->NATMap = mDNSfalse; + CheckForUnreferencedLLQMapping(m); + info->question = mDNSNULL; ufree(info); - question->uDNS_info.llq = NULL; - + question->uDNS_info.llq = mDNSNULL; + question->LongLived = mDNSfalse; } mDNSexport mStatus uDNS_StopQuery(mDNS *const m, DNSQuestion *const question) { uDNS_GlobalInfo *u = &m->uDNS_info; - DNSQuestion *qptr, *prev = NULL; + DNSQuestion *qptr, *prev = mDNSNULL; CacheRecord *ka; qptr = u->ActiveQueries; @@ -1674,93 +2990,58 @@ mDNSexport mStatus uDNS_StopQuery(mDNS *const m, DNSQuestion *const question) prev = qptr; qptr = qptr->next; } - LogMsg("uDNS_StopQuery: no such active query (%s)", question->qname.c); + LogMsg("uDNS_StopQuery: no such active query (%##s)", question->qname.c); return mStatus_UnknownErr; } -mDNSexport void uDNS_SuspendLLQs(mDNS *m) - { - DNSQuestion *q; - LLQ_Info *llq; - for (q = m->uDNS_info.ActiveQueries; q; q = q->next) - { - llq = q->uDNS_info.llq; - if (q->LongLived && llq && llq->state < LLQ_Suspended) - { - if (llq->state == LLQ_Established || llq->state == LLQ_Refresh) - sendLLQRefresh(m, q, 0); - // note that we suspend LLQs in setup states too - if (llq->state != LLQ_Retry) llq->state = LLQ_Suspended; - } - } - } - -extern void uDNS_RestartLLQs(mDNS *m) - { - uDNS_GlobalInfo *u = &m->uDNS_info; - DNSQuestion *q; - LLQ_Info *llqInfo; - - u->CurrentQuery = u->ActiveQueries; - while (u->CurrentQuery) - { - q = u->CurrentQuery; - u->CurrentQuery = u->CurrentQuery->next; - llqInfo = q->uDNS_info.llq; - if (q->LongLived && llqInfo && llqInfo->state == LLQ_Suspended) - { llqInfo->ntries = 0; llqInfo->deriveRemovesOnResume = mDNStrue; startLLQHandshake(m, llqInfo); } - } - } - - mDNSlocal mStatus startQuery(mDNS *const m, DNSQuestion *const question, mDNSBool internal) { uDNS_GlobalInfo *u = &m->uDNS_info; DNSMessage msg; mDNSu8 *endPtr; mStatus err = mStatus_NoError; - const mDNSAddr *server; + mDNSAddr server; //!!!KRS we should check if the question is already in our acivequestion list if (!ValidateDomainName(&question->qname)) { - LogMsg("Attempt to start query with invalid qname %##s %s", question->qname.c, DNSTypeName(question->qtype)); + LogMsg("Attempt to start query with invalid qname %##s %##s", question->qname.c, DNSTypeName(question->qtype)); return mStatus_Invalid; } - question->next = NULL; + question->next = mDNSNULL; question->qnamehash = DomainNameHashValue(&question->qname); // to do quick domain name comparisons question->uDNS_info.id = newMessageID(u); + question->uDNS_info.Answered = mDNSfalse; // break here if its and LLQ if (question->LongLived) return startLLQ(m, question); + // else send the query to our server err = constructQueryMsg(&msg, &endPtr, question); if (err) return err; - // else send the query to our server - - question->LastQTime = mDNSPlatformTimeNow(); + question->LastQTime = mDNSPlatformTimeNow(m); question->ThisQInterval = INIT_UCAST_POLL_INTERVAL; // store the question/id in active question list - question->uDNS_info.timestamp = question->LastQTime; question->uDNS_info.internal = internal; LinkActiveQuestion(u, question); - question->uDNS_info.knownAnswers = NULL; - - server = getInitializedDNS(u); - if (!server) { LogMsg("startQuery - no initialized DNS"); err = mStatus_NotInitializedErr; } - else err = mDNSSendDNSMessage(m, &msg, endPtr, question->InterfaceID, server, UnicastDNSPort); - if (err) { LogMsg("ERROR: startQuery - %d (keeping question in list for retransmission", err); } + question->uDNS_info.knownAnswers = mDNSNULL; + if (GetServerForName(u, &question->qname, &server)) + { + err = mDNSSendDNSMessage(m, &msg, endPtr, mDNSInterface_Any, &server, UnicastDNSPort, -1, mDNSNULL); + if (err) { debugf("ERROR: startQuery - %ld (keeping question in list for retransmission", err); } + } - return err; + return mStatus_NoError; // don't return transient errors to caller } mDNSexport mStatus uDNS_StartQuery(mDNS *const m, DNSQuestion *const question) { ubzero(&question->uDNS_info, sizeof(uDNS_QuestionInfo)); question->uDNS_info.responseCallback = simpleResponseHndlr; - question->uDNS_info.context = NULL; + question->uDNS_info.context = mDNSNULL; + //LogOperation("uDNS_StartQuery %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); return startQuery(m, question, 0); } @@ -1784,17 +3065,17 @@ mDNSlocal mStatus startInternalQuery(DNSQuestion *q, mDNS *m, InternalResponseHn /* startGetZoneData * - * asyncronously find the address of the nameserver for the enclosing zone for a given domain name, + * Asynchronously find the address of the nameserver for the enclosing zone for a given domain name, * i.e. the server to which update and LLQ requests will be sent for a given name. Once the address is * derived, it will be passed to the callback, along with a context pointer. If the zone cannot * be determined or if an error occurs, an all-zeros address will be passed and a message will be * written to the syslog. * * If the FindUpdatePort arg is set, the port on which the server accepts dynamic updates is determined - * by querying for the _update._dns-sd._udp.. SRV record. Likewise, if the FindLLQPort arg is set, - * the port on which the server accepts long lived queries is determined by querying for _llq._dns-sd. - * _udp.. record. If either of these queries fail, or flags are not specified, the llqPort and - * updatePort fields in the result structure are set to zero. + * by querying for the _dns-update._udp.. SRV record. Likewise, if the FindLLQPort arg is set, + * the port on which the server accepts long lived queries is determined by querying for + * _dns-llq._udp.. record. If either of these queries fail, or flags are not specified, + * the llqPort and updatePort fields in the result structure are set to zero. * * Steps for deriving the zone name are as follows: * @@ -1878,14 +3159,14 @@ mDNSlocal mStatus startGetZoneData(domainname *name, mDNS *m, mDNSBool findUpdat ntaContext *context = (ntaContext*)umalloc(sizeof(ntaContext)); if (!context) { LogMsg("ERROR: startGetZoneData - umalloc failed"); return mStatus_NoMemoryErr; } ubzero(context, sizeof(ntaContext)); - ustrcpy(context->origName.c, name->c); + AssignDomainName(context->origName, *name); context->state = init; context->m = m; context->callback = callback; context->callbackInfo = callbackInfo; context->findUpdatePort = findUpdatePort; context->findLLQPort = findLLQPort; - getZoneData(m, NULL, NULL, NULL, context); + getZoneData(m, mDNSNULL, mDNSNULL, mDNSNULL, context); return mStatus_NoError; } @@ -1955,7 +3236,7 @@ mDNSlocal void getZoneData(mDNS *const m, DNSMessage *msg, const mDNSu8 *end, DN result.type = zoneDataResult; result.zoneData.primaryAddr.ip.v4.NotAnInteger = context->addr.NotAnInteger; result.zoneData.primaryAddr.type = mDNSAddrType_IPv4; - ustrcpy(result.zoneData.zoneName.c, context->zone.c); + AssignDomainName(result.zoneData.zoneName, context->zone); result.zoneData.zoneClass = context->zoneClass; result.zoneData.llqPort = context->findLLQPort ? context->llqPort : zeroIPPort; result.zoneData.updatePort = context->findUpdatePort ? context->updatePort : zeroIPPort; @@ -1964,7 +3245,7 @@ mDNSlocal void getZoneData(mDNS *const m, DNSMessage *msg, const mDNSu8 *end, DN error: if (context && context->callback) - context->callback(mStatus_UnknownErr, context->m, context->callbackInfo, NULL); + context->callback(mStatus_UnknownErr, context->m, context->callbackInfo, mDNSNULL); cleanup: if (context && context->questionActive) { @@ -2014,7 +3295,7 @@ mDNSlocal smAction hndlLookupSOA(DNSMessage *msg, const mDNSu8 *end, ntaContext if (context->state != init && !context->curSOA->c[0]) { // we've gone down to the root and have not found an SOA - LogMsg("ERROR: hndlLookupSOA - recursed to root label of %s without finding SOA", + LogMsg("ERROR: hndlLookupSOA - recursed to root label of %##s without finding SOA", context->origName.c); return smError; } @@ -2025,21 +3306,21 @@ mDNSlocal smAction hndlLookupSOA(DNSMessage *msg, const mDNSu8 *end, ntaContext else context->curSOA = (domainname *)(context->curSOA->c + context->curSOA->c[0]+1); context->state = lookupSOA; - ustrcpy(query->qname.c, context->curSOA->c); + AssignDomainName(query->qname, *context->curSOA); query->qtype = kDNSType_SOA; query->qclass = kDNSClass_IN; err = startInternalQuery(query, context->m, getZoneData, context); context->questionActive = mDNStrue; - if (err) LogMsg("hndlLookupSOA: startInternalQuery returned error %d (breaking until next periodic retransmission)", err); + if (err) LogMsg("hndlLookupSOA: startInternalQuery returned error %ld (breaking until next periodic retransmission)", err); return smBreak; // break from state machine until we receive another packet } mDNSlocal void processSOA(ntaContext *context, ResourceRecord *rr) { - ustrcpy(context->zone.c, rr->name.c); + AssignDomainName(context->zone, rr->name); context->zoneClass = rr->rrclass; - ustrcpy(context->ns.c, rr->rdata->u.soa.mname.c); + AssignDomainName(context->ns, rr->rdata->u.soa.mname); context->state = foundZone; } @@ -2049,19 +3330,19 @@ mDNSlocal smAction confirmNS(DNSMessage *msg, const mDNSu8 *end, ntaContext *con DNSQuestion *query = &context->question; mStatus err; LargeCacheRecord lcr; - ResourceRecord *rr = &lcr.r.resrec; + const ResourceRecord *const rr = &lcr.r.resrec; const mDNSu8 *ptr; int i; if (context->state == foundZone) { // we've just learned the zone. confirm that an NS record exists - ustrcpy(query->qname.c, context->zone.c); + AssignDomainName(query->qname, context->zone); query->qtype = kDNSType_NS; query->qclass = kDNSClass_IN; err = startInternalQuery(query, context->m, getZoneData, context); context->questionActive = mDNStrue; - if (err) LogMsg("confirmNS: startInternalQuery returned error %d (breaking until next periodic retransmission", err); + if (err) LogMsg("confirmNS: startInternalQuery returned error %ld (breaking until next periodic retransmission", err); context->state = lookupNS; return smBreak; // break from SM until we receive another packet } @@ -2079,7 +3360,7 @@ mDNSlocal smAction confirmNS(DNSMessage *msg, const mDNSu8 *end, ntaContext *con return smContinue; // next routine will examine additionals section of A record } } - LogMsg("ERROR: could not confirm existance of NS record %s", context->zone.c); + LogMsg("ERROR: could not confirm existence of record %##s NS %##s", context->zone.c, context->ns.c); return smError; } else { LogMsg("ERROR: confirmNS - bad state %d", context->state); return smError; } @@ -2090,12 +3371,12 @@ mDNSlocal smAction queryNSAddr(ntaContext *context) mStatus err; DNSQuestion *query = &context->question; - ustrcpy(query->qname.c, context->ns.c); + AssignDomainName(query->qname, context->ns); query->qtype = kDNSType_A; query->qclass = kDNSClass_IN; err = startInternalQuery(query, context->m, getZoneData, context); context->questionActive = mDNStrue; - if (err) LogMsg("confirmNS: startInternalQuery returned error %d (breaking until next periodic retransmission)", err); + if (err) LogMsg("confirmNS: startInternalQuery returned error %ld (breaking until next periodic retransmission)", err); context->state = lookupA; return smBreak; } @@ -2129,7 +3410,7 @@ mDNSlocal smAction lookupNSAddr(DNSMessage *msg, const mDNSu8 *end, ntaContext * } if (rr->rrtype == kDNSType_A && SameDomainName(&context->ns, &rr->name)) { - context->addr.NotAnInteger = rr->rdata->u.ip.NotAnInteger; + context->addr.NotAnInteger = rr->rdata->u.ipv4.NotAnInteger; context->state = foundA; return smContinue; } @@ -2148,7 +3429,7 @@ mDNSlocal smAction lookupNSAddr(DNSMessage *msg, const mDNSu8 *end, ntaContext * if (!ptr) { LogMsg("ERROR: lookupNSAddr, Answers - GetLargeResourceRecord returned NULL"); break; } if (rr->rrtype == kDNSType_A && SameDomainName(&context->ns, &rr->name)) { - context->addr.NotAnInteger = rr->rdata->u.ip.NotAnInteger; + context->addr.NotAnInteger = rr->rdata->u.ipv4.NotAnInteger; context->state = foundA; return smContinue; } @@ -2182,8 +3463,8 @@ mDNSlocal smAction lookupDNSPort(DNSMessage *msg, const mDNSu8 *end, ntaContext return smContinue; } } - LogMsg("hndlLookupUpdatePort %s - answer not contained in reply. Guessing port %d", portName, UnicastDNSPort); - *port = UnicastDNSPort; + debugf("hndlLookupUpdatePort - no answer for type %s", portName); + port->NotAnInteger = 0; context->state = foundPort; return smContinue; } @@ -2192,12 +3473,12 @@ mDNSlocal smAction lookupDNSPort(DNSMessage *msg, const mDNSu8 *end, ntaContext context->state = lookupPort; q = &context->question; MakeDomainNameFromDNSNameString(&q->qname, portName); - ustrcpy((q->qname.c + ustrlen(q->qname.c)), context->zone.c); + AppendDomainName(&q->qname, &context->zone); q->qtype = kDNSType_SRV; q->qclass = kDNSClass_IN; err = startInternalQuery(q, context->m, getZoneData, context); context->questionActive = mDNStrue; - if (err) LogMsg("hndlLookupSOA: startInternalQuery returned error %d (breaking until next periodic retransmission)", err); + if (err) LogMsg("hndlLookupSOA: startInternalQuery returned error %ld (breaking until next periodic retransmission)", err); return smBreak; // break from state machine until we receive another packet } @@ -2241,18 +3522,19 @@ mDNSlocal void conQueryCallback(int sd, void *context, mDNSBool ConnectionEstabl tcpInfo_t *info = (tcpInfo_t *)context; DNSQuestion *question = info->question; int n; + mDNS *m = info->m; - question->uDNS_info.id.NotAnInteger = (mDNSu16)~0; - + mDNS_Lock(m); + if (ConnectionEstablished) { // connection is established - send the message msg = (DNSMessage *)&msgbuf; err = constructQueryMsg(msg, &end, question); - if (err) { LogMsg("ERROR: conQueryCallback: constructQueryMsg - %d", err); goto error; } - err = mDNSSendDNSMessage_tcp(info->m, msg, end, sd); - if (err) { LogMsg("ERROR: conQueryCallback: mDNSSendDNSMessage_tcp - %d", err); goto error; } - return; + if (err) { LogMsg("ERROR: conQueryCallback: constructQueryMsg - %ld", err); goto error; } + err = mDNSSendDNSMessage(m, msg, end, mDNSInterface_Any, &zeroAddr, zeroIPPort, sd, mDNSNULL); + question->LastQTime = mDNSPlatformTimeNow(m); + if (err) { LogMsg("ERROR: conQueryCallback: mDNSSendDNSMessage_tcp - %ld", err); goto error; } } else { @@ -2272,18 +3554,19 @@ mDNSlocal void conQueryCallback(int sd, void *context, mDNSBool ConnectionEstabl if (info->nread == info->replylen) { // finished reading message - receiveMsg(info->m, &info->reply, ((mDNSu8 *)&info->reply) + info->replylen, question->InterfaceID); + uDNS_ReceiveMsg(m, &info->reply, ((mDNSu8 *)&info->reply) + info->replylen, mDNSNULL, zeroIPPort, mDNSNULL, zeroIPPort, question->InterfaceID); mDNSPlatformTCPCloseConnection(sd); ufree(info); - return; } - else return; } + + mDNS_Unlock(m); return; error: mDNSPlatformTCPCloseConnection(sd); ufree(info); + mDNS_Unlock(m); } mDNSlocal void hndlTruncatedAnswer(DNSQuestion *question, const mDNSAddr *src, mDNS *m) @@ -2292,23 +3575,23 @@ mDNSlocal void hndlTruncatedAnswer(DNSQuestion *question, const mDNSAddr *src, uDNS_QuestionInfo *info = &question->uDNS_info; int sd; tcpInfo_t *context; + + if (!src) { LogMsg("hndlTruncatedAnswer: TCP DNS response had TC bit set: ignoring"); return; } context = (tcpInfo_t *)umalloc(sizeof(tcpInfo_t)); if (!context) { LogMsg("ERROR: hndlTruncatedAnswer - memallocate failed"); return; } ubzero(context, sizeof(tcpInfo_t)); context->question = question; context->m = m; - - info->id.NotAnInteger = (mDNSu16)~0; // all 1's indicates TCP queries - info->timestamp = mDNSPlatformTimeNow(); // reset timestamp + info->id = newMessageID(&m->uDNS_info); connectionStatus = mDNSPlatformTCPConnect(src, UnicastDNSPort, question->InterfaceID, conQueryCallback, context, &sd); - if (connectionStatus == mStatus_ConnectionEstablished) // manually invoke callback if connection completes + if (connectionStatus == mStatus_ConnEstablished) // manually invoke callback if connection completes { conQueryCallback(sd, context, mDNStrue); return; } - if (connectionStatus == mStatus_ConnectionPending) return; // callback will be automatically invoked when connection completes + if (connectionStatus == mStatus_ConnPending) return; // callback will be automatically invoked when connection completes LogMsg("hndlTruncatedAnswer: connection failed"); uDNS_StopQuery(m, question); //!!!KRS can we really call this here? } @@ -2320,68 +3603,6 @@ mDNSlocal void hndlTruncatedAnswer(DNSQuestion *question, const mDNSAddr *src, #endif -mDNSlocal mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, const domainname *zone, mDNSOpaque16 zoneClass) - { - ptr = putDomainNameAsLabels(msg, ptr, limit, zone); - if (!ptr || ptr + 4 > limit) return NULL; // If we're out-of-space, return NULL - ((mDNSOpaque16 *)ptr)->NotAnInteger = kDNSType_SOA; - ptr += 2; - ((mDNSOpaque16 *)ptr)->NotAnInteger = zoneClass.NotAnInteger; - ptr += 2; - msg->h.mDNS_numZones++; - return ptr; - } - - - -mDNSlocal mDNSu8 *putPrereqNameNotInUse(domainname *name, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *end) - { - AuthRecord prereq; - - ubzero(&prereq, sizeof(AuthRecord)); - ustrcpy(prereq.resrec.name.c, name->c); - prereq.resrec.rrtype = kDNSQType_ANY; - prereq.resrec.rrclass = kDNSClass_NONE; - ptr = putEmptyResourceRecord(msg, ptr, end, &msg->h.mDNS_numPrereqs, &prereq); - return ptr; - } - -mDNSlocal mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr) - { - mDNSu16 origclass; - // deletion: specify record w/ TTL 0, class NONE - - origclass = rr->rrclass; - rr->rrclass = kDNSClass_NONE; - ptr = PutResourceRecordTTL(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0); - rr->rrclass = origclass; - return ptr; - } - -mDNSlocal mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end) - { - AuthRecord rr; - ResourceRecord *opt = &rr.resrec; - rdataOpt *optRD; - - ubzero(&rr, sizeof(AuthRecord)); - opt->rdata = &rr.rdatastorage; - - opt->RecordType = kDNSRecordTypeKnownUnique; // to avoid warnings in other layers - opt->rrtype = kDNSType_OPT; - opt->rdlength = LEASE_OPT_SIZE; - opt->rdestimate = LEASE_OPT_SIZE; - - optRD = &rr.resrec.rdata->u.opt; - optRD->opt = kDNSOpt_Lease; - optRD->optlen = sizeof(mDNSs32); - optRD->OptData.lease = kUpdate_DefLease; - end = PutResourceRecordTTL(msg, end, &msg->h.numAdditionals, opt, 0); - if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return NULL; } - - return end; - - } mDNSlocal void sendRecordRegistration(mDNS *const m, AuthRecord *rr) { @@ -2390,7 +3611,6 @@ mDNSlocal void sendRecordRegistration(mDNS *const m, AuthRecord *rr) mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage); uDNS_GlobalInfo *u = &m->uDNS_info; mDNSOpaque16 id; - uDNS_AuthInfo *authInfo; uDNS_RegInfo *regInfo = &rr->uDNS_info; mStatus err = mStatus_UnknownErr; @@ -2403,67 +3623,68 @@ mDNSlocal void sendRecordRegistration(mDNS *const m, AuthRecord *rr) if (!ptr) goto error; if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique || rr->uDNS_info.state == regState_Refresh) - { + { // KnownUnique means the record must ALREADY exist, as does refresh // prereq: record must exist (put record in prereq section w/ TTL 0) - ptr = PutResourceRecordTTL(&msg, ptr, &msg.h.mDNS_numPrereqs, &rr->resrec, 0); + ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numPrereqs, &rr->resrec, 0); if (!ptr) goto error; - } + } else if (rr->resrec.RecordType != kDNSRecordTypeShared) { ptr = putPrereqNameNotInUse(&rr->resrec.name, &msg, ptr, end); if (!ptr) goto error; } - ptr = PutResourceRecord(&msg, ptr, &msg.h.mDNS_numUpdates, &rr->resrec); + ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl); if (!ptr) goto error; if (rr->uDNS_info.lease) - ptr = putUpdateLease(&msg, ptr); + { ptr = putUpdateLease(&msg, ptr, kLLQ_DefLease); if (!ptr) goto error; } rr->uDNS_info.expire = -1; - authInfo = GetAuthInfoForZone(u, ®Info->zone); - if (authInfo) - { - err = mDNSSendSignedDNSMessage(m, &msg, ptr, 0, ®Info->ns, regInfo->port, authInfo); - if (err) { LogMsg("ERROR: sendRecordRegistration - mDNSSendSignedDNSMessage - %d", err); goto error; } - } - else - { - err = mDNSSendDNSMessage(m, &msg, ptr, 0, ®Info->ns, regInfo->port); - if (err) { LogMsg("ERROR: sendRecordRegistration - mDNSSendDNSMessage - %d", err); goto error; } - } + err = mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, ®Info->ns, regInfo->port, -1, GetAuthInfoForZone(u, ®Info->zone)); + if (err) LogMsg("ERROR: sendRecordRegistration - mDNSSendDNSMessage - %ld", err); + SetRecordRetry(m, rr); + if (regInfo->state != regState_Refresh) regInfo->state = regState_Pending; return; error: + LogMsg("sendRecordRegistration: Error formatting message"); if (rr->uDNS_info.state != regState_Unregistered) { unlinkAR(&u->RecordRegistrations, rr); rr->uDNS_info.state = regState_Unregistered; } - rr->RecordCallback(m, rr, err); + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + if (rr->RecordCallback) rr->RecordCallback(m, rr, err); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again // NOTE: not safe to touch any client structures here } mDNSlocal void RecordRegistrationCallback(mStatus err, mDNS *const m, void *authPtr, const AsyncOpResult *result) { AuthRecord *newRR = (AuthRecord*)authPtr; - const zoneData_t *zoneData = &result->zoneData; + const zoneData_t *zoneData = mDNSNULL; uDNS_GlobalInfo *u = &m->uDNS_info; AuthRecord *ptr; + // make sure record is still in list for (ptr = u->RecordRegistrations; ptr; ptr = ptr->next) if (ptr == newRR) break; if (!ptr) { LogMsg("RecordRegistrationCallback - RR no longer in list. Discarding."); return; } - - if (err) { LogMsg("RecordRegistrationCallback: error %d", err); goto error; } + + // check error/result + if (err) { LogMsg("RecordRegistrationCallback: error %ld", err); goto error; } + if (!result) { LogMsg("ERROR: RecordRegistrationCallback invoked with NULL result and no error"); goto error; } + else zoneData = &result->zoneData; + if (newRR->uDNS_info.state == regState_Cancelled) { //!!!KRS we should send a memfree callback here! - LogMsg("Registration of %s type %d cancelled prior to update", + debugf("Registration of %##s type %d cancelled prior to update", newRR->resrec.name.c, newRR->resrec.rrtype); newRR->uDNS_info.state = regState_Unregistered; unlinkAR(&u->RecordRegistrations, newRR); @@ -2484,10 +3705,16 @@ mDNSlocal void RecordRegistrationCallback(mStatus err, mDNS *const m, void *auth } // cache zone data - ustrcpy(newRR->uDNS_info.zone.c, zoneData->zoneName.c); + AssignDomainName(newRR->uDNS_info.zone, zoneData->zoneName); newRR->uDNS_info.ns.type = mDNSAddrType_IPv4; newRR->uDNS_info.ns.ip.v4.NotAnInteger = zoneData->primaryAddr.ip.v4.NotAnInteger; - newRR->uDNS_info.port.NotAnInteger = zoneData->updatePort.NotAnInteger; + if (zoneData->updatePort.NotAnInteger) newRR->uDNS_info.port = zoneData->updatePort; + else + { + debugf("Update port not advertised via SRV - guessing port 53, no lease option"); + newRR->uDNS_info.port = UnicastDNSPort; + newRR->uDNS_info.lease = mDNSfalse; + } sendRecordRegistration(m, newRR); return; @@ -2498,39 +3725,13 @@ error: unlinkAR(&u->RecordRegistrations, newRR); newRR->uDNS_info.state = regState_Unregistered; } - newRR->RecordCallback(m, newRR, err); + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + if (newRR->RecordCallback) + newRR->RecordCallback(m, newRR, err); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again // NOTE: not safe to touch any client structures here } - -mDNSlocal mDNSBool setHostTarget(AuthRecord *rr, mDNS *m) - { - domainname *target; - - if (!rr->HostTarget) - { - debugf("Service %s - not updating host target", rr->resrec.name.c); - return mDNSfalse; - } - - // set SRV target - target = GetRRDomainNameTarget(&rr->resrec); - if (!target) - { - LogMsg("ERROR: setHostTarget: Can't set target of rrtype %d", rr->resrec.rrtype); - return mDNSfalse; - } - - if (SameDomainName(target, &m->uDNS_info.hostname)) - { - debugf("Host target for %s unchanged", rr->resrec.name.c); - return mDNSfalse; - } - AssignDomainName(*target, m->uDNS_info.hostname); - SetNewRData(&rr->resrec, NULL, 0); - return mDNStrue; - } - mDNSlocal void SendServiceRegistration(mDNS *m, ServiceRecordSet *srs) { DNSMessage msg; @@ -2538,71 +3739,93 @@ mDNSlocal void SendServiceRegistration(mDNS *m, ServiceRecordSet *srs) mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage); uDNS_GlobalInfo *u = &m->uDNS_info; mDNSOpaque16 id; - uDNS_AuthInfo *authInfo; uDNS_RegInfo *rInfo = &srs->uDNS_info; mStatus err = mStatus_UnknownErr; + mDNSIPPort privport; + NATTraversalInfo *nat = srs->uDNS_info.NATinfo; + mDNSBool mapped = mDNSfalse; + domainname target; + AuthRecord *srv = &srs->RR_SRV; id = newMessageID(u); InitializeDNSMessage(&msg.h, id, UpdateReqFlags); - rInfo->id.NotAnInteger = id.NotAnInteger; // setup resource records - if (setHostTarget(&srs->RR_SRV, m)) - SetNewRData(&srs->RR_SRV.resrec, NULL, 0); // set rdlen/estimate/hash + SetNewRData(&srs->RR_PTR.resrec, mDNSNULL, 0); + SetNewRData(&srs->RR_TXT.resrec, mDNSNULL, 0); - //SetNewRData(&srs->RR_ADV.resrec, NULL, 0); //!!!KRS - SetNewRData(&srs->RR_PTR.resrec, NULL, 0); - SetNewRData(&srs->RR_TXT.resrec, NULL, 0); + // replace port w/ NAT mapping if necessary + if (nat && nat->PublicPort.NotAnInteger && + (nat->state == NATState_Established || nat->state == NATState_Refresh || nat->state == NATState_Legacy)) + { + privport = srv->resrec.rdata->u.srv.port; + srv->resrec.rdata->u.srv.port = nat->PublicPort; + mapped = mDNStrue; + } // construct update packet // set zone - ptr = putZone(&msg, ptr, end, &rInfo->zone, mDNSOpaque16fromIntVal(srs->RR_SRV.resrec.rrclass)); + ptr = putZone(&msg, ptr, end, &rInfo->zone, mDNSOpaque16fromIntVal(srv->resrec.rrclass)); if (!ptr) goto error; - - if (srs->uDNS_info.state == regState_Refresh) + + if (srs->uDNS_info.TestForSelfConflict) { - // prereq: record must exist (put record in prereq section w/ TTL 0) - ptr = PutResourceRecordTTL(&msg, ptr, &msg.h.mDNS_numPrereqs, &srs->RR_SRV.resrec, 0); - if (!ptr) goto error; + // update w/ prereq that records already exist to make sure previous registration was ours + if (!(ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numPrereqs, &srs->RR_SRV.resrec, 0))) goto error; + if (!(ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numPrereqs, &srs->RR_TXT.resrec, 0))) goto error; } - else + + else if (srs->uDNS_info.state != regState_Refresh) { - // use SRV for prereq - ptr = putPrereqNameNotInUse(&srs->RR_SRV.resrec.name, &msg, ptr, end); + // use SRV name for prereq + ptr = putPrereqNameNotInUse(&srv->resrec.name, &msg, ptr, end); if (!ptr) goto error; } - + //!!!KRS Need to do bounds checking and use TCP if it won't fit!!! - //if (!(ptr = PutResourceRecord(&msg, ptr, &msg.h.mDNS_numUpdates, &srs->RR_ADV.resrec))) goto error; - if (!(ptr = PutResourceRecord(&msg, ptr, &msg.h.mDNS_numUpdates, &srs->RR_PTR.resrec))) goto error; - if (!(ptr = PutResourceRecord(&msg, ptr, &msg.h.mDNS_numUpdates, &srs->RR_SRV.resrec))) goto error; - if (!(ptr = PutResourceRecord(&msg, ptr, &msg.h.mDNS_numUpdates, &srs->RR_TXT.resrec))) goto error; - // !!!KRS do subtypes/extras etc. + if (!(ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numUpdates, &srs->RR_PTR.resrec, srs->RR_PTR.resrec.rroriginalttl))) goto error; + if (!(ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numUpdates, &srs->RR_TXT.resrec, srs->RR_TXT.resrec.rroriginalttl))) goto error; + + if (!GetServiceTarget(u, srv, &target)) + { + debugf("Couldn't get target for service %##s", srv->resrec.name.c); + rInfo->state = regState_NoTarget; + return; + } + if (!SameDomainName(&target, &srv->resrec.rdata->u.srv.target)) + { + AssignDomainName(srv->resrec.rdata->u.srv.target, target); + SetNewRData(&srv->resrec, mDNSNULL, 0); + } + + ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numUpdates, &srv->resrec, srv->resrec.rroriginalttl); + if (!ptr) goto error; + // !!!KRS do subtypes/extras etc. if (srs->uDNS_info.lease) - ptr = putUpdateLease(&msg, ptr); + { ptr = putUpdateLease(&msg, ptr, kLLQ_DefLease); if (!ptr) goto error; } srs->uDNS_info.expire = -1; - authInfo = GetAuthInfoForZone(u, &rInfo->zone); - if (authInfo) - { - err = mDNSSendSignedDNSMessage(m, &msg, ptr, 0, &rInfo->ns, rInfo->port, authInfo); - if (err) { LogMsg("ERROR: SendServiceRegistration - mDNSSendSignedDNSMessage - %d", err); goto error; } - } - else - { - err = mDNSSendDNSMessage(m, &msg, ptr, 0, &rInfo->ns, rInfo->port); - if (err) { LogMsg("ERROR: SendServiceRegistration - mDNSSendDNSMessage - %d", err); goto error; } - } + err = mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, &rInfo->ns, rInfo->port, -1, GetAuthInfoForZone(u, &rInfo->zone)); + if (err) { LogMsg("ERROR: SendServiceRegistration - mDNSSendDNSMessage - %ld", err); goto error; } + if (rInfo->state != regState_Refresh) rInfo->state = regState_Pending; + + SetRecordRetry(m, &srs->RR_SRV); + rInfo->id.NotAnInteger = id.NotAnInteger; + if (mapped) srv->resrec.rdata->u.srv.port = privport; return; error: + LogMsg("SendServiceRegistration - Error formatting message"); + if (mapped) srv->resrec.rdata->u.srv.port = privport; unlinkSRS(u, srs); rInfo->state = regState_Unregistered; + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback srs->ServiceCallback(m, srs, err); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again //!!!KRS will mem still be free'd on error? // NOTE: not safe to touch any client structures here } @@ -2610,10 +3833,13 @@ error: mDNSlocal void serviceRegistrationCallback(mStatus err, mDNS *const m, void *srsPtr, const AsyncOpResult *result) { ServiceRecordSet *srs = (ServiceRecordSet *)srsPtr; - const zoneData_t *zoneData = &result->zoneData; + const zoneData_t *zoneData = mDNSNULL; uDNS_GlobalInfo *u = &m->uDNS_info; if (err) goto error; + if (!result) { LogMsg("ERROR: serviceRegistrationCallback invoked with NULL result and no error"); goto error; } + else zoneData = &result->zoneData; + if (result->type != zoneDataResult) { LogMsg("ERROR: buildUpdatePacket passed incorrect result type %d", result->type); @@ -2625,95 +3851,54 @@ mDNSlocal void serviceRegistrationCallback(mStatus err, mDNS *const m, void *srs // client cancelled registration while fetching zone data srs->uDNS_info.state = regState_Unregistered; unlinkSRS(u, srs); + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback srs->ServiceCallback(m, srs, mStatus_MemFree); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again return; } - + if (srs->RR_SRV.resrec.rrclass != zoneData->zoneClass) { - LogMsg("Service %s - class does not match zone", srs->RR_SRV.resrec.name.c); + LogMsg("Service %##s - class does not match zone", srs->RR_SRV.resrec.name.c); goto error; } // cache zone data - ustrcpy(srs->uDNS_info.zone.c, zoneData->zoneName.c); + AssignDomainName(srs->uDNS_info.zone, zoneData->zoneName); srs->uDNS_info.ns.type = mDNSAddrType_IPv4; - srs->uDNS_info.ns.ip.v4.NotAnInteger = zoneData->primaryAddr.ip.v4.NotAnInteger; - srs->uDNS_info.port.NotAnInteger = zoneData->updatePort.NotAnInteger; - + srs->uDNS_info.ns = zoneData->primaryAddr; + if (zoneData->updatePort.NotAnInteger) srs->uDNS_info.port = zoneData->updatePort; + else + { + debugf("Update port not advertised via SRV - guessing port 53, no lease option"); + srs->uDNS_info.port = UnicastDNSPort; + srs->uDNS_info.lease = mDNSfalse; + } SendServiceRegistration(m, srs); return; error: unlinkSRS(u, srs); srs->uDNS_info.state = regState_Unregistered; + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback srs->ServiceCallback(m, srs, err); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again //!!!KRS will mem still be free'd on error? // NOTE: not safe to touch any client structures here } -mDNSexport void uDNS_UpdateServiceTargets(mDNS *const m) - { - DNSMessage msg; - mDNSu8 *ptr = msg.data; - mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage); - uDNS_GlobalInfo *u = &m->uDNS_info; - ServiceRecordSet *srs; - AuthRecord *rr; - mStatus err = mStatus_NoError; - - if (!m->uDNS_info.hostname.c[0]) - { - LogMsg("ERROR: uDNS_UpdateServiceTargets called before registration of hostname"); - return; - //!!!KRS need to handle this case properly! - } - - for (srs = u->ServiceRegistrations; srs; srs = srs->next) - { - if (err) srs = u->ServiceRegistrations; - // start again from beginning of list, since it may have changed - // (setHostTarget() will skip records already updated) - rr = &srs->RR_SRV; - if (srs->uDNS_info.state != regState_Registered) - { - LogMsg("ERROR: uDNS_UpdateServiceTargets - service %s not registered", rr->resrec.name.c); - continue; - //!!!KRS need to handle this - } - InitializeDNSMessage(&msg.h, srs->uDNS_info.id, UpdateReqFlags); - - // construct update packet - ptr = putZone(&msg, ptr, end, &srs->uDNS_info.zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass)); - if (ptr) ptr = putDeletionRecord(&msg, ptr, &rr->resrec); // delete the old target - // update the target - if (!setHostTarget(rr, m)) continue; - if (ptr) ptr = PutResourceRecord(&msg, ptr, &msg.h.mDNS_numUpdates, &rr->resrec); // put the new target - // !!!KRS do subtypes/extras etc. - if (!ptr) err = mStatus_UnknownErr; - else err = mDNSSendDNSMessage(m, &msg, ptr, 0, &srs->uDNS_info.ns, srs->uDNS_info.port); - if (err) - { - LogMsg("ERROR: uDNS_UpdateServiceTargets - %s", ptr ? "mDNSSendDNSMessage" : "message formatting error"); - unlinkSRS(u, srs); - srs->uDNS_info.state = regState_Unregistered; - srs->ServiceCallback(m, srs, err); - //!!!KRS will mem still be free'd on error? - // NOTE: not safe to touch any client structures here - } - else srs->uDNS_info.state = regState_TargetChange; - } -} - - -mDNSexport mStatus uDNS_RegisterRecord(mDNS *const m, AuthRecord *const rr) +mDNSlocal mStatus SetupRecordRegistration(mDNS *m, AuthRecord *rr) { domainname *target = GetRRDomainNameTarget(&rr->resrec); + AuthRecord *ptr = m->uDNS_info.RecordRegistrations; + + while (ptr && ptr != rr) ptr = ptr->next; + if (ptr) { LogMsg("Error: SetupRecordRegistration - record %##s already in list!", rr->resrec.name.c); return mStatus_AlreadyRegistered; } if (rr->uDNS_info.state == regState_FetchingZoneData || rr->uDNS_info.state == regState_Pending || rr->uDNS_info.state == regState_Registered) { - LogMsg("Requested double-registration of physical record %s type %s", + LogMsg("Requested double-registration of physical record %##s type %d", rr->resrec.name.c, rr->resrec.rrtype); return mStatus_AlreadyRegistered; } @@ -2723,13 +3908,14 @@ mDNSexport mStatus uDNS_RegisterRecord(mDNS *const m, AuthRecord *const rr) if (!ValidateDomainName(&rr->resrec.name)) { - LogMsg("Attempt to register record with invalid name: %s", GetRRDisplayString(m, rr)); + LogMsg("Attempt to register record with invalid name: %s", ARDisplayString(m, rr)); return mStatus_Invalid; } // Don't do this until *after* we've set rr->resrec.rdlength if (!ValidateRData(rr->resrec.rrtype, rr->resrec.rdlength, rr->resrec.rdata)) - { LogMsg("Attempt to register record with invalid rdata: %s", GetRRDisplayString(m, rr)); + { + LogMsg("Attempt to register record with invalid rdata: %s", ARDisplayString(m, rr)); return mStatus_Invalid; } @@ -2740,262 +3926,812 @@ mDNSexport mStatus uDNS_RegisterRecord(mDNS *const m, AuthRecord *const rr) rr->uDNS_info.state = regState_FetchingZoneData; rr->next = m->uDNS_info.RecordRegistrations; m->uDNS_info.RecordRegistrations = rr; - rr->uDNS_info.lease = mDNStrue; - return startGetZoneData(&rr->resrec.name, m, mDNStrue, mDNSfalse, RecordRegistrationCallback, rr); - } + return mStatus_NoError; + } +mDNSexport mStatus uDNS_RegisterRecord(mDNS *const m, AuthRecord *const rr) + { + mStatus err = SetupRecordRegistration(m, rr); + if (err) return err; + else return startGetZoneData(&rr->resrec.name, m, mDNStrue, mDNSfalse, RecordRegistrationCallback, rr); + } -mDNSexport mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr) +mDNSlocal void SendRecordDeregistration(mDNS *m, AuthRecord *rr) { uDNS_GlobalInfo *u = &m->uDNS_info; DNSMessage msg; mDNSu8 *ptr = msg.data; mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage); mStatus err; - uDNS_AuthInfo *authInfo; + + InitializeDNSMessage(&msg.h, rr->uDNS_info.id, UpdateReqFlags); + + ptr = putZone(&msg, ptr, end, &rr->uDNS_info.zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass)); + if (!ptr) goto error; + if (!(ptr = putDeletionRecord(&msg, ptr, &rr->resrec))) goto error; + + err = mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, &rr->uDNS_info.ns, rr->uDNS_info.port, -1, GetAuthInfoForZone(u, &rr->uDNS_info.zone)); + if (err) LogMsg("ERROR: SendRecordDeregistration - mDNSSendDNSMessage - %ld", err); + + SetRecordRetry(m, rr); + rr->uDNS_info.state = regState_DeregPending; + return; + + error: + LogMsg("Error: SendRecordDeregistration - could not contruct deregistration packet"); + unlinkAR(&u->RecordRegistrations, rr); + rr->uDNS_info.state = regState_Unregistered; + } + + + +mDNSexport mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + NATTraversalInfo *n = rr->uDNS_info.NATinfo; + switch (rr->uDNS_info.state) { + case regState_NATMap: + // we're in the middle of a NAT traversal operation + if (!n) LogMsg("uDNS_DeregisterRecord: no NAT info context"); + else FreeNATInfo(m, n); // cause response to outstanding request to be ignored. + // Note: normally here we're trying to determine our public address, + //in which case there is not state to be torn down. For simplicity, + //we allow other operations to expire. + rr->uDNS_info.NATinfo = mDNSNULL; + rr->uDNS_info.state = regState_Unregistered; + break; + case regState_ExtraQueued: + rr->uDNS_info.state = regState_Unregistered; + break; case regState_FetchingZoneData: rr->uDNS_info.state = regState_Cancelled; return mStatus_NoError; + case regState_Refresh: case regState_Pending: + case regState_UpdatePending: rr->uDNS_info.state = regState_DeregDeferred; - debugf("Deferring deregistration of record %s until registration completes", rr->resrec.name.c); + LogMsg("Deferring deregistration of record %##s until registration completes", rr->resrec.name.c); return mStatus_NoError; case regState_Registered: - break; case regState_DeregPending: + break; + case regState_DeregDeferred: case regState_Cancelled: - LogMsg("Double deregistration of record %s type %d", + LogMsg("Double deregistration of record %##s type %d", rr->resrec.name.c, rr->resrec.rrtype); return mStatus_UnknownErr; case regState_Unregistered: - LogMsg("Requested deregistration of unregistered record %s type %d", + LogMsg("Requested deregistration of unregistered record %##s type %d", rr->resrec.name.c, rr->resrec.rrtype); return mStatus_UnknownErr; - default: - LogMsg("ERROR: uDNS_DeregisterRecord called for record %s type %d with unknown state %d", - rr->resrec.name.c, rr->resrec.rrtype, rr->uDNS_info.state); + case regState_NoTarget: + LogMsg("ERROR: uDNS_DeregisterRecord called for record %##s with bad state (regState_NoTarget)", rr->resrec.name.c); return mStatus_UnknownErr; } - - InitializeDNSMessage(&msg.h, rr->uDNS_info.id, UpdateReqFlags); - - // put zone - ptr = putZone(&msg, ptr, end, &rr->uDNS_info.zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass)); - if (!ptr) goto error; - - if (!(ptr = putDeletionRecord(&msg, ptr, &rr->resrec))) goto error; - authInfo = GetAuthInfoForZone(u, &rr->uDNS_info.zone); - if (authInfo) - { - err = mDNSSendSignedDNSMessage(m, &msg, ptr, 0, &rr->uDNS_info.ns, rr->uDNS_info.port, authInfo); - if (err) { LogMsg("ERROR: uDNS_DeregiserRecord - mDNSSendSignedDNSMessage - %d", err); goto error; } - } - else + if (rr->uDNS_info.state == regState_Unregistered) { - err = mDNSSendDNSMessage(m, &msg, ptr, 0, &rr->uDNS_info.ns, rr->uDNS_info.port); - if (err) { LogMsg("ERROR: uDNS_DeregisterRecord - mDNSSendDNSMessage - %d", err); goto error; } + // unlink and deliver memfree + + unlinkAR(&u->RecordRegistrations, rr); + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + if (rr->RecordCallback) rr->RecordCallback(m, rr, mStatus_MemFree); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + return mStatus_NoError; } - - return mStatus_NoError; - error: - if (rr->uDNS_info.state != regState_Unregistered) - { - unlinkAR(&u->RecordRegistrations, rr); - rr->uDNS_info.state = regState_Unregistered; - } - return mStatus_UnknownErr; - } + if (n) FreeNATInfo(m, n); + rr->uDNS_info.NATinfo = mDNSNULL; + SendRecordDeregistration(m, rr); + return mStatus_NoError; + } -mDNSexport mStatus uDNS_RegisterService(mDNS *const m, ServiceRecordSet *srs) +// register a service already in list with initialized state +mDNSlocal mStatus RegisterService(mDNS *m, ServiceRecordSet *srs) { - if (!*m->uDNS_info.NameRegDomain) - { - LogMsg("ERROR: uDNS_RegisterService - cannot register unicast service " - "without setting the NameRegDomain via mDNSResponder.conf"); - srs->uDNS_info.state = regState_Unregistered; - return mStatus_UnknownErr; - } + uDNS_RegInfo *info = &srs->uDNS_info; srs->RR_SRV.resrec.rroriginalttl = 3; srs->RR_TXT.resrec.rroriginalttl = 3; srs->RR_PTR.resrec.rroriginalttl = 3; - // set state and link into list - srs->uDNS_info.state = regState_FetchingZoneData; - srs->next = m->uDNS_info.ServiceRegistrations; - m->uDNS_info.ServiceRegistrations = srs; - srs->uDNS_info.lease = mDNStrue; + info->lease = mDNStrue; + + if (srs->RR_SRV.resrec.rdata->u.srv.port.NotAnInteger && MapServicePort(m)) + { + // !!!KRS if interface is already in NATState_Legacy, don't try NATPMP + info->state = regState_NATMap; + StartNATPortMap(m, srs); + return mStatus_NoError; + } + + info->state = regState_FetchingZoneData; + return startGetZoneData(&srs->RR_SRV.resrec.name, m, mDNStrue, mDNSfalse, serviceRegistrationCallback, srs); + } + +mDNSexport mStatus uDNS_RegisterService(mDNS *const m, ServiceRecordSet *srs) + { + // Note: ServiceRegistrations list is in the order they were created; important for in-order event delivery + ServiceRecordSet **p = &m->uDNS_info.ServiceRegistrations; + while (*p && *p != srs) p=&(*p)->next; + if (*p) { LogMsg("uDNS_RegisterService: %p %##s already in list", srs, srs->RR_SRV.resrec.name.c); return(mStatus_AlreadyRegistered); } + ubzero(&srs->uDNS_info, sizeof(uDNS_RegInfo)); + srs->uDNS_info.state = regState_Unregistered; + *p = srs; + srs->next = mDNSNULL; + return RegisterService(m, srs); + } + +mDNSlocal void SendServiceDeregistration(mDNS *m, ServiceRecordSet *srs) + { + uDNS_RegInfo *info = &srs->uDNS_info; + uDNS_GlobalInfo *u = &m->uDNS_info; + DNSMessage msg; + mDNSOpaque16 id; + mDNSu8 *ptr = msg.data; + mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage); + mStatus err = mStatus_UnknownErr; + + id = newMessageID(u); + InitializeDNSMessage(&msg.h, id, UpdateReqFlags); + + // put zone + ptr = putZone(&msg, ptr, end, &info->zone, mDNSOpaque16fromIntVal(srs->RR_SRV.resrec.rrclass)); + if (!ptr) { LogMsg("ERROR: SendServiceDeregistration - putZone"); goto error; } + + if (!(ptr = putDeleteAllRRSets(&msg, ptr, &srs->RR_SRV.resrec.name))) goto error; // this deletes SRV, TXT, and Extras + if (!(ptr = putDeletionRecord(&msg, ptr, &srs->RR_PTR.resrec))) goto error; + + err = mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, &info->ns, info->port, -1, GetAuthInfoForZone(u, &info->zone)); + if (err) { LogMsg("ERROR: SendServiceDeregistration - mDNSSendDNSMessage - %ld", err); goto error; } + + SetRecordRetry(m, &srs->RR_SRV); + info->id.NotAnInteger = id.NotAnInteger; + info->state = regState_DeregPending; + + return; - return startGetZoneData(&srs->RR_SRV.resrec.name, m, mDNStrue, mDNSfalse, serviceRegistrationCallback, srs); + error: + unlinkSRS(u, srs); + info->state = regState_Unregistered; } mDNSexport mStatus uDNS_DeregisterService(mDNS *const m, ServiceRecordSet *srs) { uDNS_GlobalInfo *u = &m->uDNS_info; - DNSMessage msg; - mDNSu8 *ptr = msg.data; - mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage); - mStatus err = mStatus_UnknownErr; - uDNS_AuthInfo *authInfo; + NATTraversalInfo *nat = srs->uDNS_info.NATinfo; + AuthRecord **r = &u->RecordRegistrations; + char *errmsg = "Unknown State"; - //!!!KRS make sure we're doing the right thing w/ memfree + // We "silently" unlink any Extras from our RecordRegistration list, as they are implicitly deleted from + // the server when we delete all RRSets for this name + while (*r) + { + if (SameDomainName(&srs->RR_SRV.resrec.name, &(*r)->resrec.name)) *r = (*r)->next; + else r = &(*r)->next; + } + + // don't re-register with a new target following deregistration + srs->uDNS_info.LostTarget = srs->uDNS_info.SRVUpdateDeferred = mDNSfalse; switch (srs->uDNS_info.state) { + case regState_NATMap: + // we're in the middle of nat mapping. clear ptr from NAT info to RR, unlink and give memfree + if (!nat) LogMsg("uDNS_DeregisterRecord: no NAT info context"); + else { nat->reg.ServiceRegistration = mDNSNULL; FreeNATInfo(m, nat); } + unlinkSRS(u, srs); + srs->uDNS_info.state = regState_Unregistered; + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + srs->ServiceCallback(m, srs, mStatus_MemFree); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + return mStatus_NoError; case regState_Unregistered: - LogMsg("ERROR: uDNS_DeregisterService - service not registerd"); - return mStatus_UnknownErr; + errmsg = "service not registered"; + goto error; case regState_FetchingZoneData: - case regState_Pending: // let the async op complete, then terminate srs->uDNS_info.state = regState_Cancelled; return mStatus_NoError; // deliver memfree upon completion of async op + case regState_Pending: + case regState_Refresh: + case regState_UpdatePending: + // deregister following completion of in-flight operation + srs->uDNS_info.state = regState_DeregDeferred; + return mStatus_NoError; case regState_DeregPending: case regState_DeregDeferred: case regState_Cancelled: - LogMsg("uDNS_DeregisterService - deregistration in process"); - return mStatus_UnknownErr; + debugf("Double deregistration of service %##s", srs->RR_SRV.resrec.name.c); + return mStatus_NoError; + case regState_NoTarget: + unlinkSRS(u, srs); + srs->uDNS_info.state = regState_Unregistered; + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + srs->ServiceCallback(m, srs, mStatus_MemFree); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + return mStatus_NoError; + case regState_Registered: + if (nat) DeleteNATPortMapping(m, nat, srs); + srs->uDNS_info.state = regState_DeregPending; + SendServiceDeregistration(m, srs); + return mStatus_NoError; + case regState_ExtraQueued: // only for record registrations + errmsg = "bad state (regState_ExtraQueued)"; + goto error; } - srs->uDNS_info.state = regState_DeregPending; - InitializeDNSMessage(&msg.h, srs->uDNS_info.id, UpdateReqFlags); + error: + LogMsg("Error, uDNS_DeregisterService: %s", errmsg); + return mStatus_BadReferenceErr; + } - // put zone - ptr = putZone(&msg, ptr, end, &srs->uDNS_info.zone, mDNSOpaque16fromIntVal(srs->RR_SRV.resrec.rrclass)); - if (!ptr) { LogMsg("ERROR: uDNS_DeregisterService - putZone"); goto error; } +// note that the RegInfo will be either for the record, or for the parent ServiceRecordSet +mDNSlocal void SendRecordUpdate(mDNS *m, AuthRecord *rr, uDNS_RegInfo *info) + { + DNSMessage msg; + mDNSu8 *ptr = msg.data; + mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage); + uDNS_GlobalInfo *u = &m->uDNS_info; + mDNSOpaque16 id; + mStatus err = mStatus_UnknownErr; + + rr->uDNS_info.UpdateQueued = mDNSfalse; // if this was queued, clear flag + id = newMessageID(u); + InitializeDNSMessage(&msg.h, id, UpdateReqFlags); + info->id.NotAnInteger = id.NotAnInteger; + + // set zone + ptr = putZone(&msg, ptr, end, &info->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass)); + if (!ptr) goto error; + + // delete the original record + ptr = putDeletionRecord(&msg, ptr, &rr->resrec); + if (!ptr) goto error; + + // change the rdata, add the new record + SwapRData(m, rr, mDNSfalse); + ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl); + SwapRData(m, rr, mDNSfalse); // swap rdata back to original in case we need to retransmit + if (!ptr) goto error; // (rdata gets changed permanently on success) + + if (info->lease) + { ptr = putUpdateLease(&msg, ptr, kLLQ_DefLease); if (!ptr) goto error; } - // prereq: record must exist (put record in prereq section w/ TTL 0) - ptr = PutResourceRecordTTL(&msg, ptr, &msg.h.mDNS_numPrereqs, &srs->RR_SRV.resrec, 0); - if (!ptr) { LogMsg("ERROR: uDNS_DeregisterService - PutResourceRecordTTL"); goto error; } + // don't report send errors - retransmission will occurr if necessary + err = mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, &info->ns, info->port, -1, GetAuthInfoForZone(u, &info->zone)); + if (err) debugf("ERROR: sendRecordRegistration - mDNSSendDNSMessage - %ld", err); - if (!(ptr = putDeletionRecord(&msg, ptr, &srs->RR_SRV.resrec))) goto error; - if (!(ptr = putDeletionRecord(&msg, ptr, &srs->RR_TXT.resrec))) goto error; - if (!(ptr = putDeletionRecord(&msg, ptr, &srs->RR_PTR.resrec))) goto error; - //if (!(ptr = putDeletionRecord(&msg, ptr, &srs->RR_ADV.resrec))) goto error; - //!!!KRS need to handle extras/subtypes etc + //!!! Update this when we implement retransmission for services + SetRecordRetry(m, rr); + + rr->uDNS_info.state = regState_UpdatePending; + if (&rr->uDNS_info != info) info->state = regState_UpdatePending; // set parent SRS + return; +error: + LogMsg("ERROR: SendRecordUpdate. Error formatting update message."); + info ->state = regState_Registered; + } - authInfo = GetAuthInfoForZone(u, &srs->uDNS_info.zone); - if (authInfo) +mDNSexport mStatus uDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra) + { + mStatus err = mStatus_UnknownErr; + + extra->r.resrec.RecordType = kDNSRecordTypeShared; // don't want it to conflict with the service name + extra->r.RecordCallback = mDNSNULL; // don't generate callbacks for extra RRs + + if (sr->uDNS_info.state == regState_Registered || sr->uDNS_info.state == regState_Refresh) + err = uDNS_RegisterRecord(m, &extra->r); + else { - err = mDNSSendSignedDNSMessage(m, &msg, ptr, 0, &srs->uDNS_info.ns, srs->uDNS_info.port, authInfo); - if (err) { LogMsg("ERROR: uDNS_DeregiserService - mDNSSendSignedDNSMessage - %d", err); goto error; } + err = SetupRecordRegistration(m, &extra->r); + extra->r.uDNS_info.state = regState_ExtraQueued; } - else + + if (!err) + { + extra->next = sr->Extras; + sr->Extras = extra; + } + return err; + } + +mDNSexport mStatus uDNS_UpdateRecord(mDNS *m, AuthRecord *rr) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + ServiceRecordSet *sptr, *parent = mDNSNULL; + AuthRecord *rptr; + uDNS_RegInfo *info = mDNSNULL; + + // find the record in registered service list + for (sptr = u->ServiceRegistrations; sptr; sptr = sptr->next) + if (&sptr->RR_TXT == rr) { info = &sptr->uDNS_info; parent = sptr; break; } + + if (!parent) { - err = mDNSSendDNSMessage(m, &msg, ptr, 0, &srs->uDNS_info.ns, srs->uDNS_info.port); - if (err) { LogMsg("ERROR: uDNS_DeregisterService - mDNSSendDNSMessage - %d", err); goto error; } + // record not part of a service - check individual record registrations + for (rptr = u->RecordRegistrations; rptr; rptr = rptr->next) + if (rptr == rr) { info = &rr->uDNS_info; break; } } + + if (!info) goto unreg_error; + + // uDNS-private pointers so that mDNS.c layer doesn't nuke rdata of an in-flight update + rr->uDNS_info.UpdateRData = rr->NewRData; + rr->uDNS_info.UpdateRDLen = rr->newrdlength; + rr->uDNS_info.UpdateRDCallback = rr->UpdateCallback; + rr->NewRData = mDNSNULL; - return mStatus_NoError; + switch(info->state) + { + case regState_DeregPending: + case regState_DeregDeferred: + case regState_Cancelled: + case regState_Unregistered: + // not actively registered + goto unreg_error; + + case regState_FetchingZoneData: + case regState_NATMap: + case regState_ExtraQueued: + // change rdata directly since it hasn't been sent yet + SwapRData(m, rr, mDNStrue); + return mStatus_NoError; + + case regState_Pending: + case regState_Refresh: + case regState_UpdatePending: + // registration in-flight. mark for update after service registration completes + rr->uDNS_info.UpdateQueued = mDNStrue; // note that we mark the record's Queued flag, not its parent's + return mStatus_NoError; + + case regState_Registered: + SendRecordUpdate(m, rr, info); + return mStatus_NoError; - error: - unlinkSRS(u, srs); - srs->uDNS_info.state = regState_Unregistered; - return err; + case regState_NoTarget: + LogMsg("Bad state (regState_NoTarget)"); return mStatus_UnknownErr; // state for service records only + } + + unreg_error: + LogMsg("Requested update of record %##s type %d, part of service not currently registered", + rr->resrec.name.c, rr->resrec.rrtype); + return mStatus_Invalid; } -mDNSexport void uDNS_Execute(mDNS *const m) + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Periodic Execution Routines +#endif + + +mDNSlocal mDNSs32 CheckNATMappings(mDNS *m, mDNSs32 timenow) + { + NATTraversalInfo *ptr, *cur; + mDNSs32 nextevent; + + ptr = m->uDNS_info.NATTraversals; + nextevent = timenow + MIN_UCAST_PERIODIC_EXEC; + + while (ptr) + { + cur = ptr; + ptr = ptr->next; + if (cur->retry - timenow < 0) + { + if (cur->state == NATState_Established) RefreshNATMapping(cur, m); + else if (cur->state == NATState_Request || cur->state == NATState_Refresh) + { + if (cur->ntries >= NATMAP_MAX_TRIES) cur->ReceiveResponse(cur, m, mDNSNULL, 0); // may invalidate "cur" + else SendNATMsg(cur, m); + } + } + else if (cur->retry - nextevent < 0) nextevent = cur->retry; + } + return nextevent; + } + +mDNSlocal mDNSs32 CheckQueries(mDNS *m, mDNSs32 timenow) { DNSQuestion *q; + uDNS_GlobalInfo *u = &m->uDNS_info; + LLQ_Info *llq; + mDNSs32 sendtime; + mDNSs32 nextevent = timenow + MIN_UCAST_PERIODIC_EXEC; DNSMessage msg; mStatus err; mDNSu8 *end; - mDNSs32 sendtime; - LLQ_Info *llq; - AuthRecord *rr; - ServiceRecordSet *srs; - uDNS_RegInfo *rInfo; - uDNS_GlobalInfo *u = &m->uDNS_info; - const mDNSAddr *server = getInitializedDNS(&m->uDNS_info); - mDNSs32 timenow = mDNSPlatformTimeNow(); + uDNS_QuestionInfo *info; - u->nextevent = timenow + 0x78000000; - if (!server) { debugf("uDNS_Execute - no DNS server"); return; } - - for (q = u->ActiveQueries; q; q = q->next) + u->CurrentQuery = u->ActiveQueries; + while (u->CurrentQuery) { - llq = q->uDNS_info.llq; - if (q->LongLived && llq->state != LLQ_Poll) + q = u->CurrentQuery; + info = &q->uDNS_info; + llq = info->llq; + + if (!info->internal && ((!q->LongLived && !info->Answered) || (llq && llq->state < LLQ_Established)) && + info->RestartTime + RESTART_GOODBYE_DELAY - timenow < 0) { - if (llq->state >= LLQ_InitialRequest && llq->state <= LLQ_Suspended && llq->retry <= timenow) + // if we've been spinning on restart setup, and we have known answers, give goodbyes (they may be re-added later) + while (info->knownAnswers) + { + CacheRecord *cr = info->knownAnswers; + info->knownAnswers = info->knownAnswers->next; + + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + q->QuestionCallback(m, q, &cr->resrec, mDNSfalse); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + ufree(cr); + if (q != u->CurrentQuery) { debugf("CheckQueries - question removed via callback."); break; } + } + } + if (q != u->CurrentQuery) continue; + + if (q->LongLived && llq->state != LLQ_Poll) + { + if (llq->state >= LLQ_InitialRequest && llq->state <= LLQ_Established) { - - // sanity check to avoid packet flood bugs - if (!llq->retry) - LogMsg("ERROR: retry timer not set for LLQ %s in state %d", q->qname.c, llq->state); - else if (llq->state == LLQ_Established || llq->state == LLQ_Refresh) - sendLLQRefresh(m, q, llq->origLease); - else if (llq->state == LLQ_InitialRequest) - startLLQHandshake(m, llq); - else if (llq->state == LLQ_SecondaryRequest) - sendChallengeResponse(m, q, NULL); - else if (llq->state == LLQ_Retry) - { llq->ntries = 0; startLLQHandshake(m, llq); } + if (llq->retry - timenow < 0) + { + // sanity check to avoid packet flood bugs + if (!llq->retry) + LogMsg("ERROR: retry timer not set for LLQ %##s in state %d", q->qname.c, llq->state); + else if (llq->state == LLQ_Established || llq->state == LLQ_Refresh) + sendLLQRefresh(m, q, llq->origLease); + else if (llq->state == LLQ_InitialRequest) + startLLQHandshake(m, llq, mDNSfalse); + else if (llq->state == LLQ_SecondaryRequest) + sendChallengeResponse(m, q, mDNSNULL); + else if (llq->state == LLQ_Retry) + { llq->ntries = 0; startLLQHandshake(m, llq, mDNSfalse); } + } + else if (llq->retry - nextevent < 0) nextevent = llq->retry; } } else { sendtime = q->LastQTime + q->ThisQInterval; - if (sendtime <= timenow) + if (sendtime - timenow < 0) { - err = constructQueryMsg(&msg, &end, q); - if (err) + mDNSAddr server; + if (GetServerForName(&m->uDNS_info, &q->qname, &server)) { - LogMsg("Error: uDNS_Idle - constructQueryMsg. Skipping question %s", - q->qname.c); - continue; + err = constructQueryMsg(&msg, &end, q); + if (err) LogMsg("Error: uDNS_Idle - constructQueryMsg. Skipping question %##s", q->qname.c); + else + { + err = mDNSSendDNSMessage(m, &msg, end, mDNSInterface_Any, &server, UnicastDNSPort, -1, mDNSNULL); + if (err) { debugf("ERROR: uDNS_idle - mDNSSendDNSMessage - %ld", err); } // surpress syslog messages if we have no network + q->LastQTime = timenow; + if (q->ThisQInterval < MAX_UCAST_POLL_INTERVAL) q->ThisQInterval = q->ThisQInterval * 2; + } } - err = mDNSSendDNSMessage(m, &msg, end, q->InterfaceID, server, UnicastDNSPort); - if (err) { debugf("ERROR: uDNS_idle - mDNSSendDNSMessage - %d", err); } // surpress syslog messages if we have no network - q->LastQTime = timenow; - if (q->ThisQInterval < MAX_UCAST_POLL_INTERVAL) q->ThisQInterval = q->ThisQInterval * 2; } - else if (u->nextevent - sendtime > 0) u->nextevent = sendtime; + else if (sendtime - nextevent < 0) nextevent = sendtime; } + u->CurrentQuery = u->CurrentQuery->next; } + return nextevent; + } +mDNSlocal mDNSs32 CheckRecordRegistrations(mDNS *m, mDNSs32 timenow) + { + AuthRecord *rr; + uDNS_RegInfo *rInfo; + uDNS_GlobalInfo *u = &m->uDNS_info; + mDNSs32 nextevent = timenow + MIN_UCAST_PERIODIC_EXEC; + //!!!KRS list should be pre-sorted by expiration for (rr = u->RecordRegistrations; rr; rr = rr->next) { rInfo = &rr->uDNS_info; + if (rInfo->state == regState_Pending || rInfo->state == regState_DeregPending || rInfo->state == regState_UpdatePending || rInfo->state == regState_DeregDeferred || rInfo->state == regState_Refresh) + { + if (rr->LastAPTime + rr->ThisAPInterval - timenow < 0) + { +#if MDNS_DEBUGMSGS + char *op = "(unknown operation)"; + if (rInfo->state == regState_Pending) op = "registration"; + else if (rInfo->state == regState_DeregPending) op = "deregistration"; + else if (rInfo->state == regState_Refresh) op = "refresh"; + debugf("Retransmit record %s %##s", op, rr->resrec.name.c); +#endif + //LogMsg("Retransmit record %##s", rr->resrec.name.c); + if (rInfo->state == regState_DeregPending) SendRecordDeregistration(m, rr); + else if (rInfo->state == regState_UpdatePending) SendRecordUpdate(m, rr, rInfo); + else sendRecordRegistration(m, rr); + } + if (rr->LastAPTime + rr->ThisAPInterval - nextevent < 0) nextevent = rr->LastAPTime + rr->ThisAPInterval; + } if (rInfo->lease && rInfo->state == regState_Registered && rInfo->expire > 0) { - if (rInfo->expire < timenow) + if (rInfo->expire - timenow < 0) { - debugf("refreshing record %s", rr->resrec.name.c); + debugf("refreshing record %##s", rr->resrec.name.c); rInfo->state = regState_Refresh; sendRecordRegistration(m, rr); } - else if (u->nextevent - rInfo->expire > 0) u->nextevent = rInfo->expire; - } + if (rInfo->expire - nextevent < 0) nextevent = rInfo->expire; + } } - //!!!KRS list should be pre-sorted by expiration - for (srs = u->ServiceRegistrations; srs; srs = srs->next) + return nextevent; + } + +mDNSlocal mDNSs32 CheckServiceRegistrations(mDNS *m, mDNSs32 timenow) + { + ServiceRecordSet *s = m->uDNS_info.ServiceRegistrations; + uDNS_RegInfo *rInfo; + mDNSs32 nextevent = timenow + MIN_UCAST_PERIODIC_EXEC; + + // Note: ServiceRegistrations list is in the order they were created; important for in-order event delivery + while (s) { + ServiceRecordSet *srs = s; + // NOTE: Must advance s here -- SendServiceDeregistration may delete the object we're looking at, + // and then if we tried to do srs = srs->next at the end we'd be referencing a dead object + s = s->next; + rInfo = &srs->uDNS_info; + if (rInfo->state == regState_Pending || rInfo->state == regState_DeregPending || rInfo->state == regState_DeregDeferred || rInfo->state == regState_Refresh) + { + if (srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval - timenow < 0) + { +#if MDNS_DEBUGMSGS + char *op = "unknown"; + if (rInfo->state == regState_Pending) op = "registration"; + else if (rInfo->state == regState_DeregPending) op = "deregistration"; + else if (rInfo->state == regState_Refresh) op = "refresh"; + debugf("Retransmit service %s %##s", op, srs->RR_SRV.resrec.name.c); +#endif + if (rInfo->state == regState_DeregPending) { SendServiceDeregistration(m, srs); continue; } + else SendServiceRegistration (m, srs); + } + if (nextevent - srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval > 0) + nextevent = srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval; + } + if (rInfo->lease && rInfo->state == regState_Registered && rInfo->expire > 0) { - if (rInfo->expire < timenow) + if (rInfo->expire - timenow < 0) { - debugf("refreshing service %s", srs->RR_SRV.resrec.name.c); + debugf("refreshing service %##s", srs->RR_SRV.resrec.name.c); rInfo->state = regState_Refresh; SendServiceRegistration(m, srs); } - else if (u->nextevent - rInfo->expire > 0) u->nextevent = rInfo->expire; - } + if (rInfo->expire - nextevent < 0) nextevent = rInfo->expire; + } + } + return nextevent; + } + +mDNSexport void uDNS_Execute(mDNS *const m) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + mDNSs32 nexte, timenow = mDNSPlatformTimeNow(m); + + u->nextevent = timenow + MIN_UCAST_PERIODIC_EXEC; + + nexte = CheckNATMappings(m, timenow); + if (nexte - u->nextevent < 0) u->nextevent = nexte; + + nexte = CheckQueries(m, timenow); + if (nexte - u->nextevent < 0) u->nextevent = nexte; + + nexte = CheckRecordRegistrations(m, timenow); + if (nexte - u->nextevent < 0) u->nextevent = nexte; + + nexte = CheckServiceRegistrations(m, timenow); + if (nexte - u->nextevent < 0) u->nextevent = nexte; + + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Startup, Shutdown, and Sleep +#endif + +// DeregisterActive causes active LLQs to be removed from the server, e.g. before sleep. Pass false +// following a location change, as the server will reject deletions from a source address different +// from the address on which the LLQ was created. + +mDNSlocal void SuspendLLQs(mDNS *m, mDNSBool DeregisterActive) + { + DNSQuestion *q; + LLQ_Info *llq; + for (q = m->uDNS_info.ActiveQueries; q; q = q->next) + { + llq = q->uDNS_info.llq; + if (q->LongLived && llq) + { + if (llq->state == LLQ_GetZoneInfo) + { + debugf("Marking %##s suspend-deferred", q->qname.c); + llq->state = LLQ_SuspendDeferred; // suspend once we're done getting zone info + } + else if (llq->state < LLQ_Suspended) + { + if (DeregisterActive && (llq->state == LLQ_Established || llq->state == LLQ_Refresh)) + { debugf("Deleting LLQ %##s", q->qname.c); sendLLQRefresh(m, q, 0); } + debugf("Marking %##s suspended", q->qname.c); + llq->state = LLQ_Suspended; + ubzero(llq->id, 8); + } + else if (llq->state == LLQ_Poll) { debugf("Marking %##s suspended-poll", q->qname.c); llq->state = LLQ_SuspendedPoll; } + if (llq->NATMap) llq->NATMap = mDNSfalse; // may not need nat mapping if we restart with new route + } + } + CheckForUnreferencedLLQMapping(m); + } + +mDNSlocal void RestartQueries(mDNS *m) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + DNSQuestion *q; + LLQ_Info *llqInfo; + mDNSs32 timenow = mDNSPlatformTimeNow(m); + + u->CurrentQuery = u->ActiveQueries; + while (u->CurrentQuery) + { + q = u->CurrentQuery; + u->CurrentQuery = u->CurrentQuery->next; + llqInfo = q->uDNS_info.llq; + q->uDNS_info.RestartTime = timenow; + q->uDNS_info.Answered = mDNSfalse; + if (q->LongLived) + { + if (!llqInfo) { LogMsg("Error: RestartQueries - %##s long-lived with NULL info", q->qname.c); continue; } + if (llqInfo->state == LLQ_Suspended || llqInfo->state == LLQ_NatMapWait) + { + llqInfo->ntries = -1; + llqInfo->deriveRemovesOnResume = mDNStrue; + startLLQHandshake(m, llqInfo, mDNStrue); // we set defer to true since several events that may generate restarts often arrive in rapid succession, and this cuts unnecessary packets + } + else if (llqInfo->state == LLQ_SuspendDeferred) + llqInfo->state = LLQ_GetZoneInfo; // we never finished getting zone data - proceed as usual + else if (llqInfo->state == LLQ_SuspendedPoll) + { + // if we were polling, we may have had bad zone data due to firewall, etc. - refetch + llqInfo->ntries = 0; + llqInfo->deriveRemovesOnResume = mDNStrue; + llqInfo->state = LLQ_GetZoneInfo; + startGetZoneData(&q->qname, m, mDNSfalse, mDNStrue, startLLQHandshakeCallback, llqInfo); + } + } + else { q->LastQTime = timenow; q->ThisQInterval = INIT_UCAST_POLL_INTERVAL; } // trigger poll in 1 second (to reduce packet rate when restarts come in rapid succession) + } + } + +mDNSexport void mDNS_UpdateLLQs(mDNS *m) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + + mDNS_Lock(m); + if (u->LLQNatInfo) + { + NATTraversalInfo *nat = u->LLQNatInfo; + u->LLQNatInfo = mDNSNULL; + DeleteNATPortMapping(m, nat, mDNSNULL); + } + SuspendLLQs(m, mDNStrue); + RestartQueries(m); + mDNS_Unlock(m); + } + +// simplest sleep logic - rather than having sleep states that must be dealt with explicitly in all parts of +// the code, we simply send a deregistration, and put the service in Refresh state, with a timeout far enough +// in the future that we'll sleep (or the sleep will be cancelled) before it is retransmitted. Then to wake, +// we just move up the timers. + + + +mDNSlocal void SleepRecordRegistrations(mDNS *m) + { + DNSMessage msg; + AuthRecord *rr = m->uDNS_info.RecordRegistrations; + mDNSs32 timenow = mDNSPlatformTimeNow(m); + + while (rr) + { + if (rr->uDNS_info.state == regState_Registered || + rr->uDNS_info.state == regState_Refresh) + { + mDNSu8 *ptr = msg.data, *end = (mDNSu8 *)&msg + sizeof(DNSMessage); + InitializeDNSMessage(&msg.h, newMessageID(&m->uDNS_info), UpdateReqFlags); + + // construct deletion update + ptr = putZone(&msg, ptr, end, &rr->uDNS_info.zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass)); + if (!ptr) { LogMsg("Error: SleepRecordRegistrations - could not put zone"); return; } + ptr = putDeletionRecord(&msg, ptr, &rr->resrec); + if (!ptr) { LogMsg("Error: SleepRecordRegistrations - could not put deletion record"); return; } + + mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, &rr->uDNS_info.ns, rr->uDNS_info.port, -1, GetAuthInfoForZone(&m->uDNS_info, &rr->uDNS_info.zone)); + rr->uDNS_info.state = regState_Refresh; + rr->LastAPTime = timenow; + rr->ThisAPInterval = 300 * mDNSPlatformOneSecond; + } + rr = rr->next; + } + } + +mDNSlocal void WakeRecordRegistrations(mDNS *m) + { + mDNSs32 timenow = mDNSPlatformTimeNow(m); + AuthRecord *rr = m->uDNS_info.RecordRegistrations; + + while (rr) + { + if (rr->uDNS_info.state == regState_Refresh) + { + // trigger slightly delayed refresh (we usually get this message before kernel is ready to send packets) + rr->LastAPTime = timenow; + rr->ThisAPInterval = INIT_UCAST_POLL_INTERVAL; + } + rr = rr->next; + } + } + +mDNSlocal void SleepServiceRegistrations(mDNS *m) + { + mDNSs32 timenow = mDNSPlatformTimeNow(m); + ServiceRecordSet *srs = m->uDNS_info.ServiceRegistrations; + while(srs) + { + if (srs->uDNS_info.state == regState_Registered || + srs->uDNS_info.state == regState_Refresh) + { + mDNSOpaque16 origid = srs->uDNS_info.id; + srs->uDNS_info.state = regState_DeregPending; // state expected by SendDereg() + SendServiceDeregistration(m, srs); + srs->uDNS_info.id = origid; + srs->uDNS_info.state = regState_Refresh; + srs->RR_SRV.LastAPTime = timenow; + srs->RR_SRV.ThisAPInterval = 300 * mDNSPlatformOneSecond; + } + srs = srs->next; + } + } + +mDNSlocal void WakeServiceRegistrations(mDNS *m) + { + mDNSs32 timenow = mDNSPlatformTimeNow(m); + ServiceRecordSet *srs = m->uDNS_info.ServiceRegistrations; + while(srs) + { + if (srs->uDNS_info.state == regState_Refresh) + { + // trigger slightly delayed refresh (we usually get this message before kernel is ready to send packets) + srs->RR_SRV.LastAPTime = timenow; + srs->RR_SRV.ThisAPInterval = INIT_UCAST_POLL_INTERVAL; + } + srs = srs->next; } } mDNSexport void uDNS_Init(mDNS *const m) { mDNSPlatformMemZero(&m->uDNS_info, sizeof(uDNS_GlobalInfo)); - m->uDNS_info.nextevent = mDNSPlatformTimeNow() + 0x78000000; + m->uDNS_info.nextevent = m->timenow_last + 0x78000000; + } + +mDNSexport void uDNS_Sleep(mDNS *m) + { + SuspendLLQs(m, mDNStrue); + SleepServiceRegistrations(m); + SleepRecordRegistrations(m); + } + +mDNSexport void uDNS_Wake(mDNS *m) + { + RestartQueries(m); + WakeServiceRegistrations(m); + WakeRecordRegistrations(m); } diff --git a/mDNSCore/uDNS.h b/mDNSCore/uDNS.h index 52fc878..20edb2d 100755 --- a/mDNSCore/uDNS.h +++ b/mDNSCore/uDNS.h @@ -3,8 +3,6 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -25,6 +23,58 @@ Change History (most recent first): $Log: uDNS.h,v $ +Revision 1.27 2004/11/23 04:06:50 cheshire +Get rid of floating point constant -- in a small embedded device, bringing in all +the floating point libraries just to halve an integer value is a bit too heavyweight. + +Revision 1.26 2004/11/22 17:49:15 ksekar +Changed INIT_REFRESH from fraction to decimal + +Revision 1.25 2004/11/22 17:16:20 ksekar + Unicast services don't disappear when you disable all networking + +Revision 1.24 2004/11/19 04:24:08 ksekar + Security: Enforce a "window" on one-shot wide-area queries + +Revision 1.23 2004/11/18 18:04:21 ksekar +Add INIT_REFRESH constant + +Revision 1.22 2004/11/15 20:09:24 ksekar + Wide Area support for Add/Remove record + +Revision 1.21 2004/11/11 20:14:55 ksekar + Wide-Area registrations not deregistered on sleep + +Revision 1.20 2004/10/16 00:16:59 cheshire + Replace IP TTL 255 check with local subnet source address check + +Revision 1.19 2004/09/17 01:08:49 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.18 2004/09/03 19:23:05 ksekar +: Need retransmission mechanism for wide-area service registrations + +Revision 1.17 2004/09/01 03:59:29 ksekar +: Conditionally compile out uDNS code on Windows + +Revision 1.16 2004/08/25 00:37:27 ksekar +: Cleanup DynDNS hostname registration code + +Revision 1.15 2004/07/30 17:40:06 ksekar +: TXT Record updates not available for wide-area services + +Revision 1.14 2004/07/29 19:27:15 ksekar +NATPMP Support - minor fixes and cleanup + +Revision 1.13 2004/07/29 02:03:35 ksekar +Delete unused #define and structure field + +Revision 1.12 2004/07/26 22:49:30 ksekar +: Feature #9516: Need support for NATPMP in client + Revision 1.11 2004/06/17 01:13:11 ksekar : polling interval too short @@ -65,49 +115,58 @@ Revision 1.1 2003/12/13 03:05:27 ksekar #ifndef __UDNS_H_ #define __UDNS_H_ -#include "mDNSClientAPI.h" +#include "mDNSEmbeddedAPI.h" #include "DNSCommon.h" #ifdef __cplusplus extern "C" { #endif -#define INIT_UCAST_POLL_INTERVAL (15 * mDNSPlatformOneSecond) +#define RESTART_GOODBYE_DELAY (2 * mDNSPlatformOneSecond) // delay after restarting LLQ before nuking previous known answers (avoids flutter if we restart before we have networking up) +#define MIN_UCAST_PERIODIC_EXEC (5 * mDNSPlatformOneSecond) +#define INIT_UCAST_POLL_INTERVAL mDNSPlatformOneSecond // this interval is used after send failures on network transitions + // which typically heal quickly, so we start agressively and exponentially back off #define MAX_UCAST_POLL_INTERVAL (15 * 60 * mDNSPlatformOneSecond) -#define NO_GOODBYE // will we receive goodbye packets from the server? +#define RESPONSE_WINDOW (60 * mDNSPlatformOneSecond) // require server responses within one minute of request #define UPDATE_PORT_NAME "_dns-update._udp." #define LLQ_PORT_NAME "_dns-llq._udp" // Entry points into unicast-specific routines -extern void uDNS_AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set); -extern void uDNS_DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set); - extern mStatus uDNS_StartQuery(mDNS *const m, DNSQuestion *const question); -extern mDNSBool IsActiveUnicastQuery(DNSQuestion *const question, uDNS_GlobalInfo *u); // returns true if OK to call StopQuery +extern mDNSBool uDNS_IsActiveQuery(DNSQuestion *const question, uDNS_GlobalInfo *u); // returns true if OK to call StopQuery extern mStatus uDNS_StopQuery(mDNS *const m, DNSQuestion *const question); - -// SuspendLLQs stops all LLQs, preserving known answers. RestartLLQs re-starts these suspended LLQs, generating appropriate add/removes -// Call SuspendLLQs prior to sleep, and on shutdown. Call RestartLLQs on wake from sleep. -extern void uDNS_SuspendLLQs(mDNS *m); -extern void uDNS_RestartLLQs(mDNS *m); + +extern void uDNS_Init(mDNS *m); +extern void uDNS_Sleep(mDNS *m); +extern void uDNS_Wake(mDNS *m); +#define uDNS_Close uDNS_Sleep + +// uDNS_UpdateRecord +// following fields must be set, and the update validated, upon entry. +// rr->NewRData +// rr->newrdlength +// rr->UpdateCallback + +extern mStatus uDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra); +extern mStatus uDNS_UpdateRecord(mDNS *m, AuthRecord *rr); extern mStatus uDNS_RegisterRecord(mDNS *const m, AuthRecord *const rr); extern mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr); extern mStatus uDNS_RegisterService(mDNS *const m, ServiceRecordSet *srs); extern mStatus uDNS_DeregisterService(mDNS *const m, ServiceRecordSet *srs); -extern void uDNS_UpdateServiceTargets(mDNS *const m); // call following namechange // integer fields of msg header must be in HOST byte order before calling this routine extern void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, -const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, -const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, mDNSu8 ttl); + const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, + const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID); +extern void uDNS_ReceiveNATMap(mDNS *m, mDNSu8 *pkt, mDNSu16 len); + // returns time of next scheduled event extern void uDNS_Execute(mDNS *const m); -extern void uDNS_Init(mDNS *const m); #ifdef __cplusplus } diff --git a/mDNSMacOS9/CarbonResource.r b/mDNSMacOS9/CarbonResource.r index 853a4ac..105a398 100644 --- a/mDNSMacOS9/CarbonResource.r +++ b/mDNSMacOS9/CarbonResource.r @@ -3,8 +3,6 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in diff --git a/mDNSMacOS9/Mac OS Test Responder.c b/mDNSMacOS9/Mac OS Test Responder.c index 9ff78dc..cf03862 100644 --- a/mDNSMacOS9/Mac OS Test Responder.c +++ b/mDNSMacOS9/Mac OS Test Responder.c @@ -3,8 +3,6 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -25,6 +23,16 @@ Change History (most recent first): $Log: Mac\040OS\040Test\040Responder.c,v $ +Revision 1.23 2004/09/17 01:08:50 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.22 2004/08/13 23:25:01 cheshire +Now that we do both uDNS and mDNS, global replace "m->hostname" with +"m->MulticastHostname" for clarity + Revision 1.21 2004/03/12 21:30:25 cheshire Build a System-Context Shared Library from mDNSCore, for the benefit of developers like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code. @@ -53,7 +61,7 @@ Update to APSL 2.0 #include // For WaitNextEvent() #include // For SIOUXHandleOneEvent() -#include "mDNSClientAPI.h" // Defines the interface to the client layer above +#include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above #include "mDNSMacOS9.h" // Defines the specific types needed to run mDNS on this platform @@ -164,7 +172,7 @@ mDNSlocal OSStatus mDNSResponderTestSetup(mDNS *m) char buffer[MAX_ESCAPED_DOMAIN_NAME]; mDNSv4Addr ip = m->HostInterfaces->ip.ip.v4; - ConvertDomainNameToCString(&m->hostname, buffer); + ConvertDomainNameToCString(&m->MulticastHostname, buffer); printf("Name %s\n", buffer); printf("IP %d.%d.%d.%d\n", ip.b[0], ip.b[1], ip.b[2], ip.b[3]); diff --git a/mDNSMacOS9/Mac OS Test Searcher.c b/mDNSMacOS9/Mac OS Test Searcher.c index 6b5d90d..f2fb9ce 100644 --- a/mDNSMacOS9/Mac OS Test Searcher.c +++ b/mDNSMacOS9/Mac OS Test Searcher.c @@ -3,8 +3,6 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -25,6 +23,20 @@ Change History (most recent first): $Log: Mac\040OS\040Test\040Searcher.c,v $ +Revision 1.20 2004/10/19 21:33:18 cheshire + Cannot resolve non-local registrations using the mach API +Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name +doesn't force multicast unless you set this flag to indicate explicitly that this is what you want + +Revision 1.19 2004/09/17 01:08:50 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.18 2004/09/16 21:59:16 cheshire +For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr + Revision 1.17 2004/06/10 04:37:27 cheshire Add new parameter in mDNS_GetDomains() @@ -51,7 +63,7 @@ Update to APSL 2.0 #include // For WaitNextEvent() #include // For SIOUXHandleOneEvent() -#include "mDNSClientAPI.h" // Defines the interface to the client layer above +#include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above #include "mDNSMacOS9.h" // Defines the specific types needed to run mDNS on this platform typedef struct @@ -155,7 +167,7 @@ static void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRe info->i.name = answer->rdata->u.name; info->i.InterfaceID = answer->InterfaceID; info->i.ip.type = mDNSAddrType_IPv4; - info->i.ip.ip.v4 = zeroIPAddr; + info->i.ip.ip.v4 = zerov4Addr; info->i.port = zeroIPPort; info->add = AddRecord; info->dom = mDNSfalse; @@ -188,7 +200,7 @@ static void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceReco info->i.name = answer->rdata->u.name; info->i.InterfaceID = answer->InterfaceID; info->i.ip.type = mDNSAddrType_IPv4; - info->i.ip.ip.v4 = zeroIPAddr; + info->i.ip.ip.v4 = zerov4Addr; info->i.port = zeroIPPort; info->add = AddRecord; info->dom = mDNStrue; @@ -253,7 +265,7 @@ int main() printf("\nSending mDNS service lookup queries and waiting for responses...\n\n"); MakeDomainNameFromDNSNameString(&srvtype, "_http._tcp."); MakeDomainNameFromDNSNameString(&srvdom, "local."); - err = mDNS_StartBrowse(&mDNSStorage, &browsequestion, &srvtype, &srvdom, mDNSInterface_Any, FoundInstance, &services); + err = mDNS_StartBrowse(&mDNSStorage, &browsequestion, &srvtype, &srvdom, mDNSInterface_Any, mDNSfalse, FoundInstance, &services); if (err) break; err = mDNS_GetDomains(&mDNSStorage, &domainquestion, mDNS_DomainTypeBrowse, NULL, mDNSInterface_Any, FoundDomain, &services); if (err) break; diff --git a/mDNSMacOS9/Responder.c b/mDNSMacOS9/Responder.c index d1612f8..2e7135e 100644 --- a/mDNSMacOS9/Responder.c +++ b/mDNSMacOS9/Responder.c @@ -3,8 +3,6 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in diff --git a/mDNSMacOS9/Searcher.c b/mDNSMacOS9/Searcher.c index 4d0d494..a0a562f 100644 --- a/mDNSMacOS9/Searcher.c +++ b/mDNSMacOS9/Searcher.c @@ -3,8 +3,6 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in diff --git a/mDNSMacOS9/SubTypeTester.c b/mDNSMacOS9/SubTypeTester.c new file mode 100644 index 0000000..7c153a3 --- /dev/null +++ b/mDNSMacOS9/SubTypeTester.c @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: SubTypeTester.c,v $ +Revision 1.5 2004/09/17 01:08:50 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.4 2004/09/16 00:24:49 cheshire + Fix unsafe use of mDNSPlatformTimeNow() + +Revision 1.3 2004/08/13 23:25:01 cheshire +Now that we do both uDNS and mDNS, global replace "m->hostname" with +"m->MulticastHostname" for clarity + +Revision 1.2 2004/08/04 22:11:30 cheshire + Current method of doing subtypes causes name collisions +Change to use "._sub." instead of ".s." to mark subtypes. + +Revision 1.1 2004/06/11 00:03:28 cheshire +Add code for testing avail/busy subtypes + + + */ + +#include // For printf() +#include // For strlen() etc. + +#include // For WaitNextEvent() +#include // For SIOUXHandleOneEvent() + +#include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above + +#include "mDNSMacOS9.h" // Defines the specific types needed to run mDNS on this platform + +// These don't have to be globals, but their memory does need to remain valid for as +// long as the search is going on. They are declared as globals here for simplicity. +static mDNS m; +static mDNS_PlatformSupport p; +static ServiceRecordSet p1, p2; +static AuthRecord availRec1, availRec2; +static Boolean availRec2Active; + +// This sample code just calls mDNS_RenameAndReregisterService to automatically pick a new +// unique name for the service. For a device such as a printer, this may be appropriate. +// For a device with a user interface, and a screen, and a keyboard, the appropriate +// response may be to prompt the user and ask them to choose a new name for the service. +mDNSlocal void Callback(mDNS *const m, ServiceRecordSet *const sr, mStatus result) + { + switch (result) + { + case mStatus_NoError: debugf("Callback: %##s Name Registered", sr->RR_SRV.resrec.name.c); break; + case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", sr->RR_SRV.resrec.name.c); break; + case mStatus_MemFree: debugf("Callback: %##s Memory Free", sr->RR_SRV.resrec.name.c); break; + default: debugf("Callback: %##s Unknown Result %d", sr->RR_SRV.resrec.name.c, result); break; + } + + if (result == mStatus_NameConflict) mDNS_RenameAndReregisterService(m, sr, mDNSNULL); + } + +// RegisterService() is a simple wrapper function which takes C string +// parameters, converts them to domainname parameters, and calls mDNS_RegisterService() +mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset, + UInt16 PortAsNumber, const char txtinfo[], + const domainlabel *const n, const char type[], const char domain[]) + { + domainname t; + domainname d; + char buffer[MAX_ESCAPED_DOMAIN_NAME]; + UInt8 txtbuffer[512]; + + MakeDomainNameFromDNSNameString(&t, type); + MakeDomainNameFromDNSNameString(&d, domain); + + if (txtinfo) + { + strncpy((char*)txtbuffer+1, txtinfo, sizeof(txtbuffer)-1); + txtbuffer[0] = (UInt8)strlen(txtinfo); + } + else + txtbuffer[0] = 0; + + mDNS_RegisterService(m, recordset, + n, &t, &d, // Name, type, domain + mDNSNULL, mDNSOpaque16fromIntVal(PortAsNumber), + txtbuffer, (mDNSu16)(1+txtbuffer[0]), // TXT data, length + mDNSNULL, 0, // Subtypes (none) + mDNSInterface_Any, // Interface ID + Callback, mDNSNULL); // Callback and context + + ConvertDomainNameToCString(&recordset->RR_SRV.resrec.name, buffer); + printf("Made Service Records for %s\n", buffer); + } + +// RegisterFakeServiceForTesting() simulates the effect of services being registered on +// dynamically-allocated port numbers. No real service exists on that port -- this is just for testing. +mDNSlocal void RegisterFakeServiceForTesting(mDNS *m, ServiceRecordSet *recordset, const char txtinfo[], + const char name[], const char type[], const char domain[]) + { + static UInt16 NextPort = 0xF000; + domainlabel n; + MakeDomainLabelFromLiteralString(&n, name); + RegisterService(m, recordset, NextPort++, txtinfo, &n, type, domain); + } + +// Done once on startup, and then again every time our address changes +mDNSlocal OSStatus mDNSResponderTestSetup(mDNS *m) + { + char buffer[MAX_ESCAPED_DOMAIN_NAME]; + mDNSv4Addr ip = m->HostInterfaces->ip.ip.v4; + + ConvertDomainNameToCString(&m->MulticastHostname, buffer); + printf("Name %s\n", buffer); + printf("IP %d.%d.%d.%d\n", ip.b[0], ip.b[1], ip.b[2], ip.b[3]); + + printf("\n"); + printf("Registering Service Records\n"); + // Create example printer discovery records + //static ServiceRecordSet p1, p2; + + RegisterFakeServiceForTesting(m, &p1, "", "One", "_raop._tcp.", "local."); + RegisterFakeServiceForTesting(m, &p2, "", "Two", "_raop._tcp.", "local."); + + return(kOTNoError); + } + +mDNSlocal void AvailCallback(mDNS *const m, AuthRecord *const rr, mStatus result) + { + Boolean *b = (Boolean *)rr->RecordContext; + (void)m; // Unused + // Signal that our record is now free for re-use + if (result == mStatus_MemFree) *b = false; + } + +mDNSlocal OSStatus mDNSResponderSetAvail(mDNS *m, AuthRecord *rr, ServiceRecordSet *sr) + { + // 1. Initialize required fields of AuthRecord + // 2. Set name of subtype PTR record to our special subtype name denoting "available" instances + // 3. Set target of subtype PTR record to point to our SRV record (exactly the same as the main service PTR record) + // 4. And register it + mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSInterface_Any, kDNSType_PTR, 2*3600, kDNSRecordTypeShared, AvailCallback, &availRec2Active); + MakeDomainNameFromDNSNameString(&rr->resrec.name, "a._sub._raop._tcp.local."); + AssignDomainName(rr->resrec.rdata->u.name, sr->RR_SRV.resrec.name); + return(mDNS_Register(m, rr)); + } + +// YieldSomeTime() just cooperatively yields some time to other processes running on classic Mac OS +mDNSlocal Boolean YieldSomeTime(UInt32 milliseconds) + { + extern Boolean SIOUXQuitting; + EventRecord e; + WaitNextEvent(everyEvent, &e, milliseconds / 17, NULL); + SIOUXHandleOneEvent(&e); + return(SIOUXQuitting); + } + +int main() + { + mStatus err; + Boolean DoneSetup = false; + mDNSs32 nextAvail, nextBusy; + + SIOUXSettings.asktosaveonclose = false; + SIOUXSettings.userwindowtitle = "\pMulticast DNS Responder"; + + printf("Multicast DNS Responder\n\n"); + printf("This software reports errors using MacsBug breaks,\n"); + printf("so if you don't have MacsBug installed your Mac may crash.\n\n"); + printf("******************************************************************************\n"); + + err = InitOpenTransport(); + if (err) { debugf("InitOpenTransport failed %d", err); return(err); } + + err = mDNS_Init(&m, &p, mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize, + mDNS_Init_AdvertiseLocalAddresses, mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); + if (err) return(err); + + while (!YieldSomeTime(35)) + { +#if MDNS_ONLYSYSTEMTASK + // For debugging, use "#define MDNS_ONLYSYSTEMTASK 1" and call mDNSPlatformIdle() periodically. + // For shipping code, don't define MDNS_ONLYSYSTEMTASK, and you don't need to call mDNSPlatformIdle() + extern void mDNSPlatformIdle(mDNS *const m); + mDNSPlatformIdle(&m); // Only needed for debugging version +#endif + if (m.mDNSPlatformStatus == mStatus_NoError && !DoneSetup) + { + DoneSetup = true; + printf("\nListening for mDNS queries...\n"); + mDNSResponderTestSetup(&m); + mDNSResponderSetAvail(&m, &availRec1, &p1); + availRec2Active = false; + nextAvail = mDNS_TimeNow(&m) + mDNSPlatformOneSecond * 10; + nextBusy = mDNS_TimeNow(&m) + mDNSPlatformOneSecond * 15; + } + + if (DoneSetup) + { + // We check availRec2.RecordType because we don't want to re-register this record + // if the previous mDNS_Deregister() has not yet completed + if (mDNS_TimeNow(&m) - nextAvail > 0 && !availRec2Active) + { + printf("Setting Two now available\n"); + availRec2Active = true; + mDNSResponderSetAvail(&m, &availRec2, &p2); + nextAvail = nextBusy + mDNSPlatformOneSecond * 10; + } + else if (mDNS_TimeNow(&m) - nextBusy > 0) + { + printf("Setting Two now busy\n"); + mDNS_Deregister(&m, &availRec2); + nextBusy = nextAvail + mDNSPlatformOneSecond * 5; + } + } + } + + if (p1.RR_SRV.resrec.RecordType) mDNS_DeregisterService(&m, &p1); + if (p2.RR_SRV.resrec.RecordType) mDNS_DeregisterService(&m, &p2); + if (availRec1.resrec.RecordType) mDNS_Deregister(&m, &availRec1); + if (availRec2Active) mDNS_Deregister(&m, &availRec2); + + mDNS_Close(&m); + + return(0); + } diff --git a/mDNSMacOS9/mDNS.mcp b/mDNSMacOS9/mDNS.mcp index fdf6c0e..24cdedd 100644 Binary files a/mDNSMacOS9/mDNS.mcp and b/mDNSMacOS9/mDNS.mcp differ diff --git a/mDNSMacOS9/mDNSLibrary.c b/mDNSMacOS9/mDNSLibrary.c index a8ac4a9..13dc10a 100644 --- a/mDNSMacOS9/mDNSLibrary.c +++ b/mDNSMacOS9/mDNSLibrary.c @@ -3,8 +3,6 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -25,6 +23,12 @@ Change History (most recent first): $Log: mDNSLibrary.c,v $ +Revision 1.2 2004/09/17 01:08:50 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + Revision 1.1 2004/03/12 21:30:26 cheshire Build a System-Context Shared Library from mDNSCore, for the benefit of developers like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code. @@ -33,7 +37,7 @@ like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code // Define the required CFM Shared Library entry and exit points #include -#include "mDNSClientAPI.h" // Defines the interface to the client layer above +#include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above #include "mDNSMacOS9.h" // Defines the specific types needed to run mDNS on this platform mDNS mDNSStorage; diff --git a/mDNSMacOS9/mDNSLibraryLoader.c b/mDNSMacOS9/mDNSLibraryLoader.c index c708eac..d671d77 100644 --- a/mDNSMacOS9/mDNSLibraryLoader.c +++ b/mDNSMacOS9/mDNSLibraryLoader.c @@ -3,8 +3,6 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in diff --git a/mDNSMacOS9/mDNSLibraryResources.r b/mDNSMacOS9/mDNSLibraryResources.r index c77311c..b699ed5 100644 --- a/mDNSMacOS9/mDNSLibraryResources.r +++ b/mDNSMacOS9/mDNSLibraryResources.r @@ -3,8 +3,6 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -25,6 +23,45 @@ Change History (most recent first): $Log: mDNSLibraryResources.r,v $ +Revision 1.17 2004/12/13 21:54:30 cheshire +mDNSResponder-87 + +Revision 1.16 2004/12/06 19:09:47 cheshire +mDNSResponder-86 + +Revision 1.15 2004/11/29 23:36:15 cheshire +mDNSResponder-85 + +Revision 1.14 2004/11/23 03:46:31 cheshire +mDNSResponder-84 + +Revision 1.13 2004/10/26 20:30:30 cheshire +mDNSResponder-82 + +Revision 1.12 2004/10/14 23:38:04 cheshire +mDNSResponder-80 + +Revision 1.11 2004/10/07 21:49:15 cheshire +mDNSResponder-79 + +Revision 1.10 2004/09/25 02:52:09 cheshire +mDNSResponder-78 + +Revision 1.9 2004/09/22 22:52:07 cheshire +mDNSResponder-77 + +Revision 1.8 2004/09/21 00:20:52 cheshire +mDNSResponder-76 + +Revision 1.7 2004/09/15 19:45:06 cheshire +mDNSResponder-75 + +Revision 1.6 2004/09/09 23:32:35 cheshire +mDNSResponder-74 + +Revision 1.5 2004/08/10 21:51:45 cheshire +mDNSResponder-69 + Revision 1.4 2004/06/10 20:28:16 cheshire Update version string to 1.0a66 @@ -55,15 +92,15 @@ like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code resource 'vers' (1, purgeable) { - 0x01, 0x00, alpha, 66, verUS, - "1.0a66", - "Multicast DNS & DNS Service Discovery 1.0a66" + 0x01, 0x00, alpha, 87, verUS, + "1.0a87", + "Multicast DNS & DNS Service Discovery 1.0a87" }; resource 'vers' (2, purgeable) { - 0x01, 0x00, alpha, 66, verUS, - "1.0a66", + 0x01, 0x00, alpha, 87, verUS, + "1.0a87", "developer.apple.com/darwin/projects/rendezvous/" }; diff --git a/mDNSMacOS9/mDNSMacOS9.c b/mDNSMacOS9/mDNSMacOS9.c index 6563112..d38facc 100644 --- a/mDNSMacOS9/mDNSMacOS9.c +++ b/mDNSMacOS9/mDNSMacOS9.c @@ -3,8 +3,6 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -25,6 +23,49 @@ Change History (most recent first): $Log: mDNSMacOS9.c,v $ +Revision 1.41 2004/10/16 00:17:00 cheshire + Replace IP TTL 255 check with local subnet source address check + +Revision 1.40 2004/09/27 23:56:27 cheshire +Fix infinite loop where mDNSPlatformUnlock() called mDNS_TimeNow(), +and then mDNS_TimeNow() called mDNSPlatformUnlock() + +Revision 1.39 2004/09/21 21:02:54 cheshire +Set up ifname before calling mDNS_RegisterInterface() + +Revision 1.38 2004/09/17 01:08:50 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.37 2004/09/17 00:19:10 cheshire +For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4 + +Revision 1.36 2004/09/16 21:59:16 cheshire +For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr + +Revision 1.35 2004/09/16 00:24:49 cheshire + Fix unsafe use of mDNSPlatformTimeNow() + +Revision 1.34 2004/09/14 23:42:36 cheshire + Need to seed random number generator from platform-layer data + +Revision 1.33 2004/09/14 23:16:31 cheshire +Fix compile error: mDNS_SetFQDNs has been renamed to mDNS_SetFQDN + +Revision 1.32 2004/09/14 21:03:16 cheshire +Fix spacing + +Revision 1.31 2004/08/14 03:22:42 cheshire + Dynamic DNS UI <-> mDNSResponder glue +Add GetUserSpecifiedDDNSName() routine +Convert ServiceRegDomain to domainname instead of C string +Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs + +Revision 1.30 2004/07/29 19:26:03 ksekar +Plaform-level changes for NATPMP support + Revision 1.29 2004/05/26 20:53:16 cheshire Remove unncecessary "return( -1 );" at the end of mDNSPlatformUTC() @@ -55,10 +96,10 @@ Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again Revision 1.20 2003/11/14 20:59:09 cheshire Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h. -Best solution is just to combine mDNSClientAPI.h and mDNSPlatformFunctions.h into a single file. +Best solution is just to combine mDNSEmbeddedAPI.h and mDNSPlatformFunctions.h into a single file. Revision 1.19 2003/08/18 23:09:20 cheshire - mDNSResponder divide by zero in mDNSPlatformTimeNow() + mDNSResponder divide by zero in mDNSPlatformRawTime() Revision 1.18 2003/08/12 19:56:24 cheshire Update to APSL 2.0 @@ -72,7 +113,7 @@ Update to APSL 2.0 #include // For smSystemScript #include // For ConvertFromPStringToUnicode() -#include "mDNSClientAPI.h" // Defines the interface provided to the client layer above +#include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above #include "mDNSMacOS9.h" // Defines the specific types needed to run mDNS on this platform @@ -165,7 +206,7 @@ mDNSexport void LogMsg(const char *format, ...) } #endif -mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, +mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstPort) { // Note: If we did multi-homing, we'd have to use the InterfaceID parameter to specify from which interface to send this response @@ -223,11 +264,11 @@ mDNSlocal OSStatus readpacket(mDNS *m) senderport.NotAnInteger = sender.fPort; destaddr.type = mDNSAddrType_IPv4; - destaddr.ip.v4 = zeroIPAddr; + destaddr.ip.v4 = zerov4Addr; #if OTCARBONAPPLICATION // IP_RCVDSTADDR is known to fail on OS X Carbon, so we'll just assume the packet was probably multicast - destaddr.ip.v4 = AllDNSLinkGroup; + destaddr.ip.v4 = AllDNSLinkGroupv4; #endif if (recvdata.opt.len) @@ -246,8 +287,7 @@ mDNSlocal OSStatus readpacket(mDNS *m) if (flags & T_MORE) debugf("ERROR: OTRcvUData() buffer too small (T_MORE set)"); else if (recvdata.addr.len < sizeof(InetAddress)) debugf("ERROR: recvdata.addr.len (%d) too short", recvdata.addr.len); - else if (recvdata.udata.len < sizeof(DNSMessageHeader)) debugf("ERROR: recvdata.udata.len (%d) too short", recvdata.udata.len); - else mDNSCoreReceive(m, &packet, recvdata.udata.buf + recvdata.udata.len, &senderaddr, senderport, &destaddr, MulticastDNSPort, interface, 255); + else mDNSCoreReceive(m, &packet, recvdata.udata.buf + recvdata.udata.len, &senderaddr, senderport, &destaddr, MulticastDNSPort, interface); return(err); } @@ -324,11 +364,14 @@ mDNSlocal pascal void mDNSNotifier(void *contextPtr, OTEventCode code, OTResult if (err) { LogMsg("OTInetGetInterfaceInfo failed %d", err); mDNSinitComplete(m, err); return; } // Make our basic standard host resource records (address, PTR, etc.) - m->p->interface.InterfaceID = (mDNSInterfaceID)&m->p->interface; - m->p->interface.ip.type = mDNSAddrType_IPv4; - m->p->interface.ip.ip.v4.NotAnInteger = interfaceinfo.fAddress; - m->p->interface.Advertise = m->AdvertiseLocalAddresses; - m->p->interface.McastTxRx = mDNStrue; + m->p->interface.InterfaceID = (mDNSInterfaceID)&m->p->interface; + m->p->interface.ip .type = mDNSAddrType_IPv4; + m->p->interface.ip .ip.v4.NotAnInteger = interfaceinfo.fAddress; + m->p->interface.mask.type = mDNSAddrType_IPv4; + m->p->interface.mask.ip.v4.NotAnInteger = interfaceinfo.fMask; + m->p->interface.ifname[0] = 0; + m->p->interface.Advertise = m->AdvertiseLocalAddresses; + m->p->interface.McastTxRx = mDNStrue; } case T_OPTMGMTCOMPLETE: @@ -572,7 +615,7 @@ mDNSexport mStatus mDNSPlatformInit(mDNS *const m) ConvertUTF8PstringToRFC1034HostLabel(m->nicelabel.c, &m->hostlabel); if (m->hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->hostlabel, "Macintosh"); - mDNS_GenerateFQDN(m); + mDNS_SetFQDN(m); // When it's finished mDNSOpenEndpoint asynchronously calls mDNSinitComplete() and then mDNS_RegisterInterface() CallmDNSNotifierUPP = NewOTNotifyUPP(CallmDNSNotifier); @@ -655,7 +698,7 @@ mDNSlocal void ScheduleNextTimerCallback(const mDNS *const m) { if (m->mDNSPlatformStatus == mStatus_NoError) { - SInt32 interval = m->NextScheduledEvent - mDNSPlatformTimeNow(); + SInt32 interval = m->NextScheduledEvent - (mDNSPlatformRawTime() + m->timenow_adjust); if (interval < 1) interval = 1; else if (interval > 0x70000000 / 1000) interval = 0x70000000 / mDNSPlatformOneSecond; else interval = (interval * 1000 + mDNSPlatformOneSecond-1)/ mDNSPlatformOneSecond; @@ -680,9 +723,10 @@ mDNSexport void mDNSPlatformMemCopy(const void *src, void *dst, UInt32 mDNSexport mDNSBool mDNSPlatformMemSame(const void *src, const void *dst, UInt32 len) { return(OTMemcmp(dst, src, len)); } mDNSexport void mDNSPlatformMemZero( void *dst, UInt32 len) { OTMemzero(dst, len); } mDNSexport void * mDNSPlatformMemAllocate(mDNSu32 len) { return(OTAllocMem(len)); } -mDNSexport void mDNSPlatformMemFree (void *mem) { OTFreeMem(mem); } -mDNSexport mStatus mDNSPlatformTimeInit(mDNSs32 *timenow) { *timenow = mDNSPlatformTimeNow(); return(mStatus_NoError); } -mDNSexport SInt32 mDNSPlatformTimeNow() { return((SInt32)TickCount()); } +mDNSexport void mDNSPlatformMemFree(void *mem) { OTFreeMem(mem); } +mDNSexport mDNSu32 mDNSPlatformRandomSeed(void) { return(TickCount()); } +mDNSexport mStatus mDNSPlatformTimeInit(void) { return(mStatus_NoError); } +mDNSexport SInt32 mDNSPlatformRawTime() { return((SInt32)TickCount()); } mDNSexport SInt32 mDNSPlatformOneSecond = 60; mDNSexport mDNSs32 mDNSPlatformUTC(void) diff --git a/mDNSMacOS9/mDNSMacOS9.h b/mDNSMacOS9/mDNSMacOS9.h index 03c7bd5..c4fa310 100755 --- a/mDNSMacOS9/mDNSMacOS9.h +++ b/mDNSMacOS9/mDNSMacOS9.h @@ -3,8 +3,6 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in diff --git a/mDNSMacOS9/mDNSPrefix.h b/mDNSMacOS9/mDNSPrefix.h index 3d1b0aa..80adab4 100644 --- a/mDNSMacOS9/mDNSPrefix.h +++ b/mDNSMacOS9/mDNSPrefix.h @@ -3,8 +3,6 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -25,6 +23,9 @@ Change History (most recent first): $Log: mDNSPrefix.h,v $ +Revision 1.3 2004/06/11 00:03:28 cheshire +Add code for testing avail/busy subtypes + Revision 1.2 2004/05/21 01:57:08 cheshire Add macros for malloc() and free() so that dnssd_clientlib.c can use them @@ -46,26 +47,32 @@ like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code // the standalone application samples that have their own event loop -- don't try // to build the System Extension with MDNS_ONLYSYSTEMTASK set because it won't work.) -#if __ide_target("Standalone TestResponder") || __ide_target("Standalone TestSearcher") +#if __ide_target("Standalone TestResponder") || __ide_target("Standalone TestSearcher") || __ide_target("Standalone SubTypeTester") #define TARGET_API_MAC_CARBON 1 #define OTCARBONAPPLICATION 1 #define MDNS_ONLYSYSTEMTASK 0 #define MDNS_DEBUGMSGS 0 + #elif __ide_target("Standalone TestResponder (Debug)") || __ide_target("Standalone TestSearcher (Debug)") #define TARGET_API_MAC_CARBON 1 #define OTCARBONAPPLICATION 1 #define MDNS_ONLYSYSTEMTASK 1 #define MDNS_DEBUGMSGS 1 + #elif __ide_target("Standalone TestResponder (Classic)") || __ide_target("Standalone TestSearcher (Classic)") #define MDNS_ONLYSYSTEMTASK 0 #define MDNS_DEBUGMSGS 0 + #elif __ide_target("CFM Library for Extensions Folder") #define MDNS_BUILDINGSHAREDLIBRARY 2 + #elif __ide_target("CFM Library for Extensions (Debug)") #define MDNS_DEBUGMSGS 0 #define MDNS_BUILDINGSHAREDLIBRARY 1 + #elif __ide_target("CFM Stub for clients to link against") #define MDNS_BUILDINGSTUBLIBRARY 1 + #else #error Options for this target not found in prefix file #endif diff --git a/mDNSMacOSX/CFSocket.c b/mDNSMacOSX/CFSocket.c deleted file mode 100644 index 388cdf2..0000000 --- a/mDNSMacOSX/CFSocket.c +++ /dev/null @@ -1,2352 +0,0 @@ -/* - * Copright (c) 2002-2003 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - - Change History (most recent first): - -$Log: CFSocket.c,v $ -Revision 1.157 2004/06/08 18:54:48 ksekar -: mDNSResponder leaks after exploring in Printer Setup Utility - -Revision 1.156 2004/06/05 00:04:26 cheshire -: wide-area domains should be returned in reg. domain enumeration - -Revision 1.155 2004/06/04 08:58:30 ksekar -: Keychain integration for secure dynamic update - -Revision 1.154 2004/05/31 22:22:28 ksekar -: wide-area domains should be returned in -reg. domain enumeration - -Revision 1.153 2004/05/26 17:06:33 cheshire -: Don't rely on CFSocketInvalidate() to remove RunLoopSource - -Revision 1.152 2004/05/18 23:51:26 cheshire -Tidy up all checkin comments to use consistent "" format for bug numbers - -Revision 1.151 2004/05/17 21:46:34 cheshire -: When interface is turned off, browse "remove" events are delivered with interface index zero -Take care to correctly update InterfaceIDs when a dormant interface comes back to life - -Revision 1.150 2004/05/13 04:54:20 ksekar -Unified list copy/free code. Added symetric list for - -Revision 1.149 2004/05/13 03:55:14 ksekar -Fixed list traversal bug in FoundDefSearchDomain. - -Revision 1.148 2004/05/12 22:03:08 ksekar -Made GetSearchDomainList a true platform-layer call (declaration moved -from mDNSMacOSX.h to mDNSClientAPI.h), impelemted to return "local" -only on non-OSX platforms. Changed call to return a copy of the list -to avoid shared memory issues. Added a routine to free the list. - -Revision 1.147 2004/05/12 02:03:25 ksekar -Non-local domains will only be browsed by default, and show up in -_browse domain enumeration, if they contain an _browse._dns-sd ptr record. - -Revision 1.146 2004/04/27 02:49:15 cheshire -: mDNSResponder leaks sockets on bind() error - -Revision 1.145 2004/04/21 03:08:03 cheshire -Rename 'alias' to more descriptive name 'primary' - -Revision 1.144 2004/04/21 03:04:35 cheshire -Minor cleanup for clarity - -Revision 1.143 2004/04/21 03:03:30 cheshire -Preparation work: AddInterfaceToList() should return pointer to structure it creates - -Revision 1.142 2004/04/21 02:49:11 cheshire -To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx' - -Revision 1.141 2004/04/21 02:20:47 cheshire -Rename interface field 'CurrentlyActive' to more descriptive 'Exists' - -Revision 1.140 2004/04/14 23:09:29 ksekar -Support for TSIG signed dynamic updates. - -Revision 1.139 2004/04/09 17:40:26 cheshire -Remove unnecessary "Multicast" field -- it duplicates the semantics of the existing McastTxRx field - -Revision 1.138 2004/04/09 16:37:16 cheshire -Suggestion from Bob Bradley: -Move NumCacheRecordsForInterfaceID() to DNSCommon.c so it's available to all platform layers - -Revision 1.137 2004/04/08 00:59:55 cheshire - When interface turned off, browse "remove" events delivered with interface index zero -Unify use of the InterfaceID field, and make code that walks the list respect the 'Exists' flag - -Revision 1.136 2004/04/07 01:08:57 cheshire - When interface turned off, browse "remove" events delivered with interface index zero - -Revision 1.135 2004/03/19 01:01:03 ksekar -Fixed config file parsing to chop newline - -Revision 1.134 2004/03/13 01:57:34 ksekar -: DynDNS: Dynamic update of service records - -Revision 1.133 2004/02/02 22:46:56 cheshire -Move "CFRelease(dict);" inside the "if (dict)" check - -Revision 1.132 2004/01/28 02:30:08 ksekar -Added default Search Domains to unicast browsing, controlled via -Networking sharing prefs pane. Stopped sending unicast messages on -every interface. Fixed unicast resolving via mach-port API. - -Revision 1.131 2004/01/27 22:57:48 cheshire -: Need separate socket for issuing unicast queries - -Revision 1.130 2004/01/27 22:28:40 cheshire -: Time to prune obsolete code for listening on port 53 -Additional lingering port 53 code deleted - -Revision 1.129 2004/01/27 20:15:23 cheshire -: Time to prune obsolete code for listening on port 53 - -Revision 1.128 2004/01/24 23:58:17 cheshire -Change to use mDNSVal16() instead of shifting and ORing - -Revision 1.127 2004/01/24 04:59:16 cheshire -Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again - -Revision 1.126 2004/01/23 23:23:15 ksekar -Added TCP support for truncated unicast messages. - -Revision 1.125 2004/01/22 03:43:09 cheshire -Export constants like mDNSInterface_LocalOnly so that the client layers can use them - -Revision 1.124 2004/01/21 21:53:19 cheshire -: Don't try to receive unicast responses if we're not the first to bind to the UDP port - -Revision 1.123 2004/01/20 03:18:25 cheshire -Removed "LogMsg("Hey There!");" that evidently got checked in my mistake - -Revision 1.122 2003/12/17 20:43:59 cheshire -: Syslog messages saying "sendto failed" - -Revision 1.121 2003/12/13 03:05:28 ksekar -: DynDNS: Unicast query of service records - -Revision 1.120 2003/12/08 21:00:46 rpantos -Changes to support mDNSResponder on Linux. - -Revision 1.119 2003/12/03 02:35:15 cheshire -Also report value of m->timenow when logging sendto() failure - -Revision 1.118 2003/11/14 20:59:09 cheshire -Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h. -Best solution is just to combine mDNSClientAPI.h and mDNSPlatformFunctions.h into a single file. - -Revision 1.117 2003/11/08 22:18:29 cheshire -: Don't need to show process ID in *every* mDNSResponder syslog message - -Revision 1.116 2003/09/23 16:39:49 cheshire -When LogAllOperations is set, also report registration and deregistration of interfaces - -Revision 1.115 2003/09/10 00:45:55 cheshire - Don't log "sendto failed" errors during the first two minutes of startup - -Revision 1.114 2003/08/27 02:55:13 cheshire -: Bug: Don't report mDNSPlatformSendUDP sendto errno 64 (Host is down) - -Revision 1.113 2003/08/19 22:20:00 cheshire - Don't use IPv6 on interfaces that have a routable IPv4 address configured -More minor refinements - -Revision 1.112 2003/08/19 03:04:43 cheshire - Don't use IPv6 on interfaces that have a routable IPv4 address configured - -Revision 1.111 2003/08/18 22:53:37 cheshire - mDNSResponder divide by zero in mDNSPlatformTimeNow() - -Revision 1.110 2003/08/16 03:39:00 cheshire - InterfaceID -1 indicates "local only" - -Revision 1.109 2003/08/15 02:19:49 cheshire - syslog messages: myCFSocketCallBack recvfrom skt 6 error -1 errno 35 -Also limit number of messages to at most 100 - -Revision 1.108 2003/08/12 22:24:52 cheshire - syslog messages: myCFSocketCallBack recvfrom skt 6 error -1 errno 35 -This message indicates a kernel bug, but still we don't want to flood syslog. -Do a sleep(1) after writing this log message, to limit the rate. - -Revision 1.107 2003/08/12 19:56:25 cheshire -Update to APSL 2.0 - -Revision 1.106 2003/08/12 13:48:32 cheshire -Add comment explaining clockdivisor calculation - -Revision 1.105 2003/08/12 13:44:14 cheshire - mDNSResponder *VERY* unhappy if time goes backwards -Use mach_absolute_time() (which is guaranteed to always go forwards, resetting only on reboot) -instead of gettimeofday() (which can jump back if the user manually changes their time/date) - -Revision 1.104 2003/08/12 13:12:07 cheshire -Textual search/replace: Indicate local functions using "mDNSlocal" instead of "static" - -Revision 1.103 2003/08/08 18:36:04 cheshire - Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug - -Revision 1.102 2003/08/06 00:14:52 cheshire - Need to check IP TTL on responses -Also add corresponding checks in the IPv6 code path - -Revision 1.101 2003/08/05 22:20:16 cheshire - Need to check IP TTL on responses - -Revision 1.100 2003/08/05 21:18:50 cheshire - mDNSResponder should ignore 6to4 -Only use interfaces that are marked as multicast-capable (IFF_MULTICAST) - -Revision 1.99 2003/08/05 20:13:52 cheshire - mDNSResponder using IPv6 interfaces before they are ready -Ignore interfaces with the IN6_IFF_NOTREADY flag set - -Revision 1.98 2003/07/20 03:38:51 ksekar - -Completed support for Unix-domain socket based API. - -Revision 1.97 2003/07/19 03:15:16 cheshire -Add generic MemAllocate/MemFree prototypes to mDNSPlatformFunctions.h, -and add the obvious trivial implementations to each platform support layer - -Revision 1.96 2003/07/18 00:30:00 cheshire - Remove mDNSResponder version from packet header and use HINFO record instead - -Revision 1.95 2003/07/12 03:15:20 cheshire - After SCDynamicStore notification, mDNSResponder updates -m->hostlabel even if user hasn't actually actually changed their dot-local hostname - -Revision 1.94 2003/07/03 00:51:54 cheshire - When select() and recvmgs() disagree, get more info from kernel about the socket state - -Revision 1.93 2003/07/03 00:09:14 cheshire - New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call -Additional refinement suggested by Josh: Use info->scope_id instead of if_nametoindex(info->ifa_name); - -Revision 1.92 2003/07/02 21:19:51 cheshire - Update copyright notices, etc., in source code comments - -Revision 1.91 2003/06/24 01:53:51 cheshire -Minor update to comments - -Revision 1.90 2003/06/24 01:51:47 cheshire - Oops: Double-dispose of sockets -Don't need to close sockets: CFSocketInvalidate() does that for us - -Revision 1.89 2003/06/21 18:12:47 cheshire - Rendezvous cannot handle interfaces whose total name is >3 chars -One-line change: should say "IF_NAMESIZE", not sizeof(ifname) - -Revision 1.88 2003/06/12 23:38:37 cheshire - mDNSResponder doesn't detect some configuration changes -Also check that scope_id matches before concluding that two interfaces are the same - -Revision 1.87 2003/06/10 01:14:11 cheshire - New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call - -Revision 1.86 2003/05/28 02:41:52 cheshire - Time to remove Mac OS 9 UDP Port 53 legacy support - -Revision 1.85 2003/05/28 02:39:47 cheshire -Minor change to debugging messages - -Revision 1.84 2003/05/27 22:29:40 cheshire -Remove out-dated comment - -Revision 1.83 2003/05/26 03:21:29 cheshire -Tidy up address structure naming: -mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) -mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 -mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 - -Revision 1.82 2003/05/26 03:01:27 cheshire - sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead - -Revision 1.81 2003/05/24 02:06:42 cheshire - IPv6 Multicast Loopback doesn't work -Tried setting IPV6_MULTICAST_LOOP; it doesn't help. -However, it is probably wise to have the code explicitly set this socket -option anyway, in case the default changes in later versions of Unix. - -Revision 1.80 2003/05/24 02:02:24 cheshire - if_indextoname consumes a lot of CPU -Fix error in myIfIndexToName; was returning prematurely - -Revision 1.79 2003/05/23 23:07:44 cheshire - Must not write to stderr when running as daemon - -Revision 1.78 2003/05/23 01:19:04 cheshire - mDNSResponder needs to signal type of service to AirPort -Mark packets as high-throughput/low-delay (i.e. lowest reliability) to get maximum 802.11 multicast rate - -Revision 1.77 2003/05/23 01:12:05 cheshire -Minor code tidying - -Revision 1.76 2003/05/22 01:26:01 cheshire -Tidy up log messages - -Revision 1.75 2003/05/22 00:07:09 cheshire - myCFSocketCallBack recvfrom(5) error 1, errno 35 -Extra logging to determine whether there is a bug in CFSocket - -Revision 1.74 2003/05/21 20:20:12 cheshire -Fix warnings (mainly printf format string warnings, like using "%d" where -it should say "%lu", etc.) and improve error logging (use strerror() -to include textual error message as well as numeric error in log messages). - -Revision 1.73 2003/05/21 17:56:29 ksekar -: mDNSResponder doesn't watch for IPv6 address changes - -Revision 1.72 2003/05/14 18:48:41 cheshire - mDNSResponder should be smarter about reconfigurations -More minor refinements: -CFSocket.c needs to do *all* its mDNS_DeregisterInterface calls before freeing memory -mDNS_DeregisterInterface revalidates cache record when *any* representative of an interface goes away - -Revision 1.71 2003/05/14 07:08:37 cheshire - mDNSResponder should be smarter about reconfigurations -Previously, when there was any network configuration change, mDNSResponder -would tear down the entire list of active interfaces and start again. -That was very disruptive, and caused the entire cache to be flushed, -and caused lots of extra network traffic. Now it only removes interfaces -that have really gone, and only adds new ones that weren't there before. - -Revision 1.70 2003/05/07 18:30:24 cheshire -Fix signed/unsigned comparison warning - -Revision 1.69 2003/05/06 20:14:44 cheshire -Change "tp" to "tv" - -Revision 1.68 2003/05/06 00:00:49 cheshire - Rationalize naming of domainname manipulation functions - -Revision 1.67 2003/04/29 00:43:44 cheshire -Fix compiler warnings - -Revision 1.66 2003/04/26 02:41:58 cheshire - Change timenow from a local variable to a structure member - -Revision 1.65 2003/04/26 02:34:01 cheshire -Add missing mDNSexport - -Revision 1.64 2003/04/15 16:48:06 jgraessl - -Modified code in CFSocket notifier function to read all packets on the socket -instead of reading only one packet every time the notifier was called. - -Revision 1.63 2003/04/15 16:33:50 jgraessl - -Switched to our own copy of if_indextoname to improve performance. - -Revision 1.62 2003/03/28 01:55:44 cheshire -Minor improvements to debugging messages - -Revision 1.61 2003/03/27 03:30:56 cheshire - Name conflicts not handled properly, resulting in memory corruption, and eventual crash -Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback -Fixes: -1. Make mDNS_DeregisterInterface() safe to call from a callback -2. Make HostNameCallback() use mDNS_DeadvertiseInterface() instead - (it never really needed to deregister the interface at all) - -Revision 1.60 2003/03/15 04:40:38 cheshire -Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID" - -Revision 1.59 2003/03/11 01:23:26 cheshire - mDNSResponder socket problems - -Revision 1.58 2003/03/06 01:43:04 cheshire - Additional debugging code in mDNSResponder -Improve "LIST_ALL_INTERFACES" output - -Revision 1.57 2003/03/05 22:36:27 cheshire - Loopback doesn't work with mDNSResponder-27 -Temporary workaround: Skip loopback interface *only* if we found at least one v4 interface to use - -Revision 1.56 2003/03/05 01:50:38 cheshire - Additional debugging code in mDNSResponder - -Revision 1.55 2003/02/21 01:54:09 cheshire - mDNSResponder needs performance improvements -Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt") - -Revision 1.54 2003/02/20 06:48:35 cheshire - Xserve RAID needs to do interface-specific registrations -Reviewed by: Josh Graessley, Bob Bradley - -Revision 1.53 2003/01/29 02:21:23 cheshire -Return mStatus_Invalid if can't send packet because socket not available - -Revision 1.52 2003/01/28 19:39:43 jgraessl -Enabling AAAA over IPv4 support. - -Revision 1.51 2003/01/28 05:11:23 cheshire -Fixed backwards comparison in SearchForInterfaceByName - -Revision 1.50 2003/01/13 23:49:44 jgraessl -Merged changes for the following fixes in to top of tree: - computer name changes not handled properly - service name changes are not properly handled - announcements sent in pairs, failing chattiness test - -Revision 1.49 2002/12/23 22:13:30 jgraessl -Reviewed by: Stuart Cheshire -Initial IPv6 support for mDNSResponder. - -Revision 1.48 2002/11/22 01:37:52 cheshire - mDNSResponder is monitoring ServiceEntities instead of InterfaceEntities - -Revision 1.47 2002/09/21 20:44:51 zarzycki -Added APSL info - -Revision 1.46 2002/09/19 21:25:35 cheshire -mDNS_snprintf() doesn't need to be in a separate file - -Revision 1.45 2002/09/17 01:45:13 cheshire -Add LIST_ALL_INTERFACES symbol for debugging - -Revision 1.44 2002/09/17 01:36:23 cheshire -Move Puma support to CFSocketPuma.c - -Revision 1.43 2002/09/17 01:05:28 cheshire -Change mDNS_AdvertiseLocalAddresses to be an Init parameter instead of a global - -Revision 1.42 2002/09/16 23:13:50 cheshire -Minor code tidying - - */ - -// *************************************************************************** -// mDNS-CFSocket.c: -// Supporting routines to run mDNS on a CFRunLoop platform -// *************************************************************************** - -// For debugging, set LIST_ALL_INTERFACES to 1 to display all found interfaces, -// including ones that mDNSResponder chooses not to use. -#define LIST_ALL_INTERFACES 0 - -// For enabling AAAA records over IPv4. Setting this to 0 sends only -// A records over IPv4 and AAAA over IPv6. Setting this to 1 sends both -// AAAA and A records over both IPv4 and IPv6. -#define AAAA_OVER_V4 1 - -#include "mDNSClientAPI.h" // Defines the interface provided to the client layer above -#include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform - -#include -#include // For select() and close() -#include // For va_list support -#include -#include -#include -#include -#include -#include -#include -#include -#include // platform support for UTC time -#include // for inet_aton - -#include // For IP_RECVTTL -#ifndef IP_RECVTTL -#define IP_RECVTTL 24 // bool; receive reception TTL w/dgram -#endif - -#include // For n_long, required by below -#include // For IPTOS_LOWDELAY etc. -#include // For IN6_IFF_NOTREADY etc. - -#include - -// Code contributed by Dave Heller: -// Define RUN_ON_PUMA_WITHOUT_IFADDRS to compile code that will -// work on Mac OS X 10.1, which does not have the getifaddrs call. -#define RUN_ON_PUMA_WITHOUT_IFADDRS 0 -#if RUN_ON_PUMA_WITHOUT_IFADDRS -#include "CFSocketPuma.c" -#else -#include -#endif - -#include -#include -#include - -typedef struct AuthRecordListElem - { - struct AuthRecordListElem *next; - AuthRecord ar; - } AuthRecordListElem; - -typedef struct SearchListElem - { - struct SearchListElem *next; - domainname domain; - int flag; - DNSQuestion browseQ; - DNSQuestion registerQ; - AuthRecordListElem *AuthRecs; - } SearchListElem; - - -// *************************************************************************** -// Globals - -static mDNSu32 clockdivisor = 0; -static mDNSBool DNSConfigInitialized = mDNSfalse; -#define MAX_SEARCH_DOMAINS 32 - -// for domain enumeration and default browsing -static SearchListElem *SearchList = NULL; // where we search for _browse domains -static DNSQuestion DefBrowseDomainQ; // our local enumeration query for _browse domains -static DNameListElem *DefBrowseList = NULL; // cache of answers to above query (where we search for empty string browses) - -#define CONFIG_FILE "/etc/mDNSResponder.conf" -#define LH_KEYCHAIN_DESC "Lighthouse Shared Secret" -#define LH_KEYCHAIN_SERVICE "Lighthouse" -#define SYS_KEYCHAIN_PATH "/Library/Keychains/System.keychain" -#define LH_SUFFIX "members.mac.com." - -// *************************************************************************** -// Macros - -#define mDNSSameIPv4Address(A,B) ((A).NotAnInteger == (B).NotAnInteger) -#define mDNSSameIPv6Address(A,B) ((A).l[0] == (B).l[0] && (A).l[1] == (B).l[1] && (A).l[2] == (B).l[2] && (A).l[3] == (B).l[3]) - -#define mDNSAddressIsAllDNSLinkGroup(X) ( \ - ((X)->type == mDNSAddrType_IPv4 && mDNSSameIPv4Address((X)->ip.v4, AllDNSLinkGroup )) || \ - ((X)->type == mDNSAddrType_IPv6 && mDNSSameIPv6Address((X)->ip.v6, AllDNSLinkGroupv6)) ) - -// *************************************************************************** -// Functions - -mDNSlocal struct ifaddrs* myGetIfAddrs(int refresh) - { - static struct ifaddrs *ifa = NULL; - - if (refresh && ifa) - { - freeifaddrs(ifa); - ifa = NULL; - } - - if (ifa == NULL) getifaddrs(&ifa); - - return ifa; - } - -mDNSlocal int myIfIndexToName(u_short index, char* name) - { - struct ifaddrs *ifa; - for (ifa = myGetIfAddrs(0); ifa; ifa = ifa->ifa_next) - if (ifa->ifa_addr->sa_family == AF_LINK) - if (((struct sockaddr_dl*)ifa->ifa_addr)->sdl_index == index) - { strncpy(name, ifa->ifa_name, IF_NAMESIZE); return 0; } - return -1; - } - -mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(const mDNS *const m, mDNSu32 index) - { - NetworkInterfaceInfoOSX *i; - if (index == (uint32_t)~0) return(mDNSInterface_LocalOnly); - if (index) - for (i = m->p->InterfaceList; i; i = i->next) - // Don't get tricked by inactive interfaces with no InterfaceID set - if (i->ifinfo.InterfaceID && i->scope_id == index) return(i->ifinfo.InterfaceID); - return(mDNSNULL); - } - -mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(const mDNS *const m, mDNSInterfaceID id) - { - NetworkInterfaceInfoOSX *i; - if (id == mDNSInterface_LocalOnly) return((mDNSu32)~0); - if (id) - for (i = m->p->InterfaceList; i; i = i->next) - // Don't use i->ifinfo.InterfaceID here, because we DO want to find inactive interfaces, which have no InterfaceID set - if ((mDNSInterfaceID)i == id) return(i->scope_id); - return 0; - } - -// NOTE: If InterfaceID is NULL, it means, "send this packet through our anonymous unicast socket" -// NOTE: If InterfaceID is non-NULL it means, "send this packet through our port 5353 socket on the specified interface" -mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, - mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstPort) - { - #pragma unused(m) - - // Note: For this platform we've adopted the convention that InterfaceIDs are secretly pointers - // to the NetworkInterfaceInfoOSX structure that holds the active sockets. The mDNSCore code - // doesn't know that and doesn't need to know that -- it just treats InterfaceIDs as opaque identifiers. - NetworkInterfaceInfoOSX *info = (NetworkInterfaceInfoOSX *)InterfaceID; - char *ifa_name = info ? info->ifa_name : "unicast"; - struct sockaddr_storage to; - int s = -1, err; - - if (dst->type == mDNSAddrType_IPv4) - { - struct sockaddr_in *sin_to = (struct sockaddr_in*)&to; - sin_to->sin_len = sizeof(*sin_to); - sin_to->sin_family = AF_INET; - sin_to->sin_port = dstPort.NotAnInteger; - sin_to->sin_addr.s_addr = dst->ip.v4.NotAnInteger; - s = info ? info->ss.sktv4 : m->p->unicastsockets.sktv4; - } - else if (dst->type == mDNSAddrType_IPv6) - { - struct sockaddr_in6 *sin6_to = (struct sockaddr_in6*)&to; - sin6_to->sin6_len = sizeof(*sin6_to); - sin6_to->sin6_family = AF_INET6; - sin6_to->sin6_port = dstPort.NotAnInteger; - sin6_to->sin6_flowinfo = 0; - sin6_to->sin6_addr = *(struct in6_addr*)&dst->ip.v6; - sin6_to->sin6_scope_id = info ? info->scope_id : 0; - s = info ? info->ss.sktv6 : m->p->unicastsockets.sktv6; - } - else - { - LogMsg("mDNSPlatformSendUDP: dst is not an IPv4 or IPv6 address!\n"); - return mStatus_BadParamErr; - } - - if (s >= 0) - verbosedebugf("mDNSPlatformSendUDP: sending on InterfaceID %X %s/%d to %#a:%d skt %d", - InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s); - else - verbosedebugf("mDNSPlatformSendUDP: NOT sending on InterfaceID %X %s/%d (socket of this type not available)", - InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort)); - - // Note: When sending, mDNSCore may often ask us to send both a v4 multicast packet and then a v6 multicast packet - // If we don't have the corresponding type of socket available, then return mStatus_Invalid - if (s < 0) return(mStatus_Invalid); - - err = sendto(s, msg, (UInt8*)end - (UInt8*)msg, 0, (struct sockaddr *)&to, to.ss_len); - if (err < 0) - { - // Don't report EHOSTDOWN (i.e. ARP failure) to unicast destinations - if (errno == EHOSTDOWN && !mDNSAddressIsAllDNSLinkGroup(dst)) return(err); - // Don't report EHOSTUNREACH in the first three minutes after boot - // This is because mDNSResponder intentionally starts up early in the boot process (See ) - // but this means that sometimes it starts before configd has finished setting up the multicast routing entries. - if (errno == EHOSTUNREACH && (mDNSu32)(m->timenow) < (mDNSu32)(mDNSPlatformOneSecond * 180)) return(err); - LogMsg("mDNSPlatformSendUDP sendto failed to send packet on InterfaceID %p %s/%ld to %#a:%d skt %d error %d errno %d (%s) %lu", - InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow)); - return(err); - } - - return(mStatus_NoError); - } - -mDNSlocal ssize_t myrecvfrom(const int s, void *const buffer, const size_t max, - struct sockaddr *const from, size_t *const fromlen, mDNSAddr *dstaddr, char ifname[IF_NAMESIZE], mDNSu8 *ttl) - { - static unsigned int numLogMessages = 0; - struct iovec databuffers = { (char *)buffer, max }; - struct msghdr msg; - ssize_t n; - struct cmsghdr *cmPtr; - char ancillary[1024]; - - *ttl = 255; // If kernel fails to provide TTL data (e.g. Jaguar doesn't) then assume the TTL was 255 as it should be - - // Set up the message - msg.msg_name = (caddr_t)from; - msg.msg_namelen = *fromlen; - msg.msg_iov = &databuffers; - msg.msg_iovlen = 1; - msg.msg_control = (caddr_t)&ancillary; - msg.msg_controllen = sizeof(ancillary); - msg.msg_flags = 0; - - // Receive the data - n = recvmsg(s, &msg, 0); - if (n<0) - { - if (errno != EWOULDBLOCK && numLogMessages++ < 100) LogMsg("CFSocket.c: recvmsg(%d) returned error %d errno %d", s, n, errno); - return(-1); - } - if (msg.msg_controllen < (int)sizeof(struct cmsghdr)) - { - if (numLogMessages++ < 100) LogMsg("CFSocket.c: recvmsg(%d) msg.msg_controllen %d < sizeof(struct cmsghdr) %lu", - s, msg.msg_controllen, sizeof(struct cmsghdr)); - return(-1); - } - if (msg.msg_flags & MSG_CTRUNC) - { - if (numLogMessages++ < 100) LogMsg("CFSocket.c: recvmsg(%d) msg.msg_flags & MSG_CTRUNC", s); - return(-1); - } - - *fromlen = msg.msg_namelen; - - // Parse each option out of the ancillary data. - for (cmPtr = CMSG_FIRSTHDR(&msg); cmPtr; cmPtr = CMSG_NXTHDR(&msg, cmPtr)) - { - // debugf("myrecvfrom cmsg_level %d cmsg_type %d", cmPtr->cmsg_level, cmPtr->cmsg_type); - if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVDSTADDR) - { - dstaddr->type = mDNSAddrType_IPv4; - dstaddr->ip.v4.NotAnInteger = *(u_int32_t*)CMSG_DATA(cmPtr); - } - if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVIF) - { - struct sockaddr_dl *sdl = (struct sockaddr_dl *)CMSG_DATA(cmPtr); - if (sdl->sdl_nlen < IF_NAMESIZE) - { - mDNSPlatformMemCopy(sdl->sdl_data, ifname, sdl->sdl_nlen); - ifname[sdl->sdl_nlen] = 0; - // debugf("IP_RECVIF sdl_index %d, sdl_data %s len %d", sdl->sdl_index, ifname, sdl->sdl_nlen); - } - } - if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVTTL) - { - *ttl = *(u_char*)CMSG_DATA(cmPtr); - } - if (cmPtr->cmsg_level == IPPROTO_IPV6 && cmPtr->cmsg_type == IPV6_PKTINFO) - { - struct in6_pktinfo *ip6_info = (struct in6_pktinfo*)CMSG_DATA(cmPtr); - dstaddr->type = mDNSAddrType_IPv6; - dstaddr->ip.v6 = *(mDNSv6Addr*)&ip6_info->ipi6_addr; - myIfIndexToName(ip6_info->ipi6_ifindex, ifname); - } - if (cmPtr->cmsg_level == IPPROTO_IPV6 && cmPtr->cmsg_type == IPV6_HOPLIMIT) - { - *ttl = *(int*)CMSG_DATA(cmPtr); - } - } - - return(n); - } - -// On entry, context points to our CFSocketSet -// If ss->info is NULL, we received this packet on our anonymous unicast socket -// If ss->info is non-NULL, we received this packet on port 5353 on the indicated interface -mDNSlocal void myCFSocketCallBack(CFSocketRef cfs, CFSocketCallBackType CallBackType, CFDataRef address, const void *data, void *context) - { - mDNSAddr senderAddr, destAddr; - mDNSIPPort senderPort, destPort = MulticastDNSPort; - const CFSocketSet *ss = (const CFSocketSet *)context; - mDNS *const m = ss->m; - const mDNSInterfaceID InterfaceID = ss->info ? ss->info->ifinfo.InterfaceID : mDNSNULL; - DNSMessage packet; - struct sockaddr_storage from; - size_t fromlen = sizeof(from); - char packetifname[IF_NAMESIZE] = ""; - int err, s1 = -1, skt = CFSocketGetNative(cfs); - int count = 0; - - (void)address; // Parameter not used - (void)data; // Parameter not used - - if (CallBackType != kCFSocketReadCallBack) - LogMsg("myCFSocketCallBack: Why is CallBackType %d not kCFSocketReadCallBack?", CallBackType); - - if (cfs == ss->cfsv4) s1 = ss->sktv4; - else if (cfs == ss->cfsv6) s1 = ss->sktv6; - - if (s1 < 0 || s1 != skt) - { - LogMsg("myCFSocketCallBack: s1 %d native socket %d, cfs %p", s1, skt, cfs); - LogMsg("myCFSocketCallBack: cfsv4 %p, sktv4 %d", ss->cfsv4, ss->sktv4); - LogMsg("myCFSocketCallBack: cfsv6 %p, sktv6 %d", ss->cfsv6, ss->sktv6); - } - - mDNSu8 ttl; - while ((err = myrecvfrom(s1, &packet, sizeof(packet), (struct sockaddr *)&from, &fromlen, &destAddr, packetifname, &ttl)) >= 0) - { - count++; - if (from.ss_family == AF_INET) - { - struct sockaddr_in *sin = (struct sockaddr_in*)&from; - senderAddr.type = mDNSAddrType_IPv4; - senderAddr.ip.v4.NotAnInteger = sin->sin_addr.s_addr; - senderPort.NotAnInteger = sin->sin_port; - } - else if (from.ss_family == AF_INET6) - { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&from; - senderAddr.type = mDNSAddrType_IPv6; - senderAddr.ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr; - senderPort.NotAnInteger = sin6->sin6_port; - } - else - { - LogMsg("myCFSocketCallBack from is unknown address family %d", from.ss_family); - return; - } - - // Even though we indicated a specific interface in the IP_ADD_MEMBERSHIP call, a weirdness of the - // sockets API means that even though this socket has only officially joined the multicast group - // on one specific interface, the kernel will still deliver multicast packets to it no matter which - // interface they arrive on. According to the official Unix Powers That Be, this is Not A Bug. - // To work around this weirdness, we use the IP_RECVIF option to find the name of the interface - // on which the packet arrived, and ignore the packet if it really arrived on some other interface. - if (!ss->info) - verbosedebugf("myCFSocketCallBack got a packet from %#a to %#a on unicast socket", &senderAddr, &destAddr); - else if (!strcmp(ss->info->ifa_name, packetifname)) - verbosedebugf("myCFSocketCallBack got a packet from %#a to %#a on interface %#a/%s", - &senderAddr, &destAddr, &ss->info->ifinfo.ip, ss->info->ifa_name); - else - { - verbosedebugf("myCFSocketCallBack got a packet from %#a to %#a on interface %#a/%s (Ignored -- really arrived on interface %s)", - &senderAddr, &destAddr, &ss->info->ifinfo.ip, ss->info->ifa_name, packetifname); - return; - } - - if (err < (int)sizeof(DNSMessageHeader)) { debugf("myCFSocketCallBack packet length (%d) too short", err); return; } - - mDNSCoreReceive(m, &packet, (unsigned char*)&packet + err, &senderAddr, senderPort, &destAddr, destPort, InterfaceID, ttl); - } - - if (err < 0 && (errno != EWOULDBLOCK || count == 0)) - { - // Something is busted here. - // CFSocket says there is a packet, but myrecvfrom says there is not. - // Try calling select() to get another opinion. - // Find out about other socket parameter that can help understand why select() says the socket is ready for read - // All of this is racy, as data may have arrived after the call to select() - int save_errno = errno; - int so_error = -1; - int so_nread = -1; - int fionread = -1; - int solen = sizeof(int); - fd_set readfds; - FD_ZERO(&readfds); - FD_SET(s1, &readfds); - struct timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = 0; - int selectresult = select(s1+1, &readfds, NULL, NULL, &timeout); - if (getsockopt(s1, SOL_SOCKET, SO_ERROR, &so_error, &solen) == -1) - LogMsg("myCFSocketCallBack getsockopt(SO_ERROR) error %d", errno); - if (getsockopt(s1, SOL_SOCKET, SO_NREAD, &so_nread, &solen) == -1) - LogMsg("myCFSocketCallBack getsockopt(SO_NREAD) error %d", errno); - if (ioctl(s1, FIONREAD, &fionread) == -1) - LogMsg("myCFSocketCallBack ioctl(FIONREAD) error %d", errno); - static unsigned int numLogMessages = 0; - if (numLogMessages++ < 100) - LogMsg("myCFSocketCallBack recvfrom skt %d error %d errno %d (%s) select %d (%spackets waiting) so_error %d so_nread %d fionread %d count %d", - s1, err, save_errno, strerror(save_errno), selectresult, FD_ISSET(s1, &readfds) ? "" : "*NO* ", so_error, so_nread, fionread, count); - sleep(1); // After logging this error, rate limit so we don't flood syslog - } - } - -// TCP socket support for unicast DNS and Dynamic DNS Update - -typedef struct - { - TCPConnectionCallback callback; - void *context; - int connected; - } tcpInfo_t; - -mDNSlocal void tcpCFSocketCallback(CFSocketRef cfs, CFSocketCallBackType CallbackType, CFDataRef address, - const void *data, void *context) - { - #pragma unused(CallbackType, address, data) - mDNSBool connect = mDNSfalse; - - tcpInfo_t *info = context; - if (!info->connected) - { - connect = mDNStrue; - info->connected = mDNStrue; // prevent connected flag from being set in future callbacks - } - info->callback(CFSocketGetNative(cfs), info->context, connect); - // NOTE: the callback may call CloseConnection here, which frees the context structure! - } - -mDNSexport mStatus mDNSPlatformTCPConnect(const mDNSAddr *dst, mDNSOpaque16 dstport, mDNSInterfaceID InterfaceID, - TCPConnectionCallback callback, void *context, int *descriptor) - { - int sd, on = 1; // "on" for setsockopt - struct sockaddr_in saddr; - CFSocketContext cfContext = { 0, NULL, 0, 0, 0 }; - tcpInfo_t *info; - CFSocketRef sr; - CFRunLoopSourceRef rls; - CFOptionFlags srFlags; - - (void)InterfaceID; //!!!KRS use this if non-zero!!! - - *descriptor = 0; - if (dst->type != mDNSAddrType_IPv4) - { - LogMsg("ERROR: mDNSPlatformTCPConnect - attempt to connect to an IPv6 address: opperation not supported"); - return mStatus_UnknownErr; - } - - sd = socket(AF_INET, SOCK_STREAM, 0); - if (sd < 0) - { - LogMsg("ERROR: socket; %s", strerror(errno)); - return mStatus_UnknownErr; - } - // set non-blocking - if (fcntl(sd, F_SETFL, O_NONBLOCK) < 0) - { - LogMsg("ERROR: setsockopt O_NONBLOCK - %s", strerror(errno)); - return mStatus_UnknownErr; - } - - // receive interface identifiers - if (setsockopt(sd, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)) < 0) - { - LogMsg("setsockopt IP_RECVIF - %s", strerror(errno)); - return mStatus_UnknownErr; - } - // set up CF wrapper, add to Run Loop - info = mallocL("mDNSPlatformTCPConnect", sizeof(tcpInfo_t)); - info->callback = callback; - info->context = context; - cfContext.info = info; - sr = CFSocketCreateWithNative(kCFAllocatorDefault, sd, kCFSocketReadCallBack | kCFSocketConnectCallBack, - tcpCFSocketCallback, &cfContext); - if (!sr) - { - LogMsg("ERROR: mDNSPlatformTCPConnect - CFSocketRefCreateWithNative failed"); - freeL("mDNSPlatformTCPConnect", info); - return mStatus_UnknownErr; - } - - // prevent closing of native socket - srFlags = CFSocketGetSocketFlags(sr); - CFSocketSetSocketFlags(sr, srFlags & (~kCFSocketCloseOnInvalidate)); - - rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, sr, 0); - if (!rls) - { - LogMsg("ERROR: mDNSPlatformTCPConnect - CFSocketCreateRunLoopSource failed"); - freeL("mDNSPlatformTCPConnect", info); - return mStatus_UnknownErr; - } - - CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); - CFRelease(rls); - - // initiate connection wth peer - bzero(&saddr, sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_port = dstport.NotAnInteger; - memcpy(&saddr.sin_addr, &dst->ip.v4.NotAnInteger, sizeof(saddr.sin_addr)); - if (connect(sd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) - { - if (errno == EINPROGRESS) - { - info->connected = 0; - *descriptor= sd; - return mStatus_ConnectionPending; - } - LogMsg("ERROR: mDNSPlatformTCPConnect - connect failed: %s", strerror(errno)); - freeL("mDNSPlatformTCPConnect", info); - CFSocketInvalidate(sr); - return mStatus_ConnectionFailed; - } - info->connected = 1; - *descriptor = sd; - return mStatus_ConnectionEstablished; - } - -mDNSexport void mDNSPlatformTCPCloseConnection(int sd) - { - CFSocketContext cfContext; - tcpInfo_t *info; - CFSocketRef sr; - - // get the CFSocket for the descriptor, if it exists - sr = CFSocketCreateWithNative(kCFAllocatorDefault, sd, NULL, NULL, NULL); - if (!sr) - { - LogMsg("ERROR: mDNSPlatformTCPCloseConnection - attempt to close a socket that was not properly created"); - return; - } - CFSocketGetContext(sr, &cfContext); - if (!cfContext.info) - { - LogMsg("ERROR: mDNSPlatformTCPCloseConnection - could not retreive tcpInfo from socket context"); - CFRelease(sr); - return; - } - CFRelease(sr); // this only releases the copy we allocated with CreateWithNative above - - info = cfContext.info; - CFSocketInvalidate(sr); - CFRelease(sr); - close(sd); - freeL("mDNSPlatformTCPCloseConnection", info); - } - -mDNSexport int mDNSPlatformReadTCP(int sd, void *buf, int buflen) - { - int nread = recv(sd, buf, buflen, 0); - if (nread < 0) - { - if (errno == EAGAIN) return 0; // no data available (call would block) - LogMsg("ERROR: mDNSPlatformReadTCP - recv: %s", strerror(errno)); - return -1; - } - return nread; - } - -mDNSexport int mDNSPlatformWriteTCP(int sd, const char *msg, int len) - { - int nsent = send(sd, msg, len, 0); - - if (nsent < 0) - { - if (errno == EAGAIN) return 0; // blocked - LogMsg("ERROR: mDNSPlatformWriteTCP - sendL %s", strerror(errno)); - return -1; - } - return nsent; - } - -// This gets the text of the field currently labelled "Computer Name" in the Sharing Prefs Control Panel -mDNSlocal void GetUserSpecifiedFriendlyComputerName(domainlabel *const namelabel) - { - CFStringEncoding encoding = kCFStringEncodingUTF8; - CFStringRef cfs = SCDynamicStoreCopyComputerName(NULL, &encoding); - if (cfs) - { - CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); - CFRelease(cfs); - } - } - -// This gets the text of the field currently labelled "Rendezvous Name" in the Sharing Prefs Control Panel -mDNSlocal void GetUserSpecifiedRFC1034ComputerName(domainlabel *const namelabel) - { - CFStringRef cfs = SCDynamicStoreCopyLocalHostName(NULL); - if (cfs) - { - CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); - CFRelease(cfs); - } - } - -// If mDNSIPPort port is non-zero, then it's a multicast socket on the specified interface -// If mDNSIPPort port is zero, then it's a randomly assigned port number, used for sending unicast queries -mDNSlocal mStatus SetupSocket(CFSocketSet *cp, mDNSIPPort port, const mDNSAddr *ifaddr, u_short sa_family) - { - int *s = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6; - CFSocketRef *c = (sa_family == AF_INET) ? &cp->cfsv4 : &cp->cfsv6; - CFRunLoopSourceRef *r = (sa_family == AF_INET) ? &cp->rlsv4 : &cp->rlsv6; - const int on = 1; - const int twofivefive = 255; - mStatus err = mStatus_NoError; - char *errstr = mDNSNULL; - - if (*s >= 0) { LogMsg("SetupSocket ERROR: socket %d is already set", *s); return(-1); } - if (*c) { LogMsg("SetupSocket ERROR: CFSocketRef %p is already set", *c); return(-1); } - - // Open the socket... - int skt = socket(sa_family, SOCK_DGRAM, IPPROTO_UDP); - if (skt < 0) { LogMsg("socket error %d errno %d (%s)", skt, errno, strerror(errno)); return(skt); } - - // ... with a shared UDP port, if it's for multicast receiving - if (port.NotAnInteger) err = setsockopt(skt, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); - if (err < 0) { errstr = "setsockopt - SO_REUSEPORT"; goto fail; } - - if (sa_family == AF_INET) - { - // We want to receive destination addresses - err = setsockopt(skt, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)); - if (err < 0) { errstr = "setsockopt - IP_RECVDSTADDR"; goto fail; } - - // We want to receive interface identifiers - err = setsockopt(skt, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)); - if (err < 0) { errstr = "setsockopt - IP_RECVIF"; goto fail; } - - // We want to receive packet TTL value so we can check it - err = setsockopt(skt, IPPROTO_IP, IP_RECVTTL, &on, sizeof(on)); - // We ignore errors here -- we already know Jaguar doesn't support this, but we can get by without it - - // Add multicast group membership on this interface, if it's for multicast receiving - if (port.NotAnInteger) - { - struct in_addr addr = { ifaddr->ip.v4.NotAnInteger }; - struct ip_mreq imr; - imr.imr_multiaddr.s_addr = AllDNSLinkGroup.NotAnInteger; - imr.imr_interface = addr; - err = setsockopt(skt, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)); - if (err < 0) { errstr = "setsockopt - IP_ADD_MEMBERSHIP"; goto fail; } - - // Specify outgoing interface too - err = setsockopt(skt, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr)); - if (err < 0) { errstr = "setsockopt - IP_MULTICAST_IF"; goto fail; } - } - - // Send unicast packets with TTL 255 - err = setsockopt(skt, IPPROTO_IP, IP_TTL, &twofivefive, sizeof(twofivefive)); - if (err < 0) { errstr = "setsockopt - IP_TTL"; goto fail; } - - // And multicast packets with TTL 255 too - err = setsockopt(skt, IPPROTO_IP, IP_MULTICAST_TTL, &twofivefive, sizeof(twofivefive)); - if (err < 0) { errstr = "setsockopt - IP_MULTICAST_TTL"; goto fail; } - - // Mark packets as high-throughput/low-delay (i.e. lowest reliability) to get maximum 802.11 multicast rate - const int ip_tosbits = IPTOS_LOWDELAY | IPTOS_THROUGHPUT; - err = setsockopt(skt, IPPROTO_IP, IP_TOS, &ip_tosbits, sizeof(ip_tosbits)); - if (err < 0) { errstr = "setsockopt - IP_TOS"; goto fail; } - - // And start listening for packets - struct sockaddr_in listening_sockaddr; - listening_sockaddr.sin_family = AF_INET; - listening_sockaddr.sin_port = port.NotAnInteger; - listening_sockaddr.sin_addr.s_addr = 0; // Want to receive multicasts AND unicasts on this socket - err = bind(skt, (struct sockaddr *) &listening_sockaddr, sizeof(listening_sockaddr)); - if (err) { errstr = "bind"; goto fail; } - } - else if (sa_family == AF_INET6) - { - // We want to receive destination addresses and receive interface identifiers - err = setsockopt(skt, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on)); - if (err < 0) { errstr = "setsockopt - IPV6_PKTINFO"; goto fail; } - - // We want to receive packet hop count value so we can check it - err = setsockopt(skt, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, sizeof(on)); - if (err < 0) { errstr = "setsockopt - IPV6_HOPLIMIT"; goto fail; } - - // We want to receive only IPv6 packets, without this option, we may - // get IPv4 addresses as mapped addresses. - err = setsockopt(skt, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); - if (err < 0) { errstr = "setsockopt - IPV6_V6ONLY"; goto fail; } - - if (port.NotAnInteger) - { - // Add multicast group membership on this interface, if it's for multicast receiving - int interface_id = if_nametoindex(cp->info->ifa_name); - struct ipv6_mreq i6mr; - i6mr.ipv6mr_interface = interface_id; - i6mr.ipv6mr_multiaddr = *(struct in6_addr*)&AllDNSLinkGroupv6; - err = setsockopt(skt, IPPROTO_IPV6, IPV6_JOIN_GROUP, &i6mr, sizeof(i6mr)); - if (err < 0) { errstr = "setsockopt - IPV6_JOIN_GROUP"; goto fail; } - - // Specify outgoing interface too - err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_IF, &interface_id, sizeof(interface_id)); - if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_IF"; goto fail; } - } - - // Send unicast packets with TTL 255 - err = setsockopt(skt, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &twofivefive, sizeof(twofivefive)); - if (err < 0) { errstr = "setsockopt - IPV6_UNICAST_HOPS"; goto fail; } - - // And multicast packets with TTL 255 too - err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &twofivefive, sizeof(twofivefive)); - if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_HOPS"; goto fail; } - - // Note: IPV6_TCLASS appears not to be implemented on OS X right now (or indeed on ANY version of Unix?) - #ifdef IPV6_TCLASS - // Mark packets as high-throughput/low-delay (i.e. lowest reliability) to get maximum 802.11 multicast rate - int tclass = IPTOS_LOWDELAY | IPTOS_THROUGHPUT; // This may not be right (since tclass is not implemented on OS X, I can't test it) - err = setsockopt(skt, IPPROTO_IPV6, IPV6_TCLASS, &tclass, sizeof(tclass)); - if (err < 0) { errstr = "setsockopt - IPV6_TCLASS"; goto fail; } - #endif - - // Want to receive our own packets - err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(on)); - if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_LOOP"; goto fail; } - - // And start listening for packets - struct sockaddr_in6 listening_sockaddr6; - bzero(&listening_sockaddr6, sizeof(listening_sockaddr6)); - listening_sockaddr6.sin6_len = sizeof(listening_sockaddr6); - listening_sockaddr6.sin6_family = AF_INET6; - listening_sockaddr6.sin6_port = port.NotAnInteger; - listening_sockaddr6.sin6_flowinfo = 0; -// listening_sockaddr6.sin6_addr = IN6ADDR_ANY_INIT; // Want to receive multicasts AND unicasts on this socket - listening_sockaddr6.sin6_scope_id = 0; - err = bind(skt, (struct sockaddr *) &listening_sockaddr6, sizeof(listening_sockaddr6)); - if (err) { errstr = "bind"; goto fail; } - } - - fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK); // set non-blocking - *s = skt; - CFSocketContext myCFSocketContext = { 0, cp, NULL, NULL, NULL }; - *c = CFSocketCreateWithNative(kCFAllocatorDefault, *s, kCFSocketReadCallBack, myCFSocketCallBack, &myCFSocketContext); - *r = CFSocketCreateRunLoopSource(kCFAllocatorDefault, *c, 0); - CFRunLoopAddSource(CFRunLoopGetCurrent(), *r, kCFRunLoopDefaultMode); - - return(err); - -fail: - LogMsg("%s error %ld errno %d (%s)", errstr, err, errno, strerror(errno)); - close(skt); - return(err); - } - -mDNSlocal mStatus SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa) - { - if (sa->sa_family == AF_INET) - { - struct sockaddr_in *ifa_addr = (struct sockaddr_in *)sa; - ip->type = mDNSAddrType_IPv4; - ip->ip.v4.NotAnInteger = ifa_addr->sin_addr.s_addr; - return(0); - } - else if (sa->sa_family == AF_INET6) - { - struct sockaddr_in6 *ifa_addr = (struct sockaddr_in6 *)sa; - ip->type = mDNSAddrType_IPv6; - if (IN6_IS_ADDR_LINKLOCAL(&ifa_addr->sin6_addr)) ifa_addr->sin6_addr.__u6_addr.__u6_addr16[1] = 0; - ip->ip.v6 = *(mDNSv6Addr*)&ifa_addr->sin6_addr; - return(0); - } - else - { - LogMsg("SetupAddr invalid sa_family %d", sa->sa_family); - return(-1); - } - } - -mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifaddrs *ifa) - { - mDNSu32 scope_id = if_nametoindex(ifa->ifa_name); - mDNSAddr ip; - SetupAddr(&ip, ifa->ifa_addr); - NetworkInterfaceInfoOSX **p; - for (p = &m->p->InterfaceList; *p; p = &(*p)->next) - if (scope_id == (*p)->scope_id && mDNSSameAddress(&ip, &(*p)->ifinfo.ip)) - { - debugf("AddInterfaceToList: Found existing interface %u with address %#a", scope_id, &ip); - (*p)->Exists = mDNStrue; - return(*p); - } - - debugf("AddInterfaceToList: Making new interface %u with address %#a", scope_id, &ip); - NetworkInterfaceInfoOSX *i = (NetworkInterfaceInfoOSX *)mallocL("NetworkInterfaceInfoOSX", sizeof(*i)); - if (!i) return(mDNSNULL); - i->ifa_name = (char *)mallocL("NetworkInterfaceInfoOSX name", strlen(ifa->ifa_name) + 1); - if (!i->ifa_name) { freeL("NetworkInterfaceInfoOSX", i); return(mDNSNULL); } - strcpy(i->ifa_name, ifa->ifa_name); - - bzero(&i->ifinfo.uDNS_info, sizeof(uDNS_NetworkInterfaceInfo)); - i->ifinfo.InterfaceID = mDNSNULL; - i->ifinfo.ip = ip; - i->ifinfo.Advertise = m->AdvertiseLocalAddresses; - i->ifinfo.McastTxRx = mDNSfalse; // For now; will be set up later at the end of UpdateInterfaceList - - i->next = mDNSNULL; - i->Exists = mDNStrue; - i->scope_id = scope_id; - i->sa_family = ifa->ifa_addr->sa_family; - i->Multicast = (ifa->ifa_flags & IFF_MULTICAST) && !(ifa->ifa_flags & IFF_POINTOPOINT); - - i->ss.m = m; - i->ss.info = i; - i->ss.sktv4 = i->ss.sktv6 = -1; - i->ss.cfsv4 = i->ss.cfsv6 = NULL; - i->ss.rlsv4 = i->ss.rlsv6 = NULL; - - *p = i; - return(i); - } - -mDNSlocal NetworkInterfaceInfoOSX *FindRoutableIPv4(mDNS *const m, mDNSu32 scope_id) - { - NetworkInterfaceInfoOSX *i; - for (i = m->p->InterfaceList; i; i = i->next) - if (i->Exists && i->scope_id == scope_id && i->ifinfo.ip.type == mDNSAddrType_IPv4) - if (!(i->ifinfo.ip.ip.v4.b[0] == 169 && i->ifinfo.ip.ip.v4.b[1] == 254)) - return(i); - return(mDNSNULL); - } - -mDNSlocal mStatus UpdateInterfaceList(mDNS *const m) - { - mDNSBool foundav4 = mDNSfalse; - struct ifaddrs *ifa = myGetIfAddrs(1); - struct ifaddrs *theLoopback = NULL; - int err = (ifa != NULL) ? 0 : (errno != 0 ? errno : -1); - int InfoSocket = err ? -1 : socket(AF_INET6, SOCK_DGRAM, 0); - if (err) return(err); - - // Set up the nice label - m->nicelabel.c[0] = 0; - GetUserSpecifiedFriendlyComputerName(&m->nicelabel); - if (m->nicelabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->nicelabel, "Macintosh"); - - // Set up the RFC 1034-compliant label - domainlabel hostlabel; - hostlabel.c[0] = 0; - GetUserSpecifiedRFC1034ComputerName(&hostlabel); - if (hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&hostlabel, "Macintosh"); - // If the user has changed their dot-local host name since the last time we checked, then update our local copy. - // If the user has not changed their dot-local host name, then leave ours alone (m->hostlabel may have gone through - // repeated conflict resolution to get to its current value, and if we reset it, we'll have to go through all that again.) - if (SameDomainLabel(m->p->userhostlabel.c, hostlabel.c)) - debugf("Userhostlabel (%#s) unchanged since last time; not changing m->hostlabel (%#s)", m->p->userhostlabel.c, m->hostlabel.c); - else - { - debugf("Updating m->hostlabel to %#s", hostlabel.c); - m->p->userhostlabel = m->hostlabel = hostlabel; - mDNS_GenerateFQDN(m); - if (mDNS_DNSRegistered(m)) mDNS_GenerateGlobalFQDN(m); - } - - while (ifa) - { -#if LIST_ALL_INTERFACES - if (ifa->ifa_addr->sa_family == AF_APPLETALK) - debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d is AF_APPLETALK", - ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); - else if (ifa->ifa_addr->sa_family == AF_LINK) - debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d is AF_LINK", - ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); - else if (ifa->ifa_addr->sa_family != AF_INET && ifa->ifa_addr->sa_family != AF_INET6) - debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d not AF_INET (2) or AF_INET6 (30)", - ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); - if (!(ifa->ifa_flags & IFF_UP)) - debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d Interface not IFF_UP", - ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); - if (!(ifa->ifa_flags & IFF_MULTICAST)) - debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d Interface not IFF_MULTICAST", - ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); - if (ifa->ifa_flags & IFF_POINTOPOINT) - debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d Interface IFF_POINTOPOINT", - ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); - if (ifa->ifa_flags & IFF_LOOPBACK) - debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d Interface IFF_LOOPBACK", - ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); -#endif - if (ifa->ifa_flags & IFF_UP) - if (ifa->ifa_addr->sa_family == AF_INET || ifa->ifa_addr->sa_family == AF_INET6) - { - int ifru_flags6 = 0; - if (ifa->ifa_addr->sa_family == AF_INET6 && InfoSocket >= 0) - { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; - struct in6_ifreq ifr6; - bzero((char *)&ifr6, sizeof(ifr6)); - strncpy(ifr6.ifr_name, ifa->ifa_name, sizeof(ifr6.ifr_name)); - ifr6.ifr_addr = *sin6; - if (ioctl(InfoSocket, SIOCGIFAFLAG_IN6, &ifr6) != -1) - ifru_flags6 = ifr6.ifr_ifru.ifru_flags6; - verbosedebugf("%s %.16a %04X %04X", ifa->ifa_name, &sin6->sin6_addr, ifa->ifa_flags, ifru_flags6); - } - if (!(ifru_flags6 & (IN6_IFF_NOTREADY | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED | IN6_IFF_TEMPORARY))) - { - if (ifa->ifa_flags & IFF_LOOPBACK) - theLoopback = ifa; - else - { - AddInterfaceToList(m, ifa); - if (ifa->ifa_addr->sa_family == AF_INET) - foundav4 = mDNStrue; - } - } - } - ifa = ifa->ifa_next; - } - - // Temporary workaround: Multicast loopback on IPv6 interfaces appears not to work. - // In the interim, we skip loopback interface only if we found at least one v4 interface to use - if (!foundav4 && theLoopback) - AddInterfaceToList(m, theLoopback); - - // Now the list is complete, set the McastTxRx setting for each interface. - // We always send and receive using IPv4. - // To reduce traffic, we send and receive using IPv6 only on interfaces that have no routable IPv4 address. - // Having a routable IPv4 address assigned is a reasonable indicator of being on a large configured network, - // which means there's a good chance that most or all the other devices on that network should also have v4. - // By doing this we lose the ability to talk to true v6-only devices on that link, but we cut the packet rate in half. - // At this time, reducing the packet rate is more important than v6-only devices on a large configured network, - // so we are willing to make that sacrifice. - NetworkInterfaceInfoOSX *i; - for (i = m->p->InterfaceList; i; i = i->next) - if (i->Exists) - { - mDNSBool txrx = i->Multicast && ((i->ifinfo.ip.type == mDNSAddrType_IPv4) || !FindRoutableIPv4(m, i->scope_id)); - if (i->ifinfo.McastTxRx != txrx) - { - i->ifinfo.McastTxRx = txrx; - i->Exists = 2; // State change; need to deregister and reregister this interface - } - } - - if (InfoSocket >= 0) close(InfoSocket); - return(err); - } - -mDNSlocal NetworkInterfaceInfoOSX *SearchForInterfaceByName(mDNS *const m, char *ifname, int type) - { - NetworkInterfaceInfoOSX *i; - for (i = m->p->InterfaceList; i; i = i->next) - if (i->Exists && !strcmp(i->ifa_name, ifname) && - ((AAAA_OVER_V4 ) || - (type == AF_INET && i->ifinfo.ip.type == mDNSAddrType_IPv4) || - (type == AF_INET6 && i->ifinfo.ip.type == mDNSAddrType_IPv6) )) return(i); - return(NULL); - } - -mDNSlocal void SetupActiveInterfaces(mDNS *const m) - { - NetworkInterfaceInfoOSX *i; - for (i = m->p->InterfaceList; i; i = i->next) - if (i->Exists) - { - NetworkInterfaceInfo *n = &i->ifinfo; - NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifa_name, i->sa_family); - if (!primary) LogMsg("SetupActiveInterfaces ERROR! SearchForInterfaceByName didn't find %s", i->ifa_name); - - if (n->InterfaceID && n->InterfaceID != (mDNSInterfaceID)primary) // Sanity check - { - LogMsg("SetupActiveInterfaces ERROR! n->InterfaceID %p != primary %p", n->InterfaceID, primary); - n->InterfaceID = mDNSNULL; - } - - if (!n->InterfaceID) - { - // NOTE: If n->InterfaceID is set, that means we've called mDNS_RegisterInterface() for this interface, - // so we need to make sure we call mDNS_DeregisterInterface() before disposing it. - // If n->InterfaceID is NOT set, then we haven't registered it and we should not try to deregister it - n->InterfaceID = (mDNSInterfaceID)primary; - mDNS_RegisterInterface(m, n); - LogOperation("SetupActiveInterfaces: Registered %s(%lu) InterfaceID %p %#a%s", - i->ifa_name, i->scope_id, primary, &n->ip, n->InterfaceActive ? " (Primary)" : ""); - } - - if (!n->McastTxRx) - debugf("SetupActiveInterfaces: No Tx/Rx on %s(%lu) InterfaceID %p %#a", i->ifa_name, i->scope_id, primary, &n->ip); - else - { - if (i->sa_family == AF_INET && primary->ss.sktv4 == -1) - { - mStatus err = SetupSocket(&primary->ss, MulticastDNSPort, &i->ifinfo.ip, AF_INET); - if (err == 0) debugf("SetupActiveInterfaces: v4 socket%2d %s(%lu) InterfaceID %p %#a", primary->ss.sktv4, i->ifa_name, i->scope_id, n->InterfaceID, &n->ip); - else LogMsg("SetupActiveInterfaces: v4 socket%2d %s(%lu) InterfaceID %p %#a FAILED", primary->ss.sktv4, i->ifa_name, i->scope_id, n->InterfaceID, &n->ip); - } - - if (i->sa_family == AF_INET6 && primary->ss.sktv6 == -1) - { - mStatus err = SetupSocket(&primary->ss, MulticastDNSPort, &i->ifinfo.ip, AF_INET6); - if (err == 0) debugf("SetupActiveInterfaces: v6 socket%2d %s(%lu) InterfaceID %p %#a", primary->ss.sktv6, i->ifa_name, i->scope_id, n->InterfaceID, &n->ip); - else LogMsg("SetupActiveInterfaces: v6 socket%2d %s(%lu) InterfaceID %p %#a FAILED", primary->ss.sktv6, i->ifa_name, i->scope_id, n->InterfaceID, &n->ip); - } - } - } - } - -mDNSlocal void MarkAllInterfacesInactive(mDNS *const m) - { - NetworkInterfaceInfoOSX *i; - for (i = m->p->InterfaceList; i; i = i->next) - i->Exists = mDNSfalse; - } - -mDNSlocal void CloseSocketSet(CFSocketSet *ss) - { - // Note: MUST NOT close the underlying native BSD sockets. - // CFSocketInvalidate() will do that for us, in its own good time, which may not necessarily be immediately, - // because it first has to unhook the sockets from its select() call, before it can safely close them. - if (ss->cfsv4) { CFRunLoopRemoveSource(CFRunLoopGetCurrent(), ss->rlsv4, kCFRunLoopDefaultMode); CFRelease(ss->rlsv4); CFSocketInvalidate(ss->cfsv4); CFRelease(ss->cfsv4); } - if (ss->cfsv6) { CFRunLoopRemoveSource(CFRunLoopGetCurrent(), ss->rlsv6, kCFRunLoopDefaultMode); CFRelease(ss->rlsv6); CFSocketInvalidate(ss->cfsv6); CFRelease(ss->cfsv6); } - ss->sktv4 = ss->sktv6 = -1; - ss->cfsv4 = ss->cfsv6 = NULL; - ss->rlsv4 = ss->rlsv6 = NULL; - } - -mDNSlocal void ClearInactiveInterfaces(mDNS *const m) - { - // First pass: - // If an interface is going away, then deregister this from the mDNSCore. - // We also have to deregister it if the primary interface that it's using for its InterfaceID is going away. - // We have to do this because mDNSCore will use that InterfaceID when sending packets, and if the memory - // it refers to has gone away we'll crash. - // Don't actually close the sockets or free the memory yet: When the last representative of an interface goes away - // mDNSCore may want to send goodbye packets on that interface. (Not yet implemented, but a good idea anyway.) - NetworkInterfaceInfoOSX *i; - for (i = m->p->InterfaceList; i; i = i->next) - { - // 1. If this interface is no longer active, or its InterfaceID is changing, deregister it - NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifa_name, i->sa_family); - if (i->ifinfo.InterfaceID) - if (i->Exists == 0 || i->Exists == 2 || i->ifinfo.InterfaceID != (mDNSInterfaceID)primary) - { - LogOperation("ClearInactiveInterfaces: Deregistering %s(%lu) InterfaceID %p %#a%s", - i->ifa_name, i->scope_id, i->ifinfo.InterfaceID, &i->ifinfo.ip, i->ifinfo.InterfaceActive ? " (Primary)" : ""); - mDNS_DeregisterInterface(m, &i->ifinfo); - i->ifinfo.InterfaceID = mDNSNULL; - // NOTE: If n->InterfaceID is set, that means we've called mDNS_RegisterInterface() for this interface, - // so we need to make sure we call mDNS_DeregisterInterface() before disposing it. - // If n->InterfaceID is NOT set, then it's not registered and we should not call mDNS_DeregisterInterface() on it. - } - } - - // Second pass: - // Now that everything that's going to deregister has done so, we can close sockets and free the memory - NetworkInterfaceInfoOSX **p = &m->p->InterfaceList; - while (*p) - { - i = *p; - // 2. Close all our CFSockets. We'll recreate them later as necessary. - // (We may have previously had both v4 and v6, and we may not need both any more.) - CloseSocketSet(&i->ss); - // 3. If no longer active, delete interface from list and free memory - if (!i->Exists && NumCacheRecordsForInterfaceID(m, (mDNSInterfaceID)i) == 0) - { - debugf("ClearInactiveInterfaces: Deleting %#a", &i->ifinfo.ip); - *p = i->next; - if (i->ifa_name) freeL("NetworkInterfaceInfoOSX name", i->ifa_name); - freeL("NetworkInterfaceInfoOSX", i); - } - else - p = &i->next; - } - } - - -mDNSlocal mStatus RegisterNameServers(mDNS *const m, CFDictionaryRef dict) - { - int i, count; - CFArrayRef values; - char buf[256]; - mDNSv4Addr saddr; - CFStringRef s; - - - mDNS_DeregisterDNSList(m); // deregister orig list - values = CFDictionaryGetValue(dict, kSCPropNetDNSServerAddresses); - if (!values) return mStatus_NoError; - - count = CFArrayGetCount(values); - for (i = 0; i < count; i++) - { - s = CFArrayGetValueAtIndex(values, i); - if (!s) { LogMsg("ERROR: RegisterNameServers - CFArrayGetValueAtIndex"); break; } - if (!CFStringGetCString(s, buf, 256, kCFStringEncodingASCII)) - { - LogMsg("ERROR: RegisterNameServers - CFStringGetCString"); - continue; - } - if (!inet_aton(buf, (struct in_addr *)saddr.b)) - { - LogMsg("ERROR: RegisterNameServers - invalid address string %s", buf); - continue; - } - mDNS_RegisterDNS(m, &saddr); - } - return mStatus_NoError; - } - -mDNSlocal void FreeARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result) - { - (void)m; // unused - AuthRecordListElem *elem = rr->RecordContext; - if (result == mStatus_MemFree) freeL("FreeARElemCallback", elem); - } - -mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) - { - SearchListElem *slElem = question->QuestionContext; - AuthRecordListElem *arElem, *ptr, *prev; - AuthRecord *dereg; - char *name; - mStatus err; - - if (AddRecord) - { - arElem = mallocL("FoundDomain - arElem", sizeof(AuthRecordListElem)); - if (!arElem) { LogMsg("ERROR: malloc"); return; } - mDNS_SetupResourceRecord(&arElem->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, FreeARElemCallback, arElem); - if (question == &slElem->browseQ) name = "_browse._dns-sd._udp.local."; - else name = "_register._dns-sd._udp.local."; - MakeDomainNameFromDNSNameString(&arElem->ar.resrec.name, name); - strcpy(arElem->ar.resrec.rdata->u.name.c, answer->rdata->u.name.c); - err = mDNS_Register(m, &arElem->ar); - if (err) - { - LogMsg("ERROR: FoundDomain - mDNS_Register returned %d", err); - freeL("FoundDomain - arElem", arElem); - return; - } - arElem->next = slElem->AuthRecs; - slElem->AuthRecs = arElem; - } - else - { - ptr = slElem->AuthRecs; - prev = NULL; - while (ptr) - { - if (SameDomainName(&ptr->ar.resrec.name, &answer->name) && SameDomainName(&ptr->ar.resrec.rdata->u.name, &answer->rdata->u.name)) - { - debugf("Deregistering PTR %s -> %s", ptr->ar.resrec.name.c, ptr->ar.resrec.rdata->u.name.c); - dereg = &ptr->ar; - if (prev) prev->next = ptr->next; - else slElem->AuthRecs = ptr->next; - ptr = ptr->next; - err = mDNS_Deregister(m, dereg); - if (err) LogMsg("ERROR: FoundDomain - mDNS_Deregister returned %d", err); - } - else - { - prev = ptr; - ptr = ptr->next; - } - } - } - } - -mDNSlocal mStatus RegisterSearchDomains(mDNS *const m, CFDictionaryRef dict) - { - int i, count; - CFArrayRef values; - domainname domain; - char buf[MAX_ESCAPED_DOMAIN_NAME]; - CFStringRef s; - SearchListElem *new, *ptr, *prev, *freeSLPtr; - AuthRecordListElem *arList; - mStatus err; - - // step 1: mark each elem for removal (-1) - for (ptr = SearchList; ptr; ptr = ptr->next) ptr->flag = -1; - - values = CFDictionaryGetValue(dict, kSCPropNetDNSSearchDomains); - if (values) - { - count = CFArrayGetCount(values); - for (i = 0; i < count; i++) - { - s = CFArrayGetValueAtIndex(values, i); - if (!s) { LogMsg("ERROR: RegisterNameServers - CFArrayGetValueAtIndex"); break; } - if (!CFStringGetCString(s, buf, MAX_ESCAPED_DOMAIN_NAME, kCFStringEncodingASCII)) - { - LogMsg("ERROR: RegisterNameServers - CFStringGetCString"); - continue; - } - if (!MakeDomainNameFromDNSNameString(&domain, buf)) - { - LogMsg("ERROR: RegisterNameServers - invalid search domain %s", buf); - continue; - } - // if domain is in list, mark as pre-existent (0) - for (ptr = SearchList; ptr; ptr = ptr->next) - if (SameDomainName(&ptr->domain, &domain)) { ptr->flag = 0; break; } - - // if domain not in list, add to list, mark as add (1) - if (!ptr) - { - new = mallocL("RegisterSearchDomains - SearchListElem", sizeof(SearchListElem)); - if (!new) { LogMsg("ERROR: RegisterSearchDomains - malloc"); return mStatus_UnknownErr; } - bzero(new, sizeof(SearchListElem)); - strcpy(new->domain.c, domain.c); - new->flag = 1; // add - new->next = SearchList; - SearchList = new; - } - } - } - // delete elems marked for removal, do queries for elems marked add - prev = NULL; - ptr = SearchList; - while (ptr) - { - if (ptr->flag == -1) // remove - { - mDNS_StopQuery(m, &ptr->browseQ); - mDNS_StopQuery(m, &ptr->registerQ); - // deregister records generated from answers to the query - arList = ptr->AuthRecs; - ptr->AuthRecs = NULL; - while (arList) - { - AuthRecord *dereg = &arList->ar; - arList = arList->next; - debugf("Deregistering PTR %s -> %s", dereg->resrec.name.c, dereg->resrec.rdata->u.name.c); - err = mDNS_Deregister(m, dereg); - if (err) LogMsg("ERROR: RegisterSearchDomains mDNS_Deregister returned %d", err); - } - - // remove elem from list, delete - if (prev) prev->next = ptr->next; - else SearchList = ptr->next; - freeSLPtr = ptr; - ptr = ptr->next; - freeL("RegisterNameServers - freeSLPtr", freeSLPtr); - continue; - } - - if (ptr->flag == 1) // add - { - err = mDNS_GetDomains(m, &ptr->browseQ, mDNS_DomainTypeBrowse, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr); - if (err) LogMsg("ERROR: RegisterNameServers - mDNS_DomainTypeBrowse, %d", err); - - err = mDNS_GetDomains(m, &ptr->registerQ, mDNS_DomainTypeRegistration, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr); - if (err) LogMsg("ERROR: RegisterNameServers - mDNS_DomainTypeRegistration, %d", err); - ptr->flag = 0; - } - - if (ptr->flag) { LogMsg("RegisterNameServers - unknown flag %d. Skipping.", ptr->flag); } - - prev = ptr; - ptr = ptr->next; - } - - return mStatus_NoError; - } - -// key must be kSCPropNetDNSServerAddresses or kSCPropNetDNSSearchDomains -mDNSlocal mStatus RegisterDNSConfig(mDNS *const m, CFDictionaryRef dict, const CFStringRef key) - { - if (key == kSCPropNetDNSSearchDomains) return RegisterSearchDomains(m, dict); - if (key == kSCPropNetDNSServerAddresses) return RegisterNameServers(m, dict); - LogMsg("ERROR: RegisterDNSConfig - bad key"); return mStatus_UnknownErr; - } - - -mDNSlocal void DNSConfigChanged(SCDynamicStoreRef session, CFArrayRef changes, void *context) - { - mDNS *m = context; - CFDictionaryRef dict; - CFStringRef key; - - if (DNSConfigInitialized && (!changes || CFArrayGetCount(changes) == 0)) return; - - //!!!KRS fixme - we need a list of registerd servers. this wholesale - // dereg doesn't work if there's an error and we bail out before registering the new list - - key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetDNS); - if (!key) { LogMsg("ERROR: DNSConfigChanged - SCDynamicStoreKeyCreateNetworkGlobalEntity"); return; } - dict = SCDynamicStoreCopyValue(session, key); - CFRelease(key); - if (dict) - { - RegisterDNSConfig(m, dict, kSCPropNetDNSServerAddresses); - RegisterDNSConfig(m, dict, kSCPropNetDNSSearchDomains); - CFRelease(dict); - } - if (mDNS_DNSRegistered(m)) mDNS_GenerateGlobalFQDN(m); - // no-op if label & domain are unchanged - } - -mDNSlocal mStatus WatchForDNSChanges(mDNS *const m) - { - CFStringRef key; - CFMutableArrayRef keyList; - CFRunLoopSourceRef rls; - SCDynamicStoreRef session; - SCDynamicStoreContext context = { 0, m, NULL, NULL, NULL }; - - session = SCDynamicStoreCreate(NULL, CFSTR("trackDNS"), DNSConfigChanged, &context); - if (!session) { LogMsg("ERROR: WatchForDNSChanges - SCDynamicStoreCreate"); return mStatus_UnknownErr; } - - keyList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - if (!keyList) { LogMsg("ERROR: WatchForDNSChanges - CFArrayCreateMutable"); return mStatus_UnknownErr; } - - // create a pattern that matches the global DNS dictionary key - key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetDNS); - if (!key) { LogMsg("ERROR: WatchForDNSChanges - SCDynamicStoreKeyCreateNetworkGlobalEntity"); return mStatus_UnknownErr; } - - CFArrayAppendValue(keyList, key); - CFRelease(key); - - // set the keys for our DynamicStore session - SCDynamicStoreSetNotificationKeys(session, keyList, NULL); - CFRelease(keyList); - - // create a CFRunLoopSource for our DynamicStore session - rls = SCDynamicStoreCreateRunLoopSource(NULL, session, 0); - if (!rls) { LogMsg("ERROR: WatchForDNSChanges - SCDynamicStoreCreateRunLoopSource"); return mStatus_UnknownErr; } - - // add the run loop source to our current run loop - CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); - CFRelease(rls); - - // get initial configuration - DNSConfigChanged(session, NULL, m); - return mStatus_NoError; - } - -mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context) - { - (void)store; // Parameter not used - (void)changedKeys; // Parameter not used - debugf("*** Network Configuration Change ***"); - - mDNS *const m = (mDNS *const)context; - MarkAllInterfacesInactive(m); - UpdateInterfaceList(m); - ClearInactiveInterfaces(m); - SetupActiveInterfaces(m); - - if (m->MainCallback) - m->MainCallback(m, mStatus_ConfigChanged); - } - -mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m) - { - mStatus err = -1; - SCDynamicStoreContext context = { 0, m, NULL, NULL, NULL }; - SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder"), NetworkChanged, &context); - CFStringRef key1 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4); - CFStringRef key2 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6); - CFStringRef key3 = SCDynamicStoreKeyCreateComputerName(NULL); - CFStringRef key4 = SCDynamicStoreKeyCreateHostNames(NULL); - CFStringRef pattern1 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4); - CFStringRef pattern2 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv6); - - CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - CFMutableArrayRef patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - - if (!store) { LogMsg("SCDynamicStoreCreate failed: %s\n", SCErrorString(SCError())); goto error; } - if (!key1 || !key2 || !key3 || !key4 || !keys || !pattern1 || !pattern2 || !patterns) goto error; - - CFArrayAppendValue(keys, key1); - CFArrayAppendValue(keys, key2); - CFArrayAppendValue(keys, key3); - CFArrayAppendValue(keys, key4); - CFArrayAppendValue(patterns, pattern1); - CFArrayAppendValue(patterns, pattern2); - if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns)) - { LogMsg("SCDynamicStoreSetNotificationKeys failed: %s\n", SCErrorString(SCError())); goto error; } - - m->p->StoreRLS = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); - if (!m->p->StoreRLS) { LogMsg("SCDynamicStoreCreateRunLoopSource failed: %s\n", SCErrorString(SCError())); goto error; } - - CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode); - m->p->Store = store; - err = 0; - goto exit; - -error: - if (store) CFRelease(store); - -exit: - if (key1) CFRelease(key1); - if (key2) CFRelease(key2); - if (key3) CFRelease(key3); - if (key4) CFRelease(key4); - if (pattern1) CFRelease(pattern1); - if (pattern2) CFRelease(pattern2); - if (keys) CFRelease(keys); - if (patterns) CFRelease(patterns); - - return(err); - } - -mDNSlocal void PowerChanged(void *refcon, io_service_t service, natural_t messageType, void *messageArgument) - { - mDNS *const m = (mDNS *const)refcon; - (void)service; // Parameter not used - switch(messageType) - { - case kIOMessageCanSystemPowerOff: debugf("PowerChanged kIOMessageCanSystemPowerOff (no action)"); break; // E0000240 - case kIOMessageSystemWillPowerOff: debugf("PowerChanged kIOMessageSystemWillPowerOff"); mDNSCoreMachineSleep(m, true); break; // E0000250 - case kIOMessageSystemWillNotPowerOff: debugf("PowerChanged kIOMessageSystemWillNotPowerOff (no action)"); break; // E0000260 - case kIOMessageCanSystemSleep: debugf("PowerChanged kIOMessageCanSystemSleep (no action)"); break; // E0000270 - case kIOMessageSystemWillSleep: debugf("PowerChanged kIOMessageSystemWillSleep"); mDNSCoreMachineSleep(m, true); break; // E0000280 - case kIOMessageSystemWillNotSleep: debugf("PowerChanged kIOMessageSystemWillNotSleep (no action)"); break; // E0000290 - case kIOMessageSystemHasPoweredOn: debugf("PowerChanged kIOMessageSystemHasPoweredOn"); mDNSCoreMachineSleep(m, false); break; // E0000300 - default: debugf("PowerChanged unknown message %X", messageType); break; - } - IOAllowPowerChange(m->p->PowerConnection, (long)messageArgument); - } - -mDNSlocal mStatus WatchForPowerChanges(mDNS *const m) - { - IONotificationPortRef thePortRef; - m->p->PowerConnection = IORegisterForSystemPower(m, &thePortRef, PowerChanged, &m->p->PowerNotifier); - if (m->p->PowerConnection) - { - m->p->PowerRLS = IONotificationPortGetRunLoopSource(thePortRef); - CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->PowerRLS, kCFRunLoopDefaultMode); - return(mStatus_NoError); - } - return(-1); - } - -mDNSexport mDNSBool haveSecInfo = mDNSfalse; // this must go away once we have full keychain integration -mDNSlocal void GetAuthInfoFromKeychainItem(mDNS *m, SecKeychainItemRef item) - { - OSStatus err; - mDNSu32 infoTag = kSecAccountItemAttr; - mDNSu32 infoFmt = 0; // string - SecKeychainAttributeInfo info; - SecKeychainAttributeList *authAttrList = NULL; - void *data; - mDNSu32 dataLen; - - mStatus regErr; - char accountName[MAX_ESCAPED_DOMAIN_NAME]; - domainname zone; - AuthRecord *rrReg, *rrBrowse; - - info.count = 1; - info.tag = &infoTag; - info.format = &infoFmt; - - err = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &authAttrList, &dataLen, &data); - if (err) { LogMsg("SecKeychainItemCopyAttributesAndData returned error %d", err); return; } - - // copy account name - if (!authAttrList->count || authAttrList->attr->tag != kSecAccountItemAttr) - { LogMsg("Received bad authAttrList"); return; } - - if (authAttrList->attr->length + strlen(LH_SUFFIX) > MAX_ESCAPED_DOMAIN_NAME) - { LogMsg("Account name too long (%d bytes)", authAttrList->attr->length); return; } - memcpy(accountName, authAttrList->attr->data, authAttrList->attr->length); - accountName[authAttrList->attr->length] = '\0'; - - zone.c[0] = '\0'; - if (!AppendLiteralLabelString(&zone, accountName) || - !AppendDNSNameString(&zone, LH_SUFFIX)) - { LogMsg("InitAuthInfo - bad account name"); return; } - - mDNS_UpdateDomainRequiresAuthentication(m, &zone, &zone, data, dataLen, mDNStrue); - if(m->uDNS_info.NameRegDomain) { debugf("Overwriting config file options with KeyChain values"); } - - if (!ConvertDomainNameToCString(&zone, m->uDNS_info.NameRegDomain) || - !ConvertDomainNameToCString(&zone, m->uDNS_info.ServiceRegDomain)) - { LogMsg("Couldn't set keychain username in uDNS global info"); } - - mDNS_GenerateGlobalFQDN(m); - // normally we'd query the zone for _register/_browse domains, but to reduce server load we manually generate the records - - haveSecInfo = mDNStrue; - //!!!KRS need to do better bookkeeping once we support multiple users - rrReg = mallocL("AuthRecord", sizeof(AuthRecord)); - rrBrowse = mallocL("AuthRecord", sizeof(AuthRecord)); - if (!rrReg || !rrBrowse) { LogMsg("ERROR: Malloc"); return; } - - // set up _browse - mDNS_SetupResourceRecord(rrBrowse, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, mDNSNULL, mDNSNULL); - MakeDomainNameFromDNSNameString(&rrBrowse->resrec.name, "_browse._dns-sd._udp.local."); - strcpy(rrBrowse->resrec.rdata->u.name.c, zone.c); - - // set up _register - mDNS_SetupResourceRecord(rrReg, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, mDNSNULL, mDNSNULL); - MakeDomainNameFromDNSNameString(&rrReg->resrec.name, "_register._dns-sd._udp.local."); - strcpy(rrReg->resrec.rdata->u.name.c, zone.c); - - regErr = mDNS_Register(m, rrReg); - if (regErr) LogMsg("Registration of local-only reg domain %s failed", zone.c); - - regErr = mDNS_Register(m, rrBrowse); - if (regErr) LogMsg("Registration of local-only browse domain %s failed", zone.c); - SecKeychainItemFreeContent(authAttrList, data); - } - -mDNSlocal void InitAuthInfo(mDNS *m); - -mDNSlocal OSStatus KeychainCallback(SecKeychainEvent event, SecKeychainCallbackInfo *info, void *context) - { - (void)event; - (void)info; - // unused - - debugf("SecKeychainAddCallback received event %d", event); - InitAuthInfo((mDNS *)context); // keychain events happen rarely - just rebuild the list - return 0; - } - -mDNSexport void InitAuthInfo(mDNS *m) - { - OSStatus err; - - SecKeychainSearchRef searchRef = NULL; - SecKeychainRef sysKeychain = NULL; - SecKeychainAttribute searchAttrs[] = { { kSecDescriptionItemAttr, strlen(LH_KEYCHAIN_DESC), LH_KEYCHAIN_DESC }, - { kSecServiceItemAttr, strlen(LH_KEYCHAIN_SERVICE), LH_KEYCHAIN_SERVICE } }; - SecKeychainAttributeList searchList = { sizeof(searchAttrs) / sizeof(*searchAttrs), searchAttrs }; - SecKeychainItemRef item; - - // clear any previous entries - mDNS_ClearAuthenticationList(m); - - err = SecKeychainOpen(SYS_KEYCHAIN_PATH, &sysKeychain); - if (err) { LogMsg("ERROR: InitAuthInfo - couldn't open system keychain - %d", err); goto release_refs; } - err = SecKeychainSetDomainDefault(kSecPreferencesDomainSystem, sysKeychain); - if (err) { LogMsg("ERROR: InitAuthInfo - couldn't set domain default for system keychain - %d", err); goto release_refs; } - - err = SecKeychainSearchCreateFromAttributes(sysKeychain, kSecGenericPasswordItemClass, &searchList, &searchRef); - if (err) { LogMsg("ERROR: InitAuthInfo - SecKeychainSearchCreateFromAttributes %d", err); goto release_refs; } - - while (!SecKeychainSearchCopyNext(searchRef, &item)) - { - GetAuthInfoFromKeychainItem(m, item); - CFRelease(item); - } - err = SecKeychainAddCallback(KeychainCallback, kSecAddEventMask | kSecDeleteEventMask | kSecUpdateEventMask | kSecPasswordChangedEventMask, m); - if (err && err != errSecDuplicateCallback) { LogMsg("SecKeychainAddCallback returned error %d", err); } - - release_refs: - - if (searchRef) CFRelease(searchRef); - if (sysKeychain) CFRelease(sysKeychain); - } - -CF_EXPORT CFDictionaryRef _CFCopySystemVersionDictionary(void); -CF_EXPORT const CFStringRef _kCFSystemVersionProductNameKey; -CF_EXPORT const CFStringRef _kCFSystemVersionProductVersionKey; -CF_EXPORT const CFStringRef _kCFSystemVersionBuildVersionKey; - -mDNSexport mDNSBool mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring) - { - int major = 0, minor = 0; - char letter = 0, prodname[256]="Mac OS X", prodvers[256]="", buildver[256]="?"; - CFDictionaryRef vers = _CFCopySystemVersionDictionary(); - if (vers) - { - CFStringRef cfprodname = CFDictionaryGetValue(vers, _kCFSystemVersionProductNameKey); - CFStringRef cfprodvers = CFDictionaryGetValue(vers, _kCFSystemVersionProductVersionKey); - CFStringRef cfbuildver = CFDictionaryGetValue(vers, _kCFSystemVersionBuildVersionKey); - if (cfprodname) CFStringGetCString(cfprodname, prodname, sizeof(prodname), kCFStringEncodingUTF8); - if (cfprodvers) CFStringGetCString(cfprodvers, prodvers, sizeof(prodvers), kCFStringEncodingUTF8); - if (cfbuildver) CFStringGetCString(cfbuildver, buildver, sizeof(buildver), kCFStringEncodingUTF8); - sscanf(buildver, "%d%c%d", &major, &letter, &minor); - CFRelease(vers); - } - if (HINFO_SWstring) mDNS_snprintf(HINFO_SWstring, 256, "%s %s (%s), %s", prodname, prodvers, buildver, mDNSResponderVersionString); - return(major); - } - -// Test to see if we're the first client running on UDP port 5353, by trying to bind to 5353 without using SO_REUSEPORT. -// If we fail, someone else got here first. That's not a big problem; we can share the port for multicast responses -- -// we just need to be aware that we shouldn't expect to successfully receive unicast UDP responses. -mDNSlocal mDNSBool mDNSPlatformInit_ReceiveUnicast(void) - { - int err; - int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - struct sockaddr_in s5353; - s5353.sin_family = AF_INET; - s5353.sin_port = MulticastDNSPort.NotAnInteger; - s5353.sin_addr.s_addr = 0; - err = bind(s, (struct sockaddr *)&s5353, sizeof(s5353)); - close(s); - if (err) debugf("No unicast UDP responses"); - else debugf("Unicast UDP responses okay"); - return(err == 0); - } - - -//!!!KRS this should be less order-dependent as we support more configuration options -mDNSlocal mDNSBool GetConfigOption(char *dst, const char *option, FILE *f) - { - char buf[1024]; - int len; - - if (!fgets(buf, 1024, f)) { LogMsg("Option %s not set", option); return mDNSfalse; } - len = strlen(option); - if (!strncmp(buf, option, len)) - { - strcpy(dst, buf + len + 1); - len = strlen(dst); - if ( len && dst[len-1] == '\n') dst[len-1] = '\0'; // chop newline - return mDNStrue; - } - LogMsg("Malformatted config file - %s not set", option); - return mDNSfalse; - } - - - -mDNSlocal void ReadRegDomainFromConfig(mDNS *const m) - { - FILE *f; - uDNS_GlobalInfo *u = &m->uDNS_info;; - char key[MAX_ESCAPED_DOMAIN_NAME]; - domainname key_d, name_d, service_d; - char secret[1024]; - int slen; - mStatus err; - - // read registration domain (for dynamic updates) from config file - // !!!KRS these must go away once we can learn the reg domain from the network or prefs - if (m->uDNS_info.NameRegDomain[0] || m->uDNS_info.ServiceRegDomain[0]) - { debugf("Options from config already set via keychain. Ignoring config file."); return; } - - f = fopen(CONFIG_FILE, "r"); - if (!f) - { - if (errno != ENOENT) LogMsg("ERROR: Config file exists, but cannot be opened."); - return; - } - - if (!GetConfigOption(u->NameRegDomain, "name-reg", f)) goto end; - if (!GetConfigOption(u->ServiceRegDomain, "service-reg", f)) goto end; - if (!GetConfigOption(key, "key-name", f)) goto end; - if (!GetConfigOption(secret, "secret-64", f)) { LogMsg("ERROR: config file contains key without secret"); goto end; } - - // we don't actually need this in domain-name format - just convert it to error check - if (!MakeDomainNameFromDNSNameString(&service_d, u->ServiceRegDomain)) - { LogMsg("ERROR: config file contains bad service reg domain %s", u->ServiceRegDomain); u->ServiceRegDomain[0] = '\0'; } - - if (!MakeDomainNameFromDNSNameString(&name_d, u->NameRegDomain)) - { LogMsg("ERROR: config file contains bad name reg domain %s", u->NameRegDomain); u->NameRegDomain[0] = '\0'; } - - if (!MakeDomainNameFromDNSNameString(&key_d, key)) - { LogMsg("ERROR: config file contains bad key %s", key); key[0] = '\0'; } - - if (key[0]) - { - slen = strlen(secret); - if (u->ServiceRegDomain[0]) - { - err = mDNS_UpdateDomainRequiresAuthentication(m, &service_d, &key_d, secret, slen, mDNStrue); - if (err) LogMsg("ERROR: mDNS_UpdateDomainRequiresAuthentication returned %d for domain ", err, u->ServiceRegDomain); - } - if (u->NameRegDomain[0]) - { - err = mDNS_UpdateDomainRequiresAuthentication(m, &name_d, &key_d, secret, slen, mDNStrue); - if (err) LogMsg("ERROR: mDNS_UpdateDomainRequiresAuthentication returned %d for domain ", err, u->NameRegDomain); - } - } - - end: - fclose(f); - } - -mDNSexport DNameListElem *mDNSPlatformGetSearchDomainList(void) - { - return mDNS_CopyDNameList(DefBrowseList); - } - -mDNSexport DNameListElem *mDNSPlatformGetRegDomainList(void) - { - static DNameListElem tmp; - static mDNSBool init = mDNSfalse; - - if (!init) - { - MakeDomainNameFromDNSNameString(&tmp.name, "local."); - tmp.next = NULL; - init = mDNStrue; - } - return mDNS_CopyDNameList(&tmp); - } - - -mDNSlocal void FoundDefBrowseDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) - { - DNameListElem *ptr, *prev, *new; - (void)m; // unused; - (void)question; // unused - - if (AddRecord) - { - new = mallocL("FoundDefBrowseDomain", sizeof(DNameListElem)); - if (!new) { LogMsg("ERROR: malloc"); return; } - strcpy(new->name.c, answer->rdata->u.name.c); - new->next = DefBrowseList; - DefBrowseList = new; - return; - } - else - { - ptr = DefBrowseList; - prev = NULL; - while (ptr) - { - if (SameDomainName(&ptr->name, &answer->rdata->u.name)) - { - if (prev) prev->next = ptr->next; - else DefBrowseList = ptr->next; - freeL("FoundDefBrowseDomain", ptr); - return; - } - prev = ptr; - ptr = ptr->next; - } - LogMsg("FoundDefBrowseDomain: Got remove event for domain %s not in list", answer->rdata->u.name.c); - } - } - -// Construction of Default Browse domain list (i.e. when clients pass NULL) is as follows: -// 1) query for _browse._dns-sd._udp.local on LocalOnly interface -// (.local manually generated via explicit callback) -// 2) for each search domain (from prefs pane), query for _browse._dns-sd._udp.. -// 3) for each result from (2), register LocalOnly PTR record_browse._dns-sd._udp.local. -> -// 4) result above should generate a callback from question in (1). result added to global list -// 5) global list delivered to client via GetSearchDomainList() -// 6) client calls to enumerate domains now go over LocalOnly interface -// (!!!KRS may add outgoing interface in addition) - -mDNSlocal mStatus InitDNSConfig(mDNS *const m) - { - mStatus err; - AuthRecord local; - DNSConfigInitialized = mDNStrue; - - // start query for domains to be used in default (empty string domain) browses - err = mDNS_GetDomains(m, &DefBrowseDomainQ, mDNS_DomainTypeBrowse, NULL, mDNSInterface_LocalOnly, FoundDefBrowseDomain, NULL); - - // provide .local automatically - mDNS_SetupResourceRecord(&local, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, mDNSNULL, mDNSNULL); - MakeDomainNameFromDNSNameString(&local.resrec.name, "_browse._dns-sd._udp.local."); - MakeDomainNameFromDNSNameString(&local.resrec.rdata->u.name, "local."); - // other fields ignored - FoundDefBrowseDomain(m, &DefBrowseDomainQ, &local.resrec, 1); - - return mStatus_NoError; - } - -mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) - { - mStatus err; - - m->hostlabel.c[0] = 0; - - char *HINFO_HWstring = "Macintosh"; - char HINFO_HWstring_buffer[256]; - int get_model[2] = { CTL_HW, HW_MODEL }; - size_t len_model = sizeof(HINFO_HWstring_buffer); - if (sysctl(get_model, 2, HINFO_HWstring_buffer, &len_model, NULL, 0) == 0) - HINFO_HWstring = HINFO_HWstring_buffer; - - char HINFO_SWstring[256] = ""; - if (mDNSMacOSXSystemBuildNumber(HINFO_SWstring) < 7) m->KnownBugs |= mDNS_KnownBug_PhantomInterfaces; - if (mDNSPlatformInit_ReceiveUnicast()) m->CanReceiveUnicast = mDNStrue; - - mDNSu32 hlen = mDNSPlatformStrLen(HINFO_HWstring); - mDNSu32 slen = mDNSPlatformStrLen(HINFO_SWstring); - if (hlen + slen < 254) - { - m->HIHardware.c[0] = hlen; - m->HISoftware.c[0] = slen; - mDNSPlatformMemCopy(HINFO_HWstring, &m->HIHardware.c[1], hlen); - mDNSPlatformMemCopy(HINFO_SWstring, &m->HISoftware.c[1], slen); - } - - m->p->unicastsockets.m = m; - m->p->unicastsockets.info = NULL; - m->p->unicastsockets.sktv4 = m->p->unicastsockets.sktv6 = -1; - m->p->unicastsockets.cfsv4 = m->p->unicastsockets.cfsv6 = NULL; - m->p->unicastsockets.rlsv4 = m->p->unicastsockets.rlsv6 = NULL; - - err = SetupSocket(&m->p->unicastsockets, zeroIPPort, &zeroAddr, AF_INET); - err = SetupSocket(&m->p->unicastsockets, zeroIPPort, &zeroAddr, AF_INET6); - - m->p->InterfaceList = mDNSNULL; - m->p->userhostlabel.c[0] = 0; - UpdateInterfaceList(m); - SetupActiveInterfaces(m); - - err = WatchForNetworkChanges(m); - if (err) return(err); - - err = WatchForPowerChanges(m); - if (err) return err; - - err = WatchForDNSChanges(m); - - InitDNSConfig(m); - - m->uDNS_info.ServiceRegDomain[0] = '\0'; - m->uDNS_info.NameRegDomain[0] = '\0'; - InitAuthInfo(m); - ReadRegDomainFromConfig(m); - - return(err); - } - -mDNSexport mStatus mDNSPlatformInit(mDNS *const m) - { - mStatus result = mDNSPlatformInit_setup(m); - - // We don't do asynchronous initialization on OS X, so by the time we get here the setup will already - // have succeeded or failed -- so if it succeeded, we should just call mDNSCoreInitComplete() immediately - if (result == mStatus_NoError) mDNSCoreInitComplete(m, mStatus_NoError); - return(result); - } - -mDNSexport void mDNSPlatformClose(mDNS *const m) - { - if (m->p->PowerConnection) - { - CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->PowerRLS, kCFRunLoopDefaultMode); - CFRunLoopSourceInvalidate(m->p->PowerRLS); - CFRelease(m->p->PowerRLS); - IODeregisterForSystemPower(&m->p->PowerNotifier); - m->p->PowerConnection = NULL; - m->p->PowerNotifier = NULL; - m->p->PowerRLS = NULL; - } - - if (m->p->Store) - { - CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode); - CFRunLoopSourceInvalidate(m->p->StoreRLS); - CFRelease(m->p->StoreRLS); - CFRelease(m->p->Store); - m->p->Store = NULL; - m->p->StoreRLS = NULL; - } - - MarkAllInterfacesInactive(m); - ClearInactiveInterfaces(m); - CloseSocketSet(&m->p->unicastsockets); - } - -mDNSexport mDNSs32 mDNSPlatformOneSecond = 1000; - -mDNSexport mStatus mDNSPlatformTimeInit(mDNSs32 *timenow) - { - // Notes: Typical values for mach_timebase_info: - // tbi.numer = 1000 million - // tbi.denom = 33 million - // These are set such that (mach_absolute_time() * numer/denom) gives us nanoseconds; - // numer / denom = nanoseconds per hardware clock tick (e.g. 30); - // denom / numer = hardware clock ticks per nanosecond (e.g. 0.033) - // (denom*1000000) / numer = hardware clock ticks per millisecond (e.g. 33333) - // So: mach_absolute_time() / ((denom*1000000)/numer) = milliseconds - // - // Arithmetic notes: - // tbi.denom is at least 1, and not more than 2^32-1. - // Therefore (tbi.denom * 1000000) is at least one million, but cannot overflow a uint64_t. - // tbi.denom is at least 1, and not more than 2^32-1. - // Therefore clockdivisor should end up being a number roughly in the range 10^3 - 10^9. - // If clockdivisor is less than 10^3 then that means that the native clock frequency is less than 1MHz, - // which is unlikely on any current or future Macintosh. - // If clockdivisor is greater than 10^9 then that means the native clock frequency is greater than 1000GHz. - // When we ship Macs with clock frequencies above 1000GHz, we may have to update this code. - struct mach_timebase_info tbi; - kern_return_t result = mach_timebase_info(&tbi); - if (result != KERN_SUCCESS) return(result); - clockdivisor = ((uint64_t)tbi.denom * 1000000) / tbi.numer; - *timenow = mDNSPlatformTimeNow(); - return(mStatus_NoError); - } - -mDNSexport mDNSs32 mDNSPlatformTimeNow(void) - { - if (clockdivisor == 0) { LogMsg("mDNSPlatformTimeNow called before mDNSPlatformTimeInit"); return(0); } - return((mDNSs32)(mach_absolute_time() / clockdivisor)); - } - -mDNSexport mDNSs32 mDNSPlatformUTC(void) - { - return time(NULL); - } - -// Locking is a no-op here, because we're single-threaded with a CFRunLoop, so we can never interrupt ourselves -mDNSexport void mDNSPlatformLock (const mDNS *const m) { (void)m; } -mDNSexport void mDNSPlatformUnlock (const mDNS *const m) { (void)m; } -mDNSexport void mDNSPlatformStrCopy(const void *src, void *dst) { strcpy((char *)dst, (char *)src); } -mDNSexport mDNSu32 mDNSPlatformStrLen (const void *src) { return(strlen((char*)src)); } -mDNSexport void mDNSPlatformMemCopy(const void *src, void *dst, mDNSu32 len) { memcpy(dst, src, len); } -mDNSexport mDNSBool mDNSPlatformMemSame(const void *src, const void *dst, mDNSu32 len) { return(memcmp(dst, src, len) == 0); } -mDNSexport void mDNSPlatformMemZero( void *dst, mDNSu32 len) { bzero(dst, len); } -mDNSexport void * mDNSPlatformMemAllocate(mDNSu32 len) { return(mallocL("mDNSPlatformMemAllocate", len)); } -mDNSexport void mDNSPlatformMemFree (void *mem) { freeL("mDNSPlatformMemFree", mem); } diff --git a/mDNSMacOSX/CFSocketPuma.c b/mDNSMacOSX/CFSocketPuma.c deleted file mode 100644 index 3a7e266..0000000 --- a/mDNSMacOSX/CFSocketPuma.c +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - * - * This file is not normally used. - * It can be conditionally compiled in by defining RUN_ON_PUMA_WITHOUT_IFADDRS - * in CFSocket.c. It is included mainly as sample code for people building - * for other platforms that (like Puma) lack the getifaddrs() call. - * NOTE: YOU CANNOT use this code to build an mDNSResponder daemon for Puma - * that works just like the Jaguar one, because Puma lacks other necessary - * functionality (like the LibInfo support to receive MIG messages from clients). - - Change History (most recent first): - -$Log: CFSocketPuma.c,v $ -Revision 1.4 2003/08/12 19:56:25 cheshire -Update to APSL 2.0 - -Revision 1.3 2003/07/02 21:19:51 cheshire - Update copyright notices, etc., in source code comments - -Revision 1.2 2002/09/21 20:44:51 zarzycki -Added APSL info - -Revision 1.1 2002/09/17 01:36:23 cheshire -Move Puma support to CFSocketPuma.c - - */ - -#include -#include -#define ifaddrs ifa_info -#ifndef ifa_broadaddr -#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */ -#endif -#include - -/* Our own header for the programs that need interface configuration info. - Include this file, instead of "unp.h". */ - -#define IFA_NAME 16 /* same as IFNAMSIZ in */ -#define IFA_HADDR 8 /* allow for 64-bit EUI-64 in future */ - -struct ifa_info { - char ifa_name[IFA_NAME]; /* interface name, null terminated */ - u_char ifa_haddr[IFA_HADDR]; /* hardware address */ - u_short ifa_hlen; /* #bytes in hardware address: 0, 6, 8 */ - short ifa_flags; /* IFF_xxx constants from */ - short ifa_myflags; /* our own IFI_xxx flags */ - struct sockaddr *ifa_addr; /* primary address */ - struct sockaddr *ifa_brdaddr;/* broadcast address */ - struct sockaddr *ifa_dstaddr;/* destination address */ - struct ifa_info *ifa_next; /* next of these structures */ -}; - -#define IFI_ALIAS 1 /* ifa_addr is an alias */ - - /* function prototypes */ -struct ifa_info *get_ifa_info(int, int); -struct ifa_info *Get_ifa_info(int, int); -void free_ifa_info(struct ifa_info *); - -#define HAVE_SOCKADDR_SA_LEN 1 - -struct ifa_info * -get_ifa_info(int family, int doaliases) -{ - struct ifa_info *ifi, *ifihead, **ifipnext; - int sockfd, len, lastlen, flags, myflags; - char *ptr, *buf, lastname[IFNAMSIZ], *cptr; - struct ifconf ifc; - struct ifreq *ifr, ifrcopy; - struct sockaddr_in *sinptr; - - sockfd = socket(AF_INET, SOCK_DGRAM, 0); - - lastlen = 0; - len = 100 * sizeof(struct ifreq); /* initial buffer size guess */ - for ( ; ; ) { - buf = (char *) malloc(len); - ifc.ifc_len = len; - ifc.ifc_buf = buf; - if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) { - if (errno != EINVAL || lastlen != 0) - debugf("ioctl error"); - } else { - if (ifc.ifc_len == lastlen) - break; /* success, len has not changed */ - lastlen = ifc.ifc_len; - } - len += 10 * sizeof(struct ifreq); /* increment */ - free(buf); - } - ifihead = NULL; - ifipnext = &ifihead; - lastname[0] = 0; -/* end get_ifa_info1 */ - -/* include get_ifa_info2 */ - for (ptr = buf; ptr < buf + ifc.ifc_len; ) { - ifr = (struct ifreq *) ptr; - -#ifdef HAVE_SOCKADDR_SA_LEN - len = MAX(sizeof(struct sockaddr), ifr->ifr_addr.sa_len); -#else - switch (ifr->ifr_addr.sa_family) { -#ifdef IPV6 - case AF_INET6: - len = sizeof(struct sockaddr_in6); - break; -#endif - case AF_INET: - default: - len = sizeof(struct sockaddr); - break; - } -#endif /* HAVE_SOCKADDR_SA_LEN */ - ptr += sizeof(ifr->ifr_name) + len; /* for next one in buffer */ - - if (ifr->ifr_addr.sa_family != family) - continue; /* ignore if not desired address family */ - - myflags = 0; - if ( (cptr = strchr(ifr->ifr_name, ':')) != NULL) - *cptr = 0; /* replace colon will null */ - if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) { - if (doaliases == 0) - continue; /* already processed this interface */ - myflags = IFI_ALIAS; - } - memcpy(lastname, ifr->ifr_name, IFNAMSIZ); - - ifrcopy = *ifr; - ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy); - flags = ifrcopy.ifr_flags; - if ((flags & IFF_UP) == 0) - continue; /* ignore if interface not up */ - - ifi = (struct ifa_info *) calloc(1, sizeof(struct ifa_info)); - *ifipnext = ifi; /* prev points to this new one */ - ifipnext = &ifi->ifa_next; /* pointer to next one goes here */ - - ifi->ifa_flags = flags; /* IFF_xxx values */ - ifi->ifa_myflags = myflags; /* IFI_xxx values */ - memcpy(ifi->ifa_name, ifr->ifr_name, IFA_NAME); - ifi->ifa_name[IFA_NAME-1] = '\0'; -/* end get_ifa_info2 */ -/* include get_ifa_info3 */ - switch (ifr->ifr_addr.sa_family) { - case AF_INET: - sinptr = (struct sockaddr_in *) &ifr->ifr_addr; - if (ifi->ifa_addr == NULL) { - ifi->ifa_addr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in)); - memcpy(ifi->ifa_addr, sinptr, sizeof(struct sockaddr_in)); - -#ifdef SIOCGIFBRDADDR - if (flags & IFF_BROADCAST) { - ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy); - sinptr = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr; - ifi->ifa_brdaddr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in)); - memcpy(ifi->ifa_brdaddr, sinptr, sizeof(struct sockaddr_in)); - } -#endif - -#ifdef SIOCGIFDSTADDR - if (flags & IFF_POINTOPOINT) { - ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy); - sinptr = (struct sockaddr_in *) &ifrcopy.ifr_dstaddr; - ifi->ifa_dstaddr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in)); - memcpy(ifi->ifa_dstaddr, sinptr, sizeof(struct sockaddr_in)); - } -#endif - } - break; - - default: - break; - } - } - free(buf); - return(ifihead); /* pointer to first structure in linked list */ -} -/* end get_ifa_info3 */ - -/* include free_ifa_info */ -mDNSlocal void freeifaddrs(struct ifa_info *ifihead) -{ - struct ifa_info *ifi, *ifinext; - - for (ifi = ifihead; ifi != NULL; ifi = ifinext) { - if (ifi->ifa_addr != NULL) - free(ifi->ifa_addr); - if (ifi->ifa_brdaddr != NULL) - free(ifi->ifa_brdaddr); - if (ifi->ifa_dstaddr != NULL) - free(ifi->ifa_dstaddr); - ifinext = ifi->ifa_next; /* can't fetch ifa_next after free() */ - free(ifi); /* the ifa_info{} itself */ - } -} -/* end free_ifa_info */ - -struct ifa_info * -Get_ifa_info(int family, int doaliases) -{ - struct ifa_info *ifi; - - if ( (ifi = get_ifa_info(family, doaliases)) == NULL) - debugf("get_ifa_info error"); - return(ifi); -} - -mDNSlocal int getifaddrs(struct ifa_info **ifalist) - { - *ifalist = get_ifa_info(PF_INET, false); - if( ifalist == nil ) - return -1; - else - return(0); - } diff --git a/mDNSMacOSX/DNSServiceDiscoveryDefines.h b/mDNSMacOSX/DNSServiceDiscoveryDefines.h index bd5b11b..c2f50c1 100644 --- a/mDNSMacOSX/DNSServiceDiscoveryDefines.h +++ b/mDNSMacOSX/DNSServiceDiscoveryDefines.h @@ -3,8 +3,6 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -25,6 +23,9 @@ Change History (most recent first): $Log: DNSServiceDiscoveryDefines.h,v $ +Revision 1.6 2004/09/20 21:45:27 ksekar +Mach IPC cleanup + Revision 1.5 2003/08/12 19:56:25 cheshire Update to APSL 2.0 @@ -41,5 +42,6 @@ typedef char DNSCString[1024]; typedef char sockaddr_t[128]; typedef const char * record_data_t; +typedef struct { char bytes[4]; } IPPort; #endif /* __DNS_SERVICE_DISCOVERY_DEFINES_H */ diff --git a/mDNSMacOSX/DNSServiceDiscoveryReply.defs b/mDNSMacOSX/DNSServiceDiscoveryReply.defs index 6ae6004..942fb6b 100644 --- a/mDNSMacOSX/DNSServiceDiscoveryReply.defs +++ b/mDNSMacOSX/DNSServiceDiscoveryReply.defs @@ -3,8 +3,6 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in diff --git a/mDNSMacOSX/DNSServiceDiscoveryRequest.defs b/mDNSMacOSX/DNSServiceDiscoveryRequest.defs index a4596af..b6d9cf8 100644 --- a/mDNSMacOSX/DNSServiceDiscoveryRequest.defs +++ b/mDNSMacOSX/DNSServiceDiscoveryRequest.defs @@ -3,8 +3,6 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -36,6 +34,7 @@ import "DNSServiceDiscoveryDefines.h"; type DNSCString = c_string[*:1024]; type record_data = ^ array [] of MACH_MSG_TYPE_BYTE ctype: record_data_t; +type IPPort = struct[4] of char ctype:IPPort; simpleroutine DNSServiceBrowserCreate_rpc( server: mach_port_t; @@ -55,7 +54,7 @@ simpleroutine DNSServiceRegistrationCreate_rpc( in name: DNSCString; in regtype: DNSCString; in domain: DNSCString; - in port: int; + in port: IPPort; in txtRecord: DNSCString); diff --git a/mDNSMacOSX/LegacyNATTraversal.c b/mDNSMacOSX/LegacyNATTraversal.c new file mode 100644 index 0000000..d8f0480 --- /dev/null +++ b/mDNSMacOSX/LegacyNATTraversal.c @@ -0,0 +1,3010 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: LegacyNATTraversal.c,v $ +Revision 1.11 2004/12/03 03:34:20 ksekar + LegacyNATTraversal.c leaks threads + +Revision 1.10 2004/12/01 02:43:49 cheshire +Update copyright message + +Revision 1.9 2004/10/27 02:25:05 cheshire + Random memory smashing bug + +Revision 1.8 2004/10/27 02:17:21 cheshire +Turn off "safe_close: ERROR" error messages -- there are too many of them + +Revision 1.7 2004/10/26 21:15:40 cheshire + Legacy NAT traversal code closes file descriptor 0 +Additional fixes: Code should set fds to -1 after closing sockets. + +Revision 1.6 2004/10/26 20:59:20 cheshire + Legacy NAT traversal code closes file descriptor 0 + +Revision 1.5 2004/10/26 01:01:35 cheshire +Use "#if 0" instead of commenting out code + +Revision 1.4 2004/10/10 06:51:36 cheshire +Declared some strings "const" as appropriate + +Revision 1.3 2004/09/21 23:40:12 ksekar + mDNSResponder to return errors on NAT traversal failure + +Revision 1.2 2004/09/17 01:08:52 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.1 2004/08/18 17:35:41 ksekar +: Feature #9586: Need support for Legacy NAT gateways + + +*/ + +#include "mDNSEmbeddedAPI.h" +#include "mDNSMacOSX.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "memory.h" +#include +#include + +//#include "IPAddr.h" +//#include "upnp.h" +//#include "debug.h" + +// use error codes +//#include "netaddr.h" + +// TODO: remove later and do variable length +#define MAX_SOAPMSGSIZE 65536 + +static int safe_close(int fd) + { + if (fd < 3) { /* LogMsg("safe_close: ERROR sd %d < 3", fd); */ return(-1); } + return(close(fd)); + } + +#define close safe_close + +//////////////////////////////////////////////////////////////////////// +// NetAddr Functions +//////////////////////////////////////////////////////////////////////// + +// Return codes +#define NA_E_SUCCESS (0) +#define NA_E_INTERNAL_ERROR (-1) /* somewhere something wrong */ +#define NA_E_INVALID_PARAMETER (-2) /* bad params */ +#define NA_E_OPERATION_FAILED (-3) /* can't fulfill request */ +#define NA_E_TIMEOUT (-4) /* operation timed out */ +#define NA_E_THREAD_ERROR (-5) /* some error related to threads */ +#define NA_E_PARSE_ERROR (-6) /* a parsing error occured */ +#define NA_E_NOT_READY (-7) /* this op can't proceed yet */ +#define NA_E_NOT_FOUND (-8) /* resource/prereq not found */ +#define NA_E_NOT_AVAILABLE (-9) /* service not available */ +#define NA_E_EXISTS (-10) /* can't modify existing item */ +#define NA_E_AGAIN (-11) /* something wrong - try again */ +#define NA_E_NOT_SUPPORTED (-12) /* wait until next version */ +#define NA_E_ABORT (-14) /* operation aborted */ +#define NA_E_NET (-15) /* network layer problem */ + +// Logging flags - log types (increasing degree of detail) +#define NALOG_ERROR (1UL) /* error messages */ +#define NALOG_ALERT (2UL) /* useful warning/alerts */ +#define NALOG_INFO0 (4UL) /* info - potential problem */ +#define NALOG_INFO1 (8UL) /* extra info */ +#define NALOG_DUMP (16UL) /* data dumps */ + +#define NALOG_RSRV1 (32UL) /* reserved */ +#define NALOG_RSRV2 (64UL) /* reserved */ +#define NALOG_RSRV3 (128UL) /* reserved */ + +// Logging flags - component (not used for now) +#define NALOG_UPNP (256) /* UPnP */ + +// Default Logging levels +#define NALOG_LEVEL0 (0) +#define NALOG_LEVEL1 (NALOG_UPNP | NALOG_ERROR) +#define NALOG_LEVEL2 (NALOG_LEVEL1 | NALOG_ALERT) +#define NALOG_LEVEL3 (NALOG_LEVEL2 | NALOG_INFO0) +#define NALOG_LEVEL4 (NALOG_LEVEL3 | NALOG_INFO1) +#define NALOG_LEVEL5 (NALOG_LEVEL4 | NALOG_DUMP) +#define NALOG_DEFAULT_LEVEL (NALOG_LEVEL2) + +// Default timeout values (in m-seconds (milli)) +// 50 milliseconds for function timeout +#define NA_DEFAULT_FUNCTION_TIMEOUT (50) + +//////////////////////////////////////////////////////////////////////// +// GLOBAL Defines +//////////////////////////////////////////////////////////////////////// +#define SSDP_IP "239.255.255.250" +#define SSDP_PORT 1900 +#define SSDP_TTL 4 + +#define CRLF "\r\n" +#define H_CRLF "\r\n" +// SOAP message's CRLF: +//#define S_CRLF "\r\n" +#define S_CRLF + +// standard 200 ok msg +#define HTTP200OK "HTTP/1.1 200 OK\r\n\r\n" +#define HTTP200OKLEN (sizeof(HTTP200OK) - 1) + +// maximum time to wait for an event (in microseconds) +#define MAX_EXPECTEVENTTIME (10000) + +//////////////////////////////////////////////////////////////////////// +// GLOBAL Data Types +//////////////////////////////////////////////////////////////////////// +typedef struct tagProperty { + char *pszName; + char *pszValue; + char *pszType; +} Property, *PProperty; + +typedef struct tagHTTPResponse { + char *pszStatus; + char *pszReason; + int iNumHeaders; + Property aHeaders[30]; // assume at most this many headers + char *pszBody; + + // for admin use + int fFree; + char *buf; +} HTTPResponse, *PHTTPResponse, **PPHTTPResponse; + +//////////////////////////////////////////////////////////////////////// +// GLOBAL Constants +//////////////////////////////////////////////////////////////////////// +static const char szSSDPMsgDiscoverRoot[] = + "M-SEARCH * HTTP/1.1\r\n" + "Host:239.255.255.250:1900\r\n" + "ST:upnp:rootdevice\r\n" + "Man:\"ssdp:discover\"\r\n" + "MX:3\r\n" + "\r\n"; + +static const char szSSDPMsgDiscoverIGD[] = + "M-SEARCH * HTTP/1.1\r\n" + "Host:239.255.255.250:1900\r\n" + "ST:urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n" + "Man:\"ssdp:discover\"\r\n" + "MX:3\r\n" + "\r\n"; + +static const char szSSDPMsgDiscoverNAT[] = + "M-SEARCH * HTTP/1.1\r\n" + "Host:239.255.255.250:1900\r\n" + "ST:urn:schemas-upnp-org:service:WANIPConnection:1\r\n" + "Man:\"ssdp:discover\"\r\n" + "MX:3\r\n" + "\r\n"; + +//// Subscribe message +// 1$s: control URL +// 2$s: local's host/port ("host:port") +// 3$s: router's host/port ("host:port") +// 4$d: subscription timeout in seconds +static const char szEventMsgSubscribeFMT[] = + "SUBSCRIBE %1$s HTTP/1.1\r\n" + "NT: upnp:event\r\n" + "Callback: \r\n" + "Timeout: Second-%4$d\r\n" + "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n" + "Host: %3$s\r\n" + "Content-Length: 0\r\n" + "Pragma: no-cache\r\n" + "\r\n"; + +//// Unsubscribe message +// 1$s: control URL +// 2$s: SID (some uuid passed back during subscribe) +// 3$s: router's host ("host") +#if 0 +static const char szEventMsgUnsubscribeFMT[] = + "UNSUBSCRIBE %1$s HTTP/1.1\r\n" + "SID: %2$s\r\n" + "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n" + "Host: %3$s\r\n" + "Content-Length: 0\r\n" + "Pragma: no-cache\r\n" + "\r\n"; +#endif + +//// Generic SOAP Control:Action request messages +// 1$s: control URL +// 2$s: router's host/port ("host:port") +// 3$s: action (string) +// 4$d: content-length +static const char szSOAPMsgControlAHeaderFMT[] = + //"M-POST %1$s HTTP/1.1\r\n" + "POST %1$s HTTP/1.1\r\n" + "Content-Type: text/xml; charset=\"utf-8\"\r\n" + //"TEST: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n" + //"Man: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n" + //"01-SOAPAction: \"urn:schemas-upnp-org:service:WANIPConnection:1#%3$s\"\r\n" + "SOAPAction: \"urn:schemas-upnp-org:service:WANIPConnection:1#%3$s\"\r\n" + "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows 9x)\r\n" + "Host: %2$s\r\n" + "Content-Length: %4$d\r\n" + "Connection: close\r\n" +// "Connection: Keep-Alive\r\n" + "Pragma: no-cache\r\n" + "\r\n"; + +// 1$: action (string) +// 2$: argument list +static const char szSOAPMsgControlABodyFMT[] = + "" CRLF + "" S_CRLF + "" S_CRLF + "" S_CRLF + "%2$s" + "" S_CRLF + "" S_CRLF + "" S_CRLF +// CRLF +// "0" +// CRLF + CRLF; + +// 1$: argument name +// 2$: argument value +static const char szSOAPMsgControlAArgumentFMT[] = + "<%1$s>%2$s" S_CRLF; + +// 1$: argument name +// 2$: argument value +// 3$: argument type +static const char szSOAPMsgControlAArgumentFMT_t[] = + "<%1$s" + " xmlns:dt=\"urn:schemas-microsoft-com:datatypes\"" + " dt:dt=\"%3$s\">%2$s" S_CRLF; + +#if 0 +//// Generic SOAP Control:Query request messages +// 1$s: control URL +// 2$s: router's host/port ("host:port") +// 3$d: content-length +static const char szSOAPMsgControlQHeaderFMT[] = + "M-POST %1$s HTTP/1.1\r\n" + //"POST %1$s HTTP/1.1\r\n" + "Host: %2$s\r\n" + "Content-Length: %3$d\r\n" + "Content-Type: text/xml; charset-\"utf-8\"\r\n" + //"Man: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n" + //"SOAPAction: \"urn:schemas-upnp-org:control-1-0#QueryStateVariable\"\r\n" + "01-SOAPAction: \"urn:schemas-upnp-org:control-1-0#QueryStateVariable\"\r\n" + "\r\n"; + +// 1$: variable name +static const char szSOAPMsgControlQBodyFMT[] = + "" S_CRLF + "" S_CRLF + "%s" S_CRLF + "" S_CRLF + "" S_CRLF + "" S_CRLF + "" S_CRLF; +#endif +// 1$: device description URL +// 2$: host/port +static const char szSSDPMsgDescribeDeviceFMT[] = + "GET %s HTTP/1.1\r\n" + "Accept: text/xml, application/xml\r\n" + "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n" + "Host: %s\r\n" + "Connection: close\r\n" +// "Connection: Keep-Alive\r\n" + "\r\n"; + +//////////////////////////////////////////////////////////////////////// +// GLOBAL Variables +//////////////////////////////////////////////////////////////////////// + +static int g_fFirstInit = TRUE; +static int g_fQuit = FALSE; +static FILE *g_log; +static int g_fLogging; + +// Globally-accessible UDP socket +static int g_sUDP = -1; +static int g_sUDPCancel = -1; + +// Globally-accessible TCP socket +static int g_sTCP = -1; +static int g_sTCPCancel = -1; + +// Event Vars +static int g_fEventEnabled = FALSE; +static unsigned short g_wEventPort; +static struct sockaddr_in g_saddrRouterEvent; +static char g_szRouterHostPortEvent[1024]; +static char g_szEventURL[1024]; + +// UPnP Router info +static char g_szFriendlyName[1024]; +static char g_szManufacturer[1024]; +static char g_szModelName[1024]; +static char g_szModelDescription[1024]; + +// URL base +static struct sockaddr_in g_saddrRouterBase; +static char g_szRouterHostPortBase[1024]; + +// the threads +static pthread_t g_UDPthread = NULL; +static pthread_t g_TCPthread = NULL; + +// Local IP +static unsigned long g_dwLocalIP = 0; + +// Globally accessible info about the router/UPnP +static int g_fUPnPEnabled = FALSE; +static char g_szUSN[1024]; + +static struct sockaddr_in g_saddrRouterDesc; +static char g_szRouterHostPortDesc[1024]; +static char g_szNATDevDescURL[1024]; + +static struct sockaddr_in g_saddrRouterSOAP; +static char g_szRouterHostPortSOAP[1024]; +static char g_szControlURL[1024]; +static int g_fControlURLSet = FALSE; + +// Lock/condvar for synchronous upnp calls +static pthread_mutex_t g_xUPnP; +static pthread_mutex_t g_xUPnPMsg; +static pthread_cond_t g_condUPnP; +static pthread_cond_t g_condUPnPControlURL; +static struct timeval g_tvUPnPInitTime; +static struct timeval g_tvLastUpdateTime; + +// timeout values in seconds +static int g_iFunctionTimeout = NA_DEFAULT_FUNCTION_TIMEOUT; + +static void GetDeviceDescription(void); +static void SetLocalIP(void); + +//////////////////////////////////////////////////////////////////////// +// IPAddr Functions +//////////////////////////////////////////////////////////////////////// + + +#define ISIPV6 0x01 +#define ISPPP 0x02 +#define IFNAMELEN 16 /* Interface Name Length */ +#define IPLEN 16 /* 16 bytes(128 bits) for IPv6 */ + +typedef struct tagIPINFO +{ + int iFlags; + char szIfName[IFNAMELEN]; /* Interface name */ + unsigned char abIP[IPLEN]; /* IP in host byte order */ + unsigned short wPort; +} IPINFO, *PIPINFO, **PPIPINFO; + +typedef struct hostent HOSTENT, *PHOSTENT; + +static unsigned long GetNATIPNetmask(unsigned long dwIP) +{ + if ((dwIP & 0xFF000000) == 0x0A000000) return 0xFF000000; + if ((dwIP & 0xFFF00000) == 0xAC100000) return 0xFFF00000; + if ((dwIP & 0xFFFF0000) == 0xC0a80000) return 0xFFFF0000; + + return 0; /* No NAT IP */ +} + +static int GetIPInfo(PPIPINFO ppIPInfo) +{ + int fd; + int iLastLen, iLen, iNum = 0, iMax = 0; + unsigned long dwIP; + char *pcBuf, *pcTemp; + PIPINFO pIPInfo = NULL; + struct ifconf ifc; + struct ifreq *ifr, ifrcopy; + + if (ppIPInfo == NULL) return 0; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + + iLastLen = -1; + iLen = 100 * sizeof(struct ifreq); + + for (;;) + { + pcBuf = (char *)malloc(iLen); + ifc.ifc_len = iLen; + ifc.ifc_buf = pcBuf; + + if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) + { + if (errno != EINVAL || iLastLen != -1) + { +// DbgPrint(ELL_ERROR, "ioctl failed(%d)\n", errno); + free(pcBuf); + close(fd); + return 0; + } + } + else + { + if (ifc.ifc_len == iLastLen) break; + iLastLen = ifc.ifc_len; + } + + iLen += 10 * sizeof(struct ifreq); + free(pcBuf); + } + + for (pcTemp = pcBuf; pcTemp < pcBuf + ifc.ifc_len; ) + { + if (iNum >= iMax) + { + PIPINFO pIPInfoNew; + + iMax += 10; + pIPInfoNew = (PIPINFO)realloc(pIPInfo, sizeof(IPINFO) * iMax); + if (pIPInfoNew == NULL) + { + free(pIPInfo); + free(pcBuf); + close(fd); + return 0; + } + else pIPInfo = pIPInfoNew; + + memset(pIPInfo + (iMax - 10), 0, sizeof(IPINFO) * 10); + } + + ifr = (struct ifreq *)pcTemp; + + pcTemp += sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len; + + /* discard invalid address families & loopback */ + if ((ifr->ifr_addr.sa_family != AF_INET && + ifr->ifr_addr.sa_family != AF_INET6) || + strncmp(ifr->ifr_name, "lo", 2) == 0) continue; + + ifrcopy = *ifr; + ioctl(fd, SIOCGIFFLAGS, &ifrcopy); + if ((ifrcopy.ifr_flags & IFF_UP) == 0) continue; + + switch (ifr->ifr_addr.sa_family) + { + case AF_INET: + memcpy(pIPInfo[iNum].szIfName, ifr->ifr_name, IFNAMELEN); + dwIP = + ntohl(((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr.s_addr); + memcpy(pIPInfo[iNum].abIP, &dwIP, sizeof(unsigned long)); + if (ifrcopy.ifr_flags & IFF_POINTOPOINT) + pIPInfo[iNum].iFlags |= ISPPP; + iNum++; + break; + + case AF_INET6: + memcpy(pIPInfo[iNum].szIfName, ifr->ifr_name, IFNAMELEN); + memcpy(pIPInfo[iNum].abIP, + ((struct sockaddr_in6 *)&(ifr->ifr_addr))-> sin6_addr.s6_addr, + 16); + pIPInfo[iNum].iFlags |= ISIPV6; + if (ifrcopy.ifr_flags & IFF_POINTOPOINT) + pIPInfo[iNum].iFlags |= ISPPP; + iNum++; + break; + + default: + break; + } + } + + free(pcBuf); + close(fd); + + *ppIPInfo = pIPInfo; + + return iNum; +} + +static void FreeIPInfo(PIPINFO pIPInfo) +{ + if (pIPInfo != NULL) free(pIPInfo); +} + + +//////////////////////////////////////////////////////////////////////// +// Function Definitions +//////////////////////////////////////////////////////////////////////// + +static void SendDiscoveryMsg(); + +// SSDPListen +// Creates a UDP multicast socket and listens to the SSDP IP/PORT +// Returns +// -1 on error, or the socket descriptor if success +static int SSDPListen() +{ + char fLoop; + int iTTL; + struct ip_mreq mreq; + struct sockaddr_in saddr; + int sd; + + // IPPROTO_IP == 0; IPPROTO_TCP == 6; IPPROTO_UDP == 17; etc. + sd = socket(AF_INET, SOCK_DGRAM, 0); + if (sd == -1) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "Can't create socket! SSDPListen exiting\n"); + return NA_E_NET; + } + + // sock options values + fLoop = 0; // false - don't send copy to self + iTTL = SSDP_TTL; + + // bind to listen to ssdp multicast address + bzero(&saddr, sizeof(saddr)); + saddr.sin_len = sizeof(saddr); + saddr.sin_family = AF_INET; + //saddr.sin_addr.s_addr = inet_addr(SSDP_IP); + //saddr.sin_port = htons(SSDP_PORT); + saddr.sin_addr.s_addr = htonl(g_dwLocalIP); + saddr.sin_port = 0; + + // and set the multicast add_member structure + // (TODO: need to find interfaces later - ioctl, with: + // SIOCFIFCONF to find if's, SIOCGIFADDR to get addr, and SIOCFIFFLAGS + // to check for IFF_MULTICAST flag for multicast support on an if) + bzero(&mreq, sizeof(mreq)); + mreq.imr_interface.s_addr = g_dwLocalIP; + mreq.imr_multiaddr.s_addr = inet_addr(SSDP_IP); + + if ( + bind(sd, (struct sockaddr *)&saddr, sizeof(saddr)) //|| + //setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, &fLoop, sizeof(fLoop)) || + //setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &iTTL, sizeof(iTTL)) || + //setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) + ) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, + "bind/setsockopt for multicast failed... errno = %d\n", errno); + close(sd); + return NA_E_NET; + } + + return sd; +} + +static int EventListen() +{ + struct sockaddr_in saddr; + int sd; + + // try 5 ports before failing completely + for (g_wEventPort = 5000; g_wEventPort < 5005; g_wEventPort++) + { + sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sd == -1) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "Can't create socket! EventListen exiting\n"); + return NA_E_NET; + } + + bzero(&saddr, sizeof(saddr)); + saddr.sin_len = sizeof(saddr); + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = htonl(g_dwLocalIP); + saddr.sin_port = htons(g_wEventPort); + + // return if okay + if (bind(sd, (struct sockaddr *)&saddr, sizeof(saddr)) == 0) + { + listen(sd, 128); + ////TracePrint(ELL_TRACE, "UPnP: EventListen @%u\n", g_wEventPort); + return sd; + } + + // unsuccessful - close sd and try again + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, + "bind TCP port %u failed: errno = %d\n", g_wEventPort, errno); + close(sd); + } + + return NA_E_NET; +} + +static void *TCPProc(void *in); + +static int EventInit() +{ + int iRet; + pthread_attr_t attr; + + if (g_fEventEnabled == FALSE) + { + // initialize TCP socket for Eventing + g_sTCP = EventListen(); + if (g_sTCP < 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "EventInit - Failed to init tcp socket.\n"); + return NA_E_INTERNAL_ERROR; + } + + // make TCP thread + pthread_attr_init(&attr); + iRet = pthread_create(&g_TCPthread, &attr, TCPProc, 0); + if (iRet != 0) { + close(g_sTCP); + g_sTCP = -1; + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "EventInit: TCPProc create failed(%d)\n", iRet); + return NA_E_THREAD_ERROR; + } + } + + g_fEventEnabled = TRUE; + + return NA_E_SUCCESS; +} + +static void DumpHex(char *buf, int len) +{ + int i; + int nexti; + int j; + int endj; + + if (g_fLogging & NALOG_DUMP) { + if (buf == NULL) return; + if (len <= 0) return; + + for (i = 0; i < len; i = nexti) { + fprintf(g_log, "%04x: ", i); + nexti = i + 16; + endj = (nexti > len) ? len : nexti; + for (j = i; j < endj; j++) + fprintf(g_log, "%02x ", buf[j] & 0xff); + if (j == len) { + if ((j % 16) != 0) { + char pad[3 * 16 + 1]; // don't need the last 3 bytes anyway + j = (16 - (j % 16)) * 3; + memset(pad, ' ', j); + pad[j] = '\0'; + fputs(pad, g_log); + } + } + for (j = i; j < endj; j++) + isprint(buf[j]) ? fputc(buf[j], g_log) : fputc('.', g_log); + fputc('\n', g_log); + } + + } +} + +// FindHTTPHeaderNewLine +// Returns a pointer to the beginning of a CRLF, that is not a +// part of LWS. (LWS is CRLF followed by a space or tab, and in +// HTTP, considered as equivalent to a single space) (LWS stands +// for "linear white space") +// Returns a pointer the beginning of CRLF, and sets the EOH flag to +// whether this is the last header in the HTTP header section. +// Also, if pbuf is NULL, or if there isn't any CRLF found in the +// string, or if the HTTP syntax is wrong, NULL is returned, and +// the EOH flag is not touched. +static char *FindHTTPHeaderNewLine(char *pbuf, int iBufSize, int *pfEOH) +{ + char *result; + int i = 0; + + if (pbuf == NULL) return NULL; + + for (;;) { + result = memchr(pbuf, '\r', iBufSize); + if (result == NULL) { + if (g_fLogging & NALOG_INFO0) { + fprintf(g_log, "FindHTTPHeaderNewLine: er @(%d)\n", i); + fflush(g_log); + } + return NULL; + } + i++; // count chars + + // decrement iBufSize, and move pbuf forward + iBufSize -= (result - pbuf); + pbuf = result; + + ++pbuf; // now pointing right after "\r" + --iBufSize; + if (*pbuf == '\0') break; + if (*pbuf != '\n') continue; + + ++pbuf; // now pointing after "\r\n" + --iBufSize; + if (*pbuf == '\0') break; + if ((*pbuf == ' ') || (*pbuf == '\t')) continue; + + // at this point we know we're at the end of a header field, + // and there's more stuff coming... + + // just need to check if this is the last header + if ((pbuf[0] == '\r') && (pbuf[1] == '\n')) + *pfEOH = TRUE; + else + *pfEOH = FALSE; + + return result; + } + + return NULL; +} + +// NewHTTPResponse_sz +// Creates an HTTPResponse structure from a string (sz). Set +// fDestroyOriginal to TRUE if the buffer passed in can be overwritten. +// Otherwise, NewHTTPResponse_sz will duplicate the buffer. +// Returns the created HTTPResponse structure if successful, or if an +// error occured (out of memory, or bad http syntax), returns NULL. +// NOTE: ALWAYS call DeleteHTTPResponse after using the HTTPResponse structure. +// NOTE: The input is assumed to be correct. If there're HTTP syntax errors, +// and the pszHTTPResponse is not null-terminated, result may be undefined. +// (to be fixed next version) +static PHTTPResponse NewHTTPResponse_sz( + char *pszHTTPResponse, + int iBufferSize, + int fDestroyOriginal) +{ + PHTTPResponse pResponse; + int fEOH; + char *pszEOL; + int iNumHeaders; + char *pBuf; + + if ((pResponse = (PHTTPResponse)malloc(sizeof(HTTPResponse))) == NULL) { + if (g_fLogging & NALOG_INFO0) { + fprintf(g_log, "NewHTTPResponse_sz: er 1\n"); + fflush(g_log); + } + return NULL; + } + + // make copy of buffer now + if (fDestroyOriginal) { + pResponse->buf = NULL; + pBuf = pszHTTPResponse; + } + else { + int len = strlen(pszHTTPResponse); + if ((len+1) > iBufferSize) { + if (g_fLogging & NALOG_INFO0) + fprintf(g_log, "Length: %d > %d\n", len+1, iBufferSize); + iBufferSize = len+1; + } + if ((pResponse->buf = (char *)malloc(iBufferSize)) == NULL) { + free(pResponse); + if (g_fLogging & NALOG_INFO0) { + fprintf(g_log, "NewHTTPResponse_sz: er 2\n"); + fflush(g_log); + } + return NULL; + } + memcpy(pResponse->buf, pszHTTPResponse, iBufferSize); + pBuf = pResponse->buf; + } + + // get the first line + pszEOL = FindHTTPHeaderNewLine(pBuf, iBufferSize, &fEOH); + if (pszEOL == NULL) { + if (g_fLogging & NALOG_INFO0) { + fprintf(g_log, "NewHTTPResponse_sz: er 3\n"); + fflush(g_log); + } + goto cleanup; + } + + *pszEOL = '\0'; // terminate the status line + pszEOL += 2; // point to the rest of the buffer + + // set the status string first + pResponse->pszStatus = strchr(pBuf, ' '); + if (pResponse->pszStatus == NULL) { + if (g_fLogging & NALOG_INFO0) { + fprintf(g_log, "NewHTTPResponse_sz: er 4\n"); + fflush(g_log); + } + goto cleanup; // syntax error + } + + pResponse->pszStatus++; // point to the actual status + + pResponse->pszReason = strchr(pResponse->pszStatus, ' '); + if (pResponse->pszReason == NULL) { + if (g_fLogging & NALOG_INFO0) { + fprintf(g_log, "NewHTTPResponse_sz: er 5\n"); + fflush(g_log); + } + goto cleanup; // syntax error + } + + pResponse->pszReason[0] = '\0'; // terminate status string + pResponse->pszReason++; // point to the reason string + + iNumHeaders = 0; // initialize to 0 headers + + // parse header fields line by line (while not end of headers) + while (!fEOH) { + PProperty pHeader = &(pResponse->aHeaders[iNumHeaders]); + // point header field name to the first char of the line + pHeader->pszName = pszEOL; + + // search for the end of line + pszEOL = FindHTTPHeaderNewLine(pszEOL, + iBufferSize - (pszEOL - pBuf), // remainder size + &fEOH); + if (pszEOL == NULL) goto cleanup; // syntax error + + *pszEOL = '\0'; // terminate this string + pszEOL += 2; // point to beginning of next line + + pHeader->pszValue = strchr(pHeader->pszName, ':'); + if (pHeader->pszValue == NULL) { + if (g_fLogging & NALOG_INFO0) { + fprintf(g_log, "NewHTTPResponse_sz: er 6\n"); + fflush(g_log); + } + goto cleanup; // syntax error (header field has no ":") + } + + pHeader->pszValue[0] = '\0'; // terminate the header name string + pHeader->pszValue++; // point after the ":" + // get rid of leading spaces for the value part + while ( + (pHeader->pszValue[0] == ' ') || + (pHeader->pszValue[0] == '\t') || + (pHeader->pszValue[0] == '\r') || + (pHeader->pszValue[0] == '\n') + ) { + pHeader->pszValue++; // skip the space + } + + iNumHeaders++; // added one more header + pHeader++; // point to the next header in pResponse->aHeaders + } + + pResponse->iNumHeaders = iNumHeaders; // remember to set it in pResponse + + pResponse->pszBody = pszEOL + 2; // point after the empty line + + return pResponse; + +cleanup: + if (pResponse->buf != NULL) free(pResponse->buf); + free(pResponse); + return NULL; +} + +// DeleteHTTPResponse +// Deallocates stuff in the HTTPResponse structure, effectively returning +// memory to the system and destroying the structure. +// NOTE: The pointer pResponse WILL BE FREED, and will be unusable after +// the call to DeleteHTTPResponse. +static void DeleteHTTPResponse(PHTTPResponse pResponse) +{ +// int i; + + if (pResponse == NULL) return; + + // Current impl is just simple array - no need to free() + //for (i = 0; i < pResponse->iNumHeaders; i++) { + // free(pResponse->aHeaders[i]); + //} + + if (pResponse->buf != NULL) + free(pResponse->buf); + free(pResponse); +} + +//typedef struct tagHTTPResponse { +// char *pszStatus; +// char *pszReason; +// int iNumHeaders; +// Property aHeaders[30]; // assume at most this many headers +// char *pszBody; +// +// // for admin use +// int fFree; +// char *buf; +//} HTTPResponse, *PHTTPResponse, **PPHTTPResponse; + +static void PrintHTTPResponse(PHTTPResponse pResponse) +{ + int i; + + if (g_fLogging & (NALOG_INFO1)) { + if (pResponse == NULL) return; + fprintf(g_log, " *** HTTP response begin *** \n"); + fprintf(g_log, " * status = [%s], reason = [%s] *\n", + pResponse->pszStatus, pResponse->pszReason); + for (i = 0; i < pResponse->iNumHeaders; i++) { + fprintf(g_log, " * Header \"%s\" = [%s]\n", + pResponse->aHeaders[i].pszName, + pResponse->aHeaders[i].pszValue); + } + if (g_fLogging & NALOG_DUMP) + fprintf(g_log, " * body = [%s] *\n", pResponse->pszBody); + fprintf(g_log, " *** HTTP response end *** \n"); + } +} + +static int DiscoverRouter(PHTTPResponse pResponse) +{ + int i; + int fLocation = FALSE; + int fUSN = FALSE; + int fIsNATDevice = FALSE; + +#if 0 + if (strcmp(pResponse->pszStatus, "200") != 0) + return -1; +#endif + + if (pResponse == NULL) { + if (g_fLogging & NALOG_INFO0) + fprintf(g_log, "DiscoverRouter: pResponse == NULL\n"); + return -1; + } + + // check to see if this is a relevant packet + for (i = 0; i < pResponse->iNumHeaders; i++) { + PProperty pHeader = &(pResponse->aHeaders[i]); + + if ((strcasecmp(pHeader->pszName, "ST") == 0) || + (strcasecmp(pHeader->pszName, "NT") == 0)) { + if ((strcmp(pHeader->pszValue, + "urn:schemas-upnp-org:service:WANIPConnection:1") == 0) || + (strcmp(pHeader->pszValue, + "urn:schemas-upnp-org:device:InternetGatewayDevice:1") == 0)) { + fIsNATDevice = TRUE; + } + } + } + + // leave the message alone if we don't need it + if (!fIsNATDevice) + return -1; + + // Now that we know we're looking at the message about the NAT device: + pthread_mutex_lock(&g_xUPnP); + + // set upnp to be unconfigured for now + g_fUPnPEnabled = FALSE; + + // loop through the headers + for (i = 0; i < pResponse->iNumHeaders; i++) { + PProperty pHeader = &(pResponse->aHeaders[i]); + + if (strcasecmp(pHeader->pszName, "Location") == 0) { + char *p; + char *q; + + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "Checking Location...\n"); + p = pHeader->pszValue; + if (strncmp(p, "http://", 7) != 0) + continue; // hope for another Location header to correct it + p += 7; // skip over "http://" + q = strchr(p, '/'); + + // set the control URL first + if (q == NULL) { + g_szNATDevDescURL[0] = '/'; + g_szNATDevDescURL[1] = '\0'; + } + else { + strncpy(g_szNATDevDescURL, q, sizeof(g_szNATDevDescURL) - 1); + g_szNATDevDescURL[sizeof(g_szNATDevDescURL) - 1] = '\0'; + // terminate the host/port string + *q = '\0'; + } + + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, " Device Description URL set to[%s]...\n", + g_szNATDevDescURL); + + // see if port is specified + q = strchr(p, ':'); + if (q == NULL) { + sprintf(g_szRouterHostPortDesc, "%s", p); + + g_saddrRouterDesc.sin_addr.s_addr = inet_addr(p); + g_saddrRouterDesc.sin_port = htons(80); + } + else { + // don't include the ":80" - HTTP is by default port 80 + if (atoi(q+1) == 80) *q = '\0'; + + strcpy(g_szRouterHostPortDesc, p); + + // terminate the host part and point to it + *q = '\0'; + q++; + + g_saddrRouterDesc.sin_addr.s_addr = inet_addr(p); + g_saddrRouterDesc.sin_port = htons(atoi(q)); + } + + g_saddrRouterDesc.sin_family = AF_INET; + + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, " Router Address set to[%s]...\n", + g_szRouterHostPortDesc); + fLocation = TRUE; + } + else if (strcasecmp(pHeader->pszName, "USN") == 0) { + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "Checking USN...\n"); + strncpy(g_szUSN, pHeader->pszValue, sizeof(g_szUSN) - 1); + g_szUSN[sizeof(g_szUSN) - 1] = '\0'; + fUSN = TRUE; + } + else { + ; // do nothing for other headers for now + } + } + + // now check flags and set enabled if all set + if (fLocation && fUSN) { + if (g_fLogging & NALOG_INFO1) { + fprintf(g_log, + "Description Host/port string: [%s]\n" + "NATDevDescURL: [%s], USN: [%s]\n", + g_szRouterHostPortDesc, + g_szNATDevDescURL, g_szUSN); + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "Got router information\n"); + } + + g_fUPnPEnabled = TRUE; + pthread_cond_broadcast(&g_condUPnP); + } + + // remember to unlock before return + pthread_mutex_unlock(&g_xUPnP); + + return 0; +} + +// granularity is specified as: granularity = 1/nth seconds +#define UPNP_TIMEOUT_GRANULARITY (1000) +#define U_TOGRAN UPNP_TIMEOUT_GRANULARITY + +// result = a - b +static void TimevalSubtract( + struct timeval *result, + const struct timeval *a, + const struct timeval *b) +{ + result->tv_sec = a->tv_sec - b->tv_sec; + + if (b->tv_usec > a->tv_usec) { + result->tv_sec--; + result->tv_usec = 1000000 + a->tv_usec - b->tv_usec; + } + else + result->tv_usec = a->tv_usec - b->tv_usec; +} + +// elapsed = end - start +static void GetTimeElapsed( + const struct timeval *tv_start, + const struct timeval *tv_end, + struct timeval *tv_elapsed) +{ + TimevalSubtract(tv_elapsed, tv_end, tv_start); +#if 0 + tv_elapsed->tv_sec = tv_end->tv_sec - tv_start->tv_sec; + + if (tv_start->tv_usec > tv_end->tv_usec) { + tv_elapsed->tv_sec--; + tv_elapsed->tv_usec = 1000000 + tv_end->tv_usec - tv_start->tv_usec; + } + else + tv_elapsed->tv_usec = tv_end->tv_usec - tv_start->tv_usec; +#endif +} + +// returns +1, 0, or -1, if a>b, a==b, atv_sec == b->tv_sec) && + (a->tv_usec == b->tv_usec)) return 0; + + if (a->tv_sec > b->tv_sec) return 1; + else if (a->tv_sec < b->tv_sec) return -1; + + // if seconds are equal... + if (a->tv_usec > b->tv_usec) return 1; + else return -1; +} + +static int WaitControlURLSet(double timeout) +{ + struct timespec ts; + struct timeval tv; + struct timeval tv_start; + int iRet; + long to_sec = (int) (timeout / U_TOGRAN); + long to_usec = + (int) (((timeout / U_TOGRAN) - to_sec) * 1000000.0); + //long to_sec = (int) timeout; + //long to_usec = (int) ((timeout - to_sec) * 1000000.0); + struct timeval elapsed; + + // get function start time + gettimeofday(&tv_start, NULL); + + pthread_mutex_lock(&g_xUPnP); + +#if 0 + // if last update is too long ago then wait for it + GetTimeElapsed(&g_tvLastUpdateTime, &tv_start, &elapsed); + if ((elapsed.tv_sec + (elapsed.tv_usec / 1000000.0)) > + (((double) g_iUPnPTimeout) / U_TOGRAN)) + g_fControlURLSet = 0; +#endif + + while (!g_fControlURLSet) { + // get current time + gettimeofday(&tv, NULL); + +#if 0 +for now ignore device timeout + // see if we've past the device's timeout first + GetTimeElapsed(&g_tvUPnPInitTime, &tv, &elapsed); + if ((elapsed.tv_sec > g_timeout_sec) || + ( (elapsed.tv_sec == g_timeout_sec) && + (elapsed.tv_usec > g_timeout_usec) + )) + { + pthread_mutex_unlock(&g_xUPnP); + return FALSE; + } +#endif + + // calculate ts to sleep till + ts.tv_sec = tv.tv_sec + to_sec; + ts.tv_nsec = (tv.tv_usec + to_usec) * 1000; + if (ts.tv_nsec > 1000000000) { + ts.tv_nsec -= 1000000000; + ts.tv_sec += 1; + } + + // now get how long we've been in this function already and deduct + GetTimeElapsed(&tv_start, &tv, &elapsed); + ts.tv_sec -= elapsed.tv_sec; + if (ts.tv_nsec < (elapsed.tv_usec * 1000)) { + ts.tv_sec--; + ts.tv_nsec = 1000000000 + ts.tv_nsec - (elapsed.tv_usec * 1000); + } + else { + ts.tv_nsec -= (elapsed.tv_usec * 1000); + } + + iRet = pthread_cond_timedwait(&g_condUPnPControlURL, &g_xUPnP, &ts); + + // if timeout then return false + if (iRet != 0) + { + pthread_mutex_unlock(&g_xUPnP); + return FALSE; + } + } + pthread_mutex_unlock(&g_xUPnP); + + return TRUE; +} + +static int WaitUPnPFunction() +{ + struct timeval start; +// struct timeval end; + double wait2; +// struct timeval elapsed; + + gettimeofday(&start, NULL); + + wait2 = (double)g_iFunctionTimeout; + + WaitControlURLSet(wait2); + +//gettimeofday(&end, NULL); +//GetTimeElapsed(&start, &end, &elapsed); +//fprintf(stderr, "== wait2: (%f) %d.%06d\n", +// wait2/U_TOGRAN, elapsed.tv_sec, elapsed.tv_usec); + + return g_fControlURLSet; +} + +static void SetLocalIP(); + +static int SendTCPMsg_saddr_parse( + char *msg, int iLen, + char *result, int resultSize, + struct sockaddr_in *saHost); + +static void *TCPProc(void *in) +{ + int iRet; + unsigned char buf[MAX_SOAPMSGSIZE]; + int iBufLen; + + (void)in; // unused + WaitUPnPFunction(); + //TracePrint(ELL_TRACE, "UPnP: Begin TCPProc\n"); + + // do the subscription + { + char callback[100]; + char response[2000]; + PHTTPResponse resp; + int n; + sprintf(callback, "%lu.%lu.%lu.%lu:%u", + (g_dwLocalIP >> 24) & 0xFF, + (g_dwLocalIP >> 16) & 0xFF, + (g_dwLocalIP >> 8) & 0xFF, + (g_dwLocalIP >> 0) & 0xFF, + g_wEventPort); + + n = sprintf(buf, + szEventMsgSubscribeFMT, + g_szEventURL, + callback, g_szRouterHostPortEvent, 1800); + + memset(response, 0, 2000); + n = SendTCPMsg_saddr_parse( + buf, n, + response, 2000, + &g_saddrRouterEvent); + if (n > 0) + { + response[n] = '\0'; + resp = NewHTTPResponse_sz(buf, n, TRUE); + if (NULL != resp) + { +////TracePrint(ELL_TRACE, "UPnP Subscribe returns %s/%d\n", resp->pszStatus, n); + } + else + { +////TracePrint(ELL_TRACE, "UPnP Subscribe not enough response (%d) \n[%s]\n", +// n, response); + } + DeleteHTTPResponse(resp); + } + else + { +////TracePrint(ELL_TRACE, "UPnP Subscribe failed (%d)\n", n); + return NULL; + } + } + + //TracePrint(ELL_TRACE, "UPnP: TCPProc begin loop\n"); + + g_sTCPCancel = -1; + + for (;;) + { +// ssize_t n; + struct sockaddr_in recvaddr; + int recvaddrlen; + fd_set readfds; + struct timeval timeout; + int sEvent; + int fFirstRecv; + int sMax; + + // for after responding to long(?) TCP event + if (g_fQuit) + goto cleanup; + + if (g_sTCPCancel != -1) close(g_sTCPCancel); + sMax = g_sTCPCancel = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sMax < g_sTCP) sMax = g_sTCP; + + FD_ZERO(&readfds); + FD_SET(g_sTCP, &readfds); + FD_SET(g_sTCPCancel, &readfds); + iRet = select(sMax+1, &readfds, NULL, NULL, NULL); + if (iRet <= 0) { + if (EBADF == errno) + continue; + //TracePrint(ELL_TRACE, "UPnP Event select failed (%d)\n", errno); + continue; + } + + recvaddrlen = sizeof(recvaddr); + sEvent = accept(g_sTCP, (struct sockaddr *)&recvaddr, &recvaddrlen); + // not likely - (system's descriptor/file table full) + if (sEvent <= 0) continue; + + ////TracePrint(ELL_TRACE, "UPnP receiving event..\n"); + + // read all we could from this event + fFirstRecv = 1; + iBufLen = 0; + for (;;) + { + FD_ZERO(&readfds); + FD_SET(sEvent, &readfds); + timeout.tv_sec = 0; + timeout.tv_usec = 400000; // long cause we're dealing with input + iRet = select(sEvent+1, &readfds, NULL, NULL, &timeout); + if (iRet <= 0) { + if (g_fQuit) + { + close(sEvent); + goto cleanup; + } + break; + } + + // recv + iRet = recv(sEvent, buf + iBufLen, MAX_SOAPMSGSIZE - iBufLen, 0); + if (iRet < 0) + { + // something is wrong + break; + } + else if (iRet == 0) + { + break; + } + + iBufLen += iRet; + + if (fFirstRecv) + { + int iTemp; + iTemp = send(sEvent, HTTP200OK, HTTP200OKLEN, 0); + shutdown(sEvent, 1); + fFirstRecv = 0; + } + } + + // now send 200 OK and be done + close(sEvent); + + ////TracePrint(ELL_TRACE, "UPnP event (%d) received (%d)\n", g_fExpectEvent, iBufLen); + + // and parse the XML here. + if (iBufLen < MAX_SOAPMSGSIZE) + { + buf[iBufLen] = '\0'; + // for now do nothing + } + else + { + buf[MAX_SOAPMSGSIZE - 1] = '\0'; + } + } + +cleanup: + //TracePrint(ELL_TRACE, "UPnP: TCPProc end\n"); + close(g_sTCP); + g_sTCP = -1; + g_fEventEnabled = FALSE; + if (g_sTCPCancel != -1) close(g_sTCPCancel); + g_sTCPCancel = -1; + return NULL; +} + +static void *UDPProc(void *in) +{ +// char fLoop = 0; // false - don't send copy to self +// int iTTL = SSDP_TTL; + int iRet; +// struct ip_mreq mreq; +// struct sockaddr_in saddr; + unsigned char buf[65536]; +// FILE *log = g_log; + static time_t last_getdevicedesc_t = 0; + + (void)in; // unused + pthread_mutex_lock(&g_xUPnP); + gettimeofday(&g_tvUPnPInitTime, NULL); + pthread_mutex_unlock(&g_xUPnP); + + for (;;) { + ssize_t n; + struct sockaddr_in recvaddr; + int recvaddrlen; + fd_set readfds; + //struct timeval timeout; + //int i; + int sMax; + + if (g_sUDPCancel < g_sUDP) sMax = g_sUDP; + else sMax = g_sUDPCancel; + + FD_ZERO(&readfds); + FD_SET(g_sUDP, &readfds); + FD_SET(g_sUDPCancel, &readfds); + iRet = select(sMax+1, &readfds, NULL, NULL, NULL); + + if (iRet <= 0) { + if (g_fQuit) + { + close(g_sUDP); + close(g_sUDPCancel); + g_sUDP = -1; + g_sUDPCancel = -1; + return NULL; + } + continue; + } + + if (!FD_ISSET(g_sUDP, &readfds)) continue; + recvaddrlen = sizeof(recvaddr); + n = recvfrom(g_sUDP, buf, sizeof(buf), 0, + (struct sockaddr *)&recvaddr, &recvaddrlen); + if (n < 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "recv failed (%d)\n", errno); + close(g_sUDP); + close(g_sUDPCancel); + g_sUDP = -1; + g_sUDPCancel = -1; + return NULL; + } + buf[n] = '\0'; + if (strncmp(buf, "HTTP/1.1", 8) == 0) { + PHTTPResponse pResponse = NewHTTPResponse_sz(buf, n, TRUE); + PrintHTTPResponse(pResponse); + if (DiscoverRouter(pResponse) == 0) + { + time_t now = time(NULL); + if (!g_fControlURLSet || + ((now - last_getdevicedesc_t) > 5)) + { + GetDeviceDescription(); + SetLocalIP(); + last_getdevicedesc_t = now; + } + } + DeleteHTTPResponse(pResponse); + } + else if (strncmp(buf, "NOTIFY * HTTP/1.1", 7) == 0) { + // temporarily use this to fudge - will have the exact same + // parsing, only status/reason set to "*" and "HTTP/1.1". + // TODO: add support for HTTP requests + PHTTPResponse pResponse = NewHTTPResponse_sz(buf, n, TRUE); + if (DiscoverRouter(pResponse) == 0) + { + time_t now = time(NULL); + if (!g_fControlURLSet || + ((now - last_getdevicedesc_t) > 5)) + { + GetDeviceDescription(); + SetLocalIP(); + last_getdevicedesc_t = now; + } + } + DeleteHTTPResponse(pResponse); + } + else { + if (g_fLogging & NALOG_DUMP) + fprintf(g_log, "(%ld) Buffer: \n[%s]\n", time(NULL), buf); + fflush(g_log); + } + } + + close(g_sUDP); + g_sUDP = -1; +} + +static void SendUDPMsg(const char *msg) { + struct sockaddr_in saSendTo; + int iRet; + int iLen; + + bzero(&saSendTo, sizeof(saSendTo)); + saSendTo.sin_family = AF_INET; + saSendTo.sin_addr.s_addr = inet_addr(SSDP_IP); + saSendTo.sin_port = htons(SSDP_PORT); + + iLen = strlen(msg); + + if (g_fLogging & NALOG_DUMP) + fprintf(g_log, "SendUDP: [%s]\n", msg); + + iRet = sendto(g_sUDP, msg, iLen, 0, + (struct sockaddr *)&saSendTo, sizeof(saSendTo)); + + // sanity check + if (iRet != iLen) + if (g_fLogging & NALOG_ALERT) + fprintf(g_log, + "SendUDPMsg: iRet(%d) != strlen(msg)(%d)! (errno %d)\n", + iRet, iLen, errno); +} + +// strstr, case insensitive, and is limited by len +static char *strcasestr_n(const char *big, const char *little, int len) +{ + int bigLen; + int littleLen; + int i; + int end; + + if (little == NULL) return (char *)big; + if (big == NULL) return NULL; + + //bigLen = strlen(big); + bigLen = len; + littleLen = strlen(little); + + if (bigLen < littleLen) return NULL; + + end = bigLen - littleLen; + for (i = 0; i <= end; (i++), (big++)) { + if (strncasecmp(big, little, littleLen) == 0) + return (char *)big; + } + + return NULL; +} + +// this is strnstr, only portable +static char *strstr_n(const char *big, const char *little, int len) +{ + int iBigLen; + int iLittleLen; + + (void)len; // unused + + if ((big == NULL) || (little == NULL)) return NULL; + + iBigLen = strlen(big); + iLittleLen = strlen(little); + + // this part is basically strnstr, except this is portable + for (;;) { + if (iBigLen < iLittleLen) + return NULL; + if (strncmp(big, little, iLittleLen) == 0) + return (char *)big; + ++big; + --iBigLen; + } +} + +// returns -1 for "not found" +static int FindContentLength(char *pbuf, int iLen) +{ + // non reusable HTTP header parsing code: + // ---------------------------------------------- + char *p; + int iResult; + + // find content length header + p = strcasestr_n(pbuf, "\r\nContent-Length:", iLen); + if (p == NULL) return -1; + + p += sizeof("\r\nContent-Length:") - 1; // minus '\0' + + iResult = atoi(p); + + return iResult; + // ---------------------------------------------- +} + +// returns -1 for "not found" +static int FindBody(char *pbuf, int iLen) +{ + // non reusable HTTP header parsing code: + // ---------------------------------------------- + char *p; +// int iResult; + + // find the empty line + p = strstr_n(pbuf, "\r\n\r\n", iLen); + if (p == NULL) return -1; + + p += sizeof("\r\n\r\n") - 1; // minus '\0' + + return (p - pbuf); + // ---------------------------------------------- +} + +static int SendTCPMsg_saddr_2part( + char *msg, int iLen, + char *msg2, int iLen2, + char *result, int resultSize, + struct sockaddr_in *saHost) +{ + int s; + struct sockaddr_in saSendTo; + int iRet; + int iBufLen; + int fND; + int fcntl_flags; + int iRetcode; + struct timeval tv; + fd_set writefds; + + struct timeval tv_start; + struct timeval tv_end; + struct timeval tv_elapsed; + + int iContentLength = -1; + int iBodyOffset = -1; + + gettimeofday(&tv_start, NULL); + + if (g_fUPnPEnabled != TRUE) { +//TracePrint(ELL_TRACE, "UPnP not enabled\n"); + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "UPnP not enabled (no UPnP device found yet)\n"); + return NA_E_NOT_AVAILABLE; + } + + s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (s == -1) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "Can't get TCP socket (%d)\n", errno); + return NA_E_NET; + } + + fND = 1; + if (setsockopt(s, IPPROTO_IP, TCP_NODELAY, &fND, sizeof(fND)) != 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "SendTCPMsg/2part: Can't set TCP_NODELAY option!\n"); + iRetcode = NA_E_NET; + goto cleanup; + } + + fcntl_flags = 0; + fcntl_flags = fcntl(s, F_GETFL, 0); + fcntl_flags |= O_NONBLOCK; + if (fcntl(s, F_SETFL, fcntl_flags) != 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "SendTCPMsg/2part: Can't set O_NONBLOCK option!\n"); + iRetcode = NA_E_NET; + goto cleanup; + } + + if (saHost == NULL) + memcpy(&saSendTo, &g_saddrRouterDesc, sizeof(saSendTo)); + else + memcpy(&saSendTo, saHost, sizeof(saSendTo)); + + iRet = connect(s, (struct sockaddr *) &saSendTo, sizeof(saSendTo)); + if ((iRet < 0) && (errno != EINPROGRESS)) { +//TracePrint(ELL_TRACE, "UPnP connect failed\n"); + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "SendTCPMsg/2part: connect failed (%d)\n", errno); + iRetcode = NA_E_NET; + goto cleanup; + } + + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, + "- Before Sending TCP Msg1: %d == %lu?\n", iLen, strlen(msg)); + if (g_fLogging & NALOG_DUMP) + fprintf(g_log, "Sending TCP msg part 1:\n[%s]\n", msg); + + tv.tv_sec = g_iFunctionTimeout / UPNP_TIMEOUT_GRANULARITY; + tv.tv_usec = (g_iFunctionTimeout % U_TOGRAN) * 1000000 / U_TOGRAN; + FD_ZERO(&writefds); + FD_SET(s, &writefds); + iRet = select(s+1, 0, &writefds, 0, &tv); + if (iRet < 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "SendTCPMsg/2part: select failed (%d)\n", errno); + iRetcode = NA_E_NET; + goto cleanup; + } + if (iRet == 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "SendTCPMsg/2part: select timed out\n"); + iRetcode = NA_E_TIMEOUT; +gettimeofday(&tv_end, NULL); +GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed); +//TracePrint(ELL_TRACE, "UPnP 2part: timeout @1st after %lu.%06lu secs\n", +// tv_elapsed.tv_sec, tv_elapsed.tv_usec); + goto cleanup; + } + + iRet = send(s, msg, iLen, 0); + // sanity check + if (iRet != iLen) + if (g_fLogging & NALOG_ALERT) + fprintf(g_log, "SendTCPMsg/2part: iRet(%d) != strlen(msg)(%d)!\n", + iRet, iLen); + +//TracePrint(ELL_TRACE, "UPnP 2part: 1st %d == %d (%d) (%d)?\n", iRet, iLen, strlen(msg), errno); + + tv.tv_sec = g_iFunctionTimeout / UPNP_TIMEOUT_GRANULARITY; + tv.tv_usec = (g_iFunctionTimeout % U_TOGRAN) * 1000000 / U_TOGRAN; + FD_ZERO(&writefds); + FD_SET(s, &writefds); + // calculate how much time elapsed + gettimeofday(&tv_end, NULL); + GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed); + if (CompareTime(&tv_elapsed, &tv) > 0) { + close(s); + return NA_E_TIMEOUT; + //tv.tv_sec = 0; + //tv.tv_usec = 0; + } + else { + // subtract that from timeout accordingly + tv.tv_sec -= tv_elapsed.tv_sec; + if (tv.tv_usec < tv_elapsed.tv_usec) { + tv.tv_sec--; + tv.tv_usec = 1000000 + tv.tv_usec - tv_elapsed.tv_usec; + } + else + tv.tv_usec = tv.tv_usec - tv_elapsed.tv_usec; + } + iRet = select(s+1, 0, &writefds, 0, &tv); + if (iRet < 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "SendTCPMsg/2part: select2 failed (%d)\n", errno); + iRetcode = NA_E_NET; + goto cleanup; + } + if (iRet == 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "SendTCPMsg/2part: select2 timed out\n"); + iRetcode = NA_E_TIMEOUT; +gettimeofday(&tv_end, NULL); +GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed); +//TracePrint(ELL_TRACE, "UPnP 2part: timeout @2nd after %lu.%06lu secs\n", +// tv_elapsed.tv_sec, tv_elapsed.tv_usec); + goto cleanup; + } + + iRet = send(s, msg2, iLen2, 0); + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, + "SendTCPMsg/parse: Before Sending TCP Msg2: %d == %lu?\n", + iLen2, strlen(msg2)); + if (g_fLogging & NALOG_DUMP) + fprintf(g_log, "Sending TCP msg part 2:\n[%s]\n", msg2); + +//TracePrint(ELL_TRACE, "UPnP 2part: 2nd %d == %d (%d) (%d)?\n", iRet, iLen2, strlen(msg2), errno); + + // sanity check + if (iRet != iLen2) + if (g_fLogging & NALOG_ALERT) + fprintf(g_log, "SendTCPMsg/2part: iRet(%d) != strlen(msg2)(%d)!\n", + iRet, iLen2); + + if (result == NULL) { // if caller just want to send/display msgs + if (g_fLogging & NALOG_DUMP) + fprintf(g_log, "TCP Buffer: ["); + } + + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "start recv @%lu\n", time(NULL)); + + iBufLen = 0; + iContentLength = -1; + iBodyOffset = -1; + for (;;) { + fd_set readfds; + struct timeval timeout; + int i; + + FD_ZERO(&readfds); + FD_SET(s, &readfds); + //timeout.tv_sec = g_iFunctionTimeout / U_TOGRAN; + //timeout.tv_usec = (g_iFunctionTimeout % U_TOGRAN) * 1000000 / U_TOGRAN; + // just do flat 2 sec now, since connection already established + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + + iRet = select(s+1, &readfds, NULL, NULL, &timeout); + if (iRet <= 0) + { +//TracePrint(ELL_TRACE, "UPnP 2part: select timeout? (%d, %d)\n", +// iRet, errno); + break; + } + +//gettimeofday(&tv_end, NULL); +//GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed); +//fprintf(stderr, "2 == loop: %d.%06d\n", tv_elapsed.tv_sec, tv_elapsed.tv_usec); + + // if only sending messages + if (result == NULL) { + char t[1000]; + i = recv(s, t, 1000-1, 0); // leave room for '\0' for dump + if (i== 0) break; + if (g_fLogging & NALOG_DUMP) { + t[i] = '\0'; + fprintf(g_log, "%s", t); + } + continue; + } + + // EO result buf: discard extra bytes + if (resultSize <= iBufLen) { + char t[1000]; + i = recv(s, &t, 1000, 0); + if (i== 0) break; + // Note that there's no dump here - prevents DoS attack from + // flooding the logs/diskspace + continue; + } + + i = recv(s, result + iBufLen, resultSize - iBufLen, 0); + if (i <= 0) { +//TracePrint(ELL_TRACE, "UPnP 2part: recv done %d (%d, %d)\n", +// iBufLen, i, errno); + break; + } + + iBufLen += i; + + // parse and see if we can find content-length to quit early + iContentLength = FindContentLength(result, iBufLen); + + // now if we're still in header, see if we can find body + iBodyOffset = FindBody(result, iBufLen); + + // now check if we can leave early. conditions are: + // past headers, and we've already recv'ed content-length of body + if ((iBodyOffset >= 0) && + (iContentLength >= 0) && + ((iBufLen - iBodyOffset) >= iContentLength)) + { +//TracePrint(ELL_TRACE, "UPnP 2part: read all specified %d (%d, %d) (%d, %d)\n", +// iBufLen, i, errno, iBodyOffset, iContentLength); + break; + } + } + +//fprintf(stderr, "2 -- \n"); + + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "done recv @%lu\n", time(NULL)); + + if (result == NULL) { // if caller just want to send/display msgs + if (g_fLogging & NALOG_DUMP) + fprintf(g_log, "]\n"); + } + + close(s); + return iBufLen; + +cleanup: + close(s); + return iRetcode; +} + +static int SendTCPMsg_saddr_parse( + char *msg, int iLen, + char *result, int resultSize, + struct sockaddr_in *saHost) +{ + int s; + struct sockaddr_in saSendTo; + int iRet; + int iBufLen; + int fcntl_flags; + fd_set writefds; + struct timeval tv; + + struct timeval tv_start; +// struct timeval tv_end; +// struct timeval tv_elapsed; + + // HTTP parsing vars + char *pszCurHdr; + int iContentLength; + int iBodyOffset; +// char prevChar; + + tv.tv_sec = 0; + tv.tv_usec = 25000; + select(0, NULL, NULL, NULL, &tv); + + pthread_mutex_lock(&g_xUPnPMsg); + + gettimeofday(&tv_start, NULL); + + if (g_fUPnPEnabled != TRUE) { +//TracePrint(ELL_TRACE, "UPnP not enabled\n"); + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "UPnP not enabled (no UPnP device found yet)\n"); + pthread_mutex_unlock(&g_xUPnPMsg); + return NA_E_NOT_AVAILABLE; + } + + s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (s == -1) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "Can't get TCP socket (%d)\n", errno); + pthread_mutex_unlock(&g_xUPnPMsg); + return NA_E_NET; + } + + fcntl_flags = 0; + fcntl_flags = fcntl(s, F_GETFL, 0); + fcntl_flags |= O_NONBLOCK; + if (fcntl(s, F_SETFL, fcntl_flags) != 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "SendTCPMsg/parse: Can't set O_NONBLOCK option!\n"); + close(s); + pthread_mutex_unlock(&g_xUPnPMsg); + return NA_E_NET; + } + + if (saHost == NULL) + memcpy(&saSendTo, &g_saddrRouterDesc, sizeof(saSendTo)); + else + memcpy(&saSendTo, saHost, sizeof(saSendTo)); + + iRet = connect(s, (struct sockaddr *) &saSendTo, sizeof(saSendTo)); + if ((iRet < 0) && (errno != EINPROGRESS)) { +//TracePrint(ELL_TRACE, "UPnP connect failed\n"); + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "SendTCPMsg/parse: connect failed (%d)\n", errno); + close(s); + pthread_mutex_unlock(&g_xUPnPMsg); + return NA_E_NET; + } + + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "SendTCPMsg/parse: Before Sending TCP Msg: %d == %lu?\n", + iLen, strlen(msg)); + if (g_fLogging & NALOG_DUMP) + fprintf(g_log,"Sending TCP msg:\n[%s]\n", msg); + + tv.tv_sec = g_iFunctionTimeout / UPNP_TIMEOUT_GRANULARITY; + tv.tv_usec = (g_iFunctionTimeout % U_TOGRAN) * 1000000 / U_TOGRAN; + FD_ZERO(&writefds); + FD_SET(s, &writefds); + iRet = select(s+1, 0, &writefds, 0, &tv); + if (iRet < 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "SendTCPMsg/parse: select failed (%d)\n", errno); + close(s); + pthread_mutex_unlock(&g_xUPnPMsg); + return NA_E_NET; + } + if (iRet == 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "SendTCPMsg/parse: select timed out\n"); + close(s); + pthread_mutex_unlock(&g_xUPnPMsg); + return NA_E_TIMEOUT; + } + + iRet = send(s, msg, iLen, 0); + + // sanity check + if (iRet != iLen) + if (g_fLogging & NALOG_ALERT) + fprintf(g_log, "SendTCPMsg: iRet (%d) != strlen(msg) (%d)!\n", + iRet, iLen); + + if (result == NULL) { // if caller just want to send/display msgs + if (g_fLogging & NALOG_DUMP) + fprintf(g_log, "TCP Buffer: ["); + } + + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "start recv @%lu\n", time(NULL)); + + iBufLen = 0; + pszCurHdr = result; + iContentLength = -1; + iBodyOffset = -1; + for (;;) { + fd_set readfds; + struct timeval timeout; + int i; + + FD_ZERO(&readfds); + FD_SET(s, &readfds); + //timeout.tv_sec = g_iFunctionTimeout / U_TOGRAN; + //timeout.tv_usec = (g_iFunctionTimeout % U_TOGRAN) * 1000000 / U_TOGRAN; + // just do flat 2 sec now, since connection already established + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + iRet = select(s+1, &readfds, NULL, NULL, &timeout); + if (iRet <= 0) { +//fprintf(stderr, "**********: select failed (%d/%d)\n", iRet, errno); + break; + } + +//gettimeofday(&tv_end, NULL); +//GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed); +//fprintf(stderr, "p == loop: %d.%06d\n", tv_elapsed.tv_sec, tv_elapsed.tv_usec); + + // if only sending messages + if (result == NULL) { + char t[1000]; + i = recv(s, t, 1000-1, 0); // leave room for '\0' for dump + if (i== 0) break; + if (g_fLogging & NALOG_DUMP) { + t[i] = '\0'; + fprintf(g_log, "%s", t); + } + continue; + } + + // EO result buf: discard extra bytes + if (resultSize <= iBufLen) { + char t[1000]; + i = recv(s, &t, 1000, 0); + if (i== 0) break; + // Note that there's no dump here - prevents DoS attack from + // flooding the logs/diskspace + continue; + } + + i = recv(s, result + iBufLen, resultSize - iBufLen, 0); + if (0 == i) { + + break; + } + else if (i < 0) { + if (EAGAIN == errno) continue; + break; + } + + iBufLen += i; + + // parse and see if we can find content-length to quit early + iContentLength = FindContentLength(result, iBufLen); + + // now if we're still in header, see if we can find body + iBodyOffset = FindBody(result, iBufLen); + + } + +//fprintf(stderr, "p -- \n"); + + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "done recv @%lu\n", time(NULL)); + + if (result == NULL) { // if caller just want to send/display msgs + if (g_fLogging & NALOG_DUMP) + fprintf(g_log, "]\n"); + } + + close(s); + pthread_mutex_unlock(&g_xUPnPMsg); + return iBufLen; +} + + + +// szSOAPMsgControlAHeaderFMT - 4 args (ctrl_url, host/port, action, length) +// szSOAPMsgControlABodyFMT - 2 args (action, args string) +// szSOAPMsgControlAArgumentFMT - 2 args (name/value) +static PHTTPResponse SendSOAPMsgControlAction( + char *action, + int argc, + PProperty args, + int f2Part) +{ + //char outBuffer[65536]; + //char outBufferBody[65536]; + //char outBufferArgs[65536]; + char *outBuffer = NULL; + char *outBufferBody = NULL; + char *outBufferArgs = NULL; + char *inBuffer = NULL; + int iLen; + int iHeaderLen; + int iBodyLen; + int iArgsLen; + int iResultLen; + int i; + int n; + PHTTPResponse pResponse = NULL; + + + if (!WaitUPnPFunction()) + return NULL; + + if ((outBuffer = (char *) malloc(MAX_SOAPMSGSIZE)) == NULL) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "can't malloc for outBuffer\n"); + goto cleanup; + } + if ((outBufferBody = (char *) malloc(MAX_SOAPMSGSIZE)) == NULL) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "can't malloc for outBufferBody\n"); + goto cleanup; + } + if ((outBufferArgs = (char *) malloc(MAX_SOAPMSGSIZE)) == NULL) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "can't malloc for outBufferArgs\n"); + goto cleanup; + } + if ((inBuffer = (char *) malloc(MAX_SOAPMSGSIZE)) == NULL) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "can't malloc for inBuffer\n"); + goto cleanup; + } + + iArgsLen = 0; + if (args != NULL) + for (i=0; i 0) { + if (iResultLen > MAX_SOAPMSGSIZE) { + if (g_fLogging & NALOG_ALERT) + fprintf(g_log, "result truncated..\n"); + iResultLen = MAX_SOAPMSGSIZE; + } + pResponse = NewHTTPResponse_sz(inBuffer, iResultLen, FALSE); + if (pResponse != NULL) { + PrintHTTPResponse(pResponse); + //DeleteHTTPResponse(pResponse); + // - return response to caller + } + } + else { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "No TCP Response\n"); + //TracePrint(ELL_TRACE, "UPnP SendSOAPMsg got no TCP response (%d)\n", +// iResultLen); + } + +cleanup: + if (outBuffer != NULL) free(outBuffer); + if (outBufferBody != NULL) free(outBufferBody); + if (outBufferArgs != NULL) free(outBufferArgs); + if (inBuffer != NULL) free(inBuffer); + + return pResponse; +} + +static int FindURLBase(char *pbuf, int iLen, char *szURLBase) +{ + // non reusable XML parsing code: + // ---------------------------------------------- + char *p; + int i = 0; + + // now skip after end of this tag, then skip until controlURL tag + p = strstr_n(pbuf, "", iLen); + if (p == NULL) return -1; + + // skip to the actual stuff + p += sizeof("") - 1; // minus '\0' + + // skip white spaces (just in case) + while (isspace(*p)) + p++; + + // copy into szURLBase + while ((*p != '\0') && (*p != '<') && !isspace(*p)) { + if (i++ > 1000) break; + *szURLBase = *p; + szURLBase++; + p++; + } + *szURLBase = '\0'; + + return 0; + // ---------------------------------------------- +} + + +static int FindDescInfo( + char *pbuf, + int iLen, + const char *szParentName, + const char *szName, + char *szValue) +{ + char *p; + char szSearch[100]; + int iSearchLen; + int i = 0; + + // find the device within pbuf + p = strstr_n( + pbuf, + szParentName, + iLen); + if (p == NULL) + return -1; + + // adjust strlen + iLen -= (p - pbuf); + pbuf = p; + + // now skip after end of this tag, then skip until manufacturer tag + iSearchLen = sprintf(szSearch, "<%s>", szName); + p = strstr_n(pbuf, szSearch, iLen); + if (p == NULL) return -1; + p += iSearchLen; + + // skip white spaces (just in case) + while (isspace(*p)) + p++; + + // copy into szValue + while ((*p != '\0') && (*p != '<')) { + if (i++ > 1000) break; + *szValue = *p; + szValue++; + p++; + } + *szValue = '\0'; + + return 0; +} + +static int FindIGDInfo(char *pbuf, int iLen, const char *szName, char *szValue) +{ + return FindDescInfo( + pbuf, iLen, + "urn:schemas-upnp-org:device:InternetGatewayDevice:1", + szName, szValue); +} + +static int FindManufacturer(char *pbuf, int iLen, char *szManuf) +{ + return FindIGDInfo(pbuf, iLen, "manufacturer", szManuf); +} + +static int FindFriendlyName(char *pbuf, int iLen, char *szValue) +{ + return FindIGDInfo(pbuf, iLen, "friendlyName", szValue); +} + +static int FindModelName(char *pbuf, int iLen, char *szValue) +{ + return FindIGDInfo(pbuf, iLen, "modelName", szValue); +} + +static int FindModelDescription(char *pbuf, int iLen, char *szValue) +{ + return FindIGDInfo(pbuf, iLen, "modelDescription", szValue); +} + +static int FindWANIPInfo(char *pbuf, int iLen, const char *szName, char *szValue) +{ + return FindDescInfo( + pbuf, iLen, + "urn:schemas-upnp-org:service:WANIPConnection:1", + szName, szValue); +} + +static int FindControlURL(char *pbuf, int iLen, char *szControlURL) +{ + return FindWANIPInfo(pbuf, iLen, "controlURL", szControlURL); +} + +static int FindEventURL(char *pbuf, int iLen, char *szEventURL) +{ + return FindWANIPInfo(pbuf, iLen, "eventSubURL", szEventURL); +} + +static int FindRouterInfo(char *inBuffer, int iLen) +{ + if (FindManufacturer(inBuffer, iLen, g_szManufacturer) != 0) + g_szManufacturer[0] = '\0'; + + if (FindFriendlyName(inBuffer, iLen, g_szFriendlyName) != 0) + g_szFriendlyName[0] = '\0'; + + if (FindModelName(inBuffer, iLen, g_szModelName) != 0) + g_szModelName[0] = '\0'; + + if (FindModelDescription(inBuffer, iLen, g_szModelDescription) != 0) + g_szModelDescription[0] = '\0'; + +//TracePrint(ELL_TRACE, +// "UPnP Router Info:\n" +// " - manufacturer [%s]\n" +// " - friendly name [%s]\n" +// " - model name [%s]\n" +// " - model desc [%s]\n", +// g_szManufacturer, g_szFriendlyName, g_szModelName, g_szModelDescription); + + return 0; +} + +static void ParseURL( + const char *szBuf, char *pszHostPort, + struct sockaddr_in *psaddr, char *pszPath) +{ + char buf[1024]; + char *p; + char *q; + unsigned short port; + + strcpy(buf, szBuf); + + p = buf; + if (0 == strncmp(p, "http://", 7)) + p += 7; + + q = strchr(p, '/'); + + if (pszPath) { + if (NULL == q) { + pszPath[0] = '/'; + pszPath[1] = '\0'; + } + else { + strcpy(pszPath, q); + *q = '\0'; + } + } + + // find the port separetor + q = strchr(p, ':'); + if (NULL == q) + port = 80; + else { + port = atoi(q + 1); + // HTTP's by default port 80, so don't have it in the "Host:" header + if (80 == port) *q = '\0'; + } + + if (pszHostPort) strcpy(pszHostPort, p); + + if (NULL != q) *q = '\0'; + + if (NULL != psaddr) { + psaddr->sin_family = AF_INET; + psaddr->sin_addr.s_addr = inet_addr(p); + psaddr->sin_port = htons(port); + } +#if 0 +//TracePrint(ELL_TRACE, "ParseURL [%s] -> [%s][%s] %lu.%lu.%lu.%lu:%u\n", + szBuf, + pszHostPort?pszHostPort:"", + pszPath?pszPath:"", + (psaddr->sin_addr.s_addr >> 24) & 0xff, + (psaddr->sin_addr.s_addr >> 16) & 0xff, + (psaddr->sin_addr.s_addr >> 8) & 0xff, + (psaddr->sin_addr.s_addr >> 0) & 0xff, + psaddr->sin_port); +#endif +} + +static void GetDeviceDescription(void) +{ + char *outBuffer = NULL; + char *inBuffer = NULL; + int iBufLen; + int iLen; + char szURLBase[1024]; + char szControlURL[1024]; + char szEventURL[1024]; + + if (!g_fUPnPEnabled) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "GetDeviceDescription: upnp not enabled\n"); + return; + } + + if ((outBuffer = (char *) malloc(MAX_SOAPMSGSIZE)) == NULL) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "can't malloc for outBuffer\n"); + goto cleanup; + } + if ((inBuffer = (char *) malloc(MAX_SOAPMSGSIZE)) == NULL) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "can't malloc for inBuffer\n"); + goto cleanup; + } + + iBufLen = sprintf(outBuffer, szSSDPMsgDescribeDeviceFMT, g_szNATDevDescURL, + g_szRouterHostPortDesc); + + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "Describe Device: [%s]\n", outBuffer); + iLen = SendTCPMsg_saddr_parse(outBuffer, iBufLen, inBuffer, MAX_SOAPMSGSIZE, + &g_saddrRouterDesc); + + g_fControlURLSet = FALSE; + + if (FindControlURL(inBuffer, iLen, szControlURL) != 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "GetDeviceDesc: can't find control URL\n"); + goto cleanup; + } + + // start modifying global + pthread_mutex_lock(&g_xUPnP); + + { + // now see if there's the URLBase + if (FindURLBase(inBuffer, iLen, szURLBase) != 0) { + // not there? try default numbers from device description + memcpy(&g_saddrRouterBase, &g_saddrRouterDesc, + sizeof(g_saddrRouterBase)); + strcpy(g_szRouterHostPortBase, g_szRouterHostPortDesc); + } + else { + ParseURL(szURLBase, + g_szRouterHostPortBase, &g_saddrRouterBase, NULL); + + if ((strlen(g_szRouterHostPortBase) == 0) || + (g_saddrRouterBase.sin_addr.s_addr == INADDR_NONE)) { + memcpy(&g_saddrRouterBase, &g_saddrRouterDesc, + sizeof(g_saddrRouterBase)); + strcpy(g_szRouterHostPortBase, g_szRouterHostPortDesc); + } + } + } + + ParseURL(szControlURL, + g_szRouterHostPortSOAP, &g_saddrRouterSOAP, g_szControlURL); + if ((strlen(g_szRouterHostPortSOAP) == 0) || + (g_saddrRouterSOAP.sin_addr.s_addr == INADDR_NONE)) { + memcpy(&g_saddrRouterSOAP, &g_saddrRouterBase, + sizeof(g_saddrRouterSOAP)); + strcpy(g_szRouterHostPortSOAP, g_szRouterHostPortBase); + } + + +////TracePrint(ELL_TRACE, "UPnP Control URL set to[%s][%s]...\n", +// g_szRouterHostPortSOAP, g_szControlURL); + + g_fControlURLSet = TRUE; + gettimeofday(&g_tvLastUpdateTime, NULL); + pthread_cond_broadcast(&g_condUPnPControlURL); + + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "Got Device Description\n"); + + // find router info + FindRouterInfo(inBuffer, iLen); + + if (FindEventURL(inBuffer, iLen, szEventURL) != 0) { + szEventURL[0] = '\0'; + } + else { + ParseURL(szEventURL, + g_szRouterHostPortEvent, &g_saddrRouterEvent, g_szEventURL); + if ((strlen(g_szRouterHostPortEvent) == 0) || + (g_saddrRouterEvent.sin_addr.s_addr == INADDR_NONE)) { + memcpy(&g_saddrRouterEvent, &g_saddrRouterBase, + sizeof(g_saddrRouterEvent)); + strcpy(g_szRouterHostPortEvent, g_szRouterHostPortBase); + } + + EventInit(); + } + +cleanup: + if (outBuffer != NULL) free(outBuffer); + if (inBuffer != NULL) free(inBuffer); + + pthread_mutex_unlock(&g_xUPnP); +} + + +static void GetIPByName(char *hostname, unsigned long *ip_ret) +{ + unsigned long ip; + + ip = inet_addr(hostname); + if (ip == INADDR_NONE) { + struct hostent *pHEnt; + pHEnt = gethostbyname(hostname); + if (pHEnt == NULL) { + if (g_fLogging & NALOG_ALERT) + fprintf(g_log, "Can't translate [%s] to IP...\n", hostname); + g_dwLocalIP = htonl(INADDR_ANY); + return; + } + ip = ntohl(*(unsigned long *)(pHEnt->h_addr)); + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "hostname [%s] to ip: %ld.%ld.%ld.%ld\n", + hostname, + (ip >> 24) & 0xff, + (ip >> 16) & 0xff, + (ip >> 8) & 0xff, + (ip >> 0) & 0xff); + } + *ip_ret = ip; +} + +static void SetLocalIP() +{ + PIPINFO pIPInfo = NULL; + int count = GetIPInfo(&pIPInfo); + if (NULL != pIPInfo) + { + // choose first non IPV6 address + // iterate through array and set port information + int i; + unsigned long dwFirst = 0; + for(i = 0; i < count; i++) + { + if (!(pIPInfo[i].iFlags & ISIPV6) && + (strncmp(pIPInfo[i].szIfName, "ppp", 3) != 0)) + { + unsigned long dwTemp; + + memcpy(&dwTemp, pIPInfo[i].abIP, sizeof(unsigned long)); + + if (0 != GetNATIPNetmask(dwTemp)) { + g_dwLocalIP = dwTemp; + break; + } + + if (0 == dwFirst) + dwFirst = dwTemp; + } + } + if (i == count) + g_dwLocalIP = dwFirst; + FreeIPInfo(pIPInfo); + } + +} + +static int FindTagContent(const char *text, const char *tagname, char *buf) +{ + char *p; + // parse the xml + p = strstr(text, tagname); + if (p == NULL) { + if (g_fLogging & NALOG_INFO0) + fprintf(g_log, "FindTagContent: can't find %s\n", tagname); + return NA_E_PARSE_ERROR; + } + + if (sscanf(p, "%*[^>]> %[^ <] <", buf) < 1) { + if (g_fLogging & NALOG_INFO0) + fprintf(g_log, "FindTagContent: Can't parse tag %s\n", tagname); + return NA_E_PARSE_ERROR; + } + + return NA_E_SUCCESS; +} + +mStatus LNT_UnmapPort(mDNSIPPort PubPort, mDNSBool tcp) +{ + //int iLen; + char szEPort[10]; + //char szRemoteHost[1024]; + //unsigned long dwIP; + Property propArgs[3]; + PHTTPResponse resp; + unsigned short port = PubPort.NotAnInteger; + int protocol = tcp ? IPPROTO_TCP : IPPROTO_UDP; + sprintf(szEPort, "%u", port); + + bzero(propArgs, sizeof(propArgs)); + propArgs[0].pszName = "NewRemoteHost"; + propArgs[0].pszValue = ""; + propArgs[0].pszType = "string"; + propArgs[1].pszName = "NewExternalPort"; + propArgs[1].pszValue = szEPort; + propArgs[1].pszType = "ui2"; + propArgs[2].pszName = "NewProtocol"; + if (protocol == IPPROTO_TCP) { + propArgs[2].pszValue = "TCP"; + } + else if (protocol == IPPROTO_UDP) { + propArgs[2].pszValue = "UDP"; + } + else { + return -1; + } + propArgs[2].pszType = "string"; + + resp = SendSOAPMsgControlAction( + "DeletePortMapping", 3, propArgs, FALSE); + if (resp == NULL) { + return mStatus_NATTraversal; + } + + if (strcmp(resp->pszStatus, "200") != 0) { + DeleteHTTPResponse(resp); + return mStatus_NATTraversal; + } + + DeleteHTTPResponse(resp); + return mStatus_NoError; +} + + +static int GetMappingUnused(unsigned short eport, int protocol); + +extern mStatus LNT_MapPort(mDNSIPPort priv, mDNSIPPort pub, mDNSBool tcp) +{ + char szEPort[6]; + char szIPort[6]; + unsigned long dwIP; + char szLocalIP[30]; + char descr[40]; + Property propArgs[8]; + PHTTPResponse resp; + unsigned short iport = priv.NotAnInteger; + unsigned short eport = pub.NotAnInteger; + int protocol = tcp ? IPPROTO_TCP : IPPROTO_UDP; + + + if (NA_E_EXISTS == GetMappingUnused(eport, protocol)) + return mStatus_AlreadyRegistered; + + //DeletePortMapping(eport, protocol); + + sprintf(szEPort, "%u", eport); + + sprintf(szIPort, "%u", iport); + + dwIP = g_dwLocalIP; + sprintf(szLocalIP, "%u.%u.%u.%u", + (unsigned int)((dwIP >> 24) & 0xff), + (unsigned int)((dwIP >> 16) & 0xff), + (unsigned int)((dwIP >> 8) & 0xff), + (unsigned int)((dwIP >> 0) & 0xff)); + + bzero(propArgs, sizeof(propArgs)); + propArgs[0].pszName = "NewRemoteHost"; + propArgs[0].pszValue = ""; + propArgs[0].pszType = "string"; + propArgs[1].pszName = "NewExternalPort"; + propArgs[1].pszValue = szEPort; + propArgs[1].pszType = "ui2"; + propArgs[2].pszName = "NewProtocol"; + if (protocol == IPPROTO_TCP) { + propArgs[2].pszValue = "TCP"; + } + else if (protocol == IPPROTO_UDP) { + propArgs[2].pszValue = "UDP"; + } + else { + return mStatus_BadParamErr; + } + propArgs[2].pszType = "string"; + propArgs[3].pszName = "NewInternalPort"; + propArgs[3].pszValue = szIPort; + propArgs[3].pszType = "ui2"; + propArgs[4].pszName = "NewInternalClient"; + propArgs[4].pszValue = szLocalIP; + propArgs[4].pszType = "string"; + propArgs[5].pszName = "NewEnabled"; + propArgs[5].pszValue = "1"; + propArgs[5].pszType = "boolean"; + propArgs[6].pszName = "NewPortMappingDescription"; + sprintf(descr, "iC%u", eport); + //propArgs[6].pszValue = "V"; + propArgs[6].pszValue = descr; + propArgs[6].pszType = "string"; + propArgs[7].pszName = "NewLeaseDuration"; + propArgs[7].pszValue = "0"; + propArgs[7].pszType = "ui4"; + + resp = SendSOAPMsgControlAction( + "AddPortMapping", 8, propArgs, FALSE); + + if (resp == NULL) { + return mStatus_NATTraversal; + } + + if (strcmp(resp->pszStatus, "200") != 0) { + DeleteHTTPResponse(resp); + return mStatus_NATTraversal; + } + + DeleteHTTPResponse(resp); + return mStatus_NoError; +} + +static int GetMappingUnused(unsigned short eport, int protocol) +{ + char buf[1024]; + char szPort[10]; + Property propArgs[3]; + PHTTPResponse resp; + unsigned long ip; + + sprintf( szPort, "%u", eport); + + bzero(&propArgs, sizeof(propArgs)); + propArgs[0].pszName = "NewRemoteHost"; + propArgs[0].pszValue = ""; + propArgs[0].pszType = "string"; + propArgs[1].pszName = "NewExternalPort"; + propArgs[1].pszValue = szPort; + propArgs[1].pszType = "ui2"; + propArgs[2].pszName = "NewProtocol"; + if (protocol == IPPROTO_TCP) { + propArgs[2].pszValue = "TCP"; + } + else if (protocol == IPPROTO_UDP) { + propArgs[2].pszValue = "UDP"; + } + else { + return NA_E_INVALID_PARAMETER; + } + propArgs[2].pszType = "string"; + + resp = SendSOAPMsgControlAction( + "GetSpecificPortMappingEntry", 3, propArgs, FALSE); + if (resp != NULL) { + if ((strcmp(resp->pszStatus, "200") == 0) && + (FindTagContent(resp->pszBody, "NewInternalClient", buf) == 0)) + { + GetIPByName(buf, &ip); + if (ip == g_dwLocalIP) { + // (perhaps we let it go?) + DeleteHTTPResponse(resp); + return NA_E_SUCCESS; + } + else { + DeleteHTTPResponse(resp); + return NA_E_EXISTS; + } + } + DeleteHTTPResponse(resp); + } + + return NA_E_SUCCESS; +} + +mStatus LNT_GetPublicIP(mDNSOpaque32 *IpPtr) +{ + char buf[1024]; + PHTTPResponse resp; + static struct timeval tvLastGoodIP = {0,0}; + static unsigned long dwLastGoodIP; + struct timeval tv; + unsigned long *ip = (unsigned long *)IpPtr; + if (ip == NULL) return mStatus_BadParamErr; + + gettimeofday(&tv, NULL); + GetTimeElapsed(&tvLastGoodIP, &tv, &tv); + if (tv.tv_sec < 4) + { + return dwLastGoodIP; + } + + resp = SendSOAPMsgControlAction( + "GetExternalIPAddress", 0, NULL, FALSE); + + if (resp == NULL) + return mStatus_NATTraversal; + + if (FindTagContent(resp->pszBody, "NewExternalIPAddress", buf) == 0) { + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "Mapped remote host = %s\n", buf); + *ip = inet_addr(buf); + DeleteHTTPResponse(resp); + + gettimeofday(&tvLastGoodIP, NULL); + dwLastGoodIP = *ip; + + return mStatus_NoError; + } + + DeleteHTTPResponse(resp); + return mStatus_NATTraversal; +} + +static void SendDiscoveryMsg() +{ + // do it twice to avoid lost packet + //SendUDPMsg(szSSDPMsgDiscoverNAT); + SendUDPMsg(szSSDPMsgDiscoverRoot); + SendUDPMsg(szSSDPMsgDiscoverIGD); + SendUDPMsg(szSSDPMsgDiscoverNAT); +} + +// Set up threads for upnp responses, etc. +int LegacyNATInit(void) +{ + //pthread_t UDPthread; + pthread_attr_t attr; + int iRet; + //struct timeval tv; + + static int fFirstInitLocks = TRUE; + FILE *log = NULL; + + g_fLogging = 0; + g_log = stderr; + + SetLocalIP(); + + g_fQuit = FALSE; + + if (fFirstInitLocks) + { + // init locks + if (pthread_mutex_init(&g_xUPnP, NULL)) { + if (g_fLogging & NALOG_ERROR) + fprintf(log, "UpnpInit - mutex init failed\n"); + return NA_E_INTERNAL_ERROR; + } + if (pthread_cond_init(&g_condUPnP, NULL)) { + pthread_mutex_destroy(&g_xUPnP); + if (g_fLogging & NALOG_ERROR) + fprintf(log, "UpnpInit - cond init failed\n"); + return NA_E_INTERNAL_ERROR; + } + if (pthread_cond_init(&g_condUPnPControlURL, NULL)) { + pthread_mutex_destroy(&g_xUPnP); + pthread_cond_destroy(&g_condUPnP); + if (g_fLogging & NALOG_ERROR) + fprintf(log, "UpnpInit - cond init failed\n"); + return NA_E_INTERNAL_ERROR; + } + if (pthread_mutex_init(&g_xUPnPMsg, NULL)) { + pthread_mutex_destroy(&g_xUPnP); + pthread_cond_destroy(&g_condUPnP); + pthread_cond_destroy(&g_condUPnPControlURL); + if (g_fLogging & NALOG_ERROR) + fprintf(log, "UpnpInit - mutex init failed\n"); + return NA_E_INTERNAL_ERROR; + } + + fFirstInitLocks = FALSE; + } + + if (g_fFirstInit) + { + // initialize UDP socket for SSDP + g_sUDP = SSDPListen(); + g_sUDPCancel = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // sock to signal canccelation to UDP thread + if (g_sUDP < 0 || g_sUDPCancel < 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(log, "UpnpInit - Failed to init multicast socket.\n"); + return NA_E_INTERNAL_ERROR; + } + + // make UDP thread + pthread_attr_init(&attr); + iRet = pthread_create(&g_UDPthread, &attr, UDPProc, log); + if (iRet != 0) { + g_fFirstInit = TRUE; // so we'll redo this part next time + close(g_sUDP); + g_sUDP = -1; + if (g_fLogging & NALOG_ERROR) + fprintf(log, "UpnpInit - pthread create failed (%d)\n", iRet); + return NA_E_THREAD_ERROR; + } + + // set this to FALSE only if first call succeeded + g_fFirstInit = FALSE; + + //TracePrint(ELL_TRACE, "UPnP init passed\n"); + + //tv.tv_sec = 0; + //tv.tv_usec = 20000; // wait 20ms for thread/udp/multicast init + //select(0, 0, 0, 0, &tv); + } + + // send discovery message + SendDiscoveryMsg(); + + return NA_E_SUCCESS; +} + +int LegacyNATDestroy() +{ + void *UDPThreadRetVal; + g_fQuit = TRUE; + if (g_sTCPCancel >= 0) close(g_sTCPCancel); + if (g_sUDPCancel >= 0) close(g_sUDPCancel); + pthread_join(g_UDPthread, &UDPThreadRetVal); + g_sTCPCancel = -1; + g_sUDPCancel = -1; + g_fFirstInit = TRUE; + g_fUPnPEnabled = FALSE; + g_fControlURLSet = FALSE; + return NA_E_SUCCESS; +} diff --git a/mDNSMacOSX/SamplemDNSClient.c b/mDNSMacOSX/SamplemDNSClient.c index 430d0e6..a387269 100644 --- a/mDNSMacOSX/SamplemDNSClient.c +++ b/mDNSMacOSX/SamplemDNSClient.c @@ -3,8 +3,6 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -38,6 +36,12 @@ Change History (most recent first): $Log: SamplemDNSClient.c,v $ +Revision 1.46 2004/11/02 01:32:34 cheshire + Update code so it still compiles when DNSServiceDiscovery.h is deprecated + +Revision 1.45 2004/06/15 02:39:47 cheshire +When displaying error message, only show command name, not entire path + Revision 1.44 2004/05/28 02:20:06 cheshire If we allow dot or empty string for domain when resolving a service, it should be a synonym for "local" @@ -80,6 +84,12 @@ Add checkin history header #include #include #include + +// We already know this tool is using the old deprecated API (that's its purpose) +// Since we compile with all warnings treated as errors, we have to turn off the warnings here or the project won't compile +#include +#undef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED +#define AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED #include //************************************************************************************************************* @@ -322,6 +332,7 @@ static void reg_reply(DNSServiceRegistrationReplyErrorType errorCode, void *cont int main(int argc, char **argv) { + const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0]; char *dom; setlinebuf(stdout); // Want to see lines as they appear, not block buffered @@ -443,16 +454,16 @@ Exit: return 0; Fail: - fprintf(stderr, "%s -E (Enumerate recommended registration domains)\n", argv[0]); - fprintf(stderr, "%s -F (Enumerate recommended browsing domains)\n", argv[0]); - fprintf(stderr, "%s -B (Browse for services instances)\n", argv[0]); - fprintf(stderr, "%s -L (Look up a service instance)\n", argv[0]); - fprintf(stderr, "%s -R [...] (Register a service)\n", argv[0]); - fprintf(stderr, "%s -A (Test Adding/Updating/Deleting a record)\n", argv[0]); - fprintf(stderr, "%s -U (Test updating a TXT record)\n", argv[0]); - fprintf(stderr, "%s -N (Test adding a large NULL record)\n", argv[0]); - fprintf(stderr, "%s -T (Test creating a large TXT record)\n", argv[0]); - fprintf(stderr, "%s -M (Test creating a registration with multiple TXT records)\n", argv[0]); - fprintf(stderr, "%s -I (Test registering and then immediately updating TXT record)\n", argv[0]); + fprintf(stderr, "%s -E (Enumerate recommended registration domains)\n", progname); + fprintf(stderr, "%s -F (Enumerate recommended browsing domains)\n", progname); + fprintf(stderr, "%s -B (Browse for services instances)\n", progname); + fprintf(stderr, "%s -L (Look up a service instance)\n", progname); + fprintf(stderr, "%s -R [...] (Register a service)\n", progname); + fprintf(stderr, "%s -A (Test Adding/Updating/Deleting a record)\n", progname); + fprintf(stderr, "%s -U (Test updating a TXT record)\n", progname); + fprintf(stderr, "%s -N (Test adding a large NULL record)\n", progname); + fprintf(stderr, "%s -T (Test creating a large TXT record)\n", progname); + fprintf(stderr, "%s -M (Test creating a registration with multiple TXT records)\n", progname); + fprintf(stderr, "%s -I (Test registering and then immediately updating TXT record)\n", progname); return 0; } diff --git a/mDNSMacOSX/daemon.c b/mDNSMacOSX/daemon.c index 9cb6a47..e82657e 100644 --- a/mDNSMacOSX/daemon.c +++ b/mDNSMacOSX/daemon.c @@ -3,8 +3,6 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -38,6 +36,177 @@ Change History (most recent first): $Log: daemon.c,v $ +Revision 1.227 2004/12/10 13:52:57 cheshire + Turn off SIGPIPE signals + +Revision 1.226 2004/12/10 05:27:26 cheshire + Guard against multiple autoname services of the same type on the same machine + +Revision 1.225 2004/12/10 04:28:29 cheshire + User not notified of name changes for services using new UDS API + +Revision 1.224 2004/12/10 00:41:05 cheshire +Adjust alignment of log messages + +Revision 1.223 2004/12/07 20:42:34 cheshire +Add explicit context parameter to mDNS_RemoveRecordFromService() + +Revision 1.222 2004/12/06 21:15:23 ksekar + mDNSResponder crashed in CheckServiceRegistrations + +Revision 1.221 2004/11/30 03:24:04 cheshire + Defer processing network configuration changes until configuration has stabilized + +Revision 1.220 2004/11/29 23:34:31 cheshire +On platforms with coarse time resolutions, ORing time values with one to ensure they are non-zero +is crude, and effectively halves the time resolution. The more selective NonZeroTime() function +only nudges the time value to 1 if the interval calculation happens to result in the value zero. + +Revision 1.219 2004/11/25 01:00:56 cheshire +Checkin 1.217 not necessary + +Revision 1.218 2004/11/24 20:27:19 cheshire +Add missing "err" parameter in LogMsg() call + +Revision 1.217 2004/11/24 17:55:01 ksekar +Added log message clarifying For unicast operations, verify that service types are legal + +Revision 1.216 2004/11/24 00:10:44 cheshire + For unicast operations, verify that service types are legal + +Revision 1.215 2004/11/23 22:33:01 cheshire + Remove temporary workaround code for iChat + +Revision 1.214 2004/11/23 22:13:59 cheshire + Subtype advertising broken for Mach API + +Revision 1.213 2004/11/23 06:12:55 cheshire + Update wording for name conflict dialogs + +Revision 1.212 2004/11/23 05:15:37 cheshire + Computer Name in use message garbled + +Revision 1.211 2004/11/23 05:00:41 cheshire + Name conflict log message should not have ".local" appended + +Revision 1.210 2004/11/03 03:45:17 cheshire + mDNSResponder does not inform user of Computer Name collisions + +Revision 1.209 2004/11/03 02:25:50 cheshire + Conflict for Computer Name should update *all* empty string services, not just the one with the conflict + +Revision 1.208 2004/11/03 01:54:14 cheshire +Update debugging messages + +Revision 1.207 2004/11/02 23:58:19 cheshire + mDNSResponder does not inform user of name collisions + +Revision 1.206 2004/10/28 02:40:47 cheshire +Add log message to confirm receipt of SIGUSR1 (simulate network configuration change event) + +Revision 1.205 2004/10/28 02:21:01 cheshire + Improve mDNSResponder signal handling +Added SIGHUP as a way to do a forced restart of the daemon (better than kill -9) +Added SIGUSR1 to simulate a network change notification from System Configuration Framework + +Revision 1.204 2004/10/27 01:57:21 cheshire +Add check of m->p->InterfaceList + +Revision 1.203 2004/10/26 04:31:44 cheshire +Rename CountSubTypes() as ChopSubTypes() + +Revision 1.202 2004/10/26 01:29:18 cheshire +Use "#if 0" instead of commenting out code + +Revision 1.201 2004/10/25 21:41:39 ksekar + wide-area name conflicts can cause crash + +Revision 1.200 2004/10/22 01:03:55 cheshire + select() says data is waiting; recvfrom() says there is no data +Log error message if attempt to remap stdin/stdout/stderr to /dev/null fails + +Revision 1.199 2004/10/19 21:33:19 cheshire + Cannot resolve non-local registrations using the mach API +Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name +doesn't force multicast unless you set this flag to indicate explicitly that this is what you want + +Revision 1.198 2004/10/15 23:00:18 ksekar + Need to update LLQs on location changes + +Revision 1.197 2004/10/12 23:38:59 ksekar + remove unnecessary log message + +Revision 1.196 2004/10/04 05:56:04 cheshire + mDNSResponder doesn't respond to certain AirPort changes + +Revision 1.195 2004/09/30 00:24:59 ksekar + Dynamically update default registration domains on config change + +Revision 1.194 2004/09/26 23:20:35 ksekar + Allow default registrations in multiple wide-area domains + +Revision 1.193 2004/09/23 23:35:27 cheshire +Update error message + +Revision 1.192 2004/09/21 23:40:12 ksekar + mDNSResponder to return errors on NAT traversal failure + +Revision 1.191 2004/09/21 21:05:12 cheshire +Move duplicate code out of mDNSMacOSX/daemon.c and mDNSPosix/PosixDaemon.c, +into mDNSShared/uds_daemon.c + +Revision 1.190 2004/09/21 19:51:15 cheshire +Move "Starting time value" message from mDNS.c to mDNSMacOSX/daemon.c + +Revision 1.189 2004/09/21 18:17:23 cheshire + Add version info to mDNSResponder + +Revision 1.188 2004/09/20 21:45:27 ksekar +Mach IPC cleanup + +Revision 1.187 2004/09/17 01:08:52 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.186 2004/09/16 00:24:49 cheshire + Fix unsafe use of mDNSPlatformTimeNow() + +Revision 1.185 2004/08/25 02:01:45 cheshire + Need to be able to get status of Dynamic DNS Host Name Update + +Revision 1.184 2004/08/19 19:04:12 ksekar +: mDNSResponder crashes when adding a record to a service + +Revision 1.183 2004/08/14 03:22:42 cheshire + Dynamic DNS UI <-> mDNSResponder glue +Add GetUserSpecifiedDDNSName() routine +Convert ServiceRegDomain to domainname instead of C string +Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs + +Revision 1.182 2004/08/13 23:57:59 cheshire +Get rid of non-portable "_UNUSED" + +Revision 1.181 2004/08/11 02:02:26 cheshire +Remove "mDNS *globalInstance" parameter from udsserver_init(); +Move CheckForDuplicateRegistrations to uds_daemon.c + +Revision 1.180 2004/07/13 21:24:25 rpantos +Fix for . + +Revision 1.179 2004/06/19 00:02:54 cheshire +Restore fix for Should not allow empty string for resolve domain + +Revision 1.178 2004/06/18 19:10:00 cheshire + Current method of doing subtypes causes name collisions + +Revision 1.177 2004/06/16 23:14:46 ksekar + Remove fix for Should not allow empty string for resolve domain + +Revision 1.176 2004/06/11 20:27:42 cheshire +Rename "SocketRef" as "cfs" to avoid conflict with other plaforms + Revision 1.175 2004/06/10 20:23:21 cheshire Also list interfaces in SIGINFO output @@ -77,7 +246,7 @@ Unified list copy/free code. Added symetric list for Revision 1.164 2004/05/12 22:03:08 ksekar Made GetSearchDomainList a true platform-layer call (declaration moved -from mDNSMacOSX.h to mDNSClientAPI.h), impelemted to return "local" +from mDNSMacOSX.h to mDNSEmbeddedAPI.h), impelemted to return "local" only on non-OSX platforms. Changed call to return a copy of the list to avoid shared memory issues. Added a routine to free the list. @@ -251,10 +420,10 @@ standard error file descriptors to /dev/null just like any other well behaved daemon Revision 1.112 2003/06/25 23:42:19 ksekar -: Feature: New Rendezvous APIs (#7875) +: Feature: New DNS-SD APIs (#7875) Reviewed by: Stuart Cheshire Added files necessary to implement Unix domain sockets based enhanced -Rendezvous APIs, and integrated with existing Mach-port based daemon. +DNS-SD APIs, and integrated with existing Mach-port based daemon. Revision 1.111 2003/06/11 01:02:43 cheshire mDNSResponder binary compatibility @@ -336,11 +505,12 @@ Add $Log header #include #include #include +#include #include "DNSServiceDiscoveryRequestServer.h" #include "DNSServiceDiscoveryReply.h" -#include "mDNSClientAPI.h" // Defines the interface to the client layer above +#include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above #include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform #include "uds_daemon.h" // Interface to the server side implementation of dns_sd.h @@ -355,26 +525,21 @@ Add $Log header // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4" // To expand "version" to its value before making the string, use STRINGIFY(version) instead -#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s +#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) -// convenience definition -#define _UNUSED __attribute__ ((unused)) - //************************************************************************************************************* // Globals #define LOCAL_DEFAULT_REG 1 // empty string means register in the local domain #define DEFAULT_REG_DOMAIN "apple.com." // used if the above flag is turned off -mDNSexport mDNS mDNSStorage; static mDNS_PlatformSupport PlatformStorage; #define RR_CACHE_SIZE 64 static CacheRecord rrcachestorage[RR_CACHE_SIZE]; static const char kmDNSBootstrapName[] = "com.apple.mDNSResponderRestart"; static mach_port_t client_death_port = MACH_PORT_NULL; -static mach_port_t exit_m_port = MACH_PORT_NULL; -static mach_port_t info_m_port = MACH_PORT_NULL; +static mach_port_t signal_port = MACH_PORT_NULL; static mach_port_t server_priv_port = MACH_PORT_NULL; // mDNS Mach Message Timeout, in milliseconds. @@ -411,17 +576,20 @@ typedef struct DNSServiceBrowser_struct DNSServiceBrowser; typedef struct DNSServiceBrowserQuestion { - struct DNSServiceBrowserQuestion *next; - DNSQuestion q; + struct DNSServiceBrowserQuestion *next; + DNSQuestion q; + domainname domain; } DNSServiceBrowserQuestion; struct DNSServiceBrowser_struct { DNSServiceBrowser *next; mach_port_t ClientMachPort; - DNSServiceBrowserQuestion *qlist; + DNSServiceBrowserQuestion *qlist; DNSServiceBrowserResult *results; mDNSs32 lastsuccess; + mDNSBool DefaultDomain; // was the browse started on an explicit domain? + domainname type; // registration type }; typedef struct DNSServiceResolver_struct DNSServiceResolver; @@ -434,33 +602,41 @@ struct DNSServiceResolver_struct mDNSs32 ReportTime; }; - -typedef struct ExtraRecordRef +// A single registered service: ServiceRecordSet + bookkeeping +// Note that we duplicate some fields from parent DNSServiceRegistration object +// to facilitate cleanup, when instances and parent may be deallocated at different times. +typedef struct ServiceInstance { - ExtraResourceRecord *localRef; // extra added to .local service - ExtraResourceRecord *globalRef; // extra added to default global service (may be NULL) - struct ExtraRecordRef *next; - } ExtraRecordRef; - -typedef struct DNSServiceRegistration_struct DNSServiceRegistration; -struct DNSServiceRegistration_struct - { - DNSServiceRegistration *next; + struct ServiceInstance *next; mach_port_t ClientMachPort; - mDNSBool autoname; - mDNSBool autorenameLS; - mDNSBool autorenameGS; - mDNSBool deallocate; // gs and ls (below) will receive separate MemFree callbacks, - // the latter of which must deallocate the wrapper structure. - // ls MemFree callback: if (!gs) free wrapper; else set deallocate flag - // gs callback: if (deallocate) free wrapper; else free (gs), gs = NULL - domainlabel name; - ExtraRecordRef *ExtraRefList; - ServiceRecordSet *gs; // default "global" (wide area) service (may be NULL) - ServiceRecordSet ls; // .local service (also used if client passes an explicit domain) + mDNSBool autoname; // Set if this name is tied to the Computer Name + mDNSBool autorename; // Set if we just got a name conflict and now need to automatically pick a new name + domainlabel name; + domainname domain; + ServiceRecordSet srs; // Don't add any fields after ServiceRecordSet. // This is where the implicit extra space goes if we allocate an oversized ServiceRecordSet object - }; + } ServiceInstance; + +// A client-created service. May reference several ServiceInstance objects if default +// settings cause registration in multiple domains. +typedef struct DNSServiceRegistration + { + struct DNSServiceRegistration *next; + mach_port_t ClientMachPort; + mDNSBool DefaultDomain; + mDNSBool autoname; + size_t rdsize; + int NumSubTypes; + char regtype[MAX_ESCAPED_DOMAIN_NAME]; // for use in AllocateSubtypes + domainlabel name; // used only if autoname is false + domainname type; + mDNSIPPort port; + unsigned char txtinfo[1024]; + size_t txt_len; + uint32_t NextRef; + ServiceInstance *regs; + } DNSServiceRegistration; static DNSServiceDomainEnumeration *DNSServiceDomainEnumerationList = NULL; static DNSServiceBrowser *DNSServiceBrowserList = NULL; @@ -484,7 +660,8 @@ static void validatelists(mDNS *const m) CacheRecord *cr; DNSQuestion *q; mDNSu32 slot; - + NetworkInterfaceInfoOSX *i; + for (e = DNSServiceDomainEnumerationList; e; e=e->next) if (e->ClientMachPort == 0 || e->ClientMachPort == (mach_port_t)~0) LogMsg("!!!! DNSServiceDomainEnumerationList: %p is garbage (%X) !!!!", e, e->ClientMachPort); @@ -514,9 +691,13 @@ static void validatelists(mDNS *const m) LogMsg("!!!! Questions list: %p is garbage (%lX) !!!!", q, q->ThisQInterval); for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) - for (cr = mDNSStorage.rrcache_hash[slot]; cr; cr=cr->next) + for (cr = m->rrcache_hash[slot]; cr; cr=cr->next) if (cr->resrec.RecordType == 0 || cr->resrec.RecordType == 0xFF) LogMsg("!!!! Cache slot %lu: %p is garbage (%X) !!!!", slot, rr, rr->resrec.RecordType); + + for (i = m->p->InterfaceList; i; i = i->next) + if (!i->ifa_name) + LogMsg("!!!! InterfaceList: %p is garbage !!!!", i); } void *mallocL(char *msg, unsigned int size) @@ -525,7 +706,7 @@ void *mallocL(char *msg, unsigned int size) if (!mem) { LogMsg("malloc( %s : %d ) failed", msg, size); - return(NULL); + return(NULL); } else { @@ -563,46 +744,26 @@ void freeL(char *msg, void *x) //************************************************************************************************************* // Client Death Detection -mDNSlocal void FreeSRS(ServiceRecordSet *s) +mDNSlocal void FreeServiceInstance(ServiceInstance *x) { - while (s->Extras) + ServiceRecordSet *s = &x->srs; + ExtraResourceRecord *e = x->srs.Extras, *tmp; + + while(e) { - ExtraResourceRecord *extras = s->Extras; - s->Extras = s->Extras->next; - if (extras->r.resrec.rdata != &extras->r.rdatastorage) - freeL("Extra RData", extras->r.resrec.rdata); - freeL("ExtraResourceRecord", extras); + e->r.RecordContext = e; + tmp = e; + e = e->next; + FreeExtraRR(&mDNSStorage, &tmp->r, mStatus_MemFree); } - + if (s->RR_TXT.resrec.rdata != &s->RR_TXT.rdatastorage) freeL("TXT RData", s->RR_TXT.resrec.rdata); - if (s->SubTypes) freeL("ServiceSubTypes", s->SubTypes); + if (s->SubTypes) freeL("ServiceSubTypes", s->SubTypes); + freeL("ServiceInstance", x); } -mDNSlocal void FreeDNSServiceRegistration(ServiceRecordSet *srs) - { - DNSServiceRegistration *x = srs->ServiceContext; - ExtraRecordRef *ref, *fptr; - - FreeSRS(srs); - if (srs == x->gs) - { - freeL("DNSServiceRegistration GlobalService", srs); - x->gs = NULL; - } - else x->deallocate = mDNStrue; - - if (x->deallocate && !x->gs) - { - ref = x->ExtraRefList; - while (ref) - { fptr = ref; ref = ref->next; freeL("ExtraRecordRef", fptr); } - freeL("DNSServiceRegistration", x); - } - } - - // AbortClient finds whatever client is identified by the given Mach port, // stops whatever operation that client was doing, and frees its memory. // In the case of a service registration, the actual freeing may be deferred @@ -631,7 +792,7 @@ mDNSlocal void AbortClient(mach_port_t ClientMachPort, void *m) while (*b && (*b)->ClientMachPort != ClientMachPort) b = &(*b)->next; if (*b) { - DNSServiceBrowser *x = *b; + DNSServiceBrowser *x = *b; DNSServiceBrowserQuestion *freePtr, *qptr = x->qlist; *b = (*b)->next; while (qptr) @@ -641,7 +802,7 @@ mDNSlocal void AbortClient(mach_port_t ClientMachPort, void *m) else LogOperation("%5d: DNSServiceBrowser(%##s) STOP", ClientMachPort, qptr->q.qname.c); mDNS_StopBrowse(&mDNSStorage, &qptr->q); freePtr = qptr; - qptr = qptr->next; + qptr = qptr->next; freeL("DNSServiceBrowserQuestion", freePtr); } while (x->results) @@ -670,32 +831,27 @@ mDNSlocal void AbortClient(mach_port_t ClientMachPort, void *m) while (*r && (*r)->ClientMachPort != ClientMachPort) r = &(*r)->next; if (*r) { + ServiceInstance *si = NULL; DNSServiceRegistration *x = *r; *r = (*r)->next; - x->autorenameLS = mDNSfalse; - x->autorenameGS = mDNSfalse; - if (m && m != x) - { - LogMsg("%5d: DNSServiceRegistration(%##s, %u) STOP; WARNING m %p != x %p", ClientMachPort, x->ls.RR_SRV.resrec.name.c, SRS_PORT(&x->ls), m, x); - if (x->gs) LogMsg("%5d: DNSServiceRegistration(%##s, %u) STOP; WARNING m %p != x %p", ClientMachPort, x->gs->RR_SRV.resrec.name.c, SRS_PORT(x->gs), m, x); - } - else - { - LogOperation("%5d: DNSServiceRegistration(%##s, %u) STOP", ClientMachPort, x->ls.RR_SRV.resrec.name.c, SRS_PORT(&x->ls)); - if (x->gs) LogOperation("%5d: DNSServiceRegistration(%##s, %u) STOP", ClientMachPort, x->gs->RR_SRV.resrec.name.c, SRS_PORT(x->gs)); - } - // If mDNS_DeregisterService() returns mStatus_NoError, that means that the service was found in the list, - // is sending its goodbye packet, and we'll get an mStatus_MemFree message when we can free the memory. - // If mDNS_DeregisterService() returns an error, it means that the service had already been removed from - // the list, so we should go ahead and free the memory right now - if (x->gs && mDNS_DeregisterService(&mDNSStorage, x->gs)) + + si = x->regs; + while (si) { - // Deregister returned an error, so we free immediately - FreeSRS(x->gs); - x->gs = NULL; + ServiceInstance *instance = si; + si = si->next; + instance->autorename = mDNSfalse; + if (m && m != x) LogMsg("%5d: DNSServiceRegistration(%##s, %u) STOP; WARNING m %p != x %p", ClientMachPort, instance->srs.RR_SRV.resrec.name.c, SRS_PORT(&instance->srs), m, x); + else LogOperation("%5d: DNSServiceRegistration(%##s, %u) STOP", ClientMachPort, instance->srs.RR_SRV.resrec.name.c, SRS_PORT(&instance->srs)); + + // If mDNS_DeregisterService() returns mStatus_NoError, that means that the service was found in the list, + // is sending its goodbye packet, and we'll get an mStatus_MemFree message when we can free the memory. + // If mDNS_DeregisterService() returns an error, it means that the service had already been removed from + // the list, so we should go ahead and free the memory right now + if (mDNS_DeregisterService(&mDNSStorage, &instance->srs)) FreeServiceInstance(instance); // FreeServiceInstance invalidates pointer } - if (mDNS_DeregisterService(&mDNSStorage, &x->ls)) - FreeDNSServiceRegistration(&x->ls); + x->regs = NULL; + freeL("DNSServiceRegistration", x); return; } @@ -725,8 +881,8 @@ mDNSlocal void AbortClientWithLogMessage(mach_port_t c, char *reason, char *msg, else if (l) LogMsg("%5d: Resolver(%##s) %s%s", c, l->i.name.c, reason, msg); else if (r) { - LogMsg("%5d: Registration(%##s) %s%s", c, r->ls.RR_SRV.resrec.name.c, reason, msg); - if (r->gs) LogMsg("%5d: Registration(%##s) %s%s", c, r->gs->RR_SRV.resrec.name.c, reason, msg); + ServiceInstance *si; + for (si = r->regs; si; si = si->next) LogMsg("%5d: Registration(%##s) %s%s", c, si->srs.RR_SRV.resrec.name.c, reason, msg); } else LogMsg("%5d: (%s) %s, but no record of client can be found!", c, reason, msg); @@ -740,7 +896,7 @@ mDNSlocal mDNSBool CheckForExistingClient(mach_port_t c) DNSServiceResolver *l = DNSServiceResolverList; DNSServiceRegistration *r = DNSServiceRegistrationList; DNSServiceBrowserQuestion *qptr; - + while (e && e->ClientMachPort != c) e = e->next; while (b && b->ClientMachPort != c) b = b->next; while (l && l->ClientMachPort != c) l = l->next; @@ -752,7 +908,7 @@ mDNSlocal mDNSBool CheckForExistingClient(mach_port_t c) LogMsg("%5d: Browser(%##s) already exists!", c, qptr->q.qname.c); } if (l) LogMsg("%5d: Resolver(%##s) already exists!", c, l->i.name.c); - if (r) LogMsg("%5d: Registration(%##s) already exists!", c, r->ls.RR_SRV.resrec.name.c); + if (r) LogMsg("%5d: Registration(%##s) already exists!", c, r->regs ? r->regs->srs.RR_SRV.resrec.name.c : NULL); return(e || b || l || r); } @@ -841,7 +997,7 @@ mDNSexport kern_return_t provide_DNSServiceDomainEnumerationCreate_rpc(mach_port x->ClientMachPort = client; x->next = DNSServiceDomainEnumerationList; DNSServiceDomainEnumerationList = x; - + // Generate initial response verbosedebugf("%5d: Enumerate %s Domains", client, regDom ? "Registration" : "Browsing"); // We always give local. as the initial default browse domain, and then look for more @@ -853,7 +1009,7 @@ mDNSexport kern_return_t provide_DNSServiceDomainEnumerationCreate_rpc(mach_port err = mDNS_GetDomains(&mDNSStorage, &x->dom, dt1, NULL, mDNSInterface_LocalOnly, FoundDomain, x); if (!err) err = mDNS_GetDomains(&mDNSStorage, &x->def, dt2, NULL, mDNSInterface_LocalOnly, FoundDomain, x); if (err) { AbortClient(client, x); errormsg = "mDNS_GetDomains"; goto fail; } - + // Succeeded: Wrap up and return LogOperation("%5d: DNSServiceDomainEnumeration(%##s) START", client, x->dom.qname.c); EnableDeathNotificationForClient(client, x); @@ -870,10 +1026,10 @@ fail: mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) { (void)m; // Unused - + if (answer->rrtype != kDNSType_PTR) { LogMsg("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer->rrtype); return; } - + domainlabel name; domainname type, domain; if (!DeconstructServiceName(&answer->rdata->u.name, &name, &type, &domain)) @@ -885,7 +1041,7 @@ mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const Resourc DNSServiceBrowserResult *x = mallocL("DNSServiceBrowserResult", sizeof(*x)); if (!x) { LogMsg("FoundInstance: Failed to allocate memory for result %##s", answer->rdata->u.name.c); return; } - + verbosedebugf("FoundInstance: %s %##s", AddRecord ? "Add" : "Rmv", answer->rdata->u.name.c); AssignDomainName(x->result, answer->rdata->u.name); if (AddRecord) @@ -899,6 +1055,72 @@ mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const Resourc *p = x; } +mDNSlocal mStatus AddDomainToBrowser(DNSServiceBrowser *browser, const domainname *d) + { + mStatus err = mStatus_NoError; + DNSServiceBrowserQuestion *ptr, *question = NULL; + + for (ptr = browser->qlist; ptr; ptr = ptr->next) + { + if (SameDomainName(&ptr->q.qname, d)) + { debugf("Domain %##s already contained in browser", d->c); return mStatus_AlreadyRegistered; } + } + + question = mallocL("DNSServiceBrowserQuestion", sizeof(DNSServiceBrowserQuestion)); + if (!question) { LogMsg("Error: malloc"); return mStatus_NoMemoryErr; } + AssignDomainName(question->domain, *d); + question->next = browser->qlist; + browser->qlist = question; + LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", browser->ClientMachPort, browser->type.c, d->c); + err = mDNS_StartBrowse(&mDNSStorage, &question->q, &browser->type, d, mDNSInterface_Any, mDNSfalse, FoundInstance, browser); + if (err) LogMsg("Error: AddDomainToBrowser: mDNS_StartBrowse %d", err); + return err; + } + +mDNSexport void DefaultBrowseDomainChanged(const domainname *d, mDNSBool add) + { + DNSServiceBrowser *ptr; + + debugf("%s default browse domain %##s", add ? "Adding" : "Removing", d->c); + for (ptr = DNSServiceBrowserList; ptr; ptr = ptr->next) + { + if (ptr->DefaultDomain) + { + if (add) + { + mStatus err = AddDomainToBrowser(ptr, d); + if (err && err != mStatus_AlreadyRegistered) LogMsg("Default browse in domain %##s for client %5d failed. Continuing", d, ptr->ClientMachPort); + } + else + { +#if 0 + /* + * By cancelling the browse immediately, we may lose remove events. + * Instead, we allow the browse to run. If our previous results are no longer valid (e.g. because we + * moved out from behind a firewall) we will get remove events for those names. + */ + // find the question for this domain + DNSServiceBrowserQuestion *q = ptr->qlist, *prev = NULL; + while (q) + { + if (SameDomainName(&q->domain, d)) + { + if (prev) prev->next = q->next; + else ptr->qlist = q->next; + mDNS_StopBrowse(&mDNSStorage, &q->q); + freeL("DNSServiceBrowserQuestion", q); + break; + } + prev = q; + q = q->next; + } + if (!q) LogMsg("Requested removal of default domain %##s not in client %5d's list", d->c, ptr->ClientMachPort); +#endif + } + } + } + } + mDNSexport kern_return_t provide_DNSServiceBrowserCreate_rpc(mach_port_t unusedserver, mach_port_t client, DNSCString regtype, DNSCString domain) { @@ -907,76 +1129,79 @@ mDNSexport kern_return_t provide_DNSServiceBrowserCreate_rpc(mach_port_t unuseds mStatus err = mStatus_NoError; const char *errormsg = "Unknown"; DNameListElem *SearchDomains = NULL, *sdPtr; - DNSServiceBrowserQuestion *qptr; - + if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; } // Check other parameters domainname t, d; - if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Illegal regtype"; goto badparam; } + t.c[0] = 0; + mDNSs32 NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes + if (NumSubTypes < 0 || NumSubTypes > 1) { errormsg = "Bad Service SubType"; goto badparam; } + if (NumSubTypes == 1 && !AppendDNSNameString(&t, regtype + strlen(regtype) + 1)) + { errormsg = "Bad Service SubType"; goto badparam; } + if (!regtype[0] || !AppendDNSNameString(&t, regtype)) { errormsg = "Illegal regtype"; goto badparam; } + domainname temp; + if (!MakeDomainNameFromDNSNameString(&temp, regtype)) { errormsg = "Illegal regtype"; goto badparam; } + if (temp.c[0] > 15 && (!domain || domain[0] == 0)) domain = "local."; // For over-long service types, we only allow domain "local" // Allocate memory, and handle failure DNSServiceBrowser *x = mallocL("DNSServiceBrowser", sizeof(*x)); if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } - + // Set up object, and link into list + AssignDomainName(x->type, t); x->ClientMachPort = client; x->results = NULL; x->lastsuccess = 0; - x->qlist = NULL; + x->qlist = NULL; x->next = DNSServiceBrowserList; DNSServiceBrowserList = x; - //!!!KRS browse locally for ichat - if (!domain[0] && (!strcmp(regtype, "_ichat._tcp.") || !strcmp(regtype, "_presence._tcp."))) - domain = "local."; - if (domain[0]) { // Start browser for an explicit domain - x->qlist = mallocL("DNSServiceBrowserQuestion", sizeof(DNSServiceBrowserQuestion)); - x->qlist->next = NULL; - if (!x->qlist) { err = mStatus_UnknownErr; AbortClient(client, x); errormsg = "malloc"; goto fail; } - - if (!MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Illegal domain"; goto badparam; } - LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", client, t.c, d.c); - err = mDNS_StartBrowse(&mDNSStorage, &x->qlist->q, &t, &d, mDNSInterface_Any, FoundInstance, x); - if (err) { AbortClient(client, x); errormsg = "mDNS_StartBrowse"; goto fail; } + x->DefaultDomain = mDNSfalse; + if (!MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Illegal domain"; goto badparam; } + err = AddDomainToBrowser(x, &d); + if (err) { AbortClient(client, x); errormsg = "AddDomainToBrowser"; goto fail; } } else { // Start browser on all domains + x->DefaultDomain = mDNStrue; SearchDomains = mDNSPlatformGetSearchDomainList(); if (!SearchDomains) { AbortClient(client, x); errormsg = "GetSearchDomainList"; goto fail; } for (sdPtr = SearchDomains; sdPtr; sdPtr = sdPtr->next) { - qptr = mallocL("DNSServiceBrowserQuestion", sizeof(DNSServiceBrowserQuestion)); - if (!qptr) { err = mStatus_UnknownErr; AbortClient(client, x); errormsg = "malloc"; goto fail; } - qptr->next = x->qlist; - x->qlist = qptr; - LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", client, t.c, sdPtr->name.c); - err = mDNS_StartBrowse(&mDNSStorage, &qptr->q, &t, &sdPtr->name, mDNSInterface_Any, FoundInstance, x); - if (err) { AbortClient(client, x); errormsg = "mDNS_StartBrowse"; goto fail; } - } + err = AddDomainToBrowser(x, &sdPtr->name); + if (err) + { + // only terminally bail if .local fails + if (!SameDomainName(&localdomain, &sdPtr->name)) + LogMsg("Default browse in domain %##s failed. Continuing", sdPtr->name.c); + else { AbortClient(client, x); errormsg = "AddDomainToBrowser"; goto fail; } + } + } } + // Succeeded: Wrap up and return EnableDeathNotificationForClient(client, x); mDNS_FreeDNameList(SearchDomains); return(mStatus_NoError); - + badparam: err = mStatus_BadParamErr; fail: LogMsg("%5d: DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%ld)", client, regtype, domain, errormsg, err); if (SearchDomains) mDNS_FreeDNameList(SearchDomains); return(err); - } + } //************************************************************************************************************* // Resolve Service Info - -mDNSlocal void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query) + + mDNSlocal void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query) { kern_return_t status; DNSServiceResolver *x = (DNSServiceResolver *)query->ServiceInfoQueryContext; @@ -1013,7 +1238,7 @@ mDNSlocal void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query) sin6->sin6_addr = *(struct in6_addr*)&ifx->ifinfo.ip.ip.v6; sin6->sin6_scope_id = ifx->scope_id; } - + if (query->info->ip.type == mDNSAddrType_IPv4) { struct sockaddr_in *sin = (struct sockaddr_in*)&address; @@ -1049,7 +1274,7 @@ mDNSlocal void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query) } } cstring[i-1] = 0; // Put the terminating NULL on the end - + LogOperation("%5d: DNSServiceResolver(%##s) -> %#a:%u", x->ClientMachPort, x->i.name.c, &query->info->ip, mDNSVal16(query->info->port)); status = DNSServiceResolverReply_rpc(x->ClientMachPort, @@ -1084,11 +1309,7 @@ mDNSexport kern_return_t provide_DNSServiceResolverResolve_rpc(mach_port_t unuse x->ClientMachPort = client; x->i.InterfaceID = mDNSInterface_Any; x->i.name = srv; - x->ReportTime = (mDNSPlatformTimeNow() + 130 * mDNSPlatformOneSecond) | 1; - // Don't report errors for old iChat ("_ichat._tcp") service. - // New iChat ("_presence._tcp") uses DNSServiceQueryRecord() (from /usr/include/dns_sd.h) instead, - // and so should other applications that have valid reasons to be doing ongoing record monitoring. - if (SameDomainLabel(t.c, (mDNSu8*)"\x6_ichat")) x->ReportTime = 0; + x->ReportTime = NonZeroTime(mDNS_TimeNow(&mDNSStorage) + 130 * mDNSPlatformOneSecond); x->next = DNSServiceResolverList; DNSServiceResolverList = x; @@ -1101,7 +1322,7 @@ mDNSexport kern_return_t provide_DNSServiceResolverResolve_rpc(mach_port_t unuse EnableDeathNotificationForClient(client, x); return(mStatus_NoError); -badparam: +badparam: err = mStatus_BadParamErr; fail: LogMsg("%5d: DNSServiceResolve(\"%s\", \"%s\", \"%s\") failed: %s (%ld)", client, name, regtype, domain, errormsg, err); @@ -1111,113 +1332,186 @@ fail: //************************************************************************************************************* // Registration -mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result) +mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay) + { + m->p->NotifyUser = NonZeroTime(m->timenow + delay); + } + +mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const srs, mStatus result) { - DNSServiceRegistration *x = (DNSServiceRegistration*)sr->ServiceContext; + ServiceInstance *si = (ServiceInstance*)srs->ServiceContext; if (result == mStatus_NoError) { kern_return_t status; - LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Registered", x->ClientMachPort, sr->RR_SRV.resrec.name.c, SRS_PORT(sr)); - status = DNSServiceRegistrationReply_rpc(x->ClientMachPort, result, MDNS_MM_TIMEOUT); + LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Registered", si->ClientMachPort, srs->RR_SRV.resrec.name.c, SRS_PORT(srs)); + status = DNSServiceRegistrationReply_rpc(si->ClientMachPort, result, MDNS_MM_TIMEOUT); if (status == MACH_SEND_TIMED_OUT) - AbortBlockedClient(x->ClientMachPort, "registration success", x); + AbortBlockedClient(si->ClientMachPort, "registration success", si); + if (si->autoname && CountPeerRegistrations(m, srs) == 0) + RecordUpdatedNiceLabel(m, 0); // Successfully got new name, tell user immediately } else if (result == mStatus_NameConflict) { - LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Conflict", x->ClientMachPort, sr->RR_SRV.resrec.name.c, SRS_PORT(sr)); + LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Conflict", si->ClientMachPort, srs->RR_SRV.resrec.name.c, SRS_PORT(srs)); // Note: By the time we get the mStatus_NameConflict message, the service is already deregistered // and the memory is free, so we don't have to wait for an mStatus_MemFree message as well. - if (x->autoname) - mDNS_RenameAndReregisterService(m, sr, mDNSNULL); + if (si->autoname && CountPeerRegistrations(m, srs) == 0) + { + // On conflict for an autoname service, rename and reregister *all* autoname services + IncrementLabelSuffix(&m->nicelabel, mDNStrue); + m->MainCallback(m, mStatus_ConfigChanged); + } + else if (si->autoname) + { + mDNS_RenameAndReregisterService(m, srs, mDNSNULL); + return; + } else { // If we get a name conflict, we tell the client about it, and then they are expected to dispose // of their registration in the usual way (which we will catch via client death notification). // If the Mach queue is full, we forcibly abort the client immediately. - kern_return_t status = DNSServiceRegistrationReply_rpc(x->ClientMachPort, result, MDNS_MM_TIMEOUT); + kern_return_t status = DNSServiceRegistrationReply_rpc(si->ClientMachPort, result, MDNS_MM_TIMEOUT); if (status == MACH_SEND_TIMED_OUT) - AbortBlockedClient(x->ClientMachPort, "registration conflict", x); + AbortBlockedClient(si->ClientMachPort, "registration conflict", NULL); } } - + else if (result == mStatus_MemFree) { - mDNSBool *autorename = (sr == &x->ls ? &x->autorenameLS : &x->autorenameGS); - if (*autorename) + if (si->autorename) { - debugf("RegCallback renaming %#s to %#s", x->name.c, mDNSStorage.nicelabel.c); - *autorename = mDNSfalse; - x->name = mDNSStorage.nicelabel; - mDNS_RenameAndReregisterService(m, sr, &x->name); + debugf("RegCallback renaming %#s to %#s", si->name.c, m->nicelabel.c); + si->autorename = mDNSfalse; + si->name = m->nicelabel; + mDNS_RenameAndReregisterService(m, srs, &si->name); } else { - // SANITY CHECK: Should only get mStatus_MemFree as a result of calling mDNS_DeregisterService() - // and should only get it with x->autorename false if we've already removed the record from our - // list, but this check is just to make sure... - DNSServiceRegistration **r = &DNSServiceRegistrationList; - while (*r && *r != x) r = &(*r)->next; - if (*r) + // SANITY CHECK: make sure service instance is no longer in any ServiceRegistration's list + DNSServiceRegistration *r; + for (r = DNSServiceRegistrationList; r; r = r->next) { - LogMsg("RegCallback: %##s Still in DNSServiceRegistration list; removing now", sr->RR_SRV.resrec.name.c); - *r = (*r)->next; - } + ServiceInstance *sp = r->regs, *prev = NULL; + while (sp) + { + if (sp == si) + { + LogMsg("RegCallback: %##s Still in DNSServiceRegistration list; removing now", srs->RR_SRV.resrec.name.c); + if (prev) prev->next = sp->next; + else r->regs = sp->next; + break; + } + prev = sp; + sp = sp->next; + } + } // END SANITY CHECK - LogOperation("%5d: DNSServiceRegistration(%##s, %u) Memory Free", x->ClientMachPort, sr->RR_SRV.resrec.name.c, SRS_PORT(sr)); - FreeDNSServiceRegistration(sr); + FreeServiceInstance(si); } } + + else if (result != mStatus_NATTraversal) + LogMsg("%5d: DNSServiceRegistration(%##s, %u) Unknown Result %ld", si->ClientMachPort, srs->RR_SRV.resrec.name.c, SRS_PORT(srs), result); + } + +mDNSlocal mStatus AddServiceInstance(DNSServiceRegistration *x, const domainname *domain) + { + mStatus err = 0; + ServiceInstance *si = NULL; + AuthRecord *SubTypes = NULL; + + for (si = x->regs; si; si = si->next) + { + if (SameDomainName(&si->domain, domain)) + { LogMsg("Requested addition of domain %##s already in list", domain->c); return mStatus_AlreadyRegistered; } + } + SubTypes = AllocateSubTypes(x->NumSubTypes, x->regtype); + if (x->NumSubTypes && !SubTypes) return mStatus_NoMemoryErr; + + si = mallocL("ServiceInstance", sizeof(*si) - sizeof(RDataBody) + x->rdsize); + if (!si) return mStatus_NoMemoryErr; + + si->ClientMachPort = x->ClientMachPort; + si->autorename = mDNSfalse; + si->autoname = x->autoname; + si->name = x->autoname ? mDNSStorage.nicelabel : x->name; + si->domain = *domain; + + err = mDNS_RegisterService(&mDNSStorage, &si->srs, &si->name, &x->type, domain, NULL, x->port, x->txtinfo, x->txt_len, SubTypes, x->NumSubTypes, mDNSInterface_Any, RegCallback, si); + if (!err) + { + si->next = x->regs; + x->regs = si; + } else { - LogMsg("%5d: DNSServiceRegistration(%##s, %u) Unknown Result %ld", - x->ClientMachPort, sr->RR_SRV.resrec.name.c, SRS_PORT(sr), result); - if (sr == x->gs) { freeL("RegCallback - ServiceRecordSet", x->gs); x->gs = NULL; } + LogMsg("Error %d for registration of service in domain %##s", err, domain->c); + freeL("ServiceInstance", si); } + return err; } -mDNSlocal void CheckForDuplicateRegistrations(DNSServiceRegistration *x, domainname *srv, mDNSIPPort port) +mDNSexport void DefaultRegDomainChanged(const domainname *d, mDNSBool add) { - int count = 1; // Start with the one we're planning to register, then see if there are any more - AuthRecord *rr; - for (rr = mDNSStorage.ResourceRecords; rr; rr=rr->next) - if (rr->resrec.rrtype == kDNSType_SRV && - rr->resrec.rdata->u.srv.port.NotAnInteger == port.NotAnInteger && - SameDomainName(&rr->resrec.name, srv)) - count++; - - if (count > 1) - LogMsg("%5d: Client application registered %d identical instances of service %##s port %u.", - x->ClientMachPort, count, srv->c, mDNSVal16(port)); + DNSServiceRegistration *reg; + + LogMsg("%s default registration domain %##s", add ? "Adding" : "Removing", d->c); + for (reg = DNSServiceRegistrationList; reg; reg = reg->next) + { + if (reg->DefaultDomain) + { + if (add) + { + AddServiceInstance(reg, d); + } + else + { + ServiceInstance *si = reg->regs, *prev = NULL; + while (si) + { + if (SameDomainName(&si->domain, d)) + { + if (prev) prev->next = si->next; + else reg->regs = si->next; + if (mDNS_DeregisterService(&mDNSStorage, &si->srs)) + FreeServiceInstance(si); // only free memory synchronously on error + break; + } + prev = si; + si = si->next; + } + if (!si) LogMsg("Requested removal of default domain %##s not in client %5d's list", d, reg->ClientMachPort); + } + } + } } -// Pass NULL for x to allocate the structure (for local service). Call again w/ initialized x to add a global service. -mDNSlocal DNSServiceRegistration *RegisterService(mach_port_t client, DNSCString name, DNSCString regtype, DNSCString domain, - int notAnIntPort, DNSCString txtRecord, DNSServiceRegistration *x) +mDNSexport kern_return_t provide_DNSServiceRegistrationCreate_rpc(mach_port_t unusedserver, mach_port_t client, + DNSCString name, DNSCString regtype, DNSCString domain, IPPort IpPort, DNSCString txtRecord) { - ServiceRecordSet *srs = NULL; // record set to use in registration operation + (void)unusedserver; // Unused mStatus err = mStatus_NoError; const char *errormsg = "Unknown"; - + + // older versions of this code passed the port via mach IPC as an int. + // we continue to pass it as 4 bytes to maintain binary compatibility, + // but now ensure that the network byte order is preserved by using a struct + mDNSIPPort port; + port.b[0] = IpPort.bytes[2]; + port.b[1] = IpPort.bytes[3]; + + if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } + if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; } + // Check for sub-types after the service type - AuthRecord *SubTypes = mDNSNULL; - mDNSu32 i, NumSubTypes = 0; - char *comma = regtype; - while (*comma && *comma != ',') comma++; - if (*comma) // If we found a comma... - { - *comma = 0; // Overwrite the first comma with a nul - char *p = comma + 1; // Start scanning from the next character - while (*p) - { - if ( !(*p && *p != ',')) { errormsg = "Bad Service SubType"; goto badparam; } - while (*p && *p != ',') p++; - if (*p) *p++ = 0; - NumSubTypes++; - } - } + size_t reglen = strlen(regtype) + 1; + if (reglen > MAX_ESCAPED_DOMAIN_NAME) { errormsg = "reglen too long"; goto badparam; } + mDNSs32 NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes + if (NumSubTypes < 0) { errormsg = "Bad Service SubType"; goto badparam; } // Check other parameters domainlabel n; @@ -1225,14 +1519,10 @@ mDNSlocal DNSServiceRegistration *RegisterService(mach_port_t client, DNSCString domainname srv; if (!name[0]) n = mDNSStorage.nicelabel; else if (!MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; } - if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; } - - if (!MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Bad Domain"; goto badparam; } + if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; } + if (!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) { errormsg = "Bad Domain"; goto badparam; } if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; } - mDNSIPPort port; - port.NotAnInteger = notAnIntPort; - unsigned char txtinfo[1024] = ""; unsigned int data_len = 0; unsigned int size = sizeof(RDataBody); @@ -1264,116 +1554,148 @@ mDNSlocal DNSServiceRegistration *RegisterService(mach_port_t client, DNSCString if (size < data_len) size = data_len; - // Allocate memory, and handle failure - if (!x) - { - x = mallocL("DNSServiceRegistration", sizeof(*x) - sizeof(RDataBody) + size); - if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } - bzero(x, sizeof(*x) - sizeof(RDataBody) + size); - // Set up object, and link into list - x->ClientMachPort = client; - x->autoname = (!name[0]); - x->name = n; - x->next = DNSServiceRegistrationList; - DNSServiceRegistrationList = x; - srs = &x->ls; - } - else + // Some clients use mDNS for lightweight copy protection, registering a pseudo-service with + // a port number of zero. When two instances of the protected client are allowed to run on one + // machine, we don't want to see misleading "Bogus client" messages in syslog and the console. + if (port.NotAnInteger) { - x->gs = mallocL("DNSServiceRegistration GlobalService", sizeof(ServiceRecordSet) - sizeof(RDataBody) + size); - if (!x->gs) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } - srs = x->gs; - bzero(srs, sizeof(ServiceRecordSet) - sizeof(RDataBody) + size); + int count = CountExistingRegistrations(&srv, port); + if (count) + LogMsg("%5d: Client application registered %d identical instances of service %##s port %u.", + client, count+1, srv.c, mDNSVal16(port)); } - if (NumSubTypes) - { - SubTypes = mallocL("ServiceSubTypes", NumSubTypes * sizeof(AuthRecord)); - if (!SubTypes) { freeL("DNSServiceRegistration", x); err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } - for (i = 0; i < NumSubTypes; i++) - { - comma++; // Advance over the nul character - MakeDomainNameFromDNSNameString(&SubTypes[i].resrec.name, comma); - while (*comma) comma++; // Advance comma to point to the next terminating nul - } - } + // Allocate memory, and handle failure + DNSServiceRegistration *x = mallocL("DNSServiceRegistration", sizeof(*x)); + if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + bzero(x, sizeof(*x)); + + // Set up object, and link into list + x->ClientMachPort = client; + x->DefaultDomain = !domain[0]; + x->autoname = (!name[0]); + x->rdsize = size; + x->NumSubTypes = NumSubTypes; + memcpy(x->regtype, regtype, reglen); + x->name = n; + x->type = t; + x->port = port; + memcpy(x->txtinfo, txtinfo, 1024); + x->txt_len = data_len; + x->NextRef = 0; + x->regs = NULL; + + x->next = DNSServiceRegistrationList; + DNSServiceRegistrationList = x; - // Do the operation LogOperation("%5d: DNSServiceRegistration(\"%s\", \"%s\", \"%s\", %u) START", x->ClientMachPort, name, regtype, domain, mDNSVal16(port)); - // Some clients use mDNS for lightweight copy protection, registering a pseudo-service with - // a port number of zero. When two instances of the protected client are allowed to run on one - // machine, we don't want to see misleading "Bogus client" messages in syslog and the console. - if (port.NotAnInteger) CheckForDuplicateRegistrations(x, &srv, port); - err = mDNS_RegisterService(&mDNSStorage, srs, - &x->name, &t, &d, // Name, type, domain - mDNSNULL, port, // Host and port - txtinfo, data_len, // TXT data, length - SubTypes, NumSubTypes, // Subtypes - mDNSInterface_Any, // Interface ID - RegCallback, x); // Callback and context + err = AddServiceInstance(x, &d); + if (err) { AbortClient(client, x); errormsg = "mDNS_RegisterService"; goto fail; } // bail if .local (or explicit domain) fails - if (err) + if (x->DefaultDomain) { - if (srs == &x->ls) AbortClient(client, x); // don't abort client for global service - else FreeDNSServiceRegistration(x->gs); - errormsg = "mDNS_RegisterService"; - goto fail; - } - return x; - + DNameListElem *ptr, *regdomains = mDNSPlatformGetRegDomainList(); + for (ptr = regdomains; ptr; ptr = ptr->next) + AddServiceInstance(x, &ptr->name); + mDNS_FreeDNameList(regdomains); + } + + // Succeeded: Wrap up and return + EnableDeathNotificationForClient(client, x); + return(mStatus_NoError); + badtxt: LogMsg("%5d: TXT record: %.100s...", client, txtRecord); badparam: err = mStatus_BadParamErr; fail: LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%ld)", - client, name, regtype, domain, notAnIntPort, errormsg, err); - return NULL; + client, name, regtype, domain, mDNSVal16(port), errormsg, err); + return(err); } -mDNSexport kern_return_t provide_DNSServiceRegistrationCreate_rpc(mach_port_t unusedserver, mach_port_t client, - DNSCString name, DNSCString regtype, DNSCString domain, int notAnIntPort, DNSCString txtRecord) +// This updates either the text of the field currently labelled "Local Hostname", +// or the text of the field currently labelled "Computer Name" +// in the Sharing Prefs Control Panel +mDNSlocal void RecordUpdatedName(const mDNS *const m, domainlabel *n1, domainlabel *n2, char *msg, char *suffix, CFStringRef subtext) { - // Check client parameter - (void)unusedserver; // Unused - mStatus err = mStatus_NoError; - const char *errormsg = "Unknown"; - DNSServiceRegistration *x = NULL; - if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } - if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; } - - x = RegisterService(client, name, regtype, *domain ? domain : "local.", notAnIntPort, txtRecord, NULL); - if (!x) { err = mStatus_UnknownErr; goto fail; } - - //!!!KRS if we got a dynamic reg domain from the config file, use it for default (except for iChat) - if (!*domain && mDNSStorage.uDNS_info.ServiceRegDomain[0] && strcmp(regtype, "_presence._tcp.") && strcmp(regtype, "_ichat._tcp.")) - x = RegisterService(client, name, regtype, mDNSStorage.uDNS_info.ServiceRegDomain, notAnIntPort, txtRecord, x); - - // Succeeded: Wrap up and return - EnableDeathNotificationForClient(client, x); - return(mStatus_NoError); - -fail: - LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%ld)", - client, name, regtype, domain, notAnIntPort, errormsg, err); - return mStatus_UnknownErr; + char oldname[MAX_DOMAIN_LABEL+1]; + char newname[MAX_DOMAIN_LABEL+1]; + ConvertDomainLabelToCString_unescaped(n1, oldname); + ConvertDomainLabelToCString_unescaped(n2, newname); + const CFStringRef cfoldname = CFStringCreateWithCString(NULL, oldname, kCFStringEncodingUTF8); + const CFStringRef cfnewname = CFStringCreateWithCString(NULL, newname, kCFStringEncodingUTF8); + const CFStringRef f1 = CFStringCreateWithCString(NULL, "“%@%s”", kCFStringEncodingUTF8); + const CFStringRef f2 = CFStringCreateWithCString(NULL, "“%@%s”", kCFStringEncodingUTF8); + const SCPreferencesRef session = SCPreferencesCreate(NULL, CFSTR("mDNSResponder"), NULL); + *n1 = *n2; + if (!cfoldname || !cfnewname || !f1 || !f2 || !session || !SCPreferencesLock(session, 0)) // If we can't get the lock don't wait + LogMsg("RecordUpdatedName: ERROR: Couldn't create SCPreferences session"); + else + { + const CFStringRef s0 = CFStringCreateWithCString(NULL, msg, kCFStringEncodingUTF8); + const CFStringRef s1 = CFStringCreateWithFormat(NULL, NULL, f1, cfoldname, suffix); + const CFStringRef s2 = CFStringCreateWithFormat(NULL, NULL, f2, cfnewname, suffix); +// const CFMutableArrayRef alertMessage = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + const CFMutableStringRef alertMessage = CFStringCreateMutable(NULL, 0); + Boolean result; + if (n2 == &m->hostlabel) result = SCPreferencesSetLocalHostName(session, cfnewname); + else result = SCPreferencesSetComputerName(session, cfnewname, kCFStringEncodingUTF8); + if (!result || !SCPreferencesCommitChanges(session) || !SCPreferencesApplyChanges(session) || !s0 || !s1 || !s2 || !alertMessage) + LogMsg("RecordUpdatedName: ERROR: Couldn't update SCPreferences"); + else if (m->p->NotifyUser) + { +// CFArrayAppendValue(alertMessage, s0); + CFStringAppend(alertMessage, s0); + CFStringAppend(alertMessage, s1); + CFStringAppend(alertMessage, CFSTR(" is already in use on this network. The name has been changed to ")); + CFStringAppend(alertMessage, s2); + CFStringAppend(alertMessage, CFSTR(" automatically.")); + CFUserNotificationDisplayNotice(60.0, // Auto-dismiss after 60 seconds + kCFUserNotificationCautionAlertLevel, + NULL, NULL, NULL, // iconURL, soundURL, localizationURL + (CFStringRef)alertMessage, subtext, NULL); // alertHeader, alertMessage, defaultButtonTitle + } + if (s0) CFRelease(s0); + if (s1) CFRelease(s1); + if (s2) CFRelease(s2); + if (alertMessage) CFRelease(alertMessage); + SCPreferencesUnlock(session); + } + if (cfoldname) CFRelease(cfoldname); + if (cfnewname) CFRelease(cfnewname); + if (f1) CFRelease(f1); + if (f2) CFRelease(f2); + if (session) CFRelease(session); } - + mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result) { (void)m; // Unused - if (result == mStatus_ConfigChanged) + if (result == mStatus_NoError) + { + // Allow three seconds in case we get a Computer Name update too -- don't want to alert the user twice + RecordUpdatedNiceLabel(m, mDNSPlatformOneSecond*3); + } + else if (result == mStatus_ConfigChanged) { DNSServiceRegistration *r; for (r = DNSServiceRegistrationList; r; r=r->next) - if (r->autoname && !SameDomainLabel(r->name.c, mDNSStorage.nicelabel.c)) + if (r->autoname) { - debugf("NetworkChanged renaming %#s to %#s", r->name.c, mDNSStorage.nicelabel.c); - r->autorenameLS = mDNStrue; - mDNS_DeregisterService(&mDNSStorage, &r->ls); - if (r->gs) { mDNS_DeregisterService(&mDNSStorage, r->gs); r->autorenameGS = mDNStrue; } + ServiceInstance *si; + for (si = r->regs; si; si = si->next) + { + if (!SameDomainLabel(si->name.c, m->nicelabel.c)) + { + debugf("NetworkChanged renaming %##s to %#s", si->srs.RR_SRV.resrec.name.c, m->nicelabel.c); + si->autorename = mDNStrue; + if (mDNS_DeregisterService(m, &si->srs)) // If service deregistered already, we can re-register immediately + RegCallback(m, &si->srs, mStatus_MemFree); + } + } } udsserver_handle_configchange(); } @@ -1382,86 +1704,62 @@ mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result) // If we've run out of cache space, then double the total cache size and give the memory to mDNSCore mDNSu32 numrecords = m->rrcache_size; CacheRecord *storage = mallocL("mStatus_GrowCache", sizeof(CacheRecord) * numrecords); - if (storage) mDNS_GrowCache(&mDNSStorage, storage, numrecords); + if (storage) mDNS_GrowCache(m, storage, numrecords); } } //************************************************************************************************************* // Add / Update / Remove records from existing Registration - -mDNSlocal ExtraResourceRecord *AddExtraRecord(DNSServiceRegistration *x, ServiceRecordSet *srs, mach_port_t client, - int type, const char *data, mach_msg_type_number_t data_len, uint32_t ttl) - { - mStatus err = mStatus_NoError; - const char *errormsg = "Unknown"; - domainname *name = (domainname *)""; - name = &srs->RR_SRV.resrec.name; - - (void)x; // unused - - unsigned int size = sizeof(RDataBody); - if (size < data_len) - size = data_len; - - // Allocate memory, and handle failure - ExtraResourceRecord *extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size); - if (!extra) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } - - // Fill in type, length, and data of new record - extra->r.resrec.rrtype = type; - extra->r.rdatastorage.MaxRDLength = size; - extra->r.resrec.rdlength = data_len; - memcpy(&extra->r.rdatastorage.u.data, data, data_len); - - // Do the operation - LogOperation("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) REF %p", - client, srs->RR_SRV.resrec.name.c, type, data_len, extra); - err = mDNS_AddRecordToService(&mDNSStorage, srs, extra, &extra->r.rdatastorage, ttl); - - if (err) - { - freeL("Extra Resource Record", extra); - errormsg = "mDNS_AddRecordToService"; - goto fail; - } - - return extra; - -fail: - LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%ld)", client, name->c, type, data_len, errormsg, err); - return NULL; - } - - mDNSexport kern_return_t provide_DNSServiceRegistrationAddRecord_rpc(mach_port_t unusedserver, mach_port_t client, int type, const char *data, mach_msg_type_number_t data_len, uint32_t ttl, natural_t *reference) { // Check client parameter - (void)unusedserver; // Unused + uint32_t id; mStatus err = mStatus_NoError; const char *errormsg = "Unknown"; if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } DNSServiceRegistration *x = DNSServiceRegistrationList; + ServiceInstance *si; + size_t size; + (void)unusedserver; // Unused while (x && x->ClientMachPort != client) x = x->next; if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; } // Check other parameters if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; } + if (data_len > sizeof(RDataBody)) size = data_len; + else size = sizeof(RDataBody); + + id = x->NextRef++; + *reference = (natural_t)id; + for (si = x->regs; si; si = si->next) + { + // Allocate memory, and handle failure + ExtraResourceRecord *extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size); + if (!extra) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + + // Fill in type, length, and data of new record + extra->r.resrec.rrtype = type; + extra->r.rdatastorage.MaxRDLength = size; + extra->r.resrec.rdlength = data_len; + memcpy(&extra->r.rdatastorage.u.data, data, data_len); + + // Do the operation + LogOperation("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) REF %p", + client, si->srs.RR_SRV.resrec.name.c, type, data_len, extra); + err = mDNS_AddRecordToService(&mDNSStorage, &si->srs, extra, &extra->r.rdatastorage, ttl); + + if (err) + { + freeL("Extra Resource Record", extra); + errormsg = "mDNS_AddRecordToService"; + goto fail; + } - ExtraRecordRef *ref = mallocL("ExtraRecordRef", sizeof(ExtraRecordRef)); - if (!ref) { LogMsg("ERROR: malloc"); return mStatus_NoMemoryErr; } - - ref->localRef = AddExtraRecord(x, &x->ls, client, type, data, data_len, ttl); - if (!ref->localRef) { freeL("ExtraRecordRef", ref); *reference = (natural_t)NULL; return mStatus_UnknownErr; } + extra->ClientID = id; + } - if (x->gs) ref->globalRef = AddExtraRecord(x, x->gs, client, type, data, data_len, ttl); // return success even if global case fails - else ref->globalRef = NULL; - - // Succeeded: Wrap up and return - ref->next = x->ExtraRefList; - x->ExtraRefList = ref; - *reference = (natural_t)ref; return mStatus_NoError; fail: @@ -1496,7 +1794,7 @@ mDNSlocal mStatus UpdateRecord(ServiceRecordSet *srs, mach_port_t client, AuthRe // Fill in new length, and data newrdata->MaxRDLength = size; memcpy(&newrdata->u, data, data_len); - + // Do the operation LogOperation("%5d: DNSServiceRegistrationUpdateRecord(%##s, new length %d)", client, srs->RR_SRV.resrec.name.c, data_len); @@ -1507,14 +1805,13 @@ mDNSlocal mStatus UpdateRecord(ServiceRecordSet *srs, mach_port_t client, AuthRe errormsg = "mDNS_Update"; freeL("RData", newrdata); return err; - } + } return(mStatus_NoError); fail: LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %d) failed: %s (%ld)", client, name->c, data_len, errormsg, err); return(err); - } - + } mDNSexport kern_return_t provide_DNSServiceRegistrationUpdateRecord_rpc(mach_port_t unusedserver, mach_port_t client, natural_t reference, const char *data, mach_msg_type_number_t data_len, uint32_t ttl) @@ -1523,9 +1820,9 @@ mDNSexport kern_return_t provide_DNSServiceRegistrationUpdateRecord_rpc(mach_por mStatus err = mStatus_NoError; const char *errormsg = "Unknown"; domainname *name = (domainname *)""; - AuthRecord *gRR, *lRR; + ServiceInstance *si; - (void)unusedserver; // unused + (void)unusedserver; // unused if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } DNSServiceRegistration *x = DNSServiceRegistrationList; while (x && x->ClientMachPort != client) x = x->next; @@ -1534,28 +1831,28 @@ mDNSexport kern_return_t provide_DNSServiceRegistrationUpdateRecord_rpc(mach_por // Check other parameters if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; } - // Find the record we're updating. NULL reference means update the primary TXT record - if (!reference) - { - lRR = &x->ls.RR_TXT; - gRR = x->gs ? &x->gs->RR_TXT : NULL; - } - else + for (si = x->regs; si; si = si->next) { - ExtraRecordRef *ref; - for (ref = x->ExtraRefList; ref; ref= ref->next) - if (ref == (ExtraRecordRef *)reference) break; - if (!ref) { err = mStatus_BadReferenceErr; errormsg = "No such record"; goto fail; } - lRR = &ref->localRef->r; - gRR = ref->globalRef ? &ref->globalRef->r : NULL; - } - - err = UpdateRecord(&x->ls, client, lRR, data, data_len, ttl); - if (err) goto fail; + AuthRecord *r = NULL; - if (gRR) UpdateRecord(x->gs, client, gRR, data, data_len, ttl); // don't return error if global fails + // Find the record we're updating. NULL reference means update the primary TXT record + if (!reference) r = &si->srs.RR_TXT; + else + { + ExtraResourceRecord *ptr; + for (ptr = si->srs.Extras; ptr; ptr = ptr->next) + { + if ((natural_t)ptr->ClientID == reference) + { r = &ptr->r; break; } + } + if (!r) { err = mStatus_BadReferenceErr; errormsg = "No such record"; goto fail; } + } + err = UpdateRecord(&si->srs, client, r, data, data_len, ttl); + if (err) goto fail; //!!!KRS this will cause failures for non-local defaults! + } + return mStatus_NoError; - + fail: LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, %d) failed: %s (%ld)", client, name->c, reference, data_len, errormsg, err); return(err); @@ -1565,23 +1862,14 @@ mDNSlocal mStatus RemoveRecord(ServiceRecordSet *srs, ExtraResourceRecord *extra { domainname *name = &srs->RR_SRV.resrec.name; mStatus err = mStatus_NoError; - const char *errormsg = "Unknown"; - + // Do the operation LogOperation("%5d: DNSServiceRegistrationRemoveRecord(%##s)", client, srs->RR_SRV.resrec.name.c); - err = mDNS_RemoveRecordFromService(&mDNSStorage, srs, extra); - if (err) { errormsg = "mDNS_RemoveRecordFromService (No such record)"; goto fail; } - - // Succeeded: Wrap up and return - if (extra->r.resrec.rdata != &extra->r.rdatastorage) - freeL("Extra RData", extra->r.resrec.rdata); - freeL("ExtraResourceRecord", extra); - return(mStatus_NoError); - -fail: - LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%##s, %X) failed: %s", client, name->c, errormsg, err); - return(err); + err = mDNS_RemoveRecordFromService(&mDNSStorage, srs, extra, FreeExtraRR, extra); + if (err) LogMsg("%5d: DNSServiceRegistrationRemoveRecord (%##s) failed: %d", client, name->c, err); + + return err; } mDNSexport kern_return_t provide_DNSServiceRegistrationRemoveRecord_rpc(mach_port_t unusedserver, mach_port_t client, @@ -1591,30 +1879,28 @@ mDNSexport kern_return_t provide_DNSServiceRegistrationRemoveRecord_rpc(mach_por (void)unusedserver; // Unused mStatus err = mStatus_NoError; const char *errormsg = "Unknown"; - ExtraRecordRef *ref, *prev = NULL; if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } DNSServiceRegistration *x = DNSServiceRegistrationList; + ServiceInstance *si; + while (x && x->ClientMachPort != client) x = x->next; if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; } - ref = x->ExtraRefList; - while (ref) + for (si = x->regs; si; si = si->next) { - if (ref == (ExtraRecordRef *)ref) break; - prev = ref; - ref = ref->next; + ExtraResourceRecord *e; + for (e = si->srs.Extras; e; e = e->next) + { + if ((natural_t)e->ClientID == reference) + { + err = RemoveRecord(&si->srs, e, client); + break; + } + } + if (!e) { err = mStatus_BadReferenceErr; errormsg = "No such reference"; goto fail; } } - - if (!ref) { err = mStatus_BadReferenceErr; errormsg = "No such reference"; goto fail; } - err = RemoveRecord(&x->ls, ref->localRef, client); - if (x->gs && ref->globalRef) RemoveRecord(x->gs, ref->globalRef, client); // don't return error if this fails - - // delete the ref struct - if (prev) prev->next = ref->next; - else x->ExtraRefList = ref->next; - ref->next = NULL; - freeL("ExtraRecordRef", ref); - return err; + + return mStatus_NoError; fail: LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%X) failed: %s (%ld)", client, reference, errormsg, err); @@ -1705,7 +1991,7 @@ mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, vo break; default : - /* Includes success case. */ + /* Includes success case. */ break; } @@ -1772,23 +2058,19 @@ mDNSlocal kern_return_t destroyBootstrapService() return bootstrap_register(server_priv_port, (char*)kmDNSBootstrapName, MACH_PORT_NULL); } -mDNSlocal void ExitCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) +mDNSlocal void ExitCallback(int signal) { - (void)port; // Unused - (void)msg; // Unused - (void)size; // Unused - (void)info; // Unused -/* +#if 0 CacheRecord *rr; int rrcache_active = 0; for (rr = mDNSStorage.rrcache; rr; rr=rr->next) if (CacheRRActive(&mDNSStorage, rr)) rrcache_active++; debugf("ExitCallback: RR Cache now using %d records, %d active", mDNSStorage.rrcache_used, rrcache_active); -*/ +#endif LogMsgIdent(mDNSResponderVersionString, "stopping"); debugf("ExitCallback: destroyBootstrapService"); - if (!mDNS_DebugMode) + if (!mDNS_DebugMode && signal != SIGHUP) destroyBootstrapService(); debugf("ExitCallback: Aborting MIG clients"); @@ -1808,89 +2090,62 @@ mDNSlocal void ExitCallback(CFMachPortRef port, void *msg, CFIndex size, void *i } // Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit -mDNSlocal void HandleSIGTERM(int signal) +mDNSlocal void HandleSIG(int signal) { - (void)signal; // Unused debugf(" "); - debugf("SIGINT/SIGTERM"); + debugf("HandleSIG %d", signal); mach_msg_header_t header; header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); - header.msgh_remote_port = exit_m_port; + header.msgh_remote_port = signal_port; header.msgh_local_port = MACH_PORT_NULL; header.msgh_size = sizeof(header); - header.msgh_id = 0; + header.msgh_id = signal; if (mach_msg_send(&header) != MACH_MSG_SUCCESS) - { LogMsg("HandleSIGTERM: mach_msg_send failed; Exiting immediately."); exit(-1); } + { + LogMsg("HandleSIG %d: mach_msg_send failed", signal); + if (signal == SIGHUP || signal == SIGTERM || signal == SIGINT) exit(-1); + } } -mDNSlocal void INFOCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) +mDNSlocal void INFOCallback(void) { - (void)port; // Unused - (void)msg; // Unused - (void)size; // Unused - (void)info; // Unused DNSServiceDomainEnumeration *e; DNSServiceBrowser *b; DNSServiceResolver *l; DNSServiceRegistration *r; NetworkInterfaceInfoOSX *i; - mDNSu32 slot; - CacheRecord *rr; - mDNSu32 CacheUsed = 0, CacheActive = 0; - mDNSs32 now = mDNSPlatformTimeNow(); LogMsgIdent(mDNSResponderVersionString, "---- BEGIN STATE LOG ----"); - for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) - { - mDNSu32 SlotUsed = 0; - for (rr = mDNSStorage.rrcache_hash[slot]; rr; rr=rr->next) - { - mDNSs32 remain = rr->resrec.rroriginalttl - (now - rr->TimeRcvd) / mDNSPlatformOneSecond; - CacheUsed++; - SlotUsed++; - if (rr->CRActiveQuestion) CacheActive++; - LogMsgNoIdent("%s%6ld %-6s%-6s%s", rr->CRActiveQuestion ? "*" : " ", remain, DNSTypeName(rr->resrec.rrtype), - ((NetworkInterfaceInfoOSX *)rr->resrec.InterfaceID)->ifa_name, GetRRDisplayString(&mDNSStorage, rr)); - usleep(1000); // Limit rate a little so we don't flood syslog too fast - } - if (mDNSStorage.rrcache_used[slot] != SlotUsed) - LogMsgNoIdent("Cache use mismatch: rrcache_used[slot] is %lu, true count %lu", mDNSStorage.rrcache_used[slot], SlotUsed); - } - if (mDNSStorage.rrcache_totalused != CacheUsed) - LogMsgNoIdent("Cache use mismatch: rrcache_totalused is %lu, true count %lu", mDNSStorage.rrcache_totalused, CacheUsed); - if (mDNSStorage.rrcache_active != CacheActive) - LogMsgNoIdent("Cache use mismatch: rrcache_active is %lu, true count %lu", mDNSStorage.rrcache_active, CacheActive); - LogMsgNoIdent("Cache currently contains %lu records; %lu referenced by active questions", CacheUsed, CacheActive); + udsserver_info(&mDNSStorage); for (e = DNSServiceDomainEnumerationList; e; e=e->next) - LogMsgNoIdent("%5d: DomainEnumeration %##s", e->ClientMachPort, e->dom.qname.c); + LogMsgNoIdent("%5d: Mach DomainEnumeration %##s", e->ClientMachPort, e->dom.qname.c); for (b = DNSServiceBrowserList; b; b=b->next) { DNSServiceBrowserQuestion *qptr; for (qptr = b->qlist; qptr; qptr = qptr->next) - LogMsgNoIdent("%5d: ServiceBrowse %##s", b->ClientMachPort, qptr->q.qname.c); + LogMsgNoIdent("%5d: Mach ServiceBrowse %##s", b->ClientMachPort, qptr->q.qname.c); } for (l = DNSServiceResolverList; l; l=l->next) - LogMsgNoIdent("%5d: ServiceResolve %##s", l->ClientMachPort, l->i.name.c); + LogMsgNoIdent("%5d: Mach ServiceResolve %##s", l->ClientMachPort, l->i.name.c); for (r = DNSServiceRegistrationList; r; r=r->next) { - LogMsgNoIdent("%5d: ServiceRegistration %##s %u", r->ClientMachPort, r->ls.RR_SRV.resrec.name.c, mDNSVal16(r->ls.RR_SRV.resrec.rdata->u.srv.port)); - if (r->gs) LogMsgNoIdent("%5d: ServiceRegistration %##s %u", r->ClientMachPort, r->gs->RR_SRV.resrec.name.c, mDNSVal16(r->gs->RR_SRV.resrec.rdata->u.srv.port)); + ServiceInstance *si; + for (si = r->regs; si; si = si->next) + LogMsgNoIdent("%5d: Mach ServiceInstance %##s %u", si->ClientMachPort, si->srs.RR_SRV.resrec.name.c, mDNSVal16(si->srs.RR_SRV.resrec.rdata->u.srv.port)); } - udsserver_info(); - for (i = mDNSStorage.p->InterfaceList; i; i = i->next) { if (!i->Exists) - LogMsgNoIdent("Interface: %s %5s(%lu) DORMANT", - i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifa_name, i->scope_id); + LogMsgNoIdent("Interface: %s %5s(%lu) %.6a DORMANT", + i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifa_name, i->scope_id, &i->BSSID); else - LogMsgNoIdent("Interface: %s %5s(%lu) %s %s %2d %s %2d InterfaceID %p %s %s %#a", - i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifa_name, i->scope_id, + LogMsgNoIdent("Interface: %s %5s(%lu) %.6a %s %s %2d %s %2d InterfaceID %p %s %s %#a", + i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifa_name, i->scope_id, &i->BSSID, i->ifinfo.InterfaceActive ? "Active" : " ", i->ifinfo.IPv4Available ? "v4" : " ", i->ss.sktv4, i->ifinfo.IPv6Available ? "v6" : " ", i->ss.sktv6, @@ -1903,17 +2158,22 @@ mDNSlocal void INFOCallback(CFMachPortRef port, void *msg, CFIndex size, void *i LogMsgIdent(mDNSResponderVersionString, "---- END STATE LOG ----"); } -mDNSlocal void HandleSIGINFO(int signal) +mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) { - (void)signal; // Unused - mach_msg_header_t header; - header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); - header.msgh_remote_port = info_m_port; - header.msgh_local_port = MACH_PORT_NULL; - header.msgh_size = sizeof(header); - header.msgh_id = 0; - if (mach_msg_send(&header) != MACH_MSG_SUCCESS) - LogMsg("HandleSIGINFO: mach_msg_send failed; No state log will be generated."); + (void)port; // Unused + (void)size; // Unused + (void)info; // Unused + mach_msg_header_t *m = (mach_msg_header_t *)msg; + switch(m->msgh_id) + { + case SIGHUP: + case SIGINT: + case SIGTERM: ExitCallback(m->msgh_id); break; + case SIGINFO: INFOCallback(); break; + case SIGUSR1: LogMsg("SIGUSR1: Simulate Network Configuration Change Event"); + mDNSMacOSXNetworkChanged(&mDNSStorage); break; + default: LogMsg("SignalCallback: Unknown signal %d", m->msgh_id); break; + } } mDNSlocal kern_return_t mDNSDaemonInitialize(void) @@ -1921,16 +2181,14 @@ mDNSlocal kern_return_t mDNSDaemonInitialize(void) mStatus err; CFMachPortRef d_port = CFMachPortCreate(NULL, ClientDeathCallback, NULL, NULL); CFMachPortRef s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL); - CFMachPortRef e_port = CFMachPortCreate(NULL, ExitCallback, NULL, NULL); - CFMachPortRef i_port = CFMachPortCreate(NULL, INFOCallback, NULL, NULL); + CFMachPortRef i_port = CFMachPortCreate(NULL, SignalCallback, NULL, NULL); mach_port_t m_port = CFMachPortGetPort(s_port); char *MachServerName = mDNSMacOSXSystemBuildNumber(NULL) < 7 ? "DNSServiceDiscoveryServer" : "com.apple.mDNSResponder"; kern_return_t status = bootstrap_register(bootstrap_port, MachServerName, m_port); CFRunLoopSourceRef d_rls = CFMachPortCreateRunLoopSource(NULL, d_port, 0); CFRunLoopSourceRef s_rls = CFMachPortCreateRunLoopSource(NULL, s_port, 0); - CFRunLoopSourceRef e_rls = CFMachPortCreateRunLoopSource(NULL, e_port, 0); CFRunLoopSourceRef i_rls = CFMachPortCreateRunLoopSource(NULL, i_port, 0); - + if (status) { if (status == 1103) @@ -1944,37 +2202,34 @@ mDNSlocal kern_return_t mDNSDaemonInitialize(void) rrcachestorage, RR_CACHE_SIZE, mDNS_Init_AdvertiseLocalAddresses, mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext); - + if (err) { LogMsg("Daemon start: mDNS_Init failed %ld", err); return(err); } client_death_port = CFMachPortGetPort(d_port); - exit_m_port = CFMachPortGetPort(e_port); - info_m_port = CFMachPortGetPort(i_port); + signal_port = CFMachPortGetPort(i_port); CFRunLoopAddSource(CFRunLoopGetCurrent(), d_rls, kCFRunLoopDefaultMode); CFRunLoopAddSource(CFRunLoopGetCurrent(), s_rls, kCFRunLoopDefaultMode); - CFRunLoopAddSource(CFRunLoopGetCurrent(), e_rls, kCFRunLoopDefaultMode); CFRunLoopAddSource(CFRunLoopGetCurrent(), i_rls, kCFRunLoopDefaultMode); CFRelease(d_rls); CFRelease(s_rls); - CFRelease(e_rls); CFRelease(i_rls); if (mDNS_DebugMode) printf("Service registered with Mach Port %d\n", m_port); - err = udsserver_init(&mDNSStorage); + err = udsserver_init(); if (err) { LogMsg("Daemon start: udsserver_init failed"); return err; } return(err); } -mDNSlocal mDNSs32 mDNSDaemonIdle(void) +mDNSlocal mDNSs32 mDNSDaemonIdle(mDNS *const m) { // 1. Call mDNS_Execute() to let mDNSCore do what it needs to do - mDNSs32 nextevent = mDNS_Execute(&mDNSStorage); + mDNSs32 nextevent = mDNS_Execute(m); + + mDNSs32 now = mDNS_TimeNow(m); - mDNSs32 now = mDNSPlatformTimeNow(); - // 2. Deliver any waiting browse messages to clients DNSServiceBrowser *b = DNSServiceBrowserList; - + while (b) { // NOTE: Need to advance b to the next element BEFORE we call DeliverInstance(), because in the @@ -2023,10 +2278,46 @@ mDNSlocal mDNSs32 mDNSDaemonIdle(void) if (l->ReportTime && now - l->ReportTime >= 0) { l->ReportTime = 0; - LogMsg("%5d: DNSServiceResolver(%##s) active for over two minutes. " - "This places considerable burden on the network.", l->ClientMachPort, l->i.name.c); + LogMsgNoIdent("Client application bug: DNSServiceResolver(%##s) active for over two minutes. " + "This places considerable burden on the network.", l->i.name.c); } + if (m->p->NotifyUser) + { + if (m->p->NotifyUser - now < 0) + { + if (!SameDomainLabel(m->p->usernicelabel.c, m->nicelabel.c)) + { + LogMsg("Updating Computer Name from \"%#s\" to \"%#s\"", m->p->usernicelabel.c, m->nicelabel.c); + RecordUpdatedName(m, &m->p->usernicelabel, &m->nicelabel, "The name of your computer ", "", + CFSTR("To change the name of your computer, open System Preferences and click Sharing. Then type the name in the Computer Name field.")); + m->p->NotifyUser = 0; // Clear m->p->NotifyUser here -- even if the hostlabel has changed too, we don't want to bug the user with *two* alerts + } + if (!SameDomainLabel(m->p->userhostlabel.c, m->hostlabel.c)) + { + LogMsg("Updating Local Hostname from \"%#s.local\" to \"%#s.local\"", m->p->userhostlabel.c, m->hostlabel.c); + RecordUpdatedName(m, &m->p->userhostlabel, &m->hostlabel, "This computer's local hostname ", ".local", + CFSTR("To change the local hostname, open System Preferences and click Sharing. Then click Edit and type the name in the Local Hostname field.")); + } + m->p->NotifyUser = 0; + } + else + if (nextevent - m->p->NotifyUser > 0) + nextevent = m->p->NotifyUser; + } + + if (m->p->NetworkChanged) + { + if (m->p->NetworkChanged - now < 0) + { + m->p->NetworkChanged = 0; + mDNSMacOSXNetworkChanged(m); + } + else + if (nextevent - m->p->NetworkChanged > 0) + nextevent = m->p->NetworkChanged; + } + return(nextevent); } @@ -2034,15 +2325,18 @@ mDNSexport int main(int argc, char **argv) { int i; kern_return_t status; - + for (i=1; ipw_uid); else setuid(-2); // User "nobody" is -2; use that value if "nobody" does not appear in the password database +#endif if (status == 0) { + LogMsg("Starting time value 0x%08lX (%ld)", (mDNSu32)mDNSStorage.timenow_last, mDNSStorage.timenow_last); int numevents = 0; int RunLoopStatus = kCFRunLoopRunTimedOut; - + // This is the main work loop: // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time // (2) Then we make sure we've delivered all waiting browse messages to our clients @@ -2090,11 +2387,11 @@ mDNSexport int main(int argc, char **argv) { // 1. Before going into a blocking wait call and letting our process to go sleep, // call mDNSDaemonIdle to allow any deferred work to be completed. - mDNSs32 nextevent = mDNSDaemonIdle(); + mDNSs32 nextevent = mDNSDaemonIdle(&mDNSStorage); nextevent = udsserver_idle(nextevent); // 2. Work out how long we expect to sleep before the next scheduled task - mDNSs32 ticks = nextevent - mDNSPlatformTimeNow(); + mDNSs32 ticks = nextevent - mDNS_TimeNow(&mDNSStorage); static mDNSs32 RepeatedBusy = 0; // Debugging sanity check, to guard against CPU spins if (ticks > 1) RepeatedBusy = 0; @@ -2105,7 +2402,7 @@ mDNSexport int main(int argc, char **argv) { LogMsg("Task Scheduling Error: Continuously busy for the last ten seconds"); RepeatedBusy = 0; } } CFAbsoluteTime interval = (CFAbsoluteTime)ticks / (CFAbsoluteTime)mDNSPlatformOneSecond; - + // 3. Now do a blocking "CFRunLoopRunInMode" call so we sleep until // (a) our next wakeup time, or (b) an event occurs. // The 'true' parameter makes it return after handling any event that occurs @@ -2133,23 +2430,27 @@ mDNSexport int main(int argc, char **argv) // uds_daemon.c support routines ///////////////////////////////////////////// -// We keep a list of client-supplied event sources in PosixEventSource records +// We keep a list of client-supplied event sources in PosixEventSource records struct CFSocketEventSource { udsEventCallback Callback; void *Context; int fd; struct CFSocketEventSource *Next; - CFSocketRef SocketRef; + CFSocketRef cfs; CFRunLoopSourceRef RLS; }; typedef struct CFSocketEventSource CFSocketEventSource; static GenLinkedList gEventSources; // linked list of CFSocketEventSource's -static void cf_callback(CFSocketRef s _UNUSED, CFSocketCallBackType t _UNUSED, CFDataRef dr _UNUSED, const void *c _UNUSED, void *i) +static void cf_callback(CFSocketRef s, CFSocketCallBackType t, CFDataRef dr, const void *c, void *i) // Called by CFSocket when data appears on socket { + (void)s; // Unused + (void)t; // Unused + (void)dr; // Unused + (void)c; // Unused CFSocketEventSource *source = (CFSocketEventSource*) i; source->Callback(source->Context); } @@ -2159,7 +2460,7 @@ mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *cont { CFSocketEventSource *newSource; CFSocketContext cfContext = { 0, NULL, NULL, NULL, NULL }; - + if (gEventSources.LinkOffset == 0) InitLinkedList(&gEventSources, offsetof(CFSocketEventSource, Next)); @@ -2177,19 +2478,19 @@ mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *cont newSource->fd = fd; cfContext.info = newSource; - if ( NULL != (newSource->SocketRef = CFSocketCreateWithNative(kCFAllocatorDefault, fd, kCFSocketReadCallBack, + if ( NULL != (newSource->cfs = CFSocketCreateWithNative(kCFAllocatorDefault, fd, kCFSocketReadCallBack, cf_callback, &cfContext)) && - NULL != (newSource->RLS = CFSocketCreateRunLoopSource(kCFAllocatorDefault, newSource->SocketRef, 0))) + NULL != (newSource->RLS = CFSocketCreateRunLoopSource(kCFAllocatorDefault, newSource->cfs, 0))) { CFRunLoopAddSource(CFRunLoopGetCurrent(), newSource->RLS, kCFRunLoopDefaultMode); AddToTail(&gEventSources, newSource); } else { - if (newSource->SocketRef) + if (newSource->cfs) { - CFSocketInvalidate(newSource->SocketRef); // automatically closes socket - CFRelease(newSource->SocketRef); + CFSocketInvalidate(newSource->cfs); // automatically closes socket + CFRelease(newSource->cfs); } return mStatus_NoMemoryErr; } @@ -2210,8 +2511,8 @@ mStatus udsSupportRemoveFDFromEventLoop(int fd) CFRunLoopRemoveSource(CFRunLoopGetCurrent(), iSource->RLS, kCFRunLoopDefaultMode); CFRunLoopSourceInvalidate(iSource->RLS); CFRelease(iSource->RLS); - CFSocketInvalidate(iSource->SocketRef); - CFRelease(iSource->SocketRef); + CFSocketInvalidate(iSource->cfs); + CFRelease(iSource->cfs); free(iSource); return mStatus_NoError; } @@ -2219,5 +2520,9 @@ mStatus udsSupportRemoveFDFromEventLoop(int fd) return mStatus_NoSuchNameErr; } +// If mDNSResponder crashes, then this string will be magically included in the automatically-generated crash log +const char *__crashreporter_info__ = mDNSResponderVersionString; +asm(".desc ___crashreporter_info__, 0x10"); + // For convenience when using the "strings" command, this is the last thing in the file mDNSexport const char mDNSResponderVersionString[] = STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; diff --git a/mDNSMacOSX/mDNSMacOSX.c b/mDNSMacOSX/mDNSMacOSX.c new file mode 100644 index 0000000..eebba79 --- /dev/null +++ b/mDNSMacOSX/mDNSMacOSX.c @@ -0,0 +1,3068 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: mDNSMacOSX.c,v $ +Revision 1.258 2004/12/14 00:18:05 cheshire +Don't log dns_configuration_copy() failures in the first three minutes after boot + +Revision 1.257 2004/12/10 19:45:46 cheshire + Reduce egregious stack space usage +Reduced myCFSocketCallBack() stack frame from 9K to 512 bytes + +Revision 1.256 2004/12/10 04:35:43 cheshire + Show "Note: Compiled without Apple-specific split DNS support" only once + +Revision 1.255 2004/12/10 04:12:54 ksekar + Need new DefaultBrowseDomain key + +Revision 1.254 2004/12/10 01:55:31 ksekar + Keychain lookups should be in lower case. + +Revision 1.253 2004/12/09 03:15:41 ksekar + use _legacy instead of _default to find "empty string" browse domains + +Revision 1.252 2004/12/07 01:32:42 cheshire +Don't log dns_configuration_copy() failure when running on 10.3 + +Revision 1.251 2004/12/06 22:30:31 cheshire +Added debugging log message + +Revision 1.250 2004/12/06 06:59:08 ksekar +RegisterSplitDNS should return Unsupported error when compiled on Panther + +Revision 1.249 2004/12/04 00:29:46 cheshire +Add "#ifdef MAC_OS_X_VERSION_10_4" around split-DNS code that can't be compiled on 10.3 systems +(When compiled on 10.3, code will not include split-DNS support.) + +Revision 1.248 2004/12/01 20:57:20 ksekar + Wide Area Rendezvous must be split-DNS aware + +Revision 1.247 2004/12/01 03:26:58 cheshire +Remove unused variables + +Revision 1.246 2004/12/01 01:51:34 cheshire +Move ReadDDNSSettingsFromConfFile() from mDNSMacOSX.c to PlatformCommon.c + +Revision 1.245 2004/11/30 03:24:04 cheshire + Defer processing network configuration changes until configuration has stabilized + +Revision 1.244 2004/11/30 02:59:35 cheshire +For debugging diagnostics, added identifying strings in SCDynamicStoreCreate() calls + +Revision 1.243 2004/11/29 19:17:29 ksekar + Unnecessary GetUserSpecifiedDDNSConfig log messages + +Revision 1.242 2004/11/29 18:37:38 ksekar + Buffer overflow in GetConfigOption + +Revision 1.241 2004/11/25 01:37:04 ksekar + Config file and SCPreferences don't play well together + +Revision 1.240 2004/11/25 01:29:42 ksekar +Remove unnecessary log messages + +Revision 1.239 2004/11/25 01:27:19 ksekar + Don't try to advertise link-local IP addresses via dynamic update + +Revision 1.238 2004/11/24 22:00:59 cheshire +Move definition of mDNSAddressIsAllDNSLinkGroup() from mDNSMacOSX.c to mDNSEmbeddedAPI.h + +Revision 1.237 2004/11/24 21:54:44 cheshire + mDNSCore not receiving unicast responses properly + +Revision 1.236 2004/11/23 03:39:46 cheshire +Let interface name/index mapping capability live directly in JNISupport.c, +instead of having to call through to the daemon via IPC to get this information. + +Revision 1.235 2004/11/17 01:45:35 cheshire + Rendezvous buddy list frequently becomes empty if you let the machine sleep +Refresh our interface list on receiving kIOMessageSystemHasPoweredOn, +in case we get no System Configuration Framework "network changed" event. + +Revision 1.234 2004/11/17 00:32:56 ksekar + mDNSResponder will not discover zones contained both in Search Domains and DHCP Domain option + +Revision 1.233 2004/11/12 03:16:45 rpantos +rdar://problem/3809541 Add mDNSPlatformGetInterfaceByName, mDNSPlatformGetInterfaceName + +Revision 1.232 2004/11/10 20:40:54 ksekar + LLQ mobility fragile on non-primary interface + +Revision 1.231 2004/11/06 00:59:33 ksekar +Don't log ENETDOWN errors for unicast destinations (pollutes log on +wake from sleep) + +Revision 1.230 2004/11/05 01:04:10 ksekar + LegacyNATDestroy() called too enthusiastically + +Revision 1.229 2004/11/03 03:45:16 cheshire + mDNSResponder does not inform user of Computer Name collisions + +Revision 1.228 2004/11/02 23:47:32 cheshire + Default hostname and Computer Name should be unique + +Revision 1.227 2004/11/02 04:23:03 cheshire +Change to more informative name "GetUserSpecifiedLocalHostName()" + +Revision 1.226 2004/11/01 20:36:19 ksekar + mDNSResponder should not receive Keychain Notifications + +Revision 1.225 2004/10/28 19:03:04 cheshire +Remove \n from LogMsg() calls + +Revision 1.224 2004/10/28 17:47:34 cheshire +Oops. Forgot the %d in the log message. + +Revision 1.223 2004/10/28 17:24:28 cheshire +Updated "bad ifa_netmask" log message to give more information + +Revision 1.222 2004/10/28 03:36:34 cheshire + Share the same port for both multicast and unicast receiving + +Revision 1.221 2004/10/28 03:24:41 cheshire +Rename m->CanReceiveUnicastOn as m->CanReceiveUnicastOn5353 + +Revision 1.220 2004/10/28 00:53:57 cheshire +Export mDNSMacOSXNetworkChanged() so it's callable from outside this mDNSMacOSX.c; +Add LogOperation() call to record when we get network change events + +Revision 1.219 2004/10/27 20:42:20 cheshire +Clean up debugging messages + +Revision 1.218 2004/10/27 02:03:59 cheshire +Update debugging messages + +Revision 1.217 2004/10/26 20:48:21 cheshire +Improve logging messages + +Revision 1.216 2004/10/26 01:02:37 cheshire +Update comment + +Revision 1.215 2004/10/25 20:09:00 ksekar +Cleaned up config file parsing. + +Revision 1.214 2004/10/25 19:30:53 ksekar + Simplify dynamic host name structures + +Revision 1.213 2004/10/23 01:16:01 cheshire + uDNS operations not always reliable on multi-homed hosts + +Revision 1.212 2004/10/22 20:52:08 ksekar + Create NAT port mappings for Long Lived Queries + +Revision 1.211 2004/10/22 01:07:11 cheshire + select() says data is waiting; recvfrom() says there is no data +Log error message if socket() ever returns file descriptors 0, 1 or 2 (stdin/stdout/stderr). +These are all supposed to be remapped to /dev/null + +Revision 1.210 2004/10/20 02:19:54 cheshire +Eliminate "SetupAddr invalid sa_family" warning from RegisterSearchDomains() + +Revision 1.209 2004/10/16 00:17:00 cheshire + Replace IP TTL 255 check with local subnet source address check + +Revision 1.208 2004/10/15 23:00:18 ksekar + Need to update LLQs on location changes + +Revision 1.207 2004/10/13 22:45:23 cheshire + Ten-second delay before kIOMessageSystemHasPoweredOn message + +Revision 1.206 2004/10/13 22:11:46 cheshire +Update debugging messages + +Revision 1.205 2004/10/12 21:10:11 cheshire + mach_absolute_time() not monotonically increasing +Do a NotifyOfElusiveBug() if we see mach_absolute_time() go backwards + +Revision 1.204 2004/10/12 03:20:52 ksekar + Incorrect LogMsg produces garbage on errors + +Revision 1.203 2004/10/08 04:29:25 ksekar + Allow default search domains to be set via hint from DHCP + +Revision 1.202 2004/10/04 05:56:04 cheshire + mDNSResponder doesn't respond to certain AirPort changes + +Revision 1.201 2004/09/30 00:24:59 ksekar + Dynamically update default registration domains on config change + +Revision 1.200 2004/09/26 23:20:35 ksekar + Allow default registrations in multiple wide-area domains + +Revision 1.199 2004/09/24 23:54:55 cheshire + Don't use kCFSocketCloseOnInvalidate + +Revision 1.198 2004/09/24 23:47:49 cheshire +Correct comment and error message + +Revision 1.197 2004/09/24 23:39:27 cheshire + Only IPv6 loopback address advertised on laptop w/no networking + +Revision 1.196 2004/09/24 20:53:04 cheshire +Revise error message to say "Setsockopt SO_REUSEPORT failed" instead of "Flaw in Kernel" + +Revision 1.195 2004/09/24 19:21:45 cheshire + Report "Address already in use" errors + +Revision 1.194 2004/09/24 19:16:54 cheshire +Remove "mDNS *const m" parameter from NotifyOfElusiveBug(); +Refine error message to say "Flaw in Kernel (select/recvfrom mismatch)" + +Revision 1.193 2004/09/22 00:41:59 cheshire +Move tcp connection status codes into the legal range allocated for mDNS use + +Revision 1.192 2004/09/21 21:02:55 cheshire +Set up ifname before calling mDNS_RegisterInterface() + +Revision 1.191 2004/09/21 19:19:36 cheshire + Combine WatchForDynDNSChanges() into WatchForNetworkChanges() + +Revision 1.190 2004/09/21 19:04:45 cheshire +Strip trailing white space from the ends of lines + +Revision 1.189 2004/09/21 00:13:28 cheshire +Fix build failure (io_connect_t and io_object_t are integers, not pointers) + +Revision 1.188 2004/09/20 23:52:02 cheshire +CFSocket{Puma}.c renamed to mDNSMacOSX{Puma}.c + +Revision 1.187 2004/09/18 01:37:01 cheshire +Update comment + +Revision 1.186 2004/09/18 01:11:57 ksekar + Add a user's default domain to empty-string browse list + +Revision 1.185 2004/09/17 01:08:52 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.184 2004/09/17 00:19:10 cheshire +For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4 + +Revision 1.183 2004/09/17 00:15:56 cheshire +Rename mDNSPlatformInit_ReceiveUnicast to mDNSPlatformInit_CanReceiveUnicast + +Revision 1.182 2004/09/16 21:36:36 cheshire + Fix unsafe use of mDNSPlatformTimeNow() +Changes to add necessary locking calls around unicast DNS operations + +Revision 1.181 2004/09/16 02:03:42 cheshire + Change address to notify user of kernel flaw + +Revision 1.180 2004/09/16 01:58:22 cheshire +Fix compiler warnings + +Revision 1.179 2004/09/16 00:24:49 cheshire + Fix unsafe use of mDNSPlatformTimeNow() + +Revision 1.178 2004/09/15 21:51:34 cheshire + mDNSResponder should notify user of kernel flaw +Calling CFUserNotificationDisplayNotice too early in the boot process seems to kill +the machine, so make sure we don't do this until at least three minutes after boot. + +Revision 1.177 2004/09/15 01:16:29 cheshire + mDNSResponder should notify user of kernel flaw + +Revision 1.176 2004/09/14 23:42:36 cheshire + Need to seed random number generator from platform-layer data + +Revision 1.175 2004/09/14 21:35:46 cheshire +Minor code tidying, and added comments about CFRetainCounts + +Revision 1.174 2004/09/14 19:14:57 ksekar + DynDNS: Discovery of DynDNS Zones via Reverse-Map PTR + +Revision 1.173 2004/08/25 23:35:22 ksekar +: Error converting shared secret from base-64 to binary + +Revision 1.172 2004/08/25 02:01:45 cheshire + Need to be able to get status of Dynamic DNS Host Name Update + +Revision 1.171 2004/08/25 01:04:42 cheshire +Don't need to CFRelease name and array + +Revision 1.170 2004/08/25 00:37:28 ksekar +: Cleanup DynDNS hostname registration code + +Revision 1.169 2004/08/18 17:35:41 ksekar +: Feature #9586: Need support for Legacy NAT gateways + +Revision 1.168 2004/08/17 03:16:24 ksekar +Fixed checkin 1.166 - enumeration type changed for wrong invocation of mDNS_GetDomains + +Revision 1.167 2004/08/17 00:52:43 ksekar +Fix config file parse error, make semantics match SCPreferences +configuration input. + +Revision 1.166 2004/08/16 19:55:07 ksekar +Change enumeration type to BrowseDefault to construct empty-string +browse list as result of checking 1.161. + +Revision 1.165 2004/08/16 19:52:40 ksekar +Pass computer name + zone for FQDN after keychain notification, +setting global default service registration domain to the zone. + +Revision 1.164 2004/08/16 16:52:37 ksekar +Pass in zone read from keychain to mDNS_SetFQDNs. + +Revision 1.163 2004/08/14 03:22:42 cheshire + Dynamic DNS UI <-> mDNSResponder glue +Add GetUserSpecifiedDDNSName() routine +Convert ServiceRegDomain to domainname instead of C string +Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs + +Revision 1.162 2004/08/12 22:34:00 cheshire +All strings should be read as kCFStringEncodingUTF8, not kCFStringEncodingASCII + +Revision 1.161 2004/08/11 00:17:46 ksekar +: 8A227: Need Lighthouse configred machines to +set default bit for their domains + +Revision 1.160 2004/07/29 19:27:16 ksekar +NATPMP Support - minor fixes and cleanup + +Revision 1.159 2004/07/26 22:49:31 ksekar +: Feature #9516: Need support for NATPMP in client + +Revision 1.158 2004/07/13 21:24:24 rpantos +Fix for . + +Revision 1.157 2004/06/08 18:54:48 ksekar +: mDNSResponder leaks after exploring in Printer Setup Utility + +Revision 1.156 2004/06/05 00:04:26 cheshire +: wide-area domains should be returned in reg. domain enumeration + +Revision 1.155 2004/06/04 08:58:30 ksekar +: Keychain integration for secure dynamic update + +Revision 1.154 2004/05/31 22:22:28 ksekar +: wide-area domains should be returned in +reg. domain enumeration + +Revision 1.153 2004/05/26 17:06:33 cheshire +: Don't rely on CFSocketInvalidate() to remove RunLoopSource + +Revision 1.152 2004/05/18 23:51:26 cheshire +Tidy up all checkin comments to use consistent "" format for bug numbers + +Revision 1.151 2004/05/17 21:46:34 cheshire +: When interface is turned off, browse "remove" events are delivered with interface index zero +Take care to correctly update InterfaceIDs when a dormant interface comes back to life + +Revision 1.150 2004/05/13 04:54:20 ksekar +Unified list copy/free code. Added symetric list for + +Revision 1.149 2004/05/13 03:55:14 ksekar +Fixed list traversal bug in FoundDefSearchDomain. + +Revision 1.148 2004/05/12 22:03:08 ksekar +Made GetSearchDomainList a true platform-layer call (declaration moved +from mDNSMacOSX.h to mDNSEmbeddedAPI.h), impelemted to return "local" +only on non-OSX platforms. Changed call to return a copy of the list +to avoid shared memory issues. Added a routine to free the list. + +Revision 1.147 2004/05/12 02:03:25 ksekar +Non-local domains will only be browsed by default, and show up in +_browse domain enumeration, if they contain an _browse._dns-sd ptr record. + +Revision 1.146 2004/04/27 02:49:15 cheshire +: mDNSResponder leaks sockets on bind() error + +Revision 1.145 2004/04/21 03:08:03 cheshire +Rename 'alias' to more descriptive name 'primary' + +Revision 1.144 2004/04/21 03:04:35 cheshire +Minor cleanup for clarity + +Revision 1.143 2004/04/21 03:03:30 cheshire +Preparation work: AddInterfaceToList() should return pointer to structure it creates + +Revision 1.142 2004/04/21 02:49:11 cheshire +To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx' + +Revision 1.141 2004/04/21 02:20:47 cheshire +Rename interface field 'CurrentlyActive' to more descriptive 'Exists' + +Revision 1.140 2004/04/14 23:09:29 ksekar +Support for TSIG signed dynamic updates. + +Revision 1.139 2004/04/09 17:40:26 cheshire +Remove unnecessary "Multicast" field -- it duplicates the semantics of the existing McastTxRx field + +Revision 1.138 2004/04/09 16:37:16 cheshire +Suggestion from Bob Bradley: +Move NumCacheRecordsForInterfaceID() to DNSCommon.c so it's available to all platform layers + +Revision 1.137 2004/04/08 00:59:55 cheshire + When interface turned off, browse "remove" events delivered with interface index zero +Unify use of the InterfaceID field, and make code that walks the list respect the 'Exists' flag + +Revision 1.136 2004/04/07 01:08:57 cheshire + When interface turned off, browse "remove" events delivered with interface index zero + +Revision 1.135 2004/03/19 01:01:03 ksekar +Fixed config file parsing to chop newline + +Revision 1.134 2004/03/13 01:57:34 ksekar +: DynDNS: Dynamic update of service records + +Revision 1.133 2004/02/02 22:46:56 cheshire +Move "CFRelease(dict);" inside the "if (dict)" check + +Revision 1.132 2004/01/28 02:30:08 ksekar +Added default Search Domains to unicast browsing, controlled via +Networking sharing prefs pane. Stopped sending unicast messages on +every interface. Fixed unicast resolving via mach-port API. + +Revision 1.131 2004/01/27 22:57:48 cheshire +: Need separate socket for issuing unicast queries + +Revision 1.130 2004/01/27 22:28:40 cheshire +: Time to prune obsolete code for listening on port 53 +Additional lingering port 53 code deleted + +Revision 1.129 2004/01/27 20:15:23 cheshire +: Time to prune obsolete code for listening on port 53 + +Revision 1.128 2004/01/24 23:58:17 cheshire +Change to use mDNSVal16() instead of shifting and ORing + +Revision 1.127 2004/01/24 04:59:16 cheshire +Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again + +Revision 1.126 2004/01/23 23:23:15 ksekar +Added TCP support for truncated unicast messages. + +Revision 1.125 2004/01/22 03:43:09 cheshire +Export constants like mDNSInterface_LocalOnly so that the client layers can use them + +Revision 1.124 2004/01/21 21:53:19 cheshire +: Don't try to receive unicast responses if we're not the first to bind to the UDP port + +Revision 1.123 2004/01/20 03:18:25 cheshire +Removed "LogMsg("Hey There!");" that evidently got checked in my mistake + +Revision 1.122 2003/12/17 20:43:59 cheshire +: Syslog messages saying "sendto failed" + +Revision 1.121 2003/12/13 03:05:28 ksekar +: DynDNS: Unicast query of service records + +Revision 1.120 2003/12/08 21:00:46 rpantos +Changes to support mDNSResponder on Linux. + +Revision 1.119 2003/12/03 02:35:15 cheshire +Also report value of m->timenow when logging sendto() failure + +Revision 1.118 2003/11/14 20:59:09 cheshire +Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h. +Best solution is just to combine mDNSEmbeddedAPI.h and mDNSPlatformFunctions.h into a single file. + +Revision 1.117 2003/11/08 22:18:29 cheshire +: Don't need to show process ID in *every* mDNSResponder syslog message + +Revision 1.116 2003/09/23 16:39:49 cheshire +When LogAllOperations is set, also report registration and deregistration of interfaces + +Revision 1.115 2003/09/10 00:45:55 cheshire + Don't log "sendto failed" errors during the first two minutes of startup + +Revision 1.114 2003/08/27 02:55:13 cheshire +: Bug: Don't report mDNSPlatformSendUDP sendto errno 64 (Host is down) + +Revision 1.113 2003/08/19 22:20:00 cheshire + Don't use IPv6 on interfaces that have a routable IPv4 address configured +More minor refinements + +Revision 1.112 2003/08/19 03:04:43 cheshire + Don't use IPv6 on interfaces that have a routable IPv4 address configured + +Revision 1.111 2003/08/18 22:53:37 cheshire + mDNSResponder divide by zero in mDNSPlatformRawTime() + +Revision 1.110 2003/08/16 03:39:00 cheshire + InterfaceID -1 indicates "local only" + +Revision 1.109 2003/08/15 02:19:49 cheshire + syslog messages: myCFSocketCallBack recvfrom skt 6 error -1 errno 35 +Also limit number of messages to at most 100 + +Revision 1.108 2003/08/12 22:24:52 cheshire + syslog messages: myCFSocketCallBack recvfrom skt 6 error -1 errno 35 +This message indicates a kernel bug, but still we don't want to flood syslog. +Do a sleep(1) after writing this log message, to limit the rate. + +Revision 1.107 2003/08/12 19:56:25 cheshire +Update to APSL 2.0 + +Revision 1.106 2003/08/12 13:48:32 cheshire +Add comment explaining clockdivisor calculation + +Revision 1.105 2003/08/12 13:44:14 cheshire + mDNSResponder *VERY* unhappy if time goes backwards +Use mach_absolute_time() (which is guaranteed to always go forwards, resetting only on reboot) +instead of gettimeofday() (which can jump back if the user manually changes their time/date) + +Revision 1.104 2003/08/12 13:12:07 cheshire +Textual search/replace: Indicate local functions using "mDNSlocal" instead of "static" + +Revision 1.103 2003/08/08 18:36:04 cheshire + Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug + +Revision 1.102 2003/08/06 00:14:52 cheshire + Need to check IP TTL on responses +Also add corresponding checks in the IPv6 code path + +Revision 1.101 2003/08/05 22:20:16 cheshire + Need to check IP TTL on responses + +Revision 1.100 2003/08/05 21:18:50 cheshire + mDNSResponder should ignore 6to4 +Only use interfaces that are marked as multicast-capable (IFF_MULTICAST) + +Revision 1.99 2003/08/05 20:13:52 cheshire + mDNSResponder using IPv6 interfaces before they are ready +Ignore interfaces with the IN6_IFF_NOTREADY flag set + +Revision 1.98 2003/07/20 03:38:51 ksekar + +Completed support for Unix-domain socket based API. + +Revision 1.97 2003/07/19 03:15:16 cheshire +Add generic MemAllocate/MemFree prototypes to mDNSPlatformFunctions.h, +and add the obvious trivial implementations to each platform support layer + +Revision 1.96 2003/07/18 00:30:00 cheshire + Remove mDNSResponder version from packet header and use HINFO record instead + +Revision 1.95 2003/07/12 03:15:20 cheshire + After SCDynamicStore notification, mDNSResponder updates +m->hostlabel even if user hasn't actually actually changed their dot-local hostname + +Revision 1.94 2003/07/03 00:51:54 cheshire + When select() and recvmgs() disagree, get more info from kernel about the socket state + +Revision 1.93 2003/07/03 00:09:14 cheshire + New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call +Additional refinement suggested by Josh: Use info->scope_id instead of if_nametoindex(info->ifa_name); + +Revision 1.92 2003/07/02 21:19:51 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.91 2003/06/24 01:53:51 cheshire +Minor update to comments + +Revision 1.90 2003/06/24 01:51:47 cheshire + Oops: Double-dispose of sockets +Don't need to close sockets: CFSocketInvalidate() does that for us + +Revision 1.89 2003/06/21 18:12:47 cheshire + mDNSResponder cannot handle interfaces whose total name is >3 chars +One-line change: should say "IF_NAMESIZE", not sizeof(ifname) + +Revision 1.88 2003/06/12 23:38:37 cheshire + mDNSResponder doesn't detect some configuration changes +Also check that scope_id matches before concluding that two interfaces are the same + +Revision 1.87 2003/06/10 01:14:11 cheshire + New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call + +Revision 1.86 2003/05/28 02:41:52 cheshire + Time to remove Mac OS 9 UDP Port 53 legacy support + +Revision 1.85 2003/05/28 02:39:47 cheshire +Minor change to debugging messages + +Revision 1.84 2003/05/27 22:29:40 cheshire +Remove out-dated comment + +Revision 1.83 2003/05/26 03:21:29 cheshire +Tidy up address structure naming: +mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) +mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 +mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 + +Revision 1.82 2003/05/26 03:01:27 cheshire + sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead + +Revision 1.81 2003/05/24 02:06:42 cheshire + IPv6 Multicast Loopback doesn't work +Tried setting IPV6_MULTICAST_LOOP; it doesn't help. +However, it is probably wise to have the code explicitly set this socket +option anyway, in case the default changes in later versions of Unix. + +Revision 1.80 2003/05/24 02:02:24 cheshire + if_indextoname consumes a lot of CPU +Fix error in myIfIndexToName; was returning prematurely + +Revision 1.79 2003/05/23 23:07:44 cheshire + Must not write to stderr when running as daemon + +Revision 1.78 2003/05/23 01:19:04 cheshire + mDNSResponder needs to signal type of service to AirPort +Mark packets as high-throughput/low-delay (i.e. lowest reliability) to get maximum 802.11 multicast rate + +Revision 1.77 2003/05/23 01:12:05 cheshire +Minor code tidying + +Revision 1.76 2003/05/22 01:26:01 cheshire +Tidy up log messages + +Revision 1.75 2003/05/22 00:07:09 cheshire + myCFSocketCallBack recvfrom(5) error 1, errno 35 +Extra logging to determine whether there is a bug in CFSocket + +Revision 1.74 2003/05/21 20:20:12 cheshire +Fix warnings (mainly printf format string warnings, like using "%d" where +it should say "%lu", etc.) and improve error logging (use strerror() +to include textual error message as well as numeric error in log messages). + +Revision 1.73 2003/05/21 17:56:29 ksekar +: mDNSResponder doesn't watch for IPv6 address changes + +Revision 1.72 2003/05/14 18:48:41 cheshire + mDNSResponder should be smarter about reconfigurations +More minor refinements: +mDNSMacOSX.c needs to do *all* its mDNS_DeregisterInterface calls before freeing memory +mDNS_DeregisterInterface revalidates cache record when *any* representative of an interface goes away + +Revision 1.71 2003/05/14 07:08:37 cheshire + mDNSResponder should be smarter about reconfigurations +Previously, when there was any network configuration change, mDNSResponder +would tear down the entire list of active interfaces and start again. +That was very disruptive, and caused the entire cache to be flushed, +and caused lots of extra network traffic. Now it only removes interfaces +that have really gone, and only adds new ones that weren't there before. + +Revision 1.70 2003/05/07 18:30:24 cheshire +Fix signed/unsigned comparison warning + +Revision 1.69 2003/05/06 20:14:44 cheshire +Change "tp" to "tv" + +Revision 1.68 2003/05/06 00:00:49 cheshire + Rationalize naming of domainname manipulation functions + +Revision 1.67 2003/04/29 00:43:44 cheshire +Fix compiler warnings + +Revision 1.66 2003/04/26 02:41:58 cheshire + Change timenow from a local variable to a structure member + +Revision 1.65 2003/04/26 02:34:01 cheshire +Add missing mDNSexport + +Revision 1.64 2003/04/15 16:48:06 jgraessl + +Modified code in CFSocket notifier function to read all packets on the socket +instead of reading only one packet every time the notifier was called. + +Revision 1.63 2003/04/15 16:33:50 jgraessl + +Switched to our own copy of if_indextoname to improve performance. + +Revision 1.62 2003/03/28 01:55:44 cheshire +Minor improvements to debugging messages + +Revision 1.61 2003/03/27 03:30:56 cheshire + Name conflicts not handled properly, resulting in memory corruption, and eventual crash +Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback +Fixes: +1. Make mDNS_DeregisterInterface() safe to call from a callback +2. Make HostNameCallback() use mDNS_DeadvertiseInterface() instead + (it never really needed to deregister the interface at all) + +Revision 1.60 2003/03/15 04:40:38 cheshire +Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID" + +Revision 1.59 2003/03/11 01:23:26 cheshire + mDNSResponder socket problems + +Revision 1.58 2003/03/06 01:43:04 cheshire + Additional debugging code in mDNSResponder +Improve "LIST_ALL_INTERFACES" output + +Revision 1.57 2003/03/05 22:36:27 cheshire + Loopback doesn't work with mDNSResponder-27 +Temporary workaround: Skip loopback interface *only* if we found at least one v4 interface to use + +Revision 1.56 2003/03/05 01:50:38 cheshire + Additional debugging code in mDNSResponder + +Revision 1.55 2003/02/21 01:54:09 cheshire + mDNSResponder needs performance improvements +Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt") + +Revision 1.54 2003/02/20 06:48:35 cheshire + Xserve RAID needs to do interface-specific registrations +Reviewed by: Josh Graessley, Bob Bradley + +Revision 1.53 2003/01/29 02:21:23 cheshire +Return mStatus_Invalid if can't send packet because socket not available + +Revision 1.52 2003/01/28 19:39:43 jgraessl +Enabling AAAA over IPv4 support. + +Revision 1.51 2003/01/28 05:11:23 cheshire +Fixed backwards comparison in SearchForInterfaceByName + +Revision 1.50 2003/01/13 23:49:44 jgraessl +Merged changes for the following fixes in to top of tree: + computer name changes not handled properly + service name changes are not properly handled + announcements sent in pairs, failing chattiness test + +Revision 1.49 2002/12/23 22:13:30 jgraessl +Reviewed by: Stuart Cheshire +Initial IPv6 support for mDNSResponder. + +Revision 1.48 2002/11/22 01:37:52 cheshire + mDNSResponder is monitoring ServiceEntities instead of InterfaceEntities + +Revision 1.47 2002/09/21 20:44:51 zarzycki +Added APSL info + +Revision 1.46 2002/09/19 21:25:35 cheshire +mDNS_snprintf() doesn't need to be in a separate file + +Revision 1.45 2002/09/17 01:45:13 cheshire +Add LIST_ALL_INTERFACES symbol for debugging + +Revision 1.44 2002/09/17 01:36:23 cheshire +Move Puma support to mDNSMacOSXPuma.c + +Revision 1.43 2002/09/17 01:05:28 cheshire +Change mDNS_AdvertiseLocalAddresses to be an Init parameter instead of a global + +Revision 1.42 2002/09/16 23:13:50 cheshire +Minor code tidying + + */ + +// *************************************************************************** +// mDNSMacOSX.c: +// Supporting routines to run mDNS on a CFRunLoop platform +// *************************************************************************** + +// For debugging, set LIST_ALL_INTERFACES to 1 to display all found interfaces, +// including ones that mDNSResponder chooses not to use. +#define LIST_ALL_INTERFACES 0 + +// For enabling AAAA records over IPv4. Setting this to 0 sends only +// A records over IPv4 and AAAA over IPv6. Setting this to 1 sends both +// AAAA and A records over both IPv4 and IPv6. +#define AAAA_OVER_V4 1 + +#include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above +#include "DNSCommon.h" +#include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform +#include "../mDNSShared/uds_daemon.h" // Defines communication interface from platform layer up to UDS daemon +#include "PlatformCommon.h" + +#include +#include // For va_list support +#include +#include // For IFT_ETHER +#include +#include +#include +#include +#include +#include +#include +#include // platform support for UTC time +#include // for inet_aton + +#include // For IP_RECVTTL +#ifndef IP_RECVTTL +#define IP_RECVTTL 24 // bool; receive reception TTL w/dgram +#endif + +#include // For n_long, required by below +#include // For IPTOS_LOWDELAY etc. +#include // For IN6_IFF_NOTREADY etc. + +#include + +#include +#ifdef MAC_OS_X_VERSION_10_4 +#include +#endif + +// Code contributed by Dave Heller: +// Define RUN_ON_PUMA_WITHOUT_IFADDRS to compile code that will +// work on Mac OS X 10.1, which does not have the getifaddrs call. +#define RUN_ON_PUMA_WITHOUT_IFADDRS 0 +#if RUN_ON_PUMA_WITHOUT_IFADDRS +#include "mDNSMacOSXPuma.c" +#else +#include +#endif + +#include +#include +#include + +typedef struct SearchListElem + { + struct SearchListElem *next; + domainname domain; + int flag; + DNSQuestion BrowseQ; + DNSQuestion DefBrowseQ; + DNSQuestion LegacyBrowseQ; + DNSQuestion RegisterQ; + DNSQuestion DefRegisterQ; + ARListElem *AuthRecs; + } SearchListElem; + + +// *************************************************************************** +// Globals + +static mDNSu32 clockdivisor = 0; + +// for domain enumeration and default browsing/registration +static SearchListElem *SearchList = NULL; // where we search for _browse domains +static DNSQuestion LegacyBrowseDomainQ; // our local enumeration query for _legacy._browse domains +static DNameListElem *DefBrowseList = NULL; // cache of answers to above query (where we search for empty string browses) +static DNameListElem *DefRegList = NULL; // manually generated list of domains where we register for empty string registrations +static ARListElem *SCPrefBrowseDomains = NULL; // manually generated local-only PTR records for browse domains we get from SCPreferences + +static domainname DynDNSRegDomain; // Default wide-area zone for service registration +static domainname DynDNSBrowseDomain; // Default wide-area zone for legacy ("empty string") browses +static domainname DynDNSHostname; + +#define CONFIG_FILE "/etc/mDNSResponder.conf" +#define DYNDNS_KEYCHAIN_SERVICE "DynDNS Shared Secret" +#define SYS_KEYCHAIN_PATH "/Library/Keychains/System.keychain" + +// Function Prototypes +mDNSlocal void SetSCPrefsBrowseDomain(mDNS *m, const domainname *d, mDNSBool add); + +// *************************************************************************** +// Functions + +// routines to allow access to default domain lists from daemon layer + +mDNSexport DNameListElem *mDNSPlatformGetSearchDomainList(void) + { + return mDNS_CopyDNameList(DefBrowseList); + } + +mDNSexport DNameListElem *mDNSPlatformGetRegDomainList(void) + { + return mDNS_CopyDNameList(DefRegList); + } + +// utility routines to manage registration domain lists + +mDNSlocal void AddDefRegDomain(domainname *d) + { + DNameListElem *newelem = NULL, *ptr; + + // make sure name not already in list + for (ptr = DefRegList; ptr; ptr = ptr->next) + { + if (SameDomainName(&ptr->name, d)) + { debugf("duplicate addition of default reg domain %##s", d->c); return; } + } + + newelem = mallocL("DNameListElem", sizeof(*newelem)); + if (!newelem) { LogMsg("Error - malloc"); return; } + AssignDomainName(newelem->name, *d); + newelem->next = DefRegList; + DefRegList = newelem; + + DefaultRegDomainChanged(d, mDNStrue); + udsserver_default_reg_domain_changed(d, mDNStrue); + } + +mDNSlocal void RemoveDefRegDomain(domainname *d) + { + DNameListElem *ptr = DefRegList, *prev = NULL; + + while (ptr) + { + if (SameDomainName(&ptr->name, d)) + { + if (prev) prev->next = ptr->next; + else DefRegList = ptr->next; + freeL("DNameListElem", ptr); + DefaultRegDomainChanged(d, mDNSfalse); + udsserver_default_reg_domain_changed(d, mDNSfalse); + return; + } + prev = ptr; + ptr = ptr->next; + } + debugf("Requested removal of default registration domain %##s not in contained in list", d->c); + } + +mDNSlocal void NotifyOfElusiveBug(const char *title, mDNSu32 radarid, const char *msg) + { + extern mDNS mDNSStorage; + NetworkInterfaceInfoOSX *i; + static int notifyCount = 0; + if (notifyCount) return; + + // If we display our alert early in the boot process, then it vanishes once the desktop appears. + // To avoid this, we don't try to display alerts in the first three minutes after boot. + if ((mDNSu32)(mDNSPlatformRawTime()) < (mDNSu32)(mDNSPlatformOneSecond * 180)) return; + + // Determine if we're at Apple (17.*.*.*) + for (i = mDNSStorage.p->InterfaceList; i; i = i->next) + if (i->ifinfo.ip.type == mDNSAddrType_IPv4 && i->ifinfo.ip.ip.v4.b[0] == 17) + break; + if (!i) return; // If not at Apple, don't show the alert + + // Send a notification to the user to contact coreos-networking + notifyCount++; + CFStringRef alertHeader = CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8); + CFStringRef alertFormat = CFSTR("Congratulations, you've reproduced an elusive bug. Please contact the owner of . %s"); + CFStringRef alertMessage = CFStringCreateWithFormat(NULL, NULL, alertFormat, radarid, msg); + CFUserNotificationDisplayNotice(0.0, kCFUserNotificationStopAlertLevel, NULL, NULL, NULL, alertHeader, alertMessage, NULL); + } + +mDNSlocal struct ifaddrs* myGetIfAddrs(int refresh) + { + static struct ifaddrs *ifa = NULL; + + if (refresh && ifa) + { + freeifaddrs(ifa); + ifa = NULL; + } + + if (ifa == NULL) getifaddrs(&ifa); + + return ifa; + } + +mDNSlocal NetworkInterfaceInfoOSX *SearchForInterfaceByName(mDNS *const m, const char *ifname, int type) + { + NetworkInterfaceInfoOSX *i; + for (i = m->p->InterfaceList; i; i = i->next) + if (i->Exists && !strcmp(i->ifa_name, ifname) && + ((AAAA_OVER_V4 ) || + (type == AF_INET && i->ifinfo.ip.type == mDNSAddrType_IPv4) || + (type == AF_INET6 && i->ifinfo.ip.type == mDNSAddrType_IPv6) )) return(i); + return(NULL); + } + +mDNSlocal int myIfIndexToName(u_short index, char* name) + { + struct ifaddrs *ifa; + for (ifa = myGetIfAddrs(0); ifa; ifa = ifa->ifa_next) + if (ifa->ifa_addr->sa_family == AF_LINK) + if (((struct sockaddr_dl*)ifa->ifa_addr)->sdl_index == index) + { strncpy(name, ifa->ifa_name, IF_NAMESIZE); return 0; } + return -1; + } + +mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(const mDNS *const m, mDNSu32 index) + { + NetworkInterfaceInfoOSX *i; + if (index == (uint32_t)~0) return(mDNSInterface_LocalOnly); + if (index) + for (i = m->p->InterfaceList; i; i = i->next) + // Don't get tricked by inactive interfaces with no InterfaceID set + if (i->ifinfo.InterfaceID && i->scope_id == index) return(i->ifinfo.InterfaceID); + return(mDNSNULL); + } + +mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(const mDNS *const m, mDNSInterfaceID id) + { + NetworkInterfaceInfoOSX *i; + if (id == mDNSInterface_LocalOnly) return((mDNSu32)~0); + if (id) + for (i = m->p->InterfaceList; i; i = i->next) + // Don't use i->ifinfo.InterfaceID here, because we DO want to find inactive interfaces, which have no InterfaceID set + if ((mDNSInterfaceID)i == id) return(i->scope_id); + return 0; + } + +// NOTE: If InterfaceID is NULL, it means, "send this packet through our anonymous unicast socket" +// NOTE: If InterfaceID is non-NULL it means, "send this packet through our port 5353 socket on the specified interface" +// OR send via our primary v4 unicast socket +mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end, + mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstPort) + { + #pragma unused(m) + + // Note: For this platform we've adopted the convention that InterfaceIDs are secretly pointers + // to the NetworkInterfaceInfoOSX structure that holds the active sockets. The mDNSCore code + // doesn't know that and doesn't need to know that -- it just treats InterfaceIDs as opaque identifiers. + NetworkInterfaceInfoOSX *info = (NetworkInterfaceInfoOSX *)InterfaceID; + char *ifa_name = info ? info->ifa_name : "unicast"; + struct sockaddr_storage to; + int s = -1, err; + + // Sanity check: Make sure that if we're sending a query via unicast, we're sending it using our + // anonymous socket created for this purpose, so that we'll receive the response. + // If we use one of the many multicast sockets bound to port 5353 then we may not receive responses reliably. + if (info && !mDNSAddrIsDNSMulticast(dst)) + { + const DNSMessage *const m = (DNSMessage *)msg; + if ((m->h.flags.b[0] & kDNSFlag0_QR_Mask) == kDNSFlag0_QR_Query) + LogMsg("mDNSPlatformSendUDP: ERROR: Sending query OP from mDNS port to non-mDNS destination %#a:%d", dst, mDNSVal16(dstPort)); + } + + if (dst->type == mDNSAddrType_IPv4) + { + struct sockaddr_in *sin_to = (struct sockaddr_in*)&to; + sin_to->sin_len = sizeof(*sin_to); + sin_to->sin_family = AF_INET; + sin_to->sin_port = dstPort.NotAnInteger; + sin_to->sin_addr.s_addr = dst->ip.v4.NotAnInteger; + s = info ? info->ss.sktv4 : m->p->unicastsockets.sktv4; + } + else if (dst->type == mDNSAddrType_IPv6) + { + struct sockaddr_in6 *sin6_to = (struct sockaddr_in6*)&to; + sin6_to->sin6_len = sizeof(*sin6_to); + sin6_to->sin6_family = AF_INET6; + sin6_to->sin6_port = dstPort.NotAnInteger; + sin6_to->sin6_flowinfo = 0; + sin6_to->sin6_addr = *(struct in6_addr*)&dst->ip.v6; + sin6_to->sin6_scope_id = info ? info->scope_id : 0; + s = info ? info->ss.sktv6 : m->p->unicastsockets.sktv6; + } + else + { + LogMsg("mDNSPlatformSendUDP: dst is not an IPv4 or IPv6 address!"); + return mStatus_BadParamErr; + } + + if (s >= 0) + verbosedebugf("mDNSPlatformSendUDP: sending on InterfaceID %p %5s/%ld to %#a:%d skt %d", + InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s); + else + verbosedebugf("mDNSPlatformSendUDP: NOT sending on InterfaceID %p %5s/%ld (socket of this type not available)", + InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort)); + + // Note: When sending, mDNSCore may often ask us to send both a v4 multicast packet and then a v6 multicast packet + // If we don't have the corresponding type of socket available, then return mStatus_Invalid + if (s < 0) return(mStatus_Invalid); + + err = sendto(s, msg, (UInt8*)end - (UInt8*)msg, 0, (struct sockaddr *)&to, to.ss_len); + if (err < 0) + { + // Don't report EHOSTDOWN (i.e. ARP failure), ENETDOWN, or no route to host for unicast destinations + if (!mDNSAddressIsAllDNSLinkGroup(dst) && (errno == EHOSTDOWN || errno == ENETDOWN || errno == EHOSTUNREACH)) return(err); + // Don't report EHOSTUNREACH in the first three minutes after boot + // This is because mDNSResponder intentionally starts up early in the boot process (See ) + // but this means that sometimes it starts before configd has finished setting up the multicast routing entries. + if (errno == EHOSTUNREACH && (mDNSu32)(mDNSPlatformRawTime()) < (mDNSu32)(mDNSPlatformOneSecond * 180)) return(err); + LogMsg("mDNSPlatformSendUDP sendto failed to send packet on InterfaceID %p %5s/%ld to %#a:%d skt %d error %d errno %d (%s) %lu", + InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow)); + return(err); + } + + return(mStatus_NoError); + } + +mDNSlocal ssize_t myrecvfrom(const int s, void *const buffer, const size_t max, + struct sockaddr *const from, size_t *const fromlen, mDNSAddr *dstaddr, char ifname[IF_NAMESIZE], mDNSu8 *ttl) + { + static unsigned int numLogMessages = 0; + struct iovec databuffers = { (char *)buffer, max }; + struct msghdr msg; + ssize_t n; + struct cmsghdr *cmPtr; + char ancillary[1024]; + + *ttl = 255; // If kernel fails to provide TTL data (e.g. Jaguar doesn't) then assume the TTL was 255 as it should be + + // Set up the message + msg.msg_name = (caddr_t)from; + msg.msg_namelen = *fromlen; + msg.msg_iov = &databuffers; + msg.msg_iovlen = 1; + msg.msg_control = (caddr_t)&ancillary; + msg.msg_controllen = sizeof(ancillary); + msg.msg_flags = 0; + + // Receive the data + n = recvmsg(s, &msg, 0); + if (n<0) + { + if (errno != EWOULDBLOCK && numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) returned error %d errno %d", s, n, errno); + return(-1); + } + if (msg.msg_controllen < (int)sizeof(struct cmsghdr)) + { + if (numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) msg.msg_controllen %d < sizeof(struct cmsghdr) %lu", + s, msg.msg_controllen, sizeof(struct cmsghdr)); + return(-1); + } + if (msg.msg_flags & MSG_CTRUNC) + { + if (numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) msg.msg_flags & MSG_CTRUNC", s); + return(-1); + } + + *fromlen = msg.msg_namelen; + + // Parse each option out of the ancillary data. + for (cmPtr = CMSG_FIRSTHDR(&msg); cmPtr; cmPtr = CMSG_NXTHDR(&msg, cmPtr)) + { + // debugf("myrecvfrom cmsg_level %d cmsg_type %d", cmPtr->cmsg_level, cmPtr->cmsg_type); + if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVDSTADDR) + { + dstaddr->type = mDNSAddrType_IPv4; + dstaddr->ip.v4.NotAnInteger = *(u_int32_t*)CMSG_DATA(cmPtr); + } + if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVIF) + { + struct sockaddr_dl *sdl = (struct sockaddr_dl *)CMSG_DATA(cmPtr); + if (sdl->sdl_nlen < IF_NAMESIZE) + { + mDNSPlatformMemCopy(sdl->sdl_data, ifname, sdl->sdl_nlen); + ifname[sdl->sdl_nlen] = 0; + // debugf("IP_RECVIF sdl_index %d, sdl_data %s len %d", sdl->sdl_index, ifname, sdl->sdl_nlen); + } + } + if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVTTL) + { + *ttl = *(u_char*)CMSG_DATA(cmPtr); + } + if (cmPtr->cmsg_level == IPPROTO_IPV6 && cmPtr->cmsg_type == IPV6_PKTINFO) + { + struct in6_pktinfo *ip6_info = (struct in6_pktinfo*)CMSG_DATA(cmPtr); + dstaddr->type = mDNSAddrType_IPv6; + dstaddr->ip.v6 = *(mDNSv6Addr*)&ip6_info->ipi6_addr; + myIfIndexToName(ip6_info->ipi6_ifindex, ifname); + } + if (cmPtr->cmsg_level == IPPROTO_IPV6 && cmPtr->cmsg_type == IPV6_HOPLIMIT) + { + *ttl = *(int*)CMSG_DATA(cmPtr); + } + } + + return(n); + } + +// On entry, context points to our CFSocketSet +// If ss->info is NULL, we received this packet on our anonymous unicast socket +// If ss->info is non-NULL, we received this packet on port 5353 on the indicated interface +mDNSlocal void myCFSocketCallBack(CFSocketRef cfs, CFSocketCallBackType CallBackType, CFDataRef address, const void *data, void *context) + { + mDNSAddr senderAddr, destAddr; + mDNSIPPort senderPort, destPort = MulticastDNSPort; + const CFSocketSet *ss = (const CFSocketSet *)context; + mDNS *const m = ss->m; + mDNSInterfaceID InterfaceID = ss->info ? ss->info->ifinfo.InterfaceID : mDNSNULL; + struct sockaddr_storage from; + size_t fromlen = sizeof(from); + char packetifname[IF_NAMESIZE] = ""; + int err, s1 = -1, skt = CFSocketGetNative(cfs); + int count = 0; + + (void)address; // Parameter not used + (void)data; // Parameter not used + + if (CallBackType != kCFSocketReadCallBack) + LogMsg("myCFSocketCallBack: Why is CallBackType %d not kCFSocketReadCallBack?", CallBackType); + + if (cfs == ss->cfsv4) s1 = ss->sktv4; + else if (cfs == ss->cfsv6) s1 = ss->sktv6; + + if (s1 < 0 || s1 != skt) + { + LogMsg("myCFSocketCallBack: s1 %d native socket %d, cfs %p", s1, skt, cfs); + LogMsg("myCFSocketCallBack: cfsv4 %p, sktv4 %d", ss->cfsv4, ss->sktv4); + LogMsg("myCFSocketCallBack: cfsv6 %p, sktv6 %d", ss->cfsv6, ss->sktv6); + } + + mDNSu8 ttl; + while ((err = myrecvfrom(s1, &m->imsg, sizeof(m->imsg), (struct sockaddr *)&from, &fromlen, &destAddr, packetifname, &ttl)) >= 0) + { + count++; + if (from.ss_family == AF_INET) + { + struct sockaddr_in *sin = (struct sockaddr_in*)&from; + senderAddr.type = mDNSAddrType_IPv4; + senderAddr.ip.v4.NotAnInteger = sin->sin_addr.s_addr; + senderPort.NotAnInteger = sin->sin_port; + } + else if (from.ss_family == AF_INET6) + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&from; + senderAddr.type = mDNSAddrType_IPv6; + senderAddr.ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr; + senderPort.NotAnInteger = sin6->sin6_port; + } + else + { + LogMsg("myCFSocketCallBack from is unknown address family %d", from.ss_family); + return; + } + + if (mDNSAddrIsDNSMulticast(&destAddr)) + { + // Even though we indicated a specific interface in the IP_ADD_MEMBERSHIP call, a weirdness of the + // sockets API means that even though this socket has only officially joined the multicast group + // on one specific interface, the kernel will still deliver multicast packets to it no matter which + // interface they arrive on. According to the official Unix Powers That Be, this is Not A Bug. + // To work around this weirdness, we use the IP_RECVIF option to find the name of the interface + // on which the packet arrived, and ignore the packet if it really arrived on some other interface. + if (!ss->info) + { + verbosedebugf("myCFSocketCallBack got multicast packet from %#a to %#a on unicast socket (Ignored)", &senderAddr, &destAddr); + return; + } + else if (!strcmp(ss->info->ifa_name, packetifname)) + verbosedebugf("myCFSocketCallBack got multicast packet from %#a to %#a on interface %#a/%s", + &senderAddr, &destAddr, &ss->info->ifinfo.ip, ss->info->ifa_name); + else + { + verbosedebugf("myCFSocketCallBack got multicast packet from %#a to %#a on interface %#a/%s (Ignored -- really arrived on interface %s)", + &senderAddr, &destAddr, &ss->info->ifinfo.ip, ss->info->ifa_name, packetifname); + return; + } + } + else + { + // Note: For unicast packets, try to find the matching mDNSCore interface object + // (though we may not be able to, for unicast packets received over something like a PPP link) + NetworkInterfaceInfo *intf = m->HostInterfaces; + while (intf && strcmp(intf->ifname, packetifname)) intf = intf->next; + if (intf) InterfaceID = intf->InterfaceID; + } + + mDNSCoreReceive(m, &m->imsg, (unsigned char*)&m->imsg + err, &senderAddr, senderPort, &destAddr, destPort, InterfaceID); + } + + if (err < 0 && (errno != EWOULDBLOCK || count == 0)) + { + // Something is busted here. + // CFSocket says there is a packet, but myrecvfrom says there is not. + // Try calling select() to get another opinion. + // Find out about other socket parameter that can help understand why select() says the socket is ready for read + // All of this is racy, as data may have arrived after the call to select() + int save_errno = errno; + int so_error = -1; + int so_nread = -1; + int fionread = -1; + int solen = sizeof(int); + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(s1, &readfds); + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + int selectresult = select(s1+1, &readfds, NULL, NULL, &timeout); + if (getsockopt(s1, SOL_SOCKET, SO_ERROR, &so_error, &solen) == -1) + LogMsg("myCFSocketCallBack getsockopt(SO_ERROR) error %d", errno); + if (getsockopt(s1, SOL_SOCKET, SO_NREAD, &so_nread, &solen) == -1) + LogMsg("myCFSocketCallBack getsockopt(SO_NREAD) error %d", errno); + if (ioctl(s1, FIONREAD, &fionread) == -1) + LogMsg("myCFSocketCallBack ioctl(FIONREAD) error %d", errno); + static unsigned int numLogMessages = 0; + if (numLogMessages++ < 100) + LogMsg("myCFSocketCallBack recvfrom skt %d error %d errno %d (%s) select %d (%spackets waiting) so_error %d so_nread %d fionread %d count %d", + s1, err, save_errno, strerror(save_errno), selectresult, FD_ISSET(s1, &readfds) ? "" : "*NO* ", so_error, so_nread, fionread, count); + if (numLogMessages > 5) + NotifyOfElusiveBug("Flaw in Kernel (select/recvfrom mismatch)", 3375328, + "Alternatively, you can send email to radar-3387020@group.apple.com. " + "If possible, please leave your machine undisturbed so that someone can come to investigate the problem."); + + sleep(1); // After logging this error, rate limit so we don't flood syslog + } + } + +// TCP socket support for unicast DNS and Dynamic DNS Update + +typedef struct + { + TCPConnectionCallback callback; + void *context; + int connected; + } tcpInfo_t; + +mDNSlocal void tcpCFSocketCallback(CFSocketRef cfs, CFSocketCallBackType CallbackType, CFDataRef address, + const void *data, void *context) + { + #pragma unused(CallbackType, address, data) + mDNSBool connect = mDNSfalse; + + tcpInfo_t *info = context; + if (!info->connected) + { + connect = mDNStrue; + info->connected = mDNStrue; // prevent connected flag from being set in future callbacks + } + info->callback(CFSocketGetNative(cfs), info->context, connect); + // NOTE: the callback may call CloseConnection here, which frees the context structure! + } + +mDNSexport mStatus mDNSPlatformTCPConnect(const mDNSAddr *dst, mDNSOpaque16 dstport, mDNSInterfaceID InterfaceID, + TCPConnectionCallback callback, void *context, int *descriptor) + { + int sd, on = 1; // "on" for setsockopt + struct sockaddr_in saddr; + CFSocketContext cfContext = { 0, NULL, 0, 0, 0 }; + tcpInfo_t *info; + CFSocketRef sr; + CFRunLoopSourceRef rls; + + (void)InterfaceID; //!!!KRS use this if non-zero!!! + + *descriptor = 0; + if (dst->type != mDNSAddrType_IPv4) + { + LogMsg("ERROR: mDNSPlatformTCPConnect - attempt to connect to an IPv6 address: opperation not supported"); + return mStatus_UnknownErr; + } + + sd = socket(AF_INET, SOCK_STREAM, 0); + if (sd < 3) { LogMsg("mDNSPlatformTCPConnect: socket error %d errno %d (%s)", sd, errno, strerror(errno)); return mStatus_UnknownErr; } + + // set non-blocking + if (fcntl(sd, F_SETFL, O_NONBLOCK) < 0) + { + LogMsg("ERROR: setsockopt O_NONBLOCK - %s", strerror(errno)); + return mStatus_UnknownErr; + } + + // receive interface identifiers + if (setsockopt(sd, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)) < 0) + { + LogMsg("setsockopt IP_RECVIF - %s", strerror(errno)); + return mStatus_UnknownErr; + } + // set up CF wrapper, add to Run Loop + info = mallocL("mDNSPlatformTCPConnect", sizeof(tcpInfo_t)); + info->callback = callback; + info->context = context; + cfContext.info = info; + sr = CFSocketCreateWithNative(kCFAllocatorDefault, sd, kCFSocketReadCallBack | kCFSocketConnectCallBack, + tcpCFSocketCallback, &cfContext); + if (!sr) + { + LogMsg("ERROR: mDNSPlatformTCPConnect - CFSocketRefCreateWithNative failed"); + freeL("mDNSPlatformTCPConnect", info); + return mStatus_UnknownErr; + } + + rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, sr, 0); + if (!rls) + { + LogMsg("ERROR: mDNSPlatformTCPConnect - CFSocketCreateRunLoopSource failed"); + freeL("mDNSPlatformTCPConnect", info); + return mStatus_UnknownErr; + } + + CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); + CFRelease(rls); + + // initiate connection wth peer + bzero(&saddr, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = dstport.NotAnInteger; + memcpy(&saddr.sin_addr, &dst->ip.v4.NotAnInteger, sizeof(saddr.sin_addr)); + if (connect(sd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) + { + if (errno == EINPROGRESS) + { + info->connected = 0; + *descriptor= sd; + return mStatus_ConnPending; + } + LogMsg("ERROR: mDNSPlatformTCPConnect - connect failed: %s", strerror(errno)); + freeL("mDNSPlatformTCPConnect", info); + CFSocketInvalidate(sr); + return mStatus_ConnFailed; + } + info->connected = 1; + *descriptor = sd; + return mStatus_ConnEstablished; + } + +mDNSexport void mDNSPlatformTCPCloseConnection(int sd) + { + CFSocketContext cfContext; + tcpInfo_t *info; + CFSocketRef sr; + + if (sd < 3) LogMsg("mDNSPlatformTCPCloseConnection: ERROR sd %d < 3", sd); + + // Get the CFSocket for the descriptor + sr = CFSocketCreateWithNative(kCFAllocatorDefault, sd, kCFSocketNoCallBack, NULL, NULL); + if (!sr) { LogMsg("ERROR: mDNSPlatformTCPCloseConnection - CFSocketCreateWithNative returned NULL"); return; } + + CFSocketGetContext(sr, &cfContext); + if (!cfContext.info) + { + LogMsg("ERROR: mDNSPlatformTCPCloseConnection - could not retreive tcpInfo from socket context"); + CFRelease(sr); + return; + } + CFRelease(sr); // this only releases the copy we allocated with CreateWithNative above + + info = cfContext.info; + + // Note: MUST NOT close the underlying native BSD socket. + // CFSocketInvalidate() will do that for us, in its own good time, which may not necessarily be immediately, + // because it first has to unhook the sockets from its select() call, before it can safely close them. + CFSocketInvalidate(sr); + CFRelease(sr); + freeL("mDNSPlatformTCPCloseConnection", info); + } + +mDNSexport int mDNSPlatformReadTCP(int sd, void *buf, int buflen) + { + int nread = recv(sd, buf, buflen, 0); + if (nread < 0) + { + if (errno == EAGAIN) return 0; // no data available (call would block) + LogMsg("ERROR: mDNSPlatformReadTCP - recv: %s", strerror(errno)); + return -1; + } + return nread; + } + +mDNSexport int mDNSPlatformWriteTCP(int sd, const char *msg, int len) + { + int nsent = send(sd, msg, len, 0); + + if (nsent < 0) + { + if (errno == EAGAIN) return 0; // blocked + LogMsg("ERROR: mDNSPlatformWriteTCP - sendL %s", strerror(errno)); + return -1; + } + return nsent; + } + +// This gets the text of the field currently labelled "Computer Name" in the Sharing Prefs Control Panel +mDNSlocal void GetUserSpecifiedFriendlyComputerName(domainlabel *const namelabel) + { + CFStringEncoding encoding = kCFStringEncodingUTF8; + CFStringRef cfs = SCDynamicStoreCopyComputerName(NULL, &encoding); + if (cfs) + { + CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); + CFRelease(cfs); + } + } + +// This gets the text of the field currently labelled "Local Hostname" in the Sharing Prefs Control Panel +mDNSlocal void GetUserSpecifiedLocalHostName(domainlabel *const namelabel) + { + CFStringRef cfs = SCDynamicStoreCopyLocalHostName(NULL); + if (cfs) + { + CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); + CFRelease(cfs); + } + } + +mDNSlocal void GetUserSpecifiedDDNSConfig(domainname *const fqdn, domainname *const regDomain, domainname *const browseDomain) + { + char buf[MAX_ESCAPED_DOMAIN_NAME]; + + fqdn->c[0] = 0; + regDomain->c[0] = 0; + browseDomain->c[0] = 0; + buf[0] = 0; + + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:GetUserSpecifiedDDNSConfig"), NULL, NULL); + if (store) + { + CFDictionaryRef dict = SCDynamicStoreCopyValue(store, CFSTR("Setup:/Network/DynamicDNS")); + if (dict) + { + CFArrayRef fqdnArray = CFDictionaryGetValue(dict, CFSTR("FQDN")); + CFArrayRef regArray = CFDictionaryGetValue(dict, CFSTR("DefaultRegistrationDomain")); + CFArrayRef browseArray = CFDictionaryGetValue(dict, CFSTR("DefaultBrowseDomain")); + if (fqdnArray) + { + CFStringRef name = CFArrayGetValueAtIndex(fqdnArray, 0); + if (name) + { + if (!CFStringGetCString(name, buf, sizeof(buf), kCFStringEncodingUTF8) || + !MakeDomainNameFromDNSNameString(fqdn, buf) || !fqdn->c[0]) + LogMsg("GetUserSpecifiedDDNSConfig SCDynamicStore bad DDNS host name: %s", buf[0] ? buf : "(unknown)"); + else debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS host name: %s", buf); + } + } + if (regArray) + { + CFStringRef name = CFArrayGetValueAtIndex(regArray, 0); + if (name) + { + if (!CFStringGetCString(name, buf, sizeof(buf), kCFStringEncodingUTF8) || + !MakeDomainNameFromDNSNameString(regDomain, buf) || !regDomain->c[0]) + LogMsg("GetUserSpecifiedDDNSConfig SCDynamicStore bad DDNS registration domain: %s", buf[0] ? buf : "(unknown)"); + else debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS registration zone: %s", buf); + } + } + if (browseArray) + { + CFStringRef name = CFArrayGetValueAtIndex(browseArray, 0); + if (name) + { + if (!CFStringGetCString(name, buf, sizeof(buf), kCFStringEncodingUTF8) || + !MakeDomainNameFromDNSNameString(browseDomain, buf) || !browseDomain->c[0]) + LogMsg("GetUserSpecifiedDDNSConfig SCDynamicStore bad DDNS browse domain: %s", buf[0] ? buf : "(unknown)"); + else debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS browse zone: %s", buf); + } + } + CFRelease(dict); + } + CFRelease(store); + } + } + +mDNSlocal void SetDDNSNameStatus(domainname *const dname, mStatus status) + { + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:SetDDNSNameStatus"), NULL, NULL); + if (store) + { + char uname[MAX_ESCAPED_DOMAIN_NAME]; + ConvertDomainNameToCString(dname, uname); + char *p = uname; + while (*p) { *p = tolower(*p); p++; } + const void *n1 = CFStringCreateWithCString(NULL, uname, kCFStringEncodingUTF8); // CFStringRef + const void *v1 = CFNumberCreate(NULL, kCFNumberSInt32Type, &status); // CFNumberRef + const void *n2 = CFSTR("FQDN"); // CFStringRef + const void *v2 = CFDictionaryCreate(NULL, &n1, &v1, 1, NULL, NULL); // CFDictionaryRef + CFDictionaryRef dict = CFDictionaryCreate(NULL, &n2, &v2, 1, NULL, NULL); + SCDynamicStoreSetValue(store, CFSTR("State:/Network/DynamicDNS"), dict); + CFRelease(dict); + CFRelease(v2); + CFRelease(n2); + CFRelease(v1); + CFRelease(n1); + CFRelease(store); + } + } + +// If mDNSIPPort port is non-zero, then it's a multicast socket on the specified interface +// If mDNSIPPort port is zero, then it's a randomly assigned port number, used for sending unicast queries +mDNSlocal mStatus SetupSocket(mDNS *const m, CFSocketSet *cp, mDNSBool mcast, const mDNSAddr *ifaddr, u_short sa_family) + { + int *s = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6; + CFSocketRef *c = (sa_family == AF_INET) ? &cp->cfsv4 : &cp->cfsv6; + CFRunLoopSourceRef *r = (sa_family == AF_INET) ? &cp->rlsv4 : &cp->rlsv6; + const int on = 1; + const int twofivefive = 255; + mStatus err = mStatus_NoError; + char *errstr = mDNSNULL; + + mDNSIPPort port = (mcast | m->CanReceiveUnicastOn5353) ? MulticastDNSPort : zeroIPPort; + + if (*s >= 0) { LogMsg("SetupSocket ERROR: socket %d is already set", *s); return(-1); } + if (*c) { LogMsg("SetupSocket ERROR: CFSocketRef %p is already set", *c); return(-1); } + + // Open the socket... + int skt = socket(sa_family, SOCK_DGRAM, IPPROTO_UDP); + if (skt < 3) { LogMsg("SetupSocket: socket error %d errno %d (%s)", skt, errno, strerror(errno)); return(skt); } + + // ... with a shared UDP port, if it's for multicast receiving + if (port.NotAnInteger) err = setsockopt(skt, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); + if (err < 0) { errstr = "setsockopt - SO_REUSEPORT"; goto fail; } + + if (sa_family == AF_INET) + { + // We want to receive destination addresses + err = setsockopt(skt, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)); + if (err < 0) { errstr = "setsockopt - IP_RECVDSTADDR"; goto fail; } + + // We want to receive interface identifiers + err = setsockopt(skt, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)); + if (err < 0) { errstr = "setsockopt - IP_RECVIF"; goto fail; } + + // We want to receive packet TTL value so we can check it + err = setsockopt(skt, IPPROTO_IP, IP_RECVTTL, &on, sizeof(on)); + // We ignore errors here -- we already know Jaguar doesn't support this, but we can get by without it + + // Add multicast group membership on this interface, if it's for multicast receiving + if (mcast) + { + struct in_addr addr = { ifaddr->ip.v4.NotAnInteger }; + struct ip_mreq imr; + imr.imr_multiaddr.s_addr = AllDNSLinkGroupv4.NotAnInteger; + imr.imr_interface = addr; + err = setsockopt(skt, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)); + if (err < 0) { errstr = "setsockopt - IP_ADD_MEMBERSHIP"; goto fail; } + + // Specify outgoing interface too + err = setsockopt(skt, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr)); + if (err < 0) { errstr = "setsockopt - IP_MULTICAST_IF"; goto fail; } + } + + // Send unicast packets with TTL 255 + err = setsockopt(skt, IPPROTO_IP, IP_TTL, &twofivefive, sizeof(twofivefive)); + if (err < 0) { errstr = "setsockopt - IP_TTL"; goto fail; } + + // And multicast packets with TTL 255 too + err = setsockopt(skt, IPPROTO_IP, IP_MULTICAST_TTL, &twofivefive, sizeof(twofivefive)); + if (err < 0) { errstr = "setsockopt - IP_MULTICAST_TTL"; goto fail; } + + // Mark packets as high-throughput/low-delay (i.e. lowest reliability) to get maximum 802.11 multicast rate + const int ip_tosbits = IPTOS_LOWDELAY | IPTOS_THROUGHPUT; + err = setsockopt(skt, IPPROTO_IP, IP_TOS, &ip_tosbits, sizeof(ip_tosbits)); + if (err < 0) { errstr = "setsockopt - IP_TOS"; goto fail; } + + // And start listening for packets + struct sockaddr_in listening_sockaddr; + listening_sockaddr.sin_family = AF_INET; + listening_sockaddr.sin_port = port.NotAnInteger; + listening_sockaddr.sin_addr.s_addr = 0; // Want to receive multicasts AND unicasts on this socket + err = bind(skt, (struct sockaddr *) &listening_sockaddr, sizeof(listening_sockaddr)); + if (err) { errstr = "bind"; goto fail; } + } + else if (sa_family == AF_INET6) + { + // We want to receive destination addresses and receive interface identifiers + err = setsockopt(skt, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on)); + if (err < 0) { errstr = "setsockopt - IPV6_PKTINFO"; goto fail; } + + // We want to receive packet hop count value so we can check it + err = setsockopt(skt, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, sizeof(on)); + if (err < 0) { errstr = "setsockopt - IPV6_HOPLIMIT"; goto fail; } + + // We want to receive only IPv6 packets, without this option, we may + // get IPv4 addresses as mapped addresses. + err = setsockopt(skt, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); + if (err < 0) { errstr = "setsockopt - IPV6_V6ONLY"; goto fail; } + + if (mcast) + { + // Add multicast group membership on this interface, if it's for multicast receiving + int interface_id = if_nametoindex(cp->info->ifa_name); + struct ipv6_mreq i6mr; + i6mr.ipv6mr_interface = interface_id; + i6mr.ipv6mr_multiaddr = *(struct in6_addr*)&AllDNSLinkGroupv6; + err = setsockopt(skt, IPPROTO_IPV6, IPV6_JOIN_GROUP, &i6mr, sizeof(i6mr)); + if (err < 0) { errstr = "setsockopt - IPV6_JOIN_GROUP"; goto fail; } + + // Specify outgoing interface too + err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_IF, &interface_id, sizeof(interface_id)); + if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_IF"; goto fail; } + } + + // Send unicast packets with TTL 255 + err = setsockopt(skt, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &twofivefive, sizeof(twofivefive)); + if (err < 0) { errstr = "setsockopt - IPV6_UNICAST_HOPS"; goto fail; } + + // And multicast packets with TTL 255 too + err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &twofivefive, sizeof(twofivefive)); + if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_HOPS"; goto fail; } + + // Note: IPV6_TCLASS appears not to be implemented on OS X right now (or indeed on ANY version of Unix?) + #ifdef IPV6_TCLASS + // Mark packets as high-throughput/low-delay (i.e. lowest reliability) to get maximum 802.11 multicast rate + int tclass = IPTOS_LOWDELAY | IPTOS_THROUGHPUT; // This may not be right (since tclass is not implemented on OS X, I can't test it) + err = setsockopt(skt, IPPROTO_IPV6, IPV6_TCLASS, &tclass, sizeof(tclass)); + if (err < 0) { errstr = "setsockopt - IPV6_TCLASS"; goto fail; } + #endif + + // Want to receive our own packets + err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(on)); + if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_LOOP"; goto fail; } + + // And start listening for packets + struct sockaddr_in6 listening_sockaddr6; + bzero(&listening_sockaddr6, sizeof(listening_sockaddr6)); + listening_sockaddr6.sin6_len = sizeof(listening_sockaddr6); + listening_sockaddr6.sin6_family = AF_INET6; + listening_sockaddr6.sin6_port = port.NotAnInteger; + listening_sockaddr6.sin6_flowinfo = 0; +// listening_sockaddr6.sin6_addr = IN6ADDR_ANY_INIT; // Want to receive multicasts AND unicasts on this socket + listening_sockaddr6.sin6_scope_id = 0; + err = bind(skt, (struct sockaddr *) &listening_sockaddr6, sizeof(listening_sockaddr6)); + if (err) { errstr = "bind"; goto fail; } + } + + fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK); // set non-blocking + *s = skt; + CFSocketContext myCFSocketContext = { 0, cp, NULL, NULL, NULL }; + *c = CFSocketCreateWithNative(kCFAllocatorDefault, *s, kCFSocketReadCallBack, myCFSocketCallBack, &myCFSocketContext); + *r = CFSocketCreateRunLoopSource(kCFAllocatorDefault, *c, 0); + CFRunLoopAddSource(CFRunLoopGetCurrent(), *r, kCFRunLoopDefaultMode); + + return(err); + +fail: + LogMsg("%s error %ld errno %d (%s)", errstr, err, errno, strerror(errno)); + if (!strcmp(errstr, "bind") && errno == EADDRINUSE) + NotifyOfElusiveBug("Setsockopt SO_REUSEPORT failed", 3814904, + "Alternatively, you can send email to radar-3387020@group.apple.com. " + "If possible, please leave your machine undisturbed so that someone can come to investigate the problem."); + close(skt); + return(err); + } + +mDNSlocal mStatus SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa) + { + if (!sa) { LogMsg("SetupAddr ERROR: NULL sockaddr"); return(mStatus_Invalid); } + + if (sa->sa_family == AF_INET) + { + struct sockaddr_in *ifa_addr = (struct sockaddr_in *)sa; + ip->type = mDNSAddrType_IPv4; + ip->ip.v4.NotAnInteger = ifa_addr->sin_addr.s_addr; + return(mStatus_NoError); + } + + if (sa->sa_family == AF_INET6) + { + struct sockaddr_in6 *ifa_addr = (struct sockaddr_in6 *)sa; + ip->type = mDNSAddrType_IPv6; + if (IN6_IS_ADDR_LINKLOCAL(&ifa_addr->sin6_addr)) ifa_addr->sin6_addr.__u6_addr.__u6_addr16[1] = 0; + ip->ip.v6 = *(mDNSv6Addr*)&ifa_addr->sin6_addr; + return(mStatus_NoError); + } + + LogMsg("SetupAddr invalid sa_family %d", sa->sa_family); + return(mStatus_Invalid); + } + +mDNSlocal mDNSEthAddr GetBSSID(char *ifa_name) + { + mDNSEthAddr eth = zeroEthAddr; + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:GetBSSID"), NULL, NULL); + if (store) + { + CFStringRef entityname = CFStringCreateWithFormat(NULL, NULL, CFSTR("State:/Network/Interface/%s/AirPort"), ifa_name); + if (entityname) + { + CFDictionaryRef dict = SCDynamicStoreCopyValue(store, entityname); + if (dict) + { + CFRange range = { 0, 6 }; // Offset, length + CFDataRef data = CFDictionaryGetValue(dict, CFSTR("BSSID")); + if (data && CFDataGetLength(data) == 6) CFDataGetBytes(data, range, eth.b); + CFRelease(dict); + } + CFRelease(entityname); + } + CFRelease(store); + } + return(eth); + } + +mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifaddrs *ifa) + { + mDNSu32 scope_id = if_nametoindex(ifa->ifa_name); + mDNSEthAddr bssid = GetBSSID(ifa->ifa_name); + + mDNSAddr ip, mask; + if (SetupAddr(&ip, ifa->ifa_addr ) != mStatus_NoError) return(NULL); + if (SetupAddr(&mask, ifa->ifa_netmask) != mStatus_NoError) return(NULL); + + NetworkInterfaceInfoOSX **p; + for (p = &m->p->InterfaceList; *p; p = &(*p)->next) + if (scope_id == (*p)->scope_id && mDNSSameAddress(&ip, &(*p)->ifinfo.ip) && mDNSSameEthAddress(&bssid, &(*p)->BSSID)) + { + debugf("AddInterfaceToList: Found existing interface %lu %.6a with address %#a at %p", scope_id, &bssid, &ip, *p); + (*p)->Exists = mDNStrue; + return(*p); + } + + NetworkInterfaceInfoOSX *i = (NetworkInterfaceInfoOSX *)mallocL("NetworkInterfaceInfoOSX", sizeof(*i)); + debugf("AddInterfaceToList: Making new interface %lu %.6a with address %#a at %p", scope_id, &bssid, &ip, i); + if (!i) return(mDNSNULL); + bzero(i, sizeof(NetworkInterfaceInfoOSX)); + i->ifa_name = (char *)mallocL("NetworkInterfaceInfoOSX name", strlen(ifa->ifa_name) + 1); + if (!i->ifa_name) { freeL("NetworkInterfaceInfoOSX", i); return(mDNSNULL); } + strcpy(i->ifa_name, ifa->ifa_name); + + i->ifinfo.InterfaceID = mDNSNULL; + i->ifinfo.ip = ip; + i->ifinfo.mask = mask; + strncpy(i->ifinfo.ifname, ifa->ifa_name, sizeof(i->ifinfo.ifname)); + i->ifinfo.ifname[sizeof(i->ifinfo.ifname)-1] = 0; + i->ifinfo.Advertise = m->AdvertiseLocalAddresses; + i->ifinfo.McastTxRx = mDNSfalse; // For now; will be set up later at the end of UpdateInterfaceList + + i->next = mDNSNULL; + i->Exists = mDNStrue; + i->scope_id = scope_id; + i->BSSID = bssid; + i->sa_family = ifa->ifa_addr->sa_family; + i->Multicast = (ifa->ifa_flags & IFF_MULTICAST) && !(ifa->ifa_flags & IFF_POINTOPOINT); + + i->ss.m = m; + i->ss.info = i; + i->ss.sktv4 = i->ss.sktv6 = -1; + i->ss.cfsv4 = i->ss.cfsv6 = NULL; + i->ss.rlsv4 = i->ss.rlsv6 = NULL; + + *p = i; + return(i); + } + +mDNSlocal NetworkInterfaceInfoOSX *FindRoutableIPv4(mDNS *const m, mDNSu32 scope_id) + { + NetworkInterfaceInfoOSX *i; + for (i = m->p->InterfaceList; i; i = i->next) + if (i->Exists && i->scope_id == scope_id && i->ifinfo.ip.type == mDNSAddrType_IPv4) + if (!(i->ifinfo.ip.ip.v4.b[0] == 169 && i->ifinfo.ip.ip.v4.b[1] == 254)) + return(i); + return(mDNSNULL); + } + +mDNSlocal mStatus UpdateInterfaceList(mDNS *const m) + { + mDNSBool foundav4 = mDNSfalse; + mDNSBool foundav6 = mDNSfalse; + struct ifaddrs *ifa = myGetIfAddrs(1); + struct ifaddrs *v4Loopback = NULL; + struct ifaddrs *v6Loopback = NULL; + mDNSEthAddr PrimaryMAC = zeroEthAddr; + char defaultname[32]; + int InfoSocket = socket(AF_INET6, SOCK_DGRAM, 0); + if (InfoSocket < 3) LogMsg("UpdateInterfaceList: InfoSocket error %d errno %d (%s)", InfoSocket, errno, strerror(errno)); + if (m->SleepState) ifa = NULL; + + while (ifa) + { +#if LIST_ALL_INTERFACES + if (ifa->ifa_addr->sa_family == AF_APPLETALK) + debugf("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d is AF_APPLETALK", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); + else if (ifa->ifa_addr->sa_family == AF_LINK) + debugf("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d is AF_LINK", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); + else if (ifa->ifa_addr->sa_family != AF_INET && ifa->ifa_addr->sa_family != AF_INET6) + debugf("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d not AF_INET (2) or AF_INET6 (30)", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); + if (!(ifa->ifa_flags & IFF_UP)) + debugf("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d Interface not IFF_UP", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); + if (!(ifa->ifa_flags & IFF_MULTICAST)) + debugf("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d Interface not IFF_MULTICAST", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); + if (ifa->ifa_flags & IFF_POINTOPOINT) + debugf("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d Interface IFF_POINTOPOINT", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); + if (ifa->ifa_flags & IFF_LOOPBACK) + debugf("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d Interface IFF_LOOPBACK", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); +#endif + + if (ifa->ifa_addr->sa_family == AF_LINK) + { + struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr; + if (sdl->sdl_type == IFT_ETHER && sdl->sdl_alen == sizeof(PrimaryMAC) && mDNSSameEthAddress(&PrimaryMAC, &zeroEthAddr)) + mDNSPlatformMemCopy(sdl->sdl_data + sdl->sdl_nlen, PrimaryMAC.b, 6); + } + + if (ifa->ifa_flags & IFF_UP && ifa->ifa_addr) + if (ifa->ifa_addr->sa_family == AF_INET || ifa->ifa_addr->sa_family == AF_INET6) + { + if (!ifa->ifa_netmask) + { + mDNSAddr ip; + SetupAddr(&ip, ifa->ifa_addr); + LogMsg("getifaddrs: ifa_netmask is NULL for %5s(%d) Flags %04X Family %2d %#a", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family, &ip); + } + else if (ifa->ifa_addr->sa_family != ifa->ifa_netmask->sa_family) + { + mDNSAddr ip; + SetupAddr(&ip, ifa->ifa_addr); + LogMsg("getifaddrs ifa_netmask for %5s(%d) Flags %04X Family %2d %#a has different family: %d", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family, &ip, ifa->ifa_netmask->sa_family); + } + else + { + int ifru_flags6 = 0; + if (ifa->ifa_addr->sa_family == AF_INET6 && InfoSocket >= 0) + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; + struct in6_ifreq ifr6; + bzero((char *)&ifr6, sizeof(ifr6)); + strncpy(ifr6.ifr_name, ifa->ifa_name, sizeof(ifr6.ifr_name)); + ifr6.ifr_addr = *sin6; + if (ioctl(InfoSocket, SIOCGIFAFLAG_IN6, &ifr6) != -1) + ifru_flags6 = ifr6.ifr_ifru.ifru_flags6; + verbosedebugf("%s %.16a %04X %04X", ifa->ifa_name, &sin6->sin6_addr, ifa->ifa_flags, ifru_flags6); + } + if (!(ifru_flags6 & (IN6_IFF_NOTREADY | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED | IN6_IFF_TEMPORARY))) + { + if (ifa->ifa_flags & IFF_LOOPBACK) + if (ifa->ifa_addr->sa_family == AF_INET) v4Loopback = ifa; + else v6Loopback = ifa; + else + { + AddInterfaceToList(m, ifa); + if (ifa->ifa_addr->sa_family == AF_INET) foundav4 = mDNStrue; + else foundav6 = mDNStrue; + } + } + } + } + ifa = ifa->ifa_next; + } + + // For efficiency, we don't register a loopback interface when other interfaces of that family are available + if (!foundav4 && v4Loopback) AddInterfaceToList(m, v4Loopback); + if (!foundav6 && v6Loopback) AddInterfaceToList(m, v6Loopback); + + // Now the list is complete, set the McastTxRx setting for each interface. + // We always send and receive using IPv4. + // To reduce traffic, we send and receive using IPv6 only on interfaces that have no routable IPv4 address. + // Having a routable IPv4 address assigned is a reasonable indicator of being on a large configured network, + // which means there's a good chance that most or all the other devices on that network should also have v4. + // By doing this we lose the ability to talk to true v6-only devices on that link, but we cut the packet rate in half. + // At this time, reducing the packet rate is more important than v6-only devices on a large configured network, + // so we are willing to make that sacrifice. + NetworkInterfaceInfoOSX *i; + for (i = m->p->InterfaceList; i; i = i->next) + if (i->Exists) + { + mDNSBool txrx = i->Multicast && ((i->ifinfo.ip.type == mDNSAddrType_IPv4) || !FindRoutableIPv4(m, i->scope_id)); + if (i->ifinfo.McastTxRx != txrx) + { + i->ifinfo.McastTxRx = txrx; + i->Exists = 2; // State change; need to deregister and reregister this interface + } + } + if (InfoSocket >= 0) close(InfoSocket); + + mDNS_snprintf(defaultname, sizeof(defaultname), "Macintosh-%02X%02X%02X%02X%02X%02X", + PrimaryMAC.b[0], PrimaryMAC.b[1], PrimaryMAC.b[2], PrimaryMAC.b[3], PrimaryMAC.b[4], PrimaryMAC.b[5]); + + // Set up the nice label + domainlabel nicelabel; + nicelabel.c[0] = 0; + GetUserSpecifiedFriendlyComputerName(&nicelabel); + if (nicelabel.c[0] == 0) MakeDomainLabelFromLiteralString(&nicelabel, defaultname); + + // Set up the RFC 1034-compliant label + domainlabel hostlabel; + hostlabel.c[0] = 0; + GetUserSpecifiedLocalHostName(&hostlabel); + if (hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&hostlabel, defaultname); + + if (SameDomainLabel(m->p->usernicelabel.c, nicelabel.c)) + debugf("Usernicelabel (%#s) unchanged since last time; not changing m->nicelabel (%#s)", m->p->usernicelabel.c, m->nicelabel.c); + else + { + debugf("Updating m->nicelabel to %#s", nicelabel.c); + m->p->usernicelabel = m->nicelabel = nicelabel; + } + + if (SameDomainLabel(m->p->userhostlabel.c, hostlabel.c)) + debugf("Userhostlabel (%#s) unchanged since last time; not changing m->hostlabel (%#s)", m->p->userhostlabel.c, m->hostlabel.c); + else + { + debugf("Updating m->hostlabel to %#s", hostlabel.c); + m->p->userhostlabel = m->hostlabel = hostlabel; + mDNS_SetFQDN(m); + } + + return(mStatus_NoError); + } + +// returns count of non-link local V4 addresses registered +mDNSlocal int SetupActiveInterfaces(mDNS *const m) + { + NetworkInterfaceInfoOSX *i; + int count = 0; + for (i = m->p->InterfaceList; i; i = i->next) + if (i->Exists) + { + NetworkInterfaceInfo *n = &i->ifinfo; + NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifa_name, i->sa_family); + if (!primary) LogMsg("SetupActiveInterfaces ERROR! SearchForInterfaceByName didn't find %s", i->ifa_name); + + if (n->InterfaceID && n->InterfaceID != (mDNSInterfaceID)primary) // Sanity check + { + LogMsg("SetupActiveInterfaces ERROR! n->InterfaceID %p != primary %p", n->InterfaceID, primary); + n->InterfaceID = mDNSNULL; + } + + if (!n->InterfaceID) + { + // NOTE: If n->InterfaceID is set, that means we've called mDNS_RegisterInterface() for this interface, + // so we need to make sure we call mDNS_DeregisterInterface() before disposing it. + // If n->InterfaceID is NOT set, then we haven't registered it and we should not try to deregister it + n->InterfaceID = (mDNSInterfaceID)primary; + mDNS_RegisterInterface(m, n); + if (i->ifinfo.ip.type == mDNSAddrType_IPv4 && (i->ifinfo.ip.ip.v4.b[0] != 169 || i->ifinfo.ip.ip.v4.b[1] != 254)) count++; + LogOperation("SetupActiveInterfaces: Registered %5s(%lu) %.6a InterfaceID %p %#a/%#a%s", + i->ifa_name, i->scope_id, &i->BSSID, primary, &n->ip, &n->mask, n->InterfaceActive ? " (Primary)" : ""); + } + + if (!n->McastTxRx) + debugf("SetupActiveInterfaces: No Tx/Rx on %5s(%lu) %.6a InterfaceID %p %#a", i->ifa_name, i->scope_id, &i->BSSID, primary, &n->ip); + else + { + if (i->sa_family == AF_INET && primary->ss.sktv4 == -1) + { + mStatus err = SetupSocket(m, &primary->ss, mDNStrue, &i->ifinfo.ip, AF_INET); + if (err == 0) debugf("SetupActiveInterfaces: v4 socket%2d %5s(%lu) %.6a InterfaceID %p %#a", primary->ss.sktv4, i->ifa_name, i->scope_id, &i->BSSID, n->InterfaceID, &n->ip); + else LogMsg("SetupActiveInterfaces: v4 socket%2d %5s(%lu) %.6a InterfaceID %p %#a FAILED", primary->ss.sktv4, i->ifa_name, i->scope_id, &i->BSSID, n->InterfaceID, &n->ip); + } + + if (i->sa_family == AF_INET6 && primary->ss.sktv6 == -1) + { + mStatus err = SetupSocket(m, &primary->ss, mDNStrue, &i->ifinfo.ip, AF_INET6); + if (err == 0) debugf("SetupActiveInterfaces: v6 socket%2d %5s(%lu) %.6a InterfaceID %p %#a", primary->ss.sktv6, i->ifa_name, i->scope_id, &i->BSSID, n->InterfaceID, &n->ip); + else LogMsg("SetupActiveInterfaces: v6 socket%2d %5s(%lu) %.6a InterfaceID %p %#a FAILED", primary->ss.sktv6, i->ifa_name, i->scope_id, &i->BSSID, n->InterfaceID, &n->ip); + } + } + } + return count; + } + +mDNSlocal void MarkAllInterfacesInactive(mDNS *const m) + { + NetworkInterfaceInfoOSX *i; + for (i = m->p->InterfaceList; i; i = i->next) + i->Exists = mDNSfalse; + } + +mDNSlocal void CloseRunLoopSourceSocket(CFRunLoopSourceRef rls, CFSocketRef cfs) + { + // Comments show retain counts (obtained via CFGetRetainCount()) after each call. rls 3 cfs 3 + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); // rls 2 cfs 3 + CFRelease(rls); // rls ? cfs 3 + CFSocketInvalidate(cfs); // rls ? cfs 1 + CFRelease(cfs); // rls ? cfs ? + } + +mDNSlocal void CloseSocketSet(CFSocketSet *ss) + { + // Note: MUST NOT close the underlying native BSD sockets. + // CFSocketInvalidate() will do that for us, in its own good time, which may not necessarily be immediately, + // because it first has to unhook the sockets from its select() call, before it can safely close them. + if (ss->cfsv4) CloseRunLoopSourceSocket(ss->rlsv4, ss->cfsv4); + if (ss->cfsv6) CloseRunLoopSourceSocket(ss->rlsv6, ss->cfsv6); + ss->sktv4 = ss->sktv6 = -1; + ss->cfsv4 = ss->cfsv6 = NULL; + ss->rlsv4 = ss->rlsv6 = NULL; + } + +// returns count of non-link local V4 addresses deregistered +mDNSlocal int ClearInactiveInterfaces(mDNS *const m) + { + // First pass: + // If an interface is going away, then deregister this from the mDNSCore. + // We also have to deregister it if the primary interface that it's using for its InterfaceID is going away. + // We have to do this because mDNSCore will use that InterfaceID when sending packets, and if the memory + // it refers to has gone away we'll crash. + // Don't actually close the sockets or free the memory yet: When the last representative of an interface goes away + // mDNSCore may want to send goodbye packets on that interface. (Not yet implemented, but a good idea anyway.) + NetworkInterfaceInfoOSX *i; + int count = 0; + for (i = m->p->InterfaceList; i; i = i->next) + { + // 1. If this interface is no longer active, or its InterfaceID is changing, deregister it + NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifa_name, i->sa_family); + if (i->ifinfo.InterfaceID) + if (i->Exists == 0 || i->Exists == 2 || i->ifinfo.InterfaceID != (mDNSInterfaceID)primary) + { + LogOperation("ClearInactiveInterfaces: Deregistering %5s(%lu) %.6a InterfaceID %p %#a%s", + i->ifa_name, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, &i->ifinfo.ip, i->ifinfo.InterfaceActive ? " (Primary)" : ""); + mDNS_DeregisterInterface(m, &i->ifinfo); + if (i->ifinfo.ip.type == mDNSAddrType_IPv4 && (i->ifinfo.ip.ip.v4.b[0] != 169 || i->ifinfo.ip.ip.v4.b[1] != 254)) count++; + i->ifinfo.InterfaceID = mDNSNULL; + // NOTE: If n->InterfaceID is set, that means we've called mDNS_RegisterInterface() for this interface, + // so we need to make sure we call mDNS_DeregisterInterface() before disposing it. + // If n->InterfaceID is NOT set, then it's not registered and we should not call mDNS_DeregisterInterface() on it. + } + } + + // Second pass: + // Now that everything that's going to deregister has done so, we can close sockets and free the memory + NetworkInterfaceInfoOSX **p = &m->p->InterfaceList; + while (*p) + { + i = *p; + // 2. Close all our CFSockets. We'll recreate them later as necessary. + // (We may have previously had both v4 and v6, and we may not need both any more.) + CloseSocketSet(&i->ss); + // 3. If no longer active, delete interface from list and free memory + if (!i->Exists && NumCacheRecordsForInterfaceID(m, (mDNSInterfaceID)i) == 0) + { + debugf("ClearInactiveInterfaces: Deleting %#a", &i->ifinfo.ip); + *p = i->next; + if (i->ifa_name) freeL("NetworkInterfaceInfoOSX name", i->ifa_name); + freeL("NetworkInterfaceInfoOSX", i); + } + else + p = &i->next; + } + return count; + } + +mDNSlocal mStatus RegisterSplitDNS(mDNS *m) + { +#ifndef MAC_OS_X_VERSION_10_4 + static int MessageShown = 0; + (void)m; + if (!MessageShown) { MessageShown = 1; LogMsg("Note: Compiled without Apple-specific split DNS support"); } + return mStatus_UnsupportedErr; +#else + int i; + dns_config_t *config = dns_configuration_copy(); + + if (!config) + { + // When running on 10.3 (build 7xxx) and earlier, we don't expect dns_configuration_copy() to succeed + if (mDNSMacOSXSystemBuildNumber(NULL) < 8) return mStatus_UnsupportedErr; + + // On 10.4, calls to dns_configuration_copy() early in the boot process often fail. + // Apparently this is expected behaviour -- "not a bug". + // Accordingly, we suppress syslog messages for the first three minutes after boot. + // If we are still getting failures after three minutes, then we log them. + if ((mDNSu32)(mDNSPlatformRawTime()) < (mDNSu32)(mDNSPlatformOneSecond * 180)) return mStatus_UnknownErr; + + LogMsg("RegisterSplitDNS: Error: dns_configuration_copy returned NULL"); + return mStatus_UnknownErr; + } + mDNS_DeleteDNSServers(m); + + LogOperation("RegisterSplitDNS: Registering %d resolvers", config->n_resolver); + for (i = 0; i < config->n_resolver; i++) + { + int j, n; + domainname d; + dns_resolver_t *r = config->resolver[i]; + if (r->port == MulticastDNSPort.NotAnInteger) continue; // ignore configurations for .local + if (r->search_order == DEFAULT_SEARCH_ORDER || !r->domain || !*r->domain) d.c[0] = 0; // we ignore domain for "default" resolver + else if (!MakeDomainNameFromDNSNameString(&d, r->domain)) { LogMsg("RegisterSplitDNS: bad domain %s", r->domain); continue; } + + // check if this is the lowest-weighted server for the domain + for (j = 0; j < config->n_resolver; j++) + { + dns_resolver_t *p = config->resolver[j]; + if (p->port == MulticastDNSPort.NotAnInteger) continue; + if (p->search_order <= r->search_order) + { + domainname tmp; + if (p->search_order == DEFAULT_SEARCH_ORDER || !p->domain || !*p->domain) tmp.c[0] = '\0'; + else if (!MakeDomainNameFromDNSNameString(&tmp, p->domain)) { LogMsg("RegisterSplitDNS: bad domain %s", p->domain); continue; } + if (SameDomainName(&d, &tmp)) + if (p->search_order < r->search_order || j < i) break; // if equal weights, pick first in list, otherwise pick lower-weight (p) + } + } + if (j < config->n_resolver) // found a lower-weighted resolver for this domain + { debugf("Rejecting DNS server in slot %d domain %##s (slot %d outranks)", i, d.c, j); continue; } + // we're using this resolver - find the first IPv4 address + for (n = 0; n < r->n_nameserver; n++) + { + if (r->nameserver[n]->sa_family == AF_INET) + { + mDNSAddr saddr; + if (SetupAddr(&saddr, r->nameserver[n])) { LogMsg("RegisterSplitDNS: bad IP address"); continue; } + debugf("Adding dns server from slot %d %d.%d.%d.%d for domain %##s", i, saddr.ip.v4.b[0], saddr.ip.v4.b[1], saddr.ip.v4.b[2], saddr.ip.v4.b[3], d.c); + mDNS_AddDNSServer(m, &saddr, &d); + break; // !!!KRS if we ever support round-robin servers, don't break here + } + } + } + dns_configuration_free(config); + return mStatus_NoError; +#endif + } + +mDNSlocal mStatus RegisterNameServers(mDNS *const m, CFDictionaryRef dict) + { + int i, count; + CFArrayRef values; + char buf[256]; + mDNSAddr saddr = { mDNSAddrType_IPv4, { { { 0 } } } }; + CFStringRef s; + + mDNS_DeleteDNSServers(m); // deregister orig list + values = CFDictionaryGetValue(dict, kSCPropNetDNSServerAddresses); + if (!values) return mStatus_NoError; + + count = CFArrayGetCount(values); + for (i = 0; i < count; i++) + { + s = CFArrayGetValueAtIndex(values, i); + if (!s) { LogMsg("ERROR: RegisterNameServers - CFArrayGetValueAtIndex"); break; } + if (!CFStringGetCString(s, buf, 256, kCFStringEncodingUTF8)) + { + LogMsg("ERROR: RegisterNameServers - CFStringGetCString"); + continue; + } + if (!inet_aton(buf, (struct in_addr *)saddr.ip.v4.b)) + { + LogMsg("ERROR: RegisterNameServers - invalid address string %s", buf); + continue; + } + LogOperation("RegisterNameServers: Adding %#a", &saddr); + mDNS_AddDNSServer(m, &saddr, NULL); + } + return mStatus_NoError; + } + +mDNSlocal void FreeARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result) + { + (void)m; // unused + ARListElem *elem = rr->RecordContext; + if (result == mStatus_MemFree) freeL("FreeARElemCallback", elem); + } + +mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + SearchListElem *slElem = question->QuestionContext; + ARListElem *arElem, *ptr, *prev; + AuthRecord *dereg; + char *name; + mStatus err; + + if (AddRecord) + { + arElem = mallocL("FoundDomain - arElem", sizeof(ARListElem)); + if (!arElem) { LogMsg("ERROR: malloc"); return; } + mDNS_SetupResourceRecord(&arElem->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, FreeARElemCallback, arElem); + if (question == &slElem->BrowseQ) name = "_browse._dns-sd._udp.local."; + else if (question == &slElem->DefBrowseQ) name = "_default._browse._dns-sd._udp.local."; + else if (question == &slElem->LegacyBrowseQ) name = "_legacy._browse._dns-sd._udp.local."; + else if (question == &slElem->RegisterQ) name = "_register._dns-sd._udp.local."; + else if (question == &slElem->DefRegisterQ) name = "_default._register._dns-sd._udp.local."; + else { LogMsg("FoundDomain - unknown question"); return; } + + MakeDomainNameFromDNSNameString(&arElem->ar.resrec.name, name); + AssignDomainName(arElem->ar.resrec.rdata->u.name, answer->rdata->u.name); + err = mDNS_Register(m, &arElem->ar); + if (err) + { + LogMsg("ERROR: FoundDomain - mDNS_Register returned %d", err); + freeL("FoundDomain - arElem", arElem); + return; + } + arElem->next = slElem->AuthRecs; + slElem->AuthRecs = arElem; + } + else + { + ptr = slElem->AuthRecs; + prev = NULL; + while (ptr) + { + if (SameDomainName(&ptr->ar.resrec.rdata->u.name, &answer->rdata->u.name)) + { + debugf("Deregistering PTR %s -> %s", ptr->ar.resrec.name.c, ptr->ar.resrec.rdata->u.name.c); + dereg = &ptr->ar; + if (prev) prev->next = ptr->next; + else slElem->AuthRecs = ptr->next; + ptr = ptr->next; + err = mDNS_Deregister(m, dereg); + if (err) LogMsg("ERROR: FoundDomain - mDNS_Deregister returned %d", err); + } + else + { + prev = ptr; + ptr = ptr->next; + } + } + } + } + +mDNSlocal void MarkSearchListElem(domainname *domain) + { + SearchListElem *new, *ptr; + + // if domain is in list, mark as pre-existent (0) + for (ptr = SearchList; ptr; ptr = ptr->next) + if (SameDomainName(&ptr->domain, domain)) + { + if (ptr->flag != 1) ptr->flag = 0; // gracefully handle duplicates - if it is already marked as add, don't bump down to preexistent + break; + } + + // if domain not in list, add to list, mark as add (1) + if (!ptr) + { + new = mallocL("MarkSearchListElem - SearchListElem", sizeof(SearchListElem)); + if (!new) { LogMsg("ERROR: MarkSearchListElem - malloc"); return; } + bzero(new, sizeof(SearchListElem)); + AssignDomainName(new->domain, *domain); + new->flag = 1; // add + new->next = SearchList; + SearchList = new; + } + } + +mDNSlocal mStatus RegisterSearchDomains(mDNS *const m, CFDictionaryRef dict) + { + struct ifaddrs *ifa = NULL; + int i, count; + domainname domain; + char buf[MAX_ESCAPED_DOMAIN_NAME]; + CFStringRef s; + SearchListElem *ptr, *prev, *freeSLPtr; + ARListElem *arList; + mStatus err; + + // step 1: mark each elem for removal (-1), unless we aren't passed a dictionary in which case we mark as preexistent + for (ptr = SearchList; ptr; ptr = ptr->next) ptr->flag = dict ? -1 : 0; + + // get all the domains from "Search Domains" field of sharing prefs + if (dict) + { + CFArrayRef searchdomains = CFDictionaryGetValue(dict, kSCPropNetDNSSearchDomains); + if (searchdomains) + { + count = CFArrayGetCount(searchdomains); + for (i = 0; i < count; i++) + { + s = CFArrayGetValueAtIndex(searchdomains, i); + if (!s) { LogMsg("ERROR: RegisterSearchDomains - CFArrayGetValueAtIndex"); break; } + if (!CFStringGetCString(s, buf, MAX_ESCAPED_DOMAIN_NAME, kCFStringEncodingUTF8)) + { + LogMsg("ERROR: RegisterSearchDomains - CFStringGetCString"); + continue; + } + if (!MakeDomainNameFromDNSNameString(&domain, buf)) + { + LogMsg("ERROR: RegisterSearchDomains - invalid search domain %s", buf); + continue; + } + MarkSearchListElem(&domain); + } + } + CFStringRef dname = CFDictionaryGetValue(dict, kSCPropNetDNSDomainName); + if (dname) + { + if (CFStringGetCString(dname, buf, MAX_ESCAPED_DOMAIN_NAME, kCFStringEncodingUTF8)) + { + if (MakeDomainNameFromDNSNameString(&domain, buf)) MarkSearchListElem(&domain); + else LogMsg("ERROR: RegisterSearchDomains - invalid domain %s", buf); + } + else LogMsg("ERROR: RegisterSearchDomains - CFStringGetCString"); + } + } + + // Construct reverse-map search domains + ifa = myGetIfAddrs(1); + while (ifa) + { + mDNSAddr addr; + if (ifa->ifa_addr->sa_family == AF_INET && !SetupAddr(&addr, ifa->ifa_addr) && !IsPrivateV4Addr(&addr) && !(ifa->ifa_flags & IFF_LOOPBACK) && ifa->ifa_netmask) + { + mDNSAddr netmask; + char buffer[256]; + if (!SetupAddr(&netmask, ifa->ifa_netmask)) + { + sprintf(buffer, "%d.%d.%d.%d.in-addr.arpa.", addr.ip.v4.b[3] & netmask.ip.v4.b[3], + addr.ip.v4.b[2] & netmask.ip.v4.b[2], + addr.ip.v4.b[1] & netmask.ip.v4.b[1], + addr.ip.v4.b[0] & netmask.ip.v4.b[0]); + MakeDomainNameFromDNSNameString(&domain, buffer); + MarkSearchListElem(&domain); + } + } + ifa = ifa->ifa_next; + } + + if (DynDNSRegDomain.c[0]) MarkSearchListElem(&DynDNSRegDomain); // implicitly browse reg domain too (no-op if same as BrowseDomain) + + // delete elems marked for removal, do queries for elems marked add + prev = NULL; + ptr = SearchList; + while (ptr) + { + if (ptr->flag == -1) // remove + { + mDNS_StopQuery(m, &ptr->BrowseQ); + mDNS_StopQuery(m, &ptr->RegisterQ); + mDNS_StopQuery(m, &ptr->DefBrowseQ); + mDNS_StopQuery(m, &ptr->DefRegisterQ); + mDNS_StopQuery(m, &ptr->LegacyBrowseQ); + + // deregister records generated from answers to the query + arList = ptr->AuthRecs; + ptr->AuthRecs = NULL; + while (arList) + { + AuthRecord *dereg = &arList->ar; + arList = arList->next; + debugf("Deregistering PTR %s -> %s", dereg->resrec.name.c, dereg->resrec.rdata->u.name.c); + err = mDNS_Deregister(m, dereg); + if (err) LogMsg("ERROR: RegisterSearchDomains mDNS_Deregister returned %d", err); + } + + // remove elem from list, delete + if (prev) prev->next = ptr->next; + else SearchList = ptr->next; + freeSLPtr = ptr; + ptr = ptr->next; + freeL("RegisterSearchDomains - freeSLPtr", freeSLPtr); + continue; + } + + if (ptr->flag == 1) // add + { + mStatus err1, err2, err3, err4, err5; + err1 = mDNS_GetDomains(m, &ptr->BrowseQ, mDNS_DomainTypeBrowse, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr); + err2 = mDNS_GetDomains(m, &ptr->DefBrowseQ, mDNS_DomainTypeBrowseDefault, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr); + err3 = mDNS_GetDomains(m, &ptr->RegisterQ, mDNS_DomainTypeRegistration, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr); + err4 = mDNS_GetDomains(m, &ptr->DefRegisterQ, mDNS_DomainTypeRegistrationDefault, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr); + err5 = mDNS_GetDomains(m, &ptr->LegacyBrowseQ, mDNS_DomainTypeBrowseLegacy, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr); + if (err1 || err2 || err3 || err4 || err5) + LogMsg("GetDomains for domain %##s returned error(s):\n" + "%d (mDNS_DomainTypeBrowse)\n" + "%d (mDNS_DomainTypeBrowseDefault)\n" + "%d (mDNS_DomainTypeRegistration)\n" + "%d (mDNS_DomainTypeRegistrationDefault)" + "%d (mDNS_DomainTypeBrowseLegacy)\n", + ptr->domain.c, err1, err2, err3, err4, err5); + ptr->flag = 0; + } + + if (ptr->flag) { LogMsg("RegisterSearchDomains - unknown flag %d. Skipping.", ptr->flag); } + + prev = ptr; + ptr = ptr->next; + } + + return mStatus_NoError; + } + +//!!!KRS here is where we will give success/failure notification to the UI +mDNSlocal void SCPrefsDynDNSCallback(mDNS *const m, AuthRecord *const rr, mStatus result) + { + (void)m; // unused + debugf("SCPrefsDynDNSCallback: result %d for registration of name %##s", result, rr->resrec.name.c); + SetDDNSNameStatus(&rr->resrec.name, result); + } + +mDNSlocal void SetSecretForDomain(mDNS *m, const domainname *domain) + { + OSStatus err = 0; + SecKeychainRef SysKeychain = NULL; + SecKeychainItemRef KeychainItem; + char dstring[MAX_ESCAPED_DOMAIN_NAME]; + mDNSu32 secretlen; + void *secret = NULL; + domainname *d, canon; + int i, dlen; + + // canonicalize name by converting to lower case (keychain and some name servers are case sensitive) + ConvertDomainNameToCString(domain, dstring); + dlen = strlen(dstring); + for (i = 0; i < dlen; i++) dstring[i] = tolower(dstring[i]); // canonicalize -> lower case + MakeDomainNameFromDNSNameString(&canon, dstring); + d = &canon; + + err = SecKeychainOpen(SYS_KEYCHAIN_PATH, &SysKeychain); + if (err) { LogMsg("SetSecretForDomain: couldn't open system keychain (error %d)", err); return; } + // find longest-match key ("account") name, excluding last label (e.g. excluding ".com") + while (d->c[0] && *(d->c + d->c[0] + 1)) + { + if (!ConvertDomainNameToCString(d, dstring)) { LogMsg("SetSecretForDomain: bad domain %##s", d->c); return; } + dlen = strlen(dstring); + if (dstring[dlen-1] == '.') { dstring[dlen-1] = '\0'; dlen--; } // chop trailing dot + err = SecKeychainFindGenericPassword(SysKeychain, strlen(DYNDNS_KEYCHAIN_SERVICE), DYNDNS_KEYCHAIN_SERVICE, dlen, dstring, &secretlen, &secret, &KeychainItem); + if (!err) + { + debugf("Setting shared secret for zone %s with key %##s", dstring, d->c); + mDNS_SetSecretForZone(m, d, d, secret, secretlen, mDNStrue); + free(secret); + return; + } + if (err == errSecItemNotFound) d = (domainname *)(d->c + d->c[0] + 1); + else + { + if (err == errSecNoSuchKeychain) debugf("SetSecretForDomain: keychain not found"); + else LogMsg("SetSecretForDomain: SecKeychainFindGenericPassword returned error %d", err); + return; + } + } + } + +mDNSlocal void DynDNSConfigChanged(mDNS *const m) + { + static mDNSBool LegacyNATInitialized = mDNSfalse; + uDNS_GlobalInfo *u = &m->uDNS_info; + CFDictionaryRef dict; + CFStringRef key; + domainname BrowseDomain, RegDomain, fqdn; + + // get fqdn, zone from SCPrefs + GetUserSpecifiedDDNSConfig(&fqdn, &RegDomain, &BrowseDomain); + if (!fqdn.c[0] && !RegDomain.c[0]) ReadDDNSSettingsFromConfFile(m, CONFIG_FILE, &fqdn, &RegDomain); + + if (!SameDomainName(&BrowseDomain, &DynDNSBrowseDomain)) + { + if (DynDNSBrowseDomain.c[0]) SetSCPrefsBrowseDomain(m, &DynDNSBrowseDomain, mDNSfalse); + AssignDomainName(DynDNSBrowseDomain, BrowseDomain); + if (DynDNSBrowseDomain.c[0]) SetSCPrefsBrowseDomain(m, &DynDNSBrowseDomain, mDNStrue); + } + + if (!SameDomainName(&RegDomain, &DynDNSRegDomain)) + { + if (DynDNSRegDomain.c[0]) RemoveDefRegDomain(&DynDNSRegDomain); + AssignDomainName(DynDNSRegDomain, RegDomain); + if (DynDNSRegDomain.c[0]) { SetSecretForDomain(m, &DynDNSRegDomain); AddDefRegDomain(&DynDNSRegDomain); } + } + + if (!SameDomainName(&fqdn, &DynDNSHostname)) + { + if (DynDNSHostname.c[0]) mDNS_RemoveDynDNSHostName(m, &DynDNSHostname); + AssignDomainName(DynDNSHostname, fqdn); + if (DynDNSHostname.c[0]) + { + SetSecretForDomain(m, &fqdn); // no-op if "zone" secret, above, is to be used for hostname + mDNS_AddDynDNSHostName(m, &DynDNSHostname, SCPrefsDynDNSCallback, NULL); + SetDDNSNameStatus(&DynDNSHostname, 1); + } + } + + // get DNS settings + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:DynDNSConfigChanged"), NULL, NULL); + if (!store) return; + + key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetDNS); + if (!key) { LogMsg("ERROR: DNSConfigChanged - SCDynamicStoreKeyCreateNetworkGlobalEntity"); CFRelease(store); return; } + dict = SCDynamicStoreCopyValue(store, key); + CFRelease(key); + + // handle any changes to search domains and DNS server addresses + if (RegisterSplitDNS(m) != mStatus_NoError) + if (dict) RegisterNameServers(m, dict); // fall back to non-split DNS aware configuration on failure + RegisterSearchDomains(m, dict); // note that we register name servers *before* search domains + if (dict) CFRelease(dict); + + // get IPv4 settings + key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,kSCDynamicStoreDomainState, kSCEntNetIPv4); + if (!key) { LogMsg("ERROR: RouterChanged - SCDynamicStoreKeyCreateNetworkGlobalEntity"); CFRelease(store); return; } + dict = SCDynamicStoreCopyValue(store, key); + CFRelease(key); + CFRelease(store); + if (!dict) + { mDNS_SetPrimaryInterfaceInfo(m, NULL, NULL); return; } // lost v4 + + // handle router changes + mDNSAddr r; + char buf[256]; + r.type = mDNSAddrType_IPv4; + r.ip.v4.NotAnInteger = 0; + CFStringRef router = CFDictionaryGetValue(dict, kSCPropNetIPv4Router); + if (router) + { + if (!CFStringGetCString(router, buf, 256, kCFStringEncodingUTF8)) + LogMsg("Could not convert router to CString"); + else inet_aton(buf, (struct in_addr *)&r.ip.v4); + } + + // handle primary interface changes + CFStringRef primary = CFDictionaryGetValue(dict, kSCDynamicStorePropNetPrimaryInterface); + if (primary) + { + struct ifaddrs *ifa = myGetIfAddrs(1); + + if (!CFStringGetCString(primary, buf, 256, kCFStringEncodingUTF8)) + { LogMsg("Could not convert router to CString"); goto error; } + + // find primary interface in list + while (ifa) + { + if (ifa->ifa_addr->sa_family == AF_INET && !strcmp(buf, ifa->ifa_name)) + { + mDNSAddr ip; + SetupAddr(&ip, ifa->ifa_addr); + if (ip.ip.v4.b[0] == 169 && ip.ip.v4.b[1] == 254) + { mDNS_SetPrimaryInterfaceInfo(m, NULL, NULL); break; } // primary IP is link-local + if (ip.ip.v4.NotAnInteger != u->PrimaryIP.ip.v4.NotAnInteger || + r.ip.v4.NotAnInteger != u->Router.ip.v4.NotAnInteger) + { + if (LegacyNATInitialized) { LegacyNATDestroy(); LegacyNATInitialized = mDNSfalse; } + if (IsPrivateV4Addr(&ip)) + { + mStatus err = LegacyNATInit(); + if (err) LogMsg("ERROR: LegacyNATInit"); + else LegacyNATInitialized = mDNStrue; + } + mDNS_SetPrimaryInterfaceInfo(m, &ip, r.ip.v4.NotAnInteger ? &r : NULL); + break; + } + } + ifa = ifa->ifa_next; + } + } + + error: + CFRelease(dict); + } + +mDNSexport void mDNSMacOSXNetworkChanged(mDNS *const m) + { + LogOperation("*** Network Configuration Change ***"); + MarkAllInterfacesInactive(m); + UpdateInterfaceList(m); + int nDeletions = ClearInactiveInterfaces(m); + int nAdditions = SetupActiveInterfaces(m); + if (nDeletions || nAdditions) mDNS_UpdateLLQs(m); + DynDNSConfigChanged(m); + + if (m->MainCallback) + m->MainCallback(m, mStatus_ConfigChanged); + } + +mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context) + { + (void)store; // Parameter not used + (void)changedKeys; // Parameter not used + LogOperation("*** NetworkChanged ***"); + mDNS *const m = (mDNS *const)context; + mDNS_Lock(m); + m->p->NetworkChanged = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2); + if (!m->SuppressSending || + m->SuppressSending - m->p->NetworkChanged < 0) + m->SuppressSending = m->p->NetworkChanged; + mDNS_Unlock(m); + } + +mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m) + { + mStatus err = -1; + SCDynamicStoreContext context = { 0, m, NULL, NULL, NULL }; + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:WatchForNetworkChanges"), NetworkChanged, &context); + CFStringRef key1 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4); + CFStringRef key2 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6); + CFStringRef key3 = SCDynamicStoreKeyCreateComputerName(NULL); + CFStringRef key4 = SCDynamicStoreKeyCreateHostNames(NULL); + CFStringRef key5 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetDNS); + CFStringRef pattern1 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4); + CFStringRef pattern2 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv6); + + CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + CFMutableArrayRef patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + + if (!store) { LogMsg("SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); goto error; } + if (!key1 || !key2 || !key3 || !key4 || !keys || !pattern1 || !pattern2 || !patterns) goto error; + + CFArrayAppendValue(keys, key1); + CFArrayAppendValue(keys, key2); + CFArrayAppendValue(keys, key3); + CFArrayAppendValue(keys, key4); + CFArrayAppendValue(keys, key5); + CFArrayAppendValue(keys, CFSTR("Setup:/Network/DynamicDNS")); + CFArrayAppendValue(patterns, pattern1); + CFArrayAppendValue(patterns, pattern2); + CFArrayAppendValue(patterns, CFSTR("State:/Network/Interface/[^/]+/AirPort")); + if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns)) + { LogMsg("SCDynamicStoreSetNotificationKeys failed: %s", SCErrorString(SCError())); goto error; } + + m->p->StoreRLS = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); + if (!m->p->StoreRLS) { LogMsg("SCDynamicStoreCreateRunLoopSource failed: %s", SCErrorString(SCError())); goto error; } + + CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode); + m->p->Store = store; + err = 0; + goto exit; + +error: + if (store) CFRelease(store); + +exit: + if (key1) CFRelease(key1); + if (key2) CFRelease(key2); + if (key3) CFRelease(key3); + if (key4) CFRelease(key4); + if (key5) CFRelease(key5); + if (pattern1) CFRelease(pattern1); + if (pattern2) CFRelease(pattern2); + if (keys) CFRelease(keys); + if (patterns) CFRelease(patterns); + + return(err); + } + +mDNSlocal void PowerChanged(void *refcon, io_service_t service, natural_t messageType, void *messageArgument) + { + mDNS *const m = (mDNS *const)refcon; + (void)service; // Parameter not used + switch(messageType) + { + case kIOMessageCanSystemPowerOff: debugf("PowerChanged kIOMessageCanSystemPowerOff (no action)"); break; // E0000240 + case kIOMessageSystemWillPowerOff: debugf("PowerChanged kIOMessageSystemWillPowerOff"); mDNSCoreMachineSleep(m, true); break; // E0000250 + case kIOMessageSystemWillNotPowerOff: debugf("PowerChanged kIOMessageSystemWillNotPowerOff (no action)"); break; // E0000260 + case kIOMessageCanSystemSleep: debugf("PowerChanged kIOMessageCanSystemSleep (no action)"); break; // E0000270 + case kIOMessageSystemWillSleep: debugf("PowerChanged kIOMessageSystemWillSleep"); mDNSCoreMachineSleep(m, true); break; // E0000280 + case kIOMessageSystemWillNotSleep: debugf("PowerChanged kIOMessageSystemWillNotSleep (no action)"); break; // E0000290 + case kIOMessageSystemHasPoweredOn: debugf("PowerChanged kIOMessageSystemHasPoweredOn"); + // If still sleeping (didn't get 'WillPowerOn' message for some reason?) wake now + if (m->SleepState) mDNSCoreMachineSleep(m, false); + // Just to be safe, also make sure our interface list is fully up to date, in case we + // haven't yet received the System Configuration Framework "network changed" event that + // we expect to receive some time shortly after the kIOMessageSystemWillPowerOn message + mDNSMacOSXNetworkChanged(m); break; // E0000300 + case kIOMessageSystemWillRestart: debugf("PowerChanged kIOMessageSystemWillRestart (no action)"); break; // E0000310 + case kIOMessageSystemWillPowerOn: debugf("PowerChanged kIOMessageSystemWillPowerOn"); + // Make sure our interface list is cleared to the empty state, then tell mDNSCore to wake + mDNSMacOSXNetworkChanged(m); mDNSCoreMachineSleep(m, false); break; // E0000320 + default: debugf("PowerChanged unknown message %X", messageType); break; + } + IOAllowPowerChange(m->p->PowerConnection, (long)messageArgument); + } + +mDNSlocal mStatus WatchForPowerChanges(mDNS *const m) + { + IONotificationPortRef thePortRef; + m->p->PowerConnection = IORegisterForSystemPower(m, &thePortRef, PowerChanged, &m->p->PowerNotifier); + if (m->p->PowerConnection) + { + m->p->PowerRLS = IONotificationPortGetRunLoopSource(thePortRef); + CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->PowerRLS, kCFRunLoopDefaultMode); + return(mStatus_NoError); + } + return(-1); + } + +CF_EXPORT CFDictionaryRef _CFCopySystemVersionDictionary(void); +CF_EXPORT const CFStringRef _kCFSystemVersionProductNameKey; +CF_EXPORT const CFStringRef _kCFSystemVersionProductVersionKey; +CF_EXPORT const CFStringRef _kCFSystemVersionBuildVersionKey; + +mDNSexport int mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring) + { + int major = 0, minor = 0; + char letter = 0, prodname[256]="Mac OS X", prodvers[256]="", buildver[256]="?"; + CFDictionaryRef vers = _CFCopySystemVersionDictionary(); + if (vers) + { + CFStringRef cfprodname = CFDictionaryGetValue(vers, _kCFSystemVersionProductNameKey); + CFStringRef cfprodvers = CFDictionaryGetValue(vers, _kCFSystemVersionProductVersionKey); + CFStringRef cfbuildver = CFDictionaryGetValue(vers, _kCFSystemVersionBuildVersionKey); + if (cfprodname) CFStringGetCString(cfprodname, prodname, sizeof(prodname), kCFStringEncodingUTF8); + if (cfprodvers) CFStringGetCString(cfprodvers, prodvers, sizeof(prodvers), kCFStringEncodingUTF8); + if (cfbuildver) CFStringGetCString(cfbuildver, buildver, sizeof(buildver), kCFStringEncodingUTF8); + sscanf(buildver, "%d%c%d", &major, &letter, &minor); + CFRelease(vers); + } + if (HINFO_SWstring) mDNS_snprintf(HINFO_SWstring, 256, "%s %s (%s), %s", prodname, prodvers, buildver, mDNSResponderVersionString); + return(major); + } + +// Test to see if we're the first client running on UDP port 5353, by trying to bind to 5353 without using SO_REUSEPORT. +// If we fail, someone else got here first. That's not a big problem; we can share the port for multicast responses -- +// we just need to be aware that we shouldn't expect to successfully receive unicast UDP responses. +mDNSlocal mDNSBool mDNSPlatformInit_CanReceiveUnicast(void) + { + int err = -1; + int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (s < 3) + LogMsg("mDNSPlatformInit_CanReceiveUnicast: socket error %d errno %d (%s)", s, errno, strerror(errno)); + else + { + struct sockaddr_in s5353; + s5353.sin_family = AF_INET; + s5353.sin_port = MulticastDNSPort.NotAnInteger; + s5353.sin_addr.s_addr = 0; + err = bind(s, (struct sockaddr *)&s5353, sizeof(s5353)); + close(s); + } + + if (err) LogMsg("No unicast UDP responses"); + else debugf("Unicast UDP responses okay"); + return(err == 0); + } + +// Callback for the _legacy._browse queries - add answer to list of domains to search for empty-string browses +mDNSlocal void FoundDefBrowseDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + DNameListElem *ptr, *prev, *new; + (void)m; // unused; + (void)question; // unused + + if (AddRecord) + { + new = mallocL("FoundDefBrowseDomain", sizeof(DNameListElem)); + if (!new) { LogMsg("ERROR: malloc"); return; } + AssignDomainName(new->name, answer->rdata->u.name); + new->next = DefBrowseList; + DefBrowseList = new; + DefaultBrowseDomainChanged(&new->name, mDNStrue); + udsserver_default_browse_domain_changed(&new->name, mDNStrue); + return; + } + else + { + ptr = DefBrowseList; + prev = NULL; + while (ptr) + { + if (SameDomainName(&ptr->name, &answer->rdata->u.name)) + { + DefaultBrowseDomainChanged(&ptr->name, mDNSfalse); + udsserver_default_browse_domain_changed(&ptr->name, mDNSfalse); + if (prev) prev->next = ptr->next; + else DefBrowseList = ptr->next; + freeL("FoundDefBrowseDomain", ptr); + return; + } + prev = ptr; + ptr = ptr->next; + } + LogMsg("FoundDefBrowseDomain: Got remove event for domain %s not in list", answer->rdata->u.name.c); + } + } + +// Add or remove a user-specified domain to the list of empty-string browse domains +// Also register a non-legacy _browse PTR record so that the domain appears in enumeration lists +mDNSlocal void SetSCPrefsBrowseDomain(mDNS *m, const domainname *d, mDNSBool add) + { + AuthRecord rec; + + // Create dummy record pointing to the domain to be added/removed + mDNS_SetupResourceRecord(&rec, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, mDNSNULL, mDNSNULL); + AssignDomainName(rec.resrec.rdata->u.name, *d); + + // add/remove the "_legacy" entry + MakeDomainNameFromDNSNameString(&rec.resrec.name, "_legacy._browse._dns-sd._udp.local."); + FoundDefBrowseDomain(m, &LegacyBrowseDomainQ, &rec.resrec, add); + + if (add) + { + // allocate/register a non-legacy _browse PTR record + ARListElem *ptr = mallocL("ARListElem", sizeof(*ptr)); + mDNS_SetupResourceRecord(&ptr->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, FreeARElemCallback, ptr); + MakeDomainNameFromDNSNameString(&ptr->ar.resrec.name, "_browse._dns-sd._udp.local."); + AssignDomainName(ptr->ar.resrec.rdata->u.name, *d); + mStatus err = mDNS_Register(m, &ptr->ar); + if (err) + { + LogMsg("SetSCPrefsBrowseDomain: mDNS_Register returned error %d", err); + freeL("ARListElem", ptr); + } + else + { + ptr->next = SCPrefBrowseDomains; + SCPrefBrowseDomains = ptr; + } + } + else + { + ARListElem **remove = &SCPrefBrowseDomains; + while (*remove && !SameDomainName(&(*remove)->ar.resrec.rdata->u.name, d)) remove = &(*remove)->next; + if (!*remove) { LogMsg("SetSCPrefsBrowseDomain (remove) - domain %##s not found!", d->c); return; } + mDNS_Deregister(m, &(*remove)->ar); + *remove = (*remove)->next; + } + } + + +// Construction of Default Browse domain list (i.e. when clients pass NULL) is as follows: +// 1) query for _browse._dns-sd._udp.local on LocalOnly interface +// (.local manually generated via explicit callback) +// 2) for each search domain (from prefs pane), query for _browse._dns-sd._udp.. +// 3) for each result from (2), register LocalOnly PTR record_browse._dns-sd._udp.local. -> +// 4) result above should generate a callback from question in (1). result added to global list +// 5) global list delivered to client via GetSearchDomainList() +// 6) client calls to enumerate domains now go over LocalOnly interface +// (!!!KRS may add outgoing interface in addition) + +mDNSlocal mStatus InitDNSConfig(mDNS *const m) + { + mStatus err; + + // start query for domains to be used in default (empty string domain) browses + err = mDNS_GetDomains(m, &LegacyBrowseDomainQ, mDNS_DomainTypeBrowseLegacy, NULL, mDNSInterface_LocalOnly, FoundDefBrowseDomain, NULL); + + // provide .local automatically + SetSCPrefsBrowseDomain(m, &localdomain, mDNStrue); + return mStatus_NoError; + } + +mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) + { + mStatus err; + + m->hostlabel.c[0] = 0; + + char *HINFO_HWstring = "Macintosh"; + char HINFO_HWstring_buffer[256]; + int get_model[2] = { CTL_HW, HW_MODEL }; + size_t len_model = sizeof(HINFO_HWstring_buffer); + if (sysctl(get_model, 2, HINFO_HWstring_buffer, &len_model, NULL, 0) == 0) + HINFO_HWstring = HINFO_HWstring_buffer; + + char HINFO_SWstring[256] = ""; + if (mDNSMacOSXSystemBuildNumber(HINFO_SWstring) < 7) m->KnownBugs |= mDNS_KnownBug_PhantomInterfaces; + if (mDNSPlatformInit_CanReceiveUnicast()) m->CanReceiveUnicastOn5353 = mDNStrue; + + mDNSu32 hlen = mDNSPlatformStrLen(HINFO_HWstring); + mDNSu32 slen = mDNSPlatformStrLen(HINFO_SWstring); + if (hlen + slen < 254) + { + m->HIHardware.c[0] = hlen; + m->HISoftware.c[0] = slen; + mDNSPlatformMemCopy(HINFO_HWstring, &m->HIHardware.c[1], hlen); + mDNSPlatformMemCopy(HINFO_SWstring, &m->HISoftware.c[1], slen); + } + + m->p->unicastsockets.m = m; + m->p->unicastsockets.info = NULL; + m->p->unicastsockets.sktv4 = m->p->unicastsockets.sktv6 = -1; + m->p->unicastsockets.cfsv4 = m->p->unicastsockets.cfsv6 = NULL; + m->p->unicastsockets.rlsv4 = m->p->unicastsockets.rlsv6 = NULL; + + err = SetupSocket(m, &m->p->unicastsockets, mDNSfalse, &zeroAddr, AF_INET); + err = SetupSocket(m, &m->p->unicastsockets, mDNSfalse, &zeroAddr, AF_INET6); + + struct sockaddr_in s4; + struct sockaddr_in6 s6; + int n4 = sizeof(s4); + int n6 = sizeof(s6); + if (getsockname(m->p->unicastsockets.sktv4, (struct sockaddr *)&s4, &n4) < 0) LogMsg("getsockname v4 error %d (%s)", errno, strerror(errno)); + else m->UnicastPort4.NotAnInteger = s4.sin_port; + if (getsockname(m->p->unicastsockets.sktv6, (struct sockaddr *)&s6, &n6) < 0) LogMsg("getsockname v6 error %d (%s)", errno, strerror(errno)); + else m->UnicastPort6.NotAnInteger = s6.sin6_port; + + m->p->InterfaceList = mDNSNULL; + m->p->userhostlabel.c[0] = 0; + m->p->usernicelabel.c[0] = 0; + m->p->NotifyUser = 0; + UpdateInterfaceList(m); + SetupActiveInterfaces(m); + + err = WatchForNetworkChanges(m); + if (err) return(err); + + err = WatchForPowerChanges(m); + if (err) return err; + + DynDNSRegDomain.c[0] = '\0'; + DynDNSBrowseDomain.c[0] = '\0'; + DynDNSConfigChanged(m); // Get initial DNS configuration + + InitDNSConfig(m); + return(err); + } + +mDNSexport mStatus mDNSPlatformInit(mDNS *const m) + { + mStatus result = mDNSPlatformInit_setup(m); + + // We don't do asynchronous initialization on OS X, so by the time we get here the setup will already + // have succeeded or failed -- so if it succeeded, we should just call mDNSCoreInitComplete() immediately + if (result == mStatus_NoError) mDNSCoreInitComplete(m, mStatus_NoError); + return(result); + } + +mDNSexport void mDNSPlatformClose(mDNS *const m) + { + if (m->p->PowerConnection) + { + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->PowerRLS, kCFRunLoopDefaultMode); + CFRunLoopSourceInvalidate(m->p->PowerRLS); + CFRelease(m->p->PowerRLS); + IODeregisterForSystemPower(&m->p->PowerNotifier); + m->p->PowerConnection = 0; + m->p->PowerNotifier = 0; + m->p->PowerRLS = NULL; + } + + if (m->p->Store) + { + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode); + CFRunLoopSourceInvalidate(m->p->StoreRLS); + CFRelease(m->p->StoreRLS); + CFRelease(m->p->Store); + m->p->Store = NULL; + m->p->StoreRLS = NULL; + } + + MarkAllInterfacesInactive(m); + ClearInactiveInterfaces(m); + CloseSocketSet(&m->p->unicastsockets); + } + +mDNSexport mDNSu32 mDNSPlatformRandomSeed(void) + { + return(mach_absolute_time()); + } + +mDNSexport mDNSs32 mDNSPlatformOneSecond = 1000; + +mDNSexport mStatus mDNSPlatformTimeInit(void) + { + // Notes: Typical values for mach_timebase_info: + // tbi.numer = 1000 million + // tbi.denom = 33 million + // These are set such that (mach_absolute_time() * numer/denom) gives us nanoseconds; + // numer / denom = nanoseconds per hardware clock tick (e.g. 30); + // denom / numer = hardware clock ticks per nanosecond (e.g. 0.033) + // (denom*1000000) / numer = hardware clock ticks per millisecond (e.g. 33333) + // So: mach_absolute_time() / ((denom*1000000)/numer) = milliseconds + // + // Arithmetic notes: + // tbi.denom is at least 1, and not more than 2^32-1. + // Therefore (tbi.denom * 1000000) is at least one million, but cannot overflow a uint64_t. + // tbi.denom is at least 1, and not more than 2^32-1. + // Therefore clockdivisor should end up being a number roughly in the range 10^3 - 10^9. + // If clockdivisor is less than 10^3 then that means that the native clock frequency is less than 1MHz, + // which is unlikely on any current or future Macintosh. + // If clockdivisor is greater than 10^9 then that means the native clock frequency is greater than 1000GHz. + // When we ship Macs with clock frequencies above 1000GHz, we may have to update this code. + struct mach_timebase_info tbi; + kern_return_t result = mach_timebase_info(&tbi); + if (result == KERN_SUCCESS) clockdivisor = ((uint64_t)tbi.denom * 1000000) / tbi.numer; + return(result); + } + +mDNSexport mDNSs32 mDNSPlatformRawTime(void) + { + if (clockdivisor == 0) { LogMsg("mDNSPlatformRawTime called before mDNSPlatformTimeInit"); return(0); } + + static uint64_t last_mach_absolute_time = 0; + uint64_t this_mach_absolute_time = mach_absolute_time(); + if ((int64_t)this_mach_absolute_time - (int64_t)last_mach_absolute_time < 0) + { + LogMsg("mDNSPlatformRawTime: last_mach_absolute_time %08X%08X", last_mach_absolute_time); + LogMsg("mDNSPlatformRawTime: this_mach_absolute_time %08X%08X", this_mach_absolute_time); + // Update last_mach_absolute_time *before* calling NotifyOfElusiveBug() + last_mach_absolute_time = this_mach_absolute_time; + NotifyOfElusiveBug("mach_absolute_time went backwards!", 3438376, ""); + } + last_mach_absolute_time = this_mach_absolute_time; + + return((mDNSs32)(this_mach_absolute_time / clockdivisor)); + } + +mDNSexport mDNSs32 mDNSPlatformUTC(void) + { + return time(NULL); + } + +// Locking is a no-op here, because we're single-threaded with a CFRunLoop, so we can never interrupt ourselves +mDNSexport void mDNSPlatformLock (const mDNS *const m) { (void)m; } +mDNSexport void mDNSPlatformUnlock (const mDNS *const m) { (void)m; } +mDNSexport void mDNSPlatformStrCopy(const void *src, void *dst) { strcpy((char *)dst, (char *)src); } +mDNSexport mDNSu32 mDNSPlatformStrLen (const void *src) { return(strlen((char*)src)); } +mDNSexport void mDNSPlatformMemCopy(const void *src, void *dst, mDNSu32 len) { memcpy(dst, src, len); } +mDNSexport mDNSBool mDNSPlatformMemSame(const void *src, const void *dst, mDNSu32 len) { return(memcmp(dst, src, len) == 0); } +mDNSexport void mDNSPlatformMemZero( void *dst, mDNSu32 len) { bzero(dst, len); } +mDNSexport void * mDNSPlatformMemAllocate(mDNSu32 len) { return(mallocL("mDNSPlatformMemAllocate", len)); } +mDNSexport void mDNSPlatformMemFree (void *mem) { freeL("mDNSPlatformMemFree", mem); } diff --git a/mDNSMacOSX/mDNSMacOSX.h b/mDNSMacOSX/mDNSMacOSX.h index ba07125..2a81939 100644 --- a/mDNSMacOSX/mDNSMacOSX.h +++ b/mDNSMacOSX/mDNSMacOSX.h @@ -3,8 +3,6 @@ * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -25,6 +23,43 @@ Change History (most recent first): $Log: mDNSMacOSX.h,v $ +Revision 1.48 2004/12/07 01:31:31 cheshire +mDNSMacOSXSystemBuildNumber() returns int, not mDNSBool + +Revision 1.47 2004/11/30 03:24:03 cheshire + Defer processing network configuration changes until configuration has stabilized + +Revision 1.46 2004/11/03 03:45:16 cheshire + mDNSResponder does not inform user of Computer Name collisions + +Revision 1.45 2004/10/28 00:53:57 cheshire +Export mDNSMacOSXNetworkChanged() so it's callable from outside this mDNSMacOSX.c; +Add LogOperation() call to record when we get network change events + +Revision 1.44 2004/10/23 01:16:01 cheshire + uDNS operations not always reliable on multi-homed hosts + +Revision 1.43 2004/10/15 23:00:18 ksekar + Need to update LLQs on location changes + +Revision 1.42 2004/10/04 05:56:04 cheshire + mDNSResponder doesn't respond to certain AirPort changes + +Revision 1.41 2004/09/30 00:24:59 ksekar + Dynamically update default registration domains on config change + +Revision 1.40 2004/09/17 01:08:52 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.39 2004/08/18 17:35:41 ksekar +: Feature #9586: Need support for Legacy NAT gateways + +Revision 1.38 2004/07/13 21:24:25 rpantos +Fix for . + Revision 1.37 2004/06/04 08:58:30 ksekar : Keychain integration for secure dynamic update @@ -36,7 +71,7 @@ Tidy up all checkin comments to use consistent "" format Revision 1.34 2004/05/12 22:03:09 ksekar Made GetSearchDomainList a true platform-layer call (declaration moved -from mDNSMacOSX.h to mDNSClientAPI.h), impelemted to return "local" +from mDNSMacOSX.h to mDNSEmbeddedAPI.h), impelemted to return "local" only on non-OSX platforms. Changed call to return a copy of the list to avoid shared memory issues. Added a routine to free the list. @@ -116,10 +151,10 @@ Revision 1.11 2003/07/02 21:19:51 cheshire Update copyright notices, etc., in source code comments Revision 1.10 2003/06/25 23:42:19 ksekar -: Feature: New Rendezvous APIs (#7875) +: Feature: New DNS-SD APIs (#7875) Reviewed by: Stuart Cheshire Added files necessary to implement Unix domain sockets based enhanced -Rendezvous APIs, and integrated with existing Mach-port based daemon. +DNS-SD APIs, and integrated with existing Mach-port based daemon. Revision 1.9 2003/06/10 01:14:11 cheshire New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call @@ -167,7 +202,7 @@ Defines mDNS_PlatformSupport_struct for OS X #include #include #include -#include "mDNSClientAPI.h" // for domain name structure +#include "mDNSEmbeddedAPI.h" // for domain name structure typedef struct NetworkInterfaceInfoOSX_struct NetworkInterfaceInfoOSX; @@ -192,6 +227,7 @@ struct NetworkInterfaceInfoOSX_struct // 2 = exists, but McastTxRx state changed char *ifa_name; // Memory for this is allocated using malloc mDNSu32 scope_id; // interface index / IPv6 scope ID + mDNSEthAddr BSSID; // BSSID of 802.11 base station, if applicable u_short sa_family; mDNSBool Multicast; CFSocketSet ss; @@ -202,6 +238,9 @@ struct mDNS_PlatformSupport_struct NetworkInterfaceInfoOSX *InterfaceList; CFSocketSet unicastsockets; domainlabel userhostlabel; + domainlabel usernicelabel; + mDNSs32 NotifyUser; + mDNSs32 NetworkChanged; SCDynamicStoreRef Store; CFRunLoopSourceRef StoreRLS; io_connect_t PowerConnection; @@ -209,10 +248,19 @@ struct mDNS_PlatformSupport_struct CFRunLoopSourceRef PowerRLS; }; -extern mDNSBool mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring); +extern void mDNSMacOSXNetworkChanged(mDNS *const m); +extern int mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring); extern const char mDNSResponderVersionString[]; +// Legacy NAT Traversal Support Setup/Teardown +extern int LegacyNATDestroy(void); +extern int LegacyNATInit(void); + +// Allow platform layer to tell daemon when default registration/browse domains +extern void DefaultRegDomainChanged(const domainname *d, mDNSBool add); +extern void DefaultBrowseDomainChanged(const domainname *d, mDNSBool add); + #ifdef __cplusplus } #endif diff --git a/mDNSMacOSX/mDNSMacOSXPuma.c b/mDNSMacOSX/mDNSMacOSXPuma.c new file mode 100644 index 0000000..7764e2a --- /dev/null +++ b/mDNSMacOSX/mDNSMacOSXPuma.c @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + * + * This file is not normally used. + * It can be conditionally compiled in by defining RUN_ON_PUMA_WITHOUT_IFADDRS + * in mDNSMacOSX.c. It is included mainly as sample code for people building + * for other platforms that (like Puma) lack the getifaddrs() call. + * NOTE: YOU CANNOT use this code to build an mDNSResponder daemon for Puma + * that works just like the Jaguar one, because Puma lacks other necessary + * functionality (like the LibInfo support to receive MIG messages from clients). + + Change History (most recent first): + +$Log: mDNSMacOSXPuma.c,v $ +Revision 1.5 2004/09/20 23:52:02 cheshire +CFSocket{Puma}.c renamed to mDNSMacOSX{Puma}.c + +Revision 1.4 2003/08/12 19:56:25 cheshire +Update to APSL 2.0 + +Revision 1.3 2003/07/02 21:19:51 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.2 2002/09/21 20:44:51 zarzycki +Added APSL info + +Revision 1.1 2002/09/17 01:36:23 cheshire +Move Puma support to mDNSMacOSXPuma.c + + */ + +#include +#include +#define ifaddrs ifa_info +#ifndef ifa_broadaddr +#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */ +#endif +#include + +/* Our own header for the programs that need interface configuration info. + Include this file, instead of "unp.h". */ + +#define IFA_NAME 16 /* same as IFNAMSIZ in */ +#define IFA_HADDR 8 /* allow for 64-bit EUI-64 in future */ + +struct ifa_info { + char ifa_name[IFA_NAME]; /* interface name, null terminated */ + u_char ifa_haddr[IFA_HADDR]; /* hardware address */ + u_short ifa_hlen; /* #bytes in hardware address: 0, 6, 8 */ + short ifa_flags; /* IFF_xxx constants from */ + short ifa_myflags; /* our own IFI_xxx flags */ + struct sockaddr *ifa_addr; /* primary address */ + struct sockaddr *ifa_brdaddr;/* broadcast address */ + struct sockaddr *ifa_dstaddr;/* destination address */ + struct ifa_info *ifa_next; /* next of these structures */ +}; + +#define IFI_ALIAS 1 /* ifa_addr is an alias */ + + /* function prototypes */ +struct ifa_info *get_ifa_info(int, int); +struct ifa_info *Get_ifa_info(int, int); +void free_ifa_info(struct ifa_info *); + +#define HAVE_SOCKADDR_SA_LEN 1 + +struct ifa_info * +get_ifa_info(int family, int doaliases) +{ + struct ifa_info *ifi, *ifihead, **ifipnext; + int sockfd, len, lastlen, flags, myflags; + char *ptr, *buf, lastname[IFNAMSIZ], *cptr; + struct ifconf ifc; + struct ifreq *ifr, ifrcopy; + struct sockaddr_in *sinptr; + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + + lastlen = 0; + len = 100 * sizeof(struct ifreq); /* initial buffer size guess */ + for ( ; ; ) { + buf = (char *) malloc(len); + ifc.ifc_len = len; + ifc.ifc_buf = buf; + if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) { + if (errno != EINVAL || lastlen != 0) + debugf("ioctl error"); + } else { + if (ifc.ifc_len == lastlen) + break; /* success, len has not changed */ + lastlen = ifc.ifc_len; + } + len += 10 * sizeof(struct ifreq); /* increment */ + free(buf); + } + ifihead = NULL; + ifipnext = &ifihead; + lastname[0] = 0; +/* end get_ifa_info1 */ + +/* include get_ifa_info2 */ + for (ptr = buf; ptr < buf + ifc.ifc_len; ) { + ifr = (struct ifreq *) ptr; + +#ifdef HAVE_SOCKADDR_SA_LEN + len = MAX(sizeof(struct sockaddr), ifr->ifr_addr.sa_len); +#else + switch (ifr->ifr_addr.sa_family) { +#ifdef IPV6 + case AF_INET6: + len = sizeof(struct sockaddr_in6); + break; +#endif + case AF_INET: + default: + len = sizeof(struct sockaddr); + break; + } +#endif /* HAVE_SOCKADDR_SA_LEN */ + ptr += sizeof(ifr->ifr_name) + len; /* for next one in buffer */ + + if (ifr->ifr_addr.sa_family != family) + continue; /* ignore if not desired address family */ + + myflags = 0; + if ( (cptr = strchr(ifr->ifr_name, ':')) != NULL) + *cptr = 0; /* replace colon will null */ + if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) { + if (doaliases == 0) + continue; /* already processed this interface */ + myflags = IFI_ALIAS; + } + memcpy(lastname, ifr->ifr_name, IFNAMSIZ); + + ifrcopy = *ifr; + ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy); + flags = ifrcopy.ifr_flags; + if ((flags & IFF_UP) == 0) + continue; /* ignore if interface not up */ + + ifi = (struct ifa_info *) calloc(1, sizeof(struct ifa_info)); + *ifipnext = ifi; /* prev points to this new one */ + ifipnext = &ifi->ifa_next; /* pointer to next one goes here */ + + ifi->ifa_flags = flags; /* IFF_xxx values */ + ifi->ifa_myflags = myflags; /* IFI_xxx values */ + memcpy(ifi->ifa_name, ifr->ifr_name, IFA_NAME); + ifi->ifa_name[IFA_NAME-1] = '\0'; +/* end get_ifa_info2 */ +/* include get_ifa_info3 */ + switch (ifr->ifr_addr.sa_family) { + case AF_INET: + sinptr = (struct sockaddr_in *) &ifr->ifr_addr; + if (ifi->ifa_addr == NULL) { + ifi->ifa_addr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in)); + memcpy(ifi->ifa_addr, sinptr, sizeof(struct sockaddr_in)); + +#ifdef SIOCGIFBRDADDR + if (flags & IFF_BROADCAST) { + ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy); + sinptr = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr; + ifi->ifa_brdaddr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in)); + memcpy(ifi->ifa_brdaddr, sinptr, sizeof(struct sockaddr_in)); + } +#endif + +#ifdef SIOCGIFDSTADDR + if (flags & IFF_POINTOPOINT) { + ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy); + sinptr = (struct sockaddr_in *) &ifrcopy.ifr_dstaddr; + ifi->ifa_dstaddr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in)); + memcpy(ifi->ifa_dstaddr, sinptr, sizeof(struct sockaddr_in)); + } +#endif + } + break; + + default: + break; + } + } + free(buf); + return(ifihead); /* pointer to first structure in linked list */ +} +/* end get_ifa_info3 */ + +/* include free_ifa_info */ +mDNSlocal void freeifaddrs(struct ifa_info *ifihead) +{ + struct ifa_info *ifi, *ifinext; + + for (ifi = ifihead; ifi != NULL; ifi = ifinext) { + if (ifi->ifa_addr != NULL) + free(ifi->ifa_addr); + if (ifi->ifa_brdaddr != NULL) + free(ifi->ifa_brdaddr); + if (ifi->ifa_dstaddr != NULL) + free(ifi->ifa_dstaddr); + ifinext = ifi->ifa_next; /* can't fetch ifa_next after free() */ + free(ifi); /* the ifa_info{} itself */ + } +} +/* end free_ifa_info */ + +struct ifa_info * +Get_ifa_info(int family, int doaliases) +{ + struct ifa_info *ifi; + + if ( (ifi = get_ifa_info(family, doaliases)) == NULL) + debugf("get_ifa_info error"); + return(ifi); +} + +mDNSlocal int getifaddrs(struct ifa_info **ifalist) + { + *ifalist = get_ifa_info(PF_INET, false); + if( ifalist == nil ) + return -1; + else + return(0); + } diff --git a/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj b/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj index 1a2786a..8ba74f8 100644 --- a/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj +++ b/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj @@ -11,15 +11,9 @@ path = mDNSMacOSX.h; refType = 4; }; - 004EFB9604CC78130CCA2C71 = { - fileEncoding = 4; - isa = PBXFileReference; - name = dnssd_clientstub.c; - path = ../mDNSShared/dnssd_clientstub.c; - refType = 2; - }; 00AD62A3032D799A0CCA2C71 = { buildPhases = ( + FFF7174A07614A8600E10551, 00AD62A4032D799A0CCA2C71, 00AD62AC032D799A0CCA2C71, 00AD62B3032D799A0CCA2C71, @@ -28,12 +22,13 @@ buildSettings = { FRAMEWORK_SEARCH_PATHS = ""; GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_VERSION = 3.3; HEADER_SEARCH_PATHS = "\"$(APPLE_INTERNAL_DEVELOPER_DIR)/Headers\""; LIBRARY_SEARCH_PATHS = ""; MACOSX_DEPLOYMENT_TARGET = 10.2; OPTIMIZATION_CFLAGS = "-O0"; - OTHER_CFLAGS = "-no-cpp-precomp -D__MACOSX__ -DMDNS_DEBUGMSGS=1 -DmDNSResponderVersion=$(MVERS)"; - OTHER_LDFLAGS = ""; + OTHER_CFLAGS = "-no-cpp-precomp -mdynamic-no-pic -D__MACOSX__ -DmDNSResponderVersion=$(MVERS) -D_LEGACY_NAT_TRAVERSAL_ -DMDNS_DEBUGMSGS=1"; + OTHER_LDFLAGS = "-weak-ldnsinfo"; OTHER_REZFLAGS = ""; PRODUCT_NAME = mDNSResponder.debug; REZ_EXECUTABLE = YES; @@ -72,14 +67,16 @@ 00AD62AD032D799A0CCA2C71, 00AD62AE032D799A0CCA2C71, 00AD62AF032D799A0CCA2C71, - 00AD62B0032D799A0CCA2C71, - F5E11B5E04A28126019798ED, - F525E72B04AA167A01F1CF4D, - DBAAFE2B057E8F4D0085CAD0, - DBAAFE2E057E8F660085CAD0, - 7F18A9FA0587CEF6001880B3, 7F18A9FB0587CEF6001880B3, + 7F18A9FA0587CEF6001880B3, 7F461DB7062DBF2900672BF3, + DBAAFE2E057E8F660085CAD0, + DBAAFE2B057E8F4D0085CAD0, + F525E72B04AA167A01F1CF4D, + F5E11B5E04A28126019798ED, + FFCB6D75075D595E00B8AF62, + 00AD62B0032D799A0CCA2C71, + 7FC8F9D606D14E66007E879D, 00AD62B1032D799A0CCA2C71, ); isa = PBXSourcesBuildPhase; @@ -174,9 +171,11 @@ SECTORDER_FLAGS = ""; }; dependencies = ( + FF25795106C9AB1D00376F7B, 00AD62BC032D7A160CCA2C71, 00AD62BD032D7A1B0CCA2C71, 00AD62BE032D7A1D0CCA2C71, + FF16238F07023BD2001AB7D7, FFD41DDB0664169900F0C438, FFD41DDC0664169B00F0C438, ); @@ -249,8 +248,10 @@ 08FB779FFE84155DC02AAC07, 00AD62A3032D799A0CCA2C71, 6575FC1C022EB76000000109, + FF1C919207021C84001048AB, DB2CC4530662DD6800335AB3, DB2CC4660662DF5C00335AB3, + FF25792906C9A70800376F7B, ); }; 08FB7794FE84155DC02AAC07 = { @@ -268,6 +269,7 @@ }; 08FB7795FE84155DC02AAC07 = { children = ( + 7FC8F9D406D14E66007E879D, 7F461DB5062DBF2900672BF3, F525E72804AA167501F1CF4D, F5E11B5A04A28126019798ED, @@ -280,10 +282,16 @@ DBAAFE29057E8F4D0085CAD0, 000753D303367C1C0CCA2C71, DBAAFE2C057E8F660085CAD0, + FFCB6D73075D539900B8AF62, FF0E0B5D065ADC7600FE4D9C, + FF1C919D07021D77001048AB, FF485D5105632E0000130380, + FFF4F63A06CFE4DD00459EFD, 7F18A9F60587CEF6001880B3, 7F18A9F70587CEF6001880B3, + FF25794606C9A8BF00376F7B, + FF25794C06C9A9D500376F7B, + FF25794F06C9AA8B00376F7B, ); isa = PBXGroup; name = "mDNS Server Sources"; @@ -304,22 +312,23 @@ }; 08FB779FFE84155DC02AAC07 = { buildPhases = ( + FF37BE9207614059003C0420, 08FB77A0FE84155DC02AAC07, 08FB77A1FE84155DC02AAC07, 08FB77A3FE84155DC02AAC07, 08FB77A5FE84155DC02AAC07, FF5A0AE705632EA600743C27, - FF0E0B5C065ADC3800FE4D9C, ); buildSettings = { FRAMEWORK_SEARCH_PATHS = ""; GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_VERSION = 3.3; HEADER_SEARCH_PATHS = "\"$(APPLE_INTERNAL_DEVELOPER_DIR)/Headers\""; INSTALL_PATH = /usr/sbin; LIBRARY_SEARCH_PATHS = ""; MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = "-no-cpp-precomp -D__MACOSX__ -DmDNSResponderVersion=$(MVERS)"; - OTHER_LDFLAGS = ""; + OTHER_CFLAGS = "-no-cpp-precomp -mdynamic-no-pic -D__MACOSX__ -DmDNSResponderVersion=$(MVERS) -D_LEGACY_NAT_TRAVERSAL_"; + OTHER_LDFLAGS = "-weak-ldnsinfo"; OTHER_REZFLAGS = ""; PRODUCT_NAME = mDNSResponder; REZ_EXECUTABLE = YES; @@ -350,14 +359,16 @@ 6575FC0D022EB18700000109, 6575FC0E022EB18700000109, 6575FBEA022EAF5A00000109, - 6575FBED022EAF7200000109, - F5E11B5C04A28126019798ED, - F525E72904AA167501F1CF4D, - DBAAFE2A057E8F4D0085CAD0, - DBAAFE2D057E8F660085CAD0, - 7F18A9F80587CEF6001880B3, 7F18A9F90587CEF6001880B3, + 7F18A9F80587CEF6001880B3, 7F461DB6062DBF2900672BF3, + DBAAFE2D057E8F660085CAD0, + DBAAFE2A057E8F4D0085CAD0, + F525E72904AA167501F1CF4D, + F5E11B5C04A28126019798ED, + FFCB6D74075D539900B8AF62, + 6575FBED022EAF7200000109, + 7FC8F9D506D14E66007E879D, 6575FBEE022EAF7200000109, ); isa = PBXSourcesBuildPhase; @@ -420,6 +431,8 @@ 00AD62B8032D799A0CCA2C71, DB2CC4670662DF5C00335AB3, FFD41DDA0664157900F0C438, + FF25794406C9A70800376F7B, + FF1C919B07021C84001048AB, ); isa = PBXGroup; name = Products; @@ -438,8 +451,8 @@ 654BE64F02B63B93000001D1 = { fileEncoding = 4; isa = PBXFileReference; - name = mDNSClientAPI.h; - path = ../mDNSCore/mDNSClientAPI.h; + name = mDNSEmbeddedAPI.h; + path = ../mDNSCore/mDNSEmbeddedAPI.h; refType = 4; }; 654BE65002B63B93000001D1 = { @@ -481,7 +494,7 @@ fileEncoding = 4; indentWidth = 4; isa = PBXFileReference; - path = CFSocket.c; + path = mDNSMacOSX.c; refType = 4; tabWidth = 4; usesTabs = 1; @@ -596,12 +609,14 @@ 6575FC19022EB76000000109, 6575FC1A022EB76000000109, 6575FC1B022EB76000000109, + FFF4F63C06CFE53300459EFD, ); buildSettings = { GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_VERSION = 3.3; INSTALL_PATH = /usr/bin; MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = "-no-cpp-precomp -D__MACOSX__ -DmDNSResponderVersion=$(MVERS)"; + OTHER_CFLAGS = "-no-cpp-precomp -mdynamic-no-pic -D__MACOSX__ -DmDNSResponderVersion=$(MVERS)"; OTHER_LDFLAGS = ""; OTHER_REZFLAGS = ""; PRODUCT_NAME = mDNS; @@ -613,9 +628,9 @@ dependencies = ( ); isa = PBXToolTarget; - name = "mDNS Command-Line tool"; + name = "mDNS command-line tool"; productInstallPath = /usr/bin; - productName = "Sample mDNS Client"; + productName = "mDNS command-line tool"; productReference = 6575FC1D022EB76000000109; }; 6575FC1D022EB76000000109 = { @@ -626,10 +641,10 @@ 6575FC1F022EB78C00000109 = { children = ( 6575FC20022EB7AA00000109, - 004EFB9604CC78130CCA2C71, + FF1C919F07021E3F001048AB, ); isa = PBXGroup; - name = SampleMulticastDNSClient; + name = "Command-Line Clients"; refType = 4; }; 6575FC20022EB7AA00000109 = { @@ -637,7 +652,7 @@ indentWidth = 4; isa = PBXFileReference; path = SamplemDNSClient.c; - refType = 4; + refType = 2; tabWidth = 4; usesTabs = 0; }; @@ -670,14 +685,14 @@ //7F3 //7F4 7F18A9F60587CEF6001880B3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = DNSCommon.c; path = ../mDNSCore/DNSCommon.c; refType = 2; }; 7F18A9F70587CEF6001880B3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = uDNS.c; path = ../mDNSCore/uDNS.c; @@ -708,7 +723,7 @@ }; }; 7F461DB5062DBF2900672BF3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = DNSDigest.c; path = ../mDNSCore/DNSDigest.c; @@ -744,6 +759,24 @@ settings = { }; }; + 7FC8F9D406D14E66007E879D = { + fileEncoding = 4; + isa = PBXFileReference; + path = LegacyNATTraversal.c; + refType = 2; + }; + 7FC8F9D506D14E66007E879D = { + fileRef = 7FC8F9D406D14E66007E879D; + isa = PBXBuildFile; + settings = { + }; + }; + 7FC8F9D606D14E66007E879D = { + fileRef = 7FC8F9D406D14E66007E879D; + isa = PBXBuildFile; + settings = { + }; + }; //7F0 //7F1 //7F2 @@ -775,91 +808,91 @@ refType = 4; }; DB2CC4430662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = BaseListener.java; path = ../mDNSShared/Java/BaseListener.java; refType = 2; }; DB2CC4440662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = BrowseListener.java; path = ../mDNSShared/Java/BrowseListener.java; refType = 2; }; DB2CC4450662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = DNSRecord.java; path = ../mDNSShared/Java/DNSRecord.java; refType = 2; }; DB2CC4460662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = DNSSD.java; path = ../mDNSShared/Java/DNSSD.java; refType = 2; }; DB2CC4470662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = DNSSDException.java; path = ../mDNSShared/Java/DNSSDException.java; refType = 2; }; DB2CC4480662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = DNSSDRegistration.java; path = ../mDNSShared/Java/DNSSDRegistration.java; refType = 2; }; DB2CC4490662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = DNSSDService.java; path = ../mDNSShared/Java/DNSSDService.java; refType = 2; }; DB2CC44A0662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = DomainListener.java; path = ../mDNSShared/Java/DomainListener.java; refType = 2; }; DB2CC44B0662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = JNISupport.c; path = ../mDNSShared/Java/JNISupport.c; refType = 2; }; DB2CC44C0662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = QueryListener.java; path = ../mDNSShared/Java/QueryListener.java; refType = 2; }; DB2CC44D0662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = RegisterListener.java; path = ../mDNSShared/Java/RegisterListener.java; refType = 2; }; DB2CC44E0662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = ResolveListener.java; path = ../mDNSShared/Java/ResolveListener.java; refType = 2; }; DB2CC44F0662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = TXTRecord.java; path = ../mDNSShared/Java/TXTRecord.java; @@ -909,6 +942,7 @@ buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; + GCC_VERSION = 3.3; INSTALL_PATH = /System/Library/Java/Extensions; JAVA_ARCHIVE_CLASSES = YES; JAVA_ARCHIVE_COMPRESSION = YES; @@ -1062,7 +1096,8 @@ DEBUGGING_SYMBOLS = NO; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; - HEADER_SEARCH_PATHS = "\"$(SYSTEM_LIBRARY_DIR)/Frameworks/JavaVM.framework/Versions/A/Headers\" \"$(SYSTEM_LIBRARY_DIR)/Frameworks/JavaVM.framework/Versions/1.3.1/Headers\" \"$(OBJROOT)/mDNSResponder.build/dns_sd.jar.build\""; + GCC_VERSION = 3.3; + HEADER_SEARCH_PATHS = "../mDNSShared \"$(SYSTEM_LIBRARY_DIR)/Frameworks/JavaVM.framework/Versions/A/Headers\" \"$(SYSTEM_LIBRARY_DIR)/Frameworks/JavaVM.framework/Versions/1.3.1/Headers\" \"$(OBJROOT)/mDNSResponder.build/dns_sd.jar.build\""; INSTALL_PATH = /usr/lib/java; LIBRARY_STYLE = DYNAMIC; OTHER_CFLAGS = ""; @@ -1107,7 +1142,7 @@ }; }; DBAAFE29057E8F4D0085CAD0 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = mDNSDebug.c; path = ../mDNSShared/mDNSDebug.c; @@ -1126,7 +1161,7 @@ }; }; DBAAFE2C057E8F660085CAD0 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = GenLinkedList.c; path = ../mDNSShared/GenLinkedList.c; @@ -1239,29 +1274,329 @@ //FF2 //FF3 //FF4 - FF0E0B5C065ADC3800FE4D9C = { + FF0E0B5D065ADC7600FE4D9C = { + fileEncoding = 4; + isa = PBXFileReference; + name = mDNS.1; + path = ../mDNSShared/mDNS.1; + refType = 2; + }; + FF16238F07023BD2001AB7D7 = { + isa = PBXTargetDependency; + target = FF1C919207021C84001048AB; + }; + FF1C919207021C84001048AB = { + buildPhases = ( + FF1C919307021C84001048AB, + FF1C919407021C84001048AB, + FF1C919607021C84001048AB, + FF1C919807021C84001048AB, + FF1C919907021C84001048AB, + ); + buildSettings = { + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_VERSION = 3.3; + INSTALL_PATH = /usr/bin; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OTHER_CFLAGS = "-no-cpp-precomp -mdynamic-no-pic -D__MACOSX__ -DmDNSResponderVersion=$(MVERS) -I../mDNSShared"; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = "dns-sd"; + REZ_EXECUTABLE = YES; + SECTORDER_FLAGS = ""; + STRIPFLAGS = "-S"; + WARNING_CFLAGS = "-W -Wall -Wmissing-prototypes -Wno-four-char-constants -Wno-unknown-pragmas"; + }; + dependencies = ( + ); + isa = PBXToolTarget; + name = "dns-sd command-line tool"; + productInstallPath = /usr/bin; + productName = "dns-sd command-line tool"; + productReference = FF1C919B07021C84001048AB; + }; + FF1C919307021C84001048AB = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF1C919407021C84001048AB = { + buildActionMask = 2147483647; + files = ( + FF1C91A007021E40001048AB, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF1C919607021C84001048AB = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF1C919807021C84001048AB = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXRezBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF1C919907021C84001048AB = { buildActionMask = 8; dstPath = /usr/share/man/man1; dstSubfolderSpec = 0; files = ( - FF0E0B5E065ADCA400FE4D9C, + FF1C919E07021D78001048AB, ); isa = PBXCopyFilesBuildPhase; runOnlyForDeploymentPostprocessing = 1; }; - FF0E0B5D065ADC7600FE4D9C = { + FF1C919B07021C84001048AB = { + isa = PBXExecutableFileReference; + path = "dns-sd"; + refType = 3; + }; + FF1C919D07021D77001048AB = { fileEncoding = 4; isa = PBXFileReference; - name = mDNS.1; - path = ../mDNSShared/mDNS.1; + name = "dns-sd.1"; + path = "../mDNSShared/dns-sd.1"; refType = 2; }; - FF0E0B5E065ADCA400FE4D9C = { - fileRef = FF0E0B5D065ADC7600FE4D9C; + FF1C919E07021D78001048AB = { + fileRef = FF1C919D07021D77001048AB; + isa = PBXBuildFile; + settings = { + }; + }; + FF1C919F07021E3F001048AB = { + fileEncoding = 4; + isa = PBXFileReference; + name = "dns-sd.c"; + path = "../Clients/dns-sd.c"; + refType = 2; + }; + FF1C91A007021E40001048AB = { + fileRef = FF1C919F07021E3F001048AB; + isa = PBXBuildFile; + settings = { + }; + }; + FF25792906C9A70800376F7B = { + buildPhases = ( + FF25792A06C9A70800376F7B, + FF25792D06C9A70800376F7B, + FF25793A06C9A70800376F7B, + FF25793F06C9A70800376F7B, + FF25794006C9A70800376F7B, + ); + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ""; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_VERSION = 3.3; + HEADER_SEARCH_PATHS = "\"$(APPLE_INTERNAL_DEVELOPER_DIR)/Headers\""; + INSTALL_PATH = /usr/sbin; + LIBRARY_SEARCH_PATHS = ""; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OTHER_CFLAGS = "-no-cpp-precomp -mdynamic-no-pic -D__MACOSX__ -DmDNSResponderVersion=$(MVERS)"; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = dnsextd; + REZ_EXECUTABLE = YES; + SECTORDER_FLAGS = ""; + STRIPFLAGS = "-S"; + WARNING_CFLAGS = "-W -Wall -Wmissing-prototypes -Wno-four-char-constants -Wno-unknown-pragmas"; + }; + dependencies = ( + ); + isa = PBXToolTarget; + name = dnsextd; + productInstallPath = /usr/sbin; + productName = mDNSResponder; + productReference = FF25794406C9A70800376F7B; + }; + FF25792A06C9A70800376F7B = { + buildActionMask = 2147483647; + files = ( + FF25792B06C9A70800376F7B, + FF25792C06C9A70800376F7B, + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF25792B06C9A70800376F7B = { + fileRef = 6575FBFF022EAFBA00000109; isa = PBXBuildFile; settings = { }; }; + FF25792C06C9A70800376F7B = { + fileRef = F5E11B5B04A28126019798ED; + isa = PBXBuildFile; + settings = { + }; + }; + FF25792D06C9A70800376F7B = { + buildActionMask = 2147483647; + files = ( + FF25793606C9A70800376F7B, + FF25793806C9A70800376F7B, + FF25794706C9A8BF00376F7B, + FF25794906C9A97400376F7B, + FF25794A06C9A98700376F7B, + FF25794D06C9A9D500376F7B, + FF25794E06C9AA3000376F7B, + FF25795006C9AA8B00376F7B, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF25793606C9A70800376F7B = { + fileRef = 7F18A9F60587CEF6001880B3; + isa = PBXBuildFile; + settings = { + }; + }; + FF25793806C9A70800376F7B = { + fileRef = 7F461DB5062DBF2900672BF3; + isa = PBXBuildFile; + settings = { + }; + }; + FF25793A06C9A70800376F7B = { + buildActionMask = 2147483647; + files = ( + FF25793B06C9A70800376F7B, + FF25793C06C9A70800376F7B, + FF25793D06C9A70800376F7B, + FF25793E06C9A70800376F7B, + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF25793B06C9A70800376F7B = { + fileRef = 09AB6884FE841BABC02AAC07; + isa = PBXBuildFile; + settings = { + }; + }; + FF25793C06C9A70800376F7B = { + fileRef = 65713D46025A293200000109; + isa = PBXBuildFile; + settings = { + }; + }; + FF25793D06C9A70800376F7B = { + fileRef = 00CA213D02786FC30CCA2C71; + isa = PBXBuildFile; + settings = { + }; + }; + FF25793E06C9A70800376F7B = { + fileRef = 7F869685066EE02400D2A2DC; + isa = PBXBuildFile; + settings = { + }; + }; + FF25793F06C9A70800376F7B = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXRezBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF25794006C9A70800376F7B = { + buildActionMask = 8; + dstPath = /usr/share/man/man8; + dstSubfolderSpec = 0; + files = ( + FFF4F63B06CFE4DD00459EFD, + ); + isa = PBXCopyFilesBuildPhase; + runOnlyForDeploymentPostprocessing = 1; + }; + FF25794406C9A70800376F7B = { + isa = PBXExecutableFileReference; + path = dnsextd; + refType = 3; + }; + FF25794606C9A8BF00376F7B = { + fileEncoding = 4; + isa = PBXFileReference; + name = dnsextd.c; + path = ../mDNSPosix/dnsextd.c; + refType = 2; + }; + FF25794706C9A8BF00376F7B = { + fileRef = FF25794606C9A8BF00376F7B; + isa = PBXBuildFile; + settings = { + }; + }; + FF25794906C9A97400376F7B = { + fileRef = 7F18A9F70587CEF6001880B3; + isa = PBXBuildFile; + settings = { + }; + }; + FF25794A06C9A98700376F7B = { + fileRef = DBAAFE29057E8F4D0085CAD0; + isa = PBXBuildFile; + settings = { + }; + }; + FF25794C06C9A9D500376F7B = { + fileEncoding = 4; + isa = PBXFileReference; + name = mDNSPosix.c; + path = ../mDNSPosix/mDNSPosix.c; + refType = 2; + }; + FF25794D06C9A9D500376F7B = { + fileRef = FF25794C06C9A9D500376F7B; + isa = PBXBuildFile; + settings = { + }; + }; + FF25794E06C9AA3000376F7B = { + fileRef = DBAAFE2C057E8F660085CAD0; + isa = PBXBuildFile; + settings = { + }; + }; + FF25794F06C9AA8B00376F7B = { + fileEncoding = 4; + isa = PBXFileReference; + name = mDNSUNP.c; + path = ../mDNSPosix/mDNSUNP.c; + refType = 2; + }; + FF25795006C9AA8B00376F7B = { + fileRef = FF25794F06C9AA8B00376F7B; + isa = PBXBuildFile; + settings = { + }; + }; + FF25795106C9AB1D00376F7B = { + isa = PBXTargetDependency; + target = FF25792906C9A70800376F7B; + }; + FF37BE9207614059003C0420 = { + buildActionMask = 2147483647; + files = ( + ); + generatedFileNames = ( + ); + isa = PBXShellScriptBuildPhase; + neededFileNames = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [ -e /usr/local/lib/libdnsinfo.a ]\nthen\nrm -f \"${OBJROOT}/libdnsinfo.a\"\nelse\ntouch ${OBJROOT}/empty.c\ncc ${OBJROOT}/empty.c -c -o \"${OBJROOT}/libdnsinfo.a\"\nrm -f ${OBJROOT}/empty.c\nfi"; + }; FF485D5105632E0000130380 = { fileEncoding = 4; isa = PBXFileReference; @@ -1285,6 +1620,25 @@ settings = { }; }; + FFCB6D73075D539900B8AF62 = { + fileEncoding = 4; + isa = PBXFileReference; + name = PlatformCommon.c; + path = ../mDNSShared/PlatformCommon.c; + refType = 2; + }; + FFCB6D74075D539900B8AF62 = { + fileRef = FFCB6D73075D539900B8AF62; + isa = PBXBuildFile; + settings = { + }; + }; + FFCB6D75075D595E00B8AF62 = { + fileRef = FFCB6D73075D539900B8AF62; + isa = PBXBuildFile; + settings = { + }; + }; FFD41DDA0664157900F0C438 = { includeInIndex = 0; isa = PBXZipArchiveReference; @@ -1316,6 +1670,48 @@ isa = PBXTargetDependency; target = DB2CC4530662DD6800335AB3; }; + FFF4F63A06CFE4DD00459EFD = { + fileEncoding = 4; + isa = PBXFileReference; + name = dnsextd.8; + path = ../mDNSShared/dnsextd.8; + refType = 2; + }; + FFF4F63B06CFE4DD00459EFD = { + fileRef = FFF4F63A06CFE4DD00459EFD; + isa = PBXBuildFile; + settings = { + }; + }; + FFF4F63C06CFE53300459EFD = { + buildActionMask = 8; + dstPath = /usr/share/man/man1; + dstSubfolderSpec = 0; + files = ( + FFF4F63D06CFE54300459EFD, + ); + isa = PBXCopyFilesBuildPhase; + runOnlyForDeploymentPostprocessing = 1; + }; + FFF4F63D06CFE54300459EFD = { + fileRef = FF0E0B5D065ADC7600FE4D9C; + isa = PBXBuildFile; + settings = { + }; + }; + FFF7174A07614A8600E10551 = { + buildActionMask = 2147483647; + files = ( + ); + generatedFileNames = ( + ); + isa = PBXShellScriptBuildPhase; + neededFileNames = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [ -e /usr/local/lib/libdnsinfo.a ]\nthen\nrm -f \"${OBJROOT}/libdnsinfo.a\"\nelse\ntouch ${OBJROOT}/empty.c\ncc ${OBJROOT}/empty.c -c -o \"${OBJROOT}/libdnsinfo.a\"\nrm -f ${OBJROOT}/empty.c\nfi"; + }; }; rootObject = 08FB7793FE84155DC02AAC07; } diff --git a/mDNSPosix/Client.c b/mDNSPosix/Client.c index 193272d..ad3480c 100755 --- a/mDNSPosix/Client.c +++ b/mDNSPosix/Client.c @@ -1,10 +1,9 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -25,6 +24,20 @@ Change History (most recent first): $Log: Client.c,v $ +Revision 1.14 2004/11/30 22:37:00 cheshire +Update copyright dates and add "Mode: C; tab-width: 4" headers + +Revision 1.13 2004/10/19 21:33:20 cheshire + Cannot resolve non-local registrations using the mach API +Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name +doesn't force multicast unless you set this flag to indicate explicitly that this is what you want + +Revision 1.12 2004/09/17 01:08:53 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + Revision 1.11 2003/11/17 20:14:32 cheshire Typo: Wrote "domC" where it should have said "domainC" @@ -70,7 +83,7 @@ First checkin #include #include -#include "mDNSClientAPI.h"// Defines the interface to the mDNS core code +#include "mDNSEmbeddedAPI.h"// Defines the interface to the mDNS core code #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform #include "ExampleClientApp.h" @@ -236,7 +249,7 @@ int main(int argc, char **argv) MakeDomainNameFromDNSNameString(&type, gServiceType); MakeDomainNameFromDNSNameString(&domain, "local."); - status = mDNS_StartBrowse(&mDNSStorage, &question, &type, &domain, mDNSInterface_Any, BrowseCallback, NULL); + status = mDNS_StartBrowse(&mDNSStorage, &question, &type, &domain, mDNSInterface_Any, mDNSfalse, BrowseCallback, NULL); // Run the platform main event loop until the user types ^C. // The BrowseCallback routine is responsible for printing diff --git a/mDNSPosix/ExampleClientApp.c b/mDNSPosix/ExampleClientApp.c index 8561388..17843af 100644 --- a/mDNSPosix/ExampleClientApp.c +++ b/mDNSPosix/ExampleClientApp.c @@ -1,10 +1,9 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -25,6 +24,18 @@ Change History (most recent first): $Log: ExampleClientApp.c,v $ +Revision 1.12 2004/11/30 22:37:00 cheshire +Update copyright dates and add "Mode: C; tab-width: 4" headers + +Revision 1.11 2004/09/17 01:08:53 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.10 2004/09/16 01:58:22 cheshire +Fix compiler warnings + Revision 1.9 2003/08/12 19:56:26 cheshire Update to APSL 2.0 @@ -49,7 +60,7 @@ Add log header #include // For gethostbyname() #include // For SIGINT, etc. -#include "mDNSClientAPI.h" // Defines the interface to the client layer above +#include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform //******************************************************************************************* @@ -60,7 +71,7 @@ static volatile mDNSBool StopNow; mDNSlocal void HandleSIG(int signal) { (void)signal; // Unused - debugf(""); + debugf("%s",""); debugf("HandleSIG"); StopNow = mDNStrue; } diff --git a/mDNSPosix/ExampleClientApp.h b/mDNSPosix/ExampleClientApp.h index d9b20c5..fa9f0fc 100644 --- a/mDNSPosix/ExampleClientApp.h +++ b/mDNSPosix/ExampleClientApp.h @@ -1,10 +1,9 @@ -/* +/* -*- Mode: C; tab-width: 4 -*- + * * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -25,6 +24,9 @@ Change History (most recent first): $Log: ExampleClientApp.h,v $ +Revision 1.6 2004/11/30 22:37:00 cheshire +Update copyright dates and add "Mode: C; tab-width: 4" headers + Revision 1.5 2003/08/12 19:56:26 cheshire Update to APSL 2.0 diff --git a/mDNSPosix/Identify.c b/mDNSPosix/Identify.c index ed9fd7d..27b2505 100644 --- a/mDNSPosix/Identify.c +++ b/mDNSPosix/Identify.c @@ -1,10 +1,9 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -38,6 +37,41 @@ Change History (most recent first): $Log: Identify.c,v $ +Revision 1.33 2004/11/30 22:37:00 cheshire +Update copyright dates and add "Mode: C; tab-width: 4" headers + +Revision 1.32 2004/10/19 21:33:21 cheshire + Cannot resolve non-local registrations using the mach API +Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name +doesn't force multicast unless you set this flag to indicate explicitly that this is what you want + +Revision 1.31 2004/10/16 00:17:00 cheshire + Replace IP TTL 255 check with local subnet source address check + +Revision 1.30 2004/09/21 23:29:51 cheshire + DNSServiceResolve should delay sending packets + +Revision 1.29 2004/09/17 01:08:53 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.28 2004/09/17 00:31:52 cheshire +For consistency with ipv6, renamed rdata field 'ip' to 'ipv4' + +Revision 1.27 2004/09/16 01:58:22 cheshire +Fix compiler warnings + +Revision 1.26 2004/08/24 21:55:07 cheshire +Don't try to build IPv6 code on systems that don't have IPv6 + +Revision 1.25 2004/07/20 23:42:37 cheshire +Update to use only "_services._dns-sd._udp.local." meta-query for service enumeration + +Revision 1.24 2004/06/15 02:39:47 cheshire +When displaying error message, only show command name, not entire path + Revision 1.23 2004/05/18 23:51:26 cheshire Tidy up all checkin comments to use consistent "" format for bug numbers @@ -137,7 +171,7 @@ Add mDNSIdentify tool, used to discover what version of mDNSResponder a particul #include #include -#include "mDNSClientAPI.h"// Defines the interface to the mDNS core code +#include "mDNSEmbeddedAPI.h"// Defines the interface to the mDNS core code #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform #include "ExampleClientApp.h" @@ -177,16 +211,16 @@ mDNSlocal mDNSu32 mprintf(const char *format, ...) mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport, - const mDNSInterfaceID InterfaceID, mDNSu8 ttl) + const mDNSInterfaceID InterfaceID) { + (void)dstaddr; // Unused // Snag copy of header ID, then call through lastid = msg->h.id; lastsrc = *srcaddr; // We *want* to allow off-net unicast responses here. - // For now, the simplest way to allow that is to smash the TTL to 255 so that mDNSCore doesn't reject the packet - ttl = 255; - __MDNS__mDNSCoreReceive(m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID, ttl); + // For now, the simplest way to allow that is to pretend it was received via multicast so that mDNSCore doesn't reject the packet + __MDNS__mDNSCoreReceive(m, msg, end, srcaddr, srcport, &AllDNSLinkGroup_v4, dstport, InterfaceID); } static void NameCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) @@ -199,7 +233,7 @@ static void NameCallback(mDNS *const m, DNSQuestion *question, const ResourceRec { ConvertDomainNameToCString(&answer->rdata->u.name, hostname); StopNow = 1; - mprintf("%##s %s %##s\n", answer->name.c, DNSTypeName(answer->rrtype), &answer->rdata->u.name.c); + mprintf("%##s %s %##s\n", answer->name.c, DNSTypeName(answer->rrtype), answer->rdata->u.name.c); } } @@ -213,9 +247,9 @@ static void InfoCallback(mDNS *const m, DNSQuestion *question, const ResourceRec if (!id.NotAnInteger) id = lastid; NumAnswers++; NumAddr++; - mprintf("%##s %s %.4a\n", answer->name.c, DNSTypeName(answer->rrtype), &answer->rdata->u.ip); + mprintf("%##s %s %.4a\n", answer->name.c, DNSTypeName(answer->rrtype), &answer->rdata->u.ipv4); hostaddr.type = mDNSAddrType_IPv4; // Prefer v4 target to v6 target, for now - hostaddr.ip.v4 = answer->rdata->u.ip; + hostaddr.ip.v4 = answer->rdata->u.ipv4; } else if (answer->rrtype == kDNSType_AAAA) { @@ -257,7 +291,7 @@ static void ServicesCallback(mDNS *const m, DNSQuestion *question, const Resourc { NumAnswers++; NumAddr++; - mprintf("%##s %s %##s\n", answer->name.c, DNSTypeName(answer->rrtype), &answer->rdata->u.name.c); + mprintf("%##s %s %##s\n", answer->name.c, DNSTypeName(answer->rrtype), answer->rdata->u.name.c); StopNow = 1; } } @@ -295,9 +329,12 @@ mDNSlocal mStatus StartQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const m q->Target = target ? *target : zeroAddr; q->TargetPort = MulticastDNSPort; q->TargetQID = zeroID; - q->InterfaceID = mDNSInterface_ForceMCast; + q->InterfaceID = mDNSInterface_Any; q->qtype = qtype; q->qclass = kDNSClass_IN; + q->LongLived = mDNSfalse; + q->ExpectUnique = mDNStrue; + q->ForceMCast = mDNStrue; // Query via multicast, even for apparently uDNS names like 1.1.1.17.in-addr.arpa. q->QuestionCallback = callback; q->QuestionContext = NULL; @@ -333,17 +370,20 @@ mDNSlocal int DoQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr mDNSlocal void HandleSIG(int signal) { (void)signal; // Unused - debugf(""); + debugf("%s",""); debugf("HandleSIG"); StopNow = 2; } mDNSexport int main(int argc, char **argv) { + const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0]; int this_arg = 1; mStatus status; struct in_addr s4; +#if HAVE_IPV6 struct in6_addr s6; +#endif char buffer[256]; DNSQuestion q; @@ -382,6 +422,7 @@ mDNSexport int main(int argc, char **argv) DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback); if (StopNow == 2) break; } +#if HAVE_IPV6 else if (inet_pton(AF_INET6, arg, &s6) == 1) { int i; @@ -400,6 +441,7 @@ mDNSexport int main(int argc, char **argv) DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback); if (StopNow == 2) break; } +#endif else strcpy(hostname, arg); @@ -409,16 +451,14 @@ mDNSexport int main(int argc, char **argv) if (hardware[0] || software[0]) { - DNSQuestion q1, q2; + DNSQuestion q1; printf("HINFO Hardware: %s\n", hardware); printf("HINFO Software: %s\n", software); // We need to make sure the services query is targeted if (target.type == 0) target = hostaddr; - StartQuery(&q1, "_services._mdns._udp.local.", kDNSQType_ANY, &target, ServicesCallback); - StartQuery(&q2, "_services._dns-sd._udp.local.", kDNSQType_ANY, &target, ServicesCallback); + StartQuery(&q1, "_services._dns-sd._udp.local.", kDNSQType_ANY, &target, ServicesCallback); WaitForAnswer(&mDNSStorage, 4); mDNS_StopQuery(&mDNSStorage, &q1); - mDNS_StopQuery(&mDNSStorage, &q2); if (StopNow == 2) break; } else if (NumAnswers) @@ -436,6 +476,6 @@ mDNSexport int main(int argc, char **argv) return(0); usage: - fprintf(stderr, "%s or or ...\n", argv[0]); + fprintf(stderr, "%s or or ...\n", progname); return(-1); } diff --git a/mDNSPosix/Makefile b/mDNSPosix/Makefile index 9d28ad5..1f8b65b 100755 --- a/mDNSPosix/Makefile +++ b/mDNSPosix/Makefile @@ -1,25 +1,93 @@ -# Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ +# Copyright (c) 2002-2004, Apple Computer, Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # $Log: Makefile,v $ +# Revision 1.53 2004/12/01 20:04:31 cheshire +# Tidy up alignment +# +# Revision 1.52 2004/12/01 19:46:12 cheshire +# Add install case for Suse 9 (rc*.d directories *inside* the init.d directory) +# +# Revision 1.51 2004/12/01 03:30:29 cheshire +# Add Unicast DNS support to mDNSPosix +# +# Revision 1.50 2004/12/01 01:14:20 cheshire +# Add $(LIBFLAGS) to cc command to build dnsextd (required for Solaris) +# +# Revision 1.49 2004/11/11 01:44:52 cheshire +# Updated error message +# +# Revision 1.48 2004/10/06 02:22:19 cheshire +# Changed MacRoman copyright symbol (should have been UTF-8 in any case :-) to ASCII-compatible "(c)" +# +# Revision 1.47 2004/10/01 22:15:54 rpantos +# rdar://problem/3824265: Replace APSL in client lib with BSD license. +# +# Revision 1.46 2004/09/24 21:15:25 cheshire +# Library "libmdns" misnamed; should be "libdns_sd" +# +# Revision 1.45 2004/09/22 16:23:41 cheshire +# Modify installation for compatibility with Gentoo Linux +# (Thanks to David Black for this information) +# +# Revision 1.44 2004/09/17 01:08:53 cheshire +# Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h +# The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces +# declared in that file are ONLY appropriate to single-address-space embedded applications. +# For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. +# +# Revision 1.43 2004/09/17 00:30:11 cheshire +# Added some '@' signs to make build output less verbose -- +# when there's too much on the screen it's easy to miss build errors and warnings +# +# Revision 1.42 2004/08/24 22:04:37 cheshire +# Need to specify -lpthread for building dnsextd +# +# Revision 1.41 2004/08/11 00:43:26 ksekar +# : DNS Extension daemon for DNS Update Lease +# +# Revision 1.40 2004/07/08 21:45:55 cheshire +# Make nss_mdns only build on Linux. We can add it to other targets (Solaris, +# FreeBSD, etc., as we verify them). In particular, NSS is NOT supported on +# OS X, so including it for "os=jaguar" or "os=panther" broke those builds. +# +# Revision 1.39 2004/06/29 03:34:28 cheshire +# Add 'dot-local' Name Service Switch support from Andrew White at NICTA +# +# Revision 1.38 2004/06/25 02:19:40 rpantos +# And FreeBSD... +# +# Revision 1.37 2004/06/25 00:51:09 rpantos +# And fix the Java build for Posix on Solaris, too. +# +# Revision 1.36 2004/06/25 00:26:27 rpantos +# Changes to fix the Posix build on Solaris. +# +# Revision 1.35 2004/06/18 18:51:31 cheshire +# Add (commented out) "-pedantic" for when we want to check for "mixed declarations and code" warnings +# # Revision 1.34 2004/05/25 18:29:33 cheshire # Move DNSServiceConstructFullName() from dnssd_clientstub.c to dnssd_clientlib.c, # so that it's also accessible to dnssd_clientshim.c (single address space) clients. @@ -85,7 +153,7 @@ # # Revision 1.14 2003/11/14 20:59:09 cheshire # Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h. -# Best solution is just to combine mDNSClientAPI.h and mDNSPlatformFunctions.h into a single file. +# Best solution is just to combine mDNSEmbeddedAPI.h and mDNSPlatformFunctions.h into a single file. # # Revision 1.13 2003/08/06 18:20:51 cheshire # Makefile cleanup @@ -114,13 +182,13 @@ # Added NetMonitor.c # -# This Makefile builds an mDNSResponder daemon and a libmdns.so shared library +# This Makefile builds an mDNSResponder daemon and a libdns_sd.so shared library # for Linux. It also builds several example programs for embedded systems. # # Make with no arguments to build all production targets. # 'make DEBUG=1' to build debugging targets. # 'make clean' or 'make clean DEBUG=1' to delete prod/debug objects & targets -# 'sudo make install [DEBUG=1]' to install mdnsd daemon and libmdns. +# 'sudo make install [DEBUG=1]' to install mdnsd daemon and libdns_sd. # # Notes: # $@ means "The file name of the target of the rule" @@ -137,15 +205,16 @@ COREDIR = ../mDNSCore SHAREDDIR = ../mDNSShared JDK = /usr/jdk -CC = cc -LD = ld +CC = @cc +LD = ld -shared CP = cp RM = rm LN = ln -s -f CFLAGS_COMMON = -I. -I$(COREDIR) -I$(SHAREDDIR) -W -Wall -DPID_FILE=\"/var/run/mdnsd.pid\" -LDFLAGS = -shared +LIBFLAGS = +DNSEXT_FLAGS = -D_REENTRANT -g -Wall -lpthread LDSUFFIX = so -JAVACFLAGS_OS = -fPIC -shared -lmdns +JAVACFLAGS_OS = -fPIC -shared -ldns_sd # Set up diverging paths for debug vs. prod builds DEBUG=0 @@ -155,7 +224,7 @@ OBJDIR = objects/debug BUILDDIR = build/debug STRIP = echo else -CFLAGS_DEBUG = -Os -DMDNS_DEBUGMSGS=0 +CFLAGS_DEBUG = -O0 -DMDNS_DEBUGMSGS=0 OBJDIR = objects/prod BUILDDIR = build/prod STRIP = strip -S @@ -163,16 +232,29 @@ endif # Configure per-OS peculiarities ifeq ($(os),solaris) -CFLAGS_OS = -DNOT_HAVE_DAEMON -DNOT_HAVE_SA_LEN -D_XPG4_2 -D__EXTENSIONS__ -DHAVE_BROKEN_RECVIF_NAME -lsocket -lnsl +CFLAGS_OS = -DNOT_HAVE_DAEMON -DNOT_HAVE_SA_LEN -DNOT_HAVE_SOCKLEN_T -DNOT_HAVE_IF_NAMETOINDEX \ + -DLOG_PERROR=0 -D_XPG4_2 -D__EXTENSIONS__ -DHAVE_BROKEN_RECVIF_NAME -DUSE_TCP_LOOPBACK +CC = gcc +LD = gcc -shared +LIBFLAGS = -lsocket -lnsl +JAVACFLAGS_OS += -I$(JDK)/include/solaris +ifneq ($(DEBUG),1) +STRIP = strip +endif else + ifeq ($(os),linux) CFLAGS_OS = -DNOT_HAVE_SA_LEN -DUSES_NETLINK JAVACFLAGS_OS += -I$(JDK)/include/linux +OPTIONALTARG = nss_mdns +OPTINSTALL = InstalledNSS else + ifeq ($(os),netbsd) CFLAGS_OS = LDCONFIG = ldconfig else + ifeq ($(os),freebsd) # If not already defined, set LOCALBASE to /usr/local # FreeBSD requires the startup script to end in ".sh" @@ -180,29 +262,35 @@ LOCALBASE?=/usr/local INSTBASE=$(LOCALBASE) STARTUPSCRIPTNAME=mdns.sh CFLAGS_OS = +JAVACFLAGS_OS += -I$(JDK)/include/freebsd LDCONFIG = ldconfig else + ifeq ($(os),openbsd) CFLAGS_OS = -DHAVE_BROKEN_RECVDSTADDR LDCONFIG = ldconfig else + ifeq ($(os),jaguar) CFLAGS_OS = -DHAVE_IPV6 -no-cpp-precomp -DNOT_HAVE_SOCKLEN_T -LD = libtool -LDFLAGS = -dynamic -lSystem +LD = libtool -dynamic +LIBFLAGS = -lSystem LDSUFFIX = dylib JDK = /System/Library/Frameworks/JavaVM.framework/Home JAVACFLAGS_OS = -dynamiclib -I/System/Library/Frameworks/JavaVM.framework/Headers -framework JavaVM else + ifeq ($(os),panther) -CFLAGS_OS = -DHAVE_IPV6 -no-cpp-precomp -LD = libtool -LDFLAGS = -dynamic -lSystem +CFLAGS_OS = -DHAVE_IPV6 -no-cpp-precomp #-pedantic +LD = libtool -dynamic +LIBFLAGS = -lSystem LDSUFFIX = dylib JDK = /System/Library/Frameworks/JavaVM.framework/Home JAVACFLAGS_OS = -dynamiclib -I/System/Library/Frameworks/JavaVM.framework/Headers -framework JavaVM else -$(error ERROR: Must specify target OS on command-line: "make os={jaguar,panther,linux,netbsd,freebsd,openbsd,solaris} [target]") + +$(error ERROR: Must specify target OS on command-line, e.g. "make os=panther [target]".\ +Supported operating systems include: jaguar, panther, linux, netbsd, freebsd, openbsd, solaris) endif endif endif @@ -211,6 +299,12 @@ endif endif endif +NSSLIBNAME := libnss_mdns +NSSVERSION := 0.2 +NSSLIBFILE := $(NSSLIBNAME)-$(NSSVERSION).so +NSSLINKNAME := $(NSSLIBNAME).so.2 +NSSINSTPATH := /lib + # If not otherwise defined, we install into /usr/lib and /usr/include # and our startup script is called mdns (e.g. /etc/init.d/mdns) INSTBASE?=/usr @@ -224,7 +318,19 @@ CFLAGS_OS += -DHAVE_IPV6=0 endif endif -# If directory /etc/rc.d/init.d/ exists, then we install into that (old Linux) +# If directory /usr/share/man exists, then we install man pages into that, else /usr/man +ifeq ($(wildcard /usr/share/man), /usr/share/man) +MANPATH := /usr/share/man +else +MANPATH := /usr/man +endif + +# If directories /etc/init.d/rc*.d exist, then we install into that (Suse) +ifeq ($(wildcard /etc/init.d/rc2.d/), /etc/init.d/rc2.d/) +STARTUPSCRIPTDIR = /etc/init.d +RUNLEVELSCRIPTSDIR = /etc/init.d +else +# else if directory /etc/rc.d/init.d/ exists, then we install into that (old Linux) ifeq ($(wildcard /etc/rc.d/init.d/), /etc/rc.d/init.d/) STARTUPSCRIPTDIR = /etc/rc.d/init.d RUNLEVELSCRIPTSDIR = /etc/rc.d @@ -238,14 +344,15 @@ else STARTUPSCRIPTDIR = $(INSTBASE)/etc/rc.d endif endif +endif CFLAGS = $(CFLAGS_COMMON) $(CFLAGS_OS) $(CFLAGS_DEBUG) ############################################################################# -all: setup Daemon libmdns Client Responder ProxyResponder Identify NetMonitor +all: setup Daemon libdns_sd Client Responder ProxyResponder Identify NetMonitor dnsextd $(OPTIONALTARG) -install: setup InstalledDaemon InstalledLib InstalledStartup +install: setup InstalledDaemon InstalledLib InstalledStartup InstalledManPages $(OPTINSTALL) # 'setup' sets up the build directory structure the way we want setup: @@ -264,41 +371,57 @@ clean: # daemon target builds the daemon DAEMONOBJS = $(OBJDIR)/PosixDaemon.c.o $(OBJDIR)/mDNSPosix.c.o $(OBJDIR)/mDNSUNP.c.o $(OBJDIR)/mDNS.c.o \ $(OBJDIR)/DNSDigest.c.o $(OBJDIR)/uDNS.c.o $(OBJDIR)/DNSCommon.c.o $(OBJDIR)/uds_daemon.c.o \ - $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/dnssd_ipc.c.o $(OBJDIR)/GenLinkedList.c.o + $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/dnssd_ipc.c.o $(OBJDIR)/GenLinkedList.c.o $(OBJDIR)/PlatformCommon.c.o Daemon: setup $(BUILDDIR)/mdnsd @echo "Responder daemon done" $(BUILDDIR)/mdnsd: $(DAEMONOBJS) - $(CC) -o $@ $+ - $(STRIP) $@ + $(CC) -o $@ $+ $(CFLAGS) $(LIBFLAGS) + @$(STRIP) $@ -# libmdns target builds the client library -libmdns: setup $(BUILDDIR)/libmdns.$(LDSUFFIX) +# libdns_sd target builds the client library +libdns_sd: setup $(BUILDDIR)/libdns_sd.$(LDSUFFIX) @echo "Client library done" -$(BUILDDIR)/libmdns.$(LDSUFFIX): $(OBJDIR)/dnssd_clientlib.c.so.o $(OBJDIR)/dnssd_clientstub.c.so.o $(OBJDIR)/dnssd_ipc.c.so.o - $(LD) $(LDFLAGS) -o $@ $+ - $(STRIP) $@ +CLIENTLIBOBJS = $(OBJDIR)/dnssd_clientlib.c.so.o $(OBJDIR)/dnssd_clientstub.c.so.o $(OBJDIR)/dnssd_ipc.c.so.o + +$(BUILDDIR)/libdns_sd.$(LDSUFFIX): $(CLIENTLIBOBJS) + @$(LD) $(LIBFLAGS) -o $@ $+ + @$(STRIP) $@ + +# nss_mdns target builds the Name Service Switch module +nss_mdns: setup $(BUILDDIR)/$(NSSLIBFILE) + @echo "Name Service Switch module done" + +$(BUILDDIR)/$(NSSLIBFILE): $(CLIENTLIBOBJS) $(OBJDIR)/nss_mdns.c.so.o + @$(LD) $(LIBFLAGS) -o $@ $+ + @$(STRIP) $@ ############################################################################# # The Install targets place built stuff in their proper places InstalledDaemon: $(INSTBASE)/sbin/mdnsd - @echo $< " installed" + @echo $+ " installed" -InstalledLib: $(INSTBASE)/lib/libmdns.$(LDSUFFIX).$(LIBVERS) $(INSTBASE)/include/dns_sd.h - @echo $< " installed" +InstalledLib: $(INSTBASE)/lib/libdns_sd.$(LDSUFFIX).$(LIBVERS) $(INSTBASE)/include/dns_sd.h + @echo $+ " installed" InstalledStartup: $(STARTUPSCRIPTDIR)/$(STARTUPSCRIPTNAME) - @echo $< " installed" + @echo $+ " installed" + +InstalledNSS: $(NSSINSTPATH)/$(NSSLINKNAME) /etc/nss_mdns.conf $(MANPATH)/man5/nss_mdns.conf.5 $(MANPATH)/man8/libnss_mdns.8 + @echo $+ " installed" + +InstalledManPages: $(MANPATH)/man8/mdnsd.8 + @echo $+ " installed" $(INSTBASE)/sbin/mdnsd: $(BUILDDIR)/mdnsd $(CP) $< $@ -$(INSTBASE)/lib/libmdns.$(LDSUFFIX).$(LIBVERS): $(BUILDDIR)/libmdns.$(LDSUFFIX) +$(INSTBASE)/lib/libdns_sd.$(LDSUFFIX).$(LIBVERS): $(BUILDDIR)/libdns_sd.$(LDSUFFIX) $(CP) $< $@ - $(LN) $@ $(INSTBASE)/lib/libmdns.$(LDSUFFIX) + $(LN) $@ $(INSTBASE)/lib/libdns_sd.$(LDSUFFIX) ifdef LDCONFIG # -m means 'merge into existing database', -R means 'rescan directories' $(LDCONFIG) -mR @@ -311,6 +434,9 @@ $(STARTUPSCRIPTDIR)/$(STARTUPSCRIPTNAME): mdnsd.sh $(STARTUPSCRIPTDIR) $(CP) $< $@ chmod ugo+x $@ ifdef RUNLEVELSCRIPTSDIR +ifeq ($(wildcard $(RUNLEVELSCRIPTSDIR)/runlevels/default), $(RUNLEVELSCRIPTSDIR)/runlevels/default) + $(LN) $@ $(RUNLEVELSCRIPTSDIR)/runlevels/default/mdns +else $(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc2.d/S52mdns $(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc3.d/S52mdns $(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc4.d/S52mdns @@ -318,6 +444,35 @@ ifdef RUNLEVELSCRIPTSDIR $(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc0.d/K16mdns $(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc6.d/K16mdns endif +endif + +$(NSSINSTPATH)/$(NSSLINKNAME): $(NSSINSTPATH)/$(NSSLIBFILE) + $(LN) $< $@ + ldconfig + +$(NSSINSTPATH)/$(NSSLIBFILE): $(BUILDDIR)/$(NSSLIBFILE) + $(CP) $< $@ + chmod 444 $@ + +/etc/nss_mdns.conf: nss_mdns.conf + $(CP) $< $@ + chmod 444 $@ + # Check the nsswitch.conf file. + # If 'mdns' does not already appear on the "hosts:" line, then add it right before 'dns' + cp -f /etc/nsswitch.conf /etc/nsswitch.conf.pre-mdns + sed -e '/mdns/!s/^\(hosts:.*\)dns\(.*\)/\1mdns dns\2/' /etc/nsswitch.conf.pre-mdns > /etc/nsswitch.conf + +$(MANPATH)/man5/%.5: %.5 + cp $< $@ + chmod 444 $@ + +$(MANPATH)/man8/%.8: %.8 + cp $< $@ + chmod 444 $@ + +$(MANPATH)/man8/mdnsd.8: $(SHAREDDIR)/mDNSResponder.8 + cp $< $@ + chmod 444 $@ ############################################################################# @@ -394,20 +549,26 @@ Identify: setup $(BUILDDIR)/mDNSIdentify NetMonitor: setup $(BUILDDIR)/mDNSNetMonitor @echo "NetMonitor done" +dnsextd: setup $(BUILDDIR)/dnsextd + @echo "dnsextd done" + $(BUILDDIR)/mDNSClientPosix: $(APPOBJ) $(OBJDIR)/Client.c.o - $(CC) $+ -o $@ + $(CC) $+ -o $@ $(LIBFLAGS) $(BUILDDIR)/mDNSResponderPosix: $(COMMONOBJ) $(OBJDIR)/Responder.c.o - $(CC) $+ -o $@ + $(CC) $+ -o $@ $(LIBFLAGS) $(BUILDDIR)/mDNSProxyResponderPosix: $(COMMONOBJ) $(OBJDIR)/ProxyResponder.c.o - $(CC) $+ -o $@ + $(CC) $+ -o $@ $(LIBFLAGS) $(BUILDDIR)/mDNSIdentify: $(SPECIALOBJ) $(OBJDIR)/Identify.c.o - $(CC) $+ -o $@ + $(CC) $+ -o $@ $(LIBFLAGS) $(BUILDDIR)/mDNSNetMonitor: $(SPECIALOBJ) $(OBJDIR)/NetMonitor.c.o - $(CC) $+ -o $@ + $(CC) $+ -o $@ $(LIBFLAGS) + +$(BUILDDIR)/dnsextd: $(SPECIALOBJ) $(OBJDIR)/dnsextd.c.o + $(CC) $+ -o $@ $(LIBFLAGS) $(DNSEXT_FLAGS) ############################################################################# diff --git a/mDNSPosix/NetMonitor.c b/mDNSPosix/NetMonitor.c index 8bce09e..c8aad7b 100644 --- a/mDNSPosix/NetMonitor.c +++ b/mDNSPosix/NetMonitor.c @@ -1,10 +1,9 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -38,6 +37,27 @@ Change History (most recent first): $Log: NetMonitor.c,v $ +Revision 1.70 2004/12/04 02:13:20 cheshire +Pass proper record type in GetLargeResourceRecord() calls + +Revision 1.69 2004/11/30 22:37:01 cheshire +Update copyright dates and add "Mode: C; tab-width: 4" headers + +Revision 1.68 2004/10/23 01:16:01 cheshire + uDNS operations not always reliable on multi-homed hosts + +Revision 1.67 2004/10/16 00:17:00 cheshire + Replace IP TTL 255 check with local subnet source address check + +Revision 1.66 2004/09/17 00:31:52 cheshire +For consistency with ipv6, renamed rdata field 'ip' to 'ipv4' + +Revision 1.65 2004/09/16 01:58:22 cheshire +Fix compiler warnings + +Revision 1.64 2004/06/15 02:39:47 cheshire +When displaying error message, only show command name, not entire path + Revision 1.63 2004/05/18 23:51:26 cheshire Tidy up all checkin comments to use consistent "" format for bug numbers @@ -521,7 +541,7 @@ mDNSlocal void SendUnicastQuery(mDNS *const m, HostEntry *entry, domainname *nam m->ExpectUnicastResponse = m->timenow; } - mDNSSendDNSMessage(&mDNSStorage, &query, qptr, InterfaceID, target, MulticastDNSPort); + mDNSSendDNSMessage(&mDNSStorage, &query, qptr, InterfaceID, target, MulticastDNSPort, -1, mDNSNULL); } mDNSlocal void AnalyseHost(mDNS *const m, HostEntry *entry, const mDNSInterfaceID InterfaceID) @@ -563,7 +583,7 @@ mDNSlocal void ShowSortedHostList(HostList *list, int max) { int len = mprintf("%#-25a", &e->addr); if (len > 25) mprintf("\n%25s", ""); - mprintf("%8d %8d %8d %8d %8d %8d %8d", e->totalops, + mprintf("%8lu %8lu %8lu %8lu %8lu %8lu %8lu", e->totalops, e->stat[OP_probe], e->stat[OP_goodbye], e->stat[OP_browseq], e->stat[OP_browsea], e->stat[OP_resolveq], e->stat[OP_resolvea]); @@ -653,7 +673,7 @@ mDNSlocal void printstats(int max) if (!m) return; m->printed = mDNStrue; if (i==0) mprintf("%-25s%s\n", "Service Type", OPBanner); - mprintf("%##-25s%8d %8d %8d %8d %8d %8d %8d\n", &m->srvtype, m->totalops, m->stat[OP_probe], + mprintf("%##-25s%8d %8d %8d %8d %8d %8d %8d\n", m->srvtype.c, m->totalops, m->stat[OP_probe], m->stat[OP_goodbye], m->stat[OP_browseq], m->stat[OP_browsea], m->stat[OP_resolveq], m->stat[OP_resolvea]); } } @@ -665,7 +685,7 @@ mDNSlocal const mDNSu8 *FindUpdate(mDNS *const m, const DNSMessage *const query, for (i = 0; i < query->h.numAuthorities; i++) { const mDNSu8 *p2 = ptr; - ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, 0, pkt); + ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, pkt); if (!ptr) break; if (ResourceRecordAnswersQuestion(&pkt->r.resrec, q)) return(p2); } @@ -681,7 +701,7 @@ mDNSlocal void DisplayTimestamp(void) mprintf("\n%d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec); } -mDNSlocal void DisplayPacketHeader(const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSu8 ttl) +mDNSlocal void DisplayPacketHeader(const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr) { const char *const ptype = (msg->h.flags.b[0] & kDNSFlag0_QR_Response) ? "-R- " : (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) ? "-Q- " : "-LQ-"; @@ -692,8 +712,6 @@ mDNSlocal void DisplayPacketHeader(const DNSMessage *const msg, const mDNSu8 *co if (msg->h.id.NotAnInteger) mprintf(" ID:%u", mDNSVal16(msg->h.id)); - if (ttl != 255) mprintf(" TTL:%u", ttl); - if (!mDNSAddrIsDNSMulticast(dstaddr)) mprintf(" To: %#a", dstaddr); if (msg->h.flags.b[0] & kDNSFlag0_TC) @@ -713,12 +731,12 @@ mDNSlocal void DisplayResourceRecord(const mDNSAddr *const srcaddr, const char * RDataBody *rd = &pktrr->rdata->u; mDNSu8 *rdend = (mDNSu8 *)rd + pktrr->rdlength; - mDNSu32 n = mprintf("%#-16a %-5s %-5s%5d %##s -> ", srcaddr, op, DNSTypeName(pktrr->rrtype), pktrr->rroriginalttl, pktrr->name.c); + int n = mprintf("%#-16a %-5s %-5s%5lu %##s -> ", srcaddr, op, DNSTypeName(pktrr->rrtype), pktrr->rroriginalttl, pktrr->name.c); switch(pktrr->rrtype) { - case kDNSType_A: n += mprintf("%.4a", &rd->ip); break; - case kDNSType_PTR: n += mprintf("%##.*s", MaxWidth - n, &rd->name); break; + case kDNSType_A: n += mprintf("%.4a", &rd->ipv4); break; + case kDNSType_PTR: n += mprintf("%##.*s", MaxWidth - n, rd->name.c); break; case kDNSType_HINFO:// same as kDNSType_TXT below case kDNSType_TXT: { mDNSu8 *t = rd->txt.c; @@ -745,7 +763,7 @@ mDNSlocal void DisplayResourceRecord(const mDNSAddr *const srcaddr, const char * n += mprintf("%.*s", MaxWidth - n, buffer); } break; case kDNSType_AAAA: n += mprintf("%.16a", &rd->ipv6); break; - case kDNSType_SRV: n += mprintf("%##s:%d", &rd->srv.target, mDNSVal16(rd->srv.port)); break; + case kDNSType_SRV: n += mprintf("%##s:%d", rd->srv.target.c, mDNSVal16(rd->srv.port)); break; default: { mDNSu8 *s = rd->data; while (s < rdend && p < buffer+MaxWidth) @@ -792,7 +810,7 @@ mDNSlocal void DisplayError(const mDNSAddr *srcaddr, const mDNSu8 *ptr, const mD } mDNSlocal void DisplayQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, - const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID, mDNSu8 ttl) + const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID) { int i; const mDNSu8 *ptr = msg->data; @@ -801,7 +819,7 @@ mDNSlocal void DisplayQuery(mDNS *const m, const DNSMessage *const msg, const mD HostEntry *entry = GotPacketFromHost(srcaddr, MQ ? HostPkt_Q : HostPkt_L, msg->h.id); LargeCacheRecord pkt; - DisplayPacketHeader(msg, end, srcaddr, srcport, dstaddr, ttl); + DisplayPacketHeader(msg, end, srcaddr, srcport, dstaddr); if (msg->h.id.NotAnInteger != 0xFFFF) { if (MQ) NumPktQ++; else NumPktL++; @@ -838,7 +856,7 @@ mDNSlocal void DisplayQuery(mDNS *const m, const DNSMessage *const msg, const mD for (i=0; ih.numAnswers; i++) { const mDNSu8 *ep = ptr; - ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt); + ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt); if (!ptr) { DisplayError(srcaddr, ep, end, "KNOWN ANSWER"); return; } DisplayResourceRecord(srcaddr, "(KA)", &pkt.r.resrec); @@ -860,14 +878,14 @@ mDNSlocal void DisplayQuery(mDNS *const m, const DNSMessage *const msg, const mD } mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, - const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID, mDNSu8 ttl) + const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID) { int i; const mDNSu8 *ptr = msg->data; HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R, msg->h.id); LargeCacheRecord pkt; - DisplayPacketHeader(msg, end, srcaddr, srcport, dstaddr, ttl); + DisplayPacketHeader(msg, end, srcaddr, srcport, dstaddr); if (msg->h.id.NotAnInteger != 0xFFFF) NumPktR++; for (i=0; ih.numQuestions; i++) @@ -885,7 +903,7 @@ mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const for (i=0; ih.numAnswers; i++) { const mDNSu8 *ep = ptr; - ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt); + ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt); if (!ptr) { DisplayError(srcaddr, ep, end, "ANSWER"); return; } if (pkt.r.resrec.rroriginalttl) { @@ -905,7 +923,7 @@ mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const for (i=0; ih.numAuthorities; i++) { const mDNSu8 *ep = ptr; - ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt); + ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &pkt); if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; } mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE AUTHORITY IN mDNS RESPONSE **** %-5s %##s\n", srcaddr, DNSTypeName(pkt.r.resrec.rrtype), pkt.r.resrec.name.c); @@ -914,7 +932,7 @@ mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const for (i=0; ih.numAdditionals; i++) { const mDNSu8 *ep = ptr; - ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt); + ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAdd, &pkt); if (!ptr) { DisplayError(srcaddr, ep, end, "ADDITIONAL"); return; } NumAdditionals++; DisplayResourceRecord(srcaddr, (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AD)" : "(AD+)", &pkt.r.resrec); @@ -934,7 +952,7 @@ mDNSlocal void ProcessUnicastResponse(mDNS *const m, const DNSMessage *const msg for (i=0; ih.numAnswers + msg->h.numAuthorities + msg->h.numAdditionals; i++) { LargeCacheRecord pkt; - ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt); + ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt); if (pkt.r.resrec.rroriginalttl && entry) RecordHostInfo(entry, &pkt.r.resrec); } } @@ -948,7 +966,7 @@ mDNSlocal mDNSBool AddressMatchesFilterList(const mDNSAddr *srcaddr) } mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, - const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, mDNSu8 ttl) + const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, const mDNSInterfaceID InterfaceID) { const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery; const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; @@ -964,17 +982,6 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNS msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]); - if (ttl < 254) - { - debugf("** Apparent spoof mDNS %s packet from %#-15a to %#-15a TTL %d on %p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s", - (QR_OP == StdQ) ? "Query" : (QR_OP == StdR) ? "Response" : "Unkown", - srcaddr, dstaddr, ttl, InterfaceID, - msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", - msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", - msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,", - msg->h.numAdditionals, msg->h.numAdditionals == 1 ? "" : "s"); - } - // For now we're only interested in monitoring IPv4 traffic. // All IPv6 packets should just be duplicates of the v4 packets. if (AddressMatchesFilterList(srcaddr)) @@ -987,8 +994,8 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNS } else { - if (QR_OP == StdQ) DisplayQuery (m, msg, end, srcaddr, srcport, dstaddr, InterfaceID, ttl); - else if (QR_OP == StdR) DisplayResponse (m, msg, end, srcaddr, srcport, dstaddr, InterfaceID, ttl); + if (QR_OP == StdQ) DisplayQuery (m, msg, end, srcaddr, srcport, dstaddr, InterfaceID); + else if (QR_OP == StdR) DisplayResponse (m, msg, end, srcaddr, srcport, dstaddr, InterfaceID); else { debugf("Unknown DNS packet type %02X%02X (ignored)", msg->h.flags.b[0], msg->h.flags.b[1]); @@ -1053,7 +1060,7 @@ mDNSlocal mStatus mDNSNetMonitor(void) localtime_r((time_t*)&tv_end.tv_sec, &tm); mprintf("End %3d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv_end.tv_usec); mprintf("Captured for %3d:%02d:%02d.%06d\n", h, m, s, tv_interval.tv_usec); - if (!Filters) mprintf("Unique source addresses seen on network: %d\n", IPv4HostList.num + IPv6HostList.num); + if (!Filters) mprintf("Unique source addresses seen on network: %ld\n", IPv4HostList.num + IPv6HostList.num); mprintf("\n"); mprintf("Modern Query Packets: %7d (avg%5d/min)\n", NumPktQ, NumPktQ * mul / div); mprintf("Legacy Query Packets: %7d (avg%5d/min)\n", NumPktL, NumPktL * mul / div); @@ -1081,6 +1088,7 @@ mDNSlocal mStatus mDNSNetMonitor(void) mDNSexport int main(int argc, char **argv) { + const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0]; int i; mStatus status; @@ -1115,12 +1123,12 @@ mDNSexport int main(int argc, char **argv) } status = mDNSNetMonitor(); - if (status) { fprintf(stderr, "%s: mDNSNetMonitor failed %ld\n", argv[0], status); return(status); } + if (status) { fprintf(stderr, "%s: mDNSNetMonitor failed %ld\n", progname, status); return(status); } return(0); usage: fprintf(stderr, "\nmDNS traffic monitor\n"); - fprintf(stderr, "Usage: %s ()\n", argv[0]); + fprintf(stderr, "Usage: %s ()\n", progname); fprintf(stderr, "Optional parameter displays only packets from that host\n"); fprintf(stderr, "\nPer-packet header output:\n"); diff --git a/mDNSPosix/PosixDaemon.c b/mDNSPosix/PosixDaemon.c index 6e943de..fcb3990 100644 --- a/mDNSPosix/PosixDaemon.c +++ b/mDNSPosix/PosixDaemon.c @@ -1,10 +1,9 @@ -/* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -22,17 +21,55 @@ * * @APPLE_LICENSE_HEADER_END@ - File: daemon.c + File: daemon.c - Contains: main & associated Application layer for mDNSResponder on Linux. + Contains: main & associated Application layer for mDNSResponder on Linux. - Version: 1.0 - Tabs: 4 spaces - - Change History (most recent first): + Change History (most recent first): $Log: PosixDaemon.c,v $ -Revision 1.10 2004/06/08 04:59:40 cheshire +Revision 1.22 2004/12/10 13:12:08 cheshire +Create no-op function RecordUpdatedNiceLabel(), required by uds_daemon.c + +Revision 1.21 2004/12/01 20:57:20 ksekar + Wide Area Rendezvous must be split-DNS aware + +Revision 1.20 2004/12/01 04:28:43 cheshire + Darwin patches for Solaris and Suse +Use version of daemon() provided in mDNSUNP.c instead of local copy + +Revision 1.19 2004/12/01 03:30:29 cheshire + Add Unicast DNS support to mDNSPosix + +Revision 1.18 2004/11/30 22:45:59 cheshire +Minor code tidying + +Revision 1.17 2004/11/30 22:18:59 cheshire + Posix needs to read the list of unicast DNS servers and set server list + +Revision 1.16 2004/09/21 21:05:12 cheshire +Move duplicate code out of mDNSMacOSX/daemon.c and mDNSPosix/PosixDaemon.c, +into mDNSShared/uds_daemon.c + +Revision 1.15 2004/09/17 01:08:53 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.14 2004/09/16 00:24:49 cheshire + Fix unsafe use of mDNSPlatformTimeNow() + +Revision 1.13 2004/08/11 01:59:41 cheshire +Remove "mDNS *globalInstance" parameter from udsserver_init() + +Revision 1.12 2004/06/28 23:19:19 cheshire +Fix "Daemon_Init declared but never defined" warning on Linux + +Revision 1.11 2004/06/25 00:26:27 rpantos +Changes to fix the Posix build on Solaris. + +Revision 1.10 2004/06/08 04:59:40 cheshire Tidy up wording -- log messages are already prefixed with "mDNSResponder", so don't need to repeat it Revision 1.9 2004/05/29 00:14:20 rpantos @@ -61,7 +98,6 @@ Clean up mDNSPosix so that it builds on OS X again. Revision 1.1 2003/12/08 20:47:02 rpantos Add support for mDNSResponder on Linux. - */ #include @@ -70,218 +106,221 @@ Add support for mDNSResponder on Linux. #include #include #include +#include #include #include +#include +#include -#include "mDNSClientAPI.h" +#include "mDNSEmbeddedAPI.h" #include "mDNSDebug.h" #include "mDNSPosix.h" #include "uds_daemon.h" +#include "PlatformCommon.h" +#define uDNS_SERVERS_FILE "/etc/resolv.conf" -static void ParseCmdLinArgs( int argc, char **argv); -static void DumpStateLog( mDNS *m); -static mStatus MainLoop( mDNS *m); - +#define CONFIG_FILE "/etc/mdnsd.conf" +static domainname DynDNSZone; // Default wide-area zone for service registration +static domainname DynDNSHostname; #define RR_CACHE_SIZE 500 static CacheRecord gRRCache[RR_CACHE_SIZE]; extern const char mDNSResponderVersionString[]; -int main( int argc, char **argv) -{ - mDNS mDNSRecord; - mDNS_PlatformSupport platformStorage; - mStatus err; - - bzero( &mDNSRecord, sizeof mDNSRecord); - bzero( &platformStorage, sizeof platformStorage); - - ParseCmdLinArgs( argc, argv); - - err = mDNS_Init( &mDNSRecord, &platformStorage, gRRCache, RR_CACHE_SIZE, mDNS_Init_AdvertiseLocalAddresses, - mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); - - if ( mStatus_NoError == err) - err = udsserver_init( &mDNSRecord); - - // Now that we're finished with anything privileged, switch over to running as "nobody" - if ( mStatus_NoError == err) +static int ParseDNSServers(mDNS *m, const char *filePath) { - const struct passwd *pw = getpwnam("nobody"); - if ( pw != NULL) - setuid( pw->pw_uid); - else - LogMsg("WARNING: mdnsd continuing as root because user \"nobody\" does not exist"); + char line[256]; + char nameserver[16]; + char keyword[10]; + int numOfServers = 0; + FILE *fp = fopen(filePath, "r"); + if (fp == NULL) return -1; + while (fgets(line,sizeof(line),fp)) + { + struct in_addr ina; + line[255]='\0'; // just to be safe + if (sscanf(line,"%10s %15s", keyword, nameserver) != 2) continue; // it will skip whitespaces + if (strncmp(keyword,"nameserver",10)) continue; + if (inet_aton(nameserver, (struct in_addr *)&ina) != 0) + { + mDNSAddr DNSAddr; + DNSAddr.type = mDNSAddrType_IPv4; + DNSAddr.ip.v4.NotAnInteger = ina.s_addr; + mDNS_AddDNSServer(m, &DNSAddr, NULL); + numOfServers++; + } + } + return (numOfServers > 0) ? 0 : -1; } - if ( mStatus_NoError == err) - err = MainLoop( &mDNSRecord); - - mDNS_Close( &mDNSRecord); - - if (udsserver_exit() < 0) - LogMsg("ExitCallback: udsserver_exit failed"); - - #if MDNS_DEBUGMSGS > 0 - printf( "mDNSResponder exiting normally with %ld\n", err); - #endif - - return err; -} - - -static void ParseCmdLinArgs( int argc, char **argv) -// Do appropriate things at startup with command line arguments. Calls exit() if unhappy. -{ - if ( argc > 1) +static void Reconfigure(mDNS *m) { - if ( 0 == strcmp( argv[1], "-debug")) - { - mDNS_DebugMode = mDNStrue; - } - else - printf( "Usage: mDNSResponder [-debug]\n"); + mDNSAddr DynDNSIP; + mDNS_SetPrimaryInterfaceInfo(m, NULL, NULL); + mDNS_DeleteDNSServers(m); + if (ParseDNSServers(m, uDNS_SERVERS_FILE) < 0) + LogMsg("Unable to parse DNS server list. Unicast DNS-SD unavailable"); + ReadDDNSSettingsFromConfFile(m, CONFIG_FILE, &DynDNSHostname, &DynDNSZone); + FindDefaultRouteIP(&DynDNSIP); + if (DynDNSHostname.c[0]) mDNS_AddDynDNSHostName(m, &DynDNSHostname, NULL, NULL); + if (DynDNSIP.type) mDNS_SetPrimaryInterfaceInfo(m, &DynDNSIP, NULL); } - if ( !mDNS_DebugMode) +// Do appropriate things at startup with command line arguments. Calls exit() if unhappy. +static void ParseCmdLinArgs(int argc, char **argv) { - int result = daemon( 0, 0); - - if ( result != 0) + if (argc > 1) { - LogMsg("Could not run as daemon - exiting"); - exit( result); + if (0 == strcmp(argv[1], "-debug")) mDNS_DebugMode = mDNStrue; + else printf("Usage: mDNSResponder [-debug]\n"); } -#if __APPLE__ + if (!mDNS_DebugMode) { - LogMsg("The POSIX mDNSResponder should only be used on OS X for testing - exiting"); - exit( -1); - } + int result = daemon(0, 0); + if (result != 0) { LogMsg("Could not run as daemon - exiting"); exit(result); } +#if __APPLE__ + LogMsg("The POSIX mDNSResponder should only be used on OS X for testing - exiting"); + exit(-1); #endif + } } -} - -static void DumpStateLog( mDNS *m) +static void DumpStateLog(mDNS *const m) // Dump a little log of what we've been up to. -{ - mDNSu32 slot; - CacheRecord *rr; - mDNSu32 CacheUsed = 0, CacheActive = 0; - mDNSs32 now = mDNSPlatformTimeNow(); - + { LogMsgIdent(mDNSResponderVersionString, "---- BEGIN STATE LOG ----"); - - for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) - { - mDNSu32 SlotUsed = 0; - for (rr = m->rrcache_hash[slot]; rr; rr=rr->next) - { - mDNSs32 remain = rr->resrec.rroriginalttl - (now - rr->TimeRcvd) / mDNSPlatformOneSecond; - CacheUsed++; - SlotUsed++; - if (rr->CRActiveQuestion) CacheActive++; - LogMsgNoIdent("%s%6ld %-6s%-6s%s", rr->CRActiveQuestion ? "*" : " ", remain, DNSTypeName(rr->resrec.rrtype), - ((PosixNetworkInterface *)rr->resrec.InterfaceID)->intfName, GetRRDisplayString(m, rr)); - usleep(1000); // Limit rate a little so we don't flood syslog too fast - } - if (m->rrcache_used[slot] != SlotUsed) - LogMsgNoIdent("Cache use mismatch: rrcache_used[slot] is %lu, true count %lu", m->rrcache_used[slot], SlotUsed); - } - if (m->rrcache_totalused != CacheUsed) - LogMsgNoIdent("Cache use mismatch: rrcache_totalused is %lu, true count %lu", m->rrcache_totalused, CacheUsed); - if (m->rrcache_active != CacheActive) - LogMsgNoIdent("Cache use mismatch: rrcache_active is %lu, true count %lu", m->rrcache_active, CacheActive); - LogMsgNoIdent("Cache currently contains %lu records; %lu referenced by active questions", CacheUsed, CacheActive); - - udsserver_info(); - + udsserver_info(m); LogMsgIdent(mDNSResponderVersionString, "---- END STATE LOG ----"); -} + } -static mStatus MainLoop( mDNS *m) -// Loop until we quit. -{ +static mStatus MainLoop(mDNS *m) // Loop until we quit. + { sigset_t signals; mDNSBool gotData = mDNSfalse; - mDNSPosixListenForSignalInEventLoop( SIGINT); - mDNSPosixListenForSignalInEventLoop( SIGTERM); - mDNSPosixListenForSignalInEventLoop( SIGUSR1); - mDNSPosixListenForSignalInEventLoop( SIGPIPE); + mDNSPosixListenForSignalInEventLoop(SIGINT); + mDNSPosixListenForSignalInEventLoop(SIGTERM); + mDNSPosixListenForSignalInEventLoop(SIGUSR1); + mDNSPosixListenForSignalInEventLoop(SIGPIPE); + mDNSPosixListenForSignalInEventLoop(SIGHUP) ; - for ( ; ;) - { + for (; ;) + { // Work out how long we expect to sleep before the next scheduled task struct timeval timeout; mDNSs32 ticks; // Only idle if we didn't find any data the last time around - if ( !gotData) - { + if (!gotData) + { mDNSs32 nextTimerEvent = mDNS_Execute(m); - - nextTimerEvent = udsserver_idle( nextTimerEvent); - - ticks = nextTimerEvent - mDNSPlatformTimeNow(); + nextTimerEvent = udsserver_idle(nextTimerEvent); + ticks = nextTimerEvent - mDNS_TimeNow(m); if (ticks < 1) ticks = 1; - } + } else // otherwise call EventLoop again with 0 timemout ticks = 0; timeout.tv_sec = ticks / mDNSPlatformOneSecond; timeout.tv_usec = (ticks % mDNSPlatformOneSecond) * 1000000 / mDNSPlatformOneSecond; - (void) mDNSPosixRunEventLoopOnce( m, &timeout, &signals, &gotData); + (void) mDNSPosixRunEventLoopOnce(m, &timeout, &signals, &gotData); - if ( sigismember( &signals, SIGUSR1)) - DumpStateLog( m); - if ( sigismember( &signals, SIGPIPE)) // happens when we try to write to a dead client; death should be detected soon in request_callback() and cleaned up. - LogMsg("Received SIGPIPE - ignoring"); - if ( sigismember( &signals, SIGINT) || sigismember( &signals, SIGTERM)) - break; + if (sigismember(&signals, SIGHUP )) Reconfigure(m); + if (sigismember(&signals, SIGUSR1)) DumpStateLog(m); + // SIGPIPE happens when we try to write to a dead client; death should be detected soon in request_callback() and cleaned up. + if (sigismember(&signals, SIGPIPE)) LogMsg("Received SIGPIPE - ignoring"); + if (sigismember(&signals, SIGINT) || sigismember(&signals, SIGTERM)) break; + } + return EINTR; } - return EINTR; -} +int main(int argc, char **argv) + { + #define mDNSRecord mDNSStorage + mDNS_PlatformSupport platformStorage; + mStatus err; + + bzero(&mDNSRecord, sizeof mDNSRecord); + bzero(&platformStorage, sizeof platformStorage); + + ParseCmdLinArgs(argc, argv); + + err = mDNS_Init(&mDNSRecord, &platformStorage, gRRCache, RR_CACHE_SIZE, mDNS_Init_AdvertiseLocalAddresses, + mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); + + if (mStatus_NoError == err) + err = udsserver_init(); + + Reconfigure(&mDNSRecord); + + // Now that we're finished with anything privileged, switch over to running as "nobody" + if (mStatus_NoError == err) + { + const struct passwd *pw = getpwnam("nobody"); + if (pw != NULL) + setuid(pw->pw_uid); + else + LogMsg("WARNING: mdnsd continuing as root because user \"nobody\" does not exist"); + } + + if (mStatus_NoError == err) + err = MainLoop(&mDNSRecord); + + mDNS_Close(&mDNSRecord); + if (udsserver_exit() < 0) + LogMsg("ExitCallback: udsserver_exit failed"); + + #if MDNS_DEBUGMSGS > 0 + printf("mDNSResponder exiting normally with %ld\n", err); + #endif + + return err; + } // uds_daemon support //////////////////////////////////////////////////////////// #if MDNS_MALLOC_DEBUGGING >= 2 #define LogMalloc LogMsg #else -#define LogMalloc(ARGS...) ((void)0) +#define LogMalloc(ARGS...) ((void)0) #endif - -mStatus udsSupportAddFDToEventLoop( int fd, udsEventCallback callback, void *context) +mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *context) /* Support routine for uds_daemon.c */ -{ + { // Depends on the fact that udsEventCallback == mDNSPosixEventCallback - return mDNSPosixAddFDToEventLoop( fd, callback, context); -} + return mDNSPosixAddFDToEventLoop(fd, callback, context); + } -mStatus udsSupportRemoveFDFromEventLoop( int fd) -{ - return mDNSPosixRemoveFDFromEventLoop( fd); -} +mStatus udsSupportRemoveFDFromEventLoop(int fd) + { + return mDNSPosixRemoveFDFromEventLoop(fd); + } + +mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay) + { + (void)m; + (void)delay; + // No-op, for now + } #if MACOSX_MDNS_MALLOC_DEBUGGING >= 1 void *mallocL(char *msg, unsigned int size) -{ + { unsigned long *mem = malloc(size+8); if (!mem) - { + { LogMsg("malloc( %s : %d ) failed", msg, size); return(NULL); - } + } else - { + { LogMalloc("malloc( %s : %lu ) = %p", msg, size, &mem[2]); mem[0] = 0xDEAD1234; mem[1] = size; @@ -289,15 +328,15 @@ void *mallocL(char *msg, unsigned int size) memset(&mem[2], 0xFF, size); // validatelists(&mDNSStorage); return(&mem[2]); + } } -} void freeL(char *msg, void *x) -{ + { if (!x) LogMsg("free( %s @ NULL )!", msg); else - { + { unsigned long *mem = ((unsigned long *)x) - 2; if (mem[0] != 0xDEAD1234) { LogMsg("free( %s @ %p ) !!!! NOT ALLOCATED !!!!", msg, &mem[2]); return; } @@ -308,13 +347,11 @@ void freeL(char *msg, void *x) memset(mem, 0xDD, mem[1]+8); // validatelists(&mDNSStorage); free(mem); + } } -} #endif // MACOSX_MDNS_MALLOC_DEBUGGING >= 1 - - // For convenience when using the "strings" command, this is the last thing in the file #if mDNSResponderVersion > 1 mDNSexport const char mDNSResponderVersionString[] = "mDNSResponder-" STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ") "; diff --git a/mDNSPosix/ProxyResponder.c b/mDNSPosix/ProxyResponder.c index 5030d93..bca7356 100644 --- a/mDNSPosix/ProxyResponder.c +++ b/mDNSPosix/ProxyResponder.c @@ -1,10 +1,9 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -25,6 +24,31 @@ Change History (most recent first): $Log: ProxyResponder.c,v $ +Revision 1.34 2004/12/01 04:27:28 cheshire + Darwin patches for Solaris and Suse +Don't use uint32_t, etc. -- they require stdint.h, which doesn't exist on FreeBSD 4.x, Solaris, etc. + +Revision 1.33 2004/11/30 22:37:01 cheshire +Update copyright dates and add "Mode: C; tab-width: 4" headers + +Revision 1.32 2004/10/26 03:59:41 cheshire +Update comments + +Revision 1.31 2004/09/17 01:08:53 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.30 2004/09/17 00:31:52 cheshire +For consistency with ipv6, renamed rdata field 'ip' to 'ipv4' + +Revision 1.29 2004/09/16 01:58:22 cheshire +Fix compiler warnings + +Revision 1.28 2004/06/25 00:26:27 rpantos +Changes to fix the Posix build on Solaris. + Revision 1.27 2004/03/12 08:03:14 cheshire Update comments @@ -75,27 +99,32 @@ Revision 1.13 2003/04/18 22:46:12 cheshire Fix mistake in 1.8 -- INADDR_NONE is 0xFFFFFFFF, not 0 Revision 1.12 2003/04/16 02:11:07 cheshire -Fixed mDNS_RegisterNoSuchService non-existance function so that it works again +Fixed mDNS_RegisterNoSuchService non-existence function so that it works again Revision 1.11 2003/03/31 22:49:35 cheshire Add "$Log" header */ -#include // For printf() -#include // For exit() etc. -#include // For strlen() etc. -#include // For select() -#include // For SIGINT, SIGTERM -#include // For errno, EINTR -#include // For inet_addr() -#include // For INADDR_NONE -#include // For gethostbyname() - -#include "mDNSClientAPI.h" // Defines the interface to the client layer above -#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform +#include // For printf() +#include // For exit() etc. +#include // For strlen() etc. +#include // For select() +#include // For SIGINT, SIGTERM +#include // For errno, EINTR +#include // For inet_addr() +#include // For INADDR_NONE +#include // For gethostbyname() + +#include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above +#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform #include "ExampleClientApp.h" +// Compatibility workaround: Solaris 2.5 has no INADDR_NONE +#ifndef INADDR_NONE +#define INADDR_NONE (mDNSu32)0xffffffff +#endif + //************************************************************************************************************* // Globals static mDNS mDNSStorage; // mDNS core uses this to store its globals @@ -116,10 +145,10 @@ mDNSlocal void HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus res { ProxyHost *f = (ProxyHost*)rr->RecordContext; if (result == mStatus_NoError) - debugf("Host name successfully registered: %##s", &rr->resrec.name); + debugf("Host name successfully registered: %##s", rr->resrec.name.c); else { - debugf("Host name conflict for %##s", &rr->resrec.name); + debugf("Host name conflict for %##s", rr->resrec.name.c); mDNS_Deregister(m, &f->RR_A); mDNS_Deregister(m, &f->RR_PTR); exit(-1); @@ -140,13 +169,13 @@ mDNSlocal mStatus mDNS_RegisterProxyHost(mDNS *m, ProxyHost *p) mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", p->ip.b[3], p->ip.b[2], p->ip.b[1], p->ip.b[0]); MakeDomainNameFromDNSNameString(&p->RR_PTR.resrec.name, buffer); - p->RR_A. resrec.rdata->u.ip = p->ip; + p->RR_A. resrec.rdata->u.ipv4 = p->ip; p->RR_PTR.resrec.rdata->u.name = p->RR_A.resrec.name; mDNS_Register(m, &p->RR_A); mDNS_Register(m, &p->RR_PTR); - debugf("Made Proxy Host Records for %##s", &p->RR_A.resrec.name); + debugf("Made Proxy Host Records for %##s", p->RR_A.resrec.name.c); return(mStatus_NoError); } @@ -162,10 +191,10 @@ mDNSlocal void ServiceCallback(mDNS *const m, ServiceRecordSet *const sr, mStatu { switch (result) { - case mStatus_NoError: debugf("Callback: %##s Name Registered", &sr->RR_SRV.resrec.name); break; - case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", &sr->RR_SRV.resrec.name); break; - case mStatus_MemFree: debugf("Callback: %##s Memory Free", &sr->RR_SRV.resrec.name); break; - default: debugf("Callback: %##s Unknown Result %d", &sr->RR_SRV.resrec.name, result); break; + case mStatus_NoError: debugf("Callback: %##s Name Registered", sr->RR_SRV.resrec.name.c); break; + case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", sr->RR_SRV.resrec.name.c); break; + case mStatus_MemFree: debugf("Callback: %##s Memory Free", sr->RR_SRV.resrec.name.c); break; + default: debugf("Callback: %##s Unknown Result %ld", sr->RR_SRV.resrec.name.c, result); break; } if (result == mStatus_NoError) @@ -235,10 +264,10 @@ mDNSlocal void NoSuchServiceCallback(mDNS *const m, AuthRecord *const rr, mStatu domainname *proxyhostname = (domainname *)rr->RecordContext; switch (result) { - case mStatus_NoError: debugf("Callback: %##s Name Registered", &rr->resrec.name); break; - case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", &rr->resrec.name); break; - case mStatus_MemFree: debugf("Callback: %##s Memory Free", &rr->resrec.name); break; - default: debugf("Callback: %##s Unknown Result %d", &rr->resrec.name, result); break; + case mStatus_NoError: debugf("Callback: %##s Name Registered", rr->resrec.name.c); break; + case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", rr->resrec.name.c); break; + case mStatus_MemFree: debugf("Callback: %##s Memory Free", rr->resrec.name.c); break; + default: debugf("Callback: %##s Unknown Result %ld", rr->resrec.name.c, result); break; } if (result == mStatus_NoError) diff --git a/mDNSPosix/ReadMe.txt b/mDNSPosix/ReadMe.txt index 06df6e7..d128c8d 100755 --- a/mDNSPosix/ReadMe.txt +++ b/mDNSPosix/ReadMe.txt @@ -20,8 +20,8 @@ zeroconf technologies. This sample is designed to show how easy it is to make a device "Rendezvous compatible". The code in this sample was compiled and tested on Mac OS X (10.1.x, -10.2), Solaris (SunOS 5.6), Linux (Redhat 2.4.9-21), and OpenBSD (2.9). -YMMV. +10.2, 10.3), Solaris (SunOS 5.8), Linux (Redhat 2.4.9-21, Fedora Core 1), +and OpenBSD (2.9). YMMV. Packing List @@ -72,6 +72,7 @@ When you compile, you will get: o Main products for general-purpose use (e.g. on a desktop computer): - mdnsd - libmdns + - nss_mdns (See nss_ReadMe.txt for important information about nss_mdns) o Standalone products for dedicated devices (printer, network camera, etc.) - mDNSClientPosix @@ -82,14 +83,20 @@ o Debugging tools - mDNSNetMonitor - mDNSIdentify -Type "sudo make install" to install four things: -o mdnsd (usually in /usr/sbin) -o libmdns (usually in /usr/lib) -o dns_sd.h (usually in /usr/include) -o startup scripts (e.g. in /etc/rc.d) - -Once you've installed the header and the library, and started the -daemon running, you can cd to the "Clients" folder and type "make". +As root type "make install" to install six things: +o mdnsd (usually in /usr/sbin) +o libmdns (usually in /usr/lib) +o dns_sd.h (usually in /usr/include) +o startup scripts (e.g. in /etc/rc.d) +o manual pages (usually in /usr/share/man) +o nss_mdns (usually in /lib) +o nss configuration files (usually in /etc) + +Once you've installed the files in their respective places, +you need to start the daemon running, either by rebooting, +or by running the startup script "/etc/init.d/mdns start" +(the exact path may be different on your system). +Then you can cd to the "Clients" folder and type "make". This builds a test client showing how to exercise all the major functionality of the daemon. @@ -117,6 +124,11 @@ Client applications link with the libmdns, which implements the functions defined in the dns_sd.h header file, and implements the IPC protocol used to communicate over the Unix Domain Socket interface to the daemon. +Note that, strictly speaking, nss_mdns could be just another client of +mdnsd, linking with libmdns just like any other client. However, because +of its central role in the normal operation of multicast DNS, it is built +and installed along with the other essential system support components. + Clients for Embedded Systems ---------------------------- diff --git a/mDNSPosix/Responder.c b/mDNSPosix/Responder.c index a8c297a..93b0c81 100755 --- a/mDNSPosix/Responder.c +++ b/mDNSPosix/Responder.c @@ -1,10 +1,9 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -25,6 +24,31 @@ Change History (most recent first): $Log: Responder.c,v $ +Revision 1.27 2004/12/01 04:28:43 cheshire + Darwin patches for Solaris and Suse +Use version of daemon() provided in mDNSUNP.c instead of local copy + +Revision 1.26 2004/11/30 22:37:01 cheshire +Update copyright dates and add "Mode: C; tab-width: 4" headers + +Revision 1.25 2004/11/11 02:00:51 cheshire +Minor fixes to getopt, error message + +Revision 1.24 2004/11/09 19:32:10 rpantos +Suggestion from Ademar de Souza Reis Jr. to allow comments in services file + +Revision 1.23 2004/09/17 01:08:54 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.22 2004/09/16 01:58:22 cheshire +Fix compiler warnings + +Revision 1.21 2004/06/15 03:48:07 cheshire +Update mDNSResponderPosix to take multiple name=val arguments in a sane way + Revision 1.20 2004/05/18 23:51:26 cheshire Tidy up all checkin comments to use consistent "" format for bug numbers @@ -89,7 +113,7 @@ First checkin */ -#include "mDNSClientAPI.h"// Defines the interface to the client layer above +#include "mDNSEmbeddedAPI.h"// Defines the interface to the client layer above #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform #include @@ -250,91 +274,6 @@ static mDNSBool CheckThatServiceTypeIsUsable(const char *serviceType, mDNSBool p return result; } -static mDNSBool CheckThatServiceTextIsUsable(const char *serviceText, mDNSBool printExplanation, - mDNSu8 *pStringList, mDNSu16 *pStringListLen) - // Checks that serviceText is a reasonable service text record - // and, if it isn't and printExplanation is true, prints - // an explanation of why not. Also parse the text into - // the packed PString buffer denoted by pStringList and - // return the length of that buffer in *pStringListLen. - // Note that this routine assumes that the buffer is - // sizeof(RDataBody) bytes long. -{ - mDNSBool result; - size_t serviceTextLen; - - // Note that parsing a C string into a PString list always - // expands the data by one character, so the following - // compare is ">=", not ">". Here's the logic: - // - // #1 For a string with not ^A's, the PString length is one - // greater than the C string length because we add a length - // byte. - // #2 For every regular (not ^A) character you add to the C - // string, you add a regular character to the PString list. - // This does not affect the equivalence stated in #1. - // #3 For every ^A you add to the C string, you add a length - // byte to the PString list but you also eliminate the ^A, - // which again does not affect the equivalence stated in #1. - - result = mDNStrue; - serviceTextLen = strlen(serviceText); - if (result && strlen(serviceText) >= sizeof(RDataBody)) { - if (printExplanation) { - fprintf(stderr, - "%s: Service text record is too long (must be less than %d characters)\n", - gProgramName, - (int) sizeof(RDataBody) ); - } - result = mDNSfalse; - } - - // Now break the string up into PStrings delimited by ^A. - // We know the data will fit so we can ignore buffer overrun concerns. - // However, we still have to treat runs long than 255 characters as - // an error. - - if (result) { - int lastPStringOffset; - int i; - int thisPStringLen; - - // This algorithm is a little tricky. We start by copying - // the string directly into the output buffer, shifted up by - // one byte. We then fill in the first byte with a ^A. - // We then walk backwards through the buffer and, for each - // ^A that we find, we replace it with the difference between - // its offset and the offset of the last ^A that we found - // (ie lastPStringOffset). - - memcpy(&pStringList[1], serviceText, serviceTextLen); - pStringList[0] = 1; - lastPStringOffset = serviceTextLen + 1; - for (i = serviceTextLen; i >= 0; i--) { - if ( pStringList[i] == 1 ) { - thisPStringLen = (lastPStringOffset - i - 1); - assert(thisPStringLen >= 0); - if (thisPStringLen > 255) { - result = mDNSfalse; - if (printExplanation) { - fprintf(stderr, - "%s: Each component of the service text record must be 255 characters or less\n", - gProgramName); - } - break; - } else { - pStringList[i] = thisPStringLen; - lastPStringOffset = i; - } - } - } - - *pStringListLen = serviceTextLen + 1; - } - - return result; -} - static mDNSBool CheckThatPortNumberIsUsable(long portNumber, mDNSBool printExplanation) // Checks that portNumber is a reasonable port number // and, if it isn't and printExplanation is true, prints @@ -368,7 +307,7 @@ enum { static void PrintUsage() { fprintf(stderr, - "Usage: %s [-v level ] [-r] [-n name] [-t type] [-d domain] [-x TXT] [-p port] [-f file] [-b] [-P pidfile]\n", + "Usage: %s [-v level ] [-r] [-n name] [-t type] [-d domain] [-p port] [-f file] [-b] [-P pidfile] [-x name=val ...]\n", gProgramName); fprintf(stderr, " -v verbose mode, level is a number from 0 to 2\n"); fprintf(stderr, " 0 = no debugging info (default)\n"); @@ -379,13 +318,15 @@ static void PrintUsage() fprintf(stderr, " -n uses 'name' as the host name (default is none)\n"); fprintf(stderr, " -t uses 'type' as the service type (default is '%s')\n", kDefaultServiceType); fprintf(stderr, " -d uses 'domain' as the service domain (default is '%s')\n", kDefaultServiceDomain); - fprintf(stderr, " -x uses 'TXT' as the service TXT record (default is empty)\n"); fprintf(stderr, " -p uses 'port' as the port number (default is '%d')\n", kDefaultPortNumber); fprintf(stderr, " -f reads a service list from 'file'\n"); fprintf(stderr, " -b forces daemon (background) mode\n"); fprintf(stderr, " -P uses 'pidfile' as the PID file\n"); fprintf(stderr, " (default is '%s')\n", kDefaultPIDFile); fprintf(stderr, " only meaningful if -b also specified\n"); + fprintf(stderr, " -x stores name=val in TXT record (default is empty).\n"); + fprintf(stderr, " MUST be the last command-line argument;\n"); + fprintf(stderr, " all subsequent arguments after -x are treated as name=val pairs.\n"); } static mDNSBool gAvoidPort53 = mDNStrue; @@ -417,7 +358,7 @@ static void ParseArguments(int argc, char **argv) // Parse command line options using getopt. do { - ch = getopt(argc, argv, "v:rn:t:d:x:p:f:dPb"); + ch = getopt(argc, argv, "v:rn:t:d:p:f:dP:bx"); if (ch != -1) { switch (ch) { case 'v': @@ -447,11 +388,6 @@ static void ParseArguments(int argc, char **argv) case 'd': gServiceDomain = optarg; break; - case 'x': - if ( ! CheckThatServiceTextIsUsable(optarg, mDNStrue, gServiceText, &gServiceTextLen) ) { - exit(1); - } - break; case 'p': gPortNumber = atol(optarg); if ( ! CheckThatPortNumberIsUsable(gPortNumber, mDNStrue) ) { @@ -467,6 +403,16 @@ static void ParseArguments(int argc, char **argv) case 'P': gPIDFile = optarg; break; + case 'x': + while (optind < argc) + { + gServiceText[gServiceTextLen] = strlen(argv[optind]); + memcpy(gServiceText+gServiceTextLen+1, argv[optind], gServiceText[gServiceTextLen]); + gServiceTextLen += 1 + gServiceText[gServiceTextLen]; + optind++; + } + ch = -1; + break; case '?': default: PrintUsage(); @@ -559,7 +505,7 @@ static void RegistrationCallback(mDNS *const m, ServiceRecordSet *const thisRegi break; default: - debugf("Callback: %##s Unknown Status %d", thisRegistration->RR_SRV.resrec.name.c, status); + debugf("Callback: %##s Unknown Status %ld", thisRegistration->RR_SRV.resrec.name.c, status); break; } } @@ -621,91 +567,88 @@ static mStatus RegisterOneService(const char * richTextHostName, } static mDNSBool ReadALine(char *buf, size_t bufSize, FILE *fp) +// Read a line, skipping over any blank lines or lines starting with '#' +{ + mDNSBool good, skip; + do { + good = (fgets(buf, bufSize, fp) != NULL); + skip = (good && (buf[0] == '#' || buf[0] == '\r' || buf[0] == '\n')); + } while (good && skip); + if (good) { - mDNSBool good = (fgets(buf, bufSize, fp) != NULL); - if (good) - { - size_t len = strlen(buf); - good = (len > 0 && buf[len - 1] == '\n'); - if (good) buf[len - 1] = 0; - } - return good; + int len = strlen( buf); + if ( buf[len - 1] == '\r' || buf[len - 1] == '\n') + buf[len - 1] = '\0'; } + return good; +} static mStatus RegisterServicesInFile(const char *filePath) { - mStatus status; - FILE * fp; + mStatus status = mStatus_NoError; + FILE * fp = fopen(filePath, "r"); int junk; - mDNSBool good; - int ch; - char name[256]; - char type[256]; - const char *dom = kDefaultServiceDomain; - char rawText[1024]; - mDNSu8 text[sizeof(RDataBody)]; - mDNSu16 textLen; - char port[256]; - status = mStatus_NoError; - fp = fopen(filePath, "r"); if (fp == NULL) { status = mStatus_UnknownErr; } if (status == mStatus_NoError) { - good = mDNStrue; + mDNSBool good = mDNStrue; do { - // Skip over any blank lines. - do { - ch = fgetc(fp); - } while ( ch == '\n' || ch == '\r' ); - if (ch != EOF) { - good = (ungetc(ch, fp) == ch); - } + char name[256]; + char type[256]; + const char *dom = kDefaultServiceDomain; + char rawText[1024]; + mDNSu8 text[sizeof(RDataBody)]; + mDNSu16 textLen = 0; + char port[256]; // Read three lines, check them for validity, and register the service. - if ( good && ! feof(fp) ) { - good = ReadALine(name, sizeof(name), fp); - if (good) { - good = ReadALine(type, sizeof(type), fp); - } - if (good) { - char *p = type; - while (*p && *p != ' ') p++; - if (*p) { - *p = 0; - dom = p+1; - } - } - if (good) { - good = ReadALine(rawText, sizeof(rawText), fp); - } - if (good) { - good = ReadALine(port, sizeof(port), fp); - } - if (good) { - good = CheckThatRichTextHostNameIsUsable(name, mDNSfalse) - && CheckThatServiceTypeIsUsable(type, mDNSfalse) - && CheckThatServiceTextIsUsable(rawText, mDNSfalse, text, &textLen) - && CheckThatPortNumberIsUsable(atol(port), mDNSfalse); - } - if (good) { - status = RegisterOneService(name, type, dom, text, textLen, atol(port)); - if (status != mStatus_NoError) { - fprintf(stderr, - "%s: Failed to register service, name = %s, type = %s, port = %s\n", - gProgramName, - name, - type, - port); - status = mStatus_NoError; // keep reading - } - } - } + good = ReadALine(name, sizeof(name), fp); + if (good) { + good = ReadALine(type, sizeof(type), fp); + } + if (good) { + char *p = type; + while (*p && *p != ' ') p++; + if (*p) { + *p = 0; + dom = p+1; + } + } + if (good) { + good = ReadALine(port, sizeof(port), fp); + } + if (good) { + good = CheckThatRichTextHostNameIsUsable(name, mDNSfalse) + && CheckThatServiceTypeIsUsable(type, mDNSfalse) + && CheckThatPortNumberIsUsable(atol(port), mDNSfalse); + } + if (good) { + while (1) { + if (!ReadALine(rawText, sizeof(rawText), fp)) break; + text[textLen] = strlen(rawText); + if (text[textLen] == 0) break; + memcpy(text + textLen + 1, rawText, text[textLen]); + textLen += 1 + text[textLen]; + } + } + if (good) { + status = RegisterOneService(name, type, dom, text, textLen, atol(port)); + if (status != mStatus_NoError) { + fprintf(stderr, + "%s: Failed to register service, name = %s, type = %s, port = %s\n", + gProgramName, + name, + type, + port); + status = mStatus_NoError; // keep reading + } + } } while (good && !feof(fp)); if ( ! good ) { - fprintf(stderr, "%s: Error reading service file %s\n", gProgramName, gServiceFile); + fprintf(stderr, "%s: Error reading service file %s\n", gProgramName, filePath); } } @@ -761,43 +704,6 @@ static void DeregisterOurServices(void) #pragma mark **** Main #endif -#ifdef NOT_HAVE_DAEMON - - // The version of Solaris that I tested on didn't have the daemon - // call. This implementation was basically stolen from the - // Mac OS X standard C library. - - static int daemon(int nochdir, int noclose) - { - int fd; - - switch (fork()) { - case -1: - return (-1); - case 0: - break; - default: - _exit(0); - } - - if (setsid() == -1) - return (-1); - - if (!nochdir) - (void)chdir("/"); - - if (!noclose && (fd = _open("/dev/null", O_RDWR, 0)) != -1) { - (void)dup2(fd, STDIN_FILENO); - (void)dup2(fd, STDOUT_FILENO); - (void)dup2(fd, STDERR_FILENO); - if (fd > 2) - (void)_close(fd); - } - return (0); - } - -#endif /* NOT_HAVE_DAEMON */ - int main(int argc, char **argv) { mStatus status; diff --git a/mDNSPosix/Services.txt b/mDNSPosix/Services.txt index a7b73da..f5870bb 100755 --- a/mDNSPosix/Services.txt +++ b/mDNSPosix/Services.txt @@ -1,14 +1,15 @@ Tweedlebug _afpovertcp._tcp. -name=val1 548 +name=val1 Tweedlebug2 _afpovertcp._tcp. local. -name=val2name2=anotherattribute 548 +name=val2 +name2=anotherattribute Tweedlebug3 -_afpovertcp._tcp. apple.com. -name=val3 +_afpovertcp._tcp. 548 +name=val3 diff --git a/mDNSPosix/dnsextd.c b/mDNSPosix/dnsextd.c new file mode 100644 index 0000000..41bc5e7 --- /dev/null +++ b/mDNSPosix/dnsextd.c @@ -0,0 +1,1983 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: dnsextd.c,v $ +Revision 1.23 2004/12/06 20:24:31 ksekar + dnsextd leaks sockets + +Revision 1.22 2004/12/03 20:20:29 ksekar + dnsextd: support delivery of large records via LLQ events + +Revision 1.21 2004/12/03 06:11:34 ksekar + clean up dnsextd arguments + +Revision 1.20 2004/12/01 04:27:28 cheshire + Darwin patches for Solaris and Suse +Don't use uint32_t, etc. -- they require stdint.h, which doesn't exist on FreeBSD 4.x, Solaris, etc. + +Revision 1.19 2004/12/01 01:16:29 cheshire +Solaris compatibility fixes + +Revision 1.18 2004/11/30 23:51:06 cheshire +Remove double semicolons + +Revision 1.17 2004/11/30 22:37:01 cheshire +Update copyright dates and add "Mode: C; tab-width: 4" headers + +Revision 1.16 2004/11/25 02:02:28 ksekar +Fixed verbose log message argument + +Revision 1.15 2004/11/19 02:35:02 ksekar + Wide Area Rendezvous Security: Add LLQ-ID to events + +Revision 1.14 2004/11/17 06:17:58 cheshire +Update comments to show correct SRV names: _dns-update._udp.. and _dns-llq._udp.. + +Revision 1.13 2004/11/13 02:22:36 ksekar + Refresh Acks from daemon malformatted + +Revision 1.12 2004/11/12 01:05:01 ksekar + dnsextd: daemon registers the SRV same record +twice at startup + +Revision 1.11 2004/11/12 01:03:31 ksekar + dnsextd: KnownAnswers (CacheRecords) leaked + +Revision 1.10 2004/11/12 00:35:28 ksekar + dnsextd: uninitialized pointer can cause crash + +Revision 1.9 2004/11/10 20:38:17 ksekar + dnsextd: allow a "fudge" in LLQ lease echo + +Revision 1.8 2004/11/01 17:48:14 cheshire +Changed SOA serial number back to signed. RFC 1035 may describe it as "unsigned", but +it's wrong. The SOA serial is a modular counter, as explained in "DNS & BIND", page +137. Since C doesn't have a modular type, we used signed, C's closest approximation. + +Revision 1.7 2004/10/30 00:06:58 ksekar + Support Long Lived Queries in DNS Extension daemon + +Revision 1.6 2004/09/17 01:08:54 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.5 2004/09/16 00:50:54 cheshire +Don't use MSG_WAITALL -- it returns "Invalid argument" on some Linux versions + +Revision 1.4 2004/09/14 23:27:48 cheshire +Fix compile errors + +Revision 1.3 2004/09/02 01:39:40 cheshire +For better readability, follow consistent convention that QR bit comes first, followed by OP bits + +Revision 1.2 2004/08/24 23:27:57 cheshire +Fixes for Linux compatibility: +Don't use strings.h +Don't assume SIGINFO +Don't try to set servaddr.sin_len on platforms that don't have sa_len + +Revision 1.1 2004/08/11 00:43:26 ksekar +: DNS Extension daemon for DNS Update Lease + +*/ + +#include "../mDNSCore/mDNSEmbeddedAPI.h" +#include "../mDNSCore/DNSCommon.h" +#include "../mDNSCore/mDNS.c" +//!!!KRS we #include mDNS.c for the various constants defined there - we should move these to DNSCommon.h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Compatibility workaround +#ifndef AF_LOCAL +#define AF_LOCAL AF_UNIX +#endif + +// +// Constants +// + +#define LOOPBACK "127.0.0.1" +#define NS_PORT 53 +#define DAEMON_PORT 5355 // default, may be overridden via command line argument +#define LISTENQ 128 // tcp connection backlog +#define RECV_BUFLEN 9000 +#define LEASETABLE_INIT_NBUCKETS 256 // initial hashtable size (doubles as table fills) +#define LLQ_TABLESIZE 1024 // !!!KRS make this dynamically growable +#define EXPIRATION_INTERVAL 300 // check for expired records every 5 minutes +#define SRV_TTL 7200 // TTL For _dns-update SRV records + +// LLQ Lease bounds (seconds) +#define LLQ_MIN_LEASE (15 * 60) +#define LLQ_MAX_LEASE (120 * 60) +#define LLQ_LEASE_FUDGE 60 + +// LLQ SOA poll interval (microseconds) +#define LLQ_MONITOR_ERR_INTERVAL (60 * 1000000) +#define LLQ_MONITOR_INTERVAL 250000 +#ifdef SIGINFO +#define INFO_SIGNAL SIGINFO +#else +#define INFO_SIGNAL SIGUSR1 +#endif + +#define SAME_INADDR(x,y) (*((mDNSu32 *)&x) == *((mDNSu32 *)&y)) +#define ZERO_LLQID(x) (!memcmp(x, "\x0\x0\x0\x0", 8)) + +// +// Data Structures +// Structs/fields that must be locked for thread safety are explicitly commented +// + +typedef struct + { + struct sockaddr_in src; + size_t len; + DNSMessage msg; + // Note: extra storage for oversized (TCP) messages goes here + } PktMsg; + +// lease table entry +typedef struct RRTableElem + { + struct RRTableElem *next; + struct sockaddr_in cli; // client's source address + long expire; // expiration time, in seconds since epoch + domainname zone; // from zone field of update message + CacheRecord rr; // last field in struct allows for allocation of oversized RRs + } RRTableElem; + +typedef enum + { + RequestReceived = 0, + ChallengeSent = 1, + Established = 2 + } LLQState; + +// llq table entry +typedef struct LLQEntry + { + struct LLQEntry *next; + struct sockaddr_in cli; // clien'ts source address + domainname qname; + mDNSu16 qtype; + mDNSu8 id[8]; + LLQState state; + mDNSu32 lease; // original lease, in seconds + mDNSs32 expire; // expiration, absolute, in seconds since epoch + CacheRecord *KnownAnswers;// !!!KRS this should be shared amongst identical questions + } LLQEntry; + +// daemon-wide information +typedef struct + { + // server variables - read only after initialization (no locking) + struct in_addr saddr; // server address + domainname zone; // zone being updated + int tcpsd; // listening TCP socket + int udpsd; // listening UDP socket + + // daemon variables - read only after initialization (no locking) + uDNS_AuthInfo *AuthInfo; // linked list of keys for signing deletion updates + mDNSIPPort port; // listening port + + // lease table variables (locked via mutex after initialization) + RRTableElem **table; // hashtable for records with leases + pthread_mutex_t tablelock; // mutex for lease table + mDNSs32 nbuckets; // buckets allocated + mDNSs32 nelems; // elements in table + + // LLQ table variables + LLQEntry *LLQTable[LLQ_TABLESIZE]; // !!!KRS change this and RRTable to use a common data structure + int LLQEventListenSock; // Unix domain socket pair - polling thread writes to ServPollSock, which wakes + int LLQServPollSock; // the main thread listening on EventListenSock, indicating that the zone has changed + } DaemonInfo; + +// args passed to UDP request handler thread as void* +typedef struct + { + PktMsg pkt; + struct sockaddr_in cliaddr; + DaemonInfo *d; + } UDPRequestArgs; + +// args passed to TCP request handler thread as void* +typedef struct + { + int sd; // socket connected to client + struct sockaddr_in cliaddr; + DaemonInfo *d; + } TCPRequestArgs; + +// +// Global Variables +// + +// booleans to determine runtime output +// read-only after initialization (no mutex protection) +static mDNSBool foreground = 0; +static mDNSBool verbose = 0; + +// globals set via signal handler (accessed exclusively by main select loop and signal handler) +static mDNSBool terminate = 0; +static mDNSBool dumptable = 0; + +// +// Logging Routines +// Log messages are delivered to syslog unless -f option specified +// + +// common message logging subroutine +mDNSlocal void PrintLog(const char *buffer) + { + if (foreground) + { + fprintf(stderr,"%s\n", buffer); + fflush(stderr); + } + else + { + openlog("dnsextd", LOG_CONS | LOG_PERROR, LOG_DAEMON); + syslog(LOG_ERR, "%s", buffer); + closelog(); + } + } + +// Verbose Logging (conditional on -v option) +mDNSlocal void VLog(const char *format, ...) + { + unsigned char buffer[512]; + va_list ptr; + + if (!verbose) return; + va_start(ptr,format); + buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; + va_end(ptr); + PrintLog(buffer); + } + +// Unconditional Logging +mDNSlocal void Log(const char *format, ...) + { + unsigned char buffer[512]; + va_list ptr; + + va_start(ptr,format); + buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; + va_end(ptr); + PrintLog(buffer); + } + +// Error Logging +// prints message "dnsextd : - " +// must be compiled w/ -D_REENTRANT for thread-safe errno usage +mDNSlocal void LogErr(const char *fn, const char *operation) + { + char buf[512]; + snprintf(buf, sizeof(buf), "%s: %s - %s", fn, operation, strerror(errno)); + PrintLog(buf); + } + +// +// Networking Utility Routines +// + +// Convert DNS Message Header from Network to Host byte order +mDNSlocal void HdrNToH(PktMsg *pkt) + { + // Read the integer parts which are in IETF byte-order (MSB first, LSB second) + mDNSu8 *ptr = (mDNSu8 *)&pkt->msg.h.numQuestions; + pkt->msg.h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); + pkt->msg.h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); + pkt->msg.h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); + pkt->msg.h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]); + } + +// Convert DNS Message Header from Host to Network byte order +mDNSlocal void HdrHToN(PktMsg *pkt) + { + mDNSu16 numQuestions = pkt->msg.h.numQuestions; + mDNSu16 numAnswers = pkt->msg.h.numAnswers; + mDNSu16 numAuthorities = pkt->msg.h.numAuthorities; + mDNSu16 numAdditionals = pkt->msg.h.numAdditionals; + mDNSu8 *ptr = (mDNSu8 *)&pkt->msg.h.numQuestions; + + // Put all the integer values in IETF byte-order (MSB first, LSB second) + *ptr++ = (mDNSu8)(numQuestions >> 8); + *ptr++ = (mDNSu8)(numQuestions & 0xFF); + *ptr++ = (mDNSu8)(numAnswers >> 8); + *ptr++ = (mDNSu8)(numAnswers & 0xFF); + *ptr++ = (mDNSu8)(numAuthorities >> 8); + *ptr++ = (mDNSu8)(numAuthorities & 0xFF); + *ptr++ = (mDNSu8)(numAdditionals >> 8); + *ptr++ = (mDNSu8)(numAdditionals & 0xFF); + } + +// create a socket connected to nameserver +// caller terminates connection via close() +mDNSlocal int ConnectToServer(DaemonInfo *d) + { + struct sockaddr_in servaddr; + int sd; + + bzero(&servaddr, sizeof(servaddr)); + if (d->saddr.s_addr) servaddr.sin_addr = d->saddr; + else inet_pton(AF_INET, LOOPBACK, &d->saddr); // use loopback if server not explicitly specified + servaddr.sin_port = htons(NS_PORT); + servaddr.sin_family = AF_INET; +#ifndef NOT_HAVE_SA_LEN + servaddr.sin_len = sizeof(servaddr); +#endif + sd = socket(AF_INET, SOCK_STREAM, 0); + if (sd < 0) { LogErr("ConnectToServer", "socket"); return -1; } + if (connect(sd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { LogErr("ConnectToServer", "connect"); return -1; } + return sd; + } + +// send an entire block of data over a connected socket, blocking if buffers are full +mDNSlocal int MySend(int sd, const void *msg, int len) + { + int n, nsent = 0; + + while (nsent < len) + { + n = send(sd, (char *)msg + nsent, len - nsent, 0); + if (n < 0) { LogErr("MySend", "send"); return -1; } + nsent += n; + } + return 0; + } + +// Transmit a DNS message, prefixed by its length, over TCP, blocking if necessary +mDNSlocal int SendTCPMsg(int sd, PktMsg *pkt) + { + // send the lenth, in network byte order + mDNSu16 len = htons((mDNSu16)pkt->len); + if (MySend(sd, &len, sizeof(len)) < 0) return -1; + + // send the message + return MySend(sd, &pkt->msg, pkt->len); + } + +// Receive len bytes, waiting until we have all of them. +// Returns number of bytes read (which should always be the number asked for). +static int my_recv(const int sd, void *const buf, const int len) + { + // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; + // use an explicit while() loop instead. + // Also, don't try to do '+=' arithmetic on the original "void *" pointer -- + // arithmetic on "void *" pointers is compiler-dependent. + int remaining = len; + char *ptr = (char *)buf; + while (remaining) + { + ssize_t num_read = recv(sd, ptr, remaining, 0); + if ((num_read == 0) || (num_read < 0) || (num_read > remaining)) return -1; + ptr += num_read; + remaining -= num_read; + } + return(len); + } + +// Return a DNS Message read off of a TCP socket, or NULL on failure +// If storage is non-null, result is placed in that buffer. Otherwise, +// returned value is allocated with Malloc, and contains sufficient extra +// storage for a Lease OPT RR + +mDNSlocal PktMsg *ReadTCPMsg(int sd, PktMsg *storage) + { + int nread, allocsize; + mDNSu16 msglen = 0; + PktMsg *pkt = NULL; + int srclen; + + nread = my_recv(sd, &msglen, sizeof(msglen)); + if (nread < 0) { LogErr("TCPRequestForkFn", "recv"); goto error; } + msglen = ntohs(msglen); + if (nread != sizeof(msglen)) { Log("Could not read length field of message"); goto error; } + + if (storage) + { + if (msglen > sizeof(storage->msg)) { Log("ReadTCPMsg: provided buffer too small."); goto error; } + pkt = storage; + } + else + { + // buffer extra space to add an OPT RR + if (msglen > sizeof(DNSMessage)) allocsize = sizeof(PktMsg) - sizeof(DNSMessage) + msglen; + else allocsize = sizeof(PktMsg); + pkt = malloc(allocsize); + if (!pkt) { LogErr("ReadTCPMsg", "malloc"); goto error; } + bzero(pkt, sizeof(*pkt)); + } + + pkt->len = msglen; + srclen = sizeof(pkt->src); + if (getpeername(sd, (struct sockaddr *)&pkt->src, &srclen) || + srclen != sizeof(pkt->src)) { LogErr("ReadTCPMsg", "getpeername"); bzero(&pkt->src, sizeof(pkt->src)); } + nread = my_recv(sd, &pkt->msg, msglen); + if (nread < 0) { LogErr("TCPRequestForkFn", "recv"); goto error; } + if (nread != msglen) { Log("Could not read entire message"); goto error; } + if (pkt->len < sizeof(DNSMessageHeader)) + { Log("ReadTCPMsg: Message too short (%d bytes)", pkt->len); goto error; } + HdrNToH(pkt); + return pkt; + //!!!KRS convert to HBO here? + error: + if (pkt && pkt != storage) free(pkt); + return NULL; + } + +// +// Dynamic Update Utility Routines +// + +// Get the lease life of records in a dynamic update +// returns -1 on error or if no lease present +mDNSlocal mDNSs32 GetPktLease(PktMsg *pkt) + { + mDNSs32 lease = -1; + const mDNSu8 *ptr = NULL, *end = (mDNSu8 *)&pkt->msg + pkt->len; + LargeCacheRecord lcr; + int i; + + HdrNToH(pkt); + ptr = LocateAdditionals(&pkt->msg, end); + if (ptr) + for (i = 0; i < pkt->msg.h.numAdditionals; i++) + { + ptr = GetLargeResourceRecord(NULL, &pkt->msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr); + if (!ptr) { Log("Unable to read additional record"); break; } + if (lcr.r.resrec.rrtype == kDNSType_OPT) + { + if (lcr.r.resrec.rdlength < LEASE_OPT_SIZE) continue; + if (lcr.r.resrec.rdata->u.opt.opt != kDNSOpt_Lease) continue; + lease = (mDNSs32)lcr.r.resrec.rdata->u.opt.OptData.lease; + break; + } + } + + HdrHToN(pkt); + return lease; + } + +// check if a request and server response complete a successful dynamic update +mDNSlocal mDNSBool SuccessfulUpdateTransaction(PktMsg *request, PktMsg *reply) + { + char buf[32]; + char *vlogmsg = NULL; + + // check messages + if (!request || !reply) { vlogmsg = "NULL message"; goto failure; } + if (request->len < sizeof(DNSMessageHeader) || reply->len < sizeof(DNSMessageHeader)) { vlogmsg = "Malformatted message"; goto failure; } + + // check request operation + if ((request->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) != (request->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask)) + { vlogmsg = "Request opcode not an update"; goto failure; } + + // check result + if ((reply->msg.h.flags.b[1] & kDNSFlag1_RC)) { vlogmsg = "Reply contains non-zero rcode"; goto failure; } + if ((reply->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) != (kDNSFlag0_OP_Update | kDNSFlag0_QR_Response)) + { vlogmsg = "Reply opcode not an update response"; goto failure; } + + VLog("Successful update from %s", inet_ntop(AF_INET, &request->src.sin_addr, buf, 32)); + return mDNStrue; + + failure: + VLog("Request %s: %s", inet_ntop(AF_INET, &request->src.sin_addr, buf, 32), vlogmsg); + return mDNSfalse; + } + +// Allocate an appropriately sized CacheRecord and copy data from original +mDNSlocal CacheRecord *CopyCacheRecord(CacheRecord *orig) + { + CacheRecord *cr; + size_t size = sizeof(*cr); + if (orig->resrec.rdlength > InlineCacheRDSize) size += orig->resrec.rdlength - InlineCacheRDSize; + cr = malloc(size); + if (!cr) { LogErr("CopyCacheRecord", "malloc"); return NULL; } + memcpy(cr, orig, size); + cr->resrec.rdata = (RData*)&cr->rdatastorage; + return cr; + } + + +// +// Lease Hashtable Utility Routines +// + +// double hash table size +// caller must lock table prior to invocation +mDNSlocal void RehashTable(DaemonInfo *d) + { + RRTableElem *ptr, *tmp, **new; + int i, bucket, newnbuckets = d->nbuckets * 2; + + new = malloc(sizeof(RRTableElem *) * newnbuckets); + if (!new) { LogErr("RehashTable", "malloc"); return; } + bzero(new, newnbuckets * sizeof(RRTableElem *)); + + for (i = 0; i < d->nbuckets; i++) + { + ptr = d->table[i]; + while (ptr) + { + bucket = ptr->rr.resrec.namehash % newnbuckets; + tmp = ptr; + ptr = ptr->next; + tmp->next = new[bucket]; + new[bucket] = tmp; + } + } + d->nbuckets = newnbuckets; + } + +// print entire contents of hashtable, invoked via SIGINFO +mDNSlocal void PrintLeaseTable(DaemonInfo *d) + { + int i; + RRTableElem *ptr; + char rrbuf[80], addrbuf[16]; + struct timeval now; + int hr, min, sec; + + if (gettimeofday(&now, NULL)) { LogErr("PrintTable", "gettimeofday"); return; } + if (pthread_mutex_lock(&d->tablelock)) { LogErr("PrintTable", "pthread_mutex_lock"); return; } + + Log("Dumping Lease Table Contents (table contains %d resource records)", d->nelems); + for (i = 0; i < d->nbuckets; i++) + { + for (ptr = d->table[i]; ptr; ptr = ptr->next) + { + hr = ((ptr->expire - now.tv_sec) / 60) / 60; + min = ((ptr->expire - now.tv_sec) / 60) % 60; + sec = (ptr->expire - now.tv_sec) % 60; + Log("Update from %s, Expires in %d:%d:%d\n\t%s", inet_ntop(AF_INET, &ptr->cli.sin_addr, addrbuf, 16), hr, min, sec, + GetRRDisplayString_rdb(&ptr->rr.resrec, &ptr->rr.resrec.rdata->u, rrbuf)); + } + } + pthread_mutex_unlock(&d->tablelock); + } + +// +// Startup SRV Registration Routines +// Register _dns-update._udp/_tcp. SRV records indicating the port on which +// the daemon accepts requests +// + +// delete all RRS of a given name/type +mDNSlocal mDNSu8 *putRRSetDeletion(DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *limit, ResourceRecord *rr) + { + ptr = putDomainNameAsLabels(msg, ptr, limit, &rr->name); + if (!ptr || ptr + 10 >= limit) return NULL; // out of space + ptr[0] = (mDNSu8)(rr->rrtype >> 8); + ptr[1] = (mDNSu8)(rr->rrtype & 0xFF); + ptr[2] = (mDNSu8)((mDNSu16)kDNSQClass_ANY >> 8); + ptr[3] = (mDNSu8)((mDNSu16)kDNSQClass_ANY & 0xFF); + bzero(ptr+4, sizeof(rr->rroriginalttl) + sizeof(rr->rdlength)); // zero ttl/rdata + msg->h.mDNS_numUpdates++; + return ptr + 10; + } + +mDNSlocal mDNSu8 *PutUpdateSRV(DaemonInfo *d, PktMsg *pkt, mDNSu8 *ptr, char *regtype, mDNSBool registration) + { + AuthRecord rr; + char hostname[1024], buf[80]; + mDNSu8 *end = (mDNSu8 *)&pkt->msg + sizeof(DNSMessage); + + mDNS_SetupResourceRecord(&rr, NULL, 0, kDNSType_SRV, SRV_TTL, kDNSRecordTypeUnique, NULL, NULL); + rr.resrec.rrclass = kDNSClass_IN; + rr.resrec.rdata->u.srv.priority = 0; + rr.resrec.rdata->u.srv.weight = 0; + rr.resrec.rdata->u.srv.port.NotAnInteger = d->port.NotAnInteger; + if (!gethostname(hostname, 1024) < 0 || MakeDomainNameFromDNSNameString(&rr.resrec.rdata->u.srv.target, hostname)) + rr.resrec.rdata->u.srv.target.c[0] = '\0'; + + MakeDomainNameFromDNSNameString(&rr.resrec.name, regtype); + strcpy(rr.resrec.name.c + strlen(rr.resrec.name.c), d->zone.c); + VLog("%s %s", registration ? "Registering SRV record" : "Deleting existing RRSet", + GetRRDisplayString_rdb(&rr.resrec, &rr.resrec.rdata->u, buf)); + if (registration) ptr = PutResourceRecord(&pkt->msg, ptr, &pkt->msg.h.mDNS_numUpdates, &rr.resrec); + else ptr = putRRSetDeletion(&pkt->msg, ptr, end, &rr.resrec); + return ptr; + } + + +// perform dynamic update. +// specify deletion by passing false for the register parameter, otherwise register the records. +mDNSlocal int UpdateSRV(DaemonInfo *d, mDNSBool registration) + { + int sd = -1; + mDNSOpaque16 id; + PktMsg pkt; + mDNSu8 *ptr = pkt.msg.data; + mDNSu8 *end = (mDNSu8 *)&pkt.msg + sizeof(DNSMessage); + mDNSu16 nAdditHBO; // num additionas, in host byte order, required by message digest routine + PktMsg *reply = NULL; + + int result = -1; + + // Initialize message + id.NotAnInteger = 0; + InitializeDNSMessage(&pkt.msg.h, id, UpdateReqFlags); + pkt.src.sin_addr.s_addr = htonl(INADDR_ANY); // address field set solely for verbose logging in subroutines + pkt.src.sin_family = AF_INET; + + // format message body + ptr = putZone(&pkt.msg, ptr, end, &d->zone, mDNSOpaque16fromIntVal(kDNSClass_IN)); + if (!ptr) goto end; + + ptr = PutUpdateSRV(d, &pkt, ptr, "_dns-update._udp.", registration); if (!ptr) goto end; + ptr = PutUpdateSRV(d, &pkt, ptr, "_dns-update._tcp.", registration); if (!ptr) goto end; + ptr = PutUpdateSRV(d, &pkt, ptr, "_dns-llq._udp.", registration); if (!ptr) goto end; + + nAdditHBO = pkt.msg.h.numAdditionals; + HdrHToN(&pkt); + if (d->AuthInfo) + { + ptr = DNSDigest_SignMessage(&pkt.msg, &ptr, &nAdditHBO, d->AuthInfo); + if (!ptr) goto end; + } + pkt.len = ptr - (mDNSu8 *)&pkt.msg; + + // send message, receive reply + sd = ConnectToServer(d); + if (sd < 0) { Log("UpdateSRV: ConnectToServer failed"); goto end; } + if (SendTCPMsg(sd, &pkt)) { Log("UpdateSRV: SendTCPMsg failed"); } + reply = ReadTCPMsg(sd, NULL); + if (!SuccessfulUpdateTransaction(&pkt, reply)) + Log("SRV record registration failed with rcode %d", reply->msg.h.flags.b[1] & kDNSFlag1_RC); + else result = 0; + + end: + if (!ptr) { Log("UpdateSRV: Error constructing lease expiration update"); } + if (sd >= 0) close(sd); + if (reply) free(reply); + return result; + } + +// wrapper routines/macros +#define ClearUpdateSRV(d) UpdateSRV(d, 0) + +// clear any existing records prior to registration +mDNSlocal int SetUpdateSRV(DaemonInfo *d) + { + int err; + + err = ClearUpdateSRV(d); // clear any existing record + if (!err) err = UpdateSRV(d, 1); + return err; + } + +// +// Argument Parsing and Configuration +// + +// read authentication information for a zone from command line argument +// global optind corresponds to keyname argument on entry +mDNSlocal int ReadAuthKey(int argc, char *argv[], DaemonInfo *d) + { + uDNS_AuthInfo *auth = NULL; + char keybuf[512]; + mDNSs32 keylen; + + auth = malloc(sizeof(*auth)); + if (!auth) { perror("ReadAuthKey, malloc"); goto error; } + auth->next = NULL; + if (argc < optind + 1) return -1; // keyname + secret + if (!MakeDomainNameFromDNSNameString(&auth->keyname, optarg)) + { fprintf(stderr, "Bad key name %s", optarg); goto error; } + keylen = DNSDigest_Base64ToBin(argv[optind++], keybuf, 512); + if (keylen < 0) + { fprintf(stderr, "Bad shared secret %s (must be base-64 encoded string)", argv[optind-1]); goto error; } + DNSDigest_ConstructHMACKey(auth, keybuf, (mDNSu32)keylen); + d->AuthInfo = auth; + return 0; + + error: + if (auth) free(auth); + return -1; + } + +mDNSlocal int SetPort(DaemonInfo *d, char *PortAsString) + { + long l; + + l = strtol(PortAsString, NULL, 10); // convert string to long + if ((!l && errno == EINVAL) || l > 65535) return -1; // error check conversion + d->port.NotAnInteger = htons((mDNSu16)l); // set to network byte order + return 0; + } + +mDNSlocal void PrintUsage(void) + { + fprintf(stderr, "Usage: dnsextd -z [-vf] [ -s server ] [-k zone keyname secret] ...\n" + "Use \"dnsextd -h\" for help\n"); + } + +mDNSlocal void PrintHelp(void) + { + fprintf(stderr, "\n\n"); + PrintUsage(); + + fprintf(stderr, + "dnsextd is a daemon that implements DNS extensions supporting Dynamic DNS Update Leases\n" + "and Long Lived Queries, used in Wide-Area DNS Service Discovery, on behalf of name servers\n" + "that do not natively support these extensions. (See dns-sd.org for more info on DNS Service\n" + "Discovery, Update Leases, and Long Lived Queries.)\n\n" + + "dnsextd requires one argument,the zone, which is the domain for which Update Leases\n" + "and Long Lived Queries are to be administered. dnsextd communicates directly with the\n" + "primary master server for this zone.\n\n" + + "The options are as follows:\n\n" + + "-f Run daemon in foreground.\n\n" + + "-h Print help.\n\n" + + "-k Specify TSIG authentication key for dynamic updates from daemon to name server.\n" + " -k option is followed by the name of the key, and the shared secret as a base-64\n" + " encoded string. This key/secret are used by the daemon to delete resource records\n" + " from the server when leases expire. Clients are responsible for signing their\n" + " update requests.\n\n" + + "-s Specify address (IPv4 address in dotted-decimal notation) of the Primary Master\n" + " name server. Defaults to loopback (127.0.0.1), i.e. daemon and name server\n" + " running on the same machine.\n\n" + + "-v Verbose output.\n\n" + ); + } + +// Note: ProcessArgs called before process is daemonized, and therefore must open no descriptors +// returns 0 (success) if program is to continue execution +// output control arguments (-f, -v) do not affect this routine +mDNSlocal int ProcessArgs(int argc, char *argv[], DaemonInfo *d) + { + int opt; + + if (argc < 2) goto arg_error; + + d->port.NotAnInteger = htons(DAEMON_PORT); // default, may be overriden by command option + while ((opt = getopt(argc, argv, "z:p:hfvs:k:")) != -1) + { + switch(opt) + { + case 'p': if (SetPort(d, optarg) < 0) goto arg_error; + break; + + case 'h': PrintHelp(); return -1; + case 'f': foreground = 1; break; + case 'v': verbose = 1; break; + case 's': if (!inet_pton(AF_INET, optarg, &d->saddr)) goto arg_error; + break; + case 'k': if (ReadAuthKey(argc, argv, d) < 0) goto arg_error; + break; + case 'z': if (!MakeDomainNameFromDNSNameString(&d->zone, optarg)) + { + fprintf(stderr, "Bad zone %s", optarg); + goto arg_error; + } + break; + default: goto arg_error; + } + } + + if (!d->zone.c[0]) goto arg_error; // zone is the only required argument + if (d->AuthInfo) AssignDomainName(d->AuthInfo->zone, d->zone); // if we have a shared secret, use it for the entire zone + return 0; + + arg_error: + PrintUsage(); + return -1; + } + + +// +// Initialization Routines +// + +// Allocate memory, initialize locks and bookkeeping variables +mDNSlocal int InitLeaseTable(DaemonInfo *d) + { + if (pthread_mutex_init(&d->tablelock, NULL)) { LogErr("InitLeaseTable", "pthread_mutex_init"); return -1; } + d->nbuckets = LEASETABLE_INIT_NBUCKETS; + d->nelems = 0; + d->table = malloc(sizeof(RRTableElem *) * LEASETABLE_INIT_NBUCKETS); + if (!d->table) { LogErr("InitLeaseTable", "malloc"); return -1; } + bzero(d->table, sizeof(RRTableElem *) * LEASETABLE_INIT_NBUCKETS); + return 0; + } +mDNSlocal int SetupSockets(DaemonInfo *daemon) + { + struct sockaddr_in daddr; + int sockpair[2]; + + // set up sockets on which we receive requests + bzero(&daddr, sizeof(daddr)); + daddr.sin_family = AF_INET; + daddr.sin_addr.s_addr = htonl(INADDR_ANY); + + if (daemon->port.NotAnInteger) daddr.sin_port = daemon->port.NotAnInteger; + else daddr.sin_port = htons(DAEMON_PORT); + + daemon->tcpsd = socket(AF_INET, SOCK_STREAM, 0); + if (!daemon->tcpsd) { LogErr("SetupSockets", "socket"); return -1; } + if (bind(daemon->tcpsd, (struct sockaddr *)&daddr, sizeof(daddr)) < 0) { LogErr("SetupSockets", "bind"); return -1; } + if (listen(daemon->tcpsd, LISTENQ) < 0) { LogErr("SetupSockets", "listen"); return -1; } + + daemon->udpsd = socket(AF_INET, SOCK_DGRAM, 0); + if (!daemon->udpsd) { LogErr("SetupSockets", "socket"); return -1; } + if (bind(daemon->udpsd, (struct sockaddr *)&daddr, sizeof(daddr)) < 0) { LogErr("SetupSockets", "bind"); return -1; } + + // set up Unix domain socket pair for LLQ polling thread to signal main thread that a change to the zone occurred + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sockpair) < 0) { LogErr("SetupSockets", "socketpair"); return -1; } + daemon->LLQEventListenSock = sockpair[0]; + daemon->LLQServPollSock = sockpair[1]; + return 0; + } + +// +// periodic table updates +// + +// Delete a resource record from the nameserver via a dynamic update +mDNSlocal void DeleteRecord(DaemonInfo *d, CacheRecord *rr, domainname *zone) + { + int sd = -1; + mDNSOpaque16 id; + PktMsg pkt; + mDNSu8 *ptr = pkt.msg.data; + mDNSu8 *end = (mDNSu8 *)&pkt.msg + sizeof(DNSMessage); + mDNSu16 nAdditHBO; // num additionas, in host byte order, required by message digest routine + char buf[80]; + PktMsg *reply = NULL; + + VLog("Expiring record %s", GetRRDisplayString_rdb(&rr->resrec, &rr->resrec.rdata->u, buf)); + sd = ConnectToServer(d); + if (sd < 0) { Log("DeleteRecord: ConnectToServer failed"); goto end; } + + id.NotAnInteger = 0; + InitializeDNSMessage(&pkt.msg.h, id, UpdateReqFlags); + + ptr = putZone(&pkt.msg, ptr, end, zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass)); + if (!ptr) goto end; + ptr = putDeletionRecord(&pkt.msg, ptr, &rr->resrec); + if (!ptr) goto end; + + nAdditHBO = pkt.msg.h.numAdditionals; + HdrHToN(&pkt); + + if (d->AuthInfo) + { + ptr = DNSDigest_SignMessage(&pkt.msg, &ptr, &nAdditHBO, d->AuthInfo); + if (!ptr) goto end; + } + + pkt.len = ptr - (mDNSu8 *)&pkt.msg; + pkt.src.sin_addr.s_addr = htonl(INADDR_ANY); // address field set solely for verbose logging in subroutines + pkt.src.sin_family = AF_INET; + if (SendTCPMsg(sd, &pkt)) { Log("DeleteRecord: SendTCPMsg failed"); } + reply = ReadTCPMsg(sd, NULL); + if (!SuccessfulUpdateTransaction(&pkt, reply)) + Log("Expiration update failed with rcode %d", reply->msg.h.flags.b[1] & kDNSFlag1_RC); + + end: + if (!ptr) { Log("DeleteRecord: Error constructing lease expiration update"); } + if (sd >= 0) close(sd); + if (reply) free(reply); + } + +// iterate over table, deleting expired records +mDNSlocal void DeleteExpiredRecords(DaemonInfo *d) + { + int i; + RRTableElem *ptr, *prev, *fptr; + struct timeval now; + + if (gettimeofday(&now, NULL)) { LogErr("DeleteExpiredRecords ", "gettimeofday"); return; } + if (pthread_mutex_lock(&d->tablelock)) { LogErr("DeleteExpiredRecords", "pthread_mutex_lock"); return; } + for (i = 0; i < d->nbuckets; i++) + { + ptr = d->table[i]; + prev = NULL; + while (ptr) + { + if (ptr->expire - now.tv_sec < 0) + { + // delete record from server + DeleteRecord(d, &ptr->rr, &ptr->zone); + if (prev) prev->next = ptr->next; + else d->table[i] = ptr->next; + fptr = ptr; + ptr = ptr->next; + free(fptr); + d->nelems--; + } + else + { + prev = ptr; + ptr = ptr->next; + } + } + } + pthread_mutex_unlock(&d->tablelock); + } + +// +// main update request handling +// + +// Add, delete, or refresh records in table based on contents of a successfully completed dynamic update +mDNSlocal void UpdateLeaseTable(PktMsg *pkt, DaemonInfo *d, mDNSs32 lease) + { + RRTableElem *prev, *rptr, *new = NULL; + int i, bucket; + LargeCacheRecord lcr; + const mDNSu8 *ptr, *end; + struct timeval time; + DNSQuestion zone; + char buf[80]; + + if (pthread_mutex_lock(&d->tablelock)) { LogErr("UpdateLeaseTable", "pthread_mutex_lock"); return; } + HdrNToH(pkt); + ptr = pkt->msg.data; + end = (mDNSu8 *)&pkt->msg + pkt->len; + ptr = getQuestion(&pkt->msg, ptr, end, 0, &zone); + if (!ptr) { Log("UpdateLeaseTable: cannot read zone"); goto cleanup; } + ptr = LocateAuthorities(&pkt->msg, end); + if (!ptr) { Log("UpdateLeaseTable: Format error"); goto cleanup; } + + for (i = 0; i < pkt->msg.h.mDNS_numUpdates; i++) + { + ptr = GetLargeResourceRecord(NULL, &pkt->msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr); + if (!ptr) { Log("UpdateLeaseTable: GetLargeResourceRecord returned NULL"); goto cleanup; } + //!!!KRS we should include rdata in hash here + bucket = lcr.r.resrec.namehash % d->nbuckets; + + // look for RR in table + prev = NULL; + rptr = d->table[bucket]; + while (rptr) + { + if (SameResourceRecord(&rptr->rr.resrec, &lcr.r.resrec)) break; + prev = rptr; + rptr = rptr->next; + } + + if (rptr) + { + // Record is already in table + if (!lcr.r.resrec.rroriginalttl && lcr.r.resrec.rrclass == kDNSClass_NONE) + { + // deletion record + VLog("Received deletion update for %s", GetRRDisplayString_rdb(&lcr.r.resrec, &lcr.r.resrec.rdata->u, buf)); + if (prev) prev->next = rptr->next; + else d->table[bucket] = rptr->next; + free(rptr); + d->nelems--; + } + else + { + // refresh + if (lease < 0) + { + Log("Update for record %s already in lease table with no refresh lease specified", + GetRRDisplayString_rdb(&lcr.r.resrec, &lcr.r.resrec.rdata->u, buf)); + } + else + { + if (gettimeofday(&time, NULL)) { LogErr("UpdateLeaseTable", "gettimeofday"); goto cleanup; } + rptr->expire = time.tv_sec + (unsigned)lease; + VLog("Refreshing lease for %s", GetRRDisplayString_rdb(&lcr.r.resrec, &lcr.r.resrec.rdata->u, buf)); + } + } + } + else if (lease > 0) + { + // New record - add to table + if (d->nelems > d->nbuckets) RehashTable(d); + if (gettimeofday(&time, NULL)) { LogErr("UpdateLeaseTable", "gettimeofday"); goto cleanup; } + new = malloc(sizeof(RRTableElem) + lcr.r.resrec.rdlength - InlineCacheRDSize); + if (!new) { LogErr("UpdateLeaseTable", "malloc"); goto cleanup; } + memcpy(&new->rr, &lcr.r, sizeof(CacheRecord) + lcr.r.resrec.rdlength - InlineCacheRDSize); + new->rr.resrec.rdata = (RData *)&new->rr.rdatastorage; + new->expire = time.tv_sec + (unsigned)lease; + new->cli.sin_addr = pkt->src.sin_addr; + strcpy(new->zone.c, zone.qname.c); + new->next = d->table[bucket]; + d->table[bucket] = new; + d->nelems++; + VLog("Adding update for %s to lease table", GetRRDisplayString_rdb(&lcr.r.resrec, &lcr.r.resrec.rdata->u, buf)); + } + } + + cleanup: + pthread_mutex_unlock(&d->tablelock); + HdrHToN(pkt); + } + +// Given a successful reply from a server, create a new reply that contains lease information +// Replies are currently not signed !!!KRS change this +mDNSlocal PktMsg *FormatLeaseReply(DaemonInfo *d, PktMsg *orig, mDNSu32 lease) + { + PktMsg *reply; + mDNSu8 *ptr, *end; + mDNSOpaque16 flags; + + (void)d; //unused + reply = malloc(sizeof(*reply)); + if (!reply) { LogErr("FormatLeaseReply", "malloc"); return NULL; } + flags.b[0] = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update; + flags.b[1] = 0; + + InitializeDNSMessage(&reply->msg.h, orig->msg.h.id, flags); + reply->src.sin_addr.s_addr = htonl(INADDR_ANY); // unused except for log messages + reply->src.sin_family = AF_INET; + ptr = reply->msg.data; + end = (mDNSu8 *)&reply->msg + sizeof(DNSMessage); + ptr = putUpdateLease(&reply->msg, ptr, lease); + if (!ptr) { Log("FormatLeaseReply: putUpdateLease failed"); free(reply); return NULL; } + reply->len = ptr - (mDNSu8 *)&reply->msg; + return reply; + } + +// pkt is thread-local, not requiring locking +mDNSlocal PktMsg *HandleRequest(PktMsg *pkt, DaemonInfo *d) + { + int sd = -1; + PktMsg *reply = NULL, *LeaseReply; + mDNSs32 lease; + char buf[32]; + + // send msg to server, read reply + sd = ConnectToServer(d); + if (sd < 0) + { Log("Discarding request from %s due to connection errors", inet_ntop(AF_INET, &pkt->src.sin_addr, buf, 32)); goto cleanup; } + if (SendTCPMsg(sd, pkt) < 0) + { Log("Couldn't relay message from %s to server. Discarding.", inet_ntop(AF_INET, &pkt->src.sin_addr, buf, 32)); goto cleanup; } + reply = ReadTCPMsg(sd, NULL); + + // process reply + if (!SuccessfulUpdateTransaction(pkt, reply)) + { VLog("Message from %s not a successful update.", inet_ntop(AF_INET, &pkt->src.sin_addr, buf, 32)); goto cleanup; } + lease = GetPktLease(pkt); + UpdateLeaseTable(pkt, d, lease); + if (lease > 0) + { + LeaseReply = FormatLeaseReply(d, reply, lease); + if (!LeaseReply) Log("HandleRequest - unable to format lease reply"); + free(reply); + reply = LeaseReply; + } + cleanup: + if (sd >= 0) close(sd); + return reply; + } + + +// +// LLQ Support Routines +// + +// Set fields of an LLQ Opt Resource Record +mDNSlocal void FormatLLQOpt(AuthRecord *opt, int opcode, mDNSu8 *id, mDNSs32 lease) + { + bzero(opt, sizeof(*opt)); + opt->resrec.rdata = &opt->rdatastorage; + opt->resrec.RecordType = kDNSRecordTypeKnownUnique; // to suppress warnings from other layers + opt->resrec.rrtype = kDNSType_OPT; + opt->resrec.rdlength = LLQ_OPT_SIZE; + opt->resrec.rdestimate = LLQ_OPT_SIZE; + opt->resrec.rdata->u.opt.opt = kDNSOpt_LLQ; + opt->resrec.rdata->u.opt.optlen = sizeof(LLQOptData); + opt->resrec.rdata->u.opt.OptData.llq.vers = kLLQ_Vers; + opt->resrec.rdata->u.opt.OptData.llq.llqOp = opcode; + opt->resrec.rdata->u.opt.OptData.llq.err = LLQErr_NoError; + memcpy(opt->resrec.rdata->u.opt.OptData.llq.id, id, 8); + opt->resrec.rdata->u.opt.OptData.llq.lease = lease; + } + +// Calculate effective remaining lease of an LLQ +mDNSlocal mDNSu32 LLQLease(LLQEntry *e) + { + struct timeval t; + + gettimeofday(&t, NULL); + if (e->expire < t.tv_sec) return 0; + else return e->expire - t.tv_sec; + } + +mDNSlocal void FreeKnownAnswers(LLQEntry *e) + { + CacheRecord *tmp; + + while(e->KnownAnswers) + { + tmp = e->KnownAnswers; + e->KnownAnswers = e->KnownAnswers->next; + free(tmp); + } + } + +mDNSlocal void DeleteLLQ(DaemonInfo *d, LLQEntry *e) + { + int bucket = bucket = DomainNameHashValue(&e->qname) % LLQ_TABLESIZE; + LLQEntry *prev = NULL, *ptr = d->LLQTable[bucket]; + char addr[32]; + + inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32); + VLog("Deleting LLQ table entry for %##s client %s", e->qname.c, addr); + + FreeKnownAnswers(e); + while(ptr) + { + if (ptr == e) + { + if (prev) prev->next = ptr->next; + else d->LLQTable[bucket] = ptr->next; + free(e); + return; + } + prev = ptr; + ptr = ptr->next; + } + Log("Error: DeleteLLQ - LLQ not in table"); + } + +mDNSlocal int SendLLQ(DaemonInfo *d, PktMsg *pkt, struct sockaddr_in dst) + { + char addr[32]; + int err = -1; + + HdrHToN(pkt); + if (sendto(d->udpsd, &pkt->msg, pkt->len, 0, (struct sockaddr *)&dst, sizeof(dst)) != (int)pkt->len) + { + LogErr("DaemonInfo", "sendto"); + Log("Could not send response to client %s", inet_ntop(AF_INET, &dst.sin_addr, addr, 32)); + } + else err = 0; + HdrNToH(pkt); + return err; + } + +// if non-negative, sd is a TCP socket connected to the nameserver +// otherwise, this routine creates and closes its own socket +mDNSlocal CacheRecord *AnswerQuestion(DaemonInfo *d, LLQEntry *e, int sd) + { + PktMsg q; + int i; + const mDNSu8 *ansptr; + mDNSu8 *end = q.msg.data; + mDNSOpaque16 id, flags = QueryFlags; + PktMsg *reply = NULL; + LargeCacheRecord lcr; + CacheRecord *AnswerList = NULL; + mDNSu8 rcode; + mDNSBool CloseSDOnExit = sd < 0; + + VLog("Querying server for %##s type %d", e->qname.c, e->qtype); + + flags.b[0] |= kDNSFlag0_RD; // recursion desired + id.NotAnInteger = 0; + InitializeDNSMessage(&q.msg.h, id, flags); + + end = putQuestion(&q.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN); + if (!end) { Log("Error: AnswerQuestion - putQuestion returned NULL"); goto end; } + q.len = (int)(end - (mDNSu8 *)&q.msg); + + if (sd < 0) sd = ConnectToServer(d); + if (sd < 0) { Log("AnswerQuestion: ConnectToServer failed"); goto end; } + if (SendTCPMsg(sd, &q)) { Log("AnswerQuestion: SendTCPMsg failed"); close(sd); goto end; } + reply = ReadTCPMsg(sd, NULL); + + if ((reply->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) != (kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery)) + { Log("AnswerQuestion: %##s type %d - Invalid response flags from server"); goto end; } + rcode = (mDNSu8)(reply->msg.h.flags.b[1] & kDNSFlag1_RC); + if (rcode && rcode != kDNSFlag1_RC_NXDomain) { Log("AnswerQuestion: %##s type %d - non-zero rcode %d from server", e->qname.c, e->qtype, rcode); goto end; } + + end = (mDNSu8 *)&reply->msg + reply->len; + ansptr = LocateAnswers(&reply->msg, end); + if (!ansptr) { Log("Error: AnswerQuestion - LocateAnswers returned NULL"); goto end; } + + for (i = 0; i < reply->msg.h.numAnswers; i++) + { + //rr = malloc(sizeof(*rr)); + //if (!rr) { LogErr("AnswerQuestion", "malloc"); goto end; } + ansptr = GetLargeResourceRecord(NULL, &reply->msg, ansptr, end, 0, kDNSRecordTypePacketAns, &lcr); + if (!ansptr) { Log("AnswerQuestions: GetLargeResourceRecord returned NULL"); goto end; } + if (lcr.r.resrec.rrtype != e->qtype || lcr.r.resrec.rrclass != kDNSClass_IN || !SameDomainName(&lcr.r.resrec.name, &e->qname)) + { + Log("AnswerQuestion: response %##s type #d does not answer question %##s type #d. Discarding", + lcr.r.resrec.name.c, lcr.r.resrec.rrtype, e->qname.c, e->qtype); + } + else + { + CacheRecord *cr = CopyCacheRecord(&lcr.r); + if (!cr) { Log("Error: AnswerQuestion - CopyCacheRecord returned NULL"); goto end; } + cr->next = AnswerList; + AnswerList = cr; + } + } + + end: + if (sd > -1 && CloseSDOnExit) close(sd); + if (reply) free(reply); + e->state = Established; + return AnswerList; + } + +mDNSlocal void UpdateAnswerList(DaemonInfo *d, LLQEntry *e, CacheRecord *answers) + { + CacheRecord *prev = NULL, *na, *ka; // "new answer", "known answer" + PktMsg response; + mDNSu8 *end = (mDNSu8 *)&response.msg.data; + mDNSOpaque16 msgID; + char rrbuf[80], addrbuf[32]; + AuthRecord opt; + + // first pass - mark all answers for deletion + for (ka = e->KnownAnswers; ka; ka = ka->next) + ka->resrec.rroriginalttl = -1; // -1 means delete + + // second pass - mark answers pre-existent + for (ka = e->KnownAnswers; ka; ka = ka->next) + { + for (na = answers; na; na = na->next) + { + if (SameResourceRecord(&ka->resrec, &na->resrec)) + { ka->resrec.rroriginalttl = 0; break; } // 0 means no change + } + } + + // third pass - add new records + for (na = answers; na; na = na->next) + { + for (ka = e->KnownAnswers; ka; ka = ka->next) + if (SameResourceRecord(&ka->resrec, &na->resrec)) break; + if (!ka) + { + // answer is not in KA list + CacheRecord *cr = CopyCacheRecord(na); + if (!cr) { Log("Error: UpdateAnswerList - CopyCacheRecord returned NULL"); return; } + cr->resrec.rroriginalttl = 1; // 1 means add + cr->next = e->KnownAnswers; + e->KnownAnswers = cr; + } + } + + // now send the update + msgID.NotAnInteger = random(); + InitializeDNSMessage(&response.msg.h, msgID, ResponseFlags); + end = putQuestion(&response.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN); + if (!end) { Log("Error: UpdateAnswerList - putQuestion returned NULL"); return; } + + if (verbose) inet_ntop(AF_INET, &e->cli.sin_addr, addrbuf, 32); + // put adds/removes in packet + for (ka = e->KnownAnswers; ka; ka = ka->next) + { + if (ka->resrec.rroriginalttl) + { + if (verbose) GetRRDisplayString_rdb(&ka->resrec, &ka->resrec.rdata->u, rrbuf); + VLog("%s (%s): %s", addrbuf, (mDNSs32)ka->resrec.rroriginalttl < 0 ? "Remove": "Add", rrbuf); + end = PutResourceRecordTTLJumbo(&response.msg, end, &response.msg.h.numAnswers, &ka->resrec, ka->resrec.rroriginalttl); + if (!end) { Log("Error: UpdateAnswerList - UpdateAnswerList returned NULL"); return; } + } + } + + // delete removes from list + ka = e->KnownAnswers; + while (ka) + { + if ((mDNSs32)ka->resrec.rroriginalttl < 0) + { + CacheRecord *fptr = ka; + if (prev) prev->next = ka->next; + else e->KnownAnswers = ka->next; + ka = ka->next; + free(fptr); + } + else + { + prev = ka; + ka = ka->next; + } + } + + FormatLLQOpt(&opt, kLLQOp_Event, e->id, LLQLease(e)); + end = PutResourceRecordTTLJumbo(&response.msg, end, &response.msg.h.numAdditionals, &opt.resrec, 0); + if (!end) { Log("Error: PutResourceRecordTTLJumbo"); return; } + + response.len = (int)(end - (mDNSu8 *)&response.msg); + if (response.msg.h.numAnswers) + if (SendLLQ(d, &response, e->cli) < 0) LogErr("UpdateAnswerList", "SendLLQ"); + } + +mDNSlocal void PrintLLQTable(DaemonInfo *d) + { + LLQEntry *e; + char addr[32]; + int i; + + Log("Printing LLQ table contents"); + + for (i = 0; i < LLQ_TABLESIZE; i++) + { + e = d->LLQTable[i]; + while(e) + { + inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32); + Log("LLQ from %##s type %d lease %d (%d remaining)", + addr, e->qname.c, e->qtype, e->lease, LLQLease(e)); + e = e->next; + } + } + } + +// Send events to clients as a result of a change in the zone +mDNSlocal void GenLLQEvents(DaemonInfo *d) + { + LLQEntry *e, *tmp; + int i, sd; + struct timeval t; + char addr[32]; + VLog("Generating LLQ Events"); + + gettimeofday(&t, NULL); + sd = ConnectToServer(d); + if (sd < 0) { Log("GenLLQEvents: ConnectToServer failed"); return; } + + for (i = 0; i < LLQ_TABLESIZE; i++) + { + e = d->LLQTable[i]; + while(e) + { + if (e->expire < t.tv_sec) + { + inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32); + VLog("Expiring LLQ %##s from %s", e->qname.c, addr); + tmp = e; + e = e->next; + DeleteLLQ(d, tmp); + } + else + { + CacheRecord *answers = AnswerQuestion(d, e, sd); + UpdateAnswerList(d, e, answers); + e = e->next; + } + } + } + close(sd); + } + +// Monitor zone for changes that may produce LLQ events +mDNSlocal void *LLQEventMonitor(void *DInfoPtr) + { + DaemonInfo *d = DInfoPtr; + PktMsg q; + mDNSu8 *end = q.msg.data; + const mDNSu8 *ptr; + mDNSOpaque16 id, flags = QueryFlags; + PktMsg reply; + mDNSs32 serial = 0; + mDNSBool SerialInitialized = mDNSfalse; + int sd; + LargeCacheRecord lcr; + ResourceRecord *rr = &lcr.r.resrec; + int i, sleeptime = 0; + domainname zone; + char pingmsg[4]; + + // create question + id.NotAnInteger = 0; + InitializeDNSMessage(&q.msg.h, id, flags); + AssignDomainName(zone, d->zone); + end = putQuestion(&q.msg, end, end + AbsoluteMaxDNSMessageData, &zone, kDNSType_SOA, kDNSClass_IN); + if (!end) { Log("Error: LLQEventMonitor - putQuestion returned NULL"); return NULL; } + q.len = (int)(end - (mDNSu8 *)&q.msg); + + sd = ConnectToServer(d); + if (sd < 0) { Log("LLQEventMonitor: ConnectToServer failed"); return NULL; } + + while(1) + { + usleep(sleeptime); + sleeptime = LLQ_MONITOR_ERR_INTERVAL; // if we bail on error below, rate limit retry + + // send message, receive response + if (SendTCPMsg(sd, &q)) { Log("LLQEventMonitor: SendTCPMsg failed"); continue; } + if (!ReadTCPMsg(sd, &reply)) { Log("LLQEventMonitor: ReadTCPMsg failed"); continue; } + end = (mDNSu8 *)&reply.msg + reply.len; + if (reply.msg.h.flags.b[1] & kDNSFlag1_RC) { Log("LLQEventMonitor - received non-zero rcode"); continue; } + + // find answer + ptr = LocateAnswers(&reply.msg, end); + if (!ptr) { Log("Error: LLQEventMonitor - LocateAnswers returned NULL"); continue; } + for (i = 0; i < reply.msg.h.numAnswers; i++) + { + ptr = GetLargeResourceRecord(NULL, &reply.msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr); + if (!ptr) { Log("Error: LLQEventMonitor - GetLargeResourceRecord returned NULL"); continue; } + if (rr->rrtype != kDNSType_SOA || rr->rrclass != kDNSClass_IN || !SameDomainName(&rr->name, &zone)) continue; + if (!SerialInitialized) + { + // first time through loop + SerialInitialized = mDNStrue; + serial = rr->rdata->u.soa.serial; + sleeptime = LLQ_MONITOR_INTERVAL; + break; + } + else if (rr->rdata->u.soa.serial != serial) + { + // update serial, wake main thread + serial = rr->rdata->u.soa.serial; + VLog("LLQEventMonitor: zone changed. Signaling main thread."); + if (send(d->LLQServPollSock, pingmsg, sizeof(pingmsg), 0) != sizeof(pingmsg)) + { LogErr("LLQEventMonitor", "send"); break; } + } + sleeptime = LLQ_MONITOR_INTERVAL; + break; + } + if (!ptr) Log("LLQEventMonitor: response to query did not contain SOA"); + } + } + + // Allocate LLQ entry, insert into table +mDNSlocal LLQEntry *NewLLQ(DaemonInfo *d, struct sockaddr_in cli, domainname *qname, mDNSu16 qtype, mDNSu32 lease) + { + char addr[32]; + struct timeval t; + int bucket; + LLQEntry *e = malloc(sizeof(*e)); + if (!e) { LogErr("NewLLQ", "malloc"); return NULL; } + + inet_ntop(AF_INET, &cli.sin_addr, addr, 32); + VLog("Allocating LLQ entry for client %s question %##s type %d", addr, qname->c, qtype); + + // initialize structure + e->cli = cli; + AssignDomainName(e->qname, *qname); + e->qtype = qtype; + memset(e->id, 0, 8); + e->state = RequestReceived; + e->KnownAnswers = NULL; + + if (lease < LLQ_MIN_LEASE) lease = LLQ_MIN_LEASE; + else if (lease > LLQ_MAX_LEASE) lease = LLQ_MIN_LEASE; + gettimeofday(&t, NULL); + e->expire = t.tv_sec + (int)lease; + e->lease = lease; + + // add to table + bucket = DomainNameHashValue(qname) % LLQ_TABLESIZE; + e->next = d->LLQTable[bucket]; + d->LLQTable[bucket] = e; + + return e; + } + +// Handle a refresh request from client +mDNSlocal void LLQRefresh(DaemonInfo *d, LLQEntry *e, LLQOptData *llq, mDNSOpaque16 msgID) + { + AuthRecord opt; + PktMsg ack; + mDNSu8 *end = (mDNSu8 *)&ack.msg.data; + char addr[32]; + + inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32); + VLog("%s LLQ for %##s from %s", llq->lease ? "Refreshing" : "Deleting", e->qname.c, addr); + + if (llq->lease) + { + if (llq->lease < LLQ_MIN_LEASE) llq->lease = LLQ_MIN_LEASE; + else if (llq->lease > LLQ_MAX_LEASE) llq->lease = LLQ_MIN_LEASE; + } + + ack.src.sin_addr.s_addr = 0; // unused + InitializeDNSMessage(&ack.msg.h, msgID, ResponseFlags); + end = putQuestion(&ack.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN); + if (!end) { Log("Error: putQuestion"); return; } + + FormatLLQOpt(&opt, kLLQOp_Refresh, e->id, llq->lease ? LLQLease(e) : 0); + end = PutResourceRecordTTLJumbo(&ack.msg, end, &ack.msg.h.numAdditionals, &opt.resrec, 0); + if (!end) { Log("Error: PutResourceRecordTTLJumbo"); return; } + + ack.len = (int)(end - (mDNSu8 *)&ack.msg); + if (SendLLQ(d, &ack, e->cli)) Log("Error: LLQRefresh"); + + if (llq->lease) e->state = Established; + else DeleteLLQ(d, e); + } + +// Complete handshake with Ack an initial answers +mDNSlocal void LLQCompleteHandshake(DaemonInfo *d, LLQEntry *e, LLQOptData *llq, mDNSOpaque16 msgID) + { + char addr[32]; + CacheRecord *ptr; + AuthRecord opt; + PktMsg ack; + mDNSu8 *end = (mDNSu8 *)&ack.msg.data; + char rrbuf[80], addrbuf[32]; + + inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32); + + if (memcmp(llq->id, e->id, 8) || + llq->vers != kLLQ_Vers || + llq->llqOp != kLLQOp_Setup || + llq->err != LLQErr_NoError || + llq->lease > e->lease + LLQ_LEASE_FUDGE || + llq->lease < e->lease - LLQ_LEASE_FUDGE) + { Log("Incorrect challenge response from %s", addr); return; } + + if (e->state == Established) VLog("Retransmitting LLQ ack + answers for %##s", e->qname.c); + else VLog("Delivering LLQ ack + answers for %##s", e->qname.c); + + // format ack + answers + ack.src.sin_addr.s_addr = 0; // unused + InitializeDNSMessage(&ack.msg.h, msgID, ResponseFlags); + end = putQuestion(&ack.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN); + if (!end) { Log("Error: putQuestion"); return; } + + if (e->state != Established) e->KnownAnswers = AnswerQuestion(d, e, -1); // only fetch KA list the first time through + if (verbose) inet_ntop(AF_INET, &e->cli.sin_addr, addrbuf, 32); + for (ptr = e->KnownAnswers; ptr; ptr = ptr->next) + { + if (verbose) GetRRDisplayString_rdb(&ptr->resrec, &ptr->resrec.rdata->u, rrbuf); + VLog("%s Intitial Answer - %s", addr, rrbuf); + end = PutResourceRecordTTLJumbo(&ack.msg, end, &ack.msg.h.numAnswers, &ptr->resrec, 1); + if (!end) { Log("Error: PutResourceRecordTTLJumbo"); return; } + } + + FormatLLQOpt(&opt, kLLQOp_Setup, e->id, LLQLease(e)); + end = PutResourceRecordTTLJumbo(&ack.msg, end, &ack.msg.h.numAdditionals, &opt.resrec, 0); + if (!end) { Log("Error: PutResourceRecordTTLJumbo"); return; } + + ack.len = (int)(end - (mDNSu8 *)&ack.msg); + if (SendLLQ(d, &ack, e->cli)) Log("Error: LLQCompleteHandshake"); + } + +mDNSlocal void LLQSetupChallenge(DaemonInfo *d, LLQEntry *e, LLQOptData *llq, mDNSOpaque16 msgID) + { + struct timeval t; + mDNSu32 randval; + PktMsg challenge; + mDNSu8 *end = challenge.msg.data; + AuthRecord opt; + + if (e->state == ChallengeSent) VLog("Retransmitting LLQ setup challenge for %##s", e->qname.c); + else VLog("Sending LLQ setup challenge for %##s", e->qname.c); + + if (!ZERO_LLQID(llq->id)) { Log("Error: LLQSetupChallenge - nonzero ID"); return; } // server bug + if (llq->llqOp != kLLQOp_Setup) { Log("LLQSetupChallenge - incorrrect operation from client"); return; } // client error + + if (ZERO_LLQID(e->id)) // don't regenerate random ID for retransmissions + { + // construct ID