From 1c5d8ecce63196ea0c6572b8c7f0d354eea00987 Mon Sep 17 00:00:00 2001
From: rswindell <>
Date: Thu, 10 Sep 2015 08:08:21 +0000
Subject: [PATCH] Verify standard UDP sevices. Store the capture stopcause.
 Added "add", "update", and "imsglist" commands.

---
 exec/sbbslist.js | 152 ++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 131 insertions(+), 21 deletions(-)

diff --git a/exec/sbbslist.js b/exec/sbbslist.js
index 0011b092ad..6c9d72d323 100644
--- a/exec/sbbslist.js
+++ b/exec/sbbslist.js
@@ -11,6 +11,7 @@
 var REVISION = "$Revision$".split(' ')[1];
 var sbl_dir = "../xtrn/sbl/";
 
+load("sockdefs.js");
 load("portdefs.js");
 var options={sub:"syncdata"};
 opts=load(new Object, "modopts.js", "sbbslist");
@@ -20,7 +21,7 @@ if(this.opts && opts.sub)
 var lib = load(new Object, "sbbslist_lib.js");
 load("graphic.js");
 var capture = load(new Object, "termcapture_lib.js");
-capture.timeout=5;
+capture.timeout=15;
 
 // This date format is required for backwards compatibility with SMB2SBL
 function date_to_str(date)
@@ -40,7 +41,8 @@ function export_entry(bbs, msgbase)
 
     var body = "";      // This section for SMB2SBL compatibility
     body += "Name:          " + bbs.name + "\r\n";
-    body += "Birth:         " + date_to_str(new Date(bbs.first_online)) + "\r\n";
+	if(bbs.first_online)
+		body += "Birth:         " + date_to_str(new Date(bbs.first_online)) + "\r\n";
     if(bbs.software.bbs)
         body += "Software:      " + bbs.software.bbs + "\r\n";
     for(i in bbs.sysop) {
@@ -529,34 +531,71 @@ function verify_terminal_service(service)
     return capture.capture();
 }
 
-function other_services(address)
+// Perform a limited TCP port scan to test for common "other services" on standard ports
+// Results are used for instant message list and other stuff
+// Terminal services (e.g. telnet, rlogin, ssh) are purposely excluded
+function verify_services(address, timeout)
 {
     var i;
-    var services=[
+    var tcp_services=[
         "ftp", 
-        "finger", 
-        "smtp", 
-        "submission", 
-        "users", 
         "msp", 
         "nntp", 
-        "gopher", 
-        "qotd", 
-        "pop3", 
-        "imap",
+	"smtp",
+	"submission",
+	"pop3",
+	"imap",
     ];
-    var verified=[];
+    var udp_services=[
+        "finger",
+        "systat",
+    ];
+
+    var verified={ tcp: [], udp: []};
+
+    var udp_socket = new Socket(SOCK_DGRAM);
+
+    for(i in udp_services) {
+        var service = udp_services[i];
+        printf("Verifying %-10s UDP connection at %s\r\n", service, address);
+        if(!udp_socket.sendto("\r\n", address, standard_service_port[service]))
+            log(LOG_ERR,"FAILED Send to %s UDP service at %s", service, address);
+    }            
 
-    for(i in services) {
+    for(i in tcp_services) {
         if(js.terminated)
             break;
-        print(format("Verifying %-10s", services[i]) + " connection at " + address);
+        var service = tcp_services[i]
+        printf("Verifying %-10s TCP connection at %s ", service, address);
         var socket = new Socket();
-        if(socket.connect(address, standard_service_port[services[i]]), /* timeout: */1) {
-            verified.push(services[i]);
+        if(socket.connect(address, standard_service_port[service], timeout)) {
+            print("Succeeded");
+            verified.tcp.push(service);
             socket.close();
+        } else
+            print("Failed");
+    }
+    print("Waiting for UDP replies");
+    while(verified.udp.length < udp_services.length && udp_socket.poll(1)) {
+        if(js.terminated)
+            break;
+		var msg=udp_socket.recvfrom(32*1024);
+        if(msg==null)
+            log(LOG_ERR, "FAILED (UDP recv)");
+        else {
+            log(LOG_DEBUG, format("UDP message (%u bytes) from %s port %u", msg.data.length, msg.ip_address, msg.port));
+            if(msg.ip_address != address)
+                continue;
+            for(i in udp_services) {
+                var service = udp_services[i];
+                if(standard_service_port[service] == msg.port) {
+                    print("Valid UDP reply for " + service);
+                    verified.udp.push(service);
+                }
+            }
         }
     }
+    print(format("Successfully verified %u TCP services and %u UDP services", verified.tcp.length, verified.udp.length));
     return verified;
 }
 
@@ -565,6 +604,8 @@ function verify_bbs(bbs)
     var i;
     var error="N/A";
 
+	if(!bbs.entry.autoverify)
+		bbs.entry.autoverify = {attempts:0, successes:0};
     bbs.entry.autoverify.success=false;
     for(i in bbs.service) {
         if(js.terminated)
@@ -581,17 +622,20 @@ function verify_bbs(bbs)
             ip_address: result.ip_address,
         };
         if(result == false) {
+            print(capture.error);
             bbs.entry.autoverify.last_failure = failure;
         } else {
+            print(result.stopcause);
             if(result.hello.length && result.hello[0].indexOf("Synchronet BBS") == 0) {
                 bbs.entry.autoverify.success=true;
                 bbs.entry.autoverify.successes++;
                 bbs.entry.autoverify.last_success = {
                     on: new Date(),
                     result: result.hello[0],
+                    stopcause: result.stopcause,
                     service: bbs.service[i],
                     ip_address: result.ip_address,
-                    other_services: other_services(result.ip_address)
+                    other_services: verify_services(result.ip_address, 5)
                 };
                 bbs.entry.verified = { by: js.exec_file + " " + REVISION, on: new Date() };
                 graphic = new Graphic();
@@ -623,7 +667,14 @@ function verify_list(list)
             break;
     }
 }
-    
+
+function unique_strings(a, offset) {
+    var seen = {};
+    return a.filter(function(item) {
+        return seen.hasOwnProperty(item.substring(offset)) ? false : (seen[item.substring(offset)] = true);
+    });
+}
+
 function main()
 {
     var i,j;
@@ -639,7 +690,8 @@ function main()
     for(i in argv) {
         switch(argv[i]) {
             case "upgrade":
-                upgrade_list(sbl_dir + "sbl.dab");
+                list=upgrade_list(sbl_dir + "sbl.dab");
+                print(list.length + " BBS entries upgraded from " + sbl_dir + "sbl.dab");
                 break;
             case "import":
                 import_msgs = true;
@@ -663,7 +715,7 @@ function main()
     else
         list=lib.read_list();
 
-    if(argv.indexOf("-backup") >= 0)
+    if(argv.indexOf("backup") >= 0)
         file_backup(lib.list_fname);
 
     if(import_msgs || export_msgs) {
@@ -685,6 +737,39 @@ function main()
         list.sort(lib.compare);
     }
 
+    if(argv.indexOf("add") >= 0) {
+		if(lib.system_exists(list, system.name)) {
+			alert("System '" + system.name + "' already exists");
+			exit(-1);
+		}
+	    var bbs = lib.new_system(system.name, system.nodes, lib.system_stats());
+		bbs.sysop.push({ name: system.operator, email: 'sysop@'+system.inet_addr });
+		bbs.software = { bbs: "Synchronet " + system.version} ;
+		bbs.terminal.support.push("TTY", "ANSI");
+		while((str=prompt("Description"))) {
+			bbs.description.push(str);
+		}
+		bbs.service.push({ protocol: 'telnet', address: system.inet_addr, port: standard_service_port['telnet'] });
+		lib.add_system(list, bbs, system.operator);
+		lib.write_list(list);
+		print("System added successfully");
+    }
+
+	if(argv.indexOf("update") >= 0) {
+		var index = lib.system_index(list, system.name);
+		if(index < 0) {
+			alert("System '" + system.name + "' does not exist");
+			exit(-1);
+		}
+		var bbs=list[i];
+		bbs.software = { bbs: "Synchronet " + system.version} ;
+		bbs.total = lib.system_stats();
+		bbs.terminal.nodes = system.nodes;
+		lib.update_system(bbs, js.exec_file + " " + REVISION);
+		lib.write_list(list);
+		print("System updated successfully");
+	}
+    
     if(argv.indexOf("verify") >= 0) {
         verify_list(list);
         lib.write_list(list);
@@ -724,6 +809,31 @@ function main()
         print(list.length + " BBS entries exported to: " + f.name);
     }
 
+    if(argv.indexOf("imsglist") >= 0) {
+        var ibbs = [];
+        for(i in list) {
+            var bbs = list[i];
+            if(!bbs.entry.autoverify.success)
+                continue;
+			if(!lib.imsg_capable_system(bbs))
+				continue;
+            ibbs.push(format("%-63s %s\t%s\t%s", 
+                bbs.entry.autoverify.last_success.service.address, 
+                bbs.entry.autoverify.last_success.ip_address,
+				bbs.entry.autoverify.last_success.other_services.udp.sort(),
+				bbs.entry.autoverify.last_success.other_services.tcp.sort()));
+        }
+        ibbs = unique_strings(ibbs, /* offset: */64);
+        var f = new File("sbbsimsg.lst");
+        if(!f.open("w")) {
+            log(LOG_ERR,"Error opening " + f.name);
+            exit();
+        }
+        f.writeAll(ibbs);
+        f.close();
+        print(ibbs.length + " BBS entries exported to: " + f.name);
+    }
+
     if(argv.indexOf("show") >= 0) {
         for(i in list) {
             if(verbose) {
-- 
GitLab