openenvutils/telnetserver/src/utility.c
changeset 0 2e3d3ce01487
child 4 0fdb7f6b0309
equal deleted inserted replaced
-1:000000000000 0:2e3d3ce01487
       
     1 // utility.c
       
     2 //
       
     3 // © Portions Copyright (c) Symbian Software Ltd 2007. All rights reserved.
       
     4 //
       
     5 /*
       
     6  * Copyright (c) 1989, 1993
       
     7  *	The Regents of the University of California.  All rights reserved.
       
     8  *
       
     9  * Redistribution and use in source and binary forms, with or without
       
    10  * modification, are permitted provided that the following conditions
       
    11  * are met:
       
    12  * 1. Redistributions of source code must retain the above copyright
       
    13  *    notice, this list of conditions and the following disclaimer.
       
    14  * 2. Redistributions in binary form must reproduce the above copyright
       
    15  *    notice, this list of conditions and the following disclaimer in the
       
    16  *    documentation and/or other materials provided with the distribution.
       
    17  * 3. Neither the name of the University nor the names of its contributors
       
    18  *    may be used to endorse or promote products derived from this software
       
    19  *    without specific prior written permission.
       
    20  *
       
    21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
       
    22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
       
    23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
       
    24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
       
    25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
       
    26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
       
    27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
       
    28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
       
    29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
       
    30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
       
    31  * SUCH DAMAGE.
       
    32  */
       
    33 
       
    34 #define PRINTOPTIONS
       
    35 #include "telnetd.h"
       
    36 
       
    37 #ifndef __SYMBIAN32__
       
    38 __RCSID("$Heimdal: utility.c,v 1.27 2001/09/03 05:54:17 assar Exp $"
       
    39         "$NetBSD: utility.c,v 1.2 2003/08/07 09:15:30 agc Exp $");
       
    40 #endif
       
    41 /*
       
    42  * utility functions performing io related tasks
       
    43  */
       
    44 
       
    45 /*
       
    46  * ttloop
       
    47  *
       
    48  * A small subroutine to flush the network output buffer, get some
       
    49  * data from the network, and pass it through the telnet state
       
    50  * machine.  We also flush the pty input buffer (by dropping its data)
       
    51  * if it becomes too full.
       
    52  *
       
    53  * return 0 if OK or 1 if interrupted by a signal.
       
    54  */
       
    55 
       
    56 int
       
    57 ttloop(void)
       
    58 {
       
    59     DIAG(TD_REPORT, {
       
    60 	output_data("td: ttloop\r\n");
       
    61     });
       
    62     
       
    63     if (nfrontp-nbackp)
       
    64 	netflush();
       
    65 	
       
    66     ncc = read(net, netibuf, sizeof netibuf);
       
    67     if (ncc < 0) {
       
    68 	if (errno == EINTR)
       
    69 	    return 1;
       
    70 #ifndef __SYMBIAN32__	
       
    71 	syslog(LOG_INFO, "ttloop:  read: %m\n");
       
    72 #endif	
       
    73 	exit(1);
       
    74     } else if (ncc == 0) {
       
    75 #ifndef __SYMBIAN32__    
       
    76 	syslog(LOG_INFO, "ttloop:  peer died\n");
       
    77 #endif	
       
    78 	exit(1);
       
    79     }
       
    80     DIAG(TD_REPORT, {
       
    81 	output_data("td: ttloop read %d chars\r\n", ncc);
       
    82     });
       
    83     netip = netibuf;
       
    84     telrcv();			/* state machine */
       
    85     if (ncc > 0) {
       
    86 	pfrontp = pbackp = ptyobuf;
       
    87 	telrcv();
       
    88     }
       
    89     return 0;
       
    90 }  /* end of ttloop */
       
    91 
       
    92 /*
       
    93  * Check a descriptor to see if out of band data exists on it.
       
    94  */
       
    95 int
       
    96 stilloob(int s)
       
    97 {
       
    98     static struct timeval timeout = { 0 };
       
    99     fd_set	excepts;
       
   100     int value;
       
   101 
       
   102     if (s >= FD_SETSIZE)
       
   103 	fatal(ourpty, "fd too large");
       
   104 
       
   105     do {
       
   106 	FD_ZERO(&excepts);
       
   107 	FD_SET(s, &excepts);
       
   108 	value = select(s+1, 0, 0, &excepts, &timeout);
       
   109     } while ((value == -1) && (errno == EINTR));
       
   110 
       
   111     if (value < 0) {
       
   112 	fatalperror(ourpty, "select");
       
   113     }
       
   114     if (FD_ISSET(s, &excepts)) {
       
   115 	return 1;
       
   116     } else {
       
   117 	return 0;
       
   118     }
       
   119 }
       
   120 
       
   121 void
       
   122 ptyflush(void)
       
   123 {
       
   124     int n;
       
   125 
       
   126     if ((n = pfrontp - pbackp) > 0) {
       
   127 	DIAG((TD_REPORT | TD_PTYDATA), { 
       
   128 	    output_data("td: ptyflush %d chars\r\n", n);
       
   129 	});
       
   130 	DIAG(TD_PTYDATA, printdata("pd", pbackp, n));
       
   131 	n = write(ourpty, pbackp, n);
       
   132     }
       
   133     if (n < 0) {
       
   134 	if (errno == EWOULDBLOCK || errno == EINTR)
       
   135 	    return;
       
   136 	cleanup(0);
       
   137     }
       
   138     pbackp += n;
       
   139     if (pbackp == pfrontp)
       
   140 	pbackp = pfrontp = ptyobuf;
       
   141 }
       
   142 
       
   143 /*
       
   144  * nextitem()
       
   145  *
       
   146  *	Return the address of the next "item" in the TELNET data
       
   147  * stream.  This will be the address of the next character if
       
   148  * the current address is a user data character, or it will
       
   149  * be the address of the character following the TELNET command
       
   150  * if the current address is a TELNET IAC ("I Am a Command")
       
   151  * character.
       
   152  */
       
   153 char *
       
   154 nextitem(char *current)
       
   155 {
       
   156     if ((*current&0xff) != IAC) {
       
   157 	return current+1;
       
   158     }
       
   159     switch (*(current+1)&0xff) {
       
   160     case DO:
       
   161     case DONT:
       
   162     case WILL:
       
   163     case WONT:
       
   164 	return current+3;
       
   165     case SB:{
       
   166 	/* loop forever looking for the SE */
       
   167 	char *look = current+2;
       
   168 
       
   169 	for (;;) {
       
   170 	    if ((*look++&0xff) == IAC) {
       
   171 		if ((*look++&0xff) == SE) {
       
   172 		    return look;
       
   173 		}
       
   174 	    }
       
   175 	}
       
   176     }
       
   177     default:
       
   178 	return current+2;
       
   179     }
       
   180 }
       
   181 
       
   182 
       
   183 /*
       
   184  * netclear()
       
   185  *
       
   186  *	We are about to do a TELNET SYNCH operation.  Clear
       
   187  * the path to the network.
       
   188  *
       
   189  *	Things are a bit tricky since we may have sent the first
       
   190  * byte or so of a previous TELNET command into the network.
       
   191  * So, we have to scan the network buffer from the beginning
       
   192  * until we are up to where we want to be.
       
   193  *
       
   194  *	A side effect of what we do, just to keep things
       
   195  * simple, is to clear the urgent data pointer.  The principal
       
   196  * caller should be setting the urgent data pointer AFTER calling
       
   197  * us in any case.
       
   198  */
       
   199 void
       
   200 netclear(void)
       
   201 {
       
   202     char *thisitem, *next;
       
   203     char *good;
       
   204 #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
       
   205 			 ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
       
   206 
       
   207 #ifdef ENCRYPTION
       
   208 	thisitem = nclearto > netobuf ? nclearto : netobuf;
       
   209 #else
       
   210 	thisitem = netobuf;
       
   211 #endif
       
   212 
       
   213 	while ((next = nextitem(thisitem)) <= nbackp) {
       
   214 	    thisitem = next;
       
   215 	}
       
   216 
       
   217 	/* Now, thisitem is first before/at boundary. */
       
   218 
       
   219 #ifdef ENCRYPTION
       
   220 	good = nclearto > netobuf ? nclearto : netobuf;
       
   221 #else
       
   222 	good = netobuf;	/* where the good bytes go */
       
   223 #endif
       
   224 
       
   225 	while (nfrontp > thisitem) {
       
   226 	    if (wewant(thisitem)) {
       
   227 		int length;
       
   228 
       
   229 		next = thisitem;
       
   230 		do {
       
   231 		    next = nextitem(next);
       
   232 		} while (wewant(next) && (nfrontp > next));
       
   233 		length = next-thisitem;
       
   234 		memmove(good, thisitem, length);
       
   235 		good += length;
       
   236 		thisitem = next;
       
   237 	    } else {
       
   238 		thisitem = nextitem(thisitem);
       
   239 	    }
       
   240 	}
       
   241 
       
   242 	nbackp = netobuf;
       
   243 	nfrontp = good;		/* next byte to be sent */
       
   244 	neturg = 0;
       
   245 }  /* end of netclear */
       
   246 
       
   247 extern int not42;
       
   248 
       
   249 /*
       
   250  *  netflush
       
   251  *		Send as much data as possible to the network,
       
   252  *	handling requests for urgent data.
       
   253  */
       
   254 void
       
   255 netflush(void)
       
   256 {
       
   257     int n;
       
   258 
       
   259     if ((n = nfrontp - nbackp) > 0) {
       
   260 	DIAG(TD_REPORT,
       
   261 	     { n += output_data("td: netflush %d chars\r\n", n);
       
   262 	     });
       
   263 #ifdef ENCRYPTION
       
   264 	if (encrypt_output) {
       
   265 	    char *s = nclearto ? nclearto : nbackp;
       
   266 	    if (nfrontp - s > 0) {
       
   267 		(*encrypt_output)((unsigned char *)s, nfrontp-s);
       
   268 		nclearto = nfrontp;
       
   269 	    }
       
   270 	}
       
   271 #endif
       
   272 	/*
       
   273 	 * if no urgent data, or if the other side appears to be an
       
   274 	 * old 4.2 client (and thus unable to survive TCP urgent data),
       
   275 	 * write the entire buffer in non-OOB mode.
       
   276 	 */
       
   277 #if 1 /* remove this to make it work between solaris 2.6 and linux */
       
   278 	if ((neturg == 0) || (not42 == 0)) {
       
   279 #endif
       
   280 	    n = write(net, nbackp, n);	/* normal write */	    
       
   281 #if 1 /* remove this to make it work between solaris 2.6 and linux */
       
   282 	} else {
       
   283 	    n = neturg - nbackp;
       
   284 	    /*
       
   285 	     * In 4.2 (and 4.3) systems, there is some question about
       
   286 	     * what byte in a sendOOB operation is the "OOB" data.
       
   287 	     * To make ourselves compatible, we only send ONE byte
       
   288 	     * out of band, the one WE THINK should be OOB (though
       
   289 	     * we really have more the TCP philosophy of urgent data
       
   290 	     * rather than the Unix philosophy of OOB data).
       
   291 	     */
       
   292 	    if (n > 1) {
       
   293 		n = send(net, nbackp, n-1, 0);	/* send URGENT all by itself */
       
   294 	    } else {
       
   295 		n = send(net, nbackp, n, MSG_OOB);	/* URGENT data */
       
   296 	    }
       
   297 	}
       
   298 #endif
       
   299     }
       
   300     if (n < 0) {
       
   301 	if (errno == EWOULDBLOCK || errno == EINTR)
       
   302 	    return;
       
   303 	cleanup(0);
       
   304     }
       
   305     nbackp += n;
       
   306 #ifdef ENCRYPTION
       
   307     if (nbackp > nclearto)
       
   308 	nclearto = 0;
       
   309 #endif
       
   310     if (nbackp >= neturg) {
       
   311 	neturg = 0;
       
   312     }
       
   313     if (nbackp == nfrontp) {
       
   314 	nbackp = nfrontp = netobuf;
       
   315 #ifdef ENCRYPTION
       
   316 	nclearto = 0;
       
   317 #endif
       
   318     }
       
   319     return;
       
   320 }
       
   321 
       
   322 
       
   323 /*
       
   324  * writenet
       
   325  *
       
   326  * Just a handy little function to write a bit of raw data to the net.
       
   327  * It will force a transmit of the buffer if necessary
       
   328  *
       
   329  * arguments
       
   330  *    ptr - A pointer to a character string to write
       
   331  *    len - How many bytes to write
       
   332  */
       
   333 void
       
   334 writenet(unsigned char *ptr, int len)
       
   335 {
       
   336     /* flush buffer if no room for new data) */
       
   337     while ((&netobuf[BUFSIZ] - nfrontp) < len) {
       
   338 	/* if this fails, don't worry, buffer is a little big */
       
   339 	netflush();
       
   340     }
       
   341 
       
   342     memmove(nfrontp, ptr, len);
       
   343     nfrontp += len;
       
   344 }
       
   345 
       
   346 
       
   347 /*
       
   348  * miscellaneous functions doing a variety of little jobs follow ...
       
   349  */
       
   350 
       
   351 
       
   352 void fatal(int f, char *msg)
       
   353 {
       
   354     char buf[BUFSIZ];
       
   355 
       
   356     snprintf(buf, sizeof(buf), "telnetd: %s.\r\n", msg);
       
   357 #ifdef ENCRYPTION
       
   358     if (encrypt_output) {
       
   359 	/*
       
   360 	 * Better turn off encryption first....
       
   361 	 * Hope it flushes...
       
   362 	 */
       
   363 	encrypt_send_end();
       
   364 	netflush();
       
   365     }
       
   366 #endif
       
   367     write(f, buf, (int)strlen(buf));
       
   368     sleep(1);	/*XXX*/
       
   369     exit(1);
       
   370 }
       
   371 
       
   372 void
       
   373 fatalperror_errno(int f, const char *msg, int error)
       
   374 {
       
   375     char buf[BUFSIZ];
       
   376     
       
   377     snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(error));
       
   378     fatal(f, buf);
       
   379 }
       
   380 
       
   381 void
       
   382 fatalperror(int f, const char *msg)
       
   383 {
       
   384     fatalperror_errno(f, msg, errno);
       
   385 }
       
   386 
       
   387 char editedhost[32];
       
   388 
       
   389 void edithost(char *pat, char *host)
       
   390 {
       
   391     char *res = editedhost;
       
   392 
       
   393     if (!pat)
       
   394 	pat = "";
       
   395     while (*pat) {
       
   396 	switch (*pat) {
       
   397 
       
   398 	case '#':
       
   399 	    if (*host)
       
   400 		host++;
       
   401 	    break;
       
   402 
       
   403 	case '@':
       
   404 	    if (*host)
       
   405 		*res++ = *host++;
       
   406 	    break;
       
   407 
       
   408 	default:
       
   409 	    *res++ = *pat;
       
   410 	    break;
       
   411 	}
       
   412 	if (res == &editedhost[sizeof editedhost - 1]) {
       
   413 	    *res = '\0';
       
   414 	    return;
       
   415 	}
       
   416 	pat++;
       
   417     }
       
   418     if (*host)
       
   419 	strlcpy (res, host,
       
   420 			 sizeof editedhost - (res - editedhost));
       
   421     else
       
   422 	*res = '\0';
       
   423     editedhost[sizeof editedhost - 1] = '\0';
       
   424 }
       
   425 
       
   426 static char *putlocation;
       
   427 
       
   428 void
       
   429 putstr(char *s)
       
   430 {
       
   431 
       
   432     while (*s)
       
   433 	putchr(*s++);
       
   434 }
       
   435 
       
   436 void
       
   437 putchr(int cc)
       
   438 {
       
   439     *putlocation++ = cc;
       
   440 }
       
   441 
       
   442 /*
       
   443  * This is split on two lines so that SCCS will not see the M
       
   444  * between two % signs and expand it...
       
   445  */
       
   446 static char fmtstr[] = { "%l:%M" "%P on %A, %d %B %Y" };
       
   447 
       
   448 void putf(char *cp, char *where)
       
   449 {
       
   450 #ifdef HAVE_UNAME
       
   451     struct utsname name;
       
   452 #endif
       
   453     char *slash;
       
   454     time_t t;
       
   455     char db[100];
       
   456 
       
   457     /* if we don't have uname, set these to sensible values */
       
   458     char *sysname = "Unix", 
       
   459 	*machine = "", 
       
   460 	*release = "",
       
   461 	*version = ""; 
       
   462 
       
   463 #ifdef HAVE_UNAME
       
   464     uname(&name);
       
   465     sysname=name.sysname;
       
   466     machine=name.machine;
       
   467     release=name.release;
       
   468     version=name.version;
       
   469 #endif
       
   470 
       
   471     putlocation = where;
       
   472 
       
   473     while (*cp) {
       
   474 	if (*cp != '%') {
       
   475 	    putchr(*cp++);
       
   476 	    continue;
       
   477 	}
       
   478 	switch (*++cp) {
       
   479 
       
   480 	case 't':
       
   481 #ifdef	STREAMSPTY
       
   482 	    /* names are like /dev/pts/2 -- we want pts/2 */
       
   483 	    slash = strchr(line+1, '/');
       
   484 #else
       
   485 	    slash = strrchr(line, '/');
       
   486 #endif
       
   487 	    if (slash == (char *) 0)
       
   488 		putstr(line);
       
   489 	    else
       
   490 		putstr(&slash[1]);
       
   491 	    break;
       
   492 
       
   493 	case 'h':
       
   494 	    putstr(editedhost);
       
   495 	    break;
       
   496 
       
   497 	case 's':
       
   498 	    putstr(sysname);
       
   499 	    break;
       
   500 
       
   501 	case 'm':
       
   502 	    putstr(machine);
       
   503 	    break;
       
   504 
       
   505 	case 'r':
       
   506 	    putstr(release);
       
   507 	    break;
       
   508 
       
   509 	case 'v':
       
   510 	    putstr(version);
       
   511 	    break;
       
   512 
       
   513 	case 'd':
       
   514 	    time(&t);
       
   515 	    strftime(db, sizeof(db), fmtstr, localtime(&t));
       
   516 	    putstr(db);
       
   517 	    break;
       
   518 
       
   519 	case '%':
       
   520 	    putchr('%');
       
   521 	    break;
       
   522 	}
       
   523 	cp++;
       
   524     }
       
   525 }
       
   526 
       
   527 #ifdef DIAGNOSTICS
       
   528 /*
       
   529  * Print telnet options and commands in plain text, if possible.
       
   530  */
       
   531 void
       
   532 printoption(char *fmt, int option)
       
   533 {
       
   534     if (TELOPT_OK(option))
       
   535 	output_data("%s %s\r\n",
       
   536 		    fmt,
       
   537 		    TELOPT(option));
       
   538     else if (TELCMD_OK(option))
       
   539 	output_data("%s %s\r\n",
       
   540 		    fmt,
       
   541 		    TELCMD(option));
       
   542     else
       
   543 	output_data("%s %d\r\n",
       
   544 		    fmt,
       
   545 		    option);
       
   546     return;
       
   547 }
       
   548 
       
   549 void
       
   550 printsub(int direction, unsigned char *pointer, int length)
       
   551         		          	/* '<' or '>' */
       
   552                  	         	/* where suboption data sits */
       
   553        			       		/* length of suboption data */
       
   554 {
       
   555     int i = 0;
       
   556     unsigned char buf[512];
       
   557 
       
   558     if (!(diagnostic & TD_OPTIONS))
       
   559 	return;
       
   560 
       
   561     if (direction) {
       
   562 	output_data("td: %s suboption ",
       
   563 		    direction == '<' ? "recv" : "send");
       
   564 	if (length >= 3) {
       
   565 	    int j;
       
   566 
       
   567 	    i = pointer[length-2];
       
   568 	    j = pointer[length-1];
       
   569 
       
   570 	    if (i != IAC || j != SE) {
       
   571 		output_data("(terminated by ");
       
   572 		if (TELOPT_OK(i))
       
   573 		    output_data("%s ",
       
   574 				TELOPT(i));
       
   575 		else if (TELCMD_OK(i))
       
   576 		    output_data("%s ",
       
   577 				TELCMD(i));
       
   578 		else
       
   579 		    output_data("%d ",
       
   580 				i);
       
   581 		if (TELOPT_OK(j))
       
   582 		    output_data("%s",
       
   583 				TELOPT(j));
       
   584 		else if (TELCMD_OK(j))
       
   585 		    output_data("%s",
       
   586 				TELCMD(j));
       
   587 		else
       
   588 		    output_data("%d",
       
   589 				j);
       
   590 		output_data(", not IAC SE!) ");
       
   591 	    }
       
   592 	}
       
   593 	length -= 2;
       
   594     }
       
   595     if (length < 1) {
       
   596 	output_data("(Empty suboption??\?)");
       
   597 	return;
       
   598     }
       
   599     switch (pointer[0]) {
       
   600     case TELOPT_TTYPE:
       
   601 	output_data("TERMINAL-TYPE ");
       
   602 	switch (pointer[1]) {
       
   603 	case TELQUAL_IS:
       
   604 	    output_data("IS \"%.*s\"",
       
   605 			length-2,
       
   606 			(char *)pointer+2);
       
   607 	    break;
       
   608 	case TELQUAL_SEND:
       
   609 	    output_data("SEND");
       
   610 	    break;
       
   611 	default:
       
   612 	    output_data("- unknown qualifier %d (0x%x).",
       
   613 			pointer[1], pointer[1]);
       
   614 	}
       
   615 	break;
       
   616     case TELOPT_TSPEED:
       
   617 	output_data("TERMINAL-SPEED");
       
   618 	if (length < 2) {
       
   619 	    output_data(" (empty suboption??\?)");
       
   620 	    break;
       
   621 	}
       
   622 	switch (pointer[1]) {
       
   623 	case TELQUAL_IS:
       
   624 	    output_data(" IS %.*s", length-2, (char *)pointer+2);
       
   625 	    break;
       
   626 	default:
       
   627 	    if (pointer[1] == 1)
       
   628 		output_data(" SEND");
       
   629 	    else
       
   630 		output_data(" %d (unknown)", pointer[1]);
       
   631 	    for (i = 2; i < length; i++) {
       
   632 		output_data(" ?%d?", pointer[i]);
       
   633 	    }
       
   634 	    break;
       
   635 	}
       
   636 	break;
       
   637 
       
   638     case TELOPT_LFLOW:
       
   639 	output_data("TOGGLE-FLOW-CONTROL");
       
   640 	if (length < 2) {
       
   641 	    output_data(" (empty suboption??\?)");
       
   642 	    break;
       
   643 	}
       
   644 	switch (pointer[1]) {
       
   645 	case LFLOW_OFF:
       
   646 	    output_data(" OFF");
       
   647 	    break;
       
   648 	case LFLOW_ON:
       
   649 	    output_data(" ON");
       
   650 	    break;
       
   651 	case LFLOW_RESTART_ANY:
       
   652 	    output_data(" RESTART-ANY");
       
   653 	    break;
       
   654 	case LFLOW_RESTART_XON:
       
   655 	    output_data(" RESTART-XON");
       
   656 	    break;
       
   657 	default:
       
   658 	    output_data(" %d (unknown)",
       
   659 			pointer[1]);
       
   660 	}
       
   661 	for (i = 2; i < length; i++) {
       
   662 	    output_data(" ?%d?",
       
   663 			pointer[i]);
       
   664 	}
       
   665 	break;
       
   666 
       
   667     case TELOPT_NAWS:
       
   668 	output_data("NAWS");
       
   669 	if (length < 2) {
       
   670 	    output_data(" (empty suboption??\?)");
       
   671 	    break;
       
   672 	}
       
   673 	if (length == 2) {
       
   674 	    output_data(" ?%d?",
       
   675 			pointer[1]);
       
   676 	    break;
       
   677 	}
       
   678 	output_data(" %u %u(%u)",
       
   679 		    pointer[1],
       
   680 		    pointer[2],
       
   681 		    (((unsigned int)pointer[1])<<8) + pointer[2]);
       
   682 	if (length == 4) {
       
   683 	    output_data(" ?%d?",
       
   684 			pointer[3]);
       
   685 	    break;
       
   686 	}
       
   687 	output_data(" %u %u(%u)",
       
   688 		    pointer[3],
       
   689 		    pointer[4],
       
   690 		    (((unsigned int)pointer[3])<<8) + pointer[4]);
       
   691 	for (i = 5; i < length; i++) {
       
   692 	    output_data(" ?%d?",
       
   693 			pointer[i]);
       
   694 	}
       
   695 	break;
       
   696 
       
   697     case TELOPT_LINEMODE:
       
   698 	output_data("LINEMODE ");
       
   699 	if (length < 2) {
       
   700 	    output_data(" (empty suboption??\?)");
       
   701 	    break;
       
   702 	}
       
   703 	switch (pointer[1]) {
       
   704 	case WILL:
       
   705 	    output_data("WILL ");
       
   706 	    goto common;
       
   707 	case WONT:
       
   708 	    output_data("WONT ");
       
   709 	    goto common;
       
   710 	case DO:
       
   711 	    output_data("DO ");
       
   712 	    goto common;
       
   713 	case DONT:
       
   714 	    output_data("DONT ");
       
   715 	common:
       
   716 	    if (length < 3) {
       
   717 		output_data("(no option??\?)");
       
   718 		break;
       
   719 	    }
       
   720 	    switch (pointer[2]) {
       
   721 	    case LM_FORWARDMASK:
       
   722 		output_data("Forward Mask");
       
   723 		for (i = 3; i < length; i++) {
       
   724 		    output_data(" %x", pointer[i]);
       
   725 		}
       
   726 		break;
       
   727 	    default:
       
   728 		output_data("%d (unknown)",
       
   729 			    pointer[2]);
       
   730 		for (i = 3; i < length; i++) {
       
   731 		    output_data(" %d",
       
   732 				pointer[i]);
       
   733 		}
       
   734 		break;
       
   735 	    }
       
   736 	    break;
       
   737 
       
   738 	case LM_SLC:
       
   739 	    output_data("SLC");
       
   740 	    for (i = 2; i < length - 2; i += 3) {
       
   741 		if (SLC_NAME_OK(pointer[i+SLC_FUNC]))
       
   742 		    output_data(" %s",
       
   743 				SLC_NAME(pointer[i+SLC_FUNC]));
       
   744 		else
       
   745 		    output_data(" %d",
       
   746 				pointer[i+SLC_FUNC]);
       
   747 		switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) {
       
   748 		case SLC_NOSUPPORT:
       
   749 		    output_data(" NOSUPPORT");
       
   750 		    break;
       
   751 		case SLC_CANTCHANGE:
       
   752 		    output_data(" CANTCHANGE");
       
   753 		    break;
       
   754 		case SLC_VARIABLE:
       
   755 		    output_data(" VARIABLE");
       
   756 		    break;
       
   757 		case SLC_DEFAULT:
       
   758 		    output_data(" DEFAULT");
       
   759 		    break;
       
   760 		}
       
   761 		output_data("%s%s%s",
       
   762 			    pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "",
       
   763 			    pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "",
       
   764 			    pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : "");
       
   765 		if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN|
       
   766 					    SLC_FLUSHOUT| SLC_LEVELBITS)) {
       
   767 		    output_data("(0x%x)",
       
   768 				pointer[i+SLC_FLAGS]);
       
   769 		}
       
   770 		output_data(" %d;",
       
   771 			    pointer[i+SLC_VALUE]);
       
   772 		if ((pointer[i+SLC_VALUE] == IAC) &&
       
   773 		    (pointer[i+SLC_VALUE+1] == IAC))
       
   774 		    i++;
       
   775 	    }
       
   776 	    for (; i < length; i++) {
       
   777 		output_data(" ?%d?",
       
   778 			    pointer[i]);
       
   779 	    }
       
   780 	    break;
       
   781 
       
   782 	case LM_MODE:
       
   783 	    output_data("MODE ");
       
   784 	    if (length < 3) {
       
   785 		output_data("(no mode??\?)");
       
   786 		break;
       
   787 	    }
       
   788 	    {
       
   789 		char tbuf[32];
       
   790 		snprintf(tbuf,
       
   791 			 sizeof(tbuf),
       
   792 			 "%s%s%s%s%s",
       
   793 			 pointer[2]&MODE_EDIT ? "|EDIT" : "",
       
   794 			 pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "",
       
   795 			 pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "",
       
   796 			 pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "",
       
   797 			 pointer[2]&MODE_ACK ? "|ACK" : "");
       
   798 		output_data("%s",
       
   799 			    tbuf[1] ? &tbuf[1] : "0");
       
   800 	    }
       
   801 	    if (pointer[2]&~(MODE_EDIT|MODE_TRAPSIG|MODE_ACK)) {
       
   802 		output_data(" (0x%x)",
       
   803 			    pointer[2]);
       
   804 	    }
       
   805 	    for (i = 3; i < length; i++) {
       
   806 		output_data(" ?0x%x?",
       
   807 			 pointer[i]);
       
   808 	    }
       
   809 	    break;
       
   810 	default:
       
   811 	    output_data("%d (unknown)",
       
   812 			pointer[1]);
       
   813 	    for (i = 2; i < length; i++) {
       
   814 		output_data(" %d", pointer[i]);
       
   815 	    }
       
   816 	}
       
   817 	break;
       
   818 
       
   819     case TELOPT_STATUS: {
       
   820 	char *cp;
       
   821 	int j, k;
       
   822 
       
   823 	output_data("STATUS");
       
   824 
       
   825 	switch (pointer[1]) {
       
   826 	default:
       
   827 	    if (pointer[1] == TELQUAL_SEND)
       
   828 		output_data(" SEND");
       
   829 	    else
       
   830 		output_data(" %d (unknown)",
       
   831 			    pointer[1]);
       
   832 	    for (i = 2; i < length; i++) {
       
   833 		output_data(" ?%d?",
       
   834 			    pointer[i]);
       
   835 	    }
       
   836 	    break;
       
   837 	case TELQUAL_IS:
       
   838 	    output_data(" IS\r\n");
       
   839 
       
   840 	    for (i = 2; i < length; i++) {
       
   841 		switch(pointer[i]) {
       
   842 		case DO:	cp = "DO"; goto common2;
       
   843 		case DONT:	cp = "DONT"; goto common2;
       
   844 		case WILL:	cp = "WILL"; goto common2;
       
   845 		case WONT:	cp = "WONT"; goto common2;
       
   846 		common2:
       
   847 		i++;
       
   848 		if (TELOPT_OK(pointer[i]))
       
   849 		    output_data(" %s %s",
       
   850 				cp,
       
   851 				TELOPT(pointer[i]));
       
   852 		else
       
   853 		    output_data(" %s %d",
       
   854 				cp,
       
   855 				pointer[i]);
       
   856 
       
   857 		output_data("\r\n");
       
   858 		break;
       
   859 
       
   860 		case SB:
       
   861 		    output_data(" SB ");
       
   862 		    i++;
       
   863 		    j = k = i;
       
   864 		    while (j < length) {
       
   865 			if (pointer[j] == SE) {
       
   866 			    if (j+1 == length)
       
   867 				break;
       
   868 			    if (pointer[j+1] == SE)
       
   869 				j++;
       
   870 			    else
       
   871 				break;
       
   872 			}
       
   873 			pointer[k++] = pointer[j++];
       
   874 		    }
       
   875 		    printsub(0, &pointer[i], k - i);
       
   876 		    if (i < length) {
       
   877 			output_data(" SE");
       
   878 			i = j;
       
   879 		    } else
       
   880 			i = j - 1;
       
   881 
       
   882 		    output_data("\r\n");
       
   883 
       
   884 		    break;
       
   885 
       
   886 		default:
       
   887 		    output_data(" %d",
       
   888 				pointer[i]);
       
   889 		    break;
       
   890 		}
       
   891 	    }
       
   892 	    break;
       
   893 	}
       
   894 	break;
       
   895     }
       
   896 
       
   897     case TELOPT_XDISPLOC:
       
   898 	output_data("X-DISPLAY-LOCATION ");
       
   899 	switch (pointer[1]) {
       
   900 	case TELQUAL_IS:
       
   901 	    output_data("IS \"%.*s\"",
       
   902 			length-2,
       
   903 			(char *)pointer+2);
       
   904 	    break;
       
   905 	case TELQUAL_SEND:
       
   906 	    output_data("SEND");
       
   907 	    break;
       
   908 	default:
       
   909 	    output_data("- unknown qualifier %d (0x%x).",
       
   910 			pointer[1], pointer[1]);
       
   911 	}
       
   912 	break;
       
   913 
       
   914     case TELOPT_NEW_ENVIRON:
       
   915 	output_data("NEW-ENVIRON ");
       
   916 	goto env_common1;
       
   917     case TELOPT_OLD_ENVIRON:
       
   918 	output_data("OLD-ENVIRON");
       
   919     env_common1:
       
   920 	switch (pointer[1]) {
       
   921 	case TELQUAL_IS:
       
   922 	    output_data("IS ");
       
   923 	    goto env_common;
       
   924 	case TELQUAL_SEND:
       
   925 	    output_data("SEND ");
       
   926 	    goto env_common;
       
   927 	case TELQUAL_INFO:
       
   928 	    output_data("INFO ");
       
   929 	env_common:
       
   930 	    {
       
   931 		int noquote = 2;
       
   932 		for (i = 2; i < length; i++ ) {
       
   933 		    switch (pointer[i]) {
       
   934 		    case NEW_ENV_VAR:
       
   935 			output_data("\" VAR " + noquote);
       
   936 			noquote = 2;
       
   937 			break;
       
   938 
       
   939 		    case NEW_ENV_VALUE:
       
   940 			output_data("\" VALUE " + noquote);
       
   941 			noquote = 2;
       
   942 			break;
       
   943 
       
   944 		    case ENV_ESC:
       
   945 			output_data("\" ESC " + noquote);
       
   946 			noquote = 2;
       
   947 			break;
       
   948 
       
   949 		    case ENV_USERVAR:
       
   950 			output_data("\" USERVAR " + noquote);
       
   951 			noquote = 2;
       
   952 			break;
       
   953 
       
   954 		    default:
       
   955 			if (isprint(pointer[i]) && pointer[i] != '"') {
       
   956 			    if (noquote) {
       
   957 				output_data ("\"");
       
   958 				noquote = 0;
       
   959 			    }
       
   960 			    output_data ("%c", pointer[i]);
       
   961 			} else {
       
   962 			    output_data("\" %03o " + noquote,
       
   963 					pointer[i]);
       
   964 			    noquote = 2;
       
   965 			}
       
   966 			break;
       
   967 		    }
       
   968 		}
       
   969 		if (!noquote)
       
   970 		    output_data ("\"");
       
   971 		break;
       
   972 	    }
       
   973 	}
       
   974 	break;
       
   975 
       
   976 #ifdef AUTHENTICATION
       
   977     case TELOPT_AUTHENTICATION:
       
   978 	output_data("AUTHENTICATION");
       
   979 
       
   980 	if (length < 2) {
       
   981 	    output_data(" (empty suboption??\?)");
       
   982 	    break;
       
   983 	}
       
   984 	switch (pointer[1]) {
       
   985 	case TELQUAL_REPLY:
       
   986 	case TELQUAL_IS:
       
   987 	    output_data(" %s ",
       
   988 			(pointer[1] == TELQUAL_IS) ?
       
   989 			"IS" : "REPLY");
       
   990 	    if (AUTHTYPE_NAME_OK(pointer[2]))
       
   991 		output_data("%s ",
       
   992 			    AUTHTYPE_NAME(pointer[2]));
       
   993 	    else
       
   994 		output_data("%d ",
       
   995 			    pointer[2]);
       
   996 	    if (length < 3) {
       
   997 		output_data("(partial suboption??\?)");
       
   998 		break;
       
   999 	    }
       
  1000 	    output_data("%s|%s",
       
  1001 			((pointer[3] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
       
  1002 			"CLIENT" : "SERVER",
       
  1003 			((pointer[3] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
       
  1004 			"MUTUAL" : "ONE-WAY");
       
  1005 
       
  1006 	    auth_printsub(&pointer[1], length - 1, buf, sizeof(buf));
       
  1007 	    output_data("%s",
       
  1008 			buf);
       
  1009 	    break;
       
  1010 
       
  1011 	case TELQUAL_SEND:
       
  1012 	    i = 2;
       
  1013 	    output_data(" SEND ");
       
  1014 	    while (i < length) {
       
  1015 		if (AUTHTYPE_NAME_OK(pointer[i]))
       
  1016 		    output_data("%s ",
       
  1017 				AUTHTYPE_NAME(pointer[i]));
       
  1018 		else
       
  1019 		    output_data("%d ",
       
  1020 				pointer[i]);
       
  1021 		if (++i >= length) {
       
  1022 		    output_data("(partial suboption??\?)");
       
  1023 		    break;
       
  1024 		}
       
  1025 		output_data("%s|%s ",
       
  1026 			    ((pointer[i] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
       
  1027 			    "CLIENT" : "SERVER",
       
  1028 			    ((pointer[i] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
       
  1029 			    "MUTUAL" : "ONE-WAY");
       
  1030 		++i;
       
  1031 	    }
       
  1032 	    break;
       
  1033 
       
  1034 	case TELQUAL_NAME:
       
  1035 	    i = 2;
       
  1036 	    output_data(" NAME \"%.*s\"",
       
  1037 			length - 2,
       
  1038 			pointer);
       
  1039 	    break;
       
  1040 
       
  1041 	default:
       
  1042 	    for (i = 2; i < length; i++) {
       
  1043 		output_data(" ?%d?",
       
  1044 			    pointer[i]);
       
  1045 	    }
       
  1046 	    break;
       
  1047 	}
       
  1048 	break;
       
  1049 #endif
       
  1050 
       
  1051 #ifdef ENCRYPTION
       
  1052     case TELOPT_ENCRYPT:
       
  1053 	output_data("ENCRYPT");
       
  1054 	if (length < 2) {
       
  1055 	    output_data(" (empty suboption?)");
       
  1056 	    break;
       
  1057 	}
       
  1058 	switch (pointer[1]) {
       
  1059 	case ENCRYPT_START:
       
  1060 	    output_data(" START");
       
  1061 	    break;
       
  1062 
       
  1063 	case ENCRYPT_END:
       
  1064 	    output_data(" END");
       
  1065 	    break;
       
  1066 
       
  1067 	case ENCRYPT_REQSTART:
       
  1068 	    output_data(" REQUEST-START");
       
  1069 	    break;
       
  1070 
       
  1071 	case ENCRYPT_REQEND:
       
  1072 	    output_data(" REQUEST-END");
       
  1073 	    break;
       
  1074 
       
  1075 	case ENCRYPT_IS:
       
  1076 	case ENCRYPT_REPLY:
       
  1077 	    output_data(" %s ",
       
  1078 			(pointer[1] == ENCRYPT_IS) ?
       
  1079 			"IS" : "REPLY");
       
  1080 	    if (length < 3) {
       
  1081 		output_data(" (partial suboption?)");
       
  1082 		break;
       
  1083 	    }
       
  1084 	    if (ENCTYPE_NAME_OK(pointer[2]))
       
  1085 		output_data("%s ",
       
  1086 			    ENCTYPE_NAME(pointer[2]));
       
  1087 	    else
       
  1088 		output_data(" %d (unknown)",
       
  1089 			    pointer[2]);
       
  1090 
       
  1091 	    encrypt_printsub(&pointer[1], length - 1, buf, sizeof(buf));
       
  1092 	    output_data("%s",
       
  1093 			buf);
       
  1094 	    break;
       
  1095 
       
  1096 	case ENCRYPT_SUPPORT:
       
  1097 	    i = 2;
       
  1098 	    output_data(" SUPPORT ");
       
  1099 	    while (i < length) {
       
  1100 		if (ENCTYPE_NAME_OK(pointer[i]))
       
  1101 		    output_data("%s ",
       
  1102 				ENCTYPE_NAME(pointer[i]));
       
  1103 		else
       
  1104 		    output_data("%d ",
       
  1105 				pointer[i]);
       
  1106 		i++;
       
  1107 	    }
       
  1108 	    break;
       
  1109 
       
  1110 	case ENCRYPT_ENC_KEYID:
       
  1111 	    output_data(" ENC_KEYID %d", pointer[1]);
       
  1112 	    goto encommon;
       
  1113 
       
  1114 	case ENCRYPT_DEC_KEYID:
       
  1115 	    output_data(" DEC_KEYID %d", pointer[1]);
       
  1116 	    goto encommon;
       
  1117 
       
  1118 	default:
       
  1119 	    output_data(" %d (unknown)", pointer[1]);
       
  1120 	encommon:
       
  1121 	    for (i = 2; i < length; i++) {
       
  1122 		output_data(" %d", pointer[i]);
       
  1123 	    }
       
  1124 	    break;
       
  1125 	}
       
  1126 	break;
       
  1127 #endif
       
  1128 
       
  1129     default:
       
  1130 	if (TELOPT_OK(pointer[0]))
       
  1131 	    output_data("%s (unknown)",
       
  1132 			TELOPT(pointer[0]));
       
  1133 	else
       
  1134 	    output_data("%d (unknown)",
       
  1135 			pointer[i]);
       
  1136 	for (i = 1; i < length; i++) {
       
  1137 	    output_data(" %d", pointer[i]);
       
  1138 	}
       
  1139 	break;
       
  1140     }
       
  1141     output_data("\r\n");
       
  1142 }
       
  1143 
       
  1144 /*
       
  1145  * Dump a data buffer in hex and ascii to the output data stream.
       
  1146  */
       
  1147 void
       
  1148 printdata(char *tag, char *ptr, int cnt)
       
  1149 {
       
  1150     int i;
       
  1151     char xbuf[30];
       
  1152 
       
  1153     while (cnt) {
       
  1154 	/* flush net output buffer if no room for new data) */
       
  1155 	if ((&netobuf[BUFSIZ] - nfrontp) < 80) {
       
  1156 	    netflush();
       
  1157 	}
       
  1158 
       
  1159 	/* add a line of output */
       
  1160 	output_data("%s: ", tag);
       
  1161 	for (i = 0; i < 20 && cnt; i++) {
       
  1162 	    output_data("%02x", *ptr);
       
  1163 	    if (isprint((unsigned char)*ptr)) {
       
  1164 		xbuf[i] = *ptr;
       
  1165 	    } else {
       
  1166 		xbuf[i] = '.';
       
  1167 	    }
       
  1168 	    if (i % 2) {
       
  1169 		output_data(" ");
       
  1170 	    }
       
  1171 	    cnt--;
       
  1172 	    ptr++;
       
  1173 	}
       
  1174 	xbuf[i] = '\0';
       
  1175 	output_data(" %s\r\n", xbuf);
       
  1176     }
       
  1177 }
       
  1178 #endif /* DIAGNOSTICS */