Package pyxmpp :: Module resolver
[hide private]

Source Code for Module pyxmpp.resolver

  1  # 
  2  # (C) Copyright 2003-2010 Jacek Konieczny <jajcus@jajcus.net> 
  3  # 
  4  # This program is free software; you can redistribute it and/or modify 
  5  # it under the terms of the GNU Lesser General Public License Version 
  6  # 2.1 as published by the Free Software Foundation. 
  7  # 
  8  # This program is distributed in the hope that it will be useful, 
  9  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 10  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 11  # GNU Lesser General Public License for more details. 
 12  # 
 13  # You should have received a copy of the GNU Lesser General Public 
 14  # License along with this program; if not, write to the Free Software 
 15  # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
 16  # 
 17   
 18  """DNS resolever with SRV record support. 
 19   
 20  Normative reference: 
 21    - `RFC 1035 <http://www.ietf.org/rfc/rfc1035.txt>`__ 
 22    - `RFC 2782 <http://www.ietf.org/rfc/rfc2782.txt>`__ 
 23  """ 
 24   
 25  __revision__="$Id: resolver.py 714 2010-04-05 10:20:10Z jajcus $" 
 26  __docformat__="restructuredtext en" 
 27   
 28  import re 
 29  import socket 
 30  import dns.resolver 
 31  import dns.name 
 32  import dns.exception 
 33  import random 
 34  from encodings import idna 
 35   
 36  service_aliases={"xmpp-server": ("jabber-server","jabber")} 
 37  ip_re=re.compile(r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}") 
 38   
39 -def shuffle_srv(records):
40 """Randomly reorder SRV records using their weights. 41 42 :Parameters: 43 - `records`: SRV records to shuffle. 44 :Types: 45 - `records`: sequence of `dns.rdtypes.IN.SRV` 46 47 :return: reordered records. 48 :returntype: `list` of `dns.rdtypes.IN.SRV`""" 49 if not records: 50 return [] 51 ret=[] 52 while len(records)>1: 53 weight_sum=0 54 for rr in records: 55 weight_sum+=rr.weight+0.1 56 thres=random.random()*weight_sum 57 weight_sum=0 58 for rr in records: 59 weight_sum+=rr.weight+0.1 60 if thres<weight_sum: 61 records.remove(rr) 62 ret.append(rr) 63 break 64 ret.append(records[0]) 65 return ret
66
67 -def reorder_srv(records):
68 """Reorder SRV records using their priorities and weights. 69 70 :Parameters: 71 - `records`: SRV records to shuffle. 72 :Types: 73 - `records`: `list` of `dns.rdtypes.IN.SRV` 74 75 :return: reordered records. 76 :returntype: `list` of `dns.rdtypes.IN.SRV`""" 77 records=list(records) 78 records.sort() 79 ret=[] 80 tmp=[] 81 for rr in records: 82 if not tmp or rr.priority==tmp[0].priority: 83 tmp.append(rr) 84 continue 85 ret+=shuffle_srv(tmp) 86 if tmp: 87 ret+=shuffle_srv(tmp) 88 return ret
89
90 -def resolve_srv(domain,service,proto="tcp"):
91 """Resolve service domain to server name and port number using SRV records. 92 93 A built-in service alias table will be used to lookup also some obsolete 94 record names. 95 96 :Parameters: 97 - `domain`: domain name. 98 - `service`: service name. 99 - `proto`: protocol name. 100 :Types: 101 - `domain`: `unicode` or `str` 102 - `service`: `unicode` or `str` 103 - `proto`: `str` 104 105 :return: host names and port numbers for the service or None. 106 :returntype: `list` of (`str`,`int`)""" 107 names_to_try=[u"_%s._%s.%s" % (service,proto,domain)] 108 if service_aliases.has_key(service): 109 for a in service_aliases[service]: 110 names_to_try.append(u"_%s._%s.%s" % (a,proto,domain)) 111 for name in names_to_try: 112 name=idna.ToASCII(name) 113 try: 114 r=dns.resolver.query(name, 'SRV') 115 except dns.exception.DNSException: 116 continue 117 if not r: 118 continue 119 return [(rr.target.to_text(),rr.port) for rr in reorder_srv(r)] 120 return None
121
122 -def getaddrinfo(host,port,family=0,socktype=socket.SOCK_STREAM,proto=0,allow_cname=True):
123 """Resolve host and port into addrinfo struct. 124 125 Does the same thing as socket.getaddrinfo, but using `pyxmpp.resolver`. This 126 makes it possible to reuse data (A records from the additional section of 127 DNS reply) returned with SRV records lookup done using this module. 128 129 :Parameters: 130 - `host`: service domain name. 131 - `port`: service port number or name. 132 - `family`: address family. 133 - `socktype`: socket type. 134 - `proto`: protocol number or name. 135 - `allow_cname`: when False CNAME responses are not allowed. 136 :Types: 137 - `host`: `unicode` or `str` 138 - `port`: `int` or `str` 139 - `family`: `int` 140 - `socktype`: `int` 141 - `proto`: `int` or `str` 142 - `allow_cname`: `bool` 143 144 :return: list of (family, socktype, proto, canonname, sockaddr). 145 :returntype: `list` of (`int`, `int`, `int`, `str`, (`str`, `int`))""" 146 ret=[] 147 if proto==0: 148 proto=socket.getprotobyname("tcp") 149 elif type(proto)!=int: 150 proto=socket.getprotobyname(proto) 151 if type(port)!=int: 152 port=socket.getservbyname(port,proto) 153 if family not in (0,socket.AF_INET): 154 raise NotImplementedError,"Protocol family other than AF_INET not supported, yet" 155 if ip_re.match(host): 156 return [(socket.AF_INET,socktype,proto,host,(host,port))] 157 host=idna.ToASCII(host) 158 try: 159 r=dns.resolver.query(host, 'A') 160 except dns.exception.DNSException: 161 r=dns.resolver.query(host+".", 'A') 162 if not allow_cname and r.rrset.name!=dns.name.from_text(host): 163 raise ValueError,"Unexpected CNAME record found for %s" % (host,) 164 if r: 165 for rr in r: 166 ret.append((socket.AF_INET,socktype,proto,r.rrset.name,(rr.to_text(),port))) 167 return ret
168 169 # vi: sts=4 et sw=4 170