[Twisted-Python] how to get extReceived call from SSHCommandClientEndpoint
Hello, I am implementing a program using SSHCommandClientEndpoint, the program works fine (dataReceived got calls) but when the server send an error message (stderr..), the function extReceived in my protocol never gets called, instead it only calls the one in the SSHChannel as I can see in the log (got extended data 1 b': No such file or directory\n'). How to override that? Or how to connect it to my protocol? I adapted the program found on Twisted website. Thanks, Sethy from twisted.conch.endpoints import SSHCommandClientEndpoint from twisted.internet.protocol import Factory, Protocol from twisted.python import log import sys from twisted.internet.defer import Deferred class NoiseProtocol(Protocol): def connectionMade(self): print("connectionMade") self.finished = Deferred() self.strings = ["bif", "pow", "zot"] self.sendNoise() def sendNoise(self): if self.strings: self.transport.write(self.strings.pop(0) + "\n") else: self.transport.loseConnection() def dataReceived(self, data): print("Server says:", data) self.sendNoise() def extReceived(self, dataType, data): print("extReceived") def connectionLost(self, reason): self.finished.callback(None) command = b"cat unknownfile" username = b"XXXX" password = b"XXXX" host = b"server" port = 22 endpoint = SSHCommandClientEndpoint.newConnection( reactor, command, username, host, port, password=password, agentEndpoint=None) factory = Factory() factory.protocol = NoiseProtocol d = endpoint.connect(factory) d.addCallback(lambda proto: proto.finished) log.startLogging(sys.stdout, setStdout=0) reactor.run()
It looks like extReceived() is a method of SSHSession.Channel.SSHChannel <https://twistedmatrix.com/documents/8.1.0/api/twisted.conch.ssh.session.SSHSession.html#extReceived>. You can access it through the protocol's transport. self.transport.conn.channels[0].extReceived() ~Sean On Fri, Apr 5, 2019 at 6:27 AM Sereysethy TOUCH <touch.sereysethy@gmail.com> wrote:
Hello,
I am implementing a program using SSHCommandClientEndpoint, the program works fine (dataReceived got calls) but when the server send an error message (stderr..), the function extReceived in my protocol never gets called, instead it only calls the one in the SSHChannel as I can see in the log (got extended data 1 b': No such file or directory\n'). How to override that? Or how to connect it to my protocol?
I adapted the program found on Twisted website.
Thanks, Sethy
from twisted.conch.endpoints import SSHCommandClientEndpoint from twisted.internet.protocol import Factory, Protocol from twisted.python import log import sys from twisted.internet.defer import Deferred
class NoiseProtocol(Protocol): def connectionMade(self): print("connectionMade") self.finished = Deferred() self.strings = ["bif", "pow", "zot"] self.sendNoise()
def sendNoise(self): if self.strings: self.transport.write(self.strings.pop(0) + "\n") else: self.transport.loseConnection()
def dataReceived(self, data): print("Server says:", data) self.sendNoise()
def extReceived(self, dataType, data): print("extReceived")
def connectionLost(self, reason): self.finished.callback(None)
command = b"cat unknownfile"
username = b"XXXX" password = b"XXXX" host = b"server" port = 22
endpoint = SSHCommandClientEndpoint.newConnection( reactor, command, username, host, port, password=password, agentEndpoint=None)
factory = Factory() factory.protocol = NoiseProtocol
d = endpoint.connect(factory) d.addCallback(lambda proto: proto.finished) log.startLogging(sys.stdout, setStdout=0) reactor.run() _______________________________________________ Twisted-Python mailing list Twisted-Python@twistedmatrix.com https://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python
Hi Sean, Yes it is a method of SSHChannel, but when I read the source code, the _CommandChannel only implements dataReceived which calls protocol's method but it does not implement extReceived. When you suggest to call it like that (self.transport.conn.channels[0].extReceived()), how do I know when extended data is received? For now I have subclassed _CommandChannel and also SSHCommandClientEndpoint in order to add extReceived. Sethy On Fri, Apr 5, 2019 at 6:35 PM Sean DiZazzo <sean.dizazzo@gmail.com> wrote:
It looks like extReceived() is a method of SSHSession.Channel.SSHChannel <https://twistedmatrix.com/documents/8.1.0/api/twisted.conch.ssh.session.SSHSession.html#extReceived>. You can access it through the protocol's transport.
self.transport.conn.channels[0].extReceived()
~Sean
On Fri, Apr 5, 2019 at 6:27 AM Sereysethy TOUCH < touch.sereysethy@gmail.com> wrote:
Hello,
I am implementing a program using SSHCommandClientEndpoint, the program works fine (dataReceived got calls) but when the server send an error message (stderr..), the function extReceived in my protocol never gets called, instead it only calls the one in the SSHChannel as I can see in the log (got extended data 1 b': No such file or directory\n'). How to override that? Or how to connect it to my protocol?
I adapted the program found on Twisted website.
Thanks, Sethy
from twisted.conch.endpoints import SSHCommandClientEndpoint from twisted.internet.protocol import Factory, Protocol from twisted.python import log import sys from twisted.internet.defer import Deferred
class NoiseProtocol(Protocol): def connectionMade(self): print("connectionMade") self.finished = Deferred() self.strings = ["bif", "pow", "zot"] self.sendNoise()
def sendNoise(self): if self.strings: self.transport.write(self.strings.pop(0) + "\n") else: self.transport.loseConnection()
def dataReceived(self, data): print("Server says:", data) self.sendNoise()
def extReceived(self, dataType, data): print("extReceived")
def connectionLost(self, reason): self.finished.callback(None)
command = b"cat unknownfile"
username = b"XXXX" password = b"XXXX" host = b"server" port = 22
endpoint = SSHCommandClientEndpoint.newConnection( reactor, command, username, host, port, password=password, agentEndpoint=None)
factory = Factory() factory.protocol = NoiseProtocol
d = endpoint.connect(factory) d.addCallback(lambda proto: proto.finished) log.startLogging(sys.stdout, setStdout=0) reactor.run() _______________________________________________ Twisted-Python mailing list Twisted-Python@twistedmatrix.com https://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python
_______________________________________________ Twisted-Python mailing list Twisted-Python@twistedmatrix.com https://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python
On Fri, Apr 5, 2019 at 2:08 PM Sereysethy TOUCH <touch.sereysethy@gmail.com> wrote:
Hi Sean,
Yes it is a method of SSHChannel, but when I read the source code, the _CommandChannel only implements dataReceived which calls protocol's method but it does not implement extReceived.
When you suggest to call it like that (self.transport.conn.channels[0].extReceived()), how do I know when extended data is received?
For now I have subclassed _CommandChannel and also SSHCommandClientEndpoint in order to add extReceived.
An interesting question might be ... what do you want to happen to such data? Do you want it mixed in with the stdout stream? For an IProtocol-based API, there aren't a lot of other options. Maybe it would be useful to have a similar API that works in terms of IProcessProtocol instead of IProtocol? There is a ProcessEndpoint that takes a flag that controls what it does with stderr - currently supporting only two options, log it or drop it. What did you hook extReceived up to in your subclass? Jean-Paul
Hi Jean-Paul, No I dont want it to be mixed with stdout stream, it should be separated from normal stdout, so that I know it is an error. To be honest, I am pretty to new to Twisted concept, there are things that I have not familiar with yet. Actually I can also implement a low level ssh client (which I already did), but as SSHCommandClientEndpoint is already available, so I want to use it straight away. But when I dived in the source code, the _CommandChannel only implements dataReceived but not extReceived. For your suggestion on using ProcessEndpoint how can I use it for my SSH client? In my extReceived I just deliver what I got to the extReceived of method protocol, it is exactly the same as Twisted implements dataReceived: class CommandChannel(_CommandChannel): def extReceived(self, dataType, data): """ When the command's extended data (usually standard error) arrives, deliver it to the protocol instance. @type dataType: L{int} @type data: L{str} """ self._protocol.extReceived(dataType, data) I have two other questions since you are here 1) how can we execute command like more or less which is an interactive command? 2) how to request a pty to ssh server? Thanks, Sethy On Fri, Apr 5, 2019 at 8:13 PM Jean-Paul Calderone < exarkun@twistedmatrix.com> wrote:
On Fri, Apr 5, 2019 at 2:08 PM Sereysethy TOUCH < touch.sereysethy@gmail.com> wrote:
Hi Sean,
Yes it is a method of SSHChannel, but when I read the source code, the _CommandChannel only implements dataReceived which calls protocol's method but it does not implement extReceived.
When you suggest to call it like that (self.transport.conn.channels[0].extReceived()), how do I know when extended data is received?
For now I have subclassed _CommandChannel and also SSHCommandClientEndpoint in order to add extReceived.
An interesting question might be ... what do you want to happen to such data? Do you want it mixed in with the stdout stream?
For an IProtocol-based API, there aren't a lot of other options. Maybe it would be useful to have a similar API that works in terms of IProcessProtocol instead of IProtocol?
There is a ProcessEndpoint that takes a flag that controls what it does with stderr - currently supporting only two options, log it or drop it.
What did you hook extReceived up to in your subclass?
Jean-Paul _______________________________________________ Twisted-Python mailing list Twisted-Python@twistedmatrix.com https://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python
On Apr 6, 2019, at 10:12 AM, Sereysethy TOUCH <touch.sereysethy@gmail.com> wrote:
Hi Jean-Paul,
No I dont want it to be mixed with stdout stream, it should be separated from normal stdout, so that I know it is an error.
To be honest, I am pretty to new to Twisted concept, there are things that I have not familiar with yet. Actually I can also implement a low level ssh client (which I already did), but as SSHCommandClientEndpoint is already available, so I want to use it straight away. But when I dived in the source code, the _CommandChannel only implements dataReceived but not extReceived.
The main point of `SSHCommandClientEndpoint` is to allow code that doesn't know about SSH to run over SSH, for example to enable forwarding. So you can't unconditionally require everyone to implement extReceived, because IProtocol doesn't require this.
For your suggestion on using ProcessEndpoint how can I use it for my SSH client?
Jean-Paul was suggesting a way that Twisted could be modified to support your use-case, not a thing that you can do today in your own code.
In my extReceived I just deliver what I got to the extReceived of method protocol, it is exactly the same as Twisted implements dataReceived:
class CommandChannel(_CommandChannel): def extReceived(self, dataType, data): """ When the command's extended data (usually standard error) arrives, deliver it to the protocol instance.
@type dataType: L{int} @type data: L{str} """ self._protocol.extReceived(dataType, data)
If you wanted to contribute this functionality to Twisted, the usual idiom here would be to do a check, like: protocol = self._protocol if ISSHExtendedProtocol.providedBy(protocol): protocol.extReceived(dataType, data) Doing this on every extReceived may be a little bit of a performance hit, so caching this computation and/or the behavior associated with it is left as an exercise for you :).
I have two other questions since you are here
1) how can we execute command like more or less which is an interactive command?
Looking at what the 'conch' script does, I think what you're asking is something like this: self.conn.sendRequest(self, b'exec', common.NS(options['command']))
2) how to request a pty to ssh server?
Again from twisted/conch/scripts/conch.py: ptyReqData = session.packRequest_pty_req(term, winSize, '') self.conn.sendRequest(self, b'pty-req', ptyReqData)
Thanks, Sethy
Thanks for using Twisted, -g
participants (4)
-
Glyph
-
Jean-Paul Calderone
-
Sean DiZazzo
-
Sereysethy TOUCH