prevent double free
[oweals/gnunet.git] / src / vpn / gnunet-dns-parser.c
1 #include "platform.h"
2 #include "gnunet-dns-parser.h"
3 #include "gnunet-vpn-packet.h"
4
5 /**
6  * Parse a name from DNS to a normal .-delimited, 0-terminated string.
7  *
8  * @param d The destination of the name. Should have at least 255 bytes allocated.
9  * @param src The DNS-Packet
10  * @param idx The offset inside the Packet from which on the name should be read
11  * @returns The offset of the first unparsed byte (the byte right behind the name)
12  */
13 static unsigned int
14 parse_dns_name(char* d, const unsigned char* src, unsigned short idx) {/*{{{*/
15     char* dest = d;
16
17     int len = src[idx++];
18     while (len != 0)
19       {
20         if (len & 0xC0)
21           { /* Compressed name, offset in this and the next octet */
22             unsigned short offset = ((len & 0x3F) << 8) | src[idx++];
23             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 */
24             return idx;
25           }
26         memcpy(dest, src+idx, len);
27         idx += len;
28         dest += len;
29         *dest = '.';
30         dest++;
31         len = src[idx++];
32       };
33     *dest = 0;
34
35     return idx;
36 }
37 /*}}}*/
38
39 /**
40  * Parse a complete DNS-Record from raw DNS-data to a struct dns_record
41  *
42  * @param data The DNS-data
43  * @param dst Pointer to count pointers; individual pointers will be allocated
44  * @param count Number of records to parse
45  * @param idx The offset inside the Packet from which on the name should be read
46  * @returns The offset of the first unparsed byte (the byte right behind the last record)
47  */
48 static unsigned short
49 parse_dns_record(unsigned char* data, /*{{{*/
50                  struct dns_record** dst,
51                  unsigned short count,
52                  unsigned short idx) {
53     int i;
54     unsigned short _idx;
55     for (i = 0; i < count; i++) {
56         dst[i] = GNUNET_malloc(sizeof(struct dns_record));
57         dst[i]->name = alloca(255); // see RFC1035, no name can be longer than this.
58         char* name = dst[i]->name;
59
60         _idx = parse_dns_name(name, data, idx);
61         dst[i]->namelen = _idx - idx;
62
63         dst[i]->name = GNUNET_malloc(dst[i]->namelen);
64         memcpy(dst[i]->name, name, dst[i]->namelen);
65
66         idx = _idx;
67
68         dst[i]->type = *((unsigned short*)(data+idx));
69         idx += 2;
70         dst[i]->class = *((unsigned short*)(data+idx));
71         idx += 2;
72         dst[i]->ttl = *((unsigned int*)(data+idx));
73         idx += 4;
74         dst[i]->data_len = *((unsigned short*)(data+idx));
75         idx += 2;
76         dst[i]->data = GNUNET_malloc(ntohs(dst[i]->data_len));
77         memcpy(dst[i]->data, data+idx, ntohs(dst[i]->data_len));
78         idx += ntohs(dst[i]->data_len);
79     }
80     return idx;
81 }/*}}}*/
82
83 /**
84  * Parse a raw DNS-Packet into an usable struct
85  */
86 struct dns_pkt_parsed*
87 parse_dns_packet(struct dns_pkt* pkt) {/*{{{*/
88     struct dns_pkt_parsed* ppkt = GNUNET_malloc(sizeof(struct dns_pkt_parsed));
89     memcpy(&ppkt->s, &pkt->s, sizeof pkt->s);
90
91     unsigned short qdcount = ntohs(ppkt->s.qdcount);
92     unsigned short ancount = ntohs(ppkt->s.ancount);
93     unsigned short nscount = ntohs(ppkt->s.nscount);
94     unsigned short arcount = ntohs(ppkt->s.arcount);
95
96     ppkt->queries = GNUNET_malloc(qdcount*sizeof(struct dns_query*));
97     ppkt->answers = GNUNET_malloc(ancount*sizeof(struct dns_record*));
98     ppkt->nameservers = GNUNET_malloc(nscount*sizeof(struct dns_record*));
99     ppkt->additional = GNUNET_malloc(arcount*sizeof(struct dns_record*));
100
101     unsigned short idx = 0, _idx; /* This keeps track how far we have parsed the data */
102
103     /* Parse the Query */
104     int i;
105     for (i = 0; i < qdcount; i++)
106       { /*{{{*/
107         ppkt->queries[i] = GNUNET_malloc(sizeof(struct dns_query));
108         char* name = alloca(255); /* see RFC1035, it can't be more than this. */
109
110         _idx = parse_dns_name(name, pkt->data, idx);
111         ppkt->queries[i]->namelen = _idx - idx;
112         idx = _idx;
113
114         ppkt->queries[i]->name = GNUNET_malloc(ppkt->queries[i]->namelen);
115         memcpy(ppkt->queries[i]->name, name, ppkt->queries[i]->namelen);
116
117         ppkt->queries[i]->qtype = *((unsigned short*)(pkt->data+idx));
118         idx += 2;
119         ppkt->queries[i]->qclass = *((unsigned short*)(pkt->data+idx));
120         idx += 2;
121     }
122     /*}}}*/
123     idx = parse_dns_record(pkt->data, ppkt->answers, ancount, idx);
124     idx = parse_dns_record(pkt->data, ppkt->nameservers, nscount, idx);
125     idx = parse_dns_record(pkt->data, ppkt->additional, arcount, idx);
126     return ppkt;
127 }/*}}}*/
128
129 void
130 free_parsed_dns_packet(struct dns_pkt_parsed* ppkt) {
131     unsigned short qdcount = ntohs(ppkt->s.qdcount);
132     unsigned short ancount = ntohs(ppkt->s.ancount);
133     unsigned short nscount = ntohs(ppkt->s.nscount);
134     unsigned short arcount = ntohs(ppkt->s.arcount);
135
136     int i;
137     for (i = 0; i < qdcount; i++) {
138         GNUNET_free(ppkt->queries[i]->name);
139         GNUNET_free(ppkt->queries[i]);
140     }
141     GNUNET_free(ppkt->queries);
142     for (i = 0; i < ancount; i++) {
143         GNUNET_free(ppkt->answers[i]->name);
144         GNUNET_free(ppkt->answers[i]->data);
145         GNUNET_free(ppkt->answers[i]);
146     }
147     GNUNET_free(ppkt->answers);
148     for (i = 0; i < nscount; i++) {
149         GNUNET_free(ppkt->nameservers[i]->name);
150         GNUNET_free(ppkt->nameservers[i]->data);
151         GNUNET_free(ppkt->nameservers[i]);
152     }
153     GNUNET_free(ppkt->nameservers);
154     for (i = 0; i < arcount; i++) {
155         GNUNET_free(ppkt->additional[i]->name);
156         GNUNET_free(ppkt->additional[i]->data);
157         GNUNET_free(ppkt->additional[i]);
158     }
159     GNUNET_free(ppkt->additional);
160     GNUNET_free(ppkt);
161 }