/** * Find TSIG RR. * */ static int query_find_tsig(query_type* q) { size_t saved_pos = 0; size_t rrcount = 0; size_t i = 0; ods_log_assert(q); ods_log_assert(q->tsig_rr); ods_log_assert(q->buffer); if (buffer_pkt_arcount(q->buffer) == 0) { q->tsig_rr->status = TSIG_NOT_PRESENT; return 1; } saved_pos = buffer_position(q->buffer); rrcount = buffer_pkt_qdcount(q->buffer) + buffer_pkt_ancount(q->buffer) + buffer_pkt_nscount(q->buffer); buffer_set_position(q->buffer, BUFFER_PKT_HEADER_SIZE); for (i=0; i < rrcount; i++) { if (!buffer_skip_rr(q->buffer, i < buffer_pkt_qdcount(q->buffer))) { buffer_set_position(q->buffer, saved_pos); return 0; } } rrcount = buffer_pkt_arcount(q->buffer); ods_log_assert(rrcount != 0); if (!tsig_rr_parse(q->tsig_rr, q->buffer)) { ods_log_debug("[%s] got bad tsig", query_str); return 0; } if (q->tsig_rr->status != TSIG_NOT_PRESENT) { --rrcount; } if (rrcount) { if (edns_rr_parse(q->edns_rr, q->buffer)) { --rrcount; } } if (rrcount && q->tsig_rr->status == TSIG_NOT_PRESENT) { /* see if tsig is after the edns record */ if (!tsig_rr_parse(q->tsig_rr, q->buffer)) { ods_log_debug("[%s] got bad tsig", query_str); return 0; } if (q->tsig_rr->status != TSIG_NOT_PRESENT) { --rrcount; } } if (rrcount > 0) { ods_log_debug("[%s] too many additional rrs", query_str); return 0; } buffer_set_position(q->buffer, saved_pos); return 1; }
/* * 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 packet_encode_rr(query_type *q, domain_type *owner, rr_type *rr) { size_t truncation_mark; uint16_t rdlength = 0; size_t rdlength_pos; uint16_t j; assert(q); assert(owner); assert(rr); /* * If the record does not in fit in the packet the packet size * will be restored to the mark. */ truncation_mark = buffer_position(q->packet); encode_dname(q, owner); buffer_write_u16(q->packet, rr->type); buffer_write_u16(q->packet, rr->klass); buffer_write_u32(q->packet, rr->ttl); /* Reserve space for rdlength. */ rdlength_pos = buffer_position(q->packet); buffer_skip(q->packet, sizeof(rdlength)); for (j = 0; j < rr->rdata_count; ++j) { switch (rdata_atom_wireformat_type(rr->type, j)) { case RDATA_WF_COMPRESSED_DNAME: encode_dname(q, rdata_atom_domain(rr->rdatas[j])); break; case RDATA_WF_UNCOMPRESSED_DNAME: { const dname_type *dname = domain_dname( rdata_atom_domain(rr->rdatas[j])); buffer_write(q->packet, dname_name(dname), dname->name_size); break; } default: buffer_write(q->packet, rdata_atom_data(rr->rdatas[j]), rdata_atom_size(rr->rdatas[j])); break; } } if (!query_overflow(q)) { rdlength = (buffer_position(q->packet) - rdlength_pos - sizeof(rdlength)); buffer_write_u16_at(q->packet, rdlength_pos, rdlength); return 1; } else { buffer_set_position(q->packet, truncation_mark); query_clear_dname_offsets(q, truncation_mark); assert(!query_overflow(q)); return 0; } }
/** * Process EDNS OPT RR. * */ static ldns_pkt_rcode query_process_edns(query_type* q) { if (!q || !q->edns_rr) { return LDNS_RCODE_SERVFAIL; } if (q->edns_rr->status == EDNS_ERROR) { /* The only error is VERSION not implemented */ return LDNS_RCODE_FORMERR; } if (q->edns_rr->status == EDNS_OK) { /* Only care about UDP size larger than normal... */ if (!q->tcp && q->edns_rr->maxlen > UDP_MAX_MESSAGE_LEN) { if (q->edns_rr->maxlen < EDNS_MAX_MESSAGE_LEN) { q->maxlen = q->edns_rr->maxlen; } else { q->maxlen = EDNS_MAX_MESSAGE_LEN; } } /* Strip the OPT resource record off... */ buffer_set_position(q->buffer, q->edns_rr->position); buffer_set_limit(q->buffer, q->edns_rr->position); buffer_pkt_set_arcount(q->buffer, buffer_pkt_arcount(q->buffer) - 1); } return LDNS_RCODE_NOERROR; }
int print_rdata(buffer_type *output, rrtype_descriptor_type *descriptor, rr_type *record) { size_t i; size_t saved_position = buffer_position(output); for (i = 0; i < record->rdata_count; ++i) { if (i == 0) { buffer_printf(output, "\t"); } else if (descriptor->type == TYPE_SOA && i == 2) { buffer_printf(output, " (\n\t\t"); } else { buffer_printf(output, " "); } if (!rdata_atom_to_string( output, (rdata_zoneformat_type) descriptor->zoneformat[i], record->rdatas[i], record)) { buffer_set_position(output, saved_position); return 0; } } if (descriptor->type == TYPE_SOA) { buffer_printf(output, " )"); } return 1; }
int packet_encode_rrset(query_type *query, domain_type *owner, rrset_type *rrset, int section) { uint16_t i; size_t truncation_mark; uint16_t added = 0; int all_added = 1; int truncate_rrset = (section == ANSWER_SECTION || section == AUTHORITY_SECTION); rrset_type *rrsig; assert(rrset->rr_count > 0); truncation_mark = buffer_position(query->packet); for (i = 0; i < rrset->rr_count; ++i) { if (packet_encode_rr(query, owner, &rrset->rrs[i])) { ++added; } else { all_added = 0; break; } } if (all_added && query->edns.dnssec_ok && zone_is_secure(rrset->zone) && rrset_rrtype(rrset) != TYPE_RRSIG && (rrsig = domain_find_rrset(owner, rrset->zone, TYPE_RRSIG))) { for (i = 0; i < rrsig->rr_count; ++i) { if (rr_rrsig_type_covered(&rrsig->rrs[i]) == rrset_rrtype(rrset)) { if (packet_encode_rr(query, owner, &rrsig->rrs[i])) { ++added; } else { all_added = 0; break; } } } } if (!all_added && truncate_rrset) { /* Truncate entire RRset and set truncate flag. */ buffer_set_position(query->packet, truncation_mark); query_clear_dname_offsets(query, truncation_mark); TC_SET(query->packet); added = 0; } return added; }
query_state_type rrl_slip(query_type* query) { /* discard half the packets, randomly */ if((random() & 0x1)) { /* set TC on the rest */ TC_SET(query->packet); ANCOUNT_SET(query->packet, 0); NSCOUNT_SET(query->packet, 0); ARCOUNT_SET(query->packet, 0); if(query->qname) /* header, type, class, qname */ buffer_set_position(query->packet, QHEADERSZ+4+query->qname->name_size); else buffer_set_position(query->packet, QHEADERSZ); return QUERY_PROCESSED; } return QUERY_DISCARDED; }
/** * SERVFAIL. * */ static query_state query_servfail(query_type* q) { if (!q) { return QUERY_DISCARDED; } ods_log_debug("[%s] servfail", query_str); buffer_set_position(q->buffer, 0); buffer_set_limit(q->buffer, BUFFER_PKT_HEADER_SIZE); buffer_pkt_set_qdcount(q->buffer, 0); return query_error(q, LDNS_RCODE_SERVFAIL); }
/** * Add RR to query. * */ int query_add_rr(query_type* q, ldns_rr* rr) { size_t i = 0; size_t tc_mark = 0; size_t rdlength_pos = 0; uint16_t rdlength = 0; ods_log_assert(q); ods_log_assert(q->buffer); ods_log_assert(rr); /* set truncation mark, in case rr does not fit */ tc_mark = buffer_position(q->buffer); /* owner type class ttl */ if (!buffer_available(q->buffer, ldns_rdf_size(ldns_rr_owner(rr)))) { goto query_add_rr_tc; } buffer_write_rdf(q->buffer, ldns_rr_owner(rr)); if (!buffer_available(q->buffer, sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t) + sizeof(rdlength))) { goto query_add_rr_tc; } buffer_write_u16(q->buffer, (uint16_t) ldns_rr_get_type(rr)); buffer_write_u16(q->buffer, (uint16_t) ldns_rr_get_class(rr)); buffer_write_u32(q->buffer, (uint32_t) ldns_rr_ttl(rr)); /* skip rdlength */ rdlength_pos = buffer_position(q->buffer); buffer_skip(q->buffer, sizeof(rdlength)); /* write rdata */ for (i=0; i < ldns_rr_rd_count(rr); i++) { if (!buffer_available(q->buffer, ldns_rdf_size(ldns_rr_rdf(rr, i)))) { goto query_add_rr_tc; } buffer_write_rdf(q->buffer, ldns_rr_rdf(rr, i)); } if (!query_overflow(q)) { /* write rdlength */ rdlength = buffer_position(q->buffer) - rdlength_pos - sizeof(rdlength); buffer_write_u16_at(q->buffer, rdlength_pos, rdlength); /* position updated by buffer_write() */ return 1; } query_add_rr_tc: buffer_set_position(q->buffer, tc_mark); ods_log_assert(!query_overflow(q)); return 0; }
/** * Error. * */ static query_state query_error(query_type* q, ldns_pkt_rcode rcode) { size_t limit = 0; if (!q) { return QUERY_DISCARDED; } limit = buffer_limit(q->buffer); buffer_clear(q->buffer); buffer_pkt_set_qr(q->buffer); buffer_pkt_set_rcode(q->buffer, rcode); buffer_pkt_set_ancount(q->buffer, 0); buffer_pkt_set_nscount(q->buffer, 0); buffer_pkt_set_arcount(q->buffer, 0); buffer_set_position(q->buffer, limit); return QUERY_PROCESSED; }
/** * Prepare response. * */ void query_prepare(query_type* q) { uint16_t limit = 0; uint16_t flags = 0; ods_log_assert(q); ods_log_assert(q->buffer); limit = buffer_limit(q->buffer); flags = buffer_pkt_flags(q->buffer); flags &= 0x0100U; /* preserve the rd flag */ flags |= 0x8000U; /* set the qr flag */ buffer_pkt_set_flags(q->buffer, flags); buffer_clear(q->buffer); buffer_set_position(q->buffer, limit); buffer_set_limit(q->buffer, buffer_capacity(q->buffer)); q->reserved_space = edns_rr_reserved_space(q->edns_rr); q->reserved_space += tsig_rr_reserved_space(q->tsig_rr); return; }
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; }
static int rdata_nsec_to_string(buffer_type *output, rdata_atom_type rdata, rr_type* ATTR_UNUSED(rr)) { size_t saved_position = buffer_position(output); buffer_type packet; int insert_space = 0; buffer_create_from( &packet, rdata_atom_data(rdata), rdata_atom_size(rdata)); while (buffer_available(&packet, 2)) { uint8_t window = buffer_read_u8(&packet); uint8_t bitmap_size = buffer_read_u8(&packet); uint8_t *bitmap = buffer_current(&packet); int i; if (!buffer_available(&packet, bitmap_size)) { buffer_set_position(output, saved_position); return 0; } for (i = 0; i < bitmap_size * 8; ++i) { if (get_bit(bitmap, i)) { buffer_printf(output, "%s%s", insert_space ? " " : "", rrtype_to_string( window * 256 + i)); insert_space = 1; } } buffer_skip(&packet, bitmap_size); } return 1; }
/* 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 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; }
int dname_make_wire_from_packet(uint8_t *buf, buffer_type *packet, int allow_pointers) { int done = 0; uint8_t visited[(MAX_PACKET_SIZE+7)/8]; size_t dname_length = 0; const uint8_t *label; ssize_t mark = -1; memset(visited, 0, (buffer_limit(packet)+7)/8); while (!done) { if (!buffer_available(packet, 1)) { /* error("dname out of bounds"); */ return 0; } if (get_bit(visited, buffer_position(packet))) { /* error("dname loops"); */ return 0; } set_bit(visited, buffer_position(packet)); label = buffer_current(packet); if (label_is_pointer(label)) { size_t pointer; if (!allow_pointers) { return 0; } if (!buffer_available(packet, 2)) { /* error("dname pointer out of bounds"); */ return 0; } pointer = label_pointer_location(label); if (pointer >= buffer_limit(packet)) { /* error("dname pointer points outside packet"); */ return 0; } buffer_skip(packet, 2); if (mark == -1) { mark = buffer_position(packet); } buffer_set_position(packet, pointer); } else if (label_is_normal(label)) { size_t length = label_length(label) + 1; done = label_is_root(label); if (!buffer_available(packet, length)) { /* error("dname label out of bounds"); */ return 0; } if (dname_length + length >= MAXDOMAINLEN+1) { /* error("dname too large"); */ return 0; } buffer_read(packet, buf + dname_length, length); dname_length += length; } else { /* error("bad label type"); */ return 0; } } if (mark != -1) { buffer_set_position(packet, mark); } return dname_length; }
/** * NOTIFY. * */ static query_state query_process_notify(query_type* q, ldns_rr_type qtype, void* engine) { engine_type* e = (engine_type*) engine; dnsin_type* dnsin = NULL; uint16_t count = 0; uint16_t rrcount = 0; uint32_t serial = 0; size_t pos = 0; char address[128]; if (!e || !q || !q->zone) { return QUERY_DISCARDED; } ods_log_assert(e->dnshandler); ods_log_assert(q->zone->name); ods_log_debug("[%s] incoming notify for zone %s", query_str, q->zone->name); if (buffer_pkt_rcode(q->buffer) != LDNS_RCODE_NOERROR || buffer_pkt_qr(q->buffer) || !buffer_pkt_aa(q->buffer) || buffer_pkt_tc(q->buffer) || buffer_pkt_rd(q->buffer) || buffer_pkt_ra(q->buffer) || buffer_pkt_ad(q->buffer) || buffer_pkt_cd(q->buffer) || buffer_pkt_qdcount(q->buffer) != 1 || buffer_pkt_ancount(q->buffer) > 1 || qtype != LDNS_RR_TYPE_SOA) { return query_formerr(q); } if (!q->zone->adinbound || q->zone->adinbound->type != ADAPTER_DNS) { ods_log_error("[%s] zone %s is not configured to have input dns " "adapter", query_str, q->zone->name); return query_notauth(q); } ods_log_assert(q->zone->adinbound->config); dnsin = (dnsin_type*) q->zone->adinbound->config; if (!acl_find(dnsin->allow_notify, &q->addr, q->tsig_rr)) { if (addr2ip(q->addr, address, sizeof(address))) { ods_log_info("[%s] unauthorized notify for zone %s from client %s: " "no acl matches", query_str, q->zone->name, address); } else { ods_log_info("[%s] unauthorized notify for zone %s from unknown " "client: no acl matches", query_str, q->zone->name); } return query_notauth(q); } ods_log_assert(q->zone->xfrd); /* skip header and question section */ buffer_skip(q->buffer, BUFFER_PKT_HEADER_SIZE); count = buffer_pkt_qdcount(q->buffer); for (rrcount = 0; rrcount < count; rrcount++) { if (!buffer_skip_rr(q->buffer, 1)) { ods_log_error("[%s] dropped packet: zone %s received bad notify " "(bad question section)", query_str, q->zone->name); return QUERY_DISCARDED; } } pos = buffer_position(q->buffer); /* examine answer section */ count = buffer_pkt_ancount(q->buffer); if (count) { if (!buffer_skip_dname(q->buffer) || !query_parse_soa(q->buffer, &serial)) { ods_log_error("[%s] dropped packet: zone %s received bad notify " "(bad soa in answer section)", query_str, q->zone->name); return QUERY_DISCARDED; } lock_basic_lock(&q->zone->xfrd->serial_lock); q->zone->xfrd->serial_notify = serial; q->zone->xfrd->serial_notify_acquired = time_now(); if (!util_serial_gt(q->zone->xfrd->serial_notify, q->zone->xfrd->serial_disk)) { ods_log_debug("[%s] ignore notify: already got zone %s serial " "%u on disk", query_str, q->zone->name, q->zone->xfrd->serial_notify); lock_basic_unlock(&q->zone->xfrd->serial_lock); goto send_notify_ok; } lock_basic_unlock(&q->zone->xfrd->serial_lock); } else { lock_basic_lock(&q->zone->xfrd->serial_lock); q->zone->xfrd->serial_notify = 0; q->zone->xfrd->serial_notify_acquired = 0; lock_basic_unlock(&q->zone->xfrd->serial_lock); } /* forward notify to xfrd */ xfrd_set_timer_now(q->zone->xfrd); dnshandler_fwd_notify(e->dnshandler, buffer_begin(q->buffer), buffer_remaining(q->buffer)); send_notify_ok: /* send notify ok */ buffer_pkt_set_qr(q->buffer); buffer_pkt_set_aa(q->buffer); buffer_pkt_set_ancount(q->buffer, 0); buffer_clear(q->buffer); /* lim = pos, pos = 0; */ buffer_set_position(q->buffer, pos); buffer_set_limit(q->buffer, buffer_capacity(q->buffer)); q->reserved_space = edns_rr_reserved_space(q->edns_rr); q->reserved_space += tsig_rr_reserved_space(q->tsig_rr); return QUERY_PROCESSED; }