mirror of
https://github.com/MatsuriDayo/nekoray.git
synced 2025-12-17 20:44:38 +03:00
support vmess header
This commit is contained in:
@@ -17,18 +17,23 @@ namespace NekoRay::fmt {
|
|||||||
|
|
||||||
if (network == "ws") {
|
if (network == "ws") {
|
||||||
QJsonObject ws;
|
QJsonObject ws;
|
||||||
if (!path.trimmed().isEmpty()) ws["path"] = path;
|
if (!path.isEmpty()) ws["path"] = path;
|
||||||
if (!host.trimmed().isEmpty()) ws["headers"] = QJsonObject{{"Host", host}};
|
if (!host.isEmpty()) ws["headers"] = QJsonObject{{"Host", host}};
|
||||||
streamSettings["wsSettings"] = ws;
|
streamSettings["wsSettings"] = ws;
|
||||||
} else if (network == "h2") {
|
} else if (network == "h2") {
|
||||||
QJsonObject h2;
|
QJsonObject h2;
|
||||||
if (!path.trimmed().isEmpty()) h2["path"] = path;
|
if (!path.isEmpty()) h2["path"] = path;
|
||||||
if (!host.trimmed().isEmpty()) h2["host"] = QList2QJsonArray(host.split(","));
|
if (!host.isEmpty()) h2["host"] = QList2QJsonArray(host.split(","));
|
||||||
streamSettings["httpSettings"] = h2;
|
streamSettings["httpSettings"] = h2;
|
||||||
} else if (network == "grpc") {
|
} else if (network == "grpc") {
|
||||||
QJsonObject grpc;
|
QJsonObject grpc;
|
||||||
if (!path.trimmed().isEmpty()) grpc["serviceName"] = path;
|
if (!path.isEmpty()) grpc["serviceName"] = path;
|
||||||
streamSettings["grpcSettings"] = grpc;
|
streamSettings["grpcSettings"] = grpc;
|
||||||
|
} else if (network == "quic") {
|
||||||
|
QJsonObject quic;
|
||||||
|
if (!path.isEmpty()) quic["key"] = path;
|
||||||
|
if (!host.isEmpty()) quic["security"] = host;
|
||||||
|
streamSettings["quicSettings"] = quic;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (security == "tls") {
|
if (security == "tls") {
|
||||||
@@ -48,6 +53,18 @@ namespace NekoRay::fmt {
|
|||||||
streamSettings["tlsSettings"] = tls;
|
streamSettings["tlsSettings"] = tls;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!header_type.isEmpty()) {
|
||||||
|
QJsonObject header{{"type", header_type}};
|
||||||
|
if (header_type == "http") {
|
||||||
|
QJsonObject request{
|
||||||
|
{"path", QList2QJsonArray(path.split(","))},
|
||||||
|
{"headers", QJsonObject{{"Host", QList2QJsonArray(host.split(","))}}},
|
||||||
|
};
|
||||||
|
header["request"] = request;
|
||||||
|
}
|
||||||
|
streamSettings["header"] = header;
|
||||||
|
}
|
||||||
|
|
||||||
return streamSettings;
|
return streamSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,18 +62,17 @@ namespace NekoRay::fmt {
|
|||||||
|
|
||||||
QString VMessBean::ToShareLink() {
|
QString VMessBean::ToShareLink() {
|
||||||
QJsonObject N{
|
QJsonObject N{
|
||||||
{"v", 2},
|
{"v", "2"},
|
||||||
{"ps", name},
|
{"ps", name},
|
||||||
{"add", serverAddress},
|
{"add", serverAddress},
|
||||||
{"port", serverPort},
|
{"port", Int2String(serverPort)},
|
||||||
{"id", uuid},
|
{"id", uuid},
|
||||||
{"aid", aid},
|
{"aid", Int2String(aid)},
|
||||||
{"net", stream->network},
|
{"net", stream->network},
|
||||||
{"host", stream->host},
|
{"host", stream->host},
|
||||||
{"path", stream->path},
|
{"path", stream->path},
|
||||||
{"type", stream->header_type},
|
{"type", stream->header_type},
|
||||||
{"scy", security},
|
{"scy", security},
|
||||||
// TODO header type
|
|
||||||
{"tls", stream->security == "tls" ? "tls" : ""},
|
{"tls", stream->security == "tls" ? "tls" : ""},
|
||||||
{"sni", stream->sni},
|
{"sni", stream->sni},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -113,11 +113,11 @@ namespace NekoRay::fmt {
|
|||||||
serverPort = objN["port"].toVariant().toInt();
|
serverPort = objN["port"].toVariant().toInt();
|
||||||
// OPTIONAL
|
// OPTIONAL
|
||||||
name = objN["ps"].toString();
|
name = objN["ps"].toString();
|
||||||
auto aid_ = objN["aid"];
|
aid = objN["aid"].toVariant().toInt();
|
||||||
aid = aid_.isString() ? aid_.toString().toInt() : aid_.toInt();
|
|
||||||
stream->host = objN["host"].toString();
|
stream->host = objN["host"].toString();
|
||||||
stream->path = objN["path"].toString();
|
stream->path = objN["path"].toString();
|
||||||
stream->sni = objN["sni"].toString();
|
stream->sni = objN["sni"].toString();
|
||||||
|
stream->header_type = objN["type"].toString();
|
||||||
auto net = objN["net"].toString().replace("http", "h2");
|
auto net = objN["net"].toString().replace("http", "h2");
|
||||||
if (!net.isEmpty()) stream->network = net;
|
if (!net.isEmpty()) stream->network = net;
|
||||||
auto scy = objN["scy"].toString();
|
auto scy = objN["scy"].toString();
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ namespace NekoRay::fmt {
|
|||||||
QString network = "tcp";
|
QString network = "tcp";
|
||||||
QString security = "none";
|
QString security = "none";
|
||||||
QString packet_encoding = "";
|
QString packet_encoding = "";
|
||||||
// ws/h2/grpc
|
// ws/h2/grpc/tcp-http
|
||||||
QString path = "";
|
QString path = "";
|
||||||
QString host = "";
|
QString host = "";
|
||||||
// QUIC & KCP
|
// kcp/quic/tcp-http
|
||||||
QString header_type = "";
|
QString header_type = "";
|
||||||
// tls
|
// tls
|
||||||
QString sni = "";
|
QString sni = "";
|
||||||
|
|||||||
@@ -164,6 +164,15 @@ namespace NekoRay::sub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NodeChild returns the first defined children or Null Node
|
||||||
|
YAML::Node NodeChild(const YAML::Node &n, const std::list<std::string> &keys) {
|
||||||
|
for (const auto &key: keys) {
|
||||||
|
auto child = n[key];
|
||||||
|
if (child.IsDefined()) return child;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// https://github.com/Dreamacro/clash/wiki/configuration
|
// https://github.com/Dreamacro/clash/wiki/configuration
|
||||||
@@ -223,7 +232,7 @@ namespace NekoRay::sub {
|
|||||||
if (Node2Bool(proxy["tls"])) bean->stream->security = "tls";
|
if (Node2Bool(proxy["tls"])) bean->stream->security = "tls";
|
||||||
if (Node2Bool(proxy["skip-cert-verify"])) bean->stream->allow_insecure = true;
|
if (Node2Bool(proxy["skip-cert-verify"])) bean->stream->allow_insecure = true;
|
||||||
|
|
||||||
auto ws = proxy["ws-opts"];
|
auto ws = NodeChild(proxy, {"ws-opts", "ws-opt"});
|
||||||
if (ws.IsMap()) {
|
if (ws.IsMap()) {
|
||||||
auto headers = ws["headers"];
|
auto headers = ws["headers"];
|
||||||
for (auto header: headers) {
|
for (auto header: headers) {
|
||||||
@@ -234,12 +243,12 @@ namespace NekoRay::sub {
|
|||||||
bean->stream->path = Node2QString(ws["path"]);
|
bean->stream->path = Node2QString(ws["path"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto grpc = proxy["grpc-opts"];
|
auto grpc = NodeChild(proxy, {"grpc-opts", "grpc-opt"});
|
||||||
if (grpc.IsMap()) {
|
if (grpc.IsMap()) {
|
||||||
bean->stream->path = Node2QString(grpc["grpc-service-name"]);
|
bean->stream->path = Node2QString(grpc["grpc-service-name"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto h2 = proxy["h2-opts"];
|
auto h2 = NodeChild(proxy, {"h2-opts", "h2-opt"});
|
||||||
if (h2.IsMap()) {
|
if (h2.IsMap()) {
|
||||||
auto hosts = ws["host"];
|
auto hosts = ws["host"];
|
||||||
for (auto host: hosts) {
|
for (auto host: hosts) {
|
||||||
@@ -248,6 +257,24 @@ namespace NekoRay::sub {
|
|||||||
}
|
}
|
||||||
bean->stream->path = Node2QString(h2["path"]);
|
bean->stream->path = Node2QString(h2["path"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto tcp_http = NodeChild(proxy, {"http-opts", "http-opt"});
|
||||||
|
if (tcp_http.IsMap()) {
|
||||||
|
bean->stream->network = "tcp";
|
||||||
|
bean->stream->header_type = "http";
|
||||||
|
auto headers = tcp_http["headers"];
|
||||||
|
for (auto header: headers) {
|
||||||
|
if (Node2QString(header.first).toLower() == "host") {
|
||||||
|
bean->stream->host = Node2QString(header.second[0]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto paths = tcp_http["path"];
|
||||||
|
for (auto path: paths) {
|
||||||
|
bean->stream->path = Node2QString(path);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,18 +31,35 @@ DialogEditProfile::DialogEditProfile(const QString &_type, int profileOrGroupId,
|
|||||||
// network changed
|
// network changed
|
||||||
network_title_base = ui->network_box->title();
|
network_title_base = ui->network_box->title();
|
||||||
connect(ui->network, &QComboBox::currentTextChanged, this, [=](const QString &txt) {
|
connect(ui->network, &QComboBox::currentTextChanged, this, [=](const QString &txt) {
|
||||||
if (txt == "tcp" || txt == "quic") {
|
|
||||||
ui->network_box->setVisible(false);
|
|
||||||
} else {
|
|
||||||
ui->network_box->setVisible(true);
|
|
||||||
ui->network_box->setTitle(network_title_base.arg(txt));
|
ui->network_box->setTitle(network_title_base.arg(txt));
|
||||||
if (txt == "grpc") {
|
if (txt == "tcp" || txt == "quic") {
|
||||||
ui->host->setVisible(false);
|
ui->header_type->setVisible(true);
|
||||||
ui->host_l->setVisible(false);
|
ui->header_type_l->setVisible(true);
|
||||||
} else {
|
ui->path->setVisible(true);
|
||||||
|
ui->path_l->setVisible(true);
|
||||||
ui->host->setVisible(true);
|
ui->host->setVisible(true);
|
||||||
ui->host_l->setVisible(true);
|
ui->host_l->setVisible(true);
|
||||||
}
|
} else if (txt == "grpc") {
|
||||||
|
ui->header_type->setVisible(false);
|
||||||
|
ui->header_type_l->setVisible(false);
|
||||||
|
ui->path->setVisible(true);
|
||||||
|
ui->path_l->setVisible(true);
|
||||||
|
ui->host->setVisible(false);
|
||||||
|
ui->host_l->setVisible(false);
|
||||||
|
} else if (txt == "ws" || txt == "h2") {
|
||||||
|
ui->header_type->setVisible(false);
|
||||||
|
ui->header_type_l->setVisible(false);
|
||||||
|
ui->path->setVisible(true);
|
||||||
|
ui->path_l->setVisible(true);
|
||||||
|
ui->host->setVisible(true);
|
||||||
|
ui->host_l->setVisible(true);
|
||||||
|
} else {
|
||||||
|
ui->header_type->setVisible(false);
|
||||||
|
ui->header_type_l->setVisible(false);
|
||||||
|
ui->path->setVisible(false);
|
||||||
|
ui->path_l->setVisible(false);
|
||||||
|
ui->host->setVisible(false);
|
||||||
|
ui->host_l->setVisible(false);
|
||||||
}
|
}
|
||||||
ADJUST_SIZE
|
ADJUST_SIZE
|
||||||
});
|
});
|
||||||
@@ -167,6 +184,7 @@ void DialogEditProfile::typeSelected(const QString &newType) {
|
|||||||
ui->sni->setText(stream->sni);
|
ui->sni->setText(stream->sni);
|
||||||
ui->alpn->setText(stream->alpn);
|
ui->alpn->setText(stream->alpn);
|
||||||
ui->insecure->setChecked(stream->allow_insecure);
|
ui->insecure->setChecked(stream->allow_insecure);
|
||||||
|
ui->header_type->setCurrentText(stream->header_type);
|
||||||
CACHE.certificate = stream->certificate;
|
CACHE.certificate = stream->certificate;
|
||||||
} else {
|
} else {
|
||||||
ui->right_all_w->setVisible(false);
|
ui->right_all_w->setVisible(false);
|
||||||
@@ -241,6 +259,7 @@ void DialogEditProfile::accept() {
|
|||||||
stream->sni = ui->sni->text();
|
stream->sni = ui->sni->text();
|
||||||
stream->alpn = ui->alpn->text();
|
stream->alpn = ui->alpn->text();
|
||||||
stream->allow_insecure = ui->insecure->isChecked();
|
stream->allow_insecure = ui->insecure->isChecked();
|
||||||
|
stream->header_type = ui->header_type->currentText();
|
||||||
stream->certificate = CACHE.certificate;
|
stream->certificate = CACHE.certificate;
|
||||||
}
|
}
|
||||||
auto custom_item = ent->bean->_get("custom");
|
auto custom_item = ent->bean->_get("custom");
|
||||||
|
|||||||
@@ -315,31 +315,63 @@
|
|||||||
<string>Network Settings (%1)</string>
|
<string>Network Settings (%1)</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="0" column="0">
|
<item row="1" column="1">
|
||||||
<widget class="QLabel" name="label_7">
|
<widget class="MyLineEdit" name="path"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="path_l">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string notr="true">http path (ws/http) 或 serviceName (gRPC)</string>
|
<string notr="true">http path (ws/h2/伪装http)
|
||||||
|
serviceName (gRPC)
|
||||||
|
key (QUIC)</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Path</string>
|
<string>Path</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="2" column="1">
|
||||||
<widget class="MyLineEdit" name="path"/>
|
<widget class="MyLineEdit" name="host"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="2" column="0">
|
||||||
<widget class="QLabel" name="host_l">
|
<widget class="QLabel" name="host_l">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string notr="true">http host</string>
|
<string notr="true">http host (ws/h2/伪装http)
|
||||||
|
security (QUIC)</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Host</string>
|
<string>Host</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="0" column="0">
|
||||||
<widget class="MyLineEdit" name="host"/>
|
<widget class="QLabel" name="header_type_l">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string notr="true">伪装头部 (tcp/quic)</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true">headerType</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QComboBox" name="header_type">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="editable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
|||||||
Reference in New Issue
Block a user