[svn-upgrade] Integrating new upstream version, iodine (0.5.0)
[debian/iodine.git] / src / dns.c
1 /*
2  * Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 #include <arpa/inet.h>
18 #include <arpa/nameser.h>
19 #ifdef DARWIN
20 #include <arpa/nameser8_compat.h>
21 #endif
22 #include <time.h>
23 #include <err.h>
24 #include <stdio.h>
25 #include <stdint.h>
26 #include <stdlib.h>
27 #include <stdint.h>
28 #include <string.h>
29 #include <ctype.h>
30
31 #include "dns.h"
32 #include "encoding.h"
33 #include "read.h"
34
35 int
36 dns_encode(char *buf, size_t buflen, struct query *q, qr_t qr, char *data, size_t datalen)
37 {
38         HEADER *header;
39         short name;
40         char *p;
41         int len;
42
43         memset(buf, 0, buflen);
44         
45         header = (HEADER*)buf;
46         
47         header->id = htons(q->id);
48         header->qr = (qr == QR_ANSWER);
49         header->opcode = 0;
50         header->aa = (qr == QR_ANSWER);
51         header->tc = 0;
52         header->rd = (qr == QR_QUERY);
53         header->ra = 0;
54
55         p = buf + sizeof(HEADER);
56
57         switch (qr) {
58         case QR_ANSWER:
59                 header->ancount = htons(1);
60                 header->qdcount = htons(1);
61         
62                 name = 0xc000 | ((p - buf) & 0x3fff);
63
64                 putname(&p, sizeof(q->name), q->name);
65
66                 putshort(&p, q->type);
67                 putshort(&p, C_IN);
68
69                 putshort(&p, name);     
70                 putshort(&p, q->type);
71                 putshort(&p, C_IN);
72                 putlong(&p, 0);
73
74                 putshort(&p, datalen);
75                 putdata(&p, data, datalen);
76                 break;
77         case QR_QUERY:
78                 header->qdcount = htons(1);
79                 header->arcount = htons(1);
80         
81                 putname(&p, datalen, data);
82
83                 putshort(&p, q->type);
84                 putshort(&p, C_IN);
85
86                 /* EDNS0 */
87                 putbyte(&p, 0x00);    /* Root */
88                 putshort(&p, 0x0029); /* OPT */
89                 putshort(&p, 0x1000); /* Payload size: 4096 */
90                 putshort(&p, 0x0000); /* Higher bits/edns version */
91                 putshort(&p, 0x8000); /* Z */
92                 putshort(&p, 0x0000); /* Data length */
93                 break;
94         }
95         
96         len = p - buf;
97
98         return len;
99 }
100
101 int
102 dns_encode_ns_response(char *buf, size_t buflen, struct query *q, char *topdomain)
103 {
104         HEADER *header;
105         int len;
106         short name;
107         short topname;
108         short nsname;
109         char *domain;
110         int domain_len;
111         char *p;
112
113         memset(buf, 0, buflen);
114         
115         header = (HEADER*)buf;
116         
117         header->id = htons(q->id);
118         header->qr = 1;
119         header->opcode = 0;
120         header->aa = 1;
121         header->tc = 0;
122         header->rd = 0;
123         header->ra = 0;
124
125         p = buf + sizeof(HEADER);
126
127         header->qdcount = htons(1);
128         header->ancount = htons(1);
129         header->arcount = htons(1);
130
131         /* pointer to start of name */
132         name = 0xc000 | ((p - buf) & 0x3fff);
133
134         domain = strstr(q->name, topdomain);
135         if (domain) {
136                 domain_len = (int) (domain - q->name); 
137         } else {
138                 return -1;
139         }
140         /* pointer to start of topdomain */
141         topname = 0xc000 | ((p - buf + domain_len) & 0x3fff);
142
143         /* Query section */
144         putname(&p, sizeof(q->name), q->name);  /* Name */
145         putshort(&p, q->type);                  /* Type */
146         putshort(&p, C_IN);                     /* Class */
147
148         /* Answer section */
149         putshort(&p, name);                     /* Name */
150         putshort(&p, q->type);                  /* Type */
151         putshort(&p, C_IN);                     /* Class */
152         putlong(&p, 0x3ea7d011);                /* TTL */
153         putshort(&p, 5);                        /* Data length */
154
155         /* pointer to ns.topdomain */
156         nsname = 0xc000 | ((p - buf) & 0x3fff);
157         putbyte(&p, 2);
158         putbyte(&p, 'n');
159         putbyte(&p, 's');
160         putshort(&p, topname);                  /* Name Server */
161
162         /* Additional data (A-record of NS server) */
163         putshort(&p, nsname);                   /* Name Server */
164         putshort(&p, T_A);                      /* Type */
165         putshort(&p, C_IN);                     /* Class */
166         putlong(&p, 0x3ea7d011);                /* TTL */
167         putshort(&p, 4);                        /* Data length */
168
169         /* ugly hack to output IP address */
170         domain = (char *) &q->destination;
171         putbyte(&p, *domain++);
172         putbyte(&p, *domain++);
173         putbyte(&p, *domain++);
174         putbyte(&p, *domain);
175
176         len = p - buf;
177         return len;
178 }
179
180 short
181 dns_get_id(char *packet, size_t packetlen)
182 {
183         HEADER *header;
184         header = (HEADER*)packet;
185
186         if (packetlen < sizeof(HEADER))
187                 return 0;
188
189         return ntohs(header->id);
190 }
191
192 int
193 dns_decode(char *buf, size_t buflen, struct query *q, qr_t qr, char *packet, size_t packetlen)
194 {
195         char name[QUERY_NAME_SIZE];
196         char rdata[4*1024];
197         HEADER *header;
198         short qdcount;
199         short ancount;
200         uint32_t ttl;
201         short class;
202         short type;
203         char *data;
204         short rlen;
205         int id; 
206         int rv;
207
208         rv = 0;
209         header = (HEADER*)packet;
210
211         /* Reject short packets */
212         if (packetlen < sizeof(HEADER)) 
213                 return 0;
214         
215         if (header->qr != qr) {
216                 warnx("header->qr does not match the requested qr");
217                 return -1;
218         }
219
220         data = packet + sizeof(HEADER);
221         qdcount = ntohs(header->qdcount);
222         ancount = ntohs(header->ancount);
223         
224         id = ntohs(header->id);
225         id = id & 0xFFFF; /* Kill any sign extension */
226                 
227         rlen = 0;
228
229         switch (qr) {
230         case QR_ANSWER:
231                 if(qdcount != 1 || ancount != 1) {
232                         switch (header->rcode) {
233                         case REFUSED:
234                                 warnx("Got REFUSED as reply");
235                                 break;
236
237                         case NOTIMP:
238                                 warnx("Got NOTIMP as reply");
239                                 break;
240
241                         case NXDOMAIN:
242                                 warnx("Got NXDOMAIN as reply");
243                                 break;
244
245                         case SERVFAIL:
246                                 warnx("Got SERVFAIL as reply");
247                                 break;
248
249                         case NOERROR:
250                         default:
251                                 warnx("no query or answer in reply packet");
252                                 break;
253                         }
254                         return -1;
255                 }
256
257                 if (q != NULL) 
258                         q->id = id;
259
260                 readname(packet, packetlen, &data, name, sizeof(name));
261                 readshort(packet, &data, &type);
262                 readshort(packet, &data, &class);
263                 
264                 readname(packet, packetlen, &data, name, sizeof(name));
265                 readshort(packet, &data, &type);
266                 readshort(packet, &data, &class);
267                 readlong(packet, &data, &ttl);
268                 readshort(packet, &data, &rlen);
269                 rv = MIN(rlen, sizeof(rdata));
270                 rv = readdata(packet, &data, rdata, rv);
271
272                 if(type == T_NULL && rv >= 2 && buf) {
273                         rv = MIN(rv, buflen);
274                         memcpy(buf, rdata, rv);
275                 }
276                 break;
277         case QR_QUERY:
278                 if (qdcount != 1) {
279                         warnx("no question section in name query");
280                         return -1;
281                 }
282
283                 readname(packet, packetlen, &data, name, sizeof(name) - 1);
284                 name[sizeof(name)-1] = '\0';
285                 readshort(packet, &data, &type);
286                 readshort(packet, &data, &class);
287
288                 strncpy(q->name, name, sizeof(q->name));
289                 q->name[sizeof(q->name) - 1] = '\0';
290                 q->type = type;
291                 q->id = id;
292
293                 rv = strlen(q->name);
294                 break;
295         }
296
297         return rv;
298 }
299