[ldns-users] suggestion for a parse-rrs callback method

Jelte Jansen jelte.jansen at sidn.nl
Mon Jun 3 13:43:21 UTC 2013


On 05/27/2013 05:17 PM, Robert Edmonds wrote:
> Jelte Jansen wrote:
> 
> this seems like a very simple iterator pattern.  why complicate it with
> a callback?  e.g.,
> 
>     in = fopen("my.zone", "r");
>     if (in != NULL) {
>         ldns_rr *rr;
>         ldns_rr_iter *iter = ldns_parse_rr_iter_frm_fp(in);
>         while ((rr = ldns_rr_iter_next(iter)) != NULL)
>             ldns_rr_print(stdout, rr);
>         ldns_rr_iter_free(iter);
>     }
> 
> this makes the interface much easier to call in languages that are not C
> or C++.
> 

OK, I didn't think callbacks are that much more complicated (certainly
since the lib-side code is easier), but yeah that makes sense, and using
something like an iterator does make it more flexible anyway, and not
much harder to do.

(BTW nlnetlabs! Move to github! I wanna do pull requests ;))

Proposal 2:


#include <errno.h>
#include <stdio.h>
#include <stdint.h>

#include <ldns/ldns.h>

#define DEBUG 1

// Iterator structure, initialize with ldns_rr_iter_frm_file()
// must be cleaned up with ldns_rr_iter_free() when done.
// Will parse and return a newly allocated rr on each call to
// ldns_rr_iter_next(). The rr must be freed by the caller.
typedef struct {
    FILE* fp;

    uint32_t ttl;
    ldns_rdf* prev;
    ldns_rdf* origin;

    ldns_status last_status;
} ldns_rr_iterator;

// Creates an iterator from a FILE pointer
// Caller must free it with ldns_rr_iter_free() when done
// Todo: Make it take defaults for ttl and origin?
ldns_rr_iterator*
ldns_rr_iter_frm_file(FILE* in) {
    ldns_rr_iterator* iter = (ldns_rr_iterator*)
                                   malloc(sizeof(ldns_rr_iterator));
    iter->fp=in;
    iter->last_status = LDNS_STATUS_OK;
    iter->ttl = 3600;
    iter->prev = NULL;
    iter->origin = NULL;
    return iter;
}

// Parses and returns the next resource records in the iterator.
// Returned RR must be freed by the caller
// Returns NULL when done, or when a parse error is encountered.
// Check the value of the iterators last_status when NULL is returned
// to see which case was found.
ldns_rr*
ldns_rr_iter_next(ldns_rr_iterator* iter) {
    ldns_rr* rr = NULL;
    ldns_status status;

    while (!feof(iter->fp)) {
        status = ldns_rr_new_frm_fp(&rr, iter->fp, &iter->ttl,
                                            &iter->origin, &iter->prev);
        if (status == LDNS_STATUS_SYNTAX_EMPTY ||
            status == LDNS_STATUS_SYNTAX_TTL ||
            status == LDNS_STATUS_SYNTAX_ORIGIN) {
                // should there be nothing left after this, we
                // don't want to report an error to the caller
                status = LDNS_STATUS_OK;
                continue;
        } else {
            iter->last_status = status;
            break;
        }
    }

    return rr;
}

void
ldns_rr_iter_free(ldns_rr_iterator* iter) {
    ldns_rdf_deep_free(iter->prev);
    ldns_rdf_deep_free(iter->origin);
    free(iter);
}

// Example use,
int main(int argc, char** argv) {
    if (argc != 2) {
        printf("usage: rr_printer <file>\n");
        return 1;
    }
    char* filename = argv[1];
    FILE* in = fopen(filename, "r");
    ldns_rr* cur_rr;

    if (in != NULL) {
        ldns_rr_iterator* iter = ldns_rr_iter_frm_file(in);
        while (cur_rr = ldns_rr_iter_next(iter)) {
            ldns_rr_print(stdout, cur_rr);
            ldns_rr_free(cur_rr);
        }
        if (iter->last_status != LDNS_STATUS_OK) {
            printf("Error: %s\n",
ldns_get_errorstr_by_id(iter->last_status));
        }
        ldns_rr_iter_free(iter);
    } else {
        printf("Error opening %s: %s\n", filename, strerror(errno));
        return 1;
    }
    fclose(in);

    return 0;
}




More information about the ldns-users mailing list