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