# Step 095 NetWatch lab

Use only on systems you own or have explicit permission to assess. This walkthrough and the automated reproduction use only `127.0.0.1`. The program requires Python 3.10+ and the standard library; no extra packages are needed.

## Start

Extract the archive and run from its directory. On Linux, substitute `python3` for `python` below.

```text
python netwatch.py --help
python reproduce.py
```

`reproduce.py` starts temporary loopback fixtures, exercises the actual CLI and database, then closes the fixtures and removes its test database. It chooses temporary ports by default. Use `--fixed-ports` only in an isolated environment where 8000, 8001 and 9000 are free.

## Three manual stages

In terminals 1 and 2, start these servers separately:

```text
python -m http.server 8000 --bind 127.0.0.1
python -m http.server 9000 --bind 127.0.0.1
```

In terminal 3, run the following twice. Expect BASELINE first, then UNCHANGED. Include port 8001 in the list from the first scan.

```text
python netwatch.py 127.0.0.1 127.0.0.1 --ports 8000,8001,9000 --db practice.sqlite3
```

In terminal 4, start the third server, then repeat the same scan command. Expect `[!] NEW: 127.0.0.1:8001`.

```text
python -m http.server 8001 --bind 127.0.0.1
```

Stop the 8001 server with Ctrl+C and scan again. Expect NOT OBSERVED. This means the connection was not observed in this run; it does not distinguish permanent closure from filtering or delay. Stop the other two servers with Ctrl+C when finished.

## Inputs and interpretation

- Start and end must be ascending IPv4 addresses in one /24. Cross-/24 ranges, reversed ranges and invalid addresses are rejected. A /24 is the educational program's range limit, not an inferred subnet mask of your network.
- `--ports` accepts up to 64 comma-separated integers between 1 and 65535. The default list is `22,80,443,445,3389,8000,8001,9000`.
- `--timeout` bounds each connect/banner wait. Default: 0.3 seconds; allowed: 0.05–2. Total duration grows with addresses × ports × waits.
- Responding IP count is a count of distinct IP addresses, not physical devices. A successful TCP connection is a point-in-time observation.
- Banner collection reads at most 64 bytes. HTTP services may wait for a request, so an empty banner can be normal. A receive failure or reset after a successful connect does not erase that connection observation.
- Compare only against the previous completed scan with the same address range, normalized port list and timeout. Changed conditions create their own BASELINE. Port order and duplicates are normalized.
- The default DB is `netwatch-lab.sqlite3`; the older example's `netwatch.db` is not overwritten. A different existing schema is refused without automatic migration or deletion. Use a new `--db` filename to start a fresh exercise.
- Exit codes: success 0, invalid input 2, runtime/DB error 1, Ctrl+C 130. After interruption, inspect the last committed scan. A scan header and its findings commit together or roll back together.

## Inspect the DB

With Python 3.12+, no separate sqlite3 command is required:

```text
python -m sqlite3 practice.sqlite3 "SELECT id,scan_time FROM scans ORDER BY id;"
python -m sqlite3 practice.sqlite3 "SELECT scan_id,ip,port,banner FROM findings ORDER BY scan_id,port;"
```

On 3.10–3.11, use `sqlite3.connect()` and `execute()` in the Python interpreter. `conn.execute(...)` returns a cursor too, so `cur = conn.execute(...); scan_id = cur.lastrowid` works. Pass data values through `?` parameters.

## Review questions

1. Why does adding 8001 to the requested port list only for the third scan produce BASELINE instead of NEW?
2. Why separate an empty banner from a failed connection?
3. Which baseline should be used if the IP range stays the same but the timeout changes?
4. Why is comparing the latest two scans wrong when asked to inspect an older scan_id?

Answers: 1, the observation conditions changed; 2, a service can accept TCP while waiting silently for a request; 3, a separate baseline for the new conditions; 4, the requested observation and comparison points no longer match.

Official references: https://docs.python.org/3/library/socket.html · https://docs.python.org/3/library/sqlite3.html · https://docs.python.org/3/library/ipaddress.html
