fix(mqtt): label SDK errors from named constants; report broker refusals
Four of the hand-written error labels named the wrong failure against the
installed SDK: Wi-Fi reason 201 is NO_AP_FOUND (reported as "security
mismatch"), 202 is AUTH_FAIL (reported as "auth mode rejected"), 39 is TIMEOUT
(reported as "SSID not found") and 34 is MISSING_ACKS (reported as an
"AP state mismatch"). esp-tls 0x8008 is TCP_CLOSED_FIN, not a timeout, and
0x8010 is CERT_PARTLY_OK, not a generic mbedTLS error. 0x800B, labelled
"cert verify failed", is not an esp-tls error at all. So the one line an
operator reads to find out why a node is offline could name the wrong cause.
Move the tables into MQTTErrorLabels.h (pure, host-tested) with every value
static_asserted against the SDK enum/#define actually being compiled, so a
framework bump fails the build instead of relabelling errors in the field.
Codes the SDK does not define — including 61/88/168, which had labels no
header supports — now return no label and the caller prints the raw number.
Also keep the CONNACK return code from a broker refusal and show it in
`get mqttN.diag` ("refused: bad user/password (4)"). A refusal is not a
transport failure, so the TLS/socket fields are empty and a slot with wrong
credentials previously showed no useful detail at all.
This commit is contained in:
1 parent
a868c8eeb3
commit
ffa5067947
4 files changed
+267
-31
No files matched your search
@@ -1,4 +1,5 @@
|
||||
#include "MQTTBridge.h"
|
||||
#include "MQTTErrorLabels.h"
|
||||
#include "../WifiPowerSavePolicy.h"
|
||||
#include "../MQTTConnectionPolicy.h"
|
||||
#include "../MQTTMessageBuilder.h"
|
||||
@@ -22,6 +23,7 @@
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include <esp_wifi.h>
|
||||
#include <esp_tls.h>
|
||||
#include <esp_sntp.h>
|
||||
#include <esp_heap_caps.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
@@ -463,41 +465,52 @@ const char* MQTTBridge::getSlotPresetName(int slot_index) const {
|
||||
return MQTT_PRESET_CUSTOM;
|
||||
}
|
||||
|
||||
// The label tables live in MQTTErrorLabels.h so host tests can exercise them.
|
||||
// These asserts are the contract between that pure table and the SDK actually
|
||||
// being compiled against: a framework bump that renumbers a reason or an
|
||||
// esp-tls error fails here instead of silently mislabelling it in the field.
|
||||
#ifdef ESP_PLATFORM
|
||||
static_assert(MQTTErrorLabels::kWifiNoApFound == WIFI_REASON_NO_AP_FOUND, "wifi reason drift");
|
||||
static_assert(MQTTErrorLabels::kWifiAuthFail == WIFI_REASON_AUTH_FAIL, "wifi reason drift");
|
||||
static_assert(MQTTErrorLabels::kWifiAssocFail == WIFI_REASON_ASSOC_FAIL, "wifi reason drift");
|
||||
static_assert(MQTTErrorLabels::kWifiHandshakeTimeout == WIFI_REASON_HANDSHAKE_TIMEOUT, "wifi reason drift");
|
||||
static_assert(MQTTErrorLabels::kWifiConnectionFail == WIFI_REASON_CONNECTION_FAIL, "wifi reason drift");
|
||||
static_assert(MQTTErrorLabels::kWifiBeaconTimeout == WIFI_REASON_BEACON_TIMEOUT, "wifi reason drift");
|
||||
static_assert(MQTTErrorLabels::kWifiApTsfReset == WIFI_REASON_AP_TSF_RESET, "wifi reason drift");
|
||||
static_assert(MQTTErrorLabels::kWifiRoaming == WIFI_REASON_ROAMING, "wifi reason drift");
|
||||
static_assert(MQTTErrorLabels::kWifiSaQueryTimeout == WIFI_REASON_SA_QUERY_TIMEOUT, "wifi reason drift");
|
||||
static_assert(MQTTErrorLabels::kWifiAuthExpire == WIFI_REASON_AUTH_EXPIRE, "wifi reason drift");
|
||||
static_assert(MQTTErrorLabels::kWifiAssocExpire == WIFI_REASON_ASSOC_EXPIRE, "wifi reason drift");
|
||||
static_assert(MQTTErrorLabels::kWifiAssocLeave == WIFI_REASON_ASSOC_LEAVE, "wifi reason drift");
|
||||
static_assert(MQTTErrorLabels::kWifiBssTransitionDisassoc == WIFI_REASON_BSS_TRANSITION_DISASSOC, "wifi reason drift");
|
||||
static_assert(MQTTErrorLabels::kWifi4WayHandshakeTimeout == WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT, "wifi reason drift");
|
||||
static_assert(MQTTErrorLabels::kWifiGroupCipherInvalid == WIFI_REASON_GROUP_CIPHER_INVALID, "wifi reason drift");
|
||||
static_assert(MQTTErrorLabels::kWifiCipherSuiteRejected == WIFI_REASON_CIPHER_SUITE_REJECTED, "wifi reason drift");
|
||||
static_assert(MQTTErrorLabels::kWifiMissingAcks == WIFI_REASON_MISSING_ACKS, "wifi reason drift");
|
||||
static_assert(MQTTErrorLabels::kWifiTimeout == WIFI_REASON_TIMEOUT, "wifi reason drift");
|
||||
static_assert(MQTTErrorLabels::kWifiInvalidPmkid == WIFI_REASON_INVALID_PMKID, "wifi reason drift");
|
||||
|
||||
static_assert(MQTTErrorLabels::kTlsCannotResolveHostname == ESP_ERR_ESP_TLS_CANNOT_RESOLVE_HOSTNAME, "esp-tls error drift");
|
||||
static_assert(MQTTErrorLabels::kTlsCannotCreateSocket == ESP_ERR_ESP_TLS_CANNOT_CREATE_SOCKET, "esp-tls error drift");
|
||||
static_assert(MQTTErrorLabels::kTlsUnsupportedProtoFamily == ESP_ERR_ESP_TLS_UNSUPPORTED_PROTOCOL_FAMILY, "esp-tls error drift");
|
||||
static_assert(MQTTErrorLabels::kTlsFailedConnectToHost == ESP_ERR_ESP_TLS_FAILED_CONNECT_TO_HOST, "esp-tls error drift");
|
||||
static_assert(MQTTErrorLabels::kTlsSocketSetoptFailed == ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED, "esp-tls error drift");
|
||||
static_assert(MQTTErrorLabels::kTlsConnectionTimeout == ESP_ERR_ESP_TLS_CONNECTION_TIMEOUT, "esp-tls error drift");
|
||||
static_assert(MQTTErrorLabels::kTlsTcpClosedFin == ESP_ERR_ESP_TLS_TCP_CLOSED_FIN, "esp-tls error drift");
|
||||
static_assert(MQTTErrorLabels::kTlsMbedtlsCertPartlyOk == ESP_ERR_MBEDTLS_CERT_PARTLY_OK, "esp-tls error drift");
|
||||
static_assert(MQTTErrorLabels::kTlsMbedtlsSetHostname == ESP_ERR_MBEDTLS_SSL_SET_HOSTNAME_FAILED, "esp-tls error drift");
|
||||
static_assert(MQTTErrorLabels::kTlsMbedtlsX509ParseFailed == ESP_ERR_MBEDTLS_X509_CRT_PARSE_FAILED, "esp-tls error drift");
|
||||
static_assert(MQTTErrorLabels::kTlsMbedtlsSslSetupFailed == ESP_ERR_MBEDTLS_SSL_SETUP_FAILED, "esp-tls error drift");
|
||||
static_assert(MQTTErrorLabels::kTlsMbedtlsSslWriteFailed == ESP_ERR_MBEDTLS_SSL_WRITE_FAILED, "esp-tls error drift");
|
||||
static_assert(MQTTErrorLabels::kTlsMbedtlsHandshakeFailed == ESP_ERR_MBEDTLS_SSL_HANDSHAKE_FAILED, "esp-tls error drift");
|
||||
#endif
|
||||
|
||||
const char* MQTTBridge::wifiReasonStr(uint8_t reason) {
|
||||
switch (reason) {
|
||||
case 2: return "auth expired";
|
||||
case 4: return "assoc timeout";
|
||||
case 8: return "AP disconnected";
|
||||
case 15: return "4-way handshake timeout";
|
||||
case 18: return "group cipher mismatch";
|
||||
case 40: return "cipher suite rejected";
|
||||
case 49: return "invalid PMKID";
|
||||
case 61: return "AP BSS management";
|
||||
case 88: return "AP BSS management";
|
||||
case 168: return "AP band-steering kick";
|
||||
case 34: return "AP state mismatch (class 3 frame)";
|
||||
case 39: return "SSID not found";
|
||||
case 63: return "SA query timeout (PMF)";
|
||||
case 200: return "signal lost";
|
||||
case 201: return "security mismatch";
|
||||
case 202: return "auth mode rejected";
|
||||
case 204: return "handshake timeout";
|
||||
default: return nullptr;
|
||||
}
|
||||
return MQTTErrorLabels::wifiReason(reason);
|
||||
}
|
||||
|
||||
const char* MQTTBridge::tlsErrorStr(int32_t err) {
|
||||
switch (err) {
|
||||
case 0x8001: return "DNS failed";
|
||||
case 0x8002: return "socket error";
|
||||
case 0x8004: return "connect refused";
|
||||
case 0x8006: return "TLS timeout";
|
||||
case 0x8008: return "connection timeout";
|
||||
case 0x800B: return "cert verify failed";
|
||||
case 0x8010: return "mbedTLS error";
|
||||
case 0x801A: return "TLS handshake failed";
|
||||
default: return nullptr;
|
||||
}
|
||||
return MQTTErrorLabels::tlsError(err);
|
||||
}
|
||||
|
||||
void MQTTBridge::formatSlotDiagReply(char* buf, size_t bufsize, int slot_index) {
|
||||
@@ -564,6 +577,16 @@ void MQTTBridge::formatSlotDiagReply(char* buf, size_t bufsize, int slot_index)
|
||||
if (slot.connected && slot.last_error_time == 0) {
|
||||
replyAppendf(buf, bufsize, &pos, ", no errors");
|
||||
} else if (slot.last_error_time > 0) {
|
||||
// Broker refusal: the CONNECT reached a broker that answered "no". Reported
|
||||
// first because the transport fields below are empty or irrelevant then.
|
||||
if (slot.last_connack_code != 0) {
|
||||
const char* why = MQTTErrorLabels::connackReason(slot.last_connack_code);
|
||||
if (why) {
|
||||
replyAppendf(buf, bufsize, &pos, ", refused: %s (%u)", why, (unsigned)slot.last_connack_code);
|
||||
} else {
|
||||
replyAppendf(buf, bufsize, &pos, ", refused: code %u", (unsigned)slot.last_connack_code);
|
||||
}
|
||||
}
|
||||
// TLS error with human-friendly description
|
||||
if (slot.last_tls_err != 0) {
|
||||
const char* desc = tlsErrorStr(slot.last_tls_err);
|
||||
@@ -1660,6 +1683,7 @@ bool MQTTBridge::ensureSlotClient(int index) {
|
||||
_slots[index].last_tls_err = 0;
|
||||
_slots[index].last_tls_stack_err = 0;
|
||||
_slots[index].last_sock_errno = 0;
|
||||
_slots[index].last_connack_code = 0;
|
||||
_slots[index].last_error_time = 0;
|
||||
_slots[index].current_outage_started_ms = 0; // clear current-outage timer for AlertReporter
|
||||
updateCachedConnectionStatus(); // bool store — safe from this (esp-mqtt) task
|
||||
@@ -1689,6 +1713,11 @@ bool MQTTBridge::ensureSlotClient(int index) {
|
||||
_slots[index].last_tls_stack_err = error.esp_tls_stack_err;
|
||||
_slots[index].last_sock_errno = error.esp_transport_sock_errno;
|
||||
_slots[index].last_error_time = millis();
|
||||
// Cleared on any other error type so the diag describes the latest failure
|
||||
// rather than pairing a fresh transport error with an old refusal.
|
||||
_slots[index].last_connack_code =
|
||||
(error.error_type == MQTT_ERROR_TYPE_CONNECTION_REFUSED)
|
||||
? (uint8_t)error.connect_return_code : 0;
|
||||
if (error.error_type == MQTT_ERROR_TYPE_CONNECTION_REFUSED) {
|
||||
_slot_force_jwt_mint[index] = true;
|
||||
// Broker rejected the MQTT CONNECT itself — not a transport failure.
|
||||
|
||||
@@ -135,6 +135,10 @@ private:
|
||||
int32_t last_tls_err; // esp_tls_last_esp_err (0 = no error)
|
||||
int32_t last_tls_stack_err; // mbedTLS stack error
|
||||
int last_sock_errno; // socket errno
|
||||
// CONNACK return code from the last broker refusal (0 = none). A refusal is
|
||||
// not a transport failure, and without this the diag shows a slot with wrong
|
||||
// credentials as an unexplained disconnect.
|
||||
uint8_t last_connack_code;
|
||||
unsigned long last_error_time; // millis() of last error
|
||||
uint32_t disconnect_count; // Number of disconnect callbacks since boot
|
||||
unsigned long first_disconnect_time; // millis() of first disconnect after boot
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Operator-facing labels for the SDK error codes the observer reports through
|
||||
// `get wifi.status`, `get mqttN.diag` and the web config panel.
|
||||
//
|
||||
// Pure lookup: no Arduino or ESP headers, so the table is host-testable. The
|
||||
// numeric values below are the installed SDK's; MQTTBridge.cpp static_asserts
|
||||
// every one of them against the real enum/#define, so a framework bump either
|
||||
// keeps these labels honest or fails the build. Codes with no entry return
|
||||
// nullptr and the caller prints the raw number — an unknown code must never be
|
||||
// given a neighbouring code's name.
|
||||
namespace MQTTErrorLabels {
|
||||
|
||||
// esp_wifi_types.h wifi_err_reason_t. Values below 200 are 802.11 reason codes
|
||||
// forwarded from the AP; 200+ are Espressif's own local failures.
|
||||
enum WifiReason : uint16_t {
|
||||
kWifiAuthExpire = 2,
|
||||
kWifiAssocExpire = 4,
|
||||
kWifiAssocLeave = 8,
|
||||
kWifi4WayHandshakeTimeout = 15,
|
||||
kWifiGroupCipherInvalid = 18,
|
||||
kWifiCipherSuiteRejected = 24,
|
||||
kWifiMissingAcks = 34,
|
||||
kWifiTimeout = 39,
|
||||
kWifiInvalidPmkid = 49,
|
||||
kWifiBssTransitionDisassoc = 12,
|
||||
kWifiBeaconTimeout = 200,
|
||||
kWifiNoApFound = 201,
|
||||
kWifiAuthFail = 202,
|
||||
kWifiAssocFail = 203,
|
||||
kWifiHandshakeTimeout = 204,
|
||||
kWifiConnectionFail = 205,
|
||||
kWifiApTsfReset = 206,
|
||||
kWifiRoaming = 207,
|
||||
kWifiSaQueryTimeout = 209,
|
||||
};
|
||||
|
||||
// esp_tls_errors.h, ESP_ERR_ESP_TLS_BASE (0x8000) + offset.
|
||||
enum TlsError : int32_t {
|
||||
kTlsCannotResolveHostname = 0x8001,
|
||||
kTlsCannotCreateSocket = 0x8002,
|
||||
kTlsUnsupportedProtoFamily= 0x8003,
|
||||
kTlsFailedConnectToHost = 0x8004,
|
||||
kTlsSocketSetoptFailed = 0x8005,
|
||||
kTlsConnectionTimeout = 0x8006,
|
||||
kTlsTcpClosedFin = 0x8008,
|
||||
kTlsMbedtlsCertPartlyOk = 0x8010,
|
||||
kTlsMbedtlsSetHostname = 0x8012,
|
||||
kTlsMbedtlsX509ParseFailed= 0x8015,
|
||||
kTlsMbedtlsSslSetupFailed = 0x8017,
|
||||
kTlsMbedtlsSslWriteFailed = 0x8018,
|
||||
kTlsMbedtlsHandshakeFailed= 0x801A,
|
||||
};
|
||||
|
||||
// MQTT 3.1.1 CONNACK return codes (esp_mqtt_error_codes.connect_return_code).
|
||||
enum ConnackCode : uint8_t {
|
||||
kConnackAccepted = 0,
|
||||
kConnackBadProtocol = 1,
|
||||
kConnackIdRejected = 2,
|
||||
kConnackServerUnavail = 3,
|
||||
kConnackBadCredentials = 4,
|
||||
kConnackNotAuthorized = 5,
|
||||
};
|
||||
|
||||
// Short enough to sit inside a LoRa-bounded diagnostic reply next to the raw code.
|
||||
static inline const char* wifiReason(uint8_t reason) {
|
||||
switch (reason) {
|
||||
case kWifiAuthExpire: return "auth expired";
|
||||
case kWifiAssocExpire: return "assoc expired";
|
||||
case kWifiAssocLeave: return "AP disconnected";
|
||||
case kWifiBssTransitionDisassoc: return "AP steered us away";
|
||||
case kWifi4WayHandshakeTimeout: return "4-way handshake timeout";
|
||||
case kWifiGroupCipherInvalid: return "group cipher mismatch";
|
||||
case kWifiCipherSuiteRejected: return "cipher suite rejected";
|
||||
case kWifiMissingAcks: return "AP saw no acks";
|
||||
case kWifiTimeout: return "802.11 timeout";
|
||||
case kWifiInvalidPmkid: return "invalid PMKID";
|
||||
case kWifiBeaconTimeout: return "beacon lost";
|
||||
case kWifiNoApFound: return "SSID not found";
|
||||
case kWifiAuthFail: return "auth failed (check password)";
|
||||
case kWifiAssocFail: return "association failed";
|
||||
case kWifiHandshakeTimeout: return "handshake timeout";
|
||||
case kWifiConnectionFail: return "connect failed";
|
||||
case kWifiApTsfReset: return "AP restarted";
|
||||
case kWifiRoaming: return "roaming";
|
||||
case kWifiSaQueryTimeout: return "SA query timeout (PMF)";
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static inline const char* tlsError(int32_t err) {
|
||||
switch (err) {
|
||||
case kTlsCannotResolveHostname: return "DNS failed";
|
||||
case kTlsCannotCreateSocket: return "socket error";
|
||||
case kTlsUnsupportedProtoFamily: return "unsupported protocol family";
|
||||
case kTlsFailedConnectToHost: return "connect failed";
|
||||
case kTlsSocketSetoptFailed: return "socket setopt failed";
|
||||
case kTlsConnectionTimeout: return "connection timeout";
|
||||
case kTlsTcpClosedFin: return "server closed connection";
|
||||
case kTlsMbedtlsCertPartlyOk: return "cert chain partly parsed";
|
||||
case kTlsMbedtlsSetHostname: return "SNI hostname rejected";
|
||||
case kTlsMbedtlsX509ParseFailed: return "cert parse failed";
|
||||
case kTlsMbedtlsSslSetupFailed: return "TLS setup failed";
|
||||
case kTlsMbedtlsSslWriteFailed: return "TLS write failed";
|
||||
case kTlsMbedtlsHandshakeFailed: return "TLS handshake failed";
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// The broker answered and refused: this is what an operator needs instead of a
|
||||
// transport error, which is why the bridge keeps the CONNACK code separately.
|
||||
static inline const char* connackReason(uint8_t code) {
|
||||
switch (code) {
|
||||
case kConnackBadProtocol: return "protocol rejected";
|
||||
case kConnackIdRejected: return "client id rejected";
|
||||
case kConnackServerUnavail: return "server unavailable";
|
||||
case kConnackBadCredentials: return "bad user/password";
|
||||
case kConnackNotAuthorized: return "not authorized";
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace MQTTErrorLabels
|
||||
@@ -0,0 +1,78 @@
|
||||
#include "helpers/bridges/MQTTErrorLabels.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
using namespace MQTTErrorLabels;
|
||||
|
||||
namespace {
|
||||
|
||||
bool labelIs(const char* actual, const char* expected) {
|
||||
return actual != nullptr && strcmp(actual, expected) == 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// F14: these four were the mislabels found in review. 201 is "no AP found"
|
||||
// (usually a wrong/hidden SSID), not a security mismatch; 202 is an auth
|
||||
// failure (usually a wrong password), not a rejected auth mode.
|
||||
TEST(MQTTErrorLabels, WifiReasonsMatchTheSdkMeaning) {
|
||||
EXPECT_TRUE(labelIs(wifiReason(201), "SSID not found"));
|
||||
EXPECT_TRUE(labelIs(wifiReason(202), "auth failed (check password)"));
|
||||
EXPECT_TRUE(labelIs(wifiReason(39), "802.11 timeout")); // was "SSID not found"
|
||||
EXPECT_TRUE(labelIs(wifiReason(34), "AP saw no acks")); // was "AP state mismatch"
|
||||
}
|
||||
|
||||
TEST(MQTTErrorLabels, TlsErrorsMatchTheSdkMeaning) {
|
||||
EXPECT_TRUE(labelIs(tlsError(0x8008), "server closed connection")); // was "connection timeout"
|
||||
EXPECT_TRUE(labelIs(tlsError(0x8010), "cert chain partly parsed")); // was "mbedTLS error"
|
||||
EXPECT_TRUE(labelIs(tlsError(0x8006), "connection timeout"));
|
||||
EXPECT_TRUE(labelIs(tlsError(0x8001), "DNS failed"));
|
||||
EXPECT_TRUE(labelIs(tlsError(0x801A), "TLS handshake failed"));
|
||||
}
|
||||
|
||||
// An unknown code must fall through to the caller's raw-number formatting
|
||||
// rather than borrow a neighbouring code's label. 0x800B is the one the review
|
||||
// caught: no such esp-tls error exists, but it was labelled "cert verify failed".
|
||||
TEST(MQTTErrorLabels, UnknownCodesHaveNoLabel) {
|
||||
EXPECT_EQ(nullptr, tlsError(0x800B));
|
||||
EXPECT_EQ(nullptr, tlsError(0));
|
||||
EXPECT_EQ(nullptr, tlsError(-1));
|
||||
EXPECT_EQ(nullptr, tlsError(0x8099));
|
||||
|
||||
EXPECT_EQ(nullptr, wifiReason(0));
|
||||
EXPECT_EQ(nullptr, wifiReason(61)); // not defined by the installed SDK
|
||||
EXPECT_EQ(nullptr, wifiReason(88));
|
||||
EXPECT_EQ(nullptr, wifiReason(168));
|
||||
EXPECT_EQ(nullptr, wifiReason(255));
|
||||
}
|
||||
|
||||
TEST(MQTTErrorLabels, ConnackReasonsCoverTheRefusalCodes) {
|
||||
EXPECT_TRUE(labelIs(connackReason(1), "protocol rejected"));
|
||||
EXPECT_TRUE(labelIs(connackReason(2), "client id rejected"));
|
||||
EXPECT_TRUE(labelIs(connackReason(3), "server unavailable"));
|
||||
EXPECT_TRUE(labelIs(connackReason(4), "bad user/password"));
|
||||
EXPECT_TRUE(labelIs(connackReason(5), "not authorized"));
|
||||
EXPECT_EQ(nullptr, connackReason(0)); // accepted: not an error to report
|
||||
EXPECT_EQ(nullptr, connackReason(6));
|
||||
}
|
||||
|
||||
// Labels share a bounded LoRa reply with the rest of the diagnostic line.
|
||||
TEST(MQTTErrorLabels, LabelsStayShortEnoughForABoundedReply) {
|
||||
for (int i = 0; i <= 255; i++) {
|
||||
const char* w = wifiReason((uint8_t)i);
|
||||
if (w) EXPECT_LE(strlen(w), 30u) << "wifi reason " << i;
|
||||
const char* c = connackReason((uint8_t)i);
|
||||
if (c) EXPECT_LE(strlen(c), 30u) << "connack code " << i;
|
||||
}
|
||||
for (int32_t e = 0x8000; e <= 0x8040; e++) {
|
||||
const char* t = tlsError(e);
|
||||
if (t) EXPECT_LE(strlen(t), 30u) << "tls error " << e;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
Reference in new issue
Block a user