1 /* zone.c 2 * 3 * Functions for ldns_zone structure 4 * a Net::DNS like library for C 5 * 6 * (c) NLnet Labs, 2005-2006 7 * See the file LICENSE for the license 8 */ 9 #include <ldns/config.h> 10 11 #include <ldns/ldns.h> 12 13 #include <strings.h> 14 #include <limits.h> 15 16 ldns_rr * 17 ldns_zone_soa(const ldns_zone *z) 18 { 19 return z->_soa; 20 } 21 22 size_t 23 ldns_zone_rr_count(const ldns_zone *z) 24 { 25 return ldns_rr_list_rr_count(z->_rrs); 26 } 27 28 void 29 ldns_zone_set_soa(ldns_zone *z, ldns_rr *soa) 30 { 31 z->_soa = soa; 32 } 33 34 ldns_rr_list * 35 ldns_zone_rrs(const ldns_zone *z) 36 { 37 return z->_rrs; 38 } 39 40 void 41 ldns_zone_set_rrs(ldns_zone *z, ldns_rr_list *rrlist) 42 { 43 z->_rrs = rrlist; 44 } 45 46 bool 47 ldns_zone_push_rr_list(ldns_zone *z, ldns_rr_list *list) 48 { 49 return ldns_rr_list_cat(ldns_zone_rrs(z), list); 50 51 } 52 53 bool 54 ldns_zone_push_rr(ldns_zone *z, ldns_rr *rr) 55 { 56 return ldns_rr_list_push_rr( ldns_zone_rrs(z), rr); 57 } 58 59 /* return a clone of the given rr list, without the glue records 60 * rr list should be the complete zone 61 * if present, stripped records are added to the list *glue_records 62 */ 63 ldns_rr_list * 64 ldns_zone_strip_glue_rrs(const ldns_rdf *zone_name, const ldns_rr_list *rrs, ldns_rr_list *glue_rrs) 65 { 66 ldns_rr_list *new_list; 67 68 /* when do we find glue? It means we find an IP address 69 * (AAAA/A) for a nameserver listed in the zone 70 * 71 * Alg used here: 72 * first find all the zonecuts (NS records) 73 * find all the AAAA or A records (can be done it the 74 * above loop). 75 * 76 * Check if the aaaa/a list are subdomains under the 77 * NS domains. 78 * If yes -> glue, if no -> not glue 79 */ 80 81 ldns_rr_list *zone_cuts; 82 ldns_rr_list *addr; 83 ldns_rr *r, *ns, *a; 84 ldns_rdf *dname_a, *ns_owner; 85 uint16_t i,j; 86 87 new_list = NULL; 88 zone_cuts = NULL; 89 addr = NULL; 90 91 new_list = ldns_rr_list_new(); 92 if (!new_list) goto memory_error; 93 zone_cuts = ldns_rr_list_new(); 94 if (!zone_cuts) goto memory_error; 95 addr = ldns_rr_list_new(); 96 if (!addr) goto memory_error; 97 98 for(i = 0; i < ldns_rr_list_rr_count(rrs); i++) { 99 r = ldns_rr_list_rr(rrs, i); 100 if (ldns_rr_get_type(r) == LDNS_RR_TYPE_A || 101 ldns_rr_get_type(r) == LDNS_RR_TYPE_AAAA) { 102 /* possibly glue */ 103 if (!ldns_rr_list_push_rr(addr, r)) goto memory_error; 104 continue; 105 } 106 if (ldns_rr_get_type(r) == LDNS_RR_TYPE_NS) { 107 /* multiple zones will end up here - 108 * for now; not a problem 109 */ 110 /* don't add NS records for the current zone itself */ 111 if (ldns_rdf_compare(ldns_rr_owner(r), 112 zone_name) != 0) { 113 if (!ldns_rr_list_push_rr(zone_cuts, r)) goto memory_error; 114 } 115 continue; 116 } 117 } 118 119 /* will sorting make it quicker ?? */ 120 for(i = 0; i < ldns_rr_list_rr_count(zone_cuts); i++) { 121 ns = ldns_rr_list_rr(zone_cuts, i); 122 ns_owner = ldns_rr_owner(ns); 123 for(j = 0; j < ldns_rr_list_rr_count(addr); j++) { 124 a = ldns_rr_list_rr(addr, j); 125 dname_a = ldns_rr_owner(a); 126 127 if (ldns_dname_is_subdomain(dname_a, ns_owner)) { 128 /* GLUE! */ 129 if (glue_rrs) { 130 if (!ldns_rr_list_push_rr(glue_rrs, a)) goto memory_error; 131 } 132 break; 133 } else { 134 if (!ldns_rr_list_push_rr(new_list, a)) goto memory_error; 135 } 136 } 137 } 138 139 ldns_rr_list_free(addr); 140 ldns_rr_list_free(zone_cuts); 141 142 return new_list; 143 144 memory_error: 145 if (new_list) { 146 ldns_rr_list_free(new_list); 147 } 148 if (zone_cuts) { 149 ldns_rr_list_free(zone_cuts); 150 } 151 if (addr) { 152 ldns_rr_list_free(addr); 153 } 154 return NULL; 155 } 156 157 /* 158 * Get the list of glue records in a zone 159 * XXX: there should be a way for this to return error, other than NULL, 160 * since NULL is a valid return 161 */ 162 ldns_rr_list * 163 ldns_zone_glue_rr_list(const ldns_zone *z) 164 { 165 /* when do we find glue? It means we find an IP address 166 * (AAAA/A) for a nameserver listed in the zone 167 * 168 * Alg used here: 169 * first find all the zonecuts (NS records) 170 * find all the AAAA or A records (can be done it the 171 * above loop). 172 * 173 * Check if the aaaa/a list are subdomains under the 174 * NS domains. 175 * If yes -> glue, if no -> not glue 176 */ 177 178 ldns_rr_list *zone_cuts; 179 ldns_rr_list *addr; 180 ldns_rr_list *glue; 181 ldns_rr *r, *ns, *a; 182 ldns_rdf *dname_a, *ns_owner; 183 size_t i,j; 184 185 zone_cuts = NULL; 186 addr = NULL; 187 glue = NULL; 188 189 /* we cannot determine glue in a 'zone' without a SOA */ 190 if (!ldns_zone_soa(z)) { 191 return NULL; 192 } 193 194 zone_cuts = ldns_rr_list_new(); 195 if (!zone_cuts) goto memory_error; 196 addr = ldns_rr_list_new(); 197 if (!addr) goto memory_error; 198 glue = ldns_rr_list_new(); 199 if (!glue) goto memory_error; 200 201 for(i = 0; i < ldns_zone_rr_count(z); i++) { 202 r = ldns_rr_list_rr(ldns_zone_rrs(z), i); 203 if (ldns_rr_get_type(r) == LDNS_RR_TYPE_A || 204 ldns_rr_get_type(r) == LDNS_RR_TYPE_AAAA) { 205 /* possibly glue */ 206 if (!ldns_rr_list_push_rr(addr, r)) goto memory_error; 207 continue; 208 } 209 if (ldns_rr_get_type(r) == LDNS_RR_TYPE_NS) { 210 /* multiple zones will end up here - 211 * for now; not a problem 212 */ 213 /* don't add NS records for the current zone itself */ 214 if (ldns_rdf_compare(ldns_rr_owner(r), 215 ldns_rr_owner(ldns_zone_soa(z))) != 0) { 216 if (!ldns_rr_list_push_rr(zone_cuts, r)) goto memory_error; 217 } 218 continue; 219 } 220 } 221 222 /* will sorting make it quicker ?? */ 223 for(i = 0; i < ldns_rr_list_rr_count(zone_cuts); i++) { 224 ns = ldns_rr_list_rr(zone_cuts, i); 225 ns_owner = ldns_rr_owner(ns); 226 227 for(j = 0; j < ldns_rr_list_rr_count(addr); j++) { 228 a = ldns_rr_list_rr(addr, j); 229 dname_a = ldns_rr_owner(a); 230 231 if (ldns_dname_is_subdomain(dname_a, ns_owner)) { 232 /* GLUE! */ 233 if (!ldns_rr_list_push_rr(glue, a)) goto memory_error; 234 } 235 } 236 } 237 238 ldns_rr_list_free(addr); 239 ldns_rr_list_free(zone_cuts); 240 241 if (ldns_rr_list_rr_count(glue) == 0) { 242 ldns_rr_list_free(glue); 243 return NULL; 244 } else { 245 return glue; 246 } 247 248 memory_error: 249 if (zone_cuts) { 250 LDNS_FREE(zone_cuts); 251 } 252 if (addr) { 253 ldns_rr_list_free(addr); 254 } 255 if (glue) { 256 ldns_rr_list_free(glue); 257 } 258 return NULL; 259 } 260 261 ldns_zone * 262 ldns_zone_new(void) 263 { 264 ldns_zone *z; 265 266 z = LDNS_MALLOC(ldns_zone); 267 if (!z) { 268 return NULL; 269 } 270 271 z->_rrs = ldns_rr_list_new(); 272 if (!z->_rrs) { 273 LDNS_FREE(z); 274 return NULL; 275 } 276 ldns_zone_set_soa(z, NULL); 277 return z; 278 } 279 280 /* we regocnize: 281 * $TTL, $ORIGIN 282 */ 283 ldns_status 284 ldns_zone_new_frm_fp(ldns_zone **z, FILE *fp, ldns_rdf *origin, uint32_t ttl, ldns_rr_class c) 285 { 286 return ldns_zone_new_frm_fp_l(z, fp, origin, ttl, c, NULL); 287 } 288 289 /* XXX: class is never used */ 290 ldns_status 291 ldns_zone_new_frm_fp_l(ldns_zone **z, FILE *fp, ldns_rdf *origin, uint32_t ttl, 292 ldns_rr_class ATTR_UNUSED(c), int *line_nr) 293 { 294 ldns_zone *newzone; 295 ldns_rr *rr; 296 uint32_t my_ttl; 297 ldns_rdf *my_origin; 298 ldns_rdf *my_prev; 299 bool soa_seen = false; /* 2 soa are an error */ 300 ldns_status s; 301 ldns_status ret; 302 303 /* most cases of error are memory problems */ 304 ret = LDNS_STATUS_MEM_ERR; 305 306 newzone = NULL; 307 my_origin = NULL; 308 my_prev = NULL; 309 310 my_ttl = ttl; 311 312 if (origin) { 313 my_origin = ldns_rdf_clone(origin); 314 if (!my_origin) goto error; 315 /* also set the prev */ 316 my_prev = ldns_rdf_clone(origin); 317 if (!my_prev) goto error; 318 } 319 320 newzone = ldns_zone_new(); 321 if (!newzone) goto error; 322 323 while(!feof(fp)) { 324 s = ldns_rr_new_frm_fp_l(&rr, fp, &my_ttl, &my_origin, &my_prev, line_nr); 325 switch (s) { 326 case LDNS_STATUS_OK: 327 if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_SOA) { 328 if (soa_seen) { 329 /* second SOA 330 * just skip, maybe we want to say 331 * something??? */ 332 ldns_rr_free(rr); 333 continue; 334 } 335 soa_seen = true; 336 ldns_zone_set_soa(newzone, rr); 337 /* set origin to soa if not specified */ 338 if (!my_origin) { 339 my_origin = ldns_rdf_clone(ldns_rr_owner(rr)); 340 } 341 continue; 342 } 343 344 /* a normal RR - as sofar the DNS is normal */ 345 if (!ldns_zone_push_rr(newzone, rr)) goto error; 346 347 case LDNS_STATUS_SYNTAX_EMPTY: 348 /* empty line was seen */ 349 case LDNS_STATUS_SYNTAX_TTL: 350 /* the function set the ttl */ 351 break; 352 case LDNS_STATUS_SYNTAX_ORIGIN: 353 /* the function set the origin */ 354 break; 355 case LDNS_STATUS_SYNTAX_INCLUDE: 356 ret = LDNS_STATUS_SYNTAX_INCLUDE_ERR_NOTIMPL; 357 break; 358 default: 359 ret = s; 360 goto error; 361 } 362 } 363 364 if (my_origin) { 365 ldns_rdf_deep_free(my_origin); 366 } 367 if (my_prev) { 368 ldns_rdf_deep_free(my_prev); 369 } 370 if (z) { 371 *z = newzone; 372 } else { 373 ldns_zone_free(newzone); 374 } 375 376 return LDNS_STATUS_OK; 377 378 error: 379 if (my_origin) { 380 ldns_rdf_deep_free(my_origin); 381 } 382 if (my_prev) { 383 ldns_rdf_deep_free(my_prev); 384 } 385 if (newzone) { 386 ldns_zone_free(newzone); 387 } 388 return ret; 389 } 390 391 void 392 ldns_zone_sort(ldns_zone *zone) 393 { 394 ldns_rr_list *zrr; 395 assert(zone != NULL); 396 397 zrr = ldns_zone_rrs(zone); 398 ldns_rr_list_sort(zrr); 399 } 400 401 #if 0 402 /** 403 * ixfr function. Work on a ldns_zone and remove and add 404 * the rrs from the rrlist 405 * \param[in] z the zone to work on 406 * \param[in] del rr_list to remove from the zone 407 * \param[in] add rr_list to add to the zone 408 * \return Tja, wat zouden we eens returnen TODO 409 */ 410 void 411 ldns_zone_ixfr_del_add(ldns_zone *z, ldns_rr_list *del, ldns_rr_list *add) 412 { 413 414 } 415 #endif 416 417 void 418 ldns_zone_free(ldns_zone *zone) 419 { 420 ldns_rr_list_free(zone->_rrs); 421 LDNS_FREE(zone); 422 } 423 424 void 425 ldns_zone_deep_free(ldns_zone *zone) 426 { 427 ldns_rr_free(zone->_soa); 428 ldns_rr_list_deep_free(zone->_rrs); 429 LDNS_FREE(zone); 430 } 431