Skip to content

Commit 16b2890

Browse files
committed
encoding: use utf-8 to encode/decode and ignore malformed data
When encode or decode using utf-8 encoding avoid any 'UnicodeError' just ignore the malformed data. Malformed data may be received for example when working in DigiMesh encrypted. If 2 nodes used different encryption keys (KY) data is received corrupted since the receiver is unencrypting with the wrong key. This data may not be utf-8 in fields that are expected to be, for example the AT command of a Remote AT Command frame. https://jira.digi.com/browse/XBHAWKDM-901 Signed-off-by: Tatiana Leon <Tatiana.Leon@digi.com>
1 parent 223d2e8 commit 16b2890

File tree

16 files changed

+452
-303
lines changed

16 files changed

+452
-303
lines changed

digi/xbee/devices.py

Lines changed: 47 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,8 @@ def _read_device_info(self, reason, init=True, fire_event=True):
633633
updated = True
634634
# Node ID:
635635
if init or not self._node_id:
636-
node_id = self.get_parameter(ATStringCommand.NI, apply=False).decode()
636+
node_id = str(self.get_parameter(ATStringCommand.NI, apply=False),
637+
encoding='utf8', errors='ignore')
637638
if self._node_id != node_id:
638639
self._node_id = node_id
639640
updated = True
@@ -2597,15 +2598,15 @@ def _send_data_64_16(self, x64addr, x16addr, data,
25972598
raise ValueError("64-bit address cannot be None")
25982599
if x16addr is None:
25992600
raise ValueError("16-bit address cannot be None")
2600-
if data is None:
2601-
raise ValueError("Data cannot be None")
2601+
if not isinstance(data, (str, bytearray, bytes)):
2602+
raise ValueError("Data must be a string or bytearray")
26022603

26032604
if self.is_remote():
26042605
raise OperationNotSupportedException(
26052606
message="Cannot send data to a remote device from a remote device")
26062607

26072608
if isinstance(data, str):
2608-
data = data.encode("utf8")
2609+
data = data.encode(encoding="utf8", errors="ignore")
26092610

26102611
packet = TransmitPacket(self.get_next_frame_id(), x64addr, x16addr,
26112612
0, transmit_options, rf_data=data)
@@ -2647,15 +2648,15 @@ def _send_data_64(self, x64addr, data, transmit_options=TransmitOptions.NONE.val
26472648
"""
26482649
if x64addr is None:
26492650
raise ValueError("64-bit address cannot be None")
2650-
if data is None:
2651-
raise ValueError("Data cannot be None")
2651+
if not isinstance(data, (str, bytearray, bytes)):
2652+
raise ValueError("Data must be a string or bytearray")
26522653

26532654
if self.is_remote():
26542655
raise OperationNotSupportedException(
26552656
message="Cannot send data to a remote device from a remote device")
26562657

26572658
if isinstance(data, str):
2658-
data = data.encode("utf8")
2659+
data = data.encode(encoding="utf8", errors="ignore")
26592660

26602661
if self.get_protocol() == XBeeProtocol.RAW_802_15_4:
26612662
packet = TX64Packet(self.get_next_frame_id(), x64addr,
@@ -2702,15 +2703,15 @@ def _send_data_16(self, x16addr, data, transmit_options=TransmitOptions.NONE.val
27022703
"""
27032704
if x16addr is None:
27042705
raise ValueError("16-bit address cannot be None")
2705-
if data is None:
2706-
raise ValueError("Data cannot be None")
2706+
if not isinstance(data, (str, bytearray, bytes)):
2707+
raise ValueError("Data must be a string or bytearray")
27072708

27082709
if self.is_remote():
27092710
raise OperationNotSupportedException(
27102711
message="Cannot send data to a remote device from a remote device")
27112712

27122713
if isinstance(data, str):
2713-
data = data.encode("utf8")
2714+
data = data.encode(encoding="utf8", errors="ignore")
27142715

27152716
packet = TX16Packet(self.get_next_frame_id(), x16addr,
27162717
transmit_options, rf_data=data)
@@ -2806,15 +2807,15 @@ def _send_data_async_64_16(self, x64addr, x16addr, data,
28062807
raise ValueError("64-bit address cannot be None")
28072808
if x16addr is None:
28082809
raise ValueError("16-bit address cannot be None")
2809-
if data is None:
2810-
raise ValueError("Data cannot be None")
2810+
if not isinstance(data, (str, bytearray, bytes)):
2811+
raise ValueError("Data must be a string or bytearray")
28112812

28122813
if self.is_remote():
28132814
raise OperationNotSupportedException(
28142815
message="Cannot send data to a remote device from a remote device")
28152816

28162817
if isinstance(data, str):
2817-
data = data.encode("utf8")
2818+
data = data.encode(encoding="utf8", errors="ignore")
28182819

28192820
packet = TransmitPacket(self.get_next_frame_id(), x64addr, x16addr, 0,
28202821
transmit_options, rf_data=data)
@@ -2848,15 +2849,15 @@ def _send_data_async_64(self, x64addr, data, transmit_options=TransmitOptions.NO
28482849
"""
28492850
if x64addr is None:
28502851
raise ValueError("64-bit address cannot be None")
2851-
if data is None:
2852-
raise ValueError("Data cannot be None")
2852+
if not isinstance(data, (str, bytearray, bytes)):
2853+
raise ValueError("Data must be a string or bytearray")
28532854

28542855
if self.is_remote():
28552856
raise OperationNotSupportedException(
28562857
message="Cannot send data to a remote device from a remote device")
28572858

28582859
if isinstance(data, str):
2859-
data = data.encode("utf8")
2860+
data = data.encode(encoding="utf8", errors="ignore")
28602861

28612862
if self.get_protocol() == XBeeProtocol.RAW_802_15_4:
28622863
packet = TX64Packet(self.get_next_frame_id(), x64addr,
@@ -2895,15 +2896,15 @@ def _send_data_async_16(self, x16addr, data, transmit_options=TransmitOptions.NO
28952896
"""
28962897
if x16addr is None:
28972898
raise ValueError("16-bit address cannot be None")
2898-
if data is None:
2899-
raise ValueError("Data cannot be None")
2899+
if not isinstance(data, (str, bytearray, bytes)):
2900+
raise ValueError("Data must be a string or bytearray")
29002901

29012902
if self.is_remote():
29022903
raise OperationNotSupportedException(
29032904
message="Cannot send data to a remote device from a remote device")
29042905

29052906
if isinstance(data, str):
2906-
data = data.encode("utf8")
2907+
data = data.encode(encoding="utf8", errors="ignore")
29072908

29082909
packet = TX16Packet(self.get_next_frame_id(),
29092910
x16addr,
@@ -3900,7 +3901,7 @@ def _exit_at_command_mode(self):
39003901
raise InvalidOperatingModeException(
39013902
message="Invalid mode. Command mode can be only be exited while in AT mode")
39023903

3903-
self._serial_port.write("ATCN\r".encode("utf-8"))
3904+
self._serial_port.write("ATCN\r".encode("utf8"))
39043905
time.sleep(self.__DEFAULT_GUARD_TIME)
39053906

39063907
def _determine_operating_mode(self):
@@ -4089,7 +4090,7 @@ def __build_expldata_packet(self, remote_xbee, data, src_endpoint, dest_endpoint
40894090
x16addr = XBee16BitAddress.UNKNOWN_ADDRESS
40904091

40914092
if isinstance(data, str):
4092-
data = data.encode("utf8")
4093+
data = data.encode(encoding="utf8", errors='ignore')
40934094

40944095
return ExplicitAddressingPacket(self._get_next_frame_id(), x64addr,
40954096
x16addr, src_endpoint, dest_endpoint,
@@ -4115,14 +4116,18 @@ def __get_actual_mode(self):
41154116
# Clear the serial input stream.
41164117
self._serial_port.flushInput()
41174118
# Send the 'AP' command.
4118-
self._serial_port.write("ATAP\r".encode("utf-8"))
4119+
self._serial_port.write("ATAP\r".encode(encoding="utf8"))
41194120
time.sleep(0.1)
41204121
# Read the 'AP' answer.
4121-
ap_answer = self._serial_port.read_existing().decode("utf-8").rstrip()
4122+
ap_answer = self._serial_port.read_existing() \
4123+
.decode(encoding="utf8", errors='ignore').rstrip()
41224124
if len(ap_answer) == 0:
41234125
return OperatingMode.UNKNOWN
41244126
# Return the corresponding operating mode for the AP answer.
4125-
return OperatingMode.get(int(ap_answer, 16))
4127+
try:
4128+
return OperatingMode.get(int(ap_answer, 16))
4129+
except ValueError:
4130+
return OperatingMode.UNKNOWN
41264131

41274132
def get_next_frame_id(self):
41284133
"""
@@ -6297,8 +6302,8 @@ def get_dest_ip_addr(self):
62976302
.. seealso::
62986303
| :class:`ipaddress.IPv4Address`
62996304
"""
6300-
resp = self.get_parameter(ATStringCommand.DL, apply=False)
6301-
return IPv4Address(resp.decode("utf8"))
6305+
return IPv4Address(
6306+
str(self.get_parameter(ATStringCommand.DL, apply=False), encoding="utf8"))
63026307

63036308
def add_ip_data_received_callback(self, callback):
63046309
"""
@@ -6383,8 +6388,8 @@ def send_ip_data(self, ip_addr, dest_port, protocol, data, close_socket=False):
63836388
raise ValueError("IP address cannot be None")
63846389
if protocol is None:
63856390
raise ValueError("Protocol cannot be None")
6386-
if data is None:
6387-
raise ValueError("Data cannot be None")
6391+
if not isinstance(data, (str, bytearray, bytes)):
6392+
raise ValueError("Data must be a string or bytearray")
63886393

63896394
if not 0 <= dest_port <= 65535:
63906395
raise ValueError("Destination port must be between 0 and 65535")
@@ -6400,7 +6405,7 @@ def send_ip_data(self, ip_addr, dest_port, protocol, data, close_socket=False):
64006405
src_port = 0
64016406

64026407
if isinstance(data, str):
6403-
data = data.encode("utf8")
6408+
data = data.encode(encoding="utf8", errors="ignore")
64046409

64056410
opts = TXIPv4Packet.OPTIONS_CLOSE_SOCKET if close_socket else TXIPv4Packet.OPTIONS_LEAVE_SOCKET_OPEN
64066411

@@ -6438,8 +6443,8 @@ def send_ip_data_async(self, ip_addr, dest_port, protocol, data, close_socket=Fa
64386443
raise ValueError("IP address cannot be None")
64396444
if protocol is None:
64406445
raise ValueError("Protocol cannot be None")
6441-
if data is None:
6442-
raise ValueError("Data cannot be None")
6446+
if not isinstance(data, (str, bytearray, bytes)):
6447+
raise ValueError("Data must be a string or bytearray")
64436448

64446449
if not 0 <= dest_port <= 65535:
64456450
raise ValueError("Destination port must be between 0 and 65535")
@@ -6456,7 +6461,7 @@ def send_ip_data_async(self, ip_addr, dest_port, protocol, data, close_socket=Fa
64566461
src_port = 0
64576462

64586463
if isinstance(data, str):
6459-
data = data.encode("utf8")
6464+
data = data.encode(encoding="utf8", errors="ignore")
64606465

64616466
opts = TXIPv4Packet.OPTIONS_CLOSE_SOCKET if close_socket else TXIPv4Packet.OPTIONS_LEAVE_SOCKET_OPEN
64626467

@@ -7669,9 +7674,9 @@ def __parse_access_point(self, ap_data):
76697674
return None
76707675

76717676
signal_quality = self.__get_signal_quality(version, signal_strength)
7672-
ssid = (ap_data[index:]).decode("utf8")
76737677

7674-
return AccessPoint(ssid, WiFiEncryptionType.get(encryption_type),
7678+
return AccessPoint(str(ap_data[index:], encoding="utf8"),
7679+
WiFiEncryptionType.get(encryption_type),
76757680
channel=channel, signal_quality=signal_quality)
76767681

76777682
@staticmethod
@@ -8905,7 +8910,7 @@ def export(self, dir_path=None, name=None, desc=None):
89058910
date_time=time.localtime(date_now.timestamp()))
89068911
info.compress_type = ZIP_DEFLATED
89078912
with xnet_zip.open(info, 'w') as xnet_file:
8908-
tree.write(xnet_file, encoding='utf-8', xml_declaration=False)
8913+
tree.write(xnet_file, encoding='utf8', xml_declaration=False)
89098914
except (OSError, IOError) as exc:
89108915
return 1, "%s (%d): %s" % (exc.strerror, exc.errno, exc.filename)
89118916

@@ -10283,10 +10288,12 @@ def __discover_devices(self, node_id=None):
1028310288
try:
1028410289
timeout = self._calculate_timeout(default_timeout=XBeeNetwork._DEFAULT_DISCOVERY_TIMEOUT)
1028510290
# send "ND" async
10286-
self._local_xbee.send_packet(ATCommPacket(self._local_xbee.get_next_frame_id(),
10287-
ATStringCommand.ND.command,
10288-
parameter=None if node_id is None else bytearray(node_id, 'utf8')),
10289-
sync=False)
10291+
self._local_xbee.send_packet(
10292+
ATCommPacket(self._local_xbee.get_next_frame_id(),
10293+
ATStringCommand.ND.command,
10294+
parameter=None if node_id is None
10295+
else bytearray(node_id, encoding='utf8', errors='ignore')),
10296+
sync=False)
1029010297

1029110298
self.__nd_processes.update({str(self._local_xbee.get_64bit_addr()): self})
1029210299

@@ -10612,7 +10619,7 @@ def __get_data_for_remote(self, data):
1061210619
# role is the next byte
1061310620
role = Role.get(utils.bytes_to_int(data[i:i+1]))
1061410621
return XBee16BitAddress(data[0:2]), XBee64BitAddress(data[2:10]), \
10615-
node_id.decode(), role, parent_addr
10622+
node_id.decode('utf8', errors='ignore'), role, parent_addr
1061610623

1061710624
def _set_node_reachable(self, node, reachable):
1061810625
"""

0 commit comments

Comments
 (0)