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