952 lines
34 KiB
OpenEdge ABL
952 lines
34 KiB
OpenEdge ABL
/*
|
|
* libounbound.i: pyUnbound module (libunbound wrapper for Python)
|
|
*
|
|
* 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.
|
|
*/
|
|
%module unbound
|
|
%{
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include "libunbound/unbound.h"
|
|
%}
|
|
|
|
%pythoncode %{
|
|
import encodings.idna
|
|
|
|
# Ensure compatibility with older python versions
|
|
if 'bytes' not in vars():
|
|
bytes = str
|
|
|
|
def ord(s):
|
|
if isinstance(s, int):
|
|
return s
|
|
return __builtins__.ord(s)
|
|
%}
|
|
|
|
//%include "doc.i"
|
|
%include "file.i"
|
|
|
|
%feature("docstring") strerror "Convert error value to a human readable string."
|
|
|
|
// ================================================================================
|
|
// ub_resolve - perform resolution and validation
|
|
// ================================================================================
|
|
%typemap(in,numinputs=0,noblock=1) (struct ub_result** result)
|
|
{
|
|
struct ub_result* newubr;
|
|
$1 = &newubr;
|
|
}
|
|
|
|
/* result generation */
|
|
%typemap(argout,noblock=1) (struct ub_result** result)
|
|
{
|
|
if(1) { /* new code block for variable on stack */
|
|
PyObject* tuple;
|
|
tuple = PyTuple_New(2);
|
|
PyTuple_SetItem(tuple, 0, $result);
|
|
if (result == 0) {
|
|
PyTuple_SetItem(tuple, 1, SWIG_NewPointerObj(SWIG_as_voidptr(newubr), SWIGTYPE_p_ub_result, SWIG_POINTER_OWN | 0 ));
|
|
} else {
|
|
PyTuple_SetItem(tuple, 1, Py_None);
|
|
}
|
|
$result = tuple;
|
|
}
|
|
}
|
|
|
|
|
|
// ================================================================================
|
|
// ub_ctx - validation context
|
|
// ================================================================================
|
|
%nodefaultctor ub_ctx; //no default constructor & destructor
|
|
%nodefaultdtor ub_ctx;
|
|
|
|
%newobject ub_ctx_create;
|
|
%delobject ub_ctx_delete;
|
|
%rename(_ub_ctx_delete) ub_ctx_delete;
|
|
|
|
%newobject ub_resolve;
|
|
|
|
%inline %{
|
|
void ub_ctx_free_dbg (struct ub_ctx* c) {
|
|
printf("******** UB_CTX free 0x%lX ************\n", (long unsigned int)c);
|
|
ub_ctx_delete(c);
|
|
}
|
|
|
|
//RR types
|
|
enum enum_rr_type
|
|
{
|
|
/** a host address */
|
|
RR_TYPE_A = 1,
|
|
/** an authoritative name server */
|
|
RR_TYPE_NS = 2,
|
|
/** a mail destination (Obsolete - use MX) */
|
|
RR_TYPE_MD = 3,
|
|
/** a mail forwarder (Obsolete - use MX) */
|
|
RR_TYPE_MF = 4,
|
|
/** the canonical name for an alias */
|
|
RR_TYPE_CNAME = 5,
|
|
/** marks the start of a zone of authority */
|
|
RR_TYPE_SOA = 6,
|
|
/** a mailbox domain name (EXPERIMENTAL) */
|
|
RR_TYPE_MB = 7,
|
|
/** a mail group member (EXPERIMENTAL) */
|
|
RR_TYPE_MG = 8,
|
|
/** a mail rename domain name (EXPERIMENTAL) */
|
|
RR_TYPE_MR = 9,
|
|
/** a null RR (EXPERIMENTAL) */
|
|
RR_TYPE_NULL = 10,
|
|
/** a well known service description */
|
|
RR_TYPE_WKS = 11,
|
|
/** a domain name pointer */
|
|
RR_TYPE_PTR = 12,
|
|
/** host information */
|
|
RR_TYPE_HINFO = 13,
|
|
/** mailbox or mail list information */
|
|
RR_TYPE_MINFO = 14,
|
|
/** mail exchange */
|
|
RR_TYPE_MX = 15,
|
|
/** text strings */
|
|
RR_TYPE_TXT = 16,
|
|
/** RFC1183 */
|
|
RR_TYPE_RP = 17,
|
|
/** RFC1183 */
|
|
RR_TYPE_AFSDB = 18,
|
|
/** RFC1183 */
|
|
RR_TYPE_X25 = 19,
|
|
/** RFC1183 */
|
|
RR_TYPE_ISDN = 20,
|
|
/** RFC1183 */
|
|
RR_TYPE_RT = 21,
|
|
/** RFC1706 */
|
|
RR_TYPE_NSAP = 22,
|
|
/** RFC1348 */
|
|
RR_TYPE_NSAP_PTR = 23,
|
|
/** 2535typecode */
|
|
RR_TYPE_SIG = 24,
|
|
/** 2535typecode */
|
|
RR_TYPE_KEY = 25,
|
|
/** RFC2163 */
|
|
RR_TYPE_PX = 26,
|
|
/** RFC1712 */
|
|
RR_TYPE_GPOS = 27,
|
|
/** ipv6 address */
|
|
RR_TYPE_AAAA = 28,
|
|
/** LOC record RFC1876 */
|
|
RR_TYPE_LOC = 29,
|
|
/** 2535typecode */
|
|
RR_TYPE_NXT = 30,
|
|
/** draft-ietf-nimrod-dns-01.txt */
|
|
RR_TYPE_EID = 31,
|
|
/** draft-ietf-nimrod-dns-01.txt */
|
|
RR_TYPE_NIMLOC = 32,
|
|
/** SRV record RFC2782 */
|
|
RR_TYPE_SRV = 33,
|
|
/** http://www.jhsoft.com/rfc/af-saa-0069.000.rtf */
|
|
RR_TYPE_ATMA = 34,
|
|
/** RFC2915 */
|
|
RR_TYPE_NAPTR = 35,
|
|
/** RFC2230 */
|
|
RR_TYPE_KX = 36,
|
|
/** RFC2538 */
|
|
RR_TYPE_CERT = 37,
|
|
/** RFC2874 */
|
|
RR_TYPE_A6 = 38,
|
|
/** RFC2672 */
|
|
RR_TYPE_DNAME = 39,
|
|
/** dnsind-kitchen-sink-02.txt */
|
|
RR_TYPE_SINK = 40,
|
|
/** Pseudo OPT record... */
|
|
RR_TYPE_OPT = 41,
|
|
/** RFC3123 */
|
|
RR_TYPE_APL = 42,
|
|
/** draft-ietf-dnsext-delegation */
|
|
RR_TYPE_DS = 43,
|
|
/** SSH Key Fingerprint */
|
|
RR_TYPE_SSHFP = 44,
|
|
/** draft-richardson-ipseckey-rr-11.txt */
|
|
RR_TYPE_IPSECKEY = 45,
|
|
/** draft-ietf-dnsext-dnssec-25 */
|
|
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,
|
|
/** A request for mailbox-related records (MB, MG or MR) */
|
|
RR_TYPE_MAILB = 253,
|
|
/** A request for mail agent RRs (Obsolete - see MX) */
|
|
RR_TYPE_MAILA = 254,
|
|
/** any type (wildcard) */
|
|
RR_TYPE_ANY = 255,
|
|
|
|
/* RFC 4431, 5074, DNSSEC Lookaside Validation */
|
|
RR_TYPE_DLV = 32769,
|
|
};
|
|
|
|
// RR classes
|
|
enum enum_rr_class
|
|
{
|
|
/** the Internet */
|
|
RR_CLASS_IN = 1,
|
|
/** Chaos class */
|
|
RR_CLASS_CH = 3,
|
|
/** Hesiod (Dyer 87) */
|
|
RR_CLASS_HS = 4,
|
|
/** None class, dynamic update */
|
|
RR_CLASS_NONE = 254,
|
|
/** Any class */
|
|
RR_CLASS_ANY = 255,
|
|
};
|
|
%}
|
|
|
|
%feature("docstring") ub_ctx "Unbound resolving and validation context.
|
|
|
|
The validation context is created to hold the resolver status, validation keys and a small cache (containing messages, rrsets, roundtrip times, trusted keys, lameness information).
|
|
|
|
**Usage**
|
|
|
|
>>> import unbound
|
|
>>> ctx = unbound.ub_ctx()
|
|
>>> ctx.resolvconf(\"/etc/resolv.conf\")
|
|
>>> status, result = ctx.resolve(\"www.google.com\", unbound.RR_TYPE_A, unbound.RR_CLASS_IN)
|
|
>>> if status==0 and result.havedata:
|
|
>>> print \"Result:\",result.data.address_list
|
|
Result: ['74.125.43.147', '74.125.43.99', '74.125.43.103', '74.125.43.104']
|
|
"
|
|
|
|
%extend ub_ctx
|
|
{
|
|
%pythoncode %{
|
|
def __init__(self):
|
|
"""Creates a resolving and validation context.
|
|
|
|
An exception is invoked if the process of creation an ub_ctx instance fails.
|
|
"""
|
|
self.this = _unbound.ub_ctx_create()
|
|
if not self.this:
|
|
raise Exception("Fatal error: unbound context initialization failed")
|
|
|
|
#__swig_destroy__ = _unbound.ub_ctx_free_dbg
|
|
__swig_destroy__ = _unbound._ub_ctx_delete
|
|
|
|
#UB_CTX_METHODS_#
|
|
def add_ta(self,ta):
|
|
"""Add a trust anchor to the given context.
|
|
|
|
The trust anchor is a string, on one line, that holds a valid DNSKEY or DS RR.
|
|
|
|
:param ta:
|
|
string, with zone-format RR on one line. [domainname] [TTL optional] [type] [class optional] [rdata contents]
|
|
:returns: (int) 0 if OK, else error.
|
|
"""
|
|
return _unbound.ub_ctx_add_ta(self,ta)
|
|
#parameters: struct ub_ctx *,char *,
|
|
#retvals: int
|
|
|
|
def add_ta_file(self,fname):
|
|
"""Add trust anchors to the given context.
|
|
|
|
Pass name of a file with DS and DNSKEY records (like from dig or drill).
|
|
|
|
:param fname:
|
|
filename of file with keyfile with trust anchors.
|
|
:returns: (int) 0 if OK, else error.
|
|
"""
|
|
return _unbound.ub_ctx_add_ta_file(self,fname)
|
|
#parameters: struct ub_ctx *,char *,
|
|
#retvals: int
|
|
|
|
def config(self,fname):
|
|
"""setup configuration for the given context.
|
|
|
|
:param fname:
|
|
unbound config file (not all settings applicable). This is a power-users interface that lets you specify all sorts of options. For some specific options, such as adding trust anchors, special routines exist.
|
|
:returns: (int) 0 if OK, else error.
|
|
"""
|
|
return _unbound.ub_ctx_config(self,fname)
|
|
#parameters: struct ub_ctx *,char *,
|
|
#retvals: int
|
|
|
|
def debuglevel(self,d):
|
|
"""Set debug verbosity for the context Output is directed to stderr.
|
|
|
|
:param d:
|
|
debug level, 0 is off, 1 is very minimal, 2 is detailed, and 3 is lots.
|
|
:returns: (int) 0 if OK, else error.
|
|
"""
|
|
return _unbound.ub_ctx_debuglevel(self,d)
|
|
#parameters: struct ub_ctx *,int,
|
|
#retvals: int
|
|
|
|
def debugout(self,out):
|
|
"""Set debug output (and error output) to the specified stream.
|
|
|
|
Pass None to disable. Default is stderr.
|
|
|
|
:param out:
|
|
File stream to log to.
|
|
:returns: (int) 0 if OK, else error.
|
|
|
|
**Usage:**
|
|
|
|
In order to log into file, use
|
|
|
|
::
|
|
|
|
ctx = unbound.ub_ctx()
|
|
fw = fopen("debug.log")
|
|
ctx.debuglevel(3)
|
|
ctx.debugout(fw)
|
|
|
|
Another option is to print the debug informations to stderr output
|
|
|
|
::
|
|
|
|
ctx = unbound.ub_ctx()
|
|
ctx.debuglevel(10)
|
|
ctx.debugout(sys.stderr)
|
|
"""
|
|
return _unbound.ub_ctx_debugout(self,out)
|
|
#parameters: struct ub_ctx *,void *,
|
|
#retvals: int
|
|
|
|
def hosts(self,fname="/etc/hosts"):
|
|
"""Read list of hosts from the filename given.
|
|
|
|
Usually "/etc/hosts". These addresses are not flagged as DNSSEC secure when queried for.
|
|
|
|
:param fname:
|
|
file name string. If None "/etc/hosts" is used.
|
|
:returns: (int) 0 if OK, else error.
|
|
"""
|
|
return _unbound.ub_ctx_hosts(self,fname)
|
|
#parameters: struct ub_ctx *,char *,
|
|
#retvals: int
|
|
|
|
def print_local_zones(self):
|
|
"""Print the local zones and their content (RR data) to the debug output.
|
|
|
|
:returns: (int) 0 if OK, else error.
|
|
"""
|
|
return _unbound.ub_ctx_print_local_zones(self)
|
|
#parameters: struct ub_ctx *,
|
|
#retvals: int
|
|
|
|
def resolvconf(self,fname="/etc/resolv.conf"):
|
|
"""Read list of nameservers to use from the filename given.
|
|
|
|
Usually "/etc/resolv.conf". Uses those nameservers as caching proxies. If they do not support DNSSEC, validation may fail.
|
|
|
|
Only nameservers are picked up, the searchdomain, ndots and other settings from resolv.conf(5) are ignored.
|
|
|
|
:param fname:
|
|
file name string. If None "/etc/resolv.conf" is used.
|
|
:returns: (int) 0 if OK, else error.
|
|
"""
|
|
return _unbound.ub_ctx_resolvconf(self,fname)
|
|
#parameters: struct ub_ctx *,char *,
|
|
#retvals: int
|
|
|
|
def set_async(self,dothread):
|
|
"""Set a context behaviour for asynchronous action.
|
|
|
|
:param dothread:
|
|
if True, enables threading and a call to :meth:`resolve_async` creates a thread to handle work in the background.
|
|
If False, a process is forked to handle work in the background.
|
|
Changes to this setting after :meth:`async` calls have been made have no effect (delete and re-create the context to change).
|
|
:returns: (int) 0 if OK, else error.
|
|
"""
|
|
return _unbound.ub_ctx_async(self,dothread)
|
|
#parameters: struct ub_ctx *,int,
|
|
#retvals: int
|
|
|
|
def set_fwd(self,addr):
|
|
"""Set machine to forward DNS queries to, the caching resolver to use.
|
|
|
|
IP4 or IP6 address. Forwards all DNS requests to that machine, which is expected to run a recursive resolver. If the is not DNSSEC-capable, validation may fail. Can be called several times, in that case the addresses are used as backup servers.
|
|
|
|
To read the list of nameservers from /etc/resolv.conf (from DHCP or so), use the call :meth:`resolvconf`.
|
|
|
|
:param addr:
|
|
address, IP4 or IP6 in string format. If the addr is None, forwarding is disabled.
|
|
:returns: (int) 0 if OK, else error.
|
|
"""
|
|
return _unbound.ub_ctx_set_fwd(self,addr)
|
|
#parameters: struct ub_ctx *,char *,
|
|
#retvals: int
|
|
|
|
def set_option(self,opt,val):
|
|
"""Set an option for the context.
|
|
|
|
Changes to the options after :meth:`resolve`, :meth:`resolve_async`, :meth:`zone_add`, :meth:`zone_remove`, :meth:`data_add` or :meth:`data_remove` have no effect (you have to delete and re-create the context).
|
|
|
|
:param opt:
|
|
option name from the unbound.conf config file format. (not all settings applicable). The name includes the trailing ':' for example set_option("logfile:", "mylog.txt"); This is a power-users interface that lets you specify all sorts of options. For some specific options, such as adding trust anchors, special routines exist.
|
|
:param val:
|
|
value of the option.
|
|
:returns: (int) 0 if OK, else error.
|
|
"""
|
|
return _unbound.ub_ctx_set_option(self,opt,val)
|
|
#parameters: struct ub_ctx *,char *,char *,
|
|
#retvals: int
|
|
|
|
def trustedkeys(self,fname):
|
|
"""Add trust anchors to the given context.
|
|
|
|
Pass the name of a bind-style config file with trusted-keys{}.
|
|
|
|
:param fname:
|
|
filename of file with bind-style config entries with trust anchors.
|
|
:returns: (int) 0 if OK, else error.
|
|
"""
|
|
return _unbound.ub_ctx_trustedkeys(self,fname)
|
|
#parameters: struct ub_ctx *,char *,
|
|
#retvals: int
|
|
#_UB_CTX_METHODS#
|
|
|
|
def zone_print(self):
|
|
"""Print local zones using debougout"""
|
|
_unbound.ub_ctx_print_local_zones(self)
|
|
|
|
def zone_add(self,zonename,zonetype):
|
|
"""Add new local zone
|
|
|
|
:param zonename: zone domain name (e.g. myzone.)
|
|
:param zonetype: type of the zone ("static",...)
|
|
:returns: (int) 0 if OK, else error.
|
|
"""
|
|
return _unbound.ub_ctx_zone_add(self,zonename, zonetype)
|
|
#parameters: struct ub_ctx *,char*, char*
|
|
#retvals: int
|
|
|
|
def zone_remove(self,zonename):
|
|
"""Remove local zone
|
|
|
|
If exists, removes local zone with all the RRs.
|
|
|
|
:param zonename: zone domain name
|
|
:returns: (int) 0 if OK, else error.
|
|
"""
|
|
return _unbound.ub_ctx_zone_remove(self,zonename)
|
|
#parameters: struct ub_ctx *,char*
|
|
#retvals: int
|
|
|
|
def data_add(self,rrdata):
|
|
"""Add new local RR data
|
|
|
|
:param rrdata: string, in zone-format on one line. [domainname] [TTL optional] [type] [class optional] [rdata contents]
|
|
:returns: (int) 0 if OK, else error.
|
|
|
|
**Usage**
|
|
The local data ...
|
|
|
|
::
|
|
|
|
>>> ctx = unbound.ub_ctx()
|
|
>>> ctx.zone_add("mydomain.net.","static")
|
|
0
|
|
>>> status = ctx.data_add("test.mydomain.net. IN A 192.168.1.1")
|
|
0
|
|
>>> status, result = ctx.resolve("test.mydomain.net")
|
|
>>> if status==0 and result.havedata:
|
|
>>> print \"Result:\",result.data.address_list
|
|
Result: ['192.168.1.1']
|
|
|
|
"""
|
|
return _unbound.ub_ctx_data_add(self,rrdata)
|
|
#parameters: struct ub_ctx *,char*
|
|
#retvals: int
|
|
|
|
def data_remove(self,rrdata):
|
|
"""Remove local RR data
|
|
|
|
If exists, remove resource record from local zone
|
|
|
|
:param rrdata: string, in zone-format on one line. [domainname] [TTL optional] [type] [class optional] [rdata contents]
|
|
:returns: (int) 0 if OK, else error.
|
|
"""
|
|
return _unbound.ub_ctx_data_remove(self,rrdata)
|
|
#parameters: struct ub_ctx *,char*
|
|
#retvals: int
|
|
|
|
#UB_METHODS_#
|
|
def cancel(self,async_id):
|
|
"""Cancel an async query in progress.
|
|
|
|
Its callback will not be called.
|
|
|
|
:param async_id:
|
|
which query to cancel.
|
|
:returns: (int) 0 if OK, else error.
|
|
"""
|
|
return _unbound.ub_cancel(self,async_id)
|
|
#parameters: struct ub_ctx *,int,
|
|
#retvals: int
|
|
|
|
def get_fd(self):
|
|
"""Get file descriptor.
|
|
|
|
Wait for it to become readable, at this point answers are returned from the asynchronous validating resolver. Then call the ub_process to continue processing. This routine works immediately after context creation, the fd does not change.
|
|
|
|
:returns: (int) -1 on error, or file descriptor to use select(2) with.
|
|
"""
|
|
return _unbound.ub_fd(self)
|
|
#parameters: struct ub_ctx *,
|
|
#retvals: int
|
|
|
|
def poll(self):
|
|
"""Poll a context to see if it has any new results Do not poll in a loop, instead extract the fd below to poll for readiness, and then check, or wait using the wait routine.
|
|
|
|
:returns: (int) 0 if nothing to read, or nonzero if a result is available. If nonzero, call ctx_process() to do callbacks.
|
|
"""
|
|
return _unbound.ub_poll(self)
|
|
#parameters: struct ub_ctx *,
|
|
#retvals: int
|
|
|
|
def process(self):
|
|
"""Call this routine to continue processing results from the validating resolver (when the fd becomes readable).
|
|
|
|
Will perform necessary callbacks.
|
|
|
|
:returns: (int) 0 if OK, else error.
|
|
"""
|
|
return _unbound.ub_process(self)
|
|
#parameters: struct ub_ctx *,
|
|
#retvals: int
|
|
|
|
def resolve(self,name,rrtype=RR_TYPE_A,rrclass=RR_CLASS_IN):
|
|
"""Perform resolution and validation of the target name.
|
|
|
|
:param name:
|
|
domain name in text format (a string or unicode string). IDN domain name have to be passed as a unicode string.
|
|
:param rrtype:
|
|
type of RR in host order (optional argument). Default value is RR_TYPE_A (A class).
|
|
:param rrclass:
|
|
class of RR in host order (optional argument). Default value is RR_CLASS_IN (for internet).
|
|
:returns: * (int) 0 if OK, else error.
|
|
* (:class:`ub_result`) the result data is returned in a newly allocated result structure. May be None on return, return value is set to an error in that case (out of memory).
|
|
"""
|
|
if isinstance(name, bytes): #probably IDN
|
|
return _unbound.ub_resolve(self,name,rrtype,rrclass)
|
|
else:
|
|
return _unbound.ub_resolve(self,idn2dname(name),rrtype,rrclass)
|
|
#parameters: struct ub_ctx *,char *,int,int,
|
|
#retvals: int,struct ub_result **
|
|
|
|
def resolve_async(self,name,mydata,callback,rrtype=RR_TYPE_A,rrclass=RR_CLASS_IN):
|
|
"""Perform resolution and validation of the target name.
|
|
|
|
Asynchronous, after a while, the callback will be called with your data and the result.
|
|
If an error happens during processing, your callback will be called with error set to a nonzero value (and result==None).
|
|
|
|
:param name:
|
|
domain name in text format (a string or unicode string). IDN domain name have to be passed as a unicode string.
|
|
:param mydata:
|
|
this data is your own data (you can pass arbitrary python object or None) which are passed on to the callback function.
|
|
:param callback:
|
|
call-back function which is called on completion of the resolution.
|
|
:param rrtype:
|
|
type of RR in host order (optional argument). Default value is RR_TYPE_A (A class).
|
|
:param rrclass:
|
|
class of RR in host order (optional argument). Default value is RR_CLASS_IN (for internet).
|
|
:returns: * (int) 0 if OK, else error.
|
|
* (int) async_id, an identifier number is returned for the query as it is in progress. It can be used to cancel the query.
|
|
|
|
**Call-back function:**
|
|
The call-back function looks as the follows::
|
|
|
|
def call_back(mydata, status, result):
|
|
pass
|
|
|
|
**Parameters:**
|
|
* `mydata` - mydata object
|
|
* `status` - 0 when a result has been found
|
|
* `result` - the result structure. The result may be None, in that case err is set.
|
|
|
|
"""
|
|
if isinstance(name, bytes): #probably IDN
|
|
return _unbound._ub_resolve_async(self,name,rrtype,rrclass,mydata,callback)
|
|
else:
|
|
return _unbound._ub_resolve_async(self,idn2dname(name),rrtype,rrclass,mydata,callback)
|
|
#parameters: struct ub_ctx *,char *,int,int,void *,ub_callback_t,
|
|
#retvals: int, int
|
|
|
|
def wait(self):
|
|
"""Wait for a context to finish with results.
|
|
|
|
Calls after the wait for you. After the wait, there are no more outstanding asynchronous queries.
|
|
|
|
:returns: (int) 0 if OK, else error.
|
|
"""
|
|
return _unbound.ub_wait(self)
|
|
#parameters: struct ub_ctx *,
|
|
#retvals: int
|
|
|
|
#_UB_METHODS#
|
|
%}
|
|
}
|
|
|
|
|
|
// ================================================================================
|
|
// ub_result - validation and resolution results
|
|
// ================================================================================
|
|
%nodefaultctor ub_result; //no default constructor & destructor
|
|
%nodefaultdtor ub_result;
|
|
|
|
%delobject ub_resolve_free;
|
|
%rename(_ub_resolve_free) ub_resolve_free;
|
|
|
|
%inline %{
|
|
void ub_resolve_free_dbg (struct ub_result* r) {
|
|
printf("******** UB_RESOLVE free 0x%lX ************\n", (long unsigned int)r);
|
|
ub_resolve_free(r);
|
|
}
|
|
%}
|
|
|
|
%feature("docstring") ub_result "The validation and resolution results."
|
|
|
|
//ub_result.rcode
|
|
%inline %{
|
|
enum result_enum_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 ub_data:
|
|
"""Class which makes the resolution results accessible"""
|
|
def __init__(self, data):
|
|
"""Creates ub_data class
|
|
:param data: a list of the result data in RAW format
|
|
"""
|
|
if data == None:
|
|
raise Exception("ub_data init: No data")
|
|
self.data = data
|
|
|
|
def __str__(self):
|
|
"""Represents data as string"""
|
|
return ';'.join([' '.join(map(lambda x:"%02X" % ord(x),a)) for a in self.data])
|
|
|
|
@staticmethod
|
|
def dname2str(s, ofs=0, maxlen=0):
|
|
"""Parses DNAME and produces a list of labels
|
|
|
|
:param ofs: where the conversion should start to parse data
|
|
:param maxlen: maximum length (0 means parse to the end)
|
|
:returns: list of labels (string)
|
|
"""
|
|
if not s:
|
|
return []
|
|
|
|
res = []
|
|
slen = len(s)
|
|
if maxlen > 0:
|
|
slen = min(slen, maxlen)
|
|
|
|
idx = ofs
|
|
while (idx < slen):
|
|
complen = ord(s[idx])
|
|
# In python 3.x `str()` converts the string to unicode which is the expected text string type
|
|
res.append(str(s[idx+1:idx+1+complen]))
|
|
idx += complen + 1
|
|
|
|
return res
|
|
|
|
def as_raw_data(self):
|
|
"""Returns a list of RAW strings"""
|
|
return self.data
|
|
|
|
raw = property(as_raw_data, doc="Returns RAW data (a list of binary encoded strings). See :meth:`as_raw_data`")
|
|
|
|
def as_mx_list(self):
|
|
"""Represents data as a list of MX records (query for RR_TYPE_MX)
|
|
|
|
:returns: list of tuples (priority, dname)
|
|
"""
|
|
return [(256*ord(rdf[0])+ord(rdf[1]),'.'.join([a for a in self.dname2str(rdf,2)])) for rdf in self.data]
|
|
|
|
mx_list = property(as_mx_list, doc="Returns a list of tuples containing priority and domain names. See :meth:`as_mx_list`")
|
|
|
|
def as_idn_mx_list(self):
|
|
"""Represents data as a list of MX records (query for RR_TYPE_MX)
|
|
|
|
:returns: list of tuples (priority, unicode dname)
|
|
"""
|
|
return [(256*ord(rdf[0])+ord(rdf[1]),'.'.join([encodings.idna.ToUnicode(a) for a in self.dname2str(rdf,2)])) for rdf in self.data]
|
|
|
|
mx_list_idn = property(as_idn_mx_list, doc="Returns a list of tuples containing priority and IDN domain names. See :meth:`as_idn_mx_list`")
|
|
|
|
def as_address_list(self):
|
|
"""Represents data as a list of IP addresses (query for RR_TYPE_PTR)
|
|
|
|
:returns: list of strings
|
|
"""
|
|
return ['.'.join(map(lambda x:str(ord(x)),a)) for a in self.data]
|
|
|
|
address_list = property(as_address_list, doc="Returns a list of IP addresses. See :meth:`as_address_list`")
|
|
|
|
def as_domain_list(self):
|
|
"""Represents data as a list of domain names (query for RR_TYPE_A)
|
|
|
|
:returns: list of strings
|
|
"""
|
|
return map(lambda x:'.'.join(self.dname2str(x)), self.data)
|
|
|
|
domain_list = property(as_domain_list, doc="Returns a list of domain names. See :meth:`as_domain_list`")
|
|
|
|
def as_idn_domain_list(self):
|
|
"""Represents data as a list of unicode domain names (query for RR_TYPE_A)
|
|
|
|
:returns: list of strings
|
|
"""
|
|
return map(lambda x: '.'.join([encodings.idna.ToUnicode(a) for a in self.dname2str(x)]), self.data)
|
|
|
|
domain_list_idn = property(as_idn_domain_list, doc="Returns a list of IDN domain names. See :meth:`as_idn_domain_list`")
|
|
%}
|
|
|
|
%extend ub_result
|
|
{
|
|
|
|
%rename(_data) data;
|
|
|
|
PyObject* _ub_result_data(struct ub_result* result) {
|
|
PyObject *list;
|
|
int i,cnt;
|
|
(void)self;
|
|
if ((result == 0) || (!result->havedata) || (result->data == 0))
|
|
return Py_None;
|
|
|
|
for (cnt=0,i=0;;i++,cnt++)
|
|
if (result->data[i] == 0)
|
|
break;
|
|
|
|
list = PyList_New(cnt);
|
|
for (i=0;i<cnt;i++)
|
|
PyList_SetItem(list, i, PyBytes_FromStringAndSize(result->data[i],result->len[i]));
|
|
|
|
return list;
|
|
}
|
|
|
|
PyObject* _packet() {
|
|
return PyBytes_FromStringAndSize($self->answer_packet, $self->answer_len);
|
|
}
|
|
|
|
%pythoncode %{
|
|
def __init__(self):
|
|
raise Exception("This class can't be created directly.")
|
|
|
|
#__swig_destroy__ = _unbound.ub_resolve_free_dbg
|
|
__swig_destroy__ = _unbound._ub_resolve_free
|
|
|
|
#havedata = property(_unbound.ub_result_havedata_get, _unbound.ub_result_havedata_set, "Havedata property")
|
|
|
|
rcode2str = {RCODE_NOERROR:'no error', RCODE_FORMERR:'form error', RCODE_SERVFAIL:'serv fail', RCODE_NXDOMAIN:'nx domain', RCODE_NOTIMPL:'not implemented', RCODE_REFUSED:'refused', RCODE_YXDOMAIN:'yxdomain', RCODE_YXRRSET:'yxrrset', RCODE_NXRRSET:'nxrrset', RCODE_NOTAUTH:'not auth', RCODE_NOTZONE:'not zone'}
|
|
|
|
def _get_rcode_str(self):
|
|
"""Returns rcode in display representation form
|
|
|
|
:returns: string
|
|
"""
|
|
return self.rcode2str[self.rcode]
|
|
|
|
__swig_getmethods__["rcode_str"] = _get_rcode_str
|
|
if _newclass:rcode_str = _swig_property(_get_rcode_str)
|
|
|
|
def _get_raw_data(self):
|
|
"""Result data, a list of network order DNS rdata items.
|
|
|
|
Data are represented as a list of strings. To decode RAW data to the list of IP addresses use :attr:`data` attribute which returns an :class:`ub_data` instance containing conversion function.
|
|
"""
|
|
return self._ub_result_data(self)
|
|
|
|
__swig_getmethods__["rawdata"] = _get_raw_data
|
|
rawdata = property(_get_raw_data, doc="Returns raw data, a list of rdata items. To decode RAW data use the :attr:`data` attribute which returns an instance of :class:`ub_data` containing the conversion functions.")
|
|
|
|
def _get_data(self):
|
|
if not self.havedata: return None
|
|
return ub_data(self._ub_result_data(self))
|
|
|
|
__swig_getmethods__["data"] = _get_data
|
|
__swig_getmethods__["packet"] = _packet
|
|
data = property(_get_data, doc="Returns :class:`ub_data` instance containing various decoding functions or None")
|
|
|
|
%}
|
|
|
|
}
|
|
|
|
%exception ub_resolve
|
|
%{
|
|
//printf("resolve_start(%lX)\n",(long unsigned int)arg1);
|
|
Py_BEGIN_ALLOW_THREADS
|
|
$function
|
|
Py_END_ALLOW_THREADS
|
|
//printf("resolve_stop()\n");
|
|
%}
|
|
|
|
%include "libunbound/unbound.h"
|
|
|
|
%inline %{
|
|
//SWIG will see the ub_ctx as a class
|
|
struct ub_ctx {
|
|
};
|
|
%}
|
|
|
|
//ub_ctx_debugout void* parameter correction
|
|
int ub_ctx_debugout(struct ub_ctx* ctx, FILE* out);
|
|
|
|
// ================================================================================
|
|
// ub_resolve_async - perform asynchronous resolution and validation
|
|
// ================================================================================
|
|
|
|
%typemap(in,numinputs=0,noblock=1) (int* async_id)
|
|
{
|
|
int asyncid = -1;
|
|
$1 = &asyncid;
|
|
}
|
|
|
|
%apply PyObject* {void* mydata}
|
|
|
|
/* result generation */
|
|
%typemap(argout,noblock=1) (int* async_id)
|
|
{
|
|
if(1) { /* new code block for variable on stack */
|
|
PyObject* tuple;
|
|
tuple = PyTuple_New(2);
|
|
PyTuple_SetItem(tuple, 0, $result);
|
|
PyTuple_SetItem(tuple, 1, SWIG_From_int(asyncid));
|
|
$result = tuple;
|
|
}
|
|
}
|
|
|
|
// Grab a Python function object as a Python object.
|
|
%typemap(in) (PyObject *pyfunc) {
|
|
if (!PyCallable_Check($input))
|
|
{
|
|
PyErr_SetString(PyExc_TypeError, "Need a callable object!");
|
|
return NULL;
|
|
}
|
|
$1 = $input;
|
|
}
|
|
|
|
// Python callback workaround
|
|
int _ub_resolve_async(struct ub_ctx* ctx, char* name, int rrtype, int rrclass, void* mydata, PyObject *pyfunc, int* async_id);
|
|
|
|
%{
|
|
struct cb_data {
|
|
PyObject* data;
|
|
PyObject* func;
|
|
};
|
|
|
|
static void PythonCallBack(void* iddata, int status, struct ub_result* result)
|
|
{
|
|
PyObject *arglist;
|
|
PyObject *fresult;
|
|
struct cb_data* id;
|
|
id = (struct cb_data*) iddata;
|
|
arglist = Py_BuildValue("(OiO)",id->data,status, SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_ub_result, 0 | 0 )); // Build argument list
|
|
fresult = PyEval_CallObject(id->func,arglist); // Call Python
|
|
Py_DECREF(id->func);
|
|
Py_DECREF(id->data);
|
|
free(id);
|
|
ub_resolve_free(result); //free ub_result
|
|
//ub_resolve_free_dbg(result); //free ub_result
|
|
Py_DECREF(arglist); // Trash arglist
|
|
Py_XDECREF(fresult);
|
|
}
|
|
|
|
int _ub_resolve_async(struct ub_ctx* ctx, char* name, int rrtype, int rrclass, PyObject* mydata, PyObject *pyfunc, int* async_id) {
|
|
int r;
|
|
struct cb_data* id;
|
|
id = (struct cb_data*) malloc(sizeof(struct cb_data));
|
|
id->data = mydata;
|
|
id->func = pyfunc;
|
|
|
|
r = ub_resolve_async(ctx,name,rrtype,rrclass, (void *) id, PythonCallBack, async_id);
|
|
Py_INCREF(mydata);
|
|
Py_INCREF(pyfunc);
|
|
return r;
|
|
}
|
|
|
|
%}
|
|
|
|
%pythoncode %{
|
|
ub_resolve_async = _unbound._ub_resolve_async
|
|
|
|
def reverse(domain):
|
|
"""Reverse domain name
|
|
|
|
Usable for reverse lookups when the IP address should be reversed
|
|
"""
|
|
return '.'.join([a for a in domain.split(".")][::-1])
|
|
|
|
def idn2dname(idnname):
|
|
"""Converts domain name in IDN format to canonic domain name
|
|
|
|
:param idnname: (unicode string) IDN name
|
|
:returns: (string) domain name
|
|
"""
|
|
return '.'.join([encodings.idna.ToASCII(a) for a in idnname.split('.')])
|
|
|
|
def dname2idn(name):
|
|
"""Converts canonic domain name in IDN format to unicode string
|
|
|
|
:param name: (string) domain name
|
|
:returns: (unicode string) domain name
|
|
"""
|
|
return '.'.join([encodings.idna.ToUnicode(a) for a in name.split('.')])
|
|
|
|
%}
|
|
|