pick up the system resolvconf nameservers and hosts there.
- included ldns updated (enum warning fixed).
- makefile fix for parallel makes.
+ - Patch from Zdenek Vasicek and Attila Nagy for using the source IP
+ from python scripts. See pythonmod/examples/resip.py.
+ - doxygen comment fixes.
2 September 2009: Wouter
- TRAFFIC keyword for testbound. Simplifies test generation.
pythonmod/interface.h \
pythonmod/examples/resgen.py \
pythonmod/examples/resmod.py \
+ pythonmod/examples/resip.py \
libunbound/python/unbound.py \
libunbound/python/libunbound_wrap.c \
./ldns-src
SUEXEC = sudo
UNBOUND = ../unbound
-SCRIPT = ./test-dict.conf
+SCRIPT = ./test-resip.conf
UNBOUND_OPTS = -dv -c $(SCRIPT)
--- /dev/null
+'''
+ resip.py: This example shows how to generate authoritative response
+ and how to find out the IP address of a client
+
+ Copyright (c) 2009, Zdenek Vasicek (vasicek AT fit.vutbr.cz)
+ Marek Vavrusa (xvavru00 AT stud.fit.vutbr.cz)
+
+ This software is open source.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name of the organization nor the names of its
+ contributors may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ Usage:
+
+ dig @127.0.0.1 -t TXT what.is.my.ip.
+'''
+
+def init(id, cfg): return True
+
+def deinit(id): return True
+
+def inform_super(id, qstate, superqstate, qdata): return True
+
+def operate(id, event, qstate, qdata):
+ print "Operate", event,"state:",qstate
+
+ # Please note that if this module blocks, by moving to the validator
+ # to validate or iterator to lookup or spawn a subquery to look up,
+ # then, other incoming queries are queued up onto this module and
+ # all of them receive the same reply.
+ # You can inspect the cache.
+
+ if (event == MODULE_EVENT_NEW) or (event == MODULE_EVENT_PASS):
+ if (qstate.qinfo.qname_str.endswith("what.is.my.ip.")): #query name ends with localdomain
+ #create instance of DNS message (packet) with given parameters
+ msg = DNSMessage(qstate.qinfo.qname_str, RR_TYPE_TXT, RR_CLASS_IN, PKT_QR | PKT_RA | PKT_AA)
+ #append RR
+ if (qstate.qinfo.qtype == RR_TYPE_TXT) or (qstate.qinfo.qtype == RR_TYPE_ANY):
+ rl = qstate.mesh_info.reply_list
+ while (rl):
+ if rl.query_reply:
+ q = rl.query_reply
+ # The TTL of 0 is mandatory, otherwise it ends up in
+ # the cache, and is returned to other IP addresses.
+ msg.answer.append("%s 0 IN TXT \"%s %d (%s)\"" % (qstate.qinfo.qname_str, q.addr,q.port,q.family))
+ rl = rl.next
+
+ #set qstate.return_msg
+ if not msg.set_return_msg(qstate):
+ qstate.ext_state[id] = MODULE_ERROR
+ return True
+
+ #we don't need validation, result is valid
+ qstate.return_msg.rep.security = 2
+
+ qstate.return_rcode = RCODE_NOERROR
+ qstate.ext_state[id] = MODULE_FINISHED
+ return True
+ else:
+ #pass the query to validator
+ qstate.ext_state[id] = MODULE_WAIT_MODULE
+ return True
+
+ if event == MODULE_EVENT_MODDONE:
+ log_info("pythonmod: iterator module done")
+ qstate.ext_state[id] = MODULE_FINISHED
+ return True
+
+ log_err("pythonmod: bad event")
+ qstate.ext_state[id] = MODULE_ERROR
+ return True
#include "config.h"
#include "util/log.h"
#include "util/module.h"
+ #include "util/netevent.h"
#include "util/regional.h"
#include "util/config_file.h"
#include "util/data/msgreply.h"
#include "util/data/dname.h"
#include "util/storage/lruhash.h"
#include "services/cache/dns.h"
+ #include "services/mesh.h"
%}
%include "stdint.i" // uint_16_t can be known type now
%}
}
+/* ************************************************************************************ *
+ Structure mesh_state
+ * ************************************************************************************ */
+struct mesh_state {
+ struct mesh_reply* reply_list;
+};
+
+struct mesh_reply {
+ struct mesh_reply* next;
+ struct comm_reply query_reply;
+};
+
+struct comm_reply {
+
+};
+
+%inline %{
+
+ PyObject* _comm_reply_addr_get(struct comm_reply* reply) {
+ char dest[64];
+ reply_addr2str(reply, dest, 64);
+ if (dest[0] == 0)
+ return Py_None;
+ return PyString_FromString(dest);
+ }
+
+ PyObject* _comm_reply_family_get(struct comm_reply* reply) {
+
+ int af = (int)((struct sockaddr_in*) &(reply->addr))->sin_family;
+
+ switch(af) {
+ case AF_INET: return PyString_FromString("ip4");
+ case AF_INET6: return PyString_FromString("ip6");
+ case AF_UNIX: return PyString_FromString("unix");
+ }
+
+ return Py_None;
+ }
+
+ PyObject* _comm_reply_port_get(struct comm_reply* reply) {
+ uint16_t port;
+ port = ntohs(((struct sockaddr_in*)&(reply->addr))->sin_port);
+ return PyInt_FromLong(port);
+ }
+
+%}
+
+%extend comm_reply {
+ %pythoncode %{
+ def _addr_get(self): return _comm_reply_addr_get(self)
+ __swig_getmethods__["addr"] = _addr_get
+ if _newclass:addr = _swig_property(_addr_get)
+
+ def _port_get(self): return _comm_reply_port_get(self)
+ __swig_getmethods__["port"] = _port_get
+ if _newclass:port = _swig_property(_port_get)
+
+ def _family_get(self): return _comm_reply_family_get(self)
+ __swig_getmethods__["family"] = _family_get
+ if _newclass:family = _swig_property(_family_get)
+ %}
+}
/* ************************************************************************************ *
Structure module_qstate
* ************************************************************************************ */
*/
#include "config.h"
#include "util/module.h"
+#include "util/netevent.h"
#include "util/net_help.h"
#include "services/cache/dns.h"
#include "services/cache/rrset.h"
#undef _XOPEN_SOURCE
#include <Python.h>
-/** Store the reply_info and query_info pair in message cache (qstate->msg_cache) */
+/* Store the reply_info and query_info pair in message cache (qstate->msg_cache) */
int storeQueryInCache(struct module_qstate* qstate, struct query_info* qinfo, struct reply_info* msgrep, int is_referral)
{
if (!msgrep)
return 0;
- if (msgrep->authoritative) //authoritative answer can't be stored in cache
+ if (msgrep->authoritative) /*authoritative answer can't be stored in cache*/
{
PyErr_SetString(PyExc_ValueError, "Authoritative answer can't be stored");
return 0;
return dns_cache_store(qstate->env, qinfo, msgrep, is_referral);
}
-/** Invalidate the message associated with query_info stored in message cache */
+/* Invalidate the message associated with query_info stored in message cache */
void invalidateQueryInCache(struct module_qstate* qstate, struct query_info* qinfo)
{
hashvalue_t h;
h = query_info_hash(qinfo);
if ((e=slabhash_lookup(qstate->env->msg_cache, h, qinfo, 0)))
{
- r = (struct reply_info*)(e->data);
- if (r)
- {
- r->ttl = 0;
+ r = (struct reply_info*)(e->data);
+ if (r)
+ {
+ r->ttl = 0;
if(rrset_array_lock(r->ref, r->rrset_count, *qstate->env->now)) {
for(i=0; i< r->rrset_count; i++)
{
}
rrset_array_unlock(r->ref, r->rrset_count);
}
- }
- lock_rw_unlock(&e->lock);
+ }
+ lock_rw_unlock(&e->lock);
} else {
- log_info("invalidateQueryInCache: qinfo is not in cache");
+ log_info("invalidateQueryInCache: qinfo is not in cache");
}
}
-/** Create response according to the ldns packet content */
+/* Create response according to the ldns packet content */
int createResponse(struct module_qstate* qstate, ldns_buffer* pkt)
{
struct msg_parse* prs;
/* parse message */
prs = (struct msg_parse*) regional_alloc(qstate->env->scratch, sizeof(struct msg_parse));
if (!prs) {
- log_err("storeResponse: out of memory on incoming message");
- return 0;
+ log_err("storeResponse: out of memory on incoming message");
+ return 0;
}
memset(prs, 0, sizeof(*prs));
ldns_buffer_set_position(pkt, 0);
if (parse_packet(pkt, prs, qstate->env->scratch) != LDNS_RCODE_NOERROR) {
- verbose(VERB_ALGO, "storeResponse: parse error on reply packet");
- return 0;
+ verbose(VERB_ALGO, "storeResponse: parse error on reply packet");
+ return 0;
}
/* edns is not examined, but removed from message to help cache */
if(parse_extract_edns(prs, &edns) != LDNS_RCODE_NOERROR)
- return 0;
+ return 0;
/* remove CD-bit, we asked for in case we handle validation ourself */
prs->flags &= ~BIT_CD;
memset(qstate->return_msg, 0, sizeof(*qstate->return_msg));
if(!parse_create_msg(pkt, prs, NULL, &(qstate->return_msg)->qinfo, &(qstate->return_msg)->rep, qstate->region)) {
- log_err("storeResponse: malloc failure: allocating incoming dns_msg");
- return 0;
+ log_err("storeResponse: malloc failure: allocating incoming dns_msg");
+ return 0;
}
/* Make sure that the RA flag is set (since the presence of
* this module means that recursion is available) */
- //qstate->return_msg->rep->flags |= BIT_RA;
+ /* qstate->return_msg->rep->flags |= BIT_RA; */
/* Clear the AA flag */
/* FIXME: does this action go here or in some other module? */
- //qstate->return_msg->rep->flags &= ~BIT_AA;
+ /*qstate->return_msg->rep->flags &= ~BIT_AA; */
/* make sure QR flag is on */
- //qstate->return_msg->rep->flags |= BIT_QR;
+ /*qstate->return_msg->rep->flags |= BIT_QR; */
if(verbosity >= VERB_ALGO)
- log_dns_msg("storeResponse: packet:", &qstate->return_msg->qinfo, qstate->return_msg->rep);
+ log_dns_msg("storeResponse: packet:", &qstate->return_msg->qinfo, qstate->return_msg->rep);
return 1;
}
+
+
+/* Convert reply->addr to string */
+void reply_addr2str(struct comm_reply* reply, char* dest, int maxlen)
+{
+ int af = (int)((struct sockaddr_in*) &(reply->addr))->sin_family;
+ void* sinaddr = &((struct sockaddr_in*) &(reply->addr))->sin_addr;
+
+ if(af == AF_INET6)
+ sinaddr = &((struct sockaddr_in6*)&(reply->addr))->sin6_addr;
+ dest[0] = 0;
+ if (inet_ntop(af, sinaddr, dest, (socklen_t)maxlen) == 0)
+ return;
+ dest[maxlen-1] = 0;
+}
*/
int createResponse(struct module_qstate* qstate, ldns_buffer* pkt);
+/**
+ * Convert reply->addr to string
+ * @param reply: comm reply with address in it.
+ * @param dest: destination string.
+ * @param maxlen: length of string buffer.
+ */
+void reply_addr2str(struct comm_reply* reply, char* dest, int maxlen);
+
#endif /* PYTHONMOD_UTILS_H */
--- /dev/null
+# Example configuration file for resip.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 python iterator"
+
+# Python config section
+python:
+ # Script file to load
+ python-script: "./examples/resip.py"
+
int ub_c_lex(void);
/** wrap function */
int ub_c_wrap(void);
-/** print error with file and line number */
-void ub_c_error(const char *message);
/** init ports possible for use */
static void init_outgoing_availports(int* array, int num);
return 0;
}
-/** free event base, free events yourself */
+/* free event base, free events yourself */
void event_base_free(struct event_base* base)
{
if(!base)
ev->added = 0;
}
-/** add event to a base */
+/* add event to a base */
int event_base_set(struct event_base* base, struct event* ev)
{
ev->ev_base = base;
return 0;
}
-/** add event to make it active, you may not change it with event_set anymore */
+/* add event to make it active, you may not change it with event_set anymore */
int event_add(struct event* ev, struct timeval* tv)
{
if(ev->added)
return 0;
}
-/** remove event, you may change it again */
+/* remove event, you may change it again */
int event_del(struct event* ev)
{
if(ev->ev_fd != -1 && ev->ev_fd >= ev->ev_base->capfd)
int maxfd;
/** capacity - size of the fds array */
int capfd;
- /** fdset for read write, for fds ready, and added */
- fd_set reads, writes, ready, content;
+ /* fdset for read write, for fds ready, and added */
+ fd_set
+ /** fds for reading */
+ reads,
+ /** fds for writing */
+ writes,
+ /** fds determined ready for use */
+ ready,
+ /** ready plus newly added events. */
+ content;
/** array of 0 - maxsig of ptr to event for it */
struct event** signals;
/** if we need to exit */
/** remove event. You may change it again */
int event_del(struct event *);
+/** add a timer */
#define evtimer_add(ev, tv) event_add(ev, tv)
+/** remove a timer */
#define evtimer_del(ev) event_del(ev)
/* uses different implementation. Cannot mix fd/timeouts and signals inside
/**
* Read a keyword skipping bind comments; spaces, specials, restkeywords.
* The file is split into the following tokens:
- * * special characters, on their own, rdlen=1, { } " ;
+ * * special characters, on their own, rdlen=1, { } doublequote ;
* * whitespace becomes a single ' ' or tab. Newlines become spaces.
* * other words ('keywords')
* * comments are skipped if desired