Skip to content

Commit

Permalink
[miio] validate response id matching command id (openhab#9966)
Browse files Browse the repository at this point in the history
* [miio] validate response id matching command id

This PR prevents out of sync issues in case devices are too slow to
respond or the timeout is set too short.

Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com>

* [miio] spotless adding space

Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com>

* [miio] update based on review

Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com>
  • Loading branch information
marcelrv authored and thinkingstone committed Nov 7, 2021
1 parent 490ecf6 commit 2d265ea
Showing 1 changed file with 41 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
Expand Down Expand Up @@ -187,15 +189,33 @@ MiIoSendCommand sendMiIoSendCommand(MiIoSendCommand miIoSendCommand) {
decryptedResponse = decryptedResponse.replace(",,", ",");
JsonElement response;
response = parser.parse(decryptedResponse);
if (response.isJsonObject()) {
if (!response.isJsonObject()) {
errorMsg = "Received message is not a JSON object ";
} else {
needPing = false;
logger.trace("Received JSON message {}", response.toString());
miIoSendCommand.setResponse(response.getAsJsonObject());
return miIoSendCommand;
} else {
errorMsg = "Received message is invalid JSON";
logger.debug("{}: {}", errorMsg, decryptedResponse);
JsonObject resJson = response.getAsJsonObject();
if (resJson.has("id")) {
int id = resJson.get("id").getAsInt();
if (id == miIoSendCommand.getId()) {
miIoSendCommand.setResponse(response.getAsJsonObject());
return miIoSendCommand;
} else {
if (id < miIoSendCommand.getId()) {
errorMsg = String.format(
"Received message out of sync, extend timeout time. Expected id: %d, received id: %d",
miIoSendCommand.getId(), id);
} else {
errorMsg = String.format("Received message out of sync. Expected id: %d, received id: %d",
miIoSendCommand.getId(), id);
}
}
} else {
errorMsg = "Received message is without id";
}

}
logger.debug("{}: {}", errorMsg, decryptedResponse);
} catch (MiIoCryptoException | IOException e) {
logger.debug("Send command '{}' -> {} (Device: {}) gave error {}", miIoSendCommand.getCommandString(), ip,
Utils.getHex(deviceId), e.getMessage());
Expand Down Expand Up @@ -272,10 +292,13 @@ public void run() {

private String sendCommand(String command, byte[] token, String ip, byte[] deviceId)
throws MiIoCryptoException, IOException {
byte[] encr;
encr = MiIoCrypto.encrypt(command.getBytes(), token);
timeStamp = (int) TimeUnit.MILLISECONDS.toSeconds(Calendar.getInstance().getTime().getTime());
byte[] sendMsg = Message.createMsgData(encr, token, deviceId, timeStamp + timeDelta);
byte[] sendMsg = new byte[0];
if (!command.isBlank()) {
byte[] encr;
encr = MiIoCrypto.encrypt(command.getBytes(StandardCharsets.UTF_8), token);
timeStamp = (int) Instant.now().getEpochSecond();
sendMsg = Message.createMsgData(encr, token, deviceId, timeStamp + timeDelta);
}
Message miIoResponseMsg = sendData(sendMsg, ip);
if (miIoResponseMsg == null) {
if (logger.isTraceEnabled()) {
Expand Down Expand Up @@ -374,12 +397,14 @@ private synchronized byte[] comms(byte[] message, String ip) throws IOException
DatagramPacket receivePacket = new DatagramPacket(new byte[MSG_BUFFER_SIZE], MSG_BUFFER_SIZE);
try {
logger.trace("Connection {}:{}", ip, clientSocket.getLocalPort());
byte[] sendData = new byte[MSG_BUFFER_SIZE];
sendData = message;
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, ipAddress,
MiIoBindingConstants.PORT);
clientSocket.send(sendPacket);
sendPacket.setData(new byte[MSG_BUFFER_SIZE]);
if (message.length > 0) {
byte[] sendData = new byte[MSG_BUFFER_SIZE];
sendData = message;
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, ipAddress,
MiIoBindingConstants.PORT);
clientSocket.send(sendPacket);
sendPacket.setData(new byte[MSG_BUFFER_SIZE]);
}
clientSocket.receive(receivePacket);
byte[] response = Arrays.copyOfRange(receivePacket.getData(), receivePacket.getOffset(),
receivePacket.getOffset() + receivePacket.getLength());
Expand Down

0 comments on commit 2d265ea

Please sign in to comment.