1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
|
#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);
}
|