network: track connecting/failed state from Connect() reply

Network.Connected can flicker true mid-handshake, so a row could show ✔
even while the passphrase was still being verified (and stay ✔ on a bad
PSK). Add a local per-network conn_status (IDLE / CONNECTING / FAILED)
driven by iwd's Connect() reply, which only returns once the attempt
truly concludes.

- iwd_network_connect() sets CONNECTING and refreshes immediately.
- The connect reply ctx now carries the object path, re-resolved through
  the live hash (same UAF-safe pattern as the SSID); on error → FAILED,
  on success → IDLE.
- A confirmed Connected=true clears the marker back to IDLE.
- popup row marker priority: … (connecting) and ! (failed) take
  precedence over ✔, with matching accessibility text.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
nemunaire 2026-06-29 14:55:20 +09:00
commit 59861872f4
3 changed files with 68 additions and 10 deletions

View file

@ -257,13 +257,24 @@ _rebuild_list(Popup *p)
memcpy(ssid_buf + copy, "\xe2\x80\xa6", 4); /* "…" + NUL */
raw_ssid = ssid_buf;
}
/* Connection marker: connecting (…) and failure (!) take priority
* over Connected, which iwd may briefly assert mid-handshake. */
const char *mark, *amark;
switch (n->conn_status)
{
case IWD_CONN_CONNECTING: mark = ""; amark = ", connecting"; break;
case IWD_CONN_FAILED: mark = " !"; amark = ", failed"; break;
default:
mark = n->connected ? "" : "";
amark = n->connected ? ", connected" : "";
}
char label[256];
snprintf(label, sizeof(label), "%s %s%s [%s]%s",
_signal_bars(iwd_network_signal_tier(n)),
n->known_path ? "" : " ",
raw_ssid,
iwd_security_label(n->security),
n->connected ? "" : "");
mark);
elm_object_text_set(btn, label);
/* Spoken label: avoids the ▂▄▆█ glyphs and ★/✔ markers which
* screen readers announce as raw Unicode. */
@ -274,7 +285,7 @@ _rebuild_list(Popup *p)
iwd_network_signal_tier(n),
iwd_security_label(n->security),
n->known_path ? ", saved" : "",
n->connected ? ", connected" : "");
amark);
elm_object_access_info_set(btn, access);
evas_object_size_hint_weight_set(btn, EVAS_HINT_EXPAND, 0);
evas_object_size_hint_align_set(btn, EVAS_HINT_FILL, 0);

View file

@ -22,7 +22,12 @@ _prop_cb(void *data, const char *key, Eldbus_Message_Iter *v)
Iwd_Network *n = data;
if (!strcmp(key, "Name")) { free(n->ssid); n->ssid = iwd_props_str_dup(v); }
else if (!strcmp(key, "Type")) { char *s = iwd_props_str_dup(v); n->security = _sec_from_str(s); free(s); }
else if (!strcmp(key, "Connected")) { n->connected = iwd_props_bool(v); }
else if (!strcmp(key, "Connected")) {
n->connected = iwd_props_bool(v);
/* A confirmed connection clears any in-flight/failed marker so the
* row settles on (and a stale ! from a prior attempt disappears). */
if (n->connected) n->conn_status = IWD_CONN_IDLE;
}
else if (!strcmp(key, "Device")) { free(n->device_path); n->device_path = iwd_props_str_dup(v); }
else if (!strcmp(key, "KnownNetwork")) { free(n->known_path); n->known_path = iwd_props_str_dup(v); }
}
@ -79,25 +84,50 @@ iwd_network_free(Iwd_Network *n)
free(n);
}
/* Reply context captures the *manager* (which outlives all sub-objects) and
* a strdup'd SSID, never the Iwd_Network the network may disappear from
* the next scan before iwd's reply lands, and a raw back-ref would UAF. */
/* Reply context captures the *manager* (which outlives all sub-objects) plus
* a strdup'd SSID and object path, never the Iwd_Network the network may
* disappear from the next scan before iwd's reply lands, and a raw back-ref
* would UAF. The path is re-resolved through the live hash at reply time. */
typedef struct
{
Iwd_Manager *m;
char *ssid;
char *path;
} _Net_Reply_Ctx;
static Iwd_Network *
_ctx_resolve(const _Net_Reply_Ctx *ctx)
{
if (!ctx->m || !ctx->path) return NULL;
const Eina_Hash *h = iwd_manager_networks(ctx->m);
return h ? eina_hash_find(h, ctx->path) : NULL;
}
static void
_on_connect_reply(void *data, const Eldbus_Message *msg, Eldbus_Pending *p EINA_UNUSED)
{
_Net_Reply_Ctx *ctx = data;
Iwd_Network *n = _ctx_resolve(ctx);
const char *en, *em;
if (eldbus_message_error_get(msg, &en, &em) && ctx->m)
if (eldbus_message_error_get(msg, &en, &em))
{
/* iwd only fails Connect() once the attempt is truly over — wrong
* passphrase, out of range, etc. Mark the row failed (!) rather than
* leaving a misleading behind. */
if (n) n->conn_status = IWD_CONN_FAILED;
if (ctx->m)
iwd_manager_report_error(ctx->m,
"Connect to '%s' failed: %s",
ctx->ssid ? ctx->ssid : "?", em ? em : en);
}
else
{
if (n) n->conn_status = IWD_CONN_IDLE;
/* report_error notifies on the failure path; do it here for success. */
if (ctx->m) iwd_manager_notify(ctx->m);
}
free(ctx->ssid);
free(ctx->path);
free(ctx);
}
@ -111,6 +141,7 @@ _on_forget_reply(void *data, const Eldbus_Message *msg, Eldbus_Pending *p EINA_U
"Forget '%s' failed: %s",
ctx->ssid ? ctx->ssid : "?", em ? em : en);
free(ctx->ssid);
free(ctx->path);
free(ctx);
}
@ -134,6 +165,7 @@ _reply_ctx_new(Iwd_Network *n)
if (!ctx) return NULL;
ctx->m = n->manager;
ctx->ssid = n->ssid ? strdup(n->ssid) : NULL;
ctx->path = n->path ? strdup(n->path) : NULL;
return ctx;
}
@ -141,6 +173,10 @@ void
iwd_network_connect(Iwd_Network *n)
{
if (!n || !n->proxy) return;
/* Show progress immediately and clear any prior failure marker; the reply
* (success/failure) and the Connected property settle the final state. */
n->conn_status = IWD_CONN_CONNECTING;
if (n->manager) iwd_manager_notify(n->manager);
/* Network.Connect() takes no args; iwd will dial the registered Agent
* for a passphrase if needed. */
eldbus_proxy_call(n->proxy, "Connect", _on_connect_reply,

View file

@ -13,6 +13,16 @@ typedef enum {
IWD_SEC_UNKNOWN,
} Iwd_Security;
/* Connection attempt status, tracked locally from our own Connect() call so
* the row can show progress and failure. iwd's Network.Connect() only returns
* once the attempt concludes, so its reply is authoritative Network.Connected
* may flicker true mid-handshake and must not be trusted on its own. */
typedef enum {
IWD_CONN_IDLE, /* not attempting; ✔ iff Network.Connected */
IWD_CONN_CONNECTING, /* Connect() in flight */
IWD_CONN_FAILED, /* last Connect() returned an error (e.g. bad PSK) */
} Iwd_Conn_Status;
typedef struct _Iwd_Network Iwd_Network;
struct _Iwd_Network
@ -23,6 +33,7 @@ struct _Iwd_Network
char *known_path;
Iwd_Security security;
Eina_Bool connected;
Iwd_Conn_Status conn_status;
/* Signal strength in 100*dBm units (iwd convention). Valid iff have_signal. */
int16_t signal_dbm;