mirror of
https://github.com/gravitational/teleport.git
synced 2026-09-21 14:35:22 +08:00
SharedDirectoryMoveRequest and SharedDirectoryMoveResponse (#14959)
This commit is contained in:
@@ -509,6 +509,16 @@ func (c *Client) start() {
|
||||
return
|
||||
}
|
||||
}
|
||||
case tdp.SharedDirectoryMoveResponse:
|
||||
if c.cfg.AllowDirectorySharing {
|
||||
if errCode := C.handle_tdp_sd_move_response(c.rustClient, C.CGOSharedDirectoryMoveResponse{
|
||||
completion_id: C.uint32_t(m.CompletionID),
|
||||
err_code: m.ErrCode,
|
||||
}); errCode != C.ErrCodeSuccess {
|
||||
c.cfg.Log.Errorf("SharedDirectoryMoveResponse failed: %v", errCode)
|
||||
return
|
||||
}
|
||||
}
|
||||
default:
|
||||
c.cfg.Log.Warningf("Skipping unimplemented TDP message type %T", msg)
|
||||
}
|
||||
@@ -585,15 +595,15 @@ func tdp_sd_acknowledge(handle C.uintptr_t, ack *C.CGOSharedDirectoryAcknowledge
|
||||
// sharedDirectoryAcknowledge is sent by the TDP server to the client
|
||||
// to acknowledge that a SharedDirectoryAnnounce was received.
|
||||
func (c *Client) sharedDirectoryAcknowledge(ack tdp.SharedDirectoryAcknowledge) C.CGOErrCode {
|
||||
if c.cfg.AllowDirectorySharing {
|
||||
if err := c.cfg.Conn.OutputMessage(ack); err != nil {
|
||||
c.cfg.Log.Errorf("failed to send SharedDirectoryAcknowledge: %v", err)
|
||||
return C.ErrCodeFailure
|
||||
}
|
||||
return C.ErrCodeSuccess
|
||||
if !c.cfg.AllowDirectorySharing {
|
||||
return C.ErrCodeFailure
|
||||
}
|
||||
|
||||
return C.ErrCodeFailure
|
||||
if err := c.cfg.Conn.OutputMessage(ack); err != nil {
|
||||
c.cfg.Log.Errorf("failed to send SharedDirectoryAcknowledge: %v", err)
|
||||
return C.ErrCodeFailure
|
||||
}
|
||||
return C.ErrCodeSuccess
|
||||
}
|
||||
|
||||
//export tdp_sd_info_request
|
||||
@@ -608,15 +618,15 @@ func tdp_sd_info_request(handle C.uintptr_t, req *C.CGOSharedDirectoryInfoReques
|
||||
// sharedDirectoryInfoRequest is sent from the TDP server to the client
|
||||
// to request information about a file or directory at a given path.
|
||||
func (c *Client) sharedDirectoryInfoRequest(req tdp.SharedDirectoryInfoRequest) C.CGOErrCode {
|
||||
if c.cfg.AllowDirectorySharing {
|
||||
if err := c.cfg.Conn.OutputMessage(req); err != nil {
|
||||
c.cfg.Log.Errorf("failed to send SharedDirectoryAcknowledge: %v", err)
|
||||
return C.ErrCodeFailure
|
||||
}
|
||||
return C.ErrCodeSuccess
|
||||
if !c.cfg.AllowDirectorySharing {
|
||||
return C.ErrCodeFailure
|
||||
}
|
||||
|
||||
return C.ErrCodeFailure
|
||||
if err := c.cfg.Conn.OutputMessage(req); err != nil {
|
||||
c.cfg.Log.Errorf("failed to send SharedDirectoryAcknowledge: %v", err)
|
||||
return C.ErrCodeFailure
|
||||
}
|
||||
return C.ErrCodeSuccess
|
||||
}
|
||||
|
||||
//export tdp_sd_create_request
|
||||
@@ -632,15 +642,15 @@ func tdp_sd_create_request(handle C.uintptr_t, req *C.CGOSharedDirectoryCreateRe
|
||||
// sharedDirectoryCreateRequest is sent by the TDP server to
|
||||
// the client to request the creation of a new file or directory.
|
||||
func (c *Client) sharedDirectoryCreateRequest(req tdp.SharedDirectoryCreateRequest) C.CGOErrCode {
|
||||
if c.cfg.AllowDirectorySharing {
|
||||
if err := c.cfg.Conn.OutputMessage(req); err != nil {
|
||||
c.cfg.Log.Errorf("failed to send SharedDirectoryCreateRequest: %v", err)
|
||||
return C.ErrCodeFailure
|
||||
}
|
||||
return C.ErrCodeSuccess
|
||||
if !c.cfg.AllowDirectorySharing {
|
||||
return C.ErrCodeFailure
|
||||
}
|
||||
|
||||
return C.ErrCodeFailure
|
||||
if err := c.cfg.Conn.OutputMessage(req); err != nil {
|
||||
c.cfg.Log.Errorf("failed to send SharedDirectoryCreateRequest: %v", err)
|
||||
return C.ErrCodeFailure
|
||||
}
|
||||
return C.ErrCodeSuccess
|
||||
}
|
||||
|
||||
//export tdp_sd_delete_request
|
||||
@@ -655,15 +665,15 @@ func tdp_sd_delete_request(handle C.uintptr_t, req *C.CGOSharedDirectoryDeleteRe
|
||||
// sharedDirectoryDeleteRequest is sent by the TDP server to the client
|
||||
// to request the deletion of a file or directory at path.
|
||||
func (c *Client) sharedDirectoryDeleteRequest(req tdp.SharedDirectoryDeleteRequest) C.CGOErrCode {
|
||||
if c.cfg.AllowDirectorySharing {
|
||||
if err := c.cfg.Conn.OutputMessage(req); err != nil {
|
||||
c.cfg.Log.Errorf("failed to send SharedDirectoryDeleteRequest: %v", err)
|
||||
return C.ErrCodeFailure
|
||||
}
|
||||
return C.ErrCodeSuccess
|
||||
if !c.cfg.AllowDirectorySharing {
|
||||
return C.ErrCodeFailure
|
||||
}
|
||||
|
||||
return C.ErrCodeFailure
|
||||
if err := c.cfg.Conn.OutputMessage(req); err != nil {
|
||||
c.cfg.Log.Errorf("failed to send SharedDirectoryDeleteRequest: %v", err)
|
||||
return C.ErrCodeFailure
|
||||
}
|
||||
return C.ErrCodeSuccess
|
||||
}
|
||||
|
||||
//export tdp_sd_list_request
|
||||
@@ -678,15 +688,15 @@ func tdp_sd_list_request(handle C.uintptr_t, req *C.CGOSharedDirectoryListReques
|
||||
// sharedDirectoryListRequest is sent by the TDP server to the client
|
||||
// to request the contents of a directory.
|
||||
func (c *Client) sharedDirectoryListRequest(req tdp.SharedDirectoryListRequest) C.CGOErrCode {
|
||||
if c.cfg.AllowDirectorySharing {
|
||||
if err := c.cfg.Conn.OutputMessage(req); err != nil {
|
||||
c.cfg.Log.Errorf("failed to send SharedDirectoryListRequest: %v", err)
|
||||
return C.ErrCodeFailure
|
||||
}
|
||||
return C.ErrCodeSuccess
|
||||
if !c.cfg.AllowDirectorySharing {
|
||||
return C.ErrCodeFailure
|
||||
}
|
||||
|
||||
return C.ErrCodeFailure
|
||||
if err := c.cfg.Conn.OutputMessage(req); err != nil {
|
||||
c.cfg.Log.Errorf("failed to send SharedDirectoryListRequest: %v", err)
|
||||
return C.ErrCodeFailure
|
||||
}
|
||||
return C.ErrCodeSuccess
|
||||
}
|
||||
|
||||
//export tdp_sd_read_request
|
||||
@@ -695,7 +705,6 @@ func tdp_sd_read_request(handle C.uintptr_t, req *C.CGOSharedDirectoryReadReques
|
||||
CompletionID: uint32(req.completion_id),
|
||||
DirectoryID: uint32(req.directory_id),
|
||||
Path: C.GoString(req.path),
|
||||
PathLength: uint32(req.path_length),
|
||||
Offset: uint64(req.offset),
|
||||
Length: uint32(req.length),
|
||||
})
|
||||
@@ -704,14 +713,15 @@ func tdp_sd_read_request(handle C.uintptr_t, req *C.CGOSharedDirectoryReadReques
|
||||
// SharedDirectoryReadRequest is sent by the TDP server to the client
|
||||
// to request the contents of a file.
|
||||
func (c *Client) sharedDirectoryReadRequest(req tdp.SharedDirectoryReadRequest) C.CGOErrCode {
|
||||
if c.cfg.AllowDirectorySharing {
|
||||
if err := c.cfg.Conn.OutputMessage(req); err != nil {
|
||||
c.cfg.Log.Errorf("failed to send SharedDirectoryReadRequest: %v", err)
|
||||
return C.ErrCodeFailure
|
||||
}
|
||||
return C.ErrCodeSuccess
|
||||
if !c.cfg.AllowDirectorySharing {
|
||||
return C.ErrCodeFailure
|
||||
}
|
||||
return C.ErrCodeFailure
|
||||
|
||||
if err := c.cfg.Conn.OutputMessage(req); err != nil {
|
||||
c.cfg.Log.Errorf("failed to send SharedDirectoryReadRequest: %v", err)
|
||||
return C.ErrCodeFailure
|
||||
}
|
||||
return C.ErrCodeSuccess
|
||||
}
|
||||
|
||||
//export tdp_sd_write_request
|
||||
@@ -720,7 +730,6 @@ func tdp_sd_write_request(handle C.uintptr_t, req *C.CGOSharedDirectoryWriteRequ
|
||||
CompletionID: uint32(req.completion_id),
|
||||
DirectoryID: uint32(req.directory_id),
|
||||
Offset: uint64(req.offset),
|
||||
PathLength: uint32(req.path_length),
|
||||
Path: C.GoString(req.path),
|
||||
WriteDataLength: uint32(req.write_data_length),
|
||||
WriteData: C.GoBytes(unsafe.Pointer(req.write_data), C.int(req.write_data_length)),
|
||||
@@ -730,14 +739,38 @@ func tdp_sd_write_request(handle C.uintptr_t, req *C.CGOSharedDirectoryWriteRequ
|
||||
// SharedDirectoryWriteRequest is sent by the TDP server to the client
|
||||
// to write to a file.
|
||||
func (c *Client) sharedDirectoryWriteRequest(req tdp.SharedDirectoryWriteRequest) C.CGOErrCode {
|
||||
if c.cfg.AllowDirectorySharing {
|
||||
if err := c.cfg.Conn.OutputMessage(req); err != nil {
|
||||
c.cfg.Log.Errorf("failed to send SharedDirectoryWriteRequest: %v", err)
|
||||
return C.ErrCodeFailure
|
||||
}
|
||||
return C.ErrCodeSuccess
|
||||
if !c.cfg.AllowDirectorySharing {
|
||||
return C.ErrCodeFailure
|
||||
}
|
||||
return C.ErrCodeFailure
|
||||
|
||||
if err := c.cfg.Conn.OutputMessage(req); err != nil {
|
||||
c.cfg.Log.Errorf("failed to send SharedDirectoryWriteRequest: %v", err)
|
||||
return C.ErrCodeFailure
|
||||
}
|
||||
return C.ErrCodeSuccess
|
||||
}
|
||||
|
||||
//export tdp_sd_move_request
|
||||
func tdp_sd_move_request(handle C.uintptr_t, req *C.CGOSharedDirectoryMoveRequest) C.CGOErrCode {
|
||||
return cgo.Handle(handle).Value().(*Client).sharedDirectoryMoveRequest(tdp.SharedDirectoryMoveRequest{
|
||||
CompletionID: uint32(req.completion_id),
|
||||
DirectoryID: uint32(req.directory_id),
|
||||
OriginalPath: C.GoString(req.original_path),
|
||||
NewPath: C.GoString(req.new_path),
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Client) sharedDirectoryMoveRequest(req tdp.SharedDirectoryMoveRequest) C.CGOErrCode {
|
||||
if !c.cfg.AllowDirectorySharing {
|
||||
return C.ErrCodeFailure
|
||||
}
|
||||
|
||||
if err := c.cfg.Conn.OutputMessage(req); err != nil {
|
||||
c.cfg.Log.Errorf("failed to send SharedDirectoryMoveRequest: %v", err)
|
||||
return C.ErrCodeFailure
|
||||
}
|
||||
return C.ErrCodeSuccess
|
||||
|
||||
}
|
||||
|
||||
// close closes the RDP client connection and
|
||||
|
||||
@@ -504,6 +504,46 @@ fn connect_rdp_inner(
|
||||
}
|
||||
});
|
||||
|
||||
let tdp_sd_move_request = Box::new(move |req: SharedDirectoryMoveRequest| -> RdpResult<()> {
|
||||
debug!("sending TDP SharedDirectoryMoveRequest: {:?}", req);
|
||||
match req.original_path.to_cstring() {
|
||||
Ok(original_path) => match req.new_path.to_cstring() {
|
||||
Ok(new_path) => {
|
||||
unsafe {
|
||||
let err = tdp_sd_move_request(
|
||||
go_ref,
|
||||
&mut CGOSharedDirectoryMoveRequest {
|
||||
completion_id: req.completion_id,
|
||||
directory_id: req.directory_id,
|
||||
original_path: original_path.as_ptr(),
|
||||
new_path: new_path.as_ptr(),
|
||||
},
|
||||
);
|
||||
|
||||
if err != CGOErrCode::ErrCodeSuccess {
|
||||
return Err(RdpError::TryError(String::from(
|
||||
"call to tdp_sd_Move_failed",
|
||||
)));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Err(_) => {
|
||||
return Err(RdpError::TryError(format!(
|
||||
"new_path contained characters that couldn't be converted to a C string: {:?}",
|
||||
req.new_path
|
||||
)));
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
return Err(RdpError::TryError(format!(
|
||||
"original_path contained characters that couldn't be converted to a C string: {:?}",
|
||||
req.original_path
|
||||
)));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Client for the "rdpdr" channel - smartcard emulation and drive redirection.
|
||||
let rdpdr = rdpdr::Client::new(rdpdr::Config {
|
||||
cert_der: params.cert_der,
|
||||
@@ -517,6 +557,7 @@ fn connect_rdp_inner(
|
||||
tdp_sd_list_request,
|
||||
tdp_sd_read_request,
|
||||
tdp_sd_write_request,
|
||||
tdp_sd_move_request,
|
||||
});
|
||||
|
||||
// Client for the "cliprdr" channel - clipboard sharing.
|
||||
@@ -653,6 +694,13 @@ impl<S: Read + Write> RdpClient<S> {
|
||||
self.rdpdr.handle_tdp_sd_write_response(res, &mut self.mcs)
|
||||
}
|
||||
|
||||
pub fn handle_tdp_sd_move_response(
|
||||
&mut self,
|
||||
res: SharedDirectoryMoveResponse,
|
||||
) -> RdpResult<()> {
|
||||
self.rdpdr.handle_tdp_sd_move_response(res, &mut self.mcs)
|
||||
}
|
||||
|
||||
pub fn shutdown(&mut self) -> RdpResult<()> {
|
||||
self.mcs.shutdown()
|
||||
}
|
||||
@@ -1033,6 +1081,43 @@ pub unsafe extern "C" fn handle_tdp_sd_write_response(
|
||||
}
|
||||
}
|
||||
|
||||
/// handle_tdp_sd_move_response handles a TDP Shared Directory Move Response
|
||||
/// message
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// client_ptr MUST be a valid pointer.
|
||||
/// (validity defined by https://doc.rust-lang.org/nightly/core/primitive.pointer.html#method.as_ref-1)
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn handle_tdp_sd_move_response(
|
||||
client_ptr: *mut Client,
|
||||
res: CGOSharedDirectoryMoveResponse,
|
||||
) -> CGOErrCode {
|
||||
// # Safety
|
||||
//
|
||||
// This function MUST NOT hang on to any of the pointers passed in to it after it returns.
|
||||
// In other words, all pointer data that needs to persist after this function returns MUST
|
||||
// be copied into Rust-owned memory.
|
||||
|
||||
let res: SharedDirectoryMoveResponse = res;
|
||||
|
||||
let client = match Client::from_ptr(client_ptr) {
|
||||
Ok(client) => client,
|
||||
Err(cgo_error) => {
|
||||
return cgo_error;
|
||||
}
|
||||
};
|
||||
|
||||
let mut rdp_client = client.rdp_client.lock().unwrap();
|
||||
match rdp_client.handle_tdp_sd_move_response(res) {
|
||||
Ok(()) => CGOErrCode::ErrCodeSuccess,
|
||||
Err(e) => {
|
||||
error!("failed to handle Shared Directory Move Response: {:?}", e);
|
||||
CGOErrCode::ErrCodeFailure
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `read_rdp_output` reads incoming RDP bitmap frames from client at client_ref and forwards them to
|
||||
/// handle_bitmap.
|
||||
///
|
||||
@@ -1641,6 +1726,24 @@ pub struct CGOSharedDirectoryListResponse {
|
||||
fso_list: *mut CGOFileSystemObject,
|
||||
}
|
||||
|
||||
/// SharedDirectoryMoveRequest is sent from the TDP server to the client
|
||||
/// to request a file at original_path be moved to new_path.
|
||||
#[derive(Debug)]
|
||||
pub struct SharedDirectoryMoveRequest {
|
||||
completion_id: u32,
|
||||
directory_id: u32,
|
||||
original_path: UnixPath,
|
||||
new_path: UnixPath,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct CGOSharedDirectoryMoveRequest {
|
||||
pub completion_id: u32,
|
||||
pub directory_id: u32,
|
||||
pub original_path: *const c_char,
|
||||
pub new_path: *const c_char,
|
||||
}
|
||||
|
||||
pub type CGOSharedDirectoryCreateResponse = SharedDirectoryCreateResponse;
|
||||
/// SharedDirectoryDeleteRequest is sent by the TDP server to the client
|
||||
/// to request the deletion of a file or directory at path.
|
||||
@@ -1654,6 +1757,10 @@ pub type CGOSharedDirectoryDeleteResponse = SharedDirectoryCreateResponse;
|
||||
/// to request the contents of a directory.
|
||||
pub type SharedDirectoryListRequest = SharedDirectoryInfoRequest;
|
||||
pub type CGOSharedDirectoryListRequest = CGOSharedDirectoryInfoRequest;
|
||||
/// SharedDirectoryMoveResponse is sent by the TDP client to the server
|
||||
/// to acknowledge a SharedDirectoryMoveRequest was received and executed.
|
||||
pub type SharedDirectoryMoveResponse = SharedDirectoryCreateResponse;
|
||||
pub type CGOSharedDirectoryMoveResponse = CGOSharedDirectoryCreateResponse;
|
||||
|
||||
// These functions are defined on the Go side. Look for functions with '//export funcname'
|
||||
// comments.
|
||||
@@ -1687,6 +1794,10 @@ extern "C" {
|
||||
client_ref: usize,
|
||||
req: *mut CGOSharedDirectoryWriteRequest,
|
||||
) -> CGOErrCode;
|
||||
fn tdp_sd_move_request(
|
||||
client_ref: usize,
|
||||
req: *mut CGOSharedDirectoryMoveRequest,
|
||||
) -> CGOErrCode;
|
||||
}
|
||||
|
||||
/// Payload is a generic type used to represent raw incoming RDP messages for parsing.
|
||||
|
||||
@@ -124,7 +124,7 @@ pub enum NTSTATUS {
|
||||
|
||||
/// 2.4 File Information Classes [MS-FSCC]
|
||||
/// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/4718fc40-e539-4014-8e33-b675af74e3e1
|
||||
#[derive(FromPrimitive, Debug, PartialEq)]
|
||||
#[derive(FromPrimitive, Debug, PartialEq, Clone)]
|
||||
#[repr(u32)]
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
pub enum FileInformationClassLevel {
|
||||
|
||||
@@ -28,8 +28,9 @@ use crate::{
|
||||
FileSystemObject, FileType, Payload, SharedDirectoryAcknowledge, SharedDirectoryCreateRequest,
|
||||
SharedDirectoryCreateResponse, SharedDirectoryDeleteRequest, SharedDirectoryDeleteResponse,
|
||||
SharedDirectoryInfoRequest, SharedDirectoryInfoResponse, SharedDirectoryListRequest,
|
||||
SharedDirectoryListResponse, SharedDirectoryReadRequest, SharedDirectoryReadResponse,
|
||||
SharedDirectoryWriteRequest, SharedDirectoryWriteResponse, TdpErrCode,
|
||||
SharedDirectoryListResponse, SharedDirectoryMoveRequest, SharedDirectoryMoveResponse,
|
||||
SharedDirectoryReadRequest, SharedDirectoryReadResponse, SharedDirectoryWriteRequest,
|
||||
SharedDirectoryWriteResponse, TdpErrCode,
|
||||
};
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
@@ -78,6 +79,7 @@ pub struct Client {
|
||||
tdp_sd_list_request: SharedDirectoryListRequestSender,
|
||||
tdp_sd_read_request: SharedDirectoryReadRequestSender,
|
||||
tdp_sd_write_request: SharedDirectoryWriteRequestSender,
|
||||
tdp_sd_move_request: SharedDirectoryMoveRequestSender,
|
||||
|
||||
// CompletionId-indexed maps of handlers for tdp messages coming from the browser client.
|
||||
pending_sd_info_resp_handlers: HashMap<u32, SharedDirectoryInfoResponseHandler>,
|
||||
@@ -86,6 +88,7 @@ pub struct Client {
|
||||
pending_sd_list_resp_handlers: HashMap<u32, SharedDirectoryListResponseHandler>,
|
||||
pending_sd_read_resp_handlers: HashMap<u32, SharedDirectoryReadResponseHandler>,
|
||||
pending_sd_write_resp_handlers: HashMap<u32, SharedDirectoryWriteResponseHandler>,
|
||||
pending_sd_move_resp_handlers: HashMap<u32, SharedDirectoryMoveResponseHandler>,
|
||||
}
|
||||
|
||||
pub struct Config {
|
||||
@@ -101,6 +104,7 @@ pub struct Config {
|
||||
pub tdp_sd_list_request: SharedDirectoryListRequestSender,
|
||||
pub tdp_sd_read_request: SharedDirectoryReadRequestSender,
|
||||
pub tdp_sd_write_request: SharedDirectoryWriteRequestSender,
|
||||
pub tdp_sd_move_request: SharedDirectoryMoveRequestSender,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
@@ -126,6 +130,7 @@ impl Client {
|
||||
tdp_sd_list_request: cfg.tdp_sd_list_request,
|
||||
tdp_sd_read_request: cfg.tdp_sd_read_request,
|
||||
tdp_sd_write_request: cfg.tdp_sd_write_request,
|
||||
tdp_sd_move_request: cfg.tdp_sd_move_request,
|
||||
|
||||
pending_sd_info_resp_handlers: HashMap::new(),
|
||||
pending_sd_create_resp_handlers: HashMap::new(),
|
||||
@@ -133,6 +138,7 @@ impl Client {
|
||||
pending_sd_list_resp_handlers: HashMap::new(),
|
||||
pending_sd_read_resp_handlers: HashMap::new(),
|
||||
pending_sd_write_resp_handlers: HashMap::new(),
|
||||
pending_sd_move_resp_handlers: HashMap::new(),
|
||||
}
|
||||
}
|
||||
/// Reads raw RDP messages sent on the rdpdr virtual channel and replies as necessary.
|
||||
@@ -695,46 +701,44 @@ impl Client {
|
||||
let rdp_req = ServerDriveQueryVolumeInformationRequest::decode(device_io_request, payload)?;
|
||||
debug!("received RDP: {:?}", rdp_req);
|
||||
if let Some(dir) = self.file_cache.get(rdp_req.device_io_request.file_id) {
|
||||
// TODO(isaiah): we should support all of the fs_info_class_lvls that FreeRDP does:
|
||||
// https://github.com/FreeRDP/FreeRDP/blob/511444a65e7aa2f537c5e531fa68157a50c1bd4d/channels/drive/client/drive_main.c#L468
|
||||
match rdp_req.fs_info_class_lvl {
|
||||
let buffer = match rdp_req.fs_info_class_lvl {
|
||||
FileSystemInformationClassLevel::FileFsVolumeInformation => {
|
||||
let buffer = Some(FileSystemInformationClass::FileFsVolumeInformation(
|
||||
Some(FileSystemInformationClass::FileFsVolumeInformation(
|
||||
FileFsVolumeInformation::new(dir.fso.last_modified as i64),
|
||||
));
|
||||
return self.prep_query_vol_info_response(
|
||||
&rdp_req.device_io_request,
|
||||
NTSTATUS::STATUS_SUCCESS,
|
||||
buffer,
|
||||
);
|
||||
))
|
||||
}
|
||||
FileSystemInformationClassLevel::FileFsAttributeInformation => {
|
||||
let buffer = Some(FileSystemInformationClass::FileFsAttributeInformation(
|
||||
Some(FileSystemInformationClass::FileFsAttributeInformation(
|
||||
FileFsAttributeInformation::new(),
|
||||
));
|
||||
return self.prep_query_vol_info_response(
|
||||
&rdp_req.device_io_request,
|
||||
NTSTATUS::STATUS_SUCCESS,
|
||||
buffer,
|
||||
);
|
||||
))
|
||||
}
|
||||
FileSystemInformationClassLevel::FileFsSizeInformation
|
||||
| FileSystemInformationClassLevel::FileFsFullSizeInformation
|
||||
| FileSystemInformationClassLevel::FileFsDeviceInformation => {
|
||||
return Err(not_implemented_error(&format!(
|
||||
"support for ServerDriveQueryVolumeInformationRequest with fs_info_class_lvl = {:?} is not implemented",
|
||||
rdp_req.fs_info_class_lvl
|
||||
)));
|
||||
FileSystemInformationClassLevel::FileFsFullSizeInformation => {
|
||||
Some(FileSystemInformationClass::FileFsFullSizeInformation(
|
||||
FileFsFullSizeInformation::new(),
|
||||
))
|
||||
}
|
||||
_ => {
|
||||
// https://github.com/FreeRDP/FreeRDP/blob/511444a65e7aa2f537c5e531fa68157a50c1bd4d/channels/drive/client/drive_main.c#L574-L577
|
||||
return self.prep_query_vol_info_response(
|
||||
&rdp_req.device_io_request,
|
||||
NTSTATUS::STATUS_UNSUCCESSFUL,
|
||||
None,
|
||||
);
|
||||
FileSystemInformationClassLevel::FileFsDeviceInformation => {
|
||||
Some(FileSystemInformationClass::FileFsDeviceInformation(
|
||||
FileFsDeviceInformation::new(),
|
||||
))
|
||||
}
|
||||
}
|
||||
FileSystemInformationClassLevel::FileFsSizeInformation => Some(
|
||||
FileSystemInformationClass::FileFsSizeInformation(FileFsSizeInformation::new()),
|
||||
),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let io_status = if buffer.is_some() {
|
||||
NTSTATUS::STATUS_SUCCESS
|
||||
} else {
|
||||
NTSTATUS::STATUS_UNSUCCESSFUL
|
||||
};
|
||||
|
||||
return self.prep_query_vol_info_response(
|
||||
&rdp_req.device_io_request,
|
||||
io_status,
|
||||
buffer,
|
||||
);
|
||||
}
|
||||
|
||||
// File not found in cache
|
||||
@@ -765,6 +769,7 @@ impl Client {
|
||||
self.tdp_sd_write(rdp_req)
|
||||
}
|
||||
|
||||
#[allow(clippy::wildcard_in_or_patterns)]
|
||||
fn process_irp_set_information(
|
||||
&mut self,
|
||||
device_io_request: DeviceIoRequest,
|
||||
@@ -772,25 +777,33 @@ impl Client {
|
||||
) -> RdpResult<Vec<Vec<u8>>> {
|
||||
let rdp_req = ServerDriveSetInformationRequest::decode(device_io_request, payload)?;
|
||||
|
||||
let resp = match rdp_req.file_information_class_level {
|
||||
match rdp_req.file_information_class_level {
|
||||
FileInformationClassLevel::FileRenameInformation => match rdp_req.set_buffer {
|
||||
FileInformationClass::FileRenameInformation(ref rename_info) => {
|
||||
self.rename(rdp_req.clone(), rename_info)
|
||||
}
|
||||
_ => Err(invalid_data_error(
|
||||
"FileInformationClass does not match FileInformationClassLevel",
|
||||
)),
|
||||
},
|
||||
FileInformationClassLevel::FileBasicInformation
|
||||
| FileInformationClassLevel::FileEndOfFileInformation
|
||||
| FileInformationClassLevel::FileAllocationInformation
|
||||
| FileInformationClassLevel::FileDispositionInformation => {
|
||||
ClientDriveSetInformationResponse::new(&rdp_req, NTSTATUS::STATUS_SUCCESS)
|
||||
| FileInformationClassLevel::FileAllocationInformation => {
|
||||
// Each of these ask us to change something we don't have control over at the browser
|
||||
// level, so we just do nothing and send back a success.
|
||||
// https://github.com/FreeRDP/FreeRDP/blob/dfa231c0a55b005af775b833f92f6bcd30363d77/channels/drive/client/drive_file.c#L579
|
||||
self.prep_set_info_response(&rdp_req, NTSTATUS::STATUS_SUCCESS)
|
||||
}
|
||||
_ => {
|
||||
return Err(not_implemented_error(&format!(
|
||||
|
||||
// TODO(isaiah) or TODO(lkozlowski): implement FileDispositionInformation as is the case in FreeRDP.
|
||||
// Remove the #[allow(clippy::wildcard_in_or_patterns)] macro above this function once completed.
|
||||
FileInformationClassLevel::FileDispositionInformation | _ => {
|
||||
Err(not_implemented_error(&format!(
|
||||
"support for ServerDriveSetInformationRequest with fs_info_class_lvl = {:?} is not implemented",
|
||||
rdp_req.file_information_class_level
|
||||
)));
|
||||
)))
|
||||
}
|
||||
};
|
||||
|
||||
debug!("sending RDP: {:?}", resp);
|
||||
let resp = self
|
||||
.add_headers_and_chunkify(PacketId::PAKID_CORE_DEVICE_IOCOMPLETION, resp.encode()?)?;
|
||||
Ok(resp)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_client_device_list_announce<S: Read + Write>(
|
||||
@@ -955,6 +968,30 @@ impl Client {
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn handle_tdp_sd_move_response<S: Read + Write>(
|
||||
&mut self,
|
||||
res: SharedDirectoryMoveResponse,
|
||||
mcs: &mut mcs::Client<S>,
|
||||
) -> RdpResult<()> {
|
||||
debug!("received TDP SharedDirectoryMoveResponse: {:?}", res);
|
||||
if let Some(tdp_resp_handler) = self
|
||||
.pending_sd_move_resp_handlers
|
||||
.remove(&res.completion_id)
|
||||
{
|
||||
let rdp_responses = tdp_resp_handler(self, res)?;
|
||||
let chan = &CHANNEL_NAME.to_string();
|
||||
for resp in rdp_responses {
|
||||
mcs.write(chan, resp)?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err(try_error(&format!(
|
||||
"received invalid completion id: {}",
|
||||
res.completion_id
|
||||
)))
|
||||
}
|
||||
|
||||
fn prep_device_create_response(
|
||||
&mut self,
|
||||
req: &DeviceCreateRequest,
|
||||
@@ -1134,6 +1171,18 @@ impl Client {
|
||||
Ok(resp)
|
||||
}
|
||||
|
||||
fn prep_set_info_response(
|
||||
&mut self,
|
||||
req: &ServerDriveSetInformationRequest,
|
||||
io_status: NTSTATUS,
|
||||
) -> RdpResult<Vec<Vec<u8>>> {
|
||||
let resp = ClientDriveSetInformationResponse::new(req, io_status);
|
||||
debug!("sending RDP: {:?}", resp);
|
||||
let resp = self
|
||||
.add_headers_and_chunkify(PacketId::PAKID_CORE_DEVICE_IOCOMPLETION, resp.encode()?)?;
|
||||
Ok(resp)
|
||||
}
|
||||
|
||||
/// Helper function for sending a TDP SharedDirectoryCreateRequest based on an
|
||||
/// RDP DeviceCreateRequest and handling the TDP SharedDirectoryCreateResponse.
|
||||
fn tdp_sd_create(
|
||||
@@ -1316,6 +1365,97 @@ impl Client {
|
||||
self.prep_write_response(rdp_req.device_io_request, NTSTATUS::STATUS_UNSUCCESSFUL, 0)
|
||||
}
|
||||
|
||||
fn rename(
|
||||
&mut self,
|
||||
rdp_req: ServerDriveSetInformationRequest,
|
||||
rename_info: &FileRenameInformation,
|
||||
) -> RdpResult<Vec<Vec<u8>>> {
|
||||
// https://github.com/FreeRDP/FreeRDP/blob/dfa231c0a55b005af775b833f92f6bcd30363d77/channels/drive/client/drive_file.c#L709
|
||||
match rename_info.replace_if_exists {
|
||||
Boolean::True => self.rename_replace_if_exists(rdp_req, rename_info),
|
||||
Boolean::False => self.rename_dont_replace_if_exists(rdp_req, rename_info),
|
||||
}
|
||||
}
|
||||
|
||||
fn rename_replace_if_exists(
|
||||
&mut self,
|
||||
rdp_req: ServerDriveSetInformationRequest,
|
||||
rename_info: &FileRenameInformation,
|
||||
) -> RdpResult<Vec<Vec<u8>>> {
|
||||
// If replace_if_exists is true, we can just send a TDP SharedDirectoryMoveRequest,
|
||||
// which works like the unix `mv` utility (meaning it will automatically replace if exists).
|
||||
self.tdp_sd_move(rdp_req, rename_info)
|
||||
}
|
||||
|
||||
fn rename_dont_replace_if_exists(
|
||||
&mut self,
|
||||
rdp_req: ServerDriveSetInformationRequest,
|
||||
rename_info: &FileRenameInformation,
|
||||
) -> RdpResult<Vec<Vec<u8>>> {
|
||||
let new_path = UnixPath::from(&rename_info.file_name);
|
||||
// If replace_if_exists is false, first check if the new_path exists.
|
||||
(self.tdp_sd_info_request)(SharedDirectoryInfoRequest {
|
||||
completion_id: rdp_req.device_io_request.completion_id,
|
||||
directory_id: rdp_req.device_io_request.device_id,
|
||||
path: new_path,
|
||||
})?;
|
||||
|
||||
let rename_info = (*rename_info).clone();
|
||||
self.pending_sd_info_resp_handlers.insert(
|
||||
rdp_req.device_io_request.completion_id,
|
||||
Box::new(
|
||||
move |cli: &mut Self,
|
||||
res: SharedDirectoryInfoResponse|
|
||||
-> RdpResult<Vec<Vec<u8>>> {
|
||||
if res.err_code == TdpErrCode::DoesNotExist {
|
||||
// If the file doesn't already exist, send a move request.
|
||||
return cli.tdp_sd_move(rdp_req, &rename_info);
|
||||
}
|
||||
// If it does, send back a name collision error, as is done in FreeRDP.
|
||||
cli.prep_set_info_response(&rdp_req, NTSTATUS::STATUS_OBJECT_NAME_COLLISION)
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
fn tdp_sd_move(
|
||||
&mut self,
|
||||
rdp_req: ServerDriveSetInformationRequest,
|
||||
rename_info: &FileRenameInformation,
|
||||
) -> RdpResult<Vec<Vec<u8>>> {
|
||||
if let Some(file) = self.file_cache.get(rdp_req.device_io_request.file_id) {
|
||||
(self.tdp_sd_move_request)(SharedDirectoryMoveRequest {
|
||||
completion_id: rdp_req.device_io_request.completion_id,
|
||||
directory_id: rdp_req.device_io_request.device_id,
|
||||
original_path: file.path.clone(),
|
||||
new_path: UnixPath::from(&rename_info.file_name),
|
||||
})?;
|
||||
|
||||
self.pending_sd_move_resp_handlers.insert(
|
||||
rdp_req.device_io_request.completion_id,
|
||||
Box::new(
|
||||
move |cli: &mut Self,
|
||||
res: SharedDirectoryMoveResponse|
|
||||
-> RdpResult<Vec<Vec<u8>>> {
|
||||
if res.err_code != TdpErrCode::Nil {
|
||||
return cli
|
||||
.prep_set_info_response(&rdp_req, NTSTATUS::STATUS_UNSUCCESSFUL);
|
||||
}
|
||||
|
||||
cli.prep_set_info_response(&rdp_req, NTSTATUS::STATUS_SUCCESS)
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
// File not found in cache
|
||||
self.prep_set_info_response(&rdp_req, NTSTATUS::STATUS_UNSUCCESSFUL)
|
||||
}
|
||||
|
||||
/// add_headers_and_chunkify takes an encoded PDU ready to be sent over a virtual channel (payload),
|
||||
/// adds on the Shared Header based the passed packet_id, adds the appropriate (virtual) Channel PDU Header,
|
||||
/// and splits the entire payload into chunks if the payload exceeds the maximum size.
|
||||
@@ -1371,7 +1511,7 @@ impl Client {
|
||||
/// | -------- | ------------- | ---------------------------------------------------------|
|
||||
/// | 3 | IRP_MJ_CLOSE | The FCO is deleted from the cache |
|
||||
/// | -------- | ------------- | ---------------------------------------------------------|
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct FileCacheObject {
|
||||
path: UnixPath,
|
||||
delete_pending: bool,
|
||||
@@ -2236,9 +2376,8 @@ struct ServerDriveQueryInformationRequest {
|
||||
|
||||
impl ServerDriveQueryInformationRequest {
|
||||
fn decode(device_io_request: DeviceIoRequest, payload: &mut Payload) -> RdpResult<Self> {
|
||||
if let Some(file_info_class_lvl) =
|
||||
FileInformationClassLevel::from_u32(payload.read_u32::<LittleEndian>()?)
|
||||
{
|
||||
let n = payload.read_u32::<LittleEndian>()?;
|
||||
if let Some(file_info_class_lvl) = FileInformationClassLevel::from_u32(n) {
|
||||
return Ok(Self {
|
||||
device_io_request,
|
||||
file_info_class_lvl,
|
||||
@@ -2246,14 +2385,18 @@ impl ServerDriveQueryInformationRequest {
|
||||
}
|
||||
|
||||
Err(invalid_data_error(
|
||||
"received invalid FileInformationClass in ServerDriveQueryInformationRequest",
|
||||
format!(
|
||||
"received invalid FileInformationClass in ServerDriveQueryInformationRequest: {}",
|
||||
n
|
||||
)
|
||||
.as_str(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// 2.4 File Information Classes [MS-FSCC]
|
||||
/// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/4718fc40-e539-4014-8e33-b675af74e3e1
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code, clippy::enum_variant_names)]
|
||||
enum FileInformationClass {
|
||||
FileBasicInformation(FileBasicInformation),
|
||||
@@ -2336,7 +2479,7 @@ impl FileInformationClass {
|
||||
|
||||
/// 2.4.7 FileBasicInformation [MS-FSCC]
|
||||
/// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/16023025-8a78-492f-8b96-c873b042ac50
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct FileBasicInformation {
|
||||
creation_time: i64,
|
||||
last_access_time: i64,
|
||||
@@ -2385,7 +2528,7 @@ impl FileBasicInformation {
|
||||
|
||||
/// 2.4.41 FileStandardInformation [MS-FSCC]
|
||||
/// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/5afa7f66-619c-48f3-955f-68c4ece704ae
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct FileStandardInformation {
|
||||
/// A 64-bit signed integer that contains the file allocation size, in bytes. The value of this field MUST be an
|
||||
/// integer multiple of the cluster size.
|
||||
@@ -2437,7 +2580,7 @@ impl FileStandardInformation {
|
||||
|
||||
/// 2.4.6 FileAttributeTagInformation [MS-FSCC]
|
||||
/// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/d295752f-ce89-4b98-8553-266d37c84f0e?redirectedfrom=MSDN
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct FileAttributeTagInformation {
|
||||
file_attributes: flags::FileAttributes,
|
||||
reparse_tag: u32,
|
||||
@@ -2460,7 +2603,7 @@ impl FileAttributeTagInformation {
|
||||
|
||||
/// 2.1.8 Boolean
|
||||
/// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/8ce7b38c-d3cc-415d-ab39-944000ea77ff
|
||||
#[derive(Debug, FromPrimitive, ToPrimitive)]
|
||||
#[derive(Debug, FromPrimitive, ToPrimitive, PartialEq, Clone)]
|
||||
#[repr(u8)]
|
||||
enum Boolean {
|
||||
True = 1,
|
||||
@@ -2469,7 +2612,7 @@ enum Boolean {
|
||||
|
||||
/// 2.4.8 FileBothDirectoryInformation
|
||||
/// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/270df317-9ba5-4ccb-ba00-8d22be139bc5
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct FileBothDirectoryInformation {
|
||||
next_entry_offset: u32,
|
||||
file_index: u32,
|
||||
@@ -2572,7 +2715,7 @@ impl FileBothDirectoryInformation {
|
||||
|
||||
/// 2.4.14 FileFullDirectoryInformation
|
||||
/// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/e8d926d1-3a22-4654-be9c-58317a85540b
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct FileFullDirectoryInformation {
|
||||
next_entry_offset: u32,
|
||||
file_index: u32,
|
||||
@@ -2666,7 +2809,7 @@ impl FileFullDirectoryInformation {
|
||||
|
||||
// 2.4.13 FileEndOfFileInformation
|
||||
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/75241cca-3167-472f-8058-a52d77c6bb17
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct FileEndOfFileInformation {
|
||||
end_of_file: i64,
|
||||
}
|
||||
@@ -2692,7 +2835,7 @@ impl FileEndOfFileInformation {
|
||||
|
||||
// 2.4.11 FileDispositionInformation
|
||||
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/12c3dd1c-14f6-4229-9d29-75fb2cb392f6
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct FileDispositionInformation {
|
||||
delete_pending: u8,
|
||||
}
|
||||
@@ -2718,10 +2861,11 @@ impl FileDispositionInformation {
|
||||
|
||||
// 2.4.37 FileRenameInformation
|
||||
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/1d2673a8-8fb9-4868-920a-775ccaa30cf8
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct FileRenameInformation {
|
||||
replace_if_exists: Boolean,
|
||||
file_name: String,
|
||||
/// file_name is the relative path to the new location of the file
|
||||
file_name: WindowsPath,
|
||||
}
|
||||
|
||||
impl FileRenameInformation {
|
||||
@@ -2738,7 +2882,7 @@ impl FileRenameInformation {
|
||||
// RootDirectory. For network operations, this value MUST be zero.
|
||||
w.write_u8(0)?;
|
||||
w.write_u32::<LittleEndian>(self.file_name.len() as u32)?;
|
||||
w.extend_from_slice(&util::to_unicode(&self.file_name, false));
|
||||
w.extend_from_slice(&util::to_unicode(&self.file_name.path, false));
|
||||
Ok(w)
|
||||
}
|
||||
|
||||
@@ -2750,7 +2894,7 @@ impl FileRenameInformation {
|
||||
let file_name_length = payload.read_u32::<LittleEndian>()?;
|
||||
let mut file_name = vec![0u8; file_name_length as usize];
|
||||
payload.read_exact(&mut file_name)?;
|
||||
let file_name = util::from_unicode(file_name)?;
|
||||
let file_name = WindowsPath::from(util::from_unicode(file_name)?);
|
||||
|
||||
Ok(Self {
|
||||
replace_if_exists: Boolean::from_u8(replace_if_exists).unwrap(),
|
||||
@@ -2765,7 +2909,7 @@ impl FileRenameInformation {
|
||||
|
||||
// 2.4.4 FileAllocationInformation
|
||||
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/0201c69b-50db-412d-bab3-dd97aeede13b
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct FileAllocationInformation {
|
||||
allocation_size: i64,
|
||||
}
|
||||
@@ -3388,7 +3532,7 @@ impl ClientDriveSetInformationResponse {
|
||||
|
||||
/// 2.2.3.3.9 Server Drive Set Information Request (DR_DRIVE_SET_INFORMATION_REQ)
|
||||
/// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/b5d3104b-0e42-4cf8-9059-e9fe86615e5c
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct ServerDriveSetInformationRequest {
|
||||
/// The MajorFunction field in the DR_DEVICE_IOREQUEST header MUST be set to IRP_MJ_SET_INFORMATION.
|
||||
device_io_request: DeviceIoRequest,
|
||||
@@ -3743,6 +3887,7 @@ type SharedDirectoryDeleteRequestSender =
|
||||
type SharedDirectoryListRequestSender = Box<dyn Fn(SharedDirectoryListRequest) -> RdpResult<()>>;
|
||||
type SharedDirectoryReadRequestSender = Box<dyn Fn(SharedDirectoryReadRequest) -> RdpResult<()>>;
|
||||
type SharedDirectoryWriteRequestSender = Box<dyn Fn(SharedDirectoryWriteRequest) -> RdpResult<()>>;
|
||||
type SharedDirectoryMoveRequestSender = Box<dyn Fn(SharedDirectoryMoveRequest) -> RdpResult<()>>;
|
||||
|
||||
type SharedDirectoryInfoResponseHandler =
|
||||
Box<dyn FnOnce(&mut Client, SharedDirectoryInfoResponse) -> RdpResult<Vec<Vec<u8>>>>;
|
||||
@@ -3756,6 +3901,8 @@ type SharedDirectoryReadResponseHandler =
|
||||
Box<dyn FnOnce(&mut Client, SharedDirectoryReadResponse) -> RdpResult<Vec<Vec<u8>>>>;
|
||||
type SharedDirectoryWriteResponseHandler =
|
||||
Box<dyn FnOnce(&mut Client, SharedDirectoryWriteResponse) -> RdpResult<Vec<Vec<u8>>>>;
|
||||
type SharedDirectoryMoveResponseHandler =
|
||||
Box<dyn FnOnce(&mut Client, SharedDirectoryMoveResponse) -> RdpResult<Vec<Vec<u8>>>>;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
@@ -26,7 +26,13 @@ use std::ffi::{CString, NulError};
|
||||
/// r"2018\January.xlsx": A relative path to a file in a subdirectory of the current directory.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WindowsPath {
|
||||
path: String,
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
impl WindowsPath {
|
||||
pub fn len(&self) -> u32 {
|
||||
self.path.len() as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for WindowsPath {
|
||||
|
||||
@@ -67,6 +67,8 @@ const (
|
||||
TypeSharedDirectoryReadResponse = MessageType(20)
|
||||
TypeSharedDirectoryWriteRequest = MessageType(21)
|
||||
TypeSharedDirectoryWriteResponse = MessageType(22)
|
||||
TypeSharedDirectoryMoveRequest = MessageType(23)
|
||||
TypeSharedDirectoryMoveResponse = MessageType(24)
|
||||
TypeSharedDirectoryListRequest = MessageType(25)
|
||||
TypeSharedDirectoryListResponse = MessageType(26)
|
||||
)
|
||||
@@ -149,6 +151,10 @@ func decode(in peekReader) (Message, error) {
|
||||
return decodeSharedDirectoryWriteRequest(in)
|
||||
case TypeSharedDirectoryWriteResponse:
|
||||
return decodeSharedDirectoryWriteResponse(in)
|
||||
case TypeSharedDirectoryMoveRequest:
|
||||
return decodeSharedDirectoryMoveRequest(in)
|
||||
case TypeSharedDirectoryMoveResponse:
|
||||
return decodeSharedDirectoryMoveResponse(in)
|
||||
default:
|
||||
return nil, trace.BadParameter("unsupported desktop protocol message type %d", t)
|
||||
}
|
||||
@@ -1120,7 +1126,6 @@ type SharedDirectoryReadRequest struct {
|
||||
CompletionID uint32
|
||||
DirectoryID uint32
|
||||
Path string
|
||||
PathLength uint32
|
||||
Offset uint64
|
||||
Length uint32
|
||||
}
|
||||
@@ -1149,7 +1154,7 @@ func decodeSharedDirectoryReadRequest(in peekReader) (SharedDirectoryReadRequest
|
||||
return SharedDirectoryReadRequest{}, trace.BadParameter("got message type %v, expected TypeSharedDirectoryReadRequest(%v)", t, TypeSharedDirectoryReadRequest)
|
||||
}
|
||||
|
||||
var completionID, directoryID, pathLength, length uint32
|
||||
var completionID, directoryID, length uint32
|
||||
var offset uint64
|
||||
|
||||
err = binary.Read(in, binary.BigEndian, &completionID)
|
||||
@@ -1167,11 +1172,6 @@ func decodeSharedDirectoryReadRequest(in peekReader) (SharedDirectoryReadRequest
|
||||
return SharedDirectoryReadRequest{}, trace.Wrap(err)
|
||||
}
|
||||
|
||||
err = binary.Read(in, binary.BigEndian, &pathLength)
|
||||
if err != nil {
|
||||
return SharedDirectoryReadRequest{}, trace.Wrap(err)
|
||||
}
|
||||
|
||||
err = binary.Read(in, binary.BigEndian, &offset)
|
||||
if err != nil {
|
||||
return SharedDirectoryReadRequest{}, trace.Wrap(err)
|
||||
@@ -1186,7 +1186,6 @@ func decodeSharedDirectoryReadRequest(in peekReader) (SharedDirectoryReadRequest
|
||||
CompletionID: completionID,
|
||||
DirectoryID: directoryID,
|
||||
Path: path,
|
||||
PathLength: pathLength,
|
||||
Offset: offset,
|
||||
Length: length,
|
||||
}, nil
|
||||
@@ -1259,7 +1258,6 @@ type SharedDirectoryWriteRequest struct {
|
||||
DirectoryID uint32
|
||||
Offset uint64
|
||||
Path string
|
||||
PathLength uint32
|
||||
WriteDataLength uint32
|
||||
WriteData []byte
|
||||
}
|
||||
@@ -1291,7 +1289,7 @@ func decodeSharedDirectoryWriteRequest(in peekReader) (SharedDirectoryWriteReque
|
||||
return SharedDirectoryWriteRequest{}, trace.BadParameter("got message type %v, expected TypeSharedDirectoryWriteRequest(%v)", t, TypeSharedDirectoryWriteRequest)
|
||||
}
|
||||
|
||||
var completionID, directoryID, pathLength, writeDataLength uint32
|
||||
var completionID, directoryID, writeDataLength uint32
|
||||
var offset uint64
|
||||
|
||||
err = binary.Read(in, binary.BigEndian, &completionID)
|
||||
@@ -1314,11 +1312,6 @@ func decodeSharedDirectoryWriteRequest(in peekReader) (SharedDirectoryWriteReque
|
||||
return SharedDirectoryWriteRequest{}, trace.Wrap(err)
|
||||
}
|
||||
|
||||
err = binary.Read(in, binary.BigEndian, &pathLength)
|
||||
if err != nil {
|
||||
return SharedDirectoryWriteRequest{}, trace.Wrap(err)
|
||||
}
|
||||
|
||||
err = binary.Read(in, binary.BigEndian, &writeDataLength)
|
||||
if err != nil {
|
||||
return SharedDirectoryWriteRequest{}, trace.Wrap(err)
|
||||
@@ -1333,7 +1326,6 @@ func decodeSharedDirectoryWriteRequest(in peekReader) (SharedDirectoryWriteReque
|
||||
CompletionID: completionID,
|
||||
DirectoryID: directoryID,
|
||||
Path: path,
|
||||
PathLength: pathLength,
|
||||
Offset: offset,
|
||||
WriteDataLength: writeDataLength,
|
||||
WriteData: writeData,
|
||||
@@ -1370,6 +1362,88 @@ func decodeSharedDirectoryWriteResponse(in peekReader) (SharedDirectoryWriteResp
|
||||
return res, err
|
||||
}
|
||||
|
||||
// SharedDirectoryMoveRequest is sent from the TDP server to the client
|
||||
// to request a file at original_path be moved to new_path.
|
||||
type SharedDirectoryMoveRequest struct {
|
||||
CompletionID uint32
|
||||
DirectoryID uint32
|
||||
OriginalPath string
|
||||
NewPath string
|
||||
}
|
||||
|
||||
func (s SharedDirectoryMoveRequest) Encode() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
buf.WriteByte(byte(TypeSharedDirectoryMoveRequest))
|
||||
binary.Write(buf, binary.BigEndian, s.CompletionID)
|
||||
binary.Write(buf, binary.BigEndian, s.DirectoryID)
|
||||
if err := encodeString(buf, s.OriginalPath); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
if err := encodeString(buf, s.NewPath); err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func decodeSharedDirectoryMoveRequest(in peekReader) (SharedDirectoryMoveRequest, error) {
|
||||
t, err := in.ReadByte()
|
||||
if err != nil {
|
||||
return SharedDirectoryMoveRequest{}, trace.Wrap(err)
|
||||
}
|
||||
if t != byte(TypeSharedDirectoryMoveRequest) {
|
||||
return SharedDirectoryMoveRequest{}, trace.BadParameter("got message type %v, expected TypeClientUsername(%v)", t, TypeClientUsername)
|
||||
}
|
||||
var completionID, directoryID uint32
|
||||
err = binary.Read(in, binary.BigEndian, &completionID)
|
||||
if err != nil {
|
||||
return SharedDirectoryMoveRequest{}, trace.Wrap(err)
|
||||
}
|
||||
err = binary.Read(in, binary.BigEndian, &directoryID)
|
||||
if err != nil {
|
||||
return SharedDirectoryMoveRequest{}, trace.Wrap(err)
|
||||
}
|
||||
originalPath, err := decodeString(in, windowsMaxUsernameLength)
|
||||
if err != nil {
|
||||
return SharedDirectoryMoveRequest{}, trace.Wrap(err)
|
||||
}
|
||||
newPath, err := decodeString(in, windowsMaxUsernameLength)
|
||||
if err != nil {
|
||||
return SharedDirectoryMoveRequest{}, trace.Wrap(err)
|
||||
}
|
||||
return SharedDirectoryMoveRequest{
|
||||
CompletionID: completionID,
|
||||
DirectoryID: directoryID,
|
||||
OriginalPath: originalPath,
|
||||
NewPath: newPath,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type SharedDirectoryMoveResponse struct {
|
||||
CompletionID uint32
|
||||
ErrCode uint32
|
||||
}
|
||||
|
||||
func (s SharedDirectoryMoveResponse) Encode() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
buf.WriteByte(byte(TypeSharedDirectoryMoveResponse))
|
||||
binary.Write(buf, binary.BigEndian, s)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func decodeSharedDirectoryMoveResponse(in peekReader) (SharedDirectoryMoveResponse, error) {
|
||||
t, err := in.ReadByte()
|
||||
if err != nil {
|
||||
return SharedDirectoryMoveResponse{}, trace.Wrap(err)
|
||||
}
|
||||
if t != byte(TypeSharedDirectoryMoveResponse) {
|
||||
return SharedDirectoryMoveResponse{}, trace.BadParameter("got message type %v, expected SharedDirectoryMoveResponse(%v)", t, TypeSharedDirectoryMoveResponse)
|
||||
}
|
||||
|
||||
var res SharedDirectoryMoveResponse
|
||||
err = binary.Read(in, binary.BigEndian, &res)
|
||||
return res, err
|
||||
}
|
||||
|
||||
// encodeString encodes strings for TDP. Strings are encoded as UTF-8 with
|
||||
// a 32-bit length prefix (in bytes):
|
||||
// https://github.com/gravitational/teleport/blob/master/rfd/0037-desktop-access-protocol.md#field-types
|
||||
|
||||
Reference in New Issue
Block a user