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 ldns_dname_compare(dname_a, ns_owner) == 0) { 233 /* GLUE! */ 234 if (!ldns_rr_list_push_rr(glue, a)) goto memory_error; 235 } 236 } 237 } 238 239 ldns_rr_list_free(addr); 240 ldns_rr_list_free(zone_cuts); 241 242 if (ldns_rr_list_rr_count(glue) == 0) { 243 ldns_rr_list_free(glue); 244 return NULL; 245 } else { 246 return glue; 247 } 248 249 memory_error: 250 if (zone_cuts) { 251 LDNS_FREE(zone_cuts); 252 } 253 if (addr) { 254 ldns_rr_list_free(addr); 255 } 256 if (glue) { 257 ldns_rr_list_free(glue); 258 } 259 return NULL; 260 } 261 262 ldns_zone * 263 ldns_zone_new(void) 264 { 265 ldns_zone *z; 266 267 z = LDNS_MALLOC(ldns_zone); 268 if (!z) { 269 return NULL; 270 } 271 272 z->_rrs = ldns_rr_list_new(); 273 if (!z->_rrs) { 274 LDNS_FREE(z); 275 return NULL; 276 } 277 ldns_zone_set_soa(z, NULL); 278 return z; 279 } 280 281 /* we regocnize: 282 * $TTL, $ORIGIN 283 */ 284 ldns_status 285 ldns_zone_new_frm_fp(ldns_zone **z, FILE *fp, ldns_rdf *origin, uint32_t ttl, ldns_rr_class c) 286 { 287 return ldns_zone_new_frm_fp_l(z, fp, origin, ttl, c, NULL); 288 } 289 290 /* XXX: class is never used */ 291 ldns_status 292 ldns_zone_new_frm_fp_l(ldns_zone **z, FILE *fp, ldns_rdf *origin, uint32_t ttl, 293 ldns_rr_class ATTR_UNUSED(c), int *line_nr) 294 { 295 ldns_zone *newzone; 296 ldns_rr *rr; 297 uint32_t my_ttl; 298 ldns_rdf *my_origin; 299 ldns_rdf *my_prev; 300 bool soa_seen = false; /* 2 soa are an error */ 301 ldns_status s; 302 ldns_status ret; 303 304 /* most cases of error are memory problems */ 305 ret = LDNS_STATUS_MEM_ERR; 306 307 newzone = NULL; 308 my_origin = NULL; 309 my_prev = NULL; 310 311 my_ttl = ttl; 312 313 if (origin) { 314 my_origin = ldns_rdf_clone(origin); 315 if (!my_origin) goto error; 316 /* also set the prev */ 317 my_prev = ldns_rdf_clone(origin); 318 if (!my_prev) goto error; 319 } 320 321 newzone = ldns_zone_new(); 322 if (!newzone) goto error; 323 324 while(!feof(fp)) { 325 s = ldns_rr_new_frm_fp_l(&rr, fp, &my_ttl, &my_origin, &my_prev, line_nr); 326 switch (s) { 327 case LDNS_STATUS_OK: 328 if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_SOA) { 329 if (soa_seen) { 330 /* second SOA 331 * just skip, maybe we want to say 332 * something??? */ 333 ldns_rr_free(rr); 334 continue; 335 } 336 soa_seen = true; 337 ldns_zone_set_soa(newzone, rr); 338 /* set origin to soa if not specified */ 339 if (!my_origin) { 340 my_origin = ldns_rdf_clone(ldns_rr_owner(rr)); 341 } 342 continue; 343 } 344 345 /* a normal RR - as sofar the DNS is normal */ 346 if (!ldns_zone_push_rr(newzone, rr)) goto error; 347 348 case LDNS_STATUS_SYNTAX_EMPTY: 349 /* empty line was seen */ 350 case LDNS_STATUS_SYNTAX_TTL: 351 /* the function set the ttl */ 352 break; 353 case LDNS_STATUS_SYNTAX_ORIGIN: 354 /* the function set the origin */ 355 break; 356 case LDNS_STATUS_SYNTAX_INCLUDE: 357 ret = LDNS_STATUS_SYNTAX_INCLUDE_ERR_NOTIMPL; 358 break; 359 default: 360 ret = s; 361 goto error; 362 } 363 } 364 365 if (my_origin) { 366 ldns_rdf_deep_free(my_origin); 367 } 368 if (my_prev) { 369 ldns_rdf_deep_free(my_prev); 370 } 371 if (z) { 372 *z = newzone; 373 } else { 374 ldns_zone_free(newzone); 375 } 376 377 return LDNS_STATUS_OK; 378 379 error: 380 if (my_origin) { 381 ldns_rdf_deep_free(my_origin); 382 } 383 if (my_prev) { 384 ldns_rdf_deep_free(my_prev); 385 } 386 if (newzone) { 387 ldns_zone_free(newzone); 388 } 389 return ret; 390 } 391 392 void 393 ldns_zone_sort(ldns_zone *zone) 394 { 395 ldns_rr_list *zrr; 396 assert(zone != NULL); 397 398 zrr = ldns_zone_rrs(zone); 399 ldns_rr_list_sort(zrr); 400 } 401 402 #if 0 403 /** 404 * ixfr function. Work on a ldns_zone and remove and add 405 * the rrs from the rrlist 406 * \param[in] z the zone to work on 407 * \param[in] del rr_list to remove from the zone 408 * \param[in] add rr_list to add to the zone 409 * \return Tja, wat zouden we eens returnen TODO 410 */ 411 void 412 ldns_zone_ixfr_del_add(ldns_zone *z, ldns_rr_list *del, ldns_rr_list *add) 413 { 414 415 } 416 #endif 417 418 void 419 ldns_zone_free(ldns_zone *zone) 420 { 421 ldns_rr_list_free(zone->_rrs); 422 LDNS_FREE(zone); 423 } 424 425 void 426 ldns_zone_deep_free(ldns_zone *zone) 427 { 428 ldns_rr_free(zone->_soa); 429 ldns_rr_list_deep_free(zone->_rrs); 430 LDNS_FREE(zone); 431 } 432