aboutsummaryrefslogtreecommitdiffstats
path: root/c
diff options
context:
space:
mode:
authoruser <user@clank>2026-07-17 13:06:38 +0200
committeruser <user@clank>2026-07-17 13:06:38 +0200
commitc0e74377af490b93aa452a19652c61cead9c39ff (patch)
tree2e27a587637654814d11e1da658a24909ce77a04 /c
parentnet: UDP sockets in the kernel + a DNS resolver (real hostname lookups) (diff)
downloadgbos-c0e74377af490b93aa452a19652c61cead9c39ff.tar.gz
gbos-c0e74377af490b93aa452a19652c61cead9c39ff.tar.xz
gbos-c0e74377af490b93aa452a19652c61cead9c39ff.zip
net: TCP sockets in the kernel - a Game Boy fetches HTTP over real TCP
Kernel TCP client on the socket layer: net_connect(SOCK_TCP) runs the 3-way handshake in-kernel, send()/recv() drive the byte stream, close() does the FIN. - Socket gains state + 32-bit snd_nxt/rcv_nxt (big-endian, add-with-carry-fold). - tcp_send_seg builds IP+TCP with a pseudo-header checksum; the SYN carries an MSS option (200) so the peer never sends a segment larger than our 256-byte frame buffer (we don't do IP reassembly). - tcp_in state machine: SYN_SENT->ESTABLISHED on SYN-ACK, buffers in-order data and ACKs it, handles FIN -> recv() returns 0 (EOF). - No retransmission: the GB<->host link is lossless and the host's real TCP owns the internet side - which removes TCP's hardest part. - net_pump now processes one frame per call so recv drains each segment before the next arrives (single rx slot, no overwrite). wget.c is now a thin socket client: connect -> send "GET / HTTP/1.0" -> recv to EOF -> print. Verified end to end against a host HTTP server: /# wget 10.0.0.1 HTTP/1.0 200 OK Hello from a real HTTP server, fetched by a Game Boy! with a clean SYN/SYN-ACK/ACK ... PSH ... FIN/ACK trace on the wire.
Diffstat (limited to 'c')
-rw-r--r--c/wget.c56
1 files changed, 42 insertions, 14 deletions
diff --git a/c/wget.c b/c/wget.c
index 013b326..9d99eb0 100644
--- a/c/wget.c
+++ b/c/wget.c
@@ -1,18 +1,46 @@
-#include "netlib.h"
-/* wget URL : send the URL to the host gateway over SLIP, print the reply body.
- The gateway does the real DNS/TCP/HTTP and streams the body back as frames:
- each reply frame is 'D'<chunk>, ending with a single 'E' frame. */
+#include "sock.h"
+/* wget [A.B.C.D] - fetch http://TARGET/ over the kernel TCP socket layer and
+ print it. No SLIP/IP/TCP here: connect() handshakes in the kernel, send()/
+ recv() drive the stream. Default target 10.0.0.1:80 (the SLIP peer/host). */
+
+static unsigned char buf[220]; /* first + large so dst[] lands arg-safe */
+static unsigned char dst[4];
+
+static unsigned char parse_ip(char *s, unsigned char *out) {
+ unsigned char i, v;
+ for (i = 0; i < 4; i++) {
+ if (*s < '0' || *s > '9') return 0;
+ v = 0;
+ while (*s >= '0' && *s <= '9') { v = (unsigned char)(v * 10 + (*s - '0')); s++; }
+ out[i] = v;
+ if (i < 3) { if (*s != '.') return 0; s++; }
+ }
+ return 1;
+}
+
void main(void) {
- char *argv[4];
- unsigned char argc = argv_parse(argv, 4);
- char buf[220];
- unsigned char len, i;
- if (argc < 1) { puts("usage: wget URL"); nl(); return; }
- slip_send(argv[0], strlen(argv[0]));
+ char *arg, *r;
+ unsigned char s, n, i;
+ arg = getargs();
+ if (!arg || !*arg) { dst[0] = 10; dst[1] = 0; dst[2] = 0; dst[3] = 1; }
+ else if (!parse_ip(arg, dst)) { puts("wget: bad address"); nl(); sexit(1); }
+
+ s = net_socket(SOCK_TCP);
+ if (s == 0xFF) { puts("wget: no socket"); nl(); sexit(1); }
+ net_bind(s, 40001);
+ puts("connecting"); nl();
+ if (net_connect(s, dst, 80) == 0xFF) { puts("wget: connect failed"); nl(); net_close(s); sexit(1); }
+
+ r = "GET / HTTP/1.0\r\nHost: gbos\r\n\r\n";
+ n = 0;
+ while (r[n]) { buf[n] = r[n]; n++; }
+ net_send(s, buf, n);
+
for (;;) {
- len = slip_recv(buf, 219);
- if (len >= 1 && buf[0] == 'E') break; /* end of stream */
- for (i = 1; i < len; i++) putc(buf[i]); /* skip the 'D' type byte */
+ n = net_recv(s, buf, (unsigned char)sizeof(buf));
+ if (n == 0xFF) { nl(); puts("[timeout]"); nl(); break; }
+ if (n == 0) { nl(); puts("[eof]"); nl(); break; }
+ for (i = 0; i < n; i++) putc((char)buf[i]);
}
- nl();
+ net_close(s);
}