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