summaryrefslogtreecommitdiff
path: root/include/dns.h
blob: 3102ec72c21e7b4601754a013dc582de595abfc9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/*
 *
 * (C) 2003-2012 Anope Team
 * Contact us at team@anope.org
 *
 * Please read COPYING and README for further details.
 *
 * Based on the original code of Epona by Lara.
 * Based on the original code of Services by Andy Church.
 *
 *
 */

#ifndef DNS_H
#define DNS_H

#include "sockets.h"
#include "timers.h"
#include "extern.h"
#include "config.h"

/** Valid query types
 */
enum QueryType
{
	/* Nothing */
	DNS_QUERY_NONE,
	/* A simple A lookup */
	DNS_QUERY_A = 1,
	/* A CNAME lookup */
	DNS_QUERY_CNAME = 5,
	/* Reverse DNS lookup */
	DNS_QUERY_PTR = 12,
	/* IPv6 AAAA lookup */
	DNS_QUERY_AAAA = 28
};

/** Flags that can be AND'd into DNSPacket::flags to receive certain values
 */
enum
{
	DNS_QUERYFLAGS_QR = 0x8000,
	DNS_QUERYFLAGS_OPCODE = 0x7800,
	DNS_QUERYFLAGS_AA = 0x400,
	DBS_QUERYFLAGS_TC = 0x200,
	DNS_QUERYFLAGS_RD = 0x100,
	DNS_QUERYFLAGS_RA = 0x80,
	DNS_QUERYFLAGS_Z = 0x70,
	DNS_QUERYFLAGS_RCODE = 0xF
};

enum DNSError
{
	DNS_ERROR_NONE,
	DNS_ERROR_UNKNOWN,
	DNS_ERROR_UNLOADED,
	DNS_ERROR_TIMEOUT,
	DNS_ERROR_NOT_AN_ANSWER,
	DNS_ERROR_NONSTANDARD_QUERY,
	DNS_ERROR_FORMAT_ERROR,
	DNS_ERROR_SERVER_FAILURE,
	DNS_ERROR_DOMAIN_NOT_FOUND,
	DNS_ERROR_NOT_IMPLEMENTED,
	DNS_ERROR_REFUSED,
	DNS_ERROR_NO_RECORDS,
	DNS_ERROR_INVALIDTYPE
};


struct CoreExport Question
{
	Anope::string name;
	QueryType type;
	unsigned short qclass;

	Question();
	Question(const Anope::string &, QueryType, unsigned short = 1);
};

struct CoreExport ResourceRecord : public Question
{
	unsigned int ttl;
	Anope::string rdata;
	time_t created;

	ResourceRecord(const Anope::string &, QueryType, unsigned short = 1);
	ResourceRecord(const Question &);
};

struct CoreExport DNSQuery
{
	std::vector<Question> questions;
	std::vector<ResourceRecord> answers, authorities, additional;
	DNSError error;

	DNSQuery();
	DNSQuery(const Question &q);
	DNSQuery(const DNSPacket &p);
};

/** The request
 */
class CoreExport DNSRequest : public Timer, public Question
{
	/* Use result cache if available */
	bool use_cache;

 public:
	/* Request id */
 	unsigned short id;
 	/* Creator of this request */
	Module *creator;

	DNSRequest(const Anope::string &addr, QueryType qt, bool cache = false, Module *c = NULL);

	virtual ~DNSRequest();

	void Process();

	virtual void OnLookupComplete(const DNSQuery *r) = 0;

	virtual void OnError(const DNSQuery *r);
	
	void Tick(time_t) anope_override;
};

/** A full packet sent or recieved to/from the nameserver, may contain multiple queries
 */
class DNSPacket : public DNSQuery
{
	static const int DNS_POINTER = 0xC0;
	static const int DNS_LABEL = 0x3F;

	void PackName(unsigned char *output, unsigned short output_size, unsigned short &pos, const Anope::string &name);
	Anope::string UnpackName(const unsigned char *input, unsigned short input_size, unsigned short &pos);
	
	Question UnpackQuestion(const unsigned char *input, unsigned short input_size, unsigned short &pos);
	ResourceRecord UnpackResourceRecord(const unsigned char *input, unsigned short input_size, unsigned short &poss);
 public:
	static const int HEADER_LENGTH = 12;

	/* Our 16-bit id for this header */
	unsigned short id;
	/* Flags on the query */
	unsigned short flags;

	DNSPacket();
	void Fill(const unsigned char *input, const unsigned short len);
	unsigned short Pack(unsigned char *output, unsigned short output_size);
};

/** DNS manager, manages all requests
 */
class CoreExport DNSManager : public Timer, public Socket
{
	typedef std::multimap<Anope::string, ResourceRecord, ci::less> cache_map;
	cache_map cache;
	sockaddrs addrs;
 public:
	std::deque<DNSPacket *> packets;
	std::map<unsigned short, DNSRequest *> requests;

	static const int DNSPort = 53;

	DNSManager(const Anope::string &nameserver, int port);

	~DNSManager();

	bool ProcessRead();

	bool ProcessWrite();

	/** Add a record to the dns cache
	 * @param r The record
	 */
	void AddCache(DNSQuery &r);

	/** Check the DNS cache to see if request can be handled by a cached result
	 * @return true if a cached result was found.
	 */
	bool CheckCache(DNSRequest *request);

	/** Tick this timer, used to clear the DNS cache.
	 */
	void Tick(time_t now) anope_override;

	/** Cleanup all pending DNS queries for a module
	 * @param mod The module
	 */
	void Cleanup(Module *mod);

	/** Does a BLOCKING DNS query and returns the first IP.
	 * Only use this if you know what you are doing. Unless you specifically
	 * need a blocking query use the DNSRequest system
	 */
	static DNSQuery BlockingQuery(const Anope::string &mask, QueryType qt);
};

extern DNSManager *DNSEngine;

#endif // DNS_H