// $Id: DNS.h,v 1.11 2002/10/11 02:20:15 leres Exp $
//
// Copyright (c) 2001, 2002
//      The Regents of the University of California.  All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that: (1) source code distributions
// retain the above copyright notice and this paragraph in its entirety, (2)
// distributions including binary code include the above copyright notice and
// this paragraph in its entirety in the documentation or other materials
// provided with the distribution, and (3) all advertising materials mentioning
// features or use of this software display the following acknowledgement:
// ``This product includes software developed by the University of California,
// Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
// the University nor the names of its contributors may be used to endorse
// or promote products derived from this software without specific prior
// written permission.
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

#ifndef dns_h
#define dns_h

#include "UDP.h"
#include "TCP.h"


typedef enum {
	DNS_OP_QUERY = 0,
	DNS_OP_IQUERY = 1,
	DNS_OP_SERVER_STATUS = 3,
} DNS_Opcode;

typedef enum {
	DNS_CODE_OK = 0,
	DNS_CODE_FORMAT_ERR = 1,
	DNS_CODE_SERVER_FAIL = 2,
	DNS_CODE_NAME_ERR = 3,
	DNS_CODE_NOT_IMPL = 4,
	DNS_CODE_REFUSED = 5,
} DNS_Code;

typedef enum {
	TYPE_A = 1, TYPE_NS = 2, TYPE_CNAME = 5, TYPE_SOA = 6,
	TYPE_WKS = 11, TYPE_PTR = 12, TYPE_HINFO = 13, TYPE_MX = 15,
	TYPE_TXT = 16, TYPE_SRV = 33,
	TYPE_EDNS0 = 41,	// RFC 2671

	// The following are only valid in queries.
	TYPE_AXFR = 252,
	TYPE_ALL = 255,
} RR_Type;

#define DNS_CLASS_IN 1
#define DNS_CLASS_ANY 255

typedef enum {
	DNS_QUESTION, DNS_ANSWER, DNS_AUTHORITY, DNS_ADDITIONAL,
} DNS_AnswerType;


struct DNS_RawMsgHdr {
	unsigned short id;
	unsigned short flags;
	unsigned short qdcount;
	unsigned short ancount;
	unsigned short nscount;
	unsigned short arcount;
};

class DNS_MsgInfo {
public:
	DNS_MsgInfo(DNS_RawMsgHdr* hdr, int is_query);
	~DNS_MsgInfo();

	Val* BuildHdrVal();
	Val* BuildAnswerVal();

	int id;
	int opcode;
	int rcode;
	int QR, AA, TC, RD, RA, Z;
	int qdcount, ancount, nscount, arcount;
	int is_query;	// whether it came from the session initiator

	StringVal* query_name;
	RR_Type atype;
	int aclass;
	int ttl;

	DNS_AnswerType answer_type;
	int skip_event;	// if true, don't generate corresponding events
};


class DNS_Interpreter {
public:
	DNS_Interpreter(Connection* conn);

	int ParseMessage(const u_char* data, int len, int is_query);

	void Timeout()	{ }

	Connection* Conn() const		{ return conn; }
	TCP_Connection* TCP_Conn() const	{ return (TCP_Connection*) conn; }

protected:
	int ParseQuestions(DNS_MsgInfo* msg,
				const u_char*& data, int& len,
				const u_char* start);
	int ParseAnswers(DNS_MsgInfo* msg, int n, DNS_AnswerType answer_type,
				const u_char*& data, int& len,
				const u_char* start);

	int ParseQuestion(DNS_MsgInfo* msg,
			const u_char*& data, int& len, const u_char* start);
	int ParseAnswer(DNS_MsgInfo* msg,
			const u_char*& data, int& len, const u_char* start);

	u_char* ExtractName(const u_char*& data, int& len,
				u_char* label, int label_len,
				const u_char* msg_start);
	int ExtractLabel(const u_char*& data, int& len,
			 u_char*& label, int& label_len,
			 const u_char* msg_start);

	int ExtractShort(const u_char*& data, int& len);
	int ExtractLong(const u_char*& data, int& len);

	int ParseRR_Name(DNS_MsgInfo* msg,
				const u_char*& data, int& len, int rdlength,
				const u_char* msg_start);
	int ParseRR_SOA(DNS_MsgInfo* msg,
				const u_char*& data, int& len, int rdlength,
				const u_char* msg_start);
	int ParseRR_MX(DNS_MsgInfo* msg,
				const u_char*& data, int& len, int rdlength,
				const u_char* msg_start);
	int ParseRR_SRV(DNS_MsgInfo* msg,
				const u_char*& data, int& len, int rdlength,
				const u_char* msg_start);
	int ParseRR_EDNS0(DNS_MsgInfo* msg,
				const u_char*& data, int& len, int rdlength,
				const u_char* msg_start);
	int ParseRR_A(DNS_MsgInfo* msg,
				const u_char*& data, int& len, int rdlength);
	int ParseRR_WKS(DNS_MsgInfo* msg,
				const u_char*& data, int& len, int rdlength);
	int ParseRR_HINFO(DNS_MsgInfo* msg,
				const u_char*& data, int& len, int rdlength);
	int ParseRR_TXT(DNS_MsgInfo* msg,
				const u_char*& data, int& len, int rdlength);

	Connection* conn;
};


typedef enum {
	DNS_LEN_HI,	// looking for the high-order byte of the length
	DNS_LEN_LO,	// looking for the low-order byte of the length
	DNS_MESSAGE_BUFFER,	// building up the message in the buffer
} TCP_DNS_state;

// ### This should be merged with TCP_Endpoint_RPC.
class TCP_Endpoint_DNS : public TCP_EndpointContents {
public:
	TCP_Endpoint_DNS(DNS_Interpreter* interp, int is_orig);
	~TCP_Endpoint_DNS();

	void Flush();	// process any partially-received data

	TCP_DNS_state State() const		{ return state; }

protected:
	void Deliver(double t, int seq, int len, u_char* data);

	DNS_Interpreter* interp;

	u_char* msg_buf;
	int buf_n;	// number of bytes in msg_buf
	int buf_len;	// size of msg_buf
	int msg_size;	// expected size of message

	TCP_DNS_state state;
	int is_orig;
};

class TCP_DNS : public TCP_Connection {
public:
	TCP_DNS(NetSessions* s, HashKey* k, double t, const ConnID* id,
			const struct tcphdr* tp);
	~TCP_DNS();
	void Done();

protected:
	void BuildEndpoints();

	void ConnectionClosed(TCP_Endpoint* endpoint,
				TCP_Endpoint* peer, int gen_event);

	DNS_Interpreter* interp;
};

class UDP_DNS : public UDP_Connection {
public:
	UDP_DNS(NetSessions* s, HashKey* k, double t, const ConnID* id,
		const struct udphdr* up);
	~UDP_DNS();
	void Done();

protected:
	int Request(double t, const u_char* data, int len);
	int Reply(double t, const u_char* data, int len);

	void ExpireTimer(double t);

	int IsReuse(double t, const u_char* pkt);

	DNS_Interpreter* interp;
	int did_session_done;
};

#endif
