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