Ausgabe der neuen DB Einträge
This commit is contained in:
parent
bad48e1627
commit
cfbbb9ee3d
2399 changed files with 843193 additions and 43 deletions
|
|
@ -0,0 +1 @@
|
|||
'conch scripts'
|
||||
949
venv/lib/python3.9/site-packages/twisted/conch/scripts/cftp.py
Normal file
949
venv/lib/python3.9/site-packages/twisted/conch/scripts/cftp.py
Normal file
|
|
@ -0,0 +1,949 @@
|
|||
# -*- test-case-name: twisted.conch.test.test_cftp -*-
|
||||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
Implementation module for the I{cftp} command.
|
||||
"""
|
||||
from __future__ import division, print_function
|
||||
import os, sys, getpass, struct, tty, fcntl, stat
|
||||
import fnmatch, pwd, glob
|
||||
|
||||
from twisted.conch.client import connect, default, options
|
||||
from twisted.conch.ssh import connection, common
|
||||
from twisted.conch.ssh import channel, filetransfer
|
||||
from twisted.protocols import basic
|
||||
from twisted.python.compat import _PY3, unicode
|
||||
from twisted.internet import reactor, stdio, defer, utils
|
||||
from twisted.python import log, usage, failure
|
||||
from twisted.python.filepath import FilePath
|
||||
|
||||
class ClientOptions(options.ConchOptions):
|
||||
|
||||
synopsis = """Usage: cftp [options] [user@]host
|
||||
cftp [options] [user@]host[:dir[/]]
|
||||
cftp [options] [user@]host[:file [localfile]]
|
||||
"""
|
||||
longdesc = ("cftp is a client for logging into a remote machine and "
|
||||
"executing commands to send and receive file information")
|
||||
|
||||
optParameters = [
|
||||
['buffersize', 'B', 32768, 'Size of the buffer to use for sending/receiving.'],
|
||||
['batchfile', 'b', None, 'File to read commands from, or \'-\' for stdin.'],
|
||||
['requests', 'R', 5, 'Number of requests to make before waiting for a reply.'],
|
||||
['subsystem', 's', 'sftp', 'Subsystem/server program to connect to.']]
|
||||
|
||||
compData = usage.Completions(
|
||||
descriptions={
|
||||
"buffersize": "Size of send/receive buffer (default: 32768)"},
|
||||
extraActions=[usage.CompleteUserAtHost(),
|
||||
usage.CompleteFiles(descr="local file")])
|
||||
|
||||
def parseArgs(self, host, localPath=None):
|
||||
self['remotePath'] = ''
|
||||
if ':' in host:
|
||||
host, self['remotePath'] = host.split(':', 1)
|
||||
self['remotePath'].rstrip('/')
|
||||
self['host'] = host
|
||||
self['localPath'] = localPath
|
||||
|
||||
def run():
|
||||
# import hotshot
|
||||
# prof = hotshot.Profile('cftp.prof')
|
||||
# prof.start()
|
||||
args = sys.argv[1:]
|
||||
if '-l' in args: # cvs is an idiot
|
||||
i = args.index('-l')
|
||||
args = args[i:i+2]+args
|
||||
del args[i+2:i+4]
|
||||
options = ClientOptions()
|
||||
try:
|
||||
options.parseOptions(args)
|
||||
except usage.UsageError as u:
|
||||
print('ERROR: %s' % u)
|
||||
sys.exit(1)
|
||||
if options['log']:
|
||||
realout = sys.stdout
|
||||
log.startLogging(sys.stderr)
|
||||
sys.stdout = realout
|
||||
else:
|
||||
log.discardLogs()
|
||||
doConnect(options)
|
||||
reactor.run()
|
||||
# prof.stop()
|
||||
# prof.close()
|
||||
|
||||
def handleError():
|
||||
global exitStatus
|
||||
exitStatus = 2
|
||||
try:
|
||||
reactor.stop()
|
||||
except: pass
|
||||
log.err(failure.Failure())
|
||||
raise
|
||||
|
||||
def doConnect(options):
|
||||
# log.deferr = handleError # HACK
|
||||
if '@' in options['host']:
|
||||
options['user'], options['host'] = options['host'].split('@',1)
|
||||
host = options['host']
|
||||
if not options['user']:
|
||||
options['user'] = getpass.getuser()
|
||||
if not options['port']:
|
||||
options['port'] = 22
|
||||
else:
|
||||
options['port'] = int(options['port'])
|
||||
host = options['host']
|
||||
port = options['port']
|
||||
conn = SSHConnection()
|
||||
conn.options = options
|
||||
vhk = default.verifyHostKey
|
||||
uao = default.SSHUserAuthClient(options['user'], options, conn)
|
||||
connect.connect(host, port, options, vhk, uao).addErrback(_ebExit)
|
||||
|
||||
def _ebExit(f):
|
||||
#global exitStatus
|
||||
if hasattr(f.value, 'value'):
|
||||
s = f.value.value
|
||||
else:
|
||||
s = str(f)
|
||||
print(s)
|
||||
#exitStatus = "conch: exiting with error %s" % f
|
||||
try:
|
||||
reactor.stop()
|
||||
except: pass
|
||||
|
||||
def _ignore(*args): pass
|
||||
|
||||
class FileWrapper:
|
||||
|
||||
def __init__(self, f):
|
||||
self.f = f
|
||||
self.total = 0.0
|
||||
f.seek(0, 2) # seek to the end
|
||||
self.size = f.tell()
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.f, attr)
|
||||
|
||||
class StdioClient(basic.LineReceiver):
|
||||
|
||||
_pwd = pwd
|
||||
|
||||
ps = 'cftp> '
|
||||
delimiter = b'\n'
|
||||
|
||||
reactor = reactor
|
||||
|
||||
def __init__(self, client, f = None):
|
||||
self.client = client
|
||||
self.currentDirectory = ''
|
||||
self.file = f
|
||||
self.useProgressBar = (not f and 1) or 0
|
||||
|
||||
def connectionMade(self):
|
||||
self.client.realPath('').addCallback(self._cbSetCurDir)
|
||||
|
||||
def _cbSetCurDir(self, path):
|
||||
self.currentDirectory = path
|
||||
self._newLine()
|
||||
|
||||
def _writeToTransport(self, msg):
|
||||
if isinstance(msg, unicode):
|
||||
msg = msg.encode("utf-8")
|
||||
return self.transport.write(msg)
|
||||
|
||||
def lineReceived(self, line):
|
||||
if self.client.transport.localClosed:
|
||||
return
|
||||
if _PY3 and isinstance(line, bytes):
|
||||
line = line.decode("utf-8")
|
||||
log.msg('got line %s' % line)
|
||||
line = line.lstrip()
|
||||
if not line:
|
||||
self._newLine()
|
||||
return
|
||||
if self.file and line.startswith('-'):
|
||||
self.ignoreErrors = 1
|
||||
line = line[1:]
|
||||
else:
|
||||
self.ignoreErrors = 0
|
||||
d = self._dispatchCommand(line)
|
||||
if d is not None:
|
||||
d.addCallback(self._cbCommand)
|
||||
d.addErrback(self._ebCommand)
|
||||
|
||||
|
||||
def _dispatchCommand(self, line):
|
||||
if ' ' in line:
|
||||
command, rest = line.split(' ', 1)
|
||||
rest = rest.lstrip()
|
||||
else:
|
||||
command, rest = line, ''
|
||||
if command.startswith('!'): # command
|
||||
f = self.cmd_EXEC
|
||||
rest = (command[1:] + ' ' + rest).strip()
|
||||
else:
|
||||
command = command.upper()
|
||||
log.msg('looking up cmd %s' % command)
|
||||
f = getattr(self, 'cmd_%s' % command, None)
|
||||
if f is not None:
|
||||
return defer.maybeDeferred(f, rest)
|
||||
else:
|
||||
errMsg = "No command called `%s'" % (command)
|
||||
self._ebCommand(failure.Failure(NotImplementedError(errMsg)))
|
||||
self._newLine()
|
||||
|
||||
def _printFailure(self, f):
|
||||
log.msg(f)
|
||||
e = f.trap(NotImplementedError, filetransfer.SFTPError, OSError, IOError)
|
||||
if e == NotImplementedError:
|
||||
self._writeToTransport(self.cmd_HELP(''))
|
||||
elif e == filetransfer.SFTPError:
|
||||
errMsg = "remote error %i: %s\n" % (f.value.code, f.value.message)
|
||||
self._writeToTransport(errMsg)
|
||||
elif e in (OSError, IOError):
|
||||
errMsg = "local error %i: %s\n" % (f.value.errno, f.value.strerror)
|
||||
self._writeToTransport(errMsg)
|
||||
|
||||
def _newLine(self):
|
||||
if self.client.transport.localClosed:
|
||||
return
|
||||
self._writeToTransport(self.ps)
|
||||
self.ignoreErrors = 0
|
||||
if self.file:
|
||||
l = self.file.readline()
|
||||
if not l:
|
||||
self.client.transport.loseConnection()
|
||||
else:
|
||||
self._writeToTransport(l)
|
||||
self.lineReceived(l.strip())
|
||||
|
||||
def _cbCommand(self, result):
|
||||
if result is not None:
|
||||
if isinstance(result, unicode):
|
||||
result = result.encode("utf-8")
|
||||
self._writeToTransport(result)
|
||||
if not result.endswith(b'\n'):
|
||||
self._writeToTransport(b'\n')
|
||||
self._newLine()
|
||||
|
||||
def _ebCommand(self, f):
|
||||
self._printFailure(f)
|
||||
if self.file and not self.ignoreErrors:
|
||||
self.client.transport.loseConnection()
|
||||
self._newLine()
|
||||
|
||||
def cmd_CD(self, path):
|
||||
path, rest = self._getFilename(path)
|
||||
if not path.endswith('/'):
|
||||
path += '/'
|
||||
newPath = path and os.path.join(self.currentDirectory, path) or ''
|
||||
d = self.client.openDirectory(newPath)
|
||||
d.addCallback(self._cbCd)
|
||||
d.addErrback(self._ebCommand)
|
||||
return d
|
||||
|
||||
def _cbCd(self, directory):
|
||||
directory.close()
|
||||
d = self.client.realPath(directory.name)
|
||||
d.addCallback(self._cbCurDir)
|
||||
return d
|
||||
|
||||
def _cbCurDir(self, path):
|
||||
self.currentDirectory = path
|
||||
|
||||
def cmd_CHGRP(self, rest):
|
||||
grp, rest = rest.split(None, 1)
|
||||
path, rest = self._getFilename(rest)
|
||||
grp = int(grp)
|
||||
d = self.client.getAttrs(path)
|
||||
d.addCallback(self._cbSetUsrGrp, path, grp=grp)
|
||||
return d
|
||||
|
||||
def cmd_CHMOD(self, rest):
|
||||
mod, rest = rest.split(None, 1)
|
||||
path, rest = self._getFilename(rest)
|
||||
mod = int(mod, 8)
|
||||
d = self.client.setAttrs(path, {'permissions':mod})
|
||||
d.addCallback(_ignore)
|
||||
return d
|
||||
|
||||
def cmd_CHOWN(self, rest):
|
||||
usr, rest = rest.split(None, 1)
|
||||
path, rest = self._getFilename(rest)
|
||||
usr = int(usr)
|
||||
d = self.client.getAttrs(path)
|
||||
d.addCallback(self._cbSetUsrGrp, path, usr=usr)
|
||||
return d
|
||||
|
||||
def _cbSetUsrGrp(self, attrs, path, usr=None, grp=None):
|
||||
new = {}
|
||||
new['uid'] = (usr is not None) and usr or attrs['uid']
|
||||
new['gid'] = (grp is not None) and grp or attrs['gid']
|
||||
d = self.client.setAttrs(path, new)
|
||||
d.addCallback(_ignore)
|
||||
return d
|
||||
|
||||
def cmd_GET(self, rest):
|
||||
remote, rest = self._getFilename(rest)
|
||||
if '*' in remote or '?' in remote: # wildcard
|
||||
if rest:
|
||||
local, rest = self._getFilename(rest)
|
||||
if not os.path.isdir(local):
|
||||
return "Wildcard get with non-directory target."
|
||||
else:
|
||||
local = b''
|
||||
d = self._remoteGlob(remote)
|
||||
d.addCallback(self._cbGetMultiple, local)
|
||||
return d
|
||||
if rest:
|
||||
local, rest = self._getFilename(rest)
|
||||
else:
|
||||
local = os.path.split(remote)[1]
|
||||
log.msg((remote, local))
|
||||
lf = open(local, 'wb', 0)
|
||||
path = FilePath(self.currentDirectory).child(remote)
|
||||
d = self.client.openFile(path.path, filetransfer.FXF_READ, {})
|
||||
d.addCallback(self._cbGetOpenFile, lf)
|
||||
d.addErrback(self._ebCloseLf, lf)
|
||||
return d
|
||||
|
||||
def _cbGetMultiple(self, files, local):
|
||||
#if self._useProgressBar: # one at a time
|
||||
# XXX this can be optimized for times w/o progress bar
|
||||
return self._cbGetMultipleNext(None, files, local)
|
||||
|
||||
def _cbGetMultipleNext(self, res, files, local):
|
||||
if isinstance(res, failure.Failure):
|
||||
self._printFailure(res)
|
||||
elif res:
|
||||
self._writeToTransport(res)
|
||||
if not res.endswith('\n'):
|
||||
self._writeToTransport('\n')
|
||||
if not files:
|
||||
return
|
||||
f = files.pop(0)[0]
|
||||
lf = open(os.path.join(local, os.path.split(f)[1]), 'wb', 0)
|
||||
path = FilePath(self.currentDirectory).child(f)
|
||||
d = self.client.openFile(path.path, filetransfer.FXF_READ, {})
|
||||
d.addCallback(self._cbGetOpenFile, lf)
|
||||
d.addErrback(self._ebCloseLf, lf)
|
||||
d.addBoth(self._cbGetMultipleNext, files, local)
|
||||
return d
|
||||
|
||||
def _ebCloseLf(self, f, lf):
|
||||
lf.close()
|
||||
return f
|
||||
|
||||
def _cbGetOpenFile(self, rf, lf):
|
||||
return rf.getAttrs().addCallback(self._cbGetFileSize, rf, lf)
|
||||
|
||||
def _cbGetFileSize(self, attrs, rf, lf):
|
||||
if not stat.S_ISREG(attrs['permissions']):
|
||||
rf.close()
|
||||
lf.close()
|
||||
return "Can't get non-regular file: %s" % rf.name
|
||||
rf.size = attrs['size']
|
||||
bufferSize = self.client.transport.conn.options['buffersize']
|
||||
numRequests = self.client.transport.conn.options['requests']
|
||||
rf.total = 0.0
|
||||
dList = []
|
||||
chunks = []
|
||||
startTime = self.reactor.seconds()
|
||||
for i in range(numRequests):
|
||||
d = self._cbGetRead('', rf, lf, chunks, 0, bufferSize, startTime)
|
||||
dList.append(d)
|
||||
dl = defer.DeferredList(dList, fireOnOneErrback=1)
|
||||
dl.addCallback(self._cbGetDone, rf, lf)
|
||||
return dl
|
||||
|
||||
def _getNextChunk(self, chunks):
|
||||
end = 0
|
||||
for chunk in chunks:
|
||||
if end == 'eof':
|
||||
return # nothing more to get
|
||||
if end != chunk[0]:
|
||||
i = chunks.index(chunk)
|
||||
chunks.insert(i, (end, chunk[0]))
|
||||
return (end, chunk[0] - end)
|
||||
end = chunk[1]
|
||||
bufSize = int(self.client.transport.conn.options['buffersize'])
|
||||
chunks.append((end, end + bufSize))
|
||||
return (end, bufSize)
|
||||
|
||||
def _cbGetRead(self, data, rf, lf, chunks, start, size, startTime):
|
||||
if data and isinstance(data, failure.Failure):
|
||||
log.msg('get read err: %s' % data)
|
||||
reason = data
|
||||
reason.trap(EOFError)
|
||||
i = chunks.index((start, start + size))
|
||||
del chunks[i]
|
||||
chunks.insert(i, (start, 'eof'))
|
||||
elif data:
|
||||
log.msg('get read data: %i' % len(data))
|
||||
lf.seek(start)
|
||||
lf.write(data)
|
||||
if len(data) != size:
|
||||
log.msg('got less than we asked for: %i < %i' %
|
||||
(len(data), size))
|
||||
i = chunks.index((start, start + size))
|
||||
del chunks[i]
|
||||
chunks.insert(i, (start, start + len(data)))
|
||||
rf.total += len(data)
|
||||
if self.useProgressBar:
|
||||
self._printProgressBar(rf, startTime)
|
||||
chunk = self._getNextChunk(chunks)
|
||||
if not chunk:
|
||||
return
|
||||
else:
|
||||
start, length = chunk
|
||||
log.msg('asking for %i -> %i' % (start, start+length))
|
||||
d = rf.readChunk(start, length)
|
||||
d.addBoth(self._cbGetRead, rf, lf, chunks, start, length, startTime)
|
||||
return d
|
||||
|
||||
def _cbGetDone(self, ignored, rf, lf):
|
||||
log.msg('get done')
|
||||
rf.close()
|
||||
lf.close()
|
||||
if self.useProgressBar:
|
||||
self._writeToTransport('\n')
|
||||
return "Transferred %s to %s" % (rf.name, lf.name)
|
||||
|
||||
|
||||
def cmd_PUT(self, rest):
|
||||
"""
|
||||
Do an upload request for a single local file or a globing expression.
|
||||
|
||||
@param rest: Requested command line for the PUT command.
|
||||
@type rest: L{str}
|
||||
|
||||
@return: A deferred which fires with L{None} when transfer is done.
|
||||
@rtype: L{defer.Deferred}
|
||||
"""
|
||||
local, rest = self._getFilename(rest)
|
||||
|
||||
# FIXME: https://twistedmatrix.com/trac/ticket/7241
|
||||
# Use a better check for globbing expression.
|
||||
if '*' in local or '?' in local:
|
||||
if rest:
|
||||
remote, rest = self._getFilename(rest)
|
||||
remote = os.path.join(self.currentDirectory, remote)
|
||||
else:
|
||||
remote = ''
|
||||
|
||||
files = glob.glob(local)
|
||||
return self._putMultipleFiles(files, remote)
|
||||
|
||||
else:
|
||||
if rest:
|
||||
remote, rest = self._getFilename(rest)
|
||||
else:
|
||||
remote = os.path.split(local)[1]
|
||||
return self._putSingleFile(local, remote)
|
||||
|
||||
|
||||
def _putSingleFile(self, local, remote):
|
||||
"""
|
||||
Perform an upload for a single file.
|
||||
|
||||
@param local: Path to local file.
|
||||
@type local: L{str}.
|
||||
|
||||
@param remote: Remote path for the request relative to current working
|
||||
directory.
|
||||
@type remote: L{str}
|
||||
|
||||
@return: A deferred which fires when transfer is done.
|
||||
"""
|
||||
return self._cbPutMultipleNext(None, [local], remote, single=True)
|
||||
|
||||
|
||||
def _putMultipleFiles(self, files, remote):
|
||||
"""
|
||||
Perform an upload for a list of local files.
|
||||
|
||||
@param files: List of local files.
|
||||
@type files: C{list} of L{str}.
|
||||
|
||||
@param remote: Remote path for the request relative to current working
|
||||
directory.
|
||||
@type remote: L{str}
|
||||
|
||||
@return: A deferred which fires when transfer is done.
|
||||
"""
|
||||
return self._cbPutMultipleNext(None, files, remote)
|
||||
|
||||
|
||||
def _cbPutMultipleNext(
|
||||
self, previousResult, files, remotePath, single=False):
|
||||
"""
|
||||
Perform an upload for the next file in the list of local files.
|
||||
|
||||
@param previousResult: Result form previous file form the list.
|
||||
@type previousResult: L{str}
|
||||
|
||||
@param files: List of local files.
|
||||
@type files: C{list} of L{str}
|
||||
|
||||
@param remotePath: Remote path for the request relative to current
|
||||
working directory.
|
||||
@type remotePath: L{str}
|
||||
|
||||
@param single: A flag which signals if this is a transfer for a single
|
||||
file in which case we use the exact remote path
|
||||
@type single: L{bool}
|
||||
|
||||
@return: A deferred which fires when transfer is done.
|
||||
"""
|
||||
if isinstance(previousResult, failure.Failure):
|
||||
self._printFailure(previousResult)
|
||||
elif previousResult:
|
||||
if isinstance(previousResult, unicode):
|
||||
previousResult = previousResult.encode("utf-8")
|
||||
self._writeToTransport(previousResult)
|
||||
if not previousResult.endswith(b'\n'):
|
||||
self._writeToTransport(b'\n')
|
||||
|
||||
currentFile = None
|
||||
while files and not currentFile:
|
||||
try:
|
||||
currentFile = files.pop(0)
|
||||
localStream = open(currentFile, 'rb')
|
||||
except:
|
||||
self._printFailure(failure.Failure())
|
||||
currentFile = None
|
||||
|
||||
# No more files to transfer.
|
||||
if not currentFile:
|
||||
return None
|
||||
|
||||
if single:
|
||||
remote = remotePath
|
||||
else:
|
||||
name = os.path.split(currentFile)[1]
|
||||
remote = os.path.join(remotePath, name)
|
||||
log.msg((name, remote, remotePath))
|
||||
|
||||
d = self._putRemoteFile(localStream, remote)
|
||||
d.addBoth(self._cbPutMultipleNext, files, remotePath)
|
||||
return d
|
||||
|
||||
|
||||
def _putRemoteFile(self, localStream, remotePath):
|
||||
"""
|
||||
Do an upload request.
|
||||
|
||||
@param localStream: Local stream from where data is read.
|
||||
@type localStream: File like object.
|
||||
|
||||
@param remotePath: Remote path for the request relative to current working directory.
|
||||
@type remotePath: L{str}
|
||||
|
||||
@return: A deferred which fires when transfer is done.
|
||||
"""
|
||||
remote = os.path.join(self.currentDirectory, remotePath)
|
||||
flags = (
|
||||
filetransfer.FXF_WRITE |
|
||||
filetransfer.FXF_CREAT |
|
||||
filetransfer.FXF_TRUNC
|
||||
)
|
||||
d = self.client.openFile(remote, flags, {})
|
||||
d.addCallback(self._cbPutOpenFile, localStream)
|
||||
d.addErrback(self._ebCloseLf, localStream)
|
||||
return d
|
||||
|
||||
|
||||
def _cbPutOpenFile(self, rf, lf):
|
||||
numRequests = self.client.transport.conn.options['requests']
|
||||
if self.useProgressBar:
|
||||
lf = FileWrapper(lf)
|
||||
dList = []
|
||||
chunks = []
|
||||
startTime = self.reactor.seconds()
|
||||
for i in range(numRequests):
|
||||
d = self._cbPutWrite(None, rf, lf, chunks, startTime)
|
||||
if d:
|
||||
dList.append(d)
|
||||
dl = defer.DeferredList(dList, fireOnOneErrback=1)
|
||||
dl.addCallback(self._cbPutDone, rf, lf)
|
||||
return dl
|
||||
|
||||
def _cbPutWrite(self, ignored, rf, lf, chunks, startTime):
|
||||
chunk = self._getNextChunk(chunks)
|
||||
start, size = chunk
|
||||
lf.seek(start)
|
||||
data = lf.read(size)
|
||||
if self.useProgressBar:
|
||||
lf.total += len(data)
|
||||
self._printProgressBar(lf, startTime)
|
||||
if data:
|
||||
d = rf.writeChunk(start, data)
|
||||
d.addCallback(self._cbPutWrite, rf, lf, chunks, startTime)
|
||||
return d
|
||||
else:
|
||||
return
|
||||
|
||||
def _cbPutDone(self, ignored, rf, lf):
|
||||
lf.close()
|
||||
rf.close()
|
||||
if self.useProgressBar:
|
||||
self._writeToTransport('\n')
|
||||
return 'Transferred %s to %s' % (lf.name, rf.name)
|
||||
|
||||
def cmd_LCD(self, path):
|
||||
os.chdir(path)
|
||||
|
||||
def cmd_LN(self, rest):
|
||||
linkpath, rest = self._getFilename(rest)
|
||||
targetpath, rest = self._getFilename(rest)
|
||||
linkpath, targetpath = map(
|
||||
lambda x: os.path.join(self.currentDirectory, x),
|
||||
(linkpath, targetpath))
|
||||
return self.client.makeLink(linkpath, targetpath).addCallback(_ignore)
|
||||
|
||||
def cmd_LS(self, rest):
|
||||
# possible lines:
|
||||
# ls current directory
|
||||
# ls name_of_file that file
|
||||
# ls name_of_directory that directory
|
||||
# ls some_glob_string current directory, globbed for that string
|
||||
options = []
|
||||
rest = rest.split()
|
||||
while rest and rest[0] and rest[0][0] == '-':
|
||||
opts = rest.pop(0)[1:]
|
||||
for o in opts:
|
||||
if o == 'l':
|
||||
options.append('verbose')
|
||||
elif o == 'a':
|
||||
options.append('all')
|
||||
rest = ' '.join(rest)
|
||||
path, rest = self._getFilename(rest)
|
||||
if not path:
|
||||
fullPath = self.currentDirectory + '/'
|
||||
else:
|
||||
fullPath = os.path.join(self.currentDirectory, path)
|
||||
d = self._remoteGlob(fullPath)
|
||||
d.addCallback(self._cbDisplayFiles, options)
|
||||
return d
|
||||
|
||||
def _cbDisplayFiles(self, files, options):
|
||||
files.sort()
|
||||
if 'all' not in options:
|
||||
files = [f for f in files if not f[0].startswith(b'.')]
|
||||
if 'verbose' in options:
|
||||
lines = [f[1] for f in files]
|
||||
else:
|
||||
lines = [f[0] for f in files]
|
||||
if not lines:
|
||||
return None
|
||||
else:
|
||||
return b'\n'.join(lines)
|
||||
|
||||
def cmd_MKDIR(self, path):
|
||||
path, rest = self._getFilename(path)
|
||||
path = os.path.join(self.currentDirectory, path)
|
||||
return self.client.makeDirectory(path, {}).addCallback(_ignore)
|
||||
|
||||
def cmd_RMDIR(self, path):
|
||||
path, rest = self._getFilename(path)
|
||||
path = os.path.join(self.currentDirectory, path)
|
||||
return self.client.removeDirectory(path).addCallback(_ignore)
|
||||
|
||||
def cmd_LMKDIR(self, path):
|
||||
os.system("mkdir %s" % path)
|
||||
|
||||
def cmd_RM(self, path):
|
||||
path, rest = self._getFilename(path)
|
||||
path = os.path.join(self.currentDirectory, path)
|
||||
return self.client.removeFile(path).addCallback(_ignore)
|
||||
|
||||
def cmd_LLS(self, rest):
|
||||
os.system("ls %s" % rest)
|
||||
|
||||
def cmd_RENAME(self, rest):
|
||||
oldpath, rest = self._getFilename(rest)
|
||||
newpath, rest = self._getFilename(rest)
|
||||
oldpath, newpath = map (
|
||||
lambda x: os.path.join(self.currentDirectory, x),
|
||||
(oldpath, newpath))
|
||||
return self.client.renameFile(oldpath, newpath).addCallback(_ignore)
|
||||
|
||||
def cmd_EXIT(self, ignored):
|
||||
self.client.transport.loseConnection()
|
||||
|
||||
cmd_QUIT = cmd_EXIT
|
||||
|
||||
def cmd_VERSION(self, ignored):
|
||||
version = "SFTP version %i" % self.client.version
|
||||
if isinstance(version, unicode):
|
||||
version = version.encode("utf-8")
|
||||
return version
|
||||
|
||||
def cmd_HELP(self, ignored):
|
||||
return """Available commands:
|
||||
cd path Change remote directory to 'path'.
|
||||
chgrp gid path Change gid of 'path' to 'gid'.
|
||||
chmod mode path Change mode of 'path' to 'mode'.
|
||||
chown uid path Change uid of 'path' to 'uid'.
|
||||
exit Disconnect from the server.
|
||||
get remote-path [local-path] Get remote file.
|
||||
help Get a list of available commands.
|
||||
lcd path Change local directory to 'path'.
|
||||
lls [ls-options] [path] Display local directory listing.
|
||||
lmkdir path Create local directory.
|
||||
ln linkpath targetpath Symlink remote file.
|
||||
lpwd Print the local working directory.
|
||||
ls [-l] [path] Display remote directory listing.
|
||||
mkdir path Create remote directory.
|
||||
progress Toggle progress bar.
|
||||
put local-path [remote-path] Put local file.
|
||||
pwd Print the remote working directory.
|
||||
quit Disconnect from the server.
|
||||
rename oldpath newpath Rename remote file.
|
||||
rmdir path Remove remote directory.
|
||||
rm path Remove remote file.
|
||||
version Print the SFTP version.
|
||||
? Synonym for 'help'.
|
||||
"""
|
||||
|
||||
def cmd_PWD(self, ignored):
|
||||
return self.currentDirectory
|
||||
|
||||
def cmd_LPWD(self, ignored):
|
||||
return os.getcwd()
|
||||
|
||||
def cmd_PROGRESS(self, ignored):
|
||||
self.useProgressBar = not self.useProgressBar
|
||||
return "%ssing progess bar." % (self.useProgressBar and "U" or "Not u")
|
||||
|
||||
def cmd_EXEC(self, rest):
|
||||
"""
|
||||
Run C{rest} using the user's shell (or /bin/sh if they do not have
|
||||
one).
|
||||
"""
|
||||
shell = self._pwd.getpwnam(getpass.getuser())[6]
|
||||
if not shell:
|
||||
shell = '/bin/sh'
|
||||
if rest:
|
||||
cmds = ['-c', rest]
|
||||
return utils.getProcessOutput(shell, cmds, errortoo=1)
|
||||
else:
|
||||
os.system(shell)
|
||||
|
||||
# accessory functions
|
||||
|
||||
def _remoteGlob(self, fullPath):
|
||||
log.msg('looking up %s' % fullPath)
|
||||
head, tail = os.path.split(fullPath)
|
||||
if '*' in tail or '?' in tail:
|
||||
glob = 1
|
||||
else:
|
||||
glob = 0
|
||||
if tail and not glob: # could be file or directory
|
||||
# try directory first
|
||||
d = self.client.openDirectory(fullPath)
|
||||
d.addCallback(self._cbOpenList, '')
|
||||
d.addErrback(self._ebNotADirectory, head, tail)
|
||||
else:
|
||||
d = self.client.openDirectory(head)
|
||||
d.addCallback(self._cbOpenList, tail)
|
||||
return d
|
||||
|
||||
def _cbOpenList(self, directory, glob):
|
||||
files = []
|
||||
d = directory.read()
|
||||
d.addBoth(self._cbReadFile, files, directory, glob)
|
||||
return d
|
||||
|
||||
def _ebNotADirectory(self, reason, path, glob):
|
||||
d = self.client.openDirectory(path)
|
||||
d.addCallback(self._cbOpenList, glob)
|
||||
return d
|
||||
|
||||
def _cbReadFile(self, files, l, directory, glob):
|
||||
if not isinstance(files, failure.Failure):
|
||||
if glob:
|
||||
if _PY3:
|
||||
glob = glob.encode("utf-8")
|
||||
l.extend([f for f in files if fnmatch.fnmatch(f[0], glob)])
|
||||
else:
|
||||
l.extend(files)
|
||||
d = directory.read()
|
||||
d.addBoth(self._cbReadFile, l, directory, glob)
|
||||
return d
|
||||
else:
|
||||
reason = files
|
||||
reason.trap(EOFError)
|
||||
directory.close()
|
||||
return l
|
||||
|
||||
def _abbrevSize(self, size):
|
||||
# from http://mail.python.org/pipermail/python-list/1999-December/018395.html
|
||||
_abbrevs = [
|
||||
(1<<50, 'PB'),
|
||||
(1<<40, 'TB'),
|
||||
(1<<30, 'GB'),
|
||||
(1<<20, 'MB'),
|
||||
(1<<10, 'kB'),
|
||||
(1, 'B')
|
||||
]
|
||||
|
||||
for factor, suffix in _abbrevs:
|
||||
if size > factor:
|
||||
break
|
||||
return '%.1f' % (size/factor) + suffix
|
||||
|
||||
def _abbrevTime(self, t):
|
||||
if t > 3600: # 1 hour
|
||||
hours = int(t / 3600)
|
||||
t -= (3600 * hours)
|
||||
mins = int(t / 60)
|
||||
t -= (60 * mins)
|
||||
return "%i:%02i:%02i" % (hours, mins, t)
|
||||
else:
|
||||
mins = int(t/60)
|
||||
t -= (60 * mins)
|
||||
return "%02i:%02i" % (mins, t)
|
||||
|
||||
|
||||
def _printProgressBar(self, f, startTime):
|
||||
"""
|
||||
Update a console progress bar on this L{StdioClient}'s transport, based
|
||||
on the difference between the start time of the operation and the
|
||||
current time according to the reactor, and appropriate to the size of
|
||||
the console window.
|
||||
|
||||
@param f: a wrapper around the file which is being written or read
|
||||
@type f: L{FileWrapper}
|
||||
|
||||
@param startTime: The time at which the operation being tracked began.
|
||||
@type startTime: L{float}
|
||||
"""
|
||||
diff = self.reactor.seconds() - startTime
|
||||
total = f.total
|
||||
try:
|
||||
winSize = struct.unpack('4H',
|
||||
fcntl.ioctl(0, tty.TIOCGWINSZ, '12345679'))
|
||||
except IOError:
|
||||
winSize = [None, 80]
|
||||
if diff == 0.0:
|
||||
speed = 0.0
|
||||
else:
|
||||
speed = total / diff
|
||||
if speed:
|
||||
timeLeft = (f.size - total) / speed
|
||||
else:
|
||||
timeLeft = 0
|
||||
front = f.name
|
||||
if f.size:
|
||||
percentage = (total / f.size) * 100
|
||||
else:
|
||||
percentage = 100
|
||||
back = '%3i%% %s %sps %s ' % (percentage,
|
||||
self._abbrevSize(total),
|
||||
self._abbrevSize(speed),
|
||||
self._abbrevTime(timeLeft))
|
||||
spaces = (winSize[1] - (len(front) + len(back) + 1)) * ' '
|
||||
command = '\r%s%s%s' % (front, spaces, back)
|
||||
self._writeToTransport(command)
|
||||
|
||||
|
||||
def _getFilename(self, line):
|
||||
"""
|
||||
Parse line received as command line input and return first filename
|
||||
together with the remaining line.
|
||||
|
||||
@param line: Arguments received from command line input.
|
||||
@type line: L{str}
|
||||
|
||||
@return: Tupple with filename and rest. Return empty values when no path was not found.
|
||||
@rtype: C{tupple}
|
||||
"""
|
||||
line = line.strip()
|
||||
if not line:
|
||||
return '', ''
|
||||
if line[0] in '\'"':
|
||||
ret = []
|
||||
line = list(line)
|
||||
try:
|
||||
for i in range(1,len(line)):
|
||||
c = line[i]
|
||||
if c == line[0]:
|
||||
return ''.join(ret), ''.join(line[i+1:]).lstrip()
|
||||
elif c == '\\': # quoted character
|
||||
del line[i]
|
||||
if line[i] not in '\'"\\':
|
||||
raise IndexError("bad quote: \\%s" % (line[i],))
|
||||
ret.append(line[i])
|
||||
else:
|
||||
ret.append(line[i])
|
||||
except IndexError:
|
||||
raise IndexError("unterminated quote")
|
||||
ret = line.split(None, 1)
|
||||
if len(ret) == 1:
|
||||
return ret[0], ''
|
||||
else:
|
||||
return ret[0], ret[1]
|
||||
|
||||
setattr(StdioClient, 'cmd_?', StdioClient.cmd_HELP)
|
||||
|
||||
class SSHConnection(connection.SSHConnection):
|
||||
def serviceStarted(self):
|
||||
self.openChannel(SSHSession())
|
||||
|
||||
class SSHSession(channel.SSHChannel):
|
||||
|
||||
name = b'session'
|
||||
|
||||
def channelOpen(self, foo):
|
||||
log.msg('session %s open' % self.id)
|
||||
if self.conn.options['subsystem'].startswith('/'):
|
||||
request = 'exec'
|
||||
else:
|
||||
request = 'subsystem'
|
||||
d = self.conn.sendRequest(self, request, \
|
||||
common.NS(self.conn.options['subsystem']), wantReply=1)
|
||||
d.addCallback(self._cbSubsystem)
|
||||
d.addErrback(_ebExit)
|
||||
|
||||
def _cbSubsystem(self, result):
|
||||
self.client = filetransfer.FileTransferClient()
|
||||
self.client.makeConnection(self)
|
||||
self.dataReceived = self.client.dataReceived
|
||||
f = None
|
||||
if self.conn.options['batchfile']:
|
||||
fn = self.conn.options['batchfile']
|
||||
if fn != '-':
|
||||
f = open(fn)
|
||||
self.stdio = stdio.StandardIO(StdioClient(self.client, f))
|
||||
|
||||
def extReceived(self, t, data):
|
||||
if t==connection.EXTENDED_DATA_STDERR:
|
||||
log.msg('got %s stderr data' % len(data))
|
||||
sys.stderr.write(data)
|
||||
sys.stderr.flush()
|
||||
|
||||
def eofReceived(self):
|
||||
log.msg('got eof')
|
||||
self.stdio.loseWriteConnection()
|
||||
|
||||
def closeReceived(self):
|
||||
log.msg('remote side closed %s' % self)
|
||||
self.conn.sendClose(self)
|
||||
|
||||
def closed(self):
|
||||
try:
|
||||
reactor.stop()
|
||||
except:
|
||||
pass
|
||||
|
||||
def stopWriting(self):
|
||||
self.stdio.pauseProducing()
|
||||
|
||||
def startWriting(self):
|
||||
self.stdio.resumeProducing()
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
|
|
@ -0,0 +1,317 @@
|
|||
# -*- test-case-name: twisted.conch.test.test_ckeygen -*-
|
||||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
Implementation module for the `ckeygen` command.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys, os, getpass, socket
|
||||
from functools import wraps
|
||||
from imp import reload
|
||||
|
||||
if getpass.getpass == getpass.unix_getpass:
|
||||
try:
|
||||
import termios # hack around broken termios
|
||||
termios.tcgetattr, termios.tcsetattr
|
||||
except (ImportError, AttributeError):
|
||||
sys.modules['termios'] = None
|
||||
reload(getpass)
|
||||
|
||||
from twisted.conch.ssh import keys
|
||||
from twisted.python import failure, filepath, log, usage
|
||||
from twisted.python.compat import raw_input, _PY3
|
||||
|
||||
|
||||
|
||||
supportedKeyTypes = dict()
|
||||
def _keyGenerator(keyType):
|
||||
def assignkeygenerator(keygenerator):
|
||||
@wraps(keygenerator)
|
||||
def wrapper(*args, **kwargs):
|
||||
return keygenerator(*args, **kwargs)
|
||||
supportedKeyTypes[keyType] = wrapper
|
||||
return wrapper
|
||||
return assignkeygenerator
|
||||
|
||||
|
||||
|
||||
class GeneralOptions(usage.Options):
|
||||
synopsis = """Usage: ckeygen [options]
|
||||
"""
|
||||
|
||||
longdesc = "ckeygen manipulates public/private keys in various ways."
|
||||
|
||||
optParameters = [['bits', 'b', None, 'Number of bits in the key to create.'],
|
||||
['filename', 'f', None, 'Filename of the key file.'],
|
||||
['type', 't', None, 'Specify type of key to create.'],
|
||||
['comment', 'C', None, 'Provide new comment.'],
|
||||
['newpass', 'N', None, 'Provide new passphrase.'],
|
||||
['pass', 'P', None, 'Provide old passphrase.'],
|
||||
['format', 'o', 'sha256-base64',
|
||||
'Fingerprint format of key file.'],
|
||||
['private-key-subtype', None, 'PEM',
|
||||
'OpenSSH private key subtype to write ("PEM" or "v1").']]
|
||||
|
||||
optFlags = [['fingerprint', 'l', 'Show fingerprint of key file.'],
|
||||
['changepass', 'p', 'Change passphrase of private key file.'],
|
||||
['quiet', 'q', 'Quiet.'],
|
||||
['no-passphrase', None, "Create the key with no passphrase."],
|
||||
['showpub', 'y',
|
||||
'Read private key file and print public key.']]
|
||||
|
||||
compData = usage.Completions(
|
||||
optActions={
|
||||
"type": usage.CompleteList(list(supportedKeyTypes.keys())),
|
||||
"private-key-subtype": usage.CompleteList(["PEM", "v1"]),
|
||||
})
|
||||
|
||||
|
||||
|
||||
def run():
|
||||
options = GeneralOptions()
|
||||
try:
|
||||
options.parseOptions(sys.argv[1:])
|
||||
except usage.UsageError as u:
|
||||
print('ERROR: %s' % u)
|
||||
options.opt_help()
|
||||
sys.exit(1)
|
||||
log.discardLogs()
|
||||
log.deferr = handleError # HACK
|
||||
if options['type']:
|
||||
if options['type'].lower() in supportedKeyTypes:
|
||||
print('Generating public/private %s key pair.' % (options['type']))
|
||||
supportedKeyTypes[options['type'].lower()](options)
|
||||
else:
|
||||
sys.exit(
|
||||
'Key type was %s, must be one of %s'
|
||||
% (options['type'], ', '.join(supportedKeyTypes.keys())))
|
||||
elif options['fingerprint']:
|
||||
printFingerprint(options)
|
||||
elif options['changepass']:
|
||||
changePassPhrase(options)
|
||||
elif options['showpub']:
|
||||
displayPublicKey(options)
|
||||
else:
|
||||
options.opt_help()
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def enumrepresentation(options):
|
||||
if options['format'] == 'md5-hex':
|
||||
options['format'] = keys.FingerprintFormats.MD5_HEX
|
||||
return options
|
||||
elif options['format'] == 'sha256-base64':
|
||||
options['format'] = keys.FingerprintFormats.SHA256_BASE64
|
||||
return options
|
||||
else:
|
||||
raise keys.BadFingerPrintFormat(
|
||||
'Unsupported fingerprint format: %s' % (options['format'],))
|
||||
|
||||
|
||||
|
||||
def handleError():
|
||||
global exitStatus
|
||||
exitStatus = 2
|
||||
log.err(failure.Failure())
|
||||
raise
|
||||
|
||||
|
||||
@_keyGenerator('rsa')
|
||||
def generateRSAkey(options):
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||
|
||||
if not options['bits']:
|
||||
options['bits'] = 1024
|
||||
keyPrimitive = rsa.generate_private_key(
|
||||
key_size=int(options['bits']),
|
||||
public_exponent=65537,
|
||||
backend=default_backend(),
|
||||
)
|
||||
key = keys.Key(keyPrimitive)
|
||||
_saveKey(key, options)
|
||||
|
||||
|
||||
|
||||
@_keyGenerator('dsa')
|
||||
def generateDSAkey(options):
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives.asymmetric import dsa
|
||||
|
||||
if not options['bits']:
|
||||
options['bits'] = 1024
|
||||
keyPrimitive = dsa.generate_private_key(
|
||||
key_size=int(options['bits']),
|
||||
backend=default_backend(),
|
||||
)
|
||||
key = keys.Key(keyPrimitive)
|
||||
_saveKey(key, options)
|
||||
|
||||
|
||||
|
||||
@_keyGenerator('ecdsa')
|
||||
def generateECDSAkey(options):
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives.asymmetric import ec
|
||||
|
||||
if not options['bits']:
|
||||
options['bits'] = 256
|
||||
# OpenSSH supports only mandatory sections of RFC5656.
|
||||
# See https://www.openssh.com/txt/release-5.7
|
||||
curve = b'ecdsa-sha2-nistp' + str(options['bits']).encode('ascii')
|
||||
keyPrimitive = ec.generate_private_key(
|
||||
curve=keys._curveTable[curve],
|
||||
backend=default_backend()
|
||||
)
|
||||
key = keys.Key(keyPrimitive)
|
||||
_saveKey(key, options)
|
||||
|
||||
|
||||
|
||||
def printFingerprint(options):
|
||||
if not options['filename']:
|
||||
filename = os.path.expanduser('~/.ssh/id_rsa')
|
||||
options['filename'] = raw_input('Enter file in which the key is (%s): ' % filename)
|
||||
if os.path.exists(options['filename']+'.pub'):
|
||||
options['filename'] += '.pub'
|
||||
options = enumrepresentation(options)
|
||||
try:
|
||||
key = keys.Key.fromFile(options['filename'])
|
||||
print('%s %s %s' % (
|
||||
key.size(),
|
||||
key.fingerprint(options['format']),
|
||||
os.path.basename(options['filename'])))
|
||||
except keys.BadKeyError:
|
||||
sys.exit('bad key')
|
||||
|
||||
|
||||
|
||||
def changePassPhrase(options):
|
||||
if not options['filename']:
|
||||
filename = os.path.expanduser('~/.ssh/id_rsa')
|
||||
options['filename'] = raw_input(
|
||||
'Enter file in which the key is (%s): ' % filename)
|
||||
try:
|
||||
key = keys.Key.fromFile(options['filename'])
|
||||
except keys.EncryptedKeyError:
|
||||
# Raised if password not supplied for an encrypted key
|
||||
if not options.get('pass'):
|
||||
options['pass'] = getpass.getpass('Enter old passphrase: ')
|
||||
try:
|
||||
key = keys.Key.fromFile(
|
||||
options['filename'], passphrase=options['pass'])
|
||||
except keys.BadKeyError:
|
||||
sys.exit('Could not change passphrase: old passphrase error')
|
||||
except keys.EncryptedKeyError as e:
|
||||
sys.exit('Could not change passphrase: %s' % (e,))
|
||||
except keys.BadKeyError as e:
|
||||
sys.exit('Could not change passphrase: %s' % (e,))
|
||||
|
||||
if not options.get('newpass'):
|
||||
while 1:
|
||||
p1 = getpass.getpass(
|
||||
'Enter new passphrase (empty for no passphrase): ')
|
||||
p2 = getpass.getpass('Enter same passphrase again: ')
|
||||
if p1 == p2:
|
||||
break
|
||||
print('Passphrases do not match. Try again.')
|
||||
options['newpass'] = p1
|
||||
|
||||
try:
|
||||
newkeydata = key.toString(
|
||||
'openssh', subtype=options.get('private-key-subtype'),
|
||||
passphrase=options['newpass'])
|
||||
except Exception as e:
|
||||
sys.exit('Could not change passphrase: %s' % (e,))
|
||||
|
||||
try:
|
||||
keys.Key.fromString(newkeydata, passphrase=options['newpass'])
|
||||
except (keys.EncryptedKeyError, keys.BadKeyError) as e:
|
||||
sys.exit('Could not change passphrase: %s' % (e,))
|
||||
|
||||
with open(options['filename'], 'wb') as fd:
|
||||
fd.write(newkeydata)
|
||||
|
||||
print('Your identification has been saved with the new passphrase.')
|
||||
|
||||
|
||||
|
||||
def displayPublicKey(options):
|
||||
if not options['filename']:
|
||||
filename = os.path.expanduser('~/.ssh/id_rsa')
|
||||
options['filename'] = raw_input('Enter file in which the key is (%s): ' % filename)
|
||||
try:
|
||||
key = keys.Key.fromFile(options['filename'])
|
||||
except keys.EncryptedKeyError:
|
||||
if not options.get('pass'):
|
||||
options['pass'] = getpass.getpass('Enter passphrase: ')
|
||||
key = keys.Key.fromFile(
|
||||
options['filename'], passphrase = options['pass'])
|
||||
displayKey = key.public().toString('openssh')
|
||||
if _PY3:
|
||||
displayKey = displayKey.decode("ascii")
|
||||
print(displayKey)
|
||||
|
||||
|
||||
|
||||
def _saveKey(key, options):
|
||||
"""
|
||||
Persist a SSH key on local filesystem.
|
||||
|
||||
@param key: Key which is persisted on local filesystem.
|
||||
@type key: C{keys.Key} implementation.
|
||||
|
||||
@param options:
|
||||
@type options: L{dict}
|
||||
"""
|
||||
KeyTypeMapping = {'EC': 'ecdsa', 'RSA': 'rsa', 'DSA': 'dsa'}
|
||||
keyTypeName = KeyTypeMapping[key.type()]
|
||||
if not options['filename']:
|
||||
defaultPath = os.path.expanduser(u'~/.ssh/id_%s' % (keyTypeName,))
|
||||
newPath = raw_input(
|
||||
'Enter file in which to save the key (%s): ' % (defaultPath,))
|
||||
|
||||
options['filename'] = newPath.strip() or defaultPath
|
||||
|
||||
if os.path.exists(options['filename']):
|
||||
print('%s already exists.' % (options['filename'],))
|
||||
yn = raw_input('Overwrite (y/n)? ')
|
||||
if yn[0].lower() != 'y':
|
||||
sys.exit()
|
||||
|
||||
if options.get('no-passphrase'):
|
||||
options['pass'] = b''
|
||||
elif not options['pass']:
|
||||
while 1:
|
||||
p1 = getpass.getpass(
|
||||
'Enter passphrase (empty for no passphrase): ')
|
||||
p2 = getpass.getpass('Enter same passphrase again: ')
|
||||
if p1 == p2:
|
||||
break
|
||||
print('Passphrases do not match. Try again.')
|
||||
options['pass'] = p1
|
||||
|
||||
comment = '%s@%s' % (getpass.getuser(), socket.gethostname())
|
||||
|
||||
filepath.FilePath(options['filename']).setContent(
|
||||
key.toString(
|
||||
'openssh', subtype=options.get('private-key-subtype'),
|
||||
passphrase=options['pass']))
|
||||
os.chmod(options['filename'], 33152)
|
||||
|
||||
filepath.FilePath(options['filename'] + '.pub').setContent(
|
||||
key.public().toString('openssh', comment=comment))
|
||||
options = enumrepresentation(options)
|
||||
|
||||
print('Your identification has been saved in %s' % (options['filename'],))
|
||||
print('Your public key has been saved in %s.pub' % (options['filename'],))
|
||||
print('The key fingerprint in %s is:' % (options['format'],))
|
||||
print(key.fingerprint(options['format']))
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
585
venv/lib/python3.9/site-packages/twisted/conch/scripts/conch.py
Normal file
585
venv/lib/python3.9/site-packages/twisted/conch/scripts/conch.py
Normal file
|
|
@ -0,0 +1,585 @@
|
|||
# -*- test-case-name: twisted.conch.test.test_conch -*-
|
||||
#
|
||||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
#
|
||||
# $Id: conch.py,v 1.65 2004/03/11 00:29:14 z3p Exp $
|
||||
|
||||
#""" Implementation module for the `conch` command.
|
||||
#"""
|
||||
from __future__ import print_function
|
||||
|
||||
from twisted.conch.client import connect, default, options
|
||||
from twisted.conch.error import ConchError
|
||||
from twisted.conch.ssh import connection, common
|
||||
from twisted.conch.ssh import session, forwarding, channel
|
||||
from twisted.internet import reactor, stdio, task
|
||||
from twisted.python import log, usage
|
||||
from twisted.python.compat import ioType, networkString, unicode
|
||||
|
||||
import os
|
||||
import sys
|
||||
import getpass
|
||||
import struct
|
||||
import tty
|
||||
import fcntl
|
||||
import signal
|
||||
|
||||
|
||||
|
||||
class ClientOptions(options.ConchOptions):
|
||||
|
||||
synopsis = """Usage: conch [options] host [command]
|
||||
"""
|
||||
longdesc = ("conch is a SSHv2 client that allows logging into a remote "
|
||||
"machine and executing commands.")
|
||||
|
||||
optParameters = [['escape', 'e', '~'],
|
||||
['localforward', 'L', None, 'listen-port:host:port Forward local port to remote address'],
|
||||
['remoteforward', 'R', None, 'listen-port:host:port Forward remote port to local address'],
|
||||
]
|
||||
|
||||
optFlags = [['null', 'n', 'Redirect input from /dev/null.'],
|
||||
['fork', 'f', 'Fork to background after authentication.'],
|
||||
['tty', 't', 'Tty; allocate a tty even if command is given.'],
|
||||
['notty', 'T', 'Do not allocate a tty.'],
|
||||
['noshell', 'N', 'Do not execute a shell or command.'],
|
||||
['subsystem', 's', 'Invoke command (mandatory) as SSH2 subsystem.'],
|
||||
]
|
||||
|
||||
compData = usage.Completions(
|
||||
mutuallyExclusive=[("tty", "notty")],
|
||||
optActions={
|
||||
"localforward": usage.Completer(descr="listen-port:host:port"),
|
||||
"remoteforward": usage.Completer(descr="listen-port:host:port")},
|
||||
extraActions=[usage.CompleteUserAtHost(),
|
||||
usage.Completer(descr="command"),
|
||||
usage.Completer(descr="argument", repeat=True)]
|
||||
)
|
||||
|
||||
localForwards = []
|
||||
remoteForwards = []
|
||||
|
||||
def opt_escape(self, esc):
|
||||
"""
|
||||
Set escape character; ``none'' = disable
|
||||
"""
|
||||
if esc == 'none':
|
||||
self['escape'] = None
|
||||
elif esc[0] == '^' and len(esc) == 2:
|
||||
self['escape'] = chr(ord(esc[1])-64)
|
||||
elif len(esc) == 1:
|
||||
self['escape'] = esc
|
||||
else:
|
||||
sys.exit("Bad escape character '{}'.".format(esc))
|
||||
|
||||
|
||||
def opt_localforward(self, f):
|
||||
"""
|
||||
Forward local port to remote address (lport:host:port)
|
||||
"""
|
||||
localPort, remoteHost, remotePort = f.split(':') # Doesn't do v6 yet
|
||||
localPort = int(localPort)
|
||||
remotePort = int(remotePort)
|
||||
self.localForwards.append((localPort, (remoteHost, remotePort)))
|
||||
|
||||
|
||||
def opt_remoteforward(self, f):
|
||||
"""
|
||||
Forward remote port to local address (rport:host:port)
|
||||
"""
|
||||
remotePort, connHost, connPort = f.split(':') # Doesn't do v6 yet
|
||||
remotePort = int(remotePort)
|
||||
connPort = int(connPort)
|
||||
self.remoteForwards.append((remotePort, (connHost, connPort)))
|
||||
|
||||
|
||||
def parseArgs(self, host, *command):
|
||||
self['host'] = host
|
||||
self['command'] = ' '.join(command)
|
||||
|
||||
|
||||
|
||||
# Rest of code in "run"
|
||||
options = None
|
||||
conn = None
|
||||
exitStatus = 0
|
||||
old = None
|
||||
_inRawMode = 0
|
||||
_savedRawMode = None
|
||||
|
||||
|
||||
|
||||
def run():
|
||||
global options, old
|
||||
args = sys.argv[1:]
|
||||
if '-l' in args: # CVS is an idiot
|
||||
i = args.index('-l')
|
||||
args = args[i:i+2]+args
|
||||
del args[i+2:i+4]
|
||||
for arg in args[:]:
|
||||
try:
|
||||
i = args.index(arg)
|
||||
if arg[:2] == '-o' and args[i+1][0] != '-':
|
||||
args[i:i+2] = [] # Suck on it scp
|
||||
except ValueError:
|
||||
pass
|
||||
options = ClientOptions()
|
||||
try:
|
||||
options.parseOptions(args)
|
||||
except usage.UsageError as u:
|
||||
print('ERROR: {}'.format(u))
|
||||
options.opt_help()
|
||||
sys.exit(1)
|
||||
if options['log']:
|
||||
if options['logfile']:
|
||||
if options['logfile'] == '-':
|
||||
f = sys.stdout
|
||||
else:
|
||||
f = open(options['logfile'], 'a+')
|
||||
else:
|
||||
f = sys.stderr
|
||||
realout = sys.stdout
|
||||
log.startLogging(f)
|
||||
sys.stdout = realout
|
||||
else:
|
||||
log.discardLogs()
|
||||
doConnect()
|
||||
fd = sys.stdin.fileno()
|
||||
try:
|
||||
old = tty.tcgetattr(fd)
|
||||
except:
|
||||
old = None
|
||||
try:
|
||||
oldUSR1 = signal.signal(signal.SIGUSR1, lambda *a: reactor.callLater(0, reConnect))
|
||||
except:
|
||||
oldUSR1 = None
|
||||
try:
|
||||
reactor.run()
|
||||
finally:
|
||||
if old:
|
||||
tty.tcsetattr(fd, tty.TCSANOW, old)
|
||||
if oldUSR1:
|
||||
signal.signal(signal.SIGUSR1, oldUSR1)
|
||||
if (options['command'] and options['tty']) or not options['notty']:
|
||||
signal.signal(signal.SIGWINCH, signal.SIG_DFL)
|
||||
if sys.stdout.isatty() and not options['command']:
|
||||
print('Connection to {} closed.'.format(options['host']))
|
||||
sys.exit(exitStatus)
|
||||
|
||||
|
||||
|
||||
def handleError():
|
||||
from twisted.python import failure
|
||||
global exitStatus
|
||||
exitStatus = 2
|
||||
reactor.callLater(0.01, _stopReactor)
|
||||
log.err(failure.Failure())
|
||||
raise
|
||||
|
||||
|
||||
|
||||
def _stopReactor():
|
||||
try:
|
||||
reactor.stop()
|
||||
except: pass
|
||||
|
||||
|
||||
|
||||
def doConnect():
|
||||
if '@' in options['host']:
|
||||
options['user'], options['host'] = options['host'].split('@', 1)
|
||||
if not options.identitys:
|
||||
options.identitys = ['~/.ssh/id_rsa', '~/.ssh/id_dsa']
|
||||
host = options['host']
|
||||
if not options['user']:
|
||||
options['user'] = getpass.getuser()
|
||||
if not options['port']:
|
||||
options['port'] = 22
|
||||
else:
|
||||
options['port'] = int(options['port'])
|
||||
host = options['host']
|
||||
port = options['port']
|
||||
vhk = default.verifyHostKey
|
||||
if not options['host-key-algorithms']:
|
||||
options['host-key-algorithms'] = default.getHostKeyAlgorithms(
|
||||
host, options)
|
||||
uao = default.SSHUserAuthClient(options['user'], options, SSHConnection())
|
||||
connect.connect(host, port, options, vhk, uao).addErrback(_ebExit)
|
||||
|
||||
|
||||
|
||||
def _ebExit(f):
|
||||
global exitStatus
|
||||
exitStatus = "conch: exiting with error {}".format(f)
|
||||
reactor.callLater(0.1, _stopReactor)
|
||||
|
||||
|
||||
|
||||
def onConnect():
|
||||
# if keyAgent and options['agent']:
|
||||
# cc = protocol.ClientCreator(reactor, SSHAgentForwardingLocal, conn)
|
||||
# cc.connectUNIX(os.environ['SSH_AUTH_SOCK'])
|
||||
if hasattr(conn.transport, 'sendIgnore'):
|
||||
_KeepAlive(conn)
|
||||
if options.localForwards:
|
||||
for localPort, hostport in options.localForwards:
|
||||
s = reactor.listenTCP(localPort,
|
||||
forwarding.SSHListenForwardingFactory(conn,
|
||||
hostport,
|
||||
SSHListenClientForwardingChannel))
|
||||
conn.localForwards.append(s)
|
||||
if options.remoteForwards:
|
||||
for remotePort, hostport in options.remoteForwards:
|
||||
log.msg('asking for remote forwarding for {}:{}'.format(
|
||||
remotePort, hostport))
|
||||
conn.requestRemoteForwarding(remotePort, hostport)
|
||||
reactor.addSystemEventTrigger('before', 'shutdown', beforeShutdown)
|
||||
if not options['noshell'] or options['agent']:
|
||||
conn.openChannel(SSHSession())
|
||||
if options['fork']:
|
||||
if os.fork():
|
||||
os._exit(0)
|
||||
os.setsid()
|
||||
for i in range(3):
|
||||
try:
|
||||
os.close(i)
|
||||
except OSError as e:
|
||||
import errno
|
||||
if e.errno != errno.EBADF:
|
||||
raise
|
||||
|
||||
|
||||
|
||||
def reConnect():
|
||||
beforeShutdown()
|
||||
conn.transport.transport.loseConnection()
|
||||
|
||||
|
||||
|
||||
def beforeShutdown():
|
||||
remoteForwards = options.remoteForwards
|
||||
for remotePort, hostport in remoteForwards:
|
||||
log.msg('cancelling {}:{}'.format(remotePort, hostport))
|
||||
conn.cancelRemoteForwarding(remotePort)
|
||||
|
||||
|
||||
|
||||
def stopConnection():
|
||||
if not options['reconnect']:
|
||||
reactor.callLater(0.1, _stopReactor)
|
||||
|
||||
|
||||
|
||||
class _KeepAlive:
|
||||
|
||||
def __init__(self, conn):
|
||||
self.conn = conn
|
||||
self.globalTimeout = None
|
||||
self.lc = task.LoopingCall(self.sendGlobal)
|
||||
self.lc.start(300)
|
||||
|
||||
|
||||
def sendGlobal(self):
|
||||
d = self.conn.sendGlobalRequest(b"conch-keep-alive@twistedmatrix.com",
|
||||
b"", wantReply=1)
|
||||
d.addBoth(self._cbGlobal)
|
||||
self.globalTimeout = reactor.callLater(30, self._ebGlobal)
|
||||
|
||||
|
||||
def _cbGlobal(self, res):
|
||||
if self.globalTimeout:
|
||||
self.globalTimeout.cancel()
|
||||
self.globalTimeout = None
|
||||
|
||||
|
||||
def _ebGlobal(self):
|
||||
if self.globalTimeout:
|
||||
self.globalTimeout = None
|
||||
self.conn.transport.loseConnection()
|
||||
|
||||
|
||||
|
||||
class SSHConnection(connection.SSHConnection):
|
||||
def serviceStarted(self):
|
||||
global conn
|
||||
conn = self
|
||||
self.localForwards = []
|
||||
self.remoteForwards = {}
|
||||
if not isinstance(self, connection.SSHConnection):
|
||||
# make these fall through
|
||||
del self.__class__.requestRemoteForwarding
|
||||
del self.__class__.cancelRemoteForwarding
|
||||
onConnect()
|
||||
|
||||
|
||||
def serviceStopped(self):
|
||||
lf = self.localForwards
|
||||
self.localForwards = []
|
||||
for s in lf:
|
||||
s.loseConnection()
|
||||
stopConnection()
|
||||
|
||||
|
||||
def requestRemoteForwarding(self, remotePort, hostport):
|
||||
data = forwarding.packGlobal_tcpip_forward(('0.0.0.0', remotePort))
|
||||
d = self.sendGlobalRequest(b'tcpip-forward', data,
|
||||
wantReply=1)
|
||||
log.msg('requesting remote forwarding {}:{}'.format(
|
||||
remotePort, hostport))
|
||||
d.addCallback(self._cbRemoteForwarding, remotePort, hostport)
|
||||
d.addErrback(self._ebRemoteForwarding, remotePort, hostport)
|
||||
|
||||
|
||||
def _cbRemoteForwarding(self, result, remotePort, hostport):
|
||||
log.msg('accepted remote forwarding {}:{}'.format(
|
||||
remotePort, hostport))
|
||||
self.remoteForwards[remotePort] = hostport
|
||||
log.msg(repr(self.remoteForwards))
|
||||
|
||||
|
||||
def _ebRemoteForwarding(self, f, remotePort, hostport):
|
||||
log.msg('remote forwarding {}:{} failed'.format(
|
||||
remotePort, hostport))
|
||||
log.msg(f)
|
||||
|
||||
|
||||
def cancelRemoteForwarding(self, remotePort):
|
||||
data = forwarding.packGlobal_tcpip_forward(('0.0.0.0', remotePort))
|
||||
self.sendGlobalRequest(b'cancel-tcpip-forward', data)
|
||||
log.msg('cancelling remote forwarding {}'.format(remotePort))
|
||||
try:
|
||||
del self.remoteForwards[remotePort]
|
||||
except Exception:
|
||||
pass
|
||||
log.msg(repr(self.remoteForwards))
|
||||
|
||||
|
||||
def channel_forwarded_tcpip(self, windowSize, maxPacket, data):
|
||||
log.msg('FTCP {!r}'.format(data))
|
||||
remoteHP, origHP = forwarding.unpackOpen_forwarded_tcpip(data)
|
||||
log.msg(self.remoteForwards)
|
||||
log.msg(remoteHP)
|
||||
if remoteHP[1] in self.remoteForwards:
|
||||
connectHP = self.remoteForwards[remoteHP[1]]
|
||||
log.msg('connect forwarding {}'.format(connectHP))
|
||||
return SSHConnectForwardingChannel(connectHP,
|
||||
remoteWindow=windowSize,
|
||||
remoteMaxPacket=maxPacket,
|
||||
conn=self)
|
||||
else:
|
||||
raise ConchError(connection.OPEN_CONNECT_FAILED,
|
||||
"don't know about that port")
|
||||
|
||||
|
||||
def channelClosed(self, channel):
|
||||
log.msg('connection closing {}'.format(channel))
|
||||
log.msg(self.channels)
|
||||
if len(self.channels) == 1: # Just us left
|
||||
log.msg('stopping connection')
|
||||
stopConnection()
|
||||
else:
|
||||
# Because of the unix thing
|
||||
self.__class__.__bases__[0].channelClosed(self, channel)
|
||||
|
||||
|
||||
|
||||
class SSHSession(channel.SSHChannel):
|
||||
|
||||
name = b'session'
|
||||
|
||||
def channelOpen(self, foo):
|
||||
log.msg('session {} open'.format(self.id))
|
||||
if options['agent']:
|
||||
d = self.conn.sendRequest(self, b'auth-agent-req@openssh.com',
|
||||
b'', wantReply=1)
|
||||
d.addBoth(lambda x: log.msg(x))
|
||||
if options['noshell']:
|
||||
return
|
||||
if (options['command'] and options['tty']) or not options['notty']:
|
||||
_enterRawMode()
|
||||
c = session.SSHSessionClient()
|
||||
if options['escape'] and not options['notty']:
|
||||
self.escapeMode = 1
|
||||
c.dataReceived = self.handleInput
|
||||
else:
|
||||
c.dataReceived = self.write
|
||||
c.connectionLost = lambda x: self.sendEOF()
|
||||
self.stdio = stdio.StandardIO(c)
|
||||
fd = 0
|
||||
if options['subsystem']:
|
||||
self.conn.sendRequest(self, b'subsystem',
|
||||
common.NS(options['command']))
|
||||
elif options['command']:
|
||||
if options['tty']:
|
||||
term = os.environ['TERM']
|
||||
winsz = fcntl.ioctl(fd, tty.TIOCGWINSZ, '12345678')
|
||||
winSize = struct.unpack('4H', winsz)
|
||||
ptyReqData = session.packRequest_pty_req(term, winSize, '')
|
||||
self.conn.sendRequest(self, b'pty-req', ptyReqData)
|
||||
signal.signal(signal.SIGWINCH, self._windowResized)
|
||||
self.conn.sendRequest(self, b'exec', common.NS(options['command']))
|
||||
else:
|
||||
if not options['notty']:
|
||||
term = os.environ['TERM']
|
||||
winsz = fcntl.ioctl(fd, tty.TIOCGWINSZ, '12345678')
|
||||
winSize = struct.unpack('4H', winsz)
|
||||
ptyReqData = session.packRequest_pty_req(term, winSize, '')
|
||||
self.conn.sendRequest(self, b'pty-req', ptyReqData)
|
||||
signal.signal(signal.SIGWINCH, self._windowResized)
|
||||
self.conn.sendRequest(self, b'shell', b'')
|
||||
#if hasattr(conn.transport, 'transport'):
|
||||
# conn.transport.transport.setTcpNoDelay(1)
|
||||
|
||||
|
||||
def handleInput(self, char):
|
||||
if char in (b'\n', b'\r'):
|
||||
self.escapeMode = 1
|
||||
self.write(char)
|
||||
elif self.escapeMode == 1 and char == options['escape']:
|
||||
self.escapeMode = 2
|
||||
elif self.escapeMode == 2:
|
||||
self.escapeMode = 1 # So we can chain escapes together
|
||||
if char == b'.': # Disconnect
|
||||
log.msg('disconnecting from escape')
|
||||
stopConnection()
|
||||
return
|
||||
elif char == b'\x1a': # ^Z, suspend
|
||||
def _():
|
||||
_leaveRawMode()
|
||||
sys.stdout.flush()
|
||||
sys.stdin.flush()
|
||||
os.kill(os.getpid(), signal.SIGTSTP)
|
||||
_enterRawMode()
|
||||
reactor.callLater(0, _)
|
||||
return
|
||||
elif char == b'R': # Rekey connection
|
||||
log.msg('rekeying connection')
|
||||
self.conn.transport.sendKexInit()
|
||||
return
|
||||
elif char == b'#': # Display connections
|
||||
self.stdio.write(
|
||||
b'\r\nThe following connections are open:\r\n')
|
||||
channels = self.conn.channels.keys()
|
||||
channels.sort()
|
||||
for channelId in channels:
|
||||
self.stdio.write(networkString(' #{} {}\r\n'.format(
|
||||
channelId,
|
||||
self.conn.channels[channelId])))
|
||||
return
|
||||
self.write(b'~' + char)
|
||||
else:
|
||||
self.escapeMode = 0
|
||||
self.write(char)
|
||||
|
||||
|
||||
def dataReceived(self, data):
|
||||
self.stdio.write(data)
|
||||
|
||||
|
||||
def extReceived(self, t, data):
|
||||
if t == connection.EXTENDED_DATA_STDERR:
|
||||
log.msg('got {} stderr data'.format(len(data)))
|
||||
if ioType(sys.stderr) == unicode:
|
||||
sys.stderr.buffer.write(data)
|
||||
else:
|
||||
sys.stderr.write(data)
|
||||
|
||||
|
||||
def eofReceived(self):
|
||||
log.msg('got eof')
|
||||
self.stdio.loseWriteConnection()
|
||||
|
||||
|
||||
def closeReceived(self):
|
||||
log.msg('remote side closed {}'.format(self))
|
||||
self.conn.sendClose(self)
|
||||
|
||||
|
||||
def closed(self):
|
||||
global old
|
||||
log.msg('closed {}'.format(self))
|
||||
log.msg(repr(self.conn.channels))
|
||||
|
||||
|
||||
def request_exit_status(self, data):
|
||||
global exitStatus
|
||||
exitStatus = int(struct.unpack('>L', data)[0])
|
||||
log.msg('exit status: {}'.format(exitStatus))
|
||||
|
||||
|
||||
def sendEOF(self):
|
||||
self.conn.sendEOF(self)
|
||||
|
||||
|
||||
def stopWriting(self):
|
||||
self.stdio.pauseProducing()
|
||||
|
||||
|
||||
def startWriting(self):
|
||||
self.stdio.resumeProducing()
|
||||
|
||||
|
||||
def _windowResized(self, *args):
|
||||
winsz = fcntl.ioctl(0, tty.TIOCGWINSZ, '12345678')
|
||||
winSize = struct.unpack('4H', winsz)
|
||||
newSize = winSize[1], winSize[0], winSize[2], winSize[3]
|
||||
self.conn.sendRequest(self, b'window-change', struct.pack('!4L', *newSize))
|
||||
|
||||
|
||||
|
||||
class SSHListenClientForwardingChannel(forwarding.SSHListenClientForwardingChannel): pass
|
||||
class SSHConnectForwardingChannel(forwarding.SSHConnectForwardingChannel): pass
|
||||
|
||||
|
||||
|
||||
def _leaveRawMode():
|
||||
global _inRawMode
|
||||
if not _inRawMode:
|
||||
return
|
||||
fd = sys.stdin.fileno()
|
||||
tty.tcsetattr(fd, tty.TCSANOW, _savedRawMode)
|
||||
_inRawMode = 0
|
||||
|
||||
|
||||
|
||||
def _enterRawMode():
|
||||
global _inRawMode, _savedRawMode
|
||||
if _inRawMode:
|
||||
return
|
||||
fd = sys.stdin.fileno()
|
||||
try:
|
||||
old = tty.tcgetattr(fd)
|
||||
new = old[:]
|
||||
except:
|
||||
log.msg('not a typewriter!')
|
||||
else:
|
||||
# iflage
|
||||
new[0] = new[0] | tty.IGNPAR
|
||||
new[0] = new[0] & ~(tty.ISTRIP | tty.INLCR | tty.IGNCR | tty.ICRNL |
|
||||
tty.IXON | tty.IXANY | tty.IXOFF)
|
||||
if hasattr(tty, 'IUCLC'):
|
||||
new[0] = new[0] & ~tty.IUCLC
|
||||
|
||||
# lflag
|
||||
new[3] = new[3] & ~(tty.ISIG | tty.ICANON | tty.ECHO | tty.ECHO |
|
||||
tty.ECHOE | tty.ECHOK | tty.ECHONL)
|
||||
if hasattr(tty, 'IEXTEN'):
|
||||
new[3] = new[3] & ~tty.IEXTEN
|
||||
|
||||
#oflag
|
||||
new[1] = new[1] & ~tty.OPOST
|
||||
|
||||
new[6][tty.VMIN] = 1
|
||||
new[6][tty.VTIME] = 0
|
||||
|
||||
_savedRawMode = old
|
||||
tty.tcsetattr(fd, tty.TCSANOW, new)
|
||||
#tty.setraw(fd)
|
||||
_inRawMode = 1
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
|
|
@ -0,0 +1,586 @@
|
|||
# -*- test-case-name: twisted.conch.test.test_scripts -*-
|
||||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
Implementation module for the `tkconch` command.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from twisted.conch import error
|
||||
from twisted.conch.ui import tkvt100
|
||||
from twisted.conch.ssh import transport, userauth, connection, common, keys
|
||||
from twisted.conch.ssh import session, forwarding, channel
|
||||
from twisted.conch.client.default import isInKnownHosts
|
||||
from twisted.internet import reactor, defer, protocol, tksupport
|
||||
from twisted.python import usage, log
|
||||
from twisted.python.compat import _PY3
|
||||
|
||||
import os, sys, getpass, struct, base64, signal
|
||||
|
||||
if _PY3:
|
||||
import tkinter as Tkinter
|
||||
import tkinter.filedialog as tkFileDialog
|
||||
import tkinter.messagebox as tkMessageBox
|
||||
else:
|
||||
import Tkinter, tkFileDialog, tkMessageBox
|
||||
|
||||
class TkConchMenu(Tkinter.Frame):
|
||||
def __init__(self, *args, **params):
|
||||
## Standard heading: initialization
|
||||
Tkinter.Frame.__init__(self, *args, **params)
|
||||
|
||||
self.master.title('TkConch')
|
||||
self.localRemoteVar = Tkinter.StringVar()
|
||||
self.localRemoteVar.set('local')
|
||||
|
||||
Tkinter.Label(self, anchor='w', justify='left', text='Hostname').grid(column=1, row=1, sticky='w')
|
||||
self.host = Tkinter.Entry(self)
|
||||
self.host.grid(column=2, columnspan=2, row=1, sticky='nesw')
|
||||
|
||||
Tkinter.Label(self, anchor='w', justify='left', text='Port').grid(column=1, row=2, sticky='w')
|
||||
self.port = Tkinter.Entry(self)
|
||||
self.port.grid(column=2, columnspan=2, row=2, sticky='nesw')
|
||||
|
||||
Tkinter.Label(self, anchor='w', justify='left', text='Username').grid(column=1, row=3, sticky='w')
|
||||
self.user = Tkinter.Entry(self)
|
||||
self.user.grid(column=2, columnspan=2, row=3, sticky='nesw')
|
||||
|
||||
Tkinter.Label(self, anchor='w', justify='left', text='Command').grid(column=1, row=4, sticky='w')
|
||||
self.command = Tkinter.Entry(self)
|
||||
self.command.grid(column=2, columnspan=2, row=4, sticky='nesw')
|
||||
|
||||
Tkinter.Label(self, anchor='w', justify='left', text='Identity').grid(column=1, row=5, sticky='w')
|
||||
self.identity = Tkinter.Entry(self)
|
||||
self.identity.grid(column=2, row=5, sticky='nesw')
|
||||
Tkinter.Button(self, command=self.getIdentityFile, text='Browse').grid(column=3, row=5, sticky='nesw')
|
||||
|
||||
Tkinter.Label(self, text='Port Forwarding').grid(column=1, row=6, sticky='w')
|
||||
self.forwards = Tkinter.Listbox(self, height=0, width=0)
|
||||
self.forwards.grid(column=2, columnspan=2, row=6, sticky='nesw')
|
||||
Tkinter.Button(self, text='Add', command=self.addForward).grid(column=1, row=7)
|
||||
Tkinter.Button(self, text='Remove', command=self.removeForward).grid(column=1, row=8)
|
||||
self.forwardPort = Tkinter.Entry(self)
|
||||
self.forwardPort.grid(column=2, row=7, sticky='nesw')
|
||||
Tkinter.Label(self, text='Port').grid(column=3, row=7, sticky='nesw')
|
||||
self.forwardHost = Tkinter.Entry(self)
|
||||
self.forwardHost.grid(column=2, row=8, sticky='nesw')
|
||||
Tkinter.Label(self, text='Host').grid(column=3, row=8, sticky='nesw')
|
||||
self.localForward = Tkinter.Radiobutton(self, text='Local', variable=self.localRemoteVar, value='local')
|
||||
self.localForward.grid(column=2, row=9)
|
||||
self.remoteForward = Tkinter.Radiobutton(self, text='Remote', variable=self.localRemoteVar, value='remote')
|
||||
self.remoteForward.grid(column=3, row=9)
|
||||
|
||||
Tkinter.Label(self, text='Advanced Options').grid(column=1, columnspan=3, row=10, sticky='nesw')
|
||||
|
||||
Tkinter.Label(self, anchor='w', justify='left', text='Cipher').grid(column=1, row=11, sticky='w')
|
||||
self.cipher = Tkinter.Entry(self, name='cipher')
|
||||
self.cipher.grid(column=2, columnspan=2, row=11, sticky='nesw')
|
||||
|
||||
Tkinter.Label(self, anchor='w', justify='left', text='MAC').grid(column=1, row=12, sticky='w')
|
||||
self.mac = Tkinter.Entry(self, name='mac')
|
||||
self.mac.grid(column=2, columnspan=2, row=12, sticky='nesw')
|
||||
|
||||
Tkinter.Label(self, anchor='w', justify='left', text='Escape Char').grid(column=1, row=13, sticky='w')
|
||||
self.escape = Tkinter.Entry(self, name='escape')
|
||||
self.escape.grid(column=2, columnspan=2, row=13, sticky='nesw')
|
||||
Tkinter.Button(self, text='Connect!', command=self.doConnect).grid(column=1, columnspan=3, row=14, sticky='nesw')
|
||||
|
||||
# Resize behavior(s)
|
||||
self.grid_rowconfigure(6, weight=1, minsize=64)
|
||||
self.grid_columnconfigure(2, weight=1, minsize=2)
|
||||
|
||||
self.master.protocol("WM_DELETE_WINDOW", sys.exit)
|
||||
|
||||
|
||||
def getIdentityFile(self):
|
||||
r = tkFileDialog.askopenfilename()
|
||||
if r:
|
||||
self.identity.delete(0, Tkinter.END)
|
||||
self.identity.insert(Tkinter.END, r)
|
||||
|
||||
def addForward(self):
|
||||
port = self.forwardPort.get()
|
||||
self.forwardPort.delete(0, Tkinter.END)
|
||||
host = self.forwardHost.get()
|
||||
self.forwardHost.delete(0, Tkinter.END)
|
||||
if self.localRemoteVar.get() == 'local':
|
||||
self.forwards.insert(Tkinter.END, 'L:%s:%s' % (port, host))
|
||||
else:
|
||||
self.forwards.insert(Tkinter.END, 'R:%s:%s' % (port, host))
|
||||
|
||||
def removeForward(self):
|
||||
cur = self.forwards.curselection()
|
||||
if cur:
|
||||
self.forwards.remove(cur[0])
|
||||
|
||||
def doConnect(self):
|
||||
finished = 1
|
||||
options['host'] = self.host.get()
|
||||
options['port'] = self.port.get()
|
||||
options['user'] = self.user.get()
|
||||
options['command'] = self.command.get()
|
||||
cipher = self.cipher.get()
|
||||
mac = self.mac.get()
|
||||
escape = self.escape.get()
|
||||
if cipher:
|
||||
if cipher in SSHClientTransport.supportedCiphers:
|
||||
SSHClientTransport.supportedCiphers = [cipher]
|
||||
else:
|
||||
tkMessageBox.showerror('TkConch', 'Bad cipher.')
|
||||
finished = 0
|
||||
|
||||
if mac:
|
||||
if mac in SSHClientTransport.supportedMACs:
|
||||
SSHClientTransport.supportedMACs = [mac]
|
||||
elif finished:
|
||||
tkMessageBox.showerror('TkConch', 'Bad MAC.')
|
||||
finished = 0
|
||||
|
||||
if escape:
|
||||
if escape == 'none':
|
||||
options['escape'] = None
|
||||
elif escape[0] == '^' and len(escape) == 2:
|
||||
options['escape'] = chr(ord(escape[1])-64)
|
||||
elif len(escape) == 1:
|
||||
options['escape'] = escape
|
||||
elif finished:
|
||||
tkMessageBox.showerror('TkConch', "Bad escape character '%s'." % escape)
|
||||
finished = 0
|
||||
|
||||
if self.identity.get():
|
||||
options.identitys.append(self.identity.get())
|
||||
|
||||
for line in self.forwards.get(0,Tkinter.END):
|
||||
if line[0]=='L':
|
||||
options.opt_localforward(line[2:])
|
||||
else:
|
||||
options.opt_remoteforward(line[2:])
|
||||
|
||||
if '@' in options['host']:
|
||||
options['user'], options['host'] = options['host'].split('@',1)
|
||||
|
||||
if (not options['host'] or not options['user']) and finished:
|
||||
tkMessageBox.showerror('TkConch', 'Missing host or username.')
|
||||
finished = 0
|
||||
if finished:
|
||||
self.master.quit()
|
||||
self.master.destroy()
|
||||
if options['log']:
|
||||
realout = sys.stdout
|
||||
log.startLogging(sys.stderr)
|
||||
sys.stdout = realout
|
||||
else:
|
||||
log.discardLogs()
|
||||
log.deferr = handleError # HACK
|
||||
if not options.identitys:
|
||||
options.identitys = ['~/.ssh/id_rsa', '~/.ssh/id_dsa']
|
||||
host = options['host']
|
||||
port = int(options['port'] or 22)
|
||||
log.msg((host,port))
|
||||
reactor.connectTCP(host, port, SSHClientFactory())
|
||||
frame.master.deiconify()
|
||||
frame.master.title('%s@%s - TkConch' % (options['user'], options['host']))
|
||||
else:
|
||||
self.focus()
|
||||
|
||||
class GeneralOptions(usage.Options):
|
||||
synopsis = """Usage: tkconch [options] host [command]
|
||||
"""
|
||||
|
||||
optParameters = [['user', 'l', None, 'Log in using this user name.'],
|
||||
['identity', 'i', '~/.ssh/identity', 'Identity for public key authentication'],
|
||||
['escape', 'e', '~', "Set escape character; ``none'' = disable"],
|
||||
['cipher', 'c', None, 'Select encryption algorithm.'],
|
||||
['macs', 'm', None, 'Specify MAC algorithms for protocol version 2.'],
|
||||
['port', 'p', None, 'Connect to this port. Server must be on the same port.'],
|
||||
['localforward', 'L', None, 'listen-port:host:port Forward local port to remote address'],
|
||||
['remoteforward', 'R', None, 'listen-port:host:port Forward remote port to local address'],
|
||||
]
|
||||
|
||||
optFlags = [['tty', 't', 'Tty; allocate a tty even if command is given.'],
|
||||
['notty', 'T', 'Do not allocate a tty.'],
|
||||
['version', 'V', 'Display version number only.'],
|
||||
['compress', 'C', 'Enable compression.'],
|
||||
['noshell', 'N', 'Do not execute a shell or command.'],
|
||||
['subsystem', 's', 'Invoke command (mandatory) as SSH2 subsystem.'],
|
||||
['log', 'v', 'Log to stderr'],
|
||||
['ansilog', 'a', 'Print the received data to stdout']]
|
||||
|
||||
_ciphers = transport.SSHClientTransport.supportedCiphers
|
||||
_macs = transport.SSHClientTransport.supportedMACs
|
||||
|
||||
compData = usage.Completions(
|
||||
mutuallyExclusive=[("tty", "notty")],
|
||||
optActions={
|
||||
"cipher": usage.CompleteList(_ciphers),
|
||||
"macs": usage.CompleteList(_macs),
|
||||
"localforward": usage.Completer(descr="listen-port:host:port"),
|
||||
"remoteforward": usage.Completer(descr="listen-port:host:port")},
|
||||
extraActions=[usage.CompleteUserAtHost(),
|
||||
usage.Completer(descr="command"),
|
||||
usage.Completer(descr="argument", repeat=True)]
|
||||
)
|
||||
|
||||
identitys = []
|
||||
localForwards = []
|
||||
remoteForwards = []
|
||||
|
||||
def opt_identity(self, i):
|
||||
self.identitys.append(i)
|
||||
|
||||
def opt_localforward(self, f):
|
||||
localPort, remoteHost, remotePort = f.split(':') # doesn't do v6 yet
|
||||
localPort = int(localPort)
|
||||
remotePort = int(remotePort)
|
||||
self.localForwards.append((localPort, (remoteHost, remotePort)))
|
||||
|
||||
def opt_remoteforward(self, f):
|
||||
remotePort, connHost, connPort = f.split(':') # doesn't do v6 yet
|
||||
remotePort = int(remotePort)
|
||||
connPort = int(connPort)
|
||||
self.remoteForwards.append((remotePort, (connHost, connPort)))
|
||||
|
||||
def opt_compress(self):
|
||||
SSHClientTransport.supportedCompressions[0:1] = ['zlib']
|
||||
|
||||
def parseArgs(self, *args):
|
||||
if args:
|
||||
self['host'] = args[0]
|
||||
self['command'] = ' '.join(args[1:])
|
||||
else:
|
||||
self['host'] = ''
|
||||
self['command'] = ''
|
||||
|
||||
# Rest of code in "run"
|
||||
options = None
|
||||
menu = None
|
||||
exitStatus = 0
|
||||
frame = None
|
||||
|
||||
def deferredAskFrame(question, echo):
|
||||
if frame.callback:
|
||||
raise ValueError("can't ask 2 questions at once!")
|
||||
d = defer.Deferred()
|
||||
resp = []
|
||||
def gotChar(ch, resp=resp):
|
||||
if not ch: return
|
||||
if ch=='\x03': # C-c
|
||||
reactor.stop()
|
||||
if ch=='\r':
|
||||
frame.write('\r\n')
|
||||
stresp = ''.join(resp)
|
||||
del resp
|
||||
frame.callback = None
|
||||
d.callback(stresp)
|
||||
return
|
||||
elif 32 <= ord(ch) < 127:
|
||||
resp.append(ch)
|
||||
if echo:
|
||||
frame.write(ch)
|
||||
elif ord(ch) == 8 and resp: # BS
|
||||
if echo: frame.write('\x08 \x08')
|
||||
resp.pop()
|
||||
frame.callback = gotChar
|
||||
frame.write(question)
|
||||
frame.canvas.focus_force()
|
||||
return d
|
||||
|
||||
def run():
|
||||
global menu, options, frame
|
||||
args = sys.argv[1:]
|
||||
if '-l' in args: # cvs is an idiot
|
||||
i = args.index('-l')
|
||||
args = args[i:i+2]+args
|
||||
del args[i+2:i+4]
|
||||
for arg in args[:]:
|
||||
try:
|
||||
i = args.index(arg)
|
||||
if arg[:2] == '-o' and args[i+1][0]!='-':
|
||||
args[i:i+2] = [] # suck on it scp
|
||||
except ValueError:
|
||||
pass
|
||||
root = Tkinter.Tk()
|
||||
root.withdraw()
|
||||
top = Tkinter.Toplevel()
|
||||
menu = TkConchMenu(top)
|
||||
menu.pack(side=Tkinter.TOP, fill=Tkinter.BOTH, expand=1)
|
||||
options = GeneralOptions()
|
||||
try:
|
||||
options.parseOptions(args)
|
||||
except usage.UsageError as u:
|
||||
print('ERROR: %s' % u)
|
||||
options.opt_help()
|
||||
sys.exit(1)
|
||||
for k,v in options.items():
|
||||
if v and hasattr(menu, k):
|
||||
getattr(menu,k).insert(Tkinter.END, v)
|
||||
for (p, (rh, rp)) in options.localForwards:
|
||||
menu.forwards.insert(Tkinter.END, 'L:%s:%s:%s' % (p, rh, rp))
|
||||
options.localForwards = []
|
||||
for (p, (rh, rp)) in options.remoteForwards:
|
||||
menu.forwards.insert(Tkinter.END, 'R:%s:%s:%s' % (p, rh, rp))
|
||||
options.remoteForwards = []
|
||||
frame = tkvt100.VT100Frame(root, callback=None)
|
||||
root.geometry('%dx%d'%(tkvt100.fontWidth*frame.width+3, tkvt100.fontHeight*frame.height+3))
|
||||
frame.pack(side = Tkinter.TOP)
|
||||
tksupport.install(root)
|
||||
root.withdraw()
|
||||
if (options['host'] and options['user']) or '@' in options['host']:
|
||||
menu.doConnect()
|
||||
else:
|
||||
top.mainloop()
|
||||
reactor.run()
|
||||
sys.exit(exitStatus)
|
||||
|
||||
def handleError():
|
||||
from twisted.python import failure
|
||||
global exitStatus
|
||||
exitStatus = 2
|
||||
log.err(failure.Failure())
|
||||
reactor.stop()
|
||||
raise
|
||||
|
||||
class SSHClientFactory(protocol.ClientFactory):
|
||||
noisy = 1
|
||||
|
||||
def stopFactory(self):
|
||||
reactor.stop()
|
||||
|
||||
def buildProtocol(self, addr):
|
||||
return SSHClientTransport()
|
||||
|
||||
def clientConnectionFailed(self, connector, reason):
|
||||
tkMessageBox.showwarning('TkConch','Connection Failed, Reason:\n %s: %s' % (reason.type, reason.value))
|
||||
|
||||
class SSHClientTransport(transport.SSHClientTransport):
|
||||
|
||||
def receiveError(self, code, desc):
|
||||
global exitStatus
|
||||
exitStatus = 'conch:\tRemote side disconnected with error code %i\nconch:\treason: %s' % (code, desc)
|
||||
|
||||
def sendDisconnect(self, code, reason):
|
||||
global exitStatus
|
||||
exitStatus = 'conch:\tSending disconnect with error code %i\nconch:\treason: %s' % (code, reason)
|
||||
transport.SSHClientTransport.sendDisconnect(self, code, reason)
|
||||
|
||||
def receiveDebug(self, alwaysDisplay, message, lang):
|
||||
global options
|
||||
if alwaysDisplay or options['log']:
|
||||
log.msg('Received Debug Message: %s' % message)
|
||||
|
||||
def verifyHostKey(self, pubKey, fingerprint):
|
||||
#d = defer.Deferred()
|
||||
#d.addCallback(lambda x:defer.succeed(1))
|
||||
#d.callback(2)
|
||||
#return d
|
||||
goodKey = isInKnownHosts(options['host'], pubKey, {'known-hosts': None})
|
||||
if goodKey == 1: # good key
|
||||
return defer.succeed(1)
|
||||
elif goodKey == 2: # AAHHHHH changed
|
||||
return defer.fail(error.ConchError('bad host key'))
|
||||
else:
|
||||
if options['host'] == self.transport.getPeer().host:
|
||||
host = options['host']
|
||||
khHost = options['host']
|
||||
else:
|
||||
host = '%s (%s)' % (options['host'],
|
||||
self.transport.getPeer().host)
|
||||
khHost = '%s,%s' % (options['host'],
|
||||
self.transport.getPeer().host)
|
||||
keyType = common.getNS(pubKey)[0]
|
||||
ques = """The authenticity of host '%s' can't be established.\r
|
||||
%s key fingerprint is %s.""" % (host,
|
||||
{b'ssh-dss':'DSA', b'ssh-rsa':'RSA'}[keyType],
|
||||
fingerprint)
|
||||
ques+='\r\nAre you sure you want to continue connecting (yes/no)? '
|
||||
return deferredAskFrame(ques, 1).addCallback(self._cbVerifyHostKey, pubKey, khHost, keyType)
|
||||
|
||||
def _cbVerifyHostKey(self, ans, pubKey, khHost, keyType):
|
||||
if ans.lower() not in ('yes', 'no'):
|
||||
return deferredAskFrame("Please type 'yes' or 'no': ",1).addCallback(self._cbVerifyHostKey, pubKey, khHost, keyType)
|
||||
if ans.lower() == 'no':
|
||||
frame.write('Host key verification failed.\r\n')
|
||||
raise error.ConchError('bad host key')
|
||||
try:
|
||||
frame.write(
|
||||
"Warning: Permanently added '%s' (%s) to the list of "
|
||||
"known hosts.\r\n" %
|
||||
(khHost, {b'ssh-dss':'DSA', b'ssh-rsa':'RSA'}[keyType]))
|
||||
with open(os.path.expanduser('~/.ssh/known_hosts'), 'a') as known_hosts:
|
||||
encodedKey = base64.encodestring(pubKey).replace(b'\n', b'')
|
||||
known_hosts.write('\n%s %s %s' % (khHost, keyType, encodedKey))
|
||||
except:
|
||||
log.deferr()
|
||||
raise error.ConchError
|
||||
|
||||
def connectionSecure(self):
|
||||
if options['user']:
|
||||
user = options['user']
|
||||
else:
|
||||
user = getpass.getuser()
|
||||
self.requestService(SSHUserAuthClient(user, SSHConnection()))
|
||||
|
||||
class SSHUserAuthClient(userauth.SSHUserAuthClient):
|
||||
usedFiles = []
|
||||
|
||||
def getPassword(self, prompt = None):
|
||||
if not prompt:
|
||||
prompt = "%s@%s's password: " % (self.user, options['host'])
|
||||
return deferredAskFrame(prompt,0)
|
||||
|
||||
def getPublicKey(self):
|
||||
files = [x for x in options.identitys if x not in self.usedFiles]
|
||||
if not files:
|
||||
return None
|
||||
file = files[0]
|
||||
log.msg(file)
|
||||
self.usedFiles.append(file)
|
||||
file = os.path.expanduser(file)
|
||||
file += '.pub'
|
||||
if not os.path.exists(file):
|
||||
return
|
||||
try:
|
||||
return keys.Key.fromFile(file).blob()
|
||||
except:
|
||||
return self.getPublicKey() # try again
|
||||
|
||||
def getPrivateKey(self):
|
||||
file = os.path.expanduser(self.usedFiles[-1])
|
||||
if not os.path.exists(file):
|
||||
return None
|
||||
try:
|
||||
return defer.succeed(keys.Key.fromFile(file).keyObject)
|
||||
except keys.BadKeyError as e:
|
||||
if e.args[0] == 'encrypted key with no password':
|
||||
prompt = "Enter passphrase for key '%s': " % \
|
||||
self.usedFiles[-1]
|
||||
return deferredAskFrame(prompt, 0).addCallback(self._cbGetPrivateKey, 0)
|
||||
def _cbGetPrivateKey(self, ans, count):
|
||||
file = os.path.expanduser(self.usedFiles[-1])
|
||||
try:
|
||||
return keys.Key.fromFile(file, password = ans).keyObject
|
||||
except keys.BadKeyError:
|
||||
if count == 2:
|
||||
raise
|
||||
prompt = "Enter passphrase for key '%s': " % \
|
||||
self.usedFiles[-1]
|
||||
return deferredAskFrame(prompt, 0).addCallback(self._cbGetPrivateKey, count+1)
|
||||
|
||||
class SSHConnection(connection.SSHConnection):
|
||||
def serviceStarted(self):
|
||||
if not options['noshell']:
|
||||
self.openChannel(SSHSession())
|
||||
if options.localForwards:
|
||||
for localPort, hostport in options.localForwards:
|
||||
reactor.listenTCP(localPort,
|
||||
forwarding.SSHListenForwardingFactory(self,
|
||||
hostport,
|
||||
forwarding.SSHListenClientForwardingChannel))
|
||||
if options.remoteForwards:
|
||||
for remotePort, hostport in options.remoteForwards:
|
||||
log.msg('asking for remote forwarding for %s:%s' %
|
||||
(remotePort, hostport))
|
||||
data = forwarding.packGlobal_tcpip_forward(
|
||||
('0.0.0.0', remotePort))
|
||||
self.sendGlobalRequest('tcpip-forward', data)
|
||||
self.remoteForwards[remotePort] = hostport
|
||||
|
||||
class SSHSession(channel.SSHChannel):
|
||||
|
||||
name = b'session'
|
||||
|
||||
def channelOpen(self, foo):
|
||||
#global globalSession
|
||||
#globalSession = self
|
||||
# turn off local echo
|
||||
self.escapeMode = 1
|
||||
c = session.SSHSessionClient()
|
||||
if options['escape']:
|
||||
c.dataReceived = self.handleInput
|
||||
else:
|
||||
c.dataReceived = self.write
|
||||
c.connectionLost = self.sendEOF
|
||||
frame.callback = c.dataReceived
|
||||
frame.canvas.focus_force()
|
||||
if options['subsystem']:
|
||||
self.conn.sendRequest(self, b'subsystem', \
|
||||
common.NS(options['command']))
|
||||
elif options['command']:
|
||||
if options['tty']:
|
||||
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, '')
|
||||
self.conn.sendRequest(self, b'pty-req', ptyReqData)
|
||||
self.conn.sendRequest(self, 'exec', \
|
||||
common.NS(options['command']))
|
||||
else:
|
||||
if not options['notty']:
|
||||
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, '')
|
||||
self.conn.sendRequest(self, b'pty-req', ptyReqData)
|
||||
self.conn.sendRequest(self, b'shell', b'')
|
||||
self.conn.transport.transport.setTcpNoDelay(1)
|
||||
|
||||
def handleInput(self, char):
|
||||
#log.msg('handling %s' % repr(char))
|
||||
if char in ('\n', '\r'):
|
||||
self.escapeMode = 1
|
||||
self.write(char)
|
||||
elif self.escapeMode == 1 and char == options['escape']:
|
||||
self.escapeMode = 2
|
||||
elif self.escapeMode == 2:
|
||||
self.escapeMode = 1 # so we can chain escapes together
|
||||
if char == '.': # disconnect
|
||||
log.msg('disconnecting from escape')
|
||||
reactor.stop()
|
||||
return
|
||||
elif char == '\x1a': # ^Z, suspend
|
||||
# following line courtesy of Erwin@freenode
|
||||
os.kill(os.getpid(), signal.SIGSTOP)
|
||||
return
|
||||
elif char == 'R': # rekey connection
|
||||
log.msg('rekeying connection')
|
||||
self.conn.transport.sendKexInit()
|
||||
return
|
||||
self.write('~' + char)
|
||||
else:
|
||||
self.escapeMode = 0
|
||||
self.write(char)
|
||||
|
||||
def dataReceived(self, data):
|
||||
if _PY3 and isinstance(data, bytes):
|
||||
data = data.decode("utf-8")
|
||||
if options['ansilog']:
|
||||
print(repr(data))
|
||||
frame.write(data)
|
||||
|
||||
def extReceived(self, t, data):
|
||||
if t==connection.EXTENDED_DATA_STDERR:
|
||||
log.msg('got %s stderr data' % len(data))
|
||||
sys.stderr.write(data)
|
||||
sys.stderr.flush()
|
||||
|
||||
def eofReceived(self):
|
||||
log.msg('got eof')
|
||||
sys.stdin.close()
|
||||
|
||||
def closed(self):
|
||||
log.msg('closed %s' % self)
|
||||
if len(self.conn.channels) == 1: # just us left
|
||||
reactor.stop()
|
||||
|
||||
def request_exit_status(self, data):
|
||||
global exitStatus
|
||||
exitStatus = int(struct.unpack('>L', data)[0])
|
||||
log.msg('exit status: %s' % exitStatus)
|
||||
|
||||
def sendEOF(self):
|
||||
self.conn.sendEOF(self)
|
||||
|
||||
if __name__=="__main__":
|
||||
run()
|
||||
Loading…
Add table
Add a link
Reference in a new issue