mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-05-08 14:33:23 +00:00
fix: outbound freedom for xray (#2479)
* fix: outbound freedom for xray on linux * fix: outbound freedom for xray on macOS * build: auto-generate pf rules based on the build type
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -81,6 +81,7 @@ client/.DS_Store
|
|||||||
._.DS_Store
|
._.DS_Store
|
||||||
._*
|
._*
|
||||||
*.dmg
|
*.dmg
|
||||||
|
deploy/data/macos/pf/amn.400.allowPIA.conf
|
||||||
|
|
||||||
# tmp files
|
# tmp files
|
||||||
*.*~
|
*.*~
|
||||||
|
|||||||
@@ -42,6 +42,27 @@ if(APPLE)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(APPLE AND NOT IOS)
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
set(AMN_PF_RULE_IDENTITY "user { root }")
|
||||||
|
else()
|
||||||
|
set(AMN_PF_RULE_IDENTITY "group { amnvpn }")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
"${CMAKE_SOURCE_DIR}/deploy/data/pf-templates/amn.400.allowPIA.conf.in"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/amn.400.allowPIA.conf"
|
||||||
|
@ONLY
|
||||||
|
)
|
||||||
|
|
||||||
|
file(COPY_FILE
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/amn.400.allowPIA.conf"
|
||||||
|
"${CMAKE_SOURCE_DIR}/deploy/data/macos/pf/amn.400.allowPIA.conf"
|
||||||
|
ONLY_IF_DIFFERENT
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
add_subdirectory(client)
|
add_subdirectory(client)
|
||||||
|
|
||||||
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
|
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
#include "linuxfirewall.h"
|
#include "linuxfirewall.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
#include "xray_defs.h"
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
|
|
||||||
#define BRAND_CODE "amn"
|
#define BRAND_CODE "amn"
|
||||||
@@ -282,6 +283,10 @@ void LinuxFirewall::install()
|
|||||||
QStringLiteral("-o tun2+ -j ACCEPT"),
|
QStringLiteral("-o tun2+ -j ACCEPT"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
installAnchor(Both, QStringLiteral("130.allowMarkedXray"), {
|
||||||
|
QStringLiteral("-m mark --mark %1 -j ACCEPT").arg(amnezia::xray::xrayTrafficMark),
|
||||||
|
});
|
||||||
|
|
||||||
installAnchor(IPv4, QStringLiteral("120.blockNets"), {});
|
installAnchor(IPv4, QStringLiteral("120.blockNets"), {});
|
||||||
|
|
||||||
installAnchor(IPv4, QStringLiteral("110.allowNets"), {});
|
installAnchor(IPv4, QStringLiteral("110.allowNets"), {});
|
||||||
@@ -358,6 +363,7 @@ void LinuxFirewall::uninstall()
|
|||||||
uninstallAnchor(IPv6, QStringLiteral("250.blockIPv6"));
|
uninstallAnchor(IPv6, QStringLiteral("250.blockIPv6"));
|
||||||
uninstallAnchor(Both, QStringLiteral("200.allowVPN"));
|
uninstallAnchor(Both, QStringLiteral("200.allowVPN"));
|
||||||
uninstallAnchor(IPv4, QStringLiteral("120.blockNets"));
|
uninstallAnchor(IPv4, QStringLiteral("120.blockNets"));
|
||||||
|
uninstallAnchor(Both, QStringLiteral("130.allowMarkedXray"));
|
||||||
uninstallAnchor(IPv4, QStringLiteral("110.allowNets"));
|
uninstallAnchor(IPv4, QStringLiteral("110.allowNets"));
|
||||||
uninstallAnchor(Both, QStringLiteral("100.blockAll"));
|
uninstallAnchor(Both, QStringLiteral("100.blockAll"));
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,13 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
|
|||||||
QString dns1 = m_vpnConfiguration.value(configKey::dns1).toString();
|
QString dns1 = m_vpnConfiguration.value(configKey::dns1).toString();
|
||||||
QString dns2 = m_vpnConfiguration.value(configKey::dns2).toString();
|
QString dns2 = m_vpnConfiguration.value(configKey::dns2).toString();
|
||||||
|
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
if (!m_appSettingsRepository->isSitesSplitTunnelingEnabled() || m_appSettingsRepository->routeMode() != amnezia::RouteMode::VpnAllExceptSites) {
|
||||||
|
iface->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << dns1 << dns2);
|
||||||
|
}
|
||||||
|
#else
|
||||||
iface->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << dns1 << dns2);
|
iface->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << dns1 << dns2);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (m_appSettingsRepository->isSitesSplitTunnelingEnabled()) {
|
if (m_appSettingsRepository->isSitesSplitTunnelingEnabled()) {
|
||||||
iface->routeDeleteList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0");
|
iface->routeDeleteList(m_vpnProtocol->vpnGateway(), QStringList() << "0.0.0.0");
|
||||||
@@ -105,6 +111,9 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
|
|||||||
iface->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << "128.0.0.0/1");
|
iface->routeAddList(m_vpnProtocol->vpnGateway(), QStringList() << "128.0.0.0/1");
|
||||||
|
|
||||||
iface->routeAddList(m_vpnProtocol->routeGateway(), QStringList() << remoteAddress());
|
iface->routeAddList(m_vpnProtocol->routeGateway(), QStringList() << remoteAddress());
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
iface->routeAddList(m_vpnProtocol->routeGateway(), QStringList() << dns1 << dns2);
|
||||||
|
#endif
|
||||||
addSitesRoutes(m_vpnProtocol->routeGateway(), routeMode);
|
addSitesRoutes(m_vpnProtocol->routeGateway(), routeMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
<true/>
|
<true/>
|
||||||
<key>RunAtLoad</key>
|
<key>RunAtLoad</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>GroupName</key>
|
||||||
|
<string>amnvpn</string>
|
||||||
<key>Sockets</key>
|
<key>Sockets</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>Listeners</key>
|
<key>Listeners</key>
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
# Allow traffic by privileged group (used by daemon)
|
|
||||||
pass out proto { tcp, udp } group { amnvpn } flags any no state
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
APP_NAME=AmneziaVPN
|
APP_NAME=AmneziaVPN
|
||||||
|
SERVICE_GROUP=amnvpn
|
||||||
PLIST_NAME=$APP_NAME.plist
|
PLIST_NAME=$APP_NAME.plist
|
||||||
LAUNCH_DAEMONS_PLIST_NAME=/Library/LaunchDaemons/$PLIST_NAME
|
LAUNCH_DAEMONS_PLIST_NAME=/Library/LaunchDaemons/$PLIST_NAME
|
||||||
LOG_FOLDER=/var/log/$APP_NAME
|
LOG_FOLDER=/var/log/$APP_NAME
|
||||||
@@ -34,6 +35,18 @@ fi
|
|||||||
run_cmd launchctl bootout system "$LAUNCH_DAEMONS_PLIST_NAME" || run_cmd launchctl unload "$LAUNCH_DAEMONS_PLIST_NAME"
|
run_cmd launchctl bootout system "$LAUNCH_DAEMONS_PLIST_NAME" || run_cmd launchctl unload "$LAUNCH_DAEMONS_PLIST_NAME"
|
||||||
run_cmd rm -f "$LAUNCH_DAEMONS_PLIST_NAME"
|
run_cmd rm -f "$LAUNCH_DAEMONS_PLIST_NAME"
|
||||||
|
|
||||||
|
# Add separate group for xray filtering
|
||||||
|
if dscl . -read "/Groups/$SERVICE_GROUP" >/dev/null 2>&1; then
|
||||||
|
log "Group $SERVICE_GROUP already exists"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
local next_gid
|
||||||
|
next_gid=$(dscl . -list /Groups PrimaryGroupID 2>/dev/null | awk '{print $2}' | sort -n | awk '$1>=500{g=$1} END{print (g?g+1:501)}')
|
||||||
|
run_cmd dscl . -create "/Groups/$SERVICE_GROUP"
|
||||||
|
run_cmd dscl . -create "/Groups/$SERVICE_GROUP" PrimaryGroupID "$next_gid"
|
||||||
|
run_cmd dscl . -create "/Groups/$SERVICE_GROUP" RealName "Amnezia VPN Service Group"
|
||||||
|
fi
|
||||||
|
|
||||||
run_cmd sudo chmod -R a-w "$APP_PATH/"
|
run_cmd sudo chmod -R a-w "$APP_PATH/"
|
||||||
run_cmd sudo chown -R root "$APP_PATH/"
|
run_cmd sudo chown -R root "$APP_PATH/"
|
||||||
run_cmd sudo chgrp -R wheel "$APP_PATH/"
|
run_cmd sudo chgrp -R wheel "$APP_PATH/"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ USER_APP_SUPPORT="$HOME/Library/Application Support/$APP_NAME"
|
|||||||
SYSTEM_APP_SUPPORT="/Library/Application Support/$APP_NAME"
|
SYSTEM_APP_SUPPORT="/Library/Application Support/$APP_NAME"
|
||||||
LOG_FOLDER="/var/log/$APP_NAME"
|
LOG_FOLDER="/var/log/$APP_NAME"
|
||||||
CACHES_FOLDER="$HOME/Library/Caches/$APP_NAME"
|
CACHES_FOLDER="$HOME/Library/Caches/$APP_NAME"
|
||||||
|
SERVICE_GROUP="amnvpn"
|
||||||
|
|
||||||
# Attempt to quit the GUI application if it's currently running
|
# Attempt to quit the GUI application if it's currently running
|
||||||
if pgrep -x "$APP_NAME" > /dev/null; then
|
if pgrep -x "$APP_NAME" > /dev/null; then
|
||||||
@@ -81,4 +82,20 @@ if sudo pfctl -s info 2>/dev/null | grep -q '^Status: Enabled' && \
|
|||||||
sudo pfctl -d 2>/dev/null || true
|
sudo pfctl -d 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Remove amnvpn group if it's not referenced by users
|
||||||
|
if dscl . -read "/Groups/$SERVICE_GROUP" >/dev/null 2>&1; then
|
||||||
|
group_gid=$(dscl . -read "/Groups/$SERVICE_GROUP" PrimaryGroupID 2>/dev/null | awk '{print $2}')
|
||||||
|
users_with_primary_gid=""
|
||||||
|
if [ -n "$group_gid" ]; then
|
||||||
|
users_with_primary_gid=$(dscl . -list /Users PrimaryGroupID 2>/dev/null | awk -v gid="$group_gid" '$2 == gid {print $1}')
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$users_with_primary_gid" ]; then
|
||||||
|
echo "Removing group $SERVICE_GROUP"
|
||||||
|
sudo dscl . -delete "/Groups/$SERVICE_GROUP" || true
|
||||||
|
else
|
||||||
|
echo "Keeping group $SERVICE_GROUP (still used by users): $users_with_primary_gid"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# -----------------------------------------------------------
|
# -----------------------------------------------------------
|
||||||
|
|||||||
2
deploy/data/pf-templates/amn.400.allowPIA.conf.in
Normal file
2
deploy/data/pf-templates/amn.400.allowPIA.conf.in
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Allow traffic by configured identity (set by CMake)
|
||||||
|
pass out proto { tcp, udp } @AMN_PF_RULE_IDENTITY@ flags any no state
|
||||||
@@ -84,6 +84,7 @@ bool KillSwitch::disableKillSwitch() {
|
|||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), true);
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), true);
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), false);
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), false);
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), false);
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), false);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("130.allowMarkedXray"), false);
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), false);
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), false);
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true);
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true);
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), false);
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), false);
|
||||||
@@ -96,6 +97,7 @@ bool KillSwitch::disableKillSwitch() {
|
|||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), false);
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), false);
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), false);
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), false);
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), false);
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), false);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("130.allowMarkedXray"), false);
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), false);
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), false);
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), false);
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), false);
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true);
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true);
|
||||||
@@ -118,6 +120,7 @@ bool KillSwitch::disableKillSwitch() {
|
|||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("290.allowDHCP"), false);
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("290.allowDHCP"), false);
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("300.allowLAN"), false);
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("300.allowLAN"), false);
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), false);
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), false);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("400.allowPIA"), false);
|
||||||
} else {
|
} else {
|
||||||
MacOSFirewall::uninstall();
|
MacOSFirewall::uninstall();
|
||||||
}
|
}
|
||||||
@@ -143,6 +146,7 @@ bool KillSwitch::disableAllTraffic() {
|
|||||||
LinuxFirewall::install();
|
LinuxFirewall::install();
|
||||||
}
|
}
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), true);
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), true);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("130.allowMarkedXray"), false);
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true);
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true);
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true);
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true);
|
||||||
#endif
|
#endif
|
||||||
@@ -279,15 +283,18 @@ bool KillSwitch::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIn
|
|||||||
bool blockAll = 0;
|
bool blockAll = 0;
|
||||||
bool allowNets = 0;
|
bool allowNets = 0;
|
||||||
bool blockNets = 0;
|
bool blockNets = 0;
|
||||||
|
bool allowMarkedXray = 0;
|
||||||
QStringList allownets;
|
QStringList allownets;
|
||||||
QStringList blocknets;
|
QStringList blocknets;
|
||||||
|
|
||||||
if (splitTunnelType == 0) {
|
if (splitTunnelType == 0) {
|
||||||
blockAll = true;
|
blockAll = true;
|
||||||
allowNets = true;
|
allowNets = true;
|
||||||
|
allowMarkedXray = true;
|
||||||
allownets.append(configStr.value("vpnServer").toString());
|
allownets.append(configStr.value("vpnServer").toString());
|
||||||
} else if (splitTunnelType == 1) {
|
} else if (splitTunnelType == 1) {
|
||||||
blockNets = true;
|
blockNets = true;
|
||||||
|
allowMarkedXray = true;
|
||||||
for (auto v : splitTunnelSites) {
|
for (auto v : splitTunnelSites) {
|
||||||
blocknets.append(v.toString());
|
blocknets.append(v.toString());
|
||||||
}
|
}
|
||||||
@@ -313,6 +320,7 @@ bool KillSwitch::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIn
|
|||||||
LinuxFirewall::updateAllowNets(allownets);
|
LinuxFirewall::updateAllowNets(allownets);
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), blockAll);
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), blockAll);
|
||||||
LinuxFirewall::updateBlockNets(blocknets);
|
LinuxFirewall::updateBlockNets(blocknets);
|
||||||
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("130.allowMarkedXray"), true);
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), true);
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), true);
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true);
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true);
|
||||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true);
|
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true);
|
||||||
@@ -378,6 +386,7 @@ bool KillSwitch::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIn
|
|||||||
|
|
||||||
MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), true);
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), true);
|
||||||
MacOSFirewall::setAnchorTable(QStringLiteral("310.blockDNS"), true, QStringLiteral("dnsaddr"), dnsServers);
|
MacOSFirewall::setAnchorTable(QStringLiteral("310.blockDNS"), true, QStringLiteral("dnsaddr"), dnsServers);
|
||||||
|
MacOSFirewall::setAnchorEnabled(QStringLiteral("400.allowPIA"), true);
|
||||||
#endif
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,6 +162,96 @@ bool RouterMac::restoreResolvers() {
|
|||||||
return m_dnsUtil->restoreResolvers();
|
return m_dnsUtil->restoreResolvers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RouterMac::routeAddXray(const QString& ifname, const QString& gateway)
|
||||||
|
{
|
||||||
|
if (ifname.isEmpty() || gateway.isEmpty()) {
|
||||||
|
qWarning().noquote() << "routeAddXray: invalid iface/gateway:" << ifname << gateway;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString cmd = QString("route add -net 0.0.0.0/1 %1 -ifscope %2").arg(gateway).arg(ifname);
|
||||||
|
QStringList parts = cmd.split(" ");
|
||||||
|
|
||||||
|
int argc = parts.size();
|
||||||
|
char **argv = new char*[argc];
|
||||||
|
for (int i = 0; i < argc; i++) {
|
||||||
|
argv[i] = new char[parts.at(i).toStdString().length() + 1];
|
||||||
|
strcpy(argv[i], parts.at(i).toStdString().c_str());
|
||||||
|
}
|
||||||
|
mainRouteIface(argc, argv);
|
||||||
|
for (int i = 0; i < argc; i++) {
|
||||||
|
delete [] argv[i];
|
||||||
|
}
|
||||||
|
delete[] argv;
|
||||||
|
|
||||||
|
cmd = QString("route add -net 128.0.0.0/1 %1 -ifscope %2").arg(gateway).arg(ifname);
|
||||||
|
parts = cmd.split(" ");
|
||||||
|
|
||||||
|
argc = parts.size();
|
||||||
|
argv = new char*[argc];
|
||||||
|
for (int i = 0; i < argc; i++) {
|
||||||
|
argv[i] = new char[parts.at(i).toStdString().length() + 1];
|
||||||
|
strcpy(argv[i], parts.at(i).toStdString().c_str());
|
||||||
|
}
|
||||||
|
mainRouteIface(argc, argv);
|
||||||
|
for (int i = 0; i < argc; i++) {
|
||||||
|
delete [] argv[i];
|
||||||
|
}
|
||||||
|
delete[] argv;
|
||||||
|
|
||||||
|
qDebug().noquote() << "Installed xray routes via" << gateway << "on" << ifname;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RouterMac::routeDeleteXray(const QString& ifname, const QString& gateway)
|
||||||
|
{
|
||||||
|
if (ifname.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString cmd;
|
||||||
|
if (!gateway.isEmpty()) {
|
||||||
|
cmd = QString("route delete -net 0.0.0.0/1 %1 -ifscope %2").arg(gateway).arg(ifname);
|
||||||
|
} else {
|
||||||
|
cmd = QString("route delete -net 0.0.0.0/1 -ifscope %1").arg(ifname);
|
||||||
|
}
|
||||||
|
QStringList parts = cmd.split(" ");
|
||||||
|
|
||||||
|
int argc = parts.size();
|
||||||
|
char **argv = new char*[argc];
|
||||||
|
for (int i = 0; i < argc; i++) {
|
||||||
|
argv[i] = new char[parts.at(i).toStdString().length() + 1];
|
||||||
|
strcpy(argv[i], parts.at(i).toStdString().c_str());
|
||||||
|
}
|
||||||
|
mainRouteIface(argc, argv);
|
||||||
|
for (int i = 0; i < argc; i++) {
|
||||||
|
delete [] argv[i];
|
||||||
|
}
|
||||||
|
delete[] argv;
|
||||||
|
|
||||||
|
if (!gateway.isEmpty()) {
|
||||||
|
cmd = QString("route delete -net 128.0.0.0/1 %1 -ifscope %2").arg(gateway).arg(ifname);
|
||||||
|
} else {
|
||||||
|
cmd = QString("route delete -net 128.0.0.0/1 -ifscope %1").arg(ifname);
|
||||||
|
}
|
||||||
|
parts = cmd.split(" ");
|
||||||
|
|
||||||
|
argc = parts.size();
|
||||||
|
argv = new char*[argc];
|
||||||
|
for (int i = 0; i < argc; i++) {
|
||||||
|
argv[i] = new char[parts.at(i).toStdString().length() + 1];
|
||||||
|
strcpy(argv[i], parts.at(i).toStdString().c_str());
|
||||||
|
}
|
||||||
|
mainRouteIface(argc, argv);
|
||||||
|
for (int i = 0; i < argc; i++) {
|
||||||
|
delete [] argv[i];
|
||||||
|
}
|
||||||
|
delete[] argv;
|
||||||
|
|
||||||
|
qDebug().noquote() << "Removed xray routes on" << ifname;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool RouterMac::deleteTun(const QString &dev)
|
bool RouterMac::deleteTun(const QString &dev)
|
||||||
{
|
{
|
||||||
qDebug().noquote() << "deleteTun start";
|
qDebug().noquote() << "deleteTun start";
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ public:
|
|||||||
bool deleteTun(const QString &dev);
|
bool deleteTun(const QString &dev);
|
||||||
bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers);
|
bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers);
|
||||||
bool restoreResolvers();
|
bool restoreResolvers();
|
||||||
|
bool routeAddXray(const QString& ifname, const QString& gateway);
|
||||||
|
bool routeDeleteXray(const QString& ifname, const QString& gateway);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
@@ -47,4 +49,3 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
#endif // ROUTERMAC_H
|
#endif // ROUTERMAC_H
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#include "xray.h"
|
#include "xray.h"
|
||||||
#include "core/utils/networkUtilities.h"
|
#include "core/utils/networkUtilities.h"
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
#include "router_mac.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QNetworkInterface>
|
#include <QNetworkInterface>
|
||||||
@@ -25,18 +28,34 @@
|
|||||||
#endif
|
#endif
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include "xray_defs.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool Xray::startXray(const QString &cfg)
|
bool Xray::startXray(const QString &cfg)
|
||||||
{
|
{
|
||||||
qDebug() << "Xray::startXray()";
|
qDebug() << "Xray::startXray()";
|
||||||
|
const auto gatewayAndIface = NetworkUtilities::getGatewayAndIface();
|
||||||
auto defaultIface = NetworkUtilities::getGatewayAndIface().second;
|
const QString defaultGateway = gatewayAndIface.first;
|
||||||
|
const QNetworkInterface defaultIface = gatewayAndIface.second;
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
m_defaultIfaceName = defaultIface.name().toUtf8();
|
m_defaultIfaceName = defaultIface.name().toUtf8();
|
||||||
#else
|
#else
|
||||||
m_defaultIfaceIdx = defaultIface.index();
|
m_defaultIfaceIdx = defaultIface.index();
|
||||||
#endif
|
#endif
|
||||||
|
if (defaultIface.index() > 0) {
|
||||||
|
qDebug() << "[xray] using uplink interface:" << defaultIface.name() << "(" << defaultIface.index() << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
m_uplinkIfaceName = defaultIface.name();
|
||||||
|
m_uplinkGateway = defaultGateway;
|
||||||
|
if (!m_uplinkIfaceName.isEmpty()) {
|
||||||
|
const bool installed = RouterMac::Instance().routeAddXray(m_uplinkIfaceName, m_uplinkGateway);
|
||||||
|
if (!installed) {
|
||||||
|
qWarning() << "[xray] failed to install xray routes on" << m_uplinkIfaceName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (auto err = amnezia_xray_setsockcallback(ctxSockCallback, this); err != nullptr) {
|
if (auto err = amnezia_xray_setsockcallback(ctxSockCallback, this); err != nullptr) {
|
||||||
qDebug() << "[xray] sockopt failed: " << err;
|
qDebug() << "[xray] sockopt failed: " << err;
|
||||||
@@ -65,13 +84,22 @@ bool Xray::startXray(const QString &cfg)
|
|||||||
bool Xray::stopXray()
|
bool Xray::stopXray()
|
||||||
{
|
{
|
||||||
qDebug() << "Xray::stopXray()";
|
qDebug() << "Xray::stopXray()";
|
||||||
|
bool success = true;
|
||||||
if (auto err = amnezia_xray_stop(); err != nullptr) {
|
if (auto err = amnezia_xray_stop(); err != nullptr) {
|
||||||
qDebug() << "[xray] failed to stop: " << err;
|
qDebug() << "[xray] failed to stop: " << err;
|
||||||
amnezia_xray_free(err);
|
amnezia_xray_free(err);
|
||||||
return false;
|
success = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
#ifdef Q_OS_MAC
|
||||||
|
if (!m_uplinkIfaceName.isEmpty()) {
|
||||||
|
RouterMac::Instance().routeDeleteXray(m_uplinkIfaceName, m_uplinkGateway);
|
||||||
|
}
|
||||||
|
m_uplinkIfaceName.clear();
|
||||||
|
m_uplinkGateway.clear();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Xray::logHandler(char* str)
|
void Xray::logHandler(char* str)
|
||||||
@@ -99,6 +127,7 @@ void Xray::sockCallback(uintptr_t fd)
|
|||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
if (!m_defaultIfaceName.isEmpty()) {
|
if (!m_defaultIfaceName.isEmpty()) {
|
||||||
setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, m_defaultIfaceName.data(), m_defaultIfaceName.size());
|
setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, m_defaultIfaceName.data(), m_defaultIfaceName.size());
|
||||||
|
setsockopt(fd, SOL_SOCKET, SO_MARK, &amnezia::xray::xrayTrafficMark, sizeof(amnezia::xray::xrayTrafficMark));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ private:
|
|||||||
#else
|
#else
|
||||||
int m_defaultIfaceIdx;
|
int m_defaultIfaceIdx;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
QString m_uplinkIfaceName;
|
||||||
|
QString m_uplinkGateway;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // XRAY_H
|
#endif // XRAY_H
|
||||||
|
|||||||
11
service/server/xray_defs.h
Normal file
11
service/server/xray_defs.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#ifndef XRAY_DEFS_H
|
||||||
|
#define XRAY_DEFS_H
|
||||||
|
|
||||||
|
namespace amnezia
|
||||||
|
{
|
||||||
|
namespace xray
|
||||||
|
{
|
||||||
|
constexpr unsigned int xrayTrafficMark = 0x82;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // XRAY_DEFS_H
|
||||||
Reference in New Issue
Block a user