6921f0d341c82e92089d300beaa5e5e9b99f5d5d
[oweals/gnunet.git] / src / dns / dnsparser.c
1 #include "platform.h"
2 #include "gnunet_dnsparser_lib.h"
3
4 /**
5  * Parse a name from DNS to a normal .-delimited, 0-terminated string.
6  *
7  * @param d The destination of the name. Should have at least 255 bytes allocated.
8  * @param src The DNS-Packet
9  * @param idx The offset inside the Packet from which on the name should be read
10  * @returns The offset of the first unparsed byte (the byte right behind the name)
11  */
12 static unsigned int
13 parse_dns_name (char *d, const unsigned char *src, unsigned short idx)
14 {                               /*{{{ */
15   char *dest = d;
16
17   int len = src[idx++];
18
19   while (len != 0)
20   {
21     if (len & 0xC0)
22     {                           /* Compressed name, offset in this and the next octet */
23       unsigned short offset = ((len & 0x3F) << 8) | src[idx++];
24
25       parse_dns_name (dest, src, offset - 12);  /* 12 for the Header of the DNS-Packet, idx starts at 0 which is 12 bytes from the start of the packet */
26       return idx;
27     }
28     memcpy (dest, src + idx, len);
29     idx += len;
30     dest += len;
31     *dest = '.';
32     dest++;
33     len = src[idx++];
34   };
35   *dest = 0;
36
37   return idx;
38 }
39
40 /*}}}*/
41
42 /**
43  * Parse a complete DNS-Record from raw DNS-data to a struct dns_record
44  *
45  * @param data The DNS-data
46  * @param dst Pointer to count pointers; individual pointers will be allocated
47  * @param count Number of records to parse
48  * @param idx The offset inside the Packet from which on the name should be read
49  * @returns The offset of the first unparsed byte (the byte right behind the last record)
50  */
51 static unsigned short
52 parse_dns_record (unsigned char *data,  /*{{{ */
53                   struct dns_record **dst, unsigned short count,
54                   unsigned short idx)
55 {
56   int i;
57   unsigned short _idx;
58
59   for (i = 0; i < count; i++)
60   {
61     dst[i] = GNUNET_malloc (sizeof (struct dns_record));
62     dst[i]->name = alloca (255);        // see RFC1035, no name can be longer than this.
63     char *name = dst[i]->name;
64
65     _idx = parse_dns_name (name, data, idx);
66     dst[i]->namelen = _idx - idx;
67
68     dst[i]->name = GNUNET_malloc (dst[i]->namelen);
69     memcpy (dst[i]->name, name, dst[i]->namelen);
70
71     idx = _idx;
72
73     dst[i]->type = *((unsigned short *) (data + idx));
74     idx += 2;
75     dst[i]->class = *((unsigned short *) (data + idx));
76     idx += 2;
77     dst[i]->ttl = *((unsigned int *) (data + idx));
78     idx += 4;
79     dst[i]->data_len = *((unsigned short *) (data + idx));
80     idx += 2;
81     dst[i]->data = GNUNET_malloc (ntohs (dst[i]->data_len));
82     memcpy (dst[i]->data, data + idx, ntohs (dst[i]->data_len));
83     idx += ntohs (dst[i]->data_len);
84   }
85   return idx;
86 }                               /*}}} */
87
88 /**
89  * Parse a raw DNS-Packet into an usable struct
90  */
91 struct dns_pkt_parsed *
92 parse_dns_packet (struct dns_pkt *pkt)
93 {                               /*{{{ */
94   struct dns_pkt_parsed *ppkt = GNUNET_malloc (sizeof (struct dns_pkt_parsed));
95
96   memcpy (&ppkt->s, &pkt->s, sizeof pkt->s);
97
98   unsigned short qdcount = ntohs (ppkt->s.qdcount);
99   unsigned short ancount = ntohs (ppkt->s.ancount);
100   unsigned short nscount = ntohs (ppkt->s.nscount);
101   unsigned short arcount = ntohs (ppkt->s.arcount);
102
103   ppkt->queries = GNUNET_malloc (qdcount * sizeof (struct dns_query *));
104   ppkt->answers = GNUNET_malloc (ancount * sizeof (struct dns_record *));
105   ppkt->nameservers = GNUNET_malloc (nscount * sizeof (struct dns_record *));
106   ppkt->additional = GNUNET_malloc (arcount * sizeof (struct dns_record *));
107
108   unsigned short idx = 0, _idx; /* This keeps track how far we have parsed the data */
109
110   /* Parse the Query */
111   int i;
112
113   for (i = 0; i < qdcount; i++)
114   {                             /*{{{ */
115     ppkt->queries[i] = GNUNET_malloc (sizeof (struct dns_query));
116     char *name = alloca (255);  /* see RFC1035, it can't be more than this. */
117
118     _idx = parse_dns_name (name, pkt->data, idx);
119     ppkt->queries[i]->namelen = _idx - idx;
120     idx = _idx;
121
122     ppkt->queries[i]->name = GNUNET_malloc (ppkt->queries[i]->namelen);
123     memcpy (ppkt->queries[i]->name, name, ppkt->queries[i]->namelen);
124
125     ppkt->queries[i]->qtype = *((unsigned short *) (pkt->data + idx));
126     idx += 2;
127     ppkt->queries[i]->qclass = *((unsigned short *) (pkt->data + idx));
128     idx += 2;
129   }
130   /*}}} */
131   idx = parse_dns_record (pkt->data, ppkt->answers, ancount, idx);
132   idx = parse_dns_record (pkt->data, ppkt->nameservers, nscount, idx);
133   idx = parse_dns_record (pkt->data, ppkt->additional, arcount, idx);
134   return ppkt;
135 }                               /*}}} */
136
137 static void
138 unparse_dns_name (char *dest, char *src, size_t len)
139 {
140   char *b = dest;
141   char cnt = 0;
142
143   dest++;
144   while (*src != 0)
145   {
146     while (*src != '.' && *src != 0)
147     {
148       *dest = *src;
149       src++;
150       dest++;
151       cnt++;
152     }
153     *b = cnt;
154     cnt = 0;
155     b = dest;
156     dest++;
157     src++;
158   }
159   *b = 0;
160 }
161
162 struct dns_pkt *
163 unparse_dns_packet (struct dns_pkt_parsed *ppkt)
164 {
165   size_t size = sizeof (struct dns_pkt) - 1;
166   int i;
167
168   for (i = 0; i < ntohs (ppkt->s.qdcount); i++)
169     size += ppkt->queries[i]->namelen + 1;
170
171   for (i = 0; i < ntohs (ppkt->s.ancount); i++)
172   {
173     size += ppkt->answers[i]->namelen + 1;
174     size += ppkt->answers[i]->data_len;
175   }
176   for (i = 0; i < ntohs (ppkt->s.nscount); i++)
177   {
178     size += ppkt->nameservers[i]->namelen + 1;
179     size += ppkt->nameservers[i]->data_len;
180   }
181   for (i = 0; i < ntohs (ppkt->s.arcount); i++)
182   {
183     size += ppkt->additional[i]->namelen + 1;
184     size += ppkt->additional[i]->data_len;
185   }
186
187   size +=
188       4 * ntohs (ppkt->s.qdcount) + 10 * (ntohs (ppkt->s.ancount) +
189                                           ntohs (ppkt->s.arcount) +
190                                           ntohs (ppkt->s.nscount));
191
192   struct dns_pkt *pkt = GNUNET_malloc (size);
193   char *pkt_c = (char *) pkt;
194
195   memcpy (&pkt->s, &ppkt->s, sizeof ppkt->s);
196   size_t idx = sizeof ppkt->s;
197
198   for (i = 0; i < ntohs (ppkt->s.qdcount); i++)
199   {
200     unparse_dns_name (&pkt_c[idx], ppkt->queries[i]->name,
201                       ppkt->queries[i]->namelen);
202     idx += ppkt->queries[i]->namelen;
203     struct dns_query_line *d = (struct dns_query_line *) &pkt_c[idx];
204
205     d->class = ppkt->queries[i]->qclass;
206     d->type = ppkt->queries[i]->qtype;
207     idx += sizeof (struct dns_query_line);
208   }
209
210   for (i = 0; i < ntohs (ppkt->s.ancount); i++)
211   {
212     unparse_dns_name (&pkt_c[idx], ppkt->answers[i]->name,
213                       ppkt->answers[i]->namelen);
214     idx += ppkt->answers[i]->namelen;
215     struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
216
217     r->type = ppkt->answers[i]->type;
218     r->class = ppkt->answers[i]->class;
219     r->ttl = ppkt->answers[i]->ttl;
220     r->data_len = ppkt->answers[i]->data_len;
221     idx += sizeof (struct dns_record_line);
222     memcpy (&r->data, ppkt->answers[i]->data, ppkt->answers[i]->data_len);
223     idx += ppkt->answers[i]->data_len;
224   }
225
226   for (i = 0; i < ntohs (ppkt->s.nscount); i++)
227   {
228     unparse_dns_name (&pkt_c[idx], ppkt->nameservers[i]->name,
229                       ppkt->nameservers[i]->namelen);
230     idx += ppkt->nameservers[i]->namelen;
231     struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
232
233     r->type = ppkt->nameservers[i]->type;
234     r->class = ppkt->nameservers[i]->class;
235     r->ttl = ppkt->nameservers[i]->ttl;
236     r->data_len = ppkt->nameservers[i]->data_len;
237     idx += sizeof (struct dns_record_line);
238     memcpy (&r->data, ppkt->nameservers[i]->data,
239             ppkt->nameservers[i]->data_len);
240     idx += ppkt->nameservers[i]->data_len;
241   }
242
243   for (i = 0; i < ntohs (ppkt->s.arcount); i++)
244   {
245     unparse_dns_name (&pkt_c[idx], ppkt->additional[i]->name,
246                       ppkt->additional[i]->namelen);
247     idx += ppkt->additional[i]->namelen;
248     struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
249
250     r->type = ppkt->additional[i]->type;
251     r->class = ppkt->additional[i]->class;
252     r->ttl = ppkt->additional[i]->ttl;
253     r->data_len = ppkt->additional[i]->data_len;
254     idx += sizeof (struct dns_record_line);
255     memcpy (&r->data, ppkt->additional[i]->data, ppkt->additional[i]->data_len);
256     idx += ppkt->additional[i]->data_len;
257   }
258
259   return pkt;
260 }
261
262 void
263 free_parsed_dns_packet (struct dns_pkt_parsed *ppkt)
264 {
265   unsigned short qdcount = ntohs (ppkt->s.qdcount);
266   unsigned short ancount = ntohs (ppkt->s.ancount);
267   unsigned short nscount = ntohs (ppkt->s.nscount);
268   unsigned short arcount = ntohs (ppkt->s.arcount);
269
270   int i;
271
272   for (i = 0; i < qdcount; i++)
273   {
274     GNUNET_free (ppkt->queries[i]->name);
275     GNUNET_free (ppkt->queries[i]);
276   }
277   GNUNET_free (ppkt->queries);
278   for (i = 0; i < ancount; i++)
279   {
280     GNUNET_free (ppkt->answers[i]->name);
281     GNUNET_free (ppkt->answers[i]->data);
282     GNUNET_free (ppkt->answers[i]);
283   }
284   GNUNET_free (ppkt->answers);
285   for (i = 0; i < nscount; i++)
286   {
287     GNUNET_free (ppkt->nameservers[i]->name);
288     GNUNET_free (ppkt->nameservers[i]->data);
289     GNUNET_free (ppkt->nameservers[i]);
290   }
291   GNUNET_free (ppkt->nameservers);
292   for (i = 0; i < arcount; i++)
293   {
294     GNUNET_free (ppkt->additional[i]->name);
295     GNUNET_free (ppkt->additional[i]->data);
296     GNUNET_free (ppkt->additional[i]);
297   }
298   GNUNET_free (ppkt->additional);
299   GNUNET_free (ppkt);
300 }