/* * 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.1 (Berkeley) 09/01/89"; #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]; extern char *sys_errlist[]; (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[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 */