#include "mdnsd_config.h"
#include "mdnsd.h"
#include <string.h>
#include <stdlib.h>
#include <errno.h>

#define SPRIME 108		/* Size of query/publish hashes */
#define LPRIME 1009		/* Size of cache hash */

#define GC 86400                /* Brute force garbage cleanup
				 * frequency, rarely needed (daily
				 * default) */

#ifdef _MSC_VER
#include "ms_stdint.h" /* Includes stdint.h or workaround for older Visual Studios */

int gettimeofday(struct timeval * tp, struct timezone * tzp)
{
	// Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's
	static const uint64_t EPOCH = ((uint64_t) 116444736000000000ULL);

	SYSTEMTIME  system_time;
	FILETIME    file_time;
	uint64_t    time;

	GetSystemTime( &system_time );
	SystemTimeToFileTime( &system_time, &file_time );
	time =  ((uint64_t)file_time.dwLowDateTime )      ;
	time += ((uint64_t)file_time.dwHighDateTime) << 32;

	tp->tv_sec  = (long) ((time - EPOCH) / 10000000L);
	tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
	return 0;
}
#else
#include <sys/time.h>
#endif

#if defined(__MINGW32__)
static char *my_strdup(const char *s) {
    char *p = (char *)MDNSD_malloc(strlen(s) + 1);
    if(p) { strcpy(p, s); }
    return p;
}
#define STRDUP my_strdup
#elif defined(_WIN32)
#define STRDUP _strdup
#else
#define STRDUP strdup
#endif

#ifndef _WIN32
# include <netdb.h>
#endif

/**
 * Messy, but it's the best/simplest balance I can find at the moment
 *
 * Some internal data types, and a few hashes: querys, answers, cached,
 * and records (published, unique and shared).  Each type has different
 * semantics for processing, both for timeouts, incoming, and outgoing
 * I/O.  They inter-relate too, like records affect the querys they are
 * relevant to.  Nice things about MDNS: we only publish once (and then
 * ask asked), and only query once, then just expire records we've got
 * cached
*/

struct query {
	char *name;
	int type;
	unsigned long int nexttry;
	int tries;
	int (*answer)(mdns_answer_t *, void *);
	void *arg;
	struct query *next, *list;
};

struct unicast {
	int id;
	struct sockaddr *to;
	unsigned short int port;
	mdns_record_t *r;
	struct unicast *next;
};

struct cached {
	struct mdns_answer rr;
	struct query *q;
	struct cached *next;
};

struct mdns_record {
	struct mdns_answer rr;
	char unique;		/* # of checks performed to ensure */
	int tries;
	void (*conflict)(char *, int, void *);
	void *arg;
	struct timeval last_sent;
	struct mdns_record *next, *list;
};

struct mdns_daemon {
	char shutdown;
	unsigned long int expireall, checkqlist;
	struct timeval now, sleep, pause, probe, publish;
	int clazz, frame;
	struct cached *cache[LPRIME];
	struct mdns_record *published[SPRIME], *probing, *a_now, *a_pause, *a_publish;
	struct unicast *uanswers;
	struct query *queries[SPRIME], *qlist;
	mdnsd_record_received_callback received_callback;
	void *received_callback_data;
};

static int _namehash(const char *s)
{
	const unsigned char *name = (const unsigned char *)s;
	unsigned long h = 0;

	while (*name) {		/* do some fancy bitwanking on the string */
		unsigned long int g;
		h = (h << 4) + (unsigned long int)(*name++);
		if ((g = (h & 0xF0000000UL)) != 0)
			h ^= (g >> 24);
		h &= ~g;
	}

	return (int)h;
}

/* Basic linked list and hash primitives */
static struct query *_q_next(mdns_daemon_t *d, struct query *q, const char *host, int type)
{
	if (q == 0)
		q = d->queries[_namehash(host) % SPRIME];
	else
		q = q->next;

	for (; q != 0; q = q->next) {
		if (q->type == type && strcmp(q->name, host) == 0)
			return q;
	}

	return 0;
}

static struct cached *_c_next(mdns_daemon_t *d, struct cached *c,const char *host, int type)
{
	if (c == 0)
		c = d->cache[_namehash(host) % LPRIME];
	else
		c = c->next;

	for (; c != 0; c = c->next) {
		if ((type == c->rr.type || type == 255) && strcmp(c->rr.name, host) == 0)
			return c;
	}

	return 0;
}

static mdns_record_t *_r_next(mdns_daemon_t *d, mdns_record_t *r, const char *host, int type)
{
	if (r == 0)
		r = d->published[_namehash(host) % SPRIME];
	else
		r = r->next;

	for (; r != 0; r = r->next) {
		if (type == r->rr.type && strcmp(r->rr.name, host) == 0)
			return r;
	}

	return 0;
}

static int _rr_len(mdns_answer_t *rr)
{
	int len = 12;		/* name is always compressed (dup of earlier), plus normal stuff */

	if (rr->rdata)
		len += rr->rdlen;
	if (rr->rdname)
		len += (int)strlen(rr->rdname); /* worst case */
	if (rr->ip.s_addr)
		len += 4;
	if (rr->type == QTYPE_PTR)
		len += 6;	/* srv record stuff */

	return len;
}

/* Compares new rdata with known a, painfully */
static int _a_match(struct resource *r, mdns_answer_t *a)
{
	if (!a->name)
		return 0;
	if (strcmp(r->name, a->name) != 0 || r->type != a->type)
		return 0;

	if (r->type == QTYPE_SRV && !strcmp(r->known.srv.name, a->rdname) && a->srv.port == r->known.srv.port &&
		a->srv.weight == r->known.srv.weight && a->srv.priority == r->known.srv.priority)
		return 1;

	if ((r->type == QTYPE_PTR || r->type == QTYPE_NS || r->type == QTYPE_CNAME) && !strcmp(a->rdname, r->known.ns.name))
		return 1;

	if (r->rdlength == a->rdlen && r->rdlength == 0)
	    return 1;

	if ((r->rdlength == a->rdlen) && !memcmp(r->rdata, a->rdata, r->rdlength))
		return 1;

	return 0;
}

/* Compare time values easily */
static int _tvdiff(struct timeval old_time, struct timeval new_time)
{
	int udiff = 0;

	if (old_time.tv_sec != new_time.tv_sec)
		udiff = (int)((new_time.tv_sec - old_time.tv_sec) * 1000000);

	return (int)((new_time.tv_usec - old_time.tv_usec) + udiff);
}

static void _r_remove_list(mdns_record_t **list, mdns_record_t *r) {
	if (*list == r) {
		*list = r->list;
	} else {
		mdns_record_t *tmp = *list;
		while (tmp) {
			if (tmp->list == r) {
				tmp->list = r->list;
				break;
			}
			if (tmp == tmp->list)
				break;
			tmp = tmp->list;
		}
	}
}

static void _r_remove_lists(mdns_daemon_t *d, mdns_record_t *r, mdns_record_t **skip) {
	if (d->probing && &d->probing != skip) {
		_r_remove_list(&d->probing, r);
	}
	if (d->a_now && &d->a_now != skip) {
		_r_remove_list(&d->a_now, r);
	}
	if (d->a_pause && &d->a_pause != skip) {
		_r_remove_list(&d->a_pause, r);
	}
	if (d->a_publish && &d->a_publish != skip) {
		_r_remove_list(&d->a_publish, r);
	}
}

/* Make sure not already on the list, then insert */
static void _r_push(mdns_record_t **list, mdns_record_t *r)
{
	mdns_record_t *cur;

	for (cur = *list; cur != 0; cur = cur->list) {
		if (cur == r)
			return;
	}

	r->list = *list;
	*list = r;
}

/* Force any r out right away, if valid */
static void _r_publish(mdns_daemon_t *d, mdns_record_t *r)
{
	if (r->unique && r->unique < 5)
		return;		/* Probing already */

	r->tries = 0;
	d->publish.tv_sec = d->now.tv_sec;
	d->publish.tv_usec = d->now.tv_usec;
	_r_push(&d->a_publish, r);
}

/* send r out asap */
static void _r_send(mdns_daemon_t *d, mdns_record_t *r)
{
	/* Being published, make sure that happens soon */
	if (r->tries < 4) {
		d->publish.tv_sec = d->now.tv_sec;
		d->publish.tv_usec = d->now.tv_usec;
		return;
	}

	/* Known unique ones can be sent asap */
	if (r->unique) {

		// check if r already in other lists. If yes, remove it from there
		_r_remove_lists(d,r, &d->a_now);
		_r_push(&d->a_now, r);
		return;
	}

	/* Set d->pause.tv_usec to random 20-120 msec */
	d->pause.tv_sec = d->now.tv_sec;
	d->pause.tv_usec = d->now.tv_usec + (d->now.tv_usec % 100) + 20;
	_r_push(&d->a_pause, r);
}

/* Create generic unicast response struct */
static void _u_push(mdns_daemon_t *d, mdns_record_t *r, int id, struct sockaddr *to, unsigned short int port)
{
	struct unicast *u;

	u = (struct unicast *)MDNSD_calloc(1, sizeof(struct unicast));
	u->r = r;
	u->id = id;
	u->to = to;
	u->port = port;
	u->next = d->uanswers;
	d->uanswers = u;
}

static void _q_reset(mdns_daemon_t *d, struct query *q)
{
	struct cached *cur = 0;

	q->nexttry = 0;
	q->tries = 0;

	while ((cur = _c_next(d, cur, q->name, q->type))) {
		if (q->nexttry == 0 || cur->rr.ttl - 7 < q->nexttry)
			q->nexttry = cur->rr.ttl - 7;
	}

	if (q->nexttry != 0 && q->nexttry < d->checkqlist)
		d->checkqlist = q->nexttry;
}

/* No more query, update all it's cached entries, remove from lists */
static void _q_done(mdns_daemon_t *d, struct query *q)
{
	struct cached *c = 0;
	struct query *cur;
	int i = _namehash(q->name) % LPRIME;

	while ((c = _c_next(d, c, q->name, q->type)))
		c->q = 0;

	if (d->qlist == q) {
		d->qlist = q->list;
	} else {
		for (cur = d->qlist; cur->list != q; cur = cur->list)
			;
		cur->list = q->list;
	}

	if (d->queries[i] == q) {
		d->queries[i] = q->next;
	} else {
		for (cur = d->queries[i]; cur->next != q; cur = cur->next)
			;
		cur->next = q->next;
	}

	MDNSD_free(q->name);
	MDNSD_free(q);
}

/* buh-bye, remove from hash and free */
static void _r_done(mdns_daemon_t *d, mdns_record_t *r)
{
	mdns_record_t *cur = 0;
	int i = _namehash(r->rr.name) % SPRIME;

	if (d->published[i] == r)
		d->published[i] = r->next;
	else {
		for (cur = d->published[i]; cur && cur->next != r; cur = cur->next) ;
		if (cur)
			cur->next = r->next;
	}
	MDNSD_free(r->rr.name);
	MDNSD_free(r->rr.rdata);
	MDNSD_free(r->rr.rdname);
	MDNSD_free(r);
}

/* Call the answer function with this cached entry */
static void _q_answer(mdns_daemon_t *d, struct cached *c)
{
	if (c->rr.ttl <= (unsigned long int)d->now.tv_sec)
		c->rr.ttl = 0;
	if (c->q->answer(&c->rr, c->q->arg) == -1)
		_q_done(d, c->q);
}

static void _conflict(mdns_daemon_t *d, mdns_record_t *r)
{
	r->conflict(r->rr.name, r->rr.type, r->arg);
	mdnsd_done(d, r);
}

/* Expire any old entries in this list */
static void _c_expire(mdns_daemon_t *d, struct cached **list)
{
	struct cached *next, *cur = *list, *last = 0;

	while (cur != 0) {
		next = cur->next;
		if ((unsigned long int)d->now.tv_sec >= cur->rr.ttl) {
			if (last)
				last->next = next;

			/* Update list pointer if the first one expired */
			if (*list == cur)
				*list = next;

			if (cur->q)
				_q_answer(d, cur);

			MDNSD_free(cur->rr.name);
			MDNSD_free(cur->rr.rdata);
			MDNSD_free(cur->rr.rdname);
			MDNSD_free(cur);
		} else {
			last = cur;
		}
		cur = next;
	}
}

/* Brute force expire any old cached records */
static void _gc(mdns_daemon_t *d)
{
	int i;

	for (i = 0; i < LPRIME; i++) {
		if (d->cache[i])
			_c_expire(d, &d->cache[i]);
	}

	d->expireall = (unsigned long int)(d->now.tv_sec + GC);
}

static int _cache(mdns_daemon_t *d, struct resource *r)
{
	struct cached *c = 0;
	int i = _namehash(r->name) % LPRIME;

	/* Cache flush for unique entries */
	if (r->clazz == 32768 + d->clazz) {
		while ((c = _c_next(d, c, r->name, r->type)))
			c->rr.ttl = 0;
		_c_expire(d, &d->cache[i]);
	}

	/* Process deletes */
	if (r->ttl == 0) {
		while ((c = _c_next(d, c, r->name, r->type))) {
			if (_a_match(r, &c->rr)) {
				c->rr.ttl = 0;
				_c_expire(d, &d->cache[i]);
				c = NULL;
			}
		}

		return 0;
	}

	/*
	 * XXX: The c->rr.ttl is a hack for now, BAD SPEC, start
	 *      retrying just after half-waypoint, then expire
	 */
	c = (struct cached *)MDNSD_calloc(1, sizeof(struct cached));
	c->rr.name = STRDUP(r->name);
	c->rr.type = r->type;
	c->rr.ttl = (unsigned int)((unsigned long)d->now.tv_sec + (r->ttl / 2) + 8);
	c->rr.rdlen = r->rdlength;
	if (r->rdlength && !r->rdata) {
		//MDNSD_LOG_ERROR("rdlength is %d but rdata is NULL for domain name %s, type: %d, ttl: %ld", r->rdlength, r->name, r->type, r->ttl);
		MDNSD_free(c->rr.name);
		MDNSD_free(c);
		return 1;
	}
	if (r->rdlength) {
		c->rr.rdata = (unsigned char *)MDNSD_malloc(r->rdlength);
		memcpy(c->rr.rdata, r->rdata, r->rdlength);
	} else {
		c->rr.rdata = NULL;
	}

	switch (r->type) {
		case QTYPE_AAAA:
			c->rr.ip6 = r->known.aaaa.ip6;
		break;

		case QTYPE_A:
			c->rr.ip = r->known.a.ip;
			break;

		case QTYPE_NS:
		case QTYPE_CNAME:
		case QTYPE_PTR:
			c->rr.rdname = STRDUP(r->known.ns.name);
			break;

		case QTYPE_SRV:
			c->rr.rdname = STRDUP(r->known.srv.name);
			c->rr.srv.port = r->known.srv.port;
			c->rr.srv.weight = r->known.srv.weight;
			c->rr.srv.priority = r->known.srv.priority;
			break;
	}

	c->next = d->cache[i];
	d->cache[i] = c;

	if ((c->q = _q_next(d, 0, r->name, r->type)))
		_q_answer(d, c);

	return 0;
}

/* Copy the data bits only */
static void _a_copy(struct message *m, mdns_answer_t *a)
{
	if (a->rdata) {
		message_rdata_raw(m, a->rdata, a->rdlen);
		return;
	}

	if (a->ip.s_addr)
		message_rdata_long(m, a->ip);
	if (a->type == QTYPE_SRV)
		message_rdata_srv(m, a->srv.priority, a->srv.weight, a->srv.port, a->rdname);
	else if (a->rdname)
		message_rdata_name(m, a->rdname);
}

/* Copy a published record into an outgoing message */
static int _r_out(mdns_daemon_t *d, struct message *m, mdns_record_t **list)
{
	mdns_record_t *r;
	int ret = 0;

	while ((r = *list) != 0 && message_packet_len(m) + _rr_len(&r->rr) < d->frame) {
		if (r != r->list)
			*list = r->list;
		else
			*list = NULL;
		ret++;

		if (r->unique)
			message_an(m, r->rr.name, r->rr.type, (unsigned short int)(d->clazz + 32768), r->rr.ttl);
		else
			message_an(m, r->rr.name, r->rr.type,  (unsigned short int)d->clazz, r->rr.ttl);
		r->last_sent = d->now;

		_a_copy(m, &r->rr);
		if (r->rr.ttl == 0) {

			// also remove from other lists, because record may be in multiple lists at the same time
			_r_remove_lists(d, r, list);

			_r_done(d, r);

		}
	}

	return ret;
}


mdns_daemon_t *mdnsd_new(int clazz, int frame)
{
	mdns_daemon_t *d;

	d = (mdns_daemon_t *)MDNSD_calloc(1, sizeof(struct mdns_daemon));
	gettimeofday(&d->now, 0);
	d->expireall = (unsigned long int)(d->now.tv_sec + GC);
	d->clazz = clazz;
	d->frame = frame;
	d->received_callback = NULL;

	return d;
}

/* Shutting down, zero out ttl and push out all records */
void mdnsd_shutdown(mdns_daemon_t *d)
{
	int i;
	mdns_record_t *cur, *next;

	d->a_now = 0;
	for (i = 0; i < SPRIME; i++) {
		for (cur = d->published[i]; cur != 0;) {
			next = cur->next;
			cur->rr.ttl = 0;
			cur->list = d->a_now;
			d->a_now = cur;
			cur = next;
		}
	}

	d->shutdown = 1;
}

void mdnsd_flush(mdns_daemon_t *d)
{
	(void)d;
	/* - Set all querys to 0 tries
	 * - Free whole cache
	 * - Set all mdns_record_t *to probing
	 * - Reset all answer lists
	 */
}

void mdnsd_free(mdns_daemon_t *d)
{
	size_t i;
	for (i = 0; i< LPRIME; i++) {
		struct cached* cur = d->cache[i];
		while (cur) {
			struct cached* next = cur->next;
			MDNSD_free(cur->rr.name);
			MDNSD_free(cur->rr.rdata);
			MDNSD_free(cur->rr.rdname);
			MDNSD_free(cur);
			cur = next;
		}
	}

	for (i = 0; i< SPRIME; i++) {
		struct mdns_record* cur = d->published[i];
		struct query* curq = NULL;
		while (cur) {
			struct mdns_record* next = cur->next;
			MDNSD_free(cur->rr.name);
			MDNSD_free(cur->rr.rdata);
			MDNSD_free(cur->rr.rdname);
			MDNSD_free(cur);
			cur = next;
		}


		curq = d->queries[i];
		while (curq) {
			struct query* next = curq->next;
			MDNSD_free(curq->name);
			MDNSD_free(curq);
			curq = next;
		}

	}

	{
		struct unicast *u = d->uanswers;
		while (u) {
			struct unicast *next = u->next;
			MDNSD_free(u);
			u=next;
		}
	}

	MDNSD_free(d);
}


void mdnsd_register_receive_callback(mdns_daemon_t *d, mdnsd_record_received_callback cb, void* data) {
	d->received_callback = cb;
	d->received_callback_data = data;
}

int mdnsd_in(mdns_daemon_t *d, struct message *m, struct sockaddr *ip, unsigned short int port)
{
	int i;
	mdns_record_t *r = 0;

	if (d->shutdown)
		return 1;

	gettimeofday(&d->now, 0);

	if (m->header.qr == 0) {
		/* Process each query */
		for (i = 0; i < m->qdcount; i++) {
			mdns_record_t *r_start, *r_next = NULL;
			bool hasConflict = false;
			if (m->qd[i].clazz != d->clazz || (r = _r_next(d, 0, m->qd[i].name, m->qd[i].type)) == 0)
				continue;
			r_start = r;


			/* Check all of our potential answers */
			for (; r != 0; r = r_next) {

				MDNSD_LOG_TRACE("Got Query: Name: %s, Type: %d", r->rr.name, r->rr.type);

				// do this here, because _conflict deletes r and thus next is not valid anymore
				r_next = _r_next(d, r, m->qd[i].name, m->qd[i].type);
				/* probing state, check for conflicts */
				if (r->unique && r->unique < 5) {
					/* Check all to-be answers against our own */
					int j;
					for (j = 0; j < m->nscount; j++) {
						if (m->qd[i].type != m->an[j].type || strcmp(m->qd[i].name, m->an[j].name))
							continue;

						/* This answer isn't ours, conflict! */
						if (!_a_match(&m->an[j], &r->rr)) {
							_conflict(d, r);
							hasConflict = true;
							break;
						}
					}
					continue;
				}

				/* Check the known answers for this question */
				{
					int j;
					for (j = 0; j < m->ancount; j++) {
						if (m->qd[i].type != m->an[j].type || strcmp(m->qd[i].name, m->an[j].name))
							continue;

						if (d->received_callback) {
							d->received_callback(&m->an[j], d->received_callback_data);
						}

						/* Do they already have this answer? */
						if (_a_match(&m->an[j], &r->rr))
							break;
					}
					if (j == m->ancount)
						_r_send(d, r);
				}

			}

			/* Send the matching unicast reply */
			if (!hasConflict && port != 5353)
				_u_push(d, r_start, m->id, ip, port);
		}

		return 0;
	}

	/* Process each answer, check for a conflict, and cache */
	for (i = 0; i < m->ancount; i++) {
		if (m->an[i].name == NULL) {
			MDNSD_LOG_ERROR("Got answer with NULL name at %p. Type: %d, TTL: %ld\n", (void*)&m->an[i], m->an[i].type, m->an[i].ttl);
			return 3;
		}

		MDNSD_LOG_TRACE("Got Answer: Name: %s, Type: %d", m->an[i].name, m->an[i].type);
		if ((r = _r_next(d, 0, m->an[i].name, m->an[i].type)) != 0 &&
			r->unique && _a_match(&m->an[i], &r->rr) == 0)
			_conflict(d, r);

		if (d->received_callback) {
			d->received_callback(&m->an[i], d->received_callback_data);
		}
		if (_cache(d, &m->an[i]) != 0)
			return 2;
	}
	return 0;
}

int mdnsd_out(mdns_daemon_t *d, struct message *m, struct sockaddr *ip, unsigned short int *port)
{
	mdns_record_t *r;
	int ret = 0;

	gettimeofday(&d->now, 0);
	memset(m, 0, sizeof(struct message));

	/* Defaults, multicast */
	*port = htons(5353);
	if(ip->sa_family == AF_INET6) {
		struct sockaddr_in6 addr6;

		memset(&addr6, 0, sizeof(addr6));
		addr6.sin6_family = AF_INET6;
		addr6.sin6_port = *port;
		inet_pton(AF_INET6, "ff02::fb", &(addr6.sin6_addr));
		memcpy(ip, &addr6, sizeof(addr6));
	} else {
			struct sockaddr_in addr;

			memset(&addr, 0, sizeof(addr));
			addr.sin_family = AF_INET;
			addr.sin_port = *port;
			inet_pton(AF_INET, "224.0.0.251", &(addr.sin_addr));
			memcpy(ip, &addr, sizeof(addr));
	}
	m->header.qr = 1;
	m->header.aa = 1;

	/* Send out individual unicast answers */
	if (d->uanswers) {
		struct unicast *u = d->uanswers;

		MDNSD_LOG_TRACE("Send Unicast Answer: Name: %s, Type: %d", u->r->rr.name, u->r->rr.type);

		d->uanswers = u->next;
		*port = u->port;
		ip = u->to;
		m->id = (unsigned short int)u->id;
		message_qd(m, u->r->rr.name, u->r->rr.type, (unsigned short int)d->clazz);
		message_an(m, u->r->rr.name, u->r->rr.type, (unsigned short int)d->clazz, u->r->rr.ttl);
		u->r->last_sent = d->now;
		_a_copy(m, &u->r->rr);
		MDNSD_free(u);

		return 1;
	}

//	printf("OUT: probing %X now %X pause %X publish %X\n",d->probing,d->a_now,d->a_pause,d->a_publish);

	/* Accumulate any immediate responses */
	if (d->a_now)
		ret += _r_out(d, m, &d->a_now);

	/* Check if it's time to send the publish retries (unlink if done) */
	if (d->a_publish && _tvdiff(d->now, d->publish) <= 0) {

		mdns_record_t *next, *cur = d->a_publish, *last = NULL;

		while (cur && message_packet_len(m) + _rr_len(&cur->rr) < d->frame) {

			if (cur->rr.type == QTYPE_PTR) {
				MDNSD_LOG_TRACE("Send Publish PTR: Name: %s, rdlen: %d, rdata: %.*s, rdname: %s",
				        cur->rr.name,cur->rr.rdlen, cur->rr.rdlen, cur->rr.rdata, cur->rr.rdname == NULL ? "" : cur->rr.rdname);
			} else if (cur->rr.type == QTYPE_SRV) {
				MDNSD_LOG_TRACE("Send Publish SRV: Name: %s, rdlen: %d, rdata: %.*s, rdname: %s, port: %d, prio: %d, weight: %d",
				        cur->rr.name,cur->rr.rdlen, cur->rr.rdlen, cur->rr.rdata, cur->rr.rdname == NULL ? "" : cur->rr.rdname,
				        cur->rr.srv.port, cur->rr.srv.priority, cur->rr.srv.weight);
			} else {
				MDNSD_LOG_TRACE("Send Publish: Name: %s, Type: %d", cur->rr.name, cur->rr.type);
			}
			next = cur->list;
			ret++;
			cur->tries++;

			if (cur->unique)
				message_an(m, cur->rr.name, cur->rr.type, (unsigned short int)(d->clazz + 32768), cur->rr.ttl);
			else
				message_an(m, cur->rr.name, cur->rr.type, (unsigned short int)d->clazz, cur->rr.ttl);
			_a_copy(m, &cur->rr);
			cur->last_sent = d->now;

			if (cur->rr.ttl != 0 && cur->tries < 4) {
				last = cur;
				cur = next;
				continue;
			}

			if (d->a_publish == cur)
				d->a_publish = next;
			if (last)
				last->list = next;
			if (cur->rr.ttl == 0)
				_r_done(d, cur);
			cur = next;
		}

		if (d->a_publish) {
			d->publish.tv_sec = d->now.tv_sec + 2;
			d->publish.tv_usec = d->now.tv_usec;
		}
	}

	/* If we're in shutdown, we're done */
	if (d->shutdown)
		return ret;

	/* Check if a_pause is ready */
	if (d->a_pause && _tvdiff(d->now, d->pause) <= 0)
		ret += _r_out(d, m, &d->a_pause);

	/* Now process questions */
	if (ret > 0)
		return ret;

	m->header.qr = 0;
	m->header.aa = 0;

	if (d->probing && _tvdiff(d->now, d->probe) <= 0) {
		mdns_record_t *last = 0;

		/* Scan probe list to ask questions and process published */
		for (r = d->probing; r != 0;) {
			/* Done probing, publish */
			if (r->unique == 4) {
				mdns_record_t *next = r->list;

				if (d->probing == r)
					d->probing = r->list;
				else
					last->list = r->list;

				r->list = 0;
				r->unique = 5;
				_r_publish(d, r);
				r = next;
				continue;
			}

			MDNSD_LOG_TRACE("Send Probing: Name: %s, Type: %d", r->rr.name, r->rr.type);

			message_qd(m, r->rr.name, r->rr.type, (unsigned short int)d->clazz);
			r->last_sent = d->now;
			last = r;
			r = r->list;
		}

		/* Scan probe list again to append our to-be answers */
		for (r = d->probing; r != 0; r = r->list) {
			r->unique++;

			MDNSD_LOG_TRACE("Send Answer in Probe: Name: %s, Type: %d", r->rr.name, r->rr.type);
			message_ns(m, r->rr.name, r->rr.type, (unsigned short int)d->clazz, r->rr.ttl);
			_a_copy(m, &r->rr);
			r->last_sent = d->now;
			ret++;
		}

		/* Process probes again in the future */
		if (ret) {
			d->probe.tv_sec = d->now.tv_sec;
			d->probe.tv_usec = d->now.tv_usec + 250000;
			return ret;
		}
	}

	/* Process qlist for retries or expirations */
	if (d->checkqlist && (unsigned long int)d->now.tv_sec >= d->checkqlist) {
		struct query *q;
		struct cached *c;
		unsigned long int nextbest = 0;

		/* Ask questions first, track nextbest time */
		for (q = d->qlist; q != 0; q = q->list) {
			if (q->nexttry > 0 && q->nexttry <= (unsigned long int)d->now.tv_sec && q->tries < 3)
				message_qd(m, q->name, (unsigned short int)q->type, (unsigned short int)d->clazz);
			else if (q->nexttry > 0 && (nextbest == 0 || q->nexttry < nextbest))
				nextbest = q->nexttry;
		}

		/* Include known answers, update questions */
		for (q = d->qlist; q != 0; q = q->list) {
			if (q->nexttry == 0 || q->nexttry > (unsigned long int)d->now.tv_sec)
				continue;

			/* Done retrying, expire and reset */
			if (q->tries == 3) {
				_c_expire(d, &d->cache[_namehash(q->name) % LPRIME]);
				_q_reset(d, q);
				continue;
			}

			ret++;
			q->nexttry = (unsigned long int)(d->now.tv_sec + ++q->tries);
			if (nextbest == 0 || q->nexttry < nextbest)
				nextbest = q->nexttry;

			/* If room, add all known good entries */
			c = 0;
			while ((c = _c_next(d, c, q->name, q->type)) != 0 && c->rr.ttl > (unsigned long int)d->now.tv_sec + 8 &&
				   message_packet_len(m) + _rr_len(&c->rr) < d->frame) {

				MDNSD_LOG_TRACE("Add known answer: Name: %s, Type: %d", c->rr.name, c->rr.type);
				message_an(m, q->name, (unsigned short int)q->type, (unsigned short int)d->clazz, c->rr.ttl - (unsigned long int)d->now.tv_sec);
				_a_copy(m, &c->rr);
			}
		}
		d->checkqlist = nextbest;
	}

	if ((unsigned long int)d->now.tv_sec > d->expireall)
		_gc(d);

	return ret;
}


#define RET					\
	while (d->sleep.tv_usec > 1000000) {	\
		d->sleep.tv_sec++;		\
		d->sleep.tv_usec -= 1000000;	\
	}					\
	return &d->sleep;

struct timeval *mdnsd_sleep(mdns_daemon_t *d)
{
	int usec, minExpire;

	d->sleep.tv_sec = d->sleep.tv_usec = 0;

	/* First check for any immediate items to handle */
	if (d->uanswers || d->a_now)
		return &d->sleep;

	gettimeofday(&d->now, 0);

	/* Then check for paused answers or nearly expired records */
	if (d->a_pause) {
		if ((usec = _tvdiff(d->now, d->pause)) > 0)
			d->sleep.tv_usec = usec;
		RET;
	}

	/* Now check for probe retries */
	if (d->probing) {
		if ((usec = _tvdiff(d->now, d->probe)) > 0)
			d->sleep.tv_usec = usec;
		RET;
	}

	/* Now check for publish retries */
	if (d->a_publish) {
		if ((usec = _tvdiff(d->now, d->publish)) > 0)
			d->sleep.tv_usec = usec;
		RET;
	}

	/* Also check for queries with known answer expiration/retry */
	if (d->checkqlist) {
		int sec;
		if ((sec = (int)(d->checkqlist - (unsigned long int)d->now.tv_sec)) > 0)
			d->sleep.tv_sec = sec;
		RET;
	}

	/* Resend published records before TTL expires */
	// latest expire is garbage collection
	minExpire = (int)(d->expireall - (unsigned long int)d->now.tv_sec);
	if (minExpire < 0)
		return &d->sleep;

	{
		size_t i;
		for (i=0; i<SPRIME; i++) {
			int expire;
			if (!d->published[i])
				continue;
			expire = (int)((d->published[i]->last_sent.tv_sec + (long int)d->published[i]->rr.ttl) - d->now.tv_sec);
			if (expire < minExpire)
				d->a_pause = NULL;
			minExpire = expire < minExpire ? expire : minExpire;
			_r_push(&d->a_pause, d->published[i]);
		}
	}
	// publish 2 seconds before expire.
	d->sleep.tv_sec = minExpire > 2 ? minExpire-2 : 0;
	d->pause.tv_sec = d->now.tv_sec + d->sleep.tv_sec;
	RET;
}

void mdnsd_query(mdns_daemon_t *d, const char *host, int type, int (*answer)(mdns_answer_t *a, void *arg), void *arg)
{
	struct query *q;
	int i = _namehash(host) % SPRIME;

	if (!(q = _q_next(d, 0, host, type))) {
		if (!answer)
			return;

		q = (struct query *)MDNSD_calloc(1, sizeof(struct query));
		q->name = STRDUP(host);
		q->type = type;
		q->next = d->queries[i];
		q->list = d->qlist;
		d->qlist = d->queries[i] = q;

		/* Any cached entries should be associated */
		{
			struct cached *cur = 0;
			while ((cur = _c_next(d, cur, q->name, q->type)))
				cur->q = q;
		}
		_q_reset(d, q);

		/* New question, immediately send out */
		q->nexttry = d->checkqlist = (unsigned long int)d->now.tv_sec;
	}

	/* No answer means we don't care anymore */
	if (!answer) {
		_q_done(d, q);
		return;
	}

	q->answer = answer;
	q->arg = arg;
}

mdns_answer_t *mdnsd_list(mdns_daemon_t *d,const char *host, int type, mdns_answer_t *last)
{
	return (mdns_answer_t *)_c_next(d, (struct cached *)last, host, type);
}

mdns_record_t *mdnsd_record_next(const mdns_record_t* r) {
	return r ? r->next : NULL;
}

const mdns_answer_t *mdnsd_record_data(const mdns_record_t* r) {
	return &r->rr;
}

mdns_record_t *mdnsd_shared(mdns_daemon_t *d, const char *host, unsigned short int type, unsigned long int ttl)
{
	int i = _namehash(host) % SPRIME;
	mdns_record_t *r;

	r = (struct mdns_record *)MDNSD_calloc(1, sizeof(struct mdns_record));
	r->rr.name = STRDUP(host);
	r->rr.type = type;
	r->rr.ttl = ttl;
	r->next = d->published[i];
	d->published[i] = r;

	return r;
}

mdns_record_t *mdnsd_unique(mdns_daemon_t *d, const char *host, unsigned short int type, unsigned long int ttl, void (*conflict)(char *host, int type, void *arg), void *arg)
{
	mdns_record_t *r;

	r = mdnsd_shared(d, host, type, ttl);
	r->conflict = conflict;
	r->arg = arg;
	r->unique = 1;
	_r_push(&d->probing, r);
	d->probe.tv_sec = d->now.tv_sec;
	d->probe.tv_usec = d->now.tv_usec;

	return r;
}

mdns_record_t * mdnsd_get_published(const mdns_daemon_t *d, const char *host) {
	return d->published[_namehash(host) % SPRIME];
}

int mdnsd_has_query(const mdns_daemon_t *d, const char *host) {
	return d->queries[_namehash(host) % SPRIME]!=NULL;
}

void mdnsd_done(mdns_daemon_t *d, mdns_record_t *r)
{
	mdns_record_t *cur;

	if (r->unique && r->unique < 5) {
		/* Probing yet, zap from that list first! */
		if (d->probing == r) {
			d->probing = r->list;
		} else {
			for (cur = d->probing; cur->list != r; cur = cur->list)
				;
			cur->list = r->list;
		}

		_r_done(d, r);
		return;
	}

	r->rr.ttl = 0;
	_r_send(d, r);
}

void mdnsd_set_raw(mdns_daemon_t *d, mdns_record_t *r, const char *data, unsigned short int len)
{
	MDNSD_free(r->rr.rdata);
	r->rr.rdata = (unsigned char *)MDNSD_malloc(len);
	memcpy(r->rr.rdata, data, len);
	r->rr.rdlen = len;
	_r_publish(d, r);
}

void mdnsd_set_host(mdns_daemon_t *d, mdns_record_t *r, const char *name)
{
	MDNSD_free(r->rr.rdname);
	r->rr.rdname = STRDUP(name);
	_r_publish(d, r);
}

void mdnsd_set_ip(mdns_daemon_t *d, mdns_record_t *r, struct in_addr ip)
{
	r->rr.ip = ip;
	_r_publish(d, r);
}

void mdnsd_set_srv(mdns_daemon_t *d, mdns_record_t *r, unsigned short int priority, unsigned short int weight, unsigned short int port, const char *name)
{
	r->rr.srv.priority = priority;
	r->rr.srv.weight = weight;
	r->rr.srv.port = port;
	mdnsd_set_host(d, r, name);
}

#if MDNSD_LOGLEVEL <= 100
#include <ctype.h>
static void dump_hex_pkg(const char* buffer, int bufferLen) {
	char ascii[17];
	memset(ascii,0,17);
	for (int i = 0; i < bufferLen; i++)
	{
		if (i%16 == 0)
			printf("%s\n%06x ", ascii, i);
		if (isprint((int)(buffer[i])))
			ascii[i%16] = buffer[i];
		else
			ascii[i%16] = '.';
		printf("%02X ", (unsigned char)buffer[i]);
	}
	printf("%s\n%06x ", ascii, bufferLen);
	printf("\n");
}
#endif

unsigned short int mdnsd_step(mdns_daemon_t *d, int mdns_socket, bool processIn, bool processOut, struct timeval *nextSleep) {

	struct message m;

	if (processIn) {
		int bsize;
		unsigned char buf[MAX_PACKET_LEN];
		struct sockaddr_storage from;
		socklen_t ssize = sizeof(from);

		while ((bsize = (int)recvfrom(mdns_socket, (char*)buf, MAX_PACKET_LEN, 0, (struct sockaddr *)&from, &ssize)) > 0) {
			memset(&m, 0, sizeof(struct message));
#if MDNSD_LOGLEVEL <= 100
			MDNSD_LOG_TRACE("Got Data:");
			dump_hex_pkg((const char*)buf, bsize);
#endif
#ifdef MDNSD_DEBUG_DUMP_PKGS_FILE
            mdnsd_debug_dumpCompleteChunk(d, (char*)buf, (size_t) bsize);
#endif
			if (!message_parse(&m, buf, (size_t)bsize))
			    continue;

			unsigned short int fromPort;
			if (from.ss_family == AF_INET){
				fromPort = ((struct sockaddr_in *)&from)->sin_port;
			} else {
				fromPort = ((struct sockaddr_in6 *)&from)->sin6_port;
			}
			if (mdnsd_in(d, &m,(struct sockaddr *)&from, fromPort) !=0)
				return 2;
		}
#ifdef _WIN32
		if (bsize < 0 && WSAGetLastError() != WSAEWOULDBLOCK)
#else
		if (bsize < 0 && errno != EAGAIN)
#endif
		{
			return 1;
		}
	}

	if (processOut) {
		struct sockaddr_storage addrStorage;
		struct sockaddr *to = (struct sockaddr*)&addrStorage;
		socklen_t addrLength = sizeof(struct sockaddr_storage);
		unsigned short int port;
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wcast-align"
#endif

		if(getsockname(mdns_socket, to, &addrLength))
			return 2;
		while (mdnsd_out(d, &m, to, &port)) {
#ifdef __clang__
#pragma clang diagnostic pop
#endif
			int len = message_packet_len(&m);
			char* buf = (char*)message_packet(&m);
#if MDNSD_LOGLEVEL <= 100
			MDNSD_LOG_TRACE("Send Data:");
			dump_hex_pkg(buf, (int)len);
#endif

#ifdef MDNSD_DEBUG_DUMP_PKGS_FILE
			mdnsd_debug_dumpCompleteChunk(d, buf, (size_t) len);
#endif
			if ((sendto(mdns_socket, buf, (unsigned int)len, 0, to,
						addrLength)) != len) {
				return 2;
			}
		}
	}

	if (nextSleep) {
		struct timeval *tv = mdnsd_sleep(d);
		nextSleep->tv_sec = tv->tv_sec;
		nextSleep->tv_usec = tv->tv_usec;
	}

	return 0;
}
