1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
22 * @APPLE_LICENSE_HEADER_END@
24 Change History (most recent first):
26 $Log: uds_daemon.c,v $
27 Revision 1.164 2005/01/28 06:07:55 cheshire
28 Don't use deliver_error() from within handle_regrecord_request()
30 Revision 1.163 2005/01/28 01:39:16 cheshire
31 Include file descriptor number in "broken pipe" message
33 Revision 1.162 2005/01/27 23:59:20 cheshire
34 Remove extraneous LogMsg
36 Revision 1.161 2005/01/27 22:57:56 cheshire
37 Fix compile errors on gcc4
39 Revision 1.160 2005/01/27 20:52:11 cheshire
40 <rdar://problem/3972566> mDNSResponder leaks sockets for add/update/remove record calls
42 Revision 1.159 2005/01/27 01:45:25 cheshire
43 <rdar://problem/3976147> mDNSResponder should never call exit(1);
45 Revision 1.158 2005/01/25 17:28:07 ksekar
46 <rdar://problem/3971467> Should not return "local" twice for domain enumeration
48 Revision 1.157 2005/01/21 02:20:39 cheshire
49 Fix mistake in LogOperation() format string
51 Revision 1.156 2005/01/19 19:15:36 ksekar
52 Refinement to <rdar://problem/3954575> - Simplify mDNS_PurgeResultsForDomain logic and move into daemon layer
54 Revision 1.155 2005/01/19 03:00:47 cheshire
55 Show Add/Rmv in DNSServiceBrowse LogOperation() message
57 Revision 1.154 2005/01/15 00:56:42 ksekar
58 <rdar://problem/3954575> Unicast services don't disappear when logging
61 Revision 1.153 2005/01/14 18:44:28 ksekar
62 <rdar://problem/3954609> mDNSResponder is crashing when changing domains
64 Revision 1.152 2005/01/13 17:16:38 ksekar
65 Back out checkin 1.150 - correct fix is on clientstub side
67 Revision 1.151 2005/01/11 21:06:29 ksekar
68 Changed now-benign LogMsg to debugf
70 Revision 1.150 2005/01/07 23:59:15 ksekar
71 <rdar://problem/3942900> dnd-sd shows the wrong port numbers
73 Revision 1.149 2004/12/20 23:20:35 cheshire
74 <rdar://problem/3928361> mDNSResponder crashes repeatedly when printer sharing is enabled
75 Make sure to call mDNS_SetupResourceRecord() for all newly created AuthRecords
77 Revision 1.148 2004/12/20 20:37:35 cheshire
78 AllowRemoteQuery not set for the extras in a ServiceRecordSet
80 Revision 1.147 2004/12/20 00:15:41 cheshire
81 Include client file descriptor numbers in udsserver_info() output
83 Revision 1.146 2004/12/17 05:25:47 cheshire
84 <rdar://problem/3925163> Shorten DNS-SD queries to avoid NAT bugs
86 Revision 1.145 2004/12/16 21:39:46 cheshire
87 Include CacheGroup objects in CacheUsed count
89 Revision 1.144 2004/12/16 21:27:38 ksekar
90 Fixed build failures when compiled with verbose debugging messages
92 Revision 1.143 2004/12/16 20:13:02 cheshire
93 <rdar://problem/3324626> Cache memory management improvements
95 Revision 1.142 2004/12/16 08:07:33 shersche
96 Fix compiler error (mixed declarations and code) on Windows
98 Revision 1.141 2004/12/16 01:56:21 cheshire
99 Improve DNSServiceEnumerateDomains syslog message
101 Revision 1.140 2004/12/14 03:02:10 ksekar
102 <rdar://problem/3919016> Rare race condition can cause crash
104 Revision 1.139 2004/12/13 21:18:45 ksekar
105 Include uDNS registrations in CountPeerRegistrations
107 Revision 1.138 2004/12/13 18:23:18 ksekar
108 <rdar://problem/3915805> mDNSResponder error when quitting iChat -
109 don't close sockets delivering errors to blocked clients
111 Revision 1.137 2004/12/13 00:09:22 ksekar
112 <rdar://problem/3915805> mDNSResponder error when quitting iChat
114 Revision 1.136 2004/12/11 01:52:10 cheshire
115 <rdar://problem/3785820> Support kDNSServiceFlagsAllowRemoteQuery for registering services too
117 Revision 1.135 2004/12/10 20:46:37 cheshire
118 Change LogOperation message to debugf
120 Revision 1.134 2004/12/10 13:19:37 cheshire
121 Add verbosedebugf() logging message in CountPeerRegistrations()
123 Revision 1.133 2004/12/10 05:27:26 cheshire
124 <rdar://problem/3909147> Guard against multiple autoname services of the same type on the same machine
126 Revision 1.132 2004/12/10 04:28:28 cheshire
127 <rdar://problem/3914406> User not notified of name changes for services using new UDS API
129 Revision 1.131 2004/12/10 02:09:25 cheshire
130 <rdar://problem/3898376> Modify default TTLs
132 Revision 1.130 2004/12/10 00:55:24 cheshire
133 Add full name and type to LogOperation messages for DNSServiceAddRecord/UpdateRecord/RemoveRecord
135 Revision 1.129 2004/12/09 03:17:23 ksekar
136 <rdar://problem/3910435> DomainEnumeration interface index should be zero
138 Revision 1.128 2004/12/07 21:26:05 ksekar
139 <rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration
141 Revision 1.127 2004/12/07 20:42:34 cheshire
142 Add explicit context parameter to mDNS_RemoveRecordFromService()
144 Revision 1.126 2004/12/07 17:23:55 ksekar
147 Revision 1.125 2004/12/06 21:15:23 ksekar
148 <rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
150 Revision 1.124 2004/11/30 02:19:14 cheshire
151 <rdar://problem/3827971> Raise maxfds.rlim_cur for mDNSResponder
153 Revision 1.123 2004/11/29 23:50:57 cheshire
154 Checkin 1.122 not necessary
156 Revision 1.122 2004/11/24 17:55:01 ksekar
157 Added log message clarifying <rdar://problem/3869241> For unicast operations, verify that service types are legal
159 Revision 1.121 2004/11/24 04:45:52 cheshire
162 Revision 1.120 2004/11/24 00:10:44 cheshire
163 <rdar://problem/3869241> For unicast operations, verify that service types are legal
165 Revision 1.119 2004/11/23 23:54:17 ksekar
166 <rdar://problem/3890318> Wide-Area DNSServiceRegisterRecord() failures
167 can crash mDNSResponder
169 Revision 1.118 2004/11/23 22:33:01 cheshire
170 <rdar://problem/3654910> Remove temporary workaround code for iChat
172 Revision 1.117 2004/11/23 20:23:10 ksekar
173 Fixed LogOperation that causes crash on connected service deregistrations
175 Revision 1.116 2004/11/23 03:39:47 cheshire
176 Let interface name/index mapping capability live directly in JNISupport.c,
177 instead of having to call through to the daemon via IPC to get this information.
179 Revision 1.115 2004/11/13 00:12:53 ksekar
180 Fixed some LogOperation printf converstions for debug builds.
182 Revision 1.114 2004/11/12 18:25:45 shersche
183 Tidy up cross platform usleep code fragment.
185 Revision 1.113 2004/11/12 03:21:41 rpantos
186 rdar://problem/3809541 Add DNSSDMapIfIndexToName, DNSSDMapNameToIfIndex.
188 Revision 1.112 2004/11/11 16:58:32 ksekar
189 Removed unused code (previously wrapped in #if 0)
191 Revision 1.111 2004/11/05 22:47:37 shersche
192 Conditionally compile usleep(1000) to be Sleep(1) on Windows
193 Submitted by: Pavel Repin <prepin@gmail.com>
195 Revision 1.110 2004/11/05 19:56:56 ksekar
196 <rdar://problem/3862646> We no longer need to browse .Mac domains by
197 default - changed #if 0 to more descriptive #ifdef _HAVE_SETDOMAIN_SUPPORT_
199 Revision 1.109 2004/11/04 03:40:45 cheshire
200 More debugging messages
202 Revision 1.108 2004/11/03 02:25:51 cheshire
203 <rdar://problem/3324137> Conflict for Computer Name should update *all* empty string services, not just the one with the conflict
205 Revision 1.107 2004/11/02 19:39:23 ksekar
206 <rdar://problem/3862646> We no longer need to browse .Mac domains by default
208 Revision 1.106 2004/11/02 02:12:21 cheshire
209 <rdar://problem/3839111> Remove unnecessary memory allocations
211 Revision 1.105 2004/10/28 19:07:19 cheshire
212 Add some more debugging checks and improved LogOperation() messages
214 Revision 1.104 2004/10/26 18:53:15 cheshire
215 Avoid unused variable warning
217 Revision 1.103 2004/10/26 07:15:55 cheshire
218 Add file descriptor number to all LogOperation messages
220 Revision 1.102 2004/10/26 06:11:42 cheshire
221 Add improved logging to aid in diagnosis of <rdar://problem/3842714> mDNSResponder crashed
223 Revision 1.101 2004/10/26 04:31:44 cheshire
224 Rename CountSubTypes() as ChopSubTypes()
226 Revision 1.100 2004/10/26 01:17:48 cheshire
227 Use "#if 0" instead of commenting out code
229 Revision 1.99 2004/10/19 21:33:22 cheshire
230 <rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
231 Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name
232 doesn't force multicast unless you set this flag to indicate explicitly that this is what you want
234 Revision 1.98 2004/10/14 01:59:33 cheshire
235 <rdar://problem/3839208> UDS resolves don't work for uDNS services
237 Revision 1.97 2004/10/13 00:58:35 cheshire
238 <rdar://problem/3832738> Registering a proxy doesn't work
240 Revision 1.96 2004/09/30 00:25:00 ksekar
241 <rdar://problem/3695802> Dynamically update default registration domains on config change
243 Revision 1.95 2004/09/26 23:20:36 ksekar
244 <rdar://problem/3813108> Allow default registrations in multiple wide-area domains
246 Revision 1.94 2004/09/22 18:27:06 ksekar
247 <rdar://problem/3811427> allow DNSServiceAddRecord to pass zero to get
250 Revision 1.93 2004/09/22 02:39:44 cheshire
251 <rdar://problem/3810757> Allow DNSServiceRegisterRecord to pass zero to get default record TTL
253 Revision 1.92 2004/09/22 02:34:04 cheshire
254 Rename parameter "ttl" to "GetTTL" for clarity
256 Revision 1.91 2004/09/22 02:25:43 cheshire
259 Revision 1.90 2004/09/21 23:40:12 ksekar
260 <rdar://problem/3810349> mDNSResponder to return errors on NAT traversal failure
262 Revision 1.89 2004/09/21 23:29:51 cheshire
263 <rdar://problem/3680045> DNSServiceResolve should delay sending packets
265 Revision 1.88 2004/09/21 23:12:46 cheshire
266 Reorder initialization of question fields to match structure order
268 Revision 1.87 2004/09/21 22:18:33 cheshire
269 In SIGINFO output, display a '-' next to records that have the Unique bit set
271 Revision 1.86 2004/09/21 21:05:11 cheshire
272 Move duplicate code out of mDNSMacOSX/daemon.c and mDNSPosix/PosixDaemon.c,
273 into mDNSShared/uds_daemon.c
275 Revision 1.85 2004/09/18 01:11:58 ksekar
276 <rdar://problem/3806734> Add a user's default domain to empty-string browse list
278 Revision 1.84 2004/09/17 01:08:55 cheshire
279 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
280 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
281 declared in that file are ONLY appropriate to single-address-space embedded applications.
282 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
284 Revision 1.83 2004/09/16 23:26:33 cheshire
285 Move version check inside preceeding "if" that checks we have a complete header
287 Revision 1.82 2004/09/16 23:14:25 cheshire
288 Changes for Windows compatibility
290 Revision 1.81 2004/09/16 21:46:38 ksekar
291 <rdar://problem/3665304> Need SPI for LoginWindow to associate a UID with a Wide Area Rendezvous domain
293 Revision 1.80 2004/09/16 01:58:23 cheshire
294 Fix compiler warnings
296 Revision 1.79 2004/09/16 00:24:49 cheshire
297 <rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
299 Revision 1.78 2004/09/15 21:44:20 cheshire
300 <rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init
301 Show time value in log to help diagnose errors
303 Revision 1.77 2004/09/15 00:19:18 cheshire
304 <rdar://problem/3785823> read_rr_from_ipc_msg should use mDNS_SetupResourceRecord()
306 Revision 1.76 2004/09/02 06:39:52 cheshire
307 Minor textual cleanup for clarity
309 Revision 1.75 2004/09/02 03:48:47 cheshire
310 <rdar://problem/3709039> Disable targeted unicast query support by default
311 1. New flag kDNSServiceFlagsAllowRemoteQuery to indicate we want to allow remote queries for this record
312 2. New field AllowRemoteQuery in AuthRecord structure
313 3. uds_daemon.c sets AllowRemoteQuery if kDNSServiceFlagsAllowRemoteQuery is set
314 4. mDNS.c only answers remote queries if AllowRemoteQuery is set
316 Revision 1.74 2004/08/25 02:32:47 cheshire
317 Minor cleanup: replace "®type[0]" with "regtype"
319 Revision 1.73 2004/08/25 02:30:40 cheshire
320 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
322 Revision 1.72 2004/08/14 03:22:42 cheshire
323 <rdar://problem/3762579> Dynamic DNS UI <-> mDNSResponder glue
324 Add GetUserSpecifiedDDNSName() routine
325 Convert ServiceRegDomain to domainname instead of C string
326 Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs
328 Revision 1.71 2004/08/11 04:21:21 rpantos
331 Revision 1.70 2004/08/11 02:07:00 cheshire
332 Remove "mDNS *globalInstance" parameter from udsserver_init()
333 Move CheckForDuplicateRegistrations from daemon.c
334 <rdar://problem/3501938> No warning when accidentally registering the same service multiple times using socket API
336 Revision 1.69 2004/08/10 16:14:48 cheshire
337 Fix debug builds (oops)
339 Revision 1.68 2004/08/10 06:24:56 cheshire
340 Use types with precisely defined sizes for 'op' and 'reg_index', for better
341 compatibility if the daemon and the client stub are built using different compilers
343 Revision 1.67 2004/07/27 07:14:16 shersche
344 make error socket non-blocking after call to connect()
346 Revision 1.66 2004/07/13 21:24:25 rpantos
347 Fix for <rdar://problem/3701120>.
349 Revision 1.65 2004/06/26 03:17:14 shersche
350 implement cross-platform strerror function
352 Submitted by: herscher
354 Revision 1.64 2004/06/25 00:26:27 rpantos
355 Changes to fix the Posix build on Solaris.
357 Revision 1.63 2004/06/24 03:43:44 rpantos
358 Fix previous checkin so it builds on Windows.
360 Revision 1.62 2004/06/24 00:57:08 ksekar
361 Replaced code acccidentally removed in checkin 1.59.
363 Revision 1.61 2004/06/19 00:09:39 cheshire
364 Remove unused strsep() implementation
366 Revision 1.60 2004/06/18 19:10:00 cheshire
367 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
369 Revision 1.59 2004/06/18 05:10:31 rpantos
370 Changes to allow code to be used on Windows
372 Revision 1.58 2004/06/15 03:54:08 cheshire
373 Include mDNS_TimeNow(&mDNSStorage) in SIGINFO output
375 Revision 1.57 2004/06/12 01:47:27 ksekar
376 <rdar://problem/3690241>: BBEdit crashes when trying to check for newer version
377 udsserver_idle compared time in ticks to interval in seconds.
379 Revision 1.56 2004/06/12 01:35:47 cheshire
380 Changes for Windows compatibility
382 Revision 1.55 2004/06/05 00:04:27 cheshire
383 <rdar://problem/3668639>: wide-area domains should be returned in reg. domain enumeration
385 Revision 1.54 2004/06/01 22:22:52 ksekar
386 <rdar://problem/3668635>: wide-area default registrations should be in
389 Revision 1.53 2004/05/28 23:42:37 ksekar
390 <rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
392 Revision 1.52 2004/05/26 00:39:49 ksekar
393 <rdar://problem/3667105>: wide-area DNS-SD servers don't appear in
395 Use local-only InterfaceID for GetDomains calls for sockets-API
397 Revision 1.51 2004/05/18 23:51:27 cheshire
398 Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
400 Revision 1.50 2004/05/14 16:39:47 ksekar
401 Browse for iChat locally for now.
403 Revision 1.49 2004/05/13 21:33:52 ksekar
404 Clean up non-local registration control via config file. Force iChat
405 registrations to be local for now.
407 Revision 1.48 2004/05/13 04:13:19 ksekar
408 Updated SIGINFO handler for multi-domain browses
410 Revision 1.47 2004/05/12 22:04:01 ksekar
411 Implemented multi-domain browsing by default for uds_daemon.
413 Revision 1.46 2004/05/06 18:42:58 ksekar
414 General dns_sd.h API cleanup, including the following radars:
415 <rdar://problem/3592068>: Remove flags with zero value
416 <rdar://problem/3479569>: Passing in NULL causes a crash.
418 Revision 1.45 2004/03/12 08:49:28 cheshire
419 #include <sys/socket.h>
421 Revision 1.44 2004/02/25 01:25:27 ksekar
422 <rdar://problem/3569212>: DNSServiceRegisterRecord flags not error-checked
424 Revision 1.43 2004/02/24 01:46:40 cheshire
425 Manually reinstate lost checkin 1.36
427 Revision 1.42 2004/02/05 19:39:29 cheshire
428 Move creation of /var/run/mDNSResponder.pid to uds_daemon.c,
429 so that all platforms get this functionality
431 Revision 1.41 2004/02/03 18:59:02 cheshire
432 Change "char *domain" parameter for format_enumeration_reply to "const char *domain"
434 Revision 1.40 2004/01/28 03:41:00 cheshire
435 <rdar://problem/3541946>: Need ability to do targeted queries as well as multicast queries
437 Revision 1.39 2004/01/25 00:03:21 cheshire
438 Change to use mDNSVal16() instead of private PORT_AS_NUM() macro
440 Revision 1.38 2004/01/19 19:51:46 cheshire
441 Fix compiler error (mixed declarations and code) on some versions of Linux
443 Revision 1.37 2003/12/08 21:11:42 rpantos
444 Changes necessary to support mDNSResponder on Linux.
446 Revision 1.36 2003/12/04 23:40:57 cheshire
447 <rdar://problem/3484766>: Security: Crashing bug in mDNSResponder
448 Fix some more code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers.
450 Revision 1.35 2003/12/03 19:10:22 ksekar
451 <rdar://problem/3498644>: malloc'd data not zero'd
453 Revision 1.34 2003/12/03 02:00:01 ksekar
454 <rdar://problem/3498644>: malloc'd data not zero'd
456 Revision 1.33 2003/11/22 01:18:46 ksekar
457 <rdar://problem/3486646>: config change handler not called for dns-sd services
459 Revision 1.32 2003/11/20 21:46:12 ksekar
460 <rdar://problem/3486635>: leak: DNSServiceRegisterRecord
462 Revision 1.31 2003/11/20 20:33:05 ksekar
463 <rdar://problem/3486635>: leak: DNSServiceRegisterRecord
465 Revision 1.30 2003/11/20 02:10:55 ksekar
466 <rdar://problem/3486643>: cleanup DNSServiceAdd/RemoveRecord
468 Revision 1.29 2003/11/14 21:18:32 cheshire
469 <rdar://problem/3484766>: Security: Crashing bug in mDNSResponder
470 Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers.
472 Revision 1.28 2003/11/08 22:18:29 cheshire
473 <rdar://problem/3477870>: Don't need to show process ID in *every* mDNSResponder syslog message
475 Revision 1.27 2003/11/05 22:44:57 ksekar
476 <rdar://problem/3335230>: No bounds checking when reading data from client
477 Reviewed by: Stuart Cheshire
479 Revision 1.26 2003/10/23 17:51:04 ksekar
480 <rdar://problem/3335216>: handle blocked clients more efficiently
481 Changed gettimeofday() to mDNS_TimeNow(&mDNSStorage);
483 Revision 1.25 2003/10/22 23:37:49 ksekar
484 <rdar://problem/3459141>: crash/hang in abort_client
486 Revision 1.24 2003/10/21 20:59:40 ksekar
487 <rdar://problem/3335216>: handle blocked clients moreefficiently
489 Revision 1.23 2003/09/23 02:12:43 cheshire
490 Also include port number in list of services registered via new UDS API
492 Revision 1.22 2003/08/19 16:03:55 ksekar
493 <rdar://problem/3380097>: ER: SIGINFO dump should include resolves started by DNSServiceQueryRecord
494 Check termination_context for NULL before dereferencing.
496 Revision 1.21 2003/08/19 05:39:43 cheshire
497 <rdar://problem/3380097> SIGINFO dump should include resolves started by DNSServiceQueryRecord
499 Revision 1.20 2003/08/16 03:39:01 cheshire
500 <rdar://problem/3338440> InterfaceID -1 indicates "local only"
502 Revision 1.19 2003/08/15 20:16:03 cheshire
503 <rdar://problem/3366590> mDNSResponder takes too much RPRVT
504 We want to avoid touching the rdata pages, so we don't page them in.
505 1. RDLength was stored with the rdata, which meant touching the page just to find the length.
506 Moved this from the RData to the ResourceRecord object.
507 2. To avoid unnecessarily touching the rdata just to compare it,
508 compute a hash of the rdata and store the hash in the ResourceRecord object.
510 Revision 1.18 2003/08/15 00:38:00 ksekar
511 <rdar://problem/3377005>: Bug: buffer overrun when reading long rdata from client
513 Revision 1.17 2003/08/14 02:18:21 cheshire
514 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
516 Revision 1.16 2003/08/13 23:58:52 ksekar
517 <rdar://problem/3374911>: Bug: UDS Sub-type browsing works, but not sub-type registration
518 Fixed pointer increment error, moved subtype reading for-loop for easier error bailout.
520 Revision 1.15 2003/08/13 17:30:33 ksekar
521 <rdar://problem/3374671>: DNSServiceAddRecord doesn't work
522 Fixed various problems with handling the AddRecord request and freeing the ExtraResourceRecords.
524 Revision 1.14 2003/08/12 19:56:25 cheshire
531 #define dnssd_strerror(X) win32_strerror(X)
532 #define usleep(X) Sleep(((X)+999)/1000)
533 static char * win32_strerror(int inErrorCode
);
537 #include <sys/ioctl.h>
538 #include <sys/types.h>
539 #include <sys/time.h>
540 #include <sys/resource.h>
541 #define dnssd_strerror(X) strerror(X)
546 #include "mDNSEmbeddedAPI.h"
547 #include "DNSCommon.h"
548 #include "uds_daemon.h"
550 #include "dnssd_ipc.h"
552 // Apple specific configuration functionality, not required for other platforms
554 #include <sys/ucred.h>
555 #ifndef LOCAL_PEERCRED
556 #define LOCAL_PEERCRED 0x001 /* retrieve peer credentials */
557 #endif // LOCAL_PEERCRED
560 // Types and Data Structures
561 // ----------------------------------------------------------------------
572 typedef void (*req_termination_fn
)(void *);
574 typedef struct registered_record_entry
578 struct registered_record_entry
*next
;
579 client_context_t client_context
;
580 struct request_state
*rstate
;
581 } registered_record_entry
;
583 // A single registered service: ServiceRecordSet + bookkeeping
584 // Note that we duplicate some fields from parent service_info object
585 // to facilitate cleanup, when instances and parent may be deallocated at different times.
586 typedef struct service_instance
588 struct service_instance
*next
;
589 mDNSBool autoname
; // Set if this name is tied to the Computer Name
590 mDNSBool autorename
; // Set if this client wants us to automatically rename on conflict
591 mDNSBool allowremotequery
; // Respond to unicast queries from outside the local link?
592 mDNSBool rename_on_memfree
; // Set on config change when we deregister original name
595 mDNSBool default_local
; // is this the "local." from an empty-string registration?
596 struct request_state
*request
;
598 AuthRecord
*subtypes
;
599 ServiceRecordSet srs
; // note - must be last field in struct
602 // A client-created service. May reference several service_info objects if default
603 // settings cause registration in multiple domains.
610 char type_as_string
[MAX_ESCAPED_DOMAIN_NAME
];
612 mDNSBool default_domain
;
614 mDNSBool autoname
; // Set if this name is tied to the Computer Name
615 mDNSBool autorename
; // Set if this client wants us to automatically rename on conflict
616 mDNSBool allowremotequery
; // Respond to unicast queries from outside the local link?
618 mDNSInterfaceID InterfaceID
;
619 service_instance
*instances
;
620 struct request_state
*request
;
623 // for multi-domain default browsing
624 typedef struct browser_t
628 struct browser_t
*next
;
631 // parent struct for browser instances: list pointer plus metadata
634 mDNSBool default_domain
;
637 mDNSInterfaceID interface_id
;
638 struct request_state
*rstate
;
644 mStatus err
; // Note: This field is in NETWORK byte order
647 } undelivered_error_t
;
649 typedef struct request_state
651 // connection structures
654 // state of read (in case message is read over several recv() calls)
656 uint32_t hdr_bytes
; // bytes of header already read
658 uint32_t data_bytes
; // bytes of message data already read
659 char *msgbuf
; // pointer to data storage to pass to free()
660 char *msgdata
; // pointer to data to be read from (may be modified)
661 int bufsize
; // size of data storage
663 // reply, termination, error, and client context info
664 int no_reply
; // don't send asynchronous replies to client
665 int time_blocked
; // record time of a blocked client
666 void *client_context
; // don't touch this - pointer only valid in client's addr space
667 struct reply_state
*replies
; // corresponding (active) reply list
668 undelivered_error_t
*u_err
;
669 void *termination_context
;
670 req_termination_fn terminate
;
672 //!!!KRS toss these pointers in a union
673 // registration context associated with this request (null if not applicable)
674 registered_record_entry
*reg_recs
; // muliple registrations for a connection-oriented request
675 service_info
*service_registration
;
676 browser_info_t
*browser_info
;
677 struct request_state
*next
;
680 // struct physically sits between ipc message header and call-specific fields in the message buffer
683 DNSServiceFlags flags
; // Note: This field is in NETWORK byte order
684 uint32_t ifi
; // Note: This field is in NETWORK byte order
685 DNSServiceErrorType error
; // Note: This field is in NETWORK byte order
688 typedef struct reply_state
690 // state of the transmission
695 // context of the reply
696 struct request_state
*request
; // the request that this answers
697 struct reply_state
*next
; // if there are multiple unsent replies
698 // pointer into message buffer - allows fields to be changed after message is formatted
701 char *sdata
; // pointer to start of call-specific data
702 // pointer to malloc'd buffer
706 // domain enumeration and resolv calls require 2 mDNSCore calls, so we need separate interconnected
707 // structures to handle callbacks
710 DNSQuestion question
;
711 mDNS_DomainType type
;
712 request_state
*rstate
;
719 request_state
*rstate
;
720 } enum_termination_t
;
724 request_state
*rstate
;
727 // const ResourceRecord *txt;
728 // const ResourceRecord *srv;
734 mDNSu8 txtdata
[AbsoluteMaxDNSMessageData
];
735 } resolve_termination_t
;
737 #ifdef _HAVE_SETDOMAIN_SUPPORT_
738 typedef struct default_browse_list_t
740 struct default_browse_list_t
*next
;
743 } default_browse_list_t
;
745 static default_browse_list_t
*default_browse_list
= NULL
;
746 #endif // _HAVE_SETDOMAIN_SUPPORT_
749 mDNSexport mDNS mDNSStorage
;
750 #define gmDNS (&mDNSStorage)
752 static dnssd_sock_t listenfd
= dnssd_InvalidSocket
;
753 static request_state
* all_requests
= NULL
;
755 #define MAX_TIME_BLOCKED 60 * mDNSPlatformOneSecond // try to send data to a blocked client for 60 seconds before
756 // terminating connection
757 #define MSG_PAD_BYTES 5 // pad message buffer (read from client) with n zero'd bytes to guarantee
758 // n get_string() calls w/o buffer overrun
759 // private function prototypes
760 static void connect_callback(void *info
);
761 static int read_msg(request_state
*rs
);
762 static int send_msg(reply_state
*rs
);
763 static void abort_request(request_state
*rs
);
764 static void request_callback(void *info
);
765 static void handle_resolve_request(request_state
*rstate
);
766 static void question_result_callback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
);
767 static void question_termination_callback(void *context
);
768 static void handle_browse_request(request_state
*request
);
769 static void browse_termination_callback(void *context
);
770 static void browse_result_callback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
);
771 static void handle_regservice_request(request_state
*request
);
772 static void regservice_termination_callback(void *context
);
773 static void process_service_registration(ServiceRecordSet
*const srs
);
774 static void regservice_callback(mDNS
*const m
, ServiceRecordSet
*const srs
, mStatus result
);
775 static mStatus
handle_add_request(request_state
*rstate
);
776 static mStatus
handle_update_request(request_state
*rstate
);
777 static mStatus
gen_rr_response(domainname
*servicename
, mDNSInterfaceID id
, request_state
*request
, reply_state
**rep
);
778 static void append_reply(request_state
*req
, reply_state
*rep
);
779 static int build_domainname_from_strings(domainname
*srv
, char *name
, char *regtype
, char *domain
);
780 static void enum_termination_callback(void *context
);
781 static void enum_result_callback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
);
782 static void handle_query_request(request_state
*rstate
);
783 static reply_state
*format_enumeration_reply(request_state
*rstate
, const char *domain
, DNSServiceFlags flags
, uint32_t ifi
, DNSServiceErrorType err
);
784 static void handle_enum_request(request_state
*rstate
);
785 static mStatus
handle_regrecord_request(request_state
*rstate
);
786 static void regrecord_callback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
);
787 static void connected_registration_termination(void *context
);
788 static void handle_reconfirm_request(request_state
*rstate
);
789 static AuthRecord
*read_rr_from_ipc_msg(char *msgbuf
, int ttl
, int validate_flags
);
790 static mStatus
handle_removerecord_request(request_state
*rstate
);
791 static void reset_connected_rstate(request_state
*rstate
);
792 static int deliver_error(request_state
*rstate
, mStatus err
);
793 static int deliver_async_error(request_state
*rs
, reply_op_t op
, mStatus err
);
794 static transfer_state
send_undelivered_error(request_state
*rs
);
795 static reply_state
*create_reply(reply_op_t op
, size_t datalen
, request_state
*request
);
796 static void update_callback(mDNS
*const m
, AuthRecord
*const rr
, RData
*oldrd
);
797 static void my_perror(char *errmsg
);
798 static void unlink_request(request_state
*rs
);
799 static void resolve_result_callback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
);
800 static void resolve_termination_callback(void *context
);
801 static int validate_message(request_state
*rstate
);
802 static mStatus
remove_extra(request_state
*rstate
, service_instance
*serv
);
803 static mStatus
remove_record(request_state
*rstate
);
804 static void free_service_instance(service_instance
*srv
);
805 static uint32_t dnssd_htonl(uint32_t l
);
806 static void handle_setdomain_request(request_state
*rstate
);
808 // initialization, setup/teardown functions
810 // If a platform specifies its own PID file name, we use that
812 #define PID_FILE "/var/run/mDNSResponder.pid"
815 static void FatalError(char *errmsg
)
817 LogMsg("%s: %s", errmsg
, dnssd_strerror(dnssd_errno()));
818 *(long*)0 = 0; // On OS X abort() doesn't generate a crash log, but writing to zero does
819 abort(); // On platforms where writing to zero doesn't generate an exception, abort instead
822 int udsserver_init(void)
824 dnssd_sockaddr_t laddr
;
830 // If a particular platform wants to opt out of having a PID file, define PID_FILE to be ""
833 FILE *fp
= fopen(PID_FILE
, "w");
836 fprintf(fp
, "%d\n", getpid());
841 if ((listenfd
= socket(AF_DNSSD
, SOCK_STREAM
, 0)) == dnssd_InvalidSocket
)
844 bzero(&laddr
, sizeof(laddr
));
846 #if defined(USE_TCP_LOOPBACK)
848 laddr
.sin_family
= AF_INET
;
849 laddr
.sin_port
= htons(MDNS_TCP_SERVERPORT
);
850 laddr
.sin_addr
.s_addr
= inet_addr(MDNS_TCP_SERVERADDR
);
851 ret
= bind(listenfd
, (struct sockaddr
*) &laddr
, sizeof(laddr
));
857 mode_t mask
= umask(0);
858 unlink(MDNS_UDS_SERVERPATH
); //OK if this fails
859 laddr
.sun_family
= AF_LOCAL
;
860 #ifndef NOT_HAVE_SA_LEN
861 // According to Stevens (section 3.2), there is no portable way to
862 // determine whether sa_len is defined on a particular platform.
863 laddr
.sun_len
= sizeof(struct sockaddr_un
);
865 strcpy(laddr
.sun_path
, MDNS_UDS_SERVERPATH
);
866 ret
= bind(listenfd
, (struct sockaddr
*) &laddr
, sizeof(laddr
));
875 // SEH: do we even need to do this on windows? this socket
876 // will be given to WSAEventSelect which will automatically
877 // set it to non-blocking
879 if (ioctlsocket(listenfd
, FIONBIO
, &opt
) != 0)
881 if (fcntl(listenfd
, F_SETFL
, O_NONBLOCK
) != 0)
884 my_perror("ERROR: could not set listen socket to non-blocking mode");
888 if (listen(listenfd
, LISTENQ
) != 0)
890 my_perror("ERROR: could not listen on listen socket");
894 if (mStatus_NoError
!= udsSupportAddFDToEventLoop(listenfd
, connect_callback
, (void *) NULL
))
896 my_perror("ERROR: could not add listen socket to event loop");
900 #if !defined(PLATFORM_NO_RLIMIT)
902 // Set maximum number of open file descriptors
903 #define MIN_OPENFILES 10240
904 struct rlimit maxfds
, newfds
;
906 // Due to bugs in OS X (<rdar://problem/2941095>, <rdar://problem/3342704>, <rdar://problem/3839173>)
907 // you have to get and set rlimits once before getrlimit will return sensible values
908 if (getrlimit(RLIMIT_NOFILE
, &maxfds
) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; }
909 if (setrlimit(RLIMIT_NOFILE
, &maxfds
) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit");
911 if (getrlimit(RLIMIT_NOFILE
, &maxfds
) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; }
912 newfds
.rlim_max
= (maxfds
.rlim_max
> MIN_OPENFILES
) ? maxfds
.rlim_max
: MIN_OPENFILES
;
913 newfds
.rlim_cur
= (maxfds
.rlim_cur
> MIN_OPENFILES
) ? maxfds
.rlim_cur
: MIN_OPENFILES
;
914 if (newfds
.rlim_max
!= maxfds
.rlim_max
|| newfds
.rlim_cur
!= maxfds
.rlim_cur
)
915 if (setrlimit(RLIMIT_NOFILE
, &newfds
) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit");
917 if (getrlimit(RLIMIT_NOFILE
, &maxfds
) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; }
918 debugf("maxfds.rlim_max %d", (long)maxfds
.rlim_max
);
919 debugf("maxfds.rlim_cur %d", (long)maxfds
.rlim_cur
);
927 my_perror("ERROR: udsserver_init");
931 int udsserver_exit(void)
933 dnssd_close(listenfd
);
935 #if !defined(USE_TCP_LOOPBACK)
936 unlink(MDNS_UDS_SERVERPATH
);
942 mDNSs32
udsserver_idle(mDNSs32 nextevent
)
944 request_state
*req
= all_requests
, *tmp
, *prev
= NULL
;
946 transfer_state result
;
947 mDNSs32 now
= mDNS_TimeNow(&mDNSStorage
);
951 result
= t_uninitialized
;
953 result
= send_undelivered_error(req
);
954 if (result
!= t_error
&& result
!= t_morecoming
&& // don't try to send msg if send_error failed
955 (req
->ts
== t_complete
|| req
->ts
== t_morecoming
))
959 if (req
->replies
->next
) req
->replies
->rhdr
->flags
|= dnssd_htonl(kDNSServiceFlagsMoreComing
);
960 result
= send_msg(req
->replies
);
961 if (result
== t_complete
)
964 req
->replies
= req
->replies
->next
;
965 freeL("udsserver_idle", fptr
);
966 req
->time_blocked
= 0; // reset failure counter after successful send
968 else if (result
== t_terminated
|| result
== t_error
)
973 else if (result
== t_morecoming
) break; // client's queues are full, move to next
976 if (result
== t_morecoming
)
978 if (!req
->time_blocked
) req
->time_blocked
= now
;
979 debugf("udsserver_idle: client has been blocked for %ld seconds", (now
- req
->time_blocked
) / mDNSPlatformOneSecond
);
980 if (now
- req
->time_blocked
>= MAX_TIME_BLOCKED
)
982 LogMsg("Could not write data to client after %ld seconds - aborting connection", MAX_TIME_BLOCKED
/ mDNSPlatformOneSecond
);
984 result
= t_terminated
;
986 else if (nextevent
- now
> mDNSPlatformOneSecond
) nextevent
= now
+ mDNSPlatformOneSecond
; // try again in a second
988 if (result
== t_terminated
|| result
== t_error
)
989 //since we're already doing a list traversal, we unlink the request manunally instead of calling unlink_request()
992 if (prev
) prev
->next
= req
->next
;
993 if (req
== all_requests
) all_requests
= all_requests
->next
;
995 freeL("udsserver_idle", tmp
);
1006 void udsserver_info(mDNS
*const m
)
1008 mDNSs32 now
= mDNS_TimeNow(m
);
1009 mDNSu32 CacheUsed
= 0, CacheActive
= 0;
1015 LogMsgNoIdent("Timenow 0x%08lX (%ld)", (mDNSu32
)now
, now
);
1017 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++)
1018 for(cg
= m
->rrcache_hash
[slot
]; cg
; cg
=cg
->next
)
1020 CacheUsed
++; // Count one cache entity for the CacheGroup object
1021 for (rr
= cg
->members
; rr
; rr
=rr
->next
)
1023 mDNSs32 remain
= rr
->resrec
.rroriginalttl
- (now
- rr
->TimeRcvd
) / mDNSPlatformOneSecond
;
1025 if (rr
->CRActiveQuestion
) CacheActive
++;
1026 LogMsgNoIdent("%s%6ld %s%-6s%-6s%s",
1027 rr
->CRActiveQuestion
? "*" : " ", remain
,
1028 (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) ? "-" : " ", DNSTypeName(rr
->resrec
.rrtype
),
1029 ((NetworkInterfaceInfo
*)rr
->resrec
.InterfaceID
)->ifname
, CRDisplayString(m
, rr
));
1030 usleep(1000); // Limit rate a little so we don't flood syslog too fast
1034 if (m
->rrcache_totalused
!= CacheUsed
)
1035 LogMsgNoIdent("Cache use mismatch: rrcache_totalused is %lu, true count %lu", m
->rrcache_totalused
, CacheUsed
);
1036 if (m
->rrcache_active
!= CacheActive
)
1037 LogMsgNoIdent("Cache use mismatch: rrcache_active is %lu, true count %lu", m
->rrcache_active
, CacheActive
);
1038 LogMsgNoIdent("Cache currently contains %lu records; %lu referenced by active questions", CacheUsed
, CacheActive
);
1040 for (req
= all_requests
; req
; req
=req
->next
)
1042 void *t
= req
->termination_context
;
1044 if (req
->terminate
== regservice_termination_callback
)
1046 service_instance
*ptr
;
1047 for (ptr
= ((service_info
*)t
)->instances
; ptr
; ptr
= ptr
->next
)
1048 LogMsgNoIdent("%3d: DNSServiceRegister %##s %u", req
->sd
, ptr
->srs
.RR_SRV
.resrec
.name
->c
, SRS_PORT(&ptr
->srs
));
1050 else if (req
->terminate
== browse_termination_callback
)
1053 for (blist
= req
->browser_info
->browsers
; blist
; blist
= blist
->next
)
1054 LogMsgNoIdent("%3d: DNSServiceBrowse %##s", req
->sd
, blist
->q
.qname
.c
);
1056 else if (req
->terminate
== resolve_termination_callback
)
1057 LogMsgNoIdent("%3d: DNSServiceResolve %##s", req
->sd
, ((resolve_termination_t
*)t
)->qsrv
.qname
.c
);
1058 else if (req
->terminate
== question_termination_callback
)
1059 LogMsgNoIdent("%3d: DNSServiceQueryRecord %##s", req
->sd
, ((DNSQuestion
*) t
)->qname
.c
);
1060 else if (req
->terminate
== enum_termination_callback
)
1061 LogMsgNoIdent("%3d: DNSServiceEnumerateDomains %##s", req
->sd
, ((enum_termination_t
*) t
)->all
->question
.qname
.c
);
1064 now
= mDNS_TimeNow(m
);
1065 LogMsgNoIdent("Timenow 0x%08lX (%ld)", (mDNSu32
)now
, now
);
1068 static void rename_service(service_instance
*srv
)
1070 if (srv
->autoname
&& !SameDomainLabel(srv
->name
.c
, gmDNS
->nicelabel
.c
))
1072 srv
->rename_on_memfree
= 1;
1073 if (mDNS_DeregisterService(gmDNS
, &srv
->srs
)) // If service deregistered already, we can re-register immediately
1074 regservice_callback(gmDNS
, &srv
->srs
, mStatus_MemFree
);
1078 void udsserver_handle_configchange(void)
1083 for (req
= all_requests
; req
; req
= req
->next
)
1085 if (req
->service_registration
)
1087 service_instance
*ptr
;
1088 for (ptr
= req
->service_registration
->instances
; ptr
; ptr
= ptr
->next
)
1089 rename_service(ptr
);
1094 static void connect_callback(void *info
)
1098 unsigned long optval
;
1099 dnssd_sockaddr_t cliaddr
;
1100 request_state
*rstate
;
1101 (void)info
; // Unused
1103 len
= (int) sizeof(cliaddr
);
1105 sd
= accept(listenfd
, (struct sockaddr
*) &cliaddr
, &len
);
1107 if (sd
== dnssd_InvalidSocket
)
1109 if (dnssd_errno() == dnssd_EWOULDBLOCK
) return;
1110 my_perror("ERROR: accept");
1116 // Some environments (e.g. OS X) support turning off SIGPIPE for a socket
1117 if (setsockopt(sd
, SOL_SOCKET
, SO_NOSIGPIPE
, &optval
, sizeof(optval
)) < 0)
1119 my_perror("ERROR: setsockopt - SO_NOSIGPIPE - aborting client");
1126 if (ioctlsocket(sd
, FIONBIO
, &optval
) != 0)
1128 if (fcntl(sd
, F_SETFL
, O_NONBLOCK
) != 0)
1131 my_perror("ERROR: fcntl(sd, F_SETFL, O_NONBLOCK) - aborting client");
1136 // allocate a request_state struct that will live with the socket
1137 rstate
= mallocL("connect_callback", sizeof(request_state
));
1138 if (!rstate
) FatalError("ERROR: malloc");
1139 bzero(rstate
, sizeof(request_state
));
1140 rstate
->ts
= t_morecoming
;
1143 LogOperation("%3d: Adding FD", rstate
->sd
);
1144 if ( mStatus_NoError
!= udsSupportAddFDToEventLoop( sd
, request_callback
, rstate
))
1146 rstate
->next
= all_requests
;
1147 all_requests
= rstate
;
1151 static void request_callback(void *info
)
1153 request_state
*rstate
= info
;
1154 transfer_state result
;
1155 dnssd_sockaddr_t cliaddr
;
1156 int dedicated_error_socket
;
1161 result
= read_msg(rstate
);
1162 if (result
== t_morecoming
)
1166 if (result
== t_terminated
)
1168 abort_request(rstate
);
1169 unlink_request(rstate
);
1172 if (result
== t_error
)
1174 abort_request(rstate
);
1175 unlink_request(rstate
);
1179 if (rstate
->hdr
.version
!= VERSION
)
1181 LogMsg("ERROR: client incompatible with daemon (client version = %d, "
1182 "daemon version = %d)\n", rstate
->hdr
.version
, VERSION
);
1183 abort_request(rstate
);
1184 unlink_request(rstate
);
1188 if (validate_message(rstate
) < 0)
1190 // note that we cannot deliver an error message if validation fails, since the path to the error socket
1191 // may be contained in the (invalid) message body for some message types
1192 abort_request(rstate
);
1193 unlink_request(rstate
);
1194 LogMsg("Invalid message sent by client - may indicate a malicious program running on this machine!");
1198 // check if client wants silent operation
1199 if (rstate
->hdr
.flags
& IPC_FLAGS_NOREPLY
) rstate
->no_reply
= 1;
1201 dedicated_error_socket
= (rstate
->hdr
.op
== reg_record_request
|| rstate
->hdr
.op
== add_record_request
||
1202 rstate
->hdr
.op
== update_record_request
|| rstate
->hdr
.op
== remove_record_request
);
1204 if (((rstate
->hdr
.flags
& IPC_FLAGS_REUSE_SOCKET
) == 0) != dedicated_error_socket
)
1205 LogMsg("WARNING: client request %d with incorrect flags setting 0x%X", rstate
->hdr
.op
, rstate
->hdr
.flags
);
1207 // check if primary socket is to be used for synchronous errors, else open new socket
1208 if (dedicated_error_socket
)
1212 int errfd
= socket(AF_DNSSD
, SOCK_STREAM
, 0);
1213 if (errfd
== dnssd_InvalidSocket
)
1215 my_perror("ERROR: socket");
1216 abort_request(rstate
);
1217 unlink_request(rstate
);
1221 #if defined(USE_TCP_LOOPBACK)
1224 port
.b
[0] = rstate
->msgdata
[0];
1225 port
.b
[1] = rstate
->msgdata
[1];
1226 rstate
->msgdata
+= 2;
1227 cliaddr
.sin_family
= AF_INET
;
1228 cliaddr
.sin_port
= port
.NotAnInteger
;
1229 cliaddr
.sin_addr
.s_addr
= inet_addr(MDNS_TCP_SERVERADDR
);
1233 char ctrl_path
[MAX_CTLPATH
];
1234 get_string(&rstate
->msgdata
, ctrl_path
, 256); // path is first element in message buffer
1235 bzero(&cliaddr
, sizeof(cliaddr
));
1236 cliaddr
.sun_family
= AF_LOCAL
;
1237 strcpy(cliaddr
.sun_path
, ctrl_path
);
1240 if (connect(errfd
, (struct sockaddr
*)&cliaddr
, sizeof(cliaddr
)) < 0)
1242 my_perror("ERROR: connect");
1243 abort_request(rstate
);
1244 unlink_request(rstate
);
1248 if (ioctlsocket(errfd
, FIONBIO
, &opt
) != 0)
1250 if (fcntl(errfd
, F_SETFL
, O_NONBLOCK
) != 0)
1253 my_perror("ERROR: could not set control socket to non-blocking mode");
1254 abort_request(rstate
);
1255 unlink_request(rstate
);
1259 switch(rstate
->hdr
.op
)
1261 case reg_record_request
: err
= handle_regrecord_request (rstate
); break;
1262 case add_record_request
: err
= handle_add_request (rstate
); break;
1263 case update_record_request
: err
= handle_update_request (rstate
); break;
1264 case remove_record_request
: err
= handle_removerecord_request(rstate
); break;
1265 default: LogMsg("%3d: ERROR: udsserver_recv_request - unsupported request type: %d", rstate
->sd
, rstate
->hdr
.op
);
1268 err
= dnssd_htonl(err
);
1269 nwritten
= send(errfd
, &err
, sizeof(err
), 0);
1270 // On a freshly-created Unix Domain Socket, the kernel should *never* fail to buffer a four-byte write for us.
1271 // If not, we don't attempt to handle this failure, but we do log it.
1272 if (nwritten
< (int)sizeof(err
))
1273 LogMsg("ERROR: failed to write error response back to caller: %d %d %s",
1274 nwritten
, dnssd_errno(), dnssd_strerror(dnssd_errno()));
1276 reset_connected_rstate(rstate
); // Reset ready to accept the next request on this pipe
1280 switch(rstate
->hdr
.op
)
1282 case resolve_request
: handle_resolve_request (rstate
); break;
1283 case query_request
: handle_query_request (rstate
); break;
1284 case browse_request
: handle_browse_request (rstate
); break;
1285 case reg_service_request
: handle_regservice_request(rstate
); break;
1286 case enumeration_request
: handle_enum_request (rstate
); break;
1287 case reconfirm_record_request
: handle_reconfirm_request (rstate
); break;
1288 case setdomain_request
: handle_setdomain_request (rstate
); break;
1289 default: LogMsg("%3d: ERROR: udsserver_recv_request - unsupported request type: %d", rstate
->sd
, rstate
->hdr
.op
);
1294 // mDNS operation functions. Each operation has 3 associated functions - a request handler that parses
1295 // the client's request and makes the appropriate mDNSCore call, a result handler (passed as a callback
1296 // to the mDNSCore routine) that sends results back to the client, and a termination routine that aborts
1297 // the mDNSCore operation if the client dies or closes its socket.
1299 // query and resolve calls have separate request handlers that parse the arguments from the client and
1300 // massage the name parameters appropriately, but the rest of the operations (making the query call,
1301 // delivering the result to the client, and termination) are identical.
1303 static void handle_query_request(request_state
*rstate
)
1305 DNSServiceFlags flags
;
1308 uint16_t rrtype
, rrclass
;
1311 mDNSInterfaceID InterfaceID
;
1314 if (rstate
->ts
!= t_complete
)
1316 LogMsg("ERROR: handle_query_request - transfer state != t_complete");
1319 ptr
= rstate
->msgdata
;
1322 LogMsg("ERROR: handle_query_request - NULL msgdata");
1326 flags
= get_flags(&ptr
);
1327 ifi
= get_long(&ptr
);
1328 if (get_string(&ptr
, name
, 256) < 0) goto bad_param
;
1329 rrtype
= get_short(&ptr
);
1330 rrclass
= get_short(&ptr
);
1331 InterfaceID
= mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS
, ifi
);
1332 if (ifi
&& !InterfaceID
) goto bad_param
;
1334 q
= mallocL("DNSQuestion", sizeof(DNSQuestion
));
1335 if (!q
) FatalError("ERROR: handle_query - malloc");
1336 bzero(q
, sizeof(DNSQuestion
));
1338 q
->InterfaceID
= InterfaceID
;
1339 q
->Target
= zeroAddr
;
1340 if (!MakeDomainNameFromDNSNameString(&q
->qname
, name
)) { freeL("DNSQuestion", q
); goto bad_param
; }
1342 q
->qclass
= rrclass
;
1343 q
->LongLived
= (flags
& kDNSServiceFlagsLongLivedQuery
) != 0;
1344 q
->ExpectUnique
= mDNSfalse
;
1345 q
->ForceMCast
= (flags
& kDNSServiceFlagsForceMulticast
) != 0;
1346 q
->QuestionCallback
= question_result_callback
;
1347 q
->QuestionContext
= rstate
;
1349 rstate
->termination_context
= q
;
1350 rstate
->terminate
= question_termination_callback
;
1352 LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) START", rstate
->sd
, q
->qname
.c
, DNSTypeName(q
->qtype
));
1353 result
= mDNS_StartQuery(gmDNS
, q
);
1354 if (result
!= mStatus_NoError
) LogMsg("ERROR: mDNS_StartQuery: %d", (int)result
);
1356 if (result
) rstate
->terminate
= NULL
;
1357 if (deliver_error(rstate
, result
) < 0) goto error
;
1361 deliver_error(rstate
, mStatus_BadParamErr
);
1362 rstate
->terminate
= NULL
; // don't try to terminate insuccessful Core calls
1364 abort_request(rstate
);
1365 unlink_request(rstate
);
1369 static void handle_resolve_request(request_state
*rstate
)
1371 DNSServiceFlags flags
;
1372 uint32_t interfaceIndex
;
1373 mDNSInterfaceID InterfaceID
;
1374 char name
[256], regtype
[MAX_ESCAPED_DOMAIN_NAME
], domain
[MAX_ESCAPED_DOMAIN_NAME
];
1375 char *ptr
; // message data pointer
1377 resolve_termination_t
*term
;
1380 if (rstate
->ts
!= t_complete
)
1382 LogMsg("ERROR: handle_resolve_request - transfer state != t_complete");
1383 abort_request(rstate
);
1384 unlink_request(rstate
);
1388 // extract the data from the message
1389 ptr
= rstate
->msgdata
;
1392 LogMsg("ERROR: handle_resolve_request - NULL msgdata");
1393 abort_request(rstate
);
1394 unlink_request(rstate
);
1397 flags
= get_flags(&ptr
);
1398 interfaceIndex
= get_long(&ptr
);
1399 InterfaceID
= mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS
, interfaceIndex
);
1400 if (interfaceIndex
&& !InterfaceID
)
1401 { LogMsg("ERROR: handle_resolve_request - Couldn't find InterfaceID for interfaceIndex %d", interfaceIndex
); goto bad_param
; }
1402 if (get_string(&ptr
, name
, 256) < 0 ||
1403 get_string(&ptr
, regtype
, MAX_ESCAPED_DOMAIN_NAME
) < 0 ||
1404 get_string(&ptr
, domain
, MAX_ESCAPED_DOMAIN_NAME
) < 0)
1405 { LogMsg("ERROR: handle_resolve_request - Couldn't read name/regtype/domain"); goto bad_param
; }
1407 // free memory in rstate since we don't need it anymore
1408 freeL("handle_resolve_request", rstate
->msgbuf
);
1409 rstate
->msgbuf
= NULL
;
1411 if (build_domainname_from_strings(&fqdn
, name
, regtype
, domain
) < 0)
1412 { LogMsg("ERROR: handle_resolve_request - Couldn't build_domainname_from_strings “%s” “%s” “%s”", name
, regtype
, domain
); goto bad_param
; }
1414 // set up termination info
1415 term
= mallocL("handle_resolve_request", sizeof(resolve_termination_t
));
1416 bzero(term
, sizeof(*term
));
1417 if (!term
) FatalError("ERROR: malloc");
1420 term
->qsrv
.InterfaceID
= InterfaceID
;
1421 term
->qsrv
.Target
= zeroAddr
;
1422 memcpy(&term
->qsrv
.qname
, &fqdn
, MAX_DOMAIN_NAME
);
1423 term
->qsrv
.qtype
= kDNSType_SRV
;
1424 term
->qsrv
.qclass
= kDNSClass_IN
;
1425 term
->qsrv
.LongLived
= mDNSfalse
;
1426 term
->qsrv
.ExpectUnique
= mDNStrue
;
1427 term
->qsrv
.ForceMCast
= mDNSfalse
;
1428 term
->qsrv
.QuestionCallback
= resolve_result_callback
;
1429 term
->qsrv
.QuestionContext
= rstate
;
1431 term
->qtxt
.InterfaceID
= InterfaceID
;
1432 term
->qtxt
.Target
= zeroAddr
;
1433 memcpy(&term
->qtxt
.qname
, &fqdn
, MAX_DOMAIN_NAME
);
1434 term
->qtxt
.qtype
= kDNSType_TXT
;
1435 term
->qtxt
.qclass
= kDNSClass_IN
;
1436 term
->qtxt
.LongLived
= mDNSfalse
;
1437 term
->qtxt
.ExpectUnique
= mDNStrue
;
1438 term
->qtxt
.ForceMCast
= mDNSfalse
;
1439 term
->qtxt
.QuestionCallback
= resolve_result_callback
;
1440 term
->qtxt
.QuestionContext
= rstate
;
1442 term
->rstate
= rstate
;
1443 rstate
->termination_context
= term
;
1444 rstate
->terminate
= resolve_termination_callback
;
1446 // ask the questions
1447 LogOperation("%3d: DNSServiceResolve(%##s) START", rstate
->sd
, term
->qsrv
.qname
.c
);
1448 err
= mDNS_StartQuery(gmDNS
, &term
->qsrv
);
1449 if (!err
) err
= mDNS_StartQuery(gmDNS
, &term
->qtxt
);
1453 freeL("handle_resolve_request", term
);
1454 rstate
->terminate
= NULL
; // prevent abort_request() from invoking termination callback
1456 if (deliver_error(rstate
, err
) < 0 || err
)
1458 abort_request(rstate
);
1459 unlink_request(rstate
);
1464 deliver_error(rstate
, mStatus_BadParamErr
);
1465 abort_request(rstate
);
1466 unlink_request(rstate
);
1469 static void resolve_termination_callback(void *context
)
1471 resolve_termination_t
*term
= context
;
1476 LogMsg("ERROR: resolve_termination_callback: double termination");
1480 LogOperation("%3d: DNSServiceResolve(%##s) STOP", rs
->sd
, term
->qtxt
.qname
.c
);
1482 mDNS_StopQuery(gmDNS
, &term
->qtxt
);
1483 mDNS_StopQuery(gmDNS
, &term
->qsrv
);
1485 freeL("resolve_termination_callback", term
);
1486 rs
->termination_context
= NULL
;
1489 static void resolve_result_callback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
1492 char fullname
[MAX_ESCAPED_DOMAIN_NAME
], target
[MAX_ESCAPED_DOMAIN_NAME
];
1494 transfer_state result
;
1496 request_state
*rs
= question
->QuestionContext
;
1497 resolve_termination_t
*res
= rs
->termination_context
;
1500 LogOperation("%3d: DNSServiceResolve(%##s, %s) RESULT %s", rs
->sd
, question
->qname
.c
, DNSTypeName(question
->qtype
), RRDisplayString(m
, answer
));
1502 // This code used to do this trick of just keeping a copy of the pointer to
1503 // the answer record in the cache, but the unicast query code doesn't currently
1504 // put its answer records in the cache, so for now we can't do this.
1508 // if (answer->rrtype == kDNSType_TXT && res->txt == answer) res->txt = mDNSNULL;
1509 // if (answer->rrtype == kDNSType_SRV && res->srv == answer) res->srv = mDNSNULL;
1513 // if (answer->rrtype == kDNSType_TXT) res->txt = answer;
1514 // if (answer->rrtype == kDNSType_SRV) res->srv = answer;
1516 if (answer
->rrtype
== kDNSType_SRV
)
1518 AssignDomainName(&res
->target
, &answer
->rdata
->u
.srv
.target
);
1519 res
->port
= answer
->rdata
->u
.srv
.port
;
1520 res
->srv
= mDNStrue
;
1522 if (answer
->rrtype
== kDNSType_TXT
)
1524 if (answer
->rdlength
> AbsoluteMaxDNSMessageData
) return;
1525 res
->txtlen
= answer
->rdlength
;
1526 mDNSPlatformMemCopy(answer
->rdata
->u
.data
, res
->txtdata
, res
->txtlen
);
1527 res
->txt
= mDNStrue
;
1530 if (!res
->txt
|| !res
->srv
) return; // only deliver result to client if we have both answers
1532 ConvertDomainNameToCString(answer
->name
, fullname
);
1533 ConvertDomainNameToCString(&res
->target
, target
);
1535 // calculate reply length
1536 len
+= sizeof(DNSServiceFlags
);
1537 len
+= sizeof(uint32_t); // interface index
1538 len
+= sizeof(DNSServiceErrorType
);
1539 len
+= strlen(fullname
) + 1;
1540 len
+= strlen(target
) + 1;
1541 len
+= 2 * sizeof(uint16_t); // port, txtLen
1544 // allocate/init reply header
1545 rep
= create_reply(resolve_reply
, len
, rs
);
1546 rep
->rhdr
->flags
= dnssd_htonl(0);
1547 rep
->rhdr
->ifi
= dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS
, answer
->InterfaceID
));
1548 rep
->rhdr
->error
= dnssd_htonl(kDNSServiceErr_NoError
);
1552 // write reply data to message
1553 put_string(fullname
, &data
);
1554 put_string(target
, &data
);
1555 *data
++ = res
->port
.b
[0];
1556 *data
++ = res
->port
.b
[1];
1557 put_short(res
->txtlen
, &data
);
1558 put_rdata(res
->txtlen
, res
->txtdata
, &data
);
1560 result
= send_msg(rep
);
1561 if (result
== t_error
|| result
== t_terminated
)
1565 freeL("resolve_result_callback", rep
);
1567 else if (result
== t_complete
) freeL("resolve_result_callback", rep
);
1568 else append_reply(rs
, rep
);
1571 // what gets called when a resolve is completed and we need to send the data back to the client
1572 static void question_result_callback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
1575 char name
[MAX_ESCAPED_DOMAIN_NAME
];
1576 request_state
*req
= question
->QuestionContext
;
1581 LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) RESULT %s", req
->sd
, question
->qname
.c
, DNSTypeName(question
->qtype
), RRDisplayString(m
, answer
));
1582 //mDNS_StopQuery(m, question);
1584 // calculate reply data length
1585 len
= sizeof(DNSServiceFlags
);
1586 len
+= 2 * sizeof(uint32_t); // if index + ttl
1587 len
+= sizeof(DNSServiceErrorType
);
1588 len
+= 3 * sizeof(uint16_t); // type, class, rdlen
1589 len
+= answer
->rdlength
;
1590 ConvertDomainNameToCString(answer
->name
, name
);
1591 len
+= strlen(name
) + 1;
1593 rep
= create_reply(query_reply
, len
, req
);
1595 rep
->rhdr
->flags
= dnssd_htonl(AddRecord
? kDNSServiceFlagsAdd
: 0);
1596 rep
->rhdr
->ifi
= dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS
, answer
->InterfaceID
));
1597 rep
->rhdr
->error
= dnssd_htonl(kDNSServiceErr_NoError
);
1601 put_string(name
, &data
);
1602 put_short(answer
->rrtype
, &data
);
1603 put_short(answer
->rrclass
, &data
);
1604 put_short(answer
->rdlength
, &data
);
1605 put_rdata(answer
->rdlength
, answer
->rdata
->u
.data
, &data
);
1606 put_long(AddRecord
? answer
->rroriginalttl
: 0, &data
);
1608 append_reply(req
, rep
);
1612 static void question_termination_callback(void *context
)
1614 DNSQuestion
*q
= context
;
1615 LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) STOP", ((request_state
*)q
->QuestionContext
)->sd
, q
->qname
.c
, DNSTypeName(q
->qtype
));
1616 mDNS_StopQuery(gmDNS
, q
); // no need to error check
1617 freeL("question_termination_callback", q
);
1620 // If there's a comma followed by another character,
1621 // FindFirstSubType overwrites the comma with a nul and returns the pointer to the next character.
1622 // Otherwise, it returns a pointer to the final nul at the end of the string
1623 static char *FindFirstSubType(char *p
)
1627 if (p
[0] == '\\' && p
[1]) p
+= 2;
1628 else if (p
[0] == ',' && p
[1]) { *p
++ = 0; return(p
); }
1634 // If there's a comma followed by another character,
1635 // FindNextSubType overwrites the comma with a nul and returns the pointer to the next character.
1636 // If it finds an illegal unescaped dot in the subtype name, it returns mDNSNULL
1637 // Otherwise, it returns a pointer to the final nul at the end of the string
1638 static char *FindNextSubType(char *p
)
1642 if (p
[0] == '\\' && p
[1]) // If escape character
1643 p
+= 2; // ignore following character
1644 else if (p
[0] == ',') // If we found a comma
1649 else if (p
[0] == '.')
1656 // Returns -1 if illegal subtype found
1657 mDNSexport mDNSs32
ChopSubTypes(char *regtype
)
1659 mDNSs32 NumSubTypes
= 0;
1660 char *stp
= FindFirstSubType(regtype
);
1661 while (stp
&& *stp
) // If we found a comma...
1663 if (*stp
== ',') return(-1);
1665 stp
= FindNextSubType(stp
);
1667 if (!stp
) return(-1);
1668 return(NumSubTypes
);
1671 mDNSexport AuthRecord
*AllocateSubTypes(mDNSs32 NumSubTypes
, char *p
)
1673 AuthRecord
*st
= mDNSNULL
;
1677 st
= mallocL("ServiceSubTypes", NumSubTypes
* sizeof(AuthRecord
));
1678 if (!st
) return(mDNSNULL
);
1679 for (i
= 0; i
< NumSubTypes
; i
++)
1681 mDNS_SetupResourceRecord(&st
[i
], mDNSNULL
, mDNSInterface_Any
, kDNSQType_ANY
, kStandardTTL
, 0, mDNSNULL
, mDNSNULL
);
1684 if (!MakeDomainNameFromDNSNameString(st
[i
].resrec
.name
, p
))
1685 { freeL("ServiceSubTypes", st
); return(mDNSNULL
); }
1691 #ifdef _HAVE_SETDOMAIN_SUPPORT_
1692 static void free_defdomain(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
1695 if (result
== mStatus_MemFree
) free(rr
->RecordContext
); // context is the enclosing list structure
1699 static void handle_setdomain_request(request_state
*request
)
1701 mStatus err
= mStatus_NoError
;
1703 char domainstr
[MAX_ESCAPED_DOMAIN_NAME
];
1705 DNSServiceFlags flags
;
1706 #ifdef _HAVE_SETDOMAIN_SUPPORT_
1711 if (request
->ts
!= t_complete
)
1713 LogMsg("ERROR: handle_setdomain_request - transfer state != t_complete");
1714 abort_request(request
);
1715 unlink_request(request
);
1719 // extract flags/domain from message
1720 ptr
= request
->msgdata
;
1721 flags
= get_flags(&ptr
);
1722 if (get_string(&ptr
, domainstr
, MAX_ESCAPED_DOMAIN_NAME
) < 0 ||
1723 !MakeDomainNameFromDNSNameString(&domain
, domainstr
))
1724 { err
= mStatus_BadParamErr
; goto end
; }
1726 freeL("handle_setdomain_request", request
->msgbuf
);
1727 request
->msgbuf
= NULL
;
1729 debugf("%3d: DNSServiceSetDefaultDomainForUser(%##s)", request
->sd
, domain
.c
);
1731 #ifdef _HAVE_SETDOMAIN_SUPPORT_
1732 // this functionality currently only used for Apple-specific configuration, so we don't burned other platforms by mandating
1733 // the existence of this socket option
1734 xuclen
= sizeof(xuc
);
1735 if (getsockopt(request
->sd
, 0, LOCAL_PEERCRED
, &xuc
, &xuclen
))
1736 { my_perror("ERROR: getsockopt, LOCAL_PEERCRED"); err
= mStatus_UnknownErr
; goto end
; }
1737 if (xuc
.cr_version
!= XUCRED_VERSION
) { LogMsg("getsockopt, LOCAL_PEERCRED - bad version"); err
= mStatus_UnknownErr
; goto end
; }
1738 LogMsg("Default domain %s %s for UID %d", domainstr
, flags
& kDNSServiceFlagsAdd
? "set" : "removed", xuc
.cr_uid
);
1740 if (flags
& kDNSServiceFlagsAdd
)
1742 // register a local-only PRT record
1743 default_browse_list_t
*newelem
= malloc(sizeof(default_browse_list_t
));
1744 if (!newelem
) { LogMsg("ERROR: malloc"); err
= mStatus_NoMemoryErr
; goto end
; }
1745 mDNS_SetupResourceRecord(&newelem
->ptr_rec
, mDNSNULL
, mDNSInterface_LocalOnly
, kDNSType_PTR
, 7200, kDNSRecordTypeShared
, free_defdomain
, newelem
);
1746 MakeDomainNameFromDNSNameString(&newelem
->ptr_rec
.resrec
.name
, mDNS_DomainTypeNames
[mDNS_DomainTypeBrowseDefault
]);
1747 AppendDNSNameString (&newelem
->ptr_rec
.resrec
.name
, "local");
1748 AssignDomainName(&newelem
->ptr_rec
.resrec
.rdata
->u
.name
, &domain
);
1749 newelem
->uid
= xuc
.cr_uid
;
1750 err
= mDNS_Register(gmDNS
, &newelem
->ptr_rec
);
1751 if (err
) free(newelem
);
1755 newelem
->next
= default_browse_list
;
1756 default_browse_list
= newelem
;
1762 // remove - find in list, deregister
1763 default_browse_list_t
*ptr
= default_browse_list
, *prev
= NULL
;
1766 if (SameDomainName(&ptr
->ptr_rec
.resrec
.rdata
->u
.name
, &domain
))
1768 if (prev
) prev
->next
= ptr
->next
;
1769 else default_browse_list
= ptr
->next
;
1770 err
= mDNS_Deregister(gmDNS
, &ptr
->ptr_rec
);
1776 if (!ptr
) { LogMsg("Attempt to remove nonexistent domain %s for UID %d", domainstr
, xuc
.cr_uid
); err
= mStatus_Invalid
; }
1779 err
= mStatus_NoError
;
1780 #endif // _HAVE_SETDOMAIN_SUPPORT_
1783 deliver_error(request
, err
);
1784 abort_request(request
);
1785 unlink_request(request
);
1788 static mStatus
add_domain_to_browser(browser_info_t
*info
, const domainname
*d
)
1793 for (p
= info
->browsers
; p
; p
= p
->next
)
1795 if (SameDomainName(&p
->domain
, d
))
1796 { debugf("add_domain_to_browser - attempt to add domain %##d already in list", d
->c
); return mStatus_AlreadyRegistered
; }
1799 b
= mallocL("browser_t", sizeof(*b
));
1800 if (!b
) return mStatus_NoMemoryErr
;
1801 AssignDomainName(&b
->domain
, d
);
1802 err
= mDNS_StartBrowse(gmDNS
, &b
->q
, &info
->regtype
, d
, info
->interface_id
, info
->ForceMCast
, browse_result_callback
, info
->rstate
);
1805 LogMsg("mDNS_StartBrowse returned %d for type %##s domain %##s", err
, info
->regtype
.c
, d
->c
);
1806 freeL("browser_t", b
);
1810 b
->next
= info
->browsers
;
1816 static void handle_browse_request(request_state
*request
)
1818 DNSServiceFlags flags
;
1819 uint32_t interfaceIndex
;
1820 mDNSInterfaceID InterfaceID
;
1821 char regtype
[MAX_ESCAPED_DOMAIN_NAME
], domain
[MAX_ESCAPED_DOMAIN_NAME
];
1822 domainname typedn
, d
, temp
;
1823 mDNSs32 NumSubTypes
;
1826 DNameListElem
*search_domain_list
, *sdom
;
1827 browser_info_t
*info
= NULL
;
1829 if (request
->ts
!= t_complete
)
1831 LogMsg("ERROR: handle_browse_request - transfer state != t_complete");
1832 abort_request(request
);
1833 unlink_request(request
);
1837 // extract data from message
1838 ptr
= request
->msgdata
;
1839 flags
= get_flags(&ptr
);
1840 interfaceIndex
= get_long(&ptr
);
1841 if (get_string(&ptr
, regtype
, MAX_ESCAPED_DOMAIN_NAME
) < 0 ||
1842 get_string(&ptr
, domain
, MAX_ESCAPED_DOMAIN_NAME
) < 0)
1843 { err
= mStatus_BadParamErr
; goto error
; }
1844 freeL("handle_browse_request", request
->msgbuf
);
1845 request
->msgbuf
= NULL
;
1847 InterfaceID
= mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS
, interfaceIndex
);
1848 if (interfaceIndex
&& !InterfaceID
) { err
= mStatus_BadParamErr
; goto error
; }
1851 NumSubTypes
= ChopSubTypes(regtype
); // Note: Modifies regtype string to remove trailing subtypes
1852 if (NumSubTypes
< 0 || NumSubTypes
> 1) { err
= mStatus_BadParamErr
; goto error
; }
1853 if (NumSubTypes
== 1 && !AppendDNSNameString(&typedn
, regtype
+ strlen(regtype
) + 1))
1854 { err
= mStatus_BadParamErr
; goto error
; }
1856 if (!regtype
[0] || !AppendDNSNameString(&typedn
, regtype
)) { err
= mStatus_BadParamErr
; goto error
; }
1858 if (!MakeDomainNameFromDNSNameString(&temp
, regtype
)) { err
= mStatus_BadParamErr
; goto error
; }
1859 if (temp
.c
[0] > 15 && domain
[0] == 0) strcpy(domain
, "local."); // For over-long service types, we only allow domain "local"
1861 // allocate and set up browser info
1862 info
= mallocL("browser_info_t", sizeof(*info
));
1863 if (!info
) { err
= mStatus_NoMemoryErr
; goto error
; }
1865 request
->browser_info
= info
;
1866 info
->ForceMCast
= (flags
& kDNSServiceFlagsForceMulticast
) != 0;
1867 info
->interface_id
= InterfaceID
;
1868 AssignDomainName(&info
->regtype
, &typedn
);
1869 info
->rstate
= request
;
1870 info
->default_domain
= !domain
[0];
1871 info
->browsers
= NULL
;
1873 // setup termination context
1874 request
->termination_context
= info
;
1875 request
->terminate
= browse_termination_callback
;
1877 LogOperation("%3d: DNSServiceBrowse(%##s%s) START", request
->sd
, info
->regtype
.c
, domain
);
1880 if (!MakeDomainNameFromDNSNameString(&d
, domain
)) { err
= mStatus_BadParamErr
; goto error
; }
1881 err
= add_domain_to_browser(info
, &d
);
1886 search_domain_list
= mDNSPlatformGetSearchDomainList();
1887 for (sdom
= search_domain_list
; sdom
; sdom
= sdom
->next
)
1889 err
= add_domain_to_browser(info
, &sdom
->name
);
1892 if (SameDomainName(&sdom
->name
, &localdomain
)) break;
1893 else err
= mStatus_NoError
; // suppress errors for non-local "default" domains
1897 mDNS_FreeDNameList(search_domain_list
);
1900 deliver_error(request
, mStatus_NoError
);
1904 if (info
) freeL("browser_info_t", info
);
1905 if (request
->termination_context
) request
->termination_context
= NULL
;
1906 deliver_error(request
, err
);
1907 abort_request(request
);
1908 unlink_request(request
);
1911 static void browse_result_callback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
1913 request_state
*req
= question
->QuestionContext
;
1917 LogOperation("%3d: DNSServiceBrowse(%##s, %s) RESULT %s %s",
1918 req
->sd
, question
->qname
.c
, DNSTypeName(question
->qtype
), AddRecord
? "Add" : "Rmv", RRDisplayString(m
, answer
));
1920 err
= gen_rr_response(&answer
->rdata
->u
.name
, answer
->InterfaceID
, req
, &rep
);
1923 if (deliver_async_error(req
, browse_reply
, err
) < 0)
1926 unlink_request(req
);
1930 if (AddRecord
) rep
->rhdr
->flags
|= dnssd_htonl(kDNSServiceFlagsAdd
); // non-zero TTL indicates add
1931 append_reply(req
, rep
);
1935 static void browse_termination_callback(void *context
)
1937 browser_info_t
*info
= context
;
1942 while(info
->browsers
)
1944 ptr
= info
->browsers
;
1945 info
->browsers
= ptr
->next
;
1946 LogOperation("%3d: DNSServiceBrowse(%##s) STOP", info
->rstate
->sd
, ptr
->q
.qname
.c
);
1947 mDNS_StopBrowse(gmDNS
, &ptr
->q
); // no need to error-check result
1948 freeL("browse_termination_callback", ptr
);
1951 info
->rstate
->termination_context
= NULL
;
1952 freeL("browser_info", info
);
1955 mDNSexport
void udsserver_default_browse_domain_changed(const domainname
*d
, mDNSBool add
)
1959 for (r
= all_requests
; r
; r
= r
->next
)
1961 browser_info_t
*info
= r
->browser_info
;
1963 if (!info
|| !info
->default_domain
) continue;
1964 if (add
) add_domain_to_browser(info
, d
);
1967 browser_t
**ptr
= &info
->browsers
;
1970 if (SameDomainName(&(*ptr
)->domain
, d
))
1972 browser_t
*remove
= *ptr
;
1973 *ptr
= (*ptr
)->next
;
1974 if (remove
->q
.LongLived
)
1976 // give goodbyes for known answers.
1977 // note that since events are sent to client via udsserver_idle(), we don't need to worry about the question being cancelled mid-loop
1978 CacheRecord
*ka
= remove
->q
.uDNS_info
.knownAnswers
;
1979 while (ka
) { remove
->q
.QuestionCallback(gmDNS
, &remove
->q
, &ka
->resrec
, mDNSfalse
); ka
= ka
->next
; }
1981 mDNS_StopBrowse(gmDNS
, &remove
->q
);
1982 freeL("browser_t", remove
);
1985 ptr
= &(*ptr
)->next
;
1987 LogMsg("Requested removal of default domain %##s not in list for sd %d", d
->c
, r
->sd
);
1992 // Count how many other service records we have locally with the same name, but different rdata.
1993 // For auto-named services, we can have at most one per machine -- if we allowed two auto-named services of
1994 // the same type on the same machine, we'd get into an infinite autoimmune-response loop of continuous renaming.
1995 mDNSexport
int CountPeerRegistrations(mDNS
*const m
, ServiceRecordSet
*const srs
)
1998 ResourceRecord
*r
= &srs
->RR_SRV
.resrec
;
2000 ServiceRecordSet
*s
;
2002 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
2003 if (rr
->resrec
.rrtype
== kDNSType_SRV
&& SameDomainName(rr
->resrec
.name
, r
->name
) && !SameRData(&rr
->resrec
, r
))
2006 for (rr
= m
->uDNS_info
.RecordRegistrations
; rr
; rr
=rr
->next
)
2007 if (rr
->uDNS_info
.state
!= regState_Unregistered
&& rr
->resrec
.rrtype
== kDNSType_SRV
&& SameDomainName(rr
->resrec
.name
, r
->name
) && !SameRData(&rr
->resrec
, r
))
2010 for (s
= m
->uDNS_info
.ServiceRegistrations
; s
; s
= s
->next
)
2011 if (s
->uDNS_info
.state
!= regState_Unregistered
&& SameDomainName(s
->RR_SRV
.resrec
.name
, r
->name
) && !SameRData(&s
->RR_SRV
.resrec
, r
))
2014 verbosedebugf("%d peer registrations for %##s", count
, r
->name
->c
);
2018 mDNSexport
int CountExistingRegistrations(domainname
*srv
, mDNSIPPort port
)
2022 for (rr
= gmDNS
->ResourceRecords
; rr
; rr
=rr
->next
)
2023 if (rr
->resrec
.rrtype
== kDNSType_SRV
&&
2024 rr
->resrec
.rdata
->u
.srv
.port
.NotAnInteger
== port
.NotAnInteger
&&
2025 SameDomainName(rr
->resrec
.name
, srv
))
2030 static mStatus
register_service_instance(request_state
*request
, const domainname
*domain
)
2032 service_info
*info
= request
->service_registration
;
2033 service_instance
*ptr
, *instance
;
2037 for (ptr
= info
->instances
; ptr
; ptr
= ptr
->next
)
2039 if (SameDomainName(&ptr
->domain
, domain
))
2040 { LogMsg("register_service_instance: domain %##s already registered", domain
->c
); return mStatus_AlreadyRegistered
; }
2043 instance_size
= sizeof(*instance
);
2044 if (info
->txtlen
> sizeof(RDataBody
)) instance_size
+= (info
->txtlen
- sizeof(RDataBody
));
2045 instance
= mallocL("service_instance", instance_size
);
2046 if (!instance
) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr
; }
2048 instance
->subtypes
= AllocateSubTypes(info
->num_subtypes
, info
->type_as_string
);
2049 if (info
->num_subtypes
&& !instance
->subtypes
)
2050 { free_service_instance(instance
); instance
= NULL
; FatalError("ERROR: malloc"); }
2051 instance
->request
= request
;
2052 instance
->sd
= request
->sd
;
2053 instance
->autoname
= info
->autoname
;
2054 instance
->autorename
= info
->autorename
;
2055 instance
->allowremotequery
= info
->allowremotequery
;
2056 instance
->rename_on_memfree
= 0;
2057 instance
->name
= info
->name
;
2058 AssignDomainName(&instance
->domain
, domain
);
2059 instance
->default_local
= (info
->default_domain
&& SameDomainName(domain
, &localdomain
));
2060 result
= mDNS_RegisterService(gmDNS
, &instance
->srs
, &instance
->name
, &info
->type
, domain
, info
->host
.c
[0] ? &info
->host
: NULL
, info
->port
,
2061 info
->txtdata
, info
->txtlen
, instance
->subtypes
, info
->num_subtypes
, info
->InterfaceID
, regservice_callback
, instance
);
2063 if (result
) free_service_instance(instance
);
2066 instance
->next
= info
->instances
;
2067 info
->instances
= instance
;
2072 mDNSexport
void udsserver_default_reg_domain_changed(const domainname
*d
, mDNSBool add
)
2074 request_state
*rstate
;
2077 for (rstate
= all_requests
; rstate
; rstate
= rstate
->next
)
2079 if (rstate
->terminate
!= regservice_termination_callback
) continue;
2080 info
= rstate
->service_registration
;
2081 if (!info
) { LogMsg("udsserver_default_reg_domain_changed - NULL service info"); continue; } // this should never happen
2082 if (!info
->default_domain
) continue;
2084 // valid default registration
2085 if (add
) register_service_instance(rstate
, d
);
2088 // find the instance to remove
2089 service_instance
*si
= rstate
->service_registration
->instances
, *prev
= NULL
;
2092 if (SameDomainName(&si
->domain
, d
))
2095 if (prev
) prev
->next
= si
->next
;
2096 else info
->instances
= si
->next
;
2097 err
= mDNS_DeregisterService(gmDNS
, &si
->srs
);
2100 LogMsg("udsserver_default_reg_domain_changed - mDNS_DeregisterService err %d", err
);
2101 free_service_instance(si
);
2108 if (!si
) LogMsg("udsserver_default_reg_domain_changed - domain %##s not registered", d
->c
);
2113 // service registration
2114 static void handle_regservice_request(request_state
*request
)
2116 DNSServiceFlags flags
;
2118 char name
[256], domain
[MAX_ESCAPED_DOMAIN_NAME
], host
[MAX_ESCAPED_DOMAIN_NAME
];
2122 service_info
*service
= NULL
;
2124 if (request
->ts
!= t_complete
)
2126 LogMsg("ERROR: handle_regservice_request - transfer state != t_complete");
2127 abort_request(request
);
2128 unlink_request(request
);
2132 service
= mallocL("service_info", sizeof(*service
));
2133 if (!service
) { my_perror("ERROR: malloc"); result
= mStatus_NoMemoryErr
; goto finish
; }
2135 service
->instances
= NULL
;
2136 service
->request
= request
;
2137 request
->service_registration
= service
;
2138 request
->termination_context
= request
->service_registration
;
2139 request
->terminate
= regservice_termination_callback
;
2141 // extract data from message
2142 ptr
= request
->msgdata
;
2143 flags
= get_flags(&ptr
);
2144 ifi
= get_long(&ptr
);
2145 service
->InterfaceID
= mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS
, ifi
);
2146 if (ifi
&& !service
->InterfaceID
)
2147 { LogMsg("ERROR: handle_regservice_request - Couldn't find InterfaceID for interfaceIndex %d", ifi
); goto bad_param
; }
2148 if (get_string(&ptr
, name
, 256) < 0 ||
2149 get_string(&ptr
, service
->type_as_string
, MAX_ESCAPED_DOMAIN_NAME
) < 0 ||
2150 get_string(&ptr
, domain
, MAX_ESCAPED_DOMAIN_NAME
) < 0 ||
2151 get_string(&ptr
, host
, MAX_ESCAPED_DOMAIN_NAME
) < 0)
2152 { LogMsg("ERROR: handle_regservice_request - Couldn't read name/regtype/domain"); goto bad_param
; }
2154 service
->port
.b
[0] = *ptr
++;
2155 service
->port
.b
[1] = *ptr
++;
2157 service
->txtlen
= get_short(&ptr
);
2158 service
->txtdata
= get_rdata(&ptr
, service
->txtlen
);
2160 // Check for sub-types after the service type
2161 service
->num_subtypes
= ChopSubTypes(service
->type_as_string
); // Note: Modifies regtype string to remove trailing subtypes
2162 if (service
->num_subtypes
< 0)
2163 { LogMsg("ERROR: handle_regservice_request - ChopSubTypes failed %s", service
->type_as_string
); goto bad_param
; }
2165 // Don't try to construct "domainname t" until *after* ChopSubTypes has worked its magic
2166 if (!*service
->type_as_string
|| !MakeDomainNameFromDNSNameString(&service
->type
, service
->type_as_string
))
2167 { LogMsg("ERROR: handle_regservice_request - service->type_as_string bad %s", service
->type_as_string
); goto bad_param
; }
2171 service
->name
= (gmDNS
)->nicelabel
;
2172 service
->autoname
= mDNStrue
;
2176 if (!MakeDomainLabelFromLiteralString(&service
->name
, name
))
2177 { LogMsg("ERROR: handle_regservice_request - name bad %s", name
); goto bad_param
; }
2178 service
->autoname
= mDNSfalse
;
2183 service
->default_domain
= mDNSfalse
;
2184 if (!MakeDomainNameFromDNSNameString(&d
, domain
))
2185 { LogMsg("ERROR: handle_regservice_request - domain bad %s", domain
); goto bad_param
; }
2189 service
->default_domain
= mDNStrue
;
2190 MakeDomainNameFromDNSNameString(&d
, "local.");
2193 if (!ConstructServiceName(&srv
, &service
->name
, &service
->type
, &d
))
2194 { LogMsg("ERROR: handle_regservice_request - Couldn't ConstructServiceName from, “%#s” “%##s” “%##s”", service
->name
.c
, service
->type
.c
, d
.c
); goto bad_param
; }
2196 if (!MakeDomainNameFromDNSNameString(&service
->host
, host
))
2197 { LogMsg("ERROR: handle_regservice_request - host bad %s", host
); goto bad_param
; }
2198 service
->autorename
= (flags
& kDNSServiceFlagsNoAutoRename
) == 0;
2199 service
->allowremotequery
= (flags
& kDNSServiceFlagsAllowRemoteQuery
) != 0;
2201 // Some clients use mDNS for lightweight copy protection, registering a pseudo-service with
2202 // a port number of zero. When two instances of the protected client are allowed to run on one
2203 // machine, we don't want to see misleading "Bogus client" messages in syslog and the console.
2204 if (service
->port
.NotAnInteger
)
2206 int count
= CountExistingRegistrations(&srv
, service
->port
);
2208 LogMsg("Client application registered %d identical instances of service %##s port %u.",
2209 count
+1, srv
.c
, mDNSVal16(service
->port
));
2212 LogOperation("%3d: DNSServiceRegister(%##s, %u) START", request
->sd
, srv
.c
, mDNSVal16(service
->port
));
2213 result
= register_service_instance(request
, &d
);
2215 if (!result
&& !*domain
)
2217 DNameListElem
*ptr
, *def_domains
= mDNSPlatformGetRegDomainList();
2218 for (ptr
= def_domains
; ptr
; ptr
= ptr
->next
)
2219 register_service_instance(request
, &ptr
->name
);
2220 // note that we don't report errors for non-local, non-explicit domains
2221 mDNS_FreeDNameList(def_domains
);
2225 deliver_error(request
, result
);
2226 if (result
!= mStatus_NoError
)
2228 abort_request(request
);
2229 unlink_request(request
);
2232 reset_connected_rstate(request
); // prepare to receive add/remove messages
2237 //if (service) freeL("service_info", service); Don't think we should do this -- abort_request will free it a second time and crash
2238 deliver_error(request
, mStatus_BadParamErr
);
2239 abort_request(request
);
2240 unlink_request(request
);
2243 // service registration callback performs three duties - frees memory for deregistered services,
2244 // handles name conflicts, and delivers completed registration information to the client (via
2245 // process_service_registraion())
2247 static void regservice_callback(mDNS
*const m
, ServiceRecordSet
*const srs
, mStatus result
)
2250 service_instance
*instance
= srs
->ServiceContext
;
2252 if (!srs
) { LogMsg("regservice_callback: srs is NULL %d", result
); return; }
2253 if (!instance
) { LogMsg("regservice_callback: srs->ServiceContext is NULL %d", result
); return; }
2255 if (result
== mStatus_NoError
)
2256 LogOperation("%3d: DNSServiceRegister(%##s, %u) REGISTERED ", instance
->sd
, srs
->RR_SRV
.resrec
.name
->c
, mDNSVal16(srs
->RR_SRV
.resrec
.rdata
->u
.srv
.port
));
2257 else if (result
== mStatus_MemFree
)
2258 LogOperation("%3d: DNSServiceRegister(%##s, %u) DEREGISTERED", instance
->sd
, srs
->RR_SRV
.resrec
.name
->c
, mDNSVal16(srs
->RR_SRV
.resrec
.rdata
->u
.srv
.port
));
2259 else if (result
== mStatus_NameConflict
)
2260 LogOperation("%3d: DNSServiceRegister(%##s, %u) NAME CONFLICT", instance
->sd
, srs
->RR_SRV
.resrec
.name
->c
, mDNSVal16(srs
->RR_SRV
.resrec
.rdata
->u
.srv
.port
));
2262 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
);
2264 if (result
== mStatus_NoError
)
2266 if (instance
->allowremotequery
)
2268 ExtraResourceRecord
*e
;
2269 srs
->RR_ADV
.AllowRemoteQuery
= mDNStrue
;
2270 srs
->RR_PTR
.AllowRemoteQuery
= mDNStrue
;
2271 srs
->RR_SRV
.AllowRemoteQuery
= mDNStrue
;
2272 srs
->RR_TXT
.AllowRemoteQuery
= mDNStrue
;
2273 for (e
= instance
->srs
.Extras
; e
; e
= e
->next
) e
->r
.AllowRemoteQuery
= mDNStrue
;
2275 process_service_registration(srs
);
2276 if (instance
->autoname
&& CountPeerRegistrations(m
, srs
) == 0)
2277 RecordUpdatedNiceLabel(m
, 0); // Successfully got new name, tell user immediately
2280 else if (result
== mStatus_MemFree
)
2282 if (instance
->rename_on_memfree
)
2284 instance
->rename_on_memfree
= 0;
2285 instance
->name
= gmDNS
->nicelabel
;
2286 err
= mDNS_RenameAndReregisterService(gmDNS
, srs
, &instance
->name
);
2287 if (err
) LogMsg("ERROR: regservice_callback - RenameAndReregisterService returned %ld", err
);
2288 // error should never happen - safest to log and continue
2292 free_service_instance(instance
);
2296 else if (result
== mStatus_NameConflict
)
2298 if (instance
->autoname
&& CountPeerRegistrations(m
, srs
) == 0)
2300 // On conflict for an autoname service, rename and reregister *all* autoname services
2301 IncrementLabelSuffix(&m
->nicelabel
, mDNStrue
);
2302 m
->MainCallback(m
, mStatus_ConfigChanged
);
2304 else if (instance
->autoname
|| instance
->autorename
)
2306 mDNS_RenameAndReregisterService(gmDNS
, srs
, mDNSNULL
);
2311 request_state
*rs
= instance
->request
;
2312 if (!rs
) { LogMsg("ERROR: regservice_callback: received result %ld with a NULL request pointer", result
); return; }
2313 free_service_instance(instance
);
2314 if (deliver_async_error(rs
, reg_service_reply
, result
) < 0)
2324 request_state
*rs
= instance
->request
;
2325 if (!rs
) { LogMsg("ERROR: regservice_callback: received result %ld with a NULL request pointer", result
); return; }
2326 if (result
!= mStatus_NATTraversal
) LogMsg("ERROR: unknown result in regservice_callback: %ld", result
);
2327 free_service_instance(instance
);
2328 if (deliver_async_error(rs
, reg_service_reply
, result
) < 0)
2337 mDNSexport
void FreeExtraRR(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
2339 ExtraResourceRecord
*extra
= (ExtraResourceRecord
*)rr
->RecordContext
;
2342 if (result
!= mStatus_MemFree
) { LogMsg("Error: FreeExtraRR invoked with unexpected error %d", result
); return; }
2344 debugf("%##s: MemFree", rr
->resrec
.name
->c
);
2345 if (rr
->resrec
.rdata
!= &rr
->rdatastorage
)
2346 freeL("Extra RData", rr
->resrec
.rdata
);
2347 freeL("ExtraResourceRecord", extra
);
2350 static mStatus
add_record_to_service(request_state
*rstate
, service_instance
*instance
, uint16_t rrtype
, uint16_t rdlen
, char *rdata
, uint32_t ttl
)
2352 ServiceRecordSet
*srs
= &instance
->srs
;
2353 ExtraResourceRecord
*extra
;
2357 if (rdlen
> sizeof(RDataBody
)) size
= rdlen
;
2358 else size
= sizeof(RDataBody
);
2360 extra
= mallocL("ExtraResourceRecord", sizeof(*extra
) - sizeof(RDataBody
) + size
);
2363 my_perror("ERROR: malloc");
2364 return mStatus_NoMemoryErr
;
2367 bzero(extra
, sizeof(ExtraResourceRecord
)); // OK if oversized rdata not zero'd
2368 extra
->r
.resrec
.rrtype
= rrtype
;
2369 extra
->r
.rdatastorage
.MaxRDLength
= (mDNSu16
) size
;
2370 extra
->r
.resrec
.rdlength
= rdlen
;
2371 memcpy(&extra
->r
.rdatastorage
.u
.data
, rdata
, rdlen
);
2373 result
= mDNS_AddRecordToService(gmDNS
, srs
, extra
, &extra
->r
.rdatastorage
, ttl
);
2374 if (result
) { freeL("ExtraResourceRecord", extra
); return result
; }
2376 extra
->ClientID
= rstate
->hdr
.reg_index
;
2380 static mStatus
handle_add_request(request_state
*rstate
)
2383 uint16_t rrtype
, rdlen
;
2385 mStatus result
= mStatus_UnknownErr
;
2386 DNSServiceFlags flags
;
2387 service_info
*srvinfo
= rstate
->service_registration
;
2388 service_instance
*i
;
2390 if (!srvinfo
) { LogMsg("handle_add_request called with NULL service_registration"); return(-1); }
2392 ptr
= rstate
->msgdata
;
2393 flags
= get_flags(&ptr
);
2394 rrtype
= get_short(&ptr
);
2395 rdlen
= get_short(&ptr
);
2396 rdata
= get_rdata(&ptr
, rdlen
);
2397 ttl
= get_long(&ptr
);
2399 if (!ttl
) ttl
= DefaultTTLforRRType(rrtype
);
2401 LogOperation("%3d: DNSServiceAddRecord(%##s, %s)", rstate
->sd
,
2402 (srvinfo
->instances
) ? srvinfo
->instances
->srs
.RR_SRV
.resrec
.name
->c
: NULL
, DNSTypeName(rrtype
));
2404 for (i
= srvinfo
->instances
; i
; i
= i
->next
)
2406 result
= add_record_to_service(rstate
, i
, rrtype
, rdlen
, rdata
, ttl
);
2407 if (result
&& i
->default_local
) break;
2408 else result
= mStatus_NoError
; // suppress non-local default errors
2414 static mStatus
update_record(AuthRecord
*rr
, uint16_t rdlen
, char *rdata
, uint32_t ttl
)
2420 if (rdlen
> sizeof(RDataBody
)) rdsize
= rdlen
;
2421 else rdsize
= sizeof(RDataBody
);
2422 newrd
= mallocL("handle_update_request", sizeof(RData
) - sizeof(RDataBody
) + rdsize
);
2423 if (!newrd
) FatalError("ERROR: malloc");
2424 newrd
->MaxRDLength
= (mDNSu16
) rdsize
;
2425 memcpy(&newrd
->u
, rdata
, rdlen
);
2426 result
= mDNS_Update(gmDNS
, rr
, ttl
, rdlen
, newrd
, update_callback
);
2427 if (result
) { LogMsg("ERROR: mDNS_Update - %ld", result
); freeL("handle_update_request", newrd
); }
2431 static mStatus
handle_update_request(request_state
*rstate
)
2436 mStatus result
= mStatus_BadReferenceErr
;
2437 service_info
*srvinfo
= rstate
->service_registration
;
2438 service_instance
*i
;
2439 AuthRecord
*rr
= NULL
;
2441 // get the message data
2442 ptr
= rstate
->msgdata
;
2443 get_flags(&ptr
); // flags unused
2444 rdlen
= get_short(&ptr
);
2445 rdata
= get_rdata(&ptr
, rdlen
);
2446 ttl
= get_long(&ptr
);
2448 if (rstate
->reg_recs
)
2450 // update an individually registered record
2451 registered_record_entry
*reptr
;
2452 for (reptr
= rstate
->reg_recs
; reptr
; reptr
= reptr
->next
)
2454 if (reptr
->key
== rstate
->hdr
.reg_index
)
2456 result
= update_record(reptr
->rr
, rdlen
, rdata
, ttl
);
2460 result
= mStatus_BadReferenceErr
;
2464 // update a record from a service record set
2465 if (!srvinfo
) { result
= mStatus_BadReferenceErr
; goto end
; }
2466 for (i
= srvinfo
->instances
; i
; i
= i
->next
)
2468 if (rstate
->hdr
.reg_index
== TXT_RECORD_INDEX
) rr
= &i
->srs
.RR_TXT
;
2471 ExtraResourceRecord
*e
;
2472 for (e
= i
->srs
.Extras
; e
; e
= e
->next
)
2473 if (e
->ClientID
== rstate
->hdr
.reg_index
) { rr
= &e
->r
; break; }
2476 if (!rr
) { result
= mStatus_BadReferenceErr
; goto end
; }
2477 result
= update_record(rr
, rdlen
, rdata
, ttl
);
2478 if (result
&& i
->default_local
) goto end
;
2479 else result
= mStatus_NoError
; // suppress non-local default errors
2483 LogOperation("%3d: DNSServiceUpdateRecord(%##s, %s)", rstate
->sd
,
2484 (srvinfo
->instances
) ? srvinfo
->instances
->srs
.RR_SRV
.resrec
.name
->c
: NULL
,
2485 rr
? DNSTypeName(rr
->resrec
.rrtype
) : "<NONE>");
2490 static void update_callback(mDNS
*const m
, AuthRecord
*const rr
, RData
*oldrd
)
2493 if (oldrd
!= &rr
->rdatastorage
) freeL("update_callback", oldrd
);
2496 static void process_service_registration(ServiceRecordSet
*const srs
)
2499 transfer_state send_result
;
2501 service_instance
*instance
= srs
->ServiceContext
;
2502 request_state
*req
= instance
->request
;
2504 if (!req
) { LogMsg("ERROR: process_service_registration - null request object"); return; }
2505 err
= gen_rr_response(srs
->RR_SRV
.resrec
.name
, srs
->RR_SRV
.resrec
.InterfaceID
, req
, &rep
);
2508 if (deliver_async_error(req
, reg_service_reply
, err
) < 0)
2511 unlink_request(req
);
2515 send_result
= send_msg(rep
);
2516 if (send_result
== t_error
|| send_result
== t_terminated
)
2519 unlink_request(req
);
2520 freeL("process_service_registration", rep
);
2522 else if (send_result
== t_complete
) freeL("process_service_registration", rep
);
2523 else append_reply(req
, rep
);
2526 static void free_service_instance(service_instance
*srv
)
2528 request_state
*rstate
= srv
->request
;
2529 ExtraResourceRecord
*e
= srv
->srs
.Extras
, *tmp
;
2531 // clear pointers from parent struct
2534 service_instance
*ptr
= rstate
->service_registration
->instances
, *prev
= NULL
;
2539 if (prev
) prev
->next
= ptr
->next
;
2540 else rstate
->service_registration
->instances
= ptr
->next
;
2550 e
->r
.RecordContext
= e
;
2553 FreeExtraRR(gmDNS
, &tmp
->r
, mStatus_MemFree
);
2556 if (srv
->subtypes
) { freeL("regservice_callback", srv
->subtypes
); srv
->subtypes
= NULL
; }
2557 freeL("regservice_callback", srv
);
2560 static void regservice_termination_callback(void *context
)
2562 service_info
*info
= context
;
2563 service_instance
*i
, *p
;
2564 if (!info
) { LogMsg("regservice_termination_callback context is NULL"); return; }
2565 if (!info
->request
) { LogMsg("regservice_termination_callback info->request is NULL"); return; }
2566 i
= info
->instances
;
2571 p
->request
= NULL
; // clear back pointer
2572 // only safe to free memory if registration is not valid, i.e. deregister fails (which invalidates p)
2573 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
));
2574 if (mDNS_DeregisterService(gmDNS
, &p
->srs
)) free_service_instance(p
);
2576 info
->request
->service_registration
= NULL
; // clear pointer from request back to info
2577 freeL("service_info", info
);
2580 static mStatus
handle_regrecord_request(request_state
*rstate
)
2583 registered_record_entry
*re
;
2586 if (rstate
->ts
!= t_complete
)
2588 LogMsg("ERROR: handle_regrecord_request - transfer state != t_complete");
2589 abort_request(rstate
);
2590 unlink_request(rstate
);
2594 rr
= read_rr_from_ipc_msg(rstate
->msgdata
, 1, 1);
2595 if (!rr
) return(mStatus_BadParamErr
);
2597 // allocate registration entry, link into list
2598 re
= mallocL("handle_regrecord_request", sizeof(registered_record_entry
));
2599 if (!re
) FatalError("ERROR: malloc");
2600 re
->key
= rstate
->hdr
.reg_index
;
2602 re
->rstate
= rstate
;
2603 re
->client_context
= rstate
->hdr
.client_context
;
2604 rr
->RecordContext
= re
;
2605 rr
->RecordCallback
= regrecord_callback
;
2606 re
->next
= rstate
->reg_recs
;
2607 rstate
->reg_recs
= re
;
2609 if (!rstate
->terminate
)
2611 rstate
->terminate
= connected_registration_termination
;
2612 rstate
->termination_context
= rstate
;
2615 if (rr
->resrec
.rroriginalttl
== 0)
2616 rr
->resrec
.rroriginalttl
= DefaultTTLforRRType(rr
->resrec
.rrtype
);
2618 LogOperation("%3d: DNSServiceRegisterRecord %s", rstate
->sd
, RRDisplayString(gmDNS
, &rr
->resrec
));
2619 result
= mDNS_Register(gmDNS
, rr
);
2623 static void regrecord_callback(mDNS
*const m
, AuthRecord
* rr
, mStatus result
)
2625 registered_record_entry
*re
= rr
->RecordContext
;
2626 request_state
*rstate
= re
? re
->rstate
: NULL
;
2634 // parent struct alreadt freed by termination callback
2635 if (!result
) LogMsg("Error: regrecord_callback: successful registration of orphaned record");
2638 if (result
!= mStatus_MemFree
) LogMsg("regrecord_callback: error %d received after parent termination", result
);
2639 freeL("regrecord_callback", rr
);
2644 // format result, add to the list for the request, including the client context in the header
2645 len
= sizeof(DNSServiceFlags
);
2646 len
+= sizeof(uint32_t); //interfaceIndex
2647 len
+= sizeof(DNSServiceErrorType
);
2649 reply
= create_reply(reg_record_reply
, len
, rstate
);
2650 reply
->mhdr
->client_context
= re
->client_context
;
2651 reply
->rhdr
->flags
= dnssd_htonl(0);
2652 reply
->rhdr
->ifi
= dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS
, rr
->resrec
.InterfaceID
));
2653 reply
->rhdr
->error
= dnssd_htonl(result
);
2657 // unlink from list, free memory
2658 registered_record_entry
**ptr
= &re
->rstate
->reg_recs
;
2659 while (*ptr
&& (*ptr
) != re
) ptr
= &(*ptr
)->next
;
2660 if (!*ptr
) { LogMsg("regrecord_callback - record not in list!"); return; }
2661 *ptr
= (*ptr
)->next
;
2662 freeL("regrecord_callback", re
->rr
);
2664 freeL("regrecord_callback", re
);
2668 ts
= send_msg(reply
);
2670 if (ts
== t_error
|| ts
== t_terminated
) { abort_request(rstate
); unlink_request(rstate
); }
2671 else if (ts
== t_complete
) freeL("regrecord_callback", reply
);
2672 else if (ts
== t_morecoming
) append_reply(rstate
, reply
); // client is blocked, link reply into list
2675 static void connected_registration_termination(void *context
)
2678 registered_record_entry
*fptr
, *ptr
= ((request_state
*)context
)->reg_recs
;
2683 shared
= fptr
->rr
->resrec
.RecordType
== kDNSRecordTypeShared
;
2684 fptr
->rr
->RecordContext
= NULL
;
2685 mDNS_Deregister(gmDNS
, fptr
->rr
);
2686 freeL("connected_registration_termination", fptr
);
2690 static mStatus
handle_removerecord_request(request_state
*rstate
)
2692 mStatus err
= mStatus_BadReferenceErr
;
2694 service_info
*srvinfo
= rstate
->service_registration
;
2696 ptr
= rstate
->msgdata
;
2697 get_flags(&ptr
); // flags unused
2699 if (rstate
->reg_recs
) err
= remove_record(rstate
); // remove individually registered record
2700 else if (!srvinfo
) LogOperation("%3d: DNSServiceRemoveRecord (bad ref)", rstate
->sd
);
2703 service_instance
*i
;
2704 LogOperation("%3d: DNSServiceRemoveRecord(%##s)", rstate
->sd
,
2705 (srvinfo
->instances
) ? srvinfo
->instances
->srs
.RR_SRV
.resrec
.name
->c
: NULL
);
2706 for (i
= srvinfo
->instances
; i
; i
= i
->next
)
2708 err
= remove_extra(rstate
, i
);
2709 if (err
&& i
->default_local
) break;
2710 else err
= mStatus_NoError
; // suppress non-local default errors
2717 // remove a resource record registered via DNSServiceRegisterRecord()
2718 static mStatus
remove_record(request_state
*rstate
)
2721 mStatus err
= mStatus_UnknownErr
;
2722 registered_record_entry
*e
, **ptr
= &rstate
->reg_recs
;
2724 while(*ptr
&& (*ptr
)->key
!= rstate
->hdr
.reg_index
) ptr
= &(*ptr
)->next
;
2725 if (!*ptr
) { LogMsg("DNSServiceRemoveRecord - bad reference"); return mStatus_BadReferenceErr
; }
2727 *ptr
= e
->next
; // unlink
2729 LogOperation("%3d: DNSServiceRemoveRecord(%#s)", rstate
->sd
, e
->rr
->resrec
.name
->c
);
2730 shared
= e
->rr
->resrec
.RecordType
== kDNSRecordTypeShared
;
2731 e
->rr
->RecordContext
= NULL
;
2732 err
= mDNS_Deregister(gmDNS
, e
->rr
);
2735 LogMsg("ERROR: remove_record, mDNS_Deregister: %ld", err
);
2736 freeL("remove_record", e
->rr
);
2737 freeL("remove_record", e
);
2742 static mStatus
remove_extra(request_state
*rstate
, service_instance
*serv
)
2744 mStatus err
= mStatus_BadReferenceErr
;
2745 ExtraResourceRecord
*ptr
;
2747 for (ptr
= serv
->srs
.Extras
; ptr
; ptr
= ptr
->next
)
2749 if (ptr
->ClientID
== rstate
->hdr
.reg_index
) // found match
2750 return mDNS_RemoveRecordFromService(gmDNS
, &serv
->srs
, ptr
, FreeExtraRR
, ptr
);
2755 // domain enumeration
2756 static void handle_enum_request(request_state
*rstate
)
2758 DNSServiceFlags flags
;
2760 mDNSInterfaceID InterfaceID
;
2761 char *ptr
= rstate
->msgdata
;
2762 domain_enum_t
*def
, *all
;
2763 enum_termination_t
*term
;
2767 if (rstate
->ts
!= t_complete
)
2769 LogMsg("ERROR: handle_enum_request - transfer state != t_complete");
2770 abort_request(rstate
);
2771 unlink_request(rstate
);
2775 flags
= get_flags(&ptr
);
2776 ifi
= get_long(&ptr
);
2777 InterfaceID
= mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS
, ifi
);
2778 if (ifi
&& !InterfaceID
)
2780 deliver_error(rstate
, mStatus_BadParamErr
);
2781 abort_request(rstate
);
2782 unlink_request(rstate
);
2785 // allocate context structures
2786 def
= mallocL("handle_enum_request", sizeof(domain_enum_t
));
2787 all
= mallocL("handle_enum_request", sizeof(domain_enum_t
));
2788 term
= mallocL("handle_enum_request", sizeof(enum_termination_t
));
2789 if (!def
|| !all
|| !term
) FatalError("ERROR: malloc");
2791 // enumeration requires multiple questions, so we must link all the context pointers so that
2792 // necessary context can be reached from the callbacks
2793 def
->rstate
= rstate
;
2794 all
->rstate
= rstate
;
2797 term
->rstate
= rstate
;
2798 rstate
->termination_context
= term
;
2799 rstate
->terminate
= enum_termination_callback
;
2800 def
->question
.QuestionContext
= def
;
2801 def
->type
= (flags
& kDNSServiceFlagsRegistrationDomains
) ?
2802 mDNS_DomainTypeRegistrationDefault
: mDNS_DomainTypeBrowseDefault
;
2803 all
->question
.QuestionContext
= all
;
2804 all
->type
= (flags
& kDNSServiceFlagsRegistrationDomains
) ?
2805 mDNS_DomainTypeRegistration
: mDNS_DomainTypeBrowse
;
2807 // if the caller hasn't specified an explicit interface, we use local-only to get the system-wide list.
2808 if (!InterfaceID
) InterfaceID
= mDNSInterface_LocalOnly
;
2811 LogOperation("%3d: DNSServiceEnumerateDomains(%X=%s)", rstate
->sd
, flags
,
2812 (flags
& kDNSServiceFlagsBrowseDomains
) ? "kDNSServiceFlagsBrowseDomains" :
2813 (flags
& kDNSServiceFlagsRegistrationDomains
) ? "kDNSServiceFlagsRegistrationDomains" : "<<Unknown>>");
2814 err
= mDNS_GetDomains(gmDNS
, &all
->question
, all
->type
, NULL
, InterfaceID
, enum_result_callback
, all
);
2815 if (err
== mStatus_NoError
)
2816 err
= mDNS_GetDomains(gmDNS
, &def
->question
, def
->type
, NULL
, InterfaceID
, enum_result_callback
, def
);
2817 result
= deliver_error(rstate
, err
); // send error *before* returning local domain
2819 if (result
< 0 || err
)
2821 abort_request(rstate
);
2822 unlink_request(rstate
);
2827 static void enum_result_callback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
2829 char domain
[MAX_ESCAPED_DOMAIN_NAME
];
2830 domain_enum_t
*de
= question
->QuestionContext
;
2831 DNSServiceFlags flags
= 0;
2835 if (answer
->rrtype
!= kDNSType_PTR
) return;
2838 flags
|= kDNSServiceFlagsAdd
;
2839 if (de
->type
== mDNS_DomainTypeRegistrationDefault
|| de
->type
== mDNS_DomainTypeBrowseDefault
)
2840 flags
|= kDNSServiceFlagsDefault
;
2842 ConvertDomainNameToCString(&answer
->rdata
->u
.name
, domain
);
2843 // note that we do NOT propagate specific interface indexes to the client - for example, a domain we learn from
2844 // a machine's system preferences may be discovered on the LocalOnly interface, but should be browsed on the
2845 // network, so we just pass kDNSServiceInterfaceIndexAny
2846 reply
= format_enumeration_reply(de
->rstate
, domain
, flags
, kDNSServiceInterfaceIndexAny
, kDNSServiceErr_NoError
);
2849 LogMsg("ERROR: enum_result_callback, format_enumeration_reply");
2853 append_reply(de
->rstate
, reply
);
2857 static reply_state
*format_enumeration_reply(request_state
*rstate
, const char *domain
, DNSServiceFlags flags
, uint32_t ifi
, DNSServiceErrorType err
)
2864 len
= sizeof(DNSServiceFlags
);
2865 len
+= sizeof(uint32_t);
2866 len
+= sizeof(DNSServiceErrorType
);
2867 len
+= strlen(domain
) + 1;
2869 reply
= create_reply(enumeration_reply
, len
, rstate
);
2870 reply
->rhdr
->flags
= dnssd_htonl(flags
);
2871 reply
->rhdr
->ifi
= dnssd_htonl(ifi
);
2872 reply
->rhdr
->error
= dnssd_htonl(err
);
2873 data
= reply
->sdata
;
2874 put_string(domain
, &data
);
2878 static void enum_termination_callback(void *context
)
2880 enum_termination_t
*t
= context
;
2881 mDNS
*coredata
= gmDNS
;
2883 mDNS_StopGetDomains(coredata
, &t
->all
->question
);
2884 mDNS_StopGetDomains(coredata
, &t
->def
->question
);
2885 freeL("enum_termination_callback", t
->all
);
2886 freeL("enum_termination_callback", t
->def
);
2887 t
->rstate
->termination_context
= NULL
;
2888 freeL("enum_termination_callback", t
);
2891 static void handle_reconfirm_request(request_state
*rstate
)
2895 rr
= read_rr_from_ipc_msg(rstate
->msgdata
, 0, 1);
2897 LogOperation("%3d: DNSServiceReconfirmRecord(%##s) %s", rstate
->sd
, RRDisplayString(gmDNS
, &rr
->resrec
));
2898 mDNS_ReconfirmByValue(gmDNS
, &rr
->resrec
);
2899 abort_request(rstate
);
2900 unlink_request(rstate
);
2901 freeL("handle_reconfirm_request", rr
);
2904 // setup rstate to accept new reg/dereg requests
2905 static void reset_connected_rstate(request_state
*rstate
)
2907 rstate
->ts
= t_morecoming
;
2908 rstate
->hdr_bytes
= 0;
2909 rstate
->data_bytes
= 0;
2910 if (rstate
->msgbuf
) freeL("reset_connected_rstate", rstate
->msgbuf
);
2911 rstate
->msgbuf
= NULL
;
2912 rstate
->bufsize
= 0;
2915 // returns a resource record (allocated w/ malloc) containing the data found in an IPC message
2916 // data must be in format flags, interfaceIndex, name, rrtype, rrclass, rdlen, rdata, (optional)ttl
2917 // (ttl only extracted/set if ttl argument is non-zero). returns NULL for a bad-parameter error
2918 static AuthRecord
*read_rr_from_ipc_msg(char *msgbuf
, int GetTTL
, int validate_flags
)
2920 char *rdata
, name
[256];
2922 DNSServiceFlags flags
;
2923 uint32_t interfaceIndex
;
2924 uint16_t type
, class, rdlen
;
2927 flags
= get_flags(&msgbuf
);
2928 if (validate_flags
&&
2929 !((flags
& kDNSServiceFlagsShared
) == kDNSServiceFlagsShared
) &&
2930 !((flags
& kDNSServiceFlagsUnique
) == kDNSServiceFlagsUnique
))
2932 LogMsg("ERROR: Bad resource record flags (must be kDNSServiceFlagsShared or kDNSServiceFlagsUnique)");
2936 interfaceIndex
= get_long(&msgbuf
);
2937 if (get_string(&msgbuf
, name
, 256) < 0)
2939 LogMsg("ERROR: read_rr_from_ipc_msg - get_string");
2942 type
= get_short(&msgbuf
);
2943 class = get_short(&msgbuf
);
2944 rdlen
= get_short(&msgbuf
);
2946 if (rdlen
> sizeof(RDataBody
)) storage_size
= rdlen
;
2947 else storage_size
= sizeof(RDataBody
);
2949 rr
= mallocL("read_rr_from_ipc_msg", sizeof(AuthRecord
) - sizeof(RDataBody
) + storage_size
);
2950 if (!rr
) FatalError("ERROR: malloc");
2951 bzero(rr
, sizeof(AuthRecord
)); // ok if oversized rdata not zero'd
2953 mDNS_SetupResourceRecord(rr
, mDNSNULL
, mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS
, interfaceIndex
),
2954 type
, 0, (flags
& kDNSServiceFlagsShared
) ? kDNSRecordTypeShared
: kDNSRecordTypeUnique
, mDNSNULL
, mDNSNULL
);
2956 if (!MakeDomainNameFromDNSNameString(rr
->resrec
.name
, name
))
2958 LogMsg("ERROR: bad name: %s", name
);
2959 freeL("read_rr_from_ipc_msg", rr
);
2962 if (flags
& kDNSServiceFlagsAllowRemoteQuery
) rr
->AllowRemoteQuery
= mDNStrue
;
2963 rr
->resrec
.rrclass
= class;
2964 rr
->resrec
.rdlength
= rdlen
;
2965 rr
->resrec
.rdata
->MaxRDLength
= rdlen
;
2966 rdata
= get_rdata(&msgbuf
, rdlen
);
2967 memcpy(rr
->resrec
.rdata
->u
.data
, rdata
, rdlen
);
2970 rr
->resrec
.rroriginalttl
= get_long(&msgbuf
);
2975 // generate a response message for a browse result, service registration result, or any other call with the
2976 // identical callback signature. on successful completion rep is set to point to a malloc'd reply_state struct,
2977 // and mStatus_NoError is returned. otherwise the appropriate error is returned.
2979 static mStatus
gen_rr_response(domainname
*servicename
, mDNSInterfaceID id
, request_state
*request
, reply_state
**rep
)
2984 domainname type
, dom
;
2985 char namestr
[MAX_DOMAIN_LABEL
+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL.
2986 char typestr
[MAX_ESCAPED_DOMAIN_NAME
];
2987 char domstr
[MAX_ESCAPED_DOMAIN_NAME
];
2991 if (!DeconstructServiceName(servicename
, &name
, &type
, &dom
))
2992 return kDNSServiceErr_Unknown
;
2994 ConvertDomainLabelToCString_unescaped(&name
, namestr
);
2995 ConvertDomainNameToCString(&type
, typestr
);
2996 ConvertDomainNameToCString(&dom
, domstr
);
2998 // calculate reply data length
2999 len
= sizeof(DNSServiceFlags
);
3000 len
+= sizeof(uint32_t); // if index
3001 len
+= sizeof(DNSServiceErrorType
);
3002 len
+= (int) (strlen(namestr
) + 1);
3003 len
+= (int) (strlen(typestr
) + 1);
3004 len
+= (int) (strlen(domstr
) + 1);
3006 *rep
= create_reply(query_reply
, len
, request
);
3008 (*rep
)->rhdr
->flags
= dnssd_htonl(0);
3009 (*rep
)->rhdr
->ifi
= dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS
, id
));
3010 (*rep
)->rhdr
->error
= dnssd_htonl(kDNSServiceErr_NoError
);
3012 data
= (*rep
)->sdata
;
3014 put_string(namestr
, &data
);
3015 put_string(typestr
, &data
);
3016 put_string(domstr
, &data
);
3017 return mStatus_NoError
;
3020 static int build_domainname_from_strings(domainname
*srv
, char *name
, char *regtype
, char *domain
)
3025 if (!MakeDomainLabelFromLiteralString(&n
, name
)) return -1;
3026 if (!MakeDomainNameFromDNSNameString(&t
, regtype
)) return -1;
3027 if (!MakeDomainNameFromDNSNameString(&d
, domain
)) return -1;
3028 if (!ConstructServiceName(srv
, &n
, &t
, &d
)) return -1;
3032 // append a reply to the list in a request object
3033 static void append_reply(request_state
*req
, reply_state
*rep
)
3037 if (!req
->replies
) req
->replies
= rep
;
3041 while (ptr
->next
) ptr
= ptr
->next
;
3047 // read_msg may be called any time when the transfer state (rs->ts) is t_morecoming.
3048 // returns the current state of the request (morecoming, error, complete, terminated.)
3049 // if there is no data on the socket, the socket will be closed and t_terminated will be returned
3050 static int read_msg(request_state
*rs
)
3054 char buf
[4]; // dummy for death notification
3056 if (rs
->ts
== t_terminated
|| rs
->ts
== t_error
)
3058 LogMsg("ERROR: read_msg called with transfer state terminated or error");
3063 if (rs
->ts
== t_complete
)
3064 { // this must be death or something is wrong
3065 nread
= recv(rs
->sd
, buf
, 4, 0);
3066 if (!nread
) { rs
->ts
= t_terminated
; return t_terminated
; }
3067 if (nread
< 0) goto rerror
;
3068 LogMsg("ERROR: read data from a completed request.");
3073 if (rs
->ts
!= t_morecoming
)
3075 LogMsg("ERROR: read_msg called with invalid transfer state (%d)", rs
->ts
);
3080 if (rs
->hdr_bytes
< sizeof(ipc_msg_hdr
))
3082 nleft
= sizeof(ipc_msg_hdr
) - rs
->hdr_bytes
;
3083 nread
= recv(rs
->sd
, (char *)&rs
->hdr
+ rs
->hdr_bytes
, nleft
, 0);
3084 if (nread
== 0) { rs
->ts
= t_terminated
; return t_terminated
; }
3085 if (nread
< 0) goto rerror
;
3086 rs
->hdr_bytes
+= nread
;
3087 if (rs
->hdr_bytes
== sizeof(ipc_msg_hdr
))
3089 ConvertHeaderBytes(&rs
->hdr
);
3090 if (rs
->hdr
.version
!= VERSION
)
3092 LogMsg("ERROR: read_msg - client version 0x%08X does not match daemon version 0x%08X", rs
->hdr
.version
, VERSION
);
3097 if (rs
->hdr_bytes
> sizeof(ipc_msg_hdr
))
3099 LogMsg("ERROR: read_msg - read too many header bytes");
3105 // only read data if header is complete
3106 if (rs
->hdr_bytes
== sizeof(ipc_msg_hdr
))
3108 if (rs
->hdr
.datalen
== 0) // ok in removerecord requests
3110 rs
->ts
= t_complete
;
3115 if (!rs
->msgbuf
) // allocate the buffer first time through
3117 rs
->msgbuf
= mallocL("read_msg", rs
->hdr
.datalen
+ MSG_PAD_BYTES
);
3120 my_perror("ERROR: malloc");
3124 rs
->msgdata
= rs
->msgbuf
;
3126 bzero(rs
->msgbuf
, rs
->hdr
.datalen
+ MSG_PAD_BYTES
);
3127 nleft
= rs
->hdr
.datalen
- rs
->data_bytes
;
3128 nread
= recv(rs
->sd
, rs
->msgbuf
+ rs
->data_bytes
, nleft
, 0);
3129 if (nread
== 0) { rs
->ts
= t_terminated
; return t_terminated
; }
3130 if (nread
< 0) goto rerror
;
3131 rs
->data_bytes
+= nread
;
3132 if (rs
->data_bytes
> rs
->hdr
.datalen
)
3134 LogMsg("ERROR: read_msg - read too many data bytes");
3140 if (rs
->hdr_bytes
== sizeof(ipc_msg_hdr
) && rs
->data_bytes
== rs
->hdr
.datalen
)
3141 rs
->ts
= t_complete
;
3142 else rs
->ts
= t_morecoming
;
3147 if (dnssd_errno() == dnssd_EWOULDBLOCK
|| dnssd_errno() == dnssd_EINTR
) return t_morecoming
;
3148 my_perror("ERROR: read_msg");
3153 static int send_msg(reply_state
*rs
)
3159 LogMsg("ERROR: send_msg called with NULL message buffer");
3163 if (rs
->request
->no_reply
) //!!!KRS this behavior should be optimized if it becomes more common
3165 rs
->ts
= t_complete
;
3166 freeL("send_msg", rs
->msgbuf
);
3170 ConvertHeaderBytes(rs
->mhdr
);
3171 nwriten
= send(rs
->sd
, rs
->msgbuf
+ rs
->nwriten
, rs
->len
- rs
->nwriten
, 0);
3172 ConvertHeaderBytes(rs
->mhdr
);
3175 if (dnssd_errno() == dnssd_EINTR
|| dnssd_errno() == dnssd_EWOULDBLOCK
) nwriten
= 0;
3178 #if !defined(PLATFORM_NO_EPIPE)
3179 if (dnssd_errno() == EPIPE
)
3181 LogMsg("%3d: broken pipe - cleanup will be handled by run-loop read wakeup", rs
->sd
);
3182 rs
->ts
= t_terminated
;
3183 rs
->request
->ts
= t_terminated
;
3184 return t_terminated
;
3189 my_perror("ERROR: send\n");
3195 rs
->nwriten
+= nwriten
;
3197 if (rs
->nwriten
== rs
->len
)
3199 rs
->ts
= t_complete
;
3200 freeL("send_msg", rs
->msgbuf
);
3205 static reply_state
*create_reply(reply_op_t op
, size_t datalen
, request_state
*request
)
3211 if ((unsigned)datalen
< sizeof(reply_hdr
))
3213 LogMsg("ERROR: create_reply - data length less than lenght of required fields");
3217 totallen
= (int) (datalen
+ sizeof(ipc_msg_hdr
));
3218 reply
= mallocL("create_reply", sizeof(reply_state
));
3219 if (!reply
) FatalError("ERROR: malloc");
3220 bzero(reply
, sizeof(reply_state
));
3221 reply
->ts
= t_morecoming
;
3222 reply
->sd
= request
->sd
;
3223 reply
->request
= request
;
3224 reply
->len
= totallen
;
3225 reply
->msgbuf
= mallocL("create_reply", totallen
);
3226 if (!reply
->msgbuf
) FatalError("ERROR: malloc");
3227 bzero(reply
->msgbuf
, totallen
);
3228 reply
->mhdr
= (ipc_msg_hdr
*)reply
->msgbuf
;
3229 reply
->rhdr
= (reply_hdr
*)(reply
->msgbuf
+ sizeof(ipc_msg_hdr
));
3230 reply
->sdata
= reply
->msgbuf
+ sizeof(ipc_msg_hdr
) + sizeof(reply_hdr
);
3231 reply
->mhdr
->version
= VERSION
;
3232 reply
->mhdr
->op
= op
;
3233 reply
->mhdr
->datalen
= totallen
- sizeof(ipc_msg_hdr
);
3237 static int deliver_error(request_state
*rstate
, mStatus err
)
3240 undelivered_error_t
*undeliv
;
3242 err
= dnssd_htonl(err
);
3243 nwritten
= send(rstate
->sd
, &err
, sizeof(mStatus
), 0);
3244 if (nwritten
< (int)sizeof(mStatus
))
3246 if (dnssd_errno() == dnssd_EINTR
|| dnssd_errno() == dnssd_EWOULDBLOCK
)
3250 my_perror("ERROR: send - unable to deliver error to client");
3255 //client blocked - store result and come backr
3256 undeliv
= mallocL("deliver_error", sizeof(undelivered_error_t
));
3257 if (!undeliv
) FatalError("ERROR: malloc");
3259 undeliv
->nwritten
= nwritten
;
3260 undeliv
->sd
= rstate
->sd
;
3261 rstate
->u_err
= undeliv
;
3268 // returns 0 on success, -1 if send is incomplete, or on terminal failure (request is aborted)
3269 static transfer_state
send_undelivered_error(request_state
*rs
)
3273 nwritten
= send(rs
->u_err
->sd
, (char *)(&rs
->u_err
->err
) + rs
->u_err
->nwritten
, sizeof(mStatus
) - rs
->u_err
->nwritten
, 0);
3276 if (dnssd_errno() == dnssd_EINTR
|| dnssd_errno() == dnssd_EWOULDBLOCK
)
3280 my_perror("ERROR: send - unable to deliver error to client\n");
3284 if ((unsigned int)(nwritten
+ rs
->u_err
->nwritten
) >= sizeof(mStatus
))
3286 freeL("send_undelivered_error", rs
->u_err
);
3290 rs
->u_err
->nwritten
+= nwritten
;
3291 return t_morecoming
;
3294 // send bogus data along with an error code to the app callback
3295 // returns 0 on success (linking reply into list of not fully delivered),
3296 // -1 on failure (request should be aborted)
3297 static int deliver_async_error(request_state
*rs
, reply_op_t op
, mStatus err
)
3303 if (rs
->no_reply
) return 0;
3304 len
= 256; // long enough for any reply handler to read all args w/o buffer overrun
3305 reply
= create_reply(op
, len
, rs
);
3306 reply
->rhdr
->error
= dnssd_htonl(err
);
3307 ts
= send_msg(reply
);
3308 if (ts
== t_error
|| ts
== t_terminated
)
3310 freeL("deliver_async_error", reply
);
3313 else if (ts
== t_complete
) freeL("deliver_async_error", reply
);
3314 else if (ts
== t_morecoming
) append_reply(rs
, reply
); // client is blocked, link reply into list
3318 static void abort_request(request_state
*rs
)
3320 reply_state
*rep
, *ptr
;
3322 if (rs
->terminate
) rs
->terminate(rs
->termination_context
); // terminate field may not be set yet
3323 if (rs
->msgbuf
) freeL("abort_request", rs
->msgbuf
);
3324 LogOperation("%3d: Removing FD", rs
->sd
);
3325 udsSupportRemoveFDFromEventLoop(rs
->sd
); // Note: This also closes file descriptor rs->sd for us
3326 rs
->sd
= dnssd_InvalidSocket
;
3328 // free pending replies
3332 if (rep
->msgbuf
) freeL("abort_request", rep
->msgbuf
);
3335 freeL("abort_request", ptr
);
3340 freeL("abort_request", rs
->u_err
);
3345 static void unlink_request(request_state
*rs
)
3349 if (rs
== all_requests
)
3351 all_requests
= all_requests
->next
;
3352 freeL("unlink_request", rs
);
3355 for(ptr
= all_requests
; ptr
->next
; ptr
= ptr
->next
)
3356 if (ptr
->next
== rs
)
3358 ptr
->next
= rs
->next
;
3359 freeL("unlink_request", rs
);
3364 //hack to search-replace perror's to LogMsg's
3365 static void my_perror(char *errmsg
)
3367 LogMsg("%s: %s", errmsg
, dnssd_strerror(dnssd_errno()));
3370 // check that the message delivered by the client is sufficiently long to extract the required data from the buffer
3371 // without overrunning it.
3372 // returns 0 on success, -1 on error.
3374 static int validate_message(request_state
*rstate
)
3378 switch(rstate
->hdr
.op
)
3380 case resolve_request
: min_size
= sizeof(DNSServiceFlags
) + // flags
3381 sizeof(uint32_t) + // interface
3382 (3 * sizeof(char)); // name, regtype, domain
3384 case query_request
: min_size
= sizeof(DNSServiceFlags
) + // flags
3385 sizeof(uint32_t) + // interface
3386 sizeof(char) + // fullname
3387 (2 * sizeof(uint16_t)); // type, class
3389 case browse_request
: min_size
= sizeof(DNSServiceFlags
) + // flags
3390 sizeof(uint32_t) + // interface
3391 (2 * sizeof(char)); // regtype, domain
3393 case reg_service_request
: min_size
= sizeof(DNSServiceFlags
) + // flags
3394 sizeof(uint32_t) + // interface
3395 (4 * sizeof(char)) + // name, type, domain, host
3396 (2 * sizeof(uint16_t)); // port, textlen
3398 case enumeration_request
: min_size
= sizeof(DNSServiceFlags
) + // flags
3399 sizeof(uint32_t); // interface
3401 case reg_record_request
: min_size
= sizeof(DNSServiceFlags
) + // flags
3402 sizeof(uint32_t) + // interface
3403 sizeof(char) + // fullname
3404 (3 * sizeof(uint16_t)) + // type, class, rdlen
3405 sizeof(uint32_t); // ttl
3407 case add_record_request
: min_size
= sizeof(DNSServiceFlags
) + // flags
3408 (2 * sizeof(uint16_t)) + // type, rdlen
3409 sizeof(uint32_t); // ttl
3411 case update_record_request
: min_size
= sizeof(DNSServiceFlags
) + // flags
3412 sizeof(uint16_t) + // rdlen
3413 sizeof(uint32_t); // ttl
3415 case remove_record_request
: min_size
= sizeof(DNSServiceFlags
); // flags
3417 case reconfirm_record_request
: min_size
=sizeof(DNSServiceFlags
) + // flags
3418 sizeof(uint32_t) + // interface
3419 sizeof(char) + // fullname
3420 (3 * sizeof(uint16_t)); // type, class, rdlen
3422 case setdomain_request
: min_size
= sizeof(DNSServiceFlags
) + sizeof(char); // flags + domain
3425 LogMsg("ERROR: validate_message - unsupported request type: %d", rstate
->hdr
.op
);
3429 return (rstate
->data_bytes
>= min_size
? 0 : -1);
3433 static uint32_t dnssd_htonl(uint32_t l
)
3438 data
= (char*) &ret
;
3447 static char * win32_strerror(int inErrorCode
)
3449 static char buffer
[1024];
3452 memset(buffer
, 0, sizeof(buffer
));
3455 FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS
,
3457 (DWORD
) inErrorCode
,
3458 MAKELANGID( LANG_NEUTRAL
, SUBLANG_DEFAULT
),
3465 // Remove any trailing CR's or LF's since some messages have them.
3467 while( ( n
> 0 ) && isspace( ( (unsigned char *) buffer
)[ n
- 1 ] ) )
3469 buffer
[ --n
] = '\0';