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