INSTALL=$(SHELL) $(srcdir)/install-sh
-DYNLIBMOD_SRC=dynlibmod/dynlibmod.c
+DYNLIBMOD_SRC=dynlibmod/dynlibmod.c dynlibmod/dnsmessage.c
DYNLIBMOD_OBJ=@DYNLIBMOD_OBJ@
DYNLIBMOD_HEADER=@DYNLIBMOD_HEADER@
DYNLIBMOD_EXTRALIBS=@DYNLIBMOD_EXTRALIBS@
dnstap_fstrm.lo dnstap_fstrm.o: $(srcdir)/dnstap/dnstap_fstrm.c config.h $(srcdir)/dnstap/dnstap_fstrm.h
unbound-dnstap-socket.lo unbound-dnstap-socket.o: $(srcdir)/dnstap/unbound-dnstap-socket.c config.h $(srcdir)/dnstap/dtstream.h
dynlibmod.lo dynlibdmod.o: $(srcdir)/dynlibmod/dynlibmod.c config.h $(srcdir)/dynlibmod/dynlibmod.h
+dnsmessage.lo dynlibdmod.o: $(srcdir)/dynlibmod/dnsmessage.c config.h $(srcdir)/dynlibmod/dnsmessage.h
# dnscrypt
dnscrypt.lo dnscrypt.o: $(srcdir)/dnscrypt/dnscrypt.c config.h \
WITH_DYNLIBMODULE=yes
- DYNLIBMOD_OBJ="dynlibmod.lo"
+ DYNLIBMOD_OBJ="dynlibmod.lo dnsmessage.lo"
DYNLIBMOD_HEADER='$(srcdir)/dynlibmod/dynlibmod.h'
--- /dev/null
+#include <stdint.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <malloc.h>
+
+#include <config.h>
+#include <util/module.h>
+#include <sldns/parseutil.h>
+#include <dynlibmod/dynlibmod.h>
+
+#include <sldns/sbuffer.h>
+#include <sldns/str2wire.h>
+#include <pythonmod/pythonmod_utils.h>
+#include <services/cache/dns.h>
+
+#include "dnsmessage.h"
+
+struct dynlibmod_section_txt {
+ char *txt;
+ struct dynlibmod_section_txt *next;
+};
+
+struct dynlibmod_dnsmessage {
+ char *rr_name;
+ sldns_rr_type rr_type;
+ sldns_rr_class rr_class;
+ uint32_t default_ttl;
+ uint16_t flags;
+
+ // Question, Answer, Authority, and Additional sections
+ struct dynlibmod_section_txt *sections[4];
+};
+
+struct dynlibmod_dnsmessage *dynlibmod_dnsmessage_new(char *rr_name, sldns_rr_type rr_type, sldns_rr_class rr_class, uint16_t flags, uint32_t default_ttl) {
+ struct dynlibmod_dnsmessage *dns_message = malloc(sizeof(struct dynlibmod_dnsmessage));
+
+ if (!dns_message) {
+ log_err("%s:%d malloc failure allocating dns_message", __FUNCTION__, __LINE__);
+ goto error;
+ }
+
+ memset(dns_message, 0, sizeof(struct dynlibmod_dnsmessage));
+
+ if (rr_name) {
+ dns_message->rr_name = strdup(rr_name);
+
+ if (!dns_message->rr_name) {
+ log_err("%s:%d malloc failure allocating rr_name", __FUNCTION__, __LINE__);
+ goto error;
+ }
+ }
+
+ dns_message->rr_type = rr_type;
+ dns_message->rr_class = rr_class;
+ dns_message->flags = flags;
+ dns_message->default_ttl = default_ttl;
+
+ return(dns_message);
+
+error:
+ if (dns_message)
+ free(rr_name);
+
+ free(dns_message);
+ return NULL;
+}
+
+void dynlibmod_dnsmessage_free(struct dynlibmod_dnsmessage *dnsmessage) {
+ if (!dnsmessage)
+ return;
+
+ free(dnsmessage->rr_name);
+ for (int i = 0; i < 4; i++)
+ for (struct dynlibmod_section_txt *ptr = dnsmessage->sections[i]; ptr;) {
+ free(ptr->txt);
+ struct dynlibmod_section_txt *tmp = ptr;
+ ptr = ptr->next;
+ free(tmp);
+ }
+
+ free(dnsmessage);
+}
+
+void dynlibmod_msg_append(struct dynlibmod_dnsmessage *msg, enum enum_section section, const char *format, ...) {
+ va_list args;
+
+ va_start(args, format);
+ dynlibmod_vmsg_append(msg, section, format, args);
+ va_end( args );
+}
+
+void dynlibmod_vmsg_append(struct dynlibmod_dnsmessage *msg, enum enum_section section, const char *format, va_list args) {
+ struct dynlibmod_section_txt *txt = malloc(sizeof(struct dynlibmod_section_txt));
+
+ if (!txt) {
+ log_err("%s:%d malloc failure allocating section txt structure", __FUNCTION__, __LINE__);
+ goto error;
+ }
+
+ memset(txt, 0, sizeof(struct dynlibmod_section_txt));
+
+ va_list cpy_args;
+ va_copy(cpy_args, args);
+
+ int len = vsnprintf(NULL, 0, format, args);
+ len++;
+ txt->txt = malloc(len);
+
+ if (!txt->txt) {
+ va_end(cpy_args);
+ log_err("%s:%d malloc failure allocating section txt member", __FUNCTION__, __LINE__);
+ goto error;
+ }
+
+ vsnprintf(txt->txt, len, format, cpy_args);
+ va_end(cpy_args);
+
+ if (!msg->sections[section]) {
+ msg->sections[section] = txt;
+ return;
+ }
+
+ struct dynlibmod_section_txt *ptr = NULL;
+ for (ptr = msg->sections[section]; ptr->next; ptr = ptr->next)
+ ;
+
+ ptr->next = txt;
+ return;
+
+error:
+ if (txt)
+ free(txt->txt);
+ free(txt);
+
+ return;
+}
+
+/**
+ * Enumerate a section from a dynlibmod_dnsmessage structure and add all of it's members to the dns message
+ */
+static int _dynlibmod_pushRRList(sldns_buffer* qb, struct dynlibmod_section_txt *txt, uint32_t default_ttl, int qsec, size_t count_offset) {
+ size_t len;
+ char* s;
+
+ for (; txt; txt = txt->next) {
+ s = txt->txt;
+
+ len = sldns_buffer_remaining(qb);
+ if(qsec) {
+ if(sldns_str2wire_rr_question_buf(s, sldns_buffer_current(qb), &len, NULL, NULL, 0, NULL, 0)!= 0)
+ return 0;
+ } else {
+ if(sldns_str2wire_rr_buf(s, sldns_buffer_current(qb), &len, NULL, default_ttl, NULL, 0, NULL, 0) != 0)
+ return 0;
+ }
+ sldns_buffer_skip(qb, len);
+
+ sldns_buffer_write_u16_at(qb, count_offset,
+ sldns_buffer_read_u16_at(qb, count_offset)+1);
+
+ }
+ return 1;
+}
+
+/**
+ * A low level function for preparing a DNS answer by parsing a dynlibmod_dnsmessage.
+ */
+int dynlibmod_set_return_msg(struct module_qstate* qstate, struct dynlibmod_dnsmessage *msg) {
+ sldns_buffer *qb = 0;
+ int res = 1;
+ size_t l;
+
+ if ((qb = sldns_buffer_new(LDNS_RR_BUF_SIZE)) == 0) return 0;
+
+ /* write header */
+ sldns_buffer_write_u16(qb, 0); /* ID */
+ sldns_buffer_write_u16(qb, 0); /* flags */
+ sldns_buffer_write_u16(qb, 1); /* qdcount */
+ sldns_buffer_write_u16(qb, 0); /* ancount */
+ sldns_buffer_write_u16(qb, 0); /* nscount */
+ sldns_buffer_write_u16(qb, 0); /* arcount */
+ if ((msg->flags&PKT_QR)) LDNS_QR_SET(sldns_buffer_begin(qb));
+ if ((msg->flags&PKT_AA)) LDNS_AA_SET(sldns_buffer_begin(qb));
+ if ((msg->flags&PKT_TC)) LDNS_TC_SET(sldns_buffer_begin(qb));
+ if ((msg->flags&PKT_RD)) LDNS_RD_SET(sldns_buffer_begin(qb));
+ if ((msg->flags&PKT_CD)) LDNS_CD_SET(sldns_buffer_begin(qb));
+ if ((msg->flags&PKT_RA)) LDNS_RA_SET(sldns_buffer_begin(qb));
+ if ((msg->flags&PKT_AD)) LDNS_AD_SET(sldns_buffer_begin(qb));
+
+ /* write the query */
+ l = sldns_buffer_remaining(qb);
+ if(sldns_str2wire_dname_buf(msg->rr_name, sldns_buffer_current(qb), &l) != 0) {
+ sldns_buffer_free(qb);
+ return 0;
+ }
+ sldns_buffer_skip(qb, l);
+ if (msg->rr_type == 0) { msg->rr_type = LDNS_RR_TYPE_A; }
+ if (msg->rr_class == 0) { msg->rr_class = LDNS_RR_CLASS_IN; }
+ sldns_buffer_write_u16(qb, msg->rr_type);
+ sldns_buffer_write_u16(qb, msg->rr_class);
+
+ /* write RR sections */
+ if(res && msg->sections[SECTION_QUESTION] && !_dynlibmod_pushRRList(qb, msg->sections[SECTION_QUESTION], msg->default_ttl, 1, LDNS_QDCOUNT_OFF))
+ res = 0;
+ if(res && msg->sections[SECTION_ANSWER] && !_dynlibmod_pushRRList(qb, msg->sections[SECTION_ANSWER], msg->default_ttl, 0, LDNS_ANCOUNT_OFF))
+ res = 0;
+ if(res && msg->sections[SECTION_AUTHORITY] && !_dynlibmod_pushRRList(qb, msg->sections[SECTION_AUTHORITY], msg->default_ttl, 0, LDNS_NSCOUNT_OFF))
+ res = 0;
+ if(res && msg->sections[SECTION_ADDITIONAL] && !_dynlibmod_pushRRList(qb, msg->sections[SECTION_ADDITIONAL], msg->default_ttl, 0, LDNS_ARCOUNT_OFF))
+ res = 0;
+
+ if (res) res = createResponse(qstate, qb);
+
+ if (qb) sldns_buffer_free(qb);
+ return res;
+}
+
--- /dev/null
+/**
+ * @file Implements the dnsmessage api in C for unbound plugins using the dynlib module
+ */
+
+#ifndef DNSMESSAGE_H
+#define DNSMESSAGE_H
+
+#define PKT_QR 1 /* QueRy - query flag */
+#define PKT_AA 2 /* Authoritative Answer - server flag */
+#define PKT_TC 4 /* TrunCated - server flag */
+#define PKT_RD 8 /* Recursion Desired - query flag */
+#define PKT_CD 16 /* Checking Disabled - query flag */
+#define PKT_RA 32 /* Recursion Available - server flag */
+#define PKT_AD 64 /* Authenticated Data - server flag */
+
+enum enum_section {
+ SECTION_QUESTION = 0,
+ SECTION_ANSWER = 1,
+ SECTION_AUTHORITY = 2,
+ SECTION_ADDITIONAL = 3
+};
+
+/**
+ * Structure encapsulates a new DNS Message.
+ * Created by dnylibmod_dnsmessage, it's sections
+ * are filled out by dnlibmod_msg_append.
+ * Then it is finalized by dynlibmod_set_return_msg
+ */
+struct dynlibmod_dnsmessage;
+
+/**
+ * Allocate a new dnsmessage structure and store the details provided. Must
+ * be freed by dynlibmod_dnsmessage_free after the call to dynlibmod_set_resturn_msg. (The message
+ * structure is no longer required at that point)
+ */
+struct dynlibmod_dnsmessage *dynlibmod_dnsmessage_new(char *rr_name, sldns_rr_type rr_type, sldns_rr_class rr_class, uint16_t flags, uint32_t default_ttl);
+
+/**
+ * Free a dnsmessage structure.
+ */
+void dynlibmod_dnsmessage_free(struct dynlibmod_dnsmessage *dnsmessage);
+
+/**
+ * Print an record to one of the 4 DNS sections. Uses printf conventions. See example code for usage.
+ */
+void dynlibmod_msg_append(struct dynlibmod_dnsmessage *msg, enum enum_section section, const char *format, ...);
+
+/**
+ * An alternative to dynlibmod_msg_append for use in creating wrapper functions
+ */
+void dynlibmod_vmsg_append(struct dynlibmod_dnsmessage *msg, enum enum_section section, const char *format, va_list args);
+
+/**
+ * Finalize the DNS message and store it in the qstate for return to unbound
+ */
+int dynlibmod_set_return_msg(struct module_qstate* qstate, struct dynlibmod_dnsmessage *msg);
+
+#endif /* DNSMESSAGE_H */
--- /dev/null
+CFLAGS=-I../.. -c -Wall -Werror -fpic -g -Wno-unused-variable
+
+all: resgen.so helloworld.so
+
+resgen.so: resgen.o
+ $(CC) -shared -Wall -Werror -g -o resgen.so resgen.o
+
+helloworld.so: helloworld.o
+ $(CC) -shared -Wall -Werror -g -o helloworld.so helloworld.o
+
+clean:
+ -rm -rf *.o *.so
+
+.PHONY : all clean
+
+
--- /dev/null
+/**
+ * \file
+ *
+ * This is an example to show how dynamic libraries can be made to work with
+ * unbound. To build a .so file simply run:
+ * gcc -I../.. -shared -Wall -Werror -fpic -o helloworld.so helloworld.c
+ * And to build for windows, first make unbound with the --with-dynlibmod
+ * switch, then use this command:
+ * x86_64-w64-mingw32-gcc -m64 -I../.. -shared -Wall -Werror -fpic
+ * -o helloworld.dll helloworld.c -L../.. -l:libunbound.dll.a
+ * to cross-compile a 64-bit Windows DLL. The libunbound.dll.a is produced
+ * by the compile step that makes unbound.exe and allows the dynlib dll to
+ * access definitions in unbound.exe.
+ */
+
+#include "../../config.h"
+#include "../../util/module.h"
+#include "../../sldns/parseutil.h"
+#include "../dynlibmod.h"
+#include "../../sldns/wire2str.h"
+#include "../../services/cache/dns.h"
+#include "../dnsmessage.h"
+
+
+/* Declare the EXPORT macro that expands to exporting the symbol for DLLs when
+ * compiling for Windows. All procedures marked with EXPORT in this example are
+ * called directly by the dynlib module and must be present for the module to
+ * load correctly. */
+#ifdef HAVE_WINDOWS_H
+#define EXPORT __declspec(dllexport)
+#else
+#define EXPORT
+#endif
+
+int endswith(char *str, const char *suffix, char **ptr) {
+ size_t lstr = strlen(str);
+ size_t lsuffix = strlen(suffix);
+
+ // String too long
+ if (lsuffix > lstr)
+ return 0;
+
+ int ret = memcmp(str + (lstr - lsuffix), suffix, lsuffix);
+
+ if (ret) // no match
+ return 0;
+
+ if (ptr)
+ *ptr = str + (lstr - lsuffix);
+
+ return 1;
+}
+
+/* Init is called when the module is first loaded. It should be used to set up
+ * the environment for this module and do any other initialisation required. */
+EXPORT int init(struct module_env* env, int id) {
+ log_info("dynlib: hello world from init");
+ return 1;
+}
+
+/* Deinit is run as the program is shutting down. It should be used to clean up
+ * the environment and any left over data. */
+EXPORT void deinit(struct module_env* env, int id) {
+ log_info("dynlib: hello world from deinit");
+}
+
+/* Operate is called every time a query passes by this module. The event can be
+ * used to determine which direction in the module chain it came from. */
+EXPORT void operate(struct module_qstate* qstate, enum module_ev event,
+ int id, struct outbound_entry* entry) {
+ char *qname = sldns_wire2str_dname(qstate->qinfo.qname, qstate->qinfo.qname_len);
+ struct dynlibmod_dnsmessage *msg = NULL;
+
+ log_info("dynlib: hello world from operate");
+ log_info("dynlib: incoming query: %s %s(%d) %s(%d)",
+ qname,
+ sldns_lookup_by_id(sldns_rr_classes, qstate->qinfo.qclass)->name,
+ qstate->qinfo.qclass,
+ sldns_rr_descript(qstate->qinfo.qtype)->_name,
+ qstate->qinfo.qtype);
+
+ switch(event) {
+ case module_event_new:
+ case module_event_pass:
+ if (endswith(qname, ".resgentest.", NULL)) {
+ msg = dynlibmod_dnsmessage_new(qname, (sldns_rr_type) qstate->qinfo.qtype, LDNS_RR_CLASS_IN, PKT_QR | PKT_RA | PKT_AA, 0);
+ dynlibmod_msg_append(msg, SECTION_ANSWER, "%s 10 IN A 127.1.2.3", qname);
+ if (!dynlibmod_set_return_msg(qstate, msg)) {
+ log_info("Failed to set return message");
+ qstate->ext_state[id] = module_error;
+ goto cleanup;
+ }
+
+ qstate->return_msg->rep->security = sec_status_indeterminate;
+ qstate->return_rcode = LDNS_RCODE_NOERROR;
+ qstate->ext_state[id] = module_finished;
+ goto cleanup;
+ }
+ qstate->ext_state[id] = module_wait_module;
+ goto cleanup;
+
+ case module_event_moddone:
+ qstate->ext_state[id] = module_finished;
+ goto cleanup;
+
+ default:
+ qstate->ext_state[id] = module_error;
+ goto cleanup;
+ }
+
+cleanup:
+ dynlibmod_dnsmessage_free(msg);
+ free(qname);
+}
+
+/* Inform super is called when a query is completed or errors out, but only if
+ * a sub-query has been registered to it by this module. Look at
+ * mesh_attach_sub in services/mesh.h to see how this is done. */
+EXPORT void inform_super(struct module_qstate* qstate, int id,
+ struct module_qstate* super) {
+ log_info("dynlib: hello world from inform_super");
+}
+
+/* Clear is called once a query is complete and the response has been sent
+ * back. It is used to clear up any per-query allocations. */
+EXPORT void clear(struct module_qstate* qstate, int id) {
+ log_info("dynlib: hello world from clear");
+}
+
+/* Get mem is called when Unbound is printing performance information. This
+ * only happens explicitly and is only used to show memory usage to the user. */
+EXPORT size_t get_mem(struct module_env* env, int id) {
+ log_info("dynlib: hello world from get_mem");
+ return 0;
+}
+
--- /dev/null
+# Example configuration file for resgen.py
+server:
+ verbosity: 1
+ interface: 0.0.0.0
+ do-daemonize: no
+ access-control: 0.0.0.0/0 allow
+ chroot: ""
+ username: ""
+ directory: ""
+ logfile: ""
+ pidfile: "unbound.pid"
+ module-config: "validator dynlib iterator"
+
+dynlib:
+ dynlib-file: "./examples/resgen.so"
+