issue #275: parent/ssh: centralize EC0_MARKER and change it for ssh.py.

Must maintain a minimum buffer length prior to deciding whether we have
an interesting token, and 'EC0' is too short for that.
pull/287/head
David Wilson 6 years ago
parent 84fa3ff024
commit d6126a9516

@ -51,7 +51,7 @@ can be recovered by the bootstrapped process later. It then forks into a new
process. process.
After fork, the parent half overwrites its ``stdin`` with the read end of the After fork, the parent half overwrites its ``stdin`` with the read end of the
pipe, and the child half writes the string ``EC0\n``, then begins reading the pipe, and the child half writes the string ``MITOGEN0\n``, then begins reading the
:py:mod:`zlib`-compressed payload supplied on ``stdin`` by the master, and :py:mod:`zlib`-compressed payload supplied on ``stdin`` by the master, and
writing the decompressed result to the write-end of the UNIX pipe. writing the decompressed result to the write-end of the UNIX pipe.
@ -112,12 +112,17 @@ fetched from the master a second time.
Signalling Success Signalling Success
################## ##################
Once the first stage has signalled ``EC0\n``, the master knows it is ready to Once the first stage has signalled ``MITO000\n``, the master knows it is ready
receive the compressed bootstrap. After decompressing and writing the bootstrap to receive the compressed bootstrap. After decompressing and writing the
source to its parent Python interpreter, the first stage writes the string bootstrap source to its parent Python interpreter, the first stage writes the
``EC1\n`` to ``stdout`` before exiting. The master process waits for this string ``MITO001\n`` to ``stdout`` before exiting. The master process waits for
string before considering bootstrap successful and the child's ``stdio`` ready this string before considering bootstrap successful and the child's ``stdio``
to receive messages. ready to receive messages.
The signal value is 8 bytes to match the minimum chunk size required to
disambiguate between lines containing an interesting token during SSH password
authentication, a debug message from the SSH client itself, or a message from
the first stage.
ExternalContext.main() ExternalContext.main()

@ -790,11 +790,11 @@ class Stream(mitogen.core.Stream):
sys.executable += sys.version[:3] sys.executable += sys.version[:3]
os.environ['ARGV0']=sys.executable os.environ['ARGV0']=sys.executable
os.execl(sys.executable,sys.executable+'(mitogen:CONTEXT_NAME)') os.execl(sys.executable,sys.executable+'(mitogen:CONTEXT_NAME)')
os.write(1,'EC0\n') os.write(1,'MITO000\n')
C=_(os.fdopen(0,'rb').read(PREAMBLE_COMPRESSED_LEN),'zip') C=_(os.fdopen(0,'rb').read(PREAMBLE_COMPRESSED_LEN),'zip')
os.fdopen(W,'w',0).write(C) os.fdopen(W,'w',0).write(C)
os.fdopen(w,'w',0).write('PREAMBLE_LEN\n'+C) os.fdopen(w,'w',0).write('PREAMBLE_LEN\n'+C)
os.write(1,'EC1\n') os.write(1,'MITO001\n')
def get_boot_command(self): def get_boot_command(self):
source = inspect.getsource(self._first_stage) source = inspect.getsource(self._first_stage)
@ -870,13 +870,17 @@ class Stream(mitogen.core.Stream):
self._reap_child() self._reap_child()
raise raise
#: For ssh.py, this must be at least max(len('password'), len('debug1:'))
EC0_MARKER = 'MITO000\n'
EC1_MARKER = 'MITO001\n'
def _ec0_received(self): def _ec0_received(self):
LOG.debug('%r._ec0_received()', self) LOG.debug('%r._ec0_received()', self)
write_all(self.transmit_side.fd, self.get_preamble()) write_all(self.transmit_side.fd, self.get_preamble())
discard_until(self.receive_side.fd, 'EC1\n', self.connect_deadline) discard_until(self.receive_side.fd, 'MITO001\n', self.connect_deadline)
def _connect_bootstrap(self, extra_fd): def _connect_bootstrap(self, extra_fd):
discard_until(self.receive_side.fd, 'EC0\n', self.connect_deadline) discard_until(self.receive_side.fd, 'MITO000\n', self.connect_deadline)
self._ec0_received() self._ec0_received()

@ -68,10 +68,10 @@ def filter_debug(stream, it):
while buf: while buf:
if state == 'start_of_line': if state == 'start_of_line':
if len(buf) < 8: if len(buf) < 8:
# short read near the buffer limit, block waiting for at # short read near buffer limit, block awaiting at least 8
# least 8 bytes so we can discern either a debug line, or # bytes so we can discern a debug line, or the minimum
# the minimum desired interesting token from above # interesting token from above or the bootstrap
# ('password'). # ('password', 'MITO000\n').
break break
elif buf.startswith(DEBUG_PREFIXES): elif buf.startswith(DEBUG_PREFIXES):
state = 'in_debug' state = 'in_debug'
@ -229,7 +229,7 @@ class Stream(mitogen.parent.Stream):
for buf in filter_debug(self, it): for buf in filter_debug(self, it):
LOG.debug('%r: received %r', self, buf) LOG.debug('%r: received %r', self, buf)
if buf.endswith('EC0\n'): if buf.endswith(self.EC0_MARKER):
self._router.broker.start_receive(self.tty_stream) self._router.broker.start_receive(self.tty_stream)
self._ec0_received() self._ec0_received()
return return

@ -97,7 +97,7 @@ class Stream(mitogen.parent.Stream):
for buf in it: for buf in it:
LOG.debug('%r: received %r', self, buf) LOG.debug('%r: received %r', self, buf)
if buf.endswith('EC0\n'): if buf.endswith(self.EC0_MARKER):
self._ec0_received() self._ec0_received()
return return
if any(s in buf.lower() for s in self.incorrect_prompts): if any(s in buf.lower() for s in self.incorrect_prompts):

@ -168,7 +168,7 @@ class Stream(mitogen.parent.Stream):
for buf in it: for buf in it:
LOG.debug('%r: received %r', self, buf) LOG.debug('%r: received %r', self, buf)
if buf.endswith('EC0\n'): if buf.endswith(self.EC0_MARKER):
self._ec0_received() self._ec0_received()
return return
elif PASSWORD_PROMPT in buf.lower(): elif PASSWORD_PROMPT in buf.lower():

Loading…
Cancel
Save