Made telnet protocols resync with server once their handshakes are complete. Also changed default (pre-TTYPE) to be ansi+xterm256. Set a 5-second timeout for handshakes. This pertains to issue #434.

This commit is contained in:
Griatch 2014-08-14 10:28:50 +02:00
parent d4a78a11a6
commit 31687b8a05
8 changed files with 65 additions and 10 deletions

View file

@ -54,6 +54,7 @@ class Mccp(object):
if hasattr(self.protocol, 'zlib'):
del self.protocol.zlib
self.protocol.protocol_flags['MCCP'] = False
self.protocol.handshake_done()
def do_mccp(self, option):
"""
@ -63,3 +64,4 @@ class Mccp(object):
self.protocol.protocol_flags['MCCP'] = True
self.protocol.requestNegotiation(MCCP, '')
self.protocol.zlib = zlib.compressobj(9)
self.protocol.handshake_done()

View file

@ -60,13 +60,14 @@ class Msdp(object):
def no_msdp(self, option):
"No msdp supported or wanted"
pass
self.protocol.handshake_done()
def do_msdp(self, option):
"""
Called when client confirms that it can do MSDP.
"""
self.protocol.protocol_flags['MSDP'] = True
self.protocol.handshake_done()
def evennia_to_msdp(self, cmdname, *args, **kwargs):
"""

View file

@ -47,6 +47,7 @@ class Mssp(object):
"""
This is the normal operation.
"""
self.protocol.handshake_done()
pass
def do_mssp(self, option):
@ -181,3 +182,4 @@ class Mssp(object):
# send to crawler by subnegotiation
self.protocol.requestNegotiation(MSSP, varlist)
self.protocol.handshake_done()

View file

@ -2,7 +2,7 @@
Sessionhandler for portal sessions
"""
import time
from src.server.sessionhandler import SessionHandler, PCONN, PDISCONN
from src.server.sessionhandler import SessionHandler, PCONN, PDISCONN, PSYNC, PCONNSYNC
_MOD_IMPORT = None
@ -54,6 +54,19 @@ class PortalSessionHandler(SessionHandler):
self.portal.amp_protocol.call_remote_ServerAdmin(sessid,
operation=PCONN,
data=sessdata)
def sync(self, session):
"""
Called by the protocol of an already connected session. This
can be used to sync the session info in a delayed manner,
such as when negotiation and handshakes are delayed.
"""
if session.sessid:
# only use if session already has sessid (i.e. has already connected)
sessdata = session.get_sync_data()
if self.portal.amp_protocol:
self.portal.amp_protocol.call_remote_ServerAdmin(session.sessid,
operation=PCONNSYNC,
data=sessdata)
def disconnect(self, session):
"""

View file

@ -30,6 +30,7 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
"""
# initialize the session
self.iaw_mode = False
self.handshakes = 4 # ttype, mccp, mssp, msdp
client_address = self.transport.client
self.init_session("telnet", client_address, self.factory.sessionhandler)
# negotiate ttype (client info)
@ -43,6 +44,7 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
self.msdp = msdp.Msdp(self)
# add this new connection to sessionhandler so
# the Server becomes aware of it.
self.sessionhandler.connect(self)
# This is a fix to make sure the connection does not
# continue until the handshakes are done. This is a
@ -54,9 +56,26 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
# to their defaults since sessionhandler.connect will sync
# before the handshakes have had time to finish. Keeping this patch
# until coming up with a more elegant solution /Griatch
from src.utils.utils import delay
delay(1, callback=self.sessionhandler.connect, retval=self)
#self.sessionhandler.connect(self)
delay(5, callback=self.handshake_done, retval=True)
def handshake_done(self, force=False):
"""
This is called by all telnet extensions once they are finished.
When all have reported, a sync with the server is performed.
The system will force-call this sync after a small time to handle
clients that don't reply to handshakes at all.
info - debug text from the protocol calling
"""
if self.handshakes > 0:
if force:
self.sessionhandler.sync(self)
return
self.handshakes -= 1
if self.handshakes <= 0:
# do the sync
self.sessionhandler.sync(self)
def enableRemote(self, option):
"""
@ -208,10 +227,10 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
# parse **kwargs, falling back to ttype if nothing is given explicitly
ttype = self.protocol_flags.get('TTYPE', {})
xterm256 = kwargs.get("xterm256", ttype and ttype.get('256 COLORS', False))
useansi = kwargs.get("ansi", ttype and ttype.get('ANSI', False))
xterm256 = kwargs.get("xterm256", ttype.get('256 COLORS', False) if ttype.get("init_done") else True)
useansi = kwargs.get("ansi", ttype and ttype.get('ANSI', False) if ttype.get("init_done") else True)
raw = kwargs.get("raw", False)
nomarkup = kwargs.get("nomarkup", not (xterm256 or useansi) or not ttype.get("init_done"))
nomarkup = kwargs.get("nomarkup", not (xterm256 or useansi))
prompt = kwargs.get("prompt")
if prompt:
@ -229,5 +248,5 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
else:
# we need to make sure to kill the color at the end in order
# to match the webclient output.
# print "telnet data out:", self.protocol_flags, id(self.protocol_flags), id(self)
#print "telnet data out:", self.protocol_flags, id(self.protocol_flags), id(self), "nomarkup: %s, xterm256: %s" % (nomarkup, xterm256)
self.sendLine(ansi.parse_ansi(_RE_N.sub("", text) + "{n", strip_ansi=nomarkup, xterm256=xterm256))

View file

@ -54,6 +54,7 @@ class Ttype(object):
Callback if ttype is not supported by client.
"""
self.protocol.protocol_flags['TTYPE']["init_done"] = True
self.protocol.handshake_done()
def will_ttype(self, option):
"""
@ -139,4 +140,6 @@ class Ttype(object):
self.protocol.protocol_flags['TTYPE']['init_done'] = True
# print "TTYPE final:", self.protocol.protocol_flags['TTYPE']
# we must sync ttype once it'd done
self.protocol.handshake_done()
self.ttype_step += 1