1/*	$NetBSD$	*/
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
115DNS_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
138void    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
151DNS_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
169DNS_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
181int     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
198int     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
215int     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
224int     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
231static int (*dns_rr_sort_user) (DNS_RR *, DNS_RR *);
232
233static 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
243DNS_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
289DNS_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
333DNS_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