Skip to content

Commit 5058308

Browse files
committed
Make TELNET client do both read and write in parallel.
1 parent 2b61195 commit 5058308

File tree

1 file changed

+225
-33
lines changed

1 file changed

+225
-33
lines changed

src/telnet.c

Lines changed: 225 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
#include <unistd.h>
55
#include <string.h>
66
#include <getopt.h>
7+
#include <fcntl.h>
8+
#include <termios.h>
9+
#include <sys/wait.h>
710
#include "ncp.h"
811

912
#define OLD_TELNET 1
@@ -39,15 +42,31 @@
3942
#define OPT_SUPDUP 025
4043
#define OPT_SUPDUP_OUTPUT 026
4144

45+
#define OMARK 0200
46+
#define OBREAK 0201
47+
#define ONOP 0202
48+
#define ONOECHO 0203
49+
#define OECHO 0204
50+
#define OHIDE 0205
51+
#define OASCII 0240
52+
#define OTRANSPARENT 0241
53+
#define OEBCDIC 0242
54+
55+
static pid_t reader_pid = 0;
56+
static pid_t writer_pid = 0;
57+
static struct termios saved;
58+
4259
static char options[] = {
4360
IAC, DO, OPT_ECHO,
4461
IAC, DO, OPT_SUPPRESS_GO_AHEAD,
4562
IAC, WILL, OPT_SUPPRESS_GO_AHEAD,
4663
0
4764
};
4865

49-
static void option (unsigned char c)
66+
static void option (int fd)
5067
{
68+
unsigned char c;
69+
read (fd, &c, 1);
5170
switch (c) {
5271
case OPT_BINARY:
5372
break;
@@ -76,23 +95,23 @@ static void option (unsigned char c)
7695
}
7796
}
7897

79-
static int special (unsigned char c, const unsigned char *data)
98+
static int special (unsigned char c, int fd)
8099
{
81100
switch (c) {
82101
case IAC:
83102
write (1, &c, 1);
84103
return 0;
85104
case DONT:
86-
option (*data);
105+
option (fd);
87106
return 1;
88107
case DO:
89-
option (*data);
108+
option (fd);
90109
return 1;
91110
case WONT:
92-
option (*data);
111+
option (fd);
93112
return 1;
94113
case WILL:
95-
option (*data);
114+
option (fd);
96115
return 1;
97116
case SB:
98117
return 0;
@@ -122,32 +141,187 @@ static int special (unsigned char c, const unsigned char *data)
122141
}
123142
}
124143

125-
static void process (const unsigned char *data, int size)
144+
static void process_new (unsigned char data, int fd)
126145
{
127-
int i;
146+
switch (data) {
147+
case NUL:
148+
break;
149+
case IAC:
150+
read (fd, &data, 1);
151+
special (data, fd);
152+
break;
153+
default:
154+
write (1, &data, 1);
155+
break;
156+
}
157+
}
128158

129-
for (i = 0; i < size; i++) {
130-
switch (data[i]) {
131-
case NUL:
132-
break;
133-
case IAC:
134-
i++;
135-
i += special (data[i], &data[i + 1]);
136-
break;
137-
default:
138-
write (1, &data[i], 1);
139-
break;
159+
static void process_old (unsigned char data, int fd)
160+
{
161+
unsigned char crlf[] = { 015, 012 };
162+
switch (data) {
163+
case NUL:
164+
break;
165+
case 001: case 002: case 003: case 004: case 005: case 006:
166+
break;
167+
case 016: case 017: case 020: case 021: case 022: case 023:
168+
case 024: case 025: case 026: case 027: case 030: case 031:
169+
case 032: case 033: case 034: case 035: case 036: case 037:
170+
break;
171+
case 0177:
172+
break;
173+
case 015:
174+
read (fd, &data, 1);
175+
if (data == NUL)
176+
write (1, crlf, 1);
177+
else if (data == 012)
178+
write (1, crlf, 2);
179+
else
180+
fprintf (stderr, "[CR without LF or NUL]");
181+
case OMARK:
182+
fprintf (stderr, "[MARK]"); fflush (stderr);
183+
break;
184+
case OBREAK:
185+
fprintf (stderr, "[BREAK]"); fflush (stderr);
186+
break;
187+
case ONOP:
188+
fprintf (stderr, "[NOP]"); fflush (stderr);
189+
break;
190+
case ONOECHO:
191+
fprintf (stderr, "[NOECHO]"); fflush (stderr);
192+
break;
193+
case OECHO:
194+
fprintf (stderr, "[ECHO]"); fflush (stderr);
195+
break;
196+
case OHIDE:
197+
fprintf (stderr, "[HIDE]"); fflush (stderr);
198+
break;
199+
case OASCII:
200+
fprintf (stderr, "[ASCII]"); fflush (stderr);
201+
break;
202+
case OTRANSPARENT:
203+
fprintf (stderr, "[TRANSPARENT]"); fflush (stderr);
204+
break;
205+
case OEBCDIC:
206+
fprintf (stderr, "[EBCDIC]"); fflush (stderr);
207+
break;
208+
default:
209+
write (1, &data, 1);
210+
}
211+
}
212+
213+
static int reader (int connection)
214+
{
215+
unsigned char data[200];
216+
int size, flags;
217+
int fds[2];
218+
219+
if (pipe (fds) == -1)
220+
exit (1);
221+
222+
flags = fcntl (fds[0], F_GETFL);
223+
fcntl (fds[0], F_SETFL, flags | O_NONBLOCK);
224+
225+
reader_pid = fork();
226+
if (reader_pid) {
227+
close (fds[1]);
228+
return fds[0];
229+
}
230+
close (fds[0]);
231+
232+
if (ncp_init (NULL) == -1)
233+
exit (1);
234+
235+
for (;;) {
236+
size = sizeof data;
237+
if (ncp_read (connection, data, &size) == -1) {
238+
fprintf (stderr, "NCP read error.\n");
239+
exit (1);
140240
}
241+
if (size == 0)
242+
exit (0);
243+
if (write (fds[1], data, size) == -1)
244+
exit (1);
141245
}
142246
}
143247

144-
static void telnet_client (int host, int sock, int new)
248+
static int writer (int connection)
145249
{
146-
unsigned char reply[1000];
147-
int connection, size;
250+
unsigned char data[200], *ptr;
251+
ssize_t n, m;
252+
int fds[2];
253+
int size;
254+
255+
if (pipe (fds) == -1)
256+
exit (1);
257+
258+
writer_pid = fork();
259+
if (writer_pid) {
260+
close (fds[0]);
261+
return fds[1];
262+
}
263+
close (fds[1]);
264+
265+
if (ncp_init (NULL) == -1)
266+
exit (1);
267+
268+
size = strlen (options);
269+
if (ncp_write (connection, options, &size) == -1) {
270+
fprintf (stderr, "NCP write error.\n");
271+
exit (1);
272+
}
273+
274+
for (;;) {
275+
fd_set rfds;
276+
FD_ZERO (&rfds);
277+
FD_SET (fds[0], &rfds);
278+
n = select (33, &rfds, NULL, NULL, NULL);
279+
n = read (fds[0], data, 1);
280+
if (n == 0)
281+
exit (0);
282+
if (n < 0)
283+
continue;
284+
ptr = data;
285+
for (ptr = data, m = n; m > 0; ptr += size, m -= size) {
286+
size = m;
287+
if (ncp_write (connection, ptr, &size) == -1) {
288+
fprintf (stderr, "NCP write error.\n");
289+
exit (1);
290+
}
291+
if (size == 0)
292+
exit (0);
293+
}
294+
}
295+
}
296+
297+
static void restore_termios (void)
298+
{
299+
tcsetattr (0, TCSAFLUSH, &saved);
300+
}
301+
302+
static void raw (void)
303+
{
304+
struct termios new;
305+
new = saved;
306+
new.c_iflag = 0;
307+
new.c_oflag = 0;
308+
new.c_lflag = 0;
309+
new.c_cc[VMIN] = 1;
310+
new.c_cc[VTIME] = 0;
311+
tcsetattr (0, TCSAFLUSH, &new);
312+
}
313+
314+
static void telnet_client (int host, int sock,
315+
void (*process) (unsigned char, int))
316+
{
317+
int connection;
318+
int reader_fd, writer_fd;
148319

149320
printf ("TELNET to host %03o.\n", host);
150321

322+
tcgetattr (0, &saved);
323+
raw ();
324+
151325
switch (ncp_open (host, sock, &connection)) {
152326
case 0:
153327
break;
@@ -160,19 +334,37 @@ static void telnet_client (int host, int sock, int new)
160334
exit (1);
161335
}
162336

163-
size = strlen (options);
164-
if (ncp_write (connection, options, &size) == -1) {
165-
fprintf (stderr, "NCP write error.\n");
166-
exit (1);
167-
}
337+
reader_fd = reader (connection);
338+
writer_fd = writer (connection);
168339

169-
size = sizeof reply;
170-
if (ncp_read (connection, reply, &size) == -1) {
171-
fprintf (stderr, "NCP read error.\n");
172-
exit (1);
340+
for (;;) {
341+
fd_set rfds;
342+
int n;
343+
FD_ZERO (&rfds);
344+
FD_SET (0, &rfds);
345+
FD_SET (reader_fd, &rfds);
346+
n = select (33, &rfds, NULL, NULL, NULL);
347+
if (n > 0) {
348+
if (FD_ISSET (0, &rfds)) {
349+
unsigned char data;
350+
read (0, &data, 1);
351+
if (data == 035)
352+
goto end;
353+
write (writer_fd, &data, 1);
354+
}
355+
if (FD_ISSET (reader_fd, &rfds)) {
356+
unsigned char data;
357+
n = read (reader_fd, &data, 1);
358+
if (n == 1)
359+
process (data, reader_fd);
360+
}
361+
}
173362
}
174363

175-
process (reply, size);
364+
end:
365+
kill (reader_pid, SIGTERM);
366+
kill (writer_pid, SIGTERM);
367+
restore_termios ();
176368

177369
if (ncp_close (connection) == -1) {
178370
fprintf (stderr, "NCP close error.\n");
@@ -271,7 +463,7 @@ int main (int argc, char **argv)
271463
}
272464

273465
if (client)
274-
telnet_client (host, sock, new);
466+
telnet_client (host, sock, new ? process_new : process_old);
275467
else if (server)
276468
telnet_server (sock, new);
277469

0 commit comments

Comments
 (0)