/*
 * 2008+ Copyright (c) Evgeniy Polyakov <johnpol@2ka.mipt.ru>
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#ifndef __QUERY_H
#define __QUERY_H

#include <sys/types.h>
#include <sys/socket.h>

#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <dirent.h>
#include <pthread.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#define uloga(f, a...) fprintf(stderr, f, ##a)
#define ulog_err(f, a...) uloga(f ": %s [%d].\n", ##a, strerror(errno), errno)

#define QUERY_DEBUG

#ifdef QUERY_DEBUG
#define ulog(f, a...) uloga(f, ##a)
#else
#define ulog(f, a...) do {} while (0)
#endif

#define QUERY_FLAGS_RESPONSE		0x8000	/* 0 - query, 1 - response */

#define QUERY_FLAGS_OPCODE_SHIFT	11
#define QUERY_FLAGS_OPCODE_MASK		0xf
#define QUERY_FLAGS_OPCODE_STANDARD	0	/* a standrad query: QUERY */
#define QUERY_FLAGS_OPCODE_INVERS	1	/* an inverse query: IQUERY */
#define QUERY_FLAGS_OPCODE_STATUS	2	/* a server status request: STATUS */

#define QUERY_FLAGS_AA			0x0400	/* authoritative answer */
#define QUERY_FLAGS_TC			0x0200	/* truncation bit */
#define QUERY_FLAGS_RD			0x0100	/* recursion desired */
#define QUERY_FLAGS_RA			0x0080	/* recursion available */

#define QUERY_FLAGS_RCODE_SHIFT		0
#define QUERY_FLAGS_RCODE_MASK		0xf
#define QUERY_FLAGS_RCODE_NOERROR	0	/* no error response code */
#define QUERY_FLAGS_RCODE_FORMAT_ERROR	1	/* format error response code */
#define QUERY_FLAGS_RCODE_FAIL		2	/* server failure response code */
#define QUERY_FLAGS_RCODE_NAME_ERROR	3	/* name error response code */
#define QUERY_FLAGS_RCODE_NOT_IMPL	4	/* not implemented response code */
#define QUERY_FLAGS_RCODE_REFUSED	5	/* refused response code */

typedef unsigned char __u8;
typedef unsigned short __u16;
typedef unsigned int __u32;

static inline void query_set_flags_opcode(__u16 *flags, __u16 opcode)
{
	*flags |= (opcode & QUERY_FLAGS_OPCODE_MASK) << QUERY_FLAGS_OPCODE_SHIFT;
}

static inline void query_set_flags_rcode(__u16 *flags, __u16 rcode)
{
	*flags |= (rcode & QUERY_FLAGS_RCODE_MASK) << QUERY_FLAGS_RCODE_SHIFT;
}

struct query_header
{
	unsigned short		id;
	unsigned short		flags;
	unsigned short		question_num;
	unsigned short		answer_num;
	unsigned short		auth_num;
	unsigned short		addon_num;
};

#define QUERY_RR_NAME_MAX_SIZE	256

struct rr
{
	char			name[QUERY_RR_NAME_MAX_SIZE];
	int			namelen;

	__u16			type;
	__u16			class;
	__u32			ttl;
	__u16			rdlen;
	unsigned char		rdata[0];
};

enum query_class {
	QUERY_CLASS_IN = 1,	/* Internet */
	QUERY_CLASS_CS,		/* CSNET */
	QUERY_CLASS_CH,		/* CHAOS */
	QUERY_CLASS_HS,		/* Hesoid */
	QUERY_CLASS_ANY = 255,	/* any class */
};

enum query_type {
	QUERY_TYPE_A = 1,	/* a host address */
	QUERY_TYPE_NS,		/* an authoritative name server */
	QUERY_TYPE_MD,		/* a mail destination */
	QUERY_TYPE_MF,		/* a mail forwarder */
	QUERY_TYPE_CNAME,	/* the canonical name for the alias */
	QUERY_TYPE_SOA,		/* marks the start of a zone authority */
	QUERY_TYPE_MB,		/* a mailbox domain name */
	QUERY_TYPE_MG,		/* a mail group member */
	QUERY_TYPE_MR,		/* a mail rename domain name */
	QUERY_TYPE_NULL,	/* a null RR */
	QUERY_TYPE_WKS,		/* a well known service description */
	QUERY_TYPE_PTR,		/* a domain name pointer */
	QUERY_TYPE_HINFO,	/* host information */
	QUERY_TYPE_MINFO,	/* mailbox or mail list information */
	QUERY_TYPE_MX,		/* mail exchange */
	QUERY_TYPE_TXT,		/* text strings */
	QUERY_TYPE_AXFR = 252,	/* a request for a transfer of an entire zone */
	QUERY_TYPE_MAILB,	/* a request for mailbox-related records (MB, MG or MR) */
	QUERY_TYPE_ALL,		/* A request for all records */
};

void query_header_convert(struct query_header *h);
int query_fill_name(char *query, char *name);
void query_fill_header(struct query_header *h, __u16 id);
void *query_fill_question(void *q, char *query, __u16 type, __u16 class);
void query_parse_header(struct query_header *h);
int query_parse_name(void *message, char *nptr, char *dst, int *off);
struct rr *query_parse_rr(void *message, void *data, unsigned int *off);
int query_parse_question(void *message, void *data,
		char *name, __u16 *type, __u16 *class);
int query_parse_answer(void *data);

int query_add_rr_noname(void *data, struct rr *rr);
int query_add_rr(void *answer, struct rr *rr);

#endif /* __QUERY_H */
