+++ /dev/null
-/*
- * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
-
- Change History (most recent first):
-
-$Log: uds_daemon.c,v $
-Revision 1.22.2.4 2005/01/28 05:41:45 cheshire
-<rdar://problem/3924278> SUPan: Service advertisement in qmaster can yield zombie ads
-
-Revision 1.22.2.3 2005/01/28 04:03:24 cheshire
-<rdar://problem/3759302> SUPan: Current method of doing subtypes causes name collisions
-Summary: Pulled in ConstructServiceName, CountSubTypes and AllocateSubTypes from Tiger version.
-
-Revision 1.22.2.2 2004/06/18 17:28:19 cheshire
-<rdar://problem/3588761> Current method of doing subtypes causes name collisions
-
-Revision 1.22.2.1 2003/12/05 00:03:35 cheshire
-<rdar://problem/3487869> Use buffer size MAX_ESCAPED_DOMAIN_NAME instead of 256
-
-Revision 1.22 2003/08/19 16:03:55 ksekar
-Bug #: <rdar://problem/3380097>: ER: SIGINFO dump should include resolves started by DNSServiceQueryRecord
-Check termination_context for NULL before dereferencing.
-
-Revision 1.21 2003/08/19 05:39:43 cheshire
-<rdar://problem/3380097> SIGINFO dump should include resolves started by DNSServiceQueryRecord
-
-Revision 1.20 2003/08/16 03:39:01 cheshire
-<rdar://problem/3338440> InterfaceID -1 indicates "local only"
-
-Revision 1.19 2003/08/15 20:16:03 cheshire
-<rdar://problem/3366590> mDNSResponder takes too much RPRVT
-We want to avoid touching the rdata pages, so we don't page them in.
-1. RDLength was stored with the rdata, which meant touching the page just to find the length.
- Moved this from the RData to the ResourceRecord object.
-2. To avoid unnecessarily touching the rdata just to compare it,
- compute a hash of the rdata and store the hash in the ResourceRecord object.
-
-Revision 1.18 2003/08/15 00:38:00 ksekar
-Bug #: <rdar://problem/3377005>: Bug: buffer overrun when reading long rdata from client
-
-Revision 1.17 2003/08/14 02:18:21 cheshire
-<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
-
-Revision 1.16 2003/08/13 23:58:52 ksekar
-Bug #: <rdar://problem/3374911>: Bug: UDS Sub-type browsing works, but not sub-type registration
-Fixed pointer increment error, moved subtype reading for-loop for easier error bailout.
-
-Revision 1.15 2003/08/13 17:30:33 ksekar
-Bug #: <rdar://problem/3374671>: DNSServiceAddRecord doesn't work
-Fixed various problems with handling the AddRecord request and freeing the ExtraResourceRecords.
-
-Revision 1.14 2003/08/12 19:56:25 cheshire
-Update to APSL 2.0
-
- */
-
-#include "mDNSClientAPI.h"
-#include "mDNSMacOSX.h"
-#include "dns_sd.h"
-#include "dnssd_ipc.h"
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-
-// Types and Data Structures
-// ----------------------------------------------------------------------
-
-typedef enum
- {
- t_uninitialized,
- t_morecoming,
- t_complete,
- t_error,
- t_terminated
- } transfer_state;
-
-typedef void (*req_termination_fn)(void *);
-
-
-typedef struct registered_record_entry
- {
- int key;
- AuthRecord *rr;
- struct registered_record_entry *next;
- } registered_record_entry;
-
-typedef struct registered_service
- {
- //struct registered_service *next;
- int autoname;
- int renameonconflict;
- int rename_on_memfree; // set flag on config change when we deregister original name
- domainlabel name;
- ServiceRecordSet *srs;
- struct request_state *request;
- AuthRecord *subtypes;
- } registered_service;
-
-typedef struct
- {
- mStatus err;
- int nwritten;
- int sd;
- } undelivered_error_t;
-
-typedef struct request_state
- {
- // connection structures
- CFRunLoopSourceRef rls;
- CFSocketRef sr;
- int sd;
-
- // state of read (in case message is read over several recv() calls)
- transfer_state ts;
- uint32_t hdr_bytes; // bytes of header already read
- ipc_msg_hdr hdr;
- uint32_t data_bytes; // bytes of message data already read
- char *msgbuf; // pointer to data storage to pass to free()
- char *msgdata; // pointer to data to be read from (may be modified)
- int bufsize; // size of data storage
-
- // reply, termination, error, and client context info
- int no_reply; // don't send asynchronous replies to client
- void *client_context; // don't touch this - pointer only valid in client's addr space
- struct reply_state *replies; // corresponding (active) reply list
- undelivered_error_t *u_err;
- void *termination_context;
- req_termination_fn terminate;
-
- //!!!KRS toss these pointers in a union
- // registration context associated with this request (null if not applicable)
- registered_record_entry *reg_recs; // muliple registrations for a connection-oriented request
- registered_service *service; // service record set and flags
- struct resolve_result_t *resolve_results;
-
- struct request_state *next;
- } request_state;
-
-// struct physically sits between ipc message header and call-specific fields in the message buffer
-typedef struct
- {
- DNSServiceFlags flags;
- uint32_t ifi;
- DNSServiceErrorType error;
- } reply_hdr;
-
-
-typedef struct reply_state
- {
- // state of the transmission
- int sd;
- transfer_state ts;
- uint32_t nwriten;
- uint32_t len;
- // context of the reply
- struct request_state *request; // the request that this answers
- struct reply_state *next; // if there are multiple unsent replies
- // pointer into message buffer - allows fields to be changed after message is formatted
- ipc_msg_hdr *mhdr;
- reply_hdr *rhdr;
- char *sdata; // pointer to start of call-specific data
- // pointer to malloc'd buffer
- char *msgbuf;
- } reply_state;
-
-
-// domain enumeration and resolv calls require 2 mDNSCore calls, so we need separate interconnected
-// structures to handle callbacks
-typedef struct
- {
- DNSQuestion question;
- mDNS_DomainType type;
- request_state *rstate;
- } domain_enum_t;
-
-typedef struct
- {
- domain_enum_t *all;
- domain_enum_t *def;
- request_state *rstate;
- } enum_termination_t;
-
-typedef struct
- {
- DNSQuestion question;
- uint16_t qtype;
- request_state *rstate;
- } resolve_t;
-
-typedef struct
- {
- resolve_t *txt;
- resolve_t *srv;
- request_state *rstate;
- } resolve_termination_t;
-
-typedef struct resolve_result_t
- {
- const ResourceRecord *txt;
- const ResourceRecord *srv;
- } resolve_result_t;
-
-typedef struct
- {
- request_state *rstate;
- client_context_t client_context;
- } regrecord_callback_context;
-
-
-
-
-// globals
-static int listenfd = -1;
-static request_state *all_requests = NULL;
-//!!!KRS we should keep a separate list containing only the requests that need to be examined
-//in the idle() routine.
-
-
-#define MAX_OPENFILES 1024
-
-
-// private function prototypes
-static void connect_callback(CFSocketRef sr, CFSocketCallBackType t, CFDataRef dr, const void *c, void *i);
-static int read_msg(request_state *rs);
-static int send_msg(reply_state *rs);
-static void abort_request(request_state *rs);
-static void request_callback(CFSocketRef sr, CFSocketCallBackType t, CFDataRef dr, const void *c, void *i);
-static void handle_resolve_request(request_state *rstate);
-static void question_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord);
-static void question_termination_callback(void *context);
-static void handle_browse_request(request_state *request);
-static void browse_termination_callback(void *context);
-static void browse_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord);
-static void handle_regservice_request(request_state *request);
-static void regservice_termination_callback(void *context);
-static void process_service_registration(ServiceRecordSet *const srs);
-static void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result);
-static mStatus handle_add_request(request_state *rstate);
-static mStatus handle_update_request(request_state *rstate);
-static mStatus gen_rr_response(domainname *servicename, mDNSInterfaceID id, request_state *request, reply_state **rep);
-static void append_reply(request_state *req, reply_state *rep);
-static int build_domainname_from_strings(domainname *srv, char *name, char *regtype, char *domain);
-static void enum_termination_callback(void *context);
-static void enum_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord);
-static void handle_query_request(request_state *rstate);
-static mStatus do_question(request_state *rstate, domainname *name, uint32_t ifi, uint16_t rrtype, int16_t rrclass);
-static reply_state *format_enumeration_reply(request_state *rstate, char *domain, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err);
-static void handle_enum_request(request_state *rstate);
-static mStatus handle_regrecord_request(request_state *rstate);
-static void regrecord_callback(mDNS *const m, AuthRecord *const rr, mStatus result);
-static void connected_registration_termination(void *context);
-static void handle_reconfirm_request(request_state *rstate);
-static AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int ttl);
-static mStatus handle_removerecord_request(request_state *rstate);
-static void reset_connected_rstate(request_state *rstate);
-static int deliver_error(request_state *rstate, mStatus err);
-static int deliver_async_error(request_state *rs, reply_op_t op, mStatus err);
-static transfer_state send_undelivered_error(request_state *rs);
-static reply_state *create_reply(reply_op_t op, int datalen, request_state *request);
-static void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd);
-static void my_perror(char *errmsg);
-static void unlink_request(request_state *rs);
-static void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord);
-static void resolve_termination_callback(void *context);
-
-// initialization, setup/teardown functions
-
-int udsserver_init(void)
- {
- mode_t mask;
- struct sockaddr_un laddr;
- struct rlimit maxfds;
-
- if ((listenfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
- goto error;
- unlink(MDNS_UDS_SERVERPATH); //OK if this fails
- bzero(&laddr, sizeof(laddr));
- laddr.sun_family = AF_LOCAL;
- laddr.sun_len = sizeof(struct sockaddr_un);
- strcpy(laddr.sun_path, MDNS_UDS_SERVERPATH);
- mask = umask(0);
- if (bind(listenfd, (struct sockaddr *)&laddr, sizeof(laddr)) < 0)
- goto error;
- umask(mask);
-
- if (fcntl(listenfd, F_SETFL, O_NONBLOCK) < 0)
- {
- my_perror("ERROR: could not set listen socket to non-blocking mode");
- goto error;
- }
- listen(listenfd, LISTENQ);
-
-
- // set maximum file descriptor to 1024
- if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0)
- {
- my_perror("ERROR: Unable to get file descriptor limit");
- return 0;
- }
- if (maxfds.rlim_max >= MAX_OPENFILES && maxfds.rlim_cur == maxfds.rlim_max)
- {
- // proper values already set
- return 0;
- }
- maxfds.rlim_max = MAX_OPENFILES;
- maxfds.rlim_cur = MAX_OPENFILES;
- if (setrlimit(RLIMIT_NOFILE, &maxfds) < 0)
- my_perror("ERROR: Unable to set maximum file descriptor limit");
- return 0;
-
-error:
- my_perror("ERROR: udsserver_init");
- return -1;
- }
-
-int udsserver_exit(void)
- {
- close(listenfd);
- unlink(MDNS_UDS_SERVERPATH);
- return 0;
- }
-
-
-// add the named socket as a runloop source
-int udsserver_add_rl_source(void)
- {
- CFSocketContext context = { 0, NULL, NULL, NULL, NULL };
- CFSocketRef sr = CFSocketCreateWithNative(kCFAllocatorDefault, listenfd, kCFSocketReadCallBack, connect_callback, &context);
- if (!sr)
- {
- debugf("ERROR: udsserver_add_rl_source - CFSocketCreateWithNative");
- return -1;
- }
- CFRunLoopSourceRef rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, sr, 0);
-
- if (!rls)
- {
- debugf("ERROR: udsserver_add_rl_source - CFSocketCreateRunLoopSource");
- return -1;
- }
- CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
- return 0;
- }
-
-mDNSs32 udsserver_idle(mDNSs32 nextevent)
- {
- request_state *req = all_requests, *tmp, *prev = NULL;
- reply_state *fptr;
- transfer_state result;
- mDNSs32 now = mDNSPlatformTimeNow();
-
-
- while(req)
- {
- result = t_uninitialized;
- if (req->u_err)
- result = send_undelivered_error(req);
- if (result != t_error && result != t_morecoming && // don't try to send msg if send_error failed
- (req->ts == t_complete || req->ts == t_morecoming))
- {
- while(req->replies)
- {
- if (req->replies->next) req->replies->rhdr->flags |= kDNSServiceFlagsMoreComing;
- else req->replies->rhdr->flags |= kDNSServiceFlagsFinished;
- result = send_msg(req->replies);
- if (result == t_complete)
- {
- fptr = req->replies;
- req->replies = req->replies->next;
- freeL("udsserver_idle", fptr);
- }
- else if (result == t_terminated || result == t_error)
- {
- abort_request(req);
- break;
- }
- else if (result == t_morecoming) // client's queues are full, move to next
- {
- if (nextevent - now > mDNSPlatformOneSecond)
- nextevent = now + mDNSPlatformOneSecond;
- break; // start where we left off in a second
- }
- }
- }
- if (result == t_terminated || result == t_error)
- //since we're already doing a list traversal, we unlink the request manunally instead of calling unlink_request()
- {
- tmp = req;
- if (prev) prev->next = req->next;
- if (req == all_requests) all_requests = all_requests->next;
- req = req->next;
- freeL("udsserver_idle", tmp);
- }
- else
- {
- prev = req;
- req = req->next;
- }
- }
- return nextevent;
- }
-
-void udsserver_info(void)
- {
- request_state *req;
- for (req = all_requests; req; req=req->next)
- {
- void *t = req->termination_context;
- if (!t) continue;
- if (req->terminate == regservice_termination_callback)
- LogMsg("DNSServiceRegister %##s", ((registered_service *) t)->srs->RR_SRV.resrec.name.c);
- else if (req->terminate == browse_termination_callback)
- LogMsg("DNSServiceBrowse %##s", ((DNSQuestion *) t)->qname.c);
- else if (req->terminate == resolve_termination_callback)
- LogMsg("DNSServiceResolve %##s", ((resolve_termination_t *)t)->srv->question.qname.c);
- else if (req->terminate == question_termination_callback)
- LogMsg("DNSServiceQueryRecord %##s", ((DNSQuestion *) t)->qname.c);
- else if (req->terminate == enum_termination_callback)
- LogMsg("DNSServiceEnumerateDomains %##s", ((enum_termination_t *) t)->all->question.qname.c);
- }
- }
-
-void udsserver_handle_configchange(void)
- {
- registered_service *srv;
- request_state *req;
- mStatus err;
-
- for (req = all_requests; req; req = req->next)
- {
- srv = req->service;
- if (srv && srv->autoname && !SameDomainLabel(srv->name.c, mDNSStorage.nicelabel.c))
- {
- srv->rename_on_memfree = 1;
- err = mDNS_DeregisterService(&mDNSStorage, srv->srs);
- if (err) LogMsg("ERROR: udsserver_handle_configchange: DeregisterService returned error %d. Continuing.", err);
- // error should never occur - safest to log and continue
- }
- }
- }
-
-
-
-
-// accept a connection on the named socket, adding the new descriptor to the runloop and passing the error
-// descriptor to the client
-static void connect_callback(CFSocketRef s, CFSocketCallBackType t, CFDataRef dr, const void *c, void *i)
- {
- int sd, clilen, optval;
- struct sockaddr_un cliaddr;
- CFSocketContext context = { 0, NULL, NULL, NULL, NULL };
- request_state *rstate;
-// int errpipe[2];
-
- #pragma unused(s, t, dr, c, i)
-
- clilen = sizeof(cliaddr);
- sd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
-
- if (sd < 0)
- {
- if (errno == EWOULDBLOCK) return;
- my_perror("ERROR: accept");
- return;
- }
- optval = 1;
- if (setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0)
- {
- my_perror("ERROR: setsockopt - SOL_NOSIGPIPE - aborting client");
- close(sd);
- return;
- }
-
- if (fcntl(sd, F_SETFL, O_NONBLOCK) < 0)
- {
- my_perror("ERROR: could not set connected socket to non-blocking mode - aborting client");
- close(sd);
- return;
- }
-
-/*
- // open a pipe to deliver error messages, pass descriptor to client
- if (pipe(errpipe) < 0)
- {
- my_perror("ERROR: could not create pipe");
- exit(1);
- }
-
- if (ioctl(sd, I_SENDFD, errpipe[0]) < 0)
- {
- my_perror("ERROR: could not pass pipe descriptor to client. Aborting client.\n");
- close(sd);
- return;
- }
- if (fcntl(errpipe[1], F_SETFL, O_NONBLOCK) < 0)
- {
- my_perror("ERROR: could not set error pipe to non-blocking mode - aborting client");
- close(sd);
- close(errpipe[1]);
- return;
- }
- */
-
- // allocate a request_state struct that will live with the socket
- rstate = mallocL("connect_callback", sizeof(request_state));
- if (!rstate)
- {
- my_perror("ERROR: malloc");
- exit(1);
- }
- bzero(rstate, sizeof(request_state));
- rstate->ts = t_morecoming;
- rstate->sd = sd;
- //rstate->errfd = errpipe[1];
-
- //now create CFSocket wrapper and add to run loop
- context.info = rstate;
- rstate->sr = CFSocketCreateWithNative(kCFAllocatorDefault, sd, kCFSocketReadCallBack, request_callback, &context);
- if (!rstate->sr)
- {
- debugf("ERROR: connect_callback - CFSocketCreateWithNative");
- freeL("connect_callback", rstate);
- close(sd);
- return;
- }
- rstate->rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, rstate->sr, 0);
- if (!rstate->rls)
- {
- debugf("ERROR: connect_callback - CFSocketCreateRunLoopSource");
- CFSocketInvalidate(rstate->sr); // automatically closes socket
- CFRelease(rstate->sr);
- freeL("connect_callback", rstate);
- return;
- }
- CFRunLoopAddSource(CFRunLoopGetCurrent(), rstate->rls, kCFRunLoopDefaultMode);
- if (!CFRunLoopContainsSource(CFRunLoopGetCurrent(), rstate->rls, kCFRunLoopDefaultMode))
- {
- LogMsg("ERROR: connect_callback, CFRunLoopAddSource");
- abort_request(rstate);
- return;
- }
- rstate->next = all_requests;
- all_requests = rstate;
- }
-
-
-// main client request handling routine. reads request and calls the appropriate request-specific
-// handler
-static void request_callback(CFSocketRef sr, CFSocketCallBackType t, CFDataRef dr, const void *context, void *info)
- {
- request_state *rstate = info;
- transfer_state result;
- struct sockaddr_un cliaddr;
- char ctrl_path[MAX_CTLPATH];
-
- #pragma unused(sr, t, dr, context)
-
- int native = CFSocketGetNative(sr);
- if (native != rstate->sd)
- {
- LogMsg("ERROR: request_callback - CFSocket's native descriptor does not match rstate member descriptor.");
- abort_request(rstate);
- unlink_request(rstate);
- return;
- }
-
- result = read_msg(rstate);
- if (result == t_morecoming)
- {
- return;
- }
- if (result == t_terminated)
- {
- abort_request(rstate);
- unlink_request(rstate);
- return;
- }
- if (result == t_error)
- {
- abort_request(rstate);
- unlink_request(rstate);
- return;
- }
-
- if (rstate->hdr.version != VERSION)
- {
- LogMsg("ERROR: client incompatible with daemon (client version = %d, "
- "daemon version = %d)\n", rstate->hdr.version, VERSION);
- abort_request(rstate);
- unlink_request(rstate);
- return;
- }
-
- // check if client wants silent operation
- if (rstate->hdr.flags & IPC_FLAGS_NOREPLY) rstate->no_reply = 1;
-
- // check if primary socket is to be used for synchronous errors, else open new socket
- if ((rstate->hdr.flags & IPC_FLAGS_REUSE_SOCKET) == 0)
- {
- mStatus err = 0;
- int nwritten;
- int errfd = socket(AF_LOCAL, SOCK_STREAM, 0);
- if (errfd < 0)
- {
- my_perror("ERROR: socket");
- abort_request(rstate);
- unlink_request(rstate);
- return;
- }
- if (fcntl(errfd, F_SETFL, O_NONBLOCK) < 0)
- {
- my_perror("ERROR: could not set control socket to non-blocking mode");
- abort_request(rstate);
- unlink_request(rstate);
- return;
- }
- get_string(&rstate->msgdata, ctrl_path, 256); // path is first element in message buffer
- bzero(&cliaddr, sizeof(cliaddr));
- cliaddr.sun_family = AF_LOCAL;
- strcpy(cliaddr.sun_path, ctrl_path);
- if (connect(errfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)) < 0)
- {
- my_perror("ERROR: connect");
- abort_request(rstate);
- unlink_request(rstate);
- }
-
- switch (rstate->hdr.op.request_op)
- {
- case reg_record_request: err = handle_regrecord_request (rstate); break;
- case add_record_request: err = handle_add_request (rstate); break;
- case update_record_request: err = handle_update_request (rstate); break;
- case remove_record_request: err = handle_removerecord_request(rstate); break;
- default: LogMsg("%3d: ERROR: udsserver_recv_request - unsupported request type: %d", rstate->sd, rstate->hdr.op);
- }
-
- nwritten = send(errfd, &err, sizeof(err), 0);
- // On a freshly-created Unix Domain Socket, the kernel should *never* fail to buffer a four-byte write for us.
- // If not, we don't attempt to handle this failure, but we do log it.
- if (nwritten < (int)sizeof(err))
- LogMsg("ERROR: failed to write error response back to caller: %d %d %s", nwritten, errno, strerror(errno));
- close(errfd);
- reset_connected_rstate(rstate); // Reset ready to accept the next request on this pipe
- }
- else
- {
- switch (rstate->hdr.op.request_op)
- {
- case resolve_request: handle_resolve_request (rstate); break;
- case query_request: handle_query_request (rstate); break;
- case browse_request: handle_browse_request (rstate); break;
- case reg_service_request: handle_regservice_request(rstate); break;
- case enumeration_request: handle_enum_request (rstate); break;
- case reconfirm_record_request: handle_reconfirm_request (rstate); break;
- default: LogMsg("%3d: ERROR: udsserver_recv_request - unsupported request type: %d", rstate->sd, rstate->hdr.op);
- }
- }
- }
-
-// mDNS operation functions. Each operation has 3 associated functions - a request handler that parses
-// the client's request and makes the appropriate mDNSCore call, a result handler (passed as a callback
-// to the mDNSCore routine) that sends results back to the client, and a termination routine that aborts
-// the mDNSCore operation if the client dies or closes its socket.
-
-
-// query and resolve calls have separate request handlers that parse the arguments from the client and
-// massage the name parameters appropriately, but the rest of the operations (making the query call,
-// delivering the result to the client, and termination) are identical.
-
-static void handle_query_request(request_state *rstate)
- {
- DNSServiceFlags flags;
- uint32_t interfaceIndex;
- char name[256];
- uint16_t rrtype, rrclass;
- char *ptr;
- domainname dname;
- mStatus result;
-
- if (rstate->ts != t_complete)
- {
- LogMsg("ERROR: handle_query_request - transfer state != t_complete");
- goto error;
- }
- ptr = rstate->msgdata;
- if (!ptr)
- {
- LogMsg("ERROR: handle_query_request - NULL msgdata");
- goto error;
- }
- flags = get_flags(&ptr);
- interfaceIndex = get_long(&ptr);
- if (get_string(&ptr, name, 256) < 0) goto bad_param;
- rrtype = get_short(&ptr);
- rrclass = get_short(&ptr);
- if (!MakeDomainNameFromDNSNameString(&dname, name)) goto bad_param;
- result = do_question(rstate, &dname, interfaceIndex, rrtype, rrclass);
- if (result) rstate->terminate = NULL;
- if (deliver_error(rstate, result) < 0) goto error;
- return;
-
-bad_param:
- deliver_error(rstate, mStatus_BadParamErr);
- rstate->terminate = NULL; // don't try to terminate insuccessful Core calls
-error:
- abort_request(rstate);
- unlink_request(rstate);
- return;
- }
-
-static void handle_resolve_request(request_state *rstate)
- {
- DNSServiceFlags flags;
- uint32_t interfaceIndex;
- char name[256], regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME];
- char *ptr; // message data pointer
- domainname fqdn;
- resolve_t *srv, *txt;
- resolve_termination_t *term;
- mStatus err;
-
- if (rstate->ts != t_complete)
- {
- LogMsg("ERROR: handle_resolve_request - transfer state != t_complete");
- abort_request(rstate);
- unlink_request(rstate);
- return;
- }
-
- // extract the data from the message
- ptr = rstate->msgdata;
- if (!ptr)
- {
- LogMsg("ERROR: handle_resolve_request - NULL msgdata");
- abort_request(rstate);
- unlink_request(rstate);
- return;
- }
- flags = get_flags(&ptr);
- interfaceIndex = get_long(&ptr);
- mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
- if (interfaceIndex && !InterfaceID) goto bad_param;
- if (get_string(&ptr, name, 256) < 0 ||
- get_string(&ptr, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
- get_string(&ptr, domain, MAX_ESCAPED_DOMAIN_NAME) < 0)
- goto bad_param;
-
- // free memory in rstate since we don't need it anymore
- freeL("handle_resolve_request", rstate->msgbuf);
- rstate->msgbuf = NULL;
-
- if (build_domainname_from_strings(&fqdn, name, regtype, domain) < 0)
- goto bad_param;
-
- // allocate question wrapper structs
- srv = mallocL("handle_resolve_request", sizeof(resolve_t));
- txt = mallocL("handle_resolve_request", sizeof(resolve_t));
- if (!srv || !txt) goto malloc_error;
- srv->qtype = kDNSType_SRV;
- txt->qtype = kDNSType_TXT;
- srv->rstate = rstate;
- txt->rstate = rstate;
-
- // format questions
- srv->question.QuestionContext = rstate;
- srv->question.QuestionCallback = resolve_result_callback;
- memcpy(&srv->question.qname, &fqdn, MAX_DOMAIN_NAME);
- srv->question.qtype = kDNSType_SRV;
- srv->question.qclass = kDNSClass_IN;
- srv->question.InterfaceID = InterfaceID;
-
- txt->question.QuestionContext = rstate;
- txt->question.QuestionCallback = resolve_result_callback;
- memcpy(&txt->question.qname, &fqdn, MAX_DOMAIN_NAME);
- txt->question.qtype = kDNSType_TXT;
- txt->question.qclass = kDNSClass_IN;
- txt->question.InterfaceID = InterfaceID;
-
- // set up termination info
- term = mallocL("handle_resolve_request", sizeof(resolve_termination_t));
- if (!term) goto malloc_error;
- term->srv = srv;
- term->txt = txt;
- term->rstate = rstate;
- rstate->termination_context = term;
- rstate->terminate = resolve_termination_callback;
-
- // set up reply wrapper struct (since answer will come via 2 callbacks)
- rstate->resolve_results = mallocL("handle_resolve_response", sizeof(resolve_result_t));
- if (!rstate->resolve_results) goto malloc_error;
- bzero(rstate->resolve_results, sizeof(resolve_result_t));
-
- // ask the questions
- err = mDNS_StartQuery(&mDNSStorage, &srv->question);
- if (!err) err = mDNS_StartQuery(&mDNSStorage, &txt->question);
-
- if (err)
- {
- freeL("handle_resolve_request", txt);
- freeL("handle_resolve_request", srv);
- freeL("handle_resolve_request", term);
- freeL("handle_resolve_request", rstate->resolve_results);
- rstate->terminate = NULL; // prevent abort_request() from invoking termination callback
- }
- if (deliver_error(rstate, err) < 0 || err)
- {
- abort_request(rstate);
- unlink_request(rstate);
- }
- return;
-
-bad_param:
- deliver_error(rstate, mStatus_BadParamErr);
- abort_request(rstate);
- unlink_request(rstate);
- return;
-
-malloc_error:
- my_perror("ERROR: malloc");
- exit(1);
- }
-
-static void resolve_termination_callback(void *context)
- {
- resolve_termination_t *term = context;
- request_state *rs;
-
- if (!term)
- {
- LogMsg("ERROR: resolve_termination_callback: double termination");
- return;
- }
- rs = term->rstate;
-
- mDNS_StopQuery(&mDNSStorage, &term->txt->question);
- mDNS_StopQuery(&mDNSStorage, &term->srv->question);
-
- freeL("resolve_termination_callback", term->txt);
- freeL("resolve_termination_callback", term->srv);
- freeL("resolve_termination_callback", term);
- rs->termination_context = NULL;
- freeL("resolve_termination_callback", rs->resolve_results);
- rs->resolve_results = NULL;
- }
-
-
-
-static void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
-{
- int len = 0;
- char fullname[MAX_ESCAPED_DOMAIN_NAME], target[MAX_ESCAPED_DOMAIN_NAME];
- char *data;
- transfer_state result;
- reply_state *rep;
- request_state *rs = question->QuestionContext;
- resolve_result_t *res = rs->resolve_results;
- #pragma unused(m)
-
- if (!AddRecord)
- {
- if (answer->rrtype == kDNSType_TXT && res->txt == answer) res->txt = mDNSNULL;
- if (answer->rrtype == kDNSType_SRV && res->srv == answer) res->srv = mDNSNULL;
- return;
- }
-
- if (answer->rrtype == kDNSType_TXT) res->txt = answer;
- if (answer->rrtype == kDNSType_SRV) res->srv = answer;
-
- if (!res->txt || !res->srv) return; // only deliver result to client if we have both answers
-
- ConvertDomainNameToCString(&answer->name, fullname);
- ConvertDomainNameToCString(&res->srv->rdata->u.srv.target, target);
-
- // calculate reply length
- len += sizeof(DNSServiceFlags);
- len += sizeof(uint32_t); // interface index
- len += sizeof(DNSServiceErrorType);
- len += strlen(fullname) + 1;
- len += strlen(target) + 1;
- len += 2 * sizeof(uint16_t); // port, txtLen
- len += res->txt->rdlength;
-
- // allocate/init reply header
- rep = create_reply(resolve_reply, len, rs);
- rep->rhdr->flags = 0;
- rep->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, answer->InterfaceID);
- rep->rhdr->error = kDNSServiceErr_NoError;
- data = rep->sdata;
-
- // write reply data to message
- put_string(fullname, &data);
- put_string(target, &data);
- put_short(res->srv->rdata->u.srv.port.NotAnInteger, &data);
- put_short(res->txt->rdlength, &data);
- put_rdata(res->txt->rdlength, res->txt->rdata->u.txt.c, &data);
-
- result = send_msg(rep);
- if (result == t_error || result == t_terminated)
- {
- abort_request(rs);
- unlink_request(rs);
- freeL("resolve_result_callback", rep);
- }
- else if (result == t_complete) freeL("resolve_result_callback", rep);
- else append_reply(rs, rep);
- }
-
-
-
-
-// common query issuing routine for resolve and query requests
-static mStatus do_question(request_state *rstate, domainname *name, uint32_t ifi, uint16_t rrtype, int16_t rrclass)
- {
- DNSQuestion *q;
- mStatus result;
- mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, ifi);
- if (ifi && !InterfaceID) return(mStatus_BadParamErr);
-
- q = mallocL("do_question", sizeof(DNSQuestion));
- if (!q)
- {
- my_perror("ERROR: do_question - malloc");
- exit(1);
- }
- bzero(q, sizeof(DNSQuestion));
-
- q->QuestionContext = rstate;
- q->QuestionCallback = question_result_callback;
- memcpy(&q->qname, name, MAX_DOMAIN_NAME);
- q->qtype = rrtype;
- q->qclass = rrclass;
- q->InterfaceID = InterfaceID;
-
-
- rstate->termination_context = q;
- rstate->terminate = question_termination_callback;
-
- result = mDNS_StartQuery(&mDNSStorage, q);
- if (result != mStatus_NoError) LogMsg("ERROR: mDNS_StartQuery: %d", (int)result);
- return result;
- }
-
-// what gets called when a resolve is completed and we need to send the data back to the client
-static void question_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
- {
- char *data;
- char name[MAX_ESCAPED_DOMAIN_NAME];
- request_state *req;
- reply_state *rep;
- int len;
-
- #pragma unused(m)
- //mDNS_StopQuery(m, question);
- req = question->QuestionContext;
-
- // calculate reply data length
- len = sizeof(DNSServiceFlags);
- len += 2 * sizeof(uint32_t); // if index + ttl
- len += sizeof(DNSServiceErrorType);
- len += 3 * sizeof(uint16_t); // type, class, rdlen
- len += answer->rdlength;
- ConvertDomainNameToCString(&answer->name, name);
- len += strlen(name) + 1;
-
- rep = create_reply(query_reply, len, req);
- rep->rhdr->flags = AddRecord ? kDNSServiceFlagsAdd : kDNSServiceFlagsRemove;
- rep->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, answer->InterfaceID);
- rep->rhdr->error = kDNSServiceErr_NoError;
- data = rep->sdata;
-
- put_string(name, &data);
- put_short(answer->rrtype, &data);
- put_short(answer->rrclass, &data);
- put_short(answer->rdlength, &data);
- put_rdata(answer->rdlength, (char *)&answer->rdata->u, &data);
- put_long(AddRecord ? answer->rroriginalttl : 0, &data);
-
- append_reply(req, rep);
- return;
- }
-
-static void question_termination_callback(void *context)
- {
- DNSQuestion *q = context;
-
-
- mDNS_StopQuery(&mDNSStorage, q); // no need to error check
- freeL("question_termination_callback", q);
- }
-
-// If there's a comma followed by another character,
-// FindFirstSubType overwrites the comma with a nul and returns the pointer to the next character.
-// Otherwise, it returns a pointer to the final nul at the end of the string
-static char *FindFirstSubType(char *p)
- {
- while (*p)
- {
- if (p[0] == '\\' && p[1]) p += 2;
- else if (p[0] == ',' && p[1]) { *p++ = 0; return(p); }
- else p++;
- }
- return(p);
- }
-
-// If there's a comma followed by another character,
-// FindNextSubType overwrites the comma with a nul and returns the pointer to the next character.
-// If it finds an illegal unescaped dot in the subtype name, it returns mDNSNULL
-// Otherwise, it returns a pointer to the final nul at the end of the string
-static char *FindNextSubType(char *p)
- {
- while (*p)
- {
- if (p[0] == '\\' && p[1]) // If escape character
- p += 2; // ignore following character
- else if (p[0] == ',') // If we found a comma
- {
- if (p[1]) *p++ = 0;
- return(p);
- }
- else if (p[0] == '.')
- return(mDNSNULL);
- else p++;
- }
- return(p);
- }
-
-// Returns -1 if illegal subtype found
-extern mDNSs32 CountSubTypes(char *regtype);
-mDNSexport mDNSs32 CountSubTypes(char *regtype)
- {
- mDNSs32 NumSubTypes = 0;
- char *stp = FindFirstSubType(regtype);
- while (stp && *stp) // If we found a comma...
- {
- if (*stp == ',') return(-1);
- NumSubTypes++;
- stp = FindNextSubType(stp);
- }
- if (!stp) return(-1);
- return(NumSubTypes);
- }
-
-extern AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p);
-mDNSexport AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p)
- {
- AuthRecord *st = mDNSNULL;
- if (NumSubTypes)
- {
- mDNSs32 i;
- st = mallocL("ServiceSubTypes", NumSubTypes * sizeof(AuthRecord));
- if (!st) return(mDNSNULL);
- for (i = 0; i < NumSubTypes; i++)
- {
- while (*p) p++;
- p++;
- if (!MakeDomainNameFromDNSNameString(&st[i].resrec.name, p))
- { freeL("ServiceSubTypes", st); return(mDNSNULL); }
- }
- }
- return(st);
- }
-
-static void handle_browse_request(request_state *request)
- {
- DNSServiceFlags flags;
- uint32_t interfaceIndex;
- char regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME];
- DNSQuestion *q;
- domainname typedn, domdn;
- mDNSs32 NumSubTypes;
- char *ptr;
- mStatus result;
-
- if (request->ts != t_complete)
- {
- LogMsg("ERROR: handle_browse_request - transfer state != t_complete");
- abort_request(request);
- unlink_request(request);
- return;
- }
- q = mallocL("handle_browse_request", sizeof(DNSQuestion));
- if (!q)
- {
- my_perror("ERROR: handle_browse_request - malloc");
- exit(1);
- }
- bzero(q, sizeof(DNSQuestion));
-
- // extract data from message
- ptr = request->msgdata;
- flags = get_flags(&ptr);
- interfaceIndex = get_long(&ptr);
- if (get_string(&ptr, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
- get_string(&ptr, domain, MAX_ESCAPED_DOMAIN_NAME) < 0)
- goto bad_param;
-
- freeL("handle_browse_request", request->msgbuf);
- request->msgbuf = NULL;
-
- mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
- if (interfaceIndex && !InterfaceID) goto bad_param;
- q->QuestionContext = request;
- q->QuestionCallback = browse_result_callback;
-
- typedn.c[0] = 0;
- NumSubTypes = CountSubTypes(regtype);
- if (NumSubTypes < 0 || NumSubTypes > 1) goto bad_param;
- if (NumSubTypes == 1 && !AppendDNSNameString(&typedn, regtype + strlen(regtype) + 1)) goto bad_param;
-
- if (!AppendDNSNameString(&typedn, regtype) ||
- !MakeDomainNameFromDNSNameString(&domdn, domain[0] ? domain : "local."))
- goto bad_param;
- request->termination_context = q;
- request->terminate = browse_termination_callback;
- result = mDNS_StartBrowse(&mDNSStorage, q, &typedn, &domdn, InterfaceID, browse_result_callback, request);
- deliver_error(request, result);
- return;
-
-bad_param:
- deliver_error(request, mStatus_BadParamErr);
- abort_request(request);
- unlink_request(request);
- }
-
-static void browse_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
- {
- request_state *req;
- reply_state *rep;
- mStatus err;
-
- #pragma unused(m)
- req = question->QuestionContext;
-
- err = gen_rr_response(&answer->rdata->u.name, answer->InterfaceID, req, &rep);
- if (err)
- {
- if (deliver_async_error(req, browse_reply, err) < 0)
- {
- abort_request(req);
- unlink_request(req);
- }
- return;
- }
- if (AddRecord) rep->rhdr->flags |= kDNSServiceFlagsAdd; // non-zero TTL indicates add
- append_reply(req, rep);
- return;
- }
-
-static void browse_termination_callback(void *context)
- {
- DNSQuestion *q = context;
-
- mDNS_StopBrowse(&mDNSStorage, q); // no need to error-check result
- freeL("browse_termination_callback", q);
- }
-
-// service registration
-static void handle_regservice_request(request_state *request)
- {
- DNSServiceFlags flags;
- uint32_t ifi;
- char name[256], regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME], host[MAX_ESCAPED_DOMAIN_NAME];
- uint16_t txtlen;
- mDNSIPPort port;
- void *txtdata;
- char *ptr;
- domainlabel n;
- domainname t, d, h, srv;
- registered_service *r_srv;
- int srs_size;
- mStatus result;
- mDNSs32 num_subtypes;
-
- if (request->ts != t_complete)
- {
- LogMsg("ERROR: handle_regservice_request - transfer state != t_complete");
- abort_request(request);
- unlink_request(request);
- return;
- }
-
- // extract data from message
- ptr = request->msgdata;
- flags = get_flags(&ptr);
- ifi = get_long(&ptr);
- mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, ifi);
- if (ifi && !InterfaceID) goto bad_param;
- if (get_string(&ptr, name, 256) < 0 ||
- get_string(&ptr, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
- get_string(&ptr, domain, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
- get_string(&ptr, host, MAX_ESCAPED_DOMAIN_NAME) < 0)
- goto bad_param;
-
- port.NotAnInteger = get_short(&ptr);
- txtlen = get_short(&ptr);
- txtdata = get_rdata(&ptr, txtlen);
-
- // Check for sub-types after the service type
- num_subtypes = CountSubTypes(regtype);
- if (num_subtypes < 0) goto bad_param;
-
- if (!name[0]) n = (&mDNSStorage)->nicelabel;
- else if (!MakeDomainLabelFromLiteralString(&n, name))
- goto bad_param;
- if ((!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) ||
- (!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) ||
- (!ConstructServiceName(&srv, &n, &t, &d)))
- goto bad_param;
- if (host[0] && !MakeDomainNameFromDNSNameString(&h, host)) goto bad_param;
-
- r_srv = mallocL("handle_regservice_request", sizeof(registered_service));
- if (!r_srv) goto malloc_error;
- srs_size = sizeof(ServiceRecordSet) + (sizeof(RDataBody) > txtlen ? 0 : txtlen - sizeof(RDataBody));
- r_srv->srs = mallocL("handle_regservice_request", srs_size);
- if (!r_srv->srs) goto malloc_error;
-
- r_srv->subtypes = AllocateSubTypes(num_subtypes, regtype);
- if (num_subtypes && !r_srv->subtypes)
- { freeL("handle_regservice_request", r_srv); r_srv = NULL; goto malloc_error; }
- r_srv->request = request;
-
- r_srv->autoname = (!name[0]);
- r_srv->rename_on_memfree = 0;
- r_srv->renameonconflict = !(flags & kDNSServiceFlagsNoAutoRename);
- r_srv->name = n;
- request->termination_context = r_srv;
- request->terminate = regservice_termination_callback;
- request->service = r_srv;
-
- result = mDNS_RegisterService(&mDNSStorage, r_srv->srs, &n, &t, &d, host[0] ? &h : NULL, port,
- txtdata, txtlen, r_srv->subtypes, num_subtypes, InterfaceID, regservice_callback, r_srv);
- deliver_error(request, result);
- if (result != mStatus_NoError)
- {
- abort_request(request);
- unlink_request(request);
- }
- else
- {
- reset_connected_rstate(request); // reset to receive add/remove messages
- }
- return;
-
-bad_param:
- deliver_error(request, mStatus_BadParamErr);
- abort_request(request);
- unlink_request(request);
-return;
-
-malloc_error:
- my_perror("ERROR: malloc");
- exit(1);
- }
-
-// service registration callback performs three duties - frees memory for deregistered services,
-// handles name conflicts, and delivers completed registration information to the client (via
-// process_service_registraion())
-
-static void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result)
- {
- mStatus err;
- ExtraResourceRecord *extra;
- registered_service *r_srv = srs->ServiceContext;
- request_state *rs = r_srv->request;
-
- #pragma unused(m)
-
- if (!rs && (result != mStatus_MemFree && !r_srv->rename_on_memfree))
- {
- // error should never happen - safest to log and continue
- LogMsg("ERROR: regservice_callback: received result %d with a NULL request pointer\n");
- return;
- }
-
- if (result == mStatus_NoError)
- return process_service_registration(srs);
- else if (result == mStatus_MemFree)
- {
- if (r_srv->rename_on_memfree)
- {
- r_srv->rename_on_memfree = 0;
- r_srv->name = mDNSStorage.nicelabel;
- err = mDNS_RenameAndReregisterService(&mDNSStorage, srs, &r_srv->name);
- if (err) LogMsg("ERROR: regservice_callback - RenameAndReregisterService returned %d", err);
- // error should never happen - safest to log and continue
- }
- else
- {
- while (r_srv->srs->Extras)
- {
- extra = r_srv->srs->Extras;
- r_srv->srs->Extras = r_srv->srs->Extras->next;
- freeL("regservice_callback", extra);
- }
- freeL("regservice_callback", r_srv->srs);
- if (r_srv->subtypes) freeL("regservice_callback", r_srv->subtypes);
- if (r_srv->request) r_srv->request->service = NULL;
- freeL("regservice_callback", r_srv);
- return;
- }
- }
- else if (result == mStatus_NameConflict)
- {
- if (r_srv->autoname || r_srv->renameonconflict)
- {
- mDNS_RenameAndReregisterService(&mDNSStorage, srs, mDNSNULL);
- return;
- }
- else
- {
- freeL("regservice_callback", r_srv);
- freeL("regservice_callback", r_srv->srs);
- if (r_srv->subtypes) freeL("regservice_callback", r_srv->subtypes);
- if (r_srv->request) r_srv->request->service = NULL;
- freeL("regservice_callback", r_srv);
- if (deliver_async_error(rs, reg_service_reply, result) < 0)
- {
- abort_request(rs);
- unlink_request(rs);
- }
- return;
- }
- }
- else
- {
- LogMsg("ERROR: unknown result in regservice_callback");
- if (deliver_async_error(rs, reg_service_reply, result) < 0)
- {
- abort_request(rs);
- unlink_request(rs);
- }
- return;
- }
- }
-
-static mStatus handle_add_request(request_state *rstate)
- {
- registered_record_entry *re;
- ExtraResourceRecord *extra;
- uint32_t size, ttl;
- uint16_t rrtype, rdlen;
- char *ptr, *rdata;
- mStatus result;
- DNSServiceFlags flags;
- ServiceRecordSet *srs = rstate->service->srs;
-
- if (!srs)
- {
- LogMsg("ERROR: handle_add_request - no service record set in request state");
- deliver_error(rstate, mStatus_UnknownErr);
- return(-1);
- }
-
- ptr = rstate->msgdata;
- flags = get_flags(&ptr);
- rrtype = get_short(&ptr);
- rdlen = get_short(&ptr);
- rdata = get_rdata(&ptr, rdlen);
- ttl = get_long(&ptr);
-
- if (rdlen > sizeof(RDataBody)) size = rdlen;
- else size = sizeof(RDataBody);
-
- extra = mallocL("hanle_add_request", sizeof(ExtraResourceRecord) - sizeof(RDataBody) + size);
- if (!extra)
- {
- my_perror("ERROR: malloc");
- exit(1);
- }
-
- bzero(extra, sizeof(ExtraResourceRecord)); // OK if oversized rdata not zero'd
- extra->r.resrec.rrtype = rrtype;
- extra->r.rdatastorage.MaxRDLength = size;
- extra->r.resrec.rdlength = rdlen;
- memcpy(&extra->r.rdatastorage.u.data, rdata, rdlen);
- result = mDNS_AddRecordToService(&mDNSStorage, srs , extra, &extra->r.rdatastorage, ttl);
- if (result)
- {
- freeL("handle_add_request", rstate->msgbuf);
- rstate->msgbuf = NULL;
- freeL("handle_add_request", extra);
- return(result);
- }
- re = mallocL("handle_add_request", sizeof(registered_record_entry));
- if (!re)
- {
- my_perror("ERROR: malloc");
- exit(1);
- }
- re->key = rstate->hdr.reg_index;
- re->rr = &extra->r;
- re->next = rstate->reg_recs;
- rstate->reg_recs = re;
- return(result);
- }
-
-static mStatus handle_update_request(request_state *rstate)
- {
- registered_record_entry *reptr;
- AuthRecord *rr;
- RData *newrd;
- uint16_t rdlen, rdsize;
- char *ptr, *rdata;
- uint32_t ttl;
- mStatus result;
-
- if (rstate->hdr.reg_index == TXT_RECORD_INDEX)
- {
- if (!rstate->service)
- {
- deliver_error(rstate, mStatus_BadParamErr);
- return(-1);
- }
- rr = &rstate->service->srs->RR_TXT;
- }
- else
- {
- reptr = rstate->reg_recs;
- while(reptr && reptr->key != rstate->hdr.reg_index) reptr = reptr->next;
- if (!reptr) deliver_error(rstate, mStatus_BadReferenceErr);
- rr = reptr->rr;
- }
-
- ptr = rstate->msgdata;
- get_flags(&ptr); // flags unused
- rdlen = get_short(&ptr);
- rdata = get_rdata(&ptr, rdlen);
- ttl = get_long(&ptr);
-
- if (rdlen > sizeof(RDataBody)) rdsize = rdlen;
- else rdsize = sizeof(RDataBody);
- newrd = mallocL("handle_update_request", sizeof(RData) - sizeof(RDataBody) + rdsize);
- if (!newrd)
- {
- my_perror("ERROR: malloc");
- exit(1);
- }
- newrd->MaxRDLength = rdsize;
- memcpy(&newrd->u, rdata, rdlen);
- result = mDNS_Update(&mDNSStorage, rr, ttl, rdlen, newrd, update_callback);
- return(result);
- }
-
-static void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd)
- {
- #pragma unused(m)
-
- if (oldrd != &rr->rdatastorage) freeL("update_callback", oldrd);
- }
-
-static void process_service_registration(ServiceRecordSet *const srs)
- {
- reply_state *rep;
- transfer_state send_result;
- mStatus err;
- registered_service *r_srv = srs->ServiceContext;
- request_state *req = r_srv->request;
-
-
- err = gen_rr_response(&srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, req, &rep);
- if (err)
- {
- if (deliver_async_error(req, reg_service_reply, err) < 0)
- {
- abort_request(req);
- unlink_request(req);
- }
- return;
- }
- send_result = send_msg(rep);
- if (send_result == t_error || send_result == t_terminated)
- {
- abort_request(req);
- unlink_request(req);
- freeL("process_service_registration", rep);
- }
- else if (send_result == t_complete) freeL("process_service_registration", rep);
- else append_reply(req, rep);
- }
-
-static void regservice_termination_callback(void *context)
- {
- registered_service *srv = context;
-
- // only safe to free memory if registration is not valid, ie deregister fails
- if (mDNS_DeregisterService(&mDNSStorage, srv->srs) != mStatus_NoError)
- {
- freeL("regservice_callback", srv->srs);
- if (srv->subtypes) freeL("regservice_callback", srv->subtypes);
- freeL("regservice_callback", srv);
- freeL("regservice_termination_callback", srv);
- }
- }
-
-
-static mStatus handle_regrecord_request(request_state *rstate)
- {
- AuthRecord *rr;
- regrecord_callback_context *rcc;
- registered_record_entry *re;
- mStatus result;
-
- if (rstate->ts != t_complete)
- {
- LogMsg("ERROR: handle_regrecord_request - transfer state != t_complete");
- abort_request(rstate);
- unlink_request(rstate);
- return(-1);
- }
-
- rr = read_rr_from_ipc_msg(rstate->msgdata, 1);
- if (!rr) return(mStatus_BadParamErr);
-
- rcc = mallocL("handle_regrecord_request", sizeof(regrecord_callback_context));
- if (!rcc) goto malloc_error;
- rcc->rstate = rstate;
- rcc->client_context = rstate->hdr.client_context;
- rr->RecordContext = rcc;
- rr->RecordCallback = regrecord_callback;
-
- // allocate registration entry, link into list
- re = mallocL("handle_regrecord_request", sizeof(registered_record_entry));
- if (!re) goto malloc_error;
- re->key = rstate->hdr.reg_index;
- re->rr = rr;
- re->next = rstate->reg_recs;
- rstate->reg_recs = re;
-
- if (!rstate->terminate)
- {
- rstate->terminate = connected_registration_termination;
- rstate->termination_context = rstate;
- }
-
- result = mDNS_Register(&mDNSStorage, rr);
- return(result);
-
-malloc_error:
- my_perror("ERROR: malloc");
- return(-1);
- }
-
-static void regrecord_callback(mDNS *const m, AuthRecord *const rr, mStatus result)
- {
- regrecord_callback_context *rcc;
- int len;
- reply_state *reply;
- transfer_state ts;
-
- #pragma unused(m)
-
- if (result == mStatus_MemFree) { freeL("regrecord_callback", rr); return; }
- rcc = rr->RecordContext;
-
- // format result, add to the list for the request, including the client context in the header
- len = sizeof(DNSServiceFlags);
- len += sizeof(uint32_t); //interfaceIndex
- len += sizeof(DNSServiceErrorType);
-
- reply = create_reply(reg_record_reply, len, rcc->rstate);
- reply->mhdr->client_context = rcc->client_context;
- reply->rhdr->flags = 0;
- reply->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, rr->resrec.InterfaceID);
- reply->rhdr->error = result;
-
- ts = send_msg(reply);
- if (ts == t_error || ts == t_terminated)
- {
- abort_request(rcc->rstate);
- unlink_request(rcc->rstate);
- }
- else if (ts == t_complete) freeL("regrecord_callback", reply);
- else if (ts == t_morecoming) append_reply(rcc->rstate, reply); // client is blocked, link reply into list
- }
-
-static void connected_registration_termination(void *context)
- {
- registered_record_entry *fptr, *ptr = ((request_state *)context)->reg_recs;
- while(ptr)
- {
- mDNS_Deregister(&mDNSStorage, ptr->rr);
- fptr = ptr;
- ptr = ptr->next;
- freeL("connected_registration_termination", fptr);
- }
- }
-
-
-
-static mStatus handle_removerecord_request(request_state *rstate)
- {
- registered_record_entry *reptr, *prev = NULL;
- mStatus err = mStatus_UnknownErr;
- char *ptr;
- reptr = rstate->reg_recs;
-
- ptr = rstate->msgdata;
- get_flags(&ptr); // flags unused
-
- while(reptr)
- {
- if (reptr->key == rstate->hdr.reg_index) // found match
- {
- if (prev) prev->next = reptr->next;
- else rstate->reg_recs = reptr->next;
- err = mDNS_Deregister(&mDNSStorage, reptr->rr);
- freeL("handle_removerecord_request", reptr); //rr gets freed by callback
- break;
- }
- prev = reptr;
- reptr = reptr->next;
- }
- return(err);
- }
-
-
-// domain enumeration
-static void handle_enum_request(request_state *rstate)
- {
- DNSServiceFlags flags, add_default;
- uint32_t ifi;
- char *ptr = rstate->msgdata;
- domain_enum_t *def, *all;
- enum_termination_t *term;
- reply_state *reply; // initial default reply
- transfer_state tr;
- mStatus err;
- int result;
-
- if (rstate->ts != t_complete)
- {
- LogMsg("ERROR: handle_enum_request - transfer state != t_complete");
- abort_request(rstate);
- unlink_request(rstate);
- return;
- }
-
- flags = get_flags(&ptr);
- ifi = get_long(&ptr);
- mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, ifi);
- if (ifi && !InterfaceID)
- {
- deliver_error(rstate, mStatus_BadParamErr);
- abort_request(rstate);
- unlink_request(rstate);
- }
-
- // allocate context structures
- def = mallocL("handle_enum_request", sizeof(domain_enum_t));
- all = mallocL("handle_enum_request", sizeof(domain_enum_t));
- term = mallocL("handle_enum_request", sizeof(enum_termination_t));
- if (!def || !all || !term)
- {
- my_perror("ERROR: malloc");
- exit(1);
- }
-
- // enumeration requires multiple questions, so we must link all the context pointers so that
- // necessary context can be reached from the callbacks
- def->rstate = rstate;
- all->rstate = rstate;
- term->def = def;
- term->all = all;
- term->rstate = rstate;
- rstate->termination_context = term;
- rstate->terminate = enum_termination_callback;
- def->question.QuestionContext = def;
- def->type = (flags & kDNSServiceFlagsRegistrationDomains) ?
- mDNS_DomainTypeRegistrationDefault: mDNS_DomainTypeBrowseDefault;
- all->question.QuestionContext = all;
- all->type = (flags & kDNSServiceFlagsRegistrationDomains) ?
- mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse;
-
- // make the calls
- err = mDNS_GetDomains(&mDNSStorage, &all->question, all->type, InterfaceID, enum_result_callback, all);
- if (err == mStatus_NoError)
- err = mDNS_GetDomains(&mDNSStorage, &def->question, def->type, InterfaceID, enum_result_callback, def);
- result = deliver_error(rstate, err); // send error *before* returning local domain
-
- if (result < 0 || err)
- {
- abort_request(rstate);
- unlink_request(rstate);
- return;
- }
-
- // provide local. as the first domain automatically
- add_default = kDNSServiceFlagsDefault | kDNSServiceFlagsAdd | kDNSServiceFlagsFinished;
- reply = format_enumeration_reply(rstate, "local.", add_default, ifi, 0);
- tr = send_msg(reply);
- if (tr == t_error || tr == t_terminated)
- {
- freeL("handle_enum_request", def);
- freeL("handle_enum_request", all);
- abort_request(rstate);
- unlink_request(rstate);
- return;
- }
- if (tr == t_complete) freeL("handle_enum_request", reply);
- if (tr == t_morecoming) append_reply(rstate, reply); // couldn't send whole reply because client is blocked - link into list
- }
-
-static void enum_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
- {
- char domain[MAX_ESCAPED_DOMAIN_NAME];
- domain_enum_t *de = question->QuestionContext;
- DNSServiceFlags flags = 0;
- reply_state *reply;
-
- #pragma unused(m)
- if (answer->rrtype != kDNSType_PTR) return;
- if (AddRecord)
- {
- flags |= kDNSServiceFlagsAdd;
- if (de->type == mDNS_DomainTypeRegistrationDefault || de->type == mDNS_DomainTypeBrowseDefault)
- flags |= kDNSServiceFlagsDefault;
- }
- else
- {
- flags |= kDNSServiceFlagsRemove;
- }
- ConvertDomainNameToCString(&answer->rdata->u.name, domain);
- reply = format_enumeration_reply(de->rstate, domain, flags, mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, answer->InterfaceID), kDNSServiceErr_NoError);
- if (!reply)
- {
- LogMsg("ERROR: enum_result_callback, format_enumeration_reply");
- return;
- }
- reply->next = NULL;
- append_reply(de->rstate, reply);
- return;
- }
-
-static reply_state *format_enumeration_reply(request_state *rstate, char *domain, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err)
- {
- int len;
- reply_state *reply;
- char *data;
-
-
- len = sizeof(DNSServiceFlags);
- len += sizeof(uint32_t);
- len += sizeof(DNSServiceErrorType);
- len += strlen(domain) + 1;
-
- reply = create_reply(enumeration_reply, len, rstate);
- reply->rhdr->flags = flags;
- reply->rhdr->ifi = ifi;
- reply->rhdr->error = err;
- data = reply->sdata;
- put_string(domain, &data);
- return reply;
- }
-
-static void enum_termination_callback(void *context)
- {
- enum_termination_t *t = context;
- mDNS *coredata = &mDNSStorage;
-
- mDNS_StopGetDomains(coredata, &t->all->question);
- mDNS_StopGetDomains(coredata, &t->def->question);
- freeL("enum_termination_callback", t->all);
- freeL("enum_termination_callback", t->def);
- t->rstate->termination_context = NULL;
- freeL("enum_termination_callback", t);
- }
-
-static void handle_reconfirm_request(request_state *rstate)
- {
- AuthRecord *rr;
-
- rr = read_rr_from_ipc_msg(rstate->msgdata, 0);
- if (!rr) return;
- mDNS_ReconfirmByValue(&mDNSStorage, &rr->resrec);
- abort_request(rstate);
- unlink_request(rstate);
- freeL("handle_reconfirm_request", rr);
- }
-
-
-// setup rstate to accept new reg/dereg requests
-static void reset_connected_rstate(request_state *rstate)
- {
- rstate->ts = t_morecoming;
- rstate->hdr_bytes = 0;
- rstate->data_bytes = 0;
- if (rstate->msgbuf) freeL("reset_connected_rstate", rstate->msgbuf);
- rstate->msgbuf = NULL;
- rstate->bufsize = 0;
- }
-
-
-
-// returns a resource record (allocated w/ malloc) containing the data found in an IPC message
-// data must be in format flags, interfaceIndex, name, rrtype, rrclass, rdlen, rdata, (optional)ttl
-// (ttl only extracted/set if ttl argument is non-zero). returns NULL for a bad-parameter error
-static AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int ttl)
- {
- char *rdata, name[256];
- AuthRecord *rr;
- DNSServiceFlags flags;
- uint32_t interfaceIndex;
- uint16_t type, class, rdlen;
- int storage_size;
-
- flags = get_flags(&msgbuf);
- interfaceIndex = get_long(&msgbuf);
- if (get_string(&msgbuf, name, 256) < 0)
- {
- LogMsg("ERROR: read_rr_from_ipc_msg - get_string");
- return NULL;
- }
- type = get_short(&msgbuf);
- class = get_short(&msgbuf);
- rdlen = get_short(&msgbuf);
-
- if (rdlen > sizeof(RDataBody)) storage_size = rdlen;
- else storage_size = sizeof(RDataBody);
-
- rr = mallocL("read_rr_from_ipc_msg", sizeof(AuthRecord) - sizeof(RDataBody) + storage_size);
- if (!rr)
- {
- my_perror("ERROR: malloc");
- exit(1);
- }
- bzero(rr, sizeof(AuthRecord)); // ok if oversized rdata not zero'd
- rr->resrec.rdata = &rr->rdatastorage;
- rr->resrec.InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
- if (!MakeDomainNameFromDNSNameString(&rr->resrec.name, name))
- {
- LogMsg("ERROR: bad name: %s", name);
- freeL("read_rr_from_ipc_msg", rr);
- return NULL;
- }
- rr->resrec.rrtype = type;
- if ((flags & kDNSServiceFlagsShared) == kDNSServiceFlagsShared)
- rr->resrec.RecordType = kDNSRecordTypeShared;
- if ((flags & kDNSServiceFlagsUnique) == kDNSServiceFlagsUnique)
- rr->resrec.RecordType = kDNSRecordTypeUnique;
- rr->resrec.rrclass = class;
- rr->resrec.rdlength = rdlen;
- rr->resrec.rdata->MaxRDLength = rdlen;
- rdata = get_rdata(&msgbuf, rdlen);
- memcpy(rr->resrec.rdata->u.data, rdata, rdlen);
- if (ttl)
- {
- rr->resrec.rroriginalttl = get_long(&msgbuf);
- }
- return rr;
- }
-
-
-// generate a response message for a browse result, service registration result, or any other call with the
-// identical callback signature. on successful completion rep is set to point to a malloc'd reply_state struct,
-// and mStatus_NoError is returned. otherwise the appropriate error is returned.
-
-static mStatus gen_rr_response(domainname *servicename, mDNSInterfaceID id, request_state *request, reply_state **rep)
- {
- char *data;
- int len;
- domainlabel name;
- domainname type, dom;
- char namestr[MAX_DOMAIN_LABEL+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL.
- char typestr[MAX_ESCAPED_DOMAIN_NAME];
- char domstr [MAX_ESCAPED_DOMAIN_NAME];
-
- *rep = NULL;
-
- if (!DeconstructServiceName(servicename, &name, &type, &dom))
- return kDNSServiceErr_Unknown;
-
- ConvertDomainLabelToCString_unescaped(&name, namestr);
- ConvertDomainNameToCString(&type, typestr);
- ConvertDomainNameToCString(&dom, domstr);
-
- // calculate reply data length
- len = sizeof(DNSServiceFlags);
- len += sizeof(uint32_t); // if index
- len += sizeof(DNSServiceErrorType);
- len += strlen(namestr) + 1;
- len += strlen(typestr) + 1;
- len += strlen(domstr) + 1;
-
- *rep = create_reply(query_reply, len, request);
- (*rep)->rhdr->flags = 0;
- (*rep)->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id);
- (*rep)->rhdr->error = kDNSServiceErr_NoError;
- data = (*rep)->sdata;
-
- put_string(namestr, &data);
- put_string(typestr, &data);
- put_string(domstr, &data);
- return mStatus_NoError;
- }
-
-
-static int build_domainname_from_strings(domainname *srv, char *name, char *regtype, char *domain)
- {
- domainlabel n;
- domainname d, t;
-
- if (!MakeDomainLabelFromLiteralString(&n, name)) return -1;
- if (!MakeDomainNameFromDNSNameString(&t, regtype)) return -1;
- if (!MakeDomainNameFromDNSNameString(&d, domain)) return -1;
- if (!ConstructServiceName(srv, &n, &t, &d)) return -1;
- return 0;
- }
-
-
-// append a reply to the list in a request object
-static void append_reply(request_state *req, reply_state *rep)
- {
- reply_state *ptr;
-
- if (!req->replies) req->replies = rep;
- else
- {
- ptr = req->replies;
- while (ptr->next) ptr = ptr->next;
- ptr->next = rep;
- }
- rep->next = NULL;
- }
-
-
-// read_msg may be called any time when the transfer state (rs->ts) is t_morecoming.
-// returns the current state of the request (morecoming, error, complete, terminated.)
-// if there is no data on the socket, the socket will be closed and t_terminated will be returned
-static int read_msg(request_state *rs)
- {
- uint32_t nleft;
- int nread;
- char buf[4]; // dummy for death notification
-
- if (rs->ts == t_terminated || rs->ts == t_error)
- {
- LogMsg("ERROR: read_msg called with transfer state terminated or error");
- rs->ts = t_error;
- return t_error;
- }
-
- if (rs->ts == t_complete)
- { // this must be death or something is wrong
- nread = recv(rs->sd, buf, 4, 0);
- if (!nread) { rs->ts = t_terminated; return t_terminated; }
- if (nread < 0) goto rerror;
- LogMsg("ERROR: read data from a completed request.");
- rs->ts = t_error;
- return t_error;
- }
-
- if (rs->ts != t_morecoming)
- {
- LogMsg("ERROR: read_msg called with invalid transfer state (%d)", rs->ts);
- rs->ts = t_error;
- return t_error;
- }
-
- if (rs->hdr_bytes < sizeof(ipc_msg_hdr))
- {
- nleft = sizeof(ipc_msg_hdr) - rs->hdr_bytes;
- nread = recv(rs->sd, (char *)&rs->hdr + rs->hdr_bytes, nleft, 0);
- if (nread == 0) { rs->ts = t_terminated; return t_terminated; }
- if (nread < 0) goto rerror;
- rs->hdr_bytes += nread;
- if (rs->hdr_bytes > sizeof(ipc_msg_hdr))
- {
- LogMsg("ERROR: read_msg - read too many header bytes");
- rs->ts = t_error;
- return t_error;
- }
- }
-
- // only read data if header is complete
- if (rs->hdr_bytes == sizeof(ipc_msg_hdr))
- {
- if (rs->hdr.datalen == 0) // ok in removerecord requests
- {
- rs->ts = t_complete;
- rs->msgbuf = NULL;
- return t_complete;
- }
-
- if (!rs->msgbuf) // allocate the buffer first time through
- {
- rs->msgbuf = mallocL("read_msg", rs->hdr.datalen);
- if (!rs->msgbuf)
- {
- my_perror("ERROR: malloc");
- rs->ts = t_error;
- return t_error;
- }
- rs->msgdata = rs->msgbuf;
- }
- nleft = rs->hdr.datalen - rs->data_bytes;
- nread = recv(rs->sd, rs->msgbuf + rs->data_bytes, nleft, 0);
- if (nread == 0) { rs->ts = t_terminated; return t_terminated; }
- if (nread < 0) goto rerror;
- rs->data_bytes += nread;
- if (rs->data_bytes > rs->hdr.datalen)
- {
- LogMsg("ERROR: read_msg - read too many data bytes");
- rs->ts = t_error;
- return t_error;
- }
- }
-
- if (rs->hdr_bytes == sizeof(ipc_msg_hdr) && rs->data_bytes == rs->hdr.datalen)
- rs->ts = t_complete;
- else rs->ts = t_morecoming;
-
- return rs->ts;
-
-rerror:
- if (errno == EAGAIN || errno == EINTR) return rs->ts;
- my_perror("ERROR: read_msg");
- rs->ts = t_error;
- return t_error;
- }
-
-
-static int send_msg(reply_state *rs)
- {
- ssize_t nwriten;
-
- if (!rs->msgbuf)
- {
- LogMsg("ERROR: send_msg called with NULL message buffer");
- return t_error;
- }
-
- if (rs->request->no_reply) //!!!KRS this behavior should be optimized if it becomes more common
- {
- rs->ts = t_complete;
- freeL("send_msg", rs->msgbuf);
- return t_complete;
- }
-
- nwriten = send(rs->sd, rs->msgbuf + rs->nwriten, rs->len - rs->nwriten, 0);
- if (nwriten < 0)
- {
- if (errno == EINTR || errno == EAGAIN) nwriten = 0;
- else
- {
- if (errno == EPIPE)
- {
- LogMsg("broken pipe - cleanup should be handled by run-loop read wakeup");
- rs->ts = t_terminated;
- rs->request->ts = t_terminated;
- return t_terminated;
- }
- else
- {
- my_perror("ERROR: send\n");
- rs->ts = t_error;
- return t_error;
- }
- }
- }
- rs->nwriten += nwriten;
-
- if (rs->nwriten == rs->len)
- {
- rs->ts = t_complete;
- freeL("send_msg", rs->msgbuf);
- }
- return rs->ts;
- }
-
-
-
-static reply_state *create_reply(reply_op_t op, int datalen, request_state *request)
-{
- reply_state *reply;
- int totallen;
-
-
- if ((unsigned)datalen < sizeof(reply_hdr))
- {
- LogMsg("ERROR: create_reply - data length less than lenght of required fields");
- return NULL;
- }
-
- totallen = datalen + sizeof(ipc_msg_hdr);
- reply = mallocL("create_reply", sizeof(reply_state));
- if (!reply)
- {
- my_perror("ERROR: malloc");
- exit(1);
- }
- bzero(reply, sizeof(reply_state));
- reply->ts = t_morecoming;
- reply->sd = request->sd;
- reply->request = request;
- reply->len = totallen;
- reply->msgbuf = mallocL("create_reply", totallen);
- if (!reply->msgbuf)
- {
- my_perror("ERROR: malloc");
- exit(1);
- }
- bzero(reply->msgbuf, totallen);
- reply->mhdr = (ipc_msg_hdr *)reply->msgbuf;
- reply->rhdr = (reply_hdr *)(reply->msgbuf + sizeof(ipc_msg_hdr));
- reply->sdata = reply->msgbuf + sizeof(ipc_msg_hdr) + sizeof(reply_hdr);
- reply->mhdr->version = VERSION;
- reply->mhdr->op.reply_op = op;
- reply->mhdr->datalen = totallen - sizeof(ipc_msg_hdr);
- return reply;
- }
-
-
-static int deliver_error(request_state *rstate, mStatus err)
- {
- int nwritten = -1;
- undelivered_error_t *undeliv;
-
- nwritten = send(rstate->sd, &err, sizeof(mStatus), 0);
- if (nwritten < (int)sizeof(mStatus))
- {
- if (errno == EINTR || errno == EAGAIN)
- nwritten = 0;
- if (nwritten < 0)
- {
- my_perror("ERROR: send - unable to deliver error to client");
- goto error;
- }
- //client blocked - store result and come backr
- undeliv = mallocL("deliver_error", sizeof(undelivered_error_t));
- if (!undeliv)
- {
- my_perror("ERROR: malloc");
- exit(1);
- }
- undeliv->err = err;
- undeliv->nwritten = nwritten;
- undeliv->sd = rstate->sd;
- rstate->u_err = undeliv;
- return 0;
- }
- return 0;
-
-error:
- return -1;
-
- }
-
-
-// returns 0 on success, -1 if send is incomplete, or on terminal failre (request is aborted)
-static transfer_state send_undelivered_error(request_state *rs)
- {
- int nwritten;
-
- nwritten = send(rs->u_err->sd, (char *)(&rs->u_err) + rs->u_err->nwritten, sizeof(mStatus) - rs->u_err->nwritten, 0);
- if (nwritten < 0)
- {
- if (errno == EINTR || errno == EAGAIN)
- nwritten = 0;
- else
- {
- my_perror("ERROR: send - unable to deliver error to client\n");
- if (rs->u_err->sd == rs->sd) close (rs->u_err->sd);
- return t_error;
- }
- }
- if (nwritten + rs->u_err->nwritten == sizeof(mStatus))
- {
- if (rs->u_err->sd == rs->sd) close(rs->u_err->sd);
- freeL("send_undelivered_error", rs->u_err);
- rs->u_err = NULL;
- return t_complete;
- }
- rs->u_err->nwritten += nwritten;
- return t_morecoming;
- }
-
-
-// send bogus data along with an error code to the app callback
-// returns 0 on success (linking reply into list of not fully delivered),
-// -1 on failure (request should be aborted)
-static int deliver_async_error(request_state *rs, reply_op_t op, mStatus err)
- {
- int len;
- reply_state *reply;
- transfer_state ts;
-
- if (rs->no_reply) return 0;
- len = 256; // long enough for any reply handler to read all args w/o buffer overrun
- reply = create_reply(op, len, rs);
- reply->rhdr->error = err;
- ts = send_msg(reply);
- if (ts == t_error || ts == t_terminated)
- {
- freeL("deliver_async_error", reply);
- return -1;
- }
- else if (ts == t_complete) freeL("deliver_async_error", reply);
- else if (ts == t_morecoming) append_reply(rs, reply); // client is blocked, link reply into list
- return 0;
- }
-
-
-static void abort_request(request_state *rs)
- {
- reply_state *rep, *ptr;
-
- if (rs->terminate) rs->terminate(rs->termination_context); // terminate field may not be set yet
- if (rs->msgbuf) freeL("abort_request", rs->msgbuf);
- CFRunLoopRemoveSource(CFRunLoopGetCurrent(), rs->rls, kCFRunLoopDefaultMode);
- CFRunLoopSourceInvalidate(rs->rls);
- CFRelease(rs->rls);
- CFSocketInvalidate(rs->sr);
- CFRelease(rs->sr);
- rs->sd = -1;
-
- // free pending replies
- rep = rs->replies;
- while(rep)
- {
- if (rep->msgbuf) freeL("abort_request", rep->msgbuf);
- ptr = rep;
- rep = rep->next;
- freeL("abort_request", ptr);
- }
-
- if (rs->u_err)
- {
- freeL("abort_request", rs->u_err);
- rs->u_err = NULL;
- }
- }
-
-
-static void unlink_request(request_state *rs)
- {
- request_state *ptr;
-
- if (rs == all_requests)
- {
- all_requests = all_requests->next;
- freeL("unlink_request", rs);
- return;
- }
- for(ptr = all_requests; ptr->next; ptr = ptr->next)
- if (ptr->next == rs)
- {
- ptr->next = rs->next;
- freeL("unlink_request", rs);
- return;
- }
- }
-
-
-
-//hack to search-replace perror's to LogMsg's
-static void my_perror(char *errmsg)
- {
- LogMsg("%s: %s", errmsg, strerror(errno));
- }
-
-