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
➜ MUDs
➜ General
➜ Telnet Option Negotiation
|
Telnet Option Negotiation
|
It is now over 60 days since the last post. This thread is closed.
Refresh page
Pages: 1 2
| Posted by
| Samson
USA (683 posts) Bio
|
| Date
| Thu 11 Apr 2002 09:01 AM (UTC) Amended on Mon 18 Aug 2014 04:49 AM (UTC) by Samson
|
| Message
| I ran across a snippet today that allows for a mud to do some telnet negotioation with the client to determine it's id string. I was wondering if you might know of a way to get smaug to work with this since I notice that Mushclient will respond to the request string. I've been trying to get this working in mine, but the code isn't doing much beyond disrupting the login process.
In the packet debug, this is what is sent at the beginning of the greeting display:
0a 0d ff fa 18 01 ff f0 ff fb 56 ff fb 5b ff fb 5a
After the rest of the greeting packet is spewed, this gets sent back:
Sent packet: 1 (16 bytes)
....mushclient.. ff fa 18 00 6d 75 73 68 63 6c 69 65 6e 74 ff f0
Sent packet: 2 (3 bytes)
..V ff fd 56
Sent packet: 3 (3 bytes)
..[ ff fd 5b
Sent packet: 4 (3 bytes)
..Z ff fe 5a
Now, the problem of course is, the client ID string isn't being sent with a /n/r combination since that shows up as 0a 0d. It seems to instead be terminated by the ff f0 combination. Now, what's happening is this. I boot the mud, then try to log in. The login sequence asks my name. I input it. The login prompt then tells me no such player exists, at which point I can input it again, get the password prompt, and go from there. Only once I'm in, the MCCP/MXP/MSP options have failed to negotiate, and the logs don't show that it ever detected a terminal.
The same thing is happening with Zmud too. Simply fails to detect and then disrupts the entire login process. Of course I can't get a nice neat packet debug off of that. And interestingly enough, it seems that Linux telnet doesn't even respond to the query since it logged in fine.
The code I am using to do this is as follows: (it's commented out right now since it doesn't work)
if ( d->inbuf[i] == (signed char)IAC )
iac = 1;
else if ( iac == 1 && ( d->inbuf[i] == (signed char)DO || d->inbuf[i] == (signed char)DONT
|| d->inbuf[i] == (signed char)TERMINAL_TYPE ) )
iac = 2;
else if( iac == 2 )
{
iac = 0;
/*
if( d->inbuf[i] == (signed char)IS && d->inbuf[i-1] == (signed char)TERMINAL_TYPE )
{
char tmp[100];
int x, y = i+1;
for( x = 0; x < 100; x++, y++ )
tmp[x] = d->inbuf[y];
tmp[100] = '\0';
log_printf( "%s client detected for %s.", tmp, d->host );
}
*/
if ( d->inbuf[i] == (signed char)TELOPT_COMPRESS2 )
{
if ( d->inbuf[i-1] == (signed char)DO )
{
log_printf( "MCCP support detected for %s.", d->host );
compressStart( d );
}
else if ( d->inbuf[i-1] == (signed char)DONT )
compressEnd( d );
}
else if ( d->inbuf[i] == (signed char)TELOPT_MXP )
{
if ( d->inbuf[i-1] == (signed char)DO )
{
log_printf( "MXP support detected for %s.", d->host );
send_mxp_stylesheet( d );
}
}
else if ( d->inbuf[i] == (signed char)TELOPT_MSP )
{
if ( d->inbuf[i-1] == (signed char)DO )
{
log_printf( "MSP support detected for %s.", d->host );
send_msp_startup( d );
}
}
}
The original snippet I was modifying for use on my mud was here: http://www.auricmud.com/snippets/client-code.html
--
Modified by Nick to add forum codes for readability. | | Top |
|
| Posted by
| Nick Gammon
Australia (23,165 posts) Bio
Forum Administrator |
| Date
| Reply #1 on Thu 11 Apr 2002 11:42 PM (UTC) Amended on Thu 11 Apr 2002 11:45 PM (UTC) by Nick Gammon
|
| Message
| OK, let's analyse the first packet ...
0a 0d ff fa 18 01 ff f0 ff fb 56 ff fb 5b ff fb 5a
0a 0d - line feed, carriage return ( \n \r )
ff - IAC
fa - SB - subnegotiation
18 - terminal type - TERMINAL_TYPE
01 - SEND
ff - IAC
f0 - SE - end of subnegotiation
ff - IAC
fb - WILL
56 - MCCP version 2 (MUD client compression) - TELOPT_COMPRESS2
ff - IAC
fb - WILL
5b - MXP (MUD Extension protocol) - TELOPT_MXP
ff - IAC
fb - WILL
5a - MSP (MUD Sound Protocol) - TELOPT_MSP
We reply: IAC SB TERMINAL-TYPE IS mushclient IAC SE
( see: RFC 930 and RFC 1060 )
In detail this is:
....mushclient.. ff fa 18 00 6d 75 73 68 63 6c 69 65 6e 74 ff f0
ff - IAC
fa - SB - subnegotiation
18 - terminal type
00 - IS
6d 75 73 68 63 6c 69 65 6e 74 - mushclient
ff - IAC
f0 - SE - end of subnegotiation
Note that the RFC does not call for a carriage-return/line feed combination here - the terminal type is supposed to be supplied as above (see RFCs for more details).
Looking at your code it seems it is not correctly recognising the above sequence. It seems to me that it has three "modes" for the iac variable ...
0 - nothing special
1 - received IAC
2 - received IAC followed by DO/DONT/TERMINAL TYPE
When it gets case "2" it then looks for TELOPT_COMPRESS2, TELOPT_MXP or TELOPT_MSP (plus the terminal type code which isn't working).
However the bug is that "terminal type" is preceded by SB.
I would add another mode:
3 - received IAC followed by SB
Although I haven't tested it I would do something like this ...
if ( d->inbuf[i] == (signed char)IAC )
iac = 1;
else if ( iac == 1 &&
( d->inbuf[i] == (signed char)DO || d->inbuf[i] == (signed char)DONT ) )
iac = 2; /* have DO or DONT */
else if ( iac == 1 &&
d->inbuf[i] == (signed char)SB )
iac = 3; /* have subnegotiation */
else if( iac == 2 )
/* have DO or DONT previously */
{
iac = 0; /* cancel IAC after this */
if ( d->inbuf[i] == (signed char)TELOPT_COMPRESS2 )
{
if ( d->inbuf[i-1] == (signed char)DO )
{
log_printf( "MCCP support detected for %s.", d->host );
compressStart( d );
}
else if ( d->inbuf[i-1] == (signed char)DONT )
compressEnd( d );
}
else if ( d->inbuf[i] == (signed char)TELOPT_MXP )
{
if ( d->inbuf[i-1] == (signed char)DO )
{
log_printf( "MXP support detected for %s.", d->host );
send_mxp_stylesheet( d );
}
}
else if ( d->inbuf[i] == (signed char)TELOPT_MSP )
{
if ( d->inbuf[i-1] == (signed char)DO )
{
log_printf( "MSP support detected for %s.", d->host );
send_msp_startup( d );
}
}
} /* end of iac == 2 */
else if( iac == 3 )
/* have SB previously */
{
if( d->inbuf[i] == (signed char)TERMINAL_TYPE &&
d->inbuf[i + 1] == 0 )
{
char tmp[100];
int x;
i += 2; /* skip TERMINAL_TYPE / IS characters */
for( x = 0; x < (sizeof (tmp) - 1)
&& d->inbuf [i] != 0 /* null marks end of buffer */
&& d->inbuf [i] != IAC; /* should terminate with IAC */
x++, i++)
tmp[x] = d->inbuf[i];
tmp[x] = '\0';
log_printf( "%s client detected for %s.", tmp, d->host );
}
/* if we got an IAC we are still inside the IAC */
if( d->inbuf[i] == (signed char)IAC )
iac = 1;
else
iac = 0;
} /* end of iac == 3 */
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | | Top |
|
| Posted by
| Samson
USA (683 posts) Bio
|
| Date
| Reply #2 on Fri 12 Apr 2002 11:22 AM (UTC) |
| Message
| else if( iac == 3 )
/* have SB previously */
{
log_string( "iac = 3" );
log_printf( "code: %c code2: %d", d->inbuf[i], (int)d->inbuf[i+1] );
if( d->inbuf[i] == (signed char)TERMINAL_TYPE && d->inbuf[i+1] == '0' )
{
char tmp[100];
int x;
i += 2; /* skip TERMINAL_TYPE / IS characters */
log_string( "scan for string" );
for( x = 0; x < (sizeof (tmp) - 1 )
&& d->inbuf[i] != '\0' /* null marks end of buffer */
&& d->inbuf[i] != (signed char)IAC; /* should terminate with IAC */
x++, i++ )
tmp[x] = d->inbuf[i];
tmp[x] = '\0';
log_printf( "%s client detected for %s.", tmp, d->host );
}
/* if we got an IAC we are still inside the IAC */
if( d->inbuf[i] == (signed char)IAC )
iac = 1;
else
iac = 0;
} /* end of iac == 3 */
Well. This section works. Except it doesn't work completely. The code will drop into iac = 3, the logs show me that much. However, it seems the buffer is tripping over the 'IS' code. With the above log_printf, I got this:
Log: iac = 3
Log: code: code2: 90
( that first code is an up arrow, ascii 24, 18hex )
Now, obviously something isn't right here since mushclient sent a 0 to the mud, but the buffer thinks it's a 90. It never drops into the part where tmp is built. Changing the ifcheck to look for 90, which is 'Z', results in a mess at login as it somehow attempts to send 1024 chars into the name prompt which obviously isn't going to work either. I can't figure out how to get around this, nor how anyone could possibly use this form of negotiation if the code is going to botch something as simple as storing a "0" where it should be.
So. Any ideas on how to proceed from here? :) | | Top |
|
| Posted by
| Nick Gammon
Australia (23,165 posts) Bio
Forum Administrator |
| Date
| Reply #3 on Fri 12 Apr 2002 02:18 PM (UTC) |
| Message
| Yes, you aren't getting the terminal type yet.
90 in decimal is 5A in hex, which is the code for MSP (TELOPT_MSP) so it isn't surprising you can't get a terminal type out of it.
How does the server currently handle the response for MSP?
Basically, once the client has asked "will you do MSP" (IAC WILL TELOPT_MSP) and you reply "yes I will" (IAC DO TELOPT_MSP) then it then responds saying "well do it" (IAC SB TELOPT_MSP IAC SE).
Thus you need to allow for the TELOPT_MSP (90) following the IAC SB. I'm not going to write all the code but it should be obvious what to do. Similarly for MCCP and MXP. |
- Nick Gammon
www.gammon.com.au, www.mushclient.com | | Top |
|
| Posted by
| Samson
USA (683 posts) Bio
|
| Date
| Reply #4 on Fri 12 Apr 2002 08:28 PM (UTC) |
| Message
| | No no. I think you've misunderstood. After doing some more checking I figured out that when it processes the TERMINAL_TYPE code, it is treating the 'IS' code as NULL since it's 00. And the problem comes from the fact that once the input buffer reaches a null it stops processing anything after that, which is why it never enters the part where tmp gets put together. After I tested it a few times I noticed that the "90" was turning into all sorts of things, even though the packet log from Mushclient clearly showed a 00 being delivered each time. | | Top |
|
| Posted by
| Nick Gammon
Australia (23,165 posts) Bio
Forum Administrator |
| Date
| Reply #5 on Fri 12 Apr 2002 09:38 PM (UTC) |
| Message
| You may be right about the 0 byte, however my point is that you are not looking at the packet you showed. Here it is ...
Sent packet: 1 (16 bytes)
....mushclient.. ff fa 18 00 6d 75 73 68 63 6c 69 65 6e 74 ff f0
Now you are detecting 90 (decimal) which is 5a in hex. There is no 5a in that packet, so you are looking at something else. |
- Nick Gammon
www.gammon.com.au, www.mushclient.com | | Top |
|
| Posted by
| Samson
USA (683 posts) Bio
|
| Date
| Reply #6 on Fri 12 Apr 2002 10:13 PM (UTC) |
| Message
| | And I'd say you were correct if I hadn't also seen 108, 212, 21, 10, 3, 15, 94, 97, 101, 110, and 77 in the same spot. The valus isn't constant, so I can only assume it's treating the 00 as NULL in the code. I don't see any way around this problem since you can't tell the read_from_buffer code to allow nulls without fubaring the socket read. And the iac=3 condition will only be triggered if hex 18 shows up there. So I know it's at least seeing enough to determine it needs to drop in there, which can't happen with MSP, MXP, or MCCP negotiation. And Mushclient isn't sending anything else aside from those 3 and it's terminal ID string. So there has to be some way to get around it seeing this as a null, cause everything AFTER the null is utterly ignored. | | Top |
|
| Posted by
| Nick Gammon
Australia (23,165 posts) Bio
Forum Administrator |
| Date
| Reply #7 on Fri 12 Apr 2002 11:57 PM (UTC) |
| Message
| After fairly extensive playing with the code and the debugger I think I see what is happening. Assuming - correct me if I am wrong - you are using the MXP code from this site, then the problem is with that.
My debugging displays show this ...
Number read = 26
** Found IAC ** followed by fd
** Found IAC ** followed by fa
FD is "do" and I guess it is getting "DO MXP" first. Now the problem is with this line of code in my telnet negotiation for MXP:
/* remove string from input buffer */
memmove (p, &p [strlen (do_mxp_str)], strlen (&p [strlen (do_mxp_str)]) + 1);
It is removing the MXP string by doing a 'strlen' which means the rest of the buffer will be truncated at the zero byte.
In 'read_from_descriptor' you would have to store the number of bytes read (eg d->bytes_in_buffer) rather than null terminating. Then you could use that figure for the memmove. It will take a bit of juggling here and there, but it can be done. :)
Alternatively, and more simply, change the memmove to move the maximum buffer size rather than doing a strlen. Something like this:
int pos = p - d->inbuf; // where we are in buffer
int len = sizeof (inbuf) - pos - strlen (do_mxp_str); // how much to go
memmove (p, & p [ strlen (do_mxp_str)], len);
After a bit more testing and mucking around, this seems to work. It is the start of function read_from_buffer ...
/*
* Transfer one line from input buffer to input line.
*/
void read_from_buffer( DESCRIPTOR_DATA *d )
{
int i, j, k;
unsigned char * p;
/*
* Hold horses if pending command already.
*/
if ( d->incomm[0] != '\0' )
return;
/*
Look for incoming telnet negotiation
*/
for (p = d->inbuf; *p; p++)
if (*p == IAC)
{
if (memcmp (p, do_mxp_str, strlen (do_mxp_str)) == 0)
{
int pos = (char *) p - d->inbuf; // where we are in buffer
int len = sizeof (d->inbuf) - pos - strlen (do_mxp_str); // how much to go
turn_on_mxp (d);
/* remove string from input buffer */
memmove (p, & p [ strlen (do_mxp_str)], len);
p--; /* adjust to allow for discarded bytes */
} /* end of turning on MXP */
else if (memcmp (p, dont_mxp_str, strlen (dont_mxp_str)) == 0)
{
int pos = (char *) p - d->inbuf; // where we are in buffer
int len = sizeof (d->inbuf) - pos - strlen (dont_mxp_str); // how much to go
d->mxp = FALSE;
/* remove string from input buffer */
memmove (p, & p [ strlen (dont_mxp_str)], len);
p--; /* adjust to allow for discarded bytes */
} /* end of turning off MXP */
else if (memcmp (p, terminal_type, sizeof (terminal_type)) == 0)
{
int pos = (char *) p - d->inbuf; // where we are in buffer
int len = sizeof (d->inbuf) - pos - sizeof (terminal_type); // how much to go
char tmp[100];
int x = 0;
unsigned char * oldp = p;
p += sizeof (terminal_type); /* skip TERMINAL_TYPE / IS characters */
for( x = 0; x < (sizeof (tmp) - 1)
&& *p != 0 /* null marks end of buffer */
&& *p != IAC; /* should terminate with IAC */
x++, p++)
tmp[x] = *p;
tmp[x] = '\0';
p += 2; // skip IAC and SE
printf( "%s client detected for %s.\n", tmp, d->host );
fflush (stdout);
len -= strlen (tmp) + 2;
if (len < 0)
len = 0;
/* remove string from input buffer */
memmove (oldp, p, len);
} /* end of getting terminal type */
} /* end of finding an IAC */
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | | Top |
|
| Posted by
| Nick Gammon
Australia (23,165 posts) Bio
Forum Administrator |
| Date
| Reply #8 on Sat 13 Apr 2002 02:31 AM (UTC) |
| Message
| Oh yes, and you'll need these declarations ...
#define TERMINAL_TYPE 0x18
#define IS 0x00
#define SEND 0x01
const unsigned char terminal_type [] = { IAC, SB, TERMINAL_TYPE, IS };
const unsigned char send_terminal_type [] = { IAC, SB, TERMINAL_TYPE, SEND, IAC, SE, '\0' };
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | | Top |
|
| Posted by
| Samson
USA (683 posts) Bio
|
| Date
| Reply #9 on Sat 13 Apr 2002 11:29 AM (UTC) |
| Message
| Sweet! It's working now, thanks :)
Now to actually do something useful with it :P | | Top |
|
| Posted by
| Vermithrax
USA (11 posts) Bio
|
| Date
| Reply #10 on Wed 05 Jun 2002 01:13 PM (UTC) |
| Message
| Just a quick note on this, fellas... :) Before you start the sub-negotiation of asking for the terminal type, you should ask whether the client supports asking for the terminal type by sending this first:
IAC DO TERMINAL-TYPE
The client then should respond with:
IAC WILL TERMINAL-TYPE or IAC WONT TERMINAL-TYPE
When the IAC WILL TERMINAL-TYPE comes in, at THAT point, you should start the sub-negotiation with:
IAC SB TERMINAL-TYPE SEND IAC SE
And the client (theoretically), should respond with:
IAC SB TERMINAL-TYPE IS <whatever> IAC SE
I'd recommend doing it this way just to stick with the telnet negotiation protocol (once again, RFC 930). :) If you send the subnegotiation sequence without the IAC DO TERMINAL-TYPE, some clients won't respond correctly. | | Top |
|
| Posted by
| Vermithrax
USA (11 posts) Bio
|
| Date
| Reply #11 on Wed 05 Jun 2002 01:54 PM (UTC) |
| Message
| | BTW, if I read the RFC wrong, please let me know. :) | | Top |
|
| Posted by
| Vermithrax
USA (11 posts) Bio
|
| Date
| Reply #12 on Wed 05 Jun 2002 02:42 PM (UTC) |
| Message
| | Yeah, I implemented it as per the RFC, and for sure MUSHclient responds properly to the IAC DO TERMINAL-TYPE. As does zMUD. gMUD, of course, knows nothing. :P I'm about to test SimpleMU and see how it stands up. :P *grin* | | Top |
|
| Posted by
| halkeye
Canada (28 posts) Bio
|
| Date
| Reply #13 on Tue 16 Dec 2003 08:23 AM (UTC) |
| Message
| Although i'm not fully understanding the telnet negotation (i got the basics down pact... but it seems some programs like to throw in extra stuff)
I couldn't figure out exactly how you were outputting your data (it would make thing so much easier if i could), so i wrote this quick snippet (arpa/telnet.h has all kinda of useful information)
#ifdef DEBUG
{
int temp;
char buf[255];
strcpy(buf,"IAC ");
for (temp = 1; temp < 5; temp++) {
if (*(p+temp) == IAC) break;
if (*(p+temp) == '\0')
strcat(buf, " NULL");
else if (TELOPT_OK(*(p+temp)))
strcat( buf, telopts[(unsigned int) *(p+temp)]);
else if (TELCMD_OK((*(p+temp))))
strcat( buf, telcmds[((unsigned int) *(p+temp))-236]);
else {
char tempbuf[15];
sprintf(tempbuf, "%d", (unsigned int) *(p+temp));
strcat(buf, tempbuf);
}
strcat(buf, " ");
}
bug("TELOPT: %s", buf);
}
#endif
Unfortuatly, as i've almost got it completly working.. but getting confused by the output (using the telnet client)
Tue Dec 16 01:05:05 2003 :: [*****] BUG: TELOPT: IAC DONT 85
(thats not compression i belive)
Tue Dec 16 01:05:05 2003 :: [*****] BUG: TELOPT: IAC WILL TERMINAL TYPE
(will output terminal type, though it doesn't seem to set it)
Tue Dec 16 01:05:05 2003 :: [*****] BUG: TELOPT: IAC WILL NAWS NULL NULL
(NOT A CLUE what this one is, can't figure out which RFC actually tells me what i need to know about the options)
Tue Dec 16 01:05:05 2003 :: [*****] BUG: TELOPT: IAC DO 86
(compress2 right? or do i have them backwards, no matter)
Was also getting IAC WONT EOR earlier too. When do we send EOR's btw? or when do we expect to recieve them? what are they?
I am so trying to make sure i detect everything, even if i don't need to use it. |
Gavin
Dark Warriors - Coder
http://darkwars.wolfpaw.net
telnet://darkwars.wolfpaw.net:4848 | | Top |
|
| Posted by
| halkeye
Canada (28 posts) Bio
|
| Date
| Reply #14 on Tue 16 Dec 2003 08:35 AM (UTC) |
| Message
| ftp://ftp.rfc-editor.org/in-notes/rfc885.txt
I found a copy of the RFC that mentions EORs, but doesn't exactly make much more sense than it did before.
I also forgot to mention that in telnet (tested mushclient, zmud, mcclient, and i think gmud), i got bad info on logon.
Login: Gavin
Character not found, VGavin, new character (Y/N): N
Login: Gavin
Password:
sometimes its UU (before i put some EOR trapping code in), but lately, its been V, at least in telnet. |
Gavin
Dark Warriors - Coder
http://darkwars.wolfpaw.net
telnet://darkwars.wolfpaw.net:4848 | | 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.
61,371 views.
This is page 1, subject is 2 pages long: 1 2
It is now over 60 days since the last post. This thread is closed.
Refresh page
top