dnssd_clientstub.c   [plain text]


/*
 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
 * 
 * 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: dnssd_clientstub.c,v $
Revision 1.19  2004/05/25 18:29:33  cheshire
Move DNSServiceConstructFullName() from dnssd_clientstub.c to dnssd_clientlib.c,
so that it's also accessible to dnssd_clientshim.c (single address space) clients.

Revision 1.18  2004/05/18 23:51:27  cheshire
Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers

Revision 1.17  2004/05/06 18:42:58  ksekar
General dns_sd.h API cleanup, including the following radars:
<rdar://problem/3592068>: Remove flags with zero value
<rdar://problem/3479569>: Passing in NULL causes a crash.

Revision 1.16  2004/03/12 22:00:37  cheshire
Added: #include <sys/socket.h>

Revision 1.15  2004/01/20 18:36:29  ksekar
Propagated Libinfo fix for <rdar://problem/3483971>: SU:
DNSServiceUpdateRecord() doesn't allow you to update the TXT record
into TOT mDNSResponder.

Revision 1.14  2004/01/19 22:39:17  cheshire
Don't use "MSG_WAITALL"; it makes send() return "Invalid argument" on Linux;
use an explicit while() loop instead. (In any case, this should only make a difference
with non-blocking sockets, which we don't use on the client side right now.)

Revision 1.13  2004/01/19 21:46:52  cheshire
Fix compiler warning

Revision 1.12  2003/12/23 20:46:47  ksekar
<rdar://problem/3497428>: sync dnssd files between libinfo & mDNSResponder

Revision 1.11  2003/12/08 21:11:42  rpantos
Changes necessary to support mDNSResponder on Linux.

Revision 1.10  2003/10/13 23:50:53  ksekar
Updated dns_sd clientstub files to bring copies in synch with
top-of-tree Libinfo:  A memory leak in dnssd_clientstub.c is fixed,
and comments in dns_sd.h are improved.

Revision 1.9  2003/08/15 21:30:39  cheshire
Bring up to date with LibInfo version

Revision 1.8  2003/08/13 23:54:52  ksekar
Bringing dnssd_clientstub.c up to date with Libinfo, per radar 3376640

Revision 1.7  2003/08/12 19:56:25  cheshire
Update to APSL 2.0

 */

#include <errno.h>
#include <sys/time.h>
#include <sys/socket.h>

#include "dnssd_ipc.h"


#define CTL_PATH_PREFIX "/tmp/dnssd_clippath."
// error socket (if needed) is named "dnssd_clipath.[pid].xxx:n" where xxx are the
// last 3 digits of the time (in seconds) and n is the 6-digit microsecond time

// general utility functions
static DNSServiceRef connect_to_server(void);
DNSServiceErrorType deliver_request(void *msg, DNSServiceRef sdr, int reuse_sd);
static ipc_msg_hdr *create_hdr(int op, int *len, char **data_start, int reuse_socket);
static int my_read(int sd, char *buf, int len);
static int my_write(int sd, char *buf, int len);
// server response handlers
static void handle_query_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *msg);
static void handle_browse_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data);
static void handle_regservice_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data);
static void handle_regrecord_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data);
static void handle_enumeration_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data);
static void handle_resolve_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data);

typedef struct _DNSServiceRef_t
    {
    int sockfd;  // connected socket between client and daemon
    int op;      // request/reply_op_t
    process_reply_callback process_reply;
    void *app_callback;
    void *app_context;
    uint32_t max_index;  //largest assigned record index - 0 if no additl. recs registered
    } _DNSServiceRef_t;			

typedef struct _DNSRecordRef_t
    {
    void *app_context;
    DNSServiceRegisterRecordReply app_callback;
    DNSRecordRef recref;
    int record_index;  // index is unique to the ServiceDiscoveryRef
    DNSServiceRef sdr;
    } _DNSRecordRef_t;


// exported functions

int DNSServiceRefSockFD(DNSServiceRef sdRef)
    {
    if (!sdRef) return -1;
    return sdRef->sockfd;
    }

// handle reply from server, calling application client callback.  If there is no reply
// from the daemon on the socket contained in sdRef, the call will block.
DNSServiceErrorType DNSServiceProcessResult(DNSServiceRef sdRef)
    {
    ipc_msg_hdr hdr;
    char *data;

    if (!sdRef || sdRef->sockfd < 0 || !sdRef->process_reply) 
        return kDNSServiceErr_BadReference;

    if (my_read(sdRef->sockfd, (void *)&hdr, sizeof(hdr)) < 0) 
        return kDNSServiceErr_Unknown;
    if (hdr.version != VERSION)
        return kDNSServiceErr_Incompatible;
    data = malloc(hdr.datalen);
    if (!data) return kDNSServiceErr_NoMemory;
    if (my_read(sdRef->sockfd, data, hdr.datalen) < 0) 
        return kDNSServiceErr_Unknown;
    sdRef->process_reply(sdRef, &hdr, data);
    free(data);
    return kDNSServiceErr_NoError;
    }


void DNSServiceRefDeallocate(DNSServiceRef sdRef)
    {
    if (!sdRef) return;
    if (sdRef->sockfd > 0) close(sdRef->sockfd);
    free(sdRef);
    }


DNSServiceErrorType DNSServiceResolve
    (
    DNSServiceRef                  	*sdRef,
    const DNSServiceFlags               flags,
    const uint32_t                      interfaceIndex,
    const char                         	*name,
    const char                         	*regtype,
    const char                         	*domain,
    const DNSServiceResolveReply        callBack,
    void                               	*context
    )
    {
    char *msg = NULL, *ptr;
    int len;
    ipc_msg_hdr *hdr;
    DNSServiceRef sdr;
    DNSServiceErrorType err;
    
    if (!sdRef) return kDNSServiceErr_BadParam;
    *sdRef = NULL;
    
	if (!name || !regtype || !domain || !callBack) return kDNSServiceErr_BadParam;

    // calculate total message length
    len = sizeof(flags);
    len += sizeof(interfaceIndex);
    len += strlen(name) + 1;
    len += strlen(regtype) + 1;
    len += strlen(domain) + 1;

    hdr = create_hdr(resolve_request, &len, &ptr, 1);
    if (!hdr) goto error;
    msg = (void *)hdr;

    put_flags(flags, &ptr);
    put_long(interfaceIndex, &ptr);
    put_string(name, &ptr);
    put_string(regtype, &ptr);
    put_string(domain, &ptr);
    
    sdr = connect_to_server();
    if (!sdr) goto error;
    err = deliver_request(msg, sdr, 1);
    if (err)
        {
        DNSServiceRefDeallocate(sdr);
        return err;
        }
    sdr->op = resolve_request;
    sdr->process_reply = handle_resolve_response;
    sdr->app_callback = callBack;
    sdr->app_context = context;
    *sdRef = sdr;
    
    return err;

error:
    if (msg) free(msg);
    if (*sdRef) { free(*sdRef);  *sdRef = NULL; }
    return kDNSServiceErr_Unknown;
    }
    
    
static void handle_resolve_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data)
    {
    DNSServiceFlags flags;
    char fullname[kDNSServiceMaxDomainName];
    char target[kDNSServiceMaxDomainName];
    uint16_t port, txtlen;
    uint32_t ifi;
    DNSServiceErrorType err;
    char *txtrecord;
    int str_error = 0;
    (void)hdr; 		//unused
    
    flags = get_flags(&data);
    ifi = get_long(&data);
    err = get_error_code(&data);
    if (get_string(&data, fullname, kDNSServiceMaxDomainName) < 0) str_error = 1;
    if (get_string(&data, target, kDNSServiceMaxDomainName) < 0) str_error = 1;
    port = get_short(&data);
    txtlen = get_short(&data);
    txtrecord = get_rdata(&data, txtlen);

	if (!err && str_error) err = kDNSServiceErr_Unknown;
    ((DNSServiceResolveReply)sdr->app_callback)(sdr, flags, ifi, err, fullname, target, port, txtlen, txtrecord, sdr->app_context);
    }
    
    


DNSServiceErrorType DNSServiceQueryRecord
(
 DNSServiceRef       	         	*sdRef,
 const DNSServiceFlags		         flags,
 const uint32_t                      	interfaceIndex,
 const char                   		*name,
 const uint16_t                      	rrtype,
 const uint16_t                      	rrclass,
 const DNSServiceQueryRecordReply     	callBack,
 void                               	*context
 )
    {
    char *msg = NULL, *ptr;
    int len;
    ipc_msg_hdr *hdr;
    DNSServiceRef sdr;
    DNSServiceErrorType err;
    
    if (!sdRef) return kDNSServiceErr_BadParam;
    *sdRef = NULL;

    if (!name) name = "\0";

    // calculate total message length
    len = sizeof(flags);
    len += sizeof(uint32_t);  //interfaceIndex
    len += strlen(name) + 1;
    len += 2 * sizeof(uint16_t);  // rrtype, rrclass

    hdr = create_hdr(query_request, &len, &ptr, 1);
    if (!hdr) goto error;
    msg = (void *)hdr;

    put_flags(flags, &ptr);
    put_long(interfaceIndex, &ptr);
    put_string(name, &ptr);
    put_short(rrtype, &ptr);
    put_short(rrclass, &ptr);

    sdr = connect_to_server();
    if (!sdr) goto error;
    err = deliver_request(msg, sdr, 1);
    if (err)
        {
        DNSServiceRefDeallocate(sdr);
        return err;
        }

    sdr->op = query_request;
    sdr->process_reply = handle_query_response;
    sdr->app_callback = callBack;
    sdr->app_context = context;
    *sdRef = sdr;
    return err;

error:
    if (msg) free(msg);
    if (*sdRef) { free(*sdRef);  *sdRef = NULL; }
    return kDNSServiceErr_Unknown;
    }


static void handle_query_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data)
    {
    DNSServiceFlags flags;
    uint32_t interfaceIndex, ttl;
    DNSServiceErrorType errorCode;
    char name[kDNSServiceMaxDomainName]; 
    uint16_t rrtype, rrclass, rdlen;
    char *rdata;
    int str_error = 0;
    (void)hdr;//Unused

    flags = get_flags(&data);
    interfaceIndex = get_long(&data);
    errorCode = get_error_code(&data);
    if (get_string(&data, name, kDNSServiceMaxDomainName) < 0) str_error = 1;
    rrtype = get_short(&data);
    rrclass = get_short(&data);
    rdlen = get_short(&data);
    rdata = get_rdata(&data, rdlen);
	ttl = get_long(&data);

	if (!errorCode && str_error) errorCode = kDNSServiceErr_Unknown;
	((DNSServiceQueryRecordReply)sdr->app_callback)(sdr, flags, interfaceIndex, errorCode, name, rrtype, rrclass,
													rdlen, rdata, ttl, sdr->app_context);
    return;
    }

DNSServiceErrorType DNSServiceBrowse
(
 DNSServiceRef                      *sdRef,
 const DNSServiceFlags              flags,
 const uint32_t                     interfaceIndex,
 const char                         *regtype,
 const char                         *domain,
 const DNSServiceBrowseReply        callBack,
 void                               *context
 )
    {
    char *msg = NULL, *ptr;
    int len;
    ipc_msg_hdr *hdr;
    DNSServiceRef sdr;
    DNSServiceErrorType err;

    if (!sdRef) return kDNSServiceErr_BadParam;
    *sdRef = NULL;

    if (!domain) domain = "";

    len = sizeof(flags);
    len += sizeof(interfaceIndex);
    len += strlen(regtype) + 1;
    len += strlen(domain) + 1;

    hdr = create_hdr(browse_request, &len, &ptr, 1);
    if (!hdr) goto error;
    msg = (char *)hdr;
    put_flags(flags, &ptr);
    put_long(interfaceIndex, &ptr);
    put_string(regtype, &ptr);
    put_string(domain, &ptr);

    sdr = connect_to_server();
    if (!sdr) goto error;
    err = deliver_request(msg, sdr, 1);
    if (err)
        {
        DNSServiceRefDeallocate(sdr);
        return err;
        }
    sdr->op = browse_request;
    sdr->process_reply = handle_browse_response;
    sdr->app_callback = callBack;
    sdr->app_context = context;
    *sdRef = sdr;
    return err;

error:
    if (msg) free(msg);
    if (*sdRef) { free(*sdRef);  *sdRef = NULL; }
    return kDNSServiceErr_Unknown;
    }




static void handle_browse_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data)
    {
    DNSServiceFlags      flags;
    uint32_t                      interfaceIndex;
    DNSServiceErrorType      errorCode;
    char replyName[256], replyType[kDNSServiceMaxDomainName], 
        replyDomain[kDNSServiceMaxDomainName];
    int str_error = 0;
	(void)hdr;//Unused

    flags = get_flags(&data);
    interfaceIndex = get_long(&data);
    errorCode = get_error_code(&data);
    if (get_string(&data, replyName, 256) < 0) str_error = 1;
    if (get_string(&data, replyType, kDNSServiceMaxDomainName) < 0) str_error = 1;
    if (get_string(&data, replyDomain, kDNSServiceMaxDomainName) < 0) str_error = 1;
	if (!errorCode && str_error) errorCode = kDNSServiceErr_Unknown;
	((DNSServiceBrowseReply)sdr->app_callback)(sdr, flags, interfaceIndex, errorCode, replyName, replyType, replyDomain, sdr->app_context);
    }


DNSServiceErrorType DNSServiceRegister
    (
    DNSServiceRef                       *sdRef,
    const DNSServiceFlags               flags,
    const uint32_t                      interfaceIndex,
    const char                          *name,         
    const char                          *regtype,  
    const char                          *domain,       
    const char                          *host,         
    const uint16_t                      port,
    const uint16_t                      txtLen,
    const void                          *txtRecord,    
    const DNSServiceRegisterReply       callBack,      
    void                                *context       
    )
    {
    char *msg = NULL, *ptr;
    int len;
    ipc_msg_hdr *hdr;
    DNSServiceRef sdr;
    DNSServiceErrorType err;

    if (!sdRef) return kDNSServiceErr_BadParam;
    *sdRef = NULL;

    if (!name) name = "";
    if (!regtype) return kDNSServiceErr_BadParam;
    if (!domain) domain = "";
    if (!host) host = "";
    if (!txtRecord) (char *)txtRecord = "";
    
    // auto-name must also have auto-rename
    if (!name[0]  && (flags & kDNSServiceFlagsNoAutoRename))
        return kDNSServiceErr_BadParam;

    // no callback must have auto-name
    if (!callBack && name[0]) return kDNSServiceErr_BadParam;

    len = sizeof(DNSServiceFlags);
    len += sizeof(uint32_t);  // interfaceIndex
    len += strlen(name) + strlen(regtype) + strlen(domain) + strlen(host) + 4;
    len += 2 * sizeof(uint16_t);  // port, txtLen
    len += txtLen;

    hdr = create_hdr(reg_service_request, &len, &ptr, 1);
    if (!hdr) goto error;
    if (!callBack) hdr->flags |= IPC_FLAGS_NOREPLY;
    msg = (char *)hdr;
    put_flags(flags, &ptr);
    put_long(interfaceIndex, &ptr);
    put_string(name, &ptr);
    put_string(regtype, &ptr);
    put_string(domain, &ptr);
    put_string(host, &ptr);
    put_short(port, &ptr);
    put_short(txtLen, &ptr);
    put_rdata(txtLen, txtRecord, &ptr);

    sdr = connect_to_server();
    if (!sdr) goto error;
    err = deliver_request(msg, sdr, 1);
    if (err)
        {
        DNSServiceRefDeallocate(sdr);
        return err;
        }
        
    sdr->op = reg_service_request;
    sdr->process_reply = callBack ? handle_regservice_response : NULL;
    sdr->app_callback = callBack;
    sdr->app_context = context;
    *sdRef = sdr;

    return err;
    
error:
    if (msg) free(msg);
    if (*sdRef) 	{ free(*sdRef);  *sdRef = NULL; }
    return kDNSServiceErr_Unknown;
    }


static void handle_regservice_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data)
    {
    DNSServiceFlags flags;
    uint32_t interfaceIndex;
    DNSServiceErrorType errorCode;
    char name[256], regtype[kDNSServiceMaxDomainName], domain[kDNSServiceMaxDomainName];
    int str_error = 0;
	(void)hdr;//Unused

    flags = get_flags(&data);
    interfaceIndex = get_long(&data);
    errorCode = get_error_code(&data);
    if (get_string(&data, name, 256) < 0) str_error = 1;
    if (get_string(&data, regtype, kDNSServiceMaxDomainName) < 0) str_error = 1;
    if (get_string(&data, domain, kDNSServiceMaxDomainName) < 0) str_error = 1;
	if (!errorCode && str_error) errorCode = kDNSServiceErr_Unknown;
    ((DNSServiceRegisterReply)sdr->app_callback)(sdr, flags, errorCode, name, regtype, domain, sdr->app_context);
    }

DNSServiceErrorType DNSServiceEnumerateDomains
(
 DNSServiceRef                    *sdRef,
 const DNSServiceFlags            flags,
 const uint32_t                   interfaceIndex,
 const DNSServiceDomainEnumReply  callBack,
 void                             *context
 )
    {
    char *msg = NULL, *ptr;
    int len;
    ipc_msg_hdr *hdr;
    DNSServiceRef sdr;
    DNSServiceErrorType err;


    if (!sdRef) return kDNSServiceErr_BadParam;
    *sdRef = NULL;

	if (flags != kDNSServiceFlagsBrowseDomains && flags != kDNSServiceFlagsRegistrationDomains)
		return kDNSServiceErr_BadParam;

	len = sizeof(DNSServiceFlags);
    len += sizeof(uint32_t);

    hdr = create_hdr(enumeration_request, &len, &ptr, 1);
    if (!hdr) goto error;
    msg = (void *)hdr;

    put_flags(flags, &ptr);
    put_long(interfaceIndex, &ptr);

    sdr = connect_to_server();
    if (!sdr) goto error;
    err = deliver_request(msg, sdr, 1);
    if (err)
        {
        DNSServiceRefDeallocate(sdr);
        return err;
        }

    sdr->op = enumeration_request;
    sdr->process_reply = handle_enumeration_response;
    sdr->app_callback = callBack;
    sdr->app_context = context;
    *sdRef = sdr;
    return err;

error:
    if (msg) free(msg);
    if (*sdRef) { free(*sdRef);  *sdRef = NULL; }
    return kDNSServiceErr_Unknown;
    }


static void handle_enumeration_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data)
    {
    DNSServiceFlags flags;
    uint32_t interfaceIndex;
    DNSServiceErrorType err;
    char domain[kDNSServiceMaxDomainName];
    int str_error = 0;
	(void)hdr;//Unused

    flags = get_flags(&data);
    interfaceIndex = get_long(&data);
    err = get_error_code(&data);
    if (get_string(&data, domain, kDNSServiceMaxDomainName) < 0) str_error = 1;
	if (!err && str_error) err = kDNSServiceErr_Unknown;
    ((DNSServiceDomainEnumReply)sdr->app_callback)(sdr, flags, interfaceIndex, err, domain, sdr->app_context);
    }


DNSServiceErrorType DNSServiceCreateConnection(DNSServiceRef *sdRef)
    {
    if (!sdRef) return kDNSServiceErr_BadParam;
    *sdRef = connect_to_server();
    if (!*sdRef)
            return kDNSServiceErr_Unknown;
    (*sdRef)->op = connection;
    (*sdRef)->process_reply = handle_regrecord_response;
    return 0;
    }



static void handle_regrecord_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data)
    {
    DNSServiceFlags flags;
    uint32_t interfaceIndex;
    DNSServiceErrorType errorCode;
    DNSRecordRef rref = hdr->client_context.context;
    
    if (sdr->op != connection) 
        {
        rref->app_callback(rref->sdr, rref, 0, kDNSServiceErr_Unknown, rref->app_context);
        return;
        }
    flags = get_flags(&data);
    interfaceIndex = get_long(&data);
    errorCode = get_error_code(&data);

    rref->app_callback(rref->sdr, rref, flags, errorCode, rref->app_context);
    }

DNSServiceErrorType DNSServiceRegisterRecord
(
 const DNSServiceRef            	sdRef,
 DNSRecordRef                 		*RecordRef,  
 const DNSServiceFlags      	        flags,
 const uint32_t                      	interfaceIndex,
 const char                         	*fullname,
 const uint16_t                      	rrtype,
 const uint16_t                      	rrclass,
 const uint16_t                      	rdlen,
 const void                         	*rdata,
 const uint32_t                      	ttl,
 const DNSServiceRegisterRecordReply  	callBack,
 void                               	*context
 )
    {
    char *msg = NULL, *ptr;
    int len;
    ipc_msg_hdr *hdr = NULL;
    DNSServiceRef tmp = NULL;
    DNSRecordRef rref = NULL;
    
    if (!sdRef || sdRef->op != connection || sdRef->sockfd < 0) 
        return kDNSServiceErr_BadReference;
    *RecordRef = NULL;
    
	if (flags != kDNSServiceFlagsShared && flags != kDNSServiceFlagsUnique)
		return kDNSServiceErr_BadReference;

	len = sizeof(DNSServiceFlags);
    len += 2 * sizeof(uint32_t);  // interfaceIndex, ttl
    len += 3 * sizeof(uint16_t);  // rrtype, rrclass, rdlen
    len += strlen(fullname) + 1;
    len += rdlen;

    hdr = create_hdr(reg_record_request, &len, &ptr, 0);
    if (!hdr) goto error;
    msg = (char *)hdr;
    put_flags(flags, &ptr);
    put_long(interfaceIndex, &ptr);
    put_string(fullname, &ptr);
    put_short(rrtype, &ptr);
    put_short(rrclass, &ptr);
    put_short(rdlen, &ptr);
    put_rdata(rdlen, rdata, &ptr);
    put_long(ttl, &ptr);

    rref = malloc(sizeof(_DNSRecordRef_t));
    if (!rref) goto error;
    rref->app_context = context;
    rref->app_callback = callBack;
    rref->record_index = sdRef->max_index++;
    rref->sdr = sdRef;
    *RecordRef = rref;
    hdr->client_context.context = rref;
    hdr->reg_index = rref->record_index;  
    
    return deliver_request(msg, sdRef, 0);

error:
    if (rref) free(rref);
    if (tmp) free(tmp);
    if (hdr) free(hdr);
    return kDNSServiceErr_Unknown;
    }

//sdRef returned by DNSServiceRegister()
DNSServiceErrorType DNSServiceAddRecord
    (
    const DNSServiceRef	                sdRef,
    DNSRecordRef			*RecordRef,
    const DNSServiceFlags               flags,
    const uint16_t			rrtype,
    const uint16_t			rdlen,
    const void				*rdata,
    const uint32_t			ttl
    )
    {
    ipc_msg_hdr *hdr;
    int len = 0;
    char *ptr;
    DNSRecordRef rref;

    if (!sdRef || (sdRef->op != reg_service_request) || !RecordRef) 
        return kDNSServiceErr_BadReference;
    *RecordRef = NULL;
    
    len += 2 * sizeof(uint16_t);  //rrtype, rdlen
    len += rdlen;
    len += sizeof(uint32_t);
    len += sizeof(DNSServiceFlags);

    hdr = create_hdr(add_record_request, &len, &ptr, 0);
    if (!hdr) return kDNSServiceErr_Unknown;
    put_flags(flags, &ptr);
    put_short(rrtype, &ptr);
    put_short(rdlen, &ptr);
    put_rdata(rdlen, rdata, &ptr);
    put_long(ttl, &ptr);

    rref = malloc(sizeof(_DNSRecordRef_t));
    if (!rref) goto error;
    rref->app_context = NULL;
    rref->app_callback = NULL;
    rref->record_index = sdRef->max_index++;
    rref->sdr = sdRef;
    *RecordRef = rref;
    hdr->client_context.context = rref;
    hdr->reg_index = rref->record_index;  
    return deliver_request((char *)hdr, sdRef, 0);

error:
    if (hdr) free(hdr);
    if (rref) free(rref);
    if (*RecordRef) *RecordRef = NULL;
    return kDNSServiceErr_Unknown;
}
    

//DNSRecordRef returned by DNSServiceRegisterRecord or DNSServiceAddRecord
DNSServiceErrorType DNSServiceUpdateRecord
    (
    const DNSServiceRef         	sdRef,
    DNSRecordRef			RecordRef,
    const DNSServiceFlags               flags,
    const uint16_t			rdlen,
    const void				*rdata,
    const uint32_t			ttl
    )
    {
    ipc_msg_hdr *hdr;
    int len = 0;
    char *ptr;

	if (!sdRef) return kDNSServiceErr_BadReference;
    
    len += sizeof(uint16_t);
    len += rdlen;
    len += sizeof(uint32_t);
    len += sizeof(DNSServiceFlags);

    hdr = create_hdr(update_record_request, &len, &ptr, 0);
    if (!hdr) return kDNSServiceErr_Unknown;
    hdr->reg_index = RecordRef ? RecordRef->record_index : TXT_RECORD_INDEX;
    put_flags(flags, &ptr);
    put_short(rdlen, &ptr);
    put_rdata(rdlen, rdata, &ptr);
    put_long(ttl, &ptr);
    return deliver_request((char *)hdr, sdRef, 0);
    }
    


DNSServiceErrorType DNSServiceRemoveRecord
(
 const DNSServiceRef            sdRef,
 const DNSRecordRef            	RecordRef,
 const DNSServiceFlags          flags
 )
    {
    ipc_msg_hdr *hdr;
    int len = 0;
    char *ptr;
    DNSServiceErrorType err;

    if (!sdRef || !RecordRef || !sdRef->max_index) 
        return kDNSServiceErr_BadReference;
    
    len += sizeof(flags);
    hdr = create_hdr(remove_record_request, &len, &ptr, 0);
    if (!hdr) return kDNSServiceErr_Unknown;
    hdr->reg_index = RecordRef->record_index;
    put_flags(flags, &ptr);
    err = deliver_request((char *)hdr, sdRef, 0);
    if (!err) free(RecordRef);
    return err;
    }


void DNSServiceReconfirmRecord
(
 const DNSServiceFlags              flags,
 const uint32_t                     interfaceIndex,
 const char                         *fullname,
 const uint16_t                     rrtype,
 const uint16_t                     rrclass,
 const uint16_t                     rdlen,
 const void                         *rdata
 )
    {
    char *ptr;
    int len;
    ipc_msg_hdr *hdr;
    DNSServiceRef tmp;

    len = sizeof(DNSServiceFlags);
    len += sizeof(uint32_t);
    len += strlen(fullname) + 1;
    len += 3 * sizeof(uint16_t);
    len += rdlen;
    tmp = connect_to_server();
    if (!tmp) return;
    hdr = create_hdr(reconfirm_record_request, &len, &ptr, 1);
    if (!hdr) return;

    put_flags(flags, &ptr);
    put_long(interfaceIndex, &ptr);
    put_string(fullname, &ptr);
    put_short(rrtype, &ptr);
    put_short(rrclass, &ptr);
    put_short(rdlen, &ptr);
    put_rdata(rdlen, rdata, &ptr);
    my_write(tmp->sockfd, (char *)hdr, len);
    DNSServiceRefDeallocate(tmp);
    }
        
        
    // return a connected service ref (deallocate with DNSServiceRefDeallocate)
static DNSServiceRef connect_to_server(void)
    {
    struct sockaddr_un saddr;
    DNSServiceRef sdr;

    sdr = malloc(sizeof(_DNSServiceRef_t));
    if (!sdr) return NULL;

    if ((sdr->sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) 
  	{
        free(sdr);
        return NULL;
  	}

    saddr.sun_family = AF_LOCAL;
    strcpy(saddr.sun_path, MDNS_UDS_SERVERPATH);
    if (connect(sdr->sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
  	{
        free(sdr);
        return NULL;
  	}
    return sdr;	
    }




int my_write(int sd, char *buf, int len)
    {
    while (len)
    	{
    	ssize_t num_written = send(sd, buf, len, 0);
    	if (num_written < 0 || num_written > len) return -1;
    	buf += num_written;
    	len -= num_written;
    	}
    return 0;
    }


// read len bytes.  return 0 on success, -1 on error
int my_read(int sd, char *buf, int len)
    {
    if (recv(sd, buf, len, MSG_WAITALL) != len)  return -1;
    return 0;
    }


DNSServiceErrorType deliver_request(void *msg, DNSServiceRef sdr, int reuse_sd)
    {
    ipc_msg_hdr *hdr = msg;
    mode_t mask;
    struct sockaddr_un caddr, daddr;  // (client and daemon address structs)
    char *path = NULL;
    int listenfd = -1, errsd = -1, len;
    DNSServiceErrorType err = kDNSServiceErr_Unknown;
    
    if (!hdr || sdr->sockfd < 0) return kDNSServiceErr_Unknown;

    if (!reuse_sd) 
        {
        // setup temporary error socket
        if ((listenfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) 
            goto cleanup;

        bzero(&caddr, sizeof(caddr));
        caddr.sun_family = AF_LOCAL;
#ifndef NOT_HAVE_SA_LEN		// According to Stevens (section 3.2), there is no portable way to 
							// determine whether sa_len is defined on a particular platform. 
		caddr.sun_len = sizeof(struct sockaddr_un);
#endif
	    path = (char *)msg + sizeof(ipc_msg_hdr);
        strcpy(caddr.sun_path, path);
        mask = umask(0);
        if (bind(listenfd, (struct sockaddr *)&caddr, sizeof(caddr)) < 0)
	  {
	    umask(mask);
            goto cleanup;
	  }
        umask(mask);
        listen(listenfd, 1);
        }
        
    if (my_write(sdr->sockfd, msg, hdr->datalen + sizeof(ipc_msg_hdr)) < 0)  
        goto cleanup;
    free(msg);
    msg = NULL;

    if (reuse_sd) errsd = sdr->sockfd;
    else 
        {
        len = sizeof(daddr);
        errsd = accept(listenfd, (struct sockaddr *)&daddr, &len);
        if (errsd < 0)  goto cleanup;
        }
    
    len = recv(errsd, &err, sizeof(err), MSG_WAITALL);
    if (len != sizeof(err))
        {
        err = kDNSServiceErr_Unknown;
        }
cleanup:
    if (!reuse_sd && listenfd > 0) close(listenfd);
    if (!reuse_sd && errsd > 0) close(errsd);	
    if (!reuse_sd && path) unlink(path);
    if (msg) free(msg);
    return err;
    }
    
    
    
/* create_hdr
 *
 * allocate and initialize an ipc message header.  value of len should initially be the
 * length of the data, and is set to the value of the data plus the header.  data_start 
 * is set to point to the beginning of the data section.  reuse_socket should be non-zero
 * for calls that can receive an immediate error return value on their primary socket.
 * if zero, the path to a control socket is appended at the beginning of the message buffer.
 * data_start is set past this string.
 */
     
static ipc_msg_hdr *create_hdr(int op, int *len, char **data_start, int reuse_socket)
    {
    char *msg = NULL;
    ipc_msg_hdr *hdr;
    int datalen;
    char ctrl_path[256];
    struct timeval time;

    if (!reuse_socket)
        {
	    if (gettimeofday(&time, NULL) < 0) return NULL;
	    sprintf(ctrl_path, "%s%d-%.3lx-%.6lu", CTL_PATH_PREFIX, (int)getpid(), 
		  (unsigned long)(time.tv_sec & 0xFFF), (unsigned long)(time.tv_usec));
        *len += strlen(ctrl_path) + 1;
        }
    
        
    datalen = *len;
    *len += sizeof(ipc_msg_hdr);

    // write message to buffer
    msg = malloc(*len);
    if (!msg) return NULL;

    bzero(msg, *len);
    hdr = (void *)msg;
    hdr->datalen = datalen;
    hdr->version = VERSION;
    hdr->op.request_op = op;
    if (reuse_socket) hdr->flags |= IPC_FLAGS_REUSE_SOCKET;
    *data_start = msg + sizeof(ipc_msg_hdr);
    if (!reuse_socket)  put_string(ctrl_path, data_start);
    return hdr;
    }