///|
/// Endpoint Mapper lookup opnum used by JIYUAN `DceClient.LookupEndPointMapper`.
pub let endpoint_mapper_opnum_lookup : Int = 2
///|
pub(all) struct EndpointMapperObject {
uuid : Uuid
tower_referent_id : Int
annotation_hex : String
udp_port : Int?
ip_text : String?
rpc_version_minor : Int?
uuid_floor_uuids : Array[Uuid]
uuid_floors : Array[String]
floor_count : Int
} derive(Eq, Debug)
///|
pub(all) struct EndpointMapperResponse {
handle : Bytes
object_count : Int
objects : Array[EndpointMapperObject]
return_code : Int
} derive(Eq, Debug)
///|
fn copy_bytes(
data : Bytes,
offset : Int,
length : Int,
) -> Bytes raise @frame.FrameError {
guard data.length() >= offset + length else {
raise @frame.FrameError::InvalidMacLength(data.length())
}
let output : Array[Byte] = []
for i in offset..<(offset + length) {
output.push(data[i])
}
Bytes::from_array(output)
}
///|
fn align4(value : Int) -> Int {
let rem = value % 4
if rem == 0 {
value
} else {
value + 4 - rem
}
}
///|
fn read_u16_be_local(data : Bytes, offset : Int) -> Int {
(data[offset].to_int() << 8) + data[offset + 1].to_int()
}
///|
/// Encode the EPM lookup request body (`aa.a()` in the C# reference).
pub fn encode_endpoint_mapper_lookup_body(
handle? : Bytes = Bytes::make(20, b'\x00'),
) -> Bytes raise @frame.FrameError {
guard handle.length() == 20 else {
raise @frame.FrameError::InvalidMacLength(handle.length())
}
let output : Array[Byte] = []
push_u32_le(output, 0) // inquiry type: x.a
push_u32_le(output, 1)
append_bytes(output, Uuid::empty().to_dce_bytes_le())
push_u32_le(output, 2)
append_bytes(output, Uuid::from_hex(uuid_pnio_device).to_dce_bytes_le())
push_u16_le(output, 1)
push_u16_le(output, 0)
push_u32_le(output, 1)
append_bytes(output, handle)
push_u32_le(output, 1)
Bytes::from_array(output)
}
///|
/// Assemble a DCE/RPC Endpoint Mapper lookup request packet.
pub fn encode_endpoint_mapper_lookup_request(
activity_uuid : Uuid,
sequence_number? : Int = 0,
handle? : Bytes = Bytes::make(20, b'\x00'),
) -> Bytes raise @frame.FrameError {
let body = encode_endpoint_mapper_lookup_body(handle~)
let hdr = DceHeader::{
..DceHeader::default(),
packet_type: packet_type_request,
interface_uuid: Uuid::from_hex(uuid_endpoint_mapper),
activity_uuid,
opnum: endpoint_mapper_opnum_lookup,
sequence_number,
body_length: body.length(),
}
let output : Array[Byte] = []
append_bytes(output, encode_dce_header(hdr))
append_bytes(output, body)
Bytes::from_array(output)
}
///|
fn parse_endpoint_mapper_floor(
data : Bytes,
cursor : Int,
udp_port : Int?,
ip_text : String?,
) -> (Int, Int?, String?, Int?, Uuid?, String?) raise @frame.FrameError {
guard data.length() >= cursor + 4 else {
raise @frame.FrameError::InvalidMacLength(data.length())
}
let lhs_len = read_u16_le(data, cursor)
guard data.length() >= cursor + lhs_len + 4 else {
raise @frame.FrameError::InvalidMacLength(data.length())
}
let floor_type = data[cursor + 2].to_int()
let rhs_len_offset = cursor + lhs_len + 2
guard data.length() >= rhs_len_offset + 2 else {
raise @frame.FrameError::InvalidMacLength(data.length())
}
let rhs_len = read_u16_le(data, rhs_len_offset)
let floor_end = cursor + lhs_len + rhs_len + 4
guard data.length() >= floor_end else {
raise @frame.FrameError::InvalidMacLength(data.length())
}
let floor_data_start = cursor + 3
let floor_data_len = lhs_len + rhs_len + 1
let mut next_udp_port = udp_port
let mut next_ip_text = ip_text
let mut rpc_version_minor : Int? = None
let mut uuid_floor_uuid : Uuid? = None
let mut uuid_floor : String? = None
if floor_type == 8 &&
floor_data_len >= 4 &&
data.length() >= floor_data_start + 4 {
next_udp_port = Some(
(data[floor_data_start + 2].to_int() << 8) +
data[floor_data_start + 3].to_int(),
)
}
if floor_type == 9 &&
floor_data_len >= 6 &&
data.length() >= floor_data_start + 6 {
let ip = @frame.IPv4Address::new(
Bytes::from_array([
data[floor_data_start + 2],
data[floor_data_start + 3],
data[floor_data_start + 4],
data[floor_data_start + 5],
]),
)
next_ip_text = Some(ip.to_text())
}
if floor_type == 10 &&
floor_data_len >= 4 &&
data.length() >= floor_data_start + 4 {
rpc_version_minor = Some(read_u16_be_local(data, floor_data_start + 2))
}
if floor_type == 13 &&
floor_data_len >= 22 &&
data.length() >= floor_data_start + 22 {
let uuid = Uuid::from_dce_bytes_le(data, floor_data_start)
uuid_floor_uuid = Some(uuid)
uuid_floor = Some(
uuid.to_hex() +
"@version:" +
data[floor_data_start + 16].to_int().to_string() +
"." +
data[floor_data_start + 17].to_int().to_string() +
",minor:" +
read_u16_be_local(data, floor_data_start + 20).to_string(),
)
}
(
floor_end, next_udp_port, next_ip_text, rpc_version_minor, uuid_floor_uuid, uuid_floor,
)
}
///|
/// Parse the DCE body of an Endpoint Mapper lookup response.
pub fn parse_endpoint_mapper_response_body(
data : Bytes,
) -> EndpointMapperResponse raise @frame.FrameError {
guard data.length() >= 40 else {
raise @frame.FrameError::InvalidMacLength(data.length())
}
let handle = copy_bytes(data, 0, 20)
let mut cursor = 24
guard data.length() >= cursor + 12 else {
raise @frame.FrameError::InvalidMacLength(data.length())
}
let object_count = read_u32_le(data, cursor + 8)
cursor = cursor + 12
let objects : Array[EndpointMapperObject] = []
let mut remaining = object_count
while remaining > 0 {
guard data.length() >= cursor + 28 else {
raise @frame.FrameError::InvalidMacLength(data.length())
}
let uuid = Uuid::from_dce_bytes_le(data, cursor)
let tower_referent_id = read_u32_le(data, cursor + 16)
let annotation_len = read_u32_le(data, cursor + 24)
let annotation_start = cursor + 28
let annotation = copy_bytes(data, annotation_start, annotation_len)
cursor = align4(annotation_start + annotation_len)
guard data.length() >= cursor + 10 else {
raise @frame.FrameError::InvalidMacLength(data.length())
}
let mut floors = read_u16_le(data, cursor + 8)
cursor = cursor + 10
let mut floor_count = 0
let mut udp_port : Int? = None
let mut ip_text : String? = None
let mut rpc_version_minor : Int? = None
let uuid_floor_uuids : Array[Uuid] = []
let uuid_floors : Array[String] = []
while floors > 0 {
let (
next_cursor,
next_udp_port,
next_ip_text,
next_rpc_version_minor,
uuid_floor_uuid,
uuid_floor,
) = parse_endpoint_mapper_floor(data, cursor, udp_port, ip_text)
cursor = next_cursor
udp_port = next_udp_port
ip_text = next_ip_text
match next_rpc_version_minor {
Some(value) => rpc_version_minor = Some(value)
None => ()
}
match uuid_floor_uuid {
Some(value) => uuid_floor_uuids.push(value)
None => ()
}
match uuid_floor {
Some(value) => uuid_floors.push(value)
None => ()
}
floor_count = floor_count + 1
floors = floors - 1
}
objects.push(EndpointMapperObject::{
uuid,
tower_referent_id,
annotation_hex: @frame.bytes_to_hex(annotation, separator=""),
udp_port,
ip_text,
rpc_version_minor,
uuid_floor_uuids,
uuid_floors,
floor_count,
})
remaining = remaining - 1
}
guard data.length() >= cursor + 4 else {
raise @frame.FrameError::InvalidMacLength(data.length())
}
EndpointMapperResponse::{
handle,
object_count,
objects,
return_code: read_u32_le(data, cursor),
}
}
///|
pub fn first_endpoint_mapper_object_uuid(
response : EndpointMapperResponse,
) -> Uuid? {
if response.objects.length() > 0 && response.objects[0].uuid != Uuid::empty() {
Some(response.objects[0].uuid)
} else {
None
}
}
///|
pub fn endpoint_mapper_handle_is_zero(handle : Bytes) -> Bool {
for byte in handle {
if byte != b'\x00' {
return false
}
}
true
}
///|
pub fn endpoint_mapper_response_matches_interface(
response : EndpointMapperResponse,
interface_uuid : Uuid,
) -> Bool {
for object in response.objects {
for uuid in object.uuid_floor_uuids {
if uuid == interface_uuid {
return true
}
}
}
false
}
///|
pub fn endpoint_mapper_object_uuid_for_interface(
response : EndpointMapperResponse,
interface_uuid : Uuid,
) -> Uuid? {
for object in response.objects {
for uuid in object.uuid_floor_uuids {
if uuid == interface_uuid {
return Some(object.uuid)
}
}
}
None
}
///|
pub fn endpoint_mapper_udp_port_for_interface(
response : EndpointMapperResponse,
interface_uuid : Uuid,
) -> Int? {
for object in response.objects {
for uuid in object.uuid_floor_uuids {
if uuid == interface_uuid {
return object.udp_port
}
}
}
None
}
///|
pub fn format_endpoint_mapper_response(
response : EndpointMapperResponse,
) -> String {
let lines : Array[String] = []
lines.push("epm_return_code=0x" + @frame.uint32_to_hex(response.return_code))
lines.push(
"epm_handle_hex=" + @frame.bytes_to_hex(response.handle, separator=""),
)
lines.push("epm_object_count=" + response.object_count.to_string())
for index, object in response.objects {
lines.push(
"epm_object_" +
(index + 1).to_string() +
"=uuid:" +
object.uuid.to_hex() +
",annotation_hex:" +
object.annotation_hex +
",floors:" +
object.floor_count.to_string(),
)
match object.udp_port {
Some(port) =>
lines.push(
"epm_object_" +
(index + 1).to_string() +
"_udp_port=" +
port.to_string(),
)
None => ()
}
match object.ip_text {
Some(ip) =>
lines.push("epm_object_" + (index + 1).to_string() + "_ip=" + ip)
None => ()
}
match object.rpc_version_minor {
Some(value) =>
lines.push(
"epm_object_" +
(index + 1).to_string() +
"_rpc_version_minor=" +
value.to_string(),
)
None => ()
}
for floor_index, floor in object.uuid_floors {
lines.push(
"epm_object_" +
(index + 1).to_string() +
"_uuid_floor_" +
(floor_index + 1).to_string() +
"=" +
floor,
)
}
}
lines.join("\n")
}