-/*
+/* -*- Mode: C; tab-width: 4 -*-
+ *
* Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
- * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
- *
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
Change History (most recent first):
$Log: uds_daemon.c,v $
-Revision 1.55.2.1 2004/06/13 22:32:24 ksekar
-WWDC Branch
+Revision 1.180 2005/03/10 00:13:12 cheshire
+<rdar://problem/4043098> DNSServiceBrowse no longer returning error codes for invalid types
+In handle_browse_request(), mStatus err was being set correctly if an error occurred,
+but the end of the function returned mStatus_NoError intead of err.
+
+Revision 1.179 2005/03/04 02:47:26 ksekar
+<rdar://problem/4026393> SCPreference domains disappear from enumeration when moving out from firewall
+
+Revision 1.178 2005/02/25 19:35:38 ksekar
+<rdar://problem/4023750> Non-local empty string registration failures should not return errors to caller
+
+Revision 1.177 2005/02/25 03:05:41 cheshire
+Change "broken pipe" message to debugf()
+
+Revision 1.176 2005/02/24 18:44:45 ksekar
+<rdar://problem/4018516> Printer Sharing does not get re-registered with wide-area
+
+Revision 1.175 2005/02/21 21:31:25 ksekar
+<rdar://problem/4015162> changed LogMsg to debugf
+
+Revision 1.174 2005/02/20 01:41:17 cheshire
+Fix compiler signed/unsigned warning
+
+Revision 1.173 2005/02/18 01:26:42 cheshire
+<rdar://problem/4012162> "Could not write data to client after 60 seconds" message could be more helpful
+Log additional information about failed client
+
+Revision 1.172 2005/02/18 00:58:35 cheshire
+<rdar://problem/4012162> "Could not write data to client after 60 seconds" message could be more helpful
+
+Revision 1.171 2005/02/18 00:43:12 cheshire
+<rdar://problem/4010245> mDNSResponder should auto-truncate service names that are too long
+
+Revision 1.170 2005/02/16 01:15:02 cheshire
+Improve LogOperation() debugging messages for DNSServiceBrowse and DNSServiceRegister
+
+Revision 1.169 2005/02/08 01:57:14 cheshire
+More detailed error reporting in udsserver_init()
+
+Revision 1.168 2005/02/03 00:44:37 cheshire
+<rdar://problem/3986663> DNSServiceUpdateRecord returns kDNSServiceErr_Invalid when rdlen=0, rdata=NULL
+
+Revision 1.167 2005/02/02 02:19:32 cheshire
+Add comment explaining why unlink(MDNS_UDS_SERVERPATH); fails
+
+Revision 1.166 2005/02/01 19:58:52 ksekar
+Shortened cryptic "broken pipe" syslog message
+
+Revision 1.165 2005/02/01 19:56:47 ksekar
+Moved LogMsg from daemon.c to uds_daemon.c, cleaned up wording
+
+Revision 1.164 2005/01/28 06:07:55 cheshire
+Don't use deliver_error() from within handle_regrecord_request()
+
+Revision 1.163 2005/01/28 01:39:16 cheshire
+Include file descriptor number in "broken pipe" message
+
+Revision 1.162 2005/01/27 23:59:20 cheshire
+Remove extraneous LogMsg
+
+Revision 1.161 2005/01/27 22:57:56 cheshire
+Fix compile errors on gcc4
+
+Revision 1.160 2005/01/27 20:52:11 cheshire
+<rdar://problem/3972566> mDNSResponder leaks sockets for add/update/remove record calls
+
+Revision 1.159 2005/01/27 01:45:25 cheshire
+<rdar://problem/3976147> mDNSResponder should never call exit(1);
+
+Revision 1.158 2005/01/25 17:28:07 ksekar
+<rdar://problem/3971467> Should not return "local" twice for domain enumeration
+
+Revision 1.157 2005/01/21 02:20:39 cheshire
+Fix mistake in LogOperation() format string
+
+Revision 1.156 2005/01/19 19:15:36 ksekar
+Refinement to <rdar://problem/3954575> - Simplify mDNS_PurgeResultsForDomain logic and move into daemon layer
+
+Revision 1.155 2005/01/19 03:00:47 cheshire
+Show Add/Rmv in DNSServiceBrowse LogOperation() message
+
+Revision 1.154 2005/01/15 00:56:42 ksekar
+<rdar://problem/3954575> Unicast services don't disappear when logging
+out of VPN
+
+Revision 1.153 2005/01/14 18:44:28 ksekar
+<rdar://problem/3954609> mDNSResponder is crashing when changing domains
+
+Revision 1.152 2005/01/13 17:16:38 ksekar
+Back out checkin 1.150 - correct fix is on clientstub side
+
+Revision 1.151 2005/01/11 21:06:29 ksekar
+Changed now-benign LogMsg to debugf
+
+Revision 1.150 2005/01/07 23:59:15 ksekar
+<rdar://problem/3942900> dnd-sd shows the wrong port numbers
+
+Revision 1.149 2004/12/20 23:20:35 cheshire
+<rdar://problem/3928361> mDNSResponder crashes repeatedly when printer sharing is enabled
+Make sure to call mDNS_SetupResourceRecord() for all newly created AuthRecords
+
+Revision 1.148 2004/12/20 20:37:35 cheshire
+AllowRemoteQuery not set for the extras in a ServiceRecordSet
+
+Revision 1.147 2004/12/20 00:15:41 cheshire
+Include client file descriptor numbers in udsserver_info() output
+
+Revision 1.146 2004/12/17 05:25:47 cheshire
+<rdar://problem/3925163> Shorten DNS-SD queries to avoid NAT bugs
+
+Revision 1.145 2004/12/16 21:39:46 cheshire
+Include CacheGroup objects in CacheUsed count
+
+Revision 1.144 2004/12/16 21:27:38 ksekar
+Fixed build failures when compiled with verbose debugging messages
+
+Revision 1.143 2004/12/16 20:13:02 cheshire
+<rdar://problem/3324626> Cache memory management improvements
+
+Revision 1.142 2004/12/16 08:07:33 shersche
+Fix compiler error (mixed declarations and code) on Windows
+
+Revision 1.141 2004/12/16 01:56:21 cheshire
+Improve DNSServiceEnumerateDomains syslog message
+
+Revision 1.140 2004/12/14 03:02:10 ksekar
+<rdar://problem/3919016> Rare race condition can cause crash
+
+Revision 1.139 2004/12/13 21:18:45 ksekar
+Include uDNS registrations in CountPeerRegistrations
+
+Revision 1.138 2004/12/13 18:23:18 ksekar
+<rdar://problem/3915805> mDNSResponder error when quitting iChat -
+don't close sockets delivering errors to blocked clients
+
+Revision 1.137 2004/12/13 00:09:22 ksekar
+<rdar://problem/3915805> mDNSResponder error when quitting iChat
+
+Revision 1.136 2004/12/11 01:52:10 cheshire
+<rdar://problem/3785820> Support kDNSServiceFlagsAllowRemoteQuery for registering services too
+
+Revision 1.135 2004/12/10 20:46:37 cheshire
+Change LogOperation message to debugf
+
+Revision 1.134 2004/12/10 13:19:37 cheshire
+Add verbosedebugf() logging message in CountPeerRegistrations()
+
+Revision 1.133 2004/12/10 05:27:26 cheshire
+<rdar://problem/3909147> Guard against multiple autoname services of the same type on the same machine
+
+Revision 1.132 2004/12/10 04:28:28 cheshire
+<rdar://problem/3914406> User not notified of name changes for services using new UDS API
+
+Revision 1.131 2004/12/10 02:09:25 cheshire
+<rdar://problem/3898376> Modify default TTLs
+
+Revision 1.130 2004/12/10 00:55:24 cheshire
+Add full name and type to LogOperation messages for DNSServiceAddRecord/UpdateRecord/RemoveRecord
+
+Revision 1.129 2004/12/09 03:17:23 ksekar
+<rdar://problem/3910435> DomainEnumeration interface index should be zero
+
+Revision 1.128 2004/12/07 21:26:05 ksekar
+<rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration
+
+Revision 1.127 2004/12/07 20:42:34 cheshire
+Add explicit context parameter to mDNS_RemoveRecordFromService()
+
+Revision 1.126 2004/12/07 17:23:55 ksekar
+Fixed LogOperation
+
+Revision 1.125 2004/12/06 21:15:23 ksekar
+<rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
+
+Revision 1.124 2004/11/30 02:19:14 cheshire
+<rdar://problem/3827971> Raise maxfds.rlim_cur for mDNSResponder
+
+Revision 1.123 2004/11/29 23:50:57 cheshire
+Checkin 1.122 not necessary
+
+Revision 1.122 2004/11/24 17:55:01 ksekar
+Added log message clarifying <rdar://problem/3869241> For unicast operations, verify that service types are legal
+
+Revision 1.121 2004/11/24 04:45:52 cheshire
+Spelling mistake
+
+Revision 1.120 2004/11/24 00:10:44 cheshire
+<rdar://problem/3869241> For unicast operations, verify that service types are legal
+
+Revision 1.119 2004/11/23 23:54:17 ksekar
+<rdar://problem/3890318> Wide-Area DNSServiceRegisterRecord() failures
+can crash mDNSResponder
+
+Revision 1.118 2004/11/23 22:33:01 cheshire
+<rdar://problem/3654910> Remove temporary workaround code for iChat
+
+Revision 1.117 2004/11/23 20:23:10 ksekar
+Fixed LogOperation that causes crash on connected service deregistrations
+
+Revision 1.116 2004/11/23 03:39:47 cheshire
+Let interface name/index mapping capability live directly in JNISupport.c,
+instead of having to call through to the daemon via IPC to get this information.
+
+Revision 1.115 2004/11/13 00:12:53 ksekar
+Fixed some LogOperation printf converstions for debug builds.
+
+Revision 1.114 2004/11/12 18:25:45 shersche
+Tidy up cross platform usleep code fragment.
+
+Revision 1.113 2004/11/12 03:21:41 rpantos
+rdar://problem/3809541 Add DNSSDMapIfIndexToName, DNSSDMapNameToIfIndex.
+
+Revision 1.112 2004/11/11 16:58:32 ksekar
+Removed unused code (previously wrapped in #if 0)
+
+Revision 1.111 2004/11/05 22:47:37 shersche
+Conditionally compile usleep(1000) to be Sleep(1) on Windows
+Submitted by: Pavel Repin <prepin@gmail.com>
+
+Revision 1.110 2004/11/05 19:56:56 ksekar
+<rdar://problem/3862646> We no longer need to browse .Mac domains by
+default - changed #if 0 to more descriptive #ifdef _HAVE_SETDOMAIN_SUPPORT_
+
+Revision 1.109 2004/11/04 03:40:45 cheshire
+More debugging messages
+
+Revision 1.108 2004/11/03 02:25:51 cheshire
+<rdar://problem/3324137> Conflict for Computer Name should update *all* empty string services, not just the one with the conflict
+
+Revision 1.107 2004/11/02 19:39:23 ksekar
+<rdar://problem/3862646> We no longer need to browse .Mac domains by default
+
+Revision 1.106 2004/11/02 02:12:21 cheshire
+<rdar://problem/3839111> Remove unnecessary memory allocations
+
+Revision 1.105 2004/10/28 19:07:19 cheshire
+Add some more debugging checks and improved LogOperation() messages
+
+Revision 1.104 2004/10/26 18:53:15 cheshire
+Avoid unused variable warning
+
+Revision 1.103 2004/10/26 07:15:55 cheshire
+Add file descriptor number to all LogOperation messages
+
+Revision 1.102 2004/10/26 06:11:42 cheshire
+Add improved logging to aid in diagnosis of <rdar://problem/3842714> mDNSResponder crashed
+
+Revision 1.101 2004/10/26 04:31:44 cheshire
+Rename CountSubTypes() as ChopSubTypes()
+
+Revision 1.100 2004/10/26 01:17:48 cheshire
+Use "#if 0" instead of commenting out code
+
+Revision 1.99 2004/10/19 21:33:22 cheshire
+<rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
+Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name
+doesn't force multicast unless you set this flag to indicate explicitly that this is what you want
+
+Revision 1.98 2004/10/14 01:59:33 cheshire
+<rdar://problem/3839208> UDS resolves don't work for uDNS services
+
+Revision 1.97 2004/10/13 00:58:35 cheshire
+<rdar://problem/3832738> Registering a proxy doesn't work
+
+Revision 1.96 2004/09/30 00:25:00 ksekar
+<rdar://problem/3695802> Dynamically update default registration domains on config change
+
+Revision 1.95 2004/09/26 23:20:36 ksekar
+<rdar://problem/3813108> Allow default registrations in multiple wide-area domains
+
+Revision 1.94 2004/09/22 18:27:06 ksekar
+<rdar://problem/3811427> allow DNSServiceAddRecord to pass zero to get
+default record TTL
+
+Revision 1.93 2004/09/22 02:39:44 cheshire
+<rdar://problem/3810757> Allow DNSServiceRegisterRecord to pass zero to get default record TTL
+
+Revision 1.92 2004/09/22 02:34:04 cheshire
+Rename parameter "ttl" to "GetTTL" for clarity
+
+Revision 1.91 2004/09/22 02:25:43 cheshire
+Fix spelling errors
+
+Revision 1.90 2004/09/21 23:40:12 ksekar
+<rdar://problem/3810349> mDNSResponder to return errors on NAT traversal failure
+
+Revision 1.89 2004/09/21 23:29:51 cheshire
+<rdar://problem/3680045> DNSServiceResolve should delay sending packets
+
+Revision 1.88 2004/09/21 23:12:46 cheshire
+Reorder initialization of question fields to match structure order
+
+Revision 1.87 2004/09/21 22:18:33 cheshire
+In SIGINFO output, display a '-' next to records that have the Unique bit set
+
+Revision 1.86 2004/09/21 21:05:11 cheshire
+Move duplicate code out of mDNSMacOSX/daemon.c and mDNSPosix/PosixDaemon.c,
+into mDNSShared/uds_daemon.c
+
+Revision 1.85 2004/09/18 01:11:58 ksekar
+<rdar://problem/3806734> Add a user's default domain to empty-string browse list
+
+Revision 1.84 2004/09/17 01:08:55 cheshire
+Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
+ The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
+ declared in that file are ONLY appropriate to single-address-space embedded applications.
+ For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
+
+Revision 1.83 2004/09/16 23:26:33 cheshire
+Move version check inside preceeding "if" that checks we have a complete header
+
+Revision 1.82 2004/09/16 23:14:25 cheshire
+Changes for Windows compatibility
+
+Revision 1.81 2004/09/16 21:46:38 ksekar
+<rdar://problem/3665304> Need SPI for LoginWindow to associate a UID with a Wide Area domain
+
+Revision 1.80 2004/09/16 01:58:23 cheshire
+Fix compiler warnings
+
+Revision 1.79 2004/09/16 00:24:49 cheshire
+<rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
+
+Revision 1.78 2004/09/15 21:44:20 cheshire
+<rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init
+Show time value in log to help diagnose errors
+
+Revision 1.77 2004/09/15 00:19:18 cheshire
+<rdar://problem/3785823> read_rr_from_ipc_msg should use mDNS_SetupResourceRecord()
+
+Revision 1.76 2004/09/02 06:39:52 cheshire
+Minor textual cleanup for clarity
+
+Revision 1.75 2004/09/02 03:48:47 cheshire
+<rdar://problem/3709039> Disable targeted unicast query support by default
+1. New flag kDNSServiceFlagsAllowRemoteQuery to indicate we want to allow remote queries for this record
+2. New field AllowRemoteQuery in AuthRecord structure
+3. uds_daemon.c sets AllowRemoteQuery if kDNSServiceFlagsAllowRemoteQuery is set
+4. mDNS.c only answers remote queries if AllowRemoteQuery is set
+
+Revision 1.74 2004/08/25 02:32:47 cheshire
+Minor cleanup: replace "®type[0]" with "regtype"
+
+Revision 1.73 2004/08/25 02:30:40 cheshire
+<rdar://problem/3588761> Current method of doing subtypes causes name collisions
+
+Revision 1.72 2004/08/14 03:22:42 cheshire
+<rdar://problem/3762579> Dynamic DNS UI <-> mDNSResponder glue
+Add GetUserSpecifiedDDNSName() routine
+Convert ServiceRegDomain to domainname instead of C string
+Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs
+
+Revision 1.71 2004/08/11 04:21:21 rpantos
+Fix Windows build.
+
+Revision 1.70 2004/08/11 02:07:00 cheshire
+Remove "mDNS *globalInstance" parameter from udsserver_init()
+Move CheckForDuplicateRegistrations from daemon.c
+<rdar://problem/3501938> No warning when accidentally registering the same service multiple times using socket API
+
+Revision 1.69 2004/08/10 16:14:48 cheshire
+Fix debug builds (oops)
+
+Revision 1.68 2004/08/10 06:24:56 cheshire
+Use types with precisely defined sizes for 'op' and 'reg_index', for better
+compatibility if the daemon and the client stub are built using different compilers
+
+Revision 1.67 2004/07/27 07:14:16 shersche
+make error socket non-blocking after call to connect()
+
+Revision 1.66 2004/07/13 21:24:25 rpantos
+Fix for <rdar://problem/3701120>.
+
+Revision 1.65 2004/06/26 03:17:14 shersche
+implement cross-platform strerror function
+
+Submitted by: herscher
+
+Revision 1.64 2004/06/25 00:26:27 rpantos
+Changes to fix the Posix build on Solaris.
+
+Revision 1.63 2004/06/24 03:43:44 rpantos
+Fix previous checkin so it builds on Windows.
+
+Revision 1.62 2004/06/24 00:57:08 ksekar
+Replaced code acccidentally removed in checkin 1.59.
+
+Revision 1.61 2004/06/19 00:09:39 cheshire
+Remove unused strsep() implementation
+
+Revision 1.60 2004/06/18 19:10:00 cheshire
+<rdar://problem/3588761> Current method of doing subtypes causes name collisions
+
+Revision 1.59 2004/06/18 05:10:31 rpantos
+Changes to allow code to be used on Windows
+
+Revision 1.58 2004/06/15 03:54:08 cheshire
+Include mDNS_TimeNow(&mDNSStorage) in SIGINFO output
+
+Revision 1.57 2004/06/12 01:47:27 ksekar
+<rdar://problem/3690241>: BBEdit crashes when trying to check for newer version
+udsserver_idle compared time in ticks to interval in seconds.
+
+Revision 1.56 2004/06/12 01:35:47 cheshire
+Changes for Windows compatibility
Revision 1.55 2004/06/05 00:04:27 cheshire
<rdar://problem/3668639>: wide-area domains should be returned in reg. domain enumeration
<rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
Revision 1.52 2004/05/26 00:39:49 ksekar
-<rdar://problem/3667105>: wide-area rendezvous servers don't appear in
+<rdar://problem/3667105>: wide-area DNS-SD servers don't appear in
Finder
Use local-only InterfaceID for GetDomains calls for sockets-API
Revision 1.26 2003/10/23 17:51:04 ksekar
<rdar://problem/3335216>: handle blocked clients more efficiently
-Changed gettimeofday() to mDNSPlatformTimeNow()
+Changed gettimeofday() to mDNS_TimeNow(&mDNSStorage);
Revision 1.25 2003/10/22 23:37:49 ksekar
<rdar://problem/3459141>: crash/hang in abort_client
*/
+#if defined(_WIN32)
+#include <process.h>
+#define dnssd_strerror(X) win32_strerror(X)
+#define usleep(X) Sleep(((X)+999)/1000)
+static char * win32_strerror(int inErrorCode);
+#else
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
-#include <sys/socket.h>
+#define dnssd_strerror(X) strerror(X)
+#endif
-#include "mDNSClientAPI.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include "mDNSEmbeddedAPI.h"
+#include "DNSCommon.h"
#include "uds_daemon.h"
#include "dns_sd.h"
#include "dnssd_ipc.h"
-// convenience definition
-#define _UNUSED __attribute__ ((unused))
+// Apple specific configuration functionality, not required for other platforms
+#ifdef __MACOSX__
+#include <sys/ucred.h>
+#ifndef LOCAL_PEERCRED
+#define LOCAL_PEERCRED 0x001 /* retrieve peer credentials */
+#endif // LOCAL_PEERCRED
+#endif //__MACOSX__
+
// Types and Data Structures
// ----------------------------------------------------------------------
typedef void (*req_termination_fn)(void *);
-
typedef struct registered_record_entry
{
- int key;
+ uint32_t key;
AuthRecord *rr;
struct registered_record_entry *next;
+ client_context_t client_context;
+ struct request_state *rstate;
} registered_record_entry;
-
-typedef struct extra_record_entry
- {
- int key;
- struct extra_record_entry *next;
- ExtraResourceRecord e;
- } extra_record_entry;
-typedef struct registered_service
+// A single registered service: ServiceRecordSet + bookkeeping
+// Note that we duplicate some fields from parent service_info object
+// to facilitate cleanup, when instances and parent may be deallocated at different times.
+typedef struct service_instance
{
- int autoname;
- int renameonconflict;
- int rename_on_memfree; // set flag on config change when we deregister original name
+ struct service_instance *next;
+ mDNSBool autoname; // Set if this name is tied to the Computer Name
+ mDNSBool autorename; // Set if this client wants us to automatically rename on conflict
+ mDNSBool allowremotequery; // Respond to unicast queries from outside the local link?
+ mDNSBool rename_on_memfree; // Set on config change when we deregister original name
domainlabel name;
- ServiceRecordSet *srs;
+ domainname domain;
+ mDNSBool default_local; // is this the "local." from an empty-string registration?
struct request_state *request;
+ int sd;
AuthRecord *subtypes;
- extra_record_entry *extras;
- } registered_service;
+ ServiceRecordSet srs; // note - must be last field in struct
+ } service_instance;
+
+// A client-created service. May reference several service_info objects if default
+// settings cause registration in multiple domains.
+typedef struct
+ {
+ uint16_t txtlen;
+ void *txtdata;
+ mDNSIPPort port;
+ domainlabel name;
+ char type_as_string[MAX_ESCAPED_DOMAIN_NAME];
+ domainname type;
+ mDNSBool default_domain;
+ domainname host;
+ mDNSBool autoname; // Set if this name is tied to the Computer Name
+ mDNSBool autorename; // Set if this client wants us to automatically rename on conflict
+ mDNSBool allowremotequery; // Respond to unicast queries from outside the local link?
+ int num_subtypes;
+ mDNSInterfaceID InterfaceID;
+ service_instance *instances;
+ struct request_state *request;
+ } service_info;
+// for multi-domain default browsing
+typedef struct browser_t
+ {
+ DNSQuestion q;
+ domainname domain;
+ struct browser_t *next;
+ } browser_t;
+
+// parent struct for browser instances: list pointer plus metadata
typedef struct
{
- registered_service *local;
- registered_service *global;
- } servicepair_t;
+ mDNSBool default_domain;
+ mDNSBool ForceMCast;
+ domainname regtype;
+ mDNSInterfaceID interface_id;
+ struct request_state *rstate;
+ browser_t *browsers;
+ } browser_info_t;
-typedef struct
+typedef struct
{
- mStatus err;
+ mStatus err; // Note: This field is in NETWORK byte order
int nwritten;
- int sd;
+ dnssd_sock_t sd;
} undelivered_error_t;
typedef struct request_state
{
// connection structures
- int sd;
- int errfd;
-
- // state of read (in case message is read over several recv() calls)
+ dnssd_sock_t sd;
+
+ // 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;
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
- servicepair_t servicepair;
- struct resolve_result_t *resolve_results;
-
+ service_info *service_registration;
+ browser_info_t *browser_info;
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;
+ DNSServiceFlags flags; // Note: This field is in NETWORK byte order
+ uint32_t ifi; // Note: This field is in NETWORK byte order
+ DNSServiceErrorType error; // Note: This field is in NETWORK byte order
} reply_hdr;
-
typedef struct reply_state
{
// state of the transmission
- int sd;
+ dnssd_sock_t sd;
transfer_state ts;
uint32_t nwriten;
uint32_t len;
reply_hdr *rhdr;
char *sdata; // pointer to start of call-specific data
// pointer to malloc'd buffer
- char *msgbuf;
+ char *msgbuf;
} reply_state;
-
// domain enumeration and resolv calls require 2 mDNSCore calls, so we need separate interconnected
// structures to handle callbacks
typedef struct
typedef struct
{
- DNSQuestion question;
- uint16_t qtype;
- request_state *rstate;
- } resolve_t;
-
-typedef struct
- {
- resolve_t *txt;
- resolve_t *srv;
request_state *rstate;
+ DNSQuestion qtxt;
+ DNSQuestion qsrv;
+ // const ResourceRecord *txt;
+ // const ResourceRecord *srv;
+ mDNSBool srv;
+ mDNSBool txt;
+ domainname target;
+ mDNSIPPort port;
+ mDNSu16 txtlen;
+ mDNSu8 txtdata[AbsoluteMaxDNSMessageData];
} 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;
-// for multi-domain browsing
-typedef struct qlist_t
- {
- DNSQuestion q;
- struct qlist_t *next;
- } qlist_t;
-
-typedef struct
+#ifdef _HAVE_SETDOMAIN_SUPPORT_
+typedef struct default_browse_list_t
{
- qlist_t *qlist;
- request_state *rstate;
- } browse_termination_context;
+ struct default_browse_list_t *next;
+ uid_t uid;
+ AuthRecord ptr_rec;
+ } default_browse_list_t;
+static default_browse_list_t *default_browse_list = NULL;
+#endif // _HAVE_SETDOMAIN_SUPPORT_
// globals
-static mDNS *gmDNS = NULL;
-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
-#define MAX_TIME_BLOCKED 60 * mDNSPlatformOneSecond // try to send data to a blocked client for 60 seconds before
- // terminating connection
-#define MSG_PAD_BYTES 5 // pad message buffer (read from client) with n zero'd bytes to guarantee
- // n get_string() calls w/o buffer overrun
+mDNSexport mDNS mDNSStorage;
+#define gmDNS (&mDNSStorage)
+
+static dnssd_sock_t listenfd = dnssd_InvalidSocket;
+static request_state * all_requests = NULL;
+
+#define MAX_TIME_BLOCKED 60 * mDNSPlatformOneSecond // try to send data to a blocked client for 60 seconds before
+ // terminating connection
+#define MSG_PAD_BYTES 5 // pad message buffer (read from client) with n zero'd bytes to guarantee
+ // n get_string() calls w/o buffer overrun
// private function prototypes
static void connect_callback(void *info);
static int read_msg(request_state *rs);
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 process_service_registration(ServiceRecordSet *const srs, mDNSBool SuppressError);
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 handle_add_request(request_state *rstate);
+static mStatus 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 handle_query_request(request_state *rstate);
static reply_state *format_enumeration_reply(request_state *rstate, const 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 mStatus 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, int validate_flags);
-static void handle_removerecord_request(request_state *rstate);
+static mStatus 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 reply_state *create_reply(reply_op_t op, size_t 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);
static int validate_message(request_state *rstate);
-static mStatus remove_extra_rr_from_service(request_state *rstate);
+static mStatus remove_extra(request_state *rstate, service_instance *serv);
static mStatus remove_record(request_state *rstate);
-static void free_service_registration(registered_service *srv);
+static void free_service_instance(service_instance *srv);
+static uint32_t dnssd_htonl(uint32_t l);
+static void handle_setdomain_request(request_state *rstate);
// initialization, setup/teardown functions
#define PID_FILE "/var/run/mDNSResponder.pid"
#endif
-int udsserver_init( mDNS *globalInstance)
- {
- mode_t mask;
- struct sockaddr_un laddr;
- struct rlimit maxfds;
+mDNSlocal void LogClientInfo(request_state *req)
+ {
+ void *t = req->termination_context;
+ if (t)
+ {
+ if (req->terminate == regservice_termination_callback)
+ {
+ service_instance *ptr;
+ for (ptr = ((service_info *)t)->instances; ptr; ptr = ptr->next)
+ LogMsgNoIdent("%3d: DNSServiceRegister %##s %u", req->sd, ptr->srs.RR_SRV.resrec.name->c, SRS_PORT(&ptr->srs));
+ }
+ else if (req->terminate == browse_termination_callback)
+ {
+ browser_t *blist;
+ for (blist = req->browser_info->browsers; blist; blist = blist->next)
+ LogMsgNoIdent("%3d: DNSServiceBrowse %##s", req->sd, blist->q.qname.c);
+ }
+ else if (req->terminate == resolve_termination_callback)
+ LogMsgNoIdent("%3d: DNSServiceResolve %##s", req->sd, ((resolve_termination_t *)t)->qsrv.qname.c);
+ else if (req->terminate == question_termination_callback)
+ LogMsgNoIdent("%3d: DNSServiceQueryRecord %##s", req->sd, ((DNSQuestion *) t)->qname.c);
+ else if (req->terminate == enum_termination_callback)
+ LogMsgNoIdent("%3d: DNSServiceEnumerateDomains %##s", req->sd, ((enum_termination_t *) t)->all->question.qname.c);
+ }
+ }
- if ( !globalInstance)
- goto error;
- gmDNS = globalInstance;
+static void FatalError(char *errmsg)
+ {
+ LogMsg("%s: %s", errmsg, dnssd_strerror(dnssd_errno()));
+ *(long*)0 = 0; // On OS X abort() doesn't generate a crash log, but writing to zero does
+ abort(); // On platforms where writing to zero doesn't generate an exception, abort instead
+ }
+
+int udsserver_init(void)
+ {
+ dnssd_sockaddr_t laddr;
+ int ret;
+#if defined(_WIN32)
+ u_long opt = 1;
+#endif
// If a particular platform wants to opt out of having a PID file, define PID_FILE to be ""
if (PID_FILE[0])
}
}
- if ((listenfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
- goto error;
- unlink(MDNS_UDS_SERVERPATH); //OK if this fails
+ if ((listenfd = socket(AF_DNSSD, SOCK_STREAM, 0)) == dnssd_InvalidSocket)
+ {
+ my_perror("ERROR: socket(AF_DNSSD, SOCK_STREAM, 0); failed");
+ goto error;
+ }
+
bzero(&laddr, sizeof(laddr));
- laddr.sun_family = AF_LOCAL;
-#ifndef NOT_HAVE_SA_LEN // According to Stevens (section 3.2), there is no portable way to
- // determine whether sa_len is defined on a particular platform.
- laddr.sun_len = sizeof(struct sockaddr_un);
-#endif
- 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);
-
+ #if defined(USE_TCP_LOOPBACK)
+ {
+ laddr.sin_family = AF_INET;
+ laddr.sin_port = htons(MDNS_TCP_SERVERPORT);
+ laddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR);
+ ret = bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr));
+ if (ret < 0)
+ {
+ my_perror("ERROR: bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); failed");
+ goto error;
+ }
+ }
+ #else
+ {
+ mode_t mask = umask(0);
+ unlink(MDNS_UDS_SERVERPATH); //OK if this fails
+ laddr.sun_family = AF_LOCAL;
+ #ifndef NOT_HAVE_SA_LEN
+ // According to Stevens (section 3.2), there is no portable way to
+ // determine whether sa_len is defined on a particular platform.
+ laddr.sun_len = sizeof(struct sockaddr_un);
+ #endif
+ strcpy(laddr.sun_path, MDNS_UDS_SERVERPATH);
+ ret = bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr));
+ umask(mask);
+ if (ret < 0)
+ {
+ my_perror("ERROR: bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); failed");
+ goto error;
+ }
+ }
+ #endif
+
+ #if defined(_WIN32)
+ //
+ // SEH: do we even need to do this on windows? this socket
+ // will be given to WSAEventSelect which will automatically
+ // set it to non-blocking
+ //
+ if (ioctlsocket(listenfd, FIONBIO, &opt) != 0)
+ #else
+ if (fcntl(listenfd, F_SETFL, O_NONBLOCK) != 0)
+ #endif
+ {
+ my_perror("ERROR: could not set listen socket to non-blocking mode");
+ goto error;
+ }
+
+ if (listen(listenfd, LISTENQ) != 0)
+ {
+ my_perror("ERROR: could not listen on listen socket");
+ goto error;
+ }
+
if (mStatus_NoError != udsSupportAddFDToEventLoop(listenfd, connect_callback, (void *) NULL))
{
my_perror("ERROR: could not add listen socket to event loop");
goto error;
}
-
- // 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");
+
+#if !defined(PLATFORM_NO_RLIMIT)
+ {
+ // Set maximum number of open file descriptors
+ #define MIN_OPENFILES 10240
+ struct rlimit maxfds, newfds;
+
+ // Due to bugs in OS X (<rdar://problem/2941095>, <rdar://problem/3342704>, <rdar://problem/3839173>)
+ // you have to get and set rlimits once before getrlimit will return sensible values
+ if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; }
+ if (setrlimit(RLIMIT_NOFILE, &maxfds) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit");
+
+ if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; }
+ newfds.rlim_max = (maxfds.rlim_max > MIN_OPENFILES) ? maxfds.rlim_max : MIN_OPENFILES;
+ newfds.rlim_cur = (maxfds.rlim_cur > MIN_OPENFILES) ? maxfds.rlim_cur : MIN_OPENFILES;
+ if (newfds.rlim_max != maxfds.rlim_max || newfds.rlim_cur != maxfds.rlim_cur)
+ if (setrlimit(RLIMIT_NOFILE, &newfds) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit");
+
+ if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; }
+ debugf("maxfds.rlim_max %d", (long)maxfds.rlim_max);
+ debugf("maxfds.rlim_cur %d", (long)maxfds.rlim_cur);
+ }
+#endif
+
return 0;
error:
+
my_perror("ERROR: udsserver_init");
return -1;
}
int udsserver_exit(void)
{
- close(listenfd);
- unlink(MDNS_UDS_SERVERPATH);
+ dnssd_close(listenfd);
+
+#if !defined(USE_TCP_LOOPBACK)
+ // Currently, we're unable to remove /var/run/mdnsd because we've changed to userid "nobody"
+ // to give up unnecessary privilege, but we need to be root to remove this Unix Domain Socket.
+ // It would be nice if we could find a solution to this problem
+ if (unlink(MDNS_UDS_SERVERPATH))
+ debugf("Unable to remove %s", MDNS_UDS_SERVERPATH);
+#endif
+
return 0;
}
-
mDNSs32 udsserver_idle(mDNSs32 nextevent)
{
request_state *req = all_requests, *tmp, *prev = NULL;
reply_state *fptr;
- transfer_state result;
- mDNSs32 now = mDNSPlatformTimeNow();
+ transfer_state result;
+ mDNSs32 now = mDNS_TimeNow(&mDNSStorage);
while(req)
{
result = t_uninitialized;
- if (req->u_err)
+ 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;
+ if (req->replies->next) req->replies->rhdr->flags |= dnssd_htonl(kDNSServiceFlagsMoreComing);
result = send_msg(req->replies);
if (result == t_complete)
{
}
}
if (result == t_morecoming)
- {
- if (!req->time_blocked) req->time_blocked = now;
- debugf("udsserver_idle: client has been blocked for %d seconds", now - req->time_blocked);
- if (now - req->time_blocked >= MAX_TIME_BLOCKED)
- {
- LogMsg("Could not write data to client after %d seconds - aborting connection", MAX_TIME_BLOCKED / mDNSPlatformOneSecond);
- abort_request(req);
- result = t_terminated;
- }
- else if (nextevent - now > mDNSPlatformOneSecond) nextevent = now + mDNSPlatformOneSecond; // try again in a second
- }
- if (result == t_terminated || result == t_error)
+ {
+ if (!req->time_blocked) req->time_blocked = now;
+ debugf("udsserver_idle: client has been blocked for %ld seconds", (now - req->time_blocked) / mDNSPlatformOneSecond);
+ if (now - req->time_blocked >= MAX_TIME_BLOCKED)
+ {
+ LogMsg("Could not write data to client %d after %ld seconds - aborting connection", req->sd, MAX_TIME_BLOCKED / mDNSPlatformOneSecond);
+ LogClientInfo(req);
+ abort_request(req);
+ result = t_terminated;
+ }
+ else if (nextevent - now > mDNSPlatformOneSecond) nextevent = now + mDNSPlatformOneSecond; // try again 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;
req = req->next;
freeL("udsserver_idle", tmp);
}
- else
+ else
{
prev = req;
req = req->next;
return nextevent;
}
-void udsserver_info(void)
+void udsserver_info(mDNS *const m)
{
+ mDNSs32 now = mDNS_TimeNow(m);
+ mDNSu32 CacheUsed = 0, CacheActive = 0;
+ mDNSu32 slot;
+ CacheGroup *cg;
+ CacheRecord *rr;
request_state *req;
- qlist_t *qlist;
- for (req = all_requests; req; req=req->next)
- {
- void *t = req->termination_context;
- if (!t) continue;
- if (req->terminate == regservice_termination_callback)
- LogMsgNoIdent("DNSServiceRegister %##s %u", ((registered_service *)t)->srs->RR_SRV.resrec.name.c, SRS_PORT(((registered_service *)t)->srs));
- else if (req->terminate == browse_termination_callback)
+
+ LogMsgNoIdent("Timenow 0x%08lX (%ld)", (mDNSu32)now, now);
+
+ for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
+ for(cg = m->rrcache_hash[slot]; cg; cg=cg->next)
{
- for (qlist = ((browse_termination_context *)t)->qlist; qlist; qlist = qlist->next)
- LogMsgNoIdent("DNSServiceBrowse %##s", qlist->q.qname.c);
+ CacheUsed++; // Count one cache entity for the CacheGroup object
+ for (rr = cg->members; rr; rr=rr->next)
+ {
+ mDNSs32 remain = rr->resrec.rroriginalttl - (now - rr->TimeRcvd) / mDNSPlatformOneSecond;
+ CacheUsed++;
+ if (rr->CRActiveQuestion) CacheActive++;
+ LogMsgNoIdent("%s%6ld %s%-6s%-6s%s",
+ rr->CRActiveQuestion ? "*" : " ", remain,
+ (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "-" : " ", DNSTypeName(rr->resrec.rrtype),
+ ((NetworkInterfaceInfo *)rr->resrec.InterfaceID)->ifname, CRDisplayString(m, rr));
+ usleep(1000); // Limit rate a little so we don't flood syslog too fast
+ }
}
- else if (req->terminate == resolve_termination_callback)
- LogMsgNoIdent("DNSServiceResolve %##s", ((resolve_termination_t *)t)->srv->question.qname.c);
- else if (req->terminate == question_termination_callback)
- LogMsgNoIdent("DNSServiceQueryRecord %##s", ((DNSQuestion *) t)->qname.c);
- else if (req->terminate == enum_termination_callback)
- LogMsgNoIdent("DNSServiceEnumerateDomains %##s", ((enum_termination_t *) t)->all->question.qname.c);
- }
- }
+ if (m->rrcache_totalused != CacheUsed)
+ LogMsgNoIdent("Cache use mismatch: rrcache_totalused is %lu, true count %lu", m->rrcache_totalused, CacheUsed);
+ if (m->rrcache_active != CacheActive)
+ LogMsgNoIdent("Cache use mismatch: rrcache_active is %lu, true count %lu", m->rrcache_active, CacheActive);
+ LogMsgNoIdent("Cache currently contains %lu records; %lu referenced by active questions", CacheUsed, CacheActive);
+
+ for (req = all_requests; req; req=req->next)
+ LogClientInfo(req);
+
+ now = mDNS_TimeNow(m);
+ LogMsgNoIdent("Timenow 0x%08lX (%ld)", (mDNSu32)now, now);
+ }
-static void rename_service(registered_service *srv)
+static void rename_service(service_instance *srv)
{
- mStatus err;
-
if (srv->autoname && !SameDomainLabel(srv->name.c, gmDNS->nicelabel.c))
{
srv->rename_on_memfree = 1;
- err = mDNS_DeregisterService(gmDNS, srv->srs);
- if (err) LogMsg("ERROR: udsserver_handle_configchange: DeregisterService returned error %d. Continuing.", err);
- // error should never occur - safest to log and continue
- }
+ if (mDNS_DeregisterService(gmDNS, &srv->srs)) // If service deregistered already, we can re-register immediately
+ regservice_callback(gmDNS, &srv->srs, mStatus_MemFree);
+ }
}
void udsserver_handle_configchange(void)
for (req = all_requests; req; req = req->next)
{
- if (req->servicepair.local) rename_service(req->servicepair.local);
- if (req->servicepair.global) rename_service(req->servicepair.global);
+ if (req->service_registration)
+ {
+ service_instance *ptr;
+ for (ptr = req->service_registration->instances; ptr; ptr = ptr->next)
+ rename_service(ptr);
+ }
}
}
-static void connect_callback(void *info _UNUSED)
+static void connect_callback(void *info)
{
- int sd, clilen, optval;
- struct sockaddr_un cliaddr;
+ dnssd_sock_t sd;
+ unsigned int len;
+ unsigned long optval;
+ dnssd_sockaddr_t cliaddr;
request_state *rstate;
-// int errpipe[2];
+ (void)info; // Unused
+
+ len = (int) sizeof(cliaddr);
- clilen = sizeof(cliaddr);
- sd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
+ sd = accept(listenfd, (struct sockaddr*) &cliaddr, &len);
- if (sd < 0)
+ if (sd == dnssd_InvalidSocket)
{
- if (errno == EWOULDBLOCK) return;
+ if (dnssd_errno() == dnssd_EWOULDBLOCK) return;
my_perror("ERROR: accept");
return;
}
optval = 1;
+
#ifdef SO_NOSIGPIPE
// Some environments (e.g. OS X) support turning off SIGPIPE for a socket
if (setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0)
{
- my_perror("ERROR: setsockopt - SOL_NOSIGPIPE - aborting client");
- close(sd);
+ my_perror("ERROR: setsockopt - SO_NOSIGPIPE - aborting client");
+ dnssd_close(sd);
return;
}
#endif
- 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)
+#if defined(_WIN32)
+ if (ioctlsocket(sd, FIONBIO, &optval) != 0)
+#else
+ if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
+#endif
{
- 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
+ my_perror("ERROR: fcntl(sd, F_SETFL, O_NONBLOCK) - aborting client");
+ dnssd_close(sd);
+ 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);
- }
+ if (!rstate) FatalError("ERROR: malloc");
bzero(rstate, sizeof(request_state));
rstate->ts = t_morecoming;
rstate->sd = sd;
- //rstate->errfd = errpipe[1];
+ LogOperation("%3d: Adding FD", rstate->sd);
if ( mStatus_NoError != udsSupportAddFDToEventLoop( sd, request_callback, rstate))
return;
rstate->next = all_requests;
all_requests = rstate;
}
-
// handler
static void request_callback(void *info)
- {
- request_state *rstate = info;
- transfer_state result;
- struct sockaddr_un cliaddr;
- char ctrl_path[MAX_CTLPATH];
-
- 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;
- }
-
- if (validate_message(rstate) < 0)
{
- // note that we cannot deliver an error message if validation fails, since the path to the error socket
- // may be contained in the (invalid) message body for some message types
- abort_request(rstate);
- unlink_request(rstate);
- LogMsg("Invalid message sent by client - may indicate a malicious program running on this machine!");
- return;
- }
-
- // check if client wants silent operation
- if (rstate->hdr.flags & IPC_FLAGS_NOREPLY) rstate->no_reply = 1;
+ request_state *rstate = info;
+ transfer_state result;
+ dnssd_sockaddr_t cliaddr;
+ int dedicated_error_socket;
+#if defined(_WIN32)
+ u_long opt = 1;
+#endif
+
+ 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;
+ }
- // 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);
- }
- }
+ 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;
+ }
+
+ if (validate_message(rstate) < 0)
+ {
+ // note that we cannot deliver an error message if validation fails, since the path to the error socket
+ // may be contained in the (invalid) message body for some message types
+ abort_request(rstate);
+ unlink_request(rstate);
+ LogMsg("Invalid message sent by client - may indicate a malicious program running on this machine!");
+ return;
+ }
+
+ // check if client wants silent operation
+ if (rstate->hdr.flags & IPC_FLAGS_NOREPLY) rstate->no_reply = 1;
-
+ dedicated_error_socket = (rstate->hdr.op == reg_record_request || rstate->hdr.op == add_record_request ||
+ rstate->hdr.op == update_record_request || rstate->hdr.op == remove_record_request);
+
+ if (((rstate->hdr.flags & IPC_FLAGS_REUSE_SOCKET) == 0) != dedicated_error_socket)
+ LogMsg("WARNING: client request %d with incorrect flags setting 0x%X", rstate->hdr.op, rstate->hdr.flags);
+ // check if primary socket is to be used for synchronous errors, else open new socket
+ if (dedicated_error_socket)
+ {
+ mStatus err = 0;
+ int nwritten;
+ int errfd = socket(AF_DNSSD, SOCK_STREAM, 0);
+ if (errfd == dnssd_InvalidSocket)
+ {
+ my_perror("ERROR: socket");
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
- 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);
- }
- }
+ #if defined(USE_TCP_LOOPBACK)
+ {
+ mDNSOpaque16 port;
+ port.b[0] = rstate->msgdata[0];
+ port.b[1] = rstate->msgdata[1];
+ rstate->msgdata += 2;
+ cliaddr.sin_family = AF_INET;
+ cliaddr.sin_port = port.NotAnInteger;
+ cliaddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR);
+ }
+ #else
+ {
+ char ctrl_path[MAX_CTLPATH];
+ 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);
+ }
+ #endif
+ if (connect(errfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)) < 0)
+ {
+ my_perror("ERROR: connect");
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+#if defined(_WIN32)
+ if (ioctlsocket(errfd, FIONBIO, &opt) != 0)
+#else
+ if (fcntl(errfd, F_SETFL, O_NONBLOCK) != 0)
+#endif
+ {
+ my_perror("ERROR: could not set control socket to non-blocking mode");
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+
+ switch(rstate->hdr.op)
+ {
+ case reg_record_request: err = handle_regrecord_request (rstate); break;
+ case add_record_request: err = handle_add_request (rstate); break;
+ case update_record_request: err = handle_update_request (rstate); break;
+ case remove_record_request: err = handle_removerecord_request(rstate); break;
+ default: LogMsg("%3d: ERROR: udsserver_recv_request - unsupported request type: %d", rstate->sd, rstate->hdr.op);
+ }
+
+ err = dnssd_htonl(err);
+ nwritten = send(errfd, &err, sizeof(err), 0);
+ // On a freshly-created Unix Domain Socket, the kernel should *never* fail to buffer a four-byte write for us.
+ // If not, we don't attempt to handle this failure, but we do log it.
+ if (nwritten < (int)sizeof(err))
+ LogMsg("ERROR: failed to write error response back to caller: %d %d %s",
+ nwritten, dnssd_errno(), dnssd_strerror(dnssd_errno()));
+ dnssd_close(errfd);
+ reset_connected_rstate(rstate); // Reset ready to accept the next request on this pipe
+ }
+ else
+ {
+ switch(rstate->hdr.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 reconfirm_record_request: handle_reconfirm_request (rstate); break;
+ case setdomain_request: handle_setdomain_request (rstate); break;
+ default: LogMsg("%3d: ERROR: udsserver_recv_request - unsupported request type: %d", rstate->sd, rstate->hdr.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.
if (ifi && !InterfaceID) goto bad_param;
q = mallocL("DNSQuestion", sizeof(DNSQuestion));
- if (!q)
- {
- my_perror("ERROR: handle_query - malloc");
- exit(1);
- }
- bzero(q, sizeof(DNSQuestion));
-
- if (!MakeDomainNameFromDNSNameString(&q->qname, name)) { freeL("DNSQuestion", q); goto bad_param; }
- q->QuestionContext = rstate;
+ if (!q) FatalError("ERROR: handle_query - malloc");
+ bzero(q, sizeof(DNSQuestion));
+
+ q->InterfaceID = InterfaceID;
+ q->Target = zeroAddr;
+ if (!MakeDomainNameFromDNSNameString(&q->qname, name)) { freeL("DNSQuestion", q); goto bad_param; }
+ q->qtype = rrtype;
+ q->qclass = rrclass;
+ q->LongLived = (flags & kDNSServiceFlagsLongLivedQuery) != 0;
+ q->ExpectUnique = mDNSfalse;
+ q->ForceMCast = (flags & kDNSServiceFlagsForceMulticast) != 0;
q->QuestionCallback = question_result_callback;
- q->qtype = rrtype;
- q->qclass = rrclass;
- q->InterfaceID = InterfaceID;
- q->Target = zeroAddr;
- if (flags & kDNSServiceFlagsLongLivedQuery) q->LongLived = mDNStrue;
+ q->QuestionContext = rstate;
+
rstate->termination_context = q;
rstate->terminate = question_termination_callback;
+ LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) START", rstate->sd, q->qname.c, DNSTypeName(q->qtype));
result = mDNS_StartQuery(gmDNS, q);
if (result != mStatus_NoError) LogMsg("ERROR: mDNS_StartQuery: %d", (int)result);
char name[256], regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME];
char *ptr; // message data pointer
domainname fqdn;
- resolve_t *srv, *txt;
resolve_termination_t *term;
mStatus err;
flags = get_flags(&ptr);
interfaceIndex = get_long(&ptr);
InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, interfaceIndex);
- if (interfaceIndex && !InterfaceID) goto bad_param;
+ if (interfaceIndex && !InterfaceID)
+ { LogMsg("ERROR: handle_resolve_request - Couldn't find InterfaceID for interfaceIndex %d", interfaceIndex); goto bad_param; }
if (get_string(&ptr, name, 256) < 0 ||
get_string(&ptr, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
get_string(&ptr, domain, MAX_ESCAPED_DOMAIN_NAME) < 0)
- goto bad_param;
+ { LogMsg("ERROR: handle_resolve_request - Couldn't read name/regtype/domain"); 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;
- srv->question.Target = zeroAddr;
-
- 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;
- txt->question.Target = zeroAddr;
+ { LogMsg("ERROR: handle_resolve_request - Couldn't build_domainname_from_strings “%s” “%s” “%s”", name, regtype, domain); goto bad_param; }
// set up termination info
term = mallocL("handle_resolve_request", sizeof(resolve_termination_t));
- if (!term) goto malloc_error;
- term->srv = srv;
- term->txt = txt;
+ bzero(term, sizeof(*term));
+ if (!term) FatalError("ERROR: malloc");
+
+ // format questions
+ term->qsrv.InterfaceID = InterfaceID;
+ term->qsrv.Target = zeroAddr;
+ memcpy(&term->qsrv.qname, &fqdn, MAX_DOMAIN_NAME);
+ term->qsrv.qtype = kDNSType_SRV;
+ term->qsrv.qclass = kDNSClass_IN;
+ term->qsrv.LongLived = mDNSfalse;
+ term->qsrv.ExpectUnique = mDNStrue;
+ term->qsrv.ForceMCast = mDNSfalse;
+ term->qsrv.QuestionCallback = resolve_result_callback;
+ term->qsrv.QuestionContext = rstate;
+
+ term->qtxt.InterfaceID = InterfaceID;
+ term->qtxt.Target = zeroAddr;
+ memcpy(&term->qtxt.qname, &fqdn, MAX_DOMAIN_NAME);
+ term->qtxt.qtype = kDNSType_TXT;
+ term->qtxt.qclass = kDNSClass_IN;
+ term->qtxt.LongLived = mDNSfalse;
+ term->qtxt.ExpectUnique = mDNStrue;
+ term->qtxt.ForceMCast = mDNSfalse;
+ term->qtxt.QuestionCallback = resolve_result_callback;
+ term->qtxt.QuestionContext = rstate;
+
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(gmDNS, &srv->question);
- if (!err) err = mDNS_StartQuery(gmDNS, &txt->question);
+ LogOperation("%3d: DNSServiceResolve(%##s) START", rstate->sd, term->qsrv.qname.c);
+ err = mDNS_StartQuery(gmDNS, &term->qsrv);
+ if (!err) err = mDNS_StartQuery(gmDNS, &term->qtxt);
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)
+ if (deliver_error(rstate, err) < 0 || err)
{
abort_request(rstate);
unlink_request(rstate);
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)
+ if (!term)
{
LogMsg("ERROR: resolve_termination_callback: double termination");
return;
}
rs = term->rstate;
+ LogOperation("%3d: DNSServiceResolve(%##s) STOP", rs->sd, term->qtxt.qname.c);
- mDNS_StopQuery(gmDNS, &term->txt->question);
- mDNS_StopQuery(gmDNS, &term->srv->question);
+ mDNS_StopQuery(gmDNS, &term->qtxt);
+ mDNS_StopQuery(gmDNS, &term->qsrv);
- 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 _UNUSED, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
+static void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
{
- int len = 0;
+ size_t len = 0;
char fullname[MAX_ESCAPED_DOMAIN_NAME], target[MAX_ESCAPED_DOMAIN_NAME];
char *data;
transfer_state result;
reply_state *rep;
request_state *rs = question->QuestionContext;
- resolve_result_t *res = rs->resolve_results;
+ resolve_termination_t *res = rs->termination_context;
+ (void)m; // Unused
+
+ LogOperation("%3d: DNSServiceResolve(%##s, %s) RESULT %s", rs->sd, question->qname.c, DNSTypeName(question->qtype), RRDisplayString(m, answer));
+
+ // This code used to do this trick of just keeping a copy of the pointer to
+ // the answer record in the cache, but the unicast query code doesn't currently
+ // put its answer records in the cache, so for now we can't do this.
if (!AddRecord)
{
- if (answer->rrtype == kDNSType_TXT && res->txt == answer) res->txt = mDNSNULL;
- if (answer->rrtype == kDNSType_SRV && res->srv == answer) res->srv = mDNSNULL;
+ // 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 (answer->rrtype == kDNSType_TXT) res->txt = answer;
+ // if (answer->rrtype == kDNSType_SRV) res->srv = answer;
+
+ if (answer->rrtype == kDNSType_SRV)
+ {
+ AssignDomainName(&res->target, &answer->rdata->u.srv.target);
+ res->port = answer->rdata->u.srv.port;
+ res->srv = mDNStrue;
+ }
+ if (answer->rrtype == kDNSType_TXT)
+ {
+ if (answer->rdlength > AbsoluteMaxDNSMessageData) return;
+ res->txtlen = answer->rdlength;
+ mDNSPlatformMemCopy(answer->rdata->u.data, res->txtdata, res->txtlen);
+ res->txt = mDNStrue;
+ }
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);
+ ConvertDomainNameToCString(answer->name, fullname);
+ ConvertDomainNameToCString(&res->target, target);
// calculate reply length
len += sizeof(DNSServiceFlags);
len += strlen(fullname) + 1;
len += strlen(target) + 1;
len += 2 * sizeof(uint16_t); // port, txtLen
- len += res->txt->rdlength;
+ len += res->txtlen;
// allocate/init reply header
rep = create_reply(resolve_reply, len, rs);
- rep->rhdr->flags = 0;
- rep->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, answer->InterfaceID);
- rep->rhdr->error = kDNSServiceErr_NoError;
+ rep->rhdr->flags = dnssd_htonl(0);
+ rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, answer->InterfaceID));
+ rep->rhdr->error = dnssd_htonl(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);
+ *data++ = res->port.b[0];
+ *data++ = res->port.b[1];
+ put_short(res->txtlen, &data);
+ put_rdata(res->txtlen, res->txtdata, &data);
result = send_msg(rep);
- if (result == t_error || result == t_terminated)
- {
- abort_request(rs);
+ if (result == t_error || result == t_terminated)
+ {
+ abort_request(rs);
unlink_request(rs);
- freeL("resolve_result_callback", rep);
+ freeL("resolve_result_callback", rep);
}
else if (result == t_complete) freeL("resolve_result_callback", rep);
else append_reply(rs, rep);
}
-
+
// 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 _UNUSED, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
+static void question_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
{
char *data;
char name[MAX_ESCAPED_DOMAIN_NAME];
- request_state *req;
+ request_state *req = question->QuestionContext;
reply_state *rep;
- int len;
+ size_t len;
+ (void)m; // Unused
+ LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) RESULT %s", req->sd, question->qname.c, DNSTypeName(question->qtype), RRDisplayString(m, answer));
//mDNS_StopQuery(m, question);
- req = question->QuestionContext;
// calculate reply data length
len = sizeof(DNSServiceFlags);
len += sizeof(DNSServiceErrorType);
len += 3 * sizeof(uint16_t); // type, class, rdlen
len += answer->rdlength;
- ConvertDomainNameToCString(&answer->name, name);
+ ConvertDomainNameToCString(answer->name, name);
len += strlen(name) + 1;
rep = create_reply(query_reply, len, req);
- rep->rhdr->flags = AddRecord ? kDNSServiceFlagsAdd : 0;
- rep->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, answer->InterfaceID);
- rep->rhdr->error = kDNSServiceErr_NoError;
+
+ rep->rhdr->flags = dnssd_htonl(AddRecord ? kDNSServiceFlagsAdd : 0);
+ rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, answer->InterfaceID));
+ rep->rhdr->error = dnssd_htonl(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_rdata(answer->rdlength, answer->rdata->u.data, &data);
put_long(AddRecord ? answer->rroriginalttl : 0, &data);
append_reply(req, rep);
static void question_termination_callback(void *context)
{
DNSQuestion *q = context;
-
-
+ LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) STOP", ((request_state *)q->QuestionContext)->sd, q->qname.c, DNSTypeName(q->qtype));
mDNS_StopQuery(gmDNS, q); // no need to error check
freeL("question_termination_callback", q);
}
+// If there's a comma followed by another character,
+// FindFirstSubType overwrites the comma with a nul and returns the pointer to the next character.
+// Otherwise, it returns a pointer to the final nul at the end of the string
+static char *FindFirstSubType(char *p)
+ {
+ while (*p)
+ {
+ if (p[0] == '\\' && p[1]) p += 2;
+ else if (p[0] == ',' && p[1]) { *p++ = 0; return(p); }
+ else p++;
+ }
+ return(p);
+ }
+
+// If there's a comma followed by another character,
+// FindNextSubType overwrites the comma with a nul and returns the pointer to the next character.
+// If it finds an illegal unescaped dot in the subtype name, it returns mDNSNULL
+// Otherwise, it returns a pointer to the final nul at the end of the string
+static char *FindNextSubType(char *p)
+ {
+ while (*p)
+ {
+ if (p[0] == '\\' && p[1]) // If escape character
+ p += 2; // ignore following character
+ else if (p[0] == ',') // If we found a comma
+ {
+ if (p[1]) *p++ = 0;
+ return(p);
+ }
+ else if (p[0] == '.')
+ return(mDNSNULL);
+ else p++;
+ }
+ return(p);
+ }
+
+// Returns -1 if illegal subtype found
+mDNSexport mDNSs32 ChopSubTypes(char *regtype)
+ {
+ mDNSs32 NumSubTypes = 0;
+ char *stp = FindFirstSubType(regtype);
+ while (stp && *stp) // If we found a comma...
+ {
+ if (*stp == ',') return(-1);
+ NumSubTypes++;
+ stp = FindNextSubType(stp);
+ }
+ if (!stp) return(-1);
+ return(NumSubTypes);
+ }
+
+mDNSexport AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p)
+ {
+ AuthRecord *st = mDNSNULL;
+ if (NumSubTypes)
+ {
+ mDNSs32 i;
+ st = mallocL("ServiceSubTypes", NumSubTypes * sizeof(AuthRecord));
+ if (!st) return(mDNSNULL);
+ for (i = 0; i < NumSubTypes; i++)
+ {
+ mDNS_SetupResourceRecord(&st[i], mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, mDNSNULL, mDNSNULL);
+ while (*p) p++;
+ p++;
+ if (!MakeDomainNameFromDNSNameString(st[i].resrec.name, p))
+ { freeL("ServiceSubTypes", st); return(mDNSNULL); }
+ }
+ }
+ return(st);
+ }
+
+#ifdef _HAVE_SETDOMAIN_SUPPORT_
+static void free_defdomain(mDNS *const m, AuthRecord *const rr, mStatus result)
+ {
+ (void)m; // unused
+ if (result == mStatus_MemFree) free(rr->RecordContext); // context is the enclosing list structure
+ }
+#endif
+
+static void handle_setdomain_request(request_state *request)
+ {
+ mStatus err = mStatus_NoError;
+ char *ptr;
+ char domainstr[MAX_ESCAPED_DOMAIN_NAME];
+ domainname domain;
+ DNSServiceFlags flags;
+#ifdef _HAVE_SETDOMAIN_SUPPORT_
+ struct xucred xuc;
+ socklen_t xuclen;
+#endif
+
+ if (request->ts != t_complete)
+ {
+ LogMsg("ERROR: handle_setdomain_request - transfer state != t_complete");
+ abort_request(request);
+ unlink_request(request);
+ return;
+ }
+
+ // extract flags/domain from message
+ ptr = request->msgdata;
+ flags = get_flags(&ptr);
+ if (get_string(&ptr, domainstr, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+ !MakeDomainNameFromDNSNameString(&domain, domainstr))
+ { err = mStatus_BadParamErr; goto end; }
+
+ freeL("handle_setdomain_request", request->msgbuf);
+ request->msgbuf = NULL;
+
+ debugf("%3d: DNSServiceSetDefaultDomainForUser(%##s)", request->sd, domain.c);
+
+#ifdef _HAVE_SETDOMAIN_SUPPORT_
+ // this functionality currently only used for Apple-specific configuration, so we don't burned other platforms by mandating
+ // the existence of this socket option
+ xuclen = sizeof(xuc);
+ if (getsockopt(request->sd, 0, LOCAL_PEERCRED, &xuc, &xuclen))
+ { my_perror("ERROR: getsockopt, LOCAL_PEERCRED"); err = mStatus_UnknownErr; goto end; }
+ if (xuc.cr_version != XUCRED_VERSION) { LogMsg("getsockopt, LOCAL_PEERCRED - bad version"); err = mStatus_UnknownErr; goto end; }
+ LogMsg("Default domain %s %s for UID %d", domainstr, flags & kDNSServiceFlagsAdd ? "set" : "removed", xuc.cr_uid);
+
+ if (flags & kDNSServiceFlagsAdd)
+ {
+ // register a local-only PRT record
+ default_browse_list_t *newelem = malloc(sizeof(default_browse_list_t));
+ if (!newelem) { LogMsg("ERROR: malloc"); err = mStatus_NoMemoryErr; goto end; }
+ mDNS_SetupResourceRecord(&newelem->ptr_rec, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, free_defdomain, newelem);
+ MakeDomainNameFromDNSNameString(&newelem->ptr_rec.resrec.name, mDNS_DomainTypeNames[mDNS_DomainTypeBrowseDefault]);
+ AppendDNSNameString (&newelem->ptr_rec.resrec.name, "local");
+ AssignDomainName(&newelem->ptr_rec.resrec.rdata->u.name, &domain);
+ newelem->uid = xuc.cr_uid;
+ err = mDNS_Register(gmDNS, &newelem->ptr_rec);
+ if (err) free(newelem);
+ else
+ {
+ // link into list
+ newelem->next = default_browse_list;
+ default_browse_list = newelem;
+ }
+
+ }
+ else
+ {
+ // remove - find in list, deregister
+ default_browse_list_t *ptr = default_browse_list, *prev = NULL;
+ while (ptr)
+ {
+ if (SameDomainName(&ptr->ptr_rec.resrec.rdata->u.name, &domain))
+ {
+ if (prev) prev->next = ptr->next;
+ else default_browse_list = ptr->next;
+ err = mDNS_Deregister(gmDNS, &ptr->ptr_rec);
+ break;
+ }
+ prev = ptr;
+ ptr = ptr->next;
+ }
+ if (!ptr) { LogMsg("Attempt to remove nonexistent domain %s for UID %d", domainstr, xuc.cr_uid); err = mStatus_Invalid; }
+ }
+#else
+ err = mStatus_NoError;
+#endif // _HAVE_SETDOMAIN_SUPPORT_
+
+ end:
+ deliver_error(request, err);
+ abort_request(request);
+ unlink_request(request);
+ }
+
+static mStatus add_domain_to_browser(browser_info_t *info, const domainname *d)
+ {
+ browser_t *b, *p;
+ mStatus err;
+
+ for (p = info->browsers; p; p = p->next)
+ {
+ if (SameDomainName(&p->domain, d))
+ { debugf("add_domain_to_browser - attempt to add domain %##d already in list", d->c); return mStatus_AlreadyRegistered; }
+ }
+
+ b = mallocL("browser_t", sizeof(*b));
+ if (!b) return mStatus_NoMemoryErr;
+ AssignDomainName(&b->domain, d);
+ err = mDNS_StartBrowse(gmDNS, &b->q, &info->regtype, d, info->interface_id, info->ForceMCast, browse_result_callback, info->rstate);
+ if (err)
+ {
+ LogMsg("mDNS_StartBrowse returned %d for type %##s domain %##s", err, info->regtype.c, d->c);
+ freeL("browser_t", b);
+ }
+ else
+ {
+ b->next = info->browsers;
+ info->browsers = b;
+ }
+ return err;
+ }
static void handle_browse_request(request_state *request)
{
uint32_t interfaceIndex;
mDNSInterfaceID InterfaceID;
char regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME];
- qlist_t *qlist = NULL, *qlist_elem;
- domainname typedn;
+ domainname typedn, d, temp;
+ mDNSs32 NumSubTypes;
char *ptr;
- mStatus result;
- DNameListElem *search_domain_list, *sdom, tmp;
- browse_termination_context *term;
+ mStatus err = mStatus_NoError;
+ DNameListElem *search_domain_list, *sdom;
+ browser_info_t *info = NULL;
if (request->ts != t_complete)
{
ptr = request->msgdata;
flags = get_flags(&ptr);
interfaceIndex = get_long(&ptr);
- if (get_string(&ptr, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+ if (get_string(&ptr, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
get_string(&ptr, domain, MAX_ESCAPED_DOMAIN_NAME) < 0)
- goto bad_param;
+ { err = mStatus_BadParamErr; goto error; }
freeL("handle_browse_request", request->msgbuf);
request->msgbuf = NULL;
InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, interfaceIndex);
- if (interfaceIndex && !InterfaceID) goto bad_param;
+ if (interfaceIndex && !InterfaceID) { err = mStatus_BadParamErr; goto error; }
+
+ typedn.c[0] = 0;
+ NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes
+ if (NumSubTypes < 0 || NumSubTypes > 1) { err = mStatus_BadParamErr; goto error; }
+ if (NumSubTypes == 1 && !AppendDNSNameString(&typedn, regtype + strlen(regtype) + 1))
+ { err = mStatus_BadParamErr; goto error; }
+
+ if (!regtype[0] || !AppendDNSNameString(&typedn, regtype)) { err = mStatus_BadParamErr; goto error; }
+
+ if (!MakeDomainNameFromDNSNameString(&temp, regtype)) { err = mStatus_BadParamErr; goto error; }
+ if (temp.c[0] > 15 && domain[0] == 0) strcpy(domain, "local."); // For over-long service types, we only allow domain "local"
- if (!MakeDomainNameFromDNSNameString(&typedn, regtype)) goto bad_param;
+ // allocate and set up browser info
+ info = mallocL("browser_info_t", sizeof(*info));
+ if (!info) { err = mStatus_NoMemoryErr; goto error; }
- //!!!KRS browse locally for ichat
- if (!domain[0] && (!strcmp(regtype, "_ichat._tcp.") || !strcmp(regtype, "_presence._tcp.")))
- strcpy(domain,"local.");
+ request->browser_info = info;
+ info->ForceMCast = (flags & kDNSServiceFlagsForceMulticast) != 0;
+ info->interface_id = InterfaceID;
+ AssignDomainName(&info->regtype, &typedn);
+ info->rstate = request;
+ info->default_domain = !domain[0];
+ info->browsers = NULL;
+
+ // setup termination context
+ request->termination_context = info;
+ request->terminate = browse_termination_callback;
+ LogOperation("%3d: DNSServiceBrowse(\"%##s\", \"%s\") START", request->sd, info->regtype.c, domain);
if (domain[0])
{
- // generate a fake list of one elem to reduce number of code paths
- if (!MakeDomainNameFromDNSNameString(&tmp.name, domain)) goto bad_param;
- tmp.next = NULL;
- search_domain_list = &tmp;
+ if (!MakeDomainNameFromDNSNameString(&d, domain)) { err = mStatus_BadParamErr; goto error; }
+ err = add_domain_to_browser(info, &d);
}
- else search_domain_list = mDNSPlatformGetSearchDomainList();
- for (sdom = search_domain_list; sdom; sdom = sdom->next)
+ else
{
- qlist_elem = mallocL("handle_browse_request", sizeof(qlist_t));
- if (!qlist_elem)
- {
- my_perror("ERROR: handle_browse_request - malloc");
- exit(1);
- }
- bzero(qlist_elem, sizeof(qlist_t));
- qlist_elem->q.QuestionContext = request;
- qlist_elem->q.QuestionCallback = browse_result_callback;
- qlist_elem->next = qlist;
- qlist = qlist_elem;
- }
-
- // setup termination context
- term = (browse_termination_context *)mallocL("handle_browse_request", sizeof(browse_termination_context));
- if (!term)
- {
- my_perror("ERROR: handle_browse_request - malloc");
- exit(1);
- }
- term->qlist = qlist;
- term->rstate = request;
- request->termination_context = term;
- request->terminate = browse_termination_callback;
-
- // start the browses
- sdom = search_domain_list;
- for (qlist_elem = qlist; qlist_elem; qlist_elem = qlist_elem->next)
- {
- result = mDNS_StartBrowse(gmDNS, &qlist_elem->q, &typedn, &sdom->name, InterfaceID, browse_result_callback, request);
- if (result)
+ search_domain_list = mDNSPlatformGetSearchDomainList();
+ for (sdom = search_domain_list; sdom; sdom = sdom->next)
{
- // bail here on error. questions not yet issued are in no core lists, so they can be deallocated lazily
- if (search_domain_list != &tmp) mDNS_FreeDNameList(search_domain_list);
- deliver_error(request, result);
- return;
+ err = add_domain_to_browser(info, &sdom->name);
+ if (err)
+ {
+ if (SameDomainName(&sdom->name, &localdomain)) break;
+ else err = mStatus_NoError; // suppress errors for non-local "default" domains
+ }
+
}
- sdom = sdom->next;
+ mDNS_FreeDNameList(search_domain_list);
}
- if (search_domain_list != &tmp) mDNS_FreeDNameList(search_domain_list);
- deliver_error(request, mStatus_NoError);
+
+ deliver_error(request, err);
return;
-bad_param:
- deliver_error(request, mStatus_BadParamErr);
+error:
+ if (info) freeL("browser_info_t", info);
+ if (request->termination_context) request->termination_context = NULL;
+ deliver_error(request, err);
abort_request(request);
unlink_request(request);
}
-static void browse_result_callback(mDNS *const m _UNUSED, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
+static void browse_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
{
- request_state *req;
+ request_state *req = question->QuestionContext;
reply_state *rep;
mStatus err;
-
- req = question->QuestionContext;
+ (void)m; // Unused
+ LogOperation("%3d: DNSServiceBrowse(%##s, %s) RESULT %s %s",
+ req->sd, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "Add" : "Rmv", RRDisplayString(m, answer));
err = gen_rr_response(&answer->rdata->u.name, answer->InterfaceID, req, &rep);
if (err)
{
- if (deliver_async_error(req, browse_reply, err) < 0)
+ 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
+ if (AddRecord) rep->rhdr->flags |= dnssd_htonl(kDNSServiceFlagsAdd); // non-zero TTL indicates add
append_reply(req, rep);
return;
}
static void browse_termination_callback(void *context)
{
- browse_termination_context *t = context;
- qlist_t *ptr, *fptr;
+ browser_info_t *info = context;
+ browser_t *ptr;
- if (!t) return;
+ if (!info) return;
- ptr = t->qlist;
- t->qlist = NULL;
-
- while(ptr)
+ while(info->browsers)
{
+ ptr = info->browsers;
+ info->browsers = ptr->next;
+ LogOperation("%3d: DNSServiceBrowse(%##s) STOP", info->rstate->sd, ptr->q.qname.c);
mDNS_StopBrowse(gmDNS, &ptr->q); // no need to error-check result
- fptr = ptr;
- ptr = ptr->next;
- freeL("browse_termination_callback", fptr);
+ freeL("browse_termination_callback", ptr);
}
- t->rstate->termination_context = NULL;
- freeL("browse_termination_callback", t);
+
+ info->rstate->termination_context = NULL;
+ freeL("browser_info", info);
}
-static mStatus register_service(request_state *request, registered_service **srv_ptr, DNSServiceFlags flags,
- uint16_t txtlen, void *txtdata, mDNSIPPort port, domainlabel *n, char *type_as_string,
- domainname *t, domainname *d, domainname *h, mDNSBool autoname, int num_subtypes, mDNSInterfaceID InterfaceID)
+mDNSexport void udsserver_default_browse_domain_changed(const domainname *d, mDNSBool add)
{
- registered_service *r_srv;
- int srs_size, i;
- char *sub;
- mStatus result;
- *srv_ptr = NULL;
+ request_state *r;
+
+ for (r = all_requests; r; r = r->next)
+ {
+ browser_info_t *info = r->browser_info;
+
+ if (!info || !info->default_domain) continue;
+ if (add) add_domain_to_browser(info, d);
+ else
+ {
+ browser_t **ptr = &info->browsers;
+ while (*ptr)
+ {
+ if (SameDomainName(&(*ptr)->domain, d))
+ {
+ browser_t *remove = *ptr;
+ *ptr = (*ptr)->next;
+ if (remove->q.LongLived)
+ {
+ // give goodbyes for known answers.
+ // note that since events are sent to client via udsserver_idle(), we don't need to worry about the question being cancelled mid-loop
+ CacheRecord *ka = remove->q.uDNS_info.knownAnswers;
+ while (ka) { remove->q.QuestionCallback(gmDNS, &remove->q, &ka->resrec, mDNSfalse); ka = ka->next; }
+ }
+ mDNS_StopBrowse(gmDNS, &remove->q);
+ freeL("browser_t", remove);
+ return;
+ }
+ ptr = &(*ptr)->next;
+ }
+ LogMsg("Requested removal of default domain %##s not in list for sd %d", d->c, r->sd);
+ }
+ }
+ }
+
+// Count how many other service records we have locally with the same name, but different rdata.
+// For auto-named services, we can have at most one per machine -- if we allowed two auto-named services of
+// the same type on the same machine, we'd get into an infinite autoimmune-response loop of continuous renaming.
+mDNSexport int CountPeerRegistrations(mDNS *const m, ServiceRecordSet *const srs)
+ {
+ int count = 0;
+ ResourceRecord *r = &srs->RR_SRV.resrec;
+ AuthRecord *rr;
+ ServiceRecordSet *s;
- 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 = type_as_string + strlen(type_as_string) + 1;
- for (i = 0; i < num_subtypes; i++)
- {
- if (!MakeDomainNameFromDNSNameString(&(r_srv->subtypes + i)->resrec.name, sub))
- {
- free_service_registration(r_srv);
- return mStatus_BadParamErr;
- }
- sub += strlen(sub) + 1;
- }
- }
- else r_srv->subtypes = NULL;
- r_srv->request = request;
-
- r_srv->extras = NULL;
- r_srv->autoname = autoname;
- r_srv->rename_on_memfree = 0;
- r_srv->renameonconflict = !(flags & kDNSServiceFlagsNoAutoRename);
- memcpy(r_srv->name.c, n->c, n->c[0]);
+ for (rr = m->ResourceRecords; rr; rr=rr->next)
+ if (rr->resrec.rrtype == kDNSType_SRV && SameDomainName(rr->resrec.name, r->name) && !SameRData(&rr->resrec, r))
+ count++;
- result = mDNS_RegisterService(gmDNS, r_srv->srs, n, t, d, h, port,
- txtdata, txtlen, r_srv->subtypes, num_subtypes, InterfaceID, regservice_callback, r_srv);
+ for (rr = m->uDNS_info.RecordRegistrations; rr; rr=rr->next)
+ if (rr->uDNS_info.state != regState_Unregistered && rr->resrec.rrtype == kDNSType_SRV && SameDomainName(rr->resrec.name, r->name) && !SameRData(&rr->resrec, r))
+ count++;
- if (result)
- free_service_registration(r_srv);
- else *srv_ptr = r_srv;
+ for (s = m->uDNS_info.ServiceRegistrations; s; s = s->next)
+ if (s->uDNS_info.state != regState_Unregistered && SameDomainName(s->RR_SRV.resrec.name, r->name) && !SameRData(&s->RR_SRV.resrec, r))
+ count++;
+
+ verbosedebugf("%d peer registrations for %##s", count, r->name->c);
+ return(count);
+ }
- return result;
+mDNSexport int CountExistingRegistrations(domainname *srv, mDNSIPPort port)
+ {
+ int count = 0;
+ AuthRecord *rr;
+ for (rr = gmDNS->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++;
+ return(count);
+ }
+
+static mStatus register_service_instance(request_state *request, const domainname *domain)
+ {
+ service_info *info = request->service_registration;
+ service_instance *ptr, *instance;
+ int instance_size;
+ mStatus result;
-malloc_error:
- my_perror("ERROR: malloc");
- exit(1);
+ for (ptr = info->instances; ptr; ptr = ptr->next)
+ {
+ if (SameDomainName(&ptr->domain, domain))
+ { LogMsg("register_service_instance: domain %##s already registered", domain->c); return mStatus_AlreadyRegistered; }
+ }
+
+ instance_size = sizeof(*instance);
+ if (info->txtlen > sizeof(RDataBody)) instance_size += (info->txtlen - sizeof(RDataBody));
+ instance = mallocL("service_instance", instance_size);
+ if (!instance) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; }
+
+ instance->subtypes = AllocateSubTypes(info->num_subtypes, info->type_as_string);
+ if (info->num_subtypes && !instance->subtypes)
+ { free_service_instance(instance); instance = NULL; FatalError("ERROR: malloc"); }
+ instance->request = request;
+ instance->sd = request->sd;
+ instance->autoname = info->autoname;
+ instance->autorename = info->autorename;
+ instance->allowremotequery = info->allowremotequery;
+ instance->rename_on_memfree = 0;
+ instance->name = info->name;
+ AssignDomainName(&instance->domain, domain);
+ instance->default_local = (info->default_domain && SameDomainName(domain, &localdomain));
+ result = mDNS_RegisterService(gmDNS, &instance->srs, &instance->name, &info->type, domain, info->host.c[0] ? &info->host : NULL, info->port,
+ info->txtdata, info->txtlen, instance->subtypes, info->num_subtypes, info->InterfaceID, regservice_callback, instance);
+
+ if (result) free_service_instance(instance);
+ else
+ {
+ instance->next = info->instances;
+ info->instances = instance;
+ }
+ return result;
}
+mDNSexport void udsserver_default_reg_domain_changed(const domainname *d, mDNSBool add)
+ {
+ request_state *rstate;
+ service_info *info;
+
+ LogMsg("%s registration domain %##s", add ? "Adding" : "Removing", d->c);
+ for (rstate = all_requests; rstate; rstate = rstate->next)
+ {
+ if (rstate->terminate != regservice_termination_callback) continue;
+ info = rstate->service_registration;
+ if (!info) { LogMsg("udsserver_default_reg_domain_changed - NULL service info"); continue; } // this should never happen
+ if (!info->default_domain) continue;
+
+ // valid default registration
+ if (add) register_service_instance(rstate, d);
+ else
+ {
+ // find the instance to remove
+ service_instance *si = rstate->service_registration->instances, *prev = NULL;
+ while (si)
+ {
+ if (SameDomainName(&si->domain, d))
+ {
+ mStatus err;
+ if (prev) prev->next = si->next;
+ else info->instances = si->next;
+ err = mDNS_DeregisterService(gmDNS, &si->srs);
+ if (err)
+ {
+ LogMsg("udsserver_default_reg_domain_changed - mDNS_DeregisterService err %d", err);
+ free_service_instance(si);
+ }
+ break;
+ }
+ prev = si;
+ si = si->next;
+ }
+ if (!si) debugf("udsserver_default_reg_domain_changed - domain %##s not registered", d->c); // normal if registration failed
+ }
+ }
+ }
// service registration
static void handle_regservice_request(request_state *request)
{
DNSServiceFlags flags;
uint32_t ifi;
- char name[256], regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME], host[MAX_ESCAPED_DOMAIN_NAME];
- uint16_t txtlen;
- mDNSIPPort port;
- void *txtdata;
- char *ptr, *sub;
- domainlabel n;
- domainname d, h, t, srv;
+ char name[1024]; // Lots of spare space for extra-long names that we'll auto-truncate down to 63 bytes
+ char domain[MAX_ESCAPED_DOMAIN_NAME], host[MAX_ESCAPED_DOMAIN_NAME];
+ char *ptr;
+ domainname d, srv;
mStatus result;
- mDNSInterfaceID InterfaceID;
- int num_subtypes;
- char *rtype_ptr;
-
+ service_info *service = NULL;
+
if (request->ts != t_complete)
{
LogMsg("ERROR: handle_regservice_request - transfer state != t_complete");
return;
}
+ service = mallocL("service_info", sizeof(*service));
+ if (!service) { my_perror("ERROR: malloc"); result = mStatus_NoMemoryErr; goto finish; }
+
+ service->instances = NULL;
+ service->request = request;
+ request->service_registration = service;
+ request->termination_context = request->service_registration;
+ request->terminate = regservice_termination_callback;
+
// extract data from message
ptr = request->msgdata;
flags = get_flags(&ptr);
ifi = get_long(&ptr);
- InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, ifi);
- if (ifi && !InterfaceID) goto bad_param;
- if (get_string(&ptr, name, 256) < 0 ||
- get_string(&ptr, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+ service->InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, ifi);
+ if (ifi && !service->InterfaceID)
+ { LogMsg("ERROR: handle_regservice_request - Couldn't find InterfaceID for interfaceIndex %d", ifi); goto bad_param; }
+ if (get_string(&ptr, name, sizeof(name)) < 0 ||
+ get_string(&ptr, service->type_as_string, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
get_string(&ptr, domain, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
get_string(&ptr, host, MAX_ESCAPED_DOMAIN_NAME) < 0)
- goto bad_param;
+ { LogMsg("ERROR: handle_regservice_request - Couldn't read name/regtype/domain"); goto bad_param; }
- port.NotAnInteger = get_short(&ptr);
- txtlen = get_short(&ptr);
- txtdata = get_rdata(&ptr, txtlen);
-
- if (!*regtype || !MakeDomainNameFromDNSNameString(&t, regtype)) goto bad_param;
+ service->port.b[0] = *ptr++;
+ service->port.b[1] = *ptr++;
- // 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 = (gmDNS)->nicelabel;
- else if (!MakeDomainLabelFromLiteralString(&n, name))
- goto bad_param;
-
- if ((!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) ||
- (!ConstructServiceName(&srv, &n, &t, &d)))
- goto bad_param;
+ service->txtlen = get_short(&ptr);
+ if (service->txtlen)
+ {
+ service->txtdata = mallocL("txtdata", service->txtlen);
+ if (!service->txtdata) { my_perror("ERROR: malloc"); result = mStatus_NoMemoryErr; goto finish; }
+ memcpy(service->txtdata, get_rdata(&ptr, service->txtlen), service->txtlen);
+ }
+ else service->txtdata = NULL;
- if (host[0] && !MakeDomainNameFromDNSNameString(&h, host)) goto bad_param;
+ // Check for sub-types after the service type
+ service->num_subtypes = ChopSubTypes(service->type_as_string); // Note: Modifies regtype string to remove trailing subtypes
+ if (service->num_subtypes < 0)
+ { LogMsg("ERROR: handle_regservice_request - ChopSubTypes failed %s", service->type_as_string); goto bad_param; }
- result = register_service(request, &request->servicepair.local, flags, txtlen, txtdata, port, &n, ®type[0], &t, &d, host[0] ? &h : NULL, !name[0], num_subtypes, InterfaceID);
+ // Don't try to construct "domainname t" until *after* ChopSubTypes has worked its magic
+ if (!*service->type_as_string || !MakeDomainNameFromDNSNameString(&service->type, service->type_as_string))
+ { LogMsg("ERROR: handle_regservice_request - service->type_as_string bad %s", service->type_as_string); goto bad_param; }
- //!!!KRS if we got a dynamic reg domain from the config file, use it for default (except for iChat)
- if (!domain[0] && gmDNS->uDNS_info.ServiceRegDomain[0] && strcmp(regtype, "_presence._tcp.") && strcmp(regtype, "_ichat._tcp."))
+ if (!name[0])
+ {
+ service->name = (gmDNS)->nicelabel;
+ service->autoname = mDNStrue;
+ }
+ else
+ {
+ // If the client is allowing AutoRename, then truncate name to legal length before converting it to a DomainLabel
+ if ((flags & kDNSServiceFlagsNoAutoRename) == 0)
+ {
+ int newlen = TruncateUTF8ToLength((mDNSu8*)name, mDNSPlatformStrLen(name), MAX_DOMAIN_LABEL);
+ name[newlen] = 0;
+ }
+ if (!MakeDomainLabelFromLiteralString(&service->name, name))
+ { LogMsg("ERROR: handle_regservice_request - name bad %s", name); goto bad_param; }
+ service->autoname = mDNSfalse;
+ }
+
+ if (*domain)
{
- MakeDomainNameFromDNSNameString(&d, gmDNS->uDNS_info.ServiceRegDomain);
- register_service(request, &request->servicepair.global, flags, txtlen, txtdata, port, &n, ®type[0], &t, &d, host[0] ? &h : NULL, !name[0], num_subtypes, InterfaceID);
- // don't return default global errors - it will confuse legacy clients, and we want .local to still work for them
+ service->default_domain = mDNSfalse;
+ if (!MakeDomainNameFromDNSNameString(&d, domain))
+ { LogMsg("ERROR: handle_regservice_request - domain bad %s", domain); goto bad_param; }
}
+ else
+ {
+ service->default_domain = mDNStrue;
+ MakeDomainNameFromDNSNameString(&d, "local.");
+ }
+
+ if (!ConstructServiceName(&srv, &service->name, &service->type, &d))
+ { LogMsg("ERROR: handle_regservice_request - Couldn't ConstructServiceName from, “%#s” “%##s” “%##s”", service->name.c, service->type.c, d.c); goto bad_param; }
- request->termination_context = &request->servicepair;
- request->terminate = regservice_termination_callback;
-
+ if (!MakeDomainNameFromDNSNameString(&service->host, host))
+ { LogMsg("ERROR: handle_regservice_request - host bad %s", host); goto bad_param; }
+ service->autorename = (flags & kDNSServiceFlagsNoAutoRename ) == 0;
+ service->allowremotequery = (flags & kDNSServiceFlagsAllowRemoteQuery) != 0;
+
+ // 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 (service->port.NotAnInteger)
+ {
+ int count = CountExistingRegistrations(&srv, service->port);
+ if (count)
+ LogMsg("Client application registered %d identical instances of service %##s port %u.",
+ count+1, srv.c, mDNSVal16(service->port));
+ }
+
+ LogOperation("%3d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", \"%s\", %u) START",
+ request->sd, name, service->type_as_string, domain, host, mDNSVal16(service->port));
+ result = register_service_instance(request, &d);
+
+ if (!result && !*domain)
+ {
+ DNameListElem *ptr, *def_domains = mDNSPlatformGetRegDomainList();
+ for (ptr = def_domains; ptr; ptr = ptr->next)
+ register_service_instance(request, &ptr->name);
+ // note that we don't report errors for non-local, non-explicit domains
+ mDNS_FreeDNameList(def_domains);
+ }
+
+finish:
deliver_error(request, result);
- if (result != mStatus_NoError)
+ if (result != mStatus_NoError)
{
abort_request(request);
unlink_request(request);
}
- else
- reset_connected_rstate(request); // reset to receive add/remove messages
+ else
+ reset_connected_rstate(request); // prepare to receive add/remove messages
return;
bad_param:
+ //if (service) freeL("service_info", service); Don't think we should do this -- abort_request will free it a second time and crash
deliver_error(request, mStatus_BadParamErr);
abort_request(request);
unlink_request(request);
}
-
-
-
// 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 _UNUSED, ServiceRecordSet *const srs, mStatus result)
+static void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result)
{
mStatus err;
- registered_service *r_srv = srs->ServiceContext;
- request_state *rs = r_srv->request;
-
- 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;
- }
+ mDNSBool SuppressError = mDNSfalse;
+ service_instance *instance = srs->ServiceContext;
+ (void)m; // Unused
+ if (!srs) { LogMsg("regservice_callback: srs is NULL %d", result); return; }
+ if (!instance) { LogMsg("regservice_callback: srs->ServiceContext is NULL %d", result); return; }
+
+ if (instance->request && instance->request->service_registration)
+ {
+ service_info *info = instance->request->service_registration;
+ if (info->default_domain && !instance->default_local) SuppressError = mDNStrue;
+ // don't send errors up to client for wide-area, empty-string registrations
+ }
+
+ if (result == mStatus_NoError)
+ LogOperation("%3d: DNSServiceRegister(%##s, %u) REGISTERED ", instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port));
+ else if (result == mStatus_MemFree)
+ LogOperation("%3d: DNSServiceRegister(%##s, %u) DEREGISTERED", instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port));
+ else if (result == mStatus_NameConflict)
+ LogOperation("%3d: DNSServiceRegister(%##s, %u) NAME CONFLICT", instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port));
+ else
+ LogOperation("%3d: DNSServiceRegister(%##s, %u) CALLBACK %d", instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port), result);
if (result == mStatus_NoError)
- return process_service_registration(srs);
+ {
+ if (instance->allowremotequery)
+ {
+ ExtraResourceRecord *e;
+ srs->RR_ADV.AllowRemoteQuery = mDNStrue;
+ srs->RR_PTR.AllowRemoteQuery = mDNStrue;
+ srs->RR_SRV.AllowRemoteQuery = mDNStrue;
+ srs->RR_TXT.AllowRemoteQuery = mDNStrue;
+ for (e = instance->srs.Extras; e; e = e->next) e->r.AllowRemoteQuery = mDNStrue;
+ }
+ process_service_registration(srs, SuppressError);
+ if (instance->autoname && CountPeerRegistrations(m, srs) == 0)
+ RecordUpdatedNiceLabel(m, 0); // Successfully got new name, tell user immediately
+ return;
+ }
else if (result == mStatus_MemFree)
{
- if (r_srv->rename_on_memfree)
+ if (instance->rename_on_memfree)
{
- r_srv->rename_on_memfree = 0;
- r_srv->name = gmDNS->nicelabel;
- err = mDNS_RenameAndReregisterService(gmDNS, srs, &r_srv->name);
- if (err) LogMsg("ERROR: regservice_callback - RenameAndReregisterService returned %d", err);
+ instance->rename_on_memfree = 0;
+ instance->name = gmDNS->nicelabel;
+ err = mDNS_RenameAndReregisterService(gmDNS, srs, &instance->name);
+ if (err) LogMsg("ERROR: regservice_callback - RenameAndReregisterService returned %ld", err);
// error should never happen - safest to log and continue
}
- else
+ else
{
- free_service_registration(r_srv);
+ free_service_instance(instance);
return;
}
}
else if (result == mStatus_NameConflict)
{
- if (r_srv->autoname || r_srv->renameonconflict)
+ if (instance->autoname && CountPeerRegistrations(m, srs) == 0)
+ {
+ // On conflict for an autoname service, rename and reregister *all* autoname services
+ IncrementLabelSuffix(&m->nicelabel, mDNStrue);
+ m->MainCallback(m, mStatus_ConfigChanged);
+ }
+ else if (instance->autoname || instance->autorename)
{
mDNS_RenameAndReregisterService(gmDNS, srs, mDNSNULL);
return;
}
else
{
- free_service_registration(r_srv);
- if (deliver_async_error(rs, reg_service_reply, result) < 0)
+ request_state *rs = instance->request;
+ if (!rs) { LogMsg("ERROR: regservice_callback: received result %ld with a NULL request pointer", result); return; }
+ free_service_instance(instance);
+ if (!SuppressError && deliver_async_error(rs, reg_service_reply, result) < 0)
{
abort_request(rs);
unlink_request(rs);
}
return;
}
- }
- else
+ }
+ else
{
- LogMsg("ERROR: unknown result in regservice_callback: %d", result);
- if (deliver_async_error(rs, reg_service_reply, result) < 0)
+ request_state *rs = instance->request;
+ if (!rs) { LogMsg("ERROR: regservice_callback: received result %ld with a NULL request pointer", result); return; }
+ if (result != mStatus_NATTraversal) LogMsg("ERROR: unknown result in regservice_callback: %ld", result);
+ free_service_instance(instance);
+ if (!SuppressError && deliver_async_error(rs, reg_service_reply, result) < 0)
{
abort_request(rs);
unlink_request(rs);
}
}
-static mStatus add_record_to_service(request_state *rstate, registered_service *r_srv, uint16_t rrtype, uint16_t rdlen, char *rdata, uint32_t ttl)
+mDNSexport void FreeExtraRR(mDNS *const m, AuthRecord *const rr, mStatus result)
+ {
+ ExtraResourceRecord *extra = (ExtraResourceRecord *)rr->RecordContext;
+ (void)m; //unused
+
+ if (result != mStatus_MemFree) { LogMsg("Error: FreeExtraRR invoked with unexpected error %d", result); return; }
+
+ debugf("%##s: MemFree", rr->resrec.name->c);
+ if (rr->resrec.rdata != &rr->rdatastorage)
+ freeL("Extra RData", rr->resrec.rdata);
+ freeL("ExtraResourceRecord", extra);
+ }
+
+static mStatus add_record_to_service(request_state *rstate, service_instance *instance, uint16_t rrtype, uint16_t rdlen, char *rdata, uint32_t ttl)
{
- ServiceRecordSet *srs = r_srv->srs;
+ ServiceRecordSet *srs = &instance->srs;
ExtraResourceRecord *extra;
- extra_record_entry *ere;
mStatus result;
int size;
if (rdlen > sizeof(RDataBody)) size = rdlen;
else size = sizeof(RDataBody);
- ere = mallocL("hanle_add_request", sizeof(extra_record_entry) - sizeof(RDataBody) + size);
- if (!ere)
+ extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size);
+ if (!extra)
{
my_perror("ERROR: malloc");
- exit(1);
+ return mStatus_NoMemoryErr;
}
- bzero(ere, sizeof(ExtraResourceRecord)); // OK if oversized rdata not zero'd
- extra = &ere->e;
+ bzero(extra, sizeof(ExtraResourceRecord)); // OK if oversized rdata not zero'd
extra->r.resrec.rrtype = rrtype;
- extra->r.rdatastorage.MaxRDLength = size;
+ extra->r.rdatastorage.MaxRDLength = (mDNSu16) size;
extra->r.resrec.rdlength = rdlen;
memcpy(&extra->r.rdatastorage.u.data, rdata, rdlen);
-
+
result = mDNS_AddRecordToService(gmDNS, srs , extra, &extra->r.rdatastorage, ttl);
- if (result) { freeL("handle_add_request", ere); return result; }
+ if (result) { freeL("ExtraResourceRecord", extra); return result; }
- ere->key = rstate->hdr.reg_index;
- ere->next = r_srv->extras;
- r_srv->extras = ere;
+ extra->ClientID = rstate->hdr.reg_index;
return result;
}
-
-static void handle_add_request(request_state *rstate)
+static mStatus handle_add_request(request_state *rstate)
{
uint32_t ttl;
uint16_t rrtype, rdlen;
char *ptr, *rdata;
- mStatus result;
+ mStatus result = mStatus_UnknownErr;
DNSServiceFlags flags;
- registered_service *local, *global;
-
- local = rstate->servicepair.local;
- global = rstate->servicepair.global;
+ service_info *srvinfo = rstate->service_registration;
+ service_instance *i;
- if (!local)
- {
- LogMsg("ERROR: handle_add_request - no service registered");
- deliver_error(rstate, mStatus_UnknownErr);
- return;
- }
+ if (!srvinfo) { LogMsg("handle_add_request called with NULL service_registration"); return(-1); }
- ptr = rstate->msgdata;
+ ptr = rstate->msgdata;
flags = get_flags(&ptr);
rrtype = get_short(&ptr);
rdlen = get_short(&ptr);
rdata = get_rdata(&ptr, rdlen);
ttl = get_long(&ptr);
-
- result = add_record_to_service(rstate, local, rrtype, rdlen, rdata, ttl);
- if (global) add_record_to_service(rstate, global, rrtype, rdlen, rdata, ttl); // don't report global errors to client
- deliver_error(rstate, result);
- reset_connected_rstate(rstate);
+ if (!ttl) ttl = DefaultTTLforRRType(rrtype);
+
+ LogOperation("%3d: DNSServiceAddRecord(%##s, %s)", rstate->sd,
+ (srvinfo->instances) ? srvinfo->instances->srs.RR_SRV.resrec.name->c : NULL, DNSTypeName(rrtype));
+
+ for (i = srvinfo->instances; i; i = i->next)
+ {
+ result = add_record_to_service(rstate, i, rrtype, rdlen, rdata, ttl);
+ if (result && i->default_local) break;
+ else result = mStatus_NoError; // suppress non-local default errors
+ }
+
+ return(result);
}
static mStatus update_record(AuthRecord *rr, uint16_t rdlen, char *rdata, uint32_t ttl)
- {
+ {
int rdsize;
RData *newrd;
mStatus result;
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;
+ if (!newrd) FatalError("ERROR: malloc");
+ newrd->MaxRDLength = (mDNSu16) rdsize;
memcpy(&newrd->u, rdata, rdlen);
- result = mDNS_Update(gmDNS, rr, ttl, rdlen, newrd, update_callback);
- if (result) { LogMsg("ERROR: mDNS_Update - %d", result); freeL("handle_update_request", newrd); }
- return result;
- }
-
-static mStatus find_extras_by_key(request_state *rstate, AuthRecord **lRR, AuthRecord **gRR)
- {
- extra_record_entry *e;
-
- // find the extra record for the local service
- for (e = rstate->servicepair.local->extras; e; e = e->next)
- if (e->key == rstate->hdr.reg_index) break;
- if (!e) return mStatus_BadReferenceErr;
- *lRR = &e->e.r;
+ // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
+ // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
+ // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
+ if (rr->resrec.rrtype == kDNSType_TXT && rdlen == 0) { rdlen = 1; newrd->u.txt.c[0] = 0; }
- // find the corresponding global record, if it exists
- if (rstate->servicepair.global)
- {
- for (e = rstate->servicepair.global->extras; e; e = e->next)
- if (e->key == rstate->hdr.reg_index) break;
- if (e) *gRR = &e->e.r;
- else *gRR = NULL;
- }
- return mStatus_NoError;
+ result = mDNS_Update(gmDNS, rr, ttl, rdlen, newrd, update_callback);
+ if (result) { LogMsg("ERROR: mDNS_Update - %ld", result); freeL("handle_update_request", newrd); }
+ return result;
}
-static void handle_update_request(request_state *rstate)
+static mStatus handle_update_request(request_state *rstate)
{
- registered_record_entry *reptr;
- AuthRecord *lRR, *gRR = NULL;
- uint16_t rdlen;
+ uint16_t rdlen;
char *ptr, *rdata;
uint32_t ttl;
- mStatus result;
-
- if (rstate->hdr.reg_index == TXT_RECORD_INDEX)
- {
- if (!rstate->servicepair.local)
- {
- deliver_error(rstate, mStatus_BadParamErr);
- return;
- }
- lRR = &rstate->servicepair.local->srs->RR_TXT;
- if (rstate->servicepair.global) gRR = &rstate->servicepair.global->srs->RR_TXT;
- }
- else
- {
- if (rstate->servicepair.local) // registered service
- {
- if (find_extras_by_key(rstate, &lRR, &gRR))
- { deliver_error(rstate, mStatus_BadReferenceErr); return; }
- }
- else
- {
- // record created via RegisterRecord
- reptr = rstate->reg_recs;
- while(reptr && reptr->key != rstate->hdr.reg_index) reptr = reptr->next;
- if (!reptr) { deliver_error(rstate, mStatus_BadReferenceErr); return; }
- lRR = reptr->rr;
- }
- }
+ mStatus result = mStatus_BadReferenceErr;
+ service_info *srvinfo = rstate->service_registration;
+ service_instance *i;
+ AuthRecord *rr = NULL;
// get the message data
ptr = rstate->msgdata;
rdata = get_rdata(&ptr, rdlen);
ttl = get_long(&ptr);
- result = update_record(lRR, rdlen, rdata, ttl);
- if (gRR) update_record(gRR, rdlen, rdata, ttl); // don't report errors for global registration
-
- deliver_error(rstate, result);
- reset_connected_rstate(rstate);
+ if (rstate->reg_recs)
+ {
+ // update an individually registered record
+ registered_record_entry *reptr;
+ for (reptr = rstate->reg_recs; reptr; reptr = reptr->next)
+ {
+ if (reptr->key == rstate->hdr.reg_index)
+ {
+ result = update_record(reptr->rr, rdlen, rdata, ttl);
+ goto end;
+ }
+ }
+ result = mStatus_BadReferenceErr;
+ goto end;
+ }
+
+ // update a record from a service record set
+ if (!srvinfo) { result = mStatus_BadReferenceErr; goto end; }
+ for (i = srvinfo->instances; i; i = i->next)
+ {
+ if (rstate->hdr.reg_index == TXT_RECORD_INDEX) rr = &i->srs.RR_TXT;
+ else
+ {
+ ExtraResourceRecord *e;
+ for (e = i->srs.Extras; e; e = e->next)
+ if (e->ClientID == rstate->hdr.reg_index) { rr = &e->r; break; }
+ }
+
+ if (!rr) { result = mStatus_BadReferenceErr; goto end; }
+ result = update_record(rr, rdlen, rdata, ttl);
+ if (result && i->default_local) goto end;
+ else result = mStatus_NoError; // suppress non-local default errors
+ }
+
+end:
+ LogOperation("%3d: DNSServiceUpdateRecord(%##s, %s)", rstate->sd,
+ (srvinfo->instances) ? srvinfo->instances->srs.RR_SRV.resrec.name->c : NULL,
+ rr ? DNSTypeName(rr->resrec.rrtype) : "<NONE>");
+
+ return(result);
}
-static void update_callback(mDNS *const m _UNUSED, AuthRecord *const rr, RData *oldrd)
- {
+static void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd)
+ {
+ (void)m; // Unused
if (oldrd != &rr->rdatastorage) freeL("update_callback", oldrd);
}
-static void process_service_registration(ServiceRecordSet *const srs)
+static void process_service_registration(ServiceRecordSet *const srs, mDNSBool SuppressError)
{
reply_state *rep;
transfer_state send_result;
mStatus err;
- registered_service *r_srv = srs->ServiceContext;
- request_state *req = r_srv->request;
+ service_instance *instance = srs->ServiceContext;
+ request_state *req = instance->request;
-
- err = gen_rr_response(&srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, req, &rep);
- if (err)
+ if (!req) { LogMsg("ERROR: process_service_registration - null request object"); return; }
+ 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)
+ if (SuppressError && 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);
+ if (send_result == t_error || send_result == t_terminated)
+ {
+ abort_request(req);
unlink_request(req);
- freeL("process_service_registration", rep);
+ freeL("process_service_registration", rep);
}
else if (send_result == t_complete) freeL("process_service_registration", rep);
else append_reply(req, rep);
}
-static void free_service_registration(registered_service *srv)
+static void free_service_instance(service_instance *srv)
{
request_state *rstate = srv->request;
- extra_record_entry *extra;
+ ExtraResourceRecord *e = srv->srs.Extras, *tmp;
// clear pointers from parent struct
if (rstate)
{
- if (rstate->servicepair.local == srv) rstate->servicepair.local = NULL;
- else if (rstate->servicepair.global == srv) rstate->servicepair.global = NULL;
+ service_instance *ptr = rstate->service_registration->instances, *prev = NULL;
+ while (ptr)
+ {
+ if (ptr == srv)
+ {
+ if (prev) prev->next = ptr->next;
+ else rstate->service_registration->instances = ptr->next;
+ break;
+ }
+ prev = ptr;
+ ptr = ptr->next;
+ }
}
-
- while (srv->extras)
+
+ while(e)
{
- extra = srv->extras;
- srv->extras = srv->extras->next;
- if (extra->e.r.resrec.rdata != &extra->e.r.rdatastorage)
- freeL("free_service_registration", extra->e.r.resrec.rdata);
- freeL("regservice_callback", extra);
+ e->r.RecordContext = e;
+ tmp = e;
+ e = e->next;
+ FreeExtraRR(gmDNS, &tmp->r, mStatus_MemFree);
}
if (srv->subtypes) { freeL("regservice_callback", srv->subtypes); srv->subtypes = NULL; }
- freeL("regservice_callback", srv->srs);
- srv->srs = NULL;
freeL("regservice_callback", srv);
}
static void regservice_termination_callback(void *context)
{
- servicepair_t *pair = context;
-
- if (!pair->local && !pair->global) { LogMsg("ERROR: regservice_termination_callback called with null services"); return; }
-
- // clear service pointers to parent request state
- if (pair->local) pair->local->request = NULL;
- if (pair->global) pair->global->request = NULL;
-
- // only safe to free memory if registration is not valid, ie deregister fails
- if (pair->local && mDNS_DeregisterService(gmDNS, pair->local->srs) != mStatus_NoError)
- free_service_registration(pair->local);
- if (pair->global && mDNS_DeregisterService(gmDNS, pair->global->srs) != mStatus_NoError)
- free_service_registration(pair->global);
-
- // clear pointers to services - they'll get cleaned by MemFree callback
- pair->local = NULL;
- pair->global = NULL;
+ service_info *info = context;
+ service_instance *i, *p;
+ if (!info) { LogMsg("regservice_termination_callback context is NULL"); return; }
+ if (!info->request) { LogMsg("regservice_termination_callback info->request is NULL"); return; }
+ i = info->instances;
+ while (i)
+ {
+ p = i;
+ i = i->next;
+ p->request = NULL; // clear back pointer
+ // only safe to free memory if registration is not valid, i.e. deregister fails (which invalidates p)
+ LogOperation("%3d: DNSServiceRegister(%##s, %u) STOP", info->request->sd, p->srs.RR_SRV.resrec.name->c, mDNSVal16(p->srs.RR_SRV.resrec.rdata->u.srv.port));
+ if (mDNS_DeregisterService(gmDNS, &p->srs)) free_service_instance(p);
+ }
+ info->request->service_registration = NULL; // clear pointer from request back to info
+ if (info->txtdata) { freeL("txtdata", info->txtdata); info->txtdata = NULL; }
+ freeL("service_info", info);
}
-
-static void handle_regrecord_request(request_state *rstate)
+static mStatus handle_regrecord_request(request_state *rstate)
{
AuthRecord *rr;
- regrecord_callback_context *rcc;
registered_record_entry *re;
mStatus result;
LogMsg("ERROR: handle_regrecord_request - transfer state != t_complete");
abort_request(rstate);
unlink_request(rstate);
- return;
+ return(-1);
}
rr = read_rr_from_ipc_msg(rstate->msgdata, 1, 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;
+ if (!rr) return(mStatus_BadParamErr);
// allocate registration entry, link into list
- re = mallocL("hanlde_regrecord_request", sizeof(registered_record_entry));
- if (!re) goto malloc_error;
+ re = mallocL("handle_regrecord_request", sizeof(registered_record_entry));
+ if (!re) FatalError("ERROR: malloc");
re->key = rstate->hdr.reg_index;
re->rr = rr;
+ re->rstate = rstate;
+ re->client_context = rstate->hdr.client_context;
+ rr->RecordContext = re;
+ rr->RecordCallback = regrecord_callback;
re->next = rstate->reg_recs;
rstate->reg_recs = re;
rstate->termination_context = rstate;
}
+ if (rr->resrec.rroriginalttl == 0)
+ rr->resrec.rroriginalttl = DefaultTTLforRRType(rr->resrec.rrtype);
+
+ LogOperation("%3d: DNSServiceRegisterRecord %s", rstate->sd, RRDisplayString(gmDNS, &rr->resrec));
result = mDNS_Register(gmDNS, rr);
- deliver_error(rstate, result);
- reset_connected_rstate(rstate);
- return;
-
-malloc_error:
- my_perror("ERROR: malloc");
- return;
+ return(result);
}
-static void regrecord_callback(mDNS *const m _UNUSED, AuthRecord *const rr, mStatus result)
+static void regrecord_callback(mDNS *const m, AuthRecord * rr, mStatus result)
{
- regrecord_callback_context *rcc = rr->RecordContext;
+ registered_record_entry *re = rr->RecordContext;
+ request_state *rstate = re ? re->rstate : NULL;
int len;
reply_state *reply;
transfer_state ts;
+ (void)m; // Unused
- if (result == mStatus_MemFree)
- {
- freeL("regrecord_callback", rcc);
- rr->RecordContext = NULL;
- freeL("regrecord_callback", rr);
- return;
- }
-
+ if (!re)
+ {
+ // parent struct alreadt freed by termination callback
+ if (!result) LogMsg("Error: regrecord_callback: successful registration of orphaned record");
+ else
+ {
+ if (result != mStatus_MemFree) LogMsg("regrecord_callback: error %d received after parent termination", result);
+ freeL("regrecord_callback", rr);
+ }
+ return;
+ }
+
// 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(gmDNS, rr->resrec.InterfaceID);
- reply->rhdr->error = result;
+ reply = create_reply(reg_record_reply, len, rstate);
+ reply->mhdr->client_context = re->client_context;
+ reply->rhdr->flags = dnssd_htonl(0);
+ reply->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, rr->resrec.InterfaceID));
+ reply->rhdr->error = dnssd_htonl(result);
+ if (result)
+ {
+ // unlink from list, free memory
+ registered_record_entry **ptr = &re->rstate->reg_recs;
+ while (*ptr && (*ptr) != re) ptr = &(*ptr)->next;
+ if (!*ptr) { LogMsg("regrecord_callback - record not in list!"); return; }
+ *ptr = (*ptr)->next;
+ freeL("regrecord_callback", re->rr);
+ re->rr = rr = NULL;
+ freeL("regrecord_callback", re);
+ re = NULL;
+ }
+
ts = send_msg(reply);
- if (ts == t_error || ts == t_terminated)
- {
- abort_request(rcc->rstate);
- unlink_request(rcc->rstate);
- }
+
+ if (ts == t_error || ts == t_terminated) { abort_request(rstate); unlink_request(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
- }
+ else if (ts == t_morecoming) append_reply(rstate, reply); // client is blocked, link reply into list
+ }
static void connected_registration_termination(void *context)
{
fptr = ptr;
ptr = ptr->next;
shared = fptr->rr->resrec.RecordType == kDNSRecordTypeShared;
+ fptr->rr->RecordContext = NULL;
mDNS_Deregister(gmDNS, fptr->rr);
- if (!shared)
- // shared records free'd via callback w/ mStatus_MemFree
- {
- freeL("connected_registration_termination", fptr->rr->RecordContext);
- fptr->rr->RecordContext = NULL;
- freeL("connected_registration_termination", fptr->rr);
- fptr->rr = NULL;
- }
freeL("connected_registration_termination", fptr);
- }
- }
+ }
+ }
-
-
-static void handle_removerecord_request(request_state *rstate)
+static mStatus handle_removerecord_request(request_state *rstate)
{
- mStatus err;
+ mStatus err = mStatus_BadReferenceErr;
char *ptr;
+ service_info *srvinfo = rstate->service_registration;
ptr = rstate->msgdata;
get_flags(&ptr); // flags unused
- if (rstate->servicepair.local) err = remove_extra_rr_from_service(rstate);
- else err = remove_record(rstate);
-
- reset_connected_rstate(rstate);
- if (deliver_error(rstate, err) < 0)
- {
- abort_request(rstate);
- unlink_request(rstate);
- }
+ if (rstate->reg_recs) err = remove_record(rstate); // remove individually registered record
+ else if (!srvinfo) LogOperation("%3d: DNSServiceRemoveRecord (bad ref)", rstate->sd);
+ else
+ {
+ service_instance *i;
+ LogOperation("%3d: DNSServiceRemoveRecord(%##s)", rstate->sd,
+ (srvinfo->instances) ? srvinfo->instances->srs.RR_SRV.resrec.name->c : NULL);
+ for (i = srvinfo->instances; i; i = i->next)
+ {
+ err = remove_extra(rstate, i);
+ if (err && i->default_local) break;
+ else err = mStatus_NoError; // suppress non-local default errors
+ }
+ }
+
+ return(err);
}
// remove a resource record registered via DNSServiceRegisterRecord()
static mStatus remove_record(request_state *rstate)
{
int shared;
- registered_record_entry *reptr, *prev = NULL;
mStatus err = mStatus_UnknownErr;
- reptr = rstate->reg_recs;
-
- while(reptr)
- {
- if (reptr->key == rstate->hdr.reg_index) // found match
- {
- if (prev) prev->next = reptr->next;
- else rstate->reg_recs = reptr->next;
- shared = reptr->rr->resrec.RecordType == kDNSRecordTypeShared;
- err = mDNS_Deregister(gmDNS, reptr->rr);
- if (err)
- {
- LogMsg("ERROR: remove_record, mDNS_Deregister: %d", err);
- return err; // this should not happen. don't try to free memory if there's an error
- }
- if (!shared)
- // shared records free'd via callback w/ mStatus_MemFree
- {
- freeL("remove_record", reptr->rr->RecordContext);
- reptr->rr->RecordContext = NULL;
- freeL("remove_record", reptr->rr);
- reptr->rr = NULL;
- }
- freeL("remove_record", reptr);
- break;
- }
- prev = reptr;
- reptr = reptr->next;
- }
- return err;
+ registered_record_entry *e, **ptr = &rstate->reg_recs;
+
+ while(*ptr && (*ptr)->key != rstate->hdr.reg_index) ptr = &(*ptr)->next;
+ if (!*ptr) { LogMsg("DNSServiceRemoveRecord - bad reference"); return mStatus_BadReferenceErr; }
+ e = *ptr;
+ *ptr = e->next; // unlink
+
+ LogOperation("%3d: DNSServiceRemoveRecord(%#s)", rstate->sd, e->rr->resrec.name->c);
+ shared = e->rr->resrec.RecordType == kDNSRecordTypeShared;
+ e->rr->RecordContext = NULL;
+ err = mDNS_Deregister(gmDNS, e->rr);
+ if (err)
+ {
+ LogMsg("ERROR: remove_record, mDNS_Deregister: %ld", err);
+ freeL("remove_record", e->rr);
+ freeL("remove_record", e);
+ }
+ return err;
}
-
-static mStatus remove_extra(request_state *rstate, registered_service *serv)
+static mStatus remove_extra(request_state *rstate, service_instance *serv)
{
mStatus err = mStatus_BadReferenceErr;
- extra_record_entry *ptr, *prev = NULL;
-
- ptr = serv->extras;
- while (ptr)
+ ExtraResourceRecord *ptr;
+
+ for (ptr = serv->srs.Extras; ptr; ptr = ptr->next)
{
- if (ptr->key == rstate->hdr.reg_index) // found match
- {
- if (prev) prev->next = ptr->next;
- else serv->extras = ptr->next;
- err = mDNS_RemoveRecordFromService(gmDNS, serv->srs, &ptr->e);
- if (err) return err;
- freeL("remove_extra_rr_from_service", ptr);
- break;
- }
- prev = ptr;
- ptr = ptr->next;
+ if (ptr->ClientID == rstate->hdr.reg_index) // found match
+ return mDNS_RemoveRecordFromService(gmDNS, &serv->srs, ptr, FreeExtraRR, ptr);
}
return err;
}
-static mStatus remove_extra_rr_from_service(request_state *rstate)
- {
- mStatus err = mStatus_UnknownErr;
-
- err = remove_extra(rstate, rstate->servicepair.local);
- if (rstate->servicepair.global) remove_extra(rstate, rstate->servicepair.global); // don't return error for global
-
- return err;
- }
-
-
-
// domain enumeration
static void handle_enum_request(request_state *rstate)
{
- DNSServiceFlags flags, add_default;
+ DNSServiceFlags flags;
uint32_t ifi;
mDNSInterfaceID InterfaceID;
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;
}
// allocate context structures
- def = mallocL("hanlde_enum_request", sizeof(domain_enum_t));
+ def = mallocL("handle_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);
- }
+ if (!def || !all || !term) FatalError("ERROR: malloc");
// enumeration requires multiple questions, so we must link all the context pointers so that
// necessary context can be reached from the callbacks
rstate->termination_context = term;
rstate->terminate = enum_termination_callback;
def->question.QuestionContext = def;
- def->type = (flags & kDNSServiceFlagsRegistrationDomains) ?
+ def->type = (flags & kDNSServiceFlagsRegistrationDomains) ?
mDNS_DomainTypeRegistrationDefault: mDNS_DomainTypeBrowseDefault;
all->question.QuestionContext = all;
- all->type = (flags & kDNSServiceFlagsRegistrationDomains) ?
+ all->type = (flags & kDNSServiceFlagsRegistrationDomains) ?
mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse;
// if the caller hasn't specified an explicit interface, we use local-only to get the system-wide list.
if (!InterfaceID) InterfaceID = mDNSInterface_LocalOnly;
// make the calls
+ LogOperation("%3d: DNSServiceEnumerateDomains(%X=%s)", rstate->sd, flags,
+ (flags & kDNSServiceFlagsBrowseDomains ) ? "kDNSServiceFlagsBrowseDomains" :
+ (flags & kDNSServiceFlagsRegistrationDomains) ? "kDNSServiceFlagsRegistrationDomains" : "<<Unknown>>");
err = mDNS_GetDomains(gmDNS, &all->question, all->type, NULL, InterfaceID, enum_result_callback, all);
if (err == mStatus_NoError)
err = mDNS_GetDomains(gmDNS, &def->question, def->type, NULL, InterfaceID, enum_result_callback, def);
unlink_request(rstate);
return;
}
-
- // provide local. as the first domain automatically
- add_default = kDNSServiceFlagsDefault | kDNSServiceFlagsAdd;
- 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 _UNUSED, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
+static void enum_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
{
char domain[MAX_ESCAPED_DOMAIN_NAME];
domain_enum_t *de = question->QuestionContext;
DNSServiceFlags flags = 0;
reply_state *reply;
+ (void)m; // Unused
if (answer->rrtype != kDNSType_PTR) return;
+ if (!AddRecord && de->type != mDNS_DomainTypeBrowse) return;
+
if (AddRecord)
{
flags |= kDNSServiceFlagsAdd;
if (de->type == mDNS_DomainTypeRegistrationDefault || de->type == mDNS_DomainTypeBrowseDefault)
flags |= kDNSServiceFlagsDefault;
- }
+ }
ConvertDomainNameToCString(&answer->rdata->u.name, domain);
- reply = format_enumeration_reply(de->rstate, domain, flags, mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, answer->InterfaceID), kDNSServiceErr_NoError);
+ // note that we do NOT propagate specific interface indexes to the client - for example, a domain we learn from
+ // a machine's system preferences may be discovered on the LocalOnly interface, but should be browsed on the
+ // network, so we just pass kDNSServiceInterfaceIndexAny
+ reply = format_enumeration_reply(de->rstate, domain, flags, kDNSServiceInterfaceIndexAny, kDNSServiceErr_NoError);
if (!reply)
{
LogMsg("ERROR: enum_result_callback, format_enumeration_reply");
static reply_state *format_enumeration_reply(request_state *rstate, const char *domain, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err)
{
- int len;
+ size_t len;
reply_state *reply;
char *data;
len += strlen(domain) + 1;
reply = create_reply(enumeration_reply, len, rstate);
- reply->rhdr->flags = flags;
- reply->rhdr->ifi = ifi;
- reply->rhdr->error = err;
+ reply->rhdr->flags = dnssd_htonl(flags);
+ reply->rhdr->ifi = dnssd_htonl(ifi);
+ reply->rhdr->error = dnssd_htonl(err);
data = reply->sdata;
put_string(domain, &data);
return reply;
rr = read_rr_from_ipc_msg(rstate->msgdata, 0, 1);
if (!rr) return;
+ LogOperation("%3d: DNSServiceReconfirmRecord(%##s) %s", rstate->sd, RRDisplayString(gmDNS, &rr->resrec));
mDNS_ReconfirmByValue(gmDNS, &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->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, int validate_flags)
+static AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int GetTTL, int validate_flags)
{
char *rdata, name[256];
AuthRecord *rr;
LogMsg("ERROR: read_rr_from_ipc_msg - get_string");
return NULL;
}
- type = get_short(&msgbuf);
+ type = get_short(&msgbuf);
class = get_short(&msgbuf);
rdlen = get_short(&msgbuf);
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);
- }
+ if (!rr) FatalError("ERROR: malloc");
bzero(rr, sizeof(AuthRecord)); // ok if oversized rdata not zero'd
- rr->resrec.rdata = &rr->rdatastorage;
- rr->resrec.InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, interfaceIndex);
- if (!MakeDomainNameFromDNSNameString(&rr->resrec.name, name))
+
+ mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, interfaceIndex),
+ type, 0, (flags & kDNSServiceFlagsShared) ? kDNSRecordTypeShared : kDNSRecordTypeUnique, mDNSNULL, mDNSNULL);
+
+ 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;
+ if (flags & kDNSServiceFlagsAllowRemoteQuery) rr->AllowRemoteQuery = mDNStrue;
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)
+ if (GetTTL)
{
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.
len = sizeof(DNSServiceFlags);
len += sizeof(uint32_t); // if index
len += sizeof(DNSServiceErrorType);
- len += strlen(namestr) + 1;
- len += strlen(typestr) + 1;
- len += strlen(domstr) + 1;
+ len += (int) (strlen(namestr) + 1);
+ len += (int) (strlen(typestr) + 1);
+ len += (int) (strlen(domstr) + 1);
*rep = create_reply(query_reply, len, request);
- (*rep)->rhdr->flags = 0;
- (*rep)->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, id);
- (*rep)->rhdr->error = kDNSServiceErr_NoError;
+
+ (*rep)->rhdr->flags = dnssd_htonl(0);
+ (*rep)->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, id));
+ (*rep)->rhdr->error = dnssd_htonl(kDNSServiceErr_NoError);
+
data = (*rep)->sdata;
put_string(namestr, &data);
return mStatus_NoError;
}
-
static int build_domainname_from_strings(domainname *srv, char *name, char *regtype, char *domain)
{
domainlabel n;
return 0;
}
-
// append a reply to the list in a request object
static void append_reply(request_state *req, reply_state *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
{
uint32_t nleft;
int nread;
- char buf[4]; // dummy for death notification
+ char buf[4]; // dummy for death notification
if (rs->ts == t_terminated || rs->ts == t_error)
{
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))
+ {
+ ConvertHeaderBytes(&rs->hdr);
+ if (rs->hdr.version != VERSION)
+ {
+ LogMsg("ERROR: read_msg - client version 0x%08X does not match daemon version 0x%08X", rs->hdr.version, VERSION);
+ rs->ts = t_error;
+ return t_error;
+ }
+ }
if (rs->hdr_bytes > sizeof(ipc_msg_hdr))
{
LogMsg("ERROR: read_msg - read too many header bytes");
return rs->ts;
rerror:
- if (errno == EAGAIN || errno == EINTR) return rs->ts;
+ if (dnssd_errno() == dnssd_EWOULDBLOCK || dnssd_errno() == dnssd_EINTR) return t_morecoming;
my_perror("ERROR: read_msg");
rs->ts = t_error;
return t_error;
}
-
static int send_msg(reply_state *rs)
{
ssize_t nwriten;
return t_complete;
}
+ ConvertHeaderBytes(rs->mhdr);
nwriten = send(rs->sd, rs->msgbuf + rs->nwriten, rs->len - rs->nwriten, 0);
+ ConvertHeaderBytes(rs->mhdr);
if (nwriten < 0)
{
- if (errno == EINTR || errno == EAGAIN) nwriten = 0;
+ if (dnssd_errno() == dnssd_EINTR || dnssd_errno() == dnssd_EWOULDBLOCK) nwriten = 0;
else
{
- if (errno == EPIPE)
+#if !defined(PLATFORM_NO_EPIPE)
+ if (dnssd_errno() == EPIPE)
{
- LogMsg("broken pipe - cleanup should be handled by run-loop read wakeup");
+ debugf("%3d: broken pipe", rs->sd);
rs->ts = t_terminated;
rs->request->ts = t_terminated;
- return t_terminated;
+ return t_terminated;
}
else
+#endif
{
my_perror("ERROR: send\n");
rs->ts = t_error;
return rs->ts;
}
-
-
-static reply_state *create_reply(reply_op_t op, int datalen, request_state *request)
+static reply_state *create_reply(reply_op_t op, size_t datalen, request_state *request)
{
reply_state *reply;
int totallen;
return NULL;
}
- totallen = datalen + sizeof(ipc_msg_hdr);
+ totallen = (int) (datalen + sizeof(ipc_msg_hdr));
reply = mallocL("create_reply", sizeof(reply_state));
- if (!reply)
- {
- my_perror("ERROR: malloc");
- exit(1);
- }
+ if (!reply) FatalError("ERROR: malloc");
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);
- }
+ if (!reply->msgbuf) FatalError("ERROR: malloc");
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->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;
-
- }
-
+ {
+ int nwritten = -1;
+ undelivered_error_t *undeliv;
+
+ err = dnssd_htonl(err);
+ nwritten = send(rstate->sd, &err, sizeof(mStatus), 0);
+ if (nwritten < (int)sizeof(mStatus))
+ {
+ if (dnssd_errno() == dnssd_EINTR || dnssd_errno() == dnssd_EWOULDBLOCK)
+ nwritten = 0;
+ if (nwritten < 0)
+ {
+ my_perror("ERROR: send - unable to deliver error to client");
+ return(-1);
+ }
+ else
+ {
+ //client blocked - store result and come backr
+ undeliv = mallocL("deliver_error", sizeof(undelivered_error_t));
+ if (!undeliv) FatalError("ERROR: malloc");
+ undeliv->err = err;
+ undeliv->nwritten = nwritten;
+ undeliv->sd = rstate->sd;
+ rstate->u_err = undeliv;
+ return 0;
+ }
+ }
+ return 0;
+ }
-// returns 0 on success, -1 if send is incomplete, or on terminal failre (request is aborted)
+// returns 0 on success, -1 if send is incomplete, or on terminal failure (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;
- }
-
+ {
+ int nwritten;
+
+ nwritten = send(rs->u_err->sd, (char *)(&rs->u_err->err) + rs->u_err->nwritten, sizeof(mStatus) - rs->u_err->nwritten, 0);
+ if (nwritten < 0)
+ {
+ if (dnssd_errno() == dnssd_EINTR || dnssd_errno() == dnssd_EWOULDBLOCK)
+ nwritten = 0;
+ else
+ {
+ my_perror("ERROR: send - unable to deliver error to client\n");
+ return t_error;
+ }
+ }
+ if ((unsigned int)(nwritten + rs->u_err->nwritten) >= sizeof(mStatus))
+ {
+ 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),
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;
+ reply->rhdr->error = dnssd_htonl(err);
ts = send_msg(reply);
if (ts == t_error || ts == t_terminated)
{
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);
- udsSupportRemoveFDFromEventLoop(rs->sd);
- rs->sd = -1;
- if (rs->errfd >= 0) close(rs->errfd);
- rs->errfd = -1;
+ LogOperation("%3d: Removing FD", rs->sd);
+ udsSupportRemoveFDFromEventLoop(rs->sd); // Note: This also closes file descriptor rs->sd for us
+ rs->sd = dnssd_InvalidSocket;
// free pending replies
rep = rs->replies;
}
}
-
static void unlink_request(request_state *rs)
{
request_state *ptr;
return;
}
}
-
-
//hack to search-replace perror's to LogMsg's
static void my_perror(char *errmsg)
{
- LogMsg("%s: %s", errmsg, strerror(errno));
+ LogMsg("%s: %s", errmsg, dnssd_strerror(dnssd_errno()));
}
// check that the message delivered by the client is sufficiently long to extract the required data from the buffer
{
uint32_t min_size;
- switch(rstate->hdr.op.request_op)
+ switch(rstate->hdr.op)
{
case resolve_request: min_size = sizeof(DNSServiceFlags) + // flags
sizeof(uint32_t) + // interface
(3 * sizeof(char)); // name, regtype, domain
- break;
+ break;
case query_request: min_size = sizeof(DNSServiceFlags) + // flags
sizeof(uint32_t) + // interface
sizeof(char) + // fullname
case reg_service_request: min_size = sizeof(DNSServiceFlags) + // flags
sizeof(uint32_t) + // interface
(4 * sizeof(char)) + // name, type, domain, host
- (2 * sizeof(uint16_t)); // port, textlen
- break;
+ (2 * sizeof(uint16_t)); // port, textlen
+ break;
case enumeration_request: min_size = sizeof(DNSServiceFlags) + // flags
sizeof(uint32_t); // interface
break;
sizeof(uint32_t) + // interface
sizeof(char) + // fullname
(3 * sizeof(uint16_t)); // type, class, rdlen
- default:
- LogMsg("ERROR: validate_message - unsupported request type: %d", rstate->hdr.op.request_op);
+ break;
+ case setdomain_request: min_size = sizeof(DNSServiceFlags) + sizeof(char); // flags + domain
+ break;
+ default:
+ LogMsg("ERROR: validate_message - unsupported request type: %d", rstate->hdr.op);
return -1;
- }
+ }
return (rstate->data_bytes >= min_size ? 0 : -1);
}
+static uint32_t dnssd_htonl(uint32_t l)
+ {
+ uint32_t ret;
+ char * data;
+
+ data = (char*) &ret;
+
+ put_long(l, &data);
+ return ret;
+ }
+
+#if defined(_WIN32)
+
+static char * win32_strerror(int inErrorCode)
+ {
+ static char buffer[1024];
+ DWORD n;
+
+ memset(buffer, 0, sizeof(buffer));
+
+ n = FormatMessageA(
+ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ (DWORD) inErrorCode,
+ MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
+ buffer,
+ sizeof( buffer ),
+ NULL );
+
+ if( n > 0 )
+ {
+ // Remove any trailing CR's or LF's since some messages have them.
+
+ while( ( n > 0 ) && isspace( ( (unsigned char *) buffer)[ n - 1 ] ) )
+ {
+ buffer[ --n ] = '\0';
+ }
+ }
+
+ return buffer;
+ }
+
+#endif