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