Module: E3DCMqtt::RSCP::Frame
- Defined in:
- lib/e3dc_mqtt/rscp/frame.rb
Overview
Frames a list of messages into an (unencrypted) RSCP frame, and parses frames back into messages. Encryption/decryption is handled one layer up.
Class Method Summary collapse
-
.decode(bytes) ⇒ Object
Parse an already-decrypted frame.
- .encode(messages, use_checksum: true, time: Time.now.utc) ⇒ Object
- .encode_time(time) ⇒ Object
- .pad_for_crypto(bytes) ⇒ Object
-
.trim_padding(bytes, frame_size) ⇒ Object
Drop trailing zero padding that may follow the frame (introduced by the crypto layer so that total size is a multiple of the block size).
Class Method Details
.decode(bytes) ⇒ Object
Parse an already-decrypted frame. Returns Array<Message>.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/e3dc_mqtt/rscp/frame.rb', line 30 def decode(bytes) raise InvalidFrameLength, "frame shorter than header" if bytes.bytesize < FRAME_HEADER_SIZE magic = bytes.byteslice(0, 2).unpack1("S<") raise InvalidMagic, "expected 0x%04x, got 0x%04x" % [MAGIC, magic] if magic != MAGIC ctrl = bytes.byteslice(2, 2).unpack1("S<") raise InvalidControl, "reserved bits set in control field 0x%04x" % ctrl if (ctrl | CTRL_ALLOWED_MASK) != CTRL_ALLOWED_MASK version = (ctrl & CTRL_VERSION_MASK) >> CTRL_VERSION_SHIFT raise ProtocolVersionMismatch, "expected version %d, got %d" % [VERSION_1_0, version] if version != VERSION_1_0 crc_flag = (ctrl & CTRL_CRC_MASK) != 0 data_size = bytes.byteslice(FRAME_HEADER_SIZE - FRAME_LENGTH_SIZE, FRAME_LENGTH_SIZE).unpack1("S<") frame_size = FRAME_HEADER_SIZE + data_size + (crc_flag ? FRAME_CRC_SIZE : 0) trimmed = trim_padding(bytes, frame_size) raise InvalidFrameLength, "unexpected data after frame" if trimmed.bytesize > frame_size if crc_flag expected = Zlib.crc32(trimmed.byteslice(0, frame_size - FRAME_CRC_SIZE)) actual = trimmed.byteslice(frame_size - FRAME_CRC_SIZE, FRAME_CRC_SIZE).unpack1("L<") raise InvalidCrc, "expected 0x%08x, got 0x%08x" % [expected, actual] if expected != actual end MessageCodec.decode_all(trimmed, FRAME_HEADER_SIZE, data_size) end |
.encode(messages, use_checksum: true, time: Time.now.utc) ⇒ Object
14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/e3dc_mqtt/rscp/frame.rb', line 14 def encode(, use_checksum: true, time: Time.now.utc) body = MessageCodec.encode_all() if body.bytesize > FRAME_MAX_DATA_SIZE raise DataLimitExceeded, "data size %d exceeds max %d" % [body.bytesize, FRAME_MAX_DATA_SIZE] end ctrl = (VERSION_1_0 << CTRL_VERSION_SHIFT) & CTRL_VERSION_MASK ctrl |= (1 << CTRL_CRC_SHIFT) & CTRL_CRC_MASK if use_checksum header = [MAGIC].pack("S<") + [ctrl].pack("S<") + encode_time(time) + [body.bytesize].pack("S<") frame = header + body frame += [Zlib.crc32(frame)].pack("L<") if use_checksum frame end |
.encode_time(time) ⇒ Object
58 59 60 61 |
# File 'lib/e3dc_mqtt/rscp/frame.rb', line 58 def encode_time(time) t = time.utc [t.to_i].pack("q<") + [t.nsec].pack("l<") end |
.pad_for_crypto(bytes) ⇒ Object
71 72 73 74 75 76 |
# File 'lib/e3dc_mqtt/rscp/frame.rb', line 71 def pad_for_crypto(bytes) rem = bytes.bytesize % CRYPT_BLOCK_SIZE return bytes if rem == 0 bytes + "\x00".b * (CRYPT_BLOCK_SIZE - rem) end |
.trim_padding(bytes, frame_size) ⇒ Object
Drop trailing zero padding that may follow the frame (introduced by the crypto layer so that total size is a multiple of the block size).
65 66 67 68 69 |
# File 'lib/e3dc_mqtt/rscp/frame.rb', line 65 def trim_padding(bytes, frame_size) i = bytes.bytesize i -= 1 while i > frame_size && bytes.getbyte(i - 1) == CRYPT_BLOCK_PAD bytes.byteslice(0, i) end |