/* 网络的三条命令(A14 / C1,需求 IF-19):`wifi` / `mqtt` 写配置(**只收 USB**),`net` 读状态。 `net` 的回应里**永远没有密码**,只有 `pass_set` / `mpass_set`(netcfg.h「密码永不外泄」)。 */ #include #include "cmd_priv.h" #include "netcfg.h" /* 配置命令只收 USB(cmd.h 链路那一段)。宿主机测试用的 CMD_LINK_TEST 视同 USB。 */ static bool usb_only_ok(const ctx_t *c) { return c->link != CMD_LINK_WIFI; } static cmd_err_t get_str(const ctx_t *c, const char *key, bool required, const char **out) { static const char empty[] = ""; const int g = cj_get_str(c->o, key, out); if (g == 1) { return CMD_OK; } if (g == 0 && !required) { *out = empty; return CMD_OK; } return CMD_ERR_ARG; } cmd_err_t do_wifi(ctx_t *c) { if (!usb_only_ok(c)) { c->bad = CMD_WIFI; return CMD_ERR_LINK; } const char *ssid = NULL; const char *pass = NULL; if (get_str(c, "ssid", true, &ssid) != CMD_OK || get_str(c, "pass", false, &pass) != CMD_OK) { return CMD_ERR_ARG; } if (!netcfg_wifi_ok(ssid, pass)) { c->bad = (int64_t)strlen(pass); /* 最常见的错是口令长度(<8),记长度不记内容 */ return CMD_ERR_RANGE; } return netcfg_set_wifi(ssid, pass) == HAL_OK ? CMD_OK : CMD_ERR_IO; } cmd_err_t do_mqtt(ctx_t *c) { if (!usb_only_ok(c)) { c->bad = CMD_MQTT; return CMD_ERR_LINK; } const char *uri = NULL; const char *user = NULL; const char *mpass = NULL; int64_t tele = 0; const int gt = cj_get_int(c->o, "tele_ms", &tele); if (get_str(c, "uri", true, &uri) != CMD_OK || get_str(c, "user", false, &user) != CMD_OK || get_str(c, "pass", false, &mpass) != CMD_OK || gt < 0) { return CMD_ERR_ARG; } if (tele < 0 || tele > NETCFG_TELE_MAX_MS || !netcfg_mqtt_ok(uri, user, mpass, (uint32_t)tele)) { c->bad = tele; return CMD_ERR_RANGE; } return netcfg_set_mqtt(uri, user, mpass, (uint32_t)tele) == HAL_OK ? CMD_OK : CMD_ERR_IO; } /* 把一段来路不可信的字符串写成 JSON 字符串内容:引号、反斜杠、控制字符换成 `?`。 经命令写进来的配置过了解析器,本来就没有这些;但 NVS 里读回来的可能是任何东西, 一个裸引号就能让整条回应变成非法 JSON,或者更糟 —— 变成另一段合法 JSON。 */ static void w_str(cj_writer_t *w, const char *s) { char buf[NETCFG_URI_MAX + 1]; size_t n = 0; for (; *s != '\0' && n < sizeof(buf) - 1; s++) { const unsigned char ch = (unsigned char)*s; buf[n++] = (ch < 0x20 || ch == 0x7F || ch == '"' || ch == '\\' || ch >= 0x80) ? '?' : (char)ch; } buf[n] = '\0'; cj_w(w, "\"%s\"", buf); } cmd_err_t do_net(ctx_t *c) { netcfg_t cfg; net_status_t st; netcfg_get(&cfg); net_status_get(&st); cj_writer_t *w = c->w; cj_w(w, ",\"id\":"); w_str(w, st.id); cj_w(w, ",\"wifi\":\"%s\",\"ssid\":", net_state_str(st.wifi)); w_str(w, cfg.ssid); cj_w(w, ",\"pass_set\":%s,\"ip\":", cfg.pass[0] ? "true" : "false"); w_str(w, st.ip); cj_w(w, ",\"rssi\":%d,\"wifi_reason\":%u,\"mqtt\":\"%s\",\"uri\":", (int)st.rssi, (unsigned)st.wifi_reason, net_state_str(st.mqtt)); w_str(w, cfg.uri); cj_w(w, ",\"user\":"); w_str(w, cfg.user); cj_w(w, ",\"mpass_set\":%s,\"tele_ms\":%lu,\"drops\":{\"wifi\":%lu,\"mqtt\":%lu}," "\"tele\":{\"sent\":%lu,\"skipped\":%lu}", cfg.mpass[0] ? "true" : "false", (unsigned long)cfg.tele_ms, (unsigned long)st.wifi_drops, (unsigned long)st.mqtt_drops, (unsigned long)st.tele_sent, (unsigned long)st.tele_skipped); return CMD_OK; }