#if defined(_WIN32)
#include <process.h>
#define dnssd_strerror(X) win32_strerror(X)
#define usleep(X) Sleep(((X)+999)/1000)
static char * win32_strerror(int inErrorCode);
#else
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#define dnssd_strerror(X) strerror(X)
#endif
#include <stdlib.h>
#include <stdio.h>
#include "mDNSEmbeddedAPI.h"
#include "DNSCommon.h"
#include "uds_daemon.h"
#include "dns_sd.h"
#include "dnssd_ipc.h"
#ifdef __MACOSX__
#include <sys/ucred.h>
#ifndef LOCAL_PEERCRED
#define LOCAL_PEERCRED 0x001
#endif // LOCAL_PEERCRED
#endif //__MACOSX__
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
{
uint32_t key;
AuthRecord *rr;
struct registered_record_entry *next;
client_context_t client_context;
struct request_state *rstate;
} registered_record_entry;
typedef struct service_instance
{
struct service_instance *next;
mDNSBool autoname; mDNSBool autorename; mDNSBool allowremotequery; mDNSBool rename_on_memfree; domainlabel name;
domainname domain;
mDNSBool default_local; struct request_state *request;
int sd;
AuthRecord *subtypes;
ServiceRecordSet srs; } service_instance;
typedef struct
{
uint16_t txtlen;
void *txtdata;
mDNSIPPort port;
domainlabel name;
char type_as_string[MAX_ESCAPED_DOMAIN_NAME];
domainname type;
mDNSBool default_domain;
domainname host;
mDNSBool autoname; mDNSBool autorename; mDNSBool allowremotequery; int num_subtypes;
mDNSInterfaceID InterfaceID;
service_instance *instances;
struct request_state *request;
} service_info;
typedef struct browser_t
{
DNSQuestion q;
domainname domain;
struct browser_t *next;
} browser_t;
typedef struct
{
mDNSBool default_domain;
mDNSBool ForceMCast;
domainname regtype;
mDNSInterfaceID interface_id;
struct request_state *rstate;
browser_t *browsers;
} browser_info_t;
typedef struct
{
mStatus err; int nwritten;
dnssd_sock_t sd;
} undelivered_error_t;
typedef struct request_state
{
dnssd_sock_t sd;
transfer_state ts;
uint32_t hdr_bytes; ipc_msg_hdr hdr;
uint32_t data_bytes; char *msgbuf; char *msgdata; int bufsize;
int no_reply; int time_blocked; void *client_context; struct reply_state *replies; undelivered_error_t *u_err;
void *termination_context;
req_termination_fn terminate;
registered_record_entry *reg_recs; service_info *service_registration;
browser_info_t *browser_info;
struct request_state *next;
} request_state;
typedef struct
{
DNSServiceFlags flags; uint32_t ifi; DNSServiceErrorType error; } reply_hdr;
typedef struct reply_state
{
dnssd_sock_t 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
{
request_state *rstate;
DNSQuestion qtxt;
DNSQuestion qsrv;
mDNSBool srv;
mDNSBool txt;
domainname target;
mDNSIPPort port;
mDNSu16 txtlen;
mDNSu8 txtdata[AbsoluteMaxDNSMessageData];
} resolve_termination_t;
#ifdef _HAVE_SETDOMAIN_SUPPORT_
typedef struct default_browse_list_t
{
struct default_browse_list_t *next;
uid_t uid;
AuthRecord ptr_rec;
} default_browse_list_t;
static default_browse_list_t *default_browse_list = NULL;
#endif // _HAVE_SETDOMAIN_SUPPORT_
mDNSexport mDNS mDNSStorage;
#define gmDNS (&mDNSStorage)
static dnssd_sock_t listenfd = dnssd_InvalidSocket;
static request_state * all_requests = NULL;
#define MAX_TIME_BLOCKED 60 * mDNSPlatformOneSecond // try to send data to a blocked client for 60 seconds before
#define MSG_PAD_BYTES 5 // pad message buffer (read from client) with n zero'd bytes to guarantee
static void connect_callback(void *info);
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(void *info);
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 reply_state *format_enumeration_reply(request_state *rstate, const 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, int validate_flags);
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, size_t 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);
static int validate_message(request_state *rstate);
static mStatus remove_extra(request_state *rstate, service_instance *serv);
static mStatus remove_record(request_state *rstate);
static void free_service_instance(service_instance *srv);
static uint32_t dnssd_htonl(uint32_t l);
static void handle_setdomain_request(request_state *rstate);
#ifndef PID_FILE
#define PID_FILE "/var/run/mDNSResponder.pid"
#endif
static void FatalError(char *errmsg)
{
LogMsg("%s: %s", errmsg, dnssd_strerror(dnssd_errno()));
*(long*)0 = 0; abort(); }
int udsserver_init(void)
{
dnssd_sockaddr_t laddr;
int ret;
#if defined(_WIN32)
u_long opt = 1;
#endif
if (PID_FILE[0])
{
FILE *fp = fopen(PID_FILE, "w");
if (fp != NULL)
{
fprintf(fp, "%d\n", getpid());
fclose(fp);
}
}
if ((listenfd = socket(AF_DNSSD, SOCK_STREAM, 0)) == dnssd_InvalidSocket)
goto error;
bzero(&laddr, sizeof(laddr));
#if defined(USE_TCP_LOOPBACK)
{
laddr.sin_family = AF_INET;
laddr.sin_port = htons(MDNS_TCP_SERVERPORT);
laddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR);
ret = bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr));
if (ret < 0)
goto error;
}
#else
{
mode_t mask = umask(0);
unlink(MDNS_UDS_SERVERPATH); laddr.sun_family = AF_LOCAL;
#ifndef NOT_HAVE_SA_LEN
laddr.sun_len = sizeof(struct sockaddr_un);
#endif
strcpy(laddr.sun_path, MDNS_UDS_SERVERPATH);
ret = bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr));
umask(mask);
if (ret < 0)
goto error;
}
#endif
#if defined(_WIN32)
if (ioctlsocket(listenfd, FIONBIO, &opt) != 0)
#else
if (fcntl(listenfd, F_SETFL, O_NONBLOCK) != 0)
#endif
{
my_perror("ERROR: could not set listen socket to non-blocking mode");
goto error;
}
if (listen(listenfd, LISTENQ) != 0)
{
my_perror("ERROR: could not listen on listen socket");
goto error;
}
if (mStatus_NoError != udsSupportAddFDToEventLoop(listenfd, connect_callback, (void *) NULL))
{
my_perror("ERROR: could not add listen socket to event loop");
goto error;
}
#if !defined(PLATFORM_NO_RLIMIT)
{
#define MIN_OPENFILES 10240
struct rlimit maxfds, newfds;
if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; }
if (setrlimit(RLIMIT_NOFILE, &maxfds) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit");
if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; }
newfds.rlim_max = (maxfds.rlim_max > MIN_OPENFILES) ? maxfds.rlim_max : MIN_OPENFILES;
newfds.rlim_cur = (maxfds.rlim_cur > MIN_OPENFILES) ? maxfds.rlim_cur : MIN_OPENFILES;
if (newfds.rlim_max != maxfds.rlim_max || newfds.rlim_cur != maxfds.rlim_cur)
if (setrlimit(RLIMIT_NOFILE, &newfds) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit");
if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; }
debugf("maxfds.rlim_max %d", (long)maxfds.rlim_max);
debugf("maxfds.rlim_cur %d", (long)maxfds.rlim_cur);
}
#endif
return 0;
error:
my_perror("ERROR: udsserver_init");
return -1;
}
int udsserver_exit(void)
{
dnssd_close(listenfd);
#if !defined(USE_TCP_LOOPBACK)
unlink(MDNS_UDS_SERVERPATH);
#endif
return 0;
}
mDNSs32 udsserver_idle(mDNSs32 nextevent)
{
request_state *req = all_requests, *tmp, *prev = NULL;
reply_state *fptr;
transfer_state result;
mDNSs32 now = mDNS_TimeNow(&mDNSStorage);
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 |= dnssd_htonl(kDNSServiceFlagsMoreComing);
result = send_msg(req->replies);
if (result == t_complete)
{
fptr = req->replies;
req->replies = req->replies->next;
freeL("udsserver_idle", fptr);
req->time_blocked = 0; }
else if (result == t_terminated || result == t_error)
{
abort_request(req);
break;
}
else if (result == t_morecoming) break; }
}
if (result == t_morecoming)
{
if (!req->time_blocked) req->time_blocked = now;
debugf("udsserver_idle: client has been blocked for %ld seconds", (now - req->time_blocked) / mDNSPlatformOneSecond);
if (now - req->time_blocked >= MAX_TIME_BLOCKED)
{
LogMsg("Could not write data to client after %ld seconds - aborting connection", MAX_TIME_BLOCKED / mDNSPlatformOneSecond);
abort_request(req);
result = t_terminated;
}
else if (nextevent - now > mDNSPlatformOneSecond) nextevent = now + mDNSPlatformOneSecond; }
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(mDNS *const m)
{
mDNSs32 now = mDNS_TimeNow(m);
mDNSu32 CacheUsed = 0, CacheActive = 0;
mDNSu32 slot;
CacheGroup *cg;
CacheRecord *rr;
request_state *req;
LogMsgNoIdent("Timenow 0x%08lX (%ld)", (mDNSu32)now, now);
for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
for(cg = m->rrcache_hash[slot]; cg; cg=cg->next)
{
CacheUsed++; for (rr = cg->members; rr; rr=rr->next)
{
mDNSs32 remain = rr->resrec.rroriginalttl - (now - rr->TimeRcvd) / mDNSPlatformOneSecond;
CacheUsed++;
if (rr->CRActiveQuestion) CacheActive++;
LogMsgNoIdent("%s%6ld %s%-6s%-6s%s",
rr->CRActiveQuestion ? "*" : " ", remain,
(rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "-" : " ", DNSTypeName(rr->resrec.rrtype),
((NetworkInterfaceInfo *)rr->resrec.InterfaceID)->ifname, CRDisplayString(m, rr));
usleep(1000); }
}
if (m->rrcache_totalused != CacheUsed)
LogMsgNoIdent("Cache use mismatch: rrcache_totalused is %lu, true count %lu", m->rrcache_totalused, CacheUsed);
if (m->rrcache_active != CacheActive)
LogMsgNoIdent("Cache use mismatch: rrcache_active is %lu, true count %lu", m->rrcache_active, CacheActive);
LogMsgNoIdent("Cache currently contains %lu records; %lu referenced by active questions", CacheUsed, CacheActive);
for (req = all_requests; req; req=req->next)
{
void *t = req->termination_context;
if (!t) continue;
if (req->terminate == regservice_termination_callback)
{
service_instance *ptr;
for (ptr = ((service_info *)t)->instances; ptr; ptr = ptr->next)
LogMsgNoIdent("%3d: DNSServiceRegister %##s %u", req->sd, ptr->srs.RR_SRV.resrec.name->c, SRS_PORT(&ptr->srs));
}
else if (req->terminate == browse_termination_callback)
{
browser_t *blist;
for (blist = req->browser_info->browsers; blist; blist = blist->next)
LogMsgNoIdent("%3d: DNSServiceBrowse %##s", req->sd, blist->q.qname.c);
}
else if (req->terminate == resolve_termination_callback)
LogMsgNoIdent("%3d: DNSServiceResolve %##s", req->sd, ((resolve_termination_t *)t)->qsrv.qname.c);
else if (req->terminate == question_termination_callback)
LogMsgNoIdent("%3d: DNSServiceQueryRecord %##s", req->sd, ((DNSQuestion *) t)->qname.c);
else if (req->terminate == enum_termination_callback)
LogMsgNoIdent("%3d: DNSServiceEnumerateDomains %##s", req->sd, ((enum_termination_t *) t)->all->question.qname.c);
}
now = mDNS_TimeNow(m);
LogMsgNoIdent("Timenow 0x%08lX (%ld)", (mDNSu32)now, now);
}
static void rename_service(service_instance *srv)
{
if (srv->autoname && !SameDomainLabel(srv->name.c, gmDNS->nicelabel.c))
{
srv->rename_on_memfree = 1;
if (mDNS_DeregisterService(gmDNS, &srv->srs)) regservice_callback(gmDNS, &srv->srs, mStatus_MemFree);
}
}
void udsserver_handle_configchange(void)
{
request_state *req;
for (req = all_requests; req; req = req->next)
{
if (req->service_registration)
{
service_instance *ptr;
for (ptr = req->service_registration->instances; ptr; ptr = ptr->next)
rename_service(ptr);
}
}
}
static void connect_callback(void *info)
{
dnssd_sock_t sd;
unsigned int len;
unsigned long optval;
dnssd_sockaddr_t cliaddr;
request_state *rstate;
(void)info;
len = (int) sizeof(cliaddr);
sd = accept(listenfd, (struct sockaddr*) &cliaddr, &len);
if (sd == dnssd_InvalidSocket)
{
if (dnssd_errno() == dnssd_EWOULDBLOCK) return;
my_perror("ERROR: accept");
return;
}
optval = 1;
#ifdef SO_NOSIGPIPE
if (setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0)
{
my_perror("ERROR: setsockopt - SO_NOSIGPIPE - aborting client");
dnssd_close(sd);
return;
}
#endif
#if defined(_WIN32)
if (ioctlsocket(sd, FIONBIO, &optval) != 0)
#else
if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
#endif
{
my_perror("ERROR: fcntl(sd, F_SETFL, O_NONBLOCK) - aborting client");
dnssd_close(sd);
return;
}
rstate = mallocL("connect_callback", sizeof(request_state));
if (!rstate) FatalError("ERROR: malloc");
bzero(rstate, sizeof(request_state));
rstate->ts = t_morecoming;
rstate->sd = sd;
LogOperation("%3d: Adding FD", rstate->sd);
if ( mStatus_NoError != udsSupportAddFDToEventLoop( sd, request_callback, rstate))
return;
rstate->next = all_requests;
all_requests = rstate;
}
static void request_callback(void *info)
{
request_state *rstate = info;
transfer_state result;
dnssd_sockaddr_t cliaddr;
int dedicated_error_socket;
#if defined(_WIN32)
u_long opt = 1;
#endif
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 (validate_message(rstate) < 0)
{
abort_request(rstate);
unlink_request(rstate);
LogMsg("Invalid message sent by client - may indicate a malicious program running on this machine!");
return;
}
if (rstate->hdr.flags & IPC_FLAGS_NOREPLY) rstate->no_reply = 1;
dedicated_error_socket = (rstate->hdr.op == reg_record_request || rstate->hdr.op == add_record_request ||
rstate->hdr.op == update_record_request || rstate->hdr.op == remove_record_request);
if (((rstate->hdr.flags & IPC_FLAGS_REUSE_SOCKET) == 0) != dedicated_error_socket)
LogMsg("WARNING: client request %d with incorrect flags setting 0x%X", rstate->hdr.op, rstate->hdr.flags);
if (dedicated_error_socket)
{
mStatus err = 0;
int nwritten;
int errfd = socket(AF_DNSSD, SOCK_STREAM, 0);
if (errfd == dnssd_InvalidSocket)
{
my_perror("ERROR: socket");
abort_request(rstate);
unlink_request(rstate);
return;
}
#if defined(USE_TCP_LOOPBACK)
{
mDNSOpaque16 port;
port.b[0] = rstate->msgdata[0];
port.b[1] = rstate->msgdata[1];
rstate->msgdata += 2;
cliaddr.sin_family = AF_INET;
cliaddr.sin_port = port.NotAnInteger;
cliaddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR);
}
#else
{
char ctrl_path[MAX_CTLPATH];
get_string(&rstate->msgdata, ctrl_path, 256); bzero(&cliaddr, sizeof(cliaddr));
cliaddr.sun_family = AF_LOCAL;
strcpy(cliaddr.sun_path, ctrl_path);
}
#endif
if (connect(errfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)) < 0)
{
my_perror("ERROR: connect");
abort_request(rstate);
unlink_request(rstate);
return;
}
#if defined(_WIN32)
if (ioctlsocket(errfd, FIONBIO, &opt) != 0)
#else
if (fcntl(errfd, F_SETFL, O_NONBLOCK) != 0)
#endif
{
my_perror("ERROR: could not set control socket to non-blocking mode");
abort_request(rstate);
unlink_request(rstate);
return;
}
switch(rstate->hdr.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);
}
err = dnssd_htonl(err);
nwritten = send(errfd, &err, sizeof(err), 0);
if (nwritten < (int)sizeof(err))
LogMsg("ERROR: failed to write error response back to caller: %d %d %s",
nwritten, dnssd_errno(), dnssd_strerror(dnssd_errno()));
dnssd_close(errfd);
reset_connected_rstate(rstate); }
else
{
switch(rstate->hdr.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;
case setdomain_request: handle_setdomain_request (rstate); break;
default: LogMsg("%3d: ERROR: udsserver_recv_request - unsupported request type: %d", rstate->sd, rstate->hdr.op);
}
}
}
static void handle_query_request(request_state *rstate)
{
DNSServiceFlags flags;
uint32_t ifi;
char name[256];
uint16_t rrtype, rrclass;
char *ptr;
mStatus result;
mDNSInterfaceID InterfaceID;
DNSQuestion *q;
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);
ifi = get_long(&ptr);
if (get_string(&ptr, name, 256) < 0) goto bad_param;
rrtype = get_short(&ptr);
rrclass = get_short(&ptr);
InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, ifi);
if (ifi && !InterfaceID) goto bad_param;
q = mallocL("DNSQuestion", sizeof(DNSQuestion));
if (!q) FatalError("ERROR: handle_query - malloc");
bzero(q, sizeof(DNSQuestion));
q->InterfaceID = InterfaceID;
q->Target = zeroAddr;
if (!MakeDomainNameFromDNSNameString(&q->qname, name)) { freeL("DNSQuestion", q); goto bad_param; }
q->qtype = rrtype;
q->qclass = rrclass;
q->LongLived = (flags & kDNSServiceFlagsLongLivedQuery) != 0;
q->ExpectUnique = mDNSfalse;
q->ForceMCast = (flags & kDNSServiceFlagsForceMulticast) != 0;
q->QuestionCallback = question_result_callback;
q->QuestionContext = rstate;
rstate->termination_context = q;
rstate->terminate = question_termination_callback;
LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) START", rstate->sd, q->qname.c, DNSTypeName(q->qtype));
result = mDNS_StartQuery(gmDNS, q);
if (result != mStatus_NoError) LogMsg("ERROR: mDNS_StartQuery: %d", (int)result);
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;
mDNSInterfaceID InterfaceID;
char name[256], regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME];
char *ptr; domainname fqdn;
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);
InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, interfaceIndex);
if (interfaceIndex && !InterfaceID)
{ LogMsg("ERROR: handle_resolve_request - Couldn't find InterfaceID for interfaceIndex %d", interfaceIndex); 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)
{ LogMsg("ERROR: handle_resolve_request - Couldn't read name/regtype/domain"); goto bad_param; }
freeL("handle_resolve_request", rstate->msgbuf);
rstate->msgbuf = NULL;
if (build_domainname_from_strings(&fqdn, name, regtype, domain) < 0)
{ LogMsg("ERROR: handle_resolve_request - Couldn't build_domainname_from_strings “%s” “%s” “%s”", name, regtype, domain); goto bad_param; }
term = mallocL("handle_resolve_request", sizeof(resolve_termination_t));
bzero(term, sizeof(*term));
if (!term) FatalError("ERROR: malloc");
term->qsrv.InterfaceID = InterfaceID;
term->qsrv.Target = zeroAddr;
memcpy(&term->qsrv.qname, &fqdn, MAX_DOMAIN_NAME);
term->qsrv.qtype = kDNSType_SRV;
term->qsrv.qclass = kDNSClass_IN;
term->qsrv.LongLived = mDNSfalse;
term->qsrv.ExpectUnique = mDNStrue;
term->qsrv.ForceMCast = mDNSfalse;
term->qsrv.QuestionCallback = resolve_result_callback;
term->qsrv.QuestionContext = rstate;
term->qtxt.InterfaceID = InterfaceID;
term->qtxt.Target = zeroAddr;
memcpy(&term->qtxt.qname, &fqdn, MAX_DOMAIN_NAME);
term->qtxt.qtype = kDNSType_TXT;
term->qtxt.qclass = kDNSClass_IN;
term->qtxt.LongLived = mDNSfalse;
term->qtxt.ExpectUnique = mDNStrue;
term->qtxt.ForceMCast = mDNSfalse;
term->qtxt.QuestionCallback = resolve_result_callback;
term->qtxt.QuestionContext = rstate;
term->rstate = rstate;
rstate->termination_context = term;
rstate->terminate = resolve_termination_callback;
LogOperation("%3d: DNSServiceResolve(%##s) START", rstate->sd, term->qsrv.qname.c);
err = mDNS_StartQuery(gmDNS, &term->qsrv);
if (!err) err = mDNS_StartQuery(gmDNS, &term->qtxt);
if (err)
{
freeL("handle_resolve_request", term);
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);
}
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;
LogOperation("%3d: DNSServiceResolve(%##s) STOP", rs->sd, term->qtxt.qname.c);
mDNS_StopQuery(gmDNS, &term->qtxt);
mDNS_StopQuery(gmDNS, &term->qsrv);
freeL("resolve_termination_callback", term);
rs->termination_context = NULL;
}
static void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
{
size_t 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_termination_t *res = rs->termination_context;
(void)m;
LogOperation("%3d: DNSServiceResolve(%##s, %s) RESULT %s", rs->sd, question->qname.c, DNSTypeName(question->qtype), RRDisplayString(m, answer));
if (!AddRecord)
{
return;
}
if (answer->rrtype == kDNSType_SRV)
{
AssignDomainName(&res->target, &answer->rdata->u.srv.target);
res->port = answer->rdata->u.srv.port;
res->srv = mDNStrue;
}
if (answer->rrtype == kDNSType_TXT)
{
if (answer->rdlength > AbsoluteMaxDNSMessageData) return;
res->txtlen = answer->rdlength;
mDNSPlatformMemCopy(answer->rdata->u.data, res->txtdata, res->txtlen);
res->txt = mDNStrue;
}
if (!res->txt || !res->srv) return;
ConvertDomainNameToCString(answer->name, fullname);
ConvertDomainNameToCString(&res->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->txtlen;
rep = create_reply(resolve_reply, len, rs);
rep->rhdr->flags = dnssd_htonl(0);
rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, answer->InterfaceID));
rep->rhdr->error = dnssd_htonl(kDNSServiceErr_NoError);
data = rep->sdata;
put_string(fullname, &data);
put_string(target, &data);
*data++ = res->port.b[0];
*data++ = res->port.b[1];
put_short(res->txtlen, &data);
put_rdata(res->txtlen, res->txtdata, &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 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 = question->QuestionContext;
reply_state *rep;
size_t len;
(void)m;
LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) RESULT %s", req->sd, question->qname.c, DNSTypeName(question->qtype), RRDisplayString(m, answer));
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 = dnssd_htonl(AddRecord ? kDNSServiceFlagsAdd : 0);
rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, answer->InterfaceID));
rep->rhdr->error = dnssd_htonl(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, answer->rdata->u.data, &data);
put_long(AddRecord ? answer->rroriginalttl : 0, &data);
append_reply(req, rep);
return;
}
static void question_termination_callback(void *context)
{
DNSQuestion *q = context;
LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) STOP", ((request_state *)q->QuestionContext)->sd, q->qname.c, DNSTypeName(q->qtype));
mDNS_StopQuery(gmDNS, q); freeL("question_termination_callback", q);
}
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);
}
static char *FindNextSubType(char *p)
{
while (*p)
{
if (p[0] == '\\' && p[1]) p += 2; else if (p[0] == ',') {
if (p[1]) *p++ = 0;
return(p);
}
else if (p[0] == '.')
return(mDNSNULL);
else p++;
}
return(p);
}
mDNSexport mDNSs32 ChopSubTypes(char *regtype)
{
mDNSs32 NumSubTypes = 0;
char *stp = FindFirstSubType(regtype);
while (stp && *stp) {
if (*stp == ',') return(-1);
NumSubTypes++;
stp = FindNextSubType(stp);
}
if (!stp) return(-1);
return(NumSubTypes);
}
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++)
{
mDNS_SetupResourceRecord(&st[i], mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, mDNSNULL, mDNSNULL);
while (*p) p++;
p++;
if (!MakeDomainNameFromDNSNameString(st[i].resrec.name, p))
{ freeL("ServiceSubTypes", st); return(mDNSNULL); }
}
}
return(st);
}
#ifdef _HAVE_SETDOMAIN_SUPPORT_
static void free_defdomain(mDNS *const m, AuthRecord *const rr, mStatus result)
{
(void)m; if (result == mStatus_MemFree) free(rr->RecordContext); }
#endif
static void handle_setdomain_request(request_state *request)
{
mStatus err = mStatus_NoError;
char *ptr;
char domainstr[MAX_ESCAPED_DOMAIN_NAME];
domainname domain;
DNSServiceFlags flags;
#ifdef _HAVE_SETDOMAIN_SUPPORT_
struct xucred xuc;
socklen_t xuclen;
#endif
if (request->ts != t_complete)
{
LogMsg("ERROR: handle_setdomain_request - transfer state != t_complete");
abort_request(request);
unlink_request(request);
return;
}
ptr = request->msgdata;
flags = get_flags(&ptr);
if (get_string(&ptr, domainstr, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
!MakeDomainNameFromDNSNameString(&domain, domainstr))
{ err = mStatus_BadParamErr; goto end; }
freeL("handle_setdomain_request", request->msgbuf);
request->msgbuf = NULL;
debugf("%3d: DNSServiceSetDefaultDomainForUser(%##s)", request->sd, domain.c);
#ifdef _HAVE_SETDOMAIN_SUPPORT_
xuclen = sizeof(xuc);
if (getsockopt(request->sd, 0, LOCAL_PEERCRED, &xuc, &xuclen))
{ my_perror("ERROR: getsockopt, LOCAL_PEERCRED"); err = mStatus_UnknownErr; goto end; }
if (xuc.cr_version != XUCRED_VERSION) { LogMsg("getsockopt, LOCAL_PEERCRED - bad version"); err = mStatus_UnknownErr; goto end; }
LogMsg("Default domain %s %s for UID %d", domainstr, flags & kDNSServiceFlagsAdd ? "set" : "removed", xuc.cr_uid);
if (flags & kDNSServiceFlagsAdd)
{
default_browse_list_t *newelem = malloc(sizeof(default_browse_list_t));
if (!newelem) { LogMsg("ERROR: malloc"); err = mStatus_NoMemoryErr; goto end; }
mDNS_SetupResourceRecord(&newelem->ptr_rec, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, free_defdomain, newelem);
MakeDomainNameFromDNSNameString(&newelem->ptr_rec.resrec.name, mDNS_DomainTypeNames[mDNS_DomainTypeBrowseDefault]);
AppendDNSNameString (&newelem->ptr_rec.resrec.name, "local");
AssignDomainName(&newelem->ptr_rec.resrec.rdata->u.name, &domain);
newelem->uid = xuc.cr_uid;
err = mDNS_Register(gmDNS, &newelem->ptr_rec);
if (err) free(newelem);
else
{
newelem->next = default_browse_list;
default_browse_list = newelem;
}
}
else
{
default_browse_list_t *ptr = default_browse_list, *prev = NULL;
while (ptr)
{
if (SameDomainName(&ptr->ptr_rec.resrec.rdata->u.name, &domain))
{
if (prev) prev->next = ptr->next;
else default_browse_list = ptr->next;
err = mDNS_Deregister(gmDNS, &ptr->ptr_rec);
break;
}
prev = ptr;
ptr = ptr->next;
}
if (!ptr) { LogMsg("Attempt to remove nonexistent domain %s for UID %d", domainstr, xuc.cr_uid); err = mStatus_Invalid; }
}
#else
err = mStatus_NoError;
#endif // _HAVE_SETDOMAIN_SUPPORT_
end:
deliver_error(request, err);
abort_request(request);
unlink_request(request);
}
static mStatus add_domain_to_browser(browser_info_t *info, const domainname *d)
{
browser_t *b, *p;
mStatus err;
for (p = info->browsers; p; p = p->next)
{
if (SameDomainName(&p->domain, d))
{ debugf("add_domain_to_browser - attempt to add domain %##d already in list", d->c); return mStatus_AlreadyRegistered; }
}
b = mallocL("browser_t", sizeof(*b));
if (!b) return mStatus_NoMemoryErr;
AssignDomainName(&b->domain, d);
err = mDNS_StartBrowse(gmDNS, &b->q, &info->regtype, d, info->interface_id, info->ForceMCast, browse_result_callback, info->rstate);
if (err)
{
LogMsg("mDNS_StartBrowse returned %d for type %##s domain %##s", err, info->regtype.c, d->c);
freeL("browser_t", b);
}
else
{
b->next = info->browsers;
info->browsers = b;
}
return err;
}
static void handle_browse_request(request_state *request)
{
DNSServiceFlags flags;
uint32_t interfaceIndex;
mDNSInterfaceID InterfaceID;
char regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME];
domainname typedn, d, temp;
mDNSs32 NumSubTypes;
char *ptr;
mStatus err;
DNameListElem *search_domain_list, *sdom;
browser_info_t *info = NULL;
if (request->ts != t_complete)
{
LogMsg("ERROR: handle_browse_request - transfer state != t_complete");
abort_request(request);
unlink_request(request);
return;
}
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)
{ err = mStatus_BadParamErr; goto error; }
freeL("handle_browse_request", request->msgbuf);
request->msgbuf = NULL;
InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, interfaceIndex);
if (interfaceIndex && !InterfaceID) { err = mStatus_BadParamErr; goto error; }
typedn.c[0] = 0;
NumSubTypes = ChopSubTypes(regtype); if (NumSubTypes < 0 || NumSubTypes > 1) { err = mStatus_BadParamErr; goto error; }
if (NumSubTypes == 1 && !AppendDNSNameString(&typedn, regtype + strlen(regtype) + 1))
{ err = mStatus_BadParamErr; goto error; }
if (!regtype[0] || !AppendDNSNameString(&typedn, regtype)) { err = mStatus_BadParamErr; goto error; }
if (!MakeDomainNameFromDNSNameString(&temp, regtype)) { err = mStatus_BadParamErr; goto error; }
if (temp.c[0] > 15 && domain[0] == 0) strcpy(domain, "local.");
info = mallocL("browser_info_t", sizeof(*info));
if (!info) { err = mStatus_NoMemoryErr; goto error; }
request->browser_info = info;
info->ForceMCast = (flags & kDNSServiceFlagsForceMulticast) != 0;
info->interface_id = InterfaceID;
AssignDomainName(&info->regtype, &typedn);
info->rstate = request;
info->default_domain = !domain[0];
info->browsers = NULL;
request->termination_context = info;
request->terminate = browse_termination_callback;
LogOperation("%3d: DNSServiceBrowse(%##s%s) START", request->sd, info->regtype.c, domain);
if (domain[0])
{
if (!MakeDomainNameFromDNSNameString(&d, domain)) { err = mStatus_BadParamErr; goto error; }
err = add_domain_to_browser(info, &d);
}
else
{
search_domain_list = mDNSPlatformGetSearchDomainList();
for (sdom = search_domain_list; sdom; sdom = sdom->next)
{
err = add_domain_to_browser(info, &sdom->name);
if (err)
{
if (SameDomainName(&sdom->name, &localdomain)) break;
else err = mStatus_NoError; }
}
mDNS_FreeDNameList(search_domain_list);
}
deliver_error(request, mStatus_NoError);
return;
error:
if (info) freeL("browser_info_t", info);
if (request->termination_context) request->termination_context = NULL;
deliver_error(request, err);
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 = question->QuestionContext;
reply_state *rep;
mStatus err;
(void)m; LogOperation("%3d: DNSServiceBrowse(%##s, %s) RESULT %s %s",
req->sd, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "Add" : "Rmv", RRDisplayString(m, answer));
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 |= dnssd_htonl(kDNSServiceFlagsAdd); append_reply(req, rep);
return;
}
static void browse_termination_callback(void *context)
{
browser_info_t *info = context;
browser_t *ptr;
if (!info) return;
while(info->browsers)
{
ptr = info->browsers;
info->browsers = ptr->next;
LogOperation("%3d: DNSServiceBrowse(%##s) STOP", info->rstate->sd, ptr->q.qname.c);
mDNS_StopBrowse(gmDNS, &ptr->q); freeL("browse_termination_callback", ptr);
}
info->rstate->termination_context = NULL;
freeL("browser_info", info);
}
mDNSexport void udsserver_default_browse_domain_changed(const domainname *d, mDNSBool add)
{
request_state *r;
for (r = all_requests; r; r = r->next)
{
browser_info_t *info = r->browser_info;
if (!info || !info->default_domain) continue;
if (add) add_domain_to_browser(info, d);
else
{
browser_t **ptr = &info->browsers;
while (*ptr)
{
if (SameDomainName(&(*ptr)->domain, d))
{
browser_t *remove = *ptr;
*ptr = (*ptr)->next;
if (remove->q.LongLived)
{
CacheRecord *ka = remove->q.uDNS_info.knownAnswers;
while (ka) { remove->q.QuestionCallback(gmDNS, &remove->q, &ka->resrec, mDNSfalse); ka = ka->next; }
}
mDNS_StopBrowse(gmDNS, &remove->q);
freeL("browser_t", remove);
return;
}
ptr = &(*ptr)->next;
}
LogMsg("Requested removal of default domain %##s not in list for sd %d", d->c, r->sd);
}
}
}
mDNSexport int CountPeerRegistrations(mDNS *const m, ServiceRecordSet *const srs)
{
int count = 0;
ResourceRecord *r = &srs->RR_SRV.resrec;
AuthRecord *rr;
ServiceRecordSet *s;
for (rr = m->ResourceRecords; rr; rr=rr->next)
if (rr->resrec.rrtype == kDNSType_SRV && SameDomainName(rr->resrec.name, r->name) && !SameRData(&rr->resrec, r))
count++;
for (rr = m->uDNS_info.RecordRegistrations; rr; rr=rr->next)
if (rr->uDNS_info.state != regState_Unregistered && rr->resrec.rrtype == kDNSType_SRV && SameDomainName(rr->resrec.name, r->name) && !SameRData(&rr->resrec, r))
count++;
for (s = m->uDNS_info.ServiceRegistrations; s; s = s->next)
if (s->uDNS_info.state != regState_Unregistered && SameDomainName(s->RR_SRV.resrec.name, r->name) && !SameRData(&s->RR_SRV.resrec, r))
count++;
verbosedebugf("%d peer registrations for %##s", count, r->name->c);
return(count);
}
mDNSexport int CountExistingRegistrations(domainname *srv, mDNSIPPort port)
{
int count = 0;
AuthRecord *rr;
for (rr = gmDNS->ResourceRecords; rr; rr=rr->next)
if (rr->resrec.rrtype == kDNSType_SRV &&
rr->resrec.rdata->u.srv.port.NotAnInteger == port.NotAnInteger &&
SameDomainName(rr->resrec.name, srv))
count++;
return(count);
}
static mStatus register_service_instance(request_state *request, const domainname *domain)
{
service_info *info = request->service_registration;
service_instance *ptr, *instance;
int instance_size;
mStatus result;
for (ptr = info->instances; ptr; ptr = ptr->next)
{
if (SameDomainName(&ptr->domain, domain))
{ LogMsg("register_service_instance: domain %##s already registered", domain->c); return mStatus_AlreadyRegistered; }
}
instance_size = sizeof(*instance);
if (info->txtlen > sizeof(RDataBody)) instance_size += (info->txtlen - sizeof(RDataBody));
instance = mallocL("service_instance", instance_size);
if (!instance) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; }
instance->subtypes = AllocateSubTypes(info->num_subtypes, info->type_as_string);
if (info->num_subtypes && !instance->subtypes)
{ free_service_instance(instance); instance = NULL; FatalError("ERROR: malloc"); }
instance->request = request;
instance->sd = request->sd;
instance->autoname = info->autoname;
instance->autorename = info->autorename;
instance->allowremotequery = info->allowremotequery;
instance->rename_on_memfree = 0;
instance->name = info->name;
AssignDomainName(&instance->domain, domain);
instance->default_local = (info->default_domain && SameDomainName(domain, &localdomain));
result = mDNS_RegisterService(gmDNS, &instance->srs, &instance->name, &info->type, domain, info->host.c[0] ? &info->host : NULL, info->port,
info->txtdata, info->txtlen, instance->subtypes, info->num_subtypes, info->InterfaceID, regservice_callback, instance);
if (result) free_service_instance(instance);
else
{
instance->next = info->instances;
info->instances = instance;
}
return result;
}
mDNSexport void udsserver_default_reg_domain_changed(const domainname *d, mDNSBool add)
{
request_state *rstate;
service_info *info;
for (rstate = all_requests; rstate; rstate = rstate->next)
{
if (rstate->terminate != regservice_termination_callback) continue;
info = rstate->service_registration;
if (!info) { LogMsg("udsserver_default_reg_domain_changed - NULL service info"); continue; } if (!info->default_domain) continue;
if (add) register_service_instance(rstate, d);
else
{
service_instance *si = rstate->service_registration->instances, *prev = NULL;
while (si)
{
if (SameDomainName(&si->domain, d))
{
mStatus err;
if (prev) prev->next = si->next;
else info->instances = si->next;
err = mDNS_DeregisterService(gmDNS, &si->srs);
if (err)
{
LogMsg("udsserver_default_reg_domain_changed - mDNS_DeregisterService err %d", err);
free_service_instance(si);
}
break;
}
prev = si;
si = si->next;
}
if (!si) LogMsg("udsserver_default_reg_domain_changed - domain %##s not registered", d->c);
}
}
}
static void handle_regservice_request(request_state *request)
{
DNSServiceFlags flags;
uint32_t ifi;
char name[256], domain[MAX_ESCAPED_DOMAIN_NAME], host[MAX_ESCAPED_DOMAIN_NAME];
char *ptr;
domainname d, srv;
mStatus result;
service_info *service = NULL;
if (request->ts != t_complete)
{
LogMsg("ERROR: handle_regservice_request - transfer state != t_complete");
abort_request(request);
unlink_request(request);
return;
}
service = mallocL("service_info", sizeof(*service));
if (!service) { my_perror("ERROR: malloc"); result = mStatus_NoMemoryErr; goto finish; }
service->instances = NULL;
service->request = request;
request->service_registration = service;
request->termination_context = request->service_registration;
request->terminate = regservice_termination_callback;
ptr = request->msgdata;
flags = get_flags(&ptr);
ifi = get_long(&ptr);
service->InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, ifi);
if (ifi && !service->InterfaceID)
{ LogMsg("ERROR: handle_regservice_request - Couldn't find InterfaceID for interfaceIndex %d", ifi); goto bad_param; }
if (get_string(&ptr, name, 256) < 0 ||
get_string(&ptr, service->type_as_string, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
get_string(&ptr, domain, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
get_string(&ptr, host, MAX_ESCAPED_DOMAIN_NAME) < 0)
{ LogMsg("ERROR: handle_regservice_request - Couldn't read name/regtype/domain"); goto bad_param; }
service->port.b[0] = *ptr++;
service->port.b[1] = *ptr++;
service->txtlen = get_short(&ptr);
service->txtdata = get_rdata(&ptr, service->txtlen);
service->num_subtypes = ChopSubTypes(service->type_as_string); if (service->num_subtypes < 0)
{ LogMsg("ERROR: handle_regservice_request - ChopSubTypes failed %s", service->type_as_string); goto bad_param; }
if (!*service->type_as_string || !MakeDomainNameFromDNSNameString(&service->type, service->type_as_string))
{ LogMsg("ERROR: handle_regservice_request - service->type_as_string bad %s", service->type_as_string); goto bad_param; }
if (!name[0])
{
service->name = (gmDNS)->nicelabel;
service->autoname = mDNStrue;
}
else
{
if (!MakeDomainLabelFromLiteralString(&service->name, name))
{ LogMsg("ERROR: handle_regservice_request - name bad %s", name); goto bad_param; }
service->autoname = mDNSfalse;
}
if (*domain)
{
service->default_domain = mDNSfalse;
if (!MakeDomainNameFromDNSNameString(&d, domain))
{ LogMsg("ERROR: handle_regservice_request - domain bad %s", domain); goto bad_param; }
}
else
{
service->default_domain = mDNStrue;
MakeDomainNameFromDNSNameString(&d, "local.");
}
if (!ConstructServiceName(&srv, &service->name, &service->type, &d))
{ LogMsg("ERROR: handle_regservice_request - Couldn't ConstructServiceName from, “%#s” “%##s” “%##s”", service->name.c, service->type.c, d.c); goto bad_param; }
if (!MakeDomainNameFromDNSNameString(&service->host, host))
{ LogMsg("ERROR: handle_regservice_request - host bad %s", host); goto bad_param; }
service->autorename = (flags & kDNSServiceFlagsNoAutoRename ) == 0;
service->allowremotequery = (flags & kDNSServiceFlagsAllowRemoteQuery) != 0;
if (service->port.NotAnInteger)
{
int count = CountExistingRegistrations(&srv, service->port);
if (count)
LogMsg("Client application registered %d identical instances of service %##s port %u.",
count+1, srv.c, mDNSVal16(service->port));
}
LogOperation("%3d: DNSServiceRegister(%##s, %u) START", request->sd, srv.c, mDNSVal16(service->port));
result = register_service_instance(request, &d);
if (!result && !*domain)
{
DNameListElem *ptr, *def_domains = mDNSPlatformGetRegDomainList();
for (ptr = def_domains; ptr; ptr = ptr->next)
register_service_instance(request, &ptr->name);
mDNS_FreeDNameList(def_domains);
}
finish:
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);
}
static void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result)
{
mStatus err;
service_instance *instance = srs->ServiceContext;
(void)m; if (!srs) { LogMsg("regservice_callback: srs is NULL %d", result); return; }
if (!instance) { LogMsg("regservice_callback: srs->ServiceContext is NULL %d", result); return; }
if (result == mStatus_NoError)
LogOperation("%3d: DNSServiceRegister(%##s, %u) REGISTERED ", instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port));
else if (result == mStatus_MemFree)
LogOperation("%3d: DNSServiceRegister(%##s, %u) DEREGISTERED", instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port));
else if (result == mStatus_NameConflict)
LogOperation("%3d: DNSServiceRegister(%##s, %u) NAME CONFLICT", instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port));
else
LogOperation("%3d: DNSServiceRegister(%##s, %u) CALLBACK %d", instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port), result);
if (result == mStatus_NoError)
{
if (instance->allowremotequery)
{
ExtraResourceRecord *e;
srs->RR_ADV.AllowRemoteQuery = mDNStrue;
srs->RR_PTR.AllowRemoteQuery = mDNStrue;
srs->RR_SRV.AllowRemoteQuery = mDNStrue;
srs->RR_TXT.AllowRemoteQuery = mDNStrue;
for (e = instance->srs.Extras; e; e = e->next) e->r.AllowRemoteQuery = mDNStrue;
}
process_service_registration(srs);
if (instance->autoname && CountPeerRegistrations(m, srs) == 0)
RecordUpdatedNiceLabel(m, 0); return;
}
else if (result == mStatus_MemFree)
{
if (instance->rename_on_memfree)
{
instance->rename_on_memfree = 0;
instance->name = gmDNS->nicelabel;
err = mDNS_RenameAndReregisterService(gmDNS, srs, &instance->name);
if (err) LogMsg("ERROR: regservice_callback - RenameAndReregisterService returned %ld", err);
}
else
{
free_service_instance(instance);
return;
}
}
else if (result == mStatus_NameConflict)
{
if (instance->autoname && CountPeerRegistrations(m, srs) == 0)
{
IncrementLabelSuffix(&m->nicelabel, mDNStrue);
m->MainCallback(m, mStatus_ConfigChanged);
}
else if (instance->autoname || instance->autorename)
{
mDNS_RenameAndReregisterService(gmDNS, srs, mDNSNULL);
return;
}
else
{
request_state *rs = instance->request;
if (!rs) { LogMsg("ERROR: regservice_callback: received result %ld with a NULL request pointer", result); return; }
free_service_instance(instance);
if (deliver_async_error(rs, reg_service_reply, result) < 0)
{
abort_request(rs);
unlink_request(rs);
}
return;
}
}
else
{
request_state *rs = instance->request;
if (!rs) { LogMsg("ERROR: regservice_callback: received result %ld with a NULL request pointer", result); return; }
if (result != mStatus_NATTraversal) LogMsg("ERROR: unknown result in regservice_callback: %ld", result);
free_service_instance(instance);
if (deliver_async_error(rs, reg_service_reply, result) < 0)
{
abort_request(rs);
unlink_request(rs);
}
return;
}
}
mDNSexport void FreeExtraRR(mDNS *const m, AuthRecord *const rr, mStatus result)
{
ExtraResourceRecord *extra = (ExtraResourceRecord *)rr->RecordContext;
(void)m;
if (result != mStatus_MemFree) { LogMsg("Error: FreeExtraRR invoked with unexpected error %d", result); return; }
debugf("%##s: MemFree", rr->resrec.name->c);
if (rr->resrec.rdata != &rr->rdatastorage)
freeL("Extra RData", rr->resrec.rdata);
freeL("ExtraResourceRecord", extra);
}
static mStatus add_record_to_service(request_state *rstate, service_instance *instance, uint16_t rrtype, uint16_t rdlen, char *rdata, uint32_t ttl)
{
ServiceRecordSet *srs = &instance->srs;
ExtraResourceRecord *extra;
mStatus result;
int size;
if (rdlen > sizeof(RDataBody)) size = rdlen;
else size = sizeof(RDataBody);
extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size);
if (!extra)
{
my_perror("ERROR: malloc");
return mStatus_NoMemoryErr;
}
bzero(extra, sizeof(ExtraResourceRecord)); extra->r.resrec.rrtype = rrtype;
extra->r.rdatastorage.MaxRDLength = (mDNSu16) size;
extra->r.resrec.rdlength = rdlen;
memcpy(&extra->r.rdatastorage.u.data, rdata, rdlen);
result = mDNS_AddRecordToService(gmDNS, srs , extra, &extra->r.rdatastorage, ttl);
if (result) { freeL("ExtraResourceRecord", extra); return result; }
extra->ClientID = rstate->hdr.reg_index;
return result;
}
static mStatus handle_add_request(request_state *rstate)
{
uint32_t ttl;
uint16_t rrtype, rdlen;
char *ptr, *rdata;
mStatus result = mStatus_UnknownErr;
DNSServiceFlags flags;
service_info *srvinfo = rstate->service_registration;
service_instance *i;
if (!srvinfo) { LogMsg("handle_add_request called with NULL service_registration"); 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 (!ttl) ttl = DefaultTTLforRRType(rrtype);
LogOperation("%3d: DNSServiceAddRecord(%##s, %s)", rstate->sd,
(srvinfo->instances) ? srvinfo->instances->srs.RR_SRV.resrec.name->c : NULL, DNSTypeName(rrtype));
for (i = srvinfo->instances; i; i = i->next)
{
result = add_record_to_service(rstate, i, rrtype, rdlen, rdata, ttl);
if (result && i->default_local) break;
else result = mStatus_NoError; }
return(result);
}
static mStatus update_record(AuthRecord *rr, uint16_t rdlen, char *rdata, uint32_t ttl)
{
int rdsize;
RData *newrd;
mStatus result;
if (rdlen > sizeof(RDataBody)) rdsize = rdlen;
else rdsize = sizeof(RDataBody);
newrd = mallocL("handle_update_request", sizeof(RData) - sizeof(RDataBody) + rdsize);
if (!newrd) FatalError("ERROR: malloc");
newrd->MaxRDLength = (mDNSu16) rdsize;
memcpy(&newrd->u, rdata, rdlen);
result = mDNS_Update(gmDNS, rr, ttl, rdlen, newrd, update_callback);
if (result) { LogMsg("ERROR: mDNS_Update - %ld", result); freeL("handle_update_request", newrd); }
return result;
}
static mStatus handle_update_request(request_state *rstate)
{
uint16_t rdlen;
char *ptr, *rdata;
uint32_t ttl;
mStatus result = mStatus_BadReferenceErr;
service_info *srvinfo = rstate->service_registration;
service_instance *i;
AuthRecord *rr = NULL;
ptr = rstate->msgdata;
get_flags(&ptr); rdlen = get_short(&ptr);
rdata = get_rdata(&ptr, rdlen);
ttl = get_long(&ptr);
if (rstate->reg_recs)
{
registered_record_entry *reptr;
for (reptr = rstate->reg_recs; reptr; reptr = reptr->next)
{
if (reptr->key == rstate->hdr.reg_index)
{
result = update_record(reptr->rr, rdlen, rdata, ttl);
goto end;
}
}
result = mStatus_BadReferenceErr;
goto end;
}
if (!srvinfo) { result = mStatus_BadReferenceErr; goto end; }
for (i = srvinfo->instances; i; i = i->next)
{
if (rstate->hdr.reg_index == TXT_RECORD_INDEX) rr = &i->srs.RR_TXT;
else
{
ExtraResourceRecord *e;
for (e = i->srs.Extras; e; e = e->next)
if (e->ClientID == rstate->hdr.reg_index) { rr = &e->r; break; }
}
if (!rr) { result = mStatus_BadReferenceErr; goto end; }
result = update_record(rr, rdlen, rdata, ttl);
if (result && i->default_local) goto end;
else result = mStatus_NoError; }
end:
LogOperation("%3d: DNSServiceUpdateRecord(%##s, %s)", rstate->sd,
(srvinfo->instances) ? srvinfo->instances->srs.RR_SRV.resrec.name->c : NULL,
rr ? DNSTypeName(rr->resrec.rrtype) : "<NONE>");
return(result);
}
static void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd)
{
(void)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;
service_instance *instance = srs->ServiceContext;
request_state *req = instance->request;
if (!req) { LogMsg("ERROR: process_service_registration - null request object"); return; }
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 free_service_instance(service_instance *srv)
{
request_state *rstate = srv->request;
ExtraResourceRecord *e = srv->srs.Extras, *tmp;
if (rstate)
{
service_instance *ptr = rstate->service_registration->instances, *prev = NULL;
while (ptr)
{
if (ptr == srv)
{
if (prev) prev->next = ptr->next;
else rstate->service_registration->instances = ptr->next;
break;
}
prev = ptr;
ptr = ptr->next;
}
}
while(e)
{
e->r.RecordContext = e;
tmp = e;
e = e->next;
FreeExtraRR(gmDNS, &tmp->r, mStatus_MemFree);
}
if (srv->subtypes) { freeL("regservice_callback", srv->subtypes); srv->subtypes = NULL; }
freeL("regservice_callback", srv);
}
static void regservice_termination_callback(void *context)
{
service_info *info = context;
service_instance *i, *p;
if (!info) { LogMsg("regservice_termination_callback context is NULL"); return; }
if (!info->request) { LogMsg("regservice_termination_callback info->request is NULL"); return; }
i = info->instances;
while (i)
{
p = i;
i = i->next;
p->request = NULL; LogOperation("%3d: DNSServiceRegister(%##s, %u) STOP", info->request->sd, p->srs.RR_SRV.resrec.name->c, mDNSVal16(p->srs.RR_SRV.resrec.rdata->u.srv.port));
if (mDNS_DeregisterService(gmDNS, &p->srs)) free_service_instance(p);
}
info->request->service_registration = NULL; freeL("service_info", info);
}
static mStatus handle_regrecord_request(request_state *rstate)
{
AuthRecord *rr;
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, 1);
if (!rr) return(mStatus_BadParamErr);
re = mallocL("handle_regrecord_request", sizeof(registered_record_entry));
if (!re) FatalError("ERROR: malloc");
re->key = rstate->hdr.reg_index;
re->rr = rr;
re->rstate = rstate;
re->client_context = rstate->hdr.client_context;
rr->RecordContext = re;
rr->RecordCallback = regrecord_callback;
re->next = rstate->reg_recs;
rstate->reg_recs = re;
if (!rstate->terminate)
{
rstate->terminate = connected_registration_termination;
rstate->termination_context = rstate;
}
if (rr->resrec.rroriginalttl == 0)
rr->resrec.rroriginalttl = DefaultTTLforRRType(rr->resrec.rrtype);
LogOperation("%3d: DNSServiceRegisterRecord %s", rstate->sd, RRDisplayString(gmDNS, &rr->resrec));
result = mDNS_Register(gmDNS, rr);
return(result);
}
static void regrecord_callback(mDNS *const m, AuthRecord * rr, mStatus result)
{
registered_record_entry *re = rr->RecordContext;
request_state *rstate = re ? re->rstate : NULL;
int len;
reply_state *reply;
transfer_state ts;
(void)m;
if (!re)
{
if (!result) LogMsg("Error: regrecord_callback: successful registration of orphaned record");
else
{
if (result != mStatus_MemFree) LogMsg("regrecord_callback: error %d received after parent termination", result);
freeL("regrecord_callback", rr);
}
return;
}
len = sizeof(DNSServiceFlags);
len += sizeof(uint32_t); len += sizeof(DNSServiceErrorType);
reply = create_reply(reg_record_reply, len, rstate);
reply->mhdr->client_context = re->client_context;
reply->rhdr->flags = dnssd_htonl(0);
reply->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, rr->resrec.InterfaceID));
reply->rhdr->error = dnssd_htonl(result);
if (result)
{
registered_record_entry **ptr = &re->rstate->reg_recs;
while (*ptr && (*ptr) != re) ptr = &(*ptr)->next;
if (!*ptr) { LogMsg("regrecord_callback - record not in list!"); return; }
*ptr = (*ptr)->next;
freeL("regrecord_callback", re->rr);
re->rr = rr = NULL;
freeL("regrecord_callback", re);
re = NULL;
}
ts = send_msg(reply);
if (ts == t_error || ts == t_terminated) { abort_request(rstate); unlink_request(rstate); }
else if (ts == t_complete) freeL("regrecord_callback", reply);
else if (ts == t_morecoming) append_reply(rstate, reply); }
static void connected_registration_termination(void *context)
{
int shared;
registered_record_entry *fptr, *ptr = ((request_state *)context)->reg_recs;
while(ptr)
{
fptr = ptr;
ptr = ptr->next;
shared = fptr->rr->resrec.RecordType == kDNSRecordTypeShared;
fptr->rr->RecordContext = NULL;
mDNS_Deregister(gmDNS, fptr->rr);
freeL("connected_registration_termination", fptr);
}
}
static mStatus handle_removerecord_request(request_state *rstate)
{
mStatus err = mStatus_BadReferenceErr;
char *ptr;
service_info *srvinfo = rstate->service_registration;
ptr = rstate->msgdata;
get_flags(&ptr);
if (rstate->reg_recs) err = remove_record(rstate); else if (!srvinfo) LogOperation("%3d: DNSServiceRemoveRecord (bad ref)", rstate->sd);
else
{
service_instance *i;
LogOperation("%3d: DNSServiceRemoveRecord(%##s)", rstate->sd,
(srvinfo->instances) ? srvinfo->instances->srs.RR_SRV.resrec.name->c : NULL);
for (i = srvinfo->instances; i; i = i->next)
{
err = remove_extra(rstate, i);
if (err && i->default_local) break;
else err = mStatus_NoError; }
}
return(err);
}
static mStatus remove_record(request_state *rstate)
{
int shared;
mStatus err = mStatus_UnknownErr;
registered_record_entry *e, **ptr = &rstate->reg_recs;
while(*ptr && (*ptr)->key != rstate->hdr.reg_index) ptr = &(*ptr)->next;
if (!*ptr) { LogMsg("DNSServiceRemoveRecord - bad reference"); return mStatus_BadReferenceErr; }
e = *ptr;
*ptr = e->next;
LogOperation("%3d: DNSServiceRemoveRecord(%#s)", rstate->sd, e->rr->resrec.name->c);
shared = e->rr->resrec.RecordType == kDNSRecordTypeShared;
e->rr->RecordContext = NULL;
err = mDNS_Deregister(gmDNS, e->rr);
if (err)
{
LogMsg("ERROR: remove_record, mDNS_Deregister: %ld", err);
freeL("remove_record", e->rr);
freeL("remove_record", e);
}
return err;
}
static mStatus remove_extra(request_state *rstate, service_instance *serv)
{
mStatus err = mStatus_BadReferenceErr;
ExtraResourceRecord *ptr;
for (ptr = serv->srs.Extras; ptr; ptr = ptr->next)
{
if (ptr->ClientID == rstate->hdr.reg_index) return mDNS_RemoveRecordFromService(gmDNS, &serv->srs, ptr, FreeExtraRR, ptr);
}
return err;
}
static void handle_enum_request(request_state *rstate)
{
DNSServiceFlags flags;
uint32_t ifi;
mDNSInterfaceID InterfaceID;
char *ptr = rstate->msgdata;
domain_enum_t *def, *all;
enum_termination_t *term;
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);
InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, ifi);
if (ifi && !InterfaceID)
{
deliver_error(rstate, mStatus_BadParamErr);
abort_request(rstate);
unlink_request(rstate);
}
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) FatalError("ERROR: malloc");
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;
if (!InterfaceID) InterfaceID = mDNSInterface_LocalOnly;
LogOperation("%3d: DNSServiceEnumerateDomains(%X=%s)", rstate->sd, flags,
(flags & kDNSServiceFlagsBrowseDomains ) ? "kDNSServiceFlagsBrowseDomains" :
(flags & kDNSServiceFlagsRegistrationDomains) ? "kDNSServiceFlagsRegistrationDomains" : "<<Unknown>>");
err = mDNS_GetDomains(gmDNS, &all->question, all->type, NULL, InterfaceID, enum_result_callback, all);
if (err == mStatus_NoError)
err = mDNS_GetDomains(gmDNS, &def->question, def->type, NULL, InterfaceID, enum_result_callback, def);
result = deliver_error(rstate, err);
if (result < 0 || err)
{
abort_request(rstate);
unlink_request(rstate);
return;
}
}
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;
(void)m;
if (answer->rrtype != kDNSType_PTR) return;
if (AddRecord)
{
flags |= kDNSServiceFlagsAdd;
if (de->type == mDNS_DomainTypeRegistrationDefault || de->type == mDNS_DomainTypeBrowseDefault)
flags |= kDNSServiceFlagsDefault;
}
ConvertDomainNameToCString(&answer->rdata->u.name, domain);
reply = format_enumeration_reply(de->rstate, domain, flags, kDNSServiceInterfaceIndexAny, 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, const char *domain, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err)
{
size_t 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 = dnssd_htonl(flags);
reply->rhdr->ifi = dnssd_htonl(ifi);
reply->rhdr->error = dnssd_htonl(err);
data = reply->sdata;
put_string(domain, &data);
return reply;
}
static void enum_termination_callback(void *context)
{
enum_termination_t *t = context;
mDNS *coredata = gmDNS;
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, 1);
if (!rr) return;
LogOperation("%3d: DNSServiceReconfirmRecord(%##s) %s", rstate->sd, RRDisplayString(gmDNS, &rr->resrec));
mDNS_ReconfirmByValue(gmDNS, &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 GetTTL, int validate_flags)
{
char *rdata, name[256];
AuthRecord *rr;
DNSServiceFlags flags;
uint32_t interfaceIndex;
uint16_t type, class, rdlen;
int storage_size;
flags = get_flags(&msgbuf);
if (validate_flags &&
!((flags & kDNSServiceFlagsShared) == kDNSServiceFlagsShared) &&
!((flags & kDNSServiceFlagsUnique) == kDNSServiceFlagsUnique))
{
LogMsg("ERROR: Bad resource record flags (must be kDNSServiceFlagsShared or kDNSServiceFlagsUnique)");
return NULL;
}
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) FatalError("ERROR: malloc");
bzero(rr, sizeof(AuthRecord));
mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, interfaceIndex),
type, 0, (flags & kDNSServiceFlagsShared) ? kDNSRecordTypeShared : kDNSRecordTypeUnique, mDNSNULL, mDNSNULL);
if (!MakeDomainNameFromDNSNameString(rr->resrec.name, name))
{
LogMsg("ERROR: bad name: %s", name);
freeL("read_rr_from_ipc_msg", rr);
return NULL;
}
if (flags & kDNSServiceFlagsAllowRemoteQuery) rr->AllowRemoteQuery = mDNStrue;
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 (GetTTL)
{
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 += (int) (strlen(namestr) + 1);
len += (int) (strlen(typestr) + 1);
len += (int) (strlen(domstr) + 1);
*rep = create_reply(query_reply, len, request);
(*rep)->rhdr->flags = dnssd_htonl(0);
(*rep)->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, id));
(*rep)->rhdr->error = dnssd_htonl(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))
{
ConvertHeaderBytes(&rs->hdr);
if (rs->hdr.version != VERSION)
{
LogMsg("ERROR: read_msg - client version 0x%08X does not match daemon version 0x%08X", rs->hdr.version, VERSION);
rs->ts = t_error;
return t_error;
}
}
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 + MSG_PAD_BYTES);
if (!rs->msgbuf)
{
my_perror("ERROR: malloc");
rs->ts = t_error;
return t_error;
}
rs->msgdata = rs->msgbuf;
}
bzero(rs->msgbuf, rs->hdr.datalen + MSG_PAD_BYTES);
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 (dnssd_errno() == dnssd_EWOULDBLOCK || dnssd_errno() == dnssd_EINTR) return t_morecoming;
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;
}
ConvertHeaderBytes(rs->mhdr);
nwriten = send(rs->sd, rs->msgbuf + rs->nwriten, rs->len - rs->nwriten, 0);
ConvertHeaderBytes(rs->mhdr);
if (nwriten < 0)
{
if (dnssd_errno() == dnssd_EINTR || dnssd_errno() == dnssd_EWOULDBLOCK) nwriten = 0;
else
{
#if !defined(PLATFORM_NO_EPIPE)
if (dnssd_errno() == EPIPE)
{
LogMsg("%3d: broken pipe - cleanup will be handled by run-loop read wakeup", rs->sd);
rs->ts = t_terminated;
rs->request->ts = t_terminated;
return t_terminated;
}
else
#endif
{
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, size_t 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 = (int) (datalen + sizeof(ipc_msg_hdr));
reply = mallocL("create_reply", sizeof(reply_state));
if (!reply) FatalError("ERROR: malloc");
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) FatalError("ERROR: malloc");
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 = 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;
err = dnssd_htonl(err);
nwritten = send(rstate->sd, &err, sizeof(mStatus), 0);
if (nwritten < (int)sizeof(mStatus))
{
if (dnssd_errno() == dnssd_EINTR || dnssd_errno() == dnssd_EWOULDBLOCK)
nwritten = 0;
if (nwritten < 0)
{
my_perror("ERROR: send - unable to deliver error to client");
return(-1);
}
else
{
undeliv = mallocL("deliver_error", sizeof(undelivered_error_t));
if (!undeliv) FatalError("ERROR: malloc");
undeliv->err = err;
undeliv->nwritten = nwritten;
undeliv->sd = rstate->sd;
rstate->u_err = undeliv;
return 0;
}
}
return 0;
}
static transfer_state send_undelivered_error(request_state *rs)
{
int nwritten;
nwritten = send(rs->u_err->sd, (char *)(&rs->u_err->err) + rs->u_err->nwritten, sizeof(mStatus) - rs->u_err->nwritten, 0);
if (nwritten < 0)
{
if (dnssd_errno() == dnssd_EINTR || dnssd_errno() == dnssd_EWOULDBLOCK)
nwritten = 0;
else
{
my_perror("ERROR: send - unable to deliver error to client\n");
return t_error;
}
}
if ((unsigned int)(nwritten + rs->u_err->nwritten) >= sizeof(mStatus))
{
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 = dnssd_htonl(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);
LogOperation("%3d: Removing FD", rs->sd);
udsSupportRemoveFDFromEventLoop(rs->sd); rs->sd = dnssd_InvalidSocket;
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, dnssd_strerror(dnssd_errno()));
}
static int validate_message(request_state *rstate)
{
uint32_t min_size;
switch(rstate->hdr.op)
{
case resolve_request: min_size = sizeof(DNSServiceFlags) + sizeof(uint32_t) + (3 * sizeof(char)); break;
case query_request: min_size = sizeof(DNSServiceFlags) + sizeof(uint32_t) + sizeof(char) + (2 * sizeof(uint16_t)); break;
case browse_request: min_size = sizeof(DNSServiceFlags) + sizeof(uint32_t) + (2 * sizeof(char)); break;
case reg_service_request: min_size = sizeof(DNSServiceFlags) + sizeof(uint32_t) + (4 * sizeof(char)) + (2 * sizeof(uint16_t)); break;
case enumeration_request: min_size = sizeof(DNSServiceFlags) + sizeof(uint32_t); break;
case reg_record_request: min_size = sizeof(DNSServiceFlags) + sizeof(uint32_t) + sizeof(char) + (3 * sizeof(uint16_t)) + sizeof(uint32_t); break;
case add_record_request: min_size = sizeof(DNSServiceFlags) + (2 * sizeof(uint16_t)) + sizeof(uint32_t); break;
case update_record_request: min_size = sizeof(DNSServiceFlags) + sizeof(uint16_t) + sizeof(uint32_t); break;
case remove_record_request: min_size = sizeof(DNSServiceFlags); break;
case reconfirm_record_request: min_size=sizeof(DNSServiceFlags) + sizeof(uint32_t) + sizeof(char) + (3 * sizeof(uint16_t)); break;
case setdomain_request: min_size = sizeof(DNSServiceFlags) + sizeof(char); break;
default:
LogMsg("ERROR: validate_message - unsupported request type: %d", rstate->hdr.op);
return -1;
}
return (rstate->data_bytes >= min_size ? 0 : -1);
}
static uint32_t dnssd_htonl(uint32_t l)
{
uint32_t ret;
char * data;
data = (char*) &ret;
put_long(l, &data);
return ret;
}
#if defined(_WIN32)
static char * win32_strerror(int inErrorCode)
{
static char buffer[1024];
DWORD n;
memset(buffer, 0, sizeof(buffer));
n = FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
(DWORD) inErrorCode,
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
buffer,
sizeof( buffer ),
NULL );
if( n > 0 )
{
while( ( n > 0 ) && isspace( ( (unsigned char *) buffer)[ n - 1 ] ) )
{
buffer[ --n ] = '\0';
}
}
return buffer;
}
#endif