xref: /titanic_41/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uds_daemon.c (revision 4b22b9337f359bfd063322244f5336cc7c6ffcfa)
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 $
20 Revision 1.201.2.1  2006/08/29 06:24:36  cheshire
21 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
22 
23 Revision 1.201  2006/06/29 03:02:47  cheshire
24 <rdar://problem/4607042> mDNSResponder NXDOMAIN and CNAME support
25 
26 Revision 1.200  2006/06/28 08:56:26  cheshire
27 Added "_op" to the end of the operation code enum values,
28 to differentiate them from the routines with the same names
29 
30 Revision 1.199  2006/06/28 08:53:39  cheshire
31 Added (commented out) debugging messages
32 
33 Revision 1.198  2006/06/27 20:16:07  cheshire
34 Fix code layout
35 
36 Revision 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 
40 Revision 1.196  2006/05/05 07:07:13  cheshire
41 <rdar://problem/4538206> mDNSResponder fails when UDS reads deliver partial data
42 
43 Revision 1.195  2006/04/25 20:56:28  mkrochma
44 Added comment about previous checkin
45 
46 Revision 1.194  2006/04/25 18:29:36  mkrochma
47 Workaround for warning: unused variable 'status' when building mDNSPosix
48 
49 Revision 1.193  2006/03/19 17:14:38  cheshire
50 <rdar://problem/4483117> Need faster purging of stale records
51 read_rr_from_ipc_msg was not setting namehash and rdatahash
52 
53 Revision 1.192  2006/03/18 20:58:32  cheshire
54 Misplaced curly brace
55 
56 Revision 1.191  2006/03/10 22:19:43  cheshire
57 Update debugging message in resolve_result_callback() to indicate whether event is ADD or RMV
58 
59 Revision 1.190  2006/03/10 21:56:12  cheshire
60 <rdar://problem/4111464> After record update, old record sometimes remains in cache
61 When service TXT and SRV record both change, clients with active resolve calls get *two* callbacks, one
62 when the TXT data changes, and then immediately afterwards a second callback with the new port number
63 This change suppresses the first unneccessary (and confusing) callback
64 
65 Revision 1.189  2006/01/06 00:56:31  cheshire
66 <rdar://problem/4400573> Should remove PID file on exit
67 
68 Revision 1.188  2005/10/11 22:15:03  cheshire
69 <rdar://problem/4296042> Add memory corruption safeguards to uds_daemon.c
70 Only compile uds_validatelists() when building for Mac OS X
71 
72 Revision 1.187  2005/10/11 20:30:27  cheshire
73 <rdar://problem/4296042> Add memory corruption safeguards to uds_daemon.c
74 
75 Revision 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 
78 Revision 1.185  2005/07/29 00:55:10  ksekar
79 Removed validation check in uds_validatelists which generated false alarms
80 
81 Revision 1.184  2005/07/04 22:40:26  cheshire
82 Additional debugging code to help catch memory corruption
83 
84 Revision 1.183  2005/06/13 22:39:11  cheshire
85 <rdar://problem/4144870> Missing return statement in handle_enum_request() error handling
86 
87 Revision 1.182  2005/03/21 00:39:31  shersche
88 <rdar://problem/4021486> Fix build warnings on Win32 platform
89 
90 Revision 1.181  2005/03/20 20:21:32  shersche
91 <rdar://problem/4056827> mDNSResponder crashes when incorrect interface index is passed to DNSServiceRegister()
92 Text record length and data parameters must be initialized to 0 and NULL to ensure that the service request
93 object is cleaned up correctly when encountering an interface index error.
94 
95 Revision 1.180  2005/03/10 00:13:12  cheshire
96 <rdar://problem/4043098> DNSServiceBrowse no longer returning error codes for invalid types
97 In handle_browse_request(), mStatus err was being set correctly if an error occurred,
98 but the end of the function returned mStatus_NoError intead of err.
99 
100 Revision 1.179  2005/03/04 02:47:26  ksekar
101 <rdar://problem/4026393> SCPreference domains disappear from enumeration when moving out from firewall
102 
103 Revision 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 
106 Revision 1.177  2005/02/25 03:05:41  cheshire
107 Change "broken pipe" message to debugf()
108 
109 Revision 1.176  2005/02/24 18:44:45  ksekar
110 <rdar://problem/4018516> Printer Sharing does not get re-registered with wide-area
111 
112 Revision 1.175  2005/02/21 21:31:25  ksekar
113 <rdar://problem/4015162> changed LogMsg to debugf
114 
115 Revision 1.174  2005/02/20 01:41:17  cheshire
116 Fix compiler signed/unsigned warning
117 
118 Revision 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
120 Log additional information about failed client
121 
122 Revision 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 
125 Revision 1.171  2005/02/18 00:43:12  cheshire
126 <rdar://problem/4010245> mDNSResponder should auto-truncate service names that are too long
127 
128 Revision 1.170  2005/02/16 01:15:02  cheshire
129 Improve LogOperation() debugging messages for DNSServiceBrowse and DNSServiceRegister
130 
131 Revision 1.169  2005/02/08 01:57:14  cheshire
132 More detailed error reporting in udsserver_init()
133 
134 Revision 1.168  2005/02/03 00:44:37  cheshire
135 <rdar://problem/3986663> DNSServiceUpdateRecord returns kDNSServiceErr_Invalid when rdlen=0, rdata=NULL
136 
137 Revision 1.167  2005/02/02 02:19:32  cheshire
138 Add comment explaining why unlink(MDNS_UDS_SERVERPATH); fails
139 
140 Revision 1.166  2005/02/01 19:58:52  ksekar
141 Shortened cryptic "broken pipe" syslog message
142 
143 Revision 1.165  2005/02/01 19:56:47  ksekar
144 Moved LogMsg from daemon.c to uds_daemon.c, cleaned up wording
145 
146 Revision 1.164  2005/01/28 06:07:55  cheshire
147 Don't use deliver_error() from within handle_regrecord_request()
148 
149 Revision 1.163  2005/01/28 01:39:16  cheshire
150 Include file descriptor number in "broken pipe" message
151 
152 Revision 1.162  2005/01/27 23:59:20  cheshire
153 Remove extraneous LogMsg
154 
155 Revision 1.161  2005/01/27 22:57:56  cheshire
156 Fix compile errors on gcc4
157 
158 Revision 1.160  2005/01/27 20:52:11  cheshire
159 <rdar://problem/3972566> mDNSResponder leaks sockets for add/update/remove record calls
160 
161 Revision 1.159  2005/01/27 01:45:25  cheshire
162 <rdar://problem/3976147> mDNSResponder should never call exit(1);
163 
164 Revision 1.158  2005/01/25 17:28:07  ksekar
165 <rdar://problem/3971467> Should not return "local" twice for domain enumeration
166 
167 Revision 1.157  2005/01/21 02:20:39  cheshire
168 Fix mistake in LogOperation() format string
169 
170 Revision 1.156  2005/01/19 19:15:36  ksekar
171 Refinement to <rdar://problem/3954575> - Simplify mDNS_PurgeResultsForDomain logic and move into daemon layer
172 
173 Revision 1.155  2005/01/19 03:00:47  cheshire
174 Show Add/Rmv in DNSServiceBrowse LogOperation() message
175 
176 Revision 1.154  2005/01/15 00:56:42  ksekar
177 <rdar://problem/3954575> Unicast services don't disappear when logging
178 out of VPN
179 
180 Revision 1.153  2005/01/14 18:44:28  ksekar
181 <rdar://problem/3954609> mDNSResponder is crashing when changing domains
182 
183 Revision 1.152  2005/01/13 17:16:38  ksekar
184 Back out checkin 1.150 - correct fix is on clientstub side
185 
186 Revision 1.151  2005/01/11 21:06:29  ksekar
187 Changed now-benign LogMsg to debugf
188 
189 Revision 1.150  2005/01/07 23:59:15  ksekar
190 <rdar://problem/3942900> dnd-sd shows the wrong port numbers
191 
192 Revision 1.149  2004/12/20 23:20:35  cheshire
193 <rdar://problem/3928361> mDNSResponder crashes repeatedly when printer sharing is enabled
194 Make sure to call mDNS_SetupResourceRecord() for all newly created AuthRecords
195 
196 Revision 1.148  2004/12/20 20:37:35  cheshire
197 AllowRemoteQuery not set for the extras in a ServiceRecordSet
198 
199 Revision 1.147  2004/12/20 00:15:41  cheshire
200 Include client file descriptor numbers in udsserver_info() output
201 
202 Revision 1.146  2004/12/17 05:25:47  cheshire
203 <rdar://problem/3925163> Shorten DNS-SD queries to avoid NAT bugs
204 
205 Revision 1.145  2004/12/16 21:39:46  cheshire
206 Include CacheGroup objects in CacheUsed count
207 
208 Revision 1.144  2004/12/16 21:27:38  ksekar
209 Fixed build failures when compiled with verbose debugging messages
210 
211 Revision 1.143  2004/12/16 20:13:02  cheshire
212 <rdar://problem/3324626> Cache memory management improvements
213 
214 Revision 1.142  2004/12/16 08:07:33  shersche
215 Fix compiler error (mixed declarations and code) on Windows
216 
217 Revision 1.141  2004/12/16 01:56:21  cheshire
218 Improve DNSServiceEnumerateDomains syslog message
219 
220 Revision 1.140  2004/12/14 03:02:10  ksekar
221 <rdar://problem/3919016> Rare race condition can cause crash
222 
223 Revision 1.139  2004/12/13 21:18:45  ksekar
224 Include uDNS registrations in CountPeerRegistrations
225 
226 Revision 1.138  2004/12/13 18:23:18  ksekar
227 <rdar://problem/3915805> mDNSResponder error when quitting iChat -
228 don't close sockets delivering errors to blocked clients
229 
230 Revision 1.137  2004/12/13 00:09:22  ksekar
231 <rdar://problem/3915805> mDNSResponder error when quitting iChat
232 
233 Revision 1.136  2004/12/11 01:52:10  cheshire
234 <rdar://problem/3785820> Support kDNSServiceFlagsAllowRemoteQuery for registering services too
235 
236 Revision 1.135  2004/12/10 20:46:37  cheshire
237 Change LogOperation message to debugf
238 
239 Revision 1.134  2004/12/10 13:19:37  cheshire
240 Add verbosedebugf() logging message in CountPeerRegistrations()
241 
242 Revision 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 
245 Revision 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 
248 Revision 1.131  2004/12/10 02:09:25  cheshire
249 <rdar://problem/3898376> Modify default TTLs
250 
251 Revision 1.130  2004/12/10 00:55:24  cheshire
252 Add full name and type to LogOperation messages for DNSServiceAddRecord/UpdateRecord/RemoveRecord
253 
254 Revision 1.129  2004/12/09 03:17:23  ksekar
255 <rdar://problem/3910435> DomainEnumeration interface index should be zero
256 
257 Revision 1.128  2004/12/07 21:26:05  ksekar
258 <rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration
259 
260 Revision 1.127  2004/12/07 20:42:34  cheshire
261 Add explicit context parameter to mDNS_RemoveRecordFromService()
262 
263 Revision 1.126  2004/12/07 17:23:55  ksekar
264 Fixed LogOperation
265 
266 Revision 1.125  2004/12/06 21:15:23  ksekar
267 <rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
268 
269 Revision 1.124  2004/11/30 02:19:14  cheshire
270 <rdar://problem/3827971> Raise maxfds.rlim_cur for mDNSResponder
271 
272 Revision 1.123  2004/11/29 23:50:57  cheshire
273 Checkin 1.122 not necessary
274 
275 Revision 1.122  2004/11/24 17:55:01  ksekar
276 Added log message clarifying <rdar://problem/3869241> For unicast operations, verify that service types are legal
277 
278 Revision 1.121  2004/11/24 04:45:52  cheshire
279 Spelling mistake
280 
281 Revision 1.120  2004/11/24 00:10:44  cheshire
282 <rdar://problem/3869241> For unicast operations, verify that service types are legal
283 
284 Revision 1.119  2004/11/23 23:54:17  ksekar
285 <rdar://problem/3890318> Wide-Area DNSServiceRegisterRecord() failures
286 can crash mDNSResponder
287 
288 Revision 1.118  2004/11/23 22:33:01  cheshire
289 <rdar://problem/3654910> Remove temporary workaround code for iChat
290 
291 Revision 1.117  2004/11/23 20:23:10  ksekar
292 Fixed LogOperation that causes crash on connected service deregistrations
293 
294 Revision 1.116  2004/11/23 03:39:47  cheshire
295 Let interface name/index mapping capability live directly in JNISupport.c,
296 instead of having to call through to the daemon via IPC to get this information.
297 
298 Revision 1.115  2004/11/13 00:12:53  ksekar
299 Fixed some LogOperation printf converstions for debug builds.
300 
301 Revision 1.114  2004/11/12 18:25:45  shersche
302 Tidy up cross platform usleep code fragment.
303 
304 Revision 1.113  2004/11/12 03:21:41  rpantos
305 rdar://problem/3809541 Add DNSSDMapIfIndexToName, DNSSDMapNameToIfIndex.
306 
307 Revision 1.112  2004/11/11 16:58:32  ksekar
308 Removed unused code (previously wrapped in #if 0)
309 
310 Revision 1.111  2004/11/05 22:47:37  shersche
311 Conditionally compile usleep(1000) to be Sleep(1) on Windows
312 Submitted by: Pavel Repin <prepin@gmail.com>
313 
314 Revision 1.110  2004/11/05 19:56:56  ksekar
315 <rdar://problem/3862646> We no longer need to browse .Mac domains by
316 default - changed #if 0 to more descriptive #ifdef _HAVE_SETDOMAIN_SUPPORT_
317 
318 Revision 1.109  2004/11/04 03:40:45  cheshire
319 More debugging messages
320 
321 Revision 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 
324 Revision 1.107  2004/11/02 19:39:23  ksekar
325 <rdar://problem/3862646> We no longer need to browse .Mac domains by default
326 
327 Revision 1.106  2004/11/02 02:12:21  cheshire
328 <rdar://problem/3839111> Remove unnecessary memory allocations
329 
330 Revision 1.105  2004/10/28 19:07:19  cheshire
331 Add some more debugging checks and improved LogOperation() messages
332 
333 Revision 1.104  2004/10/26 18:53:15  cheshire
334 Avoid unused variable warning
335 
336 Revision 1.103  2004/10/26 07:15:55  cheshire
337 Add file descriptor number to all LogOperation messages
338 
339 Revision 1.102  2004/10/26 06:11:42  cheshire
340 Add improved logging to aid in diagnosis of <rdar://problem/3842714> mDNSResponder crashed
341 
342 Revision 1.101  2004/10/26 04:31:44  cheshire
343 Rename CountSubTypes() as ChopSubTypes()
344 
345 Revision 1.100  2004/10/26 01:17:48  cheshire
346 Use "#if 0" instead of commenting out code
347 
348 Revision 1.99  2004/10/19 21:33:22  cheshire
349 <rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
350 Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name
351 doesn't force multicast unless you set this flag to indicate explicitly that this is what you want
352 
353 Revision 1.98  2004/10/14 01:59:33  cheshire
354 <rdar://problem/3839208> UDS resolves don't work for uDNS services
355 
356 Revision 1.97  2004/10/13 00:58:35  cheshire
357 <rdar://problem/3832738> Registering a proxy doesn't work
358 
359 Revision 1.96  2004/09/30 00:25:00  ksekar
360 <rdar://problem/3695802> Dynamically update default registration domains on config change
361 
362 Revision 1.95  2004/09/26 23:20:36  ksekar
363 <rdar://problem/3813108> Allow default registrations in multiple wide-area domains
364 
365 Revision 1.94  2004/09/22 18:27:06  ksekar
366 <rdar://problem/3811427> allow DNSServiceAddRecord to pass zero to get
367 default record TTL
368 
369 Revision 1.93  2004/09/22 02:39:44  cheshire
370 <rdar://problem/3810757> Allow DNSServiceRegisterRecord to pass zero to get default record TTL
371 
372 Revision 1.92  2004/09/22 02:34:04  cheshire
373 Rename parameter "ttl" to "GetTTL" for clarity
374 
375 Revision 1.91  2004/09/22 02:25:43  cheshire
376 Fix spelling errors
377 
378 Revision 1.90  2004/09/21 23:40:12  ksekar
379 <rdar://problem/3810349> mDNSResponder to return errors on NAT traversal failure
380 
381 Revision 1.89  2004/09/21 23:29:51  cheshire
382 <rdar://problem/3680045> DNSServiceResolve should delay sending packets
383 
384 Revision 1.88  2004/09/21 23:12:46  cheshire
385 Reorder initialization of question fields to match structure order
386 
387 Revision 1.87  2004/09/21 22:18:33  cheshire
388 In SIGINFO output, display a '-' next to records that have the Unique bit set
389 
390 Revision 1.86  2004/09/21 21:05:11  cheshire
391 Move duplicate code out of mDNSMacOSX/daemon.c and mDNSPosix/PosixDaemon.c,
392 into mDNSShared/uds_daemon.c
393 
394 Revision 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 
397 Revision 1.84  2004/09/17 01:08:55  cheshire
398 Renamed 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 
403 Revision 1.83  2004/09/16 23:26:33  cheshire
404 Move version check inside preceeding "if" that checks we have a complete header
405 
406 Revision 1.82  2004/09/16 23:14:25  cheshire
407 Changes for Windows compatibility
408 
409 Revision 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 
412 Revision 1.80  2004/09/16 01:58:23  cheshire
413 Fix compiler warnings
414 
415 Revision 1.79  2004/09/16 00:24:49  cheshire
416 <rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
417 
418 Revision 1.78  2004/09/15 21:44:20  cheshire
419 <rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init
420 Show time value in log to help diagnose errors
421 
422 Revision 1.77  2004/09/15 00:19:18  cheshire
423 <rdar://problem/3785823> read_rr_from_ipc_msg should use mDNS_SetupResourceRecord()
424 
425 Revision 1.76  2004/09/02 06:39:52  cheshire
426 Minor textual cleanup for clarity
427 
428 Revision 1.75  2004/09/02 03:48:47  cheshire
429 <rdar://problem/3709039> Disable targeted unicast query support by default
430 1. New flag kDNSServiceFlagsAllowRemoteQuery to indicate we want to allow remote queries for this record
431 2. New field AllowRemoteQuery in AuthRecord structure
432 3. uds_daemon.c sets AllowRemoteQuery if kDNSServiceFlagsAllowRemoteQuery is set
433 4. mDNS.c only answers remote queries if AllowRemoteQuery is set
434 
435 Revision 1.74  2004/08/25 02:32:47  cheshire
436 Minor cleanup: replace "&regtype[0]" with "regtype"
437 
438 Revision 1.73  2004/08/25 02:30:40  cheshire
439 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
440 
441 Revision 1.72  2004/08/14 03:22:42  cheshire
442 <rdar://problem/3762579> Dynamic DNS UI <-> mDNSResponder glue
443 Add GetUserSpecifiedDDNSName() routine
444 Convert ServiceRegDomain to domainname instead of C string
445 Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs
446 
447 Revision 1.71  2004/08/11 04:21:21  rpantos
448 Fix Windows build.
449 
450 Revision 1.70  2004/08/11 02:07:00  cheshire
451 Remove "mDNS *globalInstance" parameter from udsserver_init()
452 Move CheckForDuplicateRegistrations from daemon.c
453 <rdar://problem/3501938> No warning when accidentally registering the same service multiple times using socket API
454 
455 Revision 1.69  2004/08/10 16:14:48  cheshire
456 Fix debug builds (oops)
457 
458 Revision 1.68  2004/08/10 06:24:56  cheshire
459 Use types with precisely defined sizes for 'op' and 'reg_index', for better
460 compatibility if the daemon and the client stub are built using different compilers
461 
462 Revision 1.67  2004/07/27 07:14:16  shersche
463 make error socket non-blocking after call to connect()
464 
465 Revision 1.66  2004/07/13 21:24:25  rpantos
466 Fix for <rdar://problem/3701120>.
467 
468 Revision 1.65  2004/06/26 03:17:14  shersche
469 implement cross-platform strerror function
470 
471 Submitted by: herscher
472 
473 Revision 1.64  2004/06/25 00:26:27  rpantos
474 Changes to fix the Posix build on Solaris.
475 
476 Revision 1.63  2004/06/24 03:43:44  rpantos
477 Fix previous checkin so it builds on Windows.
478 
479 Revision 1.62  2004/06/24 00:57:08  ksekar
480 Replaced code acccidentally removed in checkin 1.59.
481 
482 Revision 1.61  2004/06/19 00:09:39  cheshire
483 Remove unused strsep() implementation
484 
485 Revision 1.60  2004/06/18 19:10:00  cheshire
486 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
487 
488 Revision 1.59  2004/06/18 05:10:31  rpantos
489 Changes to allow code to be used on Windows
490 
491 Revision 1.58  2004/06/15 03:54:08  cheshire
492 Include mDNS_TimeNow(&mDNSStorage) in SIGINFO output
493 
494 Revision 1.57  2004/06/12 01:47:27  ksekar
495 <rdar://problem/3690241>: BBEdit crashes when trying to check for newer version
496 udsserver_idle compared time in ticks to interval in seconds.
497 
498 Revision 1.56  2004/06/12 01:35:47  cheshire
499 Changes for Windows compatibility
500 
501 Revision 1.55  2004/06/05 00:04:27  cheshire
502 <rdar://problem/3668639>: wide-area domains should be returned in reg. domain enumeration
503 
504 Revision 1.54  2004/06/01 22:22:52  ksekar
505 <rdar://problem/3668635>: wide-area default registrations should be in
506 .local too
507 
508 Revision 1.53  2004/05/28 23:42:37  ksekar
509 <rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
510 
511 Revision 1.52  2004/05/26 00:39:49  ksekar
512 <rdar://problem/3667105>: wide-area DNS-SD servers don't appear in
513 Finder
514 Use local-only InterfaceID for GetDomains calls for sockets-API
515 
516 Revision 1.51  2004/05/18 23:51:27  cheshire
517 Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
518 
519 Revision 1.50  2004/05/14 16:39:47  ksekar
520 Browse for iChat locally for now.
521 
522 Revision 1.49  2004/05/13 21:33:52  ksekar
523 Clean up non-local registration control via config file.  Force iChat
524 registrations to be local for now.
525 
526 Revision 1.48  2004/05/13 04:13:19  ksekar
527 Updated SIGINFO handler for multi-domain browses
528 
529 Revision 1.47  2004/05/12 22:04:01  ksekar
530 Implemented multi-domain browsing by default for uds_daemon.
531 
532 Revision 1.46  2004/05/06 18:42:58  ksekar
533 General 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 
537 Revision 1.45  2004/03/12 08:49:28  cheshire
538 #include <sys/socket.h>
539 
540 Revision 1.44  2004/02/25 01:25:27  ksekar
541 <rdar://problem/3569212>: DNSServiceRegisterRecord flags not error-checked
542 
543 Revision 1.43  2004/02/24 01:46:40  cheshire
544 Manually reinstate lost checkin 1.36
545 
546 Revision 1.42  2004/02/05 19:39:29  cheshire
547 Move creation of /var/run/mDNSResponder.pid to uds_daemon.c,
548 so that all platforms get this functionality
549 
550 Revision 1.41  2004/02/03 18:59:02  cheshire
551 Change "char *domain" parameter for format_enumeration_reply to "const char *domain"
552 
553 Revision 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 
556 Revision 1.39  2004/01/25 00:03:21  cheshire
557 Change to use mDNSVal16() instead of private PORT_AS_NUM() macro
558 
559 Revision 1.38  2004/01/19 19:51:46  cheshire
560 Fix compiler error (mixed declarations and code) on some versions of Linux
561 
562 Revision 1.37  2003/12/08 21:11:42  rpantos
563 Changes necessary to support mDNSResponder on Linux.
564 
565 Revision 1.36  2003/12/04 23:40:57  cheshire
566 <rdar://problem/3484766>: Security: Crashing bug in mDNSResponder
567 Fix some more code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers.
568 
569 Revision 1.35  2003/12/03 19:10:22  ksekar
570 <rdar://problem/3498644>: malloc'd data not zero'd
571 
572 Revision 1.34  2003/12/03 02:00:01  ksekar
573 <rdar://problem/3498644>: malloc'd data not zero'd
574 
575 Revision 1.33  2003/11/22 01:18:46  ksekar
576 <rdar://problem/3486646>: config change handler not called for dns-sd services
577 
578 Revision 1.32  2003/11/20 21:46:12  ksekar
579 <rdar://problem/3486635>: leak: DNSServiceRegisterRecord
580 
581 Revision 1.31  2003/11/20 20:33:05  ksekar
582 <rdar://problem/3486635>: leak: DNSServiceRegisterRecord
583 
584 Revision 1.30  2003/11/20 02:10:55  ksekar
585 <rdar://problem/3486643>: cleanup DNSServiceAdd/RemoveRecord
586 
587 Revision 1.29  2003/11/14 21:18:32  cheshire
588 <rdar://problem/3484766>: Security: Crashing bug in mDNSResponder
589 Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers.
590 
591 Revision 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 
594 Revision 1.27  2003/11/05 22:44:57  ksekar
595 <rdar://problem/3335230>: No bounds checking when reading data from client
596 Reviewed by: Stuart Cheshire
597 
598 Revision 1.26  2003/10/23 17:51:04  ksekar
599 <rdar://problem/3335216>: handle blocked clients more efficiently
600 Changed gettimeofday() to mDNS_TimeNow(&mDNSStorage);
601 
602 Revision 1.25  2003/10/22 23:37:49  ksekar
603 <rdar://problem/3459141>: crash/hang in abort_client
604 
605 Revision 1.24  2003/10/21 20:59:40  ksekar
606 <rdar://problem/3335216>: handle blocked clients more efficiently
607 
608 Revision 1.23  2003/09/23 02:12:43  cheshire
609 Also include port number in list of services registered via new UDS API
610 
611 Revision 1.22  2003/08/19 16:03:55  ksekar
612 <rdar://problem/3380097>: ER: SIGINFO dump should include resolves started by DNSServiceQueryRecord
613 Check termination_context for NULL before dereferencing.
614 
615 Revision 1.21  2003/08/19 05:39:43  cheshire
616 <rdar://problem/3380097> SIGINFO dump should include resolves started by DNSServiceQueryRecord
617 
618 Revision 1.20  2003/08/16 03:39:01  cheshire
619 <rdar://problem/3338440> InterfaceID -1 indicates "local only"
620 
621 Revision 1.19  2003/08/15 20:16:03  cheshire
622 <rdar://problem/3366590> mDNSResponder takes too much RPRVT
623 We want to avoid touching the rdata pages, so we don't page them in.
624 1. 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.
626 2. 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 
629 Revision 1.18  2003/08/15 00:38:00  ksekar
630 <rdar://problem/3377005>: Bug: buffer overrun when reading long rdata from client
631 
632 Revision 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 
635 Revision 1.16  2003/08/13 23:58:52  ksekar
636 <rdar://problem/3374911>: Bug: UDS Sub-type browsing works, but not sub-type registration
637 Fixed pointer increment error, moved subtype reading for-loop for easier error bailout.
638 
639 Revision 1.15  2003/08/13 17:30:33  ksekar
640 <rdar://problem/3374671>: DNSServiceAddRecord doesn't work
641 Fixed various problems with handling the AddRecord request and freeing the ExtraResourceRecords.
642 
643 Revision 1.14  2003/08/12 19:56:25  cheshire
644 Update 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)
655 static 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)
683 extern mStatus dDNS_RegisterSearchDomains( mDNS * const m );
684 #endif
685 
686 // Types and Data Structures
687 // ----------------------------------------------------------------------
688 
689 typedef enum
690     {
691     t_uninitialized,
692     t_morecoming,
693     t_complete,
694     t_error,
695     t_terminated
696     } transfer_state;
697 
698 typedef void (*req_termination_fn)(void *);
699 
700 typedef 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.
712 typedef 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.
730 typedef 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
750 typedef 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
758 typedef 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 
768 typedef 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 
775 typedef 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
807 typedef 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 
814 typedef 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
834 typedef struct
835     {
836     DNSQuestion question;
837     mDNS_DomainType type;
838     request_state *rstate;
839     } domain_enum_t;
840 
841 typedef struct
842     {
843     domain_enum_t *all;
844     domain_enum_t *def;
845     request_state *rstate;
846     } enum_termination_t;
847 
848 typedef 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_
863 typedef 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 
870 static default_browse_list_t *default_browse_list = NULL;
871 #endif // _HAVE_SETDOMAIN_SUPPORT_
872 
873 // globals
874 mDNSexport mDNS mDNSStorage;
875 #define gmDNS (&mDNSStorage)
876 
877 static dnssd_sock_t			listenfd		=	dnssd_InvalidSocket;
878 static 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
885 mDNSlocal void connect_callback(void *info);
886 mDNSlocal int read_msg(request_state *rs);
887 mDNSlocal int send_msg(reply_state *rs);
888 mDNSlocal void abort_request(request_state *rs);
889 mDNSlocal void request_callback(void *info);
890 mDNSlocal void handle_resolve_request(request_state *rstate);
891 mDNSlocal void question_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord);
892 mDNSlocal void question_termination_callback(void *context);
893 mDNSlocal void handle_browse_request(request_state *request);
894 mDNSlocal void browse_termination_callback(void *context);
895 mDNSlocal void handle_regservice_request(request_state *request);
896 mDNSlocal void regservice_termination_callback(void *context);
897 mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result);
898 mDNSlocal mStatus handle_add_request(request_state *rstate);
899 mDNSlocal mStatus handle_update_request(request_state *rstate);
900 mDNSlocal void append_reply(request_state *req, reply_state *rep);
901 mDNSlocal int build_domainname_from_strings(domainname *srv, char *name, char *regtype, char *domain);
902 mDNSlocal void enum_termination_callback(void *context);
903 mDNSlocal void enum_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord);
904 mDNSlocal void handle_query_request(request_state *rstate);
905 mDNSlocal reply_state *format_enumeration_reply(request_state *rstate, const char *domain, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err);
906 mDNSlocal void handle_enum_request(request_state *rstate);
907 mDNSlocal mStatus handle_regrecord_request(request_state *rstate);
908 mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord * rr, mStatus result);
909 mDNSlocal void connected_registration_termination(void *context);
910 mDNSlocal void handle_reconfirm_request(request_state *rstate);
911 mDNSlocal AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int ttl, int validate_flags);
912 mDNSlocal mStatus handle_removerecord_request(request_state *rstate);
913 mDNSlocal void reset_connected_rstate(request_state *rstate);
914 mDNSlocal int deliver_error(request_state *rstate, mStatus err);
915 mDNSlocal int deliver_async_error(request_state *rs, reply_op_t op, mStatus err);
916 mDNSlocal transfer_state send_undelivered_error(request_state *rs);
917 mDNSlocal reply_state *create_reply(reply_op_t op, size_t datalen, request_state *request);
918 mDNSlocal void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd);
919 mDNSlocal void my_perror(char *errmsg);
920 mDNSlocal void unlink_request(request_state *rs);
921 mDNSlocal void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord);
922 mDNSlocal void resolve_termination_callback(void *context);
923 mDNSlocal int validate_message(request_state *rstate);
924 mDNSlocal mStatus remove_extra(request_state *rstate, service_instance *serv);
925 mDNSlocal mStatus remove_record(request_state *rstate);
926 mDNSlocal void free_service_instance(service_instance *srv);
927 mDNSlocal uint32_t dnssd_htonl(uint32_t l);
928 mDNSlocal 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 
LogClientInfo(request_state * req)937 mDNSlocal 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 
FatalError(char * errmsg)963 mDNSlocal 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 
udsserver_init(void)970 int 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 
1082 error:
1083 
1084     my_perror("ERROR: udsserver_init");
1085     return -1;
1086     }
1087 
udsserver_exit(void)1088 int 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 
udsserver_idle(mDNSs32 nextevent)1105 mDNSs32 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 
udsserver_info(mDNS * const m)1170 mDNSexport 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
uds_validatelists(void)1217 mDNSexport 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 
rename_service(service_instance * srv)1226 mDNSlocal 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 
udsserver_handle_configchange(void)1236 mDNSexport 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 
connect_callback(void * info)1251 mDNSlocal 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
request_callback(void * info)1308 mDNSlocal 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 
handle_query_request(request_state * rstate)1467 mDNSlocal 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 
1525 bad_param:
1526     deliver_error(rstate, mStatus_BadParamErr);
1527     rstate->terminate = NULL;	// don't try to terminate insuccessful Core calls
1528 error:
1529     abort_request(rstate);
1530     unlink_request(rstate);
1531     return;
1532     }
1533 
handle_resolve_request(request_state * rstate)1534 mDNSlocal 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 
1628 bad_param:
1629     deliver_error(rstate, mStatus_BadParamErr);
1630     abort_request(rstate);
1631     unlink_request(rstate);
1632     }
1633 
resolve_termination_callback(void * context)1634 mDNSlocal 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 
resolve_result_callback(mDNS * const m,DNSQuestion * question,const ResourceRecord * const answer,mDNSBool AddRecord)1654 mDNSlocal 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
question_result_callback(mDNS * const m,DNSQuestion * question,const ResourceRecord * const answer,mDNSBool AddRecord)1744 mDNSlocal 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 
question_termination_callback(void * context)1790 mDNSlocal 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
FindFirstSubType(char * p)1801 mDNSlocal 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
FindNextSubType(char * p)1816 mDNSlocal 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
ChopSubTypes(char * regtype)1835 mDNSexport 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 
AllocateSubTypes(mDNSs32 NumSubTypes,char * p)1849 mDNSexport 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_
free_defdomain(mDNS * const m,AuthRecord * const rr,mStatus result)1870 mDNSlocal 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 
handle_setdomain_request(request_state * request)1877 mDNSlocal 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
GenerateNTDResponse(domainname * servicename,mDNSInterfaceID id,request_state * request,reply_state ** rep)1969 mDNSlocal 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 
FoundInstance(mDNS * const m,DNSQuestion * question,const ResourceRecord * const answer,mDNSBool AddRecord)2012 mDNSlocal 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 
add_domain_to_browser(browser_info_t * info,const domainname * d)2035 mDNSlocal 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 
handle_browse_request(request_state * request)2063 mDNSlocal 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 
2157 error:
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 
browse_termination_callback(void * context)2165 mDNSlocal 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 
udsserver_default_browse_domain_changed(const domainname * d,mDNSBool add)2185 mDNSexport 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.
CountPeerRegistrations(mDNS * const m,ServiceRecordSet * const srs)2227 mDNSexport 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 
CountExistingRegistrations(domainname * srv,mDNSIPPort port)2250 mDNSexport 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 
register_service_instance(request_state * request,const domainname * domain)2262 mDNSlocal 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 
udsserver_default_reg_domain_changed(const domainname * d,mDNSBool add)2304 mDNSexport 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
handle_regservice_request(request_state * request)2347 mDNSlocal 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 
2473 finish:
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 
2485 bad_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 
regservice_callback(mDNS * const m,ServiceRecordSet * const srs,mStatus result)2496 mDNSlocal 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 
FreeExtraRR(mDNS * const m,AuthRecord * const rr,mStatus result)2610 mDNSexport 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 
add_record_to_service(request_state * rstate,service_instance * instance,uint16_t rrtype,uint16_t rdlen,char * rdata,uint32_t ttl)2623 mDNSlocal 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 
handle_add_request(request_state * rstate)2653 mDNSlocal 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 
update_record(AuthRecord * rr,uint16_t rdlen,char * rdata,uint32_t ttl)2687 mDNSlocal 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 
handle_update_request(request_state * rstate)2710 mDNSlocal 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 
2761 end:
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 
update_callback(mDNS * const m,AuthRecord * const rr,RData * oldrd)2769 mDNSlocal 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 
free_service_instance(service_instance * srv)2775 mDNSlocal 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 
regservice_termination_callback(void * context)2809 mDNSlocal 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 
handle_regrecord_request(request_state * rstate)2830 mDNSlocal 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 
regrecord_callback(mDNS * const m,AuthRecord * rr,mStatus result)2873 mDNSlocal 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 
connected_registration_termination(void * context)2925 mDNSlocal 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 
handle_removerecord_request(request_state * rstate)2940 mDNSlocal 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()
remove_record(request_state * rstate)2968 mDNSlocal 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 
remove_extra(request_state * rstate,service_instance * serv)2992 mDNSlocal 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
handle_enum_request(request_state * rstate)3006 mDNSlocal 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 
enum_result_callback(mDNS * const m,DNSQuestion * question,const ResourceRecord * const answer,mDNSBool AddRecord)3082 mDNSlocal 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 
format_enumeration_reply(request_state * rstate,const char * domain,DNSServiceFlags flags,uint32_t ifi,DNSServiceErrorType err)3114 mDNSlocal 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 
enum_termination_callback(void * context)3134 mDNSlocal 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 
handle_reconfirm_request(request_state * rstate)3147 mDNSlocal 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
reset_connected_rstate(request_state * rstate)3167 mDNSlocal 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
read_rr_from_ipc_msg(char * msgbuf,int GetTTL,int validate_flags)3180 mDNSlocal 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 
build_domainname_from_strings(domainname * srv,char * name,char * regtype,char * domain)3237 mDNSlocal 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
append_reply(request_state * req,reply_state * rep)3250 mDNSlocal 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
read_msg(request_state * rs)3267 mDNSlocal 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 
3363 rerror:
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 
send_msg(reply_state * rs)3370 mDNSlocal 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 
create_reply(reply_op_t op,size_t datalen,request_state * request)3422 mDNSlocal 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 
deliver_error(request_state * rstate,mStatus err)3453 mDNSlocal 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)
send_undelivered_error(request_state * rs)3485 mDNSlocal 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)
deliver_async_error(request_state * rs,reply_op_t op,mStatus err)3513 mDNSlocal 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 
abort_request(request_state * rs)3534 mDNSlocal 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 
unlink_request(request_state * rs)3564 mDNSlocal 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
my_perror(char * errmsg)3584 mDNSlocal 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 
validate_message(request_state * rstate)3593 mDNSlocal 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 
dnssd_htonl(uint32_t l)3652 mDNSlocal 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 
win32_strerror(int inErrorCode)3666 mDNSlocal 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