#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include <unbound.h> /* unbound API */
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
SV * _ub_result_to_svhv_and_free (struct ub_result* result) {
SV *val;
AV *data = newAV();
unsigned int i = 0;
if (result->data != NULL) {
while (result->data[i] != NULL) {
val = newSVpvn(result->data[i], result->len[i]);
av_push(data, val);
i++;
}
}
HV * rh = newHV();
val = newSVpv(result->qname, 0);
hv_stores(rh, "qname", val);
val = newSViv(result->qtype);
hv_stores(rh, "qtype", val);
val = newSViv(result->qclass);
hv_stores(rh, "qclass", val);
hv_stores(rh, "data", newRV_inc((SV *)data));
val = newSVpv(result->canonname, 0);
hv_stores(rh, "canonname", val);
val = newSViv(result->rcode);
hv_stores(rh, "rcode", val);
val = newSViv(result->havedata);
hv_stores(rh, "havedata", val);
val = newSViv(result->nxdomain);
hv_stores(rh, "nxdomain", val);
val = newSViv(result->secure);
hv_stores(rh, "secure", val);
val = newSViv(result->bogus);
hv_stores(rh, "bogus", val);
val = newSVpv(result->why_bogus, 0);
hv_stores(rh, "why_bogus", val);
val = newSViv(result->ttl);
hv_stores(rh, "ttl", val);
ub_resolve_free(result);
return (SV *)rh;
}
void _async_resolve_callback(void* mydata, int err, struct ub_result* result) {
SV *result_sv = (SV *) mydata;
if (err) {
SvUPGRADE( result_sv, SVt_IV );
SvIV_set( result_sv, err );
SvIOK_on( result_sv );
}
else {
SV * svres = _ub_result_to_svhv_and_free(result);
SvUPGRADE( result_sv, SVt_RV );
SvRV_set( result_sv, svres );
SvROK_on( result_sv );
}
return;
}
MODULE = DNS::Unbound PACKAGE = DNS::Unbound
PROTOTYPES: DISABLE
struct ub_ctx*
_create_context()
CODE:
struct ub_ctx* my_ctx = ub_ctx_create();
if (!my_ctx) {
croak("Failed to create Unbound context!");
}
RETVAL = my_ctx;
OUTPUT:
RETVAL
int
_ub_ctx_set_option( struct ub_ctx *ctx, const char* opt, const char* val)
CODE:
RETVAL = ub_ctx_set_option(ctx, opt, val);
OUTPUT:
RETVAL
void
_ub_ctx_debuglevel( struct ub_ctx *ctx, int d )
CODE:
ub_ctx_debuglevel(ctx, d);
void
_ub_ctx_debugout( struct ub_ctx *ctx, int fd, const char *mode )
CODE:
FILE *fstream;
// Since libunbound does equality checks against stderr,
// let’s ensure we use that same pointer.
if (fd == fileno(stderr)) {
fstream = stderr;
}
else if (fd == fileno(stdout)) {
fstream = stdout;
}
else {
// Linux doesn’t care, but MacOS will segfault if you
// setvbuf() on an append stream opened on a non-append fd.
fstream = fdopen( fd, mode );
if (fstream == NULL) {
fprintf(stderr, "fdopen failed!!\n");
}
setvbuf(fstream, NULL, _IONBF, 0);
}
ub_ctx_debugout( ctx, fstream );
const char *
_get_fd_mode_for_fdopen(int fd)
CODE:
int flags = fcntl( fd, F_GETFL );
if ( flags == -1 ) {
SETERRNO( errno, 0 );
RETVAL = "";
}
else {
RETVAL = (flags & O_APPEND) ? "a" : "w";
}
OUTPUT:
RETVAL
SV *
_ub_ctx_get_option( struct ub_ctx *ctx, const char* opt)
CODE:
char *str;
int fate = ub_ctx_get_option(ctx, opt, &str);
if (fate) {
// On failure, return a plain SV that gives the error.
RETVAL = newSViv(fate);
}
else {
SV *val = newSVpv(str, 0);
// On success, return a reference to an SV that gives the value.
RETVAL = newRV_inc(val);
}
free(str);
OUTPUT:
RETVAL
const char *
_ub_strerror( int err )
CODE:
RETVAL = ub_strerror(err);
OUTPUT:
RETVAL
int
_ub_ctx_async( struct ub_ctx *ctx, int dothread )
CODE:
RETVAL = ub_ctx_async( ctx, dothread );
OUTPUT:
RETVAL
int
_ub_poll( struct ub_ctx *ctx )
CODE:
RETVAL = ub_poll(ctx);
OUTPUT:
RETVAL
int
_ub_wait( struct ub_ctx *ctx )
CODE:
RETVAL = ub_wait(ctx);
OUTPUT:
RETVAL
int
_ub_process( struct ub_ctx *ctx )
CODE:
RETVAL = ub_process(ctx);
OUTPUT:
RETVAL
int
_ub_cancel( struct ub_ctx *ctx, int async_id )
CODE:
RETVAL = ub_cancel(ctx, async_id);
OUTPUT:
RETVAL
int
_ub_fd( struct ub_ctx *ctx )
CODE:
RETVAL = ub_fd(ctx);
OUTPUT:
RETVAL
SV *
_resolve_async( struct ub_ctx *ctx, const char *name, int type, int class, SV *result )
CODE:
int async_id = 0;
// A few different approaches were tried here, including passing
// coderefs to ub_resolve_async, but the one thing that seems to
// work is passing a pointer to the result SV, which the async
// callback then receives; that callback then populates the SV
// with either the result hashref (success) or the failure number.
// This does mean that it has to be Perl that checks for whether
// the result SV is populated--which seems to work just fine.
int reserr = ub_resolve_async(
ctx,
name, type, class,
(void *) result, _async_resolve_callback, &async_id
);
AV *ret = newAV();
av_push( ret, newSViv(reserr) );
av_push( ret, newSViv(async_id) );
RETVAL = newRV_inc((SV *)ret);
OUTPUT:
RETVAL
SV *
_resolve( struct ub_ctx *ctx, SV *name, int type, int class = 1 )
CODE:
struct ub_result* result;
int retval;
retval = ub_resolve(ctx, SvPV_nolen(name), type, class, &result);
if (retval != 0) {
RETVAL = newSViv(retval);
}
else {
SV *svhv = _ub_result_to_svhv_and_free(result);
RETVAL = newRV_inc(svhv);
}
OUTPUT:
RETVAL
BOOT:
HV *stash = gv_stashpvn("DNS::Unbound", 12, FALSE);
newCONSTSUB(stash, "unbound_version", newSVpv( ub_version(), 0 ));
void
_destroy_context( struct ub_ctx *ctx )
CODE:
// Workaround for https://github.com/NLnetLabs/unbound/issues/39:
ub_ctx_debugout(ctx, stderr);
ub_ctx_delete(ctx);