static void dnscore_arch_checkup() { /* Test the archi=tecture */ #pragma message("Don't worry about the possible warnings here") ARCH_CHECK_SIZE(__SIZEOF_POINTER__, sizeof (void*)); ARCH_CHECK_SIZE(sizeof (u8), 1); ARCH_CHECK_SIZE(sizeof (s8), 1); ARCH_CHECK_SIZE(sizeof (u16), 2); ARCH_CHECK_SIZE(sizeof (s16), 2); ARCH_CHECK_SIZE(sizeof (u32), 4); ARCH_CHECK_SIZE(sizeof (s32), 4); ARCH_CHECK_SIZE(sizeof (u64), 8); ARCH_CHECK_SIZE(sizeof (s64), 8); ARCH_CHECK_SIZE(sizeof (intptr), sizeof (void*)); ARCH_CHECK_SIGNED(s8); ARCH_CHECK_SIGNED(s16); ARCH_CHECK_SIGNED(s32); ARCH_CHECK_SIGNED(s64); ARCH_CHECK_UNSIGNED(u8); ARCH_CHECK_UNSIGNED(u16); ARCH_CHECK_UNSIGNED(u32); ARCH_CHECK_UNSIGNED(u64); ARCH_CHECK_UNSIGNED(intptr); message_data* msg = NULL; intptr msg_diffs = (intptr)(msg->buffer - msg->buffer_tcp_len); if((msg->buffer - msg->buffer_tcp_len) != 2) { printf("Structure aligment is wrong. Expected 2 but got %i. (see message_data)\n", (int)msg_diffs); DIE(ERROR); } #pragma message("You can resume worrying now ...") #if WORDS_BIGENDIAN==1 u8 endian[4] = {1, 2, 3, 4}; /* BIG */ char* endian_name = "BIG"; #else u8 endian[4] = {4, 3, 2, 1}; /* LITTLE */ char* endian_name = "LITTLE"; #endif u32 endian_match = GET_U32_AT(endian[0]); if(endian_match != 0x01020304) { fprintf(stderr, "Endianness is wrong. Compiled for %s\n", endian_name); puts(ARCH_RECOMPILE_WARNING); fflush(NULL); DIE(ERROR); } }
static ya_result journal_ix_get_ixfr_stream_at_serial(journal *jh, u32 serial_from, input_stream *out_input_stream, dns_resource_record *last_soa_rr) { journal_ix *jix = (journal_ix*)jh; ya_result return_value = SUCCESS; journal_ix_readlock(jix); /* * check that serial_from in in the journal range * set the file descriptor to the position * create a stream that'll stop at the current end of the stream */ if(serial_lt(serial_from, jix->first_serial) || serial_ge(serial_from, jix->last_serial) || ((jix->first_serial == 0) && (jix->last_serial == 0))) { /* out of known range */ journal_ix_readunlock(jix); if(serial_from == jix->last_serial) { return SUCCESS; } else { return ZDB_JOURNAL_SERIAL_OUT_OF_KNOWN_RANGE; } } /* * On success, dup() returns a new file descriptor that has the following in common with the original: * * _ Same open file (or pipe) * _ Same file pointer (both file descriptors share one file pointer) <= THIS is a problem * _ Same access mode (read, write, or read/write) * * So this is wrong: * * cloned_fd = dup(jix->fd); */ int cloned_fd; while((cloned_fd = open_ex(jix->journal_name, O_RDONLY)) < 0) { int err = errno; if(err == EINTR) { continue; } return_value = MAKE_ERRNO_ERROR(err); #ifdef DEBUG log_debug("journal: ix: unable to clone the file descriptor: %r", return_value); #endif journal_ix_readunlock(jix); return return_value; } /* * given that I use a clone of the fd and * given that only appends are done in the file and * given that the limit of the file has already been processed (should be at this point) * * THEN * * there is no point keeping the lock for reading (on unix systems) */ struct stat journal_stat; s64 last_page_offset = jix->last_page_offset; if(fstat(cloned_fd, &journal_stat) < 0) { return_value = ERRNO_ERROR; log_err("journal: ix: unable to get journal file status", return_value); close_ex(cloned_fd); return return_value; } s64 file_size = journal_stat.st_size; #if DEBUG_JOURNAL log_debug("journal: ix: the last page starts at position %lld", last_page_offset); #endif journal_ix_readunlock(jix); jix = NULL; input_stream fis; fd_input_stream_attach(&fis, cloned_fd); if(last_soa_rr != NULL) { /* seek and store the last SOA print*/ last_soa_rr->tctr.qtype = 0; // clear type if(lseek(cloned_fd, last_page_offset, SEEK_SET) >= 0) { /* deleted SOA */ if((return_value = dns_resource_record_read(last_soa_rr, &fis)) > 0 ) // Not FAIL nor EOF { if(last_soa_rr->tctr.qtype == TYPE_SOA) { /* DEL records */ last_soa_rr->tctr.qtype = 0; // clear type /* scan until added SOA found */ while((return_value = dns_resource_record_read(last_soa_rr, &fis)) > 0 ) // Not FAIL nor EOF { if(last_soa_rr->tctr.qtype == TYPE_SOA) { break; } } } } // if the SOA has not been found, it's an error (EOF has been reached is covered by this) if(ISOK(return_value)) { if(last_soa_rr->tctr.qtype != TYPE_SOA) { return_value = ZDB_JOURNAL_SOA_RECORD_EXPECTED; } } } else { return_value = ERRNO_ERROR; } if(FAIL(return_value)) { input_stream_close(&fis); return return_value; } } /* * this format has no indexing so we scan for a page that STARTS with a DELETE of the SOA with serial = serial_from */ if(lseek(cloned_fd, 0, SEEK_SET) != 0) /* the resulting offset MUST be zero */ { return_value = ERRNO_ERROR; if(ISOK(return_value)) { return_value = ERROR; } input_stream_close(&fis); return return_value; } input_stream bis; dns_resource_record rr; dns_resource_record_init(&rr); buffer_input_stream_init(&fis, &bis, 512); s64 offset = 0; /* skip until the right serial is found */ u32 soa_count = 0; #ifdef DEBUG_JOURNAL u32 rr_count = 0; #endif for(;;) { if( (return_value = dns_resource_record_read(&rr, &bis)) <= 0 ) // FAIL or nothing to { return_value = ZDB_JOURNAL_ERROR_READING_JOURNAL; /* is the journal file broken ? */ break; } #ifdef DEBUG_JOURNAL rr_count++; #endif u32 record_size = return_value; if(rr.tctr.qtype == TYPE_SOA) { // ((0+1)&1) != 0 => Y N Y N if((++soa_count & 1) != 0) // 1 2 3 4 { u8 *p = rr.rdata; if(FAIL(return_value = dnsname_len(p))) { break; } p += return_value; if(FAIL(return_value = dnsname_len(p))) { break; } p += return_value; u32 serial = ntohl(GET_U32_AT(*p)); if(serial_ge(serial, serial_from)) { if(serial == serial_from) { /* setup the serial to be from 'offset' up to the current length of the stream */ return_value = SUCCESS; } else { /* the serial does not exist in the range */ return_value = ZDB_JOURNAL_SERIAL_OUT_OF_KNOWN_RANGE; } break; } } } offset += record_size; } #if DEBUG_JOURNAL log_debug("journal: ix: serial %08x (%d) is at offset %lld. %d records parsed", serial_from, serial_from, offset, rr_count); #endif dns_resource_record_clear(&rr); /* * detach the file descriptor from the file stream in the buffer stream * I do it like this because the streams are not needed anymore but the * file descriptor still is (if no error occurred) */ fd_input_stream_detach(buffer_input_stream_get_filtered(&bis)); input_stream_close(&bis); if(ISOK(return_value)) { // offset is the start of the page we are looking for if(lseek(cloned_fd, offset, SEEK_SET) >= 0) { fd_input_stream_attach(&fis, cloned_fd); limited_input_stream_init(&fis, out_input_stream, file_size - offset); } else { return_value = ERRNO_ERROR; close_ex(cloned_fd); } } else { close_ex(cloned_fd); } return return_value; }
ya_result xfr_input_stream_init(input_stream* filtering_stream, const u8 *origin, input_stream *xfr_source_stream, message_data *message, u32 current_serial, xfr_copy_flags flags) { yassert(filtering_stream != NULL && origin != NULL && xfr_source_stream != NULL && message != NULL); input_stream *is = xfr_source_stream; packet_unpack_reader_data reader; u8 *buffer; u8 *record; u8 *ptr; #if DNSCORE_HAS_TSIG_SUPPORT const tsig_item *tsig; #endif ya_result record_len; ya_result return_value; u32 origin_len; u32 last_serial = 0; u16 tcplen; u16 qtype; u16 qclass; u16 old_mac_size; bool last_message_had_tsig; bool need_cleanup_tsig = FALSE; #if DNSCORE_HAS_TSIG_SUPPORT u8 old_mac[64]; #endif /* * ensure the stream will be unusable if the initialisation fails */ input_stream_set_void(filtering_stream); /* * Start by reading the first packet, and determine if it's an AXFR or an IXFR (for the name) * note: it's read and converted to the host endianness */ if(!is_fd_input_stream(is)) { // expected file input stream return INVALID_ARGUMENT_ERROR; } //buffer_input_stream_init(is, is, 4096); /* TCP length */ if(FAIL(return_value = input_stream_read_nu16(is, &tcplen))) { return return_value; } if(return_value != 2) { return UNEXPECTED_EOF; } /* if the length is not enough, return the most appropriate error code */ origin_len = dnsname_len(origin); if(tcplen < DNS_HEADER_LENGTH + origin_len + 4) { return_value = UNEXPECTED_EOF; if(tcplen >= DNS_HEADER_LENGTH) { if(ISOK(return_value = input_stream_read_fully(is, message->pool_buffer, DNS_HEADER_LENGTH))) { return_value = MAKE_DNSMSG_ERROR(MESSAGE_RCODE(message->pool_buffer)); } } return return_value; } /* read the whole message */ buffer = &message->buffer[0]; record = &message->pool_buffer[0]; assert(sizeof(message->pool_buffer) >= 255 + 10 + 65535); if(FAIL(return_value = input_stream_read_fully(is, buffer, tcplen))) { return return_value; } #if DEBUG_XFR_INPUT_STREAM log_memdump(g_system_logger, MSG_DEBUG1, &message->buffer[0], tcplen, 32); #endif message->received = return_value; /* check the message makes sense */ const u64 *h64 = (u64*)buffer; u64 m64 = AXFR_MESSAGE_HEADER_MASK; u64 r64 = AXFR_MESSAGE_HEADER_RESULT; if(((*h64&m64) != r64) || (MESSAGE_NS(message->buffer) != 0)) { u8 code = MESSAGE_RCODE(message->buffer); if(code != 0) { return_value = MAKE_DNSMSG_ERROR(code); } else { return_value = UNPROCESSABLE_MESSAGE; } return return_value; } //m64 = AXFR_NEXT_MESSAGE_HEADER_MASK; //r64 = AXFR_NEXT_MESSAGE_HEADER_RESULT; packet_reader_init(&reader, buffer, tcplen); reader.offset = DNS_HEADER_LENGTH; packet_reader_read_fqdn(&reader, record, RDATA_MAX_LENGTH + 1); if(!dnsname_equals(record, origin)) { return INVALID_PROTOCOL; } if(FAIL(return_value = packet_reader_read_u16(&reader, &qtype))) { return return_value; } if(return_value != 2) { return UNEXPECTED_EOF; } /* * check that we are allowed to process this particular kind of transfer * note : this does not determine what is REALLY begin transferred */ switch(qtype) { case TYPE_AXFR: { if((flags & XFR_ALLOW_AXFR) == 0) { return INVALID_PROTOCOL; } break; } case TYPE_IXFR: { if((flags & XFR_ALLOW_IXFR) == 0) { return INVALID_PROTOCOL; } break; } default: { return INVALID_PROTOCOL; } } if(FAIL(return_value = packet_reader_read_u16(&reader, &qclass))) { return return_value; } if(qclass != CLASS_IN) { /** wrong answer */ return INVALID_PROTOCOL; } /* check for TSIG and verify */ u16 ancount = ntohs(MESSAGE_AN(buffer)); #if DNSCORE_HAS_TSIG_SUPPORT if((last_message_had_tsig = ((tsig = message->tsig.tsig) != NULL))) { /* verify the TSIG * * AR > 0 * skip ALL the records until the last AR * it MUST be a TSIG * It's the first TSIG answering to our query * verify it * */ message->tsig.tsig = NULL; old_mac_size = message->tsig.mac_size; memcpy(old_mac, message->tsig.mac, old_mac_size); if(FAIL(return_value = tsig_message_extract(message))) { log_debug("xfr_input_stream: error extracting the signature"); return return_value; } if(return_value == 0) { log_debug("xfr_input_stream: no signature when one was requested"); return TSIG_BADSIG; /* no signature, when one was requested, is a bad signature */ } if(message->tsig.tsig != tsig) { /* This is not the one we started with */ log_debug("xfr_input_stream: signature key does not match"); return TSIG_BADSIG; } /// check that the tsig in the message matches the one that was sent if(FAIL(return_value = tsig_verify_tcp_first_message(message, old_mac, old_mac_size))) { return return_value; } reader.packet_size = message->received; need_cleanup_tsig = TRUE; } #endif log_debug("xfr_input_stream: expecting %5d answer records", ancount); /* * read the SOA (it MUST be an SOA) */ if(FAIL(record_len = packet_reader_read_record(&reader, record, RDATA_MAX_LENGTH + 1))) { return record_len; } if(!dnsname_equals(record, origin)) { return INVALID_PROTOCOL; } ptr = &record[origin_len]; if(GET_U16_AT(*ptr) != TYPE_SOA) { return INVALID_PROTOCOL; } ptr += 8; /* type class ttl */ u16 rdata_size = ntohs(GET_U16_AT(*ptr)); if(rdata_size < 22) { return INVALID_PROTOCOL; } rdata_size -= 16; ptr += 2; /* rdata size */ s32 len = dnsname_len(ptr); if(len >= rdata_size) { return INVALID_PROTOCOL; } rdata_size -= len; ptr += len; len = dnsname_len(ptr); if(len >= rdata_size) { return INVALID_PROTOCOL; } rdata_size -= len; if(rdata_size != 4) { return INVALID_PROTOCOL; } ptr += len; // if the serial of the SOA is the same one as we know, then there is no // need to download the zone last_serial = ntohl(GET_U32_AT(*ptr)); if(last_serial == current_serial) { //args->out_loaded_serial = args->current_serial; return ZONE_ALREADY_UP_TO_DATE; } xfr_input_stream_data *data; ZALLOC_OR_DIE(xfr_input_stream_data*, data, xfr_input_stream_data, XFRISDTA_TAG); ZEROMEMORY(data, sizeof(xfr_input_stream_data)); /* * We have got the first SOA * Next time we find this SOA (second next time for IXFR) the stream, it will be the end of the stream */ /* * The stream can be AXFR or IXFR. * The only way to know this is to look at the records, maybe on many packets. * If there are two SOA (different serial numbers) for the start, then it's an IXFR, else it's an AXFR. * * OPEN A PIPE STREAM "XFRs" * * Save the first SOA */ MALLOC_OR_DIE(u8*, data->first_soa_record, record_len, XFRISSOA_TAG); MEMCOPY(data->first_soa_record, record, record_len); data->first_soa_record_size = record_len; filtering_stream->vtbl = &xfr_input_stream_vtbl; filtering_stream->data = data; pipe_stream_init(&data->pipe_stream_output, &data->pipe_stream_input, 65536); MEMCOPY(&data->reader, &reader, sizeof(packet_unpack_reader_data)); data->origin = origin; data->message = message; data->ancount = ancount - 1; data->record_index++; data->last_serial = last_serial; data->xfr_mode = TYPE_ANY; data->ixfr_mark = FALSE; data->last_message_had_tsig = last_message_had_tsig; data->source_stream = *is; data->need_cleanup_tsig = need_cleanup_tsig; /* * Then we read all records for all packets * If we find an SOA ... * AXFR: it has to be the last serial and it is the end of the stream. * IXFR: if it's not the last serial it has to go from step to step * AND once we have reached the "last serial" once, the next hit is the end of the stream. */ data->eos = FALSE; /* * In order to know what the type is, read the first packet. */ return_value = xfr_input_stream_read_packet(data); if(FAIL(return_value)) { xfr_input_stream_close(filtering_stream); } return return_value; }
static ya_result xfr_input_stream_read_packet(xfr_input_stream_data *data) { message_data *message = data->message; packet_unpack_reader_data *reader = &data->reader; u8 *record = &message->pool_buffer[0]; u32 record_len; ya_result return_value = SUCCESS; #if DEBUG_XFR_INPUT_STREAM log_debug("xfr_input_stream_read_packet(%p) ancount=%hd record_index=%u", data, data->ancount, data->record_index); #endif while((data->ancount > 0) && (pipe_stream_write_available(&data->pipe_stream_output) > 2048 )) { --data->ancount; if(FAIL(record_len = packet_reader_read_record(reader, record, RDATA_MAX_LENGTH + 1))) { if(record_len != UNSUPPORTED_TYPE) { data->eos = TRUE; return_value = record_len; break; } log_err("xfr_input_stream: skipped unsupported record #%d %{recordwire}", data->record_index, record); data->record_index++; continue; } #if DEBUG_XFR_INPUT_STREAM log_debug("xfr_input_stream: #%u %{recordwire}", data->record_index, record); #endif u8 *ptr = record + dnsname_len(record); u16 rtype = GET_U16_AT(*ptr); switch(rtype) { case TYPE_SOA: { /* handle SOA case */ if(!dnsname_equals(record, data->origin)) { data->eos = TRUE; return_value = ERROR; // OWNER OF SOA RECORD SHOULD BE ORIGIN (protocol error) return return_value; } ptr += 10; /* type class ttl rdata_size */ ptr += dnsname_len(ptr); ptr += dnsname_len(ptr); u32 soa_serial = ntohl(GET_U32_AT(*ptr)); if(data->xfr_mode == TYPE_ANY) { if(data->record_index == 1) { /* * This is an IXFR, the first record is not sent up */ data->xfr_mode = TYPE_IXFR; } else { output_stream_write(&data->pipe_stream_output, data->first_soa_record, data->first_soa_record_size); data->xfr_mode = TYPE_AXFR; } } if(soa_serial == data->last_serial) { // the SOA serial has the same value as the last record we expect // if it's an AXFR or this is the second time it happens on an IXFR, then it's then end of the stream if(data->xfr_mode == TYPE_AXFR || ((data->xfr_mode == TYPE_IXFR) && data->ixfr_mark)) { return_value = SUCCESS; /* * The last record of an AXFR must be written, * the last record of an IXFR must not. */ if(data->xfr_mode == TYPE_AXFR) { #if DEBUG_XFR_INPUT_STREAM log_debug("xfr_input_stream: #%u %{recordwire} ; (AXFR END)", data->record_index, record); #endif return_value = output_stream_write(&data->pipe_stream_output, record, record_len); } #if DEBUG_XFR_INPUT_STREAM else { log_debug("xfr_input_stream: #%u %{recordwire} ; (IXFR END)", data->record_index, record); } #endif // done data->eos = TRUE; return return_value; // reached the end } // IXFR needs to find the mark twice #if DEBUG_XFR_INPUT_STREAM log_debug("xfr_input_stream: #%u %{recordwire} ; (IXFR LAST)", data->record_index, record); #endif data->ixfr_mark = TRUE; } break; } case TYPE_IXFR: case TYPE_AXFR: case TYPE_OPT: case TYPE_ANY: return INVALID_PROTOCOL; default: if(data->record_index == 1) { // special case to detect an AXFR returned by an IXFR query if(data->xfr_mode == TYPE_ANY) { data->xfr_mode = TYPE_AXFR; if(FAIL(return_value = output_stream_write(&data->pipe_stream_output, data->first_soa_record, data->first_soa_record_size))) { return return_value; } } else { return_value = ERROR; return return_value; // invalid status } } break; } #if DEBUG_XFR_INPUT_STREAM log_debug("xfr_input_stream: >%u %{recordwire}", data->record_index, record); #endif if(FAIL(return_value = output_stream_write(&data->pipe_stream_output, record, record_len))) { data->eos = TRUE; break; } if(return_value != record_len) { return UNEXPECTED_EOF; } data->record_index++; } return return_value; }
static void message_viewer_wire_section_record(message_viewer *mv, u8 *record_wire, u8 view_mode_with) { if(!(mv->view_mode_with & view_mode_with)) { return; } /* * there is no padding support for formats on complex types (padding is ignored) * doing it would be relatively expensive for it's best doing it manually when needed (afaik: only here) */ counter_output_stream_data counters; output_stream cos; output_stream *os_ = mv->os; counter_output_stream_init(os_, &cos, &counters); output_stream *os = &cos; /* final output stream */ /* ------------------------------------------------------------ */ /* 1. get the needed parameters: FQDN, TYPE, CLASS, TTL, RDATA size */ u8 *rname = record_wire; u8 *rdata = rname + dnsname_len(rname); u16 rtype = GET_U16_AT(rdata[0]); u16 rclass = GET_U16_AT(rdata[2]); u32 rttl = ntohl(GET_U32_AT(rdata[4])); u16 rdata_size = ntohs(GET_U16_AT(rdata[8])); /** @todo 20150716 gve -- test that rdata_size matches the record size */ /* move pointer to RDATA information in the record_wire */ rdata += 10; /* 2. write the retrieved info into the stream: * FQDN TTL CLASS TYPE RDATA * * e.g. * somedomain.eu. 86400 IN NS ns1.somedomain.eu. */ /* A. write FQDN + alignment for next item */ u64 next = counters.write_count + 24; osformat(os, "%{dnsname}", rname); while(counters.write_count < next) { output_stream_write_u8(os, (u8)' '); } output_stream_write_u8(os, (u8)' '); /* B. write TTL + alignment for next item */ osformat(os, "%7d", rttl); output_stream_write_u8(os, (u8)' '); /* C. write CLASS + alignment for next item */ next = counters.write_count + 7; osformat(os, "%7{dnsclass}", &rclass); while(counters.write_count < next) { output_stream_write_u8(os, (u8) ' '); } output_stream_write_u8(os, (u8)' '); /* D. write TYPE + alignment for next item */ next = counters.write_count + 7; osformat(os, "%7{dnstype} ", &rtype); while(counters.write_count < next) { output_stream_write_u8(os, (u8)' '); } output_stream_write_u8(os, (u8)' '); /* E. write RDATA */ osprint_rdata(os, rtype, rdata, rdata_size); osprintln(os, ""); flushout(); }