diff options
Diffstat (limited to 'src/dns/dnsparser.c')
-rw-r--r-- | src/dns/dnsparser.c | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/src/dns/dnsparser.c b/src/dns/dnsparser.c new file mode 100644 index 000000000..6921f0d34 --- /dev/null +++ b/src/dns/dnsparser.c | |||
@@ -0,0 +1,300 @@ | |||
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 | } | ||