1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 import sys
21 import os
22 import re
23 import logging
24 log = logging.getLogger('gajim.c.resolver')
25
26 if __name__ == '__main__':
27 sys.path.append('..')
28 from common import i18n
29 import common.configpaths
30 common.configpaths.gajimpaths.init(None)
31
32 from common import helpers
33 from common.xmpp.idlequeue import IdleCommand
34
35
36 ns_type_pattern = re.compile('^[a-z]+$')
37
38
39 host_pattern = re.compile('^[a-z0-9\-._]*[a-z0-9]\.[a-z]{2,}$')
40
41 try:
42
43 import libasyncns
44 USE_LIBASYNCNS = True
45 log.info("libasyncns-python loaded")
46 except ImportError:
47 USE_LIBASYNCNS = False
48 log.debug("Import of libasyncns-python failed, getaddrinfo will block", exc_info=True)
49
50
56
59
60 self.resolved_hosts = {}
61
62 self.handlers = {}
63
64 - def resolve(self, host, on_ready, type='srv'):
65 log.debug('resolve %s type=%s' % (host, type))
66 assert(type in ['srv', 'txt'])
67 if not host:
68
69 on_ready([])
70 return
71 if self.resolved_hosts.has_key(host+type):
72
73 log.debug('%s already resolved: %s')
74 on_ready(host, self.resolved_hosts[host+type])
75 return
76 if self.handlers.has_key(host+type):
77
78
79 log.debug('already resolving %s' % host)
80 self.handlers[host+type].append(on_ready)
81 else:
82
83 log.debug('Starting to resolve %s using %s' % (host, self))
84 self.handlers[host+type] = [on_ready]
85 self.start_resolve(host, type)
86
87 - def _on_ready(self, host, type, result_list):
88
89 log.debug('Resolving result for %s: %s' % (host, result_list))
90 if not self.resolved_hosts.has_key(host+type):
91 self.resolved_hosts[host+type] = result_list
92 if self.handlers.has_key(host+type):
93 for callback in self.handlers[host+type]:
94 callback(host, result_list)
95 del(self.handlers[host+type])
96
99
100
102 """
103 Asynchronous resolver using libasyncns-python. process() method has to be
104 called in order to proceed the pending requests. Based on patch submitted by
105 Damien Thebault.
106 """
107
111
113 type = libasyncns.ns_t_srv
114 if type == 'txt': type = libasyncns.ns_t_txt
115 resq = self.asyncns.res_query(host, libasyncns.ns_c_in, type)
116 resq.userdata = {'host':host, 'type':type}
117
118
119
120
121
122
123 - def _on_ready(self, host, type, result_list):
128
130 try:
131 self.asyncns.wait(False)
132 resq = self.asyncns.get_next()
133 except:
134 return True
135 if type(resq) == libasyncns.ResQuery:
136
137 while resq is not None:
138 try:
139 rl = resq.get_done()
140 except Exception:
141 rl = []
142 hosts = []
143 requested_type = resq.userdata['type']
144 requested_host = resq.userdata['host']
145 if rl:
146 for r in rl:
147 if r['type'] != requested_type:
148
149 continue
150 r['prio'] = r['pref']
151 hosts.append(r)
152 self._on_ready(host=requested_host, type=requested_type,
153 result_list=hosts)
154 try:
155 resq = self.asyncns.get_next()
156 except Exception:
157 resq = None
158 elif type(resq) == libasyncns.AddrInfoQuery:
159
160 rl = resq.get_done()
161 resq.userdata['callback'](resq.userdata['dname'], rl)
162 return True
163
164
166 """
167 Asynchronous DNS resolver calling nslookup. Processing of pending requests
168 is invoked from idlequeue which is watching file descriptor of pipe of
169 stdout of nslookup process.
170 """
171
176
178 """
179 Parse the output of nslookup command and return list of properties:
180 'host', 'port','weight', 'priority' corresponding to the found srv hosts
181 """
182 if os.name == 'nt':
183 return self._parse_srv_result_nt(fqdn, result)
184 elif os.name == 'posix':
185 return self._parse_srv_result_posix(fqdn, result)
186
188
189 if not result:
190 return []
191 hosts = []
192 lines = result.replace('\r', '').split('\n')
193 current_host = None
194 for line in lines:
195 line = line.lstrip()
196 if line == '':
197 continue
198 if line.startswith(fqdn):
199 rest = line[len(fqdn):]
200 if rest.find('service') > -1:
201 current_host = {}
202 elif isinstance(current_host, dict):
203 res = line.strip().split('=')
204 if len(res) != 2:
205 if len(current_host) == 4:
206 hosts.append(current_host)
207 current_host = None
208 continue
209 prop_type = res[0].strip()
210 prop_value = res[1].strip()
211 if prop_type.find('prio') > -1:
212 try:
213 current_host['prio'] = int(prop_value)
214 except ValueError:
215 continue
216 elif prop_type.find('weight') > -1:
217 try:
218 current_host['weight'] = int(prop_value)
219 except ValueError:
220 continue
221 elif prop_type.find('port') > -1:
222 try:
223 current_host['port'] = int(prop_value)
224 except ValueError:
225 continue
226 elif prop_type.find('host') > -1:
227
228 if prop_value[-1] == '.':
229 prop_value = prop_value[:-1]
230 current_host['host'] = prop_value
231 if len(current_host) == 4:
232 hosts.append(current_host)
233 current_host = None
234 return hosts
235
237
238
239 if not result:
240 return []
241 ufqdn = helpers.ascii_to_idn(fqdn)
242 hosts = []
243 lines = result.split('\n')
244 for line in lines:
245 if line == '':
246 continue
247 domain = None
248 if line.startswith(fqdn):
249 domain = fqdn
250 elif helpers.decode_string(line).startswith(ufqdn):
251 line = helpers.decode_string(line)
252 domain = ufqdn
253 if domain:
254 rest = line[len(domain):].split('=')
255 if len(rest) != 2:
256 continue
257 answer_type, props_str = rest
258 if answer_type.strip() != 'service':
259 continue
260 props = props_str.strip().split(' ')
261 if len(props) < 4:
262 continue
263 prio, weight, port, host = props[-4:]
264 if host[-1] == '.':
265 host = host[:-1]
266 try:
267 prio = int(prio)
268 weight = int(weight)
269 port = int(port)
270 except ValueError:
271 continue
272 hosts.append({'host': host, 'port': port, 'weight': weight,
273 'prio': prio})
274 return hosts
275
280
289
290
292 - def __init__(self, on_result, host='_xmpp-client', type='srv'):
293 IdleCommand.__init__(self, on_result)
294 self.commandtimeout = 10
295 self.host = host.lower()
296 self.type = type.lower()
297 if not host_pattern.match(self.host):
298
299 log.error('Invalid host: %s' % self.host)
300 self.canexecute = False
301 return
302 if not ns_type_pattern.match(self.type):
303 log.error('Invalid querytype: %s' % self.type)
304 self.canexecute = False
305 return
306
308 return ['nslookup', '-type=' + self.type, self.host]
309
311 if self.result_handler:
312 self.result_handler(self.host, self.type, self.result)
313 self.result_handler = None
314
315
316 if __name__ == '__main__':
317 import gobject
318 import gtk
319 from xmpp import idlequeue
320
321 idlequeue = idlequeue.get_idlequeue()
322 resolver = get_resolver(idlequeue)
323
325 global resolver
326 host = text_view.get_text()
327 def on_result(host, result_array):
328 print 'Result:\n' + repr(result_array)
329 resolver.resolve(host, on_result)
330 win = gtk.Window()
331 win.set_border_width(6)
332 text_view = gtk.Entry()
333 text_view.set_text('_xmpp-client._tcp.jabber.org')
334 hbox = gtk.HBox()
335 hbox.set_spacing(3)
336 but = gtk.Button(' Lookup SRV ')
337 hbox.pack_start(text_view, 5)
338 hbox.pack_start(but, 0)
339 but.connect('clicked', clicked)
340 win.add(hbox)
341 win.show_all()
342 gobject.timeout_add(200, idlequeue.process)
343 if USE_LIBASYNCNS:
344 gobject.timeout_add(200, resolver.process)
345 gtk.main()
346