首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用twisted.conch作为客户端通过ssh接收扩展数据

使用twisted.conch作为客户端通过ssh接收扩展数据
EN

Stack Overflow用户
提问于 2011-10-29 15:50:22
回答 1查看 1.6K关注 0票数 3

我目前正在学习ssh的过程中,通过蛮力/只要继续黑客,直到我理解它的方法。经过反复试验,我成功地发送了一个"pty-req“,然后是一个"shell”请求,我可以获得登录前导,发送命令和接收stdout,但我不确定如何告诉SSH服务我想要接收stderr和状态消息。目前,阅读其他SSH实现( paramiko、Net::SSH )并不是一个很好的指南。

也就是说,看一下SSH的RFC之一,我相信列出的请求之一可能就是我正在寻找的:https://www.rfc-editor.org/rfc/rfc4250#section-4.9.3

代码语言:javascript
复制
#!/usr/bin/env python


from twisted.conch.ssh import transport
from twisted.conch.ssh import userauth
from twisted.conch.ssh import connection
from twisted.conch.ssh import common
from twisted.conch.ssh.common import NS
from twisted.conch.ssh import keys
from twisted.conch.ssh import channel
from twisted.conch.ssh import session
from twisted.internet import defer

from twisted.internet import defer, protocol, reactor
from twisted.python import log
import struct, sys, getpass, os
log.startLogging(sys.stdout)


USER = 'dward'  
HOST = '192.168.0.19' # pristine.local
PASSWD = "password"
PRIVATE_KEY = "~/id_rsa"

class SimpleTransport(transport.SSHClientTransport):
    def verifyHostKey(self, hostKey, fingerprint):
        print 'host key fingerprint: %s' % fingerprint
        return defer.succeed(1) 

    def connectionSecure(self):
        self.requestService(
            SimpleUserAuth(USER,
                SimpleConnection()))

class SimpleUserAuth(userauth.SSHUserAuthClient):
    def getPassword(self):
        return defer.succeed(PASSWD)

    def getGenericAnswers(self, name, instruction, questions):
        print name
        print instruction
        answers = []
        for prompt, echo in questions:
            if echo:
                answer = raw_input(prompt)
            else:
                answer = getpass.getpass(prompt)
            answers.append(answer)
        return defer.succeed(answers)
            
    def getPublicKey(self):
        path = os.path.expanduser(PRIVATE_KEY) 
        # this works with rsa too
        # just change the name here and in getPrivateKey
        if not os.path.exists(path) or self.lastPublicKey:
            # the file doesn't exist, or we've tried a public key
            return
        return keys.Key.fromFile(filename=path+'.pub').blob()

    def getPrivateKey(self):
        path = os.path.expanduser(PRIVATE_KEY)
        return defer.succeed(keys.Key.fromFile(path).keyObject)
        
    
    
class SimpleConnection(connection.SSHConnection):
    def serviceStarted(self):
        self.openChannel(SmartChannel(2**16, 2**15, self))        




class SmartChannel(channel.SSHChannel):
    name = "session"
    
    
    def getResponse(self, timeout = 10):
        self.onData = defer.Deferred()
        self.timeout = reactor.callLater( timeout, self.onData.errback, Exception("Timeout") )
        return self.onData
    
    def openFailed(self, reason):
        print "Failed", reason
        
    @defer.inlineCallbacks    
    def channelOpen(self, ignoredData):
        self.data = ''
        self.oldData = ''
        self.onData = None
        self.timeout = None
        term = os.environ.get('TERM', 'xterm')
        #winsz = fcntl.ioctl(fd, tty.TIOCGWINSZ, '12345678')
        winSize = (25,80,0,0) #struct.unpack('4H', winsz)
        ptyReqData = session.packRequest_pty_req(term, winSize, '')
        
        try:
            result = yield self.conn.sendRequest(self, 'pty-req', ptyReqData, wantReply = 1 )
        except Exception as e:
            print "Failed with ", e
        
        try:
            result = yield self.conn.sendRequest(self, "shell", '', wantReply = 1)
        except Exception as e:
            print "Failed shell with ", e
        
        
        #fetch preample    
        data = yield self.getResponse()
        """
        Welcome to Ubuntu 11.04 (GNU/Linux 2.6.38-8-server x86_64)

            * Documentation:  http://www.ubuntu.com/server/doc
           
             System information as of Sat Oct 29 13:09:50 MDT 2011
           
             System load:  0.0               Processes:           111
             Usage of /:   48.0% of 6.62GB   Users logged in:     1
             Memory usage: 39%               IP address for eth1: 192.168.0.19
             Swap usage:   3%
           
             Graph this data and manage this system at https://landscape.canonical.com/
           New release 'oneiric' available.
           Run 'do-release-upgrade' to upgrade to it.
           
           Last login: Sat Oct 29 01:23:16 2011 from 192.168.0.17
        """
        print data
        while data != "" and data.strip().endswith("~$") == False:
            try:
                data = yield self.getResponse()
                print repr(data)
                """
                \x1B]0;dward@pristine: ~\x07dward@pristine:~$ 
                """
            except Exception as e:
                print e
                break
                
        self.write("false\n")
        #fetch response
        try:
            data = yield self.getResponse()
        except Exception as e:
            print "Failed to catch response?", e
        else:
            print data
            """
                false
                \x1B]0;dward@pristine: ~\x07dward@pristine:~$ 
            """
            
        self.write("true\n")
        #fetch response
        try:
            data = yield self.getResponse()
        except Exception as e:
            print "Failed to catch response?", e
        else:
            print data
            """
            true
            \x1B]0;dward@pristine: ~\x07dward@pristine:~$ 
            """
        
        self.write("echo Hello World\n\x00")
        try:
            data = yield self.getResponse()
        except Exception as e:
            print "Failed to catch response?", e
        else:            
            print data
            """
            echo Hello World
            Hello World
            \x1B]0;dward@pristine: ~\x07dward@pristine:~$ 
            """
        
        #Close up shop
        self.loseConnection()
        dbgp = 1
        
    
    def request_exit_status(self, data):
        status = struct.unpack('>L', data)[0]
        print 'status was: %s' % status    
    
    def dataReceived(self, data):
        self.data += data
        if self.onData is not None:
            if self.timeout and self.timeout.active():
                self.timeout.cancel()
            if self.onData.called == False:                
                self.onData.callback(data)
    
    def extReceived(self, dataType, data):
        dbgp = 1
        print "Extended Data recieved! dataType = %s , data = %s " % ( dataType, data, )
        self.extendData = data

    def closed(self):
        print 'got data : %s' % self.data.replace("\\r\\n","\r\n")
        self.loseConnection()
        reactor.stop()
        
    

protocol.ClientCreator(reactor, SimpleTransport).connectTCP(HOST, 22)
reactor.run()

另外,我尝试在远程shell中添加一个显式的坏命令:

代码语言:javascript
复制
    self.write("ls -alF badPathHere\n\x00")
    try:
        data = yield self.getResponse()
    except Exception as e:
        print "Failed to catch response?", e
    else:            
        print data
        """
        ls -alF badPathHere
        ls: cannot access badPathHere: No such file or directory
        \x1B]0;dward@pristine: ~\x07dward@pristine:~$ 
        """

而且看起来stderr正在被混合到stderr中。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2011-10-30 08:15:18

深入研究OpenSSH的源代码,通道会话逻辑在session.c中的第227行function -> session_input_channel_req中处理,如果给出一个pty-req,那么"shell“请求将导致do_exec_pty,最终导致对session_set_fds(s,ptyfd,fdout,-1,1,1)的调用。第四个参数通常是一个负责处理stderr的文件描述符,但是因为没有提供,所以不会有任何stderr的扩展数据。

最终,即使我修改了openssh以提供stderr FD,问题仍然存在于shell中。现在完成了猜测工作,但我相信类似于通过xterm或putty这样的终端登录ssh服务,stderr和stdout是一起发送的,除非通过"2> someFile“这样的东西显式重定向,这超出了SSH服务提供商的范围。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7937651

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档