-/* telnetd.c 4.2 82/03/01 */
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved. The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif not lint
+
+#ifndef lint
+static char sccsid[] = "@(#)telnetd.c 5.4 (Berkeley) %G%";
+#endif not lint
/*
* Stripped-down telnet server.
*/
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+
+#include <arpa/telnet.h>
+
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <sgtty.h>
-#include <wait.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <net/in.h>
-#include "telnet.h"
+#include <netdb.h>
+#include <syslog.h>
-#define INFINITY 10000000
-#define BELL '\07'
-#define swab(x) ((((x) >> 8) | ((x) << 8)) & 0xffff)
+#define BELL '\07'
+#define BANNER "\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r%s"
char hisopts[256];
char myopts[256];
char ptyibuf[BUFSIZ], *ptyip = ptyibuf;
char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
char netibuf[BUFSIZ], *netip = netibuf;
-char netobuf[BUFSIZ] =
- { IAC, DO, TELOPT_ECHO, '\r', '\n' },
- *nfrontp = netobuf + 5, *nbackp = netobuf;
+char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
int pcc, ncc;
int pty, net;
int inter;
+extern char **environ;
extern int errno;
-char line[] = "/dev/ptyp0";
-
-struct sockaddr_in sin = { AF_INET, swab(IPPORT_TELNET) };
-int options = SO_ACCEPTCONN;
-
-/*
- * Debugging hooks. Turned on with a SIGTERM.
- * Successive SIGTERM's toggle the switch.
- */
-int toggle();
-int debug;
-FILE *log;
-char logfile[80] = "/tmp/teldebugx";
+char *line;
main(argc, argv)
char *argv[];
{
- int s, pid;
- union wait status;
-
- argc--, argv++;
- if (argc > 0 && !strcmp(argv[0], "-d"))
- options |= SO_DEBUG;
- for (;;) {
- errno = 0;
- if ((s = socket(SOCK_STREAM, 0, &sin, options)) < 0) {
- perror("socket");
- sleep(5);
- continue;
- }
- if (accept(s, 0) < 0) {
- perror("accept");
- close(s);
- sleep(1);
- continue;
- }
- if ((pid = fork()) < 0)
- printf("Out of processes\n");
- else if (pid == 0)
- doit(s);
- close(s);
- while (wait3(status, WNOHANG, 0) > 0)
- continue;
+ struct sockaddr_in from;
+ int on = 1, fromlen;
+
+ openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
+ fromlen = sizeof (from);
+ if (getpeername(0, &from, &fromlen) < 0) {
+ fprintf(stderr, "%s: ", argv[0]);
+ perror("getpeername");
+ _exit(1);
}
- /*NOTREACHED*/
+ if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
+ syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
+ }
+ doit(0, &from);
}
+char *envinit[] = { "TERM=network", 0 };
int cleanup();
/*
* Get a pty, scan input lines.
*/
-doit(f)
+doit(f, who)
+ int f;
+ struct sockaddr_in *who;
{
- char *cp = line;
- int i, p, cc, t;
+ char *host, *inet_ntoa();
+ int i, p, t;
struct sgttyb b;
+ struct hostent *hp;
+ char c;
- for (i = 0; i < 16; i++) {
- cp[strlen("/dev/ptyp")] = "0123456789abcdef"[i];
- p = open(cp, 2);
- if (p > 0)
- goto gotpty;
+ for (c = 'p'; c <= 's'; c++) {
+ struct stat stb;
+
+ line = "/dev/ptyXX";
+ line[strlen("/dev/pty")] = c;
+ line[strlen("/dev/ptyp")] = '0';
+ if (stat(line, &stb) < 0)
+ break;
+ for (i = 0; i < 16; i++) {
+ line[strlen("/dev/ptyp")] = "0123456789abcdef"[i];
+ p = open(line, 2);
+ if (p > 0)
+ goto gotpty;
+ }
}
- dup2(f, 1);
- printf("All network ports in use.\n");
- exit(1);
+ fatal(f, "All network ports in use");
+ /*NOTREACHED*/
gotpty:
- logfile[strlen("/tmp/teldebug")] = "0123456789abcdef"[i];
dup2(f, 0);
- cp[strlen("/dev/")] = 't';
- t = open("/dev/tty", 2);
+ line[strlen("/dev/")] = 't';
+ t = open("/dev/tty", O_RDWR);
if (t >= 0) {
ioctl(t, TIOCNOTTY, 0);
close(t);
}
- t = open(cp, 2);
- if (t < 0) {
- dup2(f, 2);
- perror(cp);
- exit(1);
- }
+ t = open(line, O_RDWR);
+ if (t < 0)
+ fatalperror(f, line, errno);
ioctl(t, TIOCGETP, &b);
- b.sg_flags = ECHO|CRMOD|XTABS|ANYP;
+ b.sg_flags = CRMOD|XTABS|ANYP;
ioctl(t, TIOCSETP, &b);
- if ((i = fork()) < 0) {
- dup2(f, 2);
- perror("fork");
- exit(1);
- }
+ ioctl(p, TIOCGETP, &b);
+ b.sg_flags &= ~ECHO;
+ ioctl(p, TIOCSETP, &b);
+ hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr),
+ who->sin_family);
+ if (hp)
+ host = hp->h_name;
+ else
+ host = inet_ntoa(who->sin_addr);
+ if ((i = fork()) < 0)
+ fatalperror(f, "fork", errno);
if (i)
telnet(f, p);
close(f);
dup2(t, 1);
dup2(t, 2);
close(t);
- execl("/bin/login", "telnet-login", 0);
- perror("/bin/login");
+ environ = envinit;
+ execl("/bin/login", "login", "-h", host, 0);
+ fatalperror(f, "/bin/login", errno);
+ /*NOTREACHED*/
+}
+
+fatal(f, msg)
+ int f;
+ char *msg;
+{
+ char buf[BUFSIZ];
+
+ (void) sprintf(buf, "telnetd: %s.\r\n", msg);
+ (void) write(f, buf, strlen(buf));
exit(1);
}
+fatalperror(f, msg, errno)
+ int f;
+ char *msg;
+ int errno;
+{
+ char buf[BUFSIZ];
+ extern char *sys_errlist[];
+
+ (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]);
+ fatal(f, buf);
+}
+
/*
* Main loop. Select from pty and network, and
* hand data to telnet receiver finite state machine.
telnet(f, p)
{
int on = 1;
+ char hostname[32];
net = f, pty = p;
ioctl(f, FIONBIO, &on);
ioctl(p, FIONBIO, &on);
signal(SIGTSTP, SIG_IGN);
- sigset(SIGCHLD, cleanup);
- sigset(SIGTERM, toggle);
-
+ signal(SIGCHLD, cleanup);
+
+ /*
+ * Request to do remote echo.
+ */
+ dooption(TELOPT_ECHO);
+ myopts[TELOPT_ECHO] = 1;
+ /*
+ * Show banner that getty never gave.
+ */
+ gethostname(hostname, sizeof (hostname));
+ sprintf(nfrontp, BANNER, hostname, "");
+ nfrontp += strlen(nfrontp);
for (;;) {
int ibits = 0, obits = 0;
register int c;
* Never look for input if there's still
* stuff in the corresponding output buffer
*/
- if (nfrontp - nbackp)
+ if (nfrontp - nbackp || pcc > 0)
obits |= (1 << f);
else
ibits |= (1 << p);
- if (pfrontp - pbackp)
+ if (pfrontp - pbackp || ncc > 0)
obits |= (1 << p);
else
ibits |= (1 << f);
if (ncc < 0 && pcc < 0)
break;
- if (debug)
- fprintf(log, "select: ibits=%d, obits=%d\n",
- ibits, obits);
- select(32, &ibits, &obits, INFINITY);
- if (debug)
- fprintf(log, "ibits=%d, obits=%d\n", ibits, obits);
+ select(16, &ibits, &obits, 0, 0);
if (ibits == 0 && obits == 0) {
sleep(5);
continue;
*/
if (ibits & (1 << f)) {
ncc = read(f, netibuf, BUFSIZ);
- if (debug)
- fprintf(log, "read %d from net\n", ncc);
if (ncc < 0 && errno == EWOULDBLOCK)
ncc = 0;
else {
*/
if (ibits & (1 << p)) {
pcc = read(p, ptyibuf, BUFSIZ);
- if (debug)
- fprintf(log, "read %d from pty\n", pcc);
if (pcc < 0 && errno == EWOULDBLOCK)
pcc = 0;
else {
* Are You There?
*/
case AYT:
- *pfrontp++ = BELL;
+ strcpy(nfrontp, "\r\n[Yes]\r\n");
+ nfrontp += 9;
break;
/*
if (myopts[c]) {
myopts[c] = 0;
sprintf(nfrontp, wont, c);
- nfrontp += sizeof(wont) - 2;
+ nfrontp += sizeof (wont) - 2;
}
state = TS_DATA;
continue;
default:
- printf("netser: panic state=%d\n", state);
+ printf("telnetd: panic state=%d\n", state);
exit(1);
}
}
break;
}
sprintf(nfrontp, fmt, option);
- nfrontp += sizeof(dont) - 2;
+ nfrontp += sizeof (dont) - 2;
}
wontoption(option)
fmt = dont;
}
sprintf(nfrontp, fmt, option);
- nfrontp += sizeof(doopt) - 2;
+ nfrontp += sizeof (doopt) - 2;
}
dooption(option)
break;
}
sprintf(nfrontp, fmt, option);
- nfrontp += sizeof(doopt) - 2;
+ nfrontp += sizeof (doopt) - 2;
}
mode(on, off)
if ((n = pfrontp - pbackp) > 0)
n = write(pty, pbackp, n);
- if (n < 0 && errno == EWOULDBLOCK)
- n = 0;
+ if (n < 0)
+ return;
pbackp += n;
if (pbackp == pfrontp)
pbackp = pfrontp = ptyobuf;
if ((n = nfrontp - nbackp) > 0)
n = write(net, nbackp, n);
- if (n < 0 && errno == EWOULDBLOCK)
- n = 0;
+ if (n < 0) {
+ if (errno == EWOULDBLOCK)
+ return;
+ /* should blow this guy away... */
+ return;
+ }
nbackp += n;
if (nbackp == nfrontp)
nbackp = nfrontp = netobuf;
}
-toggle()
-{
- if (debug) {
- fprintf(log, "log stopped\n");
- if (log)
- fclose(log);
- } else {
- if ((log = fopen(logfile, "a")) != NULL) {
- setbuf(log, 0);
- fprintf(log, "log started on /dev/pty%c\n",
- logfile[strlen("/tmp/teldebug")]);
- fprintf(log, "net=%d, pty=%d\n", net, pty);
- }
- }
- debug = !debug;
-}
-
cleanup()
{
- int how = 2;
rmut();
- vhangup();
- ioctl(net, SIOCDONE, &how);
- kill(0, SIGKILL);
+ vhangup(); /* XXX */
+ shutdown(net, 2);
exit(1);
}
struct utmp wtmp;
char wtmpf[] = "/usr/adm/wtmp";
-char utmp[] = "/etc/utmp";
+char utmpf[] = "/etc/utmp";
#define SCPYN(a, b) strncpy(a, b, sizeof(a))
#define SCMPN(a, b) strncmp(a, b, sizeof(a))
{
register f;
int found = 0;
+ struct utmp *u, *utmp;
+ int nutmp;
+ struct stat statbf;
- f = open(utmp, 2);
+ f = open(utmpf, O_RDWR);
if (f >= 0) {
- while(read(f, (char *)&wtmp, sizeof(wtmp)) == sizeof(wtmp)) {
- if (SCMPN(wtmp.ut_line, line+5) || wtmp.ut_name[0]==0)
- continue;
- lseek(f, -(long)sizeof(wtmp), 1);
- SCPYN(wtmp.ut_name, "");
- time(&wtmp.ut_time);
- write(f, (char *)&wtmp, sizeof(wtmp));
- found++;
+ fstat(f, &statbf);
+ utmp = (struct utmp *)malloc(statbf.st_size);
+ if (!utmp)
+ syslog(LOG_ERR, "utmp malloc failed");
+ if (statbf.st_size && utmp) {
+ nutmp = read(f, utmp, statbf.st_size);
+ nutmp /= sizeof(struct utmp);
+
+ for (u = utmp ; u < &utmp[nutmp] ; u++) {
+ if (SCMPN(u->ut_line, line+5) ||
+ u->ut_name[0]==0)
+ continue;
+ lseek(f, ((long)u)-((long)utmp), L_SET);
+ SCPYN(u->ut_name, "");
+ SCPYN(u->ut_host, "");
+ time(&u->ut_time);
+ write(f, (char *)u, sizeof(wtmp));
+ found++;
+ }
}
close(f);
}
if (found) {
- f = open(wtmpf, 1);
+ f = open(wtmpf, O_WRONLY|O_APPEND);
if (f >= 0) {
SCPYN(wtmp.ut_line, line+5);
SCPYN(wtmp.ut_name, "");
+ SCPYN(wtmp.ut_host, "");
time(&wtmp.ut_time);
- lseek(f, (long)0, 2);
write(f, (char *)&wtmp, sizeof(wtmp));
close(f);
}