root/trunk/pdns/pdns/dnspacket.hh @ 84

Revision 84, 12.4 KB (checked in by ahu, 10 years ago)

wasted day

  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1/*
2    PowerDNS Versatile Database Driven Nameserver
3    Copyright (C) 2002  PowerDNS.COM BV
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18*/
19// $Id: dnspacket.hh,v 1.6 2002/12/17 16:50:30 ahu Exp $
20#ifndef DNSPACKET_HH
21#define DNSPACKET_HH
22
23#include <cstdio>
24#include <cstring>
25#include <cstdlib>
26#include <sys/types.h>
27
28#ifndef WIN32
29#include <sys/socket.h>
30#include <netinet/in.h>
31#include <sys/time.h>
32#include <unistd.h>
33#include <arpa/inet.h>
34
35#endif // WIN32
36
37#include <iostream>
38#include <string>
39
40#include <vector>
41#include "qtype.hh"
42#include "dns.hh"
43#include "misc.hh"
44#include "utility.hh"
45#include "logger.hh"
46#include "ahuexception.hh"
47
48#ifdef HAVE_CONFIG_H
49#include "config.h"
50 #endif // HAVE_CONFIG_H
51
52
53#ifdef WIN32
54# ifdef BYTE_ORDER
55#   undef BYTE_ORDER
56# endif // BYTE_ORDER
57# define BYTE_ORDER LITTLE_ENDIAN
58#endif // WIN32
59
60class DNSBackend;
61
62//! This class represents DNS packets, either received or to be sent.
63class DNSPacket
64{
65public:
66  DNSPacket();
67  DNSPacket(const DNSPacket &orig);
68
69  int expand(const char *begin, const char *end, string &expanded, int depth=0);
70  inline int parse(const char *mesg, int len); //!< parse a raw UDP or TCP packet and suck the data inward
71  string getString();
72
73  //! the raw DNS header
74  struct dnsheader
75  {
76    unsigned int id:16;  //!< id of this query/response
77#ifdef WORDS_BIGENDIAN     // ultrasparc
78    unsigned int qr:1;      //!< 1 if this is a query, 0 if response
79    unsigned int opcode:4;  //!< the opcode
80    unsigned int aa:1;   //!< packet contains authoritative data
81    unsigned int tc:1;   //!< packet is truncated
82    unsigned int rd:1;   //!< this packets wants us to recurse
83    unsigned int ra:1;     //!< ??
84    unsigned int unused:1; //!<
85    unsigned int ad:1;     //!< authentic data
86    unsigned int cd:1;     //!< checking disabled by resolver
87    unsigned int rcode:4;  //!< ??
88#else
89    unsigned int rd:1;   //!< this packets wants us to recurse
90    unsigned int tc:1;   //!< packet is truncated
91    unsigned int aa:1;   //!< packet contains authoritative data
92    unsigned int opcode:4;  //!< the opcode
93    unsigned int qr:1;      //!< 1 if this is a query, 0 if response
94
95    ///////////
96
97    unsigned int rcode:4;  //!< ??
98    unsigned int cd:1;     //!< checking disabled by resolver
99    unsigned int ad:1;     //!< authentic data
100    unsigned int unused:1; //!<
101    unsigned int ra:1;     //!< ??
102#endif
103    ////////////////
104   
105    unsigned int qdcount:16;  //!< number of questions
106    unsigned int ancount:16;  //!< number of answers
107    unsigned int nscount:16;  //!< number of authoritative nameservers included in answer
108    unsigned int arcount:16;  //!< number of additional resource records
109  };
110
111  inline void setRemote(const struct sockaddr *a, Utility::socklen_t socklen);
112  string getRemote() const;
113  void setA(bool); //!< make this packet authoritative
114  void setRA(bool); //!< set the Recursion Available flag
115  void setRD(bool); //!< set the Recursion Desired flag
116  void setAnswer(bool); //!< Make this packet an answer
117  void setID(u_int16_t); //!< set the DNS id of this packet
118  void setOpcode(u_int16_t);  //!< set the Opcode of this packet
119  void setRcode(int v); //!< set the Rcode of this packet
120
121
122  /** Add a DNSResourceRecord to this packet. A DNSPacket (as does a DNS Packet) has 4 kinds of resource records. Questions,
123      Answers, Authority and Additional. See RFC 1034 and 1035 for details. You can specify where a record needs to go in the
124      DNSResourceRecord d_place field */
125  void addRecord(const DNSResourceRecord &); 
126
127
128  /** helper function for both DNSPacket and addSOARecord() - converts a line into a struct, for easier parsing */
129  static void fillSOAData(const string &content, SOAData &data);
130
131  /** for use by DNSPacket, converts a SOAData class to a ascii line again */
132  static string serializeSOAData(const SOAData &data);
133  void setQuestion(int op, const string &qdomain, int qtype);
134  vector<DNSResourceRecord> getAnswers();
135private:
136  string compress(const string &qd);
137  void addARecord(const string&, u_int32_t, u_int32_t ttl, DNSResourceRecord::Place place); //!< add an A record to the packet
138  void addARecord(const DNSResourceRecord &); //!< add an A record to the packet
139
140  void addAAAARecord(const string &, unsigned char addr[16], u_int32_t ttl); //!< add an A record to the packet
141  void addAAAARecord(const DNSResourceRecord &); //!< add an A record to the packet
142
143
144  void addMXRecord(const string &domain, const string &mx, int priority, u_int32_t ttl); //!< add an MX record to the packet
145  void addMXRecord(const DNSResourceRecord &); //!< add an MX record to the packet
146
147  void addCNAMERecord(const string &domain, const string &alias, u_int32_t ttl); //!< add a CNAME record to the packet
148  void addCNAMERecord(const DNSResourceRecord &); //!< add a CNAME record to the packet
149
150  void addRPRecord(const string &domain, const string &content, u_int32_t ttl); //!< add a RP record to the packet
151  void addRPRecord(const DNSResourceRecord &); //!< add a RP record to the packet
152
153  void addNAPTRRecord(const string &domain, const string &content, u_int32_t ttl); //!< add a NAPTR record to the packet
154  void addNAPTRRecord(const DNSResourceRecord &); //!< add a NAPTR record to the packet
155
156
157  void addPTRRecord(const string &domain, const string &alias, u_int32_t ttl); //!< add a PTR record to the packet
158  void addPTRRecord(const DNSResourceRecord &); //!< add a PTR record to the packet
159
160  void addLOCRecord(const string &domain, const string &content, u_int32_t ttl); 
161  void addLOCRecord(const DNSResourceRecord &); //!< add a LOC record to the packet
162
163
164  /** Adds a SOA record to the packet. The SOA record is very special because we have a lot of default values,
165      that may be overridden by the contents of the database. Content can have a variety of content:
166     
167      (nothing)
168      hostmaster
169      hostmaster serial-number
170      hostmaster serial-number [refresh [retry [expire [ minimum] ] ] ]
171
172      Suggested values are:
173
174      10800           ;refresh every three hours
175      300             ;retry every 5 min
176      604800          ;expire after a week
177      86400           ;default ttl
178
179      An empty field means that we supply hostmaster+@+domain name as hostmaster. An empty serial number is replaced by the
180      number of seconds since 1 jan 1970 (unix timestamp). The other values are substituted as indicated
181
182  */
183
184
185  void addSOARecord(const string &domain, const string &content, u_int32_t ttl, DNSResourceRecord::Place place); 
186  void addSOARecord(const DNSResourceRecord &); //!< add a SOA record to the packet
187
188
189  void addTXTRecord(string domain, string, u_int32_t ttl); //!< add a TXT record to the packet
190  void addTXTRecord(const DNSResourceRecord &); //!< add a TXT record to the packet
191
192  void addHINFORecord(string domain, string, u_int32_t ttl); //!< add a HINFO record to the packet
193  void addHINFORecord(const DNSResourceRecord &); //!< add a HINFO record to the packet
194
195  void addNSRecord(string domain, string server, u_int32_t ttl, DNSResourceRecord::Place place); //!< add an NS record to the packet
196  void addNSRecord(const DNSResourceRecord &); //!< add an NS record to the packet
197
198  static string &attodot(string &str);  //!< for when you need to insert an email address in the SOA
199
200public:
201
202  DTime d_dt; //!< the time this packet was created. replyPacket() copies this in for you, so d_dt becomes the time spent processing the question+answer
203  void pasteQ(const char *question, int length); //!< set the question of this packet, useful for crafting replies
204  void trim();
205  void wrapup(void); 
206  inline const char *getData(void); //!< get binary representation of packet
207  void setRaw(char *mesg, int length);
208  const char *getRaw(void);
209  inline void spoofID(u_int16_t id); //!< change the ID of an existing packet. Useful for fixing up packets returned from the PacketCache
210  inline void spoofQuestion(const string &qd); //!< paste in the exact right case of the question. Useful for PacketCache
211  void truncate(int new_length); // has documentation in source
212
213  bool needAP(); //!< query this to find out if this packet needs additional processing
214  vector<DNSResourceRecord> getAPRecords(); //!< get a vector with DNSResourceRecords that need additional processing
215  void setCompress(bool compress);
216
217  DNSPacket *replyPacket() const; //!< convenience function that creates a virgin answer packet to this question
218  Utility::sock_t getSocket() const
219  {
220    return d_socket;
221  }
222  inline void setSocket(Utility::sock_t sock);
223  inline void commitD();
224  static bool isRD(const string &buffer)
225  {
226    return ((struct dnsheader *)buffer.c_str())->rd;
227  }
228
229  //////// DATA !
230
231  char remote[28];
232  Utility::socklen_t d_socklen; // 4
233  u_int16_t len; //!< length of the raw binary packet 2
234  u_int16_t qclass;  //!< class of the question - should always be INternet 2
235  struct dnsheader d; //!< dnsheader at the start of the databuffer 12
236
237  QType qtype;  //!< type of the question 8
238
239  string qdomain;  //!< qname of the question 4
240
241
242private:
243  bool d_wrapped; // 1
244  bool d_compress; // 1
245  u_int16_t d_qlen; // length of the question (including class & type) in this packet 2
246
247  int d_socket; // 4
248  int findlabel(string &label);
249  int toqname(const char *name, string &qname, bool compress = true);
250  int toqname(const string &name, string &qname, bool compress = true);
251  int toqname(const string &name, string *qname, bool compress = true); 
252  const string makeSoaHostmasterPiece(const string &hostmaster);
253  static string parseLOC(const unsigned char *p, unsigned int length);
254
255  int domprint();
256  int getq();
257
258  // MORE DATA!
259
260  string stringbuffer; // this is where everything lives 4
261
262  vector<DNSResourceRecord> rrs; // 4
263};
264
265
266inline void DNSPacket::spoofQuestion(const string &qd)
267{
268  string label=compress(qd);
269  for(string::size_type i=0;i<label.size();++i)
270    stringbuffer[i+sizeof(d)]=label[i];
271  d_wrapped=true; // if we do this, don't later on wrapup
272}
273
274/** This function takes data from the network, possibly received with recvfrom, and parses
275    it into our class. Results of calling this function multiple times on one packet are
276    unknown. Returns -1 if the packet cannot be parsed.
277*/
278int DNSPacket::parse(const char *mesg, int length)
279{
280  stringbuffer.assign(mesg,length); 
281  len=length;
282  if(length < 12) { 
283    L << Logger::Warning << "Ignoring packet: too short from "
284      << getRemote() << endl;
285    return -1;
286  }
287
288  memcpy((void *)&d,(const void *)stringbuffer.c_str(),12);
289
290  int offset=0;
291  d_qlen=0;
292  if(ntohs(d.qdcount)) {
293    offset = getq(); // also sets this->qdomain!
294    if(offset < 0) {
295      //    L << Logger::Warning << "Ignoring packet: invalid label in question from "
296      //  << inet_ntoa(remote.sin_addr) << endl;
297      return -1;
298    }
299    d_qlen=offset+4; // this points to the start of any answers
300  }
301
302  if(15+offset>=stringbuffer.length()) {
303    L << Logger::Warning << "Ignoring packet: question too short from "
304      << getRemote() << endl;
305    return -1;
306  }
307
308  qtype=((unsigned char)stringbuffer[12+offset])*256+(unsigned char)stringbuffer[13+offset];
309  qclass=((unsigned char)stringbuffer[14+offset]*256)+(unsigned char)stringbuffer[15+offset];
310  return 0;
311}
312
313//! Use this to set where this packet was received from or should be sent to
314inline void DNSPacket::setRemote(const struct sockaddr *s, Utility::socklen_t socklen)
315{
316  if(socklen>(Utility::socklen_t)sizeof(remote))
317    throw AhuException("Address too long for storage: "+itoa(socklen));
318
319  memcpy((void *)remote,(void *)s,socklen);
320  d_socklen=socklen;
321}
322
323inline void DNSPacket::spoofID(u_int16_t id)
324{
325  stringbuffer[1]=(id>>8)&0xff; 
326  stringbuffer[0]=id&0xff;
327  d.id=id;
328}
329
330inline void DNSPacket::setSocket(Utility::sock_t sock)
331{
332  d_socket=sock;
333}
334
335inline void DNSPacket::commitD()
336{
337  stringbuffer.replace(0,12,(char *)&d,12); // copy in d
338}
339
340inline const char *DNSPacket::getData(void)
341{
342  if(!d_wrapped)
343    wrapup();
344
345  return stringbuffer.data();
346}
347
348
349#endif
Note: See TracBrowser for help on using the browser.