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