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

Revision 128, 12.6 KB (checked in by ahu, 10 years ago)

further work on a lot of things

  • 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.12 2003/01/13 15:39:17 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 unsigned char *begin, const unsigned 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  u_int16_t getRemotePort() const;
114  void setA(bool); //!< make this packet authoritative
115  void setRA(bool); //!< set the Recursion Available flag
116  void setRD(bool); //!< set the Recursion Desired flag
117  void setAnswer(bool); //!< Make this packet an answer
118  void setID(u_int16_t); //!< set the DNS id of this packet
119  void setOpcode(u_int16_t);  //!< set the Opcode of this packet
120  void setRcode(int v); //!< set the Rcode of this packet
121
122
123  /** Add a DNSResourceRecord to this packet. A DNSPacket (as does a DNS Packet) has 4 kinds of resource records. Questions,
124      Answers, Authority and Additional. See RFC 1034 and 1035 for details. You can specify where a record needs to go in the
125      DNSResourceRecord d_place field */
126  void addRecord(const DNSResourceRecord &); 
127
128
129  /** helper function for both DNSPacket and addSOARecord() - converts a line into a struct, for easier parsing */
130  static void fillSOAData(const string &content, SOAData &data);
131
132  /** for use by DNSPacket, converts a SOAData class to a ascii line again */
133  static string serializeSOAData(const SOAData &data);
134  void setQuestion(int op, const string &qdomain, int qtype);
135  vector<DNSResourceRecord> getAnswers();
136private:
137  string compress(const string &qd);
138  void addARecord(const string&, u_int32_t, u_int32_t ttl, DNSResourceRecord::Place place); //!< add an A record to the packet
139  void addARecord(const DNSResourceRecord &); //!< add an A record to the packet
140
141  void addAAAARecord(const string &, unsigned char addr[16], u_int32_t ttl); //!< add an A record to the packet
142  void addAAAARecord(const DNSResourceRecord &); //!< add an A record to the packet
143
144
145  void addMXRecord(const string &domain, const string &mx, int priority, u_int32_t ttl); //!< add an MX record to the packet
146  void addMXRecord(const DNSResourceRecord &); //!< add an MX record to the packet
147
148  void addCNAMERecord(const string &domain, const string &alias, u_int32_t ttl); //!< add a CNAME record to the packet
149  void addCNAMERecord(const DNSResourceRecord &); //!< add a CNAME record to the packet
150
151  void addRPRecord(const string &domain, const string &content, u_int32_t ttl); //!< add a RP record to the packet
152  void addRPRecord(const DNSResourceRecord &); //!< add a RP record to the packet
153
154  void addNAPTRRecord(const string &domain, const string &content, u_int32_t ttl); //!< add a NAPTR record to the packet
155  void addNAPTRRecord(const DNSResourceRecord &); //!< add a NAPTR record to the packet
156
157
158  void addPTRRecord(const string &domain, const string &alias, u_int32_t ttl); //!< add a PTR record to the packet
159  void addPTRRecord(const DNSResourceRecord &); //!< add a PTR record to the packet
160
161  void addLOCRecord(const string &domain, const string &content, u_int32_t ttl); 
162  void addLOCRecord(const DNSResourceRecord &); //!< add a LOC record to the packet
163
164
165  /** Adds a SOA record to the packet. The SOA record is very special because we have a lot of default values,
166      that may be overridden by the contents of the database. Content can have a variety of content:
167     
168      (nothing)
169      hostmaster
170      hostmaster serial-number
171      hostmaster serial-number [refresh [retry [expire [ minimum] ] ] ]
172
173      Suggested values are:
174
175      10800           ;refresh every three hours
176      300             ;retry every 5 min
177      604800          ;expire after a week
178      86400           ;default ttl
179
180      An empty field means that we supply hostmaster+@+domain name as hostmaster. An empty serial number is replaced by the
181      number of seconds since 1 jan 1970 (unix timestamp). The other values are substituted as indicated
182
183  */
184  void addSOARecord(const string &domain, const string &content, u_int32_t ttl, DNSResourceRecord::Place place); 
185  void addSOARecord(const DNSResourceRecord &); //!< add a SOA record to the packet
186
187  void addTXTRecord(string domain, string, u_int32_t ttl); //!< add a TXT record to the packet
188  void addTXTRecord(const DNSResourceRecord &); //!< add a TXT record to the packet
189
190  void addHINFORecord(string domain, string, u_int32_t ttl); //!< add a HINFO record to the packet
191  void addHINFORecord(const DNSResourceRecord &); //!< add a HINFO record to the packet
192
193  void addNSRecord(string domain, string server, u_int32_t ttl, DNSResourceRecord::Place place); //!< add an NS record to the packet
194  void addNSRecord(const DNSResourceRecord &); //!< add an NS record to the packet
195  void addGenericRecord(const DNSResourceRecord& rr);
196  static string &attodot(string &str);  //!< for when you need to insert an email address in the SOA
197
198public:
199
200  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
201  void pasteQ(const char *question, int length); //!< set the question of this packet, useful for crafting replies
202  void trim();
203  void wrapup(void); 
204  inline const char *getData(void); //!< get binary representation of packet
205  void setRaw(char *mesg, int length);
206  const char *getRaw(void);
207  inline void spoofID(u_int16_t id); //!< change the ID of an existing packet. Useful for fixing up packets returned from the PacketCache
208  inline void spoofQuestion(const string &qd); //!< paste in the exact right case of the question. Useful for PacketCache
209  void truncate(int new_length); // has documentation in source
210
211  bool needAP(); //!< query this to find out if this packet needs additional processing
212  vector<DNSResourceRecord> getAPRecords(); //!< get a vector with DNSResourceRecords that need additional processing
213  void setCompress(bool compress);
214
215  DNSPacket *replyPacket() const; //!< convenience function that creates a virgin answer packet to this question
216  Utility::sock_t getSocket() const
217  {
218    return d_socket;
219  }
220  inline void setSocket(Utility::sock_t sock);
221  inline void commitD();
222  static bool isRD(const string &buffer)
223  {
224    return ((struct dnsheader *)buffer.c_str())->rd;
225  }
226
227  //////// DATA !
228
229  char remote[28];
230  Utility::socklen_t d_socklen; // 4
231  u_int16_t len; //!< length of the raw binary packet 2
232  u_int16_t qclass;  //!< class of the question - should always be INternet 2
233  struct dnsheader d; //!< dnsheader at the start of the databuffer 12
234
235  QType qtype;  //!< type of the question 8
236
237  string qdomain;  //!< qname of the question 4
238
239
240private:
241  bool d_wrapped; // 1
242  bool d_compress; // 1
243  u_int16_t d_qlen; // length of the question (including class & type) in this packet 2
244
245  int d_socket; // 4
246  int findlabel(string &label);
247  int toqname(const char *name, string &qname, bool compress = true);
248  int toqname(const string &name, string &qname, bool compress = true);
249  int toqname(const string &name, string *qname, bool compress = true); 
250  const string makeSoaHostmasterPiece(const string &hostmaster);
251  static string parseLOC(const unsigned char *p, unsigned int length);
252  void makeHeader(char *p,u_int16_t qtype, u_int32_t ttl);
253  int domprint();
254  int getq();
255
256  // MORE DATA!
257
258  string stringbuffer; // this is where everything lives 4
259
260  vector<DNSResourceRecord> rrs; // 4
261};
262
263
264inline void DNSPacket::spoofQuestion(const string &qd)
265{
266  string label=compress(qd);
267  for(string::size_type i=0;i<label.size();++i)
268    stringbuffer[i+sizeof(d)]=label[i];
269  d_wrapped=true; // if we do this, don't later on wrapup
270}
271
272/** This function takes data from the network, possibly received with recvfrom, and parses
273    it into our class. Results of calling this function multiple times on one packet are
274    unknown. Returns -1 if the packet cannot be parsed.
275*/
276int DNSPacket::parse(const char *mesg, int length)
277{
278  stringbuffer.assign(mesg,length); 
279  len=length;
280  if(length < 12) { 
281    L << Logger::Warning << "Ignoring packet: too short from "
282      << getRemote() << endl;
283    return -1;
284  }
285
286  memcpy((void *)&d,(const void *)stringbuffer.c_str(),12);
287
288  int offset=0;
289  d_qlen=0;
290  if(ntohs(d.qdcount)) {
291    offset = getq(); // also sets this->qdomain!
292    if(offset < 0) {
293      //    L << Logger::Warning << "Ignoring packet: invalid label in question from "
294      //  << inet_ntoa(remote.sin_addr) << endl;
295      return -1;
296    }
297    d_qlen=offset+4; // this points to the start of any answers
298  }
299
300  if((unsigned int)(15+offset)>=stringbuffer.length()) {
301    L << Logger::Warning << "Ignoring packet: question too short from "<< getRemote()<<", "<<
302      15+offset<<">="<<stringbuffer.length()<<endl;
303    return -1;
304  }
305
306  qtype=((unsigned char)stringbuffer[12+offset])*256+(unsigned char)stringbuffer[13+offset];
307  qclass=((unsigned char)stringbuffer[14+offset]*256)+(unsigned char)stringbuffer[15+offset];
308  return 0;
309}
310
311//! Use this to set where this packet was received from or should be sent to
312inline void DNSPacket::setRemote(const struct sockaddr *s, Utility::socklen_t socklen)
313{
314  if(socklen>(Utility::socklen_t)sizeof(remote))
315    throw AhuException("Address too long for storage: "+itoa(socklen));
316
317  memcpy((void *)remote,(void *)s,socklen);
318  d_socklen=socklen;
319}
320
321inline void DNSPacket::spoofID(u_int16_t id)
322{
323  stringbuffer[1]=(id>>8)&0xff; 
324  stringbuffer[0]=id&0xff;
325  d.id=id;
326}
327
328inline void DNSPacket::setSocket(Utility::sock_t sock)
329{
330  d_socket=sock;
331}
332
333inline void DNSPacket::commitD()
334{
335  stringbuffer.replace(0,12,(char *)&d,12); // copy in d
336}
337
338inline const char *DNSPacket::getData(void)
339{
340  if(!d_wrapped)
341    wrapup();
342
343  return stringbuffer.data();
344}
345
346
347#endif
Note: See TracBrowser for help on using the browser.