[Pkg-javascript-commits] [ltx] 121/469: client: proper SRV support

Jonas Smedegaard dr at jones.dk
Wed Aug 31 13:01:12 UTC 2016


This is an automated email from the git hooks/post-receive script.

js pushed a commit to branch master
in repository ltx.

commit 9a41d543eb7e1700154eafc1e472a199c9089405
Author: Astro <astro at spaceboyz.net>
Date:   Thu Sep 9 03:45:54 2010 +0200

    client: proper SRV support
---
 lib/xmpp/client.js |  45 +++++++---------
 lib/xmpp/srv.js    | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 169 insertions(+), 27 deletions(-)

diff --git a/lib/xmpp/client.js b/lib/xmpp/client.js
index 0479ebe..9b695a3 100644
--- a/lib/xmpp/client.js
+++ b/lib/xmpp/client.js
@@ -3,8 +3,8 @@ var JID = require('./jid').JID;
 var xml = require('./xml');
 var sasl = require('./sasl');
 var sys = require('sys');
-var dns = require('dns');
 var Buffer = require('buffer').Buffer;
+var SRV = require('./srv');
 
 var NS_CLIENT = 'jabber:client';
 var NS_XMPP_SASL = 'urn:ietf:params:xml:ns:xmpp-sasl';
@@ -39,39 +39,29 @@ function Client(params) {
     this.xmppVersion = "1.0";
     this.streamTo = this.jid.domain;
     this.state = STATE_PREAUTH;
-    // Immediately start stream
-    this.addListener('connect', this.startStream);
     this.addListener('rawStanza', this.onRawStanza);
 
     if (params.host) {
 	this.connect(params.port || 5222, params.host);
     } else {
 	var self = this;
-	// TODO: improve SRV lookups
-	dns.resolveSrv('_xmpp-client._tcp.' + this.jid.domain,
-		       function(err, addrs) {
-			   if (err) {
-			       /* no SRV record, try domain as A */
-			       self.connect(params.port || 5222, self.jid.domain);
-			   } else {
-			       addrs = addrs.sort(
-					   function(a, b) {
-					       if (a.priority < b.priority)
-						   return -1;
-					       else if (a.priority > b.priority)
-						   return 1;
-					       else
-						   return 0;
-					   });
-			       /* Epic design fail: we cannot retry
-				  with another SRV result because that
-				  will confuse the user with
-				  non-terminal 'error' & 'end' events.
-				*/
-			       self.connect(addrs[0].port, addrs[0].name);
-			   }
-		       });
+	var attempt = SRV.connect(this.socket,
+				  ['_xmpp-client._tcp'], this.jid.domain, 5222);
+	attempt.addListener('connect', function() {
+	    self.startStream();
+	});
+	attempt.addListener('error', function(e) {
+	    self.emit('error', e);
+	});
     }
+
+    // it's us who must restart after starttls
+    this.socket.addListener('secure', function() {
+	self.startStream();
+    });
+    this.socket.addListener('end', function() {
+	self.state = STATE_PREAUTH;
+    });
 }
 
 sys.inherits(Client, Connection.Connection);
@@ -86,6 +76,7 @@ Client.prototype.startStream = function() {
     if (this.xmppVersion)
 	tag += " version='" + this.xmppVersion + "'";
     tag += ">";
+console.log(tag);
     this.send(tag);
 };
 
diff --git a/lib/xmpp/srv.js b/lib/xmpp/srv.js
new file mode 100644
index 0000000..623df0c
--- /dev/null
+++ b/lib/xmpp/srv.js
@@ -0,0 +1,151 @@
+var dns = require('dns');
+var EventEmitter = require('events').EventEmitter;
+
+function compareNumbers(a, b) {
+    a = parseInt(a, 10);
+    b = parseInt(b, 10);
+    if (a < b)
+	return -1;
+    if (a > b)
+	return 1;
+    return 0;
+}
+
+function groupSrvRecords(addrs) {
+    var groups = {};  // by priority
+    addrs.forEach(function(addr) {
+		      if (!groups.hasOwnProperty(addr.priority))
+			  groups[addr.priority] = [];
+
+		      groups[addr.priority].push(addr);
+		  });
+
+    var result = [];
+    Object.keys(groups).sort(compareNumbers).forEach(function(priority) {
+	var group = groups[priority];
+	var totalWeight = 0;
+	group.forEach(function(addr) {
+	    totalWeight += addr.weight;
+	});
+	var w = Math.floor(Math.random() * totalWeight);
+	totalWeight = 0;
+	var candidate = group[0];
+	group.forEach(function(addr) {
+	    totalWeight += addr.weight;
+	    if (w < totalWeight)
+		candidate = addr;
+	});
+	if (candidate)
+	    result.push(candidate);
+    });
+    return result;
+}
+
+function resolveSrv(name, cb) {
+    dns.resolveSrv(name, function(err, addrs) {
+console.log({resolveSrv:[name,err,addrs]});
+	if (err) {
+	    /* no SRV record, try domain as A */
+	    cb(err);
+	} else {
+	    var pending = 0, error, results = [];
+	    var cb1 = function(e, addrs1) {
+		error = error || e;
+		results = results.concat(addrs1);
+		pending--;
+		console.log({results:results,error:error,pending:pending});
+		if (pending < 1) {
+		    cb(results ? null : error, results);
+		}
+	    };
+	    groupSrvRecords(addrs).forEach(function(addr) {
+		resolveHost(addr.name, function(e, a) {
+		    if (a)
+			a = a.map(function(a1) {
+				      return { name: a1,
+					       port: addr.port };
+				  });
+		    cb1(e, a);
+		});
+		pending++;
+	    });
+	}
+    });
+}
+
+// one of both A & AAAA, in case of broken tunnels
+function resolveHost(name, cb) {
+    var error, results = [], pending = 2;
+    var cb1 = function(e, addrs) {
+console.log({resolveHost:[name,e,addrs]});
+	error = error || e;
+	var addr = addrs && addrs[Math.floor(Math.random() * addrs.length)];
+	if (addr)
+	    results.push(addr);
+
+	pending--;
+	if (pending < 1)
+	    cb(results ? null : error, results);
+    };
+
+    dns.resolve4(name, cb1);
+    dns.resolve6(name, cb1);
+}
+
+// connection attempts to multiple addresses in a row
+function tryConnect(socket, addrs, listener) {
+console.log({tryConnect:addrs});
+    var onConnect = function() {
+	socket.removeListener('connect', onConnect);
+	socket.removeListener('error', onError);
+	// done!
+	listener.emit('connect');
+    };
+    var error;
+    var onError = function(e) {
+	error = e;
+	connectNext();
+    };
+    var connectNext = function() {
+	var addr = addrs.shift();
+	if (addr)
+	    socket.connect(addr.port, addr.name);
+	else
+	    listener.emit('error', error);
+    };
+    socket.addListener('connect', onConnect);
+    socket.addListener('error', onError);
+    connectNext();
+}
+
+// returns EventEmitter with 'connect' & 'error'
+exports.connect = function(socket, services, domain, defaultPort) {
+    var listener = new EventEmitter();
+
+    var tryServices = function() {
+	var service = services.shift();
+	if (service) {
+	    resolveSrv(service + '.' + domain, function(error, addrs) {
+		if (addrs)
+		    tryConnect(socket, addrs, listener);
+		else
+		    tryServices();
+	    });
+	} else {
+	    resolveHost(domain, function(error, addrs) {
+		if (addrs) {
+		    addrs = addrs.map(function(addr) {
+			return { name: addr,
+				 port: defaultPort };
+		    });
+		    tryConnect(socket, addrs, listener);
+		} else
+		    listener('error', error);
+	    });
+	}
+
+    };
+    tryServices();
+
+    return listener;
+};

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/ltx.git



More information about the Pkg-javascript-commits mailing list