/***************************************************************************
 * nmap_dns.h -- Handles parallel reverse DNS resolution for target IPs    *
 *                                                                         *
 ***********************IMPORTANT NMAP LICENSE TERMS************************
 *
 * The Nmap Security Scanner is (C) 1996-2026 Nmap Software LLC ("The Nmap
 * Project"). Nmap is also a registered trademark of the Nmap Project.
 *
 * This program is distributed under the terms of the Nmap Public Source
 * License (NPSL). The exact license text applying to a particular Nmap
 * release or source code control revision is contained in the LICENSE
 * file distributed with that version of Nmap or source code control
 * revision. More Nmap copyright/legal information is available from
 * https://nmap.org/book/man-legal.html, and further information on the
 * NPSL license itself can be found at https://nmap.org/npsl/ . This
 * header summarizes some key points from the Nmap license, but is no
 * substitute for the actual license text.
 *
 * Nmap is generally free for end users to download and use themselves,
 * including commercial use. It is available from https://nmap.org.
 *
 * The Nmap license generally prohibits companies from using and
 * redistributing Nmap in commercial products, but we sell a special Nmap
 * OEM Edition with a more permissive license and special features for
 * this purpose. See https://nmap.org/oem/
 *
 * If you have received a written Nmap license agreement or contract
 * stating terms other than these (such as an Nmap OEM license), you may
 * choose to use and redistribute Nmap under those terms instead.
 *
 * The official Nmap Windows builds include the Npcap software
 * (https://npcap.com) for packet capture and transmission. It is under
 * separate license terms which forbid redistribution without special
 * permission. So the official Nmap Windows builds may not be redistributed
 * without special permission (such as an Nmap OEM license).
 *
 * Source is provided to this software because we believe users have a
 * right to know exactly what a program is going to do before they run it.
 * This also allows you to audit the software for security holes.
 *
 * Source code also allows you to port Nmap to new platforms, fix bugs, and
 * add new features. You are highly encouraged to submit your changes as a
 * Github PR or by email to the dev@nmap.org mailing list for possible
 * incorporation into the main distribution. Unless you specify otherwise, it
 * is understood that you are offering us very broad rights to use your
 * submissions as described in the Nmap Public Source License Contributor
 * Agreement. This is important because we fund the project by selling licenses
 * with various terms, and also because the inability to relicense code has
 * caused devastating problems for other Free Software projects (such as KDE
 * and NASM).
 *
 * The free version of Nmap 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. Warranties,
 * indemnification and commercial support are all available through the
 * Npcap OEM program--see https://nmap.org/oem/
 *
 ***************************************************************************/

#ifndef NETUTIL_MASSDNS_H
#define NETUTIL_MASSDNS_H

#include <nbase.h>
#include <nsock.h>

#include <string>
#include <list>
#include <vector>

#define DNS_LABEL_MAX_LENGTH 63
#define DNS_NAME_MAX_LENGTH 255

namespace DNS
{

typedef enum
{
  ID = 0,
  FLAGS_OFFSET = 2,
  QDCOUNT = 4,
  ANCOUNT = 6,
  NSCOUNT = 8,
  ARCOUNT = 10,
  DATA = 12
} HEADER_OFFSET;

typedef enum {
  ERR_ALL = 0x0007,
  CHECKING_DISABLED = 0x0010,
  AUTHENTICATED_DATA = 0x0020,
  ZERO = 0x0070,
  RECURSION_AVAILABLE = 0x0080,
  RECURSION_DESIRED = 0x0100,
  TRUNCATED = 0x0200,
  AUTHORITATIVE_ANSWER = 0x0400,
  OP_STANDARD_QUERY = 0x0000,
  OP_INVERSE_QUERY = 0x0800, // Obsoleted in RFC 3425
  OP_SERVER_STATUS = 0x1000,
  RESPONSE = 0x8000
} FLAGS;

typedef enum {
  ERR_NO = 0x0000,
  ERR_FORMAT = 0x0001,
  ERR_SERVFAIL = 0x0002,
  ERR_NAME = 0x0003,
  ERR_NOT_IMPLEMENTED = 0x0004,
  ERR_REFUSED = 0x0005,
} ERRORS;

typedef enum {
  NONE = 0,
  A = 1,
  CNAME = 5,
  PTR = 12,
  AAAA = 28,
  ANY = 255, // Internally defined as "A and AAAA"
} RECORD_TYPE;

typedef enum {
  CLASS_IN = 1
} RECORD_CLASS;

const u8 COMPRESSED_NAME = 0xc0;

#define C_IPV4_PTR_DOMAIN ".in-addr.arpa"
#define C_IPV6_PTR_DOMAIN ".ip6.arpa"
const std::string IPV4_PTR_DOMAIN = C_IPV4_PTR_DOMAIN;
const std::string IPV6_PTR_DOMAIN = C_IPV6_PTR_DOMAIN;

class Factory
{
public:
  static u16 progressiveId;
  static bool ipToPtr(const sockaddr_storage &ip, std::string &ptr);
  static bool ptrToIp(const std::string &ptr, sockaddr_storage &ip);
  static size_t buildSimpleRequest(u16 id, const std::string &name, RECORD_TYPE rt, u8 *buf, size_t maxlen);
  static size_t buildReverseRequest(u16 id, const sockaddr_storage &ip, u8 *buf, size_t maxlen);
  static size_t putUnsignedShort(u16 num, u8 *buf, size_t offset, size_t maxlen);
  static size_t putDomainName(const std::string &name, u8 *buf, size_t offset, size_t maxlen);
  static size_t parseUnsignedShort(u16 &num, const u8 *buf, size_t offset, size_t maxlen);
  static size_t parseUnsignedInt(u32 &num, const u8 *buf, size_t offset, size_t maxlen);
  static size_t parseIPv4(struct in_addr &addr, const u8 *buf, size_t offset, size_t maxlen);
  static size_t parseIPv6(struct in6_addr &addr, const u8 *buf, size_t offset, size_t maxlen);
  static size_t parseDomainName(std::string &name, const u8 *buf, size_t offset, size_t maxlen);
};

class Record
{
public:
  virtual Record * clone() = 0;
  virtual ~Record() {}
  virtual size_t parseFromBuffer(const u8 *buf, size_t offset, size_t maxlen, RECORD_TYPE rt) = 0;
};

class A_Record : public Record
{
public:
  sockaddr_storage value;
  A_Record() : value() {}
  Record * clone() { return new A_Record(*this); }
  ~A_Record() {}
  size_t parseFromBuffer(const u8 *buf, size_t offset, size_t maxlen, RECORD_TYPE rt);
};

class PTR_Record : public Record
{
public:
  std::string value;
  Record * clone() { return new PTR_Record(*this); }
  ~PTR_Record() {}
  size_t parseFromBuffer(const u8 *buf, size_t offset, size_t maxlen, RECORD_TYPE rt)
  {
    return Factory::parseDomainName(value, buf, offset, maxlen);
  }
};

class CNAME_Record : public Record
{
public:
  std::string value;
  Record * clone() { return new CNAME_Record(*this); }
  ~CNAME_Record() {}
  size_t parseFromBuffer(const u8 *buf, size_t offset, size_t maxlen, RECORD_TYPE rt)
  {
    return Factory::parseDomainName(value, buf, offset, maxlen);
  }
};

class Query
{
public:
  Query() : record_type(NONE), record_class(CLASS_IN) {}
  std::string name;
  u16 record_type;
  u16 record_class;

  size_t parseFromBuffer(const u8 *buf, size_t offset, size_t maxlen);
};

class Answer
{
public:
  Answer() : record_type(NONE), record_class(CLASS_IN), ttl(0), length(0),
    record(NULL) {}
  Answer(const Answer &c) : name(c.name), record_type(c.record_type),
    record_class(c.record_class), ttl(c.ttl), length(c.length),
    record(c.record->clone()) {}
  ~Answer() { delete record; }

  std::string name;
  u16 record_type;
  u16 record_class;
  u32 ttl;
  u16 length;
  Record * record;

  // Populate the object reading from buffer and returns "consumed" bytes
  size_t parseFromBuffer(const u8 *buf, size_t offset, size_t maxlen);
  Answer& operator=(const Answer &r);
};

class Packet
{
public:
  Packet() : id(0), flags(0) {}
  ~Packet() {}

  void addFlags(FLAGS fl){ flags |= fl; }
  void removeFlags(FLAGS fl){ flags &= ~fl; }
  void resetFlags() { flags = 0; }
  //size_t writeToBuffer(u8 *buf, size_t maxlen);
  size_t parseFromBuffer(const u8 *buf, size_t maxlen);

  u16 id;
  u16 flags;
  std::list<Query> queries;
  std::list<Answer> answers;
};

struct Request
{
  RECORD_TYPE type;
  std::vector<struct sockaddr_storage> ssv;
  std::string name;
  void *userdata;
  Request() : type(NONE), ssv(), name(), userdata(NULL) {}
  const char *repr() const; // string representation
};

struct Stats
{
  Stats() : servers(0), names(0), actual(0), ok(0), nx(0), sf(0), trans(0), dropped(0), system(0) {}

  int servers; // Number of servers used
  int names;   // Requests we attempt to resolve
  int actual;  // actual DNS queries we intend to make
  int ok;      // queries answered successfully
  int nx;      // NXDOMAIN (or failed system resolution)
  int sf;      // SERVFAIL
  int trans;   // queries transmitted
  int dropped; // queries dropped after failing with all servers
  int system;  // Requests deferred to system name resolution

  void reset() {
    servers = names = actual = ok = nx = sf = trans = dropped = system = 0;
  }
  int statDone() const {
    return ok + nx + sf + dropped;
  }
  double getCompletion() const {
    return (double) statDone() / actual;
  }
};

class ResolverImpl;
class Resolver
{
public:
  Resolver();

  void Init(Request *requests, int num_requests);
  void Resolve(bool system=false);

  void setAF(int af);
  void setStatusCallback(void (*callback)(const Stats *));
  void setLogFunc(void (*log_func)(int lvl, const char *, ...));
  void setSource(const char *device, const struct sockaddr_storage *src, size_t srclen, bool spoof);
  void setIpOptions(const u8 *opts, size_t optslen);
  void setProxyChain(const nsock_proxychain proxy_chain);
  void setServers(const char *servers);

  bool isMassDnsOK(const char **err) const;
  bool isSystemDnsOK(const char **err) const;
  std::list<std::string> getServers() const;
  Stats getStats() const;
  double getCompletion() const;

private:
  ResolverImpl *impl;
};
}

#endif
