/* dns stringing functions */
#include <errno.h>
#include "ldapdns.h"
#include "mem.h"

void name_to_dns_fix(str_t retbuf, char *name, int splithow)
{
	register int ch;

	ch = (splithow ? '|' : '.');
	if (splithow == 2) ch = '@';

	/* safe if name is truly ASCIIZ */
	while (*name == ch)
		++name;

	str_init(retbuf);
	while (*name) {
		register char *start = name;
		register unsigned length;
		register int tmp;

		while (*name && *name != ch)
			++name;
		length = name - start;
		if (length >= 127 && !splithow) {
			warning("name-segment too long: %s", name);
			return; /* invalid */
		}

		while (length) {
			/* tmp is a function of length, and length must be >0 */
			str_addch(retbuf, tmp = length > 127 ? 127 : length);
			str_catb(retbuf, start, tmp);
			length -= tmp;
			start += tmp;
		}
		while (*name == ch)
			++name;
		if (splithow == 2) {
			splithow = 0;
			ch = '.';
		}
	}
}

void dns_to_name(str_t retbuf, char *dns, int joinhow)
{
	const char *ch = ".|@";

	str_init(retbuf);
	while (*dns) {
		str_catb(retbuf, dns+1, dns[0]);
		str_addch(retbuf, ch[joinhow]);
		if (joinhow == 2) joinhow = 0;
		dns += (dns[0]+1);
	}
	/* will have two dots... */
	str_chop(retbuf);
}

/* these lists are built BACKWARDS so that we can just pop() off the first
 * entries easily when searching for wildcards
 */
void join_name_parts(str_t retbuf, list_t p)
{
	str_init(retbuf);
	while (p) {
		str_cat(retbuf, p->str);
		str_addch(retbuf, '.');
		p = p->next;
	}
	str_chop(retbuf);
}
void join_dns_parts(str_t retbuf, list_t p)
{
	str_init(retbuf);
	while (p) {
		str_addch(retbuf, __str_clen(p->str));
		str_cat(retbuf, p->str);

		p = p->next;
	}
	str_chop(retbuf);
}
list_t split_name_parts(char *name)
{
	/* name is modified by this routine! */
	list_t p = 0;
	register char *start = name;

	while (*start == '.') start++;
	while (*start) {
		while (*start && *start != '.') start++;
		if (*start) {
			*start = 0;
			start++;
			while (*start == '.') start++;
		}

		list_push(&p, name);
		name = start;
	}

	/* list is in reverse */
	list_reverse(&p);

	return p;
}
list_t split_dns_parts(char *name)
{
	/* name is modified by this routine! */
	list_t p = 0;
	register unsigned int length, nl;

	while (*name) {
		length = *name;
		if (length == 0)
			break;
		nl = name[length+1];
		name[length+1] = 0;
		list_push(&p, name+1);
		name += length;
	}

	/* list is in reverse */
	list_reverse(&p);

	return p;
}

/* packet related */
unsigned int dns_packet_copy(dns_ctx *c, char *out,unsigned int outlen)
{
	while (outlen) {
		if (c->request_pos >= c->request_len) {
			errno = EBADMSG;
			return 0;
		}
		*out = c->request_buf[c->request_pos++];
		++out; --outlen;
	}
	return c->request_pos;
}

unsigned int dns_packet_skipname(dns_ctx *c)
{
	unsigned char ch;

	for (;;) { /* should be safe because it is bounded and breaks on non-movement */
		if (c->request_pos >= c->request_len)
			break;
		ch = c->request_buf[c->request_pos++];
		if (ch >= 192) { c->request_pos++; return c->request_pos; }
		if (ch >= 64) break;
		if (!ch) return c->request_pos;
		c->request_pos += ch;
	}

	errno = EBADMSG;
	return 0;
}

unsigned int dns_packet_getname(dns_ctx *c, char **d)
{
	unsigned int loop = 0;
	unsigned int state = 0;
	unsigned int firstcompress = 0;
	unsigned int where;
	unsigned char ch;
	char name[255];
	unsigned int namelen = 0;

	for (;;) { /* should be safe: breaks on non-movement;
		      OR after 1000 iterations */
		if (c->request_pos >= c->request_len) goto PROTO;
		ch = c->request_buf[c->request_pos++];
		if (++loop >= 1000) goto PROTO;

		if (state) {
			if (namelen + 1 > sizeof name) goto PROTO;
			name[namelen++] = ch;
			--state;
		} else {
			while (ch >= 192) {
				where = ch; where -= 192; where <<= 8;
				if (c->request_pos >= c->request_len)
					goto PROTO;
				ch = c->request_buf[c->request_pos++];
				if (!firstcompress)
					firstcompress = c->request_pos;
				c->request_pos = where + ch;
				if (c->request_pos >= c->request_len)
					goto PROTO;
				ch = c->request_buf[c->request_pos++];
				if (++loop >= 1000) goto PROTO;
			}
			if (ch >= 64) goto PROTO;
			if (namelen + 1 > sizeof name) goto PROTO;
			name[namelen++] = ch;
			if (!ch) break;
			state = ch;
		}
	}

	if (!dns_domain_copy(d,name)) return 0;

	if (firstcompress) c->request_pos = firstcompress;
	return c->request_pos;

PROTO:
	errno = EBADMSG;
	return 0;
}
unsigned int dns_domain_length(const char *dn)
{
	const char *x;
	unsigned char c;

	x = dn;
	while ((c = *x++)) /* should be safe if dn is a valid dns-encoded */
		x += (unsigned int) c;
	return x - dn;
}
void dns_domain_lower(char *dn)
{
	char *x;
	int i;

	x = dn;
	while (*x) { /* should be safe if dn is a valid dns-encoded */
		for (i = 1; i <= *x; i++) {
			/* Clib */
			dn[i] = tolower(dn[i]);
		}
		dn += *x;
		dn++;
		x = dn;
	}
}
int dns_domain_copy(char **out, char *in)
{
	bin_t retval;
	unsigned int len;

	len = dns_domain_length(in);
	bin_init(retval);
	bin_copy(retval, in, len);
	if (*out) mem_free(*out);
	*out = caddr(retval);

	return 1;
}



syntax highlighted by Code2HTML, v. 0.9.1