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