diff options
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/unbound/pythonmod/LICENSE | 28 | ||||
-rw-r--r-- | usr.sbin/unbound/pythonmod/Makefile | 58 | ||||
-rw-r--r-- | usr.sbin/unbound/pythonmod/interface.i | 889 | ||||
-rw-r--r-- | usr.sbin/unbound/pythonmod/pythonmod.c | 383 | ||||
-rw-r--r-- | usr.sbin/unbound/pythonmod/pythonmod.h | 68 | ||||
-rw-r--r-- | usr.sbin/unbound/pythonmod/pythonmod_utils.c | 177 | ||||
-rw-r--r-- | usr.sbin/unbound/pythonmod/pythonmod_utils.h | 89 | ||||
-rw-r--r-- | usr.sbin/unbound/pythonmod/test-calc.conf | 18 | ||||
-rw-r--r-- | usr.sbin/unbound/pythonmod/test-dict.conf | 18 | ||||
-rw-r--r-- | usr.sbin/unbound/pythonmod/test-log.conf | 17 | ||||
-rw-r--r-- | usr.sbin/unbound/pythonmod/test-resgen.conf | 18 | ||||
-rw-r--r-- | usr.sbin/unbound/pythonmod/test-resip.conf | 18 | ||||
-rw-r--r-- | usr.sbin/unbound/pythonmod/test-resmod.conf | 19 | ||||
-rw-r--r-- | usr.sbin/unbound/pythonmod/ubmodule-msg.py | 156 | ||||
-rw-r--r-- | usr.sbin/unbound/pythonmod/ubmodule-tst.py | 149 |
15 files changed, 2105 insertions, 0 deletions
diff --git a/usr.sbin/unbound/pythonmod/LICENSE b/usr.sbin/unbound/pythonmod/LICENSE new file mode 100644 index 00000000000..7b769d09120 --- /dev/null +++ b/usr.sbin/unbound/pythonmod/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2009, Zdenek Vasicek (vasicek AT fit.vutbr.cz) + Marek Vavrusa (xvavru00 AT stud.fit.vutbr.cz) + +All rights reserved. + +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 COPYRIGHT OWNER 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. diff --git a/usr.sbin/unbound/pythonmod/Makefile b/usr.sbin/unbound/pythonmod/Makefile new file mode 100644 index 00000000000..2a00152418a --- /dev/null +++ b/usr.sbin/unbound/pythonmod/Makefile @@ -0,0 +1,58 @@ +# Makefile: tests unbound python module (please edit SCRIPT variable) +# +# 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. + +SUEXEC = sudo +UNBOUND = ../unbound +SCRIPT = ./test-resip.conf + +UNBOUND_OPTS = -dv -c $(SCRIPT) + +.PHONY: test sudo suexec doc + +all: test + +$(UNBOUND): + make -C .. + +test: $(UNBOUND) + $(UNBOUND) $(UNBOUND_OPTS) + +sudo: $(UNBOUND) + sudo $(UNBOUND) $(UNBOUND_OPTS) + +suexec: $(UNBOUND) + su -c "$(UNBOUND) $(UNBOUND_OPTS)" + +doc: + $(MAKE) -C doc html diff --git a/usr.sbin/unbound/pythonmod/interface.i b/usr.sbin/unbound/pythonmod/interface.i new file mode 100644 index 00000000000..903563658b0 --- /dev/null +++ b/usr.sbin/unbound/pythonmod/interface.i @@ -0,0 +1,889 @@ +/* + * interface.i: unbound python module + */ + +%module unboundmodule +%{ +/** + * \file + * This is the interface between the unbound server and a python module + * called to perform operations on queries. + */ + #include <sys/types.h> + #include <sys/socket.h> + #include <netinet/in.h> + #include <arpa/inet.h> + #include <stdarg.h> + #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/packed_rrset.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 + +%inline %{ + //converts [len][data][len][data][0] string to a List of labels (PyStrings) + PyObject* GetNameAsLabelList(const char* name, int len) { + PyObject* list; + int cnt=0, i; + + i = 0; + while (i < len) { + i += name[i] + 1; + cnt++; + } + + list = PyList_New(cnt); + i = 0; cnt = 0; + while (i < len) { + PyList_SetItem(list, cnt, PyString_FromStringAndSize(name + i + 1, name[i])); + i += name[i] + 1; + cnt++; + } + return list; + } +%} + +/* ************************************************************************************ * + Structure query_info + * ************************************************************************************ */ +/* Query info */ +%ignore query_info::qname; +%ignore query_info::qname_len; + + +struct query_info { + %immutable; + char* qname; + size_t qname_len; + uint16_t qtype; + uint16_t qclass; + %mutable; +}; + +%inline %{ + enum enum_rr_class { + RR_CLASS_IN = 1, + RR_CLASS_CH = 3, + RR_CLASS_HS = 4, + RR_CLASS_NONE = 254, + RR_CLASS_ANY = 255, + }; + + enum enum_rr_type { + RR_TYPE_A = 1, + RR_TYPE_NS = 2, + RR_TYPE_MD = 3, + RR_TYPE_MF = 4, + RR_TYPE_CNAME = 5, + RR_TYPE_SOA = 6, + RR_TYPE_MB = 7, + RR_TYPE_MG = 8, + RR_TYPE_MR = 9, + RR_TYPE_NULL = 10, + RR_TYPE_WKS = 11, + RR_TYPE_PTR = 12, + RR_TYPE_HINFO = 13, + RR_TYPE_MINFO = 14, + RR_TYPE_MX = 15, + RR_TYPE_TXT = 16, + RR_TYPE_RP = 17, + RR_TYPE_AFSDB = 18, + RR_TYPE_X25 = 19, + RR_TYPE_ISDN = 20, + RR_TYPE_RT = 21, + RR_TYPE_NSAP = 22, + RR_TYPE_NSAP_PTR = 23, + RR_TYPE_SIG = 24, + RR_TYPE_KEY = 25, + RR_TYPE_PX = 26, + RR_TYPE_GPOS = 27, + RR_TYPE_AAAA = 28, + RR_TYPE_LOC = 29, + RR_TYPE_NXT = 30, + RR_TYPE_EID = 31, + RR_TYPE_NIMLOC = 32, + RR_TYPE_SRV = 33, + RR_TYPE_ATMA = 34, + RR_TYPE_NAPTR = 35, + RR_TYPE_KX = 36, + RR_TYPE_CERT = 37, + RR_TYPE_A6 = 38, + RR_TYPE_DNAME = 39, + RR_TYPE_SINK = 40, + RR_TYPE_OPT = 41, + RR_TYPE_APL = 42, + RR_TYPE_DS = 43, + RR_TYPE_SSHFP = 44, + RR_TYPE_IPSECKEY = 45, + RR_TYPE_RRSIG = 46, + RR_TYPE_NSEC = 47, + RR_TYPE_DNSKEY = 48, + RR_TYPE_DHCID = 49, + RR_TYPE_NSEC3 = 50, + RR_TYPE_NSEC3PARAMS = 51, + RR_TYPE_UINFO = 100, + RR_TYPE_UID = 101, + RR_TYPE_GID = 102, + RR_TYPE_UNSPEC = 103, + RR_TYPE_TSIG = 250, + RR_TYPE_IXFR = 251, + RR_TYPE_AXFR = 252, + RR_TYPE_MAILB = 253, + RR_TYPE_MAILA = 254, + RR_TYPE_ANY = 255, + RR_TYPE_DLV = 32769, + }; + + PyObject* _get_qname(struct query_info* q) { + return PyString_FromStringAndSize((char*)q->qname, q->qname_len); + } + + PyObject* _get_qname_components(struct query_info* q) { + return GetNameAsLabelList((const char*)q->qname, q->qname_len); + } +%} + +%inline %{ + PyObject* dnameAsStr(const char* dname) { + char buf[LDNS_MAX_DOMAINLEN+1]; + buf[0] = '\0'; + dname_str((uint8_t*)dname, buf); + return PyString_FromString(buf); + } +%} + +%extend query_info { + %pythoncode %{ + def _get_qtype_str(self): return ldns_rr_type2str(self.qtype) + __swig_getmethods__["qtype_str"] = _get_qtype_str + if _newclass:qtype_str = _swig_property(_get_qtype_str) + + def _get_qclass_str(self): return ldns_rr_class2str(self.qclass) + __swig_getmethods__["qclass_str"] = _get_qclass_str + if _newclass:qclass_str = _swig_property(_get_qclass_str) + + __swig_getmethods__["qname"] = _unboundmodule._get_qname + if _newclass:qname = _swig_property(_unboundmodule._get_qname) + + __swig_getmethods__["qname_list"] = _unboundmodule._get_qname_components + if _newclass:qname_list = _swig_property(_unboundmodule._get_qname_components) + + def _get_qname_str(self): return dnameAsStr(self.qname) + __swig_getmethods__["qname_str"] = _get_qname_str + if _newclass:qname_str = _swig_property(_get_qname_str) + %} +} + +/* ************************************************************************************ * + Structure packed_rrset_key + * ************************************************************************************ */ +%ignore packed_rrset_key::dname; +%ignore packed_rrset_key::dname_len; + +/* RRsets */ +struct packed_rrset_key { + %immutable; + char* dname; + size_t dname_len; + uint32_t flags; + uint16_t type; //rrset type in network format + uint16_t rrset_class; //rrset class in network format + %mutable; +}; + +//This subroutine converts values between the host and network byte order. +//Specifically, ntohs() converts 16-bit quantities from network byte order to host byte order. +uint16_t ntohs(uint16_t netshort); + +%inline %{ + PyObject* _get_dname(struct packed_rrset_key* k) { + return PyString_FromStringAndSize((char*)k->dname, k->dname_len); + } + PyObject* _get_dname_components(struct packed_rrset_key* k) { + return GetNameAsLabelList((char*)k->dname, k->dname_len); + } +%} + +%extend packed_rrset_key { + %pythoncode %{ + def _get_type_str(self): return ldns_rr_type2str(_unboundmodule.ntohs(self.type)) + __swig_getmethods__["type_str"] = _get_type_str + if _newclass:type_str = _swig_property(_get_type_str) + + def _get_class_str(self): return ldns_rr_class2str(_unboundmodule.ntohs(self.rrset_class)) + __swig_getmethods__["rrset_class_str"] = _get_class_str + if _newclass:rrset_class_str = _swig_property(_get_class_str) + + __swig_getmethods__["dname"] = _unboundmodule._get_dname + if _newclass:dname = _swig_property(_unboundmodule._get_dname) + + __swig_getmethods__["dname_list"] = _unboundmodule._get_dname_components + if _newclass:dname_list = _swig_property(_unboundmodule._get_dname_components) + + def _get_dname_str(self): return dnameAsStr(self.dname) + __swig_getmethods__["dname_str"] = _get_dname_str + if _newclass:dname_str = _swig_property(_get_dname_str) + %} +} + +#if defined(SWIGWORDSIZE64) +typedef long int rrset_id_t; +#else +typedef long long int rrset_id_t; +#endif + +struct ub_packed_rrset_key { + struct lruhash_entry entry; + rrset_id_t id; + struct packed_rrset_key rk; +}; + +struct lruhash_entry { + lock_rw_t lock; + struct lruhash_entry* overflow_next; + struct lruhash_entry* lru_next; + struct lruhash_entry* lru_prev; + hashvalue_t hash; + void* key; + struct packed_rrset_data* data; +}; + +%ignore packed_rrset_data::rr_len; +%ignore packed_rrset_data::rr_ttl; +%ignore packed_rrset_data::rr_data; + +struct packed_rrset_data { + uint32_t ttl; //TTL (in seconds like time()) + + size_t count; //number of rrs + size_t rrsig_count; //number of rrsigs + + enum rrset_trust trust; + enum sec_status security; + + size_t* rr_len; //length of every rr's rdata + uint32_t *rr_ttl; //ttl of every rr + uint8_t** rr_data; //array of pointers to every rr's rdata; The rr_data[i] rdata is stored in uncompressed wireformat. +}; + +%pythoncode %{ + class RRSetData_RRLen: + def __init__(self, obj): self.obj = obj + def __getitem__(self, index): return _unboundmodule._get_data_rr_len(self.obj, index) + def __len__(self): return obj.count + obj.rrsig_count + class RRSetData_RRTTL: + def __init__(self, obj): self.obj = obj + def __getitem__(self, index): return _unboundmodule._get_data_rr_ttl(self.obj, index) + def __setitem__(self, index, value): _unboundmodule._set_data_rr_ttl(self.obj, index, value) + def __len__(self): return obj.count + obj.rrsig_count + class RRSetData_RRData: + def __init__(self, obj): self.obj = obj + def __getitem__(self, index): return _unboundmodule._get_data_rr_data(self.obj, index) + def __len__(self): return obj.count + obj.rrsig_count +%} + +%inline %{ + PyObject* _get_data_rr_len(struct packed_rrset_data* d, int idx) { + if ((d != NULL) && (idx >= 0) && + ((size_t)idx < (d->count+d->rrsig_count))) + return PyInt_FromLong(d->rr_len[idx]); + return Py_None; + } + void _set_data_rr_ttl(struct packed_rrset_data* d, int idx, uint32_t ttl) + { + if ((d != NULL) && (idx >= 0) && + ((size_t)idx < (d->count+d->rrsig_count))) + d->rr_ttl[idx] = ttl; + } + PyObject* _get_data_rr_ttl(struct packed_rrset_data* d, int idx) { + if ((d != NULL) && (idx >= 0) && + ((size_t)idx < (d->count+d->rrsig_count))) + return PyInt_FromLong(d->rr_ttl[idx]); + return Py_None; + } + PyObject* _get_data_rr_data(struct packed_rrset_data* d, int idx) { + if ((d != NULL) && (idx >= 0) && + ((size_t)idx < (d->count+d->rrsig_count))) + return PyString_FromStringAndSize((char*)d->rr_data[idx], + d->rr_len[idx]); + return Py_None; + } +%} + +%extend packed_rrset_data { + %pythoncode %{ + def _get_data_rr_len(self): return RRSetData_RRLen(self) + __swig_getmethods__["rr_len"] = _get_data_rr_len + if _newclass:rr_len = _swig_property(_get_data_rr_len) + def _get_data_rr_ttl(self): return RRSetData_RRTTL(self) + __swig_getmethods__["rr_ttl"] =_get_data_rr_ttl + if _newclass:rr_len = _swig_property(_get_data_rr_ttl) + def _get_data_rr_data(self): return RRSetData_RRData(self) + __swig_getmethods__["rr_data"] = _get_data_rr_data + if _newclass:rr_len = _swig_property(_get_data_rr_data) + %} +} + +/* ************************************************************************************ * + Structure reply_info + * ************************************************************************************ */ +/* Messages */ +%ignore reply_info::rrsets; +%ignore reply_info::ref; + +struct reply_info { + uint16_t flags; + uint16_t qdcount; + uint32_t ttl; + uint32_t prefetch_ttl; + + uint16_t authoritative; + enum sec_status security; + + size_t an_numrrsets; + size_t ns_numrrsets; + size_t ar_numrrsets; + size_t rrset_count; // an_numrrsets + ns_numrrsets + ar_numrrsets + + struct ub_packed_rrset_key** rrsets; + struct rrset_ref ref[1]; //? +}; + +struct rrset_ref { + struct ub_packed_rrset_key* key; + rrset_id_t id; +}; + +struct dns_msg { + struct query_info qinfo; + struct reply_info *rep; +}; + +%pythoncode %{ + class ReplyInfo_RRSet: + def __init__(self, obj): self.obj = obj + def __getitem__(self, index): return _unboundmodule._rrset_rrsets_get(self.obj, index) + def __len__(self): return obj.rrset_count + + class ReplyInfo_Ref: + def __init__(self, obj): self.obj = obj + def __getitem__(self, index): return _unboundmodule._rrset_ref_get(self.obj, index) + def __len__(self): return obj.rrset_count +%} + +%inline %{ + struct ub_packed_rrset_key* _rrset_rrsets_get(struct reply_info* r, int idx) { + if ((r != NULL) && (idx >= 0) && ((size_t)idx < r->rrset_count)) + return r->rrsets[idx]; + return NULL; + } + + struct rrset_ref* _rrset_ref_get(struct reply_info* r, int idx) { + if ((r != NULL) && (idx >= 0) && ((size_t)idx < r->rrset_count)) { +//printf("_rrset_ref_get: %lX key:%lX\n", r->ref + idx, r->ref[idx].key); + return &(r->ref[idx]); +// return &(r->ref[idx]); + } +//printf("_rrset_ref_get: NULL\n"); + return NULL; + } +%} + +%extend reply_info { + %pythoncode %{ + def _rrset_ref_get(self): return ReplyInfo_Ref(self) + __swig_getmethods__["ref"] = _rrset_ref_get + if _newclass:ref = _swig_property(_rrset_ref_get) + + def _rrset_rrsets_get(self): return ReplyInfo_RRSet(self) + __swig_getmethods__["rrsets"] = _rrset_rrsets_get + if _newclass:rrsets = _swig_property(_rrset_rrsets_get) + %} +} + +/* ************************************************************************************ * + 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 + * ************************************************************************************ */ +%ignore module_qstate::ext_state; +%ignore module_qstate::minfo; + +/* Query state */ +struct module_qstate { + struct query_info qinfo; + uint16_t query_flags; //See QF_BIT_xx constants + int is_priming; + + struct comm_reply* reply; + struct dns_msg* return_msg; + int return_rcode; + struct regional* region; /* unwrapped */ + + int curmod; + + enum module_ext_state ext_state[MAX_MODULE]; + void* minfo[MAX_MODULE]; + + struct module_env* env; /* unwrapped */ + struct mesh_state* mesh_info; +}; + +%constant int MODULE_COUNT = MAX_MODULE; + +%constant int QF_BIT_CD = 0x0010; +%constant int QF_BIT_AD = 0x0020; +%constant int QF_BIT_Z = 0x0040; +%constant int QF_BIT_RA = 0x0080; +%constant int QF_BIT_RD = 0x0100; +%constant int QF_BIT_TC = 0x0200; +%constant int QF_BIT_AA = 0x0400; +%constant int QF_BIT_QR = 0x8000; + +%inline %{ + enum enum_return_rcode { + RCODE_NOERROR = 0, + RCODE_FORMERR = 1, + RCODE_SERVFAIL = 2, + RCODE_NXDOMAIN = 3, + RCODE_NOTIMPL = 4, + RCODE_REFUSED = 5, + RCODE_YXDOMAIN = 6, + RCODE_YXRRSET = 7, + RCODE_NXRRSET = 8, + RCODE_NOTAUTH = 9, + RCODE_NOTZONE = 10 + }; +%} + +%pythoncode %{ + class ExtState: + def __init__(self, obj): self.obj = obj + def __str__(self): + return ", ".join([_unboundmodule.strextstate(_unboundmodule._ext_state_get(self.obj,a)) for a in range(0, _unboundmodule.MODULE_COUNT)]) + def __getitem__(self, index): return _unboundmodule._ext_state_get(self.obj, index) + def __setitem__(self, index, value): _unboundmodule._ext_state_set(self.obj, index, value) + def __len__(self): return _unboundmodule.MODULE_COUNT +%} + +%inline %{ + enum module_ext_state _ext_state_get(struct module_qstate* q, int idx) { + if ((q != NULL) && (idx >= 0) && (idx < MAX_MODULE)) { + return q->ext_state[idx]; + } + return 0; + } + + void _ext_state_set(struct module_qstate* q, int idx, enum module_ext_state state) { + if ((q != NULL) && (idx >= 0) && (idx < MAX_MODULE)) { + q->ext_state[idx] = state; + } + } +%} + +%extend module_qstate { + %pythoncode %{ + def set_ext_state(self, id, state): + """Sets the ext state""" + _unboundmodule._ext_state_set(self, id, state) + + def __ext_state_get(self): return ExtState(self) + __swig_getmethods__["ext_state"] = __ext_state_get + if _newclass:ext_state = _swig_property(__ext_state_get)#, __ext_state_set) + %} +} + +/* ************************************************************************************ * + Structure config_strlist + * ************************************************************************************ */ +struct config_strlist { + struct config_strlist* next; + char* str; +}; + +/* ************************************************************************************ * + Structure config_str2list + * ************************************************************************************ */ +struct config_str2list { + struct config_str2list* next; + char* str; + char* str2; +}; + +/* ************************************************************************************ * + Structure config_file + * ************************************************************************************ */ +struct config_file { + int verbosity; + int stat_interval; + int stat_cumulative; + int stat_extended; + int num_threads; + int port; + int do_ip4; + int do_ip6; + int do_udp; + int do_tcp; + int outgoing_num_ports; + size_t outgoing_num_tcp; + size_t incoming_num_tcp; + int* outgoing_avail_ports; + size_t msg_buffer_size; + size_t msg_cache_size; + size_t msg_cache_slabs; + size_t num_queries_per_thread; + size_t jostle_time; + size_t rrset_cache_size; + size_t rrset_cache_slabs; + int host_ttl; + size_t infra_cache_slabs; + size_t infra_cache_numhosts; + char* target_fetch_policy; + int if_automatic; + int num_ifs; + char **ifs; + int num_out_ifs; + char **out_ifs; + struct config_strlist* root_hints; + struct config_stub* stubs; + struct config_stub* forwards; + struct config_strlist* donotqueryaddrs; + struct config_str2list* acls; + int donotquery_localhost; + int harden_short_bufsize; + int harden_large_queries; + int harden_glue; + int harden_dnssec_stripped; + int harden_referral_path; + int use_caps_bits_for_id; + struct config_strlist* private_address; + struct config_strlist* private_domain; + size_t unwanted_threshold; + char* chrootdir; + char* username; + char* directory; + char* logfile; + char* pidfile; + int use_syslog; + int hide_identity; + int hide_version; + char* identity; + char* version; + char* module_conf; + struct config_strlist* trust_anchor_file_list; + struct config_strlist* trust_anchor_list; + struct config_strlist* trusted_keys_file_list; + char* dlv_anchor_file; + struct config_strlist* dlv_anchor_list; + int max_ttl; + int32_t val_date_override; + int bogus_ttl; + int val_clean_additional; + int val_permissive_mode; + char* val_nsec3_key_iterations; + size_t key_cache_size; + size_t key_cache_slabs; + size_t neg_cache_size; + struct config_str2list* local_zones; + struct config_strlist* local_zones_nodefault; + struct config_strlist* local_data; + int remote_control_enable; + struct config_strlist* control_ifs; + int control_port; + char* server_key_file; + char* server_cert_file; + char* control_key_file; + char* control_cert_file; + int do_daemonize; + char* python_script; +}; + +/* ************************************************************************************ * + Enums + * ************************************************************************************ */ +%rename ("MODULE_STATE_INITIAL") "module_state_initial"; +%rename ("MODULE_WAIT_REPLY") "module_wait_reply"; +%rename ("MODULE_WAIT_MODULE") "module_wait_module"; +%rename ("MODULE_WAIT_SUBQUERY") "module_wait_subquery"; +%rename ("MODULE_ERROR") "module_error"; +%rename ("MODULE_FINISHED") "module_finished"; + +enum module_ext_state { + module_state_initial = 0, + module_wait_reply, + module_wait_module, + module_wait_subquery, + module_error, + module_finished +}; + +%rename ("MODULE_EVENT_NEW") "module_event_new"; +%rename ("MODULE_EVENT_PASS") "module_event_pass"; +%rename ("MODULE_EVENT_REPLY") "module_event_reply"; +%rename ("MODULE_EVENT_NOREPLY") "module_event_noreply"; +%rename ("MODULE_EVENT_CAPSFAIL") "module_event_capsfail"; +%rename ("MODULE_EVENT_MODDONE") "module_event_moddone"; +%rename ("MODULE_EVENT_ERROR") "module_event_error"; + +enum module_ev { + module_event_new = 0, + module_event_pass, + module_event_reply, + module_event_noreply, + module_event_capsfail, + module_event_moddone, + module_event_error +}; + +enum sec_status { + sec_status_unchecked = 0, + sec_status_bogus, + sec_status_indeterminate, + sec_status_insecure, + sec_status_secure +}; + +enum verbosity_value { + NO_VERBOSE = 0, + VERB_OPS, + VERB_DETAIL, + VERB_QUERY, + VERB_ALGO +}; + +%{ +int checkList(PyObject *l) +{ + PyObject* item; + int i; + + if (l == Py_None) + return 1; + + if (PyList_Check(l)) + { + for (i=0; i < PyList_Size(l); i++) + { + item = PyList_GetItem(l, i); + if (!PyString_Check(item)) + return 0; + } + return 1; + } + + return 0; +} + +ldns_rr_list* createRRList(PyObject *l, uint32_t default_ttl) +{ + PyObject* item; + ldns_status status; + ldns_rr_list* rr_list; + ldns_rr* rr; + int i; + + if (PyList_Size(l) == 0) + return NULL; + + rr_list = ldns_rr_list_new(); + + for (i=0; i < PyList_Size(l); i++) + { + item = PyList_GetItem(l, i); + + status = ldns_rr_new_frm_str(&rr, PyString_AsString(item), default_ttl, 0, 0); + if (status != LDNS_STATUS_OK) + continue; + + if (!ldns_rr_list_push_rr(rr_list, rr)) + continue; + + } + return rr_list; +} + +int set_return_msg(struct module_qstate* qstate, + const char* rr_name, ldns_rr_type rr_type, ldns_rr_class rr_class , uint16_t flags, uint32_t default_ttl, + PyObject* question, PyObject* answer, PyObject* authority, PyObject* additional) +{ + ldns_pkt* pkt = 0; + ldns_status status; + ldns_rr_list* rr_list = 0; + ldns_buffer *qb = 0; + int res = 1; + + if ((!checkList(question)) || (!checkList(answer)) || (!checkList(authority)) || (!checkList(additional))) + return 0; + + status = ldns_pkt_query_new_frm_str(&pkt, rr_name, rr_type, rr_class, flags); + if ((status != LDNS_STATUS_OK) || (pkt == 0)) + return 0; + + rr_list = createRRList(question, default_ttl); + if ((rr_list) && (res)) res = ldns_pkt_push_rr_list(pkt, LDNS_SECTION_QUESTION, rr_list); + ldns_rr_list_free(rr_list); + rr_list = createRRList(answer, default_ttl); + if ((rr_list) && (res)) res = ldns_pkt_push_rr_list(pkt, LDNS_SECTION_ANSWER, rr_list); + ldns_rr_list_free(rr_list); + rr_list = createRRList(authority, default_ttl); + if ((rr_list) && (res)) res = ldns_pkt_push_rr_list(pkt, LDNS_SECTION_AUTHORITY, rr_list); + ldns_rr_list_free(rr_list); + rr_list = createRRList(additional, default_ttl); + if ((rr_list) && (res)) res = ldns_pkt_push_rr_list(pkt, LDNS_SECTION_ADDITIONAL, rr_list); + ldns_rr_list_free(rr_list); + + if ((res) && ((qb = ldns_buffer_new(LDNS_MIN_BUFLEN)) == 0)) res = 0; + if ((res) && (ldns_pkt2buffer_wire(qb, pkt) != LDNS_STATUS_OK)) res = 0; + + if (res) res = createResponse(qstate, qb); + + if (qb) ldns_buffer_free(qb); + + ldns_pkt_free(pkt); //this function dealocates pkt as well as rrs + return res; +} +%} + +%constant uint16_t PKT_QR = 1; /* QueRy - query flag */ +%constant uint16_t PKT_AA = 2; /* Authoritative Answer - server flag */ +%constant uint16_t PKT_TC = 4; /* TrunCated - server flag */ +%constant uint16_t PKT_RD = 8; /* Recursion Desired - query flag */ +%constant uint16_t PKT_CD = 16; /* Checking Disabled - query flag */ +%constant uint16_t PKT_RA = 32; /* Recursion Available - server flag */ +%constant uint16_t PKT_AD = 64; /* Authenticated Data - server flag */ + +int set_return_msg(struct module_qstate* qstate, + const char* rr_name, int rr_type, int rr_class , uint16_t flags, uint32_t default_ttl, + PyObject* question, PyObject* answer, PyObject* authority, PyObject* additional); + +%pythoncode %{ + class DNSMessage: + def __init__(self, rr_name, rr_type, rr_class = RR_CLASS_IN, query_flags = 0, default_ttl = 0): + """Query flags is a combination of PKT_xx contants""" + self.rr_name = rr_name + self.rr_type = rr_type + self.rr_class = rr_class + self.default_ttl = default_ttl + self.query_flags = query_flags + self.question = [] + self.answer = [] + self.authority = [] + self.additional = [] + + def set_return_msg(self, qstate): + """Returns 1 if OK""" + status = _unboundmodule.set_return_msg(qstate, self.rr_name, self.rr_type, self.rr_class, + self.query_flags, self.default_ttl, + self.question, self.answer, self.authority, self.additional) + + if (status) and (PKT_AA & self.query_flags): + qstate.return_msg.rep.authoritative = 1 + + return status + +%} +/* ************************************************************************************ * + Functions + * ************************************************************************************ */ + +// Various debuging functions +void verbose(enum verbosity_value level, const char* format, ...); +void log_info(const char* format, ...); +void log_err(const char* format, ...); +void log_warn(const char* format, ...); +void log_hex(const char* msg, void* data, size_t length); +void log_dns_msg(const char* str, struct query_info* qinfo, struct reply_info* rep); +void log_query_info(enum verbosity_value v, const char* str, struct query_info* qinf); +void regional_log_stats(struct regional *r); + +// Free allocated memory from marked sources returning corresponding types +%typemap(newfree, noblock = 1) char * { + free($1); +} + +// Mark as source returning newly allocated memory +%newobject ldns_rr_type2str; +%newobject ldns_rr_class2str; + +// LDNS functions +char *ldns_rr_type2str(const uint16_t atype); +char *ldns_rr_class2str(const uint16_t aclass); + +// Functions from pythonmod_utils +int storeQueryInCache(struct module_qstate* qstate, struct query_info* qinfo, struct reply_info* msgrep, int is_referral); +void invalidateQueryInCache(struct module_qstate* qstate, struct query_info* qinfo); + +// Module conversion functions +const char* strextstate(enum module_ext_state s); +const char* strmodulevent(enum module_ev e); + diff --git a/usr.sbin/unbound/pythonmod/pythonmod.c b/usr.sbin/unbound/pythonmod/pythonmod.c new file mode 100644 index 00000000000..9860d001d0e --- /dev/null +++ b/usr.sbin/unbound/pythonmod/pythonmod.c @@ -0,0 +1,383 @@ +/* + * pythonmod.c: unbound module C wrapper + * + * 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. + */ +/** + * \file + * Python module for unbound. Calls python script. + */ + +/* ignore the varargs unused warning from SWIGs internal vararg support */ +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +#include "config.h" +#include <ldns/ldns.h> + +#undef _POSIX_C_SOURCE +#undef _XOPEN_SOURCE +#include <Python.h> + +#include "pythonmod/pythonmod.h" +#include "util/module.h" +#include "util/config_file.h" +#include "pythonmod_utils.h" + +#ifdef S_SPLINT_S +typedef struct PyObject PyObject; +typedef struct PyThreadState PyThreadState; +typedef void* PyGILState_STATE; +#endif + +/** + * Global state for the module. + */ +struct pythonmod_env { + + /** Python script filename. */ + const char* fname; + + /** Python main thread */ + PyThreadState* mainthr; + /** Python module. */ + PyObject* module; + + /** Module init function */ + PyObject* func_init; + /** Module deinit function */ + PyObject* func_deinit; + /** Module operate function */ + PyObject* func_operate; + /** Module super_inform function */ + PyObject* func_inform; + + /** Python dictionary. */ + PyObject* dict; + + /** Module data. */ + PyObject* data; + + /** Module qstate. */ + struct module_qstate* qstate; +}; + +/** + * Per query state for the iterator module. + */ +struct pythonmod_qstate { + + /** Module per query data. */ + PyObject* data; +}; + +/* Generated */ +#ifndef S_SPLINT_S +#include "pythonmod/interface.h" +#endif + +int pythonmod_init(struct module_env* env, int id) +{ + /* Initialize module */ + FILE* script_py = NULL; + PyObject* py_cfg, *res; + PyGILState_STATE gil; + struct pythonmod_env* pe = (struct pythonmod_env*)calloc(1, sizeof(struct pythonmod_env)); + if (!pe) + { + log_err("pythonmod: malloc failure"); + return 0; + } + + env->modinfo[id] = (void*) pe; + + /* Initialize module */ + pe->fname = env->cfg->python_script; + if(pe->fname==NULL || pe->fname[0]==0) { + log_err("pythonmod: no script given."); + return 0; + } + + /* Initialize Python libraries */ + if (!Py_IsInitialized()) + { + Py_SetProgramName("unbound"); + Py_NoSiteFlag = 1; + Py_Initialize(); + PyEval_InitThreads(); + SWIG_init(); + pe->mainthr = PyEval_SaveThread(); + } + + gil = PyGILState_Ensure(); + + /* Initialize Python */ + PyRun_SimpleString("import sys \n"); + PyRun_SimpleString("sys.path.append('.') \n"); + if(env->cfg->directory && env->cfg->directory[0]) { + char wdir[1524]; + snprintf(wdir, sizeof(wdir), "sys.path.append('%s') \n", + env->cfg->directory); + PyRun_SimpleString(wdir); + } + PyRun_SimpleString("sys.path.append('"RUN_DIR"') \n"); + PyRun_SimpleString("sys.path.append('"SHARE_DIR"') \n"); + PyRun_SimpleString("import distutils.sysconfig \n"); + PyRun_SimpleString("sys.path.append(distutils.sysconfig.get_python_lib(1,0)) \n"); + if (PyRun_SimpleString("from unboundmodule import *\n") < 0) + { + log_err("pythonmod: cannot initialize core module: unboundmodule.py"); + PyGILState_Release(gil); + return 0; + } + + /* Check Python file load */ + if ((script_py = fopen(pe->fname, "r")) == NULL) + { + log_err("pythonmod: can't open file %s for reading", pe->fname); + PyGILState_Release(gil); + return 0; + } + + /* Load file */ + pe->module = PyImport_AddModule("__main__"); + pe->dict = PyModule_GetDict(pe->module); + pe->data = Py_None; + Py_INCREF(pe->data); + PyModule_AddObject(pe->module, "mod_env", pe->data); + + /* TODO: deallocation of pe->... if an error occurs */ + + if (PyRun_SimpleFile(script_py, pe->fname) < 0) + { + log_err("pythonmod: can't parse Python script %s", pe->fname); + PyGILState_Release(gil); + return 0; + } + + fclose(script_py); + + if ((pe->func_init = PyDict_GetItemString(pe->dict, "init")) == NULL) + { + log_err("pythonmod: function init is missing in %s", pe->fname); + PyGILState_Release(gil); + return 0; + } + if ((pe->func_deinit = PyDict_GetItemString(pe->dict, "deinit")) == NULL) + { + log_err("pythonmod: function deinit is missing in %s", pe->fname); + PyGILState_Release(gil); + return 0; + } + if ((pe->func_operate = PyDict_GetItemString(pe->dict, "operate")) == NULL) + { + log_err("pythonmod: function operate is missing in %s", pe->fname); + PyGILState_Release(gil); + return 0; + } + if ((pe->func_inform = PyDict_GetItemString(pe->dict, "inform_super")) == NULL) + { + log_err("pythonmod: function inform_super is missing in %s", pe->fname); + PyGILState_Release(gil); + return 0; + } + + py_cfg = SWIG_NewPointerObj((void*) env->cfg, SWIGTYPE_p_config_file, 0); + res = PyObject_CallFunction(pe->func_init, "iO", id, py_cfg); + if (PyErr_Occurred()) + { + log_err("pythonmod: Exception occurred in function init"); + PyErr_Print(); + } + + Py_XDECREF(res); + Py_XDECREF(py_cfg); + PyGILState_Release(gil); + + return 1; +} + +void pythonmod_deinit(struct module_env* env, int id) +{ + struct pythonmod_env* pe = env->modinfo[id]; + if(pe == NULL) + return; + + /* Free Python resources */ + if(pe->module != NULL) + { + PyObject* res; + PyGILState_STATE gil = PyGILState_Ensure(); + + /* Deinit module */ + res = PyObject_CallFunction(pe->func_deinit, "i", id); + if (PyErr_Occurred()) { + log_err("pythonmod: Exception occurred in function deinit"); + PyErr_Print(); + } + /* Free result if any */ + Py_XDECREF(res); + /* Free shared data if any */ + Py_XDECREF(pe->data); + PyGILState_Release(gil); + + PyEval_RestoreThread(pe->mainthr); + Py_Finalize(); + pe->mainthr = NULL; + } + pe->fname = NULL; + free(pe); + + /* Module is deallocated in Python */ + env->modinfo[id] = NULL; +} + +void pythonmod_inform_super(struct module_qstate* qstate, int id, struct module_qstate* super) +{ + struct pythonmod_env* pe = (struct pythonmod_env*)qstate->env->modinfo[id]; + struct pythonmod_qstate* pq = (struct pythonmod_qstate*)qstate->minfo[id]; + PyObject* py_qstate, *py_sqstate, *res; + PyGILState_STATE gil = PyGILState_Ensure(); + + log_query_info(VERB_ALGO, "pythonmod: inform_super, sub is", &qstate->qinfo); + log_query_info(VERB_ALGO, "super is", &super->qinfo); + + py_qstate = SWIG_NewPointerObj((void*) qstate, SWIGTYPE_p_module_qstate, 0); + py_sqstate = SWIG_NewPointerObj((void*) super, SWIGTYPE_p_module_qstate, 0); + + res = PyObject_CallFunction(pe->func_inform, "iOOO", id, py_qstate, + py_sqstate, pq->data); + + if (PyErr_Occurred()) + { + log_err("pythonmod: Exception occurred in function inform_super"); + PyErr_Print(); + qstate->ext_state[id] = module_error; + } + else if ((res == NULL) || (!PyObject_IsTrue(res))) + { + log_err("pythonmod: python returned bad code in inform_super"); + qstate->ext_state[id] = module_error; + } + + Py_XDECREF(res); + Py_XDECREF(py_sqstate); + Py_XDECREF(py_qstate); + + PyGILState_Release(gil); +} + +void pythonmod_operate(struct module_qstate* qstate, enum module_ev event, + int id, struct outbound_entry* ATTR_UNUSED(outbound)) +{ + struct pythonmod_env* pe = (struct pythonmod_env*)qstate->env->modinfo[id]; + struct pythonmod_qstate* pq = (struct pythonmod_qstate*)qstate->minfo[id]; + PyObject* py_qstate, *res; + PyGILState_STATE gil = PyGILState_Ensure(); + + if ( pq == NULL) + { + /* create qstate */ + pq = qstate->minfo[id] = malloc(sizeof(struct pythonmod_qstate)); + + /* Initialize per query data */ + pq->data = Py_None; + Py_INCREF(pq->data); + } + + /* Call operate */ + py_qstate = SWIG_NewPointerObj((void*) qstate, SWIGTYPE_p_module_qstate, 0); + res = PyObject_CallFunction(pe->func_operate, "iiOO", id, (int) event, + py_qstate, pq->data); + if (PyErr_Occurred()) + { + log_err("pythonmod: Exception occurred in function operate, event: %s", strmodulevent(event)); + PyErr_Print(); + qstate->ext_state[id] = module_error; + } + else if ((res == NULL) || (!PyObject_IsTrue(res))) + { + log_err("pythonmod: python returned bad code, event: %s", strmodulevent(event)); + qstate->ext_state[id] = module_error; + } + Py_XDECREF(res); + Py_XDECREF(py_qstate); + + PyGILState_Release(gil); +} + +void pythonmod_clear(struct module_qstate* qstate, int id) +{ + struct pythonmod_qstate* pq; + if (qstate == NULL) + return; + + pq = (struct pythonmod_qstate*)qstate->minfo[id]; + verbose(VERB_ALGO, "pythonmod: clear, id: %d, pq:%lX", id, + (unsigned long int)pq); + if(pq != NULL) + { + PyGILState_STATE gil = PyGILState_Ensure(); + Py_DECREF(pq->data); + PyGILState_Release(gil); + /* Free qstate */ + free(pq); + } + + qstate->minfo[id] = NULL; +} + +size_t pythonmod_get_mem(struct module_env* env, int id) +{ + struct pythonmod_env* pe = (struct pythonmod_env*)env->modinfo[id]; + verbose(VERB_ALGO, "pythonmod: get_mem, id: %d, pe:%lX", id, + (unsigned long int)pe); + if(!pe) + return 0; + return sizeof(*pe); +} + +/** + * The module function block + */ +static struct module_func_block pythonmod_block = { + "python", + &pythonmod_init, &pythonmod_deinit, &pythonmod_operate, &pythonmod_inform_super, + &pythonmod_clear, &pythonmod_get_mem +}; + +struct module_func_block* pythonmod_get_funcblock(void) +{ + return &pythonmod_block; +} diff --git a/usr.sbin/unbound/pythonmod/pythonmod.h b/usr.sbin/unbound/pythonmod/pythonmod.h new file mode 100644 index 00000000000..b108cf9234c --- /dev/null +++ b/usr.sbin/unbound/pythonmod/pythonmod.h @@ -0,0 +1,68 @@ +/* + * pythonmod.h: module header file + * + * 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. + */ +/** + * \file + * Python module for unbound. Calls python script. + */ +#ifndef PYTHONMOD_H +#define PYTHONMOD_H +#include "util/module.h" +#include "services/outbound_list.h" + +/** + * Get the module function block. + * @return: function block with function pointers to module methods. + */ +struct module_func_block* pythonmod_get_funcblock(void); + +/** python module init */ +int pythonmod_init(struct module_env* env, int id); + +/** python module deinit */ +void pythonmod_deinit(struct module_env* env, int id); + +/** python module operate on a query */ +void pythonmod_operate(struct module_qstate* qstate, enum module_ev event, int id, struct outbound_entry* outbound); + +/** python module */ +void pythonmod_inform_super(struct module_qstate* qstate, int id, struct module_qstate* super); + +/** python module cleanup query state */ +void pythonmod_clear(struct module_qstate* qstate, int id); + +/** python module alloc size routine */ +size_t pythonmod_get_mem(struct module_env* env, int id); +#endif /* PYTHONMOD_H */ diff --git a/usr.sbin/unbound/pythonmod/pythonmod_utils.c b/usr.sbin/unbound/pythonmod/pythonmod_utils.c new file mode 100644 index 00000000000..52cfad0a6f9 --- /dev/null +++ b/usr.sbin/unbound/pythonmod/pythonmod_utils.c @@ -0,0 +1,177 @@ +/* + * pythonmod_utils.c: utilities used by wrapper + * + * 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. + */ +/** + * \file + * Utility functions for the python module that perform stores and loads and + * conversions. + */ +#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" +#include "util/data/msgparse.h" +#include "util/data/msgreply.h" +#include "util/storage/slabhash.h" +#include "util/regional.h" + +#undef _POSIX_C_SOURCE +#undef _XOPEN_SOURCE +#include <Python.h> + +/* 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*/ + { + PyErr_SetString(PyExc_ValueError, "Authoritative answer can't be stored"); + return 0; + } + + return dns_cache_store(qstate->env, qinfo, msgrep, is_referral, + qstate->prefetch_leeway, NULL); +} + +/* Invalidate the message associated with query_info stored in message cache */ +void invalidateQueryInCache(struct module_qstate* qstate, struct query_info* qinfo) +{ + hashvalue_t h; + struct lruhash_entry* e; + struct reply_info *r; + size_t i, j; + + 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; + if(rrset_array_lock(r->ref, r->rrset_count, *qstate->env->now)) { + for(i=0; i< r->rrset_count; i++) + { + struct packed_rrset_data* data = + (struct packed_rrset_data*) r->ref[i].key->entry.data; + if(i>0 && r->ref[i].key == r->ref[i-1].key) + continue; + + data->ttl = r->ttl; + for(j=0; j<data->count + data->rrsig_count; j++) + data->rr_ttl[j] = r->ttl; + } + rrset_array_unlock(r->ref, r->rrset_count); + } + } + lock_rw_unlock(&e->lock); + } else { + log_info("invalidateQueryInCache: qinfo is not in cache"); + } +} + +/* Create response according to the ldns packet content */ +int createResponse(struct module_qstate* qstate, ldns_buffer* pkt) +{ + struct msg_parse* prs; + struct edns_data edns; + + /* 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; + } + + memset(prs, 0, sizeof(*prs)); + memset(&edns, 0, sizeof(edns)); + + 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; + } + /* edns is not examined, but removed from message to help cache */ + if(parse_extract_edns(prs, &edns) != LDNS_RCODE_NOERROR) + return 0; + + /* remove CD-bit, we asked for in case we handle validation ourself */ + prs->flags &= ~BIT_CD; + + /* allocate response dns_msg in region */ + qstate->return_msg = (struct dns_msg*)regional_alloc(qstate->region, sizeof(struct dns_msg)); + if (!qstate->return_msg) + return 0; + + 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; + } + + /* 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; */ + + /* Clear the AA flag */ + /* FIXME: does this action go here or in some other module? */ + /*qstate->return_msg->rep->flags &= ~BIT_AA; */ + + /* make sure QR flag is on */ + /*qstate->return_msg->rep->flags |= BIT_QR; */ + + if(verbosity >= VERB_ALGO) + 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; +} diff --git a/usr.sbin/unbound/pythonmod/pythonmod_utils.h b/usr.sbin/unbound/pythonmod/pythonmod_utils.h new file mode 100644 index 00000000000..a1641d30858 --- /dev/null +++ b/usr.sbin/unbound/pythonmod/pythonmod_utils.h @@ -0,0 +1,89 @@ +/* + * pythonmod_utils.h: utils header file + * + * 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. + */ +/** + * \file + * Utility functions for the python module that perform stores and loads and + * conversions. + */ +#ifndef PYTHONMOD_UTILS_H +#define PYTHONMOD_UTILS_H + +#include "util/module.h" + +/** + * Store the reply_info and query_info pair in message cache (qstate->msg_cache) + * + * @param qstate: module environment + * @param qinfo: query info, the query for which answer is stored. + * @param msgrep: reply in dns_msg + * @param is_referral: If true, then the given message to be stored is a + * referral. The cache implementation may use this as a hint. + * It will store only the RRsets, not the message. + * @return 0 on alloc error (out of memory). + */ +int storeQueryInCache(struct module_qstate* qstate, struct query_info* qinfo, struct reply_info* msgrep, int is_referral); + + +/** + * Invalidate the message associated with query_info stored in message cache. + * + * This function invalidates the record in message cache associated with the given query only if such a record exists. + * + * @param qstate: module environment + * @param qinfo: query info, the query for which answer is stored. + */ +void invalidateQueryInCache(struct module_qstate* qstate, struct query_info* qinfo); + +/** + * Create response according to the ldns packet content + * + * This function fills qstate.return_msg up with data of a given packet + * + * @param qstate: module environment + * @param pkt: a ldns_buffer which contains ldns_packet data + * @return 0 on failure, out of memory or parse error. + */ +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 */ diff --git a/usr.sbin/unbound/pythonmod/test-calc.conf b/usr.sbin/unbound/pythonmod/test-calc.conf new file mode 100644 index 00000000000..ef854ce1bf6 --- /dev/null +++ b/usr.sbin/unbound/pythonmod/test-calc.conf @@ -0,0 +1,18 @@ +# Example configuration file for calc.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/calc.py" + diff --git a/usr.sbin/unbound/pythonmod/test-dict.conf b/usr.sbin/unbound/pythonmod/test-dict.conf new file mode 100644 index 00000000000..daab7250e5d --- /dev/null +++ b/usr.sbin/unbound/pythonmod/test-dict.conf @@ -0,0 +1,18 @@ +# Example configuration file for dict.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/dict.py" + diff --git a/usr.sbin/unbound/pythonmod/test-log.conf b/usr.sbin/unbound/pythonmod/test-log.conf new file mode 100644 index 00000000000..02214ba1d63 --- /dev/null +++ b/usr.sbin/unbound/pythonmod/test-log.conf @@ -0,0 +1,17 @@ +# Example configuration file for log.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/log.py" diff --git a/usr.sbin/unbound/pythonmod/test-resgen.conf b/usr.sbin/unbound/pythonmod/test-resgen.conf new file mode 100644 index 00000000000..a0a79e42dfe --- /dev/null +++ b/usr.sbin/unbound/pythonmod/test-resgen.conf @@ -0,0 +1,18 @@ +# 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 python iterator" + +# Python config section +python: + # Script file to load + python-script: "./examples/resgen.py" + diff --git a/usr.sbin/unbound/pythonmod/test-resip.conf b/usr.sbin/unbound/pythonmod/test-resip.conf new file mode 100644 index 00000000000..7620f736fe3 --- /dev/null +++ b/usr.sbin/unbound/pythonmod/test-resip.conf @@ -0,0 +1,18 @@ +# 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" + diff --git a/usr.sbin/unbound/pythonmod/test-resmod.conf b/usr.sbin/unbound/pythonmod/test-resmod.conf new file mode 100644 index 00000000000..8de5fd257fa --- /dev/null +++ b/usr.sbin/unbound/pythonmod/test-resmod.conf @@ -0,0 +1,19 @@ +# Example configuration file for resmod.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: "python iterator" + module-config: "validator python iterator" + +# Python config section +python: + # Script file to load + python-script: "./examples/resmod.py" + diff --git a/usr.sbin/unbound/pythonmod/ubmodule-msg.py b/usr.sbin/unbound/pythonmod/ubmodule-msg.py new file mode 100644 index 00000000000..648368080c0 --- /dev/null +++ b/usr.sbin/unbound/pythonmod/ubmodule-msg.py @@ -0,0 +1,156 @@ +# -*- coding: utf-8 -*- +''' + ubmodule-msg.py: simple response packet logger + + Authors: Zdenek Vasicek (vasicek AT fit.vutbr.cz) + Marek Vavrusa (xvavru00 AT stud.fit.vutbr.cz) + + Copyright (c) 2008. All rights reserved. + + 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. + + 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. +''' +import os + +def init(id, cfg): + log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, cfg.python_script)) + return True + +def deinit(id): + log_info("pythonmod: deinit called, module id is %d" % id) + return True + +def inform_super(id, qstate, superqstate, qdata): + return True + +def setTTL(qstate, ttl): + """Sets return_msg TTL and all the RRs TTL""" + if qstate.return_msg: + qstate.return_msg.rep.ttl = ttl + if (qstate.return_msg.rep): + for i in range(0,qstate.return_msg.rep.rrset_count): + d = qstate.return_msg.rep.rrsets[i].entry.data + for j in range(0,d.count+d.rrsig_count): + d.rr_ttl[j] = ttl + +def dataHex(data, prefix=""): + res = "" + for i in range(0, (len(data)+15)/16): + res += "%s0x%02X | " % (prefix, i*16) + d = map(lambda x:ord(x), data[i*16:i*16+17]) + for ch in d: + res += "%02X " % ch + for i in range(0,17-len(d)): + res += " " + res += "| " + for ch in d: + if (ch < 32) or (ch > 127): + res += ". " + else: + res += "%c " % ch + res += "\n" + return res + +def printReturnMsg(qstate): + print "Return MSG rep :: flags: %04X, QDcount: %d, Security:%d, TTL=%d" % (qstate.return_msg.rep.flags, qstate.return_msg.rep.qdcount,qstate.return_msg.rep.security, qstate.return_msg.rep.ttl) + print " qinfo :: qname:",qstate.return_msg.qinfo.qname_list, qstate.return_msg.qinfo.qname_str, "type:",qstate.return_msg.qinfo.qtype_str, "class:",qstate.return_msg.qinfo.qclass_str + if (qstate.return_msg.rep): + print "RRSets:",qstate.return_msg.rep.rrset_count + prevkey = None + for i in range(0,qstate.return_msg.rep.rrset_count): + r = qstate.return_msg.rep.rrsets[i] + rk = r.rk + print i,":",rk.dname_list, rk.dname_str, "flags: %04X" % rk.flags, + print "type:",rk.type_str,"(%d)" % ntohs(rk.type), "class:",rk.rrset_class_str,"(%d)" % ntohs(rk.rrset_class) + + d = r.entry.data + print " RRDatas:",d.count+d.rrsig_count + for j in range(0,d.count+d.rrsig_count): + print " ",j,":","TTL=",d.rr_ttl[j],"RR data:" + print dataHex(d.rr_data[j]," ") + + +def operate(id, event, qstate, qdata): + log_info("pythonmod: operate called, id: %d, event:%s" % (id, strmodulevent(event))) + #print "pythonmod: per query data", qdata + + print "Query:", ''.join(map(lambda x:chr(max(32,ord(x))),qstate.qinfo.qname)), qstate.qinfo.qname_list, qstate.qinfo.qname_str, + print "Type:",qstate.qinfo.qtype_str,"(%d)" % qstate.qinfo.qtype, + print "Class:",qstate.qinfo.qclass_str,"(%d)" % qstate.qinfo.qclass + print + + #if event == MODULE_EVENT_PASS: #pokud mame "validator python iterator" + if (event == MODULE_EVENT_NEW) and (qstate.qinfo.qname_str.endswith(".seznam.cz.")): #pokud mame "python validator iterator" + print qstate.qinfo.qname_str + + qstate.ext_state[id] = MODULE_FINISHED + + msg = DNSMessage(qstate.qinfo.qname_str, RR_TYPE_A, RR_CLASS_IN, PKT_QR | PKT_RA | PKT_AA) #, 300) + #msg.authority.append("xxx.seznam.cz. 10 IN A 192.168.1.1") + #msg.additional.append("yyy.seznam.cz. 10 IN A 1.1.1.2.") + + if qstate.qinfo.qtype == RR_TYPE_A: + msg.answer.append("%s 10 IN A 192.168.1.1" % qstate.qinfo.qname_str) + if (qstate.qinfo.qtype == RR_TYPE_SRV) or (qstate.qinfo.qtype == RR_TYPE_ANY): + msg.answer.append("%s 10 IN SRV 0 0 80 neinfo.example.com." % qstate.qinfo.qname_str) + if (qstate.qinfo.qtype == RR_TYPE_TXT) or (qstate.qinfo.qtype == RR_TYPE_ANY): + msg.answer.append("%s 10 IN TXT path=/" % qstate.qinfo.qname_str) + + if not msg.set_return_msg(qstate): + qstate.ext_state[id] = MODULE_ERROR + return True + + #qstate.return_msg.rep.security = 2 #pokud nebude nasledovat validator, je zapotrebi nastavit security, aby nebyl paket zahozen v mesh_send_reply + printReturnMsg(qstate) + + #Authoritative result can't be stored in cache + #if (not storeQueryInCache(qstate, qstate.return_msg.qinfo, qstate.return_msg.rep, 0)): + # print "Can't store in cache" + # qstate.ext_state[id] = MODULE_ERROR + # return False + #print "Store OK" + + qstate.return_rcode = RCODE_NOERROR + return True + + if event == MODULE_EVENT_NEW: + qstate.ext_state[id] = MODULE_WAIT_MODULE + return True + + if event == MODULE_EVENT_MODDONE: + log_info("pythonmod: previous module done") + qstate.ext_state[id] = MODULE_FINISHED + return True + + if event == MODULE_EVENT_PASS: + log_info("pythonmod: event_pass") + qstate.ext_state[id] = MODULE_WAIT_MODULE + return True + + log_err("pythonmod: BAD event") + qstate.ext_state[id] = MODULE_ERROR + return True + +log_info("pythonmod: script loaded.") diff --git a/usr.sbin/unbound/pythonmod/ubmodule-tst.py b/usr.sbin/unbound/pythonmod/ubmodule-tst.py new file mode 100644 index 00000000000..0b9b5a9d2cf --- /dev/null +++ b/usr.sbin/unbound/pythonmod/ubmodule-tst.py @@ -0,0 +1,149 @@ +# -*- coding: utf-8 -*- +''' + ubmodule-tst.py: + + Authors: Zdenek Vasicek (vasicek AT fit.vutbr.cz) + Marek Vavrusa (xvavru00 AT stud.fit.vutbr.cz) + + Copyright (c) 2008. All rights reserved. + + 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. + + 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. +''' +def init(id, cfg): + log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, cfg.python_script)) + return True + +def deinit(id): + log_info("pythonmod: deinit called, module id is %d" % id) + return True + +def inform_super(id, qstate, superqstate, qdata): + return True + +def setTTL(qstate, ttl): + """Sets return_msg TTL and all the RRs TTL""" + if qstate.return_msg: + qstate.return_msg.rep.ttl = ttl + if (qstate.return_msg.rep): + for i in range(0,qstate.return_msg.rep.rrset_count): + d = qstate.return_msg.rep.rrsets[i].entry.data + for j in range(0,d.count+d.rrsig_count): + d.rr_ttl[j] = ttl + +def dataHex(data, prefix=""): + res = "" + for i in range(0, (len(data)+15)/16): + res += "%s0x%02X | " % (prefix, i*16) + d = map(lambda x:ord(x), data[i*16:i*16+17]) + for ch in d: + res += "%02X " % ch + for i in range(0,17-len(d)): + res += " " + res += "| " + for ch in d: + if (ch < 32) or (ch > 127): + res += ". " + else: + res += "%c " % ch + res += "\n" + return res + +def printReturnMsg(qstate): + print "Return MSG rep :: flags: %04X, QDcount: %d, Security:%d, TTL=%d" % (qstate.return_msg.rep.flags, qstate.return_msg.rep.qdcount,qstate.return_msg.rep.security, qstate.return_msg.rep.ttl) + print " qinfo :: qname:",qstate.return_msg.qinfo.qname_list, qstate.return_msg.qinfo.qname_str, "type:",qstate.return_msg.qinfo.qtype_str, "class:",qstate.return_msg.qinfo.qclass_str + if (qstate.return_msg.rep): + print "RRSets:",qstate.return_msg.rep.rrset_count + prevkey = None + for i in range(0,qstate.return_msg.rep.rrset_count): + r = qstate.return_msg.rep.rrsets[i] + rk = r.rk + print i,":",rk.dname_list, rk.dname_str, "flags: %04X" % rk.flags, + print "type:",rk.type_str,"(%d)" % ntohs(rk.type), "class:",rk.rrset_class_str,"(%d)" % ntohs(rk.rrset_class) + + d = r.entry.data + print " RRDatas:",d.count+d.rrsig_count + for j in range(0,d.count+d.rrsig_count): + print " ",j,":","TTL=",d.rr_ttl[j],"RR data:" + print dataHex(d.rr_data[j]," ") + +def operate(id, event, qstate, qdata): + log_info("pythonmod: operate called, id: %d, event:%s" % (id, strmodulevent(event))) + #print "pythonmod: per query data", qdata + + print "Query:", ''.join(map(lambda x:chr(max(32,ord(x))),qstate.qinfo.qname)), qstate.qinfo.qname_list, + print "Type:",qstate.qinfo.qtype_str,"(%d)" % qstate.qinfo.qtype, + print "Class:",qstate.qinfo.qclass_str,"(%d)" % qstate.qinfo.qclass + print + + # TEST: + # > dig @127.0.0.1 www.seznam.cz A + # > dig @127.0.0.1 3.76.75.77.in-addr.arpa. PTR + # prvni dva dotazy vrati TTL 100 + # > dig @127.0.0.1 www.seznam.cz A + # > dig @127.0.0.1 3.76.75.77.in-addr.arpa. PTR + # dalsi dva dotazy vrati TTL 10, ktere se bude s dalsimi dotazy snizovat, nez vyprsi a znovu se zaktivuje mesh + + if qstate.return_msg: + printReturnMsg(qstate) + + #qdn = '.'.join(qstate.qinfo.qname_list) + qdn = qstate.qinfo.qname_str + + #Pokud dotaz konci na nasledujici, pozmenime TTL zpravy, ktera se posle klientovi (return_msg) i zpravy v CACHE + if qdn.endswith(".seznam.cz.") or qdn.endswith('.in-addr.arpa.'): + #pokud je v cache odpoved z iteratoru, pak ji zneplatnime, jinak se moduly nazavolaji do te doby, nez vyprsi TTL + invalidateQueryInCache(qstate, qstate.return_msg.qinfo) + + if (qstate.return_msg.rep.authoritative): + print "X"*300 + + setTTL(qstate, 10) #do cache nastavime TTL na 10 + if not storeQueryInCache(qstate, qstate.return_msg.qinfo, qstate.return_msg.rep, 0): + qstate.ext_state[id] = MODULE_ERROR + return False + + setTTL(qstate, 100) #odpoved klientovi prijde s TTL 100 + qstate.return_rcode = RCODE_NOERROR + + if event == MODULE_EVENT_NEW: + qstate.ext_state[id] = MODULE_WAIT_MODULE + return True + + if event == MODULE_EVENT_MODDONE: + log_info("pythonmod: previous module done") + qstate.ext_state[id] = MODULE_FINISHED + return True + + if event == MODULE_EVENT_PASS: + log_info("pythonmod: event_pass") + qstate.ext_state[id] = MODULE_WAIT_MODULE + return True + + log_err("pythonmod: BAD event") + qstate.ext_state[id] = MODULE_ERROR + return True + +log_info("pythonmod: script loaded.") |