1238106Sdes/*
2238106Sdes * libounbound.i: pyUnbound module (libunbound wrapper for Python)
3238106Sdes *
4238106Sdes * Copyright (c) 2009, Zdenek Vasicek (vasicek AT fit.vutbr.cz)
5238106Sdes *                     Marek Vavrusa  (xvavru00 AT stud.fit.vutbr.cz)
6238106Sdes *
7238106Sdes * This software is open source.
8238106Sdes *
9238106Sdes * Redistribution and use in source and binary forms, with or without
10238106Sdes * modification, are permitted provided that the following conditions
11238106Sdes * are met:
12238106Sdes *
13238106Sdes *    * Redistributions of source code must retain the above copyright notice,
14238106Sdes *      this list of conditions and the following disclaimer.
15238106Sdes *
16238106Sdes *    * Redistributions in binary form must reproduce the above copyright notice,
17238106Sdes *      this list of conditions and the following disclaimer in the documentation
18238106Sdes *      and/or other materials provided with the distribution.
19238106Sdes *
20238106Sdes *    * Neither the name of the organization nor the names of its
21238106Sdes *      contributors may be used to endorse or promote products derived from this
22238106Sdes *      software without specific prior written permission.
23238106Sdes *
24238106Sdes * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25238106Sdes * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26238106Sdes * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27238106Sdes * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
28238106Sdes * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29238106Sdes * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30238106Sdes * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31238106Sdes * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32238106Sdes * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33238106Sdes * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34238106Sdes * POSSIBILITY OF SUCH DAMAGE.
35238106Sdes */
36238106Sdes%module unbound
37238106Sdes%{
38238106Sdes   #include <sys/types.h>
39238106Sdes   #include <sys/socket.h>
40238106Sdes   #include <netinet/in.h>
41238106Sdes   #include <arpa/inet.h>
42238106Sdes   #include "libunbound/unbound.h"
43238106Sdes%}
44238106Sdes
45238106Sdes%pythoncode %{
46238106Sdes   import encodings.idna
47285206Sdes   try:
48285206Sdes       import builtins
49285206Sdes   except ImportError:
50285206Sdes       import __builtin__ as builtins
51285206Sdes
52285206Sdes   # Ensure compatibility with older python versions
53285206Sdes   if 'bytes' not in vars():
54285206Sdes       bytes = str
55285206Sdes
56285206Sdes   def ord(s):
57285206Sdes       if isinstance(s, int):
58285206Sdes           return s
59285206Sdes       return builtins.ord(s)
60238106Sdes%}
61238106Sdes
62238106Sdes//%include "doc.i"
63238106Sdes%include "file.i"
64238106Sdes
65238106Sdes%feature("docstring") strerror "Convert error value to a human readable string."
66238106Sdes
67238106Sdes// ================================================================================
68238106Sdes// ub_resolve - perform resolution and validation
69238106Sdes// ================================================================================
70238106Sdes%typemap(in,numinputs=0,noblock=1) (struct ub_result** result)
71238106Sdes{
72238106Sdes   struct ub_result* newubr;
73238106Sdes   $1 = &newubr;
74238106Sdes}
75238106Sdes
76238106Sdes/* result generation */
77238106Sdes%typemap(argout,noblock=1) (struct ub_result** result)
78238106Sdes{
79238106Sdes  if(1) { /* new code block for variable on stack */
80238106Sdes    PyObject* tuple;
81238106Sdes    tuple = PyTuple_New(2);
82238106Sdes    PyTuple_SetItem(tuple, 0, $result);
83238106Sdes    if (result == 0) {
84238106Sdes       PyTuple_SetItem(tuple, 1, SWIG_NewPointerObj(SWIG_as_voidptr(newubr), SWIGTYPE_p_ub_result, SWIG_POINTER_OWN |  0 ));
85238106Sdes    } else {
86238106Sdes       PyTuple_SetItem(tuple, 1, Py_None);
87238106Sdes    }
88238106Sdes    $result = tuple;
89238106Sdes  }
90238106Sdes}
91238106Sdes
92238106Sdes
93238106Sdes// ================================================================================
94238106Sdes// ub_ctx - validation context
95238106Sdes// ================================================================================
96238106Sdes%nodefaultctor ub_ctx; //no default constructor & destructor
97238106Sdes%nodefaultdtor ub_ctx;
98238106Sdes
99238106Sdes%newobject ub_ctx_create;
100238106Sdes%delobject ub_ctx_delete;
101238106Sdes%rename(_ub_ctx_delete) ub_ctx_delete;
102238106Sdes
103238106Sdes%newobject ub_resolve;
104238106Sdes
105238106Sdes%inline %{
106238106Sdes  void ub_ctx_free_dbg (struct ub_ctx* c) {
107238106Sdes    printf("******** UB_CTX free 0x%lX ************\n", (long unsigned int)c);
108238106Sdes    ub_ctx_delete(c);
109238106Sdes  }
110238106Sdes
111238106Sdes  //RR types
112238106Sdes  enum enum_rr_type
113238106Sdes  {
114238106Sdes    /**  a host address */
115238106Sdes    RR_TYPE_A = 1,
116238106Sdes    /**  an authoritative name server */
117238106Sdes    RR_TYPE_NS = 2,
118238106Sdes    /**  a mail destination (Obsolete - use MX) */
119238106Sdes    RR_TYPE_MD = 3,
120238106Sdes    /**  a mail forwarder (Obsolete - use MX) */
121238106Sdes    RR_TYPE_MF = 4,
122238106Sdes    /**  the canonical name for an alias */
123238106Sdes    RR_TYPE_CNAME = 5,
124238106Sdes    /**  marks the start of a zone of authority */
125238106Sdes    RR_TYPE_SOA = 6,
126238106Sdes    /**  a mailbox domain name (EXPERIMENTAL) */
127238106Sdes    RR_TYPE_MB = 7,
128238106Sdes    /**  a mail group member (EXPERIMENTAL) */
129238106Sdes    RR_TYPE_MG = 8,
130238106Sdes    /**  a mail rename domain name (EXPERIMENTAL) */
131238106Sdes    RR_TYPE_MR = 9,
132238106Sdes    /**  a null RR (EXPERIMENTAL) */
133238106Sdes    RR_TYPE_NULL = 10,
134238106Sdes    /**  a well known service description */
135238106Sdes    RR_TYPE_WKS = 11,
136238106Sdes    /**  a domain name pointer */
137238106Sdes    RR_TYPE_PTR = 12,
138238106Sdes    /**  host information */
139238106Sdes    RR_TYPE_HINFO = 13,
140238106Sdes    /**  mailbox or mail list information */
141238106Sdes    RR_TYPE_MINFO = 14,
142238106Sdes    /**  mail exchange */
143238106Sdes    RR_TYPE_MX = 15,
144238106Sdes    /**  text strings */
145238106Sdes    RR_TYPE_TXT = 16,
146238106Sdes    /**  RFC1183 */
147238106Sdes    RR_TYPE_RP = 17,
148238106Sdes    /**  RFC1183 */
149238106Sdes    RR_TYPE_AFSDB = 18,
150238106Sdes    /**  RFC1183 */
151238106Sdes    RR_TYPE_X25 = 19,
152238106Sdes    /**  RFC1183 */
153238106Sdes    RR_TYPE_ISDN = 20,
154238106Sdes    /**  RFC1183 */
155238106Sdes    RR_TYPE_RT = 21,
156238106Sdes    /**  RFC1706 */
157238106Sdes    RR_TYPE_NSAP = 22,
158238106Sdes    /**  RFC1348 */
159238106Sdes    RR_TYPE_NSAP_PTR = 23,
160238106Sdes    /**  2535typecode */
161238106Sdes    RR_TYPE_SIG = 24,
162238106Sdes    /**  2535typecode */
163238106Sdes    RR_TYPE_KEY = 25,
164238106Sdes    /**  RFC2163 */
165238106Sdes    RR_TYPE_PX = 26,
166238106Sdes    /**  RFC1712 */
167238106Sdes    RR_TYPE_GPOS = 27,
168238106Sdes    /**  ipv6 address */
169238106Sdes    RR_TYPE_AAAA = 28,
170238106Sdes    /**  LOC record  RFC1876 */
171238106Sdes    RR_TYPE_LOC = 29,
172238106Sdes    /**  2535typecode */
173238106Sdes    RR_TYPE_NXT = 30,
174238106Sdes    /**  draft-ietf-nimrod-dns-01.txt */
175238106Sdes    RR_TYPE_EID = 31,
176238106Sdes    /**  draft-ietf-nimrod-dns-01.txt */
177238106Sdes    RR_TYPE_NIMLOC = 32,
178238106Sdes    /**  SRV record RFC2782 */
179238106Sdes    RR_TYPE_SRV = 33,
180238106Sdes    /**  http://www.jhsoft.com/rfc/af-saa-0069.000.rtf */
181238106Sdes    RR_TYPE_ATMA = 34,
182238106Sdes    /**  RFC2915 */
183238106Sdes    RR_TYPE_NAPTR = 35,
184238106Sdes    /**  RFC2230 */
185238106Sdes    RR_TYPE_KX = 36,
186238106Sdes    /**  RFC2538 */
187238106Sdes    RR_TYPE_CERT = 37,
188238106Sdes    /**  RFC2874 */
189238106Sdes    RR_TYPE_A6 = 38,
190238106Sdes    /**  RFC2672 */
191238106Sdes    RR_TYPE_DNAME = 39,
192238106Sdes    /**  dnsind-kitchen-sink-02.txt */
193238106Sdes    RR_TYPE_SINK = 40,
194238106Sdes    /**  Pseudo OPT record... */
195238106Sdes    RR_TYPE_OPT = 41,
196238106Sdes    /**  RFC3123 */
197238106Sdes    RR_TYPE_APL = 42,
198238106Sdes    /**  draft-ietf-dnsext-delegation */
199238106Sdes    RR_TYPE_DS = 43,
200238106Sdes    /**  SSH Key Fingerprint */
201238106Sdes    RR_TYPE_SSHFP = 44,
202238106Sdes    /**  draft-richardson-ipseckey-rr-11.txt */
203238106Sdes    RR_TYPE_IPSECKEY = 45,
204238106Sdes    /**  draft-ietf-dnsext-dnssec-25 */
205238106Sdes    RR_TYPE_RRSIG = 46,
206238106Sdes    RR_TYPE_NSEC = 47,
207238106Sdes    RR_TYPE_DNSKEY = 48,
208238106Sdes    RR_TYPE_DHCID = 49,
209238106Sdes
210238106Sdes    RR_TYPE_NSEC3 = 50,
211238106Sdes    RR_TYPE_NSEC3PARAMS = 51,
212238106Sdes
213238106Sdes    RR_TYPE_UINFO = 100,
214238106Sdes    RR_TYPE_UID = 101,
215238106Sdes    RR_TYPE_GID = 102,
216238106Sdes    RR_TYPE_UNSPEC = 103,
217238106Sdes
218238106Sdes    RR_TYPE_TSIG = 250,
219238106Sdes    RR_TYPE_IXFR = 251,
220238106Sdes    RR_TYPE_AXFR = 252,
221238106Sdes    /**  A request for mailbox-related records (MB, MG or MR) */
222238106Sdes    RR_TYPE_MAILB = 253,
223238106Sdes    /**  A request for mail agent RRs (Obsolete - see MX) */
224238106Sdes    RR_TYPE_MAILA = 254,
225238106Sdes    /**  any type (wildcard) */
226238106Sdes    RR_TYPE_ANY = 255,
227238106Sdes
228238106Sdes    /* RFC 4431, 5074, DNSSEC Lookaside Validation */
229238106Sdes    RR_TYPE_DLV = 32769,
230238106Sdes  };
231238106Sdes
232238106Sdes  // RR classes
233238106Sdes  enum enum_rr_class
234238106Sdes  {
235238106Sdes    /** the Internet */
236238106Sdes    RR_CLASS_IN = 1,
237238106Sdes    /** Chaos class */
238238106Sdes    RR_CLASS_CH = 3,
239238106Sdes    /** Hesiod (Dyer 87) */
240238106Sdes    RR_CLASS_HS = 4,
241238106Sdes    /** None class, dynamic update */
242238106Sdes    RR_CLASS_NONE = 254,
243238106Sdes    /** Any class */
244238106Sdes    RR_CLASS_ANY = 255,
245238106Sdes  };
246238106Sdes%}
247238106Sdes
248238106Sdes%feature("docstring") ub_ctx "Unbound resolving and validation context.
249238106Sdes
250238106SdesThe validation context is created to hold the resolver status, validation keys and a small cache (containing messages, rrsets, roundtrip times, trusted keys, lameness information).
251238106Sdes
252238106Sdes**Usage**
253238106Sdes
254238106Sdes>>> import unbound
255238106Sdes>>> ctx = unbound.ub_ctx()
256238106Sdes>>> ctx.resolvconf(\"/etc/resolv.conf\")
257238106Sdes>>> status, result = ctx.resolve(\"www.google.com\", unbound.RR_TYPE_A, unbound.RR_CLASS_IN)
258238106Sdes>>> if status==0 and result.havedata:
259238106Sdes>>>    print \"Result:\",result.data.address_list
260238106SdesResult: ['74.125.43.147', '74.125.43.99', '74.125.43.103', '74.125.43.104']
261238106Sdes"
262238106Sdes
263238106Sdes%extend ub_ctx
264238106Sdes{
265238106Sdes %pythoncode %{
266238106Sdes        def __init__(self):
267238106Sdes            """Creates a resolving and validation context.
268238106Sdes
269238106Sdes               An exception is invoked if the process of creation an ub_ctx instance fails.
270238106Sdes            """
271238106Sdes            self.this = _unbound.ub_ctx_create()
272238106Sdes            if not self.this:
273238106Sdes                raise Exception("Fatal error: unbound context initialization failed")
274238106Sdes
275238106Sdes        #__swig_destroy__ = _unbound.ub_ctx_free_dbg
276238106Sdes        __swig_destroy__ = _unbound._ub_ctx_delete
277238106Sdes
278238106Sdes        #UB_CTX_METHODS_#
279238106Sdes        def add_ta(self,ta):
280238106Sdes            """Add a trust anchor to the given context.
281238106Sdes
282238106Sdes               The trust anchor is a string, on one line, that holds a valid DNSKEY or DS RR.
283238106Sdes
284238106Sdes               :param ta:
285238106Sdes                   string, with zone-format RR on one line. [domainname] [TTL optional] [type] [class optional] [rdata contents]
286238106Sdes               :returns: (int) 0 if OK, else error.
287238106Sdes            """
288238106Sdes            return _unbound.ub_ctx_add_ta(self,ta)
289238106Sdes            #parameters: struct ub_ctx *,char *,
290238106Sdes            #retvals: int
291238106Sdes
292238106Sdes        def add_ta_file(self,fname):
293238106Sdes            """Add trust anchors to the given context.
294238106Sdes
295238106Sdes               Pass name of a file with DS and DNSKEY records (like from dig or drill).
296238106Sdes
297238106Sdes               :param fname:
298238106Sdes                   filename of file with keyfile with trust anchors.
299238106Sdes               :returns: (int) 0 if OK, else error.
300238106Sdes            """
301238106Sdes            return _unbound.ub_ctx_add_ta_file(self,fname)
302238106Sdes            #parameters: struct ub_ctx *,char *,
303238106Sdes            #retvals: int
304238106Sdes
305238106Sdes        def config(self,fname):
306238106Sdes            """setup configuration for the given context.
307238106Sdes
308238106Sdes               :param fname:
309238106Sdes                   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.
310238106Sdes               :returns: (int) 0 if OK, else error.
311238106Sdes            """
312238106Sdes            return _unbound.ub_ctx_config(self,fname)
313238106Sdes            #parameters: struct ub_ctx *,char *,
314238106Sdes            #retvals: int
315238106Sdes
316238106Sdes        def debuglevel(self,d):
317238106Sdes            """Set debug verbosity for the context Output is directed to stderr.
318238106Sdes
319238106Sdes               :param d:
320238106Sdes                   debug level, 0 is off, 1 is very minimal, 2 is detailed, and 3 is lots.
321238106Sdes               :returns: (int) 0 if OK, else error.
322238106Sdes            """
323238106Sdes            return _unbound.ub_ctx_debuglevel(self,d)
324238106Sdes            #parameters: struct ub_ctx *,int,
325238106Sdes            #retvals: int
326238106Sdes
327238106Sdes        def debugout(self,out):
328238106Sdes            """Set debug output (and error output) to the specified stream.
329238106Sdes
330238106Sdes               Pass None to disable. Default is stderr.
331238106Sdes
332238106Sdes               :param out:
333238106Sdes                   File stream to log to.
334238106Sdes               :returns: (int) 0 if OK, else error.
335238106Sdes
336238106Sdes               **Usage:**
337238106Sdes
338238106Sdes                  In order to log into file, use
339238106Sdes
340238106Sdes                  ::
341238106Sdes
342238106Sdes                    ctx = unbound.ub_ctx()
343238106Sdes                    fw = fopen("debug.log")
344238106Sdes                    ctx.debuglevel(3)
345238106Sdes                    ctx.debugout(fw)
346238106Sdes
347238106Sdes                  Another option is to print the debug informations to stderr output
348238106Sdes
349238106Sdes                  ::
350238106Sdes
351238106Sdes                    ctx = unbound.ub_ctx()
352238106Sdes                    ctx.debuglevel(10)
353238106Sdes                    ctx.debugout(sys.stderr)
354238106Sdes            """
355238106Sdes            return _unbound.ub_ctx_debugout(self,out)
356238106Sdes            #parameters: struct ub_ctx *,void *,
357238106Sdes            #retvals: int
358238106Sdes
359238106Sdes        def hosts(self,fname="/etc/hosts"):
360238106Sdes            """Read list of hosts from the filename given.
361238106Sdes
362238106Sdes               Usually "/etc/hosts". These addresses are not flagged as DNSSEC secure when queried for.
363238106Sdes
364238106Sdes               :param fname:
365238106Sdes                   file name string. If None "/etc/hosts" is used.
366238106Sdes               :returns: (int) 0 if OK, else error.
367238106Sdes            """
368238106Sdes            return _unbound.ub_ctx_hosts(self,fname)
369238106Sdes            #parameters: struct ub_ctx *,char *,
370238106Sdes            #retvals: int
371238106Sdes
372238106Sdes        def print_local_zones(self):
373238106Sdes            """Print the local zones and their content (RR data) to the debug output.
374238106Sdes
375238106Sdes               :returns: (int) 0 if OK, else error.
376238106Sdes            """
377238106Sdes            return _unbound.ub_ctx_print_local_zones(self)
378238106Sdes            #parameters: struct ub_ctx *,
379238106Sdes            #retvals: int
380238106Sdes
381238106Sdes        def resolvconf(self,fname="/etc/resolv.conf"):
382238106Sdes            """Read list of nameservers to use from the filename given.
383238106Sdes
384238106Sdes               Usually "/etc/resolv.conf". Uses those nameservers as caching proxies. If they do not support DNSSEC, validation may fail.
385238106Sdes
386238106Sdes               Only nameservers are picked up, the searchdomain, ndots and other settings from resolv.conf(5) are ignored.
387238106Sdes
388238106Sdes               :param fname:
389238106Sdes                   file name string. If None "/etc/resolv.conf" is used.
390238106Sdes               :returns: (int) 0 if OK, else error.
391238106Sdes            """
392238106Sdes            return _unbound.ub_ctx_resolvconf(self,fname)
393238106Sdes            #parameters: struct ub_ctx *,char *,
394238106Sdes            #retvals: int
395238106Sdes
396238106Sdes        def set_async(self,dothread):
397238106Sdes            """Set a context behaviour for asynchronous action.
398238106Sdes
399238106Sdes               :param dothread:
400238106Sdes                   if True, enables threading and a call to :meth:`resolve_async` creates a thread to handle work in the background.
401238106Sdes                   If False, a process is forked to handle work in the background.
402238106Sdes                   Changes to this setting after :meth:`async` calls have been made have no effect (delete and re-create the context to change).
403238106Sdes               :returns: (int) 0 if OK, else error.
404238106Sdes            """
405238106Sdes            return _unbound.ub_ctx_async(self,dothread)
406238106Sdes            #parameters: struct ub_ctx *,int,
407238106Sdes            #retvals: int
408238106Sdes
409238106Sdes        def set_fwd(self,addr):
410238106Sdes            """Set machine to forward DNS queries to, the caching resolver to use.
411238106Sdes
412238106Sdes               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.
413238106Sdes
414238106Sdes               To read the list of nameservers from /etc/resolv.conf (from DHCP or so), use the call :meth:`resolvconf`.
415238106Sdes
416238106Sdes               :param addr:
417238106Sdes                   address, IP4 or IP6 in string format. If the addr is None, forwarding is disabled.
418238106Sdes               :returns: (int) 0 if OK, else error.
419238106Sdes            """
420238106Sdes            return _unbound.ub_ctx_set_fwd(self,addr)
421238106Sdes            #parameters: struct ub_ctx *,char *,
422238106Sdes            #retvals: int
423238106Sdes
424238106Sdes        def set_option(self,opt,val):
425238106Sdes            """Set an option for the context.
426238106Sdes
427238106Sdes               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).
428238106Sdes
429238106Sdes               :param opt:
430238106Sdes                   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.
431238106Sdes               :param val:
432238106Sdes                   value of the option.
433238106Sdes               :returns: (int) 0 if OK, else error.
434238106Sdes            """
435238106Sdes            return _unbound.ub_ctx_set_option(self,opt,val)
436238106Sdes            #parameters: struct ub_ctx *,char *,char *,
437238106Sdes            #retvals: int
438238106Sdes
439238106Sdes        def trustedkeys(self,fname):
440238106Sdes            """Add trust anchors to the given context.
441238106Sdes
442238106Sdes               Pass the name of a bind-style config file with trusted-keys{}.
443238106Sdes
444238106Sdes               :param fname:
445238106Sdes                   filename of file with bind-style config entries with trust anchors.
446238106Sdes               :returns: (int) 0 if OK, else error.
447238106Sdes            """
448238106Sdes            return _unbound.ub_ctx_trustedkeys(self,fname)
449238106Sdes            #parameters: struct ub_ctx *,char *,
450238106Sdes            #retvals: int
451238106Sdes        #_UB_CTX_METHODS#
452238106Sdes
453238106Sdes        def zone_print(self):
454238106Sdes            """Print local zones using debougout"""
455238106Sdes            _unbound.ub_ctx_print_local_zones(self)
456238106Sdes
457238106Sdes        def zone_add(self,zonename,zonetype):
458238106Sdes            """Add new local zone
459238106Sdes
460238106Sdes               :param zonename: zone domain name (e.g. myzone.)
461238106Sdes               :param zonetype: type of the zone ("static",...)
462238106Sdes               :returns: (int) 0 if OK, else error.
463238106Sdes            """
464238106Sdes            return _unbound.ub_ctx_zone_add(self,zonename, zonetype)
465238106Sdes            #parameters: struct ub_ctx *,char*, char*
466238106Sdes            #retvals: int
467238106Sdes
468238106Sdes        def zone_remove(self,zonename):
469238106Sdes            """Remove local zone
470238106Sdes
471238106Sdes               If exists, removes local zone with all the RRs.
472238106Sdes
473238106Sdes               :param zonename: zone domain name
474238106Sdes               :returns: (int) 0 if OK, else error.
475238106Sdes            """
476238106Sdes            return _unbound.ub_ctx_zone_remove(self,zonename)
477238106Sdes            #parameters: struct ub_ctx *,char*
478238106Sdes            #retvals: int
479238106Sdes
480238106Sdes        def data_add(self,rrdata):
481238106Sdes            """Add new local RR data
482238106Sdes
483238106Sdes               :param rrdata: string, in zone-format on one line. [domainname] [TTL optional] [type] [class optional] [rdata contents]
484238106Sdes               :returns: (int) 0 if OK, else error.
485238106Sdes
486238106Sdes               **Usage**
487238106Sdes                  The local data ...
488238106Sdes
489238106Sdes                  ::
490238106Sdes
491238106Sdes                    >>> ctx = unbound.ub_ctx()
492238106Sdes                    >>> ctx.zone_add("mydomain.net.","static")
493238106Sdes                    0
494238106Sdes                    >>> status = ctx.data_add("test.mydomain.net. IN A 192.168.1.1")
495238106Sdes                    0
496238106Sdes                    >>> status, result = ctx.resolve("test.mydomain.net")
497238106Sdes                    >>> if status==0 and result.havedata:
498238106Sdes                    >>>    print \"Result:\",result.data.address_list
499238106Sdes                    Result: ['192.168.1.1']
500238106Sdes
501238106Sdes            """
502238106Sdes            return _unbound.ub_ctx_data_add(self,rrdata)
503238106Sdes            #parameters: struct ub_ctx *,char*
504238106Sdes            #retvals: int
505238106Sdes
506238106Sdes        def data_remove(self,rrdata):
507238106Sdes            """Remove local RR data
508238106Sdes
509238106Sdes               If exists, remove resource record from local zone
510238106Sdes
511238106Sdes               :param rrdata: string, in zone-format on one line. [domainname] [TTL optional] [type] [class optional] [rdata contents]
512238106Sdes               :returns: (int) 0 if OK, else error.
513238106Sdes            """
514238106Sdes            return _unbound.ub_ctx_data_remove(self,rrdata)
515238106Sdes            #parameters: struct ub_ctx *,char*
516238106Sdes            #retvals: int
517238106Sdes
518238106Sdes        #UB_METHODS_#
519238106Sdes        def cancel(self,async_id):
520238106Sdes            """Cancel an async query in progress.
521238106Sdes
522238106Sdes               Its callback will not be called.
523238106Sdes
524238106Sdes               :param async_id:
525238106Sdes                   which query to cancel.
526238106Sdes               :returns: (int) 0 if OK, else error.
527238106Sdes            """
528238106Sdes            return _unbound.ub_cancel(self,async_id)
529238106Sdes            #parameters: struct ub_ctx *,int,
530238106Sdes            #retvals: int
531238106Sdes
532238106Sdes        def get_fd(self):
533238106Sdes            """Get file descriptor.
534238106Sdes
535238106Sdes               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.
536238106Sdes
537238106Sdes               :returns: (int) -1 on error, or file descriptor to use select(2) with.
538238106Sdes            """
539238106Sdes            return _unbound.ub_fd(self)
540238106Sdes            #parameters: struct ub_ctx *,
541238106Sdes            #retvals: int
542238106Sdes
543238106Sdes        def poll(self):
544238106Sdes            """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.
545238106Sdes
546238106Sdes               :returns: (int) 0 if nothing to read, or nonzero if a result is available. If nonzero, call ctx_process() to do callbacks.
547238106Sdes            """
548238106Sdes            return _unbound.ub_poll(self)
549238106Sdes            #parameters: struct ub_ctx *,
550238106Sdes            #retvals: int
551238106Sdes
552238106Sdes        def process(self):
553238106Sdes            """Call this routine to continue processing results from the validating resolver (when the fd becomes readable).
554238106Sdes
555238106Sdes               Will perform necessary callbacks.
556238106Sdes
557238106Sdes               :returns: (int) 0 if OK, else error.
558238106Sdes            """
559238106Sdes            return _unbound.ub_process(self)
560238106Sdes            #parameters: struct ub_ctx *,
561238106Sdes            #retvals: int
562238106Sdes
563238106Sdes        def resolve(self,name,rrtype=RR_TYPE_A,rrclass=RR_CLASS_IN):
564238106Sdes            """Perform resolution and validation of the target name.
565238106Sdes
566238106Sdes               :param name:
567238106Sdes                   domain name in text format (a string or unicode string). IDN domain name have to be passed as a unicode string.
568238106Sdes               :param rrtype:
569238106Sdes                   type of RR in host order (optional argument). Default value is RR_TYPE_A (A class).
570238106Sdes               :param rrclass:
571238106Sdes                   class of RR in host order (optional argument). Default value is RR_CLASS_IN (for internet).
572238106Sdes               :returns: * (int) 0 if OK, else error.
573238106Sdes                         * (: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).
574238106Sdes            """
575285206Sdes            if isinstance(name, bytes): #probably IDN
576285206Sdes                return _unbound.ub_resolve(self,name,rrtype,rrclass)
577285206Sdes            else:
578238106Sdes                return _unbound.ub_resolve(self,idn2dname(name),rrtype,rrclass)
579238106Sdes            #parameters: struct ub_ctx *,char *,int,int,
580238106Sdes            #retvals: int,struct ub_result **
581238106Sdes
582238106Sdes        def resolve_async(self,name,mydata,callback,rrtype=RR_TYPE_A,rrclass=RR_CLASS_IN):
583238106Sdes            """Perform resolution and validation of the target name.
584238106Sdes
585238106Sdes               Asynchronous, after a while, the callback will be called with your data and the result.
586238106Sdes               If an error happens during processing, your callback will be called with error set to a nonzero value (and result==None).
587238106Sdes
588238106Sdes               :param name:
589238106Sdes                   domain name in text format (a string or unicode string). IDN domain name have to be passed as a unicode string.
590238106Sdes               :param mydata:
591238106Sdes                   this data is your own data (you can pass arbitrary python object or None) which are passed on to the callback function.
592238106Sdes               :param callback:
593238106Sdes                   call-back function which is called on completion of the resolution.
594238106Sdes               :param rrtype:
595238106Sdes                   type of RR in host order (optional argument). Default value is RR_TYPE_A (A class).
596238106Sdes               :param rrclass:
597238106Sdes                   class of RR in host order (optional argument). Default value is RR_CLASS_IN (for internet).
598238106Sdes               :returns: * (int) 0 if OK, else error.
599238106Sdes                         * (int) async_id, an identifier number is returned for the query as it is in progress. It can be used to cancel the query.
600238106Sdes
601238106Sdes               **Call-back function:**
602238106Sdes                    The call-back function looks as the follows::
603238106Sdes
604238106Sdes                        def call_back(mydata, status, result):
605238106Sdes                            pass
606238106Sdes
607238106Sdes                    **Parameters:**
608238106Sdes                        * `mydata` - mydata object
609238106Sdes                        * `status` - 0 when a result has been found
610238106Sdes                        * `result` - the result structure. The result may be None, in that case err is set.
611238106Sdes
612238106Sdes            """
613285206Sdes            if isinstance(name, bytes): #probably IDN
614285206Sdes                return _unbound._ub_resolve_async(self,name,rrtype,rrclass,mydata,callback)
615285206Sdes            else:
616238106Sdes                return _unbound._ub_resolve_async(self,idn2dname(name),rrtype,rrclass,mydata,callback)
617238106Sdes            #parameters: struct ub_ctx *,char *,int,int,void *,ub_callback_t,
618238106Sdes            #retvals: int, int
619238106Sdes
620238106Sdes        def wait(self):
621238106Sdes            """Wait for a context to finish with results.
622238106Sdes
623238106Sdes               Calls  after the wait for you. After the wait, there are no more outstanding asynchronous queries.
624238106Sdes
625238106Sdes               :returns: (int) 0 if OK, else error.
626238106Sdes            """
627238106Sdes            return _unbound.ub_wait(self)
628238106Sdes            #parameters: struct ub_ctx *,
629238106Sdes            #retvals: int
630238106Sdes
631238106Sdes        #_UB_METHODS#
632238106Sdes %}
633238106Sdes}
634238106Sdes
635238106Sdes
636238106Sdes// ================================================================================
637238106Sdes// ub_result - validation and resolution results
638238106Sdes// ================================================================================
639238106Sdes%nodefaultctor ub_result; //no default constructor & destructor
640238106Sdes%nodefaultdtor ub_result;
641238106Sdes
642238106Sdes%delobject ub_resolve_free;
643238106Sdes%rename(_ub_resolve_free) ub_resolve_free;
644238106Sdes
645238106Sdes%inline %{
646238106Sdes  void ub_resolve_free_dbg (struct ub_result* r) {
647238106Sdes    printf("******** UB_RESOLVE free 0x%lX ************\n", (long unsigned int)r);
648238106Sdes    ub_resolve_free(r);
649238106Sdes  }
650238106Sdes%}
651238106Sdes
652238106Sdes%feature("docstring") ub_result "The validation and resolution results."
653238106Sdes
654238106Sdes//ub_result.rcode
655238106Sdes%inline %{
656238106Sdes  enum result_enum_rcode {
657238106Sdes    RCODE_NOERROR = 0,
658238106Sdes    RCODE_FORMERR = 1,
659238106Sdes    RCODE_SERVFAIL = 2,
660238106Sdes    RCODE_NXDOMAIN = 3,
661238106Sdes    RCODE_NOTIMPL = 4,
662238106Sdes    RCODE_REFUSED = 5,
663238106Sdes    RCODE_YXDOMAIN = 6,
664238106Sdes    RCODE_YXRRSET = 7,
665238106Sdes    RCODE_NXRRSET = 8,
666238106Sdes    RCODE_NOTAUTH = 9,
667238106Sdes    RCODE_NOTZONE = 10
668238106Sdes  };
669238106Sdes%}
670238106Sdes
671238106Sdes%pythoncode %{
672238106Sdes   class ub_data:
673238106Sdes      """Class which makes the resolution results accessible"""
674238106Sdes      def __init__(self, data):
675238106Sdes         """Creates ub_data class
676238106Sdes            :param data: a list of the result data in RAW format
677238106Sdes         """
678238106Sdes         if data == None:
679238106Sdes            raise Exception("ub_data init: No data")
680238106Sdes         self.data = data
681238106Sdes
682238106Sdes      def __str__(self):
683238106Sdes         """Represents data as string"""
684238106Sdes         return ';'.join([' '.join(map(lambda x:"%02X" % ord(x),a)) for a in self.data])
685238106Sdes
686238106Sdes      @staticmethod
687238106Sdes      def dname2str(s, ofs=0, maxlen=0):
688238106Sdes         """Parses DNAME and produces a list of labels
689238106Sdes
690238106Sdes            :param ofs: where the conversion should start to parse data
691238106Sdes            :param maxlen: maximum length (0 means parse to the end)
692238106Sdes            :returns: list of labels (string)
693238106Sdes         """
694238106Sdes         if not s:
695238106Sdes            return []
696238106Sdes
697238106Sdes         res = []
698238106Sdes         slen = len(s)
699238106Sdes         if maxlen > 0:
700238106Sdes            slen = min(slen, maxlen)
701238106Sdes
702238106Sdes         idx = ofs
703238106Sdes         while (idx < slen):
704238106Sdes            complen = ord(s[idx])
705285206Sdes            # In python 3.x `str()` converts the string to unicode which is the expected text string type
706285206Sdes            res.append(str(s[idx+1:idx+1+complen].decode()))
707238106Sdes            idx += complen + 1
708238106Sdes
709238106Sdes         return res
710238106Sdes
711238106Sdes      def as_raw_data(self):
712238106Sdes         """Returns a list of RAW strings"""
713238106Sdes         return self.data
714238106Sdes
715238106Sdes      raw = property(as_raw_data, doc="Returns RAW data (a list of binary encoded strings). See :meth:`as_raw_data`")
716238106Sdes
717238106Sdes      def as_mx_list(self):
718238106Sdes         """Represents data as a list of MX records (query for RR_TYPE_MX)
719238106Sdes
720238106Sdes            :returns: list of tuples (priority, dname)
721238106Sdes         """
722238106Sdes         return [(256*ord(rdf[0])+ord(rdf[1]),'.'.join([a for a in self.dname2str(rdf,2)])) for rdf in self.data]
723238106Sdes
724238106Sdes      mx_list = property(as_mx_list, doc="Returns a list of tuples containing priority and domain names. See :meth:`as_mx_list`")
725238106Sdes
726238106Sdes      def as_idn_mx_list(self):
727238106Sdes         """Represents data as a list of MX records (query for RR_TYPE_MX)
728238106Sdes
729238106Sdes            :returns: list of tuples (priority, unicode dname)
730238106Sdes         """
731238106Sdes         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]
732238106Sdes
733238106Sdes      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`")
734238106Sdes
735238106Sdes      def as_address_list(self):
736238106Sdes         """Represents data as a list of IP addresses (query for RR_TYPE_PTR)
737238106Sdes
738238106Sdes            :returns: list of strings
739238106Sdes         """
740238106Sdes         return ['.'.join(map(lambda x:str(ord(x)),a)) for a in self.data]
741238106Sdes
742238106Sdes      address_list = property(as_address_list, doc="Returns a list of IP addresses. See :meth:`as_address_list`")
743238106Sdes
744238106Sdes      def as_domain_list(self):
745238106Sdes         """Represents data as a list of domain names (query for RR_TYPE_A)
746238106Sdes
747238106Sdes            :returns: list of strings
748238106Sdes         """
749238106Sdes         return map(lambda x:'.'.join(self.dname2str(x)), self.data)
750238106Sdes
751238106Sdes      domain_list = property(as_domain_list, doc="Returns a list of domain names. See :meth:`as_domain_list`")
752238106Sdes
753238106Sdes      def as_idn_domain_list(self):
754238106Sdes         """Represents data as a list of unicode domain names (query for RR_TYPE_A)
755238106Sdes
756238106Sdes            :returns: list of strings
757238106Sdes         """
758238106Sdes         return map(lambda x: '.'.join([encodings.idna.ToUnicode(a) for a in self.dname2str(x)]), self.data)
759238106Sdes
760238106Sdes      domain_list_idn = property(as_idn_domain_list, doc="Returns a list of IDN domain names. See :meth:`as_idn_domain_list`")
761238106Sdes%}
762238106Sdes
763238106Sdes%extend ub_result
764238106Sdes{
765238106Sdes
766238106Sdes  %rename(_data) data;
767238106Sdes
768238106Sdes  PyObject* _ub_result_data(struct ub_result* result) {
769238106Sdes    PyObject  *list;
770238106Sdes     int i,cnt;
771238106Sdes     (void)self;
772238106Sdes     if ((result == 0) || (!result->havedata) || (result->data == 0))
773238106Sdes        return Py_None;
774238106Sdes
775238106Sdes     for (cnt=0,i=0;;i++,cnt++)
776238106Sdes         if (result->data[i] == 0)
777238106Sdes            break;
778238106Sdes
779238106Sdes     list = PyList_New(cnt);
780238106Sdes     for (i=0;i<cnt;i++)
781285206Sdes         PyList_SetItem(list, i, PyBytes_FromStringAndSize(result->data[i],result->len[i]));
782238106Sdes
783238106Sdes     return list;
784238106Sdes  }
785238106Sdes
786238106Sdes  PyObject* _packet() {
787285206Sdes      return PyBytes_FromStringAndSize($self->answer_packet, $self->answer_len);
788238106Sdes  }
789238106Sdes
790238106Sdes %pythoncode %{
791238106Sdes   def __init__(self):
792238106Sdes       raise Exception("This class can't be created directly.")
793238106Sdes
794238106Sdes   #__swig_destroy__ = _unbound.ub_resolve_free_dbg
795238106Sdes   __swig_destroy__ = _unbound._ub_resolve_free
796238106Sdes
797238106Sdes   #havedata = property(_unbound.ub_result_havedata_get, _unbound.ub_result_havedata_set, "Havedata property")
798238106Sdes
799238106Sdes   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'}
800238106Sdes
801238106Sdes   def _get_rcode_str(self):
802238106Sdes       """Returns rcode in display representation form
803238106Sdes
804238106Sdes          :returns: string
805238106Sdes       """
806238106Sdes       return self.rcode2str[self.rcode]
807238106Sdes
808238106Sdes   __swig_getmethods__["rcode_str"] = _get_rcode_str
809238106Sdes   if _newclass:rcode_str = _swig_property(_get_rcode_str)
810238106Sdes
811238106Sdes   def _get_raw_data(self):
812238106Sdes       """Result data, a list of network order DNS rdata items.
813238106Sdes
814238106Sdes          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.
815238106Sdes       """
816238106Sdes       return self._ub_result_data(self)
817238106Sdes
818238106Sdes   __swig_getmethods__["rawdata"] = _get_raw_data
819238106Sdes   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.")
820238106Sdes
821238106Sdes   def _get_data(self):
822238106Sdes       if not self.havedata: return None
823238106Sdes       return ub_data(self._ub_result_data(self))
824238106Sdes
825238106Sdes   __swig_getmethods__["data"] = _get_data
826238106Sdes   __swig_getmethods__["packet"] = _packet
827238106Sdes   data = property(_get_data, doc="Returns :class:`ub_data` instance containing various decoding functions or None")
828238106Sdes
829238106Sdes%}
830238106Sdes
831238106Sdes}
832238106Sdes
833238106Sdes%exception ub_resolve
834238106Sdes%{
835238106Sdes  //printf("resolve_start(%lX)\n",(long unsigned int)arg1);
836238106Sdes  Py_BEGIN_ALLOW_THREADS
837238106Sdes  $function
838238106Sdes  Py_END_ALLOW_THREADS
839238106Sdes  //printf("resolve_stop()\n");
840238106Sdes%}
841238106Sdes
842238106Sdes%include "libunbound/unbound.h"
843238106Sdes
844238106Sdes%inline %{
845238106Sdes  //SWIG will see the ub_ctx as a class
846238106Sdes  struct ub_ctx {
847238106Sdes  };
848238106Sdes%}
849238106Sdes
850238106Sdes//ub_ctx_debugout void* parameter correction
851238106Sdesint ub_ctx_debugout(struct ub_ctx* ctx, FILE* out);
852238106Sdes
853238106Sdes// ================================================================================
854238106Sdes// ub_resolve_async - perform asynchronous resolution and validation
855238106Sdes// ================================================================================
856238106Sdes
857238106Sdes%typemap(in,numinputs=0,noblock=1) (int* async_id)
858238106Sdes{
859238106Sdes   int asyncid = -1;
860238106Sdes   $1 = &asyncid;
861238106Sdes}
862238106Sdes
863238106Sdes%apply PyObject* {void* mydata}
864238106Sdes
865238106Sdes/* result generation */
866238106Sdes%typemap(argout,noblock=1) (int* async_id)
867238106Sdes{
868238106Sdes  if(1) { /* new code block for variable on stack */
869238106Sdes    PyObject* tuple;
870238106Sdes    tuple = PyTuple_New(2);
871238106Sdes    PyTuple_SetItem(tuple, 0, $result);
872238106Sdes    PyTuple_SetItem(tuple, 1, SWIG_From_int(asyncid));
873238106Sdes    $result = tuple;
874238106Sdes  }
875238106Sdes}
876238106Sdes
877238106Sdes// Grab a Python function object as a Python object.
878238106Sdes%typemap(in) (PyObject *pyfunc) {
879238106Sdes  if (!PyCallable_Check($input))
880238106Sdes  {
881238106Sdes     PyErr_SetString(PyExc_TypeError, "Need a callable object!");
882238106Sdes     return NULL;
883238106Sdes  }
884238106Sdes  $1 = $input;
885238106Sdes}
886238106Sdes
887238106Sdes// Python callback workaround
888238106Sdesint _ub_resolve_async(struct ub_ctx* ctx, char* name, int rrtype, int rrclass, void* mydata, PyObject *pyfunc, int* async_id);
889238106Sdes
890238106Sdes%{
891238106Sdes   struct cb_data {
892238106Sdes      PyObject* data;
893238106Sdes      PyObject* func;
894238106Sdes   };
895238106Sdes
896238106Sdes   static void PythonCallBack(void* iddata, int status, struct ub_result* result)
897238106Sdes   {
898238106Sdes      PyObject *arglist;
899238106Sdes      PyObject *fresult;
900238106Sdes      struct cb_data* id;
901238106Sdes      id = (struct cb_data*) iddata;
902238106Sdes      arglist = Py_BuildValue("(OiO)",id->data,status, SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_ub_result, 0 |  0 ));   // Build argument list
903238106Sdes      fresult = PyEval_CallObject(id->func,arglist);     // Call Python
904238106Sdes      Py_DECREF(id->func);
905238106Sdes      Py_DECREF(id->data);
906238106Sdes      free(id);
907238106Sdes      ub_resolve_free(result);                  //free ub_result
908238106Sdes      //ub_resolve_free_dbg(result);                  //free ub_result
909238106Sdes      Py_DECREF(arglist);                           // Trash arglist
910238106Sdes      Py_XDECREF(fresult);
911238106Sdes   }
912238106Sdes
913238106Sdes   int _ub_resolve_async(struct ub_ctx* ctx, char* name, int rrtype, int rrclass, PyObject* mydata, PyObject *pyfunc, int* async_id) {
914238106Sdes      int r;
915238106Sdes      struct cb_data* id;
916238106Sdes      id = (struct cb_data*) malloc(sizeof(struct cb_data));
917238106Sdes      id->data = mydata;
918238106Sdes      id->func = pyfunc;
919238106Sdes
920238106Sdes      r = ub_resolve_async(ctx,name,rrtype,rrclass, (void *) id, PythonCallBack, async_id);
921238106Sdes      Py_INCREF(mydata);
922238106Sdes      Py_INCREF(pyfunc);
923238106Sdes      return r;
924238106Sdes   }
925238106Sdes
926238106Sdes%}
927238106Sdes
928238106Sdes%pythoncode %{
929238106Sdes    ub_resolve_async = _unbound._ub_resolve_async
930238106Sdes
931238106Sdes    def reverse(domain):
932238106Sdes        """Reverse domain name
933238106Sdes
934238106Sdes           Usable for reverse lookups when the IP address should be reversed
935238106Sdes        """
936238106Sdes        return '.'.join([a for a in domain.split(".")][::-1])
937238106Sdes
938238106Sdes    def idn2dname(idnname):
939238106Sdes        """Converts domain name in IDN format to canonic domain name
940238106Sdes
941238106Sdes           :param idnname: (unicode string) IDN name
942238106Sdes           :returns: (string) domain name
943238106Sdes        """
944238106Sdes        return '.'.join([encodings.idna.ToASCII(a) for a in idnname.split('.')])
945238106Sdes
946238106Sdes    def dname2idn(name):
947238106Sdes        """Converts canonic domain name in IDN format to unicode string
948238106Sdes
949238106Sdes            :param name: (string) domain name
950238106Sdes            :returns: (unicode string) domain name
951238106Sdes        """
952238106Sdes        return '.'.join([encodings.idna.ToUnicode(a) for a in name.split('.')])
953238106Sdes
954238106Sdes%}
955238106Sdes
956