/*---------------------------------------------------------------------------*/
/* dns.h                                                                     */
/* copyright (c) innovaphone 2015                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

#define DNS_DEFAULT_TIMEOUT 5000

// these defines here match the defines used in common/interface/dns.h from the firmware, otherwise the compiler complains
#define DNS_GETHOSTBYNAME_FLAG_ALL   0x80000000
#define DNS_GETHOSTBYNAME_NO_IPV6    0x00000002
#define DNS_GETHOSTBYNAME_ITERATE    0x00000008
#define DNS_GETHOSTBYNAME_ONLY_IPV6  0x00000010

typedef enum {
    DNS_QUERY_TYPE_NONE     = 0,   /* use getaddr_info instead of res_query */
    DNS_QUERY_TYPE_A        = 1,   /* host address */
    DNS_QUERY_TYPE_NS       = 2,   /* authoritative server */
    DNS_QUERY_TYPE_MD       = 3,   /* mail destination */
    DNS_QUERY_TYPE_MF       = 4,   /* mail forwarder */
    DNS_QUERY_TYPE_CNAME    = 5,   /* canonical name */
    DNS_QUERY_TYPE_SOA      = 6,   /* start of authority zone */
    DNS_QUERY_TYPE_MB       = 7,   /* mailbox domain name */
    DNS_QUERY_TYPE_MG       = 8,   /* mail group member */
    DNS_QUERY_TYPE_MR       = 9,   /* mail rename name */
    DNS_QUERY_TYPE_NULL     = 10,  /* null resource record */
    DNS_QUERY_TYPE_WKS      = 11,  /* well known service */
    DNS_QUERY_TYPE_PTR      = 12,  /* domain name pointer */
    DNS_QUERY_TYPE_HINFO    = 13,  /* host information */
    DNS_QUERY_TYPE_MINFO    = 14,  /* mailbox information */
    DNS_QUERY_TYPE_MX       = 15,  /* mail routing information */
    DNS_QUERY_TYPE_TXT      = 16,  /* text strings */
    DNS_QUERY_TYPE_RP       = 17,  /* responsible person */
    DNS_QUERY_TYPE_AFSDB    = 18,  /* AFS cell database */
    DNS_QUERY_TYPE_X25      = 19,  /* X_25 calling address */
    DNS_QUERY_TYPE_ISDN     = 20,  /* ISDN calling address */
    DNS_QUERY_TYPE_RT       = 21,  /* router */
    DNS_QUERY_TYPE_NSAP     = 22,  /* NSAP address */
    DNS_QUERY_TYPE_NSAP_PTR = 23,  /* reverse NSAP lookup (deprecated) */
    DNS_QUERY_TYPE_SIG      = 24,  /* security signature */
    DNS_QUERY_TYPE_KEY      = 25,  /* security key */
    DNS_QUERY_TYPE_PX       = 26,  /* X.400 mail mapping */
    DNS_QUERY_TYPE_GPOS     = 27,  /* geographical position (withdrawn) */
    DNS_QUERY_TYPE_AAAA     = 28,  /* IP6 Address */
    DNS_QUERY_TYPE_LOC      = 29,  /* Location Information */
    DNS_QUERY_TYPE_NXT      = 30,  /* Next Valid Name in Zone */
    DNS_QUERY_TYPE_EID      = 31,  /* Endpoint identifier */
    DNS_QUERY_TYPE_NIMLOC   = 32,  /* Nimrod locator */
    DNS_QUERY_TYPE_SRV      = 33,  /* Server selection */
    DNS_QUERY_TYPE_ATMA     = 34,  /* ATM Address */
    DNS_QUERY_TYPE_NAPTR    = 35,  /* Naming Authority PoinTeR */
    DNS_QUERY_TYPE_KX       = 36,  /* Key Exchanger */
    DNS_QUERY_TYPE_CERT     = 37,  /* CERT */
    DNS_QUERY_TYPE_A6       = 38,  /* A6 */
    DNS_QUERY_TYPE_DNAME    = 39,  /* DNAME */
    DNS_QUERY_TYPE_SINK     = 40,  /* SINK */
    DNS_QUERY_TYPE_OPT      = 41,  /* OPT pseudo-RR, RFC2671 */
    DNS_QUERY_TYPE_APL      = 42,  /* APL */
    DNS_QUERY_TYPE_DS       = 43,  /* Delegation Signer */
    DNS_QUERY_TYPE_SSHFP    = 44,  /* SSH Key Fingerprint */
    DNS_QUERY_TYPE_RRSIG    = 46,  /* RRSIG */
    DNS_QUERY_TYPE_NSEC     = 47,  /* NSEC */
    DNS_QUERY_TYPE_DNSKEY   = 48   /* DNSKEY */
} dns_query_type_sdk;

class IDns {
public:
    static IDns * Create(class IIoMux * const iomux);
    virtual ~IDns() {};
    virtual void GetHostByName(const char * hostName, class UDns * const user, class IInstanceLog * const log, dword timeout = DNS_DEFAULT_TIMEOUT, dword flags = 0, word servicePort = 0, const char * serviceName = NULL) = 0;
    virtual void UnsuitableAddress(const char * hostName, class IInstanceLog * const log, const char * addr, word servicePort = 0, const char * serviceName = NULL) = 0;
    virtual void Query(const char * hostName, class UDns * const user, class IInstanceLog * const log, dns_query_type_sdk queryType) = 0;
};

class UDns {
public:
    virtual void DnsGetHostByNameResult(const char * addr, bool isIPv6) {};
    virtual void DnsQueryResult(const byte * answer, unsigned length) {};   // note that you can parse the answer with IDnsQueryResult::Parse
};

template <class DnsReqContextSubclass> class UDnsSubclass {
public:
    virtual void DnsGetHostByNameResult(DnsReqContextSubclass * context, const char * addr, bool isIPv6) {};
    virtual void DnsQueryResult(DnsReqContextSubclass * context, const byte * answer, unsigned length) {};
};

template <class DnsReqContextSubclass> class DnsReqContext : public UDns {
    void DnsGetHostByNameResult(const char * addr, bool isIPv6) { dnsUser->DnsGetHostByNameResult(static_cast<DnsReqContextSubclass *>(this), addr, isIPv6); };
    void DnsQueryResult(const byte * answer, unsigned length) { dnsUser->DnsQueryResult(static_cast<DnsReqContextSubclass *>(this), answer, length); };
public:
    class UDnsSubclass<DnsReqContextSubclass> * dnsUser;
    DnsReqContext(class UDnsSubclass<DnsReqContextSubclass> * dnsUser) : dnsUser(dnsUser) {};
    virtual ~DnsReqContext() {};
};

/*
    the implementation of IDnsQueryResult::Parse and the IDnsQuery* classes is done inside sdk/common/lib-cpp/dns_query.cpp
*/

class IDnsQueryResult {
public:
    static class IDnsQueryResult * Parse(const byte * answer, unsigned length);

    virtual ~IDnsQueryResult() {};

    virtual size_t GetAnswerCount() = 0;
    virtual size_t GetAuthorityCount() = 0;
    virtual size_t GetAdditionalCount() = 0;
    virtual class IDnsQueryEntry * GetAnswer(size_t index) = 0;
    virtual class IDnsQueryEntry * GetAuthority(size_t index) = 0;
    virtual class IDnsQueryEntry * GetAdditional(size_t index) = 0;
};

class IDnsQueryEntry {
public:
    virtual ~IDnsQueryEntry() {};

    virtual const char * GetName() { return nullptr; }
    virtual dns_query_type_sdk GetType() { return DNS_QUERY_TYPE_NONE; }
    virtual dword GetTTL() { return 0; }
};

class IDnsQueryEntryA : public virtual IDnsQueryEntry {
public:
    virtual ~IDnsQueryEntryA() {};

    virtual const char * GetAddress() = 0;
};

class IDnsQueryEntryAAAA : public virtual IDnsQueryEntry {
public:
    virtual ~IDnsQueryEntryAAAA() {};

    virtual const char * GetAddress() = 0;
};

class IDnsQueryEntryCNAME : public virtual IDnsQueryEntry {
public:
    virtual ~IDnsQueryEntryCNAME() {};

    virtual const char * GetCanonicalName() = 0;
};

class IDnsQueryEntryMX : public virtual IDnsQueryEntry {
public:
    virtual ~IDnsQueryEntryMX() {};

    virtual word GetPreference() = 0;
    virtual const char * GetMailServer() = 0;
};

class IDnsQueryEntryNS : public virtual IDnsQueryEntry {
public:
    virtual ~IDnsQueryEntryNS() {};

    virtual const char * GetNameServer() = 0;
};

class IDnsQueryEntrySRV : public virtual IDnsQueryEntry {
public:
    virtual ~IDnsQueryEntrySRV() {};

    virtual word GetPriority() = 0;
    virtual word GetWeight() = 0;
    virtual word GetPort() = 0;
    virtual const char * GetTarget() = 0;
};

class IDnsQueryEntryTXT : public virtual IDnsQueryEntry {
public:
    virtual ~IDnsQueryEntryTXT() {};

    virtual size_t GetTextCount() = 0;
    virtual const char * GetTxt(size_t index) = 0;
};

class IDnsQueryEntrySOA : public virtual IDnsQueryEntry {
public:
    virtual ~IDnsQueryEntrySOA() {};

    virtual const char * GetPrimaryNS() = 0;
    virtual const char * GetResponsibleMailbox() = 0;
    virtual dword GetSerialNumber() = 0;
    virtual dword GetRefreshInterval() = 0;
    virtual dword GetRetryInterval() = 0;
    virtual dword GetExpireLimit() = 0;
    virtual dword GetMinimumTTL() = 0;
};

class IDnsQueryEntryPTR : public virtual IDnsQueryEntry {
public:
    virtual ~IDnsQueryEntryPTR() {};

    virtual const char * GetDomainName() = 0;
};

class IDnsQueryEntryNAPTR : public virtual IDnsQueryEntry {
public:
    virtual ~IDnsQueryEntryNAPTR() {};

    virtual word GetOrder() = 0;
    virtual word GetPreference() = 0;
    virtual const char * GetFlags() = 0;
    virtual const char * GetServices() = 0;
    virtual const char * GetRegexp() = 0;
    virtual const char * GetReplacement() = 0;
};
