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