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