1 /*
2 ** Copyright (C) 2006 Olivier DEMBOUR
3 ** $Id: dns.c,v 1.16.4.2 2010/02/11 16:06:37 dembour Exp $
4 **
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 
21 #include <string.h>
22 #include <stdio.h>
23 
24 #include "packet.h"
25 #include "dns.h"
26 #include "myerror.h"
27 #include "mystrnlen.h"
28 #include "debug.h"
29 
30 
31 const char	*dns_error[MAX_DNS_ERROR] = {
32   "No error",
33   "Format error",
34   "Server failure",
35   "Name error",
36   "Not implemented",
37   "Request refused",
38 };
39 
40 
41 /**
42  * @brief jump at the end of the qname section
43  * @param[in] ptr where to search
44  * @param[in] maxlen max size
45  */
46 
jump_qname(void * ptr,int maxlen)47 void		*jump_qname(void *ptr, int maxlen)
48 {
49   char		*name;
50 
51   if (! maxlen)
52     return (0);
53   name = ptr;
54   while ((*name) && (maxlen--))
55     {
56       if ((*name & COMPRESS_FLAG_CHAR) == COMPRESS_FLAG_CHAR)
57 	{
58 	  if (maxlen-1)
59 	    return (name + 2);
60 	  return (0);
61 	}
62       name++;
63     }
64   return ((maxlen-1) ? name + 1 : 0);
65 }
66 
67 
jump_edns(void * buffer,int max_len)68 void			*jump_edns(void *buffer, int max_len)
69 {
70   struct dns_hdr	*hdr;
71   void			*where;
72   uint16_t		records;
73   struct rr_hdr		*aarecord;
74 
75   hdr = (struct dns_hdr *) buffer;
76   if (!(where = jump_end_answer(buffer, max_len)))
77     return (0);
78   /* jump additional records */
79   for (records =  GET_16(&hdr->nscount); records; records--)
80     {
81       if (!(where = jump_qname(where, max_len - (where - (void *)hdr))))
82 	return (0);
83     }
84   aarecord = where;
85   for (records =  GET_16(&hdr->arcount); records; records--)
86     {
87       if ((max_len - (where - (void *)hdr) -sizeof(struct add_record)) > max_len)
88 	return (0);
89       if (GET_16(&aarecord->type) ==  TYPE_EDNS)
90 	return (aarecord);
91       aarecord++;
92     }
93   return (0);
94 }
95 
96 /**
97  * @brief jump at the end of the answer
98  * @param[in] ptr where to search
99  * @param[in] maxlen max size
100  */
101 
jump_end_answer(void * buffer,int max_len)102 void			*jump_end_answer(void *buffer, int max_len)
103 {
104   struct dns_hdr	*hdr;
105   void			*where;
106   uint16_t		records;
107   struct rr_hdr		*rr;
108   uint16_t		len;
109   uint16_t		available_len;
110 
111   hdr = buffer;
112   if (!GET_16(&hdr->qdcount))
113     return (0);
114   if (!(rr = where = jump_end_query(hdr, GET_16(&hdr->qdcount), max_len)))
115     return (0);
116   available_len =  max_len - (uint16_t) ((void *)where - (void *)hdr);
117   for (records =  GET_16(&hdr->ancount); records; records--)
118     {
119       available_len =  max_len - (uint16_t) ((void *)rr - (void*)hdr);
120       if (!(rr = jump_qname(where, available_len)))
121 	return (0);
122       len = GET_16(&rr->rdlength);
123       if (( ((void *)rr - buffer) + len + RR_HDR_SIZE) > max_len)
124 	return (0);
125       rr =  (struct rr_hdr *) (JUMP_RR_HDR(rr) + len);
126       where = rr;
127     }
128   return (rr);
129 }
130 
get_edns_size(void * buffer,int max_len)131 uint16_t		get_edns_size(void *buffer, int max_len)
132 {
133   struct add_record	*edns;
134 
135   if (!(edns = jump_edns(buffer, max_len)))
136     return (0);
137   return (GET_16(&edns->payload_size));
138 }
139 
140 
141 /**
142  * @brief jump at the end of the query
143  * @param[in] ptr where to search
144  * @param[in] nb max number of requests
145  * @param[in] maxlen max size
146  */
147 
jump_end_query(void * buffer,int nb,int max_len)148 void			*jump_end_query(void *buffer, int nb, int max_len)
149 {
150   void			*tmp;
151   void			*max_ptr;
152   int			len;
153 
154   max_ptr = ((char *)buffer) + max_len;
155   tmp = ((char *)buffer) + DNS_HDR_SIZE;
156   while ((nb--) && (tmp <= max_ptr))
157     {
158       if ((len = mystrnlen(tmp , MAX_HOST_NAME_ENCODED+1)) > MAX_HOST_NAME_ENCODED)
159 	{
160 	  MYERROR("Host name too long (%d)\n", len);
161 	  return (0);
162 	}
163       tmp = jump_qname(tmp, max_ptr-tmp) +  REQ_HDR_SIZE;
164     }
165   return ((tmp <= max_ptr) ? tmp : 0);
166 }
167 
search_dot(char * buffer)168 static unsigned int	search_dot(char *buffer)
169 {
170   unsigned int		len = 0;
171 
172   while ((buffer[len] != 0)
173 	 &&  (buffer[len] != '.'))
174       len++;
175   return (len);
176 }
177 
178 /**
179  * @brief encode a data to the qname format
180  * @param[out] data
181  **/
182 
dns_encode(char * data)183 void		dns_encode(char *data)
184 {
185   char		buffer2[MAX_EDNS_LEN];
186   int		len;
187   char		*buffer = buffer2;
188 
189   strncpy(buffer, data, MAX_DNS_LEN-1);
190   do
191     {
192       len = search_dot(buffer);
193       if (len < 64)
194 	{
195 	  *data = (char) len;
196 	  if (len)
197 	    strncpy(data + 1, buffer, len);
198 	  if (buffer[len])
199 	    buffer++;
200 	}
201       else
202 	{
203 	  len = 63;
204 	  *data = (char) len;
205 	  strncpy(data + 1, buffer, len);
206 	}
207       buffer += len;
208       data += len + 1;
209     } while (len);
210 }
211 
212 /**
213  * @brief simple qname decoder, do not strip anything
214  * @param[in] input data
215  * @param[out] output where to write
216  * @param[in] max_len maximum len
217  **/
218 
219 
dns_simple_decode(char * input,char * output,int max_len)220 void		dns_simple_decode(char *input, char *output, int max_len)
221 {
222   uint8_t	len;
223   char		*ptr;
224   int		total_len =0;
225 
226   ptr = input;
227   *output = 0;
228   while (*ptr)
229     {
230       len = (uint8_t) *ptr;
231       /* compression not supported */
232       if (len > 63)
233 	len = 0;
234       total_len +=len;
235       if (++total_len > max_len)
236 	break;
237       output[total_len] = 0;
238       if (!len)
239 	break;
240       strncat(output, ptr + 1, len++);
241       output[total_len-1] = '.';
242       ptr += len;
243     }
244   if (total_len > 0)
245   output[total_len-1] = 0;
246 }
247 
248 /**
249  * @brief simple qname decoder, but strip 'fake' dot
250  * @param[in] input data
251  * @param[out] output where to write
252  * @param[in] max_len maximum len
253  **/
254 
dns_simple_decode_strip_dot(char * input,char * output,int max_len)255 void		dns_simple_decode_strip_dot(char *input, char *output, int max_len)
256 {
257   uint8_t	len;
258   char		*ptr;
259   int		total_len =0;
260 
261   ptr = input;
262   *output = 0;
263   while (*ptr)
264     {
265       len = (uint8_t) *ptr;
266       /* compression not supported */
267       if (len > 63)
268 	len = 0;
269       total_len +=len;
270       if (total_len > max_len)
271 	{
272 	  MYERROR("Error while decoding reply max_len was %d total len = %d\n", max_len,
273 		  total_len);
274 	  break;
275 	}
276       strncat(output, ptr + 1, len);
277       output[total_len] = 0;
278       ptr += (len + 1);
279     }
280 }
281