Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.

Due to spam on this forum, all posts now need moderator approval.

 Entire forum ➜ SMAUG ➜ SMAUG coding ➜ Unexplained telnet characters.

Unexplained telnet characters.

It is now over 60 days since the last post. This thread is closed.     Refresh page


Posted by ThomasWatts   USA  (66 posts)  Bio
Date Fri 09 Nov 2007 06:17 AM (UTC)
Message
Hello all those that read this, Welcome :)
I decided to place this message in this forum because of the single reason is that I started out with SMAUG code. Minus the comments and basic structure almost nothing has been left untouched. The story started out well, with adding lua and mccp as well as dlsym. Then I ripped all of that out and start almost from scratch using lua. The C code just handles the IO to/from sockets. It's rather interesting.
So interesting that I decided to add in more telnet negotiation, I figured that terminal types and window size negotiation should be simple!
That's where the rub is see. I had to pull out the newline checks and listen for EWOULDBLOCK and null termination. Not so bad in and of itself, but what I'm left with are phantom input I'm not sure where it came from.
Example:
V does not exist
Please enter your name, or 'new': ANSI supported.


does not exist
Please enter your name, or 'new':

does not exist
Please enter your name, or 'new':

The ' does not exist' I'm not worried about, that's just unfinished Lua code. It's the 'V' at the top. Code sample in next post.
Top

Posted by ThomasWatts   USA  (66 posts)  Bio
Date Reply #1 on Fri 09 Nov 2007 06:18 AM (UTC)

Amended on Fri 09 Nov 2007 08:56 AM (UTC) by Nick Gammon

Message

// overhaul to support terminal type negotiation correctly 11-08-2007
static bool read_from_descriptor( DESCRIPTOR_DATA *d ) {
	int iStart = 0, iErr = 0;
	// Check for overflow.
	iStart = strlen(d->inbuf);
	if( iStart >= MAX_INBUF_SIZE - 10 ) {
		char log_buf[MAX_STRING_LENGTH];
		sprintf( log_buf, "%s(%lu) input overflow!", d->host, d->uniqueNumber );
		log_string( log_buf );
		write_to_descriptor( d, "\n\r*** OMG SNAPS!!! ***\n\r", 0);
		return FALSE;
	}
	for( ; ; ) {
		int nRead;
		nRead = recv( d->descriptor, d->inbuf + iStart, MAX_INBUF_SIZE - 10 - iStart, 0 );
#ifdef WIN32
		iErr = WSAGetLastError();
#else
		iErr = errno;
#endif
		if( nRead > 0 ) {
			iStart += nRead;
			if( d->inbuf[iStart-1] == '\n' || d->inbuf[iStart-1] == '\r' )
				break;
		} else if( nRead == 0 ) {
			log_string( "EOF encountered on read." );
			return FALSE;
		} else if( iErr == EWOULDBLOCK ) {
			break; // nothing to read
		} else {
			perror( "Read_from_descriptor" );
			return FALSE;
		}
	}
	d->inbuf[iStart] = '\0';
	return TRUE;
}

static int check_telopts(DESCRIPTOR_DATA *d, char *soin, int i) {
	unsigned char iac_type, iac_command;
	int j = 0;
	if( (unsigned char)soin[i++] == IAC ) {
		iac_type = soin[i++];
		if( iac_type == SE ) {
			return i;
		}
		iac_command = soin[i++];
		switch( iac_type ) {
			case DO:
				if( iac_command == TELOPT_SGA ) {
					fprintf(stderr, "DO TELOPT_SGA\n");
					write_to_descriptor(d, will_suppress_str, 0);
				} else if( iac_command == TELOPT_ECHO ) {
					fprintf(stderr, "DO TELOPT_ECHO\n");
					// we control the vertical and the horizontal
				} else
					fprintf(stderr, "Unsupported IAC DO command %uc.\n", iac_command);
				break;
			case WILL:
				if( iac_command == TELOPT_COMPRESS2 ) {
					fprintf(stderr, "WILL TELOPT_COMPRESS2\n");
					compressStart(d);
				} else if( iac_command == TELOPT_NAWS ) {
					fprintf(stderr, "WILL TELOPT_NAWS\n");
					write_to_descriptor(d, neg_naws_str, 0);
				} else if( iac_command == TELOPT_TTYPE ) {
					fprintf(stderr, "WILL TELOPT_TTYPE\n");
					write_to_descriptor(d, neg_term_type_str, 0);
				} else if( iac_command == TELOPT_SGA ) {
					fprintf(stderr, "WILL TELOPT_SGA\n");
					write_to_descriptor(d, do_suppress_str, 0);
				} else if( iac_command == TELOPT_SPEED ) {
					fprintf(stderr, "WILL TELOPT_SPEED\n");
					// we do nothing as this really is obselete, but GO PUTTY!
				} else
					fprintf(stderr, "Unsupported IAC WILL command %uc.\n", iac_command);
				break;
			case DONT:
			case WONT:
				if( iac_command == TELOPT_COMPRESS2 ) {
					fprintf(stderr, "DONT/WONT TELOPT_COMPRESS2\n");
					compressEnd(d);
				} else if( iac_command == TELOPT_TTYPE ) {
					fprintf(stderr, "DONT/WONT TELOPT_TTYPE\n");
					write_to_descriptor(d, "Client does not support terminal type negotiation, ANSI color disabled.\n", 0);
					d->telOpts &= ~8;
				} else if( iac_command == TELOPT_NAWS ) {
					fprintf(stderr, "DONT/WONT TELOPT_NAWS\n");
					// unsupported negotiation on part of client, Pueblo notiably
				} else if( iac_command == TELOPT_LINEMODE ) {
					fprintf(stderr, "DONT/WONT TELOPT_LINEMODE\n");
					// unsupported negotiation on part of client, Pueblo notiably
				} else
					fprintf(stderr, "Unsupported IAC %s command %uc.\n", (iac_type == DONT ? "DONT" : "WONT"), iac_command);
				break;
			case SB:
				if( iac_command == TELOPT_TTYPE ) {
					// should be implemented without fault
					char ttype[24]; // should be well long enough for this
					j = 0;
					fprintf(stderr, "SB TELOPT_TTYPE\n");
					// blitz thru until we find ascii characters
					while( !isprint(soin[i]) ) i++;
					while( isprint(soin[i]) ) {
						ttype[j] = LOWER(soin[i]);
						i++; j++;
					}
					ttype[j] = '\0';
					j = 0;
					while( supported_ansi[j] != NULL ) {
						if( strcmp(ttype, supported_ansi[j]) == 0 ) {
							write_to_descriptor(d, "ANSI supported.\n", 0);
							d->telOpts |= 8;
							break;
						}
						j++;
					}
					// asks twice for supported clients, then kills ANSI
					if( (d->telOpts & 8) == 0 ) {
						if( (d->telOpts & 1) == 0 ) {
							write_to_descriptor(d, neg_term_type_str, 0);
							d->telOpts |= 1;
						} else {
							d->telOpts &= ~1;
							d->telOpts &= ~8;
						}
					}
				} else if( iac_command == TELOPT_NAWS ) {
					unsigned char ca, cb, cc, cd; //should be split into four characters
					fprintf(stderr, "SB TELOPT_NAWS\n");
					d->max_width = 0;
					ca = soin[i++];
					if( ca > 0 ) {
						if( ca == 255 )
							ca = soin[i++];
						d->max_width += (ca << 8);
					}
					cb = soin[i++];
					if( cb > 0 ) {
						if( cb == 255 )
							cb = soin[i++];
					}
					d->max_width += cb;
					d->max_height = 0;
					cc = soin[i++];
					if( cc > 0 ) {
						if( cc == 255 )
							cc = soin[i++];
						d->max_height += (cc << 8);
					}
					cd = soin[i++];
					if( cd > 0 ) {
						if( cd == 255 )
							cd = soin[i++];
					}
					d->max_height += cd;
				} else
					fprintf(stderr, "Unsupported IAC SB command %uc.\n", iac_command);
				break;
			case SE:
				fprintf(stderr, "Bad shit went down in check_telopts.\n");
				break;
			default:
				fprintf(stderr, "Unsupported IAC type %uc.\n", iac_type);
				break;
		}
	}
	return i;
}
Top

Posted by ThomasWatts   USA  (66 posts)  Bio
Date Reply #2 on Fri 09 Nov 2007 06:18 AM (UTC)

Amended on Fri 09 Nov 2007 08:56 AM (UTC) by Nick Gammon

Message

// Transfer one line from input buffer to input line.
// overhaul to support terminal type negotiation correctly 11-08-2007
static void read_from_buffer( DESCRIPTOR_DATA *d ) {
	int i, j, k;
	unsigned char temp;
	// Canonical input processing.
	for( i = 0, k = 0; d->inbuf[i] != '\0' ; i++ ) {
		if( k >= 254 ) {
			write_to_descriptor( d, "Line too long.\n\r", 0);
			d->inbuf[i]   = '\n';
			d->inbuf[i+1] = '\0';
			break;
		}
		temp = d->inbuf[i];
		i = check_telopts(d, d->inbuf, i);
		temp = d->inbuf[i];
		if( temp == '\0' ) {
			break;
		} else if( temp == '\b' ) { // ignore bells
		} else if( temp == '\n' ) { // tack on a new line if needed
			d->incomm[k++] = '\n';
		} else if( !isprint(temp) ) {
		} else if( isascii(temp) && isprint(temp) ) {
			d->incomm[k++] = temp;
		}
	}
	// Finish off the line. Strip the last CRLF if needed
	while( k > 1 && (d->incomm[k - 1] == '\n' || d->incomm[k - 1] == '\r') )
		--k;
	d->incomm[k] = '\0';
	// Shift the input buffer.
	while( d->inbuf[i] == '\n' || d->inbuf[i] == '\r' ) i++;
	for( j = 0; ( d->inbuf[j] = d->inbuf[i+j] ) != '\0'; j++ ) ;
	d->inbuf[j] = '\0';
	return;
}
Top

Posted by ThomasWatts   USA  (66 posts)  Bio
Date Reply #3 on Fri 09 Nov 2007 06:20 AM (UTC)
Message
Eegahs sorry about that formatting. Can I post the relevant file somewhere?
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #4 on Fri 09 Nov 2007 08:57 AM (UTC)
Message
I fixed it for you - you need to use [code] and [/code] and "escape" certain characters.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Samson   USA  (683 posts)  Bio
Date Reply #5 on Fri 09 Nov 2007 01:55 PM (UTC)
Message
I'm sure it's been asked plenty of times in the past but what's the benefit of declaring the function as:

static void read_from_buffer( DESCRIPTOR_DATA *d )

instead of:

void read_from_buffer( DESCRIPTOR_DATA *d )
Top

Posted by David Haley   USA  (3,881 posts)  Bio
Date Reply #6 on Fri 09 Nov 2007 06:09 PM (UTC)
Message
It makes the function "private" to that source file, meaning that you cannot refer to it by name from other source files. (You can still pass it as a function pointer, though.) It's a technique for encapsulation, kind of like public/private methods in classes.

But as to the problem at hand, I don't really know. :-/ I'm not too familiar with the telnet RFCs, and it's unclear if this is a problem due to not handling RFCs right or due to having a bug in the code. It's a fair bit of code to look through, after all...

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

http://david.the-haleys.org
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #7 on Fri 09 Nov 2007 07:09 PM (UTC)
Message
Quote:

I had to pull out the newline checks and listen for EWOULDBLOCK and null termination. Not so bad in and of itself, but what I'm left with are phantom input I'm not sure where it came from.


I'm not quite sure what is wrong from glancing at your code, but based on "I had to pull out the newline checks" I am not too surprised at phantom input. The newline is what defines the end of user input, after all, so without the newline checks it is likely to think that when there is no more input (ie. when the player is sitting there, pondering), that he has entered a command.

Perhaps there is more to it than that, but if you get a lot of this phantom input, this is a good time to use gdb to trace through what path the code is taking.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by ThomasWatts   USA  (66 posts)  Bio
Date Reply #8 on Fri 09 Nov 2007 08:38 PM (UTC)
Message
I may have fixed it. The function I did not post was new_descriptor in which the original IAC queries were sent.
According to the telnet RFCs, it's legal to send several IAC commands in the same line as well as one right after the other. IE:
IAC, DO, TERMTYPE, IAC, DO, LINEMODE
or
IAC, DO, TERMTYPE
IAC, DO, LINEMODE
However, it seems that some of that isn't parsed correctly by the server when the client replies. I attribute it to the server because it happened with 4 different clients, including MUSHclient.
How I hopefully fixed the problem was to stagger the IAC commands using the built in game_loop.

Also, a note on using 'static' in C. If the functions are not needed outside a specific file, it is important to add static to their declaration and/or definition so that they are NOT exported when exporting symbols for dlsym.
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


25,224 views.

It is now over 60 days since the last post. This thread is closed.     Refresh page

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.