2012-12-17 5 views
5

Twisted conch SSH 연결을 닫는 올바른 방법은 무엇입니까? 이 작업을 수행 할 수있는 명시적인 방법이 있습니까?Twisted conch SSH 연결을 닫는 올바른 방법은 무엇입니까?

모든 트위스트 조가비의 예에서 SSH 채널을 닫은 다음 원자로를 멈추게했습니다. 원자로가 종료되면 연결이 닫히는 것 같습니다. 그러나 wxPython과 함께 wxreactor를 사용하고 있는데 반응기를 멈추고 싶지는 않지만 끝내면 ssh 연결을 닫고 싶습니다.

tc.c.s.connection을보고 나서 serviceStopped() 메소드가 이동하는 것처럼 보였습니다. 채널이 닫힌 후 나는 아직도 서버에서 데이터를 가져 오는 것 같은

Unhandled Error 
Traceback (most recent call last): 
    File "C:\Users\me\venv\lib\site-packages\twisted\internet\tcp.py", line 203, in doRead 
    return self._dataReceived(data) 
    File "C:\Users\me\venv\lib\site-packages\twisted\internet\tcp.py", line 209, in _dataReceived 
    rval = self.protocol.dataReceived(data) 
    File "C:\Users\me\venv\lib\site-packages\twisted\conch\ssh\transport.py", line 438, in dataReceived 
    self.dispatchMessage(messageNum, packet[1:]) 
    File "C:\Users\me\venv\lib\site-packages\twisted\conch\ssh\transport.py", line 460, in dispatchMessage 
    messageNum, payload) 
--- <exception caught here> --- 
    File "C:\Users\me\venv\lib\site-packages\twisted\python\log.py", line 84, in callWithLogger 
    return callWithContext({"system": lp}, func, *args, **kw) 
    File "C:\Users\me\venv\lib\site-packages\twisted\python\log.py", line 69, in callWithContext 
    return context.call({ILogContext: newCtx}, func, *args, **kw) 
    File "C:\Users\me\venv\lib\site-packages\twisted\python\context.py", line 118, in callWithContext 
    return self.currentContext().callWithContext(ctx, func, *args, **kw) 
    File "C:\Users\me\venv\lib\site-packages\twisted\python\context.py", line 81, in callWithContext 
    return func(*args,**kw) 
    File "C:\Users\me\venv\lib\site-packages\twisted\conch\ssh\service.py", line 44, in packetReceived 
    return f(packet) 
    File "C:\Users\me\venv\lib\site-packages\twisted\conch\ssh\connection.py", line 228, in ssh_CHANNEL_DATA 
    channel = self.channels[localChannel] 
exceptions.KeyError: 0 

가 보이는 : 그것은() 완료,하지만 나는 아래와 같은 예외를 얻기 시작했을 때 _cleanupGlobalDeferreds을 열려있는 모든 채널을 닫고 실행합니다. #twisted의 누군가는 twisted의 다른 부분에서 자동으로 호출해야하기 때문에 serviceStopped()를 직접 호출해서는 안된다고 생각하는 것 같습니다.

나는 Twisted 소스 코드에서 약간의 파고를 들었고 serviceStopped가 t.c.s.t.SSHClientTransport.connectionLost()에 의해 호출되어 졌음을 알았다.

SFTP 클라이언트 개체를 추적하고 해당 전송 특성을 통해 SSH 연결에 액세스하고 있습니다. 다음은 문제를 설명하기 위해 로컬로 실행할 수있는 예입니다. 원시는 here을 가져올 수 있습니다.

from os.path import basename 
import sys 

from twisted.conch.client.connect import connect 
from twisted.conch.client.options import ConchOptions 
from twisted.internet.defer import Deferred 
from twisted.conch.ssh import channel, userauth 
from twisted.conch.ssh.common import NS 
from twisted.conch.ssh.connection import SSHConnection 
from twisted.conch.ssh.filetransfer import FXF_WRITE, FXF_CREAT, \ 
    FXF_TRUNC, FileTransferClient 
from twisted.internet import reactor, defer 
from twisted.python.log import startLogging 

ACTIVE_CLIENTS = {} 
USERNAME = 'user'   # change me! 
PASSWORD = 'password'  # change me! 
HOST = ('hostname', 22)  # change me! 
TEST_FILE_PATH = __file__ 
TEST_FILE_NAME = basename(__file__) 


def openSFTP(user, host): 
    conn = SFTPConnection() 
    options = ConchOptions() 
    options['host'], options['port'] = host 
    conn._sftp = Deferred() 
    auth = SimpleUserAuth(user, conn) 
    connect(options['host'], options['port'], options, verifyHostKey, auth) 
    return conn._sftp 


def verifyHostKey(ui, hostname, ip, key): 
    return defer.succeed(True) 


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


class SFTPConnection(SSHConnection): 
    def serviceStarted(self): 
     self.openChannel(SFTPChannel()) 


class SFTPChannel(channel.SSHChannel): 
    name = 'session' 

    def channelOpen(self, ignoredData): 
     d = self.conn.sendRequest(self, 'subsystem', NS('sftp'), 
            wantReply=True) 
     d.addCallback(self._cbFTP) 
     d.addErrback(self.printErr) 

    def _cbFTP(self, ignore): 
     client = FileTransferClient() 
     client.makeConnection(self) 
     self.dataReceived = client.dataReceived 
     ACTIVE_CLIENTS.update({self.conn.transport.transport.addr: client}) 
     self.conn._sftp.callback(None) 

    def printErr(self, msg): 
     print msg 
     return msg 


@defer.inlineCallbacks 
def main(): 
    d = openSFTP(USERNAME, HOST) 
    _ = yield d 

    client = ACTIVE_CLIENTS[HOST] 
    d = client.openFile(TEST_FILE_NAME, FXF_WRITE | FXF_CREAT | FXF_TRUNC, {}) 
    df = yield d 

    sf = open(TEST_FILE_PATH, 'rb') 
    d = df.writeChunk(0, sf.read()) 
    _ = yield d 

    sf.close() 
    d = df.close() 
    _ = yield d 

    ACTIVE_CLIENTS[HOST].transport.loseConnection() 
    # loseConnection() call above causes the following log messages: 
    # [SSHChannel session (0) on SSHService ssh-connection on SSHClientTransport,client] sending close 0 
    # [SSHChannel session (0) on SSHService ssh-connection on SSHClientTransport,client] unhandled request for exit-status 
    # [SSHChannel session (0) on SSHService ssh-connection on SSHClientTransport,client] remote close 
    # [SSHChannel session (0) on SSHService ssh-connection on SSHClientTransport,client] closed 
    # I can see the channel closed on the server side: 
    # sshd[4485]: debug1: session_exit_message: session 0 channel 0 pid 4486 
    # sshd[4485]: debug1: session_exit_message: release channel 0 
    # sshd[4485]: debug1: session_by_channel: session 0 channel 0 

    ACTIVE_CLIENTS[HOST].transport.conn.transport.loseConnection() 
    # loseConnection() call above does not close the SSH connection. 

    reactor.callLater(5, reactor.stop) 
    # Stopping the reactor closes the SSH connection and logs the following messages: 
    # [SSHClientTransport,client] connection lost 
    # [SSHClientTransport,client] Stopping factory <twisted.conch.client.direct.SSHClientFactory instance at 0x02E5AF30> 
    # [-] Main loop terminated. 
    # On the server side: 
    # sshd[4485]: Closing connection to xxx.xxx.xxx.xxx 


if __name__ == '__main__': 
    startLogging(sys.stdout) 
    reactor.callWhenRunning(main) 
    reactor.run() 

내가 t.c.c.d.SSHClientTransport.sendDisconnect()를 호출 ACTIVE_CLIENTS[HOST].transport.conn.transport(t.c.c.d.SSHClientTransport instance).loseConnection()를 호출하고있어, SSH를 연결을 닫습니다. 여기에 sendDisconnect() 메소드는 다음과 같습니다

def sendDisconnect(self, code, reason): 
    if self.factory.d is None: 
     return 
    d, self.factory.d = self.factory.d, None 
    transport.SSHClientTransport.sendDisconnect(self, code, reason) 
    d.errback(error.ConchError(reason, code)) 

self.factory.d 그것을() t.c.s.t.SSHClientTransport.sendDisconnect를 호출하지 않고 반환 있도록이 메서드를 호출 할 때 항상 없음을 없을 것으로 보인다. 원래 t.c.c.d.connect에서 지연된 집합이라고 생각하지만 어떤 시점에서는 None으로 설정됩니다.

SSH 연결을 닫는 올바른 방법은 SSHClientTransport.loseConnection()이라고 생각합니다. 그러나 꼬인 줄이 그것이 뭔가 다른 것으로 예상 될 때 self.factory.d가 None으로 설정된 이유는 무엇입니까?

loseConnection()이 올바른 방법으로 SSH 연결을 닫을 수 없다면 누군가 올바른 방향으로 나를 가리킬 수 있습니까?

답변

4

twisted.conch.client.direct.SSHClientFactorytwisted.conch.client.direct.SSHClientTransport처럼 사용하는 것 같습니다. 이 클래스는 conch 명령 줄 도구를 구현하는 데 가장 직접 사용하기위한 클래스입니다. 이것은 그들이 정확히 conch 인 SSH 클라이언트를 구축하는 데 상당히 유용하다는 것을 의미합니다.

그러나 이것들은 또한 conch 명령 줄 도구를 구현하는 것보다 "기타"를 수행하는 데 많은주의를 기울이지 않기 때문에 일반적으로 유용하지는 않습니다.

보다 일반적으로 적용 가능한 SSH 클라이언트 전송 클래스는 twisted.conch.ssh.transport.SSHClientTransport입니다. 이 클래스에는 conch 명령 줄 도구의 특정 동작을 구현하기위한 추가 논리가 없습니다. 그것은 단지 SSH 클라이언트 로직을 가지고 있습니다. 예를 들어 설명 할 수없는 self.factory.d 내부에 sendDisconnect 체크가 없습니다. - sendDisconnect 구현은 연결 해제 패킷을 보내고 연결을 닫습니다.

1

동일한 문제가 발생합니다. 내가 sendDisconnect() 부모 구현을 호출하지 않는 그 버그를 확신 해요. SSHClientTransportloseConnection()을 호출해도 나를위한 TCP 연결이 닫히지 않습니다. 이는 lsof -p PID을 사용하여 볼 수 있습니다. 이 문제를 해결하기 위해 내 자신의 메서드를 사용하여 SSHClientTransport을 구현했습니다. 문제는 다음 코드로 해결됩니다.

class SSHClientTransport(direct.SSHClientTransport): 
    ''' 
    Orignal sendDisconnect() is bugged. 
    ''' 

    def sendDisconnect(self, code, reason): 
     d, self.factory.d = self.factory.d, None 
     # call the sendDisconnect() on the base SSHTransport, 
     # not the imediate parent class 
     transport.SSHClientTransport.sendDisconnect(self, code, reason) 
     if d: 
      d.errback(error.ConchError(reason, code)) 
관련 문제