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