1 /* $Id$ */
2 /*
3  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 #ifndef __PJLIB_UTIL_DNS_H__
21 #define __PJLIB_UTIL_DNS_H__
22 
23 
24 /**
25  * @file dns.h
26  * @brief Low level DNS message parsing and packetization.
27  */
28 #include <pjlib-util/types.h>
29 #include <pj/sock.h>
30 
31 PJ_BEGIN_DECL
32 
33 /**
34  * @defgroup PJ_DNS DNS and Asynchronous DNS Resolver
35  * @ingroup PJ_PROTOCOLS
36  */
37 
38 /**
39  * @defgroup PJ_DNS_PARSING Low-level DNS Message Parsing and Packetization
40  * @ingroup PJ_DNS
41  * @{
42  *
43  * This module provides low-level services to parse and packetize DNS queries
44  * and responses. The functions support building a DNS query packet and parse
45  * the data in the DNS response. This implementation conforms to the
46  * following specifications:
47  *  - RFC 1035: DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION
48  *  - RFC 1886: DNS Extensions to support IP version 6
49  *
50  * To create a DNS query packet, application should call #pj_dns_make_query()
51  * function, specifying the desired DNS query type, the name to be resolved,
52  * and the buffer where the DNS packet will be built into.
53  *
54  * When incoming DNS query or response packet arrives, application can use
55  * #pj_dns_parse_packet() to parse the TCP/UDP payload into parsed DNS packet
56  * structure.
57  *
58  * This module does not provide any networking functionalities to send or
59  * receive DNS packets. This functionality should be provided by higher layer
60  * modules such as @ref PJ_DNS_RESOLVER.
61  */
62 
63 enum
64 {
65     PJ_DNS_CLASS_IN	= 1	/**< DNS class IN.			    */
66 };
67 
68 /**
69  * This enumeration describes standard DNS record types as described by
70  * RFC 1035, RFC 2782, and others.
71  */
72 typedef enum pj_dns_type
73 {
74     PJ_DNS_TYPE_A	= 1,    /**< Host address (A) record.		    */
75     PJ_DNS_TYPE_NS	= 2,    /**< Authoritative name server (NS)	    */
76     PJ_DNS_TYPE_MD	= 3,    /**< Mail destination (MD) record.	    */
77     PJ_DNS_TYPE_MF	= 4,    /**< Mail forwarder (MF) record.	    */
78     PJ_DNS_TYPE_CNAME	= 5,	/**< Canonical name (CNAME) record.	    */
79     PJ_DNS_TYPE_SOA	= 6,    /**< Marks start of zone authority.	    */
80     PJ_DNS_TYPE_MB	= 7,    /**< Mailbox domain name (MB).		    */
81     PJ_DNS_TYPE_MG	= 8,    /**< Mail group member (MG).		    */
82     PJ_DNS_TYPE_MR	= 9,    /**< Mail rename domain name.		    */
83     PJ_DNS_TYPE_NULL	= 10,	/**< NULL RR.				    */
84     PJ_DNS_TYPE_WKS	= 11,	/**< Well known service description	    */
85     PJ_DNS_TYPE_PTR	= 12,	/**< Domain name pointer.		    */
86     PJ_DNS_TYPE_HINFO	= 13,	/**< Host information.			    */
87     PJ_DNS_TYPE_MINFO	= 14,	/**< Mailbox or mail list information.	    */
88     PJ_DNS_TYPE_MX	= 15,	/**< Mail exchange record.		    */
89     PJ_DNS_TYPE_TXT	= 16,	/**< Text string.			    */
90     PJ_DNS_TYPE_RP	= 17,	/**< Responsible person.		    */
91     PJ_DNS_TYPE_AFSB	= 18,	/**< AFS cell database.			    */
92     PJ_DNS_TYPE_X25	= 19,	/**< X.25 calling address.		    */
93     PJ_DNS_TYPE_ISDN	= 20,	/**< ISDN calling address.		    */
94     PJ_DNS_TYPE_RT	= 21,	/**< Router.				    */
95     PJ_DNS_TYPE_NSAP	= 22,	/**< NSAP address.			    */
96     PJ_DNS_TYPE_NSAP_PTR= 23,	/**< NSAP reverse address.		    */
97     PJ_DNS_TYPE_SIG	= 24,	/**< Signature.				    */
98     PJ_DNS_TYPE_KEY	= 25,	/**< Key.				    */
99     PJ_DNS_TYPE_PX	= 26,	/**< X.400 mail mapping.		    */
100     PJ_DNS_TYPE_GPOS	= 27,	/**< Geographical position (withdrawn)	    */
101     PJ_DNS_TYPE_AAAA	= 28,	/**< IPv6 address.			    */
102     PJ_DNS_TYPE_LOC	= 29,	/**< Location.				    */
103     PJ_DNS_TYPE_NXT	= 30,	/**< Next valid name in the zone.	    */
104     PJ_DNS_TYPE_EID	= 31,	/**< Endpoint idenfitier.		    */
105     PJ_DNS_TYPE_NIMLOC	= 32,	/**< Nimrod locator.			    */
106     PJ_DNS_TYPE_SRV	= 33,	/**< Server selection (SRV) record.	    */
107     PJ_DNS_TYPE_ATMA	= 34,	/**< DNS ATM address record.		    */
108     PJ_DNS_TYPE_NAPTR	= 35,	/**< DNS Naming authority pointer record.   */
109     PJ_DNS_TYPE_KX	= 36,	/**< DNS key exchange record.		    */
110     PJ_DNS_TYPE_CERT	= 37,	/**< DNS certificate record.		    */
111     PJ_DNS_TYPE_A6	= 38,	/**< DNS IPv6 address (experimental)	    */
112     PJ_DNS_TYPE_DNAME	= 39,	/**< DNS non-terminal name redirection rec. */
113 
114     PJ_DNS_TYPE_OPT	= 41,	/**< DNS options - contains EDNS metadata.  */
115     PJ_DNS_TYPE_APL	= 42,	/**< DNS Address Prefix List (APL) record.  */
116     PJ_DNS_TYPE_DS	= 43,	/**< DNS Delegation Signer (DS)		    */
117     PJ_DNS_TYPE_SSHFP	= 44,	/**< DNS SSH Key Fingerprint		    */
118     PJ_DNS_TYPE_IPSECKEY= 45,	/**< DNS IPSEC Key.			    */
119     PJ_DNS_TYPE_RRSIG	= 46,	/**< DNS Resource Record signature.	    */
120     PJ_DNS_TYPE_NSEC	= 47,	/**< DNS Next Secure Name.		    */
121     PJ_DNS_TYPE_DNSKEY	= 48	/**< DNSSEC Key.			    */
122 } pj_dns_type;
123 
124 
125 
126 /**
127  * Standard DNS header, according to RFC 1035, which will be present in
128  * both DNS query and DNS response.
129  *
130  * Note that all values seen by application would be in
131  * host by order. The library would convert them to network
132  * byte order as necessary.
133  */
134 typedef struct pj_dns_hdr
135 {
136     pj_uint16_t  id;	    /**< Transaction ID.	    */
137     pj_uint16_t  flags;	    /**< Flags.			    */
138     pj_uint16_t  qdcount;   /**< Nb. of queries.	    */
139     pj_uint16_t  anscount;  /**< Nb. of res records	    */
140     pj_uint16_t  nscount;   /**< Nb. of NS records.	    */
141     pj_uint16_t  arcount;   /**< Nb. of additional records  */
142 } pj_dns_hdr;
143 
144 /** Create RCODE flag */
145 #define PJ_DNS_SET_RCODE(c)	((pj_uint16_t)((c) & 0x0F))
146 
147 /** Create RA (Recursion Available) bit */
148 #define PJ_DNS_SET_RA(on)	((pj_uint16_t)((on) << 7))
149 
150 /** Create RD (Recursion Desired) bit */
151 #define PJ_DNS_SET_RD(on)	((pj_uint16_t)((on) << 8))
152 
153 /** Create TC (Truncated) bit */
154 #define PJ_DNS_SET_TC(on)	((pj_uint16_t)((on) << 9))
155 
156 /** Create AA (Authoritative Answer) bit */
157 #define PJ_DNS_SET_AA(on)	((pj_uint16_t)((on) << 10))
158 
159 /** Create four bits opcode */
160 #define PJ_DNS_SET_OPCODE(o)	((pj_uint16_t)((o)  << 11))
161 
162 /** Create query/response bit */
163 #define PJ_DNS_SET_QR(on)	((pj_uint16_t)((on) << 15))
164 
165 
166 /** Get RCODE value */
167 #define PJ_DNS_GET_RCODE(val)	(((val) & PJ_DNS_SET_RCODE(0x0F)) >> 0)
168 
169 /** Get RA bit */
170 #define PJ_DNS_GET_RA(val)	(((val) & PJ_DNS_SET_RA(1)) >> 7)
171 
172 /** Get RD bit */
173 #define PJ_DNS_GET_RD(val)	(((val) & PJ_DNS_SET_RD(1)) >> 8)
174 
175 /** Get TC bit */
176 #define PJ_DNS_GET_TC(val)	(((val) & PJ_DNS_SET_TC(1)) >> 9)
177 
178 /** Get AA bit */
179 #define PJ_DNS_GET_AA(val)	(((val) & PJ_DNS_SET_AA(1)) >> 10)
180 
181 /** Get OPCODE value */
182 #define PJ_DNS_GET_OPCODE(val)	(((val) & PJ_DNS_SET_OPCODE(0x0F)) >> 11)
183 
184 /** Get QR bit */
185 #define PJ_DNS_GET_QR(val)	(((val) & PJ_DNS_SET_QR(1)) >> 15)
186 
187 
188 /**
189  * These constants describe DNS RCODEs. Application can fold these constants
190  * into PJLIB pj_status_t namespace by calling #PJ_STATUS_FROM_DNS_RCODE()
191  * macro.
192  */
193 typedef enum pj_dns_rcode
194 {
195     PJ_DNS_RCODE_FORMERR    = 1,    /**< Format error.			    */
196     PJ_DNS_RCODE_SERVFAIL   = 2,    /**< Server failure.		    */
197     PJ_DNS_RCODE_NXDOMAIN   = 3,    /**< Name Error.			    */
198     PJ_DNS_RCODE_NOTIMPL    = 4,    /**< Not Implemented.		    */
199     PJ_DNS_RCODE_REFUSED    = 5,    /**< Refused.			    */
200     PJ_DNS_RCODE_YXDOMAIN   = 6,    /**< The name exists.		    */
201     PJ_DNS_RCODE_YXRRSET    = 7,    /**< The RRset (name, type) exists.	    */
202     PJ_DNS_RCODE_NXRRSET    = 8,    /**< The RRset (name, type) doesn't exist*/
203     PJ_DNS_RCODE_NOTAUTH    = 9,    /**< Not authorized.		    */
204     PJ_DNS_RCODE_NOTZONE    = 10    /**< The zone specified is not a zone.  */
205 
206 } pj_dns_rcode;
207 
208 
209 /**
210  * This structure describes a DNS query record.
211  */
212 typedef struct pj_dns_parsed_query
213 {
214     pj_str_t	name;	    /**< The domain in the query.		    */
215     pj_uint16_t	type;	    /**< Type of the query (pj_dns_type)	    */
216     pj_uint16_t	dnsclass;   /**< Network class (PJ_DNS_CLASS_IN=1)	    */
217 } pj_dns_parsed_query;
218 
219 
220 /**
221  * This structure describes a Resource Record parsed from the DNS packet.
222  * All integral values are in host byte order.
223  */
224 typedef struct pj_dns_parsed_rr
225 {
226     pj_str_t	 name;	    /**< The domain name which this rec pertains.   */
227     pj_uint16_t	 type;	    /**< RR type code.				    */
228     pj_uint16_t	 dnsclass;  /**< Class of data (PJ_DNS_CLASS_IN=1).	    */
229     pj_uint32_t	 ttl;	    /**< Time to live.				    */
230     pj_uint16_t	 rdlength;  /**< Resource data length.			    */
231     void	*data;	    /**< Pointer to the raw resource data, only
232 				 when the type is not known. If it is known,
233 				 the data will be put in rdata below.	    */
234 
235     /** For resource types that are recognized/supported by this library,
236      *  the parsed resource data will be placed in this rdata union.
237      */
238     union rdata
239     {
240 	/** SRV Resource Data (PJ_DNS_TYPE_SRV, 33) */
241 	struct srv {
242 	    pj_uint16_t	prio;	/**< Target priority (lower is higher).	    */
243 	    pj_uint16_t weight;	/**< Weight/proportion			    */
244 	    pj_uint16_t port;	/**< Port number of the service		    */
245 	    pj_str_t	target;	/**< Target name.			    */
246 	} srv;
247 
248 	/** CNAME Resource Data (PJ_DNS_TYPE_CNAME, 5) */
249 	struct cname {
250 	    pj_str_t	name;	/**< Primary canonical name for an alias.   */
251 	} cname;
252 
253 	/** NS Resource Data (PJ_DNS_TYPE_NS, 2) */
254 	struct ns {
255 	    pj_str_t	name;	/**< Primary name server.		    */
256 	} ns;
257 
258 	/** PTR Resource Data (PJ_DNS_TYPE_PTR, 12) */
259 	struct ptr {
260 	    pj_str_t	name;	/**< PTR name.				    */
261 	} ptr;
262 
263 	/** A Resource Data (PJ_DNS_TYPE_A, 1) */
264 	struct a {
265 	    pj_in_addr	ip_addr;/**< IPv4 address in network byte order.    */
266 	} a;
267 
268 	/** AAAA Resource Data (PJ_DNS_TYPE_AAAA, 28) */
269 	struct aaaa {
270 	    pj_in6_addr	ip_addr;/**< IPv6 address in network byte order.    */
271 	} aaaa;
272 
273     } rdata;
274 
275 } pj_dns_parsed_rr;
276 
277 
278 /**
279  * This structure describes the parsed repersentation of the raw DNS packet.
280  * Note that all integral values in the parsed packet are represented in
281  * host byte order.
282  */
283 typedef struct pj_dns_parsed_packet
284 {
285     pj_dns_hdr		 hdr;	/**< Pointer to DNS hdr, in host byte order */
286     pj_dns_parsed_query	*q;	/**< Array of DNS queries.		    */
287     pj_dns_parsed_rr	*ans;	/**< Array of DNS RR answer.		    */
288     pj_dns_parsed_rr	*ns;	/**< Array of NS record in the answer.	    */
289     pj_dns_parsed_rr	*arr;	/**< Array of additional RR answer.	    */
290 } pj_dns_parsed_packet;
291 
292 
293 /**
294  * Option flags to be specified when calling #pj_dns_packet_dup() function.
295  * These flags can be combined with bitwise OR operation.
296  */
297 enum pj_dns_dup_options
298 {
299     PJ_DNS_NO_QD    = 1, /**< Do not duplicate the query section.	    */
300     PJ_DNS_NO_ANS   = 2, /**< Do not duplicate the answer section.	    */
301     PJ_DNS_NO_NS    = 4, /**< Do not duplicate the NS section.		    */
302     PJ_DNS_NO_AR    = 8  /**< Do not duplicate the additional rec section   */
303 };
304 
305 
306 /**
307  * Create DNS query packet to resolve the specified names. This function
308  * can be used to build any types of DNS query, such as A record or DNS SRV
309  * record.
310  *
311  * Application specifies the type of record and the name to be queried,
312  * and the function will build the DNS query packet into the buffer
313  * specified. Once the packet is successfully built, application can send
314  * the packet via TCP or UDP connection.
315  *
316  * @param packet	The buffer to put the DNS query packet.
317  * @param size		On input, it specifies the size of the buffer.
318  *			On output, it will be filled with the actual size of
319  *			the DNS query packet.
320  * @param id		DNS query ID to associate DNS response with the
321  *			query.
322  * @param qtype		DNS type of record to be queried (see #pj_dns_type).
323  * @param name		Name to be queried from the DNS server.
324  *
325  * @return		PJ_SUCCESS on success, or the appropriate error code.
326  */
327 PJ_DECL(pj_status_t) pj_dns_make_query(void *packet,
328 				       unsigned *size,
329 				       pj_uint16_t id,
330 				       int qtype,
331 				       const pj_str_t *name);
332 
333 /**
334  * Parse raw DNS packet into parsed DNS packet structure. This function is
335  * able to parse few DNS resource records such as A record, PTR record,
336  * CNAME record, NS record, and SRV record.
337  *
338  * @param pool		Pool to allocate memory for the parsed packet.
339  * @param packet	Pointer to the DNS packet (the TCP/UDP payload of
340  *			the raw packet).
341  * @param size		The size of the DNS packet.
342  * @param p_res		Pointer to store the resulting parsed packet.
343  *
344  * @return		PJ_SUCCESS on success, or the appropriate error code.
345  */
346 PJ_DECL(pj_status_t) pj_dns_parse_packet(pj_pool_t *pool,
347 					 const void *packet,
348 					 unsigned size,
349 					 pj_dns_parsed_packet **p_res);
350 
351 /**
352  * Duplicate DNS packet.
353  *
354  * @param pool		The pool to allocate memory for the duplicated packet.
355  * @param p		The DNS packet to be cloned.
356  * @param options	Option flags, from pj_dns_dup_options.
357  * @param p_dst		Pointer to store the cloned DNS packet.
358  */
359 PJ_DECL(void) pj_dns_packet_dup(pj_pool_t *pool,
360 				const pj_dns_parsed_packet*p,
361 				unsigned options,
362 				pj_dns_parsed_packet **p_dst);
363 
364 
365 /**
366  * Utility function to get the type name string of the specified DNS type.
367  *
368  * @param type		DNS type (see #pj_dns_type).
369  *
370  * @return		String name of the type (e.g. "A", "SRV", etc.).
371  */
372 PJ_DECL(const char *) pj_dns_get_type_name(int type);
373 
374 
375 /**
376  * Initialize DNS record as DNS SRV record.
377  *
378  * @param rec		The DNS resource record to be initialized as DNS
379  *			SRV record.
380  * @param res_name	Resource name.
381  * @param dnsclass	DNS class.
382  * @param ttl		Resource TTL value.
383  * @param prio		DNS SRV priority.
384  * @param weight	DNS SRV weight.
385  * @param port		Target port.
386  * @param target	Target name.
387  */
388 PJ_DECL(void) pj_dns_init_srv_rr(pj_dns_parsed_rr *rec,
389 				 const pj_str_t *res_name,
390 				 unsigned dnsclass,
391 				 unsigned ttl,
392 				 unsigned prio,
393 				 unsigned weight,
394 				 unsigned port,
395 				 const pj_str_t *target);
396 
397 /**
398  * Initialize DNS record as DNS CNAME record.
399  *
400  * @param rec		The DNS resource record to be initialized as DNS
401  *			CNAME record.
402  * @param res_name	Resource name.
403  * @param dnsclass	DNS class.
404  * @param ttl		Resource TTL value.
405  * @param name		Host name.
406  */
407 PJ_DECL(void) pj_dns_init_cname_rr(pj_dns_parsed_rr *rec,
408 				   const pj_str_t *res_name,
409 				   unsigned dnsclass,
410 				   unsigned ttl,
411 				   const pj_str_t *name);
412 
413 /**
414  * Initialize DNS record as DNS A record.
415  *
416  * @param rec		The DNS resource record to be initialized as DNS
417  *			A record.
418  * @param res_name	Resource name.
419  * @param dnsclass	DNS class.
420  * @param ttl		Resource TTL value.
421  * @param ip_addr	Host address.
422  */
423 PJ_DECL(void) pj_dns_init_a_rr(pj_dns_parsed_rr *rec,
424 			       const pj_str_t *res_name,
425 			       unsigned dnsclass,
426 			       unsigned ttl,
427 			       const pj_in_addr *ip_addr);
428 
429 /**
430  * Initialize DNS record as DNS AAAA record.
431  *
432  * @param rec		The DNS resource record to be initialized as DNS
433  *			AAAA record.
434  * @param res_name	Resource name.
435  * @param dnsclass	DNS class.
436  * @param ttl		Resource TTL value.
437  * @param ip_addr	Host address.
438  */
439 PJ_DECL(void) pj_dns_init_aaaa_rr(pj_dns_parsed_rr *rec,
440 				  const pj_str_t *res_name,
441 				  unsigned dnsclass,
442 				  unsigned ttl,
443 				  const pj_in6_addr *ip_addr);
444 
445 /**
446  * Dump DNS packet to standard log.
447  *
448  * @param res		The DNS packet.
449  */
450 PJ_DECL(void) pj_dns_dump_packet(const pj_dns_parsed_packet *res);
451 
452 
453 /**
454  * @}
455  */
456 
457 PJ_END_DECL
458 
459 
460 #endif	/* __PJLIB_UTIL_DNS_H__ */
461 
462