Skip to main content

Here is a TCP "port scanner" that works from inside your browser. It can scan your local network! Actually, it is not really a port scanner, because it can only distinguish between the following two cases...

/* The scanner needs these global variables for an ugly hack. */
var last_scanobj_index = 0;
var scanobjs = {};

function PortScanner(ip, port) {

    this.ip = ip;
    this.port = port;
    this.on_open_or_closed = null;
    this.on_stealthed = null;
    this.start_time = null;
    this.timed_out = null;
    this.total_time = null;

    this.run = function () {
        /* Check that the client gave us all the callbacks we need. */
        if (this.on_open_or_closed == null) {
            alert("Please set the on_open_or_closed callback!");
        }
        if (this.on_stealthed == null) {
            alert("Please set the on_stealthed callback!");
        }

        /* Save this object in the global directory (UGLY HACK). */
        var our_scanobj_index = last_scanobj_index;
        last_scanobj_index++;
        scanobjs[our_scanobj_index] = this;

        /* Record the starting time. */
        this.start_time = (new Date()).getTime();

        /* Create the div to load the image, passing our object's index into
            the global directory so that it can be retrieved. */
        document.getElementById("testdiv").innerHTML = '<img src="http://' + ip + ':' + port +
            '" alt="" onerror="error_handler(' + our_scanobj_index + ');" />';

        // XXX: What's the right way to do this in JS?
        var thiss = this;
        setTimeout(
            function () {
                /* This will be non-null if the event hasn't fired yet. */
                if (scanobjs[our_scanobj_index]) {
                    scanobjs[our_scanobj_index] = null;
                    thiss.timed_out = true;
                    thiss.on_stealthed();
                }
            },
            10000
        );
    }
}

function error_handler(index) {
    /* Get the PortScanner object back. */
    var thiss = scanobjs[index];

    /* If it's null, the scan timed out. */
    if (thiss == null) {
        return;
    }
    /* Set it to null so the timeout knows we handled it. */
    scanobjs[index] = null;
    thiss.timed_out = false;

    /* Measure the amount of time it took for the load to fail. */
    thiss.total_time = (new Date()).getTime() - thiss.start_time;

    /* Call the appropriate callback. */
    if (thiss.total_time < 1500) {
        thiss.on_open_or_closed();
    } else {
        thiss.on_stealthed();
    }
}

function custom_scan(form) {
    var ip = form.custom_ipaddr.value;
    var port = form.custom_port.value;
    var ip_addr_re = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;

    var match = ip_addr_re.exec(ip);
    if (match == null) {
        alert("That isn't a valid IPv4 address.");
        return;
    }

    if (match[1] > 255 || match[2] > 255 || match[3] > 255 || match[4] > 255) {
        alert("That isn't a valid IPv4 address.");
    }

    port = parseInt(port);
    if (isNaN(port) || port < 0 || port > 65535) {
        alert("Bad port number");
    }

    document.getElementById("custom_button").disabled = true;
    document.getElementById("custom_result").innerHTML = "Scanning... This will take up to 10 seconds.";

    var scanner = new PortScanner(ip, port);

    scanner.on_stealthed = function () {
        if (scanner.timed_out) {
            document.getElementById("custom_result").innerHTML = "Case 2 (no response after 10s).";
        } else {
            document.getElementById("custom_result").innerHTML = "Case 2 (" + this.total_time + " ms).";
        }
        document.getElementById("custom_button").disabled = false;
    }

    scanner.on_open_or_closed = function () {
        document.getElementById("custom_result").innerHTML = "Case 1 (" + this.total_time + " ms)."
        document.getElementById("custom_button").disabled = false;
    }

    scanner.run();
}

/* This variable keeps track of which 192.168.1 IP to scan next. */
var current_octet;
var stop;

function lan_scan(form) {
    document.getElementById("lan_button").disabled = true;
    document.getElementById("lan_button_stop").disabled = false;

    /* Skip .1 since it might visibly prompt for a password. */
    current_octet = 2;
    stop = false;

    var scanner = new PortScanner("192.168.1." + current_octet, 80);
    scanner.on_stealthed = lan_on_stealthed;
    scanner.on_open_or_closed = lan_on_open_or_closed;
    scanner.run();

    document.getElementById("lan_results").innerHTML = "Scanning... <br />";
}

function lan_stop(form) {
    stop = true;
    document.getElementById("lan_button").disabled = false;
    document.getElementById("lan_button_stop").disabled = true;
}

function lan_on_stealthed() {
    var res_div = document.getElementById("lan_results");
    res_div.innerHTML += "192.168.1." + current_octet + ": ";
    if (this.timed_out) {
        res_div.innerHTML += "Case 2 (no response after 10 seconds). <br />";
    } else {
        res_div.innerHTML += "Case 2 (" + this.total_time + " ms). <br />";
    }

    current_octet += 1;

    if (stop || current_octet >= 255) {
        res_div.innerHTML += "Done. <br />";
        document.getElementById("lan_button").disabled = false;
        document.getElementById("lan_button_stop").disabled = true;
        return;
    }

    var scanner = new PortScanner("192.168.1." + current_octet, 80);
    scanner.on_stealthed = lan_on_stealthed;
    scanner.on_open_or_closed = lan_on_open_or_closed;
    scanner.run();
}

function lan_on_open_or_closed() {
    var res_div = document.getElementById("lan_results");
    res_div.innerHTML += "192.168.1." + current_octet + ": ";
    res_div.innerHTML += "Case 1 (" + this.total_time + " ms). <br />";

    current_octet += 1;

    if (stop || current_octet >= 255) {
        res_div.innerHTML += "Done. <br />";
        document.getElementById("lan_button").disabled = false;
        document.getElementById("lan_button_stop").disabled = true;
        return;
    }

    var scanner = new PortScanner("192.168.1." + current_octet, 80);
    scanner.on_stealthed = lan_on_stealthed;
    scanner.on_open_or_closed = lan_on_open_or_closed;
    scanner.run();
}