#pragma once #include #include #include #include // The parts of the network troubleshooting commands (issue #90) that need no network: what was // asked, the packets to send, and what the answers mean. The sockets are src/services/net_tools.h (a different name on purpose: two headers of one name find themselves). namespace roro::net { // --- what was typed struct PingArgs { std::string host; int count = 4; // 1 to 100 int size = 56; // bytes of payload, 0 to 1400: with its headers a ping is 28 more }; // "ping [count] [size]". "" or how to ask. std::string parsePing(const std::string& args, PingArgs& out); struct PortArgs { std::string host; uint16_t port = 0; }; // "port ", or host:port. std::string parsePort(const std::string& args, PortArgs& out); struct LookupArgs { std::string name, server; // server: an address, or "" for the one in use }; std::string parseLookup(const std::string& args, LookupArgs& out); // --- ICMP: ping and traceroute uint16_t inetChecksum(const uint8_t* data, size_t len); // An echo request: 8 bytes of header and `payload` bytes after it. The length written, or 0 if // it doesn't fit. size_t buildEcho(uint8_t* out, size_t max, uint16_t id, uint16_t seq, size_t payload); struct IcmpAnswer { enum class Kind { Other, Echo, TimeExceeded, Unreachable } kind = Kind::Other; uint16_t id = 0, seq = 0; // of the echo request it answers }; // `packet` as a raw socket hands it over: the IP header first. A router's "time exceeded" and // "unreachable" carry the start of the packet they are about, which is where id and seq come from. IcmpAnswer parseIcmp(const uint8_t* packet, size_t len); // What a run of pings came to: "3/4 back, 25% lost, 12/25/41 ms" (the least, the mean, the most). struct PingStats { int sent = 0, back = 0; uint32_t minMs = 0, maxMs = 0, sumMs = 0; void add(uint32_t ms); std::string summary() const; }; // --- DNS: nslookup // A query for the IPv4 addresses of `name`. The length written, or 0 if that isn't a name. size_t buildDnsQuery(uint8_t* out, size_t max, uint16_t id, const std::string& name); struct DnsAnswer { int rcode = 0; // 0: fine, 3: no such name bool truncated = false; // the answer didn't fit in one packet std::vector addresses; std::string alias; // the last name a CNAME led to, if any }; // False if it isn't the answer to query `id`, or is cut short. bool parseDnsAnswer(const uint8_t* message, size_t len, uint16_t id, DnsAnswer& out); // --- NTP: the clock's offset constexpr size_t kNtpPacket = 48; void buildNtpRequest(uint8_t out[kNtpPacket]); struct NtpAnswer { int stratum = 0; // 1: a reference clock; 2 and up: that many steps from one int64_t seconds = 0; // the server's clock when it answered, UTC since 1970 uint32_t millis = 0; // and the part of a second }; // False if it isn't a server's answer, or says the server has no time to give. bool parseNtpAnswer(const uint8_t* packet, size_t len, NtpAnswer& out); // "0.3 s ahead", "12 s behind", "right, to 0.1 s": this clock against the server's, both in ms. std::string clockOffset(int64_t ownMs, int64_t serverMs); // --- TLS: who a certificate is for // The common name out of a certificate's subject or issuer as mbedTLS prints it // ("C=US, O=Let's Encrypt, CN=R11"): the CN, else the O, else all of it. std::string certName(const std::string& dn); // Whole days from one date to another (negative: the second is earlier). int daysBetween(int y1, int m1, int d1, int y2, int m2, int d2); // --- netstat // What listens on a port of this firmware, or "". const char* portLabel(uint16_t port, bool tcp); } // namespace roro::net