--- /dev/null
+APPLE PUBLIC SOURCE LICENSE
+Version 2.0 - August 6, 2003
+
+Please read this License carefully before downloading this software.
+By downloading or using this software, you are agreeing to be bound by
+the terms of this License. If you do not or cannot agree to the terms
+of this License, please do not download or use the software.
+
+1. General; Definitions. This License applies to any program or other
+work which Apple Computer, Inc. ("Apple") makes publicly available and
+which contains a notice placed by Apple identifying such program or
+work as "Original Code" and stating that it is subject to the terms of
+this Apple Public Source License version 2.0 ("License"). As used in
+this License:
+
+1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is
+the grantor of rights, (i) claims of patents that are now or hereafter
+acquired, owned by or assigned to Apple and (ii) that cover subject
+matter contained in the Original Code, but only to the extent
+necessary to use, reproduce and/or distribute the Original Code
+without infringement; and (b) in the case where You are the grantor of
+rights, (i) claims of patents that are now or hereafter acquired,
+owned by or assigned to You and (ii) that cover subject matter in Your
+Modifications, taken alone or in combination with Original Code.
+
+1.2 "Contributor" means any person or entity that creates or
+contributes to the creation of Modifications.
+
+1.3 "Covered Code" means the Original Code, Modifications, the
+combination of Original Code and any Modifications, and/or any
+respective portions thereof.
+
+1.4 "Externally Deploy" means: (a) to sublicense, distribute or
+otherwise make Covered Code available, directly or indirectly, to
+anyone other than You; and/or (b) to use Covered Code, alone or as
+part of a Larger Work, in any way to provide a service, including but
+not limited to delivery of content, through electronic communication
+with a client other than You.
+
+1.5 "Larger Work" means a work which combines Covered Code or portions
+thereof with code not governed by the terms of this License.
+
+1.6 "Modifications" mean any addition to, deletion from, and/or change
+to, the substance and/or structure of the Original Code, any previous
+Modifications, the combination of Original Code and any previous
+Modifications, and/or any respective portions thereof. When code is
+released as a series of files, a Modification is: (a) any addition to
+or deletion from the contents of a file containing Covered Code;
+and/or (b) any new file or other representation of computer program
+statements that contains any part of Covered Code.
+
+1.7 "Original Code" means (a) the Source Code of a program or other
+work as originally made available by Apple under this License,
+including the Source Code of any updates or upgrades to such programs
+or works made available by Apple under this License, and that has been
+expressly identified by Apple as such in the header file(s) of such
+work; and (b) the object code compiled from such Source Code and
+originally made available by Apple under this License.
+
+1.8 "Source Code" means the human readable form of a program or other
+work that is suitable for making modifications to it, including all
+modules it contains, plus any associated interface definition files,
+scripts used to control compilation and installation of an executable
+(object code).
+
+1.9 "You" or "Your" means an individual or a legal entity exercising
+rights under this License. For legal entities, "You" or "Your"
+includes any entity which controls, is controlled by, or is under
+common control with, You, where "control" means (a) the power, direct
+or indirect, to cause the direction or management of such entity,
+whether by contract or otherwise, or (b) ownership of fifty percent
+(50%) or more of the outstanding shares or beneficial ownership of
+such entity.
+
+2. Permitted Uses; Conditions & Restrictions. Subject to the terms
+and conditions of this License, Apple hereby grants You, effective on
+the date You accept this License and download the Original Code, a
+world-wide, royalty-free, non-exclusive license, to the extent of
+Apple's Applicable Patent Rights and copyrights covering the Original
+Code, to do the following:
+
+2.1 Unmodified Code. You may use, reproduce, display, perform,
+internally distribute within Your organization, and Externally Deploy
+verbatim, unmodified copies of the Original Code, for commercial or
+non-commercial purposes, provided that in each instance:
+
+(a) You must retain and reproduce in all copies of Original Code the
+copyright and other proprietary notices and disclaimers of Apple as
+they appear in the Original Code, and keep intact all notices in the
+Original Code that refer to this License; and
+
+(b) You must include a copy of this License with every copy of Source
+Code of Covered Code and documentation You distribute or Externally
+Deploy, and You may not offer or impose any terms on such Source Code
+that alter or restrict this License or the recipients' rights
+hereunder, except as permitted under Section 6.
+
+2.2 Modified Code. You may modify Covered Code and use, reproduce,
+display, perform, internally distribute within Your organization, and
+Externally Deploy Your Modifications and Covered Code, for commercial
+or non-commercial purposes, provided that in each instance You also
+meet all of these conditions:
+
+(a) You must satisfy all the conditions of Section 2.1 with respect to
+the Source Code of the Covered Code;
+
+(b) You must duplicate, to the extent it does not already exist, the
+notice in Exhibit A in each file of the Source Code of all Your
+Modifications, and cause the modified files to carry prominent notices
+stating that You changed the files and the date of any change; and
+
+(c) If You Externally Deploy Your Modifications, You must make
+Source Code of all Your Externally Deployed Modifications either
+available to those to whom You have Externally Deployed Your
+Modifications, or publicly available. Source Code of Your Externally
+Deployed Modifications must be released under the terms set forth in
+this License, including the license grants set forth in Section 3
+below, for as long as you Externally Deploy the Covered Code or twelve
+(12) months from the date of initial External Deployment, whichever is
+longer. You should preferably distribute the Source Code of Your
+Externally Deployed Modifications electronically (e.g. download from a
+web site).
+
+2.3 Distribution of Executable Versions. In addition, if You
+Externally Deploy Covered Code (Original Code and/or Modifications) in
+object code, executable form only, You must include a prominent
+notice, in the code itself as well as in related documentation,
+stating that Source Code of the Covered Code is available under the
+terms of this License with information on how and where to obtain such
+Source Code.
+
+2.4 Third Party Rights. You expressly acknowledge and agree that
+although Apple and each Contributor grants the licenses to their
+respective portions of the Covered Code set forth herein, no
+assurances are provided by Apple or any Contributor that the Covered
+Code does not infringe the patent or other intellectual property
+rights of any other entity. Apple and each Contributor disclaim any
+liability to You for claims brought by any other entity based on
+infringement of intellectual property rights or otherwise. As a
+condition to exercising the rights and licenses granted hereunder, You
+hereby assume sole responsibility to secure any other intellectual
+property rights needed, if any. For example, if a third party patent
+license is required to allow You to distribute the Covered Code, it is
+Your responsibility to acquire that license before distributing the
+Covered Code.
+
+3. Your Grants. In consideration of, and as a condition to, the
+licenses granted to You under this License, You hereby grant to any
+person or entity receiving or distributing Covered Code under this
+License a non-exclusive, royalty-free, perpetual, irrevocable license,
+under Your Applicable Patent Rights and other intellectual property
+rights (other than patent) owned or controlled by You, to use,
+reproduce, display, perform, modify, sublicense, distribute and
+Externally Deploy Your Modifications of the same scope and extent as
+Apple's licenses under Sections 2.1 and 2.2 above.
+
+4. Larger Works. You may create a Larger Work by combining Covered
+Code with other code not governed by the terms of this License and
+distribute the Larger Work as a single product. In each such instance,
+You must make sure the requirements of this License are fulfilled for
+the Covered Code or any portion thereof.
+
+5. Limitations on Patent License. Except as expressly stated in
+Section 2, no other patent rights, express or implied, are granted by
+Apple herein. Modifications and/or Larger Works may require additional
+patent licenses from Apple which Apple may grant in its sole
+discretion.
+
+6. Additional Terms. You may choose to offer, and to charge a fee for,
+warranty, support, indemnity or liability obligations and/or other
+rights consistent with the scope of the license granted herein
+("Additional Terms") to one or more recipients of Covered Code.
+However, You may do so only on Your own behalf and as Your sole
+responsibility, and not on behalf of Apple or any Contributor. You
+must obtain the recipient's agreement that any such Additional Terms
+are offered by You alone, and You hereby agree to indemnify, defend
+and hold Apple and every Contributor harmless for any liability
+incurred by or claims asserted against Apple or such Contributor by
+reason of any such Additional Terms.
+
+7. Versions of the License. Apple may publish revised and/or new
+versions of this License from time to time. Each version will be given
+a distinguishing version number. Once Original Code has been published
+under a particular version of this License, You may continue to use it
+under the terms of that version. You may also choose to use such
+Original Code under the terms of any subsequent version of this
+License published by Apple. No one other than Apple has the right to
+modify the terms applicable to Covered Code created under this
+License.
+
+8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in
+part pre-release, untested, or not fully tested works. The Covered
+Code may contain errors that could cause failures or loss of data, and
+may be incomplete or contain inaccuracies. You expressly acknowledge
+and agree that use of the Covered Code, or any portion thereof, is at
+Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND
+WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND
+APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE
+PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM
+ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT
+NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF
+MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR
+PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD
+PARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST
+INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE
+FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS,
+THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR
+ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO
+ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE
+AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY.
+You acknowledge that the Covered Code is not intended for use in the
+operation of nuclear facilities, aircraft navigation, communication
+systems, or air traffic control machines in which case the failure of
+the Covered Code could lead to death, personal injury, or severe
+physical or environmental damage.
+
+9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO
+EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL,
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING
+TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR
+ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY,
+TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF
+APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY
+REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF
+INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY
+TO YOU. In no event shall Apple's total liability to You for all
+damages (other than as may be required by applicable law) under this
+License exceed the amount of fifty dollars ($50.00).
+
+10. Trademarks. This License does not grant any rights to use the
+trademarks or trade names "Apple", "Apple Computer", "Mac", "Mac OS",
+"QuickTime", "QuickTime Streaming Server" or any other trademarks,
+service marks, logos or trade names belonging to Apple (collectively
+"Apple Marks") or to any trademark, service mark, logo or trade name
+belonging to any Contributor. You agree not to use any Apple Marks in
+or as part of the name of products derived from the Original Code or
+to endorse or promote products derived from the Original Code other
+than as expressly permitted by and in strict compliance at all times
+with Apple's third party trademark usage guidelines which are posted
+at http://www.apple.com/legal/guidelinesfor3rdparties.html.
+
+11. Ownership. Subject to the licenses granted under this License,
+each Contributor retains all rights, title and interest in and to any
+Modifications made by such Contributor. Apple retains all rights,
+title and interest in and to the Original Code and any Modifications
+made by or on behalf of Apple ("Apple Modifications"), and such Apple
+Modifications will not be automatically subject to this License. Apple
+may, at its sole discretion, choose to license such Apple
+Modifications under this License, or on different terms from those
+contained in this License or may choose not to license them at all.
+
+12. Termination.
+
+12.1 Termination. This License and the rights granted hereunder will
+terminate:
+
+(a) automatically without notice from Apple if You fail to comply with
+any term(s) of this License and fail to cure such breach within 30
+days of becoming aware of such breach;
+
+(b) immediately in the event of the circumstances described in Section
+13.5(b); or
+
+(c) automatically without notice from Apple if You, at any time during
+the term of this License, commence an action for patent infringement
+against Apple; provided that Apple did not first commence
+an action for patent infringement against You in that instance.
+
+12.2 Effect of Termination. Upon termination, You agree to immediately
+stop any further use, reproduction, modification, sublicensing and
+distribution of the Covered Code. All sublicenses to the Covered Code
+which have been properly granted prior to termination shall survive
+any termination of this License. Provisions which, by their nature,
+should remain in effect beyond the termination of this License shall
+survive, including but not limited to Sections 3, 5, 8, 9, 10, 11,
+12.2 and 13. No party will be liable to any other for compensation,
+indemnity or damages of any sort solely as a result of terminating
+this License in accordance with its terms, and termination of this
+License will be without prejudice to any other right or remedy of
+any party.
+
+13. Miscellaneous.
+
+13.1 Government End Users. The Covered Code is a "commercial item" as
+defined in FAR 2.101. Government software and technical data rights in
+the Covered Code include only those rights customarily provided to the
+public as defined in this License. This customary commercial license
+in technical data and software is provided in accordance with FAR
+12.211 (Technical Data) and 12.212 (Computer Software) and, for
+Department of Defense purchases, DFAR 252.227-7015 (Technical Data --
+Commercial Items) and 227.7202-3 (Rights in Commercial Computer
+Software or Computer Software Documentation). Accordingly, all U.S.
+Government End Users acquire Covered Code with only those rights set
+forth herein.
+
+13.2 Relationship of Parties. This License will not be construed as
+creating an agency, partnership, joint venture or any other form of
+legal association between or among You, Apple or any Contributor, and
+You will not represent to the contrary, whether expressly, by
+implication, appearance or otherwise.
+
+13.3 Independent Development. Nothing in this License will impair
+Apple's right to acquire, license, develop, have others develop for
+it, market and/or distribute technology or products that perform the
+same or similar functions as, or otherwise compete with,
+Modifications, Larger Works, technology or products that You may
+develop, produce, market or distribute.
+
+13.4 Waiver; Construction. Failure by Apple or any Contributor to
+enforce any provision of this License will not be deemed a waiver of
+future enforcement of that or any other provision. Any law or
+regulation which provides that the language of a contract shall be
+construed against the drafter will not apply to this License.
+
+13.5 Severability. (a) If for any reason a court of competent
+jurisdiction finds any provision of this License, or portion thereof,
+to be unenforceable, that provision of the License will be enforced to
+the maximum extent permissible so as to effect the economic benefits
+and intent of the parties, and the remainder of this License will
+continue in full force and effect. (b) Notwithstanding the foregoing,
+if applicable law prohibits or restricts You from fully and/or
+specifically complying with Sections 2 and/or 3 or prevents the
+enforceability of either of those Sections, this License will
+immediately terminate and You must immediately discontinue any use of
+the Covered Code and destroy all copies of it that are in your
+possession or control.
+
+13.6 Dispute Resolution. Any litigation or other dispute resolution
+between You and Apple relating to this License shall take place in the
+Northern District of California, and You and Apple hereby consent to
+the personal jurisdiction of, and venue in, the state and federal
+courts within that District with respect to this License. The
+application of the United Nations Convention on Contracts for the
+International Sale of Goods is expressly excluded.
+
+13.7 Entire Agreement; Governing Law. This License constitutes the
+entire agreement between the parties with respect to the subject
+matter hereof. This License shall be governed by the laws of the
+United States and the State of California, except that body of
+California law concerning conflicts of law.
+
+Where You are located in the province of Quebec, Canada, the following
+clause applies: The parties hereby confirm that they have requested
+that this License and all related documents be drafted in English. Les
+parties ont exige que le present contrat et tous les documents
+connexes soient rediges en anglais.
+
+EXHIBIT A.
+
+"Portions 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."
+++ /dev/null
-/*
- * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * The contents of this file constitute Original Code as defined in
- * and are subject to the Apple Public Source License Version 1.1
- * (the "License"). You may not use this file except in compliance
- * with the License. Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
- *
- * This 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 OR NON-INFRINGEMENT. Please see the
- * License for the specific language governing rights and limitations
- * under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-// ***************************************************************************
-// mDNS-CFSocket.c:
-// Supporting routines to run mDNS on a CFRunLoop platform
-// ***************************************************************************
-
-// Open Transport 2.7.x on Mac OS 9 used to send Multicast DNS queries to UDP port 53,
-// before the Multicast DNS port was changed to 5353. For this reason, the mDNSResponder
-// in earlier versions of Mac OS X 10.2 Jaguar used to set mDNS_AllowPort53 to 1 to allow
-// it to also listen and answer queries on UDP port 53. Now that Transport 2.8 (included in
-// the Classic subsystem of Mac OS X 10.2 Jaguar) has been corrected to issue Multicast DNS
-// queries on UDP port 5353, this backwards-compatibility legacy support is no longer needed.
-#define mDNS_AllowPort53 1
-
-// Normally mDNSResponder is advertising local services on all active interfaces.
-// However, should you wish to build a query-only mDNS client, setting mDNS_AdvertiseLocalAddresses
-// to zero will cause CFSocket.c to not set the Advertise flag in its mDNS_RegisterInterface calls.
-int mDNS_AdvertiseLocalAddresses = 1;
-
-void (*NotifyClientNetworkChanged)(void);
-
-#include "mDNSClientAPI.h" // Defines the interface provided to the client layer above
-#include "mDNSPlatformFunctions.h" // Defines the interface to the supporting layer below
-#include "mDNSPlatformEnvironment.h" // Defines the specific types needed to run mDNS on this platform
-#include "mDNSvsprintf.h" // Used to implement debugf_();
-
-#include <stdio.h>
-#include <stdarg.h> // For va_list support
-#include <net/if.h>
-#include <net/if_dl.h>
-#include <sys/uio.h>
-#include <sys/param.h>
-#include <sys/socket.h>
-
-// 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 <sys/ioctl.h>
-#include <sys/sockio.h>
-#define ifaddrs ifa_info
-#ifndef ifa_broadaddr
-#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */
-#endif
-#include <sys/cdefs.h>
-
-#else
-
-#include <ifaddrs.h>
-
-#endif
-
-#include <IOKit/IOKitLib.h>
-#include <IOKit/IOMessage.h>
-
-extern void LogErrorMessage(const char *format, ...);
-
-// ***************************************************************************
-// Structures
-
-typedef struct NetworkInterfaceInfo2_struct NetworkInterfaceInfo2;
-struct NetworkInterfaceInfo2_struct
- {
- NetworkInterfaceInfo ifinfo;
- mDNS *m;
- char *ifa_name;
- NetworkInterfaceInfo2 *alias;
- int socket;
- CFSocketRef cfsocket;
-#if mDNS_AllowPort53
- int socket53;
- CFSocketRef cfsocket53;
-#endif
- };
-
-// ***************************************************************************
-// Functions
-
-mDNSexport void debugf_(const char *format, ...)
- {
- unsigned char buffer[512];
- va_list ptr;
- va_start(ptr,format);
- buffer[mDNS_vsprintf((char *)buffer, format, ptr)] = 0;
- va_end(ptr);
- fprintf(stderr, "%s\n", buffer);
- fflush(stderr);
- }
-
-mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end,
- mDNSIPAddr src, mDNSIPPort srcport, mDNSIPAddr dst, mDNSIPPort dstport)
- {
- NetworkInterfaceInfo2 *info = (NetworkInterfaceInfo2 *)(m->HostInterfaces);
- struct sockaddr_in to;
- to.sin_family = AF_INET;
- to.sin_port = dstport.NotAnInteger;
- to.sin_addr.s_addr = dst. NotAnInteger;
-
- if (src.NotAnInteger == 0) debugf("mDNSPlatformSendUDP ERROR! Cannot send from zero source address");
-
- while (info)
- {
- if (info->ifinfo.ip.NotAnInteger == src.NotAnInteger)
- {
- int s, err;
- if (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) s = info->socket;
-#if mDNS_AllowPort53
- else if (srcport.NotAnInteger == UnicastDNSPort.NotAnInteger ) s = info->socket53;
-#endif
- else { debugf("Source port %d not allowed", (mDNSu16)srcport.b[0]<<8 | srcport.b[1]); return(-1); }
- err = sendto(s, msg, (UInt8*)end - (UInt8*)msg, 0, (struct sockaddr *)&to, sizeof(to));
- if (err < 0) { perror("mDNSPlatformSendUDP sendto"); return(err); }
- }
- info = (NetworkInterfaceInfo2 *)(info->ifinfo.next);
- }
-
- return(mStatus_NoError);
- }
-
-static ssize_t myrecvfrom(const int s, void *const buffer, const size_t max,
- struct sockaddr *const from, size_t *const fromlen, struct in_addr *dstaddr, char ifname[128])
- {
- static int numLogMessages = 0;
- struct iovec databuffers = { (char *)buffer, max };
- struct msghdr msg;
- ssize_t n;
- struct cmsghdr *cmPtr;
- char ancillary[1024];
-
- // 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 (numLogMessages++ < 100) LogErrorMessage("CFSocket.c: recvmsg(%d) returned error %d errno %d", s, n, errno);
- return(-1);
- }
- if (msg.msg_controllen < sizeof(struct cmsghdr))
- {
- if (numLogMessages++ < 100) LogErrorMessage("CFSocket.c: recvmsg(%d) msg.msg_controllen %d < sizeof(struct cmsghdr) %d",
- s, msg.msg_controllen, sizeof(struct cmsghdr));
- return(-1);
- }
- if (msg.msg_flags & MSG_CTRUNC)
- {
- if (numLogMessages++ < 100) LogErrorMessage("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 = *(struct in_addr *)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 < sizeof(ifname))
- {
- 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);
- }
- }
- }
-
- return(n);
- }
-
-mDNSlocal void myCFSocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *context)
- {
- mDNSIPAddr senderaddr, destaddr;
- mDNSIPPort senderport;
- NetworkInterfaceInfo2 *info = (NetworkInterfaceInfo2 *)context;
- mDNS *const m = info->m;
- DNSMessage packet;
- struct in_addr to;
- struct sockaddr_in from;
- size_t fromlen = sizeof(from);
- char packetifname[128] = "";
- int err;
- int s1 = -1;
-
- (void)address; // Parameter not used
- (void)data; // Parameter not used
-
- if (type != kCFSocketReadCallBack) LogErrorMessage("CFSocketCallBack: CallBackType %d is not kCFSocketReadCallBack", type);
-#if mDNS_AllowPort53
- if (s == info->cfsocket53)
- s1 = info->socket53;
- else
-#endif
- if (s == info->cfsocket)
- s1 = info->socket;
-
- err = myrecvfrom(s1, &packet, sizeof(packet), (struct sockaddr *)&from, &fromlen, &to, packetifname);
-
- if (err < 0 || s1 < 0 || s1 != CFSocketGetNative(s))
- {
- LogErrorMessage("CFSocketCallBack: s1 %d native socket %d", s1, CFSocketGetNative(s));
- LogErrorMessage("CFSocketCallBack: cfs %X, cfsocket53 %X, cfsocket %X", s, info->cfsocket53, info->cfsocket);
- LogErrorMessage("CFSocketCallBack: skt53 %X, sktv4 %X", info->socket53, info->socket);
- LogErrorMessage("CFSocketCallBack recvfrom(%d) error %d errno %d", s1, err, errno);
- return;
- }
-
- senderaddr.NotAnInteger = from.sin_addr.s_addr;
- senderport.NotAnInteger = from.sin_port;
- destaddr.NotAnInteger = to.s_addr;
-
- // 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 (strcmp(info->ifa_name, packetifname))
- {
- verbosedebugf("myCFSocketCallBack got a packet from %.4a to %.4a on interface %.4a/%s (Ignored -- really arrived on interface %s)",
- &senderaddr, &destaddr, &info->ifinfo.ip, info->ifa_name, packetifname);
- return;
- }
- else
- verbosedebugf("myCFSocketCallBack got a packet from %.4a to %.4a on interface %.4a/%s",
- &senderaddr, &destaddr, &info->ifinfo.ip, info->ifa_name);
-
- if (err < sizeof(DNSMessageHeader)) { debugf("myCFSocketCallBack packet length (%d) too short", err); return; }
-
-#if mDNS_AllowPort53
- if (s == info->cfsocket53)
- mDNSCoreReceive(m, &packet, (unsigned char*)&packet + err, senderaddr, senderport, destaddr, UnicastDNSPort, info->ifinfo.ip);
- else
-#endif
- mDNSCoreReceive(m, &packet, (unsigned char*)&packet + err, senderaddr, senderport, destaddr, MulticastDNSPort, info->ifinfo.ip);
- }
-
-mDNSlocal void myCFRunLoopTimerCallBack(CFRunLoopTimerRef timer, void *info)
- {
- (void)timer; // Parameter not used
- mDNSCoreTask((mDNS *const)info);
- }
-
-// 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);
- }
- }
-
-mDNSlocal mStatus SetupSocket(struct sockaddr_in *ifa_addr, mDNSIPPort port, int *s, CFSocketRef *c, CFSocketContext *context)
- {
- mStatus err;
- const int on = 1;
- const int twofivefive = 255;
- struct ip_mreq imr;
- struct sockaddr_in listening_sockaddr;
- CFRunLoopSourceRef rls;
-
- if (*s > 0) { LogErrorMessage("SetupSocket ERROR: socket %d is already set", *s); return(-1); }
- if (*c) { LogErrorMessage("SetupSocket ERROR: CFSocketRef %X is already set", *c); return(-1); }
-
- // Open the socket...
- *s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
- *c = NULL;
- if (*s < 0) { perror("socket"); return(*s); }
-
- // ... with a shared UDP port
- err = setsockopt(*s, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on));
- if (err < 0) { perror("setsockopt - SO_REUSEPORT"); return(err); }
-
- // We want to receive destination addresses
- err = setsockopt(*s, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on));
- if (err < 0) { perror("setsockopt - IP_RECVDSTADDR"); return(err); }
-
- // We want to receive interface identifiers
- err = setsockopt(*s, IPPROTO_IP, IP_RECVIF, &on, sizeof(on));
- if (err < 0) { perror("setsockopt - IP_RECVIF"); return(err); }
-
- // Add multicast group membership on this interface
- imr.imr_multiaddr.s_addr = AllDNSLinkGroup.NotAnInteger;
- imr.imr_interface = ifa_addr->sin_addr;
- err = setsockopt(*s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(struct ip_mreq));
- if (err < 0) { perror("setsockopt - IP_ADD_MEMBERSHIP"); return(err); }
-
- // Specify outgoing interface too
- err = setsockopt(*s, IPPROTO_IP, IP_MULTICAST_IF, &ifa_addr->sin_addr, sizeof(ifa_addr->sin_addr));
- if (err < 0) { perror("setsockopt - IP_MULTICAST_IF"); return(err); }
-
- // Send unicast packets with TTL 255
- err = setsockopt(*s, IPPROTO_IP, IP_TTL, &twofivefive, sizeof(twofivefive));
- if (err < 0) { perror("setsockopt - IP_TTL"); return(err); }
-
- // And multicast packets with TTL 255 too
- err = setsockopt(*s, IPPROTO_IP, IP_MULTICAST_TTL, &twofivefive, sizeof(twofivefive));
- if (err < 0) { perror("setsockopt - IP_MULTICAST_TTL"); return(err); }
-
- // And start listening for packets
- 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(*s, (struct sockaddr *) &listening_sockaddr, sizeof(listening_sockaddr));
- if (err)
- {
- if (port.NotAnInteger == UnicastDNSPort.NotAnInteger) err = 0;
- else perror("bind");
- return(err);
- }
-
- *c = CFSocketCreateWithNative(kCFAllocatorDefault, *s, kCFSocketReadCallBack, myCFSocketCallBack, context);
- rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, *c, 0);
- CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
- CFRelease(rls);
-
- return(err);
- }
-
-#if 0
-mDNSlocal NetworkInterfaceInfo2 *SearchForInterfaceByAddr(mDNS *const m, mDNSIPAddr ip)
- {
- NetworkInterfaceInfo2 *info = (NetworkInterfaceInfo2*)(m->HostInterfaces);
- while (info)
- {
- if (info->ifinfo.ip.NotAnInteger == ip.NotAnInteger) return(info);
- info = (NetworkInterfaceInfo2 *)(info->ifinfo.next);
- }
- return(NULL);
- }
-#endif
-
-mDNSlocal NetworkInterfaceInfo2 *SearchForInterfaceByName(mDNS *const m, char *ifname)
- {
- NetworkInterfaceInfo2 *info = (NetworkInterfaceInfo2*)(m->HostInterfaces);
- while (info)
- {
- if (!strcmp(info->ifa_name, ifname)) return(info);
- info = (NetworkInterfaceInfo2 *)(info->ifinfo.next);
- }
- return(NULL);
- }
-
-#if RUN_ON_PUMA_WITHOUT_IFADDRS
-
-/* 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 <net/if.h> */
-#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 <net/if.h> */
- 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);
- }
-
-#endif
-
-mDNSlocal mStatus SetupInterface(mDNS *const m, NetworkInterfaceInfo2 *info, struct ifaddrs *ifa)
- {
- mStatus err = 0;
- struct sockaddr_in *ifa_addr = (struct sockaddr_in *)ifa->ifa_addr;
- CFSocketContext myCFSocketContext = { 0, info, NULL, NULL, NULL };
-
- info->ifinfo.ip.NotAnInteger = ifa_addr->sin_addr.s_addr;
- info->ifinfo.Advertise = mDNS_AdvertiseLocalAddresses;
- info->m = m;
- info->ifa_name = (char *)mallocL("NetworkInterfaceInfo2 name", strlen(ifa->ifa_name) + 1);
- if (!info->ifa_name) return(-1);
- strcpy(info->ifa_name, ifa->ifa_name);
- info->alias = SearchForInterfaceByName(m, ifa->ifa_name);
- info->socket = 0;
- info->cfsocket = 0;
-#if mDNS_AllowPort53
- info->socket53 = 0;
- info->cfsocket53 = 0;
-#endif
-
- mDNS_RegisterInterface(m, &info->ifinfo);
-
- if (info->alias)
- debugf("SetupInterface: %s Flags %04X %.4a is an alias of %.4a",
- ifa->ifa_name, ifa->ifa_flags, &info->ifinfo.ip, &info->alias->ifinfo.ip);
-
-#if mDNS_AllowPort53
- err = SetupSocket(ifa_addr, UnicastDNSPort, &info->socket53, &info->cfsocket53, &myCFSocketContext);
-#endif
- if (!err)
- err = SetupSocket(ifa_addr, MulticastDNSPort, &info->socket, &info->cfsocket, &myCFSocketContext);
-
- debugf("SetupInterface: %s Flags %04X %.4a Registered socket53 %d socket5353 %d",
- ifa->ifa_name, ifa->ifa_flags, &info->ifinfo.ip, info->socket53, info->socket);
-
- return(err);
- }
-
-mDNSlocal void ClearInterfaceList(mDNS *const m)
- {
- while (m->HostInterfaces)
- {
- NetworkInterfaceInfo2 *info = (NetworkInterfaceInfo2*)(m->HostInterfaces);
- mDNS_DeregisterInterface(m, &info->ifinfo);
- if (info->ifa_name ) freeL("NetworkInterfaceInfo2 name", info->ifa_name);
- if (info->socket > 0) shutdown(info->socket, 2);
- if (info->cfsocket) { CFSocketInvalidate(info->cfsocket); CFRelease(info->cfsocket); }
-#if mDNS_AllowPort53
- if (info->socket53 > 0) shutdown(info->socket53, 2);
- if (info->cfsocket53) { CFSocketInvalidate(info->cfsocket53); CFRelease(info->cfsocket53); }
-#endif
- freeL("NetworkInterfaceInfo2", info);
- }
- }
-
-mDNSlocal mStatus SetupInterfaceList(mDNS *const m)
- {
- struct ifaddrs *ifalist;
- int err = getifaddrs(&ifalist);
- struct ifaddrs *ifa = ifalist;
- struct ifaddrs *theLoopback = NULL;
- if (err) return(err);
-
- // Set up the nice label
- m->nicelabel.c[0] = 0;
- GetUserSpecifiedFriendlyComputerName(&m->nicelabel);
- if (m->nicelabel.c[0] == 0) ConvertCStringToDomainLabel("Macintosh", &m->nicelabel);
-
- // Set up the RFC 1034-compliant label
- m->hostlabel.c[0] = 0;
- GetUserSpecifiedRFC1034ComputerName(&m->hostlabel);
- if (m->hostlabel.c[0] == 0) ConvertCStringToDomainLabel("Macintosh", &m->hostlabel);
-
- mDNS_GenerateFQDN(m);
-
- while (ifa)
- {
-#if 0
- if (ifa->ifa_addr->sa_family != AF_INET)
- debugf("SetupInterface: %s Flags %04X Family %d not AF_INET",
- ifa->ifa_name, ifa->ifa_flags, ifa->ifa_addr->sa_family);
- if (!(ifa->ifa_flags & IFF_UP))
- debugf("SetupInterface: %s Flags %04X Interface not IFF_UP", ifa->ifa_name, ifa->ifa_flags);
- if (ifa->ifa_flags & IFF_LOOPBACK)
- debugf("SetupInterface: %s Flags %04X Interface IFF_LOOPBACK", ifa->ifa_name, ifa->ifa_flags);
- if (ifa->ifa_flags & IFF_POINTOPOINT)
- debugf("SetupInterface: %s Flags %04X Interface IFF_POINTOPOINT", ifa->ifa_name, ifa->ifa_flags);
-#endif
- if (ifa->ifa_addr->sa_family == AF_INET && (ifa->ifa_flags & IFF_UP) &&
- !(ifa->ifa_flags & IFF_POINTOPOINT))
- {
- if (ifa->ifa_flags & IFF_LOOPBACK)
- theLoopback = ifa;
- else
- {
- NetworkInterfaceInfo2 *info = (NetworkInterfaceInfo2 *)mallocL("NetworkInterfaceInfo2", sizeof(*info));
- if (!info) debugf("SetupInterfaceList: Out of Memory!");
- else SetupInterface(m, info, ifa);
- }
- }
- ifa = ifa->ifa_next;
- }
-
- if (!m->HostInterfaces && theLoopback)
- {
- NetworkInterfaceInfo2 *info = (NetworkInterfaceInfo2 *)mallocL("NetworkInterfaceInfo2", sizeof(*info));
- if (!info) debugf("SetupInterfaceList: (theLoopback) Out of Memory!");
- else SetupInterface(m, info, theLoopback);
- }
-
- freeifaddrs(ifalist);
- return(err);
- }
-
-mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context)
- {
- mDNS *const m = (mDNS *const)context;
- debugf("*** Network Configuration Change ***");
- (void)store; // Parameter not used
- (void)changedKeys; // Parameter not used
-
- ClearInterfaceList(m);
- SetupInterfaceList(m);
- if (NotifyClientNetworkChanged) NotifyClientNetworkChanged();
- mDNSCoreSleep(m, false);
- }
-
-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 = SCDynamicStoreKeyCreateComputerName(NULL);
- CFStringRef key3 = SCDynamicStoreKeyCreateHostNames(NULL);
- CFStringRef pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4);
- CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
- CFMutableArrayRef patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
-
- if (!store) { fprintf(stderr, "SCDynamicStoreCreate failed: %s\n", SCErrorString(SCError())); goto error; }
- if (!key1 || !key2 || !key3 || !keys || !pattern || !patterns) goto error;
-
- CFArrayAppendValue(keys, key1);
- CFArrayAppendValue(keys, key2);
- CFArrayAppendValue(keys, key3);
- CFArrayAppendValue(patterns, pattern);
- if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns))
- { fprintf(stderr, "SCDynamicStoreSetNotificationKeys failed: %s\n", SCErrorString(SCError())); goto error; }
-
- m->p->StoreRLS = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
- if (!m->p->StoreRLS) { fprintf(stderr, "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 (pattern) CFRelease(pattern);
- 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"); mDNSCoreSleep(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"); mDNSCoreSleep(m, true); break; // E0000280
- case kIOMessageSystemWillNotSleep: debugf("PowerChanged kIOMessageSystemWillNotSleep (no action)"); break; // E0000290
- case kIOMessageSystemHasPoweredOn: debugf("PowerChanged kIOMessageSystemHasPoweredOn"); mDNSCoreSleep(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);
- }
-
-mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m)
- {
- mStatus err;
-
- CFRunLoopTimerContext myCFRunLoopTimerContext = { 0, m, NULL, NULL, NULL };
-
- // Note: Every CFRunLoopTimer has to be created with an initial fire time, and a repeat interval, or it becomes
- // a one-shot timer and you can't use CFRunLoopTimerSetNextFireDate(timer, when) to schedule subsequent firings.
- // Here we create it with an initial fire time ten seconds from now, and a repeat interval of ten seconds,
- // knowing that we'll reschedule it using CFRunLoopTimerSetNextFireDate(timer, when) long before that happens.
- m->p->CFTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + 10.0, 10.0, 0, 1,
- myCFRunLoopTimerCallBack, &myCFRunLoopTimerContext);
- CFRunLoopAddTimer(CFRunLoopGetCurrent(), m->p->CFTimer, kCFRunLoopDefaultMode);
-
- SetupInterfaceList(m);
-
- err = WatchForNetworkChanges(m);
- if (err) return(err);
-
- err = WatchForPowerChanges(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;
- }
-
- ClearInterfaceList(m);
-
- if (m->p->CFTimer)
- {
- CFRunLoopTimerInvalidate(m->p->CFTimer);
- CFRelease(m->p->CFTimer);
- m->p->CFTimer = NULL;
- }
- }
-
-mDNSexport void mDNSPlatformScheduleTask(const mDNS *const m, SInt32 NextTaskTime)
- {
- if (m->p->CFTimer)
- {
- CFAbsoluteTime ticks = (CFAbsoluteTime)(NextTaskTime - mDNSPlatformTimeNow());
- CFAbsoluteTime interval = ticks / (CFAbsoluteTime)mDNSPlatformOneSecond;
- CFRunLoopTimerSetNextFireDate(m->p->CFTimer, CFAbsoluteTimeGetCurrent() + interval);
- }
- }
-
-// Locking is a no-op here, because we're CFRunLoop-based, 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 UInt32 mDNSPlatformStrLen (const void *src) { return(strlen((char*)src)); }
-mDNSexport void mDNSPlatformMemCopy(const void *src, void *dst, UInt32 len) { memcpy(dst, src, len); }
-mDNSexport Boolean mDNSPlatformMemSame(const void *src, const void *dst, UInt32 len) { return(memcmp(dst, src, len) == 0); }
-mDNSexport void mDNSPlatformMemZero( void *dst, UInt32 len) { bzero(dst, len); }
-
-mDNSexport SInt32 mDNSPlatformTimeNow()
- {
- struct timeval tp;
- gettimeofday(&tp, NULL);
- // tp.tv_sec is seconds since 1st January 1970 (GMT, with no adjustment for daylight savings time)
- // tp.tv_usec is microseconds since the start of this second (i.e. values 0 to 999999)
- // We use the lower 22 bits of tp.tv_sec for the top 22 bits of our result
- // and we multiply tp.tv_usec by 16 / 15625 to get a value in the range 0-1023 to go in the bottom 10 bits.
- // This gives us a proper modular (cyclic) counter that has a resolution of roughly 1ms (actually 1/1024 second)
- // and correctly cycles every 2^22 seconds (4194304 seconds = approx 48 days).
- return( (tp.tv_sec << 10) | (tp.tv_usec * 16 / 15625) );
- }
-
-mDNSexport SInt32 mDNSPlatformOneSecond = 1024;
+++ /dev/null
-/*
- * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License"). You may not use this file except in compliance with the
- * License. Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
- *
- * This 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 OR NON-INFRINGEMENT. Please see the
- * License for the specific language governing rights and limitations
- * under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include <DNSServiceDiscovery/DNSServiceDiscoveryDefines.h>
-
+++ /dev/null
-/*
- * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License"). You may not use this file except in compliance with the
- * License. Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
- *
- * This 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 OR NON-INFRINGEMENT. Please see the
- * License for the specific language governing rights and limitations
- * under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
- #import "/AppleInternal/Developer/Headers/DNSServiceDiscovery/DNSServiceDiscoveryReply.defs"
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License"). You may not use this file except in compliance with the
- * License. Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
- *
- * This 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 OR NON-INFRINGEMENT. Please see the
- * License for the specific language governing rights and limitations
- * under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
- #import "/AppleInternal/Developer/Headers/DNSServiceDiscovery/DNSServiceDiscoveryRequest.defs"
\ No newline at end of file
--- /dev/null
+#
+# Top level makefile for Build & Integration.
+#
+# This file is used to facilitate checking the mDNSResponder project
+# directly out of CVS and submitting to B&I at Apple.
+#
+# The various platform directories contain makefiles or projects
+# specific to that platform.
+#
+# B&I builds must respect the following target:
+# install:
+# installsrc:
+# installhdrs:
+# clean:
+#
+
+include /Developer/Makefiles/pb_makefiles/platform.make
+
+MVERS=58
+
+install:
+ cd "$(SRCROOT)/mDNSMacOSX"; pbxbuild install OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS)
+
+installsrc:
+ ditto mDNSCore ${SRCROOT}/mDNSCore
+ ditto mDNSMacOSX ${SRCROOT}/mDNSMacOSX
+ ditto Makefile $(SRCROOT)
+
+installhdrs::
+ cd "$(SRCROOT)/mDNSMacOSX"; pbxbuild installhdrs OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS)
+
+clean::
+ echo clean
--- /dev/null
+What is mDNSResponder?
+----------------------
+
+The mDNSResponder project is a component of Rendezvous,
+Apple's ease-of-use IP networking initiative:
+<http://developer.apple.com/macosx/rendezvous/index.html>
+
+Apple's Rendezvous software derives from the ongoing standardization
+work of the IETF Zero Configuration Networking Working Group:
+<http://zeroconf.org/>
+
+The Zeroconf Working Group has identified three requirements for Zero
+Configuration Networking:
+1. An IP address (even when there is no DHCP server to assign one)
+2. Name-to-address translation (even when there is no DNS server)
+3. Discovery of Services on the network (again, without infrastucture)
+
+Requirement 1 is met by self-assigned link-local addresses, as
+described in "Dynamic Configuration of IPv4 Link-Local Addresses"
+<http://files.zeroconf.org/draft-ietf-zeroconf-ipv4-linklocal.txt>
+
+Requirement 2 is met by sending DNS-like queries via Multicast (mDNS).
+
+Requirement 3 is met by DNS Service Dicsovery (DNS-SD).
+
+Self-assigned link-local address capability has been available since
+1998, when it first appeared in Windows '98 and in Mac OS 8.5.
+Implementations for other platforms also exist.
+
+The mDNSResponder project allows us to meet requirements 2 and 3.
+It provides the ability for the user to identify hosts using names
+instead of dotted-decimal IP addresses, even if the user doesn't have a
+conventional DNS server set up. It also provides the ability for the
+user to discover what services are being advertised on the network,
+without having to know about them in advance, or configure the machines.
+
+The name "mDNS" was chosen because this protocol is designed to be,
+as much as possible, similar to conventional DNS. The main difference is
+that queries are sent via multicast to all local hosts, instead of via
+unicast to a specific known server. Every host on the local link runs an
+mDNSResponder which is constantly listening for those multicast queries,
+and if the mDNSResponder receives a query for which it knows the answer,
+then it responds. The mDNS protocol uses the same packet format as
+unicast DNS, and the same name structure, and the same DNS record types.
+The main difference is that queries are sent to a different UDP port
+(5353 instead of 53) and they are sent via multicast to address
+224.0.0.251. Another important difference is that all "mDNS" names
+end in ".local." When a user types "yourcomputer.local." into their Web
+browser, the presence of ".local." on the end of the name tells the host
+OS that the name should be looked up using local multicast instead of by
+sending that name to the worldwide DNS service for resolution. This
+helps reduce potential user confusion about whether a particular name
+is globally unique (e.g. "www.apple.com.") or whether that name has only
+local significance (e.g. "yourcomputer.local.").
+
+
+About the mDNSResponder Code
+----------------------------
+
+Because Apple benefits more from widespread adoption of Rendezvous than
+it would benefit from keeping Rendezvous proprietary, Apple is making
+this code open so that other developers can use it too.
+
+Because Apple recognises that networks are hetrogenous environments
+where devices run many different kinds of OS, this code has been made
+as portable as possible.
+
+A typical mDNS program contains three components:
+
+ +------------------+
+ | Application |
+ +------------------+
+ | mDNS Core |
+ +------------------+
+ | Platform Support |
+ +------------------+
+
+The "mDNS Core" layer is absolutely identical for all applications and
+all Operating Systems.
+
+The "Platform Support" layer provides the necessary supporting routines
+that are specific to each platform -- what routine do you call to send
+a UDP packet, what routine do you call to join multicast group, etc.
+
+The "Application" layer does whatever that particular application wants
+to do. It calls routines provided by the "mDNS Core" layer to perform
+the functions it needs --
+ * advertise services,
+ * browse for named instances of a particular type of service
+ * resolve a named instance to a specific IP address and port number,
+ * etc.
+The "mDNS Core" layer in turn calls through to the "Platform Support"
+layer to send and receive the multicast UDP packets to do the actual work.
+
+Apple currently provides a "Platform Support" layer for OS X 10.2
+("Jaguar"), and a "Platform Support" layer for other Posix platforms
+(OS X 10.1, Linux, etc.) Other support layers for platforms like Windows,
+VxWorks, etc. are also planned.
+
+Note: Developers writing applications for OS X 10.2 ("Jaguar") do not
+need to incorporate this code into their applications, since OS X 10.2
+provides a system service to handle this for them. If every application
+developer were to link-in the mDNSResponder code into their application,
+then we would end up with a situation like the picture below:
+
+ +------------------+ +------------------+ +------------------+
+ | Application 1 | | Application 2 | | Application 3 |
+ +------------------+ +------------------+ +------------------+
+ | mDNS Core | | mDNS Core | | mDNS Core |
+ +------------------+ +------------------+ +------------------+
+ | Platform Support | | Platform Support | | Platform Support |
+ +------------------+ +------------------+ +------------------+
+
+This would not be very efficient. Each separate application would be
+sending their own separate multicast UDP packets and maintaining their
+own list of answers. Because of this, OS X 10.2 provides a common system
+service which client software should access through the
+"DNSServiceDiscovery.h" APIs.
+
+The situation on OS X 10.2 looks more like the picture below:
+
+ -------------------
+ / \
+ +---------+ +------------------+ +---------+ \ +---------+
+ | App 1 |<-->| daemon.c |<-->| App 2 | ->| App 3 |
+ +---------+ +------------------+ +---------+ +---------+
+ | mDNS Core |
+ +------------------+
+ | Platform Support |
+ +------------------+
+
+Applications on OS X 10.2 make calls to the single mDNSResponder daemon
+which implements the mDNS and DNS-SD protocols.
+
+Vendors of products such as printers, which are closed environments not
+expecting to be running third-party application software, can reasonably
+implement a single monolithic mDNSResponder to advertise all the
+services of that device. Vendors of open systems which run third-party
+application software should implement a system service such as the one
+provided by the OS X mDNSResponder daemon, and application software on
+that platform should, where possible, make use of that system service
+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.
+++ /dev/null
-/*
- * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License"). You may not use this file except in compliance with the
- * License. Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
- *
- * This 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 OR NON-INFRINGEMENT. Please see the
- * License for the specific language governing rights and limitations
- * under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-/*
- * Formatting notes:
- * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
- * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
- * but for the sake of brevity here I will say just this: Curly braces are not syntactially
- * part of an "if" statement; they are the beginning and ending markers of a compound statement;
- * therefore common sense dictates that if they are part of a compound statement then they
- * should be indented to the same level as everything else in that compound statement.
- * Indenting curly braces at the same level as the "if" implies that curly braces are
- * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
- * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
- * 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.)
- */
-
-#include <libc.h>
-#include <arpa/nameser.h>
-#include <CoreFoundation/CoreFoundation.h>
-#include <DNSServiceDiscovery/DNSServiceDiscovery.h>
-
-//*************************************************************************************************************
-// Globals
-
-typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16;
-
-static char operation;
-static dns_service_discovery_ref client = NULL;
-static char addtest = 0;
-static DNSRecordReference record;
-static char myhinfo9[11] = "\003Mac\006OS 9.2";
-static char myhinfoX[ 9] = "\003Mac\004OS X";
-static char updatetest[2] = "\001A";
-static char bigNULL[4096];
-
-//*************************************************************************************************************
-// Supporting Utility Functions
-//
-// This code takes care of:
-// 1. Extracting the mach_port_t from the dns_service_discovery_ref
-// 2. Making a CFMachPortRef from it
-// 3. Making a CFRunLoopSourceRef from that
-// 4. Adding that source to the current RunLoop
-// 5. and passing the resulting messages back to DNSServiceDiscovery_handleReply() for processing
-//
-// Code that's not based around a CFRunLoop will need its own mechanism to receive Mach messages
-// from the mDNSResponder daemon and pass them to the DNSServiceDiscovery_handleReply() routine.
-// (There is no way to automate this, because it varies depending on the application's existing
-// event handling model.)
-
-static void MyHandleMachMessage(CFMachPortRef port, void *msg, CFIndex size, void *info)
- {
- DNSServiceDiscovery_handleReply(msg);
- }
-
-static int AddDNSServiceClientToRunLoop(dns_service_discovery_ref client)
- {
- mach_port_t port = DNSServiceDiscoveryMachPort(client);
- if (!port)
- return(-1);
- else
- {
- CFMachPortContext context = { 0, 0, NULL, NULL, NULL };
- Boolean shouldFreeInfo;
- CFMachPortRef cfMachPort = CFMachPortCreateWithPort(kCFAllocatorDefault, port, MyHandleMachMessage, &context, &shouldFreeInfo);
- CFRunLoopSourceRef rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0);
- CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
- CFRelease(rls);
- return(0);
- }
- }
-
-//*************************************************************************************************************
-// Sample callback functions for each of the operation types
-
-#define DomainMsg(X) ((X) == DNSServiceDomainEnumerationReplyAddDomain ? "Added" : \
- (X) == DNSServiceDomainEnumerationReplyAddDomainDefault ? "(Default)" : \
- (X) == DNSServiceDomainEnumerationReplyRemoveDomain ? "Removed" : "Unknown")
-
-static void regdom_reply(DNSServiceDomainEnumerationReplyResultType resultType, const char *replyDomain,
- DNSServiceDiscoveryReplyFlags flags, void *context)
- {
- printf("Recommended Registration Domain %s %s", replyDomain, DomainMsg(resultType));
- if (flags) printf(" Flags: %X", flags);
- printf("\n");
- }
-
-static void browsedom_reply(DNSServiceDomainEnumerationReplyResultType resultType, const char *replyDomain,
- DNSServiceDiscoveryReplyFlags flags, void *context)
- {
- printf("Recommended Browsing Domain %s %s", replyDomain, DomainMsg(resultType));
- if (flags) printf(" Flags: %X", flags);
- printf("\n");
- }
-
-static void browse_reply(DNSServiceBrowserReplyResultType resultType,
- const char *replyName, const char *replyType, const char *replyDomain, DNSServiceDiscoveryReplyFlags flags, void *context)
- {
- char *op = (resultType == DNSServiceBrowserReplyAddInstance) ? "Found" : "Removed";
- printf("Service \"%s\", type \"%s\", domain \"%s\" %s", replyName, replyType, replyDomain, op);
- if (flags) printf(" Flags: %X", flags);
- printf("\n");
- }
-
-static void resolve_reply(struct sockaddr *interface, struct sockaddr *address, const char *txtRecord, DNSServiceDiscoveryReplyFlags flags, void *context)
- {
- if (address->sa_family != AF_INET)
- printf("Unknown address family %d\n", address->sa_family);
- else
- {
- struct sockaddr_in *ip = (struct sockaddr_in *)address;
- union { uint32_t l; u_char b[4]; } addr = { ip->sin_addr.s_addr };
- union { uint16_t s; u_char b[2]; } port = { ip->sin_port };
- uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1];
- const char *src = txtRecord;
- printf("Service can be reached at %d.%d.%d.%d:%u", addr.b[0], addr.b[1], addr.b[2], addr.b[3], PortAsNumber);
- while (*src)
- {
- char txtInfo[256];
- char *dst = txtInfo;
- const char *const lim = &txtInfo[sizeof(txtInfo)];
- while (*src && *src != 1 && dst < lim-1) *dst++ = *src++;
- *dst++ = 0;
- printf(" TXT \"%s\"", txtInfo);
- if (*src == 1) src++;
- }
- if (flags) printf(" Flags: %X", flags);
- printf("\n");
- }
- }
-
-static void myCFRunLoopTimerCallBack(CFRunLoopTimerRef timer, void *info)
- {
- (void)timer; // Parameter not used
- (void)info; // Parameter not used
-
- switch (operation)
- {
- case 'A':
- {
- switch (addtest)
- {
- case 0: printf("Adding Test HINFO record\n");
- record = DNSServiceRegistrationAddRecord(client, T_HINFO, sizeof(myhinfo9), &myhinfo9[0], 120);
- addtest = 1;
- break;
- case 1: printf("Updating Test HINFO record\n");
- DNSServiceRegistrationUpdateRecord(client, record, sizeof(myhinfoX), &myhinfoX[0], 120);
- addtest = 2;
- break;
- case 2: printf("Removing Test HINFO record\n");
- DNSServiceRegistrationRemoveRecord(client, record);
- addtest = 0;
- break;
- }
- }
- break;
-
- case 'U':
- {
- if (updatetest[1] != 'Z') updatetest[1]++;
- else updatetest[1] = 'A';
- printf("Updating Test TXT record to %c\n", updatetest[1]);
- DNSServiceRegistrationUpdateRecord(client, 0, sizeof(updatetest), &updatetest[0], 120);
- }
- break;
-
- case 'N':
- {
- printf("Adding big NULL record\n");
- DNSServiceRegistrationAddRecord(client, T_NULL, sizeof(bigNULL), &bigNULL[0], 120);
- CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
- }
- break;
- }
- }
-
-static void reg_reply(DNSServiceRegistrationReplyErrorType errorCode, void *context)
- {
- printf("Got a reply from the server: ");
- switch (errorCode)
- {
- case kDNSServiceDiscoveryNoError: printf("Name now registered and active\n"); break;
- case kDNSServiceDiscoveryNameConflict: printf("Name in use, please choose another\n"); exit(-1);
- default: printf("Error %d\n", errorCode); return;
- }
-
- if (operation == 'A' || operation == 'U' || operation == 'N')
- {
- CFRunLoopTimerContext myCFRunLoopTimerContext = { 0, 0, NULL, NULL, NULL };
- CFRunLoopTimerRef timer = CFRunLoopTimerCreate(kCFAllocatorDefault,
- CFAbsoluteTimeGetCurrent() + 5.0, 5.0, 0, 1, // Next fire time, periodic interval, flags, and order
- myCFRunLoopTimerCallBack, &myCFRunLoopTimerContext);
- CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
- }
- }
-
-//*************************************************************************************************************
-// The main test function
-
-int main(int argc, char **argv)
- {
- char *dom;
-
- if (argc < 2) goto Fail; // Minimum command line is the command name and one argument
- operation = getopt(argc, (char * const *)argv, "EFBLRAUNTM");
- if (operation == -1) goto Fail;
-
- switch (operation)
- {
- case 'E': printf("Looking for recommended registration domains:\n");
- client = DNSServiceDomainEnumerationCreate(1, regdom_reply, nil);
- break;
-
- case 'F': printf("Looking for recommended browsing domains:\n");
- client = DNSServiceDomainEnumerationCreate(0, browsedom_reply, nil);
- break;
-
- case 'B': if (argc < optind+1) goto Fail;
- dom = (argc < optind+2) ? "" : argv[optind+1];
- if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string
- printf("Browsing for %s%s\n", argv[optind+0], dom);
- client = DNSServiceBrowserCreate(argv[optind+0], dom, browse_reply, nil);
- break;
-
- case 'L': if (argc < optind+2) goto Fail;
- dom = (argc < optind+3) ? "" : argv[optind+2];
- if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string
- printf("Lookup %s.%s%s\n", argv[optind+0], argv[optind+1], dom);
- client = DNSServiceResolverResolve(argv[optind+0], argv[optind+1], dom, resolve_reply, nil);
- 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 = (argc > optind+4) ? argv[optind+4] : "";
- 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
- printf("Registering Service %s.%s%s port %s %s\n", nam, typ, dom, argv[optind+3], txt);
- client = DNSServiceRegistrationCreate(nam, typ, dom, registerPort.NotAnInteger, txt, reg_reply, nil);
- break;
- }
-
- case 'A':
- case 'U':
- case 'N': {
- Opaque16 registerPort = { { 0x12, 0x34 } };
- static const char TXT[] = "First String\001Second String\001Third String";
- printf("Registering Service Test._testupdate._tcp.local.\n");
- client = DNSServiceRegistrationCreate("Test", "_testupdate._tcp.", "", registerPort.NotAnInteger, TXT, reg_reply, nil);
- break;
- }
-
- case 'T': {
- Opaque16 registerPort = { { 0x23, 0x45 } };
- char TXT[512];
- int i;
- for (i=0; i<sizeof(TXT)-1; i++)
- if ((i & 0x1F) == 0x1F) TXT[i] = 1; else TXT[i] = 'A' + (i >> 5);
- TXT[i] = 0;
- printf("Registering Service Test._testlargetxt._tcp.local.\n");
- client = DNSServiceRegistrationCreate("Test", "_testlargetxt._tcp.", "", registerPort.NotAnInteger, TXT, reg_reply, nil);
- break;
- }
-
- case 'M': {
- Opaque16 registerPort = { { 0x23, 0x45 } };
- static const char TXT1[] = "First String\001Second String\001Third String";
- static const char TXT2[] = "\x0D" "Fourth String" "\x0C" "Fifth String" "\x0C" "Sixth String";
- printf("Registering Service Test._testdualtxt._tcp.local.\n");
- client = DNSServiceRegistrationCreate("Test", "_testdualtxt._tcp.", "", registerPort.NotAnInteger, TXT1, reg_reply, nil);
- record = DNSServiceRegistrationAddRecord(client, T_TXT, sizeof(TXT2), TXT2, 120);
- break;
- }
-
- default: goto Exit;
- }
-
- if (!client) { fprintf(stderr, "DNSService call failed\n"); return (-1); }
- if (AddDNSServiceClientToRunLoop(client) != 0) { fprintf(stderr, "AddDNSServiceClientToRunLoop failed\n"); return (-1); }
- printf("Talking to DNS SD Daemon at Mach port %d\n", DNSServiceDiscoveryMachPort(client));
- CFRunLoopRun();
-
- // Be sure to deallocate the dns_service_discovery_ref when you're finished
- // Note: What other cleanup has to be done here?
- // We should probably invalidate, remove and release our CFRunLoopSourceRef?
- DNSServiceDiscoveryDeallocate(client);
-
-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 <Type> <Domain> (Browse for services instances)\n", argv[0]);
- fprintf(stderr, "%s -L <Name> <Type> <Domain> (Look up a service instance)\n", argv[0]);
- fprintf(stderr, "%s -R <Name> <Type> <Domain> <Port> <TXT> (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]);
- return 0;
- }
+++ /dev/null
-/*
- * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License"). You may not use this file except in compliance with the
- * License. Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
- *
- * This 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 OR NON-INFRINGEMENT. Please see the
- * License for the specific language governing rights and limitations
- * under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-/*
- * Formatting notes:
- * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
- * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
- * but for the sake of brevity here I will say just this: Curly braces are not syntactially
- * part of an "if" statement; they are the beginning and ending markers of a compound statement;
- * therefore common sense dictates that if they are part of a compound statement then they
- * should be indented to the same level as everything else in that compound statement.
- * Indenting curly braces at the same level as the "if" implies that curly braces are
- * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
- * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
- * 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.)
- */
-
-#include <mach/mach.h>
-#include <mach/mach_error.h>
-#include <servers/bootstrap.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "DNSServiceDiscoveryRequestServer.h"
-#include "DNSServiceDiscoveryReply.h"
-
-#include "mDNSClientAPI.h" // Defines the interface to the client layer above
-#include "mDNSPlatformFunctions.h" // For mDNSPlatformTimeNow() and mDNSPlatformOneSecond
-#include "mDNSPlatformEnvironment.h" // Defines the specific types needed to run mDNS on this platform
-#include "mDNSsprintf.h"
-#include "mDNSvsprintf.h" // Used to implement LogErrorMessage();
-
-#include <DNSServiceDiscovery/DNSServiceDiscovery.h>
-
-//*************************************************************************************************************
-// Globals
-
-static mDNS mDNSStorage;
-static mDNS_PlatformSupport PlatformStorage;
-#define RR_CACHE_SIZE 500
-static ResourceRecord rrcachestorage[RR_CACHE_SIZE];
-static const char PID_FILE[] = "/var/run/mDNSResponder.pid";
-
-static const char kmDNSBootstrapName[] = "com.apple.mDNSResponder";
-static mach_port_t client_death_port = MACH_PORT_NULL;
-static mach_port_t exit_m_port = MACH_PORT_NULL;
-static mach_port_t server_priv_port = MACH_PORT_NULL;
-static CFRunLoopTimerRef DeliverInstanceTimer;
-
-// mDNS Mach Message Timeout, in milliseconds.
-// We need this to be short enough that we don't deadlock the mDNSResponder if a client
-// fails to service its mach message queue, but long enough to give a well-written
-// client a chance to service its mach message queue without getting cut off.
-// Empirically, 50ms seems to work, so we set the timeout to 250ms to give
-// even extra-slow clients a fair chance before we cut them off.
-#define MDNS_MM_TIMEOUT 250
-
-static int restarting_via_mach_init = 0;
-
-#if DEBUGBREAKS
-static int debug_mode = 1;
-#else
-static int debug_mode = 0;
-#endif
-
-//*************************************************************************************************************
-// Active client list structures
-
-typedef struct DNSServiceDomainEnumeration_struct DNSServiceDomainEnumeration;
-struct DNSServiceDomainEnumeration_struct
- {
- DNSServiceDomainEnumeration *next;
- mach_port_t ClientMachPort;
- DNSQuestion dom; // Question asking for domains
- DNSQuestion def; // Question asking for default domain
- };
-
-typedef struct DNSServiceBrowserResult_struct DNSServiceBrowserResult;
-struct DNSServiceBrowserResult_struct
- {
- DNSServiceBrowserResult *next;
- int resultType;
- char name[256], type[256], dom[256];
- };
-
-typedef struct DNSServiceBrowser_struct DNSServiceBrowser;
-struct DNSServiceBrowser_struct
- {
- DNSServiceBrowser *next;
- mach_port_t ClientMachPort;
- DNSQuestion q;
- DNSServiceBrowserResult *results;
- mDNSs32 lastsuccess;
- };
-
-typedef struct DNSServiceResolver_struct DNSServiceResolver;
-struct DNSServiceResolver_struct
- {
- DNSServiceResolver *next;
- mach_port_t ClientMachPort;
- ServiceInfoQuery q;
- ServiceInfo i;
- };
-
-typedef struct DNSServiceRegistration_struct DNSServiceRegistration;
-struct DNSServiceRegistration_struct
- {
- DNSServiceRegistration *next;
- mach_port_t ClientMachPort;
- mDNSBool autoname;
- mDNSBool autorename;
- domainlabel name;
- ServiceRecordSet s;
- // Don't add any fields after ServiceRecordSet.
- // This is where the implicit extra space goes if we allocate an oversized ServiceRecordSet object
- };
-
-static DNSServiceDomainEnumeration *DNSServiceDomainEnumerationList = NULL;
-static DNSServiceBrowser *DNSServiceBrowserList = NULL;
-static DNSServiceResolver *DNSServiceResolverList = NULL;
-static DNSServiceRegistration *DNSServiceRegistrationList = NULL;
-
-//*************************************************************************************************************
-// General Utility Functions
-
-void LogErrorMessage(const char *format, ...)
- {
- unsigned char buffer[512];
- va_list ptr;
- va_start(ptr,format);
- buffer[mDNS_vsprintf((char *)buffer, format, ptr)] = 0;
- va_end(ptr);
- openlog("mDNSResponder", LOG_CONS | LOG_PERROR | LOG_PID, LOG_DAEMON);
- fprintf(stderr, "%s\n", buffer);
- syslog(LOG_ERR, "%s", buffer);
- closelog();
- fflush(stderr);
- }
-
-#if MACOSX_MDNS_MALLOC_DEBUGGING
-
-char _malloc_options[] = "AXZ";
-
-static void validatelists(mDNS *const m)
- {
- DNSServiceDomainEnumeration *e;
- DNSServiceBrowser *b;
- DNSServiceResolver *l;
- DNSServiceRegistration *r;
- ResourceRecord *rr;
-
- for (e = DNSServiceDomainEnumerationList; e; e=e->next)
- if (e->ClientMachPort == 0)
- LogErrorMessage("!!!! DNSServiceDomainEnumerationList %X is garbage !!!!", e);
-
- for (b = DNSServiceBrowserList; b; b=b->next)
- if (b->ClientMachPort == 0)
- LogErrorMessage("!!!! DNSServiceBrowserList %X is garbage !!!!", b);
-
- for (l = DNSServiceResolverList; l; l=l->next)
- if (l->ClientMachPort == 0)
- LogErrorMessage("!!!! DNSServiceResolverList %X is garbage !!!!", l);
-
- for (r = DNSServiceRegistrationList; r; r=r->next)
- if (r->ClientMachPort == 0)
- LogErrorMessage("!!!! DNSServiceRegistrationList %X is garbage !!!!", r);
-
- for (rr = m->ResourceRecords; rr; rr=rr->next)
- if (rr->RecordType == 0)
- LogErrorMessage("!!!! ResourceRecords %X list is garbage !!!!");
- }
-
-void *mallocL(char *msg, unsigned int size)
- {
- unsigned long *mem = malloc(size+8);
- if (!mem)
- { LogErrorMessage("malloc(%s:%d) failed", msg, size); return(NULL); }
- else
- {
- LogErrorMessage("malloc(%s:%d) = %X", msg, size, &mem[2]);
- mem[0] = 0xDEADBEEF;
- mem[1] = size;
- bzero(&mem[2], size);
- validatelists(&mDNSStorage);
- return(&mem[2]);
- }
- }
-
-void freeL(char *msg, void *x)
- {
- if (!x)
- LogErrorMessage("free(%s@NULL)!", msg);
- else
- {
- unsigned long *mem = ((unsigned long *)x) - 2;
- if (mem[0] != 0xDEADBEEF)
- { LogErrorMessage("free(%s@%X) !!!! NOT ALLOCATED !!!!", msg, &mem[2]); return; }
- if (mem[1] > 8000)
- { LogErrorMessage("free(%s:%d@%X) too big!", msg, mem[1], &mem[2]); return; }
- LogErrorMessage("free(%s:%d@%X)", msg, mem[1], &mem[2]);
- bzero(mem, mem[1]+8);
- validatelists(&mDNSStorage);
- free(mem);
- }
- }
-
-#endif
-
-//*************************************************************************************************************
-// Client Death Detection
-
-mDNSlocal void AbortClient(mach_port_t ClientMachPort)
- {
- DNSServiceDomainEnumeration **e = &DNSServiceDomainEnumerationList;
- DNSServiceBrowser **b = &DNSServiceBrowserList;
- DNSServiceResolver **l = &DNSServiceResolverList;
- DNSServiceRegistration **r = &DNSServiceRegistrationList;
-
- while (*e && (*e)->ClientMachPort != ClientMachPort) e = &(*e)->next;
- if (*e)
- {
- DNSServiceDomainEnumeration *x = *e;
- *e = (*e)->next;
- debugf("Aborting DNSServiceDomainEnumeration %d", ClientMachPort);
- mDNS_StopGetDomains(&mDNSStorage, &x->dom);
- mDNS_StopGetDomains(&mDNSStorage, &x->def);
- freeL("DNSServiceDomainEnumeration", x);
- return;
- }
-
- while (*b && (*b)->ClientMachPort != ClientMachPort) b = &(*b)->next;
- if (*b)
- {
- DNSServiceBrowser *x = *b;
- *b = (*b)->next;
- debugf("Aborting DNSServiceBrowser %d", ClientMachPort);
- mDNS_StopBrowse(&mDNSStorage, &x->q);
- while (x->results)
- {
- DNSServiceBrowserResult *r = x->results;
- x->results = x->results->next;
- freeL("DNSServiceBrowserResult", r);
- }
- freeL("DNSServiceBrowser", x);
- return;
- }
-
- while (*l && (*l)->ClientMachPort != ClientMachPort) l = &(*l)->next;
- if (*l)
- {
- DNSServiceResolver *x = *l;
- *l = (*l)->next;
- debugf("Aborting DNSServiceResolver %d", ClientMachPort);
- mDNS_StopResolveService(&mDNSStorage, &x->q);
- freeL("DNSServiceResolver", x);
- return;
- }
-
- while (*r && (*r)->ClientMachPort != ClientMachPort) r = &(*r)->next;
- if (*r)
- {
- DNSServiceRegistration *x = *r;
- *r = (*r)->next;
- x->autorename = mDNSfalse;
- mDNS_DeregisterService(&mDNSStorage, &x->s);
- // Note that we don't do the "free(x);" here -- wait for the mStatus_MemFree message
- return;
- }
- }
-
-mDNSlocal void AbortBlockedClient(mach_port_t c, char *m)
- {
- DNSServiceDomainEnumeration **e = &DNSServiceDomainEnumerationList;
- DNSServiceBrowser **b = &DNSServiceBrowserList;
- DNSServiceResolver **l = &DNSServiceResolverList;
- DNSServiceRegistration **r = &DNSServiceRegistrationList;
- while (*e && (*e)->ClientMachPort != c) e = &(*e)->next;
- while (*b && (*b)->ClientMachPort != c) b = &(*b)->next;
- while (*l && (*l)->ClientMachPort != c) l = &(*l)->next;
- while (*r && (*r)->ClientMachPort != c) r = &(*r)->next;
- if (*e) LogErrorMessage("%5d: DomainEnumeration(%##s) stopped accepting Mach messages (%s)", c, &e[0]->dom.name, m);
- else if (*b) LogErrorMessage("%5d: Browser(%##s) stopped accepting Mach messages (%s)", c, &b[0]->q.name, m);
- else if (*l) LogErrorMessage("%5d: Resolver(%##s) stopped accepting Mach messages (%s)", c, &l[0]->i.name, m);
- else if (*r) LogErrorMessage("%5d: Registration(%##s) stopped accepting Mach messages (%s)", c, &r[0]->s.RR_SRV.name, m);
- else LogErrorMessage("%5d (%s) stopped accepting Mach messages, but no record of client can be found!", c, m);
-
- AbortClient(c);
- }
-
-mDNSlocal void ClientDeathCallback(CFMachPortRef unusedport, void *voidmsg, CFIndex size, void *info)
- {
- mach_msg_header_t *msg = (mach_msg_header_t *)voidmsg;
- if (msg->msgh_id == MACH_NOTIFY_DEAD_NAME)
- {
- const mach_dead_name_notification_t *const deathMessage = (mach_dead_name_notification_t *)msg;
- AbortClient(deathMessage->not_port);
-
- /* Deallocate the send right that came in the dead name notification */
- mach_port_destroy( mach_task_self(), deathMessage->not_port );
- }
- }
-
-mDNSlocal void EnableDeathNotificationForClient(mach_port_t ClientMachPort)
- {
- mach_port_t prev;
- kern_return_t r = mach_port_request_notification(mach_task_self(), ClientMachPort, MACH_NOTIFY_DEAD_NAME, 0,
- client_death_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev);
- // If the port already died while we were thinking about it, then abort the operation right away
- if (r != KERN_SUCCESS)
- {
- if (ClientMachPort != (mach_port_t)-1)
- LogErrorMessage("Client %5d died before we could enable death notification", ClientMachPort);
- AbortClient(ClientMachPort);
- }
- }
-
-//*************************************************************************************************************
-// Domain Enumeration
-
-mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer)
- {
- kern_return_t status;
- #pragma unused(m)
- char buffer[256];
- DNSServiceDomainEnumerationReplyResultType rt;
- DNSServiceDomainEnumeration *x = (DNSServiceDomainEnumeration *)question->Context;
-
- debugf("FoundDomain: %##s PTR %##s", answer->name.c, answer->rdata->u.name.c);
- if (answer->rrtype != kDNSType_PTR) return;
- if (!x) { debugf("FoundDomain: DNSServiceDomainEnumeration is NULL"); return; }
-
- if (answer->rrremainingttl > 0)
- {
- if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyAddDomain;
- else rt = DNSServiceDomainEnumerationReplyAddDomainDefault;
- }
- else
- {
- if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyRemoveDomain;
- else return;
- }
-
- ConvertDomainNameToCString(&answer->rdata->u.name, buffer);
- status = DNSServiceDomainEnumerationReply_rpc(x->ClientMachPort, rt, buffer, 0, MDNS_MM_TIMEOUT);
- if (status == MACH_SEND_TIMED_OUT)
- AbortBlockedClient(x->ClientMachPort, "enumeration");
- }
-
-mDNSexport kern_return_t provide_DNSServiceDomainEnumerationCreate_rpc(mach_port_t unusedserver, mach_port_t client,
- int regDom)
- {
- kern_return_t status;
- mStatus err;
-
- mDNS_DomainType dt1 = regDom ? mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse;
- mDNS_DomainType dt2 = regDom ? mDNS_DomainTypeRegistrationDefault : mDNS_DomainTypeBrowseDefault;
- const DNSServiceDomainEnumerationReplyResultType rt = DNSServiceDomainEnumerationReplyAddDomainDefault;
- DNSServiceDomainEnumeration *x = mallocL("DNSServiceDomainEnumeration", sizeof(*x));
- if (!x) { debugf("provide_DNSServiceDomainEnumerationCreate_rpc: No memory!"); return(mStatus_NoMemoryErr); }
- x->ClientMachPort = client;
- x->next = DNSServiceDomainEnumerationList;
- DNSServiceDomainEnumerationList = x;
-
- debugf("Client %d: Enumerate %s Domains", client, regDom ? "Registration" : "Browsing");
- // We always give local. as the initial default browse domain, and then look for more
- status = DNSServiceDomainEnumerationReply_rpc(x->ClientMachPort, rt, "local.", 0, MDNS_MM_TIMEOUT);
- if (status == MACH_SEND_TIMED_OUT)
- {
- AbortBlockedClient(x->ClientMachPort, "local enumeration");
- return(mStatus_UnknownErr);
- }
-
- err = mDNS_GetDomains(&mDNSStorage, &x->dom, dt1, zeroIPAddr, FoundDomain, x);
- if (!err) err = mDNS_GetDomains(&mDNSStorage, &x->def, dt2, zeroIPAddr, FoundDomain, x);
-
- if (err) AbortClient(client);
- else EnableDeathNotificationForClient(client);
-
- if (err) debugf("provide_DNSServiceDomainEnumerationCreate_rpc: mDNS_GetDomains error %d", err);
- return(err);
- }
-
-//*************************************************************************************************************
-// Browse for services
-
-mDNSlocal void DeliverInstanceTimerCallBack(CFRunLoopTimerRef timer, void *info)
- {
- mDNSBool tryagain = mDNSfalse;
- DNSServiceBrowser *b = DNSServiceBrowserList;
- (void)timer; // Parameter not used
-
- while (b)
- {
- // NOTE: Need to advance b to the next element BEFORE we call DeliverInstance(), because in the
- // event that the client Mach queue overflows, DeliverInstance() will call AbortBlockedClient()
- // and that will cause the DNSServiceBrowser object's memory to be freed before it returns
- DNSServiceBrowser *x = b;
- b = b->next;
- if (x->results) // Try to deliver the list of results
- {
- mDNSs32 now = mDNSPlatformTimeNow();
- while (x->results)
- {
- DNSServiceBrowserResult *const r = x->results;
- DNSServiceDiscoveryReplyFlags flags = (r->next) ? DNSServiceDiscoverReplyFlagsMoreComing : 0;
- kern_return_t status = DNSServiceBrowserReply_rpc(x->ClientMachPort, r->resultType, r->name, r->type, r->dom, flags, 1);
- // If we failed to send the mach message, try again in one second
- if (status == MACH_SEND_TIMED_OUT)
- { tryagain = mDNStrue; break; }
- else
- {
- x->lastsuccess = now;
- x->results = x->results->next;
- freeL("DNSServiceBrowserResult", r);
- }
- }
- // If this client hasn't read a single message in the last 60 seconds, abort it
- if (now - x->lastsuccess >= 60 * mDNSPlatformOneSecond)
- AbortBlockedClient(x->ClientMachPort, "browse");
- }
- }
- if (tryagain)
- CFRunLoopTimerSetNextFireDate(DeliverInstanceTimer, CFAbsoluteTimeGetCurrent() + 1.0);
- }
-
-mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer)
- {
- DNSServiceBrowser *browser = (DNSServiceBrowser *)question->Context;
- DNSServiceBrowserResult **p = &browser->results;
- DNSServiceBrowserResult *x = mallocL("DNSServiceBrowserResult", sizeof(*x));
- domainlabel name;
- domainname type, domain;
-
- if (answer->rrtype != kDNSType_PTR)
- {
- debugf("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer->rrtype);
- return;
- }
-
- if (!DeconstructServiceName(&answer->rdata->u.name, &name, &type, &domain))
- {
- debugf("FoundInstance: %##s PTR %##s is not valid NIAS service pointer", &answer->name, &answer->rdata->u.name);
- return;
- }
-
- if (!x) return;
-
- debugf("FoundInstance: %##s", answer->rdata->u.name.c);
- ConvertDomainLabelToCString_unescaped(&name, x->name);
- ConvertDomainNameToCString(&type, x->type);
- ConvertDomainNameToCString(&domain, x->dom);
- if (answer->rrremainingttl)
- x->resultType = DNSServiceBrowserReplyAddInstance;
- else x->resultType = DNSServiceBrowserReplyRemoveInstance;
- x->next = NULL;
- while (*p) p = &(*p)->next;
- *p = x;
-
- // We schedule this timer 1/10 second in the future because CFRunLoop doesn't respect
- // the relative priority between CFSocket and CFRunLoopTimer, and continues to call
- // the timer callback even though there are packets waiting to be processed.
- CFRunLoopTimerSetNextFireDate(DeliverInstanceTimer, CFAbsoluteTimeGetCurrent() + 0.1);
- }
-
-mDNSexport kern_return_t provide_DNSServiceBrowserCreate_rpc(mach_port_t unusedserver, mach_port_t client,
- DNSCString regtype, DNSCString domain)
- {
- mStatus err;
- domainname t, d;
- DNSServiceBrowser *x = mallocL("DNSServiceBrowser", sizeof(*x));
- if (!x) { debugf("provide_DNSServiceBrowserCreate_rpc: No memory!"); return(mStatus_NoMemoryErr); }
- x->ClientMachPort = client;
- x->results = NULL;
- x->lastsuccess = 0;
- x->next = DNSServiceBrowserList;
- DNSServiceBrowserList = x;
-
- ConvertCStringToDomainName(regtype, &t);
- ConvertCStringToDomainName(*domain ? domain : "local.", &d);
-
- debugf("Client %d: provide_DNSServiceBrowserCreate_rpc", client);
- debugf("Client %d: Browse for Services: %##s%##s", client, &t, &d);
- err = mDNS_StartBrowse(&mDNSStorage, &x->q, &t, &d, zeroIPAddr, FoundInstance, x);
-
- if (err) AbortClient(client);
- else EnableDeathNotificationForClient(client);
-
- if (err) debugf("provide_DNSServiceBrowserCreate_rpc: mDNS_StartBrowse error %d", err);
- return(err);
- }
-
-//*************************************************************************************************************
-// Resolve Service Info
-
-mDNSlocal void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query)
- {
- kern_return_t status;
- DNSServiceResolver *x = (DNSServiceResolver *)query->Context;
- struct sockaddr_in interface;
- struct sockaddr_in address;
- char cstring[1024];
- int i, pstrlen = query->info->TXTinfo[0];
-
- //debugf("FoundInstanceInfo %.4a %.4a %##s", &query->info->InterfaceAddr, &query->info->ip, &query->info->name);
-
- if (query->info->TXTlen > sizeof(cstring)) return;
-
- bzero(&interface, sizeof(interface));
- bzero(&address, sizeof(address));
-
- interface.sin_len = sizeof(interface);
- interface.sin_family = AF_INET;
- interface.sin_port = 0;
- interface.sin_addr.s_addr = query->info->InterfaceAddr.NotAnInteger;
-
- address.sin_len = sizeof(address);
- address.sin_family = AF_INET;
- address.sin_port = query->info->port.NotAnInteger;
- address.sin_addr.s_addr = query->info->ip.NotAnInteger;
-
- // The OS X DNSServiceResolverResolve() API is defined using a C-string,
- // but the mDNS_StartResolveService() call actually returns a packed block of P-strings.
- // Hence we have to convert the P-string(s) to a C-string before returning the result to the client.
- // ASCII-1 characters are used in the C-string as boundary markers,
- // to indicate the boundaries between the original constituent P-strings.
- for (i=1; i<query->info->TXTlen; i++)
- {
- if (--pstrlen >= 0)
- cstring[i-1] = query->info->TXTinfo[i];
- else
- {
- cstring[i-1] = 1;
- pstrlen = query->info->TXTinfo[i];
- }
- }
- cstring[i-1] = 0; // Put the terminating NULL on the end
-
- status = DNSServiceResolverReply_rpc(x->ClientMachPort,
- (char*)&interface, (char*)&address, cstring, 0, MDNS_MM_TIMEOUT);
- if (status == MACH_SEND_TIMED_OUT)
- AbortBlockedClient(x->ClientMachPort, "resolve");
- }
-
-mDNSexport kern_return_t provide_DNSServiceResolverResolve_rpc(mach_port_t unusedserver, mach_port_t client,
- DNSCString name, DNSCString regtype, DNSCString domain)
- {
- mStatus err;
- domainlabel n;
- domainname t, d;
- DNSServiceResolver *x = mallocL("DNSServiceResolver", sizeof(*x));
- if (!x) { debugf("provide_DNSServiceResolverResolve_rpc: No memory!"); return(mStatus_NoMemoryErr); }
- x->ClientMachPort = client;
- x->next = DNSServiceResolverList;
- DNSServiceResolverList = x;
-
- ConvertCStringToDomainLabel(name, &n);
- ConvertCStringToDomainName(regtype, &t);
- ConvertCStringToDomainName(*domain ? domain : "local.", &d);
- ConstructServiceName(&x->i.name, &n, &t, &d);
- x->i.InterfaceAddr = zeroIPAddr;
-
- debugf("Client %d: provide_DNSServiceResolverResolve_rpc", client);
- debugf("Client %d: Resolve Service: %##s", client, &x->i.name);
- err = mDNS_StartResolveService(&mDNSStorage, &x->q, &x->i, FoundInstanceInfo, x);
-
- if (err) AbortClient(client);
- else EnableDeathNotificationForClient(client);
-
- if (err) debugf("provide_DNSServiceResolverResolve_rpc: mDNS_StartResolveService error %d", err);
- return(err);
- }
-
-//*************************************************************************************************************
-// Registration
-
-mDNSlocal void FreeDNSServiceRegistration(DNSServiceRegistration *x)
- {
- while (x->s.Extras)
- {
- ExtraResourceRecord *extras = x->s.Extras;
- x->s.Extras = x->s.Extras->next;
- if (extras->r.rdata != &extras->r.rdatastorage)
- freeL("Extra RData", extras->r.rdata);
- freeL("ExtraResourceRecord", extras);
- }
-
- if (x->s.RR_TXT.rdata != &x->s.RR_TXT.rdatastorage)
- freeL("TXT RData", x->s.RR_TXT.rdata);
-
- freeL("DNSServiceRegistration", x);
- }
-
-mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result)
- {
- DNSServiceRegistration *x = (DNSServiceRegistration*)sr->Context;
-
- switch (result)
- {
- case mStatus_NoError: debugf("RegCallback: %##s Name Registered", sr->RR_SRV.name.c); break;
- case mStatus_NameConflict: debugf("RegCallback: %##s Name Conflict", sr->RR_SRV.name.c); break;
- case mStatus_MemFree: debugf("RegCallback: %##s Memory Free", sr->RR_SRV.name.c); break;
- default: debugf("RegCallback: %##s Unknown Result %d", sr->RR_SRV.name.c, result); break;
- }
-
- if (result == mStatus_NoError)
- {
- kern_return_t status = DNSServiceRegistrationReply_rpc(x->ClientMachPort, result, MDNS_MM_TIMEOUT);
- if (status == MACH_SEND_TIMED_OUT)
- AbortBlockedClient(x->ClientMachPort, "registration success");
- }
-
- if (result == mStatus_NameConflict)
- {
- // 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);
- else
- {
- kern_return_t status;
- // AbortClient unlinks our DNSServiceRegistration from the list so we can safely free it
- AbortClient(x->ClientMachPort);
- status = DNSServiceRegistrationReply_rpc(x->ClientMachPort, result, MDNS_MM_TIMEOUT);
- if (status == MACH_SEND_TIMED_OUT)
- AbortBlockedClient(x->ClientMachPort, "registration conflict"); // Yes, this IS safe :-)
- FreeDNSServiceRegistration(x);
- }
- }
-
- if (result == mStatus_MemFree)
- {
- if (x->autorename)
- {
- debugf("RegCallback renaming %#s to %#s", &x->name, &mDNSStorage.nicelabel);
- x->autorename = mDNSfalse;
- x->name = mDNSStorage.nicelabel;
- mDNS_RenameAndReregisterService(m, &x->s, &x->name);
- }
- else
- {
- DNSServiceRegistration **r = &DNSServiceRegistrationList;
- while (*r && *r != x) r = &(*r)->next;
- if (*r)
- {
- debugf("RegCallback: %##s Still in DNSServiceRegistration list; removing now", sr->RR_SRV.name.c);
- *r = (*r)->next;
- }
- debugf("RegCallback: Freeing DNSServiceRegistration %##s %d", sr->RR_SRV.name.c, x->ClientMachPort);
- FreeDNSServiceRegistration(x);
- }
- }
- }
-
-mDNSlocal void CheckForDuplicateRegistrations(DNSServiceRegistration *x, domainlabel *n, domainname *t, domainname *d, mDNSIPPort port)
- {
- char name[256];
- int count = 0;
- ResourceRecord *rr;
- domainname srvname;
- ConstructServiceName(&srvname, n, t, d);
- mDNS_sprintf(name, "%##s", &srvname);
-
- for (rr = mDNSStorage.ResourceRecords; rr; rr=rr->next)
- if (rr->rrtype == kDNSType_SRV && rr->rdata->u.srv.port.NotAnInteger == port.NotAnInteger && SameDomainName(&rr->name, &srvname))
- count++;
-
- if (count)
- {
- debugf("Client %5d registering Service Record Set \"%##s\"; WARNING! now have %d instances port %d",
- x->ClientMachPort, &srvname, count+1, (int)port.b[0] << 8 | port.b[1]);
- LogErrorMessage("%5d: WARNING! Bogus client application has now registered %d identical instances of service %##s port %d",
- x->ClientMachPort, count+1, &srvname, (int)port.b[0] << 8 | port.b[1]);
- }
- }
-
-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)
- {
- mStatus err;
- domainname t, d;
- mDNSIPPort port;
- unsigned char txtinfo[1024] = "";
- int data_len = 0;
- int size = sizeof(RDataBody);
- unsigned char *pstring = &txtinfo[data_len];
- char *ptr = txtRecord;
- DNSServiceRegistration *x;
-
- // The OS X DNSServiceRegistrationCreate() API is defined using a C-string,
- // but the mDNS_RegisterService() call actually requires a packed block of P-strings.
- // Hence we have to convert the C-string to a P-string.
- // ASCII-1 characters are allowed in the C-string as boundary markers,
- // so that a single C-string can be used to represent one or more P-strings.
- while (*ptr)
- {
- if (++data_len >= sizeof(txtinfo)) return(mStatus_BadParamErr);
- if (*ptr == 1) // If this is our boundary marker, start a new P-string
- {
- pstring = &txtinfo[data_len];
- pstring[0] = 0;
- ptr++;
- }
- else
- {
- if (pstring[0] == 255) return(mStatus_BadParamErr);
- pstring[++pstring[0]] = *ptr++;
- }
- }
-
- data_len++;
- if (size < data_len)
- size = data_len;
-
- x = mallocL("DNSServiceRegistration", sizeof(*x) - sizeof(RDataBody) + size);
- if (!x) { debugf("provide_DNSServiceRegistrationCreate_rpc: No memory!"); return(mStatus_NoMemoryErr); }
- x->ClientMachPort = client;
- x->next = DNSServiceRegistrationList;
- DNSServiceRegistrationList = x;
-
- x->autoname = (*name == 0);
- x->autorename = mDNSfalse;
- if (x->autoname) x->name = mDNSStorage.nicelabel;
- else ConvertCStringToDomainLabel(name, &x->name);
- ConvertCStringToDomainName(regtype, &t);
- ConvertCStringToDomainName(*domain ? domain : "local.", &d);
- port.NotAnInteger = notAnIntPort;
-
- debugf("Client %d: provide_DNSServiceRegistrationCreate_rpc", client);
- debugf("Client %d: Register Service: %#s.%##s%##s %d %.30s",
- client, &x->name, &t, &d, (int)port.b[0] << 8 | port.b[1], txtRecord);
- if (port.NotAnInteger) CheckForDuplicateRegistrations(x, &x->name, &t, &d, port);
- err = mDNS_RegisterService(&mDNSStorage, &x->s, &x->name, &t, &d, mDNSNULL, port, txtinfo, data_len, RegCallback, x);
-
- if (err) AbortClient(client);
- else EnableDeathNotificationForClient(client);
-
- if (err) debugf("provide_DNSServiceRegistrationCreate_rpc: mDNS_RegisterService error %d", err);
- else debugf("Made Service Record Set for %##s", &x->s.RR_SRV.name);
-
- return(err);
- }
-
-void NetworkChanged(void)
- {
- DNSServiceRegistration *r;
- for (r = DNSServiceRegistrationList; r; r=r->next)
- if (r->autoname && !SameDomainLabel(r->name.c, mDNSStorage.nicelabel.c))
- {
- debugf("NetworkChanged renaming %#s to %#s", &r->name, &mDNSStorage.nicelabel);
- r->autorename = mDNStrue;
- mDNS_DeregisterService(&mDNSStorage, &r->s);
- }
- }
-
-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)
- {
- mStatus err;
- DNSServiceRegistration *x = DNSServiceRegistrationList;
- ExtraResourceRecord *extra;
- int size = sizeof(RDataBody);
- if (size < data_len)
- size = data_len;
-
- // Find this registered service
- while (x && x->ClientMachPort != client) x = x->next;
- if (!x)
- {
- debugf("provide_DNSServiceRegistrationAddRecord_rpc bad client %X", client);
- return(mStatus_BadReferenceErr);
- }
-
- // Allocate storage for our new record
- extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size);
- if (!extra) return(mStatus_NoMemoryErr);
-
- // Fill in type, length, and data
- extra->r.rrtype = type;
- extra->r.rdatastorage.MaxRDLength = size;
- extra->r.rdatastorage.RDLength = data_len;
- memcpy(&extra->r.rdatastorage.u.data, data, data_len);
-
- // And register it
- err = mDNS_AddRecordToService(&mDNSStorage, &x->s, extra, &extra->r.rdatastorage, ttl);
- *reference = (natural_t)extra;
- debugf("Received a request to add the record of type: %d length: %d; returned reference %X",
- type, data_len, *reference);
- return(err);
- }
-
-mDNSlocal void UpdateCallback(mDNS *const m, ResourceRecord *const rr, RData *OldRData)
- {
- if (OldRData != &rr->rdatastorage)
- freeL("Old RData", OldRData);
- }
-
-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)
- {
- mStatus err;
- DNSServiceRegistration *x = DNSServiceRegistrationList;
- ResourceRecord *rr;
- RData *newrdata;
- int size = sizeof(RDataBody);
- if (size < data_len)
- size = data_len;
-
- // Find this registered service
- while (x && x->ClientMachPort != client) x = x->next;
- if (!x)
- {
- debugf("provide_DNSServiceRegistrationUpdateRecord_rpc bad client %X", client);
- return(mStatus_BadReferenceErr);
- }
-
- // Find the record we're updating
- if (!reference) // NULL reference means update the primary TXT record
- rr = &x->s.RR_TXT;
- else // Else, scan our list to make sure we're updating a valid record that was previously added
- {
- ExtraResourceRecord *e = x->s.Extras;
- while (e && e != (ExtraResourceRecord*)reference) e = e->next;
- if (!e)
- {
- debugf("provide_DNSServiceRegistrationUpdateRecord_rpc failed to find record %X", reference);
- return(mStatus_BadReferenceErr);
- }
- rr = &e->r;
- }
-
- // Allocate storage for our new data
- newrdata = mallocL("RData", sizeof(*newrdata) - sizeof(RDataBody) + size);
- if (!newrdata) return(mStatus_NoMemoryErr);
-
- // Fill in new length, and data
- newrdata->MaxRDLength = size;
- newrdata->RDLength = data_len;
- memcpy(&newrdata->u, data, data_len);
-
- // And update our record
- err = mDNS_Update(&mDNSStorage, rr, ttl, newrdata, UpdateCallback);
- if (err)
- {
- debugf("Received a request to update the record of length: %d for reference: %X; failed %d",
- data_len, reference, err);
- return(err);
- }
-
- debugf("Received a request to update the record of length: %d for reference: %X", data_len, reference);
- return(err);
- }
-
-mDNSexport kern_return_t provide_DNSServiceRegistrationRemoveRecord_rpc(mach_port_t unusedserver, mach_port_t client,
- natural_t reference)
- {
- mStatus err;
- DNSServiceRegistration *x = DNSServiceRegistrationList;
- ExtraResourceRecord *extra = (ExtraResourceRecord*)reference;
-
- // Find this registered service
- while (x && x->ClientMachPort != client) x = x->next;
- if (!x)
- {
- LogErrorMessage("DNSServiceRegistrationRemoveRecord Client %5d not found", client);
- debugf("provide_DNSServiceRegistrationRemoveRecord_rpc bad client %X", client);
- return(mStatus_BadReferenceErr);
- }
-
- err = mDNS_RemoveRecordFromService(&mDNSStorage, &x->s, extra);
- if (err)
- {
- LogErrorMessage("DNSServiceRegistrationRemoveRecord Client %5d does not have record %X", client, extra);
- debugf("Received a request to remove the record of reference: %X (failed %d)", extra, err);
- return(err);
- }
-
- debugf("Received a request to remove the record of reference: %X", extra);
- if (extra->r.rdata != &extra->r.rdatastorage)
- freeL("Extra RData", extra->r.rdata);
- freeL("ExtraResourceRecord", extra);
- return(err);
- }
-
-//*************************************************************************************************************
-// Support Code
-
-mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
- {
- mig_reply_error_t *request = msg;
- mig_reply_error_t *reply;
- mach_msg_return_t mr;
- int options;
-
- /* allocate a reply buffer */
- reply = CFAllocatorAllocate(NULL, provide_DNSServiceDiscoveryRequest_subsystem.maxsize, 0);
-
- /* call the MiG server routine */
- (void) DNSServiceDiscoveryRequest_server(&request->Head, &reply->Head);
-
- if (!(reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && (reply->RetCode != KERN_SUCCESS))
- {
- if (reply->RetCode == MIG_NO_REPLY)
- {
- /*
- * This return code is a little tricky -- it appears that the
- * demux routine found an error of some sort, but since that
- * error would not normally get returned either to the local
- * user or the remote one, we pretend it's ok.
- */
- CFAllocatorDeallocate(NULL, reply);
- return;
- }
-
- /*
- * destroy any out-of-line data in the request buffer but don't destroy
- * the reply port right (since we need that to send an error message).
- */
- request->Head.msgh_remote_port = MACH_PORT_NULL;
- mach_msg_destroy(&request->Head);
- }
-
- if (reply->Head.msgh_remote_port == MACH_PORT_NULL)
- {
- /* no reply port, so destroy the reply */
- if (reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
- mach_msg_destroy(&reply->Head);
- CFAllocatorDeallocate(NULL, reply);
- return;
- }
-
- /*
- * send reply.
- *
- * We don't want to block indefinitely because the client
- * isn't receiving messages from the reply port.
- * If we have a send-once right for the reply port, then
- * this isn't a concern because the send won't block.
- * If we have a send right, we need to use MACH_SEND_TIMEOUT.
- * To avoid falling off the kernel's fast RPC path unnecessarily,
- * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
- */
-
- options = MACH_SEND_MSG;
- if (MACH_MSGH_BITS_REMOTE(reply->Head.msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE)
- options |= MACH_SEND_TIMEOUT;
-
- mr = mach_msg(&reply->Head, /* msg */
- options, /* option */
- reply->Head.msgh_size, /* send_size */
- 0, /* rcv_size */
- MACH_PORT_NULL, /* rcv_name */
- MACH_MSG_TIMEOUT_NONE, /* timeout */
- MACH_PORT_NULL); /* notify */
-
- /* Has a message error occurred? */
- switch (mr)
- {
- case MACH_SEND_INVALID_DEST:
- case MACH_SEND_TIMED_OUT:
- /* the reply can't be delivered, so destroy it */
- mach_msg_destroy(&reply->Head);
- break;
-
- default :
- /* Includes success case. */
- break;
- }
-
- CFAllocatorDeallocate(NULL, reply);
- }
-
-mDNSlocal kern_return_t registerBootstrapService()
- {
- kern_return_t status;
- mach_port_t service_send_port, service_rcv_port;
-
- debugf("Registering Bootstrap Service");
-
- /*
- * See if our service name is already registered and if we have privilege to check in.
- */
- status = bootstrap_check_in(bootstrap_port, (char*)kmDNSBootstrapName, &service_rcv_port);
- if (status == KERN_SUCCESS)
- {
- /*
- * If so, we must be a followup instance of an already defined server. In that case,
- * the bootstrap port we inherited from our parent is the server's privilege port, so set
- * that in case we have to unregister later (which requires the privilege port).
- */
- server_priv_port = bootstrap_port;
- restarting_via_mach_init = TRUE;
- }
- else if (status == BOOTSTRAP_UNKNOWN_SERVICE)
- {
- status = bootstrap_create_server(bootstrap_port, "/usr/sbin/mDNSResponder", getuid(),
- FALSE /* relaunch immediately, not on demand */, &server_priv_port);
- if (status != KERN_SUCCESS) return status;
-
- status = bootstrap_create_service(server_priv_port, (char*)kmDNSBootstrapName, &service_send_port);
- if (status != KERN_SUCCESS)
- {
- mach_port_deallocate(mach_task_self(), server_priv_port);
- return status;
- }
-
- status = bootstrap_check_in(server_priv_port, (char*)kmDNSBootstrapName, &service_rcv_port);
- if (status != KERN_SUCCESS)
- {
- mach_port_deallocate(mach_task_self(), server_priv_port);
- mach_port_deallocate(mach_task_self(), service_send_port);
- return status;
- }
- assert(service_send_port == service_rcv_port);
- }
-
- /*
- * We have no intention of responding to requests on the service port. We are not otherwise
- * a Mach port-based service. We are just using this mechanism for relaunch facilities.
- * So, we can dispose of all the rights we have for the service port. We don't destroy the
- * send right for the server's privileged bootstrap port - in case we have to unregister later.
- */
- mach_port_destroy(mach_task_self(), service_rcv_port);
- return status;
- }
-
-mDNSlocal kern_return_t destroyBootstrapService()
- {
- debugf("Destroying Bootstrap Service");
- return bootstrap_register(server_priv_port, (char*)kmDNSBootstrapName, MACH_PORT_NULL);
- }
-
-mDNSlocal void ExitCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
- {
- debugf("ExitCallback: destroyBootstrapService");
- if (!debug_mode)
- destroyBootstrapService();
-
- debugf("ExitCallback: Aborting MIG clients");
- while (DNSServiceDomainEnumerationList) AbortClient(DNSServiceDomainEnumerationList->ClientMachPort);
- while (DNSServiceBrowserList) AbortClient(DNSServiceBrowserList->ClientMachPort);
- while (DNSServiceResolverList) AbortClient(DNSServiceResolverList->ClientMachPort);
- while (DNSServiceRegistrationList) AbortClient(DNSServiceRegistrationList->ClientMachPort);
-
- debugf("ExitCallback: mDNS_Close");
- mDNS_Close(&mDNSStorage);
- exit(0);
- }
-
-mDNSlocal kern_return_t start(const char *bundleName, const char *bundleDir)
- {
- extern void (*NotifyClientNetworkChanged)(void); // Temp fix for catching name changes
- mStatus err;
- CFRunLoopTimerContext myCFRunLoopTimerContext = { 0, &mDNSStorage, NULL, NULL, NULL };
- CFMachPortRef d_port = CFMachPortCreate(NULL, ClientDeathCallback, NULL, NULL);
- CFMachPortRef s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL);
- CFMachPortRef e_port = CFMachPortCreate(NULL, ExitCallback, NULL, NULL);
- mach_port_t m_port = CFMachPortGetPort(s_port);
- kern_return_t status = bootstrap_register(bootstrap_port, DNS_SERVICE_DISCOVERY_SERVER, 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);
-
- if (status)
- {
- if (status == 1103)
- LogErrorMessage("Bootstrap_register failed(): A copy of the daemon is apparently already running");
- else
- LogErrorMessage("Bootstrap_register failed(): %s %d", mach_error_string(status), status);
- return(status);
- }
-
- // Note: Every CFRunLoopTimer has to be created with an initial fire time, and a repeat interval, or it becomes
- // a one-shot timer and you can't use CFRunLoopTimerSetNextFireDate(timer, when) to schedule subsequent firings.
- // Here we create it with an initial fire time 24 hours from now, and a repeat interval of 24 hours, with
- // the intention that we'll actually reschedule it using CFRunLoopTimerSetNextFireDate(timer, when) as necessary.
- DeliverInstanceTimer = CFRunLoopTimerCreate(kCFAllocatorDefault,
- CFAbsoluteTimeGetCurrent() + 24.0*60.0*60.0, 24.0*60.0*60.0,
- 0, // no flags
- 9, // low priority execution (after all packets, etc., have been handled).
- DeliverInstanceTimerCallBack, &myCFRunLoopTimerContext);
- if (!DeliverInstanceTimer) return(-1);
- CFRunLoopAddTimer(CFRunLoopGetCurrent(), DeliverInstanceTimer, kCFRunLoopDefaultMode);
-
- err = mDNS_Init(&mDNSStorage, &PlatformStorage, rrcachestorage, RR_CACHE_SIZE, NULL, NULL);
- if (err) { LogErrorMessage("Daemon start: mDNS_Init failed %ld", err); return(err); }
-
- client_death_port = CFMachPortGetPort(d_port);
- exit_m_port = CFMachPortGetPort(e_port);
-
- CFRunLoopAddSource(CFRunLoopGetCurrent(), d_rls, kCFRunLoopDefaultMode);
- CFRunLoopAddSource(CFRunLoopGetCurrent(), s_rls, kCFRunLoopDefaultMode);
- CFRunLoopAddSource(CFRunLoopGetCurrent(), e_rls, kCFRunLoopDefaultMode);
- CFRelease(d_rls);
- CFRelease(s_rls);
- CFRelease(e_rls);
- if (debug_mode) printf("Service registered with Mach Port %d\n", m_port);
-
- NotifyClientNetworkChanged = NetworkChanged;
-
- return(err);
- }
-
-mDNSlocal void HandleSIG(int signal)
- {
- debugf("");
- debugf("HandleSIG");
-
- // Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit
- mach_msg_return_t msg_result;
- 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_local_port = MACH_PORT_NULL;
- header.msgh_size = sizeof(header);
- header.msgh_id = 0;
-
- msg_result = mach_msg_send(&header);
- }
-
-mDNSexport int main(int argc, char **argv)
- {
- int i;
- kern_return_t status;
- FILE *fp;
-
- for (i=1; i<argc; i++)
- {
- if (!strcmp(argv[i], "-d")) debug_mode = 1;
- }
-
- signal(SIGINT, HandleSIG); // SIGINT is what you get for a Ctrl-C
- signal(SIGTERM, HandleSIG);
-
- // Register the server with mach_init for automatic restart only during debug mode
- if (!debug_mode)
- registerBootstrapService();
-
- if (!debug_mode && !restarting_via_mach_init)
- exit(0); /* mach_init will restart us immediately as a daemon */
-
- fp = fopen(PID_FILE, "w");
- if (fp != NULL)
- {
- fprintf(fp, "%d\n", getpid());
- fclose(fp);
- }
-
- LogErrorMessage("mDNSResponder (%s %s) starting", __DATE__, __TIME__);
- status = start(NULL, NULL);
-
- if (status == 0)
- {
- CFRunLoopRun();
- LogErrorMessage("CFRunLoopRun Exiting. This is bad.");
- mDNS_Close(&mDNSStorage);
- }
-
- destroyBootstrapService();
-
- return(status);
- }
--- /dev/null
+February 2002:
+
+The mDNSResponder code has a slight architectural change to improve
+efficiency.
+
+The mDNSResponder code previously called ScheduleNextTask() after every
+operation, to calculate the time at which it needed to be called back to
+perform its next timed operation. When the workload is light, and
+protocol operations are rare and far apart, this makes sense.
+
+However, on networks where there is a lot of mDNS traffic (or the CPU is
+slow), this leads to the following anomolous behaviour: mDNSResponder
+spends a lot of CPU time working out what to do next, when what it needs
+to do next should be obvious: Finish processing the big backlog of
+packets that have been received.
+
+To remedy this, mDNSResponder now only executes ScheduleNextTask() when
+there is no other obvious work waiting to be done. However, the
+mDNSResponder code does not have direct access to this knowledge. Only
+the platform layer below knows whether there are packets waiting to be
+processed. Only the client layer above knows whether it is in the
+process of performing a long sequence of back-to-back mDNS API calls.
+
+This means that the new architecture places an additional responsibility
+on the client layer and/or platform support layer. As long as they have
+immediate work to do, they should call the appropriate mDNSCore routines
+to accomplish that work. With each call, mDNSCore will do only what it
+immediately has to do to satisfy the call. Any optional work will be
+deferred. As soon as there is no more immediate work to do, the calling
+layer MUST call mDNS_Execute(). Failure to call mDNS_Execute() will lead
+to unreliable or incorrect operation.
+
+The value returned from mDNS_Execute() is the next time (in absolute
+platform time units) at which mDNS_Execute() MUST be called again to
+perform its next necessary operation (e.g. transmitting its next
+scheduled query packet, etc.) Note that the time returned is an absolute
+time, not the time *interval* between now and the next required call.
+For OS APIs that work in terms of intervals instead of absolute times,
+mDNSPlatformTimeNow() must be subtracted from the absolute time to get
+the interval between now and the next event.
+
+In a single-threaded application using a blocking select() call as its
+main synchronization point, this means that you should call
+mDNS_Execute() before calling select(), and the timeout value you pass
+to select() MUST NOT be larger than that indicated by the result
+returned from mDNS_Execute(). After the blocking select() call returns,
+you should do whatever work you have to do, and then, if mDNS packets
+were received, or mDNS API calls were made, be sure to call
+mDNS_Execute() again, and if necessary adjust your timeout value
+accordingly, before going back into the select() call.
+
+In an asynchronous or interrupt-driven application, there are three
+places that should call mDNS_Execute():
+
+1. After delivering received packets, the platform support layer should
+call mDNS_Execute(), and use the value returned to set the platform
+callback timer to fire at the indicated time.
+
+2. After making any mDNS API call or series of calls, the client layer
+should call mDNS_Execute(), and use the value returned to set the
+platform callback timer to fire at the indicated time.
+
+3. When the platform callback timer fires, it should call mDNS_Execute()
+(to allow mDNSCore to perform its necessary work) and then the timer
+routine use the result returned to reset itself to fire at the right
+time for the next scheduled event.
-// ***************************************************************************
-// mDNS.c
-// This file defines all of mDNS, including
-// mDNS Service Discovery, mDNS Responder, and mDNS Searcher.
-//
-// This code is completely 100% portable C. It does not depend on any external header files
-// from outside the mDNS project -- all the types it expects to find are defined right here.
-//
-// The previous point is very important: This file does not depend on any external
-// header files. It should complile on *any* platform that has a C compiler, without
-// making *any* assumptions about availability of so-called "standard" C functions,
-// routines, or types (which may or may not be present on any given platform).
-// ***************************************************************************
-
/*
+ * 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 code is completely 100% portable C. It does not depend on any external header files
+ * from outside the mDNS project -- all the types it expects to find are defined right here.
+ *
+ * The previous point is very important: This file does not depend on any external
+ * header files. It should complile on *any* platform that has a C compiler, without
+ * making *any* assumptions about availability of so-called "standard" C functions,
+ * routines, or types (which may or may not be present on any given platform).
+
* Formatting notes:
* This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
* on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
* thinking that variables x and y are both of type "char*" -- and anyone who doesn't
* 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: mDNS.c,v $
+Revision 1.307 2003/09/09 20:13:30 cheshire
+<rdar://problem/3411105> Don't send a Goodbye record if we never announced it
+Ammend checkin 1.304: Off-by-one error: By this place in the function we've already decremented
+rr->AnnounceCount, so the check needs to be for InitialAnnounceCount-1, not InitialAnnounceCount
+
+Revision 1.306 2003/09/09 03:00:03 cheshire
+<rdar://problem/3413099> Services take a long time to disappear when switching networks.
+Added two constants: kDefaultReconfirmTimeForNoAnswer and kDefaultReconfirmTimeForCableDisconnect
+
+Revision 1.305 2003/09/09 02:49:31 cheshire
+<rdar://problem/3413975> Initial probes and queries not grouped on wake-from-sleep
+
+Revision 1.304 2003/09/09 02:41:19 cheshire
+<rdar://problem/3411105> Don't send a Goodbye record if we never announced it
+
+Revision 1.303 2003/09/05 19:55:02 cheshire
+<rdar://problem/3409533> Include address records when announcing SRV records
+
+Revision 1.302 2003/09/05 00:01:36 cheshire
+<rdar://problem/3407549> Don't accelerate queries that have large KA lists
+
+Revision 1.301 2003/09/04 22:51:13 cheshire
+<rdar://problem/3398213> Group probes and goodbyes better
+
+Revision 1.300 2003/09/03 02:40:37 cheshire
+<rdar://problem/3404842> mDNSResponder complains about '_'s
+Underscores are not supposed to be legal in standard DNS names, but IANA appears
+to have allowed them in previous service name registrations, so we should too.
+
+Revision 1.299 2003/09/03 02:33:09 cheshire
+<rdar://problem/3404795> CacheRecordRmv ERROR
+Don't update m->NewQuestions until *after* CheckCacheExpiration();
+
+Revision 1.298 2003/09/03 01:47:01 cheshire
+<rdar://problem/3319418> Rendezvous 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
+<rdar://problem/3400967> 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.296 2003/08/29 19:08:21 cheshire
+<rdar://problem/3400986> Traffic reduction: Eliminate huge KA lists after wake from sleep
+Known answers are no longer eligible to go in the KA list if they are more than half-way to their expiry time.
+
+Revision 1.295 2003/08/28 01:10:59 cheshire
+<rdar://problem/3396034> Add syslog message to report when query is reset because of immediate answer burst
+
+Revision 1.294 2003/08/27 02:30:22 cheshire
+<rdar://problem/3395909> Traffic Reduction: Inefficiencies in DNSServiceResolverResolve()
+One more change: "query->GotTXT" is now a straightforward bi-state boolean again
+
+Revision 1.293 2003/08/27 02:25:31 cheshire
+<rdar://problem/3395909> Traffic Reduction: Inefficiencies in DNSServiceResolverResolve()
+
+Revision 1.292 2003/08/21 19:27:36 cheshire
+<rdar://problem/3387878> Traffic reduction: No need to announce record for longer than TTL
+
+Revision 1.291 2003/08/21 18:57:44 cheshire
+<rdar://problem/3387140> Synchronized queries on the network
+
+Revision 1.290 2003/08/21 02:25:23 cheshire
+Minor changes to comments and debugf() messages
+
+Revision 1.289 2003/08/21 02:21:50 cheshire
+<rdar://problem/3386473> Efficiency: Reduce repeated queries
+
+Revision 1.288 2003/08/20 23:39:30 cheshire
+<rdar://problem/3344098> Review syslog messages, and remove as appropriate
+
+Revision 1.287 2003/08/20 20:47:18 cheshire
+Fix compiler warning
+
+Revision 1.286 2003/08/20 02:18:51 cheshire
+<rdar://problem/3344098> Cleanup: Review syslog messages
+
+Revision 1.285 2003/08/20 01:59:06 cheshire
+<rdar://problem/3384478> rdatahash and rdnamehash not updated after changing rdata
+Made new routine SetNewRData() to update rdlength, rdestimate, rdatahash and rdnamehash in one place
+
+Revision 1.284 2003/08/19 22:20:00 cheshire
+<rdar://problem/3376721> Don't use IPv6 on interfaces that have a routable IPv4 address configured
+More minor refinements
+
+Revision 1.283 2003/08/19 22:16:27 cheshire
+Minor fix: Add missing "mDNS_Unlock(m);" in mDNS_DeregisterInterface() error case.
+
+Revision 1.282 2003/08/19 06:48:25 cheshire
+<rdar://problem/3376552> 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.281 2003/08/19 04:49:28 cheshire
+<rdar://problem/3368159> 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.280 2003/08/19 02:33:36 cheshire
+Update comments
+
+Revision 1.279 2003/08/19 02:31:11 cheshire
+<rdar://problem/3378386> 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.278 2003/08/18 22:53:37 cheshire
+<rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformTimeNow()
+
+Revision 1.277 2003/08/18 19:05:44 cheshire
+<rdar://problem/3382423> UpdateRecord not working right
+Added "newrdlength" field to hold new length of updated rdata
+
+Revision 1.276 2003/08/16 03:39:00 cheshire
+<rdar://problem/3338440> InterfaceID -1 indicates "local only"
+
+Revision 1.275 2003/08/16 02:51:27 cheshire
+<rdar://problem/3366590> mDNSResponder takes too much RPRVT
+Don't try to compute namehash etc, until *after* validating the name
+
+Revision 1.274 2003/08/16 01:12:40 cheshire
+<rdar://problem/3366590> mDNSResponder takes too much RPRVT
+Now that the minimum rdata object size has been reduced to 64 bytes, it is no longer safe to do a
+simple C structure assignment of a domainname, because that object is defined to be 256 bytes long,
+and in the process of copying it, the C compiler may run off the end of the rdata object into
+unmapped memory. All assignments of domainname objects of uncertain size are now replaced with a
+call to the macro AssignDomainName(), which is careful to copy only as many bytes as are valid.
+
+Revision 1.273 2003/08/15 20:16:02 cheshire
+<rdar://problem/3366590> 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.272 2003/08/14 19:29:04 cheshire
+<rdar://problem/3378473> Include cache records in SIGINFO output
+Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSClientAPI.h so daemon.c can use them
+
+Revision 1.271 2003/08/14 02:17:05 cheshire
+<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
+
+Revision 1.270 2003/08/13 17:07:28 ksekar
+Bug #: <rdar://problem/3376458>: Extra RR linked to list even if registration fails - causes crash
+Added check to result of mDNS_Register() before linking extra record into list.
+
+Revision 1.269 2003/08/12 19:56:23 cheshire
+Update to APSL 2.0
+
+Revision 1.268 2003/08/12 15:01:10 cheshire
+Add comments
+
+Revision 1.267 2003/08/12 14:59:27 cheshire
+<rdar://problem/3374490> 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.266 2003/08/12 12:47:16 cheshire
+In mDNSCoreMachineSleep debugf message, display value of m->timenow
+
+Revision 1.265 2003/08/11 20:04:28 cheshire
+<rdar://problem/3366553> Improve efficiency by restricting cases where we have to walk the entire cache
+
+Revision 1.264 2003/08/09 00:55:02 cheshire
+<rdar://problem/3366553> mDNSResponder is taking 20-30% of the CPU
+Don't scan the whole cache after every packet.
+
+Revision 1.263 2003/08/09 00:35:29 cheshire
+Moved AnswerNewQuestion() later in the file, in preparation for next checkin
+
+Revision 1.262 2003/08/08 19:50:33 cheshire
+<rdar://problem/3370332> Remove "Cache size now xxx" messages
+
+Revision 1.261 2003/08/08 19:18:45 cheshire
+<rdar://problem/3271219> Only retrigger questions on platforms with the "PhantomInterfaces" bug
+
+Revision 1.260 2003/08/08 18:55:48 cheshire
+<rdar://problem/3370365> Guard against time going backwards
+
+Revision 1.259 2003/08/08 18:36:04 cheshire
+<rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug
+
+Revision 1.258 2003/08/08 16:22:05 cheshire
+<rdar://problem/3335473> Need to check validity of TXT (and other) records
+Remove unneeded LogMsg
+
+Revision 1.257 2003/08/07 01:41:08 cheshire
+<rdar://problem/3367346> Ignore packets with invalid source address (all zeroes or all ones)
+
+Revision 1.256 2003/08/06 23:25:51 cheshire
+<rdar://problem/3290674> Increase TTL for A/AAAA/SRV from one minute to four
+
+Revision 1.255 2003/08/06 23:22:50 cheshire
+Add symbolic constants: kDefaultTTLforUnique (one minute) and kDefaultTTLforShared (two hours)
+
+Revision 1.254 2003/08/06 21:33:39 cheshire
+Fix compiler warnings on PocketPC 2003 (Windows CE)
+
+Revision 1.253 2003/08/06 20:43:57 cheshire
+<rdar://problem/3335473> Need to check validity of TXT (and other) records
+Created ValidateDomainName() and ValidateRData(), used by mDNS_Register_internal() and mDNS_Update()
+
+Revision 1.252 2003/08/06 20:35:47 cheshire
+Enhance debugging routine GetRRDisplayString() so it can also be used to display
+other RDataBody objects, not just the one currently attached the given ResourceRecord
+
+Revision 1.251 2003/08/06 19:07:34 cheshire
+<rdar://problem/3366251> mDNSResponder not inhibiting multicast responses as much as it should
+Was checking LastAPTime instead of LastMCTime
+
+Revision 1.250 2003/08/06 19:01:55 cheshire
+Update comments
+
+Revision 1.249 2003/08/06 00:13:28 cheshire
+Tidy up debugf messages
+
+Revision 1.248 2003/08/05 22:20:15 cheshire
+<rdar://problem/3330324> Need to check IP TTL on responses
+
+Revision 1.247 2003/08/05 00:56:39 cheshire
+<rdar://problem/3357075> mDNSResponder sending additional records, even after precursor record suppressed
+
+Revision 1.246 2003/08/04 19:20:49 cheshire
+Add kDNSQType_ANY to list in DNSTypeName() so it can be displayed in debugging messages
+
+Revision 1.245 2003/08/02 01:56:29 cheshire
+For debugging: log message if we ever get more than one question in a truncated packet
+
+Revision 1.244 2003/08/01 23:55:32 cheshire
+Fix for compiler warnings on Windows, submitted by Bob Bradley
+
+Revision 1.243 2003/07/25 02:26:09 cheshire
+Typo: FIxed missing semicolon
+
+Revision 1.242 2003/07/25 01:18:41 cheshire
+Fix memory leak on shutdown in mDNS_Close() (detected in Windows version)
+
+Revision 1.241 2003/07/23 21:03:42 cheshire
+Only show "Found record..." debugf message in verbose mode
+
+Revision 1.240 2003/07/23 21:01:11 cheshire
+<rdar://problem/3340584> Need Nagle-style algorithm to coalesce multiple packets into one
+After sending a packet, suppress further sending for the next 100ms.
+
+Revision 1.239 2003/07/22 01:30:05 cheshire
+<rdar://problem/3329099> Don't try to add the same question to the duplicate-questions list more than once
+
+Revision 1.238 2003/07/22 00:10:20 cheshire
+<rdar://problem/3337355> ConvertDomainLabelToCString() needs to escape escape characters
+
+Revision 1.237 2003/07/19 03:23:13 cheshire
+<rdar://problem/2986147> mDNSResponder needs to receive and cache larger records
+
+Revision 1.236 2003/07/19 03:04:55 cheshire
+Fix warnings; some debugf message improvements
+
+Revision 1.235 2003/07/19 00:03:32 cheshire
+<rdar://problem/3160248> ScheduleNextTask needs to be smarter after a no-op packet is received
+ScheduleNextTask is quite an expensive operation.
+We don't need to do all that work after receiving a no-op packet that didn't change our state.
+
+Revision 1.234 2003/07/18 23:52:11 cheshire
+To improve consistency of field naming, global search-and-replace:
+NextProbeTime -> NextScheduledProbe
+NextResponseTime -> NextScheduledResponse
+
+Revision 1.233 2003/07/18 00:29:59 cheshire
+<rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead
+
+Revision 1.232 2003/07/18 00:11:38 cheshire
+Add extra case to switch statements to handle HINFO data for Get, Put and Display
+(In all but GetRDLength(), this is is just a fall-through to kDNSType_TXT)
+
+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
+<rdar://problem/3319418> Rendezvous 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
+<rdar://problem/3325583> Rate-limit responses, to guard against packet flooding
+
+Revision 1.228 2003/07/16 20:50:27 cheshire
+<rdar://problem/3315761> Need to implement "unicast response" request, using top bit of qclass
+
+Revision 1.227 2003/07/16 05:01:36 cheshire
+Add fields 'LargeAnswers' and 'ExpectUnicastResponse' in preparation for
+<rdar://problem/3315761> Need to implement "unicast response" request, using top bit of qclass
+
+Revision 1.226 2003/07/16 04:51:44 cheshire
+Fix use of constant 'mDNSPlatformOneSecond' where it should have said 'InitialQuestionInterval'
+
+Revision 1.225 2003/07/16 04:46:41 cheshire
+Minor wording cleanup: The correct DNS term is "response", not "reply"
+
+Revision 1.224 2003/07/16 04:39:02 cheshire
+Textual cleanup (no change to functionality):
+Construct "c >= 'A' && c <= 'Z'" appears in too many places; replaced with macro "mDNSIsUpperCase(c)"
+
+Revision 1.223 2003/07/16 00:09:22 cheshire
+Textual cleanup (no change to functionality):
+Construct "((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond)" appears in too many places;
+replace with macro "TicksTTL(rr)"
+Construct "rr->TimeRcvd + ((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond)"
+replaced with macro "RRExpireTime(rr)"
+
+Revision 1.222 2003/07/15 23:40:46 cheshire
+Function rename: UpdateDupSuppressInfo() is more accurately called ExpireDupSuppressInfo()
+
+Revision 1.221 2003/07/15 22:17:56 cheshire
+<rdar://problem/3328394> mDNSResponder is not being efficient when doing certain queries
+
+Revision 1.220 2003/07/15 02:12:51 cheshire
+Slight tidy-up of debugf messages and comments
+
+Revision 1.219 2003/07/15 01:55:12 cheshire
+<rdar://problem/3315777> Need to implement service registration with subtypes
+
+Revision 1.218 2003/07/14 16:26:06 cheshire
+<rdar://problem/3324795> Duplicate query suppression not working right
+Refinement: Don't record DS information for a question in the first quarter second
+right after we send it -- in the case where a question happens to be accelerated by
+the maximum allowed amount, we don't want it to then be suppressed because the previous
+time *we* sent that question falls (just) within the valid duplicate suppression window.
+
+Revision 1.217 2003/07/13 04:43:53 cheshire
+<rdar://problem/3325169> Services on multiple interfaces not always resolving
+Minor refinement: No need to make address query broader than the original SRV query that provoked it
+
+Revision 1.216 2003/07/13 03:13:17 cheshire
+<rdar://problem/3325169> Services on multiple interfaces not always resolving
+If we get an identical SRV on a second interface, convert address queries to non-specific
+
+Revision 1.215 2003/07/13 02:28:00 cheshire
+<rdar://problem/3325166> SendResponses didn't all its responses
+Delete all references to RRInterfaceActive -- it's now superfluous
+
+Revision 1.214 2003/07/13 01:47:53 cheshire
+Fix one error and one warning in the Windows build
+
+Revision 1.213 2003/07/12 04:25:48 cheshire
+Fix minor signed/unsigned warnings
+
+Revision 1.212 2003/07/12 01:59:11 cheshire
+Minor changes to debugf messages
+
+Revision 1.211 2003/07/12 01:47:01 cheshire
+<rdar://problem/3324495> After name conflict, appended number should be higher than previous number
+
+Revision 1.210 2003/07/12 01:43:28 cheshire
+<rdar://problem/3324795> Duplicate query suppression not working right
+The correct cutoff time for duplicate query suppression is timenow less one-half the query interval.
+The code was incorrectly using the last query time plus one-half the query interval.
+This was only correct in the case where query acceleration was not in effect.
+
+Revision 1.209 2003/07/12 01:27:50 cheshire
+<rdar://problem/3320079> Hostname conflict naming should not use two hyphens
+Fix missing "-1" in RemoveLabelSuffix()
+
+Revision 1.208 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.207 2003/07/11 01:28:00 cheshire
+<rdar://problem/3161289> No more local.arpa
+
+Revision 1.206 2003/07/11 00:45:02 cheshire
+<rdar://problem/3321909> Client should get callback confirming successful host name registration
+
+Revision 1.205 2003/07/11 00:40:18 cheshire
+Tidy up debug message in HostNameCallback()
+
+Revision 1.204 2003/07/11 00:20:32 cheshire
+<rdar://problem/3320087> mDNSResponder should log a message after 16 unsuccessful probes
+
+Revision 1.203 2003/07/10 23:53:41 cheshire
+<rdar://problem/3320079> Hostname conflict naming should not use two hyphens
+
+Revision 1.202 2003/07/04 02:23:20 cheshire
+<rdar://problem/3311955> Responder too aggressive at flushing stale data
+Changed mDNSResponder to require four unanswered queries before purging a record, instead of two.
+
+Revision 1.201 2003/07/04 01:09:41 cheshire
+<rdar://problem/3315775> Need to implement subtype queries
+Modified ConstructServiceName() to allow three-part service types
+
+Revision 1.200 2003/07/03 23:55:26 cheshire
+Minor change to wording of syslog warning messages
+
+Revision 1.199 2003/07/03 23:51:13 cheshire
+<rdar://problem/3315652>: Lots of "have given xxx answers" syslog warnings
+Added more detailed debugging information
+
+Revision 1.198 2003/07/03 22:19:30 cheshire
+<rdar://problem/3314346> Bug fix in 3274153 breaks TiVo
+Make exception to allow _tivo_servemedia._tcp.
+
+Revision 1.197 2003/07/02 22:33:05 cheshire
+<rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
+Minor refinements:
+When cache is exhausted, verify that rrcache_totalused == rrcache_size and report if not
+Allow cache to grow to 512 records before considering it a potential denial-of-service attack
+
+Revision 1.196 2003/07/02 21:19:45 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.195 2003/07/02 19:56:58 cheshire
+<rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
+Minor refinement: m->rrcache_active was not being decremented when
+an active record was deleted because its TTL expired
+
+Revision 1.194 2003/07/02 18:47:40 cheshire
+Minor wording change to log messages
+
+Revision 1.193 2003/07/02 02:44:13 cheshire
+Fix warning in non-debug build
+
+Revision 1.192 2003/07/02 02:41:23 cheshire
+<rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
+
+Revision 1.191 2003/07/02 02:30:51 cheshire
+HashSlot() returns an array index. It can't be negative; hence it should not be signed.
+
+Revision 1.190 2003/06/27 00:03:05 vlubet
+<rdar://problem/3304625> Merge of build failure fix for gcc 3.3
+
+Revision 1.189 2003/06/11 19:24:03 cheshire
+<rdar://problem/3287141> Crash in SendQueries/SendResponses when no active interfaces
+Slight refinement to previous checkin
+
+Revision 1.188 2003/06/10 20:33:28 cheshire
+<rdar://problem/3287141> Crash in SendQueries/SendResponses when no active interfaces
+
+Revision 1.187 2003/06/10 04:30:44 cheshire
+<rdar://problem/3286234> Need to re-probe/re-announce on configuration change
+Only interface-specific records were re-probing and re-announcing, not non-specific records.
+
+Revision 1.186 2003/06/10 04:24:39 cheshire
+<rdar://problem/3283637> 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.185 2003/06/10 03:52:49 cheshire
+Update comments and debug messages
+
+Revision 1.184 2003/06/10 02:26:39 cheshire
+<rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function
+Make mDNS_Reconfirm() call mDNS_Lock(), like the other API routines
+
+Revision 1.183 2003/06/09 18:53:13 cheshire
+Simplify some debugf() statements (replaced block of 25 lines with 2 lines)
+
+Revision 1.182 2003/06/09 18:38:42 cheshire
+<rdar://problem/3285082> Need to be more tolerant when there are mDNS proxies on the network
+Only issue a correction if the TTL in the proxy packet is less than half the correct value.
+
+Revision 1.181 2003/06/07 06:45:05 cheshire
+<rdar://problem/3283666> No need for multiple machines to all be sending the same queries
+
+Revision 1.180 2003/06/07 06:31:07 cheshire
+Create little four-line helper function "FindIdenticalRecordInCache()"
+
+Revision 1.179 2003/06/07 06:28:13 cheshire
+For clarity, change name of "DNSQuestion q" to "DNSQuestion pktq"
+
+Revision 1.178 2003/06/07 06:25:12 cheshire
+Update some comments
+
+Revision 1.177 2003/06/07 04:50:53 cheshire
+<rdar://problem/3283637> React when we observe other people query unsuccessfully for a record that's in our cache
+
+Revision 1.176 2003/06/07 04:33:26 cheshire
+<rdar://problem/3283540> When query produces zero results, call mDNS_Reconfirm() on any antecedent records
+Minor change: Increment/decrement logic for q->CurrentAnswers should be in
+CacheRecordAdd() and CacheRecordRmv(), not AnswerQuestionWithResourceRecord()
+
+Revision 1.175 2003/06/07 04:11:52 cheshire
+Minor changes to comments and debug messages
+
+Revision 1.174 2003/06/07 01:46:38 cheshire
+<rdar://problem/3283540> When query produces zero results, call mDNS_Reconfirm() on any antecedent records
+
+Revision 1.173 2003/06/07 01:22:13 cheshire
+<rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function
+
+Revision 1.172 2003/06/07 00:59:42 cheshire
+<rdar://problem/3283454> Need some randomness to spread queries on the network
+
+Revision 1.171 2003/06/06 21:41:10 cheshire
+For consistency, mDNS_StopQuery() should return an mStatus result, just like all the other mDNSCore routines
+
+Revision 1.170 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.169 2003/06/06 21:35:55 cheshire
+Fix mis-named macro: GetRRHostNameTarget is really GetRRDomainNameTarget
+(the target is a domain name, but not necessarily a host name)
+
+Revision 1.168 2003/06/06 21:33:31 cheshire
+Instead of using (mDNSPlatformOneSecond/2) all over the place, define a constant "InitialQuestionInterval"
+
+Revision 1.167 2003/06/06 21:30:42 cheshire
+<rdar://problem/3282962> Don't delay queries for shared record types
+
+Revision 1.166 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.165 2003/06/04 02:53:21 cheshire
+Add some "#pragma warning" lines so it compiles clean on Microsoft compilers
+
+Revision 1.164 2003/06/04 01:25:33 cheshire
+<rdar://problem/3274950> Cannot perform multi-packet known-answer suppression messages
+Display time interval between first and subsequent queries
+
+Revision 1.163 2003/06/03 19:58:14 cheshire
+<rdar://problem/3277665> mDNS_DeregisterService() fixes:
+When forcibly deregistering after a conflict, ensure we don't send an incorrect goodbye packet.
+Guard against a couple of possible mDNS_DeregisterService() race conditions.
+
+Revision 1.162 2003/06/03 19:30:39 cheshire
+Minor addition refinements for
+<rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be
+
+Revision 1.161 2003/06/03 18:29:03 cheshire
+Minor changes to comments and debugf() messages
+
+Revision 1.160 2003/06/03 05:02:16 cheshire
+<rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be
+
+Revision 1.159 2003/06/03 03:31:57 cheshire
+<rdar://problem/3277033> False self-conflict when there are duplicate registrations on one machine
+
+Revision 1.158 2003/06/02 22:57:09 cheshire
+Minor clarifying changes to comments and log messages;
+IdenticalResourceRecordAnyInterface() is really more accurately called just IdenticalResourceRecord()
+
+Revision 1.157 2003/05/31 00:09:49 cheshire
+<rdar://problem/3274862> Add ability to discover what services are on a network
+
+Revision 1.156 2003/05/30 23:56:49 cheshire
+<rdar://problem/3274847> Crash after error in mDNS_RegisterService()
+Need to set "sr->Extras = mDNSNULL" before returning
+
+Revision 1.155 2003/05/30 23:48:00 cheshire
+<rdar://problem/3274832> Announcements not properly grouped
+Due to inconsistent setting of rr->LastAPTime at different places in the
+code, announcements were not properly grouped into a single packet.
+Fixed by creating a single routine called InitializeLastAPTime().
+
+Revision 1.154 2003/05/30 23:38:14 cheshire
+<rdar://problem/3274814> Fix error in IPv6 reverse-mapping PTR records
+Wrote buffer[32] where it should have said buffer[64]
+
+Revision 1.153 2003/05/30 19:10:56 cheshire
+<rdar://problem/3274153> ConstructServiceName needs to be more restrictive
+
+Revision 1.152 2003/05/29 22:39:16 cheshire
+<rdar://problem/3273209> Don't truncate strings in the middle of a UTF-8 character
+
+Revision 1.151 2003/05/29 06:35:42 cheshire
+<rdar://problem/3272221> mDNSCoreReceiveResponse() purging wrong record
+
+Revision 1.150 2003/05/29 06:25:45 cheshire
+<rdar://problem/3272218> Need to call CheckCacheExpiration() *before* AnswerNewQuestion()
+
+Revision 1.149 2003/05/29 06:18:39 cheshire
+<rdar://problem/3272217> Split AnswerLocalQuestions into CacheRecordAdd and CacheRecordRmv
+
+Revision 1.148 2003/05/29 06:11:34 cheshire
+<rdar://problem/3272214> Report if there appear to be too many "Resolve" callbacks
+
+Revision 1.147 2003/05/29 06:01:18 cheshire
+Change some debugf() calls to LogMsg() calls to help with debugging
+
+Revision 1.146 2003/05/28 21:00:44 cheshire
+Re-enable "immediate answer burst" debugf message
+
+Revision 1.145 2003/05/28 20:57:44 cheshire
+<rdar://problem/3271550> mDNSResponder reports "Cannot perform multi-packet
+known-answer suppression ..." This is a known issue caused by a bug in the OS X 10.2
+version of mDNSResponder, so for now we should suppress this warning message.
+
+Revision 1.144 2003/05/28 18:05:12 cheshire
+<rdar://problem/3009899> mDNSResponder allows invalid service registrations
+Fix silly mistake: old logic allowed "TDP" and "UCP" as valid names
+
+Revision 1.143 2003/05/28 04:31:29 cheshire
+<rdar://problem/3270733> mDNSResponder not sending probes at the prescribed time
+
+Revision 1.142 2003/05/28 03:13:07 cheshire
+<rdar://problem/3009899> mDNSResponder allows invalid service registrations
+Require that the transport protocol be _udp or _tcp
+
+Revision 1.141 2003/05/28 02:19:12 cheshire
+<rdar://problem/3270634> Misleading messages generated by iChat
+Better fix: Only generate the log message for queries where the TC bit is set.
+
+Revision 1.140 2003/05/28 01:55:24 cheshire
+Minor change to log messages
+
+Revision 1.139 2003/05/28 01:52:51 cheshire
+<rdar://problem/3270634> Misleading messages generated by iChat
+
+Revision 1.138 2003/05/27 22:35:00 cheshire
+<rdar://problem/3270277> mDNS_RegisterInterface needs to retrigger questions
+
+Revision 1.137 2003/05/27 20:04:33 cheshire
+<rdar://problem/3269900> mDNSResponder crash in mDNS_vsnprintf()
+
+Revision 1.136 2003/05/27 18:50:07 cheshire
+<rdar://problem/3269768> mDNS_StartResolveService doesn't inform client of port number changes
+
+Revision 1.135 2003/05/26 04:57:28 cheshire
+<rdar://problem/3268953> Delay queries when there are already answers in the cache
+
+Revision 1.134 2003/05/26 04:54:54 cheshire
+<rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
+Accidentally deleted '%' case from the switch statement
+
+Revision 1.133 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.132 2003/05/26 03:01:26 cheshire
+<rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
+
+Revision 1.131 2003/05/26 00:42:05 cheshire
+<rdar://problem/3268876> Temporarily include mDNSResponder version in packets
+
+Revision 1.130 2003/05/24 16:39:48 cheshire
+<rdar://problem/3268631> SendResponses also needs to handle multihoming better
+
+Revision 1.129 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.128 2003/05/23 01:55:13 cheshire
+<rdar://problem/3267127> After name change, mDNSResponder needs to re-probe for name uniqueness
+
+Revision 1.127 2003/05/23 01:02:15 ksekar
+Bug #: <rdar://problem/3032577>: mDNSResponder needs to include unique id in default name
+
+Revision 1.126 2003/05/22 02:29:22 cheshire
+<rdar://problem/2984918> SendQueries needs to handle multihoming better
+Complete rewrite of SendQueries. Works much better now :-)
+
+Revision 1.125 2003/05/22 01:50:45 cheshire
+Fix warnings, and improve log messages
+
+Revision 1.124 2003/05/22 01:41:50 cheshire
+DiscardDeregistrations doesn't need InterfaceID parameter
+
+Revision 1.123 2003/05/22 01:38:55 cheshire
+Change bracketing of #pragma mark
+
+Revision 1.122 2003/05/21 19:59:04 cheshire
+<rdar://problem/3148431> ER: Tweak responder's default name conflict behavior
+Minor refinements; make sure we don't truncate in the middle of a multi-byte UTF-8 character
+
+Revision 1.121 2003/05/21 17:54:07 ksekar
+Bug #: <rdar://problem/3148431> ER: Tweak responder's default name conflict behavior
+New rename behavior - domain name "foo" becomes "foo--2" on conflict, richtext name becomes "foo (2)"
+
+Revision 1.120 2003/05/19 22:14:14 ksekar
+<rdar://problem/3162914> mDNS probe denials/conflicts not detected unless conflict is of the same type
+
+Revision 1.119 2003/05/16 01:34:10 cheshire
+Fix some warnings
+
+Revision 1.118 2003/05/14 18:48:40 cheshire
+<rdar://problem/3159272> 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.117 2003/05/14 07:08:36 cheshire
+<rdar://problem/3159272> 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.116 2003/05/14 06:51:56 cheshire
+<rdar://problem/3027144> Rendezvous doesn't refresh server info if changed during sleep
+
+Revision 1.115 2003/05/14 06:44:31 cheshire
+Improve debugging message
+
+Revision 1.114 2003/05/07 01:47:03 cheshire
+<rdar://problem/3250330> Also protect against NULL domainlabels
+
+Revision 1.113 2003/05/07 00:28:18 cheshire
+<rdar://problem/3250330> Need to make mDNSResponder more defensive against bad clients
+
+Revision 1.112 2003/05/06 00:00:46 cheshire
+<rdar://problem/3248914> Rationalize naming of domainname manipulation functions
+
+Revision 1.111 2003/05/05 23:42:08 cheshire
+<rdar://problem/3245631> Resolves never succeed
+Was setting "rr->LastAPTime = timenow - rr->LastAPTime"
+instead of "rr->LastAPTime = timenow - rr->ThisAPInterval"
+
+Revision 1.110 2003/04/30 21:09:59 cheshire
+<rdar://problem/3244727> mDNS_vsnprintf needs to be more defensive against invalid domain names
+
+Revision 1.109 2003/04/26 02:41:56 cheshire
+<rdar://problem/3241281> Change timenow from a local variable to a structure member
+
+Revision 1.108 2003/04/25 01:45:56 cheshire
+<rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name
+
+Revision 1.107 2003/04/25 00:41:31 cheshire
+<rdar://problem/3239912> Create single routine PurgeCacheResourceRecord(), to avoid bugs in future
+
+Revision 1.106 2003/04/22 03:14:45 cheshire
+<rdar://problem/3232229> Include Include instrumented mDNSResponder in panther now
+
+Revision 1.105 2003/04/22 01:07:43 cheshire
+<rdar://problem/3176248> DNSServiceRegistrationUpdateRecord should support a default ttl
+If TTL parameter is zero, leave record TTL unchanged
+
+Revision 1.104 2003/04/21 19:15:52 cheshire
+Fix some compiler warnings
+
+Revision 1.103 2003/04/19 02:26:35 cheshire
+Bug #: <rdar://problem/3233804> Incorrect goodbye packet after conflict
+
+Revision 1.102 2003/04/17 03:06:28 cheshire
+Bug #: <rdar://problem/3231321> No need to query again when a service goes away
+Set UnansweredQueries to 2 when receiving a "goodbye" packet
+
+Revision 1.101 2003/04/15 20:58:31 jgraessl
+Bug #: 3229014
+Added a hash to lookup records in the cache.
+
+Revision 1.100 2003/04/15 18:53:14 cheshire
+Bug #: <rdar://problem/3229064> Bug in ScheduleNextTask
+mDNS.c 1.94 incorrectly combined two "if" statements into one.
+
+Revision 1.99 2003/04/15 18:09:13 jgraessl
+Bug #: 3228892
+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.98 2003/04/03 03:43:55 cheshire
+<rdar://problem/3216837> Off-by-one error in probe rate limiting
+
+Revision 1.97 2003/04/02 01:48:17 cheshire
+<rdar://problem/3212360> mDNSResponder sometimes suffers false self-conflicts when it sees its own packets
+Additional fix pointed out by Josh:
+Also set ProbeFailTime when incrementing NumFailedProbes when resetting a record back to probing state
+
+Revision 1.96 2003/04/01 23:58:55 cheshire
+Minor comment changes
+
+Revision 1.95 2003/04/01 23:46:05 cheshire
+<rdar://problem/3214832> mDNSResponder can get stuck in infinite loop after many location cycles
+mDNS_DeregisterInterface() flushes the RR cache by marking all records received on that interface
+to expire in one second. However, if a mDNS_StartResolveService() call is made in that one-second
+window, it can get an SRV answer from one of those soon-to-be-deleted records, resulting in
+FoundServiceInfoSRV() making an interface-specific query on the interface that was just removed.
+
+Revision 1.94 2003/03/29 01:55:19 cheshire
+<rdar://problem/3212360> mDNSResponder sometimes suffers false self-conflicts when it sees its own packets
+Solution: Major cleanup of packet timing and conflict handling rules
+
+Revision 1.93 2003/03/28 01:54:36 cheshire
+Minor tidyup of IPv6 (AAAA) code
+
+Revision 1.92 2003/03/27 03:30:55 cheshire
+<rdar://problem/3210018> 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.91 2003/03/15 04:40:36 cheshire
+Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID"
+
+Revision 1.90 2003/03/14 20:26:37 cheshire
+Reduce debugging messages (reclassify some "debugf" as "verbosedebugf")
+
+Revision 1.89 2003/03/12 19:57:50 cheshire
+Fixed typo in debug message
+
+Revision 1.88 2003/03/12 00:17:44 cheshire
+<rdar://problem/3195426> GetFreeCacheRR needs to be more willing to throw away recent records
+
+Revision 1.87 2003/03/11 01:27:20 cheshire
+Reduce debugging messages (reclassify some "debugf" as "verbosedebugf")
+
+Revision 1.86 2003/03/06 20:44:33 cheshire
+Comment tidyup
+
+Revision 1.85 2003/03/05 03:38:35 cheshire
+Bug #: 3185731 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.84 2003/03/05 01:27:30 cheshire
+Bug #: 3185482 Different TTL for multicast versus unicast responses
+When building unicast responses, record TTLs are capped to 10 seconds
+
+Revision 1.83 2003/03/04 23:48:52 cheshire
+Bug #: 3188865 Double probes after wake from sleep
+Don't reset record type to kDNSRecordTypeUnique if record is DependentOn another
+
+Revision 1.82 2003/03/04 23:38:29 cheshire
+Bug #: 3099194 mDNSResponder needs performance improvements
+Only set rr->CRActiveQuestion to point to the
+currently active representative of a question set
+
+Revision 1.81 2003/02/21 03:35:34 cheshire
+Bug #: 3179007 mDNSResponder needs to include AAAA records in additional answer section
+
+Revision 1.80 2003/02/21 02:47:53 cheshire
+Bug #: 3099194 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.79 2003/02/21 01:54:07 cheshire
+Bug #: 3099194 mDNSResponder needs performance improvements
+Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt")
+
+Revision 1.78 2003/02/20 06:48:32 cheshire
+Bug #: 3169535 Xserve RAID needs to do interface-specific registrations
+Reviewed by: Josh Graessley, Bob Bradley
+
+Revision 1.77 2003/01/31 03:35:59 cheshire
+Bug #: 3147097 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.76 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.75 2003/01/29 01:47:40 cheshire
+Rename 'Active' to 'CRActive' or 'InterfaceActive' for improved clarity
+
+Revision 1.74 2003/01/28 05:26:25 cheshire
+Bug #: 3147097 mDNSResponder sometimes fails to find the correct results
+Add 'Active' flag for interfaces
+
+Revision 1.73 2003/01/28 03:45:12 cheshire
+Fixed missing "not" in "!mDNSAddrIsDNSMulticast(dstaddr)"
+
+Revision 1.72 2003/01/28 01:49:48 cheshire
+Bug #: 3147097 mDNSResponder sometimes fails to find the correct results
+FindDuplicateQuestion() was incorrectly finding the question itself in the list,
+and incorrectly marking it as a duplicate (of itself), so that it became inactive.
+
+Revision 1.71 2003/01/28 01:41:44 cheshire
+Bug #: 3153091 Race condition when network change causes bad stuff
+When an interface goes away, interface-specific questions on that interface become orphaned.
+Orphan questions cause HaveQueries to return true, but there's no interface to send them on.
+Fix: mDNS_DeregisterInterface() now calls DeActivateInterfaceQuestions()
+
+Revision 1.70 2003/01/23 19:00:20 cheshire
+Protect against infinite loops in mDNS_Execute
+
+Revision 1.69 2003/01/21 22:56:32 jgraessl
+Bug #: 3124348 service name changes are not properly handled
+Submitted by: Stuart Cheshire
+Reviewed by: Joshua Graessley
+Applying changes for 3124348 to main branch. 3124348 changes went in to a
+branch for SU.
+
+Revision 1.68 2003/01/17 04:09:27 cheshire
+Bug #: 3141038 mDNSResponder Resolves are unreliable on multi-homed hosts
+
+Revision 1.67 2003/01/17 03:56:45 cheshire
+Default 24-hour TTL is far too long. Changing to two hours.
+
+Revision 1.66 2003/01/13 23:49:41 jgraessl
+Merged changes for the following fixes in to top of tree:
+3086540 computer name changes not handled properly
+3124348 service name changes are not properly handled
+3124352 announcements sent in pairs, failing chattiness test
+
+Revision 1.65 2002/12/23 22:13:28 jgraessl
+Reviewed by: Stuart Cheshire
+Initial IPv6 support for mDNSResponder.
+
+Revision 1.64 2002/11/26 20:49:06 cheshire
+Bug #: 3104543 RFC 1123 allows the first character of a name label to be either a letter or a digit
+
+Revision 1.63 2002/09/21 20:44:49 zarzycki
+Added APSL info
+
+Revision 1.62 2002/09/20 03:25:37 cheshire
+Fix some compiler warnings
+
+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
+of a particular named service
+
+Revision 1.59 2002/09/19 21:25:34 cheshire
+mDNS_snprintf() doesn't need to be in a separate file
+
+Revision 1.58 2002/09/19 04:20:43 cheshire
+Remove high-ascii characters that confuse some systems
+
+Revision 1.57 2002/09/17 01:07:08 cheshire
+Change mDNS_AdvertiseLocalAddresses to be a parameter to mDNS_Init()
+
+Revision 1.56 2002/09/16 19:44:17 cheshire
+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 "mDNSPlatformFunctions.h" // Defines the interface required of the supporting layer below
-#include "mDNSsprintf.h"
-
-extern void LogErrorMessage(const char *format, ...);
+// Disable certain benign warnings with Microsoft compilers
#if(defined(_MSC_VER))
- // Disable warnings about Microsoft Visual Studio/C++ not understanding "pragma unused"
- #pragma warning( disable:4068 )
+ // Disable "conditional expression is constant" warning for debug macros.
+ // 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 "const object should be initialized"
+ // We know that static/globals are defined to be zeroed in ANSI C, and to avoid this warning would require some
+ // *really* ugly chunk of zeroes and curly braces to initialize zeroRR and mDNSprintf_format_default to all zeroes
+ #pragma warning(disable:4132)
+
+ // Disable "assignment within conditional expression".
+ // Other compilers understand the convention that if you place the assignment expression within an extra pair
+ // of parentheses, this signals to the compiler that you really intended an assignment and no warning is necessary.
+ // The Microsoft compiler doesn't understand this convention, so in the absense of any other way to signal
+ // to the compiler that the assignment is intentional, we have to just turn this warning off completely.
+ #pragma warning(disable:4706)
#endif
// ***************************************************************************
-#if 0
+#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark - DNS Protocol Constants
#endif
} DNS_Flags;
// ***************************************************************************
-#if 0
+#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark - Program Constants
#endif
-mDNSexport const ResourceRecord zeroRR = { 0 };
-mDNSexport const mDNSIPPort zeroIPPort = { { 0 } };
-mDNSexport const mDNSIPAddr zeroIPAddr = { { 0 } };
-mDNSexport const mDNSIPAddr onesIPAddr = { { 255, 255, 255, 255 } };
+mDNSexport const ResourceRecord zeroRR;
+mDNSexport const mDNSIPPort zeroIPPort = { { 0 } };
+mDNSexport const mDNSv4Addr zeroIPAddr = { { 0 } };
+mDNSexport const mDNSv6Addr zerov6Addr = { { 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 } };
+mDNSlocal const mDNSAddr zeroAddr = { mDNSAddrType_None, {{{ 0 }}} };
+
+mDNSexport const mDNSInterfaceID mDNSInterface_Any = { 0 };
+mDNSlocal const mDNSInterfaceID mDNSInterfaceMark = { (mDNSInterfaceID)~0 };
#define UnicastDNSPortAsNumber 53
#define MulticastDNSPortAsNumber 5353
mDNSexport const mDNSIPPort UnicastDNSPort = { { UnicastDNSPortAsNumber >> 8, UnicastDNSPortAsNumber & 0xFF } };
mDNSexport const mDNSIPPort MulticastDNSPort = { { MulticastDNSPortAsNumber >> 8, MulticastDNSPortAsNumber & 0xFF } };
-mDNSexport const mDNSIPAddr AllDNSLinkGroup = { { 224, 0, 0, 251 } };
-mDNSexport const mDNSIPAddr AllDNSAdminGroup = { { 239, 255, 255, 251 } };
+mDNSexport const mDNSv4Addr AllDNSAdminGroup = { { 239, 255, 255, 251 } };
+mDNSexport const mDNSv4Addr AllDNSLinkGroup = { { 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 } } } };
static const mDNSOpaque16 zeroID = { { 0, 0 } };
static const mDNSOpaque16 QueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery, 0 } };
static const mDNSOpaque16 ResponseFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA, 0 } };
#define zeroDomainNamePtr ((domainname*)"")
+// Any records bigger than this are considered 'large' records
+#define SmallRecordLimit 1024
+
+#define kDefaultTTLforUnique 240
+#define kDefaultTTLforShared (2*3600)
+
+#define kMaxUpdateCredits 10
+
static const char *const mDNS_DomainTypeNames[] =
{
"_browse._mdns._udp.local.",
"_default._register._mdns._udp.local."
};
+#define AssignDomainName(DST, SRC) mDNSPlatformMemCopy((SRC).c, (DST).c, DomainNameLength(&(SRC)))
+
// ***************************************************************************
-#if 0
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Specialized mDNS version of vsnprintf
+#endif
+
+static const struct mDNSprintf_format
+ {
+ unsigned leftJustify : 1;
+ unsigned forceSign : 1;
+ unsigned zeroPad : 1;
+ unsigned havePrecision : 1;
+ unsigned hSize : 1;
+ unsigned lSize : 1;
+ char altForm;
+ char sign; // +, - or space
+ unsigned int fieldWidth;
+ unsigned int precision;
+ } mDNSprintf_format_default;
+
+mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg)
+ {
+ mDNSu32 nwritten = 0;
+ int c;
+ buflen--; // Pre-reserve one space in the buffer for the terminating nul
+
+ for (c = *fmt; c != 0; c = *++fmt)
+ {
+ if (c != '%')
+ {
+ *sbuffer++ = (char)c;
+ if (++nwritten >= buflen) goto exit;
+ }
+ else
+ {
+ unsigned int i=0, j;
+ // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for
+ // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc.
+ // The size needs to be enough for a 256-byte domain name plus some error text.
+ #define mDNS_VACB_Size 300
+ char mDNS_VACB[mDNS_VACB_Size];
+ #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size])
+ #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s))
+ char *s = mDNS_VACB_Lim, *digits;
+ struct mDNSprintf_format F = mDNSprintf_format_default;
+
+ while (1) // decode flags
+ {
+ c = *++fmt;
+ if (c == '-') F.leftJustify = 1;
+ else if (c == '+') F.forceSign = 1;
+ else if (c == ' ') F.sign = ' ';
+ else if (c == '#') F.altForm++;
+ else if (c == '0') F.zeroPad = 1;
+ else break;
+ }
+
+ if (c == '*') // decode field width
+ {
+ int f = va_arg(arg, int);
+ if (f < 0) { f = -f; F.leftJustify = 1; }
+ F.fieldWidth = (unsigned int)f;
+ c = *++fmt;
+ }
+ else
+ {
+ for (; c >= '0' && c <= '9'; c = *++fmt)
+ F.fieldWidth = (10 * F.fieldWidth) + (c - '0');
+ }
+
+ if (c == '.') // decode precision
+ {
+ if ((c = *++fmt) == '*')
+ { F.precision = va_arg(arg, unsigned int); c = *++fmt; }
+ else for (; c >= '0' && c <= '9'; c = *++fmt)
+ F.precision = (10 * F.precision) + (c - '0');
+ F.havePrecision = 1;
+ }
+
+ if (F.leftJustify) F.zeroPad = 0;
+
+ conv:
+ switch (c) // perform appropriate conversion
+ {
+ unsigned long n;
+ case 'h' : F.hSize = 1; c = *++fmt; goto conv;
+ case 'l' : // fall through
+ case 'L' : F.lSize = 1; c = *++fmt; goto conv;
+ case 'd' :
+ case 'i' : if (F.lSize) n = (unsigned long)va_arg(arg, long);
+ else n = (unsigned long)va_arg(arg, int);
+ if (F.hSize) n = (short) n;
+ if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; }
+ else if (F.forceSign) F.sign = '+';
+ goto decimal;
+ case 'u' : if (F.lSize) n = va_arg(arg, unsigned long);
+ else n = va_arg(arg, unsigned int);
+ if (F.hSize) n = (unsigned short) n;
+ F.sign = 0;
+ goto decimal;
+ decimal: if (!F.havePrecision)
+ {
+ if (F.zeroPad)
+ {
+ F.precision = F.fieldWidth;
+ if (F.sign) --F.precision;
+ }
+ if (F.precision < 1) F.precision = 1;
+ }
+ if (F.precision > mDNS_VACB_Size - 1)
+ F.precision = mDNS_VACB_Size - 1;
+ for (i = 0; n; n /= 10, i++) *--s = (char)(n % 10 + '0');
+ for (; i < F.precision; i++) *--s = '0';
+ if (F.sign) { *--s = F.sign; i++; }
+ break;
+
+ case 'o' : if (F.lSize) n = va_arg(arg, unsigned long);
+ else n = va_arg(arg, unsigned int);
+ if (F.hSize) n = (unsigned short) n;
+ if (!F.havePrecision)
+ {
+ if (F.zeroPad) F.precision = F.fieldWidth;
+ if (F.precision < 1) F.precision = 1;
+ }
+ if (F.precision > mDNS_VACB_Size - 1)
+ F.precision = mDNS_VACB_Size - 1;
+ for (i = 0; n; n /= 8, i++) *--s = (char)(n % 8 + '0');
+ if (F.altForm && i && *s != '0') { *--s = '0'; i++; }
+ for (; i < F.precision; i++) *--s = '0';
+ break;
+
+ case 'a' : {
+ unsigned char *a = va_arg(arg, unsigned char *);
+ if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
+ else
+ {
+ unsigned short *w = (unsigned short *)a;
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ if (F.altForm)
+ {
+ mDNSAddr *ip = (mDNSAddr*)a;
+ a = (unsigned char *)&ip->ip.v4;
+ w = (unsigned short *)&ip->ip.v6;
+ switch (ip->type)
+ {
+ case mDNSAddrType_IPv4: F.precision = 4; break;
+ case mDNSAddrType_IPv6: F.precision = 16; break;
+ default: F.precision = 0; break;
+ }
+ }
+ switch (F.precision)
+ {
+ case 4: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d",
+ a[0], a[1], a[2], a[3]); break;
+ case 6: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X",
+ a[0], a[1], a[2], a[3], a[4], a[5]); break;
+ case 16: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%04X:%04X:%04X:%04X:%04X:%04X:%04X:%04X",
+ w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7]); break;
+ default: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify address size "
+ "(i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break;
+ }
+ }
+ }
+ break;
+
+ case 'p' : F.havePrecision = F.lSize = 1;
+ F.precision = 8;
+ case 'X' : digits = "0123456789ABCDEF";
+ goto hexadecimal;
+ case 'x' : digits = "0123456789abcdef";
+ hexadecimal:if (F.lSize) n = va_arg(arg, unsigned long);
+ else n = va_arg(arg, unsigned int);
+ if (F.hSize) n = (unsigned short) n;
+ if (!F.havePrecision)
+ {
+ if (F.zeroPad)
+ {
+ F.precision = F.fieldWidth;
+ if (F.altForm) F.precision -= 2;
+ }
+ if (F.precision < 1) F.precision = 1;
+ }
+ if (F.precision > mDNS_VACB_Size - 1)
+ F.precision = mDNS_VACB_Size - 1;
+ for (i = 0; n; n /= 16, i++) *--s = digits[n % 16];
+ for (; i < F.precision; i++) *--s = '0';
+ if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; }
+ break;
+
+ case 'c' : *--s = (char)va_arg(arg, int); i = 1; break;
+
+ case 's' : s = va_arg(arg, char *);
+ if (!s) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
+ else switch (F.altForm)
+ {
+ case 0: { char *a=s; i=0; while(*a++) i++; break; } // C string
+ case 1: i = (unsigned char) *s++; break; // Pascal string
+ case 2: { // DNS label-sequence name
+ unsigned char *a = (unsigned char *)s;
+ s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
+ if (*a == 0) *s++ = '.'; // Special case for root DNS name
+ while (*a)
+ {
+ if (*a > 63) { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<INVALID LABEL LENGTH %u>>", *a); break; }
+ if (s + *a >= &mDNS_VACB[254]) { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<NAME TOO LONG>>"); break; }
+ s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "%#s.", a);
+ a += 1 + *a;
+ }
+ i = (mDNSu32)(s - mDNS_VACB);
+ s = mDNS_VACB; // Reset s back to the start of the buffer
+ break;
+ }
+ }
+ if (F.havePrecision && i > F.precision) // Make sure we don't truncate in the middle of a UTF-8 character
+ { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--; }
+ break;
+
+ case 'n' : s = va_arg(arg, char *);
+ if (F.hSize) * (short *) s = (short)nwritten;
+ else if (F.lSize) * (long *) s = (long)nwritten;
+ else * (int *) s = (int)nwritten;
+ continue;
+
+ default: s = mDNS_VACB;
+ i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c);
+
+ case '%' : *sbuffer++ = (char)c;
+ if (++nwritten >= buflen) goto exit;
+ break;
+ }
+
+ if (i < F.fieldWidth && !F.leftJustify) // Pad on the left
+ do {
+ *sbuffer++ = ' ';
+ if (++nwritten >= buflen) goto exit;
+ } while (i < --F.fieldWidth);
+
+ if (i > buflen - nwritten) // Make sure we don't truncate in the middle of a UTF-8 character
+ { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--; }
+ for (j=0; j<i; j++) *sbuffer++ = *s++; // Write the converted result
+ nwritten += i;
+ if (nwritten >= buflen) goto exit;
+
+ for (; i < F.fieldWidth; i++) // Pad on the right
+ {
+ *sbuffer++ = ' ';
+ if (++nwritten >= buflen) goto exit;
+ }
+ }
+ }
+ exit:
+ *sbuffer++ = 0;
+ return(nwritten);
+ }
+
+mDNSexport mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...)
+ {
+ mDNSu32 length;
+
+ va_list ptr;
+ va_start(ptr,fmt);
+ length = mDNS_vsnprintf(sbuffer, buflen, fmt, ptr);
+ va_end(ptr);
+
+ return(length);
+ }
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark - General Utility Functions
#endif
-#if DEBUGBREAKS
-mDNSlocal char *DNSTypeName(mDNSu16 rrtype)
+mDNSexport char *DNSTypeName(mDNSu16 rrtype)
+ {
+ switch (rrtype)
+ {
+ case kDNSType_A: return("Addr");
+ case kDNSType_CNAME:return("CNAME");
+ case kDNSType_NULL: return("NULL");
+ case kDNSType_PTR: return("PTR");
+ case kDNSType_HINFO:return("HINFO");
+ case kDNSType_TXT: return("TXT");
+ case kDNSType_AAAA: return("AAAA");
+ case kDNSType_SRV: return("SRV");
+ case kDNSQType_ANY: return("ANY");
+ default: {
+ static char buffer[16];
+ mDNS_snprintf(buffer, sizeof(buffer), "(%d)", rrtype);
+ return(buffer);
+ }
+ }
+ }
+
+mDNSexport char *GetRRDisplayString_rdb(mDNS *const m, const ResourceRecord *rr, RDataBody *rd)
+ {
+ char *ptr = m->MsgBuffer;
+ mDNSu32 length = mDNS_snprintf(m->MsgBuffer, 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_CNAME:// Same as PTR
+ case kDNSType_PTR: mDNS_snprintf(m->MsgBuffer+length, 79-length, "%##s", &rd->name); 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;
+ }
+ for (ptr = m->MsgBuffer; *ptr; ptr++) if (*ptr < ' ') *ptr='.';
+ return(m->MsgBuffer);
+ }
+
+mDNSlocal mDNSu32 mDNSRandom(mDNSu32 max)
+ {
+ static mDNSu32 seed = 0;
+ mDNSu32 mask = 1;
+
+ if (!seed) seed = (mDNSu32)mDNSPlatformTimeNow();
+ while (mask < max) mask = (mask << 1) | 1;
+ do seed = seed * 21 + 1; while ((seed & mask) > max);
+ return (seed & mask);
+ }
+
+#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)
+
+mDNSexport mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2)
+ {
+ if (ip1->type == ip2->type)
+ {
+ switch (ip1->type)
+ {
+ case mDNSAddrType_IPv4 : return(mDNSBool)(mDNSSameIPv4Address(ip1->ip.v4, ip2->ip.v4));
+ case mDNSAddrType_IPv6 : return(mDNSBool)(mDNSSameIPv6Address(ip1->ip.v6, ip2->ip.v6));
+ }
+ }
+ return(mDNSfalse);
+ }
+
+mDNSlocal mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip)
+ {
+ switch(ip->type)
+ {
+ case mDNSAddrType_IPv4: return(mDNSBool)(ip->ip.v4.NotAnInteger == AllDNSLinkGroup.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] &&
+ ip->ip.v6.l[3] == AllDNSLinkGroupv6.l[3] );
+ default: return(mDNSfalse);
+ }
+ }
+
+mDNSlocal const NetworkInterfaceInfo *GetFirstActiveInterface(const NetworkInterfaceInfo *intf)
+ {
+ while (intf && !intf->InterfaceActive) intf = intf->next;
+ return(intf);
+ }
+
+mDNSlocal mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf)
{
- switch (rrtype)
- {
- case kDNSType_A: return("Address");
- case kDNSType_CNAME:return("CNAME");
- case kDNSType_PTR: return("PTR");
- case kDNSType_TXT: return("TXT");
- case kDNSType_SRV: return("SRV");
- default: {
- static char buffer[16];
- mDNS_sprintf(buffer, "(%d)", rrtype);
- return(buffer);
- }
- }
+ const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next);
+ if (next) return(next->InterfaceID); else return(mDNSNULL);
}
-#endif
-mDNSlocal mDNSu32 mDNSRandom(mDNSu32 max)
+#define InitialQuestionInterval (mDNSPlatformOneSecond/2)
+#define ActiveQuestion(Q) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf)
+#define TimeToSendThisQuestion(Q,time) (ActiveQuestion(Q) && (time) - ((Q)->LastQTime + (Q)->ThisQInterval) >= 0)
+
+mDNSlocal void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q)
{
- static mDNSu32 seed = 1;
- mDNSu32 mask = 1;
- while (mask < max) mask = (mask << 1) | 1;
- do seed = seed * 21 + 1; while ((seed & mask) > max);
- return (seed & mask);
+ if (ActiveQuestion(q))
+ if (m->NextScheduledQuery - (q->LastQTime + q->ThisQInterval) > 0)
+ m->NextScheduledQuery = (q->LastQTime + q->ThisQInterval);
}
// ***************************************************************************
-#if 0
+#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark - Domain Name Utility Functions
#endif
-// Returns length of a domain name INCLUDING the byte for the final null label
-// i.e. for the root label "." it returns one
-// For the FQDN "com." it returns 5 (length, three data bytes, final zero)
-mDNSexport mDNSu32 DomainNameLength(const domainname *const name)
- {
- const mDNSu8 *src = name->c;
- while (*src)
- {
- if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1);
- src += 1 + *src;
- if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1);
- }
- return((mDNSu32)(src - name->c + 1));
- }
+#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))
mDNSexport mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b)
{
{
mDNSu8 ac = *a++;
mDNSu8 bc = *b++;
- if (ac >= 'A' && ac <= 'Z') ac += 'a' - 'A';
- if (bc >= 'A' && bc <= 'Z') bc += 'a' - 'A';
+ if (mDNSIsUpperCase(ac)) ac += 'a' - 'A';
+ if (mDNSIsUpperCase(bc)) bc += 'a' - 'A';
if (ac != bc) return(mDNSfalse);
}
return(mDNStrue);
return(mDNStrue);
}
+// Returns length of a domain name INCLUDING the byte for the final null label
+// i.e. for the root label "." it returns one
+// For the FQDN "com." it returns 5 (length byte, three data bytes, final zero)
+// Legal results are 1 (just root label) to 255 (MAX_DOMAIN_NAME)
+// If the given domainname is invalid, result is 256
+mDNSexport mDNSu16 DomainNameLength(const domainname *const name)
+ {
+ const mDNSu8 *src = name->c;
+ while (*src)
+ {
+ if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1);
+ src += 1 + *src;
+ if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1);
+ }
+ return((mDNSu16)(src - name->c + 1));
+ }
+
// CompressedDomainNameLength returns the length of a domain name INCLUDING the byte
// for the final null label i.e. for the root label "." it returns one.
// E.g. for the FQDN "foo.com." it returns 9
// of the child name, plus TWO bytes for the compression pointer.
// E.g. for the name "foo.com." with parent "com.", it returns 6
// (length, three data bytes, two-byte compression pointer).
-mDNSlocal mDNSu32 CompressedDomainNameLength(const domainname *const name, const domainname *parent)
+mDNSlocal mDNSu16 CompressedDomainNameLength(const domainname *const name, const domainname *parent)
{
const mDNSu8 *src = name->c;
if (parent && parent->c[0] == 0) parent = mDNSNULL;
while (*src)
{
if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1);
- if (parent && SameDomainName((domainname *)src, parent)) return((mDNSu32)(src - name->c + 2));
+ if (parent && SameDomainName((domainname *)src, parent)) return((mDNSu16)(src - name->c + 2));
src += 1 + *src;
if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1);
}
- return((mDNSu32)(src - name->c + 1));
+ return((mDNSu16)(src - name->c + 1));
}
-mDNSexport void AppendDomainLabelToName(domainname *const name, const domainlabel *const label)
- {
- int i;
- mDNSu8 *ptr = name->c + DomainNameLength(name) - 1;
- const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME;
- if (ptr + 1 + label->c[0] + 1 >= lim) return;
- for (i=0; i<=label->c[0]; i++) *ptr++ = label->c[i];
- *ptr++ = 0; // Put the null root label on the end
- }
-
-// AppendStringLabelToName appends a single label to an existing (possibly empty) domainname.
+// AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname.
// The C string contains the label as-is, with no escaping, etc.
// Any dots in the name are literal dots, not label separators
-mDNSexport void AppendStringLabelToName(domainname *const name, const char *cstr)
- {
- mDNSu8 *lengthbyte;
- mDNSu8 *ptr = name->c + DomainNameLength(name) - 1;
- const mDNSu8 *lim = name->c + MAX_DOMAIN_NAME - 1;
- if (lim > ptr + MAX_DOMAIN_LABEL + 1)
- lim = ptr + MAX_DOMAIN_LABEL + 1;
- lengthbyte = ptr++;
- while (*cstr && ptr < lim) *ptr++ = (mDNSu8)*cstr++;
- *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1);
- *ptr++ = 0; // Put the null root label on the end
- }
-
-mDNSexport void AppendDomainNameToName(domainname *const name, const domainname *const append)
- {
- int i;
- mDNSu8 *ptr = name->c + DomainNameLength(name) - 1;
- const mDNSu8 *src = append->c;
- const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME;
- while(src[0])
- {
- if (ptr + 1 + src[0] + 1 >= lim) return;
- for (i=0; i<=src[0]; i++) *ptr++ = src[i];
- *ptr = 0; // Put the null root label on the end
- src += i;
- }
- }
-
-// AppendStringNameToName appends zero or more labels to an existing (possibly empty) domainname.
-// The C string contains the labels separated by dots, but otherwise as-is, with no escaping, etc.
-mDNSexport void AppendStringNameToName(domainname *const name, const char *cstr)
+// If successful, AppendLiteralLabelString returns a pointer to the next unused byte
+// 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)
+// AppendLiteralLabelString returns mDNSNULL.
+mDNSexport mDNSu8 *AppendLiteralLabelString(domainname *const name, const char *cstr)
{
- mDNSu8 *ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
- const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Find limit of how much we can add
- while (*cstr)
- {
- mDNSu8 *const lengthbyte = ptr++;
- const mDNSu8 *const lim2 = ptr + MAX_DOMAIN_LABEL;
- const mDNSu8 *const lim3 = (lim < lim2) ? lim : lim2;
- while (*cstr && *cstr != '.' && ptr < lim3) *ptr++ = (mDNSu8)*cstr++;
- *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1);
- if (*cstr == '.') cstr++;
- }
+ mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
+ const mDNSu8 *const lim1 = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero)
+ const mDNSu8 *const lim2 = ptr + 1 + MAX_DOMAIN_LABEL;
+ const mDNSu8 *const lim = (lim1 < lim2) ? lim1 : lim2;
+ mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go
- *ptr++ = 0; // Put the null root label on the end
- }
-
-//#define IsThreeDigit(X) (IsDigit((X)[1]) && IsDigit((X)[2]) && IsDigit((X)[3]))
-//#define ValidEscape(X) (X)[0] == '\\' && ((X)[1] == '\\' || (X)[1] == '\\' || IsThreeDigit(X))
-
-#define mdnsIsLetter(X) (((X) >= 'A' && (X) <= 'Z') || ((X) >= 'a' && (X) <= 'z'))
-#define mdnsIsDigit(X) (((X) >= '0' && (X) <= '9'))
-#define mdnsValidHostChar(X, notfirst, notlast) (mdnsIsLetter(X) || \
- ((notfirst) && (mdnsIsDigit(X) || ((notlast) && (X) == '-'))) )
-
-mDNSexport void ConvertCStringToDomainLabel(const char *src, domainlabel *label)
- {
- mDNSu8 * ptr = label->c + 1; // Where we're putting it
- const mDNSu8 *const limit = ptr + MAX_DOMAIN_LABEL; // The maximum we can put
- while (*src && ptr < limit) // While we have characters in the label...
- {
- mDNSu8 c = (mDNSu8)*src++; // Read the character
- if (c == '\\') // If escape character, check next character
- {
- if (*src == '\\' || *src == '.') // If a second escape, or a dot,
- c = (mDNSu8)*src++; // just use the second character
- else if (mdnsIsDigit(src[0]) && mdnsIsDigit(src[1]) && mdnsIsDigit(src[2]))
- { // else, if three decimal digits,
- int v0 = src[0] - '0'; // then interpret as three-digit decimal
- int v1 = src[1] - '0';
- int v2 = src[2] - '0';
- int val = v0 * 100 + v1 * 10 + v2;
- if (val <= 255) { c = (mDNSu8)val; src += 3; } // If valid value, use it
- }
- }
- *ptr++ = c; // Write the character
- }
- label->c[0] = (mDNSu8)(ptr - label->c - 1);
+ while (*cstr && ptr < lim) *ptr++ = (mDNSu8)*cstr++; // Copy the data
+ *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte
+ *ptr++ = 0; // Put the null root label on the end
+ if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input
+ else return(ptr); // Success: return new value of ptr
}
-mDNSexport mDNSu8 *ConvertCStringToDomainName(const char *const cstr, domainname *name)
+// AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname.
+// The C string is in conventional DNS syntax:
+// Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
+// If successful, AppendDNSNameString returns a pointer to the next unused byte
+// 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)
{
- const mDNSu8 *src = (const mDNSu8 *)cstr; // C string we're reading
- mDNSu8 *ptr = name->c; // Where we're putting it
- const mDNSu8 *const limit = ptr + MAX_DOMAIN_NAME; // The maximum we can put
-
- while (*src && ptr < limit) // While more characters, and space to put them...
+ 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
- while (*src && *src != '.' && ptr < limit) // While we have characters in the label...
+ while (*cstr && *cstr != '.' && ptr < lim) // While we have characters in the label...
{
- mDNSu8 c = *src++; // Read the character
+ mDNSu8 c = (mDNSu8)*cstr++; // Read the character
if (c == '\\') // If escape character, check next character
{
- if (*src == '\\' || *src == '.') // If a second escape, or a dot,
- c = *src++; // just use the second character
- else if (mdnsIsDigit(src[0]) && mdnsIsDigit(src[1]) && mdnsIsDigit(src[2]))
+ if (*cstr == '\\' || *cstr == '.') // If a second escape, or a dot,
+ c = (mDNSu8)*cstr++; // just use the second character
+ else if (mdnsIsDigit(cstr[0]) && mdnsIsDigit(cstr[1]) && mdnsIsDigit(cstr[2]))
{ // else, if three decimal digits,
- int v0 = src[0] - '0'; // then interpret as three-digit decimal
- int v1 = src[1] - '0';
- int v2 = src[2] - '0';
+ int v0 = cstr[0] - '0'; // then interpret as three-digit decimal
+ int v1 = cstr[1] - '0';
+ int v2 = cstr[2] - '0';
int val = v0 * 100 + v1 * 10 + v2;
- if (val <= 255) { c = (mDNSu8)val; src += 3; } // If valid value, use it
+ if (val <= 255) { c = (mDNSu8)val; cstr += 3; } // If valid value, use it
}
}
*ptr++ = c; // Write the character
}
- if (*src) src++; // Skip over the trailing dot (if present)
- if (ptr - lengthbyte - 1 > MAX_DOMAIN_LABEL) return(mDNSNULL); // If illegal label, abort
- *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1);
+ if (*cstr) cstr++; // Skip over the trailing dot (if present)
+ if (ptr - lengthbyte - 1 > MAX_DOMAIN_LABEL) // If illegal label, abort
+ return(mDNSNULL);
+ *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte
}
- if (ptr < limit) // If we didn't run out of space
+ *ptr++ = 0; // Put the null root label on the end
+ if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input
+ else return(ptr); // Success: return new value of ptr
+ }
+
+// AppendDomainLabel appends a single label to a name.
+// If successful, AppendDomainLabel returns a pointer to the next unused byte
+// 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)
+// AppendDomainLabel returns mDNSNULL.
+mDNSexport mDNSu8 *AppendDomainLabel(domainname *const name, const domainlabel *const label)
+ {
+ int i;
+ mDNSu8 *ptr = name->c + DomainNameLength(name) - 1;
+
+ // Check label is legal
+ if (label->c[0] > MAX_DOMAIN_LABEL) return(mDNSNULL);
+
+ // Check that ptr + length byte + data bytes + final zero does not exceed our limit
+ if (ptr + 1 + label->c[0] + 1 > name->c + MAX_DOMAIN_NAME) return(mDNSNULL);
+
+ for (i=0; i<=label->c[0]; i++) *ptr++ = label->c[i]; // Copy the label data
+ *ptr++ = 0; // Put the null root label on the end
+ return(ptr);
+ }
+
+mDNSexport mDNSu8 *AppendDomainName(domainname *const name, const domainname *const append)
+ {
+ 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)
+ const mDNSu8 * src = append->c;
+ while(src[0])
{
- *ptr++ = 0; // Put the final root label
- return(ptr); // and return
+ int i;
+ if (ptr + 1 + src[0] > lim) return(mDNSNULL);
+ for (i=0; i<=src[0]; i++) *ptr++ = src[i];
+ *ptr = 0; // Put the null root label on the end
+ src += i;
}
+ return(ptr);
+ }
- return(mDNSNULL);
+// MakeDomainLabelFromLiteralString makes a single domain label from a single literal C string (with no escaping).
+// If successful, MakeDomainLabelFromLiteralString returns mDNStrue.
+// If unable to convert the whole string to a legal domain label (i.e. because length is more than 63 bytes) then
+// MakeDomainLabelFromLiteralString makes a legal domain label from the first 63 bytes of the string and returns mDNSfalse.
+// In some cases silently truncated oversized names to 63 bytes is acceptable, so the return result may be ignored.
+// In other cases silent truncation may not be acceptable, so in those cases the calling function needs to check the return result.
+mDNSexport mDNSBool MakeDomainLabelFromLiteralString(domainlabel *const label, const char *cstr)
+ {
+ mDNSu8 * ptr = label->c + 1; // Where we're putting it
+ const mDNSu8 *const limit = label->c + 1 + MAX_DOMAIN_LABEL; // The maximum we can put
+ while (*cstr && ptr < limit) *ptr++ = (mDNSu8)*cstr++; // Copy the label
+ label->c[0] = (mDNSu8)(ptr - label->c - 1); // Set the length byte
+ return(*cstr == 0); // Return mDNStrue if we successfully consumed all input
}
-//#define convertCstringtodomainname(C,D) convertCstringtodomainname_withescape((C), (D), -1)
-//#define convertescapedCstringtodomainname(C,D) convertCstringtodomainname_withescape((C), (D), '\\')
+// MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string.
+// The C string is in conventional DNS syntax:
+// Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
+// If successful, MakeDomainNameFromDNSNameString returns a pointer to the next unused byte
+// 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)
+// MakeDomainNameFromDNSNameString returns mDNSNULL.
+mDNSexport mDNSu8 *MakeDomainNameFromDNSNameString(domainname *const name, const char *cstr)
+ {
+ name->c[0] = 0; // Make an empty domain name
+ return(AppendDNSNameString(name, cstr)); // And then add this string to it
+ }
mDNSexport char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc)
{
const mDNSu8 * src = label->c; // Domain label we're reading
const mDNSu8 len = *src++; // Read length of this (non-null) label
- const mDNSu8 *const end = src + len; // Work out where the label ends
- if (len > MAX_DOMAIN_LABEL) return(mDNSNULL); // If illegal label, abort
+ const mDNSu8 *const end = src + len; // Work out where the label ends
+ if (len > MAX_DOMAIN_LABEL) return(mDNSNULL); // If illegal label, abort
while (src < end) // While we have characters in the label
{
mDNSu8 c = *src++;
if (esc)
{
- if (c == '.') // If character is a dot,
+ if (c == '.' || c == esc) // If character is a dot or the escape character
*ptr++ = esc; // Output escape character
else if (c <= ' ') // If non-printing ascii,
{ // Output decimal escape sequence
*ptr++ = esc;
- *ptr++ = (char) ('0' + (c / 100) );
- *ptr++ = (char) ('0' + (c / 10) % 10);
+ *ptr++ = (char) ('0' + (c / 100) );
+ *ptr++ = (char) ('0' + (c / 10) % 10);
c = (mDNSu8)('0' + (c ) % 10);
}
}
// RFC 1034 rules:
// Host names must start with a letter, end with a letter or digit,
// and have as interior characters only letters, digits, and hyphen.
+// This was subsequently modified in RFC 1123 to allow the first character to be either a letter or a digit
+#define mdnsValidHostChar(X, notfirst, notlast) (mdnsIsLetter(X) || mdnsIsDigit(X) || ((notfirst) && (notlast) && (X) == '-') )
mDNSexport void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], domainlabel *const hostlabel)
{
}
mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn,
- const domainlabel *const name, const domainname *const type, const domainname *const domain)
+ const domainlabel *name, const domainname *type, const domainname *const domain)
{
int i, len;
mDNSu8 *dst = fqdn->c;
- mDNSu8 *max = fqdn->c + MAX_DOMAIN_NAME;
const mDNSu8 *src;
+ const char *errormsg;
+
+ // 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)
+ {
+ 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)
+ {
+ name = (domainlabel *)type;
+ type = (domainname *)s2;
+ }
+ }
- if (name)
+ if (name && name->c[0])
{
src = name->c; // Put the service name into the domain name
len = *src;
- if (len >= 0x40) { debugf("ConstructServiceName: service name too long"); return(0); }
+ if (len >= 0x40) { errormsg="Service instance name too long"; goto fail; }
for (i=0; i<=len; i++) *dst++ = *src++;
}
+ else
+ name = (domainlabel*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below
src = type->c; // Put the service type into the domain name
len = *src;
- if (len == 0 || len >= 0x40) { debugf("ConstructServiceName: Invalid service name"); return(0); }
- if (dst + 1 + len + 1 >= max) { debugf("ConstructServiceName: service type too long"); return(0); }
+ if (len < 2 || len >= 0x40) { errormsg="Invalid service application protocol name"; goto fail; }
+ if (src[1] != '_') { errormsg="Service 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] != '_')
+ { errormsg="Service application protocol name must contain only letters, digits, and hyphens"; goto fail; }
for (i=0; i<=len; i++) *dst++ = *src++;
len = *src;
- if (len == 0 || len >= 0x40) { debugf("ConstructServiceName: Invalid service name"); return(0); }
- if (dst + 1 + len + 1 >= max) { debugf("ConstructServiceName: service type too long"); return(0); }
+ //if (len == 0 || len >= 0x40) { errormsg="Invalid service transport protocol name"; goto fail; }
+ 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; }
for (i=0; i<=len; i++) *dst++ = *src++;
- if (*src) { debugf("ConstructServiceName: Service type must have only two labels"); return(0); }
-
- src = domain->c; // Put the service domain into the domain name
- while (*src)
- {
- len = *src;
- if (dst + 1 + len + 1 >= max)
- { debugf("ConstructServiceName: service domain too long"); return(0); }
- for (i=0; i<=len; i++) *dst++ = *src++;
- }
+ if (*src) { errormsg="Service type must have only two labels"; goto fail; }
- *dst++ = 0; // Put the null root label on the end
+ *dst = 0;
+ dst = AppendDomainName(fqdn, domain);
+ if (!dst) { errormsg="Service domain too long"; goto fail; }
return(dst);
+
+fail:
+ LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg, name->c, type->c, domain->c);
+ return(mDNSNULL);
}
mDNSexport mDNSBool DeconstructServiceName(const domainname *const fqdn,
return(mDNStrue);
}
-mDNSlocal void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText)
+// Returns true if a rich text label ends in " (nnn)", or if an RFC 1034
+// name ends in "-nnn", where n is some decimal number.
+mDNSlocal mDNSBool LabelContainsSuffix(const domainlabel *const name, const mDNSBool RichText)
+ {
+ mDNSu16 l = name->c[0];
+
+ if (RichText)
+ {
+ if (l < 4) return mDNSfalse; // Need at least " (2)"
+ if (name->c[l--] != ')') return mDNSfalse; // Last char must be ')'
+ if (!mdnsIsDigit(name->c[l])) return mDNSfalse; // Preceeded by a digit
+ l--;
+ while (l > 2 && mdnsIsDigit(name->c[l])) l--; // Strip off digits
+ return (name->c[l] == '(' && name->c[l - 1] == ' ');
+ }
+ else
+ {
+ if (l < 2) return mDNSfalse; // Need at least "-2"
+ if (!mdnsIsDigit(name->c[l])) return mDNSfalse; // Last char must be a digit
+ l--;
+ while (l > 2 && mdnsIsDigit(name->c[l])) l--; // Strip off digits
+ return (name->c[l] == '-');
+ }
+ }
+
+// removes an auto-generated suffix (appended on a name collision) from a label. caller is
+// responsible for ensuring that the label does indeed contain a suffix. returns the number
+// from the suffix that was removed.
+mDNSlocal mDNSu32 RemoveLabelSuffix(domainlabel *name, mDNSBool RichText)
{
- long val = 0, multiplier = 1, divisor = 1, digits = 1;
+ mDNSu32 val = 0, multiplier = 1;
+
+ // Chop closing parentheses from RichText suffix
+ if (RichText && name->c[0] >= 1 && name->c[name->c[0]] == ')') name->c[0]--;
// Get any existing numerical suffix off the name
while (mdnsIsDigit(name->c[name->c[0]]))
{ val += (name->c[name->c[0]] - '0') * multiplier; multiplier *= 10; name->c[0]--; }
-
- // If existing suffix, increment it, else start by renaming "Foo" as "Foo2"
- if (multiplier > 1 && val < 999999) val++; else val = 2;
- // Can only add spaces to rich text names, not RFC 1034 names
- if (RichText && name->c[name->c[0]] != ' ' && name->c[0] < MAX_DOMAIN_LABEL)
- name->c[++name->c[0]] = ' ';
+ // Chop opening parentheses or dash from suffix
+ if (RichText)
+ {
+ if (name->c[0] >= 2 && name->c[name->c[0]] == '(' && name->c[name->c[0]-1] == ' ') name->c[0] -= 2;
+ }
+ else
+ {
+ if (name->c[0] >= 1 && name->c[name->c[0]] == '-') name->c[0] -= 1;
+ }
+
+ return(val);
+ }
+
+// appends a numerical suffix to a label, with the number following a whitespace and enclosed
+// in parentheses (rich text) or following two consecutive hyphens (RFC 1034 domain label).
+mDNSlocal void AppendLabelSuffix(domainlabel *name, mDNSu32 val, mDNSBool RichText)
+ {
+ mDNSu32 divisor = 1, chars = 2; // Shortest possible RFC1034 name suffix is 3 characters ("-2")
+ if (RichText) chars = 4; // Shortest possible RichText suffix is 4 characters (" (2)")
- while (val >= divisor * 10)
- { divisor *= 10; digits++; }
+ // Truncate trailing spaces from RichText names
+ if (RichText) while (name->c[name->c[0]] == ' ') name->c[0]--;
+
+ while (val >= divisor * 10) { divisor *= 10; chars++; }
+
+ if (name->c[0] > (mDNSu8)(MAX_DOMAIN_LABEL - chars))
+ {
+ name->c[0] = (mDNSu8)(MAX_DOMAIN_LABEL - chars);
+ // If the following character is a UTF-8 continuation character,
+ // we just chopped a multi-byte UTF-8 character in the middle, so strip back to a safe truncation point
+ while (name->c[0] > 0 && (name->c[name->c[0]+1] & 0xC0) == 0x80) name->c[0]--;
+ }
- if (name->c[0] > (mDNSu8)(MAX_DOMAIN_LABEL - digits))
- name->c[0] = (mDNSu8)(MAX_DOMAIN_LABEL - digits);
+ if (RichText) { name->c[++name->c[0]] = ' '; name->c[++name->c[0]] = '('; }
+ else { name->c[++name->c[0]] = '-'; }
while (divisor)
{
val %= divisor;
divisor /= 10;
}
+
+ if (RichText) name->c[++name->c[0]] = ')';
+ }
+
+mDNSexport void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText)
+ {
+ mDNSu32 val = 0;
+
+ if (LabelContainsSuffix(name, RichText))
+ val = RemoveLabelSuffix(name, RichText);
+
+ // If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate.
+ // If existing suffix in the range 2-9, increment it.
+ // If we've had ten conflicts already, there are probably too many hosts trying to use the same name,
+ // so add a random increment to improve the chances of finding an available name next time.
+ if (val == 0) val = 2;
+ else if (val < 10) val++;
+ else val += 1 + mDNSRandom(99);
+
+ AppendLabelSuffix(name, val, RichText);
}
// ***************************************************************************
-#if 0
+#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark - Resource Record Utility Functions
#endif
-#define ResourceRecordIsValidAnswer(RR) ( ((RR)-> RecordType & kDNSRecordTypeActiveMask) && \
- ((RR)->Additional1 == mDNSNULL || ((RR)->Additional1->RecordType & kDNSRecordTypeActiveMask)) && \
- ((RR)->Additional2 == mDNSNULL || ((RR)->Additional2->RecordType & kDNSRecordTypeActiveMask)) && \
- ((RR)->DependentOn == mDNSNULL || ((RR)->DependentOn->RecordType & kDNSRecordTypeActiveMask)) )
+#define RRIsAddressType(RR) ((RR)->resrec.rrtype == kDNSType_A || (RR)->resrec.rrtype == kDNSType_AAAA)
-#define ResourceRecordIsValidInterfaceAnswer(RR, I) \
+#define ResourceRecordIsValidAnswer(RR) ( ((RR)-> resrec.RecordType & kDNSRecordTypeActiveMask) && \
+ ((RR)->Additional1 == mDNSNULL || ((RR)->Additional1->resrec.RecordType & kDNSRecordTypeActiveMask)) && \
+ ((RR)->Additional2 == mDNSNULL || ((RR)->Additional2->resrec.RecordType & kDNSRecordTypeActiveMask)) && \
+ ((RR)->DependentOn == mDNSNULL || ((RR)->DependentOn->resrec.RecordType & kDNSRecordTypeActiveMask)) )
+
+#define ResourceRecordIsValidInterfaceAnswer(RR, INTID) \
(ResourceRecordIsValidAnswer(RR) && \
- ((RR)->InterfaceAddr.NotAnInteger == 0 || (RR)->InterfaceAddr.NotAnInteger == (I).NotAnInteger))
+ ((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)
-#define DefaultAnnounceCountForTypeShared ((mDNSu8)10)
-#define DefaultAnnounceCountForTypeUnique ((mDNSu8)2)
-
-#define DefaultAnnounceCountForRecordType(X) ((X) == kDNSRecordTypeShared ? DefaultAnnounceCountForTypeShared : \
- (X) == kDNSRecordTypeUnique ? DefaultAnnounceCountForTypeUnique : \
- (X) == kDNSRecordTypeVerified ? DefaultAnnounceCountForTypeUnique : \
- (X) == kDNSRecordTypeKnownUnique ? DefaultAnnounceCountForTypeUnique : (mDNSu8)0)
-
-#define DefaultSendIntervalForRecordType(X) ((X) == kDNSRecordTypeShared ? mDNSPlatformOneSecond : \
- (X) == kDNSRecordTypeUnique ? mDNSPlatformOneSecond/4 : \
- (X) == kDNSRecordTypeVerified ? mDNSPlatformOneSecond/4 : 0)
-
-#define TimeToAnnounceThisRecord(RR,time) ((RR)->AnnounceCount && time - (RR)->NextSendTime >= 0)
-#define TimeToSendThisRecord(RR,time) \
- ((TimeToAnnounceThisRecord(RR,time) || (RR)->SendPriority) && ResourceRecordIsValidAnswer(RR))
-
-mDNSlocal mDNSBool SameRData(const mDNSu16 rrtype, const RData *const r1, const RData *const r2)
+// 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
+// observed on-the-wire inter-packet interval between announcements is actually one second.
+// The half-second value here may be thought of as a conceptual (non-existent) half-second delay *before* the first packet is sent.
+#define DefaultProbeIntervalForTypeUnique (mDNSPlatformOneSecond/4)
+#define DefaultAnnounceIntervalForTypeShared (mDNSPlatformOneSecond/2)
+#define DefaultAnnounceIntervalForTypeUnique (mDNSPlatformOneSecond/2)
+
+#define DefaultAPIntervalForRecordType(X) ((X) & (kDNSRecordTypeAdvisory | kDNSRecordTypeShared ) ? DefaultAnnounceIntervalForTypeShared : \
+ (X) & (kDNSRecordTypeUnique ) ? DefaultProbeIntervalForTypeUnique : \
+ (X) & (kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique) ? DefaultAnnounceIntervalForTypeUnique : 0)
+
+#define TimeToAnnounceThisRecord(RR,time) ((RR)->AnnounceCount && (time) - ((RR)->LastAPTime + (RR)->ThisAPInterval) >= 0)
+#define TimeToSendThisRecord(RR,time) ((TimeToAnnounceThisRecord(RR,time) || (RR)->ImmedAnswer) && ResourceRecordIsValidAnswer(RR))
+#define TicksTTL(RR) ((mDNSs32)(RR)->resrec.rroriginalttl * mDNSPlatformOneSecond)
+#define RRExpireTime(RR) ((RR)->TimeRcvd + TicksTTL(RR))
+
+#define MaxUnansweredQueries 4
+
+mDNSlocal mDNSBool SameRData(const ResourceRecord *const r1, const ResourceRecord *const r2)
{
- if (r1->RDLength != r2->RDLength) return(mDNSfalse);
- switch(rrtype)
+ if (r1->rrtype != r2->rrtype) return(mDNSfalse);
+ if (r1->rdlength != r2->rdlength) return(mDNSfalse);
+ if (r1->rdatahash != r2->rdatahash) return(mDNSfalse);
+ if (r1->rdnamehash != r2->rdnamehash) return(mDNSfalse);
+ switch(r1->rrtype)
{
case kDNSType_CNAME:// Same as PTR
- case kDNSType_PTR: return(SameDomainName(&r1->u.name, &r2->u.name));
+ case kDNSType_PTR: return(SameDomainName(&r1->rdata->u.name, &r2->rdata->u.name));
- case kDNSType_SRV: return( r1->u.srv.priority == r2->u.srv.priority &&
- r1->u.srv.weight == r2->u.srv.weight &&
- r1->u.srv.port.NotAnInteger == r2->u.srv.port.NotAnInteger &&
- SameDomainName(&r1->u.srv.target, &r2->u.srv.target));
+ case kDNSType_SRV: return(mDNSBool)( r1->rdata->u.srv.priority == r2->rdata->u.srv.priority &&
+ r1->rdata->u.srv.weight == r2->rdata->u.srv.weight &&
+ r1->rdata->u.srv.port.NotAnInteger == r2->rdata->u.srv.port.NotAnInteger &&
+ SameDomainName(&r1->rdata->u.srv.target, &r2->rdata->u.srv.target) );
- default: return(mDNSPlatformMemSame(r1->u.data, r2->u.data, r1->RDLength));
+ default: return(mDNSPlatformMemSame(r1->rdata->u.data, r2->rdata->u.data, r1->rdlength));
}
}
mDNSlocal mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
{
- if (rr->InterfaceAddr.NotAnInteger &&
- q ->InterfaceAddr.NotAnInteger &&
- rr->InterfaceAddr.NotAnInteger != q->InterfaceAddr.NotAnInteger) return(mDNSfalse);
+ if (rr->InterfaceID &&
+ q ->InterfaceID &&
+ rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
// RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
- if (rr->rrtype != kDNSType_CNAME && rr->rrtype != q->rrtype && q->rrtype != kDNSQType_ANY ) return(mDNSfalse);
- if ( rr->rrclass != q->rrclass && q->rrclass != kDNSQClass_ANY) return(mDNSfalse);
- return(SameDomainName(&rr->name, &q->name));
+ if (rr->rrtype != kDNSType_CNAME && rr->rrtype != q->qtype && q->qtype != kDNSQType_ANY ) return(mDNSfalse);
+ if ( rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
+ return(rr->namehash == q->qnamehash && SameDomainName(&rr->name, &q->qname));
}
-// SameResourceRecordSignature returns true if two resources records have the same interface, name, type, and class.
-// -- i.e. if they would both be given in response to the same question.
-// (TTL and rdata may differ)
-mDNSlocal mDNSBool SameResourceRecordSignature(const ResourceRecord *const r1, const ResourceRecord *const r2)
+mDNSlocal mDNSu32 DomainNameHashValue(const domainname *const name)
+ {
+ mDNSu32 sum = 0;
+ const mDNSu8 *c;
+
+ for (c = name->c; c[0] != 0 && c[1] != 0; c += 2)
+ {
+ sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8) |
+ (mDNSIsUpperCase(c[1]) ? c[1] + 'a' - 'A' : c[1]);
+ sum = (sum<<3) | (sum>>29);
+ }
+ if (c[0]) sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8);
+ return(sum);
+ }
+
+#define HashSlot(X) (DomainNameHashValue(X) % CACHE_HASH_SLOTS)
+
+mDNSlocal mDNSu32 RDataHashValue(mDNSu16 const rdlength, const RDataBody *const rdb)
{
- if (!r1) { debugf("SameResourceRecordSignature ERROR: r1 is NULL"); return(mDNSfalse); }
- if (!r2) { debugf("SameResourceRecordSignature ERROR: r2 is NULL"); return(mDNSfalse); }
- if (r1->InterfaceAddr.NotAnInteger &&
- r2->InterfaceAddr.NotAnInteger &&
- r1->InterfaceAddr.NotAnInteger != r2->InterfaceAddr.NotAnInteger) return(mDNSfalse);
- return (r1->rrtype == r2->rrtype && r1->rrclass == r2->rrclass && SameDomainName(&r1->name, &r2->name));
+ mDNSu32 sum = 0;
+ int i;
+ for (i=0; i+1 < rdlength; i+=2)
+ {
+ sum += (((mDNSu32)(rdb->data[i])) << 8) | rdb->data[i+1];
+ sum = (sum<<3) | (sum>>29);
+ }
+ if (i < rdlength)
+ {
+ sum += ((mDNSu32)(rdb->data[i])) << 8;
+ }
+ return(sum);
}
-// SameResourceRecordSignatureAnyInterface returns true if two resources records have the same name, type, and class.
-// (InterfaceAddr, TTL and rdata may differ)
-mDNSlocal mDNSBool SameResourceRecordSignatureAnyInterface(const ResourceRecord *const r1, const ResourceRecord *const r2)
+// SameResourceRecordSignature returns true if two resources records have the same name, type, and class, and may be sent
+// (or were received) on the same interface (i.e. if *both* records specify an interface, then it has to match).
+// TTL and rdata may differ.
+// This is used for cache flush management:
+// When sending a unique record, all other records matching "SameResourceRecordSignature" must also be sent
+// When receiving a unique record, all old cache records matching "SameResourceRecordSignature" are flushed
+mDNSlocal mDNSBool SameResourceRecordSignature(const ResourceRecord *const r1, const ResourceRecord *const r2)
{
- if (!r1) { debugf("SameResourceRecordSignatureAnyInterface ERROR: r1 is NULL"); return(mDNSfalse); }
- if (!r2) { debugf("SameResourceRecordSignatureAnyInterface ERROR: r2 is NULL"); return(mDNSfalse); }
- return (r1->rrtype == r2->rrtype && r1->rrclass == r2->rrclass && SameDomainName(&r1->name, &r2->name));
+ if (!r1) { LogMsg("SameResourceRecordSignature ERROR: r1 is NULL"); return(mDNSfalse); }
+ if (!r2) { LogMsg("SameResourceRecordSignature ERROR: r2 is NULL"); return(mDNSfalse); }
+ if (r1->InterfaceID &&
+ r2->InterfaceID &&
+ r1->InterfaceID != r2->InterfaceID) return(mDNSfalse);
+ return(mDNSBool)(r1->rrtype == r2->rrtype && r1->rrclass == r2->rrclass && r1->namehash == r2->namehash && SameDomainName(&r1->name, &r2->name));
}
-// IdenticalResourceRecord returns true if two resources records have
-// the same interface, name, type, class, and identical rdata (TTL may differ)
-mDNSlocal mDNSBool IdenticalResourceRecord(const ResourceRecord *const r1, const ResourceRecord *const r2)
+// PacketRRMatchesSignature behaves as SameResourceRecordSignature, except that types may differ if the
+// authoratative record is in the probing state. Probes are sent with the wildcard type, so a response of
+// any type should match, even if it is not the type the client plans to use.
+mDNSlocal mDNSBool PacketRRMatchesSignature(const CacheRecord *const pktrr, const AuthRecord *const authrr)
{
- if (!SameResourceRecordSignature(r1, r2)) return(mDNSfalse);
- return(SameRData(r1->rrtype, r1->rdata, r2->rdata));
+ if (!pktrr) { LogMsg("PacketRRMatchesSignature ERROR: pktrr is NULL"); return(mDNSfalse); }
+ if (!authrr) { LogMsg("PacketRRMatchesSignature ERROR: authrr is NULL"); return(mDNSfalse); }
+ 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);
+ return(mDNSBool)(pktrr->resrec.rrclass == authrr->resrec.rrclass && pktrr->resrec.namehash == authrr->resrec.namehash && SameDomainName(&pktrr->resrec.name, &authrr->resrec.name));
}
-// IdenticalResourceRecordAnyInterface returns true if two resources records have
-// the same name, type, class, and identical rdata (InterfaceAddr and TTL may differ)
-mDNSlocal mDNSBool IdenticalResourceRecordAnyInterface(const ResourceRecord *const r1, const ResourceRecord *const r2)
+// IdenticalResourceRecord returns true if two resources records have
+// the same name, type, class, and identical rdata (InterfaceID and TTL may differ)
+mDNSlocal mDNSBool IdenticalResourceRecord(const ResourceRecord *const r1, const ResourceRecord *const r2)
{
- if (!SameResourceRecordSignatureAnyInterface(r1, r2)) return(mDNSfalse);
- return(SameRData(r1->rrtype, r1->rdata, r2->rdata));
+ if (!r1) { LogMsg("IdenticalResourceRecord ERROR: r1 is NULL"); return(mDNSfalse); }
+ if (!r2) { LogMsg("IdenticalResourceRecord ERROR: r2 is NULL"); return(mDNSfalse); }
+ if (r1->rrtype != r2->rrtype || r1->rrclass != r2->rrclass || r1->namehash != r2->namehash || !SameDomainName(&r1->name, &r2->name)) return(mDNSfalse);
+ return(SameRData(r1, r2));
}
-// ResourceRecord *ds is the ResourceRecord from the duplicate suppression section of the query
-// This is the information that the requester believes to be correct
-// ResourceRecord *rr is the answer we are proposing to give, if not suppressed
-// This is the information that we believe to be correct
-mDNSlocal mDNSBool SuppressDuplicate(const ResourceRecord *const ds, const ResourceRecord *const rr)
+// CacheRecord *ks is the CacheRecord from the known answer list in the query.
+// This is the information that the requester believes to be correct.
+// AuthRecord *rr is the answer we are proposing to give, if not suppressed.
+// This is the information that we believe to be correct.
+// We've already determined that we plan to give this answer on this interface
+// (either the record is non-specific, or it is specific to this interface)
+// so now we just need to check the name, type, class, rdata and TTL.
+mDNSlocal mDNSBool ShouldSuppressKnownAnswer(const CacheRecord *const ka, const AuthRecord *const rr)
{
- // If RR signature is different, or data is different, then don't suppress
- if (!IdenticalResourceRecord(ds,rr)) return(mDNSfalse);
+ // If RR signature is different, or data is different, then don't suppress our answer
+ if (!IdenticalResourceRecord(&ka->resrec,&rr->resrec)) return(mDNSfalse);
// If the requester's indicated TTL is less than half the real TTL,
// we need to give our answer before the requester's copy expires.
// (If two responders on the network are offering the same information,
// that's okay, and if they are offering the information with different TTLs,
// the one offering the lower TTL should defer to the one offering the higher TTL.)
- return(ds->rroriginalttl >= rr->rroriginalttl / 2);
+ return(mDNSBool)(ka->resrec.rroriginalttl >= rr->resrec.rroriginalttl / 2);
}
-mDNSlocal mDNSu32 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate)
+mDNSlocal mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate)
{
+ RDataBody *rd = &rr->rdata->u;
const domainname *const name = estimate ? &rr->name : mDNSNULL;
switch (rr->rrtype)
{
- case kDNSType_A: return(sizeof(rr->rdata->u.ip)); break;
+ case kDNSType_A: return(sizeof(rd->ip)); break;
case kDNSType_CNAME:// Same as PTR
- case kDNSType_PTR: return(CompressedDomainNameLength(&rr->rdata->u.name, name));
- case kDNSType_TXT: return(rr->rdata->RDLength); // TXT is not self-describing, so have to just trust rdlength
- case kDNSType_AAAA: return(16); break;
- case kDNSType_SRV: return(6 + CompressedDomainNameLength(&rr->rdata->u.srv.target, name));
+ case kDNSType_PTR: return(CompressedDomainNameLength(&rd->name, name));
+ case kDNSType_HINFO:return(mDNSu16)(2 + (int)rd->data[0] + (int)rd->data[1 + (int)rd->data[0]]);
+ case kDNSType_NULL: // Same as TXT -- not self-describing, so have to just trust rdlength
+ case kDNSType_TXT: return(rr->rdlength); // TXT is not self-describing, so have to just trust rdlength
+ case kDNSType_AAAA: return(sizeof(rd->ipv6)); break;
+ case kDNSType_SRV: return(mDNSu16)(6 + CompressedDomainNameLength(&rd->srv.target, name));
default: debugf("Warning! Don't know how to get length of resource type %d", rr->rrtype);
- return(rr->rdata->RDLength);
+ return(rr->rdlength);
}
}
-// rr is a ResourceRecord in our cache
-// (kDNSRecordTypePacketAnswer/kDNSRecordTypePacketAdditional/kDNSRecordTypePacketUniqueAns/kDNSRecordTypePacketUniqueAdd)
-mDNSlocal DNSQuestion *CacheRRActive(const mDNS *const m, ResourceRecord *rr)
+mDNSlocal void SetNextAnnounceProbeTime(mDNS *const m, const AuthRecord *const rr)
{
- DNSQuestion *q;
- for (q = m->ActiveQuestions; q; q=q->next) // Scan our list of questions
- if (q->ThisQInterval > 0 && !q->DuplicateOf && ResourceRecordAnswersQuestion(rr, q))
- return(q);
- return(mDNSNULL);
+ if (rr->resrec.RecordType == kDNSRecordTypeUnique)
+ {
+ if (m->NextScheduledProbe - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
+ m->NextScheduledProbe = (rr->LastAPTime + rr->ThisAPInterval);
+ }
+ else if (rr->AnnounceCount && ResourceRecordIsValidAnswer(rr))
+ {
+ if (m->NextScheduledResponse - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
+ m->NextScheduledResponse = (rr->LastAPTime + rr->ThisAPInterval);
+ }
}
-mDNSlocal void SetTargetToHostName(const mDNS *const m, ResourceRecord *const rr)
+#define GetRRDomainNameTarget(RR) ( \
+ ((RR)->rrtype == kDNSType_CNAME || (RR)->rrtype == kDNSType_PTR) ? &(RR)->rdata->u.name : \
+ ((RR)->rrtype == kDNSType_SRV ) ? &(RR)->rdata->u.srv.target : mDNSNULL )
+
+mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr)
{
- switch (rr->rrtype)
+ // To allow us to aggregate probes when a group of services are registered together,
+ // the first probe is delayed 1/4 second. This means the common-case behaviour is:
+ // 1/4 second wait; probe
+ // 1/4 second wait; probe
+ // 1/4 second wait; probe
+ // 1/4 second wait; announce (i.e. service is normally announced exactly one second after being registered)
+
+ // 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)
{
- case kDNSType_CNAME:// Same as PTR
- case kDNSType_PTR: rr->rdata->u.name = m->hostname1; break;
- case kDNSType_SRV: rr->rdata->u.srv.target = m->hostname1; break;
- default: debugf("SetTargetToHostName: Dont' know how to set the target of rrtype %d", rr->rrtype); break;
+ m->SuppressProbes = (m->timenow + DefaultProbeIntervalForTypeUnique) | 1;
+ // 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;
+ // If we already have a query scheduled to go out sooner, then use that time to get better aggregation
+ if (m->SuppressProbes - m->NextScheduledQuery >= 0)
+ m->SuppressProbes = m->NextScheduledQuery;
}
- rr->rdata->RDLength = GetRDLength(rr, mDNSfalse);
- rr->rdestimate = GetRDLength(rr, mDNStrue);
- // If we're in the middle of probing this record, we need to start again,
- // because changing its rdata may change the outcome of the tie-breaker.
- rr->ProbeCount = DefaultProbeCountForRecordType(rr->RecordType);
- rr->AnnounceCount = DefaultAnnounceCountForRecordType(rr->RecordType);
- rr->NextSendTime = mDNSPlatformTimeNow();
- rr->NextSendInterval = DefaultSendIntervalForRecordType(rr->RecordType);
- if (rr->RecordType == kDNSRecordTypeUnique && m->SuppressProbes) rr->NextSendTime = m->SuppressProbes;
+ // We announce to flush stale data from other caches. It is a reasonable assumption that any
+ // old stale copies will probably have the same TTL we're using, so announcing longer than
+ // this serves no purpose -- any stale copies of that record will have expired by then anyway.
+ rr->AnnounceUntil = m->timenow + TicksTTL(rr);
+ rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval;
+ // Set LastMCTime to now, to inhibit multicast responses
+ // (no need to send additional multicast responses when we're announcing anyway)
+ rr->LastMCTime = m->timenow;
+ rr->LastMCInterface = mDNSInterfaceMark;
+
+ // If this is a record type that's not going to probe, then delay its first announcement so that
+ // it will go out synchronized with the first announcement for the other records that *are* probing.
+ // This is a minor performance tweak that helps keep groups of related records synchronized together.
+ // The addition of "rr->ThisAPInterval / 2" is to make sure that, in the event that any of the probes are
+ // delayed by a few milliseconds, this announcement does not inadvertently go out *before* the probing is complete.
+ // When the probing is complete and those records begin to announce, these records will also be picked up and accelerated,
+ // because they will meet the criterion of being at least half-way to their scheduled announcement time.
+ if (rr->resrec.RecordType != kDNSRecordTypeUnique)
+ rr->LastAPTime += DefaultProbeIntervalForTypeUnique * DefaultProbeCountForTypeUnique + rr->ThisAPInterval / 2;
+
+ SetNextAnnounceProbeTime(m, rr);
}
-mDNSlocal void UpdateHostNameTargets(const mDNS *const m)
+mDNSlocal void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength)
{
- ResourceRecord *rr;
- for (rr = m->ResourceRecords; rr; rr=rr->next)
- if (rr->HostTarget)
- SetTargetToHostName(m, rr);
+ domainname *target;
+ if (NewRData)
+ {
+ rr->rdata = NewRData;
+ rr->rdlength = rdlength;
+ }
+ // Must not try to get target pointer until after updating rr->rdata
+ target = GetRRDomainNameTarget(rr);
+ rr->rdlength = GetRDLength(rr, mDNSfalse);
+ rr->rdestimate = GetRDLength(rr, mDNStrue);
+ rr->rdatahash = RDataHashValue(rr->rdlength, &rr->rdata->u);
+ rr->rdnamehash = target ? DomainNameHashValue(target) : 0;
+ }
+
+mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr)
+ {
+ domainname *target = GetRRDomainNameTarget(&rr->resrec);
+
+ if (!target) debugf("SetTargetToHostName: Don't know how to set the target of rrtype %d", rr->resrec.rrtype);
+
+ if (target && SameDomainName(target, &m->hostname))
+ debugf("SetTargetToHostName: Target of %##s is already %##s", rr->resrec.name.c, target->c);
+
+ if (target && !SameDomainName(target, &m->hostname))
+ {
+ AssignDomainName(*target, m->hostname);
+ SetNewRData(&rr->resrec, mDNSNULL, 0);
+
+ // If we're in the middle of probing this record, we need to start again,
+ // because changing its rdata may change the outcome of the tie-breaker.
+ // (If the record type is kDNSRecordTypeUnique (unconfirmed unique) then DefaultProbeCountForRecordType is non-zero.)
+ rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType);
+
+ // 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)
+ 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->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
+ InitializeLastAPTime(m,rr);
+ }
+ }
+
+mDNSlocal void CompleteProbing(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
+ // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
+ rr->Acknowledged = mDNStrue;
+ m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
+ rr->RecordCallback(m, rr, mStatus_NoError);
+ m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
+ }
+ }
+
+#define ValidateDomainName(N) (DomainNameLength(N) <= MAX_DOMAIN_NAME)
+
+mDNSlocal mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd)
+ {
+ mDNSu16 len;
+ switch(rrtype)
+ {
+ case kDNSType_A: return(rdlength == sizeof(mDNSv4Addr));
+
+ case kDNSType_NS: // Same as PTR
+ case kDNSType_MD: // Same as PTR
+ case kDNSType_MF: // Same as PTR
+ case kDNSType_CNAME:// Same as PTR
+ //case kDNSType_SOA not checked
+ case kDNSType_MB: // Same as PTR
+ case kDNSType_MG: // Same as PTR
+ case kDNSType_MR: // Same as PTR
+ //case kDNSType_NULL not checked (no specified format, so always valid)
+ //case kDNSType_WKS not checked
+ case kDNSType_PTR: len = DomainNameLength(&rd->u.name);
+ return(len <= MAX_DOMAIN_NAME && rdlength == len);
+
+ case kDNSType_HINFO:// Same as TXT (roughly)
+ case kDNSType_MINFO:// Same as TXT (roughly)
+ case kDNSType_TXT: {
+ const mDNSu8 *ptr = rd->u.txt.c;
+ const mDNSu8 *end = rd->u.txt.c + rdlength;
+ while (ptr < end) ptr += 1 + ptr[0];
+ return (ptr == end);
+ }
+
+ case kDNSType_AAAA: return(rdlength == sizeof(mDNSv6Addr));
+
+ case kDNSType_MX: len = DomainNameLength(&rd->u.mx.exchange);
+ return(len <= MAX_DOMAIN_NAME && rdlength == 2+len);
+
+ case kDNSType_SRV: len = DomainNameLength(&rd->u.srv.target);
+ return(len <= MAX_DOMAIN_NAME && rdlength == 6+len);
+
+ default: return(mDNStrue); // Allow all other types without checking
+ }
}
-mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, ResourceRecord *const rr, const mDNSs32 timenow)
+// Two records qualify to be local duplicates if the RecordTypes are the same, or if one is Unique and the other Verified
+#define RecordLDT(A,B) ((A)->resrec.RecordType == (B)->resrec.RecordType || ((A)->resrec.RecordType | (B)->resrec.RecordType) == (kDNSRecordTypeUnique | kDNSRecordTypeVerified))
+#define RecordIsLocalDuplicate(A,B) ((A)->resrec.InterfaceID == (B)->resrec.InterfaceID && RecordLDT((A),(B)) && IdenticalResourceRecord(&(A)->resrec, &(B)->resrec))
+
+mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr)
{
- ResourceRecord **p = &m->ResourceRecords;
+ domainname *target = GetRRDomainNameTarget(&rr->resrec);
+ AuthRecord *r;
+ AuthRecord **p = &m->ResourceRecords;
+ AuthRecord **d = &m->DuplicateRecords;
+ AuthRecord **l = &m->LocalOnlyRecords;
+
+#if TEST_LOCALONLY_FOR_EVERYTHING
+ rr->resrec.InterfaceID = (mDNSInterfaceID)~0;
+#endif
+
while (*p && *p != rr) p=&(*p)->next;
- if (*p)
+ while (*d && *d != rr) d=&(*d)->next;
+ while (*l && *l != rr) l=&(*l)->next;
+ if (*d || *p || *l)
{
- debugf("Error! Tried to register a ResourceRecord that's already in the list");
+ LogMsg("Error! Tried to register a AuthRecord %p %##s (%s) that's already in the list", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype));
return(mStatus_AlreadyRegistered);
}
if (rr->DependentOn)
{
- if (rr->RecordType == kDNSRecordTypeUnique)
- rr->RecordType = kDNSRecordTypeVerified;
+ if (rr->resrec.RecordType == kDNSRecordTypeUnique)
+ rr->resrec.RecordType = kDNSRecordTypeVerified;
else
{
- debugf("mDNS_Register_internal: ERROR! %##s: rr->DependentOn && RecordType != kDNSRecordTypeUnique",
- rr->name.c);
+ LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn && RecordType != kDNSRecordTypeUnique",
+ rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype));
return(mStatus_Invalid);
}
- if (rr->DependentOn->RecordType != kDNSRecordTypeUnique && rr->DependentOn->RecordType != kDNSRecordTypeVerified)
+ if (!(rr->DependentOn->resrec.RecordType & (kDNSRecordTypeUnique | kDNSRecordTypeVerified)))
{
- debugf("mDNS_Register_internal: ERROR! %##s: rr->DependentOn->RecordType bad type %X",
- rr->name.c, rr->DependentOn->RecordType);
+ LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn->RecordType bad type %X",
+ rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), rr->DependentOn->resrec.RecordType);
return(mStatus_Invalid);
}
}
+ // If this resource record is referencing a specific interface, make sure it exists
+ if (rr->resrec.InterfaceID && rr->resrec.InterfaceID != ((mDNSInterfaceID)~0))
+ {
+ NetworkInterfaceInfo *intf;
+ for (intf = m->HostInterfaces; intf; intf = intf->next)
+ if (intf->InterfaceID == rr->resrec.InterfaceID) break;
+ if (!intf)
+ {
+ debugf("mDNS_Register_internal: Bogus InterfaceID %p in resource record", rr->resrec.InterfaceID);
+ return(mStatus_BadReferenceErr);
+ }
+ }
+
rr->next = mDNSNULL;
// Field Group 1: Persistent metadata for Authoritative Records
// rr->Callback = already set in mDNS_SetupResourceRecord
// rr->Context = already set in mDNS_SetupResourceRecord
// rr->RecordType = already set in mDNS_SetupResourceRecord
-// rr->HostTarget = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
+// rr->HostTarget = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client
// Field Group 2: Transient state for Authoritative Records
rr->Acknowledged = mDNSfalse;
- rr->ProbeCount = DefaultProbeCountForRecordType(rr->RecordType);
- rr->AnnounceCount = DefaultAnnounceCountForRecordType(rr->RecordType);
+ rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType);
+ rr->AnnounceCount = InitialAnnounceCount;
rr->IncludeInProbe = mDNSfalse;
- rr->SendPriority = 0;
- rr->Requester = zeroIPAddr;
+ rr->ImmedAnswer = mDNSNULL;
+ rr->ImmedAdditional = mDNSNULL;
+ rr->SendRNow = mDNSNULL;
+ rr->v4Requester = zeroIPAddr;
+ rr->v6Requester = zerov6Addr;
rr->NextResponse = mDNSNULL;
rr->NR_AnswerTo = mDNSNULL;
rr->NR_AdditionalTo = mDNSNULL;
- rr->LastSendTime = timenow - mDNSPlatformOneSecond;
- rr->NextSendTime = timenow;
- if (rr->RecordType == kDNSRecordTypeUnique && m->SuppressProbes) rr->NextSendTime = m->SuppressProbes;
- rr->NextSendInterval = DefaultSendIntervalForRecordType(rr->RecordType);
+ rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
+ InitializeLastAPTime(m, rr);
+// rr->AnnounceUntil = Set for us in InitializeLastAPTime()
+// rr->LastAPTime = Set for us in InitializeLastAPTime()
+// rr->LastMCTime = Set for us in InitializeLastAPTime()
+// rr->LastMCInterface = Set for us in InitializeLastAPTime()
rr->NewRData = mDNSNULL;
+ rr->newrdlength = 0;
rr->UpdateCallback = mDNSNULL;
+ rr->UpdateCredits = kMaxUpdateCredits;
+ rr->NextUpdateCredit = 0;
+ rr->UpdateBlocked = 0;
- // Field Group 3: Transient state for Cache Records
- rr->NextDupSuppress = mDNSNULL; // Not strictly relevant for a local record
- rr->TimeRcvd = 0; // Not strictly relevant for a local record
- rr->LastUsed = 0; // Not strictly relevant for a local record
- rr->UseCount = 0; // Not strictly relevant for a local record
- rr->UnansweredQueries = 0; // Not strictly relevant for a local record
- rr->Active = mDNSfalse; // Not strictly relevant for a local record
- rr->NewData = mDNSfalse; // Not strictly relevant for a local record
-
- // Field Group 4: The actual information pertaining to this resource record
-// rr->interface = already set in mDNS_SetupResourceRecord
-// rr->name.c = MUST be set by client
-// rr->rrtype = already set in mDNS_SetupResourceRecord
-// rr->rrclass = already set in mDNS_SetupResourceRecord
-// rr->rroriginalttl = already set in mDNS_SetupResourceRecord
-// rr->rrremainingttl = already set in mDNS_SetupResourceRecord
+// rr->resrec.interface = already set in mDNS_SetupResourceRecord
+// rr->resrec.name.c = MUST be set by client
+// rr->resrec.rrtype = already set in mDNS_SetupResourceRecord
+// rr->resrec.rrclass = already set in mDNS_SetupResourceRecord
+// rr->resrec.rroriginalttl = already set in mDNS_SetupResourceRecord
+// rr->resrec.rdata = MUST be set by client, unless record type is CNAME or PTR and rr->HostTarget is set
if (rr->HostTarget)
+ {
+ if (target) target->c[0] = 0;
SetTargetToHostName(m, rr); // This also sets rdlength and rdestimate for us
+ }
else
{
- rr->rdata->RDLength = GetRDLength(rr, mDNSfalse);
- rr->rdestimate = GetRDLength(rr, mDNStrue);
+ rr->resrec.rdlength = GetRDLength(&rr->resrec, mDNSfalse);
+ rr->resrec.rdestimate = GetRDLength(&rr->resrec, mDNStrue);
}
-// rr->rdata = MUST be set by client
- *p = rr;
+ if (!ValidateDomainName(&rr->resrec.name))
+ { LogMsg("Attempt to register record with invalid name: %s", GetRRDisplayString(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)); return(mStatus_Invalid); }
+
+ rr->resrec.namehash = DomainNameHashValue(&rr->resrec.name);
+ rr->resrec.rdatahash = RDataHashValue(rr->resrec.rdlength, &rr->resrec.rdata->u);
+ rr->resrec.rdnamehash = target ? DomainNameHashValue(target) : 0;
+
+ if (rr->resrec.InterfaceID == ((mDNSInterfaceID)~0))
+ {
+ debugf("Adding %p %##s (%s) to LocalOnly list", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype));
+ *l = rr;
+ if (!m->NewLocalOnlyRecords) m->NewLocalOnlyRecords = rr;
+ // If this is supposed to be unique, make sure we don't have any name conflicts
+ if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
+ {
+ const AuthRecord *s1 = rr->RRSet ? rr->RRSet : rr;
+ for (r = m->LocalOnlyRecords; r; r=r->next)
+ {
+ const AuthRecord *s2 = r->RRSet ? r->RRSet : r;
+ if (s1 != s2 && SameResourceRecordSignature(&r->resrec, &rr->resrec) && !SameRData(&r->resrec, &rr->resrec))
+ break;
+ }
+ if (r) // If we found a conflict, set DiscardLocalOnlyRecords so we'll deliver the callback
+ {
+ debugf("Name conflict %p %##s (%s)", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype));
+ m->DiscardLocalOnlyRecords = mDNStrue;
+ }
+ else // else no conflict, so set ProbeCount to zero and update RecordType as appropriate
+ {
+ rr->ProbeCount = 0;
+ if (rr->resrec.RecordType == kDNSRecordTypeUnique) rr->resrec.RecordType = kDNSRecordTypeVerified;
+ }
+ }
+ }
+ else
+ {
+ // Now that's we've finished building our new record, make sure it's not identical to one we already have
+ for (r = m->ResourceRecords; r; r=r->next) if (RecordIsLocalDuplicate(r, rr)) break;
+
+ if (r)
+ {
+ debugf("Adding %p %##s (%s) to duplicate list", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype));
+ *d = rr;
+ // If the previous copy of this record is already verified unique,
+ // then indicate that we should move this record promptly to kDNSRecordTypeUnique state.
+ // Setting ProbeCount to zero will cause SendQueries() to advance this record to
+ // kDNSRecordTypeVerified state and call the client callback at the next appropriate time.
+ if (rr->resrec.RecordType == kDNSRecordTypeUnique && r->resrec.RecordType == kDNSRecordTypeVerified)
+ rr->ProbeCount = 0;
+ }
+ else
+ {
+ debugf("Adding %p %##s (%s) to active record list", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype));
+ *p = rr;
+ }
+ }
return(mStatus_NoError);
}
+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) m->SuppressProbes = (m->timenow + mDNSPlatformOneSecond * 5) | 1;
+ if (m->NumFailedProbes >= 16)
+ LogMsg("Name in use: %##s (%s); need to choose another (%d)",
+ rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), m->NumFailedProbes);
+ }
+
// mDNS_Dereg_normal is used for most calls to mDNS_Deregister_internal
// mDNS_Dereg_conflict is used to indicate that this record is being forcibly deregistered because of a conflict
// mDNS_Dereg_repeat is used when cleaning up, for records that may have already been forcibly deregistered
// NOTE: mDNS_Deregister_internal can call a user callback, which may change the record list and/or question list.
// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
-mDNSlocal void mDNS_Deregister_internal(mDNS *const m, ResourceRecord *const rr, const mDNSs32 timenow, mDNS_Dereg_type drt)
+mDNSlocal mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, mDNS_Dereg_type drt)
{
- mDNSu8 RecordType = rr->RecordType;
- // 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 <= DefaultAnnounceCountForTypeShared)
+ mDNSu8 RecordType = rr->resrec.RecordType;
+ AuthRecord **p = &m->ResourceRecords; // Find this record in our list of active records
+ if (rr->resrec.InterfaceID == ((mDNSInterfaceID)~0)) p = &m->LocalOnlyRecords;
+ while (*p && *p != rr) p=&(*p)->next;
+
+ if (*p)
{
- debugf("mDNS_Deregister_internal: Sending deregister for %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype));
- rr->RecordType = kDNSRecordTypeDeregistering;
- rr->rroriginalttl = 0;
- rr->rrremainingttl = 0;
+ // We found our record on the main list. See if there are any duplicates that need special handling.
+ if (drt == mDNS_Dereg_conflict) // If this was a conflict, see that all duplicates get the same treatment
+ {
+ AuthRecord *r2 = m->DuplicateRecords;
+ while (r2)
+ {
+ if (RecordIsLocalDuplicate(r2, rr)) { mDNS_Deregister_internal(m, r2, drt); r2 = m->DuplicateRecords; }
+ else r2=r2->next;
+ }
+ }
+ else
+ {
+ // Before we delete the record (and potentially send a goodbye packet)
+ // first see if we have a record on the duplicate list ready to take over from it.
+ AuthRecord **d = &m->DuplicateRecords;
+ while (*d && !RecordIsLocalDuplicate(*d, rr)) d=&(*d)->next;
+ if (*d)
+ {
+ AuthRecord *dup = *d;
+ debugf("Duplicate record %p taking over from %p %##s (%s)", dup, rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype));
+ *d = dup->next; // Cut replacement record from DuplicateRecords list
+ 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;
+ }
+ }
}
else
{
- // Find this record in our list of active records
- ResourceRecord **p = &m->ResourceRecords;
+ // We didn't find our record on the main list; try the DuplicateRecords list instead.
+ 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) debugf("DNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype));
+ }
+
+ if (!*p)
+ {
+ // 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));
+ return(mStatus_BadReferenceErr);
+ }
- if (*p) *p = rr->next;
+ // 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)
+ {
+ verbosedebugf("mDNS_Deregister_internal: Sending deregister for %##s (%s)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype));
+ rr->resrec.RecordType = kDNSRecordTypeDeregistering;
+ rr->resrec.rroriginalttl = 0;
+ rr->ImmedAnswer = mDNSInterfaceMark;
+ if (rr->resrec.InterfaceID == ((mDNSInterfaceID)~0))
+ m->DiscardLocalOnlyRecords = mDNStrue;
else
{
- // No need to give an error message if we already know this is a potentially repeated deregistration
- if (drt != mDNS_Dereg_repeat)
- debugf("mDNS_Deregister_internal: Record %##s (%s) not found in list", rr->name.c, DNSTypeName(rr->rrtype));
- return;
+ if (m->NextScheduledResponse - (m->timenow + mDNSPlatformOneSecond/10) >= 0)
+ m->NextScheduledResponse = (m->timenow + mDNSPlatformOneSecond/10);
}
+ }
+ else
+ {
+ *p = rr->next; // Cut this record from the list
// If someone is about to look at this, bump the pointer forward
- if (m->CurrentRecord == rr) m->CurrentRecord = rr->next;
+ if (m->CurrentRecord == rr) m->CurrentRecord = rr->next;
+ if (m->NewLocalOnlyRecords == rr) m->NewLocalOnlyRecords = rr->next;
rr->next = mDNSNULL;
if (RecordType == kDNSRecordTypeUnregistered)
- debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeUnregistered", rr->name.c, DNSTypeName(rr->rrtype));
+ debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeUnregistered",
+ rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype));
else if (RecordType == kDNSRecordTypeDeregistering)
- debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeDeregistering", rr->name.c, DNSTypeName(rr->rrtype));
+ debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeDeregistering",
+ rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype));
else
{
- debugf("mDNS_Deregister_internal: Deleting record for %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype));
- rr->RecordType = kDNSRecordTypeUnregistered;
+ verbosedebugf("mDNS_Deregister_internal: Deleting record for %##s (%s)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype));
+ rr->resrec.RecordType = kDNSRecordTypeUnregistered;
}
if ((drt == mDNS_Dereg_conflict || drt == mDNS_Dereg_repeat) && RecordType == kDNSRecordTypeShared)
- debugf("mDNS_Deregister_internal: Cannot have a conflict on a shared record! %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype));
+ debugf("mDNS_Deregister_internal: Cannot have a conflict on a shared record! %##s (%s)",
+ rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype));
// If we have an update queued up which never executed, give the client a chance to free that memory
if (rr->NewRData)
{
- RData *OldRData = rr->rdata;
- rr->rdata = rr->NewRData; // Update our rdata
- rr->NewRData = mDNSNULL; // Clear the NewRData pointer ...
- if (rr->UpdateCallback) rr->UpdateCallback(m, rr, OldRData); // ... and let the client know
+ RData *OldRData = rr->resrec.rdata;
+ SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); // Update our rdata
+ rr->NewRData = mDNSNULL; // Clear the NewRData pointer ...
+ if (rr->UpdateCallback)
+ rr->UpdateCallback(m, rr, OldRData); // ... and let the client know
}
- if (RecordType == kDNSRecordTypeShared && rr->Callback)
- rr->Callback(m, rr, mStatus_MemFree);
+ // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function
+ // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
+ // 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)
{
- m->ProbeFailTime = timenow;
- // If we've had ten 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) m->NumFailedProbes++;
- else m->SuppressProbes = (timenow + mDNSPlatformOneSecond * 5) | 1;
- if (rr->Callback) rr->Callback(m, rr, mStatus_NameConflict);
+ RecordProbeFailure(m, rr);
+ if (rr->RecordCallback)
+ rr->RecordCallback(m, rr, mStatus_NameConflict);
}
+ m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
}
+ return(mStatus_NoError);
}
// ***************************************************************************
-#if 0
+#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark -
#pragma mark - DNS Message Creation Functions
while (*np && ptr < limit-1) // While we've got characters in the name, and space to write them in the message...
{
+ if (*np > MAX_DOMAIN_LABEL)
+ { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name->c); return(mDNSNULL); }
+
+ // This check correctly allows for the final trailing root label:
+ // e.g.
+ // Suppose our domain name is exactly 255 bytes long, including the final trailing root label.
+ // Suppose np is now at name->c[248], and we're about to write our last non-null label ("local").
+ // We know that max will be at name->c[255]
+ // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our
+ // six bytes, then exit the loop, write the final terminating root label, and the domain
+ // name we've written is exactly 255 bytes long, exactly at the correct legal limit.
+ // If the name is one byte longer, then we fail the "if" test below, and correctly bail out.
if (np + 1 + *np >= max)
- { debugf("Malformed domain name (more than 255 characters)"); return(mDNSNULL); }
+ { LogMsg("Malformed domain name %##s (more than 255 bytes)", name->c); return(mDNSNULL); }
if (base) pointer = FindCompressionPointer(base, searchlimit, np);
if (pointer) // Use a compression pointer if we can
return(mDNSNULL);
}
-mDNSlocal mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit,
- const mDNSu16 rrtype, const RData *const rdata)
+mDNSlocal mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, ResourceRecord *rr)
{
- switch (rrtype)
+ switch (rr->rrtype)
{
- case kDNSType_A: if (rdata->RDLength != 4)
+ case kDNSType_A: if (rr->rdlength != 4)
{
- debugf("putRData: Illegal length %d for kDNSType_A", rdata->RDLength);
+ debugf("putRData: Illegal length %d for kDNSType_A", rr->rdlength);
return(mDNSNULL);
}
if (ptr + 4 > limit) return(mDNSNULL);
- *ptr++ = rdata->u.ip.b[0];
- *ptr++ = rdata->u.ip.b[1];
- *ptr++ = rdata->u.ip.b[2];
- *ptr++ = rdata->u.ip.b[3];
+ *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];
return(ptr);
case kDNSType_CNAME:// Same as PTR
- case kDNSType_PTR: return(putDomainNameAsLabels(msg, ptr, limit, &rdata->u.name));
+ case kDNSType_PTR: return(putDomainNameAsLabels(msg, ptr, limit, &rr->rdata->u.name));
- case kDNSType_TXT: if (ptr + rdata->RDLength > limit) return(mDNSNULL);
- mDNSPlatformMemCopy(rdata->u.data, ptr, rdata->RDLength);
- return(ptr + rdata->RDLength);
+ case kDNSType_HINFO:// Same as TXT
+ case kDNSType_TXT: if (ptr + rr->rdlength > limit) return(mDNSNULL);
+ mDNSPlatformMemCopy(rr->rdata->u.data, ptr, rr->rdlength);
+ return(ptr + rr->rdlength);
- case kDNSType_SRV: if (ptr + 6 > limit) return(mDNSNULL);
- *ptr++ = (mDNSu8)(rdata->u.srv.priority >> 8);
- *ptr++ = (mDNSu8)(rdata->u.srv.priority );
- *ptr++ = (mDNSu8)(rdata->u.srv.weight >> 8);
- *ptr++ = (mDNSu8)(rdata->u.srv.weight );
- *ptr++ = rdata->u.srv.port.b[0];
- *ptr++ = rdata->u.srv.port.b[1];
- return(putDomainNameAsLabels(msg, ptr, limit, &rdata->u.srv.target));
+ case kDNSType_AAAA: if (rr->rdlength != sizeof(rr->rdata->u.ipv6))
+ {
+ debugf("putRData: Illegal length %d for kDNSType_AAAA", rr->rdlength);
+ return(mDNSNULL);
+ }
+ if (ptr + sizeof(rr->rdata->u.ipv6) > limit) return(mDNSNULL);
+ mDNSPlatformMemCopy(&rr->rdata->u.ipv6, ptr, sizeof(rr->rdata->u.ipv6));
+ return(ptr + sizeof(rr->rdata->u.ipv6));
- default: if (ptr + rdata->RDLength > limit) return(mDNSNULL);
- debugf("putRData: Warning! Writing resource type %d as raw data", rrtype);
- mDNSPlatformMemCopy(rdata->u.data, ptr, rdata->RDLength);
- return(ptr + rdata->RDLength);
+ case kDNSType_SRV: if (ptr + 6 > limit) return(mDNSNULL);
+ *ptr++ = (mDNSu8)(rr->rdata->u.srv.priority >> 8);
+ *ptr++ = (mDNSu8)(rr->rdata->u.srv.priority );
+ *ptr++ = (mDNSu8)(rr->rdata->u.srv.weight >> 8);
+ *ptr++ = (mDNSu8)(rr->rdata->u.srv.weight );
+ *ptr++ = rr->rdata->u.srv.port.b[0];
+ *ptr++ = rr->rdata->u.srv.port.b[1];
+ return(putDomainNameAsLabels(msg, ptr, limit, &rr->rdata->u.srv.target));
+
+ default: if (ptr + rr->rdlength > limit) return(mDNSNULL);
+ debugf("putRData: Warning! Writing resource type %d as raw data", rr->rrtype);
+ mDNSPlatformMemCopy(rr->rdata->u.data, ptr, rr->rdlength);
+ return(ptr + rr->rdlength);
}
}
-// Put a domain name, type, class, ttl, length, and type-specific data
-// domainname is a fully-qualified name
-// Only pass the "m" and "timenow" parameters in cases where the LastSendTime is to be updated,
-// and the kDNSClass_UniqueRRSet bit set
-mDNSlocal mDNSu8 *putResourceRecord(DNSMessage *const msg, mDNSu8 *ptr,
- mDNSu16 *count, ResourceRecord *rr, mDNS *const m, const mDNSs32 timenow)
+mDNSlocal mDNSu8 *PutResourceRecordTTL(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl)
{
mDNSu8 *endofrdata;
- mDNSu32 actualLength;
+ 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,
if (rr->RecordType == kDNSRecordTypeUnregistered)
{
- debugf("putResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered");
+ LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype));
return(ptr);
}
ptr[1] = (mDNSu8)(rr->rrtype );
ptr[2] = (mDNSu8)(rr->rrclass >> 8);
ptr[3] = (mDNSu8)(rr->rrclass );
- ptr[4] = (mDNSu8)(rr->rrremainingttl >> 24);
- ptr[5] = (mDNSu8)(rr->rrremainingttl >> 16);
- ptr[6] = (mDNSu8)(rr->rrremainingttl >> 8);
- ptr[7] = (mDNSu8)(rr->rrremainingttl );
- endofrdata = putRData(msg, ptr+10, limit, rr->rrtype, rr->rdata);
- if (!endofrdata) { debugf("Ran out of space in putResourceRecord!"); return(mDNSNULL); }
+ ptr[4] = (mDNSu8)(ttl >> 24);
+ ptr[5] = (mDNSu8)(ttl >> 16);
+ ptr[6] = (mDNSu8)(ttl >> 8);
+ ptr[7] = (mDNSu8)(ttl );
+ endofrdata = putRData(msg, ptr+10, limit, rr);
+ if (!endofrdata) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype)); return(mDNSNULL); }
// Go back and fill in the actual number of data bytes we wrote
// (actualLength can be less than rdlength when domain name compression is used)
- actualLength = (mDNSu32)(endofrdata - ptr - 10);
+ actualLength = (mDNSu16)(endofrdata - ptr - 10);
ptr[8] = (mDNSu8)(actualLength >> 8);
ptr[9] = (mDNSu8)(actualLength );
- if (m) // If the 'm' parameter was passed in...
- {
- rr->LastSendTime = timenow; // ... then update LastSendTime
- if (rr->RecordType & kDNSRecordTypeUniqueMask) // If it is supposed to be unique
- {
- const ResourceRecord *a = mDNSNULL;
- // If we find a member of the same RRSet (same name/type/class)
- // that hasn't been updated within the last quarter second, don't set the bit
- for (a = m->ResourceRecords; a; a=a->next)
- if (SameResourceRecordSignatureAnyInterface(rr, a))
- if (timenow - a->LastSendTime > mDNSPlatformOneSecond/4)
- break;
- if (a == mDNSNULL)
- ptr[2] |= kDNSClass_UniqueRRSet >> 8;
- }
- }
-
(*count)++;
return(endofrdata);
}
+#define PutResourceRecord(MSG, P, C, RR) PutResourceRecordTTL((MSG), (P), (C), (RR), (RR)->rroriginalttl)
+
+mDNSlocal mDNSu8 *PutResourceRecordCappedTTL(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 maxttl)
+ {
+ if (maxttl > rr->rroriginalttl) maxttl = rr->rroriginalttl;
+ return(PutResourceRecordTTL(msg, ptr, count, rr, maxttl));
+ }
+
#if 0
mDNSlocal mDNSu8 *putEmptyResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit,
- mDNSu16 *count, const ResourceRecord *rr)
+ mDNSu16 *count, const AuthRecord *rr)
{
ptr = putDomainNameAsLabels(msg, ptr, limit, &rr->name);
if (!ptr || ptr + 10 > limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
- ptr[0] = (mDNSu8)(rr->rrtype >> 8); // Put type
- ptr[1] = (mDNSu8)(rr->rrtype );
- ptr[2] = (mDNSu8)(rr->rrclass >> 8); // Put class
- ptr[3] = (mDNSu8)(rr->rrclass );
+ ptr[0] = (mDNSu8)(rr->resrec.rrtype >> 8); // Put type
+ ptr[1] = (mDNSu8)(rr->resrec.rrtype );
+ ptr[2] = (mDNSu8)(rr->resrec.rrclass >> 8); // Put class
+ ptr[3] = (mDNSu8)(rr->resrec.rrclass );
ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // TTL is zero
ptr[8] = ptr[9] = 0; // RDATA length is zero
(*count)++;
}
// ***************************************************************************
-#if 0
+#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark - DNS Message Parsing Functions
#endif
mDNSlocal const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end)
{
- mDNSu32 total = 0;
+ mDNSu16 total = 0;
if (ptr < (mDNSu8*)msg || ptr >= end)
{ debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); }
return(ptr + pktrdlength);
}
-mDNSlocal const mDNSu8 *getResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end,
- const mDNSIPAddr InterfaceAddr, const mDNSs32 timenow, mDNSu8 RecordType, ResourceRecord *rr, RData *RDataStorage)
+#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))
+
+mDNSlocal const mDNSu8 *GetResourceRecord(mDNS *const m, const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end,
+ const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, CacheRecord *rr, RData *RDataStorage)
{
mDNSu16 pktrdlength;
rr->next = mDNSNULL;
-
- // Field Group 1: Persistent metadata for Authoritative Records
- rr->Additional1 = mDNSNULL;
- rr->Additional2 = mDNSNULL;
- rr->DependentOn = mDNSNULL;
- rr->RRSet = mDNSNULL;
- rr->Callback = mDNSNULL;
- rr->Context = mDNSNULL;
- rr->RecordType = RecordType;
- rr->HostTarget = mDNSfalse;
-
- // Field Group 2: Transient state for Authoritative Records
- rr->Acknowledged = mDNSfalse;
- rr->ProbeCount = 0;
- rr->AnnounceCount = 0;
- rr->IncludeInProbe = mDNSfalse;
- rr->SendPriority = 0;
- rr->Requester = zeroIPAddr;
- rr->NextResponse = mDNSNULL;
- rr->NR_AnswerTo = mDNSNULL;
- rr->NR_AdditionalTo = mDNSNULL;
- rr->LastSendTime = 0;
- rr->NextSendTime = 0;
- rr->NextSendInterval = 0;
- rr->NewRData = mDNSNULL;
- rr->UpdateCallback = mDNSNULL;
+ rr->resrec.RecordType = RecordType;
- // Field Group 3: Transient state for Cache Records
- rr->NextDupSuppress = mDNSNULL;
- rr->TimeRcvd = timenow;
- rr->LastUsed = timenow;
+ 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->CRActiveQuestion = mDNSNULL;
rr->UnansweredQueries = 0;
- rr->Active = mDNSfalse;
- rr->NewData = mDNStrue;
-
- // Field Group 4: The actual information pertaining to this resource record
- rr->InterfaceAddr = InterfaceAddr;
- ptr = getDomainName(msg, ptr, end, &rr->name);
- if (!ptr) { debugf("getResourceRecord: Malformed RR name"); return(mDNSNULL); }
-
- if (ptr + 10 > end) { debugf("getResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
-
- rr->rrtype = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
- rr->rrclass = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]) & kDNSQClass_Mask;
- rr->rroriginalttl = (mDNSu32)((mDNSu32)ptr[4] << 24 | (mDNSu32)ptr[5] << 16 | (mDNSu32)ptr[6] << 8 | ptr[7]);
- if (rr->rroriginalttl > 0x70000000UL / mDNSPlatformOneSecond)
- rr->rroriginalttl = 0x70000000UL / mDNSPlatformOneSecond;
- rr->rrremainingttl = 0;
+ rr->LastUnansweredTime= 0;
+ rr->MPUnansweredQ = 0;
+ rr->MPLastUnansweredQT= 0;
+ rr->MPUnansweredKA = 0;
+ rr->MPExpectingKA = mDNSfalse;
+ rr->NextInCFList = mDNSNULL;
+
+ rr->resrec.InterfaceID = InterfaceID;
+ ptr = getDomainName(msg, ptr, end, &rr->resrec.name);
+ if (!ptr) { debugf("GetResourceRecord: Malformed RR name"); return(mDNSNULL); }
+
+ if (ptr + 10 > end) { debugf("GetResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
+
+ rr->resrec.rrtype = (mDNSu16) ((mDNSu16)ptr[0] << 8 | ptr[1]);
+ rr->resrec.rrclass = (mDNSu16)(((mDNSu16)ptr[2] << 8 | ptr[3]) & kDNSClass_Mask);
+ rr->resrec.rroriginalttl = (mDNSu32) ((mDNSu32)ptr[4] << 24 | (mDNSu32)ptr[5] << 16 | (mDNSu32)ptr[6] << 8 | ptr[7]);
+ if (rr->resrec.rroriginalttl > 0x70000000UL / mDNSPlatformOneSecond)
+ rr->resrec.rroriginalttl = 0x70000000UL / mDNSPlatformOneSecond;
+ // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for
+ // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly.
pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]);
if (ptr[2] & (kDNSClass_UniqueRRSet >> 8))
- rr->RecordType |= kDNSRecordTypeUniqueMask;
+ rr->resrec.RecordType |= kDNSRecordTypePacketUniqueMask;
ptr += 10;
- if (ptr + pktrdlength > end) { debugf("getResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
+ if (ptr + pktrdlength > end) { debugf("GetResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
if (RDataStorage)
- rr->rdata = RDataStorage;
+ rr->resrec.rdata = RDataStorage;
else
{
- rr->rdata = &rr->rdatastorage;
- rr->rdata->MaxRDLength = sizeof(RDataBody);
+ rr->resrec.rdata = (RData*)&rr->rdatastorage;
+ rr->resrec.rdata->MaxRDLength = sizeof(RDataBody);
}
- switch (rr->rrtype)
+ switch (rr->resrec.rrtype)
{
- case kDNSType_A: rr->rdata->u.ip.b[0] = ptr[0];
- rr->rdata->u.ip.b[1] = ptr[1];
- rr->rdata->u.ip.b[2] = ptr[2];
- rr->rdata->u.ip.b[3] = ptr[3];
+ 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];
break;
- case kDNSType_CNAME:// CNAME is same as PTR
- case kDNSType_PTR: if (!getDomainName(msg, ptr, end, &rr->rdata->u.name))
- { debugf("getResourceRecord: Malformed CNAME/PTR RDATA name"); return(mDNSNULL); }
- //debugf("%##s PTR %##s rdlen %d", rr->name.c, rr->rdata->u.name.c, pktrdlength);
+ case kDNSType_CNAME:// Same as PTR
+ case kDNSType_PTR: if (!getDomainName(msg, ptr, end, &rr->resrec.rdata->u.name))
+ { debugf("GetResourceRecord: Malformed CNAME/PTR RDATA name"); return(mDNSNULL); }
+ //debugf("%##s PTR %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.name.c, pktrdlength);
break;
- case kDNSType_TXT: if (pktrdlength > rr->rdata->MaxRDLength)
+ case kDNSType_NULL: //Same as TXT
+ case kDNSType_HINFO://Same as TXT
+ case kDNSType_TXT: if (pktrdlength > rr->resrec.rdata->MaxRDLength)
{
- debugf("getResourceRecord: TXT rdata size (%d) exceeds storage (%d)",
- pktrdlength, rr->rdata->MaxRDLength);
+ debugf("GetResourceRecord: %s rdata size (%d) exceeds storage (%d)",
+ DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength);
return(mDNSNULL);
}
- rr->rdata->RDLength = pktrdlength;
- mDNSPlatformMemCopy(ptr, rr->rdata->u.data, pktrdlength);
+ rr->resrec.rdlength = pktrdlength;
+ mDNSPlatformMemCopy(ptr, rr->resrec.rdata->u.data, pktrdlength);
break;
- case kDNSType_SRV: rr->rdata->u.srv.priority = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
- rr->rdata->u.srv.weight = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
- rr->rdata->u.srv.port.b[0] = ptr[4];
- rr->rdata->u.srv.port.b[1] = ptr[5];
- if (!getDomainName(msg, ptr+6, end, &rr->rdata->u.srv.target))
- { debugf("getResourceRecord: Malformed SRV RDATA name"); return(mDNSNULL); }
- //debugf("%##s SRV %##s rdlen %d", rr->name.c, rr->rdata->u.srv.target.c, pktrdlength);
+ case kDNSType_AAAA: mDNSPlatformMemCopy(ptr, &rr->resrec.rdata->u.ipv6, sizeof(rr->resrec.rdata->u.ipv6));
break;
- default: if (pktrdlength > rr->rdata->MaxRDLength)
+ case kDNSType_SRV: rr->resrec.rdata->u.srv.priority = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
+ rr->resrec.rdata->u.srv.weight = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
+ rr->resrec.rdata->u.srv.port.b[0] = ptr[4];
+ rr->resrec.rdata->u.srv.port.b[1] = ptr[5];
+ if (!getDomainName(msg, ptr+6, end, &rr->resrec.rdata->u.srv.target))
+ { debugf("GetResourceRecord: Malformed SRV RDATA name"); return(mDNSNULL); }
+ //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.srv.target.c, pktrdlength);
+ break;
+
+ default: if (pktrdlength > rr->resrec.rdata->MaxRDLength)
{
- debugf("getResourceRecord: rdata %d size (%d) exceeds storage (%d)",
- rr->rrtype, pktrdlength, rr->rdata->MaxRDLength);
+ debugf("GetResourceRecord: rdata %d (%s) size (%d) exceeds storage (%d)",
+ rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength);
return(mDNSNULL);
}
- if (rr->rrtype != kDNSType_AAAA)
- debugf("getResourceRecord: Warning! Reading resource type %d as opaque data", rr->rrtype);
+ debugf("GetResourceRecord: Warning! Reading resource type %d (%s) as opaque data",
+ rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype));
// Note: Just because we don't understand the record type, that doesn't
// mean we fail. The DNS protocol specifies rdlength, so we can
// safely skip over unknown records and ignore them.
// We also grab a binary copy of the rdata anyway, since the caller
// might know how to interpret it even if we don't.
- rr->rdata->RDLength = pktrdlength;
- mDNSPlatformMemCopy(ptr, rr->rdata->u.data, pktrdlength);
+ rr->resrec.rdlength = pktrdlength;
+ mDNSPlatformMemCopy(ptr, rr->resrec.rdata->u.data, pktrdlength);
break;
}
- rr->rdata->RDLength = GetRDLength(rr, mDNSfalse);
- rr->rdestimate = GetRDLength(rr, mDNStrue);
+ rr->resrec.namehash = DomainNameHashValue(&rr->resrec.name);
+ SetNewRData(&rr->resrec, mDNSNULL, 0);
+
return(ptr + pktrdlength);
}
return(ptr+4);
}
-mDNSlocal const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSIPAddr InterfaceAddr,
+mDNSlocal const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID,
DNSQuestion *question)
{
- question->InterfaceAddr = InterfaceAddr;
- ptr = getDomainName(msg, ptr, end, &question->name);
+ question->InterfaceID = InterfaceID;
+ ptr = getDomainName(msg, ptr, end, &question->qname);
if (!ptr) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL); }
if (ptr+4 > end) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL); }
-
- question->rrtype = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); // Get type
- question->rrclass = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); // and class
+
+ question->qnamehash = DomainNameHashValue(&question->qname);
+ question->qtype = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); // Get type
+ question->qclass = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); // and class
return(ptr+4);
}
}
// ***************************************************************************
-#if 0
+#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark -
#pragma mark - Packet Sending Functions
#endif
mDNSlocal mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end,
- mDNSIPAddr src, mDNSIPPort srcport, mDNSIPAddr dst, mDNSIPPort dstport)
+ mDNSInterfaceID InterfaceID, mDNSIPPort srcport, const mDNSAddr *dst, mDNSIPPort dstport)
{
mStatus status;
mDNSu16 numQuestions = msg->h.numQuestions;
*ptr++ = (mDNSu8)(numAdditionals );
// Send the packet on the wire
- status = mDNSPlatformSendUDP(m, msg, end, src, srcport, dst, dstport);
+ status = mDNSPlatformSendUDP(m, msg, end, InterfaceID, srcport, dst, dstport);
// Put all the integer values back the way they were before we return
msg->h.numQuestions = numQuestions;
return(status);
}
-mDNSlocal mDNSBool HaveResponses(const mDNS *const m, const mDNSs32 timenow)
+mDNSlocal void CompleteDeregistration(mDNS *const m, AuthRecord *rr)
{
- ResourceRecord *rr;
- if (m->SleepState)
- {
- for (rr = m->ResourceRecords; rr; rr=rr->next)
- if (rr->RecordType == kDNSRecordTypeShared && rr->rrremainingttl == 0)
- return(mDNStrue);
- }
- else
- {
- for (rr = m->ResourceRecords; rr; rr=rr->next)
- {
- if (rr->RecordType == kDNSRecordTypeDeregistering)
- return(mDNStrue);
-
- if (rr->AnnounceCount && ResourceRecordIsValidAnswer(rr) && timenow - rr->NextSendTime >= 0)
- return(mDNStrue);
-
- if (rr->SendPriority >= kDNSSendPriorityAnswer && ResourceRecordIsValidAnswer(rr))
- return(mDNStrue);
- }
- }
- return(mDNSfalse);
+ // Setting AnnounceCount to InitialAnnounceCount 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);
}
// NOTE: DiscardDeregistrations calls mDNS_Deregister_internal which can call a user callback, which may change
// the record list and/or question list.
// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
-mDNSlocal void DiscardDeregistrations(mDNS *const m, mDNSs32 timenow)
+mDNSlocal void DiscardDeregistrations(mDNS *const m)
{
- if (m->CurrentRecord) debugf("DiscardDeregistrations ERROR m->CurrentRecord already set");
+ if (m->CurrentRecord) LogMsg("DiscardDeregistrations ERROR m->CurrentRecord already set");
m->CurrentRecord = m->ResourceRecords;
while (m->CurrentRecord)
{
- ResourceRecord *rr = m->CurrentRecord;
+ AuthRecord *rr = m->CurrentRecord;
m->CurrentRecord = rr->next;
- if (rr->RecordType == kDNSRecordTypeDeregistering)
- {
- rr->RecordType = kDNSRecordTypeShared;
- rr->AnnounceCount = DefaultAnnounceCountForTypeShared+1;
- mDNS_Deregister_internal(m, rr, timenow, mDNS_Dereg_normal);
- }
+ if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
+ CompleteDeregistration(m, rr);
}
}
-// This routine sends as many records as it can fit in a single DNS Response Message, in order of priority.
-// If there are any deregistrations, announcements, or answers that don't fit, they are left in the work list for next time.
-// If there are any additionals that don't fit, they are discarded -- they were optional anyway.
-// NOTE: BuildResponse calls mDNS_Deregister_internal which can call a user callback, which may change
+// Note about acceleration of announcements to facilitate automatic coalescing of
+// multiple independent threads of announcements into a single synchronized thread:
+// The announcements in the packet may be at different stages of maturity;
+// One-second interval, two-second interval, four-second interval, and so on.
+// After we've put in all the announcements that are due, we then consider
+// whether there are other nearly-due announcements that are worth accelerating.
+// To be eligible for acceleration, a record MUST NOT be older (further along
+// its timeline) than the most mature record we've already put in the packet.
+// In other words, younger records can have their timelines accelerated to catch up
+// with their elder bretheren; this narrows the age gap and helps them eventually get in sync.
+// Older records cannot have their timelines accelerated; this would just widen
+// the gap between them and their younger bretheren and get them even more out of sync.
+
+// NOTE: SendResponses calls mDNS_Deregister_internal which can call a user callback, which may change
// the record list and/or question list.
// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
-mDNSlocal mDNSu8 *BuildResponse(mDNS *const m,
- DNSMessage *const response, mDNSu8 *responseptr, const mDNSIPAddr InterfaceAddr, const mDNSs32 timenow)
+mDNSlocal void SendResponses(mDNS *const m)
{
- ResourceRecord *rr;
- mDNSu8 *newptr;
- int numDereg = 0;
- int numAnnounce = 0;
- int numAnswer = 0;
- mDNSs32 minExistingAnnounceInterval = 0;
+ int pktcount = 0;
+ AuthRecord *rr, *r2;
+ mDNSs32 maxExistingAnnounceInterval = 0;
+ const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces);
- if (m->CurrentRecord) debugf("BuildResponse ERROR m->CurrentRecord already set");
- m->CurrentRecord = m->ResourceRecords;
+ m->NextScheduledResponse = m->timenow + 0x78000000;
+
+ // ***
+ // *** 1. Setup: Set the SendRNow and ImmedAnswer fields to indicate which interface(s) the records need to be sent on
+ // ***
- // If we're sleeping, only send deregistrations
- if (m->SleepState)
+ // Run through our list of records, and decide which ones we're going to announce on all interfaces
+ for (rr = m->ResourceRecords; rr; rr=rr->next)
{
- while (m->CurrentRecord)
+ if (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0)
{
- ResourceRecord *rr = m->CurrentRecord;
- m->CurrentRecord = rr->next;
- if (rr->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger &&
- rr->RecordType == kDNSRecordTypeShared && rr->rrremainingttl == 0 &&
- (newptr = putResourceRecord(response, responseptr, &response->h.numAnswers, rr, mDNSNULL, 0)))
+ if (++rr->UpdateCredits >= kMaxUpdateCredits) rr->NextUpdateCredit = 0;
+ else rr->NextUpdateCredit = (m->timenow + mDNSPlatformOneSecond * 60) | 1;
+ }
+ if (TimeToAnnounceThisRecord(rr, m->timenow) && ResourceRecordIsValidAnswer(rr))
+ {
+ rr->ImmedAnswer = mDNSInterfaceMark; // Send on all interfaces
+ if (maxExistingAnnounceInterval < rr->ThisAPInterval)
+ maxExistingAnnounceInterval = rr->ThisAPInterval;
+ if (rr->UpdateBlocked) rr->UpdateBlocked = 0;
+ }
+ }
+
+ // Any interface-specific records we're going to send are marked as being sent on all appropriate interfaces (which is just one)
+ // Eligible records that are more than half-way to their announcement time are accelerated
+ for (rr = m->ResourceRecords; rr; rr=rr->next)
+ if ((rr->resrec.InterfaceID && rr->ImmedAnswer) ||
+ (rr->ThisAPInterval <= maxExistingAnnounceInterval &&
+ TimeToAnnounceThisRecord(rr, m->timenow + rr->ThisAPInterval/2) &&
+ ResourceRecordIsValidAnswer(rr)))
+ rr->ImmedAnswer = mDNSInterfaceMark; // Send on all interfaces
+
+ // When sending SRV records (particularly when announcing a new service) automatically add the related Address record(s)
+ for (rr = m->ResourceRecords; rr; rr=rr->next)
+ if (rr->ImmedAnswer && rr->resrec.rrtype == kDNSType_SRV)
+ for (r2=m->ResourceRecords; r2; r2=r2->next) // Scan list of resource records
+ if (RRIsAddressType(r2) && // For all address records (A/AAAA) ...
+ ResourceRecordIsValidAnswer(r2) && // ... which are valid for answer ...
+ rr->LastMCTime - r2->LastMCTime >= 0 && // ... which we have not sent recently ...
+ rr->resrec.rdnamehash == r2->resrec.namehash && // ... whose name is the name of the SRV target
+ SameDomainName(&rr->resrec.rdata->u.srv.target, &r2->resrec.name) &&
+ (rr->ImmedAnswer == mDNSInterfaceMark || rr->ImmedAnswer == r2->resrec.InterfaceID))
+ r2->ImmedAnswer = mDNSInterfaceMark; // ... then mark this address record for sending too
+
+ // If there's a record which is supposed to be unique that we're going to send, then make sure that we give
+ // the whole RRSet as an atomic unit. That means that if we have any other records with the same name/type/class
+ // then we need to mark them for sending too. Otherwise, if we set the kDNSClass_UniqueRRSet bit on a
+ // record, then other RRSet members that have not been sent recently will get flushed out of client caches.
+ // -- If a record is marked to be sent on a certain interface, make sure the whole set is marked to be sent on that interface
+ // -- If any record is marked to be sent on all interfaces, make sure the whole set is marked to be sent on all interfaces
+ for (rr = m->ResourceRecords; rr; rr=rr->next)
+ if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
+ {
+ if (rr->ImmedAnswer) // If we're sending this as answer, see that its whole RRSet is similarly marked
+ {
+ for (r2 = m->ResourceRecords; r2; r2=r2->next)
+ if (ResourceRecordIsValidAnswer(r2))
+ if (r2->ImmedAnswer != mDNSInterfaceMark && r2->ImmedAnswer != rr->ImmedAnswer && SameResourceRecordSignature(&r2->resrec, &rr->resrec))
+ r2->ImmedAnswer = rr->ImmedAnswer;
+ }
+ else if (rr->ImmedAdditional) // If we're sending this as additional, see that its whole RRSet is similarly marked
{
- numDereg++;
- responseptr = newptr;
- rr->rrremainingttl = rr->rroriginalttl;
+ for (r2 = m->ResourceRecords; r2; r2=r2->next)
+ if (ResourceRecordIsValidAnswer(r2))
+ if (r2->ImmedAdditional != rr->ImmedAdditional && SameResourceRecordSignature(&r2->resrec, &rr->resrec))
+ r2->ImmedAdditional = rr->ImmedAdditional;
}
}
- }
- else
+
+ // Now set SendRNow state appropriately
+ for (rr = m->ResourceRecords; rr; rr=rr->next)
{
- // 1. Look for deregistrations we need to send
- while (m->CurrentRecord)
+ if (rr->ImmedAnswer == mDNSInterfaceMark) // Sending this record on all appropriate interfaces
{
- ResourceRecord *rr = m->CurrentRecord;
- m->CurrentRecord = rr->next;
- if (rr->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger)
+ rr->SendRNow = !intf ? mDNSNULL : (rr->resrec.InterfaceID) ? rr->resrec.InterfaceID : intf->InterfaceID;
+ rr->ImmedAdditional = mDNSNULL; // No need to send as additional if sending as answer
+ rr->LastMCTime = m->timenow;
+ rr->LastMCInterface = rr->ImmedAnswer;
+ // If we're announcing this record, and it's at least half-way to its ordained time, then consider this announcement done
+ if (TimeToAnnounceThisRecord(rr, m->timenow + rr->ThisAPInterval/2))
{
- if (rr->NewRData) // If we have new data for this record
- {
- RData *OldRData = rr->rdata;
- if (ResourceRecordIsValidAnswer(rr)) // First see if we have to de-register the old data
- {
- rr->rrremainingttl = 0; // Clear rroriginalttl before putting record
- newptr = putResourceRecord(response, responseptr, &response->h.numAnswers, rr, mDNSNULL, 0);
- if (newptr)
- {
- numDereg++;
- responseptr = newptr;
- }
- rr->rrremainingttl = rr->rroriginalttl; // Now restore rroriginalttl
- }
- rr->rdata = rr->NewRData; // Update our rdata
- rr->NewRData = mDNSNULL; // Clear the NewRData pointer ...
- if (rr->UpdateCallback) rr->UpdateCallback(m, rr, OldRData); // ... and let the client know
- }
- if (rr->RecordType == kDNSRecordTypeDeregistering &&
- (newptr = putResourceRecord(response, responseptr, &response->h.numAnswers, rr, mDNSNULL, 0)))
- {
- numDereg++;
- responseptr = newptr;
- rr->RecordType = kDNSRecordTypeShared;
- rr->AnnounceCount = DefaultAnnounceCountForTypeShared+1;
- mDNS_Deregister_internal(m, rr, timenow, mDNS_Dereg_normal);
- }
+ rr->AnnounceCount--;
+ rr->ThisAPInterval *= 2;
+ rr->LastAPTime = m->timenow;
+ if (rr->LastAPTime + rr->ThisAPInterval - rr->AnnounceUntil >= 0) rr->AnnounceCount = 0;
+ debugf("Announcing %##s (%s) %d", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), rr->AnnounceCount);
}
}
-
- // 2. Look for announcements we are due to send in the next second
- for (rr = m->ResourceRecords; rr; rr=rr->next)
+ else if (rr->ImmedAnswer) // Else, just respond to a single query on single interface:
{
- if (rr->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger &&
- rr->AnnounceCount && ResourceRecordIsValidAnswer(rr) &&
- timenow + mDNSPlatformOneSecond - rr->NextSendTime >= 0)
- {
- newptr = putResourceRecord(response, responseptr, &response->h.numAnswers, rr, m, timenow);
- if (newptr)
- {
- numAnnounce++;
- responseptr = newptr;
- }
- // If we were able to put the record, then update the state variables
- // If we were unable to put the record because it is too large to fit, even though
- // there are no other answers in the packet, then pretend we succeeded anyway,
- // or we'll end up in an infinite loop trying to send a record that will never fit
- if (response->h.numAnswers == 0) debugf("BuildResponse announcements failed");
- if (newptr || response->h.numAnswers == 0)
- {
- if (minExistingAnnounceInterval < rr->NextSendInterval)
- minExistingAnnounceInterval = rr->NextSendInterval;
- rr->SendPriority = 0;
- rr->Requester = zeroIPAddr;
- rr->AnnounceCount--;
- rr->NextSendTime += rr->NextSendInterval;
- if (rr->NextSendTime - (timenow + rr->NextSendInterval/2) < 0)
- rr->NextSendTime = (timenow + rr->NextSendInterval/2);
- rr->NextSendInterval *= 2;
- }
- }
+ rr->SendRNow = rr->ImmedAnswer; // Just respond on that interface
+ rr->ImmedAdditional = mDNSNULL; // No need to send as additional too
+ rr->LastMCTime = m->timenow;
+ rr->LastMCInterface = rr->ImmedAnswer;
}
+ SetNextAnnounceProbeTime(m, rr);
+ }
+
+ // ***
+ // *** 2. Loop through interface list, sending records as appropriate
+ // ***
+
+ while (intf)
+ {
+ int numDereg = 0;
+ int numAnnounce = 0;
+ int numAnswer = 0;
+ DNSMessage response;
+ mDNSu8 *responseptr = response.data;
+ mDNSu8 *newptr;
+ InitializeDNSMessage(&response.h, zeroID, ResponseFlags);
- // 2a. Look for additional announcements that are worth accelerating
- // They must be (a) at least half-way to their next announcement and
- // (b) at an interval equal or less than any of the ones we've already put in
+ // First Pass. Look for:
+ // 1. Deregistering records that need to send their goodbye packet
+ // 2. Updated records that need to retract their old data
+ // 3. Answers and announcements we need to send
+ // In all cases, if we fail, and we've put at least one answer, we break out of the for loop so we can
+ // send this packet and then try again.
+ // If we have not put even one answer, then we don't bail out. We pretend we succeeded anyway,
+ // because otherwise we'll end up in an infinite loop trying to send a record that will never fit.
for (rr = m->ResourceRecords; rr; rr=rr->next)
- {
- if (rr->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger &&
- rr->AnnounceCount && ResourceRecordIsValidAnswer(rr) &&
- timenow - (rr->LastSendTime + rr->NextSendInterval/4) >= 0 &&
- rr->NextSendInterval <= minExistingAnnounceInterval)
+ if (rr->SendRNow == intf->InterfaceID)
+ {
+ if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
+ {
+ newptr = PutResourceRecordTTL(&response, responseptr, &response.h.numAnswers, &rr->resrec, 0);
+ if (!newptr && response.h.numAnswers) break;
+ numDereg++;
+ responseptr = newptr;
+ }
+ else if (rr->NewRData) // If we have new data for this record
{
- newptr = putResourceRecord(response, responseptr, &response->h.numAnswers, rr, m, timenow);
- if (newptr)
+ RData *OldRData = rr->resrec.rdata;
+ mDNSu16 oldrdlength = rr->resrec.rdlength;
+ // See if we should send a courtesy "goodbye" 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)
{
- numAnnounce++;
+ newptr = PutResourceRecordTTL(&response, responseptr, &response.h.numAnswers, &rr->resrec, 0);
+ if (!newptr && response.h.numAnswers) break;
+ numDereg++;
responseptr = newptr;
}
- // If we were able to put the record, then update the state variables
- // If we were unable to put the record because it is too large to fit, even though
- // there are no other answers in the packet, then pretend we succeeded anyway,
- // or we'll end up in an infinite loop trying to send a record that will never fit
- if (response->h.numAnswers == 0) debugf("BuildResponse announcements failed");
- if (newptr || response->h.numAnswers == 0)
+ // 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);
+ newptr = PutResourceRecord(&response, responseptr, &response.h.numAnswers, &rr->resrec);
+ if (newptr) responseptr = newptr;
+ SetNewRData(&rr->resrec, OldRData, oldrdlength);
+ }
+ else
+ {
+ // If this record is supposed to be unique, see if we've sent its whole set
+ if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
{
- rr->SendPriority = 0;
- rr->Requester = zeroIPAddr;
- rr->AnnounceCount--;
- rr->NextSendTime = timenow + rr->NextSendInterval;
- rr->NextSendInterval *= 2;
+ // 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 == intf->InterfaceID && a != rr && 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
}
+ newptr = PutResourceRecordTTL(&response, responseptr, &response.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 (rr->LastAPTime == m->timenow) numAnnounce++; else numAnswer++;
+ responseptr = newptr;
}
- }
+ // If sending on all interfaces, go to next interface; else we're finished now
+ if (rr->ImmedAnswer == mDNSInterfaceMark && rr->resrec.InterfaceID == mDNSInterface_Any)
+ rr->SendRNow = GetNextActiveInterfaceID(intf);
+ else
+ rr->SendRNow = mDNSNULL;
+ }
- // 3. Look for answers we need to send
+ // Second Pass. Add additional records, if there's space.
+ newptr = responseptr;
for (rr = m->ResourceRecords; rr; rr=rr->next)
- if (rr->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger &&
- rr->SendPriority >= kDNSSendPriorityAnswer && ResourceRecordIsValidAnswer(rr))
+ 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))
{
- newptr = putResourceRecord(response, responseptr, &response->h.numAnswers, rr, m, timenow);
- if (newptr)
- {
- numAnswer++;
- responseptr = newptr;
- }
- // If we were able to put the record, then update the state variables
- // If we were unable to put the record because it is too large to fit, even though
- // there are no other answers in the packet then pretend we succeeded anyway,
- // or we'll end up in an infinite loop trying to send a record that will never fit
- if (response->h.numAnswers == 0) debugf("BuildResponse answers failed");
- if (newptr || response->h.numAnswers == 0)
+ if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
{
- rr->SendPriority = 0;
- rr->Requester = zeroIPAddr;
+ // 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
}
+ newptr = PutResourceRecord(&response, newptr, &response.h.numAdditionals, &rr->resrec);
+ rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state
}
-
- // 4. Add additionals, if there's space
- for (rr = m->ResourceRecords; rr; rr=rr->next)
- if (rr->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger &&
- rr->SendPriority == kDNSSendPriorityAdditional)
- {
- if (ResourceRecordIsValidAnswer(rr) &&
- (newptr = putResourceRecord(response, responseptr, &response->h.numAdditionals, rr, m, timenow)))
- responseptr = newptr;
- rr->SendPriority = 0; // Clear SendPriority anyway, even if we didn't put the additional in the packet
- rr->Requester = zeroIPAddr;
}
+ if (newptr) responseptr = newptr;
+
+ if (response.h.numAnswers > 0) // We *never* send a packet with only additionals in it
+ {
+ 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);
+ mDNSSendDNSMessage(m, &response, responseptr, intf->InterfaceID, MulticastDNSPort, &AllDNSLinkGroup_v4, MulticastDNSPort);
+ mDNSSendDNSMessage(m, &response, responseptr, intf->InterfaceID, MulticastDNSPort, &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; }
+ // 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
+ {
+ const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next);
+ #if MDNS_DEBUGMSGS && 0
+ const char *const msg = next ? "SendResponses: Nothing more on %p; moving to %p" : "SendResponses: Nothing more on %p";
+ debugf(msg, intf, next);
+ #endif
+ intf = next;
+ }
}
- if (numDereg || numAnnounce || numAnswer || response->h.numAdditionals)
- verbosedebugf("BuildResponse Built %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s",
- numDereg, numDereg == 1 ? "" : "s",
- numAnnounce, numAnnounce == 1 ? "" : "s",
- numAnswer, numAnswer == 1 ? "" : "s",
- response->h.numAdditionals, response->h.numAdditionals == 1 ? "" : "s");
+ // ***
+ // *** 3. Cleanup: Now that everything is sent, call client callback functions, and reset state variables
+ // ***
- return(responseptr);
+ if (m->CurrentRecord) LogMsg("SendResponses: ERROR m->CurrentRecord already set");
+ m->CurrentRecord = m->ResourceRecords;
+ while (m->CurrentRecord)
+ {
+ rr = m->CurrentRecord;
+ m->CurrentRecord = rr->next;
+
+ if (rr->NewRData)
+ {
+ RData *OldRData = rr->resrec.rdata;
+ SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); // Update our rdata
+ rr->NewRData = mDNSNULL; // Clear the NewRData pointer ...
+ if (rr->UpdateCallback)
+ rr->UpdateCallback(m, rr, OldRData); // ... and let the client know
+ }
+
+ if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
+ CompleteDeregistration(m, rr);
+ else
+ {
+ rr->ImmedAnswer = mDNSNULL;
+ rr->v4Requester = zeroIPAddr;
+ rr->v6Requester = zerov6Addr;
+ }
+ }
+ verbosedebugf("SendResponses: Next in %d ticks", m->NextScheduledResponse - m->timenow);
}
-mDNSlocal void SendResponses(mDNS *const m, const mDNSs32 timenow)
+// Calling CheckCacheExpiration() is an expensive operation because it has to look at the entire cache,
+// so we want to be lazy about how frequently we do it.
+// 1. If a cache record is currently referenced by *no* active questions,
+// then we don't mind expiring it up to a minute late (who will know?)
+// 2. Else, if a cache record is due for some of its final expiration queries,
+// we'll allow them to be late by up to 2% of the TTL
+// 3. Else, if a cache record has completed all its final expiration queries without success,
+// and is expiring, and had an original TTL more than ten seconds, we'll allow it to be one second late
+// 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)->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->resrec.rroriginalttl
+// rr->UnansweredQueries
+// rr->CRActiveQuestion
+mDNSlocal void SetNextCacheCheckTime(mDNS *const m, CacheRecord *const rr)
{
- DNSMessage response;
- DNSMessageHeader baseheader;
- mDNSu8 *baselimit, *responseptr;
- NetworkInterfaceInfo *intf;
- ResourceRecord *rr, *r2;
+ rr->NextRequiredQuery = RRExpireTime(rr);
- // Run through our list of records,
- // and if there's a record which is supposed to be unique that we're proposing to give as an answer,
- // then make sure that the whole RRSet with that name/type/class is also marked for answering.
- // Otherwise, if we set the kDNSClass_UniqueRRSet bit on a record, then other RRSet members
- // that have not been sent recently will get flushed out of client caches.
- for (rr = m->ResourceRecords; rr; rr=rr->next)
- if (rr->RecordType & kDNSRecordTypeUniqueMask)
- if (TimeToSendThisRecord(rr,timenow))
- for (r2 = m->ResourceRecords; r2; r2=r2->next)
- if (r2 != rr && timenow - r2->LastSendTime > mDNSPlatformOneSecond/4)
- if (SameResourceRecordSignatureAnyInterface(rr, r2))
- r2->SendPriority = kDNSSendPriorityAnswer;
+ // If we have an active question, then see if we want to schedule a refresher query for this record.
+ // Usually we expect to do four queries, at 80-82%, 85-87%, 90-92% and then 95-97% of the TTL.
+ if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries)
+ {
+ rr->NextRequiredQuery -= TicksTTL(rr)/20 * (MaxUnansweredQueries - rr->UnansweredQueries);
+ rr->NextRequiredQuery += mDNSRandom((mDNSu32)TicksTTL(rr)/50);
+ verbosedebugf("SetNextCacheCheckTime: %##s (%s) NextRequiredQuery in %ld sec",
+ rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), (rr->NextRequiredQuery - m->timenow) / mDNSPlatformOneSecond);
+ }
+
+ if (m->NextCacheCheck - (rr->NextRequiredQuery + CacheCheckGracePeriod(rr)) > 0)
+ m->NextCacheCheck = (rr->NextRequiredQuery + CacheCheckGracePeriod(rr));
+ }
- // First build the generic part of the message
- InitializeDNSMessage(&response.h, zeroID, ResponseFlags);
- baselimit = BuildResponse(m, &response, response.data, zeroIPAddr, timenow);
- baseheader = response.h;
+#define kDefaultReconfirmTimeForNoAnswer ((mDNSu32)mDNSPlatformOneSecond * 45)
+#define kDefaultReconfirmTimeForCableDisconnect ((mDNSu32)mDNSPlatformOneSecond * 5)
+#define kMinimumReconfirmTime ((mDNSu32)mDNSPlatformOneSecond * 5)
- for (intf = m->HostInterfaces; intf; intf = intf->next)
+mDNSlocal mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, mDNSu32 interval)
+ {
+ if (interval < kMinimumReconfirmTime)
+ interval = kMinimumReconfirmTime;
+ if (interval > 0x10000000) // Make sure interval doesn't overflow when we multiply by four below
+ interval = 0x10000000;
+
+ // If the expected expiration time for this record is more than interval+33%, then accelerate its expiration
+ if (RRExpireTime(rr) - m->timenow > (mDNSs32)((interval * 4) / 3))
{
- // Restore the header to the counts for the generic records
- response.h = baseheader;
- // Now add any records specific to this interface
- responseptr = BuildResponse(m, &response, baselimit, intf->ip, timenow);
- if (response.h.numAnswers > 0) // We *never* send a packet with only additionals in it
- {
- mDNSSendDNSMessage(m, &response, responseptr, intf->ip, MulticastDNSPort, AllDNSLinkGroup, MulticastDNSPort);
- debugf("SendResponses Sent %d Answer%s, %d Additional%s on %.4a",
- response.h.numAnswers, response.h.numAnswers == 1 ? "" : "s",
- response.h.numAdditionals, response.h.numAdditionals == 1 ? "" : "s", &intf->ip);
- }
+ // Add a 33% random amount to the interval, to avoid synchronization between multiple hosts
+ interval += mDNSRandom(interval/3);
+ rr->TimeRcvd = m->timenow - (mDNSs32)interval * 3;
+ 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));
+ return(mStatus_NoError);
}
-#define TimeToSendThisQuestion(Q,time) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf && (time) - (Q)->NextQTime >= 0)
+#define MaxQuestionInterval (3600 * mDNSPlatformOneSecond)
-mDNSlocal mDNSBool HaveQueries(const mDNS *const m, const mDNSs32 timenow)
+// BuildQuestion puts a question into a DNS Query packet and if successful, updates the value of queryptr.
+// It also appends to the list of known answer records that need to be included,
+// and updates the forcast for the size of the known answer section.
+mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **queryptr, DNSQuestion *q,
+ CacheRecord ***kalistptrptr, mDNSu32 *answerforecast)
{
- ResourceRecord *rr;
- DNSQuestion *q;
-
- // 1. See if we've got any cache records in danger of expiring
- for (rr = m->rrcache; rr; rr=rr->next)
- if (rr->UnansweredQueries < 2)
- {
- mDNSs32 onetenth = ((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond) / 10;
- mDNSs32 t0 = rr->TimeRcvd + (mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond;
- mDNSs32 t1 = t0 - onetenth;
- mDNSs32 t2 = t1 - onetenth;
+ mDNSBool ucast = q->LargeAnswers || q->ThisQInterval <= InitialQuestionInterval*2;
+ 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));
+ if (!newptr)
+ {
+ debugf("BuildQuestion: No more space in this packet for question %##s", q->qname.c);
+ return(mDNSfalse);
+ }
+ else if (newptr + *answerforecast >= limit)
+ {
+ verbosedebugf("BuildQuestion: Retracting question %##s new forecast total %d", q->qname.c, newptr + *answerforecast - query->data);
+ query->h.numQuestions--;
+ return(mDNSfalse);
+ }
+ else
+ {
+ mDNSu32 forecast = *answerforecast;
+ CacheRecord *rr;
+ CacheRecord **ka = *kalistptrptr; // Make a working copy of the pointer we're going to update
+
+ for (rr=m->rrcache_hash[HashSlot(&q->qname)]; rr; rr=rr->next) // If we have a resource record in our cache,
+ if (rr->resrec.InterfaceID == q->SendQNow && // received on this interface
+ rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList && // which is not already in the known answer list
+ rr->resrec.rdlength <= SmallRecordLimit && // which is small enough to sensibly fit in the packet
+ ResourceRecordAnswersQuestion(&rr->resrec, q) && // which answers our question
+ rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow >= 0 && // and it is less than half-way to expiry
+ rr->NextRequiredQuery - (m->timenow + q->ThisQInterval) > 0)// and we'll ask at least once again before NextRequiredQuery
+ {
+ *ka = rr; // Link this record into our known answer chain
+ ka = &rr->NextInKAList;
+ // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
+ forecast += 12 + rr->resrec.rdestimate;
+ // If we're trying to put more than one question in this packet, and it doesn't fit
+ // then undo that last question and try again next time
+ if (query->h.numQuestions > 1 && newptr + forecast >= limit)
+ {
+ debugf("BuildQuestion: Retracting question %##s (%s) new forecast total %d",
+ q->qname.c, DNSTypeName(q->qtype), newptr + forecast - query->data);
+ query->h.numQuestions--;
+ ka = *kalistptrptr; // Go back to where we started and retract these answer records
+ while (*ka) { CacheRecord *rr = *ka; *ka = mDNSNULL; ka = &rr->NextInKAList; }
+ return(mDNSfalse); // Return false, so we'll try again in the next packet
+ }
+ }
- if (timenow - t1 >= 0 || (rr->UnansweredQueries < 1 && timenow - t2 >= 0))
+ // Traffic reduction:
+ // If we already have at least one unique answer in the cache,
+ // OR we have so many shared answers that the KA list is too big to fit in one packet
+ // The we suppress queries number 3 and 5:
+ // Query 1 (immediately; ThisQInterval = 1 sec; request unicast replies)
+ // Query 2 (after 1 second; ThisQInterval = 2 sec; send normally)
+ // Query 3 (after 2 seconds; ThisQInterval = 4 sec; may suppress)
+ // Query 4 (after 4 seconds; ThisQInterval = 8 sec; send normally)
+ // Query 5 (after 8 seconds; ThisQInterval = 16 sec; may suppress)
+ // Query 6 (after 16 seconds; ThisQInterval = 32 sec; send normally)
+ if (q->UniqueAnswers || newptr + forecast >= limit)
+ if (q->ThisQInterval == InitialQuestionInterval * 8 || q->ThisQInterval == InitialQuestionInterval * 32)
{
- DNSQuestion *q = CacheRRActive(m, rr);
- if (q) q->NextQTime = timenow;
+ query->h.numQuestions--;
+ ka = *kalistptrptr; // Go back to where we started and retract these answer records
+ while (*ka) { CacheRecord *rr = *ka; *ka = mDNSNULL; ka = &rr->NextInKAList; }
+ return(mDNStrue); // Return true: pretend we succeeded, even though we actually suppressed this question
}
- }
-
- // 2. Scan our list of questions to see if it's time to send any of them
- for (q = m->ActiveQuestions; q; q=q->next)
- if (TimeToSendThisQuestion(q, timenow))
- return(mDNStrue);
- // 3. Scan our list of Resource Records to see if we need to send any probe questions
- for (rr = m->ResourceRecords; rr; rr=rr->next) // Scan our list of records
- if (rr->RecordType == kDNSRecordTypeUnique && timenow - rr->NextSendTime >= 0)
- return(mDNStrue);
+ // Success! Update our state pointers, increment UnansweredQueries as appropriate, and return
+ *queryptr = newptr; // Update the packet pointer
+ *answerforecast = forecast; // Update the forecast
+ *kalistptrptr = ka; // Update the known answer list pointer
+ if (ucast) m->ExpectUnicastResponse = m->timenow;
- return(mDNSfalse);
+ for (rr=m->rrcache_hash[HashSlot(&q->qname)]; rr; rr=rr->next) // For every resource record in our cache,
+ if (rr->resrec.InterfaceID == q->SendQNow && // received on this interface
+ rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList && // which is not in the known answer list
+ ResourceRecordAnswersQuestion(&rr->resrec, q)) // which answers our question
+ {
+ rr->UnansweredQueries++; // indicate that we're expecting a response
+ rr->LastUnansweredTime = m->timenow;
+ SetNextCacheCheckTime(m, rr);
+ }
+
+ return(mDNStrue);
+ }
}
-// BuildProbe puts a probe question into a DNS Query packet and if successful, updates the value of queryptr.
-// It also sets the record's IncludeInProbe flag so that we know to add an Update Record too
-// and updates the forcast for the size of the duplicate suppression (answer) section.
-// NOTE: BuildProbe can call a user callback, which may change the record list and/or question list.
-// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
-mDNSlocal void BuildProbe(mDNS *const m, DNSMessage *query, mDNSu8 **queryptr,
- ResourceRecord *rr, mDNSu32 *answerforecast, const mDNSs32 timenow)
+mDNSlocal void ReconfirmAntecedents(mDNS *const m, DNSQuestion *q)
{
- if (rr->ProbeCount == 0)
- {
- rr->RecordType = kDNSRecordTypeVerified;
- rr->AnnounceCount = DefaultAnnounceCountForRecordType(rr->RecordType);
- debugf("Probing for %##s (%s) complete", rr->name.c, DNSTypeName(rr->rrtype));
- if (!rr->Acknowledged && rr->Callback)
- { rr->Acknowledged = mDNStrue; rr->Callback(m, rr, mStatus_NoError); }
- }
- else
- {
- const mDNSu8 *const limit = query->data + ((query->h.numQuestions) ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData);
- mDNSu8 *newptr = putQuestion(query, *queryptr, limit, &rr->name, kDNSQType_ANY, rr->rrclass);
- // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
- mDNSu32 forecast = *answerforecast + 12 + rr->rdestimate;
- if (newptr && newptr + forecast < limit)
- {
- *queryptr = newptr;
- *answerforecast = forecast;
- rr->ProbeCount--; // Only decrement ProbeCount if we successfully added the record to the packet
- rr->IncludeInProbe = mDNStrue;
- rr->NextSendTime = timenow + rr->NextSendInterval;
- }
- else
+ mDNSu32 slot;
+ CacheRecord *rr;
+ domainname *target;
+ for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
+ for (rr = m->rrcache_hash[slot]; rr; rr=rr->next)
+ if ((target = GetRRDomainNameTarget(&rr->resrec)) && rr->resrec.rdnamehash == q->qnamehash && SameDomainName(target, &q->qname))
+ mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer);
+ }
+
+// Only DupSuppressInfos newer than the specified 'time' are allowed to remain active
+mDNSlocal void ExpireDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 time)
+ {
+ int i;
+ for (i=0; i<DupSuppressInfoSize; i++) if (ds[i].Time - time < 0) ds[i].InterfaceID = mDNSNULL;
+ }
+
+mDNSlocal void ExpireDupSuppressInfoOnInterface(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 time, mDNSInterfaceID InterfaceID)
+ {
+ int i;
+ for (i=0; i<DupSuppressInfoSize; i++) if (ds[i].InterfaceID == InterfaceID && ds[i].Time - time < 0) ds[i].InterfaceID = mDNSNULL;
+ }
+
+mDNSlocal mDNSBool SuppressOnThisInterface(const DupSuppressInfo ds[DupSuppressInfoSize], const NetworkInterfaceInfo * const intf)
+ {
+ int i;
+ mDNSBool v4 = !intf->IPv4Available; // If this interface doesn't do v4, we don't need to find a v4 duplicate of this query
+ mDNSBool v6 = !intf->IPv6Available; // If this interface doesn't do v6, we don't need to find a v6 duplicate of this query
+ for (i=0; i<DupSuppressInfoSize; i++)
+ if (ds[i].InterfaceID == intf->InterfaceID)
{
- debugf("BuildProbe retracting Question %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype));
- query->h.numQuestions--;
+ if (ds[i].Type == mDNSAddrType_IPv4) v4 = mDNStrue;
+ else if (ds[i].Type == mDNSAddrType_IPv6) v6 = mDNStrue;
+ if (v4 && v6) return(mDNStrue);
}
+ return(mDNSfalse);
+ }
+
+mDNSlocal int RecordDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 Time, mDNSInterfaceID InterfaceID, mDNSs32 Type)
+ {
+ int i, j;
+
+ // See if we have this one in our list somewhere already
+ for (i=0; i<DupSuppressInfoSize; i++) if (ds[i].InterfaceID == InterfaceID && ds[i].Type == Type) break;
+
+ // If not, find a slot we can re-use
+ if (i >= DupSuppressInfoSize)
+ {
+ i = 0;
+ for (j=1; j<DupSuppressInfoSize && ds[i].InterfaceID; j++)
+ if (!ds[j].InterfaceID || ds[j].Time - ds[i].Time < 0)
+ i = j;
}
+
+ // Record the info about this query we saw
+ ds[i].Time = Time;
+ ds[i].InterfaceID = InterfaceID;
+ ds[i].Type = Type;
+
+ return(i);
}
-#define MaxQuestionInterval (3600 * mDNSPlatformOneSecond)
-#define GetNextQInterval(X) (((X)*2) <= MaxQuestionInterval ? ((X)*2) : MaxQuestionInterval)
-#define GetNextSendTime(T,EARLIEST) (((T) - (EARLIEST) >= 0) ? (T) : (EARLIEST) )
-
-// BuildQuestion puts a question into a DNS Query packet and if successful, updates the value of queryptr.
-// It also appends to the list of duplicate suppression records that need to be included,
-// and updates the forcast for the size of the duplicate suppression (answer) section.
-mDNSlocal void BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **queryptr, DNSQuestion *q,
- ResourceRecord ***dups_ptr, mDNSu32 *answerforecast, const mDNSs32 timenow)
+mDNSlocal mDNSBool AccelerateThisQuery(mDNS *const m, DNSQuestion *q)
{
- const mDNSu8 *const limit = query->data + (query->h.numQuestions ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData);
- mDNSu8 *newptr = putQuestion(query, *queryptr, limit, &q->name, q->rrtype, q->rrclass);
- if (!newptr)
- debugf("BuildQuestion: No more space for queries");
- else
+ // If more than 90% of the way to the query time, we should unconditionally accelerate it
+ if (TimeToSendThisQuestion(q, m->timenow + q->ThisQInterval/10))
+ return(mDNStrue);
+
+ // If half-way to next scheduled query time, only accelerate if it will add less than 512 bytes to the packet
+ if (TimeToSendThisQuestion(q, m->timenow + q->ThisQInterval/2))
{
- mDNSu32 forecast = *answerforecast;
- ResourceRecord *rr;
- ResourceRecord **d = *dups_ptr;
- mDNSs32 nst = timenow + q->NextQInterval;
-
- // If we have a resource record in our cache,
- // which is not already in the duplicate suppression list
- // which answers our question,
- // then add it to the duplicate suppression list
- for (rr=m->rrcache; rr; rr=rr->next)
- if (rr->NextDupSuppress == mDNSNULL && d != &rr->NextDupSuppress &&
- ResourceRecordAnswersQuestion(rr, q))
+ // We forecast: qname (n) type (2) class (2)
+ mDNSu32 forecast = DomainNameLength(&q->qname) + 4;
+ CacheRecord *rr;
+ for (rr=m->rrcache_hash[HashSlot(&q->qname)]; rr; rr=rr->next) // If we have a resource record in our cache,
+ if (rr->resrec.rdlength <= SmallRecordLimit && // which is small enough to sensibly fit in the packet
+ ResourceRecordAnswersQuestion(&rr->resrec, q) && // which answers our question
+ rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow >= 0 && // and it is less than half-way to expiry
+ rr->NextRequiredQuery - (m->timenow + q->ThisQInterval) > 0)// and we'll ask at least once again before NextRequiredQuery
{
- // Work out the latest time we should ask about this record to refresh it before it expires
- mDNSs32 onetenth = ((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond) / 10;
- mDNSs32 t0 = rr->TimeRcvd + (mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond;
- mDNSs32 t3 = t0 - onetenth*3;
-
- // If we'll ask again at least twice before it expires, okay to suppress it this time
- if (t3 - nst >= 0)
- {
- *d = rr; // Link this record into our duplicate suppression chain
- d = &rr->NextDupSuppress;
- // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
- forecast += 12 + rr->rdestimate;
- }
- else
- rr->UnansweredQueries++;
+ // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
+ forecast += 12 + rr->resrec.rdestimate;
+ if (forecast >= 512) return(mDNSfalse); // If this would add 512 bytes or more to the packet, don't accelerate
}
-
- // If we're trying to put more than one question in this packet, and it doesn't fit
- // then undo that last question and try again next time
- if (query->h.numQuestions > 1 && newptr + forecast >= limit)
- {
- debugf("BuildQuestion retracting question %##s answerforecast %d", q->name.c, *answerforecast);
- query->h.numQuestions--;
- d = *dups_ptr; // Go back to where we started and retract these answer records
- while (*d) { ResourceRecord *rr = *d; *d = mDNSNULL; d = &rr->NextDupSuppress; }
- }
- else
- {
- *queryptr = newptr; // Update the packet pointer
- *answerforecast = forecast; // Update the forecast
- *dups_ptr = d; // Update the dup suppression pointer
- q->NextQTime = nst;
- q->ThisQInterval = q->NextQInterval;
- q->NextQInterval = GetNextQInterval(q->ThisQInterval);
- }
+ return(mDNStrue);
}
+
+ return(mDNSfalse);
}
// How Standard Queries are generated:
// 1. The Question Section contains the question
-// 2. The Additional Section contains answers we already know, to suppress duplicate replies
+// 2. The Additional Section contains answers we already know, to suppress duplicate responses
// How Probe Queries are generated:
// 1. The Question Section contains queries for the name we intend to use, with QType=ANY because
// because if some other host is probing at the same time, we each want to know what the other is
// planning, in order to apply the tie-breaking rule to see who gets to use the name and who doesn't.
-mDNSlocal mDNSu8 *BuildQueryPacketQuestions(mDNS *const m, DNSMessage *query, mDNSu8 *queryptr,
- ResourceRecord ***dups_ptr, mDNSu32 *answerforecast,
- const mDNSIPAddr InterfaceAddr, const mDNSs32 timenow)
+mDNSlocal void SendQueries(mDNS *const m)
{
+ int pktcount = 0;
DNSQuestion *q;
+ // For explanation of maxExistingQuestionInterval logic, see comments for maxExistingAnnounceInterval
+ mDNSs32 maxExistingQuestionInterval = 0;
+ const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces);
+ CacheRecord *KnownAnswerList = mDNSNULL;
+
+ // 1. If time for a query, work out what we need to do
+ if (m->timenow - m->NextScheduledQuery >= 0)
+ {
+ mDNSu32 slot;
+ CacheRecord *rr;
+ m->NextScheduledQuery = m->timenow + 0x78000000;
+
+ // We're expecting to send a query anyway, so see if any expiring cache records are close enough
+ // to their NextRequiredQuery to be worth batching them together with this one
+ for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
+ for (rr = m->rrcache_hash[slot]; rr; rr=rr->next)
+ if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries)
+ if (m->timenow + TicksTTL(rr)/50 - rr->NextRequiredQuery >= 0)
+ {
+ q = rr->CRActiveQuestion;
+ ExpireDupSuppressInfoOnInterface(q->DupSuppress, m->timenow - TicksTTL(rr)/20, rr->resrec.InterfaceID);
+ if (q->SendQNow == mDNSNULL) q->SendQNow = rr->resrec.InterfaceID;
+ else if (q->SendQNow != rr->resrec.InterfaceID) q->SendQNow = mDNSInterfaceMark;
+ }
+
+ // Scan our list of questions to see which ones we're definitely going to send
+ for (q = m->Questions; q; q=q->next)
+ if (TimeToSendThisQuestion(q, m->timenow))
+ {
+ q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces
+ if (maxExistingQuestionInterval < q->ThisQInterval)
+ maxExistingQuestionInterval = q->ThisQInterval;
+ }
- // See which questions need to go out right now
- for (q = m->ActiveQuestions; q; q=q->next)
- if (q->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger &&
- TimeToSendThisQuestion(q, timenow))
- BuildQuestion(m, query, &queryptr, q, dups_ptr, answerforecast, timenow);
+ // Scan our list of questions
+ // (a) to see if there are any more that are worth accelerating, and
+ // (b) to update the state variables for all the questions we're going to send
+ for (q = m->Questions; q; q=q->next)
+ {
+ if (q->SendQNow || (ActiveQuestion(q) && q->ThisQInterval <= maxExistingQuestionInterval && AccelerateThisQuery(m,q)))
+ {
+ // If at least halfway to next query time, advance to next interval
+ // If less than halfway to next query time, treat this as logically a repeat of the last transmission, without advancing the interval
+ if (m->timenow - (q->LastQTime + q->ThisQInterval/2) >= 0)
+ {
+ q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces
+ q->ThisQInterval *= 2;
+ if (q->ThisQInterval > MaxQuestionInterval)
+ q->ThisQInterval = MaxQuestionInterval;
+ else if (q->CurrentAnswers == 0 && q->ThisQInterval == InitialQuestionInterval * 8)
+ {
+ debugf("SendQueries: Zero current answers for %##s (%s); will reconfirm antecedents", q->qname.c, DNSTypeName(q->qtype));
+ ReconfirmAntecedents(m, q); // If sending third query, and no answers yet, time to begin doubting the source
+ }
+ }
- // See which questions are more than half way to their NextSendTime, and send them too, if we have space
- for (q = m->ActiveQuestions; q; q=q->next)
- if (q->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger &&
- TimeToSendThisQuestion(q, timenow + q->ThisQInterval/2))
- BuildQuestion(m, query, &queryptr, q, dups_ptr, answerforecast, timenow);
+ // Mark for sending. (If no active interfaces, then don't even try.)
+ q->SendOnAll = (q->SendQNow == mDNSInterfaceMark);
+ if (q->SendOnAll)
+ {
+ q->SendQNow = !intf ? mDNSNULL : (q->InterfaceID) ? q->InterfaceID : intf->InterfaceID;
+ q->LastQTime = m->timenow;
+ }
- return(queryptr);
- }
+ // If we recorded a duplicate suppression for this question less than half an interval ago,
+ // 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);
-mDNSlocal mDNSu8 *BuildQueryPacketAnswers(DNSMessage *query, mDNSu8 *queryptr,
- ResourceRecord **dups_ptr, const mDNSs32 timenow)
- {
- while (*dups_ptr)
+ q->LastQTxTime = m->timenow;
+ q->RecentAnswers = 0;
+ }
+ // For all questions (not just the ones we're sending) check what the next scheduled event will be
+ SetNextQueryTime(m,q);
+ }
+ }
+
+ // 2. Scan our authoritative RR list to see what probes we might need to send
+ if (m->timenow - m->NextScheduledProbe >= 0)
{
- ResourceRecord *rr = *dups_ptr;
- mDNSu32 timesincercvd = (mDNSu32)(timenow - rr->TimeRcvd);
- mDNSu8 *newptr;
- // Need to update rrremainingttl correctly before we put this cache record in the packet
- rr->rrremainingttl = rr->rroriginalttl - timesincercvd / mDNSPlatformOneSecond;
- newptr = putResourceRecord(query, queryptr, &query->h.numAnswers, rr, mDNSNULL, 0);
- if (newptr)
+ m->NextScheduledProbe = m->timenow + 0x78000000;
+
+ if (m->CurrentRecord) LogMsg("SendQueries: ERROR m->CurrentRecord already set");
+ m->CurrentRecord = m->ResourceRecords;
+ while (m->CurrentRecord)
{
- *dups_ptr = rr->NextDupSuppress;
- rr->NextDupSuppress = mDNSNULL;
- queryptr = newptr;
+ AuthRecord *rr = m->CurrentRecord;
+ m->CurrentRecord = rr->next;
+ if (rr->resrec.RecordType == kDNSRecordTypeUnique) // For all records that are still probing...
+ {
+ // 1. If it's not reached its probe time, just make sure we update m->NextScheduledProbe correctly
+ if (m->timenow - (rr->LastAPTime + rr->ThisAPInterval) < 0)
+ {
+ SetNextAnnounceProbeTime(m, rr);
+ }
+ // 2. else, if it has reached its probe time, mark it for sending and then update m->NextScheduledProbe correctly
+ else if (rr->ProbeCount)
+ {
+ // Mark for sending. (If no active interfaces, then don't even try.)
+ rr->SendRNow = !intf ? mDNSNULL : (rr->resrec.InterfaceID) ? rr->resrec.InterfaceID : intf->InterfaceID;
+ rr->LastAPTime = m->timenow;
+ rr->ProbeCount--;
+ SetNextAnnounceProbeTime(m, rr);
+ }
+ // else, if it has now finished probing, move it to state Verified, and update m->NextScheduledResponse so it will be announced
+ else
+ {
+ AuthRecord *r2;
+ rr->resrec.RecordType = kDNSRecordTypeVerified;
+ rr->ThisAPInterval = DefaultAnnounceIntervalForTypeUnique;
+ rr->LastAPTime = m->timenow - DefaultAnnounceIntervalForTypeUnique;
+ SetNextAnnounceProbeTime(m, rr);
+ // If we have any records on our duplicate list that match this one, they have now also completed probing
+ for (r2 = m->DuplicateRecords; r2; r2=r2->next)
+ if (r2->resrec.RecordType == kDNSRecordTypeUnique && RecordIsLocalDuplicate(r2, rr))
+ r2->ProbeCount = 0;
+ CompleteProbing(m, rr);
+ }
+ }
}
- else
+ m->CurrentRecord = m->DuplicateRecords;
+ while (m->CurrentRecord)
{
- debugf("BuildQueryPacketAnswers: Put %d answers; No more space for duplicate suppression",
- query->h.numAnswers);
- query->h.flags.b[0] |= kDNSFlag0_TC;
- break;
+ AuthRecord *rr = m->CurrentRecord;
+ m->CurrentRecord = rr->next;
+ if (rr->resrec.RecordType == kDNSRecordTypeUnique && rr->ProbeCount == 0)
+ CompleteProbing(m, rr);
}
}
- return(queryptr);
- }
-mDNSlocal mDNSu8 *BuildQueryPacketProbes(mDNS *const m, DNSMessage *query, mDNSu8 *queryptr,
- mDNSu32 *answerforecast, const mDNSIPAddr InterfaceAddr, const mDNSs32 timenow)
- {
- if (m->CurrentRecord) debugf("BuildQueryPacketProbes ERROR m->CurrentRecord already set");
- m->CurrentRecord = m->ResourceRecords;
- while (m->CurrentRecord)
+ // 3. Now we know which queries and probes we're sending, go through our interface list sending the appropriate queries on each interface
+ while (intf)
{
- ResourceRecord *rr = m->CurrentRecord;
- m->CurrentRecord = rr->next;
- if (rr->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger &&
- rr->RecordType == kDNSRecordTypeUnique && timenow - rr->NextSendTime >= 0)
- BuildProbe(m, query, &queryptr, rr, answerforecast, timenow);
- }
- return(queryptr);
- }
+ AuthRecord *rr;
+ DNSMessage query;
+ mDNSu8 *queryptr = query.data;
+ InitializeDNSMessage(&query.h, zeroID, QueryFlags);
+ if (KnownAnswerList) verbosedebugf("SendQueries: KnownAnswerList set... Will continue from previous packet");
+ if (!KnownAnswerList)
+ {
+ // Start a new known-answer list
+ CacheRecord **kalistptr = &KnownAnswerList;
+ mDNSu32 answerforecast = 0;
+
+ // Put query questions in this packet
+ 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",
+ SuppressOnThisInterface(q->DupSuppress, intf) ? "Suppressing" : "Putting ",
+ q->qname.c, DNSTypeName(q->qtype), queryptr - query.data, queryptr + answerforecast - query.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))
+ q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf);
+ }
-mDNSlocal mDNSu8 *BuildQueryPacketUpdates(mDNS *const m, DNSMessage *query, mDNSu8 *queryptr)
- {
- ResourceRecord *rr;
- for (rr = m->ResourceRecords; rr; rr=rr->next)
- if (rr->IncludeInProbe)
+ // Put probe questions in this packet
+ for (rr = m->ResourceRecords; rr; rr=rr->next)
+ if (rr->SendRNow == intf->InterfaceID)
+ {
+ mDNSBool ucast = rr->ProbeCount >= DefaultProbeCountForTypeUnique-1;
+ 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));
+ // 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)
+ {
+ queryptr = newptr;
+ answerforecast = forecast;
+ rr->SendRNow = (rr->resrec.InterfaceID) ? mDNSNULL : GetNextActiveInterfaceID(intf);
+ rr->IncludeInProbe = mDNStrue;
+ verbosedebugf("SendQueries: Put Question %##s (%s) probecount %d", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), rr->ProbeCount);
+ }
+ else
+ {
+ verbosedebugf("SendQueries: Retracting Question %##s (%s)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype));
+ query.h.numQuestions--;
+ }
+ }
+ }
+
+ // Put our known answer list (either new one from this question or questions, or remainder of old one from last time)
+ while (KnownAnswerList)
{
- mDNSu8 *newptr = putResourceRecord(query, queryptr, &query->h.numAuthorities, rr, mDNSNULL, 0);
- rr->IncludeInProbe = mDNSfalse;
+ 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);
if (newptr)
+ {
+ verbosedebugf("SendQueries: Put %##s (%s) at %lu - %lu", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), queryptr - query.data, newptr - query.data);
queryptr = newptr;
+ KnownAnswerList = rr->NextInKAList;
+ rr->NextInKAList = mDNSNULL;
+ }
else
{
- debugf("BuildQueryPacketUpdates: How did we fail to have space for the Update record %##s (%s)?",
- rr->name.c, DNSTypeName(rr->rrtype));
+ // 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;
break;
}
}
- return(queryptr);
- }
-mDNSlocal void SendQueries(mDNS *const m, const mDNSs32 timenow)
- {
- ResourceRecord *NextDupSuppress = mDNSNULL;
- do
- {
- DNSMessage query;
- DNSMessageHeader baseheader;
- mDNSu8 *baselimit = query.data;
- NetworkInterfaceInfo *intf;
-
- // First build the generic part of the message
- InitializeDNSMessage(&query.h, zeroID, QueryFlags);
- if (!NextDupSuppress)
+ for (rr = m->ResourceRecords; rr; rr=rr->next)
+ if (rr->IncludeInProbe)
+ {
+ mDNSu8 *newptr = PutResourceRecord(&query, queryptr, &query.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)
{
- ResourceRecord **dups = &NextDupSuppress;
- mDNSu32 answerforecast = 0;
- baselimit = BuildQueryPacketQuestions(m, &query, baselimit, &dups, &answerforecast, zeroIPAddr, timenow);
- baselimit = BuildQueryPacketProbes(m, &query, baselimit, &answerforecast, zeroIPAddr, timenow);
+ 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);
+ 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);
+ mDNSSendDNSMessage(m, &query, queryptr, intf->InterfaceID, MulticastDNSPort, &AllDNSLinkGroup_v4, MulticastDNSPort);
+ mDNSSendDNSMessage(m, &query, queryptr, intf->InterfaceID, MulticastDNSPort, &AllDNSLinkGroup_v6, MulticastDNSPort);
+ if (!m->SuppressSending) m->SuppressSending = (m->timenow + mDNSPlatformOneSecond/10) | 1; // OR with one to ensure non-zero
+ 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
+ // on this interface, so go around one more time and try again.
}
- baselimit = BuildQueryPacketAnswers(&query, baselimit, &NextDupSuppress, timenow);
- baselimit = BuildQueryPacketUpdates(m, &query, baselimit);
- baseheader = query.h;
-
- if (NextDupSuppress) debugf("SendQueries: NextDupSuppress still set... Will continue in next packet");
-
- for (intf = m->HostInterfaces; intf; intf = intf->next)
+ else // Nothing more to send on this interface; go to next
{
- ResourceRecord *NextDupSuppress2 = mDNSNULL;
- do
- {
- // Restore the header to the counts for the generic records
- mDNSu8 *queryptr = baselimit;
- query.h = baseheader;
- // Now add any records specific to this interface, if we can
- if (query.h.numAnswers == 0 && query.h.numAuthorities == 0 && !NextDupSuppress)
- {
- if (!NextDupSuppress2)
- {
- ResourceRecord **dups2 = &NextDupSuppress2;
- mDNSu32 answerforecast2 = 0;
- queryptr = BuildQueryPacketQuestions(m, &query, queryptr, &dups2, &answerforecast2, intf->ip, timenow);
- queryptr = BuildQueryPacketProbes(m, &query, queryptr, &answerforecast2, intf->ip, timenow);
- }
- queryptr = BuildQueryPacketAnswers(&query, queryptr, &NextDupSuppress2, timenow);
- queryptr = BuildQueryPacketUpdates(m, &query, queryptr);
- }
-
- if (queryptr > query.data)
- {
- mDNSSendDNSMessage(m, &query, queryptr, intf->ip, MulticastDNSPort, AllDNSLinkGroup, MulticastDNSPort);
- debugf("SendQueries Sent %d Question%s %d Answer%s %d Update%s on %.4a",
- 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->ip);
- }
- } while (NextDupSuppress2);
+ const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next);
+ #if MDNS_DEBUGMSGS && 0
+ const char *const msg = next ? "SendQueries: Nothing more on %p; moving to %p" : "SendQueries: Nothing more on %p";
+ debugf(msg, intf, next);
+ #endif
+ intf = next;
}
- } while (NextDupSuppress);
+ }
}
// ***************************************************************************
-#if 0
+#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark - RR List Management & Task Management
#endif
-// rr is a new ResourceRecord just received into our cache
-// (kDNSRecordTypePacketAnswer/kDNSRecordTypePacketAdditional/kDNSRecordTypePacketUniqueAns/kDNSRecordTypePacketUniqueAdd)
-mDNSlocal void TriggerImmediateQuestions(mDNS *const m, const ResourceRecord *const rr, const mDNSs32 timenow)
- {
- // If we just received a new record off the wire that we've never seen before, we want to ask our question again
- // soon, and keep doing that repeatedly (with duplicate suppression) until we stop getting any more responses
- mDNSs32 needquery = timenow + mDNSPlatformOneSecond;
- DNSQuestion *q;
- for (q = m->ActiveQuestions; q; q=q->next) // Scan our list of questions
- if (q->ThisQInterval > 0 && !q->DuplicateOf && q->NextQTime - needquery > 0 && ResourceRecordAnswersQuestion(rr, q))
- {
- q->NextQTime = needquery;
- // As long as responses are still coming in, don't do the exponential backoff
- q->NextQInterval = q->ThisQInterval;
- }
- }
-
// NOTE: AnswerQuestionWithResourceRecord can call a user callback, which may change the record list and/or question list.
// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
-mDNSlocal void AnswerQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, ResourceRecord *rr, const mDNSs32 timenow)
+mDNSlocal void AnswerQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, CacheRecord *rr, mDNSBool AddRecord)
{
- mDNSu32 timesincercvd = (mDNSu32)(timenow - rr->TimeRcvd);
- if (rr->rroriginalttl <= timesincercvd / mDNSPlatformOneSecond) rr->rrremainingttl = 0;
- else rr->rrremainingttl = rr->rroriginalttl - timesincercvd / mDNSPlatformOneSecond;
+ verbosedebugf("AnswerQuestionWithResourceRecord:%4lu %s TTL%6lu %##s (%s)",
+ q->CurrentAnswers, AddRecord ? "Add" : "Rmv", rr->resrec.rroriginalttl, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype));
-#if DEBUGBREAKS
- if (rr->rrremainingttl)
- {
- if (rr->rrtype == kDNSType_TXT)
- debugf("AnswerQuestionWithResourceRecord Add %##s TXT %#.20s remaining ttl %d",
- rr->name.c, rr->rdata->u.txt.c, rr->rrremainingttl);
- else
- debugf("AnswerQuestionWithResourceRecord Add %##s (%s) remaining ttl %d",
- rr->name.c, DNSTypeName(rr->rrtype), rr->rrremainingttl);
- }
- else
+ rr->LastUsed = m->timenow;
+ rr->UseCount++;
+ if (ActiveQuestion(q) && rr->CRActiveQuestion != q)
{
- if (rr->rrtype == kDNSType_TXT)
- debugf("AnswerQuestionWithResourceRecord Del %##s TXT %#.20s UnansweredQueries %d",
- rr->name.c, rr->rdata->u.txt.c, rr->UnansweredQueries);
- else
- debugf("AnswerQuestionWithResourceRecord Del %##s (%s) UnansweredQueries %d",
- rr->name.c, DNSTypeName(rr->rrtype), rr->UnansweredQueries);
+ if (!rr->CRActiveQuestion) m->rrcache_active++; // If not previously active, increment rrcache_active count
+ rr->CRActiveQuestion = q; // We know q is non-null
+ SetNextCacheCheckTime(m, rr);
}
-#endif
- rr->LastUsed = timenow;
- rr->UseCount++;
- if (q->Callback) q->Callback(m, q, 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.
+ 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
}
-// AnswerLocalQuestions is called from mDNSCoreReceiveResponse,
-// and from TidyRRCache, which is called from mDNSCoreTask and from mDNSCoreReceiveResponse
-// AnswerLocalQuestions is *never* called directly as a result of a client API call
+// CacheRecordAdd is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call.
// If new questions are created as a result of invoking client callbacks, they will be added to
// the end of the question list, and m->NewQuestions will be set to indicate the first new question.
-// rr is a ResourceRecord in our cache
-// (kDNSRecordTypePacketAnswer/kDNSRecordTypePacketAdditional/kDNSRecordTypePacketUniqueAns/kDNSRecordTypePacketUniqueAdd)
-// NOTE: AnswerLocalQuestions calls AnswerQuestionWithResourceRecord which can call a user callback, which may change
-// the record list and/or question list.
+// rr is a new CacheRecord just received into our cache
+// (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique).
+// NOTE: CacheRecordAdd calls AnswerQuestionWithResourceRecord which can call a user callback,
+// which may change the record list and/or question list.
// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
-mDNSlocal void AnswerLocalQuestions(mDNS *const m, ResourceRecord *rr, const mDNSs32 timenow)
+mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr)
{
- if (m->CurrentQuestion) debugf("AnswerLocalQuestions ERROR m->CurrentQuestion already set");
- m->CurrentQuestion = m->ActiveQuestions;
+ if (m->CurrentQuestion) LogMsg("CacheRecordAdd 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, q))
- AnswerQuestionWithResourceRecord(m, q, rr, timenow);
+ if (ResourceRecordAnswersQuestion(&rr->resrec, q))
+ {
+ // If this question is one that's actively sending queries, and it's received ten answers within one second of sending the last
+ // query packet, then that indicates some radical network topology change, so reset its exponential backoff back to the start.
+ // 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)
+ {
+ 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++;
+ AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue);
+ // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
+ }
}
m->CurrentQuestion = mDNSNULL;
}
-mDNSlocal void AnswerNewQuestion(mDNS *const m, const mDNSs32 timenow)
+// CacheRecordRmv is only called from CheckCacheExpiration, which is called from mDNS_Execute
+// If new questions are created as a result of invoking client callbacks, they will be added to
+// the end of the question list, and m->NewQuestions will be set to indicate the first new question.
+// rr is an existing cache CacheRecord that just expired and is being deleted
+// (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique).
+// NOTE: CacheRecordRmv calls AnswerQuestionWithResourceRecord which can call a user callback,
+// which may change the record list and/or question list.
+// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
+mDNSlocal void CacheRecordRmv(mDNS *const m, CacheRecord *rr)
{
- ResourceRecord *rr;
- DNSQuestion *q = m->NewQuestions; // Grab the question we're going to answer
- m->NewQuestions = q->next; // Advance NewQuestions to the next (if any)
-
- if (m->lock_rrcache) debugf("AnswerNewQuestion ERROR! Cache already locked!");
- // This should be safe, because calling the client's question callback may cause the
- // question list to be modified, but should not ever cause the rrcache list to be modified.
- // If the client's question callback deletes the question, then m->CurrentQuestion will
- // be advanced, and we'll exit out of the loop
- m->lock_rrcache = 1;
- if (m->CurrentQuestion) debugf("AnswerNewQuestion ERROR m->CurrentQuestion already set");
- m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted
- for (rr=m->rrcache; rr && m->CurrentQuestion == q; rr=rr->next)
- if (ResourceRecordAnswersQuestion(rr, q))
+ if (m->CurrentQuestion) LogMsg("CacheRecordRmv 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))
{
- mDNSu32 SecsSinceRcvd = ((mDNSu32)(timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond;
- if (rr->rroriginalttl <= SecsSinceRcvd) rr->rrremainingttl = 0;
- else rr->rrremainingttl = rr->rroriginalttl - SecsSinceRcvd;
-
- // We only give positive responses to new questions.
- // Since this question is new, it has not received any answers yet, so there's no point
- // telling it about records that are going away that it never heard about in the first place.
- if (rr->rrremainingttl > 0)
- AnswerQuestionWithResourceRecord(m, q, rr, timenow);
- // MUST NOT touch q again after calling AnswerQuestionWithResourceRecord()
+ verbosedebugf("CacheRecordRmv %p %##s (%s)", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype));
+ if (q->CurrentAnswers == 0)
+ LogMsg("CacheRecordRmv ERROR: How can CurrentAnswers already be zero for %p %##s (%s)?", q, q->qname.c, DNSTypeName(q->qtype));
+ else
+ {
+ q->CurrentAnswers--;
+ if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--;
+ if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers--;
+ }
+ if (q->CurrentAnswers == 0)
+ {
+ debugf("CacheRecordRmv: Zero current answers for %##s (%s); will reconfirm antecedents", q->qname.c, DNSTypeName(q->qtype));
+ ReconfirmAntecedents(m, q);
+ }
+ AnswerQuestionWithResourceRecord(m, q, rr, mDNSfalse);
+ // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
}
+ }
m->CurrentQuestion = mDNSNULL;
- m->lock_rrcache = 0;
}
-mDNSlocal void FlushCacheRecords(mDNS *const m, mDNSIPAddr InterfaceAddr, const mDNSs32 timenow)
+mDNSlocal void ReleaseCacheRR(mDNS *const m, CacheRecord *r)
{
- mDNSu32 count = 0;
- ResourceRecord *rr;
- for (rr = m->rrcache; rr; rr=rr->next)
- {
- if (rr->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger)
- {
- // If the record's interface matches the one we're flushing,
- // then pretend we just received a 'goodbye' packet for this record.
- rr->TimeRcvd = timenow - mDNSPlatformOneSecond * 60;
- rr->UnansweredQueries = 2;
- rr->rroriginalttl = 0;
- count++;
- }
- }
-
- if (count) debugf("FlushCacheRecords Flushing %d Cache Entries on interface %.4a", count, &InterfaceAddr);
+ if (r->resrec.rdata && r->resrec.rdata != (RData*)&r->rdatastorage)
+ mDNSPlatformMemFree(r->resrec.rdata);
+ r->resrec.rdata = mDNSNULL;
+ r->next = m->rrcache_free;
+ m->rrcache_free = r;
+ m->rrcache_totalused--;
}
-// TidyRRCache
-// Throw away any cache records that have passed their TTL
-// First we prepare a list of records to delete, and pull them off the rrcache list
-// Then we go through the list of records to delete, calling the user's question callbacks if necessary
-// We do it in two phases like this to guard against the user's question callbacks modifying
-// the rrcache list while we're walking it.
-mDNSlocal void TidyRRCache(mDNS *const m, const mDNSs32 timenow)
+mDNSlocal void CheckCacheExpiration(mDNS *const m, mDNSu32 slot)
{
- mDNSu32 count = 0;
- ResourceRecord **rr = &m->rrcache;
- ResourceRecord *deletelist = mDNSNULL;
-
- if (m->lock_rrcache) { debugf("TidyRRCache ERROR! Cache already locked!"); return; }
+ CacheRecord **rp = &(m->rrcache_hash[slot]);
+
+ if (m->lock_rrcache) { LogMsg("CheckCacheExpiration ERROR! Cache already locked!"); return; }
m->lock_rrcache = 1;
-
- while (*rr)
+
+ while (*rp)
{
- mDNSu32 timesincercvd = (mDNSu32)(timenow - (*rr)->TimeRcvd);
- if ((*rr)->rroriginalttl > timesincercvd / mDNSPlatformOneSecond)
- rr=&(*rr)->next; // If TTL is greater than time elapsed, save this record
- else
+ CacheRecord *const rr = *rp;
+ mDNSs32 event = RRExpireTime(rr);
+ if (m->timenow - event >= 0) // If expired, delete it
+ {
+ *rp = rr->next; // Cut it from the list
+ verbosedebugf("CheckCacheExpiration: Deleting %s", GetRRDisplayString(m, rr));
+ if (rr->CRActiveQuestion) // If this record has one or more active questions, tell them it's going away
+ {
+ CacheRecordRmv(m, rr);
+ m->rrcache_active--;
+ }
+ m->rrcache_used[slot]--;
+ ReleaseCacheRR(m, rr);
+ }
+ else // else, not expired; see if we need to query
{
- ResourceRecord *r = *rr; // Else,
- *rr = r->next; // detatch this record from the cache list
- r->next = deletelist; // and move it onto the list of things to delete
- deletelist = r;
- count++;
+ if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries)
+ {
+ 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)
+ m->NextCacheCheck = (event + CacheCheckGracePeriod(rr));
+ rp = &rr->next;
}
}
-
- if (count) verbosedebugf("TidyRRCache Deleting %d Expired Cache Entries", count);
-
m->lock_rrcache = 0;
-
- while (deletelist)
- {
- ResourceRecord *r = deletelist;
- verbosedebugf("TidyRRCache: Deleted %##s (%s)", r->name.c, DNSTypeName(r->rrtype));
- deletelist = deletelist->next;
- AnswerLocalQuestions(m, r, timenow);
- r->next = m->rrcache_free; // and move it back to the free list
- m->rrcache_free = r;
- m->rrcache_used--;
- }
}
-mDNSlocal ResourceRecord *GetFreeCacheRR(mDNS *const m, const mDNSs32 timenow)
+mDNSlocal void AnswerNewQuestion(mDNS *const m)
{
- ResourceRecord *r = m->rrcache_free;
+ mDNSBool ShouldQueryImmediately = mDNStrue;
+ CacheRecord *rr;
+ DNSQuestion *q = m->NewQuestions; // Grab the question we're going to answer
+ mDNSu32 slot = HashSlot(&q->qname);
+
+ verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
- if (m->lock_rrcache) { debugf("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL); }
+ CheckCacheExpiration(m, slot);
+ m->NewQuestions = q->next; // Advance NewQuestions to the next *after* calling CheckCacheExpiration();
+
+ if (m->lock_rrcache) LogMsg("AnswerNewQuestion ERROR! Cache already locked!");
+ // This should be safe, because calling the client's question callback may cause the
+ // question list to be modified, but should not ever cause the rrcache list to be modified.
+ // If the client's question callback deletes the question, then m->CurrentQuestion will
+ // be advanced, and we'll exit out of the loop
m->lock_rrcache = 1;
-
- if (r) // If there are records in the free list, take one
- {
- m->rrcache_free = r->next;
- m->rrcache_used++;
- if (m->rrcache_used >= m->rrcache_report)
+ if (m->CurrentQuestion) LogMsg("AnswerNewQuestion ERROR m->CurrentQuestion already set");
+ m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted
+ for (rr=m->rrcache_hash[slot]; rr; rr=rr->next)
+ if (ResourceRecordAnswersQuestion(&rr->resrec, q))
{
- debugf("RR Cache now using %d records", m->rrcache_used);
- m->rrcache_report *= 2;
+ // SecsSinceRcvd is whole number of elapsed seconds, rounded down
+ mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond;
+ if (rr->resrec.rroriginalttl <= SecsSinceRcvd)
+ {
+ LogMsg("AnswerNewQuestion: How is rr->resrec.rroriginalttl %lu <= SecsSinceRcvd %lu for %##s (%s)",
+ rr->resrec.rroriginalttl, SecsSinceRcvd, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype));
+ continue; // Go to next one in loop
+ }
+
+ // 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;
+ q->CurrentAnswers++;
+ if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++;
+ if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++;
+ AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue);
+ // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
+ if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here
}
- }
- else // Else search for a candidate to recycle
+
+ if (ShouldQueryImmediately && m->CurrentQuestion == q)
{
- ResourceRecord **rr = &m->rrcache;
- ResourceRecord **best = mDNSNULL;
- mDNSs32 bestage = -1;
+ q->ThisQInterval = InitialQuestionInterval;
+ q->LastQTime = m->timenow - q->ThisQInterval;
+ m->NextScheduledQuery = m->timenow;
+ }
+ m->CurrentQuestion = mDNSNULL;
+ m->lock_rrcache = 0;
+ }
- while (*rr)
- {
- mDNSs32 timesincercvd = timenow - (*rr)->TimeRcvd;
+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;
+ 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
+ }
- // Records we've only just received are not candidates for deletion
- if (timesincercvd > 0)
- {
- // Work out a weighted age, which is the number of seconds since this record was last used,
- // divided by the number of times it has been used (we want to keep frequently used records longer).
- mDNSs32 count = (*rr)->UseCount < 100 ? 1 + (mDNSs32)(*rr)->UseCount : 100;
- mDNSs32 age = (timenow - (*rr)->LastUsed) / count;
- mDNSu8 rtype = ((*rr)->RecordType) & ~kDNSRecordTypeUniqueMask;
- if (rtype == kDNSRecordTypePacketAnswer) age /= 2; // Keep answer records longer than additionals
+mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m)
+ {
+ DNSQuestion *q = m->NewLocalOnlyQuestions; // Grab the question we're going to answer
+ m->NewLocalOnlyQuestions = q->next; // Advance NewQuestions to the next (if any)
- // Records that answer still-active questions are not candidates for deletion
- if (bestage < age && !CacheRRActive(m, *rr)) { best = rr; bestage = age; }
- }
+ debugf("AnswerNewLocalOnlyQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+
+ if (m->CurrentQuestion) LogMsg("AnswerNewQuestion ERROR m->CurrentQuestion already set");
+ m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted
- rr=&(*rr)->next;
+ m->CurrentRecord = m->LocalOnlyRecords;
+ while (m->CurrentRecord && m->CurrentRecord != m->NewLocalOnlyRecords)
+ {
+ AuthRecord *rr = m->CurrentRecord;
+ m->CurrentRecord = rr->next;
+ if (ResourceRecordAnswersQuestion(&rr->resrec, q))
+ {
+ AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, mDNStrue);
+ // MUST NOT dereference q again after calling AnswerLocalOnlyQuestionWithResourceRecord()
+ if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here
}
+ }
+
+ m->CurrentQuestion = mDNSNULL;
+ }
- if (best)
+mDNSlocal void AnswerLocalOnlyQuestions(mDNS *const m, AuthRecord *rr, mDNSBool AddRecord)
+ {
+ if (m->CurrentQuestion) LogMsg("AnswerLocalOnlyQuestions ERROR m->CurrentQuestion already set");
+ m->CurrentQuestion = m->LocalOnlyQuestions;
+ while (m->CurrentQuestion && m->CurrentQuestion != m->NewLocalOnlyQuestions)
+ {
+ DNSQuestion *q = m->CurrentQuestion;
+ m->CurrentQuestion = q->next;
+ if (ResourceRecordAnswersQuestion(&rr->resrec, q))
{
- r = *best; // Remember the record we chose
- *best = r->next; // And detatch it from the free list
+ 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()
}
}
+ m->CurrentQuestion = mDNSNULL;
+ }
- m->lock_rrcache = 0;
+mDNSlocal void DiscardLocalOnlyRecords(mDNS *const m)
+ {
+ AuthRecord *rr = m->LocalOnlyRecords;
+ while (rr)
+ {
+ if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
+ { AnswerLocalOnlyQuestions(m, rr, mDNSfalse); CompleteDeregistration(m, rr); return; }
+ if (rr->ProbeCount) { mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); return; }
+ rr=rr->next;
+ }
+ m->DiscardLocalOnlyRecords = mDNSfalse;
+ }
- if (r) mDNSPlatformMemZero(r, sizeof(*r));
- return(r);
+mDNSlocal void AnswerForNewLocalOnlyRecords(mDNS *const m)
+ {
+ AuthRecord *rr = m->NewLocalOnlyRecords;
+ m->NewLocalOnlyRecords = m->NewLocalOnlyRecords->next;
+ AnswerLocalOnlyQuestions(m, rr, mDNStrue);
}
-mDNSlocal void ScheduleNextTask(const mDNS *const m)
+mDNSlocal CacheRecord *GetFreeCacheRR(mDNS *const m, mDNSu16 RDLength)
{
- const mDNSs32 timenow = mDNSPlatformTimeNow();
- mDNSs32 nextevent = timenow + 0x78000000;
- const char *msg = "No Event", *sign="";
- mDNSs32 interval, fraction;
+ CacheRecord *r = mDNSNULL;
- DNSQuestion *q;
- ResourceRecord *rr;
-
- if (m->mDNSPlatformStatus != mStatus_NoError)
- return;
+ if (m->lock_rrcache) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL); }
+ m->lock_rrcache = 1;
- // 1. If sleeping, do nothing
- if (m->SleepState)
+ // If we have no free records, ask the client layer to give us some more memory
+ if (!m->rrcache_free && m->MainCallback)
{
- debugf("ScheduleNextTask: Sleeping");
- return;
+ if (m->rrcache_totalused != m->rrcache_size)
+ LogMsg("GetFreeCacheRR: count mismatch: m->rrcache_totalused %lu != m->rrcache_size %lu",
+ m->rrcache_totalused, m->rrcache_size);
+
+ // We don't want to be vulnerable to a malicious attacker flooding us with an infinite
+ // number of bogus records so that we keep growing our cache until the machine runs out of memory.
+ // To guard against this, if we're actively using less than 1/32 of our cache, then we
+ // purge all the unused records and recycle them, instead of allocating more memory.
+ if (m->rrcache_size >= 512 && m->rrcache_size / 32 > m->rrcache_active)
+ debugf("Possible denial-of-service attack in progress: m->rrcache_size %lu; m->rrcache_active %lu",
+ m->rrcache_size, m->rrcache_active);
+ else
+ m->MainCallback(m, mStatus_GrowCache);
}
- // 2. If we have new questions added to the list, we need to answer them from cache ASAP
- if (m->NewQuestions)
- {
- nextevent = timenow;
- msg = "New Questions";
- }
- else
+ // If we still have no free records, recycle all the records we can.
+ // Enumerating the entire cache is moderately expensive, so when we do it, we reclaim all the records we can in one pass.
+ if (!m->rrcache_free)
{
- // 3. Scan cache to see if any resource records are going to expire
- for (rr = m->rrcache; rr; rr=rr->next)
- {
- mDNSs32 onetenth = ((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond) / 10;
- mDNSs32 t0 = rr->TimeRcvd + (mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond;
- mDNSs32 t1 = t0 - onetenth;
- mDNSs32 t2 = t1 - onetenth;
- if (rr->UnansweredQueries < 1 && nextevent - t2 > 0 && CacheRRActive(m, rr))
- {
- nextevent = t2;
- msg = "Penultimate Query";
- }
- else if (rr->UnansweredQueries < 2 && nextevent - t1 > 0 && CacheRRActive(m, rr))
- {
- nextevent = t1;
- msg = "Final Expiration Query";
- }
- else if (nextevent - t0 > 0)
- {
- nextevent = t0;
- msg = "Cache Tidying";
- }
- }
-
- // 4. If we're suppressing sending right now, don't bother searching for packet generation events --
- // but do make sure we come back at the end of the suppression time to check again
- if (m->SuppressSending)
- {
- if (nextevent - m->SuppressSending > 0)
- {
- nextevent = m->SuppressSending;
- msg = "Send Suppressed Packets";
- }
- }
- else
+ #if MDNS_DEBUGMSGS
+ mDNSu32 oldtotalused = m->rrcache_totalused;
+ #endif
+ mDNSu32 slot;
+ CacheRecord **rr;
+ for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
{
- // 5. Scan list of active questions to see if we need to send any queries
- for (q = m->ActiveQuestions; q; q=q->next)
- if (TimeToSendThisQuestion(q, nextevent))
- {
- nextevent = q->NextQTime;
- msg = "Send Questions";
- }
-
- // 6. Scan list of local resource records to see if we have any
- // deregistrations, probes, announcements, or replies to send
- for (rr = m->ResourceRecords; rr; rr=rr->next)
+ rr = &(m->rrcache_hash[slot]);
+ while (*rr)
{
- if (rr->RecordType == kDNSRecordTypeDeregistering)
- {
- nextevent = timenow;
- msg = "Send Deregistrations";
- }
- else if (rr->SendPriority >= kDNSSendPriorityAnswer && ResourceRecordIsValidAnswer(rr))
- {
- nextevent = timenow;
- msg = "Send Answers";
- }
- else if (rr->RecordType == kDNSRecordTypeUnique && nextevent - rr->NextSendTime > 0)
- {
- nextevent = rr->NextSendTime;
- msg = "Send Probes";
- }
- else if (rr->AnnounceCount && nextevent - rr->NextSendTime > 0 && ResourceRecordIsValidAnswer(rr))
+ // Records that answer still-active questions are not candidates for deletion
+ if ((*rr)->CRActiveQuestion)
+ rr=&(*rr)->next;
+ else
{
- nextevent = rr->NextSendTime;
- msg = "Send Announcements";
+ CacheRecord *r = *rr;
+ *rr = (*rr)->next; // Cut record from list
+ m->rrcache_used[slot]--; // Decrement counts
+ ReleaseCacheRR(m, r);
}
}
}
+ #if MDNS_DEBUGMSGS
+ debugf("Clear unused records; m->rrcache_totalused was %lu; now %lu", oldtotalused, m->rrcache_totalused);
+ #endif
+ }
+
+ if (m->rrcache_free) // If there are records in the free list, take one
+ {
+ r = m->rrcache_free;
+ m->rrcache_free = r->next;
+ }
+
+ if (r)
+ {
+ if (++m->rrcache_totalused >= m->rrcache_report)
+ {
+ debugf("RR Cache now using %ld records", m->rrcache_totalused);
+ if (m->rrcache_report < 100) m->rrcache_report += 10;
+ 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
+
+ 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;
+ else { ReleaseCacheRR(m, r); r = mDNSNULL; }
+ }
}
- interval = nextevent - timenow;
- if (interval < 0) { interval = -interval; sign = "-"; }
- fraction = interval % mDNSPlatformOneSecond;
- verbosedebugf("ScheduleNextTask: Next event: <%s> in %s%d.%03d seconds", msg, sign,
- interval / mDNSPlatformOneSecond, fraction * 1000 / mDNSPlatformOneSecond);
+ m->lock_rrcache = 0;
+
+ return(r);
+ }
- mDNSPlatformScheduleTask(m, nextevent);
+mDNSlocal void PurgeCacheResourceRecord(mDNS *const m, CacheRecord *rr)
+ {
+ // Make sure we mark this record as thoroughly expired -- we don't ever want to give
+ // a positive answer using an expired record (e.g. from an interface that has gone away).
+ // We don't want to clear CRActiveQuestion here, because that would leave the record subject to
+ // summary deletion without giving the proper callback to any questions that are monitoring it.
+ // By setting UnansweredQueries to MaxUnansweredQueries we ensure it won't trigger any further expiration queries.
+ rr->TimeRcvd = m->timenow - mDNSPlatformOneSecond * 60;
+ rr->UnansweredQueries = MaxUnansweredQueries;
+ rr->resrec.rroriginalttl = 0;
+ SetNextCacheCheckTime(m, rr);
}
-mDNSlocal mDNSs32 mDNS_Lock(mDNS *const m)
+mDNSlocal void mDNS_Lock(mDNS *const m)
{
+ // MUST grab the platform lock FIRST!
mDNSPlatformLock(m);
- ++m->mDNS_busy;
- return(mDNSPlatformTimeNow());
+
+ // 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)
+ {
+ 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;
+ }
+ 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->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)
{
- // Upon unlocking, we've usually added some new work to the task list.
- // If we don't decrement mDNS_busy to zero, then we don't have to worry about calling
- // ScheduleNextTask(), because the last lock holder will do it for us on the way out.
- if (--m->mDNS_busy == 0) ScheduleNextTask(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!
mDNSPlatformUnlock(m);
}
-mDNSexport void mDNSCoreTask(mDNS *const m)
+mDNSexport mDNSs32 mDNS_Execute(mDNS *const m)
{
- const mDNSs32 timenow = mDNS_Lock(m);
+ mDNS_Lock(m); // Must grab lock before trying to read m->timenow
+
+ if (m->timenow - m->NextScheduledEvent >= 0)
+ {
+ int i;
- verbosedebugf("mDNSCoreTask");
- if (m->mDNS_busy > 1) debugf("mDNSCoreTask: Locking failure! mDNS already busy");
- if (m->CurrentQuestion) debugf("mDNSCoreTask: ERROR! m->CurrentQuestion already set");
+ verbosedebugf("mDNS_Execute");
+ if (m->CurrentQuestion) LogMsg("mDNS_Execute: ERROR! m->CurrentQuestion already set");
+
+ // 1. If we're past the probe suppression time, we can clear it
+ if (m->SuppressProbes && m->timenow - m->SuppressProbes >= 0) m->SuppressProbes = 0;
+
+ // 2. If it's been more than ten seconds since the last probe failure, we can clear the counter
+ if (m->NumFailedProbes && m->timenow - m->ProbeFailTime >= mDNSPlatformOneSecond * 10) m->NumFailedProbes = 0;
+
+ // 3. Purge our cache of stale old records
+ if (m->rrcache_size && m->timenow - m->NextCacheCheck >= 0)
+ {
+ mDNSu32 slot;
+ m->NextCacheCheck = m->timenow + 0x3FFFFFFF;
+ for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) CheckCacheExpiration(m, slot);
+ }
- if (m->SuppressProbes && timenow - m->SuppressProbes >= 0)
- m->SuppressProbes = 0;
+ // 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);
+ if (i >= 1000) debugf("mDNS_Execute: AnswerNewQuestion exceeded loop limit");
+
+ for (i=0; m->DiscardLocalOnlyRecords && i<1000; i++) DiscardLocalOnlyRecords(m);
+ if (i >= 1000) debugf("mDNS_Execute: DiscardLocalOnlyRecords exceeded loop limit");
- if (m->NumFailedProbes && timenow - m->ProbeFailTime >= mDNSPlatformOneSecond * 10)
- m->NumFailedProbes = 0;
+ for (i=0; m->NewLocalOnlyQuestions && i<1000; i++) AnswerNewLocalOnlyQuestion(m);
+ if (i >= 1000) debugf("mDNS_Execute: AnswerNewLocalOnlyQuestion exceeded loop limit");
- // 1. See if we can answer any of our new local questions from the cache
- while (m->NewQuestions) AnswerNewQuestion(m, timenow);
+ for (i=0; m->NewLocalOnlyRecords && i<1000; i++) AnswerForNewLocalOnlyRecords(m);
+ if (i >= 1000) debugf("mDNS_Execute: AnswerLocalOnlyQuestions exceeded loop limit");
- // 2. See what packets we need to send
- if (m->mDNSPlatformStatus != mStatus_NoError || m->SleepState)
- {
- // If the platform code is currently non-operational,
- // then we'll just complete deregistrations immediately,
- // without waiting for the goodbye packet to be sent
- DiscardDeregistrations(m, timenow);
- }
- else if (m->SuppressSending == 0 || timenow - m->SuppressSending >= 0)
- {
- int i;
- // If the platform code is ready,
- // and we're not suppressing packet generation right now
- // send our responses, probes, and questions
- m->SuppressSending = 0;
- for (i=0; i<100 && HaveResponses(m, timenow); i++) SendResponses(m, timenow);
- if (i >= 100) LogErrorMessage("mDNSCoreTask: HaveResponses returned true %d times", i);
- for (i=0; i<100 && HaveQueries (m, timenow); i++) SendQueries (m, timenow);
- if (i >= 100) LogErrorMessage("mDNSCoreTask: HaveQueries returned true %d times", i);
- }
+ // 5. See what packets we need to send
+ if (m->mDNSPlatformStatus != mStatus_NoError || m->SleepState) DiscardDeregistrations(m);
+ else if (m->SuppressSending == 0 || m->timenow - m->SuppressSending >= 0)
+ {
+ // If the platform code is ready, and we're not suppressing packet generation right now
+ // then send our responses, probes, and questions.
+ // We check the cache first, because there might be records close to expiring that trigger questions to refresh them
+ // We send queries next, because there might be final-stage probes that complete their probing here, causing
+ // them to advance to announcing state, and we want those to be included in any announcements we send out.
+ // Finally, we send responses, including the previously mentioned records that just completed probing
+ m->SuppressSending = 0;
+
+ // 6. Send Query packets. This may cause some probing records to advance to announcing state
+ if (m->timenow - m->NextScheduledQuery >= 0 || m->timenow - m->NextScheduledProbe >= 0) SendQueries(m);
+ if (m->timenow - m->NextScheduledQuery >= 0)
+ {
+ LogMsg("mDNS_Execute: SendQueries didn't send all its queries; will try again in one second");
+ m->NextScheduledQuery = m->timenow + mDNSPlatformOneSecond;
+ }
+ if (m->timenow - m->NextScheduledProbe >= 0)
+ {
+ LogMsg("mDNS_Execute: SendQueries didn't send all its probes; will try again in one second");
+ m->NextScheduledProbe = m->timenow + mDNSPlatformOneSecond;
+ }
+
+ // 7. Send Response packets, including probing records just advanced to announcing state
+ if (m->timenow - m->NextScheduledResponse >= 0) SendResponses(m);
+ if (m->timenow - m->NextScheduledResponse >= 0)
+ {
+ LogMsg("mDNS_Execute: SendResponses didn't send all its responses; will try again in one second");
+ m->NextScheduledResponse = m->timenow + mDNSPlatformOneSecond;
+ }
+ }
- if (m->rrcache_size) TidyRRCache(m, timenow);
+ m->RandomQueryDelay = 0; // Clear m->RandomQueryDelay, ready to pick a new different value, when necessary
+ }
- mDNS_Unlock(m);
+ // Note about multi-threaded systems:
+ // On a multi-threaded system, some other thread could run right after the mDNS_Unlock(),
+ // performing mDNS API operations that change our next scheduled event time.
+ //
+ // On multi-threaded systems (like the current Windows implementation) that have a single main thread
+ // calling mDNS_Execute() (and other threads allowed to call mDNS API routines) it is the responsibility
+ // of the mDNSPlatformUnlock() routine to signal some kind of stateful condition variable that will
+ // signal whatever blocking primitive the main thread is using, so that it will wake up and execute one
+ // more iteration of its loop, and immediately call mDNS_Execute() again. The signal has to be stateful
+ // in the sense that if the main thread has not yet entered its blocking primitive, then as soon as it
+ // does, the state of the signal will be noticed, causing the blocking primitive to return immediately
+ // without blocking. This avoids the race condition between the signal from the other thread arriving
+ // just *before* or just *after* the main thread enters the blocking primitive.
+ //
+ // On multi-threaded systems (like the current Mac OS 9 implementation) that are entirely timer-driven,
+ // with no main mDNS_Execute() thread, it is the responsibility of the mDNSPlatformUnlock() routine to
+ // set the timer according to the m->NextScheduledEvent value, and then when the timer fires, the timer
+ // 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).
+
+ mDNS_Unlock(m); // Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value
+ return(m->NextScheduledEvent);
}
-mDNSexport void mDNSCoreSleep(mDNS *const m, mDNSBool sleepstate)
+// Call mDNSCoreMachineSleep(m, mDNStrue) when the machine is about to go to sleep.
+// Call mDNSCoreMachineSleep(m, mDNSfalse) when the machine is has just woken up.
+// Normally, the platform support layer below mDNSCore should call this, not the client layer above.
+// Note that sleep/wake calls do not have to be paired one-for-one; it is acceptable to call
+// mDNSCoreMachineSleep(m, mDNSfalse) any time there is reason to believe that the machine may have just
+// found itself in a new network environment. For example, if the Ethernet hardware indicates that the
+// cable has just been connected, the platform support layer should call mDNSCoreMachineSleep(m, mDNSfalse)
+// to make mDNSCore re-issue its outstanding queries, probe for record uniqueness, etc.
+// While it is safe to call mDNSCoreMachineSleep(m, mDNSfalse) at any time, it does cause extra network
+// traffic, so it should only be called when there is legitimate reason to believe the machine
+// may have become attached to a new network.
+mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleepstate)
{
- ResourceRecord *rr;
- const mDNSs32 timenow = mDNS_Lock(m);
+ AuthRecord *rr;
+
+ mDNS_Lock(m);
m->SleepState = sleepstate;
- debugf("mDNSCoreSleep: %d", sleepstate);
+ LogMsg("mDNSResponder %s at %ld", sleepstate ? "Sleeping" : "Waking", m->timenow);
if (sleepstate)
{
- // First mark all the records we need to deregister
+ // Mark all the records we need to deregister and send them
for (rr = m->ResourceRecords; rr; rr=rr->next)
- if (rr->RecordType == kDNSRecordTypeShared && rr->AnnounceCount <= DefaultAnnounceCountForTypeShared)
- rr->rrremainingttl = 0;
- while (HaveResponses(m, timenow)) SendResponses(m, timenow);
+ if (rr->resrec.RecordType == kDNSRecordTypeShared && rr->AnnounceCount < InitialAnnounceCount)
+ rr->ImmedAnswer = mDNSInterfaceMark;
+ SendResponses(m);
}
else
{
DNSQuestion *q;
+ mDNSu32 slot;
+ CacheRecord *cr;
+
+ // 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;
+ ExpireDupSuppressInfo(q->DupSuppress, m->timenow);
+ m->NextScheduledQuery = m->timenow;
+ }
+ // 2. Re-validate our cache records
+ m->NextCacheCheck = m->timenow;
+ for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
+ for (cr = m->rrcache_hash[slot]; cr; cr=cr->next)
+ mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForCableDisconnect);
+
+ // 3. Retrigger probing and announcing for all our authoritative records
for (rr = m->ResourceRecords; rr; rr=rr->next)
{
- if (rr->RecordType == kDNSRecordTypeVerified) rr->RecordType = kDNSRecordTypeUnique;
- rr->ProbeCount = DefaultProbeCountForRecordType(rr->RecordType);
- rr->AnnounceCount = DefaultAnnounceCountForRecordType(rr->RecordType);
- rr->NextSendTime = timenow;
- rr->NextSendInterval = DefaultSendIntervalForRecordType(rr->RecordType);
+ 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);
+ InitializeLastAPTime(m, rr);
}
- for (q = m->ActiveQuestions; q; q=q->next) // Scan our list of questions
- if (q->ThisQInterval > 0 && !q->DuplicateOf)
- {
- q->NextQTime = timenow;
- q->ThisQInterval = mDNSPlatformOneSecond; // MUST NOT be zero for an active question
- q->NextQInterval = mDNSPlatformOneSecond;
- }
+
}
mDNS_Unlock(m);
}
// ***************************************************************************
-#if 0
+#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark - Packet Reception Functions
#endif
-mDNSlocal mDNSBool AddRecordToResponseList(ResourceRecord **nrp,
- ResourceRecord *rr, const mDNSu8 *answerto, ResourceRecord *additionalto)
+mDNSlocal void AddRecordToResponseList(AuthRecord ***nrpp, AuthRecord *rr, AuthRecord *add)
{
- if (rr->NextResponse == mDNSNULL && nrp != &rr->NextResponse)
+ if (rr->NextResponse == mDNSNULL && *nrpp != &rr->NextResponse)
{
- *nrp = rr;
- rr->NR_AnswerTo = answerto;
- rr->NR_AdditionalTo = additionalto;
- return(mDNStrue);
+ **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;
}
- else debugf("AddRecordToResponseList: %##s (%s) already in list", rr->name.c, DNSTypeName(rr->rrtype));
- return(mDNSfalse);
+ 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,
- const mDNSIPAddr InterfaceAddr, DNSMessage *const reply, ResourceRecord *ResponseRecords)
+ const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, DNSMessage *const response, AuthRecord *ResponseRecords)
{
- const mDNSu8 *const limit = reply->data + sizeof(reply->data);
+ mDNSu8 *responseptr = response->data;
+ const mDNSu8 *const limit = response->data + sizeof(response->data);
const mDNSu8 *ptr = query->data;
- mDNSu8 *responseptr = reply->data;
- ResourceRecord *rr;
+ AuthRecord *rr;
+ mDNSu32 maxttl = 0x70000000;
int i;
// Initialize the response fields so we can answer the questions
- InitializeDNSMessage(&reply->h, query->h.id, ResponseFlags);
+ InitializeDNSMessage(&response->h, query->h.id, ResponseFlags);
// ***
// *** 1. Write out the list of questions we are actually going to answer with this packet
// ***
- for (i=0; i<query->h.numQuestions; i++) // For each question...
+ if (LegacyQuery)
{
- DNSQuestion q;
- ptr = getQuestion(query, ptr, end, InterfaceAddr, &q); // get the question...
- if (!ptr) return(mDNSNULL);
-
- for (rr=ResponseRecords; rr; rr=rr->NextResponse) // and search our list of proposed answers
+ maxttl = 10;
+ for (i=0; i<query->h.numQuestions; i++) // For each question...
{
- if (rr->NR_AnswerTo == ptr) // If we're going to generate a record answering this question
- { // then put the question in the question section
- responseptr = putQuestion(reply, responseptr, limit, &q.name, q.rrtype, q.rrclass);
- if (!responseptr) { debugf("GenerateUnicastResponse: Ran out of space for questions!"); return(mDNSNULL); }
- break; // break out of the ResponseRecords loop, and go on to the next question
+ DNSQuestion q;
+ ptr = getQuestion(query, ptr, end, InterfaceID, &q); // get the question...
+ if (!ptr) return(mDNSNULL);
+
+ for (rr=ResponseRecords; rr; rr=rr->NextResponse) // and search our list of proposed answers
+ {
+ if (rr->NR_AnswerTo == ptr) // If we're going to generate a record answering this question
+ { // then put the question in the question section
+ responseptr = putQuestion(response, responseptr, limit, &q.qname, q.qtype, q.qclass);
+ if (!responseptr) { debugf("GenerateUnicastResponse: Ran out of space for questions!"); return(mDNSNULL); }
+ break; // break out of the ResponseRecords loop, and go on to the next question
+ }
}
}
+
+ if (response->h.numQuestions == 0) { LogMsg("GenerateUnicastResponse: ERROR! Why no questions?"); return(mDNSNULL); }
}
- if (reply->h.numQuestions == 0) { debugf("GenerateUnicastResponse: ERROR! Why no questions?"); return(mDNSNULL); }
+ // ***
+ // *** 2. Write Answers
+ // ***
+ for (rr=ResponseRecords; rr; rr=rr->NextResponse)
+ if (rr->NR_AnswerTo)
+ {
+ mDNSu8 *p = PutResourceRecordCappedTTL(response, responseptr, &response->h.numAnswers, &rr->resrec, maxttl);
+ if (p) responseptr = p;
+ else { debugf("GenerateUnicastResponse: Ran out of space for answers!"); response->h.flags.b[0] |= kDNSFlag0_TC; }
+ }
// ***
- // *** 2. Write answers and additionals
+ // *** 3. Write Additionals
// ***
for (rr=ResponseRecords; rr; rr=rr->NextResponse)
- {
- if (MustSendRecord(rr))
+ if (rr->NR_AdditionalTo && !rr->NR_AnswerTo)
{
- if (rr->NR_AnswerTo)
- {
- mDNSu8 *p = putResourceRecord(reply, responseptr, &reply->h.numAnswers, rr, mDNSNULL, 0);
- if (p) responseptr = p;
- else { debugf("GenerateUnicastResponse: Ran out of space for answers!"); reply->h.flags.b[0] |= kDNSFlag0_TC; }
- }
- else
- {
- mDNSu8 *p = putResourceRecord(reply, responseptr, &reply->h.numAdditionals, rr, mDNSNULL, 0);
- if (p) responseptr = p;
- else debugf("GenerateUnicastResponse: No more space for additionals");
- }
+ mDNSu8 *p = PutResourceRecordCappedTTL(response, responseptr, &response->h.numAdditionals, &rr->resrec, maxttl);
+ if (p) responseptr = p;
+ else debugf("GenerateUnicastResponse: No more space for additionals");
}
- }
+
return(responseptr);
}
-// ResourceRecord *pktrr is the ResourceRecord from the response packet we've witnessed on the network
-// ResourceRecord *rr is our ResourceRecord
+// AuthRecord *our is our Resource Record
+// CacheRecord *pkt is the Resource Record from the response packet we've witnessed on the network
// Returns 0 if there is no conflict
// Returns +1 if there was a conflict and we won
// Returns -1 if there was a conflict and we lost and have to rename
-mDNSlocal int CompareRData(ResourceRecord *pkt, ResourceRecord *our)
+mDNSlocal int CompareRData(AuthRecord *our, CacheRecord *pkt)
{
- mDNSu8 pktdata[256], *pktptr = pktdata, *pktend;
mDNSu8 ourdata[256], *ourptr = ourdata, *ourend;
- if (!pkt) { debugf("CompareRData ERROR: pkt is NULL"); return(+1); }
- if (!our) { debugf("CompareRData ERROR: our is NULL"); return(+1); }
-
- pktend = putRData(mDNSNULL, pktdata, pktdata + sizeof(pktdata), pkt->rrtype, pkt->rdata);
- ourend = putRData(mDNSNULL, ourdata, ourdata + sizeof(ourdata), our->rrtype, our->rdata);
- while (pktptr < pktend && ourptr < ourend && *pktptr == *ourptr) { pktptr++; ourptr++; }
- if (pktptr >= pktend && ourptr >= ourend) return(0); // If data identical, not a conflict
-
- if (pktptr >= pktend) return(-1); // Packet data is substring; We lost
- if (ourptr >= ourend) return(+1); // Our data is substring; We won
- if (*pktptr < *ourptr) return(-1); // Packet data is numerically lower; We lost
- if (*pktptr > *ourptr) return(+1); // Our data is numerically lower; We won
+ mDNSu8 pktdata[256], *pktptr = pktdata, *pktend;
+ if (!our) { LogMsg("CompareRData ERROR: our is NULL"); return(+1); }
+ if (!pkt) { LogMsg("CompareRData ERROR: pkt is NULL"); return(+1); }
+
+ ourend = putRData(mDNSNULL, ourdata, ourdata + sizeof(ourdata), &our->resrec);
+ pktend = putRData(mDNSNULL, pktdata, pktdata + sizeof(pktdata), &pkt->resrec);
+ while (ourptr < ourend && pktptr < pktend && *ourptr == *pktptr) { ourptr++; pktptr++; }
+ if (ourptr >= ourend && pktptr >= pktend) return(0); // If data identical, not a conflict
+
+ if (ourptr >= ourend) return(-1); // Our data ran out first; We lost
+ if (pktptr >= pktend) return(+1); // Packet data ran out first; We won
+ if (*pktptr > *ourptr) return(-1); // Our data is numerically lower; We lost
+ if (*pktptr < *ourptr) return(+1); // Packet data is numerically lower; We won
debugf("CompareRData: How did we get here?");
return(-1);
}
-// Find the canonical DependentOn record for this RR received in a packet.
+// See if we have an authoritative record that's identical to this packet record,
+// whose canonical DependentOn record is the specified master record.
// The DependentOn pointer is typically used for the TXT record of service registrations
// It indicates that there is no inherent conflict detection for the TXT record
// -- it depends on the SRV record to resolve name conflicts
-// If we find any identical ResourceRecord in our authoritative list, then follow its DependentOn
-// pointers (if any) to make sure we return the canonical DependentOn record
+// If we find any identical ResourceRecords in our authoritative list, then follow their DependentOn
+// pointer chain (if any) to make sure we reach the canonical DependentOn record
// If the record has no DependentOn, then just return that record's pointer
// Returns NULL if we don't have any local RRs that are identical to the one from the packet
-mDNSlocal const ResourceRecord *FindDependentOn(const mDNS *const m, const ResourceRecord *const pktrr)
+mDNSlocal mDNSBool MatchDependentOn(const mDNS *const m, const CacheRecord *const pktrr, const AuthRecord *const master)
{
- const ResourceRecord *rr;
- for (rr = m->ResourceRecords; rr; rr=rr->next)
+ const AuthRecord *r1;
+ for (r1 = m->ResourceRecords; r1; r1=r1->next)
{
- if (IdenticalResourceRecordAnyInterface(rr, pktrr))
+ if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec))
{
- while (rr->DependentOn) rr = rr->DependentOn;
- return(rr);
+ const AuthRecord *r2 = r1;
+ while (r2->DependentOn) r2 = r2->DependentOn;
+ if (r2 == master) return(mDNStrue);
}
}
- return(mDNSNULL);
+ for (r1 = m->DuplicateRecords; r1; r1=r1->next)
+ {
+ if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec))
+ {
+ const AuthRecord *r2 = r1;
+ while (r2->DependentOn) r2 = r2->DependentOn;
+ if (r2 == master) return(mDNStrue);
+ }
+ }
+ return(mDNSfalse);
}
// Find the canonical RRSet pointer for this RR received in a packet.
-// If we find any identical ResourceRecord in our authoritative list, then follow its RRSet
+// If we find any identical AuthRecord in our authoritative list, then follow its RRSet
// pointers (if any) to make sure we return the canonical member of this name/type/class
// Returns NULL if we don't have any local RRs that are identical to the one from the packet
-mDNSlocal const ResourceRecord *FindRRSet(const mDNS *const m, const ResourceRecord *const pktrr)
+mDNSlocal const AuthRecord *FindRRSet(const mDNS *const m, const CacheRecord *const pktrr)
{
- const ResourceRecord *rr;
+ const AuthRecord *rr;
for (rr = m->ResourceRecords; rr; rr=rr->next)
{
- if (IdenticalResourceRecordAnyInterface(rr, pktrr))
+ if (IdenticalResourceRecord(&rr->resrec, &pktrr->resrec))
{
while (rr->RRSet && rr != rr->RRSet) rr = rr->RRSet;
return(rr);
// TXT records, and that record is marked as dependent on 'our', its SRV record).
// 3. If we have some *other* RR that exactly matches the one from the packet, and that record and our record
// are members of the same RRSet, then this is not a conflict.
-mDNSlocal mDNSBool PacketRRConflict(const mDNS *const m, const ResourceRecord *const our, const ResourceRecord *const pktrr)
+mDNSlocal mDNSBool PacketRRConflict(const mDNS *const m, const AuthRecord *const our, const CacheRecord *const pktrr)
{
- const ResourceRecord *ourset = our->RRSet ? our->RRSet : our;
+ const AuthRecord *ourset = our->RRSet ? our->RRSet : our;
// If not supposed to be unique, not a conflict
- if (!(our->RecordType & kDNSRecordTypeUniqueMask)) return(mDNSfalse);
+ if (!(our->resrec.RecordType & kDNSRecordTypeUniqueMask)) return(mDNSfalse);
// If a dependent record, not a conflict
- if (our->DependentOn || FindDependentOn(m, pktrr) == our) return(mDNSfalse);
+ if (our->DependentOn || MatchDependentOn(m, pktrr, our)) return(mDNSfalse);
// If the pktrr matches a member of ourset, not a conflict
if (FindRRSet(m, pktrr) == ourset) return(mDNSfalse);
// the record list and/or question list.
// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
mDNSlocal void ResolveSimultaneousProbe(mDNS *const m, const DNSMessage *const query, const mDNSu8 *const end,
- DNSQuestion *q, ResourceRecord *our, const mDNSs32 timenow)
+ DNSQuestion *q, AuthRecord *our)
{
int i;
const mDNSu8 *ptr = LocateAuthorities(query, end);
for (i = 0; i < query->h.numAuthorities; i++)
{
- ResourceRecord pktrr;
- ptr = getResourceRecord(query, ptr, end, q->InterfaceAddr, 0, 0, &pktrr, mDNSNULL);
+ LargeCacheRecord pkt;
+ ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, 0, &pkt);
if (!ptr) break;
- if (ResourceRecordAnswersQuestion(&pktrr, q))
+ if (ResourceRecordAnswersQuestion(&pkt.r.resrec, q))
{
FoundUpdate = mDNStrue;
- if (PacketRRConflict(m, our, &pktrr))
+ if (PacketRRConflict(m, our, &pkt.r))
{
- int result = (int)pktrr.rrclass - (int)our->rrclass;
- if (!result) result = (int)pktrr.rrtype - (int)our->rrtype;
- if (!result) result = CompareRData(&pktrr, our);
+ 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);
switch (result)
{
- case -1: debugf("ResolveSimultaneousProbe: %##s (%s): We won", our->name.c, DNSTypeName(our->rrtype));
+ case 1: debugf("ResolveSimultaneousProbe: %##s (%s): We won", our->resrec.name.c, DNSTypeName(our->resrec.rrtype));
break;
case 0: break;
- case 1: debugf("ResolveSimultaneousProbe: %##s (%s): We lost", our->name.c, DNSTypeName(our->rrtype));
- mDNS_Deregister_internal(m, our, timenow, mDNS_Dereg_conflict);
+ 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;
}
}
}
}
if (!FoundUpdate)
- debugf("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our->name.c, DNSTypeName(our->rrtype));
+ debugf("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our->resrec.name.c, DNSTypeName(our->resrec.rrtype));
+ }
+
+mDNSlocal CacheRecord *FindIdenticalRecordInCache(const mDNS *const m, ResourceRecord *pktrr)
+ {
+ CacheRecord *rr;
+ for (rr = m->rrcache_hash[HashSlot(&pktrr->name)]; rr; rr=rr->next)
+ if (pktrr->InterfaceID == rr->resrec.InterfaceID && IdenticalResourceRecord(pktrr, &rr->resrec)) break;
+ return(rr);
}
// 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 mDNSIPAddr srcaddr, const mDNSIPAddr InterfaceAddr,
- DNSMessage *const replyunicast, mDNSBool replymulticast, const mDNSs32 timenow)
+ const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, mDNSBool QueryWasMulticast,
+ DNSMessage *const response)
{
- ResourceRecord *ResponseRecords = mDNSNULL;
- ResourceRecord **nrp = &ResponseRecords;
+ 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;
mDNSBool delayresponse = mDNSfalse;
- mDNSBool answers = mDNSfalse;
+ mDNSBool HaveUnicastAnswer = mDNSfalse;
const mDNSu8 *ptr = query->data;
mDNSu8 *responseptr = mDNSNULL;
- ResourceRecord *rr, *rr2;
+ AuthRecord *rr, *rr2;
int i;
- // If TC flag is set, it means we should expect additional duplicate suppression info may be coming in another packet.
+ // 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 = mDNStrue;
// ***
// ***
for (i=0; i<query->h.numQuestions; i++) // For each question...
{
+ mDNSBool QuestionNeedsMulticastResponse;
int NumAnswersForThisQuestion = 0;
- DNSQuestion q;
- ptr = getQuestion(query, ptr, end, InterfaceAddr, &q); // get the question...
+ DNSQuestion pktq, *q;
+ ptr = getQuestion(query, ptr, end, InterfaceID, &pktq); // get the question...
if (!ptr) goto exit;
+
+ // The only queries that *need* a multicast response are:
+ // * Queries sent via multicast
+ // * from port 5353
+ // * that don't have the kDNSQClass_UnicastResponse bit set
+ // These queries need multicast responses because other clients will:
+ // * suppress their own identical questions when they see these questions, and
+ // * expire their cache records if they don't see the expected responses
+ // For other queries, we may still choose to send the occasional multicast response anyway,
+ // to keep our neighbours caches warm, and for ongoing conflict detection.
+ QuestionNeedsMulticastResponse = QueryWasMulticast && !LegacyQuery && !(pktq.qclass & kDNSQClass_UnicastResponse);
+ // Clear the UnicastResponse flag -- don't want to confuse the rest of the code that follows later
+ pktq.qclass &= ~kDNSQClass_UnicastResponse;
// Note: We use the m->CurrentRecord mechanism here because calling ResolveSimultaneousProbe
// can result in user callbacks which may change the record list and/or question list.
// Also note: we just mark potential answer records here, without trying to build the
// "ResponseRecords" list, because we don't want to risk user callbacks deleting records
- // from that list while we're in the middle of trying to build it.
- if (m->CurrentRecord) debugf("ProcessQuery ERROR m->CurrentRecord already set");
+ // from that list while we're in the middle of trying to build it.
+ if (m->CurrentRecord) LogMsg("ProcessQuery ERROR m->CurrentRecord already set");
m->CurrentRecord = m->ResourceRecords;
while (m->CurrentRecord)
{
rr = m->CurrentRecord;
m->CurrentRecord = rr->next;
- if (ResourceRecordAnswersQuestion(rr, &q))
+ if (ResourceRecordAnswersQuestion(&rr->resrec, &pktq))
{
- if (rr->RecordType == kDNSRecordTypeUnique)
- ResolveSimultaneousProbe(m, query, end, &q, rr, timenow);
+ if (rr->resrec.RecordType == kDNSRecordTypeUnique)
+ ResolveSimultaneousProbe(m, query, end, &pktq, rr);
else if (ResourceRecordIsValidAnswer(rr))
{
NumAnswersForThisQuestion++;
- if (!rr->NR_AnswerTo) rr->NR_AnswerTo = ptr; // Mark as potential answer
+ // 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)
+ if (QuestionNeedsMulticastResponse)
+ {
+ // We only mark this question for sending if it is at least one second since the last time we multicast it
+ // on this interface. If it is more than a second, or LastMCInterface is different, then we should multicast it.
+ // This is to guard against the case where someone blasts us with queries as fast as they can.
+ if (m->timenow - (rr->LastMCTime + mDNSPlatformOneSecond) >= 0 ||
+ (rr->LastMCInterface != mDNSInterfaceMark && rr->LastMCInterface != InterfaceID))
+ rr->NR_AnswerTo = (mDNSu8*)~0;
+ }
+ else if (!rr->NR_AnswerTo) rr->NR_AnswerTo = ptr;
}
}
}
- // 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 = mDNStrue;
+
+ // 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 = mDNStrue;
+
+ // 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
+ for (rr = m->rrcache_hash[HashSlot(&pktq.qname)]; rr; rr=rr->next)
+ if (ResourceRecordAnswersQuestion(&rr->resrec, &pktq) && rr->resrec.rdlength <= SmallRecordLimit)
+ if (!rr->NextInKAList && eap != &rr->NextInKAList)
+ {
+ *eap = rr;
+ eap = &rr->NextInKAList;
+ if (rr->MPUnansweredQ == 0 || m->timenow - rr->MPLastUnansweredQT >= mDNSPlatformOneSecond)
+ {
+ // Although MPUnansweredQ is only really used for multi-packet query processing,
+ // we increment it for both single-packet and multi-packet queries, so that it stays in sync
+ // with the MPUnansweredKA value, which by necessity is incremented for both query types.
+ rr->MPUnansweredQ++;
+ rr->MPLastUnansweredQT = m->timenow;
+ rr->MPExpectingKA = mDNStrue;
+ }
+ }
+
+ // Check if this question is the same as any of mine.
+ // We only do this for non-truncated queries. Right now it would be too complicated to try
+ // to keep track of duplicate suppression state between multiple packets, especially when we
+ // can't guarantee to receive all of the Known Answer packets that go with a particular query.
+ if (!(query->h.flags.b[0] & kDNSFlag0_TC))
+ for (q = m->Questions; q; q=q->next)
+ if (ActiveQuestion(q) && m->timenow - q->LastQTxTime > mDNSPlatformOneSecond / 4)
+ if (!q->InterfaceID || q->InterfaceID == InterfaceID)
+ if (q->NextInDQList == mDNSNULL && dqp != &q->NextInDQList)
+ if (q->qtype == pktq.qtype && q->qclass == pktq.qclass && q->qnamehash == pktq.qnamehash && SameDomainName(&q->qname, &pktq.qname))
+ { *dqp = q; dqp = &q->NextInDQList; }
+ }
}
// ***
// ***
for (rr = m->ResourceRecords; rr; rr=rr->next) // Now build our list of potential answers
if (rr->NR_AnswerTo) // If we marked the record...
- if (AddRecordToResponseList(nrp, rr, rr->NR_AnswerTo, mDNSNULL)) // ... add it to the list
- {
- nrp = &rr->NextResponse;
- if (rr->RecordType == kDNSRecordTypeShared) delayresponse = mDNStrue;
- }
+ {
+ AddRecordToResponseList(&nrp, rr, mDNSNULL); // ... add it to the list
+ if (rr->resrec.RecordType == kDNSRecordTypeShared) delayresponse = mDNStrue;
+ }
// ***
// *** 3. Add additional records
{
// (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, InterfaceAddr) &&
- AddRecordToResponseList(nrp, rr->Additional1, mDNSNULL, rr))
- nrp = &rr->Additional1->NextResponse;
+ if (rr->Additional1 && ResourceRecordIsValidInterfaceAnswer(rr->Additional1, InterfaceID))
+ AddRecordToResponseList(&nrp, rr->Additional1, rr);
- if (rr->Additional2 && ResourceRecordIsValidInterfaceAnswer(rr->Additional2, InterfaceAddr) &&
- AddRecordToResponseList(nrp, rr->Additional2, mDNSNULL, rr))
- nrp = &rr->Additional2->NextResponse;
+ 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->rrtype == kDNSType_SRV)
+ if (rr->resrec.rrtype == kDNSType_SRV)
for (rr2=m->ResourceRecords; rr2; rr2=rr2->next) // Scan list of resource records
- if (rr2->rrtype == kDNSType_A && // For all records type "A" ...
- ResourceRecordIsValidInterfaceAnswer(rr2, InterfaceAddr) && // ... which are valid for answer ...
- SameDomainName(&rr->rdata->u.srv.target, &rr2->name) && // ... whose name is the name of the SRV target
- AddRecordToResponseList(nrp, rr2, mDNSNULL, rr))
- nrp = &rr2->NextResponse;
+ if (RRIsAddressType(rr2) && // For all address records (A/AAAA) ...
+ ResourceRecordIsValidInterfaceAnswer(rr2, InterfaceID) && // ... which are valid for answer ...
+ rr->resrec.rdnamehash == rr2->resrec.namehash &&
+ SameDomainName(&rr->resrec.rdata->u.srv.target, &rr2->resrec.name)) // ... whose name is the name of the SRV target
+ AddRecordToResponseList(&nrp, rr2, rr);
}
// ***
- // *** 4. Parse Answer Section and cancel any records disallowed by duplicate suppression
+ // *** 4. Parse Answer Section and cancel any records disallowed by Known-Answer list
// ***
for (i=0; i<query->h.numAnswers; i++) // For each record in the query's answer section...
{
// Get the record...
- ResourceRecord pktrr, *rr;
- ptr = getResourceRecord(query, ptr, end, InterfaceAddr, timenow, kDNSRecordTypePacketAnswer, &pktrr, mDNSNULL);
+ LargeCacheRecord pkt;
+ AuthRecord *rr;
+ CacheRecord *ourcacherr;
+ ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt);
if (!ptr) goto exit;
- // See if it suppresses any of our planned answers
+ // See if this Known-Answer suppresses any of our currently planned answers
for (rr=ResponseRecords; rr; rr=rr->NextResponse)
- if (MustSendRecord(rr) && SuppressDuplicate(&pktrr, rr))
+ if (MustSendRecord(rr) && ShouldSuppressKnownAnswer(&pkt.r, rr))
{ rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; }
- // And see if it suppresses any previously scheduled answers
+ // See if this Known-Answer suppresses any previously scheduled answers (for multi-packet KA suppression)
for (rr=m->ResourceRecords; rr; rr=rr->next)
{
- // If this record has been requested by exactly one client, and that client is
- // the same one sending this query, then allow inter-packet duplicate suppression
- if (rr->Requester.NotAnInteger && rr->Requester.NotAnInteger == srcaddr.NotAnInteger)
- if (SuppressDuplicate(&pktrr, rr))
+ // 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 (srcaddr->type == mDNSAddrType_IPv4)
{
- rr->SendPriority = 0;
- rr->Requester = zeroIPAddr;
+ if (mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = zeroIPAddr;
}
+ else if (srcaddr->type == mDNSAddrType_IPv6)
+ {
+ if (mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = zerov6Addr;
+ }
+ if (mDNSIPv4AddressIsZero(rr->v4Requester) && mDNSIPv6AddressIsZero(rr->v6Requester)) rr->ImmedAnswer = mDNSNULL;
+ }
+ }
+
+ // 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);
+ if (ourcacherr && ourcacherr->MPExpectingKA && m->timenow - ourcacherr->MPLastUnansweredQT < mDNSPlatformOneSecond)
+ {
+ ourcacherr->MPUnansweredKA++;
+ ourcacherr->MPExpectingKA = mDNSfalse;
+ }
+
+ // Having built our ExpectedAnswers list from the questions in this packet, we can definitively
+ // remove from our ExpectedAnswers list any records that are suppressed in the very same packet.
+ // For answers that are suppressed in subsequent KA list packets, we rely on the MPQ/MPKA counting to track them.
+ eap = &ExpectedAnswers;
+ while (*eap)
+ {
+ CacheRecord *rr = *eap;
+ if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&pkt.r.resrec, &rr->resrec))
+ { *eap = rr->NextInKAList; rr->NextInKAList = mDNSNULL; }
+ else eap = &rr->NextInKAList;
+ }
+
+ // See if this Known-Answer is a surprise to us. If so, we shouldn't suppress our own query.
+ if (!ourcacherr)
+ {
+ dqp = &DupQuestions;
+ while (*dqp)
+ {
+ DNSQuestion *q = *dqp;
+ if (ResourceRecordAnswersQuestion(&pkt.r.resrec, q))
+ { *dqp = q->NextInDQList; q->NextInDQList = mDNSNULL; }
+ else dqp = &q->NextInDQList;
+ }
}
}
// ***
for (rr=ResponseRecords; rr; rr=rr->NextResponse)
{
- if (MustSendRecord(rr))
+ if (rr->NR_AnswerTo)
{
- // For oversized records which we are going to send back to the requester via unicast
- // anyway, don't waste network bandwidth by also sending them via multicast.
- // This means we lose passive conflict detection for these oversized records, but
- // that is a reasonable tradeoff -- these large records usually have an associated
- // SRV record with the same name which will catch conflicts for us anyway.
- mDNSBool LargeRecordWithUnicastReply = (rr->rdestimate > 1024 && replyunicast);
-
- if (rr->NR_AnswerTo)
- answers = mDNStrue;
-
- if (replymulticast && !LargeRecordWithUnicastReply)
+ mDNSBool SendMulticastResponse = mDNSfalse;
+
+ // 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 (SendMulticastResponse)
{
- // If this query has additional duplicate suppression info
- // coming in another packet, then remember the requesting IP address
- if (query->h.flags.b[0] & kDNSFlag0_TC)
- {
- // We can only store one IP address at a time per record, so if we've already
- // stored one address, set it to some special distinguished value instead
- if (rr->Requester.NotAnInteger == zeroIPAddr.NotAnInteger) rr->Requester = srcaddr;
- else rr->Requester = onesIPAddr;
- }
- if (rr->NR_AnswerTo)
+ // If we're already planning to send this on another interface, just send it on all interfaces
+ if (rr->ImmedAnswer && rr->ImmedAnswer != InterfaceID)
{
- // This is a direct answer in response to one of the questions
- rr->SendPriority = kDNSSendPriorityAnswer;
+ rr->ImmedAnswer = mDNSInterfaceMark;
+ m->NextScheduledResponse = m->timenow;
+ debugf("ProcessQuery: %##s (%s) : Will send on all interfaces", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype));
}
else
{
- // This is an additional record supporting one of our answers
- if (rr->SendPriority < kDNSSendPriorityAdditional)
- rr->SendPriority = kDNSSendPriorityAdditional;
+ rr->ImmedAnswer = InterfaceID; // Record interface to send it on
+ m->NextScheduledResponse = m->timenow;
+ if (srcaddr->type == mDNSAddrType_IPv4)
+ {
+ if (mDNSIPv4AddressIsZero(rr->v4Requester)) rr->v4Requester = srcaddr->ip.v4;
+ else if (!mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = onesIPv4Addr;
+ }
+ else if (srcaddr->type == mDNSAddrType_IPv6)
+ {
+ if (mDNSIPv6AddressIsZero(rr->v6Requester)) rr->v6Requester = srcaddr->ip.v6;
+ else if (!mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = onesIPv6Addr;
+ }
}
}
}
+ else if (rr->NR_AdditionalTo && rr->NR_AdditionalTo->NR_AnswerTo == (mDNSu8*)~0)
+ {
+ // Since additional records are an optimization anyway, we only ever send them on one interface at a time
+ // If two clients on different interfaces do queries that invoke the same optional additional answer,
+ // then the earlier client is out of luck
+ rr->ImmedAdditional = InterfaceID;
+ // No need to set m->NextScheduledResponse here
+ // We'll send these additional records when we send them, or not, as the case may be
+ }
}
// ***
if (delayresponse && !m->SuppressSending)
{
// Pick a random delay between 20ms and 120ms.
- m->SuppressSending = timenow + (mDNSPlatformOneSecond*2 + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*10)) / 100;
+ m->SuppressSending = m->timenow + (mDNSPlatformOneSecond*2 + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*10)) / 100;
if (m->SuppressSending == 0) m->SuppressSending = 1;
}
// ***
- // *** 8. If query is from a legacy client, generate a unicast reply too
+ // *** 8. If query is from a legacy client, generate a unicast response too
// ***
- if (answers && replyunicast)
- responseptr = GenerateUnicastResponse(query, end, InterfaceAddr, replyunicast, ResponseRecords);
+ if (HaveUnicastAnswer)
+ responseptr = GenerateUnicastResponse(query, end, InterfaceID, LegacyQuery, response, ResponseRecords);
exit:
// ***
- // *** 9. Finally, clear our NextResponse link chain ready for use next time
+ // *** 9. Finally, clear our link chains ready for use next time
// ***
while (ResponseRecords)
{
rr->NR_AdditionalTo = mDNSNULL;
}
+ while (ExpectedAnswers)
+ {
+ CacheRecord *rr;
+ rr = ExpectedAnswers;
+ ExpectedAnswers = rr->NextInKAList;
+ rr->NextInKAList = mDNSNULL;
+
+ // For non-truncated queries, we can definitively say that we should expect
+ // to be seeing a response for any records still left in the ExpectedAnswers list
+ if (!(query->h.flags.b[0] & kDNSFlag0_TC))
+ if (rr->UnansweredQueries == 0 || m->timenow - rr->LastUnansweredTime >= mDNSPlatformOneSecond)
+ {
+ rr->UnansweredQueries++;
+ 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));
+ SetNextCacheCheckTime(m, rr);
+ }
+
+ // If we've seen multiple unanswered queries for this record,
+ // then mark it to expire in five seconds if we don't get a response by then.
+ if (rr->UnansweredQueries >= MaxUnansweredQueries)
+ {
+ // 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));
+ mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer);
+ }
+ // Make a guess, based on the multi-packet query / known answer counts, whether we think we
+ // should have seen an answer for this. (We multiply MPQ by 4 and MPKA by 5, to allow for
+ // possible packet loss of up to 20% of the additional KA packets.)
+ else if (rr->MPUnansweredQ * 4 > rr->MPUnansweredKA * 5 + 8)
+ {
+ // We want to do this conservatively.
+ // If there are so many machines on the network that they have to use multi-packet known-answer lists,
+ // then we don't want them to all hit the network simultaneously with their final expiration queries.
+ // By setting the record to expire in four minutes, we achieve two things:
+ // (a) the 90-95% final expiration queries will be less bunched together
+ // (b) we allow some time for us to witness enough other failed queries that we don't have to do our own
+ mDNSu32 remain = (mDNSu32)(RRExpireTime(rr) - m->timenow) / 4;
+ if (remain > 240 * (mDNSu32)mDNSPlatformOneSecond)
+ remain = 240 * (mDNSu32)mDNSPlatformOneSecond;
+
+ // 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));
+
+ if (remain <= 60 * (mDNSu32)mDNSPlatformOneSecond)
+ rr->UnansweredQueries++; // Treat this as equivalent to one definite unanswered query
+ rr->MPUnansweredQ = 0; // Clear MPQ/MPKA statistics
+ rr->MPUnansweredKA = 0;
+ rr->MPExpectingKA = mDNSfalse;
+
+ if (remain < kDefaultReconfirmTimeForNoAnswer)
+ remain = kDefaultReconfirmTimeForNoAnswer;
+ mDNS_Reconfirm_internal(m, rr, remain);
+ }
+ }
+
+ while (DupQuestions)
+ {
+ int i;
+ DNSQuestion *q = DupQuestions;
+ DupQuestions = q->NextInDQList;
+ q->NextInDQList = mDNSNULL;
+ i = RecordDupSuppressInfo(q->DupSuppress, m->timenow, InterfaceID, srcaddr->type);
+ debugf("ProcessQuery: Recorded DSI for %##s (%s) on %p/%s %d", q->qname.c, DNSTypeName(q->qtype), InterfaceID,
+ srcaddr->type == mDNSAddrType_IPv4 ? "v4" : "v6", i);
+ }
+
return(responseptr);
}
mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end,
- const mDNSIPAddr srcaddr, const mDNSIPPort srcport, const mDNSIPAddr dstaddr, mDNSIPPort dstport, const mDNSIPAddr InterfaceAddr)
+ const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport,
+ const mDNSInterfaceID InterfaceID)
{
- const mDNSs32 timenow = mDNSPlatformTimeNow();
DNSMessage response;
const mDNSu8 *responseend = mDNSNULL;
- DNSMessage *replyunicast = mDNSNULL;
- mDNSBool replymulticast = mDNSfalse;
-
- verbosedebugf("Received Query from %.4a:%d to %.4a:%d on %.4a with %d Question%s, %d Answer%s, %d Authorit%s, %d Additional%s",
- &srcaddr, (mDNSu16)srcport.b[0]<<8 | srcport.b[1],
- &dstaddr, (mDNSu16)dstport.b[0]<<8 | dstport.b[1],
- &InterfaceAddr,
- 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");
-
- // If this was a unicast query, or it was from an old (non-port-5353) client, then send a unicast response
- if (dstaddr.NotAnInteger != AllDNSLinkGroup.NotAnInteger || srcport.NotAnInteger != MulticastDNSPort.NotAnInteger)
- replyunicast = &response;
- // If this was a multicast query, then we need to send a multicast response
- if (dstaddr.NotAnInteger == AllDNSLinkGroup.NotAnInteger) replymulticast = mDNStrue;
+ verbosedebugf("Received Query from %#-15a:%d to %#-15a:%d on 0x%.8X with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s",
+ srcaddr, (mDNSu16)srcport.b[0]<<8 | srcport.b[1],
+ dstaddr, (mDNSu16)dstport.b[0]<<8 | dstport.b[1],
+ 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");
- responseend = ProcessQuery(m, msg, end, srcaddr, InterfaceAddr, replyunicast, replymulticast, timenow);
- if (replyunicast && responseend)
+ responseend = ProcessQuery(m, msg, end, srcaddr, InterfaceID,
+ (srcport.NotAnInteger != MulticastDNSPort.NotAnInteger), mDNSAddrIsDNSMulticast(dstaddr), &response);
+
+ if (responseend) // If responseend is non-null, that means we built a unicast response packet
{
- mDNSSendDNSMessage(m, replyunicast, responseend, InterfaceAddr, dstport, srcaddr, srcport);
- verbosedebugf("Unicast Response: %d Answer%s, %d Additional%s on %.4a",
- replyunicast->h.numAnswers, replyunicast->h.numAnswers == 1 ? "" : "s",
- replyunicast->h.numAdditionals, replyunicast->h.numAdditionals == 1 ? "" : "s", &InterfaceAddr);
+ 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",
+ srcaddr, (mDNSu16)srcport.b[0]<<8 | srcport.b[1], InterfaceID, srcaddr->type);
+ mDNSSendDNSMessage(m, &response, responseend, InterfaceID, dstport, srcaddr, srcport);
}
}
// the record list and/or question list.
// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
- const DNSMessage *const response, const mDNSu8 *end, const mDNSIPAddr dstaddr, const mDNSIPAddr InterfaceAddr)
+ const DNSMessage *const response, const mDNSu8 *end, const mDNSAddr *srcaddr, const mDNSAddr *dstaddr,
+ const mDNSInterfaceID InterfaceID, mDNSu8 ttl)
{
int i;
- const mDNSs32 timenow = mDNSPlatformTimeNow();
-
- // We ignore questions (if any) in a DNS response packet
- const mDNSu8 *ptr = LocateAnswers(response, end);
-
+ 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 replies, then the only credible protection against that is cryptographic
+ // 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;
- verbosedebugf("Received Response addressed to %.4a on %.4a with %d Question%s, %d Answer%s, %d Authorit%s, %d Additional%s",
- &dstaddr, &InterfaceAddr,
- 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",
+ (void)srcaddr; // Currently used only for display in debugging message
+
+ 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,
+ 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");
- // Other mDNS devices may issue unicast queries (which we correctly answer),
- // but we never *issue* unicast queries, so if we ever receive a unicast
- // response then it is someone trying to spoof us, so ignore it!
- if (dstaddr.NotAnInteger != AllDNSLinkGroup.NotAnInteger)
- { debugf("** Ignored attempted spoof unicast mDNS response packet **"); return; }
+ // 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))
+ {
+ debugf("** Ignored apparent spoof mDNS Response from %#-15a to %#-15a TTL %d on %p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s",
+ srcaddr, dstaddr, ttl, 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");
+ return;
+ }
for (i = 0; i < totalrecords && ptr && ptr < end; i++)
{
- ResourceRecord pktrr;
- mDNSu8 RecordType = (i < response->h.numAnswers) ? kDNSRecordTypePacketAnswer : kDNSRecordTypePacketAdditional;
- ptr = getResourceRecord(response, ptr, end, InterfaceAddr, timenow, RecordType, &pktrr, mDNSNULL);
- if (!ptr) return;
+ 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
// 1. Check that this packet resource record does not conflict with any of ours
- if (m->CurrentRecord) debugf("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set");
+ if (m->CurrentRecord) LogMsg("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set");
m->CurrentRecord = m->ResourceRecords;
while (m->CurrentRecord)
{
- ResourceRecord *rr = m->CurrentRecord;
+ AuthRecord *rr = m->CurrentRecord;
m->CurrentRecord = rr->next;
- if (SameResourceRecordSignature(&pktrr, rr)) // If interface, name, type and class match...
- { // ... check to see if rdata is identical
- if (SameRData(pktrr.rrtype, pktrr.rdata, rr->rdata))
+ if (PacketRRMatchesSignature(&pkt.r, rr)) // If interface, name, type (if verified) and class match...
+ {
+ // ... check to see if rdata is identical
+ if (SameRData(&pkt.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 (pktrr.rroriginalttl >= rr->rroriginalttl || m->SleepState)
- rr->SendPriority = kDNSSendPriorityNone;
+ if (pkt.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;
+ }
else
- rr->SendPriority = kDNSSendPriorityAnswer;
+ {
+ if (rr->ImmedAnswer == mDNSNULL) { rr->ImmedAnswer = InterfaceID; m->NextScheduledResponse = m->timenow; }
+ else if (rr->ImmedAnswer != InterfaceID) { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; }
+ }
}
else
{
// else, the packet RR has different rdata -- check to see if this is a conflict
- if (pktrr.rroriginalttl > 0 && PacketRRConflict(m, rr, &pktrr))
+ if (pkt.r.resrec.rroriginalttl > 0 && PacketRRConflict(m, rr, &pkt.r))
{
- if (rr->rrtype == kDNSType_SRV)
- {
- debugf("mDNSCoreReceiveResponse: Our Data %d %##s", rr->rdata->RDLength, rr->rdata->u.srv.target.c);
- debugf("mDNSCoreReceiveResponse: Pkt Data %d %##s", pktrr.rdata->RDLength, pktrr.rdata->u.srv.target.c);
- }
- else if (rr->rrtype == kDNSType_TXT)
- {
- debugf("mDNSCoreReceiveResponse: Our Data %d %#s", rr->rdata->RDLength, rr->rdata->u.txt.c);
- debugf("mDNSCoreReceiveResponse: Pkt Data %d %#s", pktrr.rdata->RDLength, pktrr.rdata->u.txt.c);
- }
- else if (rr->rrtype == kDNSType_A)
- {
- debugf("mDNSCoreReceiveResponse: Our Data %.4a", &rr->rdata->u.ip);
- debugf("mDNSCoreReceiveResponse: Pkt Data %.4a", &pktrr.rdata->u.ip);
- }
+ 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));
+
+ // 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 (rr->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->name.c, DNSTypeName(rr->rrtype));
- // If we'd previously verified this record, put it back to probing state and try again
- rr->RecordType = kDNSRecordTypeUnique;
- rr->ProbeCount = DefaultProbeCountForTypeUnique + 1;
- rr->NextSendTime = timenow;
- rr->NextSendInterval = DefaultSendIntervalForRecordType(kDNSRecordTypeUnique);
- m->ProbeFailTime = timenow;
- // If we've had ten 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) m->NumFailedProbes++;
- else m->SuppressProbes = (timenow + mDNSPlatformOneSecond * 5) | 1;
+ 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
}
- else
+ // 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: Will rename %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype));
- // If we're probing for this record (or we assumed it must be unique) we just failed
- mDNS_Deregister_internal(m, rr, timenow, mDNS_Dereg_conflict);
+ 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; }
}
}
}
// 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
{
- ResourceRecord *rr;
+ mDNSu32 slot = HashSlot(&pkt.r.resrec.name);
+ CacheRecord *rr;
// 2a. Check if this packet resource record is already in our cache
- for (rr = m->rrcache; rr; rr=rr->next)
+ for (rr = m->rrcache_hash[slot]; rr; rr=rr->next)
{
// If we found this exact resource record, refresh its TTL
- if (IdenticalResourceRecord(&pktrr, rr))
+ if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&pkt.r.resrec, &rr->resrec))
{
- //debugf("Found RR %##s size %d already in cache", pktrr.name.c, pktrr.rdata->RDLength);
- rr->TimeRcvd = timenow;
- rr->UnansweredQueries = 0;
- rr->NewData = mDNStrue;
- // If we're deleting a record, push it out one second into the future
- // to give other hosts on the network a chance to protest
- if (pktrr.rroriginalttl == 0) rr->rroriginalttl = 1;
- else rr->rroriginalttl = pktrr.rroriginalttl;
+ if (pkt.r.resrec.rdlength > InlineCacheRDSize)
+ verbosedebugf("Found record size %5d interface %p already in cache: %s",
+ pkt.r.resrec.rdlength, InterfaceID, GetRRDisplayString(m, &pkt.r));
+ rr->TimeRcvd = m->timenow;
+
+ if (pkt.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)
+ { *cfp = rr; cfp = &rr->NextInCFList; }
+
+ // If this packet record is marked unique, and our previous cached copy was not, then fix it
+ if (!(rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask))
+ {
+ DNSQuestion *q;
+ for (q = m->Questions; q; q=q->next) if (ResourceRecordAnswersQuestion(&rr->resrec, q)) q->UniqueAnswers++;
+ rr->resrec.RecordType = pkt.r.resrec.RecordType;
+ }
+ }
+
+ if (pkt.r.resrec.rroriginalttl > 0)
+ {
+ rr->resrec.rroriginalttl = pkt.r.resrec.rroriginalttl;
+ rr->UnansweredQueries = 0;
+ rr->MPUnansweredQ = 0;
+ rr->MPUnansweredKA = 0;
+ rr->MPExpectingKA = mDNSfalse;
+ }
+ else
+ {
+ // If the packet TTL is zero, that means we're deleting this record.
+ // To give other hosts on the network a chance to protest, we push the deletion
+ // out one second into the future. Also, we set UnansweredQueries to MaxUnansweredQueries.
+ // Otherwise, we'll do final queries for this record at 80% and 90% of its apparent
+ // lifetime (800ms and 900ms from now) which is a pointless waste of network bandwidth.
+ rr->resrec.rroriginalttl = 1;
+ rr->UnansweredQueries = MaxUnansweredQueries;
+ }
+ SetNextCacheCheckTime(m, rr);
break;
}
}
// 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 && pktrr.rroriginalttl > 0)
+ if (!rr && pkt.r.resrec.rroriginalttl > 0)
{
- rr = GetFreeCacheRR(m, timenow);
- if (!rr) debugf("No cache space to add record for %#s", pktrr.name.c);
+ rr = GetFreeCacheRR(m, pkt.r.resrec.rdlength);
+ if (!rr) debugf("No cache space to add record for %#s", pkt.r.resrec.name.c);
else
{
- *rr = pktrr;
- rr->rdata = &rr->rdatastorage; // For now, all cache records use local storage
- rr->next = m->rrcache;
- m->rrcache = rr;
- if ((rr->RecordType & kDNSRecordTypeUniqueMask) == 0)
- TriggerImmediateQuestions(m, rr, timenow);
- //debugf("Adding RR %##s to cache (%d)", pktrr.name.c, m->rrcache_used);
- AnswerLocalQuestions(m, rr, timenow);
+ 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
+ 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);
+ rr->next = m->rrcache_hash[slot];
+ m->rrcache_hash[slot] = rr;
+ m->rrcache_used[slot]++;
+ //debugf("Adding RR %##s to cache (%d)", pkt.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);
}
}
}
}
- // If we have a cache, then run through all the new records that we've just added,
- // clear their 'NewData' flags, and if they were marked as unique in the packet,
- // then search our cache for any records with the same name/type/class,
- // and purge them if they are more than one second old.
- if (m->rrcache_size)
+ // 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)
{
- ResourceRecord *rr;
- for (rr = m->rrcache; rr; rr=rr->next)
- {
- if (rr->NewData)
+ CacheRecord *r1 = CacheFlushRecords, *r2;
+ CacheFlushRecords = CacheFlushRecords->NextInCFList;
+ r1->NextInCFList = mDNSNULL;
+ for (r2 = m->rrcache_hash[HashSlot(&r1->resrec.name)]; r2; r2=r2->next)
+ if (SameResourceRecordSignature(&r1->resrec, &r2->resrec) && m->timenow - r2->TimeRcvd > mDNSPlatformOneSecond)
{
- rr->NewData = mDNSfalse;
- if (rr->RecordType & kDNSRecordTypeUniqueMask)
- {
- ResourceRecord *r;
- for (r = m->rrcache; r; r=r->next)
- if (SameResourceRecordSignature(rr, r) && timenow - r->TimeRcvd > mDNSPlatformOneSecond)
- r->rroriginalttl = 0;
- }
+ verbosedebugf("Cache flush %p X %p %##s (%s)", r1, r2, r2->resrec.name.c, DNSTypeName(r2->resrec.rrtype));
+ // We set stale records to expire in one second.
+ // This gives the owner a chance to rescue it if necessary.
+ // This is important in the case of multi-homing and bridged networks:
+ // Suppose host X is on Ethernet. X then connects to an AirPort base station, which happens to be
+ // bridged onto the same Ethernet. When X announces its AirPort IP address with the cache-flush bit
+ // set, the AirPort packet will be bridged onto the Ethernet, and all other hosts on the Ethernet
+ // will promptly delete their cached copies of the (still valid) Ethernet IP address record.
+ // By delaying the deletion by one second, we give X a change to notice that this bridging has
+ // happened, and re-announce its Ethernet IP address to rescue it from deletion from all our caches.
+ // We set UnansweredQueries to MaxUnansweredQueries to avoid expensive and unnecessary
+ // final expiration queries for this record.
+ r2->resrec.rroriginalttl = 1;
+ r2->TimeRcvd = m->timenow;
+ r2->UnansweredQueries = MaxUnansweredQueries;
+ SetNextCacheCheckTime(m, r2);
}
- }
- TidyRRCache(m, timenow);
}
}
mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end,
- mDNSIPAddr srcaddr, mDNSIPPort srcport, mDNSIPAddr dstaddr, mDNSIPPort dstport, mDNSIPAddr InterfaceAddr)
+ const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport,
+ const mDNSInterfaceID InterfaceID, mDNSu8 ttl)
{
- const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery;
- const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery;
- mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask);
+ const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery;
+ const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery;
+ const mDNSu8 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;
msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]);
msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]);
- if (!m) { debugf("mDNSCoreReceive ERROR m is NULL"); return; }
+ if (!m) { LogMsg("mDNSCoreReceive ERROR m is NULL"); return; }
+
+ // We use zero addresses and all-ones addresses at various places in the code to indicate special values like "no address"
+ // 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; }
mDNS_Lock(m);
- if (m->mDNS_busy > 1) debugf("mDNSCoreReceive: Locking failure! mDNS already busy");
-
- if (QR_OP == StdQ) mDNSCoreReceiveQuery (m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceAddr);
- else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, dstaddr, InterfaceAddr);
+ if (QR_OP == StdQ) mDNSCoreReceiveQuery (m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID);
+ else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, srcaddr, dstaddr, InterfaceID, ttl);
else debugf("Unknown DNS packet type %02X%02X (ignored)", msg->h.flags.b[0], msg->h.flags.b[1]);
// Packet reception often causes a change to the task list:
}
// ***************************************************************************
-#if 0
+#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark -
#pragma mark - Searcher Functions
// This prevents circular references, where two questions are each marked as a duplicate of the other.
// Accordingly, we break out of the loop when we get to 'question', because there's no point searching
// further in the list.
- for (q = m->ActiveQuestions; q && q != question; q=q->next) // Scan our list of questions
- if (q->InterfaceAddr.NotAnInteger == question->InterfaceAddr.NotAnInteger && // for another question with the same InterfaceID,
- q->rrtype == question->rrtype && // type,
- q->rrclass == question->rrclass && // class,
- SameDomainName(&q->name, &question->name)) // and name
+ for (q = m->Questions; q && q != question; q=q->next) // Scan our list of questions
+ if (q->InterfaceID == question->InterfaceID && // for another question with the same InterfaceID,
+ q->qtype == question->qtype && // type,
+ q->qclass == question->qclass && // class,
+ q->qnamehash == question->qnamehash &&
+ SameDomainName(&q->qname, &question->qname)) // and name
return(q);
return(mDNSNULL);
}
// This is called after a question is deleted, in case other identical questions were being
// suppressed as duplicates
-mDNSlocal void UpdateQuestionDuplicates(const mDNS *const m, const DNSQuestion *const question)
+mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, const DNSQuestion *const question)
{
DNSQuestion *q;
- for (q = m->ActiveQuestions; q; q=q->next) // Scan our list of questions
+ 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->NextQTime = question->NextQTime;
q->ThisQInterval = question->ThisQInterval;
- q->NextQInterval = question->NextQInterval;
+ q->LastQTime = question->LastQTime;
+ q->RecentAnswers = 0;
q->DuplicateOf = FindDuplicateQuestion(m, q);
+ q->LastQTxTime = question->LastQTxTime;
+ SetNextQueryTime(m,q);
}
}
-mDNSlocal mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question, const mDNSs32 timenow)
+mDNSlocal mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question)
{
+#if TEST_LOCALONLY_FOR_EVERYTHING
+ question->InterfaceID = (mDNSInterfaceID)~0;
+#endif
if (m->rrcache_size == 0) // Can't do queries if we have no cache space allocated
return(mStatus_NoCache);
else
{
- DNSQuestion **q = &m->ActiveQuestions;
+ int i;
+ // Note: It important that new questions are appended at the *end* of the list, not prepended at the start
+ DNSQuestion **q = &m->Questions;
+ if (question->InterfaceID == ((mDNSInterfaceID)~0)) q = &m->LocalOnlyQuestions;
while (*q && *q != question) q=&(*q)->next;
if (*q)
{
- debugf("Error! Tried to add a question that's already in the active list");
+ LogMsg("Error! Tried to add a question %##s (%s) that's already in the active list",
+ question->qname.c, DNSTypeName(question->qtype));
return(mStatus_AlreadyRegistered);
}
- if (question->InterfaceAddr.NotAnInteger)
+ // If this question is referencing a specific interface, make sure it exists
+ if (question->InterfaceID && question->InterfaceID != ((mDNSInterfaceID)~0))
{
- NetworkInterfaceInfo *p = m->HostInterfaces;
- while (p && p->ip.NotAnInteger != question->InterfaceAddr.NotAnInteger) p=p->next;
- if (!p)
+ NetworkInterfaceInfo *intf;
+ for (intf = m->HostInterfaces; intf; intf = intf->next)
+ if (intf->InterfaceID == question->InterfaceID) break;
+ if (!intf)
{
- LogErrorMessage("mDNS_StartQuery_internal: question->InterfaceAddr %.4a not found in interface list", &question->InterfaceAddr);
- question->InterfaceAddr.NotAnInteger = 0;
+ debugf("mDNS_StartQuery_internal: Question %##s InterfaceID %p not found", question->qname.c, question->InterfaceID);
+ return(mStatus_BadReferenceErr);
}
}
- question->next = mDNSNULL;
- question->NextQTime = timenow;
- question->ThisQInterval = mDNSPlatformOneSecond; // MUST NOT be zero for an active question
- question->NextQInterval = mDNSPlatformOneSecond;
- question->DuplicateOf = FindDuplicateQuestion(m, question);
+ // 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);
+ }
+
+ 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;
+ for (i=0; i<DupSuppressInfoSize; i++)
+ question->DupSuppress[i].InterfaceID = mDNSNULL;
+ // question->InterfaceID must be already set by caller
+ 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);
+ else
+ verbosedebugf("mDNS_StartQuery_internal: Question %##s %s %p (%p) duplicate of (%p)",
+ question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, question, question->DuplicateOf);
+
*q = question;
-
- if (!m->NewQuestions) m->NewQuestions = question;
+ if (question->InterfaceID == ((mDNSInterfaceID)~0))
+ {
+ if (!m->NewLocalOnlyQuestions) m->NewLocalOnlyQuestions = question;
+ }
+ else
+ {
+ if (!m->NewQuestions) m->NewQuestions = question;
+ SetNextQueryTime(m,question);
+ }
return(mStatus_NoError);
}
}
-mDNSlocal void mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const question)
+mDNSlocal mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const question)
{
- DNSQuestion **q = &m->ActiveQuestions;
+ CacheRecord *rr;
+ DNSQuestion **q = &m->Questions;
+ if (question->InterfaceID == ((mDNSInterfaceID)~0)) q = &m->LocalOnlyQuestions;
while (*q && *q != question) q=&(*q)->next;
if (*q) *q = (*q)->next;
- else debugf("mDNS_StopQuery_internal: Question %##s (%s) not found in active list",
- question->name.c, DNSTypeName(question->rrtype));
+ else
+ {
+ if (question->ThisQInterval >= 0) // Only log error message if the query was supposed to be active
+ LogMsg("mDNS_StopQuery_internal: Question %##s (%s) not found in active list",
+ question->qname.c, DNSTypeName(question->qtype));
+ return(mStatus_BadReferenceErr);
+ }
+ // Take care to cut question from list *before* calling UpdateQuestionDuplicates
UpdateQuestionDuplicates(m, question);
-
+ // But don't trash ThisQInterval until afterwards.
question->ThisQInterval = -1;
- question->NextQInterval = -1;
-
- // If we just deleted the question that AnswerLocalQuestions() is about to look at,
+
+ // If there are any cache records referencing this as their active question, then see if any other
+ // question that is also referencing them, else their CRActiveQuestion needs to get set to NULL.
+ for (rr = m->rrcache_hash[HashSlot(&question->qname)]; rr; rr=rr->next)
+ {
+ if (rr->CRActiveQuestion == question)
+ {
+ DNSQuestion *q;
+ 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);
+ 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
+ }
+ }
+
+ // If we just deleted the question that CacheRecordAdd() or CacheRecordRmv()is about to look at,
// bump its pointer forward one question.
if (m->CurrentQuestion == question)
{
- debugf("mDNS_StopQuery_internal: Just deleted the currently active question.");
- m->CurrentQuestion = m->CurrentQuestion->next;
+ debugf("mDNS_StopQuery_internal: Just deleted the currently active question: %##s (%s)",
+ question->qname.c, DNSTypeName(question->qtype));
+ m->CurrentQuestion = question->next;
}
- if (m->NewQuestions == question)
+ if (m->NewQuestions == question)
{
- debugf("mDNS_StopQuery_internal: Just deleted a new question that wasn't even answered yet.");
- m->NewQuestions = m->NewQuestions->next;
+ debugf("mDNS_StopQuery_internal: Just deleted a new question that wasn't even answered yet: %##s (%s)",
+ question->qname.c, DNSTypeName(question->qtype));
+ m->NewQuestions = question->next;
}
-
+
+ if (m->NewLocalOnlyQuestions == question) m->NewLocalOnlyQuestions = question->next;
+
// Take care not to trash question->next until *after* we've updated m->CurrentQuestion and m->NewQuestions
question->next = mDNSNULL;
+ return(mStatus_NoError);
}
mDNSexport mStatus mDNS_StartQuery(mDNS *const m, DNSQuestion *const question)
{
- const mDNSs32 timenow = mDNS_Lock(m);
- mStatus status = mDNS_StartQuery_internal(m, question, timenow);
+ mStatus status;
+ mDNS_Lock(m);
+ status = mDNS_StartQuery_internal(m, question);
+ mDNS_Unlock(m);
+ return(status);
+ }
+
+mDNSexport mStatus mDNS_StopQuery(mDNS *const m, DNSQuestion *const question)
+ {
+ mStatus status;
+ mDNS_Lock(m);
+ status = mDNS_StopQuery_internal(m, question);
+ mDNS_Unlock(m);
+ return(status);
+ }
+
+mDNSexport mStatus mDNS_Reconfirm(mDNS *const m, CacheRecord *const rr)
+ {
+ mStatus status;
+ mDNS_Lock(m);
+ status = mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer);
mDNS_Unlock(m);
return(status);
}
-mDNSexport void mDNS_StopQuery(mDNS *const m, DNSQuestion *const question)
+mDNSexport mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr)
{
+ mStatus status = mStatus_BadReferenceErr;
+ CacheRecord *cr;
mDNS_Lock(m);
- mDNS_StopQuery_internal(m, question);
+ cr = FindIdenticalRecordInCache(m, rr);
+ if (cr) status = mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer);
mDNS_Unlock(m);
+ return(status);
}
mDNSexport mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question,
const domainname *const srv, const domainname *const domain,
- const mDNSIPAddr InterfaceAddr, mDNSQuestionCallback *Callback, void *Context)
- {
- question->InterfaceAddr = InterfaceAddr;
- question->name = *srv;
- AppendDomainNameToName(&question->name, domain);
- question->rrtype = kDNSType_PTR;
- question->rrclass = kDNSClass_IN;
- question->Callback = Callback;
- question->Context = Context;
+ const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context)
+ {
+ question->ThisQInterval = -1; // Indicate that query is not yet active
+ question->InterfaceID = InterfaceID;
+ question->qtype = kDNSType_PTR;
+ question->qclass = kDNSClass_IN;
+ question->QuestionCallback = Callback;
+ question->QuestionContext = Context;
+ if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) return(mStatus_BadParamErr);
return(mDNS_StartQuery(m, question));
}
-mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer)
+mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
{
- ServiceInfoQuery *query = (ServiceInfoQuery *)question->Context;
- if (answer->rrremainingttl == 0) return;
+ ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext;
+ mDNSBool PortChanged = (mDNSBool)(query->info->port.NotAnInteger != answer->rdata->u.srv.port.NotAnInteger);
+ if (!AddRecord) return;
if (answer->rrtype != kDNSType_SRV) return;
query->info->port = answer->rdata->u.srv.port;
if (!query->GotSRV)
{
query->GotSRV = mDNStrue;
- query->qADD.InterfaceAddr = answer->InterfaceAddr;
- query->qADD.name = answer->rdata->u.srv.target;
- mDNS_StartQuery_internal(m, &query->qADD, mDNSPlatformTimeNow());
+ query->qAv4.InterfaceID = answer->InterfaceID;
+ 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);
}
// If this is not our first answer, only re-issue the address query if the target host name has changed
- else if (query->qADD.InterfaceAddr.NotAnInteger != answer->InterfaceAddr.NotAnInteger ||
- !SameDomainName(&query->qADD.name, &answer->rdata->u.srv.target))
+ 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);
+ if (SameDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target) && !PortChanged)
+ {
+ // If we get here, it means:
+ // 1. This is not our first SRV answer
+ // 2. The interface ID is different, but the target host and port are the same
+ // This implies that we're seeing the exact same SRV record on more than one interface, so we should
+ // make our address queries at least as broad as the original SRV query so that we catch all the answers.
+ query->qAv4.InterfaceID = query->qSRV.InterfaceID; // Will be mDNSInterface_Any, or a specific interface
+ query->qAv6.InterfaceID = query->qSRV.InterfaceID;
+ }
+ else
+ {
+ query->qAv4.InterfaceID = answer->InterfaceID;
+ AssignDomainName(query->qAv4.qname, answer->rdata->u.srv.target);
+ query->qAv6.InterfaceID = answer->InterfaceID;
+ 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);
+ }
+ else if (query->ServiceInfoQueryCallback && query->GotADD && query->GotTXT && PortChanged)
{
- mDNS_StopQuery_internal(m, &query->qADD);
- query->qADD.InterfaceAddr = answer->InterfaceAddr;
- query->qADD.name = answer->rdata->u.srv.target;
- mDNS_StartQuery_internal(m, &query->qADD, mDNSPlatformTimeNow());
+ if (++query->Answers >= 100)
+ debugf("**** WARNING **** Have given %lu answers for %##s (SRV) %##s %u",
+ query->Answers, query->qSRV.qname.c, answer->rdata->u.srv.target.c,
+ ((mDNSu16)answer->rdata->u.srv.port.b[0] << 8) | answer->rdata->u.srv.port.b[1]);
+ query->ServiceInfoQueryCallback(m, query);
}
-
- // Don't need to do ScheduleNextTask because this callback can only ever happen
- // (a) as a result of an immediate result from the mDNS_StartQuery call, or
- // (b) as a result of receiving a packet on the wire
- // both of which will result in a subsequent ScheduleNextTask call of their own
+ // 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.
}
-mDNSlocal void FoundServiceInfoTXT(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer)
+mDNSlocal void FoundServiceInfoTXT(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
{
- ServiceInfoQuery *query = (ServiceInfoQuery *)question->Context;
- if (answer->rrremainingttl == 0) return;
+ ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext;
+ if (!AddRecord) return;
if (answer->rrtype != kDNSType_TXT) return;
- if (answer->rdata->RDLength > sizeof(query->info->TXTinfo)) return;
+ if (answer->rdlength > sizeof(query->info->TXTinfo)) return;
- query->GotTXT = 1 + (query->GotTXT || query->GotADD);
- query->info->TXTlen = answer->rdata->RDLength;
- mDNSPlatformMemCopy(answer->rdata->u.txt.c, query->info->TXTinfo, answer->rdata->RDLength);
+ query->GotTXT = mDNStrue;
+ query->info->TXTlen = answer->rdlength;
+ mDNSPlatformMemCopy(answer->rdata->u.txt.c, query->info->TXTinfo, answer->rdlength);
- debugf("FoundServiceInfoTXT: %##s GotADD=%d", &query->info->name, query->GotADD);
+ verbosedebugf("FoundServiceInfoTXT: %##s GotADD=%d", query->info->name.c, query->GotADD);
- if (query->Callback && query->GotADD)
- query->Callback(m, query);
+ // 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.
+ if (query->ServiceInfoQueryCallback && query->GotADD)
+ {
+ if (++query->Answers >= 100)
+ debugf("**** WARNING **** have given %lu answers for %##s (TXT) %#s...",
+ query->Answers, query->qSRV.qname.c, answer->rdata->u.txt.c);
+ query->ServiceInfoQueryCallback(m, query);
+ }
}
-mDNSlocal void FoundServiceInfoADD(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer)
+mDNSlocal void FoundServiceInfo(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
{
- ServiceInfoQuery *query = (ServiceInfoQuery *)question->Context;
- if (answer->rrremainingttl == 0) return;
- if (answer->rrtype != kDNSType_A) return;
+ ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext;
+ if (!AddRecord) return;
+
+ if (answer->rrtype == kDNSType_A)
+ {
+ query->info->ip.type = mDNSAddrType_IPv4;
+ query->info->ip.ip.v4 = answer->rdata->u.ip;
+ }
+ else if (answer->rrtype == kDNSType_AAAA)
+ {
+ query->info->ip.type = mDNSAddrType_IPv6;
+ query->info->ip.ip.v6 = answer->rdata->u.ipv6;
+ }
+ else
+ {
+ debugf("FoundServiceInfo: answer %##s type %d (%s) unexpected", answer->name.c, answer->rrtype, DNSTypeName(answer->rrtype));
+ return;
+ }
+
query->GotADD = mDNStrue;
- query->info->InterfaceAddr = answer->InterfaceAddr;
- query->info->ip = answer->rdata->u.ip;
+ query->info->InterfaceID = answer->InterfaceID;
- debugf("FoundServiceInfoADD: %##s GotTXT=%d", &query->info->name, query->GotTXT);
+ verbosedebugf("FoundServiceInfo v%d: %##s GotTXT=%d", query->info->ip.type, query->info->name.c, query->GotTXT);
- // If query->GotTXT is 1 that means we already got a single TXT answer but didn't
- // deliver it to the client at that time, so no further action is required.
- // If query->GotTXT is 2 that means we either got more than one TXT answer,
- // or we got a TXT answer and delivered it to the client at that time, so in either
- // of these cases we may have lost information, so we should re-issue the TXT question.
- if (query->GotTXT > 1)
+ // 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.
+ if (query->ServiceInfoQueryCallback && query->GotTXT)
{
- mDNS_StopQuery_internal(m, &query->qTXT);
- mDNS_StartQuery_internal(m, &query->qTXT, mDNSPlatformTimeNow());
+ 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);
+ else
+ debugf("**** WARNING **** have given %lu answers for %##s (AAAA) %.16a", query->Answers, query->qSRV.qname.c, &answer->rdata->u.ipv6);
+ }
+ query->ServiceInfoQueryCallback(m, query);
}
-
- if (query->Callback && query->GotTXT)
- query->Callback(m, query);
}
-// On entry, the client must have set the name and InterfaceAddr fields of the ServiceInfo structure
-// If the query is not interface-specific, then InterfaceAddr may be zero
+// On entry, the client must have set the name and InterfaceID fields of the ServiceInfo structure
+// If the query is not interface-specific, then InterfaceID may be zero
// Each time the Callback is invoked, the remainder of the fields will have been filled in
-// In addition, InterfaceAddr will be updated to give the interface address corresponding to that reply
+// In addition, InterfaceID will be updated to give the interface identifier corresponding to that response
mDNSexport mStatus mDNS_StartResolveService(mDNS *const m,
- ServiceInfoQuery *query, ServiceInfo *info, ServiceInfoQueryCallback *Callback, void *Context)
+ ServiceInfoQuery *query, ServiceInfo *info, mDNSServiceInfoQueryCallback *Callback, void *Context)
{
mStatus status;
- const mDNSs32 timenow = mDNS_Lock(m);
-
- query->qSRV.InterfaceAddr = info->InterfaceAddr;
- query->qSRV.name = info->name;
- query->qSRV.rrtype = kDNSType_SRV;
- query->qSRV.rrclass = kDNSClass_IN;
- query->qSRV.Callback = FoundServiceInfoSRV;
- query->qSRV.Context = query;
-
- query->qTXT.InterfaceAddr = info->InterfaceAddr;
- query->qTXT.name = info->name;
- query->qTXT.rrtype = kDNSType_TXT;
- query->qTXT.rrclass = kDNSClass_IN;
- query->qTXT.Callback = FoundServiceInfoTXT;
- query->qTXT.Context = query;
-
- query->qADD.InterfaceAddr = info->InterfaceAddr;
- query->qADD.name.c[0] = 0;
- query->qADD.rrtype = kDNSType_A;
- query->qADD.rrclass = kDNSClass_IN;
- query->qADD.Callback = FoundServiceInfoADD;
- query->qADD.Context = query;
-
- query->GotSRV = mDNSfalse;
- query->GotTXT = mDNSfalse;
- query->GotADD = mDNSfalse;
-
- query->info = info;
- query->Callback = Callback;
- query->Context = Context;
+ mDNS_Lock(m);
+
+ query->qSRV.ThisQInterval = -1; // This question not yet in the question list
+ query->qSRV.InterfaceID = info->InterfaceID;
+ AssignDomainName(query->qSRV.qname, info->name);
+ query->qSRV.qtype = kDNSType_SRV;
+ query->qSRV.qclass = kDNSClass_IN;
+ query->qSRV.QuestionCallback = FoundServiceInfoSRV;
+ query->qSRV.QuestionContext = query;
+
+ query->qTXT.ThisQInterval = -1; // This question not yet in the question list
+ query->qTXT.InterfaceID = info->InterfaceID;
+ AssignDomainName(query->qTXT.qname, info->name);
+ query->qTXT.qtype = kDNSType_TXT;
+ query->qTXT.qclass = kDNSClass_IN;
+ query->qTXT.QuestionCallback = FoundServiceInfoTXT;
+ query->qTXT.QuestionContext = query;
+
+ query->qAv4.ThisQInterval = -1; // This question not yet in the question list
+ query->qAv4.InterfaceID = info->InterfaceID;
+ query->qAv4.qname.c[0] = 0;
+ query->qAv4.qtype = kDNSType_A;
+ query->qAv4.qclass = kDNSClass_IN;
+ query->qAv4.QuestionCallback = FoundServiceInfo;
+ query->qAv4.QuestionContext = query;
+
+ query->qAv6.ThisQInterval = -1; // This question not yet in the question list
+ query->qAv6.InterfaceID = info->InterfaceID;
+ query->qAv6.qname.c[0] = 0;
+ query->qAv6.qtype = kDNSType_AAAA;
+ query->qAv6.qclass = kDNSClass_IN;
+ query->qAv6.QuestionCallback = FoundServiceInfo;
+ query->qAv6.QuestionContext = query;
+
+ query->GotSRV = mDNSfalse;
+ query->GotTXT = mDNSfalse;
+ query->GotADD = mDNSfalse;
+ query->Answers = 0;
+
+ query->info = info;
+ query->ServiceInfoQueryCallback = Callback;
+ query->ServiceInfoQueryContext = Context;
// info->name = Must already be set up by client
// info->interface = Must already be set up by client
- info->ip = zeroIPAddr;
+ info->ip = zeroAddr;
info->port = zeroIPPort;
info->TXTlen = 0;
- status = mDNS_StartQuery_internal(m, &query->qSRV, timenow);
- if (status == mStatus_NoError) status = mDNS_StartQuery_internal(m, &query->qTXT, timenow);
+ 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);
mDNS_Unlock(m);
mDNS_Lock(m);
if (query->qSRV.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &query->qSRV);
if (query->qTXT.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &query->qTXT);
- if (query->qADD.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &query->qADD);
+ if (query->qAv4.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &query->qAv4);
+ if (query->qAv6.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &query->qAv6);
mDNS_Unlock(m);
}
-mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNSu8 DomainType,
- const mDNSIPAddr InterfaceAddr, mDNSQuestionCallback *Callback, void *Context)
+mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType,
+ const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context)
{
- question->InterfaceAddr = InterfaceAddr;
- ConvertCStringToDomainName(mDNS_DomainTypeNames[DomainType], &question->name);
- question->rrtype = kDNSType_PTR;
- question->rrclass = kDNSClass_IN;
- question->Callback = Callback;
- question->Context = Context;
+ MakeDomainNameFromDNSNameString(&question->qname, mDNS_DomainTypeNames[DomainType]);
+ question->InterfaceID = InterfaceID;
+ question->qtype = kDNSType_PTR;
+ question->qclass = kDNSClass_IN;
+ question->QuestionCallback = Callback;
+ question->QuestionContext = Context;
return(mDNS_StartQuery(m, question));
}
// ***************************************************************************
-#if 0
+#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark - Responder Functions
#endif
-// Set up a ResourceRecord with sensible default values.
+// Set up a AuthRecord with sensible default values.
// These defaults may be overwritten with new values before mDNS_Register is called
-mDNSexport void mDNS_SetupResourceRecord(ResourceRecord *rr, RData *RDataStorage, mDNSIPAddr InterfaceAddr,
+mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID,
mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, mDNSRecordCallback Callback, void *Context)
{
// 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 = 1;
+ ttl = kDefaultTTLforShared;
// Field Group 1: Persistent metadata for Authoritative Records
rr->Additional1 = mDNSNULL;
rr->Additional2 = mDNSNULL;
rr->DependentOn = mDNSNULL;
rr->RRSet = mDNSNULL;
- rr->Callback = Callback;
- rr->Context = Context;
+ rr->RecordCallback = Callback;
+ rr->RecordContext = Context;
- rr->RecordType = RecordType;
+ rr->resrec.RecordType = RecordType;
rr->HostTarget = 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)
// Field Group 4: The actual information pertaining to this resource record
- rr->InterfaceAddr = InterfaceAddr;
- rr->name.c[0] = 0; // MUST be set by client
- rr->rrtype = rrtype;
- rr->rrclass = kDNSClass_IN;
- rr->rroriginalttl = ttl;
- rr->rrremainingttl = ttl;
-// rr->rdlength = MUST set by client and/or in mDNS_Register_internal
-// rr->rdestimate = set in mDNS_Register_internal
-// rr->rdata = MUST be set by client
+ rr->resrec.InterfaceID = InterfaceID;
+ rr->resrec.name.c[0] = 0; // MUST be set by client
+ rr->resrec.rrtype = rrtype;
+ rr->resrec.rrclass = kDNSClass_IN;
+ rr->resrec.rroriginalttl = ttl;
+// rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal
+// rr->resrec.rdestimate = set in mDNS_Register_internal
+// rr->resrec.rdata = MUST be set by client
if (RDataStorage)
- rr->rdata = RDataStorage;
+ rr->resrec.rdata = RDataStorage;
else
{
- rr->rdata = &rr->rdatastorage;
- rr->rdata->MaxRDLength = sizeof(RDataBody);
+ rr->resrec.rdata = &rr->rdatastorage;
+ rr->resrec.rdata->MaxRDLength = sizeof(RDataBody);
}
}
-mDNSexport mStatus mDNS_Register(mDNS *const m, ResourceRecord *const rr)
+mDNSexport mStatus mDNS_Register(mDNS *const m, AuthRecord *const rr)
{
- const mDNSs32 timenow = mDNS_Lock(m);
- mStatus status = mDNS_Register_internal(m, rr, timenow);
+ mStatus status;
+ mDNS_Lock(m);
+ status = mDNS_Register_internal(m, rr);
mDNS_Unlock(m);
return(status);
}
-mDNSexport mStatus mDNS_Update(mDNS *const m, ResourceRecord *const rr, mDNSu32 newttl,
+mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newttl,
+ const mDNSu16 newrdlength,
RData *const newrdata, mDNSRecordUpdateCallback *Callback)
{
- const mDNSs32 timenow = mDNS_Lock(m);
+ mDNS_Lock(m);
+
+ 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); }
+
+ // If TTL is unspecified, leave TTL unchanged
+ if (newttl == 0) newttl = rr->resrec.rroriginalttl;
// 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)
{
RData *n = rr->NewRData;
- rr->NewRData = mDNSNULL; // Clear the NewRData pointer ...
- if (rr->UpdateCallback) rr->UpdateCallback(m, rr, n); // ...and let the client free this memory, if necessary
- }
-
- rr->AnnounceCount = DefaultAnnounceCountForRecordType(rr->RecordType);
- rr->NextSendTime = timenow;
- if (rr->RecordType == kDNSRecordTypeUnique && m->SuppressProbes) rr->NextSendTime = m->SuppressProbes;
- rr->NextSendInterval = DefaultSendIntervalForRecordType(rr->RecordType);
- rr->NewRData = newrdata;
- rr->UpdateCallback = Callback;
- rr->rroriginalttl = newttl;
- rr->rrremainingttl = newttl;
+ rr->NewRData = mDNSNULL; // Clear the NewRData pointer ...
+ if (rr->UpdateCallback)
+ rr->UpdateCallback(m, rr, n); // ...and let the client free this memory, if necessary
+ }
+
+ if (rr->AnnounceCount < ReannounceCount)
+ rr->AnnounceCount = ReannounceCount;
+ rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
+ InitializeLastAPTime(m, rr);
+ rr->NewRData = newrdata;
+ rr->newrdlength = newrdlength;
+ rr->UpdateCallback = Callback;
+ if (!rr->UpdateBlocked && rr->UpdateCredits) rr->UpdateCredits--;
+ if (!rr->NextUpdateCredit) rr->NextUpdateCredit = (m->timenow + mDNSPlatformOneSecond * 60) | 1;
+ 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;
+ rr->ThisAPInterval *= 4;
+ LogMsg("Excessive update rate for %##s; delaying announcement by %d seconds", rr->resrec.name.c, delay);
+ }
+ rr->resrec.rroriginalttl = newttl;
mDNS_Unlock(m);
return(mStatus_NoError);
}
// NOTE: mDNS_Deregister calls mDNS_Deregister_internal which can call a user callback, which may change
// the record list and/or question list.
// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
-mDNSexport void mDNS_Deregister(mDNS *const m, ResourceRecord *const rr)
+mDNSexport mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr)
{
- const mDNSs32 timenow = mDNS_Lock(m);
- mDNS_Deregister_internal(m, rr, timenow, mDNS_Dereg_normal);
+ mStatus status;
+ mDNS_Lock(m);
+ status = mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
mDNS_Unlock(m);
+ return(status);
+ }
+
+mDNSlocal void HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result);
+
+mDNSlocal NetworkInterfaceInfo *FindFirstAdvertisedInterface(mDNS *const m)
+ {
+ NetworkInterfaceInfo *intf;
+ for (intf = m->HostInterfaces; intf; intf = intf->next)
+ if (intf->Advertise) break;
+ return(intf);
+ }
+
+mDNSlocal void mDNS_AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set)
+ {
+ char buffer[256];
+ NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m);
+ if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary
+
+ mDNS_SetupResourceRecord(&set->RR_A, mDNSNULL, set->InterfaceID, kDNSType_A, kDefaultTTLforUnique, kDNSRecordTypeUnique, 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);
+
+ // 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);
+ if (set->ip.type == mDNSAddrType_IPv4)
+ {
+ set->RR_A.resrec.rrtype = kDNSType_A;
+ set->RR_A.resrec.rdata->u.ip = 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]);
+ }
+ else if (set->ip.type == mDNSAddrType_IPv6)
+ {
+ int i;
+ set->RR_A.resrec.rrtype = kDNSType_AAAA;
+ set->RR_A.resrec.rdata->u.ipv6 = set->ip.ip.v6;
+ for (i = 0; i < 16; i++)
+ {
+ static const char hexValues[] = "0123456789ABCDEF";
+ buffer[i * 4 ] = hexValues[set->ip.ip.v6.b[15 - i] & 0x0F];
+ buffer[i * 4 + 1] = '.';
+ buffer[i * 4 + 2] = hexValues[set->ip.ip.v6.b[15 - i] >> 4];
+ buffer[i * 4 + 3] = '.';
+ }
+ mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa.");
+ }
+
+ 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_A.RRSet = &primary->RR_A; // May refer to self
+
+ mDNS_Register_internal(m, &set->RR_A);
+ mDNS_Register_internal(m, &set->RR_PTR);
+
+ 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);
+ set->RR_HINFO.DependentOn = &set->RR_A;
+ mDNSPlatformMemCopy(&m->HIHardware, p, 1 + (mDNSu32)m->HIHardware.c[0]);
+ p += 1 + (int)p[0];
+ mDNSPlatformMemCopy(&m->HISoftware, p, 1 + (mDNSu32)m->HISoftware.c[0]);
+ mDNS_Register_internal(m, &set->RR_HINFO);
+ }
+ else
+ {
+ debugf("Not creating HINFO record: platform support layer provided no information");
+ set->RR_HINFO.resrec.RecordType = kDNSRecordTypeUnregistered;
+ }
+ }
+
+mDNSlocal void mDNS_DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set)
+ {
+ NetworkInterfaceInfo *intf;
+ // If we still have address records referring to this one, update them
+ NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m);
+ AuthRecord *A = primary ? &primary->RR_A : mDNSNULL;
+ for (intf = m->HostInterfaces; intf; intf = intf->next)
+ if (intf->RR_A.RRSet == &set->RR_A)
+ intf->RR_A.RRSet = A;
+
+ // Unregister these records.
+ // When doing the mDNS_Close processing, we first call mDNS_DeadvertiseInterface for each interface, so by the time the platform
+ // support layer gets to call mDNS_DeregisterInterface, the address and PTR records have already been deregistered for it.
+ // Also, in the event of a name conflict, one or more of our records will have been forcibly deregistered.
+ // To avoid unnecessary and misleading warning messages, we check the RecordType before calling mDNS_Deregister_internal().
+ if (set->RR_A. resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_A, mDNS_Dereg_normal);
+ if (set->RR_PTR. resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_PTR, mDNS_Dereg_normal);
+ if (set->RR_HINFO.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_HINFO, mDNS_Dereg_normal);
}
mDNSexport void mDNS_GenerateFQDN(mDNS *const m)
{
- // Set up the Primary mDNS FQDN
- m->hostname1.c[0] = 0;
- AppendDomainLabelToName(&m->hostname1, &m->hostlabel);
- AppendStringLabelToName(&m->hostname1, "local");
+ domainname newname;
+ mDNS_Lock(m);
- // Set up the Secondary mDNS FQDN
- m->hostname2.c[0] = 0;
- AppendDomainLabelToName(&m->hostname2, &m->hostlabel);
- AppendStringLabelToName(&m->hostname2, "local");
- AppendStringLabelToName(&m->hostname2, "arpa");
+ newname.c[0] = 0;
+ if (!AppendDomainLabel(&newname, &m->hostlabel)) LogMsg("ERROR! Cannot create dot-local hostname");
+ if (!AppendLiteralLabelString(&newname, "local")) LogMsg("ERROR! Cannot create dot-local hostname");
+ if (!SameDomainName(&m->hostname, &newname))
+ {
+ NetworkInterfaceInfo *intf;
+ AuthRecord *rr;
+
+ m->hostname = newname;
+
+ // 1. Stop advertising our address records on all interfaces
+ for (intf = m->HostInterfaces; intf; intf = intf->next)
+ if (intf->Advertise) mDNS_DeadvertiseInterface(m, intf);
+
+ // 2. Start advertising our address records using the new name
+ for (intf = m->HostInterfaces; intf; intf = intf->next)
+ if (intf->Advertise) mDNS_AdvertiseInterface(m, intf);
+
+ // 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);
+ }
- // 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
- UpdateHostNameTargets(m);
+ mDNS_Unlock(m);
}
-mDNSlocal void HostNameCallback(mDNS *const m, ResourceRecord *const rr, mStatus result)
+mDNSlocal void HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
{
- #pragma unused(rr)
- switch (result)
+ (void)rr; // Unused parameter
+
+ #if MDNS_DEBUGMSGS
{
- case mStatus_NoError:
- debugf("HostNameCallback: %##s (%s) Name registered", rr->name.c, DNSTypeName(rr->rrtype));
- break;
- case mStatus_NameConflict:
- debugf("HostNameCallback: %##s (%s) Name conflict", rr->name.c, DNSTypeName(rr->rrtype));
- break;
- default:
- debugf("HostNameCallback: %##s (%s) Unknown result %d", rr->name.c, DNSTypeName(rr->rrtype), result);
- break;
+ char *msg = "Unknown result";
+ if (result == mStatus_NoError) msg = "Name registered";
+ else if (result == mStatus_NameConflict) msg = "Name conflict";
+ debugf("HostNameCallback: %##s (%s) %s (%ld)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), msg, result);
}
+ #endif
- if (result == mStatus_NameConflict)
+ if (result == mStatus_NoError)
+ {
+ // Notify the client that the host name is successfully registered
+ if (m->MainCallback)
+ m->MainCallback(m, result);
+ }
+ else if (result == mStatus_NameConflict)
{
- NetworkInterfaceInfo *hr = mDNSNULL;
- NetworkInterfaceInfo **p = &hr;
domainlabel oldlabel = m->hostlabel;
- // 1. Deregister all our host sets
- while (m->HostInterfaces)
- {
- NetworkInterfaceInfo *set = m->HostInterfaces;
- mDNS_DeregisterInterface(m, set);
- *p = set;
- p = &set->next;
- }
-
- // 2. Pick a new name
- // First give the client callback a chance to pick a new name
- if (m->Callback) m->Callback(m, mStatus_NameConflict);
- // If the client callback didn't do it, add (or increment) an index ourselves
+ // 1. First give the client callback a chance to pick a new name
+ if (m->MainCallback)
+ m->MainCallback(m, mStatus_NameConflict);
+
+ // 2. If the client callback didn't do it, add (or increment) an index ourselves
if (SameDomainLabel(m->hostlabel.c, oldlabel.c))
IncrementLabelSuffix(&m->hostlabel, mDNSfalse);
+
+ // 3. Generate the FQDNs from the hostlabel,
+ // and make sure all SRV records, etc., are updated to reference our new hostname
mDNS_GenerateFQDN(m);
-
- // 3. Re-register all our host sets
- while (hr)
- {
- NetworkInterfaceInfo *set = hr;
- hr = hr->next;
- mDNS_RegisterInterface(m, set);
- }
}
}
-mDNSlocal NetworkInterfaceInfo *FindFirstAdvertisedInterface(mDNS *const m)
+mDNSlocal void UpdateInterfaceProtocols(mDNS *const m, NetworkInterfaceInfo *active)
{
- NetworkInterfaceInfo *i;
- for (i=m->HostInterfaces; i; i=i->next) if (i->Advertise) break;
- return(i);
+ NetworkInterfaceInfo *intf;
+ active->IPv4Available = mDNSfalse;
+ active->IPv6Available = mDNSfalse;
+ for (intf = m->HostInterfaces; intf; intf = intf->next)
+ if (intf->InterfaceID == active->InterfaceID)
+ {
+ if (intf->ip.type == mDNSAddrType_IPv4 && intf->TxAndRx) active->IPv4Available = mDNStrue;
+ if (intf->ip.type == mDNSAddrType_IPv6 && intf->TxAndRx) active->IPv6Available = mDNStrue;
+ }
}
mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *set)
{
- const mDNSs32 timenow = mDNS_Lock(m);
+ mDNSBool FirstOfType = mDNStrue;
NetworkInterfaceInfo **p = &m->HostInterfaces;
+ mDNS_Lock(m);
- while (*p && *p != set) p=&(*p)->next;
- if (*p)
- {
- debugf("Error! Tried to register a NetworkInterfaceInfo that's already in the list");
- mDNS_Unlock(m);
- return(mStatus_AlreadyRegistered);
- }
+ // Assume this interface will be active
+ set->InterfaceActive = mDNStrue;
+ set->IPv4Available = (set->ip.type == mDNSAddrType_IPv4 && set->TxAndRx);
+ set->IPv6Available = (set->ip.type == mDNSAddrType_IPv6 && set->TxAndRx);
- if (set->Advertise)
+ while (*p)
{
- char buffer[256];
- NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m);
- if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary
-
- mDNS_SetupResourceRecord(&set->RR_A1, mDNSNULL, set->ip, kDNSType_A, 60, kDNSRecordTypeUnique, HostNameCallback, set);
- mDNS_SetupResourceRecord(&set->RR_A2, mDNSNULL, set->ip, kDNSType_A, 60, kDNSRecordTypeUnique, HostNameCallback, set);
- mDNS_SetupResourceRecord(&set->RR_PTR, mDNSNULL, set->ip, kDNSType_PTR, 60, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
-
- // 1. Set up primary Address record to map from primary host name ("foo.local.") to IP address
- set->RR_A1.name = m->hostname1;
- set->RR_A1.rdata->u.ip = set->ip;
-
- // 2. Set up secondary Address record to map from secondary host name ("foo.local.arpa.") to IP address
- set->RR_A2.name = m->hostname2;
- set->RR_A2.rdata->u.ip = set->ip;
-
- // 3. Set up reverse-lookup PTR record to map from our address back to our primary host name
- // Setting HostTarget tells DNS that the target of this PTR is to be automatically kept in sync if our host name changes
- // Note: This is reverse order compared to a normal dotted-decimal IP address
- mDNS_sprintf(buffer, "%d.%d.%d.%d.in-addr.arpa.", set->ip.b[3], set->ip.b[2], set->ip.b[1], set->ip.b[0]);
- ConvertCStringToDomainName(buffer, &set->RR_PTR.name);
- 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_A1.RRSet = &primary->RR_A1; // May refer to self
- set->RR_A2.RRSet = &primary->RR_A2; // May refer to self
-
- mDNS_Register_internal(m, &set->RR_A1, timenow);
- mDNS_Register_internal(m, &set->RR_A2, timenow);
- mDNS_Register_internal(m, &set->RR_PTR, timenow);
-
- // ... Add an HINFO record, etc.?
- }
+ if (*p == set)
+ {
+ LogMsg("Error! Tried to register a NetworkInterfaceInfo that's already in the list");
+ mDNS_Unlock(m);
+ return(mStatus_AlreadyRegistered);
+ }
- { // Reactivate Interface Questions
- DNSQuestion *q;
- for (q = m->ActiveQuestions; q; q=q->next) // Scan our list of questions
- if (!q->InterfaceAddr.NotAnInteger || q->InterfaceAddr.NotAnInteger == set->ip.NotAnInteger)
+ // This InterfaceID is already in the list, so mark this interface inactive for now
+ if ((*p)->InterfaceID == set->InterfaceID)
{
- q->NextQTime = timenow;
- q->ThisQInterval = mDNSPlatformOneSecond; // MUST be > zero for an active question
- q->NextQInterval = mDNSPlatformOneSecond;
+ set->InterfaceActive = mDNSfalse;
+ if (set->ip.type == (*p)->ip.type) FirstOfType = mDNSfalse;
+ if (set->ip.type == mDNSAddrType_IPv4 && set->TxAndRx) (*p)->IPv4Available = mDNStrue;
+ if (set->ip.type == mDNSAddrType_IPv6 && set->TxAndRx) (*p)->IPv6Available = mDNStrue;
}
- }
+
+ p=&(*p)->next;
+ }
set->next = mDNSNULL;
*p = set;
- mDNS_Unlock(m);
- return(mStatus_NoError);
- }
-
-mDNSlocal void mDNS_DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set, const mDNSs32 timenow)
- {
- NetworkInterfaceInfo *i;
- // If we still have address records referring to this one, update them
- NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m);
- ResourceRecord *A1 = primary ? &primary->RR_A1 : mDNSNULL;
- ResourceRecord *A2 = primary ? &primary->RR_A2 : mDNSNULL;
- for (i=m->HostInterfaces; i; i=i->next)
+
+ debugf("mDNS_RegisterInterface: InterfaceID %p %#a %s", set->InterfaceID, &set->ip,
+ set->InterfaceActive ?
+ "not represented in list; marking active and retriggering queries" :
+ "already represented in list; marking inactive for now");
+
+ // 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.
+ // Therefore, when registering an interface, we want to re-trigger our questions and re-probe our Resource Records,
+ // even if we believe that we previously had an active representative of this interface.
+ if ((m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) || FirstOfType || set->InterfaceActive)
{
- if (i->RR_A1.RRSet == &set->RR_A1) i->RR_A1.RRSet = A1;
- if (i->RR_A2.RRSet == &set->RR_A2) i->RR_A2.RRSet = A2;
+ // Use a small amount of randomness:
+ // In the case of a network administrator turning on an Ethernet hub so that all the connected machines establish link at
+ // exactly the same time, we don't want them to all go and hit the network with identical queries at exactly the same moment.
+ if (!m->SuppressSending) m->SuppressSending = m->timenow + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval);
+ DNSQuestion *q;
+ AuthRecord *rr;
+ 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;
+ if (ActiveQuestion(q)) m->NextScheduledQuery = m->timenow;
+ }
+
+ // For all our non-specific authoritative resource records (and any dormant records specific to this interface)
+ // we now need them to re-probe if necessary, and then re-announce.
+ for (rr = m->ResourceRecords; rr; rr=rr->next)
+ 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);
+ InitializeLastAPTime(m, rr);
+ }
}
- // Unregister these records
- mDNS_Deregister_internal(m, &set->RR_A1, timenow, mDNS_Dereg_normal);
- mDNS_Deregister_internal(m, &set->RR_A2, timenow, mDNS_Dereg_normal);
- mDNS_Deregister_internal(m, &set->RR_PTR, timenow, mDNS_Dereg_normal);
+ if (set->Advertise)
+ mDNS_AdvertiseInterface(m, set);
+
+ mDNS_Unlock(m);
+ return(mStatus_NoError);
}
// NOTE: mDNS_DeregisterInterface calls mDNS_Deregister_internal which can call a user callback, which may change
mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set)
{
NetworkInterfaceInfo **p = &m->HostInterfaces;
- const mDNSs32 timenow = mDNS_Lock(m);
+
+ mDNSBool revalidate = mDNSfalse;
+ // If this platform has the "phantom interfaces" known bug (e.g. Jaguar), we have to revalidate records every
+ // time an interface goes away. Otherwise, when you disconnect the Ethernet cable, the system reports that it
+ // still has an IPv6 address, and if we don't revalidate those records don't get deleted in a timely fashion.
+ if (m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) revalidate = mDNStrue;
+
+ mDNS_Lock(m);
// Find this record in our list
while (*p && *p != set) p=&(*p)->next;
- if (!*p) { debugf("mDNS_DeregisterInterface: NetworkInterfaceInfo not found in list"); return; }
+ if (!*p) { debugf("mDNS_DeregisterInterface: NetworkInterfaceInfo not found in list"); mDNS_Unlock(m); return; }
// Unlink this record from our list
*p = (*p)->next;
set->next = mDNSNULL;
- // Flush any cache entries we received on this interface
- FlushCacheRecords(m, set->ip, timenow);
+ if (!set->InterfaceActive)
+ {
+ // If this interface not the active member of its set, update the v4/v6Available flags for the active member
+ NetworkInterfaceInfo *intf;
+ for (intf = m->HostInterfaces; intf; intf = intf->next)
+ if (intf->InterfaceActive && intf->InterfaceID == set->InterfaceID)
+ UpdateInterfaceProtocols(m, intf);
+ }
+ else
+ {
+ NetworkInterfaceInfo *intf;
+ for (intf = m->HostInterfaces; intf; intf = intf->next)
+ if (intf->InterfaceID == set->InterfaceID)
+ break;
+ if (intf)
+ {
+ debugf("mDNS_DeregisterInterface: Another representative of InterfaceID %p exists; making it active",
+ set->InterfaceID);
+ intf->InterfaceActive = mDNStrue;
+ UpdateInterfaceProtocols(m, intf);
+
+ // See if another representative *of the same type* exists. If not, we mave have gone from
+ // dual-stack to v6-only (or v4-only) so we need to reconfirm which records are still valid.
+ for (intf = m->HostInterfaces; intf; intf = intf->next)
+ if (intf->InterfaceID == set->InterfaceID && intf->ip.type == set->ip.type)
+ break;
+ if (!intf) revalidate = mDNStrue;
+ }
+ else
+ {
+ CacheRecord *rr;
+ DNSQuestion *q;
+ mDNSu32 slot;
+ debugf("mDNS_DeregisterInterface: Last representative of InterfaceID %p deregistered; marking questions etc. dormant",
+ set->InterfaceID);
+
+ // 1. Deactivate any questions specific to this interface
+ for (q = m->Questions; q; q=q->next)
+ if (q->InterfaceID == set->InterfaceID)
+ q->ThisQInterval = 0;
+
+ // 2. Flush any cache records received on this interface
+ revalidate = mDNSfalse; // Don't revalidate if we're flushing the records
+ for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
+ for (rr = m->rrcache_hash[slot]; rr; rr=rr->next)
+ if (rr->resrec.InterfaceID == set->InterfaceID)
+ PurgeCacheResourceRecord(m, rr);
+ }
+ }
- { // Deactivate Interface Questions
- DNSQuestion *q;
- for (q = m->ActiveQuestions; q; q=q->next)
- if (q->InterfaceAddr.NotAnInteger == set->ip.NotAnInteger)
- q->ThisQInterval = 0;
- }
+ // If we were advertising on this interface, deregister those address and reverse-lookup records now
+ if (set->Advertise)
+ mDNS_DeadvertiseInterface(m, set);
- // If we were advertising on this interface, deregister now
- // When doing the mDNS_Close processing, we first call mDNS_DeadvertiseInterface for each interface
- // so by the time the platform support layer gets to call mDNS_DeregisterInterface,
- // the address and PTR records have already been deregistered for it
- if (set->Advertise && set->RR_A1.RecordType) mDNS_DeadvertiseInterface(m, set, timenow);
+ // 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.
+ // Don't need to do this when shutting down, because *all* interfaces are about to go away
+ if (revalidate && !m->mDNS_shutdown)
+ {
+ mDNSu32 slot;
+ CacheRecord *rr;
+ m->NextCacheCheck = m->timenow;
+ for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
+ for (rr = m->rrcache_hash[slot]; rr; rr=rr->next)
+ if (rr->resrec.InterfaceID == set->InterfaceID)
+ mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForCableDisconnect);
+ }
mDNS_Unlock(m);
}
-mDNSlocal void ServiceCallback(mDNS *const m, ResourceRecord *const rr, mStatus result)
+mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
{
- #pragma unused(m)
- ServiceRecordSet *sr = (ServiceRecordSet *)rr->Context;
- switch (result)
- {
- case mStatus_NoError:
- if (rr == &sr->RR_SRV)
- debugf("ServiceCallback: Service RR_SRV %##s Registered", rr->name.c);
- else
- debugf("ServiceCallback: %##s (%s) ERROR Should only get mStatus_NoError callback for RR_SRV",
- rr->name.c, DNSTypeName(rr->rrtype));
- break;
-
- case mStatus_NameConflict:
- debugf("ServiceCallback: %##s (%s) Name Conflict", rr->name.c, DNSTypeName(rr->rrtype));
- break;
-
- case mStatus_MemFree:
- if (rr == &sr->RR_PTR)
- debugf("ServiceCallback: Service RR_PTR %##s Memory Free", rr->name.c);
- else
- debugf("ServiceCallback: %##s (%s) ERROR Should only get mStatus_MemFree callback for RR_PTR",
- rr->name.c, DNSTypeName(rr->rrtype));
- break;
+ ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext;
+ (void)m; // Unused parameter
- default:
- debugf("ServiceCallback: %##s (%s) Unknown Result %d", rr->name.c, DNSTypeName(rr->rrtype), result);
- break;
+ #if MDNS_DEBUGMSGS
+ {
+ char *msg = "Unknown result";
+ if (result == mStatus_NoError) msg = "Name Registered";
+ else if (result == mStatus_NameConflict) msg = "Name Conflict";
+ else if (result == mStatus_MemFree) msg = "Memory Free";
+ debugf("ServiceCallback: %##s (%s) %s (%ld)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), msg, result);
}
+ #endif
// 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; mDNS_DeregisterService(m, sr); return; }
+ 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
+ return;
+ }
- // If this ServiceRecordSet was forcibly deregistered, and now it's memory is ready for reuse,
- // then we can now report the NameConflict to the client
- if (result == mStatus_MemFree && sr->Conflict) result = mStatus_NameConflict;
+ if (result == mStatus_MemFree)
+ {
+ // If the PTR record or any of the subtype PTR record are still in the process of deregistering,
+ // don't pass on the NameConflict/MemFree message until every record is finished cleaning up.
+ mDNSu32 i;
+ if (sr->RR_PTR.resrec.RecordType != kDNSRecordTypeUnregistered) return;
+ for (i=0; i<sr->NumSubTypes; i++) if (sr->SubTypes[i].resrec.RecordType != kDNSRecordTypeUnregistered) return;
+
+ // If this ServiceRecordSet was forcibly deregistered, and now its memory is ready for reuse,
+ // then we can now report the NameConflict to the client
+ if (sr->Conflict) result = mStatus_NameConflict;
+ }
- if (sr->Callback) sr->Callback(m, sr, result);
+ // CAUTION: MUST NOT do anything more with sr after calling sr->Callback(), because the client's callback
+ // function is allowed to do anything, including deregistering this service and freeing its memory.
+ if (sr->ServiceCallback)
+ sr->ServiceCallback(m, sr, result);
}
// Note:
// Domain is fully qualified domain name (i.e. ending with a null label)
// 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
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,
- mDNSServiceCallback Callback, void *Context)
+ AuthRecord *SubTypes, mDNSu32 NumSubTypes,
+ const mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context)
{
- mDNSs32 timenow;
+ mStatus err;
+ mDNSu32 i;
- sr->Callback = Callback;
- sr->Context = Context;
- sr->Conflict = mDNSfalse;
+ sr->ServiceCallback = Callback;
+ sr->ServiceContext = Context;
+ sr->Extras = mDNSNULL;
+ sr->NumSubTypes = NumSubTypes;
+ sr->SubTypes = SubTypes;
+ sr->Conflict = mDNSfalse;
if (host && host->c[0]) sr->Host = *host;
else sr->Host.c[0] = 0;
- mDNS_SetupResourceRecord(&sr->RR_PTR, mDNSNULL, zeroIPAddr, kDNSType_PTR, 2*3600, kDNSRecordTypeShared, ServiceCallback, sr);
- mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, zeroIPAddr, kDNSType_SRV, 60, kDNSRecordTypeUnique, ServiceCallback, sr);
- mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, zeroIPAddr, kDNSType_TXT, 60, kDNSRecordTypeUnique, ServiceCallback, 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);
// 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
- if (sr->RR_TXT.rdata->MaxRDLength < txtlen)
- sr->RR_TXT.rdata->MaxRDLength = txtlen;
-
- if (ConstructServiceName(&sr->RR_PTR.name, mDNSNULL, type, domain) == mDNSNULL) return(mStatus_BadParamErr);
- if (ConstructServiceName(&sr->RR_SRV.name, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr);
- sr->RR_TXT.name = sr->RR_SRV.name;
+ if (sr->RR_TXT.resrec.rdata->MaxRDLength < txtlen)
+ sr->RR_TXT.resrec.rdata->MaxRDLength = txtlen;
+
+ // Set up the record names
+ // For now we only create an advisory record for the main type, not for subtypes
+ // We need to gain some operational experience before we decide if there's a need to create them for subtypes too
+ if (ConstructServiceName(&sr->RR_ADV.resrec.name, (domainlabel*)"\x09_services", (domainname*)"\x05_mdns\x04_udp", domain) == mDNSNULL)
+ return(mStatus_BadParamErr);
+ if (ConstructServiceName(&sr->RR_PTR.resrec.name, mDNSNULL, type, domain) == mDNSNULL) return(mStatus_BadParamErr);
+ if (ConstructServiceName(&sr->RR_SRV.resrec.name, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr);
+ AssignDomainName(sr->RR_TXT.resrec.name, sr->RR_SRV.resrec.name);
- // 1. Set up the PTR record rdata to point to our service name
+ // 1. Set up the ADV record rdata to advertise our service type
+ AssignDomainName(sr->RR_ADV.resrec.rdata->u.name, sr->RR_PTR.resrec.name);
+
+ // 2. Set up the PTR record rdata to point to our service name
// We set up two additionals, so when a client asks for this PTR we automatically send the SRV and the TXT too
- sr->RR_PTR.rdata->u.name = sr->RR_SRV.name;
+ AssignDomainName(sr->RR_PTR.resrec.rdata->u.name, sr->RR_SRV.resrec.name);
sr->RR_PTR.Additional1 = &sr->RR_SRV;
sr->RR_PTR.Additional2 = &sr->RR_TXT;
- // 2. Set up the SRV record rdata.
- sr->RR_SRV.rdata->u.srv.priority = 0;
- sr->RR_SRV.rdata->u.srv.weight = 0;
- sr->RR_SRV.rdata->u.srv.port = port;
+ // 2a. Set up any subtype PTRs to point to our service name
+ // If the client is using subtypes, it is the client's responsibility to have
+ // already set the first label of the record name to the subtype being registered
+ for (i=0; i<NumSubTypes; i++)
+ {
+ domainlabel s = *(domainlabel*)&sr->SubTypes[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);
+ 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;
+ }
+
+ // 3. Set up the SRV record rdata.
+ sr->RR_SRV.resrec.rdata->u.srv.priority = 0;
+ sr->RR_SRV.resrec.rdata->u.srv.weight = 0;
+ sr->RR_SRV.resrec.rdata->u.srv.port = port;
// Setting HostTarget tells DNS that the target of this SRV is to be automatically kept in sync with our host name
- if (sr->Host.c[0]) sr->RR_SRV.rdata->u.srv.target = sr->Host;
+ if (sr->Host.c[0]) AssignDomainName(sr->RR_SRV.resrec.rdata->u.srv.target, sr->Host);
else sr->RR_SRV.HostTarget = mDNStrue;
- // 3. Set up the TXT record rdata,
+ // 4. Set up the TXT record rdata,
// and set DependentOn because we're depending on the SRV record to find and resolve conflicts for us
- if (txtinfo == mDNSNULL) sr->RR_TXT.rdata->RDLength = 0;
- else if (txtinfo != sr->RR_TXT.rdata->u.txt.c)
+ if (txtinfo == mDNSNULL) sr->RR_TXT.resrec.rdlength = 0;
+ else if (txtinfo != sr->RR_TXT.resrec.rdata->u.txt.c)
{
- sr->RR_TXT.rdata->RDLength = txtlen;
- if (sr->RR_TXT.rdata->RDLength > sr->RR_TXT.rdata->MaxRDLength) return(mStatus_BadParamErr);
- mDNSPlatformMemCopy(txtinfo, sr->RR_TXT.rdata->u.txt.c, txtlen);
+ sr->RR_TXT.resrec.rdlength = txtlen;
+ if (sr->RR_TXT.resrec.rdlength > sr->RR_TXT.resrec.rdata->MaxRDLength) return(mStatus_BadParamErr);
+ mDNSPlatformMemCopy(txtinfo, sr->RR_TXT.resrec.rdata->u.txt.c, txtlen);
}
sr->RR_TXT.DependentOn = &sr->RR_SRV;
- // 4. We have no Extras yet
- sr->Extras = mDNSNULL;
-
- timenow = mDNS_Lock(m);
- mDNS_Register_internal(m, &sr->RR_SRV, timenow);
- mDNS_Register_internal(m, &sr->RR_TXT, timenow);
+ mDNS_Lock(m);
+ err = mDNS_Register_internal(m, &sr->RR_SRV);
+ if (!err) err = mDNS_Register_internal(m, &sr->RR_TXT);
// We register the RR_PTR last, because we want to be sure that in the event of a forced call to
// mDNS_Close, the RR_PTR will be the last one to be forcibly deregistered, since that is what triggers
// the mStatus_MemFree callback to ServiceCallback, which in turn passes on the mStatus_MemFree back to
// the client callback, which is then at liberty to free the ServiceRecordSet memory at will. We need to
// make sure we've deregistered all our records and done any other necessary cleanup before that happens.
- mDNS_Register_internal(m, &sr->RR_PTR, timenow);
+ if (!err) err = mDNS_Register_internal(m, &sr->RR_ADV);
+ for (i=0; i<NumSubTypes; i++) if (!err) err = mDNS_Register_internal(m, &sr->SubTypes[i]);
+ if (!err) err = mDNS_Register_internal(m, &sr->RR_PTR);
mDNS_Unlock(m);
- return(mStatus_NoError);
+ if (err) mDNS_DeregisterService(m, sr);
+ return(err);
}
-mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl)
+mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr,
+ ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl)
{
+ mStatus result = mStatus_UnknownErr;
ExtraResourceRecord **e = &sr->Extras;
while (*e) e = &(*e)->next;
- // If TTL is unspecified, make it 60 seconds, the same as the service's TXT and SRV default
- if (ttl == 0) ttl = 60;
+ // If TTL is unspecified, make it the same as the service's TXT and SRV default
+ if (ttl == 0) ttl = kDefaultTTLforUnique;
extra->next = mDNSNULL;
- mDNS_SetupResourceRecord(&extra->r, rdata, zeroIPAddr, extra->r.rrtype, ttl, kDNSRecordTypeUnique, ServiceCallback, sr);
- extra->r.name = sr->RR_SRV.name;
+ 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.name.c);
+ debugf("mDNS_AddRecordToService adding record to %##s", extra->r.resrec.name.c);
- *e = extra;
- return(mDNS_Register(m, &extra->r));
+ result = mDNS_Register(m, &extra->r);
+ if (!result) *e = extra;
+ return result;
}
mDNSexport mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra)
while (*e && *e != extra) e = &(*e)->next;
if (!*e)
{
- debugf("mDNS_RemoveRecordFromService failed to remove record from %##s", extra->r.name.c);
+ debugf("mDNS_RemoveRecordFromService failed to remove record from %##s", extra->r.resrec.name.c);
return(mStatus_BadReferenceErr);
}
- debugf("mDNS_RemoveRecordFromService removing record from %##s", extra->r.name.c);
+ debugf("mDNS_RemoveRecordFromService removing record from %##s", extra->r.resrec.name.c);
*e = (*e)->next;
- mDNS_Deregister(m, &extra->r);
- return(mStatus_NoError);
+ return(mDNS_Deregister(m, &extra->r));
}
mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordSet *const sr, const domainlabel *newname)
ExtraResourceRecord *extras = sr->Extras;
mStatus err;
- DeconstructServiceName(&sr->RR_SRV.name, &name, &type, &domain);
+ DeconstructServiceName(&sr->RR_SRV.resrec.name, &name, &type, &domain);
if (!newname)
{
IncrementLabelSuffix(&name, mDNStrue);
if (sr->RR_SRV.HostTarget == mDNSfalse && sr->Host.c[0]) host = &sr->Host;
err = mDNS_RegisterService(m, sr, newname, &type, &domain,
- host, sr->RR_SRV.rdata->u.srv.port, sr->RR_TXT.rdata->u.txt.c, sr->RR_TXT.rdata->RDLength,
- sr->Callback, sr->Context);
+ host, sr->RR_SRV.resrec.rdata->u.srv.port, sr->RR_TXT.resrec.rdata->u.txt.c, sr->RR_TXT.resrec.rdlength,
+ sr->SubTypes, sr->NumSubTypes,
+ sr->RR_PTR.resrec.InterfaceID, sr->ServiceCallback, sr->ServiceContext);
+ // mDNS_RegisterService() just reset sr->Extras to NULL.
+ // Fortunately we already grabbed ourselves a copy of this pointer (above), so we can now run
+ // through the old list of extra records, and re-add them to our freshly created service registration
while (!err && extras)
{
ExtraResourceRecord *e = extras;
extras = extras->next;
- err = mDNS_AddRecordToService(m, sr, e, e->r.rdata, e->r.rroriginalttl);
+ err = mDNS_AddRecordToService(m, sr, e, e->r.resrec.rdata, e->r.resrec.rroriginalttl);
}
return(err);
// NOTE: mDNS_DeregisterService calls mDNS_Deregister_internal which can call a user callback,
// which may change the record list and/or question list.
// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
-mDNSexport void mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr)
+mDNSexport mStatus mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr)
{
- const mDNSs32 timenow = mDNS_Lock(m);
- ExtraResourceRecord *e = sr->Extras;
-
- // We use mDNS_Dereg_repeat because, in the event of a collision, some or all of
- // these records could have already been automatically deregistered, and that's okay
- mDNS_Deregister_internal(m, &sr->RR_SRV, timenow, mDNS_Dereg_repeat);
- mDNS_Deregister_internal(m, &sr->RR_TXT, timenow, mDNS_Dereg_repeat);
- while (e)
+ if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeUnregistered)
+ {
+ debugf("Service set for %##s already deregistered", sr->RR_PTR.resrec.name.c);
+ return(mStatus_BadReferenceErr);
+ }
+ else if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeDeregistering)
{
- mDNS_Deregister_internal(m, &e->r, timenow, mDNS_Dereg_repeat);
- e=e->next;
+ debugf("Service set for %##s already in the process of deregistering", sr->RR_PTR.resrec.name.c);
+ return(mStatus_NoError);
}
+ else
+ {
+ mDNSu32 i;
+ mStatus status;
+ ExtraResourceRecord *e;
+ mDNS_Lock(m);
+ e = sr->Extras;
+
+ // We use mDNS_Dereg_repeat because, in the event of a collision, some or all of the
+ // SRV, TXT, or Extra records could have already been automatically deregistered, and that's okay
+ mDNS_Deregister_internal(m, &sr->RR_SRV, mDNS_Dereg_repeat);
+ mDNS_Deregister_internal(m, &sr->RR_TXT, mDNS_Dereg_repeat);
+
+ mDNS_Deregister_internal(m, &sr->RR_ADV, mDNS_Dereg_normal);
+
+ // We deregister all of the extra records, but we leave the sr->Extras list intact
+ // in case the client wants to do a RenameAndReregister and reinstate the registration
+ while (e)
+ {
+ mDNS_Deregister_internal(m, &e->r, mDNS_Dereg_repeat);
+ e = e->next;
+ }
- // Be sure to deregister the PTR last!
- // Deregistering this record is what triggers the mStatus_MemFree callback to ServiceCallback,
- // which in turn passes on the mStatus_MemFree (or mStatus_NameConflict) back to the client callback,
- // which is then at liberty to free the ServiceRecordSet memory at will. We need to make sure
- // we've deregistered all our records and done any other necessary cleanup before that happens.
- mDNS_Deregister_internal(m, &sr->RR_PTR, timenow, mDNS_Dereg_normal);
+ for (i=0; i<sr->NumSubTypes; i++)
+ mDNS_Deregister_internal(m, &sr->SubTypes[i], mDNS_Dereg_normal);
- mDNS_Unlock(m);
+ // Be sure to deregister the PTR last!
+ // Deregistering this record is what triggers the mStatus_MemFree callback to ServiceCallback,
+ // which in turn passes on the mStatus_MemFree (or mStatus_NameConflict) back to the client callback,
+ // which is then at liberty to free the ServiceRecordSet memory at will. We need to make sure
+ // we've deregistered all our records and done any other necessary cleanup before that happens.
+ status = mDNS_Deregister_internal(m, &sr->RR_PTR, mDNS_Dereg_normal);
+ mDNS_Unlock(m);
+ return(status);
+ }
+ }
+
+// Create a registration that asserts that no such service exists with this name.
+// This can be useful where there is a given function is available through several protocols.
+// For example, a printer called "Stuart's Printer" may implement printing via the "pdl-datastream" and "IPP"
+// protocols, but not via "LPR". In this case it would be prudent for the printer to assert the non-existence of an
+// "LPR" service called "Stuart's Printer". Without this precaution, another printer than offers only "LPR" printing
+// could inadvertently advertise its service under the same name "Stuart's Printer", which might be confusing for users.
+mDNSexport 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)
+ {
+ mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_SRV, kDefaultTTLforUnique, 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;
+ rr->resrec.rdata->u.srv.port = zeroIPPort;
+ if (host && host->c[0]) AssignDomainName(rr->resrec.rdata->u.srv.target, *host);
+ else rr->HostTarget = mDNStrue;
+ return(mDNS_Register(m, rr));
}
-mDNSexport mStatus mDNS_AdvertiseDomains(mDNS *const m, ResourceRecord *rr,
- mDNSu8 DomainType, const mDNSIPAddr InterfaceAddr, char *domname)
+mDNSexport mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr,
+ mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname)
{
- mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceAddr, kDNSType_PTR, 2*3600, kDNSRecordTypeShared, mDNSNULL, mDNSNULL);
- ConvertCStringToDomainName(mDNS_DomainTypeNames[DomainType], &rr->name);
- ConvertCStringToDomainName(domname, &rr->rdata->u.name);
+ mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_PTR, kDefaultTTLforShared, 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));
}
// ***************************************************************************
-#if 0
+#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark -
#pragma mark - Startup and Shutdown
#endif
+mDNSexport void mDNS_GrowCache(mDNS *const m, CacheRecord *storage, mDNSu32 numrecords)
+ {
+ if (storage && numrecords)
+ {
+ mDNSu32 i;
+ for (i=0; i<numrecords; i++) storage[i].next = &storage[i+1];
+ storage[numrecords-1].next = m->rrcache_free;
+ m->rrcache_free = storage;
+ m->rrcache_size += numrecords;
+ }
+ }
+
mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p,
- ResourceRecord *rrcachestorage, mDNSu32 rrcachesize, mDNSCallback *Callback, void *Context)
+ CacheRecord *rrcachestorage, mDNSu32 rrcachesize,
+ mDNSBool AdvertiseLocalAddresses, mDNSCallback *Callback, void *Context)
{
- mStatus result;
mDNSu32 i;
+ mDNSs32 timenow;
+ mStatus result = mDNSPlatformTimeInit(&timenow);
+ if (result != mStatus_NoError) return(result);
if (!rrcachestorage) rrcachesize = 0;
- m->p = p;
- m->mDNSPlatformStatus = mStatus_Waiting;
- m->Callback = Callback;
- m->Context = Context;
-
- m->mDNS_busy = 0;
-
- m->lock_rrcache = 0;
- m->lock_Questions = 0;
- m->lock_Records = 0;
-
- m->ActiveQuestions = mDNSNULL;
- m->NewQuestions = mDNSNULL;
- m->CurrentQuestion = mDNSNULL;
- m->rrcache_size = rrcachesize;
- m->rrcache_used = 0;
- m->rrcache_report = 10;
- m->rrcache_free = rrcachestorage;
- if (rrcachesize)
- {
- for (i=0; i<rrcachesize; i++) rrcachestorage[i].next = &rrcachestorage[i+1];
- rrcachestorage[rrcachesize-1].next = mDNSNULL;
- }
- m->rrcache = mDNSNULL;
-
- m->hostlabel.c[0] = 0;
- m->nicelabel.c[0] = 0;
- m->ResourceRecords = mDNSNULL;
- m->CurrentRecord = mDNSNULL;
- m->HostInterfaces = mDNSNULL;
- m->SuppressSending = 0;
- m->ProbeFailTime = 0;
- m->NumFailedProbes = 0;
- m->SuppressProbes = 0;
- m->SleepState = mDNSfalse;
- m->NetChanged = mDNSfalse;
+ m->p = p;
+ m->KnownBugs = 0;
+ m->AdvertiseLocalAddresses = AdvertiseLocalAddresses;
+ m->mDNSPlatformStatus = mStatus_Waiting;
+ m->MainCallback = Callback;
+ m->MainContext = Context;
+
+ // For debugging: To catch and report locking failures
+ m->mDNS_busy = 0;
+ m->mDNS_reentrancy = 0;
+ m->mDNS_shutdown = mDNSfalse;
+ m->lock_rrcache = 0;
+ m->lock_Questions = 0;
+ m->lock_Records = 0;
+
+ // Task Scheduling variables
+ m->timenow = 0; // MUST only be set within mDNS_Lock/mDNS_Unlock section
+ m->timenow_last = timenow;
+ m->timenow_adjust = 0;
+ m->NextScheduledEvent = timenow;
+ m->SuppressSending = timenow;
+ m->NextCacheCheck = timenow + 0x78000000;
+ m->NextScheduledQuery = timenow + 0x78000000;
+ m->NextScheduledProbe = timenow + 0x78000000;
+ m->NextScheduledResponse = timenow + 0x78000000;
+ m->ExpectUnicastResponse = timenow + 0x78000000;
+ m->RandomQueryDelay = 0;
+ m->SendDeregistrations = mDNSfalse;
+ m->SendImmediateAnswers = mDNSfalse;
+ m->SleepState = mDNSfalse;
+
+ // These fields only required for mDNS Searcher...
+ m->Questions = mDNSNULL;
+ m->NewQuestions = mDNSNULL;
+ m->CurrentQuestion = mDNSNULL;
+ m->LocalOnlyQuestions = mDNSNULL;
+ m->NewLocalOnlyQuestions = mDNSNULL;
+ m->rrcache_size = 0;
+ m->rrcache_totalused = 0;
+ m->rrcache_active = 0;
+ m->rrcache_report = 10;
+ m->rrcache_free = mDNSNULL;
+
+ for (i = 0; i < CACHE_HASH_SLOTS; i++)
+ {
+ m->rrcache_hash[i] = mDNSNULL;
+ m->rrcache_used[i] = 0;
+ }
+
+ mDNS_GrowCache(m, rrcachestorage, rrcachesize);
+
+ // Fields below only required for mDNS Responder...
+ m->hostlabel.c[0] = 0;
+ m->nicelabel.c[0] = 0;
+ m->hostname.c[0] = 0;
+ m->HIHardware.c[0] = 0;
+ m->HISoftware.c[0] = 0;
+ m->ResourceRecords = mDNSNULL;
+ m->DuplicateRecords = mDNSNULL;
+ m->LocalOnlyRecords = mDNSNULL;
+ m->NewLocalOnlyRecords = mDNSNULL;
+ m->DiscardLocalOnlyRecords = mDNSfalse;
+ m->CurrentRecord = mDNSNULL;
+ m->HostInterfaces = mDNSNULL;
+ m->ProbeFailTime = 0;
+ m->NumFailedProbes = 0;
+ m->SuppressProbes = 0;
result = mDNSPlatformInit(m);
-
+
return(result);
}
-extern void mDNSCoreInitComplete(mDNS *const m, mStatus result)
+mDNSexport void mDNSCoreInitComplete(mDNS *const m, mStatus result)
{
m->mDNSPlatformStatus = result;
- if (m->Callback) m->Callback(m, mStatus_NoError);
- mDNS_Lock(m); // This lock/unlock causes a ScheduleNextTask(m) to get things started
- mDNS_Unlock(m);
+ if (m->MainCallback)
+ m->MainCallback(m, mStatus_NoError);
}
-extern void mDNS_Close(mDNS *const m)
+mDNSexport void mDNS_Close(mDNS *const m)
{
- NetworkInterfaceInfo *i;
- const mDNSs32 timenow = mDNS_Lock(m);
+ mDNSu32 rrcache_active = 0;
+ mDNSu32 rrcache_totalused = 0;
+ mDNSu32 slot;
+ NetworkInterfaceInfo *intf;
+ mDNS_Lock(m);
-#if DEBUGBREAKS
- ResourceRecord *rr;
- int rrcache_active = 0;
- for (rr = m->rrcache; rr; rr=rr->next) if (CacheRRActive(m, rr)) rrcache_active++;
- debugf("mDNS_Close: RR Cache now using %d records, %d active", m->rrcache_used, rrcache_active);
-#endif
+ m->mDNS_shutdown = mDNStrue;
- m->ActiveQuestions = mDNSNULL; // We won't be answering any more questions!
+ rrcache_totalused = m->rrcache_totalused;
+ for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
+ while (m->rrcache_hash[slot])
+ {
+ CacheRecord *rr = m->rrcache_hash[slot];
+ m->rrcache_hash[slot] = rr->next;
+ if (rr->CRActiveQuestion) rrcache_active++;
+ m->rrcache_used[slot]--;
+ ReleaseCacheRR(m, rr);
+ }
+ debugf("mDNS_Close: RR Cache was using %ld records, %d 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);
+
+ m->Questions = mDNSNULL; // We won't be answering any more questions!
- for (i=m->HostInterfaces; i; i=i->next)
- if (i->Advertise)
- mDNS_DeadvertiseInterface(m, i, timenow);
+ for (intf = m->HostInterfaces; intf; intf = intf->next)
+ if (intf->Advertise)
+ mDNS_DeadvertiseInterface(m, intf);
// Make sure there are nothing but deregistering records remaining in the list
- if (m->CurrentRecord) debugf("DiscardDeregistrations ERROR m->CurrentRecord already set");
+ if (m->CurrentRecord) LogMsg("mDNS_Close ERROR m->CurrentRecord already set");
m->CurrentRecord = m->ResourceRecords;
while (m->CurrentRecord)
{
- ResourceRecord *rr = m->CurrentRecord;
+ AuthRecord *rr = m->CurrentRecord;
m->CurrentRecord = rr->next;
- if (rr->RecordType != kDNSRecordTypeDeregistering)
+ if (rr->resrec.RecordType != kDNSRecordTypeDeregistering)
{
- debugf("mDNS_Close: Record type %X still in ResourceRecords list %##s", rr->RecordType, rr->name.c);
- mDNS_Deregister_internal(m, rr, timenow, mDNS_Dereg_normal);
+ debugf("mDNS_Close: Record type %X still in ResourceRecords list %##s", rr->resrec.RecordType, rr->resrec.name.c);
+ mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
}
}
// If any deregistering records remain, send their deregistration announcements before we exit
if (m->mDNSPlatformStatus != mStatus_NoError)
- DiscardDeregistrations(m, timenow);
+ DiscardDeregistrations(m);
else
while (m->ResourceRecords)
- SendResponses(m, timenow);
+ SendResponses(m);
mDNS_Unlock(m);
debugf("mDNS_Close: mDNSPlatformClose");
-#pragma once
+/*
+ * 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: mDNSClientAPI.h,v $
+Revision 1.114 2003/08/29 19:44:15 cheshire
+<rdar://problem/3400967> 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
+<rdar://problem/3387878> Traffic reduction: No need to announce record for longer than TTL
+
+Revision 1.111 2003/08/21 02:21:50 cheshire
+<rdar://problem/3386473> Efficiency: Reduce repeated queries
+
+Revision 1.110 2003/08/20 23:39:31 cheshire
+<rdar://problem/3344098> 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
+<rdar://problem/3376721> 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
+<rdar://problem/3376552> 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
+<rdar://problem/3368159> 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
+<rdar://problem/3378386> 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
+<rdar://problem/3382423> UpdateRecord not working right
+Added "newrdlength" field to hold new length of updated rdata
+
+Revision 1.102 2003/08/16 03:39:00 cheshire
+<rdar://problem/3338440> InterfaceID -1 indicates "local only"
+
+Revision 1.101 2003/08/15 20:16:02 cheshire
+<rdar://problem/3366590> 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
+<rdar://problem/3378473> 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
+<rdar://problem/3375491> 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
+<rdar://problem/3374490> 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
+<rdar://problem/3323817> Improve cache performance
+Changed the number of hash table slots from 37 to 499
+Revision 1.95 2003/08/09 00:55:02 cheshire
+<rdar://problem/3366553> 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
+<rdar://problem/3370365> Guard against time going backwards
+
+Revision 1.92 2003/08/08 18:36:04 cheshire
+<rdar://problem/3344154> 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<b) and logical operators (a||b)
+
+Revision 1.87 2003/07/22 23:57:20 cheshire
+Move platform-layer function prototypes from mDNSClientAPI.h to mDNSPlatformFunctions.h where they belong
+
+Revision 1.86 2003/07/20 03:52:02 ksekar
+Bug #: <rdar://problem/3320722>: 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
+<rdar://problem/2986147> 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
+<rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead
+
+Revision 1.82 2003/07/17 17:35:04 cheshire
+<rdar://problem/3325583> 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
+<rdar://problem/3315761> Need to implement "unicast response" request, using top bit of qclass
+
+Revision 1.80 2003/07/15 01:55:12 cheshire
+<rdar://problem/3315777> Need to implement service registration with subtypes
+
+Revision 1.79 2003/07/13 02:28:00 cheshire
+<rdar://problem/3325166> 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
+<rdar://problem/3161289> No more local.arpa
+
+Revision 1.75 2003/07/02 21:19:45 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.74 2003/07/02 02:41:23 cheshire
+<rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
+
+Revision 1.73 2003/06/10 04:24:39 cheshire
+<rdar://problem/3283637> 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
+<rdar://problem/3283666> No need for multiple machines to all be sending the same queries
+
+Revision 1.70 2003/06/07 04:50:53 cheshire
+<rdar://problem/3283637> 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
+<rdar://problem/3283540> When query produces zero results, call mDNS_Reconfirm() on any antecedent records
+
+Revision 1.67 2003/06/07 01:22:14 cheshire
+<rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function
+
+Revision 1.66 2003/06/07 00:59:43 cheshire
+<rdar://problem/3283454> 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
+<rdar://problem/3274950> 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
+<rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be
+
+Revision 1.60 2003/05/31 00:09:49 cheshire
+<rdar://problem/3274862> Add ability to discover what services are on a network
+
+Revision 1.59 2003/05/29 06:11:35 cheshire
+<rdar://problem/3272214>: 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
+<rdar://problem/3268904> 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
+<rdar://problem/3268631> 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
+<rdar://problem/2984918> 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
+<rdar://problem/3159272> 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
+<rdar://problem/3248914> 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
+<rdar://problem/3241281> Change timenow from a local variable to a structure member
+
+Revision 1.43 2003/04/25 01:45:56 cheshire
+<rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name
+
+Revision 1.42 2003/04/15 20:58:31 jgraessl
+
+Bug #: 3229014
+Added a hash to lookup records in the cache.
+
+Revision 1.41 2003/04/15 18:09:13 jgraessl
+
+Bug #: 3228892
+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
+<rdar://problem/3212360> 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
+<rdar://problem/3210018> 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
+<rdar://problem/3176950> 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
+Bug #: 3185731 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
+Bug #: 3099194 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
+Bug #: 3099194 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
+Bug #: 3169535 Xserve RAID needs to do interface-specific registrations
+Reviewed by: Josh Graessley, Bob Bradley
+
+Revision 1.32 2003/01/31 03:35:59 cheshire
+Bug #: 3147097 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
+Bug #: 3147097 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:
+3086540 computer name changes not handled properly
+3124348 service name changes are not properly handled
+3124352 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> // stdarg.h is required for for va_list support for the mDNS_vsnprintf declaration
#include "mDNSDebug.h"
#ifdef __cplusplus
// Function scope indicators
// If you see "mDNSlocal" before a function name, it means the function is not callable outside this file
+#ifndef mDNSlocal
#define mDNSlocal static
+#endif
// If you see "mDNSexport" before a symbol, it means the symbol is exported for use by clients
+#ifndef mDNSexport
#define mDNSexport
+#endif
// ***************************************************************************
#if 0
#pragma mark - DNS Resource Record class and type constants
#endif
-typedef enum // From RFC 1035
+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]
- kDNSQClass_ANY = 255, // Not a DNS class, but a DNS query class, meaning "all classes"
- kDNSQClass_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
+ 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_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
// 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 unsigned char mDNSBool;
+typedef int mDNSBool;
typedef signed char mDNSs8;
typedef unsigned char mDNSu8;
typedef signed short mDNSs16;
typedef signed long mDNSs32;
typedef unsigned long mDNSu32;
+// 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 { 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, but don't forget -- just because it is in a register doesn't mean it is an
-// integer. Operations like add, multiply, increment, decrement, etc., are undefined for opaque 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 union { mDNSu8 b[2]; mDNSu16 NotAnInteger; } mDNSOpaque16;
typedef union { mDNSu8 b[4]; mDNSu32 NotAnInteger; } mDNSOpaque32;
+typedef union { 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 mDNSIPAddr; // An IP address is a four-byte opaque identifier (not an integer)
+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 };
mStatus_AlreadyRegistered = -65547,
mStatus_NameConflict = -65548,
mStatus_Invalid = -65549,
-
+ mStatus_GrowCache = -65550,
+ mStatus_Incompatible = -65551,
+ mStatus_ConfigChanged = -65791,
mStatus_MemFree = -65792 // 0xFFFE FF00
};
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
// ***************************************************************************
#pragma mark - Resource Record structures
#endif
-// Shared Resource Records do not have to be unique
-// -- Shared Resource Records are used for NIAS service PTRs
+// 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 replies to reduce collisions when all the hosts reply to the same query
+// -- 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
-
-// Unique Resource Records should be unique among hosts within any given mDNS scope
+// -- 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
-// -- Replies may be sent immediately, because only one host should be replying to any particular query
+// -- Responses may be sent immediately, because only one host should be responding to any particular query
// -- These RRs typically have low TTLs (e.g. ten seconds)
// -- 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
+// * 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 = 0x08, // Will become a kDNSRecordTypeVerified when probing is complete
+ kDNSRecordTypeUnique = 0x02, // Will become a kDNSRecordTypeVerified when probing is complete
- kDNSRecordTypePacketAnswer = 0x10, // Received in the Answer Section of a DNS Response
- kDNSRecordTypePacketAdditional = 0x11, // Received in the Additional Section of a DNS Response
- kDNSRecordTypePacketUniqueAns = 0x18, // Received in the Answer Section of a DNS Response with kDNSQClass_CacheFlushBit set
- kDNSRecordTypePacketUniqueAdd = 0x19, // Received in the Additional Section of a DNS Response with kDNSQClass_CacheFlushBit set
+ 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
- kDNSRecordTypeShared = 0x20, // Shared means record name does not have to be unique -- so use random delay on replies
- kDNSRecordTypeVerified = 0x28, // Unique means mDNS should check that name is unique (and then send immediate replies)
- kDNSRecordTypeKnownUnique = 0x29, // Known Unique means mDNS can assume name is unique without checking
-
- kDNSRecordTypeUniqueMask = 0x08, // Test for records that are supposed to not be shared with other hosts
- kDNSRecordTypeRegisteredMask = 0xF8, // Test for records that have not had mDNS_Deregister called on them yet
- kDNSRecordTypeActiveMask = 0xF0 // Test for all records that have finished their probing and are now active
- };
+ kDNSRecordTypeUniqueMask = (kDNSRecordTypeUnique | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique),
+ kDNSRecordTypeActiveMask = (kDNSRecordTypeAdvisory | kDNSRecordTypeShared | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique),
-enum
- {
- kDNSSendPriorityNone = 0, // Don't need to send this record right now
- kDNSSendPriorityAdditional = 1, // Send this record as an additional, if we have space in the packet
- kDNSSendPriorityAnswer = 2 // Need to send this record as an answer
+ 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 struct { mDNSu16 priority; mDNSu16 weight; mDNSIPPort port; domainname target; } rdataSRV;
+typedef struct { mDNSu16 preference; domainname exchange; } rdataMX;
+
+// 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[768]; // Generic untyped data (temporarily set 768 for the benefit of Airport Extreme printing)
- mDNSIPAddr ip; // For 'A' record
- domainname name; // For PTR and CNAME records
- UTF8str255 txt; // For TXT record
- rdataSRV srv; // For SRV record
+ mDNSu8 data[StandardAuthRDSize];
+ mDNSv4Addr ip; // For 'A' record
+ mDNSv6Addr ipv6; // For 'AAAA' record
+ domainname name; // For PTR and CNAME records
+ UTF8str255 txt; // For TXT record
+ rdataSRV srv; // For SRV record
+ rdataMX mx; // For MX record
} RDataBody;
typedef struct
{
mDNSu16 MaxRDLength; // Amount of storage allocated for rdata (usually sizeof(RDataBody))
- mDNSu16 RDLength; // Size of the rdata currently stored here
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 ResourceRecord_struct *ResourceRecordPtr;
-
+typedef struct DNSQuestion_struct DNSQuestion;
typedef struct mDNS_struct mDNS;
typedef struct mDNS_PlatformSupport_struct mDNS_PlatformSupport;
-typedef void mDNSRecordCallback(mDNS *const m, ResourceRecord *const rr, mStatus result);
-typedef void mDNSRecordUpdateCallback(mDNS *const m, ResourceRecord *const rr, RData *OldRData);
+// 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);
-// Fields labelled "AR:" apply to our authoritative records
-// Fields labelled "CR:" apply to cache records
-// Fields labelled "--:" apply to both
-// (May want to make this a union later, but not now, because using the
-// same storage for two different purposes always makes debugging harder.)
struct ResourceRecord_struct
{
- ResourceRecord *next; // --: Next in list
-
- // Field Group 1: Persistent metadata for Authoritative Records
- ResourceRecord *Additional1; // AR: Recommended additional record to include in response
- ResourceRecord *Additional2; // AR: Another additional
- ResourceRecord *DependentOn; // AR: This record depends on another for its uniqueness checking
- ResourceRecord *RRSet; // AR: This unique record is part of an RRSet
- mDNSRecordCallback *Callback; // AR: Callback function to call for state changes
- void *Context; // AR: Context parameter for the callback function
- mDNSu8 RecordType; // --: See enum above
- mDNSu8 HostTarget; // AR: Set if the target of this record (PTR, CNAME, SRV, etc.) is our host name
-
- // Field Group 2: Transient state for Authoritative Records
- mDNSu8 Acknowledged; // AR: Set if we've given the success callback to the client
- mDNSu8 ProbeCount; // AR: Number of probes remaining before this record is valid (kDNSRecordTypeUnique)
- mDNSu8 AnnounceCount; // AR: Number of announcements remaining (kDNSRecordTypeShared)
- mDNSu8 IncludeInProbe; // AR: Set if this RR is being put into a probe right now
- mDNSu8 SendPriority; // AR: See enum above
- mDNSIPAddr Requester; // AR: Used for inter-packet duplicate suppression
- // If set, give the IP address of the last host that sent a truncated query for this record
- // If set to all-ones, more than one host sent such a request in the last few milliseconds
- ResourceRecord *NextResponse; // AR: Link to the next element in the chain of responses to generate
- const mDNSu8 *NR_AnswerTo; // AR: Set if this record was selected by virtue of being a direct answer to a question
- ResourceRecord *NR_AdditionalTo; // AR: Set if this record was selected by virtue of being additional to another
- mDNSs32 LastSendTime; // AR: In platform time units
- mDNSs32 NextSendTime; // AR: In platform time units
- mDNSs32 NextSendInterval; // AR: In platform time units
- RData *NewRData; // AR: Set if we are updating this record with new rdata
- mDNSRecordUpdateCallback *UpdateCallback;
-
- // Field Group 3: Transient state for Cache Records
- ResourceRecord *NextDupSuppress; // CR: Link to the next element in the chain of duplicate suppression answers to send
- mDNSs32 TimeRcvd; // CR: In platform time units
- mDNSs32 LastUsed; // CR: In platform time units
- mDNSu32 UseCount; // CR: Number of times this RR has been used to answer a question
- mDNSu32 UnansweredQueries; // CR: Number of times we've issued a query for this record without getting an answer
- mDNSBool Active; // CR: Set if there is currently a question referencing this answer
- mDNSBool NewData; // CR: Set if this is a record we just received
-
- // Field Group 4: The actual information pertaining to this resource record
- mDNSIPAddr InterfaceAddr; // --: Set if this RR is specific to one interface (e.g. a linklocal address)
- // For records received off the wire, InterfaceAddr is *always* set to the receiving interface
- // For our authoritative records, InterfaceAddr is usually zero,
- // except those few records that are interface-specific (e.g. linklocal address records)
- domainname name; // --: All the rest are used both in our authoritative records and in cache records
+ 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.
- mDNSu32 rrremainingttl; // In seconds. Always set to correct value before calling question callback.
+ 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
+ };
+
+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;
+
+ // 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)
+ 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;
struct NetworkInterfaceInfo_struct
{
+ // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them.
NetworkInterfaceInfo *next;
- mDNSIPAddr ip;
- mDNSBool Advertise; // Set Advertise to false if you are only searching on this interface
- // Standard ResourceRecords that every Responder host should have (one per active IP address)
- ResourceRecord RR_A1; // 'A' (address) record for our ".local" name
- ResourceRecord RR_A2; // 'A' record for our ".local.arpa" name
- ResourceRecord RR_PTR; // PTR (reverse lookup) record
+
+ mDNSBool InterfaceActive; // InterfaceActive is set if interface is sending & receiving packets
+ // InterfaceActive is clear if interface is here to represent an address with A
+ // and/or AAAA records, but there is already an earlier representative for this
+ // physical interface which will be used for the actual sending & receiving
+ // packets (this status may change as interfaces are added and removed)
+ 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;
+ mDNSAddr ip;
+ mDNSBool Advertise; // Set Advertise to false if you are only searching on this interface
+ mDNSBool TxAndRx; // Set to false if not sending and receiving packets on this interface
};
typedef struct ExtraResourceRecord_struct ExtraResourceRecord;
struct ExtraResourceRecord_struct
{
ExtraResourceRecord *next;
- ResourceRecord r;
- // Note: Add any additional fields *before* the ResourceRecord in this structure, not at the end.
+ 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 ResourceRecord getting smashed
+ // 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
{
- mDNSServiceCallback *Callback;
- void *Context;
- ExtraResourceRecord *Extras; // Optional list of extra ResourceRecords attached to this service registration
+ // 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.
+ 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
- ResourceRecord RR_PTR; // e.g. _printer._tcp.local. PTR Name._printer._tcp.local.
- ResourceRecord RR_SRV; // e.g. Name._printer._tcp.local. SRV 0 0 port target
- ResourceRecord RR_TXT; // e.g. Name._printer._tcp.local. TXT PrintQueueName
- // Don't add any fields after ResourceRecord RR_TXT.
+ AuthRecord RR_ADV; // e.g. _services._mdns._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
};
#pragma mark - Question structures
#endif
-typedef struct DNSQuestion_struct DNSQuestion;
-typedef void mDNSQuestionCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer);
+// 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;
+
+// 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;
- mDNSs32 NextQTime; // In platform time units
- mDNSs32 ThisQInterval; // In platform time units (zero for questions not in list)
- // ThisQInterval will be non-zero for an active question;
- // Zero for a cancelled or inactive question
- mDNSs32 NextQInterval;
+ mDNSu32 qnamehash;
+ mDNSs32 LastQTime; // Last scheduled tranmission of this Q on *all* applicable interfaces
+ mDNSs32 ThisQInterval; // LastQTime + ThisQInterval is the next scheduled tranmission 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;
- mDNSIPAddr InterfaceAddr; // Non-zero if you want to issue link-local queries only on a single specific IP interface
- domainname name;
- mDNSu16 rrtype;
- mDNSu16 rrclass;
- mDNSQuestionCallback *Callback;
- void *Context;
+ 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
+
+ // Client API fields: The client must set up these fields *before* calling mDNS_StartQuery()
+ mDNSInterfaceID InterfaceID; // Non-zero if you want to issue link-local queries only on a single specific IP interface
+ domainname qname;
+ mDNSu16 qtype;
+ mDNSu16 qclass;
+ mDNSQuestionCallback *QuestionCallback;
+ void *QuestionContext;
};
typedef struct
{
- domainname name;
- mDNSIPAddr InterfaceAddr; // Local (source) IP Interface (needed for scoped addresses such as link-local)
- mDNSIPAddr 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)
+ // 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 ServiceInfoQueryCallback(mDNS *const m, ServiceInfoQuery *query);
+typedef void mDNSServiceInfoQueryCallback(mDNS *const m, ServiceInfoQuery *query);
struct ServiceInfoQuery_struct
{
- DNSQuestion qSRV;
- DNSQuestion qTXT;
- DNSQuestion qADD;
- mDNSu8 GotSRV;
- mDNSu8 GotTXT;
- mDNSu8 GotADD;
- ServiceInfo *info;
- ServiceInfoQueryCallback *Callback;
- void *Context;
+ // 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;
};
// ***************************************************************************
typedef void mDNSCallback(mDNS *const m, mStatus result);
+#define CACHE_HASH_SLOTS 499
+
+enum
+ {
+ mDNS_KnownBug_PhantomInterfaces = 1
+ };
+
struct mDNS_struct
{
- mDNS_PlatformSupport *p; // Pointer to platform-specific data of indeterminite size
+ // 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 AdvertiseLocalAddresses;
mStatus mDNSPlatformStatus;
- mDNSCallback *Callback;
- void *Context;
-
- mDNSu32 mDNS_busy; // For debugging: To catch and report locking failures
-
- mDNSu8 lock_rrcache; // For debugging: Set at times when these lists may not be modified
- mDNSu8 lock_Questions;
- mDNSu8 lock_Records;
- mDNSu8 padding;
+ 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 *ActiveQuestions; // List of all active questions
- DNSQuestion *NewQuestions; // Fresh questions not yet answered from cache
- DNSQuestion *CurrentQuestion; // Next question about to be examined in AnswerLocalQuestions()
- mDNSu32 rrcache_size;
- mDNSu32 rrcache_used;
+ 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 ~0 ("local only")
+ 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;
- ResourceRecord *rrcache_free;
- ResourceRecord *rrcache;
+ CacheRecord *rrcache_free;
+ CacheRecord *rrcache_hash[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 hostname1; // Primary Host Name "Foo.local."
- domainname hostname2; // Secondary Host Name "Foo.local.arpa."
- ResourceRecord *ResourceRecords;
- ResourceRecord *CurrentRecord; // Next ResourceRecord about to be examined
+ 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 ~0 ("local only")
+ 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 SuppressSending;
mDNSs32 ProbeFailTime;
mDNSs32 NumFailedProbes;
mDNSs32 SuppressProbes;
- mDNSBool SleepState;
- mDNSBool NetChanged;
};
// ***************************************************************************
#pragma mark - Useful Static Constants
#endif
-extern const ResourceRecord zeroRR;
-extern const mDNSIPPort zeroIPPort;
-extern const mDNSIPAddr zeroIPAddr;
-extern const mDNSIPAddr onesIPAddr;
-
-extern const mDNSIPPort UnicastDNSPort;
-extern const mDNSIPPort MulticastDNSPort;
-extern const mDNSIPAddr AllDNSLinkGroup;
-extern const mDNSIPAddr AllDNSAdminGroup;
+extern const mDNSIPPort zeroIPPort;
+extern const mDNSv4Addr zeroIPAddr;
+extern const mDNSv6Addr zerov6Addr;
+extern const mDNSv4Addr onesIPv4Addr;
+extern const mDNSv6Addr onesIPv6Addr;
+extern const mDNSInterfaceID mDNSInterface_Any;
+
+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;
// ***************************************************************************
#if 0
// Every client should call mDNS_Init, passing in storage for the mDNS object, mDNS_PlatformSupport object, and rrcache.
// The rrcachesize parameter is the size of (i.e. number of entries in) the rrcache array passed in.
-// When mDNS has finished setting up the initComplete callback is called
+// 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 ResourceRecord object to register a resource record
+// 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 reply
+// 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
//
// interrupt-time timer callback while in the middle of processing a client call.
extern mStatus mDNS_Init (mDNS *const m, mDNS_PlatformSupport *const p,
- ResourceRecord *rrcachestorage, mDNSu32 rrcachesize, mDNSCallback *Callback, void *Context);
+ CacheRecord *rrcachestorage, mDNSu32 rrcachesize,
+ mDNSBool AdvertiseLocalAddresses,
+ mDNSCallback *Callback, void *Context);
+#define mDNS_Init_NoCache mDNSNULL
+#define mDNS_Init_ZeroCacheSize 0
+#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 mStatus mDNS_Register (mDNS *const m, ResourceRecord *const rr);
-extern mStatus mDNS_Update (mDNS *const m, ResourceRecord *const rr, mDNSu32 newttl,
+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 void mDNS_Deregister(mDNS *const m, ResourceRecord *const rr);
+extern mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr);
+
extern mStatus mDNS_StartQuery(mDNS *const m, DNSQuestion *const question);
-extern void mDNS_StopQuery (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_RegisterHostSet is a single call to register the standard resource records associated with every host.
// 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,
// 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(ResourceRecord *rr, RData *RDataStorage, mDNSIPAddr InterfaceAddr,
+extern void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID,
mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, mDNSRecordCallback Callback, void *Context);
-extern void mDNS_GenerateFQDN(mDNS *const m);
-extern mStatus mDNS_RegisterInterface (mDNS *const m, NetworkInterfaceInfo *set);
-extern void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set);
-
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,
- mDNSServiceCallback Callback, void *Context);
+ 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 void mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr);
+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 mDNSIPAddr InterfaceAddr, mDNSQuestionCallback *Callback, void *Context);
+ 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, ServiceInfoQueryCallback *Callback, void *Context);
+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_DomainTypeRegistrationDefault = 3
} mDNS_DomainType;
-extern mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNSu8 DomainType, const mDNSIPAddr InterfaceAddr, mDNSQuestionCallback *Callback, void *Context);
+extern mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context);
#define mDNS_StopGetDomains mDNS_StopQuery
-extern mStatus mDNS_AdvertiseDomains(mDNS *const m, ResourceRecord *rr, mDNSu8 DomainType, const mDNSIPAddr InterfaceAddr, char *domname);
+extern mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname);
#define mDNS_StopAdvertiseDomains mDNS_Deregister
// ***************************************************************************
// 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.
+// Comparison functions
extern mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b);
extern mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2);
-extern mDNSu32 DomainNameLength(const domainname *const name);
-extern void AppendDomainLabelToName(domainname *const name, const domainlabel *const label);
-extern void AppendStringLabelToName(domainname *const name, const char *cstr);
-extern void AppendDomainNameToName(domainname *const name, const domainname *const append);
-extern void AppendStringNameToName(domainname *const name, const char *cstr);
-
-extern void ConvertCStringToDomainLabel(const char *src, domainlabel *label);
-extern mDNSu8 *ConvertCStringToDomainName(const char *const cstr, domainname *name);
+// 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
+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);
-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), '\\')
+// 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))
-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);
+// ***************************************************************************
+#if 0
+#pragma mark - Other utility functions
+#endif
-extern mDNSu8 *ConstructServiceName(domainname *const fqdn, const domainlabel *const name, const domainname *const type, const domainname *const domain);
-extern mDNSBool DeconstructServiceName(const domainname *const fqdn, domainlabel *const name, domainname *const type, domainname *const domain);
+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 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);
#ifdef __cplusplus
}
#endif
+
+#endif
-// Set DEBUGBREAKS to 0 to optimize debugf() calls out of the compiled code
-// Set DEBUGBREAKS to 1 to generate normal debugging messages
-// Set DEBUGBREAKS to 2 to generate verbose debugging messages
-// DEBUGBREAKS is normally set in the project options (or makefile) but can also be set here if desired
+/*
+ * 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@
-//#define DEBUGBREAKS 2
+ Change History (most recent first):
+
+$Log: mDNSDebug.h,v $
+Revision 1.14 2003/08/12 19:56:24 cheshire
+Update to APSL 2.0
+
+Revision 1.13 2003/07/02 21:19:46 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.12 2003/05/26 03:01:27 cheshire
+<rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
+
+Revision 1.11 2003/05/21 17:48:10 cheshire
+Add macro to enable GCC's printf format string checking
+
+Revision 1.10 2003/04/26 02:32:57 cheshire
+Add extern void LogMsg(const char *format, ...);
+
+Revision 1.9 2002/09/21 20:44:49 zarzycki
+Added APSL info
+
+Revision 1.8 2002/09/19 04:20:43 cheshire
+Remove high-ascii characters that confuse some systems
+
+Revision 1.7 2002/09/16 18:41:42 cheshire
+Merge in license terms from Quinn's copy, in preparation for Darwin release
+
+*/
+
+#ifndef __mDNSDebug_h
+#define __mDNSDebug_h
+
+// Set MDNS_DEBUGMSGS to 0 to optimize debugf() calls out of the compiled code
+// Set MDNS_DEBUGMSGS to 1 to generate normal debugging messages
+// Set MDNS_DEBUGMSGS to 2 to generate verbose debugging messages
+// MDNS_DEBUGMSGS is normally set in the project options (or makefile) but can also be set here if desired
+
+//#define MDNS_DEBUGMSGS 2
+
+// Set MDNS_CHECK_PRINTF_STYLE_FUNCTIONS to 1 to enable extra GCC compiler warnings
+// Note: You don't normally want to do this, because it generates a bunch of
+// spurious warnings for the following custom extensions implemented by mDNS_vsnprintf:
+// warning: `#' flag used with `%s' printf format (for %#s -- pascal string format)
+// warning: repeated `#' flag in format (for %##s -- DNS name string format)
+// warning: double format, pointer arg (arg 2) (for %.4a, %.16a, %#a -- IP address formats)
+#define MDNS_CHECK_PRINTF_STYLE_FUNCTIONS 0
+#if MDNS_CHECK_PRINTF_STYLE_FUNCTIONS
+#define IS_A_PRINTF_STYLE_FUNCTION(F,A) __attribute__ ((format(printf,F,A)))
+#else
+#define IS_A_PRINTF_STYLE_FUNCTION(F,A)
+#endif
#ifdef __cplusplus
extern "C" {
#endif
-#if DEBUGBREAKS
+#if MDNS_DEBUGMSGS
#define debugf debugf_
-extern void debugf_(const char *format, ...);
+extern void debugf_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
#else // If debug breaks are off, use a preprocessor trick to optimize those calls out of the code
#if( defined( __GNUC__ ) )
- #define debugf( ARGS... )
+ #define debugf( ARGS... ) ((void)0)
#elif( defined( __MWERKS__ ) )
#define debugf( ... )
#else
- #define debugf 1 ? ((void) 0) : (void)
+ #define debugf 1 ? ((void)0) : (void)
#endif
#endif
-#if DEBUGBREAKS > 1
-#define verbosedebugf debugf_
+#if MDNS_DEBUGMSGS > 1
+#define verbosedebugf verbosedebugf_
+extern void verbosedebugf_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
#else
#if( defined( __GNUC__ ) )
- #define verbosedebugf( ARGS... )
+ #define verbosedebugf( ARGS... ) ((void)0)
#elif( defined( __MWERKS__ ) )
#define verbosedebugf( ... )
#else
- #define verbosedebugf 1 ? ((void) 0) : (void)
+ #define verbosedebugf 1 ? ((void)0) : (void)
#endif
#endif
+// LogMsg is used even in shipping code, to write truly serious error messages to syslog (or equivalent)
+extern void LogMsg(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
+
#ifdef __cplusplus
}
#endif
+
+#endif
+++ /dev/null
-// mDNS-PlatformEnvironment.h needs to ensure that the necessary mDNS types are defined,
-// plus whatever additional types are needed to support a particular platform
-
-// To add support for a new target platform with its own networking APIs and types,
-// duplicate the "#elif __SOME_OTHER_OS__" section (including its two-line comment
-// at the start) and add support for the new target platform in the new section.
-
-#pragma once
-
-#ifdef __cplusplus
- extern "C" {
-#endif
-
-// ***************************************************************************
-// Classic Mac (Open Transport) structures
-
-#if (TARGET_API_MAC_OS8 || __MACOS__)
-
-// Headers needed for code on this platform
-#include <OpenTptInternet.h>
-#include <OpenTptClient.h>
-
-typedef enum
- {
- mOT_Reset = 0,
- mOT_Start,
- mOT_ReusePort,
- mOT_RcvDestAddr,
- mOT_LLScope,
- mOT_AdminScope,
- mOT_Bind,
- mOT_Ready
- } mOT_State;
-
-typedef struct { TOptionHeader h; mDNSIPAddr multicastGroupAddress; mDNSIPAddr InterfaceAddress; } TIPAddMulticastOption;
-typedef struct { TOptionHeader h; UInt32 flag; } TSetBooleanOption;
-
-// TOptionBlock is a union of various types.
-// What they all have in common is that they all start with a TOptionHeader.
-typedef union { TOptionHeader h; TIPAddMulticastOption m; TSetBooleanOption b; } TOptionBlock;
-
-struct mDNS_PlatformSupport_struct
- {
- EndpointRef ep;
- UInt32 mOTstate; // mOT_State enum
- TOptionBlock optBlock;
- TOptMgmt optReq;
- long OTTimerTask;
- UInt32 nesting;
-
- // Platforms that support multi-homing will want a list of HostRecordSets instead of just one
- HostRecordSet hostset;
- };
-
-// ***************************************************************************
-// Mac OS X structures
-
-#elif (TARGET_API_MAC_OSX || __MACOSX__)
-
-// Headers needed for code on this platform
-#include <SystemConfiguration/SystemConfiguration.h>
-#include <IOKit/pwr_mgt/IOPMLib.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-
-struct mDNS_PlatformSupport_struct
- {
- CFRunLoopTimerRef CFTimer;
- SCDynamicStoreRef Store;
- CFRunLoopSourceRef StoreRLS;
- io_connect_t PowerConnection;
- io_object_t PowerNotifier;
- CFRunLoopSourceRef PowerRLS;
- };
-
-// Set this symbol to 1 to do extra debug checks on malloc() and free()
-#define MACOSX_MDNS_MALLOC_DEBUGGING 0
-
-#if MACOSX_MDNS_MALLOC_DEBUGGING
-extern void *mallocL(char *msg, unsigned int size);
-extern void freeL(char *msg, void *x);
-#else
-#define mallocL(X,Y) malloc(Y)
-#define freeL(X,Y) free(Y)
-#endif
-
-// ***************************************************************************
-// Placeholder for future platforms
-
-#elif __SOME_OTHER_OS__
-
-// ***************************************************************************
-// Generic code for Unix-style platforms
-
-#else
-
-#error Other platforms need to make sure that types like UInt16 are defined
-
-#endif
-
-#ifdef __cplusplus
- }
-#endif
+/*
+ * 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: mDNSPlatformFunctions.h,v $
+Revision 1.22 2003/08/18 22:53:37 cheshire
+<rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformTimeNow()
+
+Revision 1.21 2003/08/15 20:16:57 cheshire
+Update comment for <rdar://problem/3366590> mDNSResponder takes too much RPRVT
+
+Revision 1.20 2003/08/12 19:56:24 cheshire
+Update to APSL 2.0
+
+Revision 1.19 2003/08/05 22:20:15 cheshire
+<rdar://problem/3330324> Need to check IP TTL on responses
+
+Revision 1.18 2003/07/22 23:57:20 cheshire
+Move platform-layer function prototypes from mDNSClientAPI.h to mDNSPlatformFunctions.h where they belong
+
+Revision 1.17 2003/07/19 03:15:15 cheshire
+Add generic MemAllocate/MemFree prototypes to mDNSPlatformFunctions.h,
+and add the obvious trivial implementations to each platform support layer
+
+Revision 1.16 2003/07/02 21:19:46 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.15 2003/05/23 22:39:45 cheshire
+<rdar://problem/3268151> Need to adjust maximum packet size for IPv6
+
+Revision 1.14 2003/04/28 21:54:57 cheshire
+Fix compiler warning
+
+Revision 1.13 2003/03/15 04:40:36 cheshire
+Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID"
+
+Revision 1.12 2003/02/21 01:54:08 cheshire
+Bug #: 3099194 mDNSResponder needs performance improvements
+Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt")
+
+Revision 1.11 2002/12/23 22:13:29 jgraessl
+
+Reviewed by: Stuart Cheshire
+Initial IPv6 support for mDNSResponder.
+
+Revision 1.10 2002/09/21 20:44:49 zarzycki
+Added APSL info
+
+Revision 1.9 2002/09/19 04:20:43 cheshire
+Remove high-ascii characters that confuse some systems
+
+Revision 1.8 2002/09/16 23:12:14 cheshire
+Minor code tidying
+
+Revision 1.7 2002/09/16 18:41:42 cheshire
+Merge in license terms from Quinn's copy, in preparation for Darwin release
+
+*/
+
+#ifndef __mDNSPlatformFunctions_h
+#define __mDNSPlatformFunctions_h
+
// ***************************************************************************
// Support functions which must be provided by each set of specific PlatformSupport files
// When Setup is complete, the callback is called.
// mDNSPlatformSendUDP() sends one UDP packet
// When a packet is received, the PlatformSupport code calls mDNSCoreReceive()
-// mDNSPlatformScheduleTask() indicates that a timer should be set,
-// and mDNSCoreTask() should be called when the timer expires
// mDNSPlatformClose() tidies up on exit
#ifdef __cplusplus
// 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
-#define AbsoluteMaxDNSMessageData 8960
-#define NormalMaxDNSMessageData 1460
+// 40 (IPv6 header) + 8 (UDP header) + 12 (DNS message header) + 1440 (DNS message body) = 1500 total
+#define AbsoluteMaxDNSMessageData 8940
+#define NormalMaxDNSMessageData 1440
typedef struct
{
DNSMessageHeader h; // Note: Size 12 bytes
- mDNSu8 data[AbsoluteMaxDNSMessageData]; // 20 (IP) + 8 (UDP) + 12 (header) + 8960 (data) = 9000
+ mDNSu8 data[AbsoluteMaxDNSMessageData]; // 40 (IPv6) + 8 (UDP) + 12 (DNS header) + 8940 (data) = 9000
} DNSMessage;
// ***************************************************************************
// Functions
-// Every platform support module must provide the following functions
-extern mStatus mDNSPlatformInit (mDNS *const m);
-extern void mDNSPlatformClose (mDNS *const m);
+// Every platform support module must provide the following functions.
+// Note: mDNSPlatformMemAllocate/mDNSPlatformMemFree are only required for handling oversized resource records.
+// 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,
- mDNSIPAddr src, mDNSIPPort srcport, mDNSIPAddr dst, mDNSIPPort dstport);
+ mDNSInterfaceID InterfaceID, mDNSIPPort srcport, const mDNSAddr *dst, mDNSIPPort dstport);
-extern void mDNSPlatformScheduleTask(const mDNS *const m, mDNSs32 NextTaskTime);
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 mDNSs32 mDNSPlatformTimeNow();
-extern mDNSs32 mDNSPlatformOneSecond;
+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);
// 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 dot-local host name change.
+//
+// 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)
+//
+// 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 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, DNSMessage *const msg, const mDNSu8 *const end,
- mDNSIPAddr srcaddr, mDNSIPPort srcport, mDNSIPAddr dstaddr, mDNSIPPort dstport, mDNSIPAddr InterfaceAddr);
-extern void mDNSCoreTask (mDNS *const m);
-extern void mDNSCoreSleep (mDNS *const m, mDNSBool wake);
+ 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);
#ifdef __cplusplus
}
#endif
+
+#endif
+++ /dev/null
-#include <stdio.h>
-#include <stdarg.h> // For va_list support
-
-#include "mDNSsprintf.h"
-#include "mDNSvsprintf.h"
-
-static const struct mDNSsprintf_format
- {
- unsigned leftJustify : 1;
- unsigned forceSign : 1;
- unsigned zeroPad : 1;
- unsigned havePrecision : 1;
- unsigned hSize : 1;
- unsigned lSize : 1;
- char altForm;
- char sign; // +, - or space
- int fieldWidth;
- int precision;
- } default_format = { 0 };
-
-#define BUFLEN 512
-
-int mDNS_vsprintf(char *sbuffer, const char *fmt, va_list arg)
- {
- int c, nwritten = 0;
-
- for (c = *fmt; c; c = *++fmt)
- {
- int i=0, j;
- char buf[BUFLEN], *digits;
- char *s = &buf[BUFLEN];
- struct mDNSsprintf_format F;
- if (c != '%') goto copy1;
- F = default_format;
-
- for (;;) // decode flags
- {
- c = *++fmt;
- if (c == '-') F.leftJustify = 1;
- else if (c == '+') F.forceSign = 1;
- else if (c == ' ') F.sign = ' ';
- else if (c == '#') F.altForm++;
- else if (c == '0') F.zeroPad = 1;
- else break;
- }
-
- if (c == '*') // decode field width
- {
- if ((F.fieldWidth = va_arg(arg, int)) < 0)
- {
- F.leftJustify = 1;
- F.fieldWidth = -F.fieldWidth;
- }
- c = *++fmt;
- }
- else
- {
- for (; c >= '0' && c <= '9'; c = *++fmt)
- F.fieldWidth = (10 * F.fieldWidth) + (c - '0');
- }
-
- if (c == '.') // decode precision
- {
- if ((c = *++fmt) == '*')
- { F.precision = va_arg(arg, int); c = *++fmt; }
- else for (; c >= '0' && c <= '9'; c = *++fmt)
- F.precision = (10 * F.precision) + (c - '0');
- if (F.precision >= 0) F.havePrecision = 1;
- }
-
- if (F.leftJustify) F.zeroPad = 0;
-
-conv: switch (c) // perform appropriate conversion
- {
- unsigned long n;
- case 'h' : F.hSize = 1; c = *++fmt; goto conv;
- case 'l' : // fall through
- case 'L' : F.lSize = 1; c = *++fmt; goto conv;
- case 'd' :
- case 'i' : if (F.lSize) n = (unsigned long)va_arg(arg, long);
- else n = (unsigned long)va_arg(arg, int);
- if (F.hSize) n = (short) n;
- if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; }
- else if (F.forceSign) F.sign = '+';
- goto decimal;
- case 'u' : if (F.lSize) n = va_arg(arg, unsigned long);
- else n = va_arg(arg, unsigned int);
- if (F.hSize) n = (unsigned short) n;
- F.sign = 0;
- goto decimal;
- decimal: if (!F.havePrecision)
- {
- if (F.zeroPad)
- {
- F.precision = F.fieldWidth;
- if (F.sign) --F.precision;
- }
- if (F.precision < 1) F.precision = 1;
- }
- for (i = 0; n; n /= 10, i++) *--s = (char)(n % 10 + '0');
- for (; i < F.precision; i++) *--s = '0';
- if (F.sign) { *--s = F.sign; i++; }
- break;
-
- case 'o' : if (F.lSize) n = va_arg(arg, unsigned long);
- else n = va_arg(arg, unsigned int);
- if (F.hSize) n = (unsigned short) n;
- if (!F.havePrecision)
- {
- if (F.zeroPad) F.precision = F.fieldWidth;
- if (F.precision < 1) F.precision = 1;
- }
- for (i = 0; n; n /= 8, i++) *--s = (char)(n % 8 + '0');
- if (F.altForm && i && *s != '0') { *--s = '0'; i++; }
- for (; i < F.precision; i++) *--s = '0';
- break;
-
- case 'a' : {
- unsigned char *a = va_arg(arg, unsigned char *);
- unsigned short *w = (unsigned short *)a;
- s = buf;
- switch (F.precision)
- {
- case 4: i = mDNS_sprintf(s, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]); break;
- case 6: i = mDNS_sprintf(s, "%02X:%02X:%02X:%02X:%02X:%02X", a[0], a[1], a[2], a[3], a[4], a[5]); break;
- case 16: i = mDNS_sprintf(s, "%04X:%04X:%04X:%04X:%04X:%04X:%04X:%04X",
- w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7]); break;
- default: i = mDNS_sprintf(s, "%s", "ERROR: Must specify address size "
- "(i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break;
- }
- }
- break;
-
- case 'p' : F.havePrecision = F.lSize = 1;
- F.precision = 8;
- case 'X' : digits = "0123456789ABCDEF";
- goto hexadecimal;
- case 'x' : digits = "0123456789abcdef";
- hexadecimal:if (F.lSize) n = va_arg(arg, unsigned long);
- else n = va_arg(arg, unsigned int);
- if (F.hSize) n = (unsigned short) n;
- if (!F.havePrecision)
- {
- if (F.zeroPad)
- {
- F.precision = F.fieldWidth;
- if (F.altForm) F.precision -= 2;
- }
- if (F.precision < 1) F.precision = 1;
- }
- for (i = 0; n; n /= 16, i++) *--s = digits[n % 16];
- for (; i < F.precision; i++) *--s = '0';
- if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; }
- break;
-
- case 'c' : *--s = (char)va_arg(arg, int); i = 1; break;
-
- case 's' : s = va_arg(arg, char *);
- switch (F.altForm)
- {
- case 0: { char *a=s; i=0; while(*a++) i++; break; } // C string
- case 1: i = (unsigned char) *s++; break; // Pascal string
- case 2: { // DNS label-sequence name
- unsigned char *a = (unsigned char *)s;
- s = buf;
- if (*a == 0) *s++ = '.'; // Special case for root DNS name
- while (*a && s + *a + 1 < &buf[BUFLEN])
- {
- s += mDNS_sprintf(s, "%#s.", a);
- a += 1 + *a;
- }
- i = (int)(s - buf);
- s = buf;
- break;
- }
- }
- if (F.havePrecision && i > F.precision) i = F.precision;
- break;
-
- case 'n' : s = va_arg(arg, char *);
- if (F.hSize) * (short *) s = (short)nwritten;
- else if (F.lSize) * (long *) s = (long)nwritten;
- else * (int *) s = (int)nwritten;
- continue;
-
- // oops - unknown conversion, abort
-
- case 'M': case 'N': case 'O': case 'P': case 'Q':
- case 'R': case 'S': case 'T': case 'U': case 'V':
- // (extra cases force this to be an indexed switch)
- default: goto done;
-
- case '%' :
- copy1 : *sbuffer++ = (char)c; ++nwritten; continue;
- }
-
- // pad on the left
-
- if (i < F.fieldWidth && !F.leftJustify)
- do { *sbuffer++ = ' '; ++nwritten; } while (i < --F.fieldWidth);
-
- // write the converted result
-
- for (j=0; j<i; j++) *sbuffer++ = *s++;
- nwritten += i;
-
- // pad on the right
-
- for (; i < F.fieldWidth; i++)
- { *sbuffer++ = ' '; ++nwritten; }
- }
-
-done: return(nwritten);
- }
-
-int mDNS_sprintf(char *sbuffer, const char *fmt, ...)
-{
- int length;
-
- va_list ptr;
- va_start(ptr,fmt);
- length = mDNS_vsprintf(sbuffer, fmt, ptr);
- sbuffer[length] = 0;
- va_end(ptr);
-
- return length;
-}
+++ /dev/null
-#ifdef __cplusplus
- extern "C" {
-#endif
-
-extern int mDNS_sprintf(char *sbuffer, const char *fmt, ...);
-
-#ifdef __cplusplus
- }
-#endif
+++ /dev/null
-#ifdef __cplusplus
- extern "C" {
-#endif
-
-extern int mDNS_vsprintf(char *sbuffer, const char *fmt, va_list arg);
-
-#ifdef __cplusplus
- }
-#endif
--- /dev/null
+/*
+ * 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: CarbonResource.r,v $
+Revision 1.5 2003/08/12 19:56:24 cheshire
+Update to APSL 2.0
+
+ */
+
+data 'carb' (0) { };
--- /dev/null
+/*
+ * 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: Mac\040OS\040Test\040Responder.c,v $
+Revision 1.17 2003/08/14 02:19:54 cheshire
+<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
+
+Revision 1.16 2003/08/12 19:56:24 cheshire
+Update to APSL 2.0
+
+ */
+
+#include <stdio.h> // For printf()
+#include <string.h> // For strlen() etc.
+
+#include <Events.h> // For WaitNextEvent()
+#include <SIOUX.h> // For SIOUXHandleOneEvent()
+
+#include "mDNSClientAPI.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, afp, http, njp;
+static AuthRecord browsedomain;
+
+// 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[])
+ {
+ mDNSIPPort port;
+ domainname t;
+ domainname d;
+ char buffer[512];
+ UInt8 txtbuffer[512];
+
+ port.b[0] = (UInt8)(PortAsNumber >> 8);
+ port.b[1] = (UInt8)(PortAsNumber );
+ 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, port, // Host and port
+ txtbuffer, (mDNSu16)(1+txtbuffer[0]), // TXT data, length
+ mDNSNULL, 0, // Subtypes (none)
+ mDNSInterface_Any, // Interace 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);
+ }
+
+// CreateProxyRegistrationForRealService() checks to see if the given port is currently
+// in use, and if so, advertises the specified service as present on that port.
+// This is useful for advertising existing real services (Personal Web Sharing, Personal
+// File Sharing, etc.) that currently don't register with mDNS Service Discovery themselves.
+mDNSlocal OSStatus CreateProxyRegistrationForRealService(mDNS *m, UInt16 PortAsNumber, const char txtinfo[],
+ const char *servicetype, ServiceRecordSet *recordset)
+ {
+ mDNSIPPort port;
+ InetAddress ia;
+ TBind bindReq;
+ OSStatus err;
+ TEndpointInfo endpointinfo;
+ EndpointRef ep = OTOpenEndpoint(OTCreateConfiguration(kTCPName), 0, &endpointinfo, &err);
+ if (!ep || err) { printf("OTOpenEndpoint (CreateProxyRegistrationForRealService) failed %d", err); return(err); }
+
+ port.b[0] = (UInt8)(PortAsNumber >> 8);
+ port.b[1] = (UInt8)(PortAsNumber );
+ ia.fAddressType = AF_INET;
+ ia.fPort = port.NotAnInteger;
+ ia.fHost = 0;
+ bindReq.addr.maxlen = sizeof(ia);
+ bindReq.addr.len = sizeof(ia);
+ bindReq.addr.buf = (UInt8*)&ia;
+ bindReq.qlen = 0;
+ err = OTBind(ep, &bindReq, NULL);
+
+ if (err == kOTBadAddressErr)
+ RegisterService(m, recordset, PortAsNumber, txtinfo, &m->nicelabel, servicetype, "local.");
+ else if (err)
+ debugf("OTBind failed %d", err);
+
+ OTCloseProvider(ep);
+ return(noErr);
+ }
+
+// Done once on startup, and then again every time our address changes
+mDNSlocal OSStatus mDNSResponderTestSetup(mDNS *m)
+ {
+ char buffer[256];
+ mDNSv4Addr ip = m->HostInterfaces->ip.ip.v4;
+
+ ConvertDomainNameToCString(&m->hostname, 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;
+
+#define SRSET 0
+#if SRSET==0
+ RegisterFakeServiceForTesting(m, &p1, "path=/index.html", "Web Server One", "_http._tcp.", "local.");
+ RegisterFakeServiceForTesting(m, &p2, "path=/path.html", "Web Server Two", "_http._tcp.", "local.");
+#elif SRSET==1
+ RegisterFakeServiceForTesting(m, &p1, "rn=lpq1", "Epson Stylus 900N", "_printer._tcp.", "local.");
+ RegisterFakeServiceForTesting(m, &p2, "rn=lpq2", "HP LaserJet", "_printer._tcp.", "local.");
+#else
+ RegisterFakeServiceForTesting(m, &p1, "rn=lpq3", "My Printer", "_printer._tcp.", "local.");
+ RegisterFakeServiceForTesting(m, &p2, "lrn=pq4", "My Other Printer", "_printer._tcp.", "local.");
+#endif
+
+ // If AFP Server is running, register a record for it
+ CreateProxyRegistrationForRealService(m, 548, "", "_afpovertcp._tcp.", &afp);
+
+ // If Web Server is running, register a record for it
+ CreateProxyRegistrationForRealService(m, 80, "", "_http._tcp.", &http);
+
+ // And pretend we always have an NJP server running on port 80 too
+ //RegisterService(m, &njp, 80, "NJP/", &m->nicelabel, "_njp._tcp.", "local.");
+
+ // Advertise that apple.com. is available for browsing
+ mDNS_AdvertiseDomains(m, &browsedomain, mDNS_DomainTypeBrowse, mDNSInterface_Any, "IL 2\\4th Floor.apple.com.");
+
+ return(kOTNoError);
+ }
+
+// 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()
+ {
+ extern void mDNSPlatformIdle(mDNS *const m); // Only needed for debugging version
+ mStatus err;
+ Boolean DoneSetup = false;
+
+ SIOUXSettings.asktosaveonclose = false;
+ SIOUXSettings.userwindowtitle = "\pMulticast DNS Responder";
+
+ printf("Prototype Multicast DNS Responder\n\n");
+ printf("WARNING! This is experimental software.\n\n");
+ printf("Multicast DNS is currently an experimental protocol.\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))
+ {
+ // For debugging, use "#define __ONLYSYSTEMTASK__ 1" and call mDNSPlatformIdle() periodically.
+ // For shipping code, don't define __ONLYSYSTEMTASK__, and you don't need to call mDNSPlatformIdle()
+ mDNSPlatformIdle(&m); // Only needed for debugging version
+ if (m.mDNSPlatformStatus == mStatus_NoError && !DoneSetup)
+ {
+ DoneSetup = true;
+ printf("\nListening for mDNS queries...\n");
+ mDNSResponderTestSetup(&m);
+ }
+ }
+
+ if (p1.RR_SRV.resrec.RecordType ) mDNS_DeregisterService(&m, &p1);
+ if (p2.RR_SRV.resrec.RecordType ) mDNS_DeregisterService(&m, &p2);
+ if (afp.RR_SRV.resrec.RecordType ) mDNS_DeregisterService(&m, &afp);
+ if (http.RR_SRV.resrec.RecordType) mDNS_DeregisterService(&m, &http);
+ if (njp.RR_SRV.resrec.RecordType ) mDNS_DeregisterService(&m, &njp);
+
+ mDNS_Close(&m);
+
+ return(0);
+ }
--- /dev/null
+/*
+ * 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: Mac\040OS\040Test\040Searcher.c,v $
+Revision 1.13 2003/08/14 02:19:54 cheshire
+<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
+
+Revision 1.12 2003/08/12 19:56:24 cheshire
+Update to APSL 2.0
+
+ */
+
+#include <stdio.h> // For printf()
+#include <Events.h> // For WaitNextEvent()
+#include <SIOUX.h> // For SIOUXHandleOneEvent()
+
+#include "mDNSClientAPI.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
+ {
+ OTLIFO serviceinfolist;
+ Boolean headerPrinted;
+ Boolean lostRecords;
+ } SearcherServices;
+
+typedef struct { ServiceInfo i; mDNSBool add; mDNSBool dom; OTLink link; } linkedServiceInfo;
+
+// 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.
+#define RR_CACHE_SIZE 1000
+static CacheRecord rrcachestorage[RR_CACHE_SIZE];
+static mDNS mDNSStorage;
+static mDNS_PlatformSupport PlatformSupportStorage;
+static SearcherServices services;
+static DNSQuestion browsequestion, domainquestion;
+
+// PrintServiceInfo prints the service information to standard out
+// A real application might want to do something else with the information
+static void PrintServiceInfo(SearcherServices *services)
+ {
+ OTLink *link = OTReverseList(OTLIFOStealList(&services->serviceinfolist));
+
+ while (link)
+ {
+ linkedServiceInfo *ls = OTGetLinkObject(link, linkedServiceInfo, link);
+ ServiceInfo *s = &ls->i;
+
+ if (!services->headerPrinted)
+ {
+ printf("%-55s Type Domain IP Address Port Info\n", "Name");
+ services->headerPrinted = true;
+ }
+
+ if (ls->dom)
+ {
+ char c_dom[256];
+ ConvertDomainNameToCString(&s->name, c_dom);
+ if (ls->add) printf("%-55s available for browsing\n", c_dom);
+ else printf("%-55s no longer available for browsing\n", c_dom);
+ }
+ else
+ {
+ domainlabel name;
+ domainname type, domain;
+ UInt16 port = (UInt16)((UInt16)s->port.b[0] << 8 | s->port.b[1]);
+ char c_name[64], c_type[256], c_dom[256], c_ip[20];
+
+ DeconstructServiceName(&s->name, &name, &type, &domain);
+ ConvertDomainLabelToCString_unescaped(&name, c_name);
+ ConvertDomainNameToCString(&type, c_type);
+ ConvertDomainNameToCString(&domain, c_dom);
+ sprintf(c_ip, "%d.%d.%d.%d", s->ip.ip.v4.b[0], s->ip.ip.v4.b[1], s->ip.ip.v4.b[2], s->ip.ip.v4.b[3]);
+
+ printf("%-55s %-16s %-14s ", c_name, c_type, c_dom);
+ if (ls->add) printf("%-15s %5d %#s\n", c_ip, port, s->TXTinfo);
+ else printf("Removed\n");
+ }
+
+ link = link->fNext;
+ OTFreeMem(ls);
+ }
+ }
+
+// When the name, address, port, and txtinfo for a service is found, FoundInstanceInfo()
+// enqueues a record for PrintServiceInfo() to print.
+// Note, a browsing application would *not* normally need to get all this information --
+// all it needs is the name, to display to the user.
+// Finding out the address, port, and txtinfo should be deferred to the time that the user
+// actually needs to contact the service to use it.
+static void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query)
+ {
+ SearcherServices *services = (SearcherServices *)query->ServiceInfoQueryContext;
+ linkedServiceInfo *info = (linkedServiceInfo *)(query->info);
+ if (query->info->ip.type == mDNSAddrType_IPv4)
+ {
+ mDNS_StopResolveService(m, query); // For this test code, one answer is sufficient
+ OTLIFOEnqueue(&services->serviceinfolist, &info->link);
+ OTFreeMem(query);
+ }
+ }
+
+// When a new named instance of a service is found, FoundInstance() is called.
+// In this sample code we turn around and immediately issue a query to resolve that service name to
+// find its address, port, and txtinfo, but a normal browing application would just display the name.
+static void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
+ {
+ #pragma unused (question)
+ SearcherServices *services = (SearcherServices *)question->QuestionContext;
+ linkedServiceInfo *info;
+
+ debugf("FoundInstance %##s PTR %##s", answer->name.c, answer->rdata->u.name.c);
+
+ if (answer->rrtype != kDNSType_PTR) return;
+ if (!services) { debugf("FoundInstance: services is NULL"); return; }
+
+ info = (linkedServiceInfo *)OTAllocMem(sizeof(linkedServiceInfo));
+ if (!info) { services->lostRecords = true; return; }
+
+ 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.port = zeroIPPort;
+ info->add = AddRecord;
+ info->dom = mDNSfalse;
+
+ if (!AddRecord) // If TTL == 0 we're deleting a service,
+ OTLIFOEnqueue(&services->serviceinfolist, &info->link);
+ else // else we're adding a new service
+ {
+ ServiceInfoQuery *q = (ServiceInfoQuery *)OTAllocMem(sizeof(ServiceInfoQuery));
+ if (!q) { OTFreeMem(info); services->lostRecords = true; return; }
+ mDNS_StartResolveService(m, q, &info->i, FoundInstanceInfo, services);
+ }
+ }
+
+static void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
+ {
+ #pragma unused (m)
+ #pragma unused (question)
+ SearcherServices *services = (SearcherServices *)question->QuestionContext;
+ linkedServiceInfo *info;
+
+ debugf("FoundDomain %##s PTR %##s", answer->name.c, answer->rdata->u.name.c);
+
+ if (answer->rrtype != kDNSType_PTR) return;
+ if (!services) { debugf("FoundDomain: services is NULL"); return; }
+
+ info = (linkedServiceInfo *)OTAllocMem(sizeof(linkedServiceInfo));
+ if (!info) { services->lostRecords = true; return; }
+
+ 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.port = zeroIPPort;
+ info->add = AddRecord;
+ info->dom = mDNStrue;
+
+ OTLIFOEnqueue(&services->serviceinfolist, &info->link);
+ }
+
+// YieldSomeTime() just cooperatively yields some time to other processes running on classic Mac OS
+static Boolean YieldSomeTime(UInt32 milliseconds)
+ {
+ extern Boolean SIOUXQuitting;
+ EventRecord e;
+ WaitNextEvent(everyEvent, &e, milliseconds / 17, NULL);
+ SIOUXHandleOneEvent(&e);
+ return(SIOUXQuitting);
+ }
+
+int main()
+ {
+ extern void mDNSPlatformIdle(mDNS *const m); // Only needed for debugging version
+ mStatus err;
+ Boolean DoneSetup = false;
+
+ SIOUXSettings.asktosaveonclose = false;
+ SIOUXSettings.userwindowtitle = "\pMulticast DNS Searcher";
+ SIOUXSettings.rows = 40;
+ SIOUXSettings.columns = 132;
+
+ printf("Prototype Multicast DNS Searcher\n\n");
+ printf("WARNING! This is experimental software.\n\n");
+ printf("Multicast DNS is currently an experimental protocol.\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(&mDNSStorage, &PlatformSupportStorage, rrcachestorage, RR_CACHE_SIZE,
+ mDNS_Init_DontAdvertiseLocalAddresses, mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
+ if (err) return(err);
+
+ services.serviceinfolist.fHead = NULL;
+ services.headerPrinted = false;
+ services.lostRecords = false;
+
+ while (!YieldSomeTime(35))
+ {
+ // For debugging, use "#define __ONLYSYSTEMTASK__ 1" and call mDNSPlatformIdle() periodically.
+ // For shipping code, don't define __ONLYSYSTEMTASK__, and you don't need to call mDNSPlatformIdle()
+ mDNSPlatformIdle(&mDNSStorage); // Only needed for debugging version
+ if (mDNSStorage.mDNSPlatformStatus == mStatus_NoError && !DoneSetup)
+ {
+ domainname srvtype, srvdom;
+ DoneSetup = true;
+ 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);
+ if (err) break;
+ err = mDNS_GetDomains(&mDNSStorage, &domainquestion, mDNS_DomainTypeBrowse, mDNSInterface_Any, FoundDomain, &services);
+ if (err) break;
+ }
+
+ if (services.serviceinfolist.fHead)
+ PrintServiceInfo(&services);
+
+ if (services.lostRecords)
+ {
+ services.lostRecords = false;
+ printf("**** Warning: Out of memory: Records have been missed.\n");
+ }
+ }
+
+ mDNS_StopBrowse(&mDNSStorage, &browsequestion);
+ mDNS_Close(&mDNSStorage);
+ return(0);
+ }
--- /dev/null
+This directory contains support files for running mDNS on Mac OS 9
+(and Carbon).
+
+mDNS.mcp is a CodeWarrior 8 project file.
+
+mDNSMacOS9.c and mDNSMacOS9.h are the Platform Support files that go below
+mDNS Core.
+
+"Mac OS Test Responder.c" and "Mac OS Test Searcher.c" build an example
+mDNS Responder and Searcher, respectively.
\ No newline at end of file
--- /dev/null
+/*
+ * 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: mDNSMacOS9.c,v $
+Revision 1.19 2003/08/18 23:09:20 cheshire
+<rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformTimeNow()
+
+Revision 1.18 2003/08/12 19:56:24 cheshire
+Update to APSL 2.0
+
+ */
+
+#include <LowMem.h> // For LMGetCurApName()
+#include <TextUtils.h> // For smSystemScript
+#include <UnicodeConverter.h> // For ConvertFromPStringToUnicode()
+
+#include <stdio.h>
+#include <stdarg.h> // For va_list support
+
+#include "mDNSClientAPI.h" // Defines the interface provided to the client layer above
+#include "mDNSPlatformFunctions.h" // Defines the interface to the supporting layer below
+
+#include "mDNSMacOS9.h" // Defines the specific types needed to run mDNS on this platform
+
+// ***************************************************************************
+// Constants
+
+static const TSetBooleanOption kReusePortOption =
+ { sizeof(TSetBooleanOption), INET_IP, IP_REUSEPORT, 0, true };
+
+// IP_RCVDSTADDR gives error #-3151 (kOTBadOptionErr)
+static const TSetBooleanOption kRcvDestAddrOption =
+ { sizeof(TSetBooleanOption), INET_IP, IP_REUSEPORT, 0, true };
+
+static const TIPAddMulticastOption kAddLinkMulticastOption =
+ { sizeof(TIPAddMulticastOption), INET_IP, IP_ADD_MEMBERSHIP, 0, { 224, 0, 0,251 }, { 0,0,0,0 } };
+
+static const TIPAddMulticastOption kAddAdminMulticastOption =
+ { sizeof(TIPAddMulticastOption), INET_IP, IP_ADD_MEMBERSHIP, 0, { 239,255,255,251 }, { 0,0,0,0 } };
+
+// Bind endpoint to port number. Don't specify any specific IP address --
+// we want to receive unicasts on all interfaces, as well as multicasts.
+typedef struct { OTAddressType fAddressType; mDNSIPPort fPort; mDNSv4Addr fHost; UInt8 fUnused[8]; } mDNSInetAddress;
+//static const mDNSInetAddress mDNSPortInetAddress = { AF_INET, { 0,0 }, { 0,0,0,0 } }; // For testing legacy client support
+#define MulticastDNSPortAsNumber 5353
+static const mDNSInetAddress mDNSPortInetAddress = { AF_INET, { MulticastDNSPortAsNumber >> 8, MulticastDNSPortAsNumber & 0xFF }, { 0,0,0,0 } };
+static const TBind mDNSbindReq = { sizeof(mDNSPortInetAddress), sizeof(mDNSPortInetAddress), (UInt8*)&mDNSPortInetAddress, 0 };
+
+static const TNetbuf zeroTNetbuf = { 0 };
+
+// ***************************************************************************
+// Functions
+
+#if MDNS_DEBUGMSGS
+mDNSexport void debugf_(const char *format, ...)
+ {
+ unsigned char buffer[256];
+ va_list ptr;
+ va_start(ptr,format);
+ buffer[0] = (unsigned char)mDNS_vsnprintf((char*)buffer+1, 255, format, ptr);
+ va_end(ptr);
+#if __ONLYSYSTEMTASK__
+ buffer[1+buffer[0]] = 0;
+ fprintf(stderr, "%s\n", buffer+1);
+ fflush(stderr);
+#else
+ DebugStr(buffer);
+#endif
+ }
+#endif
+
+mDNSexport void LogMsg(const char *format, ...)
+ {
+ unsigned char buffer[256];
+ va_list ptr;
+ va_start(ptr,format);
+ buffer[0] = (unsigned char)mDNS_vsnprintf((char*)buffer+1, 255, format, ptr);
+ va_end(ptr);
+#if __ONLYSYSTEMTASK__
+ buffer[1+buffer[0]] = 0;
+ fprintf(stderr, "%s\n", buffer+1);
+ fflush(stderr);
+#else
+ DebugStr(buffer);
+#endif
+ }
+
+mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end,
+ mDNSInterfaceID InterfaceID, mDNSIPPort srcPort, 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
+ #pragma unused(InterfaceID, srcPort)
+
+ InetAddress InetDest;
+ TUnitData senddata;
+
+ InetDest.fAddressType = AF_INET;
+ InetDest.fPort = dstPort.NotAnInteger;
+ InetDest.fHost = dst->ip.v4.NotAnInteger;
+
+ senddata.addr .maxlen = sizeof(InetDest);
+ senddata.addr .len = sizeof(InetDest);
+ senddata.addr .buf = (UInt8*)&InetDest;
+ senddata.opt = zeroTNetbuf;
+ senddata.udata.maxlen = (UInt32)((UInt8*)end - (UInt8*)msg);
+ senddata.udata.len = (UInt32)((UInt8*)end - (UInt8*)msg);
+ senddata.udata.buf = (UInt8*)msg;
+
+ return(OTSndUData(m->p->ep, &senddata));
+ }
+
+mDNSlocal OSStatus readpacket(mDNS *m)
+ {
+ mDNSAddr senderaddr, destaddr;
+ mDNSInterfaceID interface;
+ mDNSIPPort senderport;
+ InetAddress sender;
+ char options[512];
+ DNSMessage packet;
+ TUnitData recvdata;
+ OTFlags flags = 0;
+ OSStatus err;
+
+ recvdata.addr .maxlen = sizeof(sender);
+ recvdata.addr .len = 0;
+ recvdata.addr .buf = (UInt8*)&sender;
+ recvdata.opt .maxlen = sizeof(options);
+ recvdata.opt .len = 0;
+ recvdata.opt .buf = (UInt8*)&options;
+ recvdata.udata.maxlen = sizeof(packet);
+ recvdata.udata.len = 0;
+ recvdata.udata.buf = (UInt8*)&packet;
+
+ err = OTRcvUData(m->p->ep, &recvdata, &flags);
+ if (err && err != kOTNoDataErr) debugf("OTRcvUData error %d", err);
+
+ if (err) return(err);
+
+ senderaddr.type = mDNSAddrType_IPv4;
+ senderaddr.ip.v4.NotAnInteger = sender.fHost;
+ senderport.NotAnInteger = sender.fPort;
+ destaddr.type = mDNSAddrType_IPv4;
+ destaddr.ip.v4 = AllDNSLinkGroup; // For now, until I work out how to get the dest address, assume it was sent to AllDNSLinkGroup
+ interface = m->HostInterfaces->InterfaceID;
+
+ if (recvdata.opt.len) debugf("readpacket: got some option data at %X, len %d", options, recvdata.opt.len);
+
+ 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);
+
+ return(err);
+ }
+
+
+mDNSlocal void mDNSOptionManagement(mDNS *const m)
+ {
+ OSStatus err;
+
+ // Make sure the length in the TNetbuf agrees with the length in the TOptionHeader
+ m->p->optReq.opt.len = m->p->optBlock.h.len;
+ err = OTOptionManagement(m->p->ep, &m->p->optReq, NULL);
+ if (err) debugf("OTOptionManagement failed %d", err);
+ }
+
+mDNSlocal void mDNSinitComplete(mDNS *const m, mStatus result)
+ {
+ m->mDNSPlatformStatus = result;
+ mDNSCoreInitComplete(m, mStatus_NoError);
+ }
+
+mDNSlocal pascal void mDNSNotifier(void *contextPtr, OTEventCode code, OTResult result, void *cookie)
+ {
+ mDNS *const m = (mDNS *const)contextPtr;
+ if (!m) debugf("mDNSNotifier FATAL ERROR! No context");
+ switch (code)
+ {
+ case T_OPENCOMPLETE:
+ {
+ OSStatus err;
+ InetInterfaceInfo interfaceinfo;
+ if (result) { debugf("T_OPENCOMPLETE failed %d", result); mDNSinitComplete(m, result); return; }
+ //debugf("T_OPENCOMPLETE");
+ m->p->ep = (EndpointRef)cookie;
+ //debugf("OTInetGetInterfaceInfo");
+ // (In future may want to loop over all interfaces instead of just using kDefaultInetInterface)
+ err = OTInetGetInterfaceInfo(&interfaceinfo, kDefaultInetInterface);
+ if (err) { debugf("OTInetGetInterfaceInfo failed %d", err); mDNSinitComplete(m, err); return; }
+
+ // Make our basic standard host resource records (address, PTR, etc.)
+ 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.InterfaceID = (mDNSInterfaceID)&m->p->interface;
+ mDNS_RegisterInterface(m, &m->p->interface);
+ }
+
+ case T_OPTMGMTCOMPLETE:
+ if (result) { debugf("T_OPTMGMTCOMPLETE failed %d", result); mDNSinitComplete(m, result); return; }
+ //debugf("T_OPTMGMTCOMPLETE");
+ switch (++m->p->mOTstate)
+ {
+ case mOT_ReusePort: m->p->optBlock.b = kReusePortOption; mDNSOptionManagement(m); break;
+ case mOT_RcvDestAddr: m->p->optBlock.b = kRcvDestAddrOption; mDNSOptionManagement(m); break;
+ case mOT_LLScope: m->p->optBlock.m = kAddLinkMulticastOption; mDNSOptionManagement(m); break;
+ case mOT_AdminScope: m->p->optBlock.m = kAddAdminMulticastOption; mDNSOptionManagement(m); break;
+ case mOT_Bind: OTBind(m->p->ep, (TBind*)&mDNSbindReq, NULL); break;
+ }
+ break;
+
+ case T_BINDCOMPLETE:
+ if (result) { debugf("T_BINDCOMPLETE failed %d", result); return; }
+ if (m->p->mOTstate != mOT_Bind) { debugf("T_BINDCOMPLETE in wrong mDNS state %d", m->p->mOTstate); return; }
+ m->p->mOTstate++;
+ //debugf("T_BINDCOMPLETE");
+ mDNSinitComplete(m, mStatus_NoError);
+ break;
+
+ case T_DATA:
+ //debugf("T_DATA");
+ while (readpacket(m) == kOTNoError) continue; // Read packets until we run out
+ break;
+
+ case kOTProviderWillClose:
+ case kOTProviderIsClosed: // Machine is going to sleep, shutting down, or reconfiguring IP
+ if (m->p->ep) { OTCloseProvider(m->p->ep); m->p->ep = NULL; }
+ break; // Do we need to do anything?
+
+ default: debugf("mDNSNotifier: Unexpected OTEventCode %X", code);
+ break;
+ }
+ }
+
+#if __ONLYSYSTEMTASK__
+
+static Boolean ONLYSYSTEMTASKevent;
+static void *ONLYSYSTEMTASKcontextPtr;
+static OTEventCode ONLYSYSTEMTASKcode;
+static OTResult ONLYSYSTEMTASKresult;
+static void *ONLYSYSTEMTASKcookie;
+
+mDNSlocal pascal void CallmDNSNotifier(void *contextPtr, OTEventCode code, OTResult result, void *cookie)
+ {
+ ONLYSYSTEMTASKcontextPtr = contextPtr;
+ ONLYSYSTEMTASKcode = code;
+ ONLYSYSTEMTASKresult = result;
+ ONLYSYSTEMTASKcookie = cookie;
+ }
+
+#else
+
+mDNSlocal pascal void CallmDNSNotifier(void *contextPtr, OTEventCode code, OTResult result, void *cookie)
+ {
+ mDNS *const m = (mDNS *const)contextPtr;
+ if (!m) debugf("mDNSNotifier FATAL ERROR! No context");
+
+ // Increment m->p->nesting to indicate to mDNSPlatformLock that there's no need
+ // to call OTEnterNotifier() (because we're already in OTNotifier context)
+ if (m->p->nesting) DebugStr("\pCallmDNSNotifier ERROR! OTEnterNotifier is supposed to suppress notifier callbacks");
+ m->p->nesting++;
+ mDNSNotifier(contextPtr, code, result, cookie);
+ m->p->nesting--;
+ ScheduleNextTimerCallback(m);
+ }
+
+#endif
+
+mDNSlocal OSStatus mDNSOpenEndpoint(const mDNS *const m)
+ {
+ OSStatus err;
+ TEndpointInfo endpointinfo;
+ // m->optReq is pre-set to point to the shared m->optBlock
+ // m->optBlock is filled in by each OTOptionManagement call
+ m->p->optReq.opt.maxlen = sizeof(m->p->optBlock);
+ m->p->optReq.opt.len = sizeof(m->p->optBlock);
+ m->p->optReq.opt.buf = (UInt8*)&m->p->optBlock;
+ m->p->optReq.flags = T_NEGOTIATE;
+
+ // Open an endpoint and start answering queries
+ //printf("Opening endpoint now...\n");
+ m->p->ep = NULL;
+ m->p->mOTstate = mOT_Start;
+// err = OTAsyncOpenEndpoint(OTCreateConfiguration("udp(RxICMP=1)"), 0, &endpointinfo, CallmDNSNotifier, (void*)m); // works
+// err = OTAsyncOpenEndpoint(OTCreateConfiguration("udp(RxICMP)"), 0, &endpointinfo, CallmDNSNotifier, (void*)m); // -3151 bad option
+// err = OTAsyncOpenEndpoint(OTCreateConfiguration("udp,ip(RxICMP=1)"), 0, &endpointinfo, CallmDNSNotifier, (void*)m); // -3151
+// err = OTAsyncOpenEndpoint(OTCreateConfiguration("udp,ip"), 0, &endpointinfo, CallmDNSNotifier, (void*)m); // works
+// err = OTAsyncOpenEndpoint(OTCreateConfiguration("udp,rawip"), 0, &endpointinfo, CallmDNSNotifier, (void*)m); // -3221 invalid arg
+ err = OTAsyncOpenEndpoint(OTCreateConfiguration(kUDPName), 0, &endpointinfo, NewOTNotifyUPP(CallmDNSNotifier), (void*)m);
+ if (err) { debugf("ERROR: OTAsyncOpenEndpoint(UDP) failed with error <%d>", err); return(err); }
+
+ return(kOTNoError);
+ }
+
+// Define these here because they're not in older versions of OpenTransport.h
+enum
+ {
+ xOTStackIsLoading = 0x27000001, /* Sent before Open Transport attempts to load the TCP/IP protocol stack.*/
+ xOTStackWasLoaded = 0x27000002, /* Sent after the TCP/IP stack has been successfully loaded.*/
+ xOTStackIsUnloading = 0x27000003 /* Sent before Open Transport unloads the TCP/IP stack.*/
+ };
+
+static mDNS *ClientNotifierContext;
+
+mDNSlocal pascal void ClientNotifier(void *contextPtr, OTEventCode code, OTResult result, void *cookie)
+ {
+ mDNS *const m = ClientNotifierContext;
+
+ #pragma unused(contextPtr) // Usually zero (except one in the 'xOTStackIsLoading' case)
+ #pragma unused(cookie) // Usually 'ipv4' (except for kOTPortNetworkChange)
+ #pragma unused(result) // Usually zero
+
+ switch (code)
+ {
+ case xOTStackIsLoading: break;
+ case xOTStackWasLoaded: m->mDNSPlatformStatus = mStatus_Waiting; m->p->mOTstate = mOT_Reset; break;
+ case xOTStackIsUnloading: break;
+ case kOTPortNetworkChange: break;
+ default: debugf("ClientNotifier unknown code %X, %X, %d", contextPtr, code, result); break;
+ }
+ }
+
+#if TARGET_API_MAC_CARBON
+
+mDNSlocal void GetUserSpecifiedComputerName(domainlabel *const namelabel)
+ {
+ CFStringRef cfs = CSCopyMachineName();
+ CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8);
+ CFRelease(cfs);
+ }
+
+#else
+
+mDNSlocal OSStatus ConvertStringHandleToUTF8(const StringHandle machineName, UInt8 *const utf8, ByteCount maxlen)
+ {
+ OSStatus status;
+ TextEncoding utf8TextEncoding, SystemTextEncoding;
+ UnicodeMapping theMapping;
+ TextToUnicodeInfo textToUnicodeInfo;
+ ByteCount unicodelen = 0;
+
+ if (maxlen > 255) maxlen = 255; // Can't put more than 255 in a Pascal String
+
+ utf8TextEncoding = CreateTextEncoding(kTextEncodingUnicodeDefault, kTextEncodingDefaultVariant, kUnicodeUTF8Format);
+ UpgradeScriptInfoToTextEncoding(smSystemScript, kTextLanguageDontCare, kTextRegionDontCare, NULL, &SystemTextEncoding);
+ theMapping.unicodeEncoding = utf8TextEncoding;
+ theMapping.otherEncoding = SystemTextEncoding;
+ theMapping.mappingVersion = kUnicodeUseLatestMapping;
+ status = CreateTextToUnicodeInfo(&theMapping, &textToUnicodeInfo);
+ if (status == noErr)
+ {
+ status = ConvertFromPStringToUnicode(textToUnicodeInfo, *machineName, maxlen, &unicodelen, (UniCharArrayPtr)&(utf8[1]));
+ DisposeTextToUnicodeInfo(&textToUnicodeInfo);
+ }
+ utf8[0] = (UInt8)unicodelen;
+ return(status);
+ }
+
+mDNSlocal void GetUserSpecifiedComputerName(domainlabel *const namelabel)
+ {
+ StringHandle machineName = GetString(-16413); // Get machine name set in file sharing
+ if (machineName)
+ {
+ char machineNameState = HGetState((Handle)machineName);
+ HLock((Handle)machineName);
+ ConvertStringHandleToUTF8(machineName, namelabel->c, MAX_DOMAIN_LABEL);
+ HSetState((Handle)machineName, machineNameState);
+ }
+ }
+
+#endif
+
+static pascal void mDNSTimerTask(void *arg)
+ {
+#if __ONLYSYSTEMTASK__
+#pragma unused(arg)
+ ONLYSYSTEMTASKevent = true;
+#else
+ mDNS *const m = (mDNS *const)arg;
+ // Increment m->p->nesting to indicate to mDNSPlatformLock that there's no need
+ // to call OTEnterNotifier() (because we're already in OTNotifier context)
+ if (m->p->nesting) DebugStr("\pmDNSTimerTask ERROR! OTEnterNotifier is supposed to suppress timer callbacks too");
+ m->p->nesting++;
+ mDNS_Execute(m);
+ m->p->nesting--;
+ ScheduleNextTimerCallback(m);
+#endif
+ }
+
+#if TEST_SLEEP
+long sleep, wake, mode;
+#endif
+
+mDNSexport mStatus mDNSPlatformInit(mDNS *const m)
+ {
+ OSStatus err;
+
+ // Set up the nice label
+ m->nicelabel.c[0] = 0;
+ GetUserSpecifiedComputerName(&m->nicelabel);
+// m->nicelabel = *(domainlabel*)"\pStu"; // For conflict testing
+ if (m->nicelabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->nicelabel, "Macintosh");
+
+ // Set up the RFC 1034-compliant label
+ m->hostlabel.c[0] = 0;
+ ConvertUTF8PstringToRFC1034HostLabel(m->nicelabel.c, &m->hostlabel);
+ if (m->hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->hostlabel, "Macintosh");
+
+ mDNS_GenerateFQDN(m);
+
+ ClientNotifierContext = m;
+
+#if !TARGET_API_MAC_CARBON
+ err = OTRegisterAsClient(LMGetCurApName(), NewOTNotifyUPP(ClientNotifier));
+ if (err) debugf("OTRegisterAsClient failed %d", err);
+#endif
+
+ err = mDNSOpenEndpoint(m);
+ if (err) { debugf("mDNSOpenEndpoint failed %d", err); return(err); }
+
+ m->p->OTTimerTask = OTCreateTimerTask(NewOTProcessUPP(mDNSTimerTask), m);
+ m->p->nesting = 0;
+
+#if TEST_SLEEP
+ sleep = TickCount() + 600;
+ wake = TickCount() + 1200;
+ mode = 0;
+#endif
+
+ return(err);
+ }
+
+extern void mDNSPlatformClose (mDNS *const m)
+ {
+ if (m->p->OTTimerTask) { OTDestroyTimerTask(m->p->OTTimerTask); m->p->OTTimerTask = 0; }
+ if (m->p->ep) { OTCloseProvider (m->p->ep); m->p->ep = NULL; }
+ CloseOpenTransport();
+ }
+
+extern void mDNSPlatformIdle(mDNS *const m);
+mDNSexport void mDNSPlatformIdle(mDNS *const m)
+ {
+#if __ONLYSYSTEMTASK__
+ while (ONLYSYSTEMTASKcontextPtr)
+ {
+ void *contextPtr = ONLYSYSTEMTASKcontextPtr;
+ ONLYSYSTEMTASKcontextPtr = NULL;
+ mDNSNotifier(contextPtr, ONLYSYSTEMTASKcode, ONLYSYSTEMTASKresult, ONLYSYSTEMTASKcookie);
+ }
+ if (ONLYSYSTEMTASKevent)
+ {
+ ONLYSYSTEMTASKevent = false;
+ mDNS_Execute(m);
+ }
+#endif
+
+ if (m->p->mOTstate == mOT_Reset)
+ {
+ printf("\n");
+ printf("******************************************************************************\n");
+ printf("\n");
+ printf("Reopening endpoint\n");
+ mDNSOpenEndpoint(m);
+ m->ResourceRecords = NULL;
+ }
+
+#if TEST_SLEEP
+ switch (mode)
+ {
+ case 0: if ((long)TickCount() - sleep >= 0) { mDNSCoreMachineSleep(m, 1); mode++; }
+ break;
+ case 1: if ((long)TickCount() - wake >= 0) { mDNSCoreMachineSleep(m, 0); mode++; }
+ break;
+ }
+#endif
+
+ }
+
+mDNSexport void mDNSPlatformLock(const mDNS *const m)
+ {
+ if (!m) { DebugStr("\pmDNSPlatformLock m NULL!"); return; }
+ if (!m->p) { DebugStr("\pmDNSPlatformLock m->p NULL!"); return; }
+ if (!m->p->ep) { DebugStr("\pmDNSPlatformLock m->p->ep NULL!"); return; }
+
+ // If we try to call OTEnterNotifier and fail because we're already running at
+ // Notifier context, then make sure we don't do the matching OTLeaveNotifier() on exit.
+ if (m->p->nesting || OTEnterNotifier(m->p->ep) == false) m->p->nesting++;
+ }
+
+mDNSlocal void ScheduleNextTimerCallback(const mDNS *const m)
+ {
+ SInt32 interval;
+ interval = m->NextScheduledEvent - mDNSPlatformTimeNow();
+ if (interval < 0) interval = 0;
+ else if (interval > 0x7FFFFFFF / 1000) interval = 0x7FFFFFFF / mDNSPlatformOneSecond;
+ else interval = interval * 1000 / mDNSPlatformOneSecond;
+ //debugf("mDNSPlatformScheduleTask Interval %d", interval);
+ OTScheduleTimerTask(m->p->OTTimerTask, (OTTimeout)interval);
+ }
+
+mDNSexport void mDNSPlatformUnlock(const mDNS *const m)
+ {
+ if (!m) { DebugStr("\pmDNSPlatformUnlock m NULL!"); return; }
+ if (!m->p) { DebugStr("\pmDNSPlatformUnlock m->p NULL!"); return; }
+ if (!m->p->ep) { DebugStr("\pmDNSPlatformUnlock m->p->ep NULL!"); return; }
+ if (m->p->nesting) m->p->nesting--;
+ else
+ {
+ ScheduleNextTimerCallback(m);
+ OTLeaveNotifier(m->p->ep);
+ }
+ }
+
+mDNSexport void mDNSPlatformStrCopy(const void *src, void *dst) { OTStrCopy((char*)dst, (char*)src); }
+mDNSexport UInt32 mDNSPlatformStrLen (const void *src) { return(OTStrLength((char*)src)); }
+mDNSexport void mDNSPlatformMemCopy(const void *src, void *dst, UInt32 len) { OTMemcpy(dst, src, len); }
+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 SInt32 mDNSPlatformOneSecond = 60;
--- /dev/null
+/*
+ * 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: mDNSMacOS9.h,v $
+Revision 1.8 2003/08/12 19:56:24 cheshire
+Update to APSL 2.0
+
+ */
+
+// ***************************************************************************
+// Classic Mac (Open Transport) structures
+
+#include <OpenTransport.h>
+#include <OpenTptInternet.h>
+#include <OpenTptClient.h>
+
+typedef enum
+ {
+ mOT_Reset = 0,
+ mOT_Start,
+ mOT_ReusePort,
+ mOT_RcvDestAddr,
+ mOT_LLScope,
+ mOT_AdminScope,
+ mOT_Bind,
+ mOT_Ready
+ } mOT_State;
+
+typedef struct { TOptionHeader h; mDNSv4Addr multicastGroupAddress; mDNSv4Addr InterfaceAddress; } TIPAddMulticastOption;
+typedef struct { TOptionHeader h; UInt32 flag; } TSetBooleanOption;
+
+// TOptionBlock is a union of various types.
+// What they all have in common is that they all start with a TOptionHeader.
+typedef union { TOptionHeader h; TIPAddMulticastOption m; TSetBooleanOption b; } TOptionBlock;
+
+struct mDNS_PlatformSupport_struct
+ {
+ EndpointRef ep;
+ UInt32 mOTstate; // mOT_State enum
+ TOptionBlock optBlock;
+ TOptMgmt optReq;
+ long OTTimerTask;
+ UInt32 nesting;
+ NetworkInterfaceInfo interface;
+ };
--- /dev/null
+/*
+ * 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: mDNSPrefixCarbon.h,v $
+Revision 1.5 2003/08/12 19:56:24 cheshire
+Update to APSL 2.0
+
+ */
+
+// Global options for the Mac OS Test Responder target.
+// Symbols defined here are available within all source files, like symbols
+// defined globally for a project using "-d SYMBOL=VALUE" in Unix Makefiles
+
+// For normal DeferredTask time execution, set __ONLYSYSTEMTASK__ to 0
+// For easier debugging, set __ONLYSYSTEMTASK__ to 1, and OT Notifier executions
+// will be deferred until SystemTask time
+
+#define TARGET_API_MAC_CARBON 1
+#define OTCARBONAPPLICATION 1
+
+#define __ONLYSYSTEMTASK__ 1
+#define MDNS_DEBUGMSGS 0
--- /dev/null
+/*
+ * 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: mDNSPrefixCarbonDebug.h,v $
+Revision 1.5 2003/08/12 19:56:24 cheshire
+Update to APSL 2.0
+
+ */
+
+// Global options for the Mac OS Test Responder target.
+// Symbols defined here are available within all source files, like symbols
+// defined globally for a project using "-d SYMBOL=VALUE" in Unix Makefiles
+
+// For normal DeferredTask time execution, set __ONLYSYSTEMTASK__ to 0
+// For easier debugging, set __ONLYSYSTEMTASK__ to 1, and OT Notifier executions
+// will be deferred until SystemTask time
+
+#define TARGET_API_MAC_CARBON 1
+#define OTCARBONAPPLICATION 1
+
+#define __ONLYSYSTEMTASK__ 1
+#define MDNS_DEBUGMSGS 1
--- /dev/null
+/*
+ * 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: mDNSPrefixClassic.h,v $
+Revision 1.5 2003/08/12 19:56:24 cheshire
+Update to APSL 2.0
+
+ */
+
+// Global options for the Mac OS Test Responder target.
+// Symbols defined here are available within all source files, like symbols
+// defined globally for a project using "-d SYMBOL=VALUE" in Unix Makefiles
+
+// For normal DeferredTask time execution, set __ONLYSYSTEMTASK__ to 0
+// For easier debugging, set __ONLYSYSTEMTASK__ to 1, and OT Notifier executions
+// will be deferred until SystemTask time
+
+#define __ONLYSYSTEMTASK__ 1
+#define MDNS_DEBUGMSGS 0
--- /dev/null
+/*
+ * 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: BrowserController.h,v $
+Revision 1.7 2003/08/12 19:55:07 cheshire
+Update to APSL 2.0
+
+ */
+
+#import <Cocoa/Cocoa.h>
+#import <DNSServiceDiscovery/DNSServiceDiscovery.h>
+
+#include <netinet/in.h>
+
+@interface BrowserController : NSObject
+{
+ IBOutlet id domainField;
+ IBOutlet id nameField;
+ IBOutlet id typeField;
+
+ IBOutlet id serviceDisplayTable;
+ IBOutlet id typeColumn;
+ IBOutlet id nameColumn;
+ IBOutlet id serviceTypeField;
+ IBOutlet id serviceNameField;
+
+ IBOutlet id ipAddressField;
+ IBOutlet id portField;
+ IBOutlet id textField;
+
+ NSMutableArray *srvtypeKeys;
+ NSMutableArray *srvnameKeys;
+ NSMutableArray *domainKeys;
+ NSMutableArray *nameKeys;
+ NSString *Domain;
+ NSString *SrvType;
+ NSString *SrvName;
+ NSString *Name;
+
+ dns_service_discovery_ref browse_client;
+
+}
+
+- (IBAction)handleDomainClick:(id)sender;
+- (IBAction)handleNameClick:(id)sender;
+- (IBAction)handleTypeClick:(id)sender;
+
+- (IBAction)connect:(id)sender;
+
+- (IBAction)handleTableClick:(id)sender;
+- (IBAction)removeSelected:(id)sender;
+- (IBAction)addNewService:(id)sender;
+
+- (IBAction)update:(NSString *)Type Domain:(NSString *)Domain;
+- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication;
+- (IBAction)loadDomains:(id)sender;
+
+- (void)updateBrowseWithResult:(int)type name:(NSString *)name type:(NSString *)resulttype domain:(NSString *)domain flags:(int)flags;
+- (void)updateEnumWithResult:(int)resultType domain:(NSString *)domain flags:(int)flags;
+- (void)resolveClientWithInterface:(struct sockaddr *)interface address:(struct sockaddr *)address txtRecord:(NSString *)txtRecord;
+
+@end
\ No newline at end of file
--- /dev/null
+/*
+ * 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: BrowserController.m,v $
+Revision 1.18 2003/08/12 19:55:07 cheshire
+Update to APSL 2.0
+
+ */
+
+#import "BrowserController.h"
+
+#include "arpa/inet.h"
+
+void
+MyHandleMachMessage ( CFMachPortRef port, void * msg, CFIndex size, void * info )
+{
+ DNSServiceDiscovery_handleReply(msg);
+}
+
+void browse_reply (
+ DNSServiceBrowserReplyResultType resultType, // One of DNSServiceBrowserReplyResultType
+ const char *replyName,
+ const char *replyType,
+ const char *replyDomain,
+ DNSServiceDiscoveryReplyFlags flags, // DNS Service Discovery reply flags information
+ void *context
+ )
+{
+ [[NSApp delegate] updateBrowseWithResult:resultType name:[NSString stringWithUTF8String:replyName] type:[NSString stringWithUTF8String:replyType] domain:[NSString stringWithUTF8String:replyDomain] flags:flags];
+ return;
+}
+
+void enum_reply (
+ DNSServiceDomainEnumerationReplyResultType resultType,
+ const char *replyDomain,
+ DNSServiceDiscoveryReplyFlags flags,
+ void *context
+ )
+{
+ [[NSApp delegate] updateEnumWithResult:resultType domain:[NSString stringWithUTF8String:replyDomain] flags:flags];
+
+ return;
+}
+
+void resolve_reply (
+ struct sockaddr *interface,
+ struct sockaddr *address,
+ const char *txtRecord,
+ DNSServiceDiscoveryReplyFlags flags,
+ void *context
+ )
+{
+ [[NSApp delegate] resolveClientWithInterface:interface address:address txtRecord:[NSString stringWithUTF8String:txtRecord]];
+
+ return;
+}
+
+@implementation BrowserController //Begin implementation of BrowserController methods
+
+- (void)registerDefaults
+{
+ NSMutableDictionary *regDict = [NSMutableDictionary dictionary];
+
+ NSArray *typeArray = [NSArray arrayWithObjects:@"_ftp._tcp.", @"_tftp._tcp.",
+ @"_ssh._tcp.", @"_telnet._tcp.",
+ @"_http._tcp.",
+ @"_printer._tcp.", @"_ipp._tcp.",
+ @"_ichat._tcp.", @"_eppc._tcp.",
+ @"_afpovertcp._tcp.", @"_afpovertcp._tcp.", @"_MacOSXDupSuppress._tcp.", nil];
+ NSArray *nameArray = [NSArray arrayWithObjects:@"File Transfer (ftp)", @"Trivial File Transfer (tftp)",
+ @"Secure Shell (ssh)", @"Telnet",
+ @"Web Server (http)",
+ @"LPR Printer", @"IPP Printer",
+ @"iChat", @"Remote AppleEvents",
+ @"AppleShare Server", @"SMB File Server", @"Mystery Service", nil];
+
+ [regDict setObject:typeArray forKey:@"SrvTypeKeys"];
+ [regDict setObject:nameArray forKey:@"SrvNameKeys"];
+
+ [[NSUserDefaults standardUserDefaults] registerDefaults:regDict];
+}
+
+
+- (id)init
+{
+ [self registerDefaults];
+
+ browse_client = nil;
+
+ return [super init];
+}
+
+- (void)awakeFromNib //BrowserController startup procedure
+{
+ SrvType=NULL;
+ Domain=NULL;
+ srvtypeKeys = [NSMutableArray array]; //Define arrays for Type, Domain, and Name
+ srvnameKeys = [NSMutableArray array];
+
+ domainKeys = [NSMutableArray array];
+ [domainKeys retain];
+
+ nameKeys = [NSMutableArray array];
+ [nameKeys retain];
+
+ [srvtypeKeys retain]; //Keep arrays in memory until BrowserController closes
+ [srvnameKeys retain]; //Keep arrays in memory until BrowserController closes
+ [typeField setDataSource:self]; //Set application fields' data source to BrowserController
+ [typeField sizeLastColumnToFit]; //and set column sizes to use their whole table's width.
+ [nameField setDataSource:self];
+ [nameField sizeLastColumnToFit];
+ [domainField setDataSource:self];
+ [domainField sizeLastColumnToFit];
+
+ [nameField setDoubleAction:@selector(connect:)];
+
+ //[srvtypeKeys addObject:@"_ftp._tcp."]; //Add supported protocols and domains to their
+ //[srvnameKeys addObject:@"File Transfer (ftp)"];
+ //[srvtypeKeys addObject:@"_printer._tcp."]; //respective arrays
+ //[srvnameKeys addObject:@"Printer (lpr)"];
+ //[srvtypeKeys addObject:@"_http._tcp."]; //respective arrays
+ //[srvnameKeys addObject:@"Web Server (http)"];
+ //[srvtypeKeys addObject:@"_afp._tcp."]; //respective arrays
+ //[srvnameKeys addObject:@"AppleShare Server (afp)"];
+
+ [ipAddressField setStringValue:@""];
+ [portField setStringValue:@""];
+ [textField setStringValue:@""];
+
+ [srvtypeKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvTypeKeys"]];
+ [srvnameKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvNameKeys"]];
+
+
+ [typeField reloadData]; //Reload (redraw) data in fields
+ [domainField reloadData];
+
+ [self loadDomains:self];
+
+}
+
+- (void)dealloc //Deallocation method
+{
+ [srvtypeKeys release];
+ [srvnameKeys release];
+ [nameKeys release];
+ [domainKeys release];
+}
+
+-(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
+{
+ if (theTableView == typeField)
+ {
+ return [srvnameKeys count];
+ }
+ if (theTableView == domainField)
+ {
+ return [domainKeys count];
+ }
+ if (theTableView == nameField)
+ {
+ return [nameKeys count];
+ }
+ if (theTableView == serviceDisplayTable)
+ {
+ return [srvnameKeys count];
+ }
+ return 0;
+}
+
+- (id)tableView:(NSTableView *)theTableView objectValueForTableColumn:(NSTableColumn *)theColumn row:(int)rowIndex
+{
+ if (theTableView == typeField)
+ {
+ return [srvnameKeys objectAtIndex:rowIndex];
+ }
+ if (theTableView == domainField)
+ {
+ return [domainKeys objectAtIndex:rowIndex];
+ }
+ if (theTableView == nameField)
+ {
+ return [[nameKeys sortedArrayUsingSelector:@selector(compare:)] objectAtIndex:rowIndex];
+ }
+ if (theTableView == serviceDisplayTable)
+ {
+ if (theColumn == typeColumn) {
+ return [srvtypeKeys objectAtIndex:rowIndex];
+ }
+ if (theColumn == nameColumn) {
+ return [srvnameKeys objectAtIndex:rowIndex];
+ }
+ return 0;
+ }
+ else
+ return(0);
+} //End of mandatory TableView methods
+
+- (IBAction)handleTypeClick:(id)sender //Handle clicks for Type
+{
+ int index=[sender selectedRow]; //Find index of selected row
+ if (index==-1) return; //Error checking
+ SrvType = [srvtypeKeys objectAtIndex:index]; //Save desired Type
+ SrvName = [srvnameKeys objectAtIndex:index]; //Save desired Type
+
+ [ipAddressField setStringValue:@""];
+ [portField setStringValue:@""];
+ [textField setStringValue:@""];
+
+ [self update:SrvType Domain:Domain]; //If Type and Domain are set, update records
+}
+
+- (IBAction)handleDomainClick:(id)sender //Handle clicks for Domain
+{
+ int index=[sender selectedRow]; //Find index of selected row
+ if (index==-1) return; //Error checking
+ Domain = [domainKeys objectAtIndex:index]; //Save desired Domain
+
+ [ipAddressField setStringValue:@""];
+ [portField setStringValue:@""];
+ [textField setStringValue:@""];
+
+ if (SrvType!=NULL) [self update:SrvType Domain:Domain]; //If Type and Domain are set, update records
+}
+
+- (IBAction)handleNameClick:(id)sender //Handle clicks for Name
+{
+ int index=[sender selectedRow]; //Find index of selected row
+ if (index==-1) return; //Error checking
+ Name=[[nameKeys sortedArrayUsingSelector:@selector(compare:)] objectAtIndex:index]; //Save desired name
+
+ {
+ CFMachPortRef cfMachPort;
+ CFMachPortContext context;
+ Boolean shouldFreeInfo;
+ dns_service_discovery_ref dns_client;
+ mach_port_t port;
+ CFRunLoopSourceRef rls;
+
+ context.version = 1;
+ context.info = 0;
+ context.retain = NULL;
+ context.release = NULL;
+ context.copyDescription = NULL;
+
+ [ipAddressField setStringValue:@"?"];
+ [portField setStringValue:@"?"];
+ [textField setStringValue:@"?"];
+ // start an enumerator on the local server
+ dns_client = DNSServiceResolverResolve
+ (
+ (char *)[Name UTF8String],
+ (char *)[SrvType UTF8String],
+ (char *)(Domain?[Domain UTF8String]:""),
+ resolve_reply,
+ nil
+ );
+
+ port = DNSServiceDiscoveryMachPort(dns_client);
+
+ if (port) {
+ cfMachPort = CFMachPortCreateWithPort ( kCFAllocatorDefault, port, ( CFMachPortCallBack ) MyHandleMachMessage,&context,&shouldFreeInfo );
+
+ /* Create and add a run loop source for the port */
+ rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0);
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+ CFRelease(rls);
+ } else {
+ printf("Could not obtain client port\n");
+ return;
+ }
+ }
+}
+
+- (IBAction)loadDomains:(id)sender
+{
+ CFMachPortRef cfMachPort;
+ CFMachPortContext context;
+ Boolean shouldFreeInfo;
+ dns_service_discovery_ref dns_client;
+ mach_port_t port;
+ CFRunLoopSourceRef rls;
+
+ context.version = 1;
+ context.info = 0;
+ context.retain = NULL;
+ context.release = NULL;
+ context.copyDescription = NULL;
+
+ // start an enumerator on the local server
+ dns_client = DNSServiceDomainEnumerationCreate
+ (
+ 0,
+ enum_reply,
+ nil
+ );
+
+ port = DNSServiceDiscoveryMachPort(dns_client);
+
+ if (port) {
+ cfMachPort = CFMachPortCreateWithPort ( kCFAllocatorDefault, port, ( CFMachPortCallBack ) MyHandleMachMessage,&context,&shouldFreeInfo );
+
+ /* Create and add a run loop source for the port */
+ rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0);
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+ CFRelease(rls);
+ } else {
+ printf("Could not obtain client port\n");
+ return;
+ }
+}
+
+- (IBAction)update:theType Domain:theDomain; //The Big Kahuna: Fetch PTR records and update application
+{
+ const char * DomainC;
+ const char * TypeC=[theType UTF8String]; //Type in C string format
+
+ if (theDomain) {
+ DomainC = [theDomain UTF8String]; //Domain in C string format
+ } else {
+ DomainC = "";
+ }
+
+ [nameKeys removeAllObjects]; //Get rid of displayed records if we're going to go get new ones
+ [nameField reloadData]; //Reload (redraw) names to show the old data is gone
+
+ // get rid of the previous browser if one exists
+ if (browse_client) {
+ DNSServiceDiscoveryDeallocate(browse_client);
+ browse_client = nil;
+ }
+
+ // now create a browser to return the values for the nameField ...
+ {
+ CFMachPortRef cfMachPort;
+ CFMachPortContext context;
+ Boolean shouldFreeInfo;
+ mach_port_t port;
+ CFRunLoopSourceRef rls;
+
+ context.version = 1;
+ context.info = 0;
+ context.retain = NULL;
+ context.release = NULL;
+ context.copyDescription = NULL;
+
+ // start an enumerator on the local server
+ browse_client = DNSServiceBrowserCreate
+ (
+ (char *)TypeC,
+ (char *)DomainC,
+ browse_reply,
+ nil
+ );
+
+ port = DNSServiceDiscoveryMachPort(browse_client);
+
+ if (port) {
+ cfMachPort = CFMachPortCreateWithPort ( kCFAllocatorDefault, port, ( CFMachPortCallBack ) MyHandleMachMessage,&context,&shouldFreeInfo );
+
+ /* Create and add a run loop source for the port */
+ rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0);
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+ CFRelease(rls);
+ } else {
+ printf("Could not obtain client port\n");
+ return;
+ }
+ }
+
+}
+
+
+- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication //Quit when main window is closed
+{
+ return YES;
+}
+
+- (BOOL)windowShouldClose:(NSWindow *)sender //Save domains to our domain file when quitting
+{
+ [domainField reloadData];
+ return YES;
+}
+
+- (void)updateEnumWithResult:(int)resultType domain:(NSString *)domain flags:(int)flags
+{
+ // new domain received
+ if (DNSServiceDomainEnumerationReplyAddDomain == resultType || DNSServiceDomainEnumerationReplyAddDomainDefault == resultType) {
+ // add the domain to the list
+ [domainKeys addObject:domain];
+ } else {
+ // remove the domain from the list
+ NSEnumerator *dmnEnum = [domainKeys objectEnumerator];
+ NSString *aDomain = nil;
+
+ while (aDomain = [dmnEnum nextObject]) {
+ if ([aDomain isEqualToString:domain]) {
+ [domainKeys removeObject:domain];
+ break;
+ }
+ }
+ }
+ // update the domain table
+ [domainField reloadData];
+ return;
+}
+
+
+
+- (void)updateBrowseWithResult:(int)type name:(NSString *)name type:(NSString *)resulttype domain:(NSString *)domain flags:(int)flags
+{
+
+ //NSLog(@"Received result %@ %@ %@ %d", name, resulttype, domain, type);
+
+ if (([domain isEqualToString:Domain] || [domain isEqualToString:@"local."]) && [resulttype isEqualToString:SrvType]) {
+
+ if (type == DNSServiceBrowserReplyRemoveInstance) {
+ if ([nameKeys containsObject:name]) {
+ [nameKeys removeObject:name];
+ }
+ }
+ if (type == DNSServiceBrowserReplyAddInstance) {
+ if (![nameKeys containsObject:name]) {
+ [nameKeys addObject:name];
+ }
+ }
+
+ // If not expecting any more data, then reload (redraw) Name TableView with newly found data
+ if ((flags & kDNSServiceDiscoveryMoreRepliesImmediately) == 0)
+ [nameField reloadData];
+ }
+ return;
+}
+
+- (void)resolveClientWithInterface:(struct sockaddr *)interface address:(struct sockaddr *)address txtRecord:(NSString *)txtRecord
+{
+ if (address->sa_family != AF_INET) return; // For now we only handle IPv4
+ //printf("interface length = %d, port = %d, family = %d, address = %s\n", ((struct sockaddr_in *)interface)->sin_len, ((struct sockaddr_in *)interface)->sin_port, ((struct sockaddr_in *)interface)->sin_family, inet_ntoa(((struct in_addr)((struct sockaddr_in *)interface)->sin_addr)));
+ //printf("address length = %d, port = %d, family = %d, address = %s\n", ((struct sockaddr_in *)address)->sin_len, ((struct sockaddr_in *)address)->sin_port, ((struct sockaddr_in *)address)->sin_family, inet_ntoa(((struct in_addr)((struct sockaddr_in *)address)->sin_addr)));
+ NSString *ipAddr = [NSString stringWithCString:inet_ntoa(((struct in_addr)((struct sockaddr_in *)address)->sin_addr))];
+ int port = ((struct sockaddr_in *)address)->sin_port;
+
+ [ipAddressField setStringValue:ipAddr];
+ [portField setIntValue:port];
+ [textField setStringValue:txtRecord];
+
+ return;
+}
+
+- (void)connect:(id)sender
+{
+ NSString *ipAddr = [ipAddressField stringValue];
+ int port = [portField intValue];
+ NSString *txtRecord = [textField stringValue];
+
+ if (!txtRecord) txtRecord = @"";
+
+ if (!ipAddr || !port) return;
+
+ if ([SrvType isEqualToString:@"_ftp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"ftp://%@:%d/", ipAddr, port]]];
+ else if ([SrvType isEqualToString:@"_tftp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"tftp://%@:%d/", ipAddr, port]]];
+ else if ([SrvType isEqualToString:@"_ssh._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"ssh://%@:%d/", ipAddr, port]]];
+ else if ([SrvType isEqualToString:@"_telnet._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"telnet://%@:%d/", ipAddr, port]]];
+ else if ([SrvType isEqualToString:@"_http._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%d", ipAddr, port]]];
+ else if ([SrvType isEqualToString:@"_printer._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"lpr://%@:%d/", ipAddr, port]]];
+ else if ([SrvType isEqualToString:@"_ipp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"ipp://%@:%d/", ipAddr, port]]];
+ else if ([SrvType isEqualToString:@"_afpovertcp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"afp://%@:%d/", ipAddr, port]]];
+ else if ([SrvType isEqualToString:@"_smb._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"smb://%@:%d/", ipAddr, port]]];
+
+ return;
+}
+
+- (IBAction)handleTableClick:(id)sender
+{
+ //populate the text fields
+}
+
+- (IBAction)removeSelected:(id)sender
+{
+ // remove the selected row and force a refresh
+
+ int selectedRow = [serviceDisplayTable selectedRow];
+
+ if (selectedRow) {
+
+ [srvtypeKeys removeObjectAtIndex:selectedRow];
+ [srvnameKeys removeObjectAtIndex:selectedRow];
+
+ [[NSUserDefaults standardUserDefaults] setObject:srvtypeKeys forKey:@"SrvTypeKeys"];
+ [[NSUserDefaults standardUserDefaults] setObject:srvnameKeys forKey:@"SrvNameKeys"];
+
+ [typeField reloadData];
+ [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]) {
+ [srvtypeKeys addObject:[serviceTypeField stringValue]];
+ [srvnameKeys addObject:[serviceNameField stringValue]];
+
+ [[NSUserDefaults standardUserDefaults] setObject:srvtypeKeys forKey:@"SrvTypeKeys"];
+ [[NSUserDefaults standardUserDefaults] setObject:srvnameKeys forKey:@"SrvNameKeys"];
+
+ [typeField reloadData];
+ [serviceDisplayTable reloadData];
+ }
+
+}
+
+
+
+@end
\ No newline at end of file
--- /dev/null
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 38;
+ objects = {
+ 080E96DCFE201CFB7F000001 = {
+ fileRef = 29B97318FDCFA39411CA2CEA;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 080E96DDFE201D6D7F000001 = {
+ children = (
+ 6515F1C002245DF2000001D2,
+ 6515F1C102245DF2000001D2,
+ );
+ isa = PBXGroup;
+ name = Classes;
+ refType = 4;
+ };
+ 089C165CFE840E0CC02AAC07 = {
+ children = (
+ 089C165DFE840E0CC02AAC07,
+ );
+ isa = PBXVariantGroup;
+ name = InfoPlist.strings;
+ refType = 4;
+ };
+ 089C165DFE840E0CC02AAC07 = {
+ fileEncoding = 10;
+ isa = PBXFileReference;
+ name = English;
+ path = English.lproj/InfoPlist.strings;
+ refType = 4;
+ };
+ 089C165EFE840E0CC02AAC07 = {
+ fileRef = 089C165CFE840E0CC02AAC07;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+//080
+//081
+//082
+//083
+//084
+//100
+//101
+//102
+//103
+//104
+ 1058C7A0FEA54F0111CA2CBB = {
+ children = (
+ 1058C7A1FEA54F0111CA2CBB,
+ );
+ isa = PBXGroup;
+ name = "Linked Frameworks";
+ refType = 4;
+ };
+ 1058C7A1FEA54F0111CA2CBB = {
+ isa = PBXFrameworkReference;
+ name = Cocoa.framework;
+ path = /System/Library/Frameworks/Cocoa.framework;
+ refType = 0;
+ };
+ 1058C7A2FEA54F0111CA2CBB = {
+ children = (
+ 29B97325FDCFA39411CA2CEA,
+ 29B97324FDCFA39411CA2CEA,
+ );
+ isa = PBXGroup;
+ name = "Other Frameworks";
+ refType = 4;
+ };
+ 1058C7A3FEA54F0111CA2CBB = {
+ fileRef = 1058C7A1FEA54F0111CA2CBB;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+//100
+//101
+//102
+//103
+//104
+//170
+//171
+//172
+//173
+//174
+ 17587328FF379C6511CA2CBB = {
+ isa = PBXApplicationReference;
+ path = "DNS Service Browser.app";
+ refType = 3;
+ };
+//170
+//171
+//172
+//173
+//174
+//190
+//191
+//192
+//193
+//194
+ 19C28FACFE9D520D11CA2CBB = {
+ children = (
+ 17587328FF379C6511CA2CBB,
+ );
+ isa = PBXGroup;
+ name = Products;
+ refType = 4;
+ };
+//190
+//191
+//192
+//193
+//194
+//290
+//291
+//292
+//293
+//294
+ 29B97313FDCFA39411CA2CEA = {
+ buildStyles = (
+ 4A9504CCFFE6A4B311CA0CBA,
+ 4A9504CDFFE6A4B311CA0CBA,
+ );
+ isa = PBXProject;
+ mainGroup = 29B97314FDCFA39411CA2CEA;
+ projectDirPath = "";
+ targets = (
+ 29B97326FDCFA39411CA2CEA,
+ );
+ };
+ 29B97314FDCFA39411CA2CEA = {
+ children = (
+ 080E96DDFE201D6D7F000001,
+ 29B97315FDCFA39411CA2CEA,
+ 29B97317FDCFA39411CA2CEA,
+ 29B97323FDCFA39411CA2CEA,
+ 19C28FACFE9D520D11CA2CBB,
+ );
+ isa = PBXGroup;
+ name = "DNS Service Browser";
+ path = "";
+ refType = 4;
+ };
+ 29B97315FDCFA39411CA2CEA = {
+ children = (
+ 29B97316FDCFA39411CA2CEA,
+ );
+ isa = PBXGroup;
+ name = "Other Sources";
+ path = "";
+ refType = 4;
+ };
+ 29B97316FDCFA39411CA2CEA = {
+ isa = PBXFileReference;
+ path = main.m;
+ refType = 4;
+ };
+ 29B97317FDCFA39411CA2CEA = {
+ children = (
+ 29B97318FDCFA39411CA2CEA,
+ 089C165CFE840E0CC02AAC07,
+ 6515F1C5022460A1000001D2,
+ );
+ isa = PBXGroup;
+ name = Resources;
+ path = "";
+ refType = 4;
+ };
+ 29B97318FDCFA39411CA2CEA = {
+ children = (
+ 29B97319FDCFA39411CA2CEA,
+ );
+ isa = PBXVariantGroup;
+ name = MainMenu.nib;
+ path = "";
+ refType = 4;
+ };
+ 29B97319FDCFA39411CA2CEA = {
+ isa = PBXFileReference;
+ name = English;
+ path = English.lproj/MainMenu.nib;
+ refType = 4;
+ };
+ 29B97323FDCFA39411CA2CEA = {
+ children = (
+ 1058C7A0FEA54F0111CA2CBB,
+ 1058C7A2FEA54F0111CA2CBB,
+ );
+ isa = PBXGroup;
+ name = Frameworks;
+ path = "";
+ refType = 4;
+ };
+ 29B97324FDCFA39411CA2CEA = {
+ isa = PBXFrameworkReference;
+ name = AppKit.framework;
+ path = /System/Library/Frameworks/AppKit.framework;
+ refType = 0;
+ };
+ 29B97325FDCFA39411CA2CEA = {
+ isa = PBXFrameworkReference;
+ name = Foundation.framework;
+ path = /System/Library/Frameworks/Foundation.framework;
+ refType = 0;
+ };
+ 29B97326FDCFA39411CA2CEA = {
+ buildPhases = (
+ 29B97327FDCFA39411CA2CEA,
+ 29B97328FDCFA39411CA2CEA,
+ 29B9732BFDCFA39411CA2CEA,
+ 29B9732DFDCFA39411CA2CEA,
+ );
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = "";
+ HEADER_SEARCH_PATHS = "";
+ INSTALL_PATH = "$(HOME)/Applications";
+ LIBRARY_SEARCH_PATHS = "";
+ OTHER_CFLAGS = "";
+ OTHER_LDFLAGS = "";
+ PRODUCT_NAME = "DNS Service Browser";
+ SECTORDER_FLAGS = "";
+ WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas";
+ WRAPPER_EXTENSION = app;
+ };
+ dependencies = (
+ );
+ isa = PBXApplicationTarget;
+ name = "DNS Service Browser";
+ productInstallPath = "$(HOME)/Applications";
+ productName = "DNS Service Browser";
+ productReference = 17587328FF379C6511CA2CBB;
+ productSettingsXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
+<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
+<plist version=\"1.0\">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>DNS Service Browser</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>0.1</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
+";
+ shouldUseHeadermap = 1;
+ };
+ 29B97327FDCFA39411CA2CEA = {
+ buildActionMask = 2147483647;
+ files = (
+ 6515F1C202245DF2000001D2,
+ );
+ isa = PBXHeadersBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 29B97328FDCFA39411CA2CEA = {
+ buildActionMask = 2147483647;
+ files = (
+ 080E96DCFE201CFB7F000001,
+ 089C165EFE840E0CC02AAC07,
+ );
+ isa = PBXResourcesBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 29B9732BFDCFA39411CA2CEA = {
+ buildActionMask = 2147483647;
+ files = (
+ 29B9732CFDCFA39411CA2CEA,
+ 6515F1C302245DF3000001D2,
+ );
+ isa = PBXSourcesBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 29B9732CFDCFA39411CA2CEA = {
+ fileRef = 29B97316FDCFA39411CA2CEA;
+ isa = PBXBuildFile;
+ settings = {
+ ATTRIBUTES = (
+ );
+ };
+ };
+ 29B9732DFDCFA39411CA2CEA = {
+ buildActionMask = 2147483647;
+ files = (
+ 1058C7A3FEA54F0111CA2CBB,
+ );
+ isa = PBXFrameworksBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+//290
+//291
+//292
+//293
+//294
+//4A0
+//4A1
+//4A2
+//4A3
+//4A4
+ 4A9504CCFFE6A4B311CA0CBA = {
+ buildRules = (
+ );
+ buildSettings = {
+ COPY_PHASE_STRIP = NO;
+ OPTIMIZATION_CFLAGS = "-O0";
+ };
+ isa = PBXBuildStyle;
+ name = Development;
+ };
+ 4A9504CDFFE6A4B311CA0CBA = {
+ buildRules = (
+ );
+ buildSettings = {
+ COPY_PHASE_STRIP = YES;
+ };
+ isa = PBXBuildStyle;
+ name = Deployment;
+ };
+//4A0
+//4A1
+//4A2
+//4A3
+//4A4
+//650
+//651
+//652
+//653
+//654
+ 6515F1C002245DF2000001D2 = {
+ isa = PBXFileReference;
+ path = BrowserController.h;
+ refType = 4;
+ };
+ 6515F1C102245DF2000001D2 = {
+ isa = PBXFileReference;
+ path = BrowserController.m;
+ refType = 4;
+ };
+ 6515F1C202245DF2000001D2 = {
+ fileRef = 6515F1C002245DF2000001D2;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 6515F1C302245DF3000001D2 = {
+ fileRef = 6515F1C102245DF2000001D2;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 6515F1C5022460A1000001D2 = {
+ isa = PBXFileReference;
+ name = DNSServiceDiscovery.h;
+ path = /usr/include/DNSServiceDiscovery/DNSServiceDiscovery.h;
+ refType = 0;
+ };
+ };
+ rootObject = 29B97313FDCFA39411CA2CEA;
+}
--- /dev/null
+{
+ IBClasses = (
+ {
+ ACTIONS = {
+ addNewService = id;
+ connect = id;
+ editDomains = id;
+ handleDomainClick = id;
+ handleNameClick = id;
+ handleTableClick = id;
+ handleTypeClick = id;
+ loadDomains = id;
+ removeSelected = id;
+ };
+ CLASS = BrowserController;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ domainEditField = id;
+ domainField = id;
+ domainWindow = id;
+ ipAddressField = id;
+ nameColumn = id;
+ nameField = id;
+ portField = id;
+ serviceDisplayTable = id;
+ serviceNameField = id;
+ serviceTypeField = id;
+ textField = id;
+ typeColumn = id;
+ typeField = id;
+ };
+ SUPERCLASS = NSObject;
+ },
+ {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }
+ );
+ IBVersion = 1;
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IBDocumentLocation</key>
+ <string>14 102 356 240 0 0 1152 746 </string>
+ <key>IBEditorPositions</key>
+ <dict>
+ <key>29</key>
+ <string>22 474 271 44 0 0 1152 746 </string>
+ </dict>
+ <key>IBFramework Version</key>
+ <string>273.0</string>
+ <key>IBOpenObjects</key>
+ <array>
+ <integer>220</integer>
+ <integer>201</integer>
+ </array>
+ <key>IBSystem Version</key>
+ <string>6C35</string>
+</dict>
+</plist>
--- /dev/null
+/*
+ * 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: main.m,v $
+Revision 1.4 2003/08/12 19:55:07 cheshire
+Update to APSL 2.0
+
+ */
+
+#import <Cocoa/Cocoa.h>
+
+int main(int argc, const char *argv[])
+{
+ return NSApplicationMain(argc, argv);
+}
--- /dev/null
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 38;
+ objects = {
+ 080E96DCFE201CFB7F000001 = {
+ fileRef = 29B97318FDCFA39411CA2CEA;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 080E96DDFE201D6D7F000001 = {
+ children = (
+ 654EC28D0226D54A006533C2,
+ 654EC28C0226D54A006533C2,
+ );
+ isa = PBXGroup;
+ name = Classes;
+ refType = 4;
+ };
+ 089C165CFE840E0CC02AAC07 = {
+ children = (
+ 089C165DFE840E0CC02AAC07,
+ );
+ isa = PBXVariantGroup;
+ name = InfoPlist.strings;
+ refType = 4;
+ };
+ 089C165DFE840E0CC02AAC07 = {
+ fileEncoding = 10;
+ isa = PBXFileReference;
+ name = English;
+ path = English.lproj/InfoPlist.strings;
+ refType = 4;
+ };
+ 089C165EFE840E0CC02AAC07 = {
+ fileRef = 089C165CFE840E0CC02AAC07;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+//080
+//081
+//082
+//083
+//084
+//100
+//101
+//102
+//103
+//104
+ 1058C7A0FEA54F0111CA2CBB = {
+ children = (
+ 1058C7A1FEA54F0111CA2CBB,
+ );
+ isa = PBXGroup;
+ name = "Linked Frameworks";
+ refType = 4;
+ };
+ 1058C7A1FEA54F0111CA2CBB = {
+ isa = PBXFrameworkReference;
+ name = Cocoa.framework;
+ path = /System/Library/Frameworks/Cocoa.framework;
+ refType = 0;
+ };
+ 1058C7A2FEA54F0111CA2CBB = {
+ children = (
+ 29B97325FDCFA39411CA2CEA,
+ 29B97324FDCFA39411CA2CEA,
+ 65DD378E028194AE000001D1,
+ );
+ isa = PBXGroup;
+ name = "Other Frameworks";
+ refType = 4;
+ };
+ 1058C7A3FEA54F0111CA2CBB = {
+ fileRef = 1058C7A1FEA54F0111CA2CBB;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+//100
+//101
+//102
+//103
+//104
+//170
+//171
+//172
+//173
+//174
+ 17587328FF379C6511CA2CBB = {
+ isa = PBXApplicationReference;
+ path = "DNS Service Registration.app";
+ refType = 3;
+ };
+//170
+//171
+//172
+//173
+//174
+//190
+//191
+//192
+//193
+//194
+ 19C28FACFE9D520D11CA2CBB = {
+ children = (
+ 17587328FF379C6511CA2CBB,
+ );
+ isa = PBXGroup;
+ name = Products;
+ refType = 4;
+ };
+//190
+//191
+//192
+//193
+//194
+//290
+//291
+//292
+//293
+//294
+ 29B97313FDCFA39411CA2CEA = {
+ buildStyles = (
+ 4A9504CCFFE6A4B311CA0CBA,
+ 4A9504CDFFE6A4B311CA0CBA,
+ );
+ isa = PBXProject;
+ mainGroup = 29B97314FDCFA39411CA2CEA;
+ projectDirPath = "";
+ targets = (
+ 29B97326FDCFA39411CA2CEA,
+ );
+ };
+ 29B97314FDCFA39411CA2CEA = {
+ children = (
+ 080E96DDFE201D6D7F000001,
+ 29B97315FDCFA39411CA2CEA,
+ 29B97317FDCFA39411CA2CEA,
+ 29B97323FDCFA39411CA2CEA,
+ 19C28FACFE9D520D11CA2CBB,
+ );
+ isa = PBXGroup;
+ name = "DNS Service Registration";
+ path = "";
+ refType = 4;
+ };
+ 29B97315FDCFA39411CA2CEA = {
+ children = (
+ 29B97316FDCFA39411CA2CEA,
+ );
+ isa = PBXGroup;
+ name = "Other Sources";
+ path = "";
+ refType = 4;
+ };
+ 29B97316FDCFA39411CA2CEA = {
+ isa = PBXFileReference;
+ path = main.m;
+ refType = 4;
+ };
+ 29B97317FDCFA39411CA2CEA = {
+ children = (
+ 29B97318FDCFA39411CA2CEA,
+ 089C165CFE840E0CC02AAC07,
+ );
+ isa = PBXGroup;
+ name = Resources;
+ path = "";
+ refType = 4;
+ };
+ 29B97318FDCFA39411CA2CEA = {
+ children = (
+ 29B97319FDCFA39411CA2CEA,
+ );
+ isa = PBXVariantGroup;
+ name = MainMenu.nib;
+ path = "";
+ refType = 4;
+ };
+ 29B97319FDCFA39411CA2CEA = {
+ isa = PBXFileReference;
+ name = English;
+ path = English.lproj/MainMenu.nib;
+ refType = 4;
+ };
+ 29B97323FDCFA39411CA2CEA = {
+ children = (
+ 1058C7A0FEA54F0111CA2CBB,
+ 1058C7A2FEA54F0111CA2CBB,
+ );
+ isa = PBXGroup;
+ name = Frameworks;
+ path = "";
+ refType = 4;
+ };
+ 29B97324FDCFA39411CA2CEA = {
+ isa = PBXFrameworkReference;
+ name = AppKit.framework;
+ path = /System/Library/Frameworks/AppKit.framework;
+ refType = 0;
+ };
+ 29B97325FDCFA39411CA2CEA = {
+ isa = PBXFrameworkReference;
+ name = Foundation.framework;
+ path = /System/Library/Frameworks/Foundation.framework;
+ refType = 0;
+ };
+ 29B97326FDCFA39411CA2CEA = {
+ buildPhases = (
+ 29B97327FDCFA39411CA2CEA,
+ 29B97328FDCFA39411CA2CEA,
+ 29B9732BFDCFA39411CA2CEA,
+ 29B9732DFDCFA39411CA2CEA,
+ );
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = "";
+ HEADER_SEARCH_PATHS = "";
+ INSTALL_PATH = "$(HOME)/Applications";
+ LIBRARY_SEARCH_PATHS = "";
+ OTHER_CFLAGS = "";
+ OTHER_LDFLAGS = "";
+ PRODUCT_NAME = "DNS Service Registration";
+ SECTORDER_FLAGS = "";
+ WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas";
+ WRAPPER_EXTENSION = app;
+ };
+ dependencies = (
+ );
+ isa = PBXApplicationTarget;
+ name = "DNS Service Registration";
+ productInstallPath = "$(HOME)/Applications";
+ productName = "DNS Service Registration";
+ productReference = 17587328FF379C6511CA2CBB;
+ productSettingsXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
+<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
+<plist version=\"1.0\">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>DNS Service Registration</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>0.1</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
+";
+ shouldUseHeadermap = 1;
+ };
+ 29B97327FDCFA39411CA2CEA = {
+ buildActionMask = 2147483647;
+ files = (
+ 654EC28E0226D54A006533C2,
+ );
+ isa = PBXHeadersBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 29B97328FDCFA39411CA2CEA = {
+ buildActionMask = 2147483647;
+ files = (
+ 080E96DCFE201CFB7F000001,
+ 089C165EFE840E0CC02AAC07,
+ );
+ isa = PBXResourcesBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 29B9732BFDCFA39411CA2CEA = {
+ buildActionMask = 2147483647;
+ files = (
+ 29B9732CFDCFA39411CA2CEA,
+ 654EC28F0226D54A006533C2,
+ );
+ isa = PBXSourcesBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 29B9732CFDCFA39411CA2CEA = {
+ fileRef = 29B97316FDCFA39411CA2CEA;
+ isa = PBXBuildFile;
+ settings = {
+ ATTRIBUTES = (
+ );
+ };
+ };
+ 29B9732DFDCFA39411CA2CEA = {
+ buildActionMask = 2147483647;
+ files = (
+ 1058C7A3FEA54F0111CA2CBB,
+ );
+ isa = PBXFrameworksBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+//290
+//291
+//292
+//293
+//294
+//4A0
+//4A1
+//4A2
+//4A3
+//4A4
+ 4A9504CCFFE6A4B311CA0CBA = {
+ buildRules = (
+ );
+ buildSettings = {
+ COPY_PHASE_STRIP = NO;
+ OPTIMIZATION_CFLAGS = "-O0";
+ };
+ isa = PBXBuildStyle;
+ name = Development;
+ };
+ 4A9504CDFFE6A4B311CA0CBA = {
+ buildRules = (
+ );
+ buildSettings = {
+ COPY_PHASE_STRIP = YES;
+ };
+ isa = PBXBuildStyle;
+ name = Deployment;
+ };
+//4A0
+//4A1
+//4A2
+//4A3
+//4A4
+//650
+//651
+//652
+//653
+//654
+ 654EC28C0226D54A006533C2 = {
+ isa = PBXFileReference;
+ path = RegistrationController.m;
+ refType = 4;
+ };
+ 654EC28D0226D54A006533C2 = {
+ isa = PBXFileReference;
+ path = RegistrationController.h;
+ refType = 4;
+ };
+ 654EC28E0226D54A006533C2 = {
+ fileRef = 654EC28D0226D54A006533C2;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 654EC28F0226D54A006533C2 = {
+ fileRef = 654EC28C0226D54A006533C2;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 65DD378E028194AE000001D1 = {
+ isa = PBXFrameworkReference;
+ name = CoreFoundation.framework;
+ path = /System/Library/Frameworks/CoreFoundation.framework;
+ refType = 0;
+ };
+ };
+ rootObject = 29B97313FDCFA39411CA2CEA;
+}
--- /dev/null
+{
+ 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
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IBDocumentLocation</key>
+ <string>32 111 356 240 0 0 1152 746 </string>
+ <key>IBEditorPositions</key>
+ <dict>
+ <key>29</key>
+ <string>103 609 252 44 0 0 1152 746 </string>
+ </dict>
+ <key>IBFramework Version</key>
+ <string>273.0</string>
+ <key>IBOpenObjects</key>
+ <array>
+ <integer>243</integer>
+ <integer>21</integer>
+ </array>
+ <key>IBSystem Version</key>
+ <string>6C30</string>
+</dict>
+</plist>
--- /dev/null
+/*
+ * 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: RegistrationController.h,v $
+Revision 1.5 2003/08/12 19:55:07 cheshire
+Update to APSL 2.0
+
+ */
+
+/* RegistrationController */
+
+#import <Cocoa/Cocoa.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
--- /dev/null
+/*
+ * 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: RegistrationController.m,v $
+Revision 1.13 2003/08/12 19:55:07 cheshire
+Update to APSL 2.0
+
+ */
+
+#import "RegistrationController.h"
+
+#include <DNSServiceDiscovery/DNSServiceDiscovery.h>
+
+void reg_reply (
+ int errorCode,
+ void *context
+ )
+{
+ // registration reply
+ printf("Got a reply from the server with error %d\n", errorCode);
+ return;
+}
+
+void
+MyHandleMachMessage ( CFMachPortRef port, void * msg, CFIndex size, void * info )
+{
+ DNSServiceDiscovery_handleReply(msg);
+}
+
+@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];
+ CFRunLoopSourceRef rls;
+ uint16_t registerPort;
+ CFMachPortRef cfMachPort;
+ CFMachPortContext context;
+ Boolean shouldFreeInfo;
+ dns_service_discovery_ref dns_client;
+ mach_port_t port;
+
+ if (selectedRow < 0) {
+ return;
+ }
+
+ context.version = 1;
+ context.info = 0;
+ context.retain = NULL;
+ context.release = NULL;
+ context.copyDescription = NULL;
+
+ registerPort = [[srvportKeys objectAtIndex:selectedRow] intValue];
+
+ dns_client = DNSServiceRegistrationCreate
+ (
+ [[srvnameKeys objectAtIndex:selectedRow] UTF8String],
+ [[srvtypeKeys objectAtIndex:selectedRow] UTF8String],
+ [[srvdomainKeys objectAtIndex:selectedRow] UTF8String],
+ registerPort,
+ [[srvtextKeys objectAtIndex:selectedRow] UTF8String],
+ reg_reply,
+ nil
+ );
+
+ port = DNSServiceDiscoveryMachPort(dns_client);
+
+ if (port) {
+
+ //printf("port is %d\n", port);
+
+ cfMachPort = CFMachPortCreateWithPort ( kCFAllocatorDefault, port, ( CFMachPortCallBack ) MyHandleMachMessage,&context,&shouldFreeInfo );
+
+ rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0);
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+ CFRelease(rls);
+ [registeredDict setObject:[NSNumber numberWithUnsignedInt:(unsigned int)dns_client] forKey:[srvtypeKeys objectAtIndex:selectedRow]];
+ } else {
+ printf("Could not obtain client port\n");
+ }
+
+}
+
+- (IBAction)unregisterService:(id)sender
+{
+ int selectedRow = [serviceDisplayTable selectedRow];
+ NSString *key = [srvtypeKeys objectAtIndex:selectedRow];
+
+ NSNumber *refPtr = [registeredDict objectForKey:key];
+ dns_service_discovery_ref ref = (dns_service_discovery_ref)[refPtr unsignedIntValue];
+
+ if (ref) {
+ DNSServiceDiscoveryDeallocate(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
--- /dev/null
+/*
+ * 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: main.m,v $
+Revision 1.4 2003/08/12 19:55:07 cheshire
+Update to APSL 2.0
+
+ */
+
+#import <Cocoa/Cocoa.h>
+
+int main(int argc, const char *argv[])
+{
+ return NSApplicationMain(argc, argv);
+}
--- /dev/null
+/*
+ * 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: HAAutomounter.h,v $
+Revision 1.4 2003/08/12 19:55:08 cheshire
+Update to APSL 2.0
+
+ */
+
+#import <Foundation/Foundation.h>
+
+
+@interface HAAutomounter : NSObject {
+
+ NSNetServiceBrowser *browser;
+ NSNetService *resolver;
+}
+
+@end
--- /dev/null
+/*
+ * 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: HAAutomounter.m,v $
+Revision 1.4 2003/08/12 19:55:08 cheshire
+Update to APSL 2.0
+
+ */
+
+#import "HAAutomounter.h"
+
+#import <AppKit/AppKit.h>
+
+#include <sys/types.h>
+#include "arpa/inet.h"
+#include <netinet/in.h>
+
+@implementation HAAutomounter
+
+- (id)init
+{
+ self = [super init];
+
+ browser = [[NSNetServiceBrowser alloc] init];
+
+ [browser setDelegate:self];
+
+ [browser searchForServicesOfType:@"_mountme._tcp." inDomain:@""];
+
+ [browser scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
+
+ return self;
+}
+
+- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didFindService:(NSNetService *)aNetService moreComing:(BOOL)moreComing
+{
+ if (resolver) {
+ [resolver release];
+ }
+
+ resolver = [[NSNetService alloc] initWithDomain:[aNetService domain] type:[aNetService type] name:[aNetService name]];
+ [resolver setDelegate:self];
+ [resolver scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
+
+ [resolver resolve];
+}
+
+
+- (void)netServiceDidResolveAddress:(NSNetService *)sender;
+{
+ if ([[sender addresses] count]) {
+
+ // URL mount the volume
+ NSData *addr = [[sender addresses] objectAtIndex:0];
+ struct sockaddr_in *address = CFDataGetBytePtr((CFDataRef)addr);
+ NSString *ipAddr = [NSString stringWithCString:inet_ntoa(((struct in_addr)((struct sockaddr_in *)address)->sin_addr))];
+ int port = ((struct sockaddr_in *)address)->sin_port;
+ NSArray *txtArray = [[sender protocolSpecificInformation] componentsSeparatedByString:@","];
+
+ if ([txtArray count] == 3) {
+ NSString *user = [txtArray objectAtIndex:0];
+ NSString *password = [txtArray objectAtIndex:1];
+ NSString *share = [txtArray objectAtIndex:2];
+
+ [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"afp://%@:%@@%@:%d/%@", user, password, ipAddr, port, share]]];
+ } else {
+ NSLog(@"incompatible format for txt record, s/b user,password,share");
+ }
+
+ } else {
+ NSLog(@"No address %@", sender);
+ }
+}
+
+- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didRemoveService:(NSNetService *)aNetService moreComing:(BOOL)moreComing
+{
+ // unmount the volume
+}
+
+
+@end
--- /dev/null
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 38;
+ objects = {
+ 014CEA4F0018CE4811CA2923 = {
+ buildRules = (
+ );
+ buildSettings = {
+ COPY_PHASE_STRIP = NO;
+ OPTIMIZATION_CFLAGS = "-O0";
+ };
+ isa = PBXBuildStyle;
+ name = Development;
+ };
+ 014CEA500018CE4811CA2923 = {
+ buildRules = (
+ );
+ buildSettings = {
+ COPY_PHASE_STRIP = YES;
+ };
+ isa = PBXBuildStyle;
+ name = Deployment;
+ };
+//010
+//011
+//012
+//013
+//014
+//030
+//031
+//032
+//033
+//034
+ 034768E6FF38A76511DB9C8B = {
+ isa = PBXExecutableFileReference;
+ path = HAAutomounter;
+ refType = 3;
+ };
+//030
+//031
+//032
+//033
+//034
+//080
+//081
+//082
+//083
+//084
+ 08FB7793FE84155DC02AAC07 = {
+ buildStyles = (
+ 014CEA4F0018CE4811CA2923,
+ 014CEA500018CE4811CA2923,
+ );
+ isa = PBXProject;
+ mainGroup = 08FB7794FE84155DC02AAC07;
+ projectDirPath = "";
+ targets = (
+ 08FB779FFE84155DC02AAC07,
+ );
+ };
+ 08FB7794FE84155DC02AAC07 = {
+ children = (
+ 08FB7795FE84155DC02AAC07,
+ 08FB779DFE84155DC02AAC07,
+ 1AB674ADFE9D54B511CA2CBB,
+ );
+ isa = PBXGroup;
+ name = HAAutomounter;
+ refType = 4;
+ };
+ 08FB7795FE84155DC02AAC07 = {
+ children = (
+ 65CA1A4902808474000001D1,
+ 65CA1A4802808474000001D1,
+ 08FB7796FE84155DC02AAC07,
+ );
+ isa = PBXGroup;
+ name = Source;
+ refType = 4;
+ };
+ 08FB7796FE84155DC02AAC07 = {
+ isa = PBXFileReference;
+ path = main.m;
+ refType = 4;
+ };
+ 08FB779DFE84155DC02AAC07 = {
+ children = (
+ 08FB779EFE84155DC02AAC07,
+ 65CA1A4F0280888E000001D1,
+ 65CA1DCD028088B2000001D1,
+ 65CA1E9D02809D68000001D1,
+ 65CA1EB302809DA3000001D1,
+ 6547B9AF0282024900CE36C6,
+ );
+ isa = PBXGroup;
+ name = "External Frameworks and Libraries";
+ refType = 4;
+ };
+ 08FB779EFE84155DC02AAC07 = {
+ isa = PBXFrameworkReference;
+ name = Foundation.framework;
+ path = /System/Library/Frameworks/Foundation.framework;
+ refType = 0;
+ };
+ 08FB779FFE84155DC02AAC07 = {
+ buildPhases = (
+ 08FB77A0FE84155DC02AAC07,
+ 08FB77A1FE84155DC02AAC07,
+ 08FB77A3FE84155DC02AAC07,
+ 08FB77A5FE84155DC02AAC07,
+ );
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = "\"$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks\"";
+ HEADER_SEARCH_PATHS = "";
+ INSTALL_PATH = "$(HOME)/bin";
+ LIBRARY_SEARCH_PATHS = "";
+ OTHER_CFLAGS = "";
+ OTHER_LDFLAGS = "";
+ OTHER_REZFLAGS = "";
+ PRODUCT_NAME = HAAutomounter;
+ REZ_EXECUTABLE = YES;
+ SECTORDER_FLAGS = "";
+ WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas";
+ };
+ dependencies = (
+ );
+ isa = PBXToolTarget;
+ name = HAAutomounter;
+ productInstallPath = "$(HOME)/bin";
+ productName = HAAutomounter;
+ productReference = 034768E6FF38A76511DB9C8B;
+ shouldUseHeadermap = 1;
+ };
+ 08FB77A0FE84155DC02AAC07 = {
+ buildActionMask = 2147483647;
+ files = (
+ 65CA1A4A02808474000001D1,
+ );
+ isa = PBXHeadersBuildPhase;
+ };
+ 08FB77A1FE84155DC02AAC07 = {
+ buildActionMask = 2147483647;
+ files = (
+ 08FB77A2FE84155DC02AAC07,
+ 65CA1A4B02808474000001D1,
+ );
+ isa = PBXSourcesBuildPhase;
+ };
+ 08FB77A2FE84155DC02AAC07 = {
+ fileRef = 08FB7796FE84155DC02AAC07;
+ isa = PBXBuildFile;
+ settings = {
+ ATTRIBUTES = (
+ );
+ };
+ };
+ 08FB77A3FE84155DC02AAC07 = {
+ buildActionMask = 2147483647;
+ files = (
+ 08FB77A4FE84155DC02AAC07,
+ 65CA1DCC02808890000001D1,
+ 65CA1E9C028088B3000001D1,
+ 65CA1EB202809D68000001D1,
+ 65CA1EBC02809DA3000001D1,
+ 6547BBFD0282024900CE36C6,
+ );
+ isa = PBXFrameworksBuildPhase;
+ };
+ 08FB77A4FE84155DC02AAC07 = {
+ fileRef = 08FB779EFE84155DC02AAC07;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 08FB77A5FE84155DC02AAC07 = {
+ buildActionMask = 2147483647;
+ files = (
+ );
+ isa = PBXRezBuildPhase;
+ };
+//080
+//081
+//082
+//083
+//084
+//1A0
+//1A1
+//1A2
+//1A3
+//1A4
+ 1AB674ADFE9D54B511CA2CBB = {
+ children = (
+ 034768E6FF38A76511DB9C8B,
+ );
+ isa = PBXGroup;
+ name = Products;
+ refType = 4;
+ };
+//1A0
+//1A1
+//1A2
+//1A3
+//1A4
+//650
+//651
+//652
+//653
+//654
+ 6547B9AF0282024900CE36C6 = {
+ isa = PBXFrameworkReference;
+ name = AppKit.framework;
+ path = /System/Library/Frameworks/AppKit.framework;
+ refType = 0;
+ };
+ 6547BBFD0282024900CE36C6 = {
+ fileRef = 6547B9AF0282024900CE36C6;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 65CA1A4802808474000001D1 = {
+ isa = PBXFileReference;
+ path = HAAutomounter.h;
+ refType = 4;
+ };
+ 65CA1A4902808474000001D1 = {
+ isa = PBXFileReference;
+ path = HAAutomounter.m;
+ refType = 4;
+ };
+ 65CA1A4A02808474000001D1 = {
+ fileRef = 65CA1A4802808474000001D1;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 65CA1A4B02808474000001D1 = {
+ fileRef = 65CA1A4902808474000001D1;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 65CA1A4F0280888E000001D1 = {
+ isa = PBXFrameworkReference;
+ name = CoreFoundation.framework;
+ path = /System/Library/Frameworks/CoreFoundation.framework;
+ refType = 0;
+ };
+ 65CA1DCC02808890000001D1 = {
+ fileRef = 65CA1A4F0280888E000001D1;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 65CA1DCD028088B2000001D1 = {
+ isa = PBXFrameworkReference;
+ name = CoreServices.framework;
+ path = /System/Library/Frameworks/CoreServices.framework;
+ refType = 0;
+ };
+ 65CA1E9C028088B3000001D1 = {
+ fileRef = 65CA1DCD028088B2000001D1;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 65CA1E9D02809D68000001D1 = {
+ isa = PBXFrameworkReference;
+ name = AppleShareClientCore.framework;
+ path = /System/Library/Frameworks/AppleShareClientCore.framework;
+ refType = 0;
+ };
+ 65CA1EB202809D68000001D1 = {
+ fileRef = 65CA1E9D02809D68000001D1;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 65CA1EB302809DA3000001D1 = {
+ isa = PBXFrameworkReference;
+ name = URLMount.framework;
+ path = /System/Library/PrivateFrameworks/URLMount.framework;
+ refType = 0;
+ };
+ 65CA1EBC02809DA3000001D1 = {
+ fileRef = 65CA1EB302809DA3000001D1;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ };
+ rootObject = 08FB7793FE84155DC02AAC07;
+}
--- /dev/null
+/*
+ * 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: main.m,v $
+Revision 1.4 2003/08/12 19:55:08 cheshire
+Update to APSL 2.0
+
+ */
+
+#import <Foundation/Foundation.h>
+
+#import "HAAutomounter.h"
+
+int main (int argc, const char * argv[]) {
+ NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+
+ [[HAAutomounter alloc] init];
+
+ [[NSRunLoop currentRunLoop] run];
+
+ [pool release];
+ return 0;
+}
--- /dev/null
+/*
+ * 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: CFSocket.c,v $
+Revision 1.115 2003/09/10 00:45:55 cheshire
+<rdar://problem/3412328> Don't log "sendto failed" errors during the first two minutes of startup
+
+Revision 1.114 2003/08/27 02:55:13 cheshire
+<rdar://problem/3387910>: Bug: Don't report mDNSPlatformSendUDP sendto errno 64 (Host is down)
+
+Revision 1.113 2003/08/19 22:20:00 cheshire
+<rdar://problem/3376721> 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
+<rdar://problem/3376721> Don't use IPv6 on interfaces that have a routable IPv4 address configured
+
+Revision 1.111 2003/08/18 22:53:37 cheshire
+<rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformTimeNow()
+
+Revision 1.110 2003/08/16 03:39:00 cheshire
+<rdar://problem/3338440> InterfaceID -1 indicates "local only"
+
+Revision 1.109 2003/08/15 02:19:49 cheshire
+<rdar://problem/3375225> 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
+<rdar://problem/3375225> 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
+<rdar://problem/3370229> 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
+<rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug
+
+Revision 1.102 2003/08/06 00:14:52 cheshire
+<rdar://problem/3330324> 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
+<rdar://problem/3330324> Need to check IP TTL on responses
+
+Revision 1.100 2003/08/05 21:18:50 cheshire
+<rdar://problem/3363185> 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
+<rdar://problem/3294080> 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
+Bug #: 3320722
+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
+<rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead
+
+Revision 1.95 2003/07/12 03:15:20 cheshire
+<rdar://problem/3324848> 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
+<rdar://problem/3287213> When select() and recvmgs() disagree, get more info from kernel about the socket state
+
+Revision 1.93 2003/07/03 00:09:14 cheshire
+<rdar://problem/3286004> 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
+<rdar://problem/3313413> 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
+<rdar://problem/3303118> 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
+<rdar://problem/3296061> 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
+<rdar://problem/3291162> 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
+<rdar://problem/3286004> New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call
+
+Revision 1.86 2003/05/28 02:41:52 cheshire
+<rdar://problem/3034346> 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
+<rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
+
+Revision 1.81 2003/05/24 02:06:42 cheshire
+<rdar://problem/3268480> 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
+<rdar://problem/3221880> if_indextoname consumes a lot of CPU
+Fix error in myIfIndexToName; was returning prematurely
+
+Revision 1.79 2003/05/23 23:07:44 cheshire
+<rdar://problem/3268199> Must not write to stderr when running as daemon
+
+Revision 1.78 2003/05/23 01:19:04 cheshire
+<rdar://problem/3267085> 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
+<rdar://problem/3264366> 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
+Bug #: <rdar://problem/3191277>: mDNSResponder doesn't watch for IPv6 address changes
+
+Revision 1.72 2003/05/14 18:48:41 cheshire
+<rdar://problem/3159272> 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
+<rdar://problem/3159272> 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
+<rdar://problem/3248914> 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
+<rdar://problem/3241281> 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
+Bug #: 3228833
+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
+Bug #: 3221880
+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
+<rdar://problem/3210018> 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
+Bug #: 3194246 mDNSResponder socket problems
+
+Revision 1.58 2003/03/06 01:43:04 cheshire
+Bug #: 3189097 Additional debugging code in mDNSResponder
+Improve "LIST_ALL_INTERFACES" output
+
+Revision 1.57 2003/03/05 22:36:27 cheshire
+Bug #: 3186338 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
+Bug #: 3189097 Additional debugging code in mDNSResponder
+
+Revision 1.55 2003/02/21 01:54:09 cheshire
+Bug #: 3099194 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
+Bug #: 3169535 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:
+3086540 computer name changes not handled properly
+3124348 service name changes are not properly handled
+3124352 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
+Bug #: 3108426 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
+// ***************************************************************************
+
+// Open Transport 2.7.x on Mac OS 9 used to send Multicast DNS queries to UDP port 53,
+// before the Multicast DNS port was changed to 5353. For this reason, the mDNSResponder
+// in earlier versions of Mac OS X 10.2 Jaguar used to set mDNS_AllowPort53 to 1 to allow
+// it to also listen and answer queries on UDP port 53. Now that Transport 2.8 (included in
+// the Classic subsystem of Mac OS X 10.2 Jaguar) has been corrected to issue Multicast DNS
+// queries on UDP port 5353, this backwards-compatibility legacy support is no longer needed.
+#define mDNS_AllowPort53 0
+
+// 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 "mDNSPlatformFunctions.h" // Defines the interface to the supporting layer below
+#include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform
+
+#include <stdio.h>
+#include <unistd.h> // For select() and close()
+#include <stdarg.h> // For va_list support
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include <netinet/in.h> // For IP_RECVTTL
+#ifndef IP_RECVTTL
+#define IP_RECVTTL 24 /* bool; receive reception TTL w/dgram */
+#endif
+
+#include <netinet/in_systm.h> // For n_long, required by <netinet/ip.h> below
+#include <netinet/ip.h> // For IPTOS_LOWDELAY etc.
+#include <netinet6/in6_var.h> // For IN6_IFF_NOTREADY etc.
+
+// 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 <ifaddrs.h>
+#endif
+
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOMessage.h>
+#include <mach/mach_time.h>
+
+// ***************************************************************************
+// Globals
+
+static mDNSu32 clockdivisor = 0;
+
+// ***************************************************************************
+// 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
+
+// Note, this uses mDNS_vsnprintf instead of standard "vsnprintf", because mDNS_vsnprintf knows
+// how to print special data types like IP addresses and length-prefixed domain names
+#if MDNS_DEBUGMSGS
+mDNSexport void debugf_(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);
+ fprintf(stderr,"%s\n", buffer);
+ fflush(stderr);
+ }
+#endif
+
+#if MDNS_DEBUGMSGS > 1
+mDNSexport void verbosedebugf_(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);
+ fprintf(stderr,"%s\n", buffer);
+ fflush(stderr);
+ }
+#endif
+
+mDNSexport void LogMsg(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);
+
+ extern int debug_mode;
+ if (debug_mode) // In debug_mode we write to stderr
+ {
+ fprintf(stderr,"%s\n", buffer);
+ fflush(stderr);
+ }
+ else // else, in production mode, we write to syslog
+ {
+ openlog("mDNSResponder", LOG_CONS | LOG_PERROR | LOG_PID, LOG_DAEMON);
+ syslog(LOG_ERR, "%s", buffer);
+ closelog();
+ }
+ }
+
+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((mDNSInterfaceID)~0);
+ if (index)
+ for (i = m->p->InterfaceList; i; i = i->next)
+ if (i->scope_id == index)
+ return(i->ifinfo.InterfaceID);
+ return(mDNSNULL);
+ }
+
+mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(const mDNS *const m, mDNSInterfaceID id)
+ {
+ NetworkInterfaceInfoOSX *i;
+ if (id == (mDNSInterfaceID)~0) return((mDNSu32)~0);
+ if (id)
+ for (i = m->p->InterfaceList; i; i = i->next)
+ if (i->ifinfo.InterfaceID == id)
+ return i->scope_id;
+ return 0;
+ }
+
+mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end,
+ mDNSInterfaceID InterfaceID, mDNSIPPort srcPort, const mDNSAddr *dst, mDNSIPPort dstPort)
+ {
+ #pragma unused(m)
+ NetworkInterfaceInfoOSX *info = (NetworkInterfaceInfoOSX *)InterfaceID;
+ struct sockaddr_storage to;
+ int s, err;
+
+ if (!InterfaceID) { LogMsg("mDNSPlatformSendUDP ERROR! Cannot send from zero InterfaceID"); return mStatus_BadParamErr; }
+
+ 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;
+ }
+ 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->scope_id;
+ }
+ else
+ {
+ LogMsg("mDNSPlatformSendUDP: dst is not an IPv4 or IPv6 address!\n");
+ return mStatus_BadParamErr;
+ }
+
+ if (srcPort.NotAnInteger == MulticastDNSPort.NotAnInteger)
+ {
+ if (dst->type == mDNSAddrType_IPv4) s = info->sktv4;
+ else if (dst->type == mDNSAddrType_IPv6) s = info->sktv6;
+ else s = -1;
+ }
+#if mDNS_AllowPort53
+ else if (srcPort.NotAnInteger == UnicastDNSPort.NotAnInteger && dst->type == mDNSAddrType_IPv4)
+ s = info->skt53;
+#endif
+ else { LogMsg("Source port %d not allowed", (mDNSu16)srcPort.b[0]<<8 | srcPort.b[1]); return(-1); }
+
+ if (s >= 0)
+ verbosedebugf("mDNSPlatformSendUDP: sending on InterfaceID %X %s/%d to %#a:%d skt %d",
+ InterfaceID, info->ifa_name, dst->type, dst, (mDNSu16)dstPort.b[0]<<8 | dstPort.b[1], s);
+ else
+ verbosedebugf("mDNSPlatformSendUDP: NOT sending on InterfaceID %X %s/%d (socket of this type not available)",
+ InterfaceID, info->ifa_name, dst->type, dst, (mDNSu16)dstPort.b[0]<<8 | dstPort.b[1]);
+
+ // 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 two minutes after boot
+ // This is because mDNSResponder intentionally starts up early in the boot process (See <rdar://problem/3409090>)
+ // 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 * 120)) return(err);
+ LogMsg("mDNSPlatformSendUDP sendto failed to send packet on InterfaceID %p %s/%ld to %#a:%d skt %d error %d errno %d (%s)",
+ InterfaceID, info->ifa_name, dst->type, dst, (mDNSu16)dstPort.b[0]<<8 | dstPort.b[1], s, err, errno, strerror(errno));
+ 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);
+ }
+
+mDNSlocal void myCFSocketCallBack(CFSocketRef cfs, CFSocketCallBackType CallBackType, CFDataRef address, const void *data, void *context)
+ {
+ mDNSAddr senderAddr, destAddr;
+ mDNSIPPort senderPort, destPort = MulticastDNSPort;
+ NetworkInterfaceInfoOSX *info = (NetworkInterfaceInfoOSX *)context;
+ mDNS *const m = info->m;
+ 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 mDNS_AllowPort53
+ if (cfs == info->cfs53) { s1 = info->skt53; destPort = UnicastDNSPort; }
+ else
+#endif
+ if (cfs == info->cfsv4) s1 = info->sktv4;
+ else if (cfs == info->cfsv6) s1 = info->sktv6;
+
+ if (s1 < 0 || s1 != skt)
+ {
+ LogMsg("myCFSocketCallBack: s1 %d native socket %d, cfs %p", s1, skt, cfs);
+#if mDNS_AllowPort53
+ LogMsg("myCFSocketCallBack: cfs53 %p, skt53 %d", info->cfs53, info->skt53);
+#endif
+ LogMsg("myCFSocketCallBack: cfsv4 %p, sktv4 %d", info->cfsv4, info->sktv4);
+ LogMsg("myCFSocketCallBack: cfsv6 %p, sktv6 %d", info->cfsv6, info->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 (strcmp(info->ifa_name, packetifname))
+ {
+ verbosedebugf("myCFSocketCallBack got a packet from %#a to %#a on interface %#a/%s (Ignored -- really arrived on interface %s)",
+ &senderAddr, &destAddr, &info->ifinfo.ip, info->ifa_name, packetifname);
+ return;
+ }
+ else
+ verbosedebugf("myCFSocketCallBack got a packet from %#a to %#a on interface %#a/%s",
+ &senderAddr, &destAddr, &info->ifinfo.ip, info->ifa_name);
+
+ 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, info->ifinfo.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
+ }
+ }
+
+// 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);
+ }
+ }
+
+mDNSlocal mStatus SetupSocket(NetworkInterfaceInfoOSX *i, mDNSIPPort port, int *s, CFSocketRef *c)
+ {
+ const int on = 1;
+ const int twofivefive = 255;
+
+ 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(i->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
+ mStatus err = setsockopt(skt, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on));
+ if (err < 0) { LogMsg("setsockopt - SO_REUSEPORT error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); }
+
+ if (i->sa_family == AF_INET)
+ {
+ // We want to receive destination addresses
+ err = setsockopt(skt, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on));
+ if (err < 0) { LogMsg("setsockopt - IP_RECVDSTADDR error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); }
+
+ // We want to receive interface identifiers
+ err = setsockopt(skt, IPPROTO_IP, IP_RECVIF, &on, sizeof(on));
+ if (err < 0) { LogMsg("setsockopt - IP_RECVIF error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); }
+
+ // 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
+ struct in_addr addr = { i->ifinfo.ip.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) { LogMsg("setsockopt - IP_ADD_MEMBERSHIP error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); }
+
+ // Specify outgoing interface too
+ err = setsockopt(skt, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr));
+ if (err < 0) { LogMsg("setsockopt - IP_MULTICAST_IF error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); }
+
+ // Send unicast packets with TTL 255
+ err = setsockopt(skt, IPPROTO_IP, IP_TTL, &twofivefive, sizeof(twofivefive));
+ if (err < 0) { LogMsg("setsockopt - IP_TTL error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); }
+
+ // And multicast packets with TTL 255 too
+ err = setsockopt(skt, IPPROTO_IP, IP_MULTICAST_TTL, &twofivefive, sizeof(twofivefive));
+ if (err < 0) { LogMsg("setsockopt - IP_MULTICAST_TTL error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); }
+
+ // 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) { LogMsg("setsockopt - IP_TOS error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); }
+
+ // 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)
+ {
+ // If we fail to bind to port 53 (because we're not root), that's okay, just tidy up and silently continue
+ if (port.NotAnInteger == UnicastDNSPort.NotAnInteger) { close(skt); err = 0; }
+ else LogMsg("bind error %ld errno %d (%s)", err, errno, strerror(errno));
+ return(err);
+ }
+ }
+ else if (i->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) { LogMsg("setsockopt - IPV6_PKTINFO error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); }
+
+ // 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) { LogMsg("setsockopt - IPV6_HOPLIMIT error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); }
+
+ // 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) { LogMsg("setsockopt - IPV6_V6ONLY error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); }
+
+ // Add multicast group membership on this interface
+ int interface_id = if_nametoindex(i->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) { LogMsg("setsockopt - IPV6_JOIN_GROUP error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); }
+
+ // Specify outgoing interface too
+ err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_IF, &interface_id, sizeof(interface_id));
+ if (err < 0) { LogMsg("setsockopt - IPV6_MULTICAST_IF error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); }
+
+ // Send unicast packets with TTL 255
+ err = setsockopt(skt, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &twofivefive, sizeof(twofivefive));
+ if (err < 0) { LogMsg("setsockopt - IPV6_UNICAST_HOPS error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); }
+
+ // And multicast packets with TTL 255 too
+ err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &twofivefive, sizeof(twofivefive));
+ if (err < 0) { LogMsg("setsockopt - IPV6_MULTICAST_HOPS error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); }
+
+ // 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) { LogMsg("setsockopt - IPV6_TCLASS error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); }
+ #endif
+
+ // Want to receive our own packets
+ err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(on));
+ if (err < 0) { LogMsg("setsockopt - IPV6_MULTICAST_LOOP error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); }
+
+ // 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) { LogMsg("bind error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); }
+ }
+
+ fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK); // set non-blocking
+ *s = skt;
+ CFSocketContext myCFSocketContext = { 0, i->ifinfo.InterfaceID, NULL, NULL, NULL };
+ *c = CFSocketCreateWithNative(kCFAllocatorDefault, *s, kCFSocketReadCallBack, myCFSocketCallBack, &myCFSocketContext);
+ CFRunLoopSourceRef rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, *c, 0);
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+ CFRelease(rls);
+
+ 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 mStatus 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)->CurrentlyActive = mDNStrue;
+ return(0);
+ }
+
+ debugf("AddInterfaceToList: Making new interface %u with address %#a", scope_id, &ip);
+ NetworkInterfaceInfoOSX *i = (NetworkInterfaceInfoOSX *)mallocL("NetworkInterfaceInfoOSX", sizeof(*i));
+ if (!i) return(-1);
+ i->ifa_name = (char *)mallocL("NetworkInterfaceInfoOSX name", strlen(ifa->ifa_name) + 1);
+ if (!i->ifa_name) { freeL("NetworkInterfaceInfoOSX", i); return(-1); }
+ strcpy(i->ifa_name, ifa->ifa_name);
+
+ i->ifinfo.InterfaceID = mDNSNULL;
+ i->ifinfo.ip = ip;
+ i->ifinfo.Advertise = m->AdvertiseLocalAddresses;
+ i->ifinfo.TxAndRx = mDNSfalse; // For now; will be set up later at the end of UpdateInterfaceList
+
+ i->next = mDNSNULL;
+ i->m = m;
+ i->scope_id = scope_id;
+ i->CurrentlyActive = mDNStrue;
+ i->sa_family = ifa->ifa_addr->sa_family;
+ #if mDNS_AllowPort53
+ i->skt53 = -1;
+ i->cfs53 = NULL;
+ #endif
+ i->sktv4 = -1;
+ i->cfsv4 = NULL;
+ i->sktv6 = -1;
+ i->cfsv6 = NULL;
+
+ if (!i->ifa_name) return(-1);
+ *p = i;
+ return(0);
+ }
+
+mDNSlocal NetworkInterfaceInfoOSX *FindRoutableIPv4(mDNS *const m, mDNSu32 scope_id)
+ {
+ NetworkInterfaceInfoOSX *i;
+ for (i = m->p->InterfaceList; i; i = i->next)
+ if (i->CurrentlyActive && 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);
+ }
+
+ 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_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_addr->sa_family == AF_INET || ifa->ifa_addr->sa_family == AF_INET6) &&
+ (ifa->ifa_flags & IFF_MULTICAST) &&
+ (ifa->ifa_flags & IFF_UP) && !(ifa->ifa_flags & IFF_POINTOPOINT))
+ {
+ 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 TxAndRx 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->CurrentlyActive)
+ {
+ mDNSBool txrx = ((i->ifinfo.ip.type == mDNSAddrType_IPv4) || !FindRoutableIPv4(m, i->scope_id));
+ if (i->ifinfo.TxAndRx != txrx)
+ {
+ i->ifinfo.TxAndRx = txrx;
+ i->CurrentlyActive = 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 (!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)
+ {
+ mStatus err = 0;
+ NetworkInterfaceInfo *n = &i->ifinfo;
+ NetworkInterfaceInfoOSX *alias = SearchForInterfaceByName(m, i->ifa_name, i->sa_family);
+ if (!alias) alias = i;
+
+ if (n->InterfaceID && n->InterfaceID != (mDNSInterfaceID)alias)
+ {
+ LogMsg("SetupActiveInterfaces ERROR! n->InterfaceID %p != alias %p", n->InterfaceID, alias);
+ n->InterfaceID = mDNSNULL;
+ }
+
+ if (!n->InterfaceID)
+ {
+ n->InterfaceID = (mDNSInterfaceID)alias;
+ mDNS_RegisterInterface(m, n);
+ debugf("SetupActiveInterfaces: Registered %s(%lu) InterfaceID %p %#a%s",
+ i->ifa_name, i->scope_id, alias, &n->ip, n->InterfaceActive ? " (Primary)" : "");
+ }
+
+ if (!n->TxAndRx)
+ debugf("SetupActiveInterfaces: No TX/Rx on %s(%lu) InterfaceID %p %#a", i->ifa_name, i->scope_id, alias, &n->ip);
+ else
+ {
+ if (i->sa_family == AF_INET && alias->sktv4 == -1)
+ {
+ #if mDNS_AllowPort53
+ err = SetupSocket(i, UnicastDNSPort, &alias->skt53, &alias->cfs53);
+ #endif
+ if (!err) err = SetupSocket(i, MulticastDNSPort, &alias->sktv4, &alias->cfsv4);
+ if (err == 0) debugf("SetupActiveInterfaces: v4 socket%2d %s(%lu) InterfaceID %p %#a", alias->sktv4, i->ifa_name, i->scope_id, n->InterfaceID, &n->ip);
+ else LogMsg("SetupActiveInterfaces: v4 socket%2d %s(%lu) InterfaceID %p %#a FAILED", alias->sktv4, i->ifa_name, i->scope_id, n->InterfaceID, &n->ip);
+ }
+
+ if (i->sa_family == AF_INET6 && alias->sktv6 == -1)
+ {
+ err = SetupSocket(i, MulticastDNSPort, &alias->sktv6, &alias->cfsv6);
+ if (err == 0) debugf("SetupActiveInterfaces: v6 socket%2d %s(%lu) InterfaceID %p %#a", alias->sktv6, i->ifa_name, i->scope_id, n->InterfaceID, &n->ip);
+ else LogMsg("SetupActiveInterfaces: v6 socket%2d %s(%lu) InterfaceID %p %#a FAILED", alias->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->CurrentlyActive = mDNSfalse;
+ }
+
+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 alias 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 it's InterfaceID is changing, deregister it
+ NetworkInterfaceInfoOSX *alias = (NetworkInterfaceInfoOSX *)(i->ifinfo.InterfaceID);
+ if (i->ifinfo.InterfaceID && (!i->CurrentlyActive || (alias && !alias->CurrentlyActive) || i->CurrentlyActive == 2))
+ {
+ debugf("ClearInactiveInterfaces: Deregistering %#a", &i->ifinfo.ip);
+ mDNS_DeregisterInterface(m, &i->ifinfo);
+ i->ifinfo.InterfaceID = mDNSNULL;
+ }
+ }
+
+ // 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.)
+ // 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 mDNS_AllowPort53
+ if (i->cfs53) { CFSocketInvalidate(i->cfs53); CFRelease(i->cfs53); }
+ i->skt53 = -1;
+ i->cfs53 = NULL;
+ #endif
+ if (i->cfsv4) { CFSocketInvalidate(i->cfsv4); CFRelease(i->cfsv4); }
+ if (i->cfsv6) { CFSocketInvalidate(i->cfsv6); CFRelease(i->cfsv6); }
+ i->sktv4 = i->sktv6 = -1;
+ i->cfsv4 = i->cfsv6 = NULL;
+
+ // 3. If no longer active, delete interface from list and free memory
+ if (!i->CurrentlyActive)
+ {
+ 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 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);
+ }
+
+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);
+ }
+
+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;
+
+ 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->InterfaceList = mDNSNULL;
+ m->p->userhostlabel.c[0] = 0;
+ UpdateInterfaceList(m);
+ SetupActiveInterfaces(m);
+
+ err = WatchForNetworkChanges(m);
+ if (err) return(err);
+
+ err = WatchForPowerChanges(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);
+ }
+
+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));
+ }
+
+// 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); }
--- /dev/null
+/*
+ * 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 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
+<rdar://problem/3313413> 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 <sys/ioctl.h>
+#include <sys/sockio.h>
+#define ifaddrs ifa_info
+#ifndef ifa_broadaddr
+#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */
+#endif
+#include <sys/cdefs.h>
+
+/* 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 <net/if.h> */
+#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 <net/if.h> */
+ 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);
+ }
--- /dev/null
+/*
+ * 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: DNSServiceDiscoveryDefines.h,v $
+Revision 1.5 2003/08/12 19:56:25 cheshire
+Update to APSL 2.0
+
+ */
+
+#ifndef __DNS_SERVICE_DISCOVERY_DEFINES_H
+#define __DNS_SERVICE_DISCOVERY_DEFINES_H
+
+#include <mach/mach_types.h>
+
+#define DNS_SERVICE_DISCOVERY_SERVER "DNSServiceDiscoveryServer"
+
+typedef char DNSCString[1024];
+typedef char sockaddr_t[128];
+
+typedef const char * record_data_t;
+
+#endif /* __DNS_SERVICE_DISCOVERY_DEFINES_H */
--- /dev/null
+/*
+ * Copyright (c) 2002 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@
+ */
+
+subsystem
+ DNSServiceDiscoveryReply 7250;
+
+ServerPrefix internal_;
+
+#include <mach/std_types.defs>
+#include <mach/mach_types.defs>
+
+import "DNSServiceDiscoveryDefines.h";
+
+type DNSCString = c_string[*:1024];
+type sockaddr_t = array[128] of char;
+
+simpleroutine DNSServiceDomainEnumerationReply_rpc(
+ reply: mach_port_t;
+ in resultType: int;
+ in replyDomain: DNSCString;
+ in flags: int;
+ SendTime to: natural_t);
+
+simpleroutine DNSServiceBrowserReply_rpc(
+ reply: mach_port_t;
+ in resultType: int;
+ in replyName: DNSCString;
+ in replyType: DNSCString;
+ in replyDomain: DNSCString;
+ in flags: int;
+ SendTime to: natural_t);
+
+
+simpleroutine DNSServiceRegistrationReply_rpc(
+ reply: mach_port_t;
+ in resultType: int;
+ SendTime to: natural_t);
+
+
+simpleroutine DNSServiceResolverReply_rpc(
+ reply: mach_port_t;
+ in interface: sockaddr_t;
+ in address: sockaddr_t;
+ in txtRecord: DNSCString;
+ in flags: int;
+ SendTime to: natural_t);
--- /dev/null
+/*
+ * Copyright (c) 2002 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@
+ */
+
+subsystem
+ DNSServiceDiscoveryRequest 7200;
+
+ServerPrefix provide_;
+
+#include <mach/std_types.defs>
+#include <mach/mach_types.defs>
+
+import "DNSServiceDiscoveryDefines.h";
+
+type DNSCString = c_string[*:1024];
+type record_data = ^ array [] of MACH_MSG_TYPE_BYTE
+ ctype: record_data_t;
+
+simpleroutine DNSServiceBrowserCreate_rpc(
+ server: mach_port_t;
+ in client: mach_port_t;
+ in regtype: DNSCString;
+ in domain: DNSCString);
+
+
+simpleroutine DNSServiceDomainEnumerationCreate_rpc(
+ server: mach_port_t;
+ in client: mach_port_t;
+ in registrationDomains: int);
+
+simpleroutine DNSServiceRegistrationCreate_rpc(
+ server: mach_port_t;
+ in client: mach_port_t;
+ in name: DNSCString;
+ in regtype: DNSCString;
+ in domain: DNSCString;
+ in port: int;
+ in txtRecord: DNSCString);
+
+
+simpleroutine DNSServiceResolverResolve_rpc(
+ server: mach_port_t;
+ in client: mach_port_t;
+ in name: DNSCString;
+ in regtype: DNSCString;
+ in domain: DNSCString);
+
+routine DNSServiceRegistrationAddRecord_rpc(
+ server: mach_port_t;
+ in client: mach_port_t;
+ in record_type: int;
+ in record_data: record_data;
+ in ttl: uint32_t;
+ out record_reference: natural_t);
+
+simpleroutine DNSServiceRegistrationUpdateRecord_rpc(
+ server: mach_port_t;
+ in client: mach_port_t;
+ in record_reference: natural_t;
+ in record_data: record_data;
+ in ttl: uint32_t);
+
+simpleroutine DNSServiceRegistrationRemoveRecord_rpc(
+ server: mach_port_t;
+ in client: mach_port_t;
+ in record_reference: natural_t);
--- /dev/null
+/*
+ * Copyright (c) 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: SampleUDSClient.c,v $
+Revision 1.7 2003/08/18 18:50:15 cheshire
+Can now give "-lo" as first parameter, to test "local only" mode
+
+Revision 1.6 2003/08/12 19:56:25 cheshire
+Update to APSL 2.0
+
+ */
+
+#include <dns_sd.h>
+#include <unistd.h>
+#include <DNSServiceDiscovery/DNSServiceDiscovery.h> // include Mach API to ensure no conflicts exist
+#include <CoreFoundation/CoreFoundation.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#define BIND_8_COMPAT 1
+#include <nameser.h>
+// T_SRV is not defined in older versions of nameser.h
+#ifndef T_SRV
+#define T_SRV 33
+#endif
+
+// constants
+#define MAX_DOMAIN_LABEL 63
+#define MAX_DOMAIN_NAME 255
+#define MAX_CSTRING 2044
+
+
+// data structure defs
+typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16;
+
+typedef struct { u_char c[ 64]; } domainlabel;
+typedef struct { u_char c[256]; } domainname;
+
+
+typedef struct
+ {
+ uint16_t priority;
+ uint16_t weight;
+ uint16_t port;
+ domainname target;
+ } srv_rdata;
+
+
+// private function prototypes
+static void sighdlr(int signo);
+static char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc);
+static char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc);
+//static void MyCallbackWrapper(CFSocketRef sr, CFSocketCallBackType t, CFDataRef dr, const void *i, void *context);
+static void print_rdata(int type, int len, const u_char *rdata);
+static void query_cb(const DNSServiceRef DNSServiceRef, const DNSServiceFlags flags, const u_int32_t interfaceIndex, const DNSServiceErrorType errorCode, const char *name, const u_int16_t rrtype, const u_int16_t rrclass, const u_int16_t rdlen, const void *rdata, const u_int32_t ttl, void *context);
+static void resolve_cb(const DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const char *txtRecord, void *context);
+static void my_enum_cb( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *replyDomain, void *context);
+static void my_regecordcb(DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, void *context);
+static void browse_cb(DNSServiceRef sdr, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err, const char *serviceName, const char *regtype, const char *domain, void *context);
+
+
+// globals
+static DNSServiceRef sdr = NULL;
+static uint32_t InterfaceIndex = 0;
+
+static void regservice_cb(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, void *context)
+ {
+ #pragma unused (sdRef, flags, errorCode, context)
+ printf("regservice_cb %s %s %s\n", name, regtype, domain);
+ }
+
+int main (int argc, char * argv[]) {
+ int err, t, i;
+ char *name, *type, *domain;
+ DNSServiceFlags flags;
+ DNSRecordRef recordrefs[10];
+ char host[256];
+ int ipaddr = 12345; // random IP address
+
+ char full[1024];
+
+ // First parameter "-lo" means "local only"
+ if (!strcmp(argv[1], "-lo")) { InterfaceIndex = -1; argv++; argc--; }
+
+ if (signal(SIGINT, sighdlr) == SIG_ERR) fprintf(stderr, "ERROR - can't catch interupt!\n");
+ if (argc < 2) exit(1);
+
+ if (!strcmp(argv[1], "-regrecord"))
+ {
+ err = DNSServiceCreateConnection(&sdr);
+ if (err)
+ {
+ printf("DNSServiceCreateConnection returned %d\n", err);
+ exit(1);
+ }
+ printf("registering 10 address records...\n");
+ for (i = 0; i < 10; i++)
+ {
+ sprintf(host, "testhost-%d.local.", i);
+ ipaddr++;
+ err = DNSServiceRegisterRecord(sdr, &recordrefs[i], kDNSServiceFlagsUnique, InterfaceIndex,
+ host, 1, 1, 4, &ipaddr, 60, my_regecordcb, NULL);
+ if (err)
+ {
+ printf("DNSServiceRegisterRecord returned error %d\n", err);
+ exit(1);
+ }
+ }
+ printf("processing results...\n");
+ for (i = 0; i < 10; i++) DNSServiceProcessResult(sdr);
+ printf("deregistering half of the records\n");
+ for (i = 0; i < 10; i++)
+ {
+ if (i % 2)
+ {
+ err = DNSServiceRemoveRecord(sdr, recordrefs[i], 0);
+ if (err)
+ {
+ printf("DNSServiceRemoveRecord returned error %d\n" ,err);
+ exit(1);
+ }
+ }
+ }
+ printf("sleeping 10...\n");
+ sleep(10);
+ printf("deregistering all remaining records\n");;
+ DNSServiceRefDeallocate(sdr);
+ printf("done. sleeping 10..\n");
+ sleep(10);
+ exit(1);
+ }
+
+ if (!strcmp(argv[1], "-browse"))
+ {
+ if (argc < 3) exit(1);
+ err = DNSServiceBrowse(&sdr, 0, InterfaceIndex, argv[2], NULL /*"local."*/, browse_cb, NULL);
+ if (err)
+ {
+ printf("DNSServiceBrowse returned error %d\n", err);
+ exit(1);
+ }
+ while(1) DNSServiceProcessResult(sdr);
+ }
+
+ if (!strcmp(argv[1], "-enum"))
+ {
+ if (!strcmp(argv[2], "browse")) flags = kDNSServiceFlagsBrowseDomains;
+ else if (!strcmp(argv[2], "register")) flags = kDNSServiceFlagsRegistrationDomains;
+ else exit(1);
+
+ err = DNSServiceEnumerateDomains(&sdr, flags, InterfaceIndex, my_enum_cb, NULL);
+ if (err)
+ {
+ printf("EnumerateDomains returned error %d\n", err);
+ exit(1);
+ }
+ while(1) DNSServiceProcessResult(sdr);
+ }
+ if (!strcmp(argv[1], "-query"))
+ {
+ t = atol(argv[5]);
+ err = DNSServiceConstructFullName(full, argv[2], argv[3], argv[4]);
+ if (err) exit(1);
+ printf("resolving fullname %s type %d\n", full, t);
+ err = DNSServiceQueryRecord(&sdr, 0, 0, full, t, 1, query_cb, NULL);
+ while (1) DNSServiceProcessResult(sdr);
+ }
+
+ if (!strcmp(argv[1], "-regservice"))
+ {
+ char *regtype = "_http._tcp";
+ char txtstring[] = "\x0DMy Txt Record";
+ if (argc > 2) name = argv[2];
+ else name = NULL;
+ if (argc > 3) regtype = argv[3];
+ uint16_t PortAsNumber = 123;
+ if (argc > 4) PortAsNumber = atoi(argv[4]);
+ Opaque16 registerPort = { { PortAsNumber >> 8, PortAsNumber & 0xFF } };
+ err = DNSServiceRegister(&sdr, 0, InterfaceIndex, name, regtype, "local.", NULL, registerPort.NotAnInteger, sizeof(txtstring)-1, txtstring, regservice_cb, NULL);
+ if (err)
+ {
+ printf("DNSServiceRegister returned error %d\n", err);
+ exit(1);
+ }
+ while (1) DNSServiceProcessResult(sdr);
+ }
+ if (!strcmp(argv[1], "-resolve"))
+ {
+ name = argv[2];
+ type = argv[3];
+ domain = argv[4];
+ err = DNSServiceResolve(&sdr, 0, InterfaceIndex, name, type, domain, resolve_cb, NULL);
+ if (err)
+ {
+ printf("DNSServiceResolve returned error %d\n", err);
+ exit(1);
+ }
+ while(1) DNSServiceProcessResult(sdr);
+ }
+ exit(1);
+ }
+
+
+
+// callbacks
+
+// wrapper to make callbacks fit CFRunLoop callback signature
+/*
+static void MyCallbackWrapper(CFSocketRef sr, CFSocketCallBackType t, CFDataRef dr, const void *i, void *context)
+ {
+ (void)sr;
+ (void)t;
+ (void)dr;
+ (void)i;
+
+ DNSServiceRef *sdr = context;
+ DNSServiceDiscoveryProcessResult(*sdr);
+ }
+*/
+
+static void browse_cb(DNSServiceRef sdr, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err, const char *serviceName, const char *regtype, const char *domain, void *context)
+ {
+ #pragma unused(sdr, ifi, context)
+
+ if (err)
+ {
+ printf("Callback: error %d\n", err);
+ return;
+ }
+ printf("BrowseCB: %s %s %s %s (%s)\n", serviceName, regtype, domain, (flags & kDNSServiceFlagsMoreComing ? "(more coming)" : ""), flags & kDNSServiceFlagsAdd ? "(ADD)" : "(REMOVE)");
+
+ }
+
+static void my_enum_cb( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *replyDomain, void *context)
+ {
+ #pragma unused(sdRef, context)
+ char *type;
+ if (flags == kDNSServiceFlagsAdd) type = "add";
+ else if (flags == kDNSServiceFlagsRemove) type = "remove";
+ else if (flags == (kDNSServiceFlagsAdd | kDNSServiceFlagsDefault)) type = "add default";
+ else type = "unknown";
+
+
+ if (errorCode) printf("EnumerateDomainsCB: error code %d\n", errorCode);
+ else printf("%s domain %s on interface %d\n", type, replyDomain, interfaceIndex);
+ }
+
+static void query_cb(const DNSServiceRef DNSServiceRef, const DNSServiceFlags flags, const u_int32_t interfaceIndex, const DNSServiceErrorType errorCode, const char *name, const u_int16_t rrtype, const u_int16_t rrclass, const u_int16_t rdlen, const void *rdata, const u_int32_t ttl, void *context)
+ {
+ (void)DNSServiceRef;
+ (void)flags;
+ (void)interfaceIndex;
+ (void)rrclass;
+ (void)ttl;
+ (void)context;
+
+ if (errorCode)
+ {
+ printf("query callback: error==%d\n", errorCode);
+ return;
+ }
+ printf("query callback - name = %s, rdata=\n", name);
+ print_rdata(rrtype, rdlen, rdata);
+ }
+
+static void resolve_cb(const DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const char *txtRecord, void *context)
+ {
+ int i;
+
+ #pragma unused(sdRef, flags, interfaceIndex, errorCode, context, txtRecord)
+ printf("Resolved %s to %s:%d (%d bytes txt data)\n", fullname, hosttarget, port, txtLen);
+ printf("TXT Data:\n");
+ for (i = 0; i < txtLen; i++)
+ if (txtRecord[i] >= ' ') printf("%c", txtRecord[i]);
+ }
+
+
+
+static void my_regecordcb(DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, void *context)
+ {
+ #pragma unused (sdRef, RecordRef, flags, context)
+ if (errorCode) printf("regrecord CB received error %d\n", errorCode);
+ else printf("regrecord callback - no errors\n");
+ }
+
+
+// resource record data interpretation routines
+static char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc)
+ {
+ const u_char * src = label->c; // Domain label we're reading
+ const u_char len = *src++; // Read length of this (non-null) label
+ const u_char *const end = src + len; // Work out where the label ends
+ if (len > MAX_DOMAIN_LABEL) return(NULL); // If illegal label, abort
+ while (src < end) // While we have characters in the label
+ {
+ u_char c = *src++;
+ if (esc)
+ {
+ if (c == '.') // If character is a dot,
+ *ptr++ = esc; // Output escape character
+ else if (c <= ' ') // If non-printing ascii,
+ { // Output decimal escape sequence
+ *ptr++ = esc;
+ *ptr++ = (char) ('0' + (c / 100) );
+ *ptr++ = (char) ('0' + (c / 10) % 10);
+ c = (u_char)('0' + (c ) % 10);
+ }
+ }
+ *ptr++ = (char)c; // Copy the character
+ }
+ *ptr = 0; // Null-terminate the string
+ return(ptr); // and return
+ }
+
+static char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc)
+ {
+ const u_char *src = name->c; // Domain name we're reading
+ const u_char *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid
+
+ if (*src == 0) *ptr++ = '.'; // Special case: For root, just write a dot
+
+ while (*src) // While more characters in the domain name
+ {
+ if (src + 1 + *src >= max) return(NULL);
+ ptr = ConvertDomainLabelToCString_withescape((const domainlabel *)src, ptr, esc);
+ if (!ptr) return(NULL);
+ src += 1 + *src;
+ *ptr++ = '.'; // Write the dot after the label
+ }
+
+ *ptr++ = 0; // Null-terminate the string
+ return(ptr); // and return
+ }
+
+// print arbitrary rdata in a readable manned
+static void print_rdata(int type, int len, const u_char *rdata)
+ {
+ int i;
+ srv_rdata *srv;
+ char targetstr[MAX_CSTRING];
+ struct in_addr in;
+
+ switch (type)
+ {
+ case T_TXT:
+ // print all the alphanumeric and punctuation characters
+ for (i = 0; i < len; i++)
+ if (rdata[i] >= 32 && rdata[i] <= 127) printf("%c", rdata[i]);
+ printf("\n");
+ return;
+ case T_SRV:
+ srv = (srv_rdata *)rdata;
+ ConvertDomainNameToCString_withescape(&srv->target, targetstr, 0);
+ printf("pri=%d, w=%d, port=%d, target=%s\n", srv->priority, srv->weight, srv->port, targetstr);
+ return;
+ case T_A:
+ assert(len == 4);
+ memcpy(&in, rdata, sizeof(in));
+ printf("%s\n", inet_ntoa(in));
+ return;
+ case T_PTR:
+ ConvertDomainNameToCString_withescape((domainname *)rdata, targetstr, 0);
+ printf("%s\n", targetstr);
+ return;
+ default:
+ printf("ERROR: I dont know how to print RData of type %d\n", type);
+ return;
+ }
+ }
+
+
+
+
+// signal handlers, setup/teardown, etc.
+static void sighdlr(int signo)
+ {
+ assert(signo == SIGINT);
+ fprintf(stderr, "Received sigint - deallocating serviceref and exiting\n");
+ if (sdr)
+ DNSServiceRefDeallocate(sdr);
+ exit(1);
+ }
+
+
+
+
+
+
+
--- /dev/null
+/*
+ * 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@
+ *
+ * Formatting notes:
+ * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
+ * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
+ * but for the sake of brevity here I will say just this: Curly braces are not syntactially
+ * part of an "if" statement; they are the beginning and ending markers of a compound statement;
+ * therefore common sense dictates that if they are part of a compound statement then they
+ * should be indented to the same level as everything else in that compound statement.
+ * Indenting curly braces at the same level as the "if" implies that curly braces are
+ * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
+ * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
+ * 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: SamplemDNSClient.c,v $
+Revision 1.39 2003/08/18 19:05:45 cheshire
+<rdar://problem/3382423> UpdateRecord not working right
+Added "newrdlength" field to hold new length of updated rdata
+
+Revision 1.38 2003/08/12 19:56:25 cheshire
+Update to APSL 2.0
+
+Revision 1.37 2003/08/05 20:39:25 cheshire
+<rdar://problem/3362184> mDNS buffered std out makes it impossible to use from another tool
+Added "setlinebuf(stdout);"
+
+Revision 1.36 2003/07/19 03:23:13 cheshire
+<rdar://problem/2986147> mDNSResponder needs to receive and cache larger records
+
+Revision 1.35 2003/07/11 01:57:18 cheshire
+Add checkin history header
+
+ */
+
+#include <libc.h>
+#define BIND_8_COMPAT
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <DNSServiceDiscovery/DNSServiceDiscovery.h>
+
+//*************************************************************************************************************
+// Globals
+
+typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16;
+
+static char operation;
+static dns_service_discovery_ref client = NULL;
+static int num_printed;
+static char addtest = 0;
+static DNSRecordReference record;
+static char myhinfo9[11] = "\003Mac\006OS 9.2";
+static char myhinfoX[ 9] = "\003Mac\004OS X";
+static char updatetest[3] = "\002AA";
+static char bigNULL[4096];
+
+//*************************************************************************************************************
+// Supporting Utility Functions
+//
+// This code takes care of:
+// 1. Extracting the mach_port_t from the dns_service_discovery_ref
+// 2. Making a CFMachPortRef from it
+// 3. Making a CFRunLoopSourceRef from that
+// 4. Adding that source to the current RunLoop
+// 5. and passing the resulting messages back to DNSServiceDiscovery_handleReply() for processing
+//
+// Code that's not based around a CFRunLoop will need its own mechanism to receive Mach messages
+// from the mDNSResponder daemon and pass them to the DNSServiceDiscovery_handleReply() routine.
+// (There is no way to automate this, because it varies depending on the application's existing
+// event handling model.)
+
+static void MyHandleMachMessage(CFMachPortRef port, void *msg, CFIndex size, void *info)
+ {
+ (void)port; // Unused
+ (void)size; // Unused
+ (void)info; // Unused
+ DNSServiceDiscovery_handleReply(msg);
+ }
+
+static int AddDNSServiceClientToRunLoop(dns_service_discovery_ref client)
+ {
+ mach_port_t port = DNSServiceDiscoveryMachPort(client);
+ if (!port)
+ return(-1);
+ else
+ {
+ CFMachPortContext context = { 0, 0, NULL, NULL, NULL };
+ Boolean shouldFreeInfo;
+ CFMachPortRef cfMachPort = CFMachPortCreateWithPort(kCFAllocatorDefault, port, MyHandleMachMessage, &context, &shouldFreeInfo);
+ CFRunLoopSourceRef rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0);
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+ CFRelease(rls);
+ return(0);
+ }
+ }
+
+//*************************************************************************************************************
+// Sample callback functions for each of the operation types
+
+static void printtimestamp(void)
+ {
+ struct timeval tv;
+ struct tm tm;
+ gettimeofday(&tv, NULL);
+ localtime_r((time_t*)&tv.tv_sec, &tm);
+ printf("%d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec/1000);
+ }
+
+#define DomainMsg(X) ((X) == DNSServiceDomainEnumerationReplyAddDomain ? "Added" : \
+ (X) == DNSServiceDomainEnumerationReplyAddDomainDefault ? "(Default)" : \
+ (X) == DNSServiceDomainEnumerationReplyRemoveDomain ? "Removed" : "Unknown")
+
+static void regdom_reply(DNSServiceDomainEnumerationReplyResultType resultType, const char *replyDomain,
+ DNSServiceDiscoveryReplyFlags flags, void *context)
+ {
+ (void)context; // Unused
+ printtimestamp();
+ printf("Recommended Registration Domain %s %s", replyDomain, DomainMsg(resultType));
+ if (flags) printf(" Flags: %X", flags);
+ printf("\n");
+ }
+
+static void browsedom_reply(DNSServiceDomainEnumerationReplyResultType resultType, const char *replyDomain,
+ DNSServiceDiscoveryReplyFlags flags, void *context)
+ {
+ (void)context; // Unused
+ printtimestamp();
+ printf("Recommended Browsing Domain %s %s", replyDomain, DomainMsg(resultType));
+ if (flags) printf(" Flags: %X", flags);
+ printf("\n");
+ }
+
+static void browse_reply(DNSServiceBrowserReplyResultType resultType,
+ const char *replyName, const char *replyType, const char *replyDomain, DNSServiceDiscoveryReplyFlags flags, void *context)
+ {
+ char *op = (resultType == DNSServiceBrowserReplyAddInstance) ? "Add" : "Rmv";
+ (void)context; // Unused
+ if (num_printed++ == 0) printf("A/R Flags %-8s %-20s %s\n", "Domain", "Service Type", "Instance Name");
+ printtimestamp();
+ printf("%s%6X %-8s %-20s %s\n", op, flags, replyDomain, replyType, replyName);
+ }
+
+static void resolve_reply(struct sockaddr *interface, struct sockaddr *address, const char *txtRecord, DNSServiceDiscoveryReplyFlags flags, void *context)
+ {
+ (void)interface; // Unused
+ (void)context; // Unused
+ if (address->sa_family != AF_INET && address->sa_family != AF_INET6)
+ printf("Unknown address family %d\n", address->sa_family);
+ else
+ {
+ const char *src = txtRecord;
+ printtimestamp();
+
+ if (address->sa_family == AF_INET)
+ {
+ struct sockaddr_in *ip = (struct sockaddr_in *)address;
+ union { uint32_t l; u_char b[4]; } addr = { ip->sin_addr.s_addr };
+ union { uint16_t s; u_char b[2]; } port = { ip->sin_port };
+ uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1];
+ char ipstring[16];
+ sprintf(ipstring, "%d.%d.%d.%d", addr.b[0], addr.b[1], addr.b[2], addr.b[3]);
+ printf("Service can be reached at %-15s:%u", ipstring, PortAsNumber);
+ }
+ else if (address->sa_family == AF_INET6)
+ {
+ struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)address;
+ u_int16_t *w = ip6->sin6_addr.__u6_addr.__u6_addr16;
+ union { uint16_t s; u_char b[2]; } port = { ip6->sin6_port };
+ uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1];
+ char ipstring[40];
+ char ifname[IF_NAMESIZE + 1] = "";
+ sprintf(ipstring, "%04X:%04X:%04X:%04X:%04X:%04X:%04X:%04X", w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7]);
+ if (ip6->sin6_scope_id) { ifname[0] = '%'; if_indextoname(ip6->sin6_scope_id, &ifname[1]); }
+ printf("%s%s:%u", ipstring, ifname, PortAsNumber);
+ }
+ if (flags) printf(" Flags: %X", flags);
+ if (*src)
+ {
+ char txtInfo[64]; // Display at most first 64 characters of TXT record
+ char *dst = txtInfo;
+ const char *const lim = &txtInfo[sizeof(txtInfo)];
+ while (*src && dst < lim-1)
+ {
+ if (*src == '\\') *dst++ = '\\'; // '\' displays as "\\"
+ if (*src >= ' ') *dst++ = *src++; // Display normal characters as-is
+ else
+ {
+ *dst++ = '\\'; // Display a backslash
+ if (*src == 1) *dst++ = ' '; // String boundary displayed as "\ "
+ else // Other chararacters displayed as "\0xHH"
+ {
+ static const char hexchars[16] = "0123456789ABCDEF";
+ *dst++ = '0';
+ *dst++ = 'x';
+ *dst++ = hexchars[*src >> 4];
+ *dst++ = hexchars[*src & 0xF];
+ }
+ src++;
+ }
+ }
+ *dst++ = 0;
+ printf(" TXT %s", txtInfo);
+ }
+ printf("\n");
+ }
+ }
+
+static void myCFRunLoopTimerCallBack(CFRunLoopTimerRef timer, void *info)
+ {
+ (void)timer; // Parameter not used
+ (void)info; // Parameter not used
+
+ switch (operation)
+ {
+ case 'A':
+ {
+ switch (addtest)
+ {
+ case 0: printf("Adding Test HINFO record\n");
+ record = DNSServiceRegistrationAddRecord(client, T_HINFO, sizeof(myhinfo9), &myhinfo9[0], 120);
+ addtest = 1;
+ break;
+ case 1: printf("Updating Test HINFO record\n");
+ DNSServiceRegistrationUpdateRecord(client, record, sizeof(myhinfoX), &myhinfoX[0], 120);
+ addtest = 2;
+ break;
+ case 2: printf("Removing Test HINFO record\n");
+ DNSServiceRegistrationRemoveRecord(client, record);
+ addtest = 0;
+ break;
+ }
+ }
+ break;
+
+ case 'U':
+ {
+ if (updatetest[1] != 'Z') updatetest[1]++;
+ else updatetest[1] = 'A';
+ updatetest[0] = 3 - updatetest[0];
+ updatetest[2] = updatetest[1];
+ printf("Updating Test TXT record to %c\n", updatetest[1]);
+ DNSServiceRegistrationUpdateRecord(client, 0, 1+updatetest[0], &updatetest[0], 120);
+ }
+ break;
+
+ case 'N':
+ {
+ printf("Adding big NULL record\n");
+ DNSServiceRegistrationAddRecord(client, T_NULL, sizeof(bigNULL), &bigNULL[0], 120);
+ CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
+ }
+ break;
+ }
+ }
+
+static void reg_reply(DNSServiceRegistrationReplyErrorType errorCode, void *context)
+ {
+ (void)context; // Unused
+ printf("Got a reply from the server: ");
+ switch (errorCode)
+ {
+ case kDNSServiceDiscoveryNoError: printf("Name now registered and active\n"); break;
+ case kDNSServiceDiscoveryNameConflict: printf("Name in use, please choose another\n"); exit(-1);
+ default: printf("Error %d\n", errorCode); return;
+ }
+
+ if (operation == 'A' || operation == 'U' || operation == 'N')
+ {
+ CFRunLoopTimerContext myCFRunLoopTimerContext = { 0, 0, NULL, NULL, NULL };
+ CFRunLoopTimerRef timer = CFRunLoopTimerCreate(kCFAllocatorDefault,
+ CFAbsoluteTimeGetCurrent() + 5.0, 5.0, 0, 1, // Next fire time, periodic interval, flags, and order
+ myCFRunLoopTimerCallBack, &myCFRunLoopTimerContext);
+ CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
+ }
+ }
+
+//*************************************************************************************************************
+// The main test function
+
+int main(int argc, char **argv)
+ {
+ char *dom;
+ setlinebuf(stdout); // Want to see lines as they appear, not block buffered
+
+ if (argc < 2) goto Fail; // Minimum command line is the command name and one argument
+ operation = getopt(argc, (char * const *)argv, "EFBLRAUNTM");
+ if (operation == -1) goto Fail;
+
+ switch (operation)
+ {
+ case 'E': printf("Looking for recommended registration domains:\n");
+ client = DNSServiceDomainEnumerationCreate(1, regdom_reply, nil);
+ break;
+
+ case 'F': printf("Looking for recommended browsing domains:\n");
+ client = DNSServiceDomainEnumerationCreate(0, browsedom_reply, nil);
+ break;
+
+ case 'B': if (argc < optind+1) goto Fail;
+ dom = (argc < optind+2) ? "" : argv[optind+1];
+ if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string
+ printf("Browsing for %s%s\n", argv[optind+0], dom);
+ client = DNSServiceBrowserCreate(argv[optind+0], dom, browse_reply, nil);
+ break;
+
+ case 'L': if (argc < optind+2) goto Fail;
+ dom = (argc < optind+3) ? "" : argv[optind+2];
+ if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string
+ printf("Lookup %s.%s%s\n", argv[optind+0], argv[optind+1], dom);
+ client = DNSServiceResolverResolve(argv[optind+0], argv[optind+1], dom, resolve_reply, nil);
+ 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
+
+ // Copy all the TXT strings into one C string separated by ASCII-1 delimiters
+ for (i = optind+4; i < argc; i++)
+ {
+ strcpy(ptr, argv[i]);
+ ptr += strlen(argv[i]);
+ *ptr++ = 1;
+ }
+ if (ptr > txt) ptr--;
+ *ptr = 0;
+
+ printf("Registering Service %s.%s%s port %s %s\n", nam, typ, dom, argv[optind+3], txt);
+ client = DNSServiceRegistrationCreate(nam, typ, dom, registerPort.NotAnInteger, txt, reg_reply, nil);
+ break;
+ }
+
+ case 'A':
+ case 'U':
+ case 'N': {
+ Opaque16 registerPort = { { 0x12, 0x34 } };
+ static const char TXT[] = "First String\001Second String\001Third String";
+ printf("Registering Service Test._testupdate._tcp.local.\n");
+ client = DNSServiceRegistrationCreate("Test", "_testupdate._tcp.", "", registerPort.NotAnInteger, TXT, reg_reply, nil);
+ break;
+ }
+
+ case 'T': {
+ Opaque16 registerPort = { { 0x23, 0x45 } };
+ char TXT[1000];
+ unsigned int i;
+ for (i=0; i<sizeof(TXT)-1; i++)
+ if ((i & 0x1F) == 0x1F) TXT[i] = 1; else TXT[i] = 'A' + (i >> 5);
+ TXT[i] = 0;
+ printf("Registering Service Test._testlargetxt._tcp.local.\n");
+ client = DNSServiceRegistrationCreate("Test", "_testlargetxt._tcp.", "", registerPort.NotAnInteger, TXT, reg_reply, nil);
+ break;
+ }
+
+ case 'M': {
+ pid_t pid = getpid();
+ Opaque16 registerPort = { { pid >> 8, pid & 0xFF } };
+ static const char TXT1[] = "First String\001Second String\001Third String";
+ static const char TXT2[] = "\x0D" "Fourth String" "\x0C" "Fifth String" "\x0C" "Sixth String";
+ printf("Registering Service Test._testdualtxt._tcp.local.\n");
+ client = DNSServiceRegistrationCreate("", "_testdualtxt._tcp.", "", registerPort.NotAnInteger, TXT1, reg_reply, nil);
+ // use "sizeof(TXT2)-1" because we don't wan't the C compiler's null byte on the end of the string
+ record = DNSServiceRegistrationAddRecord(client, T_TXT, sizeof(TXT2)-1, TXT2, 120);
+ break;
+ }
+
+ default: goto Exit;
+ }
+
+ if (!client) { fprintf(stderr, "DNSService call failed\n"); return (-1); }
+ if (AddDNSServiceClientToRunLoop(client) != 0) { fprintf(stderr, "AddDNSServiceClientToRunLoop failed\n"); return (-1); }
+ printf("Talking to DNS SD Daemon at Mach port %d\n", DNSServiceDiscoveryMachPort(client));
+ CFRunLoopRun();
+
+ // Be sure to deallocate the dns_service_discovery_ref when you're finished
+ // Note: What other cleanup has to be done here?
+ // We should probably invalidate, remove and release our CFRunLoopSourceRef?
+ DNSServiceDiscoveryDeallocate(client);
+
+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 <Type> <Domain> (Browse for services instances)\n", argv[0]);
+ fprintf(stderr, "%s -L <Name> <Type> <Domain> (Look up a service instance)\n", argv[0]);
+ fprintf(stderr, "%s -R <Name> <Type> <Domain> <Port> [<TXT>...] (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]);
+ return 0;
+ }
--- /dev/null
+/*
+ * 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@
+ *
+ * Formatting notes:
+ * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
+ * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
+ * but for the sake of brevity here I will say just this: Curly braces are not syntactially
+ * part of an "if" statement; they are the beginning and ending markers of a compound statement;
+ * therefore common sense dictates that if they are part of a compound statement then they
+ * should be indented to the same level as everything else in that compound statement.
+ * Indenting curly braces at the same level as the "if" implies that curly braces are
+ * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
+ * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
+ * 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: daemon.c,v $
+Revision 1.134 2003/08/21 20:01:37 cheshire
+<rdar://problem/3387941> Traffic reduction: Detect long-lived Resolve() calls, and report them in syslog
+
+Revision 1.133 2003/08/20 23:39:31 cheshire
+<rdar://problem/3344098> Review syslog messages, and remove as appropriate
+
+Revision 1.132 2003/08/20 01:44:56 cheshire
+Fix errors in LogOperation() calls (only used for debugging)
+
+Revision 1.131 2003/08/19 05:39:43 cheshire
+<rdar://problem/3380097> SIGINFO dump should include resolves started by DNSServiceQueryRecord
+
+Revision 1.130 2003/08/16 03:39:01 cheshire
+<rdar://problem/3338440> InterfaceID -1 indicates "local only"
+
+Revision 1.129 2003/08/15 20:16:03 cheshire
+<rdar://problem/3366590> 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.128 2003/08/14 19:30:36 cheshire
+<rdar://problem/3378473> Include list of cache records in SIGINFO output
+
+Revision 1.127 2003/08/14 02:18:21 cheshire
+<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
+
+Revision 1.126 2003/08/12 19:56:25 cheshire
+Update to APSL 2.0
+
+Revision 1.125 2003/08/08 18:36:04 cheshire
+<rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug
+
+Revision 1.124 2003/07/25 18:28:23 cheshire
+Minor fix to error messages in syslog: Display string parameters with quotes
+
+Revision 1.123 2003/07/23 17:45:28 cheshire
+<rdar://problem/3339388> mDNSResponder leaks a bit
+Don't allocate memory for the reply until after we've verified that the reply is valid
+
+Revision 1.122 2003/07/23 00:00:04 cheshire
+Add comments
+
+Revision 1.121 2003/07/20 03:38:51 ksekar
+Bug #: 3320722
+Completed support for Unix-domain socket based API.
+
+Revision 1.120 2003/07/18 00:30:00 cheshire
+<rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead
+
+Revision 1.119 2003/07/17 19:08:58 cheshire
+<rdar://problem/3332153> Remove calls to enable obsolete UDS code
+
+Revision 1.118 2003/07/15 21:12:28 cheshire
+Added extra debugging checks in validatelists() (not used in final shipping version)
+
+Revision 1.117 2003/07/15 01:55:15 cheshire
+<rdar://problem/3315777> Need to implement service registration with subtypes
+
+Revision 1.116 2003/07/02 21:19:51 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.115 2003/07/02 02:41:24 cheshire
+<rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
+
+Revision 1.114 2003/07/01 21:10:20 cheshire
+Reinstate checkin 1.111, inadvertently overwritten by checkin 1.112
+
+Revision 1.113 2003/06/28 17:27:43 vlubet
+<rdar://problem/3221246> Redirect standard input, standard output, and
+standard error file descriptors to /dev/null just like any other
+well behaved daemon
+
+Revision 1.112 2003/06/25 23:42:19 ksekar
+Bug #: <rdar://problem/3249292>: Feature: New Rendezvous 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.
+
+Revision 1.111 2003/06/11 01:02:43 cheshire
+<rdar://problem/3287858> mDNSResponder binary compatibility
+Make single binary that can run on both Jaguar and Panther.
+
+Revision 1.110 2003/06/10 01:14:11 cheshire
+<rdar://problem/3286004> New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call
+
+Revision 1.109 2003/06/06 19:53:43 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.108 2003/06/06 14:08:06 cheshire
+For clarity, pull body of main while() loop out into a separate function called mDNSDaemonIdle()
+
+Revision 1.107 2003/05/29 05:44:55 cheshire
+Minor fixes to log messages
+
+Revision 1.106 2003/05/27 18:30:55 cheshire
+<rdar://problem/3262962> Need a way to easily examine current mDNSResponder state
+Dean Reece suggested SIGINFO is more appropriate than SIGHUP
+
+Revision 1.105 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.104 2003/05/26 00:42:06 cheshire
+<rdar://problem/3268876> Temporarily include mDNSResponder version in packets
+
+Revision 1.103 2003/05/23 23:07:44 cheshire
+<rdar://problem/3268199> Must not write to stderr when running as daemon
+
+Revision 1.102 2003/05/22 01:32:31 cheshire
+Fix typo in Log message format string
+
+Revision 1.101 2003/05/22 00:26:55 cheshire
+<rdar://problem/3239284> DNSServiceRegistrationCreate() should return error on dup
+Modify error message to explain that this is technically legal, but may indicate a bug.
+
+Revision 1.100 2003/05/21 21:02:24 ksekar
+Bug #: <rdar://problem/3247035>: Service should be prefixed
+Changed kmDNSBootstrapName to "com.apple.mDNSResponderRestart" since we're changing the main
+Mach message port to "com.apple.mDNSResponder.
+
+Revision 1.99 2003/05/21 17:33:49 cheshire
+Fix warnings (mainly printf format string warnings, like using "%d" where it should say "%lu", etc.)
+
+Revision 1.98 2003/05/20 00:33:07 cheshire
+<rdar://problem/3262962> Need a way to easily examine current mDNSResponder state
+SIGHUP now writes state summary to syslog
+
+Revision 1.97 2003/05/08 00:19:08 cheshire
+<rdar://problem/3250330> Forgot to set "err = mStatus_BadParamErr" in a couple of places
+
+Revision 1.96 2003/05/07 22:10:46 cheshire
+<rdar://problem/3250330> Add a few more error logging messages
+
+Revision 1.95 2003/05/07 19:20:17 cheshire
+<rdar://problem/3251391> Add version number to mDNSResponder builds
+
+Revision 1.94 2003/05/07 00:28:18 cheshire
+<rdar://problem/3250330> Need to make mDNSResponder more defensive against bad clients
+
+Revision 1.93 2003/05/06 00:00:49 cheshire
+<rdar://problem/3248914> Rationalize naming of domainname manipulation functions
+
+Revision 1.92 2003/04/04 20:38:57 cheshire
+Add $Log header
+
+ */
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+#include <servers/bootstrap.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <paths.h>
+#include <fcntl.h>
+
+#include "DNSServiceDiscoveryRequestServer.h"
+#include "DNSServiceDiscoveryReply.h"
+
+#include "mDNSClientAPI.h" // Defines the interface to the client layer above
+#include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform
+
+#include <DNSServiceDiscovery/DNSServiceDiscovery.h>
+
+#define ENABLE_UDS 1
+
+//*************************************************************************************************************
+// Macros
+
+// 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(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
+
+//*************************************************************************************************************
+// Globals
+
+mDNSexport mDNS mDNSStorage;
+static mDNS_PlatformSupport PlatformStorage;
+#define RR_CACHE_SIZE 64
+static CacheRecord rrcachestorage[RR_CACHE_SIZE];
+static const char PID_FILE[] = "/var/run/mDNSResponder.pid";
+
+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 server_priv_port = MACH_PORT_NULL;
+
+// mDNS Mach Message Timeout, in milliseconds.
+// We need this to be short enough that we don't deadlock the mDNSResponder if a client
+// fails to service its mach message queue, but long enough to give a well-written
+// client a chance to service its mach message queue without getting cut off.
+// Empirically, 50ms seems to work, so we set the timeout to 250ms to give
+// even extra-slow clients a fair chance before we cut them off.
+#define MDNS_MM_TIMEOUT 250
+
+static int restarting_via_mach_init = 0;
+
+#if MDNS_DEBUGMSGS
+int debug_mode = 1;
+#else
+int debug_mode = 0;
+#endif
+
+//*************************************************************************************************************
+// Active client list structures
+
+typedef struct DNSServiceDomainEnumeration_struct DNSServiceDomainEnumeration;
+struct DNSServiceDomainEnumeration_struct
+ {
+ DNSServiceDomainEnumeration *next;
+ mach_port_t ClientMachPort;
+ DNSQuestion dom; // Question asking for domains
+ DNSQuestion def; // Question asking for default domain
+ };
+
+typedef struct DNSServiceBrowserResult_struct DNSServiceBrowserResult;
+struct DNSServiceBrowserResult_struct
+ {
+ DNSServiceBrowserResult *next;
+ int resultType;
+ char name[256], type[256], dom[256];
+ };
+
+typedef struct DNSServiceBrowser_struct DNSServiceBrowser;
+struct DNSServiceBrowser_struct
+ {
+ DNSServiceBrowser *next;
+ mach_port_t ClientMachPort;
+ DNSQuestion q;
+ DNSServiceBrowserResult *results;
+ mDNSs32 lastsuccess;
+ };
+
+typedef struct DNSServiceResolver_struct DNSServiceResolver;
+struct DNSServiceResolver_struct
+ {
+ DNSServiceResolver *next;
+ mach_port_t ClientMachPort;
+ ServiceInfoQuery q;
+ ServiceInfo i;
+ mDNSs32 ReportTime;
+ };
+
+typedef struct DNSServiceRegistration_struct DNSServiceRegistration;
+struct DNSServiceRegistration_struct
+ {
+ DNSServiceRegistration *next;
+ mach_port_t ClientMachPort;
+ mDNSBool autoname;
+ mDNSBool autorename;
+ domainlabel name;
+ ServiceRecordSet s;
+ // Don't add any fields after ServiceRecordSet.
+ // This is where the implicit extra space goes if we allocate an oversized ServiceRecordSet object
+ };
+
+static DNSServiceDomainEnumeration *DNSServiceDomainEnumerationList = NULL;
+static DNSServiceBrowser *DNSServiceBrowserList = NULL;
+static DNSServiceResolver *DNSServiceResolverList = NULL;
+static DNSServiceRegistration *DNSServiceRegistrationList = NULL;
+
+//*************************************************************************************************************
+// General Utility Functions
+
+#if MACOSX_MDNS_MALLOC_DEBUGGING
+
+char _malloc_options[] = "AXZ";
+
+static void validatelists(mDNS *const m)
+ {
+ DNSServiceDomainEnumeration *e;
+ DNSServiceBrowser *b;
+ DNSServiceResolver *l;
+ DNSServiceRegistration *r;
+ AuthRecord *rr;
+ CacheRecord *cr;
+ DNSQuestion *q;
+ mDNSs32 slot;
+
+ 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);
+
+ for (b = DNSServiceBrowserList; b; b=b->next)
+ if (b->ClientMachPort == 0 || b->ClientMachPort == (mach_port_t)~0)
+ LogMsg("!!!! DNSServiceBrowserList: %p is garbage (%X) !!!!", b, b->ClientMachPort);
+
+ for (l = DNSServiceResolverList; l; l=l->next)
+ if (l->ClientMachPort == 0 || l->ClientMachPort == (mach_port_t)~0)
+ LogMsg("!!!! DNSServiceResolverList: %p is garbage (%X) !!!!", l, l->ClientMachPort);
+
+ for (r = DNSServiceRegistrationList; r; r=r->next)
+ if (r->ClientMachPort == 0 || r->ClientMachPort == (mach_port_t)~0)
+ LogMsg("!!!! DNSServiceRegistrationList: %p is garbage (%X) !!!!", r, r->ClientMachPort);
+
+ for (rr = m->ResourceRecords; rr; rr=rr->next)
+ if (rr->RecordType == 0 || rr->RecordType == 0xFF)
+ LogMsg("!!!! ResourceRecords list: %p is garbage (%X) !!!!", rr, rr->RecordType);
+
+ for (rr = m->DuplicateRecords; rr; rr=rr->next)
+ if (rr->RecordType == 0 || rr->RecordType == 0xFF)
+ LogMsg("!!!! DuplicateRecords list: %p is garbage (%X) !!!!", rr, rr->RecordType);
+
+ for (q = m->Questions; q; q=q->next)
+ if (q->ThisQInterval == (mDNSs32)~0)
+ 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)
+ if (cr->RecordType == 0 || cr->RecordType == 0xFF)
+ LogMsg("!!!! Cache slot %lu: %p is garbage (%X) !!!!", slot, rr, rr->RecordType);
+ }
+
+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;
+ //bzero(&mem[2], 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; }
+ if (mem[1] > 8000)
+ { LogMsg("free( %s : %ld @ %p) too big!", msg, mem[1], &mem[2]); return; }
+ LogMalloc("free( %s : %ld @ %p)", msg, mem[1], &mem[2]);
+ //bzero(mem, mem[1]+8);
+ memset(mem, 0xFF, mem[1]+8);
+ validatelists(&mDNSStorage);
+ free(mem);
+ }
+ }
+
+#endif
+
+//*************************************************************************************************************
+// Client Death Detection
+
+mDNSlocal void FreeDNSServiceRegistration(DNSServiceRegistration *x)
+ {
+ while (x->s.Extras)
+ {
+ ExtraResourceRecord *extras = x->s.Extras;
+ x->s.Extras = x->s.Extras->next;
+ if (extras->r.resrec.rdata != &extras->r.rdatastorage)
+ freeL("Extra RData", extras->r.resrec.rdata);
+ freeL("ExtraResourceRecord", extras);
+ }
+
+ if (x->s.RR_TXT.resrec.rdata != &x->s.RR_TXT.rdatastorage)
+ freeL("TXT RData", x->s.RR_TXT.resrec.rdata);
+
+ if (x->s.SubTypes) freeL("ServiceSubTypes", x->s.SubTypes);
+
+ 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
+// until we get the mStatus_MemFree message, if necessary
+mDNSlocal void AbortClient(mach_port_t ClientMachPort, void *m)
+ {
+ DNSServiceDomainEnumeration **e = &DNSServiceDomainEnumerationList;
+ DNSServiceBrowser **b = &DNSServiceBrowserList;
+ DNSServiceResolver **l = &DNSServiceResolverList;
+ DNSServiceRegistration **r = &DNSServiceRegistrationList;
+
+ while (*e && (*e)->ClientMachPort != ClientMachPort) e = &(*e)->next;
+ if (*e)
+ {
+ DNSServiceDomainEnumeration *x = *e;
+ *e = (*e)->next;
+ if (m && m != x)
+ LogMsg("%5d: DNSServiceDomainEnumeration(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->dom.qname.c, m, x);
+ else LogOperation("%5d: DNSServiceDomainEnumeration(%##s) STOP", ClientMachPort, x->dom.qname.c);
+ mDNS_StopGetDomains(&mDNSStorage, &x->dom);
+ mDNS_StopGetDomains(&mDNSStorage, &x->def);
+ freeL("DNSServiceDomainEnumeration", x);
+ return;
+ }
+
+ while (*b && (*b)->ClientMachPort != ClientMachPort) b = &(*b)->next;
+ if (*b)
+ {
+ DNSServiceBrowser *x = *b;
+ *b = (*b)->next;
+ if (m && m != x)
+ LogMsg("%5d: DNSServiceBrowser(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->q.qname.c, m, x);
+ else LogOperation("%5d: DNSServiceBrowser(%##s) STOP", ClientMachPort, x->q.qname.c);
+ mDNS_StopBrowse(&mDNSStorage, &x->q);
+ while (x->results)
+ {
+ DNSServiceBrowserResult *r = x->results;
+ x->results = x->results->next;
+ freeL("DNSServiceBrowserResult", r);
+ }
+ freeL("DNSServiceBrowser", x);
+ return;
+ }
+
+ while (*l && (*l)->ClientMachPort != ClientMachPort) l = &(*l)->next;
+ if (*l)
+ {
+ DNSServiceResolver *x = *l;
+ *l = (*l)->next;
+ if (m && m != x)
+ LogMsg("%5d: DNSServiceResolver(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->i.name.c, m, x);
+ else LogOperation("%5d: DNSServiceResolver(%##s) STOP", ClientMachPort, x->i.name.c);
+ mDNS_StopResolveService(&mDNSStorage, &x->q);
+ freeL("DNSServiceResolver", x);
+ return;
+ }
+
+ while (*r && (*r)->ClientMachPort != ClientMachPort) r = &(*r)->next;
+ if (*r)
+ {
+ DNSServiceRegistration *x = *r;
+ *r = (*r)->next;
+ x->autorename = mDNSfalse;
+ if (m && m != x)
+ LogMsg("%5d: DNSServiceRegistration(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->s.RR_SRV.resrec.name.c, m, x);
+ else LogOperation("%5d: DNSServiceRegistration(%##s) STOP", ClientMachPort, x->s.RR_SRV.resrec.name.c);
+ // 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, &x->s) != mStatus_NoError)
+ FreeDNSServiceRegistration(x);
+ return;
+ }
+
+ LogMsg("%5d: died or deallocated, but no record of client can be found!", ClientMachPort);
+ }
+
+#define AbortBlockedClient(C,MSG,M) AbortClientWithLogMessage((C), "stopped accepting Mach messages", " (" MSG ")", (M))
+
+mDNSlocal void AbortClientWithLogMessage(mach_port_t c, char *reason, char *msg, void *m)
+ {
+ DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList;
+ DNSServiceBrowser *b = DNSServiceBrowserList;
+ DNSServiceResolver *l = DNSServiceResolverList;
+ DNSServiceRegistration *r = DNSServiceRegistrationList;
+ while (e && e->ClientMachPort != c) e = e->next;
+ while (b && b->ClientMachPort != c) b = b->next;
+ while (l && l->ClientMachPort != c) l = l->next;
+ while (r && r->ClientMachPort != c) r = r->next;
+ if (e) LogMsg("%5d: DomainEnumeration(%##s) %s%s", c, e->dom.qname.c, reason, msg);
+ else if (b) LogMsg("%5d: Browser(%##s) %s%s", c, b->q.qname.c, reason, 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->s.RR_SRV.resrec.name.c, reason, msg);
+ else LogMsg("%5d: (%s) %s, but no record of client can be found!", c, reason, msg);
+
+ AbortClient(c, m);
+ }
+
+mDNSlocal mDNSBool CheckForExistingClient(mach_port_t c)
+ {
+ DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList;
+ DNSServiceBrowser *b = DNSServiceBrowserList;
+ DNSServiceResolver *l = DNSServiceResolverList;
+ DNSServiceRegistration *r = DNSServiceRegistrationList;
+ while (e && e->ClientMachPort != c) e = e->next;
+ while (b && b->ClientMachPort != c) b = b->next;
+ while (l && l->ClientMachPort != c) l = l->next;
+ while (r && r->ClientMachPort != c) r = r->next;
+ if (e) LogMsg("%5d: DomainEnumeration(%##s) already exists!", c, e->dom.qname.c);
+ if (b) LogMsg("%5d: Browser(%##s) already exists!", c, b->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->s.RR_SRV.resrec.name.c);
+ return(e || b || l || r);
+ }
+
+mDNSlocal void ClientDeathCallback(CFMachPortRef unusedport, void *voidmsg, CFIndex size, void *info)
+ {
+ mach_msg_header_t *msg = (mach_msg_header_t *)voidmsg;
+ (void)unusedport; // Unused
+ (void)size; // Unused
+ (void)info; // Unused
+ if (msg->msgh_id == MACH_NOTIFY_DEAD_NAME)
+ {
+ const mach_dead_name_notification_t *const deathMessage = (mach_dead_name_notification_t *)msg;
+ AbortClient(deathMessage->not_port, NULL);
+
+ /* Deallocate the send right that came in the dead name notification */
+ mach_port_destroy( mach_task_self(), deathMessage->not_port );
+ }
+ }
+
+mDNSlocal void EnableDeathNotificationForClient(mach_port_t ClientMachPort, void *m)
+ {
+ mach_port_t prev;
+ kern_return_t r = mach_port_request_notification(mach_task_self(), ClientMachPort, MACH_NOTIFY_DEAD_NAME, 0,
+ client_death_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev);
+ // If the port already died while we were thinking about it, then abort the operation right away
+ if (r != KERN_SUCCESS)
+ AbortClientWithLogMessage(ClientMachPort, "died/deallocated before we could enable death notification", "", m);
+ }
+
+//*************************************************************************************************************
+// Domain Enumeration
+
+mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
+ {
+ kern_return_t status;
+ #pragma unused(m)
+ char buffer[256];
+ DNSServiceDomainEnumerationReplyResultType rt;
+ DNSServiceDomainEnumeration *x = (DNSServiceDomainEnumeration *)question->QuestionContext;
+
+ debugf("FoundDomain: %##s PTR %##s", answer->name.c, answer->rdata->u.name.c);
+ if (answer->rrtype != kDNSType_PTR) return;
+ if (!x) { debugf("FoundDomain: DNSServiceDomainEnumeration is NULL"); return; }
+
+ if (AddRecord)
+ {
+ if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyAddDomain;
+ else rt = DNSServiceDomainEnumerationReplyAddDomainDefault;
+ }
+ else
+ {
+ if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyRemoveDomain;
+ else return;
+ }
+
+ LogOperation("%5d: DNSServiceDomainEnumeration(%##s) %##s %s",
+ x->ClientMachPort, x->dom.qname.c, answer->rdata->u.name.c,
+ !AddRecord ? "RemoveDomain" :
+ question == &x->dom ? "AddDomain" : "AddDomainDefault");
+
+ ConvertDomainNameToCString(&answer->rdata->u.name, buffer);
+ status = DNSServiceDomainEnumerationReply_rpc(x->ClientMachPort, rt, buffer, 0, MDNS_MM_TIMEOUT);
+ if (status == MACH_SEND_TIMED_OUT)
+ AbortBlockedClient(x->ClientMachPort, "enumeration", x);
+ }
+
+mDNSexport kern_return_t provide_DNSServiceDomainEnumerationCreate_rpc(mach_port_t unusedserver, mach_port_t client,
+ int regDom)
+ {
+ // Check client parameter
+ (void)unusedserver; // Unused
+ mStatus err = mStatus_NoError;
+ const char *errormsg = "Unknown";
+ 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; }
+
+ mDNS_DomainType dt1 = regDom ? mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse;
+ mDNS_DomainType dt2 = regDom ? mDNS_DomainTypeRegistrationDefault : mDNS_DomainTypeBrowseDefault;
+ const DNSServiceDomainEnumerationReplyResultType rt = DNSServiceDomainEnumerationReplyAddDomainDefault;
+
+ // Allocate memory, and handle failure
+ DNSServiceDomainEnumeration *x = mallocL("DNSServiceDomainEnumeration", sizeof(*x));
+ if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
+
+ // Set up object, and link into list
+ 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
+ kern_return_t status = DNSServiceDomainEnumerationReply_rpc(x->ClientMachPort, rt, "local.", 0, MDNS_MM_TIMEOUT);
+ if (status == MACH_SEND_TIMED_OUT)
+ { AbortBlockedClient(x->ClientMachPort, "local enumeration", x); return(mStatus_UnknownErr); }
+
+ // Do the operation
+ err = mDNS_GetDomains(&mDNSStorage, &x->dom, dt1, mDNSInterface_Any, FoundDomain, x);
+ if (!err) err = mDNS_GetDomains(&mDNSStorage, &x->def, dt2, mDNSInterface_Any, 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);
+ return(mStatus_NoError);
+
+fail:
+ LogMsg("%5d: DNSServiceDomainEnumeration(%d) failed: %s (%ld)", client, regDom, errormsg, err);
+ return(err);
+ }
+
+//*************************************************************************************************************
+// Browse for services
+
+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))
+ {
+ LogMsg("FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer",
+ answer->name.c, answer->rdata->u.name.c);
+ return;
+ }
+
+ 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);
+ ConvertDomainLabelToCString_unescaped(&name, x->name);
+ ConvertDomainNameToCString(&type, x->type);
+ ConvertDomainNameToCString(&domain, x->dom);
+ if (AddRecord)
+ x->resultType = DNSServiceBrowserReplyAddInstance;
+ else x->resultType = DNSServiceBrowserReplyRemoveInstance;
+ x->next = NULL;
+
+ DNSServiceBrowser *browser = (DNSServiceBrowser *)question->QuestionContext;
+ DNSServiceBrowserResult **p = &browser->results;
+ while (*p) p = &(*p)->next;
+ *p = x;
+ }
+
+mDNSexport kern_return_t provide_DNSServiceBrowserCreate_rpc(mach_port_t unusedserver, mach_port_t client,
+ DNSCString regtype, DNSCString domain)
+ {
+ // Check client parameter
+ (void)unusedserver; // Unused
+ mStatus err = mStatus_NoError;
+ const char *errormsg = "Unknown";
+ 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; }
+ if (!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) { errormsg = "Illegal domain"; goto badparam; }
+
+ // 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
+ x->ClientMachPort = client;
+ x->results = NULL;
+ x->lastsuccess = 0;
+ x->next = DNSServiceBrowserList;
+ DNSServiceBrowserList = x;
+
+ // Do the operation
+ LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", client, t.c, d.c);
+ err = mDNS_StartBrowse(&mDNSStorage, &x->q, &t, &d, mDNSInterface_Any, FoundInstance, x);
+ if (err) { AbortClient(client, x); errormsg = "mDNS_StartBrowse"; goto fail; }
+
+ // Succeeded: Wrap up and return
+ EnableDeathNotificationForClient(client, x);
+ return(mStatus_NoError);
+
+badparam:
+ err = mStatus_BadParamErr;
+fail:
+ LogMsg("%5d: DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%ld)", client, regtype, domain, errormsg, err);
+ return(err);
+ }
+
+//*************************************************************************************************************
+// Resolve Service Info
+
+mDNSlocal void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query)
+ {
+ kern_return_t status;
+ DNSServiceResolver *x = (DNSServiceResolver *)query->ServiceInfoQueryContext;
+ NetworkInterfaceInfoOSX *ifx = (NetworkInterfaceInfoOSX *)query->info->InterfaceID;
+ if (query->info->InterfaceID == (mDNSInterfaceID)~0) ifx = mDNSNULL;
+ struct sockaddr_storage interface;
+ struct sockaddr_storage address;
+ char cstring[1024];
+ int i, pstrlen = query->info->TXTinfo[0];
+ (void)m; // Unused
+
+ //debugf("FoundInstanceInfo %.4a %.4a %##s", &query->info->InterfaceAddr, &query->info->ip, &query->info->name);
+
+ if (query->info->TXTlen > sizeof(cstring)) return;
+
+ bzero(&interface, sizeof(interface));
+ bzero(&address, sizeof(address));
+
+ if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv4)
+ {
+ struct sockaddr_in *sin = (struct sockaddr_in*)&interface;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+ sin->sin_port = 0;
+ sin->sin_addr.s_addr = ifx->ifinfo.ip.ip.v4.NotAnInteger;
+ }
+ else if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv6)
+ {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&interface;
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_flowinfo = 0;
+ sin6->sin6_port = 0;
+ 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;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+ sin->sin_port = query->info->port.NotAnInteger;
+ sin->sin_addr.s_addr = query->info->ip.ip.v4.NotAnInteger;
+ }
+ else
+ {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&address;
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = query->info->port.NotAnInteger;
+ sin6->sin6_flowinfo = 0;
+ sin6->sin6_addr = *(struct in6_addr*)&query->info->ip.ip.v6;
+ sin6->sin6_scope_id = ifx ? ifx->scope_id : 0;
+ }
+
+ // The OS X DNSServiceResolverResolve() API is defined using a C-string,
+ // but the mDNS_StartResolveService() call actually returns a packed block of P-strings.
+ // Hence we have to convert the P-string(s) to a C-string before returning the result to the client.
+ // ASCII-1 characters are used in the C-string as boundary markers,
+ // to indicate the boundaries between the original constituent P-strings.
+ for (i=1; i<query->info->TXTlen; i++)
+ {
+ if (--pstrlen >= 0)
+ cstring[i-1] = query->info->TXTinfo[i];
+ else
+ {
+ cstring[i-1] = 1;
+ pstrlen = query->info->TXTinfo[i];
+ }
+ }
+ cstring[i-1] = 0; // Put the terminating NULL on the end
+
+ LogOperation("%5d: DNSServiceResolver(%##s) -> %#a:%d", x->ClientMachPort,
+ x->i.name.c, &query->info->ip, (int)query->info->port.b[0] << 8 | query->info->port.b[1]);
+ status = DNSServiceResolverReply_rpc(x->ClientMachPort,
+ (char*)&interface, (char*)&address, cstring, 0, MDNS_MM_TIMEOUT);
+ if (status == MACH_SEND_TIMED_OUT)
+ AbortBlockedClient(x->ClientMachPort, "resolve", x);
+ }
+
+mDNSexport kern_return_t provide_DNSServiceResolverResolve_rpc(mach_port_t unusedserver, mach_port_t client,
+ DNSCString name, DNSCString regtype, DNSCString domain)
+ {
+ // Check client parameter
+ (void)unusedserver; // Unused
+ mStatus err = mStatus_NoError;
+ const char *errormsg = "Unknown";
+ 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
+ domainlabel n;
+ domainname t, d, srv;
+ if (!name[0] || !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 ? domain : "local.")) { errormsg = "Bad Domain"; goto badparam; }
+ if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; }
+
+ // Allocate memory, and handle failure
+ DNSServiceResolver *x = mallocL("DNSServiceResolver", sizeof(*x));
+ if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
+
+ // Set up object, and link into list
+ 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->next = DNSServiceResolverList;
+ DNSServiceResolverList = x;
+
+ // Do the operation
+ LogOperation("%5d: DNSServiceResolver(%##s) START", client, x->i.name.c);
+ err = mDNS_StartResolveService(&mDNSStorage, &x->q, &x->i, FoundInstanceInfo, x);
+ if (err) { AbortClient(client, x); errormsg = "mDNS_StartResolveService"; goto fail; }
+
+ // Succeeded: Wrap up and return
+ EnableDeathNotificationForClient(client, x);
+ return(mStatus_NoError);
+
+badparam:
+ err = mStatus_BadParamErr;
+fail:
+ LogMsg("%5d: DNSServiceResolve(\"%s\", \"%s\", \"%s\") failed: %s (%ld)", client, name, regtype, domain, errormsg, err);
+ return(err);
+ }
+
+//*************************************************************************************************************
+// Registration
+
+mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result)
+ {
+ DNSServiceRegistration *x = (DNSServiceRegistration*)sr->ServiceContext;
+
+ if (result == mStatus_NoError)
+ {
+ kern_return_t status;
+ LogOperation("%5d: DNSServiceRegistration(%##s) Name Registered", x->ClientMachPort, sr->RR_SRV.resrec.name.c);
+ status = DNSServiceRegistrationReply_rpc(x->ClientMachPort, result, MDNS_MM_TIMEOUT);
+ if (status == MACH_SEND_TIMED_OUT)
+ AbortBlockedClient(x->ClientMachPort, "registration success", x);
+ }
+
+ else if (result == mStatus_NameConflict)
+ {
+ LogOperation("%5d: DNSServiceRegistration(%##s) Name Conflict", x->ClientMachPort, sr->RR_SRV.resrec.name.c);
+ // 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);
+ 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);
+ if (status == MACH_SEND_TIMED_OUT)
+ AbortBlockedClient(x->ClientMachPort, "registration conflict", x);
+ }
+ }
+
+ else if (result == mStatus_MemFree)
+ {
+ if (x->autorename)
+ {
+ debugf("RegCallback renaming %#s to %#s", x->name.c, mDNSStorage.nicelabel.c);
+ x->autorename = mDNSfalse;
+ x->name = mDNSStorage.nicelabel;
+ mDNS_RenameAndReregisterService(m, &x->s, &x->name);
+ }
+ else
+ {
+ DNSServiceRegistration **r = &DNSServiceRegistrationList;
+ while (*r && *r != x) r = &(*r)->next;
+ if (*r)
+ {
+ LogMsg("RegCallback: %##s Still in DNSServiceRegistration list; removing now", sr->RR_SRV.resrec.name.c);
+ *r = (*r)->next;
+ }
+ LogOperation("%5d: DNSServiceRegistration(%##s) Memory Free", x->ClientMachPort, sr->RR_SRV.resrec.name.c);
+ FreeDNSServiceRegistration(x);
+ }
+ }
+
+ else
+ LogMsg("%5d: DNSServiceRegistration(%##s) Unknown Result %ld",
+ x->ClientMachPort, sr->RR_SRV.resrec.name.c, result);
+ }
+
+mDNSlocal void CheckForDuplicateRegistrations(DNSServiceRegistration *x, domainname *srv, mDNSIPPort port)
+ {
+ 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 %d.",
+ x->ClientMachPort, count, srv->c, (int)port.b[0] << 8 | port.b[1]);
+ }
+
+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)
+ {
+ // Check client parameter
+ (void)unusedserver; // Unused
+ mStatus err = mStatus_NoError;
+ const char *errormsg = "Unknown";
+ 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++;
+ }
+ }
+
+ // Check other parameters
+ domainlabel n;
+ domainname t, d;
+ 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 ? 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);
+ unsigned char *pstring = &txtinfo[data_len];
+ char *ptr = txtRecord;
+
+ // The OS X DNSServiceRegistrationCreate() API is defined using a C-string,
+ // but the mDNS_RegisterService() call actually requires a packed block of P-strings.
+ // Hence we have to convert the C-string to a P-string.
+ // ASCII-1 characters are allowed in the C-string as boundary markers,
+ // so that a single C-string can be used to represent one or more P-strings.
+ while (*ptr)
+ {
+ if (++data_len >= sizeof(txtinfo)) { errormsg = "TXT record too long"; goto badtxt; }
+ if (*ptr == 1) // If this is our boundary marker, start a new P-string
+ {
+ pstring = &txtinfo[data_len];
+ pstring[0] = 0;
+ ptr++;
+ }
+ else
+ {
+ if (pstring[0] == 255) { errormsg = "TXT record invalid (component longer than 255)"; goto badtxt; }
+ pstring[++pstring[0]] = *ptr++;
+ }
+ }
+
+ data_len++;
+ if (size < data_len)
+ size = data_len;
+
+ // Allocate memory, and handle failure
+ DNSServiceRegistration *x = mallocL("DNSServiceRegistration", sizeof(*x) - sizeof(RDataBody) + size);
+ if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
+
+ 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
+ }
+ }
+
+ // Set up object, and link into list
+ x->ClientMachPort = client;
+ x->autoname = (!name[0]);
+ x->autorename = mDNSfalse;
+ x->name = n;
+ x->next = DNSServiceRegistrationList;
+ DNSServiceRegistrationList = x;
+
+ // Do the operation
+ LogOperation("%5d: DNSServiceRegistration(\"%s\", \"%s\", \"%s\") START", x->ClientMachPort, name, regtype, domain);
+ // 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, &x->s,
+ &x->name, &t, &d, // Name, type, domain
+ mDNSNULL, port, // Host and port
+ txtinfo, data_len, // TXT data, length
+ SubTypes, NumSubTypes, // Subtypes
+ mDNSInterface_Any, // Interace ID
+ RegCallback, x); // Callback and context
+
+ if (err) { AbortClient(client, x); errormsg = "mDNS_RegisterService"; goto fail; }
+
+ // 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(err);
+ }
+
+mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result)
+ {
+ (void)m; // Unused
+ if (result == mStatus_ConfigChanged)
+ {
+ DNSServiceRegistration *r;
+ for (r = DNSServiceRegistrationList; r; r=r->next)
+ if (r->autoname && !SameDomainLabel(r->name.c, mDNSStorage.nicelabel.c))
+ {
+ debugf("NetworkChanged renaming %#s to %#s", r->name.c, mDNSStorage.nicelabel.c);
+ r->autorename = mDNStrue;
+ mDNS_DeregisterService(&mDNSStorage, &r->s);
+ }
+ }
+ else if (result == mStatus_GrowCache)
+ {
+ // 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);
+ }
+ }
+
+//*************************************************************************************************************
+// Add / Update / Remove records from existing Registration
+
+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
+ mStatus err = mStatus_NoError;
+ const char *errormsg = "Unknown";
+ domainname *name = (domainname *)"";
+ 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;
+ if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; }
+ name = &x->s.RR_SRV.resrec.name;
+
+ // Check other parameters
+ if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; }
+ 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, x->s.RR_SRV.resrec.name.c, type, data_len, extra);
+ err = mDNS_AddRecordToService(&mDNSStorage, &x->s, extra, &extra->r.rdatastorage, ttl);
+ *reference = (natural_t)extra;
+ if (err) { errormsg = "mDNS_AddRecordToService"; goto fail; }
+
+ // Succeeded: Wrap up and return
+ return(mStatus_NoError);
+
+fail:
+ LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%ld)", client, name->c, type, data_len, errormsg, err);
+ return(err);
+ }
+
+mDNSlocal void UpdateCallback(mDNS *const m, AuthRecord *const rr, RData *OldRData)
+ {
+ (void)m; // Unused
+ if (OldRData != &rr->rdatastorage)
+ freeL("Old RData", OldRData);
+ }
+
+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)
+ {
+ // Check client parameter
+ (void)unusedserver; // Unused
+ mStatus err = mStatus_NoError;
+ const char *errormsg = "Unknown";
+ domainname *name = (domainname *)"";
+ 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;
+ if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; }
+ name = &x->s.RR_SRV.resrec.name;
+
+ // Check other parameters
+ if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; }
+ unsigned int size = sizeof(RDataBody);
+ if (size < data_len)
+ size = data_len;
+
+ // Find the record we're updating. NULL reference means update the primary TXT record
+ AuthRecord *rr = &x->s.RR_TXT;
+ if (reference) // Scan our list to make sure we're updating a valid record that was previously added
+ {
+ ExtraResourceRecord *e = x->s.Extras;
+ while (e && e != (ExtraResourceRecord*)reference) e = e->next;
+ if (!e) { err = mStatus_BadReferenceErr; errormsg = "No such record"; goto fail; }
+ rr = &e->r;
+ }
+
+ // Allocate memory, and handle failure
+ RData *newrdata = mallocL("RData", sizeof(*newrdata) - sizeof(RDataBody) + size);
+ if (!newrdata) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
+
+ // Fill in new length, and data
+ newrdata->MaxRDLength = size;
+ memcpy(&newrdata->u, data, data_len);
+
+ // Do the operation
+ LogOperation("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, new length %d)",
+ client, x->s.RR_SRV.resrec.name.c, reference, data_len);
+ err = mDNS_Update(&mDNSStorage, rr, ttl, data_len, newrdata, UpdateCallback);
+ if (err) { errormsg = "mDNS_Update"; goto fail; }
+
+ // Succeeded: Wrap up and return
+ return(mStatus_NoError);
+
+fail:
+ LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, %d) failed: %s (%ld)", client, name->c, reference, data_len, errormsg, err);
+ return(err);
+ }
+
+mDNSexport kern_return_t provide_DNSServiceRegistrationRemoveRecord_rpc(mach_port_t unusedserver, mach_port_t client,
+ natural_t reference)
+ {
+ // Check client parameter
+ (void)unusedserver; // Unused
+ mStatus err = mStatus_NoError;
+ const char *errormsg = "Unknown";
+ domainname *name = (domainname *)"";
+ 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;
+ if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; }
+ name = &x->s.RR_SRV.resrec.name;
+
+ // Do the operation
+ LogOperation("%5d: DNSServiceRegistrationRemoveRecord(%##s, %X)", client, x->s.RR_SRV.resrec.name.c, reference);
+ ExtraResourceRecord *extra = (ExtraResourceRecord*)reference;
+ err = mDNS_RemoveRecordFromService(&mDNSStorage, &x->s, 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 (%ld)", client, name->c, reference, errormsg, err);
+ return(err);
+ }
+
+//*************************************************************************************************************
+// Support Code
+
+mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
+ {
+ mig_reply_error_t *request = msg;
+ mig_reply_error_t *reply;
+ mach_msg_return_t mr;
+ int options;
+ (void)port; // Unused
+ (void)size; // Unused
+ (void)info; // Unused
+
+ /* allocate a reply buffer */
+ reply = CFAllocatorAllocate(NULL, provide_DNSServiceDiscoveryRequest_subsystem.maxsize, 0);
+
+ /* call the MiG server routine */
+ (void) DNSServiceDiscoveryRequest_server(&request->Head, &reply->Head);
+
+ if (!(reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && (reply->RetCode != KERN_SUCCESS))
+ {
+ if (reply->RetCode == MIG_NO_REPLY)
+ {
+ /*
+ * This return code is a little tricky -- it appears that the
+ * demux routine found an error of some sort, but since that
+ * error would not normally get returned either to the local
+ * user or the remote one, we pretend it's ok.
+ */
+ CFAllocatorDeallocate(NULL, reply);
+ return;
+ }
+
+ /*
+ * destroy any out-of-line data in the request buffer but don't destroy
+ * the reply port right (since we need that to send an error message).
+ */
+ request->Head.msgh_remote_port = MACH_PORT_NULL;
+ mach_msg_destroy(&request->Head);
+ }
+
+ if (reply->Head.msgh_remote_port == MACH_PORT_NULL)
+ {
+ /* no reply port, so destroy the reply */
+ if (reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
+ mach_msg_destroy(&reply->Head);
+ CFAllocatorDeallocate(NULL, reply);
+ return;
+ }
+
+ /*
+ * send reply.
+ *
+ * We don't want to block indefinitely because the client
+ * isn't receiving messages from the reply port.
+ * If we have a send-once right for the reply port, then
+ * this isn't a concern because the send won't block.
+ * If we have a send right, we need to use MACH_SEND_TIMEOUT.
+ * To avoid falling off the kernel's fast RPC path unnecessarily,
+ * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
+ */
+
+ options = MACH_SEND_MSG;
+ if (MACH_MSGH_BITS_REMOTE(reply->Head.msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE)
+ options |= MACH_SEND_TIMEOUT;
+
+ mr = mach_msg(&reply->Head, /* msg */
+ options, /* option */
+ reply->Head.msgh_size, /* send_size */
+ 0, /* rcv_size */
+ MACH_PORT_NULL, /* rcv_name */
+ MACH_MSG_TIMEOUT_NONE, /* timeout */
+ MACH_PORT_NULL); /* notify */
+
+ /* Has a message error occurred? */
+ switch (mr)
+ {
+ case MACH_SEND_INVALID_DEST:
+ case MACH_SEND_TIMED_OUT:
+ /* the reply can't be delivered, so destroy it */
+ mach_msg_destroy(&reply->Head);
+ break;
+
+ default :
+ /* Includes success case. */
+ break;
+ }
+
+ CFAllocatorDeallocate(NULL, reply);
+ }
+
+mDNSlocal kern_return_t registerBootstrapService()
+ {
+ kern_return_t status;
+ mach_port_t service_send_port, service_rcv_port;
+
+ debugf("Registering Bootstrap Service");
+
+ /*
+ * See if our service name is already registered and if we have privilege to check in.
+ */
+ status = bootstrap_check_in(bootstrap_port, (char*)kmDNSBootstrapName, &service_rcv_port);
+ if (status == KERN_SUCCESS)
+ {
+ /*
+ * If so, we must be a followup instance of an already defined server. In that case,
+ * the bootstrap port we inherited from our parent is the server's privilege port, so set
+ * that in case we have to unregister later (which requires the privilege port).
+ */
+ server_priv_port = bootstrap_port;
+ restarting_via_mach_init = TRUE;
+ }
+ else if (status == BOOTSTRAP_UNKNOWN_SERVICE)
+ {
+ status = bootstrap_create_server(bootstrap_port, "/usr/sbin/mDNSResponder", getuid(),
+ FALSE /* relaunch immediately, not on demand */, &server_priv_port);
+ if (status != KERN_SUCCESS) return status;
+
+ status = bootstrap_create_service(server_priv_port, (char*)kmDNSBootstrapName, &service_send_port);
+ if (status != KERN_SUCCESS)
+ {
+ mach_port_deallocate(mach_task_self(), server_priv_port);
+ return status;
+ }
+
+ status = bootstrap_check_in(server_priv_port, (char*)kmDNSBootstrapName, &service_rcv_port);
+ if (status != KERN_SUCCESS)
+ {
+ mach_port_deallocate(mach_task_self(), server_priv_port);
+ mach_port_deallocate(mach_task_self(), service_send_port);
+ return status;
+ }
+ assert(service_send_port == service_rcv_port);
+ }
+
+ /*
+ * We have no intention of responding to requests on the service port. We are not otherwise
+ * a Mach port-based service. We are just using this mechanism for relaunch facilities.
+ * So, we can dispose of all the rights we have for the service port. We don't destroy the
+ * send right for the server's privileged bootstrap port - in case we have to unregister later.
+ */
+ mach_port_destroy(mach_task_self(), service_rcv_port);
+ return status;
+ }
+
+mDNSlocal kern_return_t destroyBootstrapService()
+ {
+ debugf("Destroying Bootstrap Service");
+ return bootstrap_register(server_priv_port, (char*)kmDNSBootstrapName, MACH_PORT_NULL);
+ }
+
+mDNSlocal void ExitCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
+ {
+ (void)port; // Unused
+ (void)msg; // Unused
+ (void)size; // Unused
+ (void)info; // Unused
+/*
+ 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);
+*/
+
+ LogMsg("%s stopping", mDNSResponderVersionString);
+
+ debugf("ExitCallback: destroyBootstrapService");
+ if (!debug_mode)
+ destroyBootstrapService();
+
+ debugf("ExitCallback: Aborting MIG clients");
+ while (DNSServiceDomainEnumerationList)
+ AbortClient(DNSServiceDomainEnumerationList->ClientMachPort, DNSServiceDomainEnumerationList);
+ while (DNSServiceBrowserList)
+ AbortClient(DNSServiceBrowserList ->ClientMachPort, DNSServiceBrowserList);
+ while (DNSServiceResolverList)
+ AbortClient(DNSServiceResolverList ->ClientMachPort, DNSServiceResolverList);
+ while (DNSServiceRegistrationList)
+ AbortClient(DNSServiceRegistrationList ->ClientMachPort, DNSServiceRegistrationList);
+
+ debugf("ExitCallback: mDNS_Close");
+ mDNS_Close(&mDNSStorage);
+#if ENABLE_UDS
+ if (udsserver_exit() < 0) LogMsg("ExitCallback: udsserver_exit failed");
+#endif
+ exit(0);
+ }
+
+// Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit
+mDNSlocal void HandleSIGTERM(int signal)
+ {
+ (void)signal; // Unused
+ debugf(" ");
+ debugf("SIGINT/SIGTERM");
+ 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_local_port = MACH_PORT_NULL;
+ header.msgh_size = sizeof(header);
+ header.msgh_id = 0;
+ if (mach_msg_send(&header) != MACH_MSG_SUCCESS)
+ { LogMsg("HandleSIGTERM: mach_msg_send failed; Exiting immediately."); exit(-1); }
+ }
+
+mDNSlocal void INFOCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
+ {
+ (void)port; // Unused
+ (void)msg; // Unused
+ (void)size; // Unused
+ (void)info; // Unused
+ DNSServiceDomainEnumeration *e;
+ DNSServiceBrowser *b;
+ DNSServiceResolver *l;
+ DNSServiceRegistration *r;
+ mDNSs32 slot;
+ CacheRecord *rr;
+ mDNSu32 CacheUsed = 0, CacheActive = 0;
+
+ LogMsg("%s ---- BEGIN STATE LOG ----", mDNSResponderVersionString);
+
+ for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
+ for (rr = mDNSStorage.rrcache_hash[slot]; rr; rr=rr->next)
+ {
+ CacheUsed++;
+ if (rr->CRActiveQuestion) CacheActive++;
+ LogMsg("%s %-5s%-6s%s", rr->CRActiveQuestion ? "Active: " : "Inactive:", 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_totalused != CacheUsed)
+ LogMsg("Cache use mismatch: rrcache_totalused is %lu, true count %lu", mDNSStorage.rrcache_totalused, CacheUsed);
+ if (mDNSStorage.rrcache_active != CacheActive)
+ LogMsg("Cache use mismatch: rrcache_active is %lu, true count %lu", mDNSStorage.rrcache_active, CacheActive);
+ LogMsg("Cache currently contains %lu records; %lu referenced by active questions", CacheUsed, CacheActive);
+
+ for (e = DNSServiceDomainEnumerationList; e; e=e->next)
+ LogMsg("%5d: DomainEnumeration %##s", e->ClientMachPort, e->dom.qname.c);
+
+ for (b = DNSServiceBrowserList; b; b=b->next)
+ LogMsg("%5d: ServiceBrowse %##s", b->ClientMachPort, b->q.qname.c);
+
+ for (l = DNSServiceResolverList; l; l=l->next)
+ LogMsg("%5d: ServiceResolve %##s", l->ClientMachPort, l->i.name.c);
+
+ for (r = DNSServiceRegistrationList; r; r=r->next)
+ LogMsg("%5d: ServiceRegistration %##s", r->ClientMachPort, r->s.RR_SRV.resrec.name.c);
+
+ udsserver_info();
+
+ LogMsg("%s ---- END STATE LOG ----", mDNSResponderVersionString);
+ }
+
+mDNSlocal void HandleSIGINFO(int signal)
+ {
+ (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.");
+ }
+
+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);
+ 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)
+ LogMsg("Bootstrap_register failed(): A copy of the daemon is apparently already running");
+ else
+ LogMsg("Bootstrap_register failed(): %s %d", mach_error_string(status), status);
+ return(status);
+ }
+
+ err = mDNS_Init(&mDNSStorage, &PlatformStorage,
+ 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);
+
+ 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 (debug_mode) printf("Service registered with Mach Port %d\n", m_port);
+#if ENABLE_UDS
+ err = udsserver_init();
+ if (err) { LogMsg("Daemon start: udsserver_init failed"); return err; }
+ err = udsserver_add_rl_source();
+ if (err) { LogMsg("Daemon start: udsserver_add_rl_source failed"); return err; }
+#endif
+ return(err);
+ }
+
+mDNSlocal mDNSs32 mDNSDaemonIdle(void)
+ {
+ // 1. Call mDNS_Execute() to let mDNSCore do what it needs to do
+ mDNSs32 nextevent = mDNS_Execute(&mDNSStorage);
+
+ 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
+ // event that the client Mach queue overflows, DeliverInstance() will call AbortBlockedClient()
+ // and that will cause the DNSServiceBrowser object's memory to be freed before it returns
+ DNSServiceBrowser *x = b;
+ b = b->next;
+ if (x->results) // Try to deliver the list of results
+ {
+ while (x->results)
+ {
+ DNSServiceBrowserResult *const r = x->results;
+ DNSServiceDiscoveryReplyFlags flags = (r->next) ? DNSServiceDiscoverReplyFlagsMoreComing : 0;
+ kern_return_t status = DNSServiceBrowserReply_rpc(x->ClientMachPort, r->resultType, r->name, r->type, r->dom, flags, 1);
+ // If we failed to send the mach message, try again in one second
+ if (status == MACH_SEND_TIMED_OUT)
+ {
+ if (nextevent - now > mDNSPlatformOneSecond)
+ nextevent = now + mDNSPlatformOneSecond;
+ break;
+ }
+ else
+ {
+ x->lastsuccess = now;
+ x->results = x->results->next;
+ freeL("DNSServiceBrowserResult", r);
+ }
+ }
+ // If this client hasn't read a single message in the last 60 seconds, abort it
+ if (now - x->lastsuccess >= 60 * mDNSPlatformOneSecond)
+ AbortBlockedClient(x->ClientMachPort, "browse", x);
+ }
+ }
+
+ DNSServiceResolver *l;
+ for (l = DNSServiceResolverList; l; l=l->next)
+ if (l->ReportTime && now - l->ReportTime >= 0)
+ {
+ l->ReportTime = 0;
+ LogMsg("%5d: DNSServiceResolver(%##s) has remained active for over two minutes. "
+ "This places considerable burden on the network.", l->ClientMachPort, l->i.name.c);
+ }
+
+ return(nextevent);
+ }
+
+mDNSexport int main(int argc, char **argv)
+ {
+ int i;
+ kern_return_t status;
+ FILE *fp;
+
+ for (i=1; i<argc; i++)
+ {
+ if (!strcmp(argv[i], "-d")) debug_mode = 1;
+ }
+
+ signal(SIGINT, HandleSIGTERM); // SIGINT is what you get for a Ctrl-C
+ signal(SIGTERM, HandleSIGTERM);
+ signal(SIGINFO, HandleSIGINFO);
+
+ // Register the server with mach_init for automatic restart only during debug mode
+ if (!debug_mode)
+ registerBootstrapService();
+
+ if (!debug_mode && !restarting_via_mach_init)
+ exit(0); /* mach_init will restart us immediately as a daemon */
+
+ // Unlike deamon(), mach_init does redirect standard file descriptors to /dev/null
+ if (!debug_mode)
+ {
+ int fd = open(_PATH_DEVNULL, O_RDWR, 0);
+ if (fd != -1)
+ {
+ // Avoid to unnecessarily duplicate a file descriptor to itself
+ if (fd != STDIN_FILENO) (void)dup2(fd, STDIN_FILENO);
+ if (fd != STDOUT_FILENO) (void)dup2(fd, STDOUT_FILENO);
+ if (fd != STDERR_FILENO) (void)dup2(fd, STDERR_FILENO);
+ if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO)
+ (void)close (fd);
+ }
+ }
+
+ fp = fopen(PID_FILE, "w");
+ if (fp != NULL)
+ {
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+
+ LogMsg("%s starting", mDNSResponderVersionString);
+ status = mDNSDaemonInitialize();
+
+ if (status == 0)
+ {
+ 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
+ // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner
+ // (4) On wakeup we first process *all* events
+ // (5) then when no more events remain, we go back to (1) to finish off any deferred work and do it all again
+ while (RunLoopStatus == kCFRunLoopRunTimedOut)
+ {
+ // 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();
+#if ENABLE_UDS
+ nextevent = udsserver_idle(nextevent);
+#endif
+
+ // 2. Work out how long we expect to sleep before the next scheduled task
+ mDNSs32 ticks = nextevent - mDNSPlatformTimeNow();
+ if (ticks < 1) ticks = 1;
+ 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
+ // This gives us chance to regain control so we can call mDNS_Execute() before sleeping again
+ verbosedebugf("main: Handled %d events; now sleeping for %d ticks", numevents, ticks);
+ numevents = 0;
+ RunLoopStatus = CFRunLoopRunInMode(kCFRunLoopDefaultMode, interval, true);
+
+ // 4. Time to do some work? Handle all remaining events as quickly as we can, before returning to mDNSDaemonIdle()
+ while (RunLoopStatus == kCFRunLoopRunHandledSource)
+ {
+ numevents++;
+ RunLoopStatus = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, true);
+ }
+ }
+
+ LogMsg("ERROR: CFRunLoopRun Exiting.");
+ mDNS_Close(&mDNSStorage);
+ }
+
+ destroyBootstrapService();
+
+ return(status);
+ }
+
+// 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__ ")";
+#else
+mDNSexport const char mDNSResponderVersionString[] = "mDNSResponder (Engineering Build) (" __DATE__ " " __TIME__ ")";
+#endif
--- /dev/null
+/*
+ * Copyright (c) 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: dns_sd.h,v $
+Revision 1.3 2003/08/12 19:51:51 cheshire
+Update to APSL 2.0
+
+
+ */
+
+#ifndef _DNS_SD_H
+#define _DNS_SD_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdint.h>
+#include <netinet/in.h>
+
+
+/* DNSServiceRef, DNSRecordRef
+ *
+ * Opaque internal data types.
+ * Note: client is responsible for serializing access to these structures if
+ * they are shared between concurrent threads.
+ */
+
+typedef struct _DNSServiceRef_t *DNSServiceRef;
+typedef struct _DNSRecordRef_t *DNSRecordRef;
+
+/* General flags used in functions defined below */
+enum
+ {
+ kDNSServiceFlagsMoreComing = 1,
+ kDNSServiceFlagsFinished = 0, /* i.e. bit not set */
+ /* MoreComing indicates to a Browse callback that another result is
+ * queued. Applications should not update their UI to display browse
+ * results when the MoreComing flag is set, instead deferring the update
+ * until the callback's flag is Finished. */
+
+ kDNSServiceFlagsAdd = 2,
+ kDNSServiceFlagsDefault = 4,
+ kDNSServiceFlagsRemove = 0, /* i.e. bit not set */
+ /* Flags for domain enumeration and browse reply callbacks.
+ * "Default" applies only to enumeration and is only valid in
+ * conjuction with "Add"
+ */
+
+ kDNSServiceFlagsNoAutoRename = 8,
+ kDNSServiceFlagsAutoRename = 0, /* i.e. bit not set */
+ /* Flag for specifying renaming behavior on name conflict when registering
+ * non-shared records. NoAutorename is only valid if a name is explicitly
+ * specified when registering a service (ie the default name is not used.)
+ */
+
+
+ kDNSServiceFlagsShared = 16,
+ kDNSServiceFlagsUnique = 32,
+ /* Flag for registering individual records on a connected
+ * DNSServiceRef. Shared indicates that there may be multiple records
+ * with this name on the network (e.g. PTR records). Unique indicates that the
+ * record's name is to be unique on the network (e.g. SRV records).
+ */
+
+ kDNSServiceFlagsBrowseDomains = 64,
+ kDNSServiceFlagsRegistrationDomains = 128
+ /* Flags for specifying domain enumeration type in DNSServiceEnumerateDomains.
+ * BrowseDomains enumerates domains recommended for browsing, RegistrationDomains
+ * enumerates domains recommended for registration.
+ */
+ };
+
+/* possible error code values */
+enum
+ {
+ kDNSServiceErr_NoError = 0,
+ kDNSServiceErr_Unknown = -65537, /* 0xFFFE FFFF */
+ kDNSServiceErr_NoSuchName = -65538,
+ kDNSServiceErr_NoMemory = -65539,
+ kDNSServiceErr_BadParam = -65540,
+ kDNSServiceErr_BadReference = -65541,
+ kDNSServiceErr_BadState = -65542,
+ kDNSServiceErr_BadFlags = -65543,
+ kDNSServiceErr_Unsupported = -65544,
+ kDNSServiceErr_NotInitialized = -65545,
+ kDNSServiceErr_AlreadyRegistered = -65547,
+ kDNSServiceErr_NameConflict = -65548,
+ kDNSServiceErr_Invalid = -65549,
+ kDNSServiceErr_Incompatible = -65551, /* client library incompatible with daemon */
+ kDNSServiceErr_BadinterfaceIndex = -65552
+ /* mDNS Error codes are in the range
+ * FFFE FF00 (-65792) to FFFE FFFF (-65537) */
+ };
+
+
+/* Maximum length, in bytes, of a domain name represented as an escaped C-String */
+#define kDNSServiceMaxDomainName 1005
+
+
+typedef uint32_t DNSServiceFlags;
+typedef int32_t DNSServiceErrorType;
+
+
+/*********************************************************************************************
+ *
+ * Unix Domain Socket access, DNSServiceRef deallocation, and data processing functions
+ *
+ *********************************************************************************************/
+
+
+/* DNSServiceRefSockFD()
+ *
+ * Access underlying Unix domain socket for an initialized DNSServiceRef.
+ * The DNS Service Discovery implmementation uses this socket to communicate between
+ * the client and the mDNSResponder daemon. The application MUST NOT directly read from
+ * or write to this socket. Access to the socket is provided so that it can be used as a
+ * run loop source, or in a select() loop: when data is available for reading on the socket,
+ * DNSServiceProcessResult() should be called, which will extract the daemon's reply from
+ * the socket, and pass it to the appropriate application callback. By using a run loop or
+ * select(), results from the daemon can be processed asynchronously. Without using these
+ * constructs, DNSServiceProcessResult() will block until the response from the daemon arrives.
+ * The client is responsible for ensuring that the data on the socket is processed in a timely
+ * fashion - the daemon may terminate its connection with a client that does not clear its
+ * socket buffer.
+ *
+ * sdRef: A DNSServiceRef initialized by any of the DNSService calls.
+ *
+ * return value: The DNSServiceRef's underlying socket descriptor, or -1 on
+ * error.
+ */
+
+int DNSServiceRefSockFD(DNSServiceRef sdRef);
+
+/* DNSServiceProcessResult()
+ *
+ * Read a reply from the daemon, calling the appropriate application callback. This call will
+ * block until the daemon's response is received. Use DNSServiceRefSockFD() in
+ * conjunction with a run loop or select() to determine the presence of a response from the
+ * server before calling this function to process the reply without blocking. Call this function
+ * at any point if it is acceptable to block until the daemon's response arrives. Note that the
+ * client is responsible for ensuring that DNSServiceProcessResult() is called whenever there is
+ * a reply from the daemon - the daemon may terminate its connection with a client that does not
+ * process the daemon's responses.
+ *
+ * sdRef: A DNSServiceRef initialized by any of the DNSService calls
+ * that take a callback parameter.
+ *
+ * return value: Returns kDNSServiceErr_NoError on success, otherwise returns
+ * an error code indicating the specific failure that occurred.
+ */
+
+DNSServiceErrorType DNSServiceProcessResult(DNSServiceRef sdRef);
+
+/* DNSServiceRefDeallocate()
+ *
+ * Terminate a connection with the daemon and free memory associated with the DNSServiceRef.
+ * Any services or records registered with this DNSServiceRef will be deregistered. Any
+ * Browse, Resolve, or Query operations called with this reference will be terminated. If the
+ * reference's underlying socket is used in a run loop or select() call, it should be removed BEFORE
+ * DNSServiceRefDeallocate() is called, as this function closes the reference's socket.
+ *
+ * Note: This call is to be used only with the DNSServiceRef defined by this API. It is
+ * not compatible with dns_service_discovery_ref objects defined in the legacy Mach-based
+ * DNSServiceDiscovery.h API.
+ *
+ * sdRef: A DNSServiceRef initialized by any of the DNSService calls.
+ *
+ */
+
+void DNSServiceRefDeallocate(DNSServiceRef sdRef);
+
+
+/*********************************************************************************************
+ *
+ * Domain Enumeration
+ *
+ *********************************************************************************************/
+
+/* DNSServiceEnumerateDomains()
+ *
+ * Asynchronously enumerate domains available for browsing and registration.
+ * Currently, the only domain returned is "local.", but other domains will be returned in future.
+ *
+ * The enumeration MUST be cancelled via DNSServiceRefDeallocate() when no more domains
+ * are to be found.
+ *
+ *
+ * DNSServiceDomainEnumReply Callback Parameters:
+ *
+ * sdRef: The DNSServiceRef initialized by DNSServiceEnumerateDomains().
+ *
+ * flags: Possible values are:
+ * 1 (MoreComing)
+ * 2 (Add/Remove)
+ * 4 (Add Default)
+ *
+ * interfaceIndex: Specifies the interface on which the domain exists. (The index for a given
+ * interface is determined via the if_nametoindex() family of calls.)
+ *
+ * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise indicates
+ * the failure that occurred (other parameters are undefined if errorCode is nonzero).
+ *
+ * replyDomain: The name of the domain.
+ *
+ * context: The context pointer passed to DNSServiceEnumerateDomains.
+ *
+ */
+
+typedef void (*DNSServiceDomainEnumReply)
+ (
+ DNSServiceRef sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char *replyDomain,
+ void *context
+ );
+
+/* DNSServiceEnumerateDomains() Parameters:
+ *
+ *
+ * sdRef: A pointer to an uninitialized sdRef. May be passed to
+ * DNSServiceRefDeallocate() to cancel the enumeration.
+ *
+ * flags: Possible values are:
+ * 0 (BrowseDomains) to enumerate domains recommended for browsing.
+ * 32 (RegistrationDomains) to enumerate domains recommended for registration.
+ *
+ * interfaceIndex: If non-zero, specifies the interface on which to look for domains.
+ * (the index for a given interface is determined via the if_nametoindex()
+ * family of calls.) Most applications will pass 0 to enumerate domains on
+ * all interfaces.
+ *
+ * callBack: The function to be called when a domain is found or the call asynchronously
+ * fails.
+ *
+ * context: An application context pointer which is passed to the callback function
+ * (may be NULL).
+ *
+ * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous
+ * errors are delivered to the callback), otherwise returns an error code indicating
+ * the error that occurred (the callback is not invoked and the DNSServiceRef
+ * is not initialized.)
+ */
+
+DNSServiceErrorType DNSServiceEnumerateDomains
+ (
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceDomainEnumReply callBack,
+ void *context /* may be NULL */
+ );
+
+/*********************************************************************************************
+ *
+ * Service Registration
+ *
+ *********************************************************************************************/
+
+/* Register a service that is discovered via Browse() and Resolve() calls.
+ *
+ *
+ * DNSServiceRegisterReply() Callback Parameters:
+ *
+ * sdRef: The DNSServiceRef initialized by DNSServiceRegister().
+ *
+ * flags: Currently unused, reserved for future use.
+ *
+ * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will
+ * indicate the failure that occurred (including name conflicts, if the
+ * kDNSServiceFlagsNoAutoRenameOnConflict flag was passed to the
+ * callout.) Other parameters are undefined if errorCode is nonzero.
+ *
+ * name: The service name registered (if the application did not specify a name in
+ * DNSServiceRegister(), this indicates what name was automatically chosen).
+ *
+ * regtype: The type of service registered, as it was passed to the callout.
+ *
+ * domain: The domain on which the service was registered (if the application did not
+ * specify a domain in DNSServiceRegister(), this indicates the default domain
+ * on which the service was registered).
+ *
+ * context: The context pointer that was passed to the callout.
+ *
+ */
+
+typedef void (*DNSServiceRegisterReply)
+ (
+ DNSServiceRef sdRef,
+ DNSServiceFlags flags,
+ DNSServiceErrorType errorCode,
+ const char *name,
+ const char *regtype,
+ const char *domain,
+ void *context
+ );
+
+/* DNSServiceRegister() Parameters:
+ *
+ * sdRef: A pointer to an uninitialized sdRef. If this call succeeds, the reference
+ * may be passed to
+ * DNSServiceRefDeallocate() to deregister the service.
+ *
+ * interfaceIndex: If non-zero, specifies the interface on which to register the service
+ * (the index for a given interface is determined via the if_nametoindex()
+ * family of calls.) Most applications will pass 0 to register on all
+ * available interfaces. Pass -1 to register a service only on the local
+ * machine (service will not be visible to remote hosts.)
+ *
+ * flags: Indicates the renaming behavior on name conflict (most applications
+ * will pass 0). See flag definitions above for details.
+ *
+ * name: If non-NULL, specifies the service name to be registered.
+ * Most applications will not specify a name, in which case the
+ * computer name is used (this name is communicated to the client via
+ * the callback).
+ *
+ * regtype: The service type followed by the protocol, separated by a dot
+ * (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
+ *
+ * domain: If non-NULL, specifies the domain on which to advertise the service.
+ * Most applications will not specify a domain, instead automatically
+ * registering in the default domain(s).
+ *
+ * host: If non-NULL, specifies the SRV target host name. Most applications
+ * will not specify a host, instead automatically using the machine's
+ * default host name(s). Note that specifying a non-NULL host does NOT
+ * create an address record for that host - the application is responsible
+ * for ensuring that the appropriate address record exists, or creating it
+ * via DNSServiceRegisterRecord().
+ *
+ * port: The port on which the service accepts connections. Pass 0 for a
+ * "placeholder" service (i.e. a service that will not be discovered by
+ * browsing, but will cause a name conflict if another client tries to
+ * register that same name.) Most clients will not use placeholder services.
+ *
+ * txtLen: The length of the txtRecord, in bytes. Must be zero if the txtRecord is NULL.
+ *
+ * txtRecord: The txt record rdata. May be NULL. Note that a non-NULL txtRecord
+ * MUST be a properly formatted DNS TXT record, i.e. <length byte> <data>
+ * <length byte> <data> ...
+ *
+ * callBack: The function to be called when the registration completes or asynchronously
+ * fails. The client MAY pass NULL for the callback - The client will NOT be notified
+ * of the default values picked on its behalf, and the client will NOT be notified of any
+ * asynchronous errors (e.g. out of memory errors, etc.) that may prevent the registration
+ * of the service. The client may NOT pass the NoAutoRename flag if the callback is NULL.
+ * The client may still deregister the service at any time via DNSServiceRefDeallocate().
+ *
+ * context: An application context pointer which is passed to the callback function
+ * (may be NULL).
+ *
+ * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous
+ * errors are delivered to the callback), otherwise returns an error code indicating
+ * the error that occurred (the callback is never invoked and the DNSServiceRef
+ * is not initialized.)
+ *
+ */
+
+DNSServiceErrorType DNSServiceRegister
+ (
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ const char *name, /* may be NULL */
+ const char *regtype,
+ const char *domain, /* may be NULL */
+ const char *host, /* may be NULL */
+ uint16_t port,
+ uint16_t txtLen,
+ const void *txtRecord, /* may be NULL */
+ DNSServiceRegisterReply callBack, /* may be NULL */
+ void *context /* may be NULL */
+ );
+
+/* DNSServiceAddRecord()
+ *
+ * Add a record to a registered service. The name of the record will be the same as the
+ * registered service's name.
+ * The record can later be updated or deregistered by passing the RecordRef initialized
+ * by this function to DNSServiceUpdateRecord() or DNSServiceRemoveRecord().
+ *
+ *
+ * Parameters;
+ *
+ * sdRef: A DNSServiceRef initialized by DNSServiceRegister().
+ *
+ * RecordRef: A pointer to an uninitialized DNSRecordRef. Upon succesfull completion of this
+ * call, this ref may be passed to DNSServiceUpdateRecord() or DNSServiceRemoveRecord().
+ *
+ * flags: Currently ignored, reserved for future use.
+ *
+ * rrtype: The type of the record (e.g. TXT, SRV, etc), as defined in nameser.h.
+ *
+ * rdlen: The length, in bytes, of the rdata.
+ *
+ * rdata: The raw rdata to be contained in the added resource record.
+ *
+ * ttl: The time to live of the resource record, in seconds.
+ *
+ * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an
+ * error code indicating the error that occurred (the RecordRef is not initialized).
+ */
+
+DNSServiceErrorType DNSServiceAddRecord
+ (
+ DNSServiceRef sdRef,
+ DNSRecordRef *RecordRef,
+ DNSServiceFlags flags,
+ uint16_t rrtype,
+ uint16_t rdlen,
+ const void *rdata,
+ uint32_t ttl
+ );
+
+/* DNSServiceUpdateRecord
+ *
+ * Update a registered resource record. The record must either be:
+ * - The primary txt record of a service registered via DNSServiceRegister()
+ * - A record added to a registered service via DNSServiceAddRecord()
+ * - An individual record registered by DNSServiceRegisterRecord()
+ *
+ *
+ * Parameters:
+ *
+ * sdRef: A DNSServiceRef that was initialized by DNSServiceRegister()
+ * or DNSServiceCreateConnection().
+ *
+ * RecordRef: A DNSRecordRef initialized by DNSServiceAddRecord, or NULL to update the
+ * service's primary txt record.
+ *
+ * flags: Currently ignored, reserved for future use.
+ *
+ * rdlen: The length, in bytes, of the new rdata.
+ *
+ * rdata: The new rdata to be contained in the updated resource record.
+ *
+ * ttl: The time to live of the updated resource record, in seconds.
+ *
+ * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an
+ * error code indicating the error that occurred.
+ */
+
+DNSServiceErrorType DNSServiceUpdateRecord
+ (
+ DNSServiceRef sdRef,
+ DNSRecordRef RecordRef, /* may be NULL */
+ DNSServiceFlags flags,
+ uint16_t rdlen,
+ const void *rdata,
+ uint32_t ttl
+ );
+
+/* DNSServiceRemoveRecord
+ *
+ * Remove a record previously added to a service record set via DNSServiceAddRecord(), or deregister
+ * an record registered individually via DNSServiceRegisterRecord().
+ *
+ * Parameters:
+ *
+ * sdRef: A DNSServiceRef initialized by DNSServiceRegister() (if the
+ * record being removed was registered via DNSServiceAddRecord()) or by
+ * DNSServiceCreateConnection() (if the record being removed was registered via
+ * DNSServiceRegisterRecord()).
+ *
+ * recordRef: A DNSRecordRef initialized by a successful call to DNSServiceAddRecord()
+ * or DNSServiceRegisterRecord().
+ *
+ * flags: Currently ignored, reserved for future use.
+ *
+ * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an
+ * error code indicating the error that occurred.
+ */
+
+DNSServiceErrorType DNSServiceRemoveRecord
+ (
+ DNSServiceRef sdRef,
+ DNSRecordRef RecordRef,
+ DNSServiceFlags flags
+ );
+
+
+/*********************************************************************************************
+ *
+ * Service Discovery
+ *
+ *********************************************************************************************/
+
+
+/* Browse for instances of a service.
+ *
+ *
+ * DNSServiceBrowseReply() Parameters:
+ *
+ * sdRef: The DNSServiceRef initialized by DNSServiceBrowse().
+ *
+ * flags: Possible values are MoreComing and Add/Remove. See flag definitions
+ * for details.
+ *
+ * interfaceIndex: The interface on which the service is advertised. This index should
+ * be passed to DNSServiceResolve() when resolving the service.
+ *
+ * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise will
+ * indicate the failure that occurred. Other parameters are undefined if
+ * the errorCode is nonzero.
+ *
+ * serviceName: The service name discovered.
+ *
+ * regtype: The service type, as passed in to DNSServiceBrowse().
+ *
+ * domain: The domain on which the service was discovered (if the application did not
+ * specify a domain in DNSServicBrowse(), this indicates the domain on which the
+ * service was discovered.)
+ *
+ * context: The context pointer that was passed to the callout.
+ *
+ */
+
+typedef void (*DNSServiceBrowseReply)
+ (
+ DNSServiceRef sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char *serviceName,
+ const char *regtype,
+ const char *replyDomain,
+ void *context
+ );
+
+/* DNSServiceBrowse() Parameters:
+ *
+ * sdRef: A pointer to an uninitialized sdRef. May be passed to
+ * DNSServiceRefDeallocate() to terminate the browse.
+ *
+ * flags: Currently ignored, reserved for future use.
+ *
+ * interfaceIndex: If non-zero, specifies the interface on which to browse for services
+ * (the index for a given interface is determined via the if_nametoindex()
+ * family of calls.) Most applications will pass 0 to browse on all available
+ * interfaces. Pass -1 to only browse for services provided on the local host.
+ *
+ * regtype: The service type being browsed for followed by the protocol, separated by a
+ * dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
+ *
+ * domain: If non-NULL, specifies the domain on which to browse for services.
+ * Most applications will not specify a domain, instead browsing on the
+ * default domain(s).
+ *
+ * callBack: The function to be called when an instance of the service being browsed for
+ * is found, or if the call asynchronously fails.
+ *
+ * context: An application context pointer which is passed to the callback function
+ * (may be NULL).
+ *
+ * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous
+ * errors are delivered to the callback), otherwise returns an error code indicating
+ * the error that occurred (the callback is not invoked and the DNSServiceRef
+ * is not initialized.)
+ */
+
+DNSServiceErrorType DNSServiceBrowse
+ (
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ const char *regtype,
+ const char *domain, /* may be NULL */
+ DNSServiceBrowseReply callBack,
+ void *context /* may be NULL */
+ );
+
+/* DNSServiceResolve()
+ *
+ * Resolve a service name discovered via DNSServiceBrowse() to a target host name, port number, and
+ * txt record.
+ *
+ * Note: Applications should NOT use DNSServiceResolve() solely for txt record monitoring - use
+ * DNSServiceQueryRecord() instead, as it is more efficient for this task.
+ *
+ * Note: When the desired results have been returned, the client MUST terminate the resolve by calling
+ * DNSServiceRefDeallocate().
+ *
+ * Note: DNSServiceResolve() behaves correctly for typical services that have a single SRV record and
+ * a single TXT record (the TXT record may be empty.) To resolve non-standard services with multiple
+ * SRV or TXT records, DNSServiceQueryRecord() should be used.
+ *
+ * DNSServiceResolveReply Callback Parameters:
+ *
+ * sdRef: The DNSServiceRef initialized by DNSServiceResolve().
+ *
+ * flags: Possible values are MoreComing and Add/Remove. See flag definitions
+ * for details.
+ *
+ * interfaceIndex: The interface on which the service was resolved.
+ *
+ * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise will
+ * indicate the failure that occurred. Other parameters are undefined if
+ * the errorCode is nonzero.
+ *
+ * fullname: The full service domain name, in the form <servicename>.<protocol>.<domain>.
+ * (Any literal dots (".") are escaped with a backslash ("\."), and literal
+ * backslashes are escaped with a second backslash ("\\"), e.g. a web server
+ * named "Dr. Pepper" would have the fullname "Dr\.\032Pepper._http._tcp.local.").
+ * This is the appropriate format to pass to standard system DNS APIs such as
+ * res_query(), or to the special-purpose functions included in this API that
+ * take fullname parameters.
+ *
+ * hosttarget: The target hostname of the machine providing the service. This name can
+ * be passed to functions like gethostbyname() to identify the host's IP address.
+ *
+ * port: The port number on which connections are accepted for this service.
+ *
+ * txtLen: The length of the txt record, in bytes.
+ *
+ * txtRecord: The service's primary txt record, in standard txt record format.
+ *
+
+ * context: The context pointer that was passed to the callout.
+ *
+ */
+
+typedef void (*DNSServiceResolveReply)
+ (
+ DNSServiceRef sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char *fullname,
+ const char *hosttarget,
+ uint16_t port,
+ uint16_t txtLen,
+ const char *txtRecord,
+ void *context
+ );
+
+/* DNSServiceResolve() Parameters
+ *
+ * sdRef: A pointer to an uninitialized sdRef. May be passed to
+ * DNSServiceRefDeallocate() to terminate the resolve.
+ *
+ * flags: Currently ignored, reserved for future use.
+ *
+ * interfaceIndex: The interface on which to resolve the service. The client should
+ * pass the interface on which the servicename was discovered, i.e.
+ * the interfaceIndex passed to the DNSServiceBrowseReply callback,
+ * or 0 to resolve the named service on all available interfaces.
+ *
+ * name: The servicename to be resolved.
+ *
+ * regtype: The service type being resolved followed by the protocol, separated by a
+ * dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
+ *
+ * domain: The domain on which the service is registered, i.e. the domain passed
+ * to the DNSServiceBrowseReply callback.
+ *
+ * callBack: The function to be called when a result is found, or if the call
+ * asynchronously fails.
+ *
+ * context: An application context pointer which is passed to the callback function
+ * (may be NULL).
+ *
+ * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous
+ * errors are delivered to the callback), otherwise returns an error code indicating
+ * the error that occurred (the callback is never invoked and the DNSServiceRef
+ * is not initialized.)
+ */
+
+
+DNSServiceErrorType DNSServiceResolve
+ (
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ const char *name,
+ const char *regtype,
+ const char *domain,
+ DNSServiceResolveReply callBack,
+ void *context /* may be NULL */
+ );
+
+
+/*********************************************************************************************
+ *
+ * Special Purpose Calls (most applications will not use these)
+ *
+ *********************************************************************************************/
+
+/* DNS Naming Conventions:
+ *
+ * The following functions refer to resource records by their full domain name, unlike the above
+ * functions which divide the name into servicename/regtype/domain fields. In the above functions,
+ * a dot (".") is considered to be a literal dot in the servicename field (e.g. "Dr. Pepper") and
+ * a label separator in the regtype ("_ftp._tcp") or domain ("apple.com") fields. Literal dots in
+ * the domain field would be escaped with a backslash, and literal backslashes would be escaped with
+ * a second backslash (this is generally not an issue, as domain names on the Internet today almost
+ * never use characters other than letters, digits, or hyphens, and the dots are label separators.)
+ * Furthermore, this is transparent to the caller, so long as the fields are passed between functions
+ * without manipulation. However, the following, special-purpose calls use a single, full domain name.
+ * As such, all dots are considered to be label separators, unless escaped, and all backslashes are
+ * considered to be escape characters, unless preceded by a second backslash. For example, the name
+ * "Dr. Smith \ Dr. Johnson" could be passed literally as a service name parameter in the above calls,
+ * but in the special purpose call, the dots and backslash would have to be escaped
+ * (e.g. "Dr\. Smith \\ Dr\. Johnson._ftp._tcp.apple.com" for an ftp service on the apple.com domain.)
+ */
+
+/* DNSServiceConstructFullName()
+ *
+ * Concatenate a three-part domain name (as returned by the above callbacks) into a properly-escaped
+ * full domain name. Note that callbacks in the above functions ALREADY ESCAPE strings where necessary.
+ *
+ * Parameters:
+ *
+ * fullName: A pointer to a buffer that where the resulting full domain name is to be written.
+ * The buffer must be kDNSServiceDiscoveryMaxDomainName (1005) bytes in length to
+ * accommodate the longest legal domain name without buffer overrun.
+ *
+ * service: The service name - any dots or slashes must NOT be escaped.
+ * May be NULL (to construct a PTR record name, e.g.
+ * "_ftp._tcp.apple.com").
+ *
+ * regtype: The service type followed by the protocol, separated by a dot
+ * (e.g. "_ftp._tcp").
+ *
+ * domain: The domain name, e.g. "apple.com". Any literal dots or backslashes
+ * must be escaped.
+ *
+ * return value: Returns 0 on success, -1 on error.
+ *
+ */
+
+int DNSServiceConstructFullName
+ (
+ char *fullName,
+ const char *service, /* may be NULL */
+ const char *regtype,
+ const char *domain
+ );
+
+/* DNSServiceCreateConnection()
+ *
+ * Create a connection to the daemon allowing efficient registration of
+ * multiple individual records.
+ *
+ *
+ * Parameters:
+ *
+ * sdRef: A pointer to an uninitialized DNSServiceRef. Deallocating
+ * the reference (via DNSServiceRefDeallocate()) severs the
+ * connection and deregisters all records registered on this connection.
+ *
+ * return value: Returns kDNSServiceErr_NoError on success, otherwise returns
+ * an error code indicating the specific failure that occurred (in which
+ * case the DNSServiceRef is not initialized).
+ */
+
+DNSServiceErrorType DNSServiceCreateConnection(DNSServiceRef *sdRef);
+
+
+/* DNSServiceRegisterRecord
+ *
+ * Register an individual resource record on a connected DNSServiceRef.
+ *
+ * Note that name conflicts occurring for records registered via this call must be handled
+ * by the client in the callback.
+ *
+ *
+ * DNSServiceRegisterRecordReply() parameters:
+ *
+ * sdRef: The connected DNSServiceRef initialized by
+ * DNSServiceDiscoveryConnect().
+ *
+ * RecordRef: The DNSRecordRef initialized by DNSServiceRegisterRecord().
+ *
+ * flags: Currently unused, reserved for future use.
+ *
+ * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will
+ * indicate the failure that occurred (including name conflicts.)
+ * Other parameters are undefined if errorCode is nonzero.
+ *
+ * context: The context pointer that was passed to the callout.
+ *
+ */
+
+ typedef void (*DNSServiceRegisterRecordReply)
+ (
+ DNSServiceRef sdRef,
+ DNSRecordRef RecordRef,
+ DNSServiceFlags flags,
+ DNSServiceErrorType errorCode,
+ void *context
+ );
+
+
+/* DNSServiceRegisterRecord() Parameters:
+ *
+ * sdRef: A DNSServiceRef initialized by DNSServiceCreateConnection().
+ *
+ * RecordRef: A pointer to an uninitialized DNSRecordRef. Upon succesfull completion of this
+ * call, this ref may be passed to DNSServiceUpdateRecord() or DNSServiceRemoveRecord().
+ * (To deregister ALL records registered on a single connected DNSServiceRef
+ * and deallocate each of their corresponding DNSServiceRecordRefs, call
+ * DNSServiceRefDealloocate()).
+ *
+ * flags: Possible values are Shared/Unique (see flag type definitions for details).
+ *
+ * interfaceIndex: If non-zero, specifies the interface on which to register the record
+ * (the index for a given interface is determined via the if_nametoindex()
+ * family of calls.) Passing 0 causes the record to be registered on all interfaces.
+ * Passing -1 causes the record to only be visible on the local host.
+ *
+ * fullname: The full domain name of the resource record.
+ *
+ * rrtype: The numerical type of the resource record (e.g. PTR, SRV, etc), as defined
+ * in nameser.h.
+ *
+ * rrclass: The class of the resource record, as defined in nameser.h (usually 1 for the
+ * Internet class).
+ *
+ * rdlen: Length, in bytes, of the rdata.
+ *
+ * rdata: A pointer to the raw rdata, as it is to appear in the DNS record.
+ *
+ * ttl: The time to live of the resource record, in seconds.
+ *
+ * callBack: The function to be called when a result is found, or if the call
+ * asynchronously fails (e.g. because of a name conflict.)
+ *
+ * context: An application context pointer which is passed to the callback function
+ * (may be NULL).
+ *
+ * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous
+ * errors are delivered to the callback), otherwise returns an error code indicating
+ * the error that occurred (the callback is never invoked and the DNSRecordRef is
+ * not initialized.)
+ */
+
+
+DNSServiceErrorType DNSServiceRegisterRecord
+ (
+ DNSServiceRef sdRef,
+ DNSRecordRef *RecordRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ const char *fullname,
+ uint16_t rrtype,
+ uint16_t rrclass,
+ uint16_t rdlen,
+ const void *rdata,
+ uint32_t ttl,
+ DNSServiceRegisterRecordReply callBack,
+ void *context /* may be NULL */
+ );
+
+
+/* DNSServiceQueryRecord
+ *
+ * Query for an arbitrary DNS record.
+ *
+ *
+ * DNSServiceQueryRecordReply() Callback Parameters:
+ *
+ * sdRef: The DNSServiceRef initialized by DNSServiceQueryRecord().
+ *
+ * flags: Possible values are Finished/MoreComing.
+ *
+ * interfaceIndex: The interface on which the query was resolved (the index for a given
+ * interface is determined via the if_nametoindex() family of calls).
+ *
+ * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will
+ * indicate the failure that occurred. Other parameters are undefined if
+ * errorCode is nonzero.
+ *
+ * fullname: The resource record's full domain name.
+ *
+ * rrtype: The resource record's type (e.g. PTR, SRV, etc) as defined in nameser.h.
+ *
+ * rrclass: The class of the resource record, as defined in nameser.h (usually 1).
+ *
+ * rdlen: The length, in bytes, of the resource record rdata.
+ *
+ * rdata: The raw rdata of the resource record.
+ *
+ * ttl: The resource record's time to live, in seconds.
+ *
+ * context: The context pointer that was passed to the callout.
+ *
+ */
+
+typedef void (*DNSServiceQueryRecordReply)
+ (
+ DNSServiceRef DNSServiceRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char *fullname,
+ uint16_t rrtype,
+ uint16_t rrclass,
+ uint16_t rdlen,
+ const void *rdata,
+ uint32_t ttl,
+ void *context
+ );
+
+/* DNSServiceQueryRecord() Parameters:
+ *
+ * sdRef: A pointer to an uninitialized DNSServiceRef.
+ *
+ * flags: Currently unused, reserved for future use.
+ *
+ * interfaceIndex: If non-zero, specifies the interface on which to issue the query
+ * (the index for a given interface is determined via the if_nametoindex()
+ * family of calls.) Passing 0 causes the name to be queried for on all
+ * interfaces. Passing -1 causes the name to be queried for only on the
+ * local host.
+ *
+ * fullname: The full domain name of the resource record to be queried for.
+ *
+ * rrtype: The numerical type of the resource record to be queried for (e.g. PTR, SRV, etc)
+ * as defined in nameser.h.
+ *
+ * rrclass: The class of the resource record, as defined in nameser.h
+ * (usually 1 for the Internet class).
+ *
+ * callBack: The function to be called when a result is found, or if the call
+ * asynchronously fails.
+ *
+ * context: An application context pointer which is passed to the callback function
+ * (may be NULL).
+ *
+ * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous
+ * errors are delivered to the callback), otherwise returns an error code indicating
+ * the error that occurred (the callback is never invoked and the DNSServiceRef
+ * is not initialized.)
+ */
+
+DNSServiceErrorType DNSServiceQueryRecord
+ (
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ const char *fullname,
+ uint16_t rrtype,
+ uint16_t rrclass,
+ DNSServiceQueryRecordReply callBack,
+ void *context /* may be NULL */
+ );
+
+/* DNSServiceReconfirmRecord
+ *
+ * Instruct the daemon to verify the validity of a resource record that appears to
+ * be out of date (e.g. because tcp connection to a service's target failed.)
+ * Causes the record to be flushed from the daemon's cache (as well as all other
+ * daemons' caches on the network) if the record is determined to be invalid.
+ *
+ * Parameters:
+ *
+ * flags: Currently unused, reserved for future use.
+ *
+ * fullname: The resource record's full domain name.
+ *
+ * rrtype: The resource record's type (e.g. PTR, SRV, etc) as defined in nameser.h.
+ *
+ * rrclass: The class of the resource record, as defined in nameser.h (usually 1).
+ *
+ * rdlen: The length, in bytes, of the resource record rdata.
+ *
+ * rdata: The raw rdata of the resource record.
+ *
+ */
+
+void DNSServiceReconfirmRecord
+ (
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ const char *fullname,
+ uint16_t rrtype,
+ uint16_t rrclass,
+ uint16_t rdlen,
+ const void *rdata
+ );
+
+
+#endif // _DNS_SD_H
+
--- /dev/null
+/*
+ * Copyright (c) 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: dnssd_clientstub.c,v $
+Revision 1.9 2003/08/15 21:30:39 cheshire
+Bring up to date with LibInfo version
+
+Revision 1.8 2003/08/13 23:54:52 ksekar
+Bringing dnssd_clientstub.c up to date with Libinfo, per radar 3376640
+
+Revision 1.7 2003/08/12 19:56:25 cheshire
+Update to APSL 2.0
+
+ */
+
+#include "dnssd_ipc.h"
+
+#define CTL_PATH_PREFIX "/tmp/dnssd_clippath."
+// error socket (if needed) is named "dnssd_clipath.[pid].xxx:n" where xxx are the
+// last 3 digits of the time (in seconds) and n is the 6-digit microsecond time
+
+// general utility functions
+static DNSServiceRef connect_to_server(void);
+DNSServiceErrorType deliver_request(void *msg, DNSServiceRef sdr, int reuse_sd);
+static ipc_msg_hdr *create_hdr(int op, int *len, char **data_start, int reuse_socket);
+static int my_read(int sd, char *buf, int len);
+static int my_write(int sd, char *buf, int len);
+static int domain_ends_in_dot(const char *dom);
+// server response handlers
+static void handle_query_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *msg);
+static void handle_browse_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data);
+static void handle_regservice_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data);
+static void handle_regrecord_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data);
+static void handle_enumeration_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data);
+static void handle_resolve_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data);
+
+typedef struct _DNSServiceRef_t
+ {
+ int sockfd; // connected socket between client and daemon
+ int op; // request/reply_op_t
+ process_reply_callback process_reply;
+ void *app_callback;
+ void *app_context;
+ uint32_t max_index; //largest assigned record index - 0 if no additl. recs registered
+ } _DNSServiceRef_t;
+
+typedef struct _DNSRecordRef_t
+ {
+ void *app_context;
+ DNSServiceRegisterRecordReply app_callback;
+ DNSRecordRef recref;
+ int record_index; // index is unique to the ServiceDiscoveryRef
+ DNSServiceRef sdr;
+ } _DNSRecordRef_t;
+
+
+// exported functions
+
+int DNSServiceRefSockFD(DNSServiceRef sdRef)
+ {
+ if (!sdRef) return -1;
+ return sdRef->sockfd;
+ }
+
+// handle reply from server, calling application client callback. If there is no reply
+// from the daemon on the socket contained in sdRef, the call will block.
+DNSServiceErrorType DNSServiceProcessResult(DNSServiceRef sdRef)
+ {
+ ipc_msg_hdr hdr;
+ char *data;
+
+ if (!sdRef || sdRef->sockfd < 0 || !sdRef->process_reply)
+ return kDNSServiceErr_BadReference;
+
+ if (my_read(sdRef->sockfd, (void *)&hdr, sizeof(hdr)) < 0)
+ return kDNSServiceErr_Unknown;
+ if (hdr.version != VERSION)
+ return kDNSServiceErr_Incompatible;
+ data = malloc(hdr.datalen);
+ if (!data) return kDNSServiceErr_NoMemory;
+ if (my_read(sdRef->sockfd, data, hdr.datalen) < 0)
+ return kDNSServiceErr_Unknown;
+ sdRef->process_reply(sdRef, &hdr, data);
+ return kDNSServiceErr_Unknown;
+ }
+
+
+void DNSServiceRefDeallocate(DNSServiceRef sdRef)
+ {
+ if (!sdRef) return;
+ if (sdRef->sockfd > 0) close(sdRef->sockfd);
+ free(sdRef);
+ }
+
+
+DNSServiceErrorType DNSServiceResolve
+ (
+ DNSServiceRef *sdRef,
+ const DNSServiceFlags flags,
+ const uint32_t interfaceIndex,
+ const char *name,
+ const char *regtype,
+ const char *domain,
+ const DNSServiceResolveReply callBack,
+ void *context
+ )
+ {
+ char *msg = NULL, *ptr;
+ int len;
+ ipc_msg_hdr *hdr;
+ DNSServiceRef sdr;
+ DNSServiceErrorType err;
+
+ if (!sdRef) return kDNSServiceErr_BadParam;
+ *sdRef = NULL;
+
+ // calculate total message length
+ len = sizeof(flags);
+ len += sizeof(interfaceIndex);
+ len += strlen(name) + 1;
+ len += strlen(regtype) + 1;
+ len += strlen(domain) + 1;
+
+ hdr = create_hdr(resolve_request, &len, &ptr, 1);
+ if (!hdr) goto error;
+ msg = (void *)hdr;
+
+ put_flags(flags, &ptr);
+ put_long(interfaceIndex, &ptr);
+ put_string(name, &ptr);
+ put_string(regtype, &ptr);
+ put_string(domain, &ptr);
+
+ sdr = connect_to_server();
+ if (!sdr) goto error;
+ err = deliver_request(msg, sdr, 1);
+ if (err)
+ {
+ DNSServiceRefDeallocate(sdr);
+ return err;
+ }
+ sdr->op = resolve_request;
+ sdr->process_reply = handle_resolve_response;
+ sdr->app_callback = callBack;
+ sdr->app_context = context;
+ *sdRef = sdr;
+
+ return err;
+
+error:
+ if (msg) free(msg);
+ if (*sdRef) { free(*sdRef); *sdRef = NULL; }
+ return kDNSServiceErr_Unknown;
+ }
+
+
+static void handle_resolve_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data)
+ {
+ DNSServiceFlags flags;
+ char fullname[kDNSServiceMaxDomainName];
+ char target[kDNSServiceMaxDomainName];
+ uint16_t port, txtlen;
+ uint32_t ifi;
+ DNSServiceErrorType err;
+ char *txtrecord;
+
+ (void)hdr; //unused
+
+ flags = get_flags(&data);
+ ifi = get_long(&data);
+ err = get_error_code(&data);
+ get_string(&data, fullname, kDNSServiceMaxDomainName);
+ get_string(&data, target, kDNSServiceMaxDomainName);
+ port = get_short(&data);
+ txtlen = get_short(&data);
+ txtrecord = get_rdata(&data, txtlen);
+
+ ((DNSServiceResolveReply)sdr->app_callback)(sdr, flags, ifi, err, fullname, target, port, txtlen, txtrecord, sdr->app_context);
+ }
+
+
+
+
+DNSServiceErrorType DNSServiceQueryRecord
+(
+ DNSServiceRef *sdRef,
+ const DNSServiceFlags flags,
+ const uint32_t interfaceIndex,
+ const char *name,
+ const uint16_t rrtype,
+ const uint16_t rrclass,
+ const DNSServiceQueryRecordReply callBack,
+ void *context
+ )
+ {
+ char *msg = NULL, *ptr;
+ int len;
+ ipc_msg_hdr *hdr;
+ DNSServiceRef sdr;
+ DNSServiceErrorType err;
+
+ if (!sdRef) return kDNSServiceErr_BadParam;
+ *sdRef = NULL;
+
+ if (!name) name = "\0";
+
+ // calculate total message length
+ len = sizeof(flags);
+ len += sizeof(uint32_t); //interfaceIndex
+ len += strlen(name) + 1;
+ len += 2 * sizeof(uint16_t); // rrtype, rrclass
+
+ hdr = create_hdr(query_request, &len, &ptr, 1);
+ if (!hdr) goto error;
+ msg = (void *)hdr;
+
+ put_flags(flags, &ptr);
+ put_long(interfaceIndex, &ptr);
+ put_string(name, &ptr);
+ put_short(rrtype, &ptr);
+ put_short(rrclass, &ptr);
+
+ sdr = connect_to_server();
+ if (!sdr) goto error;
+ err = deliver_request(msg, sdr, 1);
+ if (err)
+ {
+ DNSServiceRefDeallocate(sdr);
+ return err;
+ }
+
+ sdr->op = query_request;
+ sdr->process_reply = handle_query_response;
+ sdr->app_callback = callBack;
+ sdr->app_context = context;
+ *sdRef = sdr;
+ return err;
+
+error:
+ if (msg) free(msg);
+ if (*sdRef) { free(*sdRef); *sdRef = NULL; }
+ return kDNSServiceErr_Unknown;
+ }
+
+
+static void handle_query_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data)
+ {
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex, ttl;
+ DNSServiceErrorType errorCode;
+ char name[256];
+ uint16_t rrtype, rrclass, rdlen;
+ char *rdata;
+ (void)hdr;//Unused
+
+ flags = get_flags(&data);
+ interfaceIndex = get_long(&data);
+ errorCode = get_error_code(&data);
+ (get_string(&data, name, 256) < 0);
+ rrtype = get_short(&data);
+ rrclass = get_short(&data);
+ rdlen = get_short(&data);
+ rdata = get_rdata(&data, rdlen);
+ ttl = get_long(&data);
+ if (!rdata) return;
+ ((DNSServiceQueryRecordReply)sdr->app_callback)(sdr, flags, interfaceIndex, errorCode, name, rrtype, rrclass,
+ rdlen, rdata, ttl, sdr->app_context);
+ return;
+ }
+
+DNSServiceErrorType DNSServiceBrowse
+(
+ DNSServiceRef *sdRef,
+ const DNSServiceFlags flags,
+ const uint32_t interfaceIndex,
+ const char *regtype,
+ const char *domain,
+ const DNSServiceBrowseReply callBack,
+ void *context
+ )
+ {
+ char *msg = NULL, *ptr;
+ int len;
+ ipc_msg_hdr *hdr;
+ DNSServiceRef sdr;
+ DNSServiceErrorType err;
+
+ if (!sdRef) return kDNSServiceErr_BadParam;
+ *sdRef = NULL;
+
+ if (!domain) domain = "";
+
+ len = sizeof(flags);
+ len += sizeof(interfaceIndex);
+ len += strlen(regtype) + 1;
+ len += strlen(domain) + 1;
+
+ hdr = create_hdr(browse_request, &len, &ptr, 1);
+ if (!hdr) goto error;
+ msg = (char *)hdr;
+ put_flags(flags, &ptr);
+ put_long(interfaceIndex, &ptr);
+ put_string(regtype, &ptr);
+ put_string(domain, &ptr);
+
+ sdr = connect_to_server();
+ if (!sdr) goto error;
+ err = deliver_request(msg, sdr, 1);
+ if (err)
+ {
+ DNSServiceRefDeallocate(sdr);
+ return err;
+ }
+ sdr->op = browse_request;
+ sdr->process_reply = handle_browse_response;
+ sdr->app_callback = callBack;
+ sdr->app_context = context;
+ *sdRef = sdr;
+ return err;
+
+error:
+ if (msg) free(msg);
+ if (*sdRef) { free(*sdRef); *sdRef = NULL; }
+ return kDNSServiceErr_Unknown;
+ }
+
+
+
+
+static void handle_browse_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data)
+ {
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex;
+ DNSServiceErrorType errorCode;
+ char replyName[256], replyType[256], replyDomain[256];
+ (void)hdr;//Unused
+
+ flags = get_flags(&data);
+ interfaceIndex = get_long(&data);
+ errorCode = get_error_code(&data);
+ get_string(&data, replyName, 256);
+ get_string(&data, replyType, 256);
+ get_string(&data, replyDomain, 256);
+ ((DNSServiceBrowseReply)sdr->app_callback)(sdr, flags, interfaceIndex, errorCode, replyName, replyType, replyDomain, sdr->app_context);
+ }
+
+
+DNSServiceErrorType DNSServiceRegister
+ (
+ DNSServiceRef *sdRef,
+ const DNSServiceFlags flags,
+ const uint32_t interfaceIndex,
+ const char *name,
+ const char *regtype,
+ const char *domain,
+ const char *host,
+ const uint16_t port,
+ const uint16_t txtLen,
+ const void *txtRecord,
+ const DNSServiceRegisterReply callBack,
+ void *context
+ )
+ {
+ char *msg = NULL, *ptr;
+ int len;
+ ipc_msg_hdr *hdr;
+ DNSServiceRef sdr;
+ DNSServiceErrorType err;
+
+ if (!sdRef) return kDNSServiceErr_BadParam;
+ *sdRef = NULL;
+
+ if (!name) name = "";
+ if (!regtype) return kDNSServiceErr_BadParam;
+ if (!domain) domain = "";
+ if (!host) host = "";
+ if (!txtRecord) (char *)txtRecord = "";
+
+ // auto-name must also have auto-rename
+ if (!name[0] && (flags & kDNSServiceFlagsNoAutoRename))
+ return kDNSServiceErr_BadParam;
+
+ // no callback must have auto-name
+ if (!callBack && name[0]) return kDNSServiceErr_BadParam;
+
+ len = sizeof(DNSServiceFlags);
+ len += sizeof(uint32_t); // interfaceIndex
+ len += strlen(name) + strlen(regtype) + strlen(domain) + strlen(host) + 4;
+ len += 2 * sizeof(uint16_t); // port, txtLen
+ len += txtLen;
+
+ hdr = create_hdr(reg_service_request, &len, &ptr, 1);
+ if (!hdr) goto error;
+ if (!callBack) hdr->flags |= IPC_FLAGS_NOREPLY;
+ msg = (char *)hdr;
+ put_flags(flags, &ptr);
+ put_long(interfaceIndex, &ptr);
+ put_string(name, &ptr);
+ put_string(regtype, &ptr);
+ put_string(domain, &ptr);
+ put_string(host, &ptr);
+ put_short(port, &ptr);
+ put_short(txtLen, &ptr);
+ put_rdata(txtLen, txtRecord, &ptr);
+
+ sdr = connect_to_server();
+ if (!sdr) goto error;
+ err = deliver_request(msg, sdr, 1);
+ if (err)
+ {
+ DNSServiceRefDeallocate(sdr);
+ return err;
+ }
+
+ sdr->op = reg_service_request;
+ sdr->process_reply = callBack ? handle_regservice_response : NULL;
+ sdr->app_callback = callBack;
+ sdr->app_context = context;
+ *sdRef = sdr;
+
+ return err;
+
+error:
+ if (msg) free(msg);
+ if (*sdRef) { free(*sdRef); *sdRef = NULL; }
+ return kDNSServiceErr_Unknown;
+ }
+
+
+static void handle_regservice_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data)
+ {
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex;
+ DNSServiceErrorType errorCode;
+ char name[256], regtype[256], domain[256];
+ (void)hdr;//Unused
+
+ flags = get_flags(&data);
+ interfaceIndex = get_long(&data);
+ errorCode = get_error_code(&data);
+ get_string(&data, name, 256);
+ get_string(&data, regtype, 256);
+ get_string(&data, domain, 256);
+ ((DNSServiceRegisterReply)sdr->app_callback)(sdr, flags, errorCode, name, regtype, domain, sdr->app_context);
+ }
+
+DNSServiceErrorType DNSServiceEnumerateDomains
+(
+ DNSServiceRef *sdRef,
+ const DNSServiceFlags flags,
+ const uint32_t interfaceIndex,
+ const DNSServiceDomainEnumReply callBack,
+ void *context
+ )
+ {
+ char *msg = NULL, *ptr;
+ int len;
+ ipc_msg_hdr *hdr;
+ DNSServiceRef sdr;
+ DNSServiceErrorType err;
+
+
+ if (!sdRef) return kDNSServiceErr_BadParam;
+ *sdRef = NULL;
+
+ len = sizeof(DNSServiceFlags);
+ len += sizeof(uint32_t);
+
+ hdr = create_hdr(enumeration_request, &len, &ptr, 1);
+ if (!hdr) goto error;
+ msg = (void *)hdr;
+
+ put_flags(flags, &ptr);
+ put_long(interfaceIndex, &ptr);
+
+ sdr = connect_to_server();
+ if (!sdr) goto error;
+ err = deliver_request(msg, sdr, 1);
+ if (err)
+ {
+ DNSServiceRefDeallocate(sdr);
+ return err;
+ }
+
+ sdr->op = enumeration_request;
+ sdr->process_reply = handle_enumeration_response;
+ sdr->app_callback = callBack;
+ sdr->app_context = context;
+ *sdRef = sdr;
+ return err;
+
+error:
+ if (msg) free(msg);
+ if (*sdRef) { free(*sdRef); *sdRef = NULL; }
+ return kDNSServiceErr_Unknown;
+ }
+
+
+static void handle_enumeration_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data)
+ {
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex;
+ DNSServiceErrorType err;
+ char domain[256];
+ (void)hdr;//Unused
+
+ flags = get_flags(&data);
+ interfaceIndex = get_long(&data);
+ err = get_error_code(&data);
+ get_string(&data, domain, 256);
+ ((DNSServiceDomainEnumReply)sdr->app_callback)(sdr, flags, interfaceIndex, err, domain, sdr->app_context);
+ }
+
+
+DNSServiceErrorType DNSServiceCreateConnection(DNSServiceRef *sdRef)
+ {
+ if (!sdRef) return kDNSServiceErr_BadParam;
+ *sdRef = connect_to_server();
+ if (!*sdRef)
+ return kDNSServiceErr_Unknown;
+ (*sdRef)->op = connection;
+ (*sdRef)->process_reply = handle_regrecord_response;
+ return 0;
+ }
+
+
+
+static void handle_regrecord_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data)
+ {
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex;
+ DNSServiceErrorType errorCode;
+ DNSRecordRef rref = hdr->client_context.context;
+
+ if (sdr->op != connection)
+ {
+ rref->app_callback(rref->sdr, rref, 0, kDNSServiceErr_Unknown, rref->app_context);
+ return;
+ }
+ flags = get_flags(&data);
+ interfaceIndex = get_long(&data);
+ errorCode = get_error_code(&data);
+
+ rref->app_callback(rref->sdr, rref, flags, errorCode, rref->app_context);
+ }
+
+DNSServiceErrorType DNSServiceRegisterRecord
+(
+ const DNSServiceRef sdRef,
+ DNSRecordRef *RecordRef,
+ const DNSServiceFlags flags,
+ const uint32_t interfaceIndex,
+ const char *fullname,
+ const uint16_t rrtype,
+ const uint16_t rrclass,
+ const uint16_t rdlen,
+ const void *rdata,
+ const uint32_t ttl,
+ const DNSServiceRegisterRecordReply callBack,
+ void *context
+ )
+ {
+ char *msg = NULL, *ptr;
+ int len;
+ ipc_msg_hdr *hdr = NULL;
+ DNSServiceRef tmp = NULL;
+ DNSRecordRef rref = NULL;
+
+ if (!sdRef || sdRef->op != connection || sdRef->sockfd < 0)
+ return kDNSServiceErr_BadReference;
+ *RecordRef = NULL;
+
+ len = sizeof(DNSServiceFlags);
+ len += 2 * sizeof(uint32_t); // interfaceIndex, ttl
+ len += 3 * sizeof(uint16_t); // rrtype, rrclass, rdlen
+ len += strlen(fullname) + 1;
+ len += rdlen;
+
+ hdr = create_hdr(reg_record_request, &len, &ptr, 0);
+ if (!hdr) goto error;
+ msg = (char *)hdr;
+ put_flags(flags, &ptr);
+ put_long(interfaceIndex, &ptr);
+ put_string(fullname, &ptr);
+ put_short(rrtype, &ptr);
+ put_short(rrclass, &ptr);
+ put_short(rdlen, &ptr);
+ put_rdata(rdlen, rdata, &ptr);
+ put_long(ttl, &ptr);
+
+ rref = malloc(sizeof(_DNSRecordRef_t));
+ if (!rref) goto error;
+ rref->app_context = context;
+ rref->app_callback = callBack;
+ rref->record_index = sdRef->max_index++;
+ rref->sdr = sdRef;
+ *RecordRef = rref;
+ hdr->client_context.context = rref;
+ hdr->reg_index = rref->record_index;
+
+ return deliver_request(msg, sdRef, 0);
+
+error:
+ if (rref) free(rref);
+ if (tmp) free(tmp);
+ if (hdr) free(hdr);
+ return kDNSServiceErr_Unknown;
+ }
+
+//sdRef returned by DNSServiceRegister()
+DNSServiceErrorType DNSServiceAddRecord
+ (
+ const DNSServiceRef sdRef,
+ DNSRecordRef *RecordRef,
+ const DNSServiceFlags flags,
+ const uint16_t rrtype,
+ const uint16_t rdlen,
+ const void *rdata,
+ const uint32_t ttl
+ )
+ {
+ ipc_msg_hdr *hdr;
+ int len = 0;
+ char *ptr;
+ DNSRecordRef rref;
+
+ if (!sdRef || (sdRef->op != reg_service_request) || !RecordRef)
+ return kDNSServiceErr_BadReference;
+ *RecordRef = NULL;
+
+ len += 2 * sizeof(uint16_t); //rrtype, rdlen
+ len += rdlen;
+ len += sizeof(uint32_t);
+ len += sizeof(DNSServiceFlags);
+
+ hdr = create_hdr(add_record_request, &len, &ptr, 0);
+ if (!hdr) return kDNSServiceErr_Unknown;
+ put_flags(flags, &ptr);
+ put_short(rrtype, &ptr);
+ put_short(rdlen, &ptr);
+ put_rdata(rdlen, rdata, &ptr);
+ put_long(ttl, &ptr);
+
+ rref = malloc(sizeof(_DNSRecordRef_t));
+ if (!rref) goto error;
+ rref->app_context = NULL;
+ rref->app_callback = NULL;
+ rref->record_index = sdRef->max_index++;
+ rref->sdr = sdRef;
+ *RecordRef = rref;
+ hdr->client_context.context = rref;
+ hdr->reg_index = rref->record_index;
+ return deliver_request((char *)hdr, sdRef, 0);
+
+error:
+ if (hdr) free(hdr);
+ if (rref) free(rref);
+ if (*RecordRef) *RecordRef = NULL;
+ return kDNSServiceErr_Unknown;
+}
+
+
+//DNSRecordRef returned by DNSServiceRegisterRecord or DNSServiceAddRecord
+DNSServiceErrorType DNSServiceUpdateRecord
+ (
+ const DNSServiceRef sdRef,
+ DNSRecordRef RecordRef,
+ const DNSServiceFlags flags,
+ const uint16_t rdlen,
+ const void *rdata,
+ const uint32_t ttl
+ )
+ {
+ ipc_msg_hdr *hdr;
+ int len = 0;
+ char *ptr;
+
+ if (!sdRef || !RecordRef || !sdRef->max_index)
+ return kDNSServiceErr_BadReference;
+
+ len += sizeof(uint16_t);
+ len += rdlen;
+ len += sizeof(uint32_t);
+ len += sizeof(DNSServiceFlags);
+
+ hdr = create_hdr(update_record_request, &len, &ptr, 0);
+ if (!hdr) return kDNSServiceErr_Unknown;
+ hdr->reg_index = RecordRef ? RecordRef->record_index : TXT_RECORD_INDEX;
+ put_flags(flags, &ptr);
+ put_short(rdlen, &ptr);
+ put_rdata(rdlen, rdata, &ptr);
+ put_long(ttl, &ptr);
+ return deliver_request((char *)hdr, sdRef, 0);
+ }
+
+
+
+DNSServiceErrorType DNSServiceRemoveRecord
+(
+ const DNSServiceRef sdRef,
+ const DNSRecordRef RecordRef,
+ const DNSServiceFlags flags
+ )
+ {
+ ipc_msg_hdr *hdr;
+ int len = 0;
+ char *ptr;
+ DNSServiceErrorType err;
+
+ if (!sdRef || !RecordRef || !sdRef->max_index)
+ return kDNSServiceErr_BadReference;
+
+ len += sizeof(flags);
+ hdr = create_hdr(remove_record_request, &len, &ptr, 0);
+ if (!hdr) return kDNSServiceErr_Unknown;
+ hdr->reg_index = RecordRef->record_index;
+ put_flags(flags, &ptr);
+ err = deliver_request((char *)hdr, sdRef, 0);
+ if (!err) free(RecordRef);
+ return err;
+ }
+
+
+void DNSServiceReconfirmRecord
+(
+ const DNSServiceFlags flags,
+ const uint32_t interfaceIndex,
+ const char *fullname,
+ const uint16_t rrtype,
+ const uint16_t rrclass,
+ const uint16_t rdlen,
+ const void *rdata
+ )
+ {
+ char *ptr;
+ int len;
+ ipc_msg_hdr *hdr;
+ DNSServiceRef tmp;
+
+ len = sizeof(DNSServiceFlags);
+ len += sizeof(uint32_t);
+ len += strlen(fullname) + 1;
+ len += 3 * sizeof(uint16_t);
+ len += rdlen;
+ tmp = connect_to_server();
+ if (!tmp) return;
+ hdr = create_hdr(reconfirm_record_request, &len, &ptr, 1);
+ if (!hdr) return;
+
+ put_flags(flags, &ptr);
+ put_long(interfaceIndex, &ptr);
+ put_string(fullname, &ptr);
+ put_short(rrtype, &ptr);
+ put_short(rrclass, &ptr);
+ put_short(rdlen, &ptr);
+ put_rdata(rdlen, rdata, &ptr);
+ my_write(tmp->sockfd, (char *)hdr, len);
+ DNSServiceRefDeallocate(tmp);
+ }
+
+
+int DNSServiceConstructFullName
+ (
+ char *fullName,
+ const char *service, /* may be NULL */
+ const char *regtype,
+ const char *domain
+ )
+ {
+ int len;
+ u_char c;
+ char *fn = fullName;
+ const char *s = service;
+ const char *r = regtype;
+ const char *d = domain;
+
+ if (service)
+ {
+ while(*s)
+ {
+ c = *s++;
+ if (c == '.' || (c == '\\')) *fn++ = '\\'; // escape dot and backslash literals
+ else if (c <= ' ') // escape non-printable characters
+ {
+ *fn++ = '\\';
+ *fn++ = (char) ('0' + (c / 100));
+ *fn++ = (char) ('0' + (c / 10) % 10);
+ c = (u_char)('0' + (c % 10));
+ }
+ *fn++ = c;
+ }
+ *fn++ = '.';
+ }
+
+ if (!regtype) return -1;
+ len = strlen(regtype);
+ if (domain_ends_in_dot(regtype)) len--;
+ if (len < 4) return -1; // regtype must end in _udp or _tcp
+ if (strncmp((regtype + len - 4), "_tcp", 4) && strncmp((regtype + len - 4), "_udp", 4)) return -1;
+ while(*r)
+ *fn++ = *r++;
+ if (!domain_ends_in_dot(regtype)) *fn++ = '.';
+
+ if (!domain) return -1;
+ len = strlen(domain);
+ if (!len) return -1;
+ while(*d)
+ *fn++ = *d++;
+ if (!domain_ends_in_dot(domain)) *fn++ = '.';
+ *fn = '\0';
+ return 0;
+ }
+
+static int domain_ends_in_dot(const char *dom)
+ {
+ while(*dom && *(dom + 1))
+ {
+ if (*dom == '\\') // advance past escaped byte sequence
+ {
+ if (*(dom + 1) >= '0' && *(dom + 1) <= '9') dom += 4;
+ else dom += 2;
+ }
+ else dom++; // else read one character
+ }
+ return (*dom == '.');
+ }
+
+
+
+ // return a connected service ref (deallocate with DNSServiceRefDeallocate)
+static DNSServiceRef connect_to_server(void)
+ {
+ struct sockaddr_un saddr;
+ DNSServiceRef sdr;
+
+ sdr = malloc(sizeof(_DNSServiceRef_t));
+ if (!sdr) return NULL;
+
+ if ((sdr->sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
+ {
+ free(sdr);
+ return NULL;
+ }
+
+ saddr.sun_family = AF_LOCAL;
+ strcpy(saddr.sun_path, MDNS_UDS_SERVERPATH);
+ if (connect(sdr->sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
+ {
+ free(sdr);
+ return NULL;
+ }
+ return sdr;
+ }
+
+
+
+
+int my_write(int sd, char *buf, int len)
+ {
+ if (send(sd, buf, len, MSG_WAITALL) != len) return -1;
+ return 0;
+ }
+
+
+// read len bytes. return 0 on success, -1 on error
+int my_read(int sd, char *buf, int len)
+ {
+ if (recv(sd, buf, len, MSG_WAITALL) != len) return -1;
+ return 0;
+ }
+
+
+DNSServiceErrorType deliver_request(void *msg, DNSServiceRef sdr, int reuse_sd)
+ {
+ ipc_msg_hdr *hdr = msg;
+ mode_t mask;
+ struct sockaddr_un caddr, daddr; // (client and daemon address structs)
+ char *path = NULL;
+ int listenfd = -1, errsd = -1, len;
+ DNSServiceErrorType err = kDNSServiceErr_Unknown;
+
+ if (!hdr || sdr->sockfd < 0) return kDNSServiceErr_Unknown;
+
+ if (!reuse_sd)
+ {
+ // setup temporary error socket
+ if ((listenfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
+ goto cleanup;
+
+ bzero(&caddr, sizeof(caddr));
+ caddr.sun_family = AF_LOCAL;
+ caddr.sun_len = sizeof(struct sockaddr_un);
+ path = (char *)msg + sizeof(ipc_msg_hdr);
+ strcpy(caddr.sun_path, path);
+ mask = umask(0);
+ if (bind(listenfd, (struct sockaddr *)&caddr, sizeof(caddr)) < 0)
+ {
+ umask(mask);
+ goto cleanup;
+ }
+ umask(mask);
+ listen(listenfd, 1);
+ }
+
+ if (my_write(sdr->sockfd, msg, hdr->datalen + sizeof(ipc_msg_hdr)) < 0)
+ goto cleanup;
+ free(msg);
+ msg = NULL;
+
+ if (reuse_sd) errsd = sdr->sockfd;
+ else
+ {
+ len = sizeof(daddr);
+ errsd = accept(listenfd, (struct sockaddr *)&daddr, &len);
+ if (errsd < 0) goto cleanup;
+ }
+
+ len = recv(errsd, &err, sizeof(err), MSG_WAITALL);
+ if (len != sizeof(err))
+ {
+ err = kDNSServiceErr_Unknown;
+ }
+cleanup:
+ if (!reuse_sd && listenfd > 0) close(listenfd);
+ if (!reuse_sd && errsd > 0) close(errsd);
+ if (!reuse_sd && path) unlink(path);
+ if (msg) free(msg);
+ return err;
+ }
+
+
+
+/* create_hdr
+ *
+ * allocate and initialize an ipc message header. value of len should initially be the
+ * length of the data, and is set to the value of the data plus the header. data_start
+ * is set to point to the beginning of the data section. reuse_socket should be non-zero
+ * for calls that can receive an immediate error return value on their primary socket.
+ * if zero, the path to a control socket is appended at the beginning of the message buffer.
+ * data_start is set past this string.
+ */
+
+static ipc_msg_hdr *create_hdr(int op, int *len, char **data_start, int reuse_socket)
+ {
+ char *msg = NULL;
+ ipc_msg_hdr *hdr;
+ int datalen;
+ char ctrl_path[256];
+ struct timeval time;
+
+ if (!reuse_socket)
+ {
+ if (gettimeofday(&time, NULL) < 0) return NULL;
+ sprintf(ctrl_path, "%s%d-%.3x-%.6u", CTL_PATH_PREFIX, (int)getpid(),
+ time.tv_sec & 0xFFF, time.tv_usec);
+
+ *len += strlen(ctrl_path) + 1;
+ }
+
+
+ datalen = *len;
+ *len += sizeof(ipc_msg_hdr);
+
+ // write message to buffer
+ msg = malloc(*len);
+ if (!msg) return NULL;
+
+ bzero(msg, *len);
+ hdr = (void *)msg;
+ hdr->datalen = datalen;
+ hdr->version = VERSION;
+ hdr->op.request_op = op;
+ if (reuse_socket) hdr->flags |= IPC_FLAGS_REUSE_SOCKET;
+ *data_start = msg + sizeof(ipc_msg_hdr);
+ if (!reuse_socket) put_string(ctrl_path, data_start);
+ return hdr;
+ }
--- /dev/null
+/*
+ * Copyright (c) 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: dnssd_ipc.c,v $
+Revision 1.7 2003/08/12 19:56:25 cheshire
+Update to APSL 2.0
+
+ */
+
+#include "dnssd_ipc.h"
+
+void put_flags(const DNSServiceFlags flags, char **ptr)
+ {
+ memcpy(*ptr, &flags, sizeof(DNSServiceFlags));
+ *ptr += sizeof(flags);
+ }
+
+DNSServiceFlags get_flags(char **ptr)
+ {
+ DNSServiceFlags flags;
+
+ flags = *(DNSServiceFlags *)*ptr;
+ *ptr += sizeof(DNSServiceFlags);
+ return flags;
+ }
+
+void put_long(const uint32_t l, char **ptr)
+ {
+
+ *(uint32_t *)(*ptr) = l;
+ *ptr += sizeof(uint32_t);
+ }
+
+uint32_t get_long(char **ptr)
+ {
+ uint32_t l;
+
+ l = *(uint32_t *)(*ptr);
+ *ptr += sizeof(uint32_t);
+ return l;
+ }
+
+void put_error_code(const DNSServiceErrorType error, char **ptr)
+ {
+ memcpy(*ptr, &error, sizeof(error));
+ *ptr += sizeof(DNSServiceErrorType);
+ }
+
+DNSServiceErrorType get_error_code(char **ptr)
+ {
+ DNSServiceErrorType error;
+
+ error = *(DNSServiceErrorType *)(*ptr);
+ *ptr += sizeof(DNSServiceErrorType);
+ return error;
+ }
+
+void put_short(const uint16_t s, char **ptr)
+ {
+ *(uint16_t *)(*ptr) = s;
+ *ptr += sizeof(uint16_t);
+ }
+
+uint16_t get_short(char **ptr)
+ {
+ uint16_t s;
+
+ s = *(uint16_t *)(*ptr);
+ *ptr += sizeof(uint16_t);
+ return s;
+ }
+
+
+int put_string(const char *str, char **ptr)
+ {
+ if (!str) str = "";
+ strcpy(*ptr, str);
+ *ptr += strlen(str) + 1;
+ return 0;
+ }
+
+// !!!KRS we don't properly handle the case where the string is longer than the buffer!!!
+int get_string(char **ptr, char *buffer, int buflen)
+ {
+ int overrun;
+
+ overrun = (int)strlen(*ptr) < buflen ? 0 : -1;
+ strncpy(buffer, *ptr, buflen - 1);
+ buffer[buflen - 1] = '\0';
+ *ptr += strlen(buffer) + 1;
+ return overrun;
+ }
+
+void put_rdata(const int rdlen, const char *rdata, char **ptr)
+ {
+ memcpy(*ptr, rdata, rdlen);
+ *ptr += rdlen;
+ }
+
+char *get_rdata(char **ptr, int rdlen)
+ {
+ char *rd;
+
+ rd = *ptr;
+ *ptr += rdlen;
+ return rd;
+ }
+
+
+
+
+
+
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 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: dnssd_ipc.h,v $
+Revision 1.6 2003/08/12 19:56:25 cheshire
+Update to APSL 2.0
+
+ */
+
+#ifndef DNSSD_IPC_H
+#define DNSSD_IPC_H
+
+#include "dns_sd.h"
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/un.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+//#define UDSDEBUG // verbose debug output
+
+// General UDS constants
+#define MDNS_UDS_SERVERPATH "/var/run/mDNSResponder"
+#define LISTENQ 100
+#define TXT_RECORD_INDEX -1 // record index for default text record
+#define MAX_CTLPATH 256 // longest legal control path length
+
+// IPC data encoding constants and types
+#define VERSION 1
+#define IPC_FLAGS_NOREPLY 1 // set flag if no asynchronous replies are to be sent to client
+#define IPC_FLAGS_REUSE_SOCKET 2 // set flag if synchronous errors are to be sent via the primary socket
+ // (if not set, first string in message buffer must be path to error socket
+
+
+typedef enum
+ {
+ connection = 1, // connected socket via DNSServiceConnect()
+ reg_record_request, // reg/remove record only valid for connected sockets
+ remove_record_request,
+ enumeration_request,
+ reg_service_request,
+ browse_request,
+ resolve_request,
+ query_request,
+ reconfirm_record_request,
+ add_record_request,
+ update_record_request
+ } request_op_t;
+
+typedef enum
+ {
+ enumeration_reply = 64,
+ reg_service_reply,
+ browse_reply,
+ resolve_reply,
+ query_reply,
+ reg_record_reply
+ } reply_op_t;
+
+
+typedef struct ipc_msg_hdr_struct ipc_msg_hdr;
+
+
+// client stub callback to process message from server and deliver results to
+// client application
+
+typedef void (*process_reply_callback)
+ (
+ DNSServiceRef sdr,
+ ipc_msg_hdr *hdr,
+ char *msg
+ );
+
+// allow 64-bit client to interoperate w/ 32-bit daemon
+typedef union
+ {
+ void *context;
+ uint32_t ptr64[2];
+ } client_context_t;
+
+
+typedef struct ipc_msg_hdr_struct
+ {
+ uint32_t version;
+ uint32_t datalen;
+ uint32_t flags;
+ union
+ {
+ request_op_t request_op;
+ reply_op_t reply_op;
+ } op;
+ client_context_t client_context; // context passed from client, returned by server in corresponding reply
+ int reg_index; // identifier for a record registered via DNSServiceRegisterRecord() on a
+ // socket connected by DNSServiceConnect(). Must be unique in the scope of the connection, such that and
+ // index/socket pair uniquely identifies a record. (Used to select records for removal by DNSServiceRemoveRecord())
+ } ipc_msg_hdr_struct;
+
+
+
+
+// routines to write to and extract data from message buffers.
+// caller responsible for bounds checking.
+// ptr is the address of the pointer to the start of the field.
+// it is advanced to point to the next field, or the end of the message
+
+
+void put_flags(const DNSServiceFlags flags, char **ptr);
+DNSServiceFlags get_flags(char **ptr);
+
+void put_long(const uint32_t l, char **ptr);
+uint32_t get_long(char **ptr);
+
+void put_error_code(const DNSServiceErrorType, char **ptr);
+DNSServiceErrorType get_error_code(char **ptr);
+
+int put_string(const char *str, char **ptr);
+int get_string(char **ptr, char *buffer, int buflen);
+
+void put_rdata(const int rdlen, const char *rdata, char **ptr);
+char *get_rdata(char **ptr, int rdlen); // return value is rdata pointed to by *ptr -
+ // rdata is not copied from buffer.
+
+void put_short(uint16_t s, char **ptr);
+uint16_t get_short(char **ptr);
+
+
+
+#endif // DNSSD_IPC_H
+
+
+
+
+
+
+
+
+
+
+
--- /dev/null
+/*
+ * 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: mDNSMacOSX.h,v $
+Revision 1.21 2003/08/19 22:20:00 cheshire
+<rdar://problem/3376721> Don't use IPv6 on interfaces that have a routable IPv4 address configured
+More minor refinements
+
+Revision 1.20 2003/08/19 05:39:43 cheshire
+<rdar://problem/3380097> SIGINFO dump should include resolves started by DNSServiceQueryRecord
+
+Revision 1.19 2003/08/19 05:36:45 cheshire
+Add missing "extern" directives
+
+Revision 1.18 2003/08/19 03:04:43 cheshire
+<rdar://problem/3376721> Don't use IPv6 on interfaces that have a routable IPv4 address configured
+
+Revision 1.17 2003/08/12 19:56:25 cheshire
+Update to APSL 2.0
+
+Revision 1.16 2003/08/08 18:36:04 cheshire
+<rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug
+
+Revision 1.15 2003/08/05 00:32:28 cheshire
+<rdar://problem/3326712> Time to turn off MACOSX_MDNS_MALLOC_DEBUGGING
+
+Revision 1.14 2003/07/20 03:38:51 ksekar
+Bug #: 3320722
+Completed support for Unix-domain socket based API.
+
+Revision 1.13 2003/07/18 00:30:00 cheshire
+<rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead
+
+Revision 1.12 2003/07/12 03:15:20 cheshire
+<rdar://problem/3324848> After SCDynamicStore notification, mDNSResponder updates
+m->hostlabel even if user hasn't actually actually changed their dot-local hostname
+
+Revision 1.11 2003/07/02 21:19:51 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.10 2003/06/25 23:42:19 ksekar
+Bug #: <rdar://problem/3249292>: Feature: New Rendezvous 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.
+
+Revision 1.9 2003/06/10 01:14:11 cheshire
+<rdar://problem/3286004> New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call
+
+Revision 1.8 2003/05/14 07:08:37 cheshire
+<rdar://problem/3159272> 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.7 2003/04/26 02:39:24 cheshire
+Remove extern void LogMsg(const char *format, ...);
+
+Revision 1.6 2003/03/05 21:59:56 cheshire
+Bug #: 3189097 Additional debugging code in mDNSResponder
+
+Revision 1.5 2003/03/05 01:50:38 cheshire
+Bug #: 3189097 Additional debugging code in mDNSResponder
+
+Revision 1.4 2003/02/21 01:54:10 cheshire
+Bug #: 3099194 mDNSResponder needs performance improvements
+Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt")
+
+Revision 1.3 2002/09/21 20:44:51 zarzycki
+Added APSL info
+
+Revision 1.2 2002/09/19 04:20:44 cheshire
+Remove high-ascii characters that confuse some systems
+
+Revision 1.1 2002/09/17 01:04:09 cheshire
+Defines mDNS_PlatformSupport_struct for OS X
+
+*/
+
+#ifndef __mDNSOSX_h
+#define __mDNSOSX_h
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#include <SystemConfiguration/SystemConfiguration.h>
+#include <IOKit/pwr_mgt/IOPMLib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+typedef struct NetworkInterfaceInfoOSX_struct NetworkInterfaceInfoOSX;
+struct NetworkInterfaceInfoOSX_struct
+ {
+ NetworkInterfaceInfo ifinfo; // MUST be the first element in this structure
+ NetworkInterfaceInfoOSX *next;
+ mDNS *m;
+ mDNSu32 CurrentlyActive; // 0 not active; 1 active; 2 active but TxRx state changed
+ char *ifa_name; // Memory for this is allocated using malloc
+ mDNSu32 scope_id; // interface index / IPv6 scope ID
+ u_short sa_family;
+#if mDNS_AllowPort53
+ int skt53;
+ CFSocketRef cfs53;
+#endif
+ int sktv4;
+ CFSocketRef cfsv4;
+ int sktv6;
+ CFSocketRef cfsv6;
+ };
+
+struct mDNS_PlatformSupport_struct
+ {
+ NetworkInterfaceInfoOSX *InterfaceList;
+ domainlabel userhostlabel;
+ SCDynamicStoreRef Store;
+ CFRunLoopSourceRef StoreRLS;
+ io_connect_t PowerConnection;
+ io_object_t PowerNotifier;
+ CFRunLoopSourceRef PowerRLS;
+ };
+
+extern mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(const mDNS *const m, mDNSu32 index);
+extern mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(const mDNS *const m, mDNSInterfaceID id);
+extern mDNSBool mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring);
+
+extern const char mDNSResponderVersionString[];
+
+// 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()
+#define MACOSX_MDNS_MALLOC_DEBUGGING 0
+
+#if MACOSX_MDNS_MALLOC_DEBUGGING >= 1
+extern void *mallocL(char *msg, unsigned int size);
+extern void freeL(char *msg, void *x);
+#else
+#define mallocL(X,Y) malloc(Y)
+#define freeL(X,Y) free(Y)
+#endif
+
+#if MACOSX_MDNS_MALLOC_DEBUGGING >= 2
+#define LogMalloc LogMsg
+#else
+#define LogMalloc(ARGS...) ((void)0)
+#endif
+
+#define LogAllOperations 0
+
+#if LogAllOperations
+#define LogOperation LogMsg
+#else
+#define LogOperation(ARGS...) ((void)0)
+#endif
+
+#ifdef __cplusplus
+ }
+#endif
+
+// UDS Server <-> daemon crossover routines/globals
+extern mDNS mDNSStorage;
+extern int udsserver_init(void);
+extern int udsserver_add_rl_source(void);
+extern mDNSs32 udsserver_idle(mDNSs32 nextevent); // takes the next scheduled event time, does idle work,
+ // and returns the updated nextevent time
+extern void udsserver_info(void);
+extern void udsserver_handle_configchange(void);
+extern int udsserver_exit(void);
+
+#endif
--- /dev/null
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 38;
+ objects = {
+ 000753D303367C1C0CCA2C71 = {
+ isa = PBXFileReference;
+ path = mDNSMacOSX.h;
+ refType = 4;
+ };
+ 0017390704CC75C30CCA2C71 = {
+ isa = PBXFileReference;
+ path = SampleUDSClient.c;
+ refType = 2;
+ };
+ 0017390804CC75C30CCA2C71 = {
+ fileRef = 0017390704CC75C30CCA2C71;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 0044D34804CC73600CCA2C71 = {
+ buildPhases = (
+ 0044D34904CC73600CCA2C71,
+ 0044D34A04CC73600CCA2C71,
+ 0044D34C04CC73600CCA2C71,
+ 0044D34E04CC73600CCA2C71,
+ );
+ buildSettings = {
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
+ OTHER_CFLAGS = "-no-cpp-precomp -DmDNSResponderVersion=$(MVERS)";
+ OTHER_LDFLAGS = "";
+ OTHER_REZFLAGS = "";
+ PRODUCT_NAME = uds_test;
+ REZ_EXECUTABLE = YES;
+ SECTORDER_FLAGS = "";
+ STRIPFLAGS = "-S";
+ WARNING_CFLAGS = "-W -Wall -Wmissing-prototypes -Wno-four-char-constants -Wno-unknown-pragmas";
+ };
+ dependencies = (
+ );
+ isa = PBXToolTarget;
+ name = "UDS API Test Tool";
+ productName = "UDS API Test Tool";
+ productReference = 0044D34F04CC73600CCA2C71;
+ shouldUseHeadermap = 0;
+ };
+ 0044D34904CC73600CCA2C71 = {
+ buildActionMask = 2147483647;
+ files = (
+ );
+ isa = PBXHeadersBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 0044D34A04CC73600CCA2C71 = {
+ buildActionMask = 2147483647;
+ files = (
+ 0017390804CC75C30CCA2C71,
+ 00DD152B04CC79700CCA2C71,
+ 00DD152C04CC79A50CCA2C71,
+ );
+ isa = PBXSourcesBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 0044D34C04CC73600CCA2C71 = {
+ buildActionMask = 2147483647;
+ files = (
+ );
+ isa = PBXFrameworksBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 0044D34E04CC73600CCA2C71 = {
+ buildActionMask = 2147483647;
+ files = (
+ );
+ isa = PBXRezBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 0044D34F04CC73600CCA2C71 = {
+ isa = PBXExecutableFileReference;
+ path = uds_test;
+ refType = 3;
+ };
+ 004EFB9604CC78130CCA2C71 = {
+ isa = PBXFileReference;
+ path = dnssd_clientstub.c;
+ refType = 2;
+ };
+ 0066920A04CC7AA80CCA2C71 = {
+ isa = PBXTargetDependency;
+ target = 0044D34804CC73600CCA2C71;
+ };
+ 00AD62A3032D799A0CCA2C71 = {
+ buildPhases = (
+ 00AD62A4032D799A0CCA2C71,
+ 00AD62AC032D799A0CCA2C71,
+ 00AD62B3032D799A0CCA2C71,
+ 00AD62B7032D799A0CCA2C71,
+ );
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = "";
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
+ HEADER_SEARCH_PATHS = "\"$(APPLE_INTERNAL_DEVELOPER_DIR)/Headers\"";
+ LIBRARY_SEARCH_PATHS = "";
+ OPTIMIZATION_CFLAGS = "-O0";
+ OTHER_CFLAGS = "-no-cpp-precomp -D__MACOSX__ -DMDNS_DEBUGMSGS=1 -DmDNSResponderVersion=$(MVERS)";
+ OTHER_LDFLAGS = "";
+ OTHER_REZFLAGS = "";
+ PRODUCT_NAME = mDNSResponder.debug;
+ REZ_EXECUTABLE = YES;
+ SECTORDER_FLAGS = "";
+ STRIPFLAGS = "-S";
+ WARNING_CFLAGS = "-W -Wall -Wmissing-prototypes -Wno-four-char-constants -Wno-unknown-pragmas";
+ };
+ dependencies = (
+ );
+ isa = PBXToolTarget;
+ name = "mDNSResponder debug";
+ productName = mDNSResponder;
+ productReference = 00AD62B8032D799A0CCA2C71;
+ shouldUseHeadermap = 1;
+ };
+ 00AD62A4032D799A0CCA2C71 = {
+ buildActionMask = 2147483647;
+ files = (
+ 00AD62A5032D799A0CCA2C71,
+ F5E11B5F04A28126019798ED,
+ F515E29604A37BB701CA296C,
+ F515E29704A37BB801CA296C,
+ F515E29804A37BBB01CA296C,
+ F515E29904A37BBB01CA296C,
+ );
+ isa = PBXHeadersBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 00AD62A5032D799A0CCA2C71 = {
+ fileRef = 6575FBFF022EAFBA00000109;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 00AD62AC032D799A0CCA2C71 = {
+ buildActionMask = 2147483647;
+ files = (
+ 00AD62AD032D799A0CCA2C71,
+ 00AD62AE032D799A0CCA2C71,
+ 00AD62AF032D799A0CCA2C71,
+ 00AD62B0032D799A0CCA2C71,
+ 00AD62B1032D799A0CCA2C71,
+ F5E11B5E04A28126019798ED,
+ F525E72B04AA167A01F1CF4D,
+ );
+ isa = PBXSourcesBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 00AD62AD032D799A0CCA2C71 = {
+ fileRef = 6575FC00022EAFBA00000109;
+ isa = PBXBuildFile;
+ settings = {
+ ATTRIBUTES = (
+ Client,
+ );
+ };
+ };
+ 00AD62AE032D799A0CCA2C71 = {
+ fileRef = 6575FC01022EAFBA00000109;
+ isa = PBXBuildFile;
+ settings = {
+ ATTRIBUTES = (
+ Server,
+ Client,
+ );
+ };
+ };
+ 00AD62AF032D799A0CCA2C71 = {
+ fileRef = 6575FBE9022EAF5A00000109;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 00AD62B0032D799A0CCA2C71 = {
+ fileRef = 6575FBEB022EAF7200000109;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 00AD62B1032D799A0CCA2C71 = {
+ fileRef = 6575FBEC022EAF7200000109;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 00AD62B3032D799A0CCA2C71 = {
+ buildActionMask = 2147483647;
+ files = (
+ 00AD62B4032D799A0CCA2C71,
+ 00AD62B5032D799A0CCA2C71,
+ 00AD62B6032D799A0CCA2C71,
+ );
+ isa = PBXFrameworksBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 00AD62B4032D799A0CCA2C71 = {
+ fileRef = 09AB6884FE841BABC02AAC07;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 00AD62B5032D799A0CCA2C71 = {
+ fileRef = 65713D46025A293200000109;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 00AD62B6032D799A0CCA2C71 = {
+ fileRef = 00CA213D02786FC30CCA2C71;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 00AD62B7032D799A0CCA2C71 = {
+ buildActionMask = 2147483647;
+ files = (
+ );
+ isa = PBXRezBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 00AD62B8032D799A0CCA2C71 = {
+ isa = PBXExecutableFileReference;
+ path = mDNSResponder.debug;
+ refType = 3;
+ };
+ 00AD62BB032D7A0C0CCA2C71 = {
+ buildPhases = (
+ );
+ buildSettings = {
+ OTHER_CFLAGS = "";
+ OTHER_LDFLAGS = "";
+ OTHER_REZFLAGS = "";
+ PRODUCT_NAME = "Build All";
+ SECTORDER_FLAGS = "";
+ };
+ dependencies = (
+ 00AD62BC032D7A160CCA2C71,
+ 00AD62BD032D7A1B0CCA2C71,
+ 00AD62BE032D7A1D0CCA2C71,
+ 0066920A04CC7AA80CCA2C71,
+ );
+ isa = PBXAggregateTarget;
+ name = "Build All";
+ productName = "Build All";
+ shouldUseHeadermap = 0;
+ };
+ 00AD62BC032D7A160CCA2C71 = {
+ isa = PBXTargetDependency;
+ target = 08FB779FFE84155DC02AAC07;
+ };
+ 00AD62BD032D7A1B0CCA2C71 = {
+ isa = PBXTargetDependency;
+ target = 00AD62A3032D799A0CCA2C71;
+ };
+ 00AD62BE032D7A1D0CCA2C71 = {
+ isa = PBXTargetDependency;
+ target = 6575FC1C022EB76000000109;
+ };
+ 00B2AB0C032D7B220CCA2C71 = {
+ buildRules = (
+ );
+ buildSettings = {
+ MVERS = 1;
+ };
+ isa = PBXBuildStyle;
+ name = Development;
+ };
+ 00CA213D02786FC30CCA2C71 = {
+ isa = PBXFrameworkReference;
+ name = IOKit.framework;
+ path = /System/Library/Frameworks/IOKit.framework;
+ refType = 0;
+ };
+ 00DD152B04CC79700CCA2C71 = {
+ fileRef = 004EFB9604CC78130CCA2C71;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 00DD152C04CC79A50CCA2C71 = {
+ fileRef = F5E11B5A04A28126019798ED;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+//000
+//001
+//002
+//003
+//004
+//030
+//031
+//032
+//033
+//034
+ 034768E2FF38A6DC11DB9C8B = {
+ isa = PBXExecutableFileReference;
+ path = mDNSResponder;
+ refType = 3;
+ };
+//030
+//031
+//032
+//033
+//034
+//080
+//081
+//082
+//083
+//084
+ 08FB7793FE84155DC02AAC07 = {
+ buildStyles = (
+ 00B2AB0C032D7B220CCA2C71,
+ );
+ isa = PBXProject;
+ mainGroup = 08FB7794FE84155DC02AAC07;
+ projectDirPath = "";
+ targets = (
+ 00AD62BB032D7A0C0CCA2C71,
+ 08FB779FFE84155DC02AAC07,
+ 00AD62A3032D799A0CCA2C71,
+ 6575FC1C022EB76000000109,
+ 0044D34804CC73600CCA2C71,
+ );
+ };
+ 08FB7794FE84155DC02AAC07 = {
+ children = (
+ 08FB7795FE84155DC02AAC07,
+ 6575FC1F022EB78C00000109,
+ 6575FBFE022EAFA800000109,
+ 08FB779DFE84155DC02AAC07,
+ 19C28FBDFE9D53C911CA2CBB,
+ );
+ isa = PBXGroup;
+ name = mDNSResponder;
+ refType = 4;
+ };
+ 08FB7795FE84155DC02AAC07 = {
+ children = (
+ F525E72804AA167501F1CF4D,
+ F5E11B5A04A28126019798ED,
+ F5E11B5B04A28126019798ED,
+ 6575FBEC022EAF7200000109,
+ 6575FBE9022EAF5A00000109,
+ 6575FBEB022EAF7200000109,
+ 654BE64F02B63B93000001D1,
+ 654BE65002B63B93000001D1,
+ 654BE65202B63B93000001D1,
+ 000753D303367C1C0CCA2C71,
+ );
+ isa = PBXGroup;
+ name = "mDNS Server Sources";
+ path = "";
+ refType = 4;
+ };
+ 08FB779DFE84155DC02AAC07 = {
+ children = (
+ 09AB6884FE841BABC02AAC07,
+ 65713D46025A293200000109,
+ 00CA213D02786FC30CCA2C71,
+ );
+ isa = PBXGroup;
+ name = "External Frameworks and Libraries";
+ refType = 4;
+ };
+ 08FB779FFE84155DC02AAC07 = {
+ buildPhases = (
+ 08FB77A0FE84155DC02AAC07,
+ 08FB77A1FE84155DC02AAC07,
+ 08FB77A3FE84155DC02AAC07,
+ 08FB77A5FE84155DC02AAC07,
+ );
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = "";
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
+ HEADER_SEARCH_PATHS = "\"$(APPLE_INTERNAL_DEVELOPER_DIR)/Headers\"";
+ INSTALL_PATH = /usr/sbin;
+ LIBRARY_SEARCH_PATHS = "";
+ OTHER_CFLAGS = "-no-cpp-precomp -D__MACOSX__ -DmDNSResponderVersion=$(MVERS)";
+ OTHER_LDFLAGS = "";
+ OTHER_REZFLAGS = "";
+ PRODUCT_NAME = mDNSResponder;
+ REZ_EXECUTABLE = YES;
+ SECTORDER_FLAGS = "";
+ STRIPFLAGS = "-S";
+ WARNING_CFLAGS = "-W -Wall -Wmissing-prototypes -Wno-four-char-constants -Wno-unknown-pragmas";
+ };
+ dependencies = (
+ );
+ isa = PBXToolTarget;
+ name = mDNSResponder;
+ productInstallPath = "$(HOME)/bin";
+ productName = mDNSResponder;
+ productReference = 034768E2FF38A6DC11DB9C8B;
+ shouldUseHeadermap = 1;
+ };
+ 08FB77A0FE84155DC02AAC07 = {
+ buildActionMask = 2147483647;
+ files = (
+ 6575FC02022EAFBA00000109,
+ F5E11B5D04A28126019798ED,
+ );
+ isa = PBXHeadersBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 08FB77A1FE84155DC02AAC07 = {
+ buildActionMask = 2147483647;
+ files = (
+ 6575FC0D022EB18700000109,
+ 6575FC0E022EB18700000109,
+ 6575FBEA022EAF5A00000109,
+ 6575FBED022EAF7200000109,
+ 6575FBEE022EAF7200000109,
+ F5E11B5C04A28126019798ED,
+ F525E72904AA167501F1CF4D,
+ );
+ isa = PBXSourcesBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 08FB77A3FE84155DC02AAC07 = {
+ buildActionMask = 2147483647;
+ files = (
+ 09AB6885FE841BABC02AAC07,
+ 65713D66025A293200000109,
+ 6585DD640279A3B7000001D1,
+ );
+ isa = PBXFrameworksBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 08FB77A5FE84155DC02AAC07 = {
+ buildActionMask = 2147483647;
+ files = (
+ );
+ isa = PBXRezBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+//080
+//081
+//082
+//083
+//084
+//090
+//091
+//092
+//093
+//094
+ 09AB6884FE841BABC02AAC07 = {
+ isa = PBXFrameworkReference;
+ name = CoreFoundation.framework;
+ path = /System/Library/Frameworks/CoreFoundation.framework;
+ refType = 0;
+ };
+ 09AB6885FE841BABC02AAC07 = {
+ fileRef = 09AB6884FE841BABC02AAC07;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+//090
+//091
+//092
+//093
+//094
+//190
+//191
+//192
+//193
+//194
+ 19C28FBDFE9D53C911CA2CBB = {
+ children = (
+ 034768E2FF38A6DC11DB9C8B,
+ 6575FC1D022EB76000000109,
+ 00AD62B8032D799A0CCA2C71,
+ 0044D34F04CC73600CCA2C71,
+ );
+ isa = PBXGroup;
+ name = Products;
+ refType = 4;
+ };
+//190
+//191
+//192
+//193
+//194
+//650
+//651
+//652
+//653
+//654
+ 654BE64F02B63B93000001D1 = {
+ isa = PBXFileReference;
+ name = mDNSClientAPI.h;
+ path = ../mDNSCore/mDNSClientAPI.h;
+ refType = 4;
+ };
+ 654BE65002B63B93000001D1 = {
+ isa = PBXFileReference;
+ name = mDNSDebug.h;
+ path = ../mDNSCore/mDNSDebug.h;
+ refType = 4;
+ };
+ 654BE65202B63B93000001D1 = {
+ isa = PBXFileReference;
+ name = mDNSPlatformFunctions.h;
+ path = ../mDNSCore/mDNSPlatformFunctions.h;
+ refType = 4;
+ };
+ 65713D46025A293200000109 = {
+ isa = PBXFrameworkReference;
+ name = SystemConfiguration.framework;
+ path = /System/Library/Frameworks/SystemConfiguration.framework;
+ refType = 0;
+ };
+ 65713D66025A293200000109 = {
+ fileRef = 65713D46025A293200000109;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 6575FBE9022EAF5A00000109 = {
+ indentWidth = 4;
+ isa = PBXFileReference;
+ name = mDNS.c;
+ path = ../mDNSCore/mDNS.c;
+ refType = 4;
+ tabWidth = 4;
+ usesTabs = 1;
+ };
+ 6575FBEA022EAF5A00000109 = {
+ fileRef = 6575FBE9022EAF5A00000109;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 6575FBEB022EAF7200000109 = {
+ indentWidth = 4;
+ isa = PBXFileReference;
+ path = CFSocket.c;
+ refType = 4;
+ tabWidth = 4;
+ usesTabs = 1;
+ };
+ 6575FBEC022EAF7200000109 = {
+ indentWidth = 4;
+ isa = PBXFileReference;
+ path = daemon.c;
+ refType = 4;
+ tabWidth = 4;
+ usesTabs = 1;
+ };
+ 6575FBED022EAF7200000109 = {
+ fileRef = 6575FBEB022EAF7200000109;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 6575FBEE022EAF7200000109 = {
+ fileRef = 6575FBEC022EAF7200000109;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 6575FBFE022EAFA800000109 = {
+ children = (
+ 6575FBFF022EAFBA00000109,
+ 6575FC00022EAFBA00000109,
+ 6575FC01022EAFBA00000109,
+ );
+ isa = PBXGroup;
+ name = "DNS Service Discovery MIG files";
+ refType = 4;
+ };
+ 6575FBFF022EAFBA00000109 = {
+ isa = PBXFileReference;
+ path = DNSServiceDiscoveryDefines.h;
+ refType = 4;
+ };
+ 6575FC00022EAFBA00000109 = {
+ isa = PBXFileReference;
+ path = DNSServiceDiscoveryReply.defs;
+ refType = 4;
+ };
+ 6575FC01022EAFBA00000109 = {
+ isa = PBXFileReference;
+ path = DNSServiceDiscoveryRequest.defs;
+ refType = 4;
+ };
+ 6575FC02022EAFBA00000109 = {
+ fileRef = 6575FBFF022EAFBA00000109;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 6575FC0D022EB18700000109 = {
+ fileRef = 6575FC00022EAFBA00000109;
+ isa = PBXBuildFile;
+ settings = {
+ ATTRIBUTES = (
+ Client,
+ );
+ };
+ };
+ 6575FC0E022EB18700000109 = {
+ fileRef = 6575FC01022EAFBA00000109;
+ isa = PBXBuildFile;
+ settings = {
+ ATTRIBUTES = (
+ Server,
+ Client,
+ );
+ };
+ };
+ 6575FC18022EB76000000109 = {
+ buildActionMask = 2147483647;
+ files = (
+ );
+ isa = PBXHeadersBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 6575FC19022EB76000000109 = {
+ buildActionMask = 2147483647;
+ files = (
+ 6575FC21022EB7AA00000109,
+ );
+ isa = PBXSourcesBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 6575FC1A022EB76000000109 = {
+ buildActionMask = 2147483647;
+ files = (
+ 6575FC24022EBA5D00000109,
+ );
+ isa = PBXFrameworksBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 6575FC1B022EB76000000109 = {
+ buildActionMask = 2147483647;
+ files = (
+ );
+ isa = PBXRezBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 6575FC1C022EB76000000109 = {
+ buildPhases = (
+ 6575FC18022EB76000000109,
+ 6575FC19022EB76000000109,
+ 6575FC1A022EB76000000109,
+ 6575FC1B022EB76000000109,
+ );
+ buildSettings = {
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
+ INSTALL_PATH = /usr/bin;
+ OTHER_CFLAGS = "-no-cpp-precomp -DmDNSResponderVersion=$(MVERS)";
+ OTHER_LDFLAGS = "";
+ OTHER_REZFLAGS = "";
+ PRODUCT_NAME = mDNS;
+ REZ_EXECUTABLE = YES;
+ SECTORDER_FLAGS = "";
+ STRIPFLAGS = "-S";
+ WARNING_CFLAGS = "-W -Wall -Wmissing-prototypes -Wno-four-char-constants -Wno-unknown-pragmas";
+ };
+ dependencies = (
+ );
+ isa = PBXToolTarget;
+ name = "mDNS Command-Line tool";
+ productInstallPath = /usr/bin;
+ productName = "Sample mDNS Client";
+ productReference = 6575FC1D022EB76000000109;
+ shouldUseHeadermap = 0;
+ };
+ 6575FC1D022EB76000000109 = {
+ isa = PBXExecutableFileReference;
+ path = mDNS;
+ refType = 3;
+ };
+ 6575FC1F022EB78C00000109 = {
+ children = (
+ 6575FC20022EB7AA00000109,
+ 0017390704CC75C30CCA2C71,
+ 004EFB9604CC78130CCA2C71,
+ );
+ isa = PBXGroup;
+ name = SampleMulticastDNSClient;
+ refType = 4;
+ };
+ 6575FC20022EB7AA00000109 = {
+ indentWidth = 4;
+ isa = PBXFileReference;
+ path = SamplemDNSClient.c;
+ refType = 4;
+ tabWidth = 4;
+ usesTabs = 0;
+ };
+ 6575FC21022EB7AA00000109 = {
+ fileRef = 6575FC20022EB7AA00000109;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 6575FC24022EBA5D00000109 = {
+ fileRef = 09AB6884FE841BABC02AAC07;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 6585DD640279A3B7000001D1 = {
+ fileRef = 00CA213D02786FC30CCA2C71;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+//650
+//651
+//652
+//653
+//654
+//F50
+//F51
+//F52
+//F53
+//F54
+ F515E29604A37BB701CA296C = {
+ fileRef = 654BE64F02B63B93000001D1;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ F515E29704A37BB801CA296C = {
+ fileRef = 654BE65002B63B93000001D1;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ F515E29804A37BBB01CA296C = {
+ fileRef = 654BE65202B63B93000001D1;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ F515E29904A37BBB01CA296C = {
+ fileRef = 000753D303367C1C0CCA2C71;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ F525E72804AA167501F1CF4D = {
+ isa = PBXFileReference;
+ path = uds_daemon.c;
+ refType = 4;
+ };
+ F525E72904AA167501F1CF4D = {
+ fileRef = F525E72804AA167501F1CF4D;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ F525E72B04AA167A01F1CF4D = {
+ fileRef = F525E72804AA167501F1CF4D;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ F5E11B5A04A28126019798ED = {
+ isa = PBXFileReference;
+ path = dnssd_ipc.c;
+ refType = 4;
+ };
+ F5E11B5B04A28126019798ED = {
+ isa = PBXFileReference;
+ path = dnssd_ipc.h;
+ refType = 4;
+ };
+ F5E11B5C04A28126019798ED = {
+ fileRef = F5E11B5A04A28126019798ED;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ F5E11B5D04A28126019798ED = {
+ fileRef = F5E11B5B04A28126019798ED;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ F5E11B5E04A28126019798ED = {
+ fileRef = F5E11B5A04A28126019798ED;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ F5E11B5F04A28126019798ED = {
+ fileRef = F5E11B5B04A28126019798ED;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ };
+ rootObject = 08FB7793FE84155DC02AAC07;
+}
--- /dev/null
+/*
+ * Copyright (c) 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: uds_daemon.c,v $
+Revision 1.22 2003/08/19 16:03:55 ksekar
+Bug #: <rdar://problem/3380097>: ER: SIGINFO dump should include resolves started by DNSServiceQueryRecord
+Check termination_context for NULL before dereferencing.
+
+Revision 1.21 2003/08/19 05:39:43 cheshire
+<rdar://problem/3380097> SIGINFO dump should include resolves started by DNSServiceQueryRecord
+
+Revision 1.20 2003/08/16 03:39:01 cheshire
+<rdar://problem/3338440> InterfaceID -1 indicates "local only"
+
+Revision 1.19 2003/08/15 20:16:03 cheshire
+<rdar://problem/3366590> 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.18 2003/08/15 00:38:00 ksekar
+Bug #: <rdar://problem/3377005>: Bug: buffer overrun when reading long rdata from client
+
+Revision 1.17 2003/08/14 02:18:21 cheshire
+<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
+
+Revision 1.16 2003/08/13 23:58:52 ksekar
+Bug #: <rdar://problem/3374911>: Bug: UDS Sub-type browsing works, but not sub-type registration
+Fixed pointer increment error, moved subtype reading for-loop for easier error bailout.
+
+Revision 1.15 2003/08/13 17:30:33 ksekar
+Bug #: <rdar://problem/3374671>: DNSServiceAddRecord doesn't work
+Fixed various problems with handling the AddRecord request and freeing the ExtraResourceRecords.
+
+Revision 1.14 2003/08/12 19:56:25 cheshire
+Update to APSL 2.0
+
+ */
+
+#include "mDNSClientAPI.h"
+#include "mDNSMacOSX.h"
+#include "dns_sd.h"
+#include "dnssd_ipc.h"
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+// Types and Data Structures
+// ----------------------------------------------------------------------
+
+typedef enum
+ {
+ t_uninitialized,
+ t_morecoming,
+ t_complete,
+ t_error,
+ t_terminated
+ } transfer_state;
+
+typedef void (*req_termination_fn)(void *);
+
+
+typedef struct registered_record_entry
+ {
+ int key;
+ AuthRecord *rr;
+ struct registered_record_entry *next;
+ } registered_record_entry;
+
+typedef struct registered_service
+ {
+ //struct registered_service *next;
+ int autoname;
+ int renameonconflict;
+ int rename_on_memfree; // set flag on config change when we deregister original name
+ domainlabel name;
+ ServiceRecordSet *srs;
+ struct request_state *request;
+ AuthRecord *subtypes;
+ } registered_service;
+
+typedef struct
+ {
+ mStatus err;
+ int nwritten;
+ int sd;
+ } undelivered_error_t;
+
+typedef struct request_state
+ {
+ // connection structures
+ CFRunLoopSourceRef rls;
+ CFSocketRef sr;
+ int sd;
+ int errfd;
+
+ // state of read (in case message is read over several recv() calls)
+ transfer_state ts;
+ uint32_t hdr_bytes; // bytes of header already read
+ ipc_msg_hdr hdr;
+ uint32_t data_bytes; // bytes of message data already read
+ char *msgbuf; // pointer to data storage to pass to free()
+ char *msgdata; // pointer to data to be read from (may be modified)
+ int bufsize; // size of data storage
+
+ // reply, termination, error, and client context info
+ int no_reply; // don't send asynchronous replies to client
+ void *client_context; // don't touch this - pointer only valid in client's addr space
+ struct reply_state *replies; // corresponding (active) reply list
+ undelivered_error_t *u_err;
+ void *termination_context;
+ req_termination_fn terminate;
+
+ //!!!KRS toss these pointers in a union
+ // registration context associated with this request (null if not applicable)
+ registered_record_entry *reg_recs; // muliple registrations for a connection-oriented request
+ registered_service *service; // service record set and flags
+ struct resolve_result_t *resolve_results;
+
+ struct request_state *next;
+ } request_state;
+
+// struct physically sits between ipc message header and call-specific fields in the message buffer
+typedef struct
+ {
+ DNSServiceFlags flags;
+ uint32_t ifi;
+ DNSServiceErrorType error;
+ } reply_hdr;
+
+
+typedef struct reply_state
+ {
+ // state of the transmission
+ int sd;
+ transfer_state ts;
+ uint32_t nwriten;
+ uint32_t len;
+ // context of the reply
+ struct request_state *request; // the request that this answers
+ struct reply_state *next; // if there are multiple unsent replies
+ // pointer into message buffer - allows fields to be changed after message is formatted
+ ipc_msg_hdr *mhdr;
+ reply_hdr *rhdr;
+ char *sdata; // pointer to start of call-specific data
+ // pointer to malloc'd buffer
+ char *msgbuf;
+ } reply_state;
+
+
+// domain enumeration and resolv calls require 2 mDNSCore calls, so we need separate interconnected
+// structures to handle callbacks
+typedef struct
+ {
+ DNSQuestion question;
+ mDNS_DomainType type;
+ request_state *rstate;
+ } domain_enum_t;
+
+typedef struct
+ {
+ domain_enum_t *all;
+ domain_enum_t *def;
+ request_state *rstate;
+ } enum_termination_t;
+
+typedef struct
+ {
+ DNSQuestion question;
+ uint16_t qtype;
+ request_state *rstate;
+ } resolve_t;
+
+typedef struct
+ {
+ resolve_t *txt;
+ resolve_t *srv;
+ request_state *rstate;
+ } resolve_termination_t;
+
+typedef struct resolve_result_t
+ {
+ const ResourceRecord *txt;
+ const ResourceRecord *srv;
+ } resolve_result_t;
+
+typedef struct
+ {
+ request_state *rstate;
+ client_context_t client_context;
+ } regrecord_callback_context;
+
+
+
+
+// globals
+static int listenfd = -1;
+static request_state *all_requests = NULL;
+//!!!KRS we should keep a separate list containing only the requests that need to be examined
+//in the idle() routine.
+
+
+#define MAX_OPENFILES 1024
+
+
+// private function prototypes
+static void connect_callback(CFSocketRef sr, CFSocketCallBackType t, CFDataRef dr, const void *c, void *i);
+static int read_msg(request_state *rs);
+static int send_msg(reply_state *rs);
+static void abort_request(request_state *rs);
+static void request_callback(CFSocketRef sr, CFSocketCallBackType t, CFDataRef dr, const void *c, void *i);
+static void handle_resolve_request(request_state *rstate);
+static void question_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord);
+static void question_termination_callback(void *context);
+static void handle_browse_request(request_state *request);
+static void browse_termination_callback(void *context);
+static void browse_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord);
+static void handle_regservice_request(request_state *request);
+static void regservice_termination_callback(void *context);
+static void process_service_registration(ServiceRecordSet *const srs);
+static void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result);
+static void handle_add_request(request_state *rstate);
+static void handle_update_request(request_state *rstate);
+static mStatus gen_rr_response(domainname *servicename, mDNSInterfaceID id, request_state *request, reply_state **rep);
+static void append_reply(request_state *req, reply_state *rep);
+static int build_domainname_from_strings(domainname *srv, char *name, char *regtype, char *domain);
+static void enum_termination_callback(void *context);
+static void enum_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord);
+static void handle_query_request(request_state *rstate);
+static mStatus do_question(request_state *rstate, domainname *name, uint32_t ifi, uint16_t rrtype, int16_t rrclass);
+static reply_state *format_enumeration_reply(request_state *rstate, char *domain, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err);
+static void handle_enum_request(request_state *rstate);
+static void handle_regrecord_request(request_state *rstate);
+static void regrecord_callback(mDNS *const m, AuthRecord *const rr, mStatus result);
+static void connected_registration_termination(void *context);
+static void handle_reconfirm_request(request_state *rstate);
+static AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int ttl);
+static void handle_removerecord_request(request_state *rstate);
+static void reset_connected_rstate(request_state *rstate);
+static int deliver_error(request_state *rstate, mStatus err);
+static int deliver_async_error(request_state *rs, reply_op_t op, mStatus err);
+static transfer_state send_undelivered_error(request_state *rs);
+static reply_state *create_reply(reply_op_t op, int datalen, request_state *request);
+static void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd);
+static void my_perror(char *errmsg);
+static void unlink_request(request_state *rs);
+static void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord);
+static void resolve_termination_callback(void *context);
+
+// initialization, setup/teardown functions
+
+int udsserver_init(void)
+ {
+ mode_t mask;
+ struct sockaddr_un laddr;
+ struct rlimit maxfds;
+
+ if ((listenfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
+ goto error;
+ unlink(MDNS_UDS_SERVERPATH); //OK if this fails
+ bzero(&laddr, sizeof(laddr));
+ laddr.sun_family = AF_LOCAL;
+ laddr.sun_len = sizeof(struct sockaddr_un);
+ strcpy(laddr.sun_path, MDNS_UDS_SERVERPATH);
+ mask = umask(0);
+ if (bind(listenfd, (struct sockaddr *)&laddr, sizeof(laddr)) < 0)
+ goto error;
+ umask(mask);
+
+ if (fcntl(listenfd, F_SETFL, O_NONBLOCK) < 0)
+ {
+ my_perror("ERROR: could not set listen socket to non-blocking mode");
+ goto error;
+ }
+ listen(listenfd, LISTENQ);
+
+
+ // set maximum file descriptor to 1024
+ if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0)
+ {
+ my_perror("ERROR: Unable to get file descriptor limit");
+ return 0;
+ }
+ if (maxfds.rlim_max >= MAX_OPENFILES && maxfds.rlim_cur == maxfds.rlim_max)
+ {
+ // proper values already set
+ return 0;
+ }
+ maxfds.rlim_max = MAX_OPENFILES;
+ maxfds.rlim_cur = MAX_OPENFILES;
+ if (setrlimit(RLIMIT_NOFILE, &maxfds) < 0)
+ my_perror("ERROR: Unable to set maximum file descriptor limit");
+ return 0;
+
+error:
+ my_perror("ERROR: udsserver_init");
+ return -1;
+ }
+
+int udsserver_exit(void)
+ {
+ close(listenfd);
+ unlink(MDNS_UDS_SERVERPATH);
+ return 0;
+ }
+
+
+// add the named socket as a runloop source
+int udsserver_add_rl_source(void)
+ {
+ CFSocketContext context = { 0, NULL, NULL, NULL, NULL };
+ CFSocketRef sr = CFSocketCreateWithNative(kCFAllocatorDefault, listenfd, kCFSocketReadCallBack, connect_callback, &context);
+ if (!sr)
+ {
+ debugf("ERROR: udsserver_add_rl_source - CFSocketCreateWithNative");
+ return -1;
+ }
+ CFRunLoopSourceRef rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, sr, 0);
+
+ if (!rls)
+ {
+ debugf("ERROR: udsserver_add_rl_source - CFSocketCreateRunLoopSource");
+ return -1;
+ }
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+ return 0;
+ }
+
+mDNSs32 udsserver_idle(mDNSs32 nextevent)
+ {
+ request_state *req = all_requests, *tmp, *prev = NULL;
+ reply_state *fptr;
+ transfer_state result;
+ mDNSs32 now = mDNSPlatformTimeNow();
+
+
+ while(req)
+ {
+ result = t_uninitialized;
+ if (req->u_err)
+ result = send_undelivered_error(req);
+ if (result != t_error && result != t_morecoming && // don't try to send msg if send_error failed
+ (req->ts == t_complete || req->ts == t_morecoming))
+ {
+ while(req->replies)
+ {
+ if (req->replies->next) req->replies->rhdr->flags |= kDNSServiceFlagsMoreComing;
+ else req->replies->rhdr->flags |= kDNSServiceFlagsFinished;
+ result = send_msg(req->replies);
+ if (result == t_complete)
+ {
+ fptr = req->replies;
+ req->replies = req->replies->next;
+ freeL("udsserver_idle", fptr);
+ }
+ else if (result == t_terminated || result == t_error)
+ {
+ abort_request(req);
+ break;
+ }
+ else if (result == t_morecoming) // client's queues are full, move to next
+ {
+ if (nextevent - now > mDNSPlatformOneSecond)
+ nextevent = now + mDNSPlatformOneSecond;
+ break; // start where we left off in a second
+ }
+ }
+ }
+ if (result == t_terminated || result == t_error)
+ //since we're already doing a list traversal, we unlink the request manunally instead of calling unlink_request()
+ {
+ tmp = req;
+ if (prev) prev->next = req->next;
+ if (req == all_requests) all_requests = all_requests->next;
+ req = req->next;
+ freeL("udsserver_idle", tmp);
+ }
+ else
+ {
+ prev = req;
+ req = req->next;
+ }
+ }
+ return nextevent;
+ }
+
+void udsserver_info(void)
+ {
+ request_state *req;
+ for (req = all_requests; req; req=req->next)
+ {
+ void *t = req->termination_context;
+ if (!t) continue;
+ if (req->terminate == regservice_termination_callback)
+ LogMsg("DNSServiceRegister %##s", ((registered_service *) t)->srs->RR_SRV.resrec.name.c);
+ else if (req->terminate == browse_termination_callback)
+ LogMsg("DNSServiceBrowse %##s", ((DNSQuestion *) t)->qname.c);
+ else if (req->terminate == resolve_termination_callback)
+ LogMsg("DNSServiceResolve %##s", ((resolve_termination_t *)t)->srv->question.qname.c);
+ else if (req->terminate == question_termination_callback)
+ LogMsg("DNSServiceQueryRecord %##s", ((DNSQuestion *) t)->qname.c);
+ else if (req->terminate == enum_termination_callback)
+ LogMsg("DNSServiceEnumerateDomains %##s", ((enum_termination_t *) t)->all->question.qname.c);
+ }
+ }
+
+void udsserver_handle_configchange(void)
+ {
+ registered_service *srv;
+ request_state *req;
+ mStatus err;
+
+ for (req = all_requests; req; req = req->next)
+ {
+ srv = req->service;
+ if (srv->autoname && !SameDomainLabel(srv->name.c, mDNSStorage.nicelabel.c))
+ {
+ srv->rename_on_memfree = 1;
+ err = mDNS_DeregisterService(&mDNSStorage, srv->srs);
+ if (err) LogMsg("ERROR: udsserver_handle_configchange: DeregisterService returned error %d. Continuing.", err);
+ // error should never occur - safest to log and continue
+ }
+ }
+ }
+
+
+
+
+// accept a connection on the named socket, adding the new descriptor to the runloop and passing the error
+// descriptor to the client
+static void connect_callback(CFSocketRef s, CFSocketCallBackType t, CFDataRef dr, const void *c, void *i)
+ {
+ int sd, clilen, optval;
+ struct sockaddr_un cliaddr;
+ CFSocketContext context = { 0, NULL, NULL, NULL, NULL };
+ request_state *rstate;
+// int errpipe[2];
+
+ #pragma unused(s, t, dr, c, i)
+
+ clilen = sizeof(cliaddr);
+ sd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
+
+ if (sd < 0)
+ {
+ if (errno == EWOULDBLOCK) return;
+ my_perror("ERROR: accept");
+ return;
+ }
+ optval = 1;
+ if (setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0)
+ {
+ my_perror("ERROR: setsockopt - SOL_NOSIGPIPE - aborting client");
+ close(sd);
+ return;
+ }
+
+ if (fcntl(sd, F_SETFL, O_NONBLOCK) < 0)
+ {
+ my_perror("ERROR: could not set connected socket to non-blocking mode - aborting client");
+ close(sd);
+ return;
+ }
+
+/*
+ // open a pipe to deliver error messages, pass descriptor to client
+ if (pipe(errpipe) < 0)
+ {
+ my_perror("ERROR: could not create pipe");
+ exit(1);
+ }
+
+ if (ioctl(sd, I_SENDFD, errpipe[0]) < 0)
+ {
+ my_perror("ERROR: could not pass pipe descriptor to client. Aborting client.\n");
+ close(sd);
+ return;
+ }
+ if (fcntl(errpipe[1], F_SETFL, O_NONBLOCK) < 0)
+ {
+ my_perror("ERROR: could not set error pipe to non-blocking mode - aborting client");
+ close(sd);
+ close(errpipe[1]);
+ return;
+ }
+ */
+
+ // allocate a request_state struct that will live with the socket
+ rstate = mallocL("connect_callback", sizeof(request_state));
+ if (!rstate)
+ {
+ my_perror("ERROR: malloc");
+ exit(1);
+ }
+ bzero(rstate, sizeof(request_state));
+ rstate->ts = t_morecoming;
+ rstate->sd = sd;
+ //rstate->errfd = errpipe[1];
+
+ //now create CFSocket wrapper and add to run loop
+ context.info = rstate;
+ rstate->sr = CFSocketCreateWithNative(kCFAllocatorDefault, sd, kCFSocketReadCallBack, request_callback, &context);
+ if (!rstate->sr)
+ {
+ debugf("ERROR: connect_callback - CFSocketCreateWithNative");
+ freeL("connect_callback", rstate);
+ close(sd);
+ return;
+ }
+ rstate->rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, rstate->sr, 0);
+ if (!rstate->rls)
+ {
+ debugf("ERROR: connect_callback - CFSocketCreateRunLoopSource");
+ CFSocketInvalidate(rstate->sr); // automatically closes socket
+ CFRelease(rstate->sr);
+ freeL("connect_callback", rstate);
+ return;
+ }
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rstate->rls, kCFRunLoopDefaultMode);
+ if (!CFRunLoopContainsSource(CFRunLoopGetCurrent(), rstate->rls, kCFRunLoopDefaultMode))
+ {
+ LogMsg("ERROR: connect_callback, CFRunLoopAddSource");
+ abort_request(rstate);
+ return;
+ }
+ rstate->next = all_requests;
+ all_requests = rstate;
+ }
+
+
+// main client request handling routine. reads request and calls the appropriate request-specific
+// handler
+static void request_callback(CFSocketRef sr, CFSocketCallBackType t, CFDataRef dr, const void *context, void *info)
+ {
+ request_state *rstate = info;
+ transfer_state result;
+ struct sockaddr_un cliaddr;
+ char ctrl_path[MAX_CTLPATH];
+
+ #pragma unused(sr, t, dr, context)
+
+ int native = CFSocketGetNative(sr);
+ if (native != rstate->sd)
+ {
+ LogMsg("ERROR: request_callback - CFSocket's native descriptor does not match rstate member descriptor.");
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+
+ result = read_msg(rstate);
+ if (result == t_morecoming)
+ {
+ return;
+ }
+ if (result == t_terminated)
+ {
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+ if (result == t_error)
+ {
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+
+ if (rstate->hdr.version != VERSION)
+ {
+ LogMsg("ERROR: client incompatible with daemon (client version = %d, "
+ "daemon version = %d)\n", rstate->hdr.version, VERSION);
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+
+ // check if client wants silent operation
+ if (rstate->hdr.flags & IPC_FLAGS_NOREPLY) rstate->no_reply = 1;
+
+ // check if primary socket is to be used for synchronous errors, else open new socket
+ if (rstate->hdr.flags & IPC_FLAGS_REUSE_SOCKET)
+ rstate->errfd = rstate->sd;
+ else
+ {
+ if ((rstate->errfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
+ {
+ my_perror("ERROR: socket");
+ exit(1);
+ }
+ if (fcntl(rstate->errfd, F_SETFL, O_NONBLOCK) < 0)
+ {
+ my_perror("ERROR: could not set control socket to non-blocking mode");
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+ get_string(&rstate->msgdata, ctrl_path, 256); // path is first element in message buffer
+ bzero(&cliaddr, sizeof(cliaddr));
+ cliaddr.sun_family = AF_LOCAL;
+ strcpy(cliaddr.sun_path, ctrl_path);
+ if (connect(rstate->errfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)) < 0)
+ {
+ my_perror("ERROR: connect");
+ abort_request(rstate);
+ unlink_request(rstate);
+ }
+ }
+
+
+
+
+ switch(rstate->hdr.op.request_op)
+ {
+ case resolve_request: handle_resolve_request(rstate); break;
+ case query_request: handle_query_request(rstate); break;
+ case browse_request: handle_browse_request(rstate); break;
+ case reg_service_request: handle_regservice_request(rstate); break;
+ case enumeration_request: handle_enum_request(rstate); break;
+ case reg_record_request: handle_regrecord_request(rstate); break;
+ case add_record_request: handle_add_request(rstate); break;
+ case update_record_request: handle_update_request(rstate); break;
+ case remove_record_request: handle_removerecord_request(rstate); break;
+ case reconfirm_record_request: handle_reconfirm_request(rstate); break;
+ default:
+ debugf("ERROR: udsserver_recv_request - unsupported request type: %d", rstate->hdr.op.request_op);
+ }
+ }
+
+// mDNS operation functions. Each operation has 3 associated functions - a request handler that parses
+// the client's request and makes the appropriate mDNSCore call, a result handler (passed as a callback
+// to the mDNSCore routine) that sends results back to the client, and a termination routine that aborts
+// the mDNSCore operation if the client dies or closes its socket.
+
+
+// query and resolve calls have separate request handlers that parse the arguments from the client and
+// massage the name parameters appropriately, but the rest of the operations (making the query call,
+// delivering the result to the client, and termination) are identical.
+
+static void handle_query_request(request_state *rstate)
+ {
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex;
+ char name[256];
+ uint16_t rrtype, rrclass;
+ char *ptr;
+ domainname dname;
+ mStatus result;
+
+ if (rstate->ts != t_complete)
+ {
+ LogMsg("ERROR: handle_query_request - transfer state != t_complete");
+ goto error;
+ }
+ ptr = rstate->msgdata;
+ if (!ptr)
+ {
+ LogMsg("ERROR: handle_query_request - NULL msgdata");
+ goto error;
+ }
+ flags = get_flags(&ptr);
+ interfaceIndex = get_long(&ptr);
+ if (get_string(&ptr, name, 256) < 0) goto bad_param;
+ rrtype = get_short(&ptr);
+ rrclass = get_short(&ptr);
+ if (!MakeDomainNameFromDNSNameString(&dname, name)) goto bad_param;
+ result = do_question(rstate, &dname, interfaceIndex, rrtype, rrclass);
+ if (result) rstate->terminate = NULL;
+ if (deliver_error(rstate, result) < 0) goto error;
+ return;
+
+bad_param:
+ deliver_error(rstate, mStatus_BadParamErr);
+ rstate->terminate = NULL; // don't try to terminate insuccessful Core calls
+error:
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+
+static void handle_resolve_request(request_state *rstate)
+ {
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex;
+ char name[256], regtype[256], domain[256];
+ char *ptr; // message data pointer
+ domainname fqdn;
+ resolve_t *srv, *txt;
+ resolve_termination_t *term;
+ mStatus err;
+
+ if (rstate->ts != t_complete)
+ {
+ LogMsg("ERROR: handle_resolve_request - transfer state != t_complete");
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+
+ // extract the data from the message
+ ptr = rstate->msgdata;
+ if (!ptr)
+ {
+ LogMsg("ERROR: handle_resolve_request - NULL msgdata");
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+ flags = get_flags(&ptr);
+ interfaceIndex = get_long(&ptr);
+ mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+ if (interfaceIndex && !InterfaceID) goto bad_param;
+ if (get_string(&ptr, name, 256) < 0 ||
+ get_string(&ptr, regtype, 256) < 0 ||
+ get_string(&ptr, domain, 256) < 0)
+ goto bad_param;
+
+ // free memory in rstate since we don't need it anymore
+ freeL("handle_resolve_request", rstate->msgbuf);
+ rstate->msgbuf = NULL;
+
+ if (build_domainname_from_strings(&fqdn, name, regtype, domain) < 0)
+ goto bad_param;
+
+ // allocate question wrapper structs
+ srv = mallocL("handle_resolve_request", sizeof(resolve_t));
+ txt = mallocL("handle_resolve_request", sizeof(resolve_t));
+ if (!srv || !txt) goto malloc_error;
+ srv->qtype = kDNSType_SRV;
+ txt->qtype = kDNSType_TXT;
+ srv->rstate = rstate;
+ txt->rstate = rstate;
+
+ // format questions
+ srv->question.QuestionContext = rstate;
+ srv->question.QuestionCallback = resolve_result_callback;
+ memcpy(&srv->question.qname, &fqdn, MAX_DOMAIN_NAME);
+ srv->question.qtype = kDNSType_SRV;
+ srv->question.qclass = kDNSClass_IN;
+ srv->question.InterfaceID = InterfaceID;
+
+ txt->question.QuestionContext = rstate;
+ txt->question.QuestionCallback = resolve_result_callback;
+ memcpy(&txt->question.qname, &fqdn, MAX_DOMAIN_NAME);
+ txt->question.qtype = kDNSType_TXT;
+ txt->question.qclass = kDNSClass_IN;
+ txt->question.InterfaceID = InterfaceID;
+
+ // set up termination info
+ term = mallocL("handle_resolve_request", sizeof(resolve_termination_t));
+ if (!term) goto malloc_error;
+ term->srv = srv;
+ term->txt = txt;
+ term->rstate = rstate;
+ rstate->termination_context = term;
+ rstate->terminate = resolve_termination_callback;
+
+ // set up reply wrapper struct (since answer will come via 2 callbacks)
+ rstate->resolve_results = mallocL("handle_resolve_response", sizeof(resolve_result_t));
+ if (!rstate->resolve_results) goto malloc_error;
+ bzero(rstate->resolve_results, sizeof(resolve_result_t));
+
+ // ask the questions
+ err = mDNS_StartQuery(&mDNSStorage, &srv->question);
+ if (!err) err = mDNS_StartQuery(&mDNSStorage, &txt->question);
+
+ if (err)
+ {
+ freeL("handle_resolve_request", txt);
+ freeL("handle_resolve_request", srv);
+ freeL("handle_resolve_request", term);
+ freeL("handle_resolve_request", rstate->resolve_results);
+ rstate->terminate = NULL; // prevent abort_request() from invoking termination callback
+ }
+ if (deliver_error(rstate, err) < 0 || err)
+ {
+ abort_request(rstate);
+ unlink_request(rstate);
+ }
+ return;
+
+bad_param:
+ deliver_error(rstate, mStatus_BadParamErr);
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+
+malloc_error:
+ my_perror("ERROR: malloc");
+ exit(1);
+ }
+
+static void resolve_termination_callback(void *context)
+ {
+ resolve_termination_t *term = context;
+ request_state *rs;
+
+ if (!term)
+ {
+ LogMsg("ERROR: resolve_termination_callback: double termination");
+ return;
+ }
+ rs = term->rstate;
+
+ mDNS_StopQuery(&mDNSStorage, &term->txt->question);
+ mDNS_StopQuery(&mDNSStorage, &term->srv->question);
+
+ freeL("resolve_termination_callback", term->txt);
+ freeL("resolve_termination_callback", term->srv);
+ freeL("resolve_termination_callback", term);
+ rs->termination_context = NULL;
+ freeL("resolve_termination_callback", rs->resolve_results);
+ rs->resolve_results = NULL;
+ }
+
+
+
+static void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
+{
+ int len = 0;
+ char fullname[MAX_DOMAIN_NAME], target[MAX_DOMAIN_NAME];
+ char *data;
+ transfer_state result;
+ reply_state *rep;
+ request_state *rs = question->QuestionContext;
+ resolve_result_t *res = rs->resolve_results;
+ #pragma unused(m)
+
+ if (!AddRecord)
+ {
+ if (answer->rrtype == kDNSType_TXT && res->txt == answer) res->txt = mDNSNULL;
+ if (answer->rrtype == kDNSType_SRV && res->srv == answer) res->srv = mDNSNULL;
+ return;
+ }
+
+ if (answer->rrtype == kDNSType_TXT) res->txt = answer;
+ if (answer->rrtype == kDNSType_SRV) res->srv = answer;
+
+ if (!res->txt || !res->srv) return; // only deliver result to client if we have both answers
+
+ ConvertDomainNameToCString(&answer->name, fullname);
+ ConvertDomainNameToCString(&res->srv->rdata->u.srv.target, target);
+
+ // calculate reply length
+ len += sizeof(DNSServiceFlags);
+ len += sizeof(uint32_t); // interface index
+ len += sizeof(DNSServiceErrorType);
+ len += strlen(fullname) + 1;
+ len += strlen(target) + 1;
+ len += 2 * sizeof(uint16_t); // port, txtLen
+ len += res->txt->rdlength;
+
+ // allocate/init reply header
+ rep = create_reply(resolve_reply, len, rs);
+ rep->rhdr->flags = 0;
+ rep->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, answer->InterfaceID);
+ rep->rhdr->error = kDNSServiceErr_NoError;
+ data = rep->sdata;
+
+ // write reply data to message
+ put_string(fullname, &data);
+ put_string(target, &data);
+ put_short(res->srv->rdata->u.srv.port.NotAnInteger, &data);
+ put_short(res->txt->rdlength, &data);
+ put_rdata(res->txt->rdlength, res->txt->rdata->u.txt.c, &data);
+
+ result = send_msg(rep);
+ if (result == t_error || result == t_terminated)
+ {
+ abort_request(rs);
+ unlink_request(rs);
+ freeL("resolve_result_callback", rep);
+ }
+ else if (result == t_complete) freeL("resolve_result_callback", rep);
+ else append_reply(rs, rep);
+ }
+
+
+
+
+// common query issuing routine for resolve and query requests
+static mStatus do_question(request_state *rstate, domainname *name, uint32_t ifi, uint16_t rrtype, int16_t rrclass)
+ {
+ DNSQuestion *q;
+ mStatus result;
+ mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, ifi);
+ if (ifi && !InterfaceID) return(mStatus_BadParamErr);
+
+ q = mallocL("do_question", sizeof(DNSQuestion));
+ if (!q)
+ {
+ my_perror("ERROR: do_question - malloc");
+ exit(1);
+ }
+ bzero(q, sizeof(DNSQuestion));
+
+ q->QuestionContext = rstate;
+ q->QuestionCallback = question_result_callback;
+ memcpy(&q->qname, name, MAX_DOMAIN_NAME);
+ q->qtype = rrtype;
+ q->qclass = rrclass;
+ q->InterfaceID = InterfaceID;
+
+
+ rstate->termination_context = q;
+ rstate->terminate = question_termination_callback;
+
+ result = mDNS_StartQuery(&mDNSStorage, q);
+ if (result != mStatus_NoError) LogMsg("ERROR: mDNS_StartQuery: %d", (int)result);
+ return result;
+ }
+
+// what gets called when a resolve is completed and we need to send the data back to the client
+static void question_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
+ {
+ char *data;
+ char name[256];
+ request_state *req;
+ reply_state *rep;
+ int len;
+
+ #pragma unused(m)
+ //mDNS_StopQuery(m, question);
+ req = question->QuestionContext;
+
+ // calculate reply data length
+ len = sizeof(DNSServiceFlags);
+ len += 2 * sizeof(uint32_t); // if index + ttl
+ len += sizeof(DNSServiceErrorType);
+ len += 3 * sizeof(uint16_t); // type, class, rdlen
+ len += answer->rdlength;
+ ConvertDomainNameToCString(&answer->name, name);
+ len += strlen(name) + 1;
+
+ rep = create_reply(query_reply, len, req);
+ rep->rhdr->flags = AddRecord ? kDNSServiceFlagsAdd : kDNSServiceFlagsRemove;
+ rep->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, answer->InterfaceID);
+ rep->rhdr->error = kDNSServiceErr_NoError;
+ data = rep->sdata;
+
+ put_string(name, &data);
+ put_short(answer->rrtype, &data);
+ put_short(answer->rrclass, &data);
+ put_short(answer->rdlength, &data);
+ put_rdata(answer->rdlength, (char *)&answer->rdata->u, &data);
+ put_long(AddRecord ? answer->rroriginalttl : 0, &data);
+
+ append_reply(req, rep);
+ return;
+ }
+
+static void question_termination_callback(void *context)
+ {
+ DNSQuestion *q = context;
+
+
+ mDNS_StopQuery(&mDNSStorage, q); // no need to error check
+ freeL("question_termination_callback", q);
+ }
+
+
+static void handle_browse_request(request_state *request)
+ {
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex;
+ char regtype[256], domain[256];
+ DNSQuestion *q;
+ domainname typedn, domdn;
+ char *ptr;
+ mStatus result;
+
+ if (request->ts != t_complete)
+ {
+ LogMsg("ERROR: handle_browse_request - transfer state != t_complete");
+ abort_request(request);
+ unlink_request(request);
+ return;
+ }
+ q = mallocL("handle_browse_request", sizeof(DNSQuestion));
+ if (!q)
+ {
+ my_perror("ERROR: handle_browse_request - malloc");
+ exit(1);
+ }
+ bzero(q, sizeof(DNSQuestion));
+
+ // extract data from message
+ ptr = request->msgdata;
+ flags = get_flags(&ptr);
+ interfaceIndex = get_long(&ptr);
+ if (get_string(&ptr, regtype, 256) < 0 ||
+ get_string(&ptr, domain, 256) < 0)
+ goto bad_param;
+
+ freeL("handle_browse_request", request->msgbuf);
+ request->msgbuf = NULL;
+
+ mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+ if (interfaceIndex && !InterfaceID) goto bad_param;
+ q->QuestionContext = request;
+ q->QuestionCallback = browse_result_callback;
+ if (!MakeDomainNameFromDNSNameString(&typedn, regtype) ||
+ !MakeDomainNameFromDNSNameString(&domdn, domain[0] ? domain : "local."))
+ goto bad_param;
+ request->termination_context = q;
+ request->terminate = browse_termination_callback;
+ result = mDNS_StartBrowse(&mDNSStorage, q, &typedn, &domdn, InterfaceID, browse_result_callback, request);
+ deliver_error(request, result);
+ return;
+
+bad_param:
+ deliver_error(request, mStatus_BadParamErr);
+ abort_request(request);
+ unlink_request(request);
+ }
+
+static void browse_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
+ {
+ request_state *req;
+ reply_state *rep;
+ mStatus err;
+
+ #pragma unused(m)
+ req = question->QuestionContext;
+
+ err = gen_rr_response(&answer->rdata->u.name, answer->InterfaceID, req, &rep);
+ if (err)
+ {
+ if (deliver_async_error(req, browse_reply, err) < 0)
+ {
+ abort_request(req);
+ unlink_request(req);
+ }
+ return;
+ }
+ if (AddRecord) rep->rhdr->flags |= kDNSServiceFlagsAdd; // non-zero TTL indicates add
+ append_reply(req, rep);
+ return;
+ }
+
+static void browse_termination_callback(void *context)
+ {
+ DNSQuestion *q = context;
+
+ mDNS_StopBrowse(&mDNSStorage, q); // no need to error-check result
+ freeL("browse_termination_callback", q);
+ }
+
+// service registration
+static void handle_regservice_request(request_state *request)
+ {
+ DNSServiceFlags flags;
+ uint32_t ifi;
+ char name[256], regtype[256], domain[256], host[256];
+ uint16_t txtlen;
+ mDNSIPPort port;
+ void *txtdata;
+ char *ptr;
+ domainlabel n;
+ domainname t, d, h, srv;
+ registered_service *r_srv;
+ int srs_size;
+ mStatus result;
+
+ char *sub, *rtype_ptr;
+ int i, num_subtypes;
+
+
+ if (request->ts != t_complete)
+ {
+ LogMsg("ERROR: handle_regservice_request - transfer state != t_complete");
+ abort_request(request);
+ unlink_request(request);
+ return;
+ }
+
+ // extract data from message
+ ptr = request->msgdata;
+ flags = get_flags(&ptr);
+ ifi = get_long(&ptr);
+ mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, ifi);
+ if (ifi && !InterfaceID) goto bad_param;
+ if (get_string(&ptr, name, 256) < 0 ||
+ get_string(&ptr, regtype, 256) < 0 ||
+ get_string(&ptr, domain, 256) < 0 ||
+ get_string(&ptr, host, 256) < 0)
+ goto bad_param;
+
+ port.NotAnInteger = get_short(&ptr);
+ txtlen = get_short(&ptr);
+ txtdata = get_rdata(&ptr, txtlen);
+
+ // count subtypes, replacing commas w/ whitespace
+ rtype_ptr = regtype;
+ num_subtypes = -1;
+ while((sub = strsep(&rtype_ptr, ",")))
+ if (*sub) num_subtypes++;
+
+ if (!name[0]) n = (&mDNSStorage)->nicelabel;
+ else if (!MakeDomainLabelFromLiteralString(&n, name))
+ goto bad_param;
+ if ((!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) ||
+ (!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) ||
+ (!ConstructServiceName(&srv, &n, &t, &d)))
+ goto bad_param;
+ if (host[0] && !MakeDomainNameFromDNSNameString(&h, host)) goto bad_param;
+
+ r_srv = mallocL("handle_regservice_request", sizeof(registered_service));
+ if (!r_srv) goto malloc_error;
+ srs_size = sizeof(ServiceRecordSet) + (sizeof(RDataBody) > txtlen ? 0 : txtlen - sizeof(RDataBody));
+ r_srv->srs = mallocL("handle_regservice_request", srs_size);
+ if (!r_srv->srs) goto malloc_error;
+ if (num_subtypes > 0)
+ {
+ r_srv->subtypes = mallocL("handle_regservice_request", num_subtypes * sizeof(AuthRecord));
+ if (!r_srv->subtypes) goto malloc_error;
+ sub = regtype + strlen(regtype) + 1;
+ for (i = 0; i < num_subtypes; i++)
+ {
+ if (!MakeDomainNameFromDNSNameString(&(r_srv->subtypes + i)->resrec.name, sub))
+ {
+ freeL("handle_regservice_request", r_srv->subtypes);
+ freeL("handle_regservice_request", r_srv);
+ r_srv = NULL;
+ goto bad_param;
+ }
+ sub += strlen(sub) + 1;
+ }
+ }
+ else r_srv->subtypes = NULL;
+ r_srv->request = request;
+
+ r_srv->autoname = (!name[0]);
+ r_srv->rename_on_memfree = 0;
+ r_srv->renameonconflict = !(flags & kDNSServiceFlagsNoAutoRename);
+ r_srv->name = n;
+ request->termination_context = r_srv;
+ request->terminate = regservice_termination_callback;
+ request->service = r_srv;
+
+ result = mDNS_RegisterService(&mDNSStorage, r_srv->srs, &n, &t, &d, host[0] ? &h : NULL, port,
+ txtdata, txtlen, r_srv->subtypes, num_subtypes, InterfaceID, regservice_callback, r_srv);
+ deliver_error(request, result);
+ if (result != mStatus_NoError)
+ {
+ abort_request(request);
+ unlink_request(request);
+ }
+ else
+ {
+ reset_connected_rstate(request); // reset to receive add/remove messages
+ }
+ return;
+
+bad_param:
+ deliver_error(request, mStatus_BadParamErr);
+ abort_request(request);
+ unlink_request(request);
+return;
+
+malloc_error:
+ my_perror("ERROR: malloc");
+ exit(1);
+ }
+
+// service registration callback performs three duties - frees memory for deregistered services,
+// handles name conflicts, and delivers completed registration information to the client (via
+// process_service_registraion())
+
+static void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result)
+ {
+ mStatus err;
+ ExtraResourceRecord *extra;
+ registered_service *r_srv = srs->ServiceContext;
+ request_state *rs = r_srv->request;
+
+ #pragma unused(m)
+
+ if (!rs && (result != mStatus_MemFree && !r_srv->rename_on_memfree))
+ {
+ // error should never happen - safest to log and continue
+ LogMsg("ERROR: regservice_callback: received result %d with a NULL request pointer\n");
+ return;
+ }
+
+ if (result == mStatus_NoError)
+ return process_service_registration(srs);
+ else if (result == mStatus_MemFree)
+ {
+ if (r_srv->rename_on_memfree)
+ {
+ r_srv->rename_on_memfree = 0;
+ r_srv->name = mDNSStorage.nicelabel;
+ err = mDNS_RenameAndReregisterService(&mDNSStorage, srs, &r_srv->name);
+ if (err) LogMsg("ERROR: regservice_callback - RenameAndReregisterService returned %d", err);
+ // error should never happen - safest to log and continue
+ }
+ else
+ {
+ while (r_srv->srs->Extras)
+ {
+ extra = r_srv->srs->Extras;
+ r_srv->srs->Extras = r_srv->srs->Extras->next;
+ freeL("regservice_callback", extra);
+ }
+ freeL("regservice_callback", r_srv->srs);
+ if (r_srv->subtypes) freeL("regservice_callback", r_srv->subtypes);
+ if (r_srv->request) r_srv->request->service = NULL;
+ freeL("regservice_callback", r_srv);
+ return;
+ }
+ }
+ else if (result == mStatus_NameConflict)
+ {
+ if (r_srv->autoname || r_srv->renameonconflict)
+ {
+ mDNS_RenameAndReregisterService(&mDNSStorage, srs, mDNSNULL);
+ return;
+ }
+ else
+ {
+ freeL("regservice_callback", r_srv);
+ freeL("regservice_callback", r_srv->srs);
+ if (r_srv->subtypes) freeL("regservice_callback", r_srv->subtypes);
+ if (r_srv->request) r_srv->request->service = NULL;
+ freeL("regservice_callback", r_srv);
+ if (deliver_async_error(rs, reg_service_reply, result) < 0)
+ {
+ abort_request(rs);
+ unlink_request(rs);
+ }
+ return;
+ }
+ }
+ else
+ {
+ LogMsg("ERROR: unknown result in regservice_callback");
+ if (deliver_async_error(rs, reg_service_reply, result) < 0)
+ {
+ abort_request(rs);
+ unlink_request(rs);
+ }
+ return;
+ }
+ }
+
+static void handle_add_request(request_state *rstate)
+ {
+ registered_record_entry *re;
+ ExtraResourceRecord *extra;
+ uint32_t size, ttl;
+ uint16_t rrtype, rdlen;
+ char *ptr, *rdata;
+ mStatus result;
+ DNSServiceFlags flags;
+ ServiceRecordSet *srs = rstate->service->srs;
+
+ if (!srs)
+ {
+ LogMsg("ERROR: handle_add_request - no service record set in request state");
+ deliver_error(rstate, mStatus_UnknownErr);
+ return;
+ }
+
+ ptr = rstate->msgdata;
+ flags = get_flags(&ptr);
+ rrtype = get_short(&ptr);
+ rdlen = get_short(&ptr);
+ rdata = get_rdata(&ptr, rdlen);
+ ttl = get_long(&ptr);
+
+ if (rdlen > sizeof(RDataBody)) size = rdlen;
+ else size = sizeof(RDataBody);
+
+ extra = mallocL("hanle_add_request", sizeof(ExtraResourceRecord) - sizeof(RDataBody) + size);
+ if (!extra)
+ {
+ my_perror("ERROR: malloc");
+ exit(1);
+ }
+
+ bzero(extra, sizeof(ExtraResourceRecord)); // OK if oversized rdata not zero'd
+ extra->r.resrec.rrtype = rrtype;
+ extra->r.rdatastorage.MaxRDLength = size;
+ extra->r.resrec.rdlength = rdlen;
+ memcpy(&extra->r.rdatastorage.u.data, rdata, rdlen);
+ result = mDNS_AddRecordToService(&mDNSStorage, srs , extra, &extra->r.rdatastorage, ttl);
+ deliver_error(rstate, result);
+ reset_connected_rstate(rstate);
+ if (result)
+ {
+ freeL("handle_add_request", rstate->msgbuf);
+ rstate->msgbuf = NULL;
+ freeL("handle_add_request", extra);
+ return;
+ }
+ re = mallocL("handle_add_request", sizeof(registered_record_entry));
+ if (!re)
+ {
+ my_perror("ERROR: malloc");
+ exit(1);
+ }
+ re->key = rstate->hdr.reg_index;
+ re->rr = &extra->r;
+ re->next = rstate->reg_recs;
+ rstate->reg_recs = re;
+ }
+
+static void handle_update_request(request_state *rstate)
+ {
+ registered_record_entry *reptr;
+ AuthRecord *rr;
+ RData *newrd;
+ uint16_t rdlen, rdsize;
+ char *ptr, *rdata;
+ uint32_t ttl;
+ mStatus result;
+
+ if (rstate->hdr.reg_index == TXT_RECORD_INDEX)
+ {
+ if (!rstate->service)
+ {
+ deliver_error(rstate, mStatus_BadParamErr);
+ return;
+ }
+ rr = &rstate->service->srs->RR_TXT;
+ }
+ else
+ {
+ reptr = rstate->reg_recs;
+ while(reptr && reptr->key != rstate->hdr.reg_index) reptr = reptr->next;
+ if (!reptr) deliver_error(rstate, mStatus_BadReferenceErr);
+ rr = reptr->rr;
+ }
+
+ ptr = rstate->msgdata;
+ get_flags(&ptr); // flags unused
+ rdlen = get_short(&ptr);
+ rdata = get_rdata(&ptr, rdlen);
+ ttl = get_long(&ptr);
+
+ if (rdlen > sizeof(RDataBody)) rdsize = rdlen;
+ else rdsize = sizeof(RDataBody);
+ newrd = mallocL("handle_update_request", sizeof(RData) - sizeof(RDataBody) + rdsize);
+ if (!newrd)
+ {
+ my_perror("ERROR: malloc");
+ exit(1);
+ }
+ newrd->MaxRDLength = rdsize;
+ memcpy(&newrd->u, rdata, rdlen);
+ result = mDNS_Update(&mDNSStorage, rr, ttl, rdlen, newrd, update_callback);
+ deliver_error(rstate, result);
+ reset_connected_rstate(rstate);
+ }
+
+static void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd)
+ {
+ #pragma unused(m)
+
+ if (oldrd != &rr->rdatastorage) freeL("update_callback", oldrd);
+ }
+
+static void process_service_registration(ServiceRecordSet *const srs)
+ {
+ reply_state *rep;
+ transfer_state send_result;
+ mStatus err;
+ registered_service *r_srv = srs->ServiceContext;
+ request_state *req = r_srv->request;
+
+
+ err = gen_rr_response(&srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, req, &rep);
+ if (err)
+ {
+ if (deliver_async_error(req, reg_service_reply, err) < 0)
+ {
+ abort_request(req);
+ unlink_request(req);
+ }
+ return;
+ }
+ send_result = send_msg(rep);
+ if (send_result == t_error || send_result == t_terminated)
+ {
+ abort_request(req);
+ unlink_request(req);
+ freeL("process_service_registration", rep);
+ }
+ else if (send_result == t_complete) freeL("process_service_registration", rep);
+ else append_reply(req, rep);
+ }
+
+static void regservice_termination_callback(void *context)
+ {
+ registered_service *srv = context;
+
+ // only safe to free memory if registration is not valid, ie deregister fails
+ if (mDNS_DeregisterService(&mDNSStorage, srv->srs) != mStatus_NoError)
+ {
+ freeL("regservice_callback", srv->srs);
+ if (srv->subtypes) freeL("regservice_callback", srv->subtypes);
+ freeL("regservice_callback", srv);
+ freeL("regservice_termination_callback", srv);
+ }
+ }
+
+
+static void handle_regrecord_request(request_state *rstate)
+ {
+ AuthRecord *rr;
+ regrecord_callback_context *rcc;
+ registered_record_entry *re;
+ mStatus result;
+
+ if (rstate->ts != t_complete)
+ {
+ LogMsg("ERROR: handle_regrecord_request - transfer state != t_complete");
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+
+ rr = read_rr_from_ipc_msg(rstate->msgdata, 1);
+ if (!rr)
+ {
+ deliver_error(rstate, mStatus_BadParamErr);
+ return;
+ }
+
+ rcc = mallocL("hanlde_regrecord_request", sizeof(regrecord_callback_context));
+ if (!rcc) goto malloc_error;
+ rcc->rstate = rstate;
+ rcc->client_context = rstate->hdr.client_context;
+ rr->RecordContext = rcc;
+ rr->RecordCallback = regrecord_callback;
+
+ // allocate registration entry, link into list
+ re = mallocL("hanlde_regrecord_request", sizeof(registered_record_entry));
+ if (!re) goto malloc_error;
+ re->key = rstate->hdr.reg_index;
+ re->rr = rr;
+ re->next = rstate->reg_recs;
+ rstate->reg_recs = re;
+
+ if (!rstate->terminate)
+ {
+ rstate->terminate = connected_registration_termination;
+ rstate->termination_context = rstate;
+ }
+
+ result = mDNS_Register(&mDNSStorage, rr);
+ deliver_error(rstate, result);
+ reset_connected_rstate(rstate);
+ return;
+
+malloc_error:
+ my_perror("ERROR: malloc");
+ return;
+ }
+
+static void regrecord_callback(mDNS *const m, AuthRecord *const rr, mStatus result)
+ {
+ regrecord_callback_context *rcc;
+ int len;
+ reply_state *reply;
+ transfer_state ts;
+
+ #pragma unused(m)
+
+ if (result == mStatus_MemFree) { freeL("regrecord_callback", rr); return; }
+ rcc = rr->RecordContext;
+
+ // format result, add to the list for the request, including the client context in the header
+ len = sizeof(DNSServiceFlags);
+ len += sizeof(uint32_t); //interfaceIndex
+ len += sizeof(DNSServiceErrorType);
+
+ reply = create_reply(reg_record_reply, len, rcc->rstate);
+ reply->mhdr->client_context = rcc->client_context;
+ reply->rhdr->flags = 0;
+ reply->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, rr->resrec.InterfaceID);
+ reply->rhdr->error = result;
+
+ ts = send_msg(reply);
+ if (ts == t_error || ts == t_terminated)
+ {
+ abort_request(rcc->rstate);
+ unlink_request(rcc->rstate);
+ }
+ else if (ts == t_complete) freeL("regrecord_callback", reply);
+ else if (ts == t_morecoming) append_reply(rcc->rstate, reply); // client is blocked, link reply into list
+ }
+
+static void connected_registration_termination(void *context)
+ {
+ registered_record_entry *fptr, *ptr = ((request_state *)context)->reg_recs;
+ while(ptr)
+ {
+ mDNS_Deregister(&mDNSStorage, ptr->rr);
+ fptr = ptr;
+ ptr = ptr->next;
+ freeL("connected_registration_termination", fptr);
+ }
+ }
+
+
+
+static void handle_removerecord_request(request_state *rstate)
+ {
+ registered_record_entry *reptr, *prev = NULL;
+ mStatus err = mStatus_UnknownErr;
+ char *ptr;
+ reptr = rstate->reg_recs;
+
+ ptr = rstate->msgdata;
+ get_flags(&ptr); // flags unused
+
+ while(reptr)
+ {
+ if (reptr->key == rstate->hdr.reg_index) // found match
+ {
+ if (prev) prev->next = reptr->next;
+ else rstate->reg_recs = reptr->next;
+ err = mDNS_Deregister(&mDNSStorage, reptr->rr);
+ freeL("handle_removerecord_request", reptr); //rr gets freed by callback
+ break;
+ }
+ prev = reptr;
+ reptr = reptr->next;
+ }
+ reset_connected_rstate(rstate);
+ if (deliver_error(rstate, err) < 0)
+ {
+ abort_request(rstate);
+ unlink_request(rstate);
+ }
+ }
+
+
+// domain enumeration
+static void handle_enum_request(request_state *rstate)
+ {
+ DNSServiceFlags flags, add_default;
+ uint32_t ifi;
+ char *ptr = rstate->msgdata;
+ domain_enum_t *def, *all;
+ enum_termination_t *term;
+ reply_state *reply; // initial default reply
+ transfer_state tr;
+ mStatus err;
+ int result;
+
+ if (rstate->ts != t_complete)
+ {
+ LogMsg("ERROR: handle_enum_request - transfer state != t_complete");
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+
+ flags = get_flags(&ptr);
+ ifi = get_long(&ptr);
+ mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, ifi);
+ if (ifi && !InterfaceID)
+ {
+ deliver_error(rstate, mStatus_BadParamErr);
+ abort_request(rstate);
+ unlink_request(rstate);
+ }
+
+ // allocate context structures
+ def = mallocL("hanlde_enum_request", sizeof(domain_enum_t));
+ all = mallocL("handle_enum_request", sizeof(domain_enum_t));
+ term = mallocL("handle_enum_request", sizeof(enum_termination_t));
+ if (!def || !all || !term)
+ {
+ my_perror("ERROR: malloc");
+ exit(1);
+ }
+
+ // enumeration requires multiple questions, so we must link all the context pointers so that
+ // necessary context can be reached from the callbacks
+ def->rstate = rstate;
+ all->rstate = rstate;
+ term->def = def;
+ term->all = all;
+ term->rstate = rstate;
+ rstate->termination_context = term;
+ rstate->terminate = enum_termination_callback;
+ def->question.QuestionContext = def;
+ def->type = (flags & kDNSServiceFlagsRegistrationDomains) ?
+ mDNS_DomainTypeRegistrationDefault: mDNS_DomainTypeBrowseDefault;
+ all->question.QuestionContext = all;
+ all->type = (flags & kDNSServiceFlagsRegistrationDomains) ?
+ mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse;
+
+ // make the calls
+ err = mDNS_GetDomains(&mDNSStorage, &all->question, all->type, InterfaceID, enum_result_callback, all);
+ if (err == mStatus_NoError)
+ err = mDNS_GetDomains(&mDNSStorage, &def->question, def->type, InterfaceID, enum_result_callback, def);
+ result = deliver_error(rstate, err); // send error *before* returning local domain
+
+ if (result < 0 || err)
+ {
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+
+ // provide local. as the first domain automatically
+ add_default = kDNSServiceFlagsDefault | kDNSServiceFlagsAdd | kDNSServiceFlagsFinished;
+ reply = format_enumeration_reply(rstate, "local.", add_default, ifi, 0);
+ tr = send_msg(reply);
+ if (tr == t_error || tr == t_terminated)
+ {
+ freeL("handle_enum_request", def);
+ freeL("handle_enum_request", all);
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+ if (tr == t_complete) freeL("handle_enum_request", reply);
+ if (tr == t_morecoming) append_reply(rstate, reply); // couldn't send whole reply because client is blocked - link into list
+ }
+
+static void enum_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
+ {
+ char domain[256];
+ domain_enum_t *de = question->QuestionContext;
+ DNSServiceFlags flags = 0;
+ reply_state *reply;
+
+ #pragma unused(m)
+ if (answer->rrtype != kDNSType_PTR) return;
+ if (AddRecord)
+ {
+ flags |= kDNSServiceFlagsAdd;
+ if (de->type == mDNS_DomainTypeRegistrationDefault || de->type == mDNS_DomainTypeBrowseDefault)
+ flags |= kDNSServiceFlagsDefault;
+ }
+ else
+ {
+ flags |= kDNSServiceFlagsRemove;
+ }
+ ConvertDomainNameToCString(&answer->rdata->u.name, domain);
+ reply = format_enumeration_reply(de->rstate, domain, flags, mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, answer->InterfaceID), kDNSServiceErr_NoError);
+ if (!reply)
+ {
+ LogMsg("ERROR: enum_result_callback, format_enumeration_reply");
+ return;
+ }
+ reply->next = NULL;
+ append_reply(de->rstate, reply);
+ return;
+ }
+
+static reply_state *format_enumeration_reply(request_state *rstate, char *domain, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err)
+ {
+ int len;
+ reply_state *reply;
+ char *data;
+
+
+ len = sizeof(DNSServiceFlags);
+ len += sizeof(uint32_t);
+ len += sizeof(DNSServiceErrorType);
+ len += strlen(domain) + 1;
+
+ reply = create_reply(enumeration_reply, len, rstate);
+ reply->rhdr->flags = flags;
+ reply->rhdr->ifi = ifi;
+ reply->rhdr->error = err;
+ data = reply->sdata;
+ put_string(domain, &data);
+ return reply;
+ }
+
+static void enum_termination_callback(void *context)
+ {
+ enum_termination_t *t = context;
+ mDNS *coredata = &mDNSStorage;
+
+ mDNS_StopGetDomains(coredata, &t->all->question);
+ mDNS_StopGetDomains(coredata, &t->def->question);
+ freeL("enum_termination_callback", t->all);
+ freeL("enum_termination_callback", t->def);
+ t->rstate->termination_context = NULL;
+ freeL("enum_termination_callback", t);
+ }
+
+static void handle_reconfirm_request(request_state *rstate)
+ {
+ AuthRecord *rr;
+
+ rr = read_rr_from_ipc_msg(rstate->msgdata, 0);
+ if (!rr) return;
+ mDNS_ReconfirmByValue(&mDNSStorage, &rr->resrec);
+ abort_request(rstate);
+ unlink_request(rstate);
+ freeL("handle_reconfirm_request", rr);
+ }
+
+
+// setup rstate to accept new reg/dereg requests
+static void reset_connected_rstate(request_state *rstate)
+ {
+ rstate->ts = t_morecoming;
+ rstate->hdr_bytes = 0;
+ rstate->data_bytes = 0;
+ if (rstate->msgbuf) freeL("reset_connected_rstate", rstate->msgbuf);
+ rstate->msgbuf = NULL;
+ rstate->bufsize = 0;
+ }
+
+
+
+// returns a resource record (allocated w/ malloc) containing the data found in an IPC message
+// data must be in format flags, interfaceIndex, name, rrtype, rrclass, rdlen, rdata, (optional)ttl
+// (ttl only extracted/set if ttl argument is non-zero). returns NULL for a bad-parameter error
+static AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int ttl)
+ {
+ char *rdata, name[256];
+ AuthRecord *rr;
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex;
+ uint16_t type, class, rdlen;
+ int storage_size;
+
+ flags = get_flags(&msgbuf);
+ interfaceIndex = get_long(&msgbuf);
+ if (get_string(&msgbuf, name, 256) < 0)
+ {
+ LogMsg("ERROR: read_rr_from_ipc_msg - get_string");
+ return NULL;
+ }
+ type = get_short(&msgbuf);
+ class = get_short(&msgbuf);
+ rdlen = get_short(&msgbuf);
+
+ if (rdlen > sizeof(RDataBody)) storage_size = rdlen;
+ else storage_size = sizeof(RDataBody);
+
+ rr = mallocL("read_rr_from_ipc_msg", sizeof(AuthRecord) - sizeof(RDataBody) + storage_size);
+ if (!rr)
+ {
+ my_perror("ERROR: malloc");
+ exit(1);
+ }
+ bzero(rr, sizeof(AuthRecord)); // ok if oversized rdata not zero'd
+ rr->resrec.rdata = &rr->rdatastorage;
+ rr->resrec.InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+ if (!MakeDomainNameFromDNSNameString(&rr->resrec.name, name))
+ {
+ LogMsg("ERROR: bad name: %s", name);
+ freeL("read_rr_from_ipc_msg", rr);
+ return NULL;
+ }
+ rr->resrec.rrtype = type;
+ if ((flags & kDNSServiceFlagsShared) == kDNSServiceFlagsShared)
+ rr->resrec.RecordType = kDNSRecordTypeShared;
+ if ((flags & kDNSServiceFlagsUnique) == kDNSServiceFlagsUnique)
+ rr->resrec.RecordType = kDNSRecordTypeUnique;
+ rr->resrec.rrclass = class;
+ rr->resrec.rdlength = rdlen;
+ rr->resrec.rdata->MaxRDLength = rdlen;
+ rdata = get_rdata(&msgbuf, rdlen);
+ memcpy(rr->resrec.rdata->u.data, rdata, rdlen);
+ if (ttl)
+ {
+ rr->resrec.rroriginalttl = get_long(&msgbuf);
+ }
+ return rr;
+ }
+
+
+// generate a response message for a browse result, service registration result, or any other call with the
+// identical callback signature. on successful completion rep is set to point to a malloc'd reply_state struct,
+// and mStatus_NoError is returned. otherwise the appropriate error is returned.
+
+static mStatus gen_rr_response(domainname *servicename, mDNSInterfaceID id, request_state *request, reply_state **rep)
+ {
+ char *data;
+ int len;
+ domainlabel name;
+ domainname type, dom;
+ char namestr[256], typestr[256], domstr[256];
+
+ *rep = NULL;
+
+ if (!DeconstructServiceName(servicename, &name, &type, &dom))
+ return kDNSServiceErr_Unknown;
+
+ ConvertDomainLabelToCString_unescaped(&name, namestr);
+ ConvertDomainNameToCString(&type, typestr);
+ ConvertDomainNameToCString(&dom, domstr);
+
+ // calculate reply data length
+ len = sizeof(DNSServiceFlags);
+ len += sizeof(uint32_t); // if index
+ len += sizeof(DNSServiceErrorType);
+ len += strlen(namestr) + 1;
+ len += strlen(typestr) + 1;
+ len += strlen(domstr) + 1;
+
+ *rep = create_reply(query_reply, len, request);
+ (*rep)->rhdr->flags = 0;
+ (*rep)->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id);
+ (*rep)->rhdr->error = kDNSServiceErr_NoError;
+ data = (*rep)->sdata;
+
+ put_string(namestr, &data);
+ put_string(typestr, &data);
+ put_string(domstr, &data);
+ return mStatus_NoError;
+ }
+
+
+static int build_domainname_from_strings(domainname *srv, char *name, char *regtype, char *domain)
+ {
+ domainlabel n;
+ domainname d, t;
+
+ if (!MakeDomainLabelFromLiteralString(&n, name)) return -1;
+ if (!MakeDomainNameFromDNSNameString(&t, regtype)) return -1;
+ if (!MakeDomainNameFromDNSNameString(&d, domain)) return -1;
+ if (!ConstructServiceName(srv, &n, &t, &d)) return -1;
+ return 0;
+ }
+
+
+// append a reply to the list in a request object
+static void append_reply(request_state *req, reply_state *rep)
+ {
+ reply_state *ptr;
+
+ if (!req->replies) req->replies = rep;
+ else
+ {
+ ptr = req->replies;
+ while (ptr->next) ptr = ptr->next;
+ ptr->next = rep;
+ }
+ rep->next = NULL;
+ }
+
+
+// read_msg may be called any time when the transfer state (rs->ts) is t_morecoming.
+// returns the current state of the request (morecoming, error, complete, terminated.)
+// if there is no data on the socket, the socket will be closed and t_terminated will be returned
+static int read_msg(request_state *rs)
+ {
+ uint32_t nleft;
+ int nread;
+ char buf[4]; // dummy for death notification
+
+ if (rs->ts == t_terminated || rs->ts == t_error)
+ {
+ LogMsg("ERROR: read_msg called with transfer state terminated or error");
+ rs->ts = t_error;
+ return t_error;
+ }
+
+ if (rs->ts == t_complete)
+ { // this must be death or something is wrong
+ nread = recv(rs->sd, buf, 4, 0);
+ if (!nread) { rs->ts = t_terminated; return t_terminated; }
+ if (nread < 0) goto rerror;
+ LogMsg("ERROR: read data from a completed request.");
+ rs->ts = t_error;
+ return t_error;
+ }
+
+ if (rs->ts != t_morecoming)
+ {
+ LogMsg("ERROR: read_msg called with invalid transfer state (%d)", rs->ts);
+ rs->ts = t_error;
+ return t_error;
+ }
+
+ if (rs->hdr_bytes < sizeof(ipc_msg_hdr))
+ {
+ nleft = sizeof(ipc_msg_hdr) - rs->hdr_bytes;
+ nread = recv(rs->sd, (char *)&rs->hdr + rs->hdr_bytes, nleft, 0);
+ if (nread == 0) { rs->ts = t_terminated; return t_terminated; }
+ if (nread < 0) goto rerror;
+ rs->hdr_bytes += nread;
+ if (rs->hdr_bytes > sizeof(ipc_msg_hdr))
+ {
+ LogMsg("ERROR: read_msg - read too many header bytes");
+ rs->ts = t_error;
+ return t_error;
+ }
+ }
+
+ // only read data if header is complete
+ if (rs->hdr_bytes == sizeof(ipc_msg_hdr))
+ {
+ if (rs->hdr.datalen == 0) // ok in removerecord requests
+ {
+ rs->ts = t_complete;
+ rs->msgbuf = NULL;
+ return t_complete;
+ }
+
+ if (!rs->msgbuf) // allocate the buffer first time through
+ {
+ rs->msgbuf = mallocL("read_msg", rs->hdr.datalen);
+ if (!rs->msgbuf)
+ {
+ my_perror("ERROR: malloc");
+ rs->ts = t_error;
+ return t_error;
+ }
+ rs->msgdata = rs->msgbuf;
+ }
+ nleft = rs->hdr.datalen - rs->data_bytes;
+ nread = recv(rs->sd, rs->msgbuf + rs->data_bytes, nleft, 0);
+ if (nread == 0) { rs->ts = t_terminated; return t_terminated; }
+ if (nread < 0) goto rerror;
+ rs->data_bytes += nread;
+ if (rs->data_bytes > rs->hdr.datalen)
+ {
+ LogMsg("ERROR: read_msg - read too many data bytes");
+ rs->ts = t_error;
+ return t_error;
+ }
+ }
+
+ if (rs->hdr_bytes == sizeof(ipc_msg_hdr) && rs->data_bytes == rs->hdr.datalen)
+ rs->ts = t_complete;
+ else rs->ts = t_morecoming;
+
+ return rs->ts;
+
+rerror:
+ if (errno == EAGAIN || errno == EINTR) return rs->ts;
+ my_perror("ERROR: read_msg");
+ rs->ts = t_error;
+ return t_error;
+ }
+
+
+static int send_msg(reply_state *rs)
+ {
+ ssize_t nwriten;
+
+ if (!rs->msgbuf)
+ {
+ LogMsg("ERROR: send_msg called with NULL message buffer");
+ return t_error;
+ }
+
+ if (rs->request->no_reply) //!!!KRS this behavior should be optimized if it becomes more common
+ {
+ rs->ts = t_complete;
+ freeL("send_msg", rs->msgbuf);
+ return t_complete;
+ }
+
+ nwriten = send(rs->sd, rs->msgbuf + rs->nwriten, rs->len - rs->nwriten, 0);
+ if (nwriten < 0)
+ {
+ if (errno == EINTR || errno == EAGAIN) nwriten = 0;
+ else
+ {
+ if (errno == EPIPE)
+ {
+ LogMsg("broken pipe - cleanup should be handled by run-loop read wakeup");
+ rs->ts = t_terminated;
+ rs->request->ts = t_terminated;
+ return t_terminated;
+ }
+ else
+ {
+ my_perror("ERROR: send\n");
+ rs->ts = t_error;
+ return t_error;
+ }
+ }
+ }
+ rs->nwriten += nwriten;
+
+ if (rs->nwriten == rs->len)
+ {
+ rs->ts = t_complete;
+ freeL("send_msg", rs->msgbuf);
+ }
+ return rs->ts;
+ }
+
+
+
+static reply_state *create_reply(reply_op_t op, int datalen, request_state *request)
+{
+ reply_state *reply;
+ int totallen;
+
+
+ if ((unsigned)datalen < sizeof(reply_hdr))
+ {
+ LogMsg("ERROR: create_reply - data length less than lenght of required fields");
+ return NULL;
+ }
+
+ totallen = datalen + sizeof(ipc_msg_hdr);
+ reply = mallocL("create_reply", sizeof(reply_state));
+ if (!reply)
+ {
+ my_perror("ERROR: malloc");
+ exit(1);
+ }
+ bzero(reply, sizeof(reply_state));
+ reply->ts = t_morecoming;
+ reply->sd = request->sd;
+ reply->request = request;
+ reply->len = totallen;
+ reply->msgbuf = mallocL("create_reply", totallen);
+ if (!reply->msgbuf)
+ {
+ my_perror("ERROR: malloc");
+ exit(1);
+ }
+ bzero(reply->msgbuf, totallen);
+ reply->mhdr = (ipc_msg_hdr *)reply->msgbuf;
+ reply->rhdr = (reply_hdr *)(reply->msgbuf + sizeof(ipc_msg_hdr));
+ reply->sdata = reply->msgbuf + sizeof(ipc_msg_hdr) + sizeof(reply_hdr);
+ reply->mhdr->version = VERSION;
+ reply->mhdr->op.reply_op = op;
+ reply->mhdr->datalen = totallen - sizeof(ipc_msg_hdr);
+ return reply;
+ }
+
+
+static int deliver_error(request_state *rstate, mStatus err)
+ {
+ int nwritten = -1;
+ undelivered_error_t *undeliv;
+
+ nwritten = send(rstate->errfd, &err, sizeof(mStatus), 0);
+ if (nwritten < (int)sizeof(mStatus))
+ {
+ if (errno == EINTR || errno == EAGAIN)
+ nwritten = 0;
+ if (nwritten < 0)
+ {
+ my_perror("ERROR: send - unable to deliver error to client");
+ goto error;
+ }
+ //client blocked - store result and come backr
+ undeliv = mallocL("deliver_error", sizeof(undelivered_error_t));
+ if (!undeliv)
+ {
+ my_perror("ERROR: malloc");
+ exit(1);
+ }
+ undeliv->err = err;
+ undeliv->nwritten = nwritten;
+ undeliv->sd = rstate->errfd;
+ rstate->u_err = undeliv;
+ return 0;
+ }
+ if (rstate->errfd != rstate->sd) close(rstate->errfd);
+ return 0;
+
+error:
+ if (rstate->errfd != rstate->sd) close(rstate->errfd);
+ return -1;
+
+ }
+
+
+// returns 0 on success, -1 if send is incomplete, or on terminal failre (request is aborted)
+static transfer_state send_undelivered_error(request_state *rs)
+ {
+ int nwritten;
+
+ nwritten = send(rs->u_err->sd, (char *)(&rs->u_err) + rs->u_err->nwritten, sizeof(mStatus) - rs->u_err->nwritten, 0);
+ if (nwritten < 0)
+ {
+ if (errno == EINTR || errno == EAGAIN)
+ nwritten = 0;
+ else
+ {
+ my_perror("ERROR: send - unable to deliver error to client\n");
+ if (rs->u_err->sd == rs->sd) close (rs->u_err->sd);
+ return t_error;
+ }
+ }
+ if (nwritten + rs->u_err->nwritten == sizeof(mStatus))
+ {
+ if (rs->u_err->sd == rs->sd) close(rs->u_err->sd);
+ freeL("send_undelivered_error", rs->u_err);
+ rs->u_err = NULL;
+ return t_complete;
+ }
+ rs->u_err->nwritten += nwritten;
+ return t_morecoming;
+ }
+
+
+// send bogus data along with an error code to the app callback
+// returns 0 on success (linking reply into list of not fully delivered),
+// -1 on failure (request should be aborted)
+static int deliver_async_error(request_state *rs, reply_op_t op, mStatus err)
+ {
+ int len;
+ reply_state *reply;
+ transfer_state ts;
+
+ if (rs->no_reply) return 0;
+ len = 256; // long enough for any reply handler to read all args w/o buffer overrun
+ reply = create_reply(op, len, rs);
+ reply->rhdr->error = err;
+ ts = send_msg(reply);
+ if (ts == t_error || ts == t_terminated)
+ {
+ freeL("deliver_async_error", reply);
+ return -1;
+ }
+ else if (ts == t_complete) freeL("deliver_async_error", reply);
+ else if (ts == t_morecoming) append_reply(rs, reply); // client is blocked, link reply into list
+ return 0;
+ }
+
+
+static void abort_request(request_state *rs)
+ {
+ reply_state *rep, *ptr;
+
+ if (rs->terminate) rs->terminate(rs->termination_context); // terminate field may not be set yet
+ if (rs->msgbuf) freeL("abort_request", rs->msgbuf);
+ CFRunLoopRemoveSource(CFRunLoopGetCurrent(), rs->rls, kCFRunLoopDefaultMode);
+ CFRunLoopSourceInvalidate(rs->rls);
+ CFRelease(rs->rls);
+ CFSocketInvalidate(rs->sr);
+ CFRelease(rs->sr);
+ rs->sd = -1;
+ if (rs->errfd >= 0) close(rs->errfd);
+ rs->errfd = -1;
+
+ // free pending replies
+ rep = rs->replies;
+ while(rep)
+ {
+ if (rep->msgbuf) freeL("abort_request", rep->msgbuf);
+ ptr = rep;
+ rep = rep->next;
+ freeL("abort_request", ptr);
+ }
+
+ if (rs->u_err)
+ {
+ freeL("abort_request", rs->u_err);
+ rs->u_err = NULL;
+ }
+ }
+
+
+static void unlink_request(request_state *rs)
+ {
+ request_state *ptr;
+
+ if (rs == all_requests)
+ {
+ all_requests = all_requests->next;
+ freeL("unlink_request", rs);
+ return;
+ }
+ for(ptr = all_requests; ptr->next; ptr = ptr->next)
+ if (ptr->next == rs)
+ {
+ ptr->next = rs->next;
+ freeL("unlink_request", rs);
+ return;
+ }
+ }
+
+
+
+//hack to search-replace perror's to LogMsg's
+static void my_perror(char *errmsg)
+ {
+ LogMsg("%s: %s", errmsg, strerror(errno));
+ }
+
+
--- /dev/null
+/*
+ * 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: Client.c,v $
+Revision 1.9 2003/08/14 02:19:55 cheshire
+<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
+
+Revision 1.8 2003/08/12 19:56:26 cheshire
+Update to APSL 2.0
+
+Revision 1.7 2003/07/02 21:19:58 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.6 2003/06/18 05:48:41 cheshire
+Fix warnings
+
+Revision 1.5 2003/05/06 00:00:50 cheshire
+<rdar://problem/3248914> Rationalize naming of domainname manipulation functions
+
+Revision 1.4 2002/12/23 22:13:31 jgraessl
+
+Reviewed by: Stuart Cheshire
+Initial IPv6 support for mDNSResponder.
+
+Revision 1.3 2002/09/21 20:44:53 zarzycki
+Added APSL info
+
+Revision 1.2 2002/09/19 04:20:44 cheshire
+Remove high-ascii characters that confuse some systems
+
+Revision 1.1 2002/09/17 06:24:35 cheshire
+First checkin
+
+*/
+
+#include <assert.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "mDNSClientAPI.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"
+
+// Globals
+static mDNS mDNSStorage; // mDNS core uses this to store its globals
+static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals
+#define RR_CACHE_SIZE 500
+static CacheRecord gRRCache[RR_CACHE_SIZE];
+
+static const char *gProgramName = "mDNSResponderPosix";
+
+static void BrowseCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
+ // A callback from the core mDNS code that indicates that we've received a
+ // response to our query. Note that this code runs on the main thread
+ // (in fact, there is only one thread!), so we can safely printf the results.
+{
+ domainlabel name;
+ domainname type;
+ domainname domain;
+ char nameC[256];
+ char typeC[256];
+ char domainC[256];
+ const char *state;
+
+ (void)m; // Unused
+ (void)question; // Unused
+
+ assert(answer->rrtype == kDNSType_PTR);
+
+ DeconstructServiceName(&answer->rdata->u.name, &name, &type, &domain);
+
+ ConvertDomainLabelToCString_unescaped(&name, nameC);
+ ConvertDomainNameToCString(&type, typeC);
+ ConvertDomainNameToCString(&domain, domainC);
+
+ // If the TTL has hit 0, the service is no longer available.
+ if (!AddRecord) {
+ state = "Lost ";
+ } else {
+ state = "Found";
+ }
+ fprintf(stderr, "*** %s name = '%s', type = '%s', domain = '%s'\n", state, nameC, typeC, domainC);
+}
+
+static mDNSBool CheckThatServiceTypeIsUsable(const char *serviceType, mDNSBool printExplanation)
+ // Checks that serviceType is a reasonable service type
+ // label and, if it isn't and printExplanation is true, prints
+ // an explanation of why not.
+{
+ mDNSBool result;
+
+ result = mDNStrue;
+ if (result && strlen(serviceType) > 63) {
+ if (printExplanation) {
+ fprintf(stderr,
+ "%s: Service type specified by -t is too long (must be 63 characters or less)\n",
+ gProgramName);
+ }
+ result = mDNSfalse;
+ }
+ if (result && serviceType[0] == 0) {
+ if (printExplanation) {
+ fprintf(stderr,
+ "%s: Service type specified by -t can't be empty\n",
+ gProgramName);
+ }
+ result = mDNSfalse;
+ }
+ return result;
+}
+
+static const char kDefaultServiceType[] = "_afpovertcp._tcp.";
+
+static void PrintUsage()
+{
+ fprintf(stderr,
+ "Usage: %s [-v level] [-t type]\n",
+ gProgramName);
+ fprintf(stderr, " -v verbose mode, level is a number from 0 to 2\n");
+ fprintf(stderr, " 0 = no debugging info (default)\n");
+ fprintf(stderr, " 1 = standard debugging info\n");
+ fprintf(stderr, " 2 = intense debugging info\n");
+ fprintf(stderr, " -t uses 'type' as the service type (default is '%s')\n", kDefaultServiceType);
+}
+
+static const char *gServiceType = kDefaultServiceType;
+
+static void ParseArguments(int argc, char **argv)
+ // Parses our command line arguments into the global variables
+ // listed above.
+{
+ int ch;
+
+ // Set gProgramName to the last path component of argv[0]
+
+ gProgramName = strrchr(argv[0], '/');
+ if (gProgramName == NULL) {
+ gProgramName = argv[0];
+ } else {
+ gProgramName += 1;
+ }
+
+ // Parse command line options using getopt.
+
+ do {
+ ch = getopt(argc, argv, "v:t:");
+ if (ch != -1) {
+ switch (ch) {
+ case 'v':
+ gMDNSPlatformPosixVerboseLevel = atoi(optarg);
+ if (gMDNSPlatformPosixVerboseLevel < 0 || gMDNSPlatformPosixVerboseLevel > 2) {
+ fprintf(stderr,
+ "%s: Verbose mode must be in the range 0..2\n",
+ gProgramName);
+ exit(1);
+ }
+ break;
+ case 't':
+ gServiceType = optarg;
+ if ( ! CheckThatServiceTypeIsUsable(gServiceType, mDNStrue) ) {
+ exit(1);
+ }
+ break;
+ case '?':
+ default:
+ PrintUsage();
+ exit(1);
+ break;
+ }
+ }
+ } while (ch != -1);
+
+ // Check for any left over command line arguments.
+
+ if (optind != argc) {
+ fprintf(stderr, "%s: Unexpected argument '%s'\n", gProgramName, argv[optind]);
+ exit(1);
+ }
+}
+
+int main(int argc, char **argv)
+ // The program's main entry point. The program does a trivial
+ // mDNS query, looking for all AFP servers in the local domain.
+{
+ int result;
+ mStatus status;
+ DNSQuestion question;
+ domainname type;
+ domainname domain;
+
+ // Parse our command line arguments. This won't come back if there's an error.
+ ParseArguments(argc, argv);
+
+ // Initialise the mDNS core.
+ status = mDNS_Init(&mDNSStorage, &PlatformStorage,
+ gRRCache, RR_CACHE_SIZE,
+ mDNS_Init_DontAdvertiseLocalAddresses,
+ mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
+ if (status == mStatus_NoError) {
+
+ // Construct and start the query.
+
+ MakeDomainNameFromDNSNameString(&type, gServiceType);
+ MakeDomainNameFromDNSNameString(&domain, "local.");
+
+ status = mDNS_StartBrowse(&mDNSStorage, &question, &type, &domain, mDNSInterface_Any, BrowseCallback, NULL);
+
+ // Run the platform main event loop until the user types ^C.
+ // The BrowseCallback routine is responsible for printing
+ // any results that we find.
+
+ if (status == mStatus_NoError) {
+ fprintf(stderr, "Hit ^C when you're bored waiting for responses.\n");
+ ExampleClientEventLoop(&mDNSStorage);
+ mDNS_StopQuery(&mDNSStorage, &question);
+ mDNS_Close(&mDNSStorage);
+ }
+ }
+
+ if (status == mStatus_NoError) {
+ result = 0;
+ } else {
+ result = 2;
+ }
+ if ( (result != 0) || (gMDNSPlatformPosixVerboseLevel > 0) ) {
+ fprintf(stderr, "%s: Finished with status %ld, result %d\n", gProgramName, status, result);
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * 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: ExampleClientApp.c,v $
+Revision 1.9 2003/08/12 19:56:26 cheshire
+Update to APSL 2.0
+
+Revision 1.8 2003/07/02 21:19:58 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.7 2003/06/18 05:48:41 cheshire
+Fix warnings
+
+Revision 1.6 2003/03/31 22:44:36 cheshire
+Add log header
+
+ */
+
+#include <stdio.h> // For printf()
+#include <stdlib.h> // For exit() etc.
+#include <string.h> // For strlen() etc.
+#include <unistd.h> // For select()
+#include <errno.h> // For errno, EINTR
+#include <arpa/inet.h> // For inet_addr()
+#include <netinet/in.h> // For INADDR_NONE
+#include <netdb.h> // For gethostbyname()
+#include <signal.h> // For SIGINT, etc.
+
+#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
+
+//*******************************************************************************************
+// Main
+
+static volatile mDNSBool StopNow;
+
+mDNSlocal void HandleSIG(int signal)
+ {
+ (void)signal; // Unused
+ debugf("");
+ debugf("HandleSIG");
+ StopNow = mDNStrue;
+ }
+
+mDNSexport void ExampleClientEventLoop(mDNS *const m)
+ {
+ signal(SIGINT, HandleSIG); // SIGINT is what you get for a Ctrl-C
+ signal(SIGTERM, HandleSIG);
+
+ while (!StopNow)
+ {
+ int nfds = 0;
+ fd_set readfds;
+ struct timeval timeout;
+ int result;
+
+ // 1. Set up the fd_set as usual here.
+ // This example client has no file descriptors of its own,
+ // but a real application would call FD_SET to add them to the set here
+ FD_ZERO(&readfds);
+
+ // 2. Set up the timeout.
+ // This example client has no other work it needs to be doing,
+ // so we set an effectively infinite timeout
+ timeout.tv_sec = 0x3FFFFFFF;
+ timeout.tv_usec = 0;
+
+ // 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout
+ mDNSPosixGetFDSet(m, &nfds, &readfds, &timeout);
+
+ // 4. Call select as normal
+ verbosedebugf("select(%d, %d.%06d)", nfds, timeout.tv_sec, timeout.tv_usec);
+ result = select(nfds, &readfds, NULL, NULL, &timeout);
+
+ if (result < 0)
+ {
+ verbosedebugf("select() returned %d errno %d", result, errno);
+ if (errno != EINTR) StopNow = mDNStrue;
+ }
+ else
+ {
+ // 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work
+ mDNSPosixProcessFDSet(m, &readfds);
+
+ // 6. This example client has no other work it needs to be doing,
+ // but a real client would do its work here
+ // ... (do work) ...
+ }
+ }
+
+ debugf("Exiting");
+ }
--- /dev/null
+/*
+ * 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: ExampleClientApp.h,v $
+Revision 1.5 2003/08/12 19:56:26 cheshire
+Update to APSL 2.0
+
+Revision 1.4 2003/07/02 21:19:58 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.3 2003/03/31 22:49:35 cheshire
+Add "$Log" header
+
+ */
+
+extern void ExampleClientEventLoop(mDNS *const m);
--- /dev/null
+/*
+ * 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@
+ *
+ * Formatting notes:
+ * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
+ * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
+ * but for the sake of brevity here I will say just this: Curly braces are not syntactially
+ * part of an "if" statement; they are the beginning and ending markers of a compound statement;
+ * therefore common sense dictates that if they are part of a compound statement then they
+ * should be indented to the same level as everything else in that compound statement.
+ * Indenting curly braces at the same level as the "if" implies that curly braces are
+ * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
+ * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
+ * 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: Identify.c,v $
+Revision 1.10 2003/09/02 20:38:57 cheshire
+#include <signal.h> for Linux
+
+Revision 1.9 2003/08/14 23:57:46 cheshire
+Report if there is no answer at all from the target host
+
+Revision 1.8 2003/08/14 02:19:55 cheshire
+<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
+
+Revision 1.7 2003/08/12 19:56:26 cheshire
+Update to APSL 2.0
+
+Revision 1.6 2003/08/06 01:46:18 cheshire
+Distinguish no answer from partial answer
+
+Revision 1.5 2003/08/05 23:56:26 cheshire
+Update code to compile with the new mDNSCoreReceive() function that requires a TTL
+(Right now mDNSPosix.c just reports 255 -- we should fix this)
+
+Revision 1.4 2003/08/04 17:24:48 cheshire
+Combine the three separate A/AAAA/HINFO queries into a single qtype "ANY" query
+
+Revision 1.3 2003/08/04 17:14:08 cheshire
+Do both AAAA queries in parallel
+
+Revision 1.2 2003/08/02 02:25:13 cheshire
+Multiple improvements: Now displays host's name, and all v4 and v6 addresses, as well as HINFO record
+
+Revision 1.1 2003/08/01 02:20:02 cheshire
+Add mDNSIdentify tool, used to discover what version of mDNSResponder a particular host is running
+
+ */
+
+//*************************************************************************************************************
+// Incorporate mDNS.c functionality
+
+// We want to use the functionality provided by "mDNS.c",
+// except we'll sneak a peek at the packets before forwarding them to the normal mDNSCoreReceive() routine
+#define mDNSCoreReceive __MDNS__mDNSCoreReceive
+#include "mDNS.c"
+#undef mDNSCoreReceive
+
+//*************************************************************************************************************
+// Headers
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h> // For n_long, required by <netinet/ip.h> below
+#include <netinet/ip.h> // For IPTOS_LOWDELAY etc.
+#include <arpa/inet.h>
+#include <signal.h>
+
+#include "mDNSClientAPI.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"
+
+//*************************************************************************************************************
+// Globals
+
+static mDNS mDNSStorage; // mDNS core uses this to store its globals
+static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals
+#define RR_CACHE_SIZE 500
+static CacheRecord gRRCache[RR_CACHE_SIZE];
+
+static volatile int StopNow; // 0 means running, 1 means stop because we got an answer, 2 means stop because of Ctrl-C
+static volatile int NumAnswers, NumAddr, NumAAAA, NumHINFO;
+static char hostname[256], hardware[256], software[256];
+static mDNSOpaque16 lastid, id;
+
+//*************************************************************************************************************
+// Utilities
+
+// Special version of printf that knows how to print IP addresses, DNS-format name strings, etc.
+mDNSlocal mDNSu32 mprintf(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
+mDNSlocal mDNSu32 mprintf(const char *format, ...)
+ {
+ mDNSu32 length;
+ unsigned char buffer[512];
+ va_list ptr;
+ va_start(ptr,format);
+ length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr);
+ va_end(ptr);
+ printf("%s", buffer);
+ return(length);
+ }
+
+//*************************************************************************************************************
+// Main code
+
+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)
+ {
+ // Snag copy of header ID, then call through
+ lastid = msg->h.id;
+ __MDNS__mDNSCoreReceive(m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID, ttl);
+ }
+
+static void NameCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
+ {
+ (void)m; // Unused
+ (void)question; // Unused
+ (void)AddRecord;// Unused
+ if (!id.NotAnInteger) id = lastid;
+ ConvertDomainNameToCString(&answer->rdata->u.name, hostname);
+ StopNow = 1;
+ mprintf("%##s %s %##s\n", answer->name.c, DNSTypeName(answer->rrtype), &answer->rdata->u.name.c);
+ }
+
+static void InfoCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
+ {
+ (void)m; // Unused
+ (void)question; // Unused
+ (void)AddRecord;// Unused
+ if (answer->rrtype == kDNSType_A)
+ {
+ if (!id.NotAnInteger) id = lastid;
+ NumAnswers++;
+ NumAddr++;
+ mprintf("%##s %s %.4a\n", answer->name.c, DNSTypeName(answer->rrtype), &answer->rdata->u.ip);
+ }
+ else if (answer->rrtype == kDNSType_AAAA)
+ {
+ if (!id.NotAnInteger) id = lastid;
+ NumAnswers++;
+ NumAAAA++;
+ mprintf("%##s %s %.16a\n", answer->name.c, DNSTypeName(answer->rrtype), &answer->rdata->u.ipv6);
+ }
+ else if (answer->rrtype == kDNSType_HINFO)
+ {
+ mDNSu8 *p = answer->rdata->u.data;
+ strncpy(hardware, p+1, p[0]);
+ hardware[p[0]] = 0;
+ p += 1 + p[0];
+ strncpy(software, p+1, p[0]);
+ software[p[0]] = 0;
+ NumAnswers++;
+ NumHINFO++;
+ }
+ }
+
+mDNSexport void WaitForAnswer(mDNS *const m, int seconds)
+ {
+ struct timeval end;
+ gettimeofday(&end, NULL);
+ end.tv_sec += seconds;
+ StopNow = 0;
+ NumAnswers = 0;
+ while (!StopNow)
+ {
+ int nfds = 0;
+ fd_set readfds;
+ struct timeval now, remain = end;
+ int result;
+
+ FD_ZERO(&readfds);
+ gettimeofday(&now, NULL);
+ if (remain.tv_usec < now.tv_usec) { remain.tv_usec += 1000000; remain.tv_sec--; }
+ if (remain.tv_sec < now.tv_sec) return;
+ remain.tv_usec -= now.tv_usec;
+ remain.tv_sec -= now.tv_sec;
+ mDNSPosixGetFDSet(m, &nfds, &readfds, &remain);
+ result = select(nfds, &readfds, NULL, NULL, &remain);
+ if (result >= 0) mDNSPosixProcessFDSet(m, &readfds);
+ else if (errno != EINTR) StopNow = 2;
+ }
+ }
+
+mDNSlocal mStatus StartQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, mDNSQuestionCallback callback)
+ {
+ if (qname) MakeDomainNameFromDNSNameString(&q->qname, qname);
+
+ q->InterfaceID = mDNSInterface_Any;
+ q->qtype = qtype;
+ q->qclass = kDNSClass_IN;
+ q->QuestionCallback = callback;
+ q->QuestionContext = NULL;
+
+ //mprintf("%##s %s ?\n", q->qname.c, DNSTypeName(qtype));
+ return(mDNS_StartQuery(&mDNSStorage, q));
+ }
+
+mDNSlocal int DoQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, mDNSQuestionCallback callback)
+ {
+ mStatus status = StartQuery(q, qname, qtype, callback);
+ if (status != mStatus_NoError)
+ StopNow = 2;
+ else
+ {
+ WaitForAnswer(&mDNSStorage, 4);
+ mDNS_StopQuery(&mDNSStorage, q);
+ if (StopNow == 0 && NumAnswers == 0)
+ printf("%s %s *** No Answer ***\n", qname, DNSTypeName(qtype));
+ }
+ return(StopNow);
+ }
+
+mDNSlocal void HandleSIG(int signal)
+ {
+ (void)signal; // Unused
+ debugf("");
+ debugf("HandleSIG");
+ StopNow = 2;
+ }
+
+mDNSexport int main(int argc, char **argv)
+ {
+ mStatus status;
+
+ if (argc < 2) goto usage;
+
+ // Initialise the mDNS core.
+ status = mDNS_Init(&mDNSStorage, &PlatformStorage,
+ gRRCache, RR_CACHE_SIZE,
+ mDNS_Init_DontAdvertiseLocalAddresses,
+ mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
+ if (status) { fprintf(stderr, "Daemon start: mDNS_Init failed %ld\n", status); return(status); }
+
+ signal(SIGINT, HandleSIG); // SIGINT is what you get for a Ctrl-C
+ signal(SIGTERM, HandleSIG);
+
+ struct in_addr s4;
+ struct in6_addr s6;
+
+ char buffer[256];
+ DNSQuestion q;
+
+ if (inet_pton(AF_INET, argv[1], &s4) == 1)
+ {
+ mDNSu8 *p = (mDNSu8 *)&s4;
+ mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", p[3], p[2], p[1], p[0]);
+ printf("%s\n", buffer);
+ if (DoQuery(&q, buffer, kDNSType_PTR, NameCallback) != 1) goto exit;
+ }
+ else if (inet_pton(AF_INET6, argv[1], &s6) == 1)
+ {
+ DNSQuestion q1, q2;
+ int i;
+ mDNSu8 *p = (mDNSu8 *)&s6;
+ for (i = 0; i < 16; i++)
+ {
+ static const char hexValues[] = "0123456789ABCDEF";
+ buffer[i * 4 ] = hexValues[p[15-i] & 0x0F];
+ buffer[i * 4 + 1] = '.';
+ buffer[i * 4 + 2] = hexValues[p[15-i] >> 4];
+ buffer[i * 4 + 3] = '.';
+ }
+ mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa.");
+ MakeDomainNameFromDNSNameString(&q1.qname, buffer);
+ mDNS_snprintf(&buffer[32], sizeof(buffer)-32, "ip6.arpa."); // Workaround for WWDC bug
+ MakeDomainNameFromDNSNameString(&q2.qname, buffer);
+ StartQuery(&q1, NULL, kDNSType_PTR, NameCallback);
+ StartQuery(&q2, NULL, kDNSType_PTR, NameCallback);
+ WaitForAnswer(&mDNSStorage, 4);
+ mDNS_StopQuery(&mDNSStorage, &q1);
+ mDNS_StopQuery(&mDNSStorage, &q2);
+ if (StopNow != 1) { mprintf("%##s %s *** No Answer ***\n", q1.qname.c, DNSTypeName(q1.qtype)); goto exit; }
+ }
+ else
+ strcpy(hostname, argv[1]);
+
+ // Now we have the host name; get its A, AAAA, and HINFO
+ if (DoQuery(&q, hostname, kDNSQType_ANY, InfoCallback) == 2) goto exit; // Interrupted with Ctrl-C
+
+ if (hardware[0] || software[0])
+ {
+ printf("HINFO Hardware: %s\n", hardware);
+ printf("HINFO Software: %s\n", software);
+ }
+ else if (NumAnswers)
+ {
+ printf("Host has no HINFO record; Best guess is ");
+ if (id.b[1]) printf("mDNSResponder-%d\n", id.b[1]);
+ else if (NumAAAA) printf("very early Panther build (mDNSResponder-33 or earlier)\n");
+ else printf("Jaguar version of mDNSResponder with no IPv6 support\n");
+ }
+ else
+ printf("Incorrect dot-local hostname, address, or no mDNSResponder running on that machine\n");
+
+exit:
+ mDNS_Close(&mDNSStorage);
+ return(0);
+
+usage:
+ fprintf(stderr, "%s <dot-local hostname> or <IPv4 address> or <IPv6 address>\n", argv[0]);
+ return(-1);
+ }
--- /dev/null
+# $Log: Makefile,v $
+# Revision 1.13 2003/08/06 18:20:51 cheshire
+# Makefile cleanup
+#
+# Revision 1.12 2003/08/01 02:20:02 cheshire
+# Add mDNSIdentify tool, used to discover what version of mDNSResponder a particular host is running
+#
+# Revision 1.11 2003/07/14 18:11:54 cheshire
+# Fix stricter compiler warnings
+#
+# Revision 1.10 2003/06/18 05:47:41 cheshire
+# Enable stricter warnings on Jaguar and Panther builds
+#
+# Revision 1.9 2003/06/04 18:34:45 ksekar
+# Bug #: <rdar://problem/3218120>: mDNSPosix does not build on Panther that has socklen_t
+# Changed build targets "osx10.2" and "osx10.3" to "jaguar" and "panther".
+#
+# Revision 1.8 2003/06/04 00:23:12 ksekar
+# Bug #: <rdar://problem/3218120>: mDNSPosix does not build on Panther that has socklen_t
+# Created separate target OS's for 10.2 and 10.3.
+#
+# Revision 1.7 2003/04/16 02:11:37 cheshire
+# Remove unnecessary $(CFLAGS) from linking rules
+#
+# Revision 1.6 2003/04/04 01:37:14 cheshire
+# Added NetMonitor.c
+#
+
+# I assume that cc will be in your path. If not, you have to change the following to point to it.
+CC = cc
+CFLAGS_COMMON = -g -I../mDNSCore -I. -DMDNS_DEBUGMSGS=2
+
+ifeq ($(os),solaris)
+CFLAGS_OS = -DNOT_HAVE_DAEMON -DNOT_HAVE_SA_LEN -D_XPG4_2 -D__EXTENSIONS__ -DHAVE_BROKEN_RECVIF_NAME -lsocket -lnsl
+else
+ifeq ($(os),linux)
+CFLAGS_OS = -DNOT_HAVE_SA_LEN -W -Wall
+else
+ifeq ($(os),netbsd)
+CFLAGS_OS =
+else
+ifeq ($(os),freebsd)
+CFLAGS_OS =
+else
+ifeq ($(os),openbsd)
+CFLAGS_OS = -DHAVE_BROKEN_RECVDSTADDR
+else
+ifeq ($(os),jaguar)
+CFLAGS_OS = -DHAVE_IPV6 -W -Wall -no-cpp-precomp -DNOT_HAVE_SOCKLEN_T
+else
+ifeq ($(os),panther)
+CFLAGS_OS = -DHAVE_IPV6 -W -Wall -no-cpp-precomp
+else
+cantbuild:
+ @echo "Error: Must specify target OS on command-line, e.g. \"make os=panther\" or \"make os=jaguar\" or \"make os=linux\""
+endif
+endif
+endif
+endif
+endif
+endif
+endif
+CFLAGS = $(CFLAGS_COMMON) $(CFLAGS_OS)
+
+COMMONOBJ = objects/mDNSPosix.c.o objects/mDNSUNP.c.o objects/ExampleClientApp.c.o
+
+HEADERS = Makefile mDNSUNP.h mDNSPosix.h \
+../mDNSCore/mDNSDebug.h \
+../mDNSCore/mDNSClientAPI.h \
+../mDNSCore/mDNSPlatformFunctions.h
+
+all: setup Client Responder ProxyResponder Identify NetMonitor
+
+setup:
+ if test ! -d objects ; then mkdir objects ; fi
+ if test ! -d build ; then mkdir build ; fi
+
+Client: setup build/mDNSClientPosix
+ @echo "Client done"
+
+Responder: setup build/mDNSResponderPosix
+ @echo "Responder done"
+
+ProxyResponder: setup build/mDNSProxyResponderPosix
+ @echo "ProxyResponder done"
+
+Identify: setup build/mDNSIdentify
+ @echo "Identify done"
+
+NetMonitor: setup build/mDNSNetMonitor
+ @echo "NetMonitor done"
+
+# $@ means "The file name of the target of the rule"
+# $< means "The name of the first prerequisite"
+# $+ means "The names of all the prerequisites, with spaces between them, exactly as given"
+# For more magic automatic sariables, see
+# <http://www.gnu.org/manual/make-3.80/html_chapter/make_10.html#SEC111>
+build/mDNSClientPosix: $(COMMONOBJ) objects/mDNS.c.o objects/Client.c.o
+ $(CC) $+ -o $@
+
+build/mDNSResponderPosix: $(COMMONOBJ) objects/mDNS.c.o objects/Responder.c.o
+ $(CC) $+ -o $@
+
+build/mDNSProxyResponderPosix: $(COMMONOBJ) objects/mDNS.c.o objects/ProxyResponder.c.o
+ $(CC) $+ -o $@
+
+build/mDNSIdentify: $(COMMONOBJ) objects/Identify.c.o
+ $(CC) $+ -o $@
+
+build/mDNSNetMonitor: $(COMMONOBJ) objects/NetMonitor.c.o
+ $(CC) $+ -o $@
+
+objects/%.c.o: %.c ../mDNSCore/mDNS.c $(HEADERS)
+ $(CC) -c $(CFLAGS) $< -o $@
+
+objects/mDNS.c.o: ../mDNSCore/mDNS.c $(HEADERS)
+ $(CC) -c $(CFLAGS) $< -o $@
+
+clean:
+ -rm -rf objects build .gdb_history
--- /dev/null
+/*
+ * 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@
+ *
+ * Formatting notes:
+ * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
+ * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
+ * but for the sake of brevity here I will say just this: Curly braces are not syntactially
+ * part of an "if" statement; they are the beginning and ending markers of a compound statement;
+ * therefore common sense dictates that if they are part of a compound statement then they
+ * should be indented to the same level as everything else in that compound statement.
+ * Indenting curly braces at the same level as the "if" implies that curly braces are
+ * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
+ * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
+ * 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: NetMonitor.c,v $
+Revision 1.47 2003/09/05 18:49:57 cheshire
+Add total packet size to display
+
+Revision 1.46 2003/09/05 02:33:48 cheshire
+Set output to be line buffered, so you can redirect to a file and "tail -f" the file in another window
+
+Revision 1.45 2003/09/04 00:16:20 cheshire
+Only show count of unique source addresses seen on network if we're not filtering
+
+Revision 1.44 2003/09/02 22:13:28 cheshire
+Show total host count in final summary table
+
+Revision 1.43 2003/09/02 21:42:52 cheshire
+Improved alignment of final summary table with v6 addresses
+
+Revision 1.42 2003/09/02 20:59:24 cheshire
+Use bcopy() instead of non-portable "__u6_addr.__u6_addr32" fields.
+
+Revision 1.41 2003/08/29 22:05:44 cheshire
+Also count subsequent KA packets for the purposes of statistics counting
+
+Revision 1.40 2003/08/29 16:43:24 cheshire
+Also display breakdown of Probe/Goodbye/BrowseQ etc. for each host
+
+Revision 1.39 2003/08/28 02:07:48 vlubet
+Added "per hosts" statistics
+
+Revision 1.38 2003/08/20 22:41:42 cheshire
+Also display total multicast packet count
+
+Revision 1.37 2003/08/20 22:32:08 cheshire
+Error in DisplayQuery: Authorities come *after* Answers, not before
+
+Revision 1.36 2003/08/18 23:20:10 cheshire
+RDLength moved from the RData to the ResourceRecord object.
+
+Revision 1.35 2003/08/15 20:17:28 cheshire
+"LargeResourceRecord" changed to "LargeCacheRecord"
+
+Revision 1.34 2003/08/14 02:19:55 cheshire
+<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
+
+Revision 1.33 2003/08/12 19:56:26 cheshire
+Update to APSL 2.0
+
+Revision 1.32 2003/08/06 18:57:01 cheshire
+Update comments
+
+Revision 1.31 2003/08/06 02:05:12 cheshire
+Add ability to give a list of hosts to monitor
+
+Revision 1.30 2003/08/05 23:56:26 cheshire
+Update code to compile with the new mDNSCoreReceive() function that requires a TTL
+(Right now mDNSPosix.c just reports 255 -- we should fix this)
+
+Revision 1.29 2003/08/05 00:43:12 cheshire
+Report errors encountered while processing authority section
+
+Revision 1.28 2003/07/29 22:51:08 cheshire
+Added hexdump for packets we can't decode, so we can find out *why* we couldn't decode them
+
+Revision 1.27 2003/07/29 22:48:04 cheshire
+Completed support for decoding packets containing oversized resource records
+
+Revision 1.26 2003/07/19 03:25:23 cheshire
+Change to make use of new GetLargeResourceRecord() call, for handling larger records
+
+Revision 1.25 2003/07/18 00:13:23 cheshire
+Remove erroneous trailing '\' from TXT record display
+
+Revision 1.24 2003/07/17 17:10:51 cheshire
+<rdar://problem/3315761> Implement "unicast response" request, using top bit of qclass
+Further work: distinguish between PM and PU
+
+Revision 1.23 2003/07/16 22:20:23 cheshire
+<rdar://problem/3315761> Implement "unicast response" request, using top bit of qclass
+Made mDNSNetMonitor distinguish between QM and QU in its logging output
+
+Revision 1.22 2003/07/02 21:19:58 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.21 2003/06/18 05:48:41 cheshire
+Fix warnings
+
+Revision 1.20 2003/06/06 22:18:22 cheshire
+Add extra space in Q output to line it up with RR output
+
+Revision 1.19 2003/06/06 21:05:04 cheshire
+Display state of cache-flush bit on additional records
+
+Revision 1.18 2003/06/06 20:01:30 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.17 2003/06/06 14:26:50 cheshire
+Explicitly #include <time.h> for the benefit of certain Linux distributions
+
+Revision 1.16 2003/05/29 21:56:36 cheshire
+More improvements:
+Distinguish modern multicast queries from legacy multicast queries
+In addition to record counts, display packet counts of queries, legacy queries, and responses
+Include TTL in RR display
+
+Revision 1.15 2003/05/29 20:03:57 cheshire
+Various improvements:
+Display start and end time, average rates in packets-per-minute,
+show legacy queries as -LQ-, improve display of TXT and unknown records
+
+Revision 1.14 2003/05/26 04:45:42 cheshire
+Limit line length when printing super-long TXT records
+
+Revision 1.13 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.12 2003/05/26 03:01:28 cheshire
+<rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
+
+Revision 1.11 2003/05/26 00:48:13 cheshire
+If mDNS packet contains a non-zero message ID, then display it.
+
+Revision 1.10 2003/05/22 01:10:32 cheshire
+Indicate when the TC bit is set on a query packet
+
+Revision 1.9 2003/05/21 03:56:00 cheshire
+Improve display of Probe queries
+
+Revision 1.8 2003/05/09 21:41:56 cheshire
+Track deletion/goodbye packets as separate category
+
+Revision 1.7 2003/05/07 00:16:01 cheshire
+More detailed decoding of Resource Records
+
+Revision 1.6 2003/05/05 21:16:16 cheshire
+<rdar://problem/3241281> Change timenow from a local variable to a structure member
+
+Revision 1.5 2003/04/19 01:16:22 cheshire
+Add filter option, to monitor only packets from a single specified source address
+
+Revision 1.4 2003/04/18 00:45:21 cheshire
+Distinguish announcements (AN) from deletions (DE)
+
+Revision 1.3 2003/04/15 18:26:01 cheshire
+Added timestamps and help information
+
+Revision 1.2 2003/04/04 20:42:02 cheshire
+Fix broken statistics counting
+
+Revision 1.1 2003/04/04 01:37:14 cheshire
+Added NetMonitor.c
+
+ */
+
+//*************************************************************************************************************
+// Incorporate mDNS.c functionality
+
+// We want to use much of the functionality provided by "mDNS.c",
+// except we'll steal the packets that would be sent to normal mDNSCoreReceive() routine
+#define mDNSCoreReceive __NOT__mDNSCoreReceive__NOT__
+#include "mDNS.c"
+#undef mDNSCoreReceive
+
+//*************************************************************************************************************
+// Headers
+
+#include <stdio.h> // For printf()
+#include <stdlib.h> // For malloc()
+#include <string.h> // For bcopy()
+#include <time.h> // For "struct tm" etc.
+#include <netdb.h> // For gethostbyname()
+#include <sys/socket.h> // For AF_INET, AF_INET6, etc.
+#include <arpa/inet.h> // For inet_addr()
+#include <netinet/in.h> // For INADDR_NONE
+
+#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
+#include "ExampleClientApp.h"
+
+//*************************************************************************************************************
+// Types and structures
+
+enum
+ {
+ // Primitive operations
+ OP_probe = 0,
+ OP_goodbye = 1,
+
+ // These are meta-categories;
+ // Query and Answer operations are actually subdivided into two classes:
+ // Browse query/answer and
+ // Resolve query/answer
+ OP_query = 2,
+ OP_answer = 3,
+
+ // The "Browse" variants of query/answer
+ OP_browsegroup = 2,
+ OP_browseq = 2,
+ OP_browsea = 3,
+
+ // The "Resolve" variants of query/answer
+ OP_resolvegroup = 4,
+ OP_resolveq = 4,
+ OP_resolvea = 5,
+
+ OP_NumTypes = 6
+ };
+
+typedef struct ActivityStat_struct ActivityStat;
+struct ActivityStat_struct
+ {
+ ActivityStat *next;
+ domainname srvtype;
+ int printed;
+ int totalops;
+ int stat[OP_NumTypes];
+ };
+
+typedef struct FilterList_struct FilterList;
+struct FilterList_struct
+ {
+ FilterList *next;
+ mDNSAddr FilterAddr;
+ };
+
+//*************************************************************************************************************
+// Globals
+
+static mDNS mDNSStorage; // mDNS core uses this to store its globals
+static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals
+
+struct timeval tv_start, tv_end, tv_interval;
+
+static FilterList *Filters;
+#define ExactlyOneFilter (Filters && !Filters->next)
+
+static int NumPktQ, NumPktL, NumPktR, NumPktB; // Query/Legacy/Response/Bad
+static int NumProbes, NumGoodbyes, NumQuestions, NumLegacy, NumAnswers, NumAdditionals;
+
+static ActivityStat *stats;
+
+#define OPBanner "Total Ops Probe Goodbye BrowseQ BrowseA ResolveQ ResolveA"
+
+//*************************************************************************************************************
+// Utilities
+
+// Special version of printf that knows how to print IP addresses, DNS-format name strings, etc.
+mDNSlocal mDNSu32 mprintf(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
+mDNSlocal mDNSu32 mprintf(const char *format, ...)
+ {
+ mDNSu32 length;
+ unsigned char buffer[512];
+ va_list ptr;
+ va_start(ptr,format);
+ length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr);
+ va_end(ptr);
+ printf("%s", buffer);
+ return(length);
+ }
+
+//*************************************************************************************************************
+// Host Address List
+//
+// Would benefit from a hash
+
+typedef enum
+ {
+ HostPkt_Q = 0, // Query
+ HostPkt_L = 1, // Legacy Query
+ HostPkt_R = 2, // Response
+ HostPkt_B = 3, // Bad
+ HostPkt_NumTypes = 4,
+ } HostPkt_Type;
+
+typedef struct
+ {
+ mDNSAddr addr;
+ unsigned long pkts[HostPkt_NumTypes];
+ unsigned long totalops;
+ unsigned long stat[OP_NumTypes];
+ } HostEntry;
+
+#define HostEntryTotalPackets(H) ((H)->pkts[HostPkt_Q] + (H)->pkts[HostPkt_L] + (H)->pkts[HostPkt_R] + (H)->pkts[HostPkt_B])
+
+typedef struct
+ {
+ long num;
+ long max;
+ HostEntry *hosts;
+ } HostList;
+
+static HostList IPv4HostList = { 0, 0, 0 };
+static HostList IPv6HostList = { 0, 0, 0 };
+
+mDNSlocal HostEntry *FindHost(const mDNSAddr *addr, HostList* list)
+ {
+ long i;
+
+ for (i = 0; i < list->num; i++)
+ {
+ HostEntry *entry = list->hosts + i;
+ if (mDNSSameAddress(addr, &entry->addr))
+ return entry;
+ }
+
+ return NULL;
+ }
+
+mDNSlocal HostEntry *AddHost(HostList* list)
+ {
+ HostEntry *entry;
+ if (list->num >= list->max)
+ {
+ long newMax = list->max + 64;
+ HostEntry *newHosts = realloc(list->hosts, newMax * sizeof(HostEntry));
+ if (newHosts == NULL)
+ return NULL;
+ list->max = newMax;
+ list->hosts = newHosts;
+ }
+ entry = list->hosts + list->num++;
+ return(entry);
+ }
+
+mDNSlocal HostEntry *GotPacketFromHost(const mDNSAddr *addr, HostPkt_Type t)
+ {
+ if (ExactlyOneFilter) return(NULL);
+ else
+ {
+ HostList *list = (addr->type == mDNSAddrType_IPv4) ? &IPv4HostList : &IPv6HostList;
+ HostEntry *entry = FindHost(addr, list);
+ if (!entry)
+ {
+ int i;
+ entry = AddHost(list);
+ if (!entry) return(NULL);
+ entry->addr = *addr;
+ for (i=0; i<HostPkt_NumTypes; i++) entry->pkts[i] = 0;
+ entry->totalops = 0;
+ for (i=0; i<OP_NumTypes; i++) entry->stat[i] = 0;
+ }
+ entry->pkts[t]++;
+ return(entry);
+ }
+ }
+
+mDNSlocal int CompareHosts(const void *p1, const void *p2)
+ {
+ return (int)(HostEntryTotalPackets((HostEntry *)p2) - HostEntryTotalPackets((HostEntry *)p1));
+ }
+
+mDNSlocal void ShowSortedHostList(HostList *list, int max)
+ {
+ HostEntry *e, *end = &list->hosts[(max < list->num) ? max : list->num];
+ qsort(list->hosts, list->num, sizeof(HostEntry), CompareHosts);
+ if (list->num) mprintf("\n%-25s%s%s\n", "Source Address", OPBanner, " Pkts Query LegacyQ Response");
+ for (e = &list->hosts[0]; e < end; e++)
+ {
+ int len = mprintf("%#-25a", &e->addr);
+ if (len > 25) mprintf("\n%25s", "");
+ mprintf("%8d %8d %8d %8d %8d %8d %8d", 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]);
+ mprintf(" %8lu %8lu %8lu %8lu",
+ HostEntryTotalPackets(e), e->pkts[HostPkt_Q], e->pkts[HostPkt_L], e->pkts[HostPkt_R]);
+ if (e->pkts[HostPkt_B]) mprintf("Bad: %8lu", e->pkts[HostPkt_B]);
+ mprintf("\n");
+ }
+ }
+
+//*************************************************************************************************************
+// Receive and process packets
+
+mDNSexport mDNSBool ExtractServiceType(const domainname *const fqdn, domainname *const srvtype)
+ {
+ int i, len;
+ const mDNSu8 *src = fqdn->c;
+ mDNSu8 *dst = srvtype->c;
+
+ len = *src;
+ if (len == 0 || len >= 0x40) return(mDNSfalse);
+ if (src[1] != '_') src += 1 + len;
+
+ len = *src;
+ if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse);
+ for (i=0; i<=len; i++) *dst++ = *src++;
+
+ len = *src;
+ if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse);
+ for (i=0; i<=len; i++) *dst++ = *src++;
+
+ *dst++ = 0; // Put the null root label on the end of the service type
+
+ return(mDNStrue);
+ }
+
+mDNSlocal void recordstat(HostEntry *entry, domainname *fqdn, int op, mDNSu16 rrtype)
+ {
+ ActivityStat **s = &stats;
+ domainname srvtype;
+
+ if (op != OP_probe)
+ {
+ if (rrtype == kDNSType_SRV || rrtype == kDNSType_TXT) op = op - OP_browsegroup + OP_resolvegroup;
+ else if (rrtype != kDNSType_PTR) return;
+ }
+
+ if (!ExtractServiceType(fqdn, &srvtype)) return;
+
+ while (*s && !SameDomainName(&(*s)->srvtype, &srvtype)) s = &(*s)->next;
+ if (!*s)
+ {
+ int i;
+ *s = malloc(sizeof(ActivityStat));
+ if (!*s) exit(-1);
+ (*s)->next = NULL;
+ (*s)->srvtype = srvtype;
+ (*s)->printed = 0;
+ (*s)->totalops = 0;
+ for (i=0; i<OP_NumTypes; i++) (*s)->stat[i] = 0;
+ }
+
+ (*s)->totalops++;
+ (*s)->stat[op]++;
+ if (entry)
+ {
+ entry->totalops++;
+ entry->stat[op]++;
+ }
+ }
+
+mDNSlocal void printstats(int max)
+ {
+ int i;
+ for (i=0; i<max; i++)
+ {
+ int max = 0;
+ ActivityStat *s, *m = NULL;
+ for (s = stats; s; s=s->next)
+ if (!s->printed && max < s->totalops)
+ { m = s; max = s->totalops; }
+ 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],
+ m->stat[OP_goodbye], m->stat[OP_browseq], m->stat[OP_browsea], m->stat[OP_resolveq], m->stat[OP_resolvea]);
+ }
+ }
+
+mDNSlocal const mDNSu8 *FindUpdate(mDNS *const m, const DNSMessage *const query, const mDNSu8 *ptr, const mDNSu8 *const end, DNSQuestion *q, LargeCacheRecord *pkt)
+ {
+ int i;
+ for (i = 0; i < query->h.numAuthorities; i++)
+ {
+ const mDNSu8 *p2 = ptr;
+ ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, 0, pkt);
+ if (!ptr) break;
+ if (ResourceRecordAnswersQuestion(&pkt->r.resrec, q)) return(p2);
+ }
+ return(mDNSNULL);
+ }
+
+mDNSlocal void DisplayTimestamp(void)
+ {
+ struct timeval tv;
+ struct tm tm;
+ gettimeofday(&tv, NULL);
+ localtime_r((time_t*)&tv.tv_sec, &tm);
+ 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 char *const ptype = (msg->h.flags.b[0] & kDNSFlag0_QR_Response) ? "-R- " :
+ (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) ? "-Q- " : "-LQ-";
+
+ DisplayTimestamp();
+ mprintf("%#-16a %s Q:%3d Ans:%3d Auth:%3d Add:%3d Size:%5d bytes",
+ srcaddr, ptype, msg->h.numQuestions, msg->h.numAnswers, msg->h.numAuthorities, msg->h.numAdditionals, end - (mDNSu8 *)msg);
+
+ if (msg->h.id.NotAnInteger) mprintf(" ID:%u", ((mDNSu16)msg->h.id.b[0])<<8 | msg->h.id.b[1]);
+
+ if (msg->h.flags.b[0] & kDNSFlag0_TC)
+ {
+ if (msg->h.flags.b[0] & kDNSFlag0_QR_Response) mprintf(" Truncated");
+ else mprintf(" Truncated (KA list continues in next packet)");
+ }
+ mprintf("\n");
+ }
+
+mDNSlocal void DisplayResourceRecord(const mDNSAddr *const srcaddr, const char *const op, const ResourceRecord *const pktrr)
+ {
+ static const char hexchars[16] = "0123456789ABCDEF";
+ #define MaxWidth 132
+ char buffer[MaxWidth+8];
+ char *p = buffer;
+
+ 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);
+
+ 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_HINFO:// same as kDNSType_TXT below
+ case kDNSType_TXT: {
+ mDNSu8 *t = rd->txt.c;
+ while (t < rdend && t[0] && p < buffer+MaxWidth)
+ {
+ int i;
+ for (i=1; i<=t[0] && p < buffer+MaxWidth; i++)
+ {
+ if (t[i] == '\\') *p++ = '\\';
+ if (t[i] >= ' ') *p++ = t[i];
+ else
+ {
+ *p++ = '\\';
+ *p++ = '0';
+ *p++ = 'x';
+ *p++ = hexchars[t[i] >> 4];
+ *p++ = hexchars[t[i] & 0xF];
+ }
+ }
+ t += 1+t[0];
+ if (t < rdend && t[0]) { *p++ = '\\'; *p++ = ' '; }
+ }
+ *p++ = 0;
+ 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, ((mDNSu16)rd->srv.port.b[0] << 8) | rd->srv.port.b[1]); break;
+ default: {
+ mDNSu8 *s = rd->data;
+ while (s < rdend && p < buffer+MaxWidth)
+ {
+ if (*s == '\\') *p++ = '\\';
+ if (*s >= ' ') *p++ = *s;
+ else
+ {
+ *p++ = '\\';
+ *p++ = '0';
+ *p++ = 'x';
+ *p++ = hexchars[*s >> 4];
+ *p++ = hexchars[*s & 0xF];
+ }
+ s++;
+ }
+ *p++ = 0;
+ n += mprintf("%.*s", MaxWidth - n, buffer);
+ } break;
+ }
+
+ mprintf("\n");
+ }
+
+mDNSlocal void HexDump(const mDNSu8 *ptr, const mDNSu8 *const end)
+ {
+ while (ptr < end)
+ {
+ int i;
+ for (i=0; i<16; i++)
+ if (&ptr[i] < end) mprintf("%02X ", ptr[i]);
+ else mprintf(" ");
+ for (i=0; i<16; i++)
+ if (&ptr[i] < end) mprintf("%c", ptr[i] <= ' ' || ptr[i] >= 126 ? '.' : ptr[i]);
+ ptr += 16;
+ mprintf("\n");
+ }
+ }
+
+mDNSlocal void DisplayError(const mDNSAddr *srcaddr, const mDNSu8 *ptr, const mDNSu8 *const end, char *msg)
+ {
+ mprintf("%#-16a **** ERROR: FAILED TO READ %s **** \n", srcaddr, msg);
+ HexDump(ptr, end);
+ }
+
+mDNSlocal void DisplayQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSInterfaceID InterfaceID)
+ {
+ int i;
+ const mDNSu8 *ptr = msg->data;
+ const mDNSu8 *auth = LocateAuthorities(msg, end);
+ mDNSBool MQ = (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger);
+ HostEntry *entry = GotPacketFromHost(srcaddr, MQ ? HostPkt_Q : HostPkt_L);
+ LargeCacheRecord pkt;
+
+ DisplayPacketHeader(msg, end, srcaddr, srcport);
+ if (MQ) NumPktQ++; else NumPktL++;
+
+ for (i=0; i<msg->h.numQuestions; i++)
+ {
+ DNSQuestion q;
+ mDNSu8 *p2;
+ const mDNSu8 *ep = ptr;
+ ptr = getQuestion(msg, ptr, end, InterfaceID, &q);
+ if (!ptr) { DisplayError(srcaddr, ep, end, "QUESTION"); return; }
+ mDNSu16 ucbit = q.qclass & kDNSQClass_UnicastResponse;
+ q.qclass &= ~kDNSQClass_UnicastResponse;
+ p2 = (mDNSu8 *)FindUpdate(m, msg, auth, end, &q, &pkt);
+ if (p2)
+ {
+ NumProbes++;
+ DisplayResourceRecord(srcaddr, ucbit ? "(PU)" : "(PM)", &pkt.r.resrec);
+ recordstat(entry, &q.qname, OP_probe, q.qtype);
+ p2 = (mDNSu8 *)skipDomainName(msg, p2, end);
+ // Having displayed this update record, clear type and class so we don't display the same one again.
+ p2[0] = p2[1] = p2[2] = p2[3] = 0;
+ }
+ else
+ {
+ const char *ptype = ucbit ? "(QU)" : "(QM)";
+ if (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) NumQuestions++;
+ else { NumLegacy++; ptype = "(LQ)"; }
+ mprintf("%#-16a %-5s %-5s %##s\n", srcaddr, ptype, DNSTypeName(q.qtype), q.qname.c);
+ recordstat(entry, &q.qname, OP_query, q.qtype);
+ }
+ }
+
+ for (i=0; i<msg->h.numAnswers; i++)
+ {
+ const mDNSu8 *ep = ptr;
+ ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt);
+ if (!ptr) { DisplayError(srcaddr, ep, end, "KNOWN ANSWER"); return; }
+ DisplayResourceRecord(srcaddr, "(KA)", &pkt.r.resrec);
+
+ // In the case of queries with long multi-packet KA lists, we count each subsequent KA packet
+ // the same as a single query, to more accurately reflect the burden on the network
+ // (A query with a six-packet KA list is *at least* six times the burden on the network as a single-packet query.)
+ if (msg->h.numQuestions == 0 && i == 0)
+ recordstat(entry, &pkt.r.resrec.name, OP_query, pkt.r.resrec.rrtype);
+ }
+
+ for (i=0; i<msg->h.numAuthorities; i++)
+ {
+ const mDNSu8 *ep = ptr;
+ ptr = skipResourceRecord(msg, ptr, end);
+ if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; }
+ }
+ }
+
+mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSInterfaceID InterfaceID)
+ {
+ int i;
+ const mDNSu8 *ptr = msg->data;
+ HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R);
+ LargeCacheRecord pkt;
+
+ DisplayPacketHeader(msg, end, srcaddr, srcport);
+ NumPktR++;
+
+ for (i=0; i<msg->h.numQuestions; i++)
+ {
+ DNSQuestion q;
+ const mDNSu8 *ep = ptr;
+ ptr = getQuestion(msg, ptr, end, InterfaceID, &q);
+ if (!ptr) { DisplayError(srcaddr, ep, end, "QUESTION"); return; }
+ mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE Q IN mDNS RESPONSE **** %-5s %##s\n", srcaddr, DNSTypeName(q.qtype), q.qname.c);
+ }
+
+ for (i=0; i<msg->h.numAnswers; i++)
+ {
+ const mDNSu8 *ep = ptr;
+ ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt);
+ if (!ptr) { DisplayError(srcaddr, ep, end, "ANSWER"); return; }
+ if (pkt.r.resrec.rroriginalttl)
+ {
+ NumAnswers++;
+ DisplayResourceRecord(srcaddr, (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AN)" : "(AN+)", &pkt.r.resrec);
+ recordstat(entry, &pkt.r.resrec.name, OP_answer, pkt.r.resrec.rrtype);
+ }
+ else
+ {
+ NumGoodbyes++;
+ DisplayResourceRecord(srcaddr, "(DE)", &pkt.r.resrec);
+ recordstat(entry, &pkt.r.resrec.name, OP_goodbye, pkt.r.resrec.rrtype);
+ }
+ }
+
+ for (i=0; i<msg->h.numAuthorities; i++)
+ {
+ const mDNSu8 *ep = ptr;
+ ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &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);
+ }
+
+ for (i=0; i<msg->h.numAdditionals; i++)
+ {
+ const mDNSu8 *ep = ptr;
+ ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt);
+ if (!ptr) { DisplayError(srcaddr, ep, end, "ADDITIONAL"); return; }
+ NumAdditionals++;
+ DisplayResourceRecord(srcaddr, (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AD)" : "(AD+)", &pkt.r.resrec);
+ }
+ }
+
+mDNSlocal mDNSBool AddressMatchesFilterList(const mDNSAddr *srcaddr)
+ {
+ FilterList *f;
+ if (!Filters) return(srcaddr->type == mDNSAddrType_IPv4);
+ for (f=Filters; f; f=f->next) if (mDNSSameAddress(srcaddr, &f->FilterAddr)) return(mDNStrue);
+ return(mDNSfalse);
+ }
+
+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 mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery;
+ const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery;
+ const mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask);
+
+ (void)dstaddr; // Unused
+ (void)dstport; // Unused
+
+ // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
+ mDNSu8 *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]);
+ 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))
+ {
+ if (QR_OP == StdQ) DisplayQuery (m, msg, end, srcaddr, srcport, InterfaceID);
+ else if (QR_OP == StdR) DisplayResponse(m, msg, end, srcaddr, srcport, InterfaceID);
+ else
+ {
+ debugf("Unknown DNS packet type %02X%02X (ignored)", msg->h.flags.b[0], msg->h.flags.b[1]);
+ GotPacketFromHost(srcaddr, HostPkt_B);
+ NumPktB++;
+ }
+ }
+ }
+
+mDNSlocal mStatus mDNSNetMonitor(void)
+ {
+ struct tm tm;
+ int h, m, s, mul, div, TotPkt;
+
+ mStatus status = mDNS_Init(&mDNSStorage, &PlatformStorage,
+ mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
+ mDNS_Init_DontAdvertiseLocalAddresses,
+ mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
+ if (status) return(status);
+
+ gettimeofday(&tv_start, NULL);
+ ExampleClientEventLoop(&mDNSStorage); // Wait for user to hit Ctrl-C
+
+ // Now display final summary
+ TotPkt = NumPktQ + NumPktL + NumPktR;
+ gettimeofday(&tv_end, NULL);
+ tv_interval = tv_end;
+ if (tv_start.tv_usec > tv_interval.tv_usec)
+ { tv_interval.tv_usec += 1000000; tv_interval.tv_sec--; }
+ tv_interval.tv_sec -= tv_start.tv_sec;
+ tv_interval.tv_usec -= tv_start.tv_usec;
+ h = (tv_interval.tv_sec / 3600);
+ m = (tv_interval.tv_sec % 3600) / 60;
+ s = (tv_interval.tv_sec % 60);
+ if (tv_interval.tv_sec > 10)
+ {
+ mul = 60;
+ div = tv_interval.tv_sec;
+ }
+ else
+ {
+ mul = 60000;
+ div = tv_interval.tv_sec * 1000 + tv_interval.tv_usec / 1000;
+ if (div == 0) div=1;
+ }
+
+ mprintf("\n\n");
+ localtime_r((time_t*)&tv_start.tv_sec, &tm);
+ mprintf("Started %3d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv_start.tv_usec);
+ 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);
+ 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);
+ mprintf("Multicast Response Packets: %7d (avg%5d/min)\n", NumPktR, NumPktR * mul / div);
+ mprintf("Total Multicast Packets: %7d (avg%5d/min)\n", TotPkt, TotPkt * mul / div);
+ mprintf("\n");
+ mprintf("Total New Service Probes: %7d (avg%5d/min)\n", NumProbes, NumProbes * mul / div);
+ mprintf("Total Goodbye Announcements: %7d (avg%5d/min)\n", NumGoodbyes, NumGoodbyes * mul / div);
+ mprintf("Total Query Questions: %7d (avg%5d/min)\n", NumQuestions, NumQuestions * mul / div);
+ mprintf("Total Queries from Legacy Clients:%7d (avg%5d/min)\n", NumLegacy, NumLegacy * mul / div);
+ mprintf("Total Answers/Announcements: %7d (avg%5d/min)\n", NumAnswers, NumAnswers * mul / div);
+ mprintf("Total Additional Records: %7d (avg%5d/min)\n", NumAdditionals, NumAdditionals * mul / div);
+ mprintf("\n");
+ printstats(15);
+
+ if (!ExactlyOneFilter)
+ {
+ ShowSortedHostList(&IPv4HostList, 15);
+ ShowSortedHostList(&IPv6HostList, 15);
+ }
+
+ mDNS_Close(&mDNSStorage);
+ return(0);
+ }
+
+mDNSexport int main(int argc, char **argv)
+ {
+ int i;
+ mStatus status;
+
+ setlinebuf(stdout); // Want to see lines as they appear, not block buffered
+
+ for (i=1; i<argc; i++)
+ {
+ struct in_addr s4;
+ struct in6_addr s6;
+ mDNSAddr a;
+ a.type = mDNSAddrType_IPv4;
+
+ if (inet_pton(AF_INET, argv[i], &s4) == 1)
+ a.ip.v4.NotAnInteger = s4.s_addr;
+ else if (inet_pton(AF_INET6, argv[i], &s6) == 1)
+ {
+ a.type = mDNSAddrType_IPv6;
+ bcopy(&s6, &a.ip.v6, sizeof(a.ip.v6));
+ }
+ else
+ {
+ struct hostent *h = gethostbyname(argv[i]);
+ if (h) a.ip.v4.NotAnInteger = *(long*)h->h_addr;
+ else goto usage;
+ }
+
+ FilterList *f = malloc(sizeof(*f));
+ f->FilterAddr = a;
+ f->next = Filters;
+ Filters = f;
+ }
+
+ status = mDNSNetMonitor();
+ if (status) { fprintf(stderr, "%s: mDNSNetMonitor failed %ld\n", argv[0], status); return(status); }
+ return(0);
+
+usage:
+ fprintf(stderr, "\nmDNS traffic monitor\n");
+ fprintf(stderr, "Usage: %s (<host>)\n", argv[0]);
+ fprintf(stderr, "Optional <host> parameter displays only packets from that host\n");
+
+ fprintf(stderr, "\nPer-packet header output:\n");
+ fprintf(stderr, "-Q- Multicast Query from mDNS client that accepts multicast responses\n");
+ fprintf(stderr, "-R- Multicast Response packet containing answers/announcements\n");
+ fprintf(stderr, "-LQ- Multicast Query from legacy client that does *not* listen for multicast responses\n");
+ fprintf(stderr, "Q/Ans/Auth/Add Number of questions, answers, authority records and additional records in packet\n");
+
+ fprintf(stderr, "\nPer-record display:\n");
+ fprintf(stderr, "(PM) Probe Question (new service starting), requesting multicast response\n");
+ fprintf(stderr, "(PU) Probe Question (new service starting), requesting unicast response\n");
+ fprintf(stderr, "(DE) Deletion/Goodbye (service going away)\n");
+ fprintf(stderr, "(LQ) Legacy Query Question\n");
+ fprintf(stderr, "(QM) Query Question, requesting multicast response\n");
+ fprintf(stderr, "(QU) Query Question, requesting unicast response\n");
+ fprintf(stderr, "(KA) Known Answer (information querier already knows)\n");
+ fprintf(stderr, "(AN) Unique Answer to question (or periodic announcment) (entire RR Set)\n");
+ fprintf(stderr, "(AN+) Answer to question (or periodic announcment) (add to existing RR Set members)\n");
+ fprintf(stderr, "(AD) Unique Additional Record Set (entire RR Set)\n");
+ fprintf(stderr, "(AD+) Additional records (add to existing RR Set members)\n");
+
+ fprintf(stderr, "\nFinal summary, sorted by service type:\n");
+ fprintf(stderr, "Probe Probes for this service type starting up\n");
+ fprintf(stderr, "Goodbye Goodbye (deletion) packets for this service type shutting down\n");
+ fprintf(stderr, "BrowseQ Browse questions from clients browsing to find a list of instances of this service\n");
+ fprintf(stderr, "BrowseA Browse answers/announcments advertising instances of this service\n");
+ fprintf(stderr, "ResolveQ Resolve questions from clients actively connecting to an instance of this service\n");
+ fprintf(stderr, "ResolveA Resolve answers/announcments giving connection information for an instance of this service\n");
+ fprintf(stderr, "\n");
+ return(-1);
+ }
--- /dev/null
+/*
+ * 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: ProxyResponder.c,v $
+Revision 1.22 2003/08/14 02:19:55 cheshire
+<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
+
+Revision 1.21 2003/08/12 19:56:26 cheshire
+Update to APSL 2.0
+
+Revision 1.20 2003/07/23 00:00:04 cheshire
+Add comments
+
+Revision 1.19 2003/07/15 01:55:16 cheshire
+<rdar://problem/3315777> Need to implement service registration with subtypes
+
+Revision 1.18 2003/07/02 21:19:58 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.17 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.16 2003/05/26 03:01:28 cheshire
+<rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
+
+Revision 1.15 2003/05/06 00:00:50 cheshire
+<rdar://problem/3248914> Rationalize naming of domainname manipulation functions
+
+Revision 1.14 2003/04/25 01:45:57 cheshire
+<rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name
+
+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
+
+Revision 1.11 2003/03/31 22:49:35 cheshire
+Add "$Log" header
+
+ */
+
+#include <stdio.h> // For printf()
+#include <stdlib.h> // For exit() etc.
+#include <string.h> // For strlen() etc.
+#include <unistd.h> // For select()
+#include <errno.h> // For errno, EINTR
+#include <arpa/inet.h> // For inet_addr()
+#include <netinet/in.h> // For INADDR_NONE
+#include <netdb.h> // 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 "ExampleClientApp.h"
+
+//*************************************************************************************************************
+// Globals
+static mDNS mDNSStorage; // mDNS core uses this to store its globals
+static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals
+
+//*************************************************************************************************************
+// Proxy Host Registration
+
+typedef struct
+ {
+ mDNSv4Addr ip;
+ domainlabel hostlabel; // Conforms to standard DNS letter-digit-hyphen host name rules
+ AuthRecord RR_A; // 'A' (address) record for our ".local" name
+ AuthRecord RR_PTR; // PTR (reverse lookup) record
+ } ProxyHost;
+
+mDNSlocal void HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
+ {
+ ProxyHost *f = (ProxyHost*)rr->RecordContext;
+ if (result == mStatus_NoError)
+ debugf("Host name successfully registered: %##s", &rr->resrec.name);
+ else
+ {
+ debugf("Host name conflict for %##s", &rr->resrec.name);
+ mDNS_Deregister(m, &f->RR_A);
+ mDNS_Deregister(m, &f->RR_PTR);
+ exit(-1);
+ }
+ }
+
+mDNSlocal mStatus mDNS_RegisterProxyHost(mDNS *m, ProxyHost *p)
+ {
+ char buffer[32];
+
+ mDNS_SetupResourceRecord(&p->RR_A, mDNSNULL, mDNSInterface_Any, kDNSType_A, 60, kDNSRecordTypeUnique, HostNameCallback, p);
+ mDNS_SetupResourceRecord(&p->RR_PTR, mDNSNULL, mDNSInterface_Any, kDNSType_PTR, 60, kDNSRecordTypeKnownUnique, HostNameCallback, p);
+
+ p->RR_A.resrec.name.c[0] = 0;
+ AppendDomainLabel(&p->RR_A.resrec.name, &p->hostlabel);
+ AppendLiteralLabelString(&p->RR_A.resrec.name, "local");
+
+ 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_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);
+
+ return(mStatus_NoError);
+ }
+
+//*************************************************************************************************************
+// Service Registration
+
+// This sample ServiceCallback 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 ServiceCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result)
+ {
+ 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;
+ }
+
+ if (result == mStatus_NoError)
+ {
+ char buffer[256];
+ ConvertDomainNameToCString_unescaped(&sr->RR_SRV.resrec.name, buffer);
+ printf("Service %s now registered and active\n", buffer);
+ }
+
+ if (result == mStatus_NameConflict)
+ {
+ char buffer1[256], buffer2[256];
+ ConvertDomainNameToCString_unescaped(&sr->RR_SRV.resrec.name, buffer1);
+ mDNS_RenameAndReregisterService(m, sr, mDNSNULL);
+ ConvertDomainNameToCString_unescaped(&sr->RR_SRV.resrec.name, buffer2);
+ printf("Name Conflict! %s renamed as %s\n", buffer1, buffer2);
+ }
+ }
+
+// 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,
+ const char name[], const char type[], const char domain[],
+ const domainname *host, mDNSu16 PortAsNumber, int argc, char **argv)
+ {
+ domainlabel n;
+ domainname t, d;
+ mDNSIPPort port;
+ unsigned char buffer[1024], *bptr = buffer;
+
+ MakeDomainLabelFromLiteralString(&n, name);
+ MakeDomainNameFromDNSNameString(&t, type);
+ MakeDomainNameFromDNSNameString(&d, domain);
+ port.b[0] = (mDNSu8)(PortAsNumber >> 8);
+ port.b[1] = (mDNSu8)(PortAsNumber );
+ while (argc)
+ {
+ int len = strlen(argv[0]);
+ printf("STR: %s\n", argv[0]);
+ bptr[0] = len;
+ strcpy(bptr+1, argv[0]);
+ bptr += 1 + len;
+ argc--;
+ argv++;
+ }
+
+ mDNS_RegisterService(m, recordset,
+ &n, &t, &d, // Name, type, domain
+ host, port, // Host and port
+ buffer, bptr-buffer, // TXT data, length
+ mDNSNULL, 0, // Subtypes
+ mDNSInterface_Any, // Interace ID
+ ServiceCallback, mDNSNULL); // Callback and context
+
+ ConvertDomainNameToCString_unescaped(&recordset->RR_SRV.resrec.name, buffer);
+ printf("Made Service Records for %s\n", buffer);
+ }
+
+//*************************************************************************************************************
+// Service non-existence assertion
+// (claiming a service name without actually providing a service at that name, to prevent anyone else using that name)
+// This is useful to avoid confusion between similar services
+// e.g. A printer that implements IPP printing service using the name "My Printer", but doesn't implement LPR service,
+// should also claim the LPR service name "My Printer" to stop a different printer offering LPR service under the same name,
+// since it would be confusing to users to have two equivalent services with the same name.
+
+mDNSlocal void NoSuchServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
+ {
+ 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;
+ }
+
+ if (result == mStatus_NoError)
+ {
+ char buffer[256];
+ ConvertDomainNameToCString_unescaped(&rr->resrec.name, buffer);
+ printf("Non-existence assertion %s now registered and active\n", buffer);
+ }
+
+ if (result == mStatus_NameConflict)
+ {
+ domainlabel n;
+ domainname t, d;
+ char buffer1[256], buffer2[256];
+ ConvertDomainNameToCString_unescaped(&rr->resrec.name, buffer1);
+ DeconstructServiceName(&rr->resrec.name, &n, &t, &d);
+ IncrementLabelSuffix(&n, mDNStrue);
+ mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, mDNSNULL);
+ ConvertDomainNameToCString_unescaped(&rr->resrec.name, buffer2);
+ printf("Name Conflict! %s renamed as %s\n", buffer1, buffer2);
+ }
+ }
+
+mDNSlocal void RegisterNoSuchService(mDNS *m, AuthRecord *const rr, domainname *proxyhostname,
+ const char name[], const char type[], const char domain[])
+ {
+ domainlabel n;
+ domainname t, d;
+ unsigned char buffer[256];
+ MakeDomainLabelFromLiteralString(&n, name);
+ MakeDomainNameFromDNSNameString(&t, type);
+ MakeDomainNameFromDNSNameString(&d, domain);
+ mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, proxyhostname);
+ ConvertDomainNameToCString_unescaped(&rr->resrec.name, buffer);
+ printf("Made Non-existence Record for %s\n", buffer);
+ }
+
+//*************************************************************************************************************
+// Main
+
+mDNSexport int main(int argc, char **argv)
+ {
+ mStatus status;
+
+ if (argc < 3) goto usage;
+
+ status = mDNS_Init(&mDNSStorage, &PlatformStorage,
+ mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
+ mDNS_Init_DontAdvertiseLocalAddresses,
+ mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
+ if (status) { fprintf(stderr, "Daemon start: mDNS_Init failed %ld\n", status); return(status); }
+
+ if (!strcmp(argv[1], "-"))
+ {
+ domainname proxyhostname;
+ AuthRecord proxyrecord;
+ if (argc < 5) goto usage;
+ proxyhostname.c[0] = 0;
+ AppendLiteralLabelString(&proxyhostname, argv[2]);
+ AppendLiteralLabelString(&proxyhostname, "local");
+ RegisterNoSuchService(&mDNSStorage, &proxyrecord, &proxyhostname, argv[3], argv[4], "local.");
+ ExampleClientEventLoop(&mDNSStorage);
+ mDNS_Close(&mDNSStorage);
+ }
+ else
+ {
+ ProxyHost proxyhost;
+ ServiceRecordSet proxyservice;
+
+ proxyhost.ip.NotAnInteger = inet_addr(argv[1]);
+ if (proxyhost.ip.NotAnInteger == INADDR_NONE) // INADDR_NONE is 0xFFFFFFFF
+ {
+ struct hostent *h = gethostbyname(argv[1]);
+ if (h) proxyhost.ip.NotAnInteger = *(long*)h->h_addr;
+ }
+ if (proxyhost.ip.NotAnInteger == INADDR_NONE) // INADDR_NONE is 0xFFFFFFFF
+ {
+ fprintf(stderr, "%s is not valid host address\n", argv[1]);
+ return(-1);
+ }
+
+ MakeDomainLabelFromLiteralString(&proxyhost.hostlabel, argv[2]);
+
+ mDNS_RegisterProxyHost(&mDNSStorage, &proxyhost);
+
+ if (argc >=6)
+ RegisterService(&mDNSStorage, &proxyservice, argv[3], argv[4], "local.",
+ &proxyhost.RR_A.resrec.name, atoi(argv[5]), argc-6, &argv[6]);
+
+ ExampleClientEventLoop(&mDNSStorage);
+ mDNS_Close(&mDNSStorage);
+ }
+
+ return(0);
+
+usage:
+ fprintf(stderr, "%s ip hostlabel [srvname srvtype port txt [txt ...]]\n", argv[0]);
+ fprintf(stderr, "ip Real IP address (or valid host name) of the host where the service actually resides\n");
+ fprintf(stderr, "hostlabel First label of the dot-local host name to create for this host, e.g. \"foo\" for \"foo.local.\"\n");
+ fprintf(stderr, "srvname Descriptive name of service, e.g. \"Stuart's Ink Jet Printer\"\n");
+ fprintf(stderr, "srvtype IANA service type, e.g. \"_ipp._tcp\" or \"_ssh._tcp\", etc.\n");
+ fprintf(stderr, "port Port number where the service resides (1-65535)\n");
+ fprintf(stderr, "txt Additional name/value pairs specified in service definition, e.g. \"pdl=application/postscript\"\n");
+ fprintf(stderr, "e.g. %s 169.254.12.34 thehost (just create a dot-local host name)\n", argv[0]);
+ fprintf(stderr, "or %s 169.254.12.34 thehost \"My Printer\" _printer._tcp. 515 rp=lpt1 pdl=application/postscript\n", argv[0]);
+ fprintf(stderr, "or %s - thehost \"My Printer\" _printer._tcp. (assertion of non-existence)\n", argv[0]);
+ return(-1);
+ }
--- /dev/null
+ReadMe About mDNSPosix
+----------------------
+
+mDNSPosix is a port of Apple's core mDNS code to the Posix platform.
+The sample shows how you might implement an mDNS responder inside an
+embedded device, such as a printer or a web camera.
+
+mDNS is short for "multicast DNS", which is a technology that allows you
+to register IP services and browse the network for those services. For
+more information about mDNS, see the mDNS web site.
+
+ <http://www.multicastdns.org/>
+
+mDNS is part of a family of technologies developed by the IETF zeroconf
+working group. For information about other zeroconf technologies, see
+the zeroconf web site.
+
+ <http://www.zeroconf.org/>
+
+Apple uses the brand name "Rendezvous" to describe our implementation of
+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.
+
+IMPORTANT
+This sample is not a full port of Apple's Rendezvous APIs to Posix.
+Specifically, the sample includes a responder daemon that registers
+entities based on its command line arguments (or a text file). This is
+perfect for a embedded device, but is not suitable for a general purpose
+computer. A real implementation of the Rendezvous APIs would require a
+mDNS daemon, client libraries that applications link with, and some form
+of RPC between them. Client libraries and client-to-daemon RPC are
+beyond the scope of this sample, however, this would be a good place to
+start if you were interested in implementing these facilities on your
+platform.
+
+
+Packing List
+------------
+The sample includes the following files and directories:
+
+o ReadMe.txt -- This file.
+
+o mDNSCore -- A directory containing the core mDNS code. This code is
+ written in pure ANSI C and has proved to be very portable.
+
+o mDNSPosix.h -- The interface to the platform support code.
+
+o mDNSPosix.c -- The platform support code for the Posix platform.
+ This code glues the mDNS core to Posix.
+
+o mDNSUNP.h -- Interface to the code in "mDNSUNP.c".
+
+o mDNSUNP.c -- A few routines from the "Unix Network Programming" book
+ that I borrowed to make the port easier. The routines are slightly
+ modified from the originals to meet my specific needs. You can get the
+ originals at the URL below.
+
+ <http://www.kohala.com/start/unpv12e.html>
+
+o Client.c -- The main program for the sample mDNS client.
+
+o Responder.c -- The main program for the sample mDNS responder.
+
+o Services.txt -- A sample configuration file for the mDNS responder.
+ You can test with this file using the option "-f Services.txt".
+
+o ProxyResponder.c -- Another sample mDNS responder, this one intended
+ for creating proxy registrations for other network devices that don't
+ have their own mDNS responders.
+
+o ExampleClientApp.h
+o ExampleClientApp.c -- shared code prioviding the
+ "ExampleClientEventLoop" used by Client.c and ProxyResponder.c.
+
+o Makefile -- A makefile for building on Mac OS X and other platforms.
+
+
+Building the Sample
+-------------------
+The sample does not use autoconf technology, primarily because I didn't
+want to delay shipping while I learnt how to use it. Thus the code
+builds using a very simple make file. To build the sample you should
+type "make os=myos", e.g.
+
+ make os=osx
+
+For Linux you would change that to:
+
+ make os=linux
+
+There are definitions for each of the platforms I ported to. If you're
+porting to any other platform you'll have to add appropriate definitions
+for it.
+
+
+Using the Sample
+----------------
+Once you've built the sample you can test it by first running the
+client, as shown below.
+
+ quinn% build/mDNSClientPosix
+ Hit ^C when you're bored waiting for responses.
+
+By default the client starts a search for AppleShare servers and then
+sits and waits, printing a message when services appear and disappear.
+
+To continue with the test you should start the responder in another
+shell window.
+
+ quinn% build/mDNSResponderPosix -n Foo
+
+This will start the responder and tell it to advertise a AppleShare
+service "Foo". In the client window you will see the client print out
+the following as the service shows up on the network.
+
+ quinn% build/mDNSClientPosix
+ Hit ^C when you're bored waiting for responses.
+ *** Found name = 'Foo', type = '_afpovertcp._tcp.', domain = 'local.'
+
+Back in the responder window you can quit the responder cleanly using
+SIGINT (typically ^C).
+
+ quinn% build/mDNSResponderPosix -n Foo
+ ^C
+ quinn%
+
+As the responder quits it will multicast that the "Foo" service is
+disappearing and the client will see that notification and print a
+message to that effect (shown below). Finally, when you're done with
+the client you can use SIGINT to quit it.
+
+ quinn% build/mDNSClientPosix
+ Hit ^C when you're bored waiting for responses.
+ *** Found name = 'Foo', type = '_afpovertcp._tcp.', domain = 'local.'
+ *** Lost name = 'Foo', type = '_afpovertcp._tcp.', domain = 'local.'
+ ^C
+ quinn%
+
+If things don't work, try starting each program in verbose mode (using
+the "-v 1" option, or very verbose mode with "-v 2") to see if there's
+an obvious cause.
+
+That's it for the core functionality. Each program supports a variety
+of other options. For example, you can advertise and browse for a
+different service type using the "-t type" option. Use the "-?" option
+on each program for more user-level information.
+
+
+How It Works
+------------
+A typical mDNS program is divided into three sections.
+
+ +----------------+
+ | Application |
+ +----------------+
+ | mDNS Core |
+ +----------------+
+ | Posix Platform |
+ +----------------+
+
+The mDNS core code comprises the files in the "mDNSCore" directory.
+It's standard ANSI C that's very portable. It relies on the underlying
+platform code for all external functionality.
+
+In this example the external platform code glues the mDNS core to a
+POSIX-ish platform. This code is contained in the files:
+
+o mDNSPosix.h
+o mDNSPosix.c
+o mDNSUNP.h
+o mDNSUNP.c
+
+The guts of the code is in "mDNSPosix.c".
+
+I should be clear that true POSIX isn't powerful enough to accomplish
+the job, so this code doesn't compile with _POSIX_SOURCE defined and
+there's a bunch of conditional code that does different things on
+different Unixen. I've isolated the hairiest parts of this code in the
+"mDNSUNP".
+
+Above the mDNS core code is the code that actually does
+application-specific tasks. In this example I've supplied two
+application programs: the responder (Responder.c) acts as a simple mDNS
+responder, listening for mDNS service lookup requests and answering
+them, and the client (Client.c), which is a simple mDNS browser, making
+simple mDNS search queries. Both programs use the same mDNS core and
+Posix platform code.
+
+A discussion of the mDNS protocol itself is beyond the scope of this
+sample. Quite frankly, my goal here was to demonstrate how it easy it
+is to use Apple's mDNS core without actually understanding mDNS, and
+because I achieved that goal I never had to learn a lot about how the
+mDNS core code works. It's just a black box that I call. If you want
+to learn more about mDNS, see the references at the top of this file.
+
+The mDNS Posix platform code is actually pretty simple. mDNS core
+requires six key features in its platform support.
+
+o the core calls the platformm at startup (mDNSPlatformInit)
+ and shutdown (mDNSPlatformClose)
+
+o the core calls the platform to send a UDP packet (mDNSPlatformSendUDP)
+
+o the core calls the platform to set a timer (mDNSPlatformScheduleTask)
+
+o the platform calls the core (mDNSCoreTask) when the timer expires
+
+o the platform calls the core (mDNSCoreReceive) when a UDP datagram arrives
+
+o the platform calls the core when network interfaces are
+ added (mDNS_RegisterInterface) or removed (mDNS_DeregisterInterface)
+
+All of these features are implemented in "mDNSPosix.c".
+
+The runtime behaviour of the code is as follows.
+
+1. The application calls mDNS_Init, which in turns calls the platform
+ (mDNSPlatformInit).
+
+2. mDNSPlatformInit gets a list of interfaces (get_ifi_info) and registers
+ each one with the core (mDNS_RegisterInterface). For each interface
+ it also creates a multicast socket (SetupSocket).
+
+3. The application then calls select() repeatedly to handle file descriptor
+ events. Before calling select() each time, the application calls
+ mDNSPosixGetFDSet() to give mDNSPosix.c a chance to add its own file
+ descriptors to the set, and then after select() returns, it calls
+ mDNSPosixProcessFDSet() to give mDNSPosix.c a chance to receive and
+ process any packets that may have arrived.
+
+4. When the core needs to send a UDP packet it calls
+ mDNSPlatformSendUDP. That routines finds the interface that
+ corresponds to the source address requested by the core, and
+ sends the datagram using the UDP socket created for the
+ interface. If the socket is flow send-side controlled it just
+ drops the packet.
+
+5. When SocketDataReady runs it uses a complex routine,
+ "recvfrom_flags", to actually receive the packet. This is required
+ because the core needs information about the packet that is
+ only available via the "recvmsg" call, and that call is complex
+ to implement in a portable way. I got my implementation of
+ "recvfrom_flags" from Stevens' "UNIX Network Programming", but
+ I had to modify it further to work with Linux.
+
+One thing to note is that the Posix platform code is very deliberately
+not multi-threaded. I do everything from a main loop that calls
+"select()". This is good because it avoids all the problems that often
+accompany multi-threaded code. If you decide to use threads in your
+platform, you will have to implement the mDNSPlatformLock() and
+mDNSPlatformUnlock() calls which are no-ops in mDNSPosix.c.
+
+
+Caveats
+-------
+Currently the program uses a simple make file.
+
+There are various problems with loopback-only self discovery. The code
+will attempt service discovery on the loopback interface only if no
+other interfaces are available. However, this exposes a number of
+problems with the underlying network stack (at least on Mac OS X).
+
+o On Mac OS X 10.1.x the code fails to start on the loopback interface
+ because the IP_ADD_MEMBERSHIP option returns ENOBUFS.
+
+o On Mac OS X 10.2 the loopback-only case fails because
+ mDNSPlatformSendUDP's call to "sendto" fails with error EHOSTUNREACH
+ [Radar ID 3016042].
+
+I haven't been able to test the loopback-only case on other platforms
+because I don't have access to the physical machine.
+
+
+Licencing
+---------
+This code is distributed under the Apple Public Source License.
+Information about the licence is included at the top of each source file.
+
+
+Credits and Version History
+---------------------------
+If you find any problems with this sample, mail <dts@apple.com> and I
+will try to fix them up.
+
+1.0a1 (Jul 2002) was a prerelease version that was distributed
+internally at Apple.
+
+1.0a2 (Jul 2002) was a prerelease version that was distributed
+internally at Apple.
+
+1.0a3 (Aug 2002) was the first shipping version. The core mDNS code is
+the code from Mac OS 10.2 (Jaguar) GM.
+
+Share and Enjoy
+
+Apple Developer Technical Support
+Networking, Communications, Hardware
+
+6 Aug 2002
+
+
+To Do List
+----------
+Â¥ port to a System V that's not Solaris
+Â¥ use sig_atomic_t for signal to main thread flags
+Â¥ test and debug the daemon function, including properly logging
--- /dev/null
+/*
+ * 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: Responder.c,v $
+Revision 1.16 2003/08/14 02:19:55 cheshire
+<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
+
+Revision 1.15 2003/08/12 19:56:26 cheshire
+Update to APSL 2.0
+
+Revision 1.14 2003/08/06 18:20:51 cheshire
+Makefile cleanup
+
+Revision 1.13 2003/07/23 00:00:04 cheshire
+Add comments
+
+Revision 1.12 2003/07/15 01:55:16 cheshire
+<rdar://problem/3315777> Need to implement service registration with subtypes
+
+Revision 1.11 2003/07/14 18:11:54 cheshire
+Fix stricter compiler warnings
+
+Revision 1.10 2003/07/10 20:27:31 cheshire
+<rdar://problem/3318717> mDNSResponder Posix version is missing a 'b' in the getopt option string
+
+Revision 1.9 2003/07/02 21:19:59 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.8 2003/06/18 05:48:41 cheshire
+Fix warnings
+
+Revision 1.7 2003/05/06 00:00:50 cheshire
+<rdar://problem/3248914> Rationalize naming of domainname manipulation functions
+
+Revision 1.6 2003/03/08 00:35:56 cheshire
+Switched to using new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt")
+
+Revision 1.5 2003/02/20 06:48:36 cheshire
+Bug #: 3169535 Xserve RAID needs to do interface-specific registrations
+Reviewed by: Josh Graessley, Bob Bradley
+
+Revision 1.4 2003/01/28 03:07:46 cheshire
+Add extra parameter to mDNS_RenameAndReregisterService(),
+and add support for specifying a domain other than dot-local.
+
+Revision 1.3 2002/09/21 20:44:53 zarzycki
+Added APSL info
+
+Revision 1.2 2002/09/19 04:20:44 cheshire
+Remove high-ascii characters that confuse some systems
+
+Revision 1.1 2002/09/17 06:24:35 cheshire
+First checkin
+
+*/
+
+#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 <assert.h>
+#include <stdio.h> // For printf()
+#include <stdlib.h> // For exit() etc.
+#include <string.h> // For strlen() etc.
+#include <unistd.h> // For select()
+#include <errno.h> // For errno, EINTR
+#include <signal.h>
+#include <fcntl.h>
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Globals
+#endif
+
+static mDNS mDNSStorage; // mDNS core uses this to store its globals
+static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals
+
+static const char *gProgramName = "mDNSResponderPosix";
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Signals
+#endif
+
+static volatile mDNSBool gReceivedSigUsr1;
+static volatile mDNSBool gReceivedSigHup;
+static volatile mDNSBool gStopNow;
+
+// We support 4 signals.
+//
+// o SIGUSR1 toggles verbose mode on and off in debug builds
+// o SIGHUP triggers the program to re-read its preferences.
+// o SIGINT causes an orderly shutdown of the program.
+// o SIGQUIT causes a somewhat orderly shutdown (direct but dangerous)
+// o SIGKILL kills us dead (easy to implement :-)
+//
+// There are fatal race conditions in our signal handling, but there's not much
+// we can do about them while remaining within the Posix space. Specifically,
+// if a signal arrives after we test the globals its sets but before we call
+// select, the signal will be dropped. The user will have to send the signal
+// again. Unfortunately, Posix does not have a "sigselect" to atomically
+// modify the signal mask and start a select.
+
+static void HandleSigUsr1(int sigraised)
+ // If we get a SIGUSR1 we toggle the state of the
+ // verbose mode.
+{
+ assert(sigraised == SIGUSR1);
+ gReceivedSigUsr1 = mDNStrue;
+}
+
+static void HandleSigHup(int sigraised)
+ // A handler for SIGHUP that causes us to break out of the
+ // main event loop when the user kill 1's us. This has the
+ // effect of triggered the main loop to deregister the
+ // current services and re-read the preferences.
+{
+ assert(sigraised == SIGHUP);
+ gReceivedSigHup = mDNStrue;
+}
+
+static void HandleSigInt(int sigraised)
+ // A handler for SIGINT that causes us to break out of the
+ // main event loop when the user types ^C. This has the
+ // effect of quitting the program.
+{
+ assert(sigraised == SIGINT);
+
+ if (gMDNSPlatformPosixVerboseLevel > 0) {
+ fprintf(stderr, "\nSIGINT\n");
+ }
+ gStopNow = mDNStrue;
+}
+
+static void HandleSigQuit(int sigraised)
+ // If we get a SIGQUIT the user is desperate and we
+ // just call mDNS_Close directly. This is definitely
+ // not safe (because it could reenter mDNS), but
+ // we presume that the user has already tried the safe
+ // alternatives.
+{
+ assert(sigraised == SIGQUIT);
+
+ if (gMDNSPlatformPosixVerboseLevel > 0) {
+ fprintf(stderr, "\nSIGQUIT\n");
+ }
+ mDNS_Close(&mDNSStorage);
+ exit(0);
+}
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Parameter Checking
+#endif
+
+static mDNSBool CheckThatRichTextHostNameIsUsable(const char *richTextHostName, mDNSBool printExplanation)
+ // Checks that richTextHostName is a reasonable host name
+ // label and, if it isn't and printExplanation is true, prints
+ // an explanation of why not.
+{
+ mDNSBool result;
+ domainlabel richLabel;
+ domainlabel poorLabel;
+
+ result = mDNStrue;
+ if (result && strlen(richTextHostName) > 63) {
+ if (printExplanation) {
+ fprintf(stderr,
+ "%s: Host name is too long (must be 63 characters or less)\n",
+ gProgramName);
+ }
+ result = mDNSfalse;
+ }
+ if (result && richTextHostName[0] == 0) {
+ if (printExplanation) {
+ fprintf(stderr, "%s: Host name can't be empty\n", gProgramName);
+ }
+ result = mDNSfalse;
+ }
+ if (result) {
+ MakeDomainLabelFromLiteralString(&richLabel, richTextHostName);
+ ConvertUTF8PstringToRFC1034HostLabel(richLabel.c, &poorLabel);
+ if (poorLabel.c[0] == 0) {
+ if (printExplanation) {
+ fprintf(stderr,
+ "%s: Host name doesn't produce a usable RFC-1034 name\n",
+ gProgramName);
+ }
+ result = mDNSfalse;
+ }
+ }
+ return result;
+}
+
+static mDNSBool CheckThatServiceTypeIsUsable(const char *serviceType, mDNSBool printExplanation)
+ // Checks that serviceType is a reasonable service type
+ // label and, if it isn't and printExplanation is true, prints
+ // an explanation of why not.
+{
+ mDNSBool result;
+
+ result = mDNStrue;
+ if (result && strlen(serviceType) > 63) {
+ if (printExplanation) {
+ fprintf(stderr,
+ "%s: Service type is too long (must be 63 characters or less)\n",
+ gProgramName);
+ }
+ result = mDNSfalse;
+ }
+ if (result && serviceType[0] == 0) {
+ if (printExplanation) {
+ fprintf(stderr,
+ "%s: Service type can't be empty\n",
+ gProgramName);
+ }
+ result = mDNSfalse;
+ }
+ 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
+ // an explanation of why not.
+{
+ mDNSBool result;
+
+ result = mDNStrue;
+ if (result && (portNumber <= 0 || portNumber > 65535)) {
+ if (printExplanation) {
+ fprintf(stderr,
+ "%s: Port number specified by -p must be in range 1..65535\n",
+ gProgramName);
+ }
+ result = mDNSfalse;
+ }
+ return result;
+}
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Command Line Arguments
+#endif
+
+static const char kDefaultPIDFile[] = "/var/run/mDNSResponder.pid";
+static const char kDefaultServiceType[] = "_afpovertcp._tcp.";
+static const char kDefaultServiceDomain[] = "local.";
+enum {
+ kDefaultPortNumber = 548
+};
+
+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",
+ gProgramName);
+ fprintf(stderr, " -v verbose mode, level is a number from 0 to 2\n");
+ fprintf(stderr, " 0 = no debugging info (default)\n");
+ fprintf(stderr, " 1 = standard debugging info\n");
+ fprintf(stderr, " 2 = intense debugging info\n");
+ fprintf(stderr, " can be cycled kill -USR1\n");
+ fprintf(stderr, " -r also bind to port 53 (port 5353 is always bound)\n");
+ 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");
+}
+
+static mDNSBool gAvoidPort53 = mDNStrue;
+static const char *gRichTextHostName = "";
+static const char *gServiceType = kDefaultServiceType;
+static const char *gServiceDomain = kDefaultServiceDomain;
+static mDNSu8 gServiceText[sizeof(RDataBody)];
+static mDNSu16 gServiceTextLen = 0;
+static int gPortNumber = kDefaultPortNumber;
+static const char *gServiceFile = "";
+static mDNSBool gDaemon = mDNSfalse;
+static const char *gPIDFile = kDefaultPIDFile;
+
+static void ParseArguments(int argc, char **argv)
+ // Parses our command line arguments into the global variables
+ // listed above.
+{
+ int ch;
+
+ // Set gProgramName to the last path component of argv[0]
+
+ gProgramName = strrchr(argv[0], '/');
+ if (gProgramName == NULL) {
+ gProgramName = argv[0];
+ } else {
+ gProgramName += 1;
+ }
+
+ // Parse command line options using getopt.
+
+ do {
+ ch = getopt(argc, argv, "v:rn:t:d:x:p:f:dPb");
+ if (ch != -1) {
+ switch (ch) {
+ case 'v':
+ gMDNSPlatformPosixVerboseLevel = atoi(optarg);
+ if (gMDNSPlatformPosixVerboseLevel < 0 || gMDNSPlatformPosixVerboseLevel > 2) {
+ fprintf(stderr,
+ "%s: Verbose mode must be in the range 0..2\n",
+ gProgramName);
+ exit(1);
+ }
+ break;
+ case 'r':
+ gAvoidPort53 = mDNSfalse;
+ break;
+ case 'n':
+ gRichTextHostName = optarg;
+ if ( ! CheckThatRichTextHostNameIsUsable(gRichTextHostName, mDNStrue) ) {
+ exit(1);
+ }
+ break;
+ case 't':
+ gServiceType = optarg;
+ if ( ! CheckThatServiceTypeIsUsable(gServiceType, mDNStrue) ) {
+ exit(1);
+ }
+ break;
+ 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) ) {
+ exit(1);
+ }
+ break;
+ case 'f':
+ gServiceFile = optarg;
+ break;
+ case 'b':
+ gDaemon = mDNStrue;
+ break;
+ case 'P':
+ gPIDFile = optarg;
+ break;
+ case '?':
+ default:
+ PrintUsage();
+ exit(1);
+ break;
+ }
+ }
+ } while (ch != -1);
+
+ // Check for any left over command line arguments.
+
+ if (optind != argc) {
+ PrintUsage();
+ fprintf(stderr, "%s: Unexpected argument '%s'\n", gProgramName, argv[optind]);
+ exit(1);
+ }
+
+ // Check for inconsistency between the arguments.
+
+ if ( (gRichTextHostName[0] == 0) && (gServiceFile[0] == 0) ) {
+ PrintUsage();
+ fprintf(stderr, "%s: You must specify a service to register (-n) or a service file (-f).\n", gProgramName);
+ exit(1);
+ }
+}
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Registration
+#endif
+
+typedef struct PosixService PosixService;
+
+struct PosixService {
+ ServiceRecordSet coreServ;
+ PosixService *next;
+ int serviceID;
+};
+
+static PosixService *gServiceList = NULL;
+
+static void RegistrationCallback(mDNS *const m, ServiceRecordSet *const thisRegistration, mStatus status)
+ // mDNS core calls this routine to tell us about the status of
+ // our registration. The appropriate action to take depends
+ // entirely on the value of status.
+{
+ switch (status) {
+
+ case mStatus_NoError:
+ debugf("Callback: %##s Name Registered", thisRegistration->RR_SRV.resrec.name.c);
+ // Do nothing; our name was successfully registered. We may
+ // get more call backs in the future.
+ break;
+
+ case mStatus_NameConflict:
+ debugf("Callback: %##s Name Conflict", thisRegistration->RR_SRV.resrec.name.c);
+
+ // In the event of a conflict, this sample RegistrationCallback
+ // 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.
+ //
+ // Also, what do we do if mDNS_RenameAndReregisterService returns an
+ // error. Right now I have no place to send that error to.
+
+ status = mDNS_RenameAndReregisterService(m, thisRegistration, mDNSNULL);
+ assert(status == mStatus_NoError);
+ break;
+
+ case mStatus_MemFree:
+ debugf("Callback: %##s Memory Free", thisRegistration->RR_SRV.resrec.name.c);
+
+ // When debugging is enabled, make sure that thisRegistration
+ // is not on our gServiceList.
+
+ #if !defined(NDEBUG)
+ {
+ PosixService *cursor;
+
+ cursor = gServiceList;
+ while (cursor != NULL) {
+ assert(&cursor->coreServ != thisRegistration);
+ cursor = cursor->next;
+ }
+ }
+ #endif
+ free(thisRegistration);
+ break;
+
+ default:
+ debugf("Callback: %##s Unknown Status %d", thisRegistration->RR_SRV.resrec.name.c, status);
+ break;
+ }
+}
+
+static int gServiceID = 0;
+
+static mStatus RegisterOneService(const char * richTextHostName,
+ const char * serviceType,
+ const char * serviceDomain,
+ const mDNSu8 text[],
+ mDNSu16 textLen,
+ long portNumber)
+{
+ mStatus status;
+ PosixService * thisServ;
+ mDNSOpaque16 port;
+ domainlabel name;
+ domainname type;
+ domainname domain;
+
+ status = mStatus_NoError;
+ thisServ = (PosixService *) malloc(sizeof(*thisServ));
+ if (thisServ == NULL) {
+ status = mStatus_NoMemoryErr;
+ }
+ if (status == mStatus_NoError) {
+ MakeDomainLabelFromLiteralString(&name, richTextHostName);
+ MakeDomainNameFromDNSNameString(&type, serviceType);
+ MakeDomainNameFromDNSNameString(&domain, serviceDomain);
+ port.b[0] = (portNumber >> 8) & 0x0FF;
+ port.b[1] = (portNumber >> 0) & 0x0FF;;
+ status = mDNS_RegisterService(&mDNSStorage, &thisServ->coreServ,
+ &name, &type, &domain, // Name, type, domain
+ NULL, port, // Host and port
+ text, textLen, // TXT data, length
+ NULL, 0, // Subtypes
+ mDNSInterface_Any, // Interace ID
+ RegistrationCallback, thisServ); // Callback and context
+ }
+ if (status == mStatus_NoError) {
+ thisServ->serviceID = gServiceID;
+ gServiceID += 1;
+
+ thisServ->next = gServiceList;
+ gServiceList = thisServ;
+
+ if (gMDNSPlatformPosixVerboseLevel > 0) {
+ fprintf(stderr,
+ "%s: Registered service %d, name '%s', type '%s', port %ld\n",
+ gProgramName,
+ thisServ->serviceID,
+ richTextHostName,
+ serviceType,
+ portNumber);
+ }
+ } else {
+ if (thisServ != NULL) {
+ free(thisServ);
+ }
+ }
+ return status;
+}
+
+static mDNSBool ReadALine(char *buf, size_t bufSize, FILE *fp)
+{
+ mDNSBool good;
+ size_t len;
+
+ good = (fgets(buf, bufSize, fp) != NULL);
+ if (good) {
+ len = strlen(buf);
+ good = (len > 0 && buf[len - 1] == '\n');
+ }
+ if (good) {
+ buf[len - 1] = 0;
+ }
+ return good;
+}
+
+static mStatus RegisterServicesInFile(const char *filePath)
+{
+ mStatus status;
+ FILE * fp;
+ 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;
+ do {
+ // Skip over any blank lines.
+ do {
+ ch = fgetc(fp);
+ } while ( ch == '\n' || ch == '\r' );
+ if (ch != EOF) {
+ good = (ungetc(ch, fp) == ch);
+ }
+
+ // 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
+ }
+ }
+ }
+ } while (good && !feof(fp));
+
+ if ( ! good ) {
+ fprintf(stderr, "%s: Error reading service file %s\n", gProgramName, gServiceFile);
+ }
+ }
+
+ if (fp != NULL) {
+ junk = fclose(fp);
+ assert(junk == 0);
+ }
+
+ return status;
+}
+
+static mStatus RegisterOurServices(void)
+{
+ mStatus status;
+
+ status = mStatus_NoError;
+ if (gRichTextHostName[0] != 0) {
+ status = RegisterOneService(gRichTextHostName,
+ gServiceType,
+ gServiceDomain,
+ gServiceText, gServiceTextLen,
+ gPortNumber);
+ }
+ if (status == mStatus_NoError && gServiceFile[0] != 0) {
+ status = RegisterServicesInFile(gServiceFile);
+ }
+ return status;
+}
+
+static void DeregisterOurServices(void)
+{
+ PosixService *thisServ;
+ int thisServID;
+
+ while (gServiceList != NULL) {
+ thisServ = gServiceList;
+ gServiceList = thisServ->next;
+
+ thisServID = thisServ->serviceID;
+
+ mDNS_DeregisterService(&mDNSStorage, &thisServ->coreServ);
+
+ if (gMDNSPlatformPosixVerboseLevel > 0) {
+ fprintf(stderr,
+ "%s: Deregistered service %d\n",
+ gProgramName,
+ thisServ->serviceID);
+ }
+ }
+}
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#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;
+ int result;
+
+ // Parse our command line arguments. This won't come back if there's an error.
+
+ ParseArguments(argc, argv);
+
+ // If we're told to run as a daemon, then do that straight away.
+ // Note that we don't treat the inability to create our PID
+ // file as an error. Also note that we assign getpid to a long
+ // because printf has no format specified for pid_t.
+
+ if (gDaemon) {
+ if (gMDNSPlatformPosixVerboseLevel > 0) {
+ fprintf(stderr, "%s: Starting in daemon mode\n", gProgramName);
+ }
+ daemon(0,0);
+ {
+ FILE *fp;
+ int junk;
+
+ fp = fopen(gPIDFile, "w");
+ if (fp != NULL) {
+ fprintf(fp, "%ld\n", (long) getpid());
+ junk = fclose(fp);
+ assert(junk == 0);
+ }
+ }
+ } else {
+ if (gMDNSPlatformPosixVerboseLevel > 0) {
+ fprintf(stderr, "%s: Starting in foreground mode, PID %ld\n", gProgramName, (long) getpid());
+ }
+ }
+
+ status = mDNS_Init(&mDNSStorage, &PlatformStorage,
+ mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
+ mDNS_Init_AdvertiseLocalAddresses,
+ mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
+ if (status != mStatus_NoError) return(2);
+
+ status = RegisterOurServices();
+ if (status != mStatus_NoError) return(2);
+
+ signal(SIGHUP, HandleSigHup); // SIGHUP has to be sent by kill -HUP <pid>
+ signal(SIGINT, HandleSigInt); // SIGINT is what you get for a Ctrl-C
+ signal(SIGQUIT, HandleSigQuit); // SIGQUIT is what you get for a Ctrl-\ (indeed)
+ signal(SIGUSR1, HandleSigUsr1); // SIGUSR1 has to be sent by kill -USR1 <pid>
+
+ while (!gStopNow)
+ {
+ int nfds = 0;
+ fd_set readfds;
+ struct timeval timeout;
+ int result;
+
+ // 1. Set up the fd_set as usual here.
+ // This example client has no file descriptors of its own,
+ // but a real application would call FD_SET to add them to the set here
+ FD_ZERO(&readfds);
+
+ // 2. Set up the timeout.
+ // This example client has no other work it needs to be doing,
+ // so we set an effectively infinite timeout
+ timeout.tv_sec = 0x3FFFFFFF;
+ timeout.tv_usec = 0;
+
+ // 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout
+ mDNSPosixGetFDSet(&mDNSStorage, &nfds, &readfds, &timeout);
+
+ // 4. Call select as normal
+ verbosedebugf("select(%d, %d.%06d)", nfds, timeout.tv_sec, timeout.tv_usec);
+ result = select(nfds, &readfds, NULL, NULL, &timeout);
+
+ if (result < 0)
+ {
+ verbosedebugf("select() returned %d errno %d", result, errno);
+ if (errno != EINTR) gStopNow = mDNStrue;
+ else
+ {
+ if (gReceivedSigUsr1)
+ {
+ gReceivedSigUsr1 = mDNSfalse;
+ gMDNSPlatformPosixVerboseLevel += 1;
+ if (gMDNSPlatformPosixVerboseLevel > 2)
+ gMDNSPlatformPosixVerboseLevel = 0;
+ if ( gMDNSPlatformPosixVerboseLevel > 0 )
+ fprintf(stderr, "\nVerbose level %d\n", gMDNSPlatformPosixVerboseLevel);
+ }
+ if (gReceivedSigHup)
+ {
+ if (gMDNSPlatformPosixVerboseLevel > 0)
+ fprintf(stderr, "\nSIGHUP\n");
+ gReceivedSigHup = mDNSfalse;
+ DeregisterOurServices();
+ status = mDNSPlatformPosixRefreshInterfaceList(&mDNSStorage);
+ if (status != mStatus_NoError) break;
+ status = RegisterOurServices();
+ if (status != mStatus_NoError) break;
+ }
+ }
+ }
+ else
+ {
+ // 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work
+ mDNSPosixProcessFDSet(&mDNSStorage, &readfds);
+
+ // 6. This example client has no other work it needs to be doing,
+ // but a real client would do its work here
+ // ... (do work) ...
+ }
+ }
+
+ debugf("Exiting");
+
+ DeregisterOurServices();
+ mDNS_Close(&mDNSStorage);
+
+ if (status == mStatus_NoError) {
+ result = 0;
+ } else {
+ result = 2;
+ }
+ if ( (result != 0) || (gMDNSPlatformPosixVerboseLevel > 0) ) {
+ fprintf(stderr, "%s: Finished with status %ld, result %d\n", gProgramName, status, result);
+ }
+
+ return result;
+}
--- /dev/null
+Tweedlebug
+_afpovertcp._tcp.
+name=val1
+548
+
+Tweedlebug2
+_afpovertcp._tcp. local.
+name=val2\ 1name2=anotherattribute
+548
+
+Tweedlebug3
+_afpovertcp._tcp. apple.com.
+name=val3
+548
--- /dev/null
+/*
+ * 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@
+ *
+ * Formatting notes:
+ * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
+ * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
+ * but for the sake of brevity here I will say just this: Curly braces are not syntactially
+ * part of an "if" statement; they are the beginning and ending markers of a compound statement;
+ * therefore common sense dictates that if they are part of a compound statement then they
+ * should be indented to the same level as everything else in that compound statement.
+ * Indenting curly braces at the same level as the "if" implies that curly braces are
+ * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
+ * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
+ * 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: mDNSPosix.c,v $
+Revision 1.24 2003/08/18 23:12:23 cheshire
+<rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformTimeNow()
+
+Revision 1.23 2003/08/12 19:56:26 cheshire
+Update to APSL 2.0
+
+Revision 1.22 2003/08/06 18:46:15 cheshire
+LogMsg() errors are serious -- always report them to stderr, regardless of debugging level
+
+Revision 1.21 2003/08/06 18:20:51 cheshire
+Makefile cleanup
+
+Revision 1.20 2003/08/05 23:56:26 cheshire
+Update code to compile with the new mDNSCoreReceive() function that requires a TTL
+(Right now mDNSPosix.c just reports 255 -- we should fix this)
+
+Revision 1.19 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.18 2003/07/14 18:11:54 cheshire
+Fix stricter compiler warnings
+
+Revision 1.17 2003/07/13 01:08:38 cheshire
+There's not much point running mDNS over a point-to-point link; exclude those
+
+Revision 1.16 2003/07/02 21:19:59 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.15 2003/06/18 05:48:41 cheshire
+Fix warnings
+
+Revision 1.14 2003/05/26 03:21:30 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.13 2003/05/26 03:01:28 cheshire
+<rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
+
+Revision 1.12 2003/05/21 03:49:18 cheshire
+Fix warning
+
+Revision 1.11 2003/05/06 00:00:50 cheshire
+<rdar://problem/3248914> Rationalize naming of domainname manipulation functions
+
+Revision 1.10 2003/04/25 01:45:57 cheshire
+<rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name
+
+Revision 1.9 2003/03/20 21:10:31 cheshire
+Fixes done at IETF 56 to make mDNSProxyResponderPosix run on Solaris
+
+Revision 1.8 2003/03/15 04:40:38 cheshire
+Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID"
+
+Revision 1.7 2003/03/13 03:46:21 cheshire
+Fixes to make the code build on Linux
+
+Revision 1.6 2003/03/08 00:35:56 cheshire
+Switched to using new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt")
+
+Revision 1.5 2002/12/23 22:13:31 jgraessl
+Reviewed by: Stuart Cheshire
+Initial IPv6 support for mDNSResponder.
+
+Revision 1.4 2002/09/27 01:47:45 cheshire
+Workaround for Linux 2.0 systems that don't have IP_PKTINFO
+
+Revision 1.3 2002/09/21 20:44:53 zarzycki
+Added APSL info
+
+Revision 1.2 2002/09/19 21:25:36 cheshire
+mDNS_snprintf() doesn't need to be in a separate file
+
+Revision 1.1 2002/09/17 06:24:34 cheshire
+First checkin
+*/
+
+#include "mDNSClientAPI.h" // Defines the interface provided to the client layer above
+#include "mDNSPlatformFunctions.h" // Defines the interface to the supporting layer below
+#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <netinet/in.h>
+
+#include "mDNSUNP.h"
+
+// ***************************************************************************
+// Structures
+
+// PosixNetworkInterface is a record extension of the core NetworkInterfaceInfo
+// type that supports extra fields needed by the Posix platform.
+//
+// IMPORTANT: coreIntf must be the first field in the structure because
+// we cast between pointers to the two different types regularly.
+
+typedef struct PosixNetworkInterface PosixNetworkInterface;
+
+struct PosixNetworkInterface
+ {
+ NetworkInterfaceInfo coreIntf;
+ const char * intfName;
+ PosixNetworkInterface * aliasIntf;
+ int index;
+ int multicastSocket;
+ int multicastSocketv6;
+ };
+
+// ***************************************************************************
+// Globals (for debugging)
+
+static int num_registered_interfaces = 0;
+static int num_pkts_accepted = 0;
+static int num_pkts_rejected = 0;
+
+// ***************************************************************************
+// Functions
+
+int gMDNSPlatformPosixVerboseLevel = 0;
+
+// Note, this uses mDNS_vsnprintf instead of standard "vsnprintf", because mDNS_vsnprintf knows
+// how to print special data types like IP addresses and length-prefixed domain names
+mDNSexport void debugf_(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);
+ if (gMDNSPlatformPosixVerboseLevel >= 1)
+ fprintf(stderr, "%s\n", buffer);
+ fflush(stderr);
+ }
+
+mDNSexport void verbosedebugf_(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);
+ if (gMDNSPlatformPosixVerboseLevel >= 2)
+ fprintf(stderr, "%s\n", buffer);
+ fflush(stderr);
+ }
+
+mDNSexport void LogMsg(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);
+ fprintf(stderr, "%s\n", buffer);
+ fflush(stderr);
+ }
+
+#define PosixErrorToStatus(errNum) ((errNum) == 0 ? mStatus_NoError : mStatus_UnknownErr)
+
+static void SockAddrTomDNSAddr(const struct sockaddr *const sa, mDNSAddr *ipAddr, mDNSIPPort *ipPort)
+ {
+ switch (sa->sa_family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in* sin = (struct sockaddr_in*)sa;
+ ipAddr->type = mDNSAddrType_IPv4;
+ ipAddr->ip.v4.NotAnInteger = sin->sin_addr.s_addr;
+ if (ipPort) ipPort->NotAnInteger = sin->sin_port;
+ break;
+ }
+
+#ifdef mDNSIPv6Support
+ case AF_INET6:
+ {
+ struct sockaddr_in6* sin6 = (struct sockaddr_in6*)sa;
+ assert(sin6->sin6_len == sizeof(*sin6));
+ ipAddr->type = mDNSAddrType_IPv6;
+ ipAddr->ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr;
+ if (ipPort) ipPort->NotAnInteger = sin6->sin6_port;
+ break;
+ }
+#endif
+
+ default:
+ verbosedebugf("SockAddrTomDNSAddr: Uknown address family %d\n", sa->sa_family);
+ ipAddr->type = mDNSAddrType_None;
+ if (ipPort) ipPort->NotAnInteger = 0;
+ break;
+ }
+ }
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Send and Receive
+#endif
+
+// mDNS core calls this routine when it needs to send a packet.
+mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end,
+ mDNSInterfaceID InterfaceID, mDNSIPPort srcPort, const mDNSAddr *dst, mDNSIPPort dstPort)
+ {
+ int err;
+ struct sockaddr_storage to;
+ PosixNetworkInterface * thisIntf;
+
+ assert(m != NULL);
+ assert(msg != NULL);
+ assert(end != NULL);
+ assert( (((char *) end) - ((char *) msg)) > 0 );
+ assert(InterfaceID != 0); // Can't send from zero source address
+ assert(srcPort.NotAnInteger != 0); // Nor from a zero source port
+ assert(dstPort.NotAnInteger != 0); // Nor from a zero source port
+
+ if (dst->type == mDNSAddrType_IPv4)
+ {
+ struct sockaddr_in *sin = (struct sockaddr_in*)&to;
+#ifndef NOT_HAVE_SA_LEN
+ sin->sin_len = sizeof(*sin);
+#endif
+ sin->sin_family = AF_INET;
+ sin->sin_port = dstPort.NotAnInteger;
+ sin->sin_addr.s_addr = dst->ip.v4.NotAnInteger;
+ }
+
+#ifdef mDNSIPv6Support
+ else if (dst->type == mDNSAddrType_IPv6)
+ {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&to;
+ mDNSPlatformMemZero(sin6, sizeof(*sin6));
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = dstPort.NotAnInteger;
+ sin6->sin6_addr = *(struct in6_addr*)&dst->ip.v6;
+ }
+#endif
+
+ err = 0;
+ thisIntf = (PosixNetworkInterface *)(InterfaceID);
+ if (dst->type == mDNSAddrType_IPv4)
+ err = sendto(thisIntf->multicastSocket, msg, (char*)end - (char*)msg, 0, (struct sockaddr *)&to, GET_SA_LEN(to));
+
+#ifdef mDNSIPv6Support
+ else if (dst->type == mDNSAddrType_IPv6)
+ err = sendto(thisIntf->multicastSocketv6, msg, (char*)end - (char*)msg, 0, (struct sockaddr *)&to, GET_SA_LEN(to));
+#endif
+
+ if (err > 0) err = 0;
+ else if (err < 0)
+ verbosedebugf("mDNSPlatformSendUDP got error %d (%s) sending packet to %#a on interface %#a/%s/%d",
+ errno, strerror(errno), dst, &thisIntf->coreIntf.ip, thisIntf->intfName, thisIntf->index);
+
+ return PosixErrorToStatus(err);
+ }
+
+// This routine is called when the main loop detects that data is available on a socket.
+static void SocketDataReady(mDNS *const m, PosixNetworkInterface *intf, int skt)
+ {
+ mDNSAddr senderAddr, destAddr;
+ mDNSIPPort senderPort;
+ ssize_t packetLen;
+ DNSMessage packet;
+ struct my_in_pktinfo packetInfo;
+ struct sockaddr_storage from;
+ socklen_t fromLen;
+ int flags;
+ mDNSBool reject;
+
+ assert(m != NULL);
+ assert(intf != NULL);
+ assert(skt >= 0);
+
+ fromLen = sizeof(from);
+ flags = 0;
+ packetLen = recvfrom_flags(skt, &packet, sizeof(packet), &flags, (struct sockaddr *) &from, &fromLen, &packetInfo);
+
+ if (packetLen >= 0)
+ {
+ SockAddrTomDNSAddr((struct sockaddr*)&from, &senderAddr, &senderPort);
+ SockAddrTomDNSAddr((struct sockaddr*)&packetInfo.ipi_addr, &destAddr, NULL);
+
+ // If we have broken IP_RECVDSTADDR functionality (so far
+ // I've only seen this on OpenBSD) then apply a hack to
+ // convince mDNS Core that this isn't a spoof packet.
+ // Basically what we do is check to see whether the
+ // packet arrived as a multicast and, if so, set its
+ // destAddr to the mDNS address.
+ //
+ // I must admit that I could just be doing something
+ // wrong on OpenBSD and hence triggering this problem
+ // but I'm at a loss as to how.
+ //
+ // If this platform doesn't have IP_PKTINFO or IP_RECVDSTADDR, then we have
+ // no way to tell the destination address or interface this packet arrived on,
+ // so all we can do is just assume it's a multicast
+
+ #if HAVE_BROKEN_RECVDSTADDR || (!defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR))
+ if ( (destAddr.NotAnInteger == 0) && (flags & MSG_MCAST) )
+ {
+ destAddr.type == senderAddr.type;
+ if (senderAddr.type == mDNSAddrType_IPv4) destAddr.ip.v4 = AllDNSLinkGroup;
+ else if (senderAddr.type == mDNSAddrType_IPv6) destAddr.ip.v6 = AllDNSLinkGroupv6;
+ }
+ #endif
+
+ // We only accept the packet if the interface on which it came
+ // in matches the interface associated with this socket.
+ // We do this match by name or by index, depending on which
+ // information is available. recvfrom_flags sets the name
+ // to "" if the name isn't available, or the index to -1
+ // if the index is available. This accomodates the various
+ // different capabilities of our target platforms.
+
+ reject = mDNSfalse;
+ if ( packetInfo.ipi_ifname[0] != 0 ) reject = (strcmp(packetInfo.ipi_ifname, intf->intfName) != 0);
+ else if ( packetInfo.ipi_ifindex != -1 ) reject = (packetInfo.ipi_ifindex != intf->index);
+
+ if (reject)
+ {
+ verbosedebugf("SocketDataReady ignored a packet from %#a to %#a on interface %s/%d expecting %#a/%s/%d",
+ &senderAddr, &destAddr, packetInfo.ipi_ifname, packetInfo.ipi_ifindex,
+ &intf->coreIntf.ip, intf->intfName, intf->index);
+ packetLen = -1;
+ num_pkts_rejected++;
+ if (num_pkts_rejected > (num_pkts_accepted + 1) * (num_registered_interfaces + 1) * 2)
+ {
+ fprintf(stderr,
+ "*** WARNING: Received %d packets; Accepted %d packets; Rejected %d packets because of interface mismatch\n",
+ num_pkts_accepted + num_pkts_rejected, num_pkts_accepted, num_pkts_rejected);
+ num_pkts_accepted = 0;
+ num_pkts_rejected = 0;
+ }
+ }
+ else
+ {
+ verbosedebugf("SocketDataReady got a packet from %#a to %#a on interface %#a/%s/%d",
+ &senderAddr, &destAddr, &intf->coreIntf.ip, intf->intfName, intf->index);
+ num_pkts_accepted++;
+ }
+ }
+
+ if (packetLen >= 0 && packetLen < (ssize_t)sizeof(DNSMessageHeader))
+ {
+ debugf("SocketDataReady packet length (%d) too short", packetLen);
+ packetLen = -1;
+ }
+
+ if (packetLen >= 0)
+ mDNSCoreReceive(m, &packet, (mDNSu8 *)&packet + packetLen,
+ &senderAddr, senderPort, &destAddr, MulticastDNSPort, intf->coreIntf.InterfaceID, 255);
+ }
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Init and Term
+#endif
+
+// On OS X this gets the text of the field labelled "Computer Name" in the Sharing Prefs Control Panel
+// Other platforms can either get the information from the appropriate place,
+// or they can alternatively just require all registering services to provide an explicit name
+mDNSlocal void GetUserSpecifiedFriendlyComputerName(domainlabel *const namelabel)
+ {
+ MakeDomainLabelFromLiteralString(namelabel, "Fill in Default Service Name Here");
+ }
+
+// This gets the current hostname, truncating it at the first dot if necessary
+mDNSlocal void GetUserSpecifiedRFC1034ComputerName(domainlabel *const namelabel)
+ {
+ int len = 0;
+ gethostname(&namelabel->c[1], MAX_DOMAIN_LABEL);
+ while (len < MAX_DOMAIN_LABEL && namelabel->c[len+1] && namelabel->c[len+1] != '.') len++;
+ namelabel->c[0] = len;
+ }
+
+// Searches the interface list looking for the named interface.
+// Returns a pointer to if it found, or NULL otherwise.
+static PosixNetworkInterface *SearchForInterfaceByName(mDNS *const m, const char *intfName)
+ {
+ PosixNetworkInterface *intf;
+
+ assert(m != NULL);
+ assert(intfName != NULL);
+
+ intf = (PosixNetworkInterface*)(m->HostInterfaces);
+ while ( (intf != NULL) && (strcmp(intf->intfName, intfName) != 0) )
+ intf = (PosixNetworkInterface *)(intf->coreIntf.next);
+
+ return intf;
+ }
+
+// Frees the specified PosixNetworkInterface structure. The underlying
+// interface must have already been deregistered with the mDNS core.
+static void FreePosixNetworkInterface(PosixNetworkInterface *intf)
+ {
+ assert(intf != NULL);
+ if (intf->intfName != NULL) free((void *)intf->intfName);
+ if (intf->multicastSocket != -1) assert(close(intf->multicastSocket) == 0);
+ if (intf->multicastSocketv6 != -1) assert(close(intf->multicastSocketv6) == 0);
+ free(intf);
+ }
+
+// Grab the first interface, deregister it, free it, and repeat until done.
+static void ClearInterfaceList(mDNS *const m)
+ {
+ assert(m != NULL);
+
+ while (m->HostInterfaces)
+ {
+ PosixNetworkInterface *intf = (PosixNetworkInterface*)(m->HostInterfaces);
+ mDNS_DeregisterInterface(m, &intf->coreIntf);
+ if (gMDNSPlatformPosixVerboseLevel > 0) fprintf(stderr, "Deregistered interface %s\n", intf->intfName);
+ FreePosixNetworkInterface(intf);
+ }
+ num_registered_interfaces = 0;
+ num_pkts_accepted = 0;
+ num_pkts_rejected = 0;
+ }
+
+// Sets up a multicast send/receive socket for the specified
+// port on the interface specified by the IP addrelss intfAddr.
+static int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interfaceIndex, int *sktPtr)
+ {
+ int err = 0;
+ static const int kOn = 1;
+ static const int kIntTwoFiveFive = 255;
+ static const unsigned char kByteTwoFiveFive = 255;
+
+ (void) interfaceIndex; // Unused
+ assert(intfAddr != NULL);
+ assert(sktPtr != NULL);
+ assert(*sktPtr == -1);
+
+ // Open the socket...
+ if (intfAddr->sa_family == AF_INET) *sktPtr = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+#ifdef mDNSIPv6Support
+ else if (intfAddr->sa_family == AF_INET6) *sktPtr = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+#endif
+ else return EINVAL;
+
+ if (*sktPtr < 0) { err = errno; perror("socket"); }
+
+ // ... with a shared UDP port
+ if (err == 0)
+ {
+ #if defined(SO_REUSEPORT)
+ err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEPORT, &kOn, sizeof(kOn));
+ #elif defined(SO_REUSEADDR)
+ err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn));
+ #else
+ #error This platform has no way to avoid address busy errors on multicast.
+ #endif
+ if (err < 0) { err = errno; perror("setsockopt - SO_REUSExxxx"); }
+ }
+
+ // We want to receive destination addresses and interface identifiers.
+ if (intfAddr->sa_family == AF_INET)
+ {
+ struct ip_mreq imr;
+ struct sockaddr_in bindAddr;
+ if (err == 0)
+ {
+ #if defined(IP_PKTINFO) // Linux
+ err = setsockopt(*sktPtr, IPPROTO_IP, IP_PKTINFO, &kOn, sizeof(kOn));
+ if (err < 0) { err = errno; perror("setsockopt - IP_PKTINFO"); }
+ #elif defined(IP_RECVDSTADDR) || defined(IP_RECVIF) // BSD and Solaris
+ #if defined(IP_RECVDSTADDR)
+ err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVDSTADDR, &kOn, sizeof(kOn));
+ if (err < 0) { err = errno; perror("setsockopt - IP_RECVDSTADDR"); }
+ #endif
+ #if defined(IP_RECVIF)
+ if (err == 0)
+ {
+ err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVIF, &kOn, sizeof(kOn));
+ if (err < 0) { err = errno; perror("setsockopt - IP_RECVIF"); }
+ }
+ #endif
+ #else
+ #warning This platform has no way to get the destination interface information -- will only work for single-homed hosts
+ #endif
+ }
+
+ // Add multicast group membership on this interface
+ if (err == 0)
+ {
+ imr.imr_multiaddr.s_addr = AllDNSLinkGroup.NotAnInteger;
+ imr.imr_interface = ((struct sockaddr_in*)intfAddr)->sin_addr;
+ err = setsockopt(*sktPtr, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr));
+ if (err < 0) { err = errno; perror("setsockopt - IP_ADD_MEMBERSHIP"); }
+ }
+
+ // Specify outgoing interface too
+ if (err == 0)
+ {
+ err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_IF, &((struct sockaddr_in*)intfAddr)->sin_addr, sizeof(struct in_addr));
+ if (err < 0) { err = errno; perror("setsockopt - IP_MULTICAST_IF"); }
+ }
+
+ // Per the mDNS spec, send unicast packets with TTL 255
+ if (err == 0)
+ {
+ err = setsockopt(*sktPtr, IPPROTO_IP, IP_TTL, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive));
+ if (err < 0) { err = errno; perror("setsockopt - IP_TTL"); }
+ }
+
+ // and multicast packets with TTL 255 too
+ // There's some debate as to whether IP_MULTICAST_TTL is an int or a byte so we just try both.
+ if (err == 0)
+ {
+ err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_TTL, &kByteTwoFiveFive, sizeof(kByteTwoFiveFive));
+ if (err < 0 && errno == EINVAL)
+ err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_TTL, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive));
+ if (err < 0) { err = errno; perror("setsockopt - IP_MULTICAST_TTL"); }
+ }
+
+ // And start listening for packets
+ if (err == 0)
+ {
+ bindAddr.sin_family = AF_INET;
+ bindAddr.sin_port = port.NotAnInteger;
+ bindAddr.sin_addr.s_addr = INADDR_ANY; // Want to receive multicasts AND unicasts on this socket
+ err = bind(*sktPtr, (struct sockaddr *) &bindAddr, sizeof(bindAddr));
+ if (err < 0) { err = errno; perror("bind"); fflush(stderr); }
+ }
+ } // endif (intfAddr->sa_family == AF_INET)
+
+#ifdef mDNSIPv6Support
+ else if (intfAddr->sa_family == AF_INET6)
+ {
+ struct ipv6_mreq imr6;
+ struct sockaddr_in6 bindAddr6;
+ if (err == 0)
+ {
+ #if defined(IPV6_PKTINFO)
+ err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_PKTINFO, &kOn, sizeof(kOn));
+ if (err < 0) { err = errno; perror("setsockopt - IPV6_PKTINFO"); }
+ #else
+ #warning This platform has no way to get the destination interface information for IPv6 -- will only work for single-homed hosts
+ #endif
+ }
+
+ // Add multicast group membership on this interface
+ if (err == 0)
+ {
+ imr6.ipv6mr_multiaddr = *(const struct in6_addr*)&AllDNSLinkGroupv6;
+ imr6.ipv6mr_interface = interfaceIndex;
+ err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_JOIN_GROUP, &imr6, sizeof(imr6));
+ if (err < 0)
+ {
+ err = errno;
+ verbosedebugf("IPV6_JOIN_GROUP %.16a on %d failed.\n", &imr6.ipv6mr_multiaddr, imr6.ipv6mr_interface);
+ perror("setsockopt - IPV6_JOIN_GROUP");
+ }
+ }
+
+ // Specify outgoing interface too
+ if (err == 0)
+ {
+ u_int multicast_if = interfaceIndex;
+ err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_if, sizeof(multicast_if));
+ if (err < 0) { err = errno; perror("setsockopt - IPV6_MULTICAST_IF"); }
+ }
+
+ // We want to receive only IPv6 packets on this socket.
+ // Without this option, we may get IPv4 addresses as mapped addresses.
+ if (err == 0)
+ {
+ err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_V6ONLY, &kOn, sizeof(kOn));
+ if (err < 0) { err = errno; perror("setsockopt - IPV6_V6ONLY"); }
+ }
+
+ // Per the mDNS spec, send unicast packets with TTL 255
+ if (err == 0)
+ {
+ err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive));
+ if (err < 0) { err = errno; perror("setsockopt - IPV6_UNICAST_HOPS"); }
+ }
+
+ // and multicast packets with TTL 255 too
+ // There's some debate as to whether IPV6_MULTICAST_HOPS is an int or a byte so we just try both.
+ if (err == 0)
+ {
+ err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kByteTwoFiveFive, sizeof(kByteTwoFiveFive));
+ if (err < 0 && errno == EINVAL)
+ err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive));
+ if (err < 0) { err = errno; perror("setsockopt - IPV6_MULTICAST_HOPS"); }
+ }
+
+ // And start listening for packets
+ if (err == 0)
+ {
+ mDNSPlatformMemZero(&bindAddr6, sizeof(bindAddr6));
+ bindAddr6.sin6_len = sizeof(bindAddr6);
+ bindAddr6.sin6_family = AF_INET6;
+ bindAddr6.sin6_port = port.NotAnInteger;
+ bindAddr6.sin6_flowinfo = 0;
+// bindAddr6.sin6_addr.s_addr = IN6ADDR_ANY_INIT; // Want to receive multicasts AND unicasts on this socket
+ bindAddr6.sin6_scope_id = 0;
+ err = bind(*sktPtr, (struct sockaddr *) &bindAddr6, sizeof(bindAddr6));
+ if (err < 0) { err = errno; perror("bind"); fflush(stderr); }
+ }
+ } // endif (intfAddr->sa_family == AF_INET6)
+#endif
+
+ // Set the socket to non-blocking.
+ if (err == 0)
+ {
+ err = fcntl(*sktPtr, F_GETFL, 0);
+ if (err < 0) err = errno;
+ else
+ {
+ err = fcntl(*sktPtr, F_SETFL, err | O_NONBLOCK);
+ if (err < 0) err = errno;
+ }
+ }
+
+ // Clean up
+ if (err != 0 && *sktPtr != -1) { assert(close(*sktPtr) == 0); *sktPtr = -1; }
+ assert( (err == 0) == (*sktPtr != -1) );
+ return err;
+ }
+
+// Creates a PosixNetworkInterface for the interface whose IP address is
+// intfAddr and whose name is intfName and registers it with mDNS core.
+static int SetupOneInterface(mDNS *const m, struct sockaddr *intfAddr, const char *intfName)
+ {
+ int err = 0;
+ PosixNetworkInterface *intf;
+ PosixNetworkInterface *alias = NULL;
+
+ assert(m != NULL);
+ assert(intfAddr != NULL);
+ assert(intfName != NULL);
+
+ // Allocate the interface structure itself.
+ intf = malloc(sizeof(*intf));
+ if (intf == NULL) { assert(0); err = ENOMEM; }
+
+ // And make a copy of the intfName.
+ if (err == 0)
+ {
+ intf->intfName = strdup(intfName);
+ if (intf->intfName == NULL) { assert(0); err = ENOMEM; }
+ }
+
+ if (err == 0)
+ {
+ // Set up the fields required by the mDNS core.
+ SockAddrTomDNSAddr(intfAddr, &intf->coreIntf.ip, NULL);
+ intf->coreIntf.Advertise = m->AdvertiseLocalAddresses;
+
+ // Set up the extra fields in PosixNetworkInterface.
+ assert(intf->intfName != NULL); // intf->intfName already set up above
+ intf->index = if_nametoindex(intf->intfName);
+ intf->multicastSocket = -1;
+ intf->multicastSocketv6 = -1;
+ alias = SearchForInterfaceByName(m, intf->intfName);
+ if (alias == NULL) alias = intf;
+ intf->coreIntf.InterfaceID = (mDNSInterfaceID)alias;
+
+ if (alias != intf)
+ debugf("SetupOneInterface: %s %#a is an alias of %#a", intfName, &intf->coreIntf.ip, &alias->coreIntf.ip);
+ }
+
+ // Set up the multicast socket
+ if (err == 0)
+ {
+ if (alias->multicastSocket == -1 && intfAddr->sa_family == AF_INET)
+ err = SetupSocket(intfAddr, MulticastDNSPort, intf->index, &alias->multicastSocket);
+#ifdef mDNSIPv6Support
+ else if (alias->multicastSocketv6 == -1 && intfAddr->sa_family == AF_INET6)
+ err = SetupSocket(intfAddr, MulticastDNSPort, intf->index, &alias->multicastSocketv6);
+#endif
+ }
+
+ // The interface is all ready to go, let's register it with the mDNS core.
+ if (err == 0)
+ err = mDNS_RegisterInterface(m, &intf->coreIntf);
+
+ // Clean up.
+ if (err == 0)
+ {
+ num_registered_interfaces++;
+ debugf("SetupOneInterface: %s %#a Registered", intf->intfName, &intf->coreIntf.ip);
+ if (gMDNSPlatformPosixVerboseLevel > 0)
+ fprintf(stderr, "Registered interface %s\n", intf->intfName);
+ }
+ else
+ {
+ // Use intfName instead of intf->intfName in the next line to avoid dereferencing NULL.
+ debugf("SetupOneInterface: %s %#a failed to register %d", intfName, &intf->coreIntf.ip, err);
+ if (intf) { FreePosixNetworkInterface(intf); intf = NULL; }
+ }
+
+ assert( (err == 0) == (intf != NULL) );
+
+ return err;
+ }
+
+static int SetupInterfaceList(mDNS *const m)
+ {
+ mDNSBool foundav4 = mDNSfalse;
+ int err = 0;
+ struct ifi_info *intfList = get_ifi_info(AF_INET, mDNStrue);
+ struct ifi_info *firstLoopback = NULL;
+
+ assert(m != NULL);
+ debugf("SetupInterfaceList");
+
+ if (intfList == NULL) err = ENOENT;
+
+#ifdef mDNSIPv6Support
+ if (err == 0) /* Link the IPv6 list to the end of the IPv4 list */
+ {
+ struct ifi_info **p = &intfList;
+ while (*p) p = &(*p)->ifi_next;
+ *p = get_ifi_info(AF_INET6, mDNStrue);
+ }
+#endif
+
+ if (err == 0)
+ {
+ struct ifi_info *i = intfList;
+ while (i)
+ {
+ if ( ((i->ifi_addr->sa_family == AF_INET)
+#ifdef mDNSIPv6Support
+ || (i->ifi_addr->sa_family == AF_INET6)
+#endif
+ ) && (i->ifi_flags & IFF_UP) && !(i->ifi_flags & IFF_POINTOPOINT) )
+ {
+ if (i->ifi_flags & IFF_LOOPBACK)
+ {
+ if (firstLoopback == NULL)
+ firstLoopback = i;
+ }
+ else
+ {
+ if (SetupOneInterface(m, i->ifi_addr, i->ifi_name) == 0)
+ if (i->ifi_addr->sa_family == AF_INET)
+ foundav4 = mDNStrue;
+ }
+ }
+ i = i->ifi_next;
+ }
+
+ // If we found no normal interfaces but we did find a loopback interface, register the
+ // loopback interface. This allows self-discovery if no interfaces are configured.
+ // 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 ( (m->HostInterfaces == NULL) && (firstLoopback != NULL) )
+ if ( !foundav4 && firstLoopback )
+ (void) SetupOneInterface(m, firstLoopback->ifi_addr, firstLoopback->ifi_name);
+ }
+
+ // Clean up.
+ if (intfList != NULL) free_ifi_info(intfList);
+ return err;
+ }
+
+// mDNS core calls this routine to initialise the platform-specific data.
+mDNSexport mStatus mDNSPlatformInit(mDNS *const m)
+ {
+ int err;
+ assert(m != NULL);
+
+ // Tell mDNS core the names of this machine.
+
+ // 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
+ m->hostlabel.c[0] = 0;
+ GetUserSpecifiedRFC1034ComputerName(&m->hostlabel);
+ if (m->hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->hostlabel, "Macintosh");
+
+ mDNS_GenerateFQDN(m);
+
+ // Tell mDNS core about the network interfaces on this machine.
+ err = SetupInterfaceList(m);
+
+ // We don't do asynchronous initialization on the Posix platform, so by the time
+ // we get here the setup will already have succeeded or failed. If it succeeded,
+ // we should just call mDNSCoreInitComplete() immediately.
+ if (err == 0)
+ mDNSCoreInitComplete(m, mStatus_NoError);
+
+ return PosixErrorToStatus(err);
+ }
+
+// mDNS core calls this routine to clean up the platform-specific data.
+// In our case all we need to do is to tear down every network interface.
+mDNSexport void mDNSPlatformClose(mDNS *const m)
+ {
+ assert(m != NULL);
+ ClearInterfaceList(m);
+ }
+
+extern mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m)
+ {
+ int err;
+ ClearInterfaceList(m);
+ err = SetupInterfaceList(m);
+ return PosixErrorToStatus(err);
+ }
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Locking
+#endif
+
+// On the Posix platform, locking is a no-op because we only ever enter
+// mDNS core on the main thread.
+
+// mDNS core calls this routine when it wants to prevent
+// the platform from reentering mDNS core code.
+mDNSexport void mDNSPlatformLock (const mDNS *const m)
+ {
+ (void) m; // Unused
+ }
+
+// mDNS core calls this routine when it release the lock taken by
+// mDNSPlatformLock and allow the platform to reenter mDNS core code.
+mDNSexport void mDNSPlatformUnlock (const mDNS *const m)
+ {
+ (void) m; // Unused
+ }
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Strings
+#endif
+
+// mDNS core calls this routine to copy C strings.
+// On the Posix platform this maps directly to the ANSI C strcpy.
+mDNSexport void mDNSPlatformStrCopy(const void *src, void *dst)
+ {
+ strcpy((char *)dst, (char *)src);
+ }
+
+// mDNS core calls this routine to get the length of a C string.
+// On the Posix platform this maps directly to the ANSI C strlen.
+mDNSexport mDNSu32 mDNSPlatformStrLen (const void *src)
+ {
+ return strlen((char*)src);
+ }
+
+// mDNS core calls this routine to copy memory.
+// On the Posix platform this maps directly to the ANSI C memcpy.
+mDNSexport void mDNSPlatformMemCopy(const void *src, void *dst, mDNSu32 len)
+ {
+ memcpy(dst, src, len);
+ }
+
+// mDNS core calls this routine to test whether blocks of memory are byte-for-byte
+// identical. On the Posix platform this is a simple wrapper around ANSI C memcmp.
+mDNSexport mDNSBool mDNSPlatformMemSame(const void *src, const void *dst, mDNSu32 len)
+ {
+ return memcmp(dst, src, len) == 0;
+ }
+
+// mDNS core calls this routine to clear blocks of memory.
+// On the Posix platform this is a simple wrapper around ANSI C memset.
+mDNSexport void mDNSPlatformMemZero( void *dst, mDNSu32 len)
+ {
+ memset(dst, 0, len);
+ }
+
+mDNSexport void * mDNSPlatformMemAllocate(mDNSu32 len) { return(malloc(len)); }
+mDNSexport void mDNSPlatformMemFree (void *mem) { free(mem); }
+
+mDNSexport mDNSs32 mDNSPlatformOneSecond = 1024;
+
+mDNSexport mStatus mDNSPlatformTimeInit(mDNSs32 *timenow)
+ {
+ // No special setup is required on Posix -- we just use gettimeofday();
+ // This is not really safe, because gettimeofday can go backwards if the user manually changes the date or time
+ // We should find a better way to do this
+ *timenow = mDNSPlatformTimeNow();
+ return(mStatus_NoError);
+ }
+
+mDNSexport mDNSs32 mDNSPlatformTimeNow()
+ {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ // tv.tv_sec is seconds since 1st January 1970 (GMT, with no adjustment for daylight savings time)
+ // tv.tv_usec is microseconds since the start of this second (i.e. values 0 to 999999)
+ // We use the lower 22 bits of tv.tv_sec for the top 22 bits of our result
+ // and we multiply tv.tv_usec by 16 / 15625 to get a value in the range 0-1023 to go in the bottom 10 bits.
+ // This gives us a proper modular (cyclic) counter that has a resolution of roughly 1ms (actually 1/1024 second)
+ // and correctly cycles every 2^22 seconds (4194304 seconds = approx 48 days).
+ return( (tv.tv_sec << 10) | (tv.tv_usec * 16 / 15625) );
+ }
+
+mDNSexport void mDNSPosixGetFDSet(mDNS *const m, int *nfds, fd_set *readfds, struct timeval *timeout)
+ {
+ mDNSs32 ticks;
+ struct timeval interval;
+
+ // 1. Call mDNS_Execute() to let mDNSCore do what it needs to do
+ mDNSs32 nextevent = mDNS_Execute(m);
+
+ // 2. Build our list of active file descriptors
+ PosixNetworkInterface *info = (PosixNetworkInterface *)(m->HostInterfaces);
+ while (info)
+ {
+ if (info->multicastSocket != -1)
+ {
+ if (*nfds < info->multicastSocket + 1)
+ *nfds = info->multicastSocket + 1;
+ FD_SET(info->multicastSocket, readfds);
+ }
+ if (info->multicastSocketv6 != -1)
+ {
+ if (*nfds < info->multicastSocketv6 + 1)
+ *nfds = info->multicastSocketv6 + 1;
+ FD_SET(info->multicastSocketv6, readfds);
+ }
+ info = (PosixNetworkInterface *)(info->coreIntf.next);
+ }
+
+ // 3. Calculate the time remaining to the next scheduled event (in struct timeval format)
+ ticks = nextevent - mDNSPlatformTimeNow();
+ if (ticks < 1) ticks = 1;
+ interval.tv_sec = ticks >> 10; // The high 22 bits are seconds
+ interval.tv_usec = ((ticks & 0x3FF) * 15625) / 16; // The low 10 bits are 1024ths
+
+ // 4. If client's proposed timeout is more than what we want, then reduce it
+ if (timeout->tv_sec > interval.tv_sec ||
+ (timeout->tv_sec == interval.tv_sec && timeout->tv_usec > interval.tv_usec))
+ *timeout = interval;
+ }
+
+mDNSexport void mDNSPosixProcessFDSet(mDNS *const m, fd_set *readfds)
+ {
+ PosixNetworkInterface *info;
+ assert(m != NULL);
+ assert(readfds != NULL);
+ info = (PosixNetworkInterface *)(m->HostInterfaces);
+ while (info)
+ {
+ if (info->multicastSocket != -1 && FD_ISSET(info->multicastSocket, readfds))
+ {
+ FD_CLR(info->multicastSocket, readfds);
+ SocketDataReady(m, info, info->multicastSocket);
+ }
+ if (info->multicastSocketv6 != -1 && FD_ISSET(info->multicastSocketv6, readfds))
+ {
+ FD_CLR(info->multicastSocketv6, readfds);
+ SocketDataReady(m, info, info->multicastSocketv6);
+ }
+ info = (PosixNetworkInterface *)(info->coreIntf.next);
+ }
+ }
--- /dev/null
+/*
+ * 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: mDNSPosix.h,v $
+Revision 1.8 2003/08/12 19:56:26 cheshire
+Update to APSL 2.0
+
+Revision 1.7 2003/07/02 21:19:59 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.6 2003/03/13 03:46:21 cheshire
+Fixes to make the code build on Linux
+
+Revision 1.5 2003/03/08 00:35:56 cheshire
+Switched to using new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt")
+
+Revision 1.4 2002/12/23 22:13:31 jgraessl
+
+Reviewed by: Stuart Cheshire
+Initial IPv6 support for mDNSResponder.
+
+Revision 1.3 2002/09/21 20:44:53 zarzycki
+Added APSL info
+
+Revision 1.2 2002/09/19 04:20:44 cheshire
+Remove high-ascii characters that confuse some systems
+
+Revision 1.1 2002/09/17 06:24:34 cheshire
+First checkin
+
+*/
+
+#ifndef __mDNSPlatformPosix_h
+#define __mDNSPlatformPosix_h
+
+#include <sys/time.h>
+
+#if HAVE_IPV6
+#define mDNSIPv6Support 1
+#endif
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+// This is a global because debugf_() needs to be able to check its value
+extern int gMDNSPlatformPosixVerboseLevel;
+
+struct mDNS_PlatformSupport_struct
+ {
+ // No additional data required for Posix at this time
+ };
+
+extern mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m);
+ // See comment in implementation.
+
+// Call mDNSPosixGetFDSet before calling select(), to update the parameters
+// as may be necessary to meet the needs of the mDNSCore code.
+// The timeout pointer MUST NOT be NULL.
+// Set timeout->tv_sec to 0x3FFFFFFF if you want to have effectively no timeout
+// After calling mDNSPosixGetFDSet(), call select(nfds, &readfds, NULL, NULL, &timeout); as usual
+// After select() returns, call mDNSPosixProcessFDSet() to let mDNSCore do its work
+extern void mDNSPosixGetFDSet(mDNS *const m, int *nfds, fd_set *readfds, struct timeval *timeout);
+extern void mDNSPosixProcessFDSet(mDNS *const m, fd_set *readfds);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
--- /dev/null
+/*
+ * 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: mDNSUNP.c,v $
+Revision 1.12 2003/09/02 20:47:13 cheshire
+Fix signed/unsigned warning
+
+Revision 1.11 2003/08/12 19:56:26 cheshire
+Update to APSL 2.0
+
+Revision 1.10 2003/08/06 18:20:51 cheshire
+Makefile cleanup
+
+Revision 1.9 2003/07/14 18:11:54 cheshire
+Fix stricter compiler warnings
+
+Revision 1.8 2003/07/02 21:19:59 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.7 2003/03/20 21:10:31 cheshire
+Fixes done at IETF 56 to make mDNSProxyResponderPosix run on Solaris
+
+Revision 1.6 2003/03/13 03:46:21 cheshire
+Fixes to make the code build on Linux
+
+Revision 1.5 2003/02/07 03:02:02 cheshire
+Submitted by: Mitsutaka Watanabe
+The code saying "index += 1;" was effectively making up random interface index values.
+The right way to find the correct interface index is if_nametoindex();
+
+Revision 1.4 2002/12/23 22:13:31 jgraessl
+
+Reviewed by: Stuart Cheshire
+Initial IPv6 support for mDNSResponder.
+
+Revision 1.3 2002/09/21 20:44:53 zarzycki
+Added APSL info
+
+Revision 1.2 2002/09/19 04:20:44 cheshire
+Remove high-ascii characters that confuse some systems
+
+Revision 1.1 2002/09/17 06:24:34 cheshire
+First checkin
+
+*/
+
+#include "mDNSUNP.h"
+
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/uio.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <stdio.h>
+
+/* Solaris defined SIOCGIFCONF etc in <sys/sockio.h> but
+ other platforms don't even have that include file. So,
+ if we haven't yet got a definition, let's try to find
+ <sys/sockio.h>.
+*/
+
+#ifndef SIOCGIFCONF
+ #include <sys/sockio.h>
+#endif
+
+/* sockaddr_dl is only referenced if we're using IP_RECVIF,
+ so only include the header in that case.
+*/
+
+#ifdef IP_RECVIF
+ #include <net/if_dl.h>
+#endif
+
+
+struct ifi_info *get_ifi_info(int family, int doaliases)
+{
+ int junk;
+ struct ifi_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;
+
+#if defined(AF_INET6) && defined(HAVE_IPV6)
+ struct sockaddr_in6 *sinptr6;
+#endif
+
+ sockfd = -1;
+ buf = NULL;
+ ifihead = NULL;
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0) {
+ goto gotError;
+ }
+
+ lastlen = 0;
+ len = 100 * sizeof(struct ifreq); /* initial buffer size guess */
+ for ( ; ; ) {
+ buf = malloc(len);
+ if (buf == NULL) {
+ goto gotError;
+ }
+ ifc.ifc_len = len;
+ ifc.ifc_buf = buf;
+ if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
+ if (errno != EINVAL || lastlen != 0) {
+ goto gotError;
+ }
+ } 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_ifi_info1 */
+
+/* include get_ifi_info2 */
+ for (ptr = buf; ptr < buf + ifc.ifc_len; ) {
+ ifr = (struct ifreq *) ptr;
+
+ len = GET_SA_LEN(ifr->ifr_addr);
+ ptr += sizeof(ifr->ifr_name) + len; /* for next one in buffer */
+
+// fprintf(stderr, "intf %d name=%s AF=%d\n", index, ifr->ifr_name, ifr->ifr_addr.sa_family);
+
+ 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;
+ if (ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy) < 0) {
+ goto gotError;
+ }
+
+ flags = ifrcopy.ifr_flags;
+ if ((flags & IFF_UP) == 0)
+ continue; /* ignore if interface not up */
+
+ ifi = calloc(1, sizeof(struct ifi_info));
+ if (ifi == NULL) {
+ goto gotError;
+ }
+ *ifipnext = ifi; /* prev points to this new one */
+ ifipnext = &ifi->ifi_next; /* pointer to next one goes here */
+
+ ifi->ifi_flags = flags; /* IFF_xxx values */
+ ifi->ifi_myflags = myflags; /* IFI_xxx values */
+ ifi->ifi_index = if_nametoindex(ifr->ifr_name);
+ memcpy(ifi->ifi_name, ifr->ifr_name, IFI_NAME);
+ ifi->ifi_name[IFI_NAME-1] = '\0';
+/* end get_ifi_info2 */
+/* include get_ifi_info3 */
+ switch (ifr->ifr_addr.sa_family) {
+ case AF_INET:
+ sinptr = (struct sockaddr_in *) &ifr->ifr_addr;
+ if (ifi->ifi_addr == NULL) {
+ ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in));
+ if (ifi->ifi_addr == NULL) {
+ goto gotError;
+ }
+ memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in));
+
+#ifdef SIOCGIFBRDADDR
+ if (flags & IFF_BROADCAST) {
+ if (ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy) < 0) {
+ goto gotError;
+ }
+ sinptr = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr;
+ ifi->ifi_brdaddr = calloc(1, sizeof(struct sockaddr_in));
+ if (ifi->ifi_brdaddr == NULL) {
+ goto gotError;
+ }
+ memcpy(ifi->ifi_brdaddr, sinptr, sizeof(struct sockaddr_in));
+ }
+#endif
+
+#ifdef SIOCGIFDSTADDR
+ if (flags & IFF_POINTOPOINT) {
+ if (ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy) < 0) {
+ goto gotError;
+ }
+ sinptr = (struct sockaddr_in *) &ifrcopy.ifr_dstaddr;
+ ifi->ifi_dstaddr = calloc(1, sizeof(struct sockaddr_in));
+ if (ifi->ifi_dstaddr == NULL) {
+ goto gotError;
+ }
+ memcpy(ifi->ifi_dstaddr, sinptr, sizeof(struct sockaddr_in));
+ }
+#endif
+ }
+ break;
+
+#if defined(AF_INET6) && defined(HAVE_IPV6)
+ case AF_INET6:
+ sinptr6 = (struct sockaddr_in6 *) &ifr->ifr_addr;
+ if (ifi->ifi_addr == NULL) {
+ ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in6));
+ if (ifi->ifi_addr == NULL) {
+ goto gotError;
+ }
+
+ /* Some platforms (*BSD) inject the prefix in IPv6LL addresses */
+ /* We need to strip that out */
+ if (IN6_IS_ADDR_LINKLOCAL(&sinptr6->sin6_addr))
+ sinptr6->sin6_addr.__u6_addr.__u6_addr16[1] = 0;
+ memcpy(ifi->ifi_addr, sinptr6, sizeof(struct sockaddr_in6));
+ }
+ break;
+#endif
+
+ default:
+ break;
+ }
+ }
+ goto done;
+
+gotError:
+ if (ifihead != NULL) {
+ free_ifi_info(ifihead);
+ ifihead = NULL;
+ }
+
+done:
+ if (buf != NULL) {
+ free(buf);
+ }
+ if (sockfd != -1) {
+ junk = close(sockfd);
+ assert(junk == 0);
+ }
+ return(ifihead); /* pointer to first structure in linked list */
+}
+/* end get_ifi_info3 */
+
+/* include free_ifi_info */
+void
+free_ifi_info(struct ifi_info *ifihead)
+{
+ struct ifi_info *ifi, *ifinext;
+
+ for (ifi = ifihead; ifi != NULL; ifi = ifinext) {
+ if (ifi->ifi_addr != NULL)
+ free(ifi->ifi_addr);
+ if (ifi->ifi_brdaddr != NULL)
+ free(ifi->ifi_brdaddr);
+ if (ifi->ifi_dstaddr != NULL)
+ free(ifi->ifi_dstaddr);
+ ifinext = ifi->ifi_next; /* can't fetch ifi_next after free() */
+ free(ifi); /* the ifi_info{} itself */
+ }
+}
+/* end free_ifi_info */
+
+ssize_t
+recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp,
+ struct sockaddr *sa, socklen_t *salenptr, struct my_in_pktinfo *pktp)
+{
+ struct msghdr msg;
+ struct iovec iov[1];
+ ssize_t n;
+
+#ifdef CMSG_FIRSTHDR
+ struct cmsghdr *cmptr;
+ union {
+ struct cmsghdr cm;
+ char control[1024];
+ } control_un;
+
+ msg.msg_control = control_un.control;
+ msg.msg_controllen = sizeof(control_un.control);
+ msg.msg_flags = 0;
+#else
+ memset(&msg, 0, sizeof(msg)); /* make certain msg_accrightslen = 0 */
+#endif /* CMSG_FIRSTHDR */
+
+ msg.msg_name = (void *) sa;
+ msg.msg_namelen = *salenptr;
+ iov[0].iov_base = ptr;
+ iov[0].iov_len = nbytes;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+ if ( (n = recvmsg(fd, &msg, *flagsp)) < 0)
+ return(n);
+
+ *salenptr = msg.msg_namelen; /* pass back results */
+ if (pktp) {
+ /* 0.0.0.0, i/f = -1 */
+ /* We set the interface to -1 so that the caller can
+ tell whether we returned a meaningful value or
+ just some default. Previously this code just
+ set the value to 0, but I'm concerned that 0
+ might be a valid interface value.
+ */
+ memset(pktp, 0, sizeof(struct my_in_pktinfo));
+ pktp->ipi_ifindex = -1;
+ }
+/* end recvfrom_flags1 */
+
+/* include recvfrom_flags2 */
+#ifndef CMSG_FIRSTHDR
+ #warning CMSG_FIRSTHDR not defined. Will not be able to determine destination address, received interface, etc.
+ *flagsp = 0; /* pass back results */
+ return(n);
+#else
+
+ *flagsp = msg.msg_flags; /* pass back results */
+ if (msg.msg_controllen < (socklen_t)sizeof(struct cmsghdr) ||
+ (msg.msg_flags & MSG_CTRUNC) || pktp == NULL)
+ return(n);
+
+ for (cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL;
+ cmptr = CMSG_NXTHDR(&msg, cmptr)) {
+
+#ifdef IP_PKTINFO
+#if in_pktinfo_definition_is_missing
+struct in_pktinfo
+{
+ int ipi_ifindex;
+ struct in_addr ipi_spec_dst;
+ struct in_addr ipi_addr;
+};
+#endif
+ if (cmptr->cmsg_level == IPPROTO_IP &&
+ cmptr->cmsg_type == IP_PKTINFO) {
+ struct in_pktinfo *tmp;
+ struct sockaddr_in *sin = (struct sockaddr_in*)&pktp->ipi_addr;
+
+ tmp = (struct in_pktinfo *) CMSG_DATA(cmptr);
+ sin->sin_family = AF_INET;
+ sin->sin_addr = tmp->ipi_addr;
+ sin->sin_port = 0;
+ pktp->ipi_ifindex = tmp->ipi_ifindex;
+ continue;
+ }
+#endif
+
+#ifdef IP_RECVDSTADDR
+ if (cmptr->cmsg_level == IPPROTO_IP &&
+ cmptr->cmsg_type == IP_RECVDSTADDR) {
+ struct sockaddr_in *sin = (struct sockaddr_in*)&pktp->ipi_addr;
+
+ sin->sin_family = AF_INET;
+ sin->sin_addr = *(struct in_addr*)CMSG_DATA(cmptr);
+ sin->sin_port = 0;
+ continue;
+ }
+#endif
+
+#ifdef IP_RECVIF
+ if (cmptr->cmsg_level == IPPROTO_IP &&
+ cmptr->cmsg_type == IP_RECVIF) {
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *) CMSG_DATA(cmptr);
+ int nameLen = (sdl->sdl_nlen < IFI_NAME - 1) ? sdl->sdl_nlen : (IFI_NAME - 1);
+ pktp->ipi_ifindex = sdl->sdl_index;
+#ifndef HAVE_BROKEN_RECVIF_NAME
+ strncpy(pktp->ipi_ifname, sdl->sdl_data, nameLen);
+#endif
+ assert(pktp->ipi_ifname[IFI_NAME - 1] == 0);
+ // null terminated because of memset above
+ continue;
+ }
+#endif
+
+#if defined(IPV6_PKTINFO) && defined(HAVE_IPV6)
+ if (cmptr->cmsg_level == IPPROTO_IPV6 &&
+ cmptr->cmsg_type == IPV6_PKTINFO) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&pktp->ipi_addr;
+ struct in6_pktinfo *ip6_info = (struct in6_pktinfo*)CMSG_DATA(cmptr);
+
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_addr = ip6_info->ipi6_addr;
+ sin6->sin6_flowinfo = 0;
+ sin6->sin6_scope_id = 0;
+ sin6->sin6_port = 0;
+ pktp->ipi_ifindex = ip6_info->ipi6_ifindex;
+ continue;
+ }
+#endif
+ assert(0); // unknown ancillary data
+ }
+ return(n);
+#endif /* CMSG_FIRSTHDR */
+}
--- /dev/null
+/*
+ * 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: mDNSUNP.h,v $
+Revision 1.8 2003/08/12 19:56:26 cheshire
+Update to APSL 2.0
+
+Revision 1.7 2003/08/06 18:20:51 cheshire
+Makefile cleanup
+
+Revision 1.6 2003/07/02 21:19:59 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.5 2003/03/13 03:46:21 cheshire
+Fixes to make the code build on Linux
+
+Revision 1.4 2002/12/23 22:13:32 jgraessl
+
+Reviewed by: Stuart Cheshire
+Initial IPv6 support for mDNSResponder.
+
+Revision 1.3 2002/09/21 20:44:53 zarzycki
+Added APSL info
+
+Revision 1.2 2002/09/19 04:20:44 cheshire
+Remove high-ascii characters that confuse some systems
+
+Revision 1.1 2002/09/17 06:24:35 cheshire
+First checkin
+
+*/
+
+#ifndef __mDNSUNP_h
+#define __mDNSUNP_h
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#ifdef NOT_HAVE_SOCKLEN_T
+ typedef unsigned int socklen_t;
+#endif
+
+#if !defined(_SS_MAXSIZE)
+ #define sockaddr_storage sockaddr
+#endif
+
+#ifndef NOT_HAVE_SA_LEN
+#define GET_SA_LEN(X) (sizeof(struct sockaddr) > ((struct sockaddr*)&(X))->sa_len ? \
+ sizeof(struct sockaddr) : ((struct sockaddr*)&(X))->sa_len )
+#elif mDNSIPv6Support
+#define GET_SA_LEN(X) (((struct sockaddr*)&(X))->sa_family == AF_INET ? sizeof(struct sockaddr_in) : \
+ ((struct sockaddr*)&(X))->sa_family == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr))
+#else
+#define GET_SA_LEN(X) ((X).sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr))
+#endif
+
+#define IFI_NAME 16 /* same as IFNAMSIZ in <net/if.h> */
+#define IFI_HADDR 8 /* allow for 64-bit EUI-64 in future */
+
+// Renamed from my_in_pktinfo because in_pktinfo is used by Linux.
+
+struct my_in_pktinfo {
+ struct sockaddr_storage ipi_addr;
+ int ipi_ifindex; /* received interface index */
+ char ipi_ifname[IFI_NAME]; /* received interface name */
+};
+
+extern ssize_t recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp,
+ struct sockaddr *sa, socklen_t *salenptr, struct my_in_pktinfo *pktp);
+
+struct ifi_info {
+ char ifi_name[IFI_NAME]; /* interface name, null terminated */
+ u_char ifi_haddr[IFI_HADDR]; /* hardware address */
+ u_short ifi_hlen; /* #bytes in hardware address: 0, 6, 8 */
+ short ifi_flags; /* IFF_xxx constants from <net/if.h> */
+ short ifi_myflags; /* our own IFI_xxx flags */
+ int ifi_index; /* interface index */
+ struct sockaddr *ifi_addr; /* primary address */
+ struct sockaddr *ifi_brdaddr;/* broadcast address */
+ struct sockaddr *ifi_dstaddr;/* destination address */
+ struct ifi_info *ifi_next; /* next of these structures */
+};
+
+#define IFI_ALIAS 1 /* ifi_addr is an alias */
+
+extern struct ifi_info *get_ifi_info(int family, int doaliases);
+extern void free_ifi_info(struct ifi_info *);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
+++ /dev/null
-// !$*UTF8*$!
-{
- archiveVersion = 1;
- classes = {
- };
- objectVersion = 38;
- objects = {
- 00CA213D02786FC30CCA2C71 = {
- isa = PBXFrameworkReference;
- name = IOKit.framework;
- path = /System/Library/Frameworks/IOKit.framework;
- refType = 0;
- };
-//000
-//001
-//002
-//003
-//004
-//010
-//011
-//012
-//013
-//014
- 014CEA490018CE3211CA2923 = {
- buildRules = (
- );
- buildSettings = {
- COPY_PHASE_STRIP = NO;
- OPTIMIZATION_CFLAGS = "-O0";
- OTHER_CFLAGS = "-D__MACOSX__ -DDEBUGBREAKS=1";
- };
- isa = PBXBuildStyle;
- name = Development;
- };
- 014CEA4A0018CE3211CA2923 = {
- buildRules = (
- );
- buildSettings = {
- COPY_PHASE_STRIP = YES;
- OTHER_CFLAGS = "-D__MACOSX__";
- };
- isa = PBXBuildStyle;
- name = Deployment;
- };
-//010
-//011
-//012
-//013
-//014
-//030
-//031
-//032
-//033
-//034
- 034768E2FF38A6DC11DB9C8B = {
- isa = PBXExecutableFileReference;
- path = mDNSResponder;
- refType = 3;
- };
-//030
-//031
-//032
-//033
-//034
-//080
-//081
-//082
-//083
-//084
- 08FB7793FE84155DC02AAC07 = {
- buildStyles = (
- 014CEA490018CE3211CA2923,
- 014CEA4A0018CE3211CA2923,
- );
- isa = PBXProject;
- mainGroup = 08FB7794FE84155DC02AAC07;
- projectDirPath = "";
- targets = (
- 08FB779FFE84155DC02AAC07,
- 6575FC1C022EB76000000109,
- );
- };
- 08FB7794FE84155DC02AAC07 = {
- children = (
- 08FB7795FE84155DC02AAC07,
- 6575FC1F022EB78C00000109,
- 6575FBFE022EAFA800000109,
- 08FB779DFE84155DC02AAC07,
- 19C28FBDFE9D53C911CA2CBB,
- );
- isa = PBXGroup;
- name = mDNSResponder;
- refType = 4;
- };
- 08FB7795FE84155DC02AAC07 = {
- children = (
- 6575FBEC022EAF7200000109,
- 6575FBE9022EAF5A00000109,
- 6575FC12022EB27800000109,
- 6575FBEB022EAF7200000109,
- 654BE64F02B63B93000001D1,
- 654BE65002B63B93000001D1,
- 654BE65102B63B93000001D1,
- 654BE65202B63B93000001D1,
- 654BE65302B63B93000001D1,
- 654BE65402B63B93000001D1,
- );
- isa = PBXGroup;
- name = "mDNS Server Sources";
- refType = 4;
- };
- 08FB779DFE84155DC02AAC07 = {
- children = (
- 09AB6884FE841BABC02AAC07,
- 65713D46025A293200000109,
- 00CA213D02786FC30CCA2C71,
- );
- isa = PBXGroup;
- name = "External Frameworks and Libraries";
- refType = 4;
- };
- 08FB779FFE84155DC02AAC07 = {
- buildPhases = (
- 08FB77A0FE84155DC02AAC07,
- 08FB77A1FE84155DC02AAC07,
- 08FB77A3FE84155DC02AAC07,
- 08FB77A5FE84155DC02AAC07,
- );
- buildSettings = {
- FRAMEWORK_SEARCH_PATHS = "";
- HEADER_SEARCH_PATHS = "\"$(APPLE_INTERNAL_DEVELOPER_DIR)/Headers\"";
- INSTALL_PATH = /usr/sbin;
- LIBRARY_SEARCH_PATHS = "";
- OTHER_CFLAGS = "-D__MACOSX__";
- OTHER_LDFLAGS = "";
- OTHER_REZFLAGS = "";
- PRODUCT_NAME = mDNSResponder;
- REZ_EXECUTABLE = YES;
- SECTORDER_FLAGS = "";
- WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas";
- };
- dependencies = (
- );
- isa = PBXToolTarget;
- name = mDNSResponder;
- productInstallPath = "$(HOME)/bin";
- productName = mDNSResponder;
- productReference = 034768E2FF38A6DC11DB9C8B;
- shouldUseHeadermap = 1;
- };
- 08FB77A0FE84155DC02AAC07 = {
- buildActionMask = 2147483647;
- files = (
- 6575FC02022EAFBA00000109,
- 654BE65502B63B93000001D1,
- 654BE65602B63B93000001D1,
- 654BE65702B63B93000001D1,
- 654BE65802B63B93000001D1,
- 654BE65902B63B93000001D1,
- 654BE65A02B63B93000001D1,
- );
- isa = PBXHeadersBuildPhase;
- runOnlyForDeploymentPostprocessing = 0;
- };
- 08FB77A1FE84155DC02AAC07 = {
- buildActionMask = 2147483647;
- files = (
- 6575FC0D022EB18700000109,
- 6575FC0E022EB18700000109,
- 6575FBEA022EAF5A00000109,
- 6575FBED022EAF7200000109,
- 6575FBEE022EAF7200000109,
- 6575FC15022EB27800000109,
- );
- isa = PBXSourcesBuildPhase;
- runOnlyForDeploymentPostprocessing = 0;
- };
- 08FB77A3FE84155DC02AAC07 = {
- buildActionMask = 2147483647;
- files = (
- 09AB6885FE841BABC02AAC07,
- 65713D66025A293200000109,
- 6585DD640279A3B7000001D1,
- );
- isa = PBXFrameworksBuildPhase;
- runOnlyForDeploymentPostprocessing = 0;
- };
- 08FB77A5FE84155DC02AAC07 = {
- buildActionMask = 2147483647;
- files = (
- );
- isa = PBXRezBuildPhase;
- runOnlyForDeploymentPostprocessing = 0;
- };
-//080
-//081
-//082
-//083
-//084
-//090
-//091
-//092
-//093
-//094
- 09AB6884FE841BABC02AAC07 = {
- isa = PBXFrameworkReference;
- name = CoreFoundation.framework;
- path = /System/Library/Frameworks/CoreFoundation.framework;
- refType = 0;
- };
- 09AB6885FE841BABC02AAC07 = {
- fileRef = 09AB6884FE841BABC02AAC07;
- isa = PBXBuildFile;
- settings = {
- };
- };
-//090
-//091
-//092
-//093
-//094
-//190
-//191
-//192
-//193
-//194
- 19C28FBDFE9D53C911CA2CBB = {
- children = (
- 034768E2FF38A6DC11DB9C8B,
- 6575FC1D022EB76000000109,
- );
- isa = PBXGroup;
- name = Products;
- refType = 4;
- };
-//190
-//191
-//192
-//193
-//194
-//650
-//651
-//652
-//653
-//654
- 654BE64F02B63B93000001D1 = {
- isa = PBXFileReference;
- name = mDNSClientAPI.h;
- path = mDNSCore/mDNSClientAPI.h;
- refType = 4;
- };
- 654BE65002B63B93000001D1 = {
- isa = PBXFileReference;
- name = mDNSDebug.h;
- path = mDNSCore/mDNSDebug.h;
- refType = 4;
- };
- 654BE65102B63B93000001D1 = {
- isa = PBXFileReference;
- name = mDNSPlatformEnvironment.h;
- path = mDNSCore/mDNSPlatformEnvironment.h;
- refType = 4;
- };
- 654BE65202B63B93000001D1 = {
- isa = PBXFileReference;
- name = mDNSPlatformFunctions.h;
- path = mDNSCore/mDNSPlatformFunctions.h;
- refType = 4;
- };
- 654BE65302B63B93000001D1 = {
- isa = PBXFileReference;
- name = mDNSsprintf.h;
- path = mDNSCore/mDNSsprintf.h;
- refType = 4;
- };
- 654BE65402B63B93000001D1 = {
- isa = PBXFileReference;
- name = mDNSvsprintf.h;
- path = mDNSCore/mDNSvsprintf.h;
- refType = 4;
- };
- 654BE65502B63B93000001D1 = {
- fileRef = 654BE64F02B63B93000001D1;
- isa = PBXBuildFile;
- settings = {
- };
- };
- 654BE65602B63B93000001D1 = {
- fileRef = 654BE65002B63B93000001D1;
- isa = PBXBuildFile;
- settings = {
- };
- };
- 654BE65702B63B93000001D1 = {
- fileRef = 654BE65102B63B93000001D1;
- isa = PBXBuildFile;
- settings = {
- };
- };
- 654BE65802B63B93000001D1 = {
- fileRef = 654BE65202B63B93000001D1;
- isa = PBXBuildFile;
- settings = {
- };
- };
- 654BE65902B63B93000001D1 = {
- fileRef = 654BE65302B63B93000001D1;
- isa = PBXBuildFile;
- settings = {
- };
- };
- 654BE65A02B63B93000001D1 = {
- fileRef = 654BE65402B63B93000001D1;
- isa = PBXBuildFile;
- settings = {
- };
- };
- 65713D46025A293200000109 = {
- isa = PBXFrameworkReference;
- name = SystemConfiguration.framework;
- path = /System/Library/Frameworks/SystemConfiguration.framework;
- refType = 0;
- };
- 65713D66025A293200000109 = {
- fileRef = 65713D46025A293200000109;
- isa = PBXBuildFile;
- settings = {
- };
- };
- 6575FBE9022EAF5A00000109 = {
- indentWidth = 4;
- isa = PBXFileReference;
- name = mDNS.c;
- path = mDNSCore/mDNS.c;
- refType = 4;
- tabWidth = 4;
- usesTabs = 1;
- };
- 6575FBEA022EAF5A00000109 = {
- fileRef = 6575FBE9022EAF5A00000109;
- isa = PBXBuildFile;
- settings = {
- };
- };
- 6575FBEB022EAF7200000109 = {
- indentWidth = 4;
- isa = PBXFileReference;
- path = CFSocket.c;
- refType = 4;
- tabWidth = 4;
- usesTabs = 1;
- };
- 6575FBEC022EAF7200000109 = {
- indentWidth = 4;
- isa = PBXFileReference;
- path = daemon.c;
- refType = 4;
- tabWidth = 4;
- usesTabs = 1;
- };
- 6575FBED022EAF7200000109 = {
- fileRef = 6575FBEB022EAF7200000109;
- isa = PBXBuildFile;
- settings = {
- };
- };
- 6575FBEE022EAF7200000109 = {
- fileRef = 6575FBEC022EAF7200000109;
- isa = PBXBuildFile;
- settings = {
- };
- };
- 6575FBFE022EAFA800000109 = {
- children = (
- 6575FBFF022EAFBA00000109,
- 6575FC00022EAFBA00000109,
- 6575FC01022EAFBA00000109,
- );
- isa = PBXGroup;
- name = "DNS Service Discovery MIG files";
- refType = 4;
- };
- 6575FBFF022EAFBA00000109 = {
- isa = PBXFileReference;
- path = DNSServiceDiscoveryDefines.h;
- refType = 4;
- };
- 6575FC00022EAFBA00000109 = {
- isa = PBXFileReference;
- path = DNSServiceDiscoveryReply.defs;
- refType = 4;
- };
- 6575FC01022EAFBA00000109 = {
- isa = PBXFileReference;
- path = DNSServiceDiscoveryRequest.defs;
- refType = 4;
- };
- 6575FC02022EAFBA00000109 = {
- fileRef = 6575FBFF022EAFBA00000109;
- isa = PBXBuildFile;
- settings = {
- };
- };
- 6575FC0D022EB18700000109 = {
- fileRef = 6575FC00022EAFBA00000109;
- isa = PBXBuildFile;
- settings = {
- ATTRIBUTES = (
- Client,
- );
- };
- };
- 6575FC0E022EB18700000109 = {
- fileRef = 6575FC01022EAFBA00000109;
- isa = PBXBuildFile;
- settings = {
- ATTRIBUTES = (
- Server,
- Client,
- );
- };
- };
- 6575FC12022EB27800000109 = {
- isa = PBXFileReference;
- name = mDNSsprintf.c;
- path = mDNSCore/mDNSsprintf.c;
- refType = 4;
- };
- 6575FC15022EB27800000109 = {
- fileRef = 6575FC12022EB27800000109;
- isa = PBXBuildFile;
- settings = {
- };
- };
- 6575FC18022EB76000000109 = {
- buildActionMask = 2147483647;
- files = (
- );
- isa = PBXHeadersBuildPhase;
- runOnlyForDeploymentPostprocessing = 0;
- };
- 6575FC19022EB76000000109 = {
- buildActionMask = 2147483647;
- files = (
- 6575FC21022EB7AA00000109,
- );
- isa = PBXSourcesBuildPhase;
- runOnlyForDeploymentPostprocessing = 0;
- };
- 6575FC1A022EB76000000109 = {
- buildActionMask = 2147483647;
- files = (
- 6575FC24022EBA5D00000109,
- );
- isa = PBXFrameworksBuildPhase;
- runOnlyForDeploymentPostprocessing = 0;
- };
- 6575FC1B022EB76000000109 = {
- buildActionMask = 2147483647;
- files = (
- );
- isa = PBXRezBuildPhase;
- runOnlyForDeploymentPostprocessing = 0;
- };
- 6575FC1C022EB76000000109 = {
- buildPhases = (
- 6575FC18022EB76000000109,
- 6575FC19022EB76000000109,
- 6575FC1A022EB76000000109,
- 6575FC1B022EB76000000109,
- );
- buildSettings = {
- OTHER_CFLAGS = "";
- OTHER_LDFLAGS = "";
- OTHER_REZFLAGS = "";
- PRODUCT_NAME = mDNS;
- REZ_EXECUTABLE = YES;
- SECTORDER_FLAGS = "";
- WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas";
- };
- dependencies = (
- );
- isa = PBXToolTarget;
- name = mDNS;
- productInstallPath = /usr/local/bin;
- productName = "Sample mDNS Client";
- productReference = 6575FC1D022EB76000000109;
- shouldUseHeadermap = 0;
- };
- 6575FC1D022EB76000000109 = {
- isa = PBXExecutableFileReference;
- path = mDNS;
- refType = 3;
- };
- 6575FC1F022EB78C00000109 = {
- children = (
- 6575FC20022EB7AA00000109,
- );
- isa = PBXGroup;
- name = SampleMulticastDNSClient;
- refType = 4;
- };
- 6575FC20022EB7AA00000109 = {
- indentWidth = 4;
- isa = PBXFileReference;
- path = SamplemDNSClient.c;
- refType = 4;
- tabWidth = 4;
- usesTabs = 0;
- };
- 6575FC21022EB7AA00000109 = {
- fileRef = 6575FC20022EB7AA00000109;
- isa = PBXBuildFile;
- settings = {
- };
- };
- 6575FC24022EBA5D00000109 = {
- fileRef = 09AB6884FE841BABC02AAC07;
- isa = PBXBuildFile;
- settings = {
- };
- };
- 6585DD640279A3B7000001D1 = {
- fileRef = 00CA213D02786FC30CCA2C71;
- isa = PBXBuildFile;
- settings = {
- };
- };
- };
- rootObject = 08FB7793FE84155DC02AAC07;
-}
--- /dev/null
+These are the platform support files for running mDNSCore on VxWorks.
+
+Please note, most of the developers working on the mDNSResponder code do
+not have access to a VxWorks development environment, so they are not able
+to personally verify that the VxWorks compiles and runs successfully after
+every single change to the mDNSCore code. We do try to take care not to
+make careless changes that would break the VxWorks build, but if you do
+find that something is broken, let us know and we'll fix it.
--- /dev/null
+/*
+ * 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@
+
+ Contains: mDNS platform plugin for VxWorks.
+
+ Copyright: Copyright (C) 2002-2003 Apple Computer, Inc., All Rights Reserved.
+
+ Change History (most recent first):
+
+$Log: mDNSVxWorks.c,v $
+Revision 1.7 2003/08/20 05:58:54 bradley
+Removed dependence on modified mDNSCore: define structures/prototypes locally.
+
+Revision 1.6 2003/08/18 23:19:05 cheshire
+<rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformTimeNow()
+
+Revision 1.5 2003/08/15 00:05:04 bradley
+Updated to use name/InterfaceID from new AuthRecord resrec field. Added output of new record sizes.
+
+Revision 1.4 2003/08/14 02:19:55 cheshire
+<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
+
+Revision 1.3 2003/08/12 19:56:27 cheshire
+Update to APSL 2.0
+
+Revision 1.2 2003/08/05 23:58:34 cheshire
+Update code to compile with the new mDNSCoreReceive() function that requires a TTL
+Right now this platform layer just reports 255 instead of returning the real value -- we should fix this
+
+Revision 1.1 2003/08/02 10:06:48 bradley
+mDNS platform plugin for VxWorks.
+
+
+ Notes for non-Apple platforms:
+
+ TARGET_NON_APPLE should be defined to 1 to avoid relying on Apple-only header files, macros, or functions.
+
+ To Do:
+
+ - Add support for IPv6 (needs VxWorks IPv6 support).
+*/
+
+// Set up the debug library to use the default category (see DebugServicesLite.h for details).
+
+#if( !TARGET_NON_APPLE )
+ #define DEBUG_USE_DEFAULT_CATEGORY 1
+#endif
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <netinet/if_ether.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "vxWorks.h"
+#include "ifLib.h"
+#include "inetLib.h"
+#include "pipeDrv.h"
+#include "selectLib.h"
+#include "semLib.h"
+#include "sockLib.h"
+#include "sysLib.h"
+#include "taskLib.h"
+#include "tickLib.h"
+
+#include "config.h"
+
+#if( !TARGET_NON_APPLE )
+ #include "ACP/ACPUtilities.h"
+ #include "Support/DebugServicesLite.h"
+ #include "Support/MiscUtilities.h"
+#endif
+
+#include "mDNSClientAPI.h"
+#include "mDNSPlatformFunctions.h"
+
+#include "mDNSVxWorks.h"
+
+#if 0
+#pragma mark == Preprocessor ==
+#endif
+
+//===========================================================================================================================
+// Preprocessor
+//===========================================================================================================================
+
+#if( !TARGET_NON_APPLE )
+ debug_log_new_default_category( mdns );
+#endif
+
+#if 0
+#pragma mark == Constants ==
+#endif
+
+//===========================================================================================================================
+// Constants
+//===========================================================================================================================
+
+#define DEBUG_NAME "[mDNS] "
+
+#define kMDNSDefaultName "My-Device"
+
+#define kMDNSTaskName "tMDNS"
+#define kMDNSTaskPriority 102
+#define kMDNSTaskStackSize 49152
+
+#define kMDNSPipeName "/pipe/mDNS"
+#define kMDNSPipeMessageQueueSize 32
+#define kMDNSPipeMessageSize 1
+
+#define kInvalidSocketRef -1
+
+typedef uint8_t MDNSPipeCommandCode;
+enum
+{
+ kMDNSPipeCommandCodeInvalid = 0,
+ kMDNSPipeCommandCodeReschedule = 1,
+ kMDNSPipeCommandCodeReconfigure = 2,
+ kMDNSPipeCommandCodeQuit = 3
+};
+
+#if 0
+#pragma mark == Structures ==
+#endif
+
+//===========================================================================================================================
+// Structures
+//===========================================================================================================================
+
+typedef int MDNSSocketRef;
+
+struct MDNSInterfaceItem
+{
+ MDNSInterfaceItem * next;
+ char name[ 32 ];
+ MDNSSocketRef multicastSocketRef;
+ MDNSSocketRef unicastSocketRef;
+ MDNSSocketRef sendingSocketRef;
+ NetworkInterfaceInfo hostSet;
+ mDNSBool hostRegistered;
+
+ int sendMulticastCounter;
+ int sendUnicastCounter;
+ int sendErrorCounter;
+
+ int recvMulticastCounter;
+ int recvUnicastCounter;
+ int recvErrorCounter;
+ int recvLoopCounter;
+};
+
+#if 0
+#pragma mark == Macros ==
+#endif
+
+//===========================================================================================================================
+// Macros
+//===========================================================================================================================
+
+#if( TARGET_NON_APPLE )
+
+ // Do-nothing versions of the debugging macros for non-Apple platforms.
+
+ #define check(assertion)
+ #define check_string( assertion, cstring )
+ #define check_noerr(err)
+ #define check_noerr_string( error, cstring )
+ #define check_errno( assertion, errno_value )
+ #define debug_string( cstring )
+ #define require( assertion, label ) do { if( !(assertion) ) goto label; } while(0)
+ #define require_string( assertion, label, string ) require(assertion, label)
+ #define require_quiet( assertion, label ) require( assertion, label )
+ #define require_noerr( error, label ) do { if( (error) != 0 ) goto label; } while(0)
+ #define require_noerr_quiet( assertion, label ) require_noerr( assertion, label )
+ #define require_noerr_action( error, label, action ) do { if( (error) != 0 ) { {action;}; goto label; } } while(0)
+ #define require_noerr_action_quiet( assertion, label, action ) require_noerr_action( assertion, label, action )
+ #define require_action( assertion, label, action ) do { if( !(assertion) ) { {action;}; goto label; } } while(0)
+ #define require_action_quiet( assertion, label, action ) require_action( assertion, label, action )
+ #define require_action_string( assertion, label, action, cstring ) do { if( !(assertion) ) { {action;}; goto label; } } while(0)
+ #define require_errno( assertion, errno_value, label ) do { if( !(assertion) ) goto label; } while(0)
+ #define require_errno_action( assertion, errno_value, label, action ) do { if( !(assertion) ) { {action;}; goto label; } } while(0)
+
+ #define dlog( ARGS... )
+
+ #define DEBUG_UNUSED( X ) (void)( X )
+#endif
+
+#if 0
+#pragma mark == Prototypes ==
+#endif
+
+//===========================================================================================================================
+// Prototypes
+//===========================================================================================================================
+
+// ifIndexToIfp is in net/if.c, but not exported by net/if.h so provide it here.
+
+extern struct ifnet * ifIndexToIfp(int ifIndex);
+
+// Platform Internals
+
+mDNSlocal void SetupNames( mDNS * const inMDNS );
+mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS );
+mDNSlocal mStatus TearDownInterfaceList( mDNS * const inMDNS );
+mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inAddr, MDNSInterfaceItem **outItem );
+mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, MDNSInterfaceItem *inItem );
+mDNSlocal mStatus
+ SetupSocket(
+ mDNS * const inMDNS,
+ const struct ifaddrs * inAddr,
+ mDNSIPPort inPort,
+ MDNSSocketRef * outSocketRef );
+
+// Commands
+
+mDNSlocal mStatus SetupCommandPipe( mDNS * const inMDNS );
+mDNSlocal mStatus TearDownCommandPipe( mDNS * const inMDNS );
+mDNSlocal mStatus SendCommand( const mDNS * const inMDNS, MDNSPipeCommandCode inCommandCode );
+mDNSlocal mStatus ProcessCommand( mDNS * const inMDNS );
+mDNSlocal void ProcessCommandReconfigure( mDNS *inMDNS );
+
+// Threads
+
+mDNSlocal mStatus SetupTask( mDNS * const inMDNS );
+mDNSlocal mStatus TearDownTask( mDNS * const inMDNS );
+mDNSlocal void Task( mDNS *inMDNS );
+mDNSlocal mStatus TaskInit( mDNS *inMDNS );
+mDNSlocal void TaskSetupReadSet( mDNS *inMDNS, fd_set *outReadSet, int *outMaxSocket );
+mDNSlocal void TaskSetupTimeout( mDNSs32 inNextTaskTime, struct timeval *outTimeout );
+mDNSlocal void TaskProcessPacket( mDNS *inMDNS, MDNSInterfaceItem *inItem, MDNSSocketRef inSocketRef );
+
+// Utilities
+
+#if( TARGET_NON_APPLE )
+ mDNSlocal void GenerateUniqueHostName( char *outName, long *ioSeed );
+ mDNSlocal void GenerateUniqueDNSName( char *outName, long *ioSeed );
+#endif
+
+// Platform Accessors
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+typedef struct mDNSPlatformInterfaceInfo mDNSPlatformInterfaceInfo;
+struct mDNSPlatformInterfaceInfo
+{
+ const char * name;
+ mDNSAddr ip;
+};
+
+mDNSexport mStatus mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID );
+mDNSexport mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo );
+
+#ifdef __cplusplus
+ }
+#endif
+
+#if 0
+#pragma mark == Globals ==
+#endif
+
+//===========================================================================================================================
+// Globals
+//===========================================================================================================================
+
+mDNSlocal mDNS * gMDNSPtr = NULL;
+mDNSlocal mDNS_PlatformSupport gMDNSPlatformSupport;
+mDNSlocal mDNSs32 gMDNSTicksToMicrosecondsMultiplier = 0;
+
+// Platform support
+
+mDNSs32 mDNSPlatformOneSecond;
+
+#if 0
+#pragma mark -
+#pragma mark == Public APIs ==
+#endif
+
+//===========================================================================================================================
+// mDNSReconfigure
+//===========================================================================================================================
+
+void mDNSReconfigure( void )
+{
+ // Send a "reconfigure" command to the MDNS task.
+
+ if( gMDNSPtr )
+ {
+ SendCommand( gMDNSPtr, kMDNSPipeCommandCodeReconfigure );
+ }
+}
+
+#if 0
+#pragma mark -
+#pragma mark == Platform Support ==
+#endif
+
+//===========================================================================================================================
+// mDNSPlatformInit
+//===========================================================================================================================
+
+mStatus mDNSPlatformInit( mDNS * const inMDNS )
+{
+ mStatus err;
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "platform init\n" );
+
+ // Initialize variables.
+
+ memset( &gMDNSPlatformSupport, 0, sizeof( gMDNSPlatformSupport ) );
+ inMDNS->p = &gMDNSPlatformSupport;
+ inMDNS->p->commandPipe = ERROR;
+ inMDNS->p->task = ERROR;
+ inMDNS->p->rescheduled = 1; // Default to rescheduled until fully initialized.
+ mDNSPlatformOneSecond = sysClkRateGet();
+ gMDNSTicksToMicrosecondsMultiplier = ( 1000000L / mDNSPlatformOneSecond );
+
+ // Allocate semaphores.
+
+ inMDNS->p->lockID = semMCreate( SEM_Q_FIFO );
+ require_action( inMDNS->p->lockID, exit, err = mStatus_NoMemoryErr );
+
+ inMDNS->p->readyEvent = semBCreate( SEM_Q_FIFO, SEM_EMPTY );
+ require_action( inMDNS->p->readyEvent, exit, err = mStatus_NoMemoryErr );
+
+ inMDNS->p->quitEvent = semBCreate( SEM_Q_FIFO, SEM_EMPTY );
+ require_action( inMDNS->p->quitEvent, exit, err = mStatus_NoMemoryErr );
+
+ gMDNSPtr = inMDNS;
+
+ // Set up the task and wait for it to initialize. Initialization is done from the task instead of here to avoid
+ // stack space issues. Some of the initialization may require a larger stack than the current task supports.
+
+ err = SetupTask( inMDNS );
+ require_noerr( err, exit );
+
+ err = semTake( inMDNS->p->readyEvent, WAIT_FOREVER );
+ require_noerr( err, exit );
+ err = inMDNS->p->taskInitErr;
+ require_noerr( err, exit );
+
+ mDNSCoreInitComplete( inMDNS, err );
+
+exit:
+ if( err )
+ {
+ mDNSPlatformClose( inMDNS );
+ }
+ dlog( kDebugLevelInfo, DEBUG_NAME "platform init done (err=%ld)\n", err );
+ return( err );
+}
+
+//===========================================================================================================================
+// mDNSPlatformClose
+//===========================================================================================================================
+
+void mDNSPlatformClose( mDNS * const inMDNS )
+{
+ mStatus err;
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "platform close\n" );
+ check( inMDNS );
+
+ // Tear everything down.
+
+ err = TearDownTask( inMDNS );
+ check_noerr( err );
+
+ err = TearDownInterfaceList( inMDNS );
+ check_noerr( err );
+
+ err = TearDownCommandPipe( inMDNS );
+ check_noerr( err );
+
+ gMDNSPtr = NULL;
+
+ // Release semaphores.
+
+ if( inMDNS->p->quitEvent )
+ {
+ semDelete( inMDNS->p->quitEvent );
+ inMDNS->p->quitEvent = 0;
+ }
+ if( inMDNS->p->readyEvent )
+ {
+ semDelete( inMDNS->p->readyEvent );
+ inMDNS->p->readyEvent = 0;
+ }
+ if( inMDNS->p->lockID )
+ {
+ semDelete( inMDNS->p->lockID );
+ inMDNS->p->lockID = 0;
+ }
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "platform close done\n" );
+}
+
+//===========================================================================================================================
+// mDNSPlatformSendUDP
+//===========================================================================================================================
+
+mStatus
+ mDNSPlatformSendUDP(
+ const mDNS * const inMDNS,
+ const DNSMessage * const inMsg,
+ const mDNSu8 * const inMsgEnd,
+ mDNSInterfaceID inInterfaceID,
+ mDNSIPPort inSrcPort,
+ const mDNSAddr * inDstIP,
+ mDNSIPPort inDstPort )
+{
+ mStatus err;
+ MDNSInterfaceItem * item;
+ struct sockaddr_in addr;
+ int n;
+
+ DEBUG_UNUSED( inSrcPort );
+
+ dlog( kDebugLevelChatty, DEBUG_NAME "platform send UDP\n" );
+
+ // Check parameters.
+
+ check( inMDNS );
+ check( inMsg );
+ check( inMsgEnd );
+ check( inInterfaceID );
+ check( inDstIP );
+ if( inDstIP->type != mDNSAddrType_IPv4 )
+ {
+ err = mStatus_BadParamErr;
+ goto exit;
+ }
+
+#if( DEBUG )
+ // Make sure the InterfaceID is valid.
+
+ for( item = inMDNS->p->interfaceList; item; item = item->next )
+ {
+ if( item == (MDNSInterfaceItem *) inInterfaceID )
+ {
+ break;
+ }
+ }
+ require_action( item, exit, err = mStatus_NoSuchNameErr );
+#endif
+
+ // Send the packet.
+
+ item = (MDNSInterfaceItem *) inInterfaceID;
+ check( item->sendingSocketRef != kInvalidSocketRef );
+
+ memset( &addr, 0, sizeof( addr ) );
+ addr.sin_family = AF_INET;
+ addr.sin_port = inDstPort.NotAnInteger;
+ addr.sin_addr.s_addr = inDstIP->ip.v4.NotAnInteger;
+
+ n = inMsgEnd - ( (const mDNSu8 * const) inMsg );
+ n = sendto( item->sendingSocketRef, (char *) inMsg, n, 0, (struct sockaddr *) &addr, sizeof( addr ) );
+ check_errno( n, errno );
+
+ item->sendErrorCounter += ( n < 0 );
+ item->sendMulticastCounter += ( inDstPort.NotAnInteger == MulticastDNSPort.NotAnInteger );
+ item->sendUnicastCounter += ( inDstPort.NotAnInteger != MulticastDNSPort.NotAnInteger );
+
+ dlog( kDebugLevelChatty, DEBUG_NAME "sent (to=%u.%u.%u.%u:%hu)\n",
+ inDstIP->ip.v4.b[ 0 ], inDstIP->ip.v4.b[ 1 ], inDstIP->ip.v4.b[ 2 ], inDstIP->ip.v4.b[ 3 ],
+ htons( inDstPort.NotAnInteger ) );
+ err = mStatus_NoError;
+
+exit:
+ dlog( kDebugLevelChatty, DEBUG_NAME "platform send UDP done\n" );
+ return( err );
+}
+
+//===========================================================================================================================
+// mDNSPlatformLock
+//===========================================================================================================================
+
+void mDNSPlatformLock( const mDNS * const inMDNS )
+{
+ check( inMDNS->p->lockID );
+
+ if( inMDNS->p->lockID )
+ {
+ #if( TARGET_NON_APPLE )
+ semTake( inMDNS->p->lockID, WAIT_FOREVER );
+ #else
+ semTakeDeadlockDetect( inMDNS->p->lockID, WAIT_FOREVER );
+ #endif
+ }
+}
+
+//===========================================================================================================================
+// mDNSPlatformUnlock
+//===========================================================================================================================
+
+void mDNSPlatformUnlock( const mDNS * const inMDNS )
+{
+ check( inMDNS );
+ check( inMDNS->p );
+ check( inMDNS->p->lockID );
+ check_string( inMDNS->p->task != ERROR, "mDNS task not started" );
+
+ // When an API routine is called, "m->NextScheduledEvent" is reset to "timenow" before calling mDNSPlatformUnlock()
+ // Since our main mDNS_Execute() loop is on a different thread, we need to wake up that thread to:
+ // (a) handle immediate work (if any) resulting from this API call
+ // (b) calculate the next sleep time between now and the next interesting event
+
+ if( ( mDNSPlatformTimeNow() - inMDNS->NextScheduledEvent ) >= 0 )
+ {
+ // We only need to send the reschedule event when called from a task other than the mDNS task since if we are
+ // called from mDNS task, we'll loop back and call mDNS_Execute. This avoids filling up the command queue.
+
+ if( ( inMDNS->p->rescheduled++ == 0 ) && ( taskIdSelf() != inMDNS->p->task ) )
+ {
+ SendCommand( inMDNS, kMDNSPipeCommandCodeReschedule );
+ }
+ }
+
+ if( inMDNS->p->lockID )
+ {
+ semGive( inMDNS->p->lockID );
+ }
+}
+
+//===========================================================================================================================
+// mDNSPlatformStrLen
+//===========================================================================================================================
+
+mDNSu32 mDNSPlatformStrLen( const void *inSrc )
+{
+ check( inSrc );
+
+ return( (mDNSu32) strlen( (const char *) inSrc ) );
+}
+
+//===========================================================================================================================
+// mDNSPlatformStrCopy
+//===========================================================================================================================
+
+void mDNSPlatformStrCopy( const void *inSrc, void *inDst )
+{
+ check( inSrc );
+ check( inDst );
+
+ strcpy( (char *) inDst, (const char*) inSrc );
+}
+
+//===========================================================================================================================
+// mDNSPlatformMemCopy
+//===========================================================================================================================
+
+void mDNSPlatformMemCopy( const void *inSrc, void *inDst, mDNSu32 inSize )
+{
+ check( inSrc );
+ check( inDst );
+
+ memcpy( inDst, inSrc, inSize );
+}
+
+//===========================================================================================================================
+// mDNSPlatformMemSame
+//===========================================================================================================================
+
+mDNSBool mDNSPlatformMemSame( const void *inSrc, const void *inDst, mDNSu32 inSize )
+{
+ check( inSrc );
+ check( inDst );
+
+ return( memcmp( inSrc, inDst, inSize ) == 0 );
+}
+
+//===========================================================================================================================
+// mDNSPlatformMemZero
+//===========================================================================================================================
+
+void mDNSPlatformMemZero( void *inDst, mDNSu32 inSize )
+{
+ check( inDst );
+
+ memset( inDst, 0, inSize );
+}
+
+//===========================================================================================================================
+// mDNSPlatformMemAllocate
+//===========================================================================================================================
+
+mDNSexport void * mDNSPlatformMemAllocate( mDNSu32 inSize )
+{
+ void * mem;
+
+ check( inSize > 0 );
+
+ mem = malloc( inSize );
+ check( mem );
+
+ return( mem );
+}
+
+//===========================================================================================================================
+// mDNSPlatformMemFree
+//===========================================================================================================================
+
+mDNSexport void mDNSPlatformMemFree( void *inMem )
+{
+ check( inMem );
+
+ free( inMem );
+}
+
+//===========================================================================================================================
+// mDNSPlatformTimeInit
+//===========================================================================================================================
+
+mDNSexport mStatus mDNSPlatformTimeInit( mDNSs32 *outTimeNow )
+{
+ check( outTimeNow );
+
+ // No special setup is required on VxWorks -- we just use tickGet().
+
+ *outTimeNow = mDNSPlatformTimeNow();
+ return( mStatus_NoError );
+}
+
+//===========================================================================================================================
+// mDNSPlatformTimeNow
+//===========================================================================================================================
+
+mDNSs32 mDNSPlatformTimeNow( void )
+{
+ return( (mDNSs32) tickGet() );
+}
+
+//===========================================================================================================================
+// mDNSPlatformInterfaceNameToID
+//===========================================================================================================================
+
+mStatus mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID )
+{
+ mStatus err;
+ MDNSInterfaceItem * ifd;
+
+ check( inMDNS );
+ check( inMDNS->p );
+ check( inName );
+
+ // Search for an interface with the specified name,
+
+ for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next )
+ {
+ if( strcmp( ifd->name, inName ) == 0 )
+ {
+ break;
+ }
+ }
+ if( !ifd )
+ {
+ err = mStatus_NoSuchNameErr;
+ goto exit;
+ }
+
+ // Success!
+
+ if( outID )
+ {
+ *outID = (mDNSInterfaceID) ifd;
+ }
+ err = mStatus_NoError;
+
+exit:
+ return( err );
+}
+
+//===========================================================================================================================
+// mDNSPlatformInterfaceIDToInfo
+//===========================================================================================================================
+
+mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo )
+{
+ mStatus err;
+ MDNSInterfaceItem * ifd;
+
+ check( inMDNS );
+ check( inID );
+ check( outInfo );
+
+ // Search for an interface with the specified ID,
+
+ for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next )
+ {
+ if( ifd == (MDNSInterfaceItem *) inID )
+ {
+ break;
+ }
+ }
+ if( !ifd )
+ {
+ err = mStatus_NoSuchNameErr;
+ goto exit;
+ }
+
+ // Success!
+
+ outInfo->name = ifd->name;
+ outInfo->ip = ifd->hostSet.ip;
+ err = mStatus_NoError;
+
+exit:
+ return( err );
+}
+
+//===========================================================================================================================
+// debugf_
+//===========================================================================================================================
+
+#if( MDNS_DEBUGMSGS )
+mDNSexport void debugf_( const char *format, ... )
+{
+ char buffer[ 512 ];
+ va_list args;
+ mDNSu32 length;
+
+ va_start( args, format );
+ length = mDNS_vsnprintf( buffer, sizeof( buffer ), format, args );
+ va_end( args );
+
+ dlog( kDebugLevelInfo, "%s\n", buffer );
+}
+#endif
+
+//===========================================================================================================================
+// verbosedebugf_
+//===========================================================================================================================
+
+#if( MDNS_DEBUGMSGS > 1 )
+mDNSexport void verbosedebugf_( const char *format, ... )
+{
+ char buffer[ 512 ];
+ va_list args;
+ mDNSu32 length;
+
+ va_start( args, format );
+ length = mDNS_vsnprintf( buffer, sizeof( buffer ), format, args );
+ va_end( args );
+
+ dlog( kDebugLevelVerbose, "%s\n", buffer );
+}
+#endif
+
+//===========================================================================================================================
+// LogMsg
+//===========================================================================================================================
+
+void LogMsg( const char *inFormat, ... )
+{
+ char buffer[ 512 ];
+ va_list args;
+ mDNSu32 length;
+
+ va_start( args, inFormat );
+ length = mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args );
+ va_end( args );
+
+ dlog( kDebugLevelWarning, "%s\n", buffer );
+}
+
+#if 0
+#pragma mark -
+#pragma mark == Platform Internals ==
+#endif
+
+//===========================================================================================================================
+// SetupNames
+//===========================================================================================================================
+
+mDNSlocal void SetupNames( mDNS * const inMDNS )
+{
+ char tempCString[ 128 ];
+ mDNSu8 tempPString[ 128 ];
+ mDNSu8 * namePtr;
+
+ // Set up the host name.
+
+ tempCString[ 0 ] = '\0';
+ GenerateUniqueHostName( tempCString, NULL );
+ check( tempCString[ 0 ] != '\0' );
+ if( tempCString[ 0 ] == '\0' )
+ {
+ // No name so use the default.
+
+ strcpy( tempCString, kMDNSDefaultName );
+ }
+ inMDNS->nicelabel.c[ 0 ] = strlen( tempCString );
+ memcpy( &inMDNS->nicelabel.c[ 1 ], tempCString, inMDNS->nicelabel.c[ 0 ] );
+ check( inMDNS->nicelabel.c[ 0 ] > 0 );
+
+ // Set up the DNS name.
+
+ tempCString[ 0 ] = '\0';
+ GenerateUniqueDNSName( tempCString, NULL );
+ if( tempCString[ 0 ] != '\0' )
+ {
+ tempPString[ 0 ] = strlen( tempCString );
+ memcpy( &tempPString[ 1 ], tempCString, tempPString[ 0 ] );
+ namePtr = tempPString;
+ }
+ else
+ {
+ // No DNS name so use the host name.
+
+ namePtr = inMDNS->nicelabel.c;
+ }
+ ConvertUTF8PstringToRFC1034HostLabel( namePtr, &inMDNS->hostlabel );
+ if( inMDNS->hostlabel.c[ 0 ] == 0 )
+ {
+ // Nice name has no characters that are representable as an RFC 1034 name (e.g. Japanese) so use the default.
+
+ MakeDomainLabelFromLiteralString( &inMDNS->hostlabel, kMDNSDefaultName );
+ }
+ check( inMDNS->hostlabel.c[ 0 ] > 0 );
+
+ mDNS_GenerateFQDN( inMDNS );
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "nice name \"%.*s\"\n", inMDNS->nicelabel.c[ 0 ], &inMDNS->nicelabel.c[ 1 ] );
+ dlog( kDebugLevelInfo, DEBUG_NAME "host name \"%.*s\"\n", inMDNS->hostlabel.c[ 0 ], &inMDNS->hostlabel.c[ 1 ] );
+}
+
+//===========================================================================================================================
+// SetupInterfaceList
+//===========================================================================================================================
+
+mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS )
+{
+ mStatus err;
+ struct ifaddrs * addrs;
+ struct ifaddrs * p;
+ uint32_t flagMask;
+ uint32_t flagTest;
+ MDNSInterfaceItem ** next;
+ MDNSInterfaceItem * item;
+
+ addrs = NULL;
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface list\n" );
+ check( inMDNS );
+
+ // Tear down any existing interfaces that may be set up.
+
+ TearDownInterfaceList( inMDNS );
+ inMDNS->p->interfaceList = NULL;
+ next = &inMDNS->p->interfaceList;
+
+ // Set up each interface that is active, multicast-capable, and not the loopback interface or point-to-point.
+
+ flagMask = IFF_UP | IFF_MULTICAST | IFF_LOOPBACK | IFF_POINTOPOINT;
+ flagTest = IFF_UP | IFF_MULTICAST;
+
+ err = getifaddrs( &addrs );
+ require_noerr( err, exit );
+
+ for( p = addrs; p; p = p->ifa_next )
+ {
+ if( ( p->ifa_flags & flagMask ) == flagTest )
+ {
+ err = SetupInterface( inMDNS, p, &item );
+ require_noerr( err, exit );
+
+ *next = item;
+ next = &item->next;
+ }
+ }
+ err = mStatus_NoError;
+
+exit:
+ if( addrs )
+ {
+ freeifaddrs( addrs );
+ }
+ if( err )
+ {
+ TearDownInterfaceList( inMDNS );
+ }
+ dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface list done (err=%ld)\n", err );
+ return( err );
+}
+
+//===========================================================================================================================
+// TearDownInterfaceList
+//===========================================================================================================================
+
+mDNSlocal mStatus TearDownInterfaceList( mDNS * const inMDNS )
+{
+ dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down interface list\n" );
+ check( inMDNS );
+
+ // Tear down all the interfaces.
+
+ while( inMDNS->p->interfaceList )
+ {
+ MDNSInterfaceItem * item;
+
+ item = inMDNS->p->interfaceList;
+ inMDNS->p->interfaceList = item->next;
+
+ TearDownInterface( inMDNS, item );
+ }
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down interface list done\n" );
+ return( mStatus_NoError );
+}
+
+//===========================================================================================================================
+// SetupInterface
+//===========================================================================================================================
+
+mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inAddr, MDNSInterfaceItem **outItem )
+{
+ mStatus err;
+ MDNSInterfaceItem * item;
+ MDNSSocketRef socketRef;
+ const struct sockaddr_in * ipv4;
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface (name=%s)\n", inAddr->ifa_name );
+ check( inMDNS );
+ check( inAddr );
+ check( inAddr->ifa_addr );
+ ipv4 = (const struct sockaddr_in *) inAddr->ifa_addr;
+ check( outItem );
+
+ // Allocate memory for the info item.
+
+ item = (MDNSInterfaceItem *) calloc( 1, sizeof( *item ) );
+ require_action( item, exit, err = mStatus_NoMemoryErr );
+ strcpy( item->name, inAddr->ifa_name );
+ item->multicastSocketRef = kInvalidSocketRef;
+ item->unicastSocketRef = kInvalidSocketRef;
+ item->sendingSocketRef = kInvalidSocketRef;
+
+ // Set up the multicast DNS (port 5353) socket for this interface.
+
+ err = SetupSocket( inMDNS, inAddr, MulticastDNSPort, &socketRef );
+ require_noerr( err, exit );
+ item->multicastSocketRef = socketRef;
+
+ // Set up the unicast DNS (port 53) socket for this interface (to handle normal DNS requests).
+
+ err = SetupSocket( inMDNS, inAddr, UnicastDNSPort, &socketRef );
+ require_noerr( err, exit );
+ item->unicastSocketRef = socketRef;
+
+ // Set up the sending socket for this interface.
+
+ err = SetupSocket( inMDNS, inAddr, zeroIPPort, &socketRef );
+ require_noerr( err, exit );
+ item->sendingSocketRef = socketRef;
+
+ // Register this interface with mDNS.
+
+ item->hostSet.InterfaceID = (mDNSInterfaceID) item;
+ item->hostSet.ip.type = mDNSAddrType_IPv4;
+ item->hostSet.ip.ip.v4.NotAnInteger = ipv4->sin_addr.s_addr;
+ item->hostSet.Advertise = inMDNS->AdvertiseLocalAddresses;
+
+ err = mDNS_RegisterInterface( inMDNS, &item->hostSet );
+ require_noerr( err, exit );
+ item->hostRegistered = mDNStrue;
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "Registered IP address: %u.%u.%u.%u\n",
+ item->hostSet.ip.ip.v4.b[ 0 ], item->hostSet.ip.ip.v4.b[ 1 ],
+ item->hostSet.ip.ip.v4.b[ 2 ], item->hostSet.ip.ip.v4.b[ 3 ] );
+
+ // Success!
+
+ *outItem = item;
+ item = NULL;
+
+exit:
+ if( item )
+ {
+ TearDownInterface( inMDNS, item );
+ }
+ dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface done (name=%s, err=%ld)\n", inAddr->ifa_name, err );
+ return( err );
+}
+
+//===========================================================================================================================
+// TearDownInterface
+//===========================================================================================================================
+
+mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, MDNSInterfaceItem *inItem )
+{
+ MDNSSocketRef socketRef;
+
+ check( inMDNS );
+ check( inItem );
+
+ // Deregister this interface with mDNS.
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "Deregistering IP address: %u.%u.%u.%u\n",
+ inItem->hostSet.ip.ip.v4.b[ 0 ], inItem->hostSet.ip.ip.v4.b[ 1 ],
+ inItem->hostSet.ip.ip.v4.b[ 2 ], inItem->hostSet.ip.ip.v4.b[ 3 ] );
+
+ if( inItem->hostRegistered )
+ {
+ inItem->hostRegistered = mDNSfalse;
+ mDNS_DeregisterInterface( inMDNS, &inItem->hostSet );
+ }
+
+ // Close the multicast socket.
+
+ socketRef = inItem->multicastSocketRef;
+ inItem->multicastSocketRef = kInvalidSocketRef;
+ if( socketRef != kInvalidSocketRef )
+ {
+ dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down multicast socket %d\n", socketRef );
+ close( socketRef );
+ }
+
+ // Close the unicast socket.
+
+ socketRef = inItem->unicastSocketRef;
+ inItem->unicastSocketRef = kInvalidSocketRef;
+ if( socketRef != kInvalidSocketRef )
+ {
+ dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down unicast socket %d\n", socketRef );
+ close( socketRef );
+ }
+
+ // Close the sending socket.
+
+ socketRef = inItem->sendingSocketRef;
+ inItem->sendingSocketRef = kInvalidSocketRef;
+ if( socketRef != kInvalidSocketRef )
+ {
+ dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down sending socket %d\n", socketRef );
+ close( socketRef );
+ }
+
+ // Free the memory used by the interface info.
+
+ free( inItem );
+ return( mStatus_NoError );
+}
+
+//===========================================================================================================================
+// SetupSocket
+//===========================================================================================================================
+
+mDNSlocal mStatus
+ SetupSocket(
+ mDNS * const inMDNS,
+ const struct ifaddrs * inAddr,
+ mDNSIPPort inPort,
+ MDNSSocketRef * outSocketRef )
+{
+ mStatus err;
+ MDNSSocketRef socketRef;
+ int option;
+ unsigned char optionByte;
+ struct ip_mreq mreq;
+ const struct sockaddr_in * ipv4;
+ struct sockaddr_in addr;
+ mDNSv4Addr ip;
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "setting up socket done\n" );
+ check( inMDNS );
+ check( inAddr );
+ check( inAddr->ifa_addr );
+ ipv4 = (const struct sockaddr_in *) inAddr->ifa_addr;
+ check( outSocketRef );
+
+ // Set up a UDP socket for multicast DNS.
+
+ socketRef = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
+ require_errno_action( socketRef, errno, exit, err = mStatus_UnknownErr );
+
+ // A port of zero means this socket is for sending and should be set up for sending. Otherwise, it is for receiving
+ // and should be set up for receiving. The reason for separate sending vs receiving sockets to workaround problems
+ // with VxWorks IP stack when using dynamic IP configuration such as DHCP (problems binding to wildcard IP when the
+ // IP address later changes). Since we have to bind the Multicast DNS address to workaround these issues we have to
+ // use a separate sending socket since it is illegal to send a packet with a multicast source address (RFC 1122).
+
+ if( inPort.NotAnInteger != zeroIPPort.NotAnInteger )
+ {
+ // Turn on reuse port option so multiple servers can listen for Multicast DNS packets.
+
+ option = 1;
+ err = setsockopt( socketRef, SOL_SOCKET, SO_REUSEADDR, (char *) &option, sizeof( option ) );
+ check_errno( err, errno );
+
+ // Join the all-DNS multicast group so we receive Multicast DNS packets.
+
+ ip.NotAnInteger = ipv4->sin_addr.s_addr;
+ mreq.imr_multiaddr.s_addr = AllDNSLinkGroup.NotAnInteger;
+ mreq.imr_interface.s_addr = ip.NotAnInteger;
+ err = setsockopt( socketRef, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof( mreq ) );
+ check_errno( err, errno );
+
+ // Bind to the multicast DNS address and specified port (53 for unicast or 5353 for multicast).
+
+ memset( &addr, 0, sizeof( addr ) );
+ addr.sin_family = AF_INET;
+ addr.sin_port = inPort.NotAnInteger;
+ addr.sin_addr.s_addr = AllDNSLinkGroup.NotAnInteger;
+ err = bind( socketRef, (struct sockaddr *) &addr, sizeof( addr ) );
+ check_errno( err, errno );
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "setting up socket done (%s, %u.%u.%u.%u:%u, %d)\n",
+ inAddr->ifa_name, ip.b[ 0 ], ip.b[ 1 ], ip.b[ 2 ], ip.b[ 3 ], ntohs( inPort.NotAnInteger ), socketRef );
+ }
+ else
+ {
+ // Bind to the interface address and multicast DNS port.
+
+ ip.NotAnInteger = ipv4->sin_addr.s_addr;
+ memset( &addr, 0, sizeof( addr ) );
+ addr.sin_family = AF_INET;
+ addr.sin_port = MulticastDNSPort.NotAnInteger;
+ addr.sin_addr.s_addr = ip.NotAnInteger;
+ err = bind( socketRef, (struct sockaddr *) &addr, sizeof( addr ) );
+ check_errno( err, errno );
+
+ // Direct multicast packets to the specified interface.
+
+ addr.sin_addr.s_addr = ip.NotAnInteger;
+ err = setsockopt( socketRef, IPPROTO_IP, IP_MULTICAST_IF, (char *) &addr.sin_addr, sizeof( addr.sin_addr ) );
+ check_errno( err, errno );
+
+ // Set the TTL of outgoing unicast packets to 255 (helps against spoofing).
+
+ option = 255;
+ err = setsockopt( socketRef, IPPROTO_IP, IP_TTL, (char *) &option, sizeof( option ) );
+ check_errno( err, errno );
+
+ // Set the TTL of outgoing multicast packets to 255 (helps against spoofing).
+
+ optionByte = 255;
+ err = setsockopt( socketRef, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &optionByte, sizeof( optionByte ) );
+ check_errno( err, errno );
+
+ // Mark packets as high-throughput/low-delay (i.e. lowest reliability) to maximize 802.11 multicast rate.
+
+ option = IPTOS_LOWDELAY | IPTOS_THROUGHPUT;
+ err = setsockopt( socketRef, IPPROTO_IP, IP_TOS, (char *) &option, sizeof( option ) );
+ check_errno( err, errno );
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "setting up sending socket done (%s, %u.%u.%u.%u, %d)\n",
+ inAddr->ifa_name, ip.b[ 0 ], ip.b[ 1 ], ip.b[ 2 ], ip.b[ 3 ], socketRef );
+ }
+
+ // Success!
+
+ *outSocketRef = socketRef;
+ socketRef = kInvalidSocketRef;
+ err = mStatus_NoError;
+
+exit:
+ if( socketRef != kInvalidSocketRef )
+ {
+ close( socketRef );
+ }
+ return( err );
+}
+
+#if 0
+#pragma mark -
+#pragma mark == Commands ==
+#endif
+
+//===========================================================================================================================
+// SetupCommandPipe
+//===========================================================================================================================
+
+mDNSlocal mStatus SetupCommandPipe( mDNS * const inMDNS )
+{
+ mStatus err;
+
+ // Clean up any leftover command pipe.
+
+ TearDownCommandPipe( inMDNS );
+
+ // Create the pipe device and open it.
+
+ pipeDevCreate( kMDNSPipeName, kMDNSPipeMessageQueueSize, kMDNSPipeMessageSize );
+
+ inMDNS->p->commandPipe = open( kMDNSPipeName, O_RDWR, 0 );
+ require_errno_action( inMDNS->p->commandPipe, errno, exit, err = mStatus_UnsupportedErr );
+
+ err = mStatus_NoError;
+
+exit:
+ return( err );
+}
+
+//===========================================================================================================================
+// TearDownCommandPipe
+//===========================================================================================================================
+
+mDNSlocal mStatus TearDownCommandPipe( mDNS * const inMDNS )
+{
+ if( inMDNS->p->commandPipe != ERROR )
+ {
+ close( inMDNS->p->commandPipe );
+ inMDNS->p->commandPipe = ERROR;
+ }
+ return( mStatus_NoError );
+}
+
+//===========================================================================================================================
+// SendCommand
+//===========================================================================================================================
+
+mDNSlocal mStatus SendCommand( const mDNS * const inMDNS, MDNSPipeCommandCode inCommandCode )
+{
+ mStatus err;
+
+ require_action( inMDNS->p->commandPipe != ERROR, exit, err = mStatus_NotInitializedErr );
+
+ err = write( inMDNS->p->commandPipe, &inCommandCode, sizeof( inCommandCode ) );
+ require_errno( err, errno, exit );
+
+ err = mStatus_NoError;
+
+exit:
+ return( err );
+}
+
+//===========================================================================================================================
+// ProcessCommand
+//===========================================================================================================================
+
+mDNSlocal mStatus ProcessCommand( mDNS * const inMDNS )
+{
+ mStatus err;
+ MDNSPipeCommandCode commandCode;
+
+ require_action( inMDNS->p->commandPipe != ERROR, exit, err = mStatus_NotInitializedErr );
+
+ // Read the command code from the pipe and dispatch it.
+
+ err = read( inMDNS->p->commandPipe, &commandCode, sizeof( commandCode ) );
+ require_errno( err, errno, exit );
+
+ switch( commandCode )
+ {
+ case kMDNSPipeCommandCodeReschedule:
+
+ // Reschedule event. Do nothing here, but this will cause mDNS_Execute to run before waiting again.
+
+ dlog( kDebugLevelChatty, DEBUG_NAME "reschedule\n" );
+ break;
+
+ case kMDNSPipeCommandCodeReconfigure:
+ ProcessCommandReconfigure( inMDNS );
+ break;
+
+ case kMDNSPipeCommandCodeQuit:
+
+ // Quit requested. Set quit flag and bump the config ID to let the thread exit normally.
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "processing pipe quit command\n" );
+ inMDNS->p->quit = mDNStrue;
+ ++inMDNS->p->configID;
+ break;
+
+ default:
+ dlog( kDebugLevelError, DEBUG_NAME "unknown pipe command code (code=0x%08X)\n", commandCode );
+ err = mStatus_BadParamErr;
+ goto exit;
+ break;
+ }
+ err = mStatus_NoError;
+
+exit:
+ return( err );
+}
+
+//===========================================================================================================================
+// ProcessCommandReconfigure
+//===========================================================================================================================
+
+mDNSlocal void ProcessCommandReconfigure( mDNS *inMDNS )
+{
+ mStatus err;
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "processing pipe reconfigure command\n" );
+
+ // Tear down the existing interfaces and set up new ones using the new IP info.
+
+ mDNSPlatformLock( inMDNS );
+
+ err = TearDownInterfaceList( inMDNS );
+ check_noerr( err );
+
+ err = SetupInterfaceList( inMDNS );
+ check_noerr( err );
+
+ mDNSPlatformUnlock( inMDNS );
+
+ // Inform clients of the change.
+
+ if( inMDNS->MainCallback )
+ {
+ inMDNS->MainCallback( inMDNS, mStatus_ConfigChanged );
+ }
+
+ // Force mDNS to update.
+
+ mDNSCoreMachineSleep( inMDNS, mDNSfalse );
+
+ // Bump the config ID so the main processing loop detects the configuration change.
+
+ ++inMDNS->p->configID;
+}
+
+#if 0
+#pragma mark -
+#pragma mark == Threads ==
+#endif
+
+//===========================================================================================================================
+// SetupTask
+//===========================================================================================================================
+
+mDNSlocal mStatus SetupTask( mDNS * const inMDNS )
+{
+ mStatus err;
+ int task;
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "setting up thread\n" );
+ check( inMDNS );
+
+ // Create our main thread. Note: The task will save off its ID in the globals. We cannot do it here because the
+ // task invokes code that needs it and the task may begin execution before taskSpawn returns the task ID.
+ // This also means code in this thread context cannot rely on the task ID until the task has fully initialized.
+
+ task = taskSpawn( kMDNSTaskName, kMDNSTaskPriority, 0, kMDNSTaskStackSize, (FUNCPTR) Task,
+ (int) inMDNS, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
+ require_action( task != ERROR, exit, err = mStatus_NoMemoryErr );
+
+ err = mStatus_NoError;
+
+exit:
+ dlog( kDebugLevelVerbose, DEBUG_NAME "setting up thread done (err=%ld, id=%d)\n", err, task );
+ return( err );
+}
+
+//===========================================================================================================================
+// TearDownTask
+//===========================================================================================================================
+
+mDNSlocal mStatus TearDownTask( mDNS * const inMDNS )
+{
+ mStatus err;
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down thread\n" );
+ check( inMDNS );
+
+ // Send a quit command to cause the thread to exit.
+
+ SendCommand( inMDNS, kMDNSPipeCommandCodeQuit );
+
+ // Wait for the thread to signal it has exited. Timeout in 10 seconds to handle a hung thread.
+
+ if( inMDNS->p->quitEvent )
+ {
+ err = semTake( inMDNS->p->quitEvent, sysClkRateGet() * 10 );
+ check_noerr( err );
+ }
+ err = mStatus_NoError;
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down thread done (err=%ld)\n", err );
+ return( err );
+}
+
+//===========================================================================================================================
+// Task
+//===========================================================================================================================
+
+mDNSlocal void Task( mDNS *inMDNS )
+{
+ mStatus err;
+ fd_set allReadSet;
+ MDNSInterfaceItem * item;
+ int maxSocket;
+ long configID;
+ struct timeval timeout;
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "task starting\n" );
+ check( inMDNS );
+
+ // Set up everything up.
+
+ err = TaskInit( inMDNS );
+ require_noerr( err, exit );
+
+ // Main Processing Loop.
+
+ while( !inMDNS->p->quit )
+ {
+ // Set up the read set here to avoid the overhead of setting it up each iteration of the main processing loop.
+ // If the configuration changes, the server ID will be bumped, causing this code to set up the read set again.
+
+ TaskSetupReadSet( inMDNS, &allReadSet, &maxSocket );
+ configID = inMDNS->p->configID;
+ dlog( kDebugLevelVerbose, DEBUG_NAME "task starting processing loop (configID=%ld)\n", configID );
+
+ while( configID == inMDNS->p->configID )
+ {
+ mDNSs32 nextTaskTime;
+ fd_set readSet;
+ int n;
+
+ // Give the mDNS core a chance to do its work. Reset the rescheduled flag before calling mDNS_Execute
+ // so anything that needs processing during or after causes a re-schedule to wake up the thread. The
+ // reschedule flag is set to 1 after processing a waking up to prevent redundant reschedules while
+ // processing packets. This introduces a window for a race condition because the thread wake-up and
+ // reschedule set are not atomic, but this would be benign. Even if the reschedule flag is "corrupted"
+ // like this, it would only result in a redundant reschedule since it will loop back to mDNS_Execute.
+
+ inMDNS->p->rescheduled = 0;
+ nextTaskTime = mDNS_Execute( inMDNS );
+ TaskSetupTimeout( nextTaskTime, &timeout );
+
+ // Wait until something occurs (e.g. command, incoming packet, or timeout).
+
+ readSet = allReadSet;
+ n = select( maxSocket + 1, &readSet, NULL, NULL, &timeout );
+ inMDNS->p->rescheduled = 1;
+ check_errno( n, errno );
+ dlog( kDebugLevelChatty - 1, DEBUG_NAME "task select result = %d\n", n );
+ if( n == 0 )
+ {
+ // Next task timeout occurred. Loop back up to give mDNS core a chance to work.
+
+ dlog( kDebugLevelChatty, DEBUG_NAME "next task timeout occurred (%ld)\n", mDNSPlatformTimeNow() );
+ continue;
+ }
+
+ // Scan the read set to determine if any sockets have something pending and process them.
+
+ n = 0;
+ for( item = inMDNS->p->interfaceList; item; item = item->next )
+ {
+ if( FD_ISSET( item->multicastSocketRef, &readSet ) )
+ {
+ TaskProcessPacket( inMDNS, item, item->multicastSocketRef );
+ ++n;
+ }
+ if( FD_ISSET( item->unicastSocketRef, &readSet ) )
+ {
+ TaskProcessPacket( inMDNS, item, item->unicastSocketRef );
+ ++n;
+ }
+ }
+
+ // Check for a pending command and process it.
+
+ if( FD_ISSET( inMDNS->p->commandPipe, &readSet ) )
+ {
+ ProcessCommand( inMDNS );
+ ++n;
+ }
+ check( n > 0 );
+ }
+ }
+
+exit:
+ // Signal we've quit.
+
+ check( inMDNS->p->quitEvent );
+ semGive( inMDNS->p->quitEvent );
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "task ended\n" );
+}
+
+//===========================================================================================================================
+// TaskInit
+//===========================================================================================================================
+
+mDNSlocal mStatus TaskInit( mDNS *inMDNS )
+{
+ mStatus err;
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "task init\n" );
+ check( inMDNS->p->readyEvent );
+
+ inMDNS->p->task = taskIdSelf();
+
+ err = SetupCommandPipe( inMDNS );
+ require_noerr( err, exit );
+
+ SetupNames( inMDNS );
+
+ err = SetupInterfaceList( inMDNS );
+ require_noerr( err, exit );
+
+exit:
+ // Signal the "ready" semaphore to indicate the task initialization code has completed (success or not).
+
+ inMDNS->p->taskInitErr = err;
+ semGive( inMDNS->p->readyEvent );
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "task init done (err=%ld)\n", err );
+ return( err );
+}
+
+//===========================================================================================================================
+// TaskSetupReadSet
+//===========================================================================================================================
+
+mDNSlocal void TaskSetupReadSet( mDNS *inMDNS, fd_set *outReadSet, int *outMaxSocket )
+{
+ MDNSInterfaceItem * item;
+ int maxSocket;
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "task setting up read set\n" );
+ check( inMDNS );
+ check( outReadSet );
+ check( outMaxSocket );
+
+ // Initialize the read set. Default the max socket to -1 so "maxSocket + 1" (as needed by select) is zero. This
+ // should never happen since we should always have at least one interface, but it's just to be safe.
+
+ FD_ZERO( outReadSet );
+ maxSocket = -1;
+
+ // Add all the receiving sockets to the read set.
+
+ for( item = inMDNS->p->interfaceList; item; item = item->next )
+ {
+ FD_SET( item->multicastSocketRef, outReadSet );
+ FD_SET( item->unicastSocketRef, outReadSet );
+ if( item->multicastSocketRef > maxSocket )
+ {
+ maxSocket = item->multicastSocketRef;
+ }
+ if( item->unicastSocketRef > maxSocket )
+ {
+ maxSocket = item->unicastSocketRef;
+ }
+ }
+
+ // Add the command pipe to the read set.
+
+ FD_SET( inMDNS->p->commandPipe, outReadSet );
+ if( inMDNS->p->commandPipe > maxSocket )
+ {
+ maxSocket = inMDNS->p->commandPipe;
+ }
+ check( maxSocket > 0 );
+ *outMaxSocket = maxSocket;
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "task setting up read set done (maxSocket=%d)\n", maxSocket );
+}
+
+//===========================================================================================================================
+// TaskSetupTimeout
+//===========================================================================================================================
+
+mDNSlocal void TaskSetupTimeout( mDNSs32 inNextTaskTime, struct timeval *outTimeout )
+{
+ mDNSs32 delta;
+
+ // Calculate how long to wait before performing idle processing.
+
+ delta = inNextTaskTime - mDNSPlatformTimeNow();
+ if( delta <= 0 )
+ {
+ // The next task time is now or in the past. Set the timeout to fire immediately.
+
+ outTimeout->tv_sec = 0;
+ outTimeout->tv_usec = 0;
+ }
+ else
+ {
+ // Calculate the seconds and microseconds until the timeout should occur. Add one to the ticks remainder
+ // before multiplying to account for integer rounding error and avoid firing the timeout too early.
+
+ outTimeout->tv_sec = delta / mDNSPlatformOneSecond;
+ outTimeout->tv_usec = ( ( delta % mDNSPlatformOneSecond ) + 1 ) * gMDNSTicksToMicrosecondsMultiplier;
+
+ // Check if the microseconds is more than 1 second. If so, bump the seconds instead.
+
+ if( outTimeout->tv_usec >= 1000000L )
+ {
+ outTimeout->tv_sec += 1;
+ outTimeout->tv_usec = 0;
+ }
+ }
+
+ dlog( kDebugLevelChatty, DEBUG_NAME "next task in %ld:%ld seconds (%ld)\n",
+ outTimeout->tv_sec, outTimeout->tv_usec, inNextTaskTime );
+}
+//===========================================================================================================================
+// TaskProcessPacket
+//===========================================================================================================================
+
+mDNSlocal void TaskProcessPacket( mDNS *inMDNS, MDNSInterfaceItem *inItem, MDNSSocketRef inSocketRef )
+{
+ int n;
+ mDNSBool isMulticast;
+ DNSMessage packet;
+ struct sockaddr_in addr;
+ int addrSize;
+ mDNSu8 * packetEndPtr;
+ mDNSAddr srcAddr;
+ mDNSIPPort srcPort;
+ mDNSAddr dstAddr;
+ mDNSIPPort dstPort;
+
+ isMulticast = ( inSocketRef == inItem->multicastSocketRef );
+
+ // Receive the packet.
+
+ addrSize = sizeof( addr );
+ n = recvfrom( inSocketRef, (char *) &packet, sizeof( packet ), 0, (struct sockaddr *) &addr, &addrSize );
+ check( n >= 0 );
+ if( n >= 0 )
+ {
+ // Set up the src/dst/interface info.
+
+ srcAddr.type = mDNSAddrType_IPv4;
+ srcAddr.ip.v4.NotAnInteger = addr.sin_addr.s_addr;
+ srcPort.NotAnInteger = addr.sin_port;
+ dstAddr.type = mDNSAddrType_IPv4;
+ dstAddr.ip.v4 = isMulticast ? AllDNSLinkGroup : inItem->hostSet.ip.ip.v4;
+ dstPort = isMulticast ? MulticastDNSPort : UnicastDNSPort;
+
+ dlog( kDebugLevelChatty, DEBUG_NAME "packet received\n" );
+ dlog( kDebugLevelChatty, DEBUG_NAME " size = %d\n", n );
+ dlog( kDebugLevelChatty, DEBUG_NAME " src = %u.%u.%u.%u:%hu\n",
+ srcAddr.ip.v4.b[ 0 ], srcAddr.ip.v4.b[ 1 ], srcAddr.ip.v4.b[ 2 ], srcAddr.ip.v4.b[ 3 ],
+ ntohs( srcPort.NotAnInteger ) );
+ dlog( kDebugLevelChatty, DEBUG_NAME " dst = %u.%u.%u.%u:%hu\n",
+ dstAddr.ip.v4.b[ 0 ], dstAddr.ip.v4.b[ 1 ], dstAddr.ip.v4.b[ 2 ], dstAddr.ip.v4.b[ 3 ],
+ ntohs( dstPort.NotAnInteger ) );
+ dlog( kDebugLevelChatty, DEBUG_NAME " interface = 0x%08X\n", (int) inItem->hostSet.InterfaceID );
+ dlog( kDebugLevelChatty, DEBUG_NAME "--\n" );
+
+ // Dispatch the packet to mDNS.
+
+ packetEndPtr = ( (mDNSu8 *) &packet ) + n;
+ mDNSCoreReceive( inMDNS, &packet, packetEndPtr, &srcAddr, srcPort, &dstAddr, dstPort, inItem->hostSet.InterfaceID, 255 );
+ }
+
+ // Update counters.
+
+ inItem->recvMulticastCounter += isMulticast;
+ inItem->recvUnicastCounter += !isMulticast;
+ inItem->recvErrorCounter += ( n < 0 );
+}
+
+#if 0
+#pragma mark -
+#pragma mark == Utilities ==
+#endif
+
+#if( TARGET_NON_APPLE )
+//===========================================================================================================================
+// GenerateUniqueHostName
+//
+// Non-Apple platform stub routine to generate a unique name for the device. Should be implemented to return a unique name.
+//===========================================================================================================================
+
+mDNSlocal void GenerateUniqueHostName( char *outName, long *ioSeed )
+{
+ DEBUG_UNUSED( ioSeed );
+
+ // $$$ Non-Apple Platforms: Fill in appropriate name for device.
+
+ mDNSPlatformStrCopy( kMDNSDefaultName, outName );
+}
+
+//===========================================================================================================================
+// GenerateUniqueDNSName
+//
+// Non-Apple platform stub routine to generate a unique RFC 1034-compatible DNS name for the device. Should be
+// implemented to return a unique name.
+//===========================================================================================================================
+
+mDNSlocal void GenerateUniqueDNSName( char *outName, long *ioSeed )
+{
+ DEBUG_UNUSED( ioSeed );
+
+ // $$$ Non-Apple Platforms: Fill in appropriate DNS name for device.
+
+ mDNSPlatformStrCopy( kMDNSDefaultName, outName );
+}
+#endif
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+// getifaddrs
+//===========================================================================================================================
+
+int getifaddrs( struct ifaddrs **outAddrs )
+{
+ int err;
+ struct ifaddrs * head;
+ struct ifaddrs ** next;
+ struct ifaddrs * ifa;
+ int i;
+ struct ifnet * ifp;
+ char ipString[ INET_ADDR_LEN ];
+ int n;
+
+ head = NULL;
+ next = &head;
+
+ i = 1;
+ for( ;; )
+ {
+ ifp = ifIndexToIfp( i );
+ if( !ifp )
+ {
+ break;
+ }
+ ++i;
+
+ // Allocate and initialize the ifaddrs structure and attach it to the linked list.
+
+ ifa = (struct ifaddrs *) calloc( 1, sizeof( struct ifaddrs ) );
+ require_action( ifa, exit, err = ENOMEM );
+
+ *next = ifa;
+ next = &ifa->ifa_next;
+
+ // Fetch the name.
+
+ ifa->ifa_name = (char *) malloc( 16 );
+ require_action( ifa->ifa_name, exit, err = ENOMEM );
+
+ n = sprintf( ifa->ifa_name, "%s%d", ifp->if_name, ifp->if_unit );
+ require_action( n < 16, exit, err = ENOBUFS );
+
+ // Fetch the address.
+
+ ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( struct sockaddr_in ) );
+ require_action( ifa->ifa_addr, exit, err = ENOMEM );
+
+ ipString[ 0 ] = '\0';
+ #if( TARGET_NON_APPLE )
+ err = ifAddrGet( ifa->ifa_name, ipString );
+ require_noerr( err, exit );
+ #else
+ err = ifAddrGetNonAlias( ifa->ifa_name, ipString );
+ require_noerr( err, exit );
+ #endif
+
+ err = sock_pton( ipString, AF_INET, ifa->ifa_addr, 0, NULL );
+ require_noerr( err, exit );
+
+ // Fetch flags.
+
+ ifa->ifa_flags = ifp->if_flags;
+ }
+
+ // Success!
+
+ if( outAddrs )
+ {
+ *outAddrs = head;
+ head = NULL;
+ }
+ err = 0;
+
+exit:
+ if( head )
+ {
+ freeifaddrs( head );
+ }
+ return( err );
+}
+
+//===========================================================================================================================
+// freeifaddrs
+//===========================================================================================================================
+
+void freeifaddrs( struct ifaddrs *inAddrs )
+{
+ struct ifaddrs * p;
+ struct ifaddrs * q;
+
+ // Free each piece of the structure. Set to null after freeing to handle macro-aliased fields.
+
+ for( p = inAddrs; p; p = q )
+ {
+ q = p->ifa_next;
+
+ if( p->ifa_name )
+ {
+ free( p->ifa_name );
+ p->ifa_name = NULL;
+ }
+ if( p->ifa_addr )
+ {
+ free( p->ifa_addr );
+ p->ifa_addr = NULL;
+ }
+ if( p->ifa_netmask )
+ {
+ free( p->ifa_netmask );
+ p->ifa_netmask = NULL;
+ }
+ if( p->ifa_dstaddr )
+ {
+ free( p->ifa_dstaddr );
+ p->ifa_dstaddr = NULL;
+ }
+ if( p->ifa_data )
+ {
+ free( p->ifa_data );
+ p->ifa_data = NULL;
+ }
+ free( p );
+ }
+}
+
+//===========================================================================================================================
+// sock_pton
+//===========================================================================================================================
+
+int sock_pton( const char *inString, int inFamily, void *outAddr, size_t inAddrSize, size_t *outAddrSize )
+{
+ int err;
+
+ if( inFamily == AF_INET )
+ {
+ struct sockaddr_in * ipv4;
+
+ if( inAddrSize == 0 )
+ {
+ inAddrSize = sizeof( struct sockaddr_in );
+ }
+ if( inAddrSize < sizeof( struct sockaddr_in ) )
+ {
+ err = EINVAL;
+ goto exit;
+ }
+
+ ipv4 = (struct sockaddr_in *) outAddr;
+ err = inet_aton( (char *) inString, &ipv4->sin_addr );
+ if( err == 0 )
+ {
+ ipv4->sin_family = AF_INET;
+ if( outAddrSize )
+ {
+ *outAddrSize = sizeof( struct sockaddr_in );
+ }
+ }
+ }
+#if( defined( AF_INET6 ) )
+ else if( inFamily == AF_INET6 ) // $$$ TO DO: Add IPv6 support.
+ {
+ err = EAFNOSUPPORT;
+ goto exit;
+ }
+#endif
+ else
+ {
+ err = EAFNOSUPPORT;
+ goto exit;
+ }
+
+exit:
+ return( err );
+}
+
+//===========================================================================================================================
+// sock_ntop
+//===========================================================================================================================
+
+char * sock_ntop( const void *inAddr, size_t inAddrSize, char *inBuffer, size_t inBufferSize )
+{
+ const struct sockaddr * addr;
+
+ addr = (const struct sockaddr *) inAddr;
+ if( addr->sa_family == AF_INET )
+ {
+ struct sockaddr_in * ipv4;
+
+ if( inAddrSize == 0 )
+ {
+ inAddrSize = sizeof( struct sockaddr_in );
+ }
+ if( inAddrSize < sizeof( struct sockaddr_in ) )
+ {
+ errno = EINVAL;
+ inBuffer = NULL;
+ goto exit;
+ }
+ if( inBufferSize < 16 )
+ {
+ errno = ENOBUFS;
+ inBuffer = NULL;
+ goto exit;
+ }
+
+ ipv4 = (struct sockaddr_in *) addr;
+ inet_ntoa_b( ipv4->sin_addr, inBuffer );
+ }
+#if( defined( AF_INET6 ) )
+ else if( addr->sa_family == AF_INET6 ) // $$$ TO DO: Add IPv6 support.
+ {
+ errno = EAFNOSUPPORT;
+ inBuffer = NULL;
+ goto exit;
+ }
+#endif
+ else
+ {
+ errno = EAFNOSUPPORT;
+ inBuffer = NULL;
+ goto exit;
+ }
+
+exit:
+ return( inBuffer );
+}
+
+#if 0
+#pragma mark -
+#pragma mark == Debugging ==
+#endif
+
+#if( DEBUG )
+
+void mDNSShow( BOOL inShowRecords );
+void mDNSShowRecords( void );
+void mDNSShowTXT( const void *inTXT, size_t inTXTSize );
+
+//===========================================================================================================================
+// mDNSShow
+//===========================================================================================================================
+
+void mDNSShow( BOOL inShowRecords )
+{
+ MDNSInterfaceItem * item;
+ mDNSAddr ip;
+ int n;
+
+ if( !gMDNSPtr )
+ {
+ printf( "### mDNS not initialized\n" );
+ return;
+ }
+
+ // Globals
+
+ printf( "\n-- mDNS globals --\n" );
+ printf( " sizeof( mDNS ) = %d\n", (int) sizeof( mDNS ) );
+ printf( " sizeof( ResourceRecord ) = %d\n", (int) sizeof( ResourceRecord ) );
+ printf( " sizeof( AuthRecord ) = %d\n", (int) sizeof( AuthRecord ) );
+ printf( " sizeof( CacheRecord ) = %d\n", (int) sizeof( CacheRecord ) );
+ printf( " gMDNSPtr = 0x%08lX\n", (unsigned long) gMDNSPtr );
+ printf( " gMDNSTicksToMicrosecondsMultiplier = %ld\n", gMDNSTicksToMicrosecondsMultiplier );
+ printf( " lockID = 0x%08lX\n", (unsigned long) gMDNSPtr->p->lockID );
+ printf( " readyEvent = 0x%08lX\n", (unsigned long) gMDNSPtr->p->readyEvent );
+ printf( " taskInitErr = %ld\n", gMDNSPtr->p->taskInitErr );
+ printf( " quitEvent = 0x%08lX\n", (unsigned long) gMDNSPtr->p->quitEvent );
+ printf( " commandPipe = %d\n", gMDNSPtr->p->commandPipe );
+ printf( " task = 0x%08lX\n", (unsigned long) gMDNSPtr->p->task );
+ printf( " quit = %d\n", gMDNSPtr->p->quit );
+ printf( " configID = %ld\n", gMDNSPtr->p->configID );
+ printf( " rescheduled = %d\n", gMDNSPtr->p->rescheduled );
+ printf( " nicelabel = \"%.*s\"\n", gMDNSPtr->nicelabel.c[ 0 ], (char *) &gMDNSPtr->nicelabel.c[ 1 ] );
+ printf( " hostLabel = \"%.*s\"\n", gMDNSPtr->hostlabel.c[ 0 ], (char *) &gMDNSPtr->hostlabel.c[ 1 ] );
+ printf( "\n");
+
+ // Interfaces
+
+ printf( "\n-- mDNS interfaces --\n" );
+ n = 1;
+ for( item = gMDNSPtr->p->interfaceList; item; item = item->next )
+ {
+ printf( " -- interface %u --\n", n );
+ printf( " name = \"%s\"\n", item->name );
+ printf( " multicastSocketRef = %d\n", item->multicastSocketRef );
+ printf( " unicastSocketRef = %d\n", item->unicastSocketRef );
+ printf( " sendingSocketRef = %d\n", item->sendingSocketRef );
+ ip = item->hostSet.ip;
+ printf( " hostSet.ip = %u.%u.%u.%u\n", ip.ip.v4.b[ 0 ], ip.ip.v4.b[ 1 ],
+ ip.ip.v4.b[ 2 ], ip.ip.v4.b[ 3 ] );
+ printf( " hostSet.advertise = %s\n", item->hostSet.Advertise ? "YES" : "NO" );
+ printf( " hostRegistered = %s\n", item->hostRegistered ? "YES" : "NO" );
+ printf( " --\n" );
+ printf( " sendMulticastCounter = %d\n", item->sendMulticastCounter );
+ printf( " sendUnicastCounter = %d\n", item->sendUnicastCounter );
+ printf( " sendErrorCounter = %d\n", item->sendErrorCounter );
+ printf( " recvMulticastCounter = %d\n", item->recvMulticastCounter );
+ printf( " recvUnicastCounter = %d\n", item->recvUnicastCounter );
+ printf( " recvErrorCounter = %d\n", item->recvErrorCounter );
+ printf( " recvLoopCounter = %d\n", item->recvLoopCounter );
+ printf( "\n" );
+ ++n;
+ }
+
+ // Resource Records
+
+ if( inShowRecords )
+ {
+ mDNSShowRecords();
+ }
+}
+
+//===========================================================================================================================
+// mDNSShowRecords
+//===========================================================================================================================
+
+void mDNSShowRecords( void )
+{
+ MDNSInterfaceItem * item;
+ int n;
+ AuthRecord * record;
+ char name[ 512 ];
+
+ printf( "\n-- mDNS resource records --\n" );
+ n = 1;
+ for( record = gMDNSPtr->ResourceRecords; record; record = record->next )
+ {
+ item = (MDNSInterfaceItem *) record->resrec.InterfaceID;
+ ConvertDomainNameToCString( &record->resrec.name, name );
+ printf( " -- record %d --\n", n );
+ printf( " interface = 0x%08X (%s)\n", (int) item, item ? item->name : "<any>" );
+ printf( " name = \"%s\"\n", name );
+ printf( "\n" );
+ ++n;
+ }
+ printf( "\n");
+}
+
+//===========================================================================================================================
+// mDNSShowTXT
+//===========================================================================================================================
+
+void mDNSShowTXT( const void *inTXT, size_t inTXTSize )
+{
+ const mDNSu8 * p;
+ const mDNSu8 * end;
+ int i;
+ mDNSu8 size;
+
+ printf( "\nTXT record (%u bytes):\n\n", inTXTSize );
+
+ p = (const mDNSu8 *) inTXT;
+ end = p + inTXTSize;
+ i = 0;
+
+ while( p < end )
+ {
+ size = *p++;
+ if( ( p + size ) > end )
+ {
+ printf( "\n### MALFORMED TXT RECORD (length byte too big for record)\n\n" );
+ break;
+ }
+ printf( "%2d (%3d bytes): \"%.*s\"\n", i, size, size, p );
+ p += size;
+ ++i;
+ }
+ printf( "\n" );
+}
+#endif // DEBUG
--- /dev/null
+/*
+ * 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@
+
+ Contains: mDNS platform plugin for VxWorks.
+
+ Copyright: Copyright (C) 2002-2003 Apple Computer, Inc., All Rights Reserved.
+
+ Change History (most recent first):
+
+$Log: mDNSVxWorks.h,v $
+Revision 1.2 2003/08/12 19:56:27 cheshire
+Update to APSL 2.0
+
+Revision 1.1 2003/08/02 10:06:49 bradley
+mDNS platform plugin for VxWorks.
+
+*/
+
+#ifndef __MDNS_VXWORKS__
+#define __MDNS_VXWORKS__
+
+#include "vxWorks.h"
+#include "semLib.h"
+
+#include "mDNSClientAPI.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+// Forward Declarations
+
+typedef struct MDNSInterfaceItem MDNSInterfaceItem;
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @struct mDNS_PlatformSupport_struct
+
+ @abstract Structure containing platform-specific data.
+*/
+
+struct mDNS_PlatformSupport_struct
+{
+ SEM_ID lockID;
+ SEM_ID readyEvent;
+ mStatus taskInitErr;
+ SEM_ID quitEvent;
+ MDNSInterfaceItem * interfaceList;
+ int commandPipe;
+ int task;
+ mDNSBool quit;
+ long configID;
+ int rescheduled;
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function mDNSReconfigure
+
+ @abstract Tell mDNS that the configuration has changed. Call when IP address changes, link goes up after being down, etc.
+
+ @discussion
+
+ VxWorks does not provide a generic mechanism for getting notified when network interfaces change so this routines
+ provides a way for BSP-specific code to signal mDNS that something has changed and it should re-build its interfaces.
+*/
+
+void mDNSReconfigure( void );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @struct ifaddrs
+
+ @abstract Interface information
+*/
+
+struct ifaddrs
+{
+ struct ifaddrs * ifa_next;
+ char * ifa_name;
+ u_int ifa_flags;
+ struct sockaddr * ifa_addr;
+ struct sockaddr * ifa_netmask;
+ struct sockaddr * ifa_dstaddr;
+ void * ifa_data;
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function getifaddrs
+
+ @abstract Builds a linked list of interfaces. Caller must free using freeifaddrs if successful.
+*/
+
+int getifaddrs( struct ifaddrs **outAddrs );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function freeifaddrs
+
+ @abstract Frees a linked list of interfaces built with getifaddrs.
+*/
+
+void freeifaddrs( struct ifaddrs *inAddrs );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function sock_pton
+
+ @abstract Converts a 'p'resentation address string into a 'n'umeric sockaddr structure.
+
+ @result 0 if successful or an error code on failure.
+*/
+
+int sock_pton( const char *inString, int inFamily, void *outAddr, size_t inAddrSize, size_t *outAddrSize );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function sock_ntop
+
+ @abstract Converts a 'n'umeric sockaddr structure into a 'p'resentation address string.
+
+ @result Ptr to 'p'resentation address string buffer if successful or NULL on failure.
+*/
+
+char * sock_ntop( const void *inAddr, size_t inAddrSize, char *inBuffer, size_t inBufferSize );
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif // __MDNS_VXWORKS__
--- /dev/null
+Microsoft Visual Studio Solution File, Format Version 7.00
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Application", "Application.vcproj", "{EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}"
+EndProject
+Global
+ GlobalSection(SolutionConfiguration) = preSolution
+ ConfigName.0 = Debug
+ ConfigName.1 = Release
+ EndGlobalSection
+ GlobalSection(ProjectDependencies) = postSolution
+ EndGlobalSection
+ GlobalSection(ProjectConfiguration) = postSolution
+ {EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}.Debug.ActiveCfg = Debug|Win32
+ {EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}.Debug.Build.0 = Debug|Win32
+ {EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}.Release.ActiveCfg = Release|Win32
+ {EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}.Release.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ EndGlobalSection
+ GlobalSection(ExtensibilityAddIns) = postSolution
+ EndGlobalSection
+EndGlobal
--- /dev/null
+<?xml version="1.0" encoding = "Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.00"
+ Name="Application"
+ ProjectGUID="{EDE4B529-4CF5-4a49-9B6F-C10F0EA24278}"
+ Keyword="MFCProj">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="Debug"
+ IntermediateDirectory="Debug"
+ ConfigurationType="1"
+ UseOfMFC="1"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=".\Resources;..\;..\..\..\..\mDNSCore;..\..\..\mDNSWindows;..\..\..\DNSServices"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;DEBUG"
+ StringPooling="TRUE"
+ MinimalRebuild="FALSE"
+ BasicRuntimeChecks="3"
+ SmallerTypeCheck="FALSE"
+ RuntimeLibrary="1"
+ BufferSecurityCheck="TRUE"
+ EnableFunctionLevelLinking="FALSE"
+ ForceConformanceInForLoopScope="TRUE"
+ RuntimeTypeInfo="TRUE"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="StdAfx.h"
+ PrecompiledHeaderFile=".\Debug/Application.pch"
+ AssemblerListingLocation=".\Debug/"
+ ObjectFile=".\Debug/"
+ ProgramDataBaseFileName=".\Debug/"
+ BrowseInformation="1"
+ WarningLevel="4"
+ WarnAsError="TRUE"
+ SuppressStartupBanner="TRUE"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="3"
+ CompileAs="0"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/MACHINE:I386"
+ AdditionalDependencies="ws2_32.lib nafxcwd.lib LIBCMTD.lib"
+ OutputFile="Rendezvous Browser Debug.exe"
+ LinkIncremental="1"
+ SuppressStartupBanner="TRUE"
+ IgnoreDefaultLibraryNames="wsock32.lib;nafxcwd.lib;LIBCMTD.lib"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile=".\Debug/Application.pdb"
+ SubSystem="2"/>
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="_DEBUG"
+ MkTypLibCompatible="FALSE"
+ SuppressStartupBanner="TRUE"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Debug/Application.tlb"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_AFXDLL;_DEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory=".\Release"
+ IntermediateDirectory=".\Release"
+ ConfigurationType="1"
+ UseOfMFC="1"
+ CharacterSet="2"
+ WholeProgramOptimization="FALSE">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ GlobalOptimizations="TRUE"
+ InlineFunctionExpansion="0"
+ FavorSizeOrSpeed="2"
+ OmitFramePointers="FALSE"
+ OptimizeForWindowsApplication="FALSE"
+ AdditionalIncludeDirectories=".\Resources;..\;..\..\..\..\mDNSCore;..\..\..\mDNSWindows;..\..\..\DNSServices"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"
+ StringPooling="TRUE"
+ MinimalRebuild="FALSE"
+ RuntimeLibrary="0"
+ BufferSecurityCheck="FALSE"
+ EnableFunctionLevelLinking="FALSE"
+ ForceConformanceInForLoopScope="TRUE"
+ RuntimeTypeInfo="TRUE"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="stdafx.h"
+ PrecompiledHeaderFile=".\Release/Application.pch"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ BrowseInformation="1"
+ WarningLevel="4"
+ WarnAsError="TRUE"
+ SuppressStartupBanner="TRUE"
+ Detect64BitPortabilityProblems="TRUE"
+ CompileAs="0"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/MACHINE:I386"
+ AdditionalDependencies="ws2_32.lib nafxcw.lib LIBCMT.lib"
+ OutputFile="Rendezvous Browser.exe"
+ LinkIncremental="1"
+ SuppressStartupBanner="TRUE"
+ IgnoreDefaultLibraryNames="wsock32.lib;nafxcw.lib;LIBCMT.lib"
+ ProgramDatabaseFile=".\Release/Application.pdb"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"/>
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="TRUE"
+ SuppressStartupBanner="TRUE"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release/Application.tlb"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_AFXDLL;NDEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ </Configuration>
+ <Configuration
+ Name="All|Win32"
+ OutputDirectory="All"
+ IntermediateDirectory="All"
+ ConfigurationType="1">
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ </Configuration>
+ </Configurations>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="">
+ <File
+ RelativePath="Sources\AboutDialog.cpp">
+ </File>
+ <File
+ RelativePath="Sources\AboutDialog.h">
+ </File>
+ <File
+ RelativePath="Sources\Application.cpp">
+ </File>
+ <File
+ RelativePath="Sources\Application.h">
+ </File>
+ <File
+ RelativePath="Sources\ChooserDialog.cpp">
+ </File>
+ <File
+ RelativePath="Sources\ChooserDialog.h">
+ </File>
+ <File
+ RelativePath="Sources\StdAfx.cpp">
+ </File>
+ <File
+ RelativePath="Sources\StdAfx.h">
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;rc">
+ <File
+ RelativePath="Resources\Application.ico">
+ </File>
+ <File
+ RelativePath="Resources\Application.rc">
+ </File>
+ <File
+ RelativePath="Resources\Application.rc2">
+ </File>
+ <File
+ RelativePath=".\Resources\Resource.h">
+ </File>
+ </Filter>
+ <Filter
+ Name="Rendezvous"
+ Filter="">
+ <File
+ RelativePath="..\..\..\DNSServices\DNSServices.c">
+ </File>
+ <File
+ RelativePath="..\..\..\..\mDNSCore\mDNS.c">
+ </File>
+ <File
+ RelativePath="..\..\..\mDNSWin32.c">
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
--- /dev/null
+// Microsoft Visual C++ generated resource script.
+//
+#include "Resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.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"
+ "\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"
+ "#ifdef _WIN32\r\n"
+ "LANGUAGE 9, 1\r\n"
+ "#pragma code_page(1252)\r\n"
+ "#endif //_WIN32\r\n"
+ "#include ""Resources\\Application.rc2"" // non-Microsoft Visual C++ edited resources\r\n"
+ "#include ""afxres.rc"" // Standard components\r\n"
+ "#endif\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDR_MAIN_ICON ICON "Application.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_CHOOSER_DIALOG DIALOGEX 0, 0, 332, 252
+STYLE DS_SETFONT | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION |
+ WS_SYSMENU
+EXSTYLE WS_EX_APPWINDOW
+CAPTION "Rendezvous Browser for Windows"
+MENU IDR_CHOOSER_DIALOG_MENU
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ CONTROL "",IDC_CHOOSER_LIST,"SysListView32",LVS_REPORT |
+ LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT |
+ LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,116,8,208,136
+ CONTROL "",IDC_SERVICE_LIST,"SysListView32",LVS_REPORT |
+ LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT |
+ LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,8,8,100,136
+ CONTROL "",IDC_DOMAIN_LIST,"SysListView32",LVS_REPORT |
+ LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT |
+ LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,8,152,100,92
+ GROUPBOX "Information",IDC_STATIC,116,148,208,96
+ RTEXT "Name:",IDC_STATIC,122,164,38,8
+ LTEXT "My Device",IDC_INFO_NAME_TEXT,164,164,152,10,SS_SUNKEN
+ RTEXT "Text:",IDC_STATIC,122,203,38,8
+ LTEXT "Information About My Device",IDC_INFO_TEXT_TEXT,164,203,
+ 152,34,SS_NOPREFIX | SS_SUNKEN
+ RTEXT "IP address:",IDC_STATIC,122,177,38,8
+ LTEXT "123.124.125.126:1234",IDC_INFO_IP_TEXT,164,177,152,10,
+ SS_SUNKEN
+ RTEXT "Interface:",IDC_STATIC,122,190,38,8
+ LTEXT "123.124.125.126",IDC_INFO_INTERFACE_TEXT,164,190,152,10,
+ SS_SUNKEN
+END
+
+IDD_ABOUT_DIALOG DIALOGEX 0, 0, 244, 73
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION
+CAPTION "About Rendezvous Browser"
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ ICON IDR_MAIN_ICON,IDC_ABOUT_APP_ICON,12,12,20,20
+ LTEXT "Rendezvous Browser for Windows",IDC_ABOUT_APP_NAME_TEXT,
+ 44,11,192,12
+ LTEXT "Version 1.0d2",IDC_ABOUT_APP_VERSION_TEXT,44,25,192,8
+ LTEXT "Copyright (C) 2002-2003 Apple Computer, Inc.",
+ IDC_ABOUT_COPYRIGHT_TEXT,4,60,156,8
+ DEFPUSHBUTTON "OK",IDOK,192,52,44,14
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,1
+ PRODUCTVERSION 1,0,0,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "Comments", "Quick & Dirty 1 day hack by Bob Bradley"
+ VALUE "CompanyName", "Apple Computer, Inc."
+ VALUE "FileDescription", "Rendezvous Browser for Windows"
+ VALUE "FileVersion", "1, 0, 0, 1"
+ VALUE "InternalName", "Rendezvous Browser for Windows"
+ VALUE "LegalCopyright", "Copyright (C) Apple Computer, Inc. 2001"
+ VALUE "OriginalFilename", "RendezvousBrowser.exe"
+ VALUE "ProductName", "Rendezvous Browser for Windows"
+ VALUE "ProductVersion", "1, 0, 0, 1"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_CHOOSER_DIALOG_MENU MENU
+BEGIN
+ POPUP "File"
+ BEGIN
+ MENUITEM "Close &Window\tCtrl+W", ID_FILE_CLOSE
+ MENUITEM SEPARATOR
+ MENUITEM "Exit", ID_FILE_EXIT
+ END
+ POPUP "Help"
+ BEGIN
+ MENUITEM "About Rendezvous Browser...", ID_HELP_ABOUT
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Accelerator
+//
+
+IDR_CHOOSER_DIALOG_MENU_ACCELERATORS ACCELERATORS
+BEGIN
+ "S", ID_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT
+ "W", ID_FILE_CLOSE, VIRTKEY, CONTROL, NOINVERT
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// RT_MANIFEST
+//
+
+1 RT_MANIFEST
+BEGIN
+ 0x3f3c, 0x6d78, 0x206c, 0x6576, 0x7372, 0x6f69, 0x3d6e, 0x3122, 0x302e,
+ 0x2022, 0x6e65, 0x6f63, 0x6964, 0x676e, 0x223d, 0x5455, 0x2d46, 0x2238,
+ 0x7320, 0x6174, 0x646e, 0x6c61, 0x6e6f, 0x3d65, 0x7922, 0x7365, 0x3f22,
+ 0x203e, 0x0a0d, 0x613c, 0x7373, 0x6d65, 0x6c62, 0x2079, 0x0a0d, 0x2020,
+ 0x7820, 0x6c6d, 0x736e, 0x223d, 0x7275, 0x3a6e, 0x6373, 0x6568, 0x616d,
+ 0x2d73, 0x696d, 0x7263, 0x736f, 0x666f, 0x2d74, 0x6f63, 0x3a6d, 0x7361,
+ 0x2e6d, 0x3176, 0x2022, 0x0a0d, 0x2020, 0x6d20, 0x6e61, 0x6669, 0x7365,
+ 0x5674, 0x7265, 0x6973, 0x6e6f, 0x223d, 0x2e31, 0x2230, 0x0d3e, 0x3c0a,
+ 0x7361, 0x6573, 0x626d, 0x796c, 0x6449, 0x6e65, 0x6974, 0x7974, 0x0d20,
+ 0x200a, 0x2020, 0x7020, 0x6f72, 0x6563, 0x7373, 0x726f, 0x7241, 0x6863,
+ 0x7469, 0x6365, 0x7574, 0x6572, 0x223d, 0x3878, 0x2236, 0x0d20, 0x200a,
+ 0x2020, 0x7620, 0x7265, 0x6973, 0x6e6f, 0x223d, 0x2e35, 0x2e31, 0x2e30,
+ 0x2230, 0x0a0d, 0x2020, 0x2020, 0x7974, 0x6570, 0x223d, 0x6977, 0x336e,
+ 0x2232, 0x0a0d, 0x2020, 0x2020, 0x616e, 0x656d, 0x223d, 0x7041, 0x2e70,
+ 0x7865, 0x2265, 0x3e2f, 0x0a0d, 0x2020, 0x2020, 0x643c, 0x7365, 0x7263,
+ 0x7069, 0x6974, 0x6e6f, 0x413e, 0x7269, 0x6f50, 0x7472, 0x4120, 0x6d64,
+ 0x6e69, 0x5520, 0x6974, 0x696c, 0x7974, 0x2f3c, 0x6564, 0x6373, 0x6972,
+ 0x7470, 0x6f69, 0x3e6e, 0x0a0d, 0x2020, 0x2020, 0x643c, 0x7065, 0x6e65,
+ 0x6564, 0x636e, 0x3e79, 0x0a0d, 0x2020, 0x2020, 0x643c, 0x7065, 0x6e65,
+ 0x6564, 0x746e, 0x7341, 0x6573, 0x626d, 0x796c, 0x0d3e, 0x200a, 0x2020,
+ 0x3c20, 0x7361, 0x6573, 0x626d, 0x796c, 0x6449, 0x6e65, 0x6974, 0x7974,
+ 0x0a0d, 0x2020, 0x2020, 0x2020, 0x2020, 0x7420, 0x7079, 0x3d65, 0x7722,
+ 0x6e69, 0x3233, 0x0d22, 0x200a, 0x2020, 0x2020, 0x2020, 0x2020, 0x616e,
+ 0x656d, 0x223d, 0x694d, 0x7263, 0x736f, 0x666f, 0x2e74, 0x6957, 0x646e,
+ 0x776f, 0x2e73, 0x6f43, 0x6d6d, 0x6e6f, 0x432d, 0x6e6f, 0x7274, 0x6c6f,
+ 0x2273, 0x0a0d, 0x2020, 0x2020, 0x2020, 0x2020, 0x7620, 0x7265, 0x6973,
+ 0x6e6f, 0x223d, 0x2e36, 0x2e30, 0x2e30, 0x2230, 0x0a0d, 0x2020, 0x2020,
+ 0x2020, 0x2020, 0x7020, 0x6275, 0x696c, 0x4b63, 0x7965, 0x6f54, 0x656b,
+ 0x3d6e, 0x3622, 0x3935, 0x6235, 0x3436, 0x3431, 0x6334, 0x6663, 0x6431,
+ 0x2266, 0x0a0d, 0x2020, 0x2020, 0x2020, 0x2020, 0x6c20, 0x6e61, 0x7567,
+ 0x6761, 0x3d65, 0x2a22, 0x0d22, 0x200a, 0x2020, 0x2020, 0x2020, 0x2020,
+ 0x7270, 0x636f, 0x7365, 0x6f73, 0x4172, 0x6372, 0x6968, 0x6574, 0x7463,
+ 0x7275, 0x3d65, 0x7822, 0x3638, 0x2f22, 0x0d3e, 0x200a, 0x2020, 0x3c20,
+ 0x642f, 0x7065, 0x6e65, 0x6564, 0x746e, 0x7341, 0x6573, 0x626d, 0x796c,
+ 0x0d3e, 0x200a, 0x2020, 0x3c20, 0x642f, 0x7065, 0x6e65, 0x6564, 0x636e,
+ 0x3e79, 0x0a0d, 0x2f3c, 0x7361, 0x6573, 0x626d, 0x796c, 0x0d3e, "\012"
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_ABOUTBOX "&About Rendezvous Browser"
+ IDS_CHOOSER_DOMAIN_COLUMN_NAME "Domains"
+ IDP_SOCKETS_INIT_FAILED "Windows sockets initialization failed."
+ IDS_CHOOSER_SERVICE_COLUMN_NAME "Services"
+ IDS_CHOOSER_CHOOSER_NAME_COLUMN_NAME "Name"
+ IDS_CHOOSER_CHOOSER_IP_COLUMN_NAME "IP Address"
+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)
+#ifdef _WIN32
+LANGUAGE 9, 1
+#pragma code_page(1252)
+#endif //_WIN32
+#include "Resources\Application.rc2" // non-Microsoft Visual C++ edited resources
+#include "afxres.rc" // Standard components
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
--- /dev/null
+/*
+ * 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: Application.rc2,v $
+Revision 1.1 2003/08/21 02:06:46 bradley
+Moved Rendezvous Browser for non-Windows CE into Windows sub-folder.
+
+Revision 1.2 2003/08/20 07:06:34 bradley
+Update to APSL 2.0. Updated change history to match other mDNSResponder files.
+
+Revision 1.1 2002/09/20 06:12:48 bradley
+Rendezvous Browser for Windows
+
+*/
+
+#ifdef APSTUDIO_INVOKED
+ #error this file is not editable by Microsoft Visual C++
+#endif //APSTUDIO_INVOKED
--- /dev/null
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by Application.rc
+//
+#define IDS_ABOUTBOX 101
+#define IDS_CHOOSER_DOMAIN_COLUMN_NAME 102
+#define IDP_SOCKETS_INIT_FAILED 103
+#define IDS_CHOOSER_SERVICE_COLUMN_NAME 104
+#define IDS_CHOOSER_CHOOSER_NAME_COLUMN_NAME 105
+#define IDS_CHOOSER_CHOOSER_IP_COLUMN_NAME 106
+#define IDC_NAME_TEXT2 124
+#define IDC_INFO_NAME_TEXT 124
+#define IDC_DESCRIPTION_TEXT2 125
+#define IDC_INFO_TEXT_TEXT 125
+#define IDC_IP_TEXT2 126
+#define IDC_INFO_IP_TEXT 126
+#define IDC_IP_TEXT3 127
+#define IDC_INFO_INTERFACE_TEXT 127
+#define IDR_MAIN_ICON 128
+#define IDR_CHOOSER_DIALOG_MENU 136
+#define IDD_CHOOSER_DIALOG 143
+#define IDD_ABOUT_DIALOG 144
+#define IDR_CHOOSER_DIALOG_MENU_ACCELERATORS 146
+#define IDC_CHOOSER_LIST 1000
+#define IDC_SERVICE_LIST2 1001
+#define IDC_SERVICE_LIST 1001
+#define IDC_SERVICE_LIST3 1002
+#define IDC_DOMAIN_LIST 1002
+#define IDC_ABOUT_APP_NAME_TEXT 1105
+#define IDC_ABOUT_APP_VERSION_TEXT 1106
+#define IDC_ABOUT_COPYRIGHT_TEXT 1107
+#define IDC_ABOUT_APP_ICON 1108
+#define ID_FILE_EXIT 32771
+#define ID_HELP_ABOUT 32806
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 164
+#define _APS_NEXT_COMMAND_VALUE 32809
+#define _APS_NEXT_CONTROL_VALUE 1182
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
--- /dev/null
+/*
+ * 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: AboutDialog.cpp,v $
+Revision 1.1 2003/08/21 02:06:47 bradley
+Moved Rendezvous Browser for non-Windows CE into Windows sub-folder.
+
+Revision 1.4 2003/08/12 19:56:28 cheshire
+Update to APSL 2.0
+
+Revision 1.3 2003/07/02 21:20:06 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.2 2002/09/21 20:44:55 zarzycki
+Added APSL info
+
+Revision 1.1 2002/09/20 06:12:49 bradley
+Rendezvous Browser for Windows
+
+*/
+
+#include <stdlib.h>
+
+#include "stdafx.h"
+
+#include "AboutDialog.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+//===========================================================================================================================
+// Message Map
+//===========================================================================================================================
+
+BEGIN_MESSAGE_MAP(AboutDialog, CDialog)
+ //{{AFX_MSG_MAP(AboutDialog)
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+//===========================================================================================================================
+// AboutDialog
+//===========================================================================================================================
+
+AboutDialog::AboutDialog(CWnd* pParent /*=NULL*/)
+ : CDialog(AboutDialog::IDD, pParent)
+{
+ //{{AFX_DATA_INIT(AboutDialog)
+ // NOTE: the ClassWizard will add member initialization here
+ //}}AFX_DATA_INIT
+}
+
+//===========================================================================================================================
+// OnInitDialog
+//===========================================================================================================================
+
+BOOL AboutDialog::OnInitDialog()
+{
+ CDialog::OnInitDialog();
+ return( true );
+}
+
+//===========================================================================================================================
+// DoDataExchange
+//===========================================================================================================================
+
+void AboutDialog::DoDataExchange(CDataExchange* pDX)
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(AboutDialog)
+ // NOTE: the ClassWizard will add DDX and DDV calls here
+ //}}AFX_DATA_MAP
+}
--- /dev/null
+/*
+ * 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: AboutDialog.h,v $
+Revision 1.1 2003/08/21 02:06:47 bradley
+Moved Rendezvous Browser for non-Windows CE into Windows sub-folder.
+
+Revision 1.4 2003/08/12 19:56:28 cheshire
+Update to APSL 2.0
+
+Revision 1.3 2003/07/02 21:20:06 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.2 2002/09/21 20:44:55 zarzycki
+Added APSL info
+
+Revision 1.1 2002/09/20 06:12:50 bradley
+Rendezvous Browser for Windows
+
+*/
+
+#if !defined(AFX_ABOUTDIALOG_H__4B8A04B2_9735_4F4A_AFCA_15F85FB3D763__INCLUDED_)
+#define AFX_ABOUTDIALOG_H__4B8A04B2_9735_4F4A_AFCA_15F85FB3D763__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "Resource.h"
+
+//===========================================================================================================================
+// AboutDialog
+//===========================================================================================================================
+
+class AboutDialog : public CDialog
+{
+ public:
+
+ // Creation/Deletion
+
+ AboutDialog(CWnd* pParent = NULL); // standard constructor
+
+ //{{AFX_DATA(AboutDialog)
+ enum { IDD = IDD_ABOUT_DIALOG };
+ // NOTE: the ClassWizard will add data members here
+ //}}AFX_DATA
+
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(AboutDialog)
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ //}}AFX_VIRTUAL
+
+ protected:
+
+ // Generated message map functions
+ //{{AFX_MSG(AboutDialog)
+ virtual BOOL OnInitDialog();
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_ABOUTDIALOG_H__4B8A04B2_9735_4F4A_AFCA_15F85FB3D763__INCLUDED_)
--- /dev/null
+/*
+ * 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: Application.cpp,v $
+Revision 1.1 2003/08/21 02:06:47 bradley
+Moved Rendezvous Browser for non-Windows CE into Windows sub-folder.
+
+Revision 1.5 2003/08/12 19:56:28 cheshire
+Update to APSL 2.0
+
+Revision 1.4 2003/07/02 21:20:06 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.3 2002/09/21 20:44:55 zarzycki
+Added APSL info
+
+Revision 1.2 2002/09/20 08:37:34 bradley
+Increased the DNS record cache from the default of 64 to 512 entries for larger networks.
+
+Revision 1.1 2002/09/20 06:12:51 bradley
+Rendezvous Browser for Windows
+
+*/
+
+#include <assert.h>
+
+#include "DNSServices.h"
+
+#include "Application.h"
+
+#include "ChooserDialog.h"
+
+#include "stdafx.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+//===========================================================================================================================
+// Message Map
+//===========================================================================================================================
+
+BEGIN_MESSAGE_MAP(Application, CWinApp)
+ //{{AFX_MSG_MAP(Application)
+ // NOTE - the ClassWizard will add and remove mapping macros here.
+ // DO NOT EDIT what you see in these blocks of generated code!
+ //}}AFX_MSG
+ ON_COMMAND(ID_HELP, CWinApp::OnHelp)
+END_MESSAGE_MAP()
+
+//===========================================================================================================================
+// Globals
+//===========================================================================================================================
+
+Application gApp;
+
+//===========================================================================================================================
+// Application
+//===========================================================================================================================
+
+Application::Application( void )
+{
+ //
+}
+
+//===========================================================================================================================
+// InitInstance
+//===========================================================================================================================
+
+BOOL Application::InitInstance()
+{
+ DNSStatus err;
+
+ // WinSock initialization.
+
+ if( !AfxSocketInit() )
+ {
+ AfxMessageBox( IDP_SOCKETS_INIT_FAILED );
+ return( FALSE );
+ }
+
+ // Standard MFC initialization.
+
+#if( !defined( AFX_DEPRECATED ) )
+ #ifdef _AFXDLL
+ Enable3dControls(); // Call this when using MFC in a shared DLL
+ #else
+ Enable3dControlsStatic(); // Call this when linking to MFC statically
+ #endif
+#endif
+
+ InitCommonControls();
+
+ // Set up DNS Services.
+
+ err = DNSServicesInitialize( 0, 512 );
+ assert( err == kDNSNoErr );
+
+ // Create the chooser dialog.
+
+ ChooserDialog * dialog;
+
+ m_pMainWnd = NULL;
+ dialog = new ChooserDialog;
+ dialog->Create( IDD_CHOOSER_DIALOG );
+ m_pMainWnd = dialog;
+ dialog->ShowWindow( SW_SHOW );
+
+ return( true );
+}
+
+//===========================================================================================================================
+// ExitInstance
+//===========================================================================================================================
+
+int Application::ExitInstance( void )
+{
+ // Clean up DNS Services.
+
+ DNSServicesFinalize();
+ return( 0 );
+}
--- /dev/null
+/*
+ * 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: Application.h,v $
+Revision 1.1 2003/08/21 02:06:47 bradley
+Moved Rendezvous Browser for non-Windows CE into Windows sub-folder.
+
+Revision 1.4 2003/08/12 19:56:28 cheshire
+Update to APSL 2.0
+
+Revision 1.3 2003/07/02 21:20:06 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.2 2002/09/21 20:44:55 zarzycki
+Added APSL info
+
+Revision 1.1 2002/09/20 06:12:51 bradley
+Rendezvous Browser for Windows
+
+*/
+
+#if !defined(AFX_ADMIN_H__8663733F_6A15_439F_B568_F5A0125CD572__INCLUDED_)
+#define AFX_ADMIN_H__8663733F_6A15_439F_B568_F5A0125CD572__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "stdafx.h"
+
+#ifndef __AFXWIN_H__
+ #error include 'stdafx.h' before including this file for PCH
+#endif
+
+#include "Resource.h"
+
+//===========================================================================================================================
+// Globals
+//===========================================================================================================================
+
+extern class Application gApp;
+
+//===========================================================================================================================
+// Application
+//===========================================================================================================================
+
+class Application : public CWinApp
+{
+ public:
+
+ // Creation/Deletion
+
+ Application();
+
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(Application)
+ public:
+ virtual BOOL InitInstance();
+ virtual int ExitInstance( void );
+ //}}AFX_VIRTUAL
+
+ //{{AFX_MSG(Application)
+ // NOTE - the ClassWizard will add and remove member functions here.
+ // DO NOT EDIT what you see in these blocks of generated code !
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_ADMIN_H__8663733F_6A15_439F_B568_F5A0125CD572__INCLUDED_)
--- /dev/null
+/*
+ * 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: ChooserDialog.cpp,v $
+Revision 1.1 2003/08/21 02:06:47 bradley
+Moved Rendezvous Browser for non-Windows CE into Windows sub-folder.
+
+Revision 1.7 2003/08/20 06:45:56 bradley
+Updated for IP address changes in DNSServices. Added support for browsing for Xserve RAID.
+
+Revision 1.6 2003/08/12 19:56:28 cheshire
+Update to APSL 2.0
+
+Revision 1.5 2003/07/13 01:03:55 cheshire
+Diffs provided by Bob Bradley to provide provide proper display of Unicode names
+
+Revision 1.4 2003/07/02 21:20:06 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.3 2002/09/21 20:44:55 zarzycki
+Added APSL info
+
+Revision 1.2 2002/09/20 08:39:21 bradley
+Make sure each resolved item matches the selected service type to handle resolved that may have
+been queued up on the Windows Message Loop. Reduce column to fit when scrollbar is present.
+
+Revision 1.1 2002/09/20 06:12:52 bradley
+Rendezvous Browser for Windows
+
+*/
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <algorithm>
+#include <memory>
+
+#include "stdafx.h"
+
+#include "DNSServices.h"
+
+#include "Application.h"
+#include "AboutDialog.h"
+#include "Resource.h"
+
+#include "ChooserDialog.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+#if 0
+#pragma mark == Constants ==
+#endif
+
+//===========================================================================================================================
+// Constants
+//===========================================================================================================================
+
+// Menus
+
+enum
+{
+ kChooserMenuIndexFile = 0,
+ kChooserMenuIndexHelp = 1
+};
+
+// Domain List
+
+#define kDomainListDefaultDomainColumnIndex 0
+#define kDomainListDefaultDomainColumnWidth 140
+
+// Service List
+
+#define kServiceListDefaultServiceColumnIndex 0
+#define kServiceListDefaultServiceColumnWidth 140
+
+// Chooser List
+
+#define kChooserListDefaultNameColumnIndex 0
+#define kChooserListDefaultNameColumnWidth 162
+
+#define kChooserListDefaultIPColumnIndex 1
+#define kChooserListDefaultIPColumnWidth 126
+
+// Windows User Messages
+
+#define WM_USER_DOMAIN_ADD ( WM_USER + 0x100 )
+#define WM_USER_DOMAIN_REMOVE ( WM_USER + 0x101 )
+#define WM_USER_SERVICE_ADD ( WM_USER + 0x102 )
+#define WM_USER_SERVICE_REMOVE ( WM_USER + 0x103 )
+#define WM_USER_RESOLVE ( WM_USER + 0x104 )
+
+#if 0
+#pragma mark == Constants - Service Table ==
+#endif
+
+//===========================================================================================================================
+// Constants - Service Table
+//===========================================================================================================================
+
+struct KnownServiceEntry
+{
+ const char * serviceType;
+ const char * description;
+ const char * urlScheme;
+ bool useText;
+};
+
+static const KnownServiceEntry kKnownServiceTable[] =
+{
+ { "_airport._tcp.", "AirPort Base Station", "acp://", false },
+ { "_afpovertcp._tcp.", "AppleShare Server", "afp://", false },
+ { "_ftp._tcp.", "File Transfer (FTP)", "ftp://", false },
+ { "_ichat._tcp.", "iChat", "ichat://", false },
+ { "_printer._tcp.", "Printer (LPD)", "ldp://", false },
+ { "_eppc._tcp.", "Remote AppleEvents", "eppc://", false },
+ { "_ssh._tcp.", "Secure Shell (SSH)", "ssh://", false },
+ { "_tftp._tcp.", "Trivial File Transfer (TFTP)", "tftp://", false },
+ { "_http._tcp.", "Web Server (HTTP)", "http://", true },
+ { "_smb._tcp.", "Windows File Sharing", "smb://", false },
+ { "_xserveraid._tcp.", "Xserve RAID", "xsr://", false },
+ { NULL, NULL, NULL, false },
+};
+
+#if 0
+#pragma mark == Structures ==
+#endif
+
+//===========================================================================================================================
+// Structures
+//===========================================================================================================================
+
+struct DomainEventInfo
+{
+ DNSBrowserEventType eventType;
+ CString domain;
+ DNSNetworkAddress ifIP;
+};
+
+struct ServiceEventInfo
+{
+ DNSBrowserEventType eventType;
+ std::string name;
+ std::string type;
+ std::string domain;
+ DNSNetworkAddress ifIP;
+};
+
+#if 0
+#pragma mark == Prototypes ==
+#endif
+
+//===========================================================================================================================
+// Prototypes
+//===========================================================================================================================
+
+static void
+ BrowserCallBack(
+ void * inContext,
+ DNSBrowserRef inRef,
+ DNSStatus inStatusCode,
+ const DNSBrowserEvent * inEvent );
+
+static char * DNSNetworkAddressToString( const DNSNetworkAddress *inAddr, char *outString );
+
+static DWORD UTF8StringToStringObject( const char *inUTF8, CString &inObject );
+
+#if 0
+#pragma mark == Message Map ==
+#endif
+
+//===========================================================================================================================
+// Message Map
+//===========================================================================================================================
+
+BEGIN_MESSAGE_MAP(ChooserDialog, CDialog)
+ //{{AFX_MSG_MAP(ChooserDialog)
+ ON_WM_SYSCOMMAND()
+ ON_NOTIFY(LVN_ITEMCHANGED, IDC_DOMAIN_LIST, OnDomainListChanged)
+ ON_NOTIFY(LVN_ITEMCHANGED, IDC_SERVICE_LIST, OnServiceListChanged)
+ ON_NOTIFY(LVN_ITEMCHANGED, IDC_CHOOSER_LIST, OnChooserListChanged)
+ ON_NOTIFY(NM_DBLCLK, IDC_CHOOSER_LIST, OnChooserListDoubleClick)
+ ON_COMMAND(ID_HELP_ABOUT, OnAbout)
+ ON_WM_INITMENUPOPUP()
+ ON_WM_ACTIVATE()
+ ON_COMMAND(ID_FILE_CLOSE, OnFileClose)
+ ON_COMMAND(ID_FILE_EXIT, OnExit)
+ ON_WM_CLOSE()
+ ON_WM_NCDESTROY()
+ //}}AFX_MSG_MAP
+ ON_MESSAGE( WM_USER_DOMAIN_ADD, OnDomainAdd )
+ ON_MESSAGE( WM_USER_DOMAIN_REMOVE, OnDomainRemove )
+ ON_MESSAGE( WM_USER_SERVICE_ADD, OnServiceAdd )
+ ON_MESSAGE( WM_USER_SERVICE_REMOVE, OnServiceRemove )
+ ON_MESSAGE( WM_USER_RESOLVE, OnResolve )
+END_MESSAGE_MAP()
+
+#if 0
+#pragma mark == Routines ==
+#endif
+
+//===========================================================================================================================
+// ChooserDialog
+//===========================================================================================================================
+
+ChooserDialog::ChooserDialog( CWnd *inParent )
+ : CDialog( ChooserDialog::IDD, inParent)
+{
+ //{{AFX_DATA_INIT(ChooserDialog)
+ // NOTE: the ClassWizard will add member initialization here
+ //}}AFX_DATA_INIT
+
+ // Load menu accelerator table.
+
+ mMenuAcceleratorTable = ::LoadAccelerators( AfxGetInstanceHandle(), MAKEINTRESOURCE( IDR_CHOOSER_DIALOG_MENU_ACCELERATORS ) );
+ assert( mMenuAcceleratorTable );
+
+ mBrowser = NULL;
+ mIsServiceBrowsing = false;
+}
+
+//===========================================================================================================================
+// ~ChooserDialog
+//===========================================================================================================================
+
+ChooserDialog::~ChooserDialog( void )
+{
+ if( mBrowser )
+ {
+ DNSStatus err;
+
+ err = DNSBrowserRelease( mBrowser, 0 );
+ assert( err == kDNSNoErr );
+ }
+}
+
+//===========================================================================================================================
+// DoDataExchange
+//===========================================================================================================================
+
+void ChooserDialog::DoDataExchange( CDataExchange *pDX )
+{
+ CDialog::DoDataExchange(pDX);
+
+ //{{AFX_DATA_MAP(ChooserDialog)
+ DDX_Control(pDX, IDC_SERVICE_LIST, mServiceList);
+ DDX_Control(pDX, IDC_DOMAIN_LIST, mDomainList);
+ DDX_Control(pDX, IDC_CHOOSER_LIST, mChooserList);
+ //}}AFX_DATA_MAP
+}
+
+//===========================================================================================================================
+// OnInitDialog
+//===========================================================================================================================
+
+BOOL ChooserDialog::OnInitDialog( void )
+{
+ BOOL result;
+ CString tempString;
+ DNSStatus err;
+
+ // Initialize our parent.
+
+ CDialog::OnInitDialog();
+
+ // Set up the Domain List.
+
+ result = tempString.LoadString( IDS_CHOOSER_DOMAIN_COLUMN_NAME );
+ assert( result );
+ mDomainList.InsertColumn( 0, tempString, LVCFMT_LEFT, kDomainListDefaultDomainColumnWidth );
+
+ // Set up the Service List.
+
+ result = tempString.LoadString( IDS_CHOOSER_SERVICE_COLUMN_NAME );
+ assert( result );
+ mServiceList.InsertColumn( 0, tempString, LVCFMT_LEFT, kServiceListDefaultServiceColumnWidth );
+
+ PopulateServicesList();
+
+ // Set up the Chooser List.
+
+ result = tempString.LoadString( IDS_CHOOSER_CHOOSER_NAME_COLUMN_NAME );
+ assert( result );
+ mChooserList.InsertColumn( 0, tempString, LVCFMT_LEFT, kChooserListDefaultNameColumnWidth );
+
+ result = tempString.LoadString( IDS_CHOOSER_CHOOSER_IP_COLUMN_NAME );
+ assert( result );
+ mChooserList.InsertColumn( 1, tempString, LVCFMT_LEFT, kChooserListDefaultIPColumnWidth );
+
+ // Set up the other controls.
+
+ UpdateInfoDisplay();
+
+ // Start browsing for domains.
+
+ err = DNSBrowserCreate( 0, BrowserCallBack, this, &mBrowser );
+ assert( err == kDNSNoErr );
+
+ err = DNSBrowserStartDomainSearch( mBrowser, 0 );
+ assert( err == kDNSNoErr );
+
+ return( true );
+}
+
+//===========================================================================================================================
+// OnFileClose
+//===========================================================================================================================
+
+void ChooserDialog::OnFileClose()
+{
+ OnClose();
+}
+
+//===========================================================================================================================
+// OnActivate
+//===========================================================================================================================
+
+void ChooserDialog::OnActivate( UINT nState, CWnd* pWndOther, BOOL bMinimized )
+{
+ // Always make the active window the "main" window so modal dialogs work better and the app quits after closing
+ // the last window.
+
+ gApp.m_pMainWnd = this;
+
+ CDialog::OnActivate(nState, pWndOther, bMinimized);
+}
+
+//===========================================================================================================================
+// PostNcDestroy
+//===========================================================================================================================
+
+void ChooserDialog::PostNcDestroy()
+{
+ // Call the base class to do the normal cleanup.
+
+ delete this;
+}
+
+//===========================================================================================================================
+// PreTranslateMessage
+//===========================================================================================================================
+
+BOOL ChooserDialog::PreTranslateMessage(MSG* pMsg)
+{
+ BOOL result;
+
+ result = false;
+ assert( mMenuAcceleratorTable );
+ if( mMenuAcceleratorTable )
+ {
+ result = ::TranslateAccelerator( m_hWnd, mMenuAcceleratorTable, pMsg );
+ }
+ if( !result )
+ {
+ result = CDialog::PreTranslateMessage( pMsg );
+ }
+ return( result );
+}
+
+//===========================================================================================================================
+// OnInitMenuPopup
+//===========================================================================================================================
+
+void ChooserDialog::OnInitMenuPopup( CMenu *pPopupMenu, UINT nIndex, BOOL bSysMenu )
+{
+ CDialog::OnInitMenuPopup( pPopupMenu, nIndex, bSysMenu );
+
+ switch( nIndex )
+ {
+ case kChooserMenuIndexFile:
+ break;
+
+ case kChooserMenuIndexHelp:
+ break;
+
+ default:
+ break;
+ }
+}
+
+//===========================================================================================================================
+// OnExit
+//===========================================================================================================================
+
+void ChooserDialog::OnExit()
+{
+ AfxPostQuitMessage( 0 );
+}
+
+//===========================================================================================================================
+// OnAbout
+//===========================================================================================================================
+
+void ChooserDialog::OnAbout()
+{
+ AboutDialog dialog;
+
+ dialog.DoModal();
+}
+
+//===========================================================================================================================
+// OnSysCommand
+//===========================================================================================================================
+
+void ChooserDialog::OnSysCommand( UINT inID, LPARAM inParam )
+{
+ CDialog::OnSysCommand( inID, inParam );
+}
+
+//===========================================================================================================================
+// OnClose
+//===========================================================================================================================
+
+void ChooserDialog::OnClose()
+{
+ StopBrowsing();
+
+ gApp.m_pMainWnd = this;
+ DestroyWindow();
+}
+
+//===========================================================================================================================
+// OnNcDestroy
+//===========================================================================================================================
+
+void ChooserDialog::OnNcDestroy()
+{
+ gApp.m_pMainWnd = this;
+
+ CDialog::OnNcDestroy();
+}
+
+//===========================================================================================================================
+// OnDomainListChanged
+//===========================================================================================================================
+
+void ChooserDialog::OnDomainListChanged( NMHDR *pNMHDR, LRESULT *pResult )
+{
+ UNUSED_ALWAYS( pNMHDR );
+
+ // Domain list changes have similar effects to service list changes so reuse that code path by calling it here.
+
+ OnServiceListChanged( NULL, NULL );
+
+ *pResult = 0;
+}
+
+//===========================================================================================================================
+// OnServiceListChanged
+//===========================================================================================================================
+
+void ChooserDialog::OnServiceListChanged( NMHDR *pNMHDR, LRESULT *pResult )
+{
+ int selectedType;
+ int selectedDomain;
+
+ UNUSED_ALWAYS( pNMHDR );
+
+ // Stop any existing service search.
+
+ StopBrowsing();
+
+ // If a domain and service type are selected, start searching for the service type on the domain.
+
+ selectedType = mServiceList.GetNextItem( -1, LVNI_SELECTED );
+ selectedDomain = mDomainList.GetNextItem( -1, LVNI_SELECTED );
+
+ if( ( selectedType >= 0 ) && ( selectedDomain >= 0 ) )
+ {
+ CString type;
+ CString domain;
+
+ type = mServiceTypes[ selectedType ].serviceType.c_str();
+ domain = mDomainList.GetItemText( selectedDomain, 0 );
+
+ StartBrowsing( type, domain );
+ }
+
+ if( pResult )
+ {
+ *pResult = 0;
+ }
+}
+
+//===========================================================================================================================
+// OnChooserListChanged
+//===========================================================================================================================
+
+void ChooserDialog::OnChooserListChanged( NMHDR *pNMHDR, LRESULT *pResult )
+{
+ UNUSED_ALWAYS( pNMHDR );
+
+ UpdateInfoDisplay();
+ *pResult = 0;
+}
+
+//===========================================================================================================================
+// OnChooserListDoubleClick
+//===========================================================================================================================
+
+void ChooserDialog::OnChooserListDoubleClick( NMHDR *pNMHDR, LRESULT *pResult )
+{
+ int selectedItem;
+
+ UNUSED_ALWAYS( pNMHDR );
+
+ // Display the service instance if it is selected. Otherwise, clear all the info.
+
+ selectedItem = mChooserList.GetNextItem( -1, LVNI_SELECTED );
+ if( selectedItem >= 0 )
+ {
+ ServiceInstanceInfo * p;
+ CString url;
+ const KnownServiceEntry * service;
+
+ assert( selectedItem < (int) mServiceInstances.size() );
+ p = &mServiceInstances[ selectedItem ];
+
+ // Search for a known service type entry that matches.
+
+ for( service = kKnownServiceTable; service->serviceType; ++service )
+ {
+ if( p->type == service->serviceType )
+ {
+ break;
+ }
+ }
+ if( service->serviceType )
+ {
+ // Create a URL representing the service instance. Special case for SMB (no port number).
+
+ if( strcmp( service->serviceType, "_smb._tcp" ) == 0 )
+ {
+ url.Format( "%s%s/", service->urlScheme, (const char *) p->ip.c_str() );
+ }
+ else
+ {
+ const char * text;
+
+ text = service->useText ? p->text.c_str() : "";
+ url.Format( "%s%s/%s", service->urlScheme, (const char *) p->ip.c_str(), text );
+ }
+
+ // Let the system open the URL in the correct app.
+
+ ShellExecute( NULL, "open", url, "", "c:\\", SW_SHOWNORMAL );
+ }
+ }
+ *pResult = 0;
+}
+
+//===========================================================================================================================
+// OnCancel
+//===========================================================================================================================
+
+void ChooserDialog::OnCancel()
+{
+ // Do nothing.
+}
+
+//===========================================================================================================================
+// PopulateServicesList
+//===========================================================================================================================
+
+void ChooserDialog::PopulateServicesList( void )
+{
+ ServiceTypeVector::iterator i;
+ CString name;
+
+ // Add a fixed list of known services.
+
+ if( mServiceTypes.empty() )
+ {
+ const KnownServiceEntry * service;
+
+ for( service = kKnownServiceTable; service->serviceType; ++service )
+ {
+ ServiceTypeInfo info;
+
+ info.serviceType = service->serviceType;
+ info.description = service->description;
+ info.urlScheme = service->urlScheme;
+ mServiceTypes.push_back( info );
+ }
+ }
+
+ // Add each service to the list.
+
+ for( i = mServiceTypes.begin(); i != mServiceTypes.end(); ++i )
+ {
+ UTF8StringToStringObject( ( *i ).description.c_str(), name );
+ mServiceList.InsertItem( mServiceList.GetItemCount(), name );
+ }
+
+ // Select the first service type by default.
+
+ if( !mServiceTypes.empty() )
+ {
+ mServiceList.SetItemState( 0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED );
+ }
+}
+
+//===========================================================================================================================
+// UpdateInfoDisplay
+//===========================================================================================================================
+
+void ChooserDialog::UpdateInfoDisplay( void )
+{
+ int selectedItem;
+ std::string name;
+ CString s;
+ std::string ip;
+ std::string ifIP;
+ std::string text;
+ CWnd * item;
+
+ // Display the service instance if it is selected. Otherwise, clear all the info.
+
+ selectedItem = mChooserList.GetNextItem( -1, LVNI_SELECTED );
+ if( selectedItem >= 0 )
+ {
+ ServiceInstanceInfo * p;
+
+ assert( selectedItem < (int) mServiceInstances.size() );
+ p = &mServiceInstances[ selectedItem ];
+
+ name = p->name;
+ ip = p->ip;
+ ifIP = p->ifIP;
+ text = p->text;
+
+ // Sync up the list items with the actual data (IP address may change).
+
+ mChooserList.SetItemText( selectedItem, 1, ip.c_str() );
+ }
+
+ // Name
+
+ item = (CWnd *) this->GetDlgItem( IDC_INFO_NAME_TEXT );
+ assert( item );
+ UTF8StringToStringObject( name.c_str(), s );
+ item->SetWindowText( s );
+
+ // IP
+
+ item = (CWnd *) this->GetDlgItem( IDC_INFO_IP_TEXT );
+ assert( item );
+ item->SetWindowText( ip.c_str() );
+
+ // Interface
+
+ item = (CWnd *) this->GetDlgItem( IDC_INFO_INTERFACE_TEXT );
+ assert( item );
+ item->SetWindowText( ifIP.c_str() );
+
+ // Text
+
+ if( text.size() > 255 )
+ {
+ text.resize( 255 );
+ }
+ item = (CWnd *) this->GetDlgItem( IDC_INFO_TEXT_TEXT );
+ assert( item );
+ item->SetWindowText( text.c_str() );
+}
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+// OnDomainAdd
+//===========================================================================================================================
+
+LONG ChooserDialog::OnDomainAdd( WPARAM inWParam, LPARAM inLParam )
+{
+ DomainEventInfo * p;
+ std::auto_ptr < DomainEventInfo > pAutoPtr;
+ int n;
+ int i;
+ CString domain;
+ CString s;
+ bool found;
+
+ UNUSED_ALWAYS( inWParam );
+
+ assert( inLParam );
+ p = reinterpret_cast <DomainEventInfo *> ( inLParam );
+ pAutoPtr.reset( p );
+
+ // Search to see if we already know about this domain. If not, add it to the list.
+
+ found = false;
+ domain = p->domain;
+ n = mDomainList.GetItemCount();
+ for( i = 0; i < n; ++i )
+ {
+ s = mDomainList.GetItemText( i, 0 );
+ if( s == domain )
+ {
+ found = true;
+ break;
+ }
+ }
+ if( !found )
+ {
+ int selectedItem;
+
+ mDomainList.InsertItem( n, domain );
+
+ // If no domains are selected and the domain being added is a default domain, select it.
+
+ selectedItem = mDomainList.GetNextItem( -1, LVNI_SELECTED );
+ if( ( selectedItem < 0 ) && ( p->eventType == kDNSBrowserEventTypeAddDefaultDomain ) )
+ {
+ mDomainList.SetItemState( n, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED );
+ }
+ }
+ return( 0 );
+}
+
+//===========================================================================================================================
+// OnDomainRemove
+//===========================================================================================================================
+
+LONG ChooserDialog::OnDomainRemove( WPARAM inWParam, LPARAM inLParam )
+{
+ DomainEventInfo * p;
+ std::auto_ptr < DomainEventInfo > pAutoPtr;
+ int n;
+ int i;
+ CString domain;
+ CString s;
+ bool found;
+
+ UNUSED_ALWAYS( inWParam );
+
+ assert( inLParam );
+ p = reinterpret_cast <DomainEventInfo *> ( inLParam );
+ pAutoPtr.reset( p );
+
+ // Search to see if we know about this domain. If so, remove it from the list.
+
+ found = false;
+ domain = p->domain;
+ n = mDomainList.GetItemCount();
+ for( i = 0; i < n; ++i )
+ {
+ s = mDomainList.GetItemText( i, 0 );
+ if( s == domain )
+ {
+ found = true;
+ break;
+ }
+ }
+ if( found )
+ {
+ mDomainList.DeleteItem( i );
+ }
+ return( 0 );
+}
+
+//===========================================================================================================================
+// OnServiceAdd
+//===========================================================================================================================
+
+LONG ChooserDialog::OnServiceAdd( WPARAM inWParam, LPARAM inLParam )
+{
+ ServiceEventInfo * p;
+ std::auto_ptr < ServiceEventInfo > pAutoPtr;
+
+ UNUSED_ALWAYS( inWParam );
+
+ assert( inLParam );
+ p = reinterpret_cast <ServiceEventInfo *> ( inLParam );
+ pAutoPtr.reset( p );
+
+ return( 0 );
+}
+
+//===========================================================================================================================
+// OnServiceRemove
+//===========================================================================================================================
+
+LONG ChooserDialog::OnServiceRemove( WPARAM inWParam, LPARAM inLParam )
+{
+ ServiceEventInfo * p;
+ std::auto_ptr < ServiceEventInfo > pAutoPtr;
+ bool found;
+ int n;
+ int i;
+
+ UNUSED_ALWAYS( inWParam );
+
+ assert( inLParam );
+ p = reinterpret_cast <ServiceEventInfo *> ( inLParam );
+ pAutoPtr.reset( p );
+
+ // Search to see if we know about this service instance. If so, remove it from the list.
+
+ found = false;
+ n = (int) mServiceInstances.size();
+ for( i = 0; i < n; ++i )
+ {
+ ServiceInstanceInfo * q;
+
+ // If the name, type, domain, and interface match, treat it as the same service instance.
+
+ q = &mServiceInstances[ i ];
+ if( ( p->name == q->name ) &&
+ ( p->type == q->type ) &&
+ ( p->domain == q->domain ) )
+ {
+ found = true;
+ break;
+ }
+ }
+ if( found )
+ {
+ mChooserList.DeleteItem( i );
+ assert( i < (int) mServiceInstances.size() );
+ mServiceInstances.erase( mServiceInstances.begin() + i );
+ }
+ return( 0 );
+}
+
+//===========================================================================================================================
+// OnResolve
+//===========================================================================================================================
+
+LONG ChooserDialog::OnResolve( WPARAM inWParam, LPARAM inLParam )
+{
+ ServiceInstanceInfo * p;
+ std::auto_ptr < ServiceInstanceInfo > pAutoPtr;
+ int selectedType;
+ int n;
+ int i;
+ bool found;
+
+ UNUSED_ALWAYS( inWParam );
+
+ assert( inLParam );
+ p = reinterpret_cast <ServiceInstanceInfo *> ( inLParam );
+ pAutoPtr.reset( p );
+
+ // Make sure it is for an item of the correct type. This handles any resolves that may have been queued up.
+
+ selectedType = mServiceList.GetNextItem( -1, LVNI_SELECTED );
+ assert( selectedType >= 0 );
+ if( selectedType >= 0 )
+ {
+ assert( selectedType <= (int) mServiceTypes.size() );
+ if( p->type != mServiceTypes[ selectedType ].serviceType )
+ {
+ goto exit;
+ }
+ }
+
+ // Search to see if we know about this service instance. If so, update its info. Otherwise, add it to the list.
+
+ found = false;
+ n = (int) mServiceInstances.size();
+ for( i = 0; i < n; ++i )
+ {
+ ServiceInstanceInfo * q;
+
+ // If the name, type, domain, and interface matches, treat it as the same service instance.
+
+ q = &mServiceInstances[ i ];
+ if( ( p->name == q->name ) &&
+ ( p->type == q->type ) &&
+ ( p->domain == q->domain ) &&
+ ( p->ifIP == q->ifIP ) )
+ {
+ found = true;
+ break;
+ }
+ }
+ if( found )
+ {
+ mServiceInstances[ i ] = *p;
+ }
+ else
+ {
+ CString s;
+
+ mServiceInstances.push_back( *p );
+ UTF8StringToStringObject( p->name.c_str(), s );
+ mChooserList.InsertItem( n, s );
+ mChooserList.SetItemText( n, 1, p->ip.c_str() );
+
+ // If this is the only item, select it.
+
+ if( n == 0 )
+ {
+ mChooserList.SetItemState( n, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED );
+ }
+ }
+ UpdateInfoDisplay();
+
+exit:
+ return( 0 );
+}
+
+//===========================================================================================================================
+// StartBrowsing
+//===========================================================================================================================
+
+void ChooserDialog::StartBrowsing( const char *inType, const char *inDomain )
+{
+ DNSStatus err;
+
+ assert( mServiceInstances.empty() );
+ assert( mChooserList.GetItemCount() == 0 );
+ assert( !mIsServiceBrowsing );
+
+ mChooserList.DeleteAllItems();
+ mServiceInstances.clear();
+
+ mIsServiceBrowsing = true;
+ err = DNSBrowserStartServiceSearch( mBrowser, kDNSBrowserFlagAutoResolve, inType, inDomain );
+ assert( err == kDNSNoErr );
+}
+
+//===========================================================================================================================
+// StopBrowsing
+//===========================================================================================================================
+
+void ChooserDialog::StopBrowsing( void )
+{
+ // If searching, stop.
+
+ if( mIsServiceBrowsing )
+ {
+ DNSStatus err;
+
+ mIsServiceBrowsing = false;
+ err = DNSBrowserStopServiceSearch( mBrowser, 0 );
+ assert( err == kDNSNoErr );
+ }
+
+ // Remove all service instances.
+
+ mChooserList.DeleteAllItems();
+ assert( mChooserList.GetItemCount() == 0 );
+ mServiceInstances.clear();
+ assert( mServiceInstances.empty() );
+ UpdateInfoDisplay();
+}
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+// BrowserCallBack
+//===========================================================================================================================
+
+static void
+ BrowserCallBack(
+ void * inContext,
+ DNSBrowserRef inRef,
+ DNSStatus inStatusCode,
+ const DNSBrowserEvent * inEvent )
+{
+ ChooserDialog * dialog;
+ UINT message;
+ BOOL posted;
+
+ UNUSED_ALWAYS( inStatusCode );
+ UNUSED_ALWAYS( inRef );
+
+ // Check parameters.
+
+ assert( inContext );
+ dialog = reinterpret_cast <ChooserDialog *> ( inContext );
+
+ try
+ {
+ switch( inEvent->type )
+ {
+ case kDNSBrowserEventTypeRelease:
+ break;
+
+ // Domains
+
+ case kDNSBrowserEventTypeAddDomain:
+ case kDNSBrowserEventTypeAddDefaultDomain:
+ case kDNSBrowserEventTypeRemoveDomain:
+ {
+ DomainEventInfo * domain;
+ std::auto_ptr < DomainEventInfo > domainAutoPtr;
+
+ domain = new DomainEventInfo;
+ domainAutoPtr.reset( domain );
+
+ domain->eventType = inEvent->type;
+ domain->domain = inEvent->data.addDomain.domain;
+ domain->ifIP = inEvent->data.addDomain.interfaceIP;
+
+ message = ( inEvent->type == kDNSBrowserEventTypeRemoveDomain ) ? WM_USER_DOMAIN_REMOVE : WM_USER_DOMAIN_ADD;
+ posted = ::PostMessage( dialog->GetSafeHwnd(), message, 0, (LPARAM) domain );
+ assert( posted );
+ if( posted )
+ {
+ domainAutoPtr.release();
+ }
+ break;
+ }
+
+ // Services
+
+ case kDNSBrowserEventTypeAddService:
+ case kDNSBrowserEventTypeRemoveService:
+ {
+ ServiceEventInfo * service;
+ std::auto_ptr < ServiceEventInfo > serviceAutoPtr;
+
+ service = new ServiceEventInfo;
+ serviceAutoPtr.reset( service );
+
+ service->eventType = inEvent->type;
+ service->name = inEvent->data.addService.name;
+ service->type = inEvent->data.addService.type;
+ service->domain = inEvent->data.addService.domain;
+ service->ifIP = inEvent->data.addService.interfaceIP;
+
+ message = ( inEvent->type == kDNSBrowserEventTypeAddService ) ? WM_USER_SERVICE_ADD : WM_USER_SERVICE_REMOVE;
+ posted = ::PostMessage( dialog->GetSafeHwnd(), message, 0, (LPARAM) service );
+ assert( posted );
+ if( posted )
+ {
+ serviceAutoPtr.release();
+ }
+ break;
+ }
+
+ // Resolves
+
+ case kDNSBrowserEventTypeResolved:
+ {
+ ServiceInstanceInfo * serviceInstance;
+ std::auto_ptr < ServiceInstanceInfo > serviceInstanceAutoPtr;
+ char s[ 32 ];
+
+ serviceInstance = new ServiceInstanceInfo;
+ serviceInstanceAutoPtr.reset( serviceInstance );
+
+ serviceInstance->name = inEvent->data.resolved->name;
+ serviceInstance->type = inEvent->data.resolved->type;
+ serviceInstance->domain = inEvent->data.resolved->domain;
+ serviceInstance->ip = DNSNetworkAddressToString( &inEvent->data.resolved->address, s );
+ serviceInstance->ifIP = DNSNetworkAddressToString( &inEvent->data.resolved->interfaceIP, s );
+ serviceInstance->text = inEvent->data.resolved->textRecord;
+
+ posted = ::PostMessage( dialog->GetSafeHwnd(), WM_USER_RESOLVE, 0, (LPARAM) serviceInstance );
+ assert( posted );
+ if( posted )
+ {
+ serviceInstanceAutoPtr.release();
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+ catch( ... )
+ {
+ // Don't let exceptions escape.
+ }
+}
+
+//===========================================================================================================================
+// DNSNetworkAddressToString
+//
+// Note: Currently only supports IPv4 network addresses.
+//===========================================================================================================================
+
+static char * DNSNetworkAddressToString( const DNSNetworkAddress *inAddr, char *outString )
+{
+ const DNSUInt8 * p;
+ DNSUInt16 port;
+
+ p = inAddr->u.ipv4.addr.v8;
+ port = ntohs( inAddr->u.ipv4.port.v16 );
+ if( port != kDNSPortInvalid )
+ {
+ sprintf( outString, "%u.%u.%u.%u:%u", p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ], port );
+ }
+ else
+ {
+ sprintf( outString, "%u.%u.%u.%u", p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ] );
+ }
+ return( outString );
+}
+
+//===========================================================================================================================
+// UTF8StringToStringObject
+//===========================================================================================================================
+
+static DWORD UTF8StringToStringObject( const char *inUTF8, CString &inObject )
+{
+ DWORD err;
+ int n;
+ BSTR unicode;
+
+ unicode = NULL;
+
+ n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, NULL, 0 );
+ if( n > 0 )
+ {
+ unicode = (BSTR) malloc( (size_t)( n * sizeof( wchar_t ) ) );
+ if( !unicode )
+ {
+ err = ERROR_INSUFFICIENT_BUFFER;
+ goto exit;
+ }
+
+ n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, unicode, n );
+ try
+ {
+ inObject = unicode;
+ }
+ catch( ... )
+ {
+ err = ERROR_NO_UNICODE_TRANSLATION;
+ goto exit;
+ }
+ }
+ else
+ {
+ inObject = "";
+ }
+ err = 0;
+
+exit:
+ if( unicode )
+ {
+ free( unicode );
+ }
+ return( err );
+}
--- /dev/null
+/*
+ * 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: ChooserDialog.h,v $
+Revision 1.1 2003/08/21 02:06:47 bradley
+Moved Rendezvous Browser for non-Windows CE into Windows sub-folder.
+
+Revision 1.4 2003/08/12 19:56:28 cheshire
+Update to APSL 2.0
+
+Revision 1.3 2003/07/02 21:20:06 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.2 2002/09/21 20:44:55 zarzycki
+Added APSL info
+
+Revision 1.1 2002/09/20 06:12:52 bradley
+Rendezvous Browser for Windows
+
+*/
+
+#if !defined(AFX_CHOOSERDIALOG_H__AC258704_B307_4901_9F98_A0AC022FD8AC__INCLUDED_)
+#define AFX_CHOOSERDIALOG_H__AC258704_B307_4901_9F98_A0AC022FD8AC__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include <string>
+#include <vector>
+
+#include "afxcmn.h"
+
+#include "Resource.h"
+
+#include "DNSServices.h"
+
+//===========================================================================================================================
+// Structures
+//===========================================================================================================================
+
+struct ServiceInstanceInfo
+{
+ std::string name;
+ std::string type;
+ std::string domain;
+ std::string ip;
+ std::string text;
+ std::string ifIP;
+};
+
+struct ServiceTypeInfo
+{
+ std::string serviceType;
+ std::string description;
+ std::string urlScheme;
+};
+
+//===========================================================================================================================
+// ChooserDialog
+//===========================================================================================================================
+
+class ChooserDialog : public CDialog
+{
+ public:
+
+ ChooserDialog(CWnd* pParent = NULL);
+ virtual ~ChooserDialog( void );
+
+ //{{AFX_DATA(ChooserDialog)
+ enum { IDD = IDD_CHOOSER_DIALOG };
+ CListCtrl mServiceList;
+ CListCtrl mDomainList;
+ CListCtrl mChooserList;
+ //}}AFX_DATA
+
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(ChooserDialog)
+ public:
+ virtual BOOL PreTranslateMessage(MSG* pMsg);
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ virtual void PostNcDestroy();
+ //}}AFX_VIRTUAL
+
+ protected:
+
+ typedef std::vector < ServiceInstanceInfo > ServiceInstanceVector;
+ typedef std::vector < ServiceTypeInfo > ServiceTypeVector;
+
+ HACCEL mMenuAcceleratorTable;
+ DNSBrowserRef mBrowser;
+ BOOL mIsServiceBrowsing;
+ ServiceInstanceVector mServiceInstances;
+ ServiceTypeVector mServiceTypes;
+
+ public:
+
+ void PopulateServicesList( void );
+ void UpdateInfoDisplay( void );
+
+ void StartBrowsing( const char *inType, const char *inDomain );
+ void StopBrowsing( void );
+
+ protected:
+
+ //{{AFX_MSG(ChooserDialog)
+ virtual BOOL OnInitDialog();
+ afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
+ afx_msg void OnDomainListChanged(NMHDR* pNMHDR, LRESULT* pResult);
+ afx_msg void OnServiceListChanged(NMHDR* pNMHDR, LRESULT* pResult);
+ afx_msg void OnChooserListChanged(NMHDR* pNMHDR, LRESULT* pResult);
+ afx_msg void OnChooserListDoubleClick(NMHDR* pNMHDR, LRESULT* pResult);
+ afx_msg void OnAbout();
+ afx_msg void OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu);
+ afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized);
+ afx_msg void OnFileClose();
+ virtual void OnCancel();
+ afx_msg void OnExit();
+ afx_msg void OnClose();
+ afx_msg void OnNcDestroy();
+ //}}AFX_MSG
+ afx_msg LONG OnDomainAdd( WPARAM inWParam, LPARAM inLParam );
+ afx_msg LONG OnDomainRemove( WPARAM inWParam, LPARAM inLParam );
+ afx_msg LONG OnServiceAdd( WPARAM inWParam, LPARAM inLParam );
+ afx_msg LONG OnServiceRemove( WPARAM inWParam, LPARAM inLParam );
+ afx_msg LONG OnResolve( WPARAM inWParam, LPARAM inLParam );
+ DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_CHOOSERDIALOG_H__AC258704_B307_4901_9F98_A0AC022FD8AC__INCLUDED_)
--- /dev/null
+/*
+ * 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: StdAfx.cpp,v $
+Revision 1.1 2003/08/21 02:06:47 bradley
+Moved Rendezvous Browser for non-Windows CE into Windows sub-folder.
+
+Revision 1.4 2003/08/12 19:56:28 cheshire
+Update to APSL 2.0
+
+Revision 1.3 2003/07/02 21:20:06 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.2 2002/09/21 20:44:55 zarzycki
+Added APSL info
+
+Revision 1.1 2002/09/20 06:12:53 bradley
+Rendezvous Browser for Windows
+
+*/
+
+#include "stdafx.h"
--- /dev/null
+/*
+ * 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: StdAfx.h,v $
+Revision 1.1 2003/08/21 02:06:47 bradley
+Moved Rendezvous Browser for non-Windows CE into Windows sub-folder.
+
+Revision 1.4 2003/08/12 19:56:28 cheshire
+Update to APSL 2.0
+
+Revision 1.3 2003/07/02 21:20:06 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.2 2002/09/21 20:44:56 zarzycki
+Added APSL info
+
+Revision 1.1 2002/09/20 06:12:53 bradley
+Rendezvous Browser for Windows
+
+*/
+
+#if !defined(AFX_STDAFX_H__424305D2_0A97_4AA0_B9B1_A7D90D18EBA0__INCLUDED_)
+#define AFX_STDAFX_H__424305D2_0A97_4AA0_B9B1_A7D90D18EBA0__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
+
+#include <afxwin.h> // MFC core and standard components
+#include <afxext.h> // MFC extensions
+#include <afxdtctl.h> // MFC support for Internet Explorer 4 Common Controls
+#ifndef _AFX_NO_AFXCMN_SUPPORT
+#include <afxcmn.h> // MFC support for Windows Common Controls
+#endif // _AFX_NO_AFXCMN_SUPPORT
+
+#include <afxsock.h> // MFC socket extensions
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#include <stdlib.h>
+
+#include "DNSServices.h"
+
+#include "Application.h"
+
+#include "ChooserDialog.h"
+
+#endif // !defined(AFX_STDAFX_H__424305D2_0A97_4AA0_B9B1_A7D90D18EBA0__INCLUDED_)
--- /dev/null
+; CLW file contains information for the MFC ClassWizard
+
+[General Info]
+Version=1
+LastClass=BrowserDialog
+LastTemplate=CDialog
+NewFileInclude1=#include "stdafx.h"
+NewFileInclude2=#include "Application.h"
+
+ClassCount=3
+Class1=Application
+Class2=BrowserDialog
+
+ResourceCount=3
+Resource2=IDR_MAINFRAME
+Resource3=IDD_APPLICATION_DIALOG
+
+[CLS:Application]
+Type=0
+HeaderFile=Application.h
+ImplementationFile=Application.cpp
+Filter=N
+
+[CLS:BrowserDialog]
+Type=0
+HeaderFile=BrowserDialog.h
+ImplementationFile=BrowserDialog.cpp
+Filter=D
+
+
+[DLG:IDD_APPLICATION_DIALOG]
+Type=1
+ControlCount=3
+Control1=IDOK,button,1342242817
+Control2=IDCANCEL,button,1342242816
+Control3=IDC_STATIC,static,1342308352
+Class=BrowserDialog
--- /dev/null
+# Microsoft eMbedded Visual Tools Project File - Name="Application" - Package Owner=<4>
+# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (WCE ARMV4) Application" 0xa301
+# TARGTYPE "Win32 (WCE emulator) Application" 0xa601
+
+CFG=Application - Win32 (WCE emulator) Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "Application.vcn".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "Application.vcn" CFG="Application - Win32 (WCE emulator) Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "Application - Win32 (WCE emulator) Release" (based on "Win32 (WCE emulator) Application")
+!MESSAGE "Application - Win32 (WCE emulator) Debug" (based on "Win32 (WCE emulator) Application")
+!MESSAGE "Application - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Application")
+!MESSAGE "Application - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+# PROP ATL_Project 2
+
+!IF "$(CFG)" == "Application - Win32 (WCE emulator) Release"
+
+# PROP BASE Use_MFC 2
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "emulatorRel"
+# PROP BASE Intermediate_Dir "emulatorRel"
+# PROP BASE CPU_ID "{32E52003-403E-442D-BE48-DE10F8C6131D}"
+# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 2
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "emulatorRel"
+# PROP Intermediate_Dir "emulatorRel"
+# PROP CPU_ID "{32E52003-403E-442D-BE48-DE10F8C6131D}"
+# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+RSC=rc.exe
+# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "_X86_" /d "x86" /d "_i386_" /d "_AFXDLL" /r
+# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "_X86_" /d "x86" /d "_i386_" /d "_AFXDLL" /r
+CPP=cl.exe
+# ADD BASE CPP /nologo /W3 /D "_i386_" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "_X86_" /D "x86" /D "NDEBUG" /D "_WIN32_WCE_CEPC" /D "_AFXDLL" /Yu"stdafx.h" /Gs8192 /GF /O2 /c
+# ADD CPP /nologo /W3 /I "Resources" /I "Sources" /I "..\..\..\DNSServices" /I "..\..\..\..\mDNSCore" /I "..\..\..\..\mDNSCore\PlatformSupport" /D "_i386_" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "_X86_" /D "x86" /D "NDEBUG" /D "_WIN32_WCE_CEPC" /D "_AFXDLL" /Gs8192 /GF /O2 /c
+# SUBTRACT CPP /YX /Yc /Yu
+MTL=midl.exe
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /subsystem:$(CESubsystem) /MACHINE:IX86
+# ADD LINK32 ws2.lib /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /subsystem:$(CESubsystem) /MACHINE:IX86
+
+!ELSEIF "$(CFG)" == "Application - Win32 (WCE emulator) Debug"
+
+# PROP BASE Use_MFC 2
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "emulatorDbg"
+# PROP BASE Intermediate_Dir "emulatorDbg"
+# PROP BASE CPU_ID "{32E52003-403E-442D-BE48-DE10F8C6131D}"
+# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 2
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "emulatorDbg"
+# PROP Intermediate_Dir "emulatorDbg"
+# PROP CPU_ID "{32E52003-403E-442D-BE48-DE10F8C6131D}"
+# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+RSC=rc.exe
+# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "_X86_" /d "x86" /d "_i386_" /d "_AFXDLL" /r
+# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "_X86_" /d "x86" /d "_i386_" /d "_AFXDLL" /r
+CPP=cl.exe
+# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D "_i386_" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "_X86_" /D "x86" /D "_WIN32_WCE_CEPC" /D "_AFXDLL" /Yu"stdafx.h" /Gs8192 /GF /c
+# ADD CPP /nologo /W3 /WX /Zi /Od /I "Resources" /I "Sources" /I "..\..\..\DNSServices" /I "..\..\..\..\mDNSCore" /I "..\..\..\..\mDNSCore\PlatformSupport" /D "_i386_" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "_X86_" /D "x86" /D "_WIN32_WCE_CEPC" /D "_AFXDLL" /FR /Gs8192 /GF /c
+MTL=midl.exe
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /debug /subsystem:$(CESubsystem) /MACHINE:IX86
+# ADD LINK32 ws2.lib /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /debug /subsystem:$(CESubsystem) /MACHINE:IX86
+
+!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Release"
+
+# PROP BASE Use_MFC 2
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "ARMV4Rel"
+# PROP BASE Intermediate_Dir "ARMV4Rel"
+# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}"
+# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 2
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "ARMV4Rel"
+# PROP Intermediate_Dir "ARMV4Rel"
+# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}"
+# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+RSC=rc.exe
+# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /d "_AFXDLL" /r
+# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /d "_AFXDLL" /r
+CPP=clarm.exe
+# ADD BASE CPP /nologo /W3 /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_AFXDLL" /Yu"stdafx.h" /O2 /M$(CECrtMT) /c
+# ADD CPP /nologo /W3 /I "Resources" /I "Sources" /I "..\..\..\DNSServices" /I "..\..\..\..\mDNSCore" /I "..\..\..\..\mDNSCore\PlatformSupport" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_AFXDLL" /O2 /M$(CECrtMT) /c
+# SUBTRACT CPP /YX /Yc /Yu
+MTL=midl.exe
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM
+# ADD LINK32 ws2.lib /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM
+
+!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug"
+
+# PROP BASE Use_MFC 2
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "ARMV4Dbg"
+# PROP BASE Intermediate_Dir "ARMV4Dbg"
+# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}"
+# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 2
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "ARMV4Dbg"
+# PROP Intermediate_Dir "ARMV4Dbg"
+# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}"
+# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+RSC=rc.exe
+# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /d "_AFXDLL" /r
+# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /d "_AFXDLL" /r
+CPP=clarm.exe
+# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "_AFXDLL" /Yu"stdafx.h" /M$(CECrtMTDebug) /c
+# ADD CPP /nologo /W3 /Zi /Od /I "Resources" /I "Sources" /I "..\..\..\DNSServices" /I "..\..\..\..\mDNSCore" /I "..\..\..\..\mDNSCore\PlatformSupport" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "_AFXDLL" /M$(CECrtMTDebug) /c
+# SUBTRACT CPP /YX /Yc /Yu
+MTL=midl.exe
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /debug /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM
+# ADD LINK32 ws2.lib /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /debug /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM
+
+!ENDIF
+
+# Begin Target
+
+# Name "Application - Win32 (WCE emulator) Release"
+# Name "Application - Win32 (WCE emulator) Debug"
+# Name "Application - Win32 (WCE ARMV4) Release"
+# Name "Application - Win32 (WCE ARMV4) Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\Sources\Application.cpp
+
+!IF "$(CFG)" == "Application - Win32 (WCE emulator) Release"
+
+DEP_CPP_APPLI=\
+ "..\..\..\DNSServices\DNSServices.h"\
+ ".\Sources\Application.h"\
+ ".\Sources\BrowserDialog.h"\
+ ".\Sources\StdAfx.h"\
+
+
+!ELSEIF "$(CFG)" == "Application - Win32 (WCE emulator) Debug"
+
+DEP_CPP_APPLI=\
+ "..\..\..\DNSServices\DNSServices.h"\
+ ".\Sources\Application.h"\
+ ".\Sources\BrowserDialog.h"\
+ ".\Sources\StdAfx.h"\
+
+
+!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_APPLI=\
+ "..\..\..\DNSServices\DNSServices.h"\
+ ".\Sources\Application.h"\
+ ".\Sources\BrowserDialog.h"\
+ ".\Sources\StdAfx.h"\
+
+
+!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_APPLI=\
+ "..\..\..\DNSServices\DNSServices.h"\
+ ".\Sources\Application.h"\
+ ".\Sources\BrowserDialog.h"\
+ ".\Sources\StdAfx.h"\
+
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sources\Application.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sources\BrowserDialog.cpp
+
+!IF "$(CFG)" == "Application - Win32 (WCE emulator) Release"
+
+DEP_CPP_BROWS=\
+ ".\Sources\Application.h"\
+ ".\Sources\BrowserDialog.h"\
+ ".\Sources\StdAfx.h"\
+
+
+!ELSEIF "$(CFG)" == "Application - Win32 (WCE emulator) Debug"
+
+DEP_CPP_BROWS=\
+ "..\..\..\DNSServices\DNSServices.h"\
+ ".\Sources\Application.h"\
+ ".\Sources\BrowserDialog.h"\
+ ".\Sources\StdAfx.h"\
+
+
+!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_BROWS=\
+ ".\Sources\Application.h"\
+ ".\Sources\BrowserDialog.h"\
+ ".\Sources\StdAfx.h"\
+
+
+!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_BROWS=\
+ ".\Sources\Application.h"\
+ ".\Sources\BrowserDialog.h"\
+ ".\Sources\StdAfx.h"\
+
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sources\BrowserDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sources\StdAfx.cpp
+
+!IF "$(CFG)" == "Application - Win32 (WCE emulator) Release"
+
+DEP_CPP_STDAF=\
+ ".\Sources\StdAfx.h"\
+
+# ADD CPP /Yc"stdafx.h"
+
+!ELSEIF "$(CFG)" == "Application - Win32 (WCE emulator) Debug"
+
+DEP_CPP_STDAF=\
+ ".\Sources\StdAfx.h"\
+
+# ADD CPP /Yc"stdafx.h"
+
+!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_STDAF=\
+ ".\Sources\StdAfx.h"\
+
+# ADD CPP /Yc"stdafx.h"
+
+!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_STDAF=\
+ ".\Sources\StdAfx.h"\
+
+# ADD CPP /Yc"stdafx.h"
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sources\StdAfx.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\Resources\Application.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\Resources\Application.rc
+
+!IF "$(CFG)" == "Application - Win32 (WCE emulator) Release"
+
+!ELSEIF "$(CFG)" == "Application - Win32 (WCE emulator) Debug"
+
+!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Release"
+
+!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug"
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\Resources\Application.rc2
+# PROP Exclude_From_Scan -1
+# PROP BASE Exclude_From_Build 1
+# PROP Exclude_From_Build 1
+# End Source File
+# Begin Source File
+
+SOURCE=.\Resources\newres.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Resources\Resource.h
+# End Source File
+# End Group
+# Begin Group "Rendezvous"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\..\DNSServices\DNSServices.c
+
+!IF "$(CFG)" == "Application - Win32 (WCE emulator) Release"
+
+DEP_CPP_DNSSE=\
+ "..\..\..\DNSServices\DNSServices.h"\
+ "..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+ "..\..\..\..\mDNSCore\mDNSDebug.h"\
+ "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\
+
+
+!ELSEIF "$(CFG)" == "Application - Win32 (WCE emulator) Debug"
+
+DEP_CPP_DNSSE=\
+ "..\..\..\DNSServices\DNSServices.h"\
+ "..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+ "..\..\..\..\mDNSCore\mDNSDebug.h"\
+ "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\
+
+
+!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_DNSSE=\
+ "..\..\..\DNSServices\DNSServices.h"\
+ "..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+ "..\..\..\..\mDNSCore\mDNSDebug.h"\
+ "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\
+
+
+!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_DNSSE=\
+ "..\..\..\DNSServices\DNSServices.h"\
+ "..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+ "..\..\..\..\mDNSCore\mDNSDebug.h"\
+ "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\
+
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\DNSServices\DNSServices.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\mDNSCore\mDNS.c
+
+!IF "$(CFG)" == "Application - Win32 (WCE emulator) Release"
+
+DEP_CPP_MDNS_=\
+ "..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+ "..\..\..\..\mDNSCore\mDNSDebug.h"\
+ "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\
+
+
+!ELSEIF "$(CFG)" == "Application - Win32 (WCE emulator) Debug"
+
+DEP_CPP_MDNS_=\
+ "..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+ "..\..\..\..\mDNSCore\mDNSDebug.h"\
+ "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\
+
+
+!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_MDNS_=\
+ "..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+ "..\..\..\..\mDNSCore\mDNSDebug.h"\
+ "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\
+
+
+!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_MDNS_=\
+ "..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+ "..\..\..\..\mDNSCore\mDNSDebug.h"\
+ "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\
+
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\mDNSCore\mDNSClientAPI.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\mDNSCore\mDNSDebug.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\mDNSCore\mDNSPlatformFunctions.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\mDNSWin32.c
+
+!IF "$(CFG)" == "Application - Win32 (WCE emulator) Release"
+
+DEP_CPP_MDNSW=\
+ "..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+ "..\..\..\..\mDNSCore\mDNSDebug.h"\
+ "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\
+ "..\..\..\mDNSWin32.h"\
+
+
+!ELSEIF "$(CFG)" == "Application - Win32 (WCE emulator) Debug"
+
+DEP_CPP_MDNSW=\
+ "..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+ "..\..\..\..\mDNSCore\mDNSDebug.h"\
+ "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\
+ "..\..\..\mDNSWin32.h"\
+
+# ADD CPP /W3
+
+!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_MDNSW=\
+ "..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+ "..\..\..\..\mDNSCore\mDNSDebug.h"\
+ "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\
+ "..\..\..\mDNSWin32.h"\
+
+
+!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_MDNSW=\
+ "..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+ "..\..\..\..\mDNSCore\mDNSDebug.h"\
+ "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\
+ "..\..\..\mDNSWin32.h"\
+
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\mDNSWin32.h
+# End Source File
+# End Group
+# End Target
+# End Project
--- /dev/null
+Microsoft eMbedded Visual Tools Workspace File, Format Version 4.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "Application"=.\Application.vcp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
--- /dev/null
+//Microsoft eMbedded Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+#include "newres.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 DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "#include ""newres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+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"
+ "#ifdef _WIN32\r\n"
+ "LANGUAGE 9, 1\r\n"
+ "#pragma code_page(1252)\r\n"
+ "#endif //_WIN32\r\n"
+ "#include ""Resources\\Application.rc2"" // non-Microsoft eMbedded Visual C++ edited resources\r\n"
+ "#include ""afxres.rc"" // Standard components\r\n"
+ "#include ""wceres.rc"" // WCE-specific components\r\n"
+ "#endif\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDR_MAINFRAME ICON DISCARDABLE "Application.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_APPLICATION_DIALOG DIALOG DISCARDABLE 0, 0, 139, 153
+STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION
+EXSTYLE WS_EX_APPWINDOW | 0x80000000L
+CAPTION "Rendezvous Browser"
+FONT 8, "System"
+BEGIN
+ CONTROL "List1",IDC_BROWSE_LIST,"SysListView32",LVS_REPORT |
+ LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOCOLUMNHEADER |
+ WS_BORDER | WS_TABSTOP,7,7,125,141
+END
+
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,1
+ PRODUCTVERSION 1,0,0,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "Comments", "\0"
+ VALUE "CompanyName", "Apple Computer, Inc.\0"
+ VALUE "FileDescription", "Rendezvous Browser for Windows CE\0"
+ VALUE "FileVersion", "1, 0, 0, 1\0"
+ VALUE "InternalName", "Application\0"
+ VALUE "LegalCopyright", "Copyright © 2003 Apple Computer, Inc.\0"
+ VALUE "LegalTrademarks", "\0"
+ VALUE "OriginalFilename", "Application.exe\0"
+ VALUE "PrivateBuild", "\0"
+ VALUE "ProductName", "Rendezvous Browser for Windows CE\0"
+ VALUE "ProductVersion", "1, 0, 0, 1\0"
+ VALUE "SpecialBuild", "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // !_MAC
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_APPLICATION_DIALOG, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 132
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 146
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDP_SOCKETS_INIT_FAILED "Windows CE sockets initialization failed."
+ IDS_BROWSER_LIST_COLUMN_NAME "Name"
+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)
+#ifdef _WIN32
+LANGUAGE 9, 1
+#pragma code_page(1252)
+#endif //_WIN32
+#include "Resources\Application.rc2" // non-Microsoft eMbedded Visual C++ edited resources
+#include "afxres.rc" // Standard components
+#include "wceres.rc" // WCE-specific components
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
--- /dev/null
+//
+// APPLICATION.RC2 - resources Microsoft eMbedded Visual C++ does not edit directly
+//
+
+#ifdef APSTUDIO_INVOKED
+ #error this file is not editable by Microsoft eMbedded Visual C++
+#endif //APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Add manually edited resources here...
+
+/////////////////////////////////////////////////////////////////////////////
--- /dev/null
+#ifndef __NEWRES_H__
+#define __NEWRES_H__
+
+#define SHMENUBAR RCDATA
+#if !(defined(_WIN32_WCE_PSPC) && (_WIN32_WCE >= 300))
+ #undef HDS_HORZ
+ #undef HDS_BUTTONS
+ #undef HDS_HIDDEN
+
+ #include <commctrl.h>
+ // for MenuBar
+ #define I_IMAGENONE (-2)
+ #define NOMENU 0xFFFF
+ #define IDS_SHNEW 1
+ #define IDM_SHAREDNEW 10
+ #define IDM_SHAREDNEWDEFAULT 11
+
+ // for Tab Control
+ #define TCS_SCROLLOPPOSITE 0x0001 // assumes multiline tab
+ #define TCS_BOTTOM 0x0002
+ #define TCS_RIGHT 0x0002
+ #define TCS_VERTICAL 0x0080
+ #define TCS_MULTISELECT 0x0004 // allow multi-select in button mode
+ #define TCS_FLATBUTTONS 0x0008
+#endif //_WIN32_WCE_PSPC
+
+
+#endif //__NEWRES_H__
--- /dev/null
+//{{NO_DEPENDENCIES}}
+// Microsoft eMbedded Visual C++ generated include file.
+// Used by Application.rc
+//
+#define IDD_APPLICATION_DIALOG 102
+#define IDP_SOCKETS_INIT_FAILED 103
+#define IDS_BROWSER_LIST_COLUMN_NAME 104
+#define IDR_MAINFRAME 128
+#define IDC_BROWSE_LIST 1000
+#define IDC_IP_TEXT 1003
+#define IDC_TXT_TEXT 1004
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 129
+#define _APS_NEXT_COMMAND_VALUE 32771
+#define _APS_NEXT_CONTROL_VALUE 1005
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
--- /dev/null
+/*
+ * 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: Application.cpp,v $
+Revision 1.1 2003/08/21 02:16:10 bradley
+Rendezvous Browser for HTTP services for Windows CE/PocketPC.
+
+*/
+
+#include "stdafx.h"
+
+#include "DNSServices.h"
+
+#include "BrowserDialog.h"
+
+#include "Application.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+//===========================================================================================================================
+// Message Map
+//===========================================================================================================================
+
+BEGIN_MESSAGE_MAP(Application, CWinApp)
+ //{{AFX_MSG_MAP(Application)
+ // NOTE - the ClassWizard will add and remove mapping macros here.
+ // DO NOT EDIT what you see in these blocks of generated code!
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+//===========================================================================================================================
+// Globals
+//===========================================================================================================================
+
+Application gApp;
+
+//===========================================================================================================================
+// Application
+//===========================================================================================================================
+
+Application::Application()
+ : CWinApp()
+{
+ //
+}
+
+//===========================================================================================================================
+// InitInstance
+//===========================================================================================================================
+
+BOOL Application::InitInstance()
+{
+ DNSStatus err;
+ BrowserDialog dialog;
+ BOOL dnsInitialized;
+
+ dnsInitialized = FALSE;
+
+ if( !AfxSocketInit() )
+ {
+ AfxMessageBox( IDP_SOCKETS_INIT_FAILED );
+ goto exit;
+ }
+
+ err = DNSServicesInitialize( kDNSFlagAdvertise, 0 );
+ if( err )
+ {
+ AfxMessageBox( IDP_SOCKETS_INIT_FAILED );
+ goto exit;
+ }
+ dnsInitialized = TRUE;
+
+ // Display the main browser dialog.
+
+ m_pMainWnd = &dialog;
+ dialog.DoModal();
+
+ // Dialog has been closed. Return false to exit the app and not start the app's message pump.
+
+exit:
+ if( dnsInitialized )
+ {
+ DNSServicesFinalize();
+ }
+ return( FALSE );
+}
--- /dev/null
+/*
+ * 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: Application.h,v $
+Revision 1.1 2003/08/21 02:16:10 bradley
+Rendezvous Browser for HTTP services for Windows CE/PocketPC.
+
+*/
+
+#if !defined(AFX_APPLICATION_H__E2E51302_D643_458E_A7A5_5157233D1E5C__INCLUDED_)
+#define AFX_APPLICATION_H__E2E51302_D643_458E_A7A5_5157233D1E5C__INCLUDED_
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+
+#ifndef __AFXWIN_H__
+ #error include 'stdafx.h' before including this file for PCH
+#endif
+
+#include "Resource.h"
+
+//===========================================================================================================================
+// Application
+//===========================================================================================================================
+
+class Application : public CWinApp
+{
+ public:
+
+ Application();
+
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(Application)
+ public:
+ virtual BOOL InitInstance();
+ //}}AFX_VIRTUAL
+
+ //{{AFX_MSG(Application)
+ // NOTE - the ClassWizard will add and remove member functions here.
+ // DO NOT EDIT what you see in these blocks of generated code !
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft eMbedded Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_APPLICATION_H__E2E51302_D643_458E_A7A5_5157233D1E5C__INCLUDED_)
--- /dev/null
+/*
+ * 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: BrowserDialog.cpp,v $
+Revision 1.1 2003/08/21 02:16:10 bradley
+Rendezvous Browser for HTTP services for Windows CE/PocketPC.
+
+*/
+
+#include "stdafx.h"
+
+#include "Application.h"
+
+#include "DNSServices.h"
+
+#include "BrowserDialog.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+//===========================================================================================================================
+// Message Map
+//===========================================================================================================================
+
+BEGIN_MESSAGE_MAP(BrowserDialog, CDialog)
+ //{{AFX_MSG_MAP(BrowserDialog)
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+static DWORD UTF8StringToStringObject( const char *inUTF8, CString &inObject );
+
+//===========================================================================================================================
+// BrowserDialog
+//===========================================================================================================================
+
+BrowserDialog::BrowserDialog( CWnd *inParent )
+ : CDialog( BrowserDialog::IDD, inParent )
+{
+ //{{AFX_DATA_INIT(BrowserDialog)
+ // NOTE: the ClassWizard will add member initialization here
+ //}}AFX_DATA_INIT
+
+ // Note that LoadIcon does not require a subsequent DestroyIcon in Win32.
+
+ mIcon = AfxGetApp()->LoadIcon( IDR_MAINFRAME );
+}
+
+//===========================================================================================================================
+// DoDataExchange
+//===========================================================================================================================
+
+void BrowserDialog::DoDataExchange( CDataExchange *pDX )
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(BrowserDialog)
+ DDX_Control(pDX, IDC_BROWSE_LIST, mBrowserList);
+ //}}AFX_DATA_MAP
+}
+
+//===========================================================================================================================
+// OnInitDialog
+//===========================================================================================================================
+
+BOOL BrowserDialog::OnInitDialog()
+{
+ CString s;
+
+ CDialog::OnInitDialog();
+
+ // Set the icon for this dialog. The framework does this automatically when the application's main window is not a dialog.
+
+ SetIcon( mIcon, TRUE ); // Set big icon
+ SetIcon( mIcon, FALSE ); // Set small icon
+
+ CenterWindow( GetDesktopWindow() );
+
+ // Set up the list.
+
+ CRect rect;
+
+ s.LoadString( IDS_BROWSER_LIST_COLUMN_NAME );
+ mBrowserList.GetWindowRect( rect );
+ mBrowserList.InsertColumn( 0, s, LVCFMT_LEFT, rect.Width() - 8 );
+
+ // Start browsing for services.
+
+ DNSStatus err;
+
+ err = DNSBrowserCreate( 0, BrowserCallBack, this, &mBrowser );
+ if( err )
+ {
+ AfxMessageBox( IDP_SOCKETS_INIT_FAILED );
+ goto exit;
+ }
+
+ err = DNSBrowserStartServiceSearch( mBrowser, 0, "_http._tcp", NULL );
+ if( err )
+ {
+ AfxMessageBox( IDP_SOCKETS_INIT_FAILED );
+ goto exit;
+ }
+
+exit:
+ return( TRUE );
+}
+
+//===========================================================================================================================
+// BrowserCallBack [static]
+//===========================================================================================================================
+
+void
+ BrowserDialog::BrowserCallBack(
+ void * inContext,
+ DNSBrowserRef inRef,
+ DNSStatus inStatusCode,
+ const DNSBrowserEvent * inEvent )
+{
+ BrowserDialog * dialog;
+
+ DNS_UNUSED( inStatusCode );
+ dialog = reinterpret_cast < BrowserDialog * > ( inContext );
+
+ switch( inEvent->type )
+ {
+ case kDNSBrowserEventTypeAddService:
+ dialog->BrowserAddService( inEvent->data.addService.name );
+ break;
+
+ case kDNSBrowserEventTypeRemoveService:
+ dialog->BrowserRemoveService( inEvent->data.removeService.name );
+ break;
+
+ default:
+ break;
+ }
+}
+
+//===========================================================================================================================
+// BrowserAddService
+//===========================================================================================================================
+
+void BrowserDialog::BrowserAddService( const char *inName )
+{
+ BrowserEntry newEntry;
+ INT_PTR n;
+ INT_PTR i;
+
+ UTF8StringToStringObject( inName, newEntry.name );
+
+ n = mBrowserEntries.GetSize();
+ for( i = 0; i < n; ++i )
+ {
+ BrowserEntry & entry = mBrowserEntries.ElementAt( i );
+
+ if( entry.name.CompareNoCase( newEntry.name ) == 0 )
+ {
+ break;
+ }
+ }
+ if( i >= n )
+ {
+ mBrowserEntries.Add( newEntry );
+ mBrowserList.InsertItem( i, newEntry.name );
+ }
+}
+
+//===========================================================================================================================
+// BrowserRemoveService
+//===========================================================================================================================
+
+void BrowserDialog::BrowserRemoveService( const char *inName )
+{
+ BrowserEntry newEntry;
+ INT_PTR n;
+ INT_PTR i;
+
+ UTF8StringToStringObject( inName, newEntry.name );
+
+ n = mBrowserEntries.GetSize();
+ for( i = 0; i < n; ++i )
+ {
+ BrowserEntry & entry = mBrowserEntries.ElementAt( i );
+
+ if( entry.name.CompareNoCase( newEntry.name ) == 0 )
+ {
+ break;
+ }
+ }
+ if( i < n )
+ {
+ mBrowserEntries.RemoveAt( i );
+ mBrowserList.DeleteItem( i );
+ }
+}
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+// UTF8StringToStringObject
+//===========================================================================================================================
+
+static DWORD UTF8StringToStringObject( const char *inUTF8, CString &inObject )
+{
+ DWORD err;
+ int n;
+ wchar_t * unicode;
+
+ unicode = NULL;
+
+ n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, NULL, 0 );
+ if( n > 0 )
+ {
+ unicode = (wchar_t *) malloc( (size_t)( n * sizeof( wchar_t ) ) );
+ if( !unicode ) { err = ERROR_INSUFFICIENT_BUFFER; goto exit; };
+
+ n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, unicode, n );
+ inObject = unicode;
+ }
+ else
+ {
+ inObject = "";
+ }
+ err = 0;
+
+exit:
+ if( unicode )
+ {
+ free( unicode );
+ }
+ return( err );
+}
--- /dev/null
+/*
+ * 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: BrowserDialog.h,v $
+Revision 1.1 2003/08/21 02:16:10 bradley
+Rendezvous Browser for HTTP services for Windows CE/PocketPC.
+
+*/
+
+#if !defined(AFX_BROWSERDIALOG_H__DECC5C82_C1C6_4630_B8D5_E1DDE570A061__INCLUDED_)
+#define AFX_BROWSERDIALOG_H__DECC5C82_C1C6_4630_B8D5_E1DDE570A061__INCLUDED_
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+
+#include "afxtempl.h"
+#include "Resource.h"
+
+#include "DNSServices.h"
+
+//===========================================================================================================================
+// BrowserDialog
+//===========================================================================================================================
+
+class BrowserDialog : public CDialog
+{
+ public:
+
+ BrowserDialog( CWnd *inParent = NULL );
+
+ //{{AFX_DATA(BrowserDialog)
+ enum { IDD = IDD_APPLICATION_DIALOG };
+ CListCtrl mBrowserList;
+ //}}AFX_DATA
+
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(BrowserDialog)
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ //}}AFX_VIRTUAL
+
+ static void
+ BrowserCallBack(
+ void * inContext,
+ DNSBrowserRef inRef,
+ DNSStatus inStatusCode,
+ const DNSBrowserEvent * inEvent );
+
+ void BrowserAddService( const char *inName );
+ void BrowserRemoveService( const char *inName );
+
+ protected:
+
+ struct BrowserEntry
+ {
+ CString name;
+ };
+
+
+ HICON mIcon;
+ DNSBrowserRef mBrowser;
+ CArray < BrowserEntry, BrowserEntry > mBrowserEntries;
+
+ // Generated message map functions
+ //{{AFX_MSG(BrowserDialog)
+ virtual BOOL OnInitDialog();
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft eMbedded Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_BROWSERDIALOG_H__DECC5C82_C1C6_4630_B8D5_E1DDE570A061__INCLUDED_)
--- /dev/null
+/*
+ * 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: StdAfx.cpp,v $
+Revision 1.1 2003/08/21 02:16:10 bradley
+Rendezvous Browser for HTTP services for Windows CE/PocketPC.
+
+*/
+
+#include "stdafx.h"
--- /dev/null
+/*
+ * 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: StdAfx.h,v $
+Revision 1.1 2003/08/21 02:16:10 bradley
+Rendezvous Browser for HTTP services for Windows CE/PocketPC.
+
+*/
+
+#if !defined(AFX_STDAFX_H__7F91E52B_CF39_429D_837D_599CE0B2B3D6__INCLUDED_)
+#define AFX_STDAFX_H__7F91E52B_CF39_429D_837D_599CE0B2B3D6__INCLUDED_
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+
+
+
+#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
+
+#include <afxwin.h> // MFC core and standard components
+#include <afxext.h> // MFC extensions
+
+#if defined(_AFXDLL)
+#include <afxdtctl.h> // MFC support for Internet Explorer 4 Common Controls
+#endif
+
+#ifndef _AFX_NO_AFXCMN_SUPPORT
+#include <afxcmn.h> // MFC support for Windows Common Controls
+#endif // _AFX_NO_AFXCMN_SUPPORT
+
+#include <afxsock.h> // MFC socket extensions
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft eMbedded Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_STDAFX_H__7F91E52B_CF39_429D_837D_599CE0B2B3D6__INCLUDED_)
--- /dev/null
+/*
+ * 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: Tool.c,v $
+Revision 1.7 2003/08/20 07:06:34 bradley
+Update to APSL 2.0. Updated change history to match other mDNSResponder files.
+
+Revision 1.6 2003/08/20 06:50:55 bradley
+Updated to latest internal version of the Rendezvous for Windows code: Re-did everything to support
+the latest DNSServices APIs (proxies, record updates, etc.); Added support for testing the platform
+neutral DNSServices-based emulation layer for the Mac OS X DNSServiceDiscovery API.
+
+*/
+
+#if( defined( _MSC_VER ) )
+ #pragma warning( disable:4068 ) // Disable "unknown pragma" warning for "pragma unused".
+ #pragma warning( disable:4127 ) // Disable "conditional expression is constant" warning for debug macros.
+ #pragma warning( disable:4311 ) // Disable "type cast : pointer truncation from void *const to int".
+
+ // No stdint.h with Visual C++ so emulate it here.
+
+ typedef signed char int8_t; // C99 stdint.h not supported in VC++/VS.NET yet.
+ typedef unsigned char uint8_t; // C99 stdint.h not supported in VC++/VS.NET yet.
+ typedef signed short int16_t; // C99 stdint.h not supported in VC++/VS.NET yet.
+ typedef unsigned short uint16_t; // C99 stdint.h not supported in VC++/VS.NET yet.
+ typedef signed long int32_t; // C99 stdint.h not supported in VC++/VS.NET yet.
+ typedef unsigned long uint32_t; // C99 stdint.h not supported in VC++/VS.NET yet.
+#else
+ #include <stdint.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if( __MACH__ )
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+
+ #include <signal.h>
+ #include <unistd.h>
+
+ #include <CoreServices/CoreServices.h>
+#else
+ #define WIN32_LEAN_AND_MEAN
+
+ #include <winsock2.h>
+ #include <windows.h>
+#endif
+
+#include "DNSServices.h"
+#include "DNSServiceDiscovery.h"
+
+//===========================================================================================================================
+// Macros
+//===========================================================================================================================
+
+#if( !TARGET_OS_MAC )
+ #define require_action_string( X, LABEL, ACTION, STR ) \
+ do \
+ { \
+ if( !( X ) ) \
+ { \
+ fprintf( stderr, "%s\n", ( STR ) ); \
+ { ACTION; } \
+ goto LABEL; \
+ } \
+ } while( 0 )
+
+ #define require_string( X, LABEL, STR ) \
+ do \
+ { \
+ if( !( X ) ) \
+ { \
+ fprintf( stderr, "%s\n", ( STR ) ); \
+ goto LABEL; \
+ \
+ } \
+ } while( 0 )
+
+ #define require_noerr_string( ERR, LABEL, STR ) \
+ do \
+ { \
+ if( ( ERR ) != 0 ) \
+ { \
+ fprintf( stderr, "%s (%ld)\n", ( STR ), ( ERR ) ); \
+ goto LABEL; \
+ } \
+ } while( 0 )
+#endif
+
+//===========================================================================================================================
+// Prototypes
+//===========================================================================================================================
+
+int main( int argc, char* argv[] );
+static void Usage( void );
+static int ProcessArgs( int argc, char* argv[] );
+static DNSStatus ProcessPreset( int inPreset );
+
+#if( __MACH__ )
+ static void SigIntHandler( int inSignalNumber );
+#endif
+
+#if( defined( WINVER ) )
+ static BOOL WINAPI ConsoleControlHandler( DWORD inControlEvent );
+#endif
+
+static void BrowserCallBack( void *inContext, DNSBrowserRef inRef, DNSStatus inStatusCode, const DNSBrowserEvent *inEvent );
+static void ResolverCallBack( void *inContext, DNSResolverRef inRef, DNSStatus inStatusCode, const DNSResolverEvent *inEvent );
+
+static void
+ RegistrationCallBack(
+ void * inContext,
+ DNSRegistrationRef inRef,
+ DNSStatus inStatusCode,
+ const DNSRegistrationEvent * inEvent );
+
+static void
+ HostRegistrationCallBack(
+ void * inContext,
+ DNSHostRegistrationRef inRef,
+ DNSStatus inStatusCode,
+ void * inData );
+
+static void
+ EmulatedBrowserCallBack(
+ DNSServiceBrowserReplyResultType inResult,
+ const char * inName,
+ const char * inType,
+ const char * inDomain,
+ DNSServiceDiscoveryReplyFlags inFlags,
+ void * inContext );
+
+static void
+ EmulatedDomainEnumerationCallBack(
+ DNSServiceDomainEnumerationReplyResultType inResult,
+ const char * inDomain,
+ DNSServiceDiscoveryReplyFlags inFlags,
+ void * inContext );
+
+static void
+ EmulatedResolverCallBack(
+ struct sockaddr * inInterfaceAddr,
+ struct sockaddr * inAddr,
+ const char * inTextRecord,
+ DNSServiceDiscoveryReplyFlags inFlags,
+ void * inContext );
+
+static void EmulatedRegistrationCallBack( DNSServiceRegistrationReplyErrorType inResult, void *inContext );
+
+static char * IPv4ToString( DNSOpaque32 inIP, char *outString );
+
+//===========================================================================================================================
+// Globals
+//===========================================================================================================================
+
+#if( defined( WINVER ) )
+ static volatile int gQuit = 0;
+#endif
+
+static int gPrintTXTRecords = 1;
+
+// Presets
+
+typedef struct PresetData PresetData;
+struct PresetData
+{
+ int argc;
+ char * argv[ 16 ];
+};
+
+#if 0
+#pragma mark == Presets ==
+#endif
+
+static const PresetData gPresets[] =
+{
+ /* 01 */ { 2, { "rendezvous", "-bbd" } },
+ /* 02 */ { 4, { "rendezvous", "-bs", "_airport._tcp", "local." } },
+ /* 03 */ { 4, { "rendezvous", "-bs", "_xserveraid._tcp", "local." } },
+ /* 04 */ { 3, { "rendezvous", "-rdb", "apple.com" } },
+ /* 05 */ { 7, { "rendezvous", "-rs", "My Fake AirPort", "_airport._tcp", "local.", "1234", "My Fake Info" } },
+ /* 06 */ { 7, { "rendezvous", "-rs", "My Fake Xserve RAID", "_xserveraid._tcp", "local.", "1234", "My Fake Info" } },
+ /* 07 */ { 7, { "rendezvous", "-rs", "My Fake Web Server", "_http._tcp", "local.", "8080", "index.html" } },
+ /* 08 */ { 9, { "rendezvous", "-rps", "www.apple.com", "17.254.0.91", "Apple Web Server", "_http._tcp", "local.", "80", "index.html" } },
+};
+
+const int gPresetsCount = sizeof( gPresets ) / sizeof( gPresets[ 0 ] );
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+// main
+//===========================================================================================================================
+
+int main( int argc, char* argv[] )
+{
+ DNSStatus err;
+
+ // Set up DNS Services and install a Console Control Handler to handle things like control-c signals.
+
+ err = DNSServicesInitialize( kDNSFlagAdvertise, 0 );
+ require_noerr_string( err, exit, "could not initialize Rendezvous" );
+
+#if( __MACH__ )
+ signal( SIGINT, SigIntHandler );
+#endif
+
+#if( defined( WINVER ) )
+ SetConsoleCtrlHandler( ConsoleControlHandler, TRUE );
+#endif
+
+ ProcessArgs( argc, argv );
+
+exit:
+ DNSServicesFinalize();
+ return( err );
+}
+
+//===========================================================================================================================
+// Usage
+//===========================================================================================================================
+
+static void Usage( void )
+{
+ fprintf( stderr, "\n" );
+ fprintf( stderr, "rendezvous - Rendezvous Tool 1.0d1\n" );
+ fprintf( stderr, "\n" );
+ fprintf( stderr, " -bbd 'b'rowse for 'b'rowsing 'd'omains\n" );
+ fprintf( stderr, " -brd 'b'rowse for 'r'egistration 'd'omains\n" );
+ fprintf( stderr, " -bs <type> <domain> 'b'rowse for 's'ervices\n" );
+ fprintf( stderr, " -lsi <name> <type> <domain> 'l'ookup 's'ervice 'i'nstance\n" );
+ fprintf( stderr, " -rdb[d] <domain> 'r'egister 'd'omain for 'b'rowsing ['d'efault]\n" );
+ fprintf( stderr, " -rdr[d] <domain> 'r'egister 'd'omain for 'r'egistration ['d'efault]\n" );
+ fprintf( stderr, " -rs <name> <type> <domain> <port> <txt> 'r'egister 's'ervice\n" );
+ fprintf( stderr, " -rps <host> <ip> <name> <type> <domain> <port> <txt> 'r'egister 'p'roxy 's'ervice\n" );
+ fprintf( stderr, " -rnss <name> <type> <domain> 'r'egister 'n'o 's'uch 's'ervice\n" );
+
+ fprintf( stderr, " -ebs <type> <domain> 'e'mulated 'b'rowse for 's'ervices\n" );
+ fprintf( stderr, " -ebd <registration/browse> 'e'mulated 'b'rowse for 'd'omains\n" );
+ fprintf( stderr, " -elsi <name> <type> <domain> 'e'mulated 'l'ookup 's'ervice 'i'nstance\n" );
+ fprintf( stderr, " -ers <name> <type> <domain> <port> <txt> 'e'mulated 'r'egister 's'ervice\n" );
+
+ fprintf( stderr, " -h[elp] 'h'elp\n" );
+ fprintf( stderr, "\n" );
+
+ fprintf( stderr, " -1 Preset 1 (browse for browsing domains) rendezvous -bbd\n" );
+ fprintf( stderr, " -2 Preset 2 (browse for AirPort) rendezvous -bs \"_airport._tcp\" \"local.\"\n" );
+ fprintf( stderr, " -3 Preset 3 (browse for Xserve RAID) rendezvous -bs \"_xserveraid._tcp\" \"local.\"\n" );
+ fprintf( stderr, " -4 Preset 4 (register apple.com domain) rendezvous -rdb \"apple.com\"\n" );
+ fprintf( stderr, " -5 Preset 5 (register fake AirPort) rendezvous -rs \"My Fake AirPort\" \"_airport._tcp\" \"local.\" 1234 \"My Fake Info\"\n" );
+ fprintf( stderr, " -6 Preset 6 (register fake Xserve RAID) rendezvous -rs \"My Fake Xserve RAID\" \"_xserveraid._tcp\" \"local.\" 1234 \"My Fake Info\"\n" );
+ fprintf( stderr, " -7 Preset 7 (register fake web server) rendezvous -rs \"My Fake Web Server\" \"_http._tcp\" \"local.\" 8080 \"index.html\"\n" );
+ fprintf( stderr, "\n" );
+}
+
+//===========================================================================================================================
+// ProcessArgs
+//===========================================================================================================================
+
+static int ProcessArgs( int argc, char* argv[] )
+{
+ DNSStatus err;
+ int i;
+ const char * name;
+ const char * type;
+ const char * domain;
+ int port;
+ const char * text;
+ size_t textSize;
+ DNSBrowserRef browser;
+ DNSResolverFlags resolverFlags;
+ DNSDomainRegistrationType domainType;
+ const char * label;
+ const char * host;
+ const char * ip;
+ unsigned int b[ 4 ];
+ DNSNetworkAddress addr;
+ dns_service_discovery_ref emulatedRef;
+
+ // Parse the command line arguments (ignore first argument since it's just the program name).
+
+ require_action_string( argc >= 2, exit, err = kDNSBadParamErr, "no arguments specified" );
+
+ for( i = 1; i < argc; ++i )
+ {
+ if( strcmp( argv[ i ], "-bbd" ) == 0 )
+ {
+ // 'b'rowse for 'b'rowsing 'd'omains
+
+ fprintf( stdout, "browsing for browsing domains\n" );
+
+ err = DNSBrowserCreate( 0, BrowserCallBack, NULL, &browser );
+ require_noerr_string( err, exit, "create browser failed" );
+
+ err = DNSBrowserStartDomainSearch( browser, 0 );
+ require_noerr_string( err, exit, "start domain search failed" );
+ }
+ else if( strcmp( argv[ i ], "-brd" ) == 0 )
+ {
+ // 'b'rowse for 'r'egistration 'd'omains
+
+ fprintf( stdout, "browsing for registration domains\n" );
+
+ err = DNSBrowserCreate( 0, BrowserCallBack, NULL, &browser );
+ require_noerr_string( err, exit, "create browser failed" );
+
+ err = DNSBrowserStartDomainSearch( browser, kDNSBrowserFlagRegistrationDomainsOnly );
+ require_noerr_string( err, exit, "start domain search failed" );
+ }
+ else if( strcmp( argv[ i ], "-bs" ) == 0 )
+ {
+ // 'b'rowse for 's'ervices <type> <domain>
+
+ require_action_string( argc > ( i + 2 ), exit, err = kDNSBadParamErr, "missing arguments" );
+ ++i;
+ type = argv[ i++ ];
+ domain = argv[ i ];
+ if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) )
+ {
+ domain = "local.";
+ }
+ fprintf( stdout, "browsing for \"%s.%s\"\n", type, domain );
+
+ err = DNSBrowserCreate( 0, BrowserCallBack, NULL, &browser );
+ require_noerr_string( err, exit, "create browser failed" );
+
+ err = DNSBrowserStartServiceSearch( browser, kDNSBrowserFlagAutoResolve, type, domain );
+ require_noerr_string( err, exit, "start service search failed" );
+ }
+ else if( strcmp( argv[ i ], "-lsi" ) == 0 )
+ {
+ // 'l'ookup 's'ervice 'i'nstance <name> <type> <domain>
+
+ require_action_string( argc > ( i + 3 ), exit, err = kDNSBadParamErr, "missing arguments" );
+ ++i;
+ name = argv[ i++ ];
+ type = argv[ i++ ];
+ domain = argv[ i ];
+ if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) )
+ {
+ domain = "local.";
+ }
+ fprintf( stdout, "resolving \"%s.%s.%s\"\n", name, type, domain );
+
+ resolverFlags = kDNSResolverFlagOnlyIfUnique |
+ kDNSResolverFlagAutoReleaseByName;
+ err = DNSResolverCreate( resolverFlags, name, type, domain, ResolverCallBack, 0, NULL, NULL );
+ require_noerr_string( err, exit, "create resolver failed" );
+ }
+ else if( ( strcmp( argv[ i ], "-rdb" ) == 0 ) || ( strcmp( argv[ i ], "-rdbd" ) == 0 ) )
+ {
+ // 'r'egister 'd'omain for 'b'rowsing ['d'efault] <domain>
+
+ require_action_string( argc > ( i + 1 ), exit, err = kDNSBadParamErr, "missing arguments" );
+ if( strcmp( argv[ i ], "-rdb" ) == 0 )
+ {
+ domainType = kDNSDomainRegistrationTypeBrowse;
+ label = "";
+ }
+ else
+ {
+ domainType = kDNSDomainRegistrationTypeBrowseDefault;
+ label = "default ";
+ }
+ ++i;
+ domain = argv[ i ];
+ if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) )
+ {
+ domain = "local.";
+ }
+ fprintf( stdout, "registering \"%s\" as %sbrowse domain\n", domain, label );
+
+ err = DNSDomainRegistrationCreate( 0, domain, domainType, NULL );
+ require_noerr_string( err, exit, "create domain registration failed" );
+ }
+ else if( ( strcmp( argv[ i ], "-rdr" ) == 0 ) || ( strcmp( argv[ i ], "-rdrd" ) == 0 ) )
+ {
+ // 'r'egister 'd'omain for 'r'egistration ['d'efault] <domain>
+
+ require_action_string( argc > ( i + 1 ), exit, err = kDNSBadParamErr, "missing arguments" );
+ if( strcmp( argv[ i ], "-rdr" ) == 0 )
+ {
+ domainType = kDNSDomainRegistrationTypeRegistration;
+ label = "";
+ }
+ else
+ {
+ domainType = kDNSDomainRegistrationTypeRegistrationDefault;
+ label = "default ";
+ }
+ ++i;
+ domain = argv[ i ];
+ if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) )
+ {
+ domain = "local.";
+ }
+ fprintf( stdout, "registering \"%s\" as %sregistration domain\n", domain, label );
+
+ err = DNSDomainRegistrationCreate( 0, domain, domainType, NULL );
+ require_noerr_string( err, exit, "create domain registration failed" );
+ }
+ else if( strcmp( argv[ i ], "-rs" ) == 0 )
+ {
+ // 'r'egister 's'ervice <name> <type> <domain> <port> <txt>
+
+ require_action_string( argc > ( i + 5 ), exit, err = kDNSBadParamErr, "missing arguments" );
+ ++i;
+ name = argv[ i++ ];
+ type = argv[ i++ ];
+ domain = argv[ i++ ];
+ port = atoi( argv[ i++ ] );
+ text = argv[ i ];
+ textSize = strlen( text );
+ if( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) )
+ {
+ domain = "local.";
+ }
+ fprintf( stdout, "registering service \"%s.%s.%s\" port %d text \"%s\"\n", name, type, domain, port, text );
+
+ err = DNSRegistrationCreate( 0, name, type, domain, (DNSPort) port, text, (DNSCount) textSize, NULL, NULL,
+ RegistrationCallBack, NULL, NULL );
+ require_noerr_string( err, exit, "create registration failed" );
+ }
+ else if( strcmp( argv[ i ], "-rps" ) == 0 )
+ {
+ DNSHostRegistrationFlags hostFlags;
+
+ // 'r'egister 'p'roxy 's'ervice <name> <type> <domain> <port> <txt>
+
+ require_action_string( argc > ( i + 7 ), exit, err = kDNSBadParamErr, "missing arguments" );
+ ++i;
+ host = argv[ i++ ];
+ ip = argv[ i++ ];
+ name = argv[ i++ ];
+ type = argv[ i++ ];
+ domain = argv[ i++ ];
+ port = atoi( argv[ i++ ] );
+ text = argv[ i ];
+ textSize = strlen( text );
+ if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) )
+ {
+ domain = "local.";
+ }
+
+ sscanf( ip, "%u.%u.%u.%u", &b[ 0 ], &b[ 1 ], &b[ 2 ], &b[ 3 ] );
+ addr.addressType = kDNSNetworkAddressTypeIPv4;
+ addr.u.ipv4.addr.v32 = (DNSUInt32)( ( b[ 0 ] << 24 ) | ( b[ 1 ] << 16 ) | ( b[ 2 ] << 8 ) | ( b[ 3 ] << 0 ) );
+
+ fprintf( stdout, "registering proxy service \"%s.%s.%s\" port %d text \"%s\"\n", name, type, domain, port, text );
+
+ hostFlags = kDNSHostRegistrationFlagOnlyIfNotFound | kDNSHostRegistrationFlagAutoRenameOnConflict;
+ err = DNSHostRegistrationCreate( hostFlags, host, domain, &addr, NULL,
+ HostRegistrationCallBack, NULL, NULL );
+ require_noerr_string( err, exit, "create host registration failed" );
+
+ err = DNSRegistrationCreate( 0, name, type, domain, (DNSPort) port, text, (DNSCount) textSize, host, NULL,
+ RegistrationCallBack, NULL, NULL );
+ require_noerr_string( err, exit, "create registration failed" );
+ }
+ else if( strcmp( argv[ i ], "-rnss" ) == 0 )
+ {
+ // 'r'egister 'n'o 's'uch 's'ervice <name> <type> <domain>
+
+ require_action_string( argc > ( i + 3 ), exit, err = kDNSBadParamErr, "missing arguments" );
+ ++i;
+ name = argv[ i++ ];
+ type = argv[ i++ ];
+ domain = argv[ i++ ];
+ if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) )
+ {
+ domain = "local.";
+ }
+ fprintf( stdout, "registering no-such-service \"%s.%s.%s\"\n", name, type, domain );
+
+ err = DNSNoSuchServiceRegistrationCreate( 0, name, type, domain, NULL, RegistrationCallBack, NULL, NULL );
+ require_noerr_string( err, exit, "create no-such-service registration failed" );
+ }
+ else if( strcmp( argv[ i ], "-ebs" ) == 0 )
+ {
+ // 'e'mulated 'b'rowse for 's'ervices <type> <domain>
+
+ require_action_string( argc > ( i + 2 ), exit, err = kDNSBadParamErr, "missing arguments" );
+ ++i;
+ type = argv[ i++ ];
+ domain = argv[ i ];
+ if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) )
+ {
+ domain = "local.";
+ }
+ fprintf( stdout, "emulated browsing for \"%s.%s\"\n", type, domain );
+
+ emulatedRef = DNSServiceBrowserCreate( type, domain, EmulatedBrowserCallBack, NULL );
+ require_action_string( emulatedRef, exit, err = kDNSUnknownErr, "create emulated browser failed" );
+ }
+ else if( strcmp( argv[ i ], "-ebd" ) == 0 )
+ {
+ int registrationOnly;
+
+ // 'e'mulated 'b'rowse for 'd'omains <registration/browse>
+
+ require_action_string( argc > ( i + 1 ), exit, err = kDNSBadParamErr, "missing arguments" );
+ ++i;
+ type = argv[ i++ ];
+ if( strcmp( type, "registration" ) == 0 )
+ {
+ registrationOnly = 1;
+ }
+ else if( strcmp( type, "browse" ) == 0 )
+ {
+ registrationOnly = 0;
+ }
+ else
+ {
+ require_action_string( 0, exit, err = kDNSBadParamErr, "invalid browse type" );
+ }
+ fprintf( stdout, "emulated browsing for %s domains\n", type );
+
+ emulatedRef = DNSServiceDomainEnumerationCreate( registrationOnly, EmulatedDomainEnumerationCallBack, NULL );
+ require_action_string( emulatedRef, exit, err = kDNSUnknownErr, "create emulated domain browser failed" );
+ }
+ else if( strcmp( argv[ i ], "-elsi" ) == 0 )
+ {
+ // 'e'mulated 'l'ookup 's'ervice 'i'nstance <name> <type> <domain>
+
+ require_action_string( argc > ( i + 3 ), exit, err = kDNSBadParamErr, "missing arguments" );
+ ++i;
+ name = argv[ i++ ];
+ type = argv[ i++ ];
+ domain = argv[ i ];
+ if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) )
+ {
+ domain = "local.";
+ }
+ fprintf( stdout, "emulated resolving \"%s.%s.%s\"\n", name, type, domain );
+
+ emulatedRef = DNSServiceResolverResolve( name, type, domain, EmulatedResolverCallBack, NULL );
+ require_action_string( emulatedRef, exit, err = kDNSUnknownErr, "create emulated resolver failed" );
+ }
+ else if( strcmp( argv[ i ], "-ers" ) == 0 )
+ {
+ // 'e'mulated 'r'egister 's'ervice <name> <type> <domain> <port> <txt>
+
+ require_action_string( argc > ( i + 5 ), exit, err = kDNSBadParamErr, "missing arguments" );
+ ++i;
+ name = argv[ i++ ];
+ type = argv[ i++ ];
+ domain = argv[ i++ ];
+ port = atoi( argv[ i++ ] );
+ text = argv[ i ];
+ textSize = strlen( text );
+ if( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) )
+ {
+ domain = "local.";
+ }
+ fprintf( stdout, "registering service \"%s.%s.%s\" port %d text \"%s\"\n", name, type, domain, port, text );
+
+ emulatedRef = DNSServiceRegistrationCreate( name, type, domain, (uint16_t) port, text,
+ EmulatedRegistrationCallBack, NULL );
+ require_action_string( emulatedRef, exit, err = kDNSUnknownErr, "create emulated registration failed" );
+ }
+ else if( ( argv[ i ][ 0 ] == '-' ) && isdigit( argv[ i ][ 1 ] ) )
+ {
+ // Preset
+
+ ProcessPreset( atoi( &argv[ i ][ 1 ] ) );
+ err = 0;
+ goto exit;
+ }
+ else if( strcmp( argv[ i ], "-q" ) == 0 )
+ {
+ // Quiet (no text records)
+
+ gPrintTXTRecords = 0;
+ }
+ else if( ( strcmp( argv[ i ], "-help" ) == 0 ) || ( strcmp( argv[ i ], "-h" ) == 0 ) )
+ {
+ // Help
+
+ Usage();
+ err = 0;
+ goto exit;
+ }
+ else
+ {
+ // Unknown parameter.
+
+ require_action_string( 0, exit, err = kDNSBadParamErr, "unknown parameter" );
+ goto exit;
+ }
+ }
+
+ // Run until control-C'd.
+
+ #if( __MACH__ )
+ CFRunLoopRun();
+ #endif
+
+ #if( defined( WINVER ) )
+ while( !gQuit )
+ {
+ Sleep( 200 );
+ }
+ #endif
+
+ err = kDNSNoErr;
+
+exit:
+ if( err )
+ {
+ Usage();
+ }
+ return( err );
+}
+
+//===========================================================================================================================
+// ProcessPreset
+//===========================================================================================================================
+
+static DNSStatus ProcessPreset( int inPreset )
+{
+ DNSStatus err;
+
+ require_action_string( ( inPreset > 0 ) && ( inPreset <= gPresetsCount ), exit, err = kDNSBadParamErr, "invalid preset" );
+
+ err = ProcessArgs( gPresets[ inPreset - 1 ].argc, (char **) gPresets[ inPreset - 1 ].argv );
+
+exit:
+ return( err );
+}
+
+#if( __MACH__ )
+//===========================================================================================================================
+// SigIntHandler
+//===========================================================================================================================
+
+static void SigIntHandler( int inSignalNumber )
+{
+ DNS_UNUSED( inSignalNumber );
+
+ signal( SIGINT, SIG_DFL );
+ CFRunLoopStop( CFRunLoopGetCurrent() );
+}
+#endif
+
+#if( defined( WINVER ) )
+//===========================================================================================================================
+// ConsoleControlHandler
+//===========================================================================================================================
+
+static BOOL WINAPI ConsoleControlHandler( DWORD inControlEvent )
+{
+ BOOL handled;
+
+ handled = 0;
+ switch( inControlEvent )
+ {
+ case CTRL_C_EVENT:
+ case CTRL_BREAK_EVENT:
+ case CTRL_CLOSE_EVENT:
+ case CTRL_LOGOFF_EVENT:
+ case CTRL_SHUTDOWN_EVENT:
+ gQuit = 1;
+ handled = 1;
+ break;
+
+ default:
+ break;
+ }
+ return( handled );
+}
+#endif
+
+//===========================================================================================================================
+// BrowserCallBack
+//===========================================================================================================================
+
+static void BrowserCallBack( void *inContext, DNSBrowserRef inRef, DNSStatus inStatusCode, const DNSBrowserEvent *inEvent )
+{
+ char ifIP[ 32 ];
+ char ip[ 32 ];
+
+ DNS_UNUSED( inContext );
+ DNS_UNUSED( inRef );
+ DNS_UNUSED( inStatusCode );
+
+ switch( inEvent->type )
+ {
+ case kDNSBrowserEventTypeRelease:
+ break;
+
+ case kDNSBrowserEventTypeAddDomain:
+ fprintf( stdout, "domain \"%s\" added on interface 0x%08X (%s)\n",
+ inEvent->data.addDomain.domain,
+ (int) inEvent->data.addDomain.interfaceID,
+ IPv4ToString( inEvent->data.addDomain.interfaceIP.u.ipv4.addr, ifIP ) );
+ break;
+
+ case kDNSBrowserEventTypeAddDefaultDomain:
+ fprintf( stdout, "default domain \"%s\" added on interface 0x%08X (%s)\n",
+ inEvent->data.addDefaultDomain.domain,
+ (int) inEvent->data.addDefaultDomain.interfaceID,
+ IPv4ToString( inEvent->data.addDefaultDomain.interfaceIP.u.ipv4.addr, ifIP ) );
+ break;
+
+ case kDNSBrowserEventTypeRemoveDomain:
+ fprintf( stdout, "domain \"%s\" removed on interface 0x%08X (%s)\n",
+ inEvent->data.removeDomain.domain,
+ (int) inEvent->data.removeDomain.interfaceID,
+ IPv4ToString( inEvent->data.removeDomain.interfaceIP.u.ipv4.addr, ifIP ) );
+ break;
+
+ case kDNSBrowserEventTypeAddService:
+ fprintf( stdout, "service \"%s.%s%s\" added on interface 0x%08X (%s)\n",
+ inEvent->data.addService.name,
+ inEvent->data.addService.type,
+ inEvent->data.addService.domain,
+ (int) inEvent->data.addService.interfaceID,
+ IPv4ToString( inEvent->data.addService.interfaceIP.u.ipv4.addr, ifIP ) );
+ break;
+
+ case kDNSBrowserEventTypeRemoveService:
+ fprintf( stdout, "service \"%s.%s%s\" removed on interface 0x%08X (%s)\n",
+ inEvent->data.removeService.name,
+ inEvent->data.removeService.type,
+ inEvent->data.removeService.domain,
+ (int) inEvent->data.removeService.interfaceID,
+ IPv4ToString( inEvent->data.removeService.interfaceIP.u.ipv4.addr, ifIP ) );
+ break;
+
+ case kDNSBrowserEventTypeResolved:
+ {
+ const uint8_t * p;
+ const uint8_t * end;
+ int i;
+
+ fprintf( stdout, "resolved \"%s.%s%s\" to %s:%u on interface 0x%08X (%s)%s\n",
+ inEvent->data.resolved->name,
+ inEvent->data.resolved->type,
+ inEvent->data.resolved->domain,
+ IPv4ToString( inEvent->data.resolved->address.u.ipv4.addr, ip ),
+ ( inEvent->data.resolved->address.u.ipv4.port.v8[ 0 ] << 8 ) |
+ inEvent->data.resolved->address.u.ipv4.port.v8[ 1 ],
+ (int) inEvent->data.resolved->interfaceID,
+ IPv4ToString( inEvent->data.resolved->interfaceIP.u.ipv4.addr, ifIP ),
+ ( inEvent->data.resolved->textRecordRawSize > 0 ) ? " with text:" : "" );
+
+ p = (const uint8_t *) inEvent->data.resolved->textRecordRaw;
+ end = p + inEvent->data.resolved->textRecordRawSize;
+ i = 0;
+
+ if( gPrintTXTRecords )
+ {
+ while( p < end )
+ {
+ uint8_t size;
+
+ size = *p++;
+ if( ( p + size ) > end )
+ {
+ fprintf( stdout, "\n### MALFORMED TXT RECORD (length byte too big for record)\n\n" );
+ break;
+ }
+ fprintf( stdout, "%5d (%3d bytes): \"%.*s\"\n", i, size, size, p );
+ p += size;
+ ++i;
+ }
+ fprintf( stdout, "\n" );
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+//===========================================================================================================================
+// ResolverCallBack
+//===========================================================================================================================
+
+static void ResolverCallBack( void *inContext, DNSResolverRef inRef, DNSStatus inStatusCode, const DNSResolverEvent *inEvent )
+{
+ char ifIP[ 32 ];
+ char ip[ 32 ];
+
+ DNS_UNUSED( inContext );
+ DNS_UNUSED( inRef );
+ DNS_UNUSED( inStatusCode );
+
+ switch( inEvent->type )
+ {
+ case kDNSResolverEventTypeResolved:
+ {
+ const uint8_t * p;
+ const uint8_t * end;
+ int i;
+
+ fprintf( stdout, "resolved \"%s.%s%s\" to %s:%u on interface 0x%08X (%s)%s\n",
+ inEvent->data.resolved.name,
+ inEvent->data.resolved.type,
+ inEvent->data.resolved.domain,
+ IPv4ToString( inEvent->data.resolved.address.u.ipv4.addr, ip ),
+ ( inEvent->data.resolved.address.u.ipv4.port.v8[ 0 ] << 8 ) |
+ inEvent->data.resolved.address.u.ipv4.port.v8[ 1 ],
+ (int) inEvent->data.resolved.interfaceID,
+ IPv4ToString( inEvent->data.resolved.interfaceIP.u.ipv4.addr, ifIP ),
+ ( inEvent->data.resolved.textRecordRawSize > 0 ) ? " with text:" : "" );
+
+ p = (const uint8_t *) inEvent->data.resolved.textRecordRaw;
+ end = p + inEvent->data.resolved.textRecordRawSize;
+ i = 0;
+
+ if( gPrintTXTRecords )
+ {
+ while( p < end )
+ {
+ uint8_t size;
+
+ size = *p++;
+ if( ( p + size ) > end )
+ {
+ fprintf( stdout, "\n### MALFORMED TXT RECORD (length byte too big for record)\n\n" );
+ break;
+ }
+ fprintf( stdout, "%5d (%3d bytes): \"%.*s\"\n", i, size, size, p );
+ p += size;
+ ++i;
+ }
+ fprintf( stdout, "\n" );
+ }
+ break;
+ }
+
+ case kDNSResolverEventTypeRelease:
+ break;
+
+ default:
+ break;
+ }
+}
+
+//===========================================================================================================================
+// RegistrationCallBack
+//===========================================================================================================================
+
+static void
+ RegistrationCallBack(
+ void * inContext,
+ DNSRegistrationRef inRef,
+ DNSStatus inStatusCode,
+ const DNSRegistrationEvent * inEvent )
+{
+ DNS_UNUSED( inContext );
+ DNS_UNUSED( inRef );
+ DNS_UNUSED( inStatusCode );
+
+ switch( inEvent->type )
+ {
+ case kDNSRegistrationEventTypeRelease:
+ break;
+
+ case kDNSRegistrationEventTypeRegistered:
+ fprintf( stdout, "name registered and active\n" );
+ break;
+
+ case kDNSRegistrationEventTypeNameCollision:
+ fprintf( stdout, "name in use, please choose another name\n" );
+ break;
+
+ default:
+ break;
+ }
+}
+
+//===========================================================================================================================
+// HostRegistrationCallBack
+//===========================================================================================================================
+
+static void
+ HostRegistrationCallBack(
+ void * inContext,
+ DNSHostRegistrationRef inRef,
+ DNSStatus inStatusCode,
+ void * inData )
+{
+ DNS_UNUSED( inContext );
+ DNS_UNUSED( inRef );
+ DNS_UNUSED( inData );
+
+ if( inStatusCode == kDNSNoErr )
+ {
+ fprintf( stdout, "host name registered and active\n" );
+ }
+ else if( inStatusCode == kDNSNameConflictErr )
+ {
+ fprintf( stdout, "host name in use, please choose another name\n" );
+ }
+ else
+ {
+ fprintf( stdout, "unknown host registration status (%ld)\n", inStatusCode );
+ }
+}
+
+//===========================================================================================================================
+// EmulatedBrowserCallBack
+//===========================================================================================================================
+
+static void
+ EmulatedBrowserCallBack(
+ DNSServiceBrowserReplyResultType inResult,
+ const char * inName,
+ const char * inType,
+ const char * inDomain,
+ DNSServiceDiscoveryReplyFlags inFlags,
+ void * inContext )
+{
+ DNS_UNUSED( inFlags );
+ DNS_UNUSED( inContext );
+
+ if( inResult == DNSServiceBrowserReplyAddInstance )
+ {
+ fprintf( stdout, "\"%s.%s%s\" service added emulated\n", inName, inType, inDomain );
+ }
+ else if( inResult == DNSServiceBrowserReplyRemoveInstance )
+ {
+ fprintf( stdout, "\"%s.%s%s\" service removed emulated\n", inName, inType, inDomain );
+ }
+ else
+ {
+ fprintf( stdout, "### unknown emulated browser callback result (%d)\n", inResult );
+ }
+}
+
+//===========================================================================================================================
+// EmulatedDomainEnumerationCallBack
+//===========================================================================================================================
+
+static void
+ EmulatedDomainEnumerationCallBack(
+ DNSServiceDomainEnumerationReplyResultType inResult,
+ const char * inDomain,
+ DNSServiceDiscoveryReplyFlags inFlags,
+ void * inContext )
+{
+ DNS_UNUSED( inFlags );
+ DNS_UNUSED( inContext );
+
+ if( inResult == DNSServiceDomainEnumerationReplyAddDomain )
+ {
+ fprintf( stdout, "\"%s\" domain added emulated\n", inDomain );
+ }
+ else if( inResult == DNSServiceDomainEnumerationReplyAddDomainDefault )
+ {
+ fprintf( stdout, "\"%s\" default domain added emulated\n", inDomain );
+ }
+ else if( inResult == DNSServiceDomainEnumerationReplyRemoveDomain )
+ {
+ fprintf( stdout, "\"%s\" domain removed emulated\n", inDomain );
+ }
+ else
+ {
+ fprintf( stdout, "### unknown emulated domain enumeration callback result (%d)\n", inResult );
+ }
+}
+
+//===========================================================================================================================
+// EmulatedResolverCallBack
+//===========================================================================================================================
+
+static void
+ EmulatedResolverCallBack(
+ struct sockaddr * inInterfaceAddr,
+ struct sockaddr * inAddr,
+ const char * inTextRecord,
+ DNSServiceDiscoveryReplyFlags inFlags,
+ void * inContext )
+{
+ struct sockaddr_in * ifSin4;
+ struct sockaddr_in * sin4;
+ char ifIP[ 64 ];
+ char ip[ 64 ];
+
+ DNS_UNUSED( inFlags );
+ DNS_UNUSED( inContext );
+
+ ifSin4 = (struct sockaddr_in *) inInterfaceAddr;
+ sin4 = (struct sockaddr_in *) inAddr;
+
+ fprintf( stdout, "service resolved to %s:%d on interface %s with text \"%s\"\n",
+ IPv4ToString( *( (DNSOpaque32 *) &sin4->sin_addr.s_addr ), ip ),
+ ntohs( sin4->sin_port ),
+ IPv4ToString( *( (DNSOpaque32 *) &ifSin4->sin_addr.s_addr ), ifIP ),
+ inTextRecord ? inTextRecord : "" );
+}
+
+//===========================================================================================================================
+// EmulatedResolverCallBack
+//===========================================================================================================================
+
+static void EmulatedRegistrationCallBack( DNSServiceRegistrationReplyErrorType inResult, void *inContext )
+{
+ DNS_UNUSED( inContext );
+
+ if( inResult == kDNSServiceDiscoveryNoError )
+ {
+ fprintf( stdout, "service name registered successfully\n" );
+ }
+ else
+ {
+ fprintf( stdout, "service registration failed( %d)\n", inResult );
+ }
+}
+
+//===========================================================================================================================
+// IPv4ToString
+//===========================================================================================================================
+
+static char * IPv4ToString( DNSOpaque32 inIP, char *outString )
+{
+ sprintf( outString, "%u.%u.%u.%u", inIP.v8[ 0 ], inIP.v8[ 1 ], inIP.v8[ 2 ], inIP.v8[ 3 ] );
+ return( outString );
+}
--- /dev/null
+/*
+ * 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: ToolPrefixWindows.h,v $
+Revision 1.2 2003/08/20 07:06:34 bradley
+Update to APSL 2.0. Updated change history to match other mDNSResponder files.
+
+Revision 1.1 2003/08/20 06:01:19 bradley
+Prefix files for CodeWarrior Windows builds to set compile options.
+
+*/
+
+#ifndef __TOOL_PREFIX_WINDOWS__
+#define __TOOL_PREFIX_WINDOWS__
+
+#define DEBUG 0
+
+#endif // __TOOL_PREFIX_WINDOWS__
--- /dev/null
+/*
+ * 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: ToolPrefixWindowsDebug.h,v $
+Revision 1.2 2003/08/20 07:06:34 bradley
+Update to APSL 2.0. Updated change history to match other mDNSResponder files.
+
+Revision 1.1 2003/08/20 06:01:19 bradley
+Prefix files for CodeWarrior Windows builds to set compile options.
+
+*/
+
+#ifndef __TOOL_PREFIX_WINDOWS_DEBUG__
+#define __TOOL_PREFIX_WINDOWS_DEBUG__
+
+#define DEBUG 1
+#define MDNS_DEBUGMSGS 1
+
+#endif // __TOOL_PREFIX_WINDOWS_DEBUG__
--- /dev/null
+Microsoft Visual Studio Solution File, Format Version 7.00
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Tool", "ToolWin32.vcproj", "{F66EFE7E-50A6-44D4-87C7-742B303BA852}"
+EndProject
+Global
+ GlobalSection(SolutionConfiguration) = preSolution
+ ConfigName.0 = Debug
+ ConfigName.1 = Release
+ EndGlobalSection
+ GlobalSection(ProjectDependencies) = postSolution
+ EndGlobalSection
+ GlobalSection(ProjectConfiguration) = postSolution
+ {F66EFE7E-50A6-44D4-87C7-742B303BA852}.Debug.ActiveCfg = Debug|Win32
+ {F66EFE7E-50A6-44D4-87C7-742B303BA852}.Debug.Build.0 = Debug|Win32
+ {F66EFE7E-50A6-44D4-87C7-742B303BA852}.Release.ActiveCfg = Release|Win32
+ {F66EFE7E-50A6-44D4-87C7-742B303BA852}.Release.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ EndGlobalSection
+ GlobalSection(ExtensibilityAddIns) = postSolution
+ EndGlobalSection
+EndGlobal
--- /dev/null
+<?xml version="1.0" encoding = "Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.00"
+ Name="Tool"
+ ProjectGUID="{F66EFE7E-50A6-44D4-87C7-742B303BA852}"
+ Keyword="Win32Proj">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="Debug"
+ IntermediateDirectory="Debug"
+ ConfigurationType="1"
+ CharacterSet="1">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\..\mDNSCore;..\..\..\mDNSWindows;..\..\DNSServices"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+ StringPooling="TRUE"
+ MinimalRebuild="TRUE"
+ BasicRuntimeChecks="3"
+ SmallerTypeCheck="FALSE"
+ RuntimeLibrary="1"
+ BufferSecurityCheck="TRUE"
+ ForceConformanceInForLoopScope="TRUE"
+ UsePrecompiledHeader="2"
+ BrowseInformation="1"
+ WarningLevel="4"
+ WarnAsError="TRUE"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="4"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib"
+ OutputFile="$(OutDir)/Tool.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile="$(OutDir)/Tool.pdb"
+ SubSystem="1"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="Release"
+ IntermediateDirectory="Release"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="FALSE">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ InlineFunctionExpansion="2"
+ FavorSizeOrSpeed="2"
+ OmitFramePointers="TRUE"
+ AdditionalIncludeDirectories="..\..\..\mDNSCore;..\..\..\mDNSWindows;..\..\DNSServices"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+ StringPooling="TRUE"
+ ExceptionHandling="FALSE"
+ RuntimeLibrary="0"
+ BufferSecurityCheck="FALSE"
+ EnableFunctionLevelLinking="FALSE"
+ DisableLanguageExtensions="FALSE"
+ ForceConformanceInForLoopScope="TRUE"
+ UsePrecompiledHeader="2"
+ WarningLevel="4"
+ WarnAsError="TRUE"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="3"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib"
+ OutputFile="$(OutDir)/Tool.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="TRUE"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ </Configuration>
+ <Configuration
+ Name="all|Win32"
+ OutputDirectory="all"
+ IntermediateDirectory="all"
+ ConfigurationType="1">
+ <Tool
+ Name="VCCLCompilerTool"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ </Configuration>
+ </Configurations>
+ <Files>
+ <Filter
+ Name="Rendezvous"
+ Filter="">
+ <File
+ RelativePath="..\..\DNSServices\DNSServiceDiscovery.c">
+ </File>
+ <File
+ RelativePath="..\..\DNSServices\DNSServiceDiscovery.h">
+ </File>
+ <File
+ RelativePath="..\..\DNSServices\DNSServices.c">
+ </File>
+ <File
+ RelativePath="..\..\DNSServices\DNSServices.h">
+ </File>
+ <File
+ RelativePath="..\..\..\mDNSCore\mDNS.c">
+ </File>
+ <File
+ RelativePath="..\..\..\mDNSCore\mDNSClientAPI.h">
+ </File>
+ <File
+ RelativePath="..\..\..\mDNSCore\mDNSDebug.h">
+ </File>
+ <File
+ RelativePath="..\..\..\mDNSCore\mDNSPlatformFunctions.h">
+ </File>
+ <File
+ RelativePath="..\..\mDNSWin32.c">
+ </File>
+ <File
+ RelativePath="..\..\mDNSWin32.h">
+ </File>
+ </Filter>
+ <File
+ RelativePath="Tool.c">
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
--- /dev/null
+/*
+ * 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: DNSServiceDiscovery.c,v $
+Revision 1.2 2003/08/20 07:06:34 bradley
+Update to APSL 2.0. Updated change history to match other mDNSResponder files.
+
+Revision 1.1 2003/08/20 06:04:45 bradley
+Platform-neutral DNSServices-based emulation layer for the Mac OS X DNSServiceDiscovery API.
+
+*/
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if( macintosh || __MACH__ )
+
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+
+#elif( defined( _MSC_VER ) || defined( __MWERKS__ ) )
+
+ #pragma warning( disable:4054 ) // Disable "type cast : from function pointer to data pointer".
+ #pragma warning( disable:4055 ) // Disable "type cast : from data pointer to function pointer".
+ #pragma warning( disable:4127 ) // Disable "conditional expression is constant" warning for debug macros.
+ #pragma warning( disable:4152 ) // Disable "nonstandard extension, function/data pointer conversion in expression".
+
+ #define WIN32_LEAN_AND_MEAN // Needed to avoid redefinitions by Windows interfaces.
+
+ #include <winsock2.h>
+
+#endif
+
+#include "mDNSClientAPI.h"
+#include "mDNSPlatformFunctions.h"
+#include "DNSServices.h"
+
+#include "DNSServiceDiscovery.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#if 0
+#pragma mark == Constants & Types ==
+#endif
+
+//===========================================================================================================================
+// Constants & Types
+//===========================================================================================================================
+
+#define DEBUG_NAME "[DNSServiceDiscovery] "
+
+typedef enum
+{
+ kDNSServiceDiscoveryObjectTypeRegistration = 1,
+ kDNSServiceDiscoveryObjectTypeDomainEnumeration = 2,
+ kDNSServiceDiscoveryObjectTypeBrowser = 3,
+ kDNSServiceDiscoveryObjectTypeResolver = 4
+
+} DNSServiceDiscoveryObjectType;
+
+typedef struct _dns_service_discovery_t _dns_service_discovery_t;
+struct _dns_service_discovery_t
+{
+ DNSServiceDiscoveryObjectType type;
+ void * ref;
+ void * callback;
+ void * context;
+};
+
+#if 0
+#pragma mark == Macros ==
+#endif
+
+//===========================================================================================================================
+// Macros
+//===========================================================================================================================
+
+// Emulate Mac OS debugging macros for non-Mac platforms.
+
+#if( !TARGET_OS_MAC )
+ #define check(assertion)
+ #define check_string( assertion, cstring )
+ #define check_noerr(err)
+ #define check_noerr_string( error, cstring )
+ #define debug_string( cstring )
+ #define require( assertion, label ) do { if( !(assertion) ) goto label; } while(0)
+ #define require_string( assertion, label, string ) require(assertion, label)
+ #define require_quiet( assertion, label ) require( assertion, label )
+ #define require_noerr( error, label ) do { if( (error) != 0 ) goto label; } while(0)
+ #define require_noerr_quiet( assertion, label ) require_noerr( assertion, label )
+ #define require_noerr_action( error, label, action ) do { if( (error) != 0 ) { {action;}; goto label; } } while(0)
+ #define require_noerr_action_quiet( assertion, label, action ) require_noerr_action( assertion, label, action )
+ #define require_action( assertion, label, action ) do { if( !(assertion) ) { {action;}; goto label; } } while(0)
+ #define require_action_quiet( assertion, label, action ) require_action( assertion, label, action )
+ #define require_action_string( assertion, label, action, cstring ) do { if( !(assertion) ) { {action;}; goto label; } } while(0)
+#endif
+
+#if 0
+#pragma mark == Prototypes ==
+#endif
+
+//===========================================================================================================================
+// Prototypes
+//===========================================================================================================================
+
+DNS_LOCAL void
+ DNSServiceRegistrationPrivateCallBack(
+ void * inContext,
+ DNSRegistrationRef inRef,
+ DNSStatus inStatusCode,
+ const DNSRegistrationEvent * inEvent );
+
+DNS_LOCAL void
+ DNSServiceDomainEnumerationPrivateCallBack(
+ void * inContext,
+ DNSBrowserRef inRef,
+ DNSStatus inStatusCode,
+ const DNSBrowserEvent * inEvent );
+
+DNS_LOCAL void
+ DNSServiceBrowserPrivateCallBack(
+ void * inContext,
+ DNSBrowserRef inRef,
+ DNSStatus inStatusCode,
+ const DNSBrowserEvent * inEvent );
+
+DNS_LOCAL void
+ DNSServiceResolverPrivateCallBack(
+ void * inContext,
+ DNSResolverRef inRef,
+ DNSStatus inStatusCode,
+ const DNSResolverEvent * inEvent );
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+// DNSServiceRegistrationCreate
+//===========================================================================================================================
+
+dns_service_discovery_ref
+ DNSServiceRegistrationCreate(
+ const char * inName,
+ const char * inType,
+ const char * inDomain,
+ uint16_t inPort,
+ const char * inTextRecord,
+ DNSServiceRegistrationReply inCallBack,
+ void * inContext )
+{
+ DNSStatus err;
+ dns_service_discovery_ref result;
+ dns_service_discovery_ref obj;
+ void * txt;
+ size_t txtSize;
+ DNSRegistrationRef registration;
+
+ result = NULL;
+ txt = NULL;
+ txtSize = 0;
+
+ // Allocate and initialize the object.
+
+ obj = (dns_service_discovery_ref) malloc( sizeof( *obj ) );
+ require_action( obj, exit, err = kDNSNoMemoryErr );
+
+ obj->type = kDNSServiceDiscoveryObjectTypeRegistration;
+ obj->ref = NULL;
+ obj->callback = inCallBack;
+ obj->context = inContext;
+
+ // Create the underlying registration. Build a \001-escaped text record if needed.
+
+ if( inTextRecord )
+ {
+ err = DNSDynamicTextRecordBuildEscaped( inTextRecord, &txt, &txtSize );
+ require_noerr( err, exit );
+ }
+
+ err = DNSRegistrationCreate( kDNSRegistrationFlagPreFormattedTextRecord, inName, inType, inDomain, inPort, txt,
+ (DNSCount) txtSize, NULL, NULL, DNSServiceRegistrationPrivateCallBack, obj, ®istration );
+ require_noerr( err, exit );
+ obj->ref = registration;
+
+ // Success!
+
+ result = obj;
+ obj = NULL;
+
+exit:
+ if( txt )
+ {
+ DNSDynamicTextRecordRelease( txt );
+ }
+ if( obj )
+ {
+ DNSServiceDiscoveryDeallocate( obj );
+ }
+ return( result );
+}
+
+//===========================================================================================================================
+// DNSServiceRegistrationPrivateCallBack
+//===========================================================================================================================
+
+DNS_LOCAL void
+ DNSServiceRegistrationPrivateCallBack(
+ void * inContext,
+ DNSRegistrationRef inRef,
+ DNSStatus inStatusCode,
+ const DNSRegistrationEvent * inEvent )
+{
+ dns_service_discovery_ref obj;
+ DNSServiceRegistrationReply callback;
+
+ DNS_UNUSED( inRef );
+ DNS_UNUSED( inStatusCode );
+
+ check( inContext );
+ obj = (dns_service_discovery_ref) inContext;
+ check( obj->callback );
+ callback = (DNSServiceRegistrationReply) obj->callback;
+
+ switch( inEvent->type )
+ {
+ case kDNSRegistrationEventTypeRegistered:
+ debugf( DEBUG_NAME "name registered and active\n" );
+
+ if( callback )
+ {
+ callback( kDNSServiceDiscoveryNoError, obj->context );
+ }
+ break;
+
+ case kDNSRegistrationEventTypeNameCollision:
+ debugf( DEBUG_NAME "name in use, please choose another name\n" );
+
+ if( callback )
+ {
+ callback( kDNSServiceDiscoveryNameConflict, obj->context );
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+//===========================================================================================================================
+// DNSServiceRegistrationAddRecord
+//===========================================================================================================================
+
+DNSRecordReference
+ DNSServiceRegistrationAddRecord(
+ dns_service_discovery_ref inRef,
+ uint16_t inRRType,
+ uint16_t inRDLength,
+ const char * inRData,
+ uint32_t inTTL )
+{
+ DNS_UNUSED( inRef );
+ DNS_UNUSED( inRRType );
+ DNS_UNUSED( inRDLength );
+ DNS_UNUSED( inRData );
+ DNS_UNUSED( inTTL );
+
+ debugf( DEBUG_NAME "DNSServiceRegistrationAddRecord is currently not supported\n" );
+ return( 0 );
+}
+
+//===========================================================================================================================
+// DNSServiceRegistrationUpdateRecord
+//===========================================================================================================================
+
+DNSServiceRegistrationReplyErrorType
+ DNSServiceRegistrationUpdateRecord(
+ dns_service_discovery_ref inRef,
+ DNSRecordReference inRecordRef,
+ uint16_t inRDLength,
+ const char * inRData,
+ uint32_t inTTL )
+{
+ DNS_UNUSED( inRef );
+ DNS_UNUSED( inRecordRef );
+ DNS_UNUSED( inRDLength );
+ DNS_UNUSED( inRData );
+ DNS_UNUSED( inTTL );
+
+ debugf( DEBUG_NAME "DNSServiceRegistrationUpdateRecord is currently not supported\n" );
+ return( kDNSServiceDiscoveryUnsupportedErr );
+}
+
+//===========================================================================================================================
+// DNSServiceRegistrationRemoveRecord
+//===========================================================================================================================
+
+DNSServiceRegistrationReplyErrorType
+ DNSServiceRegistrationRemoveRecord(
+ dns_service_discovery_ref inRef,
+ DNSRecordReference inRecordRef )
+{
+ DNS_UNUSED( inRef );
+ DNS_UNUSED( inRecordRef );
+
+ debugf( DEBUG_NAME "DNSServiceRegistrationRemoveRecord is currently not supported\n" );
+ return( kDNSServiceDiscoveryUnsupportedErr );
+}
+
+//===========================================================================================================================
+// DNSServiceDomainEnumerationCreate
+//===========================================================================================================================
+
+dns_service_discovery_ref
+ DNSServiceDomainEnumerationCreate(
+ int inRegistrationDomains,
+ DNSServiceDomainEnumerationReply inCallBack,
+ void * inContext )
+{
+ DNSStatus err;
+ dns_service_discovery_ref result;
+ dns_service_discovery_ref obj;
+ DNSBrowserRef browser;
+ DNSBrowserFlags flags;
+
+ result = NULL;
+ browser = NULL;
+
+ // Allocate and initialize the object.
+
+ obj = (dns_service_discovery_ref) malloc( sizeof( *obj ) );
+ require_action( obj, exit, err = kDNSNoMemoryErr );
+
+ obj->type = kDNSServiceDiscoveryObjectTypeDomainEnumeration;
+ obj->ref = NULL;
+ obj->callback = inCallBack;
+ obj->context = inContext;
+
+ // Create the underlying browser and start searching for domains.
+
+ err = DNSBrowserCreate( 0, DNSServiceDomainEnumerationPrivateCallBack, obj, &browser );
+ require_noerr( err, exit );
+ obj->ref = browser;
+
+ if( inRegistrationDomains )
+ {
+ flags = kDNSBrowserFlagRegistrationDomainsOnly;
+ }
+ else
+ {
+ flags = 0;
+ }
+ err = DNSBrowserStartDomainSearch( browser, flags );
+ require_noerr( err, exit );
+
+ // Success!
+
+ result = obj;
+ browser = NULL;
+ obj = NULL;
+
+exit:
+ if( browser )
+ {
+ DNSBrowserRelease( browser, 0 );
+ }
+ if( obj )
+ {
+ DNSServiceDiscoveryDeallocate( obj );
+ }
+ return( result );
+}
+
+//===========================================================================================================================
+// DNSServiceDomainEnumerationPrivateCallBack
+//===========================================================================================================================
+
+DNS_LOCAL void
+ DNSServiceDomainEnumerationPrivateCallBack(
+ void * inContext,
+ DNSBrowserRef inRef,
+ DNSStatus inStatusCode,
+ const DNSBrowserEvent * inEvent )
+{
+ dns_service_discovery_ref obj;
+ DNSServiceDomainEnumerationReply callback;
+
+ DNS_UNUSED( inRef );
+ DNS_UNUSED( inStatusCode );
+
+ check( inContext );
+ obj = (dns_service_discovery_ref) inContext;
+ check( obj->callback );
+ callback = (DNSServiceDomainEnumerationReply) obj->callback;
+
+ switch( inEvent->type )
+ {
+ case kDNSBrowserEventTypeAddDomain:
+ debugf( DEBUG_NAME "add domain \"%s\"\n", inEvent->data.addDomain.domain );
+
+ if( callback )
+ {
+ callback( DNSServiceDomainEnumerationReplyAddDomain, inEvent->data.addDomain.domain,
+ DNSServiceDiscoverReplyFlagsFinished, obj->context );
+ }
+ break;
+
+ case kDNSBrowserEventTypeAddDefaultDomain:
+ debugf( DEBUG_NAME "add default domain \"%s\"\n", inEvent->data.addDefaultDomain.domain );
+
+ if( callback )
+ {
+ callback( DNSServiceDomainEnumerationReplyAddDomainDefault, inEvent->data.addDefaultDomain.domain,
+ DNSServiceDiscoverReplyFlagsFinished, obj->context );
+ }
+ break;
+
+ case kDNSBrowserEventTypeRemoveDomain:
+ debugf( DEBUG_NAME "add default domain \"%s\"\n", inEvent->data.removeDomain.domain );
+
+ if( callback )
+ {
+ callback( DNSServiceDomainEnumerationReplyRemoveDomain, inEvent->data.removeDomain.domain,
+ DNSServiceDiscoverReplyFlagsFinished, obj->context );
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+//===========================================================================================================================
+// DNSServiceBrowserCreate
+//===========================================================================================================================
+
+dns_service_discovery_ref
+ DNSServiceBrowserCreate(
+ const char * inType,
+ const char * inDomain,
+ DNSServiceBrowserReply inCallBack,
+ void * inContext )
+{
+ DNSStatus err;
+ dns_service_discovery_ref result;
+ dns_service_discovery_ref obj;
+ DNSBrowserRef browser;
+
+ result = NULL;
+ browser = NULL;
+
+ // Allocate and initialize the object.
+
+ obj = (dns_service_discovery_ref) malloc( sizeof( *obj ) );
+ require_action( obj, exit, err = kDNSNoMemoryErr );
+
+ obj->type = kDNSServiceDiscoveryObjectTypeBrowser;
+ obj->ref = NULL;
+ obj->callback = inCallBack;
+ obj->context = inContext;
+
+ // Create the underlying browser and start searching for domains.
+
+ err = DNSBrowserCreate( 0, DNSServiceBrowserPrivateCallBack, obj, &browser );
+ require_noerr( err, exit );
+ obj->ref = browser;
+
+ err = DNSBrowserStartServiceSearch( browser, 0, inType, inDomain );
+ require_noerr( err, exit );
+
+ // Success!
+
+ result = obj;
+ browser = NULL;
+ obj = NULL;
+
+exit:
+ if( browser )
+ {
+ DNSBrowserRelease( browser, 0 );
+ }
+ if( obj )
+ {
+ DNSServiceDiscoveryDeallocate( obj );
+ }
+ return( result );
+}
+
+//===========================================================================================================================
+// DNSServiceBrowserPrivateCallBack
+//===========================================================================================================================
+
+DNS_LOCAL void
+ DNSServiceBrowserPrivateCallBack(
+ void * inContext,
+ DNSBrowserRef inRef,
+ DNSStatus inStatusCode,
+ const DNSBrowserEvent * inEvent )
+{
+ dns_service_discovery_ref obj;
+ DNSServiceBrowserReply callback;
+
+ DNS_UNUSED( inRef );
+ DNS_UNUSED( inStatusCode );
+
+ check( inContext );
+ obj = (dns_service_discovery_ref) inContext;
+ check( obj->callback );
+ callback = (DNSServiceBrowserReply) obj->callback;
+
+ switch( inEvent->type )
+ {
+ case kDNSBrowserEventTypeAddService:
+ debugf( DEBUG_NAME "add service \"%s.%s%s\"\n",
+ inEvent->data.addService.name,
+ inEvent->data.addService.type,
+ inEvent->data.addService.domain );
+
+ if( callback )
+ {
+ callback( DNSServiceBrowserReplyAddInstance,
+ inEvent->data.addService.name,
+ inEvent->data.addService.type,
+ inEvent->data.addService.domain,
+ DNSServiceDiscoverReplyFlagsFinished,
+ obj->context );
+ }
+ break;
+
+ case kDNSBrowserEventTypeRemoveService:
+ debugf( DEBUG_NAME "remove service \"%s.%s%s\"\n",
+ inEvent->data.removeService.name,
+ inEvent->data.removeService.type,
+ inEvent->data.removeService.domain );
+
+ if( callback )
+ {
+ callback( DNSServiceBrowserReplyRemoveInstance,
+ inEvent->data.removeService.name,
+ inEvent->data.removeService.type,
+ inEvent->data.removeService.domain,
+ DNSServiceDiscoverReplyFlagsFinished,
+ obj->context );
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+//===========================================================================================================================
+// DNSServiceResolverResolve
+//===========================================================================================================================
+
+dns_service_discovery_ref
+ DNSServiceResolverResolve(
+ const char * inName,
+ const char * inType,
+ const char * inDomain,
+ DNSServiceResolverReply inCallBack,
+ void * inContext )
+{
+ DNSStatus err;
+ dns_service_discovery_ref result;
+ dns_service_discovery_ref obj;
+ DNSResolverRef resolver;
+
+ result = NULL;
+
+ // Allocate and initialize the object.
+
+ obj = (dns_service_discovery_ref) malloc( sizeof( *obj ) );
+ require_action( obj, exit, err = kDNSNoMemoryErr );
+
+ obj->type = kDNSServiceDiscoveryObjectTypeResolver;
+ obj->ref = NULL;
+ obj->callback = inCallBack;
+ obj->context = inContext;
+
+ // Create the underlying resolver and start searching for domains.
+
+ err = DNSResolverCreate( 0, inName, inType, inDomain, DNSServiceResolverPrivateCallBack, obj, NULL, &resolver );
+ require_noerr( err, exit );
+ obj->ref = resolver;
+
+ // Success!
+
+ result = obj;
+ obj = NULL;
+
+exit:
+ if( obj )
+ {
+ DNSServiceDiscoveryDeallocate( obj );
+ }
+ return( result );
+}
+
+//===========================================================================================================================
+// DNSServiceResolverPrivateCallBack
+//===========================================================================================================================
+
+DNS_LOCAL void
+ DNSServiceResolverPrivateCallBack(
+ void * inContext,
+ DNSResolverRef inRef,
+ DNSStatus inStatusCode,
+ const DNSResolverEvent * inEvent )
+{
+ dns_service_discovery_ref obj;
+ DNSServiceResolverReply callback;
+ struct sockaddr_in interfaceAddr;
+ struct sockaddr_in addr;
+
+ DNS_UNUSED( inRef );
+ DNS_UNUSED( inStatusCode );
+
+ check( inContext );
+ obj = (dns_service_discovery_ref) inContext;
+ check( obj->callback );
+ callback = (DNSServiceResolverReply) obj->callback;
+
+ switch( inEvent->type )
+ {
+ case kDNSResolverEventTypeResolved:
+ debugf( DEBUG_NAME "resolved \"%s.%s%s\"\n",
+ inEvent->data.resolved.name,
+ inEvent->data.resolved.type,
+ inEvent->data.resolved.domain );
+
+ memset( &interfaceAddr, 0, sizeof( interfaceAddr ) );
+ interfaceAddr.sin_family = AF_INET;
+ interfaceAddr.sin_port = 0;
+ interfaceAddr.sin_addr.s_addr = inEvent->data.resolved.interfaceIP.u.ipv4.addr.v32;
+
+ memset( &addr, 0, sizeof( addr ) );
+ addr.sin_family = AF_INET;
+ addr.sin_port = inEvent->data.resolved.address.u.ipv4.port.v16;
+ addr.sin_addr.s_addr = inEvent->data.resolved.address.u.ipv4.addr.v32;
+
+ if( callback )
+ {
+ callback( (struct sockaddr *) &interfaceAddr, (struct sockaddr *) &addr, inEvent->data.resolved.textRecord,
+ DNSServiceDiscoverReplyFlagsFinished, obj->context );
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+//===========================================================================================================================
+// DNSServiceDiscoveryMachPort
+//===========================================================================================================================
+
+mach_port_t DNSServiceDiscoveryMachPort( dns_service_discovery_ref inRef )
+{
+ DNS_UNUSED( inRef );
+
+ debugf( DEBUG_NAME "DNSServiceDiscoveryMachPort is not supported\n" );
+ return( 0 );
+}
+
+//===========================================================================================================================
+// DNSServiceDiscoveryDeallocate
+//===========================================================================================================================
+
+void DNSServiceDiscoveryDeallocate( dns_service_discovery_ref inRef )
+{
+ _dns_service_discovery_t * obj;
+ DNSStatus err;
+
+ check( inRef );
+ check( inRef->ref );
+
+ obj = (_dns_service_discovery_t *) inRef;
+ switch( obj->type )
+ {
+ case kDNSServiceDiscoveryObjectTypeRegistration:
+ if( inRef->ref )
+ {
+ err = DNSRegistrationRelease( (DNSRegistrationRef) inRef->ref, 0 );
+ check_noerr( err );
+ }
+ free( inRef );
+ break;
+
+ case kDNSServiceDiscoveryObjectTypeDomainEnumeration:
+ if( inRef->ref )
+ {
+ err = DNSBrowserRelease( (DNSBrowserRef) inRef->ref, 0 );
+ check_noerr( err );
+ }
+ free( inRef );
+ break;
+
+ case kDNSServiceDiscoveryObjectTypeBrowser:
+ if( inRef->ref )
+ {
+ err = DNSBrowserRelease( (DNSBrowserRef) inRef->ref, 0 );
+ check_noerr( err );
+ }
+ free( inRef );
+ break;
+
+ case kDNSServiceDiscoveryObjectTypeResolver:
+ if( inRef->ref )
+ {
+ err = DNSResolverRelease( (DNSResolverRef) inRef->ref, 0 );
+ check_noerr( err );
+ }
+ free( inRef );
+ break;
+
+ default:
+ debugf( DEBUG_NAME "unknown object type (%d)\n", obj->type );
+ break;
+ }
+}
+
+//===========================================================================================================================
+// DNSServiceDiscovery_handleReply
+//===========================================================================================================================
+
+void DNSServiceDiscovery_handleReply( void *inReplyMessage )
+{
+ DNS_UNUSED( inReplyMessage );
+
+ debugf( DEBUG_NAME "DNSServiceDiscovery_handleReply is not supported\n" );
+}
+
+#ifdef __cplusplus
+ }
+#endif
--- /dev/null
+/*
+ * 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: DNSServiceDiscovery.h,v $
+Revision 1.2 2003/08/20 07:06:34 bradley
+Update to APSL 2.0. Updated change history to match other mDNSResponder files.
+
+Revision 1.1 2003/08/20 06:04:45 bradley
+Platform-neutral DNSServices-based emulation layer for the Mac OS X DNSServiceDiscovery API.
+
+*/
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @header DNSServiceDiscovery
+
+ @abstract DNSServiceDiscovery emulation using DNSServices.
+*/
+
+#ifndef __DNS_SERVICE_DISCOVERY__
+#define __DNS_SERVICE_DISCOVERY__
+
+#include <stddef.h>
+
+#if( __MACH__ )
+
+ #include <mach/mach_types.h>
+
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <sys/cdefs.h>
+
+ #include <netinet/in.h>
+
+#elif( defined( __MWERKS__ ) )
+
+ #include <stdint.h>
+
+#elif( defined( _MSC_VER ) )
+
+ typedef signed char int8_t; // C99 stdint.h not supported in VC++/VS.NET yet.
+ typedef unsigned char uint8_t; // C99 stdint.h not supported in VC++/VS.NET yet.
+ typedef signed short int16_t; // C99 stdint.h not supported in VC++/VS.NET yet.
+ typedef unsigned short uint16_t; // C99 stdint.h not supported in VC++/VS.NET yet.
+ typedef signed long int32_t; // C99 stdint.h not supported in VC++/VS.NET yet.
+ typedef unsigned long uint32_t; // C99 stdint.h not supported in VC++/VS.NET yet.
+
+#endif
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+// Note: The following is mostly copied from DNSServiceDiscovery.h.
+
+// Compatibility types.
+
+#if( !__MACH__ )
+ typedef int mach_port_t;
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef dns_service_discovery_ref
+
+ @abstract Reference to a DNS Service Discovery object.
+*/
+
+typedef struct _dns_service_discovery_t * dns_service_discovery_ref;
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @enum DNSServiceRegistrationReplyErrorType
+
+ @abstract Error codes.
+*/
+
+typedef enum
+{
+ kDNSServiceDiscoveryWaiting = 1,
+ kDNSServiceDiscoveryNoError = 0,
+
+ // mDNS Error codes are in the range
+ // FFFE FF00 (-65792) to FFFE FFFF (-65537)
+
+ kDNSServiceDiscoveryUnknownErr = -65537, // 0xFFFE FFFF
+ kDNSServiceDiscoveryNoSuchNameErr = -65538,
+ kDNSServiceDiscoveryNoMemoryErr = -65539,
+ kDNSServiceDiscoveryBadParamErr = -65540,
+ kDNSServiceDiscoveryBadReferenceErr = -65541,
+ kDNSServiceDiscoveryBadStateErr = -65542,
+ kDNSServiceDiscoveryBadFlagsErr = -65543,
+ kDNSServiceDiscoveryUnsupportedErr = -65544,
+ kDNSServiceDiscoveryNotInitializedErr = -65545,
+ kDNSServiceDiscoveryNoCache = -65546,
+ kDNSServiceDiscoveryAlreadyRegistered = -65547,
+ kDNSServiceDiscoveryNameConflict = -65548,
+ kDNSServiceDiscoveryInvalid = -65549,
+ kDNSServiceDiscoveryMemFree = -65792 // 0xFFFE FF00
+
+} DNSServiceRegistrationReplyErrorType;
+
+typedef uint32_t DNSRecordReference;
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!
+ @function DNSServiceResolver_handleReply
+
+ @param replyMsg The Mach message.
+
+ @description
+
+ This function should be called with the Mach message sent to the port returned by the call to DNSServiceResolverResolve.
+ The reply message will be interpreted and will result in a call to the specified callout function.
+*/
+
+void DNSServiceDiscovery_handleReply( void *replyMsg );
+
+/* Service Registration */
+
+typedef void (*DNSServiceRegistrationReply) (
+ DNSServiceRegistrationReplyErrorType errorCode,
+ void *context
+);
+
+/*!
+@function DNSServiceRegistrationCreate
+ @description Register a named service with DNS Service Discovery
+ @param name The name of this service instance (e.g. "Steve's Printer")
+ @param regtype The service type (e.g. "_printer._tcp." -- see
+ RFC 2782 (DNS SRV) and <http://www.iana.org/assignments/port-numbers>)
+ @param domain The domain in which to register the service (e.g. "apple.com.")
+ @param port The local port on which this service is being offered (in network byte order)
+ @param txtRecord Optional protocol-specific additional information
+ @param callBack The DNSServiceRegistrationReply function to be called
+ @param context A user specified context which will be passed to the callout function.
+ @result A dns_registration_t
+*/
+dns_service_discovery_ref DNSServiceRegistrationCreate
+(
+ const char *name,
+ const char *regtype,
+ const char *domain,
+ uint16_t port,
+ const char *txtRecord,
+ DNSServiceRegistrationReply callBack,
+ void *context
+);
+
+/***************************************************************************/
+/* DNS Domain Enumeration */
+
+typedef enum
+{
+ DNSServiceDomainEnumerationReplyAddDomain, // Domain found
+ DNSServiceDomainEnumerationReplyAddDomainDefault, // Domain found (and should be selected by default)
+ DNSServiceDomainEnumerationReplyRemoveDomain, // Domain has been removed from network
+} DNSServiceDomainEnumerationReplyResultType;
+
+typedef enum
+{
+ DNSServiceDiscoverReplyFlagsFinished,
+ DNSServiceDiscoverReplyFlagsMoreComing,
+} DNSServiceDiscoveryReplyFlags;
+
+typedef void (*DNSServiceDomainEnumerationReply) (
+ DNSServiceDomainEnumerationReplyResultType resultType, // One of DNSServiceDomainEnumerationReplyResultType
+ const char *replyDomain,
+ DNSServiceDiscoveryReplyFlags flags, // DNS Service Discovery reply flags information
+ void *context
+);
+
+/*!
+ @function DNSServiceDomainEnumerationCreate
+ @description Asynchronously create a DNS Domain Enumerator
+ @param registrationDomains A boolean indicating whether you are looking
+ for recommended registration domains
+ (e.g. equivalent to the AppleTalk zone list in the AppleTalk Control Panel)
+ or recommended browsing domains
+ (e.g. equivalent to the AppleTalk zone list in the Chooser).
+ @param callBack The function to be called when domains are found or removed
+ @param context A user specified context which will be passed to the callout function.
+ @result A dns_registration_t
+*/
+dns_service_discovery_ref DNSServiceDomainEnumerationCreate
+(
+ int registrationDomains,
+ DNSServiceDomainEnumerationReply callBack,
+ void *context
+);
+
+/***************************************************************************/
+/* DNS Service Browser */
+
+typedef enum
+{
+ DNSServiceBrowserReplyAddInstance, // Instance of service found
+ DNSServiceBrowserReplyRemoveInstance // Instance has been removed from network
+} DNSServiceBrowserReplyResultType;
+
+typedef void (*DNSServiceBrowserReply) (
+ DNSServiceBrowserReplyResultType resultType, // One of DNSServiceBrowserReplyResultType
+ const char *replyName,
+ const char *replyType,
+ const char *replyDomain,
+ DNSServiceDiscoveryReplyFlags flags, // DNS Service Discovery reply flags information
+ void *context
+);
+
+/*!
+ @function DNSServiceBrowserCreate
+ @description Asynchronously create a DNS Service browser
+ @param regtype The type of service
+ @param domain The domain in which to find the service
+ @param callBack The function to be called when service instances are found or removed
+ @param context A user specified context which will be passed to the callout function.
+ @result A dns_registration_t
+*/
+dns_service_discovery_ref DNSServiceBrowserCreate
+(
+ const char *regtype,
+ const char *domain,
+ DNSServiceBrowserReply callBack,
+ void *context
+);
+
+/***************************************************************************/
+/* Resolver requests */
+
+typedef void (*DNSServiceResolverReply) (
+ struct sockaddr *interfaceAddr, // Needed for scoped addresses like link-local
+ struct sockaddr *address,
+ const char *txtRecord,
+ DNSServiceDiscoveryReplyFlags flags, // DNS Service Discovery reply flags information
+ void *context
+);
+
+/*!
+@function DNSServiceResolverResolve
+ @description Resolved a named instance of a service to its address, port, and
+ (optionally) other demultiplexing information contained in the TXT record.
+ @param name The name of the service instance
+ @param regtype The type of service
+ @param domain The domain in which to find the service
+ @param callBack The DNSServiceResolverReply function to be called when the specified
+ address has been resolved.
+ @param context A user specified context which will be passed to the callout function.
+ @result A dns_registration_t
+*/
+
+dns_service_discovery_ref DNSServiceResolverResolve
+(
+ const char *name,
+ const char *regtype,
+ const char *domain,
+ DNSServiceResolverReply callBack,
+ void *context
+);
+
+/***************************************************************************/
+/* Mach port accessor and deallocation */
+
+/*!
+ @function DNSServiceDiscoveryMachPort
+ @description Returns the mach port for a dns_service_discovery_ref
+ @param registration A dns_service_discovery_ref as returned from DNSServiceRegistrationCreate
+ @result A mach reply port which will be sent messages as appropriate.
+ These messages should be passed to the DNSServiceDiscovery_handleReply
+ function. A NULL value indicates that no address was
+ specified or some other error occurred which prevented the
+ resolution from being started.
+*/
+mach_port_t DNSServiceDiscoveryMachPort(dns_service_discovery_ref dnsServiceDiscovery);
+
+/*!
+ @function DNSServiceDiscoveryDeallocate
+ @description Deallocates the DNS Service Discovery type / closes the connection to the server
+ @param dnsServiceDiscovery A dns_service_discovery_ref as returned from a creation or enumeration call
+ @result void
+*/
+void DNSServiceDiscoveryDeallocate(dns_service_discovery_ref dnsServiceDiscovery);
+
+/***************************************************************************/
+/* Registration updating */
+
+
+/*!
+ @function DNSServiceRegistrationAddRecord
+ @description Request that the mDNS Responder add the DNS Record of a specific type
+ @param dnsServiceDiscovery A dns_service_discovery_ref as returned from a DNSServiceRegistrationCreate call
+ @param rrtype A standard DNS Resource Record Type, from http://www.iana.org/assignments/dns-parameters
+ @param rdlen Length of the data
+ @param rdata Opaque binary Resource Record data, up to 64 kB.
+ @param ttl time to live for the added record.
+ @result DNSRecordReference An opaque reference that can be passed to the update and remove record calls. If an error occurs, this value will be zero or negative
+*/
+DNSRecordReference DNSServiceRegistrationAddRecord(dns_service_discovery_ref dnsServiceDiscovery, uint16_t rrtype, uint16_t rdlen, const char *rdata, uint32_t ttl);
+
+/*!
+ @function DNSServiceRegistrationUpdateRecord
+ @description Request that the mDNS Responder add the DNS Record of a specific type
+ @param dnsServiceDiscovery A dns_service_discovery_ref as returned from a DNSServiceRegistrationCreate call
+ @param dnsRecordReference A dnsRecordReference as returned from a DNSServiceRegistrationAddRecord call
+ @param rdlen Length of the data
+ @param rdata Opaque binary Resource Record data, up to 64 kB.
+ @param ttl time to live for the updated record.
+ @result DNSServiceRegistrationReplyErrorType If an error occurs, this value will be non zero
+*/
+DNSServiceRegistrationReplyErrorType DNSServiceRegistrationUpdateRecord(dns_service_discovery_ref ref, DNSRecordReference reference, uint16_t rdlen, const char *rdata, uint32_t ttl);
+
+/*!
+ @function DNSServiceRegistrationRemoveRecord
+ @description Request that the mDNS Responder remove the DNS Record(s) of a specific type
+ @param dnsServiceDiscovery A dns_service_discovery_ref as returned from a DNSServiceRegistrationCreate call
+ @param dnsRecordReference A dnsRecordReference as returned from a DNSServiceRegistrationAddRecord call
+ @result DNSServiceRegistrationReplyErrorType If an error occurs, this value will be non zero
+*/
+
+DNSServiceRegistrationReplyErrorType DNSServiceRegistrationRemoveRecord(dns_service_discovery_ref ref, DNSRecordReference reference);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif // __DNS_SERVICE_DISCOVERY__
--- /dev/null
+/*
+ * 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: DNSServices.c,v $
+Revision 1.15 2003/08/20 06:44:24 bradley
+Updated to latest internal version of the Rendezvous for Windows code: Added support for interface
+specific registrations; Added support for no-such-service registrations; Added support for host
+name registrations; Added support for host proxy and service proxy registrations; Added support for
+registration record updates (e.g. TXT record updates); Added support for using either a single C
+string TXT record, a raw, pre-formatted TXT record potentially containing multiple character string
+entries, or a C-string containing a Mac OS X-style \001-delimited set of TXT record character
+strings; Added support in resolve callbacks for providing both a simplified C-string for TXT records
+and a ptr/size for the raw TXT record data; Added utility routines for dynamically building TXT
+records from a variety of sources (\001-delimited, individual strings, etc.) and converting TXT
+records to various formats for use in apps; Added utility routines to validate DNS names, DNS
+service types, and TXT records; Moved to portable address representation unions (byte-stream vs host
+order integer) for consistency, to avoid swapping between host and network byte order, and for IPv6
+support; Removed dependence on modified mDNSCore: define structures and prototypes locally; Added
+support for automatically renaming services on name conflicts; Detect and correct TXT records from
+old versions of mDNS that treated a TXT record as an arbitrary block of data, but prevent other
+malformed TXT records from being accepted; Added many more error codes; Added complete HeaderDoc for
+all constants, structures, typedefs, macros, and functions. Various other minor cleanup and fixes.
+
+Revision 1.14 2003/08/14 02:19:56 cheshire
+<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
+
+Revision 1.13 2003/08/12 19:56:29 cheshire
+Update to APSL 2.0
+
+Revision 1.12 2003/07/23 00:00:04 cheshire
+Add comments
+
+Revision 1.11 2003/07/15 01:55:17 cheshire
+<rdar://problem/3315777> Need to implement service registration with subtypes
+
+Revision 1.10 2003/07/02 21:20:10 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.9 2003/05/26 03:21:30 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.8 2003/05/06 00:00:51 cheshire
+<rdar://problem/3248914> Rationalize naming of domainname manipulation functions
+
+Revision 1.7 2003/03/27 03:30:57 cheshire
+<rdar://problem/3210018> 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.6 2003/03/22 02:57:45 cheshire
+Updated mDNSWindows to use new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt")
+
+Revision 1.5 2003/02/20 00:59:04 cheshire
+Brought Windows code up to date so it complies with
+Josh Graessley's interface changes for IPv6 support.
+(Actual support for IPv6 on Windows will come later.)
+
+Revision 1.4 2002/09/21 20:44:56 zarzycki
+Added APSL info
+
+Revision 1.3 2002/09/20 08:36:50 bradley
+Fixed debug messages to output the correct information when resolving.
+
+Revision 1.2 2002/09/20 05:58:01 bradley
+DNS Services for Windows
+
+*/
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if( __MACH__ )
+ #include <CoreServices/CoreServices.h>
+#endif
+
+#include "mDNSClientAPI.h"
+#include "mDNSPlatformFunctions.h"
+
+#include "DNSServices.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#if 0
+#pragma mark == Preprocessor ==
+#endif
+
+//===========================================================================================================================
+// Preprocessor
+//===========================================================================================================================
+
+#if( defined( _MSC_VER ) )
+ #pragma warning( disable:4068 ) // Disable "unknown pragma" warning for "pragma unused".
+ #pragma warning( disable:4127 ) // Disable "conditional expression is constant" warning for debug macros.
+#endif
+
+#if 0
+#pragma mark == Constants ==
+#endif
+
+//===========================================================================================================================
+// Constants
+//===========================================================================================================================
+
+#define DEBUG_NAME "[DNSServices] "
+
+enum
+{
+ kDNSInitializeValidFlags = kDNSFlagAdvertise,
+
+ // Browser
+
+ kDNSBrowserCreateValidFlags = 0,
+ kDNSBrowserReleaseValidFlags = 0,
+ kDNSBrowserStartDomainSearchValidFlags = kDNSBrowserFlagRegistrationDomainsOnly,
+ kDNSBrowserStopDomainSearchValidFlags = 0,
+ kDNSBrowserStartServiceSearchValidFlags = kDNSBrowserFlagAutoResolve,
+ kDNSBrowserStopServiceSearchValidFlags = 0,
+
+ // Resolver
+
+ kDNSResolverCreateValidFlags = kDNSResolverFlagOneShot |
+ kDNSResolverFlagOnlyIfUnique |
+ kDNSResolverFlagAutoReleaseByName,
+ kDNSResolverReleaseValidFlags = 0,
+
+ // Service Registration
+
+ kDNSRegistrationCreateValidFlags = kDNSRegistrationFlagPreFormattedTextRecord |
+ kDNSRegistrationFlagAutoRenameOnConflict,
+ kDNSNoSuchServiceRegistrationCreateValidFlags = 0,
+ kDNSRegistrationReleaseValidFlags = 0,
+ kDNSRegistrationUpdateValidFlags = 0,
+
+ kDNSRegistrationFlagPrivateNoSuchService = ( 1 << 16 ),
+
+ // Domain Registration
+
+ kDNSDomainRegistrationCreateValidFlags = 0,
+ kDNSDomainRegistrationReleaseValidFlags = 0,
+
+ // Host Registration
+
+ kDNSHostRegistrationCreateValidFlags = kDNSHostRegistrationFlagOnlyIfNotFound |
+ kDNSHostRegistrationFlagAutoRenameOnConflict,
+ kDNSHostRegistrationReleaseValidFlags = 0
+};
+
+#define kDNSCountCacheEntryCountDefault 64
+
+#if 0
+#pragma mark == Structures ==
+#endif
+
+//===========================================================================================================================
+// Structures
+//===========================================================================================================================
+
+// Browser
+
+typedef struct DNSBrowser DNSBrowser;
+struct DNSBrowser
+{
+ DNSBrowser * next;
+ DNSBrowserFlags flags;
+ DNSBrowserCallBack callback;
+ void * callbackContext;
+ mDNSBool isDomainBrowsing;
+ DNSQuestion domainQuestion;
+ DNSQuestion defaultDomainQuestion;
+ DNSBrowserFlags domainSearchFlags;
+ mDNSBool isServiceBrowsing;
+ DNSQuestion serviceBrowseQuestion;
+ DNSBrowserFlags serviceSearchFlags;
+ char searchDomain[ 256 ];
+ char searchServiceType[ 256 ];
+};
+
+// Resolver
+
+typedef struct DNSResolver DNSResolver;
+struct DNSResolver
+{
+ DNSResolver * next;
+ DNSResolverFlags flags;
+ DNSResolverCallBack callback;
+ void * callbackContext;
+ DNSBrowserRef owner;
+ ServiceInfoQuery query;
+ ServiceInfo info;
+ mDNSBool isResolving;
+ char resolveName[ 256 ];
+ char resolveType[ 256 ];
+ char resolveDomain[ 256 ];
+};
+
+// Registration
+
+typedef struct DNSRegistration DNSRegistration;
+struct DNSRegistration
+{
+ DNSRegistration * next;
+ DNSRegistrationFlags flags;
+ DNSRegistrationCallBack callback;
+ void * callbackContext;
+ char interfaceName[ 256 ];
+ ServiceRecordSet set;
+
+ // WARNING: Do not add fields after the ServiceRecordSet. This is where oversized TXT record space is allocated.
+};
+
+// Domain Registration
+
+typedef struct DNSDomainRegistration DNSDomainRegistration;
+struct DNSDomainRegistration
+{
+ DNSDomainRegistration * next;
+ DNSDomainRegistrationFlags flags;
+ AuthRecord rr;
+};
+
+// Domain Registration
+
+typedef struct DNSHostRegistration DNSHostRegistration;
+struct DNSHostRegistration
+{
+ DNSHostRegistration * next;
+ domainlabel name;
+ domainlabel domain;
+ long refCount;
+ DNSHostRegistrationCallBack callback;
+ void * callbackContext;
+ DNSHostRegistrationFlags flags;
+ char interfaceName[ 256 ];
+ AuthRecord RR_A;
+ AuthRecord RR_PTR;
+};
+
+#if 0
+#pragma mark == Macros ==
+#endif
+
+//===========================================================================================================================
+// Macros
+//===========================================================================================================================
+
+// Emulate Mac OS debugging macros for non-Mac platforms.
+
+#if( !TARGET_OS_MAC )
+ #define check(assertion)
+ #define check_string( assertion, cstring )
+ #define check_noerr(err)
+ #define check_noerr_string( error, cstring )
+ #define debug_string( cstring )
+ #define require( assertion, label ) do { if( !(assertion) ) goto label; } while(0)
+ #define require_string( assertion, label, string ) require(assertion, label)
+ #define require_noerr( error, label ) do { if( (error) != 0 ) goto label; } while(0)
+ #define require_noerr_action( error, label, action ) do { if( (error) != 0 ) { {action;}; goto label; } } while(0)
+ #define require_action( assertion, label, action ) do { if( !(assertion) ) { {action;}; goto label; } } while(0)
+ #define require_action_string( assertion, label, action, cstring ) do { if( !(assertion) ) { {action;}; goto label; } } while(0)
+#endif
+
+#define AssignDomainName(DST, SRC) mDNSPlatformMemCopy((SRC).c, (DST).c, DomainNameLength(&(SRC)))
+
+#if 0
+#pragma mark == Prototypes ==
+#endif
+
+//===========================================================================================================================
+// Prototypes
+//===========================================================================================================================
+
+// General
+
+mDNSlocal void DNSServicesLock( void );
+mDNSlocal void DNSServicesUnlock( void );
+mDNSlocal void DNSServicesMDNSCallBack( mDNS *const inMDNS, mStatus inStatus );
+mDNSlocal void DNSServicesUpdateInterfaceSpecificObjects( mDNS *const inMDNS );
+
+// Browser
+
+mDNSlocal void
+ DNSBrowserPrivateCallBack(
+ mDNS * const inMDNS,
+ DNSQuestion * inQuestion,
+ const ResourceRecord * const inAnswer,
+ mDNSBool inAddRecord );
+
+mDNSlocal void
+ DNSBrowserPrivateResolverCallBack(
+ void * inContext,
+ DNSResolverRef inRef,
+ DNSStatus inStatusCode,
+ const DNSResolverEvent * inEvent );
+
+mDNSlocal DNSBrowserRef DNSBrowserFindObject( DNSBrowserRef inRef );
+mDNSlocal DNSBrowserRef DNSBrowserRemoveObject( DNSBrowserRef inRef );
+
+// Resolver
+
+mDNSlocal void DNSResolverPrivateCallBack( mDNS * const inMDNS, ServiceInfoQuery *inQuery );
+mDNSlocal DNSResolverRef DNSResolverFindObject( DNSResolverRef inRef );
+mDNSlocal DNSResolverRef DNSResolverRemoveObject( DNSResolverRef inRef );
+mDNSlocal void DNSResolverRemoveDependentByBrowser( DNSBrowserRef inBrowserRef );
+mDNSlocal void DNSResolverRemoveDependentByName( const domainname *inName );
+mDNSlocal DNSResolverRef DNSResolverFindObjectByName( const domainname *inName );
+
+// Registration
+
+mDNSlocal void
+ DNSRegistrationPrivateCallBack(
+ mDNS * const inMDNS,
+ ServiceRecordSet * const inSet,
+ mStatus inResult );
+
+mDNSlocal void
+ DNSNoSuchServiceRegistrationPrivateCallBack(
+ mDNS * const inMDNS,
+ AuthRecord * const inRR,
+ mStatus inResult );
+
+mDNSlocal void DNSRegistrationUpdateCallBack( mDNS * const inMDNS, AuthRecord * const inRR, RData *inOldData );
+
+mDNSlocal DNSRegistrationRef * DNSRegistrationFindObject( DNSRegistrationRef inRef );
+mDNSlocal DNSRegistrationRef DNSRegistrationRemoveObject( DNSRegistrationRef inRef );
+
+// Domain Registration
+
+mDNSlocal DNSDomainRegistrationRef DNSDomainRegistrationRemoveObject( DNSDomainRegistrationRef inRef );
+
+// Host Registration
+
+mDNSlocal DNSHostRegistrationRef * DNSHostRegistrationFindObject( DNSHostRegistrationRef inRef );
+mDNSlocal DNSHostRegistrationRef DNSHostRegistrationFindObjectByName( const domainname *inName );
+mDNSlocal void DNSHostRegistrationPrivateCallBack( mDNS * const inMDNS, AuthRecord *const inRR, mStatus inResult );
+
+// Utilities
+
+mDNSlocal DNSStatus DNSMemAlloc( size_t inSize, void *outMem );
+mDNSlocal void DNSMemFree( void *inMem );
+mDNSlocal void MDNSAddrToDNSAddress( const mDNSAddr *inAddr, DNSNetworkAddress *outAddr );
+
+// Platform Accessors
+
+typedef struct mDNSPlatformInterfaceInfo mDNSPlatformInterfaceInfo;
+struct mDNSPlatformInterfaceInfo
+{
+ const char * name;
+ mDNSAddr ip;
+};
+
+mDNSexport mStatus mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID );
+mDNSexport mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo );
+
+#if 0
+#pragma mark == Globals ==
+#endif
+
+//===========================================================================================================================
+// Globals
+//===========================================================================================================================
+
+mDNSexport mDNS gMDNS;
+mDNSlocal mDNS * gMDNSPtr = mDNSNULL;
+mDNSlocal CacheRecord * gMDNSCache = mDNSNULL;
+mDNSlocal DNSBrowserRef gDNSBrowserList = mDNSNULL;
+mDNSlocal DNSResolverRef gDNSResolverList = mDNSNULL;
+mDNSlocal DNSRegistrationRef gDNSRegistrationList = mDNSNULL;
+mDNSlocal DNSDomainRegistrationRef gDNSDomainRegistrationList = mDNSNULL;
+mDNSlocal DNSHostRegistrationRef gDNSHostRegistrationList = mDNSNULL;
+
+#if 0
+#pragma mark -
+#pragma mark == General ==
+#endif
+
+//===========================================================================================================================
+// DNSServicesInitialize
+//===========================================================================================================================
+
+DNSStatus DNSServicesInitialize( DNSFlags inFlags, DNSCount inCacheEntryCount )
+{
+ DNSStatus err;
+ mDNSBool advertise;
+
+ require_action( ( inFlags & ~kDNSInitializeValidFlags ) == 0, exit, err = kDNSBadFlagsErr );
+
+ // Allocate the record cache.
+
+ if( inCacheEntryCount == 0 )
+ {
+ inCacheEntryCount = kDNSCountCacheEntryCountDefault;
+ }
+ gMDNSCache = (CacheRecord *) malloc( inCacheEntryCount * sizeof( *gMDNSCache ) );
+ require_action( gMDNSCache, exit, err = kDNSNoMemoryErr );
+
+ // Initialize mDNS.
+
+ if( inFlags & kDNSFlagAdvertise )
+ {
+ advertise = mDNS_Init_AdvertiseLocalAddresses;
+ }
+ else
+ {
+ advertise = mDNS_Init_DontAdvertiseLocalAddresses;
+ }
+ err = mDNS_Init( &gMDNS, mDNSNULL, gMDNSCache, inCacheEntryCount, advertise, DNSServicesMDNSCallBack, mDNSNULL );
+ require_noerr( err, exit );
+ err = gMDNS.mDNSPlatformStatus;
+ require_noerr( err, exit );
+
+ gMDNSPtr = &gMDNS;
+
+exit:
+ if( err )
+ {
+ DNSServicesFinalize();
+ }
+ return( err );
+}
+
+//===========================================================================================================================
+// DNSServicesFinalize
+//===========================================================================================================================
+
+void DNSServicesFinalize( void )
+{
+ if( gMDNSPtr )
+ {
+ mDNSPlatformLock( &gMDNS );
+
+ // Clean up any dangling service registrations.
+
+ while( gDNSRegistrationList )
+ {
+ DNSRegistrationRef serviceRef;
+
+ serviceRef = gDNSRegistrationList;
+ DNSRegistrationRelease( serviceRef, 0UL );
+ check_string( serviceRef != gDNSRegistrationList, "dangling service registration cannot be cleaned up" );
+ }
+
+ // Clean up any dangling domain registrations.
+
+ while( gDNSDomainRegistrationList )
+ {
+ DNSDomainRegistrationRef domainRef;
+
+ domainRef = gDNSDomainRegistrationList;
+ DNSDomainRegistrationRelease( domainRef, 0 );
+ check_string( domainRef != gDNSDomainRegistrationList, "dangling domain registration cannot be cleaned up" );
+ }
+
+ // Clean up any dangling host registrations.
+
+ while( gDNSHostRegistrationList )
+ {
+ DNSHostRegistrationRef hostRef;
+ long refCount;
+
+ hostRef = gDNSHostRegistrationList;
+ refCount = hostRef->refCount;
+ DNSHostRegistrationRelease( hostRef, 0 );
+ check_string( ( refCount > 1 ) || ( hostRef != gDNSHostRegistrationList ),
+ "dangling host registration cannot be cleaned up" );
+ }
+
+ // Clean up any dangling browsers.
+
+ while( gDNSBrowserList )
+ {
+ DNSBrowserRef browserRef;
+
+ browserRef = gDNSBrowserList;
+ DNSBrowserRelease( browserRef, 0 );
+ check_string( browserRef != gDNSBrowserList, "dangling browser cannot be cleaned up" );
+ }
+
+ // Clean up any dangling resolvers.
+
+ while( gDNSResolverList )
+ {
+ DNSResolverRef resolverRef;
+
+ resolverRef = gDNSResolverList;
+ DNSResolverRelease( resolverRef, 0 );
+ check_string( resolverRef != gDNSResolverList, "dangling resolver cannot be cleaned up" );
+ }
+
+ // Null out our MDNS ptr before releasing the lock so no other threads can sneak in and start operations.
+
+ gMDNSPtr = mDNSNULL;
+ mDNSPlatformUnlock( &gMDNS );
+
+ // Tear down mDNS.
+
+ mDNS_Close( &gMDNS );
+ }
+ if( gMDNSCache )
+ {
+ free( gMDNSCache );
+ gMDNSCache = mDNSNULL;
+ }
+}
+
+//===========================================================================================================================
+// DNSServicesLock
+//===========================================================================================================================
+
+mDNSlocal void DNSServicesLock( void )
+{
+ if( gMDNSPtr )
+ {
+ mDNSPlatformLock( gMDNSPtr );
+ }
+}
+
+//===========================================================================================================================
+// DNSServicesUnlock
+//===========================================================================================================================
+
+mDNSlocal void DNSServicesUnlock( void )
+{
+ if( gMDNSPtr )
+ {
+ mDNSPlatformUnlock( gMDNSPtr );
+ }
+}
+
+//===========================================================================================================================
+// DNSServicesMDNSCallBack
+//===========================================================================================================================
+
+mDNSlocal void DNSServicesMDNSCallBack( mDNS *const inMDNS, mStatus inStatus )
+{
+ DNS_UNUSED( inMDNS );
+ DNS_UNUSED( inStatus );
+ check( inMDNS );
+
+ debugf( DEBUG_NAME "MDNS callback (status=%ld)", inStatus );
+
+ if( inStatus == mStatus_ConfigChanged )
+ {
+ DNSServicesUpdateInterfaceSpecificObjects( inMDNS );
+ }
+}
+
+//===========================================================================================================================
+// DNSServicesUpdateInterfaceSpecificObjects
+//===========================================================================================================================
+
+mDNSlocal void DNSServicesUpdateInterfaceSpecificObjects( mDNS *const inMDNS )
+{
+ DNSRegistration * serviceRegistration;
+
+ DNSServicesLock();
+
+ // Update interface-specific service registrations.
+
+ for( serviceRegistration = gDNSRegistrationList; serviceRegistration; serviceRegistration = serviceRegistration->next )
+ {
+ if( serviceRegistration->interfaceName[ 0 ] != '\0' )
+ {
+ mStatus err;
+ mDNSInterfaceID interfaceID;
+
+ err = mDNSPlatformInterfaceNameToID( inMDNS, serviceRegistration->interfaceName, &interfaceID );
+ check_noerr( err );
+ if( err == mStatus_NoError )
+ {
+ // Update all the resource records with the new interface ID.
+
+ serviceRegistration->set.RR_ADV.resrec.InterfaceID = interfaceID;
+ serviceRegistration->set.RR_PTR.resrec.InterfaceID = interfaceID;
+ serviceRegistration->set.RR_SRV.resrec.InterfaceID = interfaceID;
+ serviceRegistration->set.RR_TXT.resrec.InterfaceID = interfaceID;
+ }
+ }
+ }
+
+ DNSServicesUnlock();
+}
+
+#if 0
+#pragma mark -
+#pragma mark == Browser ==
+#endif
+
+//===========================================================================================================================
+// DNSBrowserCreate
+//===========================================================================================================================
+
+DNSStatus
+ DNSBrowserCreate(
+ DNSBrowserFlags inFlags,
+ DNSBrowserCallBack inCallBack,
+ void * inCallBackContext,
+ DNSBrowserRef * outRef )
+{
+ DNSStatus err;
+ DNSBrowser * objectPtr;
+
+ DNSServicesLock();
+ require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr );
+ require_action( ( inFlags & ~kDNSBrowserCreateValidFlags ) == 0, exit, err = kDNSBadFlagsErr );
+ require_action( inCallBack, exit, err = kDNSBadParamErr );
+
+ // Allocate the object and set it up.
+
+ err = DNSMemAlloc( sizeof( *objectPtr ), &objectPtr );
+ require_noerr( err, exit );
+ memset( objectPtr, 0, sizeof( *objectPtr ) );
+
+ objectPtr->flags = inFlags;
+ objectPtr->callback = inCallBack;
+ objectPtr->callbackContext = inCallBackContext;
+
+ // Add the object to the list.
+
+ objectPtr->next = gDNSBrowserList;
+ gDNSBrowserList = objectPtr;
+
+ if( outRef )
+ {
+ *outRef = objectPtr;
+ }
+
+exit:
+ DNSServicesUnlock();
+ return( err );
+}
+
+//===========================================================================================================================
+// DNSBrowserRelease
+//===========================================================================================================================
+
+DNSStatus DNSBrowserRelease( DNSBrowserRef inRef, DNSBrowserFlags inFlags )
+{
+ DNSStatus err;
+ DNSBrowserEvent event;
+
+ DNSServicesLock();
+ require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr );
+ require_action( inRef, exit, err = kDNSBadReferenceErr );
+ require_action( ( inFlags & ~kDNSBrowserReleaseValidFlags ) == 0, exit, err = kDNSBadFlagsErr );
+
+ // Stop service and domain browsing and remove any resolvers dependent on this browser.
+
+ DNSBrowserStopDomainSearch( inRef, 0 );
+ DNSBrowserStopServiceSearch( inRef, 0 );
+ DNSResolverRemoveDependentByBrowser( inRef );
+
+ // Remove the object from the list.
+
+ inRef = DNSBrowserRemoveObject( inRef );
+ require_action( inRef, exit, err = kDNSBadReferenceErr );
+
+ // Call the callback with a release event.
+
+ check( inRef->callback );
+ memset( &event, 0, sizeof( event ) );
+ event.type = kDNSBrowserEventTypeRelease;
+ inRef->callback( inRef->callbackContext, inRef, kDNSNoErr, &event );
+
+ // Release the memory used by the object.
+
+ DNSMemFree( inRef );
+ err = kDNSNoErr;
+
+exit:
+ DNSServicesUnlock();
+ return( err );
+}
+
+//===========================================================================================================================
+// DNSBrowserStartDomainSearch
+//===========================================================================================================================
+
+DNSStatus DNSBrowserStartDomainSearch( DNSBrowserRef inRef, DNSBrowserFlags inFlags )
+{
+ DNSStatus err;
+ mDNS_DomainType type;
+ mDNS_DomainType defaultType;
+ DNSBrowserEvent event;
+ mDNSBool isDomainBrowsing;
+
+ isDomainBrowsing = mDNSfalse;
+
+ DNSServicesLock();
+ require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr );
+ require_action( inRef && DNSBrowserFindObject( inRef ), exit, err = kDNSBadReferenceErr );
+ require_action( ( inFlags & ~kDNSBrowserStartDomainSearchValidFlags ) == 0, exit, err = kDNSBadFlagsErr );
+ require_action( !inRef->isDomainBrowsing, exit, err = kDNSBadStateErr );
+
+ // Determine whether to browse for normal domains or registration domains.
+
+ if( inFlags & kDNSBrowserFlagRegistrationDomainsOnly )
+ {
+ type = mDNS_DomainTypeRegistration;
+ defaultType = mDNS_DomainTypeRegistrationDefault;
+ }
+ else
+ {
+ type = mDNS_DomainTypeBrowse;
+ defaultType = mDNS_DomainTypeBrowseDefault;
+ }
+
+ // Start the browse operations.
+
+ err = mDNS_GetDomains( gMDNSPtr, &inRef->domainQuestion, type, mDNSInterface_Any, DNSBrowserPrivateCallBack, inRef );
+ require_noerr( err, exit );
+ isDomainBrowsing = mDNStrue;
+
+ err = mDNS_GetDomains( gMDNSPtr, &inRef->defaultDomainQuestion, defaultType, mDNSInterface_Any, DNSBrowserPrivateCallBack, inRef );
+ require_noerr( err, exit );
+
+ inRef->domainSearchFlags = inFlags;
+ inRef->isDomainBrowsing = mDNStrue;
+
+ // Call back immediately with "local." since that is always available for all types of browsing.
+
+ memset( &event, 0, sizeof( event ) );
+ event.type = kDNSBrowserEventTypeAddDefaultDomain;
+ event.data.addDefaultDomain.domain = kDNSLocalDomain;
+ event.data.addDefaultDomain.flags = 0;
+ inRef->callback( inRef->callbackContext, inRef, kDNSNoErr, &event );
+
+exit:
+ if( err && isDomainBrowsing )
+ {
+ mDNS_StopGetDomains( gMDNSPtr, &inRef->domainQuestion );
+ }
+ DNSServicesUnlock();
+ return( err );
+}
+
+//===========================================================================================================================
+// DNSBrowserStopDomainSearch
+//===========================================================================================================================
+
+DNSStatus DNSBrowserStopDomainSearch( DNSBrowserRef inRef, DNSBrowserFlags inFlags )
+{
+ DNSStatus err;
+
+ DNSServicesLock();
+ require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr );
+ require_action( inRef && DNSBrowserFindObject( inRef ), exit, err = kDNSBadReferenceErr );
+ require_action( ( inFlags & ~kDNSBrowserStopDomainSearchValidFlags ) == 0, exit, err = kDNSBadFlagsErr );
+ if( !inRef->isDomainBrowsing )
+ {
+ err = kDNSBadStateErr;
+ goto exit;
+ }
+
+ // Stop the browse operations.
+
+ mDNS_StopGetDomains( gMDNSPtr, &inRef->defaultDomainQuestion );
+ mDNS_StopGetDomains( gMDNSPtr, &inRef->domainQuestion );
+ inRef->isDomainBrowsing = mDNSfalse;
+ err = kDNSNoErr;
+
+exit:
+ DNSServicesUnlock();
+ return( err );
+}
+
+//===========================================================================================================================
+// DNSBrowserStartServiceSearch
+//===========================================================================================================================
+
+DNSStatus
+ DNSBrowserStartServiceSearch(
+ DNSBrowserRef inRef,
+ DNSBrowserFlags inFlags,
+ const char * inType,
+ const char * inDomain )
+{
+ DNSStatus err;
+ domainname type;
+ domainname domain;
+
+ DNSServicesLock();
+ require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr );
+ require_action( inRef && DNSBrowserFindObject( inRef ), exit, err = kDNSBadReferenceErr );
+ require_action( ( inFlags & ~kDNSBrowserStartServiceSearchValidFlags ) == 0, exit, err = kDNSBadFlagsErr );
+ require_action( !inRef->isServiceBrowsing, exit, err = kDNSBadStateErr );
+ require_action( inType, exit, err = kDNSBadParamErr );
+
+ // Default to the local domain when null is passed in.
+
+ if( !inDomain || ( inDomain[ 0 ] == '\0' ) || ( inDomain[ 0 ] == '.' ) )
+ {
+ inDomain = kDNSLocalDomain;
+ }
+
+ // Save off the search criteria (in case it needs to be automatically restarted later).
+
+ inRef->serviceSearchFlags = inFlags;
+
+ strncpy( inRef->searchServiceType, inType, sizeof( inRef->searchServiceType ) - 1 );
+ inRef->searchServiceType[ sizeof( inRef->searchServiceType ) - 1 ] = '\0';
+
+ strncpy( inRef->searchDomain, inDomain, sizeof( inRef->searchDomain ) - 1 );
+ inRef->searchDomain[ sizeof( inRef->searchDomain ) - 1 ] = '\0';
+
+ // Start the browse operation with mDNS using our private callback.
+
+ MakeDomainNameFromDNSNameString( &type, inType );
+ MakeDomainNameFromDNSNameString( &domain, inDomain );
+
+ err = mDNS_StartBrowse( gMDNSPtr, &inRef->serviceBrowseQuestion, &type, &domain, mDNSInterface_Any,
+ DNSBrowserPrivateCallBack, inRef );
+ require_noerr( err, exit );
+
+ inRef->isServiceBrowsing = mDNStrue;
+
+exit:
+ DNSServicesUnlock();
+ return( err );
+}
+
+//===========================================================================================================================
+// DNSBrowserStopServiceSearch
+//===========================================================================================================================
+
+DNSStatus DNSBrowserStopServiceSearch( DNSBrowserRef inRef, DNSBrowserFlags inFlags )
+{
+ DNSStatus err;
+
+ DNSServicesLock();
+ require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr );
+ require_action( inRef && DNSBrowserFindObject( inRef ), exit, err = kDNSBadReferenceErr );
+ require_action( ( inFlags & ~kDNSBrowserStopServiceSearchValidFlags ) == 0, exit, err = kDNSBadFlagsErr );
+ if( !inRef->isServiceBrowsing )
+ {
+ err = kDNSBadStateErr;
+ goto exit;
+ }
+
+ // Stop the browse operation with mDNS. Remove any resolvers dependent on browser since we are no longer searching.
+
+ mDNS_StopBrowse( gMDNSPtr, &inRef->serviceBrowseQuestion );
+ DNSResolverRemoveDependentByBrowser( inRef );
+ inRef->isServiceBrowsing = mDNSfalse;
+ err = kDNSNoErr;
+
+exit:
+ DNSServicesUnlock();
+ return( err );
+}
+
+//===========================================================================================================================
+// DNSBrowserPrivateCallBack
+//===========================================================================================================================
+
+mDNSlocal void
+ DNSBrowserPrivateCallBack(
+ mDNS * const inMDNS,
+ DNSQuestion * inQuestion,
+ const ResourceRecord * const inAnswer,
+ mDNSBool inAddRecord )
+{
+ DNSBrowserRef objectPtr;
+ domainlabel name;
+ domainname type;
+ domainname domain;
+ char nameString[ 256 ];
+ char typeString[ 256 ];
+ char domainString[ 256 ];
+ DNSBrowserEvent event;
+ mStatus err;
+
+ check( inMDNS );
+ check( inQuestion );
+ check( inAnswer );
+
+ DNSServicesLock();
+
+ // Exclude non-PTR answers.
+
+ require( inAnswer->rrtype == kDNSType_PTR, exit );
+
+ // Exit if object is no longer valid. Should never happen.
+
+ objectPtr = DNSBrowserFindObject( (DNSBrowserRef) inQuestion->QuestionContext );
+ require( objectPtr, exit );
+
+ // Determine what type of callback it is based on the question.
+
+ memset( &event, 0, sizeof( event ) );
+ if( inQuestion == &objectPtr->serviceBrowseQuestion )
+ {
+ DNSBrowserEventServiceData * serviceDataPtr;
+ DNSBrowserFlags browserFlags;
+
+ // Extract name, type, and domain from the resource record.
+
+ DeconstructServiceName( &inAnswer->rdata->u.name, &name, &type, &domain );
+ ConvertDomainLabelToCString_unescaped( &name, nameString );
+ ConvertDomainNameToCString( &type, typeString );
+ ConvertDomainNameToCString( &domain, domainString );
+
+ // Fill in the event data. A TTL of zero means the service is no longer available. If the service instance is going
+ // away (ttl == 0), remove any resolvers dependent on the name since it is no longer valid.
+
+ if( !inAddRecord )
+ {
+ DNSResolverRemoveDependentByName( &inAnswer->rdata->u.name );
+
+ event.type = kDNSBrowserEventTypeRemoveService;
+ serviceDataPtr = &event.data.removeService;
+ }
+ else
+ {
+ event.type = kDNSBrowserEventTypeAddService;
+ serviceDataPtr = &event.data.addService;
+ }
+ serviceDataPtr->interfaceName = "";
+ if( inAnswer->InterfaceID != mDNSInterface_Any )
+ {
+ mDNSPlatformInterfaceInfo info;
+
+ err = mDNSPlatformInterfaceIDToInfo( inMDNS, inAnswer->InterfaceID, &info );
+ if( err == mStatus_NoError )
+ {
+ serviceDataPtr->interfaceName = info.name;
+ MDNSAddrToDNSAddress( &info.ip, &serviceDataPtr->interfaceIP );
+ }
+ else
+ {
+ serviceDataPtr->interfaceName = "";
+ }
+ }
+ serviceDataPtr->interfaceID = inAnswer->InterfaceID;
+ serviceDataPtr->name = nameString;
+ serviceDataPtr->type = typeString;
+ serviceDataPtr->domain = domainString;
+ serviceDataPtr->flags = 0;
+
+ // Call the callback.
+
+ browserFlags = objectPtr->serviceSearchFlags;
+ objectPtr->callback( objectPtr->callbackContext, objectPtr, kDNSNoErr, &event );
+
+ // Automatically resolve newly discovered names if the auto-resolve option is enabled.
+
+ if( ( browserFlags & kDNSBrowserFlagAutoResolve ) && inAddRecord )
+ {
+ DNSStatus err;
+ DNSResolverFlags flags;
+
+ flags = kDNSResolverFlagOnlyIfUnique | kDNSResolverFlagAutoReleaseByName;
+ err = DNSResolverCreate( flags, nameString, typeString, domainString, DNSBrowserPrivateResolverCallBack,
+ mDNSNULL, objectPtr, mDNSNULL );
+ check_noerr( err );
+ }
+ }
+ else
+ {
+ DNSBrowserEventDomainData * domainDataPtr;
+
+ // Determine the event type. A TTL of zero means the domain is no longer available.
+
+ domainDataPtr = mDNSNULL;
+ if( inQuestion == &objectPtr->domainQuestion )
+ {
+ if( !inAddRecord )
+ {
+ event.type = kDNSBrowserEventTypeRemoveDomain;
+ domainDataPtr = &event.data.removeDomain;
+ }
+ else
+ {
+ event.type = kDNSBrowserEventTypeAddDomain;
+ domainDataPtr = &event.data.addDomain;
+ }
+ }
+ else if( inQuestion == &objectPtr->defaultDomainQuestion )
+ {
+ if( !inAddRecord )
+ {
+ event.type = kDNSBrowserEventTypeRemoveDomain;
+ domainDataPtr = &event.data.removeDomain;
+ }
+ else
+ {
+ event.type = kDNSBrowserEventTypeAddDefaultDomain;
+ domainDataPtr = &event.data.addDefaultDomain;
+ }
+ }
+ require_string( domainDataPtr, exit, "domain response for unknown question" );
+
+ // Extract domain name from the resource record and fill in the event data.
+
+ ConvertDomainNameToCString( &inAnswer->rdata->u.name, domainString );
+
+ domainDataPtr->interfaceName = "";
+ if( inAnswer->InterfaceID != mDNSInterface_Any )
+ {
+ mDNSPlatformInterfaceInfo info;
+
+ err = mDNSPlatformInterfaceIDToInfo( inMDNS, inAnswer->InterfaceID, &info );
+ if( err == mStatus_NoError )
+ {
+ domainDataPtr->interfaceName = info.name;
+ MDNSAddrToDNSAddress( &info.ip, &domainDataPtr->interfaceIP );
+ }
+ else
+ {
+ domainDataPtr->interfaceName = "";
+ }
+ }
+ domainDataPtr->interfaceID = inAnswer->InterfaceID;
+ domainDataPtr->domain = domainString;
+ domainDataPtr->flags = 0;
+
+ // Call the callback.
+
+ objectPtr->callback( objectPtr->callbackContext, objectPtr, kDNSNoErr, &event );
+ }
+
+exit:
+ DNSServicesUnlock();
+}
+
+//===========================================================================================================================
+// DNSBrowserPrivateResolverCallBack
+//===========================================================================================================================
+
+mDNSlocal void
+ DNSBrowserPrivateResolverCallBack(
+ void * inContext,
+ DNSResolverRef inRef,
+ DNSStatus inStatusCode,
+ const DNSResolverEvent * inEvent )
+{
+ DNSBrowserRef objectPtr;
+ DNSBrowserEvent event;
+
+ DNS_UNUSED( inContext );
+ DNS_UNUSED( inStatusCode );
+
+ DNSServicesLock();
+
+ // Exit if object is no longer valid. Should never happen.
+
+ objectPtr = inRef->owner;
+ require( objectPtr, exit );
+
+ switch( inEvent->type )
+ {
+ case kDNSResolverEventTypeResolved:
+ verbosedebugf( DEBUG_NAME "private resolver callback: resolved (ref=0x%08X)", inRef );
+ verbosedebugf( DEBUG_NAME " name: \"%s\"", inEvent->data.resolved.name );
+ verbosedebugf( DEBUG_NAME " type: \"%s\"", inEvent->data.resolved.type );
+ verbosedebugf( DEBUG_NAME " domain: \"%s\"", inEvent->data.resolved.domain );
+ verbosedebugf( DEBUG_NAME " if: %.4a", &inEvent->data.resolved.interfaceIP.u.ipv4.addr.v32 );
+ verbosedebugf( DEBUG_NAME " ip: %.4a:%u", &inEvent->data.resolved.address.u.ipv4.addr.v32,
+ ( inEvent->data.resolved.address.u.ipv4.port.v8[ 0 ] << 8 ) |
+ inEvent->data.resolved.address.u.ipv4.port.v8[ 1 ] );
+ verbosedebugf( DEBUG_NAME " text: \"%s\"", inEvent->data.resolved.textRecord );
+
+ // Re-package the resolver event as a browser event and call the callback.
+
+ memset( &event, 0, sizeof( event ) );
+ event.type = kDNSBrowserEventTypeResolved;
+ event.data.resolved = &inEvent->data.resolved;
+
+ objectPtr->callback( objectPtr->callbackContext, objectPtr, kDNSNoErr, &event );
+ break;
+
+ case kDNSResolverEventTypeRelease:
+ verbosedebugf( DEBUG_NAME "private resolver callback: release (ref=0x%08X)", inRef );
+ break;
+
+ default:
+ verbosedebugf( DEBUG_NAME "private resolver callback: unknown event (ref=0x%08X, event=%ld)", inRef, inEvent->type );
+ break;
+ }
+
+exit:
+ DNSServicesUnlock();
+}
+
+//===========================================================================================================================
+// DNSBrowserFindObject
+//
+// Warning: Assumes the DNS lock is held.
+//===========================================================================================================================
+
+mDNSlocal DNSBrowserRef DNSBrowserFindObject( DNSBrowserRef inRef )
+{
+ DNSBrowser * p;
+
+ check( inRef );
+
+ // Find the object in the list.
+
+ for( p = gDNSBrowserList; p; p = p->next )
+ {
+ if( p == inRef )
+ {
+ break;
+ }
+ }
+ return( p );
+}
+
+//===========================================================================================================================
+// DNSBrowserRemoveObject
+//
+// Warning: Assumes the DNS lock is held.
+//===========================================================================================================================
+
+mDNSlocal DNSBrowserRef DNSBrowserRemoveObject( DNSBrowserRef inRef )
+{
+ DNSBrowser ** p;
+ DNSBrowser * found;
+
+ for( p = &gDNSBrowserList; *p; p = &( *p )->next )
+ {
+ if( *p == inRef )
+ {
+ break;
+ }
+ }
+ found = *p;
+ if( found )
+ {
+ *p = found->next;
+ }
+ return( found );
+}
+
+#if 0
+#pragma mark -
+#pragma mark == Resolver ==
+#endif
+
+//===========================================================================================================================
+// DNSResolverCreate
+//===========================================================================================================================
+
+DNSStatus
+ DNSResolverCreate(
+ DNSResolverFlags inFlags,
+ const char * inName,
+ const char * inType,
+ const char * inDomain,
+ DNSResolverCallBack inCallBack,
+ void * inCallBackContext,
+ DNSBrowserRef inOwner,
+ DNSResolverRef * outRef )
+{
+ DNSStatus err;
+ int isAutoRelease;
+ DNSResolver * objectPtr;
+ domainlabel name;
+ domainname type;
+ domainname domain;
+ domainname fullName;
+
+ objectPtr = mDNSNULL;
+
+ // Check parameters.
+
+ DNSServicesLock();
+ require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr );
+ require_action( ( inFlags & ~kDNSResolverCreateValidFlags ) == 0, exit, err = kDNSBadFlagsErr );
+ require_action( inName, exit, err = kDNSBadParamErr );
+ require_action( inType, exit, err = kDNSBadParamErr );
+ require_action( inDomain, exit, err = kDNSBadParamErr );
+ require_action( inCallBack, exit, err = kDNSBadParamErr );
+ isAutoRelease = inOwner || ( inFlags & ( kDNSResolverFlagOneShot | kDNSResolverFlagAutoReleaseByName ) );
+ require_action( outRef || isAutoRelease, exit, err = kDNSBadParamErr );
+ require_action( !inOwner || DNSBrowserFindObject( inOwner ), exit, err = kDNSBadReferenceErr );
+
+ // Convert and package up the name, type, and domain into a single fully-qualified domain name to resolve.
+
+ MakeDomainLabelFromLiteralString( &name, inName );
+ MakeDomainNameFromDNSNameString( &type, inType );
+ MakeDomainNameFromDNSNameString( &domain, inDomain );
+ ConstructServiceName( &fullName, &name, &type, &domain );
+
+ // If the caller only wants to add unique resolvers, check if a resolver for this name is already present.
+
+ if( inFlags & kDNSResolverFlagOnlyIfUnique )
+ {
+ if( DNSResolverFindObjectByName( &fullName ) )
+ {
+ if( outRef )
+ {
+ *outRef = mDNSNULL;
+ }
+ err = kDNSNoErr;
+ goto exit;
+ }
+ }
+
+ // Allocate the object and set it up.
+
+ err = DNSMemAlloc( sizeof( *objectPtr ), &objectPtr );
+ require_noerr( err, exit );
+ memset( objectPtr, 0, sizeof( *objectPtr ) );
+
+ objectPtr->flags = inFlags;
+ objectPtr->callback = inCallBack;
+ objectPtr->callbackContext = inCallBackContext;
+ objectPtr->owner = inOwner;
+ AssignDomainName( objectPtr->info.name, fullName );
+ objectPtr->info.InterfaceID = mDNSInterface_Any;
+
+ // Save off the resolve info so the callback can get it.
+
+ strncpy( objectPtr->resolveName, inName, sizeof( objectPtr->resolveName ) - 1 );
+ objectPtr->resolveName[ sizeof( objectPtr->resolveName ) - 1 ] = '\0';
+
+ strncpy( objectPtr->resolveType, inType, sizeof( objectPtr->resolveType ) - 1 );
+ objectPtr->resolveType[ sizeof( objectPtr->resolveType ) - 1 ] = '\0';
+
+ strncpy( objectPtr->resolveDomain, inDomain, sizeof( objectPtr->resolveDomain ) - 1 );
+ objectPtr->resolveDomain[ sizeof( objectPtr->resolveDomain ) - 1 ] = '\0';
+
+ // Add the object to the list.
+
+ objectPtr->next = gDNSResolverList;
+ gDNSResolverList = objectPtr;
+
+ // Start the resolving process.
+
+ objectPtr->isResolving = mDNStrue;
+ err = mDNS_StartResolveService( gMDNSPtr, &objectPtr->query, &objectPtr->info, DNSResolverPrivateCallBack, objectPtr );
+ require_noerr( err, exit );
+
+ if( outRef )
+ {
+ *outRef = objectPtr;
+ }
+
+exit:
+ if( err && objectPtr )
+ {
+ DNSResolverRemoveObject( objectPtr );
+ DNSMemFree( objectPtr );
+ }
+ DNSServicesUnlock();
+ return( err );
+}
+
+//===========================================================================================================================
+// DNSResolverRelease
+//===========================================================================================================================
+
+DNSStatus DNSResolverRelease( DNSResolverRef inRef, DNSResolverFlags inFlags )
+{
+ DNSStatus err;
+ DNSResolverEvent event;
+
+ DNSServicesLock();
+ require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr );
+ require_action( ( inFlags & ~kDNSResolverReleaseValidFlags ) == 0, exit, err = kDNSBadFlagsErr );
+
+ // Remove the object from the list.
+
+ inRef = DNSResolverRemoveObject( inRef );
+ require_action( inRef, exit, err = kDNSBadReferenceErr );
+
+ // Stop the resolving process.
+
+ if( inRef->isResolving )
+ {
+ inRef->isResolving = mDNSfalse;
+ mDNS_StopResolveService( gMDNSPtr, &inRef->query );
+ }
+
+ // Call the callback with a release event.
+
+ check( inRef->callback );
+ memset( &event, 0, sizeof( event ) );
+ event.type = kDNSResolverEventTypeRelease;
+ inRef->callback( inRef->callbackContext, inRef, kDNSNoErr, &event );
+
+ // Release the memory used by the object.
+
+ DNSMemFree( inRef );
+ err = kDNSNoErr;
+
+exit:
+ DNSServicesUnlock();
+ return( err );
+}
+
+//===========================================================================================================================
+// DNSResolverFindObject
+//
+// Warning: Assumes the DNS lock is held.
+//===========================================================================================================================
+
+mDNSlocal DNSResolverRef DNSResolverFindObject( DNSResolverRef inRef )
+{
+ DNSResolver * p;
+
+ check( inRef );
+
+ // Find the object in the list.
+
+ for( p = gDNSResolverList; p; p = p->next )
+ {
+ if( p == inRef )
+ {
+ break;
+ }
+ }
+ return( p );
+}
+
+//===========================================================================================================================
+// DNSResolverFindObjectByName
+//
+// Warning: Assumes the DNS lock is held.
+//===========================================================================================================================
+
+mDNSlocal DNSResolverRef DNSResolverFindObjectByName( const domainname *inName )
+{
+ DNSResolver * p;
+
+ check( inName );
+
+ for( p = gDNSResolverList; p; p = p->next )
+ {
+ if( SameDomainName( &p->info.name, inName ) )
+ {
+ break;
+ }
+ }
+ return( p );
+}
+
+//===========================================================================================================================
+// DNSResolverPrivateCallBack
+//===========================================================================================================================
+
+mDNSlocal void DNSResolverPrivateCallBack( mDNS * const inMDNS, ServiceInfoQuery *inQuery )
+{
+ DNSResolverRef objectPtr;
+ DNSResolverEvent event;
+ char * txtString;
+ mStatus err;
+ mDNSBool release;
+
+ txtString = NULL;
+
+ DNSServicesLock();
+
+ // Exit if object is no longer valid. Should never happen.
+
+ objectPtr = DNSResolverFindObject( (DNSResolverRef) inQuery->ServiceInfoQueryContext );
+ require( objectPtr, exit );
+
+ // Convert the raw TXT record into a null-terminated string with \001-delimited records for Mac OS X-style clients.
+
+ err = DNSTextRecordEscape( inQuery->info->TXTinfo, inQuery->info->TXTlen, &txtString );
+ check_noerr( err );
+
+ // Package up the results and call the callback.
+
+ memset( &event, 0, sizeof( event ) );
+ event.type = kDNSResolverEventTypeResolved;
+ event.data.resolved.name = objectPtr->resolveName;
+ event.data.resolved.type = objectPtr->resolveType;
+ event.data.resolved.domain = objectPtr->resolveDomain;
+ event.data.resolved.interfaceName = "";
+ if( inQuery->info->InterfaceID != mDNSInterface_Any )
+ {
+ mDNSPlatformInterfaceInfo info;
+
+ err = mDNSPlatformInterfaceIDToInfo( inMDNS, inQuery->info->InterfaceID, &info );
+ if( err == mStatus_NoError )
+ {
+ event.data.resolved.interfaceName = info.name;
+ MDNSAddrToDNSAddress( &info.ip, &event.data.resolved.interfaceIP );
+ }
+ else
+ {
+ event.data.resolved.interfaceName = "";
+ }
+ }
+ event.data.resolved.interfaceID = inQuery->info->InterfaceID;
+ event.data.resolved.address.addressType = kDNSNetworkAddressTypeIPv4;
+ event.data.resolved.address.u.ipv4.addr.v32 = inQuery->info->ip.ip.v4.NotAnInteger;
+ event.data.resolved.address.u.ipv4.port.v16 = inQuery->info->port.NotAnInteger;
+ event.data.resolved.textRecord = txtString ? txtString : "";
+ event.data.resolved.flags = 0;
+ event.data.resolved.textRecordRaw = (const void *) inQuery->info->TXTinfo;
+ event.data.resolved.textRecordRawSize = (DNSCount) inQuery->info->TXTlen;
+ release = (mDNSBool)( ( objectPtr->flags & kDNSResolverFlagOneShot ) != 0 );
+ objectPtr->callback( objectPtr->callbackContext, objectPtr, kDNSNoErr, &event );
+
+ // Auto-release the object if needed.
+
+ if( release )
+ {
+ DNSResolverRelease( objectPtr, 0 );
+ }
+
+exit:
+ DNSServicesUnlock();
+ if( txtString )
+ {
+ free( txtString );
+ }
+}
+
+//===========================================================================================================================
+// DNSResolverRemoveObject
+//
+// Warning: Assumes the DNS lock is held.
+//===========================================================================================================================
+
+mDNSlocal DNSResolverRef DNSResolverRemoveObject( DNSResolverRef inRef )
+{
+ DNSResolver ** p;
+ DNSResolver * found;
+
+ for( p = &gDNSResolverList; *p; p = &( *p )->next )
+ {
+ if( *p == inRef )
+ {
+ break;
+ }
+ }
+ found = *p;
+ if( found )
+ {
+ *p = found->next;
+ }
+ return( found );
+}
+
+//===========================================================================================================================
+// DNSResolverRemoveDependentByBrowser
+//
+// Warning: Assumes the DNS lock is held.
+//===========================================================================================================================
+
+mDNSlocal void DNSResolverRemoveDependentByBrowser( DNSBrowserRef inBrowserRef )
+{
+ DNSResolver * p;
+
+ check( inBrowserRef );
+
+ // Removes all the resolver objects dependent on the specified browser. Restart the search from the beginning of the
+ // list after each removal to handle the list changing in possible callbacks that may be invoked.
+
+ do
+ {
+ for( p = gDNSResolverList; p; p = p->next )
+ {
+ if( p->owner == inBrowserRef )
+ {
+ DNSResolverRelease( p, 0 );
+ break;
+ }
+ }
+
+ } while( p );
+}
+
+//===========================================================================================================================
+// DNSResolverRemoveDependentByName
+//
+// Warning: Assumes the DNS lock is held.
+//===========================================================================================================================
+
+mDNSlocal void DNSResolverRemoveDependentByName( const domainname *inName )
+{
+ DNSResolver * p;
+
+ check( inName );
+
+ // Removes all the resolver objects dependent on the specified name that want to be auto-released by name. Restart
+ // the search from the beginning of the list after each removal to handle the list changing in possible callbacks
+ // that may be invoked.
+
+ do
+ {
+ for( p = gDNSResolverList; p; p = p->next )
+ {
+ if( ( p->flags & kDNSResolverFlagAutoReleaseByName ) && SameDomainName( &p->info.name, inName ) )
+ {
+ DNSResolverRelease( p, 0 );
+ break;
+ }
+ }
+
+ } while( p );
+}
+
+#if 0
+#pragma mark -
+#pragma mark == Registration ==
+#endif
+
+//===========================================================================================================================
+// DNSRegistrationCreate
+//===========================================================================================================================
+
+DNSStatus
+ DNSRegistrationCreate(
+ DNSRegistrationFlags inFlags,
+ const char * inName,
+ const char * inType,
+ const char * inDomain,
+ DNSPort inPort,
+ const void * inTextRecord,
+ DNSCount inTextRecordSize,
+ const char * inHost,
+ const char * inInterfaceName,
+ DNSRegistrationCallBack inCallBack,
+ void * inCallBackContext,
+ DNSRegistrationRef * outRef )
+{
+ DNSStatus err;
+ size_t size;
+ DNSRegistration * objectPtr;
+ mDNSInterfaceID interfaceID;
+ domainlabel name;
+ domainname type;
+ domainname domain;
+ mDNSIPPort port;
+ mDNSu8 textRecord[ 256 ];
+ const mDNSu8 * textRecordPtr;
+ domainname * host;
+ domainname tempHost;
+
+ objectPtr = mDNSNULL;
+
+ // Check parameters.
+
+ DNSServicesLock();
+ require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr );
+ require_action( ( inFlags & ~kDNSRegistrationCreateValidFlags ) == 0, exit, err = kDNSBadFlagsErr );
+ require_action( inType, exit, err = kDNSBadParamErr );
+ require_action( inTextRecord || ( inTextRecordSize == 0 ), exit, err = kDNSBadParamErr );
+ require_action( ( inFlags & kDNSRegistrationFlagPreFormattedTextRecord ) ||
+ ( inTextRecordSize < sizeof( textRecord ) ), exit, err = kDNSBadParamErr );
+ require_action( !inInterfaceName ||
+ ( strlen( inInterfaceName ) < sizeof( objectPtr->interfaceName ) ), exit, err = kDNSBadParamErr );
+
+ // Default to the local domain when null is passed in.
+
+ if( !inDomain )
+ {
+ inDomain = kDNSLocalDomain;
+ }
+
+ // Set up the text record. If the pre-formatted flag is used, the input text is assumed to be a valid text record
+ // and is used directly. Otherwise, the input text is assumed to be raw text and is converted to a text record.
+
+ textRecordPtr = (const mDNSu8 *) inTextRecord;
+ if( !( inFlags & kDNSRegistrationFlagPreFormattedTextRecord ) )
+ {
+ // Convert the raw input text to a length-prefixed text record.
+
+ if( inTextRecordSize > 0 )
+ {
+ textRecord[ 0 ] = (mDNSu8) inTextRecordSize;
+ memcpy( &textRecord[ 1 ], inTextRecord, inTextRecordSize );
+ textRecordPtr = textRecord;
+ inTextRecordSize += 1;
+ }
+ }
+
+ // Allocate the object and set it up. If the TXT record is larger than the standard RDataBody, allocate more space.
+
+ size = sizeof( *objectPtr );
+ if( inTextRecordSize > sizeof( RDataBody ) )
+ {
+ size += ( inTextRecordSize - sizeof( RDataBody ) );
+ }
+
+ err = DNSMemAlloc( size, &objectPtr );
+ require_noerr( err, exit );
+ memset( objectPtr, 0, size );
+
+ objectPtr->flags = inFlags;
+ objectPtr->callback = inCallBack;
+ objectPtr->callbackContext = inCallBackContext;
+
+ // Set up the interface for interface-specific operations.
+
+ if( inInterfaceName && ( *inInterfaceName != '\0' ) )
+ {
+ strcpy( objectPtr->interfaceName, inInterfaceName );
+
+ err = mDNSPlatformInterfaceNameToID( gMDNSPtr, inInterfaceName, &interfaceID );
+ require_noerr( err, exit );
+ }
+ else
+ {
+ interfaceID = mDNSInterface_Any;
+ }
+
+ // Add the object to the list.
+
+ objectPtr->next = gDNSRegistrationList;
+ gDNSRegistrationList = objectPtr;
+
+ // Convert the name, type, domain, and port to a format suitable for mDNS. If the name is NULL or an empty string,
+ // use the UTF-8 name of the system as the service name to make it easy for clients to use the standard name.
+ // If we're using the system name (i.e. name is NULL), automatically rename on conflicts to keep things in sync.
+
+ if( !inName || ( *inName == '\0' ) )
+ {
+ name = gMDNSPtr->nicelabel;
+ inFlags |= kDNSRegistrationFlagAutoRenameOnConflict;
+ }
+ else
+ {
+ MakeDomainLabelFromLiteralString( &name, inName );
+ }
+ MakeDomainNameFromDNSNameString( &type, inType );
+ MakeDomainNameFromDNSNameString( &domain, inDomain );
+ port.b[ 0 ] = ( mDNSu8 )( inPort >> 8 );
+ port.b[ 1 ] = ( mDNSu8 )( inPort >> 0 );
+
+ // Set up the host name (if not using the default).
+
+ host = mDNSNULL;
+ if( inHost )
+ {
+ host = &tempHost;
+ MakeDomainNameFromDNSNameString( host, inHost );
+ AppendDomainName( host, &domain );
+ }
+
+ // Register the service with mDNS.
+
+ err = mDNS_RegisterService( gMDNSPtr, &objectPtr->set, &name, &type, &domain, host, port, textRecordPtr,
+ (mDNSu16) inTextRecordSize, NULL, 0, interfaceID,
+ DNSRegistrationPrivateCallBack, objectPtr );
+ require_noerr( err, exit );
+
+ if( outRef )
+ {
+ *outRef = objectPtr;
+ }
+
+exit:
+ if( err && objectPtr )
+ {
+ DNSRegistrationRemoveObject( objectPtr );
+ DNSMemFree( objectPtr );
+ }
+ DNSServicesUnlock();
+ return( err );
+}
+
+//===========================================================================================================================
+// DNSNoSuchServiceRegistrationCreate
+//===========================================================================================================================
+
+DNSStatus
+ DNSNoSuchServiceRegistrationCreate(
+ DNSRegistrationFlags inFlags,
+ const char * inName,
+ const char * inType,
+ const char * inDomain,
+ const char * inInterfaceName,
+ DNSRegistrationCallBack inCallBack,
+ void * inCallBackContext,
+ DNSRegistrationRef * outRef )
+{
+ DNSStatus err;
+ size_t size;
+ DNSRegistration * objectPtr;
+ mDNSInterfaceID interfaceID;
+ domainlabel name;
+ domainname type;
+ domainname domain;
+
+ objectPtr = mDNSNULL;
+
+ // Check parameters.
+
+ DNSServicesLock();
+ require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr );
+ require_action( ( inFlags & ~kDNSNoSuchServiceRegistrationCreateValidFlags ) == 0, exit, err = kDNSBadFlagsErr );
+ inFlags |= kDNSRegistrationFlagPrivateNoSuchService;
+ require_action( inType, exit, err = kDNSBadParamErr );
+ require_action( !inInterfaceName ||
+ ( strlen( inInterfaceName ) < sizeof( objectPtr->interfaceName ) ), exit, err = kDNSBadParamErr );
+
+ // Default to the local domain when null is passed in.
+
+ if( !inDomain )
+ {
+ inDomain = kDNSLocalDomain;
+ }
+
+ // Allocate the object and set it up. If the TXT record is larger than the standard RDataBody, allocate more space.
+
+ size = sizeof( *objectPtr );
+
+ err = DNSMemAlloc( size, &objectPtr );
+ require_noerr( err, exit );
+ memset( objectPtr, 0, size );
+
+ objectPtr->flags = inFlags;
+ objectPtr->callback = inCallBack;
+ objectPtr->callbackContext = inCallBackContext;
+
+ // Set up the interface for interface-specific operations.
+
+ if( inInterfaceName && ( *inInterfaceName != '\0' ) )
+ {
+ strcpy( objectPtr->interfaceName, inInterfaceName );
+
+ err = mDNSPlatformInterfaceNameToID( gMDNSPtr, inInterfaceName, &interfaceID );
+ require_noerr( err, exit );
+ }
+ else
+ {
+ interfaceID = mDNSInterface_Any;
+ }
+
+ // Add the object to the list.
+
+ objectPtr->next = gDNSRegistrationList;
+ gDNSRegistrationList = objectPtr;
+
+ // Convert the name, type, domain, and port to a format suitable for mDNS. If the name is NULL or an empty string,
+ // use the UTF-8 name of the system as the service name to make it easy for clients to use the standard name.
+
+ if( !inName || ( *inName == '\0' ) )
+ {
+ name = gMDNSPtr->nicelabel;
+ }
+ else
+ {
+ MakeDomainLabelFromLiteralString( &name, inName );
+ }
+ MakeDomainNameFromDNSNameString( &type, inType );
+ MakeDomainNameFromDNSNameString( &domain, inDomain );
+
+ // Register the service with mDNS.
+
+ err = mDNS_RegisterNoSuchService( gMDNSPtr, &objectPtr->set.RR_SRV, &name, &type, &domain, mDNSNULL,
+ interfaceID, DNSNoSuchServiceRegistrationPrivateCallBack, objectPtr );
+ require_noerr( err, exit );
+
+ if( outRef )
+ {
+ *outRef = objectPtr;
+ }
+
+exit:
+ if( err && objectPtr )
+ {
+ DNSRegistrationRemoveObject( objectPtr );
+ DNSMemFree( objectPtr );
+ }
+ DNSServicesUnlock();
+ return( err );
+}
+
+//===========================================================================================================================
+// DNSRegistrationRelease
+//===========================================================================================================================
+
+DNSStatus DNSRegistrationRelease( DNSRegistrationRef inRef, DNSRegistrationFlags inFlags )
+{
+ DNSStatus err;
+ DNSRegistrationEvent event;
+
+ DNSServicesLock();
+ require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr );
+ require_action( inRef, exit, err = kDNSBadReferenceErr );
+ require_action( ( inFlags & ~kDNSRegistrationReleaseValidFlags ) == 0, exit, err = kDNSBadFlagsErr );
+
+ // Notify the client of the registration release. Remove the object first so they cannot try to use it in the callback.
+
+ inRef = DNSRegistrationRemoveObject( inRef );
+ require_action( inRef, exit, err = kDNSBadReferenceErr );
+
+ if( inRef->callback )
+ {
+ memset( &event, 0, sizeof( event ) );
+ event.type = kDNSRegistrationEventTypeRelease;
+ inRef->callback( inRef->callbackContext, inRef, kDNSNoErr, &event );
+ }
+
+ // Deregister from mDNS after everything else since it will call us back to free the memory.
+
+ if( !( inRef->flags & kDNSRegistrationFlagPrivateNoSuchService ) )
+ {
+ err = mDNS_DeregisterService( gMDNSPtr, &inRef->set );
+ require_noerr( err, exit );
+ }
+ else
+ {
+ err = mDNS_DeregisterNoSuchService( gMDNSPtr, &inRef->set.RR_SRV );
+ require_noerr( err, exit );
+ }
+
+ // Note: Don't free here. Wait for mDNS to call us back with a mem free result.
+
+exit:
+ DNSServicesUnlock();
+ return( err );
+}
+
+//===========================================================================================================================
+// DNSRegistrationUpdate
+//===========================================================================================================================
+
+DNSStatus
+ DNSRegistrationUpdate(
+ DNSRegistrationRef inRef,
+ DNSRecordFlags inFlags,
+ DNSRegistrationRecordRef inRecord,
+ const void * inData,
+ DNSCount inSize,
+ DNSUInt32 inNewTTL )
+{
+ DNSStatus err;
+ AuthRecord * rr;
+ size_t maxRDLength;
+ RData * newRData;
+
+ newRData = mDNSNULL;
+
+ DNSServicesLock();
+ require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr );
+ require_action( DNSRegistrationFindObject( inRef ), exit, err = kDNSBadReferenceErr );
+ require_action( ( inFlags & ~kDNSRegistrationUpdateValidFlags ) == 0, exit, err = kDNSBadFlagsErr );
+ require_action( inData || ( inSize == 0 ), exit, err = kDNSBadParamErr );
+
+ // If a non-NULL record is specified, update it. Otherwise, use the standard TXT record.
+
+ if( inRecord )
+ {
+ // $$$ TO DO: Add support for updating extra records (support adding and removing them too).
+
+ rr = mDNSNULL;
+ err = kDNSUnsupportedErr;
+ require_noerr( err, exit );
+ }
+ else
+ {
+ rr = &inRef->set.RR_TXT;
+ }
+
+ // Allocate storage for the new data and set it up.
+
+ maxRDLength = sizeof( RDataBody );
+ if( inSize > maxRDLength )
+ {
+ maxRDLength = inSize;
+ }
+ err = DNSMemAlloc( ( sizeof( *newRData ) - sizeof( RDataBody ) ) + maxRDLength, &newRData );
+ require_noerr( err, exit );
+
+ newRData->MaxRDLength = (mDNSu16) maxRDLength;
+ memcpy( &newRData->u, inData, inSize );
+
+ // Update the record with mDNS.
+
+ err = mDNS_Update( gMDNSPtr, rr, inNewTTL, (mDNSu16) inSize, newRData, DNSRegistrationUpdateCallBack );
+ require_noerr( err, exit );
+
+ newRData = mDNSNULL;
+
+exit:
+ if( newRData )
+ {
+ DNSMemFree( newRData );
+ }
+ DNSServicesUnlock();
+ return( err );
+}
+
+//===========================================================================================================================
+// DNSRegistrationPrivateCallBack
+//===========================================================================================================================
+
+mDNSlocal void DNSRegistrationPrivateCallBack( mDNS * const inMDNS, ServiceRecordSet * const inSet, mStatus inResult )
+{
+ DNSRegistrationRef object;
+ DNSRegistrationEvent event;
+
+ DNS_UNUSED( inMDNS );
+
+ DNSServicesLock();
+
+ // Exit if object is no longer valid. Should never happen.
+
+ object = (DNSRegistrationRef) inSet->ServiceContext;
+ require( object, exit );
+
+ // Dispatch based on the status code.
+
+ switch( inResult )
+ {
+ case mStatus_NoError:
+ debugf( DEBUG_NAME "registration callback: \"%##s\" name successfully registered", inSet->RR_SRV.resrec.name.c );
+
+ // Notify the client of a successful registration.
+
+ if( object->callback )
+ {
+ memset( &event, 0, sizeof( event ) );
+ event.type = kDNSRegistrationEventTypeRegistered;
+ object->callback( object->callbackContext, object, kDNSNoErr, &event );
+ }
+ break;
+
+ case mStatus_NameConflict:
+ {
+ DNSStatus err;
+ mDNSBool remove;
+
+ debugf( DEBUG_NAME "registration callback: \"%##s\" name conflict", inSet->RR_SRV.resrec.name.c );
+
+ // Name conflict. If the auto-rename option is enabled, uniquely rename the service and re-register it. Otherwise,
+ // remove the object so they cannot try to use it in the callback and notify the client of the name conflict.
+
+ remove = mDNStrue;
+ if( object->flags & kDNSRegistrationFlagAutoRenameOnConflict )
+ {
+ err = mDNS_RenameAndReregisterService( inMDNS, inSet, mDNSNULL );
+ check_noerr( err );
+ if( err == mStatus_NoError )
+ {
+ debugf( DEBUG_NAME "registration callback: auto-renamed to \"%##s\"", inSet->RR_SRV.resrec.name.c );
+ remove = mDNSfalse;
+ }
+ }
+ if( remove )
+ {
+ object = DNSRegistrationRemoveObject( object );
+ require( object, exit );
+
+ // Notify the client of the name collision.
+
+ if( object->callback )
+ {
+ memset( &event, 0, sizeof( event ) );
+ event.type = kDNSRegistrationEventTypeNameCollision;
+ object->callback( object->callbackContext, object, kDNSNoErr, &event );
+ }
+
+ // Notify the client that the registration is being released.
+
+ if( object->callback )
+ {
+ memset( &event, 0, sizeof( event ) );
+ event.type = kDNSRegistrationEventTypeRelease;
+ object->callback( object->callbackContext, object, kDNSNoErr, &event );
+ }
+
+ // When a name conflict occurs, mDNS will not send a separate mem free result so free the memory here.
+
+ DNSMemFree( object );
+ }
+ break;
+ }
+
+ case mStatus_MemFree:
+ debugf( DEBUG_NAME "registration callback: \"%##s\" memory free", inSet->RR_SRV.resrec.name.c );
+
+ if( object->set.RR_TXT.resrec.rdata != &object->set.RR_TXT.rdatastorage )
+ {
+ // Standard TXT record was updated with new data so free that data separately.
+
+ DNSMemFree( object->set.RR_TXT.resrec.rdata );
+ }
+ DNSMemFree( object );
+ break;
+
+ default:
+ debugf( DEBUG_NAME "registration callback: \"%##s\" unknown result %d", inSet->RR_SRV.resrec.name.c, inResult );
+ break;
+ }
+
+exit:
+ DNSServicesUnlock();
+}
+
+//===========================================================================================================================
+// DNSNoSuchServiceRegistrationPrivateCallBack
+//===========================================================================================================================
+
+mDNSlocal void DNSNoSuchServiceRegistrationPrivateCallBack( mDNS * const inMDNS, AuthRecord * const inRR, mStatus inResult )
+{
+ DNSRegistrationRef object;
+ DNSRegistrationEvent event;
+
+ DNS_UNUSED( inMDNS );
+
+ DNSServicesLock();
+
+ // Exit if object is no longer valid. Should never happen.
+
+ object = (DNSRegistrationRef) inRR->RecordContext;
+ require( object, exit );
+
+ // Dispatch based on the status code.
+
+ switch( inResult )
+ {
+ case mStatus_NoError:
+ debugf( DEBUG_NAME "registration callback: \"%##s\" name successfully registered", inRR->resrec.name.c );
+
+ // Notify the client of a successful registration.
+
+ if( object->callback )
+ {
+ memset( &event, 0, sizeof( event ) );
+ event.type = kDNSRegistrationEventTypeRegistered;
+ object->callback( object->callbackContext, object, kDNSNoErr, &event );
+ }
+ break;
+
+ case mStatus_NameConflict:
+ {
+ debugf( DEBUG_NAME "registration callback: \"%##s\" name conflict", inRR->resrec.name.c );
+
+ // Name conflict. Name conflicts for no-such-service registrations often do not make sense since the main goal
+ // is to assert that no other service exists with a name. Because of this, name conflicts should be handled by
+ // the code registering the no-such-service since it is likely that if another service is already using the
+ // name that the service registering the no-such-service should rename its other services as well. The name
+ // collision client callback invoked here can do any of this client-specific behavior. It may be worth adding
+ // support for the auto-rename feature in the future though, if that becomes necessary.
+
+ object = DNSRegistrationRemoveObject( object );
+ require( object, exit );
+
+ // Notify the client of the name collision.
+
+ if( object->callback )
+ {
+ memset( &event, 0, sizeof( event ) );
+ event.type = kDNSRegistrationEventTypeNameCollision;
+ object->callback( object->callbackContext, object, kDNSNoErr, &event );
+ }
+
+ // Notify the client that the registration is being released.
+
+ if( object->callback )
+ {
+ memset( &event, 0, sizeof( event ) );
+ event.type = kDNSRegistrationEventTypeRelease;
+ object->callback( object->callbackContext, object, kDNSNoErr, &event );
+ }
+
+ // When a name conflict occurs, mDNS will not send a separate mem free result so free the memory here.
+
+ DNSMemFree( object );
+ break;
+ }
+
+ case mStatus_MemFree:
+ debugf( DEBUG_NAME "registration callback: \"%##s\" memory free", inRR->resrec.name.c );
+
+ DNSMemFree( object );
+ break;
+
+ default:
+ debugf( DEBUG_NAME "registration callback: \"%##s\" unknown result %d", inRR->resrec.name.c, inResult );
+ break;
+ }
+
+exit:
+ DNSServicesUnlock();
+}
+
+//===========================================================================================================================
+// DNSRegistrationUpdateCallBack
+//===========================================================================================================================
+
+mDNSlocal void DNSRegistrationUpdateCallBack( mDNS * const inMDNS, AuthRecord * const inRR, RData *inOldData )
+{
+ DNS_UNUSED( inMDNS );
+
+ check( inRR );
+ check( inOldData );
+
+ if( inOldData != &inRR->rdatastorage )
+ {
+ DNSMemFree( inOldData );
+ }
+}
+
+//===========================================================================================================================
+// DNSRegistrationFindObject
+//
+// Warning: Assumes the DNS lock is held.
+//===========================================================================================================================
+
+mDNSlocal DNSRegistrationRef * DNSRegistrationFindObject( DNSRegistrationRef inRef )
+{
+ DNSRegistration ** p;
+
+ for( p = &gDNSRegistrationList; *p; p = &( *p )->next )
+ {
+ if( *p == inRef )
+ {
+ break;
+ }
+ }
+ return( p );
+}
+
+//===========================================================================================================================
+// DNSRegistrationRemoveObject
+//
+// Warning: Assumes the DNS lock is held.
+//===========================================================================================================================
+
+mDNSlocal DNSRegistrationRef DNSRegistrationRemoveObject( DNSRegistrationRef inRef )
+{
+ DNSRegistration ** p;
+ DNSRegistration * found;
+
+ for( p = &gDNSRegistrationList; *p; p = &( *p )->next )
+ {
+ if( *p == inRef )
+ {
+ break;
+ }
+ }
+ found = *p;
+ if( found )
+ {
+ *p = found->next;
+ }
+ return( found );
+}
+
+#if 0
+#pragma mark -
+#pragma mark == Domain Registration ==
+#endif
+
+//===========================================================================================================================
+// DNSDomainRegistrationCreate
+//===========================================================================================================================
+
+DNSStatus
+ DNSDomainRegistrationCreate(
+ DNSDomainRegistrationFlags inFlags,
+ const char * inName,
+ DNSDomainRegistrationType inType,
+ DNSDomainRegistrationRef * outRef )
+{
+ DNSStatus err;
+ DNSDomainRegistration * objectPtr;
+
+ objectPtr = mDNSNULL;
+
+ // Check parameters.
+
+ DNSServicesLock();
+ require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr );
+ require_action( ( inFlags & ~kDNSDomainRegistrationCreateValidFlags ) == 0, exit, err = kDNSBadFlagsErr );
+ require_action( inName, exit, err = kDNSBadParamErr );
+ require_action( inType < kDNSDomainRegistrationTypeMax, exit, err = kDNSBadParamErr );
+
+ // Allocate the object and set it up.
+
+ err = DNSMemAlloc( sizeof( *objectPtr ), &objectPtr );
+ require_noerr( err, exit );
+ memset( objectPtr, 0, sizeof( *objectPtr ) );
+
+ objectPtr->flags = inFlags;
+
+ // Add the object to the list.
+
+ objectPtr->next = gDNSDomainRegistrationList;
+ gDNSDomainRegistrationList = objectPtr;
+
+ // Register the domain with mDNS.
+
+ err = mDNS_AdvertiseDomains( gMDNSPtr, &objectPtr->rr, (mDNS_DomainType) inType, mDNSInterface_Any, (char *) inName );
+ require_noerr( err, exit );
+
+ if( outRef )
+ {
+ *outRef = objectPtr;
+ }
+
+exit:
+ if( err && objectPtr )
+ {
+ DNSDomainRegistrationRemoveObject( objectPtr );
+ DNSMemFree( objectPtr );
+ }
+ DNSServicesUnlock();
+ return( err );
+}
+
+//===========================================================================================================================
+// DNSDomainRegistrationRelease
+//===========================================================================================================================
+
+DNSStatus DNSDomainRegistrationRelease( DNSDomainRegistrationRef inRef, DNSDomainRegistrationFlags inFlags )
+{
+ DNSStatus err;
+
+ DNSServicesLock();
+ require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr );
+ require_action( inRef, exit, err = kDNSBadReferenceErr );
+ require_action( ( inFlags & ~kDNSDomainRegistrationReleaseValidFlags ) == 0, exit, err = kDNSBadFlagsErr );
+
+ // Remove the object and deregister the domain with mDNS.
+
+ inRef = DNSDomainRegistrationRemoveObject( inRef );
+ require_action( inRef, exit, err = kDNSBadReferenceErr );
+
+ mDNS_StopAdvertiseDomains( gMDNSPtr, &inRef->rr );
+
+ // Release the memory used by the object.
+
+ DNSMemFree( inRef );
+ err = kDNSNoErr;
+
+exit:
+ DNSServicesUnlock();
+ return( err );
+}
+
+//===========================================================================================================================
+// DNSDomainRegistrationRemoveObject
+//
+// Warning: Assumes the DNS lock is held.
+//===========================================================================================================================
+
+mDNSlocal DNSDomainRegistrationRef DNSDomainRegistrationRemoveObject( DNSDomainRegistrationRef inRef )
+{
+ DNSDomainRegistration ** p;
+ DNSDomainRegistration * found;
+
+ for( p = &gDNSDomainRegistrationList; *p; p = &( *p )->next )
+ {
+ if( *p == inRef )
+ {
+ break;
+ }
+ }
+ found = *p;
+ if( found )
+ {
+ *p = found->next;
+ }
+ return( found );
+}
+
+#if 0
+#pragma mark -
+#pragma mark == Domain Registration ==
+#endif
+
+//===========================================================================================================================
+// DNSHostRegistrationCreate
+//===========================================================================================================================
+
+DNSStatus
+ DNSHostRegistrationCreate(
+ DNSHostRegistrationFlags inFlags,
+ const char * inName,
+ const char * inDomain,
+ const DNSNetworkAddress * inAddr,
+ const char * inInterfaceName,
+ DNSHostRegistrationCallBack inCallBack,
+ void * inCallBackContext,
+ DNSHostRegistrationRef * outRef )
+{
+ DNSStatus err;
+ domainname name;
+ DNSHostRegistration * object;
+ mDNSInterfaceID interfaceID;
+ mDNSv4Addr ip;
+ char buffer[ 64 ];
+
+ object = mDNSNULL;
+
+ // Check parameters.
+
+ DNSServicesLock();
+ require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr );
+ require_action( ( inFlags & ~kDNSHostRegistrationCreateValidFlags ) == 0, exit, err = kDNSBadFlagsErr );
+ require_action( inName, exit, err = kDNSBadParamErr );
+ require_action( inAddr && ( inAddr->addressType == kDNSNetworkAddressTypeIPv4 ), exit, err = kDNSUnsupportedErr );
+ require_action( !inInterfaceName ||
+ ( strlen( inInterfaceName ) < sizeof( object->interfaceName ) ), exit, err = kDNSBadParamErr );
+
+ // Default to the local domain when null is passed in.
+
+ if( !inDomain )
+ {
+ inDomain = kDNSLocalDomain;
+ }
+
+ // If the caller only wants to add if not found, check if a host with this name was already registered.
+
+ MakeDomainNameFromDNSNameString( &name, inName );
+ AppendDNSNameString( &name, inDomain );
+
+ if( inFlags & kDNSHostRegistrationFlagOnlyIfNotFound )
+ {
+ object = DNSHostRegistrationFindObjectByName( &name );
+ if( object )
+ {
+ ++object->refCount;
+ if( outRef )
+ {
+ *outRef = object;
+ }
+ object = mDNSNULL;
+ err = kDNSNoErr;
+ goto exit;
+ }
+ }
+
+ // Allocate the object and set it up.
+
+ err = DNSMemAlloc( sizeof( *object ), &object );
+ require_noerr( err, exit );
+ memset( object, 0, sizeof( *object ) );
+
+ MakeDomainLabelFromLiteralString( &object->name, inName );
+ MakeDomainLabelFromLiteralString( &object->domain, inDomain );
+ object->refCount = 1;
+ object->flags = inFlags;
+ object->callback = inCallBack;
+ object->callbackContext = inCallBackContext;
+
+ // Set up the interface for interface-specific operations.
+
+ if( inInterfaceName && ( *inInterfaceName != '\0' ) )
+ {
+ strcpy( object->interfaceName, inInterfaceName );
+
+ err = mDNSPlatformInterfaceNameToID( gMDNSPtr, inInterfaceName, &interfaceID );
+ require_noerr( err, exit );
+ }
+ else
+ {
+ interfaceID = mDNSInterface_Any;
+ }
+
+ // Convert the IP address to a format suitable for mDNS.
+
+ ip.NotAnInteger = inAddr->u.ipv4.addr.v32;
+
+ // Set up the resource records and name.
+
+ mDNS_SetupResourceRecord( &object->RR_A, mDNSNULL, interfaceID, kDNSType_A, 60, kDNSRecordTypeUnique,
+ DNSHostRegistrationPrivateCallBack, object );
+ mDNS_SetupResourceRecord( &object->RR_PTR, mDNSNULL, interfaceID, kDNSType_PTR, 60, kDNSRecordTypeKnownUnique,
+ DNSHostRegistrationPrivateCallBack, object );
+
+ AssignDomainName( object->RR_A.resrec.name, name );
+
+ mDNS_snprintf( buffer, sizeof( buffer ), "%d.%d.%d.%d.in-addr.arpa.", ip.b[ 3 ], ip.b[ 2 ], ip.b[ 1 ], ip.b[ 0 ] );
+ MakeDomainNameFromDNSNameString( &object->RR_PTR.resrec.name, buffer );
+
+ object->RR_A.resrec.rdata->u.ip = ip;
+ AssignDomainName( object->RR_PTR.resrec.rdata->u.name, object->RR_A.resrec.name );
+
+ // Add the object to the list.
+
+ object->next = gDNSHostRegistrationList;
+ gDNSHostRegistrationList = object;
+
+ // Register with mDNS.
+
+ err = mDNS_Register( gMDNSPtr, &object->RR_A );
+ require_noerr( err, exit );
+
+ err = mDNS_Register( gMDNSPtr, &object->RR_PTR );
+ if( err != mStatus_NoError )
+ {
+ mDNS_Deregister( gMDNSPtr, &object->RR_A );
+ }
+ require_noerr( err, exit );
+
+ if( outRef )
+ {
+ *outRef = object;
+ }
+
+exit:
+ if( err && object )
+ {
+ DNSHostRegistration ** p;
+
+ p = DNSHostRegistrationFindObject( object );
+ *p = object->next;
+ DNSMemFree( object );
+ }
+ DNSServicesUnlock();
+ return( err );
+}
+
+//===========================================================================================================================
+// DNSHostRegistrationRelease
+//===========================================================================================================================
+
+DNSStatus DNSHostRegistrationRelease( DNSHostRegistrationRef inRef, DNSHostRegistrationFlags inFlags )
+{
+ DNSStatus err;
+ DNSHostRegistrationRef * p;
+
+ DNSServicesLock();
+ require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr );
+ require_action( inRef, exit, err = kDNSBadReferenceErr );
+ require_action( ( inFlags & ~kDNSHostRegistrationReleaseValidFlags ) == 0, exit, err = kDNSBadFlagsErr );
+
+ // Decrement the reference count and if it drops to 0, remove the object and deregister with mDNS.
+
+ p = DNSHostRegistrationFindObject( inRef );
+ inRef = *p;
+ require_action( inRef, exit, err = kDNSBadReferenceErr );
+
+ check( inRef->refCount > 0 );
+ if( --inRef->refCount == 0 )
+ {
+ *p = inRef->next;
+
+ mDNS_Deregister( gMDNSPtr, &inRef->RR_A );
+ mDNS_Deregister( gMDNSPtr, &inRef->RR_PTR );
+
+ // Release the memory used by the object.
+
+ DNSMemFree( inRef );
+ }
+ err = kDNSNoErr;
+
+exit:
+ DNSServicesUnlock();
+ return( err );
+}
+
+//===========================================================================================================================
+// DNSHostRegistrationFindObject
+//
+// Warning: Assumes the DNS lock is held.
+//===========================================================================================================================
+
+mDNSlocal DNSHostRegistrationRef * DNSHostRegistrationFindObject( DNSHostRegistrationRef inRef )
+{
+ DNSHostRegistration ** p;
+
+ for( p = &gDNSHostRegistrationList; *p; p = &( *p )->next )
+ {
+ if( *p == inRef )
+ {
+ break;
+ }
+ }
+ return( p );
+}
+
+//===========================================================================================================================
+// DNSHostRegistrationFindObjectByName
+//
+// Warning: Assumes the DNS lock is held.
+//===========================================================================================================================
+
+mDNSlocal DNSHostRegistrationRef DNSHostRegistrationFindObjectByName( const domainname *inName )
+{
+ DNSHostRegistration * p;
+
+ check( inName );
+
+ for( p = gDNSHostRegistrationList; p; p = p->next )
+ {
+ if( SameDomainName( &p->RR_A.resrec.name, inName ) )
+ {
+ break;
+ }
+ }
+ return( p );
+}
+
+//===========================================================================================================================
+// DNSHostRegistrationPrivateCallBack
+//===========================================================================================================================
+
+mDNSlocal void DNSHostRegistrationPrivateCallBack( mDNS * const inMDNS, AuthRecord *const inRR, mStatus inResult )
+{
+ DNSHostRegistrationRef object;
+
+ DNS_UNUSED( inMDNS );
+
+ DNSServicesLock();
+
+ // Exit if object is no longer valid. Should never happen.
+
+ object = (DNSHostRegistrationRef) inRR->RecordContext;
+ require( object, exit );
+
+ // Dispatch based on the status code.
+
+ if( inResult == mStatus_NoError )
+ {
+ debugf( DEBUG_NAME "host registration callback: \"%##s\" name successfully registered", inRR->resrec.name.c );
+ if( object->callback )
+ {
+ object->callback( object->callbackContext, object, kDNSNoErr, mDNSNULL );
+ }
+ }
+ else if( inResult == mStatus_NameConflict )
+ {
+ debugf( DEBUG_NAME "host registration callback: \"%##s\" name conflict", inRR->resrec.name.c );
+
+ if( object->flags & kDNSHostRegistrationFlagAutoRenameOnConflict )
+ {
+ DNSStatus err;
+ domainname name;
+
+ // De-register any resource records still registered.
+
+ if( object->RR_A.resrec.RecordType )
+ {
+ mDNS_Deregister( gMDNSPtr, &object->RR_A );
+ }
+ if( object->RR_PTR.resrec.RecordType )
+ {
+ mDNS_Deregister( gMDNSPtr, &object->RR_PTR );
+ }
+
+ // Rename the host and re-register to try again.
+
+ IncrementLabelSuffix( &object->name, mDNSfalse );
+ name.c[ 0 ] = 0;
+ AppendDomainLabel( &name, &object->name );
+ AppendDomainLabel( &name, &object->domain );
+ AssignDomainName( object->RR_PTR.resrec.name, name );
+
+ err = mDNS_Register( gMDNSPtr, &object->RR_A );
+ check_noerr( err );
+
+ err = mDNS_Register( gMDNSPtr, &object->RR_PTR );
+ check_noerr( err );
+ }
+ else
+ {
+ if( object->callback )
+ {
+ object->callback( object->callbackContext, object, kDNSNameConflictErr, mDNSNULL );
+ }
+ }
+ }
+ else
+ {
+ debugf( DEBUG_NAME "host registration callback: \"%##s\" unknown result", inRR->resrec.name.c, inResult );
+ }
+
+exit:
+ DNSServicesUnlock();
+}
+
+#if 0
+#pragma mark -
+#pragma mark == Utilities ==
+#endif
+
+//===========================================================================================================================
+// DNSMemAlloc
+//===========================================================================================================================
+
+mDNSlocal DNSStatus DNSMemAlloc( size_t inSize, void *outMem )
+{
+ void * mem;
+
+ check( inSize > 0 );
+ check( outMem );
+
+ mem = malloc( inSize );
+ *( (void **) outMem ) = mem;
+ if( mem )
+ {
+ return( kDNSNoErr );
+ }
+ return( kDNSNoMemoryErr );
+}
+
+//===========================================================================================================================
+// DNSMemFree
+//===========================================================================================================================
+
+mDNSlocal void DNSMemFree( void *inMem )
+{
+ check( inMem );
+
+ free( inMem );
+}
+
+//===========================================================================================================================
+// DNSDynamicTextRecordBuildEscaped
+//===========================================================================================================================
+
+DNSStatus DNSDynamicTextRecordBuildEscaped( const char *inFormat, void *outTextRecord, size_t *outSize )
+{
+ DNSStatus err;
+ size_t size;
+ void * textRecord;
+
+ textRecord = NULL;
+
+ // Calculate the size of the built text record, allocate a buffer for it, then build it in that buffer.
+
+ err = DNSTextRecordValidate( inFormat, 0x7FFFFFFF, NULL, &size );
+ require_noerr( err, exit );
+
+ textRecord = malloc( size );
+ require_action( textRecord, exit, err = kDNSNoMemoryErr );
+
+ err = DNSTextRecordValidate( inFormat, size, textRecord, &size );
+ require_noerr( err, exit );
+
+ // Success!
+
+ if( outTextRecord )
+ {
+ *( (void **) outTextRecord ) = textRecord;
+ textRecord = NULL;
+ }
+ if( outSize )
+ {
+ *outSize = size;
+ }
+
+exit:
+ if( textRecord )
+ {
+ free( textRecord );
+ }
+ return( err );
+}
+
+//===========================================================================================================================
+// DNSDynamicTextRecordAppendCString
+//===========================================================================================================================
+
+DNSStatus DNSDynamicTextRecordAppendCString( void *ioTxt, size_t *ioTxtSize, const char *inName, const char *inValue )
+{
+ DNSStatus err;
+ size_t valueSize;
+
+ require_action( inName, exit, err = kDNSBadParamErr );
+ require_action( inValue, exit, err = kDNSBadParamErr );
+
+ if( inValue != kDNSTextRecordStringNoValue )
+ {
+ valueSize = strlen( inValue );
+ }
+ else
+ {
+ valueSize = kDNSTextRecordNoSize;
+ }
+ err = DNSDynamicTextRecordAppendData( ioTxt, ioTxtSize, inName, inValue, valueSize );
+ require_noerr( err, exit );
+
+exit:
+ return( err );
+}
+
+//===========================================================================================================================
+// DNSDynamicTextRecordAppendData
+//===========================================================================================================================
+
+DNSStatus
+ DNSDynamicTextRecordAppendData(
+ void * ioTxt,
+ size_t * ioTxtSize,
+ const char * inName,
+ const void * inValue,
+ size_t inValueSize )
+{
+ DNSStatus err;
+ size_t oldSize;
+ size_t newSize;
+ int hasName;
+ int hasValue;
+ void ** bufferPtr;
+ void * newBuffer;
+
+ require_action( ioTxt, exit, err = kDNSBadParamErr );
+ require_action( ioTxtSize, exit, err = kDNSBadParamErr );
+ require_action( inName, exit, err = kDNSBadParamErr );
+
+ // Check for special flags to indicate no name or no value is used (e.g. "color" instead of "color=").
+
+ hasName = ( inName != kDNSTextRecordStringNoValue ) && ( *inName != '\0' );
+ hasValue = ( inValue != kDNSTextRecordNoValue ) && ( inValueSize != kDNSTextRecordNoSize );
+ require_action( hasName || hasValue, exit, err = kDNSUnsupportedErr );
+
+ // Calculate the size needed for the new data (old size + length byte + name size + '=' + value size).
+
+ oldSize = *ioTxtSize;
+ newSize = oldSize + 1; // add length byte size
+ if( hasName )
+ {
+ newSize += strlen( inName ); // add name size
+ if( hasValue )
+ {
+ newSize += 1; // add '=' size
+ }
+ }
+ if( hasValue )
+ {
+ newSize += inValueSize; // add value size
+ }
+
+ // Reallocate the buffer to make room for the new data.
+
+ bufferPtr = (void **) ioTxt;
+ newBuffer = realloc( *bufferPtr, newSize );
+ require_action( newBuffer, exit, err = kDNSNoMemoryErr );
+ *bufferPtr = newBuffer;
+
+ err = DNSTextRecordAppendData( newBuffer, oldSize, newSize, inName, inValue, inValueSize, &newSize );
+ require_noerr( err, exit );
+
+ // Success!
+
+ *ioTxtSize = newSize;
+
+exit:
+ return( err );
+}
+
+//===========================================================================================================================
+// DNSDynamicTextRecordRelease
+//===========================================================================================================================
+
+void DNSDynamicTextRecordRelease( void *inTxt )
+{
+ if( inTxt )
+ {
+ free( inTxt );
+ }
+}
+
+//===========================================================================================================================
+// DNSTextRecordAppendCString
+//===========================================================================================================================
+
+DNSStatus
+ DNSTextRecordAppendCString(
+ void * inTxt,
+ size_t inTxtSize,
+ size_t inTxtMaxSize,
+ const char * inName,
+ const char * inValue,
+ size_t * outTxtSize )
+{
+ DNSStatus err;
+ size_t valueSize;
+
+ require_action( inName, exit, err = kDNSBadParamErr );
+ require_action( inValue, exit, err = kDNSBadParamErr );
+
+ if( inValue != kDNSTextRecordStringNoValue )
+ {
+ valueSize = strlen( inValue );
+ }
+ else
+ {
+ valueSize = kDNSTextRecordNoSize;
+ }
+ err = DNSTextRecordAppendData( inTxt, inTxtSize, inTxtMaxSize, inName, inValue, valueSize, outTxtSize );
+ require_noerr( err, exit );
+
+exit:
+ return( err );
+}
+
+//===========================================================================================================================
+// DNSTextRecordAppendData
+//===========================================================================================================================
+
+DNSStatus
+ DNSTextRecordAppendData(
+ void * inTxt,
+ size_t inTxtSize,
+ size_t inTxtMaxSize,
+ const char * inName,
+ const void * inValue,
+ size_t inValueSize,
+ size_t * outTxtSize )
+{
+ DNSStatus err;
+ mDNSu8 * p;
+ int hasName;
+ int hasValue;
+ size_t size;
+ size_t newSize;
+ const mDNSu8 * q;
+
+ require_action( inTxt, exit, err = kDNSBadParamErr );
+ require_action( inName, exit, err = kDNSBadParamErr );
+
+ // Check for special flags to indicate no name or no value is used (e.g. "color" instead of "color=").
+
+ hasName = ( inName != kDNSTextRecordStringNoValue ) && ( *inName != '\0' );
+ hasValue = ( inValue != kDNSTextRecordNoValue ) && ( inValueSize != kDNSTextRecordNoSize );
+ require_action( hasName || hasValue, exit, err = kDNSUnsupportedErr );
+
+ // Calculate the size and make sure there is enough total room and enough room in an individual segment.
+
+ size = 0;
+ if( hasName )
+ {
+ size += strlen( inName ); // add name size
+ if( hasValue )
+ {
+ size += 1; // add '=' size
+ }
+ }
+ if( hasValue )
+ {
+ size += inValueSize; // add value size
+ }
+ newSize = inTxtSize + 1 + size; // old size + length byte + new data
+
+ require_action( size < 256, exit, err = kDNSNoMemoryErr );
+ require_action( newSize <= inTxtMaxSize, exit, err = kDNSNoMemoryErr );
+
+ // Write the length-prefix byte containing the size of this segment.
+
+ p = ( (mDNSu8 *) inTxt ) + inTxtSize;
+ *p++ = (mDNSu8) size;
+
+ // Copy the name.
+
+ if( hasName )
+ {
+ q = (const mDNSu8 *) inName;
+ while( *q != '\0' )
+ {
+ *p++ = *q++;
+ }
+ if( hasValue )
+ {
+ *p++ = '=';
+ }
+ }
+ if( hasValue )
+ {
+ // Copy the value.
+
+ q = (const mDNSu8 *) inValue;
+ while( inValueSize-- > 0 )
+ {
+ *p++ = *q++;
+ }
+ }
+
+ // Success!
+
+ if( outTxtSize )
+ {
+ *outTxtSize = newSize;
+ }
+ err = kDNSNoErr;
+
+exit:
+ return( err );
+}
+
+//===========================================================================================================================
+// DNSTextRecordEscape
+//===========================================================================================================================
+
+DNSStatus DNSTextRecordEscape( const void *inTextRecord, size_t inTextSize, char **outEscapedString )
+{
+ DNSStatus err;
+ const DNSUInt8 * src;
+ const DNSUInt8 * end;
+ DNSUInt8 * dstStorage;
+ DNSUInt8 * dst;
+ int size;
+
+ check( inTextRecord || ( inTextSize == 0 ) );
+
+ // Mac OS X uses a single null-terminated string to hold all the text record data with a \001 byte to delimit
+ // individual records within the entire block. The following code converts a packed array of length-prefixed
+ // records into a single \001-delimited, null-terminated string. Allocate size + 1 for the null terminator.
+
+ dstStorage = (DNSUInt8 *) malloc( inTextSize + 1 );
+ require_action( dstStorage, exit, err = kDNSNoMemoryErr );
+ dst = dstStorage;
+
+ if( inTextSize > 0 )
+ {
+ src = (const DNSUInt8 *) inTextRecord;
+ end = src + inTextSize;
+ while( src < end )
+ {
+ size = *src++;
+ if( ( src + size ) > end )
+ {
+ // Malformed TXT record. Most likely an old-style TXT record.
+
+ src = NULL;
+ break;
+ }
+ while( size-- > 0 )
+ {
+ *dst++ = *src++;
+ }
+ *dst++ = '\001'; // \001 record separator. May be overwritten later if this is the last record.
+ }
+ check( ( dst - dstStorage ) <= inTextSize );
+ if( src != end )
+ {
+ // Malformed TXT record. Assume an old-style TXT record and use the TXT record as a whole.
+
+ memcpy( dstStorage, inTextRecord, inTextSize );
+ dstStorage[ inTextSize ] = '\0';
+ }
+ else
+ {
+ dstStorage[ inTextSize - 1 ] = '\0';
+ }
+ }
+ else
+ {
+ // No text record data so just return an empty string.
+
+ *dst = '\0';
+ }
+
+ // Success!
+
+ if( outEscapedString )
+ {
+ *outEscapedString = (char *) dstStorage;
+ dstStorage = NULL;
+ }
+ err = kDNSNoErr;
+
+exit:
+ if( dstStorage )
+ {
+ free( dstStorage );
+ }
+ return( err );
+}
+
+//===========================================================================================================================
+// DNSNameValidate
+//===========================================================================================================================
+
+DNSStatus DNSNameValidate( const char *inName )
+{
+ DNSStatus err;
+ mDNSu8 * p;
+ domainname name;
+
+ p = MakeDomainNameFromDNSNameString( &name, inName );
+ if( p )
+ {
+ err = kDNSNoErr;
+ }
+ else
+ {
+ err = kDNSBadParamErr;
+ }
+ return( err );
+}
+
+//===========================================================================================================================
+// DNSServiceTypeValidate
+//===========================================================================================================================
+
+DNSStatus DNSServiceTypeValidate( const char *inServiceType )
+{
+ DNSStatus err;
+ mDNSu8 * p;
+ domainname type;
+ domainname domain;
+ domainname fqdn;
+
+ // Construct a fake fully-qualified domain name with a known good domain and the service type to be verified since
+ // there is currently no canned way to test just a service type by itself.
+
+ p = MakeDomainNameFromDNSNameString( &type, inServiceType );
+ if( !p )
+ {
+ err = kDNSBadParamErr;
+ goto exit;
+ }
+
+ p = MakeDomainNameFromDNSNameString( &domain, "local." );
+ if( !p )
+ {
+ err = kDNSBadParamErr;
+ goto exit;
+ }
+
+ p = ConstructServiceName( &fqdn, mDNSNULL, &type, &domain );
+ if( !p )
+ {
+ err = kDNSBadParamErr;
+ goto exit;
+ }
+
+ err = kDNSNoErr;
+
+exit:
+ return( err );
+}
+
+//===========================================================================================================================
+// DNSTextRecordValidate
+//===========================================================================================================================
+
+DNSStatus DNSTextRecordValidate( const char *inText, size_t inMaxSize, void *outRecord, size_t *outActualSize )
+{
+ DNSStatus err;
+ const mDNSu8 * p;
+ size_t totalSize;
+ mDNSu8 sectionSize;
+ mDNSu8 * dst;
+ mDNSu8 * section;
+
+ require_action( inText, exit, err = kDNSBadParamErr );
+
+ // A DNS TXT record consists of a packed block of length-prefixed strings of up to 255 characters each. To allow
+ // this to be described with a null-terminated C-string, a special escape sequence of \001 is used to separate
+ // individual character strings within the C-string.
+
+ totalSize = 0;
+ sectionSize = 0;
+ dst = (mDNSu8 *) outRecord;
+ section = dst;
+
+ p = (const mDNSu8 *) inText;
+ while( *p != '\0' )
+ {
+ ++totalSize;
+ if( totalSize >= inMaxSize )
+ {
+ err = kDNSBadParamErr;
+ goto exit;
+ }
+
+ if( *p == '\001' )
+ {
+ // Separator Escape sequence, start a new string section.
+
+ if( sectionSize <= 0 )
+ {
+ err = kDNSBadParamErr;
+ goto exit;
+ }
+ sectionSize = 0;
+ if( section )
+ {
+ section = &dst[ totalSize ];
+ section[ 0 ] = 0;
+ }
+ }
+ else
+ {
+ if( sectionSize >= 255 )
+ {
+ err = kDNSBadParamErr;
+ goto exit;
+ }
+ ++sectionSize;
+ if( section )
+ {
+ section[ 0 ] = sectionSize;
+ section[ sectionSize ] = *p;
+ }
+ }
+ ++p;
+ }
+ ++totalSize;
+
+ // Success!
+
+ if( outActualSize )
+ {
+ *outActualSize = totalSize;
+ }
+ err = kDNSNoErr;
+
+exit:
+ return( err );
+}
+
+//===========================================================================================================================
+// MDNSAddrToDNSAddress
+//===========================================================================================================================
+
+mDNSlocal void MDNSAddrToDNSAddress( const mDNSAddr *inAddr, DNSNetworkAddress *outAddr )
+{
+ switch( inAddr->type )
+ {
+ case mDNSAddrType_IPv4:
+ outAddr->addressType = kDNSNetworkAddressTypeIPv4;
+ outAddr->u.ipv4.addr.v32 = inAddr->ip.v4.NotAnInteger;
+ break;
+
+ case mDNSAddrType_IPv6:
+ outAddr->addressType = kDNSNetworkAddressTypeIPv6;
+ outAddr->u.ipv6.addr.v32[ 0 ] = inAddr->ip.v6.l[ 0 ];
+ outAddr->u.ipv6.addr.v32[ 1 ] = inAddr->ip.v6.l[ 1 ];
+ outAddr->u.ipv6.addr.v32[ 2 ] = inAddr->ip.v6.l[ 2 ];
+ outAddr->u.ipv6.addr.v32[ 3 ] = inAddr->ip.v6.l[ 3 ];
+ break;
+
+ default:
+ outAddr->addressType = kDNSNetworkAddressTypeInvalid;
+ break;
+ }
+}
+
+#ifdef __cplusplus
+ }
+#endif
--- /dev/null
+/*
+ * 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: DNSServices.h,v $
+Revision 1.8 2003/08/20 06:44:24 bradley
+Updated to latest internal version of the Rendezvous for Windows code: Added support for interface
+specific registrations; Added support for no-such-service registrations; Added support for host
+name registrations; Added support for host proxy and service proxy registrations; Added support for
+registration record updates (e.g. TXT record updates); Added support for using either a single C
+string TXT record, a raw, pre-formatted TXT record potentially containing multiple character string
+entries, or a C-string containing a Mac OS X-style \001-delimited set of TXT record character
+strings; Added support in resolve callbacks for providing both a simplified C-string for TXT records
+and a ptr/size for the raw TXT record data; Added utility routines for dynamically building TXT
+records from a variety of sources (\001-delimited, individual strings, etc.) and converting TXT
+records to various formats for use in apps; Added utility routines to validate DNS names, DNS
+service types, and TXT records; Moved to portable address representation unions (byte-stream vs host
+order integer) for consistency, to avoid swapping between host and network byte order, and for IPv6
+support; Removed dependence on modified mDNSCore: define structures and prototypes locally; Added
+support for automatically renaming services on name conflicts; Detect and correct TXT records from
+old versions of mDNS that treated a TXT record as an arbitrary block of data, but prevent other
+malformed TXT records from being accepted; Added many more error codes; Added complete HeaderDoc for
+all constants, structures, typedefs, macros, and functions. Various other minor cleanup and fixes.
+
+Revision 1.7 2003/08/12 19:56:29 cheshire
+Update to APSL 2.0
+
+Revision 1.6 2003/07/02 21:20:10 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.5 2003/03/22 02:57:45 cheshire
+Updated mDNSWindows to use new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt")
+
+Revision 1.4 2003/02/20 00:59:05 cheshire
+Brought Windows code up to date so it complies with
+Josh Graessley's interface changes for IPv6 support.
+(Actual support for IPv6 on Windows will come later.)
+
+Revision 1.3 2002/09/21 20:44:57 zarzycki
+Added APSL info
+
+Revision 1.2 2002/09/20 05:58:02 bradley
+DNS Services for Windows
+
+*/
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @header DNSServices
+
+ @abstract DNS Services interfaces.
+
+ @discussion
+
+ DNS Services provides DNS service registration, domain and service discovery, and name resolving services.
+*/
+
+#ifndef __DNS_SERVICES__
+#define __DNS_SERVICES__
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#if 0
+#pragma mark == General ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined dns_check_compile_time
+
+ @abstract Performs a compile-time check of something such as the size of an int.
+
+ @discussion
+
+ This declares a unique array with a size that is determined by dividing 1 by the result of the compile-time expression
+ passed to the macro. If the expression evaluates to 0, this expression results in a divide by zero, which is illegal
+ and generates a compile-time error.
+
+ For example:
+
+ dns_check_compile_time( sizeof( int ) == 4 );
+
+ Note: This only works with compile-time expressions.
+ Note: This only works in places where extern declarations are allowed (e.g. global scope).
+
+ References:
+
+ <http://www.jaggersoft.com/pubs/CVu11_3.html>
+ <http://www.jaggersoft.com/pubs/CVu11_5.html>
+
+ Note: The following macros differ from the macros on the www.jaggersoft.com web site because those versions do not
+ work with GCC due to GCC allow a zero-length array. Using a divide-by-zero condition turned out to be more portable.
+*/
+
+#define dns_check_compile_time( X ) extern int dns_unique_name[ 1 / (int)( ( X ) ) ]
+
+#define dns_unique_name dns_make_name_wrapper( __LINE__ )
+#define dns_make_name_wrapper( X ) dns_make_name( X )
+#define dns_make_name( X ) dns_check_compile_time_ ## X
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined dns_check_compile_time_code
+
+ @abstract Perform a compile-time check, suitable for placement in code, of something such as the size of an int.
+
+ @discussion
+
+ This creates a switch statement with an existing case for 0 and an additional case using the result of a
+ compile-time expression. A switch statement cannot have two case labels with the same constant so if the
+ compile-time expression evaluates to 0, it is illegal and generates a compile-time error. If the compile-time
+ expression does not evaluate to 0, the resulting value is used as the case label and it compiles without error.
+
+ For example:
+
+ dns_check_compile_time_code( sizeof( int ) == 4 );
+
+ Note: This only works with compile-time expressions.
+ Note: This does not work in a global scope so it must be inside a function.
+
+ References:
+
+ <http://www.jaggersoft.com/pubs/CVu11_3.html>
+ <http://www.jaggersoft.com/pubs/CVu11_5.html>
+*/
+
+#define dns_check_compile_time_code( X ) switch( 0 ) { case 0: case X:; }
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DNS_LOCAL
+
+ @abstract Macro to make variables and functions static when debugging is off, but exported when debugging is on.
+
+ @discussion
+
+ Rather than using "static" directly, using this macros allows you to access these variables external while
+ debugging without being penalized for production builds.
+*/
+
+#if( DEBUG )
+ #define DNS_LOCAL
+#else
+ #define DNS_LOCAL static
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DNS_EXPORT
+
+ @abstract Macro to provide a visual clue that a variable or function is globally visible.
+*/
+
+#define DNS_EXPORT
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DNS_DEBUG_USE_ONLY
+ @abstract Macro to mark a variable as unused when debugging is turned off.
+ @discussion
+
+ Variables are sometimes needed only for debugging. When debugging is turned off, these debug-only variables
+ generate compiler warnings about unused variables. To eliminate these warnings, use the DNS_DEBUG_USE_ONLY macro
+ to indicate the variables are for debugging only.
+*/
+
+#if( DEBUG )
+ #define DNS_DEBUG_USE_ONLY( X )
+#else
+ #define DNS_DEBUG_USE_ONLY( X ) (void)( X )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined DNS_UNUSED
+ @abstract Macro to mark a variable as unused.
+ @discussion
+
+ There is no universally supported pragma/attribute for indicating a variable is unused. DNS_UNUSED lets
+ indicate a variable is unused in a manner that is supported by most compilers.
+*/
+
+#define DNS_UNUSED( X ) (void)( X )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DNSUInt8
+
+ @abstract 8-bit unsigned data type.
+*/
+
+typedef unsigned char DNSUInt8;
+
+dns_check_compile_time( sizeof( DNSUInt8 ) == 1 );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DNSUInt16
+
+ @abstract 16-bit unsigned data type.
+*/
+
+typedef unsigned short DNSUInt16;
+
+dns_check_compile_time( sizeof( DNSUInt16 ) == 2 );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DNSUInt32
+
+ @abstract 32-bit unsigned data type.
+*/
+
+typedef unsigned long DNSUInt32;
+
+dns_check_compile_time( sizeof( DNSUInt32 ) == 4 );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DNSSInt32
+
+ @abstract 32-bit signed data type.
+*/
+
+typedef signed long DNSSInt32;
+
+dns_check_compile_time( sizeof( DNSSInt32 ) == 4 );
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DNSOpaque16
+
+ @abstract 16-bit opaque data type with 8-bit and 16-bit accessors.
+*/
+
+typedef union DNSOpaque16 DNSOpaque16;
+union DNSOpaque16
+{
+ DNSUInt8 v8[ 2 ];
+ DNSUInt16 v16;
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DNSOpaque32
+
+ @abstract 32-bit opaque data type with 8-bit, 16-bit, and 32-bit accessors.
+*/
+
+typedef union DNSOpaque32 DNSOpaque32;
+union DNSOpaque32
+{
+ DNSUInt8 v8[ 4 ];
+ DNSUInt16 v16[ 2 ];
+ DNSUInt32 v32;
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DNSOpaque128
+
+ @abstract 128-bit opaque data type with 8-bit, 16-bit, and 32-bit accessors.
+*/
+
+typedef union DNSOpaque128 DNSOpaque128;
+union DNSOpaque128
+{
+ DNSUInt8 v8[ 16 ];
+ DNSUInt16 v16[ 8 ];
+ DNSUInt32 v32[ 4 ];
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DNSCount
+
+ @abstract Count of at least 32-bits.
+*/
+
+typedef DNSUInt32 DNSCount;
+
+#if 0
+#pragma mark == Errors ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @enum DNSStatus
+
+ @abstract DNS Service status code.
+
+ @constant kDNSNoErr (0) Success. No error occurred.
+ @constant kDNSUnknownErr (-65537) An unknown error occurred.
+ @constant kDNSNoSuchNameErr (-65538) The name could not be found on the network.
+ @constant kDNSNoMemoryErr (-65539) Not enough memory was available.
+ @constant kDNSBadParamErr (-65540) A invalid or inappropriate parameter was specified.
+ @constant kDNSBadReferenceErr (-65541) A invalid or inappropriate reference was specified.
+ @constant kDNSBadStateErr (-65542) The current state does not allow the specified operation.
+ @constant kDNSBadFlagsErr (-65543) An invalid, inappropriate, or unsupported flag was specified.
+ @constant kDNSUnsupportedErr (-65544) The specified feature is not currently supported.
+ @constant kDNSNotInitializedErr (-65545) DNS Service has not been initialized.
+ @constant kDNSNoCacheErr (-65546) No cache was specified.
+ @constant kDNSAlreadyRegisteredErr (-65547) Service or host name is already registered.
+ @constant kDNSNameConflictErr (-65548) Name conflicts with another on the network.
+ @constant kDNSInvalidErr (-65549) A general error to indicate something is invalid.
+ @constant kDNSGrowCache (-65550) Cache needs to be grown (not used).
+ @constant kDNSIncompatibleErr (-65551) Version is incompatible.
+
+ @constant kDNSSizeErr (-65600) Size was too small or too big.
+ @constant kDNSMismatchErr (-65601) A data, version, etc. mismatch occurred.
+ @constant kDNSReadErr (-65602) Read failed.
+ @constant kDNSWriteErr (-65603) Write failed.
+ @constant kDNSCanceledErr (-65604) Operation was canceled.
+ @constant kDNSTimeoutErr (-65605) Operation timed out.
+ @constant kDNSConnectionErr (-65606) A disconnect or other connection error occurred.
+ @constant kDNSInUseErr (-65607) Object is in use (e.g. cannot reuse active param blocks).
+ @constant kDNSNoResourcesErr (-65608) Resources unavailable to perform the operation.
+ @constant kDNSEndingErr (-65609) Connection, session, or something is ending.
+
+ @constant kDNSConfigChanged (-65791) Configuration changed (not used).
+ @constant kDNSMemFree (-65792) Memory can be freed.
+*/
+
+typedef DNSSInt32 DNSStatus;
+enum
+{
+ kDNSNoErr = 0,
+
+ // DNS Services error codes are in the range FFFE FF00 (-65792) to FFFE FFFF (-65537).
+
+ kDNSStartErr = -65537, // 0xFFFE FFFF
+
+ kDNSUnknownErr = -65537,
+ kDNSNoSuchNameErr = -65538,
+ kDNSNoMemoryErr = -65539,
+ kDNSBadParamErr = -65540,
+ kDNSBadReferenceErr = -65541,
+ kDNSBadStateErr = -65542,
+ kDNSBadFlagsErr = -65543,
+ kDNSUnsupportedErr = -65544,
+ kDNSNotInitializedErr = -65545,
+ kDNSNoCacheErr = -65546,
+ kDNSAlreadyRegisteredErr = -65547,
+ kDNSNameConflictErr = -65548,
+ kDNSInvalidErr = -65549,
+ kDNSGrowCache = -65550, // Reserved for mDNSCore
+ kDNSIncompatibleErr = -65551,
+
+ kDNSSizeErr = -65600,
+ kDNSMismatchErr = -65601,
+ kDNSReadErr = -65602,
+ kDNSWriteErr = -65603,
+ kDNSCanceledErr = -65604,
+ kDNSTimeoutErr = -65605,
+ kDNSConnectionErr = -65606,
+ kDNSInUseErr = -65607,
+ kDNSNoResourcesErr = -65608,
+ kDNSEndingErr = -65609,
+
+ kDNSConfigChanged = -65791, // Reserved for mDNSCore
+ kDNSMemFree = -65792, // Reserved for mDNSCore
+
+ kDNSEndErr = -65792 // 0xFFFE FF00
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @enum DNSFlags
+
+ @abstract Flags used control DNS Services.
+
+ @constant kDNSFlagAdvertise
+ Indicates that interfaces should be advertised on the network. Software that only performs searches
+ do not need to set this flag.
+*/
+
+typedef DNSUInt32 DNSFlags;
+enum
+{
+ kDNSFlagAdvertise = ( 1 << 0 )
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @enum DNSPort
+
+ @abstract UDP/TCP port for DNS services.
+
+ @constant kDNSPortInvalid
+ Invalid port.
+
+ @constant kDNSPortUnicastDNS
+ TCP/UDP port for normal unicast DNS (see RFC 1035).
+
+ @constant kDNSPortMulticastDNS
+ TCP/UDP port for Multicast DNS (see <http://www.multicastdns.org/>).
+*/
+
+typedef DNSUInt16 DNSPort;
+enum
+{
+ kDNSPortInvalid = 0,
+ kDNSPortUnicastDNS = 53,
+ kDNSPortMulticastDNS = 5353
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @enum DNSNetworkAddressType
+
+ @abstract Type of address data within a DNSNetworkAddress.
+
+ @constant kDNSNetworkAddressTypeInvalid
+ Invalid type.
+
+ @constant kDNSNetworkAddressTypeIPv4
+ IPv4 address data.
+
+ @constant kDNSNetworkAddressTypeIPv6
+ IPv6 address data.
+*/
+
+typedef DNSUInt32 DNSNetworkAddressType;
+
+#define kDNSNetworkAddressTypeInvalid 0
+#define kDNSNetworkAddressTypeIPv4 4
+#define kDNSNetworkAddressTypeIPv6 6
+#define kDNSNetworkAddressTypeAny 0xFFFFFFFF
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @struct DNSNetworkAddressIPv4
+
+ @field addr
+ 32-bit IPv4 address in network byte order.
+
+ @field port
+ 16-bit port number in network byte order.
+*/
+
+typedef struct DNSNetworkAddressIPv4 DNSNetworkAddressIPv4;
+struct DNSNetworkAddressIPv4
+{
+ DNSOpaque32 addr;
+ DNSOpaque16 port;
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @struct DNSNetworkAddressIPv6
+
+ @field addr
+ 128-bit IPv6 address in network byte order.
+
+ @field port
+ 16-bit port number in network byte order.
+*/
+
+typedef struct DNSNetworkAddressIPv6 DNSNetworkAddressIPv6;
+struct DNSNetworkAddressIPv6
+{
+ DNSOpaque128 addr;
+ DNSOpaque16 port;
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @struct DNSNetworkAddress
+
+ @field addressType
+ Type of data contained within the address structure.
+
+ @field ipv4
+ IPv4 address data.
+
+ @field reserved
+ Reserved data (pads structure to allow for future growth). Unused portions must be zero.
+*/
+
+typedef struct DNSNetworkAddress DNSNetworkAddress;
+struct DNSNetworkAddress
+{
+ DNSNetworkAddressType addressType;
+ union
+ {
+ DNSNetworkAddressIPv4 ipv4;
+ DNSNetworkAddressIPv6 ipv6;
+ } u;
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined kDNSLocalDomain
+
+ @abstract Local DNS domain name (local.).
+*/
+
+#define kDNSLocalDomain "local."
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSServicesInitialize
+
+ @abstract Initializes DNS Services. This must be called before DNS Services functions can be used.
+
+ @param inFlags
+ Flags to control DNS Services.
+
+ @param inCacheEntryCount
+ Number of entries in the DNS record cache. Specify 0 to use the default.
+
+ @result Error code indicating failure reason or kDNSNoErr if successful.
+*/
+
+DNSStatus DNSServicesInitialize( DNSFlags inFlags, DNSCount inCacheEntryCount );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSServicesFinalize
+
+ @abstract Finalizes DNS Services. No DNS Services functions may be called after this function is called.
+*/
+
+void DNSServicesFinalize( void );
+
+#if 0
+#pragma mark == Resolving ==
+#endif
+
+//===========================================================================================================================
+// Resolving
+//===========================================================================================================================
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DNSBrowserRef
+
+ @abstract Reference to a DNS browser object.
+
+ @discussion
+
+ A browser object is typically used by a graphical user application in a manner similar to the Macintosh "Chooser"
+ application. The application creates a browser object then starts domain and/or service searches to begin browsing.
+ When domains and/or services are found, added, or removed, the application is notified via a callback routine.
+*/
+
+typedef struct DNSBrowser * DNSBrowserRef;
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DNSResolverRef
+
+ @abstract Reference to a DNS resolver object.
+
+ @discussion
+
+ A resolver object is used to resolve service names to IP addresses.
+*/
+
+typedef struct DNSResolver * DNSResolverRef;
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @enum DNSResolverFlags
+
+ @abstract Flags used to control resolve operations.
+
+ @constant kDNSResolverFlagOneShot
+ Used to indicate the resolver object should be automatically released after the first resolve.
+
+ @constant kDNSResolverFlagOnlyIfUnique
+ Used to indicate the resolver object should only be created if it is unique. This makes it easy for
+ resolver management to be handled automatically. For example, some software needs to keep active
+ resolving operations open constantly to detect things like the IP address changing (e.g. if
+ displaying it to the user), but when a service goes away then comes back, a new resolver object
+ will often be created, leaving two resolvers for the same name.
+
+ @constant kDNSResolverFlagAutoReleaseByName
+ Used to indicate the resolver object should be automatically released when the service name
+ that is associated with it is no longer on the network. When a service is added to the network,
+ a resolver object may be created and kept around to detect things like IP address changes. When
+ the service goes off the network, this option causes the resolver associated with that service
+ name to be automatically released.
+*/
+
+typedef DNSUInt32 DNSResolverFlags;
+enum
+{
+ kDNSResolverFlagOneShot = ( 1 << 0 ),
+ kDNSResolverFlagOnlyIfUnique = ( 1 << 1 ),
+ kDNSResolverFlagAutoReleaseByName = ( 1 << 2 )
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @enum DNSResolverEventType
+
+ @abstract Type of resolver event being delivered.
+
+ @constant kDNSResolverEventTypeInvalid
+ Invalid event type. Here for completeness.
+
+ @constant kDNSResolverEventTypeRelease
+ Object is being released. No additional data is associated with this event.
+
+ @constant kDNSResolverEventTypeResolved
+ Name resolved.
+*/
+
+typedef long DNSResolverEventType;
+enum
+{
+ kDNSResolverEventTypeInvalid = 0,
+ kDNSResolverEventTypeRelease = 1,
+ kDNSResolverEventTypeResolved = 10
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @struct DNSResolverEventResolveData
+
+ @abstract Data structure passed to callback routine when a resolve-related event occurs.
+
+ @field name
+ Ptr to UTF-8 string containing the resolved name of the service.
+
+ @field type
+ Ptr to UTF-8 string containing the resolved type of the service.
+
+ @field domain
+ Ptr to UTF-8 string containing the resolved domain of the service.
+
+ @field interfaceID
+ Network interface that received the event.
+
+ @field interfaceName
+ Network interface that received the event. May be empty if interface is no longer available.
+
+ @field interfaceIP
+ IP of network interface that received the event. May be invalid if interface is no longer available.
+
+ @field address
+ Network address of the service. Used to communicate with the service.
+
+ @field textRecord
+ Ptr to UTF-8 string containing any additional text information supplied by the service provider.
+
+ @field flags
+ Flags used to augment the event data.
+
+ @field textRecordRaw
+ Ptr to raw TXT record data. May be needed if a custom TXT record format is used.
+
+ @field textRecordRawSize
+ Number of bytes in raw TXT record. May be needed if a custom TXT record format is used.
+*/
+
+typedef struct DNSResolverEventResolveData DNSResolverEventResolveData;
+struct DNSResolverEventResolveData
+{
+ const char * name;
+ const char * type;
+ const char * domain;
+ void * interfaceID;
+ const char * interfaceName;
+ DNSNetworkAddress interfaceIP;
+ DNSNetworkAddress address;
+ const char * textRecord;
+ DNSResolverFlags flags;
+ const void * textRecordRaw;
+ DNSCount textRecordRawSize;
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @struct DNSResolverEvent
+
+ @abstract Data structure passed to callback routines when a resolver event occurs.
+
+ @field type
+ Type of event. The type determines which portion of the data union to use. Types and data union
+ fields are named such as the data union field is the same as the event type. For example, a
+ "resolved" event type (kDNSResolverEventTypeResolved) would refer to data union field "resolved".
+
+ @field resolved
+ Data associated with kDNSResolverEventTypeResolved event.
+*/
+
+typedef struct DNSResolverEvent DNSResolverEvent;
+struct DNSResolverEvent
+{
+ DNSResolverEventType type;
+
+ union
+ {
+ DNSResolverEventResolveData resolved;
+
+ } data;
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSResolverCallBack
+
+ @abstract CallBack routine used to indicate a resolver event.
+
+ @param inContext
+ User-supplied context for callback (specified when browser is created).
+
+ @param inRef
+ Reference to resolver object generating the event.
+
+ @param inStatusCode
+ Status of the event.
+
+ @param inEvent
+ Data associated with the event.
+*/
+
+typedef void
+ ( *DNSResolverCallBack )(
+ void * inContext,
+ DNSResolverRef inRef,
+ DNSStatus inStatusCode,
+ const DNSResolverEvent * inEvent );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSResolverCreate
+
+ @abstract Creates a resolver object and start resolving a service name.
+
+ @param inFlags
+ Flags to control the resolving process.
+
+ @param inName
+ Ptr to UTF-8 string containing the service name to resolve (e.g. "My Printer").
+
+ @param inType
+ Ptr to UTF-8 string containing the service type of the service to resolve (e.g. "_printer._tcp").
+
+ @param inDomain
+ Ptr to UTF-8 string containing the domain of the service to resolve (e.g. "apple.com"). Use NULL
+ to indicate the local domain.
+
+ @param inCallBack
+ CallBack routine to call when a resolver event occurs.
+
+ @param inCallBackContext
+ Context pointer to pass to CallBack routine when an event occurs. Not inspected by DNS Services.
+
+ @param inOwner
+ Reference to browser object related to this resolver. If a browser object is specified and is
+ later released, this resolver object will automatically be released too. May be null.
+
+ @param outRef
+ Ptr to receive reference to resolver object. If the kDNSResolverFlagOnlyIfUnique flag is specified
+ and there is already a resolver for the name, a NULL reference is returned in this parameter to let
+ the caller know that no resolver was created. May be null.
+
+ @result Error code indicating failure reason or kDNSNoErr if successful.
+*/
+
+DNSStatus
+ DNSResolverCreate(
+ DNSResolverFlags inFlags,
+ const char * inName,
+ const char * inType,
+ const char * inDomain,
+ DNSResolverCallBack inCallBack,
+ void * inCallBackContext,
+ DNSBrowserRef inOwner,
+ DNSResolverRef * outRef );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSResolverRelease
+
+ @abstract Releases a resolver object.
+
+ @param inRef
+ Reference to the resolver object to release.
+
+ @param inFlags
+ Flags to control the release process.
+
+ @result Error code indicating failure reason or kDNSNoErr if successful.
+*/
+
+DNSStatus DNSResolverRelease( DNSResolverRef inRef, DNSResolverFlags inFlags );
+
+#if 0
+#pragma mark == Browsing ==
+#endif
+
+//===========================================================================================================================
+// Browsing
+//===========================================================================================================================
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @enum DNSBrowserFlags
+
+ @abstract Flags used to control browser operations.
+
+ @constant kDNSBrowserFlagRegistrationDomainsOnly
+ Used to indicate the client is browsing only for domains to publish services. When the client wishes
+ to publish a service, a domain browse operation would be started, with this flag specified, to find
+ the domain used to register the service. Only valid when passed to DNSBrowserStartDomainSearch.
+
+ @constant kDNSBrowserFlagAutoResolve
+ Used to indicate discovered names should be automatically resolved. This eliminates the need to
+ manually create a resolver to get the IP address and other information. Only valid when passed to
+ DNSBrowserStartServiceSearch. When this option is used, it is important to avoid manually resolving
+ names because this option causes DNS Services to automatically resolve and multiple resolvers for
+ the same name will lead to unnecessary network bandwidth usage. It is also important to note that
+ the notification behavior of the browser is otherwise not affected by this option so browser callback
+ will still receive the same add/remove domain/service events it normally would.
+*/
+
+typedef DNSUInt32 DNSBrowserFlags;
+enum
+{
+ kDNSBrowserFlagRegistrationDomainsOnly = ( 1 << 0 ),
+ kDNSBrowserFlagAutoResolve = ( 1 << 1 )
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @enum DNSBrowserEventType
+
+ @abstract Type of browser event being delivered.
+
+ @constant kDNSBrowserEventTypeInvalid
+ Invalid event type. Here for completeness.
+
+ @constant kDNSBrowserEventTypeRelease
+ Object is being released. No additional data is associated with this event.
+
+ @constant kDNSBrowserEventTypeAddDomain
+ Domain added/found.
+
+ @constant kDNSBrowserEventTypeAddDefaultDomain
+ Default domain added/found. This domain should be selected as the default.
+
+ @constant kDNSBrowserEventTypeRemoveDomain
+ Domain removed.
+
+ @constant kDNSBrowserEventTypeAddService
+ Service added/found.
+
+ @constant kDNSBrowserEventTypeRemoveService
+ Service removed.
+
+ @constant kDNSBrowserEventTypeResolved
+ Name resolved. This is only delivered if the kDNSBrowserFlagAutoResolve option is used with
+ DNSBrowserStartServiceSearch.
+*/
+
+typedef long DNSBrowserEventType;
+enum
+{
+ kDNSBrowserEventTypeInvalid = 0,
+ kDNSBrowserEventTypeRelease = 1,
+ kDNSBrowserEventTypeAddDomain = 10,
+ kDNSBrowserEventTypeAddDefaultDomain = 11,
+ kDNSBrowserEventTypeRemoveDomain = 12,
+ kDNSBrowserEventTypeAddService = 20,
+ kDNSBrowserEventTypeRemoveService = 21,
+ kDNSBrowserEventTypeResolved = 30
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @struct DNSBrowserEventDomainData
+
+ @abstract Data structure referenced by callback routines when a domain-related event occurs.
+
+ @field interfaceID
+ Network interface that received the event.
+
+ @field interfaceName
+ Network interface that received the event. May be empty if interface is no longer available.
+
+ @field interfaceIP
+ IP of network interface that received the event. May be invalid if interface is no longer available.
+
+ @field domain
+ Ptr to UTF-8 string containing the domain name. NULL if no domain name is available or applicable.
+
+ @field flags
+ Flags used to augment the event data.
+*/
+
+typedef struct DNSBrowserEventDomainData DNSBrowserEventDomainData;
+struct DNSBrowserEventDomainData
+{
+ void * interfaceID;
+ const char * interfaceName;
+ DNSNetworkAddress interfaceIP;
+ const char * domain;
+ DNSBrowserFlags flags;
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @struct DNSBrowserEventServiceData
+
+ @abstract Data structure passed to callback routines when a service-related event occurs.
+
+ @field interfaceID
+ Network interface that received the event.
+
+ @field interfaceName
+ Network interface that received the event. May be empty if interface is no longer available.
+
+ @field interfaceIP
+ IP of network interface that received the event. May be invalid if interface is no longer available.
+
+ @field name
+ Ptr to UTF-8 string containing the service name. NULL if no service name is available or applicable.
+
+ @field type
+ Ptr to UTF-8 string containing the service type. NULL if no service type is available or applicable.
+
+ @field domain
+ Ptr to UTF-8 string containing the domain name. NULL if no domain name is available or applicable.
+
+ @field flags
+ Flags used to augment the event data.
+*/
+
+typedef struct DNSBrowserEventServiceData DNSBrowserEventServiceData;
+struct DNSBrowserEventServiceData
+{
+ void * interfaceID;
+ const char * interfaceName;
+ DNSNetworkAddress interfaceIP;
+ const char * name;
+ const char * type;
+ const char * domain;
+ DNSBrowserFlags flags;
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @struct DNSBrowserEvent
+
+ @abstract Data structure passed to callback routines when a browser event occurs.
+
+ @field type
+ Type of event. The type determines which portion of the data union to use. Types and data union
+ fields are named such as the data union field is the same as the event type. For example, an
+ "add domain" event type (kDNSBrowserEventTypeAddDomain) would refer to data union field "addDomain".
+
+ @field addDomain
+ Data associated with kDNSBrowserEventTypeAddDomain event.
+
+ @field addDefaultDomain
+ Data associated with kDNSBrowserEventTypeAddDefaultDomain event.
+
+ @field removeDomain
+ Data associated with kDNSBrowserEventTypeRemoveDomain event.
+
+ @field addService
+ Data associated with kDNSBrowserEventTypeAddService event.
+
+ @field removeService
+ Data associated with kDNSBrowserEventTypeRemoveService event.
+
+ @field resolved
+ Data associated with kDNSBrowserEventTypeResolved event.
+*/
+
+typedef struct DNSBrowserEvent DNSBrowserEvent;
+struct DNSBrowserEvent
+{
+ DNSBrowserEventType type;
+
+ union
+ {
+ DNSBrowserEventDomainData addDomain;
+ DNSBrowserEventDomainData addDefaultDomain;
+ DNSBrowserEventDomainData removeDomain;
+ DNSBrowserEventServiceData addService;
+ DNSBrowserEventServiceData removeService;
+ const DNSResolverEventResolveData * resolved;
+
+ } data;
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSBrowserCallBack
+
+ @abstract CallBack routine used to indicate a browser event.
+
+ @param inContext
+ User-supplied context for callback (specified when browser is created).
+
+ @param inRef
+ Reference to browser object generating the event.
+
+ @param inStatusCode
+ Status of the event.
+
+ @param inEvent
+ Data associated with the event.
+*/
+
+typedef void
+ ( *DNSBrowserCallBack )(
+ void * inContext,
+ DNSBrowserRef inRef,
+ DNSStatus inStatusCode,
+ const DNSBrowserEvent * inEvent );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSBrowserCreate
+
+ @abstract Creates a browser object.
+
+ @param inFlags
+ Flags to control the creation process.
+
+ @param inCallBack
+ CallBack routine to call when a browser event occurs.
+
+ @param inCallBackContext
+ Context pointer to pass to CallBack routine when an event occurs. Not inspected by DNS Services.
+
+ @param outRef
+ Ptr to receive reference to the created browser object. May be null.
+
+ @result Error code indicating failure reason or kDNSNoErr if successful.
+*/
+
+DNSStatus
+ DNSBrowserCreate(
+ DNSBrowserFlags inFlags,
+ DNSBrowserCallBack inCallBack,
+ void * inCallBackContext,
+ DNSBrowserRef * outRef );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSBrowserRelease
+
+ @abstract Releases a browser object.
+
+ @param inRef
+ Reference to the browser object to release.
+
+ @param inFlags
+ Flags to control the release process.
+
+ @result Error code indicating failure reason or kDNSNoErr if successful.
+*/
+
+DNSStatus DNSBrowserRelease( DNSBrowserRef inRef, DNSBrowserFlags inFlags );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSBrowserStartDomainSearch
+
+ @abstract Starts a domain name search.
+
+ @param inRef
+ Reference to browser object to start the search on.
+
+ @param inFlags
+ Flags to control the search process.
+
+ @result Error code indicating failure reason or kDNSNoErr if successful.
+*/
+
+DNSStatus DNSBrowserStartDomainSearch( DNSBrowserRef inRef, DNSBrowserFlags inFlags );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSBrowserStopDomainSearch
+
+ @abstract Stops a domain name search.
+
+ @param inRef
+ Reference to browser object to stop the search on.
+
+ @param inFlags
+ Flags to control the stopping process.
+
+ @result Error code indicating failure reason or kDNSNoErr if successful.
+*/
+
+DNSStatus DNSBrowserStopDomainSearch( DNSBrowserRef inRef, DNSBrowserFlags inFlags );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSBrowserStartServiceSearch
+
+ @abstract Starts a service search.
+
+ @param inRef
+ Reference to browser object to start the search on.
+
+ @param inFlags
+ Flags to control the search process.
+
+ @param inType
+ Ptr to UTF-8 string containing the service type to search for (e.g. "_printer._tcp").
+
+ @param inDomain
+ Ptr to UTF-8 string containing the domain to search in (e.g. "apple.com"). Use NULL to indicate
+ the local domain.
+
+ @result Error code indicating failure reason or kDNSNoErr if successful.
+*/
+
+DNSStatus
+ DNSBrowserStartServiceSearch(
+ DNSBrowserRef inRef,
+ DNSBrowserFlags inFlags,
+ const char * inType,
+ const char * inDomain );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSBrowserStopServiceSearch
+
+ @abstract Stops a service search.
+
+ @param inRef
+ Reference to browser object to stop the search on.
+
+ @param inFlags
+ Flags to control the stopping process.
+
+ @result Error code indicating failure reason or kDNSNoErr if successful.
+*/
+
+DNSStatus DNSBrowserStopServiceSearch( DNSBrowserRef inRef, DNSBrowserFlags inFlags );
+
+#if 0
+#pragma mark == Registration ==
+#endif
+
+//===========================================================================================================================
+// Registration
+//===========================================================================================================================
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DNSRegistrationRef
+
+ @abstract Reference to a DNS registration object.
+*/
+
+typedef struct DNSRegistration * DNSRegistrationRef;
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DNSRegistrationRecordRef
+
+ @abstract Reference to a DNS record object.
+*/
+
+typedef struct DNSRegistrationRecord * DNSRegistrationRecordRef;
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @enum DNSRegistrationFlags
+
+ @abstract Flags used to control registration operations.
+
+ @constant kDNSRegistrationFlagPreFormattedTextRecord
+ Text record is pre-formatted and should be used directly without interpretation.
+
+ @constant kDNSRegistrationFlagAutoRenameOnConflict
+ Automatically uniquely rename and re-register the service when a name conflict occurs.
+*/
+
+typedef DNSUInt32 DNSRegistrationFlags;
+enum
+{
+ kDNSRegistrationFlagPreFormattedTextRecord = ( 1 << 0 ),
+ kDNSRegistrationFlagAutoRenameOnConflict = ( 1 << 1 )
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @enum DNSRecordFlags
+
+ @abstract Flags used to control record operations.
+*/
+
+typedef DNSUInt32 DNSRecordFlags;
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @enum DNSRegistrationEventType
+
+ @abstract Type of registration event being delivered.
+
+ @constant kDNSResolverEventTypeInvalid
+ Invalid event type. Here for completeness.
+
+ @constant kDNSRegistrationEventTypeRelease
+ Object is being released. No additional data is associated with this event.
+
+ @constant kDNSRegistrationEventTypeRegistered
+ Name has been successfully registered.
+
+ @constant kDNSRegistrationEventTypeNameCollision
+ Name collision. The registration is no longer valid. A new registration must be created if needed.
+*/
+
+typedef long DNSRegistrationEventType;
+enum
+{
+ kDNSRegistrationEventTypeInvalid = 0,
+ kDNSRegistrationEventTypeRelease = 1,
+ kDNSRegistrationEventTypeRegistered = 10,
+ kDNSRegistrationEventTypeNameCollision = 11
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @struct DNSRegistrationEvent
+
+ @abstract Data structure passed to callback routines when a registration event occurs.
+
+ @field type
+ Type of event. The type determines which portion of the data union to use. Types and data union
+ fields are named such as the data union field is the same as the event type.
+
+ @field reserved
+ Reserved for future use.
+*/
+
+typedef struct DNSRegistrationEvent DNSRegistrationEvent;
+struct DNSRegistrationEvent
+{
+ DNSRegistrationEventType type;
+
+ union
+ {
+ DNSUInt32 reserved;
+
+ } data;
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSRegistrationCallBack
+
+ @abstract CallBack routine used to indicate a registration event.
+
+ @param inContext
+ User-supplied context for callback (specified when registration is created).
+
+ @param inRef
+ Reference to registration object generating the event.
+
+ @param inStatusCode
+ Status of the event.
+
+ @param inEvent
+ Data associated with the event.
+*/
+
+typedef void
+ ( *DNSRegistrationCallBack )(
+ void * inContext,
+ DNSRegistrationRef inRef,
+ DNSStatus inStatusCode,
+ const DNSRegistrationEvent * inEvent );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSRegistrationCreate
+
+ @abstract Creates a registration object and publish the registration.
+
+ @param inFlags
+ Flags to control the registration process.
+
+ @param inName
+ Ptr to UTF-8 string containing the service name to register (e.g. "My Printer").
+
+ @param inType
+ Ptr to UTF-8 string containing the service type of the service to registration (e.g. "_printer._tcp").
+
+ @param inDomain
+ Ptr to UTF-8 string containing the domain of the service to register (e.g. "apple.com"). Use NULL
+ to indicate the local domain.
+
+ @param inPort
+ TCP/UDP port where the service is being offered (e.g. 80 for an HTTP service).
+
+ @param inTextRecord
+ Ptr to UTF-8 string containing any additional text to provide when the service is resolved.
+
+ @param inTextRecordSize
+ Size to text record.
+
+ @param inHost
+ Name of the host to associate with the registration. Use NULL to use the default host name.
+
+ @field inInterfaceName
+ Name of an interface to restrict service registration to. Use NULL to register service on all interfaces.
+
+ @param inCallBack
+ CallBack routine to call when a registration event occurs.
+
+ @param inCallBackContext
+ Context pointer to pass to CallBack routine when an event occurs. Not inspected by DNS Services.
+
+ @param outRef
+ Ptr to receive reference to registration object. May be null.
+
+ @result Error code indicating failure reason or kDNSNoErr if successful.
+*/
+
+DNSStatus
+ DNSRegistrationCreate(
+ DNSRegistrationFlags inFlags,
+ const char * inName,
+ const char * inType,
+ const char * inDomain,
+ DNSPort inPort,
+ const void * inTextRecord,
+ DNSCount inTextRecordSize,
+ const char * inHost,
+ const char * inInterfaceName,
+ DNSRegistrationCallBack inCallBack,
+ void * inCallBackContext,
+ DNSRegistrationRef * outRef );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSNoSuchServiceRegistrationCreate
+
+ @abstract Creates a registration object and publish the registration to assert non-existance of a particular service.
+
+ @param inFlags
+ Flags to control the registration process.
+
+ @param inName
+ Ptr to UTF-8 string containing the service name to register (e.g. "My Printer").
+
+ @param inType
+ Ptr to UTF-8 string containing the service type of the service to registration (e.g. "_printer._tcp").
+
+ @param inDomain
+ Ptr to UTF-8 string containing the domain of the service to register (e.g. "apple.com"). Use NULL
+ to indicate the local domain.
+
+ @field inInterfaceName
+ Name of an interface to restrict service registration to. Use NULL to register service on all interfaces.
+
+ @param inCallBack
+ CallBack routine to call when a registration event occurs.
+
+ @param inCallBackContext
+ Context pointer to pass to CallBack routine when an event occurs. Not inspected by DNS Services.
+
+ @param outRef
+ Ptr to receive reference to registration object. May be null.
+
+ @result Error code indicating failure reason or kDNSNoErr if successful.
+*/
+
+DNSStatus
+ DNSNoSuchServiceRegistrationCreate(
+ DNSRegistrationFlags inFlags,
+ const char * inName,
+ const char * inType,
+ const char * inDomain,
+ const char * inInterfaceName,
+ DNSRegistrationCallBack inCallBack,
+ void * inCallBackContext,
+ DNSRegistrationRef * outRef );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSRegistrationRelease
+
+ @abstract Releases a registration object.
+
+ @param inRef
+ Reference to the registration object to release.
+
+ @param inFlags
+ Flags to control the release process.
+
+ @result Error code indicating failure reason or kDNSNoErr if successful.
+*/
+
+DNSStatus DNSRegistrationRelease( DNSRegistrationRef inRef, DNSRegistrationFlags inFlags );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSRegistrationUpdate
+
+ @abstract Updates an individual record for a registration.
+
+ @param inRef
+ Reference to the registration object to update.
+
+ @param inRecord
+ Record to update. Use NULL for the standard TXT record.
+
+ @param inData
+ New record data.
+
+ @param inSize
+ Size of new record data.
+
+ @param inNewTTL
+ New time-to-live (TTL) in seconds for the updated data (e.g. 120 for 2 minutes).
+
+ @result Error code indicating failure reason or kDNSNoErr if successful.
+*/
+
+DNSStatus
+ DNSRegistrationUpdate(
+ DNSRegistrationRef inRef,
+ DNSRecordFlags inFlags,
+ DNSRegistrationRecordRef inRecord,
+ const void * inData,
+ DNSCount inSize,
+ DNSUInt32 inNewTTL );
+
+#if 0
+#pragma mark == Domain Registration ==
+#endif
+
+//===========================================================================================================================
+// Domain Registration
+//===========================================================================================================================
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DNSDomainRegistrationRef
+
+ @abstract Reference to a DNS registration object.
+*/
+
+typedef struct DNSDomainRegistration * DNSDomainRegistrationRef;
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @enum DNSDomainRegistrationFlags
+
+ @abstract Flags used to control registration operations.
+*/
+
+typedef DNSUInt32 DNSDomainRegistrationFlags;
+enum
+{
+ kDNSDomainRegistrationFlagNone = 0
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @enum DNSDomainRegistrationType
+
+ @abstract Type of domain registration.
+
+ @constant kDNSDomainRegistrationTypeBrowse
+ Registration for domain browsing.
+
+ @constant kDNSDomainRegistrationTypeBrowseDefault
+ Registration for the domain browsing domain.
+
+ @constant kDNSDomainRegistrationTypeRegistration
+ Registration for domain registration.
+
+ @constant kDNSDomainRegistrationTypeRegistrationDefault
+ Registration for the domain registration domain.
+*/
+
+typedef DNSUInt32 DNSDomainRegistrationType;
+enum
+{
+ kDNSDomainRegistrationTypeBrowse = 0,
+ kDNSDomainRegistrationTypeBrowseDefault = 1,
+ kDNSDomainRegistrationTypeRegistration = 2,
+ kDNSDomainRegistrationTypeRegistrationDefault = 3,
+
+ kDNSDomainRegistrationTypeMax = 4
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSDomainRegistrationCreate
+
+ @abstract Creates a domain registration object and publish the domain.
+
+ @param inFlags
+ Flags to control the registration process.
+
+ @param inName
+ Ptr to string containing the domain name to register (e.g. "apple.com").
+
+ @param inType
+ Type of domain registration.
+
+ @param outRef
+ Ptr to receive reference to domain registration object. May be null.
+
+ @result Error code indicating failure reason or kDNSNoErr if successful.
+*/
+
+DNSStatus
+ DNSDomainRegistrationCreate(
+ DNSDomainRegistrationFlags inFlags,
+ const char * inName,
+ DNSDomainRegistrationType inType,
+ DNSDomainRegistrationRef * outRef );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSDomainRegistrationRelease
+
+ @abstract Releases a domain registration object.
+
+ @param inRef
+ Reference to the domain registration object to release.
+
+ @param inFlags
+ Flags to control the release process.
+
+ @result Error code indicating failure reason or kDNSNoErr if successful.
+*/
+
+DNSStatus DNSDomainRegistrationRelease( DNSDomainRegistrationRef inRef, DNSDomainRegistrationFlags inFlags );
+
+#if 0
+#pragma mark == Host Registration ==
+#endif
+
+//===========================================================================================================================
+// Host Registration
+//===========================================================================================================================
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef DNSHostRegistrationRef
+
+ @abstract Reference to a DNS host registration object.
+*/
+
+typedef struct DNSHostRegistration * DNSHostRegistrationRef;
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @enum DNSHostRegistrationFlags
+
+ @abstract Flags used to control registration operations.
+
+ @constant kDNSHostRegistrationFlagOnlyIfNotFound
+ Only creates the object and registers the host if it was not already found in the list.
+
+ @constant kDNSHostRegistrationFlagAutoRenameOnConflict
+ Automatically uniquely rename and re-register the host when a name conflict occurs.
+
+*/
+
+typedef DNSUInt32 DNSHostRegistrationFlags;
+enum
+{
+ kDNSHostRegistrationFlagNone = 0,
+ kDNSHostRegistrationFlagOnlyIfNotFound = ( 1 << 0 ),
+ kDNSHostRegistrationFlagAutoRenameOnConflict = ( 1 << 1 )
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSHostRegistrationCallBack
+
+ @abstract CallBack routine used to indicate a host registration event.
+
+ @param inContext
+ User-supplied context for callback (specified when browser is created).
+
+ @param inRef
+ Reference to resolver object generating the event.
+
+ @param inStatusCode
+ Status of the event.
+
+ @param inData
+ Data associated with the event.
+*/
+
+typedef void
+ ( *DNSHostRegistrationCallBack )(
+ void * inContext,
+ DNSHostRegistrationRef inRef,
+ DNSStatus inStatusCode,
+ void * inData );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSHostRegistrationCreate
+
+ @abstract Creates a host registration object and publishes the host.
+
+ @param inFlags
+ Flags to control the registration process.
+
+ @param inName
+ Name of the host to register (e.g. "My Web Server").
+
+ @param inDomain
+ Domain of the host to register (e.g. "apple.com"). Use NULL to indicate the local domain.
+
+ @param inAddr
+ IP address of host to register.
+
+ @field inInterfaceName
+ Name of an interface to restrict registration to. Use NULL to register on all interfaces.
+
+ @param inCallBack
+ CallBack routine to call when an event occurs.
+
+ @param inCallBackContext
+ Context pointer to pass to callback routine when an event occurs. Not inspected by DNS Services.
+
+ @param outRef
+ Ptr to receive reference to host registration object. May be null.
+
+ @result Error code indicating failure reason or kDNSNoErr if successful.
+*/
+
+DNSStatus
+ DNSHostRegistrationCreate(
+ DNSHostRegistrationFlags inFlags,
+ const char * inName,
+ const char * inDomain,
+ const DNSNetworkAddress * inAddr,
+ const char * inInterfaceName,
+ DNSHostRegistrationCallBack inCallBack,
+ void * inCallBackContext,
+ DNSHostRegistrationRef * outRef );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSHostRegistrationRelease
+
+ @abstract Releases a host registration object.
+
+ @param inRef
+ Reference to the host registration object to release.
+
+ @param inFlags
+ Flags to control the release process.
+
+ @result Error code indicating failure reason or kDNSNoErr if successful.
+*/
+
+DNSStatus DNSHostRegistrationRelease( DNSHostRegistrationRef inRef, DNSHostRegistrationFlags inFlags );
+
+#if 0
+#pragma mark == Utilities ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined kDNSTextRecordNoValue
+
+ @abstract Value to use when no value is desired for a name/value pair (e.g. "color" instead of "color=").
+*/
+
+#define kDNSTextRecordNoValue ( (const void *) -1 )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined kDNSTextRecordStringNoValue
+
+ @abstract Value to use when no value is desired for a name/value pair (e.g. "color" instead of "color=").
+*/
+
+#define kDNSTextRecordStringNoValue ( (const char *) -1 )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @defined kDNSTextRecordNoValue
+
+ @abstract Size value to use when no value is desired for a name/value pair (e.g. "color" instead of "color=").
+*/
+
+#define kDNSTextRecordNoSize ( (size_t) -1 )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSDynamicTextRecordBuildEscaped
+
+ @abstract Builds a TXT record from a string with \001 escape sequences to separate strings within the TXT record.
+
+ @param inFormat C-string TXT record with \001 escape sequences as record separators.
+ @param outTextRecord Receives a ptr to a built TXT record. Must free with DNSDynamicTextRecordRelease.
+ @param outSize Receive actual size of the built TXT record. Use NULL if you don't need the size.
+
+ @result Error code indicating failure reason or kDNSNoErr if successful.
+
+ @discussion
+
+ A DNS TXT record consists of a packed array of length-prefixed strings with each string being up to 255 characters.
+ To allow this to be described with a null-terminated C-string, a special escape sequence of \001 is used to separate
+ individual character strings within the C-string.
+
+ For example, to represent the following 3 segments "test1=1", "test2=2", and "test3=3", you would use the following:
+
+ DNSUInt8 * txt;
+ size_t size;
+
+ txt = NULL;
+
+ err = DNSDynamicTextRecordBuildEscaped( "test1=1\001test2=2\001test3=3", &txt, &size );
+ require_noerr( err, exit );
+
+ ... use text record
+
+exit:
+ DNSDynamicTextRecordRelease( txt );
+*/
+
+DNSStatus DNSDynamicTextRecordBuildEscaped( const char *inFormat, void *outTextRecord, size_t *outSize );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSDynamicTextRecordAppendCString
+
+ @abstract Appends a name/value pair with the value being a C-string to a dynamic DNS TXT record data section.
+
+ @param ioTxt Input: Ptr to a ptr to TXT record to append to.
+ Output: Receives newly allocated ptr to the new TXT record.
+ Note: Use a ptr to NULL the first time this is called.
+
+ @param ioTxtSize Input: Ptr to size of existing TXT record.
+ Output: Receives new size of TXT record.
+
+ @param inName C-string name in the name/value pair (e.g. "path" for HTTP).
+
+ @param inValue C-string value in the name/value pair (e.g. "/index.html for HTTP).
+
+ @result Error code indicating failure reason or kDNSNoErr if successful.
+
+ @discussion
+
+ This can be used to easily build dynamically-resized TXT records containing multiple name/value pairs of C-strings.
+ For example, the following adds "name=Ryknow", "age=30", and "job=Musician":
+
+ DNSUInt8 * txt;
+ size_t size;
+
+ txt = NULL;
+ size = 0;
+
+ err = DNSDynamicTextRecordAppendCString( &txt, &size, "name", "Ryknow" );
+ require_noerr( err, exit );
+
+ err = DNSDynamicTextRecordAppendCString( &txt, &size, "age", "30" );
+ require_noerr( err, exit );
+
+ err = DNSDynamicTextRecordAppendCString( &txt, &size, "job", "Musician" );
+ require_noerr( err, exit );
+
+ ... use text record
+
+exit:
+ DNSDynamicTextRecordRelease( txt );
+*/
+
+DNSStatus DNSDynamicTextRecordAppendCString( void *ioTxt, size_t *ioTxtSize, const char *inName, const char *inValue );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSDynamicTextRecordAppendData
+
+ @abstract Appends a name/value pair to a dynamic DNS TXT record data section.
+
+ @param ioTxt Input: Ptr to a ptr to TXT record to append to.
+ Output: Receives newly allocated ptr to the new TXT record.
+ Note: Use a ptr to NULL the first time this is called.
+
+ @param ioTxtSize Input: Ptr to size of existing TXT record.
+ Output: Receives new size of TXT record.
+
+ @param inName C-string name in the name/value pair (e.g. "path" for HTTP).
+
+ @param inValue Value data to associate with the name. Use kDNSTextRecordNoValue for no value.
+
+ @param inValueSize Size of value data. Use kDNSTextRecordNoSize for no value.
+
+ @result Error code indicating failure reason or kDNSNoErr if successful.
+*/
+
+DNSStatus
+ DNSDynamicTextRecordAppendData(
+ void * ioTxt,
+ size_t * ioTxtSize,
+ const char * inName,
+ const void * inValue,
+ size_t inValueSize );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSDynamicTextRecordRelease
+
+ @abstract Releases a dynamically allocated TXT record.
+
+ @param inTxt Dynamic TXT record to release.
+
+ @discussion
+
+ This API may only be used with TXT records generated with DNSDynamicTextRecordAppendCString and
+ DNSDynamicTextRecordAppendData.
+*/
+
+void DNSDynamicTextRecordRelease( void *inTxt );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSTextRecordAppendCString
+
+ @abstract Appends a name/value pair with the value being a C-string to DNS TXT record data section.
+
+ @param inTxt TXT record to append to.
+ @param inTxtSize Size of existing TXT record.
+ @param inTxtMaxSize Maximum size of TXT record (i.e. size of buffer).
+ @param inName C-string name in the name/value pair (e.g. "path" for HTTP).
+ @param inValue C-string value in the name/value pair (e.g. "/index.html for HTTP).
+ @param outTxtSize Receives resulting size of TXT record. Pass NULL if not needed.
+
+ @result Error code indicating failure reason or kDNSNoErr if successful.
+
+ @discussion
+
+ This can be used to easily build TXT records containing multiple name/value pairs of C-strings. For example, the
+ following adds "name=Ryknow", "age=30", and "job=Musician":
+
+ DNSUInt8 txt[ 256 ];
+ size_t size;
+
+ size = 0;
+
+ err = DNSTextRecordAppendCString( txt, size, sizeof( txt ), "name", "Ryknow", &size );
+ require_noerr( err, exit );
+
+ err = DNSTextRecordAppendCString( txt, size, sizeof( txt ), "age", "30", &size );
+ require_noerr( err, exit );
+
+ err = DNSTextRecordAppendCString( txt, size, sizeof( txt ), "job", "Musician", &size );
+ require_noerr( err, exit );
+*/
+
+DNSStatus
+ DNSTextRecordAppendCString(
+ void * inTxt,
+ size_t inTxtSize,
+ size_t inTxtMaxSize,
+ const char * inName,
+ const char * inValue,
+ size_t * outTxtSize );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSTextRecordAppendData
+
+ @abstract Appends a name/value pair to a DNS TXT record data section.
+
+ @param inTxt TXT record to append to.
+ @param inTxtSize Size of existing TXT record.
+ @param inTxtMaxSize Maximum size of TXT record (i.e. size of buffer).
+ @param inName C-string name in the name/value pair (e.g. "path" for HTTP).
+ @param inValue Value data to associate with the name. Use kDNSTextRecordNoValue for no value.
+ @param inValueSize Size of value data. Use kDNSTextRecordNoSize for no value.
+ @param outTxtSize Receives resulting size of TXT record. Pass NULL if not needed.
+
+ @result Error code indicating failure reason or kDNSNoErr if successful.
+*/
+
+DNSStatus
+ DNSTextRecordAppendData(
+ void * inTxt,
+ size_t inTxtSize,
+ size_t inTxtMaxSize,
+ const char * inName,
+ const void * inValue,
+ size_t inValueSize,
+ size_t * outTxtSize );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSTextRecordEscape
+
+ @abstract Converts a raw TXT record into a single, null-terminated string with \001 to delimit records.
+
+ @param inTextRecord Raw TXT record to escape.
+ @param inTextSize Number of bytes in the raw TXT record to escape.
+ @param outEscapedString Receives ptr to escaped, \001-delimited, null-terminated string.
+
+ @result Error code indicating failure reason or kDNSNoErr if successful.
+*/
+
+DNSStatus DNSTextRecordEscape( const void *inTextRecord, size_t inTextSize, char **outEscapedString );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSNameValidate
+
+ @abstract Validates a DNS name for correctness.
+
+ @param inName C-string DNS name to validate.
+
+ @result Error code indicating failure reason or kDNSNoErr if valid.
+*/
+
+DNSStatus DNSNameValidate( const char *inName );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSServiceTypeValidate
+
+ @abstract Validates a service type for correctness.
+
+ @param inServiceType C-string service type to validate.
+
+ @result Error code indicating failure reason or kDNSNoErr if valid.
+*/
+
+DNSStatus DNSServiceTypeValidate( const char *inServiceType );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function DNSTextRecordValidate
+
+ @abstract Validates a text record for correctness and optionally builds the TXT reocrd, and returns the actual size.
+
+ @param inText C-string TXT record to validate. Use \001 escape sequence as record separator.
+ @param inMaxSize Maximum size of the TXT record. Use a large number if a max size is not appropriate.
+ @param outRecord Buffer to receive built TXT record. Use NULL if you don't need a built TXT record.
+ @param outActualSize Ptr to receive actual size of TXT record. Use NULL if you don't need the actual size.
+
+ @result Error code indicating failure reason or kDNSNoErr if valid.
+
+ @discussion
+
+ A DNS TXT record consists of a packed array of length-prefixed strings with each string being up to 255 characters.
+ To allow this to be described with a null-terminated C-string, a special escape sequence of \001 is used to separate
+ individual character strings within the C-string.
+
+ For example, to represent the following 3 segments "test1=1", "test2=2", and "test3=3", you would use the following:
+
+ "test1=1\001test2=2\001test3=3"
+*/
+
+DNSStatus DNSTextRecordValidate( const char *inText, size_t inMaxSize, void *outRecord, size_t *outActualSize );
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif // __DNS_SERVICES__
--- /dev/null
+This directory contains support files for running mDNS on Microsoft Windows
+and Windows CE/PocketPC.
+
+mDNSWin32.c & mDNSWin32.h are the Platform Support files that go below
+mDNS Core. These work on both Windows and Windows CE/PocketPC.
+
+DNSServices is a higher-level API for using mDNS. It manages memory, tracks
+browers and registrations, etc.
+
+DNSServiceDiscovery is an emulation layer that sits on top of DNSServices
+and provides the Mac OS X DNS Service Discovery API's on any platform.
+
+Tool.c is an example client that uses the services of mDNS Core.
+
+ToolWin32.mcp is a CodeWarrior project (CodeWarrior for Windows version 8).
+ToolWin32.vcproj is a Visual Studio .NET 7 project. These projects builds
+Tool.c to make rendezvous.exe, a small Windows command-line tool to do all
+the standard Rendezvous stuff on Windows. It has the following features:
+
+- Browse for browsing and/or registration domains.
+- Browse for services.
+- Lookup Service Instances.
+- Register domains for browsing and/or registration.
+- Register services.
+
+For example, if you have a Windows machine running a Web server,
+then you can make it advertise that it is offering HTTP on port 80
+with the following command:
+
+rendezvous -rs "Windows Web Server" "_http._tcp." "local." 80 ""
+
+To search for AFP servers, use this:
+
+rendezvous -bs "_afpovertcp._tcp." "local."
+
+You can also do multiple things at once (e.g. register a service and
+browse for it so one instance of the app can be used for testing).
+Multiple instances can also be run on the same machine to discover each
+other. There is a -help command to show all the commands, their
+parameters, and some examples of using it.
+
+RendezvousBrowser contains the source code for a graphical browser application
+for Windows CE/PocketPC. The Windows CE/PocketPC version requires Microsoft
+eMbedded C++ 4.0 with SP2 installed and the PocketPC 2003 SDK.
--- /dev/null
+/*
+ * 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: mDNSWin32.c,v $
+Revision 1.22 2003/08/20 06:21:25 bradley
+Updated to latest internal version of the Rendezvous for Windows platform plugin: Added support
+for Windows CE/PocketPC 2003; re-did interface-related code to emulate getifaddrs/freeifaddrs for
+restricting usage to only active, multicast-capable, and non-point-to-point interfaces and to ease
+the addition of IPv6 support in the future; Changed init code to serialize thread initialization to
+enable ThreadID improvement to wakeup notification; Define platform support structure locally to
+allow portable mDNS_Init usage; Removed dependence on modified mDNSCore: define interface ID<->name
+structures/prototypes locally; Changed to use _beginthreadex()/_endthreadex() on non-Windows CE
+platforms (re-mapped to CreateThread on Window CE) to avoid a leak in the Microsoft C runtime;
+Added IPv4/IPv6 string<->address conversion routines; Cleaned up some code and added HeaderDoc.
+
+Revision 1.21 2003/08/18 23:09:57 cheshire
+<rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformTimeNow()
+
+Revision 1.20 2003/08/12 19:56:27 cheshire
+Update to APSL 2.0
+
+Revision 1.19 2003/08/05 23:58:18 cheshire
+Update code to compile with the new mDNSCoreReceive() function that requires a TTL
+Right now this platform layer just reports 255 instead of returning the real value -- we should fix this
+
+Revision 1.18 2003/07/23 21:16:30 cheshire
+Removed a couple of debugfs
+
+Revision 1.17 2003/07/23 02:23:01 cheshire
+Updated mDNSPlatformUnlock() to work correctly, now that <rdar://problem/3160248>
+"ScheduleNextTask needs to be smarter" has refined the way m->NextScheduledEvent is set
+
+Revision 1.16 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.15 2003/07/02 21:20:04 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.14 2003/05/26 03:21:30 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.13 2003/05/26 03:01:28 cheshire
+<rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
+
+Revision 1.12 2003/05/06 21:06:05 cheshire
+<rdar://problem/3242673> mDNSWindows needs a wakeupEvent object to signal the main thread
+
+Revision 1.11 2003/05/06 00:00:51 cheshire
+<rdar://problem/3248914> Rationalize naming of domainname manipulation functions
+
+Revision 1.10 2003/04/29 00:06:09 cheshire
+<rdar://problem/3242673> mDNSWindows needs a wakeupEvent object to signal the main thread
+
+Revision 1.9 2003/04/26 02:40:01 cheshire
+Add void LogMsg( const char *format, ... )
+
+Revision 1.8 2003/03/22 02:57:44 cheshire
+Updated mDNSWindows to use new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt")
+
+Revision 1.7 2003/03/15 04:40:38 cheshire
+Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID"
+
+Revision 1.6 2003/02/21 01:54:10 cheshire
+Bug #: 3099194 mDNSResponder needs performance improvements
+Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt")
+
+Revision 1.5 2003/02/20 00:59:03 cheshire
+Brought Windows code up to date so it complies with
+Josh Graessley's interface changes for IPv6 support.
+(Actual support for IPv6 on Windows will come later.)
+
+Revision 1.4 2002/09/21 20:44:54 zarzycki
+Added APSL info
+
+Revision 1.3 2002/09/20 05:50:45 bradley
+Multicast DNS platform plugin for Win32
+
+*/
+
+#if( defined( _MSC_VER ) )
+ #pragma warning( disable:4127 ) // Disable "conditional expression is constant" warning for debug macros.
+#endif
+
+#if( !defined( WIN32_LEAN_AND_MEAN ) )
+ #define WIN32_LEAN_AND_MEAN // Needed to avoid redefinitions by Windows interfaces.
+#endif
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <windows.h>
+#include <winsock2.h>
+#include <Ws2tcpip.h>
+
+#if( !defined( _WIN32_WCE ) ) // Windows CE does not have process.h.
+ #include <process.h>
+#endif
+
+#if( DEBUG )
+ #define mDNSlocal
+#endif
+
+#include "mDNSClientAPI.h"
+#include "mDNSPlatformFunctions.h"
+
+#include "mDNSWin32.h"
+
+#if 0
+#pragma mark == Constants ==
+#endif
+
+//===========================================================================================================================
+// Constants
+//===========================================================================================================================
+
+#define DEBUG_NAME "[mDNS] "
+
+#if( !defined( MDNS_DEBUG_SIGNATURE ) )
+ #define MDNS_DEBUG_SIGNATURE "mDNS"
+#endif
+
+#define kMDNSDefaultName "My Computer"
+
+#define kWinSockMajorMin 2
+#define kWinSockMinorMin 2
+
+#define kWaitListCancelEvent ( WAIT_OBJECT_0 + 0 )
+#define kWaitListInterfaceListChangedEvent ( WAIT_OBJECT_0 + 1 )
+#define kWaitListWakeupEvent ( WAIT_OBJECT_0 + 2 )
+#define kWaitListFixedItemCount 3
+
+#if 0
+#pragma mark == Macros - Debug ==
+#endif
+
+//===========================================================================================================================
+// Macros - Debug
+//===========================================================================================================================
+
+#define MDNS_UNUSED( X ) (void)( X )
+
+#define kDebugLevelMask 0x0000FFFFL
+#define kDebugLevelChatty 100L
+#define kDebugLevelVerbose 500L
+#define kDebugLevelTrace 800L
+#define kDebugLevelInfo 1000L
+#define kDebugLevelRareInfo 2000L
+#define kDebugLevelNotice 3000L
+#define kDebugLevelWarning 4000L
+#define kDebugLevelAllowedError 5000L
+#define kDebugLevelAssert 6000L
+#define kDebugLevelRequire 7000L
+#define kDebugLevelError 8000L
+#define kDebugLevelCritical 9000L
+#define kDebugLevelCriticalError kDebugLevelCritical // DEPRECATED
+#define kDebugLevelAlert 10000L
+#define kDebugLevelEmergency 11000L
+#define kDebugLevelTragic 12000L
+#define kDebugLevelAny 0x0000FFFFL
+
+#if( defined( __MWERKS__ ) || defined( __GNUC__ ) )
+ #define __ROUTINE__ __FUNCTION__
+#else
+ // Apple and Symantec compilers don't support the C99/GCC extensions yet.
+
+ #define __ROUTINE__ NULL
+#endif
+
+#if( MDNS_DEBUGMSGS )
+ #define debug_print_assert( ASSERT_STRING, FILENAME, LINE_NUMBER, FUNCTION ) \
+ mDNSPlatformPrintAssert( MDNS_DEBUG_SIGNATURE, 0, ( ASSERT_STRING ), NULL, ( FILENAME ), ( LINE_NUMBER ), ( FUNCTION ) )
+
+ #define debug_print_assert_err( ERR, ASSERT_STRING, ERROR_STRING, FILENAME, LINE_NUMBER, FUNCTION ) \
+ mDNSPlatformPrintAssert( MDNS_DEBUG_SIGNATURE, ( ERR ), ( ASSERT_STRING ), ( ERROR_STRING ), \
+ ( FILENAME ), ( LINE_NUMBER ), ( FUNCTION ) )
+
+ #define dlog mDNSPlatformDebugLog
+#else
+ #define debug_print_assert( ASSERT_STRING, FILENAME, LINE_NUMBER, FUNCTION )
+
+ #define debug_print_assert_err( ERR, ASSERT_STRING, ERROR_STRING, FILENAME, LINE_NUMBER, FUNCTION )
+
+ #define dlog while( 0 )
+#endif
+
+///
+/// The following debugging macros emulate those available on Mac OS in AssertMacros.h/Debugging.h.
+///
+
+// checks
+
+#define check( X ) \
+ do { \
+ if( !( X ) ) { \
+ debug_print_assert( #X, __FILE__, __LINE__, __ROUTINE__ ); \
+ } \
+ } while( 0 )
+
+#define check_noerr( ERR ) \
+ do { \
+ if( ( ERR ) != 0 ) { \
+ debug_print_assert_err( ( ERR ), #ERR, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
+ } \
+ } while( 0 )
+
+#define check_errno( ERR, ERRNO ) \
+ do { \
+ int localErr; \
+ \
+ localErr = (int)( ERR ); \
+ if( localErr < 0 ) { \
+ int localErrno; \
+ \
+ localErrno = ( ERRNO ); \
+ localErr = ( localErrno != 0 ) ? localErrno : localErr; \
+ debug_print_assert_err( localErr, #ERR, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
+ } \
+ } while( 0 )
+
+// requires
+
+#define require( X, LABEL ) \
+ do { \
+ if( !( X ) ) { \
+ debug_print_assert( #X, __FILE__, __LINE__, __ROUTINE__ ); \
+ goto LABEL; \
+ } \
+ } while( 0 )
+
+#define require_quiet( X, LABEL ) \
+ do { \
+ if( !( X ) ) { \
+ goto LABEL; \
+ } \
+ } while( 0 )
+
+#define require_action( X, LABEL, ACTION ) \
+ do { \
+ if( !( X ) ) { \
+ debug_print_assert( #X, __FILE__, __LINE__, __ROUTINE__ ); \
+ { ACTION; } \
+ goto LABEL; \
+ } \
+ } while( 0 )
+
+#define require_action_quiet( X, LABEL, ACTION ) \
+ do { \
+ if( !( X ) ) { \
+ { ACTION; } \
+ goto LABEL; \
+ } \
+ } while( 0 )
+
+#define require_noerr( ERR, LABEL ) \
+ do { \
+ if( ( ERR ) != 0 ) { \
+ debug_print_assert_err( ( ERR ), #ERR, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
+ goto LABEL; \
+ } \
+ } while( 0 )
+
+#define require_noerr_quiet( ERR, LABEL ) \
+ do { \
+ if( ( ERR ) != 0 ) { \
+ goto LABEL; \
+ } \
+ } while( 0 )
+
+#define require_errno( ERR, ERRNO, LABEL ) \
+ do { \
+ int localErr; \
+ \
+ localErr = (int)( ERR ); \
+ if( localErr < 0 ) { \
+ int localErrno; \
+ \
+ localErrno = ( ERRNO ); \
+ localErr = ( localErrno != 0 ) ? localErrno : localErr; \
+ debug_print_assert_err( localErr, #ERR, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
+ goto LABEL; \
+ } \
+ } while( 0 )
+
+#define require_errno_action( ERR, ERRNO, LABEL, ACTION ) \
+ do { \
+ int localErr; \
+ \
+ localErr = (int)( ERR ); \
+ if( localErr < 0 ) { \
+ int localErrno; \
+ \
+ localErrno = ( ERRNO ); \
+ localErr = ( localErrno != 0 ) ? localErrno : localErr; \
+ debug_print_assert_err( localErr, #ERR, NULL, __FILE__, __LINE__, __ROUTINE__ ); \
+ { ACTION; } \
+ goto LABEL; \
+ } \
+ } while( 0 )
+
+#if 0
+#pragma mark == Macros - General ==
+#endif
+
+//===========================================================================================================================
+// Macros - General
+//===========================================================================================================================
+
+#define kInvalidSocketRef INVALID_SOCKET
+#define IsValidSocket( X ) ( ( X ) != INVALID_SOCKET )
+#define close_compat( X ) closesocket( X )
+#define errno_compat() WSAGetLastError()
+
+// _beginthreadex and _endthreadex are not supported on Windows CE 2.1 or later (the C runtime issues with leaking
+// resources have apparently been resolved and they seem to have just ripped out support for the API) so map it to
+// CreateThread on Windows CE.
+
+#if( defined( _WIN32_WCE ) )
+ #define _beginthreadex( SECURITY_PTR, STACK_SIZE, START_ADDRESS, ARG_LIST, FLAGS, THREAD_ID_PTR ) \
+ CreateThread( SECURITY_PTR, STACK_SIZE, (LPTHREAD_START_ROUTINE) START_ADDRESS, ARG_LIST, FLAGS, \
+ (LPDWORD) THREAD_ID_PTR )
+
+ #define _endthreadex( RESULT )
+#endif
+
+#if 0
+#pragma mark == Prototypes ==
+#endif
+
+//===========================================================================================================================
+// Prototypes
+//===========================================================================================================================
+
+#if( MDNS_DEBUGMSGS )
+ mDNSlocal void mDNSPlatformDebugLog( unsigned long inLevel, const char *inFormat, ... );
+ mDNSlocal void
+ mDNSPlatformPrintAssert(
+ const char * inSignature,
+ long inError,
+ const char * inAssertionString,
+ const char * inErrorString,
+ const char * inFileName,
+ unsigned long inLineNumber,
+ const char * inFunction );
+#endif
+
+mDNSlocal mStatus SetupSynchronizationObjects( mDNS * const inMDNS );
+mDNSlocal mStatus TearDownSynchronizationObjects( mDNS * const inMDNS );
+mDNSlocal mStatus SetupName( mDNS * const inMDNS );
+mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS );
+mDNSlocal mStatus TearDownInterfaceList( mDNS * const inMDNS );
+mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct sockaddr_in *inAddress, mDNSInterfaceData **outIFD );
+mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, mDNSInterfaceData *inIFD );
+mDNSlocal mStatus SetupSocket( mDNS * const inMDNS,
+ const struct sockaddr_in * inAddress,
+ mDNSIPPort inPort,
+ SocketRef * outSocketRef );
+mDNSlocal mStatus SetupNotifications( mDNS * const inMDNS );
+mDNSlocal mStatus TearDownNotifications( mDNS * const inMDNS );
+
+mDNSlocal mStatus SetupThread( mDNS * const inMDNS );
+mDNSlocal mStatus TearDownThread( const mDNS * const inMDNS );
+mDNSlocal unsigned WINAPI ProcessingThread( LPVOID inParam );
+mDNSlocal mStatus ProcessingThreadInitialize( mDNS * const inMDNS );
+mDNSlocal mStatus ProcessingThreadSetupWaitList( mDNS * const inMDNS, HANDLE **outWaitList, int *outWaitListCount );
+mDNSlocal void ProcessingThreadProcessPacket( mDNS *inMDNS, mDNSInterfaceData *inIFD, SocketRef inSocketRef );
+mDNSlocal void ProcessingThreadInterfaceListChanged( mDNS *inMDNS );
+
+// Platform Accessors
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+typedef struct mDNSPlatformInterfaceInfo mDNSPlatformInterfaceInfo;
+struct mDNSPlatformInterfaceInfo
+{
+ const char * name;
+ mDNSAddr ip;
+};
+
+mDNSexport mStatus mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID );
+mDNSexport mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo );
+
+#ifdef __cplusplus
+ }
+#endif
+
+#if 0
+#pragma mark == Globals ==
+#endif
+
+//===========================================================================================================================
+// Globals
+//===========================================================================================================================
+
+mDNSlocal mDNS_PlatformSupport gMDNSPlatformSupport;
+mDNSs32 mDNSPlatformOneSecond = 0;
+
+#if( MDNS_DEBUGMSGS )
+ mDNSlocal unsigned long gDebugLevel = kDebugLevelInfo + 1;
+#endif
+
+#if 0
+#pragma mark -
+#pragma mark == Platform Support APIs ==
+#endif
+
+//===========================================================================================================================
+// mDNSPlatformInit
+//===========================================================================================================================
+
+mStatus mDNSPlatformInit( mDNS * const inMDNS )
+{
+ mStatus err;
+ WSADATA wsaData;
+ int supported;
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "platform init\n" );
+
+ // Initialize variables.
+
+ memset( &gMDNSPlatformSupport, 0, sizeof( gMDNSPlatformSupport ) );
+ inMDNS->p = &gMDNSPlatformSupport;
+ inMDNS->p->interfaceListChangedSocketRef = kInvalidSocketRef;
+ mDNSPlatformOneSecond = 1000; // Use milliseconds as the quantum of time.
+
+ // Set everything up.
+
+ err = WSAStartup( MAKEWORD( kWinSockMajorMin, kWinSockMinorMin ), &wsaData );
+ require_noerr( err, exit );
+
+ supported = ( ( LOBYTE( wsaData.wVersion ) == kWinSockMajorMin ) && ( HIBYTE( wsaData.wVersion ) == kWinSockMinorMin ) );
+ require_action( supported, exit, err = mStatus_UnsupportedErr );
+
+ err = SetupSynchronizationObjects( inMDNS );
+ require_noerr( err, exit );
+
+ err = SetupThread( inMDNS );
+ require_noerr( err, exit );
+
+ // Success!
+
+ mDNSCoreInitComplete( inMDNS, err );
+
+exit:
+ if( err )
+ {
+ mDNSPlatformClose( inMDNS );
+ }
+ dlog( kDebugLevelVerbose, DEBUG_NAME "platform init done (err=%ld)\n", err );
+ return( err );
+}
+
+//===========================================================================================================================
+// mDNSPlatformClose
+//===========================================================================================================================
+
+void mDNSPlatformClose( mDNS * const inMDNS )
+{
+ mStatus err;
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "platform close\n" );
+ check( inMDNS );
+
+ // Tear everything down in reverse order to how it was set up.
+
+ err = TearDownThread( inMDNS );
+ check_noerr( err );
+
+ err = TearDownInterfaceList( inMDNS );
+ check_noerr( err );
+
+ err = TearDownSynchronizationObjects( inMDNS );
+ check_noerr( err );
+
+ WSACleanup();
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "platform close done (err=%ld)\n", err );
+}
+
+//===========================================================================================================================
+// mDNSPlatformSendUDP
+//===========================================================================================================================
+
+mStatus
+ mDNSPlatformSendUDP(
+ const mDNS * const inMDNS,
+ const DNSMessage * const inMsg,
+ const mDNSu8 * const inMsgEnd,
+ mDNSInterfaceID inInterfaceID,
+ mDNSIPPort inSrcPort,
+ const mDNSAddr * inDstIP,
+ mDNSIPPort inDstPort )
+{
+ mStatus err;
+ mDNSInterfaceData * ifd;
+ struct sockaddr_in addr;
+ int n;
+
+ MDNS_UNUSED( inSrcPort );
+ dlog( kDebugLevelChatty, DEBUG_NAME "platform send UDP\n" );
+
+ // Check parameters.
+
+ check( inMDNS );
+ check( inMsg );
+ check( inMsgEnd );
+ check( inInterfaceID );
+ check( inDstIP );
+ if( inDstIP->type != mDNSAddrType_IPv4 )
+ {
+ err = mStatus_BadParamErr;
+ goto exit;
+ }
+
+ // Send the packet.
+
+ ifd = (mDNSInterfaceData *) inInterfaceID;
+ check( IsValidSocket( ifd->multicastSocketRef ) );
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = inDstPort.NotAnInteger;
+ addr.sin_addr.s_addr = inDstIP->ip.v4.NotAnInteger;
+
+ n = (int)( inMsgEnd - ( (const mDNSu8 * const) inMsg ) );
+ n = sendto( ifd->multicastSocketRef, (char *) inMsg, n, 0, (struct sockaddr *) &addr, sizeof( addr ) );
+ check_errno( n, errno_compat() );
+
+ ifd->sendErrorCounter += ( n < 0 );
+ ifd->sendMulticastCounter += ( inDstPort.NotAnInteger == MulticastDNSPort.NotAnInteger );
+ ifd->sendUnicastCounter += ( inDstPort.NotAnInteger != MulticastDNSPort.NotAnInteger );
+ err = mStatus_NoError;
+
+exit:
+ dlog( kDebugLevelChatty, DEBUG_NAME "platform send UDP done\n" );
+ return( err );
+}
+
+//===========================================================================================================================
+// mDNSPlatformLock
+//===========================================================================================================================
+
+void mDNSPlatformLock( const mDNS * const inMDNS )
+{
+ EnterCriticalSection( &inMDNS->p->lock );
+}
+
+//===========================================================================================================================
+// mDNSPlatformUnlock
+//===========================================================================================================================
+
+void mDNSPlatformUnlock( const mDNS * const inMDNS )
+{
+ check( inMDNS );
+ check( inMDNS->p );
+ check( inMDNS->p->threadID );
+
+ // When an API routine is called, "m->NextScheduledEvent" is reset to "timenow" before calling mDNSPlatformUnlock()
+ // Since our main mDNS_Execute() loop is on a different thread, we need to wake up that thread to:
+ // (a) handle immediate work (if any) resulting from this API call
+ // (b) calculate the next sleep time between now and the next interesting event
+
+ if( ( mDNSPlatformTimeNow() - inMDNS->NextScheduledEvent ) >= 0 )
+ {
+ // We only need to case a wakeup event when called from a task other than the mDNS task since if we are
+ // called from mDNS task, we'll loop back and call mDNS_Execute. This avoids filling up the command queue.
+
+ if( GetCurrentThreadId() != inMDNS->p->threadID )
+ {
+ BOOL wasSet;
+
+ wasSet = SetEvent( inMDNS->p->wakeupEvent );
+ check( wasSet );
+ }
+ }
+ LeaveCriticalSection( &inMDNS->p->lock );
+}
+
+//===========================================================================================================================
+// mDNSPlatformStrLen
+//===========================================================================================================================
+
+mDNSu32 mDNSPlatformStrLen( const void *inSrc )
+{
+ check( inSrc );
+
+ return( (mDNSu32) strlen( (const char *) inSrc ) );
+}
+
+//===========================================================================================================================
+// mDNSPlatformStrCopy
+//===========================================================================================================================
+
+void mDNSPlatformStrCopy( const void *inSrc, void *inDst )
+{
+ check( inSrc );
+ check( inDst );
+
+ strcpy( (char *) inDst, (const char*) inSrc );
+}
+
+//===========================================================================================================================
+// mDNSPlatformMemCopy
+//===========================================================================================================================
+
+void mDNSPlatformMemCopy( const void *inSrc, void *inDst, mDNSu32 inSize )
+{
+ check( inSrc );
+ check( inDst );
+
+ memcpy( inDst, inSrc, inSize );
+}
+
+//===========================================================================================================================
+// mDNSPlatformMemSame
+//===========================================================================================================================
+
+mDNSBool mDNSPlatformMemSame( const void *inSrc, const void *inDst, mDNSu32 inSize )
+{
+ check( inSrc );
+ check( inDst );
+
+ return( (mDNSBool)( memcmp( inSrc, inDst, inSize ) == 0 ) );
+}
+
+//===========================================================================================================================
+// mDNSPlatformMemZero
+//===========================================================================================================================
+
+void mDNSPlatformMemZero( void *inDst, mDNSu32 inSize )
+{
+ check( inDst );
+
+ memset( inDst, 0, inSize );
+}
+
+//===========================================================================================================================
+// mDNSPlatformMemAllocate
+//===========================================================================================================================
+
+mDNSexport void * mDNSPlatformMemAllocate( mDNSu32 inSize )
+{
+ void * mem;
+
+ check( inSize > 0 );
+
+ mem = malloc( inSize );
+ check( mem );
+
+ return( mem );
+}
+
+//===========================================================================================================================
+// mDNSPlatformMemFree
+//===========================================================================================================================
+
+mDNSexport void mDNSPlatformMemFree( void *inMem )
+{
+ check( inMem );
+
+ free( inMem );
+}
+
+//===========================================================================================================================
+// mDNSPlatformTimeInit
+//===========================================================================================================================
+
+mDNSexport mStatus mDNSPlatformTimeInit( mDNSs32 *outTimeNow )
+{
+ check( outTimeNow );
+
+ // No special setup is required on Windows -- we just use GetTickCount().
+
+ *outTimeNow = mDNSPlatformTimeNow();
+ return( mStatus_NoError );
+}
+
+//===========================================================================================================================
+// mDNSPlatformTimeNow
+//===========================================================================================================================
+
+mDNSs32 mDNSPlatformTimeNow( void )
+{
+ return( (mDNSs32) GetTickCount() );
+}
+
+//===========================================================================================================================
+// mDNSPlatformInterfaceNameToID
+//===========================================================================================================================
+
+mStatus mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID )
+{
+ mStatus err;
+ mDNSInterfaceData * ifd;
+
+ check( inMDNS );
+ check( inMDNS->p );
+ check( inName );
+
+ // Search for an interface with the specified name,
+
+ for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next )
+ {
+ if( strcmp( ifd->name, inName ) == 0 )
+ {
+ break;
+ }
+ }
+ require_action_quiet( ifd, exit, err = mStatus_NoSuchNameErr );
+
+ // Success!
+
+ if( outID )
+ {
+ *outID = (mDNSInterfaceID) ifd;
+ }
+ err = mStatus_NoError;
+
+exit:
+ return( err );
+}
+
+//===========================================================================================================================
+// mDNSPlatformInterfaceIDToInfo
+//===========================================================================================================================
+
+mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo )
+{
+ mStatus err;
+ mDNSInterfaceData * ifd;
+
+ check( inMDNS );
+ check( inID );
+ check( outInfo );
+
+ // Search for an interface with the specified ID,
+
+ for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next )
+ {
+ if( ifd == (mDNSInterfaceData *) inID )
+ {
+ break;
+ }
+ }
+ require_action_quiet( ifd, exit, err = mStatus_NoSuchNameErr );
+
+ // Success!
+
+ outInfo->name = ifd->name;
+ outInfo->ip = ifd->hostSet.ip;
+ err = mStatus_NoError;
+
+exit:
+ return( err );
+}
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+// debugf_
+//===========================================================================================================================
+
+#if( MDNS_DEBUGMSGS )
+void debugf_( const char *inFormat, ... )
+{
+ char buffer[ 512 ];
+ va_list args;
+ mDNSu32 length;
+
+ va_start( args, inFormat );
+ length = mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args );
+ va_end( args );
+
+ dlog( kDebugLevelInfo, "%s\n", buffer );
+}
+#endif
+
+//===========================================================================================================================
+// verbosedebugf_
+//===========================================================================================================================
+
+#if( MDNS_DEBUGMSGS > 1 )
+void verbosedebugf_( const char *inFormat, ... )
+{
+ char buffer[ 512 ];
+ va_list args;
+ mDNSu32 length;
+
+ va_start( args, inFormat );
+ length = mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args );
+ va_end( args );
+
+ dlog( kDebugLevelVerbose, "%s\n", buffer );
+}
+#endif
+
+//===========================================================================================================================
+// LogMsg
+//===========================================================================================================================
+
+void LogMsg( const char *inFormat, ... )
+{
+ char buffer[ 512 ];
+ va_list args;
+ mDNSu32 length;
+
+ va_start( args, inFormat );
+ length = mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args );
+ va_end( args );
+
+ dlog( kDebugLevelWarning, "%s\n", buffer );
+}
+
+#if( MDNS_DEBUGMSGS )
+//===========================================================================================================================
+// mDNSPlatformDebugLog
+//===========================================================================================================================
+
+mDNSlocal void mDNSPlatformDebugLog( unsigned long inLevel, const char *inFormat, ... )
+{
+ if( inLevel >= gDebugLevel )
+ {
+ va_list args;
+
+ va_start( args, inFormat );
+ vfprintf( stderr, inFormat, args );
+ fflush( stderr );
+ va_end( args );
+ }
+}
+
+//===========================================================================================================================
+// mDNSPlatformPrintAssert
+//===========================================================================================================================
+
+mDNSlocal void
+ mDNSPlatformPrintAssert(
+ const char * inSignature,
+ long inError,
+ const char * inAssertionString,
+ const char * inErrorString,
+ const char * inFileName,
+ unsigned long inLineNumber,
+ const char * inFunction )
+{
+ char * dataPtr;
+ char buffer[ 512 ];
+ char tempSignatureChar;
+
+ if( !inSignature )
+ {
+ tempSignatureChar = '\0';
+ inSignature = &tempSignatureChar;
+ }
+ dataPtr = buffer;
+ dataPtr += sprintf( dataPtr, "\n" );
+ if( inError != 0 )
+ {
+ dataPtr += sprintf( dataPtr, "[%s] Error: %ld\n", inSignature, inError );
+ }
+ else
+ {
+ dataPtr += sprintf( dataPtr, "[%s] Assertion failed", inSignature );
+ if( inAssertionString )
+ {
+ dataPtr += sprintf( dataPtr, ": %s", inAssertionString );
+ }
+ dataPtr += sprintf( dataPtr, "\n" );
+ }
+ if( inErrorString )
+ {
+ dataPtr += sprintf( dataPtr, "[%s] %s\n", inSignature, inErrorString );
+ }
+ if( inFileName )
+ {
+ dataPtr += sprintf( dataPtr, "[%s] file: \"%s\"\n", inSignature, inFileName );
+ }
+ if( inLineNumber )
+ {
+ dataPtr += sprintf( dataPtr, "[%s] line: %ld\n", inSignature, inLineNumber );
+ }
+ if( inFunction )
+ {
+ dataPtr += sprintf( dataPtr, "[%s] function: \"%s\"\n", inSignature, inFunction );
+ }
+ dataPtr += sprintf( dataPtr, "\n" );
+ fprintf( stderr, "%s", buffer );
+ fflush( stderr );
+}
+#endif // MDNS_DEBUGMSGS
+
+#if 0
+#pragma mark -
+#pragma mark == Platform Internals ==
+#endif
+
+//===========================================================================================================================
+// SetupSynchronizationObjects
+//===========================================================================================================================
+
+mDNSlocal mStatus SetupSynchronizationObjects( mDNS * const inMDNS )
+{
+ mStatus err;
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "setting up synchronization objects\n" );
+
+ InitializeCriticalSection( &inMDNS->p->lock );
+ inMDNS->p->lockInitialized = mDNStrue;
+
+ inMDNS->p->cancelEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ require_action( inMDNS->p->cancelEvent, exit, err = mStatus_NoMemoryErr );
+
+ inMDNS->p->quitEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ require_action( inMDNS->p->quitEvent, exit, err = mStatus_NoMemoryErr );
+
+ inMDNS->p->interfaceListChangedEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ require_action( inMDNS->p->interfaceListChangedEvent, exit, err = mStatus_NoMemoryErr );
+
+ inMDNS->p->wakeupEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ require_action( inMDNS->p->wakeupEvent, exit, err = mStatus_NoMemoryErr );
+
+ err = mStatus_NoError;
+
+exit:
+ if( err )
+ {
+ TearDownSynchronizationObjects( inMDNS );
+ }
+ dlog( kDebugLevelVerbose, DEBUG_NAME "setting up synchronization objects done (err=%ld)\n", err );
+ return( err );
+}
+
+//===========================================================================================================================
+// TearDownSynchronizationObjects
+//===========================================================================================================================
+
+mDNSlocal mStatus TearDownSynchronizationObjects( mDNS * const inMDNS )
+{
+ mStatus err;
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down synchronization objects\n" );
+
+ if( inMDNS->p->quitEvent )
+ {
+ CloseHandle( inMDNS->p->quitEvent );
+ inMDNS->p->quitEvent = 0;
+ }
+ if( inMDNS->p->cancelEvent )
+ {
+ CloseHandle( inMDNS->p->cancelEvent );
+ inMDNS->p->cancelEvent = 0;
+ }
+ if( inMDNS->p->interfaceListChangedEvent )
+ {
+ CloseHandle( inMDNS->p->interfaceListChangedEvent );
+ inMDNS->p->interfaceListChangedEvent = 0;
+ }
+ if( inMDNS->p->wakeupEvent )
+ {
+ CloseHandle( inMDNS->p->wakeupEvent );
+ inMDNS->p->wakeupEvent = 0;
+ }
+ if( inMDNS->p->lockInitialized )
+ {
+ DeleteCriticalSection( &inMDNS->p->lock );
+ inMDNS->p->lockInitialized = mDNSfalse;
+ }
+ err = mStatus_NoError;
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down synchronization objects done (err=%ld)\n", err );
+ return( err );
+}
+
+//===========================================================================================================================
+// SetupName
+//===========================================================================================================================
+
+mDNSlocal mStatus SetupName( mDNS * const inMDNS )
+{
+ mStatus err;
+ char tempString[ 256 ];
+
+ check( inMDNS );
+
+ // Get the name of this machine.
+
+ tempString[ 0 ] = '\0';
+ err = gethostname( tempString, sizeof( tempString ) - 1 );
+ check_errno( err, errno_compat() );
+ if( err || ( tempString[ 0 ] == '\0' ) )
+ {
+ // Invalidate name so fall back to a default name.
+
+ strcpy( tempString, kMDNSDefaultName );
+ }
+ tempString[ sizeof( tempString ) - 1 ] = '\0';
+
+ // Set up the host name with mDNS.
+
+ inMDNS->nicelabel.c[ 0 ] = (mDNSu8) strlen( tempString );
+ memcpy( &inMDNS->nicelabel.c[ 1 ], tempString, inMDNS->nicelabel.c[ 0 ] );
+ ConvertUTF8PstringToRFC1034HostLabel( inMDNS->nicelabel.c, &inMDNS->hostlabel );
+ if( inMDNS->hostlabel.c[ 0 ] == 0 )
+ {
+ // Nice name has no characters that are representable as an RFC1034 name (e.g. Japanese) so use the default.
+
+ MakeDomainLabelFromLiteralString( &inMDNS->hostlabel, kMDNSDefaultName );
+ }
+ check( inMDNS->nicelabel.c[ 0 ] != 0 );
+ check( inMDNS->hostlabel.c[ 0 ] != 0 );
+
+ mDNS_GenerateFQDN( inMDNS );
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "nice name \"%.*s\"\n", inMDNS->nicelabel.c[ 0 ], &inMDNS->nicelabel.c[ 1 ] );
+ dlog( kDebugLevelInfo, DEBUG_NAME "host name \"%.*s\"\n", inMDNS->hostlabel.c[ 0 ], &inMDNS->hostlabel.c[ 1 ] );
+ return( err );
+}
+
+//===========================================================================================================================
+// SetupInterfaceList
+//===========================================================================================================================
+
+mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS )
+{
+ mStatus err;
+ mDNSInterfaceData ** next;
+ mDNSInterfaceData * ifd;
+ struct ifaddrs * addrs;
+ struct ifaddrs * p;
+ struct ifaddrs * loopback;
+ u_int flagMask;
+ u_int flagTest;
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface list\n" );
+ check( inMDNS );
+ check( inMDNS->p );
+
+ addrs = NULL;
+
+ // Tear down any existing interfaces that may be set up.
+
+ TearDownInterfaceList( inMDNS );
+
+ // Set up the name of this machine.
+
+ err = SetupName( inMDNS );
+ check_noerr( err );
+
+ // Set up the interface list change notification.
+
+ err = SetupNotifications( inMDNS );
+ check_noerr( err );
+
+ // Set up each interface that is active, multicast-capable, and not the loopback interface or point-to-point.
+
+ flagMask = IFF_UP | IFF_MULTICAST | IFF_LOOPBACK | IFF_POINTTOPOINT;
+ flagTest = IFF_UP | IFF_MULTICAST;
+
+ loopback = NULL;
+ next = &inMDNS->p->interfaceList;
+
+ err = getifaddrs( &addrs );
+ require_noerr( err, exit );
+
+ for( p = addrs; p; p = p->ifa_next )
+ {
+ if( ( p->ifa_flags & flagMask ) == flagTest )
+ {
+ if( !p->ifa_addr || ( p->ifa_addr->sa_family != AF_INET ) ) // $$$ TO DO: Update for IPv6.
+ {
+ continue;
+ }
+
+ err = SetupInterface( inMDNS, (struct sockaddr_in *) p->ifa_addr, &ifd );
+ require_noerr( err, exit );
+
+ strcpy( ifd->name, p->ifa_name );
+
+ *next = ifd;
+ next = &ifd->next;
+ ++inMDNS->p->interfaceCount;
+ }
+ }
+
+exit:
+ if( err )
+ {
+ TearDownInterfaceList( inMDNS );
+ }
+ if( addrs )
+ {
+ freeifaddrs( addrs );
+ }
+ dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface list done (err=%ld)\n", err );
+ return( err );
+}
+
+//===========================================================================================================================
+// TearDownInterfaceList
+//===========================================================================================================================
+
+mDNSlocal mStatus TearDownInterfaceList( mDNS * const inMDNS )
+{
+ mStatus err;
+ mDNSInterfaceData * ifd;
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down interface list\n" );
+ check( inMDNS );
+ check( inMDNS->p );
+
+ // Tear down interface list change notifications.
+
+ err = TearDownNotifications( inMDNS );
+ check_noerr( err );
+
+ // Tear down all the interfaces.
+
+ while( inMDNS->p->interfaceList )
+ {
+ ifd = inMDNS->p->interfaceList;
+ inMDNS->p->interfaceList = ifd->next;
+
+ TearDownInterface( inMDNS, ifd );
+ }
+ inMDNS->p->interfaceCount = 0;
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down interface list done\n" );
+ return( mStatus_NoError );
+}
+
+//===========================================================================================================================
+// SetupInterface
+//===========================================================================================================================
+
+mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct sockaddr_in *inAddress, mDNSInterfaceData **outIFD )
+{
+ mStatus err;
+ mDNSInterfaceData * ifd;
+ SocketRef socketRef;
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface\n" );
+ check( inMDNS );
+ check( inMDNS->p );
+ check( inAddress );
+ check( outIFD );
+
+ // Allocate memory for the info item.
+
+ ifd = (mDNSInterfaceData *) calloc( 1, sizeof( *ifd ) );
+ require_action( ifd, exit, err = mStatus_NoMemoryErr );
+ ifd->multicastSocketRef = kInvalidSocketRef;
+ ifd->unicastSocketRef = kInvalidSocketRef;
+
+ ///
+ /// Set up multicast portion of interface.
+ ///
+
+ // Set up the multicast DNS (port 5353) socket for this interface.
+
+ err = SetupSocket( inMDNS, inAddress, MulticastDNSPort, &socketRef );
+ require_noerr( err, exit );
+ ifd->multicastSocketRef = socketRef;
+
+ // Set up the read pending event and associate it so we can block until data is available for this socket.
+
+ ifd->multicastReadPendingEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ require_action( ifd->multicastReadPendingEvent, exit, err = mStatus_NoMemoryErr );
+
+ err = WSAEventSelect( ifd->multicastSocketRef, ifd->multicastReadPendingEvent, FD_READ );
+ require_noerr( err, exit );
+
+ ///
+ /// Set up unicast portion of interface.
+ ///
+
+ // Set up the unicast DNS (port 53) socket for this interface (to handle normal DNS requests).
+
+ err = SetupSocket( inMDNS, inAddress, UnicastDNSPort, &socketRef );
+ require_noerr( err, exit );
+ ifd->unicastSocketRef = socketRef;
+
+ // Set up the read pending event and associate it so we can block until data is available for this socket.
+
+ ifd->unicastReadPendingEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ require_action( ifd->unicastReadPendingEvent, exit, err = mStatus_NoMemoryErr );
+
+ err = WSAEventSelect( ifd->unicastSocketRef, ifd->unicastReadPendingEvent, FD_READ );
+ require_noerr( err, exit );
+
+ // Register this interface with mDNS.
+
+ ifd->hostSet.InterfaceID = (mDNSInterfaceID) ifd;
+ ifd->hostSet.ip.type = mDNSAddrType_IPv4;
+ ifd->hostSet.ip.ip.v4.NotAnInteger = inAddress->sin_addr.s_addr;
+ ifd->hostSet.Advertise = inMDNS->AdvertiseLocalAddresses;
+
+ err = mDNS_RegisterInterface( inMDNS, &ifd->hostSet );
+ require_noerr( err, exit );
+ ifd->hostRegistered = mDNStrue;
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "Registered IP address: %u.%u.%u.%u\n",
+ ifd->hostSet.ip.ip.v4.b[ 0 ],
+ ifd->hostSet.ip.ip.v4.b[ 1 ],
+ ifd->hostSet.ip.ip.v4.b[ 2 ],
+ ifd->hostSet.ip.ip.v4.b[ 3 ] );
+
+ // Success!
+
+ *outIFD = ifd;
+ ifd = NULL;
+
+exit:
+ if( ifd )
+ {
+ TearDownInterface( inMDNS, ifd );
+ }
+ dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface done (err=%ld)\n", err );
+ return( err );
+}
+
+//===========================================================================================================================
+// TearDownInterface
+//===========================================================================================================================
+
+mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, mDNSInterfaceData *inIFD )
+{
+ SocketRef socketRef;
+
+ check( inMDNS );
+ check( inIFD );
+
+ // Deregister this interface with mDNS.
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "Deregistering IP address: %u.%u.%u.%u\n",
+ inIFD->hostSet.ip.ip.v4.b[ 0 ],
+ inIFD->hostSet.ip.ip.v4.b[ 1 ],
+ inIFD->hostSet.ip.ip.v4.b[ 2 ],
+ inIFD->hostSet.ip.ip.v4.b[ 3 ] );
+
+ if( inIFD->hostRegistered )
+ {
+ inIFD->hostRegistered = mDNSfalse;
+ mDNS_DeregisterInterface( inMDNS, &inIFD->hostSet );
+ }
+
+ // Tear down the multicast socket.
+
+ if( inIFD->multicastReadPendingEvent )
+ {
+ CloseHandle( inIFD->multicastReadPendingEvent );
+ inIFD->multicastReadPendingEvent = 0;
+ }
+
+ socketRef = inIFD->multicastSocketRef;
+ inIFD->multicastSocketRef = kInvalidSocketRef;
+ if( IsValidSocket( socketRef ) )
+ {
+ dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down multicast socket %d\n", socketRef );
+ close_compat( socketRef );
+ }
+
+ // Tear down the unicast socket.
+
+ if( inIFD->unicastReadPendingEvent )
+ {
+ CloseHandle( inIFD->unicastReadPendingEvent );
+ inIFD->unicastReadPendingEvent = 0;
+ }
+
+ socketRef = inIFD->unicastSocketRef;
+ inIFD->unicastSocketRef = kInvalidSocketRef;
+ if( IsValidSocket( socketRef ) )
+ {
+ dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down unicast socket %d\n", socketRef );
+ close_compat( socketRef );
+ }
+
+ // Free the memory used by the interface info.
+
+ free( inIFD );
+ return( mStatus_NoError );
+}
+
+//===========================================================================================================================
+// SetupSocket
+//===========================================================================================================================
+
+mDNSlocal mStatus
+ SetupSocket(
+ mDNS * const inMDNS,
+ const struct sockaddr_in * inAddress,
+ mDNSIPPort inPort,
+ SocketRef * outSocketRef )
+{
+ mStatus err;
+ SocketRef socketRef;
+ int option;
+ struct ip_mreq mreq;
+ struct sockaddr_in addr;
+ mDNSv4Addr ip;
+
+ MDNS_UNUSED( inMDNS );
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "setting up socket done\n" );
+ check( inMDNS );
+ check( outSocketRef );
+
+ // Set up a UDP socket.
+
+ socketRef = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
+ require_action( IsValidSocket( socketRef ), exit, err = mStatus_NoMemoryErr );
+
+ // Turn on reuse address option so multiple servers can listen for Multicast DNS packets.
+
+ option = 1;
+ err = setsockopt( socketRef, SOL_SOCKET, SO_REUSEADDR, (char *) &option, sizeof( option ) );
+ check_errno( err, errno_compat() );
+
+ // Bind to the specified port (53 for unicast or 5353 for multicast).
+
+ ip.NotAnInteger = inAddress->sin_addr.s_addr;
+ memset( &addr, 0, sizeof( addr ) );
+ addr.sin_family = AF_INET;
+ addr.sin_port = inPort.NotAnInteger;
+ addr.sin_addr.s_addr = ip.NotAnInteger;
+ err = bind( socketRef, (struct sockaddr *) &addr, sizeof( addr ) );
+ if( err && ( inPort.NotAnInteger == UnicastDNSPort.NotAnInteger ) )
+ {
+ // Some systems prevent code without root permissions from binding to the DNS port so ignore this
+ // error since it is not critical. This should only occur with non-root processes.
+
+ err = 0;
+ }
+ check_errno( err, errno_compat() );
+
+ // Join the all-DNS multicast group so we receive Multicast DNS packets.
+
+ if( inPort.NotAnInteger == MulticastDNSPort.NotAnInteger )
+ {
+ mreq.imr_multiaddr.s_addr = AllDNSLinkGroup.NotAnInteger;
+ mreq.imr_interface.s_addr = ip.NotAnInteger;
+ err = setsockopt( socketRef, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof( mreq ) );
+ check_errno( err, errno_compat() );
+ }
+
+ // Direct multicast packets to the specified interface.
+
+ addr.sin_addr.s_addr = ip.NotAnInteger;
+ err = setsockopt( socketRef, IPPROTO_IP, IP_MULTICAST_IF, (char *) &addr.sin_addr, sizeof( addr.sin_addr ) );
+ check_errno( err, errno_compat() );
+
+ // Set the TTL of outgoing unicast packets to 255 (helps against spoofing).
+
+ option = 255;
+ err = setsockopt( socketRef, IPPROTO_IP, IP_TTL, (char *) &option, sizeof( option ) );
+ check_errno( err, errno_compat() );
+
+ // Set the TTL of outgoing multicast packets to 255 (helps against spoofing).
+
+ option = 255;
+ err = setsockopt( socketRef, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &option, sizeof( option ) );
+ check_errno( err, errno_compat() );
+
+ // Success!
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "setting up socket done (%u.%u.%u.%u:%u, %d)\n",
+ ip.b[ 0 ], ip.b[ 1 ], ip.b[ 2 ], ip.b[ 3 ], ntohs( inPort.NotAnInteger ), socketRef );
+
+ *outSocketRef = socketRef;
+ socketRef = kInvalidSocketRef;
+ err = mStatus_NoError;
+
+exit:
+ if( IsValidSocket( socketRef ) )
+ {
+ close_compat( socketRef );
+ }
+ return( err );
+}
+
+//===========================================================================================================================
+// SetupNotifications
+//===========================================================================================================================
+
+mDNSlocal mStatus SetupNotifications( mDNS * const inMDNS )
+{
+ mStatus err;
+ SocketRef socketRef;
+ unsigned long param;
+ int inBuffer;
+ int outBuffer;
+ DWORD outSize;
+
+ // Register to listen for address list changes.
+
+ socketRef = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
+ require_action( IsValidSocket( socketRef ), exit, err = mStatus_NoMemoryErr );
+ inMDNS->p->interfaceListChangedSocketRef = socketRef;
+
+ // Make the socket non-blocking so the WSAIoctl returns immediately with WSAEWOULDBLOCK. It will set the event
+ // when a change to the interface list is detected.
+
+ param = 1;
+ err = ioctlsocket( socketRef, FIONBIO, ¶m );
+ require_errno( err, errno_compat(), exit );
+
+ inBuffer = 0;
+ outBuffer = 0;
+ err = WSAIoctl( socketRef, SIO_ADDRESS_LIST_CHANGE, &inBuffer, 0, &outBuffer, 0, &outSize, NULL, NULL );
+ if( err < 0 )
+ {
+ check( errno_compat() == WSAEWOULDBLOCK );
+ }
+
+ err = WSAEventSelect( socketRef, inMDNS->p->interfaceListChangedEvent, FD_ADDRESS_LIST_CHANGE );
+ require_errno( err, errno_compat(), exit );
+
+exit:
+ if( err )
+ {
+ TearDownNotifications( inMDNS );
+ }
+ return( err );
+}
+
+//===========================================================================================================================
+// TearDownNotifications
+//===========================================================================================================================
+
+mDNSlocal mStatus TearDownNotifications( mDNS * const inMDNS )
+{
+ SocketRef socketRef;
+
+ socketRef = inMDNS->p->interfaceListChangedSocketRef;
+ inMDNS->p->interfaceListChangedSocketRef = kInvalidSocketRef;
+ if( IsValidSocket( socketRef ) )
+ {
+ close_compat( socketRef );
+ }
+ return( mStatus_NoError );
+}
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+// SetupThread
+//===========================================================================================================================
+
+mDNSlocal mStatus SetupThread( mDNS * const inMDNS )
+{
+ mStatus err;
+ HANDLE threadHandle;
+ unsigned threadID;
+ DWORD result;
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "setting up thread\n" );
+
+ // To avoid a race condition with the thread ID needed by the unlocking code, we need to make sure the
+ // thread has fully initialized. To do this, we create the thread then wait for it to signal it is ready.
+
+ inMDNS->p->initEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ require_action( inMDNS->p->initEvent, exit, err = mStatus_NoMemoryErr );
+ inMDNS->p->initStatus = mStatus_Invalid;
+
+ // Create thread with _beginthreadex() instead of CreateThread() to avoid memory leaks when using static run-time
+ // libraries. See <http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/createthread.asp>.
+
+ threadHandle = (HANDLE) _beginthreadex( NULL, 0, ProcessingThread, inMDNS, 0, &threadID );
+ require_action( threadHandle, exit, err = mStatus_NoMemoryErr );
+
+ result = WaitForSingleObject( inMDNS->p->initEvent, INFINITE );
+ require_action( result == WAIT_OBJECT_0, exit, err = mStatus_UnknownErr );
+ err = inMDNS->p->initStatus;
+ require_noerr( err, exit );
+
+exit:
+ if( inMDNS->p->initEvent )
+ {
+ CloseHandle( inMDNS->p->initEvent );
+ inMDNS->p->initEvent = 0;
+ }
+ dlog( kDebugLevelVerbose, DEBUG_NAME "setting up thread done (err=%ld)\n", err );
+ return( err );
+}
+
+//===========================================================================================================================
+// TearDownThread
+//===========================================================================================================================
+
+mDNSlocal mStatus TearDownThread( const mDNS * const inMDNS )
+{
+ DWORD result;
+
+ // Signal the cancel event to cause the thread to exit. Then wait for the quit event to be signal indicating it did
+ // exit. If the quit event is not signal in 5 seconds, just give up and close anyway sinec the thread is probably hung.
+
+ if( inMDNS->p->cancelEvent )
+ {
+ BOOL wasSet;
+
+ wasSet = SetEvent( inMDNS->p->cancelEvent );
+ check( wasSet );
+
+ if( inMDNS->p->quitEvent )
+ {
+ result = WaitForSingleObject( inMDNS->p->quitEvent, 5 * 1000 );
+ check( result == WAIT_OBJECT_0 );
+ }
+ }
+ return( mStatus_NoError );
+}
+
+//===========================================================================================================================
+// ProcessingThread
+//===========================================================================================================================
+
+mDNSlocal unsigned WINAPI ProcessingThread( LPVOID inParam )
+{
+ mDNS * m;
+ int done;
+ mStatus err;
+ HANDLE * waitList;
+ int waitListCount;
+ DWORD result;
+ BOOL wasSet;
+
+ check( inParam );
+
+ m = (mDNS *) inParam;
+ err = ProcessingThreadInitialize( m );
+ require_noerr( err, exit );
+
+ done = 0;
+ while( !done )
+ {
+ // Set up the list of objects we'll be waiting on.
+
+ waitList = NULL;
+ waitListCount = 0;
+ err = ProcessingThreadSetupWaitList( m, &waitList, &waitListCount );
+ require_noerr( err, exit );
+
+ // Main processing loop.
+
+ for( ;; )
+ {
+ // Give the mDNS core a chance to do its work and determine next event time.
+
+ mDNSs32 interval = mDNS_Execute(m) - mDNSPlatformTimeNow();
+ if (interval < 0) interval = 0;
+ else if (interval > (0x7FFFFFFF / 1000)) interval = 0x7FFFFFFF / mDNSPlatformOneSecond;
+ else interval = (interval * 1000) / mDNSPlatformOneSecond;
+
+ // Wait until something occurs (e.g. cancel, incoming packet, or timeout).
+
+ result = WaitForMultipleObjects( (DWORD) waitListCount, waitList, FALSE, (DWORD) interval );
+ if( result == WAIT_TIMEOUT )
+ {
+ // Next task timeout occurred. Loop back up to give mDNS core a chance to work.
+
+ dlog( kDebugLevelChatty - 1, DEBUG_NAME "timeout\n" );
+ continue;
+ }
+ else if( result == kWaitListCancelEvent )
+ {
+ // Cancel event. Set the done flag and break to exit.
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "canceling...\n" );
+ done = 1;
+ break;
+ }
+ else if( result == kWaitListInterfaceListChangedEvent )
+ {
+ // Interface list changed event. Break out of the inner loop to re-setup the wait list.
+
+ ProcessingThreadInterfaceListChanged( m );
+ break;
+ }
+ else if( result == kWaitListWakeupEvent )
+ {
+ // Wakeup event due to an mDNS API call. Loop back to call mDNS_Execute.
+
+ dlog( kDebugLevelChatty - 1, DEBUG_NAME "wakeup\n" );
+ continue;
+ }
+ else
+ {
+ int waitItemIndex;
+
+ // Socket data available event. Determine which socket and process the packet.
+
+ waitItemIndex = (int)( ( (int) result ) - WAIT_OBJECT_0 );
+ dlog( kDebugLevelChatty, DEBUG_NAME "socket data available on socket index %d\n", waitItemIndex );
+ check( ( waitItemIndex >= 0 ) && ( waitItemIndex < waitListCount ) );
+ if( ( waitItemIndex >= 0 ) && ( waitItemIndex < waitListCount ) )
+ {
+ HANDLE signaledObject;
+ int n;
+ mDNSInterfaceData * ifd;
+
+ signaledObject = waitList[ waitItemIndex ];
+
+ n = 0;
+ for( ifd = m->p->interfaceList; ifd; ifd = ifd->next )
+ {
+ if( ifd->multicastReadPendingEvent == signaledObject )
+ {
+ ProcessingThreadProcessPacket( m, ifd, ifd->multicastSocketRef );
+ ++n;
+ }
+ if( ifd->unicastReadPendingEvent == signaledObject )
+ {
+ ProcessingThreadProcessPacket( m, ifd, ifd->unicastSocketRef );
+ ++n;
+ }
+ }
+ check( n > 0 );
+ }
+ else
+ {
+ // Unexpected wait result.
+
+ dlog( kDebugLevelAllowedError, DEBUG_NAME "unexpected wait result (result=0x%08X)\n", result );
+ }
+ }
+ }
+
+ // Release the wait list.
+
+ if( waitList )
+ {
+ free( waitList );
+ waitList = NULL;
+ waitListCount = 0;
+ }
+ }
+
+ // Signal the quit event to indicate that the thread is finished.
+
+exit:
+ wasSet = SetEvent( m->p->quitEvent );
+ check( wasSet );
+
+ // Call _endthreadex() explicitly instead of just exiting normally to avoid memory leaks when using static run-time
+ // libraries. See <http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/createthread.asp>.
+
+ _endthreadex( 0 );
+ return( 0 );
+}
+
+//===========================================================================================================================
+// ProcessingThreadInitialize
+//===========================================================================================================================
+
+mDNSlocal mStatus ProcessingThreadInitialize( mDNS * const inMDNS )
+{
+ mStatus err;
+ BOOL wasSet;
+
+ inMDNS->p->threadID = GetCurrentThreadId();
+
+ err = SetupInterfaceList( inMDNS );
+ require_noerr( err, exit );
+
+exit:
+ if( err )
+ {
+ TearDownInterfaceList( inMDNS );
+ }
+ inMDNS->p->initStatus = err;
+ wasSet = SetEvent( inMDNS->p->initEvent );
+ check( wasSet );
+
+ return( err );
+}
+
+//===========================================================================================================================
+// ProcessingThreadSetupWaitList
+//===========================================================================================================================
+
+mDNSlocal mStatus ProcessingThreadSetupWaitList( mDNS * const inMDNS, HANDLE **outWaitList, int *outWaitListCount )
+{
+ mStatus err;
+ int waitListCount;
+ HANDLE * waitList;
+ HANDLE * waitItemPtr;
+ mDNSInterfaceData * ifd;
+
+ dlog( kDebugLevelVerbose, DEBUG_NAME "thread setting up wait list\n" );
+ check( inMDNS );
+ check( inMDNS->p );
+ check( outWaitList );
+ check( outWaitListCount );
+
+ // Allocate an array to hold all the objects to wait on.
+
+ waitListCount = kWaitListFixedItemCount + ( 2 * inMDNS->p->interfaceCount );
+ waitList = (HANDLE *) malloc( waitListCount * sizeof( *waitList ) );
+ require_action( waitList, exit, err = mStatus_NoMemoryErr );
+ waitItemPtr = waitList;
+
+ // Add the fixed wait items to the beginning of the list.
+
+ *waitItemPtr++ = inMDNS->p->cancelEvent;
+ *waitItemPtr++ = inMDNS->p->interfaceListChangedEvent;
+ *waitItemPtr++ = inMDNS->p->wakeupEvent;
+
+ // Append all the dynamic wait items to the list.
+
+ for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next )
+ {
+ *waitItemPtr++ = ifd->multicastReadPendingEvent;
+ *waitItemPtr++ = ifd->unicastReadPendingEvent;
+ }
+
+ *outWaitList = waitList;
+ *outWaitListCount = waitListCount;
+ waitList = NULL;
+ err = mStatus_NoError;
+
+exit:
+ if( waitList )
+ {
+ free( waitList );
+ }
+ dlog( kDebugLevelVerbose, DEBUG_NAME "thread setting up wait list done (err=%ld)\n", err );
+ return( err );
+}
+
+//===========================================================================================================================
+// ProcessingThreadProcessPacket
+//===========================================================================================================================
+
+mDNSlocal void ProcessingThreadProcessPacket( mDNS *inMDNS, mDNSInterfaceData *inIFD, SocketRef inSocketRef )
+{
+ int n;
+ mDNSBool isMulticast;
+ DNSMessage packet;
+ struct sockaddr_in addr;
+ int addrSize;
+ mDNSu8 * packetEndPtr;
+ mDNSAddr srcAddr;
+ mDNSIPPort srcPort;
+ mDNSAddr dstAddr;
+ mDNSIPPort dstPort;
+
+ isMulticast = (mDNSBool)( inSocketRef == inIFD->multicastSocketRef );
+
+ // Receive the packet.
+
+ addrSize = sizeof( addr );
+ n = recvfrom( inSocketRef, (char *) &packet, sizeof( packet ), 0, (struct sockaddr *) &addr, &addrSize );
+ check( n >= 0 );
+ if( n >= 0 )
+ {
+ // Set up the src/dst/interface info.
+
+ srcAddr.type = mDNSAddrType_IPv4;
+ srcAddr.ip.v4.NotAnInteger = addr.sin_addr.s_addr;
+ srcPort.NotAnInteger = addr.sin_port;
+ dstAddr.type = mDNSAddrType_IPv4;
+ dstAddr.ip.v4 = isMulticast ? AllDNSLinkGroup : inIFD->hostSet.ip.ip.v4;
+ dstPort = isMulticast ? MulticastDNSPort : UnicastDNSPort;
+
+ dlog( kDebugLevelChatty, DEBUG_NAME "packet received\n" );
+ dlog( kDebugLevelChatty, DEBUG_NAME " size = %d\n", n );
+ dlog( kDebugLevelChatty, DEBUG_NAME " src = %u.%u.%u.%u:%u\n",
+ srcAddr.ip.v4.b[ 0 ], srcAddr.ip.v4.b[ 1 ], srcAddr.ip.v4.b[ 2 ], srcAddr.ip.v4.b[ 3 ],
+ ntohs( srcPort.NotAnInteger ) );
+ dlog( kDebugLevelChatty, DEBUG_NAME " dst = %u.%u.%u.%u:%u\n",
+ dstAddr.ip.v4.b[ 0 ], dstAddr.ip.v4.b[ 1 ], dstAddr.ip.v4.b[ 2 ], dstAddr.ip.v4.b[ 3 ],
+ ntohs( dstPort.NotAnInteger ) );
+ dlog( kDebugLevelChatty, DEBUG_NAME " interface = %u.%u.%u.%u\n",
+ inIFD->hostSet.ip.ip.v4.b[ 0 ], inIFD->hostSet.ip.ip.v4.b[ 1 ],
+ inIFD->hostSet.ip.ip.v4.b[ 2 ], inIFD->hostSet.ip.ip.v4.b[ 3 ] );
+
+ dlog( kDebugLevelChatty, DEBUG_NAME "--\n" );
+
+ // Dispatch the packet to mDNS.
+
+ packetEndPtr = ( (mDNSu8 *) &packet ) + n;
+ mDNSCoreReceive( inMDNS, &packet, packetEndPtr, &srcAddr, srcPort, &dstAddr, dstPort, inIFD->hostSet.InterfaceID, 255 );
+ }
+
+ // Update counters.
+
+ inIFD->recvMulticastCounter += isMulticast;
+ inIFD->recvUnicastCounter += !isMulticast;
+ inIFD->recvErrorCounter += ( n < 0 );
+}
+
+//===========================================================================================================================
+// ProcessingThreadInterfaceListChanged
+//===========================================================================================================================
+
+mDNSlocal void ProcessingThreadInterfaceListChanged( mDNS *inMDNS )
+{
+ mStatus err;
+
+ dlog( kDebugLevelInfo, DEBUG_NAME "interface list changed event\n" );
+ check( inMDNS );
+
+ mDNSPlatformLock( inMDNS );
+
+ // Tear down the existing interfaces and set up new ones using the new IP info.
+
+ err = TearDownInterfaceList( inMDNS );
+ check_noerr( err );
+
+ err = SetupInterfaceList( inMDNS );
+ check_noerr( err );
+
+ mDNSPlatformUnlock( inMDNS );
+
+ // Inform clients of the change.
+
+ if( inMDNS->MainCallback )
+ {
+ inMDNS->MainCallback( inMDNS, mStatus_ConfigChanged );
+ }
+
+ // Force mDNS to update.
+
+ mDNSCoreMachineSleep( inMDNS, mDNSfalse );
+}
+
+#if 0
+#pragma mark -
+#pragma mark == Utilities ==
+#endif
+
+#if( defined( _WIN32_WCE ) )
+//===========================================================================================================================
+// getifaddrs
+//===========================================================================================================================
+
+int getifaddrs( struct ifaddrs **outAddrs )
+{
+ int err;
+ SocketRef sock;
+ DWORD size;
+ void * buffer;
+ SOCKET_ADDRESS_LIST * addressList;
+ struct ifaddrs * head;
+ struct ifaddrs ** next;
+ struct ifaddrs * ifa;
+ int n;
+ int i;
+
+ sock = kInvalidSocketRef;
+ buffer = NULL;
+ head = NULL;
+ next = &head;
+
+ // Open a temporary socket because one is needed to use WSAIoctl (we'll close it before exiting this function).
+
+ sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
+ require_action( IsValidSocket( sock ), exit, err = mStatus_NoMemoryErr );
+
+ // Call WSAIoctl with SIO_ADDRESS_LIST_QUERY and pass a null buffer. This call will fail, but the size needed to
+ // for the request will be filled in. Once we know the size, allocate a buffer to hold the entire list.
+ //
+ // NOTE: Due to a bug in Windows CE, the size returned by WSAIoctl is not enough so double it as a workaround.
+
+ size = 0;
+ WSAIoctl( sock, SIO_ADDRESS_LIST_QUERY, NULL, 0, NULL, 0, &size, NULL, NULL );
+ require_action( size > 0, exit, err = -1 );
+ size *= 2;
+
+ buffer = malloc( size );
+ require_action( buffer, exit, err = -1 );
+
+ // We now know the size of the list and have a buffer to hold so call WSAIoctl again to get it.
+
+ err = WSAIoctl( sock, SIO_ADDRESS_LIST_QUERY, NULL, 0, buffer, size, &size, NULL, NULL );
+ require_noerr( err, exit );
+ addressList = (SOCKET_ADDRESS_LIST *) buffer;
+
+ // Process the raw interface list and build a linked list of interfaces.
+ //
+ // NOTE: Due to a bug in Windows CE, the iAddressCount field is always 0 so use 1 in that case.
+
+ n = addressList->iAddressCount;
+ if( n == 0 )
+ {
+ n = 1;
+ }
+ for( i = 0; i < n; ++i )
+ {
+ ifa = (struct ifaddrs *) calloc( 1, sizeof( struct ifaddrs ) );
+ require_action( ifa, exit, err = WSAENOBUFS );
+
+ *next = ifa;
+ next = &ifa->ifa_next;
+
+ // Fetch the name. $$$ TO DO: Get the real name of the interface.
+
+ ifa->ifa_name = (char *) malloc( 16 );
+ require_action( ifa->ifa_name, exit, err = WSAENOBUFS );
+ sprintf( ifa->ifa_name, "%d", i + 1 );
+
+ // Fetch flags. Note: SIO_ADDRESS_LIST_QUERY does not report flags so just fake IFF_UP and IFF_MULTICAST.
+
+ ifa->ifa_flags = IFF_UP | IFF_MULTICAST;
+
+ // Fetch addresses.
+
+ switch( addressList->Address[ i ].lpSockaddr->sa_family )
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in * sinptr4;
+
+ sinptr4 = (struct sockaddr_in *) addressList->Address[ i ].lpSockaddr;
+ ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sinptr4 ) );
+ require_action( ifa->ifa_addr, exit, err = WSAENOBUFS );
+ memcpy( ifa->ifa_addr, sinptr4, sizeof( *sinptr4 ) );
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ // Success!
+
+ if( outAddrs )
+ {
+ *outAddrs = head;
+ head = NULL;
+ }
+ err = 0;
+
+exit:
+ if( head )
+ {
+ freeifaddrs( head );
+ }
+ if( buffer )
+ {
+ free( buffer );
+ }
+ if( sock != INVALID_SOCKET )
+ {
+ closesocket( sock );
+ }
+ return( err );
+}
+#endif // defined( _WIN32_WCE ) )
+
+#if( !defined( _WIN32_WCE ) )
+//===========================================================================================================================
+// getifaddrs
+//===========================================================================================================================
+
+int getifaddrs( struct ifaddrs **outAddrs )
+{
+ int err;
+ SOCKET sock;
+ DWORD size;
+ DWORD actualSize;
+ INTERFACE_INFO * buffer;
+ INTERFACE_INFO * tempBuffer;
+ INTERFACE_INFO * ifInfo;
+ int n;
+ int i;
+ struct ifaddrs * head;
+ struct ifaddrs ** next;
+ struct ifaddrs * ifa;
+ struct sockaddr_in * sinptr4;
+ struct sockaddr_in6_old * sinptr6;
+ struct sockaddr * sa;
+
+ sock = INVALID_SOCKET;
+ buffer = NULL;
+ head = NULL;
+ next = &head;
+
+ // Get the interface list. WSAIoctl is called with SIO_GET_INTERFACE_LIST, but since this does not provide a
+ // way to determine the size of the interface list beforehand, we have to start with an initial size guess and
+ // call WSAIoctl repeatedly with increasing buffer sizes until it succeeds. Limit this to 100 tries for safety.
+
+ sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
+ require_action( sock != INVALID_SOCKET, exit, err = WSAEMFILE );
+
+ n = 0;
+ size = 16 * sizeof( INTERFACE_INFO );
+ for( ;; )
+ {
+ tempBuffer = (INTERFACE_INFO *) realloc( buffer, size );
+ require_action( tempBuffer, exit, err = WSAENOBUFS );
+ buffer = tempBuffer;
+
+ err = WSAIoctl( sock, SIO_GET_INTERFACE_LIST, NULL, 0, buffer, size, &actualSize, NULL, NULL );
+ if( err == 0 )
+ {
+ break;
+ }
+
+ ++n;
+ require_action( n < 100, exit, err = WSAEADDRNOTAVAIL );
+
+ size += ( 16 * sizeof( INTERFACE_INFO ) );
+ }
+ check( actualSize <= size );
+ check( ( actualSize % sizeof( INTERFACE_INFO ) ) == 0 );
+ n = (int)( actualSize / sizeof( INTERFACE_INFO ) );
+
+ // Process the raw interface list and build a linked list of interfaces.
+
+ for( i = 0; i < n; ++i )
+ {
+ ifInfo = &buffer[ i ];
+
+ ifa = (struct ifaddrs *) calloc( 1, sizeof( struct ifaddrs ) );
+ require_action( ifa, exit, err = WSAENOBUFS );
+
+ *next = ifa;
+ next = &ifa->ifa_next;
+
+ // Fetch the name. $$$ TO DO: Get the real name of the interface.
+
+ ifa->ifa_name = (char *) malloc( 16 );
+ require_action( ifa->ifa_name, exit, err = WSAENOBUFS );
+ sprintf( ifa->ifa_name, "%d", i + 1 );
+
+ // Fetch interface flags.
+
+ ifa->ifa_flags = (u_int) ifInfo->iiFlags;
+
+ // Fetch addresses.
+
+ switch( ifInfo->iiAddress.Address.sa_family )
+ {
+ case AF_INET:
+ sinptr4 = &ifInfo->iiAddress.AddressIn;
+ ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sinptr4 ) );
+ require_action( ifa->ifa_addr, exit, err = WSAENOBUFS );
+ memcpy( ifa->ifa_addr, sinptr4, sizeof( *sinptr4 ) );
+
+ if( ifInfo->iiNetmask.Address.sa_family == AF_INET )
+ {
+ sinptr4 = &ifInfo->iiNetmask.AddressIn;
+ ifa->ifa_netmask = (struct sockaddr *) calloc( 1, sizeof( *sinptr4 ) );
+ require_action( ifa->ifa_netmask, exit, err = WSAENOBUFS );
+ memcpy( ifa->ifa_netmask, sinptr4, sizeof( *sinptr4 ) );
+ }
+
+ if( ifInfo->iiBroadcastAddress.Address.sa_family == AF_INET )
+ {
+ sinptr4 = &ifInfo->iiBroadcastAddress.AddressIn;
+ ifa->ifa_broadaddr = (struct sockaddr *) calloc( 1, sizeof( *sinptr4 ) );
+ require_action( ifa->ifa_broadaddr, exit, err = WSAENOBUFS );
+ memcpy( ifa->ifa_broadaddr, sinptr4, sizeof( *sinptr4 ) );
+ }
+ break;
+
+ case AF_INET6:
+ sinptr6 = &ifInfo->iiAddress.AddressIn6;
+ ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sinptr6 ) );
+ require_action( ifa->ifa_addr, exit, err = WSAENOBUFS );
+ memcpy( ifa->ifa_addr, sinptr6, sizeof( *sinptr6 ) );
+ break;
+
+ default:
+ sa = &ifInfo->iiAddress.Address;
+ ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sa ) );
+ require_action( ifa->ifa_addr, exit, err = WSAENOBUFS );
+ memcpy( ifa->ifa_addr, sa, sizeof( *sa ) );
+ break;
+ }
+ }
+
+ // Success!
+
+ if( outAddrs )
+ {
+ *outAddrs = head;
+ head = NULL;
+ }
+ err = 0;
+
+exit:
+ if( head )
+ {
+ freeifaddrs( head );
+ }
+ if( buffer )
+ {
+ free( buffer );
+ }
+ if( sock != INVALID_SOCKET )
+ {
+ closesocket( sock );
+ }
+ return( err );
+}
+#endif // !defined( _WIN32_WCE ) )
+
+//===========================================================================================================================
+// freeifaddrs
+//===========================================================================================================================
+
+void freeifaddrs( struct ifaddrs *inAddrs )
+{
+ struct ifaddrs * p;
+ struct ifaddrs * q;
+
+ // Free each piece of the structure. Set to null after freeing to handle macro-aliased fields.
+
+ for( p = inAddrs; p; p = q )
+ {
+ q = p->ifa_next;
+
+ if( p->ifa_name )
+ {
+ free( p->ifa_name );
+ p->ifa_name = NULL;
+ }
+ if( p->ifa_addr )
+ {
+ free( p->ifa_addr );
+ p->ifa_addr = NULL;
+ }
+ if( p->ifa_netmask )
+ {
+ free( p->ifa_netmask );
+ p->ifa_netmask = NULL;
+ }
+ if( p->ifa_broadaddr )
+ {
+ free( p->ifa_broadaddr );
+ p->ifa_broadaddr = NULL;
+ }
+ if( p->ifa_dstaddr )
+ {
+ free( p->ifa_dstaddr );
+ p->ifa_dstaddr = NULL;
+ }
+ if( p->ifa_data )
+ {
+ free( p->ifa_data );
+ p->ifa_data = NULL;
+ }
+ free( p );
+ }
+}
+
+#if( !defined( _WIN32_WCE ) )
+//===========================================================================================================================
+// sock_pton
+//===========================================================================================================================
+
+int sock_pton( const char *inString, int inFamily, void *outAddr, size_t inAddrSize, size_t *outAddrSize )
+{
+ int err;
+ int size;
+
+ if( inAddrSize == 0 )
+ {
+ if( inFamily == AF_INET )
+ {
+ inAddrSize = sizeof( struct sockaddr_in );
+ }
+ else if( inFamily == AF_INET6 )
+ {
+ inAddrSize = sizeof( struct sockaddr_in6 );
+ }
+ else
+ {
+ err = WSAEAFNOSUPPORT;
+ goto exit;
+ }
+ }
+ size = (int) inAddrSize;
+
+ err = WSAStringToAddressA( (char *) inString, inFamily, NULL, (LPSOCKADDR) outAddr, &size );
+ if( err != 0 ) goto exit;
+
+ if( outAddrSize )
+ {
+ *outAddrSize = (size_t) size;
+ }
+
+exit:
+ return( err );
+}
+
+//===========================================================================================================================
+// sock_ntop
+//===========================================================================================================================
+
+char * sock_ntop( const void *inAddr, size_t inAddrSize, char *inBuffer, size_t inBufferSize )
+{
+ DWORD size;
+ int err;
+ DWORD stringSize;
+
+ if( inAddrSize == 0 )
+ {
+ const struct sockaddr * addr;
+
+ addr = (const struct sockaddr *) inAddr;
+ if( addr->sa_family == AF_INET )
+ {
+ size = sizeof( struct sockaddr_in );
+ }
+ else if( addr->sa_family == AF_INET6 )
+ {
+ size = sizeof( struct sockaddr_in6 );
+ }
+ else
+ {
+ WSASetLastError( WSAEAFNOSUPPORT );
+ inBuffer = NULL;
+ goto exit;
+ }
+ }
+ else
+ {
+ size = (DWORD) inAddrSize;
+ }
+
+ stringSize = (DWORD) inBufferSize;
+ err = WSAAddressToStringA( (LPSOCKADDR) inAddr, size, NULL, inBuffer, &stringSize );
+ if( err )
+ {
+ inBuffer = NULL;
+ }
+
+exit:
+ return( inBuffer );
+}
+#endif // !defined( _WIN32_WCE )
--- /dev/null
+/*
+ * 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: mDNSWin32.h,v $
+Revision 1.9 2003/08/20 06:21:25 bradley
+Updated to latest internal version of the Rendezvous for Windows platform plugin: Added support
+for Windows CE/PocketPC 2003; re-did interface-related code to emulate getifaddrs/freeifaddrs for
+restricting usage to only active, multicast-capable, and non-point-to-point interfaces and to ease
+the addition of IPv6 support in the future; Changed init code to serialize thread initialization to
+enable ThreadID improvement to wakeup notification; Define platform support structure locally to
+allow portable mDNS_Init usage; Removed dependence on modified mDNSCore: define interface ID<->name
+structures/prototypes locally; Changed to use _beginthreadex()/_endthreadex() on non-Windows CE
+platforms (re-mapped to CreateThread on Window CE) to avoid a leak in the Microsoft C runtime;
+Added IPv4/IPv6 string<->address conversion routines; Cleaned up some code and added HeaderDoc.
+
+Revision 1.8 2003/08/12 19:56:27 cheshire
+Update to APSL 2.0
+
+Revision 1.7 2003/07/23 02:23:01 cheshire
+Updated mDNSPlatformUnlock() to work correctly, now that <rdar://problem/3160248>
+"ScheduleNextTask needs to be smarter" has refined the way m->NextScheduledEvent is set
+
+Revision 1.6 2003/07/02 21:20:04 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.5 2003/04/29 00:06:09 cheshire
+<rdar://problem/3242673> mDNSWindows needs a wakeupEvent object to signal the main thread
+
+Revision 1.4 2003/03/22 02:57:44 cheshire
+Updated mDNSWindows to use new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt")
+
+Revision 1.3 2002/09/21 20:44:54 zarzycki
+Added APSL info
+
+Revision 1.2 2002/09/20 05:55:16 bradley
+Multicast DNS platform plugin for Win32
+
+*/
+
+#ifndef __MDNS_WIN32__
+#define __MDNS_WIN32__
+
+#if( !defined( WIN32_LEAN_AND_MEAN ) )
+ #define WIN32_LEAN_AND_MEAN // Needed to avoid redefinitions by Windows interfaces.
+#endif
+
+#include <windows.h>
+#include <winsock2.h>
+#include <Ws2tcpip.h>
+
+#include "mDNSClientAPI.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @typedef SocketRef
+
+ @abstract Socket file descriptor alias for improved readability.
+*/
+
+typedef SOCKET SocketRef;
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @struct mDNSInterfaceData
+
+ @abstract Structure containing interface-specific data.
+*/
+
+typedef struct mDNSInterfaceData mDNSInterfaceData;
+struct mDNSInterfaceData
+{
+ mDNSInterfaceData * next;
+ char name[ 256 ];
+ SocketRef multicastSocketRef;
+ HANDLE multicastReadPendingEvent;
+ SocketRef unicastSocketRef;
+ HANDLE unicastReadPendingEvent;
+ NetworkInterfaceInfo hostSet;
+ mDNSBool hostRegistered;
+
+ int sendMulticastCounter;
+ int sendUnicastCounter;
+ int sendErrorCounter;
+
+ int recvMulticastCounter;
+ int recvUnicastCounter;
+ int recvErrorCounter;
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @struct mDNS_PlatformSupport_struct
+
+ @abstract Structure containing platform-specific data.
+*/
+
+struct mDNS_PlatformSupport_struct
+{
+ CRITICAL_SECTION lock;
+ mDNSBool lockInitialized;
+ HANDLE cancelEvent;
+ HANDLE quitEvent;
+ HANDLE interfaceListChangedEvent;
+ HANDLE wakeupEvent;
+ HANDLE initEvent;
+ mStatus initStatus;
+
+ SocketRef interfaceListChangedSocketRef;
+ int interfaceCount;
+ mDNSInterfaceData * interfaceList;
+ DWORD threadID;
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @struct ifaddrs
+
+ @abstract Interface information
+*/
+
+struct ifaddrs
+{
+ struct ifaddrs * ifa_next;
+ char * ifa_name;
+ u_int ifa_flags;
+ struct sockaddr * ifa_addr;
+ struct sockaddr * ifa_netmask;
+ struct sockaddr * ifa_broadaddr;
+ struct sockaddr * ifa_dstaddr;
+ void * ifa_data;
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function getifaddrs
+
+ @abstract Builds a linked list of interfaces. Caller must free using freeifaddrs if successful.
+*/
+
+int getifaddrs( struct ifaddrs **outAddrs );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function freeifaddrs
+
+ @abstract Frees a linked list of interfaces built with getifaddrs.
+*/
+
+void freeifaddrs( struct ifaddrs *inAddrs );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function sock_pton
+
+ @abstract Converts a 'p'resentation address string into a 'n'umeric sockaddr structure.
+
+ @result 0 if successful or an error code on failure.
+*/
+
+int sock_pton( const char *inString, int inFamily, void *outAddr, size_t inAddrSize, size_t *outAddrSize );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*! @function sock_ntop
+
+ @abstract Converts a 'n'umeric sockaddr structure into a 'p'resentation address string.
+
+ @result Ptr to 'p'resentation address string buffer if successful or NULL on failure.
+*/
+
+char * sock_ntop( const void *inAddr, size_t inAddrSize, char *inBuffer, size_t inBufferSize );
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif // __MDNS_WIN32__