feat(net,gui): real internet access -- redirects, HTTPS guard, netcheck, and a live ONLINE indicator

The stack could already reach the real internet once the UDP-checksum and
scheduler fixes landed; this makes it work on real-world URLs and gives the
desktop a first-class "is the internet up?" answer.

HTTP client
  - Follows 3xx redirects (Location header, absolute or relative) up to five
    hops, so a bare host that 301s to a path or a canonical host resolves to
    actual content. Verified: `webget google.com/` follows the 301 to
    www.google.com and returns its HTTP 200 homepage.
  - Refuses https:// targets -- at the initial URL and on redirect -- with a
    clear message instead of speaking plaintext to port 443 and timing out.
  - Split the URL parser out so both the initial request and redirect targets
    go through the same path; added a case-insensitive header finder.

netcheck (CLI)
  One command that walks the whole chain -- link, address, gateway ping, DNS,
  and a real HTTP fetch -- and reports each PASS/FAIL, ending in ONLINE or
  DEGRADED. The one-shot answer to "is the internet working?".

Network Monitor (GUI)
  A live Internet: ONLINE/OFFLINE badge with the resolved IP, HTTP status and
  latency. It auto-probes once on open and re-checks on the Check button, all
  through the async HTTP worker so the window never blocks.

Verified against the real internet through QEMU's NAT: DNS resolved
example.com to a live Cloudflare address; `netcheck` passed all five layers
(Internet: ONLINE); the GUI badge showed "ONLINE 104.20.23.154 HTTP 200 240
ms"; redirect following and the HTTPS refusal both behaved. Builds clean on
x86_64, aarch64 and armv8i.

Known limit: no TLS, so HTTPS URLs are reported rather than fetched -- plain
HTTP only.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Fh5z6V7nGPbHvqVG5p4CXX
This commit is contained in:
subhobhai943andClaude Opus 4.8 committed 2026-08-27 02:44:53 +05:30
1 parent dd4922767a
commit f6e4511047
4 files changed
+308 -33

No files matched your search

+82
View File
@@ -8,6 +8,7 @@
#include <net/udp.h>
#include <net/tcp.h>
#include <net/http_client.h>
#include <kernel/sched.h>
#include <net/socket.h>
#include <drivers/e1000.h>
#include <drivers/ata.h>
@@ -2488,6 +2489,86 @@ static int applet_webget(int argc, char** argv) {
return 0;
}
// End-to-end connectivity check: link, address, gateway, DNS, and a real HTTP
// fetch, each reported PASS/FAIL. This is the one-command answer to "is the
// internet working?" -- it exercises every layer the stack depends on.
static int applet_netcheck(int argc, char** argv) {
const char* probe = (argc >= 2) ? argv[1] : "example.com";
printk(ANSI_BRIGHT_CYAN "=== SUB-OS Connectivity Check ===\n" ANSI_RESET);
#define OKMARK ANSI_BRIGHT_GREEN "[ ok ]" ANSI_RESET
#define FAILMARK ANSI_BRIGHT_RED "[fail]" ANSI_RESET
int failures = 0;
// 1. Physical link.
bool link = e1000_is_link_up();
printk(" %s link eth0 %s\n", link ? OKMARK : FAILMARK,
link ? "UP" : "DOWN");
if (!link) failures++;
// 2. Interface address.
net_if_t* nif = net_get_primary_if();
bool has_ip = nif && nif->ip != 0;
char ips[16], gws[16], dnss[16];
ip_to_str(nif ? nif->ip : 0, ips);
ip_to_str(nif ? nif->gateway : 0, gws);
ip_to_str(nif ? nif->dns : 0, dnss);
printk(" %s address %s gw %s dns %s\n", has_ip ? OKMARK : FAILMARK,
ips, gws, dnss);
if (!has_ip) failures++;
// 3. Gateway reachability (one quiet ping).
int recv = (nif && nif->gateway) ? net_ping(nif->gateway, 1, 1000) : 0;
printk(" %s gateway %s %s\n", recv > 0 ? OKMARK : FAILMARK, gws,
recv > 0 ? "reachable" : "no reply");
if (recv <= 0) failures++;
// 4. DNS resolution.
uint32_t rip = dns_resolve(probe);
char rips[16];
ip_to_str(rip, rips);
printk(" %s dns %s -> %s\n", rip ? OKMARK : FAILMARK, probe,
rip ? rips : "no answer");
if (!rip) failures++;
// 5. A real HTTP fetch, on the async worker, polled to completion.
char url[160];
snprintf(url, sizeof(url), "%s/", probe);
http_fetch_t* f = http_fetch_start(url, 8192);
if (!f) {
printk(" %s http could not start fetch\n", FAILMARK);
failures++;
} else {
uint64_t t0 = pit_get_ticks();
while (f->state == HTTP_STATE_RUNNING && (pit_get_ticks() - t0) < 1500) {
sched_yield();
}
if (f->state == HTTP_STATE_DONE && f->status_code > 0) {
printk(" %s http GET %s -> %d, %d bytes, %u ms%s\n", OKMARK,
probe, f->status_code, f->len, f->elapsed_ms,
f->redirects ? " (redirected)" : "");
} else {
printk(" %s http %s\n", FAILMARK,
f->state == HTTP_STATE_ERROR ? f->err : "timed out");
failures++;
}
http_fetch_release(f);
}
if (failures == 0) {
printk(ANSI_BRIGHT_GREEN "\n Internet: ONLINE -- all checks passed.\n" ANSI_RESET);
} else {
printk(ANSI_YELLOW "\n Internet: DEGRADED -- %d check(s) failed.\n" ANSI_RESET,
failures);
}
#undef OKMARK
#undef FAILMARK
return failures == 0 ? 0 : 1;
}
// A minimal HTTP/1.0 client over the active-open path.
static int applet_httpget(int argc, char** argv) {
if (argc < 2) {
@@ -2860,6 +2941,7 @@ static const lazybox_applet_t applets[] = {
{"tcpconnect", applet_tcpconnect, "tcpconnect <host> <port> [text]", "Open an outbound TCP stream", "Network"},
{"httpget", applet_httpget, "httpget <host> [path] [port]", "Fetch a URL over TCP", "Network"},
{"webget", applet_webget, "webget <url>", "Fetch a URL via the async worker", "Network"},
{"netcheck", applet_netcheck, "netcheck [host]", "End-to-end internet connectivity test", "Network"},
{"tcpserve", applet_tcpserve, "tcpserve <port>", "Accept one inbound TCP stream", "Network"},
// Storage & Devices