uds_daemon.c revision 4904:cd464a980538
1/* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2003-2006 Apple Computer, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16
17    Change History (most recent first):
18
19$Log: uds_daemon.c,v $
20Revision 1.201.2.1  2006/08/29 06:24:36  cheshire
21Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
22
23Revision 1.201  2006/06/29 03:02:47  cheshire
24<rdar://problem/4607042> mDNSResponder NXDOMAIN and CNAME support
25
26Revision 1.200  2006/06/28 08:56:26  cheshire
27Added "_op" to the end of the operation code enum values,
28to differentiate them from the routines with the same names
29
30Revision 1.199  2006/06/28 08:53:39  cheshire
31Added (commented out) debugging messages
32
33Revision 1.198  2006/06/27 20:16:07  cheshire
34Fix code layout
35
36Revision 1.197  2006/05/18 01:32:35  cheshire
37<rdar://problem/4472706> iChat: Lost connection with Bonjour
38(mDNSResponder insufficiently defensive against malformed browsing PTR responses)
39
40Revision 1.196  2006/05/05 07:07:13  cheshire
41<rdar://problem/4538206> mDNSResponder fails when UDS reads deliver partial data
42
43Revision 1.195  2006/04/25 20:56:28  mkrochma
44Added comment about previous checkin
45
46Revision 1.194  2006/04/25 18:29:36  mkrochma
47Workaround for warning: unused variable 'status' when building mDNSPosix
48
49Revision 1.193  2006/03/19 17:14:38  cheshire
50<rdar://problem/4483117> Need faster purging of stale records
51read_rr_from_ipc_msg was not setting namehash and rdatahash
52
53Revision 1.192  2006/03/18 20:58:32  cheshire
54Misplaced curly brace
55
56Revision 1.191  2006/03/10 22:19:43  cheshire
57Update debugging message in resolve_result_callback() to indicate whether event is ADD or RMV
58
59Revision 1.190  2006/03/10 21:56:12  cheshire
60<rdar://problem/4111464> After record update, old record sometimes remains in cache
61When service TXT and SRV record both change, clients with active resolve calls get *two* callbacks, one
62when the TXT data changes, and then immediately afterwards a second callback with the new port number
63This change suppresses the first unneccessary (and confusing) callback
64
65Revision 1.189  2006/01/06 00:56:31  cheshire
66<rdar://problem/4400573> Should remove PID file on exit
67
68Revision 1.188  2005/10/11 22:15:03  cheshire
69<rdar://problem/4296042> Add memory corruption safeguards to uds_daemon.c
70Only compile uds_validatelists() when building for Mac OS X
71
72Revision 1.187  2005/10/11 20:30:27  cheshire
73<rdar://problem/4296042> Add memory corruption safeguards to uds_daemon.c
74
75Revision 1.186  2005/09/12 07:11:53  herscher
76<rdar://problem/4248878> Lazily call RegisterSearchDomains to workaround crashes of several routers. This code is conditionally compiled, and currently is only enabled on Windows platforms.
77
78Revision 1.185  2005/07/29 00:55:10  ksekar
79Removed validation check in uds_validatelists which generated false alarms
80
81Revision 1.184  2005/07/04 22:40:26  cheshire
82Additional debugging code to help catch memory corruption
83
84Revision 1.183  2005/06/13 22:39:11  cheshire
85<rdar://problem/4144870> Missing return statement in handle_enum_request() error handling
86
87Revision 1.182  2005/03/21 00:39:31  shersche
88<rdar://problem/4021486> Fix build warnings on Win32 platform
89
90Revision 1.181  2005/03/20 20:21:32  shersche
91<rdar://problem/4056827> mDNSResponder crashes when incorrect interface index is passed to DNSServiceRegister()
92Text record length and data parameters must be initialized to 0 and NULL to ensure that the service request
93object is cleaned up correctly when encountering an interface index error.
94
95Revision 1.180  2005/03/10 00:13:12  cheshire
96<rdar://problem/4043098> DNSServiceBrowse no longer returning error codes for invalid types
97In handle_browse_request(), mStatus err was being set correctly if an error occurred,
98but the end of the function returned mStatus_NoError intead of err.
99
100Revision 1.179  2005/03/04 02:47:26  ksekar
101<rdar://problem/4026393> SCPreference domains disappear from enumeration when moving out from firewall
102
103Revision 1.178  2005/02/25 19:35:38  ksekar
104<rdar://problem/4023750> Non-local empty string registration failures should not return errors to caller
105
106Revision 1.177  2005/02/25 03:05:41  cheshire
107Change "broken pipe" message to debugf()
108
109Revision 1.176  2005/02/24 18:44:45  ksekar
110<rdar://problem/4018516> Printer Sharing does not get re-registered with wide-area
111
112Revision 1.175  2005/02/21 21:31:25  ksekar
113<rdar://problem/4015162> changed LogMsg to debugf
114
115Revision 1.174  2005/02/20 01:41:17  cheshire
116Fix compiler signed/unsigned warning
117
118Revision 1.173  2005/02/18 01:26:42  cheshire
119<rdar://problem/4012162> "Could not write data to client after 60 seconds" message could be more helpful
120Log additional information about failed client
121
122Revision 1.172  2005/02/18 00:58:35  cheshire
123<rdar://problem/4012162> "Could not write data to client after 60 seconds" message could be more helpful
124
125Revision 1.171  2005/02/18 00:43:12  cheshire
126<rdar://problem/4010245> mDNSResponder should auto-truncate service names that are too long
127
128Revision 1.170  2005/02/16 01:15:02  cheshire
129Improve LogOperation() debugging messages for DNSServiceBrowse and DNSServiceRegister
130
131Revision 1.169  2005/02/08 01:57:14  cheshire
132More detailed error reporting in udsserver_init()
133
134Revision 1.168  2005/02/03 00:44:37  cheshire
135<rdar://problem/3986663> DNSServiceUpdateRecord returns kDNSServiceErr_Invalid when rdlen=0, rdata=NULL
136
137Revision 1.167  2005/02/02 02:19:32  cheshire
138Add comment explaining why unlink(MDNS_UDS_SERVERPATH); fails
139
140Revision 1.166  2005/02/01 19:58:52  ksekar
141Shortened cryptic "broken pipe" syslog message
142
143Revision 1.165  2005/02/01 19:56:47  ksekar
144Moved LogMsg from daemon.c to uds_daemon.c, cleaned up wording
145
146Revision 1.164  2005/01/28 06:07:55  cheshire
147Don't use deliver_error() from within handle_regrecord_request()
148
149Revision 1.163  2005/01/28 01:39:16  cheshire
150Include file descriptor number in "broken pipe" message
151
152Revision 1.162  2005/01/27 23:59:20  cheshire
153Remove extraneous LogMsg
154
155Revision 1.161  2005/01/27 22:57:56  cheshire
156Fix compile errors on gcc4
157
158Revision 1.160  2005/01/27 20:52:11  cheshire
159<rdar://problem/3972566> mDNSResponder leaks sockets for add/update/remove record calls
160
161Revision 1.159  2005/01/27 01:45:25  cheshire
162<rdar://problem/3976147> mDNSResponder should never call exit(1);
163
164Revision 1.158  2005/01/25 17:28:07  ksekar
165<rdar://problem/3971467> Should not return "local" twice for domain enumeration
166
167Revision 1.157  2005/01/21 02:20:39  cheshire
168Fix mistake in LogOperation() format string
169
170Revision 1.156  2005/01/19 19:15:36  ksekar
171Refinement to <rdar://problem/3954575> - Simplify mDNS_PurgeResultsForDomain logic and move into daemon layer
172
173Revision 1.155  2005/01/19 03:00:47  cheshire
174Show Add/Rmv in DNSServiceBrowse LogOperation() message
175
176Revision 1.154  2005/01/15 00:56:42  ksekar
177<rdar://problem/3954575> Unicast services don't disappear when logging
178out of VPN
179
180Revision 1.153  2005/01/14 18:44:28  ksekar
181<rdar://problem/3954609> mDNSResponder is crashing when changing domains
182
183Revision 1.152  2005/01/13 17:16:38  ksekar
184Back out checkin 1.150 - correct fix is on clientstub side
185
186Revision 1.151  2005/01/11 21:06:29  ksekar
187Changed now-benign LogMsg to debugf
188
189Revision 1.150  2005/01/07 23:59:15  ksekar
190<rdar://problem/3942900> dnd-sd shows the wrong port numbers
191
192Revision 1.149  2004/12/20 23:20:35  cheshire
193<rdar://problem/3928361> mDNSResponder crashes repeatedly when printer sharing is enabled
194Make sure to call mDNS_SetupResourceRecord() for all newly created AuthRecords
195
196Revision 1.148  2004/12/20 20:37:35  cheshire
197AllowRemoteQuery not set for the extras in a ServiceRecordSet
198
199Revision 1.147  2004/12/20 00:15:41  cheshire
200Include client file descriptor numbers in udsserver_info() output
201
202Revision 1.146  2004/12/17 05:25:47  cheshire
203<rdar://problem/3925163> Shorten DNS-SD queries to avoid NAT bugs
204
205Revision 1.145  2004/12/16 21:39:46  cheshire
206Include CacheGroup objects in CacheUsed count
207
208Revision 1.144  2004/12/16 21:27:38  ksekar
209Fixed build failures when compiled with verbose debugging messages
210
211Revision 1.143  2004/12/16 20:13:02  cheshire
212<rdar://problem/3324626> Cache memory management improvements
213
214Revision 1.142  2004/12/16 08:07:33  shersche
215Fix compiler error (mixed declarations and code) on Windows
216
217Revision 1.141  2004/12/16 01:56:21  cheshire
218Improve DNSServiceEnumerateDomains syslog message
219
220Revision 1.140  2004/12/14 03:02:10  ksekar
221<rdar://problem/3919016> Rare race condition can cause crash
222
223Revision 1.139  2004/12/13 21:18:45  ksekar
224Include uDNS registrations in CountPeerRegistrations
225
226Revision 1.138  2004/12/13 18:23:18  ksekar
227<rdar://problem/3915805> mDNSResponder error when quitting iChat -
228don't close sockets delivering errors to blocked clients
229
230Revision 1.137  2004/12/13 00:09:22  ksekar
231<rdar://problem/3915805> mDNSResponder error when quitting iChat
232
233Revision 1.136  2004/12/11 01:52:10  cheshire
234<rdar://problem/3785820> Support kDNSServiceFlagsAllowRemoteQuery for registering services too
235
236Revision 1.135  2004/12/10 20:46:37  cheshire
237Change LogOperation message to debugf
238
239Revision 1.134  2004/12/10 13:19:37  cheshire
240Add verbosedebugf() logging message in CountPeerRegistrations()
241
242Revision 1.133  2004/12/10 05:27:26  cheshire
243<rdar://problem/3909147> Guard against multiple autoname services of the same type on the same machine
244
245Revision 1.132  2004/12/10 04:28:28  cheshire
246<rdar://problem/3914406> User not notified of name changes for services using new UDS API
247
248Revision 1.131  2004/12/10 02:09:25  cheshire
249<rdar://problem/3898376> Modify default TTLs
250
251Revision 1.130  2004/12/10 00:55:24  cheshire
252Add full name and type to LogOperation messages for DNSServiceAddRecord/UpdateRecord/RemoveRecord
253
254Revision 1.129  2004/12/09 03:17:23  ksekar
255<rdar://problem/3910435> DomainEnumeration interface index should be zero
256
257Revision 1.128  2004/12/07 21:26:05  ksekar
258<rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration
259
260Revision 1.127  2004/12/07 20:42:34  cheshire
261Add explicit context parameter to mDNS_RemoveRecordFromService()
262
263Revision 1.126  2004/12/07 17:23:55  ksekar
264Fixed LogOperation
265
266Revision 1.125  2004/12/06 21:15:23  ksekar
267<rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
268
269Revision 1.124  2004/11/30 02:19:14  cheshire
270<rdar://problem/3827971> Raise maxfds.rlim_cur for mDNSResponder
271
272Revision 1.123  2004/11/29 23:50:57  cheshire
273Checkin 1.122 not necessary
274
275Revision 1.122  2004/11/24 17:55:01  ksekar
276Added log message clarifying <rdar://problem/3869241> For unicast operations, verify that service types are legal
277
278Revision 1.121  2004/11/24 04:45:52  cheshire
279Spelling mistake
280
281Revision 1.120  2004/11/24 00:10:44  cheshire
282<rdar://problem/3869241> For unicast operations, verify that service types are legal
283
284Revision 1.119  2004/11/23 23:54:17  ksekar
285<rdar://problem/3890318> Wide-Area DNSServiceRegisterRecord() failures
286can crash mDNSResponder
287
288Revision 1.118  2004/11/23 22:33:01  cheshire
289<rdar://problem/3654910> Remove temporary workaround code for iChat
290
291Revision 1.117  2004/11/23 20:23:10  ksekar
292Fixed LogOperation that causes crash on connected service deregistrations
293
294Revision 1.116  2004/11/23 03:39:47  cheshire
295Let interface name/index mapping capability live directly in JNISupport.c,
296instead of having to call through to the daemon via IPC to get this information.
297
298Revision 1.115  2004/11/13 00:12:53  ksekar
299Fixed some LogOperation printf converstions for debug builds.
300
301Revision 1.114  2004/11/12 18:25:45  shersche
302Tidy up cross platform usleep code fragment.
303
304Revision 1.113  2004/11/12 03:21:41  rpantos
305rdar://problem/3809541 Add DNSSDMapIfIndexToName, DNSSDMapNameToIfIndex.
306
307Revision 1.112  2004/11/11 16:58:32  ksekar
308Removed unused code (previously wrapped in #if 0)
309
310Revision 1.111  2004/11/05 22:47:37  shersche
311Conditionally compile usleep(1000) to be Sleep(1) on Windows
312Submitted by: Pavel Repin <prepin@gmail.com>
313
314Revision 1.110  2004/11/05 19:56:56  ksekar
315<rdar://problem/3862646> We no longer need to browse .Mac domains by
316default - changed #if 0 to more descriptive #ifdef _HAVE_SETDOMAIN_SUPPORT_
317
318Revision 1.109  2004/11/04 03:40:45  cheshire
319More debugging messages
320
321Revision 1.108  2004/11/03 02:25:51  cheshire
322<rdar://problem/3324137> Conflict for Computer Name should update *all* empty string services, not just the one with the conflict
323
324Revision 1.107  2004/11/02 19:39:23  ksekar
325<rdar://problem/3862646> We no longer need to browse .Mac domains by default
326
327Revision 1.106  2004/11/02 02:12:21  cheshire
328<rdar://problem/3839111> Remove unnecessary memory allocations
329
330Revision 1.105  2004/10/28 19:07:19  cheshire
331Add some more debugging checks and improved LogOperation() messages
332
333Revision 1.104  2004/10/26 18:53:15  cheshire
334Avoid unused variable warning
335
336Revision 1.103  2004/10/26 07:15:55  cheshire
337Add file descriptor number to all LogOperation messages
338
339Revision 1.102  2004/10/26 06:11:42  cheshire
340Add improved logging to aid in diagnosis of <rdar://problem/3842714> mDNSResponder crashed
341
342Revision 1.101  2004/10/26 04:31:44  cheshire
343Rename CountSubTypes() as ChopSubTypes()
344
345Revision 1.100  2004/10/26 01:17:48  cheshire
346Use "#if 0" instead of commenting out code
347
348Revision 1.99  2004/10/19 21:33:22  cheshire
349<rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
350Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name
351doesn't force multicast unless you set this flag to indicate explicitly that this is what you want
352
353Revision 1.98  2004/10/14 01:59:33  cheshire
354<rdar://problem/3839208> UDS resolves don't work for uDNS services
355
356Revision 1.97  2004/10/13 00:58:35  cheshire
357<rdar://problem/3832738> Registering a proxy doesn't work
358
359Revision 1.96  2004/09/30 00:25:00  ksekar
360<rdar://problem/3695802> Dynamically update default registration domains on config change
361
362Revision 1.95  2004/09/26 23:20:36  ksekar
363<rdar://problem/3813108> Allow default registrations in multiple wide-area domains
364
365Revision 1.94  2004/09/22 18:27:06  ksekar
366<rdar://problem/3811427> allow DNSServiceAddRecord to pass zero to get
367default record TTL
368
369Revision 1.93  2004/09/22 02:39:44  cheshire
370<rdar://problem/3810757> Allow DNSServiceRegisterRecord to pass zero to get default record TTL
371
372Revision 1.92  2004/09/22 02:34:04  cheshire
373Rename parameter "ttl" to "GetTTL" for clarity
374
375Revision 1.91  2004/09/22 02:25:43  cheshire
376Fix spelling errors
377
378Revision 1.90  2004/09/21 23:40:12  ksekar
379<rdar://problem/3810349> mDNSResponder to return errors on NAT traversal failure
380
381Revision 1.89  2004/09/21 23:29:51  cheshire
382<rdar://problem/3680045> DNSServiceResolve should delay sending packets
383
384Revision 1.88  2004/09/21 23:12:46  cheshire
385Reorder initialization of question fields to match structure order
386
387Revision 1.87  2004/09/21 22:18:33  cheshire
388In SIGINFO output, display a '-' next to records that have the Unique bit set
389
390Revision 1.86  2004/09/21 21:05:11  cheshire
391Move duplicate code out of mDNSMacOSX/daemon.c and mDNSPosix/PosixDaemon.c,
392into mDNSShared/uds_daemon.c
393
394Revision 1.85  2004/09/18 01:11:58  ksekar
395<rdar://problem/3806734> Add a user's default domain to empty-string browse list
396
397Revision 1.84  2004/09/17 01:08:55  cheshire
398Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
399  The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
400  declared in that file are ONLY appropriate to single-address-space embedded applications.
401  For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
402
403Revision 1.83  2004/09/16 23:26:33  cheshire
404Move version check inside preceeding "if" that checks we have a complete header
405
406Revision 1.82  2004/09/16 23:14:25  cheshire
407Changes for Windows compatibility
408
409Revision 1.81  2004/09/16 21:46:38  ksekar
410<rdar://problem/3665304> Need SPI for LoginWindow to associate a UID with a Wide Area domain
411
412Revision 1.80  2004/09/16 01:58:23  cheshire
413Fix compiler warnings
414
415Revision 1.79  2004/09/16 00:24:49  cheshire
416<rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
417
418Revision 1.78  2004/09/15 21:44:20  cheshire
419<rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init
420Show time value in log to help diagnose errors
421
422Revision 1.77  2004/09/15 00:19:18  cheshire
423<rdar://problem/3785823> read_rr_from_ipc_msg should use mDNS_SetupResourceRecord()
424
425Revision 1.76  2004/09/02 06:39:52  cheshire
426Minor textual cleanup for clarity
427
428Revision 1.75  2004/09/02 03:48:47  cheshire
429<rdar://problem/3709039> Disable targeted unicast query support by default
4301. New flag kDNSServiceFlagsAllowRemoteQuery to indicate we want to allow remote queries for this record
4312. New field AllowRemoteQuery in AuthRecord structure
4323. uds_daemon.c sets AllowRemoteQuery if kDNSServiceFlagsAllowRemoteQuery is set
4334. mDNS.c only answers remote queries if AllowRemoteQuery is set
434
435Revision 1.74  2004/08/25 02:32:47  cheshire
436Minor cleanup: replace "&regtype[0]" with "regtype"
437
438Revision 1.73  2004/08/25 02:30:40  cheshire
439<rdar://problem/3588761> Current method of doing subtypes causes name collisions
440
441Revision 1.72  2004/08/14 03:22:42  cheshire
442<rdar://problem/3762579> Dynamic DNS UI <-> mDNSResponder glue
443Add GetUserSpecifiedDDNSName() routine
444Convert ServiceRegDomain to domainname instead of C string
445Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs
446
447Revision 1.71  2004/08/11 04:21:21  rpantos
448Fix Windows build.
449
450Revision 1.70  2004/08/11 02:07:00  cheshire
451Remove "mDNS *globalInstance" parameter from udsserver_init()
452Move CheckForDuplicateRegistrations from daemon.c
453<rdar://problem/3501938> No warning when accidentally registering the same service multiple times using socket API
454
455Revision 1.69  2004/08/10 16:14:48  cheshire
456Fix debug builds (oops)
457
458Revision 1.68  2004/08/10 06:24:56  cheshire
459Use types with precisely defined sizes for 'op' and 'reg_index', for better
460compatibility if the daemon and the client stub are built using different compilers
461
462Revision 1.67  2004/07/27 07:14:16  shersche
463make error socket non-blocking after call to connect()
464
465Revision 1.66  2004/07/13 21:24:25  rpantos
466Fix for <rdar://problem/3701120>.
467
468Revision 1.65  2004/06/26 03:17:14  shersche
469implement cross-platform strerror function
470
471Submitted by: herscher
472
473Revision 1.64  2004/06/25 00:26:27  rpantos
474Changes to fix the Posix build on Solaris.
475
476Revision 1.63  2004/06/24 03:43:44  rpantos
477Fix previous checkin so it builds on Windows.
478
479Revision 1.62  2004/06/24 00:57:08  ksekar
480Replaced code acccidentally removed in checkin 1.59.
481
482Revision 1.61  2004/06/19 00:09:39  cheshire
483Remove unused strsep() implementation
484
485Revision 1.60  2004/06/18 19:10:00  cheshire
486<rdar://problem/3588761> Current method of doing subtypes causes name collisions
487
488Revision 1.59  2004/06/18 05:10:31  rpantos
489Changes to allow code to be used on Windows
490
491Revision 1.58  2004/06/15 03:54:08  cheshire
492Include mDNS_TimeNow(&mDNSStorage) in SIGINFO output
493
494Revision 1.57  2004/06/12 01:47:27  ksekar
495<rdar://problem/3690241>: BBEdit crashes when trying to check for newer version
496udsserver_idle compared time in ticks to interval in seconds.
497
498Revision 1.56  2004/06/12 01:35:47  cheshire
499Changes for Windows compatibility
500
501Revision 1.55  2004/06/05 00:04:27  cheshire
502<rdar://problem/3668639>: wide-area domains should be returned in reg. domain enumeration
503
504Revision 1.54  2004/06/01 22:22:52  ksekar
505<rdar://problem/3668635>: wide-area default registrations should be in
506.local too
507
508Revision 1.53  2004/05/28 23:42:37  ksekar
509<rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
510
511Revision 1.52  2004/05/26 00:39:49  ksekar
512<rdar://problem/3667105>: wide-area DNS-SD servers don't appear in
513Finder
514Use local-only InterfaceID for GetDomains calls for sockets-API
515
516Revision 1.51  2004/05/18 23:51:27  cheshire
517Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
518
519Revision 1.50  2004/05/14 16:39:47  ksekar
520Browse for iChat locally for now.
521
522Revision 1.49  2004/05/13 21:33:52  ksekar
523Clean up non-local registration control via config file.  Force iChat
524registrations to be local for now.
525
526Revision 1.48  2004/05/13 04:13:19  ksekar
527Updated SIGINFO handler for multi-domain browses
528
529Revision 1.47  2004/05/12 22:04:01  ksekar
530Implemented multi-domain browsing by default for uds_daemon.
531
532Revision 1.46  2004/05/06 18:42:58  ksekar
533General dns_sd.h API cleanup, including the following radars:
534<rdar://problem/3592068>: Remove flags with zero value
535<rdar://problem/3479569>: Passing in NULL causes a crash.
536
537Revision 1.45  2004/03/12 08:49:28  cheshire
538#include <sys/socket.h>
539
540Revision 1.44  2004/02/25 01:25:27  ksekar
541<rdar://problem/3569212>: DNSServiceRegisterRecord flags not error-checked
542
543Revision 1.43  2004/02/24 01:46:40  cheshire
544Manually reinstate lost checkin 1.36
545
546Revision 1.42  2004/02/05 19:39:29  cheshire
547Move creation of /var/run/mDNSResponder.pid to uds_daemon.c,
548so that all platforms get this functionality
549
550Revision 1.41  2004/02/03 18:59:02  cheshire
551Change "char *domain" parameter for format_enumeration_reply to "const char *domain"
552
553Revision 1.40  2004/01/28 03:41:00  cheshire
554<rdar://problem/3541946>: Need ability to do targeted queries as well as multicast queries
555
556Revision 1.39  2004/01/25 00:03:21  cheshire
557Change to use mDNSVal16() instead of private PORT_AS_NUM() macro
558
559Revision 1.38  2004/01/19 19:51:46  cheshire
560Fix compiler error (mixed declarations and code) on some versions of Linux
561
562Revision 1.37  2003/12/08 21:11:42  rpantos
563Changes necessary to support mDNSResponder on Linux.
564
565Revision 1.36  2003/12/04 23:40:57  cheshire
566<rdar://problem/3484766>: Security: Crashing bug in mDNSResponder
567Fix some more code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers.
568
569Revision 1.35  2003/12/03 19:10:22  ksekar
570<rdar://problem/3498644>: malloc'd data not zero'd
571
572Revision 1.34  2003/12/03 02:00:01  ksekar
573<rdar://problem/3498644>: malloc'd data not zero'd
574
575Revision 1.33  2003/11/22 01:18:46  ksekar
576<rdar://problem/3486646>: config change handler not called for dns-sd services
577
578Revision 1.32  2003/11/20 21:46:12  ksekar
579<rdar://problem/3486635>: leak: DNSServiceRegisterRecord
580
581Revision 1.31  2003/11/20 20:33:05  ksekar
582<rdar://problem/3486635>: leak: DNSServiceRegisterRecord
583
584Revision 1.30  2003/11/20 02:10:55  ksekar
585<rdar://problem/3486643>: cleanup DNSServiceAdd/RemoveRecord
586
587Revision 1.29  2003/11/14 21:18:32  cheshire
588<rdar://problem/3484766>: Security: Crashing bug in mDNSResponder
589Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers.
590
591Revision 1.28  2003/11/08 22:18:29  cheshire
592<rdar://problem/3477870>: Don't need to show process ID in *every* mDNSResponder syslog message
593
594Revision 1.27  2003/11/05 22:44:57  ksekar
595<rdar://problem/3335230>: No bounds checking when reading data from client
596Reviewed by: Stuart Cheshire
597
598Revision 1.26  2003/10/23 17:51:04  ksekar
599<rdar://problem/3335216>: handle blocked clients more efficiently
600Changed gettimeofday() to mDNS_TimeNow(&mDNSStorage);
601
602Revision 1.25  2003/10/22 23:37:49  ksekar
603<rdar://problem/3459141>: crash/hang in abort_client
604
605Revision 1.24  2003/10/21 20:59:40  ksekar
606<rdar://problem/3335216>: handle blocked clients more efficiently
607
608Revision 1.23  2003/09/23 02:12:43  cheshire
609Also include port number in list of services registered via new UDS API
610
611Revision 1.22  2003/08/19 16:03:55  ksekar
612<rdar://problem/3380097>: ER: SIGINFO dump should include resolves started by DNSServiceQueryRecord
613Check termination_context for NULL before dereferencing.
614
615Revision 1.21  2003/08/19 05:39:43  cheshire
616<rdar://problem/3380097> SIGINFO dump should include resolves started by DNSServiceQueryRecord
617
618Revision 1.20  2003/08/16 03:39:01  cheshire
619<rdar://problem/3338440> InterfaceID -1 indicates "local only"
620
621Revision 1.19  2003/08/15 20:16:03  cheshire
622<rdar://problem/3366590> mDNSResponder takes too much RPRVT
623We want to avoid touching the rdata pages, so we don't page them in.
6241. RDLength was stored with the rdata, which meant touching the page just to find the length.
625   Moved this from the RData to the ResourceRecord object.
6262. To avoid unnecessarily touching the rdata just to compare it,
627   compute a hash of the rdata and store the hash in the ResourceRecord object.
628
629Revision 1.18  2003/08/15 00:38:00  ksekar
630<rdar://problem/3377005>: Bug: buffer overrun when reading long rdata from client
631
632Revision 1.17  2003/08/14 02:18:21  cheshire
633<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
634
635Revision 1.16  2003/08/13 23:58:52  ksekar
636<rdar://problem/3374911>: Bug: UDS Sub-type browsing works, but not sub-type registration
637Fixed pointer increment error, moved subtype reading for-loop for easier error bailout.
638
639Revision 1.15  2003/08/13 17:30:33  ksekar
640<rdar://problem/3374671>: DNSServiceAddRecord doesn't work
641Fixed various problems with handling the AddRecord request and freeing the ExtraResourceRecords.
642
643Revision 1.14  2003/08/12 19:56:25  cheshire
644Update to APSL 2.0
645
646 */
647
648#pragma ident	"%Z%%M%	%I%	%E% SMI"
649
650#if defined(_WIN32)
651#include <process.h>
652#define MDNS_LAZY_REGISTER_SEARCH_DOMAINS
653#define dnssd_strerror(X)	win32_strerror(X)
654#define usleep(X)				Sleep(((X)+999)/1000)
655static char *	win32_strerror(int inErrorCode);
656#else
657#include <fcntl.h>
658#include <errno.h>
659#include <sys/ioctl.h>
660#include <sys/types.h>
661#include <sys/time.h>
662#include <sys/resource.h>
663#define dnssd_strerror(X)	strerror(X)
664#endif
665
666#include <stdlib.h>
667#include <stdio.h>
668#include "mDNSEmbeddedAPI.h"
669#include "DNSCommon.h"
670#include "uds_daemon.h"
671#include "dns_sd.h"
672#include "dnssd_ipc.h"
673
674// Apple specific configuration functionality, not required for other platforms
675#ifdef __MACOSX__
676#include <sys/ucred.h>
677#ifndef LOCAL_PEERCRED
678#define LOCAL_PEERCRED 0x001 /* retrieve peer credentials */
679#endif // LOCAL_PEERCRED
680#endif //__MACOSX__
681
682#if defined(MDNS_LAZY_REGISTER_SEARCH_DOMAINS)
683extern mStatus dDNS_RegisterSearchDomains( mDNS * const m );
684#endif
685
686// Types and Data Structures
687// ----------------------------------------------------------------------
688
689typedef enum
690    {
691    t_uninitialized,
692    t_morecoming,
693    t_complete,
694    t_error,
695    t_terminated
696    } transfer_state;
697
698typedef void (*req_termination_fn)(void *);
699
700typedef struct registered_record_entry
701    {
702    uint32_t key;
703    AuthRecord *rr;
704    struct registered_record_entry *next;
705    client_context_t client_context;
706    struct request_state *rstate;
707    } registered_record_entry;
708
709// A single registered service: ServiceRecordSet + bookkeeping
710// Note that we duplicate some fields from parent service_info object
711// to facilitate cleanup, when instances and parent may be deallocated at different times.
712typedef struct service_instance
713    {
714    struct service_instance *next;
715    mDNSBool autoname;				// Set if this name is tied to the Computer Name
716    mDNSBool autorename;			// Set if this client wants us to automatically rename on conflict
717    mDNSBool allowremotequery;		// Respond to unicast queries from outside the local link?
718    mDNSBool rename_on_memfree;  	// Set on config change when we deregister original name
719    domainlabel name;
720    domainname domain;
721    mDNSBool default_local;			// is this the "local." from an empty-string registration?
722    struct request_state *request;
723    dnssd_sock_t sd;
724    AuthRecord *subtypes;
725    ServiceRecordSet srs; // note - must be last field in struct
726    } service_instance;
727
728// A client-created service.  May reference several service_info objects if default
729// settings cause registration in multiple domains.
730typedef struct
731	{
732    uint16_t txtlen;
733    void *txtdata;
734    mDNSIPPort port;
735    domainlabel name;
736    char type_as_string[MAX_ESCAPED_DOMAIN_NAME];
737    domainname type;
738    mDNSBool default_domain;
739    domainname host;
740    mDNSBool autoname;				// Set if this name is tied to the Computer Name
741    mDNSBool autorename;			// Set if this client wants us to automatically rename on conflict
742    mDNSBool allowremotequery;		// Respond to unicast queries from outside the local link?
743    int num_subtypes;
744    mDNSInterfaceID InterfaceID;
745    service_instance *instances;
746    struct request_state *request;
747	} service_info;
748
749// for multi-domain default browsing
750typedef struct browser_t
751	{
752    DNSQuestion q;
753    domainname domain;
754    struct browser_t *next;
755	} browser_t;
756
757// parent struct for browser instances: list pointer plus metadata
758typedef struct
759	{
760    mDNSBool default_domain;
761    mDNSBool ForceMCast;
762    domainname regtype;
763    mDNSInterfaceID interface_id;
764    struct request_state *rstate;
765    browser_t *browsers;
766	} browser_info_t;
767
768typedef struct
769    {
770    mStatus err;		// Note: This field is in NETWORK byte order
771    int nwritten;
772    dnssd_sock_t sd;
773    } undelivered_error_t;
774
775typedef struct request_state
776    {
777    // connection structures
778    dnssd_sock_t sd;
779
780    // state of read (in case message is read over several recv() calls)
781    transfer_state ts;
782    uint32_t hdr_bytes;		// bytes of header already read
783    ipc_msg_hdr hdr;
784    uint32_t data_bytes;	// bytes of message data already read
785    char *msgbuf;		// pointer to data storage to pass to free()
786    char *msgdata;		// pointer to data to be read from (may be modified)
787    int bufsize;		// size of data storage
788
789    // reply, termination, error, and client context info
790    int no_reply;		// don't send asynchronous replies to client
791    int time_blocked;           // record time of a blocked client
792    void *client_context;	// don't touch this - pointer only valid in client's addr space
793    struct reply_state *replies;  // corresponding (active) reply list
794    undelivered_error_t *u_err;
795    void *termination_context;
796    req_termination_fn terminate;
797
798    //!!!KRS toss these pointers in a union
799    // registration context associated with this request (null if not applicable)
800    registered_record_entry *reg_recs;  // muliple registrations for a connection-oriented request
801    service_info *service_registration;
802    browser_info_t *browser_info;
803    struct request_state *next;
804    } request_state;
805
806// struct physically sits between ipc message header and call-specific fields in the message buffer
807typedef struct
808    {
809    DNSServiceFlags flags;			// Note: This field is in NETWORK byte order
810    uint32_t ifi;					// Note: This field is in NETWORK byte order
811    DNSServiceErrorType error;		// Note: This field is in NETWORK byte order
812    } reply_hdr;
813
814typedef struct reply_state
815    {
816    // state of the transmission
817    dnssd_sock_t sd;
818    transfer_state ts;
819    uint32_t nwriten;
820    uint32_t len;
821    // context of the reply
822    struct request_state *request;  // the request that this answers
823    struct reply_state *next;   // if there are multiple unsent replies
824    // pointer into message buffer - allows fields to be changed after message is formatted
825    ipc_msg_hdr *mhdr;
826    reply_hdr *rhdr;
827    char *sdata;  // pointer to start of call-specific data
828    // pointer to malloc'd buffer
829    char *msgbuf;
830    } reply_state;
831
832// domain enumeration and resolv calls require 2 mDNSCore calls, so we need separate interconnected
833// structures to handle callbacks
834typedef struct
835    {
836    DNSQuestion question;
837    mDNS_DomainType type;
838    request_state *rstate;
839    } domain_enum_t;
840
841typedef struct
842    {
843    domain_enum_t *all;
844    domain_enum_t *def;
845    request_state *rstate;
846    } enum_termination_t;
847
848typedef struct
849    {
850    request_state *rstate;
851    DNSQuestion qtxt;
852    DNSQuestion qsrv;
853    // const ResourceRecord *txt;
854    // const ResourceRecord *srv;
855    mDNSBool   srv;
856    mDNSBool   txt;
857    rdataSRV   srvdata;
858    mDNSu16    txtlen;
859    mDNSu8     txtdata[AbsoluteMaxDNSMessageData];
860    } resolve_termination_t;
861
862#ifdef _HAVE_SETDOMAIN_SUPPORT_
863typedef struct default_browse_list_t
864	{
865    struct default_browse_list_t *next;
866    uid_t uid;
867    AuthRecord ptr_rec;
868	} default_browse_list_t;
869
870static default_browse_list_t *default_browse_list = NULL;
871#endif // _HAVE_SETDOMAIN_SUPPORT_
872
873// globals
874mDNSexport mDNS mDNSStorage;
875#define gmDNS (&mDNSStorage)
876
877static dnssd_sock_t			listenfd		=	dnssd_InvalidSocket;
878static request_state	*	all_requests	=	NULL;
879
880#define MAX_TIME_BLOCKED 60 * mDNSPlatformOneSecond  // try to send data to a blocked client for 60 seconds before
881                                                     // terminating connection
882#define MSG_PAD_BYTES 5                              // pad message buffer (read from client) with n zero'd bytes to guarantee
883                                                     // n get_string() calls w/o buffer overrun
884// private function prototypes
885mDNSlocal void connect_callback(void *info);
886mDNSlocal int read_msg(request_state *rs);
887mDNSlocal int send_msg(reply_state *rs);
888mDNSlocal void abort_request(request_state *rs);
889mDNSlocal void request_callback(void *info);
890mDNSlocal void handle_resolve_request(request_state *rstate);
891mDNSlocal void question_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord);
892mDNSlocal void question_termination_callback(void *context);
893mDNSlocal void handle_browse_request(request_state *request);
894mDNSlocal void browse_termination_callback(void *context);
895mDNSlocal void handle_regservice_request(request_state *request);
896mDNSlocal void regservice_termination_callback(void *context);
897mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result);
898mDNSlocal mStatus handle_add_request(request_state *rstate);
899mDNSlocal mStatus handle_update_request(request_state *rstate);
900mDNSlocal void append_reply(request_state *req, reply_state *rep);
901mDNSlocal int build_domainname_from_strings(domainname *srv, char *name, char *regtype, char *domain);
902mDNSlocal void enum_termination_callback(void *context);
903mDNSlocal void enum_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord);
904mDNSlocal void handle_query_request(request_state *rstate);
905mDNSlocal reply_state *format_enumeration_reply(request_state *rstate, const char *domain, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err);
906mDNSlocal void handle_enum_request(request_state *rstate);
907mDNSlocal mStatus handle_regrecord_request(request_state *rstate);
908mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord * rr, mStatus result);
909mDNSlocal void connected_registration_termination(void *context);
910mDNSlocal void handle_reconfirm_request(request_state *rstate);
911mDNSlocal AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int ttl, int validate_flags);
912mDNSlocal mStatus handle_removerecord_request(request_state *rstate);
913mDNSlocal void reset_connected_rstate(request_state *rstate);
914mDNSlocal int deliver_error(request_state *rstate, mStatus err);
915mDNSlocal int deliver_async_error(request_state *rs, reply_op_t op, mStatus err);
916mDNSlocal transfer_state send_undelivered_error(request_state *rs);
917mDNSlocal reply_state *create_reply(reply_op_t op, size_t datalen, request_state *request);
918mDNSlocal void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd);
919mDNSlocal void my_perror(char *errmsg);
920mDNSlocal void unlink_request(request_state *rs);
921mDNSlocal void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord);
922mDNSlocal void resolve_termination_callback(void *context);
923mDNSlocal int validate_message(request_state *rstate);
924mDNSlocal mStatus remove_extra(request_state *rstate, service_instance *serv);
925mDNSlocal mStatus remove_record(request_state *rstate);
926mDNSlocal void free_service_instance(service_instance *srv);
927mDNSlocal uint32_t dnssd_htonl(uint32_t l);
928mDNSlocal void handle_setdomain_request(request_state *rstate);
929
930// initialization, setup/teardown functions
931
932// If a platform specifies its own PID file name, we use that
933#ifndef PID_FILE
934#define PID_FILE "/var/run/mDNSResponder.pid"
935#endif
936
937mDNSlocal void LogClientInfo(request_state *req)
938	{
939	void *t = req->termination_context;
940	if (t)
941		{
942		if (req->terminate == regservice_termination_callback)
943			{
944			service_instance *ptr;
945			for (ptr = ((service_info *)t)->instances; ptr; ptr = ptr->next)
946				LogMsgNoIdent("%3d: DNSServiceRegister         %##s %u", req->sd, ptr->srs.RR_SRV.resrec.name->c, SRS_PORT(&ptr->srs));
947			}
948		else if (req->terminate == browse_termination_callback)
949			{
950			browser_t *blist;
951			for (blist = req->browser_info->browsers; blist; blist = blist->next)
952				LogMsgNoIdent("%3d: DNSServiceBrowse           %##s", req->sd, blist->q.qname.c);
953			}
954		else if (req->terminate == resolve_termination_callback)
955			LogMsgNoIdent("%3d: DNSServiceResolve          %##s", req->sd, ((resolve_termination_t *)t)->qsrv.qname.c);
956		else if (req->terminate == question_termination_callback)
957			LogMsgNoIdent("%3d: DNSServiceQueryRecord      %##s", req->sd, ((DNSQuestion *)          t)->qname.c);
958		else if (req->terminate == enum_termination_callback)
959			LogMsgNoIdent("%3d: DNSServiceEnumerateDomains %##s", req->sd, ((enum_termination_t *)   t)->all->question.qname.c);
960		}
961	}
962
963mDNSlocal void FatalError(char *errmsg)
964	{
965	LogMsg("%s: %s", errmsg, dnssd_strerror(dnssd_errno()));
966	*(long*)0 = 0;	// On OS X abort() doesn't generate a crash log, but writing to zero does
967	abort();		// On platforms where writing to zero doesn't generate an exception, abort instead
968	}
969
970int udsserver_init(void)
971	{
972	dnssd_sockaddr_t laddr;
973	int				 ret;
974#if defined(_WIN32)
975	u_long opt = 1;
976#endif
977
978	// If a particular platform wants to opt out of having a PID file, define PID_FILE to be ""
979	if (PID_FILE[0])
980		{
981		FILE *fp = fopen(PID_FILE, "w");
982		if (fp != NULL)
983			{
984			fprintf(fp, "%d\n", getpid());
985			fclose(fp);
986			}
987		}
988
989	if ((listenfd = socket(AF_DNSSD, SOCK_STREAM, 0)) == dnssd_InvalidSocket)
990		{
991		my_perror("ERROR: socket(AF_DNSSD, SOCK_STREAM, 0); failed");
992		goto error;
993		}
994
995    bzero(&laddr, sizeof(laddr));
996
997	#if defined(USE_TCP_LOOPBACK)
998		{
999		laddr.sin_family		=	AF_INET;
1000		laddr.sin_port			=	htons(MDNS_TCP_SERVERPORT);
1001		laddr.sin_addr.s_addr	=	inet_addr(MDNS_TCP_SERVERADDR);
1002    	ret = bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr));
1003		if (ret < 0)
1004			{
1005			my_perror("ERROR: bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); failed");
1006			goto error;
1007			}
1008		}
1009	#else
1010		{
1011    	mode_t mask = umask(0);
1012    	unlink(MDNS_UDS_SERVERPATH);  //OK if this fails
1013    	laddr.sun_family = AF_LOCAL;
1014	#ifndef NOT_HAVE_SA_LEN
1015	// According to Stevens (section 3.2), there is no portable way to
1016	// determine whether sa_len is defined on a particular platform.
1017    	laddr.sun_len = sizeof(struct sockaddr_un);
1018	#endif
1019    	strcpy(laddr.sun_path, MDNS_UDS_SERVERPATH);
1020		ret = bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr));
1021		umask(mask);
1022		if (ret < 0)
1023			{
1024			my_perror("ERROR: bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); failed");
1025			goto error;
1026			}
1027		}
1028	#endif
1029
1030	#if defined(_WIN32)
1031	//
1032	// SEH: do we even need to do this on windows?  this socket
1033	// will be given to WSAEventSelect which will automatically
1034	// set it to non-blocking
1035	//
1036	if (ioctlsocket(listenfd, FIONBIO, &opt) != 0)
1037	#else
1038    if (fcntl(listenfd, F_SETFL, O_NONBLOCK) != 0)
1039	#endif
1040		{
1041		my_perror("ERROR: could not set listen socket to non-blocking mode");
1042		goto error;
1043		}
1044
1045	if (listen(listenfd, LISTENQ) != 0)
1046		{
1047		my_perror("ERROR: could not listen on listen socket");
1048		goto error;
1049		}
1050
1051    if (mStatus_NoError != udsSupportAddFDToEventLoop(listenfd, connect_callback, (void *) NULL))
1052        {
1053        my_perror("ERROR: could not add listen socket to event loop");
1054        goto error;
1055        }
1056
1057#if !defined(PLATFORM_NO_RLIMIT)
1058	{
1059	// Set maximum number of open file descriptors
1060	#define MIN_OPENFILES 10240
1061	struct rlimit maxfds, newfds;
1062
1063	// Due to bugs in OS X (<rdar://problem/2941095>, <rdar://problem/3342704>, <rdar://problem/3839173>)
1064	// you have to get and set rlimits once before getrlimit will return sensible values
1065	if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; }
1066	if (setrlimit(RLIMIT_NOFILE, &maxfds) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit");
1067
1068	if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; }
1069	newfds.rlim_max = (maxfds.rlim_max > MIN_OPENFILES) ? maxfds.rlim_max : MIN_OPENFILES;
1070	newfds.rlim_cur = (maxfds.rlim_cur > MIN_OPENFILES) ? maxfds.rlim_cur : MIN_OPENFILES;
1071	if (newfds.rlim_max != maxfds.rlim_max || newfds.rlim_cur != maxfds.rlim_cur)
1072		if (setrlimit(RLIMIT_NOFILE, &newfds) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit");
1073
1074	if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; }
1075	debugf("maxfds.rlim_max %d", (long)maxfds.rlim_max);
1076	debugf("maxfds.rlim_cur %d", (long)maxfds.rlim_cur);
1077	}
1078#endif
1079
1080    return 0;
1081
1082error:
1083
1084    my_perror("ERROR: udsserver_init");
1085    return -1;
1086    }
1087
1088int udsserver_exit(void)
1089    {
1090	dnssd_close(listenfd);
1091
1092#if !defined(USE_TCP_LOOPBACK)
1093	// Currently, we're unable to remove /var/run/mdnsd because we've changed to userid "nobody"
1094	// to give up unnecessary privilege, but we need to be root to remove this Unix Domain Socket.
1095	// It would be nice if we could find a solution to this problem
1096	if (unlink(MDNS_UDS_SERVERPATH))
1097		debugf("Unable to remove %s", MDNS_UDS_SERVERPATH);
1098#endif
1099
1100	if (PID_FILE[0]) unlink(PID_FILE);
1101
1102    return 0;
1103    }
1104
1105mDNSs32 udsserver_idle(mDNSs32 nextevent)
1106    {
1107    request_state *req = all_requests, *tmp, *prev = NULL;
1108    reply_state *fptr;
1109    transfer_state result;
1110    mDNSs32 now = mDNS_TimeNow(&mDNSStorage);
1111
1112    while(req)
1113        {
1114        result = t_uninitialized;
1115        if (req->u_err)
1116            result = send_undelivered_error(req);
1117        if (result != t_error && result != t_morecoming &&		// don't try to send msg if send_error failed
1118            (req->ts == t_complete || req->ts == t_morecoming))
1119            {
1120            while(req->replies)
1121                {
1122                if (req->replies->next) req->replies->rhdr->flags |= dnssd_htonl(kDNSServiceFlagsMoreComing);
1123                result = send_msg(req->replies);
1124                if (result == t_complete)
1125                    {
1126                    fptr = req->replies;
1127                    req->replies = req->replies->next;
1128                    freeL("udsserver_idle", fptr);
1129                    req->time_blocked = 0;                              // reset failure counter after successful send
1130                    }
1131                else if (result == t_terminated || result == t_error)
1132                    {
1133                    abort_request(req);
1134                    break;
1135                    }
1136                else if (result == t_morecoming) break;	   		// client's queues are full, move to next
1137                }
1138            }
1139        if (result == t_morecoming)
1140			{
1141			if (!req->time_blocked) req->time_blocked = now;
1142			debugf("udsserver_idle: client has been blocked for %ld seconds", (now - req->time_blocked) / mDNSPlatformOneSecond);
1143			if (now - req->time_blocked >= MAX_TIME_BLOCKED)
1144				{
1145				LogMsg("Could not write data to client %d after %ld seconds - aborting connection", req->sd, MAX_TIME_BLOCKED / mDNSPlatformOneSecond);
1146				LogClientInfo(req);
1147				abort_request(req);
1148				result = t_terminated;
1149				}
1150			else if (nextevent - now > mDNSPlatformOneSecond) nextevent = now + mDNSPlatformOneSecond;  // try again in a second
1151			}
1152        if (result == t_terminated || result == t_error)
1153        //since we're already doing a list traversal, we unlink the request manually instead of calling unlink_request()
1154            {
1155            tmp = req;
1156            if (prev) prev->next = req->next;
1157            if (req == all_requests) all_requests = all_requests->next;
1158            req = req->next;
1159            freeL("udsserver_idle", tmp);
1160            }
1161        else
1162            {
1163            prev = req;
1164            req = req->next;
1165            }
1166        }
1167    return nextevent;
1168    }
1169
1170mDNSexport void udsserver_info(mDNS *const m)
1171    {
1172	mDNSs32 now = mDNS_TimeNow(m);
1173	mDNSu32 CacheUsed = 0, CacheActive = 0;
1174	mDNSu32 slot;
1175	CacheGroup *cg;
1176	CacheRecord *rr;
1177    request_state *req;
1178
1179    LogMsgNoIdent("Timenow 0x%08lX (%ld)", (mDNSu32)now, now);
1180
1181    LogMsgNoIdent("Slt Q   TTL U Type  if     len rdata");
1182	for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
1183		for(cg = m->rrcache_hash[slot]; cg; cg=cg->next)
1184			{
1185			CacheUsed++;	// Count one cache entity for the CacheGroup object
1186			for (rr = cg->members; rr; rr=rr->next)
1187				{
1188				mDNSs32 remain = rr->resrec.rroriginalttl - (now - rr->TimeRcvd) / mDNSPlatformOneSecond;
1189				CacheUsed++;
1190				if (rr->CRActiveQuestion) CacheActive++;
1191				LogMsgNoIdent("%3d %s%6ld %s %-6s%-6s%s",
1192					slot,
1193					rr->CRActiveQuestion ? "*" : " ",
1194					remain,
1195					(rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "-" : " ",
1196					DNSTypeName(rr->resrec.rrtype),
1197					((NetworkInterfaceInfo *)rr->resrec.InterfaceID)->ifname,
1198					CRDisplayString(m, rr));
1199				usleep(1000);	// Limit rate a little so we don't flood syslog too fast
1200				}
1201			}
1202
1203	if (m->rrcache_totalused != CacheUsed)
1204		LogMsgNoIdent("Cache use mismatch: rrcache_totalused is %lu, true count %lu", m->rrcache_totalused, CacheUsed);
1205	if (m->rrcache_active != CacheActive)
1206		LogMsgNoIdent("Cache use mismatch: rrcache_active is %lu, true count %lu", m->rrcache_active, CacheActive);
1207	LogMsgNoIdent("Cache currently contains %lu records; %lu referenced by active questions", CacheUsed, CacheActive);
1208
1209    for (req = all_requests; req; req=req->next)
1210		LogClientInfo(req);
1211
1212    now = mDNS_TimeNow(m);
1213    LogMsgNoIdent("Timenow 0x%08lX (%ld)", (mDNSu32)now, now);
1214    }
1215
1216#if __MACOSX__ && MACOSX_MDNS_MALLOC_DEBUGGING
1217mDNSexport void uds_validatelists(void)
1218	{
1219	request_state *req;
1220	for (req = all_requests; req; req=req->next)
1221		if (req->sd < 0 && req->sd != -2)
1222			LogMemCorruption("UDS request list: %p is garbage (%X)", req, req->sd);
1223	}
1224#endif
1225
1226mDNSlocal void rename_service(service_instance *srv)
1227	{
1228	if (srv->autoname && !SameDomainLabel(srv->name.c, gmDNS->nicelabel.c))
1229		{
1230		srv->rename_on_memfree = 1;
1231		if (mDNS_DeregisterService(gmDNS, &srv->srs))	// If service deregistered already, we can re-register immediately
1232			regservice_callback(gmDNS, &srv->srs, mStatus_MemFree);
1233		}
1234	}
1235
1236mDNSexport void udsserver_handle_configchange(void)
1237    {
1238    request_state *req;
1239
1240    for (req = all_requests; req; req = req->next)
1241        {
1242		if (req->service_registration)
1243			{
1244			service_instance *ptr;
1245			for (ptr = req->service_registration->instances; ptr; ptr = ptr->next)
1246				rename_service(ptr);
1247			}
1248		}
1249    }
1250
1251mDNSlocal void connect_callback(void *info)
1252    {
1253    dnssd_sock_t sd;
1254	dnssd_socklen_t len;
1255	unsigned long optval;
1256    dnssd_sockaddr_t cliaddr;
1257    request_state *rstate;
1258    (void)info; // Unused
1259
1260	len = (dnssd_socklen_t) sizeof(cliaddr);
1261
1262	sd = accept(listenfd, (struct sockaddr*) &cliaddr, &len);
1263
1264    if (sd == dnssd_InvalidSocket)
1265        {
1266        if (dnssd_errno() == dnssd_EWOULDBLOCK) return;
1267        my_perror("ERROR: accept");
1268        return;
1269    	}
1270    optval = 1;
1271
1272#ifdef SO_NOSIGPIPE
1273	// Some environments (e.g. OS X) support turning off SIGPIPE for a socket
1274    if (setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0)
1275    	{
1276        my_perror("ERROR: setsockopt - SO_NOSIGPIPE - aborting client");
1277        dnssd_close(sd);
1278        return;
1279    	}
1280#endif
1281
1282#if defined(_WIN32)
1283	if (ioctlsocket(sd, FIONBIO, &optval) != 0)
1284#else
1285	if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
1286#endif
1287        {
1288		my_perror("ERROR: fcntl(sd, F_SETFL, O_NONBLOCK) - aborting client");
1289		dnssd_close(sd);
1290		return;
1291		}
1292
1293	// allocate a request_state struct that will live with the socket
1294    rstate = mallocL("connect_callback", sizeof(request_state));
1295    if (!rstate) FatalError("ERROR: malloc");
1296    bzero(rstate, sizeof(request_state));
1297    rstate->ts = t_morecoming;
1298    rstate->sd = sd;
1299
1300	LogOperation("%3d: Adding FD", rstate->sd);
1301    if ( mStatus_NoError != udsSupportAddFDToEventLoop( sd, request_callback, rstate))
1302        return;
1303    rstate->next = all_requests;
1304    all_requests = rstate;
1305    }
1306
1307// handler
1308mDNSlocal void request_callback(void *info)
1309	{
1310	request_state *rstate = info;
1311	transfer_state result;
1312	dnssd_sockaddr_t cliaddr;
1313	int dedicated_error_socket;
1314#if defined(_WIN32)
1315	u_long opt = 1;
1316#endif
1317
1318	result = read_msg(rstate);
1319	if (result == t_morecoming)
1320		{
1321		return;
1322		}
1323	if (result == t_terminated)
1324		{
1325		abort_request(rstate);
1326		unlink_request(rstate);
1327		return;
1328		}
1329	if (result == t_error)
1330		{
1331		abort_request(rstate);
1332		unlink_request(rstate);
1333		return;
1334		}
1335
1336	if (rstate->hdr.version != VERSION)
1337		{
1338		LogMsg("ERROR: client incompatible with daemon (client version = %d, "
1339		       "daemon version = %d)\n", rstate->hdr.version, VERSION);
1340		abort_request(rstate);
1341		unlink_request(rstate);
1342		return;
1343		}
1344
1345	if (validate_message(rstate) < 0)
1346		{
1347		// note that we cannot deliver an error message if validation fails, since the path to the error socket
1348		// may be contained in the (invalid) message body for some message types
1349		abort_request(rstate);
1350		unlink_request(rstate);
1351		LogMsg("Invalid message sent by client - may indicate a malicious program running on this machine!");
1352		return;
1353		}
1354
1355	// check if client wants silent operation
1356	if (rstate->hdr.flags & IPC_FLAGS_NOREPLY) rstate->no_reply = 1;
1357
1358	dedicated_error_socket = (rstate->hdr.op == reg_record_request    || rstate->hdr.op == add_record_request ||
1359	                          rstate->hdr.op == update_record_request || rstate->hdr.op == remove_record_request);
1360
1361	if (((rstate->hdr.flags & IPC_FLAGS_REUSE_SOCKET) == 0) != dedicated_error_socket)
1362		LogMsg("WARNING: client request %d with incorrect flags setting 0x%X", rstate->hdr.op, rstate->hdr.flags);
1363
1364	// check if primary socket is to be used for synchronous errors, else open new socket
1365	if (dedicated_error_socket)
1366		{
1367		mStatus err = 0;
1368		int nwritten;
1369		dnssd_sock_t errfd = socket(AF_DNSSD, SOCK_STREAM, 0);
1370		if (errfd == dnssd_InvalidSocket)
1371			{
1372			my_perror("ERROR: socket");
1373			abort_request(rstate);
1374			unlink_request(rstate);
1375			return;
1376			}
1377
1378		//LogOperation("request_callback: Opened dedicated errfd %d", errfd);
1379
1380		#if defined(USE_TCP_LOOPBACK)
1381			{
1382			mDNSOpaque16 port;
1383			port.b[0] = rstate->msgdata[0];
1384			port.b[1] = rstate->msgdata[1];
1385			rstate->msgdata += 2;
1386			cliaddr.sin_family      = AF_INET;
1387			cliaddr.sin_port        = port.NotAnInteger;
1388			cliaddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR);
1389			}
1390		#else
1391			{
1392			char ctrl_path[MAX_CTLPATH];
1393			get_string(&rstate->msgdata, ctrl_path, 256);	// path is first element in message buffer
1394			bzero(&cliaddr, sizeof(cliaddr));
1395			cliaddr.sun_family = AF_LOCAL;
1396			strcpy(cliaddr.sun_path, ctrl_path);
1397			}
1398		#endif
1399		//LogOperation("request_callback: Connecting to ���%s���", cliaddr.sun_path);
1400		if (connect(errfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)) < 0)
1401			{
1402			//LogOperation("request_callback: Couldn't connect to ���%s���", cliaddr.sun_path);
1403			my_perror("ERROR: connect");
1404			abort_request(rstate);
1405			unlink_request(rstate);
1406			return;
1407			}
1408#if defined(_WIN32)
1409		if (ioctlsocket(errfd, FIONBIO, &opt) != 0)
1410#else
1411		if (fcntl(errfd, F_SETFL, O_NONBLOCK) != 0)
1412#endif
1413			{
1414			my_perror("ERROR: could not set control socket to non-blocking mode");
1415			abort_request(rstate);
1416			unlink_request(rstate);
1417			return;
1418			}
1419
1420		switch(rstate->hdr.op)
1421			{
1422			case reg_record_request:    err = handle_regrecord_request   (rstate); break;
1423			case add_record_request:    err = handle_add_request         (rstate); break;
1424			case update_record_request: err = handle_update_request      (rstate); break;
1425			case remove_record_request: err = handle_removerecord_request(rstate); break;
1426			default: LogMsg("%3d: ERROR: udsserver_recv_request - unsupported request type: %d", rstate->sd, rstate->hdr.op);
1427			}
1428
1429		//LogOperation("request_callback: Returning error code %d on socket %d", err, errfd);
1430		err = dnssd_htonl(err);
1431		nwritten = send(errfd, (dnssd_sockbuf_t) &err, sizeof(err), 0);
1432		// On a freshly-created Unix Domain Socket, the kernel should *never* fail to buffer a four-byte write for us.
1433		// If not, we don't attempt to handle this failure, but we do log it.
1434		if (nwritten < (int)sizeof(err))
1435			LogMsg("ERROR: failed to write error response back to caller: %d %d %s",
1436				nwritten, dnssd_errno(), dnssd_strerror(dnssd_errno()));
1437		//else LogOperation("request_callback: Returned  error code %d on socket %d", err, errfd);
1438		dnssd_close(errfd);
1439		//LogOperation("request_callback: Closed errfd %d", errfd);
1440		reset_connected_rstate(rstate);		// Reset ready to accept the next request on this pipe
1441		}
1442	else
1443		{
1444		switch(rstate->hdr.op)
1445			{
1446			case resolve_request:          handle_resolve_request   (rstate); break;
1447			case query_request:            handle_query_request     (rstate); break;
1448			case browse_request:           handle_browse_request    (rstate); break;
1449			case reg_service_request:      handle_regservice_request(rstate); break;
1450			case enumeration_request:      handle_enum_request      (rstate); break;
1451			case reconfirm_record_request: handle_reconfirm_request (rstate); break;
1452			case setdomain_request:        handle_setdomain_request (rstate); break;
1453			default: LogMsg("%3d: ERROR: udsserver_recv_request - unsupported request type: %d", rstate->sd, rstate->hdr.op);
1454			}
1455		}
1456	}
1457
1458// mDNS operation functions.  Each operation has 3 associated functions - a request handler that parses
1459// the client's request and makes the appropriate mDNSCore call, a result handler (passed as a callback
1460// to the mDNSCore routine) that sends results back to the client, and a termination routine that aborts
1461// the mDNSCore operation if the client dies or closes its socket.
1462
1463// query and resolve calls have separate request handlers that parse the arguments from the client and
1464// massage the name parameters appropriately, but the rest of the operations (making the query call,
1465// delivering the result to the client, and termination) are identical.
1466
1467mDNSlocal void handle_query_request(request_state *rstate)
1468    {
1469    DNSServiceFlags flags;
1470    uint32_t ifi;
1471    char name[256];
1472    uint16_t rrtype, rrclass;
1473    char *ptr;
1474    mStatus result;
1475    mDNSInterfaceID InterfaceID;
1476	DNSQuestion *q;
1477
1478    if (rstate->ts != t_complete)
1479        {
1480        LogMsg("ERROR: handle_query_request - transfer state != t_complete");
1481        goto error;
1482        }
1483    ptr = rstate->msgdata;
1484    if (!ptr)
1485        {
1486        LogMsg("ERROR: handle_query_request - NULL msgdata");
1487        goto error;
1488        }
1489
1490    flags = get_flags(&ptr);
1491    ifi = get_long(&ptr);
1492    if (get_string(&ptr, name, 256) < 0) goto bad_param;
1493    rrtype = get_short(&ptr);
1494    rrclass = get_short(&ptr);
1495	InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, ifi);
1496    if (ifi && !InterfaceID) goto bad_param;
1497
1498    q = mallocL("DNSQuestion", sizeof(DNSQuestion));
1499    if (!q) FatalError("ERROR: handle_query - malloc");
1500    bzero(q, sizeof(DNSQuestion));
1501
1502    q->InterfaceID      = InterfaceID;
1503    q->Target           = zeroAddr;
1504    if (!MakeDomainNameFromDNSNameString(&q->qname, name)) { freeL("DNSQuestion", q); goto bad_param; }
1505    q->qtype            = rrtype;
1506    q->qclass           = rrclass;
1507    q->LongLived        = (flags & kDNSServiceFlagsLongLivedQuery) != 0;
1508    q->ExpectUnique     = mDNSfalse;
1509    q->ForceMCast       = (flags & kDNSServiceFlagsForceMulticast) != 0;
1510    q->ReturnCNAME      = (flags & kDNSServiceFlagsReturnCNAME) != 0;
1511    q->QuestionCallback = question_result_callback;
1512    q->QuestionContext  = rstate;
1513
1514    rstate->termination_context = q;
1515    rstate->terminate = question_termination_callback;
1516
1517	LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) START", rstate->sd, q->qname.c, DNSTypeName(q->qtype));
1518    result = mDNS_StartQuery(gmDNS, q);
1519    if (result != mStatus_NoError) LogMsg("ERROR: mDNS_StartQuery: %d", (int)result);
1520
1521    if (result) rstate->terminate = NULL;
1522    if (deliver_error(rstate, result) < 0) goto error;
1523    return;
1524
1525bad_param:
1526    deliver_error(rstate, mStatus_BadParamErr);
1527    rstate->terminate = NULL;	// don't try to terminate insuccessful Core calls
1528error:
1529    abort_request(rstate);
1530    unlink_request(rstate);
1531    return;
1532    }
1533
1534mDNSlocal void handle_resolve_request(request_state *rstate)
1535    {
1536    DNSServiceFlags flags;
1537    uint32_t interfaceIndex;
1538    mDNSInterfaceID InterfaceID;
1539    char name[256], regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME];
1540    char *ptr;  // message data pointer
1541    domainname fqdn;
1542    resolve_termination_t *term;
1543    mStatus err;
1544
1545    if (rstate->ts != t_complete)
1546        {
1547        LogMsg("ERROR: handle_resolve_request - transfer state != t_complete");
1548        abort_request(rstate);
1549        unlink_request(rstate);
1550        return;
1551        }
1552
1553    // extract the data from the message
1554    ptr = rstate->msgdata;
1555    if (!ptr)
1556        {
1557        LogMsg("ERROR: handle_resolve_request - NULL msgdata");
1558        abort_request(rstate);
1559        unlink_request(rstate);
1560        return;
1561        }
1562    flags = get_flags(&ptr);
1563    interfaceIndex = get_long(&ptr);
1564    InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, interfaceIndex);
1565    if (interfaceIndex && !InterfaceID)
1566    	{ LogMsg("ERROR: handle_resolve_request - Couldn't find InterfaceID for interfaceIndex %d", interfaceIndex); goto bad_param; }
1567    if (get_string(&ptr, name, 256) < 0 ||
1568        get_string(&ptr, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
1569        get_string(&ptr, domain, MAX_ESCAPED_DOMAIN_NAME) < 0)
1570    	{ LogMsg("ERROR: handle_resolve_request - Couldn't read name/regtype/domain"); goto bad_param; }
1571
1572    // free memory in rstate since we don't need it anymore
1573    freeL("handle_resolve_request", rstate->msgbuf);
1574    rstate->msgbuf = NULL;
1575
1576    if (build_domainname_from_strings(&fqdn, name, regtype, domain) < 0)
1577    	{ LogMsg("ERROR: handle_resolve_request - Couldn't build_domainname_from_strings ���%s��� ���%s��� ���%s���", name, regtype, domain); goto bad_param; }
1578
1579    // set up termination info
1580    term = mallocL("handle_resolve_request", sizeof(resolve_termination_t));
1581    bzero(term, sizeof(*term));
1582    if (!term) FatalError("ERROR: malloc");
1583
1584    // format questions
1585    term->qsrv.InterfaceID      = InterfaceID;
1586    term->qsrv.Target           = zeroAddr;
1587    memcpy(&term->qsrv.qname, &fqdn, MAX_DOMAIN_NAME);
1588    term->qsrv.qtype            = kDNSType_SRV;
1589    term->qsrv.qclass           = kDNSClass_IN;
1590    term->qsrv.LongLived        = mDNSfalse;
1591    term->qsrv.ExpectUnique     = mDNStrue;
1592	term->qsrv.ForceMCast       = mDNSfalse;
1593    term->qsrv.QuestionCallback = resolve_result_callback;
1594    term->qsrv.QuestionContext  = rstate;
1595
1596    term->qtxt.InterfaceID      = InterfaceID;
1597    term->qtxt.Target           = zeroAddr;
1598    memcpy(&term->qtxt.qname, &fqdn, MAX_DOMAIN_NAME);
1599    term->qtxt.qtype            = kDNSType_TXT;
1600    term->qtxt.qclass           = kDNSClass_IN;
1601    term->qtxt.LongLived        = mDNSfalse;
1602    term->qtxt.ExpectUnique     = mDNStrue;
1603	term->qtxt.ForceMCast       = mDNSfalse;
1604    term->qtxt.QuestionCallback = resolve_result_callback;
1605    term->qtxt.QuestionContext  = rstate;
1606
1607    term->rstate = rstate;
1608    rstate->termination_context = term;
1609    rstate->terminate = resolve_termination_callback;
1610
1611    // ask the questions
1612	LogOperation("%3d: DNSServiceResolve(%##s) START", rstate->sd, term->qsrv.qname.c);
1613    err = mDNS_StartQuery(gmDNS, &term->qsrv);
1614    if (!err) err = mDNS_StartQuery(gmDNS, &term->qtxt);
1615
1616    if (err)
1617        {
1618        freeL("handle_resolve_request", term);
1619        rstate->terminate = NULL;  // prevent abort_request() from invoking termination callback
1620        }
1621    if (deliver_error(rstate, err) < 0 || err)
1622        {
1623        abort_request(rstate);
1624        unlink_request(rstate);
1625        }
1626    return;
1627
1628bad_param:
1629    deliver_error(rstate, mStatus_BadParamErr);
1630    abort_request(rstate);
1631    unlink_request(rstate);
1632    }
1633
1634mDNSlocal void resolve_termination_callback(void *context)
1635    {
1636    resolve_termination_t *term = context;
1637    request_state *rs;
1638
1639    if (!term)
1640        {
1641        LogMsg("ERROR: resolve_termination_callback: double termination");
1642        return;
1643        }
1644    rs = term->rstate;
1645	LogOperation("%3d: DNSServiceResolve(%##s) STOP", rs->sd, term->qtxt.qname.c);
1646
1647    mDNS_StopQuery(gmDNS, &term->qtxt);
1648    mDNS_StopQuery(gmDNS, &term->qsrv);
1649
1650    freeL("resolve_termination_callback", term);
1651    rs->termination_context = NULL;
1652    }
1653
1654mDNSlocal void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
1655	{
1656    size_t len = 0;
1657    char fullname[MAX_ESCAPED_DOMAIN_NAME], target[MAX_ESCAPED_DOMAIN_NAME];
1658    char *data;
1659    transfer_state result;
1660    reply_state *rep;
1661    request_state *rs = question->QuestionContext;
1662    resolve_termination_t *res = rs->termination_context;
1663    (void)m; // Unused
1664
1665	LogOperation("%3d: DNSServiceResolve(%##s, %s) %s %s",
1666		rs->sd, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "ADD" : "RMV", RRDisplayString(m, answer));
1667
1668    // This code used to do this trick of just keeping a copy of the pointer to
1669    // the answer record in the cache, but the unicast query code doesn't currently
1670    // put its answer records in the cache, so for now we can't do this.
1671
1672	if (!AddRecord)
1673		{
1674		// After unicast query code is updated to store its records in the common cache, use this...
1675		// if (answer->rrtype == kDNSType_SRV && res->srv == answer) res->srv = mDNSNULL;
1676		// if (answer->rrtype == kDNSType_TXT && res->txt == answer) res->txt = mDNSNULL;
1677		// intead of this...
1678		if (answer->rrtype == kDNSType_SRV && res->srv &&                                    SameRDataBody(answer, (RDataBody *)&res->srvdata))
1679			res->srv = mDNSfalse;
1680		if (answer->rrtype == kDNSType_TXT && res->txt && answer->rdlength == res->txtlen && SameRDataBody(answer, (RDataBody *)&res->txtdata))
1681			res->txt = mDNSfalse;
1682		return;
1683		}
1684
1685	// After unicast query code is updated to store its records in the common cache, use this...
1686    // if (answer->rrtype == kDNSType_SRV) res->srv = answer;
1687    // if (answer->rrtype == kDNSType_TXT) res->txt = answer;
1688	// intead of this...
1689    if (answer->rrtype == kDNSType_SRV)
1690    	{
1691    	res->srvdata = answer->rdata->u.srv;
1692    	res->srv = mDNStrue;
1693    	}
1694    if (answer->rrtype == kDNSType_TXT)
1695    	{
1696    	if (answer->rdlength > AbsoluteMaxDNSMessageData) return;
1697    	res->txtlen = answer->rdlength;
1698    	mDNSPlatformMemCopy(answer->rdata->u.data, res->txtdata, res->txtlen);
1699    	res->txt = mDNStrue;
1700    	}
1701
1702    if (!res->txt || !res->srv) return;		// only deliver result to client if we have both answers
1703
1704    ConvertDomainNameToCString(answer->name, fullname);
1705    ConvertDomainNameToCString(&res->srvdata.target, target);
1706
1707    // calculate reply length
1708    len += sizeof(DNSServiceFlags);
1709    len += sizeof(uint32_t);  // interface index
1710    len += sizeof(DNSServiceErrorType);
1711    len += strlen(fullname) + 1;
1712    len += strlen(target) + 1;
1713    len += 2 * sizeof(uint16_t);  // port, txtLen
1714    len += res->txtlen;
1715
1716    // allocate/init reply header
1717    rep =  create_reply(resolve_reply_op, len, rs);
1718    rep->rhdr->flags = dnssd_htonl(0);
1719    rep->rhdr->ifi   = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, answer->InterfaceID));
1720    rep->rhdr->error = dnssd_htonl(kDNSServiceErr_NoError);
1721
1722    data = rep->sdata;
1723
1724    // write reply data to message
1725    put_string(fullname, &data);
1726    put_string(target, &data);
1727	*data++ = res->srvdata.port.b[0];
1728	*data++ = res->srvdata.port.b[1];
1729    put_short(res->txtlen, &data);
1730    put_rdata(res->txtlen, res->txtdata, &data);
1731
1732    result = send_msg(rep);
1733    if (result == t_error || result == t_terminated)
1734        {
1735        abort_request(rs);
1736        unlink_request(rs);
1737        freeL("resolve_result_callback", rep);
1738        }
1739    else if (result == t_complete) freeL("resolve_result_callback", rep);
1740    else append_reply(rs, rep);
1741    }
1742
1743// what gets called when a resolve is completed and we need to send the data back to the client
1744mDNSlocal void question_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
1745    {
1746    char *data;
1747    char name[MAX_ESCAPED_DOMAIN_NAME];
1748    request_state *req = question->QuestionContext;
1749    reply_state *rep;
1750    size_t len;
1751    (void)m; // Unused
1752
1753	LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) RESULT %s", req->sd, question->qname.c, DNSTypeName(question->qtype), RRDisplayString(m, answer));
1754    //mDNS_StopQuery(m, question);
1755
1756	if (answer->rdlength == 0)
1757		{
1758		deliver_async_error(req, query_reply_op, kDNSServiceErr_NoSuchRecord);
1759		return;
1760		}
1761
1762    // calculate reply data length
1763    len = sizeof(DNSServiceFlags);
1764    len += 2 * sizeof(uint32_t);  // if index + ttl
1765    len += sizeof(DNSServiceErrorType);
1766    len += 3 * sizeof(uint16_t); // type, class, rdlen
1767    len += answer->rdlength;
1768    ConvertDomainNameToCString(answer->name, name);
1769    len += strlen(name) + 1;
1770
1771    rep =  create_reply(query_reply_op, len, req);
1772
1773    rep->rhdr->flags = dnssd_htonl(AddRecord ? kDNSServiceFlagsAdd : 0);
1774    rep->rhdr->ifi   = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, answer->InterfaceID));
1775    rep->rhdr->error = dnssd_htonl(kDNSServiceErr_NoError);
1776
1777    data = rep->sdata;
1778
1779    put_string(name, &data);
1780    put_short(answer->rrtype, &data);
1781    put_short(answer->rrclass, &data);
1782    put_short(answer->rdlength, &data);
1783    put_rdata(answer->rdlength, answer->rdata->u.data, &data);
1784    put_long(AddRecord ? answer->rroriginalttl : 0, &data);
1785
1786    append_reply(req, rep);
1787    return;
1788    }
1789
1790mDNSlocal void question_termination_callback(void *context)
1791    {
1792    DNSQuestion *q = context;
1793	LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) STOP", ((request_state *)q->QuestionContext)->sd, q->qname.c, DNSTypeName(q->qtype));
1794    mDNS_StopQuery(gmDNS, q);  // no need to error check
1795    freeL("question_termination_callback", q);
1796    }
1797
1798// If there's a comma followed by another character,
1799// FindFirstSubType overwrites the comma with a nul and returns the pointer to the next character.
1800// Otherwise, it returns a pointer to the final nul at the end of the string
1801mDNSlocal char *FindFirstSubType(char *p)
1802	{
1803	while (*p)
1804		{
1805		if (p[0] == '\\' && p[1]) p += 2;
1806		else if (p[0] == ',' && p[1]) { *p++ = 0; return(p); }
1807		else p++;
1808		}
1809	return(p);
1810	}
1811
1812// If there's a comma followed by another character,
1813// FindNextSubType overwrites the comma with a nul and returns the pointer to the next character.
1814// If it finds an illegal unescaped dot in the subtype name, it returns mDNSNULL
1815// Otherwise, it returns a pointer to the final nul at the end of the string
1816mDNSlocal char *FindNextSubType(char *p)
1817	{
1818	while (*p)
1819		{
1820		if (p[0] == '\\' && p[1])		// If escape character
1821			p += 2;						// ignore following character
1822		else if (p[0] == ',')			// If we found a comma
1823			{
1824			if (p[1]) *p++ = 0;
1825			return(p);
1826			}
1827		else if (p[0] == '.')
1828			return(mDNSNULL);
1829		else p++;
1830		}
1831	return(p);
1832	}
1833
1834// Returns -1 if illegal subtype found
1835mDNSexport mDNSs32 ChopSubTypes(char *regtype)
1836	{
1837	mDNSs32 NumSubTypes = 0;
1838	char *stp = FindFirstSubType(regtype);
1839	while (stp && *stp)					// If we found a comma...
1840		{
1841		if (*stp == ',') return(-1);
1842		NumSubTypes++;
1843		stp = FindNextSubType(stp);
1844		}
1845	if (!stp) return(-1);
1846	return(NumSubTypes);
1847	}
1848
1849mDNSexport AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p)
1850	{
1851	AuthRecord *st = mDNSNULL;
1852	if (NumSubTypes)
1853		{
1854		mDNSs32 i;
1855		st = mallocL("ServiceSubTypes", NumSubTypes * sizeof(AuthRecord));
1856		if (!st) return(mDNSNULL);
1857		for (i = 0; i < NumSubTypes; i++)
1858			{
1859			mDNS_SetupResourceRecord(&st[i], mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, mDNSNULL, mDNSNULL);
1860			while (*p) p++;
1861			p++;
1862			if (!MakeDomainNameFromDNSNameString(st[i].resrec.name, p))
1863				{ freeL("ServiceSubTypes", st); return(mDNSNULL); }
1864			}
1865		}
1866	return(st);
1867	}
1868
1869#ifdef _HAVE_SETDOMAIN_SUPPORT_
1870mDNSlocal void free_defdomain(mDNS *const m, AuthRecord *const rr, mStatus result)
1871	{
1872	(void)m;  // unused
1873	if (result == mStatus_MemFree) free(rr->RecordContext);  // context is the enclosing list structure
1874	}
1875#endif
1876
1877mDNSlocal void handle_setdomain_request(request_state *request)
1878	{
1879	mStatus err = mStatus_NoError;
1880	char *ptr;
1881	char domainstr[MAX_ESCAPED_DOMAIN_NAME];
1882	domainname domain;
1883	DNSServiceFlags flags;
1884#ifdef _HAVE_SETDOMAIN_SUPPORT_
1885	struct xucred xuc;
1886	socklen_t xuclen;
1887#endif
1888
1889	if (request->ts != t_complete)
1890        {
1891        LogMsg("ERROR: handle_setdomain_request - transfer state != t_complete");
1892        abort_request(request);
1893        unlink_request(request);
1894        return;
1895        }
1896
1897    // extract flags/domain from message
1898    ptr = request->msgdata;
1899    flags = get_flags(&ptr);
1900    if (get_string(&ptr, domainstr, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
1901		!MakeDomainNameFromDNSNameString(&domain, domainstr))
1902		{ err = mStatus_BadParamErr; goto end; }
1903
1904	freeL("handle_setdomain_request", request->msgbuf);
1905    request->msgbuf = NULL;
1906
1907	debugf("%3d: DNSServiceSetDefaultDomainForUser(%##s)", request->sd, domain.c);
1908
1909#ifdef _HAVE_SETDOMAIN_SUPPORT_
1910    // this functionality currently only used for Apple-specific configuration, so we don't burned other platforms by mandating
1911	// the existence of this socket option
1912	xuclen = sizeof(xuc);
1913	if (getsockopt(request->sd, 0, LOCAL_PEERCRED, &xuc, &xuclen))
1914		{ my_perror("ERROR: getsockopt, LOCAL_PEERCRED"); err = mStatus_UnknownErr; goto end; }
1915	if (xuc.cr_version != XUCRED_VERSION) { LogMsg("getsockopt, LOCAL_PEERCRED - bad version"); err = mStatus_UnknownErr; goto end; }
1916	LogMsg("Default domain %s %s for UID %d", domainstr, flags & kDNSServiceFlagsAdd ? "set" : "removed", xuc.cr_uid);
1917
1918	if (flags & kDNSServiceFlagsAdd)
1919		{
1920		// register a local-only PRT record
1921		default_browse_list_t *newelem = malloc(sizeof(default_browse_list_t));
1922		if (!newelem) { LogMsg("ERROR: malloc"); err = mStatus_NoMemoryErr; goto end; }
1923		mDNS_SetupResourceRecord(&newelem->ptr_rec, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200,  kDNSRecordTypeShared, free_defdomain, newelem);
1924		MakeDomainNameFromDNSNameString(&newelem->ptr_rec.resrec.name, mDNS_DomainTypeNames[mDNS_DomainTypeBrowseDefault]);
1925		AppendDNSNameString            (&newelem->ptr_rec.resrec.name, "local");
1926 		AssignDomainName(&newelem->ptr_rec.resrec.rdata->u.name, &domain);
1927		newelem->uid = xuc.cr_uid;
1928		err = mDNS_Register(gmDNS, &newelem->ptr_rec);
1929		if (err) free(newelem);
1930		else
1931			{
1932			// link into list
1933			newelem->next = default_browse_list;
1934			default_browse_list = newelem;
1935			}
1936
1937		}
1938	else
1939		{
1940		// remove - find in list, deregister
1941		default_browse_list_t *ptr = default_browse_list, *prev = NULL;
1942		while (ptr)
1943			{
1944			if (SameDomainName(&ptr->ptr_rec.resrec.rdata->u.name, &domain))
1945				{
1946				if (prev) prev->next = ptr->next;
1947				else default_browse_list = ptr->next;
1948				err = mDNS_Deregister(gmDNS, &ptr->ptr_rec);
1949				break;
1950				}
1951			prev = ptr;
1952			ptr = ptr->next;
1953			}
1954		if (!ptr) { LogMsg("Attempt to remove nonexistent domain %s for UID %d", domainstr, xuc.cr_uid); err = mStatus_Invalid; }
1955		}
1956#else
1957	err = mStatus_NoError;
1958#endif // _HAVE_SETDOMAIN_SUPPORT_
1959
1960	end:
1961    deliver_error(request, err);
1962    abort_request(request);
1963    unlink_request(request);
1964    }
1965
1966// Generates a response message giving name, type, domain, plus interface index,
1967// suitable for a browse result or service registration result.
1968// On successful completion rep is set to point to a malloc'd reply_state struct
1969mDNSlocal mStatus GenerateNTDResponse(domainname *servicename, mDNSInterfaceID id, request_state *request, reply_state **rep)
1970	{
1971	domainlabel name;
1972	domainname type, dom;
1973	*rep = NULL;
1974	if (!DeconstructServiceName(servicename, &name, &type, &dom))
1975		return kDNSServiceErr_Invalid;
1976	else
1977		{
1978		char namestr[MAX_DOMAIN_LABEL+1];
1979		char typestr[MAX_ESCAPED_DOMAIN_NAME];
1980		char domstr [MAX_ESCAPED_DOMAIN_NAME];
1981		int len;
1982		char *data;
1983
1984		ConvertDomainLabelToCString_unescaped(&name, namestr);
1985		ConvertDomainNameToCString(&type, typestr);
1986		ConvertDomainNameToCString(&dom, domstr);
1987
1988		// Calculate reply data length
1989		len = sizeof(DNSServiceFlags);
1990		len += sizeof(uint32_t);  // if index
1991		len += sizeof(DNSServiceErrorType);
1992		len += (int) (strlen(namestr) + 1);
1993		len += (int) (strlen(typestr) + 1);
1994		len += (int) (strlen(domstr) + 1);
1995
1996		// Build reply header
1997		*rep = create_reply(query_reply_op, len, request);
1998		(*rep)->rhdr->flags = dnssd_htonl(0);
1999		(*rep)->rhdr->ifi   = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, id));
2000		(*rep)->rhdr->error = dnssd_htonl(kDNSServiceErr_NoError);
2001
2002		// Build reply body
2003		data = (*rep)->sdata;
2004		put_string(namestr, &data);
2005		put_string(typestr, &data);
2006		put_string(domstr, &data);
2007
2008		return mStatus_NoError;
2009		}
2010	}
2011
2012mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
2013	{
2014	request_state *req = question->QuestionContext;
2015	reply_state *rep;
2016	(void)m; // Unused
2017
2018	if (answer->rrtype != kDNSType_PTR)
2019		{ LogMsg("%3d: FoundInstance: Should not be called with rrtype %d (not a PTR record)", req->sd, answer->rrtype); return; }
2020
2021	if (GenerateNTDResponse(&answer->rdata->u.name, answer->InterfaceID, req, &rep) != mStatus_NoError)
2022		{
2023		LogMsg("%3d: FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer",
2024			req->sd, answer->name->c, answer->rdata->u.name.c);
2025		return;
2026		}
2027
2028	LogOperation("%3d: DNSServiceBrowse(%##s, %s) RESULT %s %s",
2029		req->sd, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "Add" : "Rmv", RRDisplayString(m, answer));
2030
2031	if (AddRecord) rep->rhdr->flags |= dnssd_htonl(kDNSServiceFlagsAdd);
2032	append_reply(req, rep);
2033	}
2034
2035mDNSlocal mStatus add_domain_to_browser(browser_info_t *info, const domainname *d)
2036	{
2037	browser_t *b, *p;
2038	mStatus err;
2039
2040	for (p = info->browsers; p; p = p->next)
2041		{
2042		if (SameDomainName(&p->domain, d))
2043			{ debugf("add_domain_to_browser - attempt to add domain %##d already in list", d->c); return mStatus_AlreadyRegistered; }
2044		}
2045
2046	b = mallocL("browser_t", sizeof(*b));
2047	if (!b) return mStatus_NoMemoryErr;
2048	AssignDomainName(&b->domain, d);
2049	err = mDNS_StartBrowse(gmDNS, &b->q, &info->regtype, d, info->interface_id, info->ForceMCast, FoundInstance, info->rstate);
2050	if (err)
2051		{
2052		LogMsg("mDNS_StartBrowse returned %d for type %##s domain %##s", err, info->regtype.c, d->c);
2053		freeL("browser_t", b);
2054		}
2055	else
2056		{
2057		b->next = info->browsers;
2058		info->browsers = b;
2059		}
2060		return err;
2061	}
2062
2063mDNSlocal void handle_browse_request(request_state *request)
2064    {
2065    DNSServiceFlags flags;
2066    uint32_t interfaceIndex;
2067    mDNSInterfaceID InterfaceID;
2068    char regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME];
2069    domainname typedn, d, temp;
2070    mDNSs32 NumSubTypes;
2071    char *ptr;
2072    mStatus err = mStatus_NoError;
2073	DNameListElem *search_domain_list, *sdom;
2074	browser_info_t *info = NULL;
2075
2076    if (request->ts != t_complete)
2077        {
2078        LogMsg("ERROR: handle_browse_request - transfer state != t_complete");
2079        abort_request(request);
2080        unlink_request(request);
2081        return;
2082        }
2083
2084    // extract data from message
2085    ptr = request->msgdata;
2086    flags = get_flags(&ptr);
2087    interfaceIndex = get_long(&ptr);
2088    if (get_string(&ptr, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
2089        get_string(&ptr, domain, MAX_ESCAPED_DOMAIN_NAME) < 0)
2090		{ err = mStatus_BadParamErr;  goto error; }
2091    freeL("handle_browse_request", request->msgbuf);
2092    request->msgbuf = NULL;
2093
2094    InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, interfaceIndex);
2095    if (interfaceIndex && !InterfaceID) { err = mStatus_BadParamErr;  goto error; }
2096
2097#if defined(MDNS_LAZY_REGISTER_SEARCH_DOMAINS)
2098	if ( !domain || ( domain[0] == '\0' ) )
2099		{
2100		dDNS_RegisterSearchDomains( gmDNS );
2101		}
2102#endif
2103
2104	typedn.c[0] = 0;
2105	NumSubTypes = ChopSubTypes(regtype);	// Note: Modifies regtype string to remove trailing subtypes
2106	if (NumSubTypes < 0 || NumSubTypes > 1) { err = mStatus_BadParamErr;  goto error; }
2107	if (NumSubTypes == 1 && !AppendDNSNameString(&typedn, regtype + strlen(regtype) + 1))
2108		{ err = mStatus_BadParamErr;  goto error; }
2109
2110    if (!regtype[0] || !AppendDNSNameString(&typedn, regtype)) { err = mStatus_BadParamErr;  goto error; }
2111
2112	if (!MakeDomainNameFromDNSNameString(&temp, regtype)) { err = mStatus_BadParamErr;  goto error; }
2113	if (temp.c[0] > 15 && domain[0] == 0) strcpy(domain, "local."); // For over-long service types, we only allow domain "local"
2114
2115	// allocate and set up browser info
2116	info = mallocL("browser_info_t", sizeof(*info));
2117	if (!info) { err = mStatus_NoMemoryErr; goto error; }
2118
2119	request->browser_info = info;
2120	info->ForceMCast = (flags & kDNSServiceFlagsForceMulticast) != 0;
2121	info->interface_id = InterfaceID;
2122	AssignDomainName(&info->regtype, &typedn);
2123	info->rstate = request;
2124	info->default_domain = !domain[0];
2125	info->browsers = NULL;
2126
2127	// setup termination context
2128	request->termination_context = info;
2129    request->terminate = browse_termination_callback;
2130
2131	LogOperation("%3d: DNSServiceBrowse(\"%##s\", \"%s\") START", request->sd, info->regtype.c, domain);
2132	if (domain[0])
2133		{
2134		if (!MakeDomainNameFromDNSNameString(&d, domain)) { err = mStatus_BadParamErr;  goto error; }
2135		err = add_domain_to_browser(info, &d);
2136		}
2137
2138	else
2139		{
2140		search_domain_list = mDNSPlatformGetSearchDomainList();
2141		for (sdom = search_domain_list; sdom; sdom = sdom->next)
2142			{
2143			err = add_domain_to_browser(info, &sdom->name);
2144			if (err)
2145				{
2146				if (SameDomainName(&sdom->name, &localdomain)) break;
2147				else err = mStatus_NoError;  // suppress errors for non-local "default" domains
2148				}
2149
2150			}
2151		mDNS_FreeDNameList(search_domain_list);
2152		}
2153
2154	deliver_error(request, err);
2155	return;
2156
2157error:
2158	if (info) freeL("browser_info_t", info);
2159	if (request->termination_context) request->termination_context = NULL;
2160    deliver_error(request, err);
2161    abort_request(request);
2162    unlink_request(request);
2163    }
2164
2165mDNSlocal void browse_termination_callback(void *context)
2166    {
2167	browser_info_t *info = context;
2168	browser_t *ptr;
2169
2170	if (!info) return;
2171
2172	while(info->browsers)
2173		{
2174		ptr = info->browsers;
2175		info->browsers = ptr->next;
2176		LogOperation("%3d: DNSServiceBrowse(%##s) STOP", info->rstate->sd, ptr->q.qname.c);
2177		mDNS_StopBrowse(gmDNS, &ptr->q);  // no need to error-check result
2178		freeL("browse_termination_callback", ptr);
2179		}
2180
2181	info->rstate->termination_context = NULL;
2182	freeL("browser_info", info);
2183	}
2184
2185mDNSexport void udsserver_default_browse_domain_changed(const domainname *d, mDNSBool add)
2186	{
2187	request_state *r;
2188
2189  	for (r = all_requests; r; r = r->next)
2190		{
2191		browser_info_t *info = r->browser_info;
2192
2193		if (!info || !info->default_domain) continue;
2194		if (add) add_domain_to_browser(info, d);
2195		else
2196			{
2197			browser_t **ptr = &info->browsers;
2198			while (*ptr)
2199				{
2200				if (SameDomainName(&(*ptr)->domain, d))
2201					{
2202					browser_t *remove = *ptr;
2203					*ptr = (*ptr)->next;
2204					if (remove->q.LongLived)
2205						{
2206						// Give goodbyes for known answers.
2207						// Note that this a special case where we know that the QuestionCallback function is our own
2208						// code (it's FoundInstance), and that callback routine doesn't ever cancel its operation, so we
2209						// don't need to guard against the question being cancelled mid-loop the way the mDNSCore routines do.
2210						CacheRecord *ka = remove->q.uDNS_info.knownAnswers;
2211						while (ka) { remove->q.QuestionCallback(gmDNS, &remove->q, &ka->resrec, mDNSfalse); ka = ka->next; }
2212						}
2213					mDNS_StopBrowse(gmDNS, &remove->q);
2214					freeL("browser_t", remove);
2215					return;
2216					}
2217				ptr = &(*ptr)->next;
2218				}
2219			LogMsg("Requested removal of default domain %##s not in list for sd %d", d->c, r->sd);
2220			}
2221		}
2222	}
2223
2224// Count how many other service records we have locally with the same name, but different rdata.
2225// For auto-named services, we can have at most one per machine -- if we allowed two auto-named services of
2226// the same type on the same machine, we'd get into an infinite autoimmune-response loop of continuous renaming.
2227mDNSexport int CountPeerRegistrations(mDNS *const m, ServiceRecordSet *const srs)
2228	{
2229	int count = 0;
2230	ResourceRecord *r = &srs->RR_SRV.resrec;
2231	AuthRecord *rr;
2232	ServiceRecordSet *s;
2233
2234	for (rr = m->ResourceRecords; rr; rr=rr->next)
2235		if (rr->resrec.rrtype == kDNSType_SRV && SameDomainName(rr->resrec.name, r->name) && !SameRData(&rr->resrec, r))
2236			count++;
2237
2238	for (rr = m->uDNS_info.RecordRegistrations; rr; rr=rr->next)
2239		if (rr->uDNS_info.state != regState_Unregistered && rr->resrec.rrtype == kDNSType_SRV && SameDomainName(rr->resrec.name, r->name) && !SameRData(&rr->resrec, r))
2240			count++;
2241
2242	for (s = m->uDNS_info.ServiceRegistrations; s; s = s->next)
2243		if (s->uDNS_info.state != regState_Unregistered && SameDomainName(s->RR_SRV.resrec.name, r->name) && !SameRData(&s->RR_SRV.resrec, r))
2244			count++;
2245
2246	verbosedebugf("%d peer registrations for %##s", count, r->name->c);
2247	return(count);
2248	}
2249
2250mDNSexport int CountExistingRegistrations(domainname *srv, mDNSIPPort port)
2251	{
2252	int count = 0;
2253	AuthRecord *rr;
2254	for (rr = gmDNS->ResourceRecords; rr; rr=rr->next)
2255		if (rr->resrec.rrtype == kDNSType_SRV &&
2256			rr->resrec.rdata->u.srv.port.NotAnInteger == port.NotAnInteger &&
2257			SameDomainName(rr->resrec.name, srv))
2258			count++;
2259	return(count);
2260	}
2261
2262mDNSlocal mStatus register_service_instance(request_state *request, const domainname *domain)
2263	{
2264	service_info *info = request->service_registration;
2265	service_instance *ptr, *instance;
2266    int instance_size;
2267	mStatus result;
2268
2269	for (ptr = info->instances; ptr; ptr = ptr->next)
2270		{
2271		if (SameDomainName(&ptr->domain, domain))
2272			{ LogMsg("register_service_instance: domain %##s already registered", domain->c); return mStatus_AlreadyRegistered; }
2273		}
2274
2275	instance_size = sizeof(*instance);
2276	if (info->txtlen > sizeof(RDataBody)) instance_size += (info->txtlen - sizeof(RDataBody));
2277	instance = mallocL("service_instance", instance_size);
2278	if (!instance) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; }
2279
2280	instance->subtypes = AllocateSubTypes(info->num_subtypes, info->type_as_string);
2281	if (info->num_subtypes && !instance->subtypes)
2282		{ free_service_instance(instance); instance = NULL; FatalError("ERROR: malloc"); }
2283    instance->request           = request;
2284	instance->sd                = request->sd;
2285    instance->autoname          = info->autoname;
2286    instance->autorename        = info->autorename;
2287    instance->allowremotequery  = info->allowremotequery;
2288    instance->rename_on_memfree = 0;
2289	instance->name              = info->name;
2290	AssignDomainName(&instance->domain, domain);
2291	instance->default_local = (info->default_domain && SameDomainName(domain, &localdomain));
2292    result = mDNS_RegisterService(gmDNS, &instance->srs, &instance->name, &info->type, domain, info->host.c[0] ? &info->host : NULL, info->port,
2293								  info->txtdata, info->txtlen, instance->subtypes, info->num_subtypes, info->InterfaceID, regservice_callback, instance);
2294
2295	if (result) free_service_instance(instance);
2296	else
2297		{
2298		instance->next = info->instances;
2299		info->instances = instance;
2300		}
2301	return result;
2302	}
2303
2304mDNSexport void udsserver_default_reg_domain_changed(const domainname *d, mDNSBool add)
2305	{
2306	request_state *rstate;
2307	service_info *info;
2308
2309	LogMsg("%s registration domain %##s", add ? "Adding" : "Removing", d->c);
2310	for (rstate = all_requests; rstate; rstate = rstate->next)
2311		{
2312		if (rstate->terminate != regservice_termination_callback) continue;
2313		info = rstate->service_registration;
2314		if (!info) { LogMsg("udsserver_default_reg_domain_changed - NULL service info"); continue; } // this should never happen
2315		if (!info->default_domain)  continue;
2316
2317		// valid default registration
2318		if (add) register_service_instance(rstate, d);
2319		else
2320			{
2321			// find the instance to remove
2322			service_instance *si = rstate->service_registration->instances, *prev = NULL;
2323			while (si)
2324				{
2325				if (SameDomainName(&si->domain, d))
2326					{
2327					mStatus err;
2328					if (prev) prev->next = si->next;
2329					else info->instances = si->next;
2330					err = mDNS_DeregisterService(gmDNS, &si->srs);
2331					if (err)
2332						{
2333						LogMsg("udsserver_default_reg_domain_changed - mDNS_DeregisterService err %d", err);
2334						free_service_instance(si);
2335						}
2336					break;
2337					}
2338				prev = si;
2339				si = si->next;
2340				}
2341			if (!si) debugf("udsserver_default_reg_domain_changed - domain %##s not registered", d->c); // normal if registration failed
2342			}
2343		}
2344	}
2345
2346// service registration
2347mDNSlocal void handle_regservice_request(request_state *request)
2348    {
2349    DNSServiceFlags flags;
2350    uint32_t ifi;
2351    char name[1024];	// Lots of spare space for extra-long names that we'll auto-truncate down to 63 bytes
2352    char domain[MAX_ESCAPED_DOMAIN_NAME], host[MAX_ESCAPED_DOMAIN_NAME];
2353    char *ptr;
2354    domainname d, srv;
2355    mStatus result;
2356	service_info *service = NULL;
2357
2358	if (request->ts != t_complete)
2359        {
2360        LogMsg("ERROR: handle_regservice_request - transfer state != t_complete");
2361        abort_request(request);
2362        unlink_request(request);
2363        return;
2364        }
2365
2366	service = mallocL("service_info", sizeof(*service));
2367	if (!service) { my_perror("ERROR: malloc"); result = mStatus_NoMemoryErr; goto finish; }
2368
2369	service->instances = NULL;
2370	service->request = request;
2371	service->txtlen  = 0;
2372	service->txtdata = NULL;
2373	request->service_registration = service;
2374    request->termination_context = request->service_registration;
2375    request->terminate = regservice_termination_callback;
2376
2377    // extract data from message
2378    ptr = request->msgdata;
2379    flags = get_flags(&ptr);
2380    ifi = get_long(&ptr);
2381    service->InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, ifi);
2382    if (ifi && !service->InterfaceID)
2383    	{ LogMsg("ERROR: handle_regservice_request - Couldn't find InterfaceID for interfaceIndex %d", ifi); goto bad_param; }
2384    if (get_string(&ptr, name, sizeof(name)) < 0 ||
2385        get_string(&ptr, service->type_as_string, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
2386        get_string(&ptr, domain, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
2387        get_string(&ptr, host, MAX_ESCAPED_DOMAIN_NAME) < 0)
2388    	{ LogMsg("ERROR: handle_regservice_request - Couldn't read name/regtype/domain"); goto bad_param; }
2389
2390	service->port.b[0] = *ptr++;
2391	service->port.b[1] = *ptr++;
2392
2393    service->txtlen  = get_short(&ptr);
2394	if (service->txtlen)
2395		{
2396		service->txtdata = mallocL("txtdata", service->txtlen);
2397		if (!service->txtdata) { my_perror("ERROR: malloc"); result = mStatus_NoMemoryErr; goto finish; }
2398		memcpy(service->txtdata, get_rdata(&ptr, service->txtlen), service->txtlen);
2399		}
2400	else service->txtdata = NULL;
2401
2402	// Check for sub-types after the service type
2403	service->num_subtypes = ChopSubTypes(service->type_as_string);	// Note: Modifies regtype string to remove trailing subtypes
2404	if (service->num_subtypes < 0)
2405    	{ LogMsg("ERROR: handle_regservice_request - ChopSubTypes failed %s", service->type_as_string); goto bad_param; }
2406
2407	// Don't try to construct "domainname t" until *after* ChopSubTypes has worked its magic
2408    if (!*service->type_as_string || !MakeDomainNameFromDNSNameString(&service->type, service->type_as_string))
2409    	{ LogMsg("ERROR: handle_regservice_request - service->type_as_string bad %s", service->type_as_string); goto bad_param; }
2410
2411    if (!name[0])
2412		{
2413		service->name = (gmDNS)->nicelabel;
2414		service->autoname = mDNStrue;
2415		}
2416    else
2417		{
2418		// If the client is allowing AutoRename, then truncate name to legal length before converting it to a DomainLabel
2419		if ((flags & kDNSServiceFlagsNoAutoRename) == 0)
2420			{
2421			int newlen = TruncateUTF8ToLength((mDNSu8*)name, mDNSPlatformStrLen(name), MAX_DOMAIN_LABEL);
2422			name[newlen] = 0;
2423			}
2424		if (!MakeDomainLabelFromLiteralString(&service->name, name))
2425			{ LogMsg("ERROR: handle_regservice_request - name bad %s", name); goto bad_param; }
2426		service->autoname = mDNSfalse;
2427		}
2428
2429	if (*domain)
2430		{
2431		service->default_domain = mDNSfalse;
2432		if (!MakeDomainNameFromDNSNameString(&d, domain))
2433			{ LogMsg("ERROR: handle_regservice_request - domain bad %s", domain); goto bad_param; }
2434		}
2435	else
2436		{
2437		service->default_domain = mDNStrue;
2438		MakeDomainNameFromDNSNameString(&d, "local.");
2439		}
2440
2441	if (!ConstructServiceName(&srv, &service->name, &service->type, &d))
2442		{ LogMsg("ERROR: handle_regservice_request - Couldn't ConstructServiceName from, ���%#s��� ���%##s��� ���%##s���", service->name.c, service->type.c, d.c); goto bad_param; }
2443
2444	if (!MakeDomainNameFromDNSNameString(&service->host, host))
2445		{ LogMsg("ERROR: handle_regservice_request - host bad %s", host); goto bad_param; }
2446	service->autorename       = (flags & kDNSServiceFlagsNoAutoRename    ) == 0;
2447	service->allowremotequery = (flags & kDNSServiceFlagsAllowRemoteQuery) != 0;
2448
2449	// Some clients use mDNS for lightweight copy protection, registering a pseudo-service with
2450	// a port number of zero. When two instances of the protected client are allowed to run on one
2451	// machine, we don't want to see misleading "Bogus client" messages in syslog and the console.
2452	if (service->port.NotAnInteger)
2453		{
2454		int count = CountExistingRegistrations(&srv, service->port);
2455		if (count)
2456			LogMsg("Client application registered %d identical instances of service %##s port %u.",
2457				count+1, srv.c, mDNSVal16(service->port));
2458		}
2459
2460	LogOperation("%3d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", \"%s\", %u) START",
2461		request->sd, name, service->type_as_string, domain, host, mDNSVal16(service->port));
2462	result = register_service_instance(request, &d);
2463
2464	if (!result && !*domain)
2465		{
2466		DNameListElem *ptr, *def_domains = mDNSPlatformGetRegDomainList();
2467		for (ptr = def_domains; ptr; ptr = ptr->next)
2468			register_service_instance(request, &ptr->name);
2469		    // note that we don't report errors for non-local, non-explicit domains
2470		mDNS_FreeDNameList(def_domains);
2471		}
2472
2473finish:
2474    deliver_error(request, result);
2475    if (result != mStatus_NoError)
2476        {
2477        abort_request(request);
2478        unlink_request(request);
2479        }
2480    else
2481        reset_connected_rstate(request);  // prepare to receive add/remove messages
2482
2483    return;
2484
2485bad_param:
2486	//if (service) freeL("service_info", service);	Don't think we should do this -- abort_request will free it a second time and crash
2487    deliver_error(request, mStatus_BadParamErr);
2488    abort_request(request);
2489    unlink_request(request);
2490    }
2491
2492// service registration callback performs three duties - frees memory for deregistered services,
2493// handles name conflicts, and delivers completed registration information to the client (via
2494// process_service_registraion())
2495
2496mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result)
2497    {
2498    mStatus err;
2499	mDNSBool SuppressError = mDNSfalse;
2500    service_instance *instance = srs->ServiceContext;
2501    (void)m; // Unused
2502    if (!srs)      { LogMsg("regservice_callback: srs is NULL %d",                 result); return; }
2503    if (!instance) { LogMsg("regservice_callback: srs->ServiceContext is NULL %d", result); return; }
2504
2505	if (instance->request && instance->request->service_registration)
2506		{
2507		service_info *info = instance->request->service_registration;
2508		if (info->default_domain && !instance->default_local) SuppressError = mDNStrue;
2509        // don't send errors up to client for wide-area, empty-string registrations
2510		}
2511
2512    if (result == mStatus_NoError)
2513		LogOperation("%3d: DNSServiceRegister(%##s, %u) REGISTERED  ",  instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port));
2514	else if (result == mStatus_MemFree)
2515		LogOperation("%3d: DNSServiceRegister(%##s, %u) DEREGISTERED",  instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port));
2516	else if (result == mStatus_NameConflict)
2517		LogOperation("%3d: DNSServiceRegister(%##s, %u) NAME CONFLICT", instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port));
2518	else
2519		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);
2520
2521    if (result == mStatus_NoError)
2522		{
2523		request_state *req = instance->request;
2524		if (instance->allowremotequery)
2525			{
2526			ExtraResourceRecord *e;
2527			srs->RR_ADV.AllowRemoteQuery = mDNStrue;
2528			srs->RR_PTR.AllowRemoteQuery = mDNStrue;
2529			srs->RR_SRV.AllowRemoteQuery = mDNStrue;
2530			srs->RR_TXT.AllowRemoteQuery = mDNStrue;
2531			for (e = instance->srs.Extras; e; e = e->next) e->r.AllowRemoteQuery = mDNStrue;
2532			}
2533
2534		if (!req) LogMsg("ERROR: regservice_callback - null request object");
2535		else
2536			{
2537			reply_state *rep;
2538			if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, req, &rep) != mStatus_NoError)
2539				LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", req->sd, srs->RR_SRV.resrec.name->c);
2540			else
2541				{
2542				transfer_state send_result = send_msg(rep);
2543				if (send_result == t_error || send_result == t_terminated)
2544					{ abort_request(req); unlink_request(req); freeL("reply_state", rep); }
2545				else if (send_result == t_complete) freeL("regservice_callback", rep);
2546				else append_reply(req, rep);
2547				}
2548			}
2549        if (instance->autoname && CountPeerRegistrations(m, srs) == 0)
2550        	RecordUpdatedNiceLabel(m, 0);	// Successfully got new name, tell user immediately
2551		return;
2552		}
2553    else if (result == mStatus_MemFree)
2554        {
2555        if (instance->rename_on_memfree)
2556            {
2557            instance->rename_on_memfree = 0;
2558            instance->name = gmDNS->nicelabel;
2559            err = mDNS_RenameAndReregisterService(gmDNS, srs, &instance->name);
2560            if (err) LogMsg("ERROR: regservice_callback - RenameAndReregisterService returned %ld", err);
2561            // error should never happen - safest to log and continue
2562            }
2563        else
2564            {
2565			free_service_instance(instance);
2566            return;
2567            }
2568        }
2569    else if (result == mStatus_NameConflict)
2570    	{
2571        if (instance->autoname && CountPeerRegistrations(m, srs) == 0)
2572        	{
2573        	// On conflict for an autoname service, rename and reregister *all* autoname services
2574			IncrementLabelSuffix(&m->nicelabel, mDNStrue);
2575			m->MainCallback(m, mStatus_ConfigChanged);
2576        	}
2577        else if (instance->autoname || instance->autorename)
2578            {
2579            mDNS_RenameAndReregisterService(gmDNS, srs, mDNSNULL);
2580            return;
2581            }
2582        else
2583            {
2584		    request_state *rs = instance->request;
2585			if (!rs) { LogMsg("ERROR: regservice_callback: received result %ld with a NULL request pointer", result); return; }
2586			free_service_instance(instance);
2587			if (!SuppressError && deliver_async_error(rs, reg_service_reply_op, result) < 0)
2588                {
2589                abort_request(rs);
2590                unlink_request(rs);
2591                }
2592            return;
2593            }
2594    	}
2595    else
2596        {
2597		request_state *rs = instance->request;
2598		if (!rs) { LogMsg("ERROR: regservice_callback: received result %ld with a NULL request pointer", result); return; }
2599        if (result != mStatus_NATTraversal) LogMsg("ERROR: unknown result in regservice_callback: %ld", result);
2600		free_service_instance(instance);
2601        if (!SuppressError && deliver_async_error(rs, reg_service_reply_op, result) < 0)
2602            {
2603            abort_request(rs);
2604            unlink_request(rs);
2605            }
2606        return;
2607        }
2608    }
2609
2610mDNSexport void FreeExtraRR(mDNS *const m, AuthRecord *const rr, mStatus result)
2611	{
2612	ExtraResourceRecord *extra = (ExtraResourceRecord *)rr->RecordContext;
2613	(void)m;  //unused
2614
2615	if (result != mStatus_MemFree) { LogMsg("Error: FreeExtraRR invoked with unexpected error %d", result); return; }
2616
2617	debugf("%##s: MemFree", rr->resrec.name->c);
2618	if (rr->resrec.rdata != &rr->rdatastorage)
2619		freeL("Extra RData", rr->resrec.rdata);
2620	freeL("ExtraResourceRecord", extra);
2621	}
2622
2623mDNSlocal mStatus add_record_to_service(request_state *rstate, service_instance *instance, uint16_t rrtype, uint16_t rdlen, char *rdata, uint32_t ttl)
2624	{
2625	ServiceRecordSet *srs = &instance->srs;
2626    ExtraResourceRecord *extra;
2627	mStatus result;
2628	int size;
2629
2630	if (rdlen > sizeof(RDataBody)) size = rdlen;
2631    else size = sizeof(RDataBody);
2632
2633    extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size);
2634    if (!extra)
2635        {
2636        my_perror("ERROR: malloc");
2637		return mStatus_NoMemoryErr;
2638        }
2639
2640    bzero(extra, sizeof(ExtraResourceRecord));  // OK if oversized rdata not zero'd
2641    extra->r.resrec.rrtype = rrtype;
2642    extra->r.rdatastorage.MaxRDLength = (mDNSu16) size;
2643    extra->r.resrec.rdlength = rdlen;
2644    memcpy(&extra->r.rdatastorage.u.data, rdata, rdlen);
2645
2646    result =  mDNS_AddRecordToService(gmDNS, srs , extra, &extra->r.rdatastorage, ttl);
2647	if (result) { freeL("ExtraResourceRecord", extra); return result; }
2648
2649    extra->ClientID = rstate->hdr.reg_index;
2650	return result;
2651	}
2652
2653mDNSlocal mStatus handle_add_request(request_state *rstate)
2654    {
2655    uint32_t ttl;
2656    uint16_t rrtype, rdlen;
2657    char *ptr, *rdata;
2658    mStatus result = mStatus_UnknownErr;
2659    DNSServiceFlags flags;
2660	service_info *srvinfo = rstate->service_registration;
2661	service_instance *i;
2662
2663	if (!srvinfo) { LogMsg("handle_add_request called with NULL service_registration"); return(-1); }
2664
2665	ptr = rstate->msgdata;
2666    flags = get_flags(&ptr);
2667    rrtype = get_short(&ptr);
2668    rdlen = get_short(&ptr);
2669    rdata = get_rdata(&ptr, rdlen);
2670    ttl = get_long(&ptr);
2671
2672    if (!ttl) ttl = DefaultTTLforRRType(rrtype);
2673
2674	LogOperation("%3d: DNSServiceAddRecord(%##s, %s)", rstate->sd,
2675		(srvinfo->instances) ? srvinfo->instances->srs.RR_SRV.resrec.name->c : NULL, DNSTypeName(rrtype));
2676
2677	for (i = srvinfo->instances; i; i = i->next)
2678		{
2679		result = add_record_to_service(rstate, i, rrtype, rdlen, rdata, ttl);
2680		if (result && i->default_local) break;
2681		else result = mStatus_NoError;  // suppress non-local default errors
2682		}
2683
2684	return(result);
2685    }
2686
2687mDNSlocal mStatus update_record(AuthRecord *rr, uint16_t rdlen, char *rdata, uint32_t ttl)
2688	{
2689	int rdsize;
2690	RData *newrd;
2691	mStatus result;
2692
2693	if (rdlen > sizeof(RDataBody)) rdsize = rdlen;
2694    else rdsize = sizeof(RDataBody);
2695    newrd = mallocL("handle_update_request", sizeof(RData) - sizeof(RDataBody) + rdsize);
2696    if (!newrd) FatalError("ERROR: malloc");
2697    newrd->MaxRDLength = (mDNSu16) rdsize;
2698    memcpy(&newrd->u, rdata, rdlen);
2699
2700	// BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
2701	// since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
2702	// Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
2703	if (rr->resrec.rrtype == kDNSType_TXT && rdlen == 0) { rdlen = 1; newrd->u.txt.c[0] = 0; }
2704
2705    result = mDNS_Update(gmDNS, rr, ttl, rdlen, newrd, update_callback);
2706	if (result) { LogMsg("ERROR: mDNS_Update - %ld", result); freeL("handle_update_request", newrd); }
2707	return result;
2708	}
2709
2710mDNSlocal mStatus handle_update_request(request_state *rstate)
2711    {
2712	uint16_t rdlen;
2713    char *ptr, *rdata;
2714    uint32_t ttl;
2715    mStatus result = mStatus_BadReferenceErr;
2716	service_info *srvinfo = rstate->service_registration;
2717	service_instance *i;
2718	AuthRecord *rr = NULL;
2719
2720	// get the message data
2721	ptr = rstate->msgdata;
2722    get_flags(&ptr);	// flags unused
2723    rdlen = get_short(&ptr);
2724    rdata = get_rdata(&ptr, rdlen);
2725    ttl = get_long(&ptr);
2726
2727	if (rstate->reg_recs)
2728		{
2729		// update an individually registered record
2730		registered_record_entry *reptr;
2731		for (reptr = rstate->reg_recs; reptr; reptr = reptr->next)
2732			{
2733			if (reptr->key == rstate->hdr.reg_index)
2734				{
2735				result = update_record(reptr->rr, rdlen, rdata, ttl);
2736				goto end;
2737				}
2738			}
2739		result = mStatus_BadReferenceErr;
2740		goto end;
2741		}
2742
2743	// update a record from a service record set
2744	if (!srvinfo) { result = mStatus_BadReferenceErr;  goto end; }
2745	for (i = srvinfo->instances; i; i = i->next)
2746		{
2747		if (rstate->hdr.reg_index == TXT_RECORD_INDEX) rr = &i->srs.RR_TXT;
2748		else
2749			{
2750			ExtraResourceRecord *e;
2751			for (e = i->srs.Extras; e; e = e->next)
2752				if (e->ClientID == rstate->hdr.reg_index) { rr = &e->r; break; }
2753			}
2754
2755		if (!rr) { result = mStatus_BadReferenceErr; goto end; }
2756		result = update_record(rr, rdlen, rdata, ttl);
2757		if (result && i->default_local) goto end;
2758		else result = mStatus_NoError;  // suppress non-local default errors
2759		}
2760
2761end:
2762	LogOperation("%3d: DNSServiceUpdateRecord(%##s, %s)", rstate->sd,
2763		(srvinfo->instances) ? srvinfo->instances->srs.RR_SRV.resrec.name->c : NULL,
2764		rr ? DNSTypeName(rr->resrec.rrtype) : "<NONE>");
2765
2766    return(result);
2767    }
2768
2769mDNSlocal void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd)
2770    {
2771    (void)m; // Unused
2772    if (oldrd != &rr->rdatastorage) freeL("update_callback", oldrd);
2773    }
2774
2775mDNSlocal void free_service_instance(service_instance *srv)
2776	{
2777	request_state *rstate = srv->request;
2778	ExtraResourceRecord *e = srv->srs.Extras, *tmp;
2779
2780	// clear pointers from parent struct
2781	if (rstate)
2782		{
2783		service_instance *ptr = rstate->service_registration->instances, *prev = NULL;
2784		while (ptr)
2785			{
2786			if (ptr == srv)
2787				{
2788				if (prev) prev->next = ptr->next;
2789				else rstate->service_registration->instances = ptr->next;
2790				break;
2791				}
2792			prev = ptr;
2793			ptr = ptr->next;
2794			}
2795		}
2796
2797	while(e)
2798		{
2799		e->r.RecordContext = e;
2800		tmp = e;
2801		e = e->next;
2802		FreeExtraRR(gmDNS, &tmp->r, mStatus_MemFree);
2803		}
2804
2805	if (srv->subtypes) { freeL("regservice_callback", srv->subtypes); srv->subtypes = NULL; }
2806	freeL("regservice_callback", srv);
2807	}
2808
2809mDNSlocal void regservice_termination_callback(void *context)
2810    {
2811	service_info *info = context;
2812	service_instance *i, *p;
2813	if (!info) { LogMsg("regservice_termination_callback context is NULL"); return; }
2814	if (!info->request) { LogMsg("regservice_termination_callback info->request is NULL"); return; }
2815	i = info->instances;
2816	while (i)
2817		{
2818		p = i;
2819		i = i->next;
2820		p->request = NULL;  // clear back pointer
2821		// only safe to free memory if registration is not valid, i.e. deregister fails (which invalidates p)
2822		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));
2823		if (mDNS_DeregisterService(gmDNS, &p->srs)) free_service_instance(p);
2824		}
2825	info->request->service_registration = NULL; // clear pointer from request back to info
2826	if (info->txtdata) { freeL("txtdata", info->txtdata); info->txtdata = NULL; }
2827	freeL("service_info", info);
2828	}
2829
2830mDNSlocal mStatus handle_regrecord_request(request_state *rstate)
2831    {
2832    AuthRecord *rr;
2833    registered_record_entry *re;
2834    mStatus result;
2835
2836    if (rstate->ts != t_complete)
2837        {
2838        LogMsg("ERROR: handle_regrecord_request - transfer state != t_complete");
2839        abort_request(rstate);
2840        unlink_request(rstate);
2841        return(-1);
2842        }
2843
2844    rr = read_rr_from_ipc_msg(rstate->msgdata, 1, 1);
2845    if (!rr) return(mStatus_BadParamErr);
2846
2847    // allocate registration entry, link into list
2848    re = mallocL("handle_regrecord_request", sizeof(registered_record_entry));
2849    if (!re) FatalError("ERROR: malloc");
2850    re->key = rstate->hdr.reg_index;
2851    re->rr = rr;
2852    re->rstate = rstate;
2853    re->client_context = rstate->hdr.client_context;
2854    rr->RecordContext = re;
2855    rr->RecordCallback = regrecord_callback;
2856    re->next = rstate->reg_recs;
2857    rstate->reg_recs = re;
2858
2859    if (!rstate->terminate)
2860    	{
2861        rstate->terminate = connected_registration_termination;
2862        rstate->termination_context = rstate;
2863    	}
2864
2865    if (rr->resrec.rroriginalttl == 0)
2866        rr->resrec.rroriginalttl = DefaultTTLforRRType(rr->resrec.rrtype);
2867
2868	LogOperation("%3d: DNSServiceRegisterRecord %s", rstate->sd, RRDisplayString(gmDNS, &rr->resrec));
2869    result = mDNS_Register(gmDNS, rr);
2870    return(result);
2871    }
2872
2873mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord * rr, mStatus result)
2874    {
2875    registered_record_entry *re = rr->RecordContext;
2876	request_state *rstate = re ? re->rstate : NULL;
2877    int len;
2878    reply_state *reply;
2879    transfer_state ts;
2880    (void)m; // Unused
2881
2882	if (!re)
2883		{
2884		// parent struct alreadt freed by termination callback
2885		if (!result) LogMsg("Error: regrecord_callback: successful registration of orphaned record");
2886		else
2887			{
2888			if (result != mStatus_MemFree) LogMsg("regrecord_callback: error %d received after parent termination", result);
2889			freeL("regrecord_callback", rr);
2890			}
2891		return;
2892		}
2893
2894    // format result, add to the list for the request, including the client context in the header
2895    len = sizeof(DNSServiceFlags);
2896    len += sizeof(uint32_t);                //interfaceIndex
2897    len += sizeof(DNSServiceErrorType);
2898
2899    reply = create_reply(reg_record_reply_op, len, rstate);
2900    reply->mhdr->client_context = re->client_context;
2901    reply->rhdr->flags = dnssd_htonl(0);
2902    reply->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, rr->resrec.InterfaceID));
2903    reply->rhdr->error = dnssd_htonl(result);
2904
2905	if (result)
2906		{
2907		// unlink from list, free memory
2908		registered_record_entry **ptr = &re->rstate->reg_recs;
2909		while (*ptr && (*ptr) != re) ptr = &(*ptr)->next;
2910		if (!*ptr) { LogMsg("regrecord_callback - record not in list!"); return; }
2911		*ptr = (*ptr)->next;
2912		freeL("regrecord_callback", re->rr);
2913		re->rr = rr = NULL;
2914		freeL("regrecord_callback", re);
2915		re = NULL;
2916		}
2917
2918    ts = send_msg(reply);
2919
2920    if (ts == t_error || ts == t_terminated) { abort_request(rstate); unlink_request(rstate); }
2921    else if (ts == t_complete) freeL("regrecord_callback", reply);
2922    else if (ts == t_morecoming) append_reply(rstate, reply);   // client is blocked, link reply into list
2923	}
2924
2925mDNSlocal void connected_registration_termination(void *context)
2926    {
2927    int shared;
2928    registered_record_entry *fptr, *ptr = ((request_state *)context)->reg_recs;
2929    while(ptr)
2930        {
2931        fptr = ptr;
2932        ptr = ptr->next;
2933        shared = fptr->rr->resrec.RecordType == kDNSRecordTypeShared;
2934		fptr->rr->RecordContext = NULL;
2935        mDNS_Deregister(gmDNS, fptr->rr);
2936        freeL("connected_registration_termination", fptr);
2937		}
2938	}
2939
2940mDNSlocal mStatus handle_removerecord_request(request_state *rstate)
2941    {
2942    mStatus err = mStatus_BadReferenceErr;
2943    char *ptr;
2944	service_info *srvinfo = rstate->service_registration;
2945
2946    ptr = rstate->msgdata;
2947    get_flags(&ptr);	// flags unused
2948
2949	if (rstate->reg_recs)  err = remove_record(rstate);  // remove individually registered record
2950	else if (!srvinfo) LogOperation("%3d: DNSServiceRemoveRecord (bad ref)", rstate->sd);
2951    else
2952		{
2953		service_instance *i;
2954		LogOperation("%3d: DNSServiceRemoveRecord(%##s)", rstate->sd,
2955			(srvinfo->instances) ? srvinfo->instances->srs.RR_SRV.resrec.name->c : NULL);
2956		for (i = srvinfo->instances; i; i = i->next)
2957			{
2958			err = remove_extra(rstate, i);
2959			if (err && i->default_local) break;
2960			else err = mStatus_NoError;  // suppress non-local default errors
2961			}
2962		}
2963
2964    return(err);
2965    }
2966
2967// remove a resource record registered via DNSServiceRegisterRecord()
2968mDNSlocal mStatus remove_record(request_state *rstate)
2969    {
2970    int shared;
2971    mStatus err = mStatus_UnknownErr;
2972    registered_record_entry *e, **ptr = &rstate->reg_recs;
2973
2974    while(*ptr && (*ptr)->key != rstate->hdr.reg_index) ptr = &(*ptr)->next;
2975	if (!*ptr) { LogMsg("DNSServiceRemoveRecord - bad reference"); return mStatus_BadReferenceErr; }
2976	e = *ptr;
2977	*ptr = e->next; // unlink
2978
2979	LogOperation("%3d: DNSServiceRemoveRecord(%#s)", rstate->sd, e->rr->resrec.name->c);
2980	shared = e->rr->resrec.RecordType == kDNSRecordTypeShared;
2981	e->rr->RecordContext = NULL;
2982	err = mDNS_Deregister(gmDNS, e->rr);
2983	if (err)
2984		{
2985		LogMsg("ERROR: remove_record, mDNS_Deregister: %ld", err);
2986		freeL("remove_record", e->rr);
2987		freeL("remove_record", e);
2988		}
2989	return err;
2990    }
2991
2992mDNSlocal mStatus remove_extra(request_state *rstate, service_instance *serv)
2993	{
2994	mStatus err = mStatus_BadReferenceErr;
2995	ExtraResourceRecord *ptr;
2996
2997	for (ptr = serv->srs.Extras; ptr; ptr = ptr->next)
2998		{
2999		if (ptr->ClientID == rstate->hdr.reg_index) // found match
3000			return mDNS_RemoveRecordFromService(gmDNS, &serv->srs, ptr, FreeExtraRR, ptr);
3001		}
3002	return err;
3003	}
3004
3005// domain enumeration
3006mDNSlocal void handle_enum_request(request_state *rstate)
3007    {
3008    DNSServiceFlags flags;
3009    uint32_t ifi;
3010    mDNSInterfaceID InterfaceID;
3011    char *ptr = rstate->msgdata;
3012    domain_enum_t *def, *all;
3013    enum_termination_t *term;
3014    mStatus err;
3015    int result;
3016
3017    if (rstate->ts != t_complete)
3018        {
3019        LogMsg("ERROR: handle_enum_request - transfer state != t_complete");
3020        abort_request(rstate);
3021        unlink_request(rstate);
3022        return;
3023        }
3024
3025    flags = get_flags(&ptr);
3026    ifi = get_long(&ptr);
3027    InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, ifi);
3028    if (ifi && !InterfaceID)
3029    	{
3030		deliver_error(rstate, mStatus_BadParamErr);
3031		abort_request(rstate);
3032		unlink_request(rstate);
3033		return;
3034    	}
3035
3036    // allocate context structures
3037    def = mallocL("handle_enum_request", sizeof(domain_enum_t));
3038    all = mallocL("handle_enum_request", sizeof(domain_enum_t));
3039    term = mallocL("handle_enum_request", sizeof(enum_termination_t));
3040    if (!def || !all || !term) FatalError("ERROR: malloc");
3041
3042#if defined(MDNS_LAZY_REGISTER_SEARCH_DOMAINS)
3043	dDNS_RegisterSearchDomains( gmDNS );
3044#endif
3045
3046    // enumeration requires multiple questions, so we must link all the context pointers so that
3047    // necessary context can be reached from the callbacks
3048    def->rstate = rstate;
3049    all->rstate = rstate;
3050    term->def = def;
3051    term->all = all;
3052    term->rstate = rstate;
3053    rstate->termination_context = term;
3054    rstate->terminate = enum_termination_callback;
3055    def->question.QuestionContext = def;
3056    def->type = (flags & kDNSServiceFlagsRegistrationDomains) ?
3057        mDNS_DomainTypeRegistrationDefault: mDNS_DomainTypeBrowseDefault;
3058    all->question.QuestionContext = all;
3059    all->type = (flags & kDNSServiceFlagsRegistrationDomains) ?
3060        mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse;
3061
3062	// if the caller hasn't specified an explicit interface, we use local-only to get the system-wide list.
3063	if (!InterfaceID) InterfaceID = mDNSInterface_LocalOnly;
3064
3065    // make the calls
3066	LogOperation("%3d: DNSServiceEnumerateDomains(%X=%s)", rstate->sd, flags,
3067		(flags & kDNSServiceFlagsBrowseDomains      ) ? "kDNSServiceFlagsBrowseDomains" :
3068		(flags & kDNSServiceFlagsRegistrationDomains) ? "kDNSServiceFlagsRegistrationDomains" : "<<Unknown>>");
3069    err = mDNS_GetDomains(gmDNS, &all->question, all->type, NULL, InterfaceID, enum_result_callback, all);
3070    if (err == mStatus_NoError)
3071        err = mDNS_GetDomains(gmDNS, &def->question, def->type, NULL, InterfaceID, enum_result_callback, def);
3072    result = deliver_error(rstate, err);  // send error *before* returning local domain
3073
3074    if (result < 0 || err)
3075        {
3076        abort_request(rstate);
3077        unlink_request(rstate);
3078        return;
3079        }
3080    }
3081
3082mDNSlocal void enum_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
3083    {
3084    char domain[MAX_ESCAPED_DOMAIN_NAME];
3085    domain_enum_t *de = question->QuestionContext;
3086    DNSServiceFlags flags = 0;
3087    reply_state *reply;
3088    (void)m; // Unused
3089
3090    if (answer->rrtype != kDNSType_PTR) return;
3091	if (!AddRecord && de->type != mDNS_DomainTypeBrowse) return;
3092
3093    if (AddRecord)
3094    	{
3095        flags |= kDNSServiceFlagsAdd;
3096        if (de->type == mDNS_DomainTypeRegistrationDefault || de->type == mDNS_DomainTypeBrowseDefault)
3097            flags |= kDNSServiceFlagsDefault;
3098    	}
3099    ConvertDomainNameToCString(&answer->rdata->u.name, domain);
3100	// note that we do NOT propagate specific interface indexes to the client - for example, a domain we learn from
3101	// a machine's system preferences may be discovered on the LocalOnly interface, but should be browsed on the
3102	// network, so we just pass kDNSServiceInterfaceIndexAny
3103    reply = format_enumeration_reply(de->rstate, domain, flags, kDNSServiceInterfaceIndexAny, kDNSServiceErr_NoError);
3104    if (!reply)
3105    	{
3106        LogMsg("ERROR: enum_result_callback, format_enumeration_reply");
3107        return;
3108    	}
3109    reply->next = NULL;
3110    append_reply(de->rstate, reply);
3111    return;
3112    }
3113
3114mDNSlocal reply_state *format_enumeration_reply(request_state *rstate, const char *domain, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err)
3115    {
3116    size_t len;
3117    reply_state *reply;
3118    char *data;
3119
3120    len = sizeof(DNSServiceFlags);
3121    len += sizeof(uint32_t);
3122    len += sizeof(DNSServiceErrorType);
3123    len += strlen(domain) + 1;
3124
3125    reply = create_reply(enumeration_reply_op, len, rstate);
3126    reply->rhdr->flags = dnssd_htonl(flags);
3127    reply->rhdr->ifi = dnssd_htonl(ifi);
3128    reply->rhdr->error = dnssd_htonl(err);
3129    data = reply->sdata;
3130    put_string(domain, &data);
3131    return reply;
3132    }
3133
3134mDNSlocal void enum_termination_callback(void *context)
3135    {
3136    enum_termination_t *t = context;
3137    mDNS *coredata = gmDNS;
3138
3139    mDNS_StopGetDomains(coredata, &t->all->question);
3140    mDNS_StopGetDomains(coredata, &t->def->question);
3141    freeL("enum_termination_callback", t->all);
3142    freeL("enum_termination_callback", t->def);
3143    t->rstate->termination_context = NULL;
3144    freeL("enum_termination_callback", t);
3145    }
3146
3147mDNSlocal void handle_reconfirm_request(request_state *rstate)
3148    {
3149    AuthRecord *rr = read_rr_from_ipc_msg(rstate->msgdata, 0, 0);
3150    if (rr)
3151		{
3152		mStatus status = mDNS_ReconfirmByValue(gmDNS, &rr->resrec);
3153		LogOperation(
3154			(status == mStatus_NoError) ?
3155			"%3d: DNSServiceReconfirmRecord(%s) interface %d initiated" :
3156			"%3d: DNSServiceReconfirmRecord(%s) interface %d failed: %d",
3157			rstate->sd, RRDisplayString(gmDNS, &rr->resrec),
3158			mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, rr->resrec.InterfaceID), status);
3159		status = 0;  // Adding this line eliminates a build failure when building mDNSPosix on Tiger
3160		}
3161	abort_request(rstate);
3162	unlink_request(rstate);
3163	freeL("handle_reconfirm_request", rr);
3164	}
3165
3166// setup rstate to accept new reg/dereg requests
3167mDNSlocal void reset_connected_rstate(request_state *rstate)
3168    {
3169    rstate->ts = t_morecoming;
3170    rstate->hdr_bytes = 0;
3171    rstate->data_bytes = 0;
3172    if (rstate->msgbuf) freeL("reset_connected_rstate", rstate->msgbuf);
3173    rstate->msgbuf = NULL;
3174    rstate->bufsize = 0;
3175    }
3176
3177// returns a resource record (allocated w/ malloc) containing the data found in an IPC message
3178// data must be in format flags, interfaceIndex, name, rrtype, rrclass, rdlen, rdata, (optional)ttl
3179// (ttl only extracted/set if ttl argument is non-zero).  returns NULL for a bad-parameter error
3180mDNSlocal AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int GetTTL, int validate_flags)
3181    {
3182    char *rdata, name[256];
3183    AuthRecord *rr;
3184    DNSServiceFlags flags;
3185    uint32_t interfaceIndex;
3186    uint16_t type, class, rdlen;
3187    int storage_size;
3188
3189    flags = get_flags(&msgbuf);
3190	if (validate_flags &&
3191		!((flags & kDNSServiceFlagsShared) == kDNSServiceFlagsShared) &&
3192		!((flags & kDNSServiceFlagsUnique) == kDNSServiceFlagsUnique))
3193		{
3194		LogMsg("ERROR: Bad resource record flags (must be kDNSServiceFlagsShared or kDNSServiceFlagsUnique)");
3195		return NULL;
3196		}
3197
3198	interfaceIndex = get_long(&msgbuf);
3199    if (get_string(&msgbuf, name, 256) < 0)
3200        {
3201        LogMsg("ERROR: read_rr_from_ipc_msg - get_string");
3202        return NULL;
3203        }
3204    type = get_short(&msgbuf);
3205    class = get_short(&msgbuf);
3206    rdlen = get_short(&msgbuf);
3207
3208    if (rdlen > sizeof(RDataBody)) storage_size = rdlen;
3209    else storage_size = sizeof(RDataBody);
3210
3211    rr = mallocL("read_rr_from_ipc_msg", sizeof(AuthRecord) - sizeof(RDataBody) + storage_size);
3212    if (!rr) FatalError("ERROR: malloc");
3213    bzero(rr, sizeof(AuthRecord));  // ok if oversized rdata not zero'd
3214
3215    mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, interfaceIndex),
3216		type, 0, (mDNSu8) ((flags & kDNSServiceFlagsShared) ? kDNSRecordTypeShared : kDNSRecordTypeUnique), mDNSNULL, mDNSNULL);
3217
3218    if (!MakeDomainNameFromDNSNameString(rr->resrec.name, name))
3219    	{
3220        LogMsg("ERROR: bad name: %s", name);
3221        freeL("read_rr_from_ipc_msg", rr);
3222        return NULL;
3223    	}
3224
3225    if (flags & kDNSServiceFlagsAllowRemoteQuery) rr->AllowRemoteQuery  = mDNStrue;
3226    rr->resrec.rrclass = class;
3227    rr->resrec.rdlength = rdlen;
3228    rr->resrec.rdata->MaxRDLength = rdlen;
3229    rdata = get_rdata(&msgbuf, rdlen);
3230    memcpy(rr->resrec.rdata->u.data, rdata, rdlen);
3231    if (GetTTL) rr->resrec.rroriginalttl = get_long(&msgbuf);
3232    rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
3233    SetNewRData(&rr->resrec, mDNSNULL, 0);	// Sets rr->rdatahash for us
3234    return rr;
3235    }
3236
3237mDNSlocal int build_domainname_from_strings(domainname *srv, char *name, char *regtype, char *domain)
3238    {
3239    domainlabel n;
3240    domainname d, t;
3241
3242    if (!MakeDomainLabelFromLiteralString(&n, name)) return -1;
3243    if (!MakeDomainNameFromDNSNameString(&t, regtype)) return -1;
3244    if (!MakeDomainNameFromDNSNameString(&d, domain)) return -1;
3245    if (!ConstructServiceName(srv, &n, &t, &d)) return -1;
3246    return 0;
3247    }
3248
3249// append a reply to the list in a request object
3250mDNSlocal void append_reply(request_state *req, reply_state *rep)
3251    {
3252    reply_state *ptr;
3253
3254    if (!req->replies) req->replies = rep;
3255    else
3256    	{
3257        ptr = req->replies;
3258        while (ptr->next) ptr = ptr->next;
3259        ptr->next = rep;
3260    	}
3261    rep->next = NULL;
3262    }
3263
3264// read_msg may be called any time when the transfer state (rs->ts) is t_morecoming.
3265// returns the current state of the request (morecoming, error, complete, terminated.)
3266// if there is no data on the socket, the socket will be closed and t_terminated will be returned
3267mDNSlocal int read_msg(request_state *rs)
3268    {
3269    uint32_t nleft;
3270    int nread;
3271    char buf[4];   // dummy for death notification
3272
3273    if (rs->ts == t_terminated || rs->ts == t_error)
3274        {
3275        LogMsg("ERROR: read_msg called with transfer state terminated or error");
3276        rs->ts = t_error;
3277        return t_error;
3278        }
3279
3280    if (rs->ts == t_complete)
3281    	{  // this must be death or something is wrong
3282        nread = recv(rs->sd, buf, 4, 0);
3283        if (!nread) 	{  rs->ts = t_terminated;  return t_terminated;  	}
3284        if (nread < 0) goto rerror;
3285        LogMsg("ERROR: read data from a completed request.");
3286        rs->ts = t_error;
3287        return t_error;
3288    	}
3289
3290    if (rs->ts != t_morecoming)
3291        {
3292        LogMsg("ERROR: read_msg called with invalid transfer state (%d)", rs->ts);
3293        rs->ts = t_error;
3294        return t_error;
3295        }
3296
3297    if (rs->hdr_bytes < sizeof(ipc_msg_hdr))
3298    	{
3299        nleft = sizeof(ipc_msg_hdr) - rs->hdr_bytes;
3300        nread = recv(rs->sd, (char *)&rs->hdr + rs->hdr_bytes, nleft, 0);
3301        if (nread == 0)  	{ rs->ts = t_terminated;  return t_terminated;  	}
3302        if (nread < 0) goto rerror;
3303        rs->hdr_bytes += nread;
3304        if (rs->hdr_bytes == sizeof(ipc_msg_hdr))
3305        	{
3306        	ConvertHeaderBytes(&rs->hdr);
3307			if (rs->hdr.version != VERSION)
3308				{
3309				LogMsg("ERROR: read_msg - client version 0x%08X does not match daemon version 0x%08X", rs->hdr.version, VERSION);
3310				rs->ts = t_error;
3311				return t_error;
3312				}
3313			}
3314        if (rs->hdr_bytes > sizeof(ipc_msg_hdr))
3315            {
3316            LogMsg("ERROR: read_msg - read too many header bytes");
3317            rs->ts = t_error;
3318            return t_error;
3319            }
3320    	}
3321
3322    // only read data if header is complete
3323    if (rs->hdr_bytes == sizeof(ipc_msg_hdr))
3324    	{
3325        if (rs->hdr.datalen == 0)  // ok in removerecord requests
3326            {
3327            rs->ts = t_complete;
3328            rs->msgbuf = NULL;
3329            return t_complete;
3330            }
3331
3332        if (!rs->msgbuf)  // allocate the buffer first time through
3333            {
3334            rs->msgbuf = mallocL("read_msg", rs->hdr.datalen + MSG_PAD_BYTES);
3335            if (!rs->msgbuf)
3336            	{
3337                my_perror("ERROR: malloc");
3338                rs->ts = t_error;
3339                return t_error;
3340            	}
3341            rs->msgdata = rs->msgbuf;
3342            bzero(rs->msgbuf, rs->hdr.datalen + MSG_PAD_BYTES);
3343            }
3344        nleft = rs->hdr.datalen - rs->data_bytes;
3345        nread = recv(rs->sd, rs->msgbuf + rs->data_bytes, nleft, 0);
3346        if (nread == 0)  	{ rs->ts = t_terminated;  return t_terminated; 	}
3347        if (nread < 0)	goto rerror;
3348        rs->data_bytes += nread;
3349        if (rs->data_bytes > rs->hdr.datalen)
3350            {
3351            LogMsg("ERROR: read_msg - read too many data bytes");
3352            rs->ts = t_error;
3353            return t_error;
3354            }
3355        }
3356
3357    if (rs->hdr_bytes == sizeof(ipc_msg_hdr) && rs->data_bytes == rs->hdr.datalen)
3358        rs->ts = t_complete;
3359    else rs->ts = t_morecoming;
3360
3361    return rs->ts;
3362
3363rerror:
3364    if (dnssd_errno() == dnssd_EWOULDBLOCK || dnssd_errno() == dnssd_EINTR) return t_morecoming;
3365    my_perror("ERROR: read_msg");
3366    rs->ts = t_error;
3367    return t_error;
3368    }
3369
3370mDNSlocal int send_msg(reply_state *rs)
3371    {
3372    ssize_t nwriten;
3373
3374    if (!rs->msgbuf)
3375        {
3376        LogMsg("ERROR: send_msg called with NULL message buffer");
3377        return t_error;
3378        }
3379
3380    if (rs->request->no_reply)	//!!!KRS this behavior should be optimized if it becomes more common
3381        {
3382        rs->ts = t_complete;
3383        freeL("send_msg", rs->msgbuf);
3384        return t_complete;
3385        }
3386
3387	ConvertHeaderBytes(rs->mhdr);
3388    nwriten = send(rs->sd, rs->msgbuf + rs->nwriten, rs->len - rs->nwriten, 0);
3389	ConvertHeaderBytes(rs->mhdr);
3390    if (nwriten < 0)
3391    	{
3392        if (dnssd_errno() == dnssd_EINTR || dnssd_errno() == dnssd_EWOULDBLOCK) nwriten = 0;
3393        else
3394            {
3395#if !defined(PLATFORM_NO_EPIPE)
3396            if (dnssd_errno() == EPIPE)
3397            	{
3398                debugf("%3d: broken pipe", rs->sd);
3399                rs->ts = t_terminated;
3400                rs->request->ts = t_terminated;
3401                return t_terminated;
3402            	}
3403            else
3404#endif
3405            	{
3406                my_perror("ERROR: send\n");
3407                rs->ts = t_error;
3408                return t_error;
3409            	}
3410            }
3411        }
3412    rs->nwriten += nwriten;
3413
3414    if (rs->nwriten == rs->len)
3415    	{
3416        rs->ts = t_complete;
3417        freeL("send_msg", rs->msgbuf);
3418    	}
3419    return rs->ts;
3420    }
3421
3422mDNSlocal reply_state *create_reply(reply_op_t op, size_t datalen, request_state *request)
3423	{
3424    reply_state *reply;
3425    int totallen;
3426
3427    if ((unsigned)datalen < sizeof(reply_hdr))
3428        {
3429        LogMsg("ERROR: create_reply - data length less than lenght of required fields");
3430        return NULL;
3431        }
3432
3433    totallen = (int) (datalen + sizeof(ipc_msg_hdr));
3434    reply = mallocL("create_reply", sizeof(reply_state));
3435    if (!reply) FatalError("ERROR: malloc");
3436    bzero(reply, sizeof(reply_state));
3437    reply->ts = t_morecoming;
3438    reply->sd = request->sd;
3439    reply->request = request;
3440    reply->len = totallen;
3441    reply->msgbuf = mallocL("create_reply", totallen);
3442    if (!reply->msgbuf) FatalError("ERROR: malloc");
3443    bzero(reply->msgbuf, totallen);
3444    reply->mhdr = (ipc_msg_hdr *)reply->msgbuf;
3445    reply->rhdr = (reply_hdr *)(reply->msgbuf + sizeof(ipc_msg_hdr));
3446    reply->sdata = reply->msgbuf + sizeof(ipc_msg_hdr) + sizeof(reply_hdr);
3447    reply->mhdr->version = VERSION;
3448    reply->mhdr->op = op;
3449    reply->mhdr->datalen = totallen - sizeof(ipc_msg_hdr);
3450    return reply;
3451    }
3452
3453mDNSlocal int deliver_error(request_state *rstate, mStatus err)
3454	{
3455	int nwritten = -1;
3456	undelivered_error_t *undeliv;
3457
3458	err = dnssd_htonl(err);
3459	nwritten = send(rstate->sd, (dnssd_sockbuf_t) &err, sizeof(mStatus), 0);
3460	if (nwritten < (int)sizeof(mStatus))
3461		{
3462		if (dnssd_errno() == dnssd_EINTR || dnssd_errno() == dnssd_EWOULDBLOCK)
3463			nwritten = 0;
3464		if (nwritten < 0)
3465			{
3466			my_perror("ERROR: send - unable to deliver error to client");
3467			return(-1);
3468			}
3469		else
3470			{
3471			//client blocked - store result and come backr
3472			undeliv = mallocL("deliver_error", sizeof(undelivered_error_t));
3473			if (!undeliv) FatalError("ERROR: malloc");
3474			undeliv->err      = err;
3475			undeliv->nwritten = nwritten;
3476			undeliv->sd       = rstate->sd;
3477			rstate->u_err     = undeliv;
3478			return 0;
3479			}
3480		}
3481	return 0;
3482	}
3483
3484// returns 0 on success, -1 if send is incomplete, or on terminal failure (request is aborted)
3485mDNSlocal transfer_state send_undelivered_error(request_state *rs)
3486	{
3487	int nwritten;
3488
3489	nwritten = send(rs->u_err->sd, (char *)(&rs->u_err->err) + rs->u_err->nwritten, sizeof(mStatus) - rs->u_err->nwritten, 0);
3490	if (nwritten < 0)
3491		{
3492		if (dnssd_errno() == dnssd_EINTR || dnssd_errno() == dnssd_EWOULDBLOCK)
3493			nwritten = 0;
3494		else
3495			{
3496			my_perror("ERROR: send - unable to deliver error to client\n");
3497			return t_error;
3498			}
3499		}
3500	if ((unsigned int)(nwritten + rs->u_err->nwritten) >= sizeof(mStatus))
3501		{
3502		freeL("send_undelivered_error", rs->u_err);
3503		rs->u_err = NULL;
3504		return t_complete;
3505		}
3506	rs->u_err->nwritten += nwritten;
3507	return t_morecoming;
3508	}
3509
3510// send bogus data along with an error code to the app callback
3511// returns 0 on success (linking reply into list of not fully delivered),
3512// -1 on failure (request should be aborted)
3513mDNSlocal int deliver_async_error(request_state *rs, reply_op_t op, mStatus err)
3514    {
3515    int len;
3516    reply_state *reply;
3517    transfer_state ts;
3518
3519    if (rs->no_reply) return 0;
3520    len = 256;		// long enough for any reply handler to read all args w/o buffer overrun
3521    reply = create_reply(op, len, rs);
3522    reply->rhdr->error = dnssd_htonl(err);
3523    ts = send_msg(reply);
3524    if (ts == t_error || ts == t_terminated)
3525        {
3526        freeL("deliver_async_error", reply);
3527        return -1;
3528        }
3529    else if (ts == t_complete) freeL("deliver_async_error", reply);
3530    else if (ts == t_morecoming) append_reply(rs, reply);   // client is blocked, link reply into list
3531    return 0;
3532    }
3533
3534mDNSlocal void abort_request(request_state *rs)
3535    {
3536    reply_state *rep, *ptr;
3537
3538    if (rs->terminate) rs->terminate(rs->termination_context);  // terminate field may not be set yet
3539    if (rs->msgbuf) freeL("abort_request", rs->msgbuf);
3540	LogOperation("%3d: Removing FD", rs->sd);
3541    udsSupportRemoveFDFromEventLoop(rs->sd);					// Note: This also closes file descriptor rs->sd for us
3542
3543	// Don't use dnssd_InvalidSocket (-1) because that's the sentinel value MACOSX_MDNS_MALLOC_DEBUGGING uses
3544	// for detecting when the memory for an object is inadvertently freed while the object is still on some list
3545    rs->sd = -2;
3546
3547    // free pending replies
3548    rep = rs->replies;
3549    while(rep)
3550    	{
3551        if (rep->msgbuf) freeL("abort_request", rep->msgbuf);
3552        ptr = rep;
3553        rep = rep->next;
3554        freeL("abort_request", ptr);
3555    	}
3556
3557    if (rs->u_err)
3558        {
3559        freeL("abort_request", rs->u_err);
3560        rs->u_err = NULL;
3561        }
3562    }
3563
3564mDNSlocal void unlink_request(request_state *rs)
3565    {
3566    request_state *ptr;
3567
3568    if (rs == all_requests)
3569        {
3570        all_requests = all_requests->next;
3571        freeL("unlink_request", rs);
3572        return;
3573        }
3574    for(ptr = all_requests; ptr->next; ptr = ptr->next)
3575        if (ptr->next == rs)
3576            {
3577            ptr->next = rs->next;
3578            freeL("unlink_request", rs);
3579            return;
3580        }
3581    }
3582
3583//hack to search-replace perror's to LogMsg's
3584mDNSlocal void my_perror(char *errmsg)
3585    {
3586    LogMsg("%s: %s", errmsg, dnssd_strerror(dnssd_errno()));
3587    }
3588
3589// check that the message delivered by the client is sufficiently long to extract the required data from the buffer
3590// without overrunning it.
3591// returns 0 on success, -1 on error.
3592
3593mDNSlocal int validate_message(request_state *rstate)
3594    {
3595    uint32_t min_size;
3596
3597    switch(rstate->hdr.op)
3598    	{
3599        case resolve_request: min_size = 	sizeof(DNSServiceFlags) +	// flags
3600						sizeof(uint32_t) + 		// interface
3601						(3 * sizeof(char));           	// name, regtype, domain
3602						break;
3603        case query_request: min_size = 		sizeof(DNSServiceFlags) + 	// flags
3604						sizeof(uint32_t) +		// interface
3605						sizeof(char) + 			// fullname
3606						(2 * sizeof(uint16_t)); 	// type, class
3607						break;
3608        case browse_request: min_size = 	sizeof(DNSServiceFlags) +	// flags
3609						sizeof(uint32_t) +		// interface
3610						(2 * sizeof(char)); 		// regtype, domain
3611						break;
3612        case reg_service_request: min_size = 	sizeof(DNSServiceFlags) +	// flags
3613						sizeof(uint32_t) +		// interface
3614						(4 * sizeof(char)) + 		// name, type, domain, host
3615						(2 * sizeof(uint16_t));		// port, textlen
3616						break;
3617        case enumeration_request: min_size =	sizeof(DNSServiceFlags) +	// flags
3618						sizeof(uint32_t); 		// interface
3619						break;
3620        case reg_record_request: min_size = 	sizeof(DNSServiceFlags) +	// flags
3621						sizeof(uint32_t) + 		// interface
3622						sizeof(char) + 			// fullname
3623						(3 * sizeof(uint16_t)) +	// type, class, rdlen
3624						sizeof(uint32_t);		// ttl
3625						break;
3626        case add_record_request: min_size = 	sizeof(DNSServiceFlags) +	// flags
3627						(2 * sizeof(uint16_t)) + 	// type, rdlen
3628						sizeof(uint32_t);		// ttl
3629						break;
3630        case update_record_request: min_size =	sizeof(DNSServiceFlags) +	// flags
3631						sizeof(uint16_t) +		// rdlen
3632						sizeof(uint32_t); 		// ttl
3633						break;
3634        case remove_record_request: min_size =	sizeof(DNSServiceFlags);	// flags
3635						break;
3636        case reconfirm_record_request: min_size=sizeof(DNSServiceFlags) +	// flags
3637						sizeof(uint32_t) + 		// interface
3638						sizeof(char) + 			// fullname
3639						(3 * sizeof(uint16_t));		// type, class, rdlen
3640			            break;
3641		case setdomain_request: min_size = sizeof(DNSServiceFlags) + sizeof(char);  // flags + domain
3642			break;
3643		default:
3644            LogMsg("ERROR: validate_message - unsupported request type: %d", rstate->hdr.op);
3645	    return -1;
3646	}
3647
3648	return (rstate->data_bytes >= min_size ? 0 : -1);
3649
3650    }
3651
3652mDNSlocal uint32_t dnssd_htonl(uint32_t l)
3653	{
3654	uint32_t 	ret;
3655	char	*	data;
3656
3657	data = (char*) &ret;
3658
3659	put_long(l, &data);
3660
3661	return ret;
3662	}
3663
3664#if defined(_WIN32)
3665
3666mDNSlocal char * win32_strerror(int inErrorCode)
3667	{
3668	static char buffer[1024];
3669	DWORD       n;
3670
3671	memset(buffer, 0, sizeof(buffer));
3672
3673	n = FormatMessageA(
3674			FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
3675			NULL,
3676			(DWORD) inErrorCode,
3677			MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
3678			buffer,
3679			sizeof( buffer ),
3680			NULL );
3681
3682	if( n > 0 )
3683		{
3684		// Remove any trailing CR's or LF's since some messages have them.
3685
3686		while( ( n > 0 ) && isspace( ( (unsigned char *) buffer)[ n - 1 ] ) )
3687			{
3688			buffer[ --n ] = '\0';
3689			}
3690		}
3691
3692	return buffer;
3693	}
3694
3695#endif
3696