[Twisted-Python] Effects of calling transport.writeSomeData() ?

Mark Montague mark at catseye.org
Fri Jun 20 16:22:57 MDT 2014


We're writing a Twisted 14.0.0 application (on Python 2.7.7, Mac OS 
10.9.3) that uses Conch as an SSH client; this is working fine. However, 
we have the requirement that in an advanced mode of operation for power 
users that the application take advantage of OpenSSH connection 
multiplexing over an already-established-by-the-user OpenSSH 
ControlMaster session (via an OpenSSH ControlPath socket) instead of 
using Conch.

OpenSSH requires its new session command and forwarded file descriptors 
to be sent over the socket in a very particular way: the command must be 
sent first, followed by message with a '\0' byte with each forwarded 
file descriptor.  OpenSSH ignores the '\0' for each file descriptor, 
extracting the file descriptors themselves from the message's ancillary 
data.

The following will not work because none of the calls to write() send 
their data until control is returned to the reactor, while 
sendFileDescriptor() queues up the descriptors such that they get sent 
them with the very next data that is sent -- which wind up being the 
first three bytes of the command rather than the three '\0' bytes.

class OpenSSHMuxProtocol( protocol.Protocol ):
     # built via reactor.connectUNIX()
     def sendCommand( self, command ):
         # Does not work:
         self.transport.write( command )
         self.transport.sendFileDescriptor( sys.stdin.fileno() )
         self.transport.write( '\0' ) # payload for the stdin file descriptor
         self.transport.sendFileDescriptor( sys.stdout.fileno() )
         self.transport.write( '\0' ) # payload for the stdout file descriptor
         self.transport.sendFileDescriptor( sys.stdout.fileno() )
         self.transport.write( '\0' ) # payload for the stderr file descriptor
         # ^^^ Does not work


But this next solution /does/ work:

from socket import SOL_SOCKET
from twisted.python.sendmsg import SCM_RIGHTS, send1msg

class OpenSSHMuxProtocol( protocol.Protocol ):
     # built via reactor.connectUNIX()
     def sendCommand( self, command ):
         self.transport.writeSomeData( command ) # data is sent over the socket immediately
         send1msg( self.transport.socket.fileno(), "\0", 0,
             [ ( SOL_SOCKET, SCM_RIGHTS, pack( 'i', sys.stdin.fileno() ) ) ] )
         send1msg( self.transport.socket.fileno(), "\0", 0,
             [ ( SOL_SOCKET, SCM_RIGHTS, pack( 'i', sys.stdout.fileno() ) ) ] )
         send1msg( self.transport.socket.fileno(), "\0", 0,
             [ ( SOL_SOCKET, SCM_RIGHTS, pack( 'i', sys.stderr.fileno() ) ) ] )


My questions are:

Is it bad to bypass the reactor and send data directly/immediately this 
way using writeSomeData() and send1msg()?  Note that sendCommand() 
actually gets called in response to a OpenSSHMuxProtocol.dataReceived() 
event.  If bypassing the reactor this way is bad, how bad is it and what 
are the consequences or effects?

Is there a better way to get a working solution?  I think I'd need some 
way to guarantee that the write of the command was actually sent to the 
OpenSSH server before the file descriptors are forwarded -- for example, 
if a Deferred was used whose first callback wrote the command and whose 
second callback forwarded the descriptors, would a call to the reactor 
to actually sent the command be guaranteed between the two callbacks?

Any information and/or advice is appreciated!

-- 
   Mark Montague
   mark at catseye.org

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://twistedmatrix.com/pipermail/twisted-python/attachments/20140620/e653aea3/attachment.html>


More information about the Twisted-Python mailing list