chatgui: invite link connect flow + GUI_ERROR always-on diagnostics
- debug_ui.h: remove NDEBUG guard so GUI_ERROR works in Release builds - invite_link.h/cpp: add error field to InviteData with specific failure reason - joindialog: show exact decode error in QLabel instead of generic message - chat_sync: implement invite connect flow (CHANNEL_INFO_REQ/RESP, JOIN, WELCOME) - topo_node_sqlite: node/address table init and lookup - utun_node: getInviteAddresses fallback from ETCP sockets - minor fixes in mainwindow, chat_core, gui_bridge
This commit is contained in:
1 parent
102816d0dc
commit
85a91a1320
17 files changed
+747
-127
No files matched your search
@@ -2,62 +2,48 @@
|
||||
#include "debug_ui.h"
|
||||
#include <QByteArray>
|
||||
|
||||
static QByteArray encodeAddresses(const QList<NodeAddr>& addrs) {
|
||||
QByteArray buf;
|
||||
for (const auto& a : addrs) {
|
||||
buf.append(static_cast<char>(a.family));
|
||||
buf.append(a.address);
|
||||
buf.append(static_cast<char>((a.port >> 8) & 0xFF));
|
||||
buf.append(static_cast<char>(a.port & 0xFF));
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
static QList<InviteAddr> decodeAddresses(const uint8_t* data, int len,
|
||||
int count) {
|
||||
QList<InviteAddr> list;
|
||||
int off = 0;
|
||||
for (int i = 0; i < count && off < len; i++) {
|
||||
if (off + 1 > len) break;
|
||||
InviteAddr a;
|
||||
a.family = data[off++];
|
||||
int alen = (a.family == 4) ? 4 : 16;
|
||||
if (off + alen + 2 > len) break;
|
||||
a.address = QByteArray(reinterpret_cast<const char*>(data + off), alen);
|
||||
off += alen;
|
||||
a.port = (static_cast<quint16>(data[off]) << 8) | data[off + 1];
|
||||
off += 2;
|
||||
list.append(a);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
QString encodeInviteLink(const QString& channelId, const QByteArray& pubkey,
|
||||
QString encodeInviteLink(uint64_t channelId, const QByteArray& pubkey,
|
||||
const QList<NodeAddr>& addrs) {
|
||||
QByteArray cid = channelId.toUtf8();
|
||||
if (cid.size() > 65535) {
|
||||
GUI_ERROR("encodeInviteLink: channelId too long (%d bytes)", cid.size());
|
||||
return {};
|
||||
}
|
||||
if (pubkey.size() != 32) {
|
||||
GUI_ERROR("encodeInviteLink: pubkey must be 32 bytes (got %d)", pubkey.size());
|
||||
return {};
|
||||
}
|
||||
|
||||
QByteArray addrsEnc = encodeAddresses(addrs);
|
||||
int addrCount = addrs.size();
|
||||
if (addrCount > 65535) addrCount = 65535;
|
||||
if (addrs.isEmpty()) {
|
||||
GUI_ERROR("encodeInviteLink: no addresses");
|
||||
return {};
|
||||
}
|
||||
|
||||
QByteArray blob;
|
||||
blob.reserve(1 + 2 + cid.size() + 32 + 2 + addrsEnc.size());
|
||||
blob.reserve(1 + 8 + addrs.size() * (1 + 32 + 18));
|
||||
|
||||
blob.append(static_cast<char>(INVITE_LINK_VERSION));
|
||||
blob.append(static_cast<char>((cid.size() >> 8) & 0xFF));
|
||||
blob.append(static_cast<char>(cid.size() & 0xFF));
|
||||
blob.append(cid);
|
||||
blob.append(pubkey);
|
||||
blob.append(static_cast<char>((addrCount >> 8) & 0xFF));
|
||||
blob.append(static_cast<char>(addrCount & 0xFF));
|
||||
blob.append(addrsEnc);
|
||||
|
||||
/* channel_id: uint64 big-endian */
|
||||
for (int i = 7; i >= 0; i--)
|
||||
blob.append(static_cast<char>((channelId >> (i * 8)) & 0xFF));
|
||||
|
||||
int i = 0;
|
||||
while (i < addrs.size()) {
|
||||
int rem = addrs.size() - i;
|
||||
int cnt = (rem > 4) ? 4 : rem;
|
||||
|
||||
/* header: bits 1-0 = count-1, bits 5-2 = family bits */
|
||||
uint8_t header = (cnt - 1) & 0x03;
|
||||
for (int j = 0; j < cnt; j++) {
|
||||
if (addrs[i + j].family == 6)
|
||||
header |= (1 << (2 + j));
|
||||
}
|
||||
blob.append(static_cast<char>(header));
|
||||
blob.append(pubkey);
|
||||
|
||||
for (int j = 0; j < cnt; j++) {
|
||||
blob.append(addrs[i + j].address);
|
||||
quint16 p = addrs[i + j].port;
|
||||
blob.append(static_cast<char>((p >> 8) & 0xFF));
|
||||
blob.append(static_cast<char>(p & 0xFF));
|
||||
}
|
||||
i += cnt;
|
||||
}
|
||||
|
||||
return QString::fromLatin1("utun://") + QString::fromLatin1(blob.toBase64());
|
||||
}
|
||||
@@ -65,36 +51,80 @@ QString encodeInviteLink(const QString& channelId, const QByteArray& pubkey,
|
||||
InviteData decodeInviteLink(const QString& link) {
|
||||
InviteData d;
|
||||
if (!link.startsWith(QLatin1String("utun://"))) {
|
||||
d.error = QStringLiteral("invalid prefix (expected utun://)");
|
||||
GUI_ERROR("decodeInviteLink: invalid prefix");
|
||||
return d;
|
||||
}
|
||||
QByteArray blob = QByteArray::fromBase64(
|
||||
link.mid(7).toLatin1());
|
||||
QByteArray blob = QByteArray::fromBase64(link.mid(7).toLatin1());
|
||||
if (blob.isEmpty()) {
|
||||
d.error = QStringLiteral("base64 decode failed");
|
||||
GUI_ERROR("decodeInviteLink: base64 decode failed");
|
||||
return d;
|
||||
}
|
||||
const auto* data = reinterpret_cast<const uint8_t*>(blob.constData());
|
||||
int len = blob.size();
|
||||
|
||||
if (len < 5) {
|
||||
if (len < 11) {
|
||||
d.error = QStringLiteral("blob too short (%1 bytes)").arg(len);
|
||||
GUI_ERROR("decodeInviteLink: blob too short (%d bytes)", len);
|
||||
return d;
|
||||
}
|
||||
int off = 0;
|
||||
uint8_t ver = data[off++];
|
||||
if (ver != INVITE_LINK_VERSION) {
|
||||
GUI_ERROR("decodeInviteLink: unsupported version %d", ver);
|
||||
|
||||
if (data[off++] != INVITE_LINK_VERSION) {
|
||||
d.error = QStringLiteral("unsupported version 0x%1").arg(data[off - 1], 2, 16, QLatin1Char('0'));
|
||||
GUI_ERROR("decodeInviteLink: unsupported version %d", data[off - 1]);
|
||||
return d;
|
||||
}
|
||||
int cidLen = (static_cast<int>(data[off]) << 8) | data[off + 1];
|
||||
off += 2;
|
||||
if (off + cidLen + 32 + 2 > len) {
|
||||
GUI_ERROR("decodeInviteLink: truncated blob");
|
||||
|
||||
/* channel_id: uint64 big-endian */
|
||||
if (off + 8 > len) {
|
||||
d.error = QStringLiteral("truncated at channel_id");
|
||||
GUI_ERROR("decodeInviteLink: truncated at channel_id");
|
||||
return d;
|
||||
}
|
||||
d.channelId = QString::fromUtf8(reinterpret_cast<const char*>(data + off), cidLen);
|
||||
off += cidLen;
|
||||
d.pubkey = QByteArray(reinterpret_cast<const char*>(data + off), 32);
|
||||
off += 32;
|
||||
int addrCount = (static_cast<int>(data[off]) << 8) | data[off + 1];
|
||||
off += 2;
|
||||
d.addrs = decodeAddresses(data + off, len - off, addrCount);
|
||||
uint64_t chId = 0;
|
||||
for (int i = 0; i < 8; i++)
|
||||
chId = (chId << 8) | data[off++];
|
||||
d.channelId = chId;
|
||||
|
||||
bool firstBlock = true;
|
||||
while (off + 1 <= len) {
|
||||
uint8_t header = data[off++];
|
||||
int cnt = (header & 0x03) + 1;
|
||||
if (cnt < 1 || cnt > 4) {
|
||||
d.error = QStringLiteral("invalid addr count %1").arg(cnt);
|
||||
GUI_ERROR("decodeInviteLink: invalid addr count %d", cnt);
|
||||
return d;
|
||||
}
|
||||
if (off + 32 > len) {
|
||||
d.error = QStringLiteral("truncated at pubkey");
|
||||
GUI_ERROR("decodeInviteLink: truncated at pubkey");
|
||||
return d;
|
||||
}
|
||||
if (firstBlock) {
|
||||
d.pubkey = QByteArray(reinterpret_cast<const char*>(data + off), 32);
|
||||
firstBlock = false;
|
||||
}
|
||||
off += 32;
|
||||
|
||||
for (int j = 0; j < cnt; j++) {
|
||||
bool isV6 = header & (1 << (2 + j));
|
||||
int ipLen = isV6 ? 16 : 4;
|
||||
if (off + ipLen + 2 > len) {
|
||||
d.error = QStringLiteral("truncated at addr %1 of block").arg(j);
|
||||
GUI_ERROR("decodeInviteLink: truncated at addr %d of block", j);
|
||||
return d;
|
||||
}
|
||||
InviteAddr a;
|
||||
a.family = isV6 ? 6 : 4;
|
||||
a.address = QByteArray(reinterpret_cast<const char*>(data + off), ipLen);
|
||||
off += ipLen;
|
||||
a.port = (static_cast<quint16>(data[off]) << 8) | data[off + 1];
|
||||
off += 2;
|
||||
d.addrs.append(a);
|
||||
}
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
Reference in new issue
Block a user