1# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com> 2# 3# This file is part of paramiko. 4# 5# Paramiko is free software; you can redistribute it and/or modify it under the 6# terms of the GNU Lesser General Public License as published by the Free 7# Software Foundation; either version 2.1 of the License, or (at your option) 8# any later version. 9# 10# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 11# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13# details. 14# 15# You should have received a copy of the GNU Lesser General Public License 16# along with Paramiko; if not, write to the Free Software Foundation, Inc., 17# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 18 19import select 20import socket 21import struct 22 23from paramiko import util 24from paramiko.common import asbytes, DEBUG 25from paramiko.message import Message 26from paramiko.py3compat import byte_chr, byte_ord 27 28 29( 30 CMD_INIT, 31 CMD_VERSION, 32 CMD_OPEN, 33 CMD_CLOSE, 34 CMD_READ, 35 CMD_WRITE, 36 CMD_LSTAT, 37 CMD_FSTAT, 38 CMD_SETSTAT, 39 CMD_FSETSTAT, 40 CMD_OPENDIR, 41 CMD_READDIR, 42 CMD_REMOVE, 43 CMD_MKDIR, 44 CMD_RMDIR, 45 CMD_REALPATH, 46 CMD_STAT, 47 CMD_RENAME, 48 CMD_READLINK, 49 CMD_SYMLINK, 50) = range(1, 21) 51(CMD_STATUS, CMD_HANDLE, CMD_DATA, CMD_NAME, CMD_ATTRS) = range(101, 106) 52(CMD_EXTENDED, CMD_EXTENDED_REPLY) = range(200, 202) 53 54SFTP_OK = 0 55( 56 SFTP_EOF, 57 SFTP_NO_SUCH_FILE, 58 SFTP_PERMISSION_DENIED, 59 SFTP_FAILURE, 60 SFTP_BAD_MESSAGE, 61 SFTP_NO_CONNECTION, 62 SFTP_CONNECTION_LOST, 63 SFTP_OP_UNSUPPORTED, 64) = range(1, 9) 65 66SFTP_DESC = [ 67 "Success", 68 "End of file", 69 "No such file", 70 "Permission denied", 71 "Failure", 72 "Bad message", 73 "No connection", 74 "Connection lost", 75 "Operation unsupported", 76] 77 78SFTP_FLAG_READ = 0x1 79SFTP_FLAG_WRITE = 0x2 80SFTP_FLAG_APPEND = 0x4 81SFTP_FLAG_CREATE = 0x8 82SFTP_FLAG_TRUNC = 0x10 83SFTP_FLAG_EXCL = 0x20 84 85_VERSION = 3 86 87 88# for debugging 89CMD_NAMES = { 90 CMD_INIT: "init", 91 CMD_VERSION: "version", 92 CMD_OPEN: "open", 93 CMD_CLOSE: "close", 94 CMD_READ: "read", 95 CMD_WRITE: "write", 96 CMD_LSTAT: "lstat", 97 CMD_FSTAT: "fstat", 98 CMD_SETSTAT: "setstat", 99 CMD_FSETSTAT: "fsetstat", 100 CMD_OPENDIR: "opendir", 101 CMD_READDIR: "readdir", 102 CMD_REMOVE: "remove", 103 CMD_MKDIR: "mkdir", 104 CMD_RMDIR: "rmdir", 105 CMD_REALPATH: "realpath", 106 CMD_STAT: "stat", 107 CMD_RENAME: "rename", 108 CMD_READLINK: "readlink", 109 CMD_SYMLINK: "symlink", 110 CMD_STATUS: "status", 111 CMD_HANDLE: "handle", 112 CMD_DATA: "data", 113 CMD_NAME: "name", 114 CMD_ATTRS: "attrs", 115 CMD_EXTENDED: "extended", 116 CMD_EXTENDED_REPLY: "extended_reply", 117} 118 119 120class SFTPError(Exception): 121 pass 122 123 124class BaseSFTP(object): 125 def __init__(self): 126 self.logger = util.get_logger("paramiko.sftp") 127 self.sock = None 128 self.ultra_debug = False 129 130 # ...internals... 131 132 def _send_version(self): 133 self._send_packet(CMD_INIT, struct.pack(">I", _VERSION)) 134 t, data = self._read_packet() 135 if t != CMD_VERSION: 136 raise SFTPError("Incompatible sftp protocol") 137 version = struct.unpack(">I", data[:4])[0] 138 # if version != _VERSION: 139 # raise SFTPError('Incompatible sftp protocol') 140 return version 141 142 def _send_server_version(self): 143 # winscp will freak out if the server sends version info before the 144 # client finishes sending INIT. 145 t, data = self._read_packet() 146 if t != CMD_INIT: 147 raise SFTPError("Incompatible sftp protocol") 148 version = struct.unpack(">I", data[:4])[0] 149 # advertise that we support "check-file" 150 extension_pairs = ["check-file", "md5,sha1"] 151 msg = Message() 152 msg.add_int(_VERSION) 153 msg.add(*extension_pairs) 154 self._send_packet(CMD_VERSION, msg) 155 return version 156 157 def _log(self, level, msg, *args): 158 self.logger.log(level, msg, *args) 159 160 def _write_all(self, out): 161 while len(out) > 0: 162 n = self.sock.send(out) 163 if n <= 0: 164 raise EOFError() 165 if n == len(out): 166 return 167 out = out[n:] 168 return 169 170 def _read_all(self, n): 171 out = bytes() 172 while n > 0: 173 if isinstance(self.sock, socket.socket): 174 # sometimes sftp is used directly over a socket instead of 175 # through a paramiko channel. in this case, check periodically 176 # if the socket is closed. (for some reason, recv() won't ever 177 # return or raise an exception, but calling select on a closed 178 # socket will.) 179 while True: 180 read, write, err = select.select([self.sock], [], [], 0.1) 181 if len(read) > 0: 182 x = self.sock.recv(n) 183 break 184 else: 185 x = self.sock.recv(n) 186 187 if len(x) == 0: 188 raise EOFError() 189 out += x 190 n -= len(x) 191 return out 192 193 def _send_packet(self, t, packet): 194 packet = asbytes(packet) 195 out = struct.pack(">I", len(packet) + 1) + byte_chr(t) + packet 196 if self.ultra_debug: 197 self._log(DEBUG, util.format_binary(out, "OUT: ")) 198 self._write_all(out) 199 200 def _read_packet(self): 201 x = self._read_all(4) 202 # most sftp servers won't accept packets larger than about 32k, so 203 # anything with the high byte set (> 16MB) is just garbage. 204 if byte_ord(x[0]): 205 raise SFTPError("Garbage packet received") 206 size = struct.unpack(">I", x)[0] 207 data = self._read_all(size) 208 if self.ultra_debug: 209 self._log(DEBUG, util.format_binary(data, "IN: ")) 210 if size > 0: 211 t = byte_ord(data[0]) 212 return t, data[1:] 213 return 0, bytes() 214