Windows Desktop Directory Sharing (#13630)

* `IRP_MJ_CREATE` (#12665)

* `IRP_MJ_QUERY_INFORMATION` (#12717)

* `IRP_MJ_CLOSE` (#12729)

* Refactor rdpdr client (#12750)

* Adding logic for `FILE_SUPERSEDE` (#12829)

* Improve `process_irp_create` (#12830)

* adds return statements that got lost in a merge

* `IRP_MJ_DIRECTORY_CONTROL` (#12870)

* `FileFullDirectoryInformation` (#12908)

* Improve `ClientDriveQueryDirectoryResponse.encode()` (#12912)

* `IRP_MJ_QUERY_VOLUME_INFORMATION` (#13071)

* Fix Shared Directory Request handling when feature is disabled (#13439)

* IRP_MJ_READ, IRP_MJ_WRITE, and IRP_MJ_SET_INFORMATION (#13995)

* Adds constants for sizing calculations (#14051)

Co-authored-by: Łukasz Kozłowski <lukasz.kozlowski@goteleport.com>
Co-authored-by: Zac Bergquist <zac.bergquist@goteleport.com>
This commit is contained in:
Isaiah Becker-Mayer 2022-08-04 17:50:02 -04:00 committed by GitHub
parent ced6276c7b
commit 361ea8ef3f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 4062 additions and 447 deletions

103
Cargo.lock generated
View file

@ -66,9 +66,9 @@ checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
[[package]]
name = "base64ct"
version = "1.5.0"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179"
checksum = "3bdca834647821e0b13d9539a8634eb62d3501b6b6c2cec1722786ee6671b851"
[[package]]
name = "bit_field"
@ -166,9 +166,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "3.2.8"
version = "3.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "190814073e85d238f31ff738fcb0bf6910cedeb73376c87cd69291028966fd83"
checksum = "54635806b078b7925d6e36810b1755f2a4b5b4d57560432c1ecf60bcbe10602b"
dependencies = [
"atty",
"bitflags",
@ -250,9 +250,9 @@ dependencies = [
[[package]]
name = "delog"
version = "0.1.4"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb73cae03ad02cd38353f93fe84b288daffcb6371212226e09b9a4c7fc93b03f"
checksum = "e371811fb858c17e75e0316e7ebf8db0343b10d73038a3b9571006b0e7e5cc53"
dependencies = [
"log",
]
@ -378,13 +378,13 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.6"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
dependencies = [
"cfg-if",
"libc",
"wasi 0.10.2+wasi-snapshot-preview1",
"wasi 0.11.0+wasi-snapshot-preview1",
]
[[package]]
@ -398,15 +398,15 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.11.2"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3"
[[package]]
name = "heapless"
version = "0.7.13"
version = "0.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a08e755adbc0ad283725b29f4a4883deee15336f372d5f61fae59efec40f983"
checksum = "065681e99f9ef7e0e813702a0326aedbcbbde7db5e55f097aedd1bf50b9dca43"
dependencies = [
"atomic-polyfill",
"hash32",
@ -448,9 +448,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "indexmap"
version = "1.8.2"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a"
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
dependencies = [
"autocfg",
"hashbrown",
@ -492,9 +492,9 @@ checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
[[package]]
name = "js-sys"
version = "0.3.57"
version = "0.3.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397"
checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27"
dependencies = [
"wasm-bindgen",
]
@ -713,9 +713,9 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
[[package]]
name = "os_str_bytes"
version = "6.1.0"
version = "6.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa"
checksum = "648001efe5d5c0102d8cea768e348da85d90af8ba91f0bea908f157951493cd4"
[[package]]
name = "pem-rfc7468"
@ -765,18 +765,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.39"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.18"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
dependencies = [
"proc-macro2",
]
@ -840,7 +840,7 @@ version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom 0.2.6",
"getrandom 0.2.7",
]
[[package]]
@ -878,7 +878,7 @@ dependencies = [
[[package]]
name = "rdp-rs"
version = "0.1.0"
source = "git+https://github.com/gravitational/rdp-rs?rev=6075679e7c9bd8e3c2136a87f097d05c7db3235f#6075679e7c9bd8e3c2136a87f097d05c7db3235f"
source = "git+https://github.com/gravitational/rdp-rs?rev=004207e2edfbc6a57fd04daf6bcade93bcc3495e#004207e2edfbc6a57fd04daf6bcade93bcc3495e"
dependencies = [
"bufstream",
"byteorder",
@ -1100,9 +1100,9 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.8.0"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
checksum = "cc88c725d61fc6c3132893370cac4a0200e3fedf5da8331c570664b1987f5ca2"
[[package]]
name = "spin"
@ -1161,9 +1161,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "syn"
version = "1.0.96"
version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf"
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
dependencies = [
"proc-macro2",
"quote",
@ -1201,11 +1201,12 @@ checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
[[package]]
name = "time"
version = "0.1.43"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
dependencies = [
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
"winapi",
]
@ -1226,9 +1227,9 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]]
name = "unicode-ident"
version = "1.0.0"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
[[package]]
name = "untrusted"
@ -1257,7 +1258,7 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f"
dependencies = [
"getrandom 0.2.6",
"getrandom 0.2.7",
]
[[package]]
@ -1295,15 +1296,21 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.80"
version = "0.2.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad"
checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@ -1311,9 +1318,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.80"
version = "0.2.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4"
checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a"
dependencies = [
"bumpalo",
"lazy_static",
@ -1326,9 +1333,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.80"
version = "0.2.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5"
checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -1336,9 +1343,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.80"
version = "0.2.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b"
checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048"
dependencies = [
"proc-macro2",
"quote",
@ -1349,15 +1356,15 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.80"
version = "0.2.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744"
checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be"
[[package]]
name = "web-sys"
version = "0.3.57"
version = "0.3.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283"
checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90"
dependencies = [
"js-sys",
"wasm-bindgen",

View file

@ -20,7 +20,7 @@ num-traits = "0.2.15"
rand = { version = "0.8.5", features = ["getrandom"] }
rand_chacha = "0.3.1"
rsa = "0.6.1"
rdp-rs = { git = "https://github.com/gravitational/rdp-rs", rev = "6075679e7c9bd8e3c2136a87f097d05c7db3235f" }
rdp-rs = { git = "https://github.com/gravitational/rdp-rs", rev = "004207e2edfbc6a57fd04daf6bcade93bcc3495e" }
uuid = { version = "1.1.2", features = ["v4"] }
utf16string = "0.2.0"

View file

@ -412,11 +412,11 @@ func (c *Client) start() {
defer C.free(unsafe.Pointer(path))
if errCode := C.handle_tdp_sd_info_response(c.rustClient, C.CGOSharedDirectoryInfoResponse{
completion_id: C.uint32_t(m.CompletionID),
err_code: C.uint32_t(m.ErrCode),
err_code: m.ErrCode,
fso: C.CGOFileSystemObject{
last_modified: C.uint64_t(m.Fso.LastModified),
size: C.uint64_t(m.Fso.Size),
file_type: C.uint32_t(m.Fso.FileType),
file_type: m.Fso.FileType,
path: path,
},
}); errCode != C.ErrCodeSuccess {
@ -424,6 +424,91 @@ func (c *Client) start() {
return
}
}
case tdp.SharedDirectoryCreateResponse:
if c.cfg.AllowDirectorySharing {
if errCode := C.handle_tdp_sd_create_response(c.rustClient, C.CGOSharedDirectoryCreateResponse{
completion_id: C.uint32_t(m.CompletionID),
err_code: m.ErrCode,
}); errCode != C.ErrCodeSuccess {
c.cfg.Log.Errorf("SharedDirectoryCreateResponse failed: %v", errCode)
return
}
}
case tdp.SharedDirectoryDeleteResponse:
if c.cfg.AllowDirectorySharing {
if errCode := C.handle_tdp_sd_delete_response(c.rustClient, C.CGOSharedDirectoryDeleteResponse{
completion_id: C.uint32_t(m.CompletionID),
err_code: m.ErrCode,
}); errCode != C.ErrCodeSuccess {
c.cfg.Log.Errorf("SharedDirectoryDeleteResponse failed: %v", errCode)
return
}
}
case tdp.SharedDirectoryListResponse:
if c.cfg.AllowDirectorySharing {
fsoList := make([]C.CGOFileSystemObject, 0, len(m.FsoList))
for _, fso := range m.FsoList {
path := C.CString(fso.Path)
defer C.free(unsafe.Pointer(path))
fsoList = append(fsoList, C.CGOFileSystemObject{
last_modified: C.uint64_t(fso.LastModified),
size: C.uint64_t(fso.Size),
file_type: fso.FileType,
path: path,
})
}
fsoListLen := len(fsoList)
var cgoFsoList *C.CGOFileSystemObject
if fsoListLen > 0 {
cgoFsoList = (*C.CGOFileSystemObject)(unsafe.Pointer(&fsoList[0]))
} else {
cgoFsoList = (*C.CGOFileSystemObject)(unsafe.Pointer(&fsoList))
}
if errCode := C.handle_tdp_sd_list_response(c.rustClient, C.CGOSharedDirectoryListResponse{
completion_id: C.uint32_t(m.CompletionID),
err_code: m.ErrCode,
fso_list_length: C.uint32_t(fsoListLen),
fso_list: cgoFsoList,
}); errCode != C.ErrCodeSuccess {
c.cfg.Log.Errorf("SharedDirectoryListResponse failed: %v", errCode)
return
}
}
case tdp.SharedDirectoryReadResponse:
if c.cfg.AllowDirectorySharing {
var readData *C.uint8_t
if m.ReadDataLength > 0 {
readData = (*C.uint8_t)(unsafe.Pointer(&m.ReadData[0]))
} else {
readData = (*C.uint8_t)(unsafe.Pointer(&m.ReadData))
}
if errCode := C.handle_tdp_sd_read_response(c.rustClient, C.CGOSharedDirectoryReadResponse{
completion_id: C.uint32_t(m.CompletionID),
err_code: m.ErrCode,
read_data_length: C.uint32_t(m.ReadDataLength),
read_data: readData,
}); errCode != C.ErrCodeSuccess {
c.cfg.Log.Errorf("SharedDirectoryReadResponse failed: %v", errCode)
return
}
}
case tdp.SharedDirectoryWriteResponse:
if c.cfg.AllowDirectorySharing {
if errCode := C.handle_tdp_sd_write_response(c.rustClient, C.CGOSharedDirectoryWriteResponse{
completion_id: C.uint32_t(m.CompletionID),
err_code: m.ErrCode,
bytes_written: C.uint32_t(m.BytesWritten),
}); errCode != C.ErrCodeSuccess {
c.cfg.Log.Errorf("SharedDirectoryWriteResponse failed: %v", errCode)
return
}
}
default:
c.cfg.Log.Warningf("Skipping unimplemented TDP message type %T", msg)
}
@ -497,15 +582,18 @@ func tdp_sd_acknowledge(handle C.uintptr_t, ack *C.CGOSharedDirectoryAcknowledge
})
}
// sharedDirectoryAcknowledge acknowledges that a `Shared Directory Announce` TDP message was processed.
// 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
}
return C.ErrCodeSuccess
return C.ErrCodeFailure
}
//export tdp_sd_info_request
@ -517,14 +605,139 @@ 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
}
return C.ErrCodeSuccess
return C.ErrCodeFailure
}
//export tdp_sd_create_request
func tdp_sd_create_request(handle C.uintptr_t, req *C.CGOSharedDirectoryCreateRequest) C.CGOErrCode {
return cgo.Handle(handle).Value().(*Client).sharedDirectoryCreateRequest(tdp.SharedDirectoryCreateRequest{
CompletionID: uint32(req.completion_id),
DirectoryID: uint32(req.directory_id),
FileType: uint32(req.file_type),
Path: C.GoString(req.path),
})
}
// 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
}
return C.ErrCodeFailure
}
//export tdp_sd_delete_request
func tdp_sd_delete_request(handle C.uintptr_t, req *C.CGOSharedDirectoryDeleteRequest) C.CGOErrCode {
return cgo.Handle(handle).Value().(*Client).sharedDirectoryDeleteRequest(tdp.SharedDirectoryDeleteRequest{
CompletionID: uint32(req.completion_id),
DirectoryID: uint32(req.directory_id),
Path: C.GoString(req.path),
})
}
// 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
}
return C.ErrCodeFailure
}
//export tdp_sd_list_request
func tdp_sd_list_request(handle C.uintptr_t, req *C.CGOSharedDirectoryListRequest) C.CGOErrCode {
return cgo.Handle(handle).Value().(*Client).sharedDirectoryListRequest(tdp.SharedDirectoryListRequest{
CompletionID: uint32(req.completion_id),
DirectoryID: uint32(req.directory_id),
Path: C.GoString(req.path),
})
}
// 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
}
return C.ErrCodeFailure
}
//export tdp_sd_read_request
func tdp_sd_read_request(handle C.uintptr_t, req *C.CGOSharedDirectoryReadRequest) C.CGOErrCode {
return cgo.Handle(handle).Value().(*Client).sharedDirectoryReadRequest(tdp.SharedDirectoryReadRequest{
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),
})
}
// 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
}
return C.ErrCodeFailure
}
//export tdp_sd_write_request
func tdp_sd_write_request(handle C.uintptr_t, req *C.CGOSharedDirectoryWriteRequest) C.CGOErrCode {
return cgo.Handle(handle).Value().(*Client).sharedDirectoryWriteRequest(tdp.SharedDirectoryWriteRequest{
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)),
})
}
// 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
}
return C.ErrCodeFailure
}
// close closes the RDP client connection and

View file

@ -24,6 +24,7 @@ extern crate log;
#[macro_use]
extern crate num_derive;
use errors::try_error;
use libc::{fd_set, select, FD_SET};
use rand::Rng;
use rand::SeedableRng;
@ -164,8 +165,8 @@ pub unsafe extern "C" fn connect_rdp(
// Convert from C to Rust types.
let addr = from_go_string(go_addr);
let username = from_go_string(go_username);
let cert_der = from_go_array(cert_der_len, cert_der);
let key_der = from_go_array(key_der_len, key_der);
let cert_der = from_go_array(cert_der, cert_der_len);
let key_der = from_go_array(key_der, key_der_len);
connect_rdp_inner(
go_ref,
@ -288,22 +289,22 @@ fn connect_rdp_inner(
"rdp-rs",
);
let tdp_sd_acknowledge = Box::new(move |ack: SharedDirectoryAcknowledge| -> RdpResult<()> {
debug!("sending: {:?}", ack);
unsafe {
if tdp_sd_acknowledge(go_ref, &mut CGOSharedDirectoryAcknowledge::from(ack))
!= CGOErrCode::ErrCodeSuccess
{
return Err(RdpError::TryError(String::from(
"call to tdp_sd_acknowledge failed",
)));
let tdp_sd_acknowledge = Box::new(
move |mut ack: SharedDirectoryAcknowledge| -> RdpResult<()> {
debug!("sending TDP SharedDirectoryAcknowledge: {:?}", ack);
unsafe {
if tdp_sd_acknowledge(go_ref, &mut ack) != CGOErrCode::ErrCodeSuccess {
return Err(RdpError::TryError(String::from(
"call to tdp_sd_acknowledge failed",
)));
}
Ok(())
}
}
Ok(())
});
},
);
let tdp_sd_info_request = Box::new(move |req: SharedDirectoryInfoRequest| -> RdpResult<()> {
debug!("sending: {:?}", req);
debug!("sending TDP SharedDirectoryInfoRequest: {:?}", req);
// Create C compatible string from req.path
match CString::new(req.path.clone()) {
Ok(c_string) => {
@ -334,15 +335,188 @@ fn connect_rdp_inner(
}
});
let tdp_sd_create_request =
Box::new(move |req: SharedDirectoryCreateRequest| -> RdpResult<()> {
debug!("sending TDP SharedDirectoryCreateRequest: {:?}", req);
// Create C compatible string from req.path
match CString::new(req.path.clone()) {
Ok(c_string) => {
unsafe {
let err = tdp_sd_create_request(
go_ref,
&mut CGOSharedDirectoryCreateRequest {
completion_id: req.completion_id,
directory_id: req.directory_id,
file_type: req.file_type,
path: c_string.as_ptr(),
},
);
if err != CGOErrCode::ErrCodeSuccess {
return Err(RdpError::TryError(String::from(
"call to tdp_sd_create_request failed",
)));
};
}
Ok(())
}
Err(_) => {
// TODO(isaiah): change TryError to TeleportError for a generic error caused by Teleport specific code.
return Err(RdpError::TryError(format!(
"path contained characters that couldn't be converted to a C string: {}",
req.path
)));
}
}
});
let tdp_sd_delete_request =
Box::new(move |req: SharedDirectoryDeleteRequest| -> RdpResult<()> {
debug!("sending TDP SharedDirectoryDeleteRequest: {:?}", req);
// Create C compatible string from req.path
match CString::new(req.path.clone()) {
Ok(c_string) => {
unsafe {
let err = tdp_sd_delete_request(
go_ref,
&mut CGOSharedDirectoryDeleteRequest {
completion_id: req.completion_id,
directory_id: req.directory_id,
path: c_string.as_ptr(),
},
);
if err != CGOErrCode::ErrCodeSuccess {
return Err(RdpError::TryError(String::from(
"call to tdp_sd_delete_request failed",
)));
};
}
Ok(())
}
Err(_) => {
// TODO(isaiah): change TryError to TeleportError for a generic error caused by Teleport specific code.
return Err(RdpError::TryError(format!(
"path contained characters that couldn't be converted to a C string: {}",
req.path
)));
}
}
});
let tdp_sd_list_request = Box::new(move |req: SharedDirectoryListRequest| -> RdpResult<()> {
debug!("sending TDP SharedDirectoryListRequest: {:?}", req);
// Create C compatible string from req.path
match CString::new(req.path.clone()) {
Ok(c_string) => {
unsafe {
let err = tdp_sd_list_request(
go_ref,
&mut CGOSharedDirectoryListRequest {
completion_id: req.completion_id,
directory_id: req.directory_id,
path: c_string.as_ptr(),
},
);
if err != CGOErrCode::ErrCodeSuccess {
return Err(RdpError::TryError(String::from(
"call to tdp_sd_list_request failed",
)));
};
}
Ok(())
}
Err(_) => {
// TODO(isaiah): change TryError to TeleportError for a generic error caused by Teleport specific code.
return Err(RdpError::TryError(format!(
"path contained characters that couldn't be converted to a C string: {}",
req.path
)));
}
}
});
let tdp_sd_read_request = Box::new(move |req: SharedDirectoryReadRequest| -> RdpResult<()> {
debug!("sending: {:?}", req);
match CString::new(req.path.clone()) {
Ok(c_string) => {
unsafe {
let err = tdp_sd_read_request(
go_ref,
&mut CGOSharedDirectoryReadRequest {
completion_id: req.completion_id,
directory_id: req.directory_id,
path: c_string.as_ptr(),
path_length: req.path.len() as u32,
offset: req.offset,
length: req.length,
},
);
if err != CGOErrCode::ErrCodeSuccess {
return Err(RdpError::TryError(String::from(
"call to tdp_sd_read_request failed",
)));
}
}
Ok(())
}
Err(_) => {
return Err(RdpError::TryError(format!(
"path contained characters that couldn't be converted to a C string: {}",
req.path
)));
}
}
});
let tdp_sd_write_request = Box::new(move |req: SharedDirectoryWriteRequest| -> RdpResult<()> {
debug!("sending: {:?}", req);
match CString::new(req.path.clone()) {
Ok(c_string) => {
unsafe {
let err = tdp_sd_write_request(
go_ref,
&mut CGOSharedDirectoryWriteRequest {
completion_id: req.completion_id,
directory_id: req.directory_id,
offset: req.offset,
path: c_string.as_ptr(),
path_length: req.path.len() as u32,
write_data_length: req.write_data.len() as u32,
write_data: req.write_data.as_ptr() as *mut u8,
},
);
if err != CGOErrCode::ErrCodeSuccess {
return Err(RdpError::TryError(String::from(
"call to tdp_sd_write_failed",
)));
}
}
Ok(())
}
Err(_) => {
return Err(RdpError::TryError(format!(
"path contained characters that couldn't be converted to a C string: {}",
req.path
)));
}
}
});
// Client for the "rdpdr" channel - smartcard emulation and drive redirection.
let rdpdr = rdpdr::Client::new(
params.cert_der,
params.key_der,
let rdpdr = rdpdr::Client::new(rdpdr::Config {
cert_der: params.cert_der,
key_der: params.key_der,
pin,
params.allow_directory_sharing,
allow_directory_sharing: params.allow_directory_sharing,
tdp_sd_acknowledge,
tdp_sd_info_request,
);
tdp_sd_create_request,
tdp_sd_delete_request,
tdp_sd_list_request,
tdp_sd_read_request,
tdp_sd_write_request,
});
// Client for the "cliprdr" channel - clipboard sharing.
let cliprdr = if params.allow_clipboard {
@ -443,6 +617,41 @@ impl<S: Read + Write> RdpClient<S> {
self.rdpdr.handle_tdp_sd_info_response(res, &mut self.mcs)
}
pub fn handle_tdp_sd_create_response(
&mut self,
res: SharedDirectoryCreateResponse,
) -> RdpResult<()> {
self.rdpdr.handle_tdp_sd_create_response(res, &mut self.mcs)
}
pub fn handle_tdp_sd_delete_response(
&mut self,
res: SharedDirectoryDeleteResponse,
) -> RdpResult<()> {
self.rdpdr.handle_tdp_sd_delete_response(res, &mut self.mcs)
}
pub fn handle_tdp_sd_list_response(
&mut self,
res: SharedDirectoryListResponse,
) -> RdpResult<()> {
self.rdpdr.handle_tdp_sd_list_response(res, &mut self.mcs)
}
pub fn handle_tdp_sd_read_response(
&mut self,
res: SharedDirectoryReadResponse,
) -> RdpResult<()> {
self.rdpdr.handle_tdp_sd_read_response(res, &mut self.mcs)
}
pub fn handle_tdp_sd_write_response(
&mut self,
res: SharedDirectoryWriteResponse,
) -> RdpResult<()> {
self.rdpdr.handle_tdp_sd_write_response(res, &mut self.mcs)
}
pub fn shutdown(&mut self) -> RdpResult<()> {
self.mcs.shutdown()
}
@ -527,7 +736,11 @@ fn wait_for_fd(fd: usize) -> bool {
///
/// # Safety
///
/// `client_ptr` must be a valid pointer to a Client.
/// client_ptr MUST be a valid pointer.
/// (validity defined by https://doc.rust-lang.org/nightly/core/primitive.pointer.html#method.as_ref-1)
///
/// data MUST be a valid pointer.
/// (validity defined by the validity of data in https://doc.rust-lang.org/std/slice/fn.from_raw_parts_mut.html)
#[no_mangle]
pub unsafe extern "C" fn update_clipboard(
client_ptr: *mut Client,
@ -540,7 +753,7 @@ pub unsafe extern "C" fn update_clipboard(
return cgo_error;
}
};
let data = from_go_array(len, data);
let data = from_go_array(data, len);
let mut lock = client.rdp_client.lock().unwrap();
match lock.cliprdr {
@ -568,14 +781,26 @@ pub unsafe extern "C" fn update_clipboard(
/// handle_tdp_sd_announce announces a new drive that's ready to be
/// redirected over RDP.
///
///
/// # Safety
///
/// The caller must ensure that sd_announce.name points to a valid buffer.
/// client_ptr MUST be a valid pointer.
/// (validity defined by https://doc.rust-lang.org/nightly/core/primitive.pointer.html#method.as_ref-1)
///
/// sd_announce.name MUST be a non-null pointer to a C-style null terminated string.
#[no_mangle]
pub unsafe extern "C" fn handle_tdp_sd_announce(
client_ptr: *mut Client,
sd_announce: CGOSharedDirectoryAnnounce,
) -> 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 sd_announce = SharedDirectoryAnnounce::from(sd_announce);
let client = match Client::from_ptr(client_ptr) {
Ok(client) => client,
Err(cgo_error) => {
@ -583,9 +808,8 @@ pub unsafe extern "C" fn handle_tdp_sd_announce(
}
};
let drive_name = from_go_string(sd_announce.name);
let new_drive =
rdpdr::ClientDeviceListAnnounce::new_drive(sd_announce.directory_id, drive_name);
rdpdr::ClientDeviceListAnnounce::new_drive(sd_announce.directory_id, sd_announce.name);
let mut rdp_client = client.rdp_client.lock().unwrap();
match rdp_client.write_client_device_list_announce(new_drive) {
@ -602,11 +826,165 @@ pub unsafe extern "C" fn handle_tdp_sd_announce(
///
/// # Safety
///
/// The caller must ensure that res.fso.path points to a valid buffer.
/// client_ptr MUST be a valid pointer.
/// (validity defined by https://doc.rust-lang.org/nightly/core/primitive.pointer.html#method.as_ref-1)
///
/// res.fso.path MUST be a non-null pointer to a C-style null terminated string.
#[no_mangle]
pub unsafe extern "C" fn handle_tdp_sd_info_response(
client_ptr: *mut Client,
res: CGOSharedDirectoryInfoResponse,
) -> 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 = SharedDirectoryInfoResponse::from(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_info_response(res) {
Ok(()) => CGOErrCode::ErrCodeSuccess,
Err(e) => {
error!("failed to handle Shared Directory Info Response: {:?}", e);
CGOErrCode::ErrCodeFailure
}
}
}
/// handle_tdp_sd_create_response handles a TDP Shared Directory Create 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_create_response(
client_ptr: *mut Client,
res: CGOSharedDirectoryCreateResponse,
) -> 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: SharedDirectoryCreateResponse = 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_create_response(res) {
Ok(()) => CGOErrCode::ErrCodeSuccess,
Err(e) => {
error!("failed to handle Shared Directory Create Response: {:?}", e);
CGOErrCode::ErrCodeFailure
}
}
}
/// handle_tdp_sd_delete_response handles a TDP Shared Directory Delete 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_delete_response(
client_ptr: *mut Client,
res: CGOSharedDirectoryDeleteResponse,
) -> 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: SharedDirectoryDeleteResponse = 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_delete_response(res) {
Ok(()) => CGOErrCode::ErrCodeSuccess,
Err(e) => {
error!("failed to handle Shared Directory Create Response: {:?}", e);
CGOErrCode::ErrCodeFailure
}
}
}
/// handle_tdp_sd_list_response handles a TDP Shared Directory List 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)
///
/// res.fso_list MUST be a valid pointer
/// (validity defined by the validity of data in https://doc.rust-lang.org/std/slice/fn.from_raw_parts_mut.html)
///
/// each res.fso_list[i].path MUST be a non-null pointer to a C-style null terminated string.
#[no_mangle]
pub unsafe extern "C" fn handle_tdp_sd_list_response(
client_ptr: *mut Client,
res: CGOSharedDirectoryListResponse,
) -> 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 = SharedDirectoryListResponse::from(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_list_response(res) {
Ok(()) => CGOErrCode::ErrCodeSuccess,
Err(e) => {
error!("failed to handle Shared Directory List Response: {:?}", e);
CGOErrCode::ErrCodeFailure
}
}
}
/// handle_tdp_sd_read_response handles a TDP Shared Directory Read Response
/// message
///
/// # Safety
///
/// client_ptr must be a valid pointer
#[no_mangle]
pub unsafe extern "C" fn handle_tdp_sd_read_response(
client_ptr: *mut Client,
res: CGOSharedDirectoryReadResponse,
) -> CGOErrCode {
let client = match Client::from_ptr(client_ptr) {
Ok(client) => client,
@ -616,10 +994,39 @@ pub unsafe extern "C" fn handle_tdp_sd_info_response(
};
let mut rdp_client = client.rdp_client.lock().unwrap();
match rdp_client.handle_tdp_sd_info_response(SharedDirectoryInfoResponse::from(res)) {
match rdp_client.handle_tdp_sd_read_response(SharedDirectoryReadResponse::from(res)) {
Ok(()) => CGOErrCode::ErrCodeSuccess,
Err(e) => {
error!("failed to handle Shared Directory Info Response: {:?}", e);
error!("failed to handle Shared Directory Read Response: {:?}", e);
CGOErrCode::ErrCodeFailure
}
}
}
/// handle_tdp_sd_write_response handles a TDP Shared Directory Write Response
/// message
///
/// # Safety
///
/// client_ptr must be a valid pointer
#[no_mangle]
pub unsafe extern "C" fn handle_tdp_sd_write_response(
client_ptr: *mut Client,
res: CGOSharedDirectoryWriteResponse,
) -> CGOErrCode {
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_write_response(res) {
Ok(()) => CGOErrCode::ErrCodeSuccess,
Err(e) => {
error!("failed to handle Shared Directory Write Response: {:?}", e);
CGOErrCode::ErrCodeFailure
}
}
@ -735,6 +1142,11 @@ pub enum CGOPointerWheel {
impl From<CGOMousePointerEvent> for PointerEvent {
fn from(p: CGOMousePointerEvent) -> PointerEvent {
// # 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.
PointerEvent {
x: p.x,
y: p.y,
@ -757,7 +1169,8 @@ impl From<CGOMousePointerEvent> for PointerEvent {
/// # Safety
///
/// client_ptr must be a valid pointer to a Client.
/// 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 write_rdp_pointer(
client_ptr: *mut Client,
@ -797,6 +1210,11 @@ pub struct CGOKeyboardEvent {
impl From<CGOKeyboardEvent> for KeyboardEvent {
fn from(k: CGOKeyboardEvent) -> KeyboardEvent {
// # 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.
KeyboardEvent {
code: k.code,
down: k.down,
@ -806,7 +1224,8 @@ impl From<CGOKeyboardEvent> for KeyboardEvent {
/// # Safety
///
/// client_ptr must be a valid pointer to a Client.
/// 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 write_rdp_keyboard(
client_ptr: *mut Client,
@ -860,7 +1279,8 @@ pub unsafe extern "C" fn close_rdp(client_ptr: *mut Client) -> CGOErrCode {
///
/// # Safety
///
/// client_ptr must be a valid pointer to a Client.
/// 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 free_rdp(client_ptr: *mut Client) {
drop(Client::from_raw(client_ptr))
@ -872,14 +1292,24 @@ pub unsafe extern "C" fn free_rdp(client_ptr: *mut Client) {
/// s is cloned here, and the caller is responsible for
/// ensuring its memory is freed.
unsafe fn from_go_string(s: *const c_char) -> String {
// # 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.
CStr::from_ptr(s).to_string_lossy().into_owned()
}
/// # Safety
///
/// ptr must be a valid buffer of len bytes.
unsafe fn from_go_array(len: u32, ptr: *mut u8) -> Vec<u8> {
slice::from_raw_parts(ptr, len as usize).to_vec()
/// See https://doc.rust-lang.org/std/slice/fn.from_raw_parts_mut.html
unsafe fn from_go_array<T: Clone>(data: *mut T, len: u32) -> Vec<T> {
// # 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.
slice::from_raw_parts(data, len as usize).to_vec()
}
#[repr(C)]
@ -895,29 +1325,42 @@ pub struct CGOSharedDirectoryAnnounce {
pub name: *const c_char,
}
/// SharedDirectoryAcknowledge is a CGO-compatible version of
/// the TDP Shared Directory Knowledge message that we pass back to Go.
#[derive(Debug)]
pub struct SharedDirectoryAcknowledge {
pub err_code: u32,
pub directory_id: u32,
/// SharedDirectoryAnnounce is sent by the TDP client to the server
/// to announce a new directory to be shared over TDP.
pub struct SharedDirectoryAnnounce {
directory_id: u32,
name: String,
}
#[repr(C)]
pub struct CGOSharedDirectoryAcknowledge {
pub err_code: u32,
pub directory_id: u32,
}
impl From<SharedDirectoryAcknowledge> for CGOSharedDirectoryAcknowledge {
fn from(ack: SharedDirectoryAcknowledge) -> CGOSharedDirectoryAcknowledge {
CGOSharedDirectoryAcknowledge {
err_code: ack.err_code,
directory_id: ack.directory_id,
impl From<CGOSharedDirectoryAnnounce> for SharedDirectoryAnnounce {
fn from(cgo: CGOSharedDirectoryAnnounce) -> SharedDirectoryAnnounce {
// # 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.
unsafe {
SharedDirectoryAnnounce {
directory_id: cgo.directory_id,
name: from_go_string(cgo.name),
}
}
}
}
/// SharedDirectoryAcknowledge is sent by the TDP server to the client
/// to acknowledge that a SharedDirectoryAnnounce was received.
#[derive(Debug)]
#[repr(C)]
pub struct SharedDirectoryAcknowledge {
pub err_code: TdpErrCode,
pub directory_id: u32,
}
pub type CGOSharedDirectoryAcknowledge = SharedDirectoryAcknowledge;
/// SharedDirectoryInfoRequest is sent from the TDP server to the client
/// to request information about a file or directory at a given path.
#[derive(Debug)]
pub struct SharedDirectoryInfoRequest {
completion_id: u32,
@ -942,23 +1385,29 @@ impl From<ServerCreateDriveRequest> for SharedDirectoryInfoRequest {
}
}
/// SharedDirectoryInfoResponse is sent by the TDP client to the server
/// in response to a `Shared Directory Info Request`.
#[derive(Debug)]
#[allow(dead_code)]
pub struct SharedDirectoryInfoResponse {
completion_id: u32,
err_code: u32,
err_code: TdpErrCode,
fso: FileSystemObject,
}
#[repr(C)]
pub struct CGOSharedDirectoryInfoResponse {
pub completion_id: u32,
pub err_code: u32,
pub err_code: TdpErrCode,
pub fso: CGOFileSystemObject,
}
impl From<CGOSharedDirectoryInfoResponse> for SharedDirectoryInfoResponse {
fn from(cgo_res: CGOSharedDirectoryInfoResponse) -> SharedDirectoryInfoResponse {
// # 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.
SharedDirectoryInfoResponse {
completion_id: cgo_res.completion_id,
err_code: cgo_res.err_code,
@ -967,25 +1416,45 @@ impl From<CGOSharedDirectoryInfoResponse> for SharedDirectoryInfoResponse {
}
}
#[derive(Debug)]
#[allow(dead_code)]
#[derive(Debug, Clone)]
/// FileSystemObject is a TDP structure containing the metadata
/// of a file or directory.
pub struct FileSystemObject {
last_modified: u64,
size: u64,
file_type: u32, // TODO(isaiah): make an enum
file_type: FileType,
path: String,
}
impl FileSystemObject {
fn name(&self) -> RdpResult<String> {
if let Some(name) = self.path.split('/').last() {
Ok(name.to_string())
} else {
Err(try_error(&format!(
"failed to extract name from path: {:?}",
self.path
)))
}
}
}
#[repr(C)]
#[derive(Clone)]
pub struct CGOFileSystemObject {
pub last_modified: u64,
pub size: u64,
pub file_type: u32, // TODO(isaiah): make an enum
pub file_type: FileType,
pub path: *const c_char,
}
impl From<CGOFileSystemObject> for FileSystemObject {
fn from(cgo_fso: CGOFileSystemObject) -> FileSystemObject {
// # 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.
unsafe {
FileSystemObject {
last_modified: cgo_fso.last_modified,
@ -997,6 +1466,194 @@ impl From<CGOFileSystemObject> for FileSystemObject {
}
}
#[repr(C)]
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum FileType {
File = 0,
Directory = 1,
}
#[repr(C)]
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum TdpErrCode {
/// nil (no error, operation succeeded)
Nil = 0,
/// operation failed
Failed = 1,
/// resource does not exist
DoesNotExist = 2,
/// resource already exists
AlreadyExists = 3,
}
/// SharedDirectoryWriteRequest is sent by the TDP server to the client
/// to write to a file.
#[derive(Debug, Clone)]
pub struct SharedDirectoryWriteRequest {
completion_id: u32,
directory_id: u32,
offset: u64,
path: String,
write_data: Vec<u8>,
}
#[derive(Debug)]
#[repr(C)]
pub struct CGOSharedDirectoryWriteRequest {
pub completion_id: u32,
pub directory_id: u32,
pub offset: u64,
pub path_length: u32,
pub path: *const c_char,
pub write_data_length: u32,
pub write_data: *mut u8,
}
/// SharedDirectoryReadRequest is sent by the TDP server to the client
/// to request the contents of a file.
#[derive(Debug)]
pub struct SharedDirectoryReadRequest {
completion_id: u32,
directory_id: u32,
path: String,
offset: u64,
length: u32,
}
#[repr(C)]
pub struct CGOSharedDirectoryReadRequest {
pub completion_id: u32,
pub directory_id: u32,
pub path_length: u32,
pub path: *const c_char,
pub offset: u64,
pub length: u32,
}
/// SharedDirectoryReadResponse is sent by the TDP client to the server
/// with the data as requested by a SharedDirectoryReadRequest.
#[derive(Debug)]
#[repr(C)]
pub struct SharedDirectoryReadResponse {
pub completion_id: u32,
pub err_code: TdpErrCode,
pub read_data: Vec<u8>,
}
impl From<CGOSharedDirectoryReadResponse> for SharedDirectoryReadResponse {
fn from(cgo_response: CGOSharedDirectoryReadResponse) -> SharedDirectoryReadResponse {
unsafe {
SharedDirectoryReadResponse {
completion_id: cgo_response.completion_id,
err_code: cgo_response.err_code,
read_data: from_go_array(cgo_response.read_data, cgo_response.read_data_length),
}
}
}
}
#[derive(Debug)]
#[repr(C)]
pub struct CGOSharedDirectoryReadResponse {
pub completion_id: u32,
pub err_code: TdpErrCode,
pub read_data_length: u32,
pub read_data: *mut u8,
}
/// SharedDirectoryWriteResponse is sent by the TDP client to the server
/// to acknowledge the completion of a SharedDirectoryWriteRequest.
#[derive(Debug)]
#[repr(C)]
pub struct SharedDirectoryWriteResponse {
pub completion_id: u32,
pub err_code: TdpErrCode,
pub bytes_written: u32,
}
pub type CGOSharedDirectoryWriteResponse = SharedDirectoryWriteResponse;
/// SharedDirectoryCreateRequest is sent by the TDP server to
/// the client to request the creation of a new file or directory.
#[derive(Debug)]
pub struct SharedDirectoryCreateRequest {
completion_id: u32,
directory_id: u32,
file_type: FileType,
path: String,
}
#[repr(C)]
pub struct CGOSharedDirectoryCreateRequest {
pub completion_id: u32,
pub directory_id: u32,
pub file_type: FileType,
pub path: *const c_char,
}
/// SharedDirectoryCreateResponse is sent by the TDP client to the server
/// to acknowledge a SharedDirectoryCreateRequest was received and executed.
#[derive(Debug, Clone)]
#[repr(C)]
pub struct SharedDirectoryCreateResponse {
pub completion_id: u32,
pub err_code: TdpErrCode,
}
/// SharedDirectoryListResponse is sent by the TDP client to the server
/// in response to a SharedDirectoryInfoRequest.
#[derive(Debug)]
pub struct SharedDirectoryListResponse {
completion_id: u32,
err_code: TdpErrCode,
fso_list: Vec<FileSystemObject>,
}
impl From<CGOSharedDirectoryListResponse> for SharedDirectoryListResponse {
fn from(cgo: CGOSharedDirectoryListResponse) -> SharedDirectoryListResponse {
// # 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.
unsafe {
let cgo_fso_list = from_go_array(cgo.fso_list, cgo.fso_list_length);
let mut fso_list = vec![];
for cgo_fso in cgo_fso_list.into_iter() {
fso_list.push(FileSystemObject::from(cgo_fso));
}
SharedDirectoryListResponse {
completion_id: cgo.completion_id,
err_code: cgo.err_code,
fso_list,
}
}
}
}
#[repr(C)]
pub struct CGOSharedDirectoryListResponse {
completion_id: u32,
err_code: TdpErrCode,
fso_list_length: u32,
fso_list: *mut CGOFileSystemObject,
}
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.
pub type SharedDirectoryDeleteRequest = SharedDirectoryInfoRequest;
pub type CGOSharedDirectoryDeleteRequest = CGOSharedDirectoryInfoRequest;
/// SharedDirectoryDeleteResponse is sent by the TDP client to the server
/// to acknowledge a SharedDirectoryDeleteRequest was received and executed.
pub type SharedDirectoryDeleteResponse = SharedDirectoryCreateResponse;
pub type CGOSharedDirectoryDeleteResponse = SharedDirectoryCreateResponse;
/// SharedDirectoryListRequest is sent by the TDP server to the client
/// to request the contents of a directory.
pub type SharedDirectoryListRequest = SharedDirectoryInfoRequest;
pub type CGOSharedDirectoryListRequest = CGOSharedDirectoryInfoRequest;
// These functions are defined on the Go side. Look for functions with '//export funcname'
// comments.
extern "C" {
@ -1009,6 +1666,26 @@ extern "C" {
client_ref: usize,
req: *mut CGOSharedDirectoryInfoRequest,
) -> CGOErrCode;
fn tdp_sd_create_request(
client_ref: usize,
req: *mut CGOSharedDirectoryCreateRequest,
) -> CGOErrCode;
fn tdp_sd_delete_request(
client_ref: usize,
req: *mut CGOSharedDirectoryDeleteRequest,
) -> CGOErrCode;
fn tdp_sd_list_request(
client_ref: usize,
req: *mut CGOSharedDirectoryListRequest,
) -> CGOErrCode;
fn tdp_sd_read_request(
client_ref: usize,
req: *mut CGOSharedDirectoryReadRequest,
) -> CGOErrCode;
fn tdp_sd_write_request(
client_ref: usize,
req: *mut CGOSharedDirectoryWriteRequest,
) -> CGOErrCode;
}
/// Payload is a generic type used to represent raw incoming RDP messages for parsing.

View file

@ -12,10 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use super::flags;
use super::Boolean;
pub const CHANNEL_NAME: &str = "rdpdr";
// Each redirected device requires a unique ID. We only share
// one permanent smartcard device, so we can give it hardcoded ID 1.
pub const DIRECTORY_SHARE_CLIENT_NAME: &str = "teleport";
// Each redirected device requires a unique ID.
pub const SCARD_DEVICE_ID: u32 = 1;
pub const VERSION_MAJOR: u16 = 0x0001;
@ -100,7 +106,7 @@ pub enum MinorFunction {
/// Windows defines an absolutely massive list of potential NTSTATUS values.
/// This enum includes the basic ones we support for communicating with the windows machine.
/// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55
#[derive(ToPrimitive, Debug)]
#[derive(ToPrimitive, Debug, PartialEq)]
#[repr(u32)]
#[allow(non_camel_case_types)]
#[allow(dead_code)]
@ -109,6 +115,11 @@ pub enum NTSTATUS {
STATUS_UNSUCCESSFUL = 0xC0000001,
STATUS_NOT_IMPLEMENTED = 0xC0000002,
STATUS_NO_MORE_FILES = 0x80000006,
STATUS_OBJECT_NAME_COLLISION = 0xC0000035,
STATUS_ACCESS_DENIED = 0xC0000022,
STATUS_NOT_A_DIRECTORY = 0xC0000103,
STATUS_NO_SUCH_FILE = 0xC000000F,
STATUS_NOT_SUPPORTED = 0xC00000BB,
}
/// 2.4 File Information Classes [MS-FSCC]
@ -116,7 +127,7 @@ pub enum NTSTATUS {
#[derive(FromPrimitive, Debug, PartialEq)]
#[repr(u32)]
#[allow(clippy::enum_variant_names)]
pub enum FsInformationClassLevel {
pub enum FileInformationClassLevel {
FileAccessInformation = 8,
FileAlignmentInformation = 17,
FileAllInformation = 18,
@ -165,3 +176,33 @@ pub enum FsInformationClassLevel {
FileTrackingInformation = 36,
FileValidDataLengthInformation = 39,
}
/// 2.5 File System Information Classes [MS-FSCC]
/// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/ee12042a-9352-46e3-9f67-c094b75fe6c3
#[derive(FromPrimitive, Debug, PartialEq)]
#[repr(u32)]
#[allow(clippy::enum_variant_names)]
pub enum FileSystemInformationClassLevel {
FileFsVolumeInformation = 1,
FileFsLabelInformation = 2,
FileFsSizeInformation = 3,
FileFsDeviceInformation = 4,
FileFsAttributeInformation = 5,
FileFsControlInformation = 6,
FileFsFullSizeInformation = 7,
FileFsObjectIdInformation = 8,
FileFsDriverPathInformation = 9,
FileFsVolumeFlagsInformation = 10,
FileFsSectorSizeInformation = 11,
}
const fn size_of<T>() -> u32 {
std::mem::size_of::<T>() as u32
}
pub const U32_SIZE: u32 = size_of::<u32>();
pub const I64_SIZE: u32 = size_of::<i64>();
pub const I8_SIZE: u32 = size_of::<i8>();
pub const U8_SIZE: u32 = size_of::<u8>();
pub const FILE_ATTR_SIZE: u32 = size_of::<flags::FileAttributes>();
pub const BOOL_SIZE: u32 = size_of::<Boolean>();

View file

@ -198,3 +198,13 @@ bitflags! {
const FILE_NOTIFY_CHANGE_STREAM_WRITE = 0x00000800;
}
}
bitflags! {
/// Only defines the subset we require from
/// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/ebc7e6e5-4650-4e54-b17c-cf60f6fbeeaa
pub struct FileSystemAttributes: u32 {
const FILE_CASE_SENSITIVE_SEARCH = 0x00000001;
const FILE_CASE_PRESERVED_NAMES = 0x00000002;
const FILE_UNICODE_ON_DISK = 0x00000004;
}
}

File diff suppressed because it is too large Load diff

View file

@ -14,6 +14,7 @@
use crate::errors::invalid_data_error;
use rdp::model::error::RdpResult;
use std::convert::TryFrom;
use utf16string::{WString, LE};
/// According to [MS-RDPEFS] 1.1 Glossary:
@ -48,6 +49,11 @@ pub fn to_utf8(s: &str) -> Vec<u8> {
format!("{}\x00", s).into_bytes()
}
/// Takes a Rust string slice and calculates it's unicode size in bytes.
pub fn unicode_size(s: &str, with_null_term: bool) -> u32 {
u32::try_from(to_unicode(s, with_null_term).len()).unwrap()
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -45,20 +45,30 @@ type MessageType byte
// For descriptions of each message type see:
// https://github.com/gravitational/teleport/blob/master/rfd/0037-desktop-access-protocol.md#message-types
const (
TypeClientScreenSpec = MessageType(1)
TypePNGFrame = MessageType(2)
TypeMouseMove = MessageType(3)
TypeMouseButton = MessageType(4)
TypeKeyboardButton = MessageType(5)
TypeClipboardData = MessageType(6)
TypeClientUsername = MessageType(7)
TypeMouseWheel = MessageType(8)
TypeError = MessageType(9)
TypeMFA = MessageType(10)
TypeSharedDirectoryAnnounce = MessageType(11)
TypeSharedDirectoryAcknowledge = MessageType(12)
TypeSharedDirectoryInfoRequest = MessageType(13)
TypeSharedDirectoryInfoResponse = MessageType(14)
TypeClientScreenSpec = MessageType(1)
TypePNGFrame = MessageType(2)
TypeMouseMove = MessageType(3)
TypeMouseButton = MessageType(4)
TypeKeyboardButton = MessageType(5)
TypeClipboardData = MessageType(6)
TypeClientUsername = MessageType(7)
TypeMouseWheel = MessageType(8)
TypeError = MessageType(9)
TypeMFA = MessageType(10)
TypeSharedDirectoryAnnounce = MessageType(11)
TypeSharedDirectoryAcknowledge = MessageType(12)
TypeSharedDirectoryInfoRequest = MessageType(13)
TypeSharedDirectoryInfoResponse = MessageType(14)
TypeSharedDirectoryCreateRequest = MessageType(15)
TypeSharedDirectoryCreateResponse = MessageType(16)
TypeSharedDirectoryDeleteRequest = MessageType(17)
TypeSharedDirectoryDeleteResponse = MessageType(18)
TypeSharedDirectoryReadRequest = MessageType(19)
TypeSharedDirectoryReadResponse = MessageType(20)
TypeSharedDirectoryWriteRequest = MessageType(21)
TypeSharedDirectoryWriteResponse = MessageType(22)
TypeSharedDirectoryListRequest = MessageType(25)
TypeSharedDirectoryListResponse = MessageType(26)
)
// Message is a Go representation of a desktop protocol message.
@ -119,6 +129,26 @@ func decode(in peekReader) (Message, error) {
return decodeSharedDirectoryInfoRequest(in)
case TypeSharedDirectoryInfoResponse:
return decodeSharedDirectoryInfoResponse(in)
case TypeSharedDirectoryCreateRequest:
return decodeSharedDirectoryCreateRequest(in)
case TypeSharedDirectoryCreateResponse:
return decodeSharedDirectoryCreateResponse(in)
case TypeSharedDirectoryDeleteRequest:
return decodeSharedDirectoryDeleteRequest(in)
case TypeSharedDirectoryDeleteResponse:
return decodeSharedDirectoryDeleteResponse(in)
case TypeSharedDirectoryListRequest:
return decodeSharedDirectoryListRequest(in)
case TypeSharedDirectoryListResponse:
return decodeSharedDirectoryListResponse(in)
case TypeSharedDirectoryReadRequest:
return decodeSharedDirectoryReadRequest(in)
case TypeSharedDirectoryReadResponse:
return decodeSharedDirectoryReadResponse(in)
case TypeSharedDirectoryWriteRequest:
return decodeSharedDirectoryWriteRequest(in)
case TypeSharedDirectoryWriteResponse:
return decodeSharedDirectoryWriteResponse(in)
default:
return nil, trace.BadParameter("unsupported desktop protocol message type %d", t)
}
@ -821,6 +851,525 @@ func decodeFileSystemObject(in peekReader) (FileSystemObject, error) {
}, nil
}
type SharedDirectoryCreateRequest struct {
CompletionID uint32
DirectoryID uint32
FileType uint32
Path string
}
func (s SharedDirectoryCreateRequest) Encode() ([]byte, error) {
buf := new(bytes.Buffer)
buf.WriteByte(byte(TypeSharedDirectoryCreateRequest))
binary.Write(buf, binary.BigEndian, s.CompletionID)
binary.Write(buf, binary.BigEndian, s.DirectoryID)
binary.Write(buf, binary.BigEndian, s.FileType)
if err := encodeString(buf, s.Path); err != nil {
return nil, trace.Wrap(err)
}
return buf.Bytes(), nil
}
func decodeSharedDirectoryCreateRequest(in peekReader) (SharedDirectoryCreateRequest, error) {
t, err := in.ReadByte()
if err != nil {
return SharedDirectoryCreateRequest{}, trace.Wrap(err)
}
if t != byte(TypeSharedDirectoryCreateRequest) {
return SharedDirectoryCreateRequest{}, trace.BadParameter("got message type %v, expected SharedDirectoryCreateRequest(%v)", t, TypeSharedDirectoryCreateRequest)
}
var completionID, directoryID, fileType uint32
err = binary.Read(in, binary.BigEndian, &completionID)
if err != nil {
return SharedDirectoryCreateRequest{}, trace.Wrap(err)
}
err = binary.Read(in, binary.BigEndian, &directoryID)
if err != nil {
return SharedDirectoryCreateRequest{}, trace.Wrap(err)
}
err = binary.Read(in, binary.BigEndian, &fileType)
if err != nil {
return SharedDirectoryCreateRequest{}, trace.Wrap(err)
}
path, err := decodeString(in, tdpMaxPathLength)
if err != nil {
return SharedDirectoryCreateRequest{}, trace.Wrap(err)
}
return SharedDirectoryCreateRequest{
CompletionID: completionID,
DirectoryID: directoryID,
FileType: fileType,
Path: path,
}, nil
}
type SharedDirectoryCreateResponse struct {
CompletionID uint32
ErrCode uint32
}
func (s SharedDirectoryCreateResponse) Encode() ([]byte, error) {
buf := new(bytes.Buffer)
buf.WriteByte(byte(TypeSharedDirectoryCreateResponse))
binary.Write(buf, binary.BigEndian, s)
return buf.Bytes(), nil
}
func decodeSharedDirectoryCreateResponse(in peekReader) (SharedDirectoryCreateResponse, error) {
t, err := in.ReadByte()
if err != nil {
return SharedDirectoryCreateResponse{}, trace.Wrap(err)
}
if t != byte(TypeSharedDirectoryCreateRequest) {
return SharedDirectoryCreateResponse{}, trace.BadParameter("got message type %v, expected SharedDirectoryCreateResponse(%v)", t, TypeSharedDirectoryCreateRequest)
}
var res SharedDirectoryCreateResponse
err = binary.Read(in, binary.BigEndian, &res)
return res, err
}
type SharedDirectoryDeleteRequest struct {
CompletionID uint32
DirectoryID uint32
Path string
}
func (s SharedDirectoryDeleteRequest) Encode() ([]byte, error) {
buf := new(bytes.Buffer)
buf.WriteByte(byte(TypeSharedDirectoryDeleteRequest))
binary.Write(buf, binary.BigEndian, s.CompletionID)
binary.Write(buf, binary.BigEndian, s.DirectoryID)
if err := encodeString(buf, s.Path); err != nil {
return nil, trace.Wrap(err)
}
return buf.Bytes(), nil
}
func decodeSharedDirectoryDeleteRequest(in peekReader) (SharedDirectoryDeleteRequest, error) {
t, err := in.ReadByte()
if err != nil {
return SharedDirectoryDeleteRequest{}, trace.Wrap(err)
}
if t != byte(TypeSharedDirectoryDeleteRequest) {
return SharedDirectoryDeleteRequest{}, trace.BadParameter("got message type %v, expected SharedDirectoryDeleteRequest(%v)", t, TypeSharedDirectoryDeleteRequest)
}
var completionID, directoryID uint32
err = binary.Read(in, binary.BigEndian, &completionID)
if err != nil {
return SharedDirectoryDeleteRequest{}, trace.Wrap(err)
}
err = binary.Read(in, binary.BigEndian, &directoryID)
if err != nil {
return SharedDirectoryDeleteRequest{}, trace.Wrap(err)
}
path, err := decodeString(in, tdpMaxPathLength)
if err != nil {
return SharedDirectoryDeleteRequest{}, trace.Wrap(err)
}
return SharedDirectoryDeleteRequest{
CompletionID: completionID,
DirectoryID: directoryID,
Path: path,
}, nil
}
type SharedDirectoryDeleteResponse struct {
CompletionID uint32
ErrCode uint32
}
func (s SharedDirectoryDeleteResponse) Encode() ([]byte, error) {
buf := new(bytes.Buffer)
buf.WriteByte(byte(TypeSharedDirectoryDeleteResponse))
binary.Write(buf, binary.BigEndian, s)
return buf.Bytes(), nil
}
func decodeSharedDirectoryDeleteResponse(in peekReader) (SharedDirectoryDeleteResponse, error) {
t, err := in.ReadByte()
if err != nil {
return SharedDirectoryDeleteResponse{}, trace.Wrap(err)
}
if t != byte(TypeSharedDirectoryDeleteRequest) {
return SharedDirectoryDeleteResponse{}, trace.BadParameter("got message type %v, expected SharedDirectoryDeleteResponse(%v)", t, TypeSharedDirectoryDeleteRequest)
}
var res SharedDirectoryDeleteResponse
err = binary.Read(in, binary.BigEndian, &res)
return res, err
}
type SharedDirectoryListRequest struct {
CompletionID uint32
DirectoryID uint32
Path string
}
func (s SharedDirectoryListRequest) Encode() ([]byte, error) {
buf := new(bytes.Buffer)
buf.WriteByte(byte(TypeSharedDirectoryListRequest))
binary.Write(buf, binary.BigEndian, s.CompletionID)
binary.Write(buf, binary.BigEndian, s.DirectoryID)
if err := encodeString(buf, s.Path); err != nil {
return nil, trace.Wrap(err)
}
return buf.Bytes(), nil
}
func decodeSharedDirectoryListRequest(in peekReader) (SharedDirectoryListRequest, error) {
t, err := in.ReadByte()
if err != nil {
return SharedDirectoryListRequest{}, trace.Wrap(err)
}
if t != byte(TypeSharedDirectoryListRequest) {
return SharedDirectoryListRequest{}, trace.BadParameter("got message type %v, expected SharedDirectoryListRequest(%v)", t, TypeSharedDirectoryListRequest)
}
var completionID, directoryID uint32
err = binary.Read(in, binary.BigEndian, &completionID)
if err != nil {
return SharedDirectoryListRequest{}, trace.Wrap(err)
}
err = binary.Read(in, binary.BigEndian, &directoryID)
if err != nil {
return SharedDirectoryListRequest{}, trace.Wrap(err)
}
path, err := decodeString(in, tdpMaxPathLength)
if err != nil {
return SharedDirectoryListRequest{}, trace.Wrap(err)
}
return SharedDirectoryListRequest{
CompletionID: completionID,
DirectoryID: directoryID,
Path: path,
}, nil
}
// | message type (26) | completion_id uint32 | err_code uint32 | fso_list_length uint32 | fso_list fso[] |
type SharedDirectoryListResponse struct {
CompletionID uint32
ErrCode uint32
FsoList []FileSystemObject
}
func (s SharedDirectoryListResponse) Encode() ([]byte, error) {
buf := new(bytes.Buffer)
buf.WriteByte(byte(TypeSharedDirectoryListResponse))
binary.Write(buf, binary.BigEndian, s.CompletionID)
binary.Write(buf, binary.BigEndian, s.ErrCode)
binary.Write(buf, binary.BigEndian, uint32(len(s.FsoList)))
for _, fso := range s.FsoList {
fsoEnc, err := fso.Encode()
if err != nil {
return nil, trace.Wrap(err)
}
binary.Write(buf, binary.BigEndian, fsoEnc)
}
return buf.Bytes(), nil
}
func decodeSharedDirectoryListResponse(in peekReader) (SharedDirectoryListResponse, error) {
t, err := in.ReadByte()
if err != nil {
return SharedDirectoryListResponse{}, trace.Wrap(err)
}
if t != byte(TypeSharedDirectoryListResponse) {
return SharedDirectoryListResponse{}, trace.BadParameter("got message type %v, expected SharedDirectoryListResponse(%v)", t, TypeSharedDirectoryListResponse)
}
var completionID, errCode, fsoListLength uint32
err = binary.Read(in, binary.BigEndian, &completionID)
if err != nil {
return SharedDirectoryListResponse{}, trace.Wrap(err)
}
err = binary.Read(in, binary.BigEndian, &errCode)
if err != nil {
return SharedDirectoryListResponse{}, trace.Wrap(err)
}
err = binary.Read(in, binary.BigEndian, &fsoListLength)
if err != nil {
return SharedDirectoryListResponse{}, trace.Wrap(err)
}
var fsoList []FileSystemObject
for i := uint32(0); i < fsoListLength; i++ {
fso, err := decodeFileSystemObject(in)
if err != nil {
return SharedDirectoryListResponse{}, trace.Wrap(err)
}
fsoList = append(fsoList, fso)
}
return SharedDirectoryListResponse{
CompletionID: completionID,
ErrCode: errCode,
FsoList: fsoList,
}, nil
}
// SharedDirectoryReadRequest is a message sent by the server to the client to request
// bytes to be read from the file at the path and starting at byte offset.
type SharedDirectoryReadRequest struct {
CompletionID uint32
DirectoryID uint32
Path string
PathLength uint32
Offset uint64
Length uint32
}
func (s SharedDirectoryReadRequest) Encode() ([]byte, error) {
buf := new(bytes.Buffer)
buf.WriteByte(byte(TypeSharedDirectoryReadRequest))
binary.Write(buf, binary.BigEndian, s.CompletionID)
binary.Write(buf, binary.BigEndian, s.DirectoryID)
if err := encodeString(buf, s.Path); err != nil {
return nil, trace.Wrap(err)
}
binary.Write(buf, binary.BigEndian, s.Offset)
binary.Write(buf, binary.BigEndian, s.Length)
return buf.Bytes(), nil
}
func decodeSharedDirectoryReadRequest(in peekReader) (SharedDirectoryReadRequest, error) {
t, err := in.ReadByte()
if err != nil {
return SharedDirectoryReadRequest{}, trace.Wrap(err)
}
if t != byte(TypeSharedDirectoryReadRequest) {
return SharedDirectoryReadRequest{}, trace.BadParameter("got message type %v, expected TypeSharedDirectoryReadRequest(%v)", t, TypeSharedDirectoryReadRequest)
}
var completionID, directoryID, pathLength, length uint32
var offset uint64
err = binary.Read(in, binary.BigEndian, &completionID)
if err != nil {
return SharedDirectoryReadRequest{}, trace.Wrap(err)
}
err = binary.Read(in, binary.BigEndian, &directoryID)
if err != nil {
return SharedDirectoryReadRequest{}, trace.Wrap(err)
}
path, err := decodeString(in, tdpMaxPathLength)
if err != nil {
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)
}
err = binary.Read(in, binary.BigEndian, &length)
if err != nil {
return SharedDirectoryReadRequest{}, trace.Wrap(err)
}
return SharedDirectoryReadRequest{
CompletionID: completionID,
DirectoryID: directoryID,
Path: path,
PathLength: pathLength,
Offset: offset,
Length: length,
}, nil
}
// SharedDirectoryReadResponse is a message sent by the client to the server
// in response to the SharedDirectoryReadRequest.
type SharedDirectoryReadResponse struct {
CompletionID uint32
ErrCode uint32
ReadDataLength uint32
ReadData []byte
}
func (s SharedDirectoryReadResponse) Encode() ([]byte, error) {
buf := new(bytes.Buffer)
buf.WriteByte(byte(TypeSharedDirectoryReadResponse))
binary.Write(buf, binary.BigEndian, s.CompletionID)
binary.Write(buf, binary.BigEndian, s.ErrCode)
binary.Write(buf, binary.BigEndian, s.ReadDataLength)
if _, err := buf.Write(s.ReadData); err != nil {
return nil, trace.Wrap(err)
}
return buf.Bytes(), nil
}
func decodeSharedDirectoryReadResponse(in peekReader) (SharedDirectoryReadResponse, error) {
t, err := in.ReadByte()
if err != nil {
return SharedDirectoryReadResponse{}, trace.Wrap(err)
}
if t != byte(TypeSharedDirectoryReadResponse) {
return SharedDirectoryReadResponse{}, trace.BadParameter("got message type %v, expected TypeSharedDirectoryReadResponse(%v)", t, TypeSharedDirectoryReadResponse)
}
var completionID, errorCode, readDataLength uint32
err = binary.Read(in, binary.BigEndian, &completionID)
if err != nil {
return SharedDirectoryReadResponse{}, trace.Wrap(err)
}
err = binary.Read(in, binary.BigEndian, &errorCode)
if err != nil {
return SharedDirectoryReadResponse{}, trace.Wrap(err)
}
err = binary.Read(in, binary.BigEndian, &readDataLength)
if err != nil {
return SharedDirectoryReadResponse{}, trace.Wrap(err)
}
readData := make([]byte, int(readDataLength))
if _, err := io.ReadFull(in, readData); err != nil {
return SharedDirectoryReadResponse{}, trace.Wrap(err)
}
return SharedDirectoryReadResponse{
CompletionID: completionID,
ErrCode: errorCode,
ReadDataLength: readDataLength,
ReadData: readData,
}, nil
}
// SharedDirectoryWriteRequest is a message sent by the server to the client to request
// bytes to be written the file at the path and starting at byte offset.
type SharedDirectoryWriteRequest struct {
CompletionID uint32
DirectoryID uint32
Offset uint64
Path string
PathLength uint32
WriteDataLength uint32
WriteData []byte
}
func (s SharedDirectoryWriteRequest) Encode() ([]byte, error) {
buf := new(bytes.Buffer)
buf.WriteByte(byte(TypeSharedDirectoryWriteRequest))
binary.Write(buf, binary.BigEndian, s.CompletionID)
binary.Write(buf, binary.BigEndian, s.DirectoryID)
binary.Write(buf, binary.BigEndian, s.Offset)
if err := encodeString(buf, s.Path); err != nil {
return nil, trace.Wrap(err)
}
binary.Write(buf, binary.BigEndian, s.WriteDataLength)
if _, err := buf.Write(s.WriteData); err != nil {
return nil, trace.Wrap(err)
}
return buf.Bytes(), nil
}
func decodeSharedDirectoryWriteRequest(in peekReader) (SharedDirectoryWriteRequest, error) {
t, err := in.ReadByte()
if err != nil {
return SharedDirectoryWriteRequest{}, trace.Wrap(err)
}
if t != byte(TypeSharedDirectoryWriteRequest) {
return SharedDirectoryWriteRequest{}, trace.BadParameter("got message type %v, expected TypeSharedDirectoryWriteRequest(%v)", t, TypeSharedDirectoryWriteRequest)
}
var completionID, directoryID, pathLength, writeDataLength uint32
var offset uint64
err = binary.Read(in, binary.BigEndian, &completionID)
if err != nil {
return SharedDirectoryWriteRequest{}, trace.Wrap(err)
}
err = binary.Read(in, binary.BigEndian, &directoryID)
if err != nil {
return SharedDirectoryWriteRequest{}, trace.Wrap(err)
}
err = binary.Read(in, binary.BigEndian, &offset)
if err != nil {
return SharedDirectoryWriteRequest{}, trace.Wrap(err)
}
path, err := decodeString(in, tdpMaxPathLength)
if err != nil {
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)
}
writeData := make([]byte, int(writeDataLength))
if _, err := io.ReadFull(in, writeData); err != nil {
return SharedDirectoryWriteRequest{}, trace.Wrap(err)
}
return SharedDirectoryWriteRequest{
CompletionID: completionID,
DirectoryID: directoryID,
Path: path,
PathLength: pathLength,
Offset: offset,
WriteDataLength: writeDataLength,
WriteData: writeData,
}, nil
}
// SharedDirectoryWriteResponse is a message sent by the client to the server
// in response to the SharedDirectoryWriteRequest.
type SharedDirectoryWriteResponse struct {
CompletionID uint32
ErrCode uint32
BytesWritten uint32
}
func (s SharedDirectoryWriteResponse) Encode() ([]byte, error) {
buf := new(bytes.Buffer)
buf.WriteByte(byte(TypeSharedDirectoryWriteResponse))
binary.Write(buf, binary.BigEndian, s)
return buf.Bytes(), nil
}
func decodeSharedDirectoryWriteResponse(in peekReader) (SharedDirectoryWriteResponse, error) {
t, err := in.ReadByte()
if err != nil {
return SharedDirectoryWriteResponse{}, trace.Wrap(err)
}
if t != byte(TypeSharedDirectoryWriteResponse) {
return SharedDirectoryWriteResponse{}, trace.BadParameter("got message type %v, expected SharedDirectoryWriteResponse(%v)", t, TypeSharedDirectoryWriteResponse)
}
var res SharedDirectoryWriteResponse
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