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
+62
View File
@@ -143,6 +143,66 @@ void test_a_dns_answer() {
TEST_ASSERT_FALSE(parseDnsAnswer(loop, sizeof loop, 0x1234, a));
}
void test_ntp() {
uint8_t req[kNtpPacket];
buildNtpRequest(req);
TEST_ASSERT_EQUAL_HEX8(0x23, req[0]);
TEST_ASSERT_EQUAL_UINT8(0, req[47]);
// A server's answer: stratum 2, transmit time 2026-10-08 00:45:12.5 UTC.
uint8_t ans[kNtpPacket] = {0x24, 2};
uint32_t secs = 1791420312u + 2208988800u;
ans[40] = static_cast<uint8_t>(secs >> 24);
ans[41] = static_cast<uint8_t>(secs >> 16);
ans[42] = static_cast<uint8_t>(secs >> 8);
ans[43] = static_cast<uint8_t>(secs);
ans[44] = 0x80; // half a second
NtpAnswer a;
TEST_ASSERT_TRUE(parseNtpAnswer(ans, sizeof ans, a));
TEST_ASSERT_EQUAL_INT(2, a.stratum);
TEST_ASSERT_TRUE(a.seconds == 1791420312);
TEST_ASSERT_EQUAL_UINT32(500, a.millis);
TEST_ASSERT_FALSE(parseNtpAnswer(ans, 40, a)); // cut short
ans[1] = 0;
TEST_ASSERT_FALSE(parseNtpAnswer(ans, sizeof ans, a)); // "go away"
ans[1] = 2;
ans[0] = 0x23;
TEST_ASSERT_FALSE(parseNtpAnswer(ans, sizeof ans, a)); // a client's packet, not a server's
// After 2036 the count starts again from zero.
ans[0] = 0x24;
ans[40] = ans[41] = ans[42] = 0;
ans[43] = 10;
TEST_ASSERT_TRUE(parseNtpAnswer(ans, sizeof ans, a));
TEST_ASSERT_TRUE(a.seconds == 4294967296LL + 10 - 2208988800LL);
TEST_ASSERT_EQUAL_STRING("right, to 0.1 s", clockOffset(1000000, 1000040).c_str());
TEST_ASSERT_EQUAL_STRING("0.3 s ahead", clockOffset(1000300, 1000000).c_str());
TEST_ASSERT_EQUAL_STRING("2.5 s behind", clockOffset(1000000, 1002500).c_str());
TEST_ASSERT_EQUAL_STRING("45 s behind", clockOffset(0, 45000).c_str());
TEST_ASSERT_EQUAL_STRING("3 min ahead", clockOffset(180000, 0).c_str());
TEST_ASSERT_EQUAL_STRING("5 h behind", clockOffset(0, 18000000).c_str());
TEST_ASSERT_EQUAL_STRING("20552 days behind", clockOffset(0, 1775700000000LL).c_str()); // a clock still in 1970
}
void test_certificates_and_ports() {
TEST_ASSERT_EQUAL_STRING("R11", certName("C=US, O=Let's Encrypt, CN=R11").c_str());
TEST_ASSERT_EQUAL_STRING("git.twis.la", certName("CN=git.twis.la").c_str());
TEST_ASSERT_EQUAL_STRING("Example Org", certName("C=BE, O=Example Org").c_str());
TEST_ASSERT_EQUAL_STRING("C=BE", certName("C=BE").c_str());
TEST_ASSERT_EQUAL_STRING("x", certName("O=Not this, DCN=nor this, CN=x").c_str());
TEST_ASSERT_EQUAL_STRING("*.badssl.com", certName("OU=PositiveSSL Wildcard, CN=#140C2A2E62616473736C2E636F6D").c_str()); // an old kind of string
TEST_ASSERT_EQUAL_STRING("#14zz", certName("CN=#14zz").c_str());
TEST_ASSERT_EQUAL_INT(1, daysBetween(2026, 10, 7, 2026, 10, 8));
TEST_ASSERT_EQUAL_INT(53, daysBetween(2026, 10, 8, 2026, 11, 30));
TEST_ASSERT_EQUAL_INT(-8, daysBetween(2026, 10, 8, 2026, 9, 30));
TEST_ASSERT_EQUAL_INT(366, daysBetween(2028, 1, 1, 2029, 1, 1)); // a leap year
TEST_ASSERT_EQUAL_INT(365, daysBetween(2100, 1, 1, 2101, 1, 1)); // and a year that isn't one
TEST_ASSERT_EQUAL_STRING("updates", portLabel(3232, true));
TEST_ASSERT_EQUAL_STRING("Debug Console", portLabel(2323, true));
TEST_ASSERT_EQUAL_STRING("sharing", portLabel(80, true));
TEST_ASSERT_EQUAL_STRING("", portLabel(80, false));
TEST_ASSERT_EQUAL_STRING("DHCP", portLabel(68, false));
}
int main() {
UNITY_BEGIN();
RUN_TEST(test_what_was_typed);
@@ -150,5 +210,7 @@ int main() {
RUN_TEST(test_the_summary);
RUN_TEST(test_a_dns_query);
RUN_TEST(test_a_dns_answer);
RUN_TEST(test_ntp);
RUN_TEST(test_certificates_and_ports);
return UNITY_END();
}