已关闭
WiFi Only: add verified device SoftAP provisioning runtime #223
faizk创建于 5 天前关闭于 2 天前
WiFi Only: add verified device SoftAP provisioning runtime #223
已关闭
共 94 个文件变更+6676-208
| @@ -46,16 +46,49 @@ if (iotc_device_level == "mini") { | |||
| 46 | iotc_def += ["IOTC_ONECONNECT_VERSION_NAME=\"${iotc_one_connect_version_name}\""] | 46 | iotc_def += ["IOTC_ONECONNECT_VERSION_NAME=\"${iotc_one_connect_version_name}\""] |
| 47 | iotc_def += ["IOTC_ONECONNECT_VERSION_CODE=${iotc_one_connect_version_code}"] | 47 | iotc_def += ["IOTC_ONECONNECT_VERSION_CODE=${iotc_one_connect_version_code}"] |
| 48 | iotc_def += ["SPEKE_PBKDF2_ITERATIONS=${iot_connect_speke_pbkdf2_iterations}"] | 48 | iotc_def += ["SPEKE_PBKDF2_ITERATIONS=${iot_connect_speke_pbkdf2_iterations}"] |
| 49 | -iotc_def += ["SEC_TERMINAL_PSK_MAX_NUM=${sec_terminal_psk_max_num}"] | 49 | +if (iot_connect_speke_secret_trace) { |
| 50 | -iotc_def += ["SEC_ACCESS_PSK_MAX_NUM=${sec_access_psk_real_max_num}"] | 50 | + assert(iot_connect_speke_v2, |
| 51 | -iotc_def += ["SEC_ACL_MAX_NUM=${sec_acl_max_num}"] | 51 | + "SPEKE secret trace requires the Unified Connect 3.0 SPEKE v2 path") |
| 52 | + iotc_def += ["IOTC_CONF_SPEKE_SECRET_TRACE=1"] | ||
| 53 | +} | ||
| 54 | +iotc_def += ["SEC_TERMINAL_PSK_MAX_NUM=${sec_terminal_psk_max_num}"] | ||
| 55 | +iotc_def += ["SEC_ACCESS_PSK_MAX_NUM=${sec_access_psk_real_max_num}"] | ||
| 56 | +iotc_def += ["SEC_ACL_MAX_NUM=${sec_acl_max_num}"] | ||
| 57 | +if (!iot_connect_databus_support) { | ||
| 58 | + iotc_def += ["IOTC_CONF_DATABUS_SUPPORT=0"] | ||
| 59 | +} | ||
| 60 | +if (iot_connect_wifi_only_support) { | ||
| 61 | + iotc_def += ["IOTC_CONF_WIFI_ONLY_SUPPORT=1"] | ||
| 62 | + # WiFi Only 配网业务承载 PIN、PSK 与家庭 Wi-Fi 凭据。配置头的历史默认值 | ||
| 63 | + # 是开启 CoAP 转储,因此必须显式关闭,不能只依赖“不添加 debug define”。 | ||
| 64 | + iotc_def += [ | ||
| 65 | + "IOTC_CONF_COAP_DEBUG_DUMP_PACKET=0", | ||
| 66 | + "IOTC_CONF_COAP_DEBUG_DUMP_PAYLOAD=0", | ||
| 67 | + "IOTC_CONF_NET_COAP_DEBUG_DUMP_PACKET=0", | ||
| 68 | + "IOTC_CONF_NET_COAP_DEBUG_DUMP_PAYLOAD=0", | ||
| 69 | + ] | ||
| 70 | + # WS63 设备:socket fd 由 lwip 分配(>=256),libc 的 select/close 不认识 lwip 句柄, | ||
| 71 | + # 适配层须显式走 lwip_select/closesocket,见 adapter/socket/iotc_socket.c | ||
| 72 | + iotc_def += ["IOTC_CONF_ADAPTER_SOCKET_LWIP_FDS=1"] | ||
| 73 | + iotc_def += ["IOTC_CONF_ADAPTER_WIFI_MANAGE_SOFTAP_IP=1"] | ||
| 74 | + iotc_def += ["IOTC_CONF_BROAD_CAST_SUPPORT=1"] | ||
| 75 | +} | ||
| 76 | + | ||
| 77 | +if (iot_connect_mutex_lockless) { | ||
| 78 | + # 仅该开关打开的产品使用 lockless ExMutex,其他产品保持默认互斥锁逻辑 | ||
| 79 | + iotc_def += ["IOTC_CONF_UTILS_MUTEX_LOCKLESS=1"] | ||
| 80 | +} | ||
| 52 | 81 | ||
| 53 | if (defined(ohos_lite)) { | 82 | if (defined(ohos_lite)) { |
| 54 | import("//build/lite/config/component/lite_component.gni") | 83 | import("//build/lite/config/component/lite_component.gni") |
| 55 | 84 | ||
| 56 | if (ohos_kernel_type == "liteos_m") { | 85 | if (ohos_kernel_type == "liteos_m") { |
| 57 | if (ohos_build_type == "debug") { | 86 | if (ohos_build_type == "debug") { |
| 58 | - iotc_def += ["IOTC_CONF_COAP_DEBUG_DUMP_PACKET=1"] | 87 | + # WiFi Only carries provisioning credentials in CoAP payloads. Keep |
| 88 | + # packet dumping off even in a debug build for this product profile. | ||
| 89 | + if (!iot_connect_wifi_only_support) { | ||
| 90 | + iotc_def += ["IOTC_CONF_COAP_DEBUG_DUMP_PACKET=1"] | ||
| 91 | + } | ||
| 59 | iotc_def += ["IOTC_CONF_BUILD_TYPE=1"] | 92 | iotc_def += ["IOTC_CONF_BUILD_TYPE=1"] |
| 60 | iotc_def += ["IOT_CONF_LOG_DEFAULT_LEVEL=7"] | 93 | iotc_def += ["IOT_CONF_LOG_DEFAULT_LEVEL=7"] |
| 61 | } else { | 94 | } else { |
| @@ -69,7 +69,7 @@ typedef struct { | |||
| 69 | char ssid[IOTC_WIFI_SSID_MAX_LEN]; | 69 | char ssid[IOTC_WIFI_SSID_MAX_LEN]; |
| 70 | uint8_t bssid[IOTC_WIFI_BSSID_LEN]; | 70 | uint8_t bssid[IOTC_WIFI_BSSID_LEN]; |
| 71 | IotcWifiSecurityType securityType; | 71 | IotcWifiSecurityType securityType; |
| 72 | - uint8_t rssi; | 72 | + int32_t rssi; |
| 73 | uint32_t band; | 73 | uint32_t band; |
| 74 | uint32_t frequency; | 74 | uint32_t frequency; |
| 75 | } IotcWifiInfo; | 75 | } IotcWifiInfo; |
| @@ -62,18 +62,6 @@ int32_t IotcKvInit(const char *tag) | |||
| 62 | return IOTC_OK; | 62 | return IOTC_OK; |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | -static int32_t CreateFile(const char *filePath) | ||
| 66 | -{ | ||
| 67 | - IOTC_LOGD("create %s", filePath); | ||
| 68 | - int32_t fd = UtilsFileOpen(filePath, O_RDWR_FS | O_CREAT_FS, 0); | ||
| 69 | - if (fd < 0) { | ||
| 70 | - IOTC_LOGE("open or create error %d", fd); | ||
| 71 | - return IOTC_ADAPTER_KV_ERR_CREATE_FILE; | ||
| 72 | - } | ||
| 73 | - UtilsFileClose(fd); | ||
| 74 | - return IOTC_OK; | ||
| 75 | -} | ||
| 76 | - | ||
| 77 | static bool CheckFileIsExisit(const char *filePath) | 65 | static bool CheckFileIsExisit(const char *filePath) |
| 78 | { | 66 | { |
| 79 | int32_t fd = UtilsFileOpen(filePath, O_RDONLY_FS, 0); | 67 | int32_t fd = UtilsFileOpen(filePath, O_RDONLY_FS, 0); |
| @@ -88,14 +76,14 @@ static bool CheckFileIsExisit(const char *filePath) | |||
| 88 | static int32_t KvSetValue(const char *filePath, const uint8_t *buf, uint32_t len) | 76 | static int32_t KvSetValue(const char *filePath, const uint8_t *buf, uint32_t len) |
| 89 | { | 77 | { |
| 90 | IOTC_LOGD("write %s", filePath); | 78 | IOTC_LOGD("write %s", filePath); |
| 91 | - int32_t fd = UtilsFileOpen(filePath, O_RDWR_FS | O_CREAT_FS, 0); | 79 | + int32_t fd = UtilsFileOpen(filePath, O_RDWR_FS | O_CREAT_FS | O_TRUNC_FS, 0); |
| 92 | if (fd < 0) { | 80 | if (fd < 0) { |
| 93 | IOTC_LOGE("open file[%s] error", filePath); | 81 | IOTC_LOGE("open file[%s] error", filePath); |
| 94 | return IOTC_ADAPTER_KV_ERR_SET_ITEM; | 82 | return IOTC_ADAPTER_KV_ERR_SET_ITEM; |
| 95 | } | 83 | } |
| 96 | 84 | ||
| 97 | - int32_t ret = UtilsFileWrite(fd, (uint8_t *)buf, len); | 85 | + int32_t ret = UtilsFileWrite(fd, (const char *)buf, len); |
| 98 | - if (ret < 0) { | 86 | + if (ret != (int32_t)len) { |
| 99 | IOTC_LOGE("write file[%s] error", filePath); | 87 | IOTC_LOGE("write file[%s] error", filePath); |
| 100 | UtilsFileClose(fd); | 88 | UtilsFileClose(fd); |
| 101 | return IOTC_ADAPTER_KV_ERR_SET_ITEM; | 89 | return IOTC_ADAPTER_KV_ERR_SET_ITEM; |
| @@ -135,7 +123,7 @@ int32_t IotcKvSetValue(const char *key, const uint8_t *value, uint32_t len) | |||
| 135 | static int32_t KvGetValue(const char *filePath, uint8_t *buf, uint32_t *len) | 123 | static int32_t KvGetValue(const char *filePath, uint8_t *buf, uint32_t *len) |
| 136 | { | 124 | { |
| 137 | IOTC_LOGD("read %s", filePath); | 125 | IOTC_LOGD("read %s", filePath); |
| 138 | - int32_t fd = UtilsFileOpen(filePath, O_RDWR_FS | O_CREAT_FS, 0); | 126 | + int32_t fd = UtilsFileOpen(filePath, O_RDONLY_FS, 0); |
| 139 | if (fd < 0) { | 127 | if (fd < 0) { |
| 140 | IOTC_LOGE("open file[%s] error", filePath); | 128 | IOTC_LOGE("open file[%s] error", filePath); |
| 141 | return IOTC_ADAPTER_KV_ERR_GET_ITEM; | 129 | return IOTC_ADAPTER_KV_ERR_GET_ITEM; |
| @@ -154,8 +142,8 @@ static int32_t KvGetValue(const char *filePath, uint8_t *buf, uint32_t *len) | |||
| 154 | return IOTC_ADAPTER_KV_ERR_GET_ITEM; | 142 | return IOTC_ADAPTER_KV_ERR_GET_ITEM; |
| 155 | } | 143 | } |
| 156 | *len = fileLen; | 144 | *len = fileLen; |
| 157 | - ret = UtilsFileRead(fd, buf, *len); | 145 | + ret = UtilsFileRead(fd, (char *)buf, *len); |
| 158 | - if (ret < 0) { | 146 | + if (ret != (int32_t)(*len)) { |
| 159 | IOTC_LOGE("read file[%s] error", filePath); | 147 | IOTC_LOGE("read file[%s] error", filePath); |
| 160 | UtilsFileClose(fd); | 148 | UtilsFileClose(fd); |
| 161 | return IOTC_ADAPTER_KV_ERR_GET_ITEM; | 149 | return IOTC_ADAPTER_KV_ERR_GET_ITEM; |
| @@ -182,28 +170,24 @@ int32_t IotcKvGetValue(const char *key, uint8_t *buf, uint32_t *len) | |||
| 182 | return KvGetValue((const char *)filePath, buf, len); | 170 | return KvGetValue((const char *)filePath, buf, len); |
| 183 | } | 171 | } |
| 184 | 172 | ||
| 185 | -static uint32_t GetFileLenByName(const char *filePath, uint32_t *len) | 173 | +static int32_t GetFileLenByName(const char *filePath, uint32_t *len) |
| 186 | { | 174 | { |
| 187 | - int32_t fd = UtilsFileOpen(filePath, O_RDWR_FS | O_CREAT_FS, 0); | 175 | + if (!CheckFileIsExisit(filePath)) { |
| 188 | - if (fd < 0) { | 176 | + *len = 0; |
| 189 | - IOTC_LOGE("open file[%s] error", filePath); | 177 | + return IOTC_OK; |
| 190 | - return IOTC_ADAPTER_KV_ERR_OPEN_FILE; | ||
| 191 | } | 178 | } |
| 192 | - | ||
| 193 | int32_t ret = UtilsFileStat(filePath, len); | 179 | int32_t ret = UtilsFileStat(filePath, len); |
| 194 | if (ret != 0) { | 180 | if (ret != 0) { |
| 195 | IOTC_LOGE("stat file[%s] error", filePath); | 181 | IOTC_LOGE("stat file[%s] error", filePath); |
| 196 | - UtilsFileClose(fd); | ||
| 197 | *len = 0; | 182 | *len = 0; |
| 198 | return IOTC_ADAPTER_KV_ERR_STAT_FILE; | 183 | return IOTC_ADAPTER_KV_ERR_STAT_FILE; |
| 199 | } | 184 | } |
| 200 | - UtilsFileClose(fd); | ||
| 201 | return IOTC_OK; | 185 | return IOTC_OK; |
| 202 | } | 186 | } |
| 203 | 187 | ||
| 204 | int32_t IotcKvGetLen(const char *key, uint32_t *len) | 188 | int32_t IotcKvGetLen(const char *key, uint32_t *len) |
| 205 | { | 189 | { |
| 206 | - if ((key == NULL) || (key[0] == '\0')) { | 190 | + if ((key == NULL) || (key[0] == '\0') || (len == NULL)) { |
| 207 | IOTC_LOGE("invalid param"); | 191 | IOTC_LOGE("invalid param"); |
| 208 | return IOTC_ERR_PARAM_INVALID; | 192 | return IOTC_ERR_PARAM_INVALID; |
| 209 | } | 193 | } |
| @@ -216,11 +200,7 @@ int32_t IotcKvGetLen(const char *key, uint32_t *len) | |||
| 216 | if (MakeFilePath(key, filePath, sizeof(filePath)) != IOTC_OK) { | 200 | if (MakeFilePath(key, filePath, sizeof(filePath)) != IOTC_OK) { |
| 217 | return IOTC_ERR_SECUREC_SPRINTF; | 201 | return IOTC_ERR_SECUREC_SPRINTF; |
| 218 | } | 202 | } |
| 219 | - ret = GetFileLenByName((const char *)filePath, len); | 203 | + return GetFileLenByName((const char *)filePath, len); |
| 220 | - if (ret != IOTC_OK) { | ||
| 221 | - IOTC_LOGW("get fileLen err"); | ||
| 222 | - } | ||
| 223 | - return IOTC_OK; | ||
| 224 | } | 204 | } |
| 225 | 205 | ||
| 226 | static int32_t KvDelValue(const char *filePath) | 206 | static int32_t KvDelValue(const char *filePath) |
| @@ -262,4 +242,4 @@ int32_t IotcKvDeInit(void) | |||
| 262 | (void)memset_s(g_tag, sizeof(g_tag), 0, sizeof(g_tag)); | 242 | (void)memset_s(g_tag, sizeof(g_tag), 0, sizeof(g_tag)); |
| 263 | g_isInit = false; | 243 | g_isInit = false; |
| 264 | return IOTC_OK; | 244 | return IOTC_OK; |
| 265 | -} | 245 | +} |
| @@ -23,10 +23,46 @@ | |||
| 23 | 23 | ||
| 24 | 24 | ||
| 25 | 25 | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 26 | 31 | ||
| 32 | +/* | ||
| 33 | + * WS63 的 GetLinkedInfo 只查 STA 链路(STA 未使能时必失败), | ||
| 34 | + * SoftAP 场景必须改读 AP 接口的 IPv4 地址。 | ||
| 35 | + */ | ||
| 27 | int32_t IotcGetSoftApIp(char *buf, uint32_t len) | 36 | int32_t IotcGetSoftApIp(char *buf, uint32_t len) |
| 28 | { | 37 | { |
| 38 | + | ||
| 29 | return IotcGetLocalIp(buf, len); | 39 | return IotcGetLocalIp(buf, len); |
| 40 | + | ||
| 41 | + if ((buf == NULL) || (len == 0)) { | ||
| 42 | + IOTC_LOGE("invalid param"); | ||
| 43 | + return IOTC_ERR_PARAM_INVALID; | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + static const char *const candidates[] = {"ap0", "wlan0"}; | ||
| 47 | + for (uint32_t i = 0; i < sizeof(candidates) / sizeof(candidates[0]); ++i) { | ||
| 48 | + struct netif *netif = netifapi_netif_find_by_name(candidates[i]); | ||
| 49 | + if (netif == NULL) { | ||
| 50 | + continue; | ||
| 51 | + } | ||
| 52 | + ip4_addr_t ipaddr = {0}; | ||
| 53 | + if (netifapi_netif_get_addr(netif, &ipaddr, NULL, NULL) != 0) { | ||
| 54 | + continue; | ||
| 55 | + } | ||
| 56 | + if (ip4_addr_isany_val(ipaddr)) { | ||
| 57 | + continue; | ||
| 58 | + } | ||
| 59 | + const char *ip = IotcInetNtoa(IotcNtohl(ipaddr.addr), buf, len); | ||
| 60 | + if ((ip != NULL) && (ip[0] != '\0')) { | ||
| 61 | + return IOTC_OK; | ||
| 62 | + } | ||
| 63 | + } | ||
| 64 | + return IOTC_ADAPTER_NETWORK_ERR_GET_IP; | ||
| 65 | + | ||
| 30 | } | 66 | } |
| 31 | 67 | ||
| 32 | int32_t IotcGetLocalIp(char *buf, uint32_t len) | 68 | int32_t IotcGetLocalIp(char *buf, uint32_t len) |
| @@ -49,7 +85,8 @@ int32_t IotcGetLocalIp(char *buf, uint32_t len) | |||
| 49 | } | 85 | } |
| 50 | isPrint = false; | 86 | isPrint = false; |
| 51 | 87 | ||
| 52 | - const char *ip = IotcInetNtoa(info.ipAddress, buf, len); | 88 | + /* OH Lite HAL returns lwIP's IPv4 value in network byte order. */ |
| 89 | + const char *ip = IotcInetNtoa(IotcNtohl(info.ipAddress), buf, len); | ||
| 53 | if ((ip == NULL) || (ip[0] == '\0')) { | 90 | if ((ip == NULL) || (ip[0] == '\0')) { |
| 54 | return IOTC_ADAPTER_NETWORK_ERR_GET_IP; | 91 | return IOTC_ADAPTER_NETWORK_ERR_GET_IP; |
| 55 | } | 92 | } |
| @@ -91,17 +128,30 @@ int32_t IotcGetBroadcastAddr(char *buf, uint32_t len) | |||
| 91 | return IOTC_ERR_PARAM_INVALID; | 128 | return IOTC_ERR_PARAM_INVALID; |
| 92 | } | 129 | } |
| 93 | 130 | ||
| 131 | + | ||
| 132 | + struct netif *netif = netifapi_netif_find_by_name(IOTC_STA_IFNAME); | ||
| 133 | + ip4_addr_t ipAddress = {0}; | ||
| 134 | + ip4_addr_t netMask = {0}; | ||
| 135 | + if (netif == NULL || netifapi_netif_get_addr(netif, &ipAddress, &netMask, NULL) != 0 || | ||
| 136 | + ip4_addr_isany_val(ipAddress) || ip4_addr_isany_val(netMask)) { | ||
| 137 | + return IOTC_ADAPTER_NETWORK_ERR_GET_BROADCAST_IP; | ||
| 138 | + } | ||
| 139 | + uint32_t broadcastIp = (ipAddress.addr & netMask.addr) | (~netMask.addr); | ||
| 140 | + const char *ip = IotcInetNtoa(IotcNtohl(broadcastIp), buf, len); | ||
| 141 | + | ||
| 94 | IpInfo ipInfo = {0}; | 142 | IpInfo ipInfo = {0}; |
| 95 | WifiErrorCode ret = GetIpInfo(&ipInfo); | 143 | WifiErrorCode ret = GetIpInfo(&ipInfo); |
| 96 | if (ret != WIFI_SUCCESS) { | 144 | if (ret != WIFI_SUCCESS) { |
| 97 | IOTC_LOGE("get ip info error %d", ret); | 145 | IOTC_LOGE("get ip info error %d", ret); |
| 98 | - return IOTC_IOTC_NETWORK_ERR_GET_BROADCAST_IP; | 146 | + return IOTC_ADAPTER_NETWORK_ERR_GET_BROADCAST_IP; |
| 99 | } | 147 | } |
| 100 | uint32_t broadcastIp = (ipInfo.ipAddress & ipInfo.netMask) | (~ipInfo.netMask); | 148 | uint32_t broadcastIp = (ipInfo.ipAddress & ipInfo.netMask) | (~ipInfo.netMask); |
| 101 | - const char *ip = IotcInetNtoa(broadcastIp, buf, len); | 149 | + /* OH Lite HAL 的 IPv4/掩码均为网络字节序,IotcInetNtoa 接收主机序。 */ |
| 150 | + const char *ip = IotcInetNtoa(IotcNtohl(broadcastIp), buf, len); | ||
| 151 | + | ||
| 102 | if ((ip == NULL) || (ip[0] == '\0')) { | 152 | if ((ip == NULL) || (ip[0] == '\0')) { |
| 103 | - return IOTC_IOTC_NETWORK_ERR_GET_BROADCAST_IP; | 153 | + return IOTC_ADAPTER_NETWORK_ERR_GET_BROADCAST_IP; |
| 104 | } | 154 | } |
| 105 | 155 | ||
| 106 | return IOTC_OK; | 156 | return IOTC_OK; |
| 107 | -} | 157 | +} |
| @@ -15,6 +15,7 @@ | |||
| 15 | 15 | ||
| 16 | 16 | ||
| 17 | 17 | ||
| 18 | + | ||
| 18 | 19 | ||
| 19 | 20 | ||
| 20 | 21 | ||
| @@ -25,11 +26,23 @@ | |||
| 25 | 26 | ||
| 26 | 27 | ||
| 27 | 28 | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + | ||
| 35 | +/* SoftAP 接口地址与 DHCP 服务由调用方负责(同 WS63 官方 softap 示例): | ||
| 36 | + * 部分 wifiservice 实现只起热点不配地址/不起 dhcps,手机拿不到租约会掉线。 */ | ||
| 37 | + | ||
| 38 | + | ||
| 39 | + | ||
| 28 | 40 | ||
| 29 | 41 | ||
| 30 | 42 | ||
| 31 | 43 | ||
| 32 | 44 | ||
| 45 | + | ||
| 33 | 46 | ||
| 34 | static bool g_isRegisterWifiEvent = false; | 47 | static bool g_isRegisterWifiEvent = false; |
| 35 | static bool g_isStaScanSuccess = false; | 48 | static bool g_isStaScanSuccess = false; |
| @@ -38,6 +51,42 @@ static uint16_t g_disconnectReason = 0; | |||
| 38 | static bool g_isReasonRefresh = false; | 51 | static bool g_isReasonRefresh = false; |
| 39 | static IotcWifiEvent g_adapterEventHandler = {0}; | 52 | static IotcWifiEvent g_adapterEventHandler = {0}; |
| 40 | 53 | ||
| 54 | +static int32_t RegisterWifiEventToOhos(void); | ||
| 55 | + | ||
| 56 | +static int32_t CopyWifiInfoFromConfig(const WifiDeviceConfig *config, uint8_t *ssidBuf, | ||
| 57 | + uint32_t *ssidBufLen, uint8_t *pwdBuf, uint32_t *pwdBufLen) | ||
| 58 | +{ | ||
| 59 | + if (config == NULL || config->ssid[0] == '\0') { | ||
| 60 | + return IOTC_ADAPTER_WIFI_ERR_GET_INFO; | ||
| 61 | + } | ||
| 62 | + if (strcpy_s((char *)ssidBuf, *ssidBufLen, config->ssid) != EOK) { | ||
| 63 | + IOTC_LOGW("copy ssid fail"); | ||
| 64 | + return IOTC_ERR_SECUREC_STRCPY; | ||
| 65 | + } | ||
| 66 | + *ssidBufLen = strlen(config->ssid); | ||
| 67 | + | ||
| 68 | + if (config->preSharedKey[0] == '\0') { | ||
| 69 | + *pwdBufLen = 0; | ||
| 70 | + return IOTC_OK; | ||
| 71 | + } | ||
| 72 | + if (strcpy_s((char *)pwdBuf, *pwdBufLen, config->preSharedKey) != EOK) { | ||
| 73 | + IOTC_LOGW("copy password fail"); | ||
| 74 | + return IOTC_ERR_SECUREC_STRCPY; | ||
| 75 | + } | ||
| 76 | + *pwdBufLen = strlen(config->preSharedKey); | ||
| 77 | + return IOTC_OK; | ||
| 78 | +} | ||
| 79 | + | ||
| 80 | +static const WifiDeviceConfig *GetValidWifiConfig(const WifiDeviceConfig *wifiConfig, uint32_t size) | ||
| 81 | +{ | ||
| 82 | + for (uint32_t i = 0; i < size && i < WIFI_MAX_CONFIG_SIZE; ++i) { | ||
| 83 | + if (wifiConfig[i].ssid[0] != '\0') { | ||
| 84 | + return &wifiConfig[i]; | ||
| 85 | + } | ||
| 86 | + } | ||
| 87 | + return NULL; | ||
| 88 | +} | ||
| 89 | + | ||
| 41 | int32_t IotcGetWifiInfo(uint8_t *ssidBuf, uint32_t *ssidBufLen, uint8_t *pwdBuf, uint32_t *pwdBufLen) | 90 | int32_t IotcGetWifiInfo(uint8_t *ssidBuf, uint32_t *ssidBufLen, uint8_t *pwdBuf, uint32_t *pwdBufLen) |
| 42 | { | 91 | { |
| 43 | if ((ssidBuf == NULL) || (ssidBufLen == NULL) || (pwdBuf == NULL) || (pwdBufLen ==NULL)) { | 92 | if ((ssidBuf == NULL) || (ssidBufLen == NULL) || (pwdBuf == NULL) || (pwdBufLen ==NULL)) { |
| @@ -57,31 +106,15 @@ int32_t IotcGetWifiInfo(uint8_t *ssidBuf, uint32_t *ssidBufLen, uint8_t *pwdBuf, | |||
| 57 | IOTC_LOGI("get device config fail"); | 106 | IOTC_LOGI("get device config fail"); |
| 58 | return IOTC_ADAPTER_WIFI_ERR_GET_INFO; | 107 | return IOTC_ADAPTER_WIFI_ERR_GET_INFO; |
| 59 | } | 108 | } |
| 60 | - if (wifiConfig[0].ssid[0] == '\0') { | 109 | + const WifiDeviceConfig *config = GetValidWifiConfig(wifiConfig, size); |
| 110 | + if (config == NULL) { | ||
| 61 | IOTC_LOGW("GetDeviceConfigs fail, no ssid"); | 111 | IOTC_LOGW("GetDeviceConfigs fail, no ssid"); |
| 112 | + (void)memset_s(wifiConfig, sizeof(wifiConfig), 0, sizeof(wifiConfig)); | ||
| 62 | return IOTC_ADAPTER_WIFI_ERR_GET_INFO; | 113 | return IOTC_ADAPTER_WIFI_ERR_GET_INFO; |
| 63 | } | 114 | } |
| 64 | - | 115 | + int32_t ret = CopyWifiInfoFromConfig(config, ssidBuf, ssidBufLen, pwdBuf, pwdBufLen); |
| 65 | - if (strcpy_s((char *)ssidBuf, *ssidBufLen, (char *)wifiConfig[0].ssid) != EOK) { | ||
| 66 | - IOTC_LOGW("strcpy fail"); | ||
| 67 | - (void)memset_s(wifiConfig, sizeof(wifiConfig), 0, sizeof(wifiConfig)); | ||
| 68 | - return IOTC_ERR_SECUREC_STRCPY; | ||
| 69 | - } | ||
| 70 | - *ssidBufLen = strlen(wifiConfig[0].ssid); | ||
| 71 | - | ||
| 72 | - if (wifiConfig[0].preSharedKey[0] == '\0') { | ||
| 73 | - (void)memset_s(wifiConfig, sizeof(wifiConfig), 0, sizeof(wifiConfig)); | ||
| 74 | - *pwdBufLen = 0; | ||
| 75 | - return IOTC_OK; | ||
| 76 | - } | ||
| 77 | - if (strcpy_s((char *)pwdBuf, *pwdBufLen, (char *)wifiConfig[0].preSharedKey) != EOK) { | ||
| 78 | - IOTC_LOGW("strcpy fail"); | ||
| 79 | - (void)memset_s(wifiConfig, sizeof(wifiConfig), 0, sizeof(wifiConfig)); | ||
| 80 | - return IOTC_ERR_SECUREC_STRCPY; | ||
| 81 | - } | ||
| 82 | - *pwdBufLen = strlen(wifiConfig[0].preSharedKey); | ||
| 83 | (void)memset_s(wifiConfig, sizeof(wifiConfig), 0, sizeof(wifiConfig)); | 116 | (void)memset_s(wifiConfig, sizeof(wifiConfig), 0, sizeof(wifiConfig)); |
| 84 | - return IOTC_OK; | 117 | + return ret; |
| 85 | } | 118 | } |
| 86 | 119 | ||
| 87 | static int32_t GetWifiConfigFromOhos(WifiDeviceConfig *config) | 120 | static int32_t GetWifiConfigFromOhos(WifiDeviceConfig *config) |
| @@ -94,11 +127,18 @@ static int32_t GetWifiConfigFromOhos(WifiDeviceConfig *config) | |||
| 94 | return IOTC_ADAPTER_WIFI_ERR_GET_INFO; | 127 | return IOTC_ADAPTER_WIFI_ERR_GET_INFO; |
| 95 | } | 128 | } |
| 96 | 129 | ||
| 97 | - if (memcpy_s(config, sizeof(WifiDeviceConfig), &wifiConfig[0], sizeof(WifiDeviceConfig)) != EOK) { | 130 | + const WifiDeviceConfig *savedConfig = GetValidWifiConfig(wifiConfig, size); |
| 131 | + if (savedConfig == NULL) { | ||
| 132 | + IOTC_LOGW("GetDeviceConfigs fail, no ssid"); | ||
| 133 | + (void)memset_s(wifiConfig, sizeof(wifiConfig), 0, sizeof(wifiConfig)); | ||
| 134 | + return IOTC_ADAPTER_WIFI_ERR_GET_INFO; | ||
| 135 | + } | ||
| 136 | + if (memcpy_s(config, sizeof(WifiDeviceConfig), savedConfig, sizeof(WifiDeviceConfig)) != EOK) { | ||
| 98 | IOTC_LOGW("memcpy error"); | 137 | IOTC_LOGW("memcpy error"); |
| 99 | (void)memset_s(wifiConfig, sizeof(wifiConfig), 0, sizeof(wifiConfig)); | 138 | (void)memset_s(wifiConfig, sizeof(wifiConfig), 0, sizeof(wifiConfig)); |
| 100 | return IOTC_ERR_SECUREC_MEMCPY; | 139 | return IOTC_ERR_SECUREC_MEMCPY; |
| 101 | } | 140 | } |
| 141 | + IOTC_LOGI("select wifi config netId=%d, ssidLen=%u", config->netId, (uint32_t)strlen(config->ssid)); | ||
| 102 | (void)memset_s(wifiConfig, sizeof(wifiConfig), 0, sizeof(wifiConfig)); | 142 | (void)memset_s(wifiConfig, sizeof(wifiConfig), 0, sizeof(wifiConfig)); |
| 103 | return IOTC_OK; | 143 | return IOTC_OK; |
| 104 | } | 144 | } |
| @@ -108,7 +148,7 @@ static int32_t GetWifiNetworkIdFromOhos(int32_t *netId) | |||
| 108 | WifiDeviceConfig config; | 148 | WifiDeviceConfig config; |
| 109 | (void)memset_s(&config, sizeof(WifiDeviceConfig), 0, sizeof(WifiDeviceConfig)); | 149 | (void)memset_s(&config, sizeof(WifiDeviceConfig), 0, sizeof(WifiDeviceConfig)); |
| 110 | 150 | ||
| 111 | - if (GetWifiConfigFromOhos(&config) != IOTC_OK) { | 151 | + if (GetWifiConfigFromOhos(&config) != IOTC_OK || config.ssid[0] == '\0') { |
| 112 | return IOTC_ADAPTER_WIFI_ERR_GET_INFO; | 152 | return IOTC_ADAPTER_WIFI_ERR_GET_INFO; |
| 113 | } | 153 | } |
| 114 | *netId = config.netId; | 154 | *netId = config.netId; |
| @@ -119,7 +159,9 @@ static int32_t GetWifiNetworkIdFromOhos(int32_t *netId) | |||
| 119 | static void ClearWifiConfig(void) | 159 | static void ClearWifiConfig(void) |
| 120 | { | 160 | { |
| 121 | int32_t netId = 0; | 161 | int32_t netId = 0; |
| 122 | - (void)GetWifiNetworkIdFromOhos(&netId); | 162 | + if (GetWifiNetworkIdFromOhos(&netId) != IOTC_OK) { |
| 163 | + return; | ||
| 164 | + } | ||
| 123 | 165 | ||
| 124 | if (RemoveDevice(netId) != WIFI_SUCCESS) { | 166 | if (RemoveDevice(netId) != WIFI_SUCCESS) { |
| 125 | IOTC_LOGW("remove wifi config error, netId [%d]", netId); | 167 | IOTC_LOGW("remove wifi config error, netId [%d]", netId); |
| @@ -187,7 +229,7 @@ int32_t IotcRestartWifi(void) | |||
| 187 | static void OnWifiScanStateChangedCallback(int32_t state, int32_t size) | 229 | static void OnWifiScanStateChangedCallback(int32_t state, int32_t size) |
| 188 | { | 230 | { |
| 189 | IOTC_LOGI("scan state change %d/%u", state, size); | 231 | IOTC_LOGI("scan state change %d/%u", state, size); |
| 190 | - g_isStaScanSuccess = true; | 232 | + g_isStaScanSuccess = (state == WIFI_STATE_AVAILABLE); |
| 191 | int32_t iotcState = state == WIFI_STATE_AVAILABLE ? IOTC_WIFI_EVENT_STATE_OK : | 233 | int32_t iotcState = state == WIFI_STATE_AVAILABLE ? IOTC_WIFI_EVENT_STATE_OK : |
| 192 | IOTC_WIFI_EVENT_STATE_ERROR; | 234 | IOTC_WIFI_EVENT_STATE_ERROR; |
| 193 | 235 | ||
| @@ -513,7 +555,8 @@ static int32_t CopyScanWifiResultList(IotcWifiList **scanList, const WifiScanInf | |||
| 513 | for (uint32_t i = 0; i < resSize; i++) { | 555 | for (uint32_t i = 0; i < resSize; i++) { |
| 514 | if ((strcpy_s(info[i].ssid, sizeof(info[i].ssid), result[i].ssid) != EOK) || | 556 | if ((strcpy_s(info[i].ssid, sizeof(info[i].ssid), result[i].ssid) != EOK) || |
| 515 | (memcpy_s(info[i].bssid, sizeof(info[i].bssid), result[i].bssid, sizeof(result[i].bssid)) != EOK)) { | 557 | (memcpy_s(info[i].bssid, sizeof(info[i].bssid), result[i].bssid, sizeof(result[i].bssid)) != EOK)) { |
| 516 | - IotcFree(info); | 558 | + IotcFree(*scanList); |
| 559 | + *scanList = NULL; | ||
| 517 | return IOTC_ERR_SECUREC_MEMCPY; | 560 | return IOTC_ERR_SECUREC_MEMCPY; |
| 518 | } | 561 | } |
| 519 | info[i].rssi = result[i].rssi; | 562 | info[i].rssi = result[i].rssi; |
| @@ -713,6 +756,7 @@ static void SetSecurityTypeByScanInfo(WifiDeviceConfig *config, const WifiScanIn | |||
| 713 | int32_t IotcConnectWifi(void) | 756 | int32_t IotcConnectWifi(void) |
| 714 | { | 757 | { |
| 715 | int32_t ret; | 758 | int32_t ret; |
| 759 | + bool foundTarget = false; | ||
| 716 | WifiDeviceConfig config; | 760 | WifiDeviceConfig config; |
| 717 | (void)memset_s(&config, sizeof(config), 0, sizeof(config)); | 761 | (void)memset_s(&config, sizeof(config), 0, sizeof(config)); |
| 718 | WifiScanInfo info; | 762 | WifiScanInfo info; |
| @@ -732,7 +776,6 @@ int32_t IotcConnectWifi(void) | |||
| 732 | IOTC_LOGW("get wifi config fail"); | 776 | IOTC_LOGW("get wifi config fail"); |
| 733 | return IOTC_ADAPTER_WIFI_ERR_GET_INFO; | 777 | return IOTC_ADAPTER_WIFI_ERR_GET_INFO; |
| 734 | } | 778 | } |
| 735 | - | ||
| 736 | if (config.ssid[0] == '\0') { | 779 | if (config.ssid[0] == '\0') { |
| 737 | IOTC_LOGW("ssid null"); | 780 | IOTC_LOGW("ssid null"); |
| 738 | return IOTC_ADAPTER_WIFI_ERR_GET_INFO; | 781 | return IOTC_ADAPTER_WIFI_ERR_GET_INFO; |
| @@ -740,15 +783,26 @@ int32_t IotcConnectWifi(void) | |||
| 740 | 783 | ||
| 741 | for (uint32_t i = 0; i < MAX_SCAN_TIMES; ++i) { | 784 | for (uint32_t i = 0; i < MAX_SCAN_TIMES; ++i) { |
| 742 | if (AdvanceScanWifiByOhos(&config) != IOTC_OK) { | 785 | if (AdvanceScanWifiByOhos(&config) != IOTC_OK) { |
| 743 | - IOTC_LOGW("advance scan wifi fail"); | 786 | + IOTC_LOGW("advance scan wifi fail, attempt %u/%u", i + 1, MAX_SCAN_TIMES); |
| 744 | - (void)memset_s(&config, sizeof(config), 0, sizeof(config)); | 787 | + if (i + 1 < MAX_SCAN_TIMES) { |
| 745 | - return IOTC_ADAPTER_WIFI_ERR_SCAN; | 788 | + IotcSleepMs(WIFI_SCAN_RETRY_INTERVAL_MS); |
| 789 | + } | ||
| 790 | + continue; | ||
| 746 | } | 791 | } |
| 747 | if (GetScanWifiResultFromOhos(&config, &info)) { | 792 | if (GetScanWifiResultFromOhos(&config, &info)) { |
| 748 | SetSecurityTypeByScanInfo(&config, &info); | 793 | SetSecurityTypeByScanInfo(&config, &info); |
| 794 | + foundTarget = true; | ||
| 749 | break; | 795 | break; |
| 750 | } | 796 | } |
| 751 | - IOTC_LOGN("not find target wifi, tyr again"); | 797 | + IOTC_LOGN("target wifi not found, attempt %u/%u", i + 1, MAX_SCAN_TIMES); |
| 798 | + if (i + 1 < MAX_SCAN_TIMES) { | ||
| 799 | + IotcSleepMs(WIFI_SCAN_RETRY_INTERVAL_MS); | ||
| 800 | + } | ||
| 801 | + } | ||
| 802 | + if (!foundTarget) { | ||
| 803 | + IOTC_LOGW("target wifi not found after %u attempts", MAX_SCAN_TIMES); | ||
| 804 | + (void)memset_s(&config, sizeof(config), 0, sizeof(config)); | ||
| 805 | + return IOTC_ADAPTER_WIFI_ERR_SCAN; | ||
| 752 | } | 806 | } |
| 753 | ret = ConnectTo(config.netId); | 807 | ret = ConnectTo(config.netId); |
| 754 | (void)memset_s(&config, sizeof(config), 0, sizeof(config)); | 808 | (void)memset_s(&config, sizeof(config), 0, sizeof(config)); |
| @@ -842,6 +896,17 @@ int32_t IotcDisconnectWifi(void) | |||
| 842 | return IOTC_OK; | 896 | return IOTC_OK; |
| 843 | } | 897 | } |
| 844 | 898 | ||
| 899 | +/* WS63 direct SoftAP uses the device base MAC as BSSID. */ | ||
| 900 | +extern int32_t IotcPlatformConfigureSoftApMac(bool preserveSta) __attribute__((weak)); | ||
| 901 | + | ||
| 902 | +/* | ||
| 903 | + * Optional WS63 hook for binary SSIDs. The OpenHarmony Lite HotspotConfig API | ||
| 904 | + * has no SSID length field, while Unified Connect 3.0 appends binary z/p | ||
| 905 | + * bytes and p may be 0x00. The hook consumes the explicit length atomically | ||
| 906 | + * when enabling the AP; platforms without it must reject embedded NUL bytes. | ||
| 907 | + */ | ||
| 908 | +extern int32_t IotcPlatformEnableSoftApWithSsidLen(uint8_t ssidLen) __attribute__((weak)); | ||
| 909 | + | ||
| 845 | static void CheckWifiStateBeforeStartSoftap(void) | 910 | static void CheckWifiStateBeforeStartSoftap(void) |
| 846 | { | 911 | { |
| 847 | int32_t ret; | 912 | int32_t ret; |
| @@ -862,31 +927,82 @@ static void CheckWifiStateBeforeStartSoftap(void) | |||
| 862 | } | 927 | } |
| 863 | } | 928 | } |
| 864 | 929 | ||
| 930 | + | ||
| 931 | +static struct netif *FindSoftapNetif(void) | ||
| 932 | +{ | ||
| 933 | + struct netif *netif = netifapi_netif_find_by_name(IOTC_SOFTAP_IFNAME_FIRST); | ||
| 934 | + if (netif == NULL) { | ||
| 935 | + netif = netifapi_netif_find_by_name(IOTC_SOFTAP_IFNAME_SECOND); | ||
| 936 | + } | ||
| 937 | + return netif; | ||
| 938 | +} | ||
| 939 | + | ||
| 940 | +static int32_t SetupSoftApIpAndDhcp(void) | ||
| 941 | +{ | ||
| 942 | + struct netif *netif = FindSoftapNetif(); | ||
| 943 | + if (netif == NULL) { | ||
| 944 | + IOTC_LOGW("find softap netif error"); | ||
| 945 | + return IOTC_ADAPTER_SOFTAP_ERR_START; | ||
| 946 | + } | ||
| 947 | + | ||
| 948 | + /* 先停后启:wifiservice 可能已在无地址状态下拉起 dhcps,租约池不可用 */ | ||
| 949 | + (void)netifapi_dhcps_stop(netif); | ||
| 950 | + | ||
| 951 | + ip4_addr_t ipaddr = {0}; | ||
| 952 | + ip4_addr_t netmask = {0}; | ||
| 953 | + ip4_addr_t gw = {0}; | ||
| 954 | + IP4_ADDR(&ipaddr, 192, 168, 43, 1); | ||
| 955 | + IP4_ADDR(&netmask, 255, 255, 255, 0); | ||
| 956 | + IP4_ADDR(&gw, 192, 168, 43, 1); | ||
| 957 | + if (netifapi_netif_set_addr(netif, &ipaddr, &netmask, &gw) != 0) { | ||
| 958 | + IOTC_LOGW("set softap addr error"); | ||
| 959 | + return IOTC_ADAPTER_SOFTAP_ERR_START; | ||
| 960 | + } | ||
| 961 | + | ||
| 962 | + err_t ret = netifapi_dhcps_start(netif, NULL, 0); | ||
| 963 | + if (ret != 0) { | ||
| 964 | + IOTC_LOGW("dhcps start error %d", ret); | ||
| 965 | + return IOTC_ADAPTER_SOFTAP_ERR_START; | ||
| 966 | + } | ||
| 967 | + IOTC_LOGI("softap dhcp server started"); | ||
| 968 | + return IOTC_OK; | ||
| 969 | +} | ||
| 970 | + | ||
| 971 | + | ||
| 865 | int32_t IotcStartSoftAp(const uint8_t *ssid, uint32_t ssidLen, const uint8_t *pwd, uint32_t pwdLen) | 972 | int32_t IotcStartSoftAp(const uint8_t *ssid, uint32_t ssidLen, const uint8_t *pwd, uint32_t pwdLen) |
| 866 | { | 973 | { |
| 867 | - (void)pwd; | 974 | + if (ssid == NULL || ssidLen == 0U || ssidLen >= IOTC_WIFI_SSID_MAX_LEN || |
| 868 | - (void)pwdLen; | 975 | + (pwd != NULL && (pwdLen < 8U || pwdLen >= IOTC_WIFI_PWD_MAX_LEN))) { |
| 869 | - if ((ssid == NULL) || (ssidLen == 0)) { | ||
| 870 | IOTC_LOGW("invalid param"); | 976 | IOTC_LOGW("invalid param"); |
| 871 | return IOTC_ERR_PARAM_INVALID; | 977 | return IOTC_ERR_PARAM_INVALID; |
| 872 | } | 978 | } |
| 873 | 979 | ||
| 874 | CheckWifiStateBeforeStartSoftap(); | 980 | CheckWifiStateBeforeStartSoftap(); |
| 875 | 981 | ||
| 982 | + int32_t ret = IOTC_OK; | ||
| 983 | + if (IotcPlatformConfigureSoftApMac != NULL) { | ||
| 984 | + ret = IotcPlatformConfigureSoftApMac(false); | ||
| 985 | + if (ret != IOTC_OK) { | ||
| 986 | + IOTC_LOGW("set softap mac fail %d", ret); | ||
| 987 | + return IOTC_ADAPTER_SOFTAP_ERR_START; | ||
| 988 | + } | ||
| 989 | + } | ||
| 990 | + | ||
| 876 | HotspotConfig config; | 991 | HotspotConfig config; |
| 877 | (void)memset_s(&config, sizeof(HotspotConfig), 0, sizeof(HotspotConfig)); | 992 | (void)memset_s(&config, sizeof(HotspotConfig), 0, sizeof(HotspotConfig)); |
| 878 | 993 | ||
| 879 | - int32_t ret = strncpy_s(config.ssid, sizeof(config.ssid), (const char *)ssid, ssidLen); | 994 | + ret = memcpy_s(config.ssid, sizeof(config.ssid), ssid, ssidLen); |
| 880 | if (ret != EOK) { | 995 | if (ret != EOK) { |
| 881 | - IOTC_LOGW("strcpy error %u", ssidLen); | 996 | + IOTC_LOGW("copy ssid error %u", ssidLen); |
| 882 | - return IOTC_ERR_SECUREC_STRCPY; | 997 | + return IOTC_ERR_SECUREC_MEMCPY; |
| 883 | } | 998 | } |
| 999 | + config.ssid[ssidLen] = '\0'; | ||
| 884 | 1000 | ||
| 885 | - if (pwd != NULL && pwdLen != 0) { | 1001 | + if (pwd != NULL && pwdLen != 0U) { |
| 886 | - ret = strncpy_s(config.preSharedKey, sizeof(config.preSharedKey), (const char *)pwd, pwdLen); | 1002 | + ret = memcpy_s(config.preSharedKey, sizeof(config.preSharedKey), pwd, pwdLen); |
| 887 | if (ret != EOK) { | 1003 | if (ret != EOK) { |
| 888 | - IOTC_LOGW("strcpy error %u", pwdLen); | 1004 | + IOTC_LOGW("copy password error %u", pwdLen); |
| 889 | - return IOTC_ERR_SECUREC_STRCPY; | 1005 | + return IOTC_ERR_SECUREC_MEMCPY; |
| 890 | } | 1006 | } |
| 891 | config.securityType = WIFI_SEC_TYPE_PSK; | 1007 | config.securityType = WIFI_SEC_TYPE_PSK; |
| 892 | } else { | 1008 | } else { |
| @@ -901,12 +1017,30 @@ int32_t IotcStartSoftAp(const uint8_t *ssid, uint32_t ssidLen, const uint8_t *pw | |||
| 901 | } | 1017 | } |
| 902 | 1018 | ||
| 903 | IOTC_LOGI("start softap"); | 1019 | IOTC_LOGI("start softap"); |
| 904 | - ret = EnableHotspot(); | 1020 | + if (IotcPlatformEnableSoftApWithSsidLen != NULL) { |
| 1021 | + ret = IotcPlatformEnableSoftApWithSsidLen((uint8_t)ssidLen); | ||
| 1022 | + } else { | ||
| 1023 | + /* A string-only platform cannot represent a NUL byte inside the SSID IE. */ | ||
| 1024 | + if (memchr(ssid, '\0', ssidLen) != NULL) { | ||
| 1025 | + IOTC_LOGW("binary ssid unsupported by platform"); | ||
| 1026 | + return IOTC_ADAPTER_SOFTAP_ERR_START; | ||
| 1027 | + } | ||
| 1028 | + ret = EnableHotspot(); | ||
| 1029 | + } | ||
| 905 | if (ret != WIFI_SUCCESS) { | 1030 | if (ret != WIFI_SUCCESS) { |
| 906 | IOTC_LOGW("enable hotspot fail %d", ret); | 1031 | IOTC_LOGW("enable hotspot fail %d", ret); |
| 907 | return IOTC_ADAPTER_SOFTAP_ERR_START; | 1032 | return IOTC_ADAPTER_SOFTAP_ERR_START; |
| 908 | } | 1033 | } |
| 909 | 1034 | ||
| 1035 | + | ||
| 1036 | + ret = SetupSoftApIpAndDhcp(); | ||
| 1037 | + if (ret != IOTC_OK) { | ||
| 1038 | + IOTC_LOGW("setup softap ip/dhcp fail %d", ret); | ||
| 1039 | + (void)DisableHotspot(); | ||
| 1040 | + return ret; | ||
| 1041 | + } | ||
| 1042 | + | ||
| 1043 | + | ||
| 910 | return IOTC_OK; | 1044 | return IOTC_OK; |
| 911 | } | 1045 | } |
| 912 | 1046 | ||
| @@ -927,6 +1061,73 @@ void IotcUnregEventCallback(void) | |||
| 927 | (void)memset_s(&g_adapterEventHandler, sizeof(IotcWifiEvent), 0, sizeof(IotcWifiEvent)); | 1061 | (void)memset_s(&g_adapterEventHandler, sizeof(IotcWifiEvent), 0, sizeof(IotcWifiEvent)); |
| 928 | } | 1062 | } |
| 929 | 1063 | ||
| 1064 | + | ||
| 1065 | +typedef struct { | ||
| 1066 | + IotcStationInfo *stations; | ||
| 1067 | + uint32_t stationNum; | ||
| 1068 | + uint32_t resolvedNum; | ||
| 1069 | +} SoftapArpResolveArg; | ||
| 1070 | + | ||
| 1071 | +/* | ||
| 1072 | + * WS63 的精简 lwIP 配置关闭了 LWIP_NETIFAPI_DHCPS_IP,GetStationList 只能返回 | ||
| 1073 | + * 已关联终端的 MAC,ipAddress 恒为 0。这里在 tcpip 线程中读取 ARP 表,并且仅在 | ||
| 1074 | + * ARP MAC 同时存在于驱动的已关联列表时回填 IP,避免仅凭来源 IP 接受未关联终端。 | ||
| 1075 | + */ | ||
| 1076 | +static err_t ResolveSoftapStationIpFromArp(struct netif *softapNetif, void *arg) | ||
| 1077 | +{ | ||
| 1078 | + SoftapArpResolveArg *resolveArg = (SoftapArpResolveArg *)arg; | ||
| 1079 | + if (softapNetif == NULL || resolveArg == NULL || resolveArg->stations == NULL) { | ||
| 1080 | + return ERR_VAL; | ||
| 1081 | + } | ||
| 1082 | + | ||
| 1083 | + for (size_t i = 0; i < ARP_TABLE_SIZE; ++i) { | ||
| 1084 | + ip4_addr_t *ipaddr = NULL; | ||
| 1085 | + struct netif *entryNetif = NULL; | ||
| 1086 | + struct eth_addr *ethaddr = NULL; | ||
| 1087 | + if (etharp_get_entry(i, &ipaddr, &entryNetif, ðaddr) == 0 || entryNetif != softapNetif || | ||
| 1088 | + ipaddr == NULL || ethaddr == NULL) { | ||
| 1089 | + continue; | ||
| 1090 | + } | ||
| 1091 | + | ||
| 1092 | + for (uint32_t j = 0; j < resolveArg->stationNum; ++j) { | ||
| 1093 | + IotcStationInfo *station = &resolveArg->stations[j]; | ||
| 1094 | + if (station->ip != 0 || memcmp(station->mac, ethaddr->addr, sizeof(station->mac)) != 0) { | ||
| 1095 | + continue; | ||
| 1096 | + } | ||
| 1097 | + station->ip = lwip_ntohl(ip4_addr_get_u32(ipaddr)); | ||
| 1098 | + ++resolveArg->resolvedNum; | ||
| 1099 | + break; | ||
| 1100 | + } | ||
| 1101 | + } | ||
| 1102 | + return ERR_OK; | ||
| 1103 | +} | ||
| 1104 | + | ||
| 1105 | +static void FillSoftapStationIpFromArp(IotcStationInfo *stations, uint32_t stationNum) | ||
| 1106 | +{ | ||
| 1107 | + if (stations == NULL || stationNum == 0) { | ||
| 1108 | + return; | ||
| 1109 | + } | ||
| 1110 | + | ||
| 1111 | + struct netif *netif = FindSoftapNetif(); | ||
| 1112 | + if (netif == NULL) { | ||
| 1113 | + IOTC_LOGW("find softap netif for arp error"); | ||
| 1114 | + return; | ||
| 1115 | + } | ||
| 1116 | + | ||
| 1117 | + SoftapArpResolveArg arg = { | ||
| 1118 | + .stations = stations, | ||
| 1119 | + .stationNum = stationNum, | ||
| 1120 | + .resolvedNum = 0, | ||
| 1121 | + }; | ||
| 1122 | + err_t ret = netifapi_netif_call_argcb(netif, ResolveSoftapStationIpFromArp, &arg); | ||
| 1123 | + if (ret != ERR_OK) { | ||
| 1124 | + IOTC_LOGW("resolve softap station ip from arp error %d", ret); | ||
| 1125 | + return; | ||
| 1126 | + } | ||
| 1127 | + IOTC_LOGD("resolved softap station ip from arp %u/%u", arg.resolvedNum, stationNum); | ||
| 1128 | +} | ||
| 1129 | + | ||
| 1130 | + | ||
| 930 | int32_t IotcGetSoftapStationInfo(IotcStationList **staList) | 1131 | int32_t IotcGetSoftapStationInfo(IotcStationList **staList) |
| 931 | { | 1132 | { |
| 932 | if (staList == NULL || *staList != NULL) { | 1133 | if (staList == NULL || *staList != NULL) { |
| @@ -958,6 +1159,9 @@ int32_t IotcGetSoftapStationInfo(IotcStationList **staList) | |||
| 958 | return IOTC_ERR_SECUREC_MEMCPY; | 1159 | return IOTC_ERR_SECUREC_MEMCPY; |
| 959 | } | 1160 | } |
| 960 | } | 1161 | } |
| 1162 | + | ||
| 1163 | + FillSoftapStationIpFromArp(staInfo, size); | ||
| 1164 | + | ||
| 961 | (*staList)->num = size; | 1165 | (*staList)->num = size; |
| 962 | return IOTC_OK; | 1166 | return IOTC_OK; |
| 963 | } | 1167 | } |
| @@ -1006,4 +1210,4 @@ int32_t IotcGetWifiMode(void) | |||
| 1006 | return IOTC_WIFI_MODE_STATION; | 1210 | return IOTC_WIFI_MODE_STATION; |
| 1007 | } | 1211 | } |
| 1008 | return IOTC_WIFI_MODE_INVALID; | 1212 | return IOTC_WIFI_MODE_INVALID; |
| 1009 | -} | 1213 | +} |
| @@ -29,6 +29,7 @@ | |||
| 29 | "iot_connect_ble_support", | 29 | "iot_connect_ble_support", |
| 30 | "iot_connect_sle_support", | 30 | "iot_connect_sle_support", |
| 31 | "iot_connect_wifi_support", | 31 | "iot_connect_wifi_support", |
| 32 | + "iot_connect_wifi_only_support", | ||
| 32 | "iot_connect_eth_support", | 33 | "iot_connect_eth_support", |
| 33 | "iot_connect_ailife_support", | 34 | "iot_connect_ailife_support", |
| 34 | "iot_connect_mbedtls_v2_support", | 35 | "iot_connect_mbedtls_v2_support", |
| @@ -37,10 +38,12 @@ | |||
| 37 | "iot_connect_databus_support", | 38 | "iot_connect_databus_support", |
| 38 | "iot_connect_ble_adv_update_support", | 39 | "iot_connect_ble_adv_update_support", |
| 39 | "iot_connect_oh_nearlink_support", | 40 | "iot_connect_oh_nearlink_support", |
| 41 | + "iot_connect_speke_secret_trace", | ||
| 40 | "iot_connect_ssl_backend", | 42 | "iot_connect_ssl_backend", |
| 41 | "iot_connect_openhitls_include_dirs", | 43 | "iot_connect_openhitls_include_dirs", |
| 42 | "iot_connect_openhitls_defines", | 44 | "iot_connect_openhitls_defines", |
| 43 | - "iot_connect_openhitls_deps" | 45 | + "iot_connect_openhitls_deps", |
| 46 | + "iot_connect_mutex_lockless" | ||
| 44 | ], | 47 | ], |
| 45 | "rom": "", | 48 | "rom": "", |
| 46 | "ram": "", | 49 | "ram": "", |
| @@ -22,6 +22,8 @@ set(IOTC_SRC_LIST | |||
| 22 | ${CMAKE_CURRENT_SOURCE_DIR}/config_register_info.c | 22 | ${CMAKE_CURRENT_SOURCE_DIR}/config_register_info.c |
| 23 | ${CMAKE_CURRENT_SOURCE_DIR}/config_revoke.c | 23 | ${CMAKE_CURRENT_SOURCE_DIR}/config_revoke.c |
| 24 | ${CMAKE_CURRENT_SOURCE_DIR}/config_revoke_flag.c | 24 | ${CMAKE_CURRENT_SOURCE_DIR}/config_revoke_flag.c |
| 25 | + ${CMAKE_CURRENT_SOURCE_DIR}/device_state_store.c | ||
| 26 | + ${CMAKE_CURRENT_SOURCE_DIR}/versioned_store.c | ||
| 25 | PARENT_SCOPE | 27 | PARENT_SCOPE |
| 26 | ) | 28 | ) |
| 27 | 29 | ||
| @@ -35,4 +37,4 @@ set(IOTC_TEST_INC_LIST | |||
| 35 | ${IOTC_TEST_INC_LIST} | 37 | ${IOTC_TEST_INC_LIST} |
| 36 | ${CMAKE_CURRENT_SOURCE_DIR} | 38 | ${CMAKE_CURRENT_SOURCE_DIR} |
| 37 | PARENT_SCOPE | 39 | PARENT_SCOPE |
| 38 | -) | 40 | +) |
| @@ -18,14 +18,47 @@ | |||
| 18 | 18 | ||
| 19 | 19 | ||
| 20 | 20 | ||
| 21 | + | ||
| 21 | 22 | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 22 | 27 | ||
| 23 | 28 | ||
| 24 | 29 | ||
| 25 | static void RevokeEventConfigCallback(uint32_t event, void *param, uint32_t len) | 30 | static void RevokeEventConfigCallback(uint32_t event, void *param, uint32_t len) |
| 26 | { | 31 | { |
| 32 | + (void)event; | ||
| 33 | + (void)param; | ||
| 34 | + (void)len; | ||
| 35 | + | ||
| 36 | + /* | ||
| 37 | + * MAIN_RESTORE is synchronous and the framework reboots immediately after | ||
| 38 | + * its subscribers return. Clear the pending HubSetup record and security | ||
| 39 | + * configuration before reboot rather than leaving a partially reset device. | ||
| 40 | + */ | ||
| 41 | + int32_t ret = IotcDssFactoryResetRun(); | ||
| 42 | + if (ret != IOTC_OK) { | ||
| 43 | + IOTC_LOGW("run WiFi Only factory reset error %d", ret); | ||
| 44 | + } | ||
| 45 | + ret = SecConfInfoFactoryReset(); | ||
| 46 | + if (ret != IOTC_OK) { | ||
| 47 | + IOTC_LOGW("clear security config error %d", ret); | ||
| 48 | + } | ||
| 49 | + ret = SecConfKeyCertFactoryReset(); | ||
| 50 | + if (ret != IOTC_OK) { | ||
| 51 | + IOTC_LOGW("clear key cert error %d", ret); | ||
| 52 | + } | ||
| 53 | + ret = ConfigClearLoginInfo(); | ||
| 54 | + if (ret != IOTC_OK) { | ||
| 55 | + IOTC_LOGW("clear login info error %d", ret); | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + int32_t ret; | ||
| 59 | + | ||
| 27 | /* After the cloud function is complete, add the factory reset flag */ | 60 | /* After the cloud function is complete, add the factory reset flag */ |
| 28 | - int32_t ret = ConfigClearRegisterInfo(); | 61 | + ret = ConfigClearRegisterInfo(); |
| 29 | if (ret != IOTC_OK) { | 62 | if (ret != IOTC_OK) { |
| 30 | IOTC_LOGW("clear register info error %d", ret); | 63 | IOTC_LOGW("clear register info error %d", ret); |
| 31 | } | 64 | } |
| @@ -53,4 +86,4 @@ int32_t ConfigRevokeInit(void) | |||
| 53 | void ConfigRevokeDeinit(void) | 86 | void ConfigRevokeDeinit(void) |
| 54 | { | 87 | { |
| 55 | (void)EventBusUnsubscribe(RevokeEventConfigCallback); | 88 | (void)EventBusUnsubscribe(RevokeEventConfigCallback); |
| 56 | -} | 89 | +} |
| @@ -0,0 +1,99 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | +static int32_t SaveRecord(const char *domain, const void *data, uint32_t len) | ||
| 25 | +{ | ||
| 26 | + int32_t ret = IotcVsBeginWrite(domain, IOTC_DSS_SCHEMA_VERSION, (const uint8_t *)data, len); | ||
| 27 | + CHECK_RETURN(ret == IOTC_OK, ret); | ||
| 28 | + return IotcVsCommit(domain); | ||
| 29 | +} | ||
| 30 | + | ||
| 31 | +static int32_t LoadRecord(const char *domain, void *data, uint32_t len) | ||
| 32 | +{ | ||
| 33 | + uint32_t actual = len; | ||
| 34 | + int32_t ret = IotcVsLoad(domain, IOTC_DSS_SCHEMA_VERSION, (uint8_t *)data, &actual); | ||
| 35 | + CHECK_RETURN(ret == IOTC_OK, ret); | ||
| 36 | + CHECK_RETURN(actual == len, IOTC_CORE_DEVICE_ERR_STORE_CORRUPT); | ||
| 37 | + return IOTC_OK; | ||
| 38 | +} | ||
| 39 | + | ||
| 40 | +static uint32_t BoundedLen(const char *value, uint32_t maxLen) | ||
| 41 | +{ | ||
| 42 | + /* 本树 securec 未提供 strnlen_s,按 bounded 语义本地实现。 */ | ||
| 43 | + uint32_t len = 0; | ||
| 44 | + while (len < maxLen && value[len] != '\0') { | ||
| 45 | + ++len; | ||
| 46 | + } | ||
| 47 | + return len; | ||
| 48 | +} | ||
| 49 | + | ||
| 50 | +static int32_t ValidatePending(const IotcDssProvisionPending *pending) | ||
| 51 | +{ | ||
| 52 | + CHECK_RETURN(pending != NULL, IOTC_ERR_PARAM_INVALID); | ||
| 53 | + CHECK_RETURN(pending->mdidLen > 0 && pending->mdidLen <= IOTC_DSS_MDID_MAX_LEN, IOTC_ERR_PARAM_INVALID); | ||
| 54 | + CHECK_RETURN(BoundedLen(pending->tempDevId, IOTC_DSS_DEV_ID_MAX_LEN + 1) <= IOTC_DSS_DEV_ID_MAX_LEN, | ||
| 55 | + IOTC_ERR_PARAM_INVALID); | ||
| 56 | + CHECK_RETURN(BoundedLen(pending->registerCode, IOTC_DSS_REGISTER_CODE_MAX_LEN + 1) <= | ||
| 57 | + IOTC_DSS_REGISTER_CODE_MAX_LEN, IOTC_ERR_PARAM_INVALID); | ||
| 58 | + CHECK_RETURN(pending->onlineNotifyDone <= 1, IOTC_ERR_PARAM_INVALID); | ||
| 59 | + CHECK_RETURN(pending->state == IOTC_DSS_PENDING_STATE_WAIT_ACTIVATE || | ||
| 60 | + pending->state == IOTC_DSS_PENDING_STATE_ACTIVATING, IOTC_ERR_PARAM_INVALID); | ||
| 61 | + return IOTC_OK; | ||
| 62 | +} | ||
| 63 | + | ||
| 64 | +int32_t IotcDssSaveProvisionPending(const IotcDssProvisionPending *pending) | ||
| 65 | +{ | ||
| 66 | + int32_t ret = ValidatePending(pending); | ||
| 67 | + CHECK_RETURN(ret == IOTC_OK, ret); | ||
| 68 | + return SaveRecord(IOTC_DSS_DOMAIN_PROVISION_PENDING, pending, sizeof(*pending)); | ||
| 69 | +} | ||
| 70 | + | ||
| 71 | +int32_t IotcDssLoadProvisionPending(IotcDssProvisionPending *pending) | ||
| 72 | +{ | ||
| 73 | + CHECK_RETURN(pending != NULL, IOTC_ERR_PARAM_INVALID); | ||
| 74 | + int32_t ret = LoadRecord(IOTC_DSS_DOMAIN_PROVISION_PENDING, pending, sizeof(*pending)); | ||
| 75 | + CHECK_RETURN(ret == IOTC_OK, ret); | ||
| 76 | + return ValidatePending(pending); | ||
| 77 | +} | ||
| 78 | + | ||
| 79 | +int32_t IotcDssEraseProvisionPending(void) | ||
| 80 | +{ | ||
| 81 | + return IotcVsErase(IOTC_DSS_DOMAIN_PROVISION_PENDING); | ||
| 82 | +} | ||
| 83 | + | ||
| 84 | +int32_t IotcDssMarkOnlineNotifyDone(void) | ||
| 85 | +{ | ||
| 86 | + IotcDssProvisionPending pending = {0}; | ||
| 87 | + int32_t ret = IotcDssLoadProvisionPending(&pending); | ||
| 88 | + if (ret == IOTC_OK && pending.onlineNotifyDone == 0) { | ||
| 89 | + pending.onlineNotifyDone = 1; | ||
| 90 | + ret = IotcDssSaveProvisionPending(&pending); | ||
| 91 | + } | ||
| 92 | + (void)memset_s(&pending, sizeof(pending), 0, sizeof(pending)); | ||
| 93 | + return ret; | ||
| 94 | +} | ||
| 95 | + | ||
| 96 | +int32_t IotcDssFactoryResetRun(void) | ||
| 97 | +{ | ||
| 98 | + return IotcVsErase(IOTC_DSS_DOMAIN_PROVISION_PENDING); | ||
| 99 | +} | ||
| @@ -0,0 +1,79 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | +extern "C" { | ||
| 25 | + | ||
| 26 | + | ||
| 27 | +/* | ||
| 28 | + * 统一互联 3.0 WiFi Only 配网待完成状态持久化。基于 versioned_store | ||
| 29 | + * 双槽机制,记录带 schema 版本、generation、长度和完整性校验。 | ||
| 30 | + * | ||
| 31 | + * schema v2 保存 HubSetup 的原始 MDID、临时 devId、注册码、请求摘要和 | ||
| 32 | + * /onlinenotify 完成标记;完成标记复用旧结构的第一个保留字节,保持已落盘 | ||
| 33 | + * v2 记录的长度和字段偏移不变。 | ||
| 34 | + * Wi-Fi 密码只参与请求摘要,不写入本模块的双槽记录。 | ||
| 35 | + * | ||
| 36 | + * Wi-Fi 配置交给 WS63 Wi-Fi 配置存储,安全凭据只经 security service, | ||
| 37 | + * 本模块不直接读写。 | ||
| 38 | + */ | ||
| 39 | + | ||
| 40 | + | ||
| 41 | + | ||
| 42 | + | ||
| 43 | + | ||
| 44 | + | ||
| 45 | + | ||
| 46 | + | ||
| 47 | + | ||
| 48 | +typedef enum { | ||
| 49 | + IOTC_DSS_PENDING_STATE_WAIT_ACTIVATE = 0, | ||
| 50 | + IOTC_DSS_PENDING_STATE_ACTIVATING, | ||
| 51 | +} IotcDssPendingState; | ||
| 52 | + | ||
| 53 | +typedef struct { | ||
| 54 | + uint8_t mdid[IOTC_DSS_MDID_MAX_LEN]; | ||
| 55 | + uint8_t mdidLen; | ||
| 56 | + uint8_t onlineNotifyDone; | ||
| 57 | + uint8_t reserved[2]; | ||
| 58 | + char tempDevId[IOTC_DSS_DEV_ID_MAX_LEN + 1]; | ||
| 59 | + char registerCode[IOTC_DSS_REGISTER_CODE_MAX_LEN + 1]; | ||
| 60 | + uint8_t requestHash[32]; | ||
| 61 | + uint32_t state; | ||
| 62 | +} IotcDssProvisionPending; | ||
| 63 | + | ||
| 64 | +/* Save 为 BeginWrite+Commit 的便利封装。 */ | ||
| 65 | +int32_t IotcDssSaveProvisionPending(const IotcDssProvisionPending *pending); | ||
| 66 | +int32_t IotcDssLoadProvisionPending(IotcDssProvisionPending *pending); | ||
| 67 | +int32_t IotcDssEraseProvisionPending(void); | ||
| 68 | + | ||
| 69 | +/* 五次 onlinenotify 全部交给 socket 后原子标记;断电只会看到未完成或完整完成。 */ | ||
| 70 | +int32_t IotcDssMarkOnlineNotifyDone(void); | ||
| 71 | + | ||
| 72 | +/* 恢复出厂时清除本模块保存的配网待完成记录。 */ | ||
| 73 | +int32_t IotcDssFactoryResetRun(void); | ||
| 74 | + | ||
| 75 | + | ||
| 76 | +} | ||
| 77 | + | ||
| 78 | + | ||
| 79 | + | ||
| @@ -0,0 +1,67 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | +extern "C" { | ||
| 23 | + | ||
| 24 | + | ||
| 25 | +/* | ||
| 26 | + * 双槽版本化记录存储(开发计划 7.2 事务提交规则、W4 开发内容 2/3/6/7)。 | ||
| 27 | + * | ||
| 28 | + * 每个域(domain)占用两个 KV 槽位。记录头包含 magic、头部版本、payload | ||
| 29 | + * schema 版本、代际 generation、提交状态、长度和 CRC32。写入流程: | ||
| 30 | + * IotcVsBeginWrite(写 pending 新槽并回读校验)-> | ||
| 31 | + * IotcVsCommit(标记 committed、回读校验、最后擦旧槽) | ||
| 32 | + * 或 IotcVsAbort(擦新槽,保留旧值)。 | ||
| 33 | + * | ||
| 34 | + * Load 只在 committed 记录中取 generation 较高者,pending 记录不可见。 | ||
| 35 | + * 因此 BeginWrite 与外部动作(例如保存 Wi-Fi)之间断电仍加载旧值;Commit | ||
| 36 | + * 标记撕裂时旧 committed 槽仍在,标记成功后即使擦旧槽失败也加载完整新值。 | ||
| 37 | + * | ||
| 38 | + * schema 版本不兼容的记录返回 IOTC_CORE_DEVICE_ERR_STORE_SCHEMA,由调用方 | ||
| 39 | + * 决定升级迁移或 IotcVsErase 后重建;本模块不静默吞掉不兼容数据。 | ||
| 40 | + */ | ||
| 41 | + | ||
| 42 | + | ||
| 43 | + | ||
| 44 | + | ||
| 45 | +/* 加载域内有效记录。bufLen 入参为缓冲区容量,出参为实际长度。 */ | ||
| 46 | +int32_t IotcVsLoad(const char *domain, uint16_t expectedSchema, uint8_t *buf, uint32_t *bufLen); | ||
| 47 | + | ||
| 48 | +/* 向非 active 槽写入 generation+1 的 pending 记录并回读校验;此时 Load 仍返回旧值。 */ | ||
| 49 | +int32_t IotcVsBeginWrite(const char *domain, uint16_t schemaVersion, const uint8_t *data, uint32_t len); | ||
| 50 | + | ||
| 51 | +/* 提交:先把 pending 标记为 committed 并校验,再擦除旧槽。 */ | ||
| 52 | +int32_t IotcVsCommit(const char *domain); | ||
| 53 | + | ||
| 54 | +/* 回滚:擦除 BeginWrite 写入的新槽,保留旧记录。 */ | ||
| 55 | +int32_t IotcVsAbort(const char *domain); | ||
| 56 | + | ||
| 57 | +/* 擦除域内两个槽位(恢复出厂/不兼容重建使用)。 */ | ||
| 58 | +int32_t IotcVsErase(const char *domain); | ||
| 59 | + | ||
| 60 | +/* 查询当前有效记录的 generation;无有效记录返回 IOTC_CORE_DEVICE_ERR_STORE_NOT_FOUND。 */ | ||
| 61 | +int32_t IotcVsGetGeneration(const char *domain, uint32_t *generation); | ||
| 62 | + | ||
| 63 | + | ||
| 64 | +} | ||
| 65 | + | ||
| 66 | + | ||
| 67 | + | ||
| @@ -0,0 +1,382 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + | ||
| 35 | + | ||
| 36 | +typedef struct { | ||
| 37 | + uint32_t magic; | ||
| 38 | + uint16_t headerVersion; | ||
| 39 | + uint16_t schemaVersion; | ||
| 40 | + uint32_t generation; | ||
| 41 | + uint32_t payloadLen; | ||
| 42 | + uint32_t state; | ||
| 43 | + uint32_t crc32; | ||
| 44 | +} IotcVsHeader; | ||
| 45 | + | ||
| 46 | +static uint32_t Crc32Update(uint32_t crc, const uint8_t *data, uint32_t len) | ||
| 47 | +{ | ||
| 48 | + crc = ~crc; | ||
| 49 | + for (uint32_t i = 0; i < len; ++i) { | ||
| 50 | + crc ^= data[i]; | ||
| 51 | + for (uint32_t bit = 0; bit < 8; ++bit) { | ||
| 52 | + crc = (crc >> 1) ^ ((crc & 1U) ? 0xEDB88320U : 0U); | ||
| 53 | + } | ||
| 54 | + } | ||
| 55 | + return ~crc; | ||
| 56 | +} | ||
| 57 | + | ||
| 58 | +static uint32_t ComputeRecordCrc(const IotcVsHeader *header, const uint8_t *payload) | ||
| 59 | +{ | ||
| 60 | + /* 覆盖 magic 之后、CRC 字段之前的头字段和整个 payload。 */ | ||
| 61 | + uint32_t crc = Crc32Update(0, (const uint8_t *)header + sizeof(uint32_t), | ||
| 62 | + sizeof(IotcVsHeader) - sizeof(uint32_t) - sizeof(uint32_t)); | ||
| 63 | + return Crc32Update(crc, payload, header->payloadLen); | ||
| 64 | +} | ||
| 65 | + | ||
| 66 | +static int32_t ValidateDomainName(const char *domain) | ||
| 67 | +{ | ||
| 68 | + CHECK_RETURN(domain != NULL, IOTC_ERR_PARAM_INVALID); | ||
| 69 | + uint32_t domainLen = 0; | ||
| 70 | + while (domainLen <= IOTC_VS_MAX_DOMAIN_LEN && domain[domainLen] != '\0') { | ||
| 71 | + char c = domain[domainLen]; | ||
| 72 | + bool valid = (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c == '_'); | ||
| 73 | + CHECK_RETURN(valid, IOTC_ERR_PARAM_INVALID); | ||
| 74 | + ++domainLen; | ||
| 75 | + } | ||
| 76 | + CHECK_RETURN(domainLen > 0 && domainLen <= IOTC_VS_MAX_DOMAIN_LEN && domain[domainLen] == '\0', | ||
| 77 | + IOTC_ERR_PARAM_INVALID); | ||
| 78 | + return IOTC_OK; | ||
| 79 | +} | ||
| 80 | + | ||
| 81 | +static int32_t BuildSlotKey(const char *domain, uint32_t slot, char *key, uint32_t keyLen) | ||
| 82 | +{ | ||
| 83 | + CHECK_RETURN(key != NULL && slot < IOTC_VS_SLOT_COUNT, IOTC_ERR_PARAM_INVALID); | ||
| 84 | + int32_t ret = ValidateDomainName(domain); | ||
| 85 | + CHECK_RETURN(ret == IOTC_OK, ret); | ||
| 86 | + | ||
| 87 | + int written = snprintf_s(key, keyLen, keyLen - 1, "%s%s_%c", IOTC_VS_KEY_PREFIX, domain, | ||
| 88 | + (slot == 0) ? 'a' : 'b'); | ||
| 89 | + CHECK_RETURN(written > 0, IOTC_ERR_SECUREC_SPRINTF); | ||
| 90 | + return IOTC_OK; | ||
| 91 | +} | ||
| 92 | + | ||
| 93 | +/* 读取并校验一个槽位;有效返回 IOTC_OK 并填充 header/payload。 */ | ||
| 94 | +static int32_t ReadSlot(const char *domain, uint32_t slot, IotcVsHeader *header, uint8_t *payload, | ||
| 95 | + uint32_t payloadCap) | ||
| 96 | +{ | ||
| 97 | + char key[IOTC_VS_KEY_BUF_LEN] = {0}; | ||
| 98 | + int32_t ret = BuildSlotKey(domain, slot, key, sizeof(key)); | ||
| 99 | + CHECK_RETURN(ret == IOTC_OK, ret); | ||
| 100 | + | ||
| 101 | + uint32_t valueLen = 0; | ||
| 102 | + ret = IotcKvGetLen(key, &valueLen); | ||
| 103 | + CHECK_RETURN(ret == IOTC_OK, IOTC_CORE_DEVICE_ERR_STORE_NOT_FOUND); | ||
| 104 | + if (valueLen < sizeof(IotcVsHeader) || valueLen > IOTC_VS_RECORD_BUF_LEN) { | ||
| 105 | + return IOTC_CORE_DEVICE_ERR_STORE_CORRUPT; | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + uint8_t record[IOTC_VS_RECORD_BUF_LEN] = {0}; | ||
| 109 | + uint32_t readLen = valueLen; | ||
| 110 | + ret = IotcKvGetValue(key, record, &readLen); | ||
| 111 | + if (ret != IOTC_OK || readLen != valueLen) { | ||
| 112 | + return IOTC_CORE_DEVICE_ERR_STORE_CORRUPT; | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + IotcVsHeader tmp = {0}; | ||
| 116 | + if (memcpy_s(&tmp, sizeof(tmp), record, sizeof(tmp)) != EOK) { | ||
| 117 | + return IOTC_CORE_DEVICE_ERR_STORE_CORRUPT; | ||
| 118 | + } | ||
| 119 | + if (tmp.magic != IOTC_VS_MAGIC || tmp.headerVersion != IOTC_VS_HEADER_VERSION || | ||
| 120 | + (tmp.state != IOTC_VS_STATE_PENDING && tmp.state != IOTC_VS_STATE_COMMITTED) || | ||
| 121 | + tmp.payloadLen > IOTC_VS_MAX_PAYLOAD_LEN || | ||
| 122 | + valueLen != sizeof(IotcVsHeader) + tmp.payloadLen) { | ||
| 123 | + return IOTC_CORE_DEVICE_ERR_STORE_CORRUPT; | ||
| 124 | + } | ||
| 125 | + if (payload != NULL && tmp.payloadLen > payloadCap) { | ||
| 126 | + return IOTC_CORE_DEVICE_ERR_STORE_CORRUPT; | ||
| 127 | + } | ||
| 128 | + if (payload != NULL && memcpy_s(payload, payloadCap, record + sizeof(IotcVsHeader), tmp.payloadLen) != EOK) { | ||
| 129 | + return IOTC_CORE_DEVICE_ERR_STORE_CORRUPT; | ||
| 130 | + } | ||
| 131 | + if (ComputeRecordCrc(&tmp, (payload != NULL) ? payload : record + sizeof(IotcVsHeader)) != tmp.crc32) { | ||
| 132 | + return IOTC_CORE_DEVICE_ERR_STORE_CORRUPT; | ||
| 133 | + } | ||
| 134 | + if (header != NULL && memcpy_s(header, sizeof(*header), &tmp, sizeof(tmp)) != EOK) { | ||
| 135 | + return IOTC_CORE_DEVICE_ERR_STORE_CORRUPT; | ||
| 136 | + } | ||
| 137 | + return IOTC_OK; | ||
| 138 | +} | ||
| 139 | + | ||
| 140 | +/* 返回两槽中已提交记录的最高 generation;pending 记录绝不能对 Load 可见。 */ | ||
| 141 | +static int32_t FindActive(const char *domain, IotcVsHeader *header, uint8_t *payload, uint32_t payloadCap, | ||
| 142 | + uint32_t *slotOut) | ||
| 143 | +{ | ||
| 144 | + IotcVsHeader headers[IOTC_VS_SLOT_COUNT] = {0}; | ||
| 145 | + int32_t rets[IOTC_VS_SLOT_COUNT] = {0}; | ||
| 146 | + for (uint32_t slot = 0; slot < IOTC_VS_SLOT_COUNT; ++slot) { | ||
| 147 | + bool wantPayload = (payload != NULL); | ||
| 148 | + rets[slot] = ReadSlot(domain, slot, &headers[slot], | ||
| 149 | + wantPayload ? payload + (uint32_t)slot * payloadCap : NULL, payloadCap); | ||
| 150 | + } | ||
| 151 | + | ||
| 152 | + int best = -1; | ||
| 153 | + for (uint32_t slot = 0; slot < IOTC_VS_SLOT_COUNT; ++slot) { | ||
| 154 | + if (rets[slot] == IOTC_OK && headers[slot].state == IOTC_VS_STATE_COMMITTED && | ||
| 155 | + (best < 0 || headers[slot].generation > headers[best].generation)) { | ||
| 156 | + best = (int)slot; | ||
| 157 | + } | ||
| 158 | + } | ||
| 159 | + CHECK_RETURN(best >= 0, IOTC_CORE_DEVICE_ERR_STORE_NOT_FOUND); | ||
| 160 | + | ||
| 161 | + if (header != NULL && memcpy_s(header, sizeof(*header), &headers[best], sizeof(IotcVsHeader)) != EOK) { | ||
| 162 | + return IOTC_CORE_DEVICE_ERR_STORE_CORRUPT; | ||
| 163 | + } | ||
| 164 | + if (payload != NULL && payloadCap > 0 && | ||
| 165 | + memmove_s(payload, payloadCap, payload + (uint32_t)best * payloadCap, headers[best].payloadLen) != EOK) { | ||
| 166 | + return IOTC_CORE_DEVICE_ERR_STORE_CORRUPT; | ||
| 167 | + } | ||
| 168 | + if (slotOut != NULL) { | ||
| 169 | + *slotOut = (uint32_t)best; | ||
| 170 | + } | ||
| 171 | + return IOTC_OK; | ||
| 172 | +} | ||
| 173 | + | ||
| 174 | +static int32_t EraseSlot(const char *domain, uint32_t slot) | ||
| 175 | +{ | ||
| 176 | + char key[IOTC_VS_KEY_BUF_LEN] = {0}; | ||
| 177 | + int32_t ret = BuildSlotKey(domain, slot, key, sizeof(key)); | ||
| 178 | + CHECK_RETURN(ret == IOTC_OK, ret); | ||
| 179 | + uint32_t valueLen = 0; | ||
| 180 | + if (IotcKvGetLen(key, &valueLen) != IOTC_OK) { | ||
| 181 | + return IOTC_OK; /* key 不存在视为已擦除,保证恢复出厂幂等 */ | ||
| 182 | + } | ||
| 183 | + if (valueLen == 0) { | ||
| 184 | + return IOTC_OK; | ||
| 185 | + } | ||
| 186 | + ret = IotcKvDelValue(key); | ||
| 187 | + return (ret == IOTC_OK) ? IOTC_OK : IOTC_CORE_DEVICE_ERR_STORE_KV; | ||
| 188 | +} | ||
| 189 | + | ||
| 190 | +int32_t IotcVsLoad(const char *domain, uint16_t expectedSchema, uint8_t *buf, uint32_t *bufLen) | ||
| 191 | +{ | ||
| 192 | + CHECK_RETURN(buf != NULL && bufLen != NULL, IOTC_ERR_PARAM_INVALID); | ||
| 193 | + int32_t domainRet = ValidateDomainName(domain); | ||
| 194 | + CHECK_RETURN(domainRet == IOTC_OK, domainRet); | ||
| 195 | + uint32_t cap = *bufLen; | ||
| 196 | + | ||
| 197 | + IotcVsHeader header = {0}; | ||
| 198 | + uint8_t payload[IOTC_VS_SLOT_COUNT * IOTC_VS_MAX_PAYLOAD_LEN] = {0}; | ||
| 199 | + int32_t ret = FindActive(domain, &header, payload, IOTC_VS_MAX_PAYLOAD_LEN, NULL); | ||
| 200 | + CHECK_RETURN(ret == IOTC_OK, ret); | ||
| 201 | + CHECK_RETURN(header.payloadLen <= cap, IOTC_ERR_PARAM_INVALID); | ||
| 202 | + if (header.schemaVersion != expectedSchema) { | ||
| 203 | + IOTC_LOGW("schema mismatch domain=%s have=%u want=%u", domain, header.schemaVersion, expectedSchema); | ||
| 204 | + return IOTC_CORE_DEVICE_ERR_STORE_SCHEMA; | ||
| 205 | + } | ||
| 206 | + if (memcpy_s(buf, cap, payload, header.payloadLen) != EOK) { | ||
| 207 | + return IOTC_CORE_DEVICE_ERR_STORE_CORRUPT; | ||
| 208 | + } | ||
| 209 | + *bufLen = header.payloadLen; | ||
| 210 | + return IOTC_OK; | ||
| 211 | +} | ||
| 212 | + | ||
| 213 | +int32_t IotcVsBeginWrite(const char *domain, uint16_t schemaVersion, const uint8_t *data, uint32_t len) | ||
| 214 | +{ | ||
| 215 | + CHECK_RETURN(data != NULL, IOTC_ERR_PARAM_INVALID); | ||
| 216 | + int32_t domainRet = ValidateDomainName(domain); | ||
| 217 | + CHECK_RETURN(domainRet == IOTC_OK, domainRet); | ||
| 218 | + CHECK_RETURN(len > 0 && len <= IOTC_VS_MAX_PAYLOAD_LEN, IOTC_ERR_PARAM_INVALID); | ||
| 219 | + | ||
| 220 | + IotcVsHeader active = {0}; | ||
| 221 | + uint32_t activeSlot = 0; | ||
| 222 | + int32_t ret = FindActive(domain, &active, NULL, 0, &activeSlot); | ||
| 223 | + uint32_t newGeneration = 1; | ||
| 224 | + uint32_t targetSlot = 0; | ||
| 225 | + if (ret == IOTC_OK) { | ||
| 226 | + newGeneration = active.generation + 1U; | ||
| 227 | + targetSlot = (activeSlot == 0) ? 1U : 0U; | ||
| 228 | + } else if (ret == IOTC_CORE_DEVICE_ERR_STORE_NOT_FOUND) { | ||
| 229 | + newGeneration = 1; | ||
| 230 | + targetSlot = 0; | ||
| 231 | + } else { | ||
| 232 | + return ret; | ||
| 233 | + } | ||
| 234 | + CHECK_RETURN(newGeneration != 0, IOTC_CORE_DEVICE_ERR_STORE_STATE); /* generation 回绕拒绝 */ | ||
| 235 | + | ||
| 236 | + IotcVsHeader header = {0}; | ||
| 237 | + header.magic = IOTC_VS_MAGIC; | ||
| 238 | + header.headerVersion = IOTC_VS_HEADER_VERSION; | ||
| 239 | + header.schemaVersion = schemaVersion; | ||
| 240 | + header.generation = newGeneration; | ||
| 241 | + header.payloadLen = len; | ||
| 242 | + header.state = IOTC_VS_STATE_PENDING; | ||
| 243 | + header.crc32 = ComputeRecordCrc(&header, data); | ||
| 244 | + | ||
| 245 | + uint8_t record[IOTC_VS_RECORD_BUF_LEN] = {0}; | ||
| 246 | + if (memcpy_s(record, sizeof(record), &header, sizeof(header)) != EOK || | ||
| 247 | + memcpy_s(record + sizeof(header), sizeof(record) - sizeof(header), data, len) != EOK) { | ||
| 248 | + return IOTC_CORE_DEVICE_ERR_STORE_CORRUPT; | ||
| 249 | + } | ||
| 250 | + | ||
| 251 | + char key[IOTC_VS_KEY_BUF_LEN] = {0}; | ||
| 252 | + ret = BuildSlotKey(domain, targetSlot, key, sizeof(key)); | ||
| 253 | + CHECK_RETURN(ret == IOTC_OK, ret); | ||
| 254 | + ret = IotcKvSetValue(key, record, (uint32_t)sizeof(header) + len); | ||
| 255 | + CHECK_RETURN(ret == IOTC_OK, IOTC_CORE_DEVICE_ERR_STORE_KV); | ||
| 256 | + | ||
| 257 | + /* 回读校验:失败则擦除新槽,保留旧值。 */ | ||
| 258 | + IotcVsHeader verify = {0}; | ||
| 259 | + uint8_t verifyPayload[IOTC_VS_MAX_PAYLOAD_LEN] = {0}; | ||
| 260 | + ret = ReadSlot(domain, targetSlot, &verify, verifyPayload, sizeof(verifyPayload)); | ||
| 261 | + if (ret != IOTC_OK || verify.generation != newGeneration || verify.schemaVersion != schemaVersion || | ||
| 262 | + verify.payloadLen != len || verify.state != IOTC_VS_STATE_PENDING || | ||
| 263 | + memcmp(verifyPayload, data, len) != 0) { | ||
| 264 | + (void)EraseSlot(domain, targetSlot); | ||
| 265 | + return IOTC_CORE_DEVICE_ERR_STORE_CORRUPT; | ||
| 266 | + } | ||
| 267 | + return IOTC_OK; | ||
| 268 | +} | ||
| 269 | + | ||
| 270 | +/* 读取两槽的原始状态;valid[i] 标记槽位是否有完整有效记录。 */ | ||
| 271 | +static void ReadBothSlots(const char *domain, IotcVsHeader headers[IOTC_VS_SLOT_COUNT], | ||
| 272 | + bool valid[IOTC_VS_SLOT_COUNT]) | ||
| 273 | +{ | ||
| 274 | + for (uint32_t slot = 0; slot < IOTC_VS_SLOT_COUNT; ++slot) { | ||
| 275 | + valid[slot] = (ReadSlot(domain, slot, &headers[slot], NULL, 0) == IOTC_OK); | ||
| 276 | + } | ||
| 277 | +} | ||
| 278 | + | ||
| 279 | +int32_t IotcVsCommit(const char *domain) | ||
| 280 | +{ | ||
| 281 | + int32_t domainRet = ValidateDomainName(domain); | ||
| 282 | + CHECK_RETURN(domainRet == IOTC_OK, domainRet); | ||
| 283 | + IotcVsHeader headers[IOTC_VS_SLOT_COUNT] = {0}; | ||
| 284 | + bool valid[IOTC_VS_SLOT_COUNT] = {false}; | ||
| 285 | + ReadBothSlots(domain, headers, valid); | ||
| 286 | + | ||
| 287 | + int pendingSlot = -1; | ||
| 288 | + int activeSlot = -1; | ||
| 289 | + for (uint32_t slot = 0; slot < IOTC_VS_SLOT_COUNT; ++slot) { | ||
| 290 | + if (!valid[slot]) { | ||
| 291 | + continue; | ||
| 292 | + } | ||
| 293 | + if (headers[slot].state == IOTC_VS_STATE_PENDING && | ||
| 294 | + (pendingSlot < 0 || headers[slot].generation > headers[pendingSlot].generation)) { | ||
| 295 | + pendingSlot = (int)slot; | ||
| 296 | + } else if (headers[slot].state == IOTC_VS_STATE_COMMITTED && | ||
| 297 | + (activeSlot < 0 || headers[slot].generation > headers[activeSlot].generation)) { | ||
| 298 | + activeSlot = (int)slot; | ||
| 299 | + } | ||
| 300 | + } | ||
| 301 | + if (pendingSlot < 0) { | ||
| 302 | + /* 已提交记录存在时重复 Commit 幂等;全空/全损坏则报告未找到。 */ | ||
| 303 | + return (activeSlot >= 0) ? IOTC_OK : IOTC_CORE_DEVICE_ERR_STORE_NOT_FOUND; | ||
| 304 | + } | ||
| 305 | + if (activeSlot >= 0) { | ||
| 306 | + CHECK_RETURN(headers[pendingSlot].generation > headers[activeSlot].generation, | ||
| 307 | + IOTC_CORE_DEVICE_ERR_STORE_STATE); | ||
| 308 | + } | ||
| 309 | + | ||
| 310 | + uint8_t payload[IOTC_VS_MAX_PAYLOAD_LEN] = {0}; | ||
| 311 | + IotcVsHeader pending = {0}; | ||
| 312 | + int32_t ret = ReadSlot(domain, (uint32_t)pendingSlot, &pending, payload, sizeof(payload)); | ||
| 313 | + CHECK_RETURN(ret == IOTC_OK && pending.state == IOTC_VS_STATE_PENDING, | ||
| 314 | + IOTC_CORE_DEVICE_ERR_STORE_CORRUPT); | ||
| 315 | + | ||
| 316 | + /* 先把新槽改为 committed 并回读确认,再擦旧槽。任何一步断电,Load 只能看到旧值或完整新值。 */ | ||
| 317 | + pending.state = IOTC_VS_STATE_COMMITTED; | ||
| 318 | + pending.crc32 = ComputeRecordCrc(&pending, payload); | ||
| 319 | + uint8_t record[IOTC_VS_RECORD_BUF_LEN] = {0}; | ||
| 320 | + if (memcpy_s(record, sizeof(record), &pending, sizeof(pending)) != EOK || | ||
| 321 | + memcpy_s(record + sizeof(pending), sizeof(record) - sizeof(pending), payload, pending.payloadLen) != EOK) { | ||
| 322 | + return IOTC_CORE_DEVICE_ERR_STORE_CORRUPT; | ||
| 323 | + } | ||
| 324 | + char key[IOTC_VS_KEY_BUF_LEN] = {0}; | ||
| 325 | + ret = BuildSlotKey(domain, (uint32_t)pendingSlot, key, sizeof(key)); | ||
| 326 | + CHECK_RETURN(ret == IOTC_OK, ret); | ||
| 327 | + ret = IotcKvSetValue(key, record, (uint32_t)sizeof(pending) + pending.payloadLen); | ||
| 328 | + CHECK_RETURN(ret == IOTC_OK, IOTC_CORE_DEVICE_ERR_STORE_KV); | ||
| 329 | + | ||
| 330 | + IotcVsHeader verify = {0}; | ||
| 331 | + ret = ReadSlot(domain, (uint32_t)pendingSlot, &verify, NULL, 0); | ||
| 332 | + CHECK_RETURN(ret == IOTC_OK && verify.state == IOTC_VS_STATE_COMMITTED && | ||
| 333 | + verify.generation == pending.generation, IOTC_CORE_DEVICE_ERR_STORE_CORRUPT); | ||
| 334 | + | ||
| 335 | + return (activeSlot >= 0) ? EraseSlot(domain, (uint32_t)activeSlot) : IOTC_OK; | ||
| 336 | +} | ||
| 337 | + | ||
| 338 | +int32_t IotcVsAbort(const char *domain) | ||
| 339 | +{ | ||
| 340 | + int32_t domainRet = ValidateDomainName(domain); | ||
| 341 | + CHECK_RETURN(domainRet == IOTC_OK, domainRet); | ||
| 342 | + IotcVsHeader headers[IOTC_VS_SLOT_COUNT] = {0}; | ||
| 343 | + bool valid[IOTC_VS_SLOT_COUNT] = {false}; | ||
| 344 | + ReadBothSlots(domain, headers, valid); | ||
| 345 | + | ||
| 346 | + bool found = false; | ||
| 347 | + int32_t result = IOTC_OK; | ||
| 348 | + for (uint32_t slot = 0; slot < IOTC_VS_SLOT_COUNT; ++slot) { | ||
| 349 | + if (valid[slot] && headers[slot].state == IOTC_VS_STATE_PENDING) { | ||
| 350 | + found = true; | ||
| 351 | + if (EraseSlot(domain, slot) != IOTC_OK) { | ||
| 352 | + result = IOTC_CORE_DEVICE_ERR_STORE_KV; | ||
| 353 | + } | ||
| 354 | + } | ||
| 355 | + } | ||
| 356 | + if (found) { | ||
| 357 | + return result; | ||
| 358 | + } | ||
| 359 | + /* 已无 pending 时重复 Abort 幂等。 */ | ||
| 360 | + return (valid[0] || valid[1]) ? IOTC_OK : IOTC_CORE_DEVICE_ERR_STORE_NOT_FOUND; | ||
| 361 | +} | ||
| 362 | + | ||
| 363 | +int32_t IotcVsErase(const char *domain) | ||
| 364 | +{ | ||
| 365 | + int32_t domainRet = ValidateDomainName(domain); | ||
| 366 | + CHECK_RETURN(domainRet == IOTC_OK, domainRet); | ||
| 367 | + int32_t first = EraseSlot(domain, 0); | ||
| 368 | + int32_t second = EraseSlot(domain, 1); | ||
| 369 | + return (first == IOTC_OK && second == IOTC_OK) ? IOTC_OK : IOTC_CORE_DEVICE_ERR_STORE_KV; | ||
| 370 | +} | ||
| 371 | + | ||
| 372 | +int32_t IotcVsGetGeneration(const char *domain, uint32_t *generation) | ||
| 373 | +{ | ||
| 374 | + CHECK_RETURN(generation != NULL, IOTC_ERR_PARAM_INVALID); | ||
| 375 | + int32_t domainRet = ValidateDomainName(domain); | ||
| 376 | + CHECK_RETURN(domainRet == IOTC_OK, domainRet); | ||
| 377 | + IotcVsHeader active = {0}; | ||
| 378 | + int32_t ret = FindActive(domain, &active, NULL, 0, NULL); | ||
| 379 | + CHECK_RETURN(ret == IOTC_OK, ret); | ||
| 380 | + *generation = active.generation; | ||
| 381 | + return IOTC_OK; | ||
| 382 | +} | ||
| @@ -37,7 +37,9 @@ iotc_core_device_src = [ | |||
| 37 | "$iotc_core_path/device/config/config_hub_info.c", | 37 | "$iotc_core_path/device/config/config_hub_info.c", |
| 38 | "$iotc_core_path/device/config/config_revoke_flag.c", | 38 | "$iotc_core_path/device/config/config_revoke_flag.c", |
| 39 | "$iotc_core_path/device/config/config_authinfo.c", | 39 | "$iotc_core_path/device/config/config_authinfo.c", |
| 40 | - "$iotc_core_path/device/config/config_revoke.c", | 40 | + "$iotc_core_path/device/config/config_revoke.c", |
| 41 | + "$iotc_core_path/device/config/device_state_store.c", | ||
| 42 | + "$iotc_core_path/device/config/versioned_store.c", | ||
| 41 | "$iotc_core_path/device/service/device_control.c", | 43 | "$iotc_core_path/device/service/device_control.c", |
| 42 | "$iotc_core_path/device/service/svc_cloud_setup.c", | 44 | "$iotc_core_path/device/service/svc_cloud_setup.c", |
| 43 | "$iotc_core_path/device/service/svc_cloud_login.c", | 45 | "$iotc_core_path/device/service/svc_cloud_login.c", |
| @@ -47,4 +49,4 @@ iotc_core_device_src = [ | |||
| 47 | "$iotc_core_path/device/service/svc_hub_setup.c", | 49 | "$iotc_core_path/device/service/svc_hub_setup.c", |
| 48 | ] | 50 | ] |
| 49 | 51 | ||
| 50 | -iotc_core_device_def = [] | 52 | +iotc_core_device_def = [] |
| @@ -85,6 +85,15 @@ static int32_t DeviceServiceStart(int32_t instanceId, ServiceFinishCallback onFi | |||
| 85 | ret = ConfigInfoInit(); | 85 | ret = ConfigInfoInit(); |
| 86 | if (ret != IOTC_OK) { | 86 | if (ret != IOTC_OK) { |
| 87 | IOTC_LOGW("config info init %d", ret); | 87 | IOTC_LOGW("config info init %d", ret); |
| 88 | + ProfileReportUnregUplink(DeviceReportCallback); | ||
| 89 | + return ret; | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + ret = DeviceServiceRecoverHubBindInfo(); | ||
| 93 | + if (ret != IOTC_OK) { | ||
| 94 | + IOTC_LOGW("recover HubSetup info error %d", ret); | ||
| 95 | + ConfigInfoDeinit(); | ||
| 96 | + ProfileReportUnregUplink(DeviceReportCallback); | ||
| 88 | return ret; | 97 | return ret; |
| 89 | } | 98 | } |
| 90 | 99 | ||
| @@ -179,6 +188,9 @@ int32_t DeviceServiceInit(void) | |||
| 179 | .onGetAuthInfo = GetAuthInfo, | 188 | .onGetAuthInfo = GetAuthInfo, |
| 180 | .onRecvBindInfo = DeviceServiceRecvBindingInfo, | 189 | .onRecvBindInfo = DeviceServiceRecvBindingInfo, |
| 181 | .onRecvHubBindInfo = DeviceServiceRecvHubBindInfo, | 190 | .onRecvHubBindInfo = DeviceServiceRecvHubBindInfo, |
| 191 | + .onPrepareHubBindInfo = DeviceServicePrepareHubBindInfo, | ||
| 192 | + .onCommitHubBindInfo = DeviceServiceCommitHubBindInfo, | ||
| 193 | + .onAbortHubBindInfo = DeviceServiceAbortHubBindInfo, | ||
| 182 | .onRecvAuthInfo = DeviceServiceRecvAuthInfo, | 194 | .onRecvAuthInfo = DeviceServiceRecvAuthInfo, |
| 183 | .onRecvProvisionPsk = DeviceServiceRecvProvisionPsk, | 195 | .onRecvProvisionPsk = DeviceServiceRecvProvisionPsk, |
| 184 | .onRecvLoginInfo = DeviceServiceRecvLoginInfo, | 196 | .onRecvLoginInfo = DeviceServiceRecvLoginInfo, |
| @@ -22,6 +22,23 @@ | |||
| 22 | 22 | ||
| 23 | 23 | ||
| 24 | 24 | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + | ||
| 35 | +typedef struct { | ||
| 36 | + bool active; | ||
| 37 | + DevHubInfo hubInfo; | ||
| 38 | +} HubSetupTransaction; | ||
| 39 | + | ||
| 40 | +static HubSetupTransaction g_hubSetupTransaction = {0}; | ||
| 41 | + | ||
| 25 | 42 | ||
| 26 | static void DevHubInfoClear(DevHubInfo *info) | 43 | static void DevHubInfoClear(DevHubInfo *info) |
| 27 | { | 44 | { |
| @@ -30,53 +47,281 @@ static void DevHubInfoClear(DevHubInfo *info) | |||
| 30 | } | 47 | } |
| 31 | } | 48 | } |
| 32 | 49 | ||
| 50 | +static bool IsHexChar(char value) | ||
| 51 | +{ | ||
| 52 | + return (value >= '0' && value <= '9') || (value >= 'a' && value <= 'f') || | ||
| 53 | + (value >= 'A' && value <= 'F'); | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | +static bool IsValidBssid(const char *bssid) | ||
| 57 | +{ | ||
| 58 | + if (bssid == NULL || strlen(bssid) != HUB_SETUP_BSSID_STR_LEN) { | ||
| 59 | + return false; | ||
| 60 | + } | ||
| 61 | + for (uint32_t i = 0; i < HUB_SETUP_BSSID_STR_LEN; ++i) { | ||
| 62 | + if ((i + 1U) % 3U == 0U) { | ||
| 63 | + if (bssid[i] != ':') { | ||
| 64 | + return false; | ||
| 65 | + } | ||
| 66 | + } else if (!IsHexChar(bssid[i])) { | ||
| 67 | + return false; | ||
| 68 | + } | ||
| 69 | + } | ||
| 70 | + return true; | ||
| 71 | +} | ||
| 72 | + | ||
| 73 | +static int32_t ParseRequiredString(const IotcJson *jsonObj, const char *name, char *value, uint32_t valueLen) | ||
| 74 | +{ | ||
| 75 | + int32_t ret = UtilsJsonGetString(jsonObj, name, value, valueLen); | ||
| 76 | + CHECK_RETURN(ret == IOTC_OK, ret); | ||
| 77 | + CHECK_RETURN(value[0] != '\0', IOTC_ERR_PARAM_INVALID); | ||
| 78 | + return IOTC_OK; | ||
| 79 | +} | ||
| 80 | + | ||
| 33 | static int32_t ParseDeviceHubInfo(const IotcJson *jsonObj, DevHubInfo *hubInfo) | 81 | static int32_t ParseDeviceHubInfo(const IotcJson *jsonObj, DevHubInfo *hubInfo) |
| 34 | { | 82 | { |
| 35 | if (NULL == jsonObj || NULL == hubInfo) { | 83 | if (NULL == jsonObj || NULL == hubInfo) { |
| 36 | IOTC_LOGE("invalid param"); | 84 | IOTC_LOGE("invalid param"); |
| 37 | return IOTC_ERROR; | 85 | return IOTC_ERROR; |
| 38 | } | 86 | } |
| 39 | - int32_t ret = UtilsJsonGetString(jsonObj, STR_JSON_DOMAIN_ID, hubInfo->domainId, sizeof(hubInfo->domainId)); | 87 | + int32_t ret = ParseRequiredString(jsonObj, STR_JSON_DOMAIN_ID, |
| 88 | + hubInfo->domainId, sizeof(hubInfo->domainId)); | ||
| 40 | if (ret != IOTC_OK) { | 89 | if (ret != IOTC_OK) { |
| 41 | IOTC_LOGE("get domainId error %d", ret); | 90 | IOTC_LOGE("get domainId error %d", ret); |
| 42 | return ret; | 91 | return ret; |
| 43 | } | 92 | } |
| 44 | - ret = UtilsJsonGetString(jsonObj, STR_JSON_DEVID, hubInfo->devId, sizeof(hubInfo->devId)); | 93 | + ret = ParseRequiredString(jsonObj, STR_JSON_DEVID, hubInfo->devId, sizeof(hubInfo->devId)); |
| 45 | if (ret != IOTC_OK) { | 94 | if (ret != IOTC_OK) { |
| 46 | IOTC_LOGE("get devId error %d", ret); | 95 | IOTC_LOGE("get devId error %d", ret); |
| 47 | return ret; | 96 | return ret; |
| 48 | } | 97 | } |
| 49 | - // wifi Combo 设备才有的参数 | 98 | + /* Wi-Fi Only/Combo 设备在 HubSetup 中携带网络参数。 */ |
| 50 | 99 | ||
| 51 | - ret = UtilsJsonGetString(jsonObj, STR_JSON_CODE, hubInfo->code, sizeof(hubInfo->code)); | 100 | + ret = ParseRequiredString(jsonObj, STR_JSON_CODE, hubInfo->code, sizeof(hubInfo->code)); |
| 52 | if (ret != IOTC_OK) { | 101 | if (ret != IOTC_OK) { |
| 53 | IOTC_LOGE("get code error %d", ret); | 102 | IOTC_LOGE("get code error %d", ret); |
| 54 | return ret; | 103 | return ret; |
| 55 | } | 104 | } |
| 56 | - ret = UtilsJsonGetString(jsonObj, STR_NETINFO_SSID, hubInfo->ssid, sizeof(hubInfo->ssid)); | 105 | + ret = ParseRequiredString(jsonObj, STR_NETINFO_SSID, hubInfo->ssid, sizeof(hubInfo->ssid)); |
| 57 | if (ret != IOTC_OK) { | 106 | if (ret != IOTC_OK) { |
| 58 | IOTC_LOGE("get ssid error %d", ret); | 107 | IOTC_LOGE("get ssid error %d", ret); |
| 59 | return ret; | 108 | return ret; |
| 60 | } | 109 | } |
| 61 | - ret = UtilsJsonGetString(jsonObj, STR_NETINFO_PASSWORD, hubInfo->password, sizeof(hubInfo->password)); | 110 | + ret = ParseRequiredString(jsonObj, STR_NETINFO_PASSWORD, hubInfo->password, sizeof(hubInfo->password)); |
| 62 | if (ret != IOTC_OK) { | 111 | if (ret != IOTC_OK) { |
| 63 | IOTC_LOGE("get password error %d", ret); | 112 | IOTC_LOGE("get password error %d", ret); |
| 64 | return ret; | 113 | return ret; |
| 65 | } | 114 | } |
| 66 | - ret = UtilsJsonGetNum(jsonObj, STR_JSON_CHANNEL, &hubInfo->channel); // 可选参数 | 115 | + IotcJson *channelObj = IotcJsonGetObj(jsonObj, STR_JSON_CHANNEL); |
| 67 | - if (ret != IOTC_OK) { | 116 | + if (channelObj != NULL) { |
| 68 | - IOTC_LOGW("get channel error %d", ret); | 117 | + ret = UtilsJsonGetNum(jsonObj, STR_JSON_CHANNEL, &hubInfo->channel); |
| 118 | + if (ret != IOTC_OK || hubInfo->channel < HUB_SETUP_CHANNEL_MIN || | ||
| 119 | + hubInfo->channel > HUB_SETUP_CHANNEL_MAX) { | ||
| 120 | + IOTC_LOGE("invalid channel"); | ||
| 121 | + return (ret == IOTC_OK) ? IOTC_ERR_PARAM_INVALID : ret; | ||
| 122 | + } | ||
| 69 | } | 123 | } |
| 70 | - ret = UtilsJsonGetString(jsonObj, STR_JSON_BSSID, hubInfo->bssid, sizeof(hubInfo->bssid)); // 可选参数 | 124 | + IotcJson *bssidObj = IotcJsonGetObj(jsonObj, STR_JSON_BSSID); |
| 71 | - if (ret != IOTC_OK) { | 125 | + if (bssidObj != NULL) { |
| 72 | - IOTC_LOGW("get bssid error %d", ret); | 126 | + ret = UtilsJsonGetString(jsonObj, STR_JSON_BSSID, hubInfo->bssid, sizeof(hubInfo->bssid)); |
| 127 | + if (ret != IOTC_OK || !IsValidBssid(hubInfo->bssid)) { | ||
| 128 | + IOTC_LOGE("invalid bssid"); | ||
| 129 | + return (ret == IOTC_OK) ? IOTC_ERR_PARAM_INVALID : ret; | ||
| 130 | + } | ||
| 73 | } | 131 | } |
| 74 | 132 | ||
| 75 | return IOTC_OK; | 133 | return IOTC_OK; |
| 76 | } | 134 | } |
| 77 | 135 | ||
| 136 | + | ||
| 137 | +static int32_t BuildProvisionPending(const DevHubInfo *hubInfo, IotcDssProvisionPending *pending) | ||
| 138 | +{ | ||
| 139 | + CHECK_RETURN(hubInfo != NULL && pending != NULL, IOTC_ERR_PARAM_INVALID); | ||
| 140 | + uint32_t mdidLen = strlen(hubInfo->domainId); | ||
| 141 | + CHECK_RETURN(mdidLen > 0 && mdidLen <= sizeof(pending->mdid), IOTC_ERR_PARAM_INVALID); | ||
| 142 | + | ||
| 143 | + (void)memset_s(pending, sizeof(*pending), 0, sizeof(*pending)); | ||
| 144 | + if (memcpy_s(pending->mdid, sizeof(pending->mdid), hubInfo->domainId, mdidLen) != EOK || | ||
| 145 | + strcpy_s(pending->tempDevId, sizeof(pending->tempDevId), hubInfo->devId) != EOK || | ||
| 146 | + strcpy_s(pending->registerCode, sizeof(pending->registerCode), hubInfo->code) != EOK) { | ||
| 147 | + return IOTC_ERR_SECUREC_MEMCPY; | ||
| 148 | + } | ||
| 149 | + pending->mdidLen = (uint8_t)mdidLen; | ||
| 150 | + pending->state = IOTC_DSS_PENDING_STATE_WAIT_ACTIVATE; | ||
| 151 | + return IotcMdCalc(IOTC_MD_SHA256, (const uint8_t *)hubInfo, sizeof(*hubInfo), | ||
| 152 | + pending->requestHash, sizeof(pending->requestHash)); | ||
| 153 | +} | ||
| 154 | + | ||
| 155 | +static bool ProvisionPendingMatches(const IotcDssProvisionPending *left, | ||
| 156 | + const IotcDssProvisionPending *right) | ||
| 157 | +{ | ||
| 158 | + return left->mdidLen == right->mdidLen && left->state == right->state && | ||
| 159 | + memcmp(left->mdid, right->mdid, left->mdidLen) == 0 && | ||
| 160 | + strcmp(left->tempDevId, right->tempDevId) == 0 && | ||
| 161 | + strcmp(left->registerCode, right->registerCode) == 0 && | ||
| 162 | + memcmp(left->requestHash, right->requestHash, sizeof(left->requestHash)) == 0; | ||
| 163 | +} | ||
| 164 | + | ||
| 165 | +static int32_t SyncHubInfo(const IotcDssProvisionPending *pending, const DevHubInfo *requestInfo) | ||
| 166 | +{ | ||
| 167 | + CHECK_RETURN(pending != NULL, IOTC_ERR_PARAM_INVALID); | ||
| 168 | + DevHubInfo hubInfo = {0}; | ||
| 169 | + if (requestInfo != NULL && memcpy_s(&hubInfo, sizeof(hubInfo), requestInfo, sizeof(*requestInfo)) != EOK) { | ||
| 170 | + return IOTC_ERR_SECUREC_MEMCPY; | ||
| 171 | + } | ||
| 172 | + if (requestInfo == NULL) { | ||
| 173 | + if (memcpy_s(hubInfo.domainId, sizeof(hubInfo.domainId), pending->mdid, pending->mdidLen) != EOK || | ||
| 174 | + strcpy_s(hubInfo.devId, sizeof(hubInfo.devId), pending->tempDevId) != EOK || | ||
| 175 | + strcpy_s(hubInfo.code, sizeof(hubInfo.code), pending->registerCode) != EOK) { | ||
| 176 | + DevHubInfoClear(&hubInfo); | ||
| 177 | + return IOTC_ERR_SECUREC_MEMCPY; | ||
| 178 | + } | ||
| 179 | + } | ||
| 180 | + | ||
| 181 | + int32_t firstError = ConfigSaveHubInfo(&hubInfo); | ||
| 182 | + int32_t ret = ConfigSaveDomainId(hubInfo.domainId, strlen(hubInfo.domainId)); | ||
| 183 | + if (firstError == IOTC_OK && ret != IOTC_OK) { | ||
| 184 | + firstError = ret; | ||
| 185 | + } | ||
| 186 | + ret = ConfigSaveDevId(hubInfo.devId, strlen(hubInfo.devId)); | ||
| 187 | + if (firstError == IOTC_OK && ret != IOTC_OK) { | ||
| 188 | + firstError = ret; | ||
| 189 | + } | ||
| 190 | + DevHubInfoClear(&hubInfo); | ||
| 191 | + return firstError; | ||
| 192 | +} | ||
| 193 | + | ||
| 194 | +int32_t DeviceServicePrepareHubBindInfo(const IotcJson *jsonObj, bool *alreadyCommitted) | ||
| 195 | +{ | ||
| 196 | + CHECK_RETURN_LOGW(jsonObj != NULL && alreadyCommitted != NULL, IOTC_ERR_PARAM_INVALID, "param invalid"); | ||
| 197 | + CHECK_RETURN_LOGW(!g_hubSetupTransaction.active, IOTC_CORE_DEVICE_ERR_STORE_STATE, | ||
| 198 | + "HubSetup transaction already active"); | ||
| 199 | + *alreadyCommitted = false; | ||
| 200 | + | ||
| 201 | + DevHubInfo hubInfo = {0}; | ||
| 202 | + IotcDssProvisionPending pending = {0}; | ||
| 203 | + int32_t ret = ParseDeviceHubInfo(jsonObj, &hubInfo); | ||
| 204 | + if (ret == IOTC_OK) { | ||
| 205 | + ret = BuildProvisionPending(&hubInfo, &pending); | ||
| 206 | + } | ||
| 207 | + if (ret != IOTC_OK) { | ||
| 208 | + DevHubInfoClear(&hubInfo); | ||
| 209 | + (void)memset_s(&pending, sizeof(pending), 0, sizeof(pending)); | ||
| 210 | + return ret; | ||
| 211 | + } | ||
| 212 | + | ||
| 213 | + IotcDssProvisionPending active = {0}; | ||
| 214 | + ret = IotcDssLoadProvisionPending(&active); | ||
| 215 | + if (ret == IOTC_OK) { | ||
| 216 | + if (!ProvisionPendingMatches(&active, &pending)) { | ||
| 217 | + ret = IOTC_CORE_DEVICE_ERR_STORE_STATE; | ||
| 218 | + } else { | ||
| 219 | + *alreadyCommitted = true; | ||
| 220 | + ret = SyncHubInfo(&active, &hubInfo); | ||
| 221 | + } | ||
| 222 | + } else if (ret == IOTC_CORE_DEVICE_ERR_STORE_NOT_FOUND) { | ||
| 223 | + ret = IotcVsBeginWrite(IOTC_DSS_DOMAIN_PROVISION_PENDING, IOTC_DSS_SCHEMA_VERSION, | ||
| 224 | + (const uint8_t *)&pending, sizeof(pending)); | ||
| 225 | + if (ret == IOTC_OK) { | ||
| 226 | + g_hubSetupTransaction.active = true; | ||
| 227 | + (void)memcpy_s(&g_hubSetupTransaction.hubInfo, sizeof(g_hubSetupTransaction.hubInfo), | ||
| 228 | + &hubInfo, sizeof(hubInfo)); | ||
| 229 | + } | ||
| 230 | + } | ||
| 231 | + | ||
| 232 | + DevHubInfoClear(&hubInfo); | ||
| 233 | + (void)memset_s(&pending, sizeof(pending), 0, sizeof(pending)); | ||
| 234 | + (void)memset_s(&active, sizeof(active), 0, sizeof(active)); | ||
| 235 | + return ret; | ||
| 236 | +} | ||
| 237 | + | ||
| 238 | +int32_t DeviceServiceCommitHubBindInfo(void) | ||
| 239 | +{ | ||
| 240 | + CHECK_RETURN_LOGW(g_hubSetupTransaction.active, IOTC_CORE_DEVICE_ERR_STORE_STATE, | ||
| 241 | + "no HubSetup transaction"); | ||
| 242 | + int32_t ret = IotcVsCommit(IOTC_DSS_DOMAIN_PROVISION_PENDING); | ||
| 243 | + if (ret != IOTC_OK) { | ||
| 244 | + return ret; | ||
| 245 | + } | ||
| 246 | + | ||
| 247 | + IotcDssProvisionPending pending = {0}; | ||
| 248 | + int32_t buildRet = BuildProvisionPending(&g_hubSetupTransaction.hubInfo, &pending); | ||
| 249 | + if (buildRet == IOTC_OK) { | ||
| 250 | + int32_t syncRet = SyncHubInfo(&pending, &g_hubSetupTransaction.hubInfo); | ||
| 251 | + if (syncRet != IOTC_OK) { | ||
| 252 | + /* 双槽记录是权威数据;启动时会再次同步兼容缓存。 */ | ||
| 253 | + IOTC_LOGW("sync committed HubSetup cache error %d", syncRet); | ||
| 254 | + } | ||
| 255 | + } else { | ||
| 256 | + IOTC_LOGW("rebuild committed HubSetup cache error %d", buildRet); | ||
| 257 | + } | ||
| 258 | + (void)memset_s(&pending, sizeof(pending), 0, sizeof(pending)); | ||
| 259 | + DevHubInfoClear(&g_hubSetupTransaction.hubInfo); | ||
| 260 | + g_hubSetupTransaction.active = false; | ||
| 261 | + return IOTC_OK; | ||
| 262 | +} | ||
| 263 | + | ||
| 264 | +int32_t DeviceServiceAbortHubBindInfo(void) | ||
| 265 | +{ | ||
| 266 | + if (!g_hubSetupTransaction.active) { | ||
| 267 | + return IOTC_OK; | ||
| 268 | + } | ||
| 269 | + int32_t ret = IotcVsAbort(IOTC_DSS_DOMAIN_PROVISION_PENDING); | ||
| 270 | + DevHubInfoClear(&g_hubSetupTransaction.hubInfo); | ||
| 271 | + g_hubSetupTransaction.active = false; | ||
| 272 | + return (ret == IOTC_CORE_DEVICE_ERR_STORE_NOT_FOUND) ? IOTC_OK : ret; | ||
| 273 | +} | ||
| 274 | + | ||
| 275 | +int32_t DeviceServiceRecoverHubBindInfo(void) | ||
| 276 | +{ | ||
| 277 | + IotcDssProvisionPending pending = {0}; | ||
| 278 | + int32_t ret = IotcDssLoadProvisionPending(&pending); | ||
| 279 | + if (ret != IOTC_OK && ret != IOTC_CORE_DEVICE_ERR_STORE_NOT_FOUND) { | ||
| 280 | + return ret; | ||
| 281 | + } | ||
| 282 | + if (ret == IOTC_OK) { | ||
| 283 | + ret = SyncHubInfo(&pending, NULL); | ||
| 284 | + CHECK_RETURN(ret == IOTC_OK, ret); | ||
| 285 | + } | ||
| 286 | + | ||
| 287 | + (void)memset_s(&pending, sizeof(pending), 0, sizeof(pending)); | ||
| 288 | + return IOTC_OK; | ||
| 289 | +} | ||
| 290 | + | ||
| 291 | +int32_t DeviceServicePrepareHubBindInfo(const IotcJson *jsonObj, bool *alreadyCommitted) | ||
| 292 | +{ | ||
| 293 | + CHECK_RETURN(alreadyCommitted != NULL, IOTC_ERR_PARAM_INVALID); | ||
| 294 | + int32_t ret = DeviceServiceRecvHubBindInfo(jsonObj); | ||
| 295 | + *alreadyCommitted = (ret == IOTC_OK); | ||
| 296 | + return ret; | ||
| 297 | +} | ||
| 298 | + | ||
| 299 | +int32_t DeviceServiceCommitHubBindInfo(void) | ||
| 300 | +{ | ||
| 301 | + return IOTC_OK; | ||
| 302 | +} | ||
| 303 | + | ||
| 304 | +int32_t DeviceServiceAbortHubBindInfo(void) | ||
| 305 | +{ | ||
| 306 | + return IOTC_OK; | ||
| 307 | +} | ||
| 308 | + | ||
| 309 | +int32_t DeviceServiceRecoverHubBindInfo(void) | ||
| 310 | +{ | ||
| 311 | + return IOTC_OK; | ||
| 312 | +} | ||
| 313 | + | ||
| 314 | + | ||
| 78 | int32_t DeviceServiceRecvHubBindInfo(const IotcJson *jsonObj) | 315 | int32_t DeviceServiceRecvHubBindInfo(const IotcJson *jsonObj) |
| 79 | { | 316 | { |
| 317 | + | ||
| 318 | + bool alreadyCommitted = false; | ||
| 319 | + int32_t ret = DeviceServicePrepareHubBindInfo(jsonObj, &alreadyCommitted); | ||
| 320 | + if (ret != IOTC_OK || alreadyCommitted) { | ||
| 321 | + return ret; | ||
| 322 | + } | ||
| 323 | + return DeviceServiceCommitHubBindInfo(); | ||
| 324 | + | ||
| 80 | CHECK_RETURN_LOGW(jsonObj != NULL, IOTC_ERR_PARAM_INVALID, "param invalid"); | 325 | CHECK_RETURN_LOGW(jsonObj != NULL, IOTC_ERR_PARAM_INVALID, "param invalid"); |
| 81 | DevHubInfo hubInfo = {0}; | 326 | DevHubInfo hubInfo = {0}; |
| 82 | int32_t ret = ParseDeviceHubInfo(jsonObj, &hubInfo); | 327 | int32_t ret = ParseDeviceHubInfo(jsonObj, &hubInfo); |
| @@ -106,4 +351,5 @@ int32_t DeviceServiceRecvHubBindInfo(const IotcJson *jsonObj) | |||
| 106 | } | 351 | } |
| 107 | DevHubInfoClear(&hubInfo); | 352 | DevHubInfoClear(&hubInfo); |
| 108 | return IOTC_OK; | 353 | return IOTC_OK; |
| 109 | -} | 354 | +#endif |
| 355 | +} | ||
| @@ -23,7 +23,15 @@ extern "C" { | |||
| 23 | 23 | ||
| 24 | int32_t DeviceServiceRecvHubBindInfo(const IotcJson *jsonObj); | 24 | int32_t DeviceServiceRecvHubBindInfo(const IotcJson *jsonObj); |
| 25 | 25 | ||
| 26 | +int32_t DeviceServicePrepareHubBindInfo(const IotcJson *jsonObj, bool *alreadyCommitted); | ||
| 27 | + | ||
| 28 | +int32_t DeviceServiceCommitHubBindInfo(void); | ||
| 29 | + | ||
| 30 | +int32_t DeviceServiceAbortHubBindInfo(void); | ||
| 31 | + | ||
| 32 | +int32_t DeviceServiceRecoverHubBindInfo(void); | ||
| 33 | + | ||
| 26 | 34 | ||
| 27 | } | 35 | } |
| 28 | 36 | ||
| 29 | -#endif /* SERVICE_HUB_SETUP_H */ | 37 | +#endif /* SERVICE_HUB_SETUP_H */ |
| @@ -79,6 +79,8 @@ typedef struct { | |||
| 79 | 79 | ||
| 80 | 80 | ||
| 81 | 81 | ||
| 82 | + | ||
| 83 | + | ||
| 82 | 84 | ||
| 83 | 85 | ||
| 84 | 86 | ||
| @@ -284,6 +286,7 @@ typedef struct { | |||
| 284 | 286 | ||
| 285 | 287 | ||
| 286 | 288 | ||
| 289 | + | ||
| 287 | 290 | ||
| 288 | 291 | ||
| 289 | 292 | ||
| @@ -78,6 +78,7 @@ typedef struct { | |||
| 78 | char devId[DEVICE_ID_MAX_STR_LEN + 1]; | 78 | char devId[DEVICE_ID_MAX_STR_LEN + 1]; |
| 79 | char ssid[IOTC_WIFI_SSID_MAX_LEN + 1]; | 79 | char ssid[IOTC_WIFI_SSID_MAX_LEN + 1]; |
| 80 | char password[IOTC_WIFI_PWD_MAX_LEN + 1]; | 80 | char password[IOTC_WIFI_PWD_MAX_LEN + 1]; |
| 81 | + /* 6 字节 BSSID 的标准文本形式为 XX:XX:XX:XX:XX:XX,共 17 字符。 */ | ||
| 81 | char bssid[IOTC_WIFI_BSSID_STR_MAX_LEN]; | 82 | char bssid[IOTC_WIFI_BSSID_STR_MAX_LEN]; |
| 82 | int32_t channel; | 83 | int32_t channel; |
| 83 | } DevHubInfo; | 84 | } DevHubInfo; |
| @@ -95,6 +96,8 @@ typedef int32_t (*DevGetRegisterInfo)(bool *isExist, DevRegInfo *info); | |||
| 95 | typedef int32_t (*DevGetAuthInfo)(bool *isExist, DevAuthInfo *info); | 96 | typedef int32_t (*DevGetAuthInfo)(bool *isExist, DevAuthInfo *info); |
| 96 | typedef int32_t (*DevRecvBindInfo)(const IotcJson *json); | 97 | typedef int32_t (*DevRecvBindInfo)(const IotcJson *json); |
| 97 | typedef int32_t (*DevRecvHubBindInfo)(const IotcJson *json); | 98 | typedef int32_t (*DevRecvHubBindInfo)(const IotcJson *json); |
| 99 | +typedef int32_t (*DevPrepareHubBindInfo)(const IotcJson *json, bool *alreadyCommitted); | ||
| 100 | +typedef int32_t (*DevFinishHubBindInfo)(void); | ||
| 98 | typedef int32_t (*DevRecvAuthInfo)(const IotcJson *json); | 101 | typedef int32_t (*DevRecvAuthInfo)(const IotcJson *json); |
| 99 | typedef int32_t (*DevRecvProvisionPsk)(const IotcJson *json); | 102 | typedef int32_t (*DevRecvProvisionPsk)(const IotcJson *json); |
| 100 | typedef int32_t (*DevRecvLoginInfo)(const IotcJson *json); | 103 | typedef int32_t (*DevRecvLoginInfo)(const IotcJson *json); |
| @@ -115,6 +118,9 @@ typedef struct { | |||
| 115 | DevGetAuthInfo onGetAuthInfo; | 118 | DevGetAuthInfo onGetAuthInfo; |
| 116 | DevRecvBindInfo onRecvBindInfo; | 119 | DevRecvBindInfo onRecvBindInfo; |
| 117 | DevRecvHubBindInfo onRecvHubBindInfo; | 120 | DevRecvHubBindInfo onRecvHubBindInfo; |
| 121 | + DevPrepareHubBindInfo onPrepareHubBindInfo; | ||
| 122 | + DevFinishHubBindInfo onCommitHubBindInfo; | ||
| 123 | + DevFinishHubBindInfo onAbortHubBindInfo; | ||
| 118 | DevRecvAuthInfo onRecvAuthInfo; | 124 | DevRecvAuthInfo onRecvAuthInfo; |
| 119 | DevRecvProvisionPsk onRecvProvisionPsk; | 125 | DevRecvProvisionPsk onRecvProvisionPsk; |
| 120 | DevRecvLoginInfo onRecvLoginInfo; | 126 | DevRecvLoginInfo onRecvLoginInfo; |
| @@ -131,6 +137,9 @@ typedef enum { | |||
| 131 | 137 | ||
| 132 | int32_t DevSvcProxyRecvBindInfo(const IotcJson *json); | 138 | int32_t DevSvcProxyRecvBindInfo(const IotcJson *json); |
| 133 | int32_t DevSvcProxyRecvHubBindInfo(const IotcJson *json); | 139 | int32_t DevSvcProxyRecvHubBindInfo(const IotcJson *json); |
| 140 | +int32_t DevSvcProxyPrepareHubBindInfo(const IotcJson *json, bool *alreadyCommitted); | ||
| 141 | +int32_t DevSvcProxyCommitHubBindInfo(void); | ||
| 142 | +int32_t DevSvcProxyAbortHubBindInfo(void); | ||
| 134 | int32_t DevSvcProxyCtlGetCharStates(const IotcJson *inArray, IotcJson **outArray); | 143 | int32_t DevSvcProxyCtlGetCharStates(const IotcJson *inArray, IotcJson **outArray); |
| 135 | int32_t DevSvcProxyCtlReportAll(DevReportType type); | 144 | int32_t DevSvcProxyCtlReportAll(DevReportType type); |
| 136 | int32_t DevSvcProxyCtlReportByDevId(DevReportType type, const char *devId); | 145 | int32_t DevSvcProxyCtlReportByDevId(DevReportType type, const char *devId); |
| @@ -151,4 +160,4 @@ void DevSvcProxySetOnlineStatus(bool isOnline); | |||
| 151 | 160 | ||
| 152 | } | 161 | } |
| 153 | 162 | ||
| 154 | -#endif /* IOTC_SERVICE_DEVICE_H */ | 163 | +#endif /* IOTC_SERVICE_DEVICE_H */ |
| @@ -50,6 +50,33 @@ int32_t DevSvcProxyRecvHubBindInfo(const IotcJson *json) | |||
| 50 | return devApi->onRecvHubBindInfo(json); | 50 | return devApi->onRecvHubBindInfo(json); |
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | +int32_t DevSvcProxyPrepareHubBindInfo(const IotcJson *json, bool *alreadyCommitted) | ||
| 54 | +{ | ||
| 55 | + const DevSvcApi *devApi = GetDevSvcApi(); | ||
| 56 | + if (devApi == NULL || devApi->onPrepareHubBindInfo == NULL) { | ||
| 57 | + return IOTC_CORE_COMM_FWK_ERR_SERVICE_NO_API; | ||
| 58 | + } | ||
| 59 | + return devApi->onPrepareHubBindInfo(json, alreadyCommitted); | ||
| 60 | +} | ||
| 61 | + | ||
| 62 | +int32_t DevSvcProxyCommitHubBindInfo(void) | ||
| 63 | +{ | ||
| 64 | + const DevSvcApi *devApi = GetDevSvcApi(); | ||
| 65 | + if (devApi == NULL || devApi->onCommitHubBindInfo == NULL) { | ||
| 66 | + return IOTC_CORE_COMM_FWK_ERR_SERVICE_NO_API; | ||
| 67 | + } | ||
| 68 | + return devApi->onCommitHubBindInfo(); | ||
| 69 | +} | ||
| 70 | + | ||
| 71 | +int32_t DevSvcProxyAbortHubBindInfo(void) | ||
| 72 | +{ | ||
| 73 | + const DevSvcApi *devApi = GetDevSvcApi(); | ||
| 74 | + if (devApi == NULL || devApi->onAbortHubBindInfo == NULL) { | ||
| 75 | + return IOTC_CORE_COMM_FWK_ERR_SERVICE_NO_API; | ||
| 76 | + } | ||
| 77 | + return devApi->onAbortHubBindInfo(); | ||
| 78 | +} | ||
| 79 | + | ||
| 53 | int32_t DevSvcProxyCtlGetCharStates(const IotcJson *inArray, IotcJson **outArray) | 80 | int32_t DevSvcProxyCtlGetCharStates(const IotcJson *inArray, IotcJson **outArray) |
| 54 | { | 81 | { |
| 55 | const DevSvcApi *devApi = GetDevSvcApi(); | 82 | const DevSvcApi *devApi = GetDevSvcApi(); |
| @@ -208,4 +235,4 @@ void DevSvcProxySetOnlineStatus(bool isOnline) | |||
| 208 | } | 235 | } |
| 209 | 236 | ||
| 210 | devApi->onSetOnlineStatus(isOnline); | 237 | devApi->onSetOnlineStatus(isOnline); |
| 211 | -} | 238 | +} |
| @@ -18,6 +18,7 @@ set(IOTC_SRC_LIST | |||
| 18 | ${CMAKE_CURRENT_SOURCE_DIR}/svc_conn_conf.c | 18 | ${CMAKE_CURRENT_SOURCE_DIR}/svc_conn_conf.c |
| 19 | ${CMAKE_CURRENT_SOURCE_DIR}/svc_conn_ctx.c | 19 | ${CMAKE_CURRENT_SOURCE_DIR}/svc_conn_ctx.c |
| 20 | ${CMAKE_CURRENT_SOURCE_DIR}/svc_conn_net_info.c | 20 | ${CMAKE_CURRENT_SOURCE_DIR}/svc_conn_net_info.c |
| 21 | + ${CMAKE_CURRENT_SOURCE_DIR}/svc_conn_online_notify.c | ||
| 21 | ${CMAKE_CURRENT_SOURCE_DIR}/wifi_svc_conn.c | 22 | ${CMAKE_CURRENT_SOURCE_DIR}/wifi_svc_conn.c |
| 22 | PARENT_SCOPE | 23 | PARENT_SCOPE |
| 23 | ) | 24 | ) |
| @@ -32,4 +33,4 @@ set(IOTC_TEST_INC_LIST | |||
| 32 | ${IOTC_TEST_INC_LIST} | 33 | ${IOTC_TEST_INC_LIST} |
| 33 | ${CMAKE_CURRENT_SOURCE_DIR} | 34 | ${CMAKE_CURRENT_SOURCE_DIR} |
| 34 | PARENT_SCOPE | 35 | PARENT_SCOPE |
| 35 | -) | 36 | +) |
| @@ -24,11 +24,14 @@ | |||
| 24 | 24 | ||
| 25 | 25 | ||
| 26 | 26 | ||
| 27 | + | ||
| 27 | 28 | ||
| 29 | + | ||
| 28 | 30 | ||
| 29 | 31 | ||
| 30 | 32 | ||
| 31 | 33 | ||
| 34 | + | ||
| 32 | 35 | ||
| 33 | static void WifiVerifyFailedProcess(void) | 36 | static void WifiVerifyFailedProcess(void) |
| 34 | { | 37 | { |
| @@ -47,8 +50,11 @@ static void WifiVerifyFailedProcess(void) | |||
| 47 | IOTC_LOGF("clear net info flag error %d", ret); | 50 | IOTC_LOGF("clear net info flag error %d", ret); |
| 48 | } | 51 | } |
| 49 | 52 | ||
| 50 | - /* 复位回退到待配网 */ | 53 | + /* 配网阶段连接失败必须执行完整恢复出厂,避免遗留已绑定身份进入待配网。 */ |
| 51 | - IotcFwkNotifyReset(); | 54 | + ret = IotcFwkRestore(); |
| 55 | + if (ret != IOTC_OK) { | ||
| 56 | + IOTC_LOGW("restore after wifi verify failure error %d", ret); | ||
| 57 | + } | ||
| 52 | } | 58 | } |
| 53 | 59 | ||
| 54 | static void WifiVerifyConnectTimeoutTimerCallback(int32_t id, void *userData) | 60 | static void WifiVerifyConnectTimeoutTimerCallback(int32_t id, void *userData) |
| @@ -111,6 +117,59 @@ static void WifiReconnectTimerCallback(int32_t id, void *userData) | |||
| 111 | } | 117 | } |
| 112 | } | 118 | } |
| 113 | 119 | ||
| 120 | +static int32_t ScheduleReconnect(ConnectServiceContext *ctx) | ||
| 121 | +{ | ||
| 122 | + if (ctx->reconnectTimerFd >= 0) { | ||
| 123 | + return IOTC_OK; | ||
| 124 | + } | ||
| 125 | + ctx->reconnectTimerFd = SchedTimerAdd(EVENT_SOURCE_TIMER_TYPE_REPEAT, WifiReconnectTimerCallback, | ||
| 126 | + WIFI_AUTO_RECONNECT_TIMEVAL, NULL); | ||
| 127 | + if (ctx->reconnectTimerFd < 0) { | ||
| 128 | + IOTC_LOGE("create reconnect timer error %d", ctx->reconnectTimerFd); | ||
| 129 | + return ctx->reconnectTimerFd; | ||
| 130 | + } | ||
| 131 | + return IOTC_OK; | ||
| 132 | +} | ||
| 133 | + | ||
| 134 | +static void WifiInitialConnectTimerCallback(int32_t id, void *userData) | ||
| 135 | +{ | ||
| 136 | + NOT_USED(userData); | ||
| 137 | + ConnectServiceContext *ctx = GetConnSvcCtx(); | ||
| 138 | + SchedTimerRemove(id); | ||
| 139 | + ctx->initialConnectTimerFd = EVENT_SOURCE_INVALID_TIMER_FD; | ||
| 140 | + | ||
| 141 | + int32_t ret = ConnSvcActionConnectWifi(); | ||
| 142 | + if (ret == IOTC_OK) { | ||
| 143 | + return; | ||
| 144 | + } | ||
| 145 | + IOTC_LOGW("initial wifi connect error %d", ret); | ||
| 146 | + /* | ||
| 147 | + * A checked network must survive a transient platform-startup race. Keep | ||
| 148 | + * retrying at the normal reconnect cadence instead of failing the CONNECT | ||
| 149 | + * service and preventing the provisioning service from being evaluated. | ||
| 150 | + * A pending HubSetup failure is handled by ConnSvcActionConnectWifi(), | ||
| 151 | + * which clears the unverified network and triggers reset recovery. | ||
| 152 | + */ | ||
| 153 | + if (ConfigIsNetInfoChecked()) { | ||
| 154 | + (void)ScheduleReconnect(ctx); | ||
| 155 | + } | ||
| 156 | +} | ||
| 157 | + | ||
| 158 | +int32_t ConnSvcScheduleInitialConnect(void) | ||
| 159 | +{ | ||
| 160 | + ConnectServiceContext *ctx = GetConnSvcCtx(); | ||
| 161 | + if (ctx->initialConnectTimerFd >= 0) { | ||
| 162 | + return IOTC_OK; | ||
| 163 | + } | ||
| 164 | + ctx->initialConnectTimerFd = SchedTimerAdd(EVENT_SOURCE_TIMER_TYPE_ONCE, | ||
| 165 | + WifiInitialConnectTimerCallback, WIFI_INITIAL_CONNECT_DELAY_MS, NULL); | ||
| 166 | + if (ctx->initialConnectTimerFd < 0) { | ||
| 167 | + IOTC_LOGW("add initial wifi connect timer error %d", ctx->initialConnectTimerFd); | ||
| 168 | + return ctx->initialConnectTimerFd; | ||
| 169 | + } | ||
| 170 | + return IOTC_OK; | ||
| 171 | +} | ||
| 172 | + | ||
| 114 | static void WifiStatusCheckTimerCallback(int32_t id, void *userData) | 173 | static void WifiStatusCheckTimerCallback(int32_t id, void *userData) |
| 115 | { | 174 | { |
| 116 | NOT_USED(id); | 175 | NOT_USED(id); |
| @@ -127,22 +186,26 @@ static void WifiStatusCheckTimerCallback(int32_t id, void *userData) | |||
| 127 | IOTC_LOGI("wifi net disconnect"); | 186 | IOTC_LOGI("wifi net disconnect"); |
| 128 | UTILS_BIT_RESET(ctx->bitMap, CONN_SVC_BIT_MAP_WIFI_CONNECTED); | 187 | UTILS_BIT_RESET(ctx->bitMap, CONN_SVC_BIT_MAP_WIFI_CONNECTED); |
| 129 | EventBusPublishSync(IOTC_SDK_AILIFE_EVENT_WIFI_NET_DISCONNECT, NULL, 0); | 188 | EventBusPublishSync(IOTC_SDK_AILIFE_EVENT_WIFI_NET_DISCONNECT, NULL, 0); |
| 189 | + | ||
| 190 | + SvcConnOnlineNotifyStop(); | ||
| 191 | + | ||
| 130 | if (!ConfigIsNetInfoChecked()) { | 192 | if (!ConfigIsNetInfoChecked()) { |
| 131 | return; | 193 | return; |
| 132 | } | 194 | } |
| 133 | - if (ctx->reconnectTimerFd < 0) { | 195 | + (void)ScheduleReconnect(ctx); |
| 134 | - ctx->reconnectTimerFd = SchedTimerAdd(EVENT_SOURCE_TIMER_TYPE_REPEAT, WifiReconnectTimerCallback, | ||
| 135 | - WIFI_AUTO_RECONNECT_TIMEVAL, NULL); | ||
| 136 | - if (ctx->reconnectTimerFd < 0) { | ||
| 137 | - IOTC_LOGE("create reconnect timer error %d", ctx->reconnectTimerFd); | ||
| 138 | - } | ||
| 139 | - } | ||
| 140 | return; | 196 | return; |
| 141 | } | 197 | } |
| 142 | 198 | ||
| 143 | IOTC_LOGI("wifi net connect"); | 199 | IOTC_LOGI("wifi net connect"); |
| 144 | UTILS_BIT_SET(ctx->bitMap, CONN_SVC_BIT_MAP_WIFI_CONNECTED); | 200 | UTILS_BIT_SET(ctx->bitMap, CONN_SVC_BIT_MAP_WIFI_CONNECTED); |
| 201 | + | ||
| 145 | EventBusPublishSync(IOTC_SDK_AILIFE_EVENT_WIFI_NET_CONNECT, NULL, 0); | 202 | EventBusPublishSync(IOTC_SDK_AILIFE_EVENT_WIFI_NET_CONNECT, NULL, 0); |
| 203 | + | ||
| 204 | + int32_t notifyRet = SvcConnOnlineNotifyStart(); | ||
| 205 | + if (notifyRet != IOTC_OK) { | ||
| 206 | + IOTC_LOGW("start online notify error %d", notifyRet); | ||
| 207 | + } | ||
| 208 | + | ||
| 146 | 209 | ||
| 147 | if (ctx->reconnectTimerFd >= 0) { | 210 | if (ctx->reconnectTimerFd >= 0) { |
| 148 | SchedTimerRemove(ctx->reconnectTimerFd); | 211 | SchedTimerRemove(ctx->reconnectTimerFd); |
| @@ -23,6 +23,8 @@ extern "C" { | |||
| 23 | 23 | ||
| 24 | int32_t ConnSvcActionConnectWifi(void); | 24 | int32_t ConnSvcActionConnectWifi(void); |
| 25 | 25 | ||
| 26 | +int32_t ConnSvcScheduleInitialConnect(void); | ||
| 27 | + | ||
| 26 | int32_t ConnSvcStartNetCheckTimer(void); | 28 | int32_t ConnSvcStartNetCheckTimer(void); |
| 27 | 29 | ||
| 28 | 30 | ||
| @@ -27,6 +27,7 @@ int32_t ConnSvcCtxInit(void) | |||
| 27 | ctx->netCheckTimerFd = EVENT_SOURCE_INVALID_TIMER_FD; | 27 | ctx->netCheckTimerFd = EVENT_SOURCE_INVALID_TIMER_FD; |
| 28 | ctx->verifyTimerFd = EVENT_SOURCE_INVALID_TIMER_FD; | 28 | ctx->verifyTimerFd = EVENT_SOURCE_INVALID_TIMER_FD; |
| 29 | ctx->reconnectTimerFd = EVENT_SOURCE_INVALID_TIMER_FD; | 29 | ctx->reconnectTimerFd = EVENT_SOURCE_INVALID_TIMER_FD; |
| 30 | + ctx->initialConnectTimerFd = EVENT_SOURCE_INVALID_TIMER_FD; | ||
| 30 | 31 | ||
| 31 | return IOTC_OK; | 32 | return IOTC_OK; |
| 32 | } | 33 | } |
| @@ -49,10 +50,14 @@ void ConnSvcCtxDeinit(void) | |||
| 49 | SchedTimerRemove(ctx->reconnectTimerFd); | 50 | SchedTimerRemove(ctx->reconnectTimerFd); |
| 50 | ctx->reconnectTimerFd = EVENT_SOURCE_INVALID_TIMER_FD; | 51 | ctx->reconnectTimerFd = EVENT_SOURCE_INVALID_TIMER_FD; |
| 51 | } | 52 | } |
| 53 | + if (ctx->initialConnectTimerFd >= 0) { | ||
| 54 | + SchedTimerRemove(ctx->initialConnectTimerFd); | ||
| 55 | + ctx->initialConnectTimerFd = EVENT_SOURCE_INVALID_TIMER_FD; | ||
| 56 | + } | ||
| 52 | } | 57 | } |
| 53 | 58 | ||
| 54 | ConnectServiceContext *GetConnSvcCtx(void) | 59 | ConnectServiceContext *GetConnSvcCtx(void) |
| 55 | { | 60 | { |
| 56 | static ConnectServiceContext ctx; | 61 | static ConnectServiceContext ctx; |
| 57 | return &ctx; | 62 | return &ctx; |
| 58 | -} | 63 | +} |
| @@ -31,6 +31,7 @@ typedef struct { | |||
| 31 | int32_t verifyTimerFd; | 31 | int32_t verifyTimerFd; |
| 32 | int32_t netCheckTimerFd; | 32 | int32_t netCheckTimerFd; |
| 33 | int32_t reconnectTimerFd; | 33 | int32_t reconnectTimerFd; |
| 34 | + int32_t initialConnectTimerFd; | ||
| 34 | ServiceFinishCallback onFinish; | 35 | ServiceFinishCallback onFinish; |
| 35 | } ConnectServiceContext; | 36 | } ConnectServiceContext; |
| 36 | 37 | ||
| @@ -27,38 +27,98 @@ | |||
| 27 | 27 | ||
| 28 | 28 | ||
| 29 | 29 | ||
| 30 | -#define DELAY_CONNECT_WIFI_INTERVAL 10 | 30 | +/* HubSetup 响应会在 500 ms 窗口内完成发送,之后再切换到家庭 Wi-Fi。 */ |
| 31 | + | ||
| 31 | 32 | ||
| 32 | -int32_t ParseAndSaveNetCfgWifiInfo(const IotcJson *jsonObj) | 33 | +typedef struct { |
| 34 | + bool active; | ||
| 35 | + bool hadOldWifi; | ||
| 36 | + bool oldNeedVerify; | ||
| 37 | + bool oldChecked; | ||
| 38 | + bool softapSubscribed; | ||
| 39 | + int32_t delayTimer; | ||
| 40 | + uint8_t oldSsid[IOTC_WIFI_SSID_MAX_LEN + 1]; | ||
| 41 | + uint32_t oldSsidLen; | ||
| 42 | + uint8_t oldPassword[IOTC_WIFI_PWD_MAX_LEN + 1]; | ||
| 43 | + uint32_t oldPasswordLen; | ||
| 44 | +} NetInfoTransaction; | ||
| 45 | + | ||
| 46 | +static NetInfoTransaction g_netInfoTransaction = { | ||
| 47 | + .delayTimer = EVENT_SOURCE_INVALID_TIMER_FD, | ||
| 48 | +}; | ||
| 49 | + | ||
| 50 | +static void ClearNetInfoTransaction(void) | ||
| 33 | { | 51 | { |
| 34 | - CHECK_RETURN_LOGW(jsonObj != NULL, IOTC_ERR_PARAM_INVALID, "param invalid"); | 52 | + (void)memset_s(&g_netInfoTransaction, sizeof(g_netInfoTransaction), 0, sizeof(g_netInfoTransaction)); |
| 35 | - char ssid[IOTC_WIFI_SSID_MAX_LEN + 1] = {0}; | 53 | + g_netInfoTransaction.delayTimer = EVENT_SOURCE_INVALID_TIMER_FD; |
| 36 | - char pwd[IOTC_WIFI_PWD_MAX_LEN + 1] = {0}; | 54 | +} |
| 37 | - int32_t ret = UtilsJsonGetString(jsonObj, STR_NETINFO_SSID, ssid, sizeof(ssid)); | 55 | + |
| 56 | +static void SnapshotNetInfo(void) | ||
| 57 | +{ | ||
| 58 | + ClearNetInfoTransaction(); | ||
| 59 | + g_netInfoTransaction.oldNeedVerify = ConfigIsNetInfoNeedVerify(); | ||
| 60 | + g_netInfoTransaction.oldChecked = ConfigIsNetInfoChecked(); | ||
| 61 | + g_netInfoTransaction.oldSsidLen = IOTC_WIFI_SSID_MAX_LEN; | ||
| 62 | + g_netInfoTransaction.oldPasswordLen = IOTC_WIFI_PWD_MAX_LEN; | ||
| 63 | + g_netInfoTransaction.hadOldWifi = (IotcGetWifiInfo(g_netInfoTransaction.oldSsid, | ||
| 64 | + &g_netInfoTransaction.oldSsidLen, g_netInfoTransaction.oldPassword, | ||
| 65 | + &g_netInfoTransaction.oldPasswordLen) == IOTC_OK); | ||
| 66 | + g_netInfoTransaction.active = true; | ||
| 67 | +} | ||
| 68 | + | ||
| 69 | +static int32_t RestoreNetInfo(void) | ||
| 70 | +{ | ||
| 71 | + int32_t firstError = IOTC_OK; | ||
| 72 | + int32_t ret = g_netInfoTransaction.hadOldWifi ? | ||
| 73 | + IotcSetWifiInfo(g_netInfoTransaction.oldSsid, g_netInfoTransaction.oldSsidLen, | ||
| 74 | + g_netInfoTransaction.oldPassword, g_netInfoTransaction.oldPasswordLen) : | ||
| 75 | + IotcDeleteWifiInfo(); | ||
| 76 | + if (ret != IOTC_OK) { | ||
| 77 | + firstError = ret; | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + if (g_netInfoTransaction.oldNeedVerify) { | ||
| 81 | + ret = ConfigSetNetInfoNeedVerify(); | ||
| 82 | + } else if (g_netInfoTransaction.oldChecked) { | ||
| 83 | + ret = ConfigSetNetInfoChecked(); | ||
| 84 | + } else { | ||
| 85 | + ret = ConfigClearNetInfoFlag(); | ||
| 86 | + } | ||
| 87 | + if (firstError == IOTC_OK && ret != IOTC_OK) { | ||
| 88 | + firstError = ret; | ||
| 89 | + } | ||
| 90 | + return firstError; | ||
| 91 | +} | ||
| 92 | + | ||
| 93 | +static int32_t ParseNetCfgWifiInfo(const IotcJson *jsonObj, char *ssid, uint32_t ssidLen, | ||
| 94 | + char *pwd, uint32_t pwdLen) | ||
| 95 | +{ | ||
| 96 | + CHECK_RETURN_LOGW(jsonObj != NULL && ssid != NULL && pwd != NULL, IOTC_ERR_PARAM_INVALID, "param invalid"); | ||
| 97 | + int32_t ret = UtilsJsonGetString(jsonObj, STR_NETINFO_SSID, ssid, ssidLen); | ||
| 38 | if (ret != IOTC_OK) { | 98 | if (ret != IOTC_OK) { |
| 39 | IOTC_LOGE("get ssid error %d", ret); | 99 | IOTC_LOGE("get ssid error %d", ret); |
| 40 | return ret; | 100 | return ret; |
| 41 | } | 101 | } |
| 42 | 102 | ||
| 43 | - IOTC_LOGI("get ssid %s", ssid); | 103 | + ret = UtilsJsonGetString(jsonObj, STR_NETINFO_PASSWORD, pwd, pwdLen); |
| 44 | - ret = UtilsJsonGetString(jsonObj, STR_NETINFO_PASSWORD, pwd, sizeof(pwd)); | ||
| 45 | if (ret != IOTC_OK) { | 104 | if (ret != IOTC_OK) { |
| 46 | - (void)memset_s(ssid, sizeof(ssid), 0, sizeof(ssid)); | ||
| 47 | IOTC_LOGE("get pwd error %d", ret); | 105 | IOTC_LOGE("get pwd error %d", ret); |
| 48 | return ret; | 106 | return ret; |
| 49 | } | 107 | } |
| 108 | + return IOTC_OK; | ||
| 109 | +} | ||
| 50 | 110 | ||
| 51 | - IOTC_LOGI("get pwd %s", pwd); | 111 | +int32_t ParseAndSaveNetCfgWifiInfo(const IotcJson *jsonObj) |
| 52 | - ret = IotcSetWifiInfo((const uint8_t *)ssid, strlen(ssid), (const uint8_t *)pwd, strlen(pwd)); | 112 | +{ |
| 113 | + char ssid[IOTC_WIFI_SSID_MAX_LEN + 1] = {0}; | ||
| 114 | + char pwd[IOTC_WIFI_PWD_MAX_LEN + 1] = {0}; | ||
| 115 | + int32_t ret = ParseNetCfgWifiInfo(jsonObj, ssid, sizeof(ssid), pwd, sizeof(pwd)); | ||
| 116 | + if (ret == IOTC_OK) { | ||
| 117 | + ret = IotcSetWifiInfo((const uint8_t *)ssid, strlen(ssid), (const uint8_t *)pwd, strlen(pwd)); | ||
| 118 | + } | ||
| 53 | (void)memset_s(ssid, sizeof(ssid), 0, sizeof(ssid)); | 119 | (void)memset_s(ssid, sizeof(ssid), 0, sizeof(ssid)); |
| 54 | (void)memset_s(pwd, sizeof(pwd), 0, sizeof(pwd)); | 120 | (void)memset_s(pwd, sizeof(pwd), 0, sizeof(pwd)); |
| 55 | - if (ret != IOTC_OK) { | 121 | + return ret; |
| 56 | - IOTC_LOGW("set wifi info error %d", ret); | ||
| 57 | - return ret; | ||
| 58 | - } | ||
| 59 | - | ||
| 60 | - IOTC_LOGI("save net info ok"); | ||
| 61 | - return IOTC_OK; | ||
| 62 | } | 122 | } |
| 63 | 123 | ||
| 64 | static void SoftapStopEventCallback(uint32_t event, void *param, uint32_t len) | 124 | static void SoftapStopEventCallback(uint32_t event, void *param, uint32_t len) |
| @@ -77,6 +137,11 @@ static void SoftapStopEventCallback(uint32_t event, void *param, uint32_t len) | |||
| 77 | static void DelayConnetWifiCb(int32_t id, void *userData) | 137 | static void DelayConnetWifiCb(int32_t id, void *userData) |
| 78 | { | 138 | { |
| 79 | NOT_USED(userData); | 139 | NOT_USED(userData); |
| 140 | + /* 切换到家庭 Wi-Fi 前主动断开旧链路。 */ | ||
| 141 | + int32_t disconnectRet = IotcDisconnectWifi(); | ||
| 142 | + if (disconnectRet != IOTC_OK) { | ||
| 143 | + IOTC_LOGW("disconnect provisioning wifi error %d", disconnectRet); | ||
| 144 | + } | ||
| 80 | int32_t ret = ConnSvcActionConnectWifi(); | 145 | int32_t ret = ConnSvcActionConnectWifi(); |
| 81 | if (ret != IOTC_OK) { | 146 | if (ret != IOTC_OK) { |
| 82 | IOTC_LOGW("wifi connect error %d", ret); | 147 | IOTC_LOGW("wifi connect error %d", ret); |
| @@ -85,37 +150,88 @@ static void DelayConnetWifiCb(int32_t id, void *userData) | |||
| 85 | IOTC_LOGN("delay connect Wifi timer cb finish"); | 150 | IOTC_LOGN("delay connect Wifi timer cb finish"); |
| 86 | } | 151 | } |
| 87 | 152 | ||
| 88 | -int32_t SvcConnSetNetInfo(const IotcJson *json) | 153 | +int32_t SvcConnPrepareNetInfo(const IotcJson *json) |
| 89 | { | 154 | { |
| 90 | CHECK_RETURN_LOGW(json != NULL, IOTC_ERR_PARAM_INVALID, "param invalid"); | 155 | CHECK_RETURN_LOGW(json != NULL, IOTC_ERR_PARAM_INVALID, "param invalid"); |
| 156 | + CHECK_RETURN_LOGW(!g_netInfoTransaction.active, IOTC_CORE_DEVICE_ERR_STORE_STATE, | ||
| 157 | + "net info transaction already active"); | ||
| 91 | 158 | ||
| 92 | - int32_t ret = ParseAndSaveNetCfgWifiInfo(json); | 159 | + char ssid[IOTC_WIFI_SSID_MAX_LEN + 1] = {0}; |
| 160 | + char pwd[IOTC_WIFI_PWD_MAX_LEN + 1] = {0}; | ||
| 161 | + int32_t ret = ParseNetCfgWifiInfo(json, ssid, sizeof(ssid), pwd, sizeof(pwd)); | ||
| 93 | if (ret != IOTC_OK) { | 162 | if (ret != IOTC_OK) { |
| 94 | - IOTC_LOGW("save net info error %d", ret); | 163 | + goto EXIT; |
| 95 | - return ret; | ||
| 96 | } | 164 | } |
| 97 | 165 | ||
| 166 | + SnapshotNetInfo(); | ||
| 98 | ret = ConfigSetNetInfoNeedVerify(); | 167 | ret = ConfigSetNetInfoNeedVerify(); |
| 99 | if (ret != IOTC_OK) { | 168 | if (ret != IOTC_OK) { |
| 100 | IOTC_LOGW("set net info verify flag error %d", ret); | 169 | IOTC_LOGW("set net info verify flag error %d", ret); |
| 101 | - /* 不退出仅告警 因为verify flag不影响wifi连接,且此处退出会有wifi数据残留 */ | 170 | + goto ROLLBACK; |
| 171 | + } | ||
| 172 | + | ||
| 173 | + ret = IotcSetWifiInfo((const uint8_t *)ssid, strlen(ssid), (const uint8_t *)pwd, strlen(pwd)); | ||
| 174 | + if (ret != IOTC_OK) { | ||
| 175 | + IOTC_LOGW("save net info error %d", ret); | ||
| 176 | + goto ROLLBACK; | ||
| 102 | } | 177 | } |
| 103 | 178 | ||
| 104 | if (IotcGetWifiMode() != IOTC_WIFI_MODE_SOFTAP) { | 179 | if (IotcGetWifiMode() != IOTC_WIFI_MODE_SOFTAP) { |
| 105 | /* 异步执行,避免阻塞状态码响应 */ | 180 | /* 异步执行,避免阻塞状态码响应 */ |
| 106 | - int32_t delayTimer = SchedTimerAdd(EVENT_SOURCE_TIMER_TYPE_ONCE, DelayConnetWifiCb, | 181 | + g_netInfoTransaction.delayTimer = SchedTimerAdd(EVENT_SOURCE_TIMER_TYPE_ONCE, DelayConnetWifiCb, |
| 107 | DELAY_CONNECT_WIFI_INTERVAL, NULL); | 182 | DELAY_CONNECT_WIFI_INTERVAL, NULL); |
| 108 | - if (delayTimer < 0) { | 183 | + if (g_netInfoTransaction.delayTimer < 0) { |
| 109 | - IOTC_LOGW("connect wifi timer start error %d", delayTimer); | 184 | + ret = g_netInfoTransaction.delayTimer; |
| 110 | - return delayTimer; | 185 | + IOTC_LOGW("connect wifi timer start error %d", ret); |
| 186 | + goto ROLLBACK; | ||
| 111 | } | 187 | } |
| 112 | } else { | 188 | } else { |
| 113 | ret = EventBusSubscribe(SoftapStopEventCallback, IOTC_CORE_WIFI_EVENT_SOFTAP_STOP); | 189 | ret = EventBusSubscribe(SoftapStopEventCallback, IOTC_CORE_WIFI_EVENT_SOFTAP_STOP); |
| 114 | if (ret != IOTC_OK) { | 190 | if (ret != IOTC_OK) { |
| 115 | IOTC_LOGW("sub softap stop event error"); | 191 | IOTC_LOGW("sub softap stop event error"); |
| 116 | - return ret; | 192 | + goto ROLLBACK; |
| 117 | } | 193 | } |
| 194 | + g_netInfoTransaction.softapSubscribed = true; | ||
| 118 | } | 195 | } |
| 119 | - | 196 | + (void)memset_s(ssid, sizeof(ssid), 0, sizeof(ssid)); |
| 197 | + (void)memset_s(pwd, sizeof(pwd), 0, sizeof(pwd)); | ||
| 198 | + return IOTC_OK; | ||
| 199 | + | ||
| 200 | +ROLLBACK: | ||
| 201 | + (void)RestoreNetInfo(); | ||
| 202 | + ClearNetInfoTransaction(); | ||
| 203 | +EXIT: | ||
| 204 | + (void)memset_s(ssid, sizeof(ssid), 0, sizeof(ssid)); | ||
| 205 | + (void)memset_s(pwd, sizeof(pwd), 0, sizeof(pwd)); | ||
| 206 | + return ret; | ||
| 207 | +} | ||
| 208 | + | ||
| 209 | +int32_t SvcConnCommitNetInfo(void) | ||
| 210 | +{ | ||
| 211 | + CHECK_RETURN(g_netInfoTransaction.active, IOTC_CORE_DEVICE_ERR_STORE_STATE); | ||
| 212 | + ClearNetInfoTransaction(); | ||
| 120 | return IOTC_OK; | 213 | return IOTC_OK; |
| 121 | } | 214 | } |
| 215 | + | ||
| 216 | +int32_t SvcConnRollbackNetInfo(void) | ||
| 217 | +{ | ||
| 218 | + if (!g_netInfoTransaction.active) { | ||
| 219 | + return IOTC_OK; | ||
| 220 | + } | ||
| 221 | + if (g_netInfoTransaction.softapSubscribed) { | ||
| 222 | + (void)EventBusUnsubscribe(SoftapStopEventCallback); | ||
| 223 | + } | ||
| 224 | + if (g_netInfoTransaction.delayTimer >= 0) { | ||
| 225 | + SchedTimerRemove(g_netInfoTransaction.delayTimer); | ||
| 226 | + } | ||
| 227 | + int32_t ret = RestoreNetInfo(); | ||
| 228 | + ClearNetInfoTransaction(); | ||
| 229 | + return ret; | ||
| 230 | +} | ||
| 231 | + | ||
| 232 | +int32_t SvcConnSetNetInfo(const IotcJson *json) | ||
| 233 | +{ | ||
| 234 | + int32_t ret = SvcConnPrepareNetInfo(json); | ||
| 235 | + CHECK_RETURN(ret == IOTC_OK, ret); | ||
| 236 | + return SvcConnCommitNetInfo(); | ||
| 237 | +} | ||
| @@ -23,7 +23,13 @@ extern "C" { | |||
| 23 | 23 | ||
| 24 | int32_t SvcConnSetNetInfo(const IotcJson *json); | 24 | int32_t SvcConnSetNetInfo(const IotcJson *json); |
| 25 | 25 | ||
| 26 | +int32_t SvcConnPrepareNetInfo(const IotcJson *json); | ||
| 27 | + | ||
| 28 | +int32_t SvcConnCommitNetInfo(void); | ||
| 29 | + | ||
| 30 | +int32_t SvcConnRollbackNetInfo(void); | ||
| 31 | + | ||
| 26 | 32 | ||
| 27 | } | 33 | } |
| 28 | 34 | ||
| 29 | -#endif /* SERVICE_CONNECT_NET_INFO_H */ | 35 | +#endif /* SERVICE_CONNECT_NET_INFO_H */ |
| @@ -0,0 +1,232 @@ | |||
| 1 | +/* | ||
| 2 | + * Unified Connect 3.0 WiFi Only successful-provision broadcast. | ||
| 3 | + * Contract frozen in W1: 255.255.255.255:5683, POST /onlinenotify, | ||
| 4 | + * SHA-256(devId) lower-case full hex, five sends at 100 ms intervals. | ||
| 5 | + */ | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + | ||
| 35 | + | ||
| 36 | + | ||
| 37 | +typedef struct { | ||
| 38 | + bool active; | ||
| 39 | + int32_t fd; | ||
| 40 | + int32_t timer; | ||
| 41 | + uint32_t successCount; | ||
| 42 | + uint32_t attemptCount; | ||
| 43 | + uint32_t packetLen; | ||
| 44 | + uint8_t packet[ONLINE_NOTIFY_PACKET_MAX_LEN]; | ||
| 45 | +} OnlineNotifyContext; | ||
| 46 | + | ||
| 47 | +static OnlineNotifyContext g_onlineNotify = { | ||
| 48 | + .fd = -1, | ||
| 49 | + .timer = EVENT_SOURCE_INVALID_TIMER_FD, | ||
| 50 | +}; | ||
| 51 | + | ||
| 52 | +int32_t SvcConnBuildOnlineNotifyPacket(const char *devId, uint16_t msgId, uint8_t *packet, uint32_t *packetLen) | ||
| 53 | +{ | ||
| 54 | + CHECK_RETURN(devId != NULL && devId[0] != '\0' && packet != NULL && packetLen != NULL && | ||
| 55 | + *packetLen > 0, IOTC_ERR_PARAM_INVALID); | ||
| 56 | + | ||
| 57 | + uint8_t hash[ONLINE_NOTIFY_HASH_LEN] = {0}; | ||
| 58 | + char hashHex[ONLINE_NOTIFY_HASH_HEX_LEN + 1] = {0}; | ||
| 59 | + int32_t ret = IotcMdCalc(IOTC_MD_SHA256, (const uint8_t *)devId, strlen(devId), hash, sizeof(hash)); | ||
| 60 | + if (ret != IOTC_OK || !UtilsHexify(hash, sizeof(hash), hashHex, sizeof(hashHex))) { | ||
| 61 | + (void)memset_s(hash, sizeof(hash), 0, sizeof(hash)); | ||
| 62 | + return (ret == IOTC_OK) ? IOTC_CORE_COMM_UTILS_ERR_HEXIFY : ret; | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + IotcJson *json = IotcJsonCreate(); | ||
| 66 | + if (json == NULL) { | ||
| 67 | + ret = IOTC_ADAPTER_JSON_ERR_CREATE; | ||
| 68 | + goto EXIT; | ||
| 69 | + } | ||
| 70 | + ret = IotcJsonAddStr2Obj(json, STR_JSON_DEVID, hashHex); | ||
| 71 | + if (ret != IOTC_OK) { | ||
| 72 | + IotcJsonDelete(json); | ||
| 73 | + goto EXIT; | ||
| 74 | + } | ||
| 75 | + char *payloadText = IotcJsonPrint(json); | ||
| 76 | + IotcJsonDelete(json); | ||
| 77 | + if (payloadText == NULL) { | ||
| 78 | + ret = IOTC_ADAPTER_JSON_ERR_PRINT; | ||
| 79 | + goto EXIT; | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + const uint8_t contentFormat = COAP_CONTENT_FORMAT_APPLICATION_JSON; | ||
| 83 | + const CoapOption options[] = { | ||
| 84 | + { COAP_OPTION_TYPE_URI_PATH, | ||
| 85 | + { (const uint8_t *)STR_URI_ONLINE_NOTIFY, sizeof(STR_URI_ONLINE_NOTIFY) - 1U } }, | ||
| 86 | + { COAP_OPTION_TYPE_CONTENT_FORMAT, { &contentFormat, sizeof(contentFormat) } }, | ||
| 87 | + }; | ||
| 88 | + CoapData payload = { (const uint8_t *)payloadText, strlen(payloadText) }; | ||
| 89 | + CoapBuildPacket build = { | ||
| 90 | + .header = { .type = COAP_MSG_TYPE_CON, .tkl = 0, .code = COAP_METHOD_TYPE_POST, .msgId = msgId }, | ||
| 91 | + .opNum = ARRAY_SIZE(options), | ||
| 92 | + .options = options, | ||
| 93 | + .payload = &payload, | ||
| 94 | + }; | ||
| 95 | + CoapPacket decoded = {0}; | ||
| 96 | + CoapBuffer buffer = { .buffer = packet, .len = 0, .size = *packetLen }; | ||
| 97 | + ret = CoapUdpEncode(&build, &decoded, &buffer); | ||
| 98 | + IotcJsonFreePrint(payloadText); | ||
| 99 | + if (ret == IOTC_OK) { | ||
| 100 | + *packetLen = buffer.len; | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | +EXIT: | ||
| 104 | + (void)memset_s(hash, sizeof(hash), 0, sizeof(hash)); | ||
| 105 | + (void)memset_s(hashHex, sizeof(hashHex), 0, sizeof(hashHex)); | ||
| 106 | + return ret; | ||
| 107 | +} | ||
| 108 | + | ||
| 109 | + | ||
| 110 | +static void OnlineNotifyCleanup(bool removeTimer) | ||
| 111 | +{ | ||
| 112 | + if (removeTimer && g_onlineNotify.timer >= 0) { | ||
| 113 | + SchedTimerRemove(g_onlineNotify.timer); | ||
| 114 | + } | ||
| 115 | + if (g_onlineNotify.fd >= 0) { | ||
| 116 | + IotcClose(g_onlineNotify.fd); | ||
| 117 | + } | ||
| 118 | + (void)memset_s(&g_onlineNotify, sizeof(g_onlineNotify), 0, sizeof(g_onlineNotify)); | ||
| 119 | + g_onlineNotify.fd = -1; | ||
| 120 | + g_onlineNotify.timer = EVENT_SOURCE_INVALID_TIMER_FD; | ||
| 121 | +} | ||
| 122 | + | ||
| 123 | +static int32_t OnlineNotifySendOnce(void) | ||
| 124 | +{ | ||
| 125 | + IotcSockaddrIn target = { | ||
| 126 | + .sinFamily = IOTC_SOCKET_DOMAIN_AF_INET, | ||
| 127 | + .sinPort = IotcHtons(ONLINE_NOTIFY_PORT), | ||
| 128 | + .sinAddr = IotcInetAddr(ONLINE_NOTIFY_ADDR), | ||
| 129 | + }; | ||
| 130 | + ++g_onlineNotify.attemptCount; | ||
| 131 | + int32_t sent = IotcSendTo(g_onlineNotify.fd, g_onlineNotify.packet, g_onlineNotify.packetLen, | ||
| 132 | + (const IotcSockaddr *)&target, sizeof(target)); | ||
| 133 | + if (sent == (int32_t)g_onlineNotify.packetLen) { | ||
| 134 | + ++g_onlineNotify.successCount; | ||
| 135 | + return IOTC_OK; | ||
| 136 | + } | ||
| 137 | + IOTC_LOGW("online notify send error %d/%u", sent, g_onlineNotify.packetLen); | ||
| 138 | + return IOTC_ERROR; | ||
| 139 | +} | ||
| 140 | + | ||
| 141 | +static void OnlineNotifyTimerCallback(int32_t id, void *userData) | ||
| 142 | +{ | ||
| 143 | + NOT_USED(id); | ||
| 144 | + NOT_USED(userData); | ||
| 145 | + if (!g_onlineNotify.active) { | ||
| 146 | + return; | ||
| 147 | + } | ||
| 148 | + | ||
| 149 | + (void)OnlineNotifySendOnce(); | ||
| 150 | + if (g_onlineNotify.successCount >= ONLINE_NOTIFY_REQUIRED_SENDS) { | ||
| 151 | + int32_t ret = IotcDssMarkOnlineNotifyDone(); | ||
| 152 | + if (ret != IOTC_OK) { | ||
| 153 | + IOTC_LOGW("mark online notify done error %d", ret); | ||
| 154 | + } | ||
| 155 | + OnlineNotifyCleanup(true); | ||
| 156 | + } else if (g_onlineNotify.attemptCount >= ONLINE_NOTIFY_MAX_ATTEMPTS) { | ||
| 157 | + IOTC_LOGW("online notify retry exhausted %u/%u", g_onlineNotify.successCount, | ||
| 158 | + g_onlineNotify.attemptCount); | ||
| 159 | + OnlineNotifyCleanup(true); | ||
| 160 | + } | ||
| 161 | +} | ||
| 162 | + | ||
| 163 | +int32_t SvcConnOnlineNotifyStart(void) | ||
| 164 | +{ | ||
| 165 | + if (g_onlineNotify.active) { | ||
| 166 | + return IOTC_OK; | ||
| 167 | + } | ||
| 168 | + | ||
| 169 | + IotcDssProvisionPending pending = {0}; | ||
| 170 | + int32_t ret = IotcDssLoadProvisionPending(&pending); | ||
| 171 | + if (ret == IOTC_CORE_DEVICE_ERR_STORE_NOT_FOUND || | ||
| 172 | + (ret == IOTC_OK && pending.onlineNotifyDone != 0)) { | ||
| 173 | + (void)memset_s(&pending, sizeof(pending), 0, sizeof(pending)); | ||
| 174 | + return IOTC_OK; | ||
| 175 | + } | ||
| 176 | + if (ret != IOTC_OK || pending.tempDevId[0] == '\0') { | ||
| 177 | + (void)memset_s(&pending, sizeof(pending), 0, sizeof(pending)); | ||
| 178 | + return (ret == IOTC_OK) ? IOTC_ERR_PARAM_INVALID : ret; | ||
| 179 | + } | ||
| 180 | + | ||
| 181 | + uint16_t msgId = 0; | ||
| 182 | + ret = SecurityRandom((uint8_t *)&msgId, sizeof(msgId)); | ||
| 183 | + if (ret == IOTC_OK) { | ||
| 184 | + g_onlineNotify.packetLen = sizeof(g_onlineNotify.packet); | ||
| 185 | + ret = SvcConnBuildOnlineNotifyPacket(pending.tempDevId, msgId, g_onlineNotify.packet, | ||
| 186 | + &g_onlineNotify.packetLen); | ||
| 187 | + } | ||
| 188 | + (void)memset_s(&pending, sizeof(pending), 0, sizeof(pending)); | ||
| 189 | + if (ret != IOTC_OK) { | ||
| 190 | + OnlineNotifyCleanup(false); | ||
| 191 | + return ret; | ||
| 192 | + } | ||
| 193 | + | ||
| 194 | + g_onlineNotify.fd = IotcSocket(IOTC_SOCKET_DOMAIN_AF_INET, IOTC_SOCKET_TYPE_DGRAM, | ||
| 195 | + IOTC_SOCKET_PROTO_UDP); | ||
| 196 | + if (g_onlineNotify.fd < 0) { | ||
| 197 | + ret = g_onlineNotify.fd; | ||
| 198 | + OnlineNotifyCleanup(false); | ||
| 199 | + return ret; | ||
| 200 | + } | ||
| 201 | + ret = IotcSetSocketOpt(g_onlineNotify.fd, IOTC_SOCKET_OPTION_ENABLE_BROADCAST, NULL, 0); | ||
| 202 | + if (ret != IOTC_OK) { | ||
| 203 | + OnlineNotifyCleanup(false); | ||
| 204 | + return ret; | ||
| 205 | + } | ||
| 206 | + | ||
| 207 | + g_onlineNotify.active = true; | ||
| 208 | + (void)OnlineNotifySendOnce(); | ||
| 209 | + g_onlineNotify.timer = SchedTimerAdd(EVENT_SOURCE_TIMER_TYPE_REPEAT, OnlineNotifyTimerCallback, | ||
| 210 | + ONLINE_NOTIFY_INTERVAL_MS, NULL); | ||
| 211 | + if (g_onlineNotify.timer < 0) { | ||
| 212 | + ret = g_onlineNotify.timer; | ||
| 213 | + OnlineNotifyCleanup(false); | ||
| 214 | + return ret; | ||
| 215 | + } | ||
| 216 | + return IOTC_OK; | ||
| 217 | +} | ||
| 218 | + | ||
| 219 | +void SvcConnOnlineNotifyStop(void) | ||
| 220 | +{ | ||
| 221 | + OnlineNotifyCleanup(true); | ||
| 222 | +} | ||
| 223 | + | ||
| 224 | +int32_t SvcConnOnlineNotifyStart(void) | ||
| 225 | +{ | ||
| 226 | + return IOTC_OK; | ||
| 227 | +} | ||
| 228 | + | ||
| 229 | +void SvcConnOnlineNotifyStop(void) | ||
| 230 | +{ | ||
| 231 | +} | ||
| 232 | + | ||
| @@ -0,0 +1,22 @@ | |||
| 1 | +/* WiFi Only /onlinenotify packet builder and finite broadcaster. */ | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | +extern "C" { | ||
| 10 | + | ||
| 11 | + | ||
| 12 | +int32_t SvcConnBuildOnlineNotifyPacket(const char *devId, uint16_t msgId, uint8_t *packet, uint32_t *packetLen); | ||
| 13 | + | ||
| 14 | +int32_t SvcConnOnlineNotifyStart(void); | ||
| 15 | + | ||
| 16 | +void SvcConnOnlineNotifyStop(void); | ||
| 17 | + | ||
| 18 | + | ||
| 19 | +} | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| @@ -30,6 +30,10 @@ | |||
| 30 | 30 | ||
| 31 | 31 | ||
| 32 | 32 | ||
| 33 | + | ||
| 34 | + | ||
| 35 | + | ||
| 36 | + | ||
| 33 | 37 | ||
| 34 | static const char *NET_CONNECT_SERVICE_NAME = "CONNECT"; | 38 | static const char *NET_CONNECT_SERVICE_NAME = "CONNECT"; |
| 35 | 39 | ||
| @@ -38,6 +42,7 @@ static void RevokeEventWifiInfoCallback(uint32_t event, void *param, uint32_t le | |||
| 38 | NOT_USED(event); | 42 | NOT_USED(event); |
| 39 | NOT_USED(param); | 43 | NOT_USED(param); |
| 40 | NOT_USED(len); | 44 | NOT_USED(len); |
| 45 | + SvcConnOnlineNotifyStop(); | ||
| 41 | if (!ConfigIsNetInfoNeedVerify() && !ConfigIsNetInfoChecked()) { | 46 | if (!ConfigIsNetInfoNeedVerify() && !ConfigIsNetInfoChecked()) { |
| 42 | IOTC_LOGI("no wifi info need clear"); | 47 | IOTC_LOGI("no wifi info need clear"); |
| 43 | return; | 48 | return; |
| @@ -76,6 +81,7 @@ static int32_t StopWifiConnectService(int32_t instanceId, ServiceParam *param) | |||
| 76 | if (ctx->onFinish != NULL) { | 81 | if (ctx->onFinish != NULL) { |
| 77 | ctx->onFinish(instanceId); | 82 | ctx->onFinish(instanceId); |
| 78 | } | 83 | } |
| 84 | + SvcConnOnlineNotifyStop(); | ||
| 79 | ConnSvcCtxDeinit(); | 85 | ConnSvcCtxDeinit(); |
| 80 | ConnectConfigInfoDeinit(); | 86 | ConnectConfigInfoDeinit(); |
| 81 | (void)EventBusUnsubscribe(RevokeEventWifiInfoCallback); | 87 | (void)EventBusUnsubscribe(RevokeEventWifiInfoCallback); |
| @@ -96,13 +102,22 @@ static int32_t StartWifiConnectService(int32_t instanceId, ServiceFinishCallback | |||
| 96 | return ret; | 102 | return ret; |
| 97 | } | 103 | } |
| 98 | 104 | ||
| 99 | - if (ConfigIsNetInfoNeedVerify() || ConfigIsNetInfoChecked()) { | 105 | +#if IOTC_CONF_WIFI_ONLY_SUPPORT |
| 100 | - ret = ConnSvcActionConnectWifi(); | 106 | + if (ConfigIsNetInfoNeedVerify()) { |
| 107 | + IotcDssProvisionPending pending = {0}; | ||
| 108 | + ret = IotcDssLoadProvisionPending(&pending); | ||
| 109 | + (void)memset_s(&pending, sizeof(pending), 0, sizeof(pending)); | ||
| 101 | if (ret != IOTC_OK) { | 110 | if (ret != IOTC_OK) { |
| 102 | - IOTC_LOGW("conn svc connect wifi error %d", ret); | 111 | + /* HubSetup 在 Wi-Fi 写入后、pending commit 前断电:不得用半套配置连网。 */ |
| 103 | - return ret; | 112 | + IOTC_LOGW("discard incomplete HubSetup Wi-Fi state %d", ret); |
| 113 | + (void)IotcDeleteWifiInfo(); | ||
| 114 | + ret = ConfigClearNetInfoFlag(); | ||
| 115 | + if (ret != IOTC_OK) { | ||
| 116 | + return ret; | ||
| 117 | + } | ||
| 104 | } | 118 | } |
| 105 | } | 119 | } |
| 120 | + | ||
| 106 | 121 | ||
| 107 | ret = EventBusSubscribe(RevokeEventWifiInfoCallback, IOTC_CORE_COMM_EVENT_MAIN_RESTORE); | 122 | ret = EventBusSubscribe(RevokeEventWifiInfoCallback, IOTC_CORE_COMM_EVENT_MAIN_RESTORE); |
| 108 | if (ret != IOTC_OK) { | 123 | if (ret != IOTC_OK) { |
| @@ -116,6 +131,27 @@ static int32_t StartWifiConnectService(int32_t instanceId, ServiceFinishCallback | |||
| 116 | return ret; | 131 | return ret; |
| 117 | } | 132 | } |
| 118 | 133 | ||
| 134 | + if (ConfigIsNetInfoNeedVerify() || ConfigIsNetInfoChecked()) { | ||
| 135 | + | ||
| 136 | + /* | ||
| 137 | + * WS63 reports DEVICE_SVC_START before RF/Wi-Fi platform init has | ||
| 138 | + * necessarily completed. Defer the first STA attempt so a transient | ||
| 139 | + * sta_start failure cannot abort the CONNECT service at cold boot. | ||
| 140 | + */ | ||
| 141 | + ret = ConnSvcScheduleInitialConnect(); | ||
| 142 | + if (ret != IOTC_OK) { | ||
| 143 | + IOTC_LOGW("schedule initial wifi connect error %d", ret); | ||
| 144 | + return ret; | ||
| 145 | + } | ||
| 146 | + | ||
| 147 | + ret = ConnSvcActionConnectWifi(); | ||
| 148 | + if (ret != IOTC_OK) { | ||
| 149 | + IOTC_LOGW("conn svc connect wifi error %d", ret); | ||
| 150 | + return ret; | ||
| 151 | + } | ||
| 152 | + | ||
| 153 | + } | ||
| 154 | + | ||
| 119 | return IOTC_OK; | 155 | return IOTC_OK; |
| 120 | } | 156 | } |
| 121 | 157 | ||
| @@ -158,7 +194,8 @@ int32_t WifiServiceConnectInit(void) | |||
| 158 | 194 | ||
| 159 | void WifiServiceConnectDeinit(void) | 195 | void WifiServiceConnectDeinit(void) |
| 160 | { | 196 | { |
| 197 | + SvcConnOnlineNotifyStop(); | ||
| 161 | ConnectConfigInfoDeinit(); | 198 | ConnectConfigInfoDeinit(); |
| 162 | ConnSvcCtxDeinit(); | 199 | ConnSvcCtxDeinit(); |
| 163 | ServiceManagerUnregisterInstance(IOTC_SERVICE_ID_WIFI_CONNECT); | 200 | ServiceManagerUnregisterInstance(IOTC_SERVICE_ID_WIFI_CONNECT); |
| 164 | -} | 201 | +} |
| @@ -36,6 +36,7 @@ | |||
| 36 | 36 | ||
| 37 | 37 | ||
| 38 | 38 | ||
| 39 | + | ||
| 39 | 40 | ||
| 40 | 41 | ||
| 41 | 42 | ||
| @@ -132,6 +133,51 @@ static int32_t NetCfgInfoRecvProcess(const char *netInfo, uint32_t len) | |||
| 132 | return IOTC_OK; | 133 | return IOTC_OK; |
| 133 | } | 134 | } |
| 134 | 135 | ||
| 136 | +static int32_t HubSetupInfoRecvProcess(const char *netInfo, uint32_t len) | ||
| 137 | +{ | ||
| 138 | + IotcJson *jsonObj = IotcJsonParseWithLen(netInfo, len); | ||
| 139 | + if (jsonObj == NULL) { | ||
| 140 | + IOTC_LOGW("parse json error"); | ||
| 141 | + return IOTC_ADAPTER_JSON_ERR_PARSE; | ||
| 142 | + } | ||
| 143 | + IotcJson *dataObj = IotcJsonGetObj(jsonObj, STR_JSON_DATA); | ||
| 144 | + if (dataObj == NULL) { | ||
| 145 | + IotcJsonDelete(jsonObj); | ||
| 146 | + IOTC_LOGW("get json data error"); | ||
| 147 | + return IOTC_ADAPTER_JSON_ERR_GET_OBJ; | ||
| 148 | + } | ||
| 149 | + | ||
| 150 | + bool alreadyCommitted = false; | ||
| 151 | + int32_t ret = DevSvcProxyPrepareHubBindInfo(dataObj, &alreadyCommitted); | ||
| 152 | + if (ret != IOTC_OK) { | ||
| 153 | + IotcJsonDelete(jsonObj); | ||
| 154 | + IOTC_LOGW("prepare hub bind info error %d", ret); | ||
| 155 | + return ret; | ||
| 156 | + } | ||
| 157 | + if (alreadyCommitted) { | ||
| 158 | + ret = SvcConnSetNetInfo(dataObj); | ||
| 159 | + IotcJsonDelete(jsonObj); | ||
| 160 | + return ret; | ||
| 161 | + } | ||
| 162 | + | ||
| 163 | + ret = SvcConnPrepareNetInfo(dataObj); | ||
| 164 | + if (ret != IOTC_OK) { | ||
| 165 | + DevSvcProxyAbortHubBindInfo(); | ||
| 166 | + IotcJsonDelete(jsonObj); | ||
| 167 | + return ret; | ||
| 168 | + } | ||
| 169 | + ret = DevSvcProxyCommitHubBindInfo(); | ||
| 170 | + if (ret != IOTC_OK) { | ||
| 171 | + DevSvcProxyAbortHubBindInfo(); | ||
| 172 | + SvcConnRollbackNetInfo(); | ||
| 173 | + IotcJsonDelete(jsonObj); | ||
| 174 | + return ret; | ||
| 175 | + } | ||
| 176 | + ret = SvcConnCommitNetInfo(); | ||
| 177 | + IotcJsonDelete(jsonObj); | ||
| 178 | + return ret; | ||
| 179 | +} | ||
| 180 | + | ||
| 135 | static void CloseSoftapService(int32_t id, void *userData) | 181 | static void CloseSoftapService(int32_t id, void *userData) |
| 136 | { | 182 | { |
| 137 | NOT_USED(id); | 183 | NOT_USED(id); |
| @@ -142,9 +188,9 @@ static void CloseSoftapService(int32_t id, void *userData) | |||
| 142 | } | 188 | } |
| 143 | } | 189 | } |
| 144 | 190 | ||
| 145 | -void SoftapCoapSetupReqHandler(CoapEndpoint *endpoint, const CoapPacket *req, const SocketAddr *addr, void *userData) | 191 | +static void SoftapCoapSetupReqHandlerInner(CoapEndpoint *endpoint, const CoapPacket *req, |
| 192 | + const SocketAddr *addr, bool standardHubSetup) | ||
| 146 | { | 193 | { |
| 147 | - NOT_USED(userData); | ||
| 148 | CHECK_V_RETURN_LOGW(endpoint != NULL && req != NULL && addr != NULL, "invalid param"); | 194 | CHECK_V_RETURN_LOGW(endpoint != NULL && req != NULL && addr != NULL, "invalid param"); |
| 149 | SoftapServiceContext *ctx = GetSoftapServiceContext(); | 195 | SoftapServiceContext *ctx = GetSoftapServiceContext(); |
| 150 | if (ctx == NULL || UTILS_IS_BIT_SET(ctx->bitMap, SOFTAP_CTX_BIT_MAP_NETCFG_RECVED)) { | 196 | if (ctx == NULL || UTILS_IS_BIT_SET(ctx->bitMap, SOFTAP_CTX_BIT_MAP_NETCFG_RECVED)) { |
| @@ -152,14 +198,16 @@ void SoftapCoapSetupReqHandler(CoapEndpoint *endpoint, const CoapPacket *req, co | |||
| 152 | return; | 198 | return; |
| 153 | } | 199 | } |
| 154 | 200 | ||
| 155 | - bool isNetCfgFail = false; | 201 | + bool netCfgDone = false; |
| 156 | - int32_t ret = NetCfgInfoRecvProcess((const char *)req->payload.data, req->payload.len); | 202 | + int32_t ret = standardHubSetup ? |
| 203 | + HubSetupInfoRecvProcess((const char *)req->payload.data, req->payload.len) : | ||
| 204 | + NetCfgInfoRecvProcess((const char *)req->payload.data, req->payload.len); | ||
| 157 | if (ret != IOTC_OK) { | 205 | if (ret != IOTC_OK) { |
| 158 | IOTC_LOGE("net cfg info process error %d", ret); | 206 | IOTC_LOGE("net cfg info process error %d", ret); |
| 159 | } else { | 207 | } else { |
| 160 | IOTC_LOGN("net cfg info process ok"); | 208 | IOTC_LOGN("net cfg info process ok"); |
| 161 | UTILS_BIT_SET(ctx->bitMap, SOFTAP_CTX_BIT_MAP_NETCFG_RECVED); | 209 | UTILS_BIT_SET(ctx->bitMap, SOFTAP_CTX_BIT_MAP_NETCFG_RECVED); |
| 162 | - isNetCfgFail = true; | 210 | + netCfgDone = true; |
| 163 | } | 211 | } |
| 164 | 212 | ||
| 165 | IotcJson *respJson = UtilsJsonCreateErrcode(ret); | 213 | IotcJson *respJson = UtilsJsonCreateErrcode(ret); |
| @@ -188,7 +236,7 @@ void SoftapCoapSetupReqHandler(CoapEndpoint *endpoint, const CoapPacket *req, co | |||
| 188 | if (ret != IOTC_OK) { | 236 | if (ret != IOTC_OK) { |
| 189 | IOTC_LOGW("send coap resp msg error %d", ret); | 237 | IOTC_LOGW("send coap resp msg error %d", ret); |
| 190 | } | 238 | } |
| 191 | - CHECK_V_RETURN(isNetCfgFail); | 239 | + CHECK_V_RETURN(netCfgDone); |
| 192 | if (ctx->stopTimerFd < 0) { | 240 | if (ctx->stopTimerFd < 0) { |
| 193 | ctx->stopTimerFd = SchedTimerAdd(EVENT_SOURCE_TIMER_TYPE_ONCE, CloseSoftapService, RETRANS_WAIT_TIME_MS, NULL); | 241 | ctx->stopTimerFd = SchedTimerAdd(EVENT_SOURCE_TIMER_TYPE_ONCE, CloseSoftapService, RETRANS_WAIT_TIME_MS, NULL); |
| 194 | if (ctx->stopTimerFd < 0) { | 242 | if (ctx->stopTimerFd < 0) { |
| @@ -198,6 +246,19 @@ void SoftapCoapSetupReqHandler(CoapEndpoint *endpoint, const CoapPacket *req, co | |||
| 198 | } | 246 | } |
| 199 | } | 247 | } |
| 200 | 248 | ||
| 249 | +void SoftapCoapSetupReqHandler(CoapEndpoint *endpoint, const CoapPacket *req, const SocketAddr *addr, void *userData) | ||
| 250 | +{ | ||
| 251 | + NOT_USED(userData); | ||
| 252 | + SoftapCoapSetupReqHandlerInner(endpoint, req, addr, false); | ||
| 253 | +} | ||
| 254 | + | ||
| 255 | +void SoftapCoapHubSetupReqHandler(CoapEndpoint *endpoint, const CoapPacket *req, | ||
| 256 | + const SocketAddr *addr, void *userData) | ||
| 257 | +{ | ||
| 258 | + NOT_USED(userData); | ||
| 259 | + SoftapCoapSetupReqHandlerInner(endpoint, req, addr, true); | ||
| 260 | +} | ||
| 261 | + | ||
| 201 | static bool SessMsgProcessCheck(SessMsg *msg, UtilsBuffer *buf, SessAddtlInfo *info) | 262 | static bool SessMsgProcessCheck(SessMsg *msg, UtilsBuffer *buf, SessAddtlInfo *info) |
| 202 | { | 263 | { |
| 203 | CHECK_RETURN(msg != NULL && buf != NULL && buf->buffer != NULL && buf->len != 0 && buf->size >= buf->len && | 264 | CHECK_RETURN(msg != NULL && buf != NULL && buf->buffer != NULL && buf->len != 0 && buf->size >= buf->len && |
| @@ -547,4 +608,4 @@ void SoftapCoapE2eCtrlHandler(CoapEndpoint *endpoint, const CoapPacket *req, con | |||
| 547 | if (ret != IOTC_OK) { | 608 | if (ret != IOTC_OK) { |
| 548 | IOTC_LOGW("send e2e ctrl resp msg error %d", ret); | 609 | IOTC_LOGW("send e2e ctrl resp msg error %d", ret); |
| 549 | } | 610 | } |
| 550 | -} | 611 | +} |
| @@ -26,6 +26,9 @@ void SoftapCoapSpekeReqHandler(CoapEndpoint *endpoint, const CoapPacket *req, co | |||
| 26 | 26 | ||
| 27 | void SoftapCoapSetupReqHandler(CoapEndpoint *endpoint, const CoapPacket *req, const SocketAddr *addr, void *userData); | 27 | void SoftapCoapSetupReqHandler(CoapEndpoint *endpoint, const CoapPacket *req, const SocketAddr *addr, void *userData); |
| 28 | 28 | ||
| 29 | +void SoftapCoapHubSetupReqHandler(CoapEndpoint *endpoint, const CoapPacket *req, | ||
| 30 | + const SocketAddr *addr, void *userData); | ||
| 31 | + | ||
| 29 | void SoftapCoapE2eCtrlHandler(CoapEndpoint *endpoint, const CoapPacket *req, const SocketAddr *addr, void *userData); | 32 | void SoftapCoapE2eCtrlHandler(CoapEndpoint *endpoint, const CoapPacket *req, const SocketAddr *addr, void *userData); |
| 30 | 33 | ||
| 31 | SessCode SoftapCoapMsgRecvPreProcess(SessMsg *msg, UtilsBuffer *buf, SessAddtlInfo *info); | 34 | SessCode SoftapCoapMsgRecvPreProcess(SessMsg *msg, UtilsBuffer *buf, SessAddtlInfo *info); |
| @@ -44,4 +47,4 @@ SessCode SoftapCoapMsgSendFinalProcess(SessMsg *msg, UtilsBuffer *buf, SessAddtl | |||
| 44 | } | 47 | } |
| 45 | 48 | ||
| 46 | 49 | ||
| 47 | -#endif /* NETCFG_SOFTAP_METHOD_H */ | 50 | +#endif /* NETCFG_SOFTAP_METHOD_H */ |
| @@ -24,6 +24,86 @@ | |||
| 24 | 24 | ||
| 25 | 25 | ||
| 26 | 26 | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + | ||
| 35 | +static IotcJson *SoftapBuildDeviceInfoResp(void) | ||
| 36 | +{ | ||
| 37 | + IotcJson *respJson = UtilsJsonCreateErrcode(IOTC_OK); | ||
| 38 | + if (respJson == NULL) { | ||
| 39 | + return NULL; | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + DevAuthInfo authInfo = {0}; | ||
| 43 | + bool isAuthInfoExist = false; | ||
| 44 | + int32_t ret = DevSvcProxyGetAuthInfo(&isAuthInfoExist, &authInfo); | ||
| 45 | + if (ret != IOTC_OK || | ||
| 46 | + IotcJsonAddStr2Obj(respJson, STR_JSON_DEV_ID, isAuthInfoExist ? authInfo.devId : "") != IOTC_OK) { | ||
| 47 | + (void)memset_s(&authInfo, sizeof(authInfo), 0, sizeof(authInfo)); | ||
| 48 | + IotcJsonDelete(respJson); | ||
| 49 | + return NULL; | ||
| 50 | + } | ||
| 51 | + (void)memset_s(&authInfo, sizeof(authInfo), 0, sizeof(authInfo)); | ||
| 52 | + | ||
| 53 | + IotcJson *devInfoJson = MdlBuildDevInfoJson(ModelGetDevInfo()); | ||
| 54 | + if (devInfoJson == NULL) { | ||
| 55 | + IotcJsonDelete(respJson); | ||
| 56 | + return NULL; | ||
| 57 | + } | ||
| 58 | + if (IotcJsonReplaceStr2Obj(devInfoJson, STR_JSON_HIV, SOFTAP_DEVICE_INFO_HIV) != IOTC_OK) { | ||
| 59 | + IotcJsonDelete(devInfoJson); | ||
| 60 | + IotcJsonDelete(respJson); | ||
| 61 | + return NULL; | ||
| 62 | + } | ||
| 63 | + char mac[MAC_STR_MAX_LEN] = {0}; | ||
| 64 | + ret = GetWifiMacAddrStr(mac, sizeof(mac)); | ||
| 65 | + if (ret != IOTC_OK || IotcJsonAddStr2Obj(devInfoJson, STR_JSON_MAC, mac) != IOTC_OK) { | ||
| 66 | + IotcJsonDelete(devInfoJson); | ||
| 67 | + IotcJsonDelete(respJson); | ||
| 68 | + return NULL; | ||
| 69 | + } | ||
| 70 | + ret = IotcJsonAddItem2Obj(respJson, STR_JSON_DEV_INFO, devInfoJson); | ||
| 71 | + if (ret != IOTC_OK) { | ||
| 72 | + IotcJsonDelete(devInfoJson); | ||
| 73 | + IotcJsonDelete(respJson); | ||
| 74 | + return NULL; | ||
| 75 | + } | ||
| 76 | + return respJson; | ||
| 77 | +} | ||
| 78 | + | ||
| 79 | +void SoftapCoapDeviceInfoReqHandler(CoapEndpoint *endpoint, const CoapPacket *req, | ||
| 80 | + const SocketAddr *addr, void *userData) | ||
| 81 | +{ | ||
| 82 | + NOT_USED(userData); | ||
| 83 | + CHECK_V_RETURN_LOGW(endpoint != NULL && req != NULL && addr != NULL, "invalid param"); | ||
| 84 | + if (req->payload.len != 0) { | ||
| 85 | + IOTC_LOGW("DeviceInfo request payload must be empty, len=%u", req->payload.len); | ||
| 86 | + return; | ||
| 87 | + } | ||
| 88 | + IOTC_LOGI("DeviceInfo request payload len=0, hiv=%s", SOFTAP_DEVICE_INFO_HIV); | ||
| 89 | + | ||
| 90 | + IotcJson *respJson = SoftapBuildDeviceInfoResp(); | ||
| 91 | + if (respJson == NULL) { | ||
| 92 | + IOTC_LOGW("build DeviceInfo response error"); | ||
| 93 | + return; | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + CoapServerRespParam respParam; | ||
| 97 | + int32_t ret = CoapServerBuildDefaultRespParam(&respParam, req, respJson); | ||
| 98 | + if (ret == IOTC_OK) { | ||
| 99 | + CoapPacket packet; | ||
| 100 | + ret = CoapServerSendResp(endpoint, &respParam, addr, &packet); | ||
| 101 | + } | ||
| 102 | + IotcJsonDelete(respJson); | ||
| 103 | + if (ret != IOTC_OK) { | ||
| 104 | + IOTC_LOGW("send DeviceInfo response error %d", ret); | ||
| 105 | + } | ||
| 106 | +} | ||
| 27 | 107 | ||
| 28 | void SoftapCoapGetOemProvisionRequestReqHandler(CoapEndpoint *endpoint, const CoapPacket *req, | 108 | void SoftapCoapGetOemProvisionRequestReqHandler(CoapEndpoint *endpoint, const CoapPacket *req, |
| 29 | const SocketAddr *addr, void *userData) | 109 | const SocketAddr *addr, void *userData) |
| @@ -37,8 +37,11 @@ void SoftapCoapProvisionCertReqHandler(CoapEndpoint *endpoint, const CoapPacket | |||
| 37 | void SoftapCoapProvisionPskReqHandler(CoapEndpoint *endpoint, const CoapPacket *req, | 37 | void SoftapCoapProvisionPskReqHandler(CoapEndpoint *endpoint, const CoapPacket *req, |
| 38 | const SocketAddr *addr, void *userData); | 38 | const SocketAddr *addr, void *userData); |
| 39 | 39 | ||
| 40 | +void SoftapCoapDeviceInfoReqHandler(CoapEndpoint *endpoint, const CoapPacket *req, | ||
| 41 | + const SocketAddr *addr, void *userData); | ||
| 42 | + | ||
| 40 | 43 | ||
| 41 | } | 44 | } |
| 42 | 45 | ||
| 43 | 46 | ||
| 44 | -#endif /* SVC_SOFTAP_COAP_PROVISION_H */ | 47 | +#endif /* SVC_SOFTAP_COAP_PROVISION_H */ |
| @@ -21,7 +21,7 @@ | |||
| 21 | 21 | ||
| 22 | static SoftapServiceContext *g_softapCtx = NULL; | 22 | static SoftapServiceContext *g_softapCtx = NULL; |
| 23 | 23 | ||
| 24 | -SoftapServiceContext *GetSoftapServiceContext() | 24 | +SoftapServiceContext *GetSoftapServiceContext(void) |
| 25 | { | 25 | { |
| 26 | return g_softapCtx; | 26 | return g_softapCtx; |
| 27 | } | 27 | } |
| @@ -37,10 +37,14 @@ int32_t SoftapServiceContextInit(void) | |||
| 37 | g_softapCtx->tmoTimerFd = EVENT_SOURCE_INVALID_TIMER_FD; | 37 | g_softapCtx->tmoTimerFd = EVENT_SOURCE_INVALID_TIMER_FD; |
| 38 | g_softapCtx->startUpTimerFd = EVENT_SOURCE_INVALID_TIMER_FD; | 38 | g_softapCtx->startUpTimerFd = EVENT_SOURCE_INVALID_TIMER_FD; |
| 39 | g_softapCtx->stopTimerFd = EVENT_SOURCE_INVALID_TIMER_FD; | 39 | g_softapCtx->stopTimerFd = EVENT_SOURCE_INVALID_TIMER_FD; |
| 40 | + g_softapCtx->sess.staTimer = EVENT_SOURCE_INVALID_TIMER_FD; | ||
| 41 | + for (uint32_t i = 0; i < IOTC_CONF_SOFTAP_MAX_PEER_SESS_NUM; ++i) { | ||
| 42 | + g_softapCtx->sess.peerSess[i].timer = EVENT_SOURCE_INVALID_TIMER_FD; | ||
| 43 | + } | ||
| 40 | return IOTC_OK; | 44 | return IOTC_OK; |
| 41 | } | 45 | } |
| 42 | 46 | ||
| 43 | void SoftapServiceContextDeinit(void) | 47 | void SoftapServiceContextDeinit(void) |
| 44 | { | 48 | { |
| 45 | UTILS_FREE_2_NULL(g_softapCtx); | 49 | UTILS_FREE_2_NULL(g_softapCtx); |
| 46 | -} | 50 | +} |
| @@ -82,7 +82,7 @@ typedef struct { | |||
| 82 | SoftapSvcInitParam initParam; | 82 | SoftapSvcInitParam initParam; |
| 83 | } SoftapServiceContext; | 83 | } SoftapServiceContext; |
| 84 | 84 | ||
| 85 | -SoftapServiceContext *GetSoftapServiceContext(); | 85 | +SoftapServiceContext *GetSoftapServiceContext(void); |
| 86 | 86 | ||
| 87 | int32_t SoftapServiceContextInit(void); | 87 | int32_t SoftapServiceContextInit(void); |
| 88 | 88 | ||
| @@ -92,4 +92,4 @@ void SoftapServiceContextDeinit(void); | |||
| 92 | } | 92 | } |
| 93 | 93 | ||
| 94 | 94 | ||
| 95 | -#endif /* SERVICE_SOFTAP_CTX_H */ | 95 | +#endif /* SERVICE_SOFTAP_CTX_H */ |
| @@ -298,8 +298,9 @@ static int32_t SoftapSessionSetup(SoftapSess *sess) | |||
| 298 | static int32_t SoftapCoapEndpointSetup(SoftapSess *sess, const SoftapSvcInitParam *initParam) | 298 | static int32_t SoftapCoapEndpointSetup(SoftapSess *sess, const SoftapSvcInitParam *initParam) |
| 299 | { | 299 | { |
| 300 | /* uri white list for recv not decrypt or not base64 decode */ | 300 | /* uri white list for recv not decrypt or not base64 decode */ |
| 301 | - static const char *plainUri[] = {STR_URI_SPEKE}; | 301 | + /* DeviceInfo is an empty GET; its response remains encrypted. */ |
| 302 | - static const char *noBase64uri[] = {STR_URI_SPEKE}; | 302 | + static const char *plainUri[] = {STR_URI_SPEKE, STR_URI_DEVICE_INFO}; |
| 303 | + static const char *noBase64uri[] = {STR_URI_SPEKE, STR_URI_DEVICE_INFO}; | ||
| 303 | 304 | ||
| 304 | sess->plainUri = plainUri; | 305 | sess->plainUri = plainUri; |
| 305 | sess->plainUriNum = ARRAY_SIZE(plainUri); | 306 | sess->plainUriNum = ARRAY_SIZE(plainUri); |
| @@ -308,6 +309,7 @@ static int32_t SoftapCoapEndpointSetup(SoftapSess *sess, const SoftapSvcInitPara | |||
| 308 | 309 | ||
| 309 | static const CoapResource SPEKE_RES[] = { | 310 | static const CoapResource SPEKE_RES[] = { |
| 310 | {UTILS_BIT(COAP_METHOD_TYPE_POST), STR_URI_SPEKE, NULL, SoftapCoapSpekeReqHandler}, | 311 | {UTILS_BIT(COAP_METHOD_TYPE_POST), STR_URI_SPEKE, NULL, SoftapCoapSpekeReqHandler}, |
| 312 | + {UTILS_BIT(COAP_METHOD_TYPE_GET), STR_URI_DEVICE_INFO, NULL, SoftapCoapDeviceInfoReqHandler}, | ||
| 311 | {UTILS_BIT(COAP_METHOD_TYPE_POST), STR_URI_GET_OEM_PROVISION_REQUEST, NULL, | 313 | {UTILS_BIT(COAP_METHOD_TYPE_POST), STR_URI_GET_OEM_PROVISION_REQUEST, NULL, |
| 312 | SoftapCoapGetOemProvisionRequestReqHandler}, | 314 | SoftapCoapGetOemProvisionRequestReqHandler}, |
| 313 | {UTILS_BIT(COAP_METHOD_TYPE_POST), STR_URI_OEM_PROVISION, NULL, SoftapCoapOemProvisionReqHandler}, | 315 | {UTILS_BIT(COAP_METHOD_TYPE_POST), STR_URI_OEM_PROVISION, NULL, SoftapCoapOemProvisionReqHandler}, |
| @@ -316,7 +318,8 @@ static int32_t SoftapCoapEndpointSetup(SoftapSess *sess, const SoftapSvcInitPara | |||
| 316 | {UTILS_BIT(COAP_METHOD_TYPE_POST), STR_URI_PROVISION_CERT, NULL, SoftapCoapProvisionCertReqHandler}, | 318 | {UTILS_BIT(COAP_METHOD_TYPE_POST), STR_URI_PROVISION_CERT, NULL, SoftapCoapProvisionCertReqHandler}, |
| 317 | {UTILS_BIT(COAP_METHOD_TYPE_POST), STR_URI_PROVISION_PSK, NULL, SoftapCoapProvisionPskReqHandler}, | 319 | {UTILS_BIT(COAP_METHOD_TYPE_POST), STR_URI_PROVISION_PSK, NULL, SoftapCoapProvisionPskReqHandler}, |
| 318 | }; | 320 | }; |
| 319 | - static const CoapResource CLOUD_SETUP_V2_RES[] = { | 321 | + static const CoapResource NETCFG_RES[] = { |
| 322 | + {UTILS_BIT(COAP_METHOD_TYPE_POST), STR_URI_HUB_SETUP, NULL, SoftapCoapHubSetupReqHandler}, | ||
| 320 | {UTILS_BIT(COAP_METHOD_TYPE_POST), STR_URI_CLOUD_SETUP_V2, NULL, SoftapCoapSetupReqHandler}, | 323 | {UTILS_BIT(COAP_METHOD_TYPE_POST), STR_URI_CLOUD_SETUP_V2, NULL, SoftapCoapSetupReqHandler}, |
| 321 | }; | 324 | }; |
| 322 | static const CoapResource E2E_CTL_RES[] = { | 325 | static const CoapResource E2E_CTL_RES[] = { |
| @@ -330,7 +333,7 @@ static int32_t SoftapCoapEndpointSetup(SoftapSess *sess, const SoftapSvcInitPara | |||
| 330 | } | 333 | } |
| 331 | 334 | ||
| 332 | if (UTILS_IS_BIT_SET(initParam->bitMap, IOTC_WIFI_SERVICE_SOFTAP_NETCFG)) { | 335 | if (UTILS_IS_BIT_SET(initParam->bitMap, IOTC_WIFI_SERVICE_SOFTAP_NETCFG)) { |
| 333 | - ret = CoapServerAddResource(sess->coapStack.endpoint, CLOUD_SETUP_V2_RES, ARRAY_SIZE(CLOUD_SETUP_V2_RES)); | 336 | + ret = CoapServerAddResource(sess->coapStack.endpoint, NETCFG_RES, ARRAY_SIZE(NETCFG_RES)); |
| 334 | if (ret != IOTC_OK) { | 337 | if (ret != IOTC_OK) { |
| 335 | IOTC_LOGW("add cloud setup coap res error %d", ret); | 338 | IOTC_LOGW("add cloud setup coap res error %d", ret); |
| 336 | return ret; | 339 | return ret; |
| @@ -472,4 +475,8 @@ void DestroySoftapSess(SoftapSess *sess) | |||
| 472 | DestroyPeerSession(&sess->peerSess[i]); | 475 | DestroyPeerSession(&sess->peerSess[i]); |
| 473 | } | 476 | } |
| 474 | (void)memset_s(sess, sizeof(SoftapSess), 0, sizeof(SoftapSess)); | 477 | (void)memset_s(sess, sizeof(SoftapSess), 0, sizeof(SoftapSess)); |
| 475 | -} | 478 | + sess->staTimer = EVENT_SOURCE_INVALID_TIMER_FD; |
| 479 | + for (uint32_t i = 0; i < IOTC_CONF_SOFTAP_MAX_PEER_SESS_NUM; ++i) { | ||
| 480 | + sess->peerSess[i].timer = EVENT_SOURCE_INVALID_TIMER_FD; | ||
| 481 | + } | ||
| 482 | +} | ||
| @@ -86,6 +86,13 @@ iotc_core_wifi_src = [ | |||
| 86 | "$wifi_core_path/infrastructure/utils/coap_sess_utils.c", | 86 | "$wifi_core_path/infrastructure/utils/coap_sess_utils.c", |
| 87 | ] | 87 | ] |
| 88 | 88 | ||
| 89 | +if (iot_connect_wifi_only_support) { | ||
| 90 | + iotc_core_wifi_inc += [ "$wifi_core_path/application/connect" ] | ||
| 91 | + iotc_core_wifi_src += [ | ||
| 92 | + "$wifi_core_path/application/connect/svc_conn_online_notify.c", | ||
| 93 | + ] | ||
| 94 | +} | ||
| 95 | + | ||
| 89 | # Keep 2.0's Wi-Fi LAN Search and cloud implementation in the 2.0 source | 96 | # Keep 2.0's Wi-Fi LAN Search and cloud implementation in the 2.0 source |
| 90 | # set. 3.0 Wi-Fi Combo uses the Network LAN Search implementation instead. | 97 | # set. 3.0 Wi-Fi Combo uses the Network LAN Search implementation instead. |
| 91 | if (iotc_one_connect_version_code < 2) { | 98 | if (iotc_one_connect_version_code < 2) { |
| @@ -21,9 +21,13 @@ | |||
| 21 | 21 | ||
| 22 | 22 | ||
| 23 | 23 | ||
| 24 | -#include "local_ctl_svc.h" | 24 | +#include "iotc_conf.h" |
| 25 | 25 | ||
| 26 | -#if IOTC_ONECONNECT_VERSION_CODE >= 2 | 26 | +#if !IOTC_CONF_WIFI_ONLY_SUPPORT |
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 27 | 31 | ||
| 28 | 32 | ||
| 29 | 33 | ||
| @@ -35,7 +39,14 @@ | |||
| 35 | 39 | ||
| 36 | 40 | ||
| 37 | 41 | ||
| 38 | -#if IOTC_ONECONNECT_VERSION_CODE >= 2 | 42 | +#if IOTC_CONF_WIFI_ONLY_SUPPORT |
| 43 | +static const FwkInitUnit CORE_WIFI_ONLY[] = { | ||
| 44 | + {FWK_INIT_LVL_COMP, "wifi_buffer", TransBufferInit, TransBufferDeinit}, | ||
| 45 | + {FWK_INIT_LVL_FWK, "wifi_fd_watch", WifiSchedFdWatchInit, WifiSchedFdWatchDeinit}, | ||
| 46 | + {FWK_INIT_LVL_BIZ, "softap", WifiServiceSoftapInit, WifiServiceSoftapDeinit}, | ||
| 47 | + {FWK_INIT_LVL_BIZ, "wifi_connect", WifiServiceConnectInit, WifiServiceConnectDeinit}, | ||
| 48 | +}; | ||
| 49 | + | ||
| 39 | 50 | ||
| 40 | 51 | ||
| 41 | 52 | ||
| @@ -88,7 +99,9 @@ static const FwkInitUnit CORE_WIFI[] = { | |||
| 88 | int32_t CoreWifiRegisterInitItem(void) | 99 | int32_t CoreWifiRegisterInitItem(void) |
| 89 | { | 100 | { |
| 90 | int32_t ret; | 101 | int32_t ret; |
| 91 | -#if IOTC_ONECONNECT_VERSION_CODE >= 2 | 102 | +#if IOTC_CONF_WIFI_ONLY_SUPPORT |
| 103 | + ret = FwkRegInitUnitArray(CORE_WIFI_ONLY); | ||
| 104 | + | ||
| 92 | ret = FwkRegInitUnitArray(CORE_WIFI_V3); | 105 | ret = FwkRegInitUnitArray(CORE_WIFI_V3); |
| 93 | 106 | ||
| 94 | ret = FwkRegInitUnitArray(CORE_WIFI); | 107 | ret = FwkRegInitUnitArray(CORE_WIFI); |
| @@ -102,7 +115,9 @@ int32_t CoreWifiRegisterInitItem(void) | |||
| 102 | 115 | ||
| 103 | void CoreWifiUnregisterInitItem(void) | 116 | void CoreWifiUnregisterInitItem(void) |
| 104 | { | 117 | { |
| 105 | -#if IOTC_ONECONNECT_VERSION_CODE >= 2 | 118 | +#if IOTC_CONF_WIFI_ONLY_SUPPORT |
| 119 | + FwkUnregInitUnit(CORE_WIFI_ONLY); | ||
| 120 | + | ||
| 106 | FwkUnregInitUnit(CORE_WIFI_V3); | 121 | FwkUnregInitUnit(CORE_WIFI_V3); |
| 107 | 122 | ||
| 108 | FwkUnregInitUnit(CORE_WIFI); | 123 | FwkUnregInitUnit(CORE_WIFI); |
| @@ -138,10 +138,13 @@ typedef enum { | |||
| 138 | 138 | ||
| 139 | 139 | ||
| 140 | 140 | ||
| 141 | +/* Part 1 table 14: X is the SoftAP protocol version. */ | ||
| 142 | + | ||
| 143 | + | ||
| 141 | 144 | ||
| 142 | 145 | ||
| 143 | 146 | ||
| 144 | } | 147 | } |
| 145 | 148 | ||
| 146 | 149 | ||
| 147 | -#endif /* IOTC_DEFINE_H */ | 150 | +#endif /* IOTC_DEFINE_H */ |
| @@ -34,7 +34,8 @@ extern "C" { | |||
| 34 | 34 | ||
| 35 | 35 | ||
| 36 | 36 | ||
| 37 | -#define IOTC_DATE_STR_MAX_LEN 10 | 37 | +/* Part 3 5.1.1.2.10: yyyy-MM-dd HH:mm:ss. */ |
| 38 | + | ||
| 38 | 39 | ||
| 39 | 40 | ||
| 40 | /* ===== 事件参数数据结构 ===== */ | 41 | /* ===== 事件参数数据结构 ===== */ |
| @@ -23,6 +23,9 @@ declare_args() { | |||
| 23 | iot_connect_ble_support = true | 23 | iot_connect_ble_support = true |
| 24 | iot_connect_sle_support = false | 24 | iot_connect_sle_support = false |
| 25 | iot_connect_wifi_support = true | 25 | iot_connect_wifi_support = true |
| 26 | + # Product profile switch. Keep false by default so existing products retain | ||
| 27 | + # their legacy transport combination unless they opt in explicitly. | ||
| 28 | + iot_connect_wifi_only_support = false | ||
| 26 | iot_connect_eth_support = false | 29 | iot_connect_eth_support = false |
| 27 | iot_connect_bridge_support = false | 30 | iot_connect_bridge_support = false |
| 28 | iot_connect_ailife_support = false | 31 | iot_connect_ailife_support = false |
| @@ -34,6 +37,10 @@ declare_args() { | |||
| 34 | iotc_one_connect_version_name = "2.0" | 37 | iotc_one_connect_version_name = "2.0" |
| 35 | iotc_one_connect_version_code = 2 | 38 | iotc_one_connect_version_code = 2 |
| 36 | iot_connect_speke_v2 = false | 39 | iot_connect_speke_v2 = false |
| 40 | + # DANGEROUS diagnostic switch: prints SPEKE passcode and derived key | ||
| 41 | + # material in plaintext. It must only be enabled for an isolated debug | ||
| 42 | + # image and must remain false for normal builds and deliverables. | ||
| 43 | + iot_connect_speke_secret_trace = false | ||
| 37 | #SPEKE PBKDF2 迭代次数,取值范围 1000-100000,可根据设备性能调整 | 44 | #SPEKE PBKDF2 迭代次数,取值范围 1000-100000,可根据设备性能调整 |
| 38 | iot_connect_speke_pbkdf2_iterations = 10000 | 45 | iot_connect_speke_pbkdf2_iterations = 10000 |
| 39 | #TerminalPsk数量,根据设备性能调整 | 46 | #TerminalPsk数量,根据设备性能调整 |
| @@ -44,6 +51,8 @@ declare_args() { | |||
| 44 | sec_acl_max_num = 4 | 51 | sec_acl_max_num = 4 |
| 45 | #是否支持OH靠近发现广播数据更新 | 52 | #是否支持OH靠近发现广播数据更新 |
| 46 | iot_connect_ble_adv_update_support = false | 53 | iot_connect_ble_adv_update_support = false |
| 54 | + # 资源受限产品可显式开启 lockless ExMutex;默认保持互斥锁行为。 | ||
| 55 | + iot_connect_mutex_lockless = false | ||
| 47 | #是否支持iotc数据保存 | 56 | #是否支持iotc数据保存 |
| 48 | iot_connect_kv_support = true | 57 | iot_connect_kv_support = true |
| 49 | #控制发OH靠近发现广播的发出 | 58 | #控制发OH靠近发现广播的发出 |
| @@ -73,8 +82,25 @@ if (iotc_one_connect_version_code >= 2) { | |||
| 73 | iot_connect_speke_v2 = true | 82 | iot_connect_speke_v2 = true |
| 74 | } | 83 | } |
| 75 | 84 | ||
| 76 | -# Network is required by Ethernet and by the 3.0 Wi-Fi Combo route. 2.0 Wi-Fi | 85 | +if (iot_connect_wifi_only_support) { |
| 77 | -# continues to use the legacy Wi-Fi LAN Search implementation. | 86 | + # The mini product feature parser interprets values beginning with a digit |
| 87 | + # as integers, so derive the protocol version from the product profile. | ||
| 88 | + iotc_one_connect_version_name = "3.0" | ||
| 89 | + iotc_one_connect_version_code = 3 | ||
| 90 | + assert(iot_connect_wifi_support, | ||
| 91 | + "WiFi Only profile requires iot_connect_wifi_support=true") | ||
| 92 | + assert(!iot_connect_ble_support && !iot_connect_sle_support && | ||
| 93 | + !iot_connect_eth_support, | ||
| 94 | + "WiFi Only profile must not enable BLE, SLE, or Ethernet") | ||
| 95 | + assert(!iot_connect_ailife_support && !iot_connect_databus_support && | ||
| 96 | + !iot_connect_oh_nearlink_support, | ||
| 97 | + "WiFi Only profile must not enable legacy/nearlink side channels") | ||
| 98 | + assert(iotc_one_connect_version_name == "3.0" && | ||
| 99 | + iotc_one_connect_version_code == 3, | ||
| 100 | + "WiFi Only profile must use Unified Connect protocol 3.0") | ||
| 101 | +} | ||
| 102 | + | ||
| 103 | +# Network is required by Ethernet and by the 3.0 Wi-Fi transport baseline. | ||
| 78 | iot_connect_network_support = iot_connect_eth_support || | 104 | iot_connect_network_support = iot_connect_eth_support || |
| 79 | (iot_connect_wifi_support && iotc_one_connect_version_code >= 2) | 105 | (iot_connect_wifi_support && iotc_one_connect_version_code >= 2) |
| 80 | 106 | ||
| @@ -26,7 +26,10 @@ | |||
| 26 | 26 | ||
| 27 | 27 | ||
| 28 | 28 | ||
| 29 | + | ||
| 29 | 30 | ||
| 31 | + | ||
| 32 | + | ||
| 30 | 33 | ||
| 31 | 34 | ||
| 32 | 35 | ||
| @@ -34,7 +37,10 @@ | |||
| 34 | 37 | ||
| 35 | 38 | ||
| 36 | static const FwkInitUnit OH_COMM[] = { | 39 | static const FwkInitUnit OH_COMM[] = { |
| 37 | - {FWK_INIT_LVL_BIZ, "config", IotcOhStoreDataInit, IotcOhStoreDataDeinit}, | 40 | + /* SecurityStore is a dependency of device, Wi-Fi provisioning and hub |
| 41 | + * business units. It must be ready before any BIZ-level unit can | ||
| 42 | + * register or recover persistent records. */ | ||
| 43 | + {FWK_INIT_LVL_COMP, "config", IotcOhStoreDataInit, IotcOhStoreDataDeinit}, | ||
| 38 | }; | 44 | }; |
| 39 | static uint32_t g_taskSize = IOTC_CONF_OH_DEFAULT_TASK_SIZE; | 45 | static uint32_t g_taskSize = IOTC_CONF_OH_DEFAULT_TASK_SIZE; |
| 40 | static bool g_isCommOptionReg = false; | 46 | static bool g_isCommOptionReg = false; |
| @@ -180,9 +186,38 @@ static int32_t RestoreExecutorCallback(void *inData, void **outData) | |||
| 180 | return IOTC_OK; | 186 | return IOTC_OK; |
| 181 | } | 187 | } |
| 182 | 188 | ||
| 189 | +/* | ||
| 190 | + * A failed DeviceService recovery unregisters dev_data before the user can | ||
| 191 | + * request a factory reset. Recreate only that store registration after | ||
| 192 | + * deleting its stale record; the normal restore event then clears every | ||
| 193 | + * remaining provisioning record and reboots the device. | ||
| 194 | + */ | ||
| 195 | +static int32_t RecoverDeviceConfigForRestore(void) | ||
| 196 | +{ | ||
| 197 | + uint32_t len = 0; | ||
| 198 | + if (IotcKvGetLen(IOTC_STORE_KEY_CHAR_DEVICE_CONFIG, &len) == IOTC_OK) { | ||
| 199 | + int32_t ret = IotcKvDelValue(IOTC_STORE_KEY_CHAR_DEVICE_CONFIG); | ||
| 200 | + if (ret != IOTC_OK) { | ||
| 201 | + IOTC_LOGW("erase stale device config error %d", ret); | ||
| 202 | + return ret; | ||
| 203 | + } | ||
| 204 | + } | ||
| 205 | + | ||
| 206 | + int32_t ret = ConfigInfoInit(); | ||
| 207 | + if (ret != IOTC_OK) { | ||
| 208 | + IOTC_LOGW("recover device config error %d", ret); | ||
| 209 | + return ret; | ||
| 210 | + } | ||
| 211 | + return SetRevokeFlag(); | ||
| 212 | +} | ||
| 213 | + | ||
| 183 | int32_t IotcOhRestore(void) | 214 | int32_t IotcOhRestore(void) |
| 184 | { | 215 | { |
| 185 | int32_t ret = SetRevokeFlag(); | 216 | int32_t ret = SetRevokeFlag(); |
| 217 | + if (ret == IOTC_CORE_COMM_UTILS_ERR_STORE_INVALID_KEY) { | ||
| 218 | + IOTC_LOGW("device config unavailable, recover for restore"); | ||
| 219 | + ret = RecoverDeviceConfigForRestore(); | ||
| 220 | + } | ||
| 186 | if (ret != IOTC_OK) { | 221 | if (ret != IOTC_OK) { |
| 187 | IOTC_LOGF("set revoke error %d", ret); | 222 | IOTC_LOGF("set revoke error %d", ret); |
| 188 | return ret; | 223 | return ret; |
| @@ -42,10 +42,17 @@ static bool IsNeedStartWifiSoftapSvc(void) | |||
| 42 | if (GetNetCfgMode() != IOTC_NET_CONFIG_MODE_SOFTAP) { | 42 | if (GetNetCfgMode() != IOTC_NET_CONFIG_MODE_SOFTAP) { |
| 43 | return false; | 43 | return false; |
| 44 | } | 44 | } |
| 45 | + | ||
| 46 | + /* A partial HubSetup must not leave an offline device permanently hidden. */ | ||
| 47 | + bool isBinded = DevSvcProxyGetBindStatus() == DEV_BIND_STATUS_BIND; | ||
| 48 | + bool hasWifiInfo = ConnSvcProxyIsWifiInfoExits(); | ||
| 49 | + return !ConnSvcProxyIsNetConnected() && !(isBinded && hasWifiInfo); | ||
| 50 | + | ||
| 45 | if (DevSvcProxyGetBindStatus() == DEV_BIND_STATUS_BIND) { | 51 | if (DevSvcProxyGetBindStatus() == DEV_BIND_STATUS_BIND) { |
| 46 | return false; | 52 | return false; |
| 47 | } | 53 | } |
| 48 | return !ConnSvcProxyIsNetConnected() && !ConnSvcProxyIsWifiInfoExits(); | 54 | return !ConnSvcProxyIsNetConnected() && !ConnSvcProxyIsWifiInfoExits(); |
| 55 | + | ||
| 49 | } | 56 | } |
| 50 | 57 | ||
| 51 | static int32_t StartWifiSoftapSvc(void) | 58 | static int32_t StartWifiSoftapSvc(void) |
| @@ -75,7 +82,7 @@ static int32_t StartWifiSoftapSvc(void) | |||
| 75 | return IOTC_OK; | 82 | return IOTC_OK; |
| 76 | } | 83 | } |
| 77 | 84 | ||
| 78 | -#if IOTC_ONECONNECT_VERSION_CODE >= 2 | 85 | +#if IOTC_ONECONNECT_VERSION_CODE >= 2 && !IOTC_CONF_WIFI_ONLY_SUPPORT |
| 79 | /* 3.0 uses Network LAN Search and does not start the legacy cloud service. */ | 86 | /* 3.0 uses Network LAN Search and does not start the legacy cloud service. */ |
| 80 | static void StartLocalNetworkSvc(void) | 87 | static void StartLocalNetworkSvc(void) |
| 81 | { | 88 | { |
| @@ -110,7 +117,9 @@ static void EventBusStartWifiSvcCallback(uint32_t event, void *param, uint32_t l | |||
| 110 | return; | 117 | return; |
| 111 | } | 118 | } |
| 112 | 119 | ||
| 113 | -#if IOTC_ONECONNECT_VERSION_CODE >= 2 | 120 | +#if IOTC_CONF_WIFI_ONLY_SUPPORT |
| 121 | + /* This PR intentionally stops at the verified provisioning boundary. */ | ||
| 122 | + | ||
| 114 | if (GetNetCfgMode() != IOTC_NET_CONFIG_MODE_SOFTAP) { | 123 | if (GetNetCfgMode() != IOTC_NET_CONFIG_MODE_SOFTAP) { |
| 115 | StartLocalNetworkSvc(); | 124 | StartLocalNetworkSvc(); |
| 116 | } | 125 | } |
| @@ -158,12 +167,14 @@ static void IotcOhWifiMainDeinit(void) | |||
| 158 | (void)EventBusUnsubscribe(EventBusStartWifiSvcCallback); | 167 | (void)EventBusUnsubscribe(EventBusStartWifiSvcCallback); |
| 159 | (void)ServiceProxyStopService(IOTC_SERVICE_ID_SOFTAP, NULL); | 168 | (void)ServiceProxyStopService(IOTC_SERVICE_ID_SOFTAP, NULL); |
| 160 | (void)ServiceProxyStopService(IOTC_SERVICE_ID_WIFI_CONNECT, NULL); | 169 | (void)ServiceProxyStopService(IOTC_SERVICE_ID_WIFI_CONNECT, NULL); |
| 170 | + | ||
| 161 | (void)ServiceProxyStopService(IOTC_SERVICE_ID_LOCAL_CONTROL, NULL); | 171 | (void)ServiceProxyStopService(IOTC_SERVICE_ID_LOCAL_CONTROL, NULL); |
| 162 | 172 | ||
| 163 | (void)ServiceProxyStopService(IOTC_SERVICE_ID_NET_LAN_SEARCH, NULL); | 173 | (void)ServiceProxyStopService(IOTC_SERVICE_ID_NET_LAN_SEARCH, NULL); |
| 164 | 174 | ||
| 165 | (void)ServiceProxyStopService(IOTC_SERVICE_ID_LAN_SEARCH, NULL); | 175 | (void)ServiceProxyStopService(IOTC_SERVICE_ID_LAN_SEARCH, NULL); |
| 166 | 176 | ||
| 177 | + | ||
| 167 | } | 178 | } |
| 168 | 179 | ||
| 169 | static const FwkInitUnit OH_WIFI[] = { | 180 | static const FwkInitUnit OH_WIFI[] = { |
| @@ -16,22 +16,24 @@ | |||
| 16 | 16 | ||
| 17 | 17 | ||
| 18 | 18 | ||
| 19 | - | ||
| 20 | 19 | ||
| 21 | 20 | ||
| 22 | 21 | ||
| 23 | 22 | ||
| 24 | 23 | ||
| 25 | - | ||
| 26 | 24 | ||
| 27 | 25 | ||
| 28 | 26 | ||
| 29 | 27 | ||
| 30 | -#define SOFTAP_NAME_VER IOTC_ONECONNECT_VERSION_CODE | 28 | +#if defined(IOTC_ONECONNECT_VERSION_CODE) && (IOTC_ONECONNECT_VERSION_CODE >= 2) |
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 31 | 33 | ||
| 32 | 34 | ||
| 33 | 35 | ||
| 34 | -#define SOFTAP_NETCFG_CAPCITY 0b01101000 | 36 | +#define SOFTAP_NETCFG_CAPCITY 0b01111100 |
| 35 | 37 | ||
| 36 | 38 | ||
| 37 | 39 | ||
| @@ -60,31 +62,41 @@ int32_t IotcOhGetSoftApName(uint8_t *ssid, uint32_t *len) | |||
| 60 | } | 62 | } |
| 61 | DevBindStatus bindStatus = DevSvcProxyGetBindStatus(); | 63 | DevBindStatus bindStatus = DevSvcProxyGetBindStatus(); |
| 62 | 64 | ||
| 63 | - // 2.0版本广播协议 | ||
| 64 | const char *head = bindStatus == DEV_BIND_STATUS_BIND ? SOFTAP_REGED_NAME_HEAD_OC : SOFTAP_UNREG_NAME_HEAD_OC; | 65 | const char *head = bindStatus == DEV_BIND_STATUS_BIND ? SOFTAP_REGED_NAME_HEAD_OC : SOFTAP_UNREG_NAME_HEAD_OC; |
| 65 | 66 | ||
| 66 | - // 1.0版本广播协议 | ||
| 67 | const char *head = bindStatus == DEV_BIND_STATUS_BIND ? SOFTAP_REGED_NAME_HEAD : SOFTAP_UNREG_NAME_HEAD; | 67 | const char *head = bindStatus == DEV_BIND_STATUS_BIND ? SOFTAP_REGED_NAME_HEAD : SOFTAP_UNREG_NAME_HEAD; |
| 68 | 68 | ||
| 69 | - int ret = sprintf_s((char *)ssid, ssidSize, "%s-%.14s-%d%s%s", | 69 | + int ret = sprintf_s((char *)ssid, ssidSize, "%s-%.14s-%s%s%s", |
| 70 | head, customName, SOFTAP_NAME_VER, devInfo->prodId, | 70 | head, customName, SOFTAP_NAME_VER, devInfo->prodId, |
| 71 | &devInfo->sn[snLen - SOFTAP_NAME_SN_LEN]); | 71 | &devInfo->sn[snLen - SOFTAP_NAME_SN_LEN]); |
| 72 | if (ret <= 0) { | 72 | if (ret <= 0) { |
| 73 | IOTC_LOGE("sprintf_s ret=%d", ret); | 73 | IOTC_LOGE("sprintf_s ret=%d", ret); |
| 74 | return IOTC_ERROR; | 74 | return IOTC_ERROR; |
| 75 | } | 75 | } |
| 76 | - if (ret + SOFTAP_NETCFG_CAPCITY_SIZE + SOFTAP_TERMINAL_CONF_SIZE > ssidSize) { | 76 | +#if defined(IOTC_ONECONNECT_VERSION_CODE) && (IOTC_ONECONNECT_VERSION_CODE >= 2) |
| 77 | + const uint32_t suffixSize = SOFTAP_NETCFG_CAPCITY_SIZE + SOFTAP_TERMINAL_CONF_SIZE; | ||
| 78 | + | ||
| 79 | + const uint32_t suffixSize = SOFTAP_NETCFG_CAPCITY_SIZE; | ||
| 80 | + | ||
| 81 | + if ((uint32_t)ret + suffixSize > ssidSize) { | ||
| 77 | IOTC_LOGE("buffer not enough, %d/%u", ret, ssidSize); | 82 | IOTC_LOGE("buffer not enough, %d/%u", ret, ssidSize); |
| 78 | return IOTC_ERR_NOT_ENOUGH; | 83 | return IOTC_ERR_NOT_ENOUGH; |
| 79 | } | 84 | } |
| 80 | - ssid[ret] = SOFTAP_NETCFG_CAPCITY; | 85 | + uint8_t capacity = SOFTAP_NETCFG_CAPCITY; |
| 86 | + ssid[ret] = capacity; | ||
| 81 | ret += SOFTAP_NETCFG_CAPCITY_SIZE; | 87 | ret += SOFTAP_NETCFG_CAPCITY_SIZE; |
| 82 | - uint8_t terminalConf = SOFTAP_TERMINAL_CONF; | 88 | +#if defined(IOTC_ONECONNECT_VERSION_CODE) && (IOTC_ONECONNECT_VERSION_CODE >= 2) |
| 83 | - if (ProductIsConfigOemProvisionCallback()) { | 89 | + uint8_t terminalConf = ModelGetDataConfig(); |
| 84 | - terminalConf |= 0x01; | 90 | + /* Temporary interop diagnostic: make a trailing binary NUL visible to string-only configurators. */ |
| 91 | + if (terminalConf == 0x00) { | ||
| 92 | + terminalConf = (uint8_t)'0'; | ||
| 85 | } | 93 | } |
| 86 | ssid[ret] = terminalConf; | 94 | ssid[ret] = terminalConf; |
| 87 | ret += SOFTAP_TERMINAL_CONF_SIZE; | 95 | ret += SOFTAP_TERMINAL_CONF_SIZE; |
| 88 | - *len = ret; | 96 | + IOTC_LOGI("softap ssid=%.*s len=%d z=0x%02x p=0x%02x", ret, ssid, ret, capacity, terminalConf); |
| 97 | + | ||
| 98 | + IOTC_LOGI("softap ssid=%.*s len=%d z=0x%02x", ret, ssid, ret, capacity); | ||
| 99 | + | ||
| 100 | + *len = (uint32_t)ret; | ||
| 89 | return IOTC_OK; | 101 | return IOTC_OK; |
| 90 | } | 102 | } |
| @@ -128,12 +128,14 @@ set(IOTC_TLS_TEST_SOURCES | |||
| 128 | "${IOTC_ROOT}/adapter/os/posix/iotc_os.c" | 128 | "${IOTC_ROOT}/adapter/os/posix/iotc_os.c" |
| 129 | "${IOTC_ROOT}/adapter/log/iotc_log_inner.c" | 129 | "${IOTC_ROOT}/adapter/log/iotc_log_inner.c" |
| 130 | "${IOTC_ROOT}/adapter/log/printf/iotc_log.c" | 130 | "${IOTC_ROOT}/adapter/log/printf/iotc_log.c" |
| 131 | + "${IOTC_ROOT}/core/infrastructure/utils/utils_common.c" | ||
| 131 | ) | 132 | ) |
| 132 | add_executable(iotc_tls_unit_test ${IOTC_TLS_TEST_SOURCES}) | 133 | add_executable(iotc_tls_unit_test ${IOTC_TLS_TEST_SOURCES}) |
| 133 | target_include_directories(iotc_tls_unit_test PRIVATE | 134 | target_include_directories(iotc_tls_unit_test PRIVATE |
| 134 | "${IOTC_ROOT}/adapter/include" | 135 | "${IOTC_ROOT}/adapter/include" |
| 135 | "${IOTC_ROOT}/interfaces/kits/common" | 136 | "${IOTC_ROOT}/interfaces/kits/common" |
| 136 | "${IOTC_ROOT}/interfaces/kits/oh_connect" | 137 | "${IOTC_ROOT}/interfaces/kits/oh_connect" |
| 138 | + "${IOTC_ROOT}/core/infrastructure/utils/include" | ||
| 137 | "${CMAKE_CURRENT_LIST_DIR}/include" | 139 | "${CMAKE_CURRENT_LIST_DIR}/include" |
| 138 | ) | 140 | ) |
| 139 | if (NOT DEFINED IOTC_TLS_TEST_LOG_LEVEL) | 141 | if (NOT DEFINED IOTC_TLS_TEST_LOG_LEVEL) |
| @@ -233,6 +235,14 @@ target_compile_definitions(iotc_speke_api_unit_test PRIVATE | |||
| 233 | endif() | 235 | endif() |
| 234 | 236 | ||
| 235 | if (NOT IOTC_CJSON_ROOT STREQUAL "") | 237 | if (NOT IOTC_CJSON_ROOT STREQUAL "") |
| 238 | + # 与根 CMakeLists.txt 及 iotc.gni 默认一致;GN 构建注入该宏,这里补齐。 | ||
| 239 | + if (NOT DEFINED SPEKE_PBKDF2_ITERATIONS) | ||
| 240 | + set(SPEKE_PBKDF2_ITERATIONS 10000) | ||
| 241 | + endif() | ||
| 242 | + set(IOTC_SPEKE_V2_TEST_SECRET_TRACE 0 CACHE STRING | ||
| 243 | + "Enable plaintext SPEKE v2 key trace in this host-test binary") | ||
| 244 | + set(IOTC_SPEKE_V2_TEST_LOG_LEVEL 0 CACHE STRING | ||
| 245 | + "SPEKE v2 host-test compile-time log level") | ||
| 236 | set(IOTC_SPEKE_V2_API_TEST_SOURCES | 246 | set(IOTC_SPEKE_V2_API_TEST_SOURCES |
| 237 | iotc_speke_v2_api_unit_test.c | 247 | iotc_speke_v2_api_unit_test.c |
| 238 | "${IOTC_ROOT}/core/infrastructure/security/speke_v2/security_speke_session.c" | 248 | "${IOTC_ROOT}/core/infrastructure/security/speke_v2/security_speke_session.c" |
| @@ -244,6 +254,8 @@ if (NOT IOTC_CJSON_ROOT STREQUAL "") | |||
| 244 | "${IOTC_ROOT}/core/infrastructure/utils/utils_json.c" | 254 | "${IOTC_ROOT}/core/infrastructure/utils/utils_json.c" |
| 245 | "${IOTC_ROOT}/adapter/json/cjson/iotc_json.c" | 255 | "${IOTC_ROOT}/adapter/json/cjson/iotc_json.c" |
| 246 | "${IOTC_CJSON_ROOT}/cJSON.c" | 256 | "${IOTC_CJSON_ROOT}/cJSON.c" |
| 257 | + "${IOTC_ROOT}/adapter/log/iotc_log_inner.c" | ||
| 258 | + "${IOTC_ROOT}/adapter/log/printf/iotc_log.c" | ||
| 247 | "${IOTC_ROOT}/adapter/os/posix/iotc_os.c" | 259 | "${IOTC_ROOT}/adapter/os/posix/iotc_os.c" |
| 248 | ${IOTC_CRYPTO_SOURCES} | 260 | ${IOTC_CRYPTO_SOURCES} |
| 249 | ) | 261 | ) |
| @@ -262,8 +274,10 @@ if (NOT IOTC_CJSON_ROOT STREQUAL "") | |||
| 262 | ) | 274 | ) |
| 263 | iotc_add_securec(iotc_speke_v2_api_unit_test) | 275 | iotc_add_securec(iotc_speke_v2_api_unit_test) |
| 264 | target_compile_definitions(iotc_speke_v2_api_unit_test PRIVATE | 276 | target_compile_definitions(iotc_speke_v2_api_unit_test PRIVATE |
| 265 | - IOTC_CONF_LOG_BUILD_LEVEL=0 | 277 | + IOTC_CONF_LOG_BUILD_LEVEL=${IOTC_SPEKE_V2_TEST_LOG_LEVEL} |
| 278 | + IOTC_CONF_SPEKE_SECRET_TRACE=${IOTC_SPEKE_V2_TEST_SECRET_TRACE} | ||
| 266 | IOTC_CONF_SPEKE_V2 | 279 | IOTC_CONF_SPEKE_V2 |
| 280 | + SPEKE_PBKDF2_ITERATIONS=${SPEKE_PBKDF2_ITERATIONS} | ||
| 267 | ) | 281 | ) |
| 268 | if (IOTC_SSL_BACKEND STREQUAL "mbedtls") | 282 | if (IOTC_SSL_BACKEND STREQUAL "mbedtls") |
| 269 | target_include_directories(iotc_speke_v2_api_unit_test BEFORE PRIVATE "${IOTC_MBEDTLS_ROOT}/include") | 283 | target_include_directories(iotc_speke_v2_api_unit_test BEFORE PRIVATE "${IOTC_MBEDTLS_ROOT}/include") |
| @@ -0,0 +1,46 @@ | |||
| 1 | +#!/bin/bash | ||
| 2 | +# Build and run the WiFi cold-boot deferred-connect host test outside the tree. | ||
| 3 | +set -uo pipefail | ||
| 4 | + | ||
| 5 | +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) | ||
| 6 | +IOTC_ROOT=$(cd "$SCRIPT_DIR/../../.." && pwd) | ||
| 7 | +OUT_DIR=$(mktemp -d /tmp/iotc_connect_boot_host_test.XXXXXX) | ||
| 8 | +trap 'rm -rf "$OUT_DIR"' EXIT | ||
| 9 | + | ||
| 10 | +CFLAGS=( | ||
| 11 | + -std=c99 -Wall -Wextra -Werror -g -O0 | ||
| 12 | + -ffunction-sections -fdata-sections | ||
| 13 | + -DIOTC_CONF_WIFI_ONLY_SUPPORT=1 | ||
| 14 | + -I"$SCRIPT_DIR/stubs" | ||
| 15 | + -I"$IOTC_ROOT/test/unit/device_config_host" | ||
| 16 | + -I"$IOTC_ROOT/test/unit/device_config_host/stubs" | ||
| 17 | + -I"$IOTC_ROOT/adapter/include" | ||
| 18 | + -I"$IOTC_ROOT/interfaces/kits/common" | ||
| 19 | + -I"$IOTC_ROOT/core/infrastructure/define" | ||
| 20 | + -I"$IOTC_ROOT/core/infrastructure/service/include" | ||
| 21 | + -I"$IOTC_ROOT/core/infrastructure/utils/include" | ||
| 22 | + -I"$IOTC_ROOT/core/infrastructure/schedule/include" | ||
| 23 | + -I"$IOTC_ROOT/core/infrastructure/frameworks/event_loop/include" | ||
| 24 | + -I"$IOTC_ROOT/core/infrastructure/frameworks/event_bus/include" | ||
| 25 | + -I"$IOTC_ROOT/core/infrastructure/frameworks/main/include" | ||
| 26 | + -I"$IOTC_ROOT/core/infrastructure/frameworks/service/include" | ||
| 27 | + -I"$IOTC_ROOT/core/device/config/include" | ||
| 28 | + -I"$IOTC_ROOT/core/wifi/application/connect" | ||
| 29 | + -I"$IOTC_ROOT/core/wifi/infrastructure/utils/include" | ||
| 30 | +) | ||
| 31 | + | ||
| 32 | +SOURCES=( | ||
| 33 | + "$SCRIPT_DIR/test_connect_boot.c" | ||
| 34 | + "$IOTC_ROOT/core/wifi/application/connect/svc_conn_action.c" | ||
| 35 | +) | ||
| 36 | + | ||
| 37 | +gcc "${CFLAGS[@]}" "${SOURCES[@]}" -Wl,--gc-sections -o "$OUT_DIR/test_connect_boot" || exit 1 | ||
| 38 | +"$OUT_DIR/test_connect_boot" || exit 1 | ||
| 39 | + | ||
| 40 | +NET_INFO_SOURCES=( | ||
| 41 | + "$SCRIPT_DIR/test_net_info_transaction.c" | ||
| 42 | + "$IOTC_ROOT/core/wifi/application/connect/svc_conn_net_info.c" | ||
| 43 | +) | ||
| 44 | + | ||
| 45 | +gcc "${CFLAGS[@]}" "${NET_INFO_SOURCES[@]}" -Wl,--gc-sections -o "$OUT_DIR/test_net_info_transaction" || exit 1 | ||
| 46 | +"$OUT_DIR/test_net_info_transaction" | ||
| @@ -0,0 +1,237 @@ | |||
| 1 | + | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | +int g_testChecks; | ||
| 14 | +int g_testFailures; | ||
| 15 | + | ||
| 16 | +typedef struct { | ||
| 17 | + EventSourceTimerType type; | ||
| 18 | + EventSourceTimerCallback callback; | ||
| 19 | + uint32_t intervalMs; | ||
| 20 | + void *userData; | ||
| 21 | + int32_t id; | ||
| 22 | +} TimerRecord; | ||
| 23 | + | ||
| 24 | +static ConnectServiceContext g_ctx; | ||
| 25 | +static TimerRecord g_timers[8]; | ||
| 26 | +static int32_t g_timerCount; | ||
| 27 | +static int32_t g_timerAddFailure; | ||
| 28 | +static int32_t g_timerRemoveCount; | ||
| 29 | +static int32_t g_connectResult; | ||
| 30 | +static int32_t g_connectCalls; | ||
| 31 | +static bool g_needVerify; | ||
| 32 | +static bool g_checked; | ||
| 33 | +static int32_t g_disconnectCalls; | ||
| 34 | +static int32_t g_deleteCalls; | ||
| 35 | +static int32_t g_clearCalls; | ||
| 36 | +static int32_t g_restoreCalls; | ||
| 37 | +static int32_t g_eventCalls; | ||
| 38 | + | ||
| 39 | +static void ResetState(void) | ||
| 40 | +{ | ||
| 41 | + (void)memset(&g_ctx, 0, sizeof(g_ctx)); | ||
| 42 | + g_ctx.verifyTimerFd = EVENT_SOURCE_INVALID_TIMER_FD; | ||
| 43 | + g_ctx.netCheckTimerFd = EVENT_SOURCE_INVALID_TIMER_FD; | ||
| 44 | + g_ctx.reconnectTimerFd = EVENT_SOURCE_INVALID_TIMER_FD; | ||
| 45 | + g_ctx.initialConnectTimerFd = EVENT_SOURCE_INVALID_TIMER_FD; | ||
| 46 | + (void)memset(g_timers, 0, sizeof(g_timers)); | ||
| 47 | + g_timerCount = 0; | ||
| 48 | + g_timerAddFailure = IOTC_OK; | ||
| 49 | + g_timerRemoveCount = 0; | ||
| 50 | + g_connectResult = IOTC_OK; | ||
| 51 | + g_connectCalls = 0; | ||
| 52 | + g_needVerify = false; | ||
| 53 | + g_checked = false; | ||
| 54 | + g_disconnectCalls = 0; | ||
| 55 | + g_deleteCalls = 0; | ||
| 56 | + g_clearCalls = 0; | ||
| 57 | + g_restoreCalls = 0; | ||
| 58 | + g_eventCalls = 0; | ||
| 59 | +} | ||
| 60 | + | ||
| 61 | +ConnectServiceContext *GetConnSvcCtx(void) | ||
| 62 | +{ | ||
| 63 | + return &g_ctx; | ||
| 64 | +} | ||
| 65 | + | ||
| 66 | +int32_t SchedTimerAdd(EventSourceTimerType type, EventSourceTimerCallback callback, | ||
| 67 | + uint32_t ms, void *userData) | ||
| 68 | +{ | ||
| 69 | + if (g_timerAddFailure != IOTC_OK) { | ||
| 70 | + return g_timerAddFailure; | ||
| 71 | + } | ||
| 72 | + int32_t index = g_timerCount++; | ||
| 73 | + g_timers[index].type = type; | ||
| 74 | + g_timers[index].callback = callback; | ||
| 75 | + g_timers[index].intervalMs = ms; | ||
| 76 | + g_timers[index].userData = userData; | ||
| 77 | + g_timers[index].id = 100 + index; | ||
| 78 | + return g_timers[index].id; | ||
| 79 | +} | ||
| 80 | + | ||
| 81 | +void SchedTimerRemove(int32_t id) | ||
| 82 | +{ | ||
| 83 | + (void)id; | ||
| 84 | + ++g_timerRemoveCount; | ||
| 85 | +} | ||
| 86 | + | ||
| 87 | +int32_t IotcConnectWifi(void) | ||
| 88 | +{ | ||
| 89 | + ++g_connectCalls; | ||
| 90 | + return g_connectResult; | ||
| 91 | +} | ||
| 92 | + | ||
| 93 | +bool IsNetworkConnected(void) | ||
| 94 | +{ | ||
| 95 | + return false; | ||
| 96 | +} | ||
| 97 | + | ||
| 98 | +int32_t IotcDisconnectWifi(void) | ||
| 99 | +{ | ||
| 100 | + ++g_disconnectCalls; | ||
| 101 | + return IOTC_OK; | ||
| 102 | +} | ||
| 103 | + | ||
| 104 | +int32_t IotcDeleteWifiInfo(void) | ||
| 105 | +{ | ||
| 106 | + ++g_deleteCalls; | ||
| 107 | + return IOTC_OK; | ||
| 108 | +} | ||
| 109 | + | ||
| 110 | +bool ConfigIsNetInfoNeedVerify(void) | ||
| 111 | +{ | ||
| 112 | + return g_needVerify; | ||
| 113 | +} | ||
| 114 | + | ||
| 115 | +bool ConfigIsNetInfoChecked(void) | ||
| 116 | +{ | ||
| 117 | + return g_checked; | ||
| 118 | +} | ||
| 119 | + | ||
| 120 | +int32_t ConfigClearNetInfoFlag(void) | ||
| 121 | +{ | ||
| 122 | + ++g_clearCalls; | ||
| 123 | + g_needVerify = false; | ||
| 124 | + g_checked = false; | ||
| 125 | + return IOTC_OK; | ||
| 126 | +} | ||
| 127 | + | ||
| 128 | +int32_t IotcFwkRestore(void) | ||
| 129 | +{ | ||
| 130 | + ++g_restoreCalls; | ||
| 131 | + return IOTC_OK; | ||
| 132 | +} | ||
| 133 | + | ||
| 134 | +void EventBusPublishSyncInner(uint32_t event, const char *name, void *param, uint32_t len) | ||
| 135 | +{ | ||
| 136 | + (void)event; | ||
| 137 | + (void)name; | ||
| 138 | + (void)param; | ||
| 139 | + (void)len; | ||
| 140 | + ++g_eventCalls; | ||
| 141 | +} | ||
| 142 | + | ||
| 143 | +static void FireTimer(int32_t index) | ||
| 144 | +{ | ||
| 145 | + CHECK(index >= 0 && index < g_timerCount); | ||
| 146 | + CHECK(g_timers[index].callback != NULL); | ||
| 147 | + if (index >= 0 && index < g_timerCount && g_timers[index].callback != NULL) { | ||
| 148 | + g_timers[index].callback(g_timers[index].id, g_timers[index].userData); | ||
| 149 | + } | ||
| 150 | +} | ||
| 151 | + | ||
| 152 | +static void TestCheckedNetworkTransientFailure(void) | ||
| 153 | +{ | ||
| 154 | + ResetState(); | ||
| 155 | + g_checked = true; | ||
| 156 | + g_connectResult = -123; | ||
| 157 | + | ||
| 158 | + CHECK_EQ_INT(IOTC_OK, ConnSvcScheduleInitialConnect()); | ||
| 159 | + CHECK_EQ_INT(1, g_timerCount); | ||
| 160 | + CHECK_EQ_INT(EVENT_SOURCE_TIMER_TYPE_ONCE, g_timers[0].type); | ||
| 161 | + CHECK_EQ_INT(1000, g_timers[0].intervalMs); | ||
| 162 | + CHECK_EQ_INT(g_timers[0].id, g_ctx.initialConnectTimerFd); | ||
| 163 | + | ||
| 164 | + CHECK_EQ_INT(IOTC_OK, ConnSvcScheduleInitialConnect()); | ||
| 165 | + CHECK_EQ_INT(1, g_timerCount); | ||
| 166 | + | ||
| 167 | + FireTimer(0); | ||
| 168 | + CHECK_EQ_INT(1, g_connectCalls); | ||
| 169 | + CHECK_EQ_INT(EVENT_SOURCE_INVALID_TIMER_FD, g_ctx.initialConnectTimerFd); | ||
| 170 | + CHECK_EQ_INT(1, g_timerRemoveCount); | ||
| 171 | + CHECK_EQ_INT(2, g_timerCount); | ||
| 172 | + CHECK_EQ_INT(EVENT_SOURCE_TIMER_TYPE_REPEAT, g_timers[1].type); | ||
| 173 | + CHECK_EQ_INT(20000, g_timers[1].intervalMs); | ||
| 174 | + CHECK_EQ_INT(g_timers[1].id, g_ctx.reconnectTimerFd); | ||
| 175 | + CHECK_EQ_INT(0, g_disconnectCalls); | ||
| 176 | + CHECK_EQ_INT(0, g_deleteCalls); | ||
| 177 | + | ||
| 178 | + FireTimer(1); | ||
| 179 | + CHECK_EQ_INT(2, g_connectCalls); | ||
| 180 | +} | ||
| 181 | + | ||
| 182 | +static void TestSuccessfulDeferredConnect(void) | ||
| 183 | +{ | ||
| 184 | + ResetState(); | ||
| 185 | + g_checked = true; | ||
| 186 | + CHECK_EQ_INT(IOTC_OK, ConnSvcScheduleInitialConnect()); | ||
| 187 | + FireTimer(0); | ||
| 188 | + CHECK_EQ_INT(1, g_connectCalls); | ||
| 189 | + CHECK_EQ_INT(1, g_timerCount); | ||
| 190 | + CHECK_EQ_INT(EVENT_SOURCE_INVALID_TIMER_FD, g_ctx.initialConnectTimerFd); | ||
| 191 | + CHECK_EQ_INT(EVENT_SOURCE_INVALID_TIMER_FD, g_ctx.reconnectTimerFd); | ||
| 192 | +} | ||
| 193 | + | ||
| 194 | +static void TestPendingFailureUsesExistingRecovery(void) | ||
| 195 | +{ | ||
| 196 | + ResetState(); | ||
| 197 | + g_needVerify = true; | ||
| 198 | + g_connectResult = -45; | ||
| 199 | + CHECK_EQ_INT(IOTC_OK, ConnSvcScheduleInitialConnect()); | ||
| 200 | + FireTimer(0); | ||
| 201 | + CHECK_EQ_INT(1, g_connectCalls); | ||
| 202 | + CHECK_EQ_INT(1, g_disconnectCalls); | ||
| 203 | + CHECK_EQ_INT(1, g_deleteCalls); | ||
| 204 | + CHECK_EQ_INT(1, g_clearCalls); | ||
| 205 | + CHECK_EQ_INT(1, g_restoreCalls); | ||
| 206 | + CHECK_EQ_INT(1, g_timerCount); | ||
| 207 | + CHECK_EQ_INT(EVENT_SOURCE_INVALID_TIMER_FD, g_ctx.reconnectTimerFd); | ||
| 208 | +} | ||
| 209 | + | ||
| 210 | +static void TestTimerFailureAndExistingReconnect(void) | ||
| 211 | +{ | ||
| 212 | + ResetState(); | ||
| 213 | + g_timerAddFailure = -77; | ||
| 214 | + CHECK_EQ_INT(-77, ConnSvcScheduleInitialConnect()); | ||
| 215 | + CHECK_EQ_INT(-77, g_ctx.initialConnectTimerFd); | ||
| 216 | + CHECK_EQ_INT(0, g_timerCount); | ||
| 217 | + | ||
| 218 | + ResetState(); | ||
| 219 | + g_checked = true; | ||
| 220 | + g_connectResult = -88; | ||
| 221 | + g_ctx.reconnectTimerFd = 55; | ||
| 222 | + CHECK_EQ_INT(IOTC_OK, ConnSvcScheduleInitialConnect()); | ||
| 223 | + FireTimer(0); | ||
| 224 | + CHECK_EQ_INT(1, g_timerCount); | ||
| 225 | + CHECK_EQ_INT(55, g_ctx.reconnectTimerFd); | ||
| 226 | +} | ||
| 227 | + | ||
| 228 | +int main(void) | ||
| 229 | +{ | ||
| 230 | + setbuf(stdout, NULL); | ||
| 231 | + TestCheckedNetworkTransientFailure(); | ||
| 232 | + TestSuccessfulDeferredConnect(); | ||
| 233 | + TestPendingFailureUsesExistingRecovery(); | ||
| 234 | + TestTimerFailureAndExistingReconnect(); | ||
| 235 | + printf("test_connect_boot: %d checks, %d failures\n", g_testChecks, g_testFailures); | ||
| 236 | + return (g_testFailures == 0) ? 0 : 1; | ||
| 237 | +} | ||
| @@ -0,0 +1,141 @@ | |||
| 1 | +/* HubSetup Wi-Fi transaction tests for the device-hosted SoftAP path. */ | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | +int g_testChecks; | ||
| 18 | +int g_testFailures; | ||
| 19 | + | ||
| 20 | +static bool g_wifiExists; | ||
| 21 | +static bool g_needVerify; | ||
| 22 | +static bool g_checked; | ||
| 23 | +static char g_savedSsid[IOTC_WIFI_SSID_MAX_LEN + 1]; | ||
| 24 | +static char g_savedPassword[IOTC_WIFI_PWD_MAX_LEN + 1]; | ||
| 25 | +static int32_t g_connectCalls; | ||
| 26 | +static int32_t g_timerId; | ||
| 27 | +static EventSourceTimerCallback g_timerCallback; | ||
| 28 | +static void *g_timerUserData; | ||
| 29 | +static IotcWifiMode g_wifiMode; | ||
| 30 | +static EventBusCallback g_softapStopCallback; | ||
| 31 | + | ||
| 32 | +static void Reset(void) | ||
| 33 | +{ | ||
| 34 | + g_wifiExists = true; | ||
| 35 | + g_needVerify = false; | ||
| 36 | + g_checked = true; | ||
| 37 | + (void)strcpy(g_savedSsid, "old-home"); | ||
| 38 | + (void)strcpy(g_savedPassword, "old-password"); | ||
| 39 | + g_connectCalls = 0; | ||
| 40 | + g_timerId = EVENT_SOURCE_INVALID_TIMER_FD; | ||
| 41 | + g_timerCallback = NULL; | ||
| 42 | + g_timerUserData = NULL; | ||
| 43 | + g_wifiMode = IOTC_WIFI_MODE_STATION; | ||
| 44 | + g_softapStopCallback = NULL; | ||
| 45 | +} | ||
| 46 | + | ||
| 47 | +int32_t UtilsJsonGetString(const IotcJson *json, const char *key, char *buf, uint32_t len) | ||
| 48 | +{ | ||
| 49 | + (void)json; | ||
| 50 | + const char *value = strcmp(key, STR_NETINFO_SSID) == 0 ? "new-home" : "new-password"; | ||
| 51 | + size_t valueLen = strlen(value); | ||
| 52 | + if (valueLen + 1U > len) { | ||
| 53 | + return IOTC_ERR_NOT_ENOUGH; | ||
| 54 | + } | ||
| 55 | + (void)memcpy(buf, value, valueLen + 1U); | ||
| 56 | + return IOTC_OK; | ||
| 57 | +} | ||
| 58 | + | ||
| 59 | +int32_t IotcGetWifiInfo(uint8_t *ssid, uint32_t *ssidLen, uint8_t *pwd, uint32_t *pwdLen) | ||
| 60 | +{ | ||
| 61 | + if (!g_wifiExists) { | ||
| 62 | + return IOTC_ADAPTER_WIFI_ERR_GET_INFO; | ||
| 63 | + } | ||
| 64 | + size_t sl = strlen(g_savedSsid); | ||
| 65 | + size_t pl = strlen(g_savedPassword); | ||
| 66 | + if (*ssidLen < sl || *pwdLen < pl) { | ||
| 67 | + return IOTC_ERR_NOT_ENOUGH; | ||
| 68 | + } | ||
| 69 | + (void)memcpy(ssid, g_savedSsid, sl); | ||
| 70 | + (void)memcpy(pwd, g_savedPassword, pl); | ||
| 71 | + *ssidLen = (uint32_t)sl; | ||
| 72 | + *pwdLen = (uint32_t)pl; | ||
| 73 | + return IOTC_OK; | ||
| 74 | +} | ||
| 75 | + | ||
| 76 | +int32_t IotcSetWifiInfo(const uint8_t *ssid, uint32_t ssidLen, const uint8_t *pwd, uint32_t pwdLen) | ||
| 77 | +{ | ||
| 78 | + (void)memset(g_savedSsid, 0, sizeof(g_savedSsid)); | ||
| 79 | + (void)memset(g_savedPassword, 0, sizeof(g_savedPassword)); | ||
| 80 | + (void)memcpy(g_savedSsid, ssid, ssidLen); | ||
| 81 | + (void)memcpy(g_savedPassword, pwd, pwdLen); | ||
| 82 | + g_wifiExists = true; | ||
| 83 | + return IOTC_OK; | ||
| 84 | +} | ||
| 85 | + | ||
| 86 | +int32_t IotcDeleteWifiInfo(void) { g_wifiExists = false; return IOTC_OK; } | ||
| 87 | +int32_t IotcDisconnectWifi(void) { return IOTC_OK; } | ||
| 88 | +int32_t IotcGetWifiMode(void) { return g_wifiMode; } | ||
| 89 | +int32_t ConnSvcActionConnectWifi(void) { ++g_connectCalls; return IOTC_OK; } | ||
| 90 | +bool ConfigIsNetInfoNeedVerify(void) { return g_needVerify; } | ||
| 91 | +bool ConfigIsNetInfoChecked(void) { return g_checked; } | ||
| 92 | +int32_t ConfigSetNetInfoNeedVerify(void) { g_needVerify = true; g_checked = false; return IOTC_OK; } | ||
| 93 | +int32_t ConfigSetNetInfoChecked(void) { g_needVerify = false; g_checked = true; return IOTC_OK; } | ||
| 94 | +int32_t ConfigClearNetInfoFlag(void) { g_needVerify = false; g_checked = false; return IOTC_OK; } | ||
| 95 | + | ||
| 96 | +int32_t SchedTimerAdd(EventSourceTimerType type, EventSourceTimerCallback callback, uint32_t ms, void *userData) | ||
| 97 | +{ | ||
| 98 | + CHECK_EQ_INT(EVENT_SOURCE_TIMER_TYPE_ONCE, type); | ||
| 99 | + CHECK_EQ_INT(600, ms); | ||
| 100 | + g_timerId = 101; | ||
| 101 | + g_timerCallback = callback; | ||
| 102 | + g_timerUserData = userData; | ||
| 103 | + return g_timerId; | ||
| 104 | +} | ||
| 105 | + | ||
| 106 | +void SchedTimerRemove(int32_t id) { CHECK_EQ_INT(g_timerId, id); } | ||
| 107 | +int32_t EventBusSubscribe(EventBusCallback cb, uint32_t event) { (void)event; g_softapStopCallback = cb; return IOTC_OK; } | ||
| 108 | +int32_t EventBusUnsubscribe(EventBusCallback cb) { CHECK(cb == g_softapStopCallback); return IOTC_OK; } | ||
| 109 | + | ||
| 110 | +static void TestCommit(void) | ||
| 111 | +{ | ||
| 112 | + Reset(); | ||
| 113 | + const IotcJson *json = (const IotcJson *)(uintptr_t)1; | ||
| 114 | + CHECK_EQ_INT(IOTC_OK, SvcConnPrepareNetInfo(json)); | ||
| 115 | + CHECK(strcmp(g_savedSsid, "new-home") == 0); | ||
| 116 | + CHECK(g_needVerify); | ||
| 117 | + CHECK_EQ_INT(IOTC_OK, SvcConnCommitNetInfo()); | ||
| 118 | + CHECK(g_timerCallback != NULL); | ||
| 119 | + g_timerCallback(g_timerId, g_timerUserData); | ||
| 120 | + CHECK_EQ_INT(1, g_connectCalls); | ||
| 121 | +} | ||
| 122 | + | ||
| 123 | +static void TestRollback(void) | ||
| 124 | +{ | ||
| 125 | + Reset(); | ||
| 126 | + const IotcJson *json = (const IotcJson *)(uintptr_t)1; | ||
| 127 | + CHECK_EQ_INT(IOTC_OK, SvcConnPrepareNetInfo(json)); | ||
| 128 | + CHECK_EQ_INT(IOTC_OK, SvcConnRollbackNetInfo()); | ||
| 129 | + CHECK(strcmp(g_savedSsid, "old-home") == 0); | ||
| 130 | + CHECK(strcmp(g_savedPassword, "old-password") == 0); | ||
| 131 | + CHECK(g_checked); | ||
| 132 | +} | ||
| 133 | + | ||
| 134 | +int main(void) | ||
| 135 | +{ | ||
| 136 | + setbuf(stdout, NULL); | ||
| 137 | + TestCommit(); | ||
| 138 | + TestRollback(); | ||
| 139 | + printf("test_net_info_transaction: %d checks, %d failures\n", g_testChecks, g_testFailures); | ||
| 140 | + return g_testFailures == 0 ? 0 : 1; | ||
| 141 | +} | ||
| @@ -0,0 +1,60 @@ | |||
| 1 | +#!/bin/bash | ||
| 2 | +# WiFi Only 配网状态版本化存储与恢复出厂 host 单元测试。 | ||
| 3 | +# 只用本机 gcc,不依赖 OpenHarmony 构建系统,不在源码树内留下生成物。 | ||
| 4 | +set -uo pipefail | ||
| 5 | + | ||
| 6 | +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) | ||
| 7 | +IOTC_ROOT=$(cd "$SCRIPT_DIR/../../.." && pwd) | ||
| 8 | +OUT_DIR=$(mktemp -d /tmp/iotc_device_config_host_test.XXXXXX) | ||
| 9 | +trap 'rm -rf "$OUT_DIR"' EXIT | ||
| 10 | + | ||
| 11 | +CFLAGS=( | ||
| 12 | + -std=c99 -Wall -Wextra -Werror -g -O0 | ||
| 13 | + -I"$SCRIPT_DIR" | ||
| 14 | + -I"$SCRIPT_DIR/stubs" | ||
| 15 | + -I"$IOTC_ROOT/adapter/include" | ||
| 16 | + -I"$IOTC_ROOT/interfaces/kits/common" | ||
| 17 | + -I"$IOTC_ROOT/core/infrastructure/utils/include" | ||
| 18 | + -I"$IOTC_ROOT/core/infrastructure/define" | ||
| 19 | + -I"$IOTC_ROOT/core/device/config/include" | ||
| 20 | +) | ||
| 21 | + | ||
| 22 | +echo "[1/3] build test_store" | ||
| 23 | +gcc "${CFLAGS[@]}" "$SCRIPT_DIR/test_versioned_store.c" \ | ||
| 24 | + "$IOTC_ROOT/core/device/config/versioned_store.c" \ | ||
| 25 | + "$SCRIPT_DIR/stubs/iotc_kv_fake.c" \ | ||
| 26 | + -o "$OUT_DIR/test_store" || exit 1 | ||
| 27 | + | ||
| 28 | +echo "[2/3] build test_dss" | ||
| 29 | +gcc "${CFLAGS[@]}" "$SCRIPT_DIR/test_device_state_store.c" \ | ||
| 30 | + "$IOTC_ROOT/core/device/config/versioned_store.c" \ | ||
| 31 | + "$IOTC_ROOT/core/device/config/device_state_store.c" \ | ||
| 32 | + "$SCRIPT_DIR/stubs/iotc_kv_fake.c" \ | ||
| 33 | + -o "$OUT_DIR/test_dss" || exit 1 | ||
| 34 | + | ||
| 35 | +echo "[3/3] build test_config_revoke" | ||
| 36 | +gcc -I"$SCRIPT_DIR/stubs/revoke" "${CFLAGS[@]}" -DIOTC_CONF_WIFI_ONLY_SUPPORT=1 \ | ||
| 37 | + "$SCRIPT_DIR/test_config_revoke.c" \ | ||
| 38 | + "$IOTC_ROOT/core/device/config/config_revoke.c" \ | ||
| 39 | + "$SCRIPT_DIR/stubs/event_bus_stub.c" \ | ||
| 40 | + -o "$OUT_DIR/test_config_revoke" || exit 1 | ||
| 41 | + | ||
| 42 | +overall=0 | ||
| 43 | +echo "run test_store" | ||
| 44 | +"$OUT_DIR/test_store" | ||
| 45 | +[ $? -ne 0 ] && overall=1 | ||
| 46 | + | ||
| 47 | +echo "run test_dss" | ||
| 48 | +"$OUT_DIR/test_dss" | ||
| 49 | +[ $? -ne 0 ] && overall=1 | ||
| 50 | + | ||
| 51 | +echo "run test_config_revoke" | ||
| 52 | +"$OUT_DIR/test_config_revoke" | ||
| 53 | +[ $? -ne 0 ] && overall=1 | ||
| 54 | + | ||
| 55 | +if [ "$overall" -eq 0 ]; then | ||
| 56 | + echo "DEVICE CONFIG HOST TESTS PASSED" | ||
| 57 | +else | ||
| 58 | + echo "DEVICE CONFIG HOST TESTS FAILED" | ||
| 59 | +fi | ||
| 60 | +exit "$overall" | ||
| @@ -0,0 +1,26 @@ | |||
| 1 | +/* | ||
| 2 | + * Host-test stub of EventBus public API. Mirrors the real signatures and the | ||
| 3 | + * EventBusPublishSync macro; publishes invoke the subscriber synchronously. | ||
| 4 | + */ | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + | ||
| 11 | +typedef void (*EventBusCallback)(uint32_t event, void *param, uint32_t len); | ||
| 12 | + | ||
| 13 | +int32_t EventBusSubscribe(EventBusCallback listener, uint32_t event); | ||
| 14 | +int32_t EventBusUnsubscribe(EventBusCallback listener); | ||
| 15 | +void EventBusPublishSyncInner(uint32_t event, const char *name, void *param, uint32_t len); | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | +/* Test knobs. */ | ||
| 20 | +extern EventBusCallback g_eventBusStubCallback; | ||
| 21 | +extern uint32_t g_eventBusStubEvent; | ||
| 22 | +extern int g_eventBusStubPublishCalls; | ||
| 23 | +extern uint32_t g_eventBusStubLastEvent; | ||
| 24 | +extern int32_t g_eventBusStubSubscribeResult; | ||
| 25 | + | ||
| 26 | + | ||
| @@ -0,0 +1,41 @@ | |||
| 1 | +/* | ||
| 2 | + * Host-test stub of EventBus: single subscriber slot, synchronous publish. | ||
| 3 | + */ | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | +EventBusCallback g_eventBusStubCallback; | ||
| 10 | +uint32_t g_eventBusStubEvent; | ||
| 11 | +int g_eventBusStubPublishCalls; | ||
| 12 | +uint32_t g_eventBusStubLastEvent; | ||
| 13 | +int32_t g_eventBusStubSubscribeResult; | ||
| 14 | + | ||
| 15 | +int32_t EventBusSubscribe(EventBusCallback listener, uint32_t event) | ||
| 16 | +{ | ||
| 17 | + if (g_eventBusStubSubscribeResult != 0) { | ||
| 18 | + return g_eventBusStubSubscribeResult; | ||
| 19 | + } | ||
| 20 | + g_eventBusStubCallback = listener; | ||
| 21 | + g_eventBusStubEvent = event; | ||
| 22 | + return 0; | ||
| 23 | +} | ||
| 24 | + | ||
| 25 | +int32_t EventBusUnsubscribe(EventBusCallback listener) | ||
| 26 | +{ | ||
| 27 | + if (g_eventBusStubCallback == listener) { | ||
| 28 | + g_eventBusStubCallback = 0; | ||
| 29 | + } | ||
| 30 | + return 0; | ||
| 31 | +} | ||
| 32 | + | ||
| 33 | +void EventBusPublishSyncInner(uint32_t event, const char *name, void *param, uint32_t len) | ||
| 34 | +{ | ||
| 35 | + (void)name; | ||
| 36 | + ++g_eventBusStubPublishCalls; | ||
| 37 | + g_eventBusStubLastEvent = event; | ||
| 38 | + if (g_eventBusStubCallback != 0 && event == g_eventBusStubEvent) { | ||
| 39 | + g_eventBusStubCallback(event, param, len); | ||
| 40 | + } | ||
| 41 | +} | ||
| @@ -0,0 +1,199 @@ | |||
| 1 | +/* | ||
| 2 | + * Fault-injecting fake of adapter IotcKv for host power-cut tests. | ||
| 3 | + * RAM 镜像在“断电”后保留;写/删操作计数用于在指定操作点注入故障。 | ||
| 4 | + */ | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | +typedef struct { | ||
| 18 | + char key[KV_FAKE_MAX_KEY_LEN]; | ||
| 19 | + uint8_t value[KV_FAKE_MAX_VALUE_LEN]; | ||
| 20 | + uint32_t valueLen; | ||
| 21 | + int present; | ||
| 22 | +} KvFakeEntry; | ||
| 23 | + | ||
| 24 | +static KvFakeEntry g_entries[KV_FAKE_MAX_ENTRIES]; | ||
| 25 | +static int g_crashed; | ||
| 26 | +static int g_opCount; | ||
| 27 | +static int g_armOp = -1; | ||
| 28 | +static int g_armFault = KV_FAULT_NONE; | ||
| 29 | + | ||
| 30 | +static KvFakeEntry *FindEntry(const char *key) | ||
| 31 | +{ | ||
| 32 | + for (int i = 0; i < KV_FAKE_MAX_ENTRIES; ++i) { | ||
| 33 | + if (g_entries[i].present && strncmp(g_entries[i].key, key, KV_FAKE_MAX_KEY_LEN) == 0) { | ||
| 34 | + return &g_entries[i]; | ||
| 35 | + } | ||
| 36 | + } | ||
| 37 | + return NULL; | ||
| 38 | +} | ||
| 39 | + | ||
| 40 | +void KvFakeReset(void) | ||
| 41 | +{ | ||
| 42 | + (void)memset(g_entries, 0, sizeof(g_entries)); | ||
| 43 | + g_crashed = 0; | ||
| 44 | + g_opCount = 0; | ||
| 45 | + g_armOp = -1; | ||
| 46 | + g_armFault = KV_FAULT_NONE; | ||
| 47 | +} | ||
| 48 | + | ||
| 49 | +void KvFakeArm(int opIndex, int fault) | ||
| 50 | +{ | ||
| 51 | + g_armOp = opIndex; | ||
| 52 | + g_armFault = fault; | ||
| 53 | +} | ||
| 54 | + | ||
| 55 | +void KvFakeReboot(void) | ||
| 56 | +{ | ||
| 57 | + g_crashed = 0; | ||
| 58 | + g_opCount = 0; | ||
| 59 | + g_armOp = -1; | ||
| 60 | + g_armFault = KV_FAULT_NONE; | ||
| 61 | +} | ||
| 62 | + | ||
| 63 | +int32_t KvFakePresentCount(void) | ||
| 64 | +{ | ||
| 65 | + int32_t count = 0; | ||
| 66 | + for (int i = 0; i < KV_FAKE_MAX_ENTRIES; ++i) { | ||
| 67 | + count += g_entries[i].present; | ||
| 68 | + } | ||
| 69 | + return count; | ||
| 70 | +} | ||
| 71 | + | ||
| 72 | +int32_t KvFakeContainsBytes(const uint8_t *needle, uint32_t needleLen) | ||
| 73 | +{ | ||
| 74 | + if (needle == NULL || needleLen == 0) { | ||
| 75 | + return 0; | ||
| 76 | + } | ||
| 77 | + for (int i = 0; i < KV_FAKE_MAX_ENTRIES; ++i) { | ||
| 78 | + if (!g_entries[i].present || needleLen > g_entries[i].valueLen) { | ||
| 79 | + continue; | ||
| 80 | + } | ||
| 81 | + for (uint32_t offset = 0; offset <= g_entries[i].valueLen - needleLen; ++offset) { | ||
| 82 | + if (memcmp(g_entries[i].value + offset, needle, needleLen) == 0) { | ||
| 83 | + return 1; | ||
| 84 | + } | ||
| 85 | + } | ||
| 86 | + } | ||
| 87 | + return 0; | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | +int32_t KvFakeCorrupt(const char *key) | ||
| 91 | +{ | ||
| 92 | + KvFakeEntry *entry = FindEntry(key); | ||
| 93 | + if (entry == NULL) { | ||
| 94 | + return -1; | ||
| 95 | + } | ||
| 96 | + for (uint32_t i = 0; i < entry->valueLen; i += 2) { | ||
| 97 | + entry->value[i] ^= 0xA5; | ||
| 98 | + } | ||
| 99 | + return 0; | ||
| 100 | +} | ||
| 101 | + | ||
| 102 | +static int32_t StoreValue(const char *key, const uint8_t *value, uint32_t len, int torn) | ||
| 103 | +{ | ||
| 104 | + KvFakeEntry *entry = FindEntry(key); | ||
| 105 | + if (entry == NULL) { | ||
| 106 | + for (int i = 0; i < KV_FAKE_MAX_ENTRIES; ++i) { | ||
| 107 | + if (!g_entries[i].present) { | ||
| 108 | + entry = &g_entries[i]; | ||
| 109 | + break; | ||
| 110 | + } | ||
| 111 | + } | ||
| 112 | + } | ||
| 113 | + if (entry == NULL || len > KV_FAKE_MAX_VALUE_LEN) { | ||
| 114 | + return -1; | ||
| 115 | + } | ||
| 116 | + uint32_t storedLen = torn ? (len / 2) : len; | ||
| 117 | + (void)memset(entry, 0, sizeof(*entry)); | ||
| 118 | + (void)strncpy(entry->key, key, KV_FAKE_MAX_KEY_LEN - 1); | ||
| 119 | + (void)memcpy(entry->value, value, storedLen); | ||
| 120 | + entry->valueLen = storedLen; | ||
| 121 | + entry->present = 1; | ||
| 122 | + return 0; | ||
| 123 | +} | ||
| 124 | + | ||
| 125 | +int32_t IotcKvInit(const char *tag) | ||
| 126 | +{ | ||
| 127 | + (void)tag; | ||
| 128 | + return 0; | ||
| 129 | +} | ||
| 130 | + | ||
| 131 | +int32_t IotcKvDeInit(void) | ||
| 132 | +{ | ||
| 133 | + return 0; | ||
| 134 | +} | ||
| 135 | + | ||
| 136 | +int32_t IotcKvSetValue(const char *key, const uint8_t *value, uint32_t len) | ||
| 137 | +{ | ||
| 138 | + if (key == NULL || value == NULL || len == 0 || g_crashed) { | ||
| 139 | + return -1; | ||
| 140 | + } | ||
| 141 | + int opIndex = g_opCount++; | ||
| 142 | + if (opIndex == g_armOp) { | ||
| 143 | + g_crashed = 1; | ||
| 144 | + if (g_armFault == KV_FAULT_TORN_WRITE) { | ||
| 145 | + (void)StoreValue(key, value, len, 1); | ||
| 146 | + } | ||
| 147 | + return -1; | ||
| 148 | + } | ||
| 149 | + return StoreValue(key, value, len, 0); | ||
| 150 | +} | ||
| 151 | + | ||
| 152 | +int32_t IotcKvGetValue(const char *key, uint8_t *buf, uint32_t *len) | ||
| 153 | +{ | ||
| 154 | + if (key == NULL || buf == NULL || len == NULL || g_crashed) { | ||
| 155 | + return -1; | ||
| 156 | + } | ||
| 157 | + KvFakeEntry *entry = FindEntry(key); | ||
| 158 | + if (entry == NULL || *len < entry->valueLen) { | ||
| 159 | + return -1; | ||
| 160 | + } | ||
| 161 | + (void)memcpy(buf, entry->value, entry->valueLen); | ||
| 162 | + *len = entry->valueLen; | ||
| 163 | + return 0; | ||
| 164 | +} | ||
| 165 | + | ||
| 166 | +int32_t IotcKvGetLen(const char *key, uint32_t *len) | ||
| 167 | +{ | ||
| 168 | + if (key == NULL || len == NULL || g_crashed) { | ||
| 169 | + return -1; | ||
| 170 | + } | ||
| 171 | + KvFakeEntry *entry = FindEntry(key); | ||
| 172 | + if (entry == NULL) { | ||
| 173 | + return -1; | ||
| 174 | + } | ||
| 175 | + *len = entry->valueLen; | ||
| 176 | + return 0; | ||
| 177 | +} | ||
| 178 | + | ||
| 179 | +int32_t IotcKvDelValue(const char *key) | ||
| 180 | +{ | ||
| 181 | + if (key == NULL || g_crashed) { | ||
| 182 | + return -1; | ||
| 183 | + } | ||
| 184 | + int opIndex = g_opCount++; | ||
| 185 | + if (opIndex == g_armOp) { | ||
| 186 | + g_crashed = 1; | ||
| 187 | + if (g_armFault == KV_FAULT_TORN_ERASE) { | ||
| 188 | + (void)KvFakeCorrupt(key); | ||
| 189 | + return 0; | ||
| 190 | + } | ||
| 191 | + return -1; | ||
| 192 | + } | ||
| 193 | + KvFakeEntry *entry = FindEntry(key); | ||
| 194 | + if (entry == NULL) { | ||
| 195 | + return -1; | ||
| 196 | + } | ||
| 197 | + (void)memset(entry, 0, sizeof(*entry)); | ||
| 198 | + return 0; | ||
| 199 | +} | ||
| @@ -0,0 +1,29 @@ | |||
| 1 | +/* | ||
| 2 | + * Fault-injecting fake of adapter IotcKv for host power-cut tests. | ||
| 3 | + */ | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | +/* 故障类型:在 arm 的第 opIndex 个写/删操作上注入。 */ | ||
| 11 | +enum { | ||
| 12 | + KV_FAULT_NONE = 0, | ||
| 13 | + KV_FAULT_FAIL, /* 操作失败并断电(后续操作全部失败,直到 KvFakeReboot) */ | ||
| 14 | + KV_FAULT_TORN_WRITE, /* 撕裂写:只落盘前半截记录,然后断电 */ | ||
| 15 | + KV_FAULT_TORN_ERASE /* 撕裂擦除:数据被破坏但残留存在,然后断电 */ | ||
| 16 | +}; | ||
| 17 | + | ||
| 18 | +void KvFakeReset(void); | ||
| 19 | +void KvFakeArm(int opIndex, int fault); | ||
| 20 | +/* 模拟重新上电:不清 RAM 镜像,只恢复“已断电”状态并复位操作计数。 */ | ||
| 21 | +void KvFakeReboot(void); | ||
| 22 | +int32_t KvFakePresentCount(void); | ||
| 23 | + | ||
| 24 | +/* 返回 RAM KV 镜像中是否出现指定连续字节,用于验证秘密未落入通用状态槽。 */ | ||
| 25 | +int32_t KvFakeContainsBytes(const uint8_t *needle, uint32_t needleLen); | ||
| 26 | +/* 测试辅助:直接破坏指定 key 的数据但保留 key(模拟槽位损坏)。 */ | ||
| 27 | +int32_t KvFakeCorrupt(const char *key); | ||
| 28 | + | ||
| 29 | + | ||
| @@ -0,0 +1,15 @@ | |||
| 1 | +/* | ||
| 2 | + * Host-test stub of adapter log header. Logs are discarded. | ||
| 3 | + */ | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| @@ -0,0 +1,8 @@ | |||
| 1 | + | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | +int32_t ConfigClearAuthInfo(void); | ||
| 7 | + | ||
| 8 | + | ||
| @@ -0,0 +1,8 @@ | |||
| 1 | + | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | +int32_t ConfigClearLoginInfo(void); | ||
| 7 | + | ||
| 8 | + | ||
| @@ -0,0 +1,8 @@ | |||
| 1 | + | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | +int32_t ConfigClearRegisterInfo(void); | ||
| 7 | + | ||
| 8 | + | ||
| @@ -0,0 +1,8 @@ | |||
| 1 | + | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | +int32_t IotcDssFactoryResetRun(void); | ||
| 7 | + | ||
| 8 | + | ||
| @@ -0,0 +1,8 @@ | |||
| 1 | + | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | +int32_t SecConfInfoFactoryReset(void); | ||
| 7 | + | ||
| 8 | + | ||
| @@ -0,0 +1,8 @@ | |||
| 1 | + | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | +int32_t SecConfKeyCertFactoryReset(void); | ||
| 7 | + | ||
| 8 | + | ||
| @@ -0,0 +1,14 @@ | |||
| 1 | + | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +typedef enum { | ||
| 5 | + COMBO_TYPE_INVALID = -1, | ||
| 6 | + COMBO_TYPE_COMBO = 0, | ||
| 7 | + COMBO_TYPE_WIFI_ONLY, | ||
| 8 | + COMBO_TYPE_BLE_ONLY, | ||
| 9 | + COMBO_TYPE_SLE_ONLY, | ||
| 10 | +} ComboType; | ||
| 11 | + | ||
| 12 | +ComboType UtilsGetComboType(void); | ||
| 13 | + | ||
| 14 | + | ||
| @@ -0,0 +1,131 @@ | |||
| 1 | +/* | ||
| 2 | + * Host-test stub of third_party bounds_checking_function securec.h. | ||
| 3 | + * Provides the _s functions used by device config sources. | ||
| 4 | + */ | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | +typedef int errno_t; | ||
| 15 | +typedef size_t rsize_t; | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | +static inline errno_t memset_s(void *dest, rsize_t destMax, int c, rsize_t count) | ||
| 20 | +{ | ||
| 21 | + if (dest == NULL || count > destMax) { | ||
| 22 | + return EINVAL; | ||
| 23 | + } | ||
| 24 | + (void)memset(dest, c, count); | ||
| 25 | + return EOK; | ||
| 26 | +} | ||
| 27 | + | ||
| 28 | +static inline errno_t memcpy_s(void *dest, rsize_t destMax, const void *src, rsize_t count) | ||
| 29 | +{ | ||
| 30 | + if (dest == NULL || src == NULL || count > destMax) { | ||
| 31 | + return EINVAL; | ||
| 32 | + } | ||
| 33 | + (void)memcpy(dest, src, count); | ||
| 34 | + return EOK; | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +static inline errno_t memmove_s(void *dest, rsize_t destMax, const void *src, rsize_t count) | ||
| 38 | +{ | ||
| 39 | + if (dest == NULL || src == NULL || count > destMax) { | ||
| 40 | + return EINVAL; | ||
| 41 | + } | ||
| 42 | + (void)memmove(dest, src, count); | ||
| 43 | + return EOK; | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | +static inline errno_t strcpy_s(char *dest, rsize_t destMax, const char *src) | ||
| 47 | +{ | ||
| 48 | + if (dest == NULL || src == NULL || destMax == 0) { | ||
| 49 | + return EINVAL; | ||
| 50 | + } | ||
| 51 | + size_t len = strlen(src); | ||
| 52 | + if (len >= destMax) { | ||
| 53 | + dest[0] = '\0'; | ||
| 54 | + return EINVAL; | ||
| 55 | + } | ||
| 56 | + (void)memcpy(dest, src, len + 1); | ||
| 57 | + return EOK; | ||
| 58 | +} | ||
| 59 | + | ||
| 60 | +static inline errno_t strncpy_s(char *dest, rsize_t destMax, const char *src, rsize_t count) | ||
| 61 | +{ | ||
| 62 | + if (dest == NULL || src == NULL || destMax == 0 || count >= destMax) { | ||
| 63 | + if (dest != NULL && destMax > 0) { | ||
| 64 | + dest[0] = '\0'; | ||
| 65 | + } | ||
| 66 | + return EINVAL; | ||
| 67 | + } | ||
| 68 | + rsize_t srcLen = strlen(src); | ||
| 69 | + rsize_t copyLen = srcLen < count ? srcLen : count; | ||
| 70 | + (void)memcpy(dest, src, copyLen); | ||
| 71 | + dest[copyLen] = '\0'; | ||
| 72 | + return EOK; | ||
| 73 | +} | ||
| 74 | + | ||
| 75 | +static inline int snprintf_s(char *dest, rsize_t destMax, rsize_t count, const char *format, ...) | ||
| 76 | +{ | ||
| 77 | + if (dest == NULL || destMax == 0 || format == NULL) { | ||
| 78 | + return -1; | ||
| 79 | + } | ||
| 80 | + va_list ap; | ||
| 81 | + va_start(ap, format); | ||
| 82 | + int written = vsnprintf(dest, destMax, format, ap); | ||
| 83 | + va_end(ap); | ||
| 84 | + if (written < 0 || (rsize_t)written >= count) { | ||
| 85 | + dest[0] = '\0'; | ||
| 86 | + return -1; | ||
| 87 | + } | ||
| 88 | + return written; | ||
| 89 | +} | ||
| 90 | + | ||
| 91 | +static inline int sprintf_s(char *dest, rsize_t destMax, const char *format, ...) | ||
| 92 | +{ | ||
| 93 | + if (dest == NULL || destMax == 0 || format == NULL) { | ||
| 94 | + return -1; | ||
| 95 | + } | ||
| 96 | + va_list ap; | ||
| 97 | + va_start(ap, format); | ||
| 98 | + int written = vsnprintf(dest, destMax, format, ap); | ||
| 99 | + va_end(ap); | ||
| 100 | + if (written < 0 || (rsize_t)written >= destMax) { | ||
| 101 | + dest[0] = '\0'; | ||
| 102 | + return -1; | ||
| 103 | + } | ||
| 104 | + return written; | ||
| 105 | +} | ||
| 106 | + | ||
| 107 | +static inline int sscanf_s(const char *buffer, const char *format, ...) | ||
| 108 | +{ | ||
| 109 | + if (buffer == NULL || format == NULL) { | ||
| 110 | + return -1; | ||
| 111 | + } | ||
| 112 | + va_list ap; | ||
| 113 | + va_start(ap, format); | ||
| 114 | + int matched = vsscanf(buffer, format, ap); | ||
| 115 | + va_end(ap); | ||
| 116 | + return matched; | ||
| 117 | +} | ||
| 118 | + | ||
| 119 | +static inline rsize_t strnlen_s(const char *s, rsize_t maxSize) | ||
| 120 | +{ | ||
| 121 | + if (s == NULL) { | ||
| 122 | + return 0; | ||
| 123 | + } | ||
| 124 | + rsize_t len = 0; | ||
| 125 | + while (len < maxSize && s[len] != '\0') { | ||
| 126 | + ++len; | ||
| 127 | + } | ||
| 128 | + return len; | ||
| 129 | +} | ||
| 130 | + | ||
| 131 | + | ||
| @@ -0,0 +1,157 @@ | |||
| 1 | +/* | ||
| 2 | + * Host regression for the synchronous WiFi Only factory-reset subscriber. | ||
| 3 | + */ | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +int g_testChecks; | ||
| 16 | +int g_testFailures; | ||
| 17 | + | ||
| 18 | +static int32_t g_dssResult; | ||
| 19 | +static int32_t g_securityResult; | ||
| 20 | +static int32_t g_keyCertResult; | ||
| 21 | +static int32_t g_loginResult; | ||
| 22 | +static int32_t g_registerResult; | ||
| 23 | +static int g_dssCalls; | ||
| 24 | +static int g_securityCalls; | ||
| 25 | +static int g_keyCertCalls; | ||
| 26 | +static int g_loginCalls; | ||
| 27 | +static int g_registerCalls; | ||
| 28 | +static char g_callOrder[12]; | ||
| 29 | +static size_t g_callOrderLen; | ||
| 30 | + | ||
| 31 | +static void RecordCall(char call) | ||
| 32 | +{ | ||
| 33 | + if (g_callOrderLen + 1U < sizeof(g_callOrder)) { | ||
| 34 | + g_callOrder[g_callOrderLen++] = call; | ||
| 35 | + g_callOrder[g_callOrderLen] = '\0'; | ||
| 36 | + } | ||
| 37 | +} | ||
| 38 | + | ||
| 39 | +int32_t IotcDssFactoryResetRun(void) | ||
| 40 | +{ | ||
| 41 | + ++g_dssCalls; | ||
| 42 | + RecordCall('D'); | ||
| 43 | + return g_dssResult; | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | +int32_t ConfigClearRegisterInfo(void) | ||
| 47 | +{ | ||
| 48 | + ++g_registerCalls; | ||
| 49 | + RecordCall('R'); | ||
| 50 | + return g_registerResult; | ||
| 51 | +} | ||
| 52 | + | ||
| 53 | +int32_t SecConfInfoFactoryReset(void) | ||
| 54 | +{ | ||
| 55 | + ++g_securityCalls; | ||
| 56 | + RecordCall('S'); | ||
| 57 | + return g_securityResult; | ||
| 58 | +} | ||
| 59 | + | ||
| 60 | +int32_t SecConfKeyCertFactoryReset(void) | ||
| 61 | +{ | ||
| 62 | + ++g_keyCertCalls; | ||
| 63 | + RecordCall('K'); | ||
| 64 | + return g_keyCertResult; | ||
| 65 | +} | ||
| 66 | + | ||
| 67 | +int32_t ConfigClearLoginInfo(void) | ||
| 68 | +{ | ||
| 69 | + ++g_loginCalls; | ||
| 70 | + RecordCall('L'); | ||
| 71 | + return g_loginResult; | ||
| 72 | +} | ||
| 73 | + | ||
| 74 | +int32_t ConfigClearAuthInfo(void) | ||
| 75 | +{ | ||
| 76 | + return 0; | ||
| 77 | +} | ||
| 78 | + | ||
| 79 | +ComboType UtilsGetComboType(void) | ||
| 80 | +{ | ||
| 81 | + return COMBO_TYPE_WIFI_ONLY; | ||
| 82 | +} | ||
| 83 | + | ||
| 84 | +static void ResetState(void) | ||
| 85 | +{ | ||
| 86 | + g_eventBusStubCallback = NULL; | ||
| 87 | + g_eventBusStubEvent = 0; | ||
| 88 | + g_eventBusStubPublishCalls = 0; | ||
| 89 | + g_eventBusStubLastEvent = 0; | ||
| 90 | + g_eventBusStubSubscribeResult = 0; | ||
| 91 | + g_dssResult = 0; | ||
| 92 | + g_securityResult = 0; | ||
| 93 | + g_keyCertResult = 0; | ||
| 94 | + g_loginResult = 0; | ||
| 95 | + g_registerResult = 0; | ||
| 96 | + g_dssCalls = 0; | ||
| 97 | + g_securityCalls = 0; | ||
| 98 | + g_keyCertCalls = 0; | ||
| 99 | + g_loginCalls = 0; | ||
| 100 | + g_registerCalls = 0; | ||
| 101 | + g_callOrderLen = 0; | ||
| 102 | + (void)memset(g_callOrder, 0, sizeof(g_callOrder)); | ||
| 103 | +} | ||
| 104 | + | ||
| 105 | +static void TestSynchronousResetOrder(void) | ||
| 106 | +{ | ||
| 107 | + ResetState(); | ||
| 108 | + CHECK_EQ_INT(0, ConfigRevokeInit()); | ||
| 109 | + CHECK(g_eventBusStubCallback != NULL); | ||
| 110 | + CHECK_EQ_INT(IOTC_CORE_COMM_EVENT_MAIN_RESTORE, g_eventBusStubEvent); | ||
| 111 | + | ||
| 112 | + EventBusPublishSync(IOTC_CORE_COMM_EVENT_MAIN_RESTORE, NULL, 0); | ||
| 113 | + CHECK_EQ_INT(1, g_dssCalls); | ||
| 114 | + CHECK_EQ_INT(1, g_securityCalls); | ||
| 115 | + CHECK_EQ_INT(1, g_keyCertCalls); | ||
| 116 | + CHECK_EQ_INT(1, g_loginCalls); | ||
| 117 | + CHECK_EQ_INT(1, g_registerCalls); | ||
| 118 | + CHECK(strcmp(g_callOrder, "DSKLR") == 0); | ||
| 119 | + | ||
| 120 | + ConfigRevokeDeinit(); | ||
| 121 | + CHECK(g_eventBusStubCallback == NULL); | ||
| 122 | +} | ||
| 123 | + | ||
| 124 | +static void TestDssFailureStillRunsLegacyCleanup(void) | ||
| 125 | +{ | ||
| 126 | + ResetState(); | ||
| 127 | + g_dssResult = -17; | ||
| 128 | + g_registerResult = -19; | ||
| 129 | + CHECK_EQ_INT(0, ConfigRevokeInit()); | ||
| 130 | + EventBusPublishSync(IOTC_CORE_COMM_EVENT_MAIN_RESTORE, NULL, 0); | ||
| 131 | + CHECK_EQ_INT(1, g_dssCalls); | ||
| 132 | + CHECK_EQ_INT(1, g_securityCalls); | ||
| 133 | + CHECK_EQ_INT(1, g_keyCertCalls); | ||
| 134 | + CHECK_EQ_INT(1, g_loginCalls); | ||
| 135 | + CHECK_EQ_INT(1, g_registerCalls); | ||
| 136 | + CHECK(strcmp(g_callOrder, "DSKLR") == 0); | ||
| 137 | + ConfigRevokeDeinit(); | ||
| 138 | +} | ||
| 139 | + | ||
| 140 | +static void TestSubscribeFailure(void) | ||
| 141 | +{ | ||
| 142 | + ResetState(); | ||
| 143 | + g_eventBusStubSubscribeResult = -23; | ||
| 144 | + CHECK_EQ_INT(-23, ConfigRevokeInit()); | ||
| 145 | + CHECK(g_eventBusStubCallback == NULL); | ||
| 146 | + CHECK_EQ_INT(0, g_dssCalls); | ||
| 147 | + CHECK_EQ_INT(0, g_registerCalls); | ||
| 148 | +} | ||
| 149 | + | ||
| 150 | +int main(void) | ||
| 151 | +{ | ||
| 152 | + TestSynchronousResetOrder(); | ||
| 153 | + TestDssFailureStillRunsLegacyCleanup(); | ||
| 154 | + TestSubscribeFailure(); | ||
| 155 | + printf("test_config_revoke: %d checks, %d failures\n", g_testChecks, g_testFailures); | ||
| 156 | + return (g_testFailures == 0) ? 0 : 1; | ||
| 157 | +} | ||
| @@ -0,0 +1,101 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | +int g_testChecks; | ||
| 25 | +int g_testFailures; | ||
| 26 | + | ||
| 27 | +static IotcDssProvisionPending MakePending(void) | ||
| 28 | +{ | ||
| 29 | + IotcDssProvisionPending pending = {0}; | ||
| 30 | + for (uint8_t i = 0; i < 8; ++i) { | ||
| 31 | + pending.mdid[i] = (uint8_t)(0x30 + i); | ||
| 32 | + } | ||
| 33 | + pending.mdidLen = 8; | ||
| 34 | + (void)strcpy(pending.tempDevId, "temp-dev-0001"); | ||
| 35 | + (void)strcpy(pending.registerCode, "RC-TEST-ONLY-NOT-SECRET"); | ||
| 36 | + pending.state = IOTC_DSS_PENDING_STATE_WAIT_ACTIVATE; | ||
| 37 | + return pending; | ||
| 38 | +} | ||
| 39 | + | ||
| 40 | +static void TestRecordRoundtrip(void) | ||
| 41 | +{ | ||
| 42 | + KvFakeReset(); | ||
| 43 | + IotcDssProvisionPending pending = {0}; | ||
| 44 | + CHECK_EQ_INT(IOTC_CORE_DEVICE_ERR_STORE_NOT_FOUND, IotcDssLoadProvisionPending(&pending)); | ||
| 45 | + IotcDssProvisionPending saved = MakePending(); | ||
| 46 | + CHECK_EQ_INT(IOTC_OK, IotcDssSaveProvisionPending(&saved)); | ||
| 47 | + CHECK_EQ_INT(IOTC_OK, IotcDssLoadProvisionPending(&pending)); | ||
| 48 | + CHECK(memcmp(&pending, &saved, sizeof(pending)) == 0); | ||
| 49 | +} | ||
| 50 | + | ||
| 51 | +static void TestValidation(void) | ||
| 52 | +{ | ||
| 53 | + KvFakeReset(); | ||
| 54 | + IotcDssProvisionPending pending = MakePending(); | ||
| 55 | + pending.mdidLen = IOTC_DSS_MDID_MAX_LEN + 1; | ||
| 56 | + CHECK_EQ_INT(IOTC_ERR_PARAM_INVALID, IotcDssSaveProvisionPending(&pending)); | ||
| 57 | + pending = MakePending(); | ||
| 58 | + pending.state = 99; | ||
| 59 | + CHECK_EQ_INT(IOTC_ERR_PARAM_INVALID, IotcDssSaveProvisionPending(&pending)); | ||
| 60 | + pending = MakePending(); | ||
| 61 | + pending.onlineNotifyDone = 2; | ||
| 62 | + CHECK_EQ_INT(IOTC_ERR_PARAM_INVALID, IotcDssSaveProvisionPending(&pending)); | ||
| 63 | + CHECK_EQ_INT(IOTC_ERR_PARAM_INVALID, IotcDssSaveProvisionPending(NULL)); | ||
| 64 | +} | ||
| 65 | + | ||
| 66 | +static void TestOnlineNotifyDone(void) | ||
| 67 | +{ | ||
| 68 | + KvFakeReset(); | ||
| 69 | + IotcDssProvisionPending pending = MakePending(); | ||
| 70 | + CHECK_EQ_INT(IOTC_OK, IotcDssSaveProvisionPending(&pending)); | ||
| 71 | + CHECK_EQ_INT(IOTC_OK, IotcDssMarkOnlineNotifyDone()); | ||
| 72 | + CHECK_EQ_INT(IOTC_OK, IotcDssLoadProvisionPending(&pending)); | ||
| 73 | + CHECK_EQ_INT(1, pending.onlineNotifyDone); | ||
| 74 | + uint32_t generation = 0; | ||
| 75 | + CHECK_EQ_INT(IOTC_OK, IotcVsGetGeneration(IOTC_DSS_DOMAIN_PROVISION_PENDING, &generation)); | ||
| 76 | + CHECK_EQ_INT(2, generation); | ||
| 77 | + CHECK_EQ_INT(IOTC_OK, IotcDssMarkOnlineNotifyDone()); | ||
| 78 | + CHECK_EQ_INT(IOTC_OK, IotcVsGetGeneration(IOTC_DSS_DOMAIN_PROVISION_PENDING, &generation)); | ||
| 79 | + CHECK_EQ_INT(2, generation); | ||
| 80 | +} | ||
| 81 | + | ||
| 82 | +static void TestFactoryReset(void) | ||
| 83 | +{ | ||
| 84 | + KvFakeReset(); | ||
| 85 | + IotcDssProvisionPending pending = MakePending(); | ||
| 86 | + CHECK_EQ_INT(IOTC_OK, IotcDssSaveProvisionPending(&pending)); | ||
| 87 | + CHECK_EQ_INT(IOTC_OK, IotcDssFactoryResetRun()); | ||
| 88 | + CHECK_EQ_INT(IOTC_CORE_DEVICE_ERR_STORE_NOT_FOUND, IotcDssLoadProvisionPending(&pending)); | ||
| 89 | + CHECK_EQ_INT(0, KvFakePresentCount()); | ||
| 90 | +} | ||
| 91 | + | ||
| 92 | +int main(void) | ||
| 93 | +{ | ||
| 94 | + setbuf(stdout, NULL); | ||
| 95 | + TestRecordRoundtrip(); | ||
| 96 | + TestValidation(); | ||
| 97 | + TestOnlineNotifyDone(); | ||
| 98 | + TestFactoryReset(); | ||
| 99 | + printf("test_dss: %d checks, %d failures\n", g_testChecks, g_testFailures); | ||
| 100 | + return (g_testFailures == 0) ? 0 : 1; | ||
| 101 | +} | ||
| @@ -0,0 +1,33 @@ | |||
| 1 | +/* | ||
| 2 | + * Host unit test check macros for iot_connect device config tests. | ||
| 3 | + */ | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | +extern int g_testChecks; | ||
| 11 | +extern int g_testFailures; | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + do { \ | ||
| 15 | + ++g_testChecks; \ | ||
| 16 | + if (!(cond)) { \ | ||
| 17 | + ++g_testFailures; \ | ||
| 18 | + printf("FAIL %s:%d: %s\n", __FILE__, __LINE__, | ||
| 19 | + } \ | ||
| 20 | + } while (0) | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + do { \ | ||
| 24 | + ++g_testChecks; \ | ||
| 25 | + long expected_ = (long)(expected); \ | ||
| 26 | + long actual_ = (long)(actual); \ | ||
| 27 | + if (expected_ != actual_) { \ | ||
| 28 | + ++g_testFailures; \ | ||
| 29 | + printf("FAIL %s:%d: %s expected %ld got %ld\n", __FILE__, __LINE__, | ||
| 30 | + } \ | ||
| 31 | + } while (0) | ||
| 32 | + | ||
| 33 | + | ||
| @@ -0,0 +1,298 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +/* 双槽版本化存储测试:基本语义 + 断电/撕裂注入矩阵(W4 验证:任一断电点 | ||
| 17 | + * 重启后状态只能是旧有效或新有效,不能半有效)。 */ | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | +int g_testChecks; | ||
| 28 | +int g_testFailures; | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | +static const char *OLD_VALUE = "OLD-VALUE-0123456789"; | ||
| 34 | +static const char *NEW_VALUE = "NEW-VALUE-9876543210"; | ||
| 35 | + | ||
| 36 | +static void SaveCommitted(const char *domain, const char *value) | ||
| 37 | +{ | ||
| 38 | + CHECK_EQ_INT(IOTC_OK, IotcVsBeginWrite(domain, SCHEMA_V1, (const uint8_t *)value, | ||
| 39 | + (uint32_t)strlen(value) + 1)); | ||
| 40 | + CHECK_EQ_INT(IOTC_OK, IotcVsCommit(domain)); | ||
| 41 | +} | ||
| 42 | + | ||
| 43 | +/* 加载并断言内容等于 expected。 */ | ||
| 44 | +static void LoadExpect(const char *domain, const char *expected) | ||
| 45 | +{ | ||
| 46 | + uint8_t buf[64] = {0}; | ||
| 47 | + uint32_t len = sizeof(buf); | ||
| 48 | + CHECK_EQ_INT(IOTC_OK, IotcVsLoad(domain, SCHEMA_V1, buf, &len)); | ||
| 49 | + CHECK_EQ_INT((uint32_t)strlen(expected) + 1, len); | ||
| 50 | + CHECK(memcmp(buf, expected, len) == 0); | ||
| 51 | +} | ||
| 52 | + | ||
| 53 | +static void TestEmptyLoad(void) | ||
| 54 | +{ | ||
| 55 | + KvFakeReset(); | ||
| 56 | + uint8_t buf[16] = {0}; | ||
| 57 | + uint32_t len = sizeof(buf); | ||
| 58 | + CHECK_EQ_INT(IOTC_CORE_DEVICE_ERR_STORE_NOT_FOUND, IotcVsLoad(DEMO_DOMAIN, SCHEMA_V1, buf, &len)); | ||
| 59 | + uint32_t generation = 0; | ||
| 60 | + CHECK_EQ_INT(IOTC_CORE_DEVICE_ERR_STORE_NOT_FOUND, IotcVsGetGeneration(DEMO_DOMAIN, &generation)); | ||
| 61 | +} | ||
| 62 | + | ||
| 63 | +static void TestRoundtripAndGeneration(void) | ||
| 64 | +{ | ||
| 65 | + KvFakeReset(); | ||
| 66 | + SaveCommitted(DEMO_DOMAIN, OLD_VALUE); | ||
| 67 | + LoadExpect(DEMO_DOMAIN, OLD_VALUE); | ||
| 68 | + uint32_t generation = 0; | ||
| 69 | + CHECK_EQ_INT(IOTC_OK, IotcVsGetGeneration(DEMO_DOMAIN, &generation)); | ||
| 70 | + CHECK_EQ_INT(1, generation); | ||
| 71 | + CHECK_EQ_INT(1, KvFakePresentCount()); | ||
| 72 | + | ||
| 73 | + SaveCommitted(DEMO_DOMAIN, NEW_VALUE); | ||
| 74 | + LoadExpect(DEMO_DOMAIN, NEW_VALUE); | ||
| 75 | + CHECK_EQ_INT(IOTC_OK, IotcVsGetGeneration(DEMO_DOMAIN, &generation)); | ||
| 76 | + CHECK_EQ_INT(2, generation); | ||
| 77 | + | ||
| 78 | + /* 第三次写入应回到第一个槽位,但对外只表现为 generation 递增 */ | ||
| 79 | + SaveCommitted(DEMO_DOMAIN, OLD_VALUE); | ||
| 80 | + LoadExpect(DEMO_DOMAIN, OLD_VALUE); | ||
| 81 | + CHECK_EQ_INT(IOTC_OK, IotcVsGetGeneration(DEMO_DOMAIN, &generation)); | ||
| 82 | + CHECK_EQ_INT(3, generation); | ||
| 83 | + CHECK_EQ_INT(1, KvFakePresentCount()); | ||
| 84 | +} | ||
| 85 | + | ||
| 86 | +static void TestSchemaAndBuffer(void) | ||
| 87 | +{ | ||
| 88 | + KvFakeReset(); | ||
| 89 | + SaveCommitted(DEMO_DOMAIN, OLD_VALUE); | ||
| 90 | + | ||
| 91 | + uint8_t buf[64] = {0}; | ||
| 92 | + uint32_t len = sizeof(buf); | ||
| 93 | + CHECK_EQ_INT(IOTC_CORE_DEVICE_ERR_STORE_SCHEMA, IotcVsLoad(DEMO_DOMAIN, 2, buf, &len)); | ||
| 94 | + /* schema 不匹配不破坏原数据 */ | ||
| 95 | + LoadExpect(DEMO_DOMAIN, OLD_VALUE); | ||
| 96 | + | ||
| 97 | + uint8_t tiny[4] = {0}; | ||
| 98 | + len = sizeof(tiny); | ||
| 99 | + CHECK_EQ_INT(IOTC_ERR_PARAM_INVALID, IotcVsLoad(DEMO_DOMAIN, SCHEMA_V1, tiny, &len)); | ||
| 100 | +} | ||
| 101 | + | ||
| 102 | +static void TestParamValidation(void) | ||
| 103 | +{ | ||
| 104 | + KvFakeReset(); | ||
| 105 | + uint8_t buf[16] = {0}; | ||
| 106 | + uint32_t len = sizeof(buf); | ||
| 107 | + CHECK_EQ_INT(IOTC_ERR_PARAM_INVALID, IotcVsLoad(NULL, SCHEMA_V1, buf, &len)); | ||
| 108 | + CHECK_EQ_INT(IOTC_ERR_PARAM_INVALID, IotcVsLoad("BAD-CHAR", SCHEMA_V1, buf, &len)); | ||
| 109 | + CHECK_EQ_INT(IOTC_ERR_PARAM_INVALID, IotcVsLoad("", SCHEMA_V1, buf, &len)); | ||
| 110 | + CHECK_EQ_INT(IOTC_ERR_PARAM_INVALID, | ||
| 111 | + IotcVsLoad("domain_name_longer_than_24chars", SCHEMA_V1, buf, &len)); | ||
| 112 | + char unterminated[IOTC_VS_MAX_DOMAIN_LEN + 1]; | ||
| 113 | + (void)memset(unterminated, 'a', sizeof(unterminated)); | ||
| 114 | + CHECK_EQ_INT(IOTC_ERR_PARAM_INVALID, IotcVsLoad(unterminated, SCHEMA_V1, buf, &len)); | ||
| 115 | + | ||
| 116 | + uint8_t payload[IOTC_VS_MAX_PAYLOAD_LEN + 1] = {0}; | ||
| 117 | + CHECK_EQ_INT(IOTC_ERR_PARAM_INVALID, | ||
| 118 | + IotcVsBeginWrite(DEMO_DOMAIN, SCHEMA_V1, payload, 0)); | ||
| 119 | + CHECK_EQ_INT(IOTC_ERR_PARAM_INVALID, | ||
| 120 | + IotcVsBeginWrite(DEMO_DOMAIN, SCHEMA_V1, payload, IOTC_VS_MAX_PAYLOAD_LEN + 1)); | ||
| 121 | + CHECK_EQ_INT(IOTC_ERR_PARAM_INVALID, IotcVsBeginWrite(DEMO_DOMAIN, SCHEMA_V1, NULL, 4)); | ||
| 122 | + | ||
| 123 | + CHECK_EQ_INT(IOTC_OK, | ||
| 124 | + IotcVsBeginWrite(DEMO_DOMAIN, SCHEMA_V1, payload, IOTC_VS_MAX_PAYLOAD_LEN)); | ||
| 125 | + CHECK_EQ_INT(IOTC_OK, IotcVsCommit(DEMO_DOMAIN)); | ||
| 126 | + uint8_t bigBuf[IOTC_VS_MAX_PAYLOAD_LEN] = {0}; | ||
| 127 | + len = sizeof(bigBuf); | ||
| 128 | + CHECK_EQ_INT(IOTC_OK, IotcVsLoad(DEMO_DOMAIN, SCHEMA_V1, bigBuf, &len)); | ||
| 129 | + CHECK_EQ_INT(IOTC_VS_MAX_PAYLOAD_LEN, len); | ||
| 130 | +} | ||
| 131 | + | ||
| 132 | +static void TestAbortFlow(void) | ||
| 133 | +{ | ||
| 134 | + KvFakeReset(); | ||
| 135 | + SaveCommitted(DEMO_DOMAIN, OLD_VALUE); | ||
| 136 | + | ||
| 137 | + /* BeginWrite 后 Abort:擦新槽,旧值保留 */ | ||
| 138 | + CHECK_EQ_INT(IOTC_OK, IotcVsBeginWrite(DEMO_DOMAIN, SCHEMA_V1, (const uint8_t *)NEW_VALUE, | ||
| 139 | + (uint32_t)strlen(NEW_VALUE) + 1)); | ||
| 140 | + CHECK_EQ_INT(IOTC_OK, IotcVsAbort(DEMO_DOMAIN)); | ||
| 141 | + LoadExpect(DEMO_DOMAIN, OLD_VALUE); | ||
| 142 | + CHECK_EQ_INT(1, KvFakePresentCount()); | ||
| 143 | + uint32_t generation = 0; | ||
| 144 | + CHECK_EQ_INT(IOTC_OK, IotcVsGetGeneration(DEMO_DOMAIN, &generation)); | ||
| 145 | + CHECK_EQ_INT(1, generation); | ||
| 146 | + | ||
| 147 | + /* 无待回滚记录时 Abort 幂等 */ | ||
| 148 | + CHECK_EQ_INT(IOTC_OK, IotcVsAbort(DEMO_DOMAIN)); | ||
| 149 | + LoadExpect(DEMO_DOMAIN, OLD_VALUE); | ||
| 150 | +} | ||
| 151 | + | ||
| 152 | +static void TestCommitFlow(void) | ||
| 153 | +{ | ||
| 154 | + KvFakeReset(); | ||
| 155 | + CHECK_EQ_INT(IOTC_OK, IotcVsBeginWrite(DEMO_DOMAIN, SCHEMA_V1, (const uint8_t *)OLD_VALUE, | ||
| 156 | + (uint32_t)strlen(OLD_VALUE) + 1)); | ||
| 157 | + /* 首次写在 Commit 前也不可见。 */ | ||
| 158 | + uint8_t emptyBuf[64] = {0}; | ||
| 159 | + uint32_t emptyLen = sizeof(emptyBuf); | ||
| 160 | + CHECK_EQ_INT(IOTC_CORE_DEVICE_ERR_STORE_NOT_FOUND, | ||
| 161 | + IotcVsLoad(DEMO_DOMAIN, SCHEMA_V1, emptyBuf, &emptyLen)); | ||
| 162 | + CHECK_EQ_INT(IOTC_OK, IotcVsCommit(DEMO_DOMAIN)); | ||
| 163 | + LoadExpect(DEMO_DOMAIN, OLD_VALUE); | ||
| 164 | + | ||
| 165 | + KvFakeReset(); | ||
| 166 | + SaveCommitted(DEMO_DOMAIN, OLD_VALUE); | ||
| 167 | + CHECK_EQ_INT(IOTC_OK, IotcVsBeginWrite(DEMO_DOMAIN, SCHEMA_V1, (const uint8_t *)NEW_VALUE, | ||
| 168 | + (uint32_t)strlen(NEW_VALUE) + 1)); | ||
| 169 | + /* 提交前:pending 新值不可见,旧值仍是 active。 */ | ||
| 170 | + LoadExpect(DEMO_DOMAIN, OLD_VALUE); | ||
| 171 | + CHECK_EQ_INT(2, KvFakePresentCount()); | ||
| 172 | + CHECK_EQ_INT(IOTC_OK, IotcVsCommit(DEMO_DOMAIN)); | ||
| 173 | + LoadExpect(DEMO_DOMAIN, NEW_VALUE); | ||
| 174 | + CHECK_EQ_INT(1, KvFakePresentCount()); | ||
| 175 | + /* 重复提交幂等 */ | ||
| 176 | + CHECK_EQ_INT(IOTC_OK, IotcVsCommit(DEMO_DOMAIN)); | ||
| 177 | + LoadExpect(DEMO_DOMAIN, NEW_VALUE); | ||
| 178 | +} | ||
| 179 | + | ||
| 180 | +static void TestCorruptSlotSelfHeal(void) | ||
| 181 | +{ | ||
| 182 | + KvFakeReset(); | ||
| 183 | + SaveCommitted(DEMO_DOMAIN, OLD_VALUE); | ||
| 184 | + /* 直接破坏唯一有效槽:加载失败;随后写入自愈 */ | ||
| 185 | + CHECK_EQ_INT(0, KvFakeCorrupt("vs_" DEMO_DOMAIN "_a")); | ||
| 186 | + uint8_t buf[32] = {0}; | ||
| 187 | + uint32_t len = sizeof(buf); | ||
| 188 | + CHECK_EQ_INT(IOTC_CORE_DEVICE_ERR_STORE_NOT_FOUND, IotcVsLoad(DEMO_DOMAIN, SCHEMA_V1, buf, &len)); | ||
| 189 | + | ||
| 190 | + CHECK_EQ_INT(IOTC_OK, IotcVsBeginWrite(DEMO_DOMAIN, SCHEMA_V1, (const uint8_t *)NEW_VALUE, | ||
| 191 | + (uint32_t)strlen(NEW_VALUE) + 1)); | ||
| 192 | + CHECK_EQ_INT(IOTC_OK, IotcVsCommit(DEMO_DOMAIN)); | ||
| 193 | + LoadExpect(DEMO_DOMAIN, NEW_VALUE); | ||
| 194 | +} | ||
| 195 | + | ||
| 196 | +static void TestErase(void) | ||
| 197 | +{ | ||
| 198 | + KvFakeReset(); | ||
| 199 | + SaveCommitted(DEMO_DOMAIN, OLD_VALUE); | ||
| 200 | + CHECK_EQ_INT(IOTC_OK, IotcVsErase(DEMO_DOMAIN)); | ||
| 201 | + CHECK_EQ_INT(0, KvFakePresentCount()); | ||
| 202 | + /* 空域擦除幂等 */ | ||
| 203 | + CHECK_EQ_INT(IOTC_OK, IotcVsErase(DEMO_DOMAIN)); | ||
| 204 | +} | ||
| 205 | + | ||
| 206 | +/* 断电不变式:加载结果必须是 old 或 new 之一,内容逐字节一致。 */ | ||
| 207 | +static int32_t LoadEither(const char *domain, const char *oldValue, const char *newValue) | ||
| 208 | +{ | ||
| 209 | + uint8_t buf[64] = {0}; | ||
| 210 | + uint32_t len = sizeof(buf); | ||
| 211 | + int32_t ret = IotcVsLoad(domain, SCHEMA_V1, buf, &len); | ||
| 212 | + CHECK_EQ_INT(IOTC_OK, ret); | ||
| 213 | + if (ret != IOTC_OK) { | ||
| 214 | + return -1; | ||
| 215 | + } | ||
| 216 | + if (len == (uint32_t)strlen(oldValue) + 1 && memcmp(buf, oldValue, len) == 0) { | ||
| 217 | + return 0; | ||
| 218 | + } | ||
| 219 | + if (len == (uint32_t)strlen(newValue) + 1 && memcmp(buf, newValue, len) == 0) { | ||
| 220 | + return 1; | ||
| 221 | + } | ||
| 222 | + CHECK(!"loaded record is neither intact old nor intact new"); | ||
| 223 | + return -1; | ||
| 224 | +} | ||
| 225 | + | ||
| 226 | +static void PowerCutCase(int armOp, int fault, const char *caseName) | ||
| 227 | +{ | ||
| 228 | + KvFakeReset(); | ||
| 229 | + SaveCommitted(DEMO_DOMAIN, OLD_VALUE); | ||
| 230 | + KvFakeReboot(); /* 只复位操作计数,保留已提交的旧值 */ | ||
| 231 | + KvFakeArm(armOp, fault); | ||
| 232 | + | ||
| 233 | + (void)IotcVsBeginWrite(DEMO_DOMAIN, SCHEMA_V1, (const uint8_t *)NEW_VALUE, | ||
| 234 | + (uint32_t)strlen(NEW_VALUE) + 1); | ||
| 235 | + (void)IotcVsCommit(DEMO_DOMAIN); | ||
| 236 | + /* 断电恢复后:只能是旧有效或新有效 */ | ||
| 237 | + KvFakeReboot(); | ||
| 238 | + int32_t which = LoadEither(DEMO_DOMAIN, OLD_VALUE, NEW_VALUE); | ||
| 239 | + printf(" powercut %s: survived with %s\n", caseName, (which == 0) ? "old" : "new"); | ||
| 240 | + /* 恢复后继续写入必须可用且 generation 继续递增 */ | ||
| 241 | + SaveCommitted(DEMO_DOMAIN, NEW_VALUE); | ||
| 242 | + LoadExpect(DEMO_DOMAIN, NEW_VALUE); | ||
| 243 | + uint32_t generation = 0; | ||
| 244 | + CHECK_EQ_INT(IOTC_OK, IotcVsGetGeneration(DEMO_DOMAIN, &generation)); | ||
| 245 | + CHECK(generation >= 2); | ||
| 246 | +} | ||
| 247 | + | ||
| 248 | +static void TestPowerCutMatrix(void) | ||
| 249 | +{ | ||
| 250 | + /* 写前断电:新记录未落盘,旧值有效 */ | ||
| 251 | + PowerCutCase(0, KV_FAULT_FAIL, "fail_before_write"); | ||
| 252 | + /* 写中撕裂:半截记录 CRC 不过,旧值有效 */ | ||
| 253 | + PowerCutCase(0, KV_FAULT_TORN_WRITE, "torn_write"); | ||
| 254 | + /* 提交标记写入时断电/撕裂:旧 committed 值仍有效。 */ | ||
| 255 | + PowerCutCase(1, KV_FAULT_FAIL, "fail_at_commit_marker"); | ||
| 256 | + PowerCutCase(1, KV_FAULT_TORN_WRITE, "torn_commit_marker"); | ||
| 257 | + /* 标记成功、擦旧槽时断电:两条 committed 并存,加载 generation 更高的新值。 */ | ||
| 258 | + PowerCutCase(2, KV_FAULT_FAIL, "fail_at_commit_erase"); | ||
| 259 | + PowerCutCase(2, KV_FAULT_TORN_ERASE, "torn_erase_at_commit"); | ||
| 260 | + | ||
| 261 | + /* 断电点“回读后/提交前”:pending 新值不生效;Abort 后仍是旧值。 */ | ||
| 262 | + KvFakeReset(); | ||
| 263 | + SaveCommitted(DEMO_DOMAIN, OLD_VALUE); | ||
| 264 | + CHECK_EQ_INT(IOTC_OK, IotcVsBeginWrite(DEMO_DOMAIN, SCHEMA_V1, (const uint8_t *)NEW_VALUE, | ||
| 265 | + (uint32_t)strlen(NEW_VALUE) + 1)); | ||
| 266 | + KvFakeReboot(); | ||
| 267 | + CHECK_EQ_INT(0, LoadEither(DEMO_DOMAIN, OLD_VALUE, NEW_VALUE)); | ||
| 268 | + CHECK_EQ_INT(IOTC_OK, IotcVsAbort(DEMO_DOMAIN)); | ||
| 269 | + LoadExpect(DEMO_DOMAIN, OLD_VALUE); | ||
| 270 | +} | ||
| 271 | + | ||
| 272 | +static void TestPowerCutLoop(void) | ||
| 273 | +{ | ||
| 274 | + /* 每个断电/撕裂点 x 每个故障类型循环 20 轮(host 侧稳定性放大) */ | ||
| 275 | + for (int round = 0; round < 20; ++round) { | ||
| 276 | + PowerCutCase(round % 3, KV_FAULT_FAIL, "loop_fail"); | ||
| 277 | + PowerCutCase(round % 3, KV_FAULT_TORN_WRITE, "loop_torn_write"); | ||
| 278 | + PowerCutCase(round % 3, KV_FAULT_TORN_ERASE, "loop_torn_erase"); | ||
| 279 | + } | ||
| 280 | +} | ||
| 281 | + | ||
| 282 | +int main(void) | ||
| 283 | +{ | ||
| 284 | + setbuf(stdout, NULL); | ||
| 285 | + TestEmptyLoad(); | ||
| 286 | + TestRoundtripAndGeneration(); | ||
| 287 | + TestSchemaAndBuffer(); | ||
| 288 | + TestParamValidation(); | ||
| 289 | + TestAbortFlow(); | ||
| 290 | + TestCommitFlow(); | ||
| 291 | + TestCorruptSlotSelfHeal(); | ||
| 292 | + TestErase(); | ||
| 293 | + TestPowerCutMatrix(); | ||
| 294 | + TestPowerCutLoop(); | ||
| 295 | + | ||
| 296 | + printf("test_store: %d checks, %d failures\n", g_testChecks, g_testFailures); | ||
| 297 | + return (g_testFailures == 0) ? 0 : 1; | ||
| 298 | +} | ||
| @@ -0,0 +1,47 @@ | |||
| 1 | +#!/bin/bash | ||
| 2 | +# Build and run the WiFi Only HubSetup transaction test outside the source tree. | ||
| 3 | +set -uo pipefail | ||
| 4 | + | ||
| 5 | +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) | ||
| 6 | +IOTC_ROOT=$(cd "$SCRIPT_DIR/../../.." && pwd) | ||
| 7 | +if [ -n "${IOTC_TEST_OHOS_ROOT:-}" ]; then | ||
| 8 | + OHOS_ROOT=$(cd "$IOTC_TEST_OHOS_ROOT" && pwd) | ||
| 9 | +else | ||
| 10 | + OHOS_ROOT=$(cd "$IOTC_ROOT/../../.." && pwd) | ||
| 11 | +fi | ||
| 12 | +OUT_DIR=$(mktemp -d /tmp/iotc_hub_setup_host_test.XXXXXX) | ||
| 13 | +trap 'rm -rf "$OUT_DIR"' EXIT | ||
| 14 | + | ||
| 15 | +CFLAGS=( | ||
| 16 | + -std=c99 -Wall -Wextra -Werror -g -O0 | ||
| 17 | + -DIOTC_CONF_WIFI_ONLY_SUPPORT=1 | ||
| 18 | + -DIOTC_CONF_WIFI_SUPPORT=1 | ||
| 19 | + -DIOTC_ONECONNECT_VERSION_CODE=3 | ||
| 20 | + -I"$SCRIPT_DIR" | ||
| 21 | + -I"$SCRIPT_DIR/stubs" | ||
| 22 | + -I"$IOTC_ROOT/test/unit/device_config_host" | ||
| 23 | + -I"$IOTC_ROOT/test/unit/device_config_host/stubs" | ||
| 24 | + -I"$IOTC_ROOT/adapter/include" | ||
| 25 | + -I"$IOTC_ROOT/interfaces/kits/common" | ||
| 26 | + -I"$IOTC_ROOT/core/infrastructure/define" | ||
| 27 | + -I"$IOTC_ROOT/core/infrastructure/service/include" | ||
| 28 | + -I"$IOTC_ROOT/core/infrastructure/utils/include" | ||
| 29 | + -I"$IOTC_ROOT/core/device/config/include" | ||
| 30 | + -I"$IOTC_ROOT/core/device/service" | ||
| 31 | + -I"$OHOS_ROOT/third_party/cJSON" | ||
| 32 | +) | ||
| 33 | + | ||
| 34 | +SOURCES=( | ||
| 35 | + "$SCRIPT_DIR/test_hub_setup.c" | ||
| 36 | + "$SCRIPT_DIR/stubs/hub_setup_deps_stub.c" | ||
| 37 | + "$IOTC_ROOT/core/device/service/svc_hub_setup.c" | ||
| 38 | + "$IOTC_ROOT/core/device/config/versioned_store.c" | ||
| 39 | + "$IOTC_ROOT/core/device/config/device_state_store.c" | ||
| 40 | + "$IOTC_ROOT/adapter/json/cjson/iotc_json.c" | ||
| 41 | + "$IOTC_ROOT/test/unit/device_config_host/stubs/iotc_kv_fake.c" | ||
| 42 | + "$IOTC_ROOT/test/unit/device_config_host/stubs/event_bus_stub.c" | ||
| 43 | + "$OHOS_ROOT/third_party/cJSON/cJSON.c" | ||
| 44 | +) | ||
| 45 | + | ||
| 46 | +gcc "${CFLAGS[@]}" "${SOURCES[@]}" -lm -o "$OUT_DIR/test_hub_setup" || exit 1 | ||
| 47 | +"$OUT_DIR/test_hub_setup" | ||
| @@ -0,0 +1,122 @@ | |||
| 1 | +/* Minimal real-behaviour dependencies for the HubSetup host test. */ | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | +static DevHubInfo g_hubInfo; | ||
| 15 | +static char g_domainId[DOMAIN_ID_MAX_LEN + 1]; | ||
| 16 | +static char g_devId[DEVICE_ID_MAX_STR_LEN + 1]; | ||
| 17 | +static int32_t g_saveCount; | ||
| 18 | + | ||
| 19 | +void HubSetupDepsReset(void) | ||
| 20 | +{ | ||
| 21 | + (void)memset(&g_hubInfo, 0, sizeof(g_hubInfo)); | ||
| 22 | + (void)memset(g_domainId, 0, sizeof(g_domainId)); | ||
| 23 | + (void)memset(g_devId, 0, sizeof(g_devId)); | ||
| 24 | + g_saveCount = 0; | ||
| 25 | +} | ||
| 26 | + | ||
| 27 | +const DevHubInfo *HubSetupDepsGetHubInfo(void) | ||
| 28 | +{ | ||
| 29 | + return &g_hubInfo; | ||
| 30 | +} | ||
| 31 | + | ||
| 32 | +const char *HubSetupDepsGetDomainId(void) | ||
| 33 | +{ | ||
| 34 | + return g_domainId; | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +const char *HubSetupDepsGetDevId(void) | ||
| 38 | +{ | ||
| 39 | + return g_devId; | ||
| 40 | +} | ||
| 41 | + | ||
| 42 | +int32_t HubSetupDepsGetSaveCount(void) | ||
| 43 | +{ | ||
| 44 | + return g_saveCount; | ||
| 45 | +} | ||
| 46 | + | ||
| 47 | +int32_t ConfigSaveHubInfo(const DevHubInfo *info) | ||
| 48 | +{ | ||
| 49 | + if (info == NULL || memcpy_s(&g_hubInfo, sizeof(g_hubInfo), info, sizeof(*info)) != EOK) { | ||
| 50 | + return IOTC_ERR_PARAM_INVALID; | ||
| 51 | + } | ||
| 52 | + ++g_saveCount; | ||
| 53 | + return IOTC_OK; | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | +int32_t ConfigSaveDomainId(const char *domainId, uint32_t domainIdLen) | ||
| 57 | +{ | ||
| 58 | + if (domainId == NULL || domainIdLen >= sizeof(g_domainId) || | ||
| 59 | + memcpy_s(g_domainId, sizeof(g_domainId), domainId, domainIdLen) != EOK) { | ||
| 60 | + return IOTC_ERR_PARAM_INVALID; | ||
| 61 | + } | ||
| 62 | + g_domainId[domainIdLen] = '\0'; | ||
| 63 | + return IOTC_OK; | ||
| 64 | +} | ||
| 65 | + | ||
| 66 | +int32_t ConfigSaveDevId(const char *devId, uint32_t devIdLen) | ||
| 67 | +{ | ||
| 68 | + if (devId == NULL || devIdLen >= sizeof(g_devId) || | ||
| 69 | + memcpy_s(g_devId, sizeof(g_devId), devId, devIdLen) != EOK) { | ||
| 70 | + return IOTC_ERR_PARAM_INVALID; | ||
| 71 | + } | ||
| 72 | + g_devId[devIdLen] = '\0'; | ||
| 73 | + return IOTC_OK; | ||
| 74 | +} | ||
| 75 | + | ||
| 76 | +int32_t UtilsJsonGetString(const IotcJson *json, const char *key, char *buf, uint32_t len) | ||
| 77 | +{ | ||
| 78 | + if (json == NULL || key == NULL || buf == NULL || len == 0) { | ||
| 79 | + return IOTC_ERR_PARAM_INVALID; | ||
| 80 | + } | ||
| 81 | + const char *value = IotcJsonGetStr(IotcJsonGetObj(json, key)); | ||
| 82 | + if (value == NULL) { | ||
| 83 | + return IOTC_ADAPTER_JSON_ERR_GET_STRING; | ||
| 84 | + } | ||
| 85 | + return (strcpy_s(buf, len, value) == EOK) ? IOTC_OK : IOTC_ERR_SECUREC_STRCPY; | ||
| 86 | +} | ||
| 87 | + | ||
| 88 | +int32_t UtilsJsonGetNum(const IotcJson *json, const char *name, int32_t *num) | ||
| 89 | +{ | ||
| 90 | + if (json == NULL || name == NULL || num == NULL) { | ||
| 91 | + return IOTC_ERR_PARAM_INVALID; | ||
| 92 | + } | ||
| 93 | + int64_t value = 0; | ||
| 94 | + int32_t ret = IotcJsonGetNum(IotcJsonGetObj(json, name), &value); | ||
| 95 | + if (ret != IOTC_OK) { | ||
| 96 | + return ret; | ||
| 97 | + } | ||
| 98 | + if (value < INT32_MIN || value > INT32_MAX) { | ||
| 99 | + return IOTC_CORE_COMM_UTILS_ERR_JSON_INVALID_ERRCODE; | ||
| 100 | + } | ||
| 101 | + *num = (int32_t)value; | ||
| 102 | + return IOTC_OK; | ||
| 103 | +} | ||
| 104 | + | ||
| 105 | +int32_t IotcMdCalc(IotcMdType type, const uint8_t *inData, uint32_t inLen, uint8_t *md, uint32_t mdLen) | ||
| 106 | +{ | ||
| 107 | + if (type != IOTC_MD_SHA256 || inData == NULL || inLen == 0 || md == NULL || | ||
| 108 | + mdLen != IOTC_MD_SHA256_BYTE_LEN) { | ||
| 109 | + return IOTC_ERR_PARAM_INVALID; | ||
| 110 | + } | ||
| 111 | + uint32_t hash = 2166136261U; | ||
| 112 | + for (uint32_t i = 0; i < inLen; ++i) { | ||
| 113 | + hash = (hash ^ inData[i]) * 16777619U; | ||
| 114 | + } | ||
| 115 | + for (uint32_t i = 0; i < mdLen; ++i) { | ||
| 116 | + hash ^= hash << 13; | ||
| 117 | + hash ^= hash >> 17; | ||
| 118 | + hash ^= hash << 5; | ||
| 119 | + md[i] = (uint8_t)(hash >> ((i % 4U) * 8U)); | ||
| 120 | + } | ||
| 121 | + return IOTC_OK; | ||
| 122 | +} | ||
| @@ -0,0 +1,16 @@ | |||
| 1 | +/* Host-test controls for HubSetup dependencies. */ | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | +void HubSetupDepsReset(void); | ||
| 11 | +const DevHubInfo *HubSetupDepsGetHubInfo(void); | ||
| 12 | +const char *HubSetupDepsGetDomainId(void); | ||
| 13 | +const char *HubSetupDepsGetDevId(void); | ||
| 14 | +int32_t HubSetupDepsGetSaveCount(void); | ||
| 15 | + | ||
| 16 | + | ||
| @@ -0,0 +1,167 @@ | |||
| 1 | +/* HubSetup strict parsing, idempotency, persistence and recovery tests. */ | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +int g_testChecks; | ||
| 16 | +int g_testFailures; | ||
| 17 | + | ||
| 18 | +static const char *VALID_REQUEST = | ||
| 19 | + "{\"domainId\":\"mdid-0001\",\"devId\":\"temp-dev-0001\",\"code\":\"reg-code-0001\"," | ||
| 20 | + "\"ssid\":\"lab-wifi\",\"password\":\"wifi-secret-123\",\"channel\":6," | ||
| 21 | + "\"bssid\":\"AA:bb:02:03:04:05\"}"; | ||
| 22 | + | ||
| 23 | +static void ResetFixture(void) | ||
| 24 | +{ | ||
| 25 | + (void)DeviceServiceAbortHubBindInfo(); | ||
| 26 | + KvFakeReset(); | ||
| 27 | + HubSetupDepsReset(); | ||
| 28 | +} | ||
| 29 | + | ||
| 30 | +static int32_t PrepareText(const char *text, bool *alreadyCommitted) | ||
| 31 | +{ | ||
| 32 | + IotcJson *json = IotcJsonParse(text); | ||
| 33 | + CHECK(json != NULL); | ||
| 34 | + if (json == NULL) { | ||
| 35 | + return IOTC_ADAPTER_JSON_ERR_PARSE; | ||
| 36 | + } | ||
| 37 | + int32_t ret = DeviceServicePrepareHubBindInfo(json, alreadyCommitted); | ||
| 38 | + IotcJsonDelete(json); | ||
| 39 | + return ret; | ||
| 40 | +} | ||
| 41 | + | ||
| 42 | +static void CheckRejected(const char *text) | ||
| 43 | +{ | ||
| 44 | + bool alreadyCommitted = false; | ||
| 45 | + CHECK(PrepareText(text, &alreadyCommitted) != IOTC_OK); | ||
| 46 | + CHECK(!alreadyCommitted); | ||
| 47 | + CHECK_EQ_INT(IOTC_OK, DeviceServiceAbortHubBindInfo()); | ||
| 48 | +} | ||
| 49 | + | ||
| 50 | +static void TestStrictFields(void) | ||
| 51 | +{ | ||
| 52 | + ResetFixture(); | ||
| 53 | + static const char *invalid[] = { | ||
| 54 | + "{\"devId\":\"d\",\"code\":\"c\",\"ssid\":\"s\",\"password\":\"p\"}", | ||
| 55 | + "{\"domainId\":\"m\",\"code\":\"c\",\"ssid\":\"s\",\"password\":\"p\"}", | ||
| 56 | + "{\"domainId\":\"m\",\"devId\":\"d\",\"ssid\":\"s\",\"password\":\"p\"}", | ||
| 57 | + "{\"domainId\":\"m\",\"devId\":\"d\",\"code\":\"c\",\"password\":\"p\"}", | ||
| 58 | + "{\"domainId\":\"m\",\"devId\":\"d\",\"code\":\"c\",\"ssid\":\"s\"}", | ||
| 59 | + "{\"domainId\":\"\",\"devId\":\"d\",\"code\":\"c\",\"ssid\":\"s\",\"password\":\"p\"}", | ||
| 60 | + "{\"domainId\":\"m\",\"devId\":\"d\",\"code\":\"c\",\"ssid\":\"s\",\"password\":\"p\",\"channel\":0}", | ||
| 61 | + "{\"domainId\":\"m\",\"devId\":\"d\",\"code\":\"c\",\"ssid\":\"s\",\"password\":\"p\",\"channel\":197}", | ||
| 62 | + "{\"domainId\":\"m\",\"devId\":\"d\",\"code\":\"c\",\"ssid\":\"s\",\"password\":\"p\",\"channel\":\"6\"}", | ||
| 63 | + "{\"domainId\":\"m\",\"devId\":\"d\",\"code\":\"c\",\"ssid\":\"s\",\"password\":\"p\",\"channel\":-2147483649}", | ||
| 64 | + "{\"domainId\":\"m\",\"devId\":\"d\",\"code\":\"c\",\"ssid\":\"s\",\"password\":\"p\",\"bssid\":\"AA:BB:CC:DD:EE\"}", | ||
| 65 | + "{\"domainId\":\"m\",\"devId\":\"d\",\"code\":\"c\",\"ssid\":\"s\",\"password\":\"p\",\"bssid\":\"GG:BB:CC:DD:EE:FF\"}", | ||
| 66 | + }; | ||
| 67 | + for (uint32_t i = 0; i < sizeof(invalid) / sizeof(invalid[0]); ++i) { | ||
| 68 | + CheckRejected(invalid[i]); | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + char longDomain[42] = {0}; | ||
| 72 | + (void)memset(longDomain, 'd', sizeof(longDomain) - 1); | ||
| 73 | + char request[256] = {0}; | ||
| 74 | + (void)snprintf(request, sizeof(request), | ||
| 75 | + "{\"domainId\":\"%s\",\"devId\":\"d\",\"code\":\"c\",\"ssid\":\"s\",\"password\":\"p\"}", | ||
| 76 | + longDomain); | ||
| 77 | + CheckRejected(request); | ||
| 78 | + | ||
| 79 | + bool alreadyCommitted = true; | ||
| 80 | + CHECK_EQ_INT(IOTC_OK, PrepareText( | ||
| 81 | + "{\"domainId\":\"m\",\"devId\":\"d\",\"code\":\"c\",\"ssid\":\"s\",\"password\":\"p\"}", | ||
| 82 | + &alreadyCommitted)); | ||
| 83 | + CHECK(!alreadyCommitted); | ||
| 84 | + CHECK_EQ_INT(IOTC_OK, DeviceServiceAbortHubBindInfo()); | ||
| 85 | +} | ||
| 86 | + | ||
| 87 | +static void TestCommitAndNoPasswordInStateStore(void) | ||
| 88 | +{ | ||
| 89 | + ResetFixture(); | ||
| 90 | + bool alreadyCommitted = true; | ||
| 91 | + CHECK_EQ_INT(IOTC_OK, PrepareText(VALID_REQUEST, &alreadyCommitted)); | ||
| 92 | + CHECK(!alreadyCommitted); | ||
| 93 | + | ||
| 94 | + IotcDssProvisionPending pending = {0}; | ||
| 95 | + CHECK_EQ_INT(IOTC_CORE_DEVICE_ERR_STORE_NOT_FOUND, IotcDssLoadProvisionPending(&pending)); | ||
| 96 | + CHECK_EQ_INT(IOTC_OK, DeviceServiceCommitHubBindInfo()); | ||
| 97 | + CHECK_EQ_INT(IOTC_OK, IotcDssLoadProvisionPending(&pending)); | ||
| 98 | + CHECK_EQ_INT(9, pending.mdidLen); | ||
| 99 | + CHECK(memcmp(pending.mdid, "mdid-0001", pending.mdidLen) == 0); | ||
| 100 | + CHECK(strcmp(pending.tempDevId, "temp-dev-0001") == 0); | ||
| 101 | + CHECK(strcmp(pending.registerCode, "reg-code-0001") == 0); | ||
| 102 | + CHECK_EQ_INT(0, KvFakeContainsBytes((const uint8_t *)"wifi-secret-123", 15)); | ||
| 103 | + | ||
| 104 | + uint32_t generation = 0; | ||
| 105 | + CHECK_EQ_INT(IOTC_OK, IotcVsGetGeneration(IOTC_DSS_DOMAIN_PROVISION_PENDING, &generation)); | ||
| 106 | + CHECK_EQ_INT(1, generation); | ||
| 107 | + CHECK(strcmp(HubSetupDepsGetDomainId(), "mdid-0001") == 0); | ||
| 108 | + CHECK(strcmp(HubSetupDepsGetDevId(), "temp-dev-0001") == 0); | ||
| 109 | + CHECK(strcmp(HubSetupDepsGetHubInfo()->ssid, "lab-wifi") == 0); | ||
| 110 | + CHECK(strcmp(HubSetupDepsGetHubInfo()->password, "wifi-secret-123") == 0); | ||
| 111 | +} | ||
| 112 | + | ||
| 113 | +static void TestIdempotencyAndConflict(void) | ||
| 114 | +{ | ||
| 115 | + ResetFixture(); | ||
| 116 | + bool alreadyCommitted = false; | ||
| 117 | + CHECK_EQ_INT(IOTC_OK, PrepareText(VALID_REQUEST, &alreadyCommitted)); | ||
| 118 | + CHECK_EQ_INT(IOTC_OK, DeviceServiceCommitHubBindInfo()); | ||
| 119 | + | ||
| 120 | + int32_t saveCount = HubSetupDepsGetSaveCount(); | ||
| 121 | + CHECK_EQ_INT(IOTC_OK, PrepareText(VALID_REQUEST, &alreadyCommitted)); | ||
| 122 | + CHECK(alreadyCommitted); | ||
| 123 | + CHECK_EQ_INT(saveCount + 1, HubSetupDepsGetSaveCount()); | ||
| 124 | + uint32_t generation = 0; | ||
| 125 | + CHECK_EQ_INT(IOTC_OK, IotcVsGetGeneration(IOTC_DSS_DOMAIN_PROVISION_PENDING, &generation)); | ||
| 126 | + CHECK_EQ_INT(1, generation); | ||
| 127 | + | ||
| 128 | + const char *conflict = | ||
| 129 | + "{\"domainId\":\"mdid-0001\",\"devId\":\"temp-dev-0001\",\"code\":\"reg-code-0001\"," | ||
| 130 | + "\"ssid\":\"lab-wifi\",\"password\":\"different-secret\",\"channel\":6," | ||
| 131 | + "\"bssid\":\"AA:bb:02:03:04:05\"}"; | ||
| 132 | + alreadyCommitted = false; | ||
| 133 | + CHECK_EQ_INT(IOTC_CORE_DEVICE_ERR_STORE_STATE, PrepareText(conflict, &alreadyCommitted)); | ||
| 134 | + CHECK(!alreadyCommitted); | ||
| 135 | + CHECK_EQ_INT(IOTC_OK, IotcVsGetGeneration(IOTC_DSS_DOMAIN_PROVISION_PENDING, &generation)); | ||
| 136 | + CHECK_EQ_INT(1, generation); | ||
| 137 | +} | ||
| 138 | + | ||
| 139 | +static void TestAbortAndRecovery(void) | ||
| 140 | +{ | ||
| 141 | + ResetFixture(); | ||
| 142 | + bool alreadyCommitted = false; | ||
| 143 | + CHECK_EQ_INT(IOTC_OK, PrepareText(VALID_REQUEST, &alreadyCommitted)); | ||
| 144 | + CHECK_EQ_INT(IOTC_OK, DeviceServiceAbortHubBindInfo()); | ||
| 145 | + IotcDssProvisionPending pending = {0}; | ||
| 146 | + CHECK_EQ_INT(IOTC_CORE_DEVICE_ERR_STORE_NOT_FOUND, IotcDssLoadProvisionPending(&pending)); | ||
| 147 | + | ||
| 148 | + CHECK_EQ_INT(IOTC_OK, PrepareText(VALID_REQUEST, &alreadyCommitted)); | ||
| 149 | + CHECK_EQ_INT(IOTC_OK, DeviceServiceCommitHubBindInfo()); | ||
| 150 | + HubSetupDepsReset(); | ||
| 151 | + CHECK_EQ_INT(IOTC_OK, DeviceServiceRecoverHubBindInfo()); | ||
| 152 | + CHECK(strcmp(HubSetupDepsGetDomainId(), "mdid-0001") == 0); | ||
| 153 | + CHECK(strcmp(HubSetupDepsGetDevId(), "temp-dev-0001") == 0); | ||
| 154 | + CHECK(strcmp(HubSetupDepsGetHubInfo()->code, "reg-code-0001") == 0); | ||
| 155 | + CHECK(HubSetupDepsGetHubInfo()->ssid[0] == '\0'); | ||
| 156 | + CHECK(HubSetupDepsGetHubInfo()->password[0] == '\0'); | ||
| 157 | +} | ||
| 158 | + | ||
| 159 | +int main(void) | ||
| 160 | +{ | ||
| 161 | + TestStrictFields(); | ||
| 162 | + TestCommitAndNoPasswordInStateStore(); | ||
| 163 | + TestIdempotencyAndConflict(); | ||
| 164 | + TestAbortAndRecovery(); | ||
| 165 | + printf("HubSetup host checks=%d failures=%d\n", g_testChecks, g_testFailures); | ||
| 166 | + return (g_testFailures == 0) ? 0 : 1; | ||
| 167 | +} | ||
| @@ -23,6 +23,7 @@ | |||
| 23 | 23 | ||
| 24 | 24 | ||
| 25 | 25 | ||
| 26 | + | ||
| 26 | 27 | ||
| 27 | static int g_failures; | 28 | static int g_failures; |
| 28 | 29 | ||
| @@ -87,19 +88,161 @@ uint32_t SecurityRandomUint32(void) | |||
| 87 | return value; | 88 | return value; |
| 88 | } | 89 | } |
| 89 | 90 | ||
| 90 | -/* Cross-backend reference vectors: produced by the deterministic LCG run on | 91 | +/* Cross-backend reference vectors for both supported base encodings. The |
| 91 | - * the mbedTLS backend and independently reproduced byte-for-byte by the | 92 | + * little-endian variant follows the RFC 7748 X25519 wire encoding; the |
| 92 | - * openHiTLS backend. Each ciphertext covers the negotiated key and the GCM | 93 | + * default big-endian variant preserves the deployed configurator contract. |
| 93 | - * path end-to-end (version byte + IV + ciphertext + tag). */ | 94 | + * Both variants otherwise use the 2026-03-11 Appendix B algorithm: |
| 95 | + * PBKDF2-HMAC-SHA512 96B -> two ELL2 maps -> pointAdd -> clear_cofactor(8) | ||
| 96 | + * -> X25519 -> HKDF-SHA256 -> AES-128-GCM. Each ciphertext covers the | ||
| 97 | + * negotiated key and GCM path end-to-end. */ | ||
| 98 | + | ||
| 94 | static const char *VEC_ENC_C2S = | 99 | static const char *VEC_ENC_C2S = |
| 95 | - "004d4314c0b59687cb950f8f9ed435b4c346420ef2fdcc03ac9c6ef296e0" | 100 | + "004d4314c0b59687cb950f8f9e15981d65b5520475d6185ebbb4d7d827e999d0" |
| 96 | - "2bce908cba0e152fdbf4e0f079e87be45e34015f0f9ee0ecf2d6c32b8b4571"; | 101 | + "59470a888d563f4a16a97defb81dd0c935b467a5ba73e9fa1b6425b0f9"; |
| 97 | static const char *VEC_ENC_S2C = | 102 | static const char *VEC_ENC_S2C = |
| 98 | - "0079e2ffc8fd789cfd0afbc08067adfcbefeedbadfc76cf7c9b896b4e6c7" | 103 | + "0079e2ffc8fd789cfd0afbc080ffc1be38c0915c03bec23453f28b16f611cd00" |
| 99 | - "f894934c571d379b8df554ad5272b0857df8738e252e2c707962b7b5d83c18"; | 104 | + "65e455cc44fac077ca39e15700a238d76728293191eaa3f064f23dced7"; |
| 105 | + | ||
| 106 | +static const char *VEC_ENC_C2S = | ||
| 107 | + "004d4314c0b59687cb950f8f9e14cfe940fff4fabbe0b196d247f0280e522a07" | ||
| 108 | + "0690676ae8da1693d0495d6dcbcda3d61bd6a65eeb0bd2dec35a506c5d"; | ||
| 109 | +static const char *VEC_ENC_S2C = | ||
| 110 | + "0079e2ffc8fd789cfd0afbc08096e9879c7fbec2164d7d1d18192e4c6b3b816" | ||
| 111 | + "cd9d52bd6527a21c6a7e3653b8b49e1cb5d3551e7591689b7627313a135"; | ||
| 112 | + | ||
| 113 | + | ||
| 114 | +/* Deterministic message vectors: same LCG run as above, expected byte-for-byte | ||
| 115 | + * message1..4 JSON payloads. Field values (salt/challenge/pk/kcfData, upper | ||
| 116 | + * case hex per the 2026-03-11 spec message tables) come from the same | ||
| 117 | + * independent reference run, not from the implementation under test. */ | ||
| 118 | +static const char *VEC_MSG1 = | ||
| 119 | + "{\"version\":\"2.0\",\"sessionId\":\"414d180a3571de14f241770bea0537b5\",\"msgId\":\"spekeMsg1\"}"; | ||
| 120 | + | ||
| 121 | +static const char *VEC_MSG2 = | ||
| 122 | + "{\"challenge1\":\"6819A8E6CAA4E23B69BD418941DA1EDC\"," | ||
| 123 | + "\"salt\":\"71471D94EC8993C744BCD8CFCB3CC5A6\"," | ||
| 124 | + "\"pk1\":\"72148856A25C87E3B185A49D681A7D77FB58B0FAB6A6BF6FC12C18E8C9EB2068\"," | ||
| 125 | + "\"iterations\":10000," | ||
| 126 | + "\"sessionId\":\"414d180a3571de14f241770bea0537b5\",\"msgId\":\"spekeMsg2\"}"; | ||
| 127 | +static const char *VEC_MSG3 = | ||
| 128 | + "{\"challenge2\":\"DD78A93A16592A906BB244A8F2E6CB5A\"," | ||
| 129 | + "\"pk2\":\"DA7DF5FCF7FB948A9875E4B731A5C61D11C35A296D6B3D3BC99A020C6A11A324\"," | ||
| 130 | + "\"kcfData2\":\"B44B4AAA550A64CB26034A10590C5CA728CEC2914AF709E8FB3F610398294884\"," | ||
| 131 | + "\"sessionId\":\"414d180a3571de14f241770bea0537b5\",\"msgId\":\"spekeMsg3\"}"; | ||
| 132 | +static const char *VEC_MSG4 = | ||
| 133 | + "{\"kcfData1\":\"BA010DF161C31BD4FA9D645728B08B96D464EC23D1A534C284889B940EA97C90\"," | ||
| 134 | + "\"sessionId\":\"414d180a3571de14f241770bea0537b5\",\"msgId\":\"spekeMsg4\"}"; | ||
| 135 | + | ||
| 136 | +static const char *VEC_MSG2 = | ||
| 137 | + "{\"challenge1\":\"6819A8E6CAA4E23B69BD418941DA1EDC\"," | ||
| 138 | + "\"salt\":\"71471D94EC8993C744BCD8CFCB3CC5A6\"," | ||
| 139 | + "\"pk1\":\"3CB7F9E5D1818A493BBA5F9F566106048E53FAE824E62A53F4F722EF54D95F16\"," | ||
| 140 | + "\"iterations\":10000," | ||
| 141 | + "\"sessionId\":\"414d180a3571de14f241770bea0537b5\",\"msgId\":\"spekeMsg2\"}"; | ||
| 142 | +static const char *VEC_MSG3 = | ||
| 143 | + "{\"challenge2\":\"DD78A93A16592A906BB244A8F2E6CB5A\"," | ||
| 144 | + "\"pk2\":\"B001DF1F9964499479A6BFDDA10F58488C11414DB01E0D78D4A6B5ACA3484672\"," | ||
| 145 | + "\"kcfData2\":\"E5EDFCC0828A6344A0A254730364531C2C38C18E1919C439DBCCAA8B67A21DAB\"," | ||
| 146 | + "\"sessionId\":\"414d180a3571de14f241770bea0537b5\",\"msgId\":\"spekeMsg3\"}"; | ||
| 147 | +static const char *VEC_MSG4 = | ||
| 148 | + "{\"kcfData1\":\"276BD735EEEB76F66D6EC194AFC486A36C6940E714F70554A3D85B8A33FB78ED\"," | ||
| 149 | + "\"sessionId\":\"414d180a3571de14f241770bea0537b5\",\"msgId\":\"spekeMsg4\"}"; | ||
| 150 | + | ||
| 100 | 151 | ||
| 101 | 152 | ||
| 102 | 153 | ||
| 154 | + | ||
| 155 | + | ||
| 156 | +static void ExpectStrEq(const char *actual, const char *expected, const char *label) | ||
| 157 | +{ | ||
| 158 | + if ((actual == NULL) || (expected == NULL) || (strcmp(actual, expected) != 0)) { | ||
| 159 | + g_failures++; | ||
| 160 | + (void)fprintf(stderr, "%s mismatch:\n got %s\n expected %s\n", label, | ||
| 161 | + (actual != NULL) ? actual : "(null)", (expected != NULL) ? expected : "(null)"); | ||
| 162 | + } | ||
| 163 | +} | ||
| 164 | + | ||
| 165 | +/* Flip one hex digit inside a JSON string field value (always changes the | ||
| 166 | + * value, always stays a valid hex character). */ | ||
| 167 | +static void FlipHexCharInField(char *msg, const char *field) | ||
| 168 | +{ | ||
| 169 | + char anchor[40]; | ||
| 170 | + (void)snprintf_s(anchor, sizeof(anchor), sizeof(anchor) - 1, "\"%s\":\"", field); | ||
| 171 | + char *pos = strstr(msg, anchor); | ||
| 172 | + if (pos == NULL) { | ||
| 173 | + g_failures++; | ||
| 174 | + (void)fprintf(stderr, "field %s not found in message\n", field); | ||
| 175 | + return; | ||
| 176 | + } | ||
| 177 | + pos += strlen(anchor); | ||
| 178 | + *pos = *pos ^ 0x01U; | ||
| 179 | +} | ||
| 180 | + | ||
| 181 | +static char *DupMsg(const uint8_t *msg, uint32_t len) | ||
| 182 | +{ | ||
| 183 | + if ((msg == NULL) || (len == 0)) { | ||
| 184 | + return NULL; | ||
| 185 | + } | ||
| 186 | + char *copy = (char *)IotcMalloc(len + 1); | ||
| 187 | + if (copy == NULL) { | ||
| 188 | + g_failures++; | ||
| 189 | + return NULL; | ||
| 190 | + } | ||
| 191 | + if (memcpy_s(copy, len + 1, msg, len) != EOK) { | ||
| 192 | + IotcFree(copy); | ||
| 193 | + g_failures++; | ||
| 194 | + return NULL; | ||
| 195 | + } | ||
| 196 | + copy[len] = '\0'; | ||
| 197 | + return copy; | ||
| 198 | +} | ||
| 199 | + | ||
| 200 | +static char *ReplaceSubstring(const char *msg, const char *from, const char *to) | ||
| 201 | +{ | ||
| 202 | + const char *pos = strstr(msg, from); | ||
| 203 | + if (pos == NULL) { | ||
| 204 | + g_failures++; | ||
| 205 | + return NULL; | ||
| 206 | + } | ||
| 207 | + size_t prefixLen = (size_t)(pos - msg); | ||
| 208 | + size_t newLen = strlen(msg) - strlen(from) + strlen(to); | ||
| 209 | + char *copy = (char *)IotcMalloc(newLen + 1); | ||
| 210 | + if (copy == NULL) { | ||
| 211 | + g_failures++; | ||
| 212 | + return NULL; | ||
| 213 | + } | ||
| 214 | + if (memcpy_s(copy, newLen + 1, msg, prefixLen) != EOK || | ||
| 215 | + memcpy_s(copy + prefixLen, newLen + 1 - prefixLen, to, strlen(to)) != EOK || | ||
| 216 | + strcpy_s(copy + prefixLen + strlen(to), newLen + 1 - prefixLen - strlen(to), | ||
| 217 | + pos + strlen(from)) != EOK) { | ||
| 218 | + IotcFree(copy); | ||
| 219 | + g_failures++; | ||
| 220 | + return NULL; | ||
| 221 | + } | ||
| 222 | + return copy; | ||
| 223 | +} | ||
| 224 | + | ||
| 225 | +/* Negotiation must not produce a usable session before SPEKE_STATE_DONE: | ||
| 226 | + * both encrypt and decrypt have to be rejected. */ | ||
| 227 | +static void ExpectCryptoRejected(SpekeSession *session, const char *label) | ||
| 228 | +{ | ||
| 229 | + static const uint8_t dummyPlain[] = "speke v2 state gate probe"; | ||
| 230 | + static const uint8_t dummyBlob[40] = { 0 }; | ||
| 231 | + uint8_t *out = NULL; | ||
| 232 | + uint32_t outLen = 0; | ||
| 233 | + if (SpekeEncryptData(session, dummyPlain, sizeof(dummyPlain) - 1, &out, &outLen) == IOTC_OK) { | ||
| 234 | + g_failures++; | ||
| 235 | + (void)fprintf(stderr, "%s: encrypt unexpectedly succeeded before nego done\n", label); | ||
| 236 | + IotcFree(out); | ||
| 237 | + } | ||
| 238 | + out = NULL; | ||
| 239 | + outLen = 0; | ||
| 240 | + if (SpekeDecryptData(session, dummyBlob, sizeof(dummyBlob), &out, &outLen) == IOTC_OK) { | ||
| 241 | + g_failures++; | ||
| 242 | + (void)fprintf(stderr, "%s: decrypt unexpectedly succeeded before nego done\n", label); | ||
| 243 | + IotcFree(out); | ||
| 244 | + } | ||
| 245 | +} | ||
| 103 | 246 | ||
| 104 | static void ExpectHexEq(const uint8_t *buf, uint32_t len, const char *expectedHex) | 247 | static void ExpectHexEq(const uint8_t *buf, uint32_t len, const char *expectedHex) |
| 105 | { | 248 | { |
| @@ -274,12 +417,27 @@ static void CheckDataRoundtrip(SpekeSession *sender, SpekeSession *receiver, con | |||
| 274 | DumpHex(tc->dumpLabel, enc, encLen); | 417 | DumpHex(tc->dumpLabel, enc, encLen); |
| 275 | } | 418 | } |
| 276 | 419 | ||
| 277 | - /* GCM tag corruption must be rejected */ | 420 | + /* GCM tag corruption must be rejected and no (partial) plaintext delivered */ |
| 278 | enc[encLen - 1] ^= 0x01U; | 421 | enc[encLen - 1] ^= 0x01U; |
| 279 | uint8_t *bad = NULL; | 422 | uint8_t *bad = NULL; |
| 280 | uint32_t badLen = 0; | 423 | uint32_t badLen = 0; |
| 281 | ExpectTrue(SpekeDecryptData(receiver, enc, encLen, &bad, &badLen) != IOTC_OK); | 424 | ExpectTrue(SpekeDecryptData(receiver, enc, encLen, &bad, &badLen) != IOTC_OK); |
| 282 | ExpectTrue(bad == NULL); | 425 | ExpectTrue(bad == NULL); |
| 426 | + enc[encLen - 1] ^= 0x01U; /* restore tag */ | ||
| 427 | + | ||
| 428 | + /* GCM ciphertext corruption must equally be rejected (tag check fails) */ | ||
| 429 | + enc[encLen - TEST_GCM_TAG_LEN - 1] ^= 0x01U; | ||
| 430 | + ExpectTrue(SpekeDecryptData(receiver, enc, encLen, &bad, &badLen) != IOTC_OK); | ||
| 431 | + ExpectTrue(bad == NULL); | ||
| 432 | + enc[encLen - TEST_GCM_TAG_LEN - 1] ^= 0x01U; /* restore */ | ||
| 433 | + | ||
| 434 | + /* 版本字节不参与 GCM tag,必须单独校验,不能接受未知封装版本。 */ | ||
| 435 | + enc[0] = 1; | ||
| 436 | + ExpectEqInt(SpekeDecryptData(receiver, enc, encLen, &bad, &badLen), | ||
| 437 | + IOTC_CORE_COMM_SEC_ERR_SPEKE_GCM_VERSION); | ||
| 438 | + ExpectTrue(bad == NULL); | ||
| 439 | + ExpectEqInt(badLen, 0); | ||
| 440 | + enc[0] = 0; | ||
| 283 | 441 | ||
| 284 | IotcFree(enc); | 442 | IotcFree(enc); |
| 285 | } | 443 | } |
| @@ -334,11 +492,408 @@ static void TestSpekeApiWrongPin(void) | |||
| 334 | ExpectEqInt(serverUser.finishCount, 1); | 492 | ExpectEqInt(serverUser.finishCount, 1); |
| 335 | ExpectTrue(serverUser.finishErr != IOTC_OK); | 493 | ExpectTrue(serverUser.finishErr != IOTC_OK); |
| 336 | ExpectEqInt(clientUser.finishCount, 0); | 494 | ExpectEqInt(clientUser.finishCount, 0); |
| 495 | + /* failed negotiation must not leave a usable session on either end */ | ||
| 496 | + ExpectCryptoRejected(server, "wrongpin server"); | ||
| 497 | + ExpectCryptoRejected(client, "wrongpin client"); | ||
| 337 | 498 | ||
| 338 | SpekeFreeSession(client); | 499 | SpekeFreeSession(client); |
| 339 | SpekeFreeSession(server); | 500 | SpekeFreeSession(server); |
| 340 | } | 501 | } |
| 341 | 502 | ||
| 503 | +/* Both ends hold different (i.e. mutually wrong) PINs: negotiation must fail | ||
| 504 | + * and neither side may end up with a usable session. */ | ||
| 505 | +static void TestSpekeApiBothWrongPin(void) | ||
| 506 | +{ | ||
| 507 | + static const uint8_t PIN_WRONG_SERVER[] = "999999"; | ||
| 508 | + SpekeTestUser serverUser = { PIN_WRONG_SERVER, sizeof(PIN_WRONG_SERVER) - 1, 0, 0, -1 }; | ||
| 509 | + SpekeTestUser clientUser = { PIN_WRONG, sizeof(PIN_WRONG) - 1, 0, 0, -1 }; | ||
| 510 | + | ||
| 511 | + SpekeSession *server = SpekeInitSession(SPEKE_TYPE_SERVER, &g_testCb, &serverUser); | ||
| 512 | + SpekeSession *client = SpekeInitSession(SPEKE_TYPE_CLIENT, &g_testCb, &clientUser); | ||
| 513 | + ExpectTrue(server != NULL && client != NULL); | ||
| 514 | + if (server == NULL || client == NULL) { | ||
| 515 | + return; | ||
| 516 | + } | ||
| 517 | + | ||
| 518 | + int32_t serverCfmRet = -1; | ||
| 519 | + bool ok = RunNegotiation(client, server, &clientUser, &serverUser, &serverCfmRet); | ||
| 520 | + ExpectTrue(!ok); | ||
| 521 | + ExpectTrue(serverCfmRet != IOTC_OK); | ||
| 522 | + ExpectEqInt(serverUser.finishCount, 1); | ||
| 523 | + ExpectTrue(serverUser.finishErr != IOTC_OK); | ||
| 524 | + ExpectEqInt(clientUser.finishCount, 0); | ||
| 525 | + ExpectCryptoRejected(server, "bothwrong server"); | ||
| 526 | + ExpectCryptoRejected(client, "bothwrong client"); | ||
| 527 | + | ||
| 528 | + SpekeFreeSession(client); | ||
| 529 | + SpekeFreeSession(server); | ||
| 530 | +} | ||
| 531 | + | ||
| 532 | +/* Deterministic vectors: fixed LCG run, message1..4 must match the reference | ||
| 533 | + * payloads byte-for-byte (field values independently computed from the spec). */ | ||
| 534 | +static void TestSpekeApiDeterministicMessages(void) | ||
| 535 | +{ | ||
| 536 | + g_rngState = 0x12345678U; | ||
| 537 | + SpekeTestUser serverUser = { PIN, sizeof(PIN) - 1, 0, 0, -1 }; | ||
| 538 | + SpekeTestUser clientUser = { PIN, sizeof(PIN) - 1, 0, 0, -1 }; | ||
| 539 | + | ||
| 540 | + SpekeSession *server = SpekeInitSession(SPEKE_TYPE_SERVER, &g_testCb, &serverUser); | ||
| 541 | + SpekeSession *client = SpekeInitSession(SPEKE_TYPE_CLIENT, &g_testCb, &clientUser); | ||
| 542 | + ExpectTrue(server != NULL && client != NULL); | ||
| 543 | + if (server == NULL || client == NULL) { | ||
| 544 | + return; | ||
| 545 | + } | ||
| 546 | + | ||
| 547 | + uint8_t *msg1 = NULL; | ||
| 548 | + uint32_t msg1Len = 0; | ||
| 549 | + ExpectEqInt(SpekeStartSession(client, &msg1, &msg1Len), IOTC_OK); | ||
| 550 | + ExpectStrEq((const char *)msg1, VEC_MSG1, "msg1"); | ||
| 551 | + | ||
| 552 | + uint8_t *msg2 = NULL; | ||
| 553 | + uint32_t msg2Len = 0; | ||
| 554 | + ExpectEqInt(SpekeProcessPacket(server, (const char *)msg1, &msg2, &msg2Len), IOTC_OK); | ||
| 555 | + ExpectStrEq((const char *)msg2, VEC_MSG2, "msg2"); | ||
| 556 | + IotcFree(msg1); | ||
| 557 | + | ||
| 558 | + uint8_t *msg3 = NULL; | ||
| 559 | + uint32_t msg3Len = 0; | ||
| 560 | + ExpectEqInt(SpekeProcessPacket(client, (const char *)msg2, &msg3, &msg3Len), IOTC_OK); | ||
| 561 | + ExpectStrEq((const char *)msg3, VEC_MSG3, "msg3"); | ||
| 562 | + IotcFree(msg2); | ||
| 563 | + | ||
| 564 | + uint8_t *msg4 = NULL; | ||
| 565 | + uint32_t msg4Len = 0; | ||
| 566 | + ExpectEqInt(SpekeProcessPacket(server, (const char *)msg3, &msg4, &msg4Len), IOTC_OK); | ||
| 567 | + ExpectStrEq((const char *)msg4, VEC_MSG4, "msg4"); | ||
| 568 | + IotcFree(msg3); | ||
| 569 | + IotcFree(msg4); | ||
| 570 | + | ||
| 571 | + SpekeFreeSession(client); | ||
| 572 | + SpekeFreeSession(server); | ||
| 573 | +} | ||
| 574 | + | ||
| 575 | +/* Single-byte tampering of protected message fields must fail negotiation. */ | ||
| 576 | +static void TestSpekeApiMsgTamper(void) | ||
| 577 | +{ | ||
| 578 | + SpekeTestUser serverUser = { PIN, sizeof(PIN) - 1, 0, 0, -1 }; | ||
| 579 | + SpekeTestUser clientUser = { PIN, sizeof(PIN) - 1, 0, 0, -1 }; | ||
| 580 | + SpekeSession *server = SpekeInitSession(SPEKE_TYPE_SERVER, &g_testCb, &serverUser); | ||
| 581 | + SpekeSession *client = SpekeInitSession(SPEKE_TYPE_CLIENT, &g_testCb, &clientUser); | ||
| 582 | + ExpectTrue(server != NULL && client != NULL); | ||
| 583 | + if (server == NULL || client == NULL) { | ||
| 584 | + return; | ||
| 585 | + } | ||
| 586 | + | ||
| 587 | + uint8_t *msg1 = NULL; | ||
| 588 | + uint32_t msg1Len = 0; | ||
| 589 | + ExpectEqInt(SpekeStartSession(client, &msg1, &msg1Len), IOTC_OK); | ||
| 590 | + uint8_t *msg2 = NULL; | ||
| 591 | + uint32_t msg2Len = 0; | ||
| 592 | + ExpectEqInt(SpekeProcessPacket(server, (const char *)msg1, &msg2, &msg2Len), IOTC_OK); | ||
| 593 | + IotcFree(msg1); | ||
| 594 | + | ||
| 595 | + /* msg2: tamper one pk1 hex digit. The client cannot detect it, but the | ||
| 596 | + * derived keys diverge, so the server must reject the resulting msg3. */ | ||
| 597 | + char *badMsg2 = DupMsg(msg2, msg2Len); | ||
| 598 | + FlipHexCharInField(badMsg2, SPEKE_PK1_JSON); | ||
| 599 | + uint8_t *cfmBadPk1 = NULL; | ||
| 600 | + uint32_t cfmBadPk1Len = 0; | ||
| 601 | + ExpectEqInt(SpekeProcessPacket(client, badMsg2, &cfmBadPk1, &cfmBadPk1Len), IOTC_OK); | ||
| 602 | + IotcFree(badMsg2); | ||
| 603 | + ExpectTrue(cfmBadPk1 != NULL); | ||
| 604 | + if (cfmBadPk1 != NULL) { | ||
| 605 | + uint8_t *rej = NULL; | ||
| 606 | + uint32_t rejLen = 0; | ||
| 607 | + ExpectTrue(SpekeProcessPacket(server, (const char *)cfmBadPk1, &rej, &rejLen) != IOTC_OK); | ||
| 608 | + ExpectTrue(rej == NULL); | ||
| 609 | + ExpectEqInt(serverUser.finishCount, 1); | ||
| 610 | + ExpectTrue(serverUser.finishErr != IOTC_OK); | ||
| 611 | + ExpectEqInt(clientUser.finishCount, 0); | ||
| 612 | + IotcFree(cfmBadPk1); | ||
| 613 | + } | ||
| 614 | + ExpectCryptoRejected(server, "tamper-pk1 server"); | ||
| 615 | + ExpectCryptoRejected(client, "tamper-pk1 client"); | ||
| 616 | + SpekeFreeSession(client); | ||
| 617 | + SpekeFreeSession(server); | ||
| 618 | + | ||
| 619 | + /* msg3: tamper kcfData2 / pk2, server must reject immediately. */ | ||
| 620 | + static const char *msg3Fields[] = { SPEKE_KCF_DATA2_JSON, SPEKE_PK2_JSON, SPEKE_CHALLENGE2_JSON }; | ||
| 621 | + for (uint32_t i = 0; i < sizeof(msg3Fields) / sizeof(msg3Fields[0]); i++) { | ||
| 622 | + serverUser = (SpekeTestUser){ PIN, sizeof(PIN) - 1, 0, 0, -1 }; | ||
| 623 | + clientUser = (SpekeTestUser){ PIN, sizeof(PIN) - 1, 0, 0, -1 }; | ||
| 624 | + server = SpekeInitSession(SPEKE_TYPE_SERVER, &g_testCb, &serverUser); | ||
| 625 | + client = SpekeInitSession(SPEKE_TYPE_CLIENT, &g_testCb, &clientUser); | ||
| 626 | + ExpectTrue(server != NULL && client != NULL); | ||
| 627 | + if (server == NULL || client == NULL) { | ||
| 628 | + return; | ||
| 629 | + } | ||
| 630 | + msg1 = NULL; | ||
| 631 | + msg1Len = 0; | ||
| 632 | + ExpectEqInt(SpekeStartSession(client, &msg1, &msg1Len), IOTC_OK); | ||
| 633 | + msg2 = NULL; | ||
| 634 | + msg2Len = 0; | ||
| 635 | + ExpectEqInt(SpekeProcessPacket(server, (const char *)msg1, &msg2, &msg2Len), IOTC_OK); | ||
| 636 | + IotcFree(msg1); | ||
| 637 | + uint8_t *msg3 = NULL; | ||
| 638 | + uint32_t msg3Len = 0; | ||
| 639 | + ExpectEqInt(SpekeProcessPacket(client, (const char *)msg2, &msg3, &msg3Len), IOTC_OK); | ||
| 640 | + IotcFree(msg2); | ||
| 641 | + char *badMsg3 = DupMsg(msg3, msg3Len); | ||
| 642 | + IotcFree(msg3); | ||
| 643 | + FlipHexCharInField(badMsg3, msg3Fields[i]); | ||
| 644 | + uint8_t *rej = NULL; | ||
| 645 | + uint32_t rejLen = 0; | ||
| 646 | + ExpectTrue(SpekeProcessPacket(server, badMsg3, &rej, &rejLen) != IOTC_OK); | ||
| 647 | + ExpectTrue(rej == NULL); | ||
| 648 | + ExpectEqInt(serverUser.finishCount, 1); | ||
| 649 | + ExpectTrue(serverUser.finishErr != IOTC_OK); | ||
| 650 | + ExpectEqInt(clientUser.finishCount, 0); | ||
| 651 | + IotcFree(badMsg3); | ||
| 652 | + ExpectCryptoRejected(server, "tamper-msg3 server"); | ||
| 653 | + SpekeFreeSession(client); | ||
| 654 | + SpekeFreeSession(server); | ||
| 655 | + } | ||
| 656 | + | ||
| 657 | + /* msg4: tamper kcfData1, the client must reject and fail negotiation. */ | ||
| 658 | + serverUser = (SpekeTestUser){ PIN, sizeof(PIN) - 1, 0, 0, -1 }; | ||
| 659 | + clientUser = (SpekeTestUser){ PIN, sizeof(PIN) - 1, 0, 0, -1 }; | ||
| 660 | + server = SpekeInitSession(SPEKE_TYPE_SERVER, &g_testCb, &serverUser); | ||
| 661 | + client = SpekeInitSession(SPEKE_TYPE_CLIENT, &g_testCb, &clientUser); | ||
| 662 | + ExpectTrue(server != NULL && client != NULL); | ||
| 663 | + if (server == NULL || client == NULL) { | ||
| 664 | + return; | ||
| 665 | + } | ||
| 666 | + msg1 = NULL; | ||
| 667 | + msg1Len = 0; | ||
| 668 | + ExpectEqInt(SpekeStartSession(client, &msg1, &msg1Len), IOTC_OK); | ||
| 669 | + msg2 = NULL; | ||
| 670 | + msg2Len = 0; | ||
| 671 | + ExpectEqInt(SpekeProcessPacket(server, (const char *)msg1, &msg2, &msg2Len), IOTC_OK); | ||
| 672 | + IotcFree(msg1); | ||
| 673 | + uint8_t *msg3ok = NULL; | ||
| 674 | + uint32_t msg3okLen = 0; | ||
| 675 | + ExpectEqInt(SpekeProcessPacket(client, (const char *)msg2, &msg3ok, &msg3okLen), IOTC_OK); | ||
| 676 | + IotcFree(msg2); | ||
| 677 | + uint8_t *msg4ok = NULL; | ||
| 678 | + uint32_t msg4okLen = 0; | ||
| 679 | + ExpectEqInt(SpekeProcessPacket(server, (const char *)msg3ok, &msg4ok, &msg4okLen), IOTC_OK); | ||
| 680 | + IotcFree(msg3ok); | ||
| 681 | + char *badMsg4 = DupMsg(msg4ok, msg4okLen); | ||
| 682 | + IotcFree(msg4ok); | ||
| 683 | + FlipHexCharInField(badMsg4, SPEKE_KCF_DATA1_JSON); | ||
| 684 | + uint8_t *last = NULL; | ||
| 685 | + uint32_t lastLen = 0; | ||
| 686 | + ExpectTrue(SpekeProcessPacket(client, badMsg4, &last, &lastLen) != IOTC_OK); | ||
| 687 | + ExpectTrue(last == NULL); | ||
| 688 | + ExpectEqInt(clientUser.finishCount, 1); | ||
| 689 | + ExpectTrue(clientUser.finishErr != IOTC_OK); | ||
| 690 | + IotcFree(badMsg4); | ||
| 691 | + ExpectCryptoRejected(client, "tamper-msg4 client"); | ||
| 692 | + /* server already completed; a tampered msg4 must not tear its session down */ | ||
| 693 | + SpekeFreeSession(client); | ||
| 694 | + SpekeFreeSession(server); | ||
| 695 | +} | ||
| 696 | + | ||
| 697 | +/* Replayed negotiation messages must be rejected by the state machine. */ | ||
| 698 | +static void TestSpekeApiReplay(void) | ||
| 699 | +{ | ||
| 700 | + SpekeTestUser serverUser = { PIN, sizeof(PIN) - 1, 0, 0, -1 }; | ||
| 701 | + SpekeTestUser clientUser = { PIN, sizeof(PIN) - 1, 0, 0, -1 }; | ||
| 702 | + SpekeSession *server = SpekeInitSession(SPEKE_TYPE_SERVER, &g_testCb, &serverUser); | ||
| 703 | + SpekeSession *client = SpekeInitSession(SPEKE_TYPE_CLIENT, &g_testCb, &clientUser); | ||
| 704 | + ExpectTrue(server != NULL && client != NULL); | ||
| 705 | + if (server == NULL || client == NULL) { | ||
| 706 | + return; | ||
| 707 | + } | ||
| 708 | + | ||
| 709 | + uint8_t *msg1 = NULL; | ||
| 710 | + uint32_t msg1Len = 0; | ||
| 711 | + ExpectEqInt(SpekeStartSession(client, &msg1, &msg1Len), IOTC_OK); | ||
| 712 | + uint8_t *msg2 = NULL; | ||
| 713 | + uint32_t msg2Len = 0; | ||
| 714 | + ExpectEqInt(SpekeProcessPacket(server, (const char *)msg1, &msg2, &msg2Len), IOTC_OK); | ||
| 715 | + | ||
| 716 | + /* duplicate spekeMsg1 while the server waits for spekeMsg3 must be rejected */ | ||
| 717 | + uint8_t *rej = NULL; | ||
| 718 | + uint32_t rejLen = 0; | ||
| 719 | + ExpectTrue(SpekeProcessPacket(server, (const char *)msg1, &rej, &rejLen) != IOTC_OK); | ||
| 720 | + ExpectTrue(rej == NULL); | ||
| 721 | + | ||
| 722 | + /* duplicate spekeMsg2 while the client waits for spekeMsg4 must be rejected */ | ||
| 723 | + char *msg2Copy = DupMsg(msg2, msg2Len); | ||
| 724 | + uint8_t *msg3 = NULL; | ||
| 725 | + uint32_t msg3Len = 0; | ||
| 726 | + ExpectEqInt(SpekeProcessPacket(client, (const char *)msg2, &msg3, &msg3Len), IOTC_OK); | ||
| 727 | + rej = NULL; | ||
| 728 | + rejLen = 0; | ||
| 729 | + ExpectTrue(SpekeProcessPacket(client, msg2Copy, &rej, &rejLen) != IOTC_OK); | ||
| 730 | + ExpectTrue(rej == NULL); | ||
| 731 | + IotcFree(msg2Copy); | ||
| 732 | + | ||
| 733 | + char *msg3Copy = DupMsg(msg3, msg3Len); | ||
| 734 | + uint8_t *msg4 = NULL; | ||
| 735 | + uint32_t msg4Len = 0; | ||
| 736 | + ExpectEqInt(SpekeProcessPacket(server, (const char *)msg3, &msg4, &msg4Len), IOTC_OK); | ||
| 737 | + IotcFree(msg3); | ||
| 738 | + | ||
| 739 | + /* replayed spekeMsg3 after success must be rejected */ | ||
| 740 | + rej = NULL; | ||
| 741 | + rejLen = 0; | ||
| 742 | + ExpectTrue(SpekeProcessPacket(server, msg3Copy, &rej, &rejLen) != IOTC_OK); | ||
| 743 | + ExpectTrue(rej == NULL); | ||
| 744 | + IotcFree(msg3Copy); | ||
| 745 | + | ||
| 746 | + /* replayed spekeMsg1 after success must be rejected */ | ||
| 747 | + rej = NULL; | ||
| 748 | + rejLen = 0; | ||
| 749 | + ExpectTrue(SpekeProcessPacket(server, (const char *)msg1, &rej, &rejLen) != IOTC_OK); | ||
| 750 | + ExpectTrue(rej == NULL); | ||
| 751 | + IotcFree(msg1); | ||
| 752 | + | ||
| 753 | + /* client completes, then a replayed spekeMsg4 must be rejected */ | ||
| 754 | + char *msg4Copy = DupMsg(msg4, msg4Len); | ||
| 755 | + uint8_t *last = NULL; | ||
| 756 | + uint32_t lastLen = 0; | ||
| 757 | + ExpectEqInt(SpekeProcessPacket(client, (const char *)msg4, &last, &lastLen), IOTC_OK); | ||
| 758 | + ExpectTrue(last == NULL); | ||
| 759 | + IotcFree(msg4); | ||
| 760 | + rej = NULL; | ||
| 761 | + rejLen = 0; | ||
| 762 | + ExpectTrue(SpekeProcessPacket(client, msg4Copy, &rej, &rejLen) != IOTC_OK); | ||
| 763 | + ExpectTrue(rej == NULL); | ||
| 764 | + IotcFree(msg4Copy); | ||
| 765 | + | ||
| 766 | + ExpectEqInt(clientUser.finishCount, 1); | ||
| 767 | + ExpectEqInt(clientUser.finishErr, IOTC_OK); | ||
| 768 | + /* the completed session must still work after all the replay attempts */ | ||
| 769 | + static const uint8_t probe[] = "post replay probe"; | ||
| 770 | + uint8_t *enc = NULL; | ||
| 771 | + uint32_t encLen = 0; | ||
| 772 | + ExpectEqInt(SpekeEncryptData(client, probe, sizeof(probe) - 1, &enc, &encLen), IOTC_OK); | ||
| 773 | + uint8_t *dec = NULL; | ||
| 774 | + uint32_t decLen = 0; | ||
| 775 | + ExpectEqInt(SpekeDecryptData(server, enc, encLen, &dec, &decLen), IOTC_OK); | ||
| 776 | + if (dec != NULL) { | ||
| 777 | + ExpectEqInt(decLen, sizeof(probe) - 1); | ||
| 778 | + ExpectTrue(memcmp(dec, probe, sizeof(probe) - 1) == 0); | ||
| 779 | + IotcFree(dec); | ||
| 780 | + } else { | ||
| 781 | + g_failures++; | ||
| 782 | + } | ||
| 783 | + IotcFree(enc); | ||
| 784 | + | ||
| 785 | + SpekeFreeSession(client); | ||
| 786 | + SpekeFreeSession(server); | ||
| 787 | +} | ||
| 788 | + | ||
| 789 | +static void TestSpekeApiSessionBinding(void) | ||
| 790 | +{ | ||
| 791 | + SpekeTestUser serverUser = { PIN, sizeof(PIN) - 1, 0, 0, -1 }; | ||
| 792 | + SpekeTestUser clientUser = { PIN, sizeof(PIN) - 1, 0, 0, -1 }; | ||
| 793 | + SpekeSession *server = SpekeInitSession(SPEKE_TYPE_SERVER, &g_testCb, &serverUser); | ||
| 794 | + SpekeSession *client = SpekeInitSession(SPEKE_TYPE_CLIENT, &g_testCb, &clientUser); | ||
| 795 | + ExpectTrue(server != NULL && client != NULL); | ||
| 796 | + if (server == NULL || client == NULL) { | ||
| 797 | + return; | ||
| 798 | + } | ||
| 799 | + | ||
| 800 | + uint8_t *msg1 = NULL; | ||
| 801 | + uint32_t msg1Len = 0; | ||
| 802 | + uint8_t *msg2 = NULL; | ||
| 803 | + uint32_t msg2Len = 0; | ||
| 804 | + ExpectEqInt(SpekeStartSession(client, &msg1, &msg1Len), IOTC_OK); | ||
| 805 | + ExpectEqInt(SpekeProcessPacket(server, (const char *)msg1, &msg2, &msg2Len), IOTC_OK); | ||
| 806 | + IotcFree(msg1); | ||
| 807 | + | ||
| 808 | + char *wrong = DupMsg(msg2, msg2Len); | ||
| 809 | + FlipHexCharInField(wrong, SPEKE_SESSION_ID_JSON); | ||
| 810 | + uint8_t *out = (uint8_t *)(uintptr_t)1; | ||
| 811 | + uint32_t outLen = 123; | ||
| 812 | + ExpectEqInt(SpekeProcessPacket(client, wrong, &out, &outLen), IOTC_CORE_COMM_SEC_ERR_SPEKE_SESSION_ID); | ||
| 813 | + ExpectTrue(out == NULL); | ||
| 814 | + ExpectEqInt(outLen, 0); | ||
| 815 | + IotcFree(wrong); | ||
| 816 | + | ||
| 817 | + uint8_t *msg3 = NULL; | ||
| 818 | + uint32_t msg3Len = 0; | ||
| 819 | + ExpectEqInt(SpekeProcessPacket(client, (const char *)msg2, &msg3, &msg3Len), IOTC_OK); | ||
| 820 | + IotcFree(msg2); | ||
| 821 | + wrong = DupMsg(msg3, msg3Len); | ||
| 822 | + FlipHexCharInField(wrong, SPEKE_SESSION_ID_JSON); | ||
| 823 | + out = (uint8_t *)(uintptr_t)1; | ||
| 824 | + outLen = 123; | ||
| 825 | + ExpectEqInt(SpekeProcessPacket(server, wrong, &out, &outLen), IOTC_CORE_COMM_SEC_ERR_SPEKE_SESSION_ID); | ||
| 826 | + ExpectTrue(out == NULL); | ||
| 827 | + ExpectEqInt(serverUser.finishCount, 0); | ||
| 828 | + IotcFree(wrong); | ||
| 829 | + | ||
| 830 | + uint8_t *msg4 = NULL; | ||
| 831 | + uint32_t msg4Len = 0; | ||
| 832 | + ExpectEqInt(SpekeProcessPacket(server, (const char *)msg3, &msg4, &msg4Len), IOTC_OK); | ||
| 833 | + IotcFree(msg3); | ||
| 834 | + wrong = DupMsg(msg4, msg4Len); | ||
| 835 | + FlipHexCharInField(wrong, SPEKE_SESSION_ID_JSON); | ||
| 836 | + out = (uint8_t *)(uintptr_t)1; | ||
| 837 | + outLen = 123; | ||
| 838 | + ExpectEqInt(SpekeProcessPacket(client, wrong, &out, &outLen), IOTC_CORE_COMM_SEC_ERR_SPEKE_SESSION_ID); | ||
| 839 | + ExpectTrue(out == NULL); | ||
| 840 | + ExpectEqInt(clientUser.finishCount, 0); | ||
| 841 | + IotcFree(wrong); | ||
| 842 | + | ||
| 843 | + out = (uint8_t *)(uintptr_t)1; | ||
| 844 | + outLen = 123; | ||
| 845 | + ExpectEqInt(SpekeProcessPacket(client, (const char *)msg4, &out, &outLen), IOTC_OK); | ||
| 846 | + ExpectTrue(out == NULL); | ||
| 847 | + ExpectEqInt(outLen, 0); | ||
| 848 | + ExpectEqInt(clientUser.finishCount, 1); | ||
| 849 | + IotcFree(msg4); | ||
| 850 | + SpekeFreeSession(client); | ||
| 851 | + SpekeFreeSession(server); | ||
| 852 | +} | ||
| 853 | + | ||
| 854 | +static void TestSpekeApiIterationsStrict(void) | ||
| 855 | +{ | ||
| 856 | + SpekeTestUser serverUser = { PIN, sizeof(PIN) - 1, 0, 0, -1 }; | ||
| 857 | + SpekeTestUser clientUser = { PIN, sizeof(PIN) - 1, 0, 0, -1 }; | ||
| 858 | + SpekeSession *server = SpekeInitSession(SPEKE_TYPE_SERVER, &g_testCb, &serverUser); | ||
| 859 | + SpekeSession *client = SpekeInitSession(SPEKE_TYPE_CLIENT, &g_testCb, &clientUser); | ||
| 860 | + ExpectTrue(server != NULL && client != NULL); | ||
| 861 | + if (server == NULL || client == NULL) { | ||
| 862 | + return; | ||
| 863 | + } | ||
| 864 | + uint8_t *msg1 = NULL; | ||
| 865 | + uint32_t msg1Len = 0; | ||
| 866 | + uint8_t *msg2 = NULL; | ||
| 867 | + uint32_t msg2Len = 0; | ||
| 868 | + ExpectEqInt(SpekeStartSession(client, &msg1, &msg1Len), IOTC_OK); | ||
| 869 | + ExpectEqInt(SpekeProcessPacket(server, (const char *)msg1, &msg2, &msg2Len), IOTC_OK); | ||
| 870 | + IotcFree(msg1); | ||
| 871 | + | ||
| 872 | + static const char *invalidFields[] = { | ||
| 873 | + "\"iterations\":1000000,", | ||
| 874 | + "\"iterations\":\"10000\",", | ||
| 875 | + "" | ||
| 876 | + }; | ||
| 877 | + for (uint32_t i = 0; i < sizeof(invalidFields) / sizeof(invalidFields[0]); ++i) { | ||
| 878 | + char *bad = ReplaceSubstring((const char *)msg2, "\"iterations\":10000,", invalidFields[i]); | ||
| 879 | + uint8_t *out = (uint8_t *)(uintptr_t)1; | ||
| 880 | + uint32_t outLen = 123; | ||
| 881 | + ExpectEqInt(SpekeProcessPacket(client, bad, &out, &outLen), IOTC_CORE_COMM_SEC_ERR_SPEKE_ITERATIONS); | ||
| 882 | + ExpectTrue(out == NULL); | ||
| 883 | + ExpectEqInt(outLen, 0); | ||
| 884 | + IotcFree(bad); | ||
| 885 | + } | ||
| 886 | + | ||
| 887 | + /* 严格拒绝不得破坏状态;原始合法 msg2 仍可继续处理。 */ | ||
| 888 | + uint8_t *msg3 = NULL; | ||
| 889 | + uint32_t msg3Len = 0; | ||
| 890 | + ExpectEqInt(SpekeProcessPacket(client, (const char *)msg2, &msg3, &msg3Len), IOTC_OK); | ||
| 891 | + IotcFree(msg2); | ||
| 892 | + IotcFree(msg3); | ||
| 893 | + SpekeFreeSession(client); | ||
| 894 | + SpekeFreeSession(server); | ||
| 895 | +} | ||
| 896 | + | ||
| 342 | static void TestSpekeApiInitNegative(void) | 897 | static void TestSpekeApiInitNegative(void) |
| 343 | { | 898 | { |
| 344 | SpekeTestUser user = { PIN, sizeof(PIN) - 1, 0, 0, -1 }; | 899 | SpekeTestUser user = { PIN, sizeof(PIN) - 1, 0, 0, -1 }; |
| @@ -358,6 +913,8 @@ static void TestSpekeApiInitNegative(void) | |||
| 358 | if (server != NULL) { | 913 | if (server != NULL) { |
| 359 | ExpectTrue(SpekeStartSession(server, &msg, &len) != IOTC_OK); | 914 | ExpectTrue(SpekeStartSession(server, &msg, &len) != IOTC_OK); |
| 360 | ExpectTrue(SpekeProcessPacket(server, "not json", &msg, &len) != IOTC_OK); | 915 | ExpectTrue(SpekeProcessPacket(server, "not json", &msg, &len) != IOTC_OK); |
| 916 | + /* no negotiation happened: crypto must stay locked */ | ||
| 917 | + ExpectCryptoRejected(server, "init server"); | ||
| 361 | SpekeFreeSession(server); | 918 | SpekeFreeSession(server); |
| 362 | } | 919 | } |
| 363 | } | 920 | } |
| @@ -366,7 +923,13 @@ int main(int argc, char **argv) | |||
| 366 | { | 923 | { |
| 367 | bool dump = (argc > 1) && (strcmp(argv[1], "dump") == 0); | 924 | bool dump = (argc > 1) && (strcmp(argv[1], "dump") == 0); |
| 368 | TestSpekeApiLoopback(dump); | 925 | TestSpekeApiLoopback(dump); |
| 926 | + TestSpekeApiDeterministicMessages(); | ||
| 369 | TestSpekeApiWrongPin(); | 927 | TestSpekeApiWrongPin(); |
| 928 | + TestSpekeApiBothWrongPin(); | ||
| 929 | + TestSpekeApiMsgTamper(); | ||
| 930 | + TestSpekeApiReplay(); | ||
| 931 | + TestSpekeApiSessionBinding(); | ||
| 932 | + TestSpekeApiIterationsStrict(); | ||
| 370 | TestSpekeApiInitNegative(); | 933 | TestSpekeApiInitNegative(); |
| 371 | 934 | ||
| 372 | if (g_failures != 0) { | 935 | if (g_failures != 0) { |
| @@ -0,0 +1,16 @@ | |||
| 1 | +#!/bin/bash | ||
| 2 | +set -uo pipefail | ||
| 3 | + | ||
| 4 | +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) | ||
| 5 | +IOTC_ROOT=$(cd "$SCRIPT_DIR/../../.." && pwd) | ||
| 6 | +OUT_DIR=$(mktemp -d /tmp/iotc_kv_oh_lite_test.XXXXXX) | ||
| 7 | + | ||
| 8 | +gcc -std=c99 -Wall -Wextra -Werror \ | ||
| 9 | + -I"$SCRIPT_DIR/stubs" \ | ||
| 10 | + -I"$IOTC_ROOT/adapter/include" \ | ||
| 11 | + -I"$IOTC_ROOT/interfaces/kits/common" \ | ||
| 12 | + "$SCRIPT_DIR/test_kv_oh_lite.c" \ | ||
| 13 | + "$IOTC_ROOT/adapter/kv/oh_lite/iotc_kv.c" \ | ||
| 14 | + -o "$OUT_DIR/test_kv_oh_lite" || exit 1 | ||
| 15 | + | ||
| 16 | +"$OUT_DIR/test_kv_oh_lite" | ||
| @@ -0,0 +1,39 @@ | |||
| 1 | + | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + | ||
| 11 | +static inline int strcpy_s(char *dest, size_t destMax, const char *src) | ||
| 12 | +{ | ||
| 13 | + size_t len = (src == NULL) ? 0 : strlen(src); | ||
| 14 | + if (dest == NULL || src == NULL || len + 1 > destMax) { | ||
| 15 | + return -1; | ||
| 16 | + } | ||
| 17 | + (void)memcpy(dest, src, len + 1); | ||
| 18 | + return EOK; | ||
| 19 | +} | ||
| 20 | + | ||
| 21 | +static inline int sprintf_s(char *dest, size_t destMax, const char *format, ...) | ||
| 22 | +{ | ||
| 23 | + va_list ap; | ||
| 24 | + va_start(ap, format); | ||
| 25 | + int ret = vsnprintf(dest, destMax, format, ap); | ||
| 26 | + va_end(ap); | ||
| 27 | + return (ret < 0 || (size_t)ret >= destMax) ? -1 : ret; | ||
| 28 | +} | ||
| 29 | + | ||
| 30 | +static inline int memset_s(void *dest, size_t destMax, int c, size_t count) | ||
| 31 | +{ | ||
| 32 | + if (dest == NULL || count > destMax) { | ||
| 33 | + return -1; | ||
| 34 | + } | ||
| 35 | + (void)memset(dest, c, count); | ||
| 36 | + return EOK; | ||
| 37 | +} | ||
| 38 | + | ||
| 39 | + | ||
| @@ -0,0 +1,16 @@ | |||
| 1 | + | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | +int UtilsFileOpen(const char *path, int oflag, int mode); | ||
| 10 | +int UtilsFileClose(int fd); | ||
| 11 | +int UtilsFileRead(int fd, char *buf, unsigned int len); | ||
| 12 | +int UtilsFileWrite(int fd, const char *buf, unsigned int len); | ||
| 13 | +int UtilsFileDelete(const char *path); | ||
| 14 | +int UtilsFileStat(const char *path, unsigned int *fileSize); | ||
| 15 | + | ||
| 16 | + | ||
| @@ -0,0 +1,143 @@ | |||
| 1 | + | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | +typedef struct { | ||
| 14 | + char path[TEST_PATH_MAX]; | ||
| 15 | + uint8_t data[TEST_DATA_MAX]; | ||
| 16 | + uint32_t len; | ||
| 17 | + int present; | ||
| 18 | +} TestFile; | ||
| 19 | + | ||
| 20 | +static TestFile g_files[TEST_FILE_MAX]; | ||
| 21 | +static int g_checks; | ||
| 22 | +static int g_failures; | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + ++g_checks; \ | ||
| 26 | + if (!(cond)) { \ | ||
| 27 | + ++g_failures; \ | ||
| 28 | + (void)printf("FAIL %s:%d: %s\n", __FILE__, __LINE__, | ||
| 29 | + } \ | ||
| 30 | +} while (0) | ||
| 31 | + | ||
| 32 | +static TestFile *FindFile(const char *path) | ||
| 33 | +{ | ||
| 34 | + for (int i = 0; i < TEST_FILE_MAX; ++i) { | ||
| 35 | + if (g_files[i].present && strcmp(g_files[i].path, path) == 0) { | ||
| 36 | + return &g_files[i]; | ||
| 37 | + } | ||
| 38 | + } | ||
| 39 | + return NULL; | ||
| 40 | +} | ||
| 41 | + | ||
| 42 | +static TestFile *CreateFile(const char *path) | ||
| 43 | +{ | ||
| 44 | + for (int i = 0; i < TEST_FILE_MAX; ++i) { | ||
| 45 | + if (!g_files[i].present) { | ||
| 46 | + g_files[i].present = 1; | ||
| 47 | + (void)snprintf(g_files[i].path, sizeof(g_files[i].path), "%s", path); | ||
| 48 | + return &g_files[i]; | ||
| 49 | + } | ||
| 50 | + } | ||
| 51 | + return NULL; | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +int UtilsFileOpen(const char *path, int oflag, int mode) | ||
| 55 | +{ | ||
| 56 | + (void)mode; | ||
| 57 | + TestFile *file = FindFile(path); | ||
| 58 | + if (file == NULL && (oflag & O_CREAT_FS) != 0) { | ||
| 59 | + file = CreateFile(path); | ||
| 60 | + } | ||
| 61 | + if (file == NULL) { | ||
| 62 | + return -1; | ||
| 63 | + } | ||
| 64 | + if ((oflag & O_TRUNC_FS) != 0) { | ||
| 65 | + file->len = 0; | ||
| 66 | + } | ||
| 67 | + return (int)(file - g_files) + 1; | ||
| 68 | +} | ||
| 69 | + | ||
| 70 | +int UtilsFileClose(int fd) | ||
| 71 | +{ | ||
| 72 | + return (fd > 0 && fd <= TEST_FILE_MAX) ? 0 : -1; | ||
| 73 | +} | ||
| 74 | + | ||
| 75 | +int UtilsFileRead(int fd, char *buf, unsigned int len) | ||
| 76 | +{ | ||
| 77 | + if (fd <= 0 || fd > TEST_FILE_MAX || !g_files[fd - 1].present || len > g_files[fd - 1].len) { | ||
| 78 | + return -1; | ||
| 79 | + } | ||
| 80 | + (void)memcpy(buf, g_files[fd - 1].data, len); | ||
| 81 | + return (int)len; | ||
| 82 | +} | ||
| 83 | + | ||
| 84 | +int UtilsFileWrite(int fd, const char *buf, unsigned int len) | ||
| 85 | +{ | ||
| 86 | + if (fd <= 0 || fd > TEST_FILE_MAX || !g_files[fd - 1].present || len > TEST_DATA_MAX) { | ||
| 87 | + return -1; | ||
| 88 | + } | ||
| 89 | + (void)memcpy(g_files[fd - 1].data, buf, len); | ||
| 90 | + g_files[fd - 1].len = len; | ||
| 91 | + return (int)len; | ||
| 92 | +} | ||
| 93 | + | ||
| 94 | +int UtilsFileDelete(const char *path) | ||
| 95 | +{ | ||
| 96 | + TestFile *file = FindFile(path); | ||
| 97 | + if (file == NULL) { | ||
| 98 | + return -1; | ||
| 99 | + } | ||
| 100 | + (void)memset(file, 0, sizeof(*file)); | ||
| 101 | + return 0; | ||
| 102 | +} | ||
| 103 | + | ||
| 104 | +int UtilsFileStat(const char *path, unsigned int *fileSize) | ||
| 105 | +{ | ||
| 106 | + TestFile *file = FindFile(path); | ||
| 107 | + if (file == NULL || fileSize == NULL) { | ||
| 108 | + return -1; | ||
| 109 | + } | ||
| 110 | + *fileSize = file->len; | ||
| 111 | + return 0; | ||
| 112 | +} | ||
| 113 | + | ||
| 114 | +int main(void) | ||
| 115 | +{ | ||
| 116 | + CHECK(IotcKvInit("/kv") == IOTC_OK); | ||
| 117 | + static const uint8_t longValue[] = "12345678"; | ||
| 118 | + static const uint8_t shortValue[] = "abc"; | ||
| 119 | + CHECK(IotcKvSetValue("item", longValue, sizeof(longValue)) == IOTC_OK); | ||
| 120 | + CHECK(IotcKvSetValue("item", shortValue, sizeof(shortValue)) == IOTC_OK); | ||
| 121 | + | ||
| 122 | + uint32_t len = 0; | ||
| 123 | + CHECK(IotcKvGetLen("item", &len) == IOTC_OK); | ||
| 124 | + CHECK(len == sizeof(shortValue)); | ||
| 125 | + uint8_t value[16] = {0}; | ||
| 126 | + len = sizeof(value); | ||
| 127 | + CHECK(IotcKvGetValue("item", value, &len) == IOTC_OK); | ||
| 128 | + CHECK(len == sizeof(shortValue)); | ||
| 129 | + CHECK(memcmp(value, shortValue, sizeof(shortValue)) == 0); | ||
| 130 | + | ||
| 131 | + len = 99; | ||
| 132 | + CHECK(IotcKvGetLen("missing", &len) == IOTC_OK); | ||
| 133 | + CHECK(len == 0); | ||
| 134 | + CHECK(FindFile("/kv/missing") == NULL); | ||
| 135 | + len = sizeof(value); | ||
| 136 | + CHECK(IotcKvGetValue("missing", value, &len) != IOTC_OK); | ||
| 137 | + CHECK(FindFile("/kv/missing") == NULL); | ||
| 138 | + CHECK(IotcKvGetLen("item", NULL) == IOTC_ERR_PARAM_INVALID); | ||
| 139 | + CHECK(IotcKvDeInit() == IOTC_OK); | ||
| 140 | + | ||
| 141 | + (void)printf("test_kv_oh_lite: %d checks, %d failures\n", g_checks, g_failures); | ||
| 142 | + return (g_failures == 0) ? 0 : 1; | ||
| 143 | +} | ||
| @@ -0,0 +1,49 @@ | |||
| 1 | +#!/bin/bash | ||
| 2 | +# Build and run WiFi Only /onlinenotify host tests outside the source tree. | ||
| 3 | +set -uo pipefail | ||
| 4 | + | ||
| 5 | +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) | ||
| 6 | +IOTC_ROOT=$(cd "$SCRIPT_DIR/../../.." && pwd) | ||
| 7 | +if [ -n "${IOTC_TEST_OHOS_ROOT:-}" ]; then | ||
| 8 | + OHOS_ROOT=$(cd "$IOTC_TEST_OHOS_ROOT" && pwd) | ||
| 9 | +else | ||
| 10 | + OHOS_ROOT=$(cd "$IOTC_ROOT/../../.." && pwd) | ||
| 11 | +fi | ||
| 12 | +OUT_DIR=$(mktemp -d /tmp/iotc_online_notify_host_test.XXXXXX) | ||
| 13 | +trap 'rm -rf "$OUT_DIR"' EXIT | ||
| 14 | + | ||
| 15 | +CFLAGS=( | ||
| 16 | + -std=c99 -Wall -Wextra -Werror -Wno-sign-compare -g -O0 | ||
| 17 | + -DIOTC_CONF_WIFI_ONLY_SUPPORT=1 | ||
| 18 | + -DIOTC_CONF_WIFI_SUPPORT=1 | ||
| 19 | + -DIOTC_ONECONNECT_VERSION_CODE=3 | ||
| 20 | + -I"$SCRIPT_DIR" | ||
| 21 | + -I"$SCRIPT_DIR/stubs" | ||
| 22 | + -I"$IOTC_ROOT/test/unit/device_config_host" | ||
| 23 | + -I"$IOTC_ROOT/test/unit/device_config_host/stubs" | ||
| 24 | + -I"$IOTC_ROOT/adapter/include" | ||
| 25 | + -I"$IOTC_ROOT/interfaces/kits/common" | ||
| 26 | + -I"$IOTC_ROOT/core/infrastructure/define" | ||
| 27 | + -I"$IOTC_ROOT/core/infrastructure/utils/include" | ||
| 28 | + -I"$IOTC_ROOT/core/infrastructure/security/include" | ||
| 29 | + -I"$IOTC_ROOT/core/infrastructure/schedule/include" | ||
| 30 | + -I"$IOTC_ROOT/core/infrastructure/frameworks/event_loop/include" | ||
| 31 | + -I"$IOTC_ROOT/core/device/config/include" | ||
| 32 | + -I"$IOTC_ROOT/core/wifi/application/connect" | ||
| 33 | + -I"$IOTC_ROOT/core/wifi/infrastructure/transport/coap/codec" | ||
| 34 | + -I"$IOTC_ROOT/core/wifi/infrastructure/transport/coap/codec/include" | ||
| 35 | + -I"$OHOS_ROOT/third_party/cJSON" | ||
| 36 | +) | ||
| 37 | + | ||
| 38 | +SOURCES=( | ||
| 39 | + "$SCRIPT_DIR/test_online_notify.c" | ||
| 40 | + "$SCRIPT_DIR/stubs/online_notify_deps_stub.c" | ||
| 41 | + "$IOTC_ROOT/core/wifi/application/connect/svc_conn_online_notify.c" | ||
| 42 | + "$IOTC_ROOT/core/wifi/infrastructure/transport/coap/codec/coap_codec_udp.c" | ||
| 43 | + "$IOTC_ROOT/core/wifi/infrastructure/transport/coap/codec/coap_codec_comm.c" | ||
| 44 | + "$IOTC_ROOT/adapter/json/cjson/iotc_json.c" | ||
| 45 | + "$OHOS_ROOT/third_party/cJSON/cJSON.c" | ||
| 46 | +) | ||
| 47 | + | ||
| 48 | +gcc "${CFLAGS[@]}" "${SOURCES[@]}" -lm -o "$OUT_DIR/test_online_notify" || exit 1 | ||
| 49 | +"$OUT_DIR/test_online_notify" | ||
| @@ -0,0 +1,239 @@ | |||
| 1 | + | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +static IotcDssProvisionPending g_pending; | ||
| 16 | +static int32_t g_loadResult; | ||
| 17 | +static int32_t g_markResult; | ||
| 18 | +static int32_t g_randomResult; | ||
| 19 | +static int32_t g_socketResult; | ||
| 20 | +static int32_t g_socketOptResult; | ||
| 21 | +static int32_t g_timerResult; | ||
| 22 | +static uint32_t g_sendFailures; | ||
| 23 | + | ||
| 24 | +static uint32_t g_socketCalls; | ||
| 25 | +static uint32_t g_sendCalls; | ||
| 26 | +static uint32_t g_markCalls; | ||
| 27 | +static uint32_t g_closeCalls; | ||
| 28 | +static uint32_t g_timerAddCalls; | ||
| 29 | +static uint32_t g_timerRemoveCalls; | ||
| 30 | +static uint32_t g_timerInterval; | ||
| 31 | +static uint16_t g_targetPort; | ||
| 32 | +static uint32_t g_targetAddr; | ||
| 33 | +static uint8_t g_lastPacket[STUB_PACKET_MAX_LEN]; | ||
| 34 | +static uint32_t g_lastPacketLen; | ||
| 35 | +static EventSourceTimerCallback g_timerCallback; | ||
| 36 | +static void *g_timerUserData; | ||
| 37 | + | ||
| 38 | +void OnlineNotifyStubReset(void) | ||
| 39 | +{ | ||
| 40 | + (void)memset_s(&g_pending, sizeof(g_pending), 0, sizeof(g_pending)); | ||
| 41 | + (void)strcpy_s(g_pending.tempDevId, sizeof(g_pending.tempDevId), "temp-dev-0001"); | ||
| 42 | + g_pending.mdidLen = 1; | ||
| 43 | + g_pending.state = IOTC_DSS_PENDING_STATE_WAIT_ACTIVATE; | ||
| 44 | + g_loadResult = IOTC_OK; | ||
| 45 | + g_markResult = IOTC_OK; | ||
| 46 | + g_randomResult = IOTC_OK; | ||
| 47 | + g_socketResult = 27; | ||
| 48 | + g_socketOptResult = IOTC_OK; | ||
| 49 | + g_timerResult = 91; | ||
| 50 | + g_sendFailures = 0; | ||
| 51 | + g_socketCalls = 0; | ||
| 52 | + g_sendCalls = 0; | ||
| 53 | + g_markCalls = 0; | ||
| 54 | + g_closeCalls = 0; | ||
| 55 | + g_timerAddCalls = 0; | ||
| 56 | + g_timerRemoveCalls = 0; | ||
| 57 | + g_timerInterval = 0; | ||
| 58 | + g_targetPort = 0; | ||
| 59 | + g_targetAddr = 0; | ||
| 60 | + g_lastPacketLen = 0; | ||
| 61 | + g_timerCallback = NULL; | ||
| 62 | + g_timerUserData = NULL; | ||
| 63 | +} | ||
| 64 | + | ||
| 65 | +void OnlineNotifyStubFireTimer(void) | ||
| 66 | +{ | ||
| 67 | + if (g_timerCallback != NULL) { | ||
| 68 | + g_timerCallback(g_timerResult, g_timerUserData); | ||
| 69 | + } | ||
| 70 | +} | ||
| 71 | + | ||
| 72 | +void OnlineNotifyStubSetLoadResult(int32_t ret) { g_loadResult = ret; } | ||
| 73 | +void OnlineNotifyStubSetPendingDone(uint8_t done) { g_pending.onlineNotifyDone = done; } | ||
| 74 | +void OnlineNotifyStubSetSendFailures(uint32_t count) { g_sendFailures = count; } | ||
| 75 | +void OnlineNotifyStubSetRandomResult(int32_t ret) { g_randomResult = ret; } | ||
| 76 | +void OnlineNotifyStubSetSocketResult(int32_t ret) { g_socketResult = ret; } | ||
| 77 | +void OnlineNotifyStubSetSocketOptResult(int32_t ret) { g_socketOptResult = ret; } | ||
| 78 | +void OnlineNotifyStubSetTimerResult(int32_t ret) { g_timerResult = ret; } | ||
| 79 | +void OnlineNotifyStubSetMarkResult(int32_t ret) { g_markResult = ret; } | ||
| 80 | + | ||
| 81 | +uint32_t OnlineNotifyStubGetSocketCalls(void) { return g_socketCalls; } | ||
| 82 | +uint32_t OnlineNotifyStubGetSendCalls(void) { return g_sendCalls; } | ||
| 83 | +uint32_t OnlineNotifyStubGetMarkCalls(void) { return g_markCalls; } | ||
| 84 | +uint32_t OnlineNotifyStubGetCloseCalls(void) { return g_closeCalls; } | ||
| 85 | +uint32_t OnlineNotifyStubGetTimerAddCalls(void) { return g_timerAddCalls; } | ||
| 86 | +uint32_t OnlineNotifyStubGetTimerRemoveCalls(void) { return g_timerRemoveCalls; } | ||
| 87 | +uint32_t OnlineNotifyStubGetTimerInterval(void) { return g_timerInterval; } | ||
| 88 | +uint16_t OnlineNotifyStubGetTargetPort(void) { return g_targetPort; } | ||
| 89 | +uint32_t OnlineNotifyStubGetTargetAddr(void) { return g_targetAddr; } | ||
| 90 | +uint8_t OnlineNotifyStubGetPendingDone(void) { return g_pending.onlineNotifyDone; } | ||
| 91 | + | ||
| 92 | +const uint8_t *OnlineNotifyStubGetLastPacket(uint32_t *len) | ||
| 93 | +{ | ||
| 94 | + if (len != NULL) { | ||
| 95 | + *len = g_lastPacketLen; | ||
| 96 | + } | ||
| 97 | + return g_lastPacket; | ||
| 98 | +} | ||
| 99 | + | ||
| 100 | +int32_t IotcDssLoadProvisionPending(IotcDssProvisionPending *pending) | ||
| 101 | +{ | ||
| 102 | + if (g_loadResult == IOTC_OK && pending != NULL) { | ||
| 103 | + (void)memcpy_s(pending, sizeof(*pending), &g_pending, sizeof(g_pending)); | ||
| 104 | + } | ||
| 105 | + return g_loadResult; | ||
| 106 | +} | ||
| 107 | + | ||
| 108 | +int32_t IotcDssMarkOnlineNotifyDone(void) | ||
| 109 | +{ | ||
| 110 | + ++g_markCalls; | ||
| 111 | + if (g_markResult == IOTC_OK) { | ||
| 112 | + g_pending.onlineNotifyDone = 1; | ||
| 113 | + } | ||
| 114 | + return g_markResult; | ||
| 115 | +} | ||
| 116 | + | ||
| 117 | +int32_t IotcMdCalc(IotcMdType type, const uint8_t *inData, uint32_t inLen, uint8_t *md, uint32_t mdLen) | ||
| 118 | +{ | ||
| 119 | + if (type != IOTC_MD_SHA256 || inData == NULL || inLen == 0 || md == NULL || | ||
| 120 | + mdLen != IOTC_MD_SHA256_BYTE_LEN) { | ||
| 121 | + return IOTC_ERR_PARAM_INVALID; | ||
| 122 | + } | ||
| 123 | + for (uint32_t i = 0; i < mdLen; ++i) { | ||
| 124 | + md[i] = (uint8_t)i; | ||
| 125 | + } | ||
| 126 | + return IOTC_OK; | ||
| 127 | +} | ||
| 128 | + | ||
| 129 | +bool UtilsHexify(const uint8_t *inBuf, uint32_t inBufLen, char *outBuf, uint32_t outBufLen) | ||
| 130 | +{ | ||
| 131 | + static const char HEX[] = "0123456789abcdef"; | ||
| 132 | + if (inBuf == NULL || outBuf == NULL || outBufLen < HEXIFY_LEN(inBufLen)) { | ||
| 133 | + return false; | ||
| 134 | + } | ||
| 135 | + for (uint32_t i = 0; i < inBufLen; ++i) { | ||
| 136 | + outBuf[i * 2] = HEX[inBuf[i] >> 4]; | ||
| 137 | + outBuf[i * 2 + 1] = HEX[inBuf[i] & 0x0F]; | ||
| 138 | + } | ||
| 139 | + return true; | ||
| 140 | +} | ||
| 141 | + | ||
| 142 | +int32_t SecurityRandom(uint8_t *buf, uint32_t bufLen) | ||
| 143 | +{ | ||
| 144 | + if (g_randomResult != IOTC_OK) { | ||
| 145 | + return g_randomResult; | ||
| 146 | + } | ||
| 147 | + if (buf == NULL || bufLen != sizeof(uint16_t)) { | ||
| 148 | + return IOTC_ERR_PARAM_INVALID; | ||
| 149 | + } | ||
| 150 | + uint16_t msgId = 0x1234; | ||
| 151 | + (void)memcpy_s(buf, bufLen, &msgId, sizeof(msgId)); | ||
| 152 | + return IOTC_OK; | ||
| 153 | +} | ||
| 154 | + | ||
| 155 | +int32_t IotcSocket(IotcSocketDomain domain, IotcSocketType type, IotcSocketProto proto) | ||
| 156 | +{ | ||
| 157 | + ++g_socketCalls; | ||
| 158 | + if (domain != IOTC_SOCKET_DOMAIN_AF_INET || type != IOTC_SOCKET_TYPE_DGRAM || | ||
| 159 | + proto != IOTC_SOCKET_PROTO_UDP) { | ||
| 160 | + return IOTC_ERR_PARAM_INVALID; | ||
| 161 | + } | ||
| 162 | + return g_socketResult; | ||
| 163 | +} | ||
| 164 | + | ||
| 165 | +void IotcClose(int32_t fd) | ||
| 166 | +{ | ||
| 167 | + if (fd >= 0) { | ||
| 168 | + ++g_closeCalls; | ||
| 169 | + } | ||
| 170 | +} | ||
| 171 | + | ||
| 172 | +int32_t IotcSetSocketOpt(int32_t fd, IotcSocketOption option, const void *value, uint32_t len) | ||
| 173 | +{ | ||
| 174 | + (void)value; | ||
| 175 | + if (fd != g_socketResult || option != IOTC_SOCKET_OPTION_ENABLE_BROADCAST || len != 0) { | ||
| 176 | + return IOTC_ERR_PARAM_INVALID; | ||
| 177 | + } | ||
| 178 | + return g_socketOptResult; | ||
| 179 | +} | ||
| 180 | + | ||
| 181 | +int32_t IotcSendTo(int32_t fd, const uint8_t *buf, uint32_t len, const IotcSockaddr *to, uint32_t toLen) | ||
| 182 | +{ | ||
| 183 | + ++g_sendCalls; | ||
| 184 | + if (fd != g_socketResult || buf == NULL || len > sizeof(g_lastPacket) || to == NULL || | ||
| 185 | + toLen != sizeof(IotcSockaddrIn)) { | ||
| 186 | + return IOTC_ERR_PARAM_INVALID; | ||
| 187 | + } | ||
| 188 | + const IotcSockaddrIn *target = (const IotcSockaddrIn *)to; | ||
| 189 | + g_targetPort = target->sinPort; | ||
| 190 | + g_targetAddr = target->sinAddr; | ||
| 191 | + (void)memcpy_s(g_lastPacket, sizeof(g_lastPacket), buf, len); | ||
| 192 | + g_lastPacketLen = len; | ||
| 193 | + if (g_sendCalls <= g_sendFailures) { | ||
| 194 | + return -77; | ||
| 195 | + } | ||
| 196 | + return (int32_t)len; | ||
| 197 | +} | ||
| 198 | + | ||
| 199 | +uint16_t IotcHtons(uint16_t value) | ||
| 200 | +{ | ||
| 201 | + return (uint16_t)((value << 8) | (value >> 8)); | ||
| 202 | +} | ||
| 203 | + | ||
| 204 | +uint16_t IotcNtohs(uint16_t value) | ||
| 205 | +{ | ||
| 206 | + return IotcHtons(value); | ||
| 207 | +} | ||
| 208 | + | ||
| 209 | +uint32_t IotcNtohl(uint32_t value) | ||
| 210 | +{ | ||
| 211 | + return ((value & 0x000000FFU) << 24) | ((value & 0x0000FF00U) << 8) | | ||
| 212 | + ((value & 0x00FF0000U) >> 8) | ((value & 0xFF000000U) >> 24); | ||
| 213 | +} | ||
| 214 | + | ||
| 215 | +uint32_t IotcInetAddr(const char *ip) | ||
| 216 | +{ | ||
| 217 | + return (ip != NULL && strcmp(ip, "255.255.255.255") == 0) ? UINT32_MAX : 0; | ||
| 218 | +} | ||
| 219 | + | ||
| 220 | +int32_t SchedTimerAdd(EventSourceTimerType type, EventSourceTimerCallback cb, uint32_t ms, void *userData) | ||
| 221 | +{ | ||
| 222 | + ++g_timerAddCalls; | ||
| 223 | + if (type != EVENT_SOURCE_TIMER_TYPE_REPEAT || cb == NULL) { | ||
| 224 | + return IOTC_ERR_PARAM_INVALID; | ||
| 225 | + } | ||
| 226 | + g_timerCallback = cb; | ||
| 227 | + g_timerUserData = userData; | ||
| 228 | + g_timerInterval = ms; | ||
| 229 | + return g_timerResult; | ||
| 230 | +} | ||
| 231 | + | ||
| 232 | +void SchedTimerRemove(int32_t id) | ||
| 233 | +{ | ||
| 234 | + if (id >= 0) { | ||
| 235 | + ++g_timerRemoveCalls; | ||
| 236 | + } | ||
| 237 | + g_timerCallback = NULL; | ||
| 238 | + g_timerUserData = NULL; | ||
| 239 | +} | ||
| @@ -0,0 +1,31 @@ | |||
| 1 | + | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | +void OnlineNotifyStubReset(void); | ||
| 9 | +void OnlineNotifyStubFireTimer(void); | ||
| 10 | +void OnlineNotifyStubSetLoadResult(int32_t ret); | ||
| 11 | +void OnlineNotifyStubSetPendingDone(uint8_t done); | ||
| 12 | +void OnlineNotifyStubSetSendFailures(uint32_t count); | ||
| 13 | +void OnlineNotifyStubSetRandomResult(int32_t ret); | ||
| 14 | +void OnlineNotifyStubSetSocketResult(int32_t ret); | ||
| 15 | +void OnlineNotifyStubSetSocketOptResult(int32_t ret); | ||
| 16 | +void OnlineNotifyStubSetTimerResult(int32_t ret); | ||
| 17 | +void OnlineNotifyStubSetMarkResult(int32_t ret); | ||
| 18 | + | ||
| 19 | +uint32_t OnlineNotifyStubGetSocketCalls(void); | ||
| 20 | +uint32_t OnlineNotifyStubGetSendCalls(void); | ||
| 21 | +uint32_t OnlineNotifyStubGetMarkCalls(void); | ||
| 22 | +uint32_t OnlineNotifyStubGetCloseCalls(void); | ||
| 23 | +uint32_t OnlineNotifyStubGetTimerAddCalls(void); | ||
| 24 | +uint32_t OnlineNotifyStubGetTimerRemoveCalls(void); | ||
| 25 | +uint32_t OnlineNotifyStubGetTimerInterval(void); | ||
| 26 | +const uint8_t *OnlineNotifyStubGetLastPacket(uint32_t *len); | ||
| 27 | +uint16_t OnlineNotifyStubGetTargetPort(void); | ||
| 28 | +uint32_t OnlineNotifyStubGetTargetAddr(void); | ||
| 29 | +uint8_t OnlineNotifyStubGetPendingDone(void); | ||
| 30 | + | ||
| 31 | + | ||
| @@ -0,0 +1,180 @@ | |||
| 1 | + | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | +int g_testChecks; | ||
| 14 | +int g_testFailures; | ||
| 15 | + | ||
| 16 | +static const char *EXPECTED_HASH = | ||
| 17 | + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"; | ||
| 18 | + | ||
| 19 | +static void TestPacket(void) | ||
| 20 | +{ | ||
| 21 | + uint8_t packet[160] = {0}; | ||
| 22 | + uint32_t packetLen = sizeof(packet); | ||
| 23 | + CHECK_EQ_INT(IOTC_OK, SvcConnBuildOnlineNotifyPacket("temp-dev-0001", 0x1234, packet, &packetLen)); | ||
| 24 | + CHECK(packetLen > 4); | ||
| 25 | + CHECK_EQ_INT(0x40, packet[0]); /* CoAP v1, CON, TKL=0 */ | ||
| 26 | + CHECK_EQ_INT(COAP_METHOD_TYPE_POST, packet[1]); | ||
| 27 | + CHECK_EQ_INT(0x12, packet[2]); | ||
| 28 | + CHECK_EQ_INT(0x34, packet[3]); | ||
| 29 | + | ||
| 30 | + CoapPacket decoded = {0}; | ||
| 31 | + CoapData raw = { packet, packetLen }; | ||
| 32 | + CHECK_EQ_INT(IOTC_OK, CoapUdpDecode(&decoded, &raw)); | ||
| 33 | + CHECK_EQ_INT(COAP_MSG_TYPE_CON, decoded.header.type); | ||
| 34 | + CHECK_EQ_INT(COAP_METHOD_TYPE_POST, decoded.header.code); | ||
| 35 | + CHECK_EQ_INT(0x1234, decoded.header.msgId); | ||
| 36 | + CHECK_EQ_INT(2, decoded.opNum); | ||
| 37 | + CHECK_EQ_INT(COAP_OPTION_TYPE_URI_PATH, decoded.options[0].option); | ||
| 38 | + CHECK_EQ_INT(strlen("onlinenotify"), decoded.options[0].value.len); | ||
| 39 | + CHECK(memcmp(decoded.options[0].value.data, "onlinenotify", strlen("onlinenotify")) == 0); | ||
| 40 | + CHECK_EQ_INT(COAP_OPTION_TYPE_CONTENT_FORMAT, decoded.options[1].option); | ||
| 41 | + CHECK_EQ_INT(1, decoded.options[1].value.len); | ||
| 42 | + CHECK_EQ_INT(50, decoded.options[1].value.data[0]); | ||
| 43 | + | ||
| 44 | + IotcJson *json = IotcJsonParseWithLen((const char *)decoded.payload.data, decoded.payload.len); | ||
| 45 | + CHECK(json != NULL); | ||
| 46 | + IotcJson *devId = IotcJsonGetObj(json, "devId"); | ||
| 47 | + CHECK(IotcJsonIsStr(devId)); | ||
| 48 | + CHECK(strcmp(IotcJsonGetStr(devId), EXPECTED_HASH) == 0); | ||
| 49 | + IotcJsonDelete(json); | ||
| 50 | + | ||
| 51 | + uint32_t shortLen = 4; | ||
| 52 | + CHECK_EQ_INT(IOTC_CORE_WIFI_TRANS_ERR_COAP_CODEC_BUFFER_SHORT, | ||
| 53 | + SvcConnBuildOnlineNotifyPacket("temp-dev-0001", 1, packet, &shortLen)); | ||
| 54 | + CHECK_EQ_INT(IOTC_ERR_PARAM_INVALID, SvcConnBuildOnlineNotifyPacket(NULL, 1, packet, &packetLen)); | ||
| 55 | + CHECK_EQ_INT(IOTC_ERR_PARAM_INVALID, SvcConnBuildOnlineNotifyPacket("", 1, packet, &packetLen)); | ||
| 56 | + CHECK_EQ_INT(IOTC_ERR_PARAM_INVALID, SvcConnBuildOnlineNotifyPacket("dev", 1, NULL, &packetLen)); | ||
| 57 | + CHECK_EQ_INT(IOTC_ERR_PARAM_INVALID, SvcConnBuildOnlineNotifyPacket("dev", 1, packet, NULL)); | ||
| 58 | +} | ||
| 59 | + | ||
| 60 | +static void TestFiveSuccessfulSends(void) | ||
| 61 | +{ | ||
| 62 | + OnlineNotifyStubReset(); | ||
| 63 | + CHECK_EQ_INT(IOTC_OK, SvcConnOnlineNotifyStart()); | ||
| 64 | + CHECK_EQ_INT(1, OnlineNotifyStubGetSocketCalls()); | ||
| 65 | + CHECK_EQ_INT(1, OnlineNotifyStubGetSendCalls()); | ||
| 66 | + CHECK_EQ_INT(1, OnlineNotifyStubGetTimerAddCalls()); | ||
| 67 | + CHECK_EQ_INT(100, OnlineNotifyStubGetTimerInterval()); | ||
| 68 | + CHECK_EQ_INT(IotcHtons(5683), OnlineNotifyStubGetTargetPort()); | ||
| 69 | + CHECK_EQ_INT(UINT32_MAX, OnlineNotifyStubGetTargetAddr()); | ||
| 70 | + | ||
| 71 | + /* active 期间重复 Start 幂等,不新建 socket/timer,也不多发。 */ | ||
| 72 | + CHECK_EQ_INT(IOTC_OK, SvcConnOnlineNotifyStart()); | ||
| 73 | + CHECK_EQ_INT(1, OnlineNotifyStubGetSocketCalls()); | ||
| 74 | + CHECK_EQ_INT(1, OnlineNotifyStubGetSendCalls()); | ||
| 75 | + | ||
| 76 | + for (int i = 0; i < 4; ++i) { | ||
| 77 | + OnlineNotifyStubFireTimer(); | ||
| 78 | + } | ||
| 79 | + CHECK_EQ_INT(5, OnlineNotifyStubGetSendCalls()); | ||
| 80 | + CHECK_EQ_INT(1, OnlineNotifyStubGetMarkCalls()); | ||
| 81 | + CHECK_EQ_INT(1, OnlineNotifyStubGetPendingDone()); | ||
| 82 | + CHECK_EQ_INT(1, OnlineNotifyStubGetTimerRemoveCalls()); | ||
| 83 | + CHECK_EQ_INT(1, OnlineNotifyStubGetCloseCalls()); | ||
| 84 | + | ||
| 85 | + uint32_t packetLen = 0; | ||
| 86 | + const uint8_t *packet = OnlineNotifyStubGetLastPacket(&packetLen); | ||
| 87 | + CHECK(packet != NULL && packetLen > 4); | ||
| 88 | + CHECK_EQ_INT(0x40, packet[0]); | ||
| 89 | +} | ||
| 90 | + | ||
| 91 | +static void TestSkipAndRetryLimit(void) | ||
| 92 | +{ | ||
| 93 | + OnlineNotifyStubReset(); | ||
| 94 | + OnlineNotifyStubSetPendingDone(1); | ||
| 95 | + CHECK_EQ_INT(IOTC_OK, SvcConnOnlineNotifyStart()); | ||
| 96 | + CHECK_EQ_INT(0, OnlineNotifyStubGetSocketCalls()); | ||
| 97 | + | ||
| 98 | + OnlineNotifyStubReset(); | ||
| 99 | + OnlineNotifyStubSetLoadResult(IOTC_CORE_DEVICE_ERR_STORE_NOT_FOUND); | ||
| 100 | + CHECK_EQ_INT(IOTC_OK, SvcConnOnlineNotifyStart()); | ||
| 101 | + CHECK_EQ_INT(0, OnlineNotifyStubGetSocketCalls()); | ||
| 102 | + | ||
| 103 | + OnlineNotifyStubReset(); | ||
| 104 | + OnlineNotifyStubSetSendFailures(10); | ||
| 105 | + CHECK_EQ_INT(IOTC_OK, SvcConnOnlineNotifyStart()); | ||
| 106 | + for (int i = 0; i < 9; ++i) { | ||
| 107 | + OnlineNotifyStubFireTimer(); | ||
| 108 | + } | ||
| 109 | + CHECK_EQ_INT(10, OnlineNotifyStubGetSendCalls()); | ||
| 110 | + CHECK_EQ_INT(0, OnlineNotifyStubGetMarkCalls()); | ||
| 111 | + CHECK_EQ_INT(1, OnlineNotifyStubGetTimerRemoveCalls()); | ||
| 112 | + CHECK_EQ_INT(1, OnlineNotifyStubGetCloseCalls()); | ||
| 113 | +} | ||
| 114 | + | ||
| 115 | +static void TestFailureCleanupAndStop(void) | ||
| 116 | +{ | ||
| 117 | + OnlineNotifyStubReset(); | ||
| 118 | + OnlineNotifyStubSetRandomResult(-31); | ||
| 119 | + CHECK_EQ_INT(-31, SvcConnOnlineNotifyStart()); | ||
| 120 | + CHECK_EQ_INT(0, OnlineNotifyStubGetSocketCalls()); | ||
| 121 | + | ||
| 122 | + OnlineNotifyStubReset(); | ||
| 123 | + OnlineNotifyStubSetSocketResult(-32); | ||
| 124 | + CHECK_EQ_INT(-32, SvcConnOnlineNotifyStart()); | ||
| 125 | + CHECK_EQ_INT(1, OnlineNotifyStubGetSocketCalls()); | ||
| 126 | + CHECK_EQ_INT(0, OnlineNotifyStubGetCloseCalls()); | ||
| 127 | + | ||
| 128 | + OnlineNotifyStubReset(); | ||
| 129 | + OnlineNotifyStubSetSocketOptResult(-33); | ||
| 130 | + CHECK_EQ_INT(-33, SvcConnOnlineNotifyStart()); | ||
| 131 | + CHECK_EQ_INT(1, OnlineNotifyStubGetCloseCalls()); | ||
| 132 | + | ||
| 133 | + OnlineNotifyStubReset(); | ||
| 134 | + OnlineNotifyStubSetTimerResult(-34); | ||
| 135 | + CHECK_EQ_INT(-34, SvcConnOnlineNotifyStart()); | ||
| 136 | + CHECK_EQ_INT(1, OnlineNotifyStubGetSendCalls()); | ||
| 137 | + CHECK_EQ_INT(1, OnlineNotifyStubGetCloseCalls()); | ||
| 138 | + | ||
| 139 | + OnlineNotifyStubReset(); | ||
| 140 | + CHECK_EQ_INT(IOTC_OK, SvcConnOnlineNotifyStart()); | ||
| 141 | + SvcConnOnlineNotifyStop(); | ||
| 142 | + CHECK_EQ_INT(1, OnlineNotifyStubGetTimerRemoveCalls()); | ||
| 143 | + CHECK_EQ_INT(1, OnlineNotifyStubGetCloseCalls()); | ||
| 144 | + SvcConnOnlineNotifyStop(); | ||
| 145 | + CHECK_EQ_INT(1, OnlineNotifyStubGetTimerRemoveCalls()); | ||
| 146 | + CHECK_EQ_INT(1, OnlineNotifyStubGetCloseCalls()); | ||
| 147 | +} | ||
| 148 | + | ||
| 149 | +static void TestMarkFailureAllowsRestart(void) | ||
| 150 | +{ | ||
| 151 | + OnlineNotifyStubReset(); | ||
| 152 | + OnlineNotifyStubSetMarkResult(-41); | ||
| 153 | + CHECK_EQ_INT(IOTC_OK, SvcConnOnlineNotifyStart()); | ||
| 154 | + for (int i = 0; i < 4; ++i) { | ||
| 155 | + OnlineNotifyStubFireTimer(); | ||
| 156 | + } | ||
| 157 | + CHECK_EQ_INT(5, OnlineNotifyStubGetSendCalls()); | ||
| 158 | + CHECK_EQ_INT(1, OnlineNotifyStubGetMarkCalls()); | ||
| 159 | + CHECK_EQ_INT(0, OnlineNotifyStubGetPendingDone()); | ||
| 160 | + CHECK_EQ_INT(1, OnlineNotifyStubGetCloseCalls()); | ||
| 161 | + | ||
| 162 | + /* 持久化失败后保持未完成,下一次 IP ready 可重新执行完整五次。 */ | ||
| 163 | + OnlineNotifyStubSetMarkResult(IOTC_OK); | ||
| 164 | + CHECK_EQ_INT(IOTC_OK, SvcConnOnlineNotifyStart()); | ||
| 165 | + CHECK_EQ_INT(2, OnlineNotifyStubGetSocketCalls()); | ||
| 166 | +} | ||
| 167 | + | ||
| 168 | +int main(void) | ||
| 169 | +{ | ||
| 170 | + setbuf(stdout, NULL); | ||
| 171 | + OnlineNotifyStubReset(); | ||
| 172 | + TestPacket(); | ||
| 173 | + TestFiveSuccessfulSends(); | ||
| 174 | + TestSkipAndRetryLimit(); | ||
| 175 | + TestFailureCleanupAndStop(); | ||
| 176 | + TestMarkFailureAllowsRestart(); | ||
| 177 | + SvcConnOnlineNotifyStop(); | ||
| 178 | + printf("test_online_notify: %d checks, %d failures\n", g_testChecks, g_testFailures); | ||
| 179 | + return (g_testFailures == 0) ? 0 : 1; | ||
| 180 | +} | ||
| @@ -0,0 +1,25 @@ | |||
| 1 | +#!/bin/bash | ||
| 2 | +# Build and run the security-config factory-reset regression outside the source tree. | ||
| 3 | +set -uo pipefail | ||
| 4 | + | ||
| 5 | +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) | ||
| 6 | +IOTC_ROOT=$(cd "$SCRIPT_DIR/../../.." && pwd) | ||
| 7 | +OUT_DIR=$(mktemp -d /tmp/iotc_security_config_host_test.XXXXXX) | ||
| 8 | +trap 'rm -rf "$OUT_DIR"' EXIT | ||
| 9 | + | ||
| 10 | +gcc \ | ||
| 11 | + -std=c99 -Wall -Wextra -Werror -g -O0 -ffunction-sections -fdata-sections \ | ||
| 12 | + -I"$IOTC_ROOT/test/unit/device_config_host/stubs" \ | ||
| 13 | + -I"$IOTC_ROOT/adapter/include" \ | ||
| 14 | + -I"$IOTC_ROOT/interfaces/kits/common" \ | ||
| 15 | + -I"$IOTC_ROOT/interfaces/kits/oh_connect" \ | ||
| 16 | + -I"$IOTC_ROOT/core/infrastructure/define" \ | ||
| 17 | + -I"$IOTC_ROOT/core/infrastructure/security/include" \ | ||
| 18 | + -I"$IOTC_ROOT/core/infrastructure/service/include" \ | ||
| 19 | + -I"$IOTC_ROOT/core/infrastructure/utils/include" \ | ||
| 20 | + -I"$IOTC_ROOT/core/security/config/include" \ | ||
| 21 | + "$SCRIPT_DIR/test_security_config_reset.c" \ | ||
| 22 | + "$IOTC_ROOT/core/security/config/sec_conf_info.c" \ | ||
| 23 | + -Wl,--gc-sections -o "$OUT_DIR/test_security_config_reset" || exit 1 | ||
| 24 | + | ||
| 25 | +"$OUT_DIR/test_security_config_reset" | ||
| @@ -0,0 +1,115 @@ | |||
| 1 | + | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | +static int g_checks; | ||
| 11 | +static int g_failures; | ||
| 12 | +static int g_deleteCalls; | ||
| 13 | +static int32_t g_deleteResult; | ||
| 14 | +static int32_t g_deletedKey; | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + ++g_checks; \ | ||
| 18 | + if (!(expr)) { \ | ||
| 19 | + ++g_failures; \ | ||
| 20 | + fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, | ||
| 21 | + } \ | ||
| 22 | +} while (0) | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + int32_t expected_ = (expected); \ | ||
| 26 | + int32_t actual_ = (actual); \ | ||
| 27 | + ++g_checks; \ | ||
| 28 | + if (expected_ != actual_) { \ | ||
| 29 | + ++g_failures; \ | ||
| 30 | + fprintf(stderr, "FAIL %s:%d expected=%d actual=%d\n", __FILE__, __LINE__, expected_, actual_); \ | ||
| 31 | + } \ | ||
| 32 | +} while (0) | ||
| 33 | + | ||
| 34 | +int32_t SecurityStoreRegStoreList(SecurityStoreItem *list, uint32_t num) | ||
| 35 | +{ | ||
| 36 | + return (list != NULL && num == 1U) ? IOTC_OK : IOTC_ERR_PARAM_INVALID; | ||
| 37 | +} | ||
| 38 | + | ||
| 39 | +void SecurityStoreUnregStoreList(SecurityStoreItem *list) | ||
| 40 | +{ | ||
| 41 | + (void)list; | ||
| 42 | +} | ||
| 43 | + | ||
| 44 | +int32_t SecurityStorePull(int32_t key) | ||
| 45 | +{ | ||
| 46 | + return key == IOTC_STORE_KEY_INT_SECURITY_CONFIG ? IOTC_OK : IOTC_ERR_PARAM_INVALID; | ||
| 47 | +} | ||
| 48 | + | ||
| 49 | +int32_t SecurityStoreDel(int32_t key) | ||
| 50 | +{ | ||
| 51 | + ++g_deleteCalls; | ||
| 52 | + g_deletedKey = key; | ||
| 53 | + return g_deleteResult; | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | +static void Reset(void) | ||
| 57 | +{ | ||
| 58 | + g_deleteCalls = 0; | ||
| 59 | + g_deleteResult = IOTC_OK; | ||
| 60 | + g_deletedKey = -1; | ||
| 61 | +} | ||
| 62 | + | ||
| 63 | +static void TestFactoryResetClearsMemoryAndPersistentRecord(void) | ||
| 64 | +{ | ||
| 65 | + Reset(); | ||
| 66 | + CHECK_EQ(IOTC_OK, SecConfInfoInit()); | ||
| 67 | + | ||
| 68 | + static const char domain[] = "domain-1"; | ||
| 69 | + uint16_t pskNum = 1; | ||
| 70 | + SecPsk psk = {0}; | ||
| 71 | + (void)strcpy(psk.pskId, "A001"); | ||
| 72 | + psk.psk[0] = 0xA5; | ||
| 73 | + CHECK_EQ(IOTC_OK, SecConfInfoSet(SEC_CONF_INFO_KEY_DOMAIN_ID, (const uint8_t *)domain, sizeof(domain))); | ||
| 74 | + CHECK_EQ(IOTC_OK, SecConfInfoSet(SEC_CONF_INFO_KEY_TERMINAL_PSK_NUM, (const uint8_t *)&pskNum, sizeof(pskNum))); | ||
| 75 | + CHECK_EQ(IOTC_OK, SecConfInfoSet(SEC_CONF_INFO_KEY_TERMINAL_PSKS, (const uint8_t *)&psk, sizeof(psk))); | ||
| 76 | + | ||
| 77 | + CHECK_EQ(IOTC_OK, SecConfInfoFactoryReset()); | ||
| 78 | + CHECK_EQ(1, g_deleteCalls); | ||
| 79 | + CHECK_EQ(IOTC_STORE_KEY_INT_SECURITY_CONFIG, g_deletedKey); | ||
| 80 | + | ||
| 81 | + char readDomain[DOMAIN_ID_MAX_LEN + 1] = {0}; | ||
| 82 | + uint32_t domainLen = sizeof(readDomain); | ||
| 83 | + CHECK_EQ(IOTC_OK, SecConfInfoGet(SEC_CONF_INFO_KEY_DOMAIN_ID, (uint8_t *)readDomain, &domainLen)); | ||
| 84 | + CHECK(readDomain[0] == '\0'); | ||
| 85 | + pskNum = 1; | ||
| 86 | + uint32_t pskNumLen = sizeof(pskNum); | ||
| 87 | + CHECK_EQ(IOTC_OK, SecConfInfoGet(SEC_CONF_INFO_KEY_TERMINAL_PSK_NUM, (uint8_t *)&pskNum, &pskNumLen)); | ||
| 88 | + CHECK_EQ(0, pskNum); | ||
| 89 | + | ||
| 90 | + SecConfInfoDeinit(); | ||
| 91 | +} | ||
| 92 | + | ||
| 93 | +static void TestFactoryResetWipesMemoryWhenPersistentDeleteFails(void) | ||
| 94 | +{ | ||
| 95 | + Reset(); | ||
| 96 | + CHECK_EQ(IOTC_OK, SecConfInfoInit()); | ||
| 97 | + static const char domain[] = "domain-2"; | ||
| 98 | + CHECK_EQ(IOTC_OK, SecConfInfoSet(SEC_CONF_INFO_KEY_DOMAIN_ID, (const uint8_t *)domain, sizeof(domain))); | ||
| 99 | + g_deleteResult = -79; | ||
| 100 | + CHECK_EQ(-79, SecConfInfoFactoryReset()); | ||
| 101 | + | ||
| 102 | + char readDomain[DOMAIN_ID_MAX_LEN + 1] = {0}; | ||
| 103 | + uint32_t domainLen = sizeof(readDomain); | ||
| 104 | + CHECK_EQ(IOTC_OK, SecConfInfoGet(SEC_CONF_INFO_KEY_DOMAIN_ID, (uint8_t *)readDomain, &domainLen)); | ||
| 105 | + CHECK(readDomain[0] == '\0'); | ||
| 106 | + SecConfInfoDeinit(); | ||
| 107 | +} | ||
| 108 | + | ||
| 109 | +int main(void) | ||
| 110 | +{ | ||
| 111 | + TestFactoryResetClearsMemoryAndPersistentRecord(); | ||
| 112 | + TestFactoryResetWipesMemoryWhenPersistentDeleteFails(); | ||
| 113 | + printf("security config reset host: %d checks, %d failures\n", g_checks, g_failures); | ||
| 114 | + return g_failures == 0 ? 0 : 1; | ||
| 115 | +} | ||
| @@ -0,0 +1,124 @@ | |||
| 1 | +#!/bin/bash | ||
| 2 | +# Unified Connect 3.0 设备 SoftAP 配网 host 测试。 | ||
| 3 | +# 只用本机 gcc,不依赖 OpenHarmony 构建系统,不在源码树内留下生成物。 | ||
| 4 | +set -uo pipefail | ||
| 5 | + | ||
| 6 | +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) | ||
| 7 | +IOTC_ROOT=$(cd "$SCRIPT_DIR/../../.." && pwd) | ||
| 8 | +if [ -n "${IOTC_TEST_OHOS_ROOT:-}" ]; then | ||
| 9 | + OHOS_ROOT=$(cd "$IOTC_TEST_OHOS_ROOT" && pwd) | ||
| 10 | +else | ||
| 11 | + OHOS_ROOT=$(cd "$IOTC_ROOT/../../.." && pwd) | ||
| 12 | +fi | ||
| 13 | +SHARED_STUBS="$IOTC_ROOT/test/unit/device_config_host/stubs" | ||
| 14 | +OUT_DIR=$(mktemp -d /tmp/iotc_softap_host_test.XXXXXX) | ||
| 15 | +trap 'rm -rf "$OUT_DIR"' EXIT | ||
| 16 | + | ||
| 17 | +CFLAGS=( | ||
| 18 | + -std=c99 -Wall -Wextra -Werror -g -O0 | ||
| 19 | + -I"$SCRIPT_DIR/stubs" | ||
| 20 | + -I"$SHARED_STUBS" | ||
| 21 | + -I"$IOTC_ROOT/test/unit/device_config_host" | ||
| 22 | + -I"$IOTC_ROOT/adapter/include" | ||
| 23 | + -I"$IOTC_ROOT/interfaces/kits/common" | ||
| 24 | + -I"$IOTC_ROOT/interfaces/kits/oh_connect" | ||
| 25 | + -I"$IOTC_ROOT/core/infrastructure/utils/include" | ||
| 26 | + -I"$IOTC_ROOT/core/infrastructure/define" | ||
| 27 | + -I"$IOTC_ROOT/core/infrastructure/product/include" | ||
| 28 | + -I"$IOTC_ROOT/core/infrastructure/service/include" | ||
| 29 | + -I"$IOTC_ROOT/core/infrastructure/security/include" | ||
| 30 | + -I"$IOTC_ROOT/core/infrastructure/schedule/include" | ||
| 31 | + -I"$IOTC_ROOT/core/device/model/include" | ||
| 32 | + -I"$IOTC_ROOT/core/device/config/include" | ||
| 33 | + -I"$IOTC_ROOT/core/infrastructure/frameworks/event_loop/include" | ||
| 34 | + -I"$IOTC_ROOT/core/infrastructure/frameworks/service/include" | ||
| 35 | + -I"$IOTC_ROOT/core/wifi/application/softap" | ||
| 36 | + -I"$IOTC_ROOT/core/wifi/application/connect" | ||
| 37 | + -I"$IOTC_ROOT/core/wifi/application/softap/include" | ||
| 38 | + -I"$IOTC_ROOT/core/wifi/interfaces/include" | ||
| 39 | + -I"$IOTC_ROOT/core/wifi/domain/e2e_ctl/include" | ||
| 40 | + -I"$IOTC_ROOT/core/wifi/infrastructure/transport/coap/codec/include" | ||
| 41 | + -I"$IOTC_ROOT/core/wifi/infrastructure/transport/coap/endpoint/include" | ||
| 42 | + -I"$IOTC_ROOT/core/wifi/infrastructure/transport/coap/stack/include" | ||
| 43 | + -I"$IOTC_ROOT/core/wifi/infrastructure/transport/session/include" | ||
| 44 | + -I"$IOTC_ROOT/core/wifi/infrastructure/transport/buffer/include" | ||
| 45 | + -I"$IOTC_ROOT/core/wifi/infrastructure/transport/buffer/include" | ||
| 46 | + -I"$IOTC_ROOT/core/wifi/infrastructure/transport/link/include" | ||
| 47 | + -I"$IOTC_ROOT/core/wifi/infrastructure/transport/socket/include" | ||
| 48 | + -I"$IOTC_ROOT/core/wifi/infrastructure/utils/include" | ||
| 49 | + -I"$IOTC_ROOT/core/wifi/infrastructure/transport/coap/endpoint" | ||
| 50 | + -I"$IOTC_ROOT/core/network/transport/buffer/include" | ||
| 51 | + -I"$IOTC_ROOT/core/network/transport/coap/codec/include" | ||
| 52 | + -I"$IOTC_ROOT/core/network/transport/coap/endpoint/include" | ||
| 53 | + -I"$IOTC_ROOT/core/network/transport/coap/endpoint" | ||
| 54 | + -I"$IOTC_ROOT/core/network/transport/link/include" | ||
| 55 | + -I"$IOTC_ROOT/core/network/transport/session/include" | ||
| 56 | + -I"$IOTC_ROOT/core/network/transport/socket/include" | ||
| 57 | + -I"$OHOS_ROOT/third_party/cJSON" | ||
| 58 | +) | ||
| 59 | + | ||
| 60 | +overall=0 | ||
| 61 | + | ||
| 62 | +for version in 3 1; do | ||
| 63 | + echo "build test_softap_name (IOTC_ONECONNECT_VERSION_CODE=$version)" | ||
| 64 | + gcc "${CFLAGS[@]}" -DIOTC_ONECONNECT_VERSION_CODE=$version \ | ||
| 65 | + "$SCRIPT_DIR/test_softap_name.c" \ | ||
| 66 | + "$IOTC_ROOT/sdk/oh_connect/biz/iotc_oh_softap_name.c" \ | ||
| 67 | + "$SCRIPT_DIR/stubs/softap_deps_stub.c" \ | ||
| 68 | + -o "$OUT_DIR/test_softap_name_v$version" || exit 1 | ||
| 69 | + "$OUT_DIR/test_softap_name_v$version" | ||
| 70 | + [ $? -ne 0 ] && overall=1 | ||
| 71 | +done | ||
| 72 | + | ||
| 73 | +echo "build test_coap_client_runtime" | ||
| 74 | +gcc "${CFLAGS[@]}" -ffunction-sections -fdata-sections \ | ||
| 75 | + "$SCRIPT_DIR/test_coap_client_runtime.c" \ | ||
| 76 | + "$IOTC_ROOT/core/wifi/infrastructure/transport/coap/endpoint/coap_endpoint_client.c" \ | ||
| 77 | + "$IOTC_ROOT/core/network/transport/coap/endpoint/net_coap_endpoint_client.c" \ | ||
| 78 | + -Wl,--gc-sections \ | ||
| 79 | + -o "$OUT_DIR/test_coap_client_runtime" || exit 1 | ||
| 80 | +"$OUT_DIR/test_coap_client_runtime" | ||
| 81 | +[ $? -ne 0 ] && overall=1 | ||
| 82 | + | ||
| 83 | +echo "build test_softap_device_info" | ||
| 84 | +gcc "${CFLAGS[@]}" -ffunction-sections -fdata-sections \ | ||
| 85 | + -Wno-unused-parameter \ | ||
| 86 | + -DIOTC_ONECONNECT_VERSION_NAME='"3.0"' \ | ||
| 87 | + "$SCRIPT_DIR/test_softap_device_info.c" \ | ||
| 88 | + "$IOTC_ROOT/core/wifi/application/softap/svc_softap_coap_provision.c" \ | ||
| 89 | + "$IOTC_ROOT/core/device/model/dev_info_mdl.c" \ | ||
| 90 | + "$IOTC_ROOT/core/infrastructure/utils/utils_json.c" \ | ||
| 91 | + "$IOTC_ROOT/adapter/json/cjson/iotc_json.c" \ | ||
| 92 | + "$OHOS_ROOT/third_party/cJSON/cJSON.c" \ | ||
| 93 | + -Wl,--gc-sections \ | ||
| 94 | + -o "$OUT_DIR/test_softap_device_info" || exit 1 | ||
| 95 | +"$OUT_DIR/test_softap_device_info" | ||
| 96 | +[ $? -ne 0 ] && overall=1 | ||
| 97 | + | ||
| 98 | +echo "build test_softap_security_chain" | ||
| 99 | +gcc "${CFLAGS[@]}" -ffunction-sections -fdata-sections \ | ||
| 100 | + "$SCRIPT_DIR/test_softap_security_chain.c" \ | ||
| 101 | + "$IOTC_ROOT/core/wifi/application/softap/svc_softap_coap.c" \ | ||
| 102 | + -Wl,--gc-sections \ | ||
| 103 | + -o "$OUT_DIR/test_softap_security_chain" || exit 1 | ||
| 104 | +"$OUT_DIR/test_softap_security_chain" | ||
| 105 | +[ $? -ne 0 ] && overall=1 | ||
| 106 | + | ||
| 107 | +echo "check getProvisionRequest method" | ||
| 108 | +if ! grep -Fq '{UTILS_BIT(COAP_METHOD_TYPE_POST), STR_URI_GET_PROVISION_REQUEST, NULL,' \ | ||
| 109 | + "$IOTC_ROOT/core/wifi/application/softap/svc_softap_sess.c"; then | ||
| 110 | + echo "getProvisionRequest must use POST" >&2 | ||
| 111 | + overall=1 | ||
| 112 | +fi | ||
| 113 | +if grep -Fq '{UTILS_BIT(COAP_METHOD_TYPE_GET), STR_URI_GET_PROVISION_REQUEST, NULL,' \ | ||
| 114 | + "$IOTC_ROOT/core/wifi/application/softap/svc_softap_sess.c"; then | ||
| 115 | + echo "getProvisionRequest must not use GET" >&2 | ||
| 116 | + overall=1 | ||
| 117 | +fi | ||
| 118 | + | ||
| 119 | +if [ "$overall" -eq 0 ]; then | ||
| 120 | + echo "SOFTAP HOST TESTS PASSED" | ||
| 121 | +else | ||
| 122 | + echo "SOFTAP HOST TESTS FAILED" | ||
| 123 | +fi | ||
| 124 | +exit "$overall" | ||
| @@ -0,0 +1,44 @@ | |||
| 1 | +/* | ||
| 2 | + * Stub implementations of SoftAP SSID/channel dependencies for host tests. | ||
| 3 | + */ | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | +const IotcDeviceInfo *g_testDevInfo; | ||
| 10 | +DevBindStatus g_testBindStatus; | ||
| 11 | +uint8_t g_testDataConfig; | ||
| 12 | +int32_t g_testSecurityRandomRet; | ||
| 13 | +uint32_t g_testSecurityRandomValue; | ||
| 14 | + | ||
| 15 | +const IotcDeviceInfo *ModelGetDevInfo(void) | ||
| 16 | +{ | ||
| 17 | + return g_testDevInfo; | ||
| 18 | +} | ||
| 19 | + | ||
| 20 | +DevBindStatus DevSvcProxyGetBindStatus(void) | ||
| 21 | +{ | ||
| 22 | + return g_testBindStatus; | ||
| 23 | +} | ||
| 24 | + | ||
| 25 | +uint8_t ModelGetDataConfig(void) | ||
| 26 | +{ | ||
| 27 | + return g_testDataConfig; | ||
| 28 | +} | ||
| 29 | + | ||
| 30 | +int32_t SecurityRandom(uint8_t *buf, uint32_t bufLen) | ||
| 31 | +{ | ||
| 32 | + if (g_testSecurityRandomRet != 0) { | ||
| 33 | + return g_testSecurityRandomRet; | ||
| 34 | + } | ||
| 35 | + (void)memset(buf, 0, bufLen); | ||
| 36 | + (void)memcpy(buf, &g_testSecurityRandomValue, | ||
| 37 | + (bufLen < sizeof(g_testSecurityRandomValue)) ? bufLen : sizeof(g_testSecurityRandomValue)); | ||
| 38 | + return 0; | ||
| 39 | +} | ||
| 40 | + | ||
| 41 | +uint32_t SecurityRandomUint32(void) | ||
| 42 | +{ | ||
| 43 | + return g_testSecurityRandomValue; | ||
| 44 | +} | ||
| @@ -0,0 +1,20 @@ | |||
| 1 | +/* | ||
| 2 | + * Test knobs for SoftAP host tests (fixtures controlled per test case). | ||
| 3 | + */ | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | +extern const IotcDeviceInfo *g_testDevInfo; | ||
| 15 | +extern DevBindStatus g_testBindStatus; | ||
| 16 | +extern uint8_t g_testDataConfig; | ||
| 17 | +extern int32_t g_testSecurityRandomRet; | ||
| 18 | +extern uint32_t g_testSecurityRandomValue; | ||
| 19 | + | ||
| 20 | + | ||
| @@ -0,0 +1,389 @@ | |||
| 1 | +/* CoAP client timeout scheduling and request-registration regression tests. */ | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | +int g_testChecks; | ||
| 15 | +int g_testFailures; | ||
| 16 | + | ||
| 17 | +struct UtilsExMutex { | ||
| 18 | + uint32_t unused; | ||
| 19 | +}; | ||
| 20 | + | ||
| 21 | +static uint32_t g_now; | ||
| 22 | +static int32_t g_lockDepth; | ||
| 23 | +static bool g_failAlloc; | ||
| 24 | +static uint32_t g_wifiTimeoutCallbacks; | ||
| 25 | +static uint32_t g_netTimeoutCallbacks; | ||
| 26 | +static uint32_t g_netContextCallbacks; | ||
| 27 | +static uint32_t g_netContextCleanups; | ||
| 28 | + | ||
| 29 | +typedef struct { | ||
| 30 | + uint32_t marker; | ||
| 31 | + bool expectTimeout; | ||
| 32 | +} NetRequestContext; | ||
| 33 | + | ||
| 34 | +uint32_t IotcGetSysTimeMs(void) | ||
| 35 | +{ | ||
| 36 | + return g_now; | ||
| 37 | +} | ||
| 38 | + | ||
| 39 | +uint32_t UtilsDeltaTime(uint32_t timeNew, uint32_t timeOld) | ||
| 40 | +{ | ||
| 41 | + return timeNew - timeOld; | ||
| 42 | +} | ||
| 43 | + | ||
| 44 | +bool UtilsExMutexLockInner(UtilsExMutex *mutex, uint32_t timeout, const char *func) | ||
| 45 | +{ | ||
| 46 | + (void)mutex; | ||
| 47 | + (void)timeout; | ||
| 48 | + (void)func; | ||
| 49 | + ++g_lockDepth; | ||
| 50 | + return true; | ||
| 51 | +} | ||
| 52 | + | ||
| 53 | +void UtilsExMutexUnlockInner(UtilsExMutex *mutex, const char *func) | ||
| 54 | +{ | ||
| 55 | + (void)mutex; | ||
| 56 | + (void)func; | ||
| 57 | + --g_lockDepth; | ||
| 58 | +} | ||
| 59 | + | ||
| 60 | +void *IotcMalloc(uint32_t size) | ||
| 61 | +{ | ||
| 62 | + return g_failAlloc ? NULL : malloc(size); | ||
| 63 | +} | ||
| 64 | + | ||
| 65 | +void IotcFree(void *ptr) | ||
| 66 | +{ | ||
| 67 | + free(ptr); | ||
| 68 | +} | ||
| 69 | + | ||
| 70 | +int32_t SecurityRandom(uint8_t *buf, uint32_t len) | ||
| 71 | +{ | ||
| 72 | + (void)memset(buf, 0x5A, len); | ||
| 73 | + return IOTC_OK; | ||
| 74 | +} | ||
| 75 | + | ||
| 76 | +int32_t CoapEndpointSendPacket(CoapEndpoint *endpoint, const CoapBuildPacket *build, | ||
| 77 | + CoapPacket *pkt, uint32_t preSize, const SocketAddr *addr) | ||
| 78 | +{ | ||
| 79 | + (void)endpoint; | ||
| 80 | + (void)preSize; | ||
| 81 | + (void)addr; | ||
| 82 | + pkt->header = build->header; | ||
| 83 | + (void)memcpy(pkt->token, build->token, build->header.tkl); | ||
| 84 | + return IOTC_OK; | ||
| 85 | +} | ||
| 86 | + | ||
| 87 | +int32_t NetCoapEndpointSendPacket(NetCoapEndpoint *endpoint, const NetCoapBuildPacket *build, | ||
| 88 | + NetCoapPacket *pkt, uint32_t preSize, const NetSocketAddr *addr) | ||
| 89 | +{ | ||
| 90 | + (void)endpoint; | ||
| 91 | + (void)preSize; | ||
| 92 | + (void)addr; | ||
| 93 | + pkt->header = build->header; | ||
| 94 | + (void)memcpy(pkt->token, build->token, build->header.tkl); | ||
| 95 | + return IOTC_OK; | ||
| 96 | +} | ||
| 97 | + | ||
| 98 | +static void WifiTimeoutHandler(const CoapPacket *resp, const SocketAddr *addr, void *userData, bool timeout) | ||
| 99 | +{ | ||
| 100 | + (void)resp; | ||
| 101 | + (void)addr; | ||
| 102 | + (void)userData; | ||
| 103 | + CHECK(timeout); | ||
| 104 | + ++g_wifiTimeoutCallbacks; | ||
| 105 | +} | ||
| 106 | + | ||
| 107 | +static void NetTimeoutHandler(const NetCoapPacket *resp, const NetSocketAddr *addr, | ||
| 108 | + NetCoapUserData *userData, bool timeout) | ||
| 109 | +{ | ||
| 110 | + (void)resp; | ||
| 111 | + (void)addr; | ||
| 112 | + (void)userData; | ||
| 113 | + CHECK(timeout); | ||
| 114 | + ++g_netTimeoutCallbacks; | ||
| 115 | +} | ||
| 116 | + | ||
| 117 | +static void NetContextHandler(const NetCoapPacket *resp, | ||
| 118 | + const NetSocketAddr *addr, NetCoapUserData *userData, bool timeout) | ||
| 119 | +{ | ||
| 120 | + NetRequestContext *context = userData != NULL ? userData->ptr : NULL; | ||
| 121 | + CHECK(context != NULL); | ||
| 122 | + if (context != NULL) { | ||
| 123 | + CHECK_EQ_INT(0xC0A5U, context->marker); | ||
| 124 | + CHECK_EQ_INT(context->expectTimeout, timeout); | ||
| 125 | + } | ||
| 126 | + CHECK((timeout && resp == NULL && addr == NULL) || | ||
| 127 | + (!timeout && resp != NULL && addr != NULL)); | ||
| 128 | + ++g_netContextCallbacks; | ||
| 129 | +} | ||
| 130 | + | ||
| 131 | +static void NetContextCleanup(NetCoapUserData *userData) | ||
| 132 | +{ | ||
| 133 | + CHECK(userData != NULL && userData->ptr != NULL); | ||
| 134 | + if (userData != NULL && userData->ptr != NULL) { | ||
| 135 | + NetRequestContext *context = userData->ptr; | ||
| 136 | + (void)memset(context, 0, sizeof(*context)); | ||
| 137 | + free(context); | ||
| 138 | + userData->ptr = NULL; | ||
| 139 | + } | ||
| 140 | + ++g_netContextCleanups; | ||
| 141 | +} | ||
| 142 | + | ||
| 143 | +static CoapReqNode *AddWifiReq(CoapEndpoint *endpoint, uint32_t sentAt) | ||
| 144 | +{ | ||
| 145 | + CoapReqNode *node = (CoapReqNode *)calloc(1, sizeof(*node)); | ||
| 146 | + CHECK(node != NULL); | ||
| 147 | + if (node != NULL) { | ||
| 148 | + node->time = sentAt; | ||
| 149 | + node->respHandler = WifiTimeoutHandler; | ||
| 150 | + LIST_INSERT_BEFORE(&node->node, &endpoint->client.reqList); | ||
| 151 | + ++endpoint->client.reqCnt; | ||
| 152 | + } | ||
| 153 | + return node; | ||
| 154 | +} | ||
| 155 | + | ||
| 156 | +static NetCoapReqNode *AddNetReq(NetCoapEndpoint *endpoint, uint32_t sentAt) | ||
| 157 | +{ | ||
| 158 | + NetCoapReqNode *node = (NetCoapReqNode *)calloc(1, sizeof(*node)); | ||
| 159 | + CHECK(node != NULL); | ||
| 160 | + if (node != NULL) { | ||
| 161 | + node->time = sentAt; | ||
| 162 | + node->respHandler = NetTimeoutHandler; | ||
| 163 | + LIST_INSERT_BEFORE(&node->node, &endpoint->client.reqList); | ||
| 164 | + ++endpoint->client.reqCnt; | ||
| 165 | + } | ||
| 166 | + return node; | ||
| 167 | +} | ||
| 168 | + | ||
| 169 | +static void TestWifiTimeoutScheduling(void) | ||
| 170 | +{ | ||
| 171 | + struct UtilsExMutex mutex = {0}; | ||
| 172 | + CoapEndpoint endpoint = {0}; | ||
| 173 | + endpoint.mutex = &mutex; | ||
| 174 | + endpoint.client.timeout = 5000U; | ||
| 175 | + LIST_INIT(&endpoint.client.reqList); | ||
| 176 | + (void)AddWifiReq(&endpoint, 1000U); | ||
| 177 | + | ||
| 178 | + g_now = 1001U; | ||
| 179 | + uint32_t wait = UINT32_MAX; | ||
| 180 | + CoapClientReqPrepare(&endpoint, &wait); | ||
| 181 | + CHECK_EQ_INT(4999, wait); | ||
| 182 | + CHECK(!CoapEndpointReqCheck(&endpoint, g_now)); | ||
| 183 | + | ||
| 184 | + g_now = 5999U; | ||
| 185 | + wait = 10000U; | ||
| 186 | + CoapClientReqPrepare(&endpoint, &wait); | ||
| 187 | + CHECK_EQ_INT(1, wait); | ||
| 188 | + CHECK(!CoapEndpointReqCheck(&endpoint, g_now)); | ||
| 189 | + | ||
| 190 | + g_now = 6000U; | ||
| 191 | + wait = UINT32_MAX; | ||
| 192 | + CoapClientReqPrepare(&endpoint, &wait); | ||
| 193 | + CHECK_EQ_INT(0, wait); | ||
| 194 | + CHECK(CoapEndpointReqCheck(&endpoint, g_now)); | ||
| 195 | + CoapClientRespTimeoutCheck(&endpoint); | ||
| 196 | + CHECK_EQ_INT(1, g_wifiTimeoutCallbacks); | ||
| 197 | + CHECK_EQ_INT(0, endpoint.client.reqCnt); | ||
| 198 | + CHECK(LIST_EMPTY(&endpoint.client.reqList)); | ||
| 199 | + CHECK_EQ_INT(0, g_lockDepth); | ||
| 200 | +} | ||
| 201 | + | ||
| 202 | +static void TestWifiMultipleTimeoutCallbacks(void) | ||
| 203 | +{ | ||
| 204 | + struct UtilsExMutex mutex = {0}; | ||
| 205 | + CoapEndpoint endpoint = {0}; | ||
| 206 | + endpoint.mutex = &mutex; | ||
| 207 | + endpoint.client.timeout = 100U; | ||
| 208 | + LIST_INIT(&endpoint.client.reqList); | ||
| 209 | + (void)AddWifiReq(&endpoint, 10U); | ||
| 210 | + (void)AddWifiReq(&endpoint, 20U); | ||
| 211 | + g_now = 120U; | ||
| 212 | + g_wifiTimeoutCallbacks = 0U; | ||
| 213 | + CoapClientRespTimeoutCheck(&endpoint); | ||
| 214 | + CHECK_EQ_INT(2, g_wifiTimeoutCallbacks); | ||
| 215 | + CHECK_EQ_INT(0, endpoint.client.reqCnt); | ||
| 216 | + CHECK(LIST_EMPTY(&endpoint.client.reqList)); | ||
| 217 | + CHECK_EQ_INT(0, g_lockDepth); | ||
| 218 | +} | ||
| 219 | + | ||
| 220 | +static void TestWifiRegistrationErrorsPropagate(void) | ||
| 221 | +{ | ||
| 222 | + struct UtilsExMutex mutex = {0}; | ||
| 223 | + CoapEndpoint endpoint = {0}; | ||
| 224 | + endpoint.mutex = &mutex; | ||
| 225 | + endpoint.client.tkl = COAP_TOKEN_MAX_LEN; | ||
| 226 | + endpoint.client.timeout = 5000U; | ||
| 227 | + LIST_INIT(&endpoint.client.reqList); | ||
| 228 | + CoapClientReqParam param = { | ||
| 229 | + .type = COAP_MSG_TYPE_CON, | ||
| 230 | + .code = COAP_METHOD_TYPE_POST, | ||
| 231 | + .respHandler = WifiTimeoutHandler, | ||
| 232 | + }; | ||
| 233 | + SocketAddr addr = {5683U, 0xC0A804FFU}; | ||
| 234 | + CoapPacket packet; | ||
| 235 | + | ||
| 236 | + endpoint.client.reqCnt = UINT8_MAX; | ||
| 237 | + CHECK(CoapClientSendReq(&endpoint, ¶m, &addr, &packet) != IOTC_OK); | ||
| 238 | + CHECK_EQ_INT(0, g_lockDepth); | ||
| 239 | + | ||
| 240 | + endpoint.client.reqCnt = 0U; | ||
| 241 | + g_failAlloc = true; | ||
| 242 | + CHECK_EQ_INT(IOTC_ADAPTER_MEM_ERR_MALLOC, CoapClientSendReq(&endpoint, ¶m, &addr, &packet)); | ||
| 243 | + g_failAlloc = false; | ||
| 244 | + CHECK_EQ_INT(0, g_lockDepth); | ||
| 245 | +} | ||
| 246 | + | ||
| 247 | +static void TestNetTimeoutScheduling(void) | ||
| 248 | +{ | ||
| 249 | + struct UtilsExMutex mutex = {0}; | ||
| 250 | + NetCoapEndpoint endpoint = {0}; | ||
| 251 | + endpoint.mutex = &mutex; | ||
| 252 | + endpoint.client.timeout = 5000U; | ||
| 253 | + LIST_INIT(&endpoint.client.reqList); | ||
| 254 | + (void)AddNetReq(&endpoint, UINT32_MAX - 99U); | ||
| 255 | + | ||
| 256 | + g_now = 50U; | ||
| 257 | + uint32_t wait = UINT32_MAX; | ||
| 258 | + NetCoapClientReqPrepare(&endpoint, &wait); | ||
| 259 | + CHECK_EQ_INT(4850, wait); | ||
| 260 | + CHECK(!NetCoapEndpointReqCheck(&endpoint, g_now)); | ||
| 261 | + | ||
| 262 | + g_now = 4900U; | ||
| 263 | + wait = UINT32_MAX; | ||
| 264 | + NetCoapClientReqPrepare(&endpoint, &wait); | ||
| 265 | + CHECK_EQ_INT(0, wait); | ||
| 266 | + CHECK(NetCoapEndpointReqCheck(&endpoint, g_now)); | ||
| 267 | + NetCoapClientRespTimeoutCheck(&endpoint); | ||
| 268 | + CHECK_EQ_INT(1, g_netTimeoutCallbacks); | ||
| 269 | + CHECK_EQ_INT(0, endpoint.client.reqCnt); | ||
| 270 | + CHECK(LIST_EMPTY(&endpoint.client.reqList)); | ||
| 271 | + CHECK_EQ_INT(0, g_lockDepth); | ||
| 272 | +} | ||
| 273 | + | ||
| 274 | +static void TestNetRegistrationErrorsPropagate(void) | ||
| 275 | +{ | ||
| 276 | + struct UtilsExMutex mutex = {0}; | ||
| 277 | + NetCoapEndpoint endpoint = {0}; | ||
| 278 | + endpoint.mutex = &mutex; | ||
| 279 | + endpoint.client.tkl = NET_COAP_TOKEN_MAX_LEN; | ||
| 280 | + endpoint.client.timeout = 5000U; | ||
| 281 | + LIST_INIT(&endpoint.client.reqList); | ||
| 282 | + NetCoapClientReqParam param = { | ||
| 283 | + .type = NET_COAP_MSG_TYPE_CON, | ||
| 284 | + .code = NET_COAP_METHOD_TYPE_POST, | ||
| 285 | + .respHandler = NetTimeoutHandler, | ||
| 286 | + }; | ||
| 287 | + NetSocketAddr addr = {5683U, 0xC0A804FFU}; | ||
| 288 | + NetCoapPacket packet; | ||
| 289 | + | ||
| 290 | + endpoint.client.reqCnt = UINT8_MAX; | ||
| 291 | + CHECK(NetCoapClientSendReq(&endpoint, ¶m, &addr, &packet) != IOTC_OK); | ||
| 292 | + CHECK_EQ_INT(0, g_lockDepth); | ||
| 293 | + | ||
| 294 | + endpoint.client.reqCnt = 0U; | ||
| 295 | + g_failAlloc = true; | ||
| 296 | + CHECK_EQ_INT(IOTC_ADAPTER_MEM_ERR_MALLOC, NetCoapClientSendReq(&endpoint, ¶m, &addr, &packet)); | ||
| 297 | + g_failAlloc = false; | ||
| 298 | + CHECK_EQ_INT(0, g_lockDepth); | ||
| 299 | +} | ||
| 300 | + | ||
| 301 | +static NetRequestContext *NewNetContext(bool expectTimeout) | ||
| 302 | +{ | ||
| 303 | + NetRequestContext *context = calloc(1, sizeof(*context)); | ||
| 304 | + CHECK(context != NULL); | ||
| 305 | + if (context != NULL) { | ||
| 306 | + context->marker = 0xC0A5U; | ||
| 307 | + context->expectTimeout = expectTimeout; | ||
| 308 | + } | ||
| 309 | + return context; | ||
| 310 | +} | ||
| 311 | + | ||
| 312 | +static void InitNetEndpoint(NetCoapEndpoint *endpoint, | ||
| 313 | + struct UtilsExMutex *mutex) | ||
| 314 | +{ | ||
| 315 | + (void)memset(endpoint, 0, sizeof(*endpoint)); | ||
| 316 | + endpoint->mutex = mutex; | ||
| 317 | + endpoint->client.tkl = NET_COAP_TOKEN_MAX_LEN; | ||
| 318 | + endpoint->client.timeout = 50U; | ||
| 319 | + LIST_INIT(&endpoint->client.reqList); | ||
| 320 | +} | ||
| 321 | + | ||
| 322 | +static int32_t SendContextRequest(NetCoapEndpoint *endpoint, | ||
| 323 | + NetRequestContext *context) | ||
| 324 | +{ | ||
| 325 | + NetCoapClientReqParam param = { | ||
| 326 | + .type = NET_COAP_MSG_TYPE_CON, | ||
| 327 | + .code = NET_COAP_METHOD_TYPE_POST, | ||
| 328 | + .respHandler = NetContextHandler, | ||
| 329 | + .respUserData = { .ptr = context }, | ||
| 330 | + .useRespUserData = true, | ||
| 331 | + .respUserDataCleanup = NetContextCleanup, | ||
| 332 | + }; | ||
| 333 | + NetSocketAddr addr = {5683U, 0xC0A804FFU}; | ||
| 334 | + NetCoapPacket packet = {0}; | ||
| 335 | + return NetCoapClientSendReq(endpoint, ¶m, &addr, &packet); | ||
| 336 | +} | ||
| 337 | + | ||
| 338 | +static void TestNetPerRequestContext(void) | ||
| 339 | +{ | ||
| 340 | + struct UtilsExMutex mutex = {0}; | ||
| 341 | + NetCoapEndpoint endpoint = {0}; | ||
| 342 | + InitNetEndpoint(&endpoint, &mutex); | ||
| 343 | + | ||
| 344 | + NetRequestContext *normal = NewNetContext(false); | ||
| 345 | + CHECK_EQ_INT(IOTC_OK, SendContextRequest(&endpoint, normal)); | ||
| 346 | + NetCoapReqNode *node = CONTAINER_OF(LIST_HEAD(&endpoint.client.reqList), | ||
| 347 | + NetCoapReqNode, node); | ||
| 348 | + NetCoapPacket response = {0}; | ||
| 349 | + response.header.msgId = node->msgId; | ||
| 350 | + response.header.tkl = endpoint.client.tkl; | ||
| 351 | + (void)memcpy(response.token, node->token, response.header.tkl); | ||
| 352 | + NetSocketAddr peer = {5683U, 0xC0A80402U}; | ||
| 353 | + CHECK_EQ_INT(IOTC_OK, NetCoapEndpointClientRecvRespPacket(&endpoint, | ||
| 354 | + &response, &peer)); | ||
| 355 | + CHECK_EQ_INT(1, g_netContextCallbacks); | ||
| 356 | + CHECK_EQ_INT(1, g_netContextCleanups); | ||
| 357 | + CHECK_EQ_INT(0, endpoint.client.reqCnt); | ||
| 358 | + | ||
| 359 | + NetRequestContext *expired = NewNetContext(true); | ||
| 360 | + g_now = 100U; | ||
| 361 | + CHECK_EQ_INT(IOTC_OK, SendContextRequest(&endpoint, expired)); | ||
| 362 | + g_now = 150U; | ||
| 363 | + NetCoapClientRespTimeoutCheck(&endpoint); | ||
| 364 | + CHECK_EQ_INT(2, g_netContextCallbacks); | ||
| 365 | + CHECK_EQ_INT(2, g_netContextCleanups); | ||
| 366 | + CHECK_EQ_INT(0, endpoint.client.reqCnt); | ||
| 367 | + | ||
| 368 | + NetRequestContext *teardown = NewNetContext(false); | ||
| 369 | + CHECK_EQ_INT(IOTC_OK, SendContextRequest(&endpoint, teardown)); | ||
| 370 | + NetCoapEndpointClientDeinit(&endpoint.client); | ||
| 371 | + CHECK_EQ_INT(2, g_netContextCallbacks); | ||
| 372 | + CHECK_EQ_INT(3, g_netContextCleanups); | ||
| 373 | + CHECK_EQ_INT(0, endpoint.client.reqCnt); | ||
| 374 | + CHECK(LIST_EMPTY(&endpoint.client.reqList)); | ||
| 375 | + CHECK_EQ_INT(0, g_lockDepth); | ||
| 376 | +} | ||
| 377 | + | ||
| 378 | +int main(void) | ||
| 379 | +{ | ||
| 380 | + setbuf(stdout, NULL); | ||
| 381 | + TestWifiTimeoutScheduling(); | ||
| 382 | + TestWifiMultipleTimeoutCallbacks(); | ||
| 383 | + TestWifiRegistrationErrorsPropagate(); | ||
| 384 | + TestNetTimeoutScheduling(); | ||
| 385 | + TestNetRegistrationErrorsPropagate(); | ||
| 386 | + TestNetPerRequestContext(); | ||
| 387 | + printf("test_coap_client_runtime: %d checks, %d failures\n", g_testChecks, g_testFailures); | ||
| 388 | + return g_testFailures == 0 ? 0 : 1; | ||
| 389 | +} | ||
| @@ -0,0 +1,166 @@ | |||
| 1 | +/* SoftAP /DeviceInfo wire-contract regression tests. */ | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | +int g_testChecks; | ||
| 17 | +int g_testFailures; | ||
| 18 | + | ||
| 19 | +static const IotcDeviceInfo g_deviceInfo = { | ||
| 20 | + .sn = "12345678", | ||
| 21 | + .prodId = "00414", | ||
| 22 | + .subProdId = "", | ||
| 23 | + .model = "DISP-01", | ||
| 24 | + .devTypeId = "1414", | ||
| 25 | + .devTypeName = "Disp", | ||
| 26 | + .manuId = "116", | ||
| 27 | + .manuName = "SwanLink", | ||
| 28 | + .devName = "OneConnectName", | ||
| 29 | + .fwv = "1.0.0", | ||
| 30 | + .hwv = "1.0.0", | ||
| 31 | + .swv = "1.0.0", | ||
| 32 | + .protType = IOTC_PROT_TYPE_WIFI, | ||
| 33 | +}; | ||
| 34 | + | ||
| 35 | +static int g_buildCalls; | ||
| 36 | +static int g_sendCalls; | ||
| 37 | +static uint8_t g_responseType; | ||
| 38 | +static uint8_t g_responseCode; | ||
| 39 | +static char g_responseJson[1024]; | ||
| 40 | + | ||
| 41 | +bool UtilsIsEmptyStr(const char *str) | ||
| 42 | +{ | ||
| 43 | + return str == NULL || str[0] == '\0'; | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | +const IotcDeviceInfo *ModelGetDevInfo(void) | ||
| 47 | +{ | ||
| 48 | + return &g_deviceInfo; | ||
| 49 | +} | ||
| 50 | + | ||
| 51 | +int32_t DevSvcProxyGetAuthInfo(bool *isExist, DevAuthInfo *authInfo) | ||
| 52 | +{ | ||
| 53 | + if (isExist == NULL || authInfo == NULL) { | ||
| 54 | + return IOTC_ERR_PARAM_INVALID; | ||
| 55 | + } | ||
| 56 | + *isExist = false; | ||
| 57 | + (void)memset(authInfo, 0, sizeof(*authInfo)); | ||
| 58 | + return IOTC_OK; | ||
| 59 | +} | ||
| 60 | + | ||
| 61 | +int32_t GetWifiMacAddrStr(char *macStr, uint32_t len) | ||
| 62 | +{ | ||
| 63 | + if (macStr == NULL || len < sizeof("50:65:13:B1:6E:52")) { | ||
| 64 | + return IOTC_ERR_PARAM_INVALID; | ||
| 65 | + } | ||
| 66 | + (void)snprintf(macStr, len, "%s", "50:65:13:B1:6E:52"); | ||
| 67 | + return IOTC_OK; | ||
| 68 | +} | ||
| 69 | + | ||
| 70 | +int32_t CoapServerBuildDefaultRespParam(CoapServerRespParam *param, | ||
| 71 | + const CoapPacket *reqPkt, IotcJson *respJson) | ||
| 72 | +{ | ||
| 73 | + if (param == NULL || reqPkt == NULL || respJson == NULL) { | ||
| 74 | + return IOTC_ERR_PARAM_INVALID; | ||
| 75 | + } | ||
| 76 | + ++g_buildCalls; | ||
| 77 | + (void)memset(param, 0, sizeof(*param)); | ||
| 78 | + param->req = reqPkt; | ||
| 79 | + param->type = COAP_MSG_TYPE_ACK; | ||
| 80 | + param->code = COAP_RESPONSE_CODE_CONTENT; | ||
| 81 | + param->payloadUserData = respJson; | ||
| 82 | + char *json = IotcJsonPrint(respJson); | ||
| 83 | + if (json == NULL) { | ||
| 84 | + return IOTC_ADAPTER_JSON_ERR_PRINT; | ||
| 85 | + } | ||
| 86 | + (void)snprintf(g_responseJson, sizeof(g_responseJson), "%s", json); | ||
| 87 | + IotcJsonFreePrint(json); | ||
| 88 | + return IOTC_OK; | ||
| 89 | +} | ||
| 90 | + | ||
| 91 | +int32_t CoapServerSendResp(CoapEndpoint *endpoint, const CoapServerRespParam *param, | ||
| 92 | + const SocketAddr *addr, CoapPacket *packetBuf) | ||
| 93 | +{ | ||
| 94 | + if (endpoint == NULL || param == NULL || addr == NULL || packetBuf == NULL) { | ||
| 95 | + return IOTC_ERR_PARAM_INVALID; | ||
| 96 | + } | ||
| 97 | + ++g_sendCalls; | ||
| 98 | + g_responseType = param->type; | ||
| 99 | + g_responseCode = param->code; | ||
| 100 | + return IOTC_OK; | ||
| 101 | +} | ||
| 102 | + | ||
| 103 | +static void Reset(void) | ||
| 104 | +{ | ||
| 105 | + g_buildCalls = 0; | ||
| 106 | + g_sendCalls = 0; | ||
| 107 | + g_responseType = UINT8_MAX; | ||
| 108 | + g_responseCode = UINT8_MAX; | ||
| 109 | + (void)memset(g_responseJson, 0, sizeof(g_responseJson)); | ||
| 110 | +} | ||
| 111 | + | ||
| 112 | +static void TestEmptyGetReturnsHiv20(void) | ||
| 113 | +{ | ||
| 114 | + Reset(); | ||
| 115 | + CoapEndpoint endpoint = {0}; | ||
| 116 | + CoapPacket request = {0}; | ||
| 117 | + SocketAddr addr = {0}; | ||
| 118 | + request.header.type = COAP_MSG_TYPE_CON; | ||
| 119 | + request.header.code = COAP_METHOD_TYPE_GET; | ||
| 120 | + request.payload.data = NULL; | ||
| 121 | + request.payload.len = 0; | ||
| 122 | + | ||
| 123 | + SoftapCoapDeviceInfoReqHandler(&endpoint, &request, &addr, NULL); | ||
| 124 | + | ||
| 125 | + CHECK_EQ_INT(1, g_buildCalls); | ||
| 126 | + CHECK_EQ_INT(1, g_sendCalls); | ||
| 127 | + CHECK_EQ_INT(COAP_MSG_TYPE_ACK, g_responseType); | ||
| 128 | + CHECK_EQ_INT(COAP_RESPONSE_CODE_CONTENT, g_responseCode); | ||
| 129 | + CHECK(strstr(g_responseJson, "\"hiv\":\"2.0\"") != NULL); | ||
| 130 | + CHECK(strstr(g_responseJson, "\"hiv\":\"3.0\"") == NULL); | ||
| 131 | + CHECK(strstr(g_responseJson, "\"devId\":\"\"") != NULL); | ||
| 132 | + CHECK(strstr(g_responseJson, "\"devInfo\":{") != NULL); | ||
| 133 | + IotcJson *response = IotcJsonParse(g_responseJson); | ||
| 134 | + int32_t errCode = -1; | ||
| 135 | + CHECK(response != NULL); | ||
| 136 | + CHECK_EQ_INT(IOTC_OK, UtilsJsonGetNum(response, "errCode", &errCode)); | ||
| 137 | + CHECK_EQ_INT(IOTC_OK, errCode); | ||
| 138 | + IotcJsonDelete(response); | ||
| 139 | +} | ||
| 140 | + | ||
| 141 | +static void TestNonEmptyGetIsRejected(void) | ||
| 142 | +{ | ||
| 143 | + Reset(); | ||
| 144 | + CoapEndpoint endpoint = {0}; | ||
| 145 | + uint8_t payload[] = {'{', '}'}; | ||
| 146 | + CoapPacket request = {0}; | ||
| 147 | + SocketAddr addr = {0}; | ||
| 148 | + request.header.type = COAP_MSG_TYPE_CON; | ||
| 149 | + request.header.code = COAP_METHOD_TYPE_GET; | ||
| 150 | + request.payload.data = payload; | ||
| 151 | + request.payload.len = sizeof(payload); | ||
| 152 | + | ||
| 153 | + SoftapCoapDeviceInfoReqHandler(&endpoint, &request, &addr, NULL); | ||
| 154 | + | ||
| 155 | + CHECK_EQ_INT(0, g_buildCalls); | ||
| 156 | + CHECK_EQ_INT(0, g_sendCalls); | ||
| 157 | + CHECK(g_responseJson[0] == '\0'); | ||
| 158 | +} | ||
| 159 | + | ||
| 160 | +int main(void) | ||
| 161 | +{ | ||
| 162 | + TestEmptyGetReturnsHiv20(); | ||
| 163 | + TestNonEmptyGetIsRejected(); | ||
| 164 | + printf("softap DeviceInfo host: %d checks, %d failures\n", g_testChecks, g_testFailures); | ||
| 165 | + return g_testFailures == 0 ? 0 : 1; | ||
| 166 | +} | ||
| @@ -0,0 +1,230 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +/* | ||
| 17 | + * SoftAP SSID 生成测试:version>=2 带 z+p,version<2 只带 z。 | ||
| 18 | + */ | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | +int g_testChecks; | ||
| 28 | +int g_testFailures; | ||
| 29 | + | ||
| 30 | +int32_t IotcOhGetSoftApName(uint8_t *ssid, uint32_t *len); | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + | ||
| 35 | + | ||
| 36 | + | ||
| 37 | + | ||
| 38 | + | ||
| 39 | + | ||
| 40 | + | ||
| 41 | + | ||
| 42 | + | ||
| 43 | + | ||
| 44 | + | ||
| 45 | + | ||
| 46 | + | ||
| 47 | +static IotcDeviceInfo MakeDevInfo(void) | ||
| 48 | +{ | ||
| 49 | + IotcDeviceInfo info = {0}; | ||
| 50 | + info.manuName = "TestManu"; | ||
| 51 | + info.devTypeName = "Lamp"; | ||
| 52 | + info.prodId = "12345"; | ||
| 53 | + info.sn = "SN00001234CDEF"; | ||
| 54 | + return info; | ||
| 55 | +} | ||
| 56 | + | ||
| 57 | +static IotcDeviceInfo g_devInfo; | ||
| 58 | + | ||
| 59 | +static void ResetFixture(void) | ||
| 60 | +{ | ||
| 61 | + g_devInfo = MakeDevInfo(); | ||
| 62 | + g_testDevInfo = &g_devInfo; | ||
| 63 | + g_testBindStatus = DEV_BIND_STATUS_NOT_BIND; | ||
| 64 | + g_testDataConfig = 0; | ||
| 65 | +} | ||
| 66 | + | ||
| 67 | +/* 期望布局:head + name + -X + PID + SN4 + z [+ p]。 */ | ||
| 68 | +static void ExpectSsid(const char *expectedText, uint8_t expectedCap, uint8_t expectedConf) | ||
| 69 | +{ | ||
| 70 | + uint8_t ssid[64] = {0}; | ||
| 71 | + uint32_t len = sizeof(ssid); | ||
| 72 | + CHECK_EQ_INT(IOTC_OK, IotcOhGetSoftApName(ssid, &len)); | ||
| 73 | + uint32_t textLen = (uint32_t)strlen(expectedText); | ||
| 74 | + CHECK_EQ_INT(textLen + EXPECTED_SUFFIX_LEN, len); | ||
| 75 | + CHECK(memcmp(ssid, expectedText, textLen) == 0); | ||
| 76 | + CHECK_EQ_INT(expectedCap, ssid[textLen]); | ||
| 77 | + | ||
| 78 | + CHECK_EQ_INT(expectedConf, ssid[textLen + 1]); | ||
| 79 | + | ||
| 80 | + (void)expectedConf; | ||
| 81 | + | ||
| 82 | +} | ||
| 83 | + | ||
| 84 | +static void TestUnregisteredHappyPath(void) | ||
| 85 | +{ | ||
| 86 | + ResetFixture(); | ||
| 87 | + ExpectSsid(EXPECTED_UNREG_HEAD "-TestManuLamp-" EXPECTED_VER_CHAR_STR "12345CDEF", | ||
| 88 | + EXPECTED_CAPACITY, EXPECTED_ZERO_CONF); | ||
| 89 | +} | ||
| 90 | + | ||
| 91 | +static void TestRegisteredHead(void) | ||
| 92 | +{ | ||
| 93 | + ResetFixture(); | ||
| 94 | + g_testBindStatus = DEV_BIND_STATUS_BIND; | ||
| 95 | + ExpectSsid(EXPECTED_REG_HEAD "-TestManuLamp-" EXPECTED_VER_CHAR_STR "12345CDEF", | ||
| 96 | + EXPECTED_CAPACITY, EXPECTED_ZERO_CONF); | ||
| 97 | +} | ||
| 98 | + | ||
| 99 | +static void TestOemProvisionConfByte(void) | ||
| 100 | +{ | ||
| 101 | + ResetFixture(); | ||
| 102 | + | ||
| 103 | + g_testDataConfig = 0x01; | ||
| 104 | + ExpectSsid(EXPECTED_UNREG_HEAD "-TestManuLamp-" EXPECTED_VER_CHAR_STR "12345CDEF", | ||
| 105 | + EXPECTED_CAPACITY, 0x01); | ||
| 106 | + | ||
| 107 | +} | ||
| 108 | + | ||
| 109 | +static void TestTerminalConfigBinaryLengthContract(void) | ||
| 110 | +{ | ||
| 111 | + ResetFixture(); | ||
| 112 | + uint8_t ssid[64] = {0}; | ||
| 113 | + uint32_t len = sizeof(ssid); | ||
| 114 | + CHECK_EQ_INT(IOTC_OK, IotcOhGetSoftApName(ssid, &len)); | ||
| 115 | + | ||
| 116 | + /* Diagnostic build maps binary p=0x00 to visible ASCII '0' (0x30). */ | ||
| 117 | + CHECK_EQ_INT(EXPECTED_ZERO_CONF, ssid[len - 1]); | ||
| 118 | + CHECK_EQ_INT(len, strlen((const char *)ssid)); | ||
| 119 | + | ||
| 120 | + ResetFixture(); | ||
| 121 | + g_testDataConfig = 0x01; | ||
| 122 | + len = sizeof(ssid); | ||
| 123 | + CHECK_EQ_INT(IOTC_OK, IotcOhGetSoftApName(ssid, &len)); | ||
| 124 | + CHECK_EQ_INT(0x01, ssid[len - 1]); | ||
| 125 | + CHECK_EQ_INT(len, strlen((const char *)ssid)); | ||
| 126 | + | ||
| 127 | + CHECK_EQ_INT(EXPECTED_CAPACITY, ssid[len - 1]); | ||
| 128 | + CHECK_EQ_INT(len, strlen((const char *)ssid)); | ||
| 129 | + | ||
| 130 | +} | ||
| 131 | + | ||
| 132 | +static void TestWifiOnlyCapabilityBits(void) | ||
| 133 | +{ | ||
| 134 | + ResetFixture(); | ||
| 135 | + uint8_t ssid[64] = {0}; | ||
| 136 | + uint32_t len = sizeof(ssid); | ||
| 137 | + CHECK_EQ_INT(IOTC_OK, IotcOhGetSoftApName(ssid, &len)); | ||
| 138 | + uint32_t textLen = (uint32_t)strlen(EXPECTED_UNREG_HEAD "-TestManuLamp-" EXPECTED_VER_CHAR_STR "12345CDEF"); | ||
| 139 | + uint8_t capacity = ssid[textLen]; | ||
| 140 | + CHECK_EQ_INT(0, capacity & 0x80); | ||
| 141 | + CHECK_EQ_INT(0x60, capacity & 0x60); | ||
| 142 | + CHECK((capacity & 0x10) != 0); /* 专用 SSID */ | ||
| 143 | + CHECK((capacity & 0x08) != 0); /* SoftAP */ | ||
| 144 | + CHECK((capacity & 0x04) != 0); /* CoAP */ | ||
| 145 | + CHECK_EQ_INT(0, capacity & 0x03); /* WiFi Only不声明BLE/SLE能力 */ | ||
| 146 | +} | ||
| 147 | + | ||
| 148 | +static void TestLongNameTruncatedTo14(void) | ||
| 149 | +{ | ||
| 150 | + ResetFixture(); | ||
| 151 | + g_devInfo.manuName = "ABCDEFGHIJ"; | ||
| 152 | + g_devInfo.devTypeName = "KLMNOPQRST"; | ||
| 153 | + ExpectSsid(EXPECTED_UNREG_HEAD "-ABCDEFGHIJKLMN-" EXPECTED_VER_CHAR_STR "12345CDEF", | ||
| 154 | + EXPECTED_CAPACITY, EXPECTED_ZERO_CONF); | ||
| 155 | +} | ||
| 156 | + | ||
| 157 | +static void TestRepresentativeWifiOnlyIdentity(void) | ||
| 158 | +{ | ||
| 159 | + ResetFixture(); | ||
| 160 | + g_devInfo.manuName = "SwanLink"; | ||
| 161 | + g_devInfo.devTypeName = "Disp"; | ||
| 162 | + g_devInfo.prodId = "00414"; | ||
| 163 | + g_devInfo.sn = "12345678"; | ||
| 164 | + ExpectSsid(EXPECTED_UNREG_HEAD "-SwanLinkDisp-" EXPECTED_VER_CHAR_STR "004145678", | ||
| 165 | + EXPECTED_CAPACITY, EXPECTED_ZERO_CONF); | ||
| 166 | +} | ||
| 167 | + | ||
| 168 | +static void TestNullFieldsRejected(void) | ||
| 169 | +{ | ||
| 170 | + ResetFixture(); | ||
| 171 | + g_testDevInfo = NULL; | ||
| 172 | + uint8_t ssid[64] = {0}; | ||
| 173 | + uint32_t len = sizeof(ssid); | ||
| 174 | + CHECK_EQ_INT(IOTC_CORE_PROF_MDL_ERR_DEV_INFO_INVALID, IotcOhGetSoftApName(ssid, &len)); | ||
| 175 | + | ||
| 176 | + ResetFixture(); | ||
| 177 | + g_devInfo.manuName = NULL; | ||
| 178 | + len = sizeof(ssid); | ||
| 179 | + CHECK_EQ_INT(IOTC_CORE_PROF_MDL_ERR_DEV_INFO_INVALID, IotcOhGetSoftApName(ssid, &len)); | ||
| 180 | + | ||
| 181 | + ResetFixture(); | ||
| 182 | + g_devInfo.sn = NULL; | ||
| 183 | + len = sizeof(ssid); | ||
| 184 | + CHECK_EQ_INT(IOTC_CORE_PROF_MDL_ERR_DEV_INFO_INVALID, IotcOhGetSoftApName(ssid, &len)); | ||
| 185 | + | ||
| 186 | + ResetFixture(); | ||
| 187 | + CHECK_EQ_INT(IOTC_ERR_PARAM_INVALID, IotcOhGetSoftApName(NULL, &len)); | ||
| 188 | + CHECK_EQ_INT(IOTC_ERR_PARAM_INVALID, IotcOhGetSoftApName(ssid, NULL)); | ||
| 189 | +} | ||
| 190 | + | ||
| 191 | +static void TestShortSnRejected(void) | ||
| 192 | +{ | ||
| 193 | + ResetFixture(); | ||
| 194 | + g_devInfo.sn = "abc"; | ||
| 195 | + uint8_t ssid[64] = {0}; | ||
| 196 | + uint32_t len = sizeof(ssid); | ||
| 197 | + CHECK(IotcOhGetSoftApName(ssid, &len) != IOTC_OK); | ||
| 198 | +} | ||
| 199 | + | ||
| 200 | +static void TestBufferBoundary(void) | ||
| 201 | +{ | ||
| 202 | + ResetFixture(); | ||
| 203 | + uint8_t ssid[64] = {0}; | ||
| 204 | + /* 文本部分 26 字节:缓冲区 26 放不下(含 NUL 需 27),必须失败 */ | ||
| 205 | + uint32_t len = 26; | ||
| 206 | + CHECK(IotcOhGetSoftApName(ssid, &len) != IOTC_OK); | ||
| 207 | + /* 恰好装下 26 文本 + z [+ p]。 */ | ||
| 208 | + len = 26 + EXPECTED_SUFFIX_LEN; | ||
| 209 | + CHECK_EQ_INT(IOTC_OK, IotcOhGetSoftApName(ssid, &len)); | ||
| 210 | + CHECK_EQ_INT(26 + EXPECTED_SUFFIX_LEN, len); | ||
| 211 | +} | ||
| 212 | + | ||
| 213 | +int main(void) | ||
| 214 | +{ | ||
| 215 | + setbuf(stdout, NULL); | ||
| 216 | + TestUnregisteredHappyPath(); | ||
| 217 | + TestRegisteredHead(); | ||
| 218 | + TestOemProvisionConfByte(); | ||
| 219 | + TestTerminalConfigBinaryLengthContract(); | ||
| 220 | + TestWifiOnlyCapabilityBits(); | ||
| 221 | + TestLongNameTruncatedTo14(); | ||
| 222 | + TestRepresentativeWifiOnlyIdentity(); | ||
| 223 | + TestNullFieldsRejected(); | ||
| 224 | + TestShortSnRejected(); | ||
| 225 | + TestBufferBoundary(); | ||
| 226 | + | ||
| 227 | + printf("test_softap_name(v%d): %d checks, %d failures\n", IOTC_ONECONNECT_VERSION_CODE, | ||
| 228 | + g_testChecks, g_testFailures); | ||
| 229 | + return (g_testFailures == 0) ? 0 : 1; | ||
| 230 | +} | ||
| @@ -0,0 +1,92 @@ | |||
| 1 | +/* Ensure business responses cannot bypass SPEKE encryption before negotiation. */ | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | +int g_testChecks; | ||
| 15 | +int g_testFailures; | ||
| 16 | + | ||
| 17 | +static int g_encryptCalls; | ||
| 18 | +static int g_replaceCalls; | ||
| 19 | + | ||
| 20 | +SoftapPeerSess *SoftapGetPeerSess(const SocketAddr *addrInfo, SoftapSess *sess) | ||
| 21 | +{ | ||
| 22 | + (void)addrInfo; | ||
| 23 | + return &sess->peerSess[0]; | ||
| 24 | +} | ||
| 25 | + | ||
| 26 | +int32_t SpekeEncryptData(SpekeSession *session, const uint8_t *data, uint32_t dataLen, | ||
| 27 | + uint8_t **encData, uint32_t *encDataLen) | ||
| 28 | +{ | ||
| 29 | + (void)session; | ||
| 30 | + (void)data; | ||
| 31 | + ++g_encryptCalls; | ||
| 32 | + *encDataLen = dataLen + 1; | ||
| 33 | + *encData = malloc(*encDataLen); | ||
| 34 | + if (*encData == NULL) { | ||
| 35 | + return -1; | ||
| 36 | + } | ||
| 37 | + (void)memset(*encData, 0xA5, *encDataLen); | ||
| 38 | + return 0; | ||
| 39 | +} | ||
| 40 | + | ||
| 41 | +int32_t CoapUtilsReplacePayload(CoapPacket *pkt, CoapBuffer *buf, const CoapData *payload) | ||
| 42 | +{ | ||
| 43 | + (void)buf; | ||
| 44 | + ++g_replaceCalls; | ||
| 45 | + pkt->payload = *payload; | ||
| 46 | + return 0; | ||
| 47 | +} | ||
| 48 | + | ||
| 49 | +void IotcFree(void *ptr) | ||
| 50 | +{ | ||
| 51 | + free(ptr); | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +static SessCode RunEncrypt(SoftapSess *sess) | ||
| 55 | +{ | ||
| 56 | + uint8_t storage[64] = {0}; | ||
| 57 | + CoapPacket packet = {0}; | ||
| 58 | + packet.payload.data = storage; | ||
| 59 | + packet.payload.len = 4; | ||
| 60 | + UtilsBuffer buffer = {.buffer = storage, .size = sizeof(storage), .len = 16}; | ||
| 61 | + SocketAddr addr = {0}; | ||
| 62 | + SessAddtlInfo info = {.addr = &addr, .userData = sess}; | ||
| 63 | + return SoftapCoapMsgSendEncryptProcess((SessMsg *)&packet, &buffer, &info); | ||
| 64 | +} | ||
| 65 | + | ||
| 66 | +static void TestSpekeRequired(void) | ||
| 67 | +{ | ||
| 68 | + SoftapSess sess = {0}; | ||
| 69 | + g_encryptCalls = 0; | ||
| 70 | + g_replaceCalls = 0; | ||
| 71 | + | ||
| 72 | + CHECK_EQ_INT(SESS_CODE_ERR, RunEncrypt(&sess)); | ||
| 73 | + CHECK_EQ_INT(0, g_encryptCalls); | ||
| 74 | + CHECK_EQ_INT(0, g_replaceCalls); | ||
| 75 | + | ||
| 76 | + sess.peerSess[0].speke = (SpekeSession *)(uintptr_t)1; | ||
| 77 | + CHECK_EQ_INT(SESS_CODE_ERR, RunEncrypt(&sess)); | ||
| 78 | + CHECK_EQ_INT(0, g_encryptCalls); | ||
| 79 | + CHECK_EQ_INT(0, g_replaceCalls); | ||
| 80 | + | ||
| 81 | + UTILS_BIT_SET(sess.peerSess[0].bitMap, SOFTAP_PEER_SESS_BIT_MAP_SPEKE_SESS_CREATED); | ||
| 82 | + CHECK_EQ_INT(SESS_CODE_CONTINUE, RunEncrypt(&sess)); | ||
| 83 | + CHECK_EQ_INT(1, g_encryptCalls); | ||
| 84 | + CHECK_EQ_INT(1, g_replaceCalls); | ||
| 85 | +} | ||
| 86 | + | ||
| 87 | +int main(void) | ||
| 88 | +{ | ||
| 89 | + TestSpekeRequired(); | ||
| 90 | + printf("softap security chain host: %d checks, %d failures\n", g_testChecks, g_testFailures); | ||
| 91 | + return g_testFailures == 0 ? 0 : 1; | ||
| 92 | +} | ||
| @@ -14,7 +14,20 @@ ROOT = Path(__file__).resolve().parents[1] | |||
| 14 | 14 | ||
| 15 | 15 | ||
| 16 | def read_text(path): | 16 | def read_text(path): |
| 17 | - return (ROOT / path).read_text(encoding="utf-8") | 17 | + data = (ROOT / path).read_bytes() |
| 18 | + try: | ||
| 19 | + return data.decode("utf-8") | ||
| 20 | + except UnicodeDecodeError: | ||
| 21 | + # The 5.1 baseline still contains a few legacy GBK/GB18030 build | ||
| 22 | + # files. Backend checks only inspect ASCII GN/CMake tokens, so a | ||
| 23 | + # deterministic GB18030 fallback preserves their contents and avoids | ||
| 24 | + # silently skipping the check. | ||
| 25 | + try: | ||
| 26 | + return data.decode("gb18030") | ||
| 27 | + except UnicodeDecodeError: | ||
| 28 | + # Some inherited files mix legacy encodings. Latin-1 is a | ||
| 29 | + # byte-preserving last resort for the ASCII-only token checks. | ||
| 30 | + return data.decode("latin-1") | ||
| 18 | 31 | ||
| 19 | 32 | ||
| 20 | def fail(message): | 33 | def fail(message): |