1 /* $NetBSD: dns_rr.c,v 1.2 2017/02/14 01:16:44 christos Exp $ */
2
3 /*++
4 /* NAME
5 /* dns_rr 3
6 /* SUMMARY
7 /* resource record memory and list management
8 /* SYNOPSIS
9 /* #include <dns.h>
10 /*
11 /* DNS_RR *dns_rr_create(qname, rname, type, class, ttl, preference,
12 /* data, data_len)
13 /* const char *qname;
14 /* const char *rname;
15 /* unsigned short type;
16 /* unsigned short class;
17 /* unsigned int ttl;
18 /* unsigned preference;
19 /* const char *data;
20 /* size_t data_len;
21 /*
22 /* void dns_rr_free(list)
23 /* DNS_RR *list;
24 /*
25 /* DNS_RR *dns_rr_copy(record)
26 /* DNS_RR *record;
27 /*
28 /* DNS_RR *dns_rr_append(list, record)
29 /* DNS_RR *list;
30 /* DNS_RR *record;
31 /*
32 /* DNS_RR *dns_rr_sort(list, compar)
33 /* DNS_RR *list
34 /* int (*compar)(DNS_RR *, DNS_RR *);
35 /*
36 /* int dns_rr_compare_pref_ipv6(DNS_RR *a, DNS_RR *b)
37 /* DNS_RR *list
38 /* DNS_RR *list
39 /*
40 /* int dns_rr_compare_pref_ipv4(DNS_RR *a, DNS_RR *b)
41 /* DNS_RR *list
42 /* DNS_RR *list
43 /*
44 /* int dns_rr_compare_pref_any(DNS_RR *a, DNS_RR *b)
45 /* DNS_RR *list
46 /* DNS_RR *list
47 /*
48 /* DNS_RR *dns_rr_shuffle(list)
49 /* DNS_RR *list;
50 /*
51 /* DNS_RR *dns_rr_remove(list, record)
52 /* DNS_RR *list;
53 /* DNS_RR *record;
54 /* DESCRIPTION
55 /* The routines in this module maintain memory for DNS resource record
56 /* information, and maintain lists of DNS resource records.
57 /*
58 /* dns_rr_create() creates and initializes one resource record.
59 /* The \fIqname\fR field specifies the query name.
60 /* The \fIrname\fR field specifies the reply name.
61 /* \fIpreference\fR is used for MX records; \fIdata\fR is a null
62 /* pointer or specifies optional resource-specific data;
63 /* \fIdata_len\fR is the amount of resource-specific data.
64 /*
65 /* dns_rr_free() releases the resource used by of zero or more
66 /* resource records.
67 /*
68 /* dns_rr_copy() makes a copy of a resource record.
69 /*
70 /* dns_rr_append() appends a resource record to a (list of) resource
71 /* record(s).
72 /* A null input list is explicitly allowed.
73 /*
74 /* dns_rr_sort() sorts a list of resource records into ascending
75 /* order according to a user-specified criterion. The result is the
76 /* sorted list.
77 /*
78 /* dns_rr_compare_pref_XXX() are dns_rr_sort() helpers to sort
79 /* records by their MX preference and by their address family.
80 /*
81 /* dns_rr_shuffle() randomly permutes a list of resource records.
82 /*
83 /* dns_rr_remove() removes the specified record from the specified list.
84 /* The updated list is the result value.
85 /* The record MUST be a list member.
86 /* LICENSE
87 /* .ad
88 /* .fi
89 /* The Secure Mailer license must be distributed with this software.
90 /* AUTHOR(S)
91 /* Wietse Venema
92 /* IBM T.J. Watson Research
93 /* P.O. Box 704
94 /* Yorktown Heights, NY 10598, USA
95 /*--*/
96
97 /* System library. */
98
99 #include <sys_defs.h>
100 #include <string.h>
101 #include <stdlib.h>
102
103 /* Utility library. */
104
105 #include <msg.h>
106 #include <mymalloc.h>
107 #include <myrand.h>
108
109 /* DNS library. */
110
111 #include "dns.h"
112
113 /* dns_rr_create - fill in resource record structure */
114
dns_rr_create(const char * qname,const char * rname,ushort type,ushort class,unsigned int ttl,unsigned pref,const char * data,size_t data_len)115 DNS_RR *dns_rr_create(const char *qname, const char *rname,
116 ushort type, ushort class,
117 unsigned int ttl, unsigned pref,
118 const char *data, size_t data_len)
119 {
120 DNS_RR *rr;
121
122 rr = (DNS_RR *) mymalloc(sizeof(*rr) + data_len - 1);
123 rr->qname = mystrdup(qname);
124 rr->rname = mystrdup(rname);
125 rr->type = type;
126 rr->class = class;
127 rr->ttl = ttl;
128 rr->dnssec_valid = 0;
129 rr->pref = pref;
130 if (data && data_len > 0)
131 memcpy(rr->data, data, data_len);
132 rr->data_len = data_len;
133 rr->next = 0;
134 return (rr);
135 }
136
137 /* dns_rr_free - destroy resource record structure */
138
dns_rr_free(DNS_RR * rr)139 void dns_rr_free(DNS_RR *rr)
140 {
141 if (rr) {
142 if (rr->next)
143 dns_rr_free(rr->next);
144 myfree(rr->qname);
145 myfree(rr->rname);
146 myfree((void *) rr);
147 }
148 }
149
150 /* dns_rr_copy - copy resource record */
151
dns_rr_copy(DNS_RR * src)152 DNS_RR *dns_rr_copy(DNS_RR *src)
153 {
154 ssize_t len = sizeof(*src) + src->data_len - 1;
155 DNS_RR *dst;
156
157 /*
158 * Combine struct assignment and data copy in one block copy operation.
159 */
160 dst = (DNS_RR *) mymalloc(len);
161 memcpy((void *) dst, (void *) src, len);
162 dst->qname = mystrdup(src->qname);
163 dst->rname = mystrdup(src->rname);
164 dst->next = 0;
165 return (dst);
166 }
167
168 /* dns_rr_append - append resource record to list */
169
dns_rr_append(DNS_RR * list,DNS_RR * rr)170 DNS_RR *dns_rr_append(DNS_RR *list, DNS_RR *rr)
171 {
172 if (list == 0) {
173 list = rr;
174 } else {
175 list->next = dns_rr_append(list->next, rr);
176 }
177 return (list);
178 }
179
180 /* dns_rr_compare_pref_ipv6 - compare records by preference, ipv6 preferred */
181
dns_rr_compare_pref_ipv6(DNS_RR * a,DNS_RR * b)182 int dns_rr_compare_pref_ipv6(DNS_RR *a, DNS_RR *b)
183 {
184 if (a->pref != b->pref)
185 return (a->pref - b->pref);
186 #ifdef HAS_IPV6
187 if (a->type == b->type) /* 200412 */
188 return 0;
189 if (a->type == T_AAAA)
190 return (-1);
191 if (b->type == T_AAAA)
192 return (+1);
193 #endif
194 return 0;
195 }
196
197 /* dns_rr_compare_pref_ipv4 - compare records by preference, ipv4 preferred */
198
dns_rr_compare_pref_ipv4(DNS_RR * a,DNS_RR * b)199 int dns_rr_compare_pref_ipv4(DNS_RR *a, DNS_RR *b)
200 {
201 if (a->pref != b->pref)
202 return (a->pref - b->pref);
203 #ifdef HAS_IPV6
204 if (a->type == b->type)
205 return 0;
206 if (a->type == T_AAAA)
207 return (+1);
208 if (b->type == T_AAAA)
209 return (-1);
210 #endif
211 return 0;
212 }
213
214 /* dns_rr_compare_pref_any - compare records by preference, protocol-neutral */
215
dns_rr_compare_pref_any(DNS_RR * a,DNS_RR * b)216 int dns_rr_compare_pref_any(DNS_RR *a, DNS_RR *b)
217 {
218 if (a->pref != b->pref)
219 return (a->pref - b->pref);
220 return 0;
221 }
222
223 /* dns_rr_compare_pref - binary compatibility helper after name change */
224
dns_rr_compare_pref(DNS_RR * a,DNS_RR * b)225 int dns_rr_compare_pref(DNS_RR *a, DNS_RR *b)
226 {
227 return (dns_rr_compare_pref_ipv6(a, b));
228 }
229
230 /* dns_rr_sort_callback - glue function */
231
232 static int (*dns_rr_sort_user) (DNS_RR *, DNS_RR *);
233
dns_rr_sort_callback(const void * a,const void * b)234 static int dns_rr_sort_callback(const void *a, const void *b)
235 {
236 DNS_RR *aa = *(DNS_RR **) a;
237 DNS_RR *bb = *(DNS_RR **) b;
238
239 return (dns_rr_sort_user(aa, bb));
240 }
241
242 /* dns_rr_sort - sort resource record list */
243
dns_rr_sort(DNS_RR * list,int (* compar)(DNS_RR *,DNS_RR *))244 DNS_RR *dns_rr_sort(DNS_RR *list, int (*compar) (DNS_RR *, DNS_RR *))
245 {
246 int (*saved_user) (DNS_RR *, DNS_RR *);
247 DNS_RR **rr_array;
248 DNS_RR *rr;
249 int len;
250 int i;
251
252 /*
253 * Save state and initialize.
254 */
255 saved_user = dns_rr_sort_user;
256 dns_rr_sort_user = compar;
257
258 /*
259 * Build linear array with pointers to each list element.
260 */
261 for (len = 0, rr = list; rr != 0; len++, rr = rr->next)
262 /* void */ ;
263 rr_array = (DNS_RR **) mymalloc(len * sizeof(*rr_array));
264 for (len = 0, rr = list; rr != 0; len++, rr = rr->next)
265 rr_array[len] = rr;
266
267 /*
268 * Sort by user-specified criterion.
269 */
270 qsort((void *) rr_array, len, sizeof(*rr_array), dns_rr_sort_callback);
271
272 /*
273 * Fix the links.
274 */
275 for (i = 0; i < len - 1; i++)
276 rr_array[i]->next = rr_array[i + 1];
277 rr_array[i]->next = 0;
278 list = rr_array[0];
279
280 /*
281 * Cleanup.
282 */
283 myfree((void *) rr_array);
284 dns_rr_sort_user = saved_user;
285 return (list);
286 }
287
288 /* dns_rr_shuffle - shuffle resource record list */
289
dns_rr_shuffle(DNS_RR * list)290 DNS_RR *dns_rr_shuffle(DNS_RR *list)
291 {
292 DNS_RR **rr_array;
293 DNS_RR *rr;
294 int len;
295 int i;
296 int r;
297
298 /*
299 * Build linear array with pointers to each list element.
300 */
301 for (len = 0, rr = list; rr != 0; len++, rr = rr->next)
302 /* void */ ;
303 rr_array = (DNS_RR **) mymalloc(len * sizeof(*rr_array));
304 for (len = 0, rr = list; rr != 0; len++, rr = rr->next)
305 rr_array[len] = rr;
306
307 /*
308 * Shuffle resource records. Every element has an equal chance of landing
309 * in slot 0. After that every remaining element has an equal chance of
310 * landing in slot 1, ... This is exactly n! states for n! permutations.
311 */
312 for (i = 0; i < len - 1; i++) {
313 r = i + (myrand() % (len - i)); /* Victor&Son */
314 rr = rr_array[i];
315 rr_array[i] = rr_array[r];
316 rr_array[r] = rr;
317 }
318
319 /*
320 * Fix the links.
321 */
322 for (i = 0; i < len - 1; i++)
323 rr_array[i]->next = rr_array[i + 1];
324 rr_array[i]->next = 0;
325 list = rr_array[0];
326
327 /*
328 * Cleanup.
329 */
330 myfree((void *) rr_array);
331 return (list);
332 }
333
334 /* dns_rr_remove - remove record from list, return new list */
335
dns_rr_remove(DNS_RR * list,DNS_RR * record)336 DNS_RR *dns_rr_remove(DNS_RR *list, DNS_RR *record)
337 {
338 if (list == 0)
339 msg_panic("dns_rr_remove: record not found");
340
341 if (list == record) {
342 list = record->next;
343 record->next = 0;
344 dns_rr_free(record);
345 } else {
346 list->next = dns_rr_remove(list->next, record);
347 }
348 return (list);
349 }
350