Shell: tls, ntp and netstat (#90)
CI / build (pull_request) Successful in 1m49s
Site / build (pull_request) Successful in 10s

The rest of the issue's list. tls makes a handshake that checks nothing,
then says the certificate in words: who it is for, who signed it, until
when, and whether this device's roots and the name asked for accept it,
with the reason when they don't. ntp compares a time server's clock with
the device's. netstat lists what listens and what is connected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
This commit is contained in:
2026-10-08 02:54:59 +02:00
co-authored by Claude Opus 5.5
parent 9b6457d1ec
commit 9808013fc0
12 changed files with 395 additions and 16 deletions
+4 -2
View File
@@ -40,7 +40,8 @@ update check | update list | update status | update install <tag> the project'
sd card | sd list | cat <path> | log <text> | burst | sound on|off | short | normal
Irc | Wifi | Gnss | Gemini | Lora | Storage | Notes | Shell | System | Settings open that App: a capital letter is an App, not a command
ping <host> [count] [size] | nslookup <name> [server] | port <host> <port> | traceroute <host> | cancel is it there, does its name resolve, is its port open, which way; one at a time
ifconfig | arp the interfaces (Wi-Fi and the VPN), their addresses, the default route and the DNS servers; the neighbours heard
tls <host> [port] | ntp [server] a TLS handshake: who the certificate is for, by whom, until when, and whether this device trusts it; a time server's clock against this one
ifconfig | arp | netstat the interfaces (Wi-Fi and the VPN), their addresses, the default route and the DNS servers; the neighbours heard; what listens and what is connected
vpn status | vpn up [seconds] | vpn down | vpn import [path] | vpn forget | vpn auto on|off the WireGuard tunnel (Settings > VPN); import reads /vpn/wg0.conf; with seconds, it goes down by itself
debug status | debug off [seconds] the Debug Console over Wi-Fi (Settings > Debug Console); with seconds, it comes back
debug on | debug token <16 to 64 characters> | debug token new (USB serial only) switch it on, set its token
@@ -113,7 +114,8 @@ In **Safe Mode** (see [Crashes and Safe Mode](/dev/debug/crashes/)) only a few r
| `loop spin on` / `loop spin off` | Make the main loop spin without resting, to compare load and radio noise |
| `crash abort` / `crash wdt` | Crash on purpose, or hang the main loop until the watchdog fires |
| `ping <host> [count] [size]` / `nslookup <name> [server]` / `port <host> <port>` / `traceroute <host>` / `cancel` | Network troubleshooting (issue #90): does a host answer and how fast; a name's addresses, from which DNS server and in how long; is a TCP port open, refused or silent; the routers on the way. Each runs on a task of its own and prints as it goes, one at a time; `cancel` stops it |
| `ifconfig` / `arp` | The interfaces (Wi-Fi and the VPN) with their addresses, MTU, which is the default route, and the DNS servers; the neighbours heard on the Wi-Fi |
| `tls <host> [port]` / `ntp [server]` | A TLS handshake that checks nothing, then the certificate said in words: who it is for, who signed it, until when, its SHA-256, and whether this device's roots and the name asked for accept it (about 52 KB of heap while it runs; refused under 70 KB free). A time server's clock against the device's, with the round trip |
| `ifconfig` / `arp` / `netstat` | The interfaces (Wi-Fi and the VPN) with their addresses, MTU, which is the default route, and the DNS servers; the neighbours heard on the Wi-Fi; what listens and what is connected |
| `vpn status` / `vpn up [seconds]` / `vpn down` / `vpn import [path]` / `vpn forget` / `vpn auto on\|off` | The WireGuard tunnel: its state, on (for that many seconds, then off by itself: for trying a configuration from afar), off, read a `.conf` from the card (`/vpn/wg0.conf`), erase it, start with Wi-Fi. No key is ever printed |
| `debug status` / `debug off [seconds]` | The Debug Console: whether it's on, has a token and a client; switch it off. With a number of seconds, it comes back by itself after that long |
| `debug on` / `debug token <value>` / `debug token new` | USB serial only: switch it on (making a token if there's none), give it a token of 16 to 64 characters, or make a new one. The token is never printed |
+21 -1
View File
@@ -8,7 +8,7 @@ docs = true
source = "docs/milestones/N1.md"
tag = "N1"
+++
**Status:** in progress. The WireGuard tunnel (issue #8) shipped as **v0.19.0**. The first network troubleshooting commands (issue #90) shipped as **v0.20.0**: `ping`, `nslookup`, `port`, `traceroute`, `ifconfig`, `arp`; `tls`, `ntp` and `netstat` are still to do. SSH (#2) is not started.
**Status:** in progress. The WireGuard tunnel (issue #8) shipped as **v0.19.0**. The network troubleshooting commands (issue #90) shipped in two parts: `ping`, `nslookup`, `port`, `traceroute`, `ifconfig` and `arp` as **v0.20.0**; `tls`, `ntp` and `netstat` after it. SSH (#2) is not started.
**Goal:** reach things from the device that aren't on the Wi-Fi it happens to be on, and keep its traffic private on a network that isn't yours.
@@ -129,3 +129,23 @@ With a tunnel, fixed addresses and a file server on the device, "is it the netwo
**Not checked:** without the VPN (every check went through the tunnel, or to the local network); a network that drops ICMP; the commands in Safe Mode, where they are not offered.
**Found on the way:** a refused connection is reported by lwIP as "reset", not "refused"; the first version called it "no route". And the header for the tested half was first given the same name as the service's, which makes a file include itself: the same mistake as an hour before, in the same way.
### The rest of the list: `tls`, `ntp`, `netstat` (2026-10-08)
- **`tls <host> [port]`** makes a handshake that checks nothing, so that a bad certificate can be looked at, and then checks it itself: against this device's roots (`ca_roots.h`, the ones the Update Service trusts) and the name asked for. It says who the certificate is for, who signed it, from when to when with the days left, the verdict with its reasons, and the SHA-256 that a Gemini pin is. It runs on a 12 KB task and isn't tried with less than 70 KB free: a handshake peaks at about 52 KB.
- **`ntp [server]`** sends one SNTP request and compares the answer with the device's clock, allowing for half the round trip. With no server it asks the first one in Settings.
- **`netstat`** reads lwIP's own lists: what listens, labelled where the firmware knows what it is, what is connected, and the UDP ports in use.
- Host tests: the NTP packet and the year 2036, the offset in words, a certificate's name (an old string type that mbedTLS prints as hex included), days between dates. 7 tests in `test/test_net_probe` in all.
| Check on the device | Result |
|---|---|
| `tls git.twis.la` | 709 ms; for git.twis.la, 67 days left, "this device trusts it", the SHA-256 |
| `tls geminiprotocol.net 1965` | "NOT trusted here: not signed by a root this device has": a capsule signs its own |
| `tls expired.badssl.com` | "EXPIRED 4197 days ago" |
| `tls wrong.host.badssl.com` | "NOT trusted here: not for that name" |
| `tls` to a port that isn't TLS | "no handshake ... An invalid SSL record was received" |
| `ntp` | The server, its stratum, 50 ms away; "this clock is right, to 0.1 s" |
| `netstat` | The update port and the Debug Console listening, the console's own connection, the UDP ports |
| Memory during a `tls` | 44.5 KB free at the lowest, from 104 KB |
**Not checked:** `tls` with IRC connected (it should refuse for lack of memory); `ntp` against a clock that is wrong; `netstat` while sharing.