DNSServiceRef _mdns_sdref;
DNSServiceRef _mdns_old_sdref;
-static int _mdns_query_mDNSResponder(const char *name, int class, int type, const char *interface,
- uint8_t *answer, uint32_t *anslen,
- mdns_reply_t *reply, uint32_t timeout);
+static int _mdns_query_mDNSResponder(const char *name, int class, int type,
+ const char *interface, DNSServiceFlags flags,
+ uint8_t *answer, uint32_t *anslen,
+ mdns_reply_t *reply, uint32_t timeout);
static int _mdns_resolver_get_option(dns_resolver_t *resolver, const char* option);
static void _mdns_hostent_clear(mdns_hostent_t *h);
* of the default resolver's domains).
*/
static int
-_mdns_query_unqualified(mdns_config_t *config, const char *name, uint32_t class, uint32_t type, const char *interface, uint8_t *buf, uint32_t *len, mdns_reply_t *reply)
+_mdns_query_unqualified(mdns_config_t *config, const char *name, uint32_t class, uint32_t type, const char *interface, DNSServiceFlags flags, uint8_t *buf, uint32_t *len, mdns_reply_t *reply)
{
int i, res = -1;
char *qname;
asprintf(&qname, "%s.%s", name, resolver->domain ? resolver->domain : "");
- res = _mdns_query_mDNSResponder(qname, class, type, interface, buf, len, reply, resolver->timeout);
+ res = _mdns_query_mDNSResponder(qname, class, type, interface, flags, buf, len, reply, resolver->timeout);
free(qname);
if (res == 0) break;
* additional domains).
*/
static int
-_mdns_query_absolute(mdns_config_t *config, const char *name, uint32_t class, uint32_t type, const char *interface, uint32_t fqdn, uint8_t *buf, uint32_t *len, mdns_reply_t *reply)
+_mdns_query_absolute(mdns_config_t *config, const char *name, uint32_t class, uint32_t type, const char *interface, DNSServiceFlags flags, uint32_t fqdn, uint8_t *buf, uint32_t *len, mdns_reply_t *reply)
{
int res = -1;
char *qname = (char *)name;
uint32_t timeout = _mdns_timeout_for_name(config, name);
if (fqdn == 0) asprintf(&qname, "%s.", name);
- res = _mdns_query_mDNSResponder(qname, class, type, interface, buf, len, reply, timeout);
+ res = _mdns_query_mDNSResponder(qname, class, type, interface, flags, buf, len, reply, timeout);
if (fqdn == 0) free(qname);
if (res != 0) _mdns_reply_clear(reply);
return res;
}
static int
-_mdns_search(const char *name, uint32_t class, uint32_t type, const char *interface, uint32_t fqdn, uint32_t recurse, uint8_t *buf, uint32_t *len, mdns_reply_t *reply)
+_mdns_search(const char *name, uint32_t class, uint32_t type, const char *interface, DNSServiceFlags flags, uint32_t fqdn, uint32_t recurse, uint8_t *buf, uint32_t *len, mdns_reply_t *reply)
{
int res = -1;
int i, n, ndots;
// if the name has at least ndots, try first as an absolute query.
// FQDN and PTR queries are always absolute.
if (n >= ndots || fqdn == 1 || type == ns_t_ptr) {
- res = _mdns_query_absolute(config, name, class, type, interface, fqdn, buf, len, reply);
+ res = _mdns_query_absolute(config, name, class, type, interface, flags, fqdn, buf, len, reply);
if (res == 0) {
_mdns_config_release(config);
return res;
for (i = 0; i < MAXDNSRCH && search[i] != NULL; ++i) {
char *qname;
asprintf(&qname, "%s.%s", name, search[i]);
- res = _mdns_search(qname, class, type, interface, 0, 0, buf, len, reply);
+ res = _mdns_search(qname, class, type, interface, flags, 0, 0, buf, len, reply);
free(qname);
if (res == 0) break;
}
// The name is not fully qualified and there is no search list.
// Try each default resolver, qualifying the name with that
// resolver's domain.
- res = _mdns_query_unqualified(config, name, class, type, interface, buf, len, reply);
+ res = _mdns_query_unqualified(config, name, class, type, interface, flags, buf, len, reply);
}
_mdns_config_release(config);
return res;
si_item_t *out = NULL;
uint64_t bb;
int status;
+ DNSServiceFlags flags = 0;
if (err != NULL) *err = SI_STATUS_NO_ERROR;
}
h.host.h_addrtype = af;
- status = _mdns_search(name, ns_c_in, type, interface, 0, 1, NULL, NULL, &reply);
+ status = _mdns_search(name, ns_c_in, type, interface, flags, 0, 1, NULL, NULL, &reply);
if (status != 0 || h.addr_count == 0) {
_mdns_reply_clear(&reply);
if (err != NULL) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
uint64_t bb;
int cat;
int status;
+ DNSServiceFlags flags = 0;
if (err != NULL) *err = SI_STATUS_NO_ERROR;
}
h.host.h_addrtype = af;
- status = _mdns_search(name, ns_c_in, ns_t_ptr, interface, 0, 1, NULL, NULL, &reply);
+ status = _mdns_search(name, ns_c_in, ns_t_ptr, interface, flags, 0, 1, NULL, NULL, &reply);
free(name);
if (status != 0) {
_mdns_reply_clear(&reply);
}
out = si_addrinfo_list(si, socktype, proto, p4, p6, port, 0, cname, cname);
} else {
+ DNSServiceFlags dns_flags = 0;
+ if (flags & AI_ADDRCONFIG) {
+ dns_flags |= kDNSServiceFlagsSuppressUnusable;
+ }
int res;
- res = _mdns_search(node, ns_c_in, type, interface, 0, 1, NULL, NULL, &reply);
+ res = _mdns_search(node, ns_c_in, type, interface, dns_flags, 0, 1, NULL, NULL, &reply);
if (res == 0 && (h4.addr_count > 0 || h6.addr_count > 0)) {
out = si_addrinfo_list_from_hostent(si, socktype, proto,
port, 0,
mdns_srv_t *srv;
int res;
const uint64_t unused = 0;
+ DNSServiceFlags flags = 0;
if (err != NULL) *err = SI_STATUS_NO_ERROR;
memset(&reply, 0, sizeof(reply));
- res = _mdns_search(qname, ns_c_in, ns_t_srv, interface, 0, 1, NULL, NULL, &reply);
+ res = _mdns_search(qname, ns_c_in, ns_t_srv, interface, flags, 0, 1, NULL, NULL, &reply);
if (res == 0) {
srv = reply.srv;
while (srv) {
mdns_hostent_t h6;
si_item_t *out;
int norecurse = 0;
+ DNSServiceFlags flags = 0;
if (err != NULL) *err = SI_STATUS_NO_ERROR;
reply.h4 = &h4;
reply.h6 = &h6;
- res = _mdns_search(name, class, type, interface, norecurse, 1, buf, &len, &reply);
+ res = _mdns_search(name, class, type, interface, flags, norecurse, 1, buf, &len, &reply);
if (res != 0 || len <= 0 || len > DNS_MAX_RECEIVE_SIZE) {
_mdns_reply_clear(&reply);
if (err != NULL) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
* initializes the context and starts a DNS-SD query.
*/
static DNSServiceErrorType
-_mdns_query_start(mdns_query_context_t *ctx, mdns_reply_t *reply, uint8_t *answer, uint32_t *anslen, const char* name, int class, int type, const char *interface, int kq)
+_mdns_query_start(mdns_query_context_t *ctx, mdns_reply_t *reply, uint8_t *answer, uint32_t *anslen, const char* name, int class, int type, const char *interface, DNSServiceFlags flags, int kq)
{
DNSServiceErrorType status;
- int dns_flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsReturnIntermediates;
+ flags |= kDNSServiceFlagsShareConnection;
+ flags |= kDNSServiceFlagsReturnIntermediates;
memset(ctx, 0, sizeof(mdns_query_context_t));
}
if (_mdns_debug) printf(";; mdns query %s %d %d\n", qname, type, class);
- status = DNSServiceQueryRecord(&ctx->sd, dns_flags, iface, qname, type, class, _mdns_query_callback, ctx);
+ status = DNSServiceQueryRecord(&ctx->sd, flags, iface, qname, type, class, _mdns_query_callback, ctx);
if (qname != name) free(qname);
return status;
}
}
int
-_mdns_query_mDNSResponder(const char *name, int class, int type, const char *interface, uint8_t *answer, uint32_t *anslen, mdns_reply_t *reply, uint32_t timeout_sec)
+_mdns_query_mDNSResponder(const char *name, int class, int type, const char *interface, DNSServiceFlags flags, uint8_t *answer, uint32_t *anslen, mdns_reply_t *reply, uint32_t timeout_sec)
{
DNSServiceErrorType err = 0;
int kq, n, wait = 1;
err = _mdns_query_start(&ctx[n_ctx++], reply,
answer, anslen,
name, class,
- (type == 0) ? ns_t_a : type, interface, kq);
+ (type == 0) ? ns_t_a : type, interface, flags, kq);
}
if (err == 0 && type == 0) {
err = _mdns_query_start(&ctx[n_ctx++], reply,
answer, anslen,
- name, class, ns_t_aaaa, interface, kq);
+ name, class, ns_t_aaaa, interface, flags, kq);
}
if (err && _mdns_debug) printf(";; initialization error %d\n", err);
// try to reinitialize
// Check if all queries are complete (including errors)
complete = 1;
for (i = 0; i < n_ctx; ++i) {
- if (_mdns_query_is_complete(&ctx[i]) || ctx[i].error) {
+ if (_mdns_query_is_complete(&ctx[i]) || ctx[i].error != 0) {
if (ctx[i].type == ns_t_a) {
got_response = 1;
}
break;
} else if (got_response == 1) {
// got A, adjust deadline for AAAA
- struct timespec now;
+ struct timespec now, tn, ms100;
+
+ // delta = now - start
_mdns_now(&now);
- _mdns_sub_time(&delta, &now, &start); // delta = N
- // minimum N of 50ms
- if (delta.tv_sec == 0 && delta.tv_nsec < 50000000) {
- delta.tv_nsec = 50000000;
- }
+ _mdns_sub_time(&delta, &now, &start);
- // only move deadline if timeout > 2N
- _mdns_sub_time(&now, &timeout, &delta);
- if (now.tv_sec >= 0) {
- if (_mdns_debug) printf(";; new timeout %ld.%ld\n", delta.tv_sec, delta.tv_nsec);
+ // tn = 2 * delta
+ _mdns_add_time(&tn, &delta, &delta);
+
+ // delta = tn + 100ms
+ ms100.tv_sec = 0;
+ ms100.tv_nsec = 100000000;
+ _mdns_add_time(&delta, &tn, &ms100);
+
+ // check that delta doesn't exceed our total timeout
+ _mdns_sub_time(&tn, &timeout, &delta);
+ if (tn.tv_sec >= 0) {
+ if (_mdns_debug) printf(";; new timeout (waiting for AAAA) %ld.%ld\n", delta.tv_sec, delta.tv_nsec);
_mdns_deadline(&finish, &delta);
}
}
// calculate remaining timeout
_mdns_timeout(&timeout, &finish);
- // no time remaining
+
+ // check for time remaining
if (timeout.tv_sec < 0) {
if (_mdns_debug) printf(";; timeout\n");
break;
#include <net/if.h>
#include <string.h>
#include <sys/param.h>
+#include <notify.h>
+#include <notify_keys.h>
+#include <pthread.h>
#include <TargetConditionals.h>
#include "netdb_async.h"
#include "si_module.h"
#define WANT_A6_PLUS_MAPPED_A4 3
#define WANT_A6_OR_MAPPED_A4_IF_NO_A6 4
+#define V6TO4_PREFIX_16 0x2002
+
+static int net_config_token = -1;
+static uint32_t net_v4_count = 0;
+static uint32_t net_v6_count = 0; // includes 6to4 addresses
+static uint32_t net_v6to4_count = 0;
+static pthread_mutex_t net_config_mutex = PTHREAD_MUTEX_INITIALIZER;
+
typedef struct {
struct hostent host;
int alias_count;
uint64_t ttl;
} build_hostent_t;
+static int
+_si_netconfig(uint32_t *v4, uint32_t *v6, uint32_t *v6to4)
+{
+ int status, checkit;
+ struct ifaddrs *ifa, *ifap;
+ struct sockaddr_in6 *sa6;
+
+ pthread_mutex_lock(&net_config_mutex);
+
+ checkit = 1;
+
+ if (net_config_token < 0)
+ {
+ status = notify_register_check(kNotifySCNetworkChange, &net_config_token);
+ if (status != 0) net_config_token = -1;
+ }
+
+ if (net_config_token >= 0)
+ {
+ status = notify_check(net_config_token, &checkit);
+ if (status != 0) checkit = 1;
+ }
+
+ status = 0;
+
+ if (checkit != 0)
+ {
+ if (getifaddrs(&ifa) < 0)
+ {
+ status = -1;
+ }
+ else
+ {
+ net_v4_count = 0;
+ net_v6_count = 0;
+ net_v6to4_count = 0;
+ for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next)
+ {
+ if (ifap->ifa_addr == NULL) continue;
+ if ((ifap->ifa_flags & IFF_UP) == 0) continue;
+
+ if (ifap->ifa_addr->sa_family == AF_INET)
+ {
+ net_v4_count++;
+ }
+ else if (ifap->ifa_addr->sa_family == AF_INET6)
+ {
+ net_v6_count++;
+ sa6 = (struct sockaddr_in6 *)ifap->ifa_addr;
+ if (sa6->sin6_addr.__u6_addr.__u6_addr16[0] == ntohs(V6TO4_PREFIX_16)) net_v6to4_count++;
+ }
+ }
+
+ freeifaddrs(ifa);
+ }
+ }
+
+ if (v4 != NULL) *v4 = net_v4_count;
+ if (v6 != NULL) *v6 = net_v6_count;
+ if (v6to4 != NULL) *v6to4 = net_v6to4_count;
+
+ pthread_mutex_unlock(&net_config_mutex);
+
+ return status;
+}
+
void
freeaddrinfo(struct addrinfo *a)
{
return out;
}
+static si_list_t *
+_gai_sort_list(si_list_t *in)
+{
+ si_list_t *out;
+ int lead;
+ uint32_t i, v6to4;
+ si_addrinfo_t *a;
+
+ if (in == NULL) return NULL;
+
+ v6to4 = 0;
+ if (_si_netconfig(NULL, NULL, &v6to4) < 0) v6to4 = 0;
+
+ out = (si_list_t *)calloc(1, sizeof(si_list_t));
+ if (out == NULL) return in;
+
+ out->refcount = in->refcount;
+ out->count = in->count;
+ out->entry = (si_item_t **)calloc(in->count, sizeof(si_item_t *));
+ if (out->entry == NULL)
+ {
+ free(out);
+ return in;
+ }
+
+ lead = AF_INET6;
+ if (v6to4 > 0) lead = AF_INET;
+
+ out->curr = 0;
+
+ for (i = 0; i < in->count; i++)
+ {
+ a = (si_addrinfo_t *)((uintptr_t)in->entry[i] + sizeof(si_item_t));
+ if (a->ai_family == lead) out->entry[out->curr++] = in->entry[i];
+ }
+
+ for (i = 0; i < in->count; i++)
+ {
+ a = (si_addrinfo_t *)((uintptr_t)in->entry[i] + sizeof(si_item_t));
+ if (a->ai_family != lead) out->entry[out->curr++] = in->entry[i];
+ }
+
+ free(in->entry);
+ free(in);
+
+ out->curr = 0;
+
+ return out;
+}
+
/* _gai_simple
* Simple lookup via gethostbyname2(3) mechanism.
*/
si_item_release(h4_item);
si_item_release(h6_item);
- return out;
+ return _gai_sort_list(out);
}
__private_extern__ si_list_t *
struct in_addr a4, *p4;
struct in6_addr a6, *p6;
const char *cname;
+ si_list_t *out;
if (err != NULL) *err = SI_STATUS_NO_ERROR;
if ((flags & AI_SRV) != 0)
{
/* AI_SRV SPI */
- return _gai_srv(si, node, serv, family, socktype, proto, flags, interface, err);
+ out = _gai_srv(si, node, serv, family, socktype, proto, flags, interface, err);
+ return _gai_sort_list(out);
}
else
{
if (node == NULL) cname = "localhost";
/* handle trivial questions */
- return si_addrinfo_list(si, socktype, proto, p4, p6, port, 0, cname, cname);
+ out = si_addrinfo_list(si, socktype, proto, p4, p6, port, 0, cname, cname);
+ return _gai_sort_list(out);
}
else if ((si->sim_wants_addrinfo != NULL) && si->sim_wants_addrinfo(si))
{
/* or let the current module handle the host lookups intelligently */
- return si->sim_addrinfo(si, nodeptr, servptr, family, socktype, proto, flags, interface, err);
+ out = si->sim_addrinfo(si, nodeptr, servptr, family, socktype, proto, flags, interface, err);
+ return _gai_sort_list(out);
}
/* fall back to a default path */
- return _gai_simple(si, nodeptr, servptr, family, socktype, proto, flags, interface, err);
+ out = _gai_simple(si, nodeptr, servptr, family, socktype, proto, flags, interface, err);
+ return _gai_sort_list(out);
}
static struct addrinfo *
__private_extern__ si_item_t *
si_ipnode_byname(si_mod_t *si, const char *name, int family, int flags, const char *interface, uint32_t *err)
{
- int i, status, want, if4, if6;
- struct ifaddrs *ifa, *ifap;
+ int i, status, want;
+ uint32_t if4, if6;
struct in_addr addr4;
struct in6_addr addr6;
si_item_t *item4, *item6;
if (flags & AI_ADDRCONFIG)
{
- if (getifaddrs(&ifa) < 0)
+ if (_si_netconfig(&if4, &if6, NULL) < 0)
{
if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
return NULL;
}
- for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next)
- {
- if (ifap->ifa_addr == NULL) continue;
- if ((ifap->ifa_flags & IFF_UP) == 0) continue;
- if (ifap->ifa_addr->sa_family == AF_INET) if4++;
- else if (ifap->ifa_addr->sa_family == AF_INET6) if6++;
- }
-
- freeifaddrs(ifa);
-
/* Bail out if there are no interfaces */
if ((if4 == 0) && (if6 == 0))
{