#ifndef __ACL_RFC1035_INCLUDE_H__
#define __ACL_RFC1035_INCLUDE_H__

#ifdef __cplusplus
extern "C" {
#endif

#include "../stdlib/acl_define.h"
#include "../stdlib/acl_argv.h"

#ifdef	ACL_UNIX
#include <netinet/in.h>
#endif

/* RFC1035 - DNS */
#define ACL_RFC1035_MAXHOSTNAMESZ 256

typedef struct ACL_RFC1035_RR {
    char name[ACL_RFC1035_MAXHOSTNAMESZ];
    unsigned short type;
    unsigned short tclass;	/* class */
    unsigned int ttl;
    unsigned short rdlength;
    char *rdata;
} ACL_RFC1035_RR;

typedef struct ACL_RFC1035_QUERY {
    char name[ACL_RFC1035_MAXHOSTNAMESZ];
    unsigned short qtype;
    unsigned short qclass;
} ACL_RFC1035_QUERY;

typedef struct ACL_RFC1035_MESSAGE {
    unsigned short id;		/* A 16 bit identifier */
    unsigned int qr:1;		/* This message is a query (0), or a response (1) */
    unsigned int opcode:4;	/* Query kind:
 				 * 0: A standard query (QUERY)
 				 * 1: An inverse query (IQUERY)
 				 * 2: A server status request (STATUS)
 				 * 3-15: Reserved for future use
 				 */
    unsigned int aa:1;		/* Response: Authoritative Answer */
    unsigned int tc:1;		/* Response: Specifies that this message was truncated */
    unsigned int rd:1;		/* Query->Response: Recursion Desired, optional */
    unsigned int ra:1;		/* Response: Recursion Available  */
    unsigned int z:3;		/* Reserved for future use */
    unsigned int rcode:4;	/* Response code:
 				 * 0: No error condition
 				 * 1: Query format error
 				 * 2: Server failure
 				 * 3: Name Error, domain in the query not exist
 				 * 4: Not implement in server side
 				 * 5: Refused by the server
 				 */
    unsigned short qdcount;	/* the number of entries in the question section */
    unsigned short ancount;	/* the number of resource records in the answer section */
    unsigned short nscount;	/* the number of name server resource records in the authority records */
    unsigned short arcount;	/* the number of resource records in the additional records section */
    ACL_RFC1035_QUERY *query;
    ACL_RFC1035_RR    *answer;
    ACL_RFC1035_RR    *authority;
    ACL_RFC1035_RR    *additional;
} ACL_RFC1035_MESSAGE;

/**
 * Get error description with the specifed error number
 * @param errnum {int}
 * @return {const char*}
 */
ACL_API const char *acl_rfc1035_strerror(int errnum);

/**
 * Builds a message buffer with a QUESTION to lookup A records
 * for a hostname.  Caller must allocate 'buf' which should
 * probably be at least 512 octets.  The 'szp' initially
 * specifies the size of the buffer, on return it contains
 * the size of the message (i.e. how much to write).
 * @param hostname {const char*} the hostname to be resolved
 * @param buf {char*} the buffer for holding the query content
 * @param sz {size_t} the buffer's size
 * @param qid {unsigned short} the unique ID number for thie quering
 * @param query {ACL_RFC1035_QUERY*} if not null, it's qtype, qclass and
 * 	hostname will be set.
 * @return {size_t} return the size of the query in the buf, 0 return
 * 	if some error happened.
 */
ACL_API size_t acl_rfc1035_build_query4a(const char *hostname, char *buf,
		size_t sz, unsigned short qid, ACL_RFC1035_QUERY *query);

/**
 * Builds a message buffer with a QUESTION to lookup AAAA records
 * for a hostname.
 * @param hostname {const char*}
 * @param buf  {char*}
 * @param sz {size_t}
 * @param qid {unsigned short}
 * @param query {ACL_RFC1035_QUERY*}
 * @return {size_t}
 */
ACL_API size_t acl_rfc1035_build_query4aaaa(const char *hostname, char *buf,
		size_t sz, unsigned short qid, ACL_RFC1035_QUERY *query);

/**
 * Builds a message buffer with a QUESTION to lookup MX records
 * for a hostname.
 * @param hostname {const char*}
 * @param buf {char*}
 * @param sz {size_t}
 * @param qid {unsigned short}
 * @param query {ACL_RFC1035_QUERY*}
 * @return {size_t}
 */
ACL_API size_t acl_rfc1035_build_query4mx(const char *hostname, char *buf,
		size_t sz, unsigned short qid, ACL_RFC1035_QUERY *query);

/**
 * Builds a message buffer with a QUESTION to lookup PTR records
 * for an address.  Caller must allocate 'buf' which should
 * probably be at least 512 octets.  The 'szp' initially
 * specifies the size of the buffer, on return it contains
 * the size of the message (i.e. how much to write).
 * @param addr {const struct in_addr} the addr to resolve for name
 * @param buf {char*} hold the query package
 * @param sz {size_t} the buf's size at least 512 octets
 * @param qid {unsigned short} the query ID
 * @param query {ACL_RFC1035_QUERY*} if no null, it's some member variable
 * @return {size_t} Returns the size of the query in the buff
*/
ACL_API size_t acl_rfc1035_build_query4ptr(const struct in_addr addr, char *buf,
		size_t sz, unsigned short qid, ACL_RFC1035_QUERY *query);

/**
 * We're going to retry a former query, but we
 * just need a new ID for it.  Lucky for us ID
 * is the first field in the message buffer.
 * @param buf {char*} hold the result
 * @param sz {size_t} the buf's size
 * @param qid {unsigned short} the query ID
 */
ACL_API void acl_rfc1035_set_query_id(char *buf, size_t sz, unsigned short qid);

/**
 * Compares two RFC1035_QUERY entries
 * @param a {const ACL_RFC1035_QUERY *}
 * @param b {const ACL_RFC1035_QUERY *}
 * @return Returns 0 (equal) or !=0 (different)
 */
ACL_API  int acl_rfc1035_query_compare(const ACL_RFC1035_QUERY *a,
		const ACL_RFC1035_QUERY *b);

/**
 * Takes the contents of a DNS reply and fills in an array
 * of resource record structures.  The records array is allocated
 * here, and should be freed by calling acl_rfc1035_message_destroy().
 * @param buf {const char*} the data of the DNS reply
 * @param sz {size_t} the buf's size
 * @return {ACL_RFC1035_MESSAGE*} return the parsing result
 */
ACL_API ACL_RFC1035_MESSAGE *acl_rfc1035_response_unpack(const char *buf, size_t sz);

/**
 * Takes the contents of a DNS request and fills in an array
 * of resource record structures.  The records array is allocated
 * here, and should be freed by calling acl_rfc1035_message_destroy().
 * @param buf {const char*} the data of the DNS reply
 * @param sz {size_t} the buf's size
 * @return {ACL_RFC1035_MESSAGE*} return the parsing result
 */
ACL_API ACL_RFC1035_MESSAGE *acl_rfc1035_request_unpack(const char *buf, size_t sz);

/**
 * destroy and free the message created by RFC1035MessageUnpack()
 * @param message {ACL_RFC1035_MESSAGE*}
 */
ACL_API void acl_rfc1035_message_destroy(ACL_RFC1035_MESSAGE *message);

/**
 * Builds a response message for the query client.
 * @param hostname {const char*} the name to be resolved
 * @param ips {const ACL_ARGV*} the ip list of the hostname
 * @param domain_root {const char*}
 * @param dnsname  {const char*}
 * @param dnsip  {const char*}
 * @param qid {unsigned short}
 * @param buf {char*} hold the result
 * @param sz {size_t} the buf's size
 * @return {size_t} content size in buf
 */
ACL_API size_t acl_rfc1035_build_reply4a(const char *hostname, const ACL_ARGV *ips,
		const char *domain_root, const char *dnsname, const char *dnsip,
		unsigned short qid, char *buf, size_t sz);

ACL_API size_t acl_rfc1035_build_reply4aaaa(const char *hostname, const ACL_ARGV *ips,
		const char *domain_root, const char *dnsname, const char *dnsip,
		unsigned short qid, char *buf, size_t sz);

typedef struct ACL_RFC1035_REPLY {
	const char *hostname;
	const char *cname;
	const ACL_ARGV *ips;
	const char *domain_root;
	const char *dns_name;
	const char *dns_ip;
	int ip_type;		/* RFC1035_TYPE_A, RFC1035_TYPE_AAAA */
	int ttl;
	unsigned short qid;
} ACL_RFC1035_REPLY;

ACL_API size_t acl_rfc1035_build_reply(const ACL_RFC1035_REPLY *reply,
		char *buf, size_t sz);

#define ACL_RFC1035_TYPE_A	1	/* a host address */
#define ACL_RFC1035_TYPE_NS	2	/* an authoritative name server */
#define ACL_RFC1035_TYPE_MD	3	/* a mail destination (Obsolete - use MX) */
#define ACL_RFC1035_TYPE_MF	4	/* a mail forwarder (Obsolete - use MX) */
#define ACL_RFC1035_TYPE_CNAME	5	/* the canonical name for an alias */
#define ACL_RFC1035_TYPE_SOA	6	/* marks the start of a zone of authority */
#define ACL_RFC1035_TYPE_MB	7	/* a mailbox domain name (EXPERIMENTAL) */
#define ACL_RFC1035_TYPE_MG	8	/* a mail group member (EXPERIMENTAL) */
#define ACL_RFC1035_TYPE_MR	9	/* a mail rename domain name (EXPERIMENTAL) */
#define ACL_RFC1035_TYPE_NULL	10	/* a null RR (EXPERIMENTAL) */
#define ACL_RFC1035_TYPE_WKS	11	/* a well known service description */
#define ACL_RFC1035_TYPE_PTR	12	/* a domain name pointer */
#define ACL_RFC1035_TYPE_HINFO	13	/* host information */
#define ACL_RFC1035_TYPE_MINFO	14	/* mailbox or mail list information */
#define ACL_RFC1035_TYPE_MX	15	/* mail exchange */
#define ACL_RFC1035_TYPE_TXT	16	/* text strings */
#define ACL_RFC1035_TYPE_AAAA	28	/* a IPv6 address of host */
#define ACL_RFC1035_TYPE_AXFR	252	/* a request for a transfer of an entire zone */
#define ACL_RFC1035_TYPE_MAILB	253	/* a request for mailbox-related records (MB, MG or MR) */
#define ACL_RFC1035_TYPE_MAILA	253	/* a request for mail agent RRs (Obsolete - see MX) */
#define ACL_RFC1035_TYPE_ALL	255	/* a request for all records */

#define ACL_RFC1035_CLASS_IN	1	/* the Internet */
#define ACL_RFC1035_CLASS_CS	2	/* the CSNET class (Obsolete - used only for examples in some obsolete RFCs */
#define ACL_RFC1035_CLASS_CH	3	/* the CHAOS class */
#define ACL_RFC1035_CLASS_HS	4	/* Hesiod [Dyer 87] */

#ifdef __cplusplus
}
#endif

#endif
