2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
23 Change History (most recent first):
25 $Log: uds_daemon.c,v $
26 Revision 1.22 2003/08/19 16:03:55 ksekar
27 Bug #: <rdar://problem/3380097>: ER: SIGINFO dump should include resolves started by DNSServiceQueryRecord
28 Check termination_context for NULL before dereferencing.
30 Revision 1.21 2003/08/19 05:39:43 cheshire
31 <rdar://problem/3380097> SIGINFO dump should include resolves started by DNSServiceQueryRecord
33 Revision 1.20 2003/08/16 03:39:01 cheshire
34 <rdar://problem/3338440> InterfaceID -1 indicates "local only"
36 Revision 1.19 2003/08/15 20:16:03 cheshire
37 <rdar://problem/3366590> mDNSResponder takes too much RPRVT
38 We want to avoid touching the rdata pages, so we don't page them in.
39 1. RDLength was stored with the rdata, which meant touching the page just to find the length.
40 Moved this from the RData to the ResourceRecord object.
41 2. To avoid unnecessarily touching the rdata just to compare it,
42 compute a hash of the rdata and store the hash in the ResourceRecord object.
44 Revision 1.18 2003/08/15 00:38:00 ksekar
45 Bug #: <rdar://problem/3377005>: Bug: buffer overrun when reading long rdata from client
47 Revision 1.17 2003/08/14 02:18:21 cheshire
48 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
50 Revision 1.16 2003/08/13 23:58:52 ksekar
51 Bug #: <rdar://problem/3374911>: Bug: UDS Sub-type browsing works, but not sub-type registration
52 Fixed pointer increment error, moved subtype reading for-loop for easier error bailout.
54 Revision 1.15 2003/08/13 17:30:33 ksekar
55 Bug #: <rdar://problem/3374671>: DNSServiceAddRecord doesn't work
56 Fixed various problems with handling the AddRecord request and freeing the ExtraResourceRecords.
58 Revision 1.14 2003/08/12 19:56:25 cheshire
63 #include "mDNSClientAPI.h"
64 #include "mDNSMacOSX.h"
66 #include "dnssd_ipc.h"
68 #include <sys/ioctl.h>
69 #include <sys/types.h>
71 #include <sys/resource.h>
73 // Types and Data Structures
74 // ----------------------------------------------------------------------
85 typedef void (*req_termination_fn
)(void *);
88 typedef struct registered_record_entry
92 struct registered_record_entry
*next
;
93 } registered_record_entry
;
95 typedef struct registered_service
97 //struct registered_service *next;
100 int rename_on_memfree
; // set flag on config change when we deregister original name
102 ServiceRecordSet
*srs
;
103 struct request_state
*request
;
104 AuthRecord
*subtypes
;
105 } registered_service
;
112 } undelivered_error_t
;
114 typedef struct request_state
116 // connection structures
117 CFRunLoopSourceRef rls
;
122 // state of read (in case message is read over several recv() calls)
124 uint32_t hdr_bytes
; // bytes of header already read
126 uint32_t data_bytes
; // bytes of message data already read
127 char *msgbuf
; // pointer to data storage to pass to free()
128 char *msgdata
; // pointer to data to be read from (may be modified)
129 int bufsize
; // size of data storage
131 // reply, termination, error, and client context info
132 int no_reply
; // don't send asynchronous replies to client
133 void *client_context
; // don't touch this - pointer only valid in client's addr space
134 struct reply_state
*replies
; // corresponding (active) reply list
135 undelivered_error_t
*u_err
;
136 void *termination_context
;
137 req_termination_fn terminate
;
139 //!!!KRS toss these pointers in a union
140 // registration context associated with this request (null if not applicable)
141 registered_record_entry
*reg_recs
; // muliple registrations for a connection-oriented request
142 registered_service
*service
; // service record set and flags
143 struct resolve_result_t
*resolve_results
;
145 struct request_state
*next
;
148 // struct physically sits between ipc message header and call-specific fields in the message buffer
151 DNSServiceFlags flags
;
153 DNSServiceErrorType error
;
157 typedef struct reply_state
159 // state of the transmission
164 // context of the reply
165 struct request_state
*request
; // the request that this answers
166 struct reply_state
*next
; // if there are multiple unsent replies
167 // pointer into message buffer - allows fields to be changed after message is formatted
170 char *sdata
; // pointer to start of call-specific data
171 // pointer to malloc'd buffer
176 // domain enumeration and resolv calls require 2 mDNSCore calls, so we need separate interconnected
177 // structures to handle callbacks
180 DNSQuestion question
;
181 mDNS_DomainType type
;
182 request_state
*rstate
;
189 request_state
*rstate
;
190 } enum_termination_t
;
194 DNSQuestion question
;
196 request_state
*rstate
;
203 request_state
*rstate
;
204 } resolve_termination_t
;
206 typedef struct resolve_result_t
208 const ResourceRecord
*txt
;
209 const ResourceRecord
*srv
;
214 request_state
*rstate
;
215 client_context_t client_context
;
216 } regrecord_callback_context
;
222 static int listenfd
= -1;
223 static request_state
*all_requests
= NULL
;
224 //!!!KRS we should keep a separate list containing only the requests that need to be examined
225 //in the idle() routine.
228 #define MAX_OPENFILES 1024
231 // private function prototypes
232 static void connect_callback(CFSocketRef sr
, CFSocketCallBackType t
, CFDataRef dr
, const void *c
, void *i
);
233 static int read_msg(request_state
*rs
);
234 static int send_msg(reply_state
*rs
);
235 static void abort_request(request_state
*rs
);
236 static void request_callback(CFSocketRef sr
, CFSocketCallBackType t
, CFDataRef dr
, const void *c
, void *i
);
237 static void handle_resolve_request(request_state
*rstate
);
238 static void question_result_callback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
);
239 static void question_termination_callback(void *context
);
240 static void handle_browse_request(request_state
*request
);
241 static void browse_termination_callback(void *context
);
242 static void browse_result_callback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
);
243 static void handle_regservice_request(request_state
*request
);
244 static void regservice_termination_callback(void *context
);
245 static void process_service_registration(ServiceRecordSet
*const srs
);
246 static void regservice_callback(mDNS
*const m
, ServiceRecordSet
*const srs
, mStatus result
);
247 static void handle_add_request(request_state
*rstate
);
248 static void handle_update_request(request_state
*rstate
);
249 static mStatus
gen_rr_response(domainname
*servicename
, mDNSInterfaceID id
, request_state
*request
, reply_state
**rep
);
250 static void append_reply(request_state
*req
, reply_state
*rep
);
251 static int build_domainname_from_strings(domainname
*srv
, char *name
, char *regtype
, char *domain
);
252 static void enum_termination_callback(void *context
);
253 static void enum_result_callback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
);
254 static void handle_query_request(request_state
*rstate
);
255 static mStatus
do_question(request_state
*rstate
, domainname
*name
, uint32_t ifi
, uint16_t rrtype
, int16_t rrclass
);
256 static reply_state
*format_enumeration_reply(request_state
*rstate
, char *domain
, DNSServiceFlags flags
, uint32_t ifi
, DNSServiceErrorType err
);
257 static void handle_enum_request(request_state
*rstate
);
258 static void handle_regrecord_request(request_state
*rstate
);
259 static void regrecord_callback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
);
260 static void connected_registration_termination(void *context
);
261 static void handle_reconfirm_request(request_state
*rstate
);
262 static AuthRecord
*read_rr_from_ipc_msg(char *msgbuf
, int ttl
);
263 static void handle_removerecord_request(request_state
*rstate
);
264 static void reset_connected_rstate(request_state
*rstate
);
265 static int deliver_error(request_state
*rstate
, mStatus err
);
266 static int deliver_async_error(request_state
*rs
, reply_op_t op
, mStatus err
);
267 static transfer_state
send_undelivered_error(request_state
*rs
);
268 static reply_state
*create_reply(reply_op_t op
, int datalen
, request_state
*request
);
269 static void update_callback(mDNS
*const m
, AuthRecord
*const rr
, RData
*oldrd
);
270 static void my_perror(char *errmsg
);
271 static void unlink_request(request_state
*rs
);
272 static void resolve_result_callback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
);
273 static void resolve_termination_callback(void *context
);
275 // initialization, setup/teardown functions
277 int udsserver_init(void)
280 struct sockaddr_un laddr
;
281 struct rlimit maxfds
;
283 if ((listenfd
= socket(AF_LOCAL
, SOCK_STREAM
, 0)) < 0)
285 unlink(MDNS_UDS_SERVERPATH
); //OK if this fails
286 bzero(&laddr
, sizeof(laddr
));
287 laddr
.sun_family
= AF_LOCAL
;
288 laddr
.sun_len
= sizeof(struct sockaddr_un
);
289 strcpy(laddr
.sun_path
, MDNS_UDS_SERVERPATH
);
291 if (bind(listenfd
, (struct sockaddr
*)&laddr
, sizeof(laddr
)) < 0)
295 if (fcntl(listenfd
, F_SETFL
, O_NONBLOCK
) < 0)
297 my_perror("ERROR: could not set listen socket to non-blocking mode");
300 listen(listenfd
, LISTENQ
);
303 // set maximum file descriptor to 1024
304 if (getrlimit(RLIMIT_NOFILE
, &maxfds
) < 0)
306 my_perror("ERROR: Unable to get file descriptor limit");
309 if (maxfds
.rlim_max
>= MAX_OPENFILES
&& maxfds
.rlim_cur
== maxfds
.rlim_max
)
311 // proper values already set
314 maxfds
.rlim_max
= MAX_OPENFILES
;
315 maxfds
.rlim_cur
= MAX_OPENFILES
;
316 if (setrlimit(RLIMIT_NOFILE
, &maxfds
) < 0)
317 my_perror("ERROR: Unable to set maximum file descriptor limit");
321 my_perror("ERROR: udsserver_init");
325 int udsserver_exit(void)
328 unlink(MDNS_UDS_SERVERPATH
);
333 // add the named socket as a runloop source
334 int udsserver_add_rl_source(void)
336 CFSocketContext context
= { 0, NULL
, NULL
, NULL
, NULL
};
337 CFSocketRef sr
= CFSocketCreateWithNative(kCFAllocatorDefault
, listenfd
, kCFSocketReadCallBack
, connect_callback
, &context
);
340 debugf("ERROR: udsserver_add_rl_source - CFSocketCreateWithNative");
343 CFRunLoopSourceRef rls
= CFSocketCreateRunLoopSource(kCFAllocatorDefault
, sr
, 0);
347 debugf("ERROR: udsserver_add_rl_source - CFSocketCreateRunLoopSource");
350 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
354 mDNSs32
udsserver_idle(mDNSs32 nextevent
)
356 request_state
*req
= all_requests
, *tmp
, *prev
= NULL
;
358 transfer_state result
;
359 mDNSs32 now
= mDNSPlatformTimeNow();
364 result
= t_uninitialized
;
366 result
= send_undelivered_error(req
);
367 if (result
!= t_error
&& result
!= t_morecoming
&& // don't try to send msg if send_error failed
368 (req
->ts
== t_complete
|| req
->ts
== t_morecoming
))
372 if (req
->replies
->next
) req
->replies
->rhdr
->flags
|= kDNSServiceFlagsMoreComing
;
373 else req
->replies
->rhdr
->flags
|= kDNSServiceFlagsFinished
;
374 result
= send_msg(req
->replies
);
375 if (result
== t_complete
)
378 req
->replies
= req
->replies
->next
;
379 freeL("udsserver_idle", fptr
);
381 else if (result
== t_terminated
|| result
== t_error
)
386 else if (result
== t_morecoming
) // client's queues are full, move to next
388 if (nextevent
- now
> mDNSPlatformOneSecond
)
389 nextevent
= now
+ mDNSPlatformOneSecond
;
390 break; // start where we left off in a second
394 if (result
== t_terminated
|| result
== t_error
)
395 //since we're already doing a list traversal, we unlink the request manunally instead of calling unlink_request()
398 if (prev
) prev
->next
= req
->next
;
399 if (req
== all_requests
) all_requests
= all_requests
->next
;
401 freeL("udsserver_idle", tmp
);
412 void udsserver_info(void)
415 for (req
= all_requests
; req
; req
=req
->next
)
417 void *t
= req
->termination_context
;
419 if (req
->terminate
== regservice_termination_callback
)
420 LogMsg("DNSServiceRegister %##s", ((registered_service
*) t
)->srs
->RR_SRV
.resrec
.name
.c
);
421 else if (req
->terminate
== browse_termination_callback
)
422 LogMsg("DNSServiceBrowse %##s", ((DNSQuestion
*) t
)->qname
.c
);
423 else if (req
->terminate
== resolve_termination_callback
)
424 LogMsg("DNSServiceResolve %##s", ((resolve_termination_t
*)t
)->srv
->question
.qname
.c
);
425 else if (req
->terminate
== question_termination_callback
)
426 LogMsg("DNSServiceQueryRecord %##s", ((DNSQuestion
*) t
)->qname
.c
);
427 else if (req
->terminate
== enum_termination_callback
)
428 LogMsg("DNSServiceEnumerateDomains %##s", ((enum_termination_t
*) t
)->all
->question
.qname
.c
);
432 void udsserver_handle_configchange(void)
434 registered_service
*srv
;
438 for (req
= all_requests
; req
; req
= req
->next
)
441 if (srv
->autoname
&& !SameDomainLabel(srv
->name
.c
, mDNSStorage
.nicelabel
.c
))
443 srv
->rename_on_memfree
= 1;
444 err
= mDNS_DeregisterService(&mDNSStorage
, srv
->srs
);
445 if (err
) LogMsg("ERROR: udsserver_handle_configchange: DeregisterService returned error %d. Continuing.", err
);
446 // error should never occur - safest to log and continue
454 // accept a connection on the named socket, adding the new descriptor to the runloop and passing the error
455 // descriptor to the client
456 static void connect_callback(CFSocketRef s
, CFSocketCallBackType t
, CFDataRef dr
, const void *c
, void *i
)
458 int sd
, clilen
, optval
;
459 struct sockaddr_un cliaddr
;
460 CFSocketContext context
= { 0, NULL
, NULL
, NULL
, NULL
};
461 request_state
*rstate
;
464 #pragma unused(s, t, dr, c, i)
466 clilen
= sizeof(cliaddr
);
467 sd
= accept(listenfd
, (struct sockaddr
*)&cliaddr
, &clilen
);
471 if (errno
== EWOULDBLOCK
) return;
472 my_perror("ERROR: accept");
476 if (setsockopt(sd
, SOL_SOCKET
, SO_NOSIGPIPE
, &optval
, sizeof(optval
)) < 0)
478 my_perror("ERROR: setsockopt - SOL_NOSIGPIPE - aborting client");
483 if (fcntl(sd
, F_SETFL
, O_NONBLOCK
) < 0)
485 my_perror("ERROR: could not set connected socket to non-blocking mode - aborting client");
491 // open a pipe to deliver error messages, pass descriptor to client
492 if (pipe(errpipe) < 0)
494 my_perror("ERROR: could not create pipe");
498 if (ioctl(sd, I_SENDFD, errpipe[0]) < 0)
500 my_perror("ERROR: could not pass pipe descriptor to client. Aborting client.\n");
504 if (fcntl(errpipe[1], F_SETFL, O_NONBLOCK) < 0)
506 my_perror("ERROR: could not set error pipe to non-blocking mode - aborting client");
513 // allocate a request_state struct that will live with the socket
514 rstate
= mallocL("connect_callback", sizeof(request_state
));
517 my_perror("ERROR: malloc");
520 bzero(rstate
, sizeof(request_state
));
521 rstate
->ts
= t_morecoming
;
523 //rstate->errfd = errpipe[1];
525 //now create CFSocket wrapper and add to run loop
526 context
.info
= rstate
;
527 rstate
->sr
= CFSocketCreateWithNative(kCFAllocatorDefault
, sd
, kCFSocketReadCallBack
, request_callback
, &context
);
530 debugf("ERROR: connect_callback - CFSocketCreateWithNative");
531 freeL("connect_callback", rstate
);
535 rstate
->rls
= CFSocketCreateRunLoopSource(kCFAllocatorDefault
, rstate
->sr
, 0);
538 debugf("ERROR: connect_callback - CFSocketCreateRunLoopSource");
539 CFSocketInvalidate(rstate
->sr
); // automatically closes socket
540 CFRelease(rstate
->sr
);
541 freeL("connect_callback", rstate
);
544 CFRunLoopAddSource(CFRunLoopGetCurrent(), rstate
->rls
, kCFRunLoopDefaultMode
);
545 if (!CFRunLoopContainsSource(CFRunLoopGetCurrent(), rstate
->rls
, kCFRunLoopDefaultMode
))
547 LogMsg("ERROR: connect_callback, CFRunLoopAddSource");
548 abort_request(rstate
);
551 rstate
->next
= all_requests
;
552 all_requests
= rstate
;
556 // main client request handling routine. reads request and calls the appropriate request-specific
558 static void request_callback(CFSocketRef sr
, CFSocketCallBackType t
, CFDataRef dr
, const void *context
, void *info
)
560 request_state
*rstate
= info
;
561 transfer_state result
;
562 struct sockaddr_un cliaddr
;
563 char ctrl_path
[MAX_CTLPATH
];
565 #pragma unused(sr, t, dr, context)
567 int native
= CFSocketGetNative(sr
);
568 if (native
!= rstate
->sd
)
570 LogMsg("ERROR: request_callback - CFSocket's native descriptor does not match rstate member descriptor.");
571 abort_request(rstate
);
572 unlink_request(rstate
);
576 result
= read_msg(rstate
);
577 if (result
== t_morecoming
)
581 if (result
== t_terminated
)
583 abort_request(rstate
);
584 unlink_request(rstate
);
587 if (result
== t_error
)
589 abort_request(rstate
);
590 unlink_request(rstate
);
594 if (rstate
->hdr
.version
!= VERSION
)
596 LogMsg("ERROR: client incompatible with daemon (client version = %d, "
597 "daemon version = %d)\n", rstate
->hdr
.version
, VERSION
);
598 abort_request(rstate
);
599 unlink_request(rstate
);
603 // check if client wants silent operation
604 if (rstate
->hdr
.flags
& IPC_FLAGS_NOREPLY
) rstate
->no_reply
= 1;
606 // check if primary socket is to be used for synchronous errors, else open new socket
607 if (rstate
->hdr
.flags
& IPC_FLAGS_REUSE_SOCKET
)
608 rstate
->errfd
= rstate
->sd
;
611 if ((rstate
->errfd
= socket(AF_LOCAL
, SOCK_STREAM
, 0)) < 0)
613 my_perror("ERROR: socket");
616 if (fcntl(rstate
->errfd
, F_SETFL
, O_NONBLOCK
) < 0)
618 my_perror("ERROR: could not set control socket to non-blocking mode");
619 abort_request(rstate
);
620 unlink_request(rstate
);
623 get_string(&rstate
->msgdata
, ctrl_path
, 256); // path is first element in message buffer
624 bzero(&cliaddr
, sizeof(cliaddr
));
625 cliaddr
.sun_family
= AF_LOCAL
;
626 strcpy(cliaddr
.sun_path
, ctrl_path
);
627 if (connect(rstate
->errfd
, (struct sockaddr
*)&cliaddr
, sizeof(cliaddr
)) < 0)
629 my_perror("ERROR: connect");
630 abort_request(rstate
);
631 unlink_request(rstate
);
638 switch(rstate
->hdr
.op
.request_op
)
640 case resolve_request
: handle_resolve_request(rstate
); break;
641 case query_request
: handle_query_request(rstate
); break;
642 case browse_request
: handle_browse_request(rstate
); break;
643 case reg_service_request
: handle_regservice_request(rstate
); break;
644 case enumeration_request
: handle_enum_request(rstate
); break;
645 case reg_record_request
: handle_regrecord_request(rstate
); break;
646 case add_record_request
: handle_add_request(rstate
); break;
647 case update_record_request
: handle_update_request(rstate
); break;
648 case remove_record_request
: handle_removerecord_request(rstate
); break;
649 case reconfirm_record_request
: handle_reconfirm_request(rstate
); break;
651 debugf("ERROR: udsserver_recv_request - unsupported request type: %d", rstate
->hdr
.op
.request_op
);
655 // mDNS operation functions. Each operation has 3 associated functions - a request handler that parses
656 // the client's request and makes the appropriate mDNSCore call, a result handler (passed as a callback
657 // to the mDNSCore routine) that sends results back to the client, and a termination routine that aborts
658 // the mDNSCore operation if the client dies or closes its socket.
661 // query and resolve calls have separate request handlers that parse the arguments from the client and
662 // massage the name parameters appropriately, but the rest of the operations (making the query call,
663 // delivering the result to the client, and termination) are identical.
665 static void handle_query_request(request_state
*rstate
)
667 DNSServiceFlags flags
;
668 uint32_t interfaceIndex
;
670 uint16_t rrtype
, rrclass
;
675 if (rstate
->ts
!= t_complete
)
677 LogMsg("ERROR: handle_query_request - transfer state != t_complete");
680 ptr
= rstate
->msgdata
;
683 LogMsg("ERROR: handle_query_request - NULL msgdata");
686 flags
= get_flags(&ptr
);
687 interfaceIndex
= get_long(&ptr
);
688 if (get_string(&ptr
, name
, 256) < 0) goto bad_param
;
689 rrtype
= get_short(&ptr
);
690 rrclass
= get_short(&ptr
);
691 if (!MakeDomainNameFromDNSNameString(&dname
, name
)) goto bad_param
;
692 result
= do_question(rstate
, &dname
, interfaceIndex
, rrtype
, rrclass
);
693 if (result
) rstate
->terminate
= NULL
;
694 if (deliver_error(rstate
, result
) < 0) goto error
;
698 deliver_error(rstate
, mStatus_BadParamErr
);
699 rstate
->terminate
= NULL
; // don't try to terminate insuccessful Core calls
701 abort_request(rstate
);
702 unlink_request(rstate
);
706 static void handle_resolve_request(request_state
*rstate
)
708 DNSServiceFlags flags
;
709 uint32_t interfaceIndex
;
710 char name
[256], regtype
[256], domain
[256];
711 char *ptr
; // message data pointer
713 resolve_t
*srv
, *txt
;
714 resolve_termination_t
*term
;
717 if (rstate
->ts
!= t_complete
)
719 LogMsg("ERROR: handle_resolve_request - transfer state != t_complete");
720 abort_request(rstate
);
721 unlink_request(rstate
);
725 // extract the data from the message
726 ptr
= rstate
->msgdata
;
729 LogMsg("ERROR: handle_resolve_request - NULL msgdata");
730 abort_request(rstate
);
731 unlink_request(rstate
);
734 flags
= get_flags(&ptr
);
735 interfaceIndex
= get_long(&ptr
);
736 mDNSInterfaceID InterfaceID
= mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage
, interfaceIndex
);
737 if (interfaceIndex
&& !InterfaceID
) goto bad_param
;
738 if (get_string(&ptr
, name
, 256) < 0 ||
739 get_string(&ptr
, regtype
, 256) < 0 ||
740 get_string(&ptr
, domain
, 256) < 0)
743 // free memory in rstate since we don't need it anymore
744 freeL("handle_resolve_request", rstate
->msgbuf
);
745 rstate
->msgbuf
= NULL
;
747 if (build_domainname_from_strings(&fqdn
, name
, regtype
, domain
) < 0)
750 // allocate question wrapper structs
751 srv
= mallocL("handle_resolve_request", sizeof(resolve_t
));
752 txt
= mallocL("handle_resolve_request", sizeof(resolve_t
));
753 if (!srv
|| !txt
) goto malloc_error
;
754 srv
->qtype
= kDNSType_SRV
;
755 txt
->qtype
= kDNSType_TXT
;
756 srv
->rstate
= rstate
;
757 txt
->rstate
= rstate
;
760 srv
->question
.QuestionContext
= rstate
;
761 srv
->question
.QuestionCallback
= resolve_result_callback
;
762 memcpy(&srv
->question
.qname
, &fqdn
, MAX_DOMAIN_NAME
);
763 srv
->question
.qtype
= kDNSType_SRV
;
764 srv
->question
.qclass
= kDNSClass_IN
;
765 srv
->question
.InterfaceID
= InterfaceID
;
767 txt
->question
.QuestionContext
= rstate
;
768 txt
->question
.QuestionCallback
= resolve_result_callback
;
769 memcpy(&txt
->question
.qname
, &fqdn
, MAX_DOMAIN_NAME
);
770 txt
->question
.qtype
= kDNSType_TXT
;
771 txt
->question
.qclass
= kDNSClass_IN
;
772 txt
->question
.InterfaceID
= InterfaceID
;
774 // set up termination info
775 term
= mallocL("handle_resolve_request", sizeof(resolve_termination_t
));
776 if (!term
) goto malloc_error
;
779 term
->rstate
= rstate
;
780 rstate
->termination_context
= term
;
781 rstate
->terminate
= resolve_termination_callback
;
783 // set up reply wrapper struct (since answer will come via 2 callbacks)
784 rstate
->resolve_results
= mallocL("handle_resolve_response", sizeof(resolve_result_t
));
785 if (!rstate
->resolve_results
) goto malloc_error
;
786 bzero(rstate
->resolve_results
, sizeof(resolve_result_t
));
789 err
= mDNS_StartQuery(&mDNSStorage
, &srv
->question
);
790 if (!err
) err
= mDNS_StartQuery(&mDNSStorage
, &txt
->question
);
794 freeL("handle_resolve_request", txt
);
795 freeL("handle_resolve_request", srv
);
796 freeL("handle_resolve_request", term
);
797 freeL("handle_resolve_request", rstate
->resolve_results
);
798 rstate
->terminate
= NULL
; // prevent abort_request() from invoking termination callback
800 if (deliver_error(rstate
, err
) < 0 || err
)
802 abort_request(rstate
);
803 unlink_request(rstate
);
808 deliver_error(rstate
, mStatus_BadParamErr
);
809 abort_request(rstate
);
810 unlink_request(rstate
);
814 my_perror("ERROR: malloc");
818 static void resolve_termination_callback(void *context
)
820 resolve_termination_t
*term
= context
;
825 LogMsg("ERROR: resolve_termination_callback: double termination");
830 mDNS_StopQuery(&mDNSStorage
, &term
->txt
->question
);
831 mDNS_StopQuery(&mDNSStorage
, &term
->srv
->question
);
833 freeL("resolve_termination_callback", term
->txt
);
834 freeL("resolve_termination_callback", term
->srv
);
835 freeL("resolve_termination_callback", term
);
836 rs
->termination_context
= NULL
;
837 freeL("resolve_termination_callback", rs
->resolve_results
);
838 rs
->resolve_results
= NULL
;
843 static void resolve_result_callback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
846 char fullname
[MAX_DOMAIN_NAME
], target
[MAX_DOMAIN_NAME
];
848 transfer_state result
;
850 request_state
*rs
= question
->QuestionContext
;
851 resolve_result_t
*res
= rs
->resolve_results
;
856 if (answer
->rrtype
== kDNSType_TXT
&& res
->txt
== answer
) res
->txt
= mDNSNULL
;
857 if (answer
->rrtype
== kDNSType_SRV
&& res
->srv
== answer
) res
->srv
= mDNSNULL
;
861 if (answer
->rrtype
== kDNSType_TXT
) res
->txt
= answer
;
862 if (answer
->rrtype
== kDNSType_SRV
) res
->srv
= answer
;
864 if (!res
->txt
|| !res
->srv
) return; // only deliver result to client if we have both answers
866 ConvertDomainNameToCString(&answer
->name
, fullname
);
867 ConvertDomainNameToCString(&res
->srv
->rdata
->u
.srv
.target
, target
);
869 // calculate reply length
870 len
+= sizeof(DNSServiceFlags
);
871 len
+= sizeof(uint32_t); // interface index
872 len
+= sizeof(DNSServiceErrorType
);
873 len
+= strlen(fullname
) + 1;
874 len
+= strlen(target
) + 1;
875 len
+= 2 * sizeof(uint16_t); // port, txtLen
876 len
+= res
->txt
->rdlength
;
878 // allocate/init reply header
879 rep
= create_reply(resolve_reply
, len
, rs
);
880 rep
->rhdr
->flags
= 0;
881 rep
->rhdr
->ifi
= mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage
, answer
->InterfaceID
);
882 rep
->rhdr
->error
= kDNSServiceErr_NoError
;
885 // write reply data to message
886 put_string(fullname
, &data
);
887 put_string(target
, &data
);
888 put_short(res
->srv
->rdata
->u
.srv
.port
.NotAnInteger
, &data
);
889 put_short(res
->txt
->rdlength
, &data
);
890 put_rdata(res
->txt
->rdlength
, res
->txt
->rdata
->u
.txt
.c
, &data
);
892 result
= send_msg(rep
);
893 if (result
== t_error
|| result
== t_terminated
)
897 freeL("resolve_result_callback", rep
);
899 else if (result
== t_complete
) freeL("resolve_result_callback", rep
);
900 else append_reply(rs
, rep
);
906 // common query issuing routine for resolve and query requests
907 static mStatus
do_question(request_state
*rstate
, domainname
*name
, uint32_t ifi
, uint16_t rrtype
, int16_t rrclass
)
911 mDNSInterfaceID InterfaceID
= mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage
, ifi
);
912 if (ifi
&& !InterfaceID
) return(mStatus_BadParamErr
);
914 q
= mallocL("do_question", sizeof(DNSQuestion
));
917 my_perror("ERROR: do_question - malloc");
920 bzero(q
, sizeof(DNSQuestion
));
922 q
->QuestionContext
= rstate
;
923 q
->QuestionCallback
= question_result_callback
;
924 memcpy(&q
->qname
, name
, MAX_DOMAIN_NAME
);
927 q
->InterfaceID
= InterfaceID
;
930 rstate
->termination_context
= q
;
931 rstate
->terminate
= question_termination_callback
;
933 result
= mDNS_StartQuery(&mDNSStorage
, q
);
934 if (result
!= mStatus_NoError
) LogMsg("ERROR: mDNS_StartQuery: %d", (int)result
);
938 // what gets called when a resolve is completed and we need to send the data back to the client
939 static void question_result_callback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
948 //mDNS_StopQuery(m, question);
949 req
= question
->QuestionContext
;
951 // calculate reply data length
952 len
= sizeof(DNSServiceFlags
);
953 len
+= 2 * sizeof(uint32_t); // if index + ttl
954 len
+= sizeof(DNSServiceErrorType
);
955 len
+= 3 * sizeof(uint16_t); // type, class, rdlen
956 len
+= answer
->rdlength
;
957 ConvertDomainNameToCString(&answer
->name
, name
);
958 len
+= strlen(name
) + 1;
960 rep
= create_reply(query_reply
, len
, req
);
961 rep
->rhdr
->flags
= AddRecord
? kDNSServiceFlagsAdd
: kDNSServiceFlagsRemove
;
962 rep
->rhdr
->ifi
= mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage
, answer
->InterfaceID
);
963 rep
->rhdr
->error
= kDNSServiceErr_NoError
;
966 put_string(name
, &data
);
967 put_short(answer
->rrtype
, &data
);
968 put_short(answer
->rrclass
, &data
);
969 put_short(answer
->rdlength
, &data
);
970 put_rdata(answer
->rdlength
, (char *)&answer
->rdata
->u
, &data
);
971 put_long(AddRecord
? answer
->rroriginalttl
: 0, &data
);
973 append_reply(req
, rep
);
977 static void question_termination_callback(void *context
)
979 DNSQuestion
*q
= context
;
982 mDNS_StopQuery(&mDNSStorage
, q
); // no need to error check
983 freeL("question_termination_callback", q
);
987 static void handle_browse_request(request_state
*request
)
989 DNSServiceFlags flags
;
990 uint32_t interfaceIndex
;
991 char regtype
[256], domain
[256];
993 domainname typedn
, domdn
;
997 if (request
->ts
!= t_complete
)
999 LogMsg("ERROR: handle_browse_request - transfer state != t_complete");
1000 abort_request(request
);
1001 unlink_request(request
);
1004 q
= mallocL("handle_browse_request", sizeof(DNSQuestion
));
1007 my_perror("ERROR: handle_browse_request - malloc");
1010 bzero(q
, sizeof(DNSQuestion
));
1012 // extract data from message
1013 ptr
= request
->msgdata
;
1014 flags
= get_flags(&ptr
);
1015 interfaceIndex
= get_long(&ptr
);
1016 if (get_string(&ptr
, regtype
, 256) < 0 ||
1017 get_string(&ptr
, domain
, 256) < 0)
1020 freeL("handle_browse_request", request
->msgbuf
);
1021 request
->msgbuf
= NULL
;
1023 mDNSInterfaceID InterfaceID
= mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage
, interfaceIndex
);
1024 if (interfaceIndex
&& !InterfaceID
) goto bad_param
;
1025 q
->QuestionContext
= request
;
1026 q
->QuestionCallback
= browse_result_callback
;
1027 if (!MakeDomainNameFromDNSNameString(&typedn
, regtype
) ||
1028 !MakeDomainNameFromDNSNameString(&domdn
, domain
[0] ? domain
: "local."))
1030 request
->termination_context
= q
;
1031 request
->terminate
= browse_termination_callback
;
1032 result
= mDNS_StartBrowse(&mDNSStorage
, q
, &typedn
, &domdn
, InterfaceID
, browse_result_callback
, request
);
1033 deliver_error(request
, result
);
1037 deliver_error(request
, mStatus_BadParamErr
);
1038 abort_request(request
);
1039 unlink_request(request
);
1042 static void browse_result_callback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
1049 req
= question
->QuestionContext
;
1051 err
= gen_rr_response(&answer
->rdata
->u
.name
, answer
->InterfaceID
, req
, &rep
);
1054 if (deliver_async_error(req
, browse_reply
, err
) < 0)
1057 unlink_request(req
);
1061 if (AddRecord
) rep
->rhdr
->flags
|= kDNSServiceFlagsAdd
; // non-zero TTL indicates add
1062 append_reply(req
, rep
);
1066 static void browse_termination_callback(void *context
)
1068 DNSQuestion
*q
= context
;
1070 mDNS_StopBrowse(&mDNSStorage
, q
); // no need to error-check result
1071 freeL("browse_termination_callback", q
);
1074 // service registration
1075 static void handle_regservice_request(request_state
*request
)
1077 DNSServiceFlags flags
;
1079 char name
[256], regtype
[256], domain
[256], host
[256];
1085 domainname t
, d
, h
, srv
;
1086 registered_service
*r_srv
;
1090 char *sub
, *rtype_ptr
;
1091 int i
, num_subtypes
;
1094 if (request
->ts
!= t_complete
)
1096 LogMsg("ERROR: handle_regservice_request - transfer state != t_complete");
1097 abort_request(request
);
1098 unlink_request(request
);
1102 // extract data from message
1103 ptr
= request
->msgdata
;
1104 flags
= get_flags(&ptr
);
1105 ifi
= get_long(&ptr
);
1106 mDNSInterfaceID InterfaceID
= mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage
, ifi
);
1107 if (ifi
&& !InterfaceID
) goto bad_param
;
1108 if (get_string(&ptr
, name
, 256) < 0 ||
1109 get_string(&ptr
, regtype
, 256) < 0 ||
1110 get_string(&ptr
, domain
, 256) < 0 ||
1111 get_string(&ptr
, host
, 256) < 0)
1114 port
.NotAnInteger
= get_short(&ptr
);
1115 txtlen
= get_short(&ptr
);
1116 txtdata
= get_rdata(&ptr
, txtlen
);
1118 // count subtypes, replacing commas w/ whitespace
1119 rtype_ptr
= regtype
;
1121 while((sub
= strsep(&rtype_ptr
, ",")))
1122 if (*sub
) num_subtypes
++;
1124 if (!name
[0]) n
= (&mDNSStorage
)->nicelabel
;
1125 else if (!MakeDomainLabelFromLiteralString(&n
, name
))
1127 if ((!regtype
[0] || !MakeDomainNameFromDNSNameString(&t
, regtype
)) ||
1128 (!MakeDomainNameFromDNSNameString(&d
, *domain
? domain
: "local.")) ||
1129 (!ConstructServiceName(&srv
, &n
, &t
, &d
)))
1131 if (host
[0] && !MakeDomainNameFromDNSNameString(&h
, host
)) goto bad_param
;
1133 r_srv
= mallocL("handle_regservice_request", sizeof(registered_service
));
1134 if (!r_srv
) goto malloc_error
;
1135 srs_size
= sizeof(ServiceRecordSet
) + (sizeof(RDataBody
) > txtlen
? 0 : txtlen
- sizeof(RDataBody
));
1136 r_srv
->srs
= mallocL("handle_regservice_request", srs_size
);
1137 if (!r_srv
->srs
) goto malloc_error
;
1138 if (num_subtypes
> 0)
1140 r_srv
->subtypes
= mallocL("handle_regservice_request", num_subtypes
* sizeof(AuthRecord
));
1141 if (!r_srv
->subtypes
) goto malloc_error
;
1142 sub
= regtype
+ strlen(regtype
) + 1;
1143 for (i
= 0; i
< num_subtypes
; i
++)
1145 if (!MakeDomainNameFromDNSNameString(&(r_srv
->subtypes
+ i
)->resrec
.name
, sub
))
1147 freeL("handle_regservice_request", r_srv
->subtypes
);
1148 freeL("handle_regservice_request", r_srv
);
1152 sub
+= strlen(sub
) + 1;
1155 else r_srv
->subtypes
= NULL
;
1156 r_srv
->request
= request
;
1158 r_srv
->autoname
= (!name
[0]);
1159 r_srv
->rename_on_memfree
= 0;
1160 r_srv
->renameonconflict
= !(flags
& kDNSServiceFlagsNoAutoRename
);
1162 request
->termination_context
= r_srv
;
1163 request
->terminate
= regservice_termination_callback
;
1164 request
->service
= r_srv
;
1166 result
= mDNS_RegisterService(&mDNSStorage
, r_srv
->srs
, &n
, &t
, &d
, host
[0] ? &h
: NULL
, port
,
1167 txtdata
, txtlen
, r_srv
->subtypes
, num_subtypes
, InterfaceID
, regservice_callback
, r_srv
);
1168 deliver_error(request
, result
);
1169 if (result
!= mStatus_NoError
)
1171 abort_request(request
);
1172 unlink_request(request
);
1176 reset_connected_rstate(request
); // reset to receive add/remove messages
1181 deliver_error(request
, mStatus_BadParamErr
);
1182 abort_request(request
);
1183 unlink_request(request
);
1187 my_perror("ERROR: malloc");
1191 // service registration callback performs three duties - frees memory for deregistered services,
1192 // handles name conflicts, and delivers completed registration information to the client (via
1193 // process_service_registraion())
1195 static void regservice_callback(mDNS
*const m
, ServiceRecordSet
*const srs
, mStatus result
)
1198 ExtraResourceRecord
*extra
;
1199 registered_service
*r_srv
= srs
->ServiceContext
;
1200 request_state
*rs
= r_srv
->request
;
1204 if (!rs
&& (result
!= mStatus_MemFree
&& !r_srv
->rename_on_memfree
))
1206 // error should never happen - safest to log and continue
1207 LogMsg("ERROR: regservice_callback: received result %d with a NULL request pointer\n");
1211 if (result
== mStatus_NoError
)
1212 return process_service_registration(srs
);
1213 else if (result
== mStatus_MemFree
)
1215 if (r_srv
->rename_on_memfree
)
1217 r_srv
->rename_on_memfree
= 0;
1218 r_srv
->name
= mDNSStorage
.nicelabel
;
1219 err
= mDNS_RenameAndReregisterService(&mDNSStorage
, srs
, &r_srv
->name
);
1220 if (err
) LogMsg("ERROR: regservice_callback - RenameAndReregisterService returned %d", err
);
1221 // error should never happen - safest to log and continue
1225 while (r_srv
->srs
->Extras
)
1227 extra
= r_srv
->srs
->Extras
;
1228 r_srv
->srs
->Extras
= r_srv
->srs
->Extras
->next
;
1229 freeL("regservice_callback", extra
);
1231 freeL("regservice_callback", r_srv
->srs
);
1232 if (r_srv
->subtypes
) freeL("regservice_callback", r_srv
->subtypes
);
1233 if (r_srv
->request
) r_srv
->request
->service
= NULL
;
1234 freeL("regservice_callback", r_srv
);
1238 else if (result
== mStatus_NameConflict
)
1240 if (r_srv
->autoname
|| r_srv
->renameonconflict
)
1242 mDNS_RenameAndReregisterService(&mDNSStorage
, srs
, mDNSNULL
);
1247 freeL("regservice_callback", r_srv
);
1248 freeL("regservice_callback", r_srv
->srs
);
1249 if (r_srv
->subtypes
) freeL("regservice_callback", r_srv
->subtypes
);
1250 if (r_srv
->request
) r_srv
->request
->service
= NULL
;
1251 freeL("regservice_callback", r_srv
);
1252 if (deliver_async_error(rs
, reg_service_reply
, result
) < 0)
1262 LogMsg("ERROR: unknown result in regservice_callback");
1263 if (deliver_async_error(rs
, reg_service_reply
, result
) < 0)
1272 static void handle_add_request(request_state
*rstate
)
1274 registered_record_entry
*re
;
1275 ExtraResourceRecord
*extra
;
1277 uint16_t rrtype
, rdlen
;
1280 DNSServiceFlags flags
;
1281 ServiceRecordSet
*srs
= rstate
->service
->srs
;
1285 LogMsg("ERROR: handle_add_request - no service record set in request state");
1286 deliver_error(rstate
, mStatus_UnknownErr
);
1290 ptr
= rstate
->msgdata
;
1291 flags
= get_flags(&ptr
);
1292 rrtype
= get_short(&ptr
);
1293 rdlen
= get_short(&ptr
);
1294 rdata
= get_rdata(&ptr
, rdlen
);
1295 ttl
= get_long(&ptr
);
1297 if (rdlen
> sizeof(RDataBody
)) size
= rdlen
;
1298 else size
= sizeof(RDataBody
);
1300 extra
= mallocL("hanle_add_request", sizeof(ExtraResourceRecord
) - sizeof(RDataBody
) + size
);
1303 my_perror("ERROR: malloc");
1307 bzero(extra
, sizeof(ExtraResourceRecord
)); // OK if oversized rdata not zero'd
1308 extra
->r
.resrec
.rrtype
= rrtype
;
1309 extra
->r
.rdatastorage
.MaxRDLength
= size
;
1310 extra
->r
.resrec
.rdlength
= rdlen
;
1311 memcpy(&extra
->r
.rdatastorage
.u
.data
, rdata
, rdlen
);
1312 result
= mDNS_AddRecordToService(&mDNSStorage
, srs
, extra
, &extra
->r
.rdatastorage
, ttl
);
1313 deliver_error(rstate
, result
);
1314 reset_connected_rstate(rstate
);
1317 freeL("handle_add_request", rstate
->msgbuf
);
1318 rstate
->msgbuf
= NULL
;
1319 freeL("handle_add_request", extra
);
1322 re
= mallocL("handle_add_request", sizeof(registered_record_entry
));
1325 my_perror("ERROR: malloc");
1328 re
->key
= rstate
->hdr
.reg_index
;
1330 re
->next
= rstate
->reg_recs
;
1331 rstate
->reg_recs
= re
;
1334 static void handle_update_request(request_state
*rstate
)
1336 registered_record_entry
*reptr
;
1339 uint16_t rdlen
, rdsize
;
1344 if (rstate
->hdr
.reg_index
== TXT_RECORD_INDEX
)
1346 if (!rstate
->service
)
1348 deliver_error(rstate
, mStatus_BadParamErr
);
1351 rr
= &rstate
->service
->srs
->RR_TXT
;
1355 reptr
= rstate
->reg_recs
;
1356 while(reptr
&& reptr
->key
!= rstate
->hdr
.reg_index
) reptr
= reptr
->next
;
1357 if (!reptr
) deliver_error(rstate
, mStatus_BadReferenceErr
);
1361 ptr
= rstate
->msgdata
;
1362 get_flags(&ptr
); // flags unused
1363 rdlen
= get_short(&ptr
);
1364 rdata
= get_rdata(&ptr
, rdlen
);
1365 ttl
= get_long(&ptr
);
1367 if (rdlen
> sizeof(RDataBody
)) rdsize
= rdlen
;
1368 else rdsize
= sizeof(RDataBody
);
1369 newrd
= mallocL("handle_update_request", sizeof(RData
) - sizeof(RDataBody
) + rdsize
);
1372 my_perror("ERROR: malloc");
1375 newrd
->MaxRDLength
= rdsize
;
1376 memcpy(&newrd
->u
, rdata
, rdlen
);
1377 result
= mDNS_Update(&mDNSStorage
, rr
, ttl
, rdlen
, newrd
, update_callback
);
1378 deliver_error(rstate
, result
);
1379 reset_connected_rstate(rstate
);
1382 static void update_callback(mDNS
*const m
, AuthRecord
*const rr
, RData
*oldrd
)
1386 if (oldrd
!= &rr
->rdatastorage
) freeL("update_callback", oldrd
);
1389 static void process_service_registration(ServiceRecordSet
*const srs
)
1392 transfer_state send_result
;
1394 registered_service
*r_srv
= srs
->ServiceContext
;
1395 request_state
*req
= r_srv
->request
;
1398 err
= gen_rr_response(&srs
->RR_SRV
.resrec
.name
, srs
->RR_SRV
.resrec
.InterfaceID
, req
, &rep
);
1401 if (deliver_async_error(req
, reg_service_reply
, err
) < 0)
1404 unlink_request(req
);
1408 send_result
= send_msg(rep
);
1409 if (send_result
== t_error
|| send_result
== t_terminated
)
1412 unlink_request(req
);
1413 freeL("process_service_registration", rep
);
1415 else if (send_result
== t_complete
) freeL("process_service_registration", rep
);
1416 else append_reply(req
, rep
);
1419 static void regservice_termination_callback(void *context
)
1421 registered_service
*srv
= context
;
1423 // only safe to free memory if registration is not valid, ie deregister fails
1424 if (mDNS_DeregisterService(&mDNSStorage
, srv
->srs
) != mStatus_NoError
)
1426 freeL("regservice_callback", srv
->srs
);
1427 if (srv
->subtypes
) freeL("regservice_callback", srv
->subtypes
);
1428 freeL("regservice_callback", srv
);
1429 freeL("regservice_termination_callback", srv
);
1434 static void handle_regrecord_request(request_state
*rstate
)
1437 regrecord_callback_context
*rcc
;
1438 registered_record_entry
*re
;
1441 if (rstate
->ts
!= t_complete
)
1443 LogMsg("ERROR: handle_regrecord_request - transfer state != t_complete");
1444 abort_request(rstate
);
1445 unlink_request(rstate
);
1449 rr
= read_rr_from_ipc_msg(rstate
->msgdata
, 1);
1452 deliver_error(rstate
, mStatus_BadParamErr
);
1456 rcc
= mallocL("hanlde_regrecord_request", sizeof(regrecord_callback_context
));
1457 if (!rcc
) goto malloc_error
;
1458 rcc
->rstate
= rstate
;
1459 rcc
->client_context
= rstate
->hdr
.client_context
;
1460 rr
->RecordContext
= rcc
;
1461 rr
->RecordCallback
= regrecord_callback
;
1463 // allocate registration entry, link into list
1464 re
= mallocL("hanlde_regrecord_request", sizeof(registered_record_entry
));
1465 if (!re
) goto malloc_error
;
1466 re
->key
= rstate
->hdr
.reg_index
;
1468 re
->next
= rstate
->reg_recs
;
1469 rstate
->reg_recs
= re
;
1471 if (!rstate
->terminate
)
1473 rstate
->terminate
= connected_registration_termination
;
1474 rstate
->termination_context
= rstate
;
1477 result
= mDNS_Register(&mDNSStorage
, rr
);
1478 deliver_error(rstate
, result
);
1479 reset_connected_rstate(rstate
);
1483 my_perror("ERROR: malloc");
1487 static void regrecord_callback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
1489 regrecord_callback_context
*rcc
;
1496 if (result
== mStatus_MemFree
) { freeL("regrecord_callback", rr
); return; }
1497 rcc
= rr
->RecordContext
;
1499 // format result, add to the list for the request, including the client context in the header
1500 len
= sizeof(DNSServiceFlags
);
1501 len
+= sizeof(uint32_t); //interfaceIndex
1502 len
+= sizeof(DNSServiceErrorType
);
1504 reply
= create_reply(reg_record_reply
, len
, rcc
->rstate
);
1505 reply
->mhdr
->client_context
= rcc
->client_context
;
1506 reply
->rhdr
->flags
= 0;
1507 reply
->rhdr
->ifi
= mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage
, rr
->resrec
.InterfaceID
);
1508 reply
->rhdr
->error
= result
;
1510 ts
= send_msg(reply
);
1511 if (ts
== t_error
|| ts
== t_terminated
)
1513 abort_request(rcc
->rstate
);
1514 unlink_request(rcc
->rstate
);
1516 else if (ts
== t_complete
) freeL("regrecord_callback", reply
);
1517 else if (ts
== t_morecoming
) append_reply(rcc
->rstate
, reply
); // client is blocked, link reply into list
1520 static void connected_registration_termination(void *context
)
1522 registered_record_entry
*fptr
, *ptr
= ((request_state
*)context
)->reg_recs
;
1525 mDNS_Deregister(&mDNSStorage
, ptr
->rr
);
1528 freeL("connected_registration_termination", fptr
);
1534 static void handle_removerecord_request(request_state
*rstate
)
1536 registered_record_entry
*reptr
, *prev
= NULL
;
1537 mStatus err
= mStatus_UnknownErr
;
1539 reptr
= rstate
->reg_recs
;
1541 ptr
= rstate
->msgdata
;
1542 get_flags(&ptr
); // flags unused
1546 if (reptr
->key
== rstate
->hdr
.reg_index
) // found match
1548 if (prev
) prev
->next
= reptr
->next
;
1549 else rstate
->reg_recs
= reptr
->next
;
1550 err
= mDNS_Deregister(&mDNSStorage
, reptr
->rr
);
1551 freeL("handle_removerecord_request", reptr
); //rr gets freed by callback
1555 reptr
= reptr
->next
;
1557 reset_connected_rstate(rstate
);
1558 if (deliver_error(rstate
, err
) < 0)
1560 abort_request(rstate
);
1561 unlink_request(rstate
);
1566 // domain enumeration
1567 static void handle_enum_request(request_state
*rstate
)
1569 DNSServiceFlags flags
, add_default
;
1571 char *ptr
= rstate
->msgdata
;
1572 domain_enum_t
*def
, *all
;
1573 enum_termination_t
*term
;
1574 reply_state
*reply
; // initial default reply
1579 if (rstate
->ts
!= t_complete
)
1581 LogMsg("ERROR: handle_enum_request - transfer state != t_complete");
1582 abort_request(rstate
);
1583 unlink_request(rstate
);
1587 flags
= get_flags(&ptr
);
1588 ifi
= get_long(&ptr
);
1589 mDNSInterfaceID InterfaceID
= mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage
, ifi
);
1590 if (ifi
&& !InterfaceID
)
1592 deliver_error(rstate
, mStatus_BadParamErr
);
1593 abort_request(rstate
);
1594 unlink_request(rstate
);
1597 // allocate context structures
1598 def
= mallocL("hanlde_enum_request", sizeof(domain_enum_t
));
1599 all
= mallocL("handle_enum_request", sizeof(domain_enum_t
));
1600 term
= mallocL("handle_enum_request", sizeof(enum_termination_t
));
1601 if (!def
|| !all
|| !term
)
1603 my_perror("ERROR: malloc");
1607 // enumeration requires multiple questions, so we must link all the context pointers so that
1608 // necessary context can be reached from the callbacks
1609 def
->rstate
= rstate
;
1610 all
->rstate
= rstate
;
1613 term
->rstate
= rstate
;
1614 rstate
->termination_context
= term
;
1615 rstate
->terminate
= enum_termination_callback
;
1616 def
->question
.QuestionContext
= def
;
1617 def
->type
= (flags
& kDNSServiceFlagsRegistrationDomains
) ?
1618 mDNS_DomainTypeRegistrationDefault
: mDNS_DomainTypeBrowseDefault
;
1619 all
->question
.QuestionContext
= all
;
1620 all
->type
= (flags
& kDNSServiceFlagsRegistrationDomains
) ?
1621 mDNS_DomainTypeRegistration
: mDNS_DomainTypeBrowse
;
1624 err
= mDNS_GetDomains(&mDNSStorage
, &all
->question
, all
->type
, InterfaceID
, enum_result_callback
, all
);
1625 if (err
== mStatus_NoError
)
1626 err
= mDNS_GetDomains(&mDNSStorage
, &def
->question
, def
->type
, InterfaceID
, enum_result_callback
, def
);
1627 result
= deliver_error(rstate
, err
); // send error *before* returning local domain
1629 if (result
< 0 || err
)
1631 abort_request(rstate
);
1632 unlink_request(rstate
);
1636 // provide local. as the first domain automatically
1637 add_default
= kDNSServiceFlagsDefault
| kDNSServiceFlagsAdd
| kDNSServiceFlagsFinished
;
1638 reply
= format_enumeration_reply(rstate
, "local.", add_default
, ifi
, 0);
1639 tr
= send_msg(reply
);
1640 if (tr
== t_error
|| tr
== t_terminated
)
1642 freeL("handle_enum_request", def
);
1643 freeL("handle_enum_request", all
);
1644 abort_request(rstate
);
1645 unlink_request(rstate
);
1648 if (tr
== t_complete
) freeL("handle_enum_request", reply
);
1649 if (tr
== t_morecoming
) append_reply(rstate
, reply
); // couldn't send whole reply because client is blocked - link into list
1652 static void enum_result_callback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
1655 domain_enum_t
*de
= question
->QuestionContext
;
1656 DNSServiceFlags flags
= 0;
1660 if (answer
->rrtype
!= kDNSType_PTR
) return;
1663 flags
|= kDNSServiceFlagsAdd
;
1664 if (de
->type
== mDNS_DomainTypeRegistrationDefault
|| de
->type
== mDNS_DomainTypeBrowseDefault
)
1665 flags
|= kDNSServiceFlagsDefault
;
1669 flags
|= kDNSServiceFlagsRemove
;
1671 ConvertDomainNameToCString(&answer
->rdata
->u
.name
, domain
);
1672 reply
= format_enumeration_reply(de
->rstate
, domain
, flags
, mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage
, answer
->InterfaceID
), kDNSServiceErr_NoError
);
1675 LogMsg("ERROR: enum_result_callback, format_enumeration_reply");
1679 append_reply(de
->rstate
, reply
);
1683 static reply_state
*format_enumeration_reply(request_state
*rstate
, char *domain
, DNSServiceFlags flags
, uint32_t ifi
, DNSServiceErrorType err
)
1690 len
= sizeof(DNSServiceFlags
);
1691 len
+= sizeof(uint32_t);
1692 len
+= sizeof(DNSServiceErrorType
);
1693 len
+= strlen(domain
) + 1;
1695 reply
= create_reply(enumeration_reply
, len
, rstate
);
1696 reply
->rhdr
->flags
= flags
;
1697 reply
->rhdr
->ifi
= ifi
;
1698 reply
->rhdr
->error
= err
;
1699 data
= reply
->sdata
;
1700 put_string(domain
, &data
);
1704 static void enum_termination_callback(void *context
)
1706 enum_termination_t
*t
= context
;
1707 mDNS
*coredata
= &mDNSStorage
;
1709 mDNS_StopGetDomains(coredata
, &t
->all
->question
);
1710 mDNS_StopGetDomains(coredata
, &t
->def
->question
);
1711 freeL("enum_termination_callback", t
->all
);
1712 freeL("enum_termination_callback", t
->def
);
1713 t
->rstate
->termination_context
= NULL
;
1714 freeL("enum_termination_callback", t
);
1717 static void handle_reconfirm_request(request_state
*rstate
)
1721 rr
= read_rr_from_ipc_msg(rstate
->msgdata
, 0);
1723 mDNS_ReconfirmByValue(&mDNSStorage
, &rr
->resrec
);
1724 abort_request(rstate
);
1725 unlink_request(rstate
);
1726 freeL("handle_reconfirm_request", rr
);
1730 // setup rstate to accept new reg/dereg requests
1731 static void reset_connected_rstate(request_state
*rstate
)
1733 rstate
->ts
= t_morecoming
;
1734 rstate
->hdr_bytes
= 0;
1735 rstate
->data_bytes
= 0;
1736 if (rstate
->msgbuf
) freeL("reset_connected_rstate", rstate
->msgbuf
);
1737 rstate
->msgbuf
= NULL
;
1738 rstate
->bufsize
= 0;
1743 // returns a resource record (allocated w/ malloc) containing the data found in an IPC message
1744 // data must be in format flags, interfaceIndex, name, rrtype, rrclass, rdlen, rdata, (optional)ttl
1745 // (ttl only extracted/set if ttl argument is non-zero). returns NULL for a bad-parameter error
1746 static AuthRecord
*read_rr_from_ipc_msg(char *msgbuf
, int ttl
)
1748 char *rdata
, name
[256];
1750 DNSServiceFlags flags
;
1751 uint32_t interfaceIndex
;
1752 uint16_t type
, class, rdlen
;
1755 flags
= get_flags(&msgbuf
);
1756 interfaceIndex
= get_long(&msgbuf
);
1757 if (get_string(&msgbuf
, name
, 256) < 0)
1759 LogMsg("ERROR: read_rr_from_ipc_msg - get_string");
1762 type
= get_short(&msgbuf
);
1763 class = get_short(&msgbuf
);
1764 rdlen
= get_short(&msgbuf
);
1766 if (rdlen
> sizeof(RDataBody
)) storage_size
= rdlen
;
1767 else storage_size
= sizeof(RDataBody
);
1769 rr
= mallocL("read_rr_from_ipc_msg", sizeof(AuthRecord
) - sizeof(RDataBody
) + storage_size
);
1772 my_perror("ERROR: malloc");
1775 bzero(rr
, sizeof(AuthRecord
)); // ok if oversized rdata not zero'd
1776 rr
->resrec
.rdata
= &rr
->rdatastorage
;
1777 rr
->resrec
.InterfaceID
= mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage
, interfaceIndex
);
1778 if (!MakeDomainNameFromDNSNameString(&rr
->resrec
.name
, name
))
1780 LogMsg("ERROR: bad name: %s", name
);
1781 freeL("read_rr_from_ipc_msg", rr
);
1784 rr
->resrec
.rrtype
= type
;
1785 if ((flags
& kDNSServiceFlagsShared
) == kDNSServiceFlagsShared
)
1786 rr
->resrec
.RecordType
= kDNSRecordTypeShared
;
1787 if ((flags
& kDNSServiceFlagsUnique
) == kDNSServiceFlagsUnique
)
1788 rr
->resrec
.RecordType
= kDNSRecordTypeUnique
;
1789 rr
->resrec
.rrclass
= class;
1790 rr
->resrec
.rdlength
= rdlen
;
1791 rr
->resrec
.rdata
->MaxRDLength
= rdlen
;
1792 rdata
= get_rdata(&msgbuf
, rdlen
);
1793 memcpy(rr
->resrec
.rdata
->u
.data
, rdata
, rdlen
);
1796 rr
->resrec
.rroriginalttl
= get_long(&msgbuf
);
1802 // generate a response message for a browse result, service registration result, or any other call with the
1803 // identical callback signature. on successful completion rep is set to point to a malloc'd reply_state struct,
1804 // and mStatus_NoError is returned. otherwise the appropriate error is returned.
1806 static mStatus
gen_rr_response(domainname
*servicename
, mDNSInterfaceID id
, request_state
*request
, reply_state
**rep
)
1811 domainname type
, dom
;
1812 char namestr
[256], typestr
[256], domstr
[256];
1816 if (!DeconstructServiceName(servicename
, &name
, &type
, &dom
))
1817 return kDNSServiceErr_Unknown
;
1819 ConvertDomainLabelToCString_unescaped(&name
, namestr
);
1820 ConvertDomainNameToCString(&type
, typestr
);
1821 ConvertDomainNameToCString(&dom
, domstr
);
1823 // calculate reply data length
1824 len
= sizeof(DNSServiceFlags
);
1825 len
+= sizeof(uint32_t); // if index
1826 len
+= sizeof(DNSServiceErrorType
);
1827 len
+= strlen(namestr
) + 1;
1828 len
+= strlen(typestr
) + 1;
1829 len
+= strlen(domstr
) + 1;
1831 *rep
= create_reply(query_reply
, len
, request
);
1832 (*rep
)->rhdr
->flags
= 0;
1833 (*rep
)->rhdr
->ifi
= mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage
, id
);
1834 (*rep
)->rhdr
->error
= kDNSServiceErr_NoError
;
1835 data
= (*rep
)->sdata
;
1837 put_string(namestr
, &data
);
1838 put_string(typestr
, &data
);
1839 put_string(domstr
, &data
);
1840 return mStatus_NoError
;
1844 static int build_domainname_from_strings(domainname
*srv
, char *name
, char *regtype
, char *domain
)
1849 if (!MakeDomainLabelFromLiteralString(&n
, name
)) return -1;
1850 if (!MakeDomainNameFromDNSNameString(&t
, regtype
)) return -1;
1851 if (!MakeDomainNameFromDNSNameString(&d
, domain
)) return -1;
1852 if (!ConstructServiceName(srv
, &n
, &t
, &d
)) return -1;
1857 // append a reply to the list in a request object
1858 static void append_reply(request_state
*req
, reply_state
*rep
)
1862 if (!req
->replies
) req
->replies
= rep
;
1866 while (ptr
->next
) ptr
= ptr
->next
;
1873 // read_msg may be called any time when the transfer state (rs->ts) is t_morecoming.
1874 // returns the current state of the request (morecoming, error, complete, terminated.)
1875 // if there is no data on the socket, the socket will be closed and t_terminated will be returned
1876 static int read_msg(request_state
*rs
)
1880 char buf
[4]; // dummy for death notification
1882 if (rs
->ts
== t_terminated
|| rs
->ts
== t_error
)
1884 LogMsg("ERROR: read_msg called with transfer state terminated or error");
1889 if (rs
->ts
== t_complete
)
1890 { // this must be death or something is wrong
1891 nread
= recv(rs
->sd
, buf
, 4, 0);
1892 if (!nread
) { rs
->ts
= t_terminated
; return t_terminated
; }
1893 if (nread
< 0) goto rerror
;
1894 LogMsg("ERROR: read data from a completed request.");
1899 if (rs
->ts
!= t_morecoming
)
1901 LogMsg("ERROR: read_msg called with invalid transfer state (%d)", rs
->ts
);
1906 if (rs
->hdr_bytes
< sizeof(ipc_msg_hdr
))
1908 nleft
= sizeof(ipc_msg_hdr
) - rs
->hdr_bytes
;
1909 nread
= recv(rs
->sd
, (char *)&rs
->hdr
+ rs
->hdr_bytes
, nleft
, 0);
1910 if (nread
== 0) { rs
->ts
= t_terminated
; return t_terminated
; }
1911 if (nread
< 0) goto rerror
;
1912 rs
->hdr_bytes
+= nread
;
1913 if (rs
->hdr_bytes
> sizeof(ipc_msg_hdr
))
1915 LogMsg("ERROR: read_msg - read too many header bytes");
1921 // only read data if header is complete
1922 if (rs
->hdr_bytes
== sizeof(ipc_msg_hdr
))
1924 if (rs
->hdr
.datalen
== 0) // ok in removerecord requests
1926 rs
->ts
= t_complete
;
1931 if (!rs
->msgbuf
) // allocate the buffer first time through
1933 rs
->msgbuf
= mallocL("read_msg", rs
->hdr
.datalen
);
1936 my_perror("ERROR: malloc");
1940 rs
->msgdata
= rs
->msgbuf
;
1942 nleft
= rs
->hdr
.datalen
- rs
->data_bytes
;
1943 nread
= recv(rs
->sd
, rs
->msgbuf
+ rs
->data_bytes
, nleft
, 0);
1944 if (nread
== 0) { rs
->ts
= t_terminated
; return t_terminated
; }
1945 if (nread
< 0) goto rerror
;
1946 rs
->data_bytes
+= nread
;
1947 if (rs
->data_bytes
> rs
->hdr
.datalen
)
1949 LogMsg("ERROR: read_msg - read too many data bytes");
1955 if (rs
->hdr_bytes
== sizeof(ipc_msg_hdr
) && rs
->data_bytes
== rs
->hdr
.datalen
)
1956 rs
->ts
= t_complete
;
1957 else rs
->ts
= t_morecoming
;
1962 if (errno
== EAGAIN
|| errno
== EINTR
) return rs
->ts
;
1963 my_perror("ERROR: read_msg");
1969 static int send_msg(reply_state
*rs
)
1975 LogMsg("ERROR: send_msg called with NULL message buffer");
1979 if (rs
->request
->no_reply
) //!!!KRS this behavior should be optimized if it becomes more common
1981 rs
->ts
= t_complete
;
1982 freeL("send_msg", rs
->msgbuf
);
1986 nwriten
= send(rs
->sd
, rs
->msgbuf
+ rs
->nwriten
, rs
->len
- rs
->nwriten
, 0);
1989 if (errno
== EINTR
|| errno
== EAGAIN
) nwriten
= 0;
1994 LogMsg("broken pipe - cleanup should be handled by run-loop read wakeup");
1995 rs
->ts
= t_terminated
;
1996 rs
->request
->ts
= t_terminated
;
1997 return t_terminated
;
2001 my_perror("ERROR: send\n");
2007 rs
->nwriten
+= nwriten
;
2009 if (rs
->nwriten
== rs
->len
)
2011 rs
->ts
= t_complete
;
2012 freeL("send_msg", rs
->msgbuf
);
2019 static reply_state
*create_reply(reply_op_t op
, int datalen
, request_state
*request
)
2025 if ((unsigned)datalen
< sizeof(reply_hdr
))
2027 LogMsg("ERROR: create_reply - data length less than lenght of required fields");
2031 totallen
= datalen
+ sizeof(ipc_msg_hdr
);
2032 reply
= mallocL("create_reply", sizeof(reply_state
));
2035 my_perror("ERROR: malloc");
2038 bzero(reply
, sizeof(reply_state
));
2039 reply
->ts
= t_morecoming
;
2040 reply
->sd
= request
->sd
;
2041 reply
->request
= request
;
2042 reply
->len
= totallen
;
2043 reply
->msgbuf
= mallocL("create_reply", totallen
);
2046 my_perror("ERROR: malloc");
2049 bzero(reply
->msgbuf
, totallen
);
2050 reply
->mhdr
= (ipc_msg_hdr
*)reply
->msgbuf
;
2051 reply
->rhdr
= (reply_hdr
*)(reply
->msgbuf
+ sizeof(ipc_msg_hdr
));
2052 reply
->sdata
= reply
->msgbuf
+ sizeof(ipc_msg_hdr
) + sizeof(reply_hdr
);
2053 reply
->mhdr
->version
= VERSION
;
2054 reply
->mhdr
->op
.reply_op
= op
;
2055 reply
->mhdr
->datalen
= totallen
- sizeof(ipc_msg_hdr
);
2060 static int deliver_error(request_state
*rstate
, mStatus err
)
2063 undelivered_error_t
*undeliv
;
2065 nwritten
= send(rstate
->errfd
, &err
, sizeof(mStatus
), 0);
2066 if (nwritten
< (int)sizeof(mStatus
))
2068 if (errno
== EINTR
|| errno
== EAGAIN
)
2072 my_perror("ERROR: send - unable to deliver error to client");
2075 //client blocked - store result and come backr
2076 undeliv
= mallocL("deliver_error", sizeof(undelivered_error_t
));
2079 my_perror("ERROR: malloc");
2083 undeliv
->nwritten
= nwritten
;
2084 undeliv
->sd
= rstate
->errfd
;
2085 rstate
->u_err
= undeliv
;
2088 if (rstate
->errfd
!= rstate
->sd
) close(rstate
->errfd
);
2092 if (rstate
->errfd
!= rstate
->sd
) close(rstate
->errfd
);
2098 // returns 0 on success, -1 if send is incomplete, or on terminal failre (request is aborted)
2099 static transfer_state
send_undelivered_error(request_state
*rs
)
2103 nwritten
= send(rs
->u_err
->sd
, (char *)(&rs
->u_err
) + rs
->u_err
->nwritten
, sizeof(mStatus
) - rs
->u_err
->nwritten
, 0);
2106 if (errno
== EINTR
|| errno
== EAGAIN
)
2110 my_perror("ERROR: send - unable to deliver error to client\n");
2111 if (rs
->u_err
->sd
== rs
->sd
) close (rs
->u_err
->sd
);
2115 if (nwritten
+ rs
->u_err
->nwritten
== sizeof(mStatus
))
2117 if (rs
->u_err
->sd
== rs
->sd
) close(rs
->u_err
->sd
);
2118 freeL("send_undelivered_error", rs
->u_err
);
2122 rs
->u_err
->nwritten
+= nwritten
;
2123 return t_morecoming
;
2127 // send bogus data along with an error code to the app callback
2128 // returns 0 on success (linking reply into list of not fully delivered),
2129 // -1 on failure (request should be aborted)
2130 static int deliver_async_error(request_state
*rs
, reply_op_t op
, mStatus err
)
2136 if (rs
->no_reply
) return 0;
2137 len
= 256; // long enough for any reply handler to read all args w/o buffer overrun
2138 reply
= create_reply(op
, len
, rs
);
2139 reply
->rhdr
->error
= err
;
2140 ts
= send_msg(reply
);
2141 if (ts
== t_error
|| ts
== t_terminated
)
2143 freeL("deliver_async_error", reply
);
2146 else if (ts
== t_complete
) freeL("deliver_async_error", reply
);
2147 else if (ts
== t_morecoming
) append_reply(rs
, reply
); // client is blocked, link reply into list
2152 static void abort_request(request_state
*rs
)
2154 reply_state
*rep
, *ptr
;
2156 if (rs
->terminate
) rs
->terminate(rs
->termination_context
); // terminate field may not be set yet
2157 if (rs
->msgbuf
) freeL("abort_request", rs
->msgbuf
);
2158 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), rs
->rls
, kCFRunLoopDefaultMode
);
2159 CFRunLoopSourceInvalidate(rs
->rls
);
2161 CFSocketInvalidate(rs
->sr
);
2164 if (rs
->errfd
>= 0) close(rs
->errfd
);
2167 // free pending replies
2171 if (rep
->msgbuf
) freeL("abort_request", rep
->msgbuf
);
2174 freeL("abort_request", ptr
);
2179 freeL("abort_request", rs
->u_err
);
2185 static void unlink_request(request_state
*rs
)
2189 if (rs
== all_requests
)
2191 all_requests
= all_requests
->next
;
2192 freeL("unlink_request", rs
);
2195 for(ptr
= all_requests
; ptr
->next
; ptr
= ptr
->next
)
2196 if (ptr
->next
== rs
)
2198 ptr
->next
= rs
->next
;
2199 freeL("unlink_request", rs
);
2206 //hack to search-replace perror's to LogMsg's
2207 static void my_perror(char *errmsg
)
2209 LogMsg("%s: %s", errmsg
, strerror(errno
));