# Building a port scanner

**URL:** <https://swi-prolog.discourse.group/t/building-a-port-scanner/2580>\
**Category:** Help!\
**Created:** [June 30, 2020, 12:40pm UTC](https://swi-prolog.discourse.group/t/building-a-port-scanner/2580 "2020-06-30T12:40:46Z")\
**Posts on this page:** 1\
**Showing post:** 25

<div class="post-metadata">

**Author:** ![EricGT](https://avatars.discourse-cdn.com/v4/letter/e/f1d935/32.png) [@EricGT](https://swi-prolog.discourse.group/u/EricGT)\
**Post date:** [July 1, 2020, 3:12pm UTC](https://swi-prolog.discourse.group/t/building-a-port-scanner/2580/25 "2020-07-01T15:12:40Z")

</div>

The next version switches to using concurrent\_forall/3. Since this predicate is so new (3 days old) and I run on Windows, I installed the Windows 64-bit version of the [daily build](https://www.swi-prolog.org/download/daily/bin/).

`SWI-Prolog (threaded, 64 bits, version 8.3.2-198-gd839164c7)`

Note: This code also moved executing `debug/1` on the command line into a Prolog directive  
`:- debug(concurrent).` which can easily be commented out. If you peruse the SWI-Prolog source code on GitHub you will often find these lines commented out.

Also notice how much simpler the code becomes when using concurrent\_forall/3.

The hardest part about writing this was trying to understand `concurrent_forall(:Generate, :Test)`, how did `Generate` and `Test` align with my existing code. So instead of trying to understand the code from the top down I looked at the critical predicate common to all of this which is thread\_create/2 and in concurrent\_forall/2 is in the line `maplist(thread_create(fa_worker(Q, Me, Templ, Test)), Workers)` and just figured out which variables I could set and what they needed. The only one that can be set by calling concurrent\_forall/3 is `Test` which needs to be `port_scan(IP_address,Port)`, after that identifying what the rest of concurrent\_forall needed was easy.

For this use of concurrent\_forall instead of `concurrent_forall(:Generate, :Test)` I think of it as `concurrent_forall(:Generate unique threads values, :Call thread with unique values)`.

```prolog
:- debug(concurrent).

concurrent_scan(IP_address,Low_port,High_port,Number_of_threads) :-
    concurrent_forall(
        between(Low_port,High_port,Port),
        port_scan(IP_address,Port),
        [threads(Number_of_threads)]
    ).

port_scan(IP_address,Port) :-
    catch(
        setup_call_cleanup(
            tcp_socket(Socket),
            (
                % Open stream socket based on TCP/IP which uses IP address and port number, i.e. INET socket
                tcp_connect(Socket, IP_address:Port),
                format('Port ~w: open~n', [Port])
            ),
            tcp_close_socket(Socket)
        ),
        error(_,_),
        true
    ).

```

Example run

```prolog
?- concurrent_scan('140.211.166.101',75,84,3).
% [Thread 5] Running test user:port_scan('140.211.166.101',77)
% [Thread 3] Running test user:port_scan('140.211.166.101',76)
% [Thread 4] Running test user:port_scan('140.211.166.101',75)
% [Thread 5] Running test user:port_scan('140.211.166.101',78)
% [Thread 4] Running test user:port_scan('140.211.166.101',79)
% [Thread 3] Running test user:port_scan('140.211.166.101',80)
Port 80: open
% [Thread 3] Running test user:port_scan('140.211.166.101',81)
% [Thread 5] Running test user:port_scan('140.211.166.101',82)
% [Thread 4] Running test user:port_scan('140.211.166.101',83)
% [Thread 3] Running test user:port_scan('140.211.166.101',84)
true.

```

NB The threads are being reused this time.

A more comprehensive example. (debug/1 was commented out.)

```prolog
?- time(concurrent_scan('140.211.166.101',1,65536,8192)).
Port 80: open
Port 22: open
Port 443: open
% 196,643 inferences, 0.500 CPU in 174.439 seconds (0% CPU, 393286 Lips)
true.

```

8192 threads checking 65536 ports in ~3 minutes.

* * *

The comment Jan W. made about

> [@jan](#):
>
> return and collect the results.

and that I asked about now makes more sense. If you read the code for concurrent\_forall you will notice that to pass messages back would require adding more complexity to something that is already very complex. So I take it to mean that if you want to use concurrent\_forall then use it as designed, even it is breaking some rules of thumb such as have the threads know as little as possible about the outside world.

---

_[View the full topic](https://swi-prolog.discourse.group/t/building-a-port-scanner/2580)._
