diff --git a/exec/sbbsimsg.js b/exec/sbbsimsg.js
index 8f5f0a9abfe6d3387c39d7c0ee7e5b6d8636c4ed..a8fe6836dad339087deab531c7a0aec214c8acd3 100644
--- a/exec/sbbsimsg.js
+++ b/exec/sbbsimsg.js
@@ -1,13 +1,24 @@
 // sbbsimsg.js
 
+// Synchronet inter-bbs instant message module
+// uses Finger and SMTP TCP/IP protocols
+
+// $Id$
+
+const REVISION = "$Revision$".split(' ')[1];
+
+const UDP_RESPONSE_TIMEOUT = 5000	// milliseconds
+
 load("sbbsdefs.js");
 load("nodedefs.js");
+load("sockdefs.js");	// SOCK_DGRAM
 
 // Global vars
 var imsg_user;
 var last_user=0;
+var users=0;
 
-print("\1n\1hSynchronet \1cInstant Message \1wModule \1nv1.00 Alpha\r\n");
+print("\1n\1hSynchronet \1cInstant Message \1wModule \1n" + REVISION + "\r\n");
 
 // Parse arguments
 for(i=0;i<argc;i++)
@@ -40,11 +51,26 @@ for(i in list) {
 		word[0] == system.inetaddr)		// local system?
 		continue;						// ignore
 
-	sys.push( { addr: word[0], ip : word[1], failed: false } );
+	if(word[1] != undefined)  {
+		if(!word[1].length)
+		{
+			printf("Setting zero-len %s to undefined\r\n",word[1]);
+			word[1]=undefined;
+		}
+
+		if(word[1].search(/^\s*$/m)!=-1)
+		{
+			printf("Setting %s to undefined\r\n",word[1]);
+			word[1]=undefined;
+		}
+	}
+
+	sys.push( { addr: word[0], ip : word[1], udp: false, failed: false, reply: 999999 } );
 }
 
 function save_sys_list()
 {
+	sys.sort(sortarray);
 	fname = system.ctrl_dir + "sbbsimsg.lst";
 	f = new File(fname);
 	if(!f.open("w"))
@@ -58,31 +84,116 @@ function save_sys_list()
 	f.close();
 }
 
-// Truncate space off end of string
-function truncsp(str)
+function sortarray(a, b)
 {
-	var len;
+	return(a.reply-b.reply);
+}
 
-	while((len=str.length)!=0 && str.charAt(len-1)==' ')
-		str=str.slice(0,-1);
+function parse_response(response, show)
+{
+	// Skip header
+	while(response.length && response[0].charAt(0)!='-')
+		response.shift();
+	if(response.length && response[0].charAt(0)=='-')
+		response.shift();	// Delete the separator line
+	while(response.length && !response[0].length)
+		response.shift();	// Delete any blank lines
+	while(response.length && !response[response.length-1].length)
+		response.pop();		// Delete trailing blank lines
+
+	if(!response.length) {
+		if(show)
+			print();
+		return;
+	}
+
+	if(show) {
+		str = format("%lu user%s",response.length,response.length==1 ? "":"s");
+		printf("\1g\1h%-33s Time   Age Sex\r\n",str);
+	}
 
-	return(str);
+	for(j in response) {
+		if(response[j]=="")
+			continue;
+
+		if(show) {
+			console.line_counter=0;	// defeat pause
+			print(format("\1h\1y%.25s\1n\1g %.48s"
+				,response[j],response[j].slice(26)));
+		}
+		var u = new Object;
+		u.host = sys[i].addr;
+		u.name = format("%.25s",response[j]);
+		u.name = truncsp(u.name);
+		imsg_user.push(u);
+		users++;
+	}
 }
 
 function list_users(show)
 {
 	imsg_user = new Array();
-	var users=0;
+	var systems=0;
+	var replies=0;
 
+	users = 0;
 	start = new Date();
+	print("\1m\1hListing Systems and Users (Ctrl-C to Abort)...");
+
+	/* UDP systems */
+	sock = new Socket(SOCK_DGRAM);
+	sock.debug=true;
+	sock.bind();
+	for(i=0;sys[i]!=undefined && !(bbs.sys_status&SS_ABORT);i++) {
+		if(sys[i].ip==undefined)
+			continue;
+		if(!sock.sendto("\r\n",sys[i].ip,79))	// Get list of active users
+			//printf("FAILED! (%d) Sending to %s\r\n",sock.last_error,sys[i].addr);
+			continue;
+		systems++;
+	}
 
-	print("\1m\1hListing Systems and Users (Ctrl-C to Abort)...\r\n");
+	begin = new Date();
+	while(replies<systems && new Date().valueOf()-begin.valueOf() < UDP_RESPONSE_TIMEOUT)
+	{
 
+		if(!sock.poll(1))
+			continue;
+
+		message=sock.recvfrom(20000);
+		if(message==null)
+			continue;
+		i=get_sysnum(message.ip_address);
+		if(i==-1)
+			continue;
+		replies++;
+		sys[i].udp=true;
+		sys[i].reply=new Date().valueOf()-start.valueOf();
+
+		response=message.data.split("\r\n");
+
+		if(show) {
+			console.line_counter=0;	// defeat pause
+			printf("\1n\1h%-25.25s\1n ",sys[i].addr);
+		}
+
+		parse_response(response, show);
+	}
+	
+	sock.close();
+
+	/* TCP systems */
 	for(i=0;sys[i]!=undefined && !(bbs.sys_status&SS_ABORT);i++) {
 
+		if(sys[i].udp)
+			continue;
+			
+		replies++;
 		if(sys[i].failed)
 			continue;
 
+		begin = new Date();
+
 		if(show) {
 			console.line_counter=0;	// defeat pause
 			printf("\1n\1h%-25.25s\1n ",sys[i].addr);
@@ -102,8 +213,11 @@ function list_users(show)
 			sys[i].failed = true;
 			continue;
 		}
+		sys[i].reply=new Date().valueOf()-begin.valueOf();
+
 		// cache the IP address for faster resolution
-		sys[i].ip = sock.remote_ip_address;	
+		if(sys[i].ip != sock.remote_ip_address)
+			sys[i].ip = sock.remote_ip_address;	
 
 		sock.send("\r\n");	// Get list of active users
 		var response=new Array();
@@ -119,48 +233,25 @@ function list_users(show)
 		sock.close();
 
 
-		// Skip header
-		while(response.length && response[0].charAt(0)!='-')
-			response.shift();
-		if(response.length && response[0].charAt(0)=='-')
-			response.shift();	// Delete the separator line
-		while(response.length && !response[0].length)
-			response.shift();	// Delete any blank lines
-
-		if(!response.length) {
-			if(show)
-				print();
-			continue;
-		}
-
-		if(show) {
-			str = format("%lu user%s",response.length,response.length==1 ? "":"s");
-			printf("\1g\1h%-33s Time   Age Sex\r\n",str);
-		}
-
-		for(j in response) {
-			if(response[j]=="")
-				continue;
-
-			if(show) {
-				console.line_counter=0;	// defeat pause
-				print(format("\1h\1y%.25s\1n\1g %.48s"
-					,response[j],response[j].slice(26)));
-			}
-			var u = new Object;
-			u.host = sys[i].addr;
-			u.name = format("%.25s",response[j]);
-			u.name = truncsp(u.name);
-			imsg_user.push(u);
-			users++;
-		}
+		parse_response(response,show);
 	}
+	
+	
 	t = new Date().valueOf()-start.valueOf();
 	printf("\1m\1h%lu systems and %lu users listed in %d seconds.\r\n"
-		,i+1, users, t/1000);
+		,replies, users, t/1000);
 	save_sys_list();
 }
 
+function get_sysnum(ip)
+{
+	for(i in sys)
+		if(sys[i].ip==ip)
+			return(i);
+	printf("Unexpected response from %s\r\n",ip);
+	return(-1);
+}
+
 function send_msg(dest, msg)
 {