utility.c revision 78527
157416Smarkm/* 257416Smarkm * Copyright (c) 1989, 1993 357416Smarkm * The Regents of the University of California. All rights reserved. 457416Smarkm * 557416Smarkm * Redistribution and use in source and binary forms, with or without 657416Smarkm * modification, are permitted provided that the following conditions 757416Smarkm * are met: 857416Smarkm * 1. Redistributions of source code must retain the above copyright 957416Smarkm * notice, this list of conditions and the following disclaimer. 1057416Smarkm * 2. Redistributions in binary form must reproduce the above copyright 1157416Smarkm * notice, this list of conditions and the following disclaimer in the 1257416Smarkm * documentation and/or other materials provided with the distribution. 1357416Smarkm * 3. All advertising materials mentioning features or use of this software 1457416Smarkm * must display the following acknowledgement: 1557416Smarkm * This product includes software developed by the University of 1657416Smarkm * California, Berkeley and its contributors. 1757416Smarkm * 4. Neither the name of the University nor the names of its contributors 1857416Smarkm * may be used to endorse or promote products derived from this software 1957416Smarkm * without specific prior written permission. 2057416Smarkm * 2157416Smarkm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2257416Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2357416Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2457416Smarkm * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2557416Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2657416Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2757416Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2857416Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2957416Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3057416Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3157416Smarkm * SUCH DAMAGE. 3257416Smarkm */ 3357416Smarkm 3457416Smarkm#define PRINTOPTIONS 3557416Smarkm#include "telnetd.h" 3657416Smarkm 3778527SassarRCSID("$Id: utility.c,v 1.25 2001/05/17 00:34:42 assar Exp $"); 3857416Smarkm 3957416Smarkm/* 4057416Smarkm * utility functions performing io related tasks 4157416Smarkm */ 4257416Smarkm 4357416Smarkm/* 4457416Smarkm * ttloop 4557416Smarkm * 4657416Smarkm * A small subroutine to flush the network output buffer, get some 4757416Smarkm * data from the network, and pass it through the telnet state 4857416Smarkm * machine. We also flush the pty input buffer (by dropping its data) 4957416Smarkm * if it becomes too full. 5057416Smarkm * 5157416Smarkm * return 0 if OK or 1 if interrupted by a signal. 5257416Smarkm */ 5357416Smarkm 5457416Smarkmint 5557416Smarkmttloop(void) 5657416Smarkm{ 5757416Smarkm void netflush(void); 5857416Smarkm 5957416Smarkm DIAG(TD_REPORT, { 6057416Smarkm output_data("td: ttloop\r\n"); 6157416Smarkm }); 6257416Smarkm if (nfrontp-nbackp) 6357416Smarkm netflush(); 6457416Smarkm ncc = read(net, netibuf, sizeof netibuf); 6557416Smarkm if (ncc < 0) { 6657416Smarkm if (errno == EINTR) 6757416Smarkm return 1; 6857416Smarkm syslog(LOG_INFO, "ttloop: read: %m\n"); 6957416Smarkm exit(1); 7057416Smarkm } else if (ncc == 0) { 7172445Sassar syslog(LOG_INFO, "ttloop: peer died\n"); 7257416Smarkm exit(1); 7357416Smarkm } 7457416Smarkm DIAG(TD_REPORT, { 7557416Smarkm output_data("td: ttloop read %d chars\r\n", ncc); 7657416Smarkm }); 7757416Smarkm netip = netibuf; 7857416Smarkm telrcv(); /* state machine */ 7957416Smarkm if (ncc > 0) { 8057416Smarkm pfrontp = pbackp = ptyobuf; 8157416Smarkm telrcv(); 8257416Smarkm } 8357416Smarkm return 0; 8457416Smarkm} /* end of ttloop */ 8557416Smarkm 8657416Smarkm/* 8757416Smarkm * Check a descriptor to see if out of band data exists on it. 8857416Smarkm */ 8957416Smarkmint 9057416Smarkmstilloob(int s) 9157416Smarkm{ 9257416Smarkm static struct timeval timeout = { 0 }; 9357416Smarkm fd_set excepts; 9457416Smarkm int value; 9557416Smarkm 9672445Sassar if (s >= FD_SETSIZE) 9772445Sassar fatal(ourpty, "fd too large"); 9872445Sassar 9957416Smarkm do { 10057416Smarkm FD_ZERO(&excepts); 10157416Smarkm FD_SET(s, &excepts); 10257416Smarkm value = select(s+1, 0, 0, &excepts, &timeout); 10357416Smarkm } while ((value == -1) && (errno == EINTR)); 10457416Smarkm 10557416Smarkm if (value < 0) { 10657416Smarkm fatalperror(ourpty, "select"); 10757416Smarkm } 10857416Smarkm if (FD_ISSET(s, &excepts)) { 10957416Smarkm return 1; 11057416Smarkm } else { 11157416Smarkm return 0; 11257416Smarkm } 11357416Smarkm} 11457416Smarkm 11557416Smarkmvoid 11657416Smarkmptyflush(void) 11757416Smarkm{ 11857416Smarkm int n; 11957416Smarkm 12057416Smarkm if ((n = pfrontp - pbackp) > 0) { 12157416Smarkm DIAG((TD_REPORT | TD_PTYDATA), { 12257416Smarkm output_data("td: ptyflush %d chars\r\n", n); 12357416Smarkm }); 12457416Smarkm DIAG(TD_PTYDATA, printdata("pd", pbackp, n)); 12557416Smarkm n = write(ourpty, pbackp, n); 12657416Smarkm } 12757416Smarkm if (n < 0) { 12857416Smarkm if (errno == EWOULDBLOCK || errno == EINTR) 12957416Smarkm return; 13057416Smarkm cleanup(0); 13157416Smarkm } 13257416Smarkm pbackp += n; 13357416Smarkm if (pbackp == pfrontp) 13457416Smarkm pbackp = pfrontp = ptyobuf; 13557416Smarkm} 13657416Smarkm 13757416Smarkm/* 13857416Smarkm * nextitem() 13957416Smarkm * 14057416Smarkm * Return the address of the next "item" in the TELNET data 14157416Smarkm * stream. This will be the address of the next character if 14257416Smarkm * the current address is a user data character, or it will 14357416Smarkm * be the address of the character following the TELNET command 14457416Smarkm * if the current address is a TELNET IAC ("I Am a Command") 14557416Smarkm * character. 14657416Smarkm */ 14757416Smarkmchar * 14857416Smarkmnextitem(char *current) 14957416Smarkm{ 15057416Smarkm if ((*current&0xff) != IAC) { 15157416Smarkm return current+1; 15257416Smarkm } 15357416Smarkm switch (*(current+1)&0xff) { 15457416Smarkm case DO: 15557416Smarkm case DONT: 15657416Smarkm case WILL: 15757416Smarkm case WONT: 15857416Smarkm return current+3; 15957416Smarkm case SB:{ 16057416Smarkm /* loop forever looking for the SE */ 16157416Smarkm char *look = current+2; 16257416Smarkm 16357416Smarkm for (;;) { 16457416Smarkm if ((*look++&0xff) == IAC) { 16557416Smarkm if ((*look++&0xff) == SE) { 16657416Smarkm return look; 16757416Smarkm } 16857416Smarkm } 16957416Smarkm } 17057416Smarkm } 17157416Smarkm default: 17257416Smarkm return current+2; 17357416Smarkm } 17457416Smarkm} 17557416Smarkm 17657416Smarkm 17757416Smarkm/* 17857416Smarkm * netclear() 17957416Smarkm * 18057416Smarkm * We are about to do a TELNET SYNCH operation. Clear 18157416Smarkm * the path to the network. 18257416Smarkm * 18357416Smarkm * Things are a bit tricky since we may have sent the first 18457416Smarkm * byte or so of a previous TELNET command into the network. 18557416Smarkm * So, we have to scan the network buffer from the beginning 18657416Smarkm * until we are up to where we want to be. 18757416Smarkm * 18857416Smarkm * A side effect of what we do, just to keep things 18957416Smarkm * simple, is to clear the urgent data pointer. The principal 19057416Smarkm * caller should be setting the urgent data pointer AFTER calling 19157416Smarkm * us in any case. 19257416Smarkm */ 19357416Smarkmvoid 19457416Smarkmnetclear(void) 19557416Smarkm{ 19657416Smarkm char *thisitem, *next; 19757416Smarkm char *good; 19857416Smarkm#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ 19957416Smarkm ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) 20057416Smarkm 20157416Smarkm#ifdef ENCRYPTION 20257416Smarkm thisitem = nclearto > netobuf ? nclearto : netobuf; 20357416Smarkm#else 20457416Smarkm thisitem = netobuf; 20557416Smarkm#endif 20657416Smarkm 20757416Smarkm while ((next = nextitem(thisitem)) <= nbackp) { 20857416Smarkm thisitem = next; 20957416Smarkm } 21057416Smarkm 21157416Smarkm /* Now, thisitem is first before/at boundary. */ 21257416Smarkm 21357416Smarkm#ifdef ENCRYPTION 21457416Smarkm good = nclearto > netobuf ? nclearto : netobuf; 21557416Smarkm#else 21657416Smarkm good = netobuf; /* where the good bytes go */ 21757416Smarkm#endif 21857416Smarkm 21957416Smarkm while (nfrontp > thisitem) { 22057416Smarkm if (wewant(thisitem)) { 22157416Smarkm int length; 22257416Smarkm 22357416Smarkm next = thisitem; 22457416Smarkm do { 22557416Smarkm next = nextitem(next); 22657416Smarkm } while (wewant(next) && (nfrontp > next)); 22757416Smarkm length = next-thisitem; 22857416Smarkm memmove(good, thisitem, length); 22957416Smarkm good += length; 23057416Smarkm thisitem = next; 23157416Smarkm } else { 23257416Smarkm thisitem = nextitem(thisitem); 23357416Smarkm } 23457416Smarkm } 23557416Smarkm 23657416Smarkm nbackp = netobuf; 23757416Smarkm nfrontp = good; /* next byte to be sent */ 23857416Smarkm neturg = 0; 23957416Smarkm} /* end of netclear */ 24057416Smarkm 24157416Smarkm/* 24257416Smarkm * netflush 24357416Smarkm * Send as much data as possible to the network, 24457416Smarkm * handling requests for urgent data. 24557416Smarkm */ 24657416Smarkmvoid 24757416Smarkmnetflush(void) 24857416Smarkm{ 24957416Smarkm int n; 25057416Smarkm extern int not42; 25157416Smarkm 25257416Smarkm if ((n = nfrontp - nbackp) > 0) { 25357416Smarkm DIAG(TD_REPORT, 25457416Smarkm { n += output_data("td: netflush %d chars\r\n", n); 25557416Smarkm }); 25657416Smarkm#ifdef ENCRYPTION 25757416Smarkm if (encrypt_output) { 25857416Smarkm char *s = nclearto ? nclearto : nbackp; 25957416Smarkm if (nfrontp - s > 0) { 26057416Smarkm (*encrypt_output)((unsigned char *)s, nfrontp-s); 26157416Smarkm nclearto = nfrontp; 26257416Smarkm } 26357416Smarkm } 26457416Smarkm#endif 26557416Smarkm /* 26657416Smarkm * if no urgent data, or if the other side appears to be an 26757416Smarkm * old 4.2 client (and thus unable to survive TCP urgent data), 26857416Smarkm * write the entire buffer in non-OOB mode. 26957416Smarkm */ 27057416Smarkm#if 1 /* remove this to make it work between solaris 2.6 and linux */ 27157416Smarkm if ((neturg == 0) || (not42 == 0)) { 27257416Smarkm#endif 27357416Smarkm n = write(net, nbackp, n); /* normal write */ 27457416Smarkm#if 1 /* remove this to make it work between solaris 2.6 and linux */ 27557416Smarkm } else { 27657416Smarkm n = neturg - nbackp; 27757416Smarkm /* 27857416Smarkm * In 4.2 (and 4.3) systems, there is some question about 27957416Smarkm * what byte in a sendOOB operation is the "OOB" data. 28057416Smarkm * To make ourselves compatible, we only send ONE byte 28157416Smarkm * out of band, the one WE THINK should be OOB (though 28257416Smarkm * we really have more the TCP philosophy of urgent data 28357416Smarkm * rather than the Unix philosophy of OOB data). 28457416Smarkm */ 28557416Smarkm if (n > 1) { 28657416Smarkm n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ 28757416Smarkm } else { 28857416Smarkm n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ 28957416Smarkm } 29057416Smarkm } 29157416Smarkm#endif 29257416Smarkm } 29357416Smarkm if (n < 0) { 29457416Smarkm if (errno == EWOULDBLOCK || errno == EINTR) 29557416Smarkm return; 29657416Smarkm cleanup(0); 29757416Smarkm } 29857416Smarkm nbackp += n; 29957416Smarkm#ifdef ENCRYPTION 30057416Smarkm if (nbackp > nclearto) 30157416Smarkm nclearto = 0; 30257416Smarkm#endif 30357416Smarkm if (nbackp >= neturg) { 30457416Smarkm neturg = 0; 30557416Smarkm } 30657416Smarkm if (nbackp == nfrontp) { 30757416Smarkm nbackp = nfrontp = netobuf; 30857416Smarkm#ifdef ENCRYPTION 30957416Smarkm nclearto = 0; 31057416Smarkm#endif 31157416Smarkm } 31257416Smarkm return; 31357416Smarkm} 31457416Smarkm 31557416Smarkm 31657416Smarkm/* 31757416Smarkm * writenet 31857416Smarkm * 31957416Smarkm * Just a handy little function to write a bit of raw data to the net. 32057416Smarkm * It will force a transmit of the buffer if necessary 32157416Smarkm * 32257416Smarkm * arguments 32357416Smarkm * ptr - A pointer to a character string to write 32457416Smarkm * len - How many bytes to write 32557416Smarkm */ 32657416Smarkmvoid 32757416Smarkmwritenet(unsigned char *ptr, int len) 32857416Smarkm{ 32957416Smarkm /* flush buffer if no room for new data) */ 33057416Smarkm while ((&netobuf[BUFSIZ] - nfrontp) < len) { 33157416Smarkm /* if this fails, don't worry, buffer is a little big */ 33257416Smarkm netflush(); 33357416Smarkm } 33457416Smarkm 33557416Smarkm memmove(nfrontp, ptr, len); 33657416Smarkm nfrontp += len; 33757416Smarkm} 33857416Smarkm 33957416Smarkm 34057416Smarkm/* 34157416Smarkm * miscellaneous functions doing a variety of little jobs follow ... 34257416Smarkm */ 34357416Smarkm 34457416Smarkm 34557416Smarkmvoid fatal(int f, char *msg) 34657416Smarkm{ 34757416Smarkm char buf[BUFSIZ]; 34857416Smarkm 34957416Smarkm snprintf(buf, sizeof(buf), "telnetd: %s.\r\n", msg); 35057416Smarkm#ifdef ENCRYPTION 35157416Smarkm if (encrypt_output) { 35257416Smarkm /* 35357416Smarkm * Better turn off encryption first.... 35457416Smarkm * Hope it flushes... 35557416Smarkm */ 35657416Smarkm encrypt_send_end(); 35757416Smarkm netflush(); 35857416Smarkm } 35957416Smarkm#endif 36057416Smarkm write(f, buf, (int)strlen(buf)); 36157416Smarkm sleep(1); /*XXX*/ 36257416Smarkm exit(1); 36357416Smarkm} 36457416Smarkm 36557416Smarkmvoid 36678527Sassarfatalperror_errno(int f, const char *msg, int error) 36757416Smarkm{ 36857416Smarkm char buf[BUFSIZ]; 36957416Smarkm 37078527Sassar snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(error)); 37157416Smarkm fatal(f, buf); 37257416Smarkm} 37357416Smarkm 37478527Sassarvoid 37578527Sassarfatalperror(int f, const char *msg) 37678527Sassar{ 37778527Sassar fatalperror_errno(f, msg, errno); 37878527Sassar} 37978527Sassar 38057416Smarkmchar editedhost[32]; 38157416Smarkm 38257416Smarkmvoid edithost(char *pat, char *host) 38357416Smarkm{ 38457416Smarkm char *res = editedhost; 38557416Smarkm 38657416Smarkm if (!pat) 38757416Smarkm pat = ""; 38857416Smarkm while (*pat) { 38957416Smarkm switch (*pat) { 39057416Smarkm 39157416Smarkm case '#': 39257416Smarkm if (*host) 39357416Smarkm host++; 39457416Smarkm break; 39557416Smarkm 39657416Smarkm case '@': 39757416Smarkm if (*host) 39857416Smarkm *res++ = *host++; 39957416Smarkm break; 40057416Smarkm 40157416Smarkm default: 40257416Smarkm *res++ = *pat; 40357416Smarkm break; 40457416Smarkm } 40557416Smarkm if (res == &editedhost[sizeof editedhost - 1]) { 40657416Smarkm *res = '\0'; 40757416Smarkm return; 40857416Smarkm } 40957416Smarkm pat++; 41057416Smarkm } 41157416Smarkm if (*host) 41257416Smarkm strlcpy (res, host, 41357416Smarkm sizeof editedhost - (res - editedhost)); 41457416Smarkm else 41557416Smarkm *res = '\0'; 41657416Smarkm editedhost[sizeof editedhost - 1] = '\0'; 41757416Smarkm} 41857416Smarkm 41957416Smarkmstatic char *putlocation; 42057416Smarkm 42157416Smarkmvoid 42257416Smarkmputstr(char *s) 42357416Smarkm{ 42457416Smarkm 42557416Smarkm while (*s) 42657416Smarkm putchr(*s++); 42757416Smarkm} 42857416Smarkm 42957416Smarkmvoid 43057416Smarkmputchr(int cc) 43157416Smarkm{ 43257416Smarkm *putlocation++ = cc; 43357416Smarkm} 43457416Smarkm 43557416Smarkm/* 43657416Smarkm * This is split on two lines so that SCCS will not see the M 43757416Smarkm * between two % signs and expand it... 43857416Smarkm */ 43957416Smarkmstatic char fmtstr[] = { "%l:%M" "%P on %A, %d %B %Y" }; 44057416Smarkm 44157416Smarkmvoid putf(char *cp, char *where) 44257416Smarkm{ 44357416Smarkm#ifdef HAVE_UNAME 44457416Smarkm struct utsname name; 44557416Smarkm#endif 44657416Smarkm char *slash; 44757416Smarkm time_t t; 44857416Smarkm char db[100]; 44957416Smarkm 45057416Smarkm /* if we don't have uname, set these to sensible values */ 45157416Smarkm char *sysname = "Unix", 45257416Smarkm *machine = "", 45357416Smarkm *release = "", 45457416Smarkm *version = ""; 45557416Smarkm 45657416Smarkm#ifdef HAVE_UNAME 45757416Smarkm uname(&name); 45857416Smarkm sysname=name.sysname; 45957416Smarkm machine=name.machine; 46057416Smarkm release=name.release; 46157416Smarkm version=name.version; 46257416Smarkm#endif 46357416Smarkm 46457416Smarkm putlocation = where; 46557416Smarkm 46657416Smarkm while (*cp) { 46757416Smarkm if (*cp != '%') { 46857416Smarkm putchr(*cp++); 46957416Smarkm continue; 47057416Smarkm } 47157416Smarkm switch (*++cp) { 47257416Smarkm 47357416Smarkm case 't': 47457416Smarkm#ifdef STREAMSPTY 47557416Smarkm /* names are like /dev/pts/2 -- we want pts/2 */ 47657416Smarkm slash = strchr(line+1, '/'); 47757416Smarkm#else 47857416Smarkm slash = strrchr(line, '/'); 47957416Smarkm#endif 48057416Smarkm if (slash == (char *) 0) 48157416Smarkm putstr(line); 48257416Smarkm else 48357416Smarkm putstr(&slash[1]); 48457416Smarkm break; 48557416Smarkm 48657416Smarkm case 'h': 48757416Smarkm putstr(editedhost); 48857416Smarkm break; 48957416Smarkm 49057416Smarkm case 's': 49157416Smarkm putstr(sysname); 49257416Smarkm break; 49357416Smarkm 49457416Smarkm case 'm': 49557416Smarkm putstr(machine); 49657416Smarkm break; 49757416Smarkm 49857416Smarkm case 'r': 49957416Smarkm putstr(release); 50057416Smarkm break; 50157416Smarkm 50257416Smarkm case 'v': 50357416Smarkm putstr(version); 50457416Smarkm break; 50557416Smarkm 50657416Smarkm case 'd': 50757416Smarkm time(&t); 50857416Smarkm strftime(db, sizeof(db), fmtstr, localtime(&t)); 50957416Smarkm putstr(db); 51057416Smarkm break; 51157416Smarkm 51257416Smarkm case '%': 51357416Smarkm putchr('%'); 51457416Smarkm break; 51557416Smarkm } 51657416Smarkm cp++; 51757416Smarkm } 51857416Smarkm} 51957416Smarkm 52057416Smarkm#ifdef DIAGNOSTICS 52157416Smarkm/* 52257416Smarkm * Print telnet options and commands in plain text, if possible. 52357416Smarkm */ 52457416Smarkmvoid 52557416Smarkmprintoption(char *fmt, int option) 52657416Smarkm{ 52757416Smarkm if (TELOPT_OK(option)) 52857416Smarkm output_data("%s %s\r\n", 52957416Smarkm fmt, 53057416Smarkm TELOPT(option)); 53157416Smarkm else if (TELCMD_OK(option)) 53257416Smarkm output_data("%s %s\r\n", 53357416Smarkm fmt, 53457416Smarkm TELCMD(option)); 53557416Smarkm else 53657416Smarkm output_data("%s %d\r\n", 53757416Smarkm fmt, 53857416Smarkm option); 53957416Smarkm return; 54057416Smarkm} 54157416Smarkm 54257416Smarkmvoid 54357416Smarkmprintsub(int direction, unsigned char *pointer, int length) 54457416Smarkm /* '<' or '>' */ 54557416Smarkm /* where suboption data sits */ 54657416Smarkm /* length of suboption data */ 54757416Smarkm{ 54857416Smarkm int i = 0; 54957416Smarkm unsigned char buf[512]; 55057416Smarkm 55157416Smarkm if (!(diagnostic & TD_OPTIONS)) 55257416Smarkm return; 55357416Smarkm 55457416Smarkm if (direction) { 55557416Smarkm output_data("td: %s suboption ", 55657416Smarkm direction == '<' ? "recv" : "send"); 55757416Smarkm if (length >= 3) { 55857416Smarkm int j; 55957416Smarkm 56057416Smarkm i = pointer[length-2]; 56157416Smarkm j = pointer[length-1]; 56257416Smarkm 56357416Smarkm if (i != IAC || j != SE) { 56457416Smarkm output_data("(terminated by "); 56557416Smarkm if (TELOPT_OK(i)) 56657416Smarkm output_data("%s ", 56757416Smarkm TELOPT(i)); 56857416Smarkm else if (TELCMD_OK(i)) 56957416Smarkm output_data("%s ", 57057416Smarkm TELCMD(i)); 57157416Smarkm else 57257416Smarkm output_data("%d ", 57357416Smarkm i); 57457416Smarkm if (TELOPT_OK(j)) 57557416Smarkm output_data("%s", 57657416Smarkm TELOPT(j)); 57757416Smarkm else if (TELCMD_OK(j)) 57857416Smarkm output_data("%s", 57957416Smarkm TELCMD(j)); 58057416Smarkm else 58157416Smarkm output_data("%d", 58257416Smarkm j); 58357416Smarkm output_data(", not IAC SE!) "); 58457416Smarkm } 58557416Smarkm } 58657416Smarkm length -= 2; 58757416Smarkm } 58857416Smarkm if (length < 1) { 58957416Smarkm output_data("(Empty suboption??\?)"); 59057416Smarkm return; 59157416Smarkm } 59257416Smarkm switch (pointer[0]) { 59357416Smarkm case TELOPT_TTYPE: 59457416Smarkm output_data("TERMINAL-TYPE "); 59557416Smarkm switch (pointer[1]) { 59657416Smarkm case TELQUAL_IS: 59757416Smarkm output_data("IS \"%.*s\"", 59857416Smarkm length-2, 59957416Smarkm (char *)pointer+2); 60057416Smarkm break; 60157416Smarkm case TELQUAL_SEND: 60257416Smarkm output_data("SEND"); 60357416Smarkm break; 60457416Smarkm default: 60557416Smarkm output_data("- unknown qualifier %d (0x%x).", 60657416Smarkm pointer[1], pointer[1]); 60757416Smarkm } 60857416Smarkm break; 60957416Smarkm case TELOPT_TSPEED: 61057416Smarkm output_data("TERMINAL-SPEED"); 61157416Smarkm if (length < 2) { 61257416Smarkm output_data(" (empty suboption??\?)"); 61357416Smarkm break; 61457416Smarkm } 61557416Smarkm switch (pointer[1]) { 61657416Smarkm case TELQUAL_IS: 61757416Smarkm output_data(" IS %.*s", length-2, (char *)pointer+2); 61857416Smarkm break; 61957416Smarkm default: 62057416Smarkm if (pointer[1] == 1) 62157416Smarkm output_data(" SEND"); 62257416Smarkm else 62357416Smarkm output_data(" %d (unknown)", pointer[1]); 62457416Smarkm for (i = 2; i < length; i++) { 62557416Smarkm output_data(" ?%d?", pointer[i]); 62657416Smarkm } 62757416Smarkm break; 62857416Smarkm } 62957416Smarkm break; 63057416Smarkm 63157416Smarkm case TELOPT_LFLOW: 63257416Smarkm output_data("TOGGLE-FLOW-CONTROL"); 63357416Smarkm if (length < 2) { 63457416Smarkm output_data(" (empty suboption??\?)"); 63557416Smarkm break; 63657416Smarkm } 63757416Smarkm switch (pointer[1]) { 63857416Smarkm case LFLOW_OFF: 63957416Smarkm output_data(" OFF"); 64057416Smarkm break; 64157416Smarkm case LFLOW_ON: 64257416Smarkm output_data(" ON"); 64357416Smarkm break; 64457416Smarkm case LFLOW_RESTART_ANY: 64557416Smarkm output_data(" RESTART-ANY"); 64657416Smarkm break; 64757416Smarkm case LFLOW_RESTART_XON: 64857416Smarkm output_data(" RESTART-XON"); 64957416Smarkm break; 65057416Smarkm default: 65157416Smarkm output_data(" %d (unknown)", 65257416Smarkm pointer[1]); 65357416Smarkm } 65457416Smarkm for (i = 2; i < length; i++) { 65557416Smarkm output_data(" ?%d?", 65657416Smarkm pointer[i]); 65757416Smarkm } 65857416Smarkm break; 65957416Smarkm 66057416Smarkm case TELOPT_NAWS: 66157416Smarkm output_data("NAWS"); 66257416Smarkm if (length < 2) { 66357416Smarkm output_data(" (empty suboption??\?)"); 66457416Smarkm break; 66557416Smarkm } 66657416Smarkm if (length == 2) { 66757416Smarkm output_data(" ?%d?", 66857416Smarkm pointer[1]); 66957416Smarkm break; 67057416Smarkm } 67157416Smarkm output_data(" %u %u(%u)", 67257416Smarkm pointer[1], 67357416Smarkm pointer[2], 67457416Smarkm (((unsigned int)pointer[1])<<8) + pointer[2]); 67557416Smarkm if (length == 4) { 67657416Smarkm output_data(" ?%d?", 67757416Smarkm pointer[3]); 67857416Smarkm break; 67957416Smarkm } 68057416Smarkm output_data(" %u %u(%u)", 68157416Smarkm pointer[3], 68257416Smarkm pointer[4], 68357416Smarkm (((unsigned int)pointer[3])<<8) + pointer[4]); 68457416Smarkm for (i = 5; i < length; i++) { 68557416Smarkm output_data(" ?%d?", 68657416Smarkm pointer[i]); 68757416Smarkm } 68857416Smarkm break; 68957416Smarkm 69057416Smarkm case TELOPT_LINEMODE: 69157416Smarkm output_data("LINEMODE "); 69257416Smarkm if (length < 2) { 69357416Smarkm output_data(" (empty suboption??\?)"); 69457416Smarkm break; 69557416Smarkm } 69657416Smarkm switch (pointer[1]) { 69757416Smarkm case WILL: 69857416Smarkm output_data("WILL "); 69957416Smarkm goto common; 70057416Smarkm case WONT: 70157416Smarkm output_data("WONT "); 70257416Smarkm goto common; 70357416Smarkm case DO: 70457416Smarkm output_data("DO "); 70557416Smarkm goto common; 70657416Smarkm case DONT: 70757416Smarkm output_data("DONT "); 70857416Smarkm common: 70957416Smarkm if (length < 3) { 71057416Smarkm output_data("(no option??\?)"); 71157416Smarkm break; 71257416Smarkm } 71357416Smarkm switch (pointer[2]) { 71457416Smarkm case LM_FORWARDMASK: 71557416Smarkm output_data("Forward Mask"); 71657416Smarkm for (i = 3; i < length; i++) { 71757416Smarkm output_data(" %x", pointer[i]); 71857416Smarkm } 71957416Smarkm break; 72057416Smarkm default: 72157416Smarkm output_data("%d (unknown)", 72257416Smarkm pointer[2]); 72357416Smarkm for (i = 3; i < length; i++) { 72457416Smarkm output_data(" %d", 72557416Smarkm pointer[i]); 72657416Smarkm } 72757416Smarkm break; 72857416Smarkm } 72957416Smarkm break; 73057416Smarkm 73157416Smarkm case LM_SLC: 73257416Smarkm output_data("SLC"); 73357416Smarkm for (i = 2; i < length - 2; i += 3) { 73457416Smarkm if (SLC_NAME_OK(pointer[i+SLC_FUNC])) 73557416Smarkm output_data(" %s", 73657416Smarkm SLC_NAME(pointer[i+SLC_FUNC])); 73757416Smarkm else 73857416Smarkm output_data(" %d", 73957416Smarkm pointer[i+SLC_FUNC]); 74057416Smarkm switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) { 74157416Smarkm case SLC_NOSUPPORT: 74257416Smarkm output_data(" NOSUPPORT"); 74357416Smarkm break; 74457416Smarkm case SLC_CANTCHANGE: 74557416Smarkm output_data(" CANTCHANGE"); 74657416Smarkm break; 74757416Smarkm case SLC_VARIABLE: 74857416Smarkm output_data(" VARIABLE"); 74957416Smarkm break; 75057416Smarkm case SLC_DEFAULT: 75157416Smarkm output_data(" DEFAULT"); 75257416Smarkm break; 75357416Smarkm } 75457416Smarkm output_data("%s%s%s", 75557416Smarkm pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "", 75657416Smarkm pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "", 75757416Smarkm pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : ""); 75857416Smarkm if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN| 75957416Smarkm SLC_FLUSHOUT| SLC_LEVELBITS)) { 76057416Smarkm output_data("(0x%x)", 76157416Smarkm pointer[i+SLC_FLAGS]); 76257416Smarkm } 76357416Smarkm output_data(" %d;", 76457416Smarkm pointer[i+SLC_VALUE]); 76557416Smarkm if ((pointer[i+SLC_VALUE] == IAC) && 76657416Smarkm (pointer[i+SLC_VALUE+1] == IAC)) 76757416Smarkm i++; 76857416Smarkm } 76957416Smarkm for (; i < length; i++) { 77057416Smarkm output_data(" ?%d?", 77157416Smarkm pointer[i]); 77257416Smarkm } 77357416Smarkm break; 77457416Smarkm 77557416Smarkm case LM_MODE: 77657416Smarkm output_data("MODE "); 77757416Smarkm if (length < 3) { 77857416Smarkm output_data("(no mode??\?)"); 77957416Smarkm break; 78057416Smarkm } 78157416Smarkm { 78257416Smarkm char tbuf[32]; 78357416Smarkm snprintf(tbuf, 78457416Smarkm sizeof(tbuf), 78557416Smarkm "%s%s%s%s%s", 78657416Smarkm pointer[2]&MODE_EDIT ? "|EDIT" : "", 78757416Smarkm pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "", 78857416Smarkm pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "", 78957416Smarkm pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "", 79057416Smarkm pointer[2]&MODE_ACK ? "|ACK" : ""); 79157416Smarkm output_data("%s", 79257416Smarkm tbuf[1] ? &tbuf[1] : "0"); 79357416Smarkm } 79457416Smarkm if (pointer[2]&~(MODE_EDIT|MODE_TRAPSIG|MODE_ACK)) { 79557416Smarkm output_data(" (0x%x)", 79657416Smarkm pointer[2]); 79757416Smarkm } 79857416Smarkm for (i = 3; i < length; i++) { 79957416Smarkm output_data(" ?0x%x?", 80057416Smarkm pointer[i]); 80157416Smarkm } 80257416Smarkm break; 80357416Smarkm default: 80457416Smarkm output_data("%d (unknown)", 80557416Smarkm pointer[1]); 80657416Smarkm for (i = 2; i < length; i++) { 80757416Smarkm output_data(" %d", pointer[i]); 80857416Smarkm } 80957416Smarkm } 81057416Smarkm break; 81157416Smarkm 81257416Smarkm case TELOPT_STATUS: { 81357416Smarkm char *cp; 81457416Smarkm int j, k; 81557416Smarkm 81657416Smarkm output_data("STATUS"); 81757416Smarkm 81857416Smarkm switch (pointer[1]) { 81957416Smarkm default: 82057416Smarkm if (pointer[1] == TELQUAL_SEND) 82157416Smarkm output_data(" SEND"); 82257416Smarkm else 82357416Smarkm output_data(" %d (unknown)", 82457416Smarkm pointer[1]); 82557416Smarkm for (i = 2; i < length; i++) { 82657416Smarkm output_data(" ?%d?", 82757416Smarkm pointer[i]); 82857416Smarkm } 82957416Smarkm break; 83057416Smarkm case TELQUAL_IS: 83157416Smarkm output_data(" IS\r\n"); 83257416Smarkm 83357416Smarkm for (i = 2; i < length; i++) { 83457416Smarkm switch(pointer[i]) { 83557416Smarkm case DO: cp = "DO"; goto common2; 83657416Smarkm case DONT: cp = "DONT"; goto common2; 83757416Smarkm case WILL: cp = "WILL"; goto common2; 83857416Smarkm case WONT: cp = "WONT"; goto common2; 83957416Smarkm common2: 84057416Smarkm i++; 84157416Smarkm if (TELOPT_OK(pointer[i])) 84257416Smarkm output_data(" %s %s", 84357416Smarkm cp, 84457416Smarkm TELOPT(pointer[i])); 84557416Smarkm else 84657416Smarkm output_data(" %s %d", 84757416Smarkm cp, 84857416Smarkm pointer[i]); 84957416Smarkm 85057416Smarkm output_data("\r\n"); 85157416Smarkm break; 85257416Smarkm 85357416Smarkm case SB: 85457416Smarkm output_data(" SB "); 85557416Smarkm i++; 85657416Smarkm j = k = i; 85757416Smarkm while (j < length) { 85857416Smarkm if (pointer[j] == SE) { 85957416Smarkm if (j+1 == length) 86057416Smarkm break; 86157416Smarkm if (pointer[j+1] == SE) 86257416Smarkm j++; 86357416Smarkm else 86457416Smarkm break; 86557416Smarkm } 86657416Smarkm pointer[k++] = pointer[j++]; 86757416Smarkm } 86857416Smarkm printsub(0, &pointer[i], k - i); 86957416Smarkm if (i < length) { 87057416Smarkm output_data(" SE"); 87157416Smarkm i = j; 87257416Smarkm } else 87357416Smarkm i = j - 1; 87457416Smarkm 87557416Smarkm output_data("\r\n"); 87657416Smarkm 87757416Smarkm break; 87857416Smarkm 87957416Smarkm default: 88057416Smarkm output_data(" %d", 88157416Smarkm pointer[i]); 88257416Smarkm break; 88357416Smarkm } 88457416Smarkm } 88557416Smarkm break; 88657416Smarkm } 88757416Smarkm break; 88857416Smarkm } 88957416Smarkm 89057416Smarkm case TELOPT_XDISPLOC: 89157416Smarkm output_data("X-DISPLAY-LOCATION "); 89257416Smarkm switch (pointer[1]) { 89357416Smarkm case TELQUAL_IS: 89457416Smarkm output_data("IS \"%.*s\"", 89557416Smarkm length-2, 89657416Smarkm (char *)pointer+2); 89757416Smarkm break; 89857416Smarkm case TELQUAL_SEND: 89957416Smarkm output_data("SEND"); 90057416Smarkm break; 90157416Smarkm default: 90257416Smarkm output_data("- unknown qualifier %d (0x%x).", 90357416Smarkm pointer[1], pointer[1]); 90457416Smarkm } 90557416Smarkm break; 90657416Smarkm 90757416Smarkm case TELOPT_NEW_ENVIRON: 90857416Smarkm output_data("NEW-ENVIRON "); 90957416Smarkm goto env_common1; 91057416Smarkm case TELOPT_OLD_ENVIRON: 91157416Smarkm output_data("OLD-ENVIRON"); 91257416Smarkm env_common1: 91357416Smarkm switch (pointer[1]) { 91457416Smarkm case TELQUAL_IS: 91557416Smarkm output_data("IS "); 91657416Smarkm goto env_common; 91757416Smarkm case TELQUAL_SEND: 91857416Smarkm output_data("SEND "); 91957416Smarkm goto env_common; 92057416Smarkm case TELQUAL_INFO: 92157416Smarkm output_data("INFO "); 92257416Smarkm env_common: 92357416Smarkm { 92457416Smarkm int noquote = 2; 92557416Smarkm for (i = 2; i < length; i++ ) { 92657416Smarkm switch (pointer[i]) { 92757416Smarkm case NEW_ENV_VAR: 92857416Smarkm output_data("\" VAR " + noquote); 92957416Smarkm noquote = 2; 93057416Smarkm break; 93157416Smarkm 93257416Smarkm case NEW_ENV_VALUE: 93357416Smarkm output_data("\" VALUE " + noquote); 93457416Smarkm noquote = 2; 93557416Smarkm break; 93657416Smarkm 93757416Smarkm case ENV_ESC: 93857416Smarkm output_data("\" ESC " + noquote); 93957416Smarkm noquote = 2; 94057416Smarkm break; 94157416Smarkm 94257416Smarkm case ENV_USERVAR: 94357416Smarkm output_data("\" USERVAR " + noquote); 94457416Smarkm noquote = 2; 94557416Smarkm break; 94657416Smarkm 94757416Smarkm default: 94857416Smarkm if (isprint(pointer[i]) && pointer[i] != '"') { 94957416Smarkm if (noquote) { 95057416Smarkm output_data ("\""); 95157416Smarkm noquote = 0; 95257416Smarkm } 95357416Smarkm output_data ("%c", pointer[i]); 95457416Smarkm } else { 95557416Smarkm output_data("\" %03o " + noquote, 95657416Smarkm pointer[i]); 95757416Smarkm noquote = 2; 95857416Smarkm } 95957416Smarkm break; 96057416Smarkm } 96157416Smarkm } 96257416Smarkm if (!noquote) 96357416Smarkm output_data ("\""); 96457416Smarkm break; 96557416Smarkm } 96657416Smarkm } 96757416Smarkm break; 96857416Smarkm 96957416Smarkm#ifdef AUTHENTICATION 97057416Smarkm case TELOPT_AUTHENTICATION: 97157416Smarkm output_data("AUTHENTICATION"); 97257416Smarkm 97357416Smarkm if (length < 2) { 97457416Smarkm output_data(" (empty suboption??\?)"); 97557416Smarkm break; 97657416Smarkm } 97757416Smarkm switch (pointer[1]) { 97857416Smarkm case TELQUAL_REPLY: 97957416Smarkm case TELQUAL_IS: 98057416Smarkm output_data(" %s ", 98157416Smarkm (pointer[1] == TELQUAL_IS) ? 98257416Smarkm "IS" : "REPLY"); 98357416Smarkm if (AUTHTYPE_NAME_OK(pointer[2])) 98457416Smarkm output_data("%s ", 98557416Smarkm AUTHTYPE_NAME(pointer[2])); 98657416Smarkm else 98757416Smarkm output_data("%d ", 98857416Smarkm pointer[2]); 98957416Smarkm if (length < 3) { 99057416Smarkm output_data("(partial suboption??\?)"); 99157416Smarkm break; 99257416Smarkm } 99357416Smarkm output_data("%s|%s", 99457416Smarkm ((pointer[3] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ? 99557416Smarkm "CLIENT" : "SERVER", 99657416Smarkm ((pointer[3] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ? 99757416Smarkm "MUTUAL" : "ONE-WAY"); 99857416Smarkm 99957416Smarkm auth_printsub(&pointer[1], length - 1, buf, sizeof(buf)); 100057416Smarkm output_data("%s", 100157416Smarkm buf); 100257416Smarkm break; 100357416Smarkm 100457416Smarkm case TELQUAL_SEND: 100557416Smarkm i = 2; 100657416Smarkm output_data(" SEND "); 100757416Smarkm while (i < length) { 100857416Smarkm if (AUTHTYPE_NAME_OK(pointer[i])) 100957416Smarkm output_data("%s ", 101057416Smarkm AUTHTYPE_NAME(pointer[i])); 101157416Smarkm else 101257416Smarkm output_data("%d ", 101357416Smarkm pointer[i]); 101457416Smarkm if (++i >= length) { 101557416Smarkm output_data("(partial suboption??\?)"); 101657416Smarkm break; 101757416Smarkm } 101857416Smarkm output_data("%s|%s ", 101957416Smarkm ((pointer[i] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ? 102057416Smarkm "CLIENT" : "SERVER", 102157416Smarkm ((pointer[i] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ? 102257416Smarkm "MUTUAL" : "ONE-WAY"); 102357416Smarkm ++i; 102457416Smarkm } 102557416Smarkm break; 102657416Smarkm 102757416Smarkm case TELQUAL_NAME: 102857416Smarkm i = 2; 102957416Smarkm output_data(" NAME \"%.*s\"", 103057416Smarkm length - 2, 103157416Smarkm pointer); 103257416Smarkm break; 103357416Smarkm 103457416Smarkm default: 103557416Smarkm for (i = 2; i < length; i++) { 103657416Smarkm output_data(" ?%d?", 103757416Smarkm pointer[i]); 103857416Smarkm } 103957416Smarkm break; 104057416Smarkm } 104157416Smarkm break; 104257416Smarkm#endif 104357416Smarkm 104457416Smarkm#ifdef ENCRYPTION 104557416Smarkm case TELOPT_ENCRYPT: 104657416Smarkm output_data("ENCRYPT"); 104757416Smarkm if (length < 2) { 104857416Smarkm output_data(" (empty suboption?)"); 104957416Smarkm break; 105057416Smarkm } 105157416Smarkm switch (pointer[1]) { 105257416Smarkm case ENCRYPT_START: 105357416Smarkm output_data(" START"); 105457416Smarkm break; 105557416Smarkm 105657416Smarkm case ENCRYPT_END: 105757416Smarkm output_data(" END"); 105857416Smarkm break; 105957416Smarkm 106057416Smarkm case ENCRYPT_REQSTART: 106157416Smarkm output_data(" REQUEST-START"); 106257416Smarkm break; 106357416Smarkm 106457416Smarkm case ENCRYPT_REQEND: 106557416Smarkm output_data(" REQUEST-END"); 106657416Smarkm break; 106757416Smarkm 106857416Smarkm case ENCRYPT_IS: 106957416Smarkm case ENCRYPT_REPLY: 107057416Smarkm output_data(" %s ", 107157416Smarkm (pointer[1] == ENCRYPT_IS) ? 107257416Smarkm "IS" : "REPLY"); 107357416Smarkm if (length < 3) { 107457416Smarkm output_data(" (partial suboption?)"); 107557416Smarkm break; 107657416Smarkm } 107757416Smarkm if (ENCTYPE_NAME_OK(pointer[2])) 107857416Smarkm output_data("%s ", 107957416Smarkm ENCTYPE_NAME(pointer[2])); 108057416Smarkm else 108157416Smarkm output_data(" %d (unknown)", 108257416Smarkm pointer[2]); 108357416Smarkm 108457416Smarkm encrypt_printsub(&pointer[1], length - 1, buf, sizeof(buf)); 108557416Smarkm output_data("%s", 108657416Smarkm buf); 108757416Smarkm break; 108857416Smarkm 108957416Smarkm case ENCRYPT_SUPPORT: 109057416Smarkm i = 2; 109157416Smarkm output_data(" SUPPORT "); 109257416Smarkm while (i < length) { 109357416Smarkm if (ENCTYPE_NAME_OK(pointer[i])) 109457416Smarkm output_data("%s ", 109557416Smarkm ENCTYPE_NAME(pointer[i])); 109657416Smarkm else 109757416Smarkm output_data("%d ", 109857416Smarkm pointer[i]); 109957416Smarkm i++; 110057416Smarkm } 110157416Smarkm break; 110257416Smarkm 110357416Smarkm case ENCRYPT_ENC_KEYID: 110457416Smarkm output_data(" ENC_KEYID %d", pointer[1]); 110557416Smarkm goto encommon; 110657416Smarkm 110757416Smarkm case ENCRYPT_DEC_KEYID: 110857416Smarkm output_data(" DEC_KEYID %d", pointer[1]); 110957416Smarkm goto encommon; 111057416Smarkm 111157416Smarkm default: 111257416Smarkm output_data(" %d (unknown)", pointer[1]); 111357416Smarkm encommon: 111457416Smarkm for (i = 2; i < length; i++) { 111557416Smarkm output_data(" %d", pointer[i]); 111657416Smarkm } 111757416Smarkm break; 111857416Smarkm } 111957416Smarkm break; 112057416Smarkm#endif 112157416Smarkm 112257416Smarkm default: 112357416Smarkm if (TELOPT_OK(pointer[0])) 112457416Smarkm output_data("%s (unknown)", 112557416Smarkm TELOPT(pointer[0])); 112657416Smarkm else 112757416Smarkm output_data("%d (unknown)", 112857416Smarkm pointer[i]); 112957416Smarkm for (i = 1; i < length; i++) { 113057416Smarkm output_data(" %d", pointer[i]); 113157416Smarkm } 113257416Smarkm break; 113357416Smarkm } 113457416Smarkm output_data("\r\n"); 113557416Smarkm} 113657416Smarkm 113757416Smarkm/* 113857416Smarkm * Dump a data buffer in hex and ascii to the output data stream. 113957416Smarkm */ 114057416Smarkmvoid 114157416Smarkmprintdata(char *tag, char *ptr, int cnt) 114257416Smarkm{ 114357416Smarkm int i; 114457416Smarkm char xbuf[30]; 114557416Smarkm 114657416Smarkm while (cnt) { 114757416Smarkm /* flush net output buffer if no room for new data) */ 114857416Smarkm if ((&netobuf[BUFSIZ] - nfrontp) < 80) { 114957416Smarkm netflush(); 115057416Smarkm } 115157416Smarkm 115257416Smarkm /* add a line of output */ 115357416Smarkm output_data("%s: ", tag); 115457416Smarkm for (i = 0; i < 20 && cnt; i++) { 115557416Smarkm output_data("%02x", *ptr); 115657416Smarkm if (isprint(*ptr)) { 115757416Smarkm xbuf[i] = *ptr; 115857416Smarkm } else { 115957416Smarkm xbuf[i] = '.'; 116057416Smarkm } 116157416Smarkm if (i % 2) { 116257416Smarkm output_data(" "); 116357416Smarkm } 116457416Smarkm cnt--; 116557416Smarkm ptr++; 116657416Smarkm } 116757416Smarkm xbuf[i] = '\0'; 116857416Smarkm output_data(" %s\r\n", xbuf); 116957416Smarkm } 117057416Smarkm} 117157416Smarkm#endif /* DIAGNOSTICS */ 1172