internal void OnInput(byte[] ee) { var input = Encoding.ASCII.GetString(ee); //uint32 length //byte type //byte[length - 1] data payload SshDataWorker reader = new SshDataWorker(ee); SshDataWorker writer = new SshDataWorker(); if (reader.DataAvailable >= 5) { var msglength = reader.ReadUInt32(); var msgtype = (RequestPacketType)(int)reader.ReadByte(); uint requestId = 0; string path = ""; string absolutepath = ""; string handle = Guid.NewGuid().ToString().Replace("-", ""); switch (msgtype) { case RequestPacketType.SSH_FXP_INIT: var sftpclientversion = reader.ReadUInt32(); writer.Write((byte)RequestPacketType.SSH_FXP_VERSION); var version = Math.Max(3, sftpclientversion); writer.Write((uint)version); // SFTP protocol version SendPacket(writer.ToByteArray()); break; case RequestPacketType.SSH_FXP_REALPATH: requestId = reader.ReadUInt32(); path = reader.ReadString(Encoding.UTF8); // List files in root directory for this user // foreach file create a response message if (path == ".") { path = "/"; // replace with current filepath } writer.Write((byte)RequestPacketType.SSH_FXP_NAME); writer.Write((uint)requestId); writer.Write((uint)1); // Dummy file for SSH_FXP_REALPATH request writer.Write(path, Encoding.UTF8); writer.Write(@"-rwxr-xr-x 1 mjos staff 348911 Mar 25 14:29 " + path, Encoding.UTF8); writer.Write((uint)requestId); writer.Write(uint.MaxValue); // flags writer.Write((ulong)0); // size writer.Write(uint.MaxValue); // uid writer.Write(uint.MaxValue); // gid writer.Write(uint.MaxValue); // permissions writer.Write(GetUnixFileTime(DateTime.Now)); //atime writer.Write(GetUnixFileTime(DateTime.Now)); //mtime writer.Write((uint)0); // extended_count //string extended_type blank //string extended_data blank SendPacket(writer.ToByteArray()); break; case RequestPacketType.SSH_FXP_READDIR: requestId = reader.ReadUInt32(); handle = reader.ReadString(Encoding.UTF8); if (HandleToPathDictionary.ContainsKey(handle)) // remove after handle is used first time { var relativepath = HandleToPathDictionary[handle]; absolutepath = UserRootDirectory + relativepath; System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(absolutepath); var allfiles = di.GetFiles(); // returns SSH_FXP_NAME or SSH_FXP_STATUS with SSH_FX_EOF writer.Write((byte)RequestPacketType.SSH_FXP_NAME); writer.Write((uint)requestId); writer.Write((uint)allfiles.Count()); // all files at the same time foreach (var file in allfiles) { writer.Write(GetFileWithAttributes(file)); } SendPacket(writer.ToByteArray()); HandleToPathDictionary.Remove(handle); // remove will return EOF next time } else { // return SSH_FXP_STATUS indicating SSH_FX_EOF when all files have been listed writer.Write((byte)RequestPacketType.SSH_FXP_STATUS); writer.Write((uint)requestId); writer.Write((uint)SftpStatusType.SSH_FX_EOF); // status code //writer.Write("", Encoding.UTF8); //writer.Write("", Encoding.UTF8); SendPacket(writer.ToByteArray()); } break; case RequestPacketType.SSH_FXP_OPENDIR: requestId = reader.ReadUInt32(); path = reader.ReadString(Encoding.UTF8); HandleToPathDictionary.Add(handle, path); writer.Write((byte)RequestPacketType.SSH_FXP_HANDLE); writer.Write((uint)requestId); writer.Write(handle, Encoding.UTF8); // returns SSH_FXP_HANDLE on success or a SSH_FXP_STATUS message on fail SendPacket(writer.ToByteArray()); _logger.LogInformation($"sftp command {msgtype} request Id: \"{requestId}\". path: {path} on channel: {channel}"); break; case RequestPacketType.SSH_FXP_STAT: // follows symbolic links case RequestPacketType.SSH_FXP_LSTAT: // does not follow symbolic links requestId = reader.ReadUInt32(); path = reader.ReadString(Encoding.UTF8); SendAttributes(requestId, path); break; case RequestPacketType.SSH_FXP_FSTAT: // SSH_FXP_FSTAT differs from the others in that it returns status information for an open file(identified by the file handle). requestId = reader.ReadUInt32(); handle = reader.ReadString(Encoding.UTF8); if (HandleToPathDictionary.ContainsKey(handle)) { path = HandleToPathDictionary[handle]; SendAttributes(requestId, path); } else { SendStatus(requestId, SftpStatusType.SSH_FX_NO_SUCH_FILE); } break; case RequestPacketType.SSH_FXP_CLOSE: requestId = reader.ReadUInt32(); handle = reader.ReadString(Encoding.UTF8); if (HandleToPathDictionary.ContainsKey(handle)) { HandleToPathDictionary.Remove(handle); } SendStatus(requestId, SftpStatusType.SSH_FX_OK); break; case RequestPacketType.SSH_FXP_OPEN: requestId = reader.ReadUInt32(); //uint32 request-id path = reader.ReadString(Encoding.UTF8); // //string filename [UTF-8] HandleToPathDictionary.Add(handle, path); var desired_access = reader.ReadUInt32(); var flags = reader.ReadUInt32(); //uint32 desired-access var write = desired_access & (uint)FileSystemOperation.Write; var read = desired_access & (uint)FileSystemOperation.Read; var create = desired_access & (uint)FileSystemOperation.Create; //uint32 flags //ATTRS attrs absolutepath = UserRootDirectory + path; System.IO.FileInfo fi = new System.IO.FileInfo(absolutepath); if (read > 0 && write == 0 && create == 0) { try { var fs = fi.OpenRead(); HandleToFileStreamDictionary.Add(handle, fs); } catch { SendStatus(requestId, SftpStatusType.SSH_FX_PERMISSION_DENIED); } } writer.Write((byte)RequestPacketType.SSH_FXP_HANDLE); writer.Write((uint)requestId); writer.Write(handle, Encoding.UTF8); // returns SSH_FXP_HANDLE on success or a SSH_FXP_STATUS message on fail SendPacket(writer.ToByteArray()); break; case RequestPacketType.SSH_FXP_READ: requestId = reader.ReadUInt32(); //uint32 request-id handle = reader.ReadString(Encoding.UTF8); if (HandleToPathDictionary.ContainsKey(handle)) { var fs = HandleToFileStreamDictionary[handle]; var offset = (long)reader.ReadUInt64(); var length = (int)reader.ReadUInt32(); var buffer = new byte[length]; if (fs.Length - offset < 0) // EOF already reached { SendStatus((uint)requestId, SftpStatusType.SSH_FX_EOF); break; } writer.Write((byte)RequestPacketType.SSH_FXP_DATA); writer.Write((uint)requestId); if (fs.Length - offset < length) { buffer = new byte[fs.Length - offset]; } fs.Seek(offset, SeekOrigin.Begin); fs.Read(buffer); writer.WriteBinary(buffer); SendPacket(writer.ToByteArray()); } else { // send invalid handle: SSH_FX_INVALID_HANDLE } break; default: // unsupported command _logger.LogWarning($"Unsupported sftp command {msgtype.ToString()} input: \"{input}\". on channel: {channel}"); SendStatus(requestId, SftpStatusType.SSH_FX_OP_UNSUPPORTED); break; } } }