]> git.saurik.com Git - apple/mdnsresponder.git/blobdiff - mDNSMacOSX/uds_daemon.c
mDNSResponder-66.3.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / uds_daemon.c
diff --git a/mDNSMacOSX/uds_daemon.c b/mDNSMacOSX/uds_daemon.c
deleted file mode 100644 (file)
index b1d9eba..0000000
+++ /dev/null
@@ -1,2282 +0,0 @@
-/*
- * 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));
-    }
-
-