static void encode_dname(query_type *q, domain_type *domain) { while (domain->parent && query_get_dname_offset(q, domain) == 0) { query_put_dname_offset(q, domain, buffer_position(q->packet)); DEBUG(DEBUG_NAME_COMPRESSION, 2, (LOG_INFO, "dname: %s, number: %lu, offset: %u\n", dname_to_string(domain_dname(domain), NULL), (unsigned long) domain->number, query_get_dname_offset(q, domain))); buffer_write(q->packet, dname_name(domain_dname(domain)), label_length(dname_name(domain_dname(domain))) + 1U); domain = domain->parent; } if (domain->parent) { DEBUG(DEBUG_NAME_COMPRESSION, 2, (LOG_INFO, "dname: %s, number: %lu, pointer: %u\n", dname_to_string(domain_dname(domain), NULL), (unsigned long) domain->number, query_get_dname_offset(q, domain))); assert(query_get_dname_offset(q, domain) <= MAX_COMPRESSION_OFFSET); buffer_write_u16(q->packet, 0xc000 | query_get_dname_offset(q, domain)); } else { buffer_write_u8(q->packet, 0); } }
void xfrd_handle_passed_packet(buffer_type* packet, int acl_num) { uint8_t qnamebuf[MAXDOMAINLEN]; uint16_t qtype, qclass; const dname_type* dname; region_type* tempregion = region_create(xalloc, free); xfrd_zone_t* zone; buffer_skip(packet, QHEADERSZ); if(!packet_read_query_section(packet, qnamebuf, &qtype, &qclass)) { region_destroy(tempregion); return; /* drop bad packet */ } dname = dname_make(tempregion, qnamebuf, 1); DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: got passed packet for %s, acl " "%d", dname_to_string(dname,0), acl_num)); /* find the zone */ zone = (xfrd_zone_t*)rbtree_search(xfrd->zones, dname); if(!zone) { log_msg(LOG_INFO, "xfrd: incoming packet for unknown zone %s", dname_to_string(dname,0)); region_destroy(tempregion); return; /* drop packet for unknown zone */ } region_destroy(tempregion); /* handle */ if(OPCODE(packet) == OPCODE_NOTIFY) { xfrd_soa_t soa; int have_soa = 0; int next; /* get serial from a SOA */ if(ANCOUNT(packet) == 1 && packet_skip_dname(packet) && xfrd_parse_soa_info(packet, &soa)) { have_soa = 1; } if(xfrd_handle_incoming_notify(zone, have_soa?&soa:NULL)) { if(zone->zone_handler.fd == -1 && zone->tcp_conn == -1 && !zone->tcp_waiting && !zone->udp_waiting) { xfrd_set_refresh_now(zone); } } next = find_same_master_notify(zone, acl_num); if(next != -1) { zone->next_master = next; DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: notify set next master to query %d", next)); } } else { /* TODO handle incoming IXFR udp reply via port 53 */ } }
static void delete_zone_rrs(namedb_type* db, zone_type* zone) { rrset_type *rrset; domain_type *domain = zone->apex; domain_type *next = NULL; zone->updated = 1; #ifdef NSEC3 #ifndef FULL_PREHASH zone_nsec3_domains_destroy(db, zone); #endif /* !FULL_PREHASH */ #endif /* NSEC3 */ /* go through entire tree below the zone apex (incl subzones) */ while(domain && dname_is_subdomain( domain_dname(domain), domain_dname(zone->apex))) { DEBUG(DEBUG_XFRD,2, (LOG_INFO, "delete zone visit %s", dname_to_string(domain_dname(domain),0))); /* delete all rrsets of the zone */ while((rrset = domain_find_any_rrset(domain, zone))) { (void)rrset_delete(db, domain, rrset); } next = domain_next(domain); domain->nextdiff = next; domain = next; } #ifdef NSEC3 #ifndef FULL_PREHASH if (0 != zone_nsec3_domains_create(db, zone)) { log_msg(LOG_ERR, "Zone %s: unable to create zone NSEC3 prehash table", dname_to_string(domain_dname(zone->apex), NULL)); } #endif /* !FULL_PREHASH */ #endif /* NSEC3 */ DEBUG(DEBUG_XFRD, 1, (LOG_INFO, "axfrdel: recyclebin holds %lu bytes", (unsigned long) region_get_recycle_size(db->region))); #ifndef NDEBUG if(nsd_debug_level >= 1) region_log_stats(db->region); #endif assert(zone->soa_rrset == 0); /* keep zone->soa_nx_rrset alloced */ assert(zone->ns_rrset == 0); assert(zone->is_secure == 0); assert(zone->updated == 1); }
/* * Answer if this is an AXFR or IXFR query. */ query_state_type answer_axfr_ixfr(struct nsd *nsd, struct query *q) { struct acl_options *acl = NULL; /* Is it AXFR? */ switch (q->qtype) { case TYPE_AXFR: if (q->tcp) { struct zone_options* zone_opt; zone_opt = zone_options_find(nsd->options, q->qname); if(!zone_opt || acl_check_incoming(zone_opt->pattern->provide_xfr, q, &acl)==-1) { if (verbosity >= 2) { char a[128]; addr2str(&q->addr, a, sizeof(a)); VERBOSITY(2, (LOG_INFO, "axfr for %s from %s refused, %s", dname_to_string(q->qname, NULL), a, acl?"blocked":"no acl matches")); } DEBUG(DEBUG_XFRD,1, (LOG_INFO, "axfr refused, %s", acl?"blocked":"no acl matches")); if (!zone_opt) { RCODE_SET(q->packet, RCODE_NOTAUTH); } else { RCODE_SET(q->packet, RCODE_REFUSE); } return QUERY_PROCESSED; } DEBUG(DEBUG_XFRD,1, (LOG_INFO, "axfr admitted acl %s %s", acl->ip_address_spec, acl->key_name?acl->key_name:"NOKEY")); if (verbosity >= 1) { char a[128]; addr2str(&q->addr, a, sizeof(a)); VERBOSITY(1, (LOG_INFO, "%s for %s from %s", (q->qtype==TYPE_AXFR?"axfr":"ixfr"), dname_to_string(q->qname, NULL), a)); } return query_axfr(nsd, q); } /** Fallthrough: AXFR over UDP queries are discarded. */ /* fallthrough */ case TYPE_IXFR: RCODE_SET(q->packet, RCODE_IMPL); return QUERY_PROCESSED; default: return QUERY_DISCARDED; } }
static int rdata_ipsecgateway_to_string(buffer_type *output, rdata_atom_type rdata, rr_type* rr) { int gateway_type = rdata_atom_data(rr->rdatas[1])[0]; switch(gateway_type) { case IPSECKEY_NOGATEWAY: buffer_printf(output, "."); break; case IPSECKEY_IP4: rdata_a_to_string(output, rdata, rr); break; case IPSECKEY_IP6: rdata_aaaa_to_string(output, rdata, rr); break; case IPSECKEY_DNAME: { region_type* temp = region_create(xalloc, free); const dname_type* d = dname_make(temp, rdata_atom_data(rdata), 0); if(!d) { region_destroy(temp); return 0; } buffer_printf(output, "%s", dname_to_string(d, NULL)); region_destroy(temp); } break; default: return 0; } return 1; }
static domain_type* rrset_delete(namedb_type* db, domain_type* domain, rrset_type* rrset) { int i; /* find previous */ rrset_type** pp = &domain->rrsets; while(*pp && *pp != rrset) { pp = &( (*pp)->next ); } if(!*pp) { /* rrset does not exist for domain */ return NULL; } *pp = rrset->next; DEBUG(DEBUG_XFRD,2, (LOG_INFO, "delete rrset of %s type %s", dname_to_string(domain_dname(domain),0), rrtype_to_string(rrset_rrtype(rrset)))); /* is this a SOA rrset ? */ if(rrset->zone->soa_rrset == rrset) { rrset->zone->soa_rrset = 0; rrset->zone->updated = 1; domain->has_SOA = 0; } if(rrset->zone->ns_rrset == rrset) { rrset->zone->ns_rrset = 0; } if(domain == rrset->zone->apex && rrset_rrtype(rrset) == TYPE_RRSIG) { for (i = 0; i < rrset->rr_count; ++i) { if (rr_rrsig_type_covered(&rrset->rrs[i]) == TYPE_DNSKEY) { rrset->zone->is_secure = 0; break; } } } #ifdef NSEC3 #ifndef FULL_PREHASH if (rrset->rrs[0].type == TYPE_NSEC3) { namedb_del_nsec3_domain(db, domain, rrset->zone); } #endif /* !FULL_PREHASH */ #endif /* NSEC3 */ /* recycle the memory space of the rrset */ for (i = 0; i < rrset->rr_count; ++i) add_rdata_to_recyclebin(db, &rrset->rrs[i]); region_recycle(db->region, rrset->rrs, sizeof(rr_type) * rrset->rr_count); rrset->rr_count = 0; region_recycle(db->region, rrset, sizeof(rrset_type)); /* is the node now an empty node (completely deleted) */ if (domain->rrsets == 0) { return domain; } return NULL; }
static int rdata_dname_to_string(buffer_type *output, rdata_atom_type rdata, rr_type* ATTR_UNUSED(rr)) { buffer_printf(output, "%s", dname_to_string(domain_dname(rdata_atom_domain(rdata)), NULL)); return 1; }
/* * Answer if this is an AXFR or IXFR query. */ query_state_type answer_axfr_ixfr(struct nsd *nsd, struct query *q) { acl_options_t *acl; /* Is it AXFR? */ switch (q->qtype) { case TYPE_AXFR: if (q->tcp) { zone_options_t* zone_opt; zone_opt = zone_options_find(nsd->options, q->qname); if(!zone_opt || acl_check_incoming(zone_opt->provide_xfr, q, &acl)==-1) { if (verbosity > 0) { char address[128]; if (addr2ip(q->addr, address, sizeof(address))) { DEBUG(DEBUG_XFRD,1, (LOG_INFO, "addr2ip failed")); strlcpy(address, "[unknown]", sizeof(address)); } VERBOSITY(1, (LOG_INFO, "axfr for zone %s from client %s refused, %s", dname_to_string(q->qname, NULL), address, acl?"blocked":"no acl matches")); } DEBUG(DEBUG_XFRD,1, (LOG_INFO, "axfr refused, %s", acl?"blocked":"no acl matches")); if (!zone_opt) { RCODE_SET(q->packet, RCODE_NOTAUTH); } else { RCODE_SET(q->packet, RCODE_REFUSE); } return QUERY_PROCESSED; } DEBUG(DEBUG_XFRD,1, (LOG_INFO, "axfr admitted acl %s %s", acl->ip_address_spec, acl->key_name?acl->key_name:"NOKEY")); return query_axfr(nsd, q); } case TYPE_IXFR: RCODE_SET(q->packet, RCODE_IMPL); return QUERY_PROCESSED; default: return QUERY_DISCARDED; } }
int print_rr(FILE *out, struct state_pretty_rr *state, rr_type *record) { region_type *region = region_create(xalloc, free); buffer_type *output = buffer_create(region, MAX_RDLENGTH); rrtype_descriptor_type *descriptor = rrtype_descriptor_by_type(record->type); int result; const dname_type *owner = domain_dname(record->owner); const dname_type *owner_origin = dname_origin(region, owner); int owner_changed = (!state->previous_owner || dname_compare(state->previous_owner, owner) != 0); if (owner_changed) { int origin_changed = (!state->previous_owner_origin || dname_compare( state->previous_owner_origin, owner_origin) != 0); if (origin_changed) { buffer_printf( output, "$ORIGIN %s\n", dname_to_string(owner_origin, NULL)); } set_previous_owner(state, owner); buffer_printf(output, "%s", dname_to_string(owner, state->previous_owner_origin)); } buffer_printf(output, "\t%lu\t%s\t%s", (unsigned long) record->ttl, rrclass_to_string(record->klass), rrtype_to_string(record->type)); result = print_rdata(output, descriptor, record); if (!result) { /* * Some RDATA failed to print, so print the record's * RDATA in unknown format. */ result = rdata_atoms_to_unknown_string(output, descriptor, record->rdata_count, record->rdatas); } if (result) { buffer_printf(output, "\n"); buffer_flip(output); (void)write_data(out, buffer_current(output), buffer_remaining(output)); /* fflush(out); */ } region_destroy(region); return result; }
/** inspect a chunk in the file */ static void* inspect_chunk(void* base, void* cv, struct inspect_totals* t) { udb_chunk_d* cp = (udb_chunk_d*)cv; udb_void c = (udb_void)(cv - base); udb_void data; uint8_t exp = cp->exp; uint8_t tp = cp->type; uint8_t flags = cp->flags; uint64_t sz = 0; if(exp == UDB_EXP_XL) { sz = ((udb_xl_chunk_d*)cp)->size; data = c + sizeof(udb_xl_chunk_d); } else { sz = (uint64_t)1<<exp; data = c + sizeof(udb_chunk_d); } if(v) { /* print chunk details */ printf("chunk at %llu exp=%d type=%d (%s) flags=0x%x " "size=%llu\n", ULL c, exp, tp, chunk_type2str(tp), flags, ULL sz); if(tp == udb_chunk_type_free) { udb_free_chunk_d* fp = (udb_free_chunk_d*)cp; printf("prev: %llu\n", ULL fp->prev); printf("next: %llu\n", ULL fp->next); } else { printf("ptrlist: %llu\n", ULL cp->ptrlist); } } if(v>=2 && tp != udb_chunk_type_free && cp->ptrlist) { /* follow the pointer list */ udb_rel_ptr* pl = UDB_REL_PTR(cp->ptrlist); while(pl->next) { printf("relptr %llu\n", ULL UDB_SYSTOREL(base, pl)); printf(" prev-ptrlist: %llu\n", ULL pl->prev); printf(" data: %llu\n", ULL pl->data); printf(" next-ptrlist: %llu\n", ULL pl->next); pl = UDB_REL_PTR(pl->next); } } /* print data details */ if(v>=2) { if(cp->type == udb_chunk_type_radtree) { struct udb_radtree_d* d = (struct udb_radtree_d*)UDB_REL(base, data); printf(" radtree count=%llu root=%llu\n", ULL d->count, ULL d->root.data); } else if(cp->type == udb_chunk_type_radnode) { struct udb_radnode_d* d = (struct udb_radnode_d*)UDB_REL(base, data); printf(" radnode pidx=%d offset=%d elem=%llu " "parent=%llu lookup=%llu\n", (int)d->pidx, (int)d->offset, ULL d->elem.data, ULL d->parent.data, ULL d->lookup.data); } else if(cp->type == udb_chunk_type_radarray) { struct udb_radarray_d* d = (struct udb_radarray_d*)UDB_REL( base, data); unsigned i; printf(" radarray len=%d capacity=%d str_cap=%d\n", (int)d->len, (int)d->capacity, (int)d->str_cap); for(i=0; i<d->len; i++) if(d->array[i].node.data) { printf(" [%u] node=%llu len=%d ", i, ULL d->array[i].node.data, (int)d->array[i].len); print_escaped( ((uint8_t*)&d->array[d->capacity])+ i*d->str_cap, (size_t)d->array[i].len); printf("\n"); } } else if(cp->type == udb_chunk_type_zone) { struct zone_d* d = (struct zone_d*)UDB_REL(base, data); printf(" zone "); print_dname(d->name, d->namelen); printf(" rr_count=%llu rrset_count=%llu expired=%d " "node=%llu domains=%llu\n", ULL d->rr_count, ULL d->rrset_count, (int)d->expired, ULL d->node.data, ULL d->domains.data); } else if(cp->type == udb_chunk_type_domain) { struct domain_d* d = (struct domain_d*)UDB_REL(base, data); printf(" domain "); print_dname(d->name, d->namelen); printf(" node=%llu rrsets=%llu\n", ULL d->node.data, ULL d->rrsets.data); } else if(cp->type == udb_chunk_type_rrset) { struct rrset_d* d = (struct rrset_d*)UDB_REL(base, data); printf(" rrset type=%d next=%llu rrs=%llu\n", (int)d->type, ULL d->next.data, ULL d->rrs.data); } else if(cp->type == udb_chunk_type_rr) { struct rr_d* d = (struct rr_d*)UDB_REL(base, data); printf(" rr type=%d class=%d ttl=%u next=%llu len=%d ", (int)d->type, (int)d->klass, (unsigned)d->ttl, ULL d->next.data, (int)d->len); print_hex(d->wire, d->len); printf("\n"); } else if(cp->type == udb_chunk_type_task) { struct task_list_d* d = (struct task_list_d*)UDB_REL(base, data); printf(" task type=%d next=%llu yesno=%d oldserial=%u newserial=%u zone=%s\n", (int)d->task_type, ULL d->next.data, (int)d->yesno, (unsigned)d->oldserial, (unsigned)d->newserial, d->size > sizeof(*d)? dname_to_string(d->zname, NULL):"\"\""); } } /* end verbosity 2 */ /* update stats */ t->exp_num[exp]++; if(tp == udb_chunk_type_free) { t->exp_free[exp]++; } else { t->type_num[tp]++; t->type_exp_num[tp][exp]++; } /* check end marker */ if(exp == UDB_EXP_XL) { if(sz != *(uint64_t*)(cv+sz-2*sizeof(uint64_t))) { printf(" end xl size is wrong: %llu\n", ULL *(uint64_t*)(cv+sz-2*sizeof(uint64_t))); } } if(exp != *(uint8_t*)(cv+sz-1)) { printf(" end exp is wrong: %d\n", *(uint8_t*)(cv+sz-1)); } return cv+sz; }
/* return value 0: syntaxerror,badIXFR, 1:OK, 2:done_and_skip_it */ static int apply_ixfr(namedb_type* db, FILE *in, const off_t* startpos, const char* zone, uint32_t serialno, nsd_options_t* opt, uint16_t id, uint32_t seq_nr, uint32_t seq_total, int* is_axfr, int* delete_mode, int* rr_count, size_t child_count) { uint32_t filelen, msglen, pkttype, timestamp[2]; int qcount, ancount, counter; buffer_type* packet; region_type* region; int i; uint16_t rrlen; const dname_type *dname_zone, *dname; zone_type* zone_db; domain_type* last_in_list; char file_zone_name[3072]; uint32_t file_serial, file_seq_nr; uint16_t file_id; off_t mempos; memmove(&mempos, startpos, sizeof(off_t)); if(fseeko(in, mempos, SEEK_SET) == -1) { log_msg(LOG_INFO, "could not fseeko: %s.", strerror(errno)); return 0; } /* read ixfr packet RRs and apply to in memory db */ if(!diff_read_32(in, &pkttype) || pkttype != DIFF_PART_IXFR) { log_msg(LOG_ERR, "could not read type or wrong type"); return 0; } if(!diff_read_32(in, ×tamp[0]) || !diff_read_32(in, ×tamp[1])) { log_msg(LOG_ERR, "could not read timestamp"); return 0; } if(!diff_read_32(in, &filelen)) { log_msg(LOG_ERR, "could not read len"); return 0; } /* read header */ if(filelen < QHEADERSZ + sizeof(uint32_t)*3 + sizeof(uint16_t)) { log_msg(LOG_ERR, "msg too short"); return 0; } region = region_create(xalloc, free); if(!region) { log_msg(LOG_ERR, "out of memory"); return 0; } if(!diff_read_str(in, file_zone_name, sizeof(file_zone_name)) || !diff_read_32(in, &file_serial) || !diff_read_16(in, &file_id) || !diff_read_32(in, &file_seq_nr)) { log_msg(LOG_ERR, "could not part data"); region_destroy(region); return 0; } if(strcmp(file_zone_name, zone) != 0 || serialno != file_serial || id != file_id || seq_nr != file_seq_nr) { log_msg(LOG_ERR, "internal error: reading part with changed id"); region_destroy(region); return 0; } msglen = filelen - sizeof(uint32_t)*3 - sizeof(uint16_t) - strlen(file_zone_name); packet = buffer_create(region, QIOBUFSZ); dname_zone = dname_parse(region, zone); zone_db = find_zone(db, dname_zone, opt, child_count); if(!zone_db) { log_msg(LOG_ERR, "no zone exists"); region_destroy(region); /* break out and stop the IXFR, ignore it */ return 2; } if(msglen > QIOBUFSZ) { log_msg(LOG_ERR, "msg too long"); region_destroy(region); return 0; } buffer_clear(packet); if(fread(buffer_begin(packet), msglen, 1, in) != 1) { log_msg(LOG_ERR, "short fread: %s", strerror(errno)); region_destroy(region); return 0; } buffer_set_limit(packet, msglen); /* only answer section is really used, question, additional and authority section RRs are skipped */ qcount = QDCOUNT(packet); ancount = ANCOUNT(packet); buffer_skip(packet, QHEADERSZ); /* skip queries */ for(i=0; i<qcount; ++i) if(!packet_skip_rr(packet, 1)) { log_msg(LOG_ERR, "bad RR in question section"); region_destroy(region); return 0; } DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: started packet for zone %s", dname_to_string(dname_zone, 0))); /* first RR: check if SOA and correct zone & serialno */ if(*rr_count == 0) { DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s parse first RR", dname_to_string(dname_zone, 0))); dname = dname_make_from_packet(region, packet, 1, 1); if(!dname) { log_msg(LOG_ERR, "could not parse dname"); region_destroy(region); return 0; } if(dname_compare(dname_zone, dname) != 0) { log_msg(LOG_ERR, "SOA dname %s not equal to zone", dname_to_string(dname,0)); log_msg(LOG_ERR, "zone dname is %s", dname_to_string(dname_zone,0)); region_destroy(region); return 0; } if(!buffer_available(packet, 10)) { log_msg(LOG_ERR, "bad SOA RR"); region_destroy(region); return 0; } if(buffer_read_u16(packet) != TYPE_SOA || buffer_read_u16(packet) != CLASS_IN) { log_msg(LOG_ERR, "first RR not SOA IN"); region_destroy(region); return 0; } buffer_skip(packet, sizeof(uint32_t)); /* ttl */ if(!buffer_available(packet, buffer_read_u16(packet)) || !packet_skip_dname(packet) /* skip prim_ns */ || !packet_skip_dname(packet) /* skip email */) { log_msg(LOG_ERR, "bad SOA RR"); region_destroy(region); return 0; } if(buffer_read_u32(packet) != serialno) { buffer_skip(packet, -4); log_msg(LOG_ERR, "SOA serial %d different from commit %d", buffer_read_u32(packet), serialno); region_destroy(region); return 0; } buffer_skip(packet, sizeof(uint32_t)*4); counter = 1; *rr_count = 1; *is_axfr = 0; *delete_mode = 0; DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s start count %d, ax %d, delmode %d", dname_to_string(dname_zone, 0), *rr_count, *is_axfr, *delete_mode)); } else counter = 0; last_in_list = zone_db->apex; for(; counter < ancount; ++counter,++(*rr_count)) { uint16_t type, klass; uint32_t ttl; if(!(dname=dname_make_from_packet(region, packet, 1,1))) { log_msg(LOG_ERR, "bad xfr RR dname %d", *rr_count); region_destroy(region); return 0; } if(!buffer_available(packet, 10)) { log_msg(LOG_ERR, "bad xfr RR format %d", *rr_count); region_destroy(region); return 0; } type = buffer_read_u16(packet); klass = buffer_read_u16(packet); ttl = buffer_read_u32(packet); rrlen = buffer_read_u16(packet); if(!buffer_available(packet, rrlen)) { log_msg(LOG_ERR, "bad xfr RR rdata %d, len %d have %d", *rr_count, rrlen, (int)buffer_remaining(packet)); region_destroy(region); return 0; } DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s parsed count %d, ax %d, delmode %d", dname_to_string(dname_zone, 0), *rr_count, *is_axfr, *delete_mode)); if(*rr_count == 1 && type != TYPE_SOA) { /* second RR: if not SOA: this is an AXFR; delete all zone contents */ delete_zone_rrs(db, zone_db); /* add everything else (incl end SOA) */ *delete_mode = 0; *is_axfr = 1; DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s sawAXFR count %d, ax %d, delmode %d", dname_to_string(dname_zone, 0), *rr_count, *is_axfr, *delete_mode)); } if(*rr_count == 1 && type == TYPE_SOA) { /* if the serial no of the SOA equals the serialno, then AXFR */ size_t bufpos = buffer_position(packet); uint32_t thisserial; if(!packet_skip_dname(packet) || !packet_skip_dname(packet) || buffer_remaining(packet) < sizeof(uint32_t)*5) { log_msg(LOG_ERR, "bad xfr SOA RR formerr."); region_destroy(region); return 0; } thisserial = buffer_read_u32(packet); if(thisserial == serialno) { /* AXFR */ delete_zone_rrs(db, zone_db); *delete_mode = 0; *is_axfr = 1; } /* must have stuff in memory for a successful IXFR, * the serial number of the SOA has been checked * previously (by check_for_bad_serial) if it exists */ if(!*is_axfr && !domain_find_rrset(zone_db->apex, zone_db, TYPE_SOA)) { log_msg(LOG_ERR, "%s SOA serial %d is not " "in memory, skip IXFR", zone, serialno); region_destroy(region); /* break out and stop the IXFR, ignore it */ return 2; } buffer_set_position(packet, bufpos); } if(type == TYPE_SOA && !*is_axfr) { /* switch from delete-part to add-part and back again, just before soa - so it gets deleted and added too */ /* this means we switch to delete mode for the final SOA */ *delete_mode = !*delete_mode; DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s IXFRswapdel count %d, ax %d, delmode %d", dname_to_string(dname_zone, 0), *rr_count, *is_axfr, *delete_mode)); } if(type == TYPE_TSIG || type == TYPE_OPT) { /* ignore pseudo RRs */ buffer_skip(packet, rrlen); continue; } DEBUG(DEBUG_XFRD,2, (LOG_INFO, "xfr %s RR dname is %s type %s", *delete_mode?"del":"add", dname_to_string(dname,0), rrtype_to_string(type))); if(*delete_mode) { /* delete this rr */ if(!*is_axfr && type == TYPE_SOA && counter==ancount-1 && seq_nr == seq_total-1) { continue; /* do not delete final SOA RR for IXFR */ } if(!delete_RR(db, dname, type, klass, last_in_list, packet, rrlen, zone_db, region, *is_axfr)) { region_destroy(region); return 0; } if (!*is_axfr && last_in_list->nextdiff) { last_in_list = last_in_list->nextdiff; } } else { /* add this rr */ if(!add_RR(db, dname, type, klass, ttl, packet, rrlen, zone_db, *is_axfr)) { region_destroy(region); return 0; } } } fix_empty_terminals(zone_db); region_destroy(region); return 1; }
static zone_type* find_zone(namedb_type* db, const dname_type* zone_name, nsd_options_t* opt, size_t child_count) { domain_type *domain; zone_type* zone; zone_options_t* opts; domain = domain_table_find(db->domains, zone_name); if(!domain) { DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfr: creating domain %s", dname_to_string(zone_name,0))); /* create the zone and domain of apex (zone has config options) */ domain = domain_table_insert(db->domains, zone_name); } else { /* O(1) if SOA exists */ zone = domain_find_zone(domain); /* if domain was empty (no rrsets, empty zone) search in zonelist */ /* check apex to make sure we don't find a parent zone */ if(!zone || zone->apex != domain) zone = namedb_find_zone(db, domain); if(zone) { assert(zone->apex == domain); return zone; } } /* lookup in config */ opts = zone_options_find(opt, domain_dname(domain)); if(!opts) { log_msg(LOG_ERR, "xfr: zone %s not in config.", dname_to_string(zone_name,0)); return 0; } /* create the zone */ DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfr: creating zone_type %s", dname_to_string(zone_name,0))); zone = (zone_type *) region_alloc(db->region, sizeof(zone_type)); if(!zone) { log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); exit(1); } zone->next = db->zones; zone->opts = opts; db->zones = zone; db->zone_count++; zone->apex = domain; zone->soa_rrset = 0; zone->soa_nx_rrset = 0; zone->ns_rrset = 0; #ifdef NSEC3 zone->nsec3_soa_rr = NULL; zone->nsec3_last = NULL; #endif zone->dirty = region_alloc(db->region, sizeof(uint8_t)*child_count); if(!zone->dirty) { log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); exit(1); } memset(zone->dirty, 0, sizeof(uint8_t)*child_count); #ifdef NSEC3 #ifndef FULL_PREHASH zone->nsec3_domains = NULL; if (0 != zone_nsec3_domains_create(db, zone)) { log_msg(LOG_ERR, "xfr: zone NSEC3 domains " "memory allocation failure"); return 0; } #endif /* !FULL_PREHASH */ #endif /* NSEC3 */ zone->number = db->zone_count; zone->is_secure = 0; zone->updated = 1; zone->is_ok = 0; return zone; }
static int add_RR(namedb_type* db, const dname_type* dname, uint16_t type, uint16_t klass, uint32_t ttl, buffer_type* packet, size_t rdatalen, zone_type *zone, int is_axfr) { domain_type* domain; rrset_type* rrset; rdata_atom_type *rdatas; rr_type *rrs_old; ssize_t rdata_num; int rrnum; domain = domain_table_find(db->domains, dname); if(!domain) { /* create the domain */ domain = domain_table_insert(db->domains, dname); } rrset = domain_find_rrset(domain, zone, type); if(!rrset) { /* create the rrset */ rrset = region_alloc(db->region, sizeof(rrset_type)); if(!rrset) { log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); exit(1); } rrset->zone = zone; rrset->rrs = 0; rrset->rr_count = 0; domain_add_rrset(domain, rrset); } /* dnames in rdata are normalized, conform RFC 4035, * Section 6.2 */ rdata_num = rdata_wireformat_to_rdata_atoms( db->region, db->domains, type, rdatalen, packet, &rdatas); if(rdata_num == -1) { log_msg(LOG_ERR, "diff: bad rdata for %s", dname_to_string(dname,0)); return 0; } rrnum = find_rr_num(rrset, type, klass, rdatas, rdata_num); if(rrnum != -1) { DEBUG(DEBUG_XFRD, 2, (LOG_ERR, "diff: RR <%s, %s> already exists", dname_to_string(dname,0), rrtype_to_string(type))); /* ignore already existing RR: lenient accepting of messages */ return 1; } /* re-alloc the rrs and add the new */ rrs_old = rrset->rrs; rrset->rrs = region_alloc(db->region, (rrset->rr_count+1) * sizeof(rr_type)); if(!rrset->rrs) { log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); exit(1); } if(rrs_old) memcpy(rrset->rrs, rrs_old, rrset->rr_count * sizeof(rr_type)); region_recycle(db->region, rrs_old, sizeof(rr_type) * rrset->rr_count); rrset->rr_count ++; rrset->rrs[rrset->rr_count - 1].owner = domain; rrset->rrs[rrset->rr_count - 1].rdatas = rdatas; rrset->rrs[rrset->rr_count - 1].ttl = ttl; rrset->rrs[rrset->rr_count - 1].type = type; rrset->rrs[rrset->rr_count - 1].klass = klass; rrset->rrs[rrset->rr_count - 1].rdata_count = rdata_num; /* see if it is a SOA */ if(domain == zone->apex) { if(type == TYPE_SOA) { uint32_t soa_minimum; zone->soa_rrset = rrset; zone->updated = 1; /* BUG #103 tweaked SOA ttl value */ if(zone->soa_nx_rrset == 0) { zone->soa_nx_rrset = region_alloc(db->region, sizeof(rrset_type)); if(!zone->soa_nx_rrset) { log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); exit(1); } zone->soa_nx_rrset->rr_count = 1; zone->soa_nx_rrset->next = 0; zone->soa_nx_rrset->zone = zone; zone->soa_nx_rrset->rrs = region_alloc(db->region, sizeof(rr_type)); if(!zone->soa_nx_rrset->rrs) { log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); exit(1); } } memcpy(zone->soa_nx_rrset->rrs, rrset->rrs, sizeof(rr_type)); memcpy(&soa_minimum, rdata_atom_data(rrset->rrs->rdatas[6]), rdata_atom_size(rrset->rrs->rdatas[6])); if (rrset->rrs->ttl > ntohl(soa_minimum)) { rrset->zone->soa_nx_rrset->rrs[0].ttl = ntohl(soa_minimum); } domain->has_SOA = 1; } if(type == TYPE_NS) { zone->ns_rrset = rrset; } if(type == TYPE_RRSIG) { int i; for (i = 0; i < rrset->rr_count; ++i) { if (rr_rrsig_type_covered(&rrset->rrs[i]) == TYPE_DNSKEY) { zone->is_secure = 1; break; } } } } #ifdef NSEC3 #ifndef FULL_PREHASH if ((type == TYPE_NSEC3) && (rrset->rr_count == 1)) { /* NSEC3 RRset just added */ if (0 != namedb_add_nsec3_domain(db, domain, zone)) return 0; } if (is_axfr == 0) { struct domain *parent = domain; do { if (0 != namedb_add_nsec3_mod_domain(db, parent)) return 0; parent = parent->parent; } while (parent != zone->apex->parent); } #else (void)is_axfr; #endif /* !FULL_PREHASH */ #endif /* NSEC3 */ return 1; }
static int delete_RR(namedb_type* db, const dname_type* dname, uint16_t type, uint16_t klass, domain_type* prevdomain, buffer_type* packet, size_t rdatalen, zone_type *zone, region_type* temp_region, int is_axfr) { domain_type *domain; rrset_type *rrset; domain = domain_table_find(db->domains, dname); if(!domain) { log_msg(LOG_WARNING, "diff: domain %s does not exist", dname_to_string(dname,0)); buffer_skip(packet, rdatalen); return 1; /* not fatal error */ } rrset = domain_find_rrset(domain, zone, type); if(!rrset) { log_msg(LOG_WARNING, "diff: rrset %s does not exist", dname_to_string(dname,0)); buffer_skip(packet, rdatalen); return 1; /* not fatal error */ } else { /* find the RR in the rrset */ domain_table_type *temptable; rdata_atom_type *rdatas; ssize_t rdata_num; int rrnum; temptable = domain_table_create(temp_region); /* This will ensure that the dnames in rdata are * normalized, conform RFC 4035, section 6.2 */ rdata_num = rdata_wireformat_to_rdata_atoms( temp_region, temptable, type, rdatalen, packet, &rdatas); if(rdata_num == -1) { log_msg(LOG_ERR, "diff: bad rdata for %s", dname_to_string(dname,0)); return 0; } rrnum = find_rr_num(rrset, type, klass, rdatas, rdata_num); if(rrnum == -1) { log_msg(LOG_WARNING, "diff: RR <%s, %s> does not exist", dname_to_string(dname,0), rrtype_to_string(type)); return 1; /* not fatal error */ } #ifdef NSEC3 #ifndef FULL_PREHASH if (is_axfr == 0) { struct domain *parent = domain; do { if (0 != namedb_add_nsec3_mod_domain(db, parent)) { return 0; } parent = parent->parent; } while (parent != zone->apex->parent); } #else (void)is_axfr; #endif /* !FULL_PREHASH */ #endif /* NSEC3 */ if(rrset->rr_count == 1) { /* delete entire rrset */ domain = rrset_delete(db, domain, rrset); if (domain && domain != prevdomain && !domain->nextdiff) { /* this domain is not yet in the diff chain */ prevdomain->nextdiff = domain; } } else { /* swap out the bad RR and decrease the count */ rr_type* rrs_orig = rrset->rrs; add_rdata_to_recyclebin(db, &rrset->rrs[rrnum]); if(rrnum < rrset->rr_count-1) rrset->rrs[rrnum] = rrset->rrs[rrset->rr_count-1]; memset(&rrset->rrs[rrset->rr_count-1], 0, sizeof(rr_type)); /* realloc the rrs array one smaller */ rrset->rrs = region_alloc_init(db->region, rrs_orig, sizeof(rr_type) * (rrset->rr_count-1)); if(!rrset->rrs) { log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__); exit(1); } region_recycle(db->region, rrs_orig, sizeof(rr_type) * rrset->rr_count); rrset->rr_count --; } } return 1; }
struct namedb * namedb_open (const char *filename, nsd_options_t* opt, size_t num_children) { namedb_type *db; /* * Temporary region used while loading domain names from the * database. The region is freed after each time a dname is * read from the database. */ region_type *dname_region; /* * Temporary region used to store array of domains and zones * while loading the database. The region is freed before * returning. */ region_type *temp_region; uint32_t dname_count; domain_type **domains; /* Indexed by domain number. */ uint32_t zone_count; zone_type **zones; /* Indexed by zone number. */ uint32_t i; uint32_t rrset_count = 0; uint32_t rr_count = 0; rrset_type *rrset; DEBUG(DEBUG_DBACCESS, 2, (LOG_INFO, "sizeof(namedb_type) = %lu\n", (unsigned long) sizeof(namedb_type))); DEBUG(DEBUG_DBACCESS, 2, (LOG_INFO, "sizeof(zone_type) = %lu\n", (unsigned long) sizeof(zone_type))); DEBUG(DEBUG_DBACCESS, 2, (LOG_INFO, "sizeof(domain_type) = %lu\n", (unsigned long) sizeof(domain_type))); DEBUG(DEBUG_DBACCESS, 2, (LOG_INFO, "sizeof(rrset_type) = %lu\n", (unsigned long) sizeof(rrset_type))); DEBUG(DEBUG_DBACCESS, 2, (LOG_INFO, "sizeof(rr_type) = %lu\n", (unsigned long) sizeof(rr_type))); DEBUG(DEBUG_DBACCESS, 2, (LOG_INFO, "sizeof(rdata_atom_type) = %lu\n", (unsigned long) sizeof(rdata_atom_type))); DEBUG(DEBUG_DBACCESS, 2, (LOG_INFO, "sizeof(rbnode_t) = %lu\n", (unsigned long) sizeof(rbnode_t))); if ((db = namedb_create()) == NULL) { log_msg(LOG_ERR, "insufficient memory to create database"); return NULL; } db->filename = region_strdup(db->region, filename); if (gettimeofday(&(db->diff_timestamp), NULL) != 0) { log_msg(LOG_ERR, "unable to load %s: cannot initialize" "timestamp", db->filename); namedb_destroy(db); return NULL; } /* Open it... */ db->fd = fopen(db->filename, "r"); if (db->fd == NULL) { log_msg(LOG_ERR, "unable to load %s: %s", db->filename, strerror(errno)); namedb_destroy(db); return NULL; } if (!read_magic(db)) { log_msg(LOG_ERR, "corrupted database (read magic): %s", db->filename); log_msg(LOG_ERR, "cannot load database, incompatible version " "number. Please rebuild database and " "start again."); namedb_close(db); return NULL; } if (!read_size(db, &zone_count)) { log_msg(LOG_ERR, "corrupted database (read size): %s", db->filename); namedb_close(db); return NULL; } DEBUG(DEBUG_DBACCESS, 1, (LOG_INFO, "Retrieving %lu zones\n", (unsigned long) zone_count)); temp_region = region_create(xalloc, free); dname_region = region_create(xalloc, free); db->zone_count = zone_count; zones = (zone_type **) region_alloc(temp_region, zone_count * sizeof(zone_type *)); for (i = 0; i < zone_count; ++i) { const dname_type *dname = read_dname(db->fd, dname_region); if (!dname) { log_msg(LOG_ERR, "corrupted database (read dname): %s", db->filename); region_destroy(dname_region); region_destroy(temp_region); namedb_close(db); return NULL; } zones[i] = (zone_type *) region_alloc(db->region, sizeof(zone_type)); zones[i]->next = db->zones; db->zones = zones[i]; zones[i]->apex = domain_table_insert(db->domains, dname); zones[i]->soa_rrset = NULL; zones[i]->soa_nx_rrset = NULL; zones[i]->ns_rrset = NULL; #ifdef NSEC3 zones[i]->nsec3_soa_rr = NULL; zones[i]->nsec3_last = NULL; #endif zones[i]->opts = zone_options_find(opt, domain_dname(zones[i]->apex)); zones[i]->number = i + 1; zones[i]->is_secure = 0; zones[i]->updated = 1; zones[i]->is_ok = 0; zones[i]->dirty = region_alloc(db->region, sizeof(uint8_t)*num_children); memset(zones[i]->dirty, 0, sizeof(uint8_t)*num_children); if(!zones[i]->opts) { log_msg(LOG_ERR, "cannot load database. Zone %s in db " "%s, but not in config file (might " "happen if you edited the config " "file). Please rebuild database and " "start again.", dname_to_string(dname, NULL), db->filename); region_destroy(dname_region); region_destroy(temp_region); namedb_close(db); return NULL; } #ifdef NSEC3 #ifndef FULL_PREHASH zones[i]->nsec3_domains = NULL; if (0 != zone_nsec3_domains_create(db, zones[i])) { log_msg(LOG_ERR, "insufficient memory for NSEC3 tree, " "unable to read database"); region_destroy(dname_region); region_destroy(temp_region); namedb_close(db); return NULL; } #endif /* !FULL_PREHASH */ #endif /* NSEC3 */ region_free_all(dname_region); } if (!read_size(db, &dname_count)) { log_msg(LOG_ERR, "corrupted database (read size): %s", db->filename); region_destroy(dname_region); region_destroy(temp_region); namedb_close(db); return NULL; } DEBUG(DEBUG_DBACCESS, 1, (LOG_INFO, "Retrieving %lu domain names\n", (unsigned long) dname_count)); domains = (domain_type **) region_alloc( temp_region, dname_count * sizeof(domain_type *)); for (i = 0; i < dname_count; ++i) { const dname_type *dname = read_dname(db->fd, dname_region); if (!dname) { log_msg(LOG_ERR, "corrupted database (read dname): %s", db->filename); region_destroy(dname_region); region_destroy(temp_region); namedb_close(db); return NULL; } domains[i] = domain_table_insert(db->domains, dname); region_free_all(dname_region); } region_destroy(dname_region); #ifndef NDEBUG fprintf(stderr, "database region after loading domain names: "); region_dump_stats(db->region, stderr); fprintf(stderr, "\n"); #endif while ((rrset = read_rrset(db, dname_count, domains, zone_count, zones))) { ++rrset_count; rr_count += rrset->rr_count; } DEBUG(DEBUG_DBACCESS, 1, (LOG_INFO, "Retrieved %lu RRs in %lu RRsets\n", (unsigned long) rr_count, (unsigned long) rrset_count)); region_destroy(temp_region); if ((db->crc_pos = ftello(db->fd)) == -1) { log_msg(LOG_ERR, "ftello %s failed: %s", db->filename, strerror(errno)); namedb_close(db); return NULL; } if (!read_size(db, &db->crc)) { log_msg(LOG_ERR, "corrupted database (read size): %s", db->filename); namedb_close(db); return NULL; } if (!read_magic(db)) { log_msg(LOG_ERR, "corrupted database (read magic): %s", db->filename); log_msg(LOG_ERR, "cannot load database, incompatible version " "number. Please rebuild database and " "start again."); namedb_close(db); return NULL; } fclose(db->fd); db->fd = NULL; #ifndef NDEBUG fprintf(stderr, "database region after loading database: "); region_dump_stats(db->region, stderr); fprintf(stderr, "\n"); #endif return db; }