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