a85b5759f3
These files were pulled from the 1.6.3 release tarball. This new version builds against OpenSSL version 1.1 which will be the default in the new Debian Stable which is due to be released RealSoonNow (tm).
959 lines
34 KiB
OpenEdge ABL
959 lines
34 KiB
OpenEdge ABL
/*
|
|
* libunbound.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
|
|
try:
|
|
import builtins
|
|
except ImportError:
|
|
import __builtin__ as builtins
|
|
|
|
# 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"
|
|
#if PY_MAJOR_VERSION >= 3
|
|
%include "file_py3.i" // python 3 FILE *
|
|
#else
|
|
%include "file.i"
|
|
#endif
|
|
|
|
%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 debugout"""
|
|
_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].decode()))
|
|
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) if a else '' 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('.')])
|
|
|
|
%}
|
|
|