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