[Twisted-Python] Problem Reading a Directory with Conch/SFTP

Jeffrey Ollie jeff at ocjtech.us
Mon Aug 22 16:02:25 EDT 2011

I'm re-writing a client that downloads some data from a 3rd party
using SFTP.  The old client was written using Paramiko but I'd like to
rewrite it using Twisted and Conch.  Right now I'm running into an
issue trying to get a directory listing from the remote server:

2011-08-22 13:35:03-0500 [SSHChannel session (0) on SSHService
ssh-connection on _WrappingProtocol,client] Unhandled Error
	Traceback (most recent call last):
	  File "/usr/lib64/python2.7/site-packages/twisted/python/log.py",
line 84, in callWithLogger
	    return callWithContext({"system": lp}, func, *args, **kw)
	  File "/usr/lib64/python2.7/site-packages/twisted/python/log.py",
line 69, in callWithContext
	    return context.call({ILogContext: newCtx}, func, *args, **kw)
	  File "/usr/lib64/python2.7/site-packages/twisted/python/context.py",
line 118, in callWithContext
	    return self.currentContext().callWithContext(ctx, func, *args, **kw)
	  File "/usr/lib64/python2.7/site-packages/twisted/python/context.py",
line 81, in callWithContext
	    return func(*args,**kw)
	--- <exception caught here> ---
	  File "/usr/lib64/python2.7/site-packages/twisted/conch/ssh/filetransfer.py",
line 53, in dataReceived
	  File "/usr/lib64/python2.7/site-packages/twisted/conch/ssh/filetransfer.py",
line 711, in packet_STATUS
	    msg, data = getNS(data)
	  File "/usr/lib64/python2.7/site-packages/twisted/conch/ssh/common.py",
line 36, in getNS
	    l, = struct.unpack('!L',s[c:c+4])
	struct.error: unpack requires a string argument of length 4

The problem seems to be that the remote SFTP implementation isn't
returning complete status response message - it doesn't include the
error message and the language identifier.  I made a quite ugly

diff --git a/twisted/conch/ssh/filetransfer.py b/twisted/conch/ssh/filetransfer.
index 81a86fd..ed55b27 100644
--- a/twisted/conch/ssh/filetransfer.py
+++ b/twisted/conch/ssh/filetransfer.py
@@ -708,8 +708,15 @@ class FileTransferClient(FileTransferBase):
         d, data = self._parseRequest(data)
         code, = struct.unpack('!L', data[:4])
         data = data[4:]
-        msg, data = getNS(data)
-        lang = getNS(data)
+        if len(data) >= 4:
+            msg, data = getNS(data)
+            if len(data) >= 4:
+                lang = getNS(data)
+            else:
+                lang = ''
+        else:
+            msg = ''
+            lang = ''
         if code == FX_OK:
             d.callback((msg, lang))
         elif code == FX_EOF:

Looking through the Paramiko code[1] it looks like it pads SFTP
messages that are shorter than expected with null bytes.  From what I
saw in the  SFTP I-D[2], a status message that doesn't include the
error message and language code could be construed as legal even
though they are not specifically marked as optional.

[1] https://github.com/robey/paramiko/blob/master/paramiko/message.py#L103
[2] http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-4

Jeff Ollie

More information about the Twisted-Python mailing list