123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- # Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
- #
- # This file is part of paramiko.
- #
- # Paramiko is free software; you can redistribute it and/or modify it under the
- # terms of the GNU Lesser General Public License as published by the Free
- # Software Foundation; either version 2.1 of the License, or (at your option)
- # any later version.
- #
- # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
- # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
- # details.
- #
- # You should have received a copy of the GNU Lesser General Public License
- # along with Paramiko; if not, write to the Free Software Foundation, Inc.,
- # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- import select
- import socket
- import struct
- from paramiko import util
- from paramiko.common import asbytes, DEBUG
- from paramiko.message import Message
- from paramiko.py3compat import byte_chr, byte_ord
- (
- CMD_INIT,
- CMD_VERSION,
- CMD_OPEN,
- CMD_CLOSE,
- CMD_READ,
- CMD_WRITE,
- CMD_LSTAT,
- CMD_FSTAT,
- CMD_SETSTAT,
- CMD_FSETSTAT,
- CMD_OPENDIR,
- CMD_READDIR,
- CMD_REMOVE,
- CMD_MKDIR,
- CMD_RMDIR,
- CMD_REALPATH,
- CMD_STAT,
- CMD_RENAME,
- CMD_READLINK,
- CMD_SYMLINK,
- ) = range(1, 21)
- (CMD_STATUS, CMD_HANDLE, CMD_DATA, CMD_NAME, CMD_ATTRS) = range(101, 106)
- (CMD_EXTENDED, CMD_EXTENDED_REPLY) = range(200, 202)
- SFTP_OK = 0
- (
- SFTP_EOF,
- SFTP_NO_SUCH_FILE,
- SFTP_PERMISSION_DENIED,
- SFTP_FAILURE,
- SFTP_BAD_MESSAGE,
- SFTP_NO_CONNECTION,
- SFTP_CONNECTION_LOST,
- SFTP_OP_UNSUPPORTED,
- ) = range(1, 9)
- SFTP_DESC = [
- "Success",
- "End of file",
- "No such file",
- "Permission denied",
- "Failure",
- "Bad message",
- "No connection",
- "Connection lost",
- "Operation unsupported",
- ]
- SFTP_FLAG_READ = 0x1
- SFTP_FLAG_WRITE = 0x2
- SFTP_FLAG_APPEND = 0x4
- SFTP_FLAG_CREATE = 0x8
- SFTP_FLAG_TRUNC = 0x10
- SFTP_FLAG_EXCL = 0x20
- _VERSION = 3
- # for debugging
- CMD_NAMES = {
- CMD_INIT: "init",
- CMD_VERSION: "version",
- CMD_OPEN: "open",
- CMD_CLOSE: "close",
- CMD_READ: "read",
- CMD_WRITE: "write",
- CMD_LSTAT: "lstat",
- CMD_FSTAT: "fstat",
- CMD_SETSTAT: "setstat",
- CMD_FSETSTAT: "fsetstat",
- CMD_OPENDIR: "opendir",
- CMD_READDIR: "readdir",
- CMD_REMOVE: "remove",
- CMD_MKDIR: "mkdir",
- CMD_RMDIR: "rmdir",
- CMD_REALPATH: "realpath",
- CMD_STAT: "stat",
- CMD_RENAME: "rename",
- CMD_READLINK: "readlink",
- CMD_SYMLINK: "symlink",
- CMD_STATUS: "status",
- CMD_HANDLE: "handle",
- CMD_DATA: "data",
- CMD_NAME: "name",
- CMD_ATTRS: "attrs",
- CMD_EXTENDED: "extended",
- CMD_EXTENDED_REPLY: "extended_reply",
- }
- class SFTPError(Exception):
- pass
- class BaseSFTP(object):
- def __init__(self):
- self.logger = util.get_logger("paramiko.sftp")
- self.sock = None
- self.ultra_debug = False
- # ...internals...
- def _send_version(self):
- self._send_packet(CMD_INIT, struct.pack(">I", _VERSION))
- t, data = self._read_packet()
- if t != CMD_VERSION:
- raise SFTPError("Incompatible sftp protocol")
- version = struct.unpack(">I", data[:4])[0]
- # if version != _VERSION:
- # raise SFTPError('Incompatible sftp protocol')
- return version
- def _send_server_version(self):
- # winscp will freak out if the server sends version info before the
- # client finishes sending INIT.
- t, data = self._read_packet()
- if t != CMD_INIT:
- raise SFTPError("Incompatible sftp protocol")
- version = struct.unpack(">I", data[:4])[0]
- # advertise that we support "check-file"
- extension_pairs = ["check-file", "md5,sha1"]
- msg = Message()
- msg.add_int(_VERSION)
- msg.add(*extension_pairs)
- self._send_packet(CMD_VERSION, msg)
- return version
- def _log(self, level, msg, *args):
- self.logger.log(level, msg, *args)
- def _write_all(self, out):
- while len(out) > 0:
- n = self.sock.send(out)
- if n <= 0:
- raise EOFError()
- if n == len(out):
- return
- out = out[n:]
- return
- def _read_all(self, n):
- out = bytes()
- while n > 0:
- if isinstance(self.sock, socket.socket):
- # sometimes sftp is used directly over a socket instead of
- # through a paramiko channel. in this case, check periodically
- # if the socket is closed. (for some reason, recv() won't ever
- # return or raise an exception, but calling select on a closed
- # socket will.)
- while True:
- read, write, err = select.select([self.sock], [], [], 0.1)
- if len(read) > 0:
- x = self.sock.recv(n)
- break
- else:
- x = self.sock.recv(n)
- if len(x) == 0:
- raise EOFError()
- out += x
- n -= len(x)
- return out
- def _send_packet(self, t, packet):
- packet = asbytes(packet)
- out = struct.pack(">I", len(packet) + 1) + byte_chr(t) + packet
- if self.ultra_debug:
- self._log(DEBUG, util.format_binary(out, "OUT: "))
- self._write_all(out)
- def _read_packet(self):
- x = self._read_all(4)
- # most sftp servers won't accept packets larger than about 32k, so
- # anything with the high byte set (> 16MB) is just garbage.
- if byte_ord(x[0]):
- raise SFTPError("Garbage packet received")
- size = struct.unpack(">I", x)[0]
- data = self._read_all(size)
- if self.ultra_debug:
- self._log(DEBUG, util.format_binary(data, "IN: "))
- if size > 0:
- t = byte_ord(data[0])
- return t, data[1:]
- return 0, bytes()
|