libunbound.i revision 291767
1/*
2 * libounbound.i: pyUnbound module (libunbound wrapper for Python)
3 *
4 * Copyright (c) 2009, Zdenek Vasicek (vasicek AT fit.vutbr.cz)
5 *                     Marek Vavrusa  (xvavru00 AT stud.fit.vutbr.cz)
6 *
7 * This software is open source.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 *    * Redistributions of source code must retain the above copyright notice,
14 *      this list of conditions and the following disclaimer.
15 *
16 *    * Redistributions in binary form must reproduce the above copyright notice,
17 *      this list of conditions and the following disclaimer in the documentation
18 *      and/or other materials provided with the distribution.
19 *
20 *    * Neither the name of the organization nor the names of its
21 *      contributors may be used to endorse or promote products derived from this
22 *      software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 */
36%module unbound
37%{
38   #include <sys/types.h>
39   #include <sys/socket.h>
40   #include <netinet/in.h>
41   #include <arpa/inet.h>
42   #include "libunbound/unbound.h"
43%}
44
45%pythoncode %{
46   import encodings.idna
47   try:
48       import builtins
49   except ImportError:
50       import __builtin__ as builtins
51
52   # Ensure compatibility with older python versions
53   if 'bytes' not in vars():
54       bytes = str
55
56   def ord(s):
57       if isinstance(s, int):
58           return s
59       return builtins.ord(s)
60%}
61
62//%include "doc.i"
63#if PY_MAJOR_VERSION >= 3
64%include "file_py3.i" // python 3 FILE *
65#else
66%include "file.i"
67#endif
68
69%feature("docstring") strerror "Convert error value to a human readable string."
70
71// ================================================================================
72// ub_resolve - perform resolution and validation
73// ================================================================================
74%typemap(in,numinputs=0,noblock=1) (struct ub_result** result)
75{
76   struct ub_result* newubr;
77   $1 = &newubr;
78}
79
80/* result generation */
81%typemap(argout,noblock=1) (struct ub_result** result)
82{
83  if(1) { /* new code block for variable on stack */
84    PyObject* tuple;
85    tuple = PyTuple_New(2);
86    PyTuple_SetItem(tuple, 0, $result);
87    if (result == 0) {
88       PyTuple_SetItem(tuple, 1, SWIG_NewPointerObj(SWIG_as_voidptr(newubr), SWIGTYPE_p_ub_result, SWIG_POINTER_OWN |  0 ));
89    } else {
90       PyTuple_SetItem(tuple, 1, Py_None);
91    }
92    $result = tuple;
93  }
94}
95
96
97// ================================================================================
98// ub_ctx - validation context
99// ================================================================================
100%nodefaultctor ub_ctx; //no default constructor & destructor
101%nodefaultdtor ub_ctx;
102
103%newobject ub_ctx_create;
104%delobject ub_ctx_delete;
105%rename(_ub_ctx_delete) ub_ctx_delete;
106
107%newobject ub_resolve;
108
109%inline %{
110  void ub_ctx_free_dbg (struct ub_ctx* c) {
111    printf("******** UB_CTX free 0x%lX ************\n", (long unsigned int)c);
112    ub_ctx_delete(c);
113  }
114
115  //RR types
116  enum enum_rr_type
117  {
118    /**  a host address */
119    RR_TYPE_A = 1,
120    /**  an authoritative name server */
121    RR_TYPE_NS = 2,
122    /**  a mail destination (Obsolete - use MX) */
123    RR_TYPE_MD = 3,
124    /**  a mail forwarder (Obsolete - use MX) */
125    RR_TYPE_MF = 4,
126    /**  the canonical name for an alias */
127    RR_TYPE_CNAME = 5,
128    /**  marks the start of a zone of authority */
129    RR_TYPE_SOA = 6,
130    /**  a mailbox domain name (EXPERIMENTAL) */
131    RR_TYPE_MB = 7,
132    /**  a mail group member (EXPERIMENTAL) */
133    RR_TYPE_MG = 8,
134    /**  a mail rename domain name (EXPERIMENTAL) */
135    RR_TYPE_MR = 9,
136    /**  a null RR (EXPERIMENTAL) */
137    RR_TYPE_NULL = 10,
138    /**  a well known service description */
139    RR_TYPE_WKS = 11,
140    /**  a domain name pointer */
141    RR_TYPE_PTR = 12,
142    /**  host information */
143    RR_TYPE_HINFO = 13,
144    /**  mailbox or mail list information */
145    RR_TYPE_MINFO = 14,
146    /**  mail exchange */
147    RR_TYPE_MX = 15,
148    /**  text strings */
149    RR_TYPE_TXT = 16,
150    /**  RFC1183 */
151    RR_TYPE_RP = 17,
152    /**  RFC1183 */
153    RR_TYPE_AFSDB = 18,
154    /**  RFC1183 */
155    RR_TYPE_X25 = 19,
156    /**  RFC1183 */
157    RR_TYPE_ISDN = 20,
158    /**  RFC1183 */
159    RR_TYPE_RT = 21,
160    /**  RFC1706 */
161    RR_TYPE_NSAP = 22,
162    /**  RFC1348 */
163    RR_TYPE_NSAP_PTR = 23,
164    /**  2535typecode */
165    RR_TYPE_SIG = 24,
166    /**  2535typecode */
167    RR_TYPE_KEY = 25,
168    /**  RFC2163 */
169    RR_TYPE_PX = 26,
170    /**  RFC1712 */
171    RR_TYPE_GPOS = 27,
172    /**  ipv6 address */
173    RR_TYPE_AAAA = 28,
174    /**  LOC record  RFC1876 */
175    RR_TYPE_LOC = 29,
176    /**  2535typecode */
177    RR_TYPE_NXT = 30,
178    /**  draft-ietf-nimrod-dns-01.txt */
179    RR_TYPE_EID = 31,
180    /**  draft-ietf-nimrod-dns-01.txt */
181    RR_TYPE_NIMLOC = 32,
182    /**  SRV record RFC2782 */
183    RR_TYPE_SRV = 33,
184    /**  http://www.jhsoft.com/rfc/af-saa-0069.000.rtf */
185    RR_TYPE_ATMA = 34,
186    /**  RFC2915 */
187    RR_TYPE_NAPTR = 35,
188    /**  RFC2230 */
189    RR_TYPE_KX = 36,
190    /**  RFC2538 */
191    RR_TYPE_CERT = 37,
192    /**  RFC2874 */
193    RR_TYPE_A6 = 38,
194    /**  RFC2672 */
195    RR_TYPE_DNAME = 39,
196    /**  dnsind-kitchen-sink-02.txt */
197    RR_TYPE_SINK = 40,
198    /**  Pseudo OPT record... */
199    RR_TYPE_OPT = 41,
200    /**  RFC3123 */
201    RR_TYPE_APL = 42,
202    /**  draft-ietf-dnsext-delegation */
203    RR_TYPE_DS = 43,
204    /**  SSH Key Fingerprint */
205    RR_TYPE_SSHFP = 44,
206    /**  draft-richardson-ipseckey-rr-11.txt */
207    RR_TYPE_IPSECKEY = 45,
208    /**  draft-ietf-dnsext-dnssec-25 */
209    RR_TYPE_RRSIG = 46,
210    RR_TYPE_NSEC = 47,
211    RR_TYPE_DNSKEY = 48,
212    RR_TYPE_DHCID = 49,
213
214    RR_TYPE_NSEC3 = 50,
215    RR_TYPE_NSEC3PARAMS = 51,
216
217    RR_TYPE_UINFO = 100,
218    RR_TYPE_UID = 101,
219    RR_TYPE_GID = 102,
220    RR_TYPE_UNSPEC = 103,
221
222    RR_TYPE_TSIG = 250,
223    RR_TYPE_IXFR = 251,
224    RR_TYPE_AXFR = 252,
225    /**  A request for mailbox-related records (MB, MG or MR) */
226    RR_TYPE_MAILB = 253,
227    /**  A request for mail agent RRs (Obsolete - see MX) */
228    RR_TYPE_MAILA = 254,
229    /**  any type (wildcard) */
230    RR_TYPE_ANY = 255,
231
232    /* RFC 4431, 5074, DNSSEC Lookaside Validation */
233    RR_TYPE_DLV = 32769,
234  };
235
236  // RR classes
237  enum enum_rr_class
238  {
239    /** the Internet */
240    RR_CLASS_IN = 1,
241    /** Chaos class */
242    RR_CLASS_CH = 3,
243    /** Hesiod (Dyer 87) */
244    RR_CLASS_HS = 4,
245    /** None class, dynamic update */
246    RR_CLASS_NONE = 254,
247    /** Any class */
248    RR_CLASS_ANY = 255,
249  };
250%}
251
252%feature("docstring") ub_ctx "Unbound resolving and validation context.
253
254The validation context is created to hold the resolver status, validation keys and a small cache (containing messages, rrsets, roundtrip times, trusted keys, lameness information).
255
256**Usage**
257
258>>> import unbound
259>>> ctx = unbound.ub_ctx()
260>>> ctx.resolvconf(\"/etc/resolv.conf\")
261>>> status, result = ctx.resolve(\"www.google.com\", unbound.RR_TYPE_A, unbound.RR_CLASS_IN)
262>>> if status==0 and result.havedata:
263>>>    print \"Result:\",result.data.address_list
264Result: ['74.125.43.147', '74.125.43.99', '74.125.43.103', '74.125.43.104']
265"
266
267%extend ub_ctx
268{
269 %pythoncode %{
270        def __init__(self):
271            """Creates a resolving and validation context.
272
273               An exception is invoked if the process of creation an ub_ctx instance fails.
274            """
275            self.this = _unbound.ub_ctx_create()
276            if not self.this:
277                raise Exception("Fatal error: unbound context initialization failed")
278
279        #__swig_destroy__ = _unbound.ub_ctx_free_dbg
280        __swig_destroy__ = _unbound._ub_ctx_delete
281
282        #UB_CTX_METHODS_#
283        def add_ta(self,ta):
284            """Add a trust anchor to the given context.
285
286               The trust anchor is a string, on one line, that holds a valid DNSKEY or DS RR.
287
288               :param ta:
289                   string, with zone-format RR on one line. [domainname] [TTL optional] [type] [class optional] [rdata contents]
290               :returns: (int) 0 if OK, else error.
291            """
292            return _unbound.ub_ctx_add_ta(self,ta)
293            #parameters: struct ub_ctx *,char *,
294            #retvals: int
295
296        def add_ta_file(self,fname):
297            """Add trust anchors to the given context.
298
299               Pass name of a file with DS and DNSKEY records (like from dig or drill).
300
301               :param fname:
302                   filename of file with keyfile with trust anchors.
303               :returns: (int) 0 if OK, else error.
304            """
305            return _unbound.ub_ctx_add_ta_file(self,fname)
306            #parameters: struct ub_ctx *,char *,
307            #retvals: int
308
309        def config(self,fname):
310            """setup configuration for the given context.
311
312               :param fname:
313                   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.
314               :returns: (int) 0 if OK, else error.
315            """
316            return _unbound.ub_ctx_config(self,fname)
317            #parameters: struct ub_ctx *,char *,
318            #retvals: int
319
320        def debuglevel(self,d):
321            """Set debug verbosity for the context Output is directed to stderr.
322
323               :param d:
324                   debug level, 0 is off, 1 is very minimal, 2 is detailed, and 3 is lots.
325               :returns: (int) 0 if OK, else error.
326            """
327            return _unbound.ub_ctx_debuglevel(self,d)
328            #parameters: struct ub_ctx *,int,
329            #retvals: int
330
331        def debugout(self,out):
332            """Set debug output (and error output) to the specified stream.
333
334               Pass None to disable. Default is stderr.
335
336               :param out:
337                   File stream to log to.
338               :returns: (int) 0 if OK, else error.
339
340               **Usage:**
341
342                  In order to log into file, use
343
344                  ::
345
346                    ctx = unbound.ub_ctx()
347                    fw = fopen("debug.log")
348                    ctx.debuglevel(3)
349                    ctx.debugout(fw)
350
351                  Another option is to print the debug informations to stderr output
352
353                  ::
354
355                    ctx = unbound.ub_ctx()
356                    ctx.debuglevel(10)
357                    ctx.debugout(sys.stderr)
358            """
359            return _unbound.ub_ctx_debugout(self,out)
360            #parameters: struct ub_ctx *,void *,
361            #retvals: int
362
363        def hosts(self,fname="/etc/hosts"):
364            """Read list of hosts from the filename given.
365
366               Usually "/etc/hosts". These addresses are not flagged as DNSSEC secure when queried for.
367
368               :param fname:
369                   file name string. If None "/etc/hosts" is used.
370               :returns: (int) 0 if OK, else error.
371            """
372            return _unbound.ub_ctx_hosts(self,fname)
373            #parameters: struct ub_ctx *,char *,
374            #retvals: int
375
376        def print_local_zones(self):
377            """Print the local zones and their content (RR data) to the debug output.
378
379               :returns: (int) 0 if OK, else error.
380            """
381            return _unbound.ub_ctx_print_local_zones(self)
382            #parameters: struct ub_ctx *,
383            #retvals: int
384
385        def resolvconf(self,fname="/etc/resolv.conf"):
386            """Read list of nameservers to use from the filename given.
387
388               Usually "/etc/resolv.conf". Uses those nameservers as caching proxies. If they do not support DNSSEC, validation may fail.
389
390               Only nameservers are picked up, the searchdomain, ndots and other settings from resolv.conf(5) are ignored.
391
392               :param fname:
393                   file name string. If None "/etc/resolv.conf" is used.
394               :returns: (int) 0 if OK, else error.
395            """
396            return _unbound.ub_ctx_resolvconf(self,fname)
397            #parameters: struct ub_ctx *,char *,
398            #retvals: int
399
400        def set_async(self,dothread):
401            """Set a context behaviour for asynchronous action.
402
403               :param dothread:
404                   if True, enables threading and a call to :meth:`resolve_async` creates a thread to handle work in the background.
405                   If False, a process is forked to handle work in the background.
406                   Changes to this setting after :meth:`async` calls have been made have no effect (delete and re-create the context to change).
407               :returns: (int) 0 if OK, else error.
408            """
409            return _unbound.ub_ctx_async(self,dothread)
410            #parameters: struct ub_ctx *,int,
411            #retvals: int
412
413        def set_fwd(self,addr):
414            """Set machine to forward DNS queries to, the caching resolver to use.
415
416               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.
417
418               To read the list of nameservers from /etc/resolv.conf (from DHCP or so), use the call :meth:`resolvconf`.
419
420               :param addr:
421                   address, IP4 or IP6 in string format. If the addr is None, forwarding is disabled.
422               :returns: (int) 0 if OK, else error.
423            """
424            return _unbound.ub_ctx_set_fwd(self,addr)
425            #parameters: struct ub_ctx *,char *,
426            #retvals: int
427
428        def set_option(self,opt,val):
429            """Set an option for the context.
430
431               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).
432
433               :param opt:
434                   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.
435               :param val:
436                   value of the option.
437               :returns: (int) 0 if OK, else error.
438            """
439            return _unbound.ub_ctx_set_option(self,opt,val)
440            #parameters: struct ub_ctx *,char *,char *,
441            #retvals: int
442
443        def trustedkeys(self,fname):
444            """Add trust anchors to the given context.
445
446               Pass the name of a bind-style config file with trusted-keys{}.
447
448               :param fname:
449                   filename of file with bind-style config entries with trust anchors.
450               :returns: (int) 0 if OK, else error.
451            """
452            return _unbound.ub_ctx_trustedkeys(self,fname)
453            #parameters: struct ub_ctx *,char *,
454            #retvals: int
455        #_UB_CTX_METHODS#
456
457        def zone_print(self):
458            """Print local zones using debougout"""
459            _unbound.ub_ctx_print_local_zones(self)
460
461        def zone_add(self,zonename,zonetype):
462            """Add new local zone
463
464               :param zonename: zone domain name (e.g. myzone.)
465               :param zonetype: type of the zone ("static",...)
466               :returns: (int) 0 if OK, else error.
467            """
468            return _unbound.ub_ctx_zone_add(self,zonename, zonetype)
469            #parameters: struct ub_ctx *,char*, char*
470            #retvals: int
471
472        def zone_remove(self,zonename):
473            """Remove local zone
474
475               If exists, removes local zone with all the RRs.
476
477               :param zonename: zone domain name
478               :returns: (int) 0 if OK, else error.
479            """
480            return _unbound.ub_ctx_zone_remove(self,zonename)
481            #parameters: struct ub_ctx *,char*
482            #retvals: int
483
484        def data_add(self,rrdata):
485            """Add new local RR data
486
487               :param rrdata: string, in zone-format on one line. [domainname] [TTL optional] [type] [class optional] [rdata contents]
488               :returns: (int) 0 if OK, else error.
489
490               **Usage**
491                  The local data ...
492
493                  ::
494
495                    >>> ctx = unbound.ub_ctx()
496                    >>> ctx.zone_add("mydomain.net.","static")
497                    0
498                    >>> status = ctx.data_add("test.mydomain.net. IN A 192.168.1.1")
499                    0
500                    >>> status, result = ctx.resolve("test.mydomain.net")
501                    >>> if status==0 and result.havedata:
502                    >>>    print \"Result:\",result.data.address_list
503                    Result: ['192.168.1.1']
504
505            """
506            return _unbound.ub_ctx_data_add(self,rrdata)
507            #parameters: struct ub_ctx *,char*
508            #retvals: int
509
510        def data_remove(self,rrdata):
511            """Remove local RR data
512
513               If exists, remove resource record from local zone
514
515               :param rrdata: string, in zone-format on one line. [domainname] [TTL optional] [type] [class optional] [rdata contents]
516               :returns: (int) 0 if OK, else error.
517            """
518            return _unbound.ub_ctx_data_remove(self,rrdata)
519            #parameters: struct ub_ctx *,char*
520            #retvals: int
521
522        #UB_METHODS_#
523        def cancel(self,async_id):
524            """Cancel an async query in progress.
525
526               Its callback will not be called.
527
528               :param async_id:
529                   which query to cancel.
530               :returns: (int) 0 if OK, else error.
531            """
532            return _unbound.ub_cancel(self,async_id)
533            #parameters: struct ub_ctx *,int,
534            #retvals: int
535
536        def get_fd(self):
537            """Get file descriptor.
538
539               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.
540
541               :returns: (int) -1 on error, or file descriptor to use select(2) with.
542            """
543            return _unbound.ub_fd(self)
544            #parameters: struct ub_ctx *,
545            #retvals: int
546
547        def poll(self):
548            """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.
549
550               :returns: (int) 0 if nothing to read, or nonzero if a result is available. If nonzero, call ctx_process() to do callbacks.
551            """
552            return _unbound.ub_poll(self)
553            #parameters: struct ub_ctx *,
554            #retvals: int
555
556        def process(self):
557            """Call this routine to continue processing results from the validating resolver (when the fd becomes readable).
558
559               Will perform necessary callbacks.
560
561               :returns: (int) 0 if OK, else error.
562            """
563            return _unbound.ub_process(self)
564            #parameters: struct ub_ctx *,
565            #retvals: int
566
567        def resolve(self,name,rrtype=RR_TYPE_A,rrclass=RR_CLASS_IN):
568            """Perform resolution and validation of the target name.
569
570               :param name:
571                   domain name in text format (a string or unicode string). IDN domain name have to be passed as a unicode string.
572               :param rrtype:
573                   type of RR in host order (optional argument). Default value is RR_TYPE_A (A class).
574               :param rrclass:
575                   class of RR in host order (optional argument). Default value is RR_CLASS_IN (for internet).
576               :returns: * (int) 0 if OK, else error.
577                         * (: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).
578            """
579            if isinstance(name, bytes): #probably IDN
580                return _unbound.ub_resolve(self,name,rrtype,rrclass)
581            else:
582                return _unbound.ub_resolve(self,idn2dname(name),rrtype,rrclass)
583            #parameters: struct ub_ctx *,char *,int,int,
584            #retvals: int,struct ub_result **
585
586        def resolve_async(self,name,mydata,callback,rrtype=RR_TYPE_A,rrclass=RR_CLASS_IN):
587            """Perform resolution and validation of the target name.
588
589               Asynchronous, after a while, the callback will be called with your data and the result.
590               If an error happens during processing, your callback will be called with error set to a nonzero value (and result==None).
591
592               :param name:
593                   domain name in text format (a string or unicode string). IDN domain name have to be passed as a unicode string.
594               :param mydata:
595                   this data is your own data (you can pass arbitrary python object or None) which are passed on to the callback function.
596               :param callback:
597                   call-back function which is called on completion of the resolution.
598               :param rrtype:
599                   type of RR in host order (optional argument). Default value is RR_TYPE_A (A class).
600               :param rrclass:
601                   class of RR in host order (optional argument). Default value is RR_CLASS_IN (for internet).
602               :returns: * (int) 0 if OK, else error.
603                         * (int) async_id, an identifier number is returned for the query as it is in progress. It can be used to cancel the query.
604
605               **Call-back function:**
606                    The call-back function looks as the follows::
607
608                        def call_back(mydata, status, result):
609                            pass
610
611                    **Parameters:**
612                        * `mydata` - mydata object
613                        * `status` - 0 when a result has been found
614                        * `result` - the result structure. The result may be None, in that case err is set.
615
616            """
617            if isinstance(name, bytes): #probably IDN
618                return _unbound._ub_resolve_async(self,name,rrtype,rrclass,mydata,callback)
619            else:
620                return _unbound._ub_resolve_async(self,idn2dname(name),rrtype,rrclass,mydata,callback)
621            #parameters: struct ub_ctx *,char *,int,int,void *,ub_callback_t,
622            #retvals: int, int
623
624        def wait(self):
625            """Wait for a context to finish with results.
626
627               Calls  after the wait for you. After the wait, there are no more outstanding asynchronous queries.
628
629               :returns: (int) 0 if OK, else error.
630            """
631            return _unbound.ub_wait(self)
632            #parameters: struct ub_ctx *,
633            #retvals: int
634
635        #_UB_METHODS#
636 %}
637}
638
639
640// ================================================================================
641// ub_result - validation and resolution results
642// ================================================================================
643%nodefaultctor ub_result; //no default constructor & destructor
644%nodefaultdtor ub_result;
645
646%delobject ub_resolve_free;
647%rename(_ub_resolve_free) ub_resolve_free;
648
649%inline %{
650  void ub_resolve_free_dbg (struct ub_result* r) {
651    printf("******** UB_RESOLVE free 0x%lX ************\n", (long unsigned int)r);
652    ub_resolve_free(r);
653  }
654%}
655
656%feature("docstring") ub_result "The validation and resolution results."
657
658//ub_result.rcode
659%inline %{
660  enum result_enum_rcode {
661    RCODE_NOERROR = 0,
662    RCODE_FORMERR = 1,
663    RCODE_SERVFAIL = 2,
664    RCODE_NXDOMAIN = 3,
665    RCODE_NOTIMPL = 4,
666    RCODE_REFUSED = 5,
667    RCODE_YXDOMAIN = 6,
668    RCODE_YXRRSET = 7,
669    RCODE_NXRRSET = 8,
670    RCODE_NOTAUTH = 9,
671    RCODE_NOTZONE = 10
672  };
673%}
674
675%pythoncode %{
676   class ub_data:
677      """Class which makes the resolution results accessible"""
678      def __init__(self, data):
679         """Creates ub_data class
680            :param data: a list of the result data in RAW format
681         """
682         if data == None:
683            raise Exception("ub_data init: No data")
684         self.data = data
685
686      def __str__(self):
687         """Represents data as string"""
688         return ';'.join([' '.join(map(lambda x:"%02X" % ord(x),a)) for a in self.data])
689
690      @staticmethod
691      def dname2str(s, ofs=0, maxlen=0):
692         """Parses DNAME and produces a list of labels
693
694            :param ofs: where the conversion should start to parse data
695            :param maxlen: maximum length (0 means parse to the end)
696            :returns: list of labels (string)
697         """
698         if not s:
699            return []
700
701         res = []
702         slen = len(s)
703         if maxlen > 0:
704            slen = min(slen, maxlen)
705
706         idx = ofs
707         while (idx < slen):
708            complen = ord(s[idx])
709            # In python 3.x `str()` converts the string to unicode which is the expected text string type
710            res.append(str(s[idx+1:idx+1+complen].decode()))
711            idx += complen + 1
712
713         return res
714
715      def as_raw_data(self):
716         """Returns a list of RAW strings"""
717         return self.data
718
719      raw = property(as_raw_data, doc="Returns RAW data (a list of binary encoded strings). See :meth:`as_raw_data`")
720
721      def as_mx_list(self):
722         """Represents data as a list of MX records (query for RR_TYPE_MX)
723
724            :returns: list of tuples (priority, dname)
725         """
726         return [(256*ord(rdf[0])+ord(rdf[1]),'.'.join([a for a in self.dname2str(rdf,2)])) for rdf in self.data]
727
728      mx_list = property(as_mx_list, doc="Returns a list of tuples containing priority and domain names. See :meth:`as_mx_list`")
729
730      def as_idn_mx_list(self):
731         """Represents data as a list of MX records (query for RR_TYPE_MX)
732
733            :returns: list of tuples (priority, unicode dname)
734         """
735         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]
736
737      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`")
738
739      def as_address_list(self):
740         """Represents data as a list of IP addresses (query for RR_TYPE_PTR)
741
742            :returns: list of strings
743         """
744         return ['.'.join(map(lambda x:str(ord(x)),a)) for a in self.data]
745
746      address_list = property(as_address_list, doc="Returns a list of IP addresses. See :meth:`as_address_list`")
747
748      def as_domain_list(self):
749         """Represents data as a list of domain names (query for RR_TYPE_A)
750
751            :returns: list of strings
752         """
753         return map(lambda x:'.'.join(self.dname2str(x)), self.data)
754
755      domain_list = property(as_domain_list, doc="Returns a list of domain names. See :meth:`as_domain_list`")
756
757      def as_idn_domain_list(self):
758         """Represents data as a list of unicode domain names (query for RR_TYPE_A)
759
760            :returns: list of strings
761         """
762         return map(lambda x: '.'.join([encodings.idna.ToUnicode(a) for a in self.dname2str(x)]), self.data)
763
764      domain_list_idn = property(as_idn_domain_list, doc="Returns a list of IDN domain names. See :meth:`as_idn_domain_list`")
765%}
766
767%extend ub_result
768{
769
770  %rename(_data) data;
771
772  PyObject* _ub_result_data(struct ub_result* result) {
773    PyObject  *list;
774     int i,cnt;
775     (void)self;
776     if ((result == 0) || (!result->havedata) || (result->data == 0))
777        return Py_None;
778
779     for (cnt=0,i=0;;i++,cnt++)
780         if (result->data[i] == 0)
781            break;
782
783     list = PyList_New(cnt);
784     for (i=0;i<cnt;i++)
785         PyList_SetItem(list, i, PyBytes_FromStringAndSize(result->data[i],result->len[i]));
786
787     return list;
788  }
789
790  PyObject* _packet() {
791      return PyBytes_FromStringAndSize($self->answer_packet, $self->answer_len);
792  }
793
794 %pythoncode %{
795   def __init__(self):
796       raise Exception("This class can't be created directly.")
797
798   #__swig_destroy__ = _unbound.ub_resolve_free_dbg
799   __swig_destroy__ = _unbound._ub_resolve_free
800
801   #havedata = property(_unbound.ub_result_havedata_get, _unbound.ub_result_havedata_set, "Havedata property")
802
803   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'}
804
805   def _get_rcode_str(self):
806       """Returns rcode in display representation form
807
808          :returns: string
809       """
810       return self.rcode2str[self.rcode]
811
812   __swig_getmethods__["rcode_str"] = _get_rcode_str
813   if _newclass:rcode_str = _swig_property(_get_rcode_str)
814
815   def _get_raw_data(self):
816       """Result data, a list of network order DNS rdata items.
817
818          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.
819       """
820       return self._ub_result_data(self)
821
822   __swig_getmethods__["rawdata"] = _get_raw_data
823   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.")
824
825   def _get_data(self):
826       if not self.havedata: return None
827       return ub_data(self._ub_result_data(self))
828
829   __swig_getmethods__["data"] = _get_data
830   __swig_getmethods__["packet"] = _packet
831   data = property(_get_data, doc="Returns :class:`ub_data` instance containing various decoding functions or None")
832
833%}
834
835}
836
837%exception ub_resolve
838%{
839  //printf("resolve_start(%lX)\n",(long unsigned int)arg1);
840  Py_BEGIN_ALLOW_THREADS
841  $function
842  Py_END_ALLOW_THREADS
843  //printf("resolve_stop()\n");
844%}
845
846%include "libunbound/unbound.h"
847
848%inline %{
849  //SWIG will see the ub_ctx as a class
850  struct ub_ctx {
851  };
852%}
853
854//ub_ctx_debugout void* parameter correction
855int ub_ctx_debugout(struct ub_ctx* ctx, FILE* out);
856
857// ================================================================================
858// ub_resolve_async - perform asynchronous resolution and validation
859// ================================================================================
860
861%typemap(in,numinputs=0,noblock=1) (int* async_id)
862{
863   int asyncid = -1;
864   $1 = &asyncid;
865}
866
867%apply PyObject* {void* mydata}
868
869/* result generation */
870%typemap(argout,noblock=1) (int* async_id)
871{
872  if(1) { /* new code block for variable on stack */
873    PyObject* tuple;
874    tuple = PyTuple_New(2);
875    PyTuple_SetItem(tuple, 0, $result);
876    PyTuple_SetItem(tuple, 1, SWIG_From_int(asyncid));
877    $result = tuple;
878  }
879}
880
881// Grab a Python function object as a Python object.
882%typemap(in) (PyObject *pyfunc) {
883  if (!PyCallable_Check($input))
884  {
885     PyErr_SetString(PyExc_TypeError, "Need a callable object!");
886     return NULL;
887  }
888  $1 = $input;
889}
890
891// Python callback workaround
892int _ub_resolve_async(struct ub_ctx* ctx, char* name, int rrtype, int rrclass, void* mydata, PyObject *pyfunc, int* async_id);
893
894%{
895   struct cb_data {
896      PyObject* data;
897      PyObject* func;
898   };
899
900   static void PythonCallBack(void* iddata, int status, struct ub_result* result)
901   {
902      PyObject *arglist;
903      PyObject *fresult;
904      struct cb_data* id;
905      id = (struct cb_data*) iddata;
906      arglist = Py_BuildValue("(OiO)",id->data,status, SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_ub_result, 0 |  0 ));   // Build argument list
907      fresult = PyEval_CallObject(id->func,arglist);     // Call Python
908      Py_DECREF(id->func);
909      Py_DECREF(id->data);
910      free(id);
911      ub_resolve_free(result);                  //free ub_result
912      //ub_resolve_free_dbg(result);                  //free ub_result
913      Py_DECREF(arglist);                           // Trash arglist
914      Py_XDECREF(fresult);
915   }
916
917   int _ub_resolve_async(struct ub_ctx* ctx, char* name, int rrtype, int rrclass, PyObject* mydata, PyObject *pyfunc, int* async_id) {
918      int r;
919      struct cb_data* id;
920      id = (struct cb_data*) malloc(sizeof(struct cb_data));
921      id->data = mydata;
922      id->func = pyfunc;
923
924      r = ub_resolve_async(ctx,name,rrtype,rrclass, (void *) id, PythonCallBack, async_id);
925      Py_INCREF(mydata);
926      Py_INCREF(pyfunc);
927      return r;
928   }
929
930%}
931
932%pythoncode %{
933    ub_resolve_async = _unbound._ub_resolve_async
934
935    def reverse(domain):
936        """Reverse domain name
937
938           Usable for reverse lookups when the IP address should be reversed
939        """
940        return '.'.join([a for a in domain.split(".")][::-1])
941
942    def idn2dname(idnname):
943        """Converts domain name in IDN format to canonic domain name
944
945           :param idnname: (unicode string) IDN name
946           :returns: (string) domain name
947        """
948        return '.'.join([encodings.idna.ToASCII(a) for a in idnname.split('.')])
949
950    def dname2idn(name):
951        """Converts canonic domain name in IDN format to unicode string
952
953            :param name: (string) domain name
954            :returns: (unicode string) domain name
955        """
956        return '.'.join([encodings.idna.ToUnicode(a) for a in name.split('.')])
957
958%}
959
960