From: Apple Date: Wed, 10 Sep 2003 00:51:47 +0000 (+0000) Subject: mDNSResponder-58.tar.gz X-Git-Tag: mac-os-x-103^0 X-Git-Url: https://git.saurik.com/apple/mdnsresponder.git/commitdiff_plain/c9b9ae52cd24d9becbe66090b85e59f088d39b79 mDNSResponder-58.tar.gz --- diff --git a/APPLE_LICENSE b/APPLE_LICENSE new file mode 100644 index 0000000..fe81a60 --- /dev/null +++ b/APPLE_LICENSE @@ -0,0 +1,367 @@ +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." diff --git a/CFSocket.c b/CFSocket.c deleted file mode 100644 index 292d586..0000000 --- a/CFSocket.c +++ /dev/null @@ -1,879 +0,0 @@ -/* - * 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 -#include // For va_list support -#include -#include -#include -#include -#include - -// Code contributed by Dave Heller: -// Define RUN_ON_PUMA_WITHOUT_IFADDRS to compile code that will -// work on Mac OS X 10.1, which does not have the getifaddrs call. -#define RUN_ON_PUMA_WITHOUT_IFADDRS 0 - -#if RUN_ON_PUMA_WITHOUT_IFADDRS - -#include -#include -#define ifaddrs ifa_info -#ifndef ifa_broadaddr -#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */ -#endif -#include - -#else - -#include - -#endif - -#include -#include - -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 */ -#define IFA_HADDR 8 /* allow for 64-bit EUI-64 in future */ - -struct ifa_info { - char ifa_name[IFA_NAME]; /* interface name, null terminated */ - u_char ifa_haddr[IFA_HADDR]; /* hardware address */ - u_short ifa_hlen; /* #bytes in hardware address: 0, 6, 8 */ - short ifa_flags; /* IFF_xxx constants from */ - short ifa_myflags; /* our own IFI_xxx flags */ - struct sockaddr *ifa_addr; /* primary address */ - struct sockaddr *ifa_brdaddr;/* broadcast address */ - struct sockaddr *ifa_dstaddr;/* destination address */ - struct ifa_info *ifa_next; /* next of these structures */ -}; - -#define IFI_ALIAS 1 /* ifa_addr is an alias */ - - /* function prototypes */ -struct ifa_info *get_ifa_info(int, int); -struct ifa_info *Get_ifa_info(int, int); -void free_ifa_info(struct ifa_info *); - -#define HAVE_SOCKADDR_SA_LEN 1 - -struct ifa_info * -get_ifa_info(int family, int doaliases) -{ - struct ifa_info *ifi, *ifihead, **ifipnext; - int sockfd, len, lastlen, flags, myflags; - char *ptr, *buf, lastname[IFNAMSIZ], *cptr; - struct ifconf ifc; - struct ifreq *ifr, ifrcopy; - struct sockaddr_in *sinptr; - - sockfd = socket(AF_INET, SOCK_DGRAM, 0); - - lastlen = 0; - len = 100 * sizeof(struct ifreq); /* initial buffer size guess */ - for ( ; ; ) { - buf = (char *) malloc(len); - ifc.ifc_len = len; - ifc.ifc_buf = buf; - if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) { - if (errno != EINVAL || lastlen != 0) - debugf("ioctl error"); - } else { - if (ifc.ifc_len == lastlen) - break; /* success, len has not changed */ - lastlen = ifc.ifc_len; - } - len += 10 * sizeof(struct ifreq); /* increment */ - free(buf); - } - ifihead = NULL; - ifipnext = &ifihead; - lastname[0] = 0; -/* end get_ifa_info1 */ - -/* include get_ifa_info2 */ - for (ptr = buf; ptr < buf + ifc.ifc_len; ) { - ifr = (struct ifreq *) ptr; - -#ifdef HAVE_SOCKADDR_SA_LEN - len = MAX(sizeof(struct sockaddr), ifr->ifr_addr.sa_len); -#else - switch (ifr->ifr_addr.sa_family) { -#ifdef IPV6 - case AF_INET6: - len = sizeof(struct sockaddr_in6); - break; -#endif - case AF_INET: - default: - len = sizeof(struct sockaddr); - break; - } -#endif /* HAVE_SOCKADDR_SA_LEN */ - ptr += sizeof(ifr->ifr_name) + len; /* for next one in buffer */ - - if (ifr->ifr_addr.sa_family != family) - continue; /* ignore if not desired address family */ - - myflags = 0; - if ( (cptr = strchr(ifr->ifr_name, ':')) != NULL) - *cptr = 0; /* replace colon will null */ - if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) { - if (doaliases == 0) - continue; /* already processed this interface */ - myflags = IFI_ALIAS; - } - memcpy(lastname, ifr->ifr_name, IFNAMSIZ); - - ifrcopy = *ifr; - ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy); - flags = ifrcopy.ifr_flags; - if ((flags & IFF_UP) == 0) - continue; /* ignore if interface not up */ - - ifi = (struct ifa_info *) calloc(1, sizeof(struct ifa_info)); - *ifipnext = ifi; /* prev points to this new one */ - ifipnext = &ifi->ifa_next; /* pointer to next one goes here */ - - ifi->ifa_flags = flags; /* IFF_xxx values */ - ifi->ifa_myflags = myflags; /* IFI_xxx values */ - memcpy(ifi->ifa_name, ifr->ifr_name, IFA_NAME); - ifi->ifa_name[IFA_NAME-1] = '\0'; -/* end get_ifa_info2 */ -/* include get_ifa_info3 */ - switch (ifr->ifr_addr.sa_family) { - case AF_INET: - sinptr = (struct sockaddr_in *) &ifr->ifr_addr; - if (ifi->ifa_addr == NULL) { - ifi->ifa_addr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in)); - memcpy(ifi->ifa_addr, sinptr, sizeof(struct sockaddr_in)); - -#ifdef SIOCGIFBRDADDR - if (flags & IFF_BROADCAST) { - ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy); - sinptr = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr; - ifi->ifa_brdaddr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in)); - memcpy(ifi->ifa_brdaddr, sinptr, sizeof(struct sockaddr_in)); - } -#endif - -#ifdef SIOCGIFDSTADDR - if (flags & IFF_POINTOPOINT) { - ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy); - sinptr = (struct sockaddr_in *) &ifrcopy.ifr_dstaddr; - ifi->ifa_dstaddr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in)); - memcpy(ifi->ifa_dstaddr, sinptr, sizeof(struct sockaddr_in)); - } -#endif - } - break; - - default: - break; - } - } - free(buf); - return(ifihead); /* pointer to first structure in linked list */ -} -/* end get_ifa_info3 */ - -/* include free_ifa_info */ -mDNSlocal void freeifaddrs(struct ifa_info *ifihead) -{ - struct ifa_info *ifi, *ifinext; - - for (ifi = ifihead; ifi != NULL; ifi = ifinext) { - if (ifi->ifa_addr != NULL) - free(ifi->ifa_addr); - if (ifi->ifa_brdaddr != NULL) - free(ifi->ifa_brdaddr); - if (ifi->ifa_dstaddr != NULL) - free(ifi->ifa_dstaddr); - ifinext = ifi->ifa_next; /* can't fetch ifa_next after free() */ - free(ifi); /* the ifa_info{} itself */ - } -} -/* end free_ifa_info */ - -struct ifa_info * -Get_ifa_info(int family, int doaliases) -{ - struct ifa_info *ifi; - - if ( (ifi = get_ifa_info(family, doaliases)) == NULL) - debugf("get_ifa_info error"); - return(ifi); -} - -mDNSlocal int getifaddrs(struct ifa_info **ifalist) - { - *ifalist = get_ifa_info(PF_INET, false); - if( ifalist == nil ) - return -1; - else - return(0); - } - -#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; diff --git a/DNSServiceDiscoveryDefines.h b/DNSServiceDiscoveryDefines.h deleted file mode 100644 index 1c8e8ea..0000000 --- a/DNSServiceDiscoveryDefines.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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 - diff --git a/DNSServiceDiscoveryReply.defs b/DNSServiceDiscoveryReply.defs deleted file mode 100644 index c08e86f..0000000 --- a/DNSServiceDiscoveryReply.defs +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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 diff --git a/DNSServiceDiscoveryRequest.defs b/DNSServiceDiscoveryRequest.defs deleted file mode 100644 index fc17bd3..0000000 --- a/DNSServiceDiscoveryRequest.defs +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..96298f8 --- /dev/null +++ b/Makefile @@ -0,0 +1,33 @@ +# +# 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 diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..60abf83 --- /dev/null +++ b/README.txt @@ -0,0 +1,145 @@ +What is mDNSResponder? +---------------------- + +The mDNSResponder project is a component of Rendezvous, +Apple's ease-of-use IP networking initiative: + + +Apple's Rendezvous software derives from the ongoing standardization +work of the IETF Zero Configuration Networking Working Group: + + +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" + + +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. diff --git a/SamplemDNSClient.c b/SamplemDNSClient.c deleted file mode 100644 index 8f14f2d..0000000 --- a/SamplemDNSClient.c +++ /dev/null @@ -1,329 +0,0 @@ -/* - * 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 , - * 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 -#include -#include -#include - -//************************************************************************************************************* -// 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> 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 (Browse for services instances)\n", argv[0]); - fprintf(stderr, "%s -L (Look up a service instance)\n", argv[0]); - fprintf(stderr, "%s -R (Register a service)\n", argv[0]); - fprintf(stderr, "%s -A (Test Adding/Updating/Deleting a record)\n", argv[0]); - fprintf(stderr, "%s -U (Test updating a TXT record)\n", argv[0]); - fprintf(stderr, "%s -N (Test adding a large NULL record)\n", argv[0]); - fprintf(stderr, "%s -T (Test creating a large TXT record)\n", argv[0]); - fprintf(stderr, "%s -M (Test creating a registration with multiple TXT records)\n", argv[0]); - return 0; - } diff --git a/daemon.c b/daemon.c deleted file mode 100644 index 4432196..0000000 --- a/daemon.c +++ /dev/null @@ -1,1182 +0,0 @@ -/* - * 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 , - * 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 -#include -#include -#include -#include - -#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 - -//************************************************************************************************************* -// 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; iinfo->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, @@ -25,21 +40,970 @@ * 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 + 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 + 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 + Initial probes and queries not grouped on wake-from-sleep + +Revision 1.304 2003/09/09 02:41:19 cheshire + Don't send a Goodbye record if we never announced it + +Revision 1.303 2003/09/05 19:55:02 cheshire + Include address records when announcing SRV records + +Revision 1.302 2003/09/05 00:01:36 cheshire + Don't accelerate queries that have large KA lists + +Revision 1.301 2003/09/04 22:51:13 cheshire + Group probes and goodbyes better + +Revision 1.300 2003/09/03 02:40:37 cheshire + 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 + CacheRecordRmv ERROR +Don't update m->NewQuestions until *after* CheckCacheExpiration(); + +Revision 1.298 2003/09/03 01:47:01 cheshire + Rendezvous services always in a state of flux +Change mDNS_Reconfirm_internal() minimum timeout from 5 seconds to 45-60 seconds + +Revision 1.297 2003/08/29 19:44:15 cheshire + Traffic reduction: Eliminate synchronized QUs when a new service appears +1. Use m->RandomQueryDelay to impose a random delay in the range 0-500ms on queries + that already have at least one unique answer in the cache +2. For these queries, go straight to QM, skipping QU + +Revision 1.296 2003/08/29 19:08:21 cheshire + 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 + Add syslog message to report when query is reset because of immediate answer burst + +Revision 1.294 2003/08/27 02:30:22 cheshire + 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 + Traffic Reduction: Inefficiencies in DNSServiceResolverResolve() + +Revision 1.292 2003/08/21 19:27:36 cheshire + Traffic reduction: No need to announce record for longer than TTL + +Revision 1.291 2003/08/21 18:57:44 cheshire + 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 + Efficiency: Reduce repeated queries + +Revision 1.288 2003/08/20 23:39:30 cheshire + 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 + Cleanup: Review syslog messages + +Revision 1.285 2003/08/20 01:59:06 cheshire + 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 + 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 + 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 + 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 + 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 + mDNSResponder divide by zero in mDNSPlatformTimeNow() + +Revision 1.277 2003/08/18 19:05:44 cheshire + UpdateRecord not working right +Added "newrdlength" field to hold new length of updated rdata + +Revision 1.276 2003/08/16 03:39:00 cheshire + InterfaceID -1 indicates "local only" + +Revision 1.275 2003/08/16 02:51:27 cheshire + 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 + 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 + 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 + 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 + Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord + +Revision 1.270 2003/08/13 17:07:28 ksekar +Bug #: : 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 + 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 + Improve efficiency by restricting cases where we have to walk the entire cache + +Revision 1.264 2003/08/09 00:55:02 cheshire + mDNSResponder is taking 20-30% of the CPU +Don't scan the whole cache after every packet. + +Revision 1.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 + Remove "Cache size now xxx" messages + +Revision 1.261 2003/08/08 19:18:45 cheshire + Only retrigger questions on platforms with the "PhantomInterfaces" bug + +Revision 1.260 2003/08/08 18:55:48 cheshire + Guard against time going backwards + +Revision 1.259 2003/08/08 18:36:04 cheshire + Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug + +Revision 1.258 2003/08/08 16:22:05 cheshire + Need to check validity of TXT (and other) records +Remove unneeded LogMsg + +Revision 1.257 2003/08/07 01:41:08 cheshire + Ignore packets with invalid source address (all zeroes or all ones) + +Revision 1.256 2003/08/06 23:25:51 cheshire + 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 + 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 + 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 + Need to check IP TTL on responses + +Revision 1.247 2003/08/05 00:56:39 cheshire + 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 + 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 + 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 + ConvertDomainLabelToCString() needs to escape escape characters + +Revision 1.237 2003/07/19 03:23:13 cheshire + 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 + 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 + 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 + 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 + Rate-limit responses, to guard against packet flooding + +Revision 1.228 2003/07/16 20:50:27 cheshire + 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 + 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 + 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 + Need to implement service registration with subtypes + +Revision 1.218 2003/07/14 16:26:06 cheshire + 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 + 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 + 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 + 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 + After name conflict, appended number should be higher than previous number + +Revision 1.210 2003/07/12 01:43:28 cheshire + 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 + 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 + No more local.arpa + +Revision 1.206 2003/07/11 00:45:02 cheshire + 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 + mDNSResponder should log a message after 16 unsuccessful probes + +Revision 1.203 2003/07/10 23:53:41 cheshire + Hostname conflict naming should not use two hyphens + +Revision 1.202 2003/07/04 02:23:20 cheshire + 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 + 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 +: Lots of "have given xxx answers" syslog warnings +Added more detailed debugging information + +Revision 1.198 2003/07/03 22:19:30 cheshire + Bug fix in 3274153 breaks TiVo +Make exception to allow _tivo_servemedia._tcp. + +Revision 1.197 2003/07/02 22:33:05 cheshire + 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 + Update copyright notices, etc., in source code comments + +Revision 1.195 2003/07/02 19:56:58 cheshire + 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 + 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 + Merge of build failure fix for gcc 3.3 + +Revision 1.189 2003/06/11 19:24:03 cheshire + Crash in SendQueries/SendResponses when no active interfaces +Slight refinement to previous checkin + +Revision 1.188 2003/06/10 20:33:28 cheshire + Crash in SendQueries/SendResponses when no active interfaces + +Revision 1.187 2003/06/10 04:30:44 cheshire + 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 + 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 + 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 + 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 + 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 + 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 + 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 + When query produces zero results, call mDNS_Reconfirm() on any antecedent records + +Revision 1.173 2003/06/07 01:22:13 cheshire + mDNSResponder needs an mDNS_Reconfirm() function + +Revision 1.172 2003/06/07 00:59:42 cheshire + 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 + 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 + 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 + 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 + 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 + Duplicate registrations not handled as efficiently as they should be + +Revision 1.159 2003/06/03 03:31:57 cheshire + 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 + Add ability to discover what services are on a network + +Revision 1.156 2003/05/30 23:56:49 cheshire + Crash after error in mDNS_RegisterService() +Need to set "sr->Extras = mDNSNULL" before returning + +Revision 1.155 2003/05/30 23:48:00 cheshire + 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 + 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 + ConstructServiceName needs to be more restrictive + +Revision 1.152 2003/05/29 22:39:16 cheshire + Don't truncate strings in the middle of a UTF-8 character + +Revision 1.151 2003/05/29 06:35:42 cheshire + mDNSCoreReceiveResponse() purging wrong record + +Revision 1.150 2003/05/29 06:25:45 cheshire + Need to call CheckCacheExpiration() *before* AnswerNewQuestion() + +Revision 1.149 2003/05/29 06:18:39 cheshire + Split AnswerLocalQuestions into CacheRecordAdd and CacheRecordRmv + +Revision 1.148 2003/05/29 06:11:34 cheshire + 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 + 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 + 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 + mDNSResponder not sending probes at the prescribed time + +Revision 1.142 2003/05/28 03:13:07 cheshire + mDNSResponder allows invalid service registrations +Require that the transport protocol be _udp or _tcp + +Revision 1.141 2003/05/28 02:19:12 cheshire + 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 + Misleading messages generated by iChat + +Revision 1.138 2003/05/27 22:35:00 cheshire + mDNS_RegisterInterface needs to retrigger questions + +Revision 1.137 2003/05/27 20:04:33 cheshire + mDNSResponder crash in mDNS_vsnprintf() + +Revision 1.136 2003/05/27 18:50:07 cheshire + mDNS_StartResolveService doesn't inform client of port number changes + +Revision 1.135 2003/05/26 04:57:28 cheshire + Delay queries when there are already answers in the cache + +Revision 1.134 2003/05/26 04:54:54 cheshire + 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 + sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead + +Revision 1.131 2003/05/26 00:42:05 cheshire + Temporarily include mDNSResponder version in packets + +Revision 1.130 2003/05/24 16:39:48 cheshire + 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 + After name change, mDNSResponder needs to re-probe for name uniqueness + +Revision 1.127 2003/05/23 01:02:15 ksekar +Bug #: : mDNSResponder needs to include unique id in default name + +Revision 1.126 2003/05/22 02:29:22 cheshire + 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 + 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 #: 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 + 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 + 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 + 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 + 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 + Also protect against NULL domainlabels + +Revision 1.113 2003/05/07 00:28:18 cheshire + Need to make mDNSResponder more defensive against bad clients + +Revision 1.112 2003/05/06 00:00:46 cheshire + Rationalize naming of domainname manipulation functions + +Revision 1.111 2003/05/05 23:42:08 cheshire + 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 + mDNS_vsnprintf needs to be more defensive against invalid domain names + +Revision 1.109 2003/04/26 02:41:56 cheshire + Change timenow from a local variable to a structure member + +Revision 1.108 2003/04/25 01:45:56 cheshire + mDNS_RegisterNoSuchService needs to include a host name + +Revision 1.107 2003/04/25 00:41:31 cheshire + Create single routine PurgeCacheResourceRecord(), to avoid bugs in future + +Revision 1.106 2003/04/22 03:14:45 cheshire + Include Include instrumented mDNSResponder in panther now + +Revision 1.105 2003/04/22 01:07:43 cheshire + 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 #: Incorrect goodbye packet after conflict + +Revision 1.102 2003/04/17 03:06:28 cheshire +Bug #: 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 #: 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 + Off-by-one error in probe rate limiting + +Revision 1.97 2003/04/02 01:48:17 cheshire + 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 + 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 + 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 + 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 + 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 @@ -83,28 +1047,45 @@ typedef enum } 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.", @@ -113,60 +1094,413 @@ static const char *const mDNS_DomainTypeNames[] = "_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[] = "<>"; 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[] = "<>"; 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), "<>", *a); break; } + if (s + *a >= &mDNS_VACB[254]) { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<>"); 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), "<>", 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= 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) { @@ -181,8 +1515,8 @@ 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); @@ -206,6 +1540,23 @@ mDNSexport mDNSBool SameDomainName(const domainname *const d1, const domainname 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 @@ -215,175 +1566,168 @@ mDNSexport mDNSBool SameDomainName(const domainname *const d1, const domainname // 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); } } @@ -422,6 +1766,8 @@ mDNSexport char *ConvertDomainNameToCString_withescape(const domainname *const n // 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) { @@ -447,45 +1793,64 @@ mDNSexport void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], do } 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, @@ -526,26 +1891,80 @@ 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) { @@ -553,117 +1972,195 @@ mDNSlocal void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText) 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. @@ -674,93 +2171,251 @@ mDNSlocal mDNSBool SuppressDuplicate(const ResourceRecord *const ds, const Resou // (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 @@ -771,55 +2426,127 @@ mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, ResourceRecord *const rr // 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 @@ -827,74 +2554,145 @@ typedef enum { mDNS_Dereg_normal, mDNS_Dereg_conflict, mDNS_Dereg_repeat } mDNS_ // 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 @@ -970,8 +2768,20 @@ mDNSlocal mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg, 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 @@ -1000,55 +2810,59 @@ mDNSlocal mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg, 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, @@ -1058,7 +2872,7 @@ mDNSlocal mDNSu8 *putResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, 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); } @@ -1068,50 +2882,41 @@ mDNSlocal mDNSu8 *putResourceRecord(DNSMessage *const msg, mDNSu8 *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)++; @@ -1133,14 +2938,14 @@ mDNSlocal mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 * } // *************************************************************************** -#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); } @@ -1232,128 +3037,117 @@ mDNSlocal const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 * 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); } @@ -1365,16 +3159,17 @@ mDNSlocal const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, c 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); } @@ -1395,14 +3190,14 @@ mDNSlocal const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDN } // *************************************************************************** -#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; @@ -1422,7 +3217,7 @@ mDNSlocal mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, *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; @@ -1433,425 +3228,560 @@ mDNSlocal mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, 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; iIPv4Available; // 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; iInterfaceID) { - 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 = 0; + for (j=1; j= 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 @@ -1861,733 +3791,1051 @@ mDNSlocal void BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **queryptr // 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; ih.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; ih.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); @@ -2605,15 +4853,15 @@ mDNSlocal const ResourceRecord *FindRRSet(const mDNS *const m, const ResourceRec // 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); @@ -2626,7 +4874,7 @@ mDNSlocal mDNSBool PacketRRConflict(const mDNS *const m, const ResourceRecord *c // 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); @@ -2634,48 +4882,60 @@ mDNSlocal void ResolveSimultaneousProbe(mDNS *const m, const DNSMessage *const q 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; // *** @@ -2683,36 +4943,102 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con // *** for (i=0; ih.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; } + } } // *** @@ -2720,11 +5046,10 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con // *** 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 @@ -2733,50 +5058,89 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con { // (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; ih.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; + } } } @@ -2792,42 +5156,52 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con // *** 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 + } } // *** @@ -2836,19 +5210,19 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con 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) { @@ -2859,41 +5233,109 @@ exit: 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); } } @@ -2901,101 +5343,126 @@ mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, // 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; } } } } @@ -3003,77 +5470,122 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // 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; @@ -3082,13 +5594,15 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNS msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]); - if (!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: @@ -3100,7 +5614,7 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNS } // *************************************************************************** -#if 0 +#if COMPILER_LIKES_PRAGMA_MARK #pragma mark - #pragma mark - #pragma mark - Searcher Functions @@ -3113,133 +5627,235 @@ mDNSlocal DNSQuestion *FindDuplicateQuestion(const mDNS *const m, const DNSQuest // 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; iDupSuppress[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; @@ -3248,116 +5864,176 @@ mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const R 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); @@ -3369,103 +6045,123 @@ mDNSexport void mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *que 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); } @@ -3473,169 +6169,269 @@ mDNSexport mStatus mDNS_Update(mDNS *const m, ResourceRecord *const rr, mDNSu32 // 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 @@ -3644,74 +6440,136 @@ mDNSlocal void mDNS_DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *se 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; iNumSubTypes; 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: @@ -3720,92 +6578,125 @@ mDNSlocal void ServiceCallback(mDNS *const m, ResourceRecord *const rr, mStatus // 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; iSubTypes[i].resrec.name; + mDNS_SetupResourceRecord(&sr->SubTypes[i], mDNSNULL, InterfaceID, kDNSType_PTR, kDefaultTTLforShared, kDNSRecordTypeShared, ServiceCallback, sr); + if (ConstructServiceName(&sr->SubTypes[i].resrec.name, &s, type, domain) == mDNSNULL) return(mStatus_BadParamErr); + 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; iSubTypes[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) @@ -3814,15 +6705,14 @@ mDNSexport mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet 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) @@ -3833,7 +6723,7 @@ mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordS 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); @@ -3843,14 +6733,18 @@ mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordS 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); @@ -3859,134 +6753,236 @@ mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordS // 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; iNumSubTypes; 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; irrcache_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; irrcache = 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); } } @@ -3995,10 +6991,10 @@ extern void mDNS_Close(mDNS *const m) // 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"); diff --git a/mDNSCore/mDNSClientAPI.h b/mDNSCore/mDNSClientAPI.h index c6ba060..975d559 100755 --- a/mDNSCore/mDNSClientAPI.h +++ b/mDNSCore/mDNSClientAPI.h @@ -1,5 +1,401 @@ -#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 + Traffic reduction: Eliminate synchronized QUs when a new service appears +1. Use m->RandomQueryDelay to impose a random delay in the range 0-500ms on queries + that already have at least one unique answer in the cache +2. For these queries, go straight to QM, skipping QU + +Revision 1.113 2003/08/21 19:31:58 cheshire +Cosmetic: Swap order of fields + +Revision 1.112 2003/08/21 19:27:36 cheshire + Traffic reduction: No need to announce record for longer than TTL + +Revision 1.111 2003/08/21 02:21:50 cheshire + Efficiency: Reduce repeated queries + +Revision 1.110 2003/08/20 23:39:31 cheshire + Review syslog messages, and remove as appropriate + +Revision 1.109 2003/08/19 22:24:10 cheshire +Comment change + +Revision 1.108 2003/08/19 22:20:00 cheshire + Don't use IPv6 on interfaces that have a routable IPv4 address configured +More minor refinements + +Revision 1.107 2003/08/19 06:48:25 cheshire + Guard against excessive record updates +Each record starts with 10 UpdateCredits. +Every update consumes one UpdateCredit. +UpdateCredits are replenished at a rate of one one per minute, up to a maximum of 10. +As the number of UpdateCredits declines, the number of announcements is similarly scaled back. +When fewer than 5 UpdateCredits remain, the first announcement is also delayed by an increasing amount. + +Revision 1.106 2003/08/19 04:49:28 cheshire + Interaction between v4, v6 and dual-stack hosts not working quite right +1. A dual-stack host should only suppress its own query if it sees the same query from other hosts on BOTH IPv4 and IPv6. +2. When we see the first v4 (or first v6) member of a group, we re-trigger questions and probes on that interface. +3. When we see the last v4 (or v6) member of a group go away, we revalidate all the records received on that interface. + +Revision 1.105 2003/08/19 02:33:37 cheshire +Update comments + +Revision 1.104 2003/08/19 02:31:11 cheshire + mDNSResponder overenthusiastic with final expiration queries +Final expiration queries now only mark the question for sending on the particular interface +pertaining to the record that's expiring. + +Revision 1.103 2003/08/18 19:05:44 cheshire + UpdateRecord not working right +Added "newrdlength" field to hold new length of updated rdata + +Revision 1.102 2003/08/16 03:39:00 cheshire + InterfaceID -1 indicates "local only" + +Revision 1.101 2003/08/15 20:16:02 cheshire + mDNSResponder takes too much RPRVT +We want to avoid touching the rdata pages, so we don't page them in. +1. RDLength was stored with the rdata, which meant touching the page just to find the length. + Moved this from the RData to the ResourceRecord object. +2. To avoid unnecessarily touching the rdata just to compare it, + compute a hash of the rdata and store the hash in the ResourceRecord object. + +Revision 1.100 2003/08/14 19:29:04 cheshire + Include cache records in SIGINFO output +Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSClientAPI.h so daemon.c can use them + +Revision 1.99 2003/08/14 02:17:05 cheshire + Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord + +Revision 1.98 2003/08/12 19:56:23 cheshire +Update to APSL 2.0 + +Revision 1.97 2003/08/12 14:59:27 cheshire + Rate-limiting blocks some legitimate responses +When setting LastMCTime also record LastMCInterface. When checking LastMCTime to determine +whether to suppress the response, also check LastMCInterface to see if it matches. + +Revision 1.96 2003/08/12 13:57:04 cheshire + Improve cache performance +Changed the number of hash table slots from 37 to 499 +Revision 1.95 2003/08/09 00:55:02 cheshire + mDNSResponder is taking 20-30% of the CPU +Don't scan the whole cache after every packet. + +Revision 1.94 2003/08/09 00:35:29 cheshire + +Revision 1.93 2003/08/08 18:55:48 cheshire + Guard against time going backwards + +Revision 1.92 2003/08/08 18:36:04 cheshire + Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug + +Revision 1.91 2003/08/06 21:33:39 cheshire +Fix compiler warnings on PocketPC 2003 (Windows CE) + +Revision 1.90 2003/08/06 20:30:17 cheshire +Add structure definition for rdataMX (not currently used, but good to have it for completeness) + +Revision 1.89 2003/08/06 18:58:19 cheshire +Update comments + +Revision 1.88 2003/07/24 23:45:44 cheshire +To eliminate compiler warnings, changed definition of mDNSBool from +"unsigned char" to "int", since "int" is in fact truly the type that C uses +for the result of comparison operators (a: Feature: New Rendezvous APIs (#7875) (mDNSResponder component) +Added error type for incompatibility between daemon and client versions + +Revision 1.85 2003/07/19 03:23:13 cheshire + mDNSResponder needs to receive and cache larger records + +Revision 1.84 2003/07/18 23:52:12 cheshire +To improve consistency of field naming, global search-and-replace: +NextProbeTime -> NextScheduledProbe +NextResponseTime -> NextScheduledResponse + +Revision 1.83 2003/07/18 00:29:59 cheshire + Remove mDNSResponder version from packet header and use HINFO record instead + +Revision 1.82 2003/07/17 17:35:04 cheshire + Rate-limit responses, to guard against packet flooding + +Revision 1.81 2003/07/16 05:01:36 cheshire +Add fields 'LargeAnswers' and 'ExpectUnicastResponse' in preparation for + Need to implement "unicast response" request, using top bit of qclass + +Revision 1.80 2003/07/15 01:55:12 cheshire + Need to implement service registration with subtypes + +Revision 1.79 2003/07/13 02:28:00 cheshire + SendResponses didn't all its responses +Delete all references to RRInterfaceActive -- it's now superfluous + +Revision 1.78 2003/07/13 01:47:53 cheshire +Fix one error and one warning in the Windows build + +Revision 1.77 2003/07/11 01:32:38 cheshire +Syntactic cleanup (no change to funcationality): Now that we only have one host name, +rename field "hostname1" to "hostname", and field "RR_A1" to "RR_A". + +Revision 1.76 2003/07/11 01:28:00 cheshire + No more local.arpa + +Revision 1.75 2003/07/02 21:19:45 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.74 2003/07/02 02:41:23 cheshire + mDNSResponder needs to start with a smaller cache and then grow it as needed + +Revision 1.73 2003/06/10 04:24:39 cheshire + React when we observe other people query unsuccessfully for a record that's in our cache +Some additional refinements: +Don't try to do this for unicast-response queries +better tracking of Qs and KAs in multi-packet KA lists + +Revision 1.72 2003/06/10 01:46:27 cheshire +Add better comments explaining how these data structures are intended to be used from the client layer + +Revision 1.71 2003/06/07 06:45:05 cheshire + No need for multiple machines to all be sending the same queries + +Revision 1.70 2003/06/07 04:50:53 cheshire + React when we observe other people query unsuccessfully for a record that's in our cache + +Revision 1.69 2003/06/07 04:22:17 cheshire +Add MsgBuffer for error log and debug messages + +Revision 1.68 2003/06/07 01:46:38 cheshire + When query produces zero results, call mDNS_Reconfirm() on any antecedent records + +Revision 1.67 2003/06/07 01:22:14 cheshire + mDNSResponder needs an mDNS_Reconfirm() function + +Revision 1.66 2003/06/07 00:59:43 cheshire + Need some randomness to spread queries on the network + +Revision 1.65 2003/06/06 21:41:11 cheshire +For consistency, mDNS_StopQuery() should return an mStatus result, just like all the other mDNSCore routines + +Revision 1.64 2003/06/06 21:38:55 cheshire +Renamed 'NewData' as 'FreshData' (The data may not be new data, just a refresh of data that we +already had in our cache. This refreshes our TTL on the data, but the data itself stays the same.) + +Revision 1.63 2003/06/06 17:20:14 cheshire +For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass +(Global search-and-replace; no functional change to code execution.) + +Revision 1.62 2003/06/04 01:25:33 cheshire + Cannot perform multi-packet known-answer suppression messages +Display time interval between first and subsequent queries + +Revision 1.61 2003/06/03 05:02:16 cheshire + Duplicate registrations not handled as efficiently as they should be + +Revision 1.60 2003/05/31 00:09:49 cheshire + Add ability to discover what services are on a network + +Revision 1.59 2003/05/29 06:11:35 cheshire +: Report if there appear to be too many "Resolve" callbacks + +Revision 1.58 2003/05/29 05:48:06 cheshire +Minor fix for when generating printf warnings: mDNS_snprintf arguments are now 3,4 + +Revision 1.57 2003/05/26 03:21:27 cheshire +Tidy up address structure naming: +mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) +mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 +mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 + +Revision 1.56 2003/05/26 03:01:27 cheshire + sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead + +Revision 1.55 2003/05/26 00:47:30 cheshire +Comment clarification + +Revision 1.54 2003/05/24 16:39:48 cheshire + SendResponses also needs to handle multihoming better + +Revision 1.53 2003/05/23 02:15:37 cheshire +Fixed misleading use of the term "duplicate suppression" where it should have +said "known answer suppression". (Duplicate answer suppression is something +different, and duplicate question suppression is yet another thing, so the use +of the completely vague term "duplicate suppression" was particularly bad.) + +Revision 1.52 2003/05/22 02:29:22 cheshire + SendQueries needs to handle multihoming better +Complete rewrite of SendQueries. Works much better now :-) + +Revision 1.51 2003/05/21 20:14:55 cheshire +Fix comments and warnings + +Revision 1.50 2003/05/14 07:08:36 cheshire + mDNSResponder should be smarter about reconfigurations +Previously, when there was any network configuration change, mDNSResponder +would tear down the entire list of active interfaces and start again. +That was very disruptive, and caused the entire cache to be flushed, +and caused lots of extra network traffic. Now it only removes interfaces +that have really gone, and only adds new ones that weren't there before. + +Revision 1.49 2003/05/07 01:49:36 cheshire +Remove "const" in ConstructServiceName prototype + +Revision 1.48 2003/05/07 00:18:44 cheshire +Fix typo: "kDNSQClass_Mask" should be "kDNSClass_Mask" + +Revision 1.47 2003/05/06 00:00:46 cheshire + Rationalize naming of domainname manipulation functions + +Revision 1.46 2003/04/30 20:39:09 cheshire +Add comment + +Revision 1.45 2003/04/29 00:40:50 cheshire +Fix compiler warnings + +Revision 1.44 2003/04/26 02:41:56 cheshire + Change timenow from a local variable to a structure member + +Revision 1.43 2003/04/25 01:45:56 cheshire + mDNS_RegisterNoSuchService needs to include a host name + +Revision 1.42 2003/04/15 20:58:31 jgraessl + +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 + mDNSResponder sometimes suffers false self-conflicts when it sees its own packets +Solution: Major cleanup of packet timing and conflict handling rules + +Revision 1.39 2003/03/27 03:30:55 cheshire + Name conflicts not handled properly, resulting in memory corruption, and eventual crash +Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback +Fixes: +1. Make mDNS_DeregisterInterface() safe to call from a callback +2. Make HostNameCallback() use mDNS_DeadvertiseInterface() instead + (it never really needed to deregister the interface at all) + +Revision 1.38 2003/03/15 04:40:36 cheshire +Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID" + +Revision 1.37 2003/03/14 21:34:11 cheshire + Can't setup and print to Lexmark PS printers via Airport Extreme +Increase size of cache rdata from 512 to 768 + +Revision 1.36 2003/03/05 03:38:35 cheshire +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 is required for for va_list support for the mDNS_vsnprintf declaration #include "mDNSDebug.h" #ifdef __cplusplus @@ -10,25 +406,32 @@ // 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 @@ -49,7 +452,7 @@ 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 @@ -63,7 +466,7 @@ typedef enum // From RFC 1035 // 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; @@ -71,15 +474,39 @@ typedef unsigned short mDNSu16; 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 }; @@ -104,16 +531,22 @@ enum 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 // *************************************************************************** @@ -121,176 +554,292 @@ 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 }; @@ -299,48 +848,85 @@ struct ServiceRecordSet_struct #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; }; // *************************************************************************** @@ -350,44 +936,82 @@ struct ServiceInfoQuery_struct 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; }; // *************************************************************************** @@ -395,15 +1019,20 @@ struct mDNS_struct #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 @@ -412,17 +1041,17 @@ extern const mDNSIPAddr AllDNSAdminGroup; // 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 // @@ -435,21 +1064,43 @@ extern const mDNSIPAddr AllDNSAdminGroup; // 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, @@ -467,28 +1118,31 @@ extern void mDNS_StopQuery (mDNS *const m, DNSQuestion *const question); // 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 @@ -499,9 +1153,9 @@ 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 // *************************************************************************** @@ -514,30 +1168,71 @@ extern mStatus mDNS_AdvertiseDomains(mDNS *const m, ResourceRecord *rr, mDNSu8 D // 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 diff --git a/mDNSCore/mDNSDebug.h b/mDNSCore/mDNSDebug.h index f6e6e1e..65b6daf 100755 --- a/mDNSCore/mDNSDebug.h +++ b/mDNSCore/mDNSDebug.h @@ -1,39 +1,112 @@ -// 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 + Update copyright notices, etc., in source code comments + +Revision 1.12 2003/05/26 03:01:27 cheshire + 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 diff --git a/mDNSCore/mDNSPlatformEnvironment.h b/mDNSCore/mDNSPlatformEnvironment.h deleted file mode 100755 index 1a349c7..0000000 --- a/mDNSCore/mDNSPlatformEnvironment.h +++ /dev/null @@ -1,103 +0,0 @@ -// 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 -#include - -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 -#include -#include -#include - -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 diff --git a/mDNSCore/mDNSPlatformFunctions.h b/mDNSCore/mDNSPlatformFunctions.h index e058894..beef207 100755 --- a/mDNSCore/mDNSPlatformFunctions.h +++ b/mDNSCore/mDNSPlatformFunctions.h @@ -1,3 +1,85 @@ +/* + * 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 + mDNSResponder divide by zero in mDNSPlatformTimeNow() + +Revision 1.21 2003/08/15 20:16:57 cheshire +Update comment for 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 + 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 + Update copyright notices, etc., in source code comments + +Revision 1.15 2003/05/23 22:39:45 cheshire + 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 @@ -5,8 +87,6 @@ // 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 @@ -28,42 +108,78 @@ typedef struct // 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 diff --git a/mDNSCore/mDNSsprintf.c b/mDNSCore/mDNSsprintf.c deleted file mode 100755 index beaab51..0000000 --- a/mDNSCore/mDNSsprintf.c +++ /dev/null @@ -1,227 +0,0 @@ -#include -#include // 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 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 // For printf() +#include // For strlen() etc. + +#include // For WaitNextEvent() +#include // 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); + } diff --git a/mDNSMacOS9/Mac OS Test Searcher.c b/mDNSMacOS9/Mac OS Test Searcher.c new file mode 100644 index 0000000..d55dcd5 --- /dev/null +++ b/mDNSMacOS9/Mac OS Test Searcher.c @@ -0,0 +1,255 @@ +/* + * 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 + 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 // For printf() +#include // For WaitNextEvent() +#include // 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); + } diff --git a/mDNSMacOS9/README.txt b/mDNSMacOS9/README.txt new file mode 100644 index 0000000..4d13761 --- /dev/null +++ b/mDNSMacOS9/README.txt @@ -0,0 +1,10 @@ +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 diff --git a/mDNSMacOS9/mDNS.mcp b/mDNSMacOS9/mDNS.mcp new file mode 100644 index 0000000..dc4932a Binary files /dev/null and b/mDNSMacOS9/mDNS.mcp differ diff --git a/mDNSMacOS9/mDNSMacOS9.c b/mDNSMacOS9/mDNSMacOS9.c new file mode 100644 index 0000000..cdb49b3 --- /dev/null +++ b/mDNSMacOS9/mDNSMacOS9.c @@ -0,0 +1,544 @@ +/* + * 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 + mDNSResponder divide by zero in mDNSPlatformTimeNow() + +Revision 1.18 2003/08/12 19:56:24 cheshire +Update to APSL 2.0 + + */ + +#include // For LMGetCurApName() +#include // For smSystemScript +#include // For ConvertFromPStringToUnicode() + +#include +#include // 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; diff --git a/mDNSMacOS9/mDNSMacOS9.h b/mDNSMacOS9/mDNSMacOS9.h new file mode 100755 index 0000000..d707f9f --- /dev/null +++ b/mDNSMacOS9/mDNSMacOS9.h @@ -0,0 +1,66 @@ +/* + * 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 +#include +#include + +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; + }; diff --git a/mDNSMacOS9/mDNSPrefixCarbon.h b/mDNSMacOS9/mDNSPrefixCarbon.h new file mode 100644 index 0000000..95f1a48 --- /dev/null +++ b/mDNSMacOS9/mDNSPrefixCarbon.h @@ -0,0 +1,43 @@ +/* + * 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 diff --git a/mDNSMacOS9/mDNSPrefixCarbonDebug.h b/mDNSMacOS9/mDNSPrefixCarbonDebug.h new file mode 100644 index 0000000..2c31a97 --- /dev/null +++ b/mDNSMacOS9/mDNSPrefixCarbonDebug.h @@ -0,0 +1,43 @@ +/* + * 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 diff --git a/mDNSMacOS9/mDNSPrefixClassic.h b/mDNSMacOS9/mDNSPrefixClassic.h new file mode 100644 index 0000000..d275361 --- /dev/null +++ b/mDNSMacOS9/mDNSPrefixClassic.h @@ -0,0 +1,40 @@ +/* + * 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 diff --git a/mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.h b/mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.h new file mode 100755 index 0000000..f58c914 --- /dev/null +++ b/mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.h @@ -0,0 +1,83 @@ +/* + * 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 +#import + +#include + +@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 diff --git a/mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.m b/mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.m new file mode 100755 index 0000000..1dbe2a4 --- /dev/null +++ b/mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.m @@ -0,0 +1,541 @@ +/* + * 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 diff --git a/mDNSMacOSX/Applications/DNSServiceBrowser/DNS Service Browser.pbproj/project.pbxproj b/mDNSMacOSX/Applications/DNSServiceBrowser/DNS Service Browser.pbproj/project.pbxproj new file mode 100644 index 0000000..1cb8edd --- /dev/null +++ b/mDNSMacOSX/Applications/DNSServiceBrowser/DNS Service Browser.pbproj/project.pbxproj @@ -0,0 +1,377 @@ +// !$*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 = " + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + DNS Service Browser + CFBundleIconFile + + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 0.1 + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + +"; + 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; +} diff --git a/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/InfoPlist.strings b/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/InfoPlist.strings new file mode 100644 index 0000000..08c0f0c Binary files /dev/null and b/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/InfoPlist.strings differ diff --git a/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/classes.nib b/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/classes.nib new file mode 100644 index 0000000..2668d5d --- /dev/null +++ b/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/classes.nib @@ -0,0 +1,37 @@ +{ + 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 diff --git a/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/info.nib b/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/info.nib new file mode 100644 index 0000000..e31cf4c --- /dev/null +++ b/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/info.nib @@ -0,0 +1,22 @@ + + + + + IBDocumentLocation + 14 102 356 240 0 0 1152 746 + IBEditorPositions + + 29 + 22 474 271 44 0 0 1152 746 + + IBFramework Version + 273.0 + IBOpenObjects + + 220 + 201 + + IBSystem Version + 6C35 + + diff --git a/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/objects.nib b/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/objects.nib new file mode 100644 index 0000000..b330188 Binary files /dev/null and b/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/objects.nib differ diff --git a/mDNSMacOSX/Applications/DNSServiceBrowser/main.m b/mDNSMacOSX/Applications/DNSServiceBrowser/main.m new file mode 100644 index 0000000..c340509 --- /dev/null +++ b/mDNSMacOSX/Applications/DNSServiceBrowser/main.m @@ -0,0 +1,36 @@ +/* + * 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 + +int main(int argc, const char *argv[]) +{ + return NSApplicationMain(argc, argv); +} diff --git a/mDNSMacOSX/Applications/DNSServiceRegistration/DNS Service Registration.pbproj/project.pbxproj b/mDNSMacOSX/Applications/DNSServiceRegistration/DNS Service Registration.pbproj/project.pbxproj new file mode 100644 index 0000000..9487a76 --- /dev/null +++ b/mDNSMacOSX/Applications/DNSServiceRegistration/DNS Service Registration.pbproj/project.pbxproj @@ -0,0 +1,377 @@ +// !$*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 = " + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + DNS Service Registration + CFBundleIconFile + + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 0.1 + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + +"; + 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; +} diff --git a/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/InfoPlist.strings b/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/InfoPlist.strings new file mode 100644 index 0000000..2aadfdc Binary files /dev/null and b/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/InfoPlist.strings differ diff --git a/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/classes.nib b/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/classes.nib new file mode 100644 index 0000000..46f466c --- /dev/null +++ b/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/classes.nib @@ -0,0 +1,30 @@ +{ + IBClasses = ( + {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, + { + ACTIONS = { + addNewService = id; + registerService = id; + removeSelected = id; + unregisterService = id; + }; + CLASS = RegistrationController; + LANGUAGE = ObjC; + OUTLETS = { + domainColumn = NSTableColumn; + nameColumn = NSTableColumn; + portColumn = NSTableColumn; + serviceDisplayTable = NSTableView; + serviceDomainField = NSTextField; + serviceNameField = NSTextField; + servicePortField = NSTextField; + serviceTextField = NSTextField; + serviceTypeField = NSTextField; + textColumn = NSTableColumn; + typeColumn = NSTableColumn; + }; + SUPERCLASS = NSObject; + } + ); + IBVersion = 1; +} \ No newline at end of file diff --git a/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/info.nib b/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/info.nib new file mode 100644 index 0000000..9d2eb74 --- /dev/null +++ b/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/info.nib @@ -0,0 +1,22 @@ + + + + + IBDocumentLocation + 32 111 356 240 0 0 1152 746 + IBEditorPositions + + 29 + 103 609 252 44 0 0 1152 746 + + IBFramework Version + 273.0 + IBOpenObjects + + 243 + 21 + + IBSystem Version + 6C30 + + diff --git a/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/objects.nib b/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/objects.nib new file mode 100644 index 0000000..705e77d Binary files /dev/null and b/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/objects.nib differ diff --git a/mDNSMacOSX/Applications/DNSServiceRegistration/RegistrationController.h b/mDNSMacOSX/Applications/DNSServiceRegistration/RegistrationController.h new file mode 100644 index 0000000..ca53bf2 --- /dev/null +++ b/mDNSMacOSX/Applications/DNSServiceRegistration/RegistrationController.h @@ -0,0 +1,66 @@ +/* + * 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 + +@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 diff --git a/mDNSMacOSX/Applications/DNSServiceRegistration/RegistrationController.m b/mDNSMacOSX/Applications/DNSServiceRegistration/RegistrationController.m new file mode 100644 index 0000000..6255179 --- /dev/null +++ b/mDNSMacOSX/Applications/DNSServiceRegistration/RegistrationController.m @@ -0,0 +1,247 @@ +/* + * 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 + +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 diff --git a/mDNSMacOSX/Applications/DNSServiceRegistration/main.m b/mDNSMacOSX/Applications/DNSServiceRegistration/main.m new file mode 100644 index 0000000..c340509 --- /dev/null +++ b/mDNSMacOSX/Applications/DNSServiceRegistration/main.m @@ -0,0 +1,36 @@ +/* + * 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 + +int main(int argc, const char *argv[]) +{ + return NSApplicationMain(argc, argv); +} diff --git a/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.h b/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.h new file mode 100644 index 0000000..7bb3e90 --- /dev/null +++ b/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.h @@ -0,0 +1,40 @@ +/* + * 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 + + +@interface HAAutomounter : NSObject { + + NSNetServiceBrowser *browser; + NSNetService *resolver; +} + +@end diff --git a/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.m b/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.m new file mode 100644 index 0000000..4aae5ed --- /dev/null +++ b/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.m @@ -0,0 +1,102 @@ +/* + * 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 + +#include +#include "arpa/inet.h" +#include + +@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 diff --git a/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.pbproj/project.pbxproj b/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.pbproj/project.pbxproj new file mode 100644 index 0000000..7d654f8 --- /dev/null +++ b/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.pbproj/project.pbxproj @@ -0,0 +1,296 @@ +// !$*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; +} diff --git a/mDNSMacOSX/Applications/HAAutomounter/main.m b/mDNSMacOSX/Applications/HAAutomounter/main.m new file mode 100644 index 0000000..6ad4471 --- /dev/null +++ b/mDNSMacOSX/Applications/HAAutomounter/main.m @@ -0,0 +1,44 @@ +/* + * 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 + +#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; +} diff --git a/mDNSMacOSX/CFSocket.c b/mDNSMacOSX/CFSocket.c new file mode 100644 index 0000000..519a899 --- /dev/null +++ b/mDNSMacOSX/CFSocket.c @@ -0,0 +1,1464 @@ +/* + * 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 + Don't log "sendto failed" errors during the first two minutes of startup + +Revision 1.114 2003/08/27 02:55:13 cheshire +: Bug: Don't report mDNSPlatformSendUDP sendto errno 64 (Host is down) + +Revision 1.113 2003/08/19 22:20:00 cheshire + Don't use IPv6 on interfaces that have a routable IPv4 address configured +More minor refinements + +Revision 1.112 2003/08/19 03:04:43 cheshire + Don't use IPv6 on interfaces that have a routable IPv4 address configured + +Revision 1.111 2003/08/18 22:53:37 cheshire + mDNSResponder divide by zero in mDNSPlatformTimeNow() + +Revision 1.110 2003/08/16 03:39:00 cheshire + InterfaceID -1 indicates "local only" + +Revision 1.109 2003/08/15 02:19:49 cheshire + syslog messages: myCFSocketCallBack recvfrom skt 6 error -1 errno 35 +Also limit number of messages to at most 100 + +Revision 1.108 2003/08/12 22:24:52 cheshire + syslog messages: myCFSocketCallBack recvfrom skt 6 error -1 errno 35 +This message indicates a kernel bug, but still we don't want to flood syslog. +Do a sleep(1) after writing this log message, to limit the rate. + +Revision 1.107 2003/08/12 19:56:25 cheshire +Update to APSL 2.0 + +Revision 1.106 2003/08/12 13:48:32 cheshire +Add comment explaining clockdivisor calculation + +Revision 1.105 2003/08/12 13:44:14 cheshire + mDNSResponder *VERY* unhappy if time goes backwards +Use mach_absolute_time() (which is guaranteed to always go forwards, resetting only on reboot) +instead of gettimeofday() (which can jump back if the user manually changes their time/date) + +Revision 1.104 2003/08/12 13:12:07 cheshire +Textual search/replace: Indicate local functions using "mDNSlocal" instead of "static" + +Revision 1.103 2003/08/08 18:36:04 cheshire + Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug + +Revision 1.102 2003/08/06 00:14:52 cheshire + Need to check IP TTL on responses +Also add corresponding checks in the IPv6 code path + +Revision 1.101 2003/08/05 22:20:16 cheshire + Need to check IP TTL on responses + +Revision 1.100 2003/08/05 21:18:50 cheshire + mDNSResponder should ignore 6to4 +Only use interfaces that are marked as multicast-capable (IFF_MULTICAST) + +Revision 1.99 2003/08/05 20:13:52 cheshire + mDNSResponder using IPv6 interfaces before they are ready +Ignore interfaces with the IN6_IFF_NOTREADY flag set + +Revision 1.98 2003/07/20 03:38:51 ksekar +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 + Remove mDNSResponder version from packet header and use HINFO record instead + +Revision 1.95 2003/07/12 03:15:20 cheshire + After SCDynamicStore notification, mDNSResponder updates +m->hostlabel even if user hasn't actually actually changed their dot-local hostname + +Revision 1.94 2003/07/03 00:51:54 cheshire + When select() and recvmgs() disagree, get more info from kernel about the socket state + +Revision 1.93 2003/07/03 00:09:14 cheshire + New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call +Additional refinement suggested by Josh: Use info->scope_id instead of if_nametoindex(info->ifa_name); + +Revision 1.92 2003/07/02 21:19:51 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.91 2003/06/24 01:53:51 cheshire +Minor update to comments + +Revision 1.90 2003/06/24 01:51:47 cheshire + Oops: Double-dispose of sockets +Don't need to close sockets: CFSocketInvalidate() does that for us + +Revision 1.89 2003/06/21 18:12:47 cheshire + Rendezvous cannot handle interfaces whose total name is >3 chars +One-line change: should say "IF_NAMESIZE", not sizeof(ifname) + +Revision 1.88 2003/06/12 23:38:37 cheshire + mDNSResponder doesn't detect some configuration changes +Also check that scope_id matches before concluding that two interfaces are the same + +Revision 1.87 2003/06/10 01:14:11 cheshire + New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call + +Revision 1.86 2003/05/28 02:41:52 cheshire + Time to remove Mac OS 9 UDP Port 53 legacy support + +Revision 1.85 2003/05/28 02:39:47 cheshire +Minor change to debugging messages + +Revision 1.84 2003/05/27 22:29:40 cheshire +Remove out-dated comment + +Revision 1.83 2003/05/26 03:21:29 cheshire +Tidy up address structure naming: +mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) +mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 +mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 + +Revision 1.82 2003/05/26 03:01:27 cheshire + sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead + +Revision 1.81 2003/05/24 02:06:42 cheshire + IPv6 Multicast Loopback doesn't work +Tried setting IPV6_MULTICAST_LOOP; it doesn't help. +However, it is probably wise to have the code explicitly set this socket +option anyway, in case the default changes in later versions of Unix. + +Revision 1.80 2003/05/24 02:02:24 cheshire + if_indextoname consumes a lot of CPU +Fix error in myIfIndexToName; was returning prematurely + +Revision 1.79 2003/05/23 23:07:44 cheshire + Must not write to stderr when running as daemon + +Revision 1.78 2003/05/23 01:19:04 cheshire + mDNSResponder needs to signal type of service to AirPort +Mark packets as high-throughput/low-delay (i.e. lowest reliability) to get maximum 802.11 multicast rate + +Revision 1.77 2003/05/23 01:12:05 cheshire +Minor code tidying + +Revision 1.76 2003/05/22 01:26:01 cheshire +Tidy up log messages + +Revision 1.75 2003/05/22 00:07:09 cheshire + myCFSocketCallBack recvfrom(5) error 1, errno 35 +Extra logging to determine whether there is a bug in CFSocket + +Revision 1.74 2003/05/21 20:20:12 cheshire +Fix warnings (mainly printf format string warnings, like using "%d" where +it should say "%lu", etc.) and improve error logging (use strerror() +to include textual error message as well as numeric error in log messages). + +Revision 1.73 2003/05/21 17:56:29 ksekar +Bug #: : mDNSResponder doesn't watch for IPv6 address changes + +Revision 1.72 2003/05/14 18:48:41 cheshire + mDNSResponder should be smarter about reconfigurations +More minor refinements: +CFSocket.c needs to do *all* its mDNS_DeregisterInterface calls before freeing memory +mDNS_DeregisterInterface revalidates cache record when *any* representative of an interface goes away + +Revision 1.71 2003/05/14 07:08:37 cheshire + mDNSResponder should be smarter about reconfigurations +Previously, when there was any network configuration change, mDNSResponder +would tear down the entire list of active interfaces and start again. +That was very disruptive, and caused the entire cache to be flushed, +and caused lots of extra network traffic. Now it only removes interfaces +that have really gone, and only adds new ones that weren't there before. + +Revision 1.70 2003/05/07 18:30:24 cheshire +Fix signed/unsigned comparison warning + +Revision 1.69 2003/05/06 20:14:44 cheshire +Change "tp" to "tv" + +Revision 1.68 2003/05/06 00:00:49 cheshire + Rationalize naming of domainname manipulation functions + +Revision 1.67 2003/04/29 00:43:44 cheshire +Fix compiler warnings + +Revision 1.66 2003/04/26 02:41:58 cheshire + Change timenow from a local variable to a structure member + +Revision 1.65 2003/04/26 02:34:01 cheshire +Add missing mDNSexport + +Revision 1.64 2003/04/15 16:48:06 jgraessl +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 + 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 +#include // For select() and close() +#include // For va_list support +#include +#include +#include +#include +#include +#include +#include +#include + +#include // For IP_RECVTTL +#ifndef IP_RECVTTL +#define IP_RECVTTL 24 /* bool; receive reception TTL w/dgram */ +#endif + +#include // For n_long, required by below +#include // For IPTOS_LOWDELAY etc. +#include // For IN6_IFF_NOTREADY etc. + +// Code contributed by Dave Heller: +// Define RUN_ON_PUMA_WITHOUT_IFADDRS to compile code that will +// work on Mac OS X 10.1, which does not have the getifaddrs call. +#define RUN_ON_PUMA_WITHOUT_IFADDRS 0 +#if RUN_ON_PUMA_WITHOUT_IFADDRS +#include "CFSocketPuma.c" +#else +#include +#endif + +#include +#include +#include + +// *************************************************************************** +// 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 ) + // 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); } diff --git a/mDNSMacOSX/CFSocketPuma.c b/mDNSMacOSX/CFSocketPuma.c new file mode 100644 index 0000000..e0cf87c --- /dev/null +++ b/mDNSMacOSX/CFSocketPuma.c @@ -0,0 +1,238 @@ +/* + * 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 + Update copyright notices, etc., in source code comments + +Revision 1.2 2002/09/21 20:44:51 zarzycki +Added APSL info + +Revision 1.1 2002/09/17 01:36:23 cheshire +Move Puma support to CFSocketPuma.c + + */ + +#include +#include +#define ifaddrs ifa_info +#ifndef ifa_broadaddr +#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */ +#endif +#include + +/* Our own header for the programs that need interface configuration info. + Include this file, instead of "unp.h". */ + +#define IFA_NAME 16 /* same as IFNAMSIZ in */ +#define IFA_HADDR 8 /* allow for 64-bit EUI-64 in future */ + +struct ifa_info { + char ifa_name[IFA_NAME]; /* interface name, null terminated */ + u_char ifa_haddr[IFA_HADDR]; /* hardware address */ + u_short ifa_hlen; /* #bytes in hardware address: 0, 6, 8 */ + short ifa_flags; /* IFF_xxx constants from */ + short ifa_myflags; /* our own IFI_xxx flags */ + struct sockaddr *ifa_addr; /* primary address */ + struct sockaddr *ifa_brdaddr;/* broadcast address */ + struct sockaddr *ifa_dstaddr;/* destination address */ + struct ifa_info *ifa_next; /* next of these structures */ +}; + +#define IFI_ALIAS 1 /* ifa_addr is an alias */ + + /* function prototypes */ +struct ifa_info *get_ifa_info(int, int); +struct ifa_info *Get_ifa_info(int, int); +void free_ifa_info(struct ifa_info *); + +#define HAVE_SOCKADDR_SA_LEN 1 + +struct ifa_info * +get_ifa_info(int family, int doaliases) +{ + struct ifa_info *ifi, *ifihead, **ifipnext; + int sockfd, len, lastlen, flags, myflags; + char *ptr, *buf, lastname[IFNAMSIZ], *cptr; + struct ifconf ifc; + struct ifreq *ifr, ifrcopy; + struct sockaddr_in *sinptr; + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + + lastlen = 0; + len = 100 * sizeof(struct ifreq); /* initial buffer size guess */ + for ( ; ; ) { + buf = (char *) malloc(len); + ifc.ifc_len = len; + ifc.ifc_buf = buf; + if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) { + if (errno != EINVAL || lastlen != 0) + debugf("ioctl error"); + } else { + if (ifc.ifc_len == lastlen) + break; /* success, len has not changed */ + lastlen = ifc.ifc_len; + } + len += 10 * sizeof(struct ifreq); /* increment */ + free(buf); + } + ifihead = NULL; + ifipnext = &ifihead; + lastname[0] = 0; +/* end get_ifa_info1 */ + +/* include get_ifa_info2 */ + for (ptr = buf; ptr < buf + ifc.ifc_len; ) { + ifr = (struct ifreq *) ptr; + +#ifdef HAVE_SOCKADDR_SA_LEN + len = MAX(sizeof(struct sockaddr), ifr->ifr_addr.sa_len); +#else + switch (ifr->ifr_addr.sa_family) { +#ifdef IPV6 + case AF_INET6: + len = sizeof(struct sockaddr_in6); + break; +#endif + case AF_INET: + default: + len = sizeof(struct sockaddr); + break; + } +#endif /* HAVE_SOCKADDR_SA_LEN */ + ptr += sizeof(ifr->ifr_name) + len; /* for next one in buffer */ + + if (ifr->ifr_addr.sa_family != family) + continue; /* ignore if not desired address family */ + + myflags = 0; + if ( (cptr = strchr(ifr->ifr_name, ':')) != NULL) + *cptr = 0; /* replace colon will null */ + if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) { + if (doaliases == 0) + continue; /* already processed this interface */ + myflags = IFI_ALIAS; + } + memcpy(lastname, ifr->ifr_name, IFNAMSIZ); + + ifrcopy = *ifr; + ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy); + flags = ifrcopy.ifr_flags; + if ((flags & IFF_UP) == 0) + continue; /* ignore if interface not up */ + + ifi = (struct ifa_info *) calloc(1, sizeof(struct ifa_info)); + *ifipnext = ifi; /* prev points to this new one */ + ifipnext = &ifi->ifa_next; /* pointer to next one goes here */ + + ifi->ifa_flags = flags; /* IFF_xxx values */ + ifi->ifa_myflags = myflags; /* IFI_xxx values */ + memcpy(ifi->ifa_name, ifr->ifr_name, IFA_NAME); + ifi->ifa_name[IFA_NAME-1] = '\0'; +/* end get_ifa_info2 */ +/* include get_ifa_info3 */ + switch (ifr->ifr_addr.sa_family) { + case AF_INET: + sinptr = (struct sockaddr_in *) &ifr->ifr_addr; + if (ifi->ifa_addr == NULL) { + ifi->ifa_addr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in)); + memcpy(ifi->ifa_addr, sinptr, sizeof(struct sockaddr_in)); + +#ifdef SIOCGIFBRDADDR + if (flags & IFF_BROADCAST) { + ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy); + sinptr = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr; + ifi->ifa_brdaddr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in)); + memcpy(ifi->ifa_brdaddr, sinptr, sizeof(struct sockaddr_in)); + } +#endif + +#ifdef SIOCGIFDSTADDR + if (flags & IFF_POINTOPOINT) { + ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy); + sinptr = (struct sockaddr_in *) &ifrcopy.ifr_dstaddr; + ifi->ifa_dstaddr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in)); + memcpy(ifi->ifa_dstaddr, sinptr, sizeof(struct sockaddr_in)); + } +#endif + } + break; + + default: + break; + } + } + free(buf); + return(ifihead); /* pointer to first structure in linked list */ +} +/* end get_ifa_info3 */ + +/* include free_ifa_info */ +mDNSlocal void freeifaddrs(struct ifa_info *ifihead) +{ + struct ifa_info *ifi, *ifinext; + + for (ifi = ifihead; ifi != NULL; ifi = ifinext) { + if (ifi->ifa_addr != NULL) + free(ifi->ifa_addr); + if (ifi->ifa_brdaddr != NULL) + free(ifi->ifa_brdaddr); + if (ifi->ifa_dstaddr != NULL) + free(ifi->ifa_dstaddr); + ifinext = ifi->ifa_next; /* can't fetch ifa_next after free() */ + free(ifi); /* the ifa_info{} itself */ + } +} +/* end free_ifa_info */ + +struct ifa_info * +Get_ifa_info(int family, int doaliases) +{ + struct ifa_info *ifi; + + if ( (ifi = get_ifa_info(family, doaliases)) == NULL) + debugf("get_ifa_info error"); + return(ifi); +} + +mDNSlocal int getifaddrs(struct ifa_info **ifalist) + { + *ifalist = get_ifa_info(PF_INET, false); + if( ifalist == nil ) + return -1; + else + return(0); + } diff --git a/mDNSMacOSX/DNSServiceDiscoveryDefines.h b/mDNSMacOSX/DNSServiceDiscoveryDefines.h new file mode 100644 index 0000000..b6d3a9a --- /dev/null +++ b/mDNSMacOSX/DNSServiceDiscoveryDefines.h @@ -0,0 +1,43 @@ +/* + * 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 + +#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 */ diff --git a/mDNSMacOSX/DNSServiceDiscoveryReply.defs b/mDNSMacOSX/DNSServiceDiscoveryReply.defs new file mode 100644 index 0000000..942fb6b --- /dev/null +++ b/mDNSMacOSX/DNSServiceDiscoveryReply.defs @@ -0,0 +1,66 @@ +/* + * 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 +#include + +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); diff --git a/mDNSMacOSX/DNSServiceDiscoveryRequest.defs b/mDNSMacOSX/DNSServiceDiscoveryRequest.defs new file mode 100644 index 0000000..eb400b1 --- /dev/null +++ b/mDNSMacOSX/DNSServiceDiscoveryRequest.defs @@ -0,0 +1,85 @@ +/* + * 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 +#include + +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); diff --git a/mDNSMacOSX/SampleUDSClient.c b/mDNSMacOSX/SampleUDSClient.c new file mode 100755 index 0000000..7838fc1 --- /dev/null +++ b/mDNSMacOSX/SampleUDSClient.c @@ -0,0 +1,411 @@ +/* + * 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 +#include +#include // include Mach API to ensure no conflicts exist +#include +#include +#include +#include +#include +#define BIND_8_COMPAT 1 +#include +// 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); + } + + + + + + + diff --git a/mDNSMacOSX/SamplemDNSClient.c b/mDNSMacOSX/SamplemDNSClient.c new file mode 100644 index 0000000..f905bd7 --- /dev/null +++ b/mDNSMacOSX/SamplemDNSClient.c @@ -0,0 +1,428 @@ +/* + * 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 , + * 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 + 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 + 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 + mDNSResponder needs to receive and cache larger records + +Revision 1.35 2003/07/11 01:57:18 cheshire +Add checkin history header + + */ + +#include +#define BIND_8_COMPAT +#include +#include +#include +#include +#include + +//************************************************************************************************************* +// 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> 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 (Browse for services instances)\n", argv[0]); + fprintf(stderr, "%s -L (Look up a service instance)\n", argv[0]); + fprintf(stderr, "%s -R [...] (Register a service)\n", argv[0]); + fprintf(stderr, "%s -A (Test Adding/Updating/Deleting a record)\n", argv[0]); + fprintf(stderr, "%s -U (Test updating a TXT record)\n", argv[0]); + fprintf(stderr, "%s -N (Test adding a large NULL record)\n", argv[0]); + fprintf(stderr, "%s -T (Test creating a large TXT record)\n", argv[0]); + fprintf(stderr, "%s -M (Test creating a registration with multiple TXT records)\n", argv[0]); + return 0; + } diff --git a/mDNSMacOSX/daemon.c b/mDNSMacOSX/daemon.c new file mode 100644 index 0000000..b6aa01e --- /dev/null +++ b/mDNSMacOSX/daemon.c @@ -0,0 +1,1701 @@ +/* + * 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 , + * 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 + Traffic reduction: Detect long-lived Resolve() calls, and report them in syslog + +Revision 1.133 2003/08/20 23:39:31 cheshire + 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 + SIGINFO dump should include resolves started by DNSServiceQueryRecord + +Revision 1.130 2003/08/16 03:39:01 cheshire + InterfaceID -1 indicates "local only" + +Revision 1.129 2003/08/15 20:16:03 cheshire + mDNSResponder takes too much RPRVT +We want to avoid touching the rdata pages, so we don't page them in. +1. RDLength was stored with the rdata, which meant touching the page just to find the length. + Moved this from the RData to the ResourceRecord object. +2. To avoid unnecessarily touching the rdata just to compare it, + compute a hash of the rdata and store the hash in the ResourceRecord object. + +Revision 1.128 2003/08/14 19:30:36 cheshire + Include list of cache records in SIGINFO output + +Revision 1.127 2003/08/14 02:18:21 cheshire + 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 + 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 + 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 + Remove mDNSResponder version from packet header and use HINFO record instead + +Revision 1.119 2003/07/17 19:08:58 cheshire + 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 + Need to implement service registration with subtypes + +Revision 1.116 2003/07/02 21:19:51 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.115 2003/07/02 02:41:24 cheshire + 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 + 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 #: : 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 + mDNSResponder binary compatibility +Make single binary that can run on both Jaguar and Panther. + +Revision 1.110 2003/06/10 01:14:11 cheshire + 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 + 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 + Temporarily include mDNSResponder version in packets + +Revision 1.103 2003/05/23 23:07:44 cheshire + 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 + 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 #: : 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 + 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 + Forgot to set "err = mStatus_BadParamErr" in a couple of places + +Revision 1.96 2003/05/07 22:10:46 cheshire + Add a few more error logging messages + +Revision 1.95 2003/05/07 19:20:17 cheshire + Add version number to mDNSResponder builds + +Revision 1.94 2003/05/07 00:28:18 cheshire + Need to make mDNSResponder more defensive against bad clients + +Revision 1.93 2003/05/06 00:00:49 cheshire + Rationalize naming of domainname manipulation functions + +Revision 1.92 2003/04/04 20:38:57 cheshire +Add $Log header + + */ + +#include +#include +#include +#include +#include +#include +#include + +#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 + +#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; iinfo->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 1 +mDNSexport const char mDNSResponderVersionString[] = "mDNSResponder-" STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; +#else +mDNSexport const char mDNSResponderVersionString[] = "mDNSResponder (Engineering Build) (" __DATE__ " " __TIME__ ")"; +#endif diff --git a/mDNSMacOSX/dns_sd.h b/mDNSMacOSX/dns_sd.h new file mode 100755 index 0000000..eaed335 --- /dev/null +++ b/mDNSMacOSX/dns_sd.h @@ -0,0 +1,1006 @@ +/* + * 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 +#include +#include +#include + + +/* 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. + * ... + * + * 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 ... + * (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 + diff --git a/mDNSMacOSX/dnssd_clientstub.c b/mDNSMacOSX/dnssd_clientstub.c new file mode 100755 index 0000000..7277eda --- /dev/null +++ b/mDNSMacOSX/dnssd_clientstub.c @@ -0,0 +1,998 @@ +/* + * 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; + } diff --git a/mDNSMacOSX/dnssd_ipc.c b/mDNSMacOSX/dnssd_ipc.c new file mode 100644 index 0000000..186f4d3 --- /dev/null +++ b/mDNSMacOSX/dnssd_ipc.c @@ -0,0 +1,137 @@ +/* + * 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; + } + + + + + + + + + diff --git a/mDNSMacOSX/dnssd_ipc.h b/mDNSMacOSX/dnssd_ipc.h new file mode 100644 index 0000000..2b7b323 --- /dev/null +++ b/mDNSMacOSX/dnssd_ipc.h @@ -0,0 +1,162 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +//#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 + + + + + + + + + + + diff --git a/mDNSMacOSX/mDNSMacOSX.h b/mDNSMacOSX/mDNSMacOSX.h new file mode 100644 index 0000000..b255cd0 --- /dev/null +++ b/mDNSMacOSX/mDNSMacOSX.h @@ -0,0 +1,192 @@ +/* + * 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 + 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 + 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 + 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 + Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug + +Revision 1.15 2003/08/05 00:32:28 cheshire + 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 + Remove mDNSResponder version from packet header and use HINFO record instead + +Revision 1.12 2003/07/12 03:15:20 cheshire + After SCDynamicStore notification, mDNSResponder updates +m->hostlabel even if user hasn't actually actually changed their dot-local hostname + +Revision 1.11 2003/07/02 21:19:51 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.10 2003/06/25 23:42:19 ksekar +Bug #: : 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 + New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call + +Revision 1.8 2003/05/14 07:08:37 cheshire + mDNSResponder should be smarter about reconfigurations +Previously, when there was any network configuration change, mDNSResponder +would tear down the entire list of active interfaces and start again. +That was very disruptive, and caused the entire cache to be flushed, +and caused lots of extra network traffic. Now it only removes interfaces +that have really gone, and only adds new ones that weren't there before. + +Revision 1.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 +#include +#include +#include + +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 diff --git a/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj b/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj new file mode 100644 index 0000000..2bd8d35 --- /dev/null +++ b/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj @@ -0,0 +1,808 @@ +// !$*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; +} diff --git a/mDNSMacOSX/uds_daemon.c b/mDNSMacOSX/uds_daemon.c new file mode 100644 index 0000000..c7df012 --- /dev/null +++ b/mDNSMacOSX/uds_daemon.c @@ -0,0 +1,2212 @@ +/* + * 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 #: : 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 + SIGINFO dump should include resolves started by DNSServiceQueryRecord + +Revision 1.20 2003/08/16 03:39:01 cheshire + InterfaceID -1 indicates "local only" + +Revision 1.19 2003/08/15 20:16:03 cheshire + mDNSResponder takes too much RPRVT +We want to avoid touching the rdata pages, so we don't page them in. +1. RDLength was stored with the rdata, which meant touching the page just to find the length. + Moved this from the RData to the ResourceRecord object. +2. To avoid unnecessarily touching the rdata just to compare it, + compute a hash of the rdata and store the hash in the ResourceRecord object. + +Revision 1.18 2003/08/15 00:38:00 ksekar +Bug #: : Bug: buffer overrun when reading long rdata from client + +Revision 1.17 2003/08/14 02:18:21 cheshire + Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord + +Revision 1.16 2003/08/13 23:58:52 ksekar +Bug #: : 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 #: : 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 +#include +#include +#include +#include + +// 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)); + } + + diff --git a/mDNSPosix/Client.c b/mDNSPosix/Client.c new file mode 100755 index 0000000..f3f9801 --- /dev/null +++ b/mDNSPosix/Client.c @@ -0,0 +1,254 @@ +/* + * 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 + 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 + 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 + 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 +#include +#include +#include +#include +#include + +#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; +} diff --git a/mDNSPosix/ExampleClientApp.c b/mDNSPosix/ExampleClientApp.c new file mode 100644 index 0000000..a0531bc --- /dev/null +++ b/mDNSPosix/ExampleClientApp.c @@ -0,0 +1,113 @@ +/* + * 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 + 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 // For printf() +#include // For exit() etc. +#include // For strlen() etc. +#include // For select() +#include // For errno, EINTR +#include // For inet_addr() +#include // For INADDR_NONE +#include // For gethostbyname() +#include // 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"); + } diff --git a/mDNSPosix/ExampleClientApp.h b/mDNSPosix/ExampleClientApp.h new file mode 100644 index 0000000..4274a46 --- /dev/null +++ b/mDNSPosix/ExampleClientApp.h @@ -0,0 +1,37 @@ +/* + * 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 + 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); diff --git a/mDNSPosix/Identify.c b/mDNSPosix/Identify.c new file mode 100644 index 0000000..228f0a6 --- /dev/null +++ b/mDNSPosix/Identify.c @@ -0,0 +1,329 @@ +/* + * 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 , + * 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 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 + 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 +#include +#include +#include +#include +#include +#include // For n_long, required by below +#include // For IPTOS_LOWDELAY etc. +#include +#include + +#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 or or \n", argv[0]); + return(-1); + } diff --git a/mDNSPosix/Makefile b/mDNSPosix/Makefile new file mode 100755 index 0000000..1801a93 --- /dev/null +++ b/mDNSPosix/Makefile @@ -0,0 +1,120 @@ +# $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 #: : 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 #: : 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 +# +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 diff --git a/mDNSPosix/NetMonitor.c b/mDNSPosix/NetMonitor.c new file mode 100644 index 0000000..b495d9a --- /dev/null +++ b/mDNSPosix/NetMonitor.c @@ -0,0 +1,928 @@ +/* + * 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 , + * 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 + 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 + 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 + 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 + 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 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 + 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 + 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 // For printf() +#include // For malloc() +#include // For bcopy() +#include // For "struct tm" etc. +#include // For gethostbyname() +#include // For AF_INET, AF_INET6, etc. +#include // For inet_addr() +#include // 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; ipkts[i] = 0; + entry->totalops = 0; + for (i=0; istat[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; istat[i] = 0; + } + + (*s)->totalops++; + (*s)->stat[op]++; + if (entry) + { + entry->totalops++; + entry->stat[op]++; + } + } + +mDNSlocal void printstats(int max) + { + int i; + for (i=0; inext) + 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; ih.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; ih.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; ih.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; ih.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; ih.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; ih.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; ih.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; ih_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 ()\n", argv[0]); + fprintf(stderr, "Optional 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); + } diff --git a/mDNSPosix/ProxyResponder.c b/mDNSPosix/ProxyResponder.c new file mode 100644 index 0000000..3bf4f11 --- /dev/null +++ b/mDNSPosix/ProxyResponder.c @@ -0,0 +1,332 @@ +/* + * 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 + 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 + Need to implement service registration with subtypes + +Revision 1.18 2003/07/02 21:19:58 cheshire + 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 + sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead + +Revision 1.15 2003/05/06 00:00:50 cheshire + Rationalize naming of domainname manipulation functions + +Revision 1.14 2003/04/25 01:45:57 cheshire + 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 // For printf() +#include // For exit() etc. +#include // For strlen() etc. +#include // For select() +#include // For errno, EINTR +#include // For inet_addr() +#include // For INADDR_NONE +#include // For gethostbyname() + +#include "mDNSClientAPI.h" // Defines the interface to the client layer above +#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform +#include "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); + } diff --git a/mDNSPosix/ReadMe.txt b/mDNSPosix/ReadMe.txt new file mode 100755 index 0000000..fc00dbd --- /dev/null +++ b/mDNSPosix/ReadMe.txt @@ -0,0 +1,310 @@ +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. + + + +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. + + + +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. + + + +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 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 diff --git a/mDNSPosix/Responder.c b/mDNSPosix/Responder.c new file mode 100755 index 0000000..6130e0c --- /dev/null +++ b/mDNSPosix/Responder.c @@ -0,0 +1,923 @@ +/* + * 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 + 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 + 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 + mDNSResponder Posix version is missing a 'b' in the getopt option string + +Revision 1.9 2003/07/02 21:19:59 cheshire + 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 + 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 +#include // For printf() +#include // For exit() etc. +#include // For strlen() etc. +#include // For select() +#include // For errno, EINTR +#include +#include + +#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 + 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 + + 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; +} diff --git a/mDNSPosix/Services.txt b/mDNSPosix/Services.txt new file mode 100755 index 0000000..a7b73da --- /dev/null +++ b/mDNSPosix/Services.txt @@ -0,0 +1,14 @@ +Tweedlebug +_afpovertcp._tcp. +name=val1 +548 + +Tweedlebug2 +_afpovertcp._tcp. local. +name=val2name2=anotherattribute +548 + +Tweedlebug3 +_afpovertcp._tcp. apple.com. +name=val3 +548 diff --git a/mDNSPosix/mDNSPosix.c b/mDNSPosix/mDNSPosix.c new file mode 100755 index 0000000..345b2f4 --- /dev/null +++ b/mDNSPosix/mDNSPosix.c @@ -0,0 +1,999 @@ +/* + * 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 , + * 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 + 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 + 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 + 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 + Rationalize naming of domainname manipulation functions + +Revision 1.10 2003/04/25 01:45:57 cheshire + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); + } + } diff --git a/mDNSPosix/mDNSPosix.h b/mDNSPosix/mDNSPosix.h new file mode 100755 index 0000000..53f7fe9 --- /dev/null +++ b/mDNSPosix/mDNSPosix.h @@ -0,0 +1,91 @@ +/* + * 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 + 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 + +#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 diff --git a/mDNSPosix/mDNSUNP.c b/mDNSPosix/mDNSUNP.c new file mode 100755 index 0000000..fa9bda0 --- /dev/null +++ b/mDNSPosix/mDNSUNP.c @@ -0,0 +1,424 @@ +/* + * 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 + 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 +#include +#include +#include +#include +#include +#include +#include + +/* Solaris defined SIOCGIFCONF etc in but + other platforms don't even have that include file. So, + if we haven't yet got a definition, let's try to find + . +*/ + +#ifndef SIOCGIFCONF + #include +#endif + +/* sockaddr_dl is only referenced if we're using IP_RECVIF, + so only include the header in that case. +*/ + +#ifdef IP_RECVIF + #include +#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 */ +} diff --git a/mDNSPosix/mDNSUNP.h b/mDNSPosix/mDNSUNP.h new file mode 100755 index 0000000..21d13f1 --- /dev/null +++ b/mDNSPosix/mDNSUNP.h @@ -0,0 +1,120 @@ +/* + * 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 + 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 +#include +#include +#include + +#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 */ +#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 */ + 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 diff --git a/mDNSResponder.pbproj/project.pbxproj b/mDNSResponder.pbproj/project.pbxproj deleted file mode 100644 index 7fc8cc5..0000000 --- a/mDNSResponder.pbproj/project.pbxproj +++ /dev/null @@ -1,531 +0,0 @@ -// !$*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; -} diff --git a/mDNSVxWorks/README.txt b/mDNSVxWorks/README.txt new file mode 100644 index 0000000..f0ea01e --- /dev/null +++ b/mDNSVxWorks/README.txt @@ -0,0 +1,8 @@ +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. diff --git a/mDNSVxWorks/mDNSVxWorks.c b/mDNSVxWorks/mDNSVxWorks.c new file mode 100644 index 0000000..b482fb8 --- /dev/null +++ b/mDNSVxWorks/mDNSVxWorks.c @@ -0,0 +1,2097 @@ +/* + * 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 + 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 + 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 : "" ); + 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 diff --git a/mDNSVxWorks/mDNSVxWorks.h b/mDNSVxWorks/mDNSVxWorks.h new file mode 100644 index 0000000..de026a4 --- /dev/null +++ b/mDNSVxWorks/mDNSVxWorks.h @@ -0,0 +1,144 @@ +/* + * 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__ diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Application.sln b/mDNSWindows/Applications/RendezvousBrowser/Windows/Application.sln new file mode 100644 index 0000000..f588af6 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Application.sln @@ -0,0 +1,21 @@ +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 diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Application.vcproj b/mDNSWindows/Applications/RendezvousBrowser/Windows/Application.vcproj new file mode 100644 index 0000000..3f64b2a --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Application.vcproj @@ -0,0 +1,244 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.ico b/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.ico new file mode 100644 index 0000000..a992327 Binary files /dev/null and b/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.ico differ diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.rc b/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.rc new file mode 100644 index 0000000..1d9c2eb --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.rc @@ -0,0 +1,281 @@ +// 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 + diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.rc2 b/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.rc2 new file mode 100644 index 0000000..bf1983f --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.rc2 @@ -0,0 +1,39 @@ +/* + * 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 diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Resource.h b/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Resource.h new file mode 100644 index 0000000..5329fc1 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Resource.h @@ -0,0 +1,45 @@ +//{{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 diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/AboutDialog.cpp b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/AboutDialog.cpp new file mode 100644 index 0000000..cd8cafb --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/AboutDialog.cpp @@ -0,0 +1,96 @@ +/* + * 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 + 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 + +#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 +} diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/AboutDialog.h b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/AboutDialog.h new file mode 100644 index 0000000..11c9149 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/AboutDialog.h @@ -0,0 +1,87 @@ +/* + * 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 + 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_) diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/Application.cpp b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/Application.cpp new file mode 100644 index 0000000..396b7e9 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/Application.cpp @@ -0,0 +1,145 @@ +/* + * 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 + 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 + +#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 ); +} diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/Application.h b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/Application.h new file mode 100644 index 0000000..e174e4f --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/Application.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + 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 + 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_) diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/ChooserDialog.cpp b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/ChooserDialog.cpp new file mode 100644 index 0000000..ddd81f9 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/ChooserDialog.cpp @@ -0,0 +1,1171 @@ +/* + * 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 + 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 +#include +#include +#include +#include + +#include +#include + +#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 ( 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 ( 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 ( 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 ( 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 ( 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 ( 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 ); +} diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/ChooserDialog.h b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/ChooserDialog.h new file mode 100644 index 0000000..7acb0b5 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/ChooserDialog.h @@ -0,0 +1,155 @@ +/* + * 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 + 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 +#include + +#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_) diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/StdAfx.cpp b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/StdAfx.cpp new file mode 100644 index 0000000..a26a1e7 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/StdAfx.cpp @@ -0,0 +1,43 @@ +/* + * 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 + 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" diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/StdAfx.h b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/StdAfx.h new file mode 100644 index 0000000..3c16ff7 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/StdAfx.h @@ -0,0 +1,72 @@ +/* + * 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 + 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 // MFC core and standard components +#include // MFC extensions +#include // MFC support for Internet Explorer 4 Common Controls +#ifndef _AFX_NO_AFXCMN_SUPPORT +#include // MFC support for Windows Common Controls +#endif // _AFX_NO_AFXCMN_SUPPORT + +#include // MFC socket extensions + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#include + +#include "DNSServices.h" + +#include "Application.h" + +#include "ChooserDialog.h" + +#endif // !defined(AFX_STDAFX_H__424305D2_0A97_4AA0_B9B1_A7D90D18EBA0__INCLUDED_) diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Application.vcc b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Application.vcc new file mode 100644 index 0000000..22f443d --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Application.vcc @@ -0,0 +1,37 @@ +; 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 diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Application.vcp b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Application.vcp new file mode 100644 index 0000000..7f62479 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Application.vcp @@ -0,0 +1,499 @@ +# 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 diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Application.vcw b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Application.vcw new file mode 100644 index 0000000..11ef513 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Application.vcw @@ -0,0 +1,29 @@ +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> +{{{ +}}} + +############################################################################### + diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.ico b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.ico new file mode 100644 index 0000000..51a182e Binary files /dev/null and b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.ico differ diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.rc b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.rc new file mode 100644 index 0000000..c453f27 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.rc @@ -0,0 +1,194 @@ +//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 + diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.rc2 b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.rc2 new file mode 100644 index 0000000..29c9fe7 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.rc2 @@ -0,0 +1,13 @@ +// +// 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... + +///////////////////////////////////////////////////////////////////////////// diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/newres.h b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/newres.h new file mode 100644 index 0000000..31c3a43 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/newres.h @@ -0,0 +1,28 @@ +#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 + // 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__ diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/resource.h b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/resource.h new file mode 100644 index 0000000..0337c56 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/resource.h @@ -0,0 +1,22 @@ +//{{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 diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/Application.cpp b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/Application.cpp new file mode 100644 index 0000000..ba28f41 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/Application.cpp @@ -0,0 +1,111 @@ +/* + * 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 ); +} diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/Application.h b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/Application.h new file mode 100644 index 0000000..1f4fe13 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/Application.h @@ -0,0 +1,70 @@ +/* + * 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_) diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/BrowserDialog.cpp b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/BrowserDialog.cpp new file mode 100644 index 0000000..991fc08 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/BrowserDialog.cpp @@ -0,0 +1,257 @@ +/* + * 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 ); +} diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/BrowserDialog.h b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/BrowserDialog.h new file mode 100644 index 0000000..7777510 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/BrowserDialog.h @@ -0,0 +1,96 @@ +/* + * 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_) diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/StdAfx.cpp b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/StdAfx.cpp new file mode 100644 index 0000000..6622a5d --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/StdAfx.cpp @@ -0,0 +1,31 @@ +/* + * 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" diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/StdAfx.h b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/StdAfx.h new file mode 100644 index 0000000..3672152 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/StdAfx.h @@ -0,0 +1,58 @@ +/* + * 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 // MFC core and standard components +#include // MFC extensions + +#if defined(_AFXDLL) +#include // MFC support for Internet Explorer 4 Common Controls +#endif + +#ifndef _AFX_NO_AFXCMN_SUPPORT +#include // MFC support for Windows Common Controls +#endif // _AFX_NO_AFXCMN_SUPPORT + +#include // 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_) diff --git a/mDNSWindows/Applications/RendezvousTest/Tool.c b/mDNSWindows/Applications/RendezvousTest/Tool.c new file mode 100644 index 0000000..7e49566 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousTest/Tool.c @@ -0,0 +1,1047 @@ +/* + * 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 +#endif + +#include +#include + +#if( __MACH__ ) + #include + #include + #include + + #include + #include + + #include +#else + #define WIN32_LEAN_AND_MEAN + + #include + #include +#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 'b'rowse for 's'ervices\n" ); + fprintf( stderr, " -lsi 'l'ookup 's'ervice 'i'nstance\n" ); + fprintf( stderr, " -rdb[d] 'r'egister 'd'omain for 'b'rowsing ['d'efault]\n" ); + fprintf( stderr, " -rdr[d] 'r'egister 'd'omain for 'r'egistration ['d'efault]\n" ); + fprintf( stderr, " -rs 'r'egister 's'ervice\n" ); + fprintf( stderr, " -rps 'r'egister 'p'roxy 's'ervice\n" ); + fprintf( stderr, " -rnss 'r'egister 'n'o 's'uch 's'ervice\n" ); + + fprintf( stderr, " -ebs 'e'mulated 'b'rowse for 's'ervices\n" ); + fprintf( stderr, " -ebd 'e'mulated 'b'rowse for 'd'omains\n" ); + fprintf( stderr, " -elsi 'e'mulated 'l'ookup 's'ervice 'i'nstance\n" ); + fprintf( stderr, " -ers '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 + + 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 + + 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] + + 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] + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 ); +} diff --git a/mDNSWindows/Applications/RendezvousTest/ToolPrefixWindows.h b/mDNSWindows/Applications/RendezvousTest/ToolPrefixWindows.h new file mode 100644 index 0000000..eaa6a70 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousTest/ToolPrefixWindows.h @@ -0,0 +1,39 @@ +/* + * 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__ diff --git a/mDNSWindows/Applications/RendezvousTest/ToolPrefixWindowsDebug.h b/mDNSWindows/Applications/RendezvousTest/ToolPrefixWindowsDebug.h new file mode 100644 index 0000000..1f76fc2 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousTest/ToolPrefixWindowsDebug.h @@ -0,0 +1,40 @@ +/* + * 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__ diff --git a/mDNSWindows/Applications/RendezvousTest/ToolWin32.mcp b/mDNSWindows/Applications/RendezvousTest/ToolWin32.mcp new file mode 100644 index 0000000..064aba0 Binary files /dev/null and b/mDNSWindows/Applications/RendezvousTest/ToolWin32.mcp differ diff --git a/mDNSWindows/Applications/RendezvousTest/ToolWin32.sln b/mDNSWindows/Applications/RendezvousTest/ToolWin32.sln new file mode 100644 index 0000000..495d5df --- /dev/null +++ b/mDNSWindows/Applications/RendezvousTest/ToolWin32.sln @@ -0,0 +1,21 @@ +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 diff --git a/mDNSWindows/Applications/RendezvousTest/ToolWin32.vcproj b/mDNSWindows/Applications/RendezvousTest/ToolWin32.vcproj new file mode 100644 index 0000000..d5e1868 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousTest/ToolWin32.vcproj @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mDNSWindows/DNSServices/DNSServiceDiscovery.c b/mDNSWindows/DNSServices/DNSServiceDiscovery.c new file mode 100644 index 0000000..77e3dcb --- /dev/null +++ b/mDNSWindows/DNSServices/DNSServiceDiscovery.c @@ -0,0 +1,761 @@ +/* + * 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 +#include +#include + +#if( macintosh || __MACH__ ) + + #include + #include + #include + +#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 + +#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 diff --git a/mDNSWindows/DNSServices/DNSServiceDiscovery.h b/mDNSWindows/DNSServices/DNSServiceDiscovery.h new file mode 100644 index 0000000..618bbb3 --- /dev/null +++ b/mDNSWindows/DNSServices/DNSServiceDiscovery.h @@ -0,0 +1,343 @@ +/* + * 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 + +#if( __MACH__ ) + + #include + + #include + #include + #include + + #include + +#elif( defined( __MWERKS__ ) ) + + #include + +#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 ) + @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__ diff --git a/mDNSWindows/DNSServices/DNSServices.c b/mDNSWindows/DNSServices/DNSServices.c new file mode 100755 index 0000000..ca84436 --- /dev/null +++ b/mDNSWindows/DNSServices/DNSServices.c @@ -0,0 +1,3158 @@ +/* + * 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 + 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 + Need to implement service registration with subtypes + +Revision 1.10 2003/07/02 21:20:10 cheshire + 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 + Rationalize naming of domainname manipulation functions + +Revision 1.7 2003/03/27 03:30:57 cheshire + Name conflicts not handled properly, resulting in memory corruption, and eventual crash +Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback +Fixes: +1. Make mDNS_DeregisterInterface() safe to call from a callback +2. Make HostNameCallback() use mDNS_DeadvertiseInterface() instead + (it never really needed to deregister the interface at all) + +Revision 1.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 +#include +#include + +#if( __MACH__ ) + #include +#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 diff --git a/mDNSWindows/DNSServices/DNSServices.h b/mDNSWindows/DNSServices/DNSServices.h new file mode 100755 index 0000000..f5be4aa --- /dev/null +++ b/mDNSWindows/DNSServices/DNSServices.h @@ -0,0 +1,1912 @@ +/* + * 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 + 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 + +#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: + + + + + 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: + + + +*/ + +#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 ). +*/ + +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__ diff --git a/mDNSWindows/README.txt b/mDNSWindows/README.txt new file mode 100644 index 0000000..52c48b1 --- /dev/null +++ b/mDNSWindows/README.txt @@ -0,0 +1,44 @@ +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. diff --git a/mDNSWindows/mDNSWin32.c b/mDNSWindows/mDNSWin32.c new file mode 100755 index 0000000..161e635 --- /dev/null +++ b/mDNSWindows/mDNSWin32.c @@ -0,0 +1,2267 @@ +/* + * 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 + 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 +"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 + 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 + sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead + +Revision 1.12 2003/05/06 21:06:05 cheshire + mDNSWindows needs a wakeupEvent object to signal the main thread + +Revision 1.11 2003/05/06 00:00:51 cheshire + Rationalize naming of domainname manipulation functions + +Revision 1.10 2003/04/29 00:06:09 cheshire + 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 +#include +#include +#include +#include + +#include +#include +#include + +#if( !defined( _WIN32_WCE ) ) // Windows CE does not have process.h. + #include +#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 . + + 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 . + + _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 ) diff --git a/mDNSWindows/mDNSWin32.h b/mDNSWindows/mDNSWin32.h new file mode 100755 index 0000000..2bf5ddd --- /dev/null +++ b/mDNSWindows/mDNSWin32.h @@ -0,0 +1,194 @@ +/* + * 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 +"ScheduleNextTask needs to be smarter" has refined the way m->NextScheduledEvent is set + +Revision 1.6 2003/07/02 21:20:04 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.5 2003/04/29 00:06:09 cheshire + 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 +#include +#include + +#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__