test: Add initial unit tests for c-ares library
The tests are written in C++11, using the GoogleTest and GoogleMock
frameworks. They have their own independent autoconf setup, so that
users of the library need not have a C++ compiler just to get c-ares
working (however, the test/configure.ac file does assume the use of
a shared top-level m4/ directory). However, this autoconf setup has
only been tested on Linux and OSX so far.
Run with "./arestest", or "./arestest -v" to see extra debug info.
The GoogleTest options for running specific tests are also
available (e.g. "./arestest --gtest_filter=*Live*").
The tests are nowhere near complete yet (currently hitting around
60% coverage as reported by gcov), but they do include examples
of a few different styles of testing:
- There are live tests (ares-test-live.cc), which assume that the
current machine has a valid DNS setup and connection to the
internet; these tests issue queries for real domains but don't
particularly check what gets returned. The tests will fail on
an offline machine.
- There a few mock tests (ares-test-mock.cc) that set up a fake DNS
server and inject its port into the c-ares library configuration.
These tests allow specific response messages to be crafted and
injected, and so are likely to be used for many more tests in
future.
- To make this generation/injection easier, the dns-proto.h file
includes C++ helper classes for building DNS packets.
- Other library entrypoints that don't require network activity
(e.g. ares_parse_*_reply) are tested directly.
- There are few tests of library-internal functions that are not
normally visible to API users (in ares-test-internal.cc).
- A couple of the tests use a helper method of the test fixture to
inject memory allocation failures, using the earlier change to the
library to allow override of malloc/realloc/free.
- There is also an entrypoint to allow Clang's libfuzzer to drive
the packet parsing code in ares_parse_*_reply, together with a
standalone wrapper for it (./aresfuzz) to allow use of afl-fuzz
for further fuzz testing.
9 years ago
|
|
|
// -*- mode: c++ -*-
|
|
|
|
#ifndef DNS_PROTO_H
|
|
|
|
#define DNS_PROTO_H
|
|
|
|
// Utilities for processing DNS packet contents
|
|
|
|
|
|
|
|
// Include ares internal file for DNS protocol constants
|
|
|
|
#include "nameser.h"
|
|
|
|
|
|
|
|
#include <memory>
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
namespace ares {
|
|
|
|
|
|
|
|
typedef unsigned char byte;
|
|
|
|
|
|
|
|
std::string HexDump(std::vector<byte> data);
|
|
|
|
std::string HexDump(const byte *data, int len);
|
|
|
|
std::string HexDump(const char *data, int len);
|
|
|
|
|
|
|
|
std::string StatusToString(int status);
|
|
|
|
std::string RcodeToString(int rcode);
|
|
|
|
std::string RRTypeToString(int rrtype);
|
|
|
|
std::string ClassToString(int qclass);
|
|
|
|
std::string AddressToString(const void* addr, int len);
|
test: Add initial unit tests for c-ares library
The tests are written in C++11, using the GoogleTest and GoogleMock
frameworks. They have their own independent autoconf setup, so that
users of the library need not have a C++ compiler just to get c-ares
working (however, the test/configure.ac file does assume the use of
a shared top-level m4/ directory). However, this autoconf setup has
only been tested on Linux and OSX so far.
Run with "./arestest", or "./arestest -v" to see extra debug info.
The GoogleTest options for running specific tests are also
available (e.g. "./arestest --gtest_filter=*Live*").
The tests are nowhere near complete yet (currently hitting around
60% coverage as reported by gcov), but they do include examples
of a few different styles of testing:
- There are live tests (ares-test-live.cc), which assume that the
current machine has a valid DNS setup and connection to the
internet; these tests issue queries for real domains but don't
particularly check what gets returned. The tests will fail on
an offline machine.
- There a few mock tests (ares-test-mock.cc) that set up a fake DNS
server and inject its port into the c-ares library configuration.
These tests allow specific response messages to be crafted and
injected, and so are likely to be used for many more tests in
future.
- To make this generation/injection easier, the dns-proto.h file
includes C++ helper classes for building DNS packets.
- Other library entrypoints that don't require network activity
(e.g. ares_parse_*_reply) are tested directly.
- There are few tests of library-internal functions that are not
normally visible to API users (in ares-test-internal.cc).
- A couple of the tests use a helper method of the test fixture to
inject memory allocation failures, using the earlier change to the
library to allow override of malloc/realloc/free.
- There is also an entrypoint to allow Clang's libfuzzer to drive
the packet parsing code in ares_parse_*_reply, together with a
standalone wrapper for it (./aresfuzz) to allow use of afl-fuzz
for further fuzz testing.
9 years ago
|
|
|
|
|
|
|
// Convert DNS protocol data to strings.
|
|
|
|
// Note that these functions are not defensive; they assume
|
|
|
|
// a validly formatted input, and so should not be used on
|
|
|
|
// externally-determined inputs.
|
|
|
|
std::string PacketToString(const std::vector<byte>& packet);
|
|
|
|
std::string QuestionToString(const std::vector<byte>& packet,
|
|
|
|
const byte** data, int* len);
|
|
|
|
std::string RRToString(const std::vector<byte>& packet,
|
|
|
|
const byte** data, int* len);
|
|
|
|
|
|
|
|
struct DNSQuestion {
|
|
|
|
DNSQuestion(const std::string& name, ns_type rrtype, ns_class qclass)
|
|
|
|
: name_(name), rrtype_(rrtype), qclass_(qclass) {}
|
|
|
|
DNSQuestion(const std::string& name, ns_type rrtype)
|
|
|
|
: name_(name), rrtype_(rrtype), qclass_(ns_c_in) {}
|
|
|
|
virtual std::vector<byte> data() const;
|
|
|
|
std::string name_;
|
|
|
|
ns_type rrtype_;
|
|
|
|
ns_class qclass_;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DNSRR : public DNSQuestion {
|
|
|
|
DNSRR(const std::string& name, ns_type rrtype, ns_class qclass, int ttl)
|
|
|
|
: DNSQuestion(name, rrtype, qclass), ttl_(ttl) {}
|
|
|
|
DNSRR(const std::string& name, ns_type rrtype, int ttl)
|
|
|
|
: DNSQuestion(name, rrtype), ttl_(ttl) {}
|
|
|
|
virtual std::vector<byte> data() const = 0;
|
|
|
|
int ttl_;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DNSAddressRR : public DNSRR {
|
|
|
|
DNSAddressRR(const std::string& name, ns_type rrtype, int ttl,
|
|
|
|
const byte* addr, int addrlen)
|
|
|
|
: DNSRR(name, rrtype, ttl), addr_(addr, addr + addrlen) {}
|
|
|
|
DNSAddressRR(const std::string& name, ns_type rrtype, int ttl,
|
|
|
|
const std::vector<byte>& addr)
|
|
|
|
: DNSRR(name, rrtype, ttl), addr_(addr) {}
|
|
|
|
virtual std::vector<byte> data() const;
|
|
|
|
std::vector<byte> addr_;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DNSARR : public DNSAddressRR {
|
|
|
|
DNSARR(const std::string& name, int ttl, const byte* addr, int addrlen)
|
|
|
|
: DNSAddressRR(name, ns_t_a, ttl, addr, addrlen) {}
|
|
|
|
DNSARR(const std::string& name, int ttl, const std::vector<byte>& addr)
|
|
|
|
: DNSAddressRR(name, ns_t_a, ttl, addr) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DNSAaaaRR : public DNSAddressRR {
|
|
|
|
DNSAaaaRR(const std::string& name, int ttl, const byte* addr, int addrlen)
|
|
|
|
: DNSAddressRR(name, ns_t_aaaa, ttl, addr, addrlen) {}
|
|
|
|
DNSAaaaRR(const std::string& name, int ttl, const std::vector<byte>& addr)
|
|
|
|
: DNSAddressRR(name, ns_t_aaaa, ttl, addr) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DNSSingleNameRR : public DNSRR {
|
|
|
|
DNSSingleNameRR(const std::string& name, ns_type rrtype, int ttl,
|
|
|
|
const std::string& other)
|
|
|
|
: DNSRR(name, rrtype, ttl), other_(other) {}
|
|
|
|
virtual std::vector<byte> data() const;
|
|
|
|
std::string other_;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DNSCnameRR : public DNSSingleNameRR {
|
|
|
|
DNSCnameRR(const std::string& name, int ttl, const std::string& other)
|
|
|
|
: DNSSingleNameRR(name, ns_t_cname, ttl, other) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DNSNsRR : public DNSSingleNameRR {
|
|
|
|
DNSNsRR(const std::string& name, int ttl, const std::string& other)
|
|
|
|
: DNSSingleNameRR(name, ns_t_ns, ttl, other) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DNSPtrRR : public DNSSingleNameRR {
|
|
|
|
DNSPtrRR(const std::string& name, int ttl, const std::string& other)
|
|
|
|
: DNSSingleNameRR(name, ns_t_ptr, ttl, other) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DNSTxtRR : public DNSRR {
|
|
|
|
DNSTxtRR(const std::string& name, int ttl, const std::vector<std::string>& txt)
|
|
|
|
: DNSRR(name, ns_t_txt, ttl), txt_(txt) {}
|
|
|
|
virtual std::vector<byte> data() const;
|
|
|
|
std::vector<std::string> txt_;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DNSMxRR : public DNSRR {
|
|
|
|
DNSMxRR(const std::string& name, int ttl, int pref, const std::string& other)
|
|
|
|
: DNSRR(name, ns_t_mx, ttl), pref_(pref), other_(other) {}
|
|
|
|
virtual std::vector<byte> data() const;
|
|
|
|
int pref_;
|
|
|
|
std::string other_;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DNSSrvRR : public DNSRR {
|
|
|
|
DNSSrvRR(const std::string& name, int ttl,
|
|
|
|
int prio, int weight, int port, const std::string& target)
|
|
|
|
: DNSRR(name, ns_t_srv, ttl), prio_(prio), weight_(weight), port_(port), target_(target) {}
|
|
|
|
virtual std::vector<byte> data() const;
|
|
|
|
int prio_;
|
|
|
|
int weight_;
|
|
|
|
int port_;
|
|
|
|
std::string target_;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DNSSoaRR : public DNSRR {
|
|
|
|
DNSSoaRR(const std::string& name, int ttl,
|
|
|
|
const std::string& nsname, const std::string& rname,
|
|
|
|
int serial, int refresh, int retry, int expire, int minimum)
|
|
|
|
: DNSRR(name, ns_t_soa, ttl), nsname_(nsname), rname_(rname),
|
|
|
|
serial_(serial), refresh_(refresh), retry_(retry),
|
|
|
|
expire_(expire), minimum_(minimum) {}
|
|
|
|
virtual std::vector<byte> data() const;
|
|
|
|
std::string nsname_;
|
|
|
|
std::string rname_;
|
|
|
|
int serial_;
|
|
|
|
int refresh_;
|
|
|
|
int retry_;
|
|
|
|
int expire_;
|
|
|
|
int minimum_;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DNSNaptrRR : public DNSRR {
|
|
|
|
DNSNaptrRR(const std::string& name, int ttl,
|
|
|
|
int order, int pref,
|
|
|
|
const std::string& flags,
|
|
|
|
const std::string& service,
|
|
|
|
const std::string& regexp,
|
|
|
|
const std::string& replacement)
|
|
|
|
: DNSRR(name, ns_t_naptr, ttl), order_(order), pref_(pref),
|
|
|
|
flags_(flags), service_(service), regexp_(regexp), replacement_(replacement) {}
|
|
|
|
virtual std::vector<byte> data() const;
|
|
|
|
int order_;
|
|
|
|
int pref_;
|
|
|
|
std::string flags_;
|
|
|
|
std::string service_;
|
|
|
|
std::string regexp_;
|
|
|
|
std::string replacement_;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DNSOption {
|
|
|
|
int code_;
|
|
|
|
std::vector<byte> data_;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DNSOptRR : public DNSRR {
|
|
|
|
DNSOptRR(int extrcode, int udpsize)
|
|
|
|
: DNSRR("", ns_t_opt, static_cast<ns_class>(udpsize), extrcode) {}
|
|
|
|
virtual std::vector<byte> data() const;
|
|
|
|
std::vector<DNSOption> opts_;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DNSPacket {
|
|
|
|
DNSPacket()
|
|
|
|
: qid_(0), response_(false), opcode_(ns_o_query),
|
|
|
|
aa_(false), tc_(false), rd_(false), ra_(false),
|
|
|
|
z_(false), ad_(false), cd_(false), rcode_(ns_r_noerror) {}
|
|
|
|
// Convenience functions that take ownership of given pointers.
|
|
|
|
DNSPacket& add_question(DNSQuestion *q) {
|
|
|
|
questions_.push_back(std::unique_ptr<DNSQuestion>(q));
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
DNSPacket& add_answer(DNSRR *q) {
|
|
|
|
answers_.push_back(std::unique_ptr<DNSRR>(q));
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
DNSPacket& add_auth(DNSRR *q) {
|
|
|
|
auths_.push_back(std::unique_ptr<DNSRR>(q));
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
DNSPacket& add_additional(DNSRR *q) {
|
|
|
|
adds_.push_back(std::unique_ptr<DNSRR>(q));
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
// Chainable setters.
|
|
|
|
DNSPacket& set_qid(int qid) { qid_ = qid; return *this; }
|
|
|
|
DNSPacket& set_response(bool v = true) { response_ = v; return *this; }
|
|
|
|
DNSPacket& set_aa(bool v = true) { aa_ = v; return *this; }
|
|
|
|
DNSPacket& set_tc(bool v = true) { tc_ = v; return *this; }
|
|
|
|
DNSPacket& set_rd(bool v = true) { rd_ = v; return *this; }
|
|
|
|
DNSPacket& set_ra(bool v = true) { ra_ = v; return *this; }
|
|
|
|
DNSPacket& set_z(bool v = true) { z_ = v; return *this; }
|
|
|
|
DNSPacket& set_ad(bool v = true) { ad_ = v; return *this; }
|
|
|
|
DNSPacket& set_cd(bool v = true) { cd_ = v; return *this; }
|
|
|
|
DNSPacket& set_rcode(ns_rcode rcode) { rcode_ = rcode; return *this; }
|
|
|
|
|
|
|
|
// Return the encoded packet.
|
|
|
|
std::vector<byte> data() const;
|
|
|
|
|
|
|
|
int qid_;
|
|
|
|
bool response_;
|
|
|
|
ns_opcode opcode_;
|
|
|
|
bool aa_;
|
|
|
|
bool tc_;
|
|
|
|
bool rd_;
|
|
|
|
bool ra_;
|
|
|
|
bool z_;
|
|
|
|
bool ad_;
|
|
|
|
bool cd_;
|
|
|
|
ns_rcode rcode_;
|
|
|
|
std::vector<std::unique_ptr<DNSQuestion>> questions_;
|
|
|
|
std::vector<std::unique_ptr<DNSRR>> answers_;
|
|
|
|
std::vector<std::unique_ptr<DNSRR>> auths_;
|
|
|
|
std::vector<std::unique_ptr<DNSRR>> adds_;
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace ares
|
|
|
|
|
|
|
|
#endif
|