1 /*	$NetBSD: dns_rr.c,v 1.1.1.1 2009/06/23 10:08:43 tron 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(DNS_RR *a, DNS_RR *b)
37 /*	DNS_RR	*list
38 /*	DNS_RR	*list
39 /*
40 /*	DNS_RR	*dns_rr_shuffle(list)
41 /*	DNS_RR	*list;
42 /*
43 /*	DNS_RR	*dns_rr_remove(list, record)
44 /*	DNS_RR	*list;
45 /*	DNS_RR	*record;
46 /* DESCRIPTION
47 /*	The routines in this module maintain memory for DNS resource record
48 /*	information, and maintain lists of DNS resource records.
49 /*
50 /*	dns_rr_create() creates and initializes one resource record.
51 /*	The \fIqname\fR field specifies the query name.
52 /*	The \fIrname\fR field specifies the reply name.
53 /*	\fIpreference\fR is used for MX records; \fIdata\fR is a null
54 /*	pointer or specifies optional resource-specific data;
55 /*	\fIdata_len\fR is the amount of resource-specific data.
56 /*
57 /*	dns_rr_free() releases the resource used by of zero or more
58 /*	resource records.
59 /*
60 /*	dns_rr_copy() makes a copy of a resource record.
61 /*
62 /*	dns_rr_append() appends a resource record to a (list of) resource
63 /*	record(s).
64 /*	A null input list is explicitly allowed.
65 /*
66 /*	dns_rr_sort() sorts a list of resource records into ascending
67 /*	order according to a user-specified criterion. The result is the
68 /*	sorted list.
69 /*
70 /*	dns_rr_compare_pref() is a dns_rr_sort() helper to sort records
71 /*	by their MX preference.
72 /*
73 /*	dns_rr_shuffle() randomly permutes a list of resource records.
74 /*
75 /*	dns_rr_remove() removes the specified record from the specified list.
76 /*	The updated list is the result value.
77 /*	The record MUST be a list member.
78 /* LICENSE
79 /* .ad
80 /* .fi
81 /*	The Secure Mailer license must be distributed with this software.
82 /* AUTHOR(S)
83 /*	Wietse Venema
84 /*	IBM T.J. Watson Research
85 /*	P.O. Box 704
86 /*	Yorktown Heights, NY 10598, USA
87 /*--*/
88 
89 /* System library. */
90 
91 #include <sys_defs.h>
92 #include <string.h>
93 #include <stdlib.h>
94 
95 /* Utility library. */
96 
97 #include <msg.h>
98 #include <mymalloc.h>
99 #include <myrand.h>
100 
101 /* DNS library. */
102 
103 #include "dns.h"
104 
105 /* dns_rr_create - fill in resource record structure */
106 
107 DNS_RR *dns_rr_create(const char *qname, const char *rname,
108 		              ushort type, ushort class,
109 		              unsigned int ttl, unsigned pref,
110 		              const char *data, size_t data_len)
111 {
112     DNS_RR *rr;
113 
114     rr = (DNS_RR *) mymalloc(sizeof(*rr) + data_len - 1);
115     rr->qname = mystrdup(qname);
116     rr->rname = mystrdup(rname);
117     rr->type = type;
118     rr->class = class;
119     rr->ttl = ttl;
120     rr->pref = pref;
121     if (data && data_len > 0)
122 	memcpy(rr->data, data, data_len);
123     rr->data_len = data_len;
124     rr->next = 0;
125     return (rr);
126 }
127 
128 /* dns_rr_free - destroy resource record structure */
129 
130 void    dns_rr_free(DNS_RR *rr)
131 {
132     if (rr) {
133 	if (rr->next)
134 	    dns_rr_free(rr->next);
135 	myfree(rr->qname);
136 	myfree(rr->rname);
137 	myfree((char *) rr);
138     }
139 }
140 
141 /* dns_rr_copy - copy resource record */
142 
143 DNS_RR *dns_rr_copy(DNS_RR *src)
144 {
145     ssize_t len = sizeof(*src) + src->data_len - 1;
146     DNS_RR *dst;
147 
148     /*
149      * Combine struct assignment and data copy in one block copy operation.
150      */
151     dst = (DNS_RR *) mymalloc(len);
152     memcpy((char *) dst, (char *) src, len);
153     dst->qname = mystrdup(src->qname);
154     dst->rname = mystrdup(src->rname);
155     dst->next = 0;
156     return (dst);
157 }
158 
159 /* dns_rr_append - append resource record to list */
160 
161 DNS_RR *dns_rr_append(DNS_RR *list, DNS_RR *rr)
162 {
163     if (list == 0) {
164 	list = rr;
165     } else {
166 	list->next = dns_rr_append(list->next, rr);
167     }
168     return (list);
169 }
170 
171 /* dns_rr_compare_pref - compare resource records by preference */
172 
173 int     dns_rr_compare_pref(DNS_RR *a, DNS_RR *b)
174 {
175     if (a->pref != b->pref)
176 	return (a->pref - b->pref);
177 #ifdef HAS_IPV6
178     if (a->type == b->type)			/* 200412 */
179 	return 0;
180     if (a->type == T_AAAA)
181 	return (-1);
182     if (b->type == T_AAAA)
183 	return (+1);
184 #endif
185     return 0;
186 }
187 
188 /* dns_rr_sort_callback - glue function */
189 
190 static int (*dns_rr_sort_user) (DNS_RR *, DNS_RR *);
191 
192 static int dns_rr_sort_callback(const void *a, const void *b)
193 {
194     DNS_RR *aa = *(DNS_RR **) a;
195     DNS_RR *bb = *(DNS_RR **) b;
196 
197     return (dns_rr_sort_user(aa, bb));
198 }
199 
200 /* dns_rr_sort - sort resource record list */
201 
202 DNS_RR *dns_rr_sort(DNS_RR *list, int (*compar) (DNS_RR *, DNS_RR *))
203 {
204     int     (*saved_user) (DNS_RR *, DNS_RR *);
205     DNS_RR **rr_array;
206     DNS_RR *rr;
207     int     len;
208     int     i;
209 
210     /*
211      * Save state and initialize.
212      */
213     saved_user = dns_rr_sort_user;
214     dns_rr_sort_user = compar;
215 
216     /*
217      * Build linear array with pointers to each list element.
218      */
219     for (len = 0, rr = list; rr != 0; len++, rr = rr->next)
220 	 /* void */ ;
221     rr_array = (DNS_RR **) mymalloc(len * sizeof(*rr_array));
222     for (len = 0, rr = list; rr != 0; len++, rr = rr->next)
223 	rr_array[len] = rr;
224 
225     /*
226      * Sort by user-specified criterion.
227      */
228     qsort((char *) rr_array, len, sizeof(*rr_array), dns_rr_sort_callback);
229 
230     /*
231      * Fix the links.
232      */
233     for (i = 0; i < len - 1; i++)
234 	rr_array[i]->next = rr_array[i + 1];
235     rr_array[i]->next = 0;
236     list = rr_array[0];
237 
238     /*
239      * Cleanup.
240      */
241     myfree((char *) rr_array);
242     dns_rr_sort_user = saved_user;
243     return (list);
244 }
245 
246 /* dns_rr_shuffle - shuffle resource record list */
247 
248 DNS_RR *dns_rr_shuffle(DNS_RR *list)
249 {
250     DNS_RR **rr_array;
251     DNS_RR *rr;
252     int     len;
253     int     i;
254     int     r;
255 
256     /*
257      * Build linear array with pointers to each list element.
258      */
259     for (len = 0, rr = list; rr != 0; len++, rr = rr->next)
260 	 /* void */ ;
261     rr_array = (DNS_RR **) mymalloc(len * sizeof(*rr_array));
262     for (len = 0, rr = list; rr != 0; len++, rr = rr->next)
263 	rr_array[len] = rr;
264 
265     /*
266      * Shuffle resource records.
267      */
268     for (i = 0; i < len; i++) {
269 	r = myrand() % len;
270 	rr = rr_array[i];
271 	rr_array[i] = rr_array[r];
272 	rr_array[r] = rr;
273     }
274 
275     /*
276      * Fix the links.
277      */
278     for (i = 0; i < len - 1; i++)
279 	rr_array[i]->next = rr_array[i + 1];
280     rr_array[i]->next = 0;
281     list = rr_array[0];
282 
283     /*
284      * Cleanup.
285      */
286     myfree((char *) rr_array);
287     return (list);
288 }
289 
290 /* dns_rr_remove - remove record from list, return new list */
291 
292 DNS_RR *dns_rr_remove(DNS_RR *list, DNS_RR *record)
293 {
294     if (list == 0)
295 	msg_panic("dns_rr_remove: record not found");
296 
297     if (list == record) {
298 	list = record->next;
299 	record->next = 0;
300 	dns_rr_free(record);
301     } else {
302 	list->next = dns_rr_remove(list->next, record);
303     }
304     return (list);
305 }
306