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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
|
/*
*
* (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,
/* An authoritative name server */
DNS_QUERY_NS = 2,
/* A CNAME lookup */
DNS_QUERY_CNAME = 5,
/* Start of a zone of authority */
DNS_QUERY_SOA = 6,
/* Reverse DNS lookup */
DNS_QUERY_PTR = 12,
/* IPv6 AAAA lookup */
DNS_QUERY_AAAA = 28,
/* Zone transfer */
DNS_QUERY_AXFR = 252
};
/** 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);
};
/** 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
*/
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;
/* Source or destination of the packet */
sockaddrs addr;
/* ID for this packet */
unsigned short id;
/* Flags on the packet */
unsigned short flags;
DNSPacket(sockaddrs *a);
void Fill(const unsigned char *input, const unsigned short len);
unsigned short Pack(unsigned char *output, unsigned short output_size);
};
/** DNS manager
*/
class CoreExport DNSManager : public Timer
{
class ReplySocket : public virtual Socket
{
public:
virtual ~ReplySocket() { }
virtual void Reply(DNSPacket *p) = 0;
};
/* Listens for TCP requests */
class TCPSocket : public ListenSocket
{
/* A TCP client */
class Client : public ClientSocket, public Timer, public ReplySocket
{
TCPSocket *tcpsock;
DNSPacket *packet;
unsigned char packet_buffer[524];
int length;
public:
Client(TCPSocket *ls, int fd, const sockaddrs &addr);
~Client();
/* Times out after a few seconds */
void Tick(time_t) anope_override { }
void Reply(DNSPacket *p) anope_override;
bool ProcessRead() anope_override;
bool ProcessWrite() anope_override;
};
public:
TCPSocket(const Anope::string &ip, int port);
ClientSocket *OnAccept(int fd, const sockaddrs &addr) anope_override;
};
/* Listens for UDP requests */
class UDPSocket : public ReplySocket
{
std::deque<DNSPacket *> packets;
public:
UDPSocket(const Anope::string &ip, int port);
~UDPSocket();
void Reply(DNSPacket *p) anope_override;
std::deque<DNSPacket *>& GetPackets() { return packets; }
bool ProcessRead() anope_override;
bool ProcessWrite() anope_override;
};
typedef std::multimap<Anope::string, ResourceRecord, ci::less> cache_map;
cache_map cache;
bool listen;
uint32_t serial;
public:
TCPSocket *tcpsock;
UDPSocket *udpsock;
sockaddrs addrs;
std::map<unsigned short, DNSRequest *> requests;
DNSManager(const Anope::string &nameserver, const Anope::string &ip, int port);
~DNSManager();
bool HandlePacket(ReplySocket *s, const unsigned char *const data, int len, sockaddrs *from);
/** 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);
void UpdateSerial();
uint32_t GetSerial() const;
/** 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 CoreExport DNSManager *DNSEngine;
#endif // DNS_H
|