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