On 04/06/2014 06:16 PM, Ruslan Makhmatkhanov wrote:
> Hello,
> 
> gajim relies on nslookup to resolve SRV records of jabber server, so it
> fails to operate when nslookup just isn't there. It's a case for any
> FreeBSD >= 10.0, where all bind/named suite, including nslookup, was
> removed from base system and replaced with unbound. Right now we patched
> it to use host(1) to make it work on FreeBSD 10.0 and later. Please see
> the patch from walker_643:
> 
> http://svnweb.freebsd.org/ports/head/net-im/gajim/files/extra-patch-src-common_resolver.py?view=markup&pathrev=345147
> 
> 
> Can gajim source tree utilize host(1) as a fallback (or generally switch
> to it, because host(1) is there on any unix/linux system)?

host doesn't exist under "the other OS".
So the best solution would be to use host under linux and nslookup under
windows.
Here is a patch to do that. what do you think about that?


> PS. Unrelated question: what should I do to register in gajim's trac? I
> see login form, but no register form. So writing this to mailing list
> instead.

Unfortunatly because of spammers, I disabled account registration :/

I try to re-enable it ...

-- 
Yann

diff -r 6c3053fc7a12 src/common/resolver.py
--- a/src/common/resolver.py	Sun Apr 06 12:05:01 2014 +0200
+++ b/src/common/resolver.py	Sun Apr 06 19:04:48 2014 +0200
@@ -53,7 +53,9 @@
     if USE_LIBASYNCNS:
         return LibAsyncNSResolver()
     else:
-        return NSLookupResolver(idlequeue)
+        if os.name == 'nt':
+            return NSLookupResolver(idlequeue)
+        return HostResolver(idlequeue)
 
 class CommonResolver():
     def __init__(self):
@@ -62,43 +64,43 @@
         # dict {"host+type" : list of callbacks}
         self.handlers = {}
 
-    def resolve(self, host, on_ready, type='srv'):
+    def resolve(self, host, on_ready, type_='srv'):
         host = host.lower()
-        log.debug('resolve %s type=%s' % (host, type))
-        assert(type in ['srv', 'txt'])
+        log.debug('resolve %s type=%s' % (host, type_))
+        assert(type_ in ['srv', 'txt'])
         if not host:
             # empty host, return empty list of srv records
             on_ready([])
             return
-        if self.resolved_hosts.has_key(host+type):
+        if self.resolved_hosts.has_key(host+type_):
             # host is already resolved, return cached values
             log.debug('%s already resolved: %s' % (host,
-                self.resolved_hosts[host+type]))
-            on_ready(host, self.resolved_hosts[host+type])
+                self.resolved_hosts[host+type_]))
+            on_ready(host, self.resolved_hosts[host+type_])
             return
-        if self.handlers.has_key(host+type):
+        if self.handlers.has_key(host+type_):
             # host is about to be resolved by another connection,
             # attach our callback
             log.debug('already resolving %s' % host)
-            self.handlers[host+type].append(on_ready)
+            self.handlers[host+type_].append(on_ready)
         else:
             # host has never been resolved, start now
             log.debug('Starting to resolve %s using %s' % (host, self))
-            self.handlers[host+type] = [on_ready]
-            self.start_resolve(host, type)
+            self.handlers[host+type_] = [on_ready]
+            self.start_resolve(host, type_)
 
-    def _on_ready(self, host, type, result_list):
+    def _on_ready(self, host, type_, result_list):
         # practically it is impossible to be the opposite, but who knows :)
         host = host.lower()
         log.debug('Resolving result for %s: %s' % (host, result_list))
-        if not self.resolved_hosts.has_key(host+type):
-            self.resolved_hosts[host+type] = result_list
-        if self.handlers.has_key(host+type):
-            for callback in self.handlers[host+type]:
+        if not self.resolved_hosts.has_key(host+type_):
+            self.resolved_hosts[host+type_] = result_list
+        if self.handlers.has_key(host+type_):
+            for callback in self.handlers[host+type_]:
                 callback(host, result_list)
-            del(self.handlers[host+type])
+            del(self.handlers[host+type_])
 
-    def start_resolve(self, host, type):
+    def start_resolve(self, host, type_):
         pass
 
 # FIXME: API usage is not consistent! This one requires that process is called
@@ -113,22 +115,22 @@
         self.asyncns = libasyncns.Asyncns()
         CommonResolver.__init__(self)
 
-    def start_resolve(self, host, type):
-        type = libasyncns.ns_t_srv
-        if type == 'txt': type = libasyncns.ns_t_txt
-        resq = self.asyncns.res_query(host, libasyncns.ns_c_in, type)
-        resq.userdata = {'host':host, 'type':type}
+    def start_resolve(self, host, type_):
+        type_ = libasyncns.ns_t_srv
+        if type_ == 'txt': type_ = libasyncns.ns_t_txt
+        resq = self.asyncns.res_query(host, libasyncns.ns_c_in, type_)
+        resq.userdata = {'host':host, 'type':type_}
 
     # getaddrinfo to be done
     #def resolve_name(self, dname, callback):
         #resq = self.asyncns.getaddrinfo(dname)
         #resq.userdata = {'callback':callback, 'dname':dname}
 
-    def _on_ready(self, host, type, result_list):
-        if type == libasyncns.ns_t_srv: type = 'srv'
-        elif type == libasyncns.ns_t_txt: type = 'txt'
+    def _on_ready(self, host, type_, result_list):
+        if type_ == libasyncns.ns_t_srv: type_ = 'srv'
+        elif type_ == libasyncns.ns_t_txt: type_ = 'txt'
 
-        CommonResolver._on_ready(self, host, type, result_list)
+        CommonResolver._on_ready(self, host, type_, result_list)
 
     def process(self):
         try:
@@ -153,7 +155,7 @@
                             continue
                         r['prio'] = r['pref']
                         hosts.append(r)
-                self._on_ready(host=requested_host, type=requested_type,
+                self._on_ready(host=requested_host, type_=requested_type,
                         result_list=hosts)
                 try:
                     resq = self.asyncns.get_next()
@@ -277,27 +279,99 @@
                         'prio': prio})
         return hosts
 
-    def _on_ready(self, host, type, result):
+    def _on_ready(self, host, type_, result):
         # nslookup finished, parse the result and call the handlers
         result_list = self.parse_srv_result(host, result)
-        CommonResolver._on_ready(self, host, type, result_list)
+        CommonResolver._on_ready(self, host, type_, result_list)
 
-    def start_resolve(self, host, type):
+    def start_resolve(self, host, type_):
         """
         Spawn new nslookup process and start waiting for results
         """
-        ns = NsLookup(self._on_ready, host, type)
+        ns = NsLookup(self._on_ready, host, type_)
         ns.set_idlequeue(self.idlequeue)
         ns.commandtimeout = 20
         ns.start()
 
 
+class HostResolver(CommonResolver):
+    """
+    Asynchronous DNS resolver calling host. Processing of pending requests
+    is invoked from idlequeue which is watching file descriptor of pipe of
+    stdout of host process.
+    """
+
+    def __init__(self, idlequeue):
+        self.idlequeue = idlequeue
+        self.process = False
+        CommonResolver.__init__(self)
+
+    def parse_srv_result(self, fqdn, result):
+        """
+        Parse the output of host command and return list of properties:
+        'host', 'port','weight', 'priority' corresponding to the found srv hosts
+        """
+        # typical output of host command:
+        # _xmpp-client._tcp.jabber.org has SRV record 30 30 5222 jabber.org.
+        print 'parsing', result
+        if not result:
+            return []
+        ufqdn = helpers.ascii_to_idn(fqdn) # Unicode domain name
+        hosts = []
+        lines = result.split('\n')
+        for line in lines:
+            if line == '':
+                continue
+            domain = None
+            if line.startswith(fqdn):
+                domain = fqdn # For nslookup 9.5
+            elif helpers.decode_string(line).startswith(ufqdn):
+                line = helpers.decode_string(line)
+                domain = ufqdn # For nslookup 9.6
+            if domain:
+                # add 4 for ' has' after domain name
+                rest = line[len(domain)+4:].split('=')
+                if len(rest) != 2:
+                    continue
+                answer_type, props_str = rest
+                if answer_type.strip() != 'SRV':
+                    continue
+                props = props_str.strip().split(' ')
+                if len(props) < 4:
+                    continue
+                prio, weight, port, host  = props[-4:]
+                if host[-1] == '.':
+                    host = host[:-1]
+                try:
+                    prio = int(prio)
+                    weight = int(weight)
+                    port = int(port)
+                except ValueError:
+                    continue
+                hosts.append({'host': host, 'port': port, 'weight': weight,
+                        'prio': prio})
+        return hosts
+
+    def _on_ready(self, host, type_, result):
+        # host finished, parse the result and call the handlers
+        result_list = self.parse_srv_result(host, result)
+        CommonResolver._on_ready(self, host, type_, result_list)
+
+    def start_resolve(self, host, type_):
+        """
+        Spawn new nslookup process and start waiting for results
+        """
+        ns = Host(self._on_ready, host, type_)
+        ns.set_idlequeue(self.idlequeue)
+        ns.commandtimeout = 20
+        ns.start()
+
 class NsLookup(IdleCommand):
-    def __init__(self, on_result, host='_xmpp-client', type='srv'):
+    def __init__(self, on_result, host='_xmpp-client', type_='srv'):
         IdleCommand.__init__(self, on_result)
         self.commandtimeout = 10
         self.host = host.lower()
-        self.type_ = type.lower()
+        self.type_ = type_.lower()
         if not host_pattern.match(self.host):
             # invalid host name
             log.error('Invalid host: %s' % self.host)
@@ -316,6 +390,11 @@
             self.result_handler(self.host, self.type_, self.result)
         self.result_handler = None
 
+
+class Host(NsLookup):
+    def _compose_command_args(self):
+        return ['host', '-t', self.type_, self.host]
+
 # below lines is on how to use API and assist in testing
 if __name__ == '__main__':
     import gobject

_______________________________________________
Gajim-devel mailing list
Gajim-devel@gajim.org
https://lists.gajim.org/cgi-bin/listinfo/gajim-devel

Reply via email to