#include "resolve.h" /* nc [-s] HOST PORT - raw TCP, the way netcat does it. The kernel owns * SLIP/IP/TCP; this is just a pump between the socket and the terminal. * * nc towel.blinkenlights.nl 23 telnet-ish: ASCIImation on a Game Boy * nc 10.0.0.1 6667 talk to anything line-oriented * echo GET / HTTP/1.0 > r (blank line needed by most servers) * nc -s example.com 80 < r -s: send stdin, print the reply, exit * * Interactive mode: SELECT opens the on-screen keyboard, typing echoes * locally, START sends the line with a CRLF. The line "/q" quits without * being sent (there is no ^C here); a remote EOF quits too. * * Everything lives on the stack on purpose: resolve.h's DNS buffer is a * static, so it sits at $A000 - right on top of the command line the shell * left for us. Copy argv before the first DNS call, exactly like ping.c. */ #define BUFSZ 208 /* one full segment: the most the kernel buffers */ #define LINESZ 64 static unsigned char split(char *s, char **tok, unsigned char max) { unsigned char n = 0; for (;;) { while (*s == ' ') *s++ = 0; if (!*s) break; if (n < max) tok[n++] = s; while (*s && *s != ' ') s++; } return n; } /* atou() is 8-bit; ports are not */ static unsigned int atou16(const char *s) { unsigned int n = 0; while (*s >= '0' && *s <= '9') { n = n * 10 + (unsigned char)(*s - '0'); s++; } return n; } void main(void) { char args[LINESZ]; /* stack copy of the command line */ char line[LINESZ]; unsigned char buf[BUFSZ]; unsigned char dst[4]; char *tok[4]; char *host = 0, *portstr = 0; unsigned int port; unsigned char ntok, i, n, c, sock, sflag = 0, llen = 0, idle = 0; { /* copy args off $A000 before resolve()'s statics land on them */ char *a = getargs(); for (i = 0; a[i] && i < LINESZ - 1; i++) args[i] = a[i]; args[i] = 0; } ntok = split(args, tok, 4); for (i = 0; i < ntok; i++) { if (tok[i][0] == '-') { if (tok[i][1] == 's') sflag = 1; continue; } if (!host) host = tok[i]; else if (!portstr) portstr = tok[i]; } if (!host || !portstr) { puts("usage: nc [-s] HOST PORT"); nl(); puts(" -s: send stdin, print reply, exit"); nl(); sexit(2); } port = atou16(portstr); if (port == 0) { puts("nc: bad port"); nl(); sexit(2); } if (!resolve(host, dst)) { puts("nc: cannot resolve "); puts(host); nl(); sexit(1); } sock = net_socket(SOCK_TCP); if (sock == 0xFF) { puts("nc: no socket"); nl(); sexit(1); } /* vary the local port per run (DIV noise + pid): a fixed one can collide with a half-dead connection still open at the server (see irc.c) */ net_bind(sock, 42000 + ((unsigned int)*(volatile unsigned char *)0xFF04 << 2) + getpid()); puts("-!- connecting "); for (i = 0; i < 4; i++) { putu(dst[i]); if (i < 3) putc('.'); } putc(':'); putu(port); nl(); if (net_connect(sock, dst, port) == 0xFF) { puts("nc: connect failed"); nl(); net_close(sock); sexit(1); } if (sflag) { /* batch mode: stdin -> socket -> term */ n = 0; for (;;) { char ch = readc(); /* blocks forever on a console stdin! */ if (ch == EOF) break; buf[n++] = (unsigned char)ch; if (n == BUFSZ) { net_send(sock, buf, n); n = 0; } } if (n) net_send(sock, buf, n); /* Own the timeout out here (like ping): the kernel's blocking recv counts pumps, not seconds, so a peer that just goes quiet would wedge us for minutes instead of ending the transfer. */ idle = 0; for (;;) { n = net_recv_nb(sock, buf, BUFSZ); if (n == 0) { nl(); puts("[eof]"); nl(); break; } if (n == 0xFE || n == 0xFF) { if (++idle >= 80) { nl(); puts("[timeout]"); nl(); break; } msleep(50); /* ~4s of silence ends it */ continue; } writes((char *)buf, n); idle = 0; } net_close(sock); sexit(0); } puts("-!- connected (SELECT = keyboard, /q quits)"); nl(); for (;;) { n = net_recv_nb(sock, buf, BUFSZ); if (n == 0) { puts("-!- closed by peer"); nl(); break; } if (n != 0xFE && n != 0xFF) writes((char *)buf, n); /* batched render */ /* poll input every pass, even mid-flood, or a chatty peer starves the keyboard and dropped keystrokes look like a broken OSK (irc.c) */ c = pollin(); if (!c) c = pollcon(); if (c == '\n') { nl(); if (llen == 2 && line[0] == '/' && line[1] == 'q') break; line[llen] = '\r'; line[llen + 1] = '\n'; net_send(sock, line, (unsigned char)(llen + 2)); llen = 0; } else if (c == 8) { if (llen) { llen--; putc(8); } } else if (c) { if (llen < LINESZ - 2) { line[llen++] = (char)c; putc((char)c); } } if (n == 0xFE || n == 0xFF) { /* nothing arrived: back off a little */ if (!c && ++idle >= 4) { msleep(20); idle = 4; } } else idle = 0; } net_close(sock); sexit(0); }