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