xref: /netbsd-src/external/ibm-public/postfix/dist/src/dns/dns_rr.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*	$NetBSD: dns_rr.c,v 1.1.1.2 2011/03/02 19:32:10 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_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 
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->pref = pref;
129     if (data && data_len > 0)
130 	memcpy(rr->data, data, data_len);
131     rr->data_len = data_len;
132     rr->next = 0;
133     return (rr);
134 }
135 
136 /* dns_rr_free - destroy resource record structure */
137 
138 void    dns_rr_free(DNS_RR *rr)
139 {
140     if (rr) {
141 	if (rr->next)
142 	    dns_rr_free(rr->next);
143 	myfree(rr->qname);
144 	myfree(rr->rname);
145 	myfree((char *) rr);
146     }
147 }
148 
149 /* dns_rr_copy - copy resource record */
150 
151 DNS_RR *dns_rr_copy(DNS_RR *src)
152 {
153     ssize_t len = sizeof(*src) + src->data_len - 1;
154     DNS_RR *dst;
155 
156     /*
157      * Combine struct assignment and data copy in one block copy operation.
158      */
159     dst = (DNS_RR *) mymalloc(len);
160     memcpy((char *) dst, (char *) src, len);
161     dst->qname = mystrdup(src->qname);
162     dst->rname = mystrdup(src->rname);
163     dst->next = 0;
164     return (dst);
165 }
166 
167 /* dns_rr_append - append resource record to list */
168 
169 DNS_RR *dns_rr_append(DNS_RR *list, DNS_RR *rr)
170 {
171     if (list == 0) {
172 	list = rr;
173     } else {
174 	list->next = dns_rr_append(list->next, rr);
175     }
176     return (list);
177 }
178 
179 /* dns_rr_compare_pref_ipv6 - compare records by preference, ipv6 preferred */
180 
181 int     dns_rr_compare_pref_ipv6(DNS_RR *a, DNS_RR *b)
182 {
183     if (a->pref != b->pref)
184 	return (a->pref - b->pref);
185 #ifdef HAS_IPV6
186     if (a->type == b->type)			/* 200412 */
187 	return 0;
188     if (a->type == T_AAAA)
189 	return (-1);
190     if (b->type == T_AAAA)
191 	return (+1);
192 #endif
193     return 0;
194 }
195 
196 /* dns_rr_compare_pref_ipv4 - compare records by preference, ipv4 preferred */
197 
198 int     dns_rr_compare_pref_ipv4(DNS_RR *a, DNS_RR *b)
199 {
200     if (a->pref != b->pref)
201 	return (a->pref - b->pref);
202 #ifdef HAS_IPV6
203     if (a->type == b->type)
204 	return 0;
205     if (a->type == T_AAAA)
206 	return (+1);
207     if (b->type == T_AAAA)
208 	return (-1);
209 #endif
210     return 0;
211 }
212 
213 /* dns_rr_compare_pref_any - compare records by preference, protocol-neutral */
214 
215 int     dns_rr_compare_pref_any(DNS_RR *a, DNS_RR *b)
216 {
217     if (a->pref != b->pref)
218 	return (a->pref - b->pref);
219     return 0;
220 }
221 
222 /* dns_rr_compare_pref - binary compatibility helper after name change */
223 
224 int     dns_rr_compare_pref(DNS_RR *a, DNS_RR *b)
225 {
226     return (dns_rr_compare_pref_ipv6(a, b));
227 }
228 
229 /* dns_rr_sort_callback - glue function */
230 
231 static int (*dns_rr_sort_user) (DNS_RR *, DNS_RR *);
232 
233 static int dns_rr_sort_callback(const void *a, const void *b)
234 {
235     DNS_RR *aa = *(DNS_RR **) a;
236     DNS_RR *bb = *(DNS_RR **) b;
237 
238     return (dns_rr_sort_user(aa, bb));
239 }
240 
241 /* dns_rr_sort - sort resource record list */
242 
243 DNS_RR *dns_rr_sort(DNS_RR *list, int (*compar) (DNS_RR *, DNS_RR *))
244 {
245     int     (*saved_user) (DNS_RR *, DNS_RR *);
246     DNS_RR **rr_array;
247     DNS_RR *rr;
248     int     len;
249     int     i;
250 
251     /*
252      * Save state and initialize.
253      */
254     saved_user = dns_rr_sort_user;
255     dns_rr_sort_user = compar;
256 
257     /*
258      * Build linear array with pointers to each list element.
259      */
260     for (len = 0, rr = list; rr != 0; len++, rr = rr->next)
261 	 /* void */ ;
262     rr_array = (DNS_RR **) mymalloc(len * sizeof(*rr_array));
263     for (len = 0, rr = list; rr != 0; len++, rr = rr->next)
264 	rr_array[len] = rr;
265 
266     /*
267      * Sort by user-specified criterion.
268      */
269     qsort((char *) rr_array, len, sizeof(*rr_array), dns_rr_sort_callback);
270 
271     /*
272      * Fix the links.
273      */
274     for (i = 0; i < len - 1; i++)
275 	rr_array[i]->next = rr_array[i + 1];
276     rr_array[i]->next = 0;
277     list = rr_array[0];
278 
279     /*
280      * Cleanup.
281      */
282     myfree((char *) rr_array);
283     dns_rr_sort_user = saved_user;
284     return (list);
285 }
286 
287 /* dns_rr_shuffle - shuffle resource record list */
288 
289 DNS_RR *dns_rr_shuffle(DNS_RR *list)
290 {
291     DNS_RR **rr_array;
292     DNS_RR *rr;
293     int     len;
294     int     i;
295     int     r;
296 
297     /*
298      * Build linear array with pointers to each list element.
299      */
300     for (len = 0, rr = list; rr != 0; len++, rr = rr->next)
301 	 /* void */ ;
302     rr_array = (DNS_RR **) mymalloc(len * sizeof(*rr_array));
303     for (len = 0, rr = list; rr != 0; len++, rr = rr->next)
304 	rr_array[len] = rr;
305 
306     /*
307      * Shuffle resource records.
308      */
309     for (i = 0; i < len; i++) {
310 	r = myrand() % len;
311 	rr = rr_array[i];
312 	rr_array[i] = rr_array[r];
313 	rr_array[r] = rr;
314     }
315 
316     /*
317      * Fix the links.
318      */
319     for (i = 0; i < len - 1; i++)
320 	rr_array[i]->next = rr_array[i + 1];
321     rr_array[i]->next = 0;
322     list = rr_array[0];
323 
324     /*
325      * Cleanup.
326      */
327     myfree((char *) rr_array);
328     return (list);
329 }
330 
331 /* dns_rr_remove - remove record from list, return new list */
332 
333 DNS_RR *dns_rr_remove(DNS_RR *list, DNS_RR *record)
334 {
335     if (list == 0)
336 	msg_panic("dns_rr_remove: record not found");
337 
338     if (list == record) {
339 	list = record->next;
340 	record->next = 0;
341 	dns_rr_free(record);
342     } else {
343 	list->next = dns_rr_remove(list->next, record);
344     }
345     return (list);
346 }
347