sys_errlist -> strerror(3)
[unix-history] / usr / src / libexec / telnetd / utility.c
/*
* Copyright (c) 1989 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
static char sccsid[] = "@(#)utility.c 5.2 (Berkeley) %G%";
#endif /* not lint */
#include "telnetd.h"
/*
* utility functions performing io related tasks
*/
/*
* ttloop
*
* A small subroutine to flush the network output buffer, get some data
* from the network, and pass it through the telnet state machine. We
* also flush the pty input buffer (by dropping its data) if it becomes
* too full.
*/
void
ttloop()
{
void netflush();
if (nfrontp-nbackp) {
netflush();
}
ncc = read(net, netibuf, sizeof netibuf);
if (ncc < 0) {
syslog(LOG_INFO, "ttloop: read: %m\n");
exit(1);
} else if (ncc == 0) {
syslog(LOG_INFO, "ttloop: peer died: %m\n");
exit(1);
}
netip = netibuf;
telrcv(); /* state machine */
if (ncc > 0) {
pfrontp = pbackp = ptyobuf;
telrcv();
}
} /* end of ttloop */
/*
* Check a descriptor to see if out of band data exists on it.
*/
stilloob(s)
int s; /* socket number */
{
static struct timeval timeout = { 0 };
fd_set excepts;
int value;
do {
FD_ZERO(&excepts);
FD_SET(s, &excepts);
value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
} while ((value == -1) && (errno == EINTR));
if (value < 0) {
fatalperror(pty, "select");
}
if (FD_ISSET(s, &excepts)) {
return 1;
} else {
return 0;
}
}
ptyflush()
{
int n;
if ((n = pfrontp - pbackp) > 0)
n = write(pty, pbackp, n);
if (n < 0)
return;
pbackp += n;
if (pbackp == pfrontp)
pbackp = pfrontp = ptyobuf;
}
/*
* nextitem()
*
* Return the address of the next "item" in the TELNET data
* stream. This will be the address of the next character if
* the current address is a user data character, or it will
* be the address of the character following the TELNET command
* if the current address is a TELNET IAC ("I Am a Command")
* character.
*/
char *
nextitem(current)
char *current;
{
if ((*current&0xff) != IAC) {
return current+1;
}
switch (*(current+1)&0xff) {
case DO:
case DONT:
case WILL:
case WONT:
return current+3;
case SB: /* loop forever looking for the SE */
{
register char *look = current+2;
for (;;) {
if ((*look++&0xff) == IAC) {
if ((*look++&0xff) == SE) {
return look;
}
}
}
}
default:
return current+2;
}
} /* end of nextitem */
/*
* netclear()
*
* We are about to do a TELNET SYNCH operation. Clear
* the path to the network.
*
* Things are a bit tricky since we may have sent the first
* byte or so of a previous TELNET command into the network.
* So, we have to scan the network buffer from the beginning
* until we are up to where we want to be.
*
* A side effect of what we do, just to keep things
* simple, is to clear the urgent data pointer. The principal
* caller should be setting the urgent data pointer AFTER calling
* us in any case.
*/
netclear()
{
register char *thisitem, *next;
char *good;
#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \
((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
thisitem = netobuf;
while ((next = nextitem(thisitem)) <= nbackp) {
thisitem = next;
}
/* Now, thisitem is first before/at boundary. */
good = netobuf; /* where the good bytes go */
while (nfrontp > thisitem) {
if (wewant(thisitem)) {
int length;
next = thisitem;
do {
next = nextitem(next);
} while (wewant(next) && (nfrontp > next));
length = next-thisitem;
bcopy(thisitem, good, length);
good += length;
thisitem = next;
} else {
thisitem = nextitem(thisitem);
}
}
nbackp = netobuf;
nfrontp = good; /* next byte to be sent */
neturg = 0;
} /* end of netclear */
/*
* netflush
* Send as much data as possible to the network,
* handling requests for urgent data.
*/
void
netflush()
{
int n;
extern int not42;
if ((n = nfrontp - nbackp) > 0) {
/*
* if no urgent data, or if the other side appears to be an
* old 4.2 client (and thus unable to survive TCP urgent data),
* write the entire buffer in non-OOB mode.
*/
if ((neturg == 0) || (not42 == 0)) {
n = write(net, nbackp, n); /* normal write */
} else {
n = neturg - nbackp;
/*
* In 4.2 (and 4.3) systems, there is some question about
* what byte in a sendOOB operation is the "OOB" data.
* To make ourselves compatible, we only send ONE byte
* out of band, the one WE THINK should be OOB (though
* we really have more the TCP philosophy of urgent data
* rather than the Unix philosophy of OOB data).
*/
if (n > 1) {
n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */
} else {
n = send(net, nbackp, n, MSG_OOB); /* URGENT data */
}
}
}
if (n < 0) {
if (errno == EWOULDBLOCK || errno == EINTR)
return;
cleanup();
}
nbackp += n;
if (nbackp >= neturg) {
neturg = 0;
}
if (nbackp == nfrontp) {
nbackp = nfrontp = netobuf;
}
return;
} /* end of netflush */
/*
* writenet
*
* Just a handy little function to write a bit of raw data to the net.
* It will force a transmit of the buffer if necessary
*
* arguments
* ptr - A pointer to a character string to write
* len - How many bytes to write
*/
writenet(ptr, len)
register char *ptr;
register int len;
{
/* flush buffer if no room for new data) */
if ((&netobuf[BUFSIZ] - nfrontp) < len) {
/* if this fails, don't worry, buffer is a little big */
netflush();
}
bcopy(ptr, nfrontp, len);
nfrontp += len;
} /* end of writenet */
/*
* miscellaneous functions doing a variety of little jobs follow ...
*/
fatal(f, msg)
int f;
char *msg;
{
char buf[BUFSIZ];
(void) sprintf(buf, "telnetd: %s.\r\n", msg);
(void) write(f, buf, (int)strlen(buf));
sleep(1); /*XXX*/
exit(1);
}
fatalperror(f, msg)
int f;
char *msg;
{
char buf[BUFSIZ], *strerror();
(void) sprintf(buf, "%s: %s\r\n", msg, strerror(errno));
fatal(f, buf);
}
char editedhost[32];
edithost(pat, host)
register char *pat;
register char *host;
{
register char *res = editedhost;
char *strncpy();
if (!pat)
pat = "";
while (*pat) {
switch (*pat) {
case '#':
if (*host)
host++;
break;
case '@':
if (*host)
*res++ = *host++;
break;
default:
*res++ = *pat;
break;
}
if (res == &editedhost[sizeof editedhost - 1]) {
*res = '\0';
return;
}
pat++;
}
if (*host)
(void) strncpy(res, host,
sizeof editedhost - (res - editedhost) -1);
else
*res = '\0';
editedhost[sizeof editedhost - 1] = '\0';
}
static char *putlocation;
putstr(s)
register char *s;
{
while (*s)
putchr(*s++);
}
putchr(cc)
{
*putlocation++ = cc;
}
putf(cp, where)
register char *cp;
char *where;
{
char *slash;
#ifndef NO_GETTYTAB
char datebuffer[60];
#endif /* NO_GETTYTAB */
extern char *rindex();
putlocation = where;
while (*cp) {
if (*cp != '%') {
putchr(*cp++);
continue;
}
switch (*++cp) {
case 't':
slash = rindex(line, '/');
if (slash == (char *) 0)
putstr(line);
else
putstr(&slash[1]);
break;
case 'h':
putstr(editedhost);
break;
#ifndef NO_GETTYTAB
case 'd':
get_date(datebuffer);
putstr(datebuffer);
break;
#endif /* NO_GETTYTAB */
case '%':
putchr('%');
break;
}
cp++;
}
}
/*ARGSUSED*/
#ifdef NO_GETTYTAB
getent(cp, name)
char *cp, *name;
{
return(0);
}
/*ARGSUSED*/
char *
getstr(cp, cpp)
char *cp, **cpp;
{
return(0);
}
#endif /* NO_GETTYTAB */