#include <string.h>
#include <malloc.h>
#include "pcdefs.h"
#include "protocol.h"
#include "in.h"
#include "net.h"
#include "config.h"

static int dsock = -1;
static unsigned char *dname = 0;
static struct udpport *dsockp;

#define DOMSIZE 512		/* maximum domain message size to mess with */
#define DOMPORT 53
#define DOMLISTEN 1023

/*
*  flag masks for the flags field of the DOMAIN header
*/
#define DQR		0x8000			/* query = 0, response = 1 */
#define DOPCODE		0x7100			/* opcode, see below */
#define DAA		0x0400			/* Authoritative answer */
#define DTC		0x0200	/* Truncation, response was cut off at 512 */
#define DRD		0x0100			/* Recursion desired */
#define DRA		0x0080			/* Recursion available */
#define DRCODE		0x000F			/* response code, see below */

						/* opcode possible values: */
#define DOPQUERY	0			/* a standard query */
#define DOPIQ		1			/* an inverse query */
#define DOPCQM		2		/* a completion query, multiple reply */
#define DOPCQU		3     		/* a completion query, single reply */
/* the rest reserved for future */

						/* legal response codes: */
#define DROK	0				/* okay response */
#define DRFORM	1				/* format error */
#define DRFAIL	2			/* their problem, server failed */
#define DRNAME	3		/* name error, we know name doesn't exist */
#define DRNOPE	4				/* no can do request */
#define DRNOWAY	5			/* name server refusing to do request */

#define DTYPEA	1			/* host address resource record (RR) */
#define DTYPEPTR	12			/* a domain name ptr */

#define DIN		1			/* ARPA internet class */
#define DWILD	255	/* wildcard for several of the classifications */

/*
*  a resource record is made up of a compressed domain name followed by
*  this structure in network byte order.
*/
struct rrpart {
	unsigned int rtype;		/* resource record type = DTYPEA */
	unsigned int rclass;		/* RR class = DIN */
	unsigned long rttl;		/* time-to-live */
	unsigned int rdlength;		/* length of next field */
	unsigned char rdata[DOMSIZE];	/* data field */
};

/*
*  Header for the DOMAIN queries, network byte order!
*/
struct dhead {
	unsigned int	ident;		/* unique identifier */
	unsigned int	flags;
	unsigned int	qdcount;	/* question section, # of entries */
	unsigned int	ancount;	/* answers, how many */
	unsigned int	nscount;	/* count of name server RRs */
	unsigned int	arcount;	/* number of "additional" records */
};

/*
*  data for domain name lookup
*/
struct useek {
	struct dhead h;
	unsigned char x[DOMSIZE];
};

int
Sdominit()
{
	struct sockaddr saddr;

	dsockp = (struct udpport *)malloc(sizeof(struct udpport));
	if (!dsockp)
		return -1;

	dsock = Socket(SOCK_DGRAM, &saddr, 0, (struct port *)dsockp);
	saddr.port = htons(DOMLISTEN);
	(void) SoBind(dsock, &saddr);
	dname = Sgetdomain();
	return 0;
}

void
Sdomshut()
{

	(void) SoFree(dsock);
	dsock = -1;
	free(dsockp);
}

/**************************************************************************/
/*  Sdomain
*   DOMAIN based name lookup
*   query a domain name server to get an IP number
*	Returns the machine number of the machine record for future reference.
*   Events generated will have this number tagged with them.
*   Returns various negative numbers on error conditions.
*/
unsigned long
Sdomain(mname, timeout)
char *mname;		/* machine name */
unsigned int timeout;
{
	struct useek question;
	struct sockaddr saddr;
	struct rrpart *rrp;
	unsigned int num;
	unsigned char *p, *lp;
	int	i, j;

	memset(&question, 0, sizeof(question));
	if (dsock == -1)
		return 0;

	/* clear any outstanding data */
	while (SoReceive(dsock, &saddr, (unsigned char *)&question,
	    sizeof(question)) > 0)
		;

	saddr.port = htons(DOMPORT);
	saddr.addr = Sgetname();

	i = strlen(mname);
	(void) memcpy(&question.x[1], mname, i + 1);
	if (question.x[i] != '.') {
		if (!strchr(mname, '.')) {
			if (dname) {
				j = strlen((char *)dname);
				if (j) {
					question.x[i + 1] = '.';
					(void) memcpy(&question.x[i + 2],
					    dname, j + 1);
				}
			}
		}
	} else
		question.x[i] = 0;

	lp = &question.x[0];
	p = &question.x[1];
	while (*p) {
		if (*p == '.') {
			if ((*lp = p - lp - 1) > 0x3f)
				return 0;
			lp = p;
		}
		p++;
	}
	*lp = p - lp - 1;
	p++;

	*((unsigned int *)p)++ = htons(DTYPEA);
	*((unsigned int *)p)++ = htons(DIN);
	i = p - (unsigned char *)&question;

	if (i > 255 + 2 * sizeof(int))
		return 0;

	question.h.flags = htons(DRD);
	question.h.qdcount = htons(1);
	question.h.ancount = 0;
	question.h.nscount = 0;
	question.h.arcount = 0;
	num = n_clicks();
	question.h.ident = num;
	timeout += num;

	if (i != SoSend(dsock, &saddr, (unsigned char *)&question, i))
		return 0;

	do {
		netsleep();
		j = SoReceive(dsock, &saddr, (unsigned char *)&question,
		    sizeof(question));
	} while (!j && timeout != n_clicks());

	if (j <= 0)
		return 0;

	if (htons(DRCODE) & question.h.flags)
		return 0;

	i = ntohs(question.h.ancount);		/* number of answers */
	while (i-- > 0) {
		while (*p) {
			j = *p;
			if ((j & 0xC0) == 0xC0) {
				p++;
				break;
			}
			p += (j & 0x3f) + 1;
		}
		p++;		/* account for terminator on src */
		rrp = (struct rrpart *)p;
		if (rrp->rtype == htons(DTYPEA) && rrp->rclass == htons(DIN))
			return(*(unsigned long *)&rrp->rdata[0]);
		p += ntohs(rrp->rdlength) + 10;
	}
	return 0;
}
