static char sccsid
[] = "@(#)telnetd.c 4.27 (Berkeley) %G%";
* Stripped-down telnet server.
#define BANNER "\r\n\r\n4.2 BSD UNIX (%s)\r\n\r\r\n\r%s"
char doopt
[] = { IAC
, DO
, '%', 'c', 0 };
char dont
[] = { IAC
, DONT
, '%', 'c', 0 };
char will
[] = { IAC
, WILL
, '%', 'c', 0 };
char wont
[] = { IAC
, WONT
, '%', 'c', 0 };
* I/O data buffers, pointers, and counters.
char ptyibuf
[BUFSIZ
], *ptyip
= ptyibuf
;
char ptyobuf
[BUFSIZ
], *pfrontp
= ptyobuf
, *pbackp
= ptyobuf
;
char netibuf
[BUFSIZ
], *netip
= netibuf
;
char netobuf
[BUFSIZ
], *nfrontp
= netobuf
, *nbackp
= netobuf
;
char line
[] = "/dev/ptyp0";
if (getpeername(0, &from
, &fromlen
) < 0) {
fprintf(stderr
, "%s: ", argv
[0]);
if (setsockopt(0, SOL_SOCKET
, SO_KEEPALIVE
, 0, 0) < 0) {
fprintf(stderr
, "%s: ", argv
[0]);
perror("setsockopt (SO_KEEPALIVE)");
char *envinit
[] = { "TERM=network", 0 };
* Get a pty, scan input lines.
char *cp
= line
, *host
, *ntoa();
for (i
= 0; i
< 16; i
++) {
cp
[strlen("/dev/ptyp")] = "0123456789abcdef"[i
];
fatal(f
, "All network ports in use");
cp
[strlen("/dev/")] = 't';
fatalperror(f
, cp
, errno
);
b
.sg_flags
= CRMOD
|XTABS
|ANYP
;
hp
= gethostbyaddr(&who
->sin_addr
, sizeof (struct in_addr
),
host
= ntoa(who
->sin_addr
);
fatalperror(f
, "fork", errno
);
execl("/bin/login", "login", "-h", host
, 0);
fatalperror(f
, "/bin/login", errno
);
(void) sprintf(buf
, "telnetd: %s.\n", msg
);
(void) write(f
, buf
, strlen(buf
));
fatalperror(f
, msg
, errno
)
extern char *sys_errlist
[];
(void) sprintf(buf
, "%s: %s", msg
, sys_errlist
[errno
]);
* Main loop. Select from pty and network, and
* hand data to telnet receiver finite state machine.
signal(SIGTSTP
, SIG_IGN
);
signal(SIGCHLD
, cleanup
);
* Request to do remote echo.
* Show banner that getty never gave.
gethostname(hostname
, sizeof (hostname
));
sprintf(nfrontp
, BANNER
, hostname
, "");
nfrontp
+= strlen(nfrontp
);
int ibits
= 0, obits
= 0;
* Never look for input if there's still
* stuff in the corresponding output buffer
select(16, &ibits
, &obits
, 0, 0);
if (ibits
== 0 && obits
== 0) {
* Something to read from the network...
ncc
= read(f
, netibuf
, BUFSIZ
);
if (ncc
< 0 && errno
== EWOULDBLOCK
)
* Something to read from the pty...
pcc
= read(p
, ptyibuf
, BUFSIZ
);
if (pcc
< 0 && errno
== EWOULDBLOCK
)
if ((&netobuf
[BUFSIZ
] - nfrontp
) < 2)
c
= *ptyip
++ & 0377, pcc
--;
if ((obits
& (1 << f
)) && (nfrontp
- nbackp
) > 0)
if ((obits
& (1 << p
)) && (pfrontp
- pbackp
) > 0)
#define TS_DATA 0 /* base state */
#define TS_IAC 1 /* look for double IAC's */
#define TS_CR 2 /* CR-LF ->'s CR */
#define TS_BEGINNEG 3 /* throw away begin's... */
#define TS_ENDNEG 4 /* ...end's (suboption negotiation) */
#define TS_WILL 5 /* will option negotiation */
#define TS_WONT 6 /* wont " */
#define TS_DO 7 /* do " */
#define TS_DONT 8 /* dont " */
static int state
= TS_DATA
;
if ((&ptyobuf
[BUFSIZ
] - pfrontp
) < 2)
c
= *netip
++ & 0377, ncc
--;
if (!myopts
[TELOPT_BINARY
] && c
== '\r')
* Send the process on the pty side an
* interrupt. Do this with a NULL or
* interrupt char; depending on the tty mode.
ptyflush(); /* half-hearted */
ioctl(pty
, TIOCGETP
, &b
);
* Check for urgent data...
* Begin option subnegotiation...
state
= TS_WILL
+ (c
- WILL
);
state
= c
== SE
? TS_DATA
: TS_BEGINNEG
;
sprintf(nfrontp
, wont
, c
);
nfrontp
+= sizeof (wont
) - 2;
printf("telnetd: panic state=%d\n", state
);
sprintf(nfrontp
, fmt
, option
);
nfrontp
+= sizeof (dont
) - 2;
sprintf(nfrontp
, fmt
, option
);
nfrontp
+= sizeof (doopt
) - 2;
sprintf(nfrontp
, fmt
, option
);
nfrontp
+= sizeof (doopt
) - 2;
ioctl(pty
, TIOCGETP
, &b
);
ioctl(pty
, TIOCSETP
, &b
);
* Send interrupt to process on other side of pty.
* If it is in raw mode, just write NULL;
* otherwise, write intr char.
ptyflush(); /* half-hearted */
ioctl(pty
, TIOCGETP
, &b
);
*pfrontp
++ = ioctl(pty
, TIOCGETC
, &tchars
) < 0 ?
if ((n
= pfrontp
- pbackp
) > 0)
n
= write(pty
, pbackp
, n
);
pbackp
= pfrontp
= ptyobuf
;
if ((n
= nfrontp
- nbackp
) > 0)
n
= write(net
, nbackp
, n
);
if (errno
== EWOULDBLOCK
)
/* should blow this guy away... */
nbackp
= nfrontp
= netobuf
;
char wtmpf
[] = "/usr/adm/wtmp";
char utmp
[] = "/etc/utmp";
#define SCPYN(a, b) strncpy(a, b, sizeof (a))
#define SCMPN(a, b) strncmp(a, b, sizeof (a))
while(read(f
, (char *)&wtmp
, sizeof (wtmp
)) == sizeof (wtmp
)) {
if (SCMPN(wtmp
.ut_line
, line
+5) || wtmp
.ut_name
[0]==0)
lseek(f
, -(long)sizeof (wtmp
), 1);
write(f
, (char *)&wtmp
, sizeof (wtmp
));
SCPYN(wtmp
.ut_line
, line
+5);
write(f
, (char *)&wtmp
, sizeof (wtmp
));
line
[strlen("/dev/")] = 'p';
* Convert network-format internet address
* to base 256 d.d.d.d representation.
#define UC(b) (((int)b)&0xff)
sprintf(b
, "%d.%d.%d.%d", UC(p
[0]), UC(p
[1]), UC(p
[2]), UC(p
[3]));