drivers/net/telnet: Ignore unsupported subnegotiation data.

Ignore unsupported telnet subnegotiation payload until `IAC SE` so
option bytes do not leak into the first NSH command.

Keep existing NAWS handling intact and also treat `IAC IAC` inside
subnegotiation payload as an escaped `0xFF` data byte rather than a
subnegotiation terminator.

This makes subnegotiation parsing RFC-compliant for both unsupported
options and NAWS payload processing.

Signed-off-by: Shunchao Hu <ankohuu@gmail.com>
This commit is contained in:
Shunchao Hu
2026-03-24 15:52:11 +08:00
committed by Xiang Xiao
parent ba0f1806a2
commit ab0ad3cee5
+44 -9
View File
@@ -107,8 +107,8 @@ enum telnet_state_e
STATE_DO,
STATE_DONT,
STATE_SB,
STATE_SB_NAWS,
STATE_SE
STATE_SB_DATA,
STATE_SB_NAWS
};
/* This structure describes the internal state of the driver */
@@ -118,6 +118,7 @@ struct telnet_dev_s
uint8_t td_state; /* (See telnet_state_e) */
uint8_t td_crefs; /* The number of open references to the session */
uint8_t td_minor; /* Minor device number */
bool td_sb_iac; /* Saw IAC within sub-negotiation payload */
uint16_t td_offset; /* Offset to the valid, pending bytes in the rxbuffer */
uint16_t td_pending; /* Number of valid, pending bytes in the rxbuffer */
#ifdef CONFIG_TELNET_SUPPORT_NAWS
@@ -372,16 +373,17 @@ static ssize_t telnet_receive(FAR struct telnet_dev_s *priv,
priv->td_state = STATE_DONT;
break;
#ifdef CONFIG_TELNET_SUPPORT_NAWS
case TELNET_SB:
priv->td_state = STATE_SB;
priv->td_sb_iac = false;
#ifdef CONFIG_TELNET_SUPPORT_NAWS
priv->td_sb_count = 0;
#endif
break;
case TELNET_SE:
priv->td_state = STATE_NORMAL;
break;
#endif
default:
priv->td_state = STATE_NORMAL;
@@ -458,25 +460,58 @@ static ssize_t telnet_receive(FAR struct telnet_dev_s *priv,
}
break;
#ifdef CONFIG_TELNET_SUPPORT_NAWS
/* Handle Telnet Sub negotiation request */
/* Handle Telnet sub-negotiation requests. */
case STATE_SB:
switch (ch)
{
#ifdef CONFIG_TELNET_SUPPORT_NAWS
case TELNET_NAWS:
priv->td_state = STATE_SB_NAWS;
break;
#endif
default:
priv->td_state = STATE_NORMAL;
priv->td_state = STATE_SB_DATA;
break;
}
break;
/* Ignore unsupported sub-negotiation payload until IAC SE. */
case STATE_SB_DATA:
if (priv->td_sb_iac)
{
priv->td_sb_iac = false;
if (ch == TELNET_SE)
{
priv->td_state = STATE_NORMAL;
}
}
else if (ch == TELNET_IAC)
{
priv->td_sb_iac = true;
}
break;
#ifdef CONFIG_TELNET_SUPPORT_NAWS
/* Handle NAWS sub-option negotiation */
case STATE_SB_NAWS:
if (priv->td_sb_iac)
{
priv->td_sb_iac = false;
if (ch == TELNET_SE)
{
priv->td_state = STATE_NORMAL;
break;
}
}
else if (ch == TELNET_IAC)
{
priv->td_sb_iac = true;
break;
}
/* Update cols / rows based on received byte count */
@@ -500,11 +535,11 @@ static ssize_t telnet_receive(FAR struct telnet_dev_s *priv,
break;
}
/* Increment SB count and switch to NORMAL when complete */
/* Increment SB count and keep discarding until IAC SE. */
if (++priv->td_sb_count == 4)
{
priv->td_state = STATE_NORMAL;
priv->td_state = STATE_SB_DATA;
}
break;