/*
- * Copyright (c) 2008-2011 Apple Inc. All rights reserved.
+ * Copyright (c) 2008-2015 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#define MDNS_DEBUG_STDERR 0x00000002
#define MDNS_DEBUG_ASL 0x00000004
#define MDNS_DEBUG_OUT 0x00000007
-#define MDNS_DEBUG_MORE 0x00000010
static int _mdns_debug = 0;
int i;
char *name;
+ _mdns_debug_message(";; _mdns_hostent_append_alias(%p, %s)\n", h, alias);
if ((h == NULL) || (alias == NULL)) return 0;
name = _mdns_canonicalize(alias);
static int
_mdns_hostent_append_addr(mdns_hostent_t *h, const uint8_t *addr, uint32_t len)
{
+ _mdns_debug_message(";; _mdns_hostent_append_addr(%p, %p, %u)\n", h, addr, len);
if ((h == NULL) || (addr == NULL) || (len == 0)) return 0;
/* copy the address buffer */
}
_mdns_debug_message(";; mdns_hostbyname %s type %u class %u\n", name, type, ns_c_in);
-
+
h.host.h_addrtype = af;
status = _mdns_search(name, ns_c_in, type, interface, flags, NULL, NULL, &reply);
h.host.h_addrtype = af;
_mdns_debug_message(";; mdns_hostbyaddr %s type %u class %u\n", name, ns_t_ptr, ns_c_in);
-
+
status = _mdns_search(name, ns_c_in, ns_t_ptr, interface, flags, NULL, NULL, &reply);
free(name);
if (status != 0)
if (err != NULL) *err = SI_STATUS_NO_ERROR;
_mdns_debug_message(";; mdns_addrinfo node %s serv %s\n", (const char *)node, (const char *)serv);
-
+
si_list_t *out = NULL;
memset(&h4, 0, sizeof(h4));
if (err != NULL) *err = SI_STATUS_NO_ERROR;
_mdns_debug_message(";; mdns_item_call %s type %u class %u\n", name, type, class);
-
+
memset(&h4, 0, sizeof(h4));
memset(&h6, 0, sizeof(h6));
memset(&reply, 0, sizeof(reply));
if ((c[i] == 'o') || (c[i] == 'O')) _mdns_debug |= MDNS_DEBUG_STDOUT;
if ((c[i] == 'e') || (c[i] == 'E')) _mdns_debug |= MDNS_DEBUG_STDERR;
if ((c[i] == 'a') || (c[i] == 'A')) _mdns_debug |= MDNS_DEBUG_ASL;
- if ((c[i] == 'm') || (c[i] == 'M')) _mdns_debug |= MDNS_DEBUG_MORE;
}
}
}
return len;
}
-typedef struct
+typedef struct mdns_query_context_s
{
mdns_reply_t *reply;
mdns_hostent_t *host;
DNSServiceFlags flags;
DNSServiceErrorType error;
int kq; // kqueue to notify when callback received
+ struct mdns_query_context_s *next; // linked list
} mdns_query_context_t;
+static mdns_query_context_t *in_flight;
+
static void
_mdns_query_callback(DNSServiceRef, DNSServiceFlags, uint32_t, DNSServiceErrorType, const char *, uint16_t, uint16_t, uint16_t, const void *, uint32_t, void *);
status = DNSServiceQueryRecord(&ctx->sd, flags, iface, qname, type, class, _mdns_query_callback, ctx);
if (qname != name) free(qname);
+
+ /* keep a linked list of all in-flight queries */
+ ctx->next = in_flight;
+ in_flight = ctx;
+
return status;
}
* considered complete.
*/
static bool
-_mdns_query_is_complete(mdns_query_context_t *ctx)
+_mdns_query_is_complete(mdns_query_context_t *ctx, bool *more)
{
bool complete = false;
/* NULL context is an error, but we call it complete */
if (ctx == NULL) return true;
- /*
- * The default is to ignore kDNSServiceFlagsMoreComing, since it has either
- * never been supported or worked correctly. MDNS_DEBUG_MORE makes us honor it.
- */
+ /* not complete if discoveryd says there is more coming (for some in-flight query - possibly not this one) */
if (ctx->flags & kDNSServiceFlagsMoreComing)
{
- if (_mdns_debug & MDNS_DEBUG_MORE)
- {
- _mdns_debug_message(";; mdns is_complete type %d ctx %p more coming - incomplete\n", ctx->type, ctx);
- return false;
- }
- }
+ if (more != NULL) *more = true;
+ _mdns_debug_message(";; mdns is_complete type %d ctx %p more coming - incomplete\n", ctx->type, ctx);
+ return false;
+ } else {
+ if (more != NULL) *more = false;
+ _mdns_debug_message(";; mdns is_complete type %d ctx %p clear more coming - complete\n", ctx->type, ctx);
+ }
if (ctx->last_type != ctx->type)
{
{
case ns_t_a:
case ns_t_aaaa:
- if (ctx->host != NULL && ctx->host->addr_count > 0) complete = true;
+ if (ctx->host != NULL && ctx->host->addr_count > 0)
+ {
+ _mdns_debug_message(";; mdns is_complete type %d ctx %p host addr count %d complete -> true\n", ctx->type, ctx, ctx->host->addr_count);
+ complete = true;
+ }
break;
case ns_t_ptr:
- if (ctx->host != NULL && ctx->host->host.h_name != NULL) complete = true;
+ if (ctx->host != NULL && ctx->host->host.h_name != NULL)
+ {
+ complete = true;
+ _mdns_debug_message(";; mdns is_complete type %d ctx %p host name %s complete -> true\n", ctx->type, ctx, ctx->host->host.h_name);
+ }
break;
case ns_t_srv:
- if (ctx->reply != NULL && ctx->reply->srv != NULL) complete = true;
+ if (ctx->reply != NULL && ctx->reply->srv != NULL)
+ {
+ _mdns_debug_message(";; mdns is_complete type %d ctx %p srv %s complete -> true\n", ctx->type, ctx, ctx->reply->srv);
+ complete = true;
+ }
break;
default:
_mdns_debug_message(";; mdns is_complete unexpected type %d ctx %p\n", ctx->type, ctx);
}
- _mdns_debug_message(";; mdns is_complete type %d ctx %p %s%scomplete\n", ctx->type, ctx, (ctx->flags & kDNSServiceFlagsMoreComing) ? "(more coming flag ignored)" : "", complete ? " - " : " - in");
+ _mdns_debug_message(";; mdns is_complete type %d ctx %p %scomplete\n", ctx->type, ctx, complete ? " - " : " - in");
return complete;
}
/*
* _mdns_query_clear
* Clear out the temporary fields of the context, and clear any result
- * structures that are incomplete. Retrns 1 if the query was complete.
+ * structures that are incomplete. Returns true if the query was complete.
*/
static bool
_mdns_query_clear(mdns_query_context_t *ctx)
{
- bool complete = _mdns_query_is_complete(ctx);
+ mdns_query_context_t *p;
+
+ if (ctx == NULL) return true;
- if (ctx == NULL) return complete;
+ bool more = false;
+ bool complete = _mdns_query_is_complete(ctx, &more);
if (ctx->sd != NULL)
{
ctx->flags = 0;
ctx->kq = -1;
- if (!complete)
+ if (in_flight == ctx)
+ {
+ in_flight = ctx->next;
+ }
+ else
+ {
+ p = in_flight;
+ while ((p != NULL) && (p->next != ctx)) p = p->next;
+ if (p != NULL) p->next = ctx->next;
+ }
+
+ ctx->next = NULL;
+
+ if (!complete && !more)
{
_mdns_hostent_clear(ctx->host);
ctx->anslen = -1;
}
- return complete;
+ return complete | more;
}
static void
_mdns_query_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *ctx)
{
- mdns_query_context_t *context;
+ mdns_query_context_t *p, *context;
struct in6_addr a6;
context = (mdns_query_context_t *)ctx;
+ _mdns_debug_message(";; _mdns_query_callback ctx %p flags=0x%08x%s\n", context, flags, (flags & kDNSServiceFlagsMoreComing) ? " (kDNSServiceFlagsMoreComing is set)" : "");
context->flags = flags;
context->error = errorCode;
context->last_type = rrtype;
+ /* if kDNSServiceFlagsMoreComing is NOT set, there is no more data coming for ALL in-flight queries */
+ if (!(flags & kDNSServiceFlagsMoreComing))
+ {
+ for (p = in_flight; p != NULL; p = p->next)
+ {
+ if (p->flags & kDNSServiceFlagsMoreComing)
+ {
+ _mdns_debug_message(";; cleared kDNSServiceFlagsMoreComing flag for ctx %p\n", p);
+ p->flags &= ~kDNSServiceFlagsMoreComing;
+ }
+ }
+ }
+
if (errorCode != kDNSServiceErr_NoError)
{
_mdns_debug_message(";; [%s type %hu class %hu]: error %d [ctx %p]\n", fullname, rrtype, rrclass, errorCode, context);
/* Ping the waiting thread in case this callback was invoked on another */
if (context->kq != -1)
{
+ _mdns_debug_message(";; _mdns_query_callback sending kevent wakeup\n");
struct kevent ev;
EV_SET(&ev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, 0);
int res = kevent(context->kq, &ev, 1, NULL, 0, NULL);
/* 2 for A and AAAA parallel queries */
int n_ctx = 0;
mdns_query_context_t ctx[2];
+ bool more_coming[2];
if (name == NULL) return -1;
timeout = delta;
_mdns_now(&start);
- for (i = 0; i < 2; ++i) memset(&ctx[i], 0 , sizeof(mdns_query_context_t));
+ for (i = 0; i < 2; ++i) {
+ memset(&ctx[i], 0 , sizeof(mdns_query_context_t));
+ more_coming[i] = false;
+ }
/* set up the kqueue */
kq = kqueue();
EV_SET(&ev, 1, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, 0);
while (wait)
{
+ _mdns_debug_message(";; _mdns_search wait loop\n");
+
if (initialize)
{
initialize = false;
_mdns_debug_message(";; set kevent timeout %ld.%ld [ctx %p %p]\n", timeout.tv_sec, timeout.tv_nsec, (n_ctx > 0) ? &(ctx[0]) : NULL, (n_ctx > 1) ? &(ctx[1]) : NULL);
+ errno = 0;
n = kevent(kq, NULL, 0, &ev, 1, &timeout);
if ((n < 0) && (errno != EINTR))
{
if (_mdns_sdref == NULL)
{
initialize = true;
+ _mdns_debug_message(";; _mdns_sdref is NULL, initialize = true\n");
}
else if (m > 0 && ev.filter == EVFILT_READ)
{
+ _mdns_debug_message(";; _mdns_search calling DNSServiceProcessResult\n", err);
err = DNSServiceProcessResult(_mdns_sdref);
+ _mdns_debug_message(";; DNSServiceProcessResult -> %s\n", err);
if ((err == kDNSServiceErr_ServiceNotRunning) || (err == kDNSServiceErr_BadReference))
{
_mdns_debug_message(";; DNSServiceProcessResult status %d [ctx %p %p]\n", err, (n_ctx > 0) ? &(ctx[0]) : NULL, (n_ctx > 1) ? &(ctx[1]) : NULL);
initialize = true;
}
}
+ else if (m == 0 && ev.filter == EVFILT_USER)
+ {
+ _mdns_debug_message(";; kevent wakeup\n", m, (int)ev.filter);
+ }
+ else
+ {
+ _mdns_debug_message(";; kevent m=%d ev.filter=0x%08x\n", m, ev.filter);
+ }
/* Check if all queries are complete (including errors) */
complete = true;
for (i = 0; i < n_ctx; ++i)
{
- if ((ctx[i].error != 0) || _mdns_query_is_complete(&ctx[i]))
+ bool qc = _mdns_query_is_complete(&ctx[i], &more_coming[i]);
+ _mdns_debug_message(";; ctx %d %p error=%d complete=%s\n", i, &(ctx[i]), ctx[i].error, qc ? "true" : "false");
+
+ if ((ctx[i].error != 0) || qc)
{
if (ctx[i].type == ns_t_a)
{
got_a_response = GOT_DATA;
if (ctx[i].error != 0) got_a_response = GOT_ERROR;
+ _mdns_debug_message(";; type ns_t_a got_a_response=%s ctx %p\n", (got_a_response == GOT_DATA) ? "GOT_DATA" : "GOT_ERROR", &(ctx[i]));
}
_mdns_debug_message(";; [%s type %d class %d] finished processing ctx %p\n", name, type, class, &(ctx[i]));
_mdns_debug_message(";; [%s type %d class %d] done [ctx %p %p]\n", name, type, class, (n_ctx > 0) ? &(ctx[0]) : NULL, (n_ctx > 1) ? &(ctx[1]) : NULL);
break;
}
- else if (got_a_response != 0)
+ else if (more_coming[0] || more_coming[1])
+ {
+ /* got partial data - probably from cache - reduce wait time */
+ struct timespec now, tmp, extra;
+
+ /* tmp = now - start */
+ _mdns_now(&now);
+ _mdns_sub_time(&tmp, &now, &start);
+
+ extra.tv_sec = MEDIUM_AAAA_EXTRA;
+ extra.tv_nsec = 0;
+
+ /* delta = tmp + extra */
+ _mdns_add_time(&delta, &tmp, &extra);
+
+ /* check that delta doesn't exceed our total timeout */
+ _mdns_sub_time(&tmp, &timeout, &delta);
+ if (tmp.tv_sec >= 0)
+ {
+ _mdns_debug_message(";; new timeout [%s type %d class %d] (waiting for more) %ld.%ld [ctx %p %p]\n", name, type, class, delta.tv_sec, delta.tv_nsec, (n_ctx > 0) ? &(ctx[0]) : NULL, (n_ctx > 1) ? &(ctx[1]) : NULL);
+ _mdns_deadline(&finish, &delta);
+ }
+ }
+ else if (got_a_response == GOT_DATA)
{
/* got A, adjust deadline for AAAA */
struct timespec now, tn, extra;
extra.tv_sec = SHORT_AAAA_EXTRA;
extra.tv_nsec = 0;
- /* if delta is small (<= 20 milliseconds), we probably got a result from mDNSResponder's cache */
+ /* if delta is small (<= 20 milliseconds), we probably got a result from cache */
if ((delta.tv_sec == 0) && (delta.tv_nsec <= 20000000))
{
extra.tv_sec = MEDIUM_AAAA_EXTRA;
}
}
+ _mdns_debug_message(";; finished _mdns_search loop [ctx %p %p]\n", (n_ctx > 0) ? &(ctx[0]) : NULL, (n_ctx > 1) ? &(ctx[1]) : NULL);
+
complete = false;
pthread_mutex_lock(&_mdns_mutex);
for (i = 0; i < n_ctx; ++i)
{
- if (err == 0) err = ctx[i].error;
/* only clears hostents if result is incomplete */
- complete = _mdns_query_clear(&ctx[i]) | complete;
+ bool cc = _mdns_query_clear(&ctx[i]);
+ complete = cc | complete;
+ _mdns_debug_message(";; _mdns_search ctx %p %scomplete\n", &ctx[i], cc ? "" : "in");
}
+ if (more_coming[0] || more_coming[1]) complete = false;
+
+ _mdns_debug_message(";; _mdns_search overall %scomplete\n", complete ? "" : "in");
pthread_mutex_unlock(&_mdns_mutex);
/* everything should be done with the kq by now */
if (!complete) res = -1;
if (anslen != NULL) *anslen = ctx[0].anslen;
+ _mdns_debug_message(";; _mdns_search exit res %d\n", res);
return res;
}
/*
- * Copyright (c) 2004-2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2015 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <stdlib.h>
#include <sys/errno.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
#include <mach/mach.h>
#include "membership.h"
#include "membershipPriv.h"
if (memcmp(uu, _user_compat_prefix, COMPAT_PREFIX_LEN) == 0) {
id_t *tempRes = malloc(sizeof(*tempRes));
+ if (tempRes == NULL) return ENOMEM;
memcpy(&tempID, &uu[COMPAT_PREFIX_LEN], sizeof(tempID));
(*tempRes) = ntohl(tempID);
(*result) = tempRes;
return 0;
} else if (memcmp(uu, _group_compat_prefix, COMPAT_PREFIX_LEN) == 0) {
id_t *tempRes = malloc(sizeof(*tempRes));
+ if (tempRes == NULL) return ENOMEM;
memcpy(&tempID, &uu[COMPAT_PREFIX_LEN], sizeof(tempID));
(*tempRes) = ntohl(tempID);
(*result) = tempRes;
tempID = *((id_t *) identifier);
if ((tempID == 0) || (_mbr_od_available() == false)) {
uint8_t *tempUU = malloc(sizeof(uuid_t));
+ if (tempUU == NULL) return ENOMEM;
uuid_copy(tempUU, _user_compat_prefix);
*((id_t *) &tempUU[COMPAT_PREFIX_LEN]) = htonl(tempID);
(*result) = tempUU;
tempID = *((id_t *) identifier);
if ((tempID == 0) || (_mbr_od_available() == false)) {
uint8_t *tempUU = malloc(sizeof(uuid_t));
+ if (tempUU == NULL) return ENOMEM;
uuid_copy(tempUU, _group_compat_prefix);
*((id_t *) &tempUU[COMPAT_PREFIX_LEN]) = htonl(tempID);
(*result) = tempUU;
reply_id = xpc_dictionary_get_data(reply, "identifier", &idLen);
if (reply_id != NULL) {
char *identifier = malloc(idLen);
-
+ if (identifier == NULL) return ENOMEM;
+
memcpy(identifier, reply_id, idLen); // should already be NULL terminated, etc.
(*result) = identifier;
uuid_copy(uu, result);
free(result);
}
+ else if ((rc == EIO) && (_mbr_od_available() == false)) {
+ switch (id_type) {
+ case ID_TYPE_USERNAME:
+ {
+ struct passwd *pw = getpwnam(identifier);
+ if (pw) {
+ rc = mbr_identifier_translate(ID_TYPE_UID, &(pw->pw_uid), sizeof(id_t), ID_TYPE_UUID, (void **) &result, NULL);
+ if (rc == 0) {
+ uuid_copy(uu, result);
+ free(result);
+ }
+ }
+ break;
+ }
+ case ID_TYPE_GROUPNAME:
+ {
+ struct group *grp = getgrnam(identifier);
+ if (grp) {
+ rc = mbr_identifier_translate(ID_TYPE_GID, &(grp->gr_gid), sizeof(id_t), ID_TYPE_UUID, (void **) &result, NULL);
+ if (rc == 0) {
+ uuid_copy(uu, result);
+ free(result);
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
return rc;
}
xpc_dictionary_set_int64(payload, "user_idtype", userid_type);
xpc_dictionary_set_data(payload, "user_id", userid, userid_size);
xpc_dictionary_set_int64(payload, "group_idtype", groupid_type);
+ xpc_dictionary_set_bool(payload, "refresh", refresh);
switch (groupid_type) {
case ID_TYPE_GROUPNAME: