#include "bin.h"
#include "ldapdns.h"
#include "dns.h"
#include "str.h"
#include "ht.h"

#include <netinet/in.h>
#include <string.h>

int response_rstart(dns_ctx *c, char *dnsenc, char rr[2], unsigned int ttl)
{
	if (!response_addname(c, dnsenc)) return 0;
	if (!response_addbytes(c, rr, 2)) return 0;
	if (!response_addbytes(c, DNS_C_IN, 2)) return 0;
	if (!response_addulong(c, ttl)) return 0;
	if (!response_addbytes(c, "\0\0", 2)) return 0;
	c->response_dpos = clen(c->response);
	return 1;
}
static unsigned long __hash_dns(const void *str, unsigned len)
{
	const char *h;
	unsigned long d;
	unsigned i;

	d = 5431;
	for (i = 0, h = str; i < len; i++) {
		/* Clib */
		d += (d << 5) ^ (tolower((int)h[i]));
	}

	return d;
}
void response_axfr(dns_ctx *c)
{
	c->response->used = 0; /* Internal */
}
int response_axstart(dns_ctx *c, int soa, char *q, char qt[2], char qc[2], unsigned int ttl)
{
	int flushed = 0;
	char buf[10];

	if (c->response->used > 0) {
		tp_write(c);
		flushed = 1;
	}

	c->answers = 0;
	c->response_ls = 0;
	ht_die(&c->response_names);
	c->response->used = 0; /* Internal */

	ht_init(&c->response_names, 7, __hash_dns);

	if (!response_addbytes(c, c->dns_message_id, 2)) return 0;
	memcpy(buf, "\204\000\0\0\0\1\0\0\0\0", 10);
	if (!soa) buf[0] &= ~4;

	if (!flushed) {
		buf[3] = 1;
		if (!response_addbytes(c, buf, 10)) return 0;
		if (!response_addname(c, q)) return 0;
		/* we only support AXFR over internet */
		if (!response_addbytes(c, DNS_T_AXFR, 2)) return 0;
		if (!response_addbytes(c, DNS_C_IN, 2)) return 0;
	} else {
		if (!response_addbytes(c, buf, 10)) return 0;
	}
	if (!response_addname(c, q)) return 0;
	if (!response_addbytes(c, qt, 2)) return 0;
	if (!response_addbytes(c, qc, 2)) return 0;
	if (!response_addulong(c, ttl)) return 0;
	if (!response_addbytes(c, "\0\0", 2)) return 0;
	c->response_dpos = clen(c->response);
	return 1;
}
void response_axfinish(dns_ctx *c)
{
	unsigned short n;

	/* Clib */
	n = htons(clen(c->response) - c->response_dpos);
	memcpy(c->response->buf + (c->response_dpos - 2), &n, 2);
}
int response_query(dns_ctx *c, char *q, char qt[2], char qc[2])
{
	c->answers = 0;
	c->response_ls = 0;
	ht_die(&c->response_names);
	c->response->used = 0; /* Internal */

	ht_init(&c->response_names, 7, __hash_dns);

	if (!response_addbytes(c, "\0\0\201\200\0\1\0\0\0\0\0\0", 12)) return 0;
	if (!response_addname(c, q)) return 0;
	if (!response_addbytes(c, qt, 2)) return 0;
	if (!response_addbytes(c, qc, 2)) return 0;

	c->response_tc = clen(c->response);
	return 1;
}
int response_notify(dns_ctx *c, char *q, char qt[2], char qc[2])
{
	c->response->used = 0; /* Internal */

	if (!response_addbytes(c, "\0\0\204\200\0\1\0\0\0\0\0\0", 12)) return 0;
	if (!response_addname(c, q)) return 0;
	if (!response_addbytes(c, qt, 2)) return 0;
	if (!response_addbytes(c, qc, 2)) return 0;

	return 1;
}

int response_adddns(dns_ctx *c, char *dnsenc)
{
	register int dlen, i;
	unsigned short j;

	/* ALWAYS: convert dnsenc to upper case */
	for (i = 0; dnsenc[i]; i++) {
		dnsenc[i] = tolower(dnsenc[i]);
	}
	dlen = i;

	/* safe IF dnsenc is a valid dns name */
	while (*dnsenc) {
		i = ht_fetchint(&c->response_names, dnsenc, dlen);
		if (i > -1) {
			return response_addnameptr(c, i);
		}
		if (dlen <= 128) {
			j = clen(c->response);
			ht_storeint(&c->response_names, dnsenc, dlen, j);
		}
		i = ((unsigned char) *dnsenc) + 1;
		if (!response_addbytes(c, dnsenc, i))
			return 0;
		dnsenc += i;
		dlen -= i;
	}
	return response_addbytes(c, "\0", 1);
}
int response_addnetbios(dns_ctx *c, char *dnsenc)
{
	/* translates a name to netbios format */
	char buf[34];
	register int i, j, x;

	/* not a netbios name */
	if (dns_domain_length(dnsenc) > 16)
		return response_adddns(c, dnsenc);

	for (i = 0, j = 1; i < dnsenc[0]; i++) {
		x = toupper((unsigned int)(dnsenc[i+1]));
		buf[j] = (0x41 + ((x & 0xF0) >> 4));
		j++;
		buf[j] = (0x41 + ((x & 0x0F)));
		j++;
	}
	while (j < 31) {
		buf[j] = 0x43; j++; /* ca == whitespace */
		buf[j] = 0x41; j++;
	}
	buf[31] = 0x41; buf[32] = 0x41;
	buf[33] = 0;
	buf[0] = 32;
	return response_adddns(c, buf);
}
int response_addname(dns_ctx *c, char *dnsenc)
{
	if (c->protnum == PROT_NETBIOS)
		return response_addnetbios(c, dnsenc);
	return response_adddns(c, dnsenc);
}

int response_addbytes(dns_ctx *c, unsigned char *b, int l)
{
	bin_cat(c->response, b, l);
	return 1;
}

int response_addulong(dns_ctx *c, unsigned long n)
{
	char buf[4];

	/* Clib */
	n = htonl(n);
	memcpy(buf, &n, 4);
	return response_addbytes(c, buf, 4);
}
int response_addnameptr(dns_ctx *c, unsigned int u)
{
	char buf[2];

	u += 49152;
	buf[1] = u & 255;
	buf[0] = u >> 8;
	return response_addbytes(c, buf, 2);
}
int response_addushort(dns_ctx *c, unsigned short n)
{
	char buf[2];

	/* Clib */
	n = htons(n);
	memcpy(buf, &n, 2);
	return response_addbytes(c, buf, 2);
}

void response_id(dns_ctx *c, const char id[2])
{
	bin_need(c->response, 12);
	c->dns_message_id[0] = c->response->buf[0] = id[0];
	c->dns_message_id[1] = c->response->buf[1] = id[1];
}
void response_rcode(dns_ctx *c, int code)
{
	bin_need(c->response, 12);
	c->response->buf[3] &= ~15;
	c->response->buf[3] |= (code & 15);
}
void response_aa(dns_ctx *c, int setting)
{
	bin_need(c->response, 12);
	if (setting) {
		c->response->buf[2] |= 4;	/* set 'aa' flag */
	} else {
		c->response->buf[2] &= (~4);	/* clear 'aa' flag */
	}
}
void response_tc(dns_ctx *c)
{
	bin_need(c->response, 12);
	c->response->buf[2] |= 2;
	c->response->used = c->response_tc; /* Internal */
}
void response_nxdomain(dns_ctx *c)
{
	bin_need(c->response, 12);
	c->response->buf[3] |= 3;
	c->response->buf[2] |= 4;
}
void response_servfail(dns_ctx *c)
{
	bin_need(c->response, 12);
	c->response->buf[3] |= 2;
}
void response_refuse(dns_ctx *c)
{
	bin_need(c->response, 12);

	if (c->axfr) {
		c->response->buf[2] &= ~4;
		c->response->buf[3] &= ~128;
		c->response->buf[3] &= ~15;
		c->response->buf[3] |= 5;
	} else {
		c->response->buf[2] &= ~4;
		c->response->buf[3] &= ~15;
		c->response->buf[3] |= 5;
	}
}

int response_rfinish(dns_ctx *c, int section)
{
	unsigned short n;

	bin_need(c->response, 12);
	/* increment answers... */

	/* Clib */
	memcpy(&n, c->response->buf + section, 2);

	/* Clib */
	n = htons(ntohs(n) + 1);
	memcpy(c->response->buf + section, &n, 2);

	/* set length of this response (rr) data */
	
	/* Clib */
	n = htons(clen(c->response) - c->response_dpos);
	memcpy(c->response->buf + (c->response_dpos - 2), &n, 2);

	if (c->response_ls != section) {
		/* change of section */
		c->response_tc = clen(c->response);
		c->response_ls = section;
	}
	if (section == RESPONSE_ANSWER)
		c->answers++;

	return 1;
}



syntax highlighted by Code2HTML, v. 0.9.1