#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>
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
{
int autoname;
int renameonconflict;
int rename_on_memfree; 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
{
CFRunLoopSourceRef rls;
CFSocketRef sr;
int sd;
int errfd;
transfer_state ts;
uint32_t hdr_bytes; ipc_msg_hdr hdr;
uint32_t data_bytes; char *msgbuf; char *msgdata; int bufsize;
int no_reply; void *client_context; struct reply_state *replies; undelivered_error_t *u_err;
void *termination_context;
req_termination_fn terminate;
registered_record_entry *reg_recs; registered_service *service; struct resolve_result_t *resolve_results;
struct request_state *next;
} request_state;
typedef struct
{
DNSServiceFlags flags;
uint32_t ifi;
DNSServiceErrorType error;
} reply_hdr;
typedef struct reply_state
{
int sd;
transfer_state ts;
uint32_t nwriten;
uint32_t len;
struct request_state *request; struct reply_state *next; ipc_msg_hdr *mhdr;
reply_hdr *rhdr;
char *sdata; char *msgbuf;
} reply_state;
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;
static int listenfd = -1;
static request_state *all_requests = NULL;
#define MAX_OPENFILES 1024
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 void handle_add_request(request_state *rstate);
static void 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 void 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 void 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);
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); 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);
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)
{
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;
}
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 && (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) {
if (nextevent - now > mDNSPlatformOneSecond)
nextevent = now + mDNSPlatformOneSecond;
break; }
}
}
if (result == t_terminated || result == t_error)
{
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->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);
}
}
}
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;
#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;
}
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;
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); 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;
}
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;
}
if (rstate->hdr.flags & IPC_FLAGS_NOREPLY) rstate->no_reply = 1;
if (rstate->hdr.flags & IPC_FLAGS_REUSE_SOCKET)
rstate->errfd = rstate->sd;
else
{
if ((rstate->errfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
{
my_perror("ERROR: socket");
exit(1);
}
if (fcntl(rstate->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); bzero(&cliaddr, sizeof(cliaddr));
cliaddr.sun_family = AF_LOCAL;
strcpy(cliaddr.sun_path, ctrl_path);
if (connect(rstate->errfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)) < 0)
{
my_perror("ERROR: connect");
abort_request(rstate);
unlink_request(rstate);
}
}
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 reg_record_request: handle_regrecord_request(rstate); break;
case add_record_request: handle_add_request(rstate); break;
case update_record_request: handle_update_request(rstate); break;
case remove_record_request: handle_removerecord_request(rstate); break;
case reconfirm_record_request: handle_reconfirm_request(rstate); break;
default:
debugf("ERROR: udsserver_recv_request - unsupported request type: %d", rstate->hdr.op.request_op);
}
}
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; 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; 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;
}
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;
freeL("handle_resolve_request", rstate->msgbuf);
rstate->msgbuf = NULL;
if (build_domainname_from_strings(&fqdn, name, regtype, domain) < 0)
goto bad_param;
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;
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;
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;
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));
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; }
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;
ConvertDomainNameToCString(&answer->name, fullname);
ConvertDomainNameToCString(&res->srv->rdata->u.srv.target, target);
len += sizeof(DNSServiceFlags);
len += sizeof(uint32_t); len += sizeof(DNSServiceErrorType);
len += strlen(fullname) + 1;
len += strlen(target) + 1;
len += 2 * sizeof(uint16_t); len += res->txt->rdlength;
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;
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);
}
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;
}
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)
req = question->QuestionContext;
len = sizeof(DNSServiceFlags);
len += 2 * sizeof(uint32_t); len += sizeof(DNSServiceErrorType);
len += 3 * sizeof(uint16_t); 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); freeL("question_termination_callback", q);
}
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;
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));
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;
if (!MakeDomainNameFromDNSNameString(&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; append_reply(req, rep);
return;
}
static void browse_termination_callback(void *context)
{
DNSQuestion *q = context;
mDNS_StopBrowse(&mDNSStorage, q); freeL("browse_termination_callback", q);
}
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;
char *sub, *rtype_ptr;
int i, num_subtypes;
if (request->ts != t_complete)
{
LogMsg("ERROR: handle_regservice_request - transfer state != t_complete");
abort_request(request);
unlink_request(request);
return;
}
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);
rtype_ptr = regtype;
num_subtypes = -1;
while((sub = strsep(&rtype_ptr, ",")))
if (*sub) num_subtypes++;
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;
if (num_subtypes > 0)
{
r_srv->subtypes = mallocL("handle_regservice_request", num_subtypes * sizeof(AuthRecord));
if (!r_srv->subtypes) goto malloc_error;
sub = regtype + strlen(regtype) + 1;
for (i = 0; i < num_subtypes; i++)
{
if (!MakeDomainNameFromDNSNameString(&(r_srv->subtypes + i)->resrec.name, sub))
{
freeL("handle_regservice_request", r_srv->subtypes);
freeL("handle_regservice_request", r_srv);
r_srv = NULL;
goto bad_param;
}
sub += strlen(sub) + 1;
}
}
else r_srv->subtypes = NULL;
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); }
return;
bad_param:
deliver_error(request, mStatus_BadParamErr);
abort_request(request);
unlink_request(request);
return;
malloc_error:
my_perror("ERROR: malloc");
exit(1);
}
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))
{
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);
}
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 void 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;
}
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)); 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);
deliver_error(rstate, result);
reset_connected_rstate(rstate);
if (result)
{
freeL("handle_add_request", rstate->msgbuf);
rstate->msgbuf = NULL;
freeL("handle_add_request", extra);
return;
}
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;
}
static void 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;
}
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); 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);
deliver_error(rstate, result);
reset_connected_rstate(rstate);
}
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;
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 void 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;
}
rr = read_rr_from_ipc_msg(rstate->msgdata, 1);
if (!rr)
{
deliver_error(rstate, mStatus_BadParamErr);
return;
}
rcc = mallocL("hanlde_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;
re = mallocL("hanlde_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);
deliver_error(rstate, result);
reset_connected_rstate(rstate);
return;
malloc_error:
my_perror("ERROR: malloc");
return;
}
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;
len = sizeof(DNSServiceFlags);
len += sizeof(uint32_t); 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); }
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 void 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);
while(reptr)
{
if (reptr->key == rstate->hdr.reg_index) {
if (prev) prev->next = reptr->next;
else rstate->reg_recs = reptr->next;
err = mDNS_Deregister(&mDNSStorage, reptr->rr);
freeL("handle_removerecord_request", reptr); break;
}
prev = reptr;
reptr = reptr->next;
}
reset_connected_rstate(rstate);
if (deliver_error(rstate, err) < 0)
{
abort_request(rstate);
unlink_request(rstate);
}
}
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; 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);
}
def = mallocL("hanlde_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);
}
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;
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);
if (result < 0 || err)
{
abort_request(rstate);
unlink_request(rstate);
return;
}
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); }
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);
}
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;
}
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)); 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;
}
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]; 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);
len = sizeof(DNSServiceFlags);
len += sizeof(uint32_t); 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;
}
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;
}
static int read_msg(request_state *rs)
{
uint32_t nleft;
int nread;
char buf[4];
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)
{ 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;
}
}
if (rs->hdr_bytes == sizeof(ipc_msg_hdr))
{
if (rs->hdr.datalen == 0) {
rs->ts = t_complete;
rs->msgbuf = NULL;
return t_complete;
}
if (!rs->msgbuf) {
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) {
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->errfd, &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;
}
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->errfd;
rstate->u_err = undeliv;
return 0;
}
if (rstate->errfd != rstate->sd) close(rstate->errfd);
return 0;
error:
if (rstate->errfd != rstate->sd) close(rstate->errfd);
return -1;
}
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;
}
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; 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); return 0;
}
static void abort_request(request_state *rs)
{
reply_state *rep, *ptr;
if (rs->terminate) rs->terminate(rs->termination_context); 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;
if (rs->errfd >= 0) close(rs->errfd);
rs->errfd = -1;
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;
}
}
static void my_perror(char *errmsg)
{
LogMsg("%s: %s", errmsg, strerror(errno));
}