Esempio n. 1
0
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;
}
Esempio n. 2
0
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();
}
Esempio n. 3
0
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;
}
Esempio n. 4
0
ya_result
zdb_zone_update_ixfr(zdb* db, input_stream* is)
{
    u8 rname[MAX_DOMAIN_LENGTH];
    u16 rtype;
    u16 rclass;
    u32 rttl;
    u16 rdata_size;
    u8* rdata;

    zdb_packed_ttlrdata* soa_ttlrdata;
    zdb_packed_ttlrdata* tmp_ttlrdata;
    zdb_packed_ttlrdata* ttlrdata;

    dnsname_vector name;
    dnsname_vector entry_name;

    ya_result err;

    /* Get the first SOA */

    if(FAIL(err = input_stream_read_rr_header(is, rname, sizeof (rname), &rtype, &rclass, &rttl, &rdata_size)))
    {
        return err;
    }

    if(rtype != TYPE_SOA)
    {
        return ZDB_ERROR_GENERAL;
    }

    DEBUG_RESET_dnsname(name);
    dnsname_to_dnsname_vector(rname, &name);

    ZDB_RECORD_ZALLOC_EMPTY(soa_ttlrdata, rttl, rdata_size);
    if(FAIL(err = input_stream_read_fully(is, ZDB_PACKEDRECORD_PTR_RDATAPTR(soa_ttlrdata), rdata_size)))
    {
        return err;
    }

#ifndef NDEBUG
#if DUMP_ICMTL_UPDATE != 0
    if(err != 0)
    {
        format("zdb_zone_update_ixfr H: %{dnsname} %d %{dnsclass} %{dnstype} ", rname, rttl, &rclass, &rtype);
        print_rdata(rtype, ZDB_PACKEDRECORD_PTR_RDATAPTR(soa_ttlrdata), rdata_size);
        println("");
    }
#endif
#endif

    u32 serial_first;

    if(FAIL(err = rr_soa_get_serial(soa_ttlrdata->rdata_start, soa_ttlrdata->rdata_size, &serial_first)))
    {
        return err;
    }

    zdb_zone_label* zone_label = zdb_zone_label_find(db, &name, rclass);

    if((zone_label == NULL) || (zone_label->zone == NULL))
    {
        /* Not loaded */

        return ZDB_ERROR_GENERAL;
    }

    zdb_zone* zone = zone_label->zone;

    u32 serial_current;

    if(FAIL(err = zdb_zone_getserial(zone, &serial_current)))
    {
        return err;
    }

#if ZDB_NSEC3_SUPPORT != 0
    nsec3_load_context nsec3_context;
    nsec3_load_init(&nsec3_context, zone);
#endif

    MALLOC_OR_DIE(zdb_packed_ttlrdata*, tmp_ttlrdata, sizeof (zdb_ttlrdata) + RDATA_MAX_LENGTH, ZDB_RDATABUF_TAG);

    /* We do not need tmp_ttlrdata and rdata at the same time, let's spare memory */

    rdata = ZDB_PACKEDRECORD_PTR_RDATAPTR(tmp_ttlrdata);

    /* Read the next SOA (sub or end) */

    if(FAIL(err = input_stream_read_rr_header(is, rname, sizeof (rname), &rtype, &rclass, &rttl, &rdata_size)))
    {
        return err;
    }

    if(rtype != TYPE_SOA)
    {
        return ZDB_ERROR_GENERAL;
    }

    for(;;)
    {
        if(FAIL(err = input_stream_read_fully(is, rdata, rdata_size)))
        {
            break;
        }

#ifndef NDEBUG
#if DUMP_ICMTL_UPDATE != 0
        if(err != 0)
        {
            format("zdb_zone_update_ixfr F: %{dnsname} %d %{dnsclass} %{dnstype} ", rname, rttl, &rclass, &rtype);
            print_rdata(rtype, rdata, rdata_size);
            println("");
        }
#endif
#endif

        /* The SOA serial is supposed to match our current one or the first one
         *
         * If it's the first one, then the task is done
         *
         * If it's the current one, we are moving forward
         *
         * If it's something else, there is an error
         *
         */

        u32 serial_from;

        if(FAIL(err = rr_soa_get_serial(rdata, rdata_size, &serial_from)))
        {
            break;
        }

        /* Check the serial */

        if(serial_from != serial_current)
        {
            if(serial_from == serial_first)
            {
                /* IXFR done */

                err = SUCCESS;

                break;
            }

            /* Serial sequence is not right */

            err = ZDB_ERROR_GENERAL;

            break;
        }

        tmp_ttlrdata->ttl = rttl;
        tmp_ttlrdata->rdata_size = rdata_size;
        zdb_zone_record_delete(zone, NULL, -1, TYPE_SOA, tmp_ttlrdata);

        for(;;)
        {
            /* Load the next record without the data (sub) */

            if(FAIL(err = input_stream_read_rr_header(is, rname, sizeof (rname), &rtype, &rclass, &rttl, &rdata_size)))
            {
                break;
            }

            /* If we got an SOA, it's the one that starts the "add" sequence */

            if(rtype == TYPE_SOA)
            {
                break;
            }

            /* Load the data */

            if(FAIL(err = input_stream_read_fully(is, rdata, rdata_size)))
            {
                break;
            }

#ifndef NDEBUG
#if DUMP_ICMTL_UPDATE != 0
            if(err != 0)
            {
                format("zdb_zone_update_ixfr R: %{dnsname} %d %{dnsclass} %{dnstype} ", rname, rttl, &rclass, &rtype);
                print_rdata(rtype, rdata, rdata_size);
                println("");
            }
#endif
#endif
            tmp_ttlrdata->ttl = rttl;
            tmp_ttlrdata->rdata_size = rdata_size;

            /* The vector is not always required, but it's so much easier to
             * read putting it here
             */

            DEBUG_RESET_dnsname(entry_name);
            dnsname_to_dnsname_vector(rname, &entry_name);

#if ZDB_NSEC3_SUPPORT != 0

            if(rtype == TYPE_NSEC3PARAM)
            {
                /* Remove it from the zone */

                zdb_zone_record_delete(zone, name.labels, (entry_name.size - name.size) - 1, rtype, tmp_ttlrdata);

                /* Destroy the whole NSEC3 collection associated to the NSEC3PARAM  */

                nsec3_remove_nsec3param_by_record(zone, tmp_ttlrdata);

                continue;
            }

            if(rtype == TYPE_NSEC3)
            {
                /* Remove the NSEC3 label (and its signature)
                 *
                 * The previous record will have its signature changed, no doubt.
                 * But I cannot edit the previous one about this.
                 * Since we are in an IXFR, the previous NSEC3 is supposed to be
                 * removed too, until one of the previous is also added in the
                 * next section (soa add)
                 *
                 * zdb_zone_record_delete is not the right call, it's
                 *
                 * nsec3_...
                 *
                 */

                nsec3_remove_nsec3(zone, tmp_ttlrdata);

                continue;
            }

            if(rtype == TYPE_RRSIG)
            {
                if((GET_U16_AT(*rdata)) == TYPE_NSEC3) /** @note : NATIVETYPE */
                {
                    /* Remove the RRSIG from the NSEC3 label
                     *
                     * zdb_zone_record_delete is not the right call, it's
                     *
                     * nsec3_...
                     *
                     */

                    nsec3_remove_rrsig(zone, tmp_ttlrdata);

                    continue;
                }
            }

#endif

            /* Remove from the zone */

            zdb_zone_record_delete(zone, name.labels, (entry_name.size - name.size) - 1, rtype, tmp_ttlrdata);

        } /* Remove records */

        /* The current header is the "ADD" SOA */

        if(FAIL(err = input_stream_read_fully(is, rdata, rdata_size)))
        {
            break;
        }

#ifndef NDEBUG
#if DUMP_ICMTL_UPDATE != 0
        if(err != 0)
        {
            format("zdb_zone_update_ixfr T: %{dnsname} %d %{dnsclass} %{dnstype} ", rname, rttl, &rclass, &rtype);
            print_rdata(rtype, rdata, rdata_size);
            println("");
        }
#endif
#endif

        /* The SOA serial is supposed to be bigger than the previous one */

        u32 serial_to;

        if(FAIL(err = rr_soa_get_serial(rdata, rdata_size, &serial_to)))
        {
            break;
        }

        /*
         * After the "add" sequence is done, serial_current will be serial_to
         */

        do
        {
            /* Load the record without the data (add) */

            if(FAIL(err = input_stream_read_rr_header(is, rname, sizeof (rname), &rtype, &rclass, &rttl, &rdata_size)))
            {
                break;
            }

#if ZDB_NSEC3_SUPPORT != 0

            if(rtype == TYPE_NSEC3PARAM)
            {
                /* Load the data */

                if(FAIL(err = input_stream_read_fully(is, rdata, rdata_size)))
                {
                    break;
                }

#ifndef NDEBUG
#if DUMP_ICMTL_UPDATE != 0
                if(err != 0)
                {
                    format("zdb_zone_update_ixfr A: %{dnsname} %d %{dnsclass} %{dnstype} ", rname, rttl, &rclass, &rtype);
                    print_rdata(rtype, rdata, rdata_size);
                    println("");
                }
#endif
#endif
                /* Add it to the nsec3 context */

                nsec3_load_add_nsec3param(&nsec3_context, rdata, rdata_size);

                /* Add it to the zone */

                DEBUG_RESET_dnsname(entry_name);
                dnsname_to_dnsname_vector(rname, &entry_name);

                ZDB_RECORD_ZALLOC(ttlrdata, rttl, rdata_size, rdata);
                zdb_zone_record_add(zone, entry_name.labels, (entry_name.size - name.size) - 1, rtype, ttlrdata);
            }
            else if(rtype == TYPE_NSEC3)
            {
                /* Load the data */

                if(FAIL(err = input_stream_read_fully(is, rdata, rdata_size)))
                {
                    break;
                }

#ifndef NDEBUG
#if DUMP_ICMTL_UPDATE != 0
                if(err != 0)
                {
                    format("zdb_zone_update_ixfr A: %{dnsname} %d %{dnsclass} %{dnstype} ", rname, rttl, &rclass, &rtype);
                    print_rdata(rtype, rdata, rdata_size);
                    println("");
                }
#endif
#endif
                /* Add it to the nsec3 context */

                if(FAIL(err = nsec3_load_add_nsec3(&nsec3_context, rname, rttl, rdata, rdata_size)))
                {
                    break;
                }
            }
            else if(rtype == TYPE_RRSIG)
            {
                /* Load the data */

                if(FAIL(err = input_stream_read_fully(is, rdata, rdata_size)))
                {
                    break;
                }

#if DUMP_ICMTL_UPDATE != 0
                if(err != 0)
                {
                    format("zdb_zone_update_ixfr A: %{dnsname} %d %{dnsclass} %{dnstype} ", rname, rttl, &rclass, &rtype);
                    print_rdata(rtype, rdata, rdata_size);
                    println("");
                }
#endif

                if((GET_U16_AT(*rdata)) == TYPE_NSEC3) /** @note : NATIVETYPE */
                {
                    /* Add it to the nsec3 context */

                    if(FAIL(err = nsec3_load_add_rrsig(&nsec3_context, rname, rttl, rdata, rdata_size)))
                    {
                        break;
                    }
                }
                else
                {
                    /* Add it to the zone */

                    DEBUG_RESET_dnsname(entry_name);
                    dnsname_to_dnsname_vector(rname, &entry_name);

                    ZDB_RECORD_ZALLOC(ttlrdata, rttl, rdata_size, rdata);
                    zdb_zone_record_add(zone, entry_name.labels, (entry_name.size - name.size) - 1, rtype, ttlrdata);
                }
            }
            else
            {
#endif
                /* Add it to the zone */

                ZDB_RECORD_ZALLOC_EMPTY(ttlrdata, rttl, rdata_size);

                if(FAIL(err = input_stream_read_fully(is, ZDB_PACKEDRECORD_PTR_RDATAPTR(ttlrdata), rdata_size)))
                {
                    break;
                }

#ifndef NDEBUG
#if DUMP_ICMTL_UPDATE != 0
                if(err != 0)
                {
                    format("zdb_zone_update_ixfr A: %{dnsname} %d %{dnsclass} %{dnstype} ", rname, rttl, &rclass, &rtype);
                    print_rdata(rtype, ttlrdata->rdata_start, rdata_size);
                    println(termout, "");
                }
#endif
#endif
                DEBUG_RESET_dnsname(entry_name);
                dnsname_to_dnsname_vector(rname, &entry_name);

                zdb_zone_record_add(zone, entry_name.labels, (entry_name.size - name.size) - 1, rtype, ttlrdata); /* class is implicit */

#if ZDB_NSEC3_SUPPORT != 0
            }
#endif
        }
        while(rtype != TYPE_SOA);

        /*
         * The record is either the first of another SOA pair (sub, add)
         * Either the final one.
         */

        /* Update the current serial */

        serial_current = serial_to;

        break;
    }

    free(tmp_ttlrdata);

    if(ISOK(err))
    {
        /*
         * soa_ttlrdata is the new SOA
         */

        zdb_zone_record_add(zone, NULL, -1, TYPE_SOA, soa_ttlrdata);

#if ZDB_NSEC3_SUPPORT != 0
        /**
         * Check if there is both NSEC & NSEC3.  Reject if yes.
         *       compile NSEC if any
         *	 compile NSEC3 if any
         *
         * @todo: I'm only doing NSEC3 here. Do NSEC as well
         */
        
        err = nsec3_load_compile(&nsec3_context);
        
        if((nsec3_context.nsec3_rejected > 0)||(nsec3_context.nsec3_discarded > 0))
        {
            err = DNSSEC_ERROR_NSEC3_INVALIDZONESTATE;
        }

        if(ISOK(err))
        {
            nsec3_load_destroy(&nsec3_context);
#endif
            zone_label->zone = zone;

            return err;
#if ZDB_NSEC3_SUPPORT != 0
        }
#endif
    }

#if ZDB_NSEC3_SUPPORT != 0
    nsec3_load_destroy(&nsec3_context);
#endif

    /**
     * @note : do NOT use these to unload a zone.
     *         zdb_zone_label_delete(db, &name, zone->zclass);
     *         zdb_zone_destroy(zone);
     */

    zdb_zone_unload(db, &name, zdb_zone_getclass(zone));

    return err;
}