stcp: ed25519 pubkey exchange in handshake, fix instance destroy segfault, fix chat_tcp_test
- u_async.c: process immediate_queue BEFORE epoll_wait in uasync_poll - stcp_link.c: defensive NULL etcp_conn/etcp_link in stcp_link_close - utun_instance.c: move stcp_server_list_destroy_all to Phase J (before ETCP), add deferred drain in both Phase J and L - stcp.h/c: STCP_HS_ENC_CLIENT 6→38, STCP_HS_ENC_SERVER 7→39, exchange ed25519_pubkey in both handshake directions - etcp_connections.c: copy peer_ed25519 from STCP to ETCP_CONN in etcp_link_enter_ready_tcp; guard link->etcp in tcp_link_close_cb - chat_sync.c: fix lk->conn NULL dereference for TCP links - test_stcp.c: update stcp_server_create/stcp_client_connect callsites - tools/chat_tcp_test: change transport udp→tcp
This commit is contained in:
1 parent
a3783b5f55
commit
38c9f229bc
1272 files changed
+968388
-1
No files matched your search
@@ -0,0 +1,219 @@
|
||||
#include "joindialog.h"
|
||||
#include "invite_link.h"
|
||||
#include "debug_ui.h"
|
||||
#include "../transport/utun_node.h"
|
||||
#include "../transport/gui_bridge.h"
|
||||
#include "chat/chat_sync.h"
|
||||
#include "../db/db_manager.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QTimer>
|
||||
#include <QMessageBox>
|
||||
#include <QApplication>
|
||||
#include <cstring>
|
||||
|
||||
extern "C" {
|
||||
#include "secure_channel.h"
|
||||
}
|
||||
|
||||
JoinDialog::JoinDialog(UtunNode* node, DbManager* db, QWidget* parent,
|
||||
const QString& autoJoinLink)
|
||||
: QDialog(parent)
|
||||
, m_node(node)
|
||||
, m_db(db)
|
||||
{
|
||||
setWindowTitle("Join Channel");
|
||||
setMinimumWidth(420);
|
||||
setModal(true);
|
||||
|
||||
auto *layout = new QVBoxLayout(this);
|
||||
layout->setSpacing(12);
|
||||
layout->setContentsMargins(20, 16, 20, 16);
|
||||
|
||||
auto *titleLabel = new QLabel("Paste invite link:", this);
|
||||
titleLabel->setStyleSheet("font-weight: bold; font-size: 13px;");
|
||||
layout->addWidget(titleLabel);
|
||||
|
||||
m_linkEdit = new QLineEdit(this);
|
||||
m_linkEdit->setPlaceholderText("utun://...");
|
||||
m_linkEdit->setStyleSheet(
|
||||
"QLineEdit { border: 1px solid palette(mid); border-radius: 6px;"
|
||||
" padding: 8px 12px; font-size: 13px; background: palette(base); }");
|
||||
layout->addWidget(m_linkEdit);
|
||||
|
||||
m_statusLabel = new QLabel(this);
|
||||
m_statusLabel->setWordWrap(true);
|
||||
m_statusLabel->setStyleSheet("color: gray; font-size: 12px; padding: 4px 0;");
|
||||
layout->addWidget(m_statusLabel);
|
||||
|
||||
layout->addStretch();
|
||||
|
||||
auto *btnLayout = new QHBoxLayout();
|
||||
btnLayout->setSpacing(8);
|
||||
btnLayout->addStretch();
|
||||
|
||||
m_cancelBtn = new QPushButton("Cancel", this);
|
||||
m_cancelBtn->setFixedWidth(90);
|
||||
m_cancelBtn->setStyleSheet(
|
||||
"QPushButton { border: 1px solid palette(mid); border-radius: 4px;"
|
||||
" padding: 6px 16px; font-size: 13px; background: palette(button); }"
|
||||
"QPushButton:hover { background: palette(light); }");
|
||||
connect(m_cancelBtn, &QPushButton::clicked, this, &QDialog::reject);
|
||||
btnLayout->addWidget(m_cancelBtn);
|
||||
|
||||
m_connectBtn = new QPushButton("Connect", this);
|
||||
m_connectBtn->setFixedWidth(90);
|
||||
m_connectBtn->setDefault(true);
|
||||
m_connectBtn->setStyleSheet(
|
||||
"QPushButton { border: none; border-radius: 4px;"
|
||||
" padding: 6px 16px; font-size: 13px; font-weight: bold;"
|
||||
" background: #3390EC; color: white; }"
|
||||
"QPushButton:hover { background: #2B7ED4; }"
|
||||
"QPushButton:disabled { background: palette(mid); color: palette(midlight); }");
|
||||
connect(m_connectBtn, &QPushButton::clicked, this, &JoinDialog::onConnectClicked);
|
||||
btnLayout->addWidget(m_connectBtn);
|
||||
|
||||
layout->addLayout(btnLayout);
|
||||
|
||||
connect(m_linkEdit, &QLineEdit::returnPressed, this, &JoinDialog::onConnectClicked);
|
||||
connect(m_linkEdit, &QLineEdit::textChanged, this, &JoinDialog::onLinkTextChanged);
|
||||
|
||||
m_decodeTimer = new QTimer(this);
|
||||
m_decodeTimer->setSingleShot(true);
|
||||
m_decodeTimer->setInterval(400);
|
||||
connect(m_decodeTimer, &QTimer::timeout, this, [this]() {
|
||||
QString link = m_linkEdit->text().trimmed();
|
||||
if (link.isEmpty()) {
|
||||
setStatus("");
|
||||
return;
|
||||
}
|
||||
InviteData d = decodeInviteLink(link);
|
||||
if (!d.error.isEmpty() || d.pubkey.size() != 32) {
|
||||
setStatus("");
|
||||
return;
|
||||
}
|
||||
uint64_t nodeId = sc_derive_node_id_from_pubkey((const uint8_t*)d.pubkey.constData());
|
||||
|
||||
QStringList addrList;
|
||||
for (const auto& a : d.addrs) {
|
||||
QString addrStr;
|
||||
if (a.family == 4 && a.address.size() == 4) {
|
||||
const uint8_t* b = (const uint8_t*)a.address.constData();
|
||||
addrStr = QString("%1.%2.%3.%4").arg(b[0]).arg(b[1]).arg(b[2]).arg(b[3]);
|
||||
} else {
|
||||
addrStr = QString("ipv6:%1").arg(a.address.toHex().constData());
|
||||
}
|
||||
addrList.append(QString("%1:%2").arg(addrStr).arg(a.port));
|
||||
}
|
||||
setStatus(QString("Node: 0x%1 | %2")
|
||||
.arg(nodeId, 16, 16, QLatin1Char('0'))
|
||||
.arg(addrList.join(", ")));
|
||||
});
|
||||
|
||||
gui_bridge_set_connect_result_cb([](uint64_t nodeId, uint64_t channelId, int result) {
|
||||
JoinDialog* dlg = qobject_cast<JoinDialog*>(QApplication::activeModalWidget());
|
||||
if (dlg) dlg->onConnectResult(nodeId, channelId, result);
|
||||
});
|
||||
|
||||
if (!autoJoinLink.isEmpty()) {
|
||||
m_linkEdit->setText(autoJoinLink);
|
||||
QTimer::singleShot(0, this, &JoinDialog::onConnectClicked);
|
||||
}
|
||||
}
|
||||
|
||||
JoinDialog::~JoinDialog() {
|
||||
gui_bridge_set_connect_result_cb(nullptr);
|
||||
}
|
||||
|
||||
void JoinDialog::setStatus(const QString& text, bool isError) {
|
||||
if (isError)
|
||||
m_statusLabel->setStyleSheet("color: #D0021B; font-size: 12px; padding: 4px 0;");
|
||||
else
|
||||
m_statusLabel->setStyleSheet("color: gray; font-size: 12px; padding: 4px 0;");
|
||||
m_statusLabel->setText(text);
|
||||
}
|
||||
|
||||
void JoinDialog::onLinkTextChanged() {
|
||||
m_decodeTimer->start();
|
||||
}
|
||||
|
||||
void JoinDialog::onConnectClicked() {
|
||||
if (m_connecting) return;
|
||||
QString link = m_linkEdit->text().trimmed();
|
||||
if (link.isEmpty()) {
|
||||
setStatus("Enter an invite link.", true);
|
||||
return;
|
||||
}
|
||||
|
||||
setStatus("Parsing link...");
|
||||
InviteData d = decodeInviteLink(link);
|
||||
if (!d.error.isEmpty()) {
|
||||
setStatus("Invalid invite link: " + d.error, true);
|
||||
return;
|
||||
}
|
||||
if (d.pubkey.size() != 32) {
|
||||
setStatus(QString("Invalid invite link: pubkey is %1 bytes (need 32)").arg(d.pubkey.size()), true);
|
||||
return;
|
||||
}
|
||||
if (d.addrs.isEmpty()) {
|
||||
setStatus("No peer addresses in the invite link.", true);
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t nodeId = sc_derive_node_id_from_pubkey((const uint8_t*)d.pubkey.constData());
|
||||
|
||||
GUI_INFO("join: decoded invite ch=%llu pubkey=%016llx... nodeId=0x%016llx addrs=%d",
|
||||
d.channelId, *(const uint64_t*)d.pubkey.constData(), nodeId, d.addrs.size());
|
||||
|
||||
m_pendingNodeId = nodeId;
|
||||
m_pendingChannelId = d.channelId;
|
||||
m_connecting = true;
|
||||
m_connectBtn->setEnabled(false);
|
||||
m_linkEdit->setReadOnly(true);
|
||||
|
||||
setStatus("Connecting to peer...");
|
||||
|
||||
QByteArray addrsBuf;
|
||||
for (const auto& a : d.addrs) {
|
||||
addrsBuf.append(static_cast<char>(a.family));
|
||||
addrsBuf.append(static_cast<char>(a.socketId));
|
||||
addrsBuf.append(static_cast<char>(a.proto));
|
||||
addrsBuf.append(a.address);
|
||||
addrsBuf.append(static_cast<char>((a.port >> 8) & 0xFF));
|
||||
addrsBuf.append(static_cast<char>(a.port & 0xFF));
|
||||
}
|
||||
|
||||
int addrCount = d.addrs.size();
|
||||
const char* pass = d.password.isEmpty() ? NULL : d.password.toUtf8().constData();
|
||||
|
||||
chat_sync_connect_from_invite(d.channelId, nodeId,
|
||||
(const uint8_t*)d.pubkey.constData(),
|
||||
(const uint8_t*)addrsBuf.constData(), addrCount, addrsBuf.size(), pass);
|
||||
}
|
||||
|
||||
void JoinDialog::onConnectResult(uint64_t nodeId, uint64_t channelId, int result) {
|
||||
if (!m_connecting || nodeId != m_pendingNodeId) return;
|
||||
|
||||
if (result == 0) {
|
||||
setStatus("Connected!");
|
||||
QString cid = QString::number(m_pendingChannelId);
|
||||
emit joined(cid);
|
||||
accept();
|
||||
} else {
|
||||
m_connecting = false;
|
||||
m_connectBtn->setEnabled(true);
|
||||
m_linkEdit->setReadOnly(false);
|
||||
m_connectBtn->setFocus();
|
||||
|
||||
const char* errMsg = "Unknown error.";
|
||||
switch (result) {
|
||||
case -1: errMsg = "Channel not found."; break;
|
||||
case -3: errMsg = "Connection timed out."; break;
|
||||
case -4: errMsg = "Peer is unreachable."; break;
|
||||
case -6: errMsg = "Already connected to this peer."; break;
|
||||
default: break;
|
||||
}
|
||||
setStatus(QString("Connection failed: %1").arg(errMsg), true);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user