static int xfrd_parse_soa_info(buffer_type* packet, xfrd_soa_t* soa) { if(!buffer_available(packet, 10)) return 0; soa->type = htons(buffer_read_u16(packet)); soa->klass = htons(buffer_read_u16(packet)); soa->ttl = htonl(buffer_read_u32(packet)); if(ntohs(soa->type) != TYPE_SOA || ntohs(soa->klass) != CLASS_IN) { return 0; } if(!buffer_available(packet, buffer_read_u16(packet)) /* rdata length */ || !(soa->prim_ns[0] = dname_make_wire_from_packet(soa->prim_ns+1, packet, 1)) || !(soa->email[0] = dname_make_wire_from_packet(soa->email+1, packet, 1))) { return 0; } soa->rdata_count = 7; /* rdata in SOA */ soa->serial = htonl(buffer_read_u32(packet)); soa->refresh = htonl(buffer_read_u32(packet)); soa->retry = htonl(buffer_read_u32(packet)); soa->expire = htonl(buffer_read_u32(packet)); soa->minimum = htonl(buffer_read_u32(packet)); return 1; }
int bvlc_register_fd_decode(bvlc_register_fd_t *message, buffer_t *buf) { uint16_t length; uint16_t ttl; return 1 && buffer_read_u16(buf, &length) && length == 6 && buffer_read_u16(buf, &ttl) && bvlc_register_fd_init(message, ttl); }
int bvlc_result_decode(bvlc_result_t *message, buffer_t *buf) { int ret = 1; uint16_t length; uint16_t result_code; return 1 && buffer_read_u16(buf, &length) && length == 6 && buffer_read_u16(buf, &result_code) && bvlc_result_init(message, (bvlc_result_code_t)result_code); }
/* * Check the RRs in an IXFR/AXFR reply. * returns 0 on error, 1 on correct parseable packet. * done = 1 if the last SOA in an IXFR/AXFR has been seen. * soa then contains that soa info. * (soa contents is modified by the routine) */ static int xfrd_xfr_check_rrs(xfrd_zone_t* zone, buffer_type* packet, size_t count, int *done, xfrd_soa_t* soa) { /* first RR has already been checked */ uint16_t type, rrlen; size_t i, soapos; for(i=0; i<count; ++i,++zone->msg_rr_count) { if(!packet_skip_dname(packet)) return 0; if(!buffer_available(packet, 10)) return 0; soapos = buffer_position(packet); type = buffer_read_u16(packet); (void)buffer_read_u16(packet); /* class */ (void)buffer_read_u32(packet); /* ttl */ rrlen = buffer_read_u16(packet); if(!buffer_available(packet, rrlen)) return 0; if(type == TYPE_SOA) { /* check the SOAs */ size_t mempos = buffer_position(packet); buffer_set_position(packet, soapos); if(!xfrd_parse_soa_info(packet, soa)) return 0; if(zone->msg_rr_count == 1 && ntohl(soa->serial) != zone->msg_new_serial) { /* 2nd RR is SOA with lower serial, this is an IXFR */ zone->msg_is_ixfr = 1; if(!zone->soa_disk_acquired) return 0; /* got IXFR but need AXFR */ if(ntohl(soa->serial) != ntohl(zone->soa_disk.serial)) return 0; /* bad start serial in IXFR */ zone->msg_old_serial = ntohl(soa->serial); } else if(ntohl(soa->serial) == zone->msg_new_serial) { /* saw another SOA of new serial. */ if(zone->msg_is_ixfr == 1) { zone->msg_is_ixfr = 2; /* seen middle SOA in ixfr */ } else { /* 2nd SOA for AXFR or 3rd newSOA for IXFR */ *done = 1; } } buffer_set_position(packet, mempos); } buffer_skip(packet, rrlen); } /* packet seems to have a valid DNS RR structure */ return 1; }
int get_record(buffer_type *buffer, struct decompress_ctx * decompress, struct rr *record) { int ret; if (buffer == NULL || record == NULL) return -1; ret = dns_name_from_wire(buffer, decompress, record->name); if (ret == -1) return -1; if (!buffer_available(buffer, 2 * sizeof(u_int16_t))) return -1; record->type = buffer_read_u16(buffer); buffer_skip(buffer, sizeof(u_int16_t) + sizeof(u_int32_t) + sizeof(u_int16_t)); switch (record->type) { case TYPE_A: if (!buffer_available(buffer, sizeof(u_int32_t))) return -1; buffer_read(buffer, record->rdata, 4); break; case TYPE_CNAME: ret = dns_name_from_wire(buffer, decompress, record->rdata); if (ret == -1) return ret; break; default: return -1; } return 0; }
int bvlc_read_bdt_decode(bvlc_read_bdt_t *message, buffer_t *buf) { uint16_t length; return 1 && buffer_read_u16(buf, &length) && length == 4; }
rr_type * packet_read_rr(region_type *region, domain_table_type *owners, buffer_type *packet, int question_section) { const dname_type *owner; uint16_t rdlength; ssize_t rdata_count; rdata_atom_type *rdatas; rr_type *result = (rr_type *) region_alloc(region, sizeof(rr_type)); owner = dname_make_from_packet(region, packet, 1, 1); if (!owner || !buffer_available(packet, 2*sizeof(uint16_t))) { return NULL; } result->owner = domain_table_insert(owners, owner); result->type = buffer_read_u16(packet); result->klass = buffer_read_u16(packet); if (question_section) { result->ttl = 0; result->rdata_count = 0; result->rdatas = NULL; return result; } else if (!buffer_available(packet, sizeof(uint32_t) + sizeof(uint16_t))) { return NULL; } result->ttl = buffer_read_u32(packet); rdlength = buffer_read_u16(packet); if (!buffer_available(packet, rdlength)) { return NULL; } rdata_count = rdata_wireformat_to_rdata_atoms( region, owners, result->type, rdlength, packet, &rdatas); if (rdata_count == -1) { return NULL; } result->rdata_count = rdata_count; result->rdatas = rdatas; return result; }
int bvlc_delete_fdt_entry_decode(bvlc_delete_fdt_entry_t *message, buffer_t *buf) { uint16_t length; return 1 && buffer_read_u16(buf, &length) && length == 10 && bvlc_delete_fdt_entry_init(message) && buffer_read_raw(buf, message->bip_address, 6); }
int bvlc_read_fdt_ack_decode(bvlc_read_fdt_ack_t *message, buffer_t *buf) { int ret = 1; uint16_t length; ret = 1 && buffer_read_u16(buf, &length) && ((length - 4) % 10) == 0 && bvlc_read_fdt_ack_init(message, (length - 4) / 10); if(ret) { for(uint16_t i = 0; ret && i < message->entry_count; i++) { ret = 1 && buffer_read_raw(buf, message->entries[i].bip_address, 6) && buffer_read_u16(buf, &message->entries[i].original_ttl) && buffer_read_u16(buf, &message->entries[i].ttl); } } return ret; }
u_int16_t dns_get_qtype(struct dnshdr* hdr, buffer_type* buffer) { u_int16_t qtype; if (ntohs(hdr->qdcount) != 1 || buffer == NULL) return TYPE_NONE; qtype = buffer_read_u16(buffer); if (qtype < TYPE_ALL && qtype > TYPE_NONE) { return qtype; } return TYPE_NONE; }
u_int16_t dns_get_qklass(struct dnshdr* hdr, buffer_type* buffer) { u_int16_t qklass; if (ntohs(hdr->qdcount) != 1 || buffer == NULL) return CLASS_NONE; qklass = buffer_read_u16(buffer); if (qklass < CLASS_ALL && qklass > CLASS_NONE) { return qklass; } return CLASS_NONE; }
int packet_read_query_section(buffer_type *packet, uint8_t* dst, uint16_t* qtype, uint16_t* qclass) { uint8_t *query_name = buffer_current(packet); uint8_t *src = query_name; size_t len; while (*src) { /* * If we are out of buffer limits or we have a pointer * in question dname or the domain name is longer than * MAXDOMAINLEN ... */ if ((*src & 0xc0) || (src + *src + 2 > buffer_end(packet)) || (src + *src + 2 > query_name + MAXDOMAINLEN)) { return 0; } memcpy(dst, src, *src + 1); dst += *src + 1; src += *src + 1; } *dst++ = *src++; /* Make sure name is not too long or we have stripped packet... */ len = src - query_name; if (len > MAXDOMAINLEN || (src + 2*sizeof(uint16_t) > buffer_end(packet))) { return 0; } buffer_set_position(packet, src - buffer_begin(packet)); *qtype = buffer_read_u16(packet); *qclass = buffer_read_u16(packet); return 1; }
int bvlc_distribute_broadcast_decode(bvlc_distribute_broadcast_t *message, buffer_t *buf) { uint16_t length; int ret = 1 && buffer_read_u16(buf, &length) && length >= 4; if(ret) { message->npdu = buf->position; message->npdu_length = length - 4; } return ret; }
/** * Parse SOA RR in packet. * (kind of similar to xfrd_parse_soa) * */ static int query_parse_soa(buffer_type* buffer, uint32_t* serial) { ldns_rr_type type = 0; ods_log_assert(buffer); if (!buffer_available(buffer, 10)) { ods_log_error("[%s] bad soa: packet too short", query_str); return 0; } type = (ldns_rr_type) buffer_read_u16(buffer); if (type != LDNS_RR_TYPE_SOA) { ods_log_error("[%s] bad soa: rr is not soa (%d)", query_str, type); return 0; } (void)buffer_read_u16(buffer); (void)buffer_read_u32(buffer); /* rdata length */ if (!buffer_available(buffer, buffer_read_u16(buffer))) { ods_log_error("[%s] bad soa: missing rdlength", query_str); return 0; } /* MNAME */ if (!buffer_skip_dname(buffer)) { ods_log_error("[%s] bad soa: missing mname", query_str); return 0; } /* RNAME */ if (!buffer_skip_dname(buffer)) { ods_log_error("[%s] bad soa: missing rname", query_str); return 0; } if (serial) { *serial = buffer_read_u32(buffer); } return 1; }
int bvlc_forwarded_npdu_decode(bvlc_forwarded_npdu_t *message, buffer_t *buf) { uint16_t length; int ret = 1 && buffer_read_u16(buf, &length) && length >= 10 && buffer_read_raw(buf, message->original_bip_address, 6); if(ret) { message->npdu = buf->position; message->npdu_length = length - 10; } return 1; }
int bvlc_original_broadcast_decode(bvlc_original_broadcast_t *message, buffer_t *buf) { uint16_t length; int ret = 1 && buffer_read_u16(buf, &length) && length >= 4 && bvlc_original_broadcast_init(message); if(ret) { message->npdu = buf->position; message->npdu_length = length - 4; } return ret; }
int bacdl_connection_receive_msg(bacdl_connection_t *connection, bacdl_msg_t *msg) { int ret = 1; uint8_t magic; uint8_t code; uint16_t length; buffer_t *buf = &connection->rcv_buf; if(read(connection->socket, buf->bytes, 4) < 4) { warn("failed to read initial bytes of bacdl message"); ret = 0; goto done; } ret = 1 && buffer_read_u8(buf, &magic) && magic == BACDL_MAGIC_BYTE && buffer_read_u8(buf, &code) && buffer_read_u16(buf, &length); if(!ret) { warn("failed to read header info from bacdl message"); goto done; } if(read(connection->socket, buf->bytes + 4, length - 4) < length - 4) { warn("failed to read content of a bacdl message"); ret = 0; goto done; } buffer_reset(buf); if(!bacdl_msg_decode(msg, buf)) { warn("failed to decode bacdl message"); ret = 0; goto done; } done: if(!ret) { } buffer_reset(&connection->rcv_buf); return ret; }
static int rdata_apl_to_string(buffer_type *output, rdata_atom_type rdata, rr_type* ATTR_UNUSED(rr)) { int result = 0; buffer_type packet; buffer_create_from( &packet, rdata_atom_data(rdata), rdata_atom_size(rdata)); if (buffer_available(&packet, 4)) { uint16_t address_family = buffer_read_u16(&packet); uint8_t prefix = buffer_read_u8(&packet); uint8_t length = buffer_read_u8(&packet); int negated = length & APL_NEGATION_MASK; int af = -1; length &= APL_LENGTH_MASK; switch (address_family) { case 1: af = AF_INET; break; case 2: af = AF_INET6; break; } if (af != -1 && buffer_available(&packet, length)) { char text_address[1000]; uint8_t address[128]; memset(address, 0, sizeof(address)); buffer_read(&packet, address, length); if (inet_ntop(af, address, text_address, sizeof(text_address))) { buffer_printf(output, "%s%d:%s/%d", negated ? "!" : "", (int) address_family, text_address, (int) prefix); result = 1; } } } return result; }
int bvlc_write_bdt_decode(bvlc_write_bdt_t *message, buffer_t *buf) { int ret; uint16_t length; ret = 1 && buffer_read_u16(buf, &length) && ((length - 4) % 10) == 0 && bvlc_write_bdt_init(message, (length - 4) / 10); if(ret) { for(uint16_t i = 0; ret && i < message->entry_count; i++) { ret = 1 && buffer_read_raw(buf, message->entries[i].bip_address, 6) && buffer_read_raw(buf, message->entries[i].broadcast_mask, 4); } } return ret; }
int packet_skip_rr(buffer_type *packet, int question_section) { if (!packet_skip_dname(packet)) return 0; if (question_section) { if (!buffer_available(packet, 4)) return 0; buffer_skip(packet, 4); } else { uint16_t rdata_size; if (!buffer_available(packet, 10)) return 0; buffer_skip(packet, 8); rdata_size = buffer_read_u16(packet); if (!buffer_available(packet, rdata_size)) return 0; buffer_skip(packet, rdata_size); } return 1; }
int skip_record(buffer_type *buffer) { char* p; u_int16_t rdlength; p = buffer_current(buffer); while (*p != '\0') { if (buffer_available(buffer, *p + 1)) buffer_skip(buffer, *p + 1); else return -1; p = buffer_current(buffer); } if (*p != '\0') return -1; if (!buffer_available(buffer, 11)) return -1; buffer_skip(buffer, 1); buffer_skip(buffer, 8); rdlength = buffer_read_u16(buffer); if (buffer_available(buffer, rdlength)) return 0; return -1; }
static uint16_t unmarshal_u16(struct buffer* b) { return buffer_read_u16(b); }
static int dns_name_from_wire(buffer_type* data, struct decompress_ctx *decompress, char* text_name) { char *q; u_int8_t *p, label_len; int length, off, saved = -1, i; if (data == NULL || text_name == NULL || decompress == NULL) return -1; p = buffer_current(data); q = text_name; length = 0; label_len = buffer_read_u8(data); if (label_len == 0) { *q = '.'; q++; } while (label_len != 0 && length < NAME_MAX_LENGTH) { if ((label_len & 0xc0) == 0xc0) { buffer_set_position(data, buffer_position(data) - 1); label_len = buffer_read_u16(data); off = label_len & ~0xc0; if (off >= buffer_limit(data) /*|| decompress->pos[off] != off*/) return -1; if (saved == -1) saved = buffer_position(data); buffer_set_position(data, off); label_len = buffer_read_u8(data); } else { if (!buffer_available(data, label_len)) return -1; for (i = 0; i < label_len; i++) { if (decompress->pos[i] == -1) decompress->pos[i] = buffer_position(data); } length += label_len; p = buffer_current(data); memcpy(q, p, label_len); buffer_skip(data, label_len); q += label_len; *q = '.'; q++; label_len = buffer_read_u8(data); } } if (label_len == 0) { *q = '\0'; if (saved > -1) buffer_set_position(data, saved); } else { log_msg("dns:domain not ends with \\0\n"); return -1; } return 0; }
/* 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; }