Compare commits

..

14 Commits

Author SHA1 Message Date
Fedotov Anton
0d0c29493b definition of the routing string to be deleted for wg and ikev2 connections 2021-12-10 13:02:54 +03:00
pokamest
4abfc58ad0 Merge branch 'dev' into dev-netadapters 2021-12-04 18:42:14 +03:00
Fedotov Anton
5245fe103c increase timeout after connection for getting info about routes 2021-12-02 22:58:44 +03:00
Fedotov Anton
3dd207a3a1 unused code commented out 2021-12-02 20:36:56 +03:00
Fedotov Anton
1a16a61ced added the method to get the route table 2021-12-02 15:08:54 +03:00
pokamest
340639b3c8 Code cleanup 2021-11-28 19:28:51 +03:00
pokamest
dd427f89bc VPN modes ui fix 2021-11-28 19:14:12 +03:00
pokamest
051487d6ef Comments cleanup 2021-11-28 19:13:33 +03:00
pokamest
97d3b92988 Merge branch 'dev' into dev-netadapters 2021-11-28 17:42:11 +03:00
Fedotov Anton
8262d743d8 WMI dependency removed, all available protocols checked 2021-11-24 13:35:37 +03:00
Fedotov Anton
18654ca4ef the information about ikev2 was got 2021-11-24 03:05:40 +03:00
Fedotov Anton
44bc831b7f Object that receives information about network adapters was included in the source files tree 2021-11-24 03:03:36 +03:00
Fedotov Anton
3424068993 Changes to the mechanics of obtaining data about network adapters 2021-11-24 03:00:27 +03:00
ToshDev
90a92bea1b Collection of network information about the adapter through which the connection to the server is made 2021-11-22 14:21:56 +03:00
241 changed files with 6509 additions and 8345 deletions

1
.gitignore vendored
View File

@@ -47,7 +47,6 @@ client/amneziavpn_qml_plugin_import.cpp
client/qmlcache_loader.cpp
client/rep_ipc_interface_replica.h
client/resources_qmlcache.qrc
client/3rd/OpenVPNAdpter/build/
# QtCreator

6
.gitmodules vendored
View File

@@ -7,9 +7,3 @@
[submodule "client/3rd/wireguard-apple"]
path = client/3rd/wireguard-apple
url = https://github.com/WireGuard/wireguard-apple
[submodule "client/3rd/OpenVPNAdapter"]
path = client/3rd/OpenVPNAdapter
url = https://github.com/ss-abramchuk/OpenVPNAdapter.git
[submodule "client/3rd/qzxing"]
path = client/3rd/qzxing
url = https://github.com/ftylitak/qzxing.git

View File

@@ -2,5 +2,5 @@ TEMPLATE = subdirs
SUBDIRS = client
!ios:!android {
SUBDIRS += service
SUBDIRS += service platform
}

View File

@@ -0,0 +1,7 @@
HEADERS += \
3rd/AdpInfo/netadpinfo.h \
win32: {
SOURCES += \
3rd/AdpInfo/win_netadpinfo.cc \
}

View File

@@ -0,0 +1,71 @@
#pragma once
#include <vector>
#include <string>
#include <tuple>
#include <memory>
namespace adpinfo{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// {false,""} - no error
// {true,"descr"} - error with description
using RET_TYPE = std::tuple<bool, std::string>;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/*
* Getting the route table
*/
typedef struct route_table{
std::string szDestIp{};
std::string szMaskIp{};
std::string szGatewayIp{};
std::string szInterfaceIp{};
unsigned long ulIfIndex{};
}route_table;
std::vector</*std::tuple<std::string,std::string,std::string,std::string>*/route_table>get_route_table(std::string_view);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/*
* The object uses for collect the information about active network adapters/interfaces
*/
class NetAdpInfo final{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class Adapter{
std::string name{};
std::string descr{};
std::string route{};
std::string address{};
std::string gateway{};
public:
explicit Adapter() = default;
~Adapter() = default;
void set_name(std::string_view);
std::string_view get_name()const;
void set_description(std::string_view);
std::string_view get_description()const;
void set_route_gateway(std::string_view);
std::string_view get_route_gateway()const;
void set_local_address(std::string_view);
std::string_view get_local_address()const;
void set_local_gateway(std::string_view);
std::string_view get_local_gateway()const;
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int16_t _index_of_adapter{};
std::vector<std::shared_ptr<Adapter>>_adapters{};
RET_TYPE collect_adapters_data();
public:
explicit NetAdpInfo() = default;
~NetAdpInfo() = default;
RET_TYPE get_adapter_info(std::string_view );
std::string_view get_adapter_route_gateway()const;
std::string_view get_adapter_local_address()const;
std::string_view get_adapter_local_gateway()const;
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} //end namespace

View File

@@ -0,0 +1,321 @@
#include "netadpinfo.h"
#include <QDebug>
#include <algorithm>
#include <iterator>
#include <cassert>
#include <windows.h>
#include <iphlpapi.h>
#pragma comment(lib, "iphlpapi.lib")
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//static std::string convert_wide_to_ansi(const std::wstring& widestring) {
// auto nchars = WideCharToMultiByte(
// CP_ACP,
// 0,
// widestring.c_str(),
// static_cast<int>(widestring.length() + 1),
// nullptr,
// 0,
// nullptr,
// nullptr);
// std::string converted_string{};
// converted_string.resize(nchars);
// WideCharToMultiByte(CP_ACP,
// 0,
// widestring.c_str(),
// -1,
// &converted_string[0],
// static_cast<int>(widestring.length()),
// nullptr,
// nullptr);
// return converted_string;
//}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//static std::string get_founded_route(std::string_view ip_address){
// MIB_IPFORWARDROW br{0};
// ZeroMemory(&br, sizeof(MIB_IPFORWARDROW));
// struct in_addr ia;
// std::string sTmp{};
// DWORD dwRes = GetBestRoute(inet_addr(ip_address.data()), 0, &br);
// if( dwRes == NO_ERROR ){
// ia.S_un.S_addr = (u_long) br.dwForwardDest;
// sTmp = inet_ntoa(ia);
// qDebug()<<"Best Route:"<< sTmp.data();
// }
// return sTmp;
//}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static std::string get_route_gateway()
{
std::string route_gateway{};
PMIB_IPFORWARDTABLE pIpForwardTable{nullptr};
DWORD dwSize = 0;
BOOL bOrder = FALSE;
struct in_addr IpAddr;
char szGatewayIp[128]{'\0'};
DWORD dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
if (dwStatus == ERROR_INSUFFICIENT_BUFFER) {
if (!(pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc(dwSize))) {
return {"Out of memory"};
}
dwStatus = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
}
if (dwStatus != ERROR_SUCCESS) {
if (pIpForwardTable)
free(pIpForwardTable);
return {"getIpForwardTable failed"};
}
const DWORD end = pIpForwardTable->dwNumEntries;
for (DWORD i = 0; i < end; i++) {
if (pIpForwardTable->table[i].dwForwardDest == 0) {
IpAddr.S_un.S_addr =
(u_long) pIpForwardTable->table[i].dwForwardNextHop;
strcpy_s(szGatewayIp, sizeof (szGatewayIp), inet_ntoa(IpAddr));
route_gateway = std::string(szGatewayIp);
break;
}
}
if (pIpForwardTable)
free(pIpForwardTable);
return route_gateway;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static std::string get_interface_ip(DWORD index){
std::string _ipaddr{'\0'};
std::vector<BYTE> buffer{};
IP_ADAPTER_INFO *adapter_info{nullptr};
DWORD result{ERROR_BUFFER_OVERFLOW};
ULONG buffer_len = sizeof(IP_ADAPTER_INFO) * 10;
while (result == ERROR_BUFFER_OVERFLOW){
buffer.resize(buffer_len);
adapter_info = reinterpret_cast<IP_ADAPTER_INFO*>(&buffer[0]);
result = GetAdaptersInfo(adapter_info, &buffer_len);
if (result == ERROR_NO_DATA){
return _ipaddr;
}
}//end while
if (result != NO_ERROR){
return _ipaddr;
}
IP_ADAPTER_INFO *adapter_iterator = adapter_info;
while(adapter_iterator){
if (adapter_iterator->Index == index)
break;
adapter_iterator = adapter_iterator->Next;
}//end while
if ( adapter_iterator != nullptr || adapter_iterator != 0x0 || adapter_iterator != NULL )
_ipaddr = std::string(adapter_iterator->IpAddressList.IpAddress.String, 16);
else
_ipaddr = "127.0.0.1";
return _ipaddr;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
namespace adpinfo {
std::vector<route_table>get_route_table(std::string_view ipifaddrs){
/*std::tuple<std::string, std::string, std::string, std::string >*/
std::vector<route_table>ret_table{};
PMIB_IPFORWARDTABLE p_table{nullptr};
struct in_addr ip_addr{};
DWORD size{};
p_table = (MIB_IPFORWARDTABLE *) HeapAlloc(GetProcessHeap(), 0, (sizeof (MIB_IPFORWARDTABLE)));
if (p_table == nullptr)
{
return ret_table;
}
if (GetIpForwardTable(p_table, &size, 0) == ERROR_INSUFFICIENT_BUFFER) {
HeapFree(GetProcessHeap(), 0, p_table);
p_table = (MIB_IPFORWARDTABLE *) HeapAlloc(GetProcessHeap(), 0, size);
if (p_table == nullptr) {
return ret_table;
}
}
DWORD ret = GetIpForwardTable(p_table, &size, 0);
if (ret == NO_ERROR) {
const int &numEntries = static_cast<int>(p_table->dwNumEntries);
for (int i = 0; i <numEntries; ++i) {
char szDestIp[128]{};
char szMaskIp[128]{};
char szGatewayIp[128]{};
char szInterfaceIp[21]{};
ip_addr.S_un.S_addr = (u_long) p_table->table[i].dwForwardDest;
strcpy_s(szDestIp, sizeof (szDestIp), inet_ntoa(ip_addr));
ip_addr.S_un.S_addr = (u_long) p_table->table[i].dwForwardMask;
strcpy_s(szMaskIp, sizeof (szMaskIp), inet_ntoa(ip_addr));
ip_addr.S_un.S_addr = (u_long) p_table->table[i].dwForwardNextHop;
strcpy_s(szGatewayIp, sizeof (szGatewayIp), inet_ntoa(ip_addr));
const auto &ifname = get_interface_ip(p_table->table[i].dwForwardIfIndex);
const auto &ifnameSize = ifname.length() + 1;
strcpy_s(szInterfaceIp, ifnameSize, ifname.data());
if (ipifaddrs.length() == 0){
//std::make_tuple(std::string(szDestIp), std::string(szMaskIp), std::string(szGatewayIp), std::string(szInterfaceIp));
const route_table &mt = {
std::string(szDestIp),
std::string(szMaskIp),
std::string(szGatewayIp),
std::string(szInterfaceIp),
p_table->table[i].dwForwardIfIndex
};
ret_table.emplace_back(mt);
}else{
bool in_not_empty = (ifname.find(ipifaddrs) != std::string::npos);
//bool in_as_destIp = ( std::string(szDestIp).find(ipifaddrs) != std::string::npos);
bool destIp_as_zero = (std::string(szDestIp).find("0.0.0.0") != std::string::npos);
bool mask_as_zero = (std::string(szMaskIp).find("0.0.0.0") != std::string::npos);
// bool ip_the_same = (
// std::string(szDestIp).find(ipifaddrs) != std::string::npos &&
// std::string(szDestIp).find(szGatewayIp) != std::string::npos &&
// std::string(szDestIp).find(szInterfaceIp) != std::string::npos
// );
// bool not_default = (std::string(szDestIp).find("127.0.0.1") == std::string::npos);
if ( in_not_empty &&
// in_as_destIp &&
destIp_as_zero &&
mask_as_zero//) || ( ip_the_same && not_default )
)
{
// finded
const route_table &mt = {
std::string(szDestIp),
std::string(szMaskIp),
std::string(szGatewayIp),
std::string(szInterfaceIp),
p_table->table[i].dwForwardIfIndex
};//std::make_tuple(std::string(szDestIp), std::string(szMaskIp), std::string(szGatewayIp), std::string(szInterfaceIp));
ret_table.emplace_back(mt);
}
}
}//end for
}//end if
return ret_table;
}
}//end namespace adpinfo
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
namespace adpinfo {
void NetAdpInfo::Adapter::set_name(std::string_view value){
name = value;
}
std::string_view NetAdpInfo::Adapter::get_name()const{
return name;
}
void NetAdpInfo::Adapter::set_description(std::string_view value){
descr = value;
}
std::string_view NetAdpInfo::Adapter::get_description()const{
return descr;
}
void NetAdpInfo::Adapter::set_route_gateway(std::string_view value){
route = value;
}
std::string_view NetAdpInfo::Adapter::get_route_gateway()const{
return route;
}
void NetAdpInfo::Adapter::set_local_address(std::string_view value){
address = value;
}
std::string_view NetAdpInfo::Adapter::get_local_address()const{
return address;
}
void NetAdpInfo::Adapter::set_local_gateway(std::string_view value){
gateway = value;
}
std::string_view NetAdpInfo::Adapter::get_local_gateway()const{
return gateway;
}
RET_TYPE NetAdpInfo::collect_adapters_data(){
_adapters.clear();
std::vector<BYTE> buffer{};
IP_ADAPTER_INFO *adapter_info{nullptr};
DWORD result{ERROR_BUFFER_OVERFLOW};
ULONG buffer_len = sizeof(IP_ADAPTER_INFO) * 10;
while (result == ERROR_BUFFER_OVERFLOW){
buffer.resize(buffer_len);
adapter_info = reinterpret_cast<IP_ADAPTER_INFO*>(&buffer[0]);
result = GetAdaptersInfo(adapter_info, &buffer_len);
if (result == ERROR_NO_DATA){
return {true, "GetAdaptersInfo return ERROR_NO_DATA"};
}
}//end while
if (result != NO_ERROR){
const std::string &error = "GetAdaptersInfo failed :" + std::to_string(result);
return {true, error};
}
IP_ADAPTER_INFO *adapter_iterator = adapter_info;
while(adapter_iterator){
std::shared_ptr<Adapter>_tmp{std::make_shared<Adapter>()};
_tmp->set_name(adapter_iterator->AdapterName);
_tmp->set_description(adapter_iterator->Description);
_tmp->set_local_address(adapter_iterator->IpAddressList.IpAddress.String);
std::string lgw = adapter_iterator->GatewayList.IpAddress.String;
// if (lgw.length() == 0 || lgw.find("0.0.0.0") != std::string::npos)
// {
// //lgw = get_founded_route("8.8.8.8");
// if (adapter_iterator->DhcpEnabled == 1)
// {
// lgw = adapter_iterator->DhcpServer.IpAddress.String;
// }
// }
_tmp->set_local_gateway(lgw);
_tmp->set_route_gateway(get_route_gateway());
_adapters.emplace_back(_tmp);
adapter_iterator = adapter_iterator->Next;
}
return {false, ""};
}
RET_TYPE NetAdpInfo::get_adapter_info(std::string_view _adapter_name){
_index_of_adapter = -1;
const auto result{collect_adapters_data()};
if (std::get<0>(result) == true){
_index_of_adapter = -1;
return result;
}
const int16_t &len = static_cast<int16_t>(_adapters.size());
for (auto i = 0; i< len; ++i){
auto adap_name = _adapters[i]->get_name();
auto adap_desc = _adapters[i]->get_description();
if ( adap_name.find(_adapter_name) != std::string::npos || adap_desc.find(_adapter_name) != std::string::npos ){
_index_of_adapter = i;
return {false, ""};
}
}
return {true, "adapters no founded"};
}
std::string_view NetAdpInfo::get_adapter_route_gateway()const{
if (_index_of_adapter < 0)
return "error adapter index";
return _adapters.at(_index_of_adapter)->get_route_gateway();
}
std::string_view NetAdpInfo::get_adapter_local_address()const{
if (_index_of_adapter < 0)
return "error adapter index";
return _adapters.at(_index_of_adapter)->get_local_address();
}
std::string_view NetAdpInfo::get_adapter_local_gateway()const{
if (_index_of_adapter < 0)
return "error adapter index";
return _adapters.at(_index_of_adapter)->get_local_gateway();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//std::string NetAdpInfo::get_system_route(){
// return get_route_gateway();
//}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}// end namespace

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,129 @@
#if !defined(AFX_QR_ENCODE_H__AC886DF7_C0AE_4C9F_AC7A_FCDA8CB1DD37__INCLUDED_)
#define AFX_QR_ENCODE_H__AC886DF7_C0AE_4C9F_AC7A_FCDA8CB1DD37__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
/////////////////////////////////////////////////////////////////////////////
//
#define QR_LEVEL_L 0
#define QR_LEVEL_M 1
#define QR_LEVEL_Q 2
#define QR_LEVEL_H 3
//
#define QR_MODE_NUMERAL 0
#define QR_MODE_ALPHABET 1
#define QR_MODE_8BIT 2
#define QR_MODE_KANJI 3
//
#define QR_VRESION_S 0
#define QR_VRESION_M 1
#define QR_VRESION_L 2
#define MAX_ALLCODEWORD 3706
#define MAX_DATACODEWORD 2956
#define MAX_CODEBLOCK 153
#define MAX_MODULESIZE 177
#define QR_MARGIN 0
/////////////////////////////////////////////////////////////////////////////
typedef struct tagRS_BLOCKINFO
{
int ncRSBlock;
int ncAllCodeWord;
int ncDataCodeWord;
} RS_BLOCKINFO, *LPRS_BLOCKINFO;
/////////////////////////////////////////////////////////////////////////////
typedef struct tagQR_VERSIONINFO
{
int nVersionNo;
int ncAllCodeWord;
int ncDataCodeWord[4];
int ncAlignPoint;
int nAlignPoint[6];
RS_BLOCKINFO RS_BlockInfo1[4];
RS_BLOCKINFO RS_BlockInfo2[4];
} QR_VERSIONINFO, *LPQR_VERSIONINFO;
/////////////////////////////////////////////////////////////////////////////
class CQR_Encode
{
public:
CQR_Encode();
~CQR_Encode();
public:
int m_nLevel;
int m_nVersion;
bool m_bAutoExtent;
int m_nMaskingNo;
public:
int m_nSymbleSize;
unsigned char m_byModuleData[MAX_MODULESIZE][MAX_MODULESIZE]; // [x][y]
private:
int m_ncDataCodeWordBit;
unsigned char m_byDataCodeWord[MAX_DATACODEWORD];
int m_ncDataBlock;
unsigned char m_byBlockMode[MAX_DATACODEWORD];
int m_nBlockLength[MAX_DATACODEWORD];
int m_ncAllCodeWord;
unsigned char m_byAllCodeWord[MAX_ALLCODEWORD];
unsigned char m_byRSWork[MAX_CODEBLOCK];
public:
bool EncodeData(int nLevel, int nVersion, bool bAutoExtent, int nMaskingNo, const char* lpsSource, int ncSource = 0);
private:
int GetEncodeVersion(int nVersion, const char* lpsSource, int ncLength);
bool EncodeSourceData(const char* lpsSource, int ncLength, int nVerGroup);
int GetBitLength(unsigned char nMode, int ncData, int nVerGroup);
int SetBitStream(int nIndex, unsigned short wData, int ncData);
bool IsNumeralData(unsigned char c);
bool IsAlphabetData(unsigned char c);
bool IsKanjiData(unsigned char c1, unsigned char c2);
unsigned char AlphabetToBinaly(unsigned char c);
unsigned short KanjiToBinaly(unsigned short wc);
void GetRSCodeWord(unsigned char * lpbyRSWork, int ncDataCodeWord, int ncRSCodeWord);
private:
void FormatModule();
void SetFunctionModule();
void SetFinderPattern(int x, int y);
void SetAlignmentPattern(int x, int y);
void SetVersionPattern();
void SetCodeWordPattern();
void SetMaskingPattern(int nPatternNo);
void SetFormatInfoPattern(int nPatternNo);
int CountPenalty();
};
/////////////////////////////////////////////////////////////////////////////
#endif // !defined(AFX_QR_ENCODE_H__AC886DF7_C0AE_4C9F_AC7A_FCDA8CB1DD37__INCLUDED_)

View File

@@ -0,0 +1,5 @@
HEADERS += \
3rd/QRCodeGenerator/QRCodeGenerator.h \
SOURCES += \
3rd/QRCodeGenerator/QRCodeGenerator.cpp \

View File

@@ -11,7 +11,7 @@ win32 {
-lcrypt32 \
!contains(QMAKE_TARGET.arch, x86_64) {
INCLUDEPATH += $$PWD/windows/x86
INCLUDEPATH += $$PWD/windows/x86_64
HEADERS += $$PWD/windows/x86/botan_all.h
SOURCES += $$PWD/windows/x86/botan_all.cpp
}

View File

@@ -940,8 +940,8 @@ void SshConnectionPrivate::connectToHost()
this, &SshConnectionPrivate::handleSocketConnected);
connect(m_socket, &QIODevice::readyRead,
this, &SshConnectionPrivate::handleIncomingData);
//connect(m_socket, &QAbstractSocket::errorOccurred,
// this, &SshConnectionPrivate::handleSocketError);
connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this,
SLOT(handleSocketError()));
connect(m_socket, &QAbstractSocket::disconnected,
this, &SshConnectionPrivate::handleSocketDisconnected);
connect(&m_timeoutTimer, &QTimer::timeout, this, &SshConnectionPrivate::handleTimeout);

View File

@@ -217,9 +217,7 @@ enum ConnectionState : NSInteger;
SWIFT_CLASS("_TtC10AmneziaVPN18IOSVpnProtocolImpl")
@interface IOSVpnProtocolImpl : NSObject
- (nonnull instancetype)initWithBundleID:(NSString * _Nonnull)bundleID privateKey:(NSData * _Nonnull)privateKey deviceIpv4Address:(NSString * _Nonnull)deviceIpv4Address deviceIpv6Address:(NSString * _Nonnull)deviceIpv6Address closure:(void (^ _Nonnull)(enum ConnectionState, NSDate * _Nullable))closure callback:(void (^ _Nonnull)(BOOL))callback OBJC_DESIGNATED_INITIALIZER;
- (nonnull instancetype)initWithBundleID:(NSString * _Nonnull)bundleID config:(NSString * _Nonnull)config closure:(void (^ _Nonnull)(enum ConnectionState, NSDate * _Nullable))closure callback:(void (^ _Nonnull)(BOOL))callback;
- (void)connectWithDnsServer:(NSString * _Nonnull)dnsServer serverIpv6Gateway:(NSString * _Nonnull)serverIpv6Gateway serverPublicKey:(NSString * _Nonnull)serverPublicKey presharedKey:(NSString * _Nonnull)presharedKey serverIpv4AddrIn:(NSString * _Nonnull)serverIpv4AddrIn serverPort:(NSInteger)serverPort allowedIPAddressRanges:(NSArray<VPNIPAddressRange *> * _Nonnull)allowedIPAddressRanges ipv6Enabled:(Boolean)enabled reason:(NSInteger)reason failureCallback:(void (^ _Nonnull)(void))failureCallback;
- (void)connectWithOvpnConfig:(NSString * _Nonnull)ovpnConfig failureCallback:(void (^ _Nonnull)(void))failureCallback;
- (void)disconnect;
- (void)checkStatusWithCallback:(void (^ _Nonnull)(NSString * _Nonnull, NSString * _Nonnull, NSString * _Nonnull))callback;
- (nonnull instancetype)init SWIFT_UNAVAILABLE;
@@ -232,6 +230,8 @@ typedef SWIFT_ENUM(NSInteger, ConnectionState, closed) {
ConnectionStateDisconnected = 2,
};
SWIFT_CLASS("_TtC10AmneziaVPN17VPNIPAddressRange")
@interface VPNIPAddressRange : NSObject
- (nonnull instancetype)initWithAddress:(NSString * _Nonnull)address networkPrefixLength:(uint8_t)networkPrefixLength isIpv6:(BOOL)isIpv6 OBJC_DESIGNATED_INITIALIZER;

View File

@@ -1,29 +1,40 @@
<?xml version="1.0"?>
<manifest package="org.amnezia.vpn" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="-- %%INSERT_VERSION_NAME%% --" android:versionCode="-- %%INSERT_VERSION_CODE%% --" android:installLocation="auto">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
<uses-permission android:name="android.permission.CAMERA"/>
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
Remove the comment if you do not require these default features. -->
<!-- %%INSERT_FEATURES -->
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="-- %%INSERT_APP_NAME%% --" android:extractNativeLibs="true" android:icon="@drawable/icon">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="-- %%INSERT_APP_NAME%% --" android:screenOrientation="unspecified" android:launchMode="singleTop" android:theme="@style/splashScreenTheme">
<application
android:hardwareAccelerated="true"
android:name="org.qtproject.qt5.android.bindings.QtApplication"
android:label="-- %%INSERT_APP_NAME%% --"
android:extractNativeLibs="true"
android:icon="@drawable/icon">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
android:name="org.qtproject.qt5.android.bindings.QtActivity"
android:label="-- %%INSERT_APP_NAME%% --"
android:screenOrientation="unspecified"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<!-- Application arguments -->
<!-- meta-data android:name="android.app.arguments" android:value="arg1 arg2 arg3"/ -->
<!-- Application arguments -->
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
<meta-data android:name="android.app.repository" android:value="default"/>
@@ -31,6 +42,7 @@
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
<!-- Deploy Qt libs as part of package -->
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
<!-- Run with local libs -->
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
@@ -45,6 +57,7 @@
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
<meta-data android:value="@string/unsupported_android_version" android:name="android.app.unsupported_android_version"/>
<!-- Messages maps -->
<!-- Splash screen -->
<!-- Orientation-specific (portrait/landscape) data is checked first. If not available for current orientation,
then android.app.splash_screen_drawable. For best results, use together with splash_screen_sticky and
@@ -55,6 +68,7 @@
<!-- meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/ -->
<!-- meta-data android:name="android.app.splash_screen_sticky" android:value="true"/ -->
<!-- Splash screen -->
<!-- Background running -->
<!-- Warning: changing this value to true may cause unexpected crashes if the
application still try to draw after
@@ -62,9 +76,11 @@
signal is sent! -->
<meta-data android:name="android.app.background_running" android:value="false"/>
<!-- Background running -->
<!-- auto screen scale factor -->
<meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/>
<!-- auto screen scale factor -->
<!-- extract android style -->
<!-- available android:values :
* default - In most cases this will be the same as "full", but it can also be something else if needed, e.g., for compatibility reasons
@@ -75,20 +91,24 @@
<meta-data android:name="android.app.extract_android_style" android:value="default"/>
<!-- extract android style -->
<meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/splashscreen"/>
</activity>
<service android:name=".VPNService"
android:process=":QtOnlyProcess"
android:permission="android.permission.BIND_VPN_SERVICE">
<intent-filter>
<action android:name="android.net.VpnService"/>
</intent-filter>
</service>
</activity>
<service android:name=".VPNService"
android:permission="android.permission.BIND_VPN_SERVICE">
<intent-filter>
<action android:name="android.net.VpnService"/>
</intent-filter>
</service>
<service android:name="org.amnezia.vpn.qt.VPNPermissionHelper"
android:permission="android.permission.BIND_VPN_SERVICE">
</service>
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
<service android:name="org.amnezia.vpn.qt.VPNPermissionHelper"
android:permission="android.permission.BIND_VPN_SERVICE">
</service>
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
</application>
</manifest>

View File

@@ -38,9 +38,13 @@ apply plugin: 'kotlinx-serialization'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
implementation 'androidx.core:core-ktx:1.1.0'
implementation 'com.android.installreferrer:installreferrer:2.2'
implementation 'com.android.billingclient:billing-ktx:4.0.0'
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.0-alpha02"
implementation "androidx.security:security-crypto:1.1.0-alpha03"
implementation "androidx.security:security-identity-credential:1.0.0-alpha02"
implementation 'com.adjust.sdk:adjust-android:4.28.2'
implementation 'com.google.android.gms:play-services-ads-identifier:17.0.1'
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.2"
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.0.10"
}
@@ -102,8 +106,8 @@ android {
resConfig "en"
minSdkVersion = 24
targetSdkVersion = 30
versionCode 8 // Change to a higher number
versionName "2.0.8" // Change to a higher number
versionCode 2 // Change to a higher number
versionName "2.0.1" // Change to a higher number
}
buildTypes {

View File

@@ -37,13 +37,12 @@ class VPNService : android.net.VpnService() {
SharedLibraryLoader.loadSharedLibrary(this, "ovpn3")
Log.i(tag, "Loaded libs")
Log.e(tag, "Wireguard Version ${wgVersion()}")
mOpenVPNThreadv3 = OpenVPNThreadv3(this)
mOpenVPNThreadv3 = OpenVPNThreadv3 (this)
mAlreadyInitialised = true
}
override fun onUnbind(intent: Intent?): Boolean {
Log.v(tag, "Got Unbind request")
if (!isUp) {
// If the Qt Client got closed while we were not connected
// we do not need to stay as a foreground service.
@@ -53,9 +52,9 @@ class VPNService : android.net.VpnService() {
}
/**
* EntryPoint for the Service, gets Called when AndroidController.cpp
* calles bindService. Returns the [VPNServiceBinder] so QT can send Requests to it.
*/
* EntryPoint for the Service, gets Called when AndroidController.cpp
* calles bindService. Returns the [VPNServiceBinder] so QT can send Requests to it.
*/
override fun onBind(intent: Intent?): IBinder? {
Log.v(tag, "Got Bind request")
init()
@@ -63,10 +62,10 @@ class VPNService : android.net.VpnService() {
}
/**
* Might be the entryPoint if the Service gets Started via an
* Service Intent: Might be from Always-On-Vpn from Settings
* or from Booting the device and having "connect on boot" enabled.
*/
* Might be the entryPoint if the Service gets Started via an
* Service Intent: Might be from Always-On-Vpn from Settings
* or from Booting the device and having "connect on boot" enabled.
*/
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
init()
intent?.let {
@@ -85,28 +84,28 @@ class VPNService : android.net.VpnService() {
Log.e(
tag,
"VPN service was triggered without defining a Server or having a tunnel"
)
return super.onStartCommand(intent, flags, startId)
)
return super.onStartCommand(intent, flags, startId)
}
this.mConfig = JSONObject(lastConfString)
}
this.mConfig = JSONObject(lastConfString)
return super.onStartCommand(intent, flags, startId)
}
return super.onStartCommand(intent, flags, startId)
}
// Invoked when the application is revoked.
// At this moment, the VPN interface is already deactivated by the system.
override fun onRevoke() {
this.turnOff()
super.onRevoke()
}
// Invoked when the application is revoked.
// At this moment, the VPN interface is already deactivated by the system.
override fun onRevoke() {
this.turnOff()
super.onRevoke()
}
var connectionTime: Long = 0
var connectionTime: Long = 0
get() {
return mConnectionTime
}
var isUp: Boolean
var isUp: Boolean
get() {
return currentTunnelHandle >= 0
}
@@ -119,7 +118,7 @@ class VPNService : android.net.VpnService() {
mBinder.dispatchEvent(VPNServiceBinder.EVENTS.disconnected, "")
mConnectionTime = 0
}
val status: JSONObject
val status: JSONObject
get() {
val deviceIpv4: String = ""
return JSONObject().apply {
@@ -129,307 +128,299 @@ class VPNService : android.net.VpnService() {
putOpt("deviceIpv4", mConfig?.getJSONObject("device")?.getString("ipv4Address"))
}
}
/*
* Checks if the VPN Permission is given.
* If the permission is given, returns true
* Requests permission and returns false if not.
*/
fun checkPermissions(): Boolean {
// See https://developer.android.com/guide/topics/connectivity/vpn#connect_a_service
// Call Prepare, if we get an Intent back, we dont have the VPN Permission
// from the user. So we need to pass this to our main Activity and exit here.
val intent = prepare(this)
if (intent == null) {
Log.e(tag, "VPN Permission Already Present")
return true
/*
* Checks if the VPN Permission is given.
* If the permission is given, returns true
* Requests permission and returns false if not.
*/
fun checkPermissions(): Boolean {
// See https://developer.android.com/guide/topics/connectivity/vpn#connect_a_service
// Call Prepare, if we get an Intent back, we dont have the VPN Permission
// from the user. So we need to pass this to our main Activity and exit here.
val intent = prepare(this)
if (intent == null) {
Log.e(tag, "VPN Permission Already Present")
return true
}
Log.e(tag, "Requesting VPN Permission")
return false
}
Log.e(tag, "Requesting VPN Permission")
return false
}
fun turnOn(json: JSONObject?): Int {
if (!checkPermissions()) {
Log.e(tag, "turn on was called without no permissions present!")
isUp = false
return 0
}
Log.i(tag, "Permission okay")
mConfig = json!!
mProtocol = mConfig!!.getString("protocol")
when (mProtocol) {
"openvpn" -> startOpenVpn()
"wireguard" -> startWireGuard()
else -> {
Log.e(tag, "No protocol")
fun turnOn(json: JSONObject?): Int {
if (!checkPermissions()) {
Log.e(tag, "turn on was called without no permissions present!")
isUp = false
return 0
}
}
NotificationUtil.show(this) // Go foreground
return 1
}
fun establish(): ParcelFileDescriptor? {
mbuilder.allowFamily(OsConstants.AF_INET)
mbuilder.allowFamily(OsConstants.AF_INET6)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) mbuilder.setMetered(false)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) setUnderlyingNetworks(null)
return mbuilder.establish()
}
fun setMtu(mtu: Int) {
mbuilder.setMtu(mtu)
}
fun addAddress(ip: String, len: Int) {
Log.v(tag, "mbuilder.addAddress($ip, $len)")
mbuilder.addAddress(ip, len)
}
fun addRoute(ip: String, len: Int) {
Log.v(tag, "mbuilder.addRoute($ip, $len)")
mbuilder.addRoute(ip, len)
}
fun addDNS(ip: String) {
Log.v(tag, "mbuilder.addDnsServer($ip)")
mbuilder.addDnsServer(ip)
if ("samsung".equals(Build.BRAND) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mbuilder.addRoute(ip, 32)
}
}
fun setSessionName(name: String) {
Log.v(tag, "mbuilder.setSession($name)")
mbuilder.setSession(name)
}
fun addHttpProxy(host: String, port: Int): Boolean {
val proxyInfo = ProxyInfo.buildDirectProxy(host, port)
Log.v(tag, "mbuilder.addHttpProxy($host, $port)")
mbuilder.setHttpProxy(proxyInfo)
return true
}
fun setDomain(domain: String) {
Log.v(tag, "mbuilder.setDomain($domain)")
mbuilder.addSearchDomain(domain)
}
fun turnOff() {
Log.v(tag, "Try to disable tunnel")
when (mProtocol) {
"wireguard" -> wgTurnOff(currentTunnelHandle)
"openvpn" -> ovpnTurnOff()
else -> {
Log.e(tag, "No protocol")
}
}
currentTunnelHandle = -1
stopForeground(true)
isUp = false
stopSelf();
}
private fun ovpnTurnOff() {
mOpenVPNThreadv3?.stop()
mOpenVPNThreadv3 = null
Log.e(tag, "mOpenVPNThreadv3 stop!")
}
/**
* Configures an Android VPN Service Tunnel
* with a given Wireguard Config
*/
private fun setupBuilder(config: Config, builder: Builder) {
// Setup Split tunnel
for (excludedApplication in config.`interface`.excludedApplications)
builder.addDisallowedApplication(excludedApplication)
// Device IP
for (addr in config.`interface`.addresses) builder.addAddress(addr.address, addr.mask)
// DNS
for (addr in config.`interface`.dnsServers) builder.addDnsServer(addr.hostAddress)
// Add All routes the VPN may route tos
for (peer in config.peers) {
for (addr in peer.allowedIps) {
builder.addRoute(addr.address, addr.mask)
}
}
builder.allowFamily(OsConstants.AF_INET)
builder.allowFamily(OsConstants.AF_INET6)
builder.setMtu(config.`interface`.mtu.orElse(1280))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) builder.setMetered(false)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) setUnderlyingNetworks(null)
builder.setBlocking(true)
}
/**
* Gets config value for {key} from the Current
* running Wireguard tunnel
*/
private fun getConfigValue(key: String): String? {
if (!isUp) {
return null
}
val config = wgGetConfig(currentTunnelHandle) ?: return null
val lines = config.split("\n")
for (line in lines) {
val parts = line.split("=")
val k = parts.first()
val value = parts.last()
if (key == k) {
return value
}
}
return null
}
private fun parseConfigData(data: String): Map<String, Map<String, String>> {
val parseData = mutableMapOf<String, Map<String, String>>()
var currentSection: Pair<String, MutableMap<String, String>>? = null
data.lines().forEach { line ->
if (line.isNotEmpty()) {
if (line.startsWith('[')) {
currentSection?.let {
parseData.put(it.first, it.second)
}
currentSection =
line.substring(1, line.indexOfLast { it == ']' }) to mutableMapOf()
} else {
val parameter = line.split("=", limit = 2)
currentSection!!.second.put(parameter.first().trim(), parameter.last().trim())
Log.i(tag, "Permission okay")
mConfig = json!!
mProtocol = mConfig!!.getString("protocol")
when (mProtocol) {
"openvpn" -> startOpenVpn()
"wireguard" -> startWireGuard()
else -> {
Log.e(tag, "No protocol")
return 0
}
}
return 1
}
currentSection?.let {
parseData.put(it.first, it.second)
}
return parseData
}
/**
* Create a Wireguard [Config] from a [json] string -
* The [json] will be created in AndroidVpnProtocol.cpp
*/
private fun buildWireugardConfig(obj: JSONObject): Config {
val confBuilder = Config.Builder()
val wireguardConfigData = obj.getJSONObject("wireguard_config_data")
val config = parseConfigData(wireguardConfigData.getString("config"))
val peerBuilder = Peer.Builder()
val peerConfig = config["Peer"]!!
peerBuilder.setPublicKey(Key.fromBase64(peerConfig["PublicKey"]))
peerConfig["PresharedKey"]?.let {
peerBuilder.setPreSharedKey(Key.fromBase64(it))
fun establish(): ParcelFileDescriptor? {
mbuilder.allowFamily(OsConstants.AF_INET)
mbuilder.allowFamily(OsConstants.AF_INET6)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) mbuilder.setMetered(false)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) setUnderlyingNetworks(null)
return mbuilder.establish()
}
val allowedIPList = peerConfig["AllowedIPs"]?.split(",") ?: emptyList()
if (allowedIPList.isEmpty()) {
val internet = InetNetwork.parse("0.0.0.0/0") // aka The whole internet.
peerBuilder.addAllowedIp(internet)
} else {
allowedIPList.forEach {
val network = InetNetwork.parse(it.trim())
peerBuilder.addAllowedIp(network)
fun setMtu(mtu: Int) {
mbuilder.setMtu(mtu)
}
fun addAddress(ip: String, len: Int){
Log.v(tag, "mbuilder.addAddress($ip, $len)")
mbuilder.addAddress(ip, len)
}
fun addRoute(ip: String, len: Int){
Log.v(tag, "mbuilder.addRoute($ip, $len)")
mbuilder.addRoute(ip, len)
}
fun addDNS(ip: String){
Log.v(tag, "mbuilder.addDnsServer($ip)")
mbuilder.addDnsServer(ip)
if ("samsung".equals(Build.BRAND) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
mbuilder.addRoute(ip, 32)
}
}
peerBuilder.setEndpoint(InetEndpoint.parse(peerConfig["Endpoint"]))
peerConfig["PersistentKeepalive"]?.let {
peerBuilder.setPersistentKeepalive(it.toInt())
fun setSessionName(name: String){
Log.v(tag, "mbuilder.setSession($name)")
mbuilder.setSession(name)
}
confBuilder.addPeer(peerBuilder.build())
val ifaceBuilder = Interface.Builder()
val ifaceConfig = config["Interface"]!!
ifaceBuilder.parsePrivateKey(ifaceConfig["PrivateKey"])
ifaceBuilder.addAddress(InetNetwork.parse(ifaceConfig["Address"]))
ifaceConfig["DNS"]!!.split(",").forEach {
ifaceBuilder.addDnsServer(InetNetwork.parse(it.trim()).address)
fun addHttpProxy(host: String, port: Int): Boolean{
val proxyInfo = ProxyInfo.buildDirectProxy(host, port)
Log.v(tag, "mbuilder.addHttpProxy($host, $port)")
mbuilder.setHttpProxy(proxyInfo)
return true
}
/*val jExcludedApplication = obj.getJSONArray("excludedApps")
(0 until jExcludedApplication.length()).toList().forEach {
val appName = jExcludedApplication.get(it).toString()
ifaceBuilder.excludeApplication(appName)
}*/
confBuilder.setInterface(ifaceBuilder.build())
return confBuilder.build()
}
fun getVpnConfig(): JSONObject {
return mConfig!!
}
private fun startOpenVpn() {
mOpenVPNThreadv3 = OpenVPNThreadv3(this)
Thread({
mOpenVPNThreadv3?.run()
}).start()
}
private fun startWireGuard() {
val wireguard_conf = buildWireugardConfig(mConfig!!)
if (currentTunnelHandle != -1) {
Log.e(tag, "Tunnel already up")
// Turn the tunnel down because this might be a switch
wgTurnOff(currentTunnelHandle)
fun setDomain(domain: String) {
Log.v(tag, "mbuilder.setDomain($domain)")
mbuilder.addSearchDomain(domain)
}
val wgConfig: String = wireguard_conf!!.toWgUserspaceString()
val builder = Builder()
setupBuilder(wireguard_conf, builder)
builder.setSession("avpn0")
builder.establish().use { tun ->
if (tun == null) return
Log.i(tag, "Go backend " + wgVersion())
currentTunnelHandle = wgTurnOn("avpn0", tun.detachFd(), wgConfig)
}
if (currentTunnelHandle < 0) {
Log.e(tag, "Activation Error Code -> $currentTunnelHandle")
fun turnOff() {
Log.v(tag, "Try to disable tunnel")
when(mProtocol){
"wireguard" -> wgTurnOff(currentTunnelHandle)
"openvpn" -> ovpnTurnOff()
else -> {
Log.e(tag, "No protocol")
}
}
currentTunnelHandle = -1
stopForeground(true)
isUp = false
return
}
protect(wgGetSocketV4(currentTunnelHandle))
protect(wgGetSocketV6(currentTunnelHandle))
isUp = true
// Store the config in case the service gets
// asked boot vpn from the OS
val prefs = Prefs.get(this)
prefs.edit()
.putString("lastConf", mConfig.toString())
.apply()
}
companion object {
@JvmStatic
fun startService(c: Context) {
c.applicationContext.startService(
Intent(c.applicationContext, VPNService::class.java).apply {
putExtra("startOnly", true)
})
stopSelf();
}
@JvmStatic
private external fun wgGetConfig(handle: Int): String?
@JvmStatic
private external fun wgGetSocketV4(handle: Int): Int
private fun ovpnTurnOff() {
mOpenVPNThreadv3?.stop()
mOpenVPNThreadv3 = null
Log.e(tag, "mOpenVPNThreadv3 stop!")
}
/**
* Configures an Android VPN Service Tunnel
* with a given Wireguard Config
*/
private fun setupBuilder(config: Config, builder: Builder) {
// Setup Split tunnel
for (excludedApplication in config.`interface`.excludedApplications)
builder.addDisallowedApplication(excludedApplication)
@JvmStatic
private external fun wgGetSocketV6(handle: Int): Int
// Device IP
for (addr in config.`interface`.addresses) builder.addAddress(addr.address, addr.mask)
// DNS
for (addr in config.`interface`.dnsServers) builder.addDnsServer(addr.hostAddress)
// Add All routes the VPN may route tos
for (peer in config.peers) {
for (addr in peer.allowedIps) {
builder.addRoute(addr.address, addr.mask)
}
}
builder.allowFamily(OsConstants.AF_INET)
builder.allowFamily(OsConstants.AF_INET6)
builder.setMtu(config.`interface`.mtu.orElse(1280))
@JvmStatic
private external fun wgTurnOff(handle: Int)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) builder.setMetered(false)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) setUnderlyingNetworks(null)
@JvmStatic
private external fun wgTurnOn(ifName: String, tunFd: Int, settings: String): Int
builder.setBlocking(true)
}
@JvmStatic
private external fun wgVersion(): String?
}
}
/**
* Gets config value for {key} from the Current
* running Wireguard tunnel
*/
private fun getConfigValue(key: String): String? {
if (!isUp) {
return null
}
val config = wgGetConfig(currentTunnelHandle) ?: return null
val lines = config.split("\n")
for (line in lines) {
val parts = line.split("=")
val k = parts.first()
val value = parts.last()
if (key == k) {
return value
}
}
return null
}
private fun parseConfigData(data: String): Map<String, Map<String, String>> {
val parseData = mutableMapOf<String, Map<String, String>>()
var currentSection: Pair<String, MutableMap<String, String>>? = null
data.lines().forEach { line ->
if (line.isNotEmpty()) {
if (line.startsWith('[')) {
currentSection?.let {
parseData.put(it.first, it.second)
}
currentSection = line.substring(1, line.indexOfLast { it == ']' }) to mutableMapOf()
} else {
val parameter = line.split("=", limit = 2)
currentSection!!.second.put(parameter.first().trim(), parameter.last().trim())
}
}
}
currentSection?.let {
parseData.put(it.first, it.second)
}
return parseData
}
/**
* Create a Wireguard [Config] from a [json] string -
* The [json] will be created in AndroidVpnProtocol.cpp
*/
private fun buildWireugardConfig(obj: JSONObject): Config {
val confBuilder = Config.Builder()
val wireguardConfigData = obj.getJSONObject("wireguard_config_data")
val config = parseConfigData(wireguardConfigData.getString("config"))
val peerBuilder = Peer.Builder()
val peerConfig = config["Peer"]!!
peerBuilder.setPublicKey(Key.fromBase64(peerConfig["PublicKey"]))
peerConfig["PresharedKey"]?.let {
peerBuilder.setPreSharedKey(Key.fromBase64(it))
}
val allowedIPList = peerConfig["AllowedIPs"]?.split(",") ?: emptyList()
if (allowedIPList.isEmpty()) {
val internet = InetNetwork.parse("0.0.0.0/0") // aka The whole internet.
peerBuilder.addAllowedIp(internet)
} else {
allowedIPList.forEach {
val network = InetNetwork.parse(it.trim())
peerBuilder.addAllowedIp(network)
}
}
peerBuilder.setEndpoint(InetEndpoint.parse(peerConfig["Endpoint"]))
peerConfig["PersistentKeepalive"]?.let {
peerBuilder.setPersistentKeepalive(it.toInt())
}
confBuilder.addPeer(peerBuilder.build())
val ifaceBuilder = Interface.Builder()
val ifaceConfig = config["Interface"]!!
ifaceBuilder.parsePrivateKey(ifaceConfig["PrivateKey"])
ifaceBuilder.addAddress(InetNetwork.parse(ifaceConfig["Address"]))
ifaceConfig["DNS"]!!.split(",").forEach {
ifaceBuilder.addDnsServer(InetNetwork.parse(it.trim()).address)
}
/*val jExcludedApplication = obj.getJSONArray("excludedApps")
(0 until jExcludedApplication.length()).toList().forEach {
val appName = jExcludedApplication.get(it).toString()
ifaceBuilder.excludeApplication(appName)
}*/
confBuilder.setInterface(ifaceBuilder.build())
return confBuilder.build()
}
fun getVpnConfig(): JSONObject {
return mConfig!!
}
private fun startOpenVpn() {
mOpenVPNThreadv3 = OpenVPNThreadv3 (this)
Thread ({
mOpenVPNThreadv3?.run()
}).start()
}
private fun startWireGuard(){
val wireguard_conf = buildWireugardConfig(mConfig!!)
if (currentTunnelHandle != -1) {
Log.e(tag, "Tunnel already up")
// Turn the tunnel down because this might be a switch
wgTurnOff(currentTunnelHandle)
}
val wgConfig: String = wireguard_conf!!.toWgUserspaceString()
val builder = Builder()
setupBuilder(wireguard_conf, builder)
builder.setSession("avpn0")
builder.establish().use { tun ->
if (tun == null)return
Log.i(tag, "Go backend " + wgVersion())
currentTunnelHandle = wgTurnOn("avpn0", tun.detachFd(), wgConfig)
}
if (currentTunnelHandle < 0) {
Log.e(tag, "Activation Error Code -> $currentTunnelHandle")
isUp = false
return
}
protect(wgGetSocketV4(currentTunnelHandle))
protect(wgGetSocketV6(currentTunnelHandle))
isUp = true
// Store the config in case the service gets
// asked boot vpn from the OS
val prefs = Prefs.get(this)
prefs.edit()
.putString("lastConf", mConfig.toString())
.apply()
NotificationUtil.show(this) // Go foreground
}
companion object {
@JvmStatic
fun startService(c: Context) {
c.applicationContext.startService(
Intent(c.applicationContext, VPNService::class.java).apply {
putExtra("startOnly", true)
})
}
@JvmStatic
private external fun wgGetConfig(handle: Int): String?
@JvmStatic
private external fun wgGetSocketV4(handle: Int): Int
@JvmStatic
private external fun wgGetSocketV6(handle: Int): Int
@JvmStatic
private external fun wgTurnOff(handle: Int)
@JvmStatic
private external fun wgTurnOn(ifName: String, tunFd: Int, settings: String): Int
@JvmStatic
private external fun wgVersion(): String?
}
}

View File

@@ -1,6 +0,0 @@
package org.ftylitak.qzxing;
public class NativeFunctions {
public static native void onPermissionsGranted();
public static native void onPermissionsDenied();
}

View File

@@ -1,25 +0,0 @@
package org.ftylitak.qzxing;
import android.Manifest;
import android.content.pm.PackageManager;
import org.qtproject.qt5.android.bindings.QtActivity;
import static org.ftylitak.qzxing.Utilities.REQUEST_CAMERA;
public class QZXingLiveActivity extends QtActivity {
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case REQUEST_CAMERA: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
NativeFunctions.onPermissionsGranted();
} else {
NativeFunctions.onPermissionsDenied();
}
return;
}
}
}
}

View File

@@ -1,42 +0,0 @@
package org.ftylitak.qzxing;
import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.util.ArrayList;
public class Utilities {
public static final int REQUEST_CAMERA = 0;
public static final String[] requiredPermissionsModifyPhoneState = {
Manifest.permission.CAMERA,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
public static void checkAndRequestPermissionList(Activity activity, String[] permissions) {
ArrayList<String> permissionsToRequest = new ArrayList<>();
for (int i = 0; i < permissions.length; i++) {
if (ContextCompat.checkSelfPermission(activity, permissions[i])
!= PackageManager.PERMISSION_GRANTED)
permissionsToRequest.add(permissions[i]);
}
if (permissionsToRequest.size() != 0)
ActivityCompat.requestPermissions(activity,
permissionsToRequest.toArray(new String[0]),
REQUEST_CAMERA);
else
NativeFunctions.onPermissionsGranted();
}
public static void requestQZXingPermissions(Activity activity) {
checkAndRequestPermissionList(activity, requiredPermissionsModifyPhoneState);
}
}

View File

@@ -1,21 +1,19 @@
QT += widgets core gui network xml remoteobjects quick svg
QT += widgets core gui network xml remoteobjects quick
TARGET = AmneziaVPN
TEMPLATE = app
#CONFIG += console
CONFIG += qtquickcompiler
CONFIG += qzxing_multimedia \
enable_decoder_qr_code \
enable_encoder_qr_code
DEFINES += QT_DEPRECATED_WARNINGS
include("3rd/QtSsh/src/ssh/qssh.pri")
include("3rd/QtSsh/src/botan/botan.pri")
!android:!ios:include("3rd/SingleApplication/singleapplication.pri")
include("3rd/QRCodeGenerator/QRCodeGenerator.pri")
include ("3rd/SortFilterProxyModel/SortFilterProxyModel.pri")
include("3rd/qzxing/src/QZXing-components.pri")
include("3rd/AdpInfo/adpinfo.pri")
INCLUDEPATH += $$PWD/3rd/OpenSSL/include
DEPENDPATH += $$PWD/3rd/OpenSSL/include
@@ -31,14 +29,15 @@ HEADERS += \
containers/containers_defs.h \
core/defs.h \
core/errorstrings.h \
core/ipcclient.h \
configurators/openvpn_configurator.h \
core/privileged_process.h \
core/scripts_registry.h \
core/server_defs.h \
core/servercontroller.h \
debug.h \
defines.h \
managementserver.h \
platforms/linux/leakdetector.h \
protocols/protocols_defs.h \
settings.h \
ui/notificationhandler.h \
@@ -50,7 +49,6 @@ HEADERS += \
ui/pages_logic/NetworkSettingsLogic.h \
ui/pages_logic/NewServerProtocolsLogic.h \
ui/pages_logic/PageLogicBase.h \
ui/pages_logic/QrDecoderLogic.h \
ui/pages_logic/ServerConfiguringProgressLogic.h \
ui/pages_logic/ServerContainersLogic.h \
ui/pages_logic/ServerListLogic.h \
@@ -87,14 +85,15 @@ SOURCES += \
configurators/wireguard_configurator.cpp \
containers/containers_defs.cpp \
core/errorstrings.cpp \
core/ipcclient.cpp \
configurators/openvpn_configurator.cpp \
core/privileged_process.cpp \
core/scripts_registry.cpp \
core/server_defs.cpp \
core/servercontroller.cpp \
debug.cpp \
main.cpp \
managementserver.cpp \
platforms/linux/leakdetector.cpp \
protocols/protocols_defs.cpp \
settings.cpp \
ui/notificationhandler.cpp \
@@ -105,7 +104,6 @@ SOURCES += \
ui/pages_logic/NetworkSettingsLogic.cpp \
ui/pages_logic/NewServerProtocolsLogic.cpp \
ui/pages_logic/PageLogicBase.cpp \
ui/pages_logic/QrDecoderLogic.cpp \
ui/pages_logic/ServerConfiguringProgressLogic.cpp \
ui/pages_logic/ServerContainersLogic.cpp \
ui/pages_logic/ServerListLogic.cpp \
@@ -144,14 +142,12 @@ win32 {
RC_FILE = platform_win/vpnclient.rc
HEADERS += \
protocols/ikev2_vpn_protocol_windows.h \
ui/framelesswindow.h
ui/framelesswindow.h \
SOURCES += \
protocols/ikev2_vpn_protocol_windows.cpp \
ui/framelesswindow.cpp
VERSION = 2.0.0.0
VERSION = 1.0.0.0
QMAKE_TARGET_COMPANY = "AmneziaVPN"
QMAKE_TARGET_PRODUCT = "AmneziaVPN"
@@ -193,41 +189,27 @@ macx {
linux:!android {
DEFINES += MVPN_LINUX
HEADERS += \
platforms/linux/linuxsystemtraynotificationhandler.h \
SOURCES += \
platforms/linux/linuxsystemtraynotificationhandler.cpp \
LIBS += /usr/lib/x86_64-linux-gnu/libcrypto.a
LIBS += /usr/lib/x86_64-linux-gnu/libssl.a
INCLUDEPATH += $$PWD/platforms/linux
}
win32|macx|linux:!android {
DEFINES += AMNEZIA_DESKTOP
HEADERS += \
core/ipcclient.h \
core/privileged_process.h \
ui/systemtray_notificationhandler.h \
protocols/openvpnprotocol.h \
protocols/ikev2_vpn_protocol.h \
protocols/openvpnovercloakprotocol.h \
protocols/shadowsocksvpnprotocol.h \
protocols/wireguardprotocol.h \
SOURCES += \
core/ipcclient.cpp \
core/privileged_process.cpp \
ui/systemtray_notificationhandler.cpp \
protocols/openvpnprotocol.cpp \
protocols/ikev2_vpn_protocol.cpp \
protocols/openvpnovercloakprotocol.cpp \
protocols/shadowsocksvpnprotocol.cpp \
protocols/wireguardprotocol.cpp \
REPC_REPLICA += ../ipc/ipc_interface.rep
REPC_REPLICA += ../ipc/ipc_process_interface.rep
}
android {
@@ -237,13 +219,11 @@ android {
INCLUDEPATH += platforms/android
HEADERS += \
platforms/android/native.h \
platforms/android/android_controller.h \
platforms/android/android_notificationhandler.h \
protocols/android_vpnprotocol.h
SOURCES += \
platforms/android/native.cpp \
platforms/android/android_controller.cpp \
platforms/android/android_notificationhandler.cpp \
protocols/android_vpnprotocol.cpp
@@ -297,11 +277,6 @@ ios {
LIBS += -framework StoreKit
LIBS += -framework UserNotifications
# LIBS += $$PWD/3rd/OpenVPNAdapter/build/Debug-iphoneos/LZ4.framework
# LIBS += $$PWD/3rd/OpenVPNAdapter/build/Debug-iphoneos/mbedTLS.framework
# LIBS += $$PWD/3rd/OpenVPNAdapter/build/Debug-iphoneos/OpenVPNClient.framework
# LIBS += $$PWD/3rd/OpenVPNAdapter/build/Debug-iphoneos/OpenVPNAdapter.framework
DEFINES += MVPN_IOS
HEADERS += \
@@ -335,7 +310,7 @@ ios {
QMAKE_DEVELOPMENT_TEAM = X7UJ388FXK
QMAKE_PROVISIONING_PROFILE = f2fefb59-14aa-4aa9-ac14-1d5531b06dcc
QMAKE_XCODE_CODE_SIGN_IDENTITY = "Apple Distribution"
QMAKE_INFO_PLIST = $$PWD/ios/app/Info.plist
QMAKE_INFO_PLIST= $$PWD/ios/app/Info.plist
XCODEBUILD_FLAGS += -allowProvisioningUpdates
@@ -378,4 +353,6 @@ ios {
}
REPC_REPLICA += ../ipc/ipc_interface.rep
!ios: REPC_REPLICA += ../ipc/ipc_process_interface.rep

View File

@@ -22,7 +22,7 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se
connData.host = credentials.hostName;
connData.clientId = Utils::getRandomString(16);
connData.password = Utils::getRandomString(16);
connData.password = "";
//connData.password = "";
QString certFileName = "/opt/amnezia/ikev2/clients/" + connData.clientId + ".p12";

View File

@@ -10,7 +10,6 @@
#include <QJsonDocument>
#include "containers/containers_defs.h"
#include "utils.h"
Settings &VpnConfigurator::m_settings()
{
@@ -42,59 +41,24 @@ QString VpnConfigurator::genVpnProtocolConfig(const ServerCredentials &credentia
}
}
QPair<QString, QString> VpnConfigurator::getDnsForConfig(int serverIndex)
QString VpnConfigurator::processConfigWithLocalSettings(DockerContainer container, Proto proto, QString config)
{
QPair<QString, QString> dns;
bool useAmneziaDns = m_settings().useAmneziaDns();
const QJsonObject &server = m_settings().server(serverIndex);
dns.first = server.value(config_key::dns1).toString();
dns.second = server.value(config_key::dns2).toString();
if (dns.first.isEmpty() || !Utils::checkIPv4Format(dns.first)) {
if (useAmneziaDns && m_settings().containers(serverIndex).contains(DockerContainer::Dns)) {
dns.first = protocols::dns::amneziaDnsIp;
}
else dns.first = m_settings().primaryDns();
}
if (dns.second.isEmpty() || !Utils::checkIPv4Format(dns.second)) {
dns.second = m_settings().secondaryDns();
}
qDebug() << "VpnConfigurator::getDnsForConfig" << dns.first << dns.second;
return dns;
}
QString &VpnConfigurator::processConfigWithDnsSettings(int serverIndex, DockerContainer container,
Proto proto, QString &config)
{
auto dns = getDnsForConfig(serverIndex);
config.replace("$PRIMARY_DNS", dns.first);
config.replace("$SECONDARY_DNS", dns.second);
return config;
}
QString &VpnConfigurator::processConfigWithLocalSettings(int serverIndex, DockerContainer container,
Proto proto, QString &config)
{
processConfigWithDnsSettings(serverIndex, container, proto, config);
config.replace("$PRIMARY_DNS", m_settings().primaryDns());
config.replace("$SECONDARY_DNS", m_settings().secondaryDns());
if (proto == Proto::OpenVpn) {
config = OpenVpnConfigurator::processConfigWithLocalSettings(config);
return OpenVpnConfigurator::processConfigWithLocalSettings(config);
}
return config;
}
QString &VpnConfigurator::processConfigWithExportSettings(int serverIndex, DockerContainer container,
Proto proto, QString &config)
QString VpnConfigurator::processConfigWithExportSettings(DockerContainer container, Proto proto, QString config)
{
processConfigWithDnsSettings(serverIndex, container, proto, config);
config.replace("$PRIMARY_DNS", m_settings().primaryDns());
config.replace("$SECONDARY_DNS", m_settings().secondaryDns());
if (proto == Proto::OpenVpn) {
config = OpenVpnConfigurator::processConfigWithExportSettings(config);
return OpenVpnConfigurator::processConfigWithExportSettings(config);
}
return config;
}

View File

@@ -15,11 +15,8 @@ public:
static QString genVpnProtocolConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, Proto proto, ErrorCode *errorCode = nullptr);
static QPair<QString, QString> getDnsForConfig(int serverIndex);
static QString &processConfigWithDnsSettings(int serverIndex, DockerContainer container, Proto proto, QString &config);
static QString &processConfigWithLocalSettings(int serverIndex, DockerContainer container, Proto proto, QString &config);
static QString &processConfigWithExportSettings(int serverIndex, DockerContainer container, Proto proto, QString &config);
static QString processConfigWithLocalSettings(DockerContainer container, Proto proto, QString config);
static QString processConfigWithExportSettings(DockerContainer container, Proto proto, QString config);
// workaround for containers which is not support normal configaration
static void updateContainerConfigAfterInstallation(DockerContainer container,

View File

@@ -50,7 +50,7 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::genClientKeys()
}
WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardConfig(const ServerCredentials &credentials,
DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode)
DockerContainer container, ErrorCode *errorCode)
{
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
connData.host = credentials.hostName;
@@ -61,49 +61,6 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
}
ErrorCode e = ErrorCode::NoError;
// Get list of already created clients (only IP addreses)
QString nextIpNumber;
{
QString script = QString("cat %1 | grep AllowedIPs").arg(amnezia::protocols::wireguard::serverConfigPath);
QString stdOut;
auto cbReadStdOut = [&](const QString &data, QSharedPointer<QSsh::SshRemoteProcess> proc) {
stdOut += data + "\n";
};
ServerController::runContainerScript(credentials, container, script, cbReadStdOut);
stdOut.replace("AllowedIPs = ", "");
stdOut.replace("/32", "");
QStringList ips = stdOut.split("\n", Qt::SkipEmptyParts);
// Calc next IP address
if (ips.isEmpty()) {
nextIpNumber = "2";
}
else {
int next = ips.last().split(".").last().toInt() + 1;
if (next > 254) {
if (errorCode) *errorCode = ErrorCode::AddressPoolError;
return connData;
}
nextIpNumber = QString::number(next);
}
}
QString subnetIp = containerConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress);
{
QStringList l = subnetIp.split(".", Qt::SkipEmptyParts);
if (l.isEmpty()) {
if (errorCode) *errorCode = ErrorCode::AddressPoolError;
return connData;
}
l.removeLast();
l.append(nextIpNumber);
connData.clientIP = l.join(".");
}
// Get keys
connData.serverPubKey = ServerController::getTextFileFromContainer(container, credentials, amnezia::protocols::wireguard::serverPublicKeyPath, &e);
connData.serverPubKey.replace("\n", "");
if (e) {
@@ -119,15 +76,18 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
return connData;
}
// Add client to config
QString configPart = QString(
"[Peer]\n"
"PublicKey = %1\n"
"PresharedKey = %2\n"
"AllowedIPs = %3/32\n\n").
"AllowedIPs = $WIREGUARD_SUBNET_IP/$WIREGUARD_SUBNET_CIDR\n\n").
arg(connData.clientPubKey).
arg(connData.pskKey).
arg(connData.clientIP);
arg(connData.pskKey);
configPart = ServerController::replaceVars(configPart, ServerController::genVarsForScript(credentials, container));
qDebug().noquote() << "Adding wg conf part to server" << configPart;
e = ServerController::uploadTextFileToContainer(container, credentials, configPart,
protocols::wireguard::serverConfigPath, QSsh::SftpOverwriteMode::SftpAppendToExisting);
@@ -156,13 +116,12 @@ QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &crede
QString config = ServerController::replaceVars(amnezia::scriptData(ProtocolScriptType::wireguard_template, container),
ServerController::genVarsForScript(credentials, container, containerConfig));
ConnectionData connData = prepareWireguardConfig(credentials, container, containerConfig, errorCode);
ConnectionData connData = prepareWireguardConfig(credentials, container, errorCode);
if (errorCode && *errorCode) {
return "";
}
config.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", connData.clientPrivKey);
config.replace("$WIREGUARD_CLIENT_IP", connData.clientIP);
config.replace("$WIREGUARD_SERVER_PUBLIC_KEY", connData.serverPubKey);
config.replace("$WIREGUARD_PSK", connData.pskKey);
@@ -171,7 +130,6 @@ QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &crede
jConfig[config_key::hostName] = connData.host;
jConfig[config_key::client_priv_key] = connData.clientPrivKey;
jConfig[config_key::client_ip] = connData.clientIP;
jConfig[config_key::client_pub_key] = connData.clientPubKey;
jConfig[config_key::psk_key] = connData.pskKey;
jConfig[config_key::server_pub_key] = connData.serverPubKey;

View File

@@ -15,7 +15,6 @@ public:
struct ConnectionData {
QString clientPrivKey; // client private key
QString clientPubKey; // client public key
QString clientIP; // internal client IP address
QString serverPubKey; // tls-auth key
QString pskKey; // preshared key
QString host; // host ip
@@ -30,7 +29,7 @@ public:
private:
static ConnectionData prepareWireguardConfig(const ServerCredentials &credentials,
DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);
DockerContainer container, ErrorCode *errorCode = nullptr);
static ConnectionData genClientKeys();

View File

@@ -136,7 +136,7 @@ Proto ContainerProps::defaultProtocol(DockerContainer c)
}
}
bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
bool ContainerProps::isWorkingOnPlatform(DockerContainer c)
{
#ifdef Q_OS_WINDOWS
return true;
@@ -148,11 +148,7 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
default: return false;
}
#elif defined (Q_OS_MAC)
switch (c) {
case DockerContainer::WireGuard: return false;
case DockerContainer::Ipsec: return false;
default: return true;
}
return false;
#elif defined (Q_OS_ANDROID)
switch (c) {
@@ -162,7 +158,7 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
}
#elif defined (Q_OS_LINUX)
return true;
return false;
#else
return false;

View File

@@ -54,7 +54,7 @@ public:
// it may be changed fot future containers :)
Q_INVOKABLE static Proto defaultProtocol(DockerContainer c);
Q_INVOKABLE static bool isSupportedByCurrentPlatform(DockerContainer c);
Q_INVOKABLE static bool isWorkingOnPlatform(DockerContainer c);
};

View File

@@ -6,8 +6,6 @@
namespace amnezia {
constexpr const qint16 qrMagicCode = 1984;
struct ServerCredentials
{
QString hostName;
@@ -60,7 +58,6 @@ enum ErrorCode
OpenVpnAdaptersInUseError,
OpenVpnUnknownError,
OpenVpnTapAdapterError,
AddressPoolError,
// 3rd party utils errors
OpenSslFailed,

View File

@@ -45,7 +45,6 @@ QString errorString(ErrorCode code){
// VPN errors
case (OpenVpnAdaptersInUseError): return QObject::tr("Can't connect: another VPN connection is active");
case (OpenVpnTapAdapterError): return QObject::tr("Can't setup OpenVPN TAP network adapter");
case (AddressPoolError): return QObject::tr("VPN pool error: no available addresses");
case(InternalError):
default:

View File

@@ -65,6 +65,7 @@ bool IpcClient::init(IpcClient *instance)
QSharedPointer<PrivilegedProcess> IpcClient::CreatePrivilegedProcess()
{
#ifndef Q_OS_IOS
if (! Instance()->m_ipcClient || ! Instance()->m_ipcClient->isReplicaValid()) {
qWarning() << "IpcClient::createPrivilegedProcess : IpcClient IpcClient replica is not valid";
return nullptr;
@@ -106,6 +107,9 @@ QSharedPointer<PrivilegedProcess> IpcClient::CreatePrivilegedProcess()
auto proccessReplica = QSharedPointer<PrivilegedProcess>(pd->ipcProcess);
return proccessReplica;
#else
return QSharedPointer<PrivilegedProcess>();
#endif
}

View File

@@ -1,5 +1,6 @@
#include "privileged_process.h"
#ifndef Q_OS_IOS
PrivilegedProcess::PrivilegedProcess() :
IpcProcessInterfaceReplica()
{
@@ -25,3 +26,4 @@ void PrivilegedProcess::waitForFinished(int msecs)
loop->exec();
}
#endif // Q_OS_IOS

View File

@@ -3,6 +3,7 @@
#include <QObject>
#ifndef Q_OS_IOS
#include "rep_ipc_process_interface_replica.h"
// This class is dangerous - instance of this class casted from base class,
// so it support only functions
@@ -19,6 +20,11 @@ public:
};
#else // defined Q_OS_IOS
class IpcProcessInterfaceReplica {};
class PrivilegedProcess {};
#endif // Q_OS_IOS
#endif // PRIVILEGED_PROCESS_H

View File

@@ -79,8 +79,6 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
}
qDebug().noquote() << "EXEC" << lineToExec;
Debug::appendSshLog("Run command:" + lineToExec);
QSharedPointer<SshRemoteProcess> proc = client->createRemoteProcess(lineToExec.toUtf8());
if (!proc) {
@@ -103,9 +101,7 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardOutput, &wait, [proc, cbReadStdOut](){
QString s = proc->readAllStandardOutput();
if (s != "." && !s.isEmpty()) {
Debug::appendSshLog("Output: " + s);
qDebug().noquote() << "stdout" << s;
}
if (cbReadStdOut) cbReadStdOut(s, proc);
@@ -114,7 +110,6 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardError, &wait, [proc, cbReadStdErr](){
QString s = proc->readAllStandardError();
if (s != "." && !s.isEmpty()) {
Debug::appendSshLog("Output: " + s);
qDebug().noquote() << "stderr" << s;
}
if (cbReadStdErr) cbReadStdErr(s, proc);
@@ -140,7 +135,6 @@ ErrorCode ServerController::runContainerScript(const ServerCredentials &credenti
const std::function<void (const QString &, QSharedPointer<QSsh::SshRemoteProcess>)> &cbReadStdErr)
{
QString fileName = "/opt/amnezia/" + Utils::getRandomString(16) + ".sh";
Debug::appendSshLog("Run container script for " + ContainerProps::containerToString(container) + ":\n" + script);
ErrorCode e = uploadTextFileToContainer(container, credentials, script, fileName);
if (e) return e;
@@ -171,9 +165,8 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
};
// mkdir
QString mkdir = QString("sudo docker exec -i $CONTAINER_NAME mkdir -p \"$(dirname %1)\"")
.arg(path);
QFileInfo fi(path);
QString mkdir = "sudo docker exec -i $CONTAINER_NAME mkdir -p " + fi.absoluteDir().absolutePath();
e = runScript(credentials,
replaceVars(mkdir, genVarsForScript(credentials, container)));
if (e) return e;
@@ -484,10 +477,8 @@ QJsonObject ServerController::createContainerInitialConfig(DockerContainer conta
bool ServerController::isReinstallContainerRequred(DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig)
{
Proto mainProto = ContainerProps::defaultProtocol(container);
const QJsonObject &oldProtoConfig = oldConfig.value(ProtocolProps::protoToString(mainProto)).toObject();
const QJsonObject &newProtoConfig = newConfig.value(ProtocolProps::protoToString(mainProto)).toObject();
const QJsonObject &oldProtoConfig = oldConfig[ContainerProps::containerToString(container)].toObject();
const QJsonObject &newProtoConfig = newConfig[ContainerProps::containerToString(container)].toObject();
if (container == DockerContainer::OpenVpn) {
if (oldProtoConfig.value(config_key::transport_proto).toString(protocols::openvpn::defaultTransportProto) !=
@@ -675,9 +666,9 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
vars.append({{"$FAKE_WEB_SITE_ADDRESS", cloakConfig.value(config_key::site).toString(protocols::cloak::defaultRedirSite) }});
// Wireguard vars
vars.append({{"$WIREGUARD_SUBNET_IP", wireguarConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress) }});
vars.append({{"$WIREGUARD_SUBNET_CIDR", wireguarConfig.value(config_key::subnet_cidr).toString(protocols::wireguard::defaultSubnetCidr) }});
vars.append({{"$WIREGUARD_SUBNET_MASK", wireguarConfig.value(config_key::subnet_mask).toString(protocols::wireguard::defaultSubnetMask) }});
vars.append({{"$WIREGUARD_SUBNET_IP", openvpnConfig.value(config_key::subnet_address).toString(protocols::wireguard::defaultSubnetAddress) }});
vars.append({{"$WIREGUARD_SUBNET_CIDR", openvpnConfig.value(config_key::subnet_cidr).toString(protocols::wireguard::defaultSubnetCidr) }});
vars.append({{"$WIREGUARD_SUBNET_MASK", openvpnConfig.value(config_key::subnet_mask).toString(protocols::wireguard::defaultSubnetMask) }});
vars.append({{"$WIREGUARD_SERVER_PORT", wireguarConfig.value(config_key::port).toString(protocols::wireguard::defaultPort) }});

View File

@@ -5,7 +5,6 @@
#include <QObject>
#include "sshconnection.h"
#include "sshremoteprocess.h"
#include "debug.h"
#include "defs.h"
#include "settings.h"

View File

@@ -1,4 +1,3 @@
#include <QDateTime>
#include <QDebug>
#include <QDesktopServices>
#include <QDir>
@@ -11,13 +10,9 @@
#include "defines.h"
#include "utils.h"
#ifdef AMNEZIA_DESKTOP
#include <core/ipcclient.h>
#endif
QFile Debug::m_file;
QTextStream Debug::m_textStream;
QString Debug::m_logFileName = QString("%1.log").arg(APPLICATION_NAME);
QString Debug::m_logFileName;
void debugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg)
{
@@ -31,30 +26,10 @@ void debugMessageHandler(QtMsgType type, const QMessageLogContext& context, cons
}
Debug::m_textStream << qFormatLogMessage(type, context, msg) << endl << flush;
Debug::appendAllLog(qFormatLogMessage(type, context, msg));
std::cout << qFormatLogMessage(type, context, msg).toStdString() << std::endl << std::flush;
}
Debug &Debug::Instance()
{
static Debug s;
return s;
}
void Debug::appendSshLog(const QString &log)
{
QString dt = QDateTime::currentDateTime().toString();
Instance().m_sshLog.append(dt + ": " + log + "\n");
emit Instance().sshLogChanged(Instance().sshLog());
}
void Debug::appendAllLog(const QString &log)
{
Instance().m_allLog.append(log + "\n");
emit Instance().allLogChanged(Instance().allLog());
}
bool Debug::init()
{
qSetMessagePattern("%{time yyyy-MM-dd hh:mm:ss} %{type} %{message}");
@@ -65,8 +40,11 @@ bool Debug::init()
return false;
}
m_logFileName = QString("%1.log").arg(APPLICATION_NAME);
m_file.setFileName(appDir.filePath(m_logFileName));
if (!m_file.open(QIODevice::Append)) {
if (!m_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
qWarning() << "Cannot open log file:" << m_logFileName;
return false;
}
@@ -85,20 +63,6 @@ QString Debug::userLogsDir()
return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/log";
}
QString Debug::userLogsFilePath()
{
return userLogsDir() + QDir::separator() + m_logFileName;
}
QString Debug::getLogFile()
{
m_file.flush();
QFile file(userLogsFilePath());
file.open(QIODevice::ReadOnly);
return file.readAll();
}
bool Debug::openLogsFolder()
{
QString path = userLogsDir();
@@ -124,50 +88,3 @@ QString Debug::appLogFileNamePath()
{
return m_file.fileName();
}
void Debug::clearLogs()
{
bool isLogActive = m_file.isOpen();
m_file.close();
QFile file(userLogsFilePath());
file.open(QIODevice::WriteOnly | QIODevice::Truncate);
file.resize(0);
file.close();
if (isLogActive) {
init();
}
}
void Debug::clearServiceLogs()
{
#ifdef AMNEZIA_DESKTOP
IpcClient *m_IpcClient = new IpcClient;
if (!m_IpcClient->isSocketConnected()) {
if (!IpcClient::init(m_IpcClient)) {
qWarning() << "Error occured when init IPC client";
return;
}
}
if (m_IpcClient->Interface()) {
m_IpcClient->Interface()->setLogsEnabled(false);
m_IpcClient->Interface()->cleanUp();
}
else {
qWarning() << "Error occured cleaning up service logs";
}
#endif
}
void Debug::cleanUp()
{
clearLogs();
QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
dir.removeRecursively();
clearServiceLogs();
}

View File

@@ -7,37 +7,15 @@
#include <QString>
#include <QTextStream>
#include "ui/property_helper.h"
class Debug : public QObject
class Debug
{
Q_OBJECT
AUTO_PROPERTY(QString, sshLog)
AUTO_PROPERTY(QString, allLog)
public:
static Debug& Instance();
static void appendSshLog(const QString &log);
static void appendAllLog(const QString &log);
static bool init();
static bool openLogsFolder();
static bool openServiceLogsFolder();
static QString appLogFileNamePath();
static void clearLogs();
static void clearServiceLogs();
static void cleanUp();
static QString userLogsFilePath();
static QString getLogFile();
private:
Debug() {}
Debug(Debug const &) = delete;
Debug& operator= (Debug const&) = delete;
static QString userLogsDir();
static QFile m_file;

View File

@@ -4,7 +4,7 @@
#define APPLICATION_NAME "AmneziaVPN"
#define SERVICE_NAME "AmneziaVPN-service"
#define ORGANIZATION_NAME "AmneziaVPN.ORG"
#define APP_MAJOR_VERSION "2.0.8"
#define APP_VERSION "2.0.8.0"
#define APP_MAJOR_VERSION "2.0.1"
#define APP_VERSION "2.0.1.0"
#endif // DEFINES_H

Binary file not shown.

Before

Width:  |  Height:  |  Size: 499 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

BIN
client/images/plus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B

BIN
client/images/reload.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 690 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/></svg>

Before

Width:  |  Height:  |  Size: 268 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M13 7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1-5C6.49 2 2 6.49 2 12s4.49 10 10 10 10-4.49 10-10S17.51 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></svg>

Before

Width:  |  Height:  |  Size: 317 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M16 9v10H8V9h8m-1.5-6h-5l-1 1H5v2h14V4h-3.5l-1-1zM18 7H6v12c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7z"/></svg>

Before

Width:  |  Height:  |  Size: 252 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><rect fill="none" height="24" width="24"/></g><g><g><rect height="2" width="18" x="3" y="2"/><rect height="2" width="18" x="3" y="20"/><rect height="2" width="18" x="3" y="14"/><rect height="2" width="18" x="3" y="8"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 371 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"/></svg>

Before

Width:  |  Height:  |  Size: 209 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M4 10.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-6c-.83 0-1.5.67-1.5 1.5S3.17 7.5 4 7.5 5.5 6.83 5.5 6 4.83 4.5 4 4.5zm0 12c-.83 0-1.5.68-1.5 1.5s.68 1.5 1.5 1.5 1.5-.68 1.5-1.5-.67-1.5-1.5-1.5zM7 19h14v-2H7v2zm0-6h14v-2H7v2zm0-8v2h14V5H7z"/></svg>

Before

Width:  |  Height:  |  Size: 430 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><path d="M0,0h24v24H0V0z" fill="none"/></g><g><path d="M12,2L4,5v6.09c0,5.05,3.41,9.76,8,10.91c4.59-1.15,8-5.86,8-10.91V5L12,2z M18,11.09c0,4-2.55,7.7-6,8.83 c-3.45-1.13-6-4.82-6-8.83V6.31l6-2.12l6,2.12V11.09z M8.82,10.59L7.4,12l3.54,3.54l5.66-5.66l-1.41-1.41l-4.24,4.24L8.82,10.59z"/></g></svg>

Before

Width:  |  Height:  |  Size: 434 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><path d="M0,0h24v24H0V0z" fill="none"/></g><g><g><path d="M12,2L4,5v6.09c0,5.05,3.41,9.76,8,10.91c4.59-1.15,8-5.86,8-10.91V5L12,2z M18,11.09c0,4-2.55,7.7-6,8.83 c-3.45-1.13-6-4.82-6-8.83v-4.7l6-2.25l6,2.25V11.09z"/><rect height="2" width="2" x="11" y="14"/><rect height="5" width="2" x="11" y="7"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 451 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><path d="M0,0h24v24H0V0z" fill="none"/></g><g><path d="M17,8l-1.41,1.41L17.17,11H9v2h8.17l-1.58,1.58L17,16l4-4L17,8z M5,5h7V3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h7v-2H5V5z"/></g></svg>

Before

Width:  |  Height:  |  Size: 324 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><rect fill="none" height="24" width="24"/></g><g><g><path d="M14.17,13.71l1.4-2.42c0.09-0.15,0.05-0.34-0.08-0.45l-1.48-1.16c0.03-0.22,0.05-0.45,0.05-0.68s-0.02-0.46-0.05-0.69 l1.48-1.16c0.13-0.11,0.17-0.3,0.08-0.45l-1.4-2.42c-0.09-0.15-0.27-0.21-0.43-0.15L12,4.83c-0.36-0.28-0.75-0.51-1.18-0.69 l-0.26-1.85C10.53,2.13,10.38,2,10.21,2h-2.8C7.24,2,7.09,2.13,7.06,2.3L6.8,4.15C6.38,4.33,5.98,4.56,5.62,4.84l-1.74-0.7 c-0.16-0.06-0.34,0-0.43,0.15l-1.4,2.42C1.96,6.86,2,7.05,2.13,7.16l1.48,1.16C3.58,8.54,3.56,8.77,3.56,9s0.02,0.46,0.05,0.69 l-1.48,1.16C2,10.96,1.96,11.15,2.05,11.3l1.4,2.42c0.09,0.15,0.27,0.21,0.43,0.15l1.74-0.7c0.36,0.28,0.75,0.51,1.18,0.69 l0.26,1.85C7.09,15.87,7.24,16,7.41,16h2.8c0.17,0,0.32-0.13,0.35-0.3l0.26-1.85c0.42-0.18,0.82-0.41,1.18-0.69l1.74,0.7 C13.9,13.92,14.08,13.86,14.17,13.71z M8.81,11c-1.1,0-2-0.9-2-2c0-1.1,0.9-2,2-2s2,0.9,2,2C10.81,10.1,9.91,11,8.81,11z"/><path d="M21.92,18.67l-0.96-0.74c0.02-0.14,0.04-0.29,0.04-0.44c0-0.15-0.01-0.3-0.04-0.44l0.95-0.74 c0.08-0.07,0.11-0.19,0.05-0.29l-0.9-1.55c-0.05-0.1-0.17-0.13-0.28-0.1l-1.11,0.45c-0.23-0.18-0.48-0.33-0.76-0.44l-0.17-1.18 C18.73,13.08,18.63,13,18.53,13h-1.79c-0.11,0-0.21,0.08-0.22,0.19l-0.17,1.18c-0.27,0.12-0.53,0.26-0.76,0.44l-1.11-0.45 c-0.1-0.04-0.22,0-0.28,0.1l-0.9,1.55c-0.05,0.1-0.04,0.22,0.05,0.29l0.95,0.74c-0.02,0.14-0.03,0.29-0.03,0.44 c0,0.15,0.01,0.3,0.03,0.44l-0.95,0.74c-0.08,0.07-0.11,0.19-0.05,0.29l0.9,1.55c0.05,0.1,0.17,0.13,0.28,0.1l1.11-0.45 c0.23,0.18,0.48,0.33,0.76,0.44l0.17,1.18c0.02,0.11,0.11,0.19,0.22,0.19h1.79c0.11,0,0.21-0.08,0.22-0.19l0.17-1.18 c0.27-0.12,0.53-0.26,0.75-0.44l1.12,0.45c0.1,0.04,0.22,0,0.28-0.1l0.9-1.55C22.03,18.86,22,18.74,21.92,18.67z M17.63,18.83 c-0.74,0-1.35-0.6-1.35-1.35s0.6-1.35,1.35-1.35s1.35,0.6,1.35,1.35S18.37,18.83,17.63,18.83z"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>

Before

Width:  |  Height:  |  Size: 361 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M19.43 12.98c.04-.32.07-.64.07-.98 0-.34-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.09-.16-.26-.25-.44-.25-.06 0-.12.01-.17.03l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.06-.02-.12-.03-.18-.03-.17 0-.34.09-.43.25l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98 0 .33.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.09.16.26.25.44.25.06 0 .12-.01.17-.03l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.06.02.12.03.18.03.17 0 .34-.09.43-.25l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zm-1.98-1.71c.04.31.05.52.05.73 0 .21-.02.43-.05.73l-.14 1.13.89.7 1.08.84-.7 1.21-1.27-.51-1.04-.42-.9.68c-.43.32-.84.56-1.25.73l-1.06.43-.16 1.13-.2 1.35h-1.4l-.19-1.35-.16-1.13-1.06-.43c-.43-.18-.83-.41-1.23-.71l-.91-.7-1.06.43-1.27.51-.7-1.21 1.08-.84.89-.7-.14-1.13c-.03-.31-.05-.54-.05-.74s.02-.43.05-.73l.14-1.13-.89-.7-1.08-.84.7-1.21 1.27.51 1.04.42.9-.68c.43-.32.84-.56 1.25-.73l1.06-.43.16-1.13.2-1.35h1.39l.19 1.35.16 1.13 1.06.43c.43.18.83.41 1.23.71l.91.7 1.06-.43 1.27-.51.7 1.21-1.07.85-.89.7.14 1.13zM12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/></svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><rect fill="none" height="24" width="24"/><path d="M10,13c0.55,0,1,0.45,1,1s-0.45,1-1,1s-1-0.45-1-1S9.45,13,10,13 M10,11c-1.66,0-3,1.34-3,3s1.34,3,3,3s3-1.34,3-3 S11.66,11,10,11L10,11z M18.5,9l1.09-2.41L22,5.5l-2.41-1.09L18.5,2l-1.09,2.41L15,5.5l2.41,1.09L18.5,9z M21.28,12.72L20.5,11 l-0.78,1.72L18,13.5l1.72,0.78L20.5,16l0.78-1.72L23,13.5L21.28,12.72z M16.25,14c0-0.12,0-0.25-0.01-0.37l1.94-1.47l-2.5-4.33 l-2.24,0.94c-0.2-0.13-0.42-0.26-0.64-0.37L12.5,6h-5L7.2,8.41C6.98,8.52,6.77,8.65,6.56,8.78L4.32,7.83l-2.5,4.33l1.94,1.47 C3.75,13.75,3.75,13.88,3.75,14s0,0.25,0.01,0.37l-1.94,1.47l2.5,4.33l2.24-0.94c0.2,0.13,0.42,0.26,0.64,0.37L7.5,22h5l0.3-2.41 c0.22-0.11,0.43-0.23,0.64-0.37l2.24,0.94l2.5-4.33l-1.94-1.47C16.25,14.25,16.25,14.12,16.25,14z M14.83,17.64l-1.73-0.73 c-0.56,0.6-1.3,1.04-2.13,1.23L10.73,20H9.27l-0.23-1.86c-0.83-0.19-1.57-0.63-2.13-1.23l-1.73,0.73l-0.73-1.27l1.49-1.13 c-0.12-0.39-0.18-0.8-0.18-1.23c0-0.43,0.06-0.84,0.18-1.23l-1.49-1.13l0.73-1.27l1.73,0.73c0.56-0.6,1.3-1.04,2.13-1.23L9.27,8 h1.47l0.23,1.86c0.83,0.19,1.57,0.63,2.13,1.23l1.73-0.73l0.73,1.27l-1.49,1.13c0.12,0.39,0.18,0.8,0.18,1.23 c0,0.43-0.06,0.84-0.18,1.23l1.49,1.13L14.83,17.64z"/></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92s2.92-1.31 2.92-2.92c0-1.61-1.31-2.92-2.92-2.92zM18 4c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zM6 13c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm12 7.02c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1z"/></svg>

Before

Width:  |  Height:  |  Size: 679 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M22 19h-6v-4h-2.68c-1.14 2.42-3.6 4-6.32 4-3.86 0-7-3.14-7-7s3.14-7 7-7c2.72 0 5.17 1.58 6.32 4H24v6h-2v4zm-4-2h2v-4h2v-2H11.94l-.23-.67C11.01 8.34 9.11 7 7 7c-2.76 0-5 2.24-5 5s2.24 5 5 5c2.11 0 4.01-1.34 4.71-3.33l.23-.67H18v4zM7 15c-1.65 0-3-1.35-3-3s1.35-3 3-3 3 1.35 3 3-1.35 3-3 3zm0-4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1z"/></svg>

Before

Width:  |  Height:  |  Size: 498 B

View File

@@ -1,62 +0,0 @@
{
"images" : [
{
"filename" : "icon_40x40.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"filename" : "icon_60x60.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"filename" : "icon_58x58.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"filename" : "icon_87x87.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"filename" : "icon_80x80.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"filename" : "icon_120x120.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"filename" : "icon_120x120-1.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"filename" : "icon_180x180.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"filename" : "icon_1024x1024.png",
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 499 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -1,6 +0,0 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -16,9 +16,8 @@
#include "ui/pages_logic/AppSettingsLogic.h"
#include "ui/pages_logic/GeneralSettingsLogic.h"
#include "ui/pages_logic/NetworkSettingsLogic.h"
#include "ui/pages_logic/NewServerProtocolsLogic.h"
#include "ui/pages_logic/QrDecoderLogic.h"
#include "ui/pages_logic/ServerConfiguringProgressLogic.h"
#include "ui/pages_logic/NewServerProtocolsLogic.h"
#include "ui/pages_logic/ServerContainersLogic.h"
#include "ui/pages_logic/ServerListLogic.h"
#include "ui/pages_logic/ServerSettingsLogic.h"
@@ -35,8 +34,6 @@
#include "ui/uilogic.h"
#include "QZXing.h"
#include "debug.h"
#include "defines.h"
@@ -51,10 +48,6 @@
#include "Windows.h"
#endif
#if defined(Q_OS_ANDROID)
#include "native.h"
#endif
static void loadTranslator()
{
QTranslator* translator = new QTranslator;
@@ -86,15 +79,10 @@ int main(int argc, char *argv[])
QApplication app(argc, argv);
#endif
#ifdef Q_OS_WIN
AllowSetForegroundWindow(0);
#endif
#if defined(Q_OS_ANDROID)
NativeHelpers::registerApplicationInstance(&app);
#endif
loadTranslator();
QFontDatabase::addApplicationFont(":/fonts/Lato-Black.ttf");
@@ -120,32 +108,14 @@ int main(int argc, char *argv[])
QCommandLineOption c_autostart {{"a", "autostart"}, "System autostart"};
parser.addOption(c_autostart);
QCommandLineOption c_cleanup {{"c", "cleanup"}, "Cleanup logs"};
parser.addOption(c_cleanup);
parser.process(app);
if (parser.isSet(c_cleanup)) {
Debug::cleanUp();
QTimer::singleShot(100,[&app]{
app.quit();
});
app.exec();
return 0;
}
Settings settings;
if (settings.isSaveLogs()) {
if (!Debug::init()) {
qWarning() << "Initialization of debug subsystem failed";
}
if (!Debug::init()) {
qWarning() << "Initialization of debug subsystem failed";
}
app.setQuitOnLastWindowClosed(false);
QZXing::registerQMLTypes();
qRegisterMetaType<VpnProtocol::VpnConnectionState>("VpnProtocol::VpnConnectionState");
qRegisterMetaType<ServerCredentials>("ServerCredentials");
@@ -181,16 +151,13 @@ int main(int argc, char *argv[])
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine->rootContext()->setContextProperty("Debug", &Debug::Instance());
engine->rootContext()->setContextProperty("UiLogic", uiLogic);
engine->rootContext()->setContextProperty("AppSettingsLogic", uiLogic->appSettingsLogic());
engine->rootContext()->setContextProperty("GeneralSettingsLogic", uiLogic->generalSettingsLogic());
engine->rootContext()->setContextProperty("NetworkSettingsLogic", uiLogic->networkSettingsLogic());
engine->rootContext()->setContextProperty("NewServerProtocolsLogic", uiLogic->newServerProtocolsLogic());
engine->rootContext()->setContextProperty("QrDecoderLogic", uiLogic->qrDecoderLogic());
engine->rootContext()->setContextProperty("ServerConfiguringProgressLogic", uiLogic->serverConfiguringProgressLogic());
engine->rootContext()->setContextProperty("NewServerProtocolsLogic", uiLogic->newServerProtocolsLogic());
engine->rootContext()->setContextProperty("ServerListLogic", uiLogic->serverListLogic());
engine->rootContext()->setContextProperty("ServerSettingsLogic", uiLogic->serverSettingsLogic());
engine->rootContext()->setContextProperty("ServerContainersLogic", uiLogic->serverprotocolsLogic());
@@ -214,23 +181,26 @@ int main(int argc, char *argv[])
uiLogic->setQmlRoot(engine->rootObjects().at(0));
}
#ifdef Q_OS_WIN
if (parser.isSet("a")) uiLogic->showOnStartup();
else emit uiLogic->show();
#else
uiLogic->showOnStartup();
#endif
// TODO - fix
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
if (app.isPrimary()) {
QObject::connect(&app, &SingleApplication::instanceStarted, uiLogic, [&](){
qDebug() << "Secondary instance started, showing this window instead";
emit uiLogic->show();
emit uiLogic->raise();
});
}
#endif
//#ifdef Q_OS_WIN
// if (parser.isSet("a")) mainWindow.showOnStartup();
// else mainWindow.show();
//#else
// mainWindow.showOnStartup();
//#endif
// TODO - fix
//#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
// if (app.isPrimary()) {
// QObject::connect(&app, &SingleApplication::instanceStarted, &mainWindow, [&](){
// qDebug() << "Secondary instance started, showing this window instead";
// mainWindow.show();
// mainWindow.showNormal();
// mainWindow.raise();
// });
// }
//#endif
return app.exec();
}

View File

@@ -1,54 +0,0 @@
#include "native.h"
#include <QMetaObject>
#if defined(Q_OS_ANDROID)
#include <jni.h>
#endif // Q_OS_ANDROID
QObject *NativeHelpers::application_p_ = 0;
#if defined(Q_OS_ANDROID)
// define our native static functions
// these are the functions that Java part will call directly from Android UI thread
static void onPermissionsGranted(JNIEnv * /*env*/, jobject /*obj*/)
{
QMetaObject::invokeMethod(NativeHelpers::getApplicationInstance(), "onPermissionsGranted"
, Qt::QueuedConnection);
}
static void onPermissionsDenied(JNIEnv * /*env*/, jobject /*obj*/)
{
QMetaObject::invokeMethod(NativeHelpers::getApplicationInstance(), "onPermissionsDenied"
, Qt::QueuedConnection);
}
//create a vector with all our JNINativeMethod(s)
static JNINativeMethod methods[] = {
{"onPermissionsGranted", "()V", (void *)onPermissionsGranted},
{"onPermissionsDenied", "()V", (void *)onPermissionsDenied},
};
// this method is called automatically by Java after the .so file is loaded
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/)
{
JNIEnv* env;
// get the JNIEnv pointer.
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK)
return JNI_ERR;
// search for Java class which declares the native methods
jclass javaClass = env->FindClass("org/ftylitak/qzxing/NativeFunctions");
if (!javaClass)
return JNI_ERR;
// register our native methods
if (env->RegisterNatives(javaClass, methods,
sizeof(methods) / sizeof(methods[0])) < 0) {
return JNI_ERR;
}
return JNI_VERSION_1_6;
}
#endif // Q_OS_ANDROID

View File

@@ -1,20 +0,0 @@
#ifndef NATIVE_H
#define NATIVE_H
#include <QObject>
class NativeHelpers {
public:
static void registerApplicationInstance(QObject *app_p) {
application_p_ = app_p;
}
static QObject* getApplicationInstance() {
return application_p_;
}
private:
static QObject *application_p_;
};
#endif // NATIVE_H

View File

@@ -4,72 +4,94 @@
import Foundation
import NetworkExtension
import os
import OpenVPNAdapter
enum TunnelProtoType: String {
case wireguard, openvpn, none
}
class PacketTunnelProvider: NEPacketTunnelProvider {
private lazy var wgAdapter: WireGuardAdapter = {
return WireGuardAdapter(with: self) { logLevel, message in
wg_log(logLevel.osLogLevel, message: message)
}
}()
private lazy var ovpnAdapter: OpenVPNAdapter = {
let adapter = OpenVPNAdapter()
adapter.delegate = self
return adapter
}()
let vpnReachability = OpenVPNReachability()
var startHandler: ((Error?) -> Void)?
var stopHandler: (() -> Void)?
var protoType: TunnelProtoType = .wireguard
override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) {
let activationAttemptId = options?["activationAttemptId"] as? String
let errorNotifier = ErrorNotifier(activationAttemptId: activationAttemptId)
Logger.configureGlobal(tagged: "NET", withFilePath: FileManager.logFileURL?.path)
if let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
let providerConfiguration = protocolConfiguration.providerConfiguration,
let _: Data = providerConfiguration["ovpn"] as? Data {
protoType = .openvpn
} else {
protoType = .wireguard
wg_log(.info, message: "Starting tunnel from the " + (activationAttemptId == nil ? "OS directly, rather than the app" : "app"))
guard let tunnelProviderProtocol = self.protocolConfiguration as? NETunnelProviderProtocol,
let tunnelConfiguration = tunnelProviderProtocol.asTunnelConfiguration() else {
errorNotifier.notify(PacketTunnelProviderError.savedProtocolConfigurationIsInvalid)
completionHandler(PacketTunnelProviderError.savedProtocolConfigurationIsInvalid)
return
}
switch protoType {
case .wireguard:
startWireguard(activationAttemptId: activationAttemptId,
errorNotifier: errorNotifier,
completionHandler: completionHandler)
case .openvpn:
startOpenVPN(completionHandler: completionHandler)
case .none:
break
// Start the tunnel
wgAdapter.start(tunnelConfiguration: tunnelConfiguration) { adapterError in
guard let adapterError = adapterError else {
let interfaceName = self.wgAdapter.interfaceName ?? "unknown"
wg_log(.info, message: "Tunnel interface is \(interfaceName)")
completionHandler(nil)
return
}
switch adapterError {
case .cannotLocateTunnelFileDescriptor:
wg_log(.error, staticMessage: "Starting tunnel failed: could not determine file descriptor")
errorNotifier.notify(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
completionHandler(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
case .dnsResolution(let dnsErrors):
let hostnamesWithDnsResolutionFailure = dnsErrors.map { $0.address }
.joined(separator: ", ")
wg_log(.error, message: "DNS resolution failed for the following hostnames: \(hostnamesWithDnsResolutionFailure)")
errorNotifier.notify(PacketTunnelProviderError.dnsResolutionFailure)
completionHandler(PacketTunnelProviderError.dnsResolutionFailure)
case .setNetworkSettings(let error):
wg_log(.error, message: "Starting tunnel failed with setTunnelNetworkSettings returning \(error.localizedDescription)")
errorNotifier.notify(PacketTunnelProviderError.couldNotSetNetworkSettings)
completionHandler(PacketTunnelProviderError.couldNotSetNetworkSettings)
case .startWireGuardBackend(let errorCode):
wg_log(.error, message: "Starting tunnel failed with wgTurnOn returning \(errorCode)")
errorNotifier.notify(PacketTunnelProviderError.couldNotStartBackend)
completionHandler(PacketTunnelProviderError.couldNotStartBackend)
case .invalidState:
// Must never happen
fatalError()
}
}
}
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
switch protoType {
case .wireguard:
stopWireguard(with: reason, completionHandler: completionHandler)
case .openvpn:
stopOpenVPN(with: reason, completionHandler: completionHandler)
case .none:
break
wg_log(.info, staticMessage: "Stopping tunnel")
wgAdapter.stop { error in
ErrorNotifier.removeLastErrorFile()
if let error = error {
wg_log(.error, message: "Failed to stop WireGuard adapter: \(error.localizedDescription)")
}
completionHandler()
#if os(macOS)
// HACK: This is a filthy hack to work around Apple bug 32073323 (dup'd by us as 47526107).
// Remove it when they finally fix this upstream and the fix has been rolled out to
// sufficient quantities of users.
exit(0)
#endif
}
}
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
guard let completionHandler = completionHandler else { return }
if messageData.count == 1 && messageData[0] == 0 {
wgAdapter.getRuntimeConfiguration { settings in
var data: Data?
@@ -86,144 +108,31 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
completionHandler(nil)
return
}
do {
let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: configString)
wgAdapter.update(tunnelConfiguration: tunnelConfiguration) { error in
if let error = error {
wg_log(.error, message: "Failed to switch tunnel configuration: \(error.localizedDescription)")
completionHandler(nil)
return
}
self.wgAdapter.getRuntimeConfiguration { settings in
var data: Data?
if let settings = settings {
data = settings.data(using: .utf8)!
}
completionHandler(data)
}
let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: configString)
wgAdapter.update(tunnelConfiguration: tunnelConfiguration) { error in
if let error = error {
wg_log(.error, message: "Failed to switch tunnel configuration: \(error.localizedDescription)")
completionHandler(nil)
return
}
self.wgAdapter.getRuntimeConfiguration { settings in
var data: Data?
if let settings = settings {
data = settings.data(using: .utf8)!
}
completionHandler(data)
}
}
} catch {
completionHandler(nil)
completionHandler(nil)
}
} else {
completionHandler(nil)
}
}
// MARK: Private methods
private func startWireguard(activationAttemptId: String?,
errorNotifier: ErrorNotifier,
completionHandler: @escaping (Error?) -> Void) {
guard let tunnelProviderProtocol = self.protocolConfiguration as? NETunnelProviderProtocol,
let tunnelConfiguration = tunnelProviderProtocol.asTunnelConfiguration() else {
errorNotifier.notify(PacketTunnelProviderError.savedProtocolConfigurationIsInvalid)
completionHandler(PacketTunnelProviderError.savedProtocolConfigurationIsInvalid)
return
}
wg_log(.info, message: "Starting wireguard tunnel from the " + (activationAttemptId == nil ? "OS directly, rather than the app" : "app"))
// Start the tunnel
wgAdapter.start(tunnelConfiguration: tunnelConfiguration) { adapterError in
guard let adapterError = adapterError else {
let interfaceName = self.wgAdapter.interfaceName ?? "unknown"
wg_log(.info, message: "Tunnel interface is \(interfaceName)")
completionHandler(nil)
return
}
switch adapterError {
case .cannotLocateTunnelFileDescriptor:
wg_log(.error, staticMessage: "Starting tunnel failed: could not determine file descriptor")
errorNotifier.notify(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
completionHandler(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
case .dnsResolution(let dnsErrors):
let hostnamesWithDnsResolutionFailure = dnsErrors.map { $0.address }
.joined(separator: ", ")
wg_log(.error, message: "DNS resolution failed for the following hostnames: \(hostnamesWithDnsResolutionFailure)")
errorNotifier.notify(PacketTunnelProviderError.dnsResolutionFailure)
completionHandler(PacketTunnelProviderError.dnsResolutionFailure)
case .setNetworkSettings(let error):
wg_log(.error, message: "Starting tunnel failed with setTunnelNetworkSettings returning \(error.localizedDescription)")
errorNotifier.notify(PacketTunnelProviderError.couldNotSetNetworkSettings)
completionHandler(PacketTunnelProviderError.couldNotSetNetworkSettings)
case .startWireGuardBackend(let errorCode):
wg_log(.error, message: "Starting tunnel failed with wgTurnOn returning \(errorCode)")
errorNotifier.notify(PacketTunnelProviderError.couldNotStartBackend)
completionHandler(PacketTunnelProviderError.couldNotStartBackend)
case .invalidState:
// Must never happen
fatalError()
}
}
}
private func startOpenVPN(completionHandler: @escaping (Error?) -> Void) {
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
let providerConfiguration = protocolConfiguration.providerConfiguration,
let ovpnConfiguration: Data = providerConfiguration["ovpn"] as? Data else {
return
}
let configuration = OpenVPNConfiguration()
configuration.fileContent = ovpnConfiguration
// configuration.settings = [] // Additional setting if needed any
// configuration.tunPersist = true // keep tun active during pauses/reconections
let evaluation: OpenVPNConfigurationEvaluation
do {
evaluation = try ovpnAdapter.apply(configuration: configuration)
} catch {
completionHandler(error)
return
}
if !evaluation.autologin {
print("Implement login with user credentials")
}
vpnReachability.startTracking { [weak self] status in
guard status == .reachableViaWiFi else { return }
self?.ovpnAdapter.reconnect(afterTimeInterval: 5)
}
startHandler = completionHandler
ovpnAdapter.connect(using: packetFlow)
}
private func stopWireguard(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
wg_log(.info, staticMessage: "Stopping tunnel")
wgAdapter.stop { error in
ErrorNotifier.removeLastErrorFile()
if let error = error {
wg_log(.error, message: "Failed to stop WireGuard adapter: \(error.localizedDescription)")
}
completionHandler()
#if os(macOS)
// HACK: This is a filthy hack to work around Apple bug 32073323 (dup'd by us as 47526107).
// Remove it when they finally fix this upstream and the fix has been rolled out to
// sufficient quantities of users.
exit(0)
#endif
}
}
private func stopOpenVPN(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
stopHandler = completionHandler
if vpnReachability.isTracking {
vpnReachability.stopTracking()
}
ovpnAdapter.disconnect()
}
}
extension WireGuardLogLevel {
@@ -236,87 +145,3 @@ extension WireGuardLogLevel {
}
}
}
extension NEPacketTunnelFlow: OpenVPNAdapterPacketFlow {}
extension PacketTunnelProvider: OpenVPNAdapterDelegate {
// OpenVPNAdapter calls this delegate method to configure a VPN tunnel.
// `completionHandler` callback requires an object conforming to `OpenVPNAdapterPacketFlow`
// protocol if the tunnel is configured without errors. Otherwise send nil.
// `OpenVPNAdapterPacketFlow` method signatures are similar to `NEPacketTunnelFlow` so
// you can just extend that class to adopt `OpenVPNAdapterPacketFlow` protocol and
// send `self.packetFlow` to `completionHandler` callback.
func openVPNAdapter(
_ openVPNAdapter: OpenVPNAdapter,
configureTunnelWithNetworkSettings networkSettings: NEPacketTunnelNetworkSettings?,
completionHandler: @escaping (Error?) -> Void
) {
// In order to direct all DNS queries first to the VPN DNS servers before the primary DNS servers
// send empty string to NEDNSSettings.matchDomains
networkSettings?.dnsSettings?.matchDomains = [""]
// Set the network settings for the current tunneling session.
setTunnelNetworkSettings(networkSettings, completionHandler: completionHandler)
}
// Process events returned by the OpenVPN library
func openVPNAdapter(
_ openVPNAdapter: OpenVPNAdapter,
handleEvent event:
OpenVPNAdapterEvent, message: String?
) {
switch event {
case .connected:
if reasserting {
reasserting = false
}
guard let startHandler = startHandler else { return }
startHandler(nil)
self.startHandler = nil
case .disconnected:
guard let stopHandler = stopHandler else { return }
if vpnReachability.isTracking {
vpnReachability.stopTracking()
}
stopHandler()
self.stopHandler = nil
case .reconnecting:
reasserting = true
default:
break
}
}
// Handle errors thrown by the OpenVPN library
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleError error: Error) {
// Handle only fatal errors
guard let fatal = (error as NSError).userInfo[OpenVPNAdapterErrorFatalKey] as? Bool,
fatal == true else { return }
if vpnReachability.isTracking {
vpnReachability.stopTracking()
}
if let startHandler = startHandler {
startHandler(error)
self.startHandler = nil
} else {
cancelTunnelWithError(error)
}
}
// Use this method to process any log message returned by OpenVPN library.
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleLogMessage logMessage: String) {
// Handle log messages
}
}

View File

@@ -5,7 +5,7 @@
import Foundation
import NetworkExtension
let vpnName = "Amnezia WireguardVPN"
let vpnName = "AmneziaVPN"
var vpnBundleID = "";
@objc class VPNIPAddressRange : NSObject {
@@ -30,102 +30,27 @@ public class IOSVpnProtocolImpl : NSObject {
private var deviceIpv4Address: String? = nil
private var deviceIpv6Address: String? = nil
@objc enum ConnectionState: Int { case Error, Connected, Disconnected }
@objc init(bundleID: String,
config: String,
closure: @escaping (ConnectionState, Date?) -> Void,
callback: @escaping (Bool) -> Void) {
super.init()
Logger.configureGlobal(tagged: "APP", withFilePath: "")
print("Config from caller: \(config)")
@objc enum IOSConnectionState: Int { case Error, Connected, Disconnected }
vpnBundleID = bundleID;
precondition(!vpnBundleID.isEmpty)
stateChangeCallback = callback
NotificationCenter.default.removeObserver(self)
NotificationCenter.default.addObserver(self,
selector: #selector(self.vpnStatusDidChange(notification:)),
name: Notification.Name.NEVPNStatusDidChange,
object: nil)
NETunnelProviderManager.loadAllFromPreferences { [weak self] managers, error in
if let error = error {
Logger.global?.log(message: "Loading from preference failed: \(error)")
closure(ConnectionState.Error, nil)
return
}
if self == nil {
Logger.global?.log(message: "We are shutting down.")
return
}
let nsManagers = managers ?? []
Logger.global?.log(message: "We have received \(nsManagers.count) managers.")
print("We have received \(nsManagers.count) managers.")
let tunnel = nsManagers.first(where: IOSVpnProtocolImpl.isOurManager(_:))
// if let name = tunnel?.localizedDescription, name == vpnName {
// tunnel?.removeFromPreferences(completionHandler: { removeError in
// if let error = removeError {
// Logger.global?.log(message: "WireguardVPN Tunnel Remove from Prefs Error: \(error)")
// closure(ConnectionState.Error, nil)
// return
// }
// })
// }
if tunnel == nil {
Logger.global?.log(message: "Creating the tunnel")
print("Creating the tunnel")
self!.tunnel = NETunnelProviderManager()
closure(ConnectionState.Disconnected, nil)
return
}
Logger.global?.log(message: "Tunnel already exists")
print("Tunnel already exists")
self!.tunnel = tunnel
if tunnel?.connection.status == .connected {
closure(ConnectionState.Connected, tunnel?.connection.connectedDate)
} else {
closure(ConnectionState.Disconnected, nil)
}
}
}
@objc init(bundleID: String,
privateKey: Data,
deviceIpv4Address: String,
deviceIpv6Address: String,
closure: @escaping (ConnectionState, Date?) -> Void,
callback: @escaping (Bool) -> Void) {
@objc init(bundleID: String, privateKey: Data, deviceIpv4Address: String, deviceIpv6Address: String, closure: @escaping (IOSConnectionState, Date?) -> Void, callback: @escaping (Bool) -> Void) {
super.init()
Logger.configureGlobal(tagged: "APP", withFilePath: "")
vpnBundleID = bundleID;
precondition(!vpnBundleID.isEmpty)
stateChangeCallback = callback
self.privateKey = PrivateKey(rawValue: privateKey)
self.deviceIpv4Address = deviceIpv4Address
self.deviceIpv6Address = deviceIpv6Address
NotificationCenter.default.removeObserver(self)
NotificationCenter.default.addObserver(self, selector: #selector(self.vpnStatusDidChange(notification:)), name: Notification.Name.NEVPNStatusDidChange, object: nil)
NETunnelProviderManager.loadAllFromPreferences { [weak self] managers, error in
if let error = error {
Logger.global?.log(message: "Loading from preference failed: \(error)")
closure(ConnectionState.Error, nil)
closure(IOSConnectionState.Error, nil)
return
}
@@ -139,34 +64,22 @@ public class IOSVpnProtocolImpl : NSObject {
print("We have received \(nsManagers.count) managers.")
let tunnel = nsManagers.first(where: IOSVpnProtocolImpl.isOurManager(_:))
// if let name = tunnel?.localizedDescription, name != vpnName {
// tunnel?.removeFromPreferences(completionHandler: { removeError in
// if let error = removeError {
// Logger.global?.log(message: "OpenVpn Tunnel Remove from Prefs Error: \(error)")
// closure(ConnectionState.Error, nil)
// return
// }
// })
// }
if tunnel == nil {
Logger.global?.log(message: "Creating the tunnel")
print("Creating the tunnel")
self!.tunnel = NETunnelProviderManager()
closure(ConnectionState.Disconnected, nil)
closure(IOSConnectionState.Disconnected, nil)
return
}
Logger.global?.log(message: "Tunnel already exists")
print("Tunnel already exists")
self!.tunnel = tunnel
if tunnel?.connection.status == .connected {
closure(ConnectionState.Connected, tunnel?.connection.connectedDate)
closure(IOSConnectionState.Connected, tunnel?.connection.connectedDate)
} else {
closure(ConnectionState.Disconnected, nil)
closure(IOSConnectionState.Disconnected, nil)
}
}
}
@@ -229,22 +142,6 @@ public class IOSVpnProtocolImpl : NSObject {
print("Found the manager with the correct bundle identifier: \(tunnelProto.providerBundleIdentifier!)")
return true
}
@objc func connect(ovpnConfig: String, failureCallback: @escaping () -> Void) {
Logger.global?.log(message: "Connecting")
assert(tunnel != nil)
let addr: String = ovpnConfig
.splitToArray(separator: "\n", trimmingCharacters: nil)
.first { $0.starts(with: "remote ") }
.splitToArray(separator: " ", trimmingCharacters: nil)[1]
print("server: \(addr)")
// Let's remove the previous config if it exists.
(tunnel?.protocolConfiguration as? NETunnelProviderProtocol)?.destroyConfigurationReference()
self.configureOpenVPNTunnel(serverAddress: addr, config: ovpnConfig, failureCallback: failureCallback)
}
@objc func connect(dnsServer: String, serverIpv6Gateway: String, serverPublicKey: String, presharedKey: String, serverIpv4AddrIn: String, serverPort: Int, allowedIPAddressRanges: Array<VPNIPAddressRange>, ipv6Enabled: Bool, reason: Int, failureCallback: @escaping () -> Void) {
Logger.global?.log(message: "Connecting")
@@ -350,46 +247,6 @@ public class IOSVpnProtocolImpl : NSObject {
}
}
}
func configureOpenVPNTunnel(serverAddress: String, config: String, failureCallback: @escaping () -> Void) {
let tunnelProtocol = NETunnelProviderProtocol()
tunnelProtocol.serverAddress = serverAddress
tunnelProtocol.providerBundleIdentifier = vpnBundleID
tunnelProtocol.providerConfiguration = ["ovpn": Data(config.utf8)]
tunnel?.protocolConfiguration = tunnelProtocol
tunnel?.localizedDescription = "Amnezia OpenVPN"
tunnel?.isEnabled = true
tunnel?.saveToPreferences { [unowned self] saveError in
if let error = saveError {
Logger.global?.log(message: "Connect OpenVPN Tunnel Save Error: \(error)")
failureCallback()
return
}
Logger.global?.log(message: "Saving the OpenVPN tunnel succeeded")
self.tunnel?.loadFromPreferences { error in
if let error = error {
Logger.global?.log(message: "Connect OpenVPN Tunnel Load Error: \(error)")
failureCallback()
return
}
Logger.global?.log(message: "Loading the OpenVPN tunnel succeeded")
print("Loading the openvpn tunnel succeeded")
do {
print("starting openvpn tunnel")
try self.tunnel?.connection.startVPNTunnel()
} catch let error {
Logger.global?.log(message: "Something went wrong: \(error)")
failureCallback()
return
}
}
}
}
@objc func disconnect() {
Logger.global?.log(message: "Disconnecting")
@@ -400,74 +257,7 @@ public class IOSVpnProtocolImpl : NSObject {
@objc func checkStatus(callback: @escaping (String, String, String) -> Void) {
Logger.global?.log(message: "Check status")
assert(tunnel != nil)
let protoType = (tunnel!.localizedDescription ?? "").toTunnelType
switch protoType {
case .wireguard:
checkWireguardStatus(callback: callback)
case .openvpn:
checkOVPNStatus(callback: callback)
case .empty:
break
}
}
private func checkOVPNStatus(callback: @escaping (String, String, String) -> Void) {
Logger.global?.log(message: "Check OpenVPN")
guard let proto = tunnel?.protocolConfiguration as? NETunnelProviderProtocol else {
callback("", "", "")
return
}
guard let configData = proto.providerConfiguration?["ovpn"] as? Data,
let ovpnConfig = String(data: configData, encoding: .utf8) else {
callback("", "", "")
return
}
let serverIpv4Gateway: String = ovpnConfig
.splitToArray(separator: "\n", trimmingCharacters: nil)
.first { $0.starts(with: "remote ") }
.splitToArray(separator: " ", trimmingCharacters: nil)[1]
print("server IP: \(serverIpv4Gateway)")
let deviceIpv4Address = getTunIPAddress()
print("device IP: \(serverIpv4Gateway)")
if deviceIpv4Address == nil {
callback("", "", "")
return
}
guard let session = tunnel?.connection as? NETunnelProviderSession else {
callback("", "", "")
return
}
do {
try session.sendProviderMessage(Data([UInt8(0)])) { [callback] data in
guard let data = data,
let configString = String(data: data, encoding: .utf8)
else {
Logger.global?.log(message: "Failed to convert data to string")
callback("", "", "")
return
}
callback("\(serverIpv4Gateway)", "\(deviceIpv4Address!)", configString)
}
} catch {
Logger.global?.log(message: "Failed to retrieve data from session")
callback("", "", "")
}
}
private func checkWireguardStatus(callback: @escaping (String, String, String) -> Void) {
Logger.global?.log(message: "Check Wireguard")
let proto = tunnel!.protocolConfiguration as? NETunnelProviderProtocol
if proto == nil {
callback("", "", "")
@@ -515,50 +305,4 @@ public class IOSVpnProtocolImpl : NSObject {
callback("", "", "")
}
}
private func getTunIPAddress() -> String? {
var address: String? = nil
var interfaces: UnsafeMutablePointer<ifaddrs>? = nil
var temp_addr: UnsafeMutablePointer<ifaddrs>? = nil
var success: Int = 0
// retrieve the current interfaces - returns 0 on success
success = Int(getifaddrs(&interfaces))
if success == 0 {
// Loop through linked list of interfaces
temp_addr = interfaces
while temp_addr != nil {
if temp_addr?.pointee.ifa_addr == nil {
continue
}
if temp_addr?.pointee.ifa_addr.pointee.sa_family == UInt8(AF_INET) {
// Check if interface is en0 which is the wifi connection on the iPhone
if let name = temp_addr?.pointee.ifa_name, ((String(utf8String: name)?.contains("tun")) != nil) {
// Get NSString from C String
if let value = temp_addr?.pointee.ifa_addr as? sockaddr_in {
address = String(utf8String: inet_ntoa(value.sin_addr))
}
}
}
temp_addr = temp_addr?.pointee.ifa_next
}
}
freeifaddrs(interfaces)
return address
}
}
enum TunnelType: String {
case wireguard, openvpn, empty
}
extension String {
var toTunnelType: TunnelType {
switch self {
case "wireguard": return .wireguard
case "openvpn": return .openvpn
default:
return .empty
}
}
}

View File

@@ -1,36 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "backendlogsobserver.h"
#include "leakdetector.h"
#include "logger.h"
#include <QDBusPendingCallWatcher>
#include <QDBusPendingReply>
namespace {
Logger logger({LOG_LINUX, LOG_CONTROLLER}, "BackendLogsObserver");
}
BackendLogsObserver::BackendLogsObserver(
QObject* parent, std::function<void(const QString&)>&& callback)
: QObject(parent), m_callback(std::move(callback)) {
MVPN_COUNT_CTOR(BackendLogsObserver);
}
BackendLogsObserver::~BackendLogsObserver() {
MVPN_COUNT_DTOR(BackendLogsObserver);
}
void BackendLogsObserver::completed(QDBusPendingCallWatcher* call) {
QDBusPendingReply<QString> reply = *call;
if (reply.isError()) {
logger.error() << "Error received from the DBus service";
m_callback("Failed to retrieve logs from the mozillavpn linuxdaemon.");
return;
}
QString status = reply.argumentAt<0>();
m_callback(status);
}

View File

@@ -1,29 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef BACKENDLOGSOBSERVER_H
#define BACKENDLOGSOBSERVER_H
#include <functional>
#include <QObject>
class QDBusPendingCallWatcher;
class BackendLogsObserver final : public QObject {
Q_OBJECT
Q_DISABLE_COPY_MOVE(BackendLogsObserver)
public:
BackendLogsObserver(QObject* parent,
std::function<void(const QString&)>&& callback);
~BackendLogsObserver();
public slots:
void completed(QDBusPendingCallWatcher* call);
private:
std::function<void(const QString&)> m_callback;
};
#endif // BACKENDLOGSOBSERVER_H

View File

@@ -1,109 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "apptracker.h"
#include "dbustypeslinux.h"
#include "leakdetector.h"
#include "logger.h"
#include <QtDBus/QtDBus>
#include <QDBusConnection>
#include <QDBusInterface>
#include <QScopeGuard>
#include <unistd.h>
constexpr const char* GTK_DESKTOP_APP_SERVICE = "org.gtk.gio.DesktopAppInfo";
constexpr const char* GTK_DESKTOP_APP_PATH = "/org/gtk/gio/DesktopAppInfo";
constexpr const char* DBUS_LOGIN_SERVICE = "org.freedesktop.login1";
constexpr const char* DBUS_LOGIN_PATH = "/org/freedesktop/login1";
constexpr const char* DBUS_LOGIN_MANAGER = "org.freedesktop.login1.Manager";
namespace {
Logger logger(LOG_LINUX, "AppTracker");
}
AppTracker::AppTracker(QObject* parent) : QObject(parent) {
MVPN_COUNT_CTOR(AppTracker);
logger.debug() << "AppTracker created.";
QDBusConnection m_conn = QDBusConnection::systemBus();
m_conn.connect("", DBUS_LOGIN_PATH, DBUS_LOGIN_MANAGER, "UserNew", this,
SLOT(userCreated(uint, const QDBusObjectPath&)));
m_conn.connect("", DBUS_LOGIN_PATH, DBUS_LOGIN_MANAGER, "UserRemoved", this,
SLOT(userRemoved(uint, const QDBusObjectPath&)));
QDBusInterface n(DBUS_LOGIN_SERVICE, DBUS_LOGIN_PATH, DBUS_LOGIN_MANAGER,
m_conn);
QDBusPendingReply<UserDataList> reply = n.asyncCall("ListUsers");
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this);
QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this,
SLOT(userListCompleted(QDBusPendingCallWatcher*)));
}
AppTracker::~AppTracker() {
MVPN_COUNT_DTOR(AppTracker);
logger.debug() << "AppTracker destroyed.";
}
void AppTracker::userListCompleted(QDBusPendingCallWatcher* watcher) {
QDBusPendingReply<UserDataList> reply = *watcher;
if (reply.isValid()) {
UserDataList list = reply.value();
for (auto user : list) {
userCreated(user.userid, user.path);
}
}
delete watcher;
}
void AppTracker::userCreated(uint userid, const QDBusObjectPath& path) {
logger.debug() << "User created uid:" << userid << "at:" << path.path();
/* Acquire the effective UID of the user to connect to their session bus. */
uid_t realuid = getuid();
if (seteuid(userid) < 0) {
logger.warning() << "Failed to set effective UID";
}
auto guard = qScopeGuard([&] {
if (seteuid(realuid) < 0) {
logger.warning() << "Failed to restore effective UID";
}
});
/* For correctness we should ask systemd for the user's runtime directory. */
QString busPath = "unix:path=/run/user/" + QString::number(userid) + "/bus";
logger.debug() << "Connection to" << busPath;
QDBusConnection connection =
QDBusConnection::connectToBus(busPath, "user-" + QString::number(userid));
/* Connect to the user's GTK launch event. */
bool isConnected = connection.connect(
"", GTK_DESKTOP_APP_PATH, GTK_DESKTOP_APP_SERVICE, "Launched", this,
SLOT(gtkLaunchEvent(const QByteArray&, const QString&, qlonglong,
const QStringList&, const QVariantMap&)));
if (!isConnected) {
logger.warning() << "Failed to connect to GTK Launched signal";
}
}
void AppTracker::userRemoved(uint uid, const QDBusObjectPath& path) {
logger.debug() << "User removed uid:" << uid << "at:" << path.path();
QDBusConnection::disconnectFromBus("user-" + QString::number(uid));
}
void AppTracker::gtkLaunchEvent(const QByteArray& appid, const QString& display,
qlonglong pid, const QStringList& uris,
const QVariantMap& extra) {
Q_UNUSED(display);
Q_UNUSED(uris);
Q_UNUSED(extra);
QString appIdName(appid);
if (!appIdName.isEmpty()) {
emit appLaunched(appIdName, pid);
}
}

View File

@@ -1,31 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef APPTRACKER_H
#define APPTRACKER_H
#include <QDBusPendingCallWatcher>
#include <QDBusObjectPath>
class AppTracker final : public QObject {
Q_OBJECT
Q_DISABLE_COPY_MOVE(AppTracker)
public:
explicit AppTracker(QObject* parent);
~AppTracker();
signals:
void appLaunched(const QString& name, int rootpid);
private slots:
void userListCompleted(QDBusPendingCallWatcher* call);
void userCreated(uint uid, const QDBusObjectPath& path);
void userRemoved(uint uid, const QDBusObjectPath& path);
void gtkLaunchEvent(const QByteArray& appid, const QString& display,
qlonglong pid, const QStringList& uris,
const QVariantMap& extra);
};
#endif // APPTRACKER_H

View File

@@ -1,249 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "dbusservice.h"
#include "dbus_adaptor.h"
#include "leakdetector.h"
#include "logger.h"
#include "loghandler.h"
#include "polkithelper.h"
#include <QCoreApplication>
#include <QJsonDocument>
#include <QJsonObject>
namespace {
Logger logger(LOG_LINUX, "DBusService");
}
constexpr const char* APP_STATE_ACTIVE = "active";
constexpr const char* APP_STATE_EXCLUDED = "excluded";
constexpr const char* APP_STATE_BLOCKED = "blocked";
DBusService::DBusService(QObject* parent) : Daemon(parent) {
MVPN_COUNT_CTOR(DBusService);
m_wgutils = new WireguardUtilsLinux(this);
m_apptracker = new AppTracker(this);
m_pidtracker = new PidTracker(this);
connect(m_apptracker, SIGNAL(appLaunched(const QString&, int)), this,
SLOT(appLaunched(const QString&, int)));
connect(m_pidtracker, SIGNAL(terminated(const QString&, int)), this,
SLOT(appTerminated(const QString&, int)));
if (!removeInterfaceIfExists()) {
qFatal("Interface `%s` exists and cannot be removed. Cannot proceed!",
WG_INTERFACE);
}
}
DBusService::~DBusService() { MVPN_COUNT_DTOR(DBusService); }
IPUtils* DBusService::iputils() {
if (!m_iputils) {
m_iputils = new IPUtilsLinux(this);
}
return m_iputils;
}
DnsUtils* DBusService::dnsutils() {
if (!m_dnsutils) {
m_dnsutils = new DnsUtilsLinux(this);
}
return m_dnsutils;
}
void DBusService::setAdaptor(DbusAdaptor* adaptor) {
Q_ASSERT(!m_adaptor);
m_adaptor = adaptor;
}
bool DBusService::removeInterfaceIfExists() {
if (m_wgutils->interfaceExists()) {
logger.warning() << "Device already exists. Let's remove it.";
if (!m_wgutils->deleteInterface()) {
logger.error() << "Failed to remove the device.";
return false;
}
}
return true;
}
QString DBusService::version() {
logger.debug() << "Version request";
return PROTOCOL_VERSION;
}
bool DBusService::activate(const QString& jsonConfig) {
logger.debug() << "Activate";
if (!PolkitHelper::instance()->checkAuthorization(
"org.mozilla.vpn.activate")) {
logger.error() << "Polkit rejected";
return false;
}
QJsonDocument json = QJsonDocument::fromJson(jsonConfig.toLocal8Bit());
if (!json.isObject()) {
logger.error() << "Invalid input";
return false;
}
QJsonObject obj = json.object();
InterfaceConfig config;
if (!parseConfig(obj, config)) {
logger.error() << "Invalid configuration";
return false;
}
if (obj.contains("vpnDisabledApps")) {
QJsonArray disabledApps = obj["vpnDisabledApps"].toArray();
for (const QJsonValue& app : disabledApps) {
firewallApp(app.toString(), APP_STATE_EXCLUDED);
}
}
return Daemon::activate(config);
}
bool DBusService::deactivate(bool emitSignals) {
logger.debug() << "Deactivate";
firewallClear();
return Daemon::deactivate(emitSignals);
}
QString DBusService::status() { return QString(getStatus()); }
QByteArray DBusService::getStatus() {
logger.debug() << "Status request";
QJsonObject json;
if (!m_connections.contains(0)) {
json.insert("status", QJsonValue(false));
return QJsonDocument(json).toJson(QJsonDocument::Compact);
}
const InterfaceConfig& config = m_connections.value(0).m_config;
if (!m_wgutils->interfaceExists()) {
logger.error() << "Unable to get device";
json.insert("status", QJsonValue(false));
return QJsonDocument(json).toJson(QJsonDocument::Compact);
}
json.insert("status", QJsonValue(true));
json.insert("serverIpv4Gateway", QJsonValue(config.m_serverIpv4Gateway));
json.insert("deviceIpv4Address", QJsonValue(config.m_deviceIpv4Address));
WireguardUtilsLinux::peerStatus status =
m_wgutils->getPeerStatus(config.m_serverPublicKey);
json.insert("txBytes", QJsonValue(status.txBytes));
json.insert("rxBytes", QJsonValue(status.rxBytes));
return QJsonDocument(json).toJson(QJsonDocument::Compact);
}
QString DBusService::getLogs() {
logger.debug() << "Log request";
return Daemon::logs();
}
void DBusService::appLaunched(const QString& name, int rootpid) {
logger.debug() << "tracking:" << name << "PID:" << rootpid;
ProcessGroup* group = m_pidtracker->track(name, rootpid);
if (m_firewallApps.contains(name)) {
group->state = m_firewallApps[name];
group->moveToCgroup(getAppStateCgroup(group->state));
}
}
void DBusService::appTerminated(const QString& name, int rootpid) {
logger.debug() << "terminate:" << name << "PID:" << rootpid;
}
/* Get the list of running applications that the firewall knows about. */
QString DBusService::runningApps() {
QJsonArray result;
for (auto i = m_pidtracker->begin(); i != m_pidtracker->end(); i++) {
const ProcessGroup* group = *i;
QJsonObject appObject;
QJsonArray pidList;
appObject.insert("name", QJsonValue(group->name));
appObject.insert("rootpid", QJsonValue(group->rootpid));
appObject.insert("state", QJsonValue(group->state));
for (auto pid : group->kthreads.keys()) {
pidList.append(QJsonValue(pid));
}
appObject.insert("pids", pidList);
result.append(appObject);
}
return QJsonDocument(result).toJson(QJsonDocument::Compact);
}
/* Update the firewall for running applications matching the application ID. */
bool DBusService::firewallApp(const QString& appName, const QString& state) {
logger.debug() << "Setting" << appName << "to firewall state" << state;
m_firewallApps[appName] = state;
QString cgroup = getAppStateCgroup(state);
/* Change matching applications' state to excluded */
for (auto i = m_pidtracker->begin(); i != m_pidtracker->end(); i++) {
ProcessGroup* group = *i;
if (group->name != appName) {
continue;
}
group->state = state;
group->moveToCgroup(cgroup);
}
return true;
}
/* Update the firewall for the application matching the desired PID. */
bool DBusService::firewallPid(int rootpid, const QString& state) {
ProcessGroup* group = m_pidtracker->group(rootpid);
if (!group) {
return false;
}
group->state = state;
group->moveToCgroup(getAppStateCgroup(group->state));
logger.debug() << "Setting" << group->name << "PID:" << rootpid
<< "to firewall state" << state;
return true;
}
/* Clear the firewall and return all applications to the active state */
bool DBusService::firewallClear() {
const QString cgroup = getAppStateCgroup(APP_STATE_ACTIVE);
m_firewallApps.clear();
for (auto i = m_pidtracker->begin(); i != m_pidtracker->end(); i++) {
ProcessGroup* group = *i;
if (group->state == APP_STATE_ACTIVE) {
continue;
}
group->state = APP_STATE_ACTIVE;
group->moveToCgroup(cgroup);
logger.debug() << "Setting" << group->name << "PID:" << group->rootpid
<< "to firewall state" << group->state;
}
return true;
}
QString DBusService::getAppStateCgroup(const QString& state) {
if (state == APP_STATE_EXCLUDED) {
return m_wgutils->getExcludeCgroup();
}
if (state == APP_STATE_BLOCKED) {
return m_wgutils->getBlockCgroup();
}
return m_wgutils->getDefaultCgroup();
}

View File

@@ -1,72 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef DBUSSERVICE_H
#define DBUSSERVICE_H
#include "daemon/daemon.h"
#include "apptracker.h"
#include "iputilslinux.h"
#include "dnsutilslinux.h"
#include "pidtracker.h"
#include "wireguardutilslinux.h"
class DbusAdaptor;
class DBusService final : public Daemon {
Q_OBJECT
Q_DISABLE_COPY_MOVE(DBusService)
Q_CLASSINFO("D-Bus Interface", "org.mozilla.vpn.dbus")
public:
DBusService(QObject* parent);
~DBusService();
void setAdaptor(DbusAdaptor* adaptor);
using Daemon::activate;
public slots:
bool activate(const QString& jsonConfig);
bool deactivate(bool emitSignals = true) override;
QString status();
QString version();
QString getLogs();
QString runningApps();
bool firewallApp(const QString& appName, const QString& state);
bool firewallPid(int rootpid, const QString& state);
bool firewallClear();
protected:
WireguardUtils* wgutils() const override { return m_wgutils; }
bool supportIPUtils() const override { return true; }
IPUtils* iputils() override;
bool supportDnsUtils() const override { return true; }
DnsUtils* dnsutils() override;
QByteArray getStatus() override;
private:
bool removeInterfaceIfExists();
QString getAppStateCgroup(const QString& state);
private slots:
void appLaunched(const QString& name, int rootpid);
void appTerminated(const QString& name, int rootpid);
private:
DbusAdaptor* m_adaptor = nullptr;
WireguardUtilsLinux* m_wgutils = nullptr;
IPUtilsLinux* m_iputils = nullptr;
DnsUtilsLinux* m_dnsutils = nullptr;
AppTracker* m_apptracker = nullptr;
PidTracker* m_pidtracker = nullptr;
QMap<QString, QString> m_firewallApps;
};
#endif // DBUSSERVICE_H

View File

@@ -1,170 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef DBUSTYPESLINUX_H
#define DBUSTYPESLINUX_H
#include <QtDBus/QtDBus>
#include <QDBusArgument>
#include <QByteArray>
#include <QHostAddress>
#include <sys/socket.h>
/* D-Bus metatype for marshalling arguments to the SetLinkDNS method */
class DnsResolver : public QHostAddress {
public:
DnsResolver(const QHostAddress& address = QHostAddress())
: QHostAddress(address) {}
friend QDBusArgument& operator<<(QDBusArgument& args, const DnsResolver& ip) {
args.beginStructure();
if (ip.protocol() == QAbstractSocket::IPv6Protocol) {
Q_IPV6ADDR addrv6 = ip.toIPv6Address();
args << AF_INET6;
args << QByteArray::fromRawData((const char*)&addrv6, sizeof(addrv6));
} else {
quint32 addrv4 = ip.toIPv4Address();
QByteArray data(4, 0);
data[0] = (addrv4 >> 24) & 0xff;
data[1] = (addrv4 >> 16) & 0xff;
data[2] = (addrv4 >> 8) & 0xff;
data[3] = (addrv4 >> 0) & 0xff;
args << AF_INET;
args << data;
}
args.endStructure();
return args;
}
friend const QDBusArgument& operator>>(const QDBusArgument& args,
DnsResolver& ip) {
int family;
QByteArray data;
args.beginStructure();
args >> family >> data;
args.endStructure();
if (family == AF_INET6) {
ip.setAddress(data.constData());
} else if (data.count() >= 4) {
quint32 addrv4 = 0;
addrv4 |= (data[0] << 24);
addrv4 |= (data[1] << 16);
addrv4 |= (data[2] << 8);
addrv4 |= (data[3] << 0);
ip.setAddress(addrv4);
}
return args;
}
};
typedef QList<DnsResolver> DnsResolverList;
Q_DECLARE_METATYPE(DnsResolver);
Q_DECLARE_METATYPE(DnsResolverList);
/* D-Bus metatype for marshalling arguments to the SetLinkDomains method */
class DnsLinkDomain {
public:
DnsLinkDomain(const QString d = "", bool s = false) {
domain = d;
search = s;
};
QString domain;
bool search;
friend QDBusArgument& operator<<(QDBusArgument& args,
const DnsLinkDomain& data) {
args.beginStructure();
args << data.domain << data.search;
args.endStructure();
return args;
}
friend const QDBusArgument& operator>>(const QDBusArgument& args,
DnsLinkDomain& data) {
args.beginStructure();
args >> data.domain >> data.search;
args.endStructure();
return args;
}
bool operator==(const DnsLinkDomain& other) const {
return (domain == other.domain) && (search == other.search);
}
bool operator==(const QString& other) const { return (domain == other); }
};
typedef QList<DnsLinkDomain> DnsLinkDomainList;
Q_DECLARE_METATYPE(DnsLinkDomain);
Q_DECLARE_METATYPE(DnsLinkDomainList);
/* D-Bus metatype for marshalling the Domains property */
class DnsDomain {
public:
DnsDomain() {}
int ifindex = 0;
QString domain = "";
bool search = false;
friend QDBusArgument& operator<<(QDBusArgument& args, const DnsDomain& data) {
args.beginStructure();
args << data.ifindex << data.domain << data.search;
args.endStructure();
return args;
}
friend const QDBusArgument& operator>>(const QDBusArgument& args,
DnsDomain& data) {
args.beginStructure();
args >> data.ifindex >> data.domain >> data.search;
args.endStructure();
return args;
}
};
typedef QList<DnsDomain> DnsDomainList;
Q_DECLARE_METATYPE(DnsDomain);
Q_DECLARE_METATYPE(DnsDomainList);
/* D-Bus metatype for marshalling the freedesktop login manager data. */
class UserData {
public:
QString name;
uint userid;
QDBusObjectPath path;
friend QDBusArgument& operator<<(QDBusArgument& args, const UserData& data) {
args.beginStructure();
args << data.userid << data.name << data.path;
args.endStructure();
return args;
}
friend const QDBusArgument& operator>>(const QDBusArgument& args,
UserData& data) {
args.beginStructure();
args >> data.userid >> data.name >> data.path;
args.endStructure();
return args;
}
};
typedef QList<UserData> UserDataList;
Q_DECLARE_METATYPE(UserData);
Q_DECLARE_METATYPE(UserDataList);
class DnsMetatypeRegistrationProxy {
public:
DnsMetatypeRegistrationProxy() {
qRegisterMetaType<DnsResolver>();
qDBusRegisterMetaType<DnsResolver>();
qRegisterMetaType<DnsResolverList>();
qDBusRegisterMetaType<DnsResolverList>();
qRegisterMetaType<DnsLinkDomain>();
qDBusRegisterMetaType<DnsLinkDomain>();
qRegisterMetaType<DnsLinkDomainList>();
qDBusRegisterMetaType<DnsLinkDomainList>();
qRegisterMetaType<DnsDomain>();
qDBusRegisterMetaType<DnsDomain>();
qRegisterMetaType<DnsDomainList>();
qDBusRegisterMetaType<DnsDomainList>();
qRegisterMetaType<UserData>();
qDBusRegisterMetaType<UserData>();
qRegisterMetaType<UserDataList>();
qDBusRegisterMetaType<UserDataList>();
}
};
#endif // DBUSTYPESLINUX_H

View File

@@ -1,206 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "dnsutilslinux.h"
#include "leakdetector.h"
#include "logger.h"
#include <QtDBus/QtDBus>
#include <QDBusVariant>
#include <net/if.h>
constexpr const char* DBUS_RESOLVE_SERVICE = "org.freedesktop.resolve1";
constexpr const char* DBUS_RESOLVE_PATH = "/org/freedesktop/resolve1";
constexpr const char* DBUS_RESOLVE_MANAGER = "org.freedesktop.resolve1.Manager";
constexpr const char* DBUS_PROPERTY_INTERFACE =
"org.freedesktop.DBus.Properties";
namespace {
Logger logger(LOG_LINUX, "DnsUtilsLinux");
}
DnsUtilsLinux::DnsUtilsLinux(QObject* parent) : DnsUtils(parent) {
MVPN_COUNT_CTOR(DnsUtilsLinux);
logger.debug() << "DnsUtilsLinux created.";
QDBusConnection conn = QDBusConnection::systemBus();
m_resolver = new QDBusInterface(DBUS_RESOLVE_SERVICE, DBUS_RESOLVE_PATH,
DBUS_RESOLVE_MANAGER, conn, this);
}
DnsUtilsLinux::~DnsUtilsLinux() {
MVPN_COUNT_DTOR(DnsUtilsLinux);
for (int ifindex : m_linkDomains.keys()) {
QList<QVariant> argumentList;
argumentList << QVariant::fromValue(ifindex);
argumentList << QVariant::fromValue(m_linkDomains[ifindex]);
m_resolver->asyncCallWithArgumentList(QStringLiteral("SetLinkDomains"),
argumentList);
}
if (m_ifindex > 0) {
m_resolver->asyncCall(QStringLiteral("RevertLink"), m_ifindex);
}
logger.debug() << "DnsUtilsLinux destroyed.";
}
bool DnsUtilsLinux::updateResolvers(const QString& ifname,
const QList<QHostAddress>& resolvers) {
m_ifindex = if_nametoindex(qPrintable(ifname));
if (m_ifindex <= 0) {
logger.error() << "Unable to resolve ifindex for" << ifname;
return false;
}
setLinkDNS(m_ifindex, resolvers);
setLinkDefaultRoute(m_ifindex, true);
updateLinkDomains();
return true;
}
bool DnsUtilsLinux::restoreResolvers() {
for (auto ifindex : m_linkDomains.keys()) {
setLinkDomains(ifindex, m_linkDomains[ifindex]);
}
m_linkDomains.clear();
/* Revert the VPN interface's DNS configuration */
if (m_ifindex > 0) {
QList<QVariant> argumentList = {QVariant::fromValue(m_ifindex)};
QDBusPendingReply<> reply = m_resolver->asyncCallWithArgumentList(
QStringLiteral("RevertLink"), argumentList);
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this);
QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this,
SLOT(dnsCallCompleted(QDBusPendingCallWatcher*)));
m_ifindex = 0;
}
return true;
}
void DnsUtilsLinux::dnsCallCompleted(QDBusPendingCallWatcher* call) {
QDBusPendingReply<> reply = *call;
if (reply.isError()) {
logger.error() << "Error received from the DBus service";
}
delete call;
}
void DnsUtilsLinux::setLinkDNS(int ifindex,
const QList<QHostAddress>& resolvers) {
QList<DnsResolver> resolverList;
char ifnamebuf[IF_NAMESIZE];
const char* ifname = if_indextoname(ifindex, ifnamebuf);
for (auto ip : resolvers) {
resolverList.append(ip);
if (ifname) {
logger.debug() << "Adding DNS resolver" << ip.toString() << "via"
<< ifname;
}
}
QList<QVariant> argumentList;
argumentList << QVariant::fromValue(ifindex);
argumentList << QVariant::fromValue(resolverList);
QDBusPendingReply<> reply = m_resolver->asyncCallWithArgumentList(
QStringLiteral("SetLinkDNS"), argumentList);
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this);
QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this,
SLOT(dnsCallCompleted(QDBusPendingCallWatcher*)));
}
void DnsUtilsLinux::setLinkDomains(int ifindex,
const QList<DnsLinkDomain>& domains) {
char ifnamebuf[IF_NAMESIZE];
const char* ifname = if_indextoname(ifindex, ifnamebuf);
if (ifname) {
for (auto d : domains) {
logger.debug() << "Setting DNS domain:" << d.domain << "via" << ifname
<< (d.search ? "search" : "");
}
}
QList<QVariant> argumentList;
argumentList << QVariant::fromValue(ifindex);
argumentList << QVariant::fromValue(domains);
QDBusPendingReply<> reply = m_resolver->asyncCallWithArgumentList(
QStringLiteral("SetLinkDomains"), argumentList);
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this);
QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this,
SLOT(dnsCallCompleted(QDBusPendingCallWatcher*)));
}
void DnsUtilsLinux::setLinkDefaultRoute(int ifindex, bool enable) {
QList<QVariant> argumentList;
argumentList << QVariant::fromValue(ifindex);
argumentList << QVariant::fromValue(enable);
QDBusPendingReply<> reply = m_resolver->asyncCallWithArgumentList(
QStringLiteral("SetLinkDefaultRoute"), argumentList);
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this);
QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this,
SLOT(dnsCallCompleted(QDBusPendingCallWatcher*)));
}
void DnsUtilsLinux::updateLinkDomains() {
/* Get the list of search domains, and remove any others that might conspire
* to satisfy DNS resolution. Unfortunately, this is a pain because Qt doesn't
* seem to be able to demarshall complex property types.
*/
QDBusMessage message = QDBusMessage::createMethodCall(
DBUS_RESOLVE_SERVICE, DBUS_RESOLVE_PATH, DBUS_PROPERTY_INTERFACE, "Get");
message << QString(DBUS_RESOLVE_MANAGER);
message << QString("Domains");
QDBusPendingReply<QVariant> reply =
m_resolver->connection().asyncCall(message);
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this);
QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this,
SLOT(dnsDomainsReceived(QDBusPendingCallWatcher*)));
}
void DnsUtilsLinux::dnsDomainsReceived(QDBusPendingCallWatcher* call) {
QDBusPendingReply<QVariant> reply = *call;
if (reply.isError()) {
logger.error() << "Error retrieving the DNS domains from the DBus service";
delete call;
return;
}
/* Update the state of the DNS domains */
m_linkDomains.clear();
QDBusArgument args = qvariant_cast<QDBusArgument>(reply.value());
QList<DnsDomain> list = qdbus_cast<QList<DnsDomain>>(args);
for (auto d : list) {
if (d.ifindex == 0) {
continue;
}
m_linkDomains[d.ifindex].append(DnsLinkDomain(d.domain, d.search));
}
/* Drop any competing root search domains. */
DnsLinkDomain root = DnsLinkDomain(".", true);
for (auto ifindex : m_linkDomains.keys()) {
if (!m_linkDomains[ifindex].contains(root)) {
continue;
}
QList<DnsLinkDomain> newlist = m_linkDomains[ifindex];
newlist.removeAll(root);
setLinkDomains(ifindex, newlist);
}
/* Add a root search domain for the new interface. */
QList<DnsLinkDomain> newlist = {root};
setLinkDomains(m_ifindex, newlist);
delete call;
}
static DnsMetatypeRegistrationProxy s_dnsMetatypeProxy;

View File

@@ -1,41 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef DNSUTILSLINUX_H
#define DNSUTILSLINUX_H
#include "daemon/dnsutils.h"
#include "dbustypeslinux.h"
#include <QDBusInterface>
#include <QDBusPendingCallWatcher>
class DnsUtilsLinux final : public DnsUtils {
Q_OBJECT
Q_DISABLE_COPY_MOVE(DnsUtilsLinux)
public:
DnsUtilsLinux(QObject* parent);
~DnsUtilsLinux();
bool updateResolvers(const QString& ifname,
const QList<QHostAddress>& resolvers) override;
bool restoreResolvers() override;
private:
void setLinkDNS(int ifindex, const QList<QHostAddress>& resolvers);
void setLinkDomains(int ifindex, const QList<DnsLinkDomain>& domains);
void setLinkDefaultRoute(int ifindex, bool enable);
void updateLinkDomains();
private slots:
void dnsCallCompleted(QDBusPendingCallWatcher*);
void dnsDomainsReceived(QDBusPendingCallWatcher*);
private:
int m_ifindex = 0;
QMap<int, DnsLinkDomainList> m_linkDomains;
QDBusInterface* m_resolver = nullptr;
};
#endif // DNSUTILSLINUX_H

View File

@@ -1,150 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "iputilslinux.h"
#include "daemon/wireguardutils.h"
#include "leakdetector.h"
#include "logger.h"
#include <arpa/inet.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <QHostAddress>
#include <QScopeGuard>
constexpr uint32_t ETH_MTU = 1500;
constexpr uint32_t WG_MTU_OVERHEAD = 80;
namespace {
Logger logger(LOG_LINUX, "IPUtilsLinux");
}
IPUtilsLinux::IPUtilsLinux(QObject* parent) : IPUtils(parent) {
MVPN_COUNT_CTOR(IPUtilsLinux);
logger.debug() << "IPUtilsLinux created.";
}
IPUtilsLinux::~IPUtilsLinux() {
MVPN_COUNT_DTOR(IPUtilsLinux);
logger.debug() << "IPUtilsLinux destroyed.";
}
bool IPUtilsLinux::addInterfaceIPs(const InterfaceConfig& config) {
return addIP4AddressToDevice(config) && addIP6AddressToDevice(config);
}
bool IPUtilsLinux::setMTUAndUp(const InterfaceConfig& config) {
Q_UNUSED(config);
// Create socket file descriptor to perform the ioctl operations on
int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (sockfd < 0) {
logger.error() << "Failed to create ioctl socket.";
return false;
}
auto guard = qScopeGuard([&] { close(sockfd); });
// Setup the interface to interact with
struct ifreq ifr;
strncpy(ifr.ifr_name, WG_INTERFACE, IFNAMSIZ);
// MTU
// FIXME: We need to know how many layers deep this particular
// interface is into a tunnel to work effectively. Otherwise
// we will run into fragmentation issues.
ifr.ifr_mtu = ETH_MTU - WG_MTU_OVERHEAD;
int ret = ioctl(sockfd, SIOCSIFMTU, &ifr);
if (ret) {
logger.error() << "Failed to set MTU -- Return code: " << ret;
return false;
}
// Up
ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
ret = ioctl(sockfd, SIOCSIFFLAGS, &ifr);
if (ret) {
logger.error() << "Failed to set device up -- Return code: " << ret;
return false;
}
return true;
}
bool IPUtilsLinux::addIP4AddressToDevice(const InterfaceConfig& config) {
struct ifreq ifr;
struct sockaddr_in* ifrAddr = (struct sockaddr_in*)&ifr.ifr_addr;
// Name the interface and set family
strncpy(ifr.ifr_name, WG_INTERFACE, IFNAMSIZ);
ifr.ifr_addr.sa_family = AF_INET;
// Get the device address to add to interface
QPair<QHostAddress, int> parsedAddr =
QHostAddress::parseSubnet(config.m_deviceIpv4Address);
QByteArray _deviceAddr = parsedAddr.first.toString().toLocal8Bit();
char* deviceAddr = _deviceAddr.data();
inet_pton(AF_INET, deviceAddr, &ifrAddr->sin_addr);
// Create IPv4 socket to perform the ioctl operations on
int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (sockfd < 0) {
logger.error() << "Failed to create ioctl socket.";
return false;
}
auto guard = qScopeGuard([&] { close(sockfd); });
// Set ifr to interface
int ret = ioctl(sockfd, SIOCSIFADDR, &ifr);
if (ret) {
logger.error() << "Failed to set IPv4: " << deviceAddr
<< "error:" << strerror(errno);
return false;
}
return true;
}
bool IPUtilsLinux::addIP6AddressToDevice(const InterfaceConfig& config) {
// Set up the ifr and the companion ifr6
struct in6_ifreq ifr6;
ifr6.prefixlen = 64;
// Get the device address to add to ifr6 interface
QPair<QHostAddress, int> parsedAddr =
QHostAddress::parseSubnet(config.m_deviceIpv6Address);
QByteArray _deviceAddr = parsedAddr.first.toString().toLocal8Bit();
char* deviceAddr = _deviceAddr.data();
inet_pton(AF_INET6, deviceAddr, &ifr6.addr);
// Create IPv6 socket to perform the ioctl operations on
int sockfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
if (sockfd < 0) {
logger.error() << "Failed to create ioctl socket.";
return false;
}
auto guard = qScopeGuard([&] { close(sockfd); });
// Get the index of named ifr and link with ifr6
struct ifreq ifr;
strncpy(ifr.ifr_name, WG_INTERFACE, IFNAMSIZ);
ifr.ifr_addr.sa_family = AF_INET6;
int ret = ioctl(sockfd, SIOGIFINDEX, &ifr);
if (ret) {
logger.error() << "Failed to get ifindex. Return code: " << ret;
return false;
}
ifr6.ifindex = ifr.ifr_ifindex;
// Set ifr6 to the interface
ret = ioctl(sockfd, SIOCSIFADDR, &ifr6);
if (ret && (errno != EEXIST)) {
logger.error() << "Failed to set IPv6: " << deviceAddr
<< "error:" << strerror(errno);
return false;
}
return true;
}

View File

@@ -1,31 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef IPUTILSLINUX_H
#define IPUTILSLINUX_H
#include "daemon/iputils.h"
#include <arpa/inet.h>
class IPUtilsLinux final : public IPUtils {
public:
IPUtilsLinux(QObject* parent);
~IPUtilsLinux();
bool addInterfaceIPs(const InterfaceConfig& config) override;
bool setMTUAndUp(const InterfaceConfig& config) override;
private:
bool addIP4AddressToDevice(const InterfaceConfig& config);
bool addIP6AddressToDevice(const InterfaceConfig& config);
private:
struct in6_ifreq {
struct in6_addr addr;
uint32_t prefixlen;
unsigned int ifindex;
};
};
#endif // IPUTILSLINUX_H

View File

@@ -1,58 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "command.h"
#include "dbusservice.h"
#include "dbus_adaptor.h"
#include "leakdetector.h"
#include "logger.h"
#include "loghandler.h"
#include "signalhandler.h"
namespace {
Logger logger(LOG_LINUX, "main");
}
class CommandLinuxDaemon final : public Command {
public:
explicit CommandLinuxDaemon(QObject* parent)
: Command(parent, "linuxdaemon", "Starts the linux daemon") {
MVPN_COUNT_CTOR(CommandLinuxDaemon);
}
~CommandLinuxDaemon() { MVPN_COUNT_DTOR(CommandLinuxDaemon); }
int run(QStringList& tokens) override {
Q_ASSERT(!tokens.isEmpty());
LogHandler::setLocation("/var/log");
return runCommandLineApp([&]() {
DBusService* dbus = new DBusService(qApp);
DbusAdaptor* adaptor = new DbusAdaptor(dbus);
dbus->setAdaptor(adaptor);
QDBusConnection connection = QDBusConnection::systemBus();
logger.debug() << "Connecting to DBus...";
if (!connection.registerService("org.mozilla.vpn.dbus") ||
!connection.registerObject("/", dbus)) {
logger.error() << "Connection failed - name:"
<< connection.lastError().name()
<< "message:" << connection.lastError().message();
return 1;
}
SignalHandler sh;
QObject::connect(&sh, &SignalHandler::quitRequested, [&]() {
dbus->deactivate();
qApp->quit();
});
logger.debug() << "Ready!";
return qApp->exec();
});
}
};
static Command::RegistrationProxy<CommandLinuxDaemon> s_commandLinuxDaemon;

View File

@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- -->
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<!-- Only root can own the service -->
<policy user="root">
<allow own="org.mozilla.vpn.dbus"/>
</policy>
<!-- Allow anyone to invoke methods on the interfaces -->
<policy context="default">
<allow own="org.mozilla.vpn.dbus"/>
<allow send_destination="org.mozilla.vpn.dbus"/>
<allow send_destination="org.mozilla.vpn.dbus"
send_interface="org.freedesktop.DBus.Introspectable"/>
<allow send_destination="org.mozilla.vpn.dbus"
send_interface="org.freedesktop.DBus.Properties"/>
<allow send_destination="org.mozilla.vpn.dbus"
send_interface="org.freedesktop.DBus.Properties"/>
</policy>
</busconfig>

View File

@@ -1,5 +0,0 @@
[D-BUS Service]
User=root
Name=org.mozilla.vpn.dbus
Exec=/usr/bin/mozillavpn linuxdaemon
SystemdService=mozillavpn.service

View File

@@ -1,46 +0,0 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.mozilla.vpn.dbus">
<method name="version">
<arg type="s" direction="out"/>
</method>
<method name="activate">
<arg type="b" direction="out"/>
<arg name="jsonConfig" type="s" direction="in"/>
</method>
<method name="deactivate">
<arg type="b" direction="out"/>
</method>
<method name="status">
<arg name="jsonStatus" type="s" direction="out"/>
</method>
<method name="runningApps">
<arg type="s" direction="out"/>
</method>
<method name="firewallApp">
<arg type="b" direction="out"/>
<arg name="appName" type="s" direction="in"/>
<arg name="state" type="s" direction="in"/>
</method>
<method name="firewallPid">
<arg type="b" direction="out"/>
<arg name="rootpid" type="i" direction="in"/>
<arg name="state" type="s" direction="in"/>
</method>
<method name="firewallClear">
<arg type="b" direction="out"/>
</method>
<method name="getLogs">
<arg name="logs" type="s" direction="out"/>
</method>
<method name="cleanupLogs">
</method>
<signal name="connected">
<arg name="hopindex" type="i" direction="out"/>
</signal>
<signal name="disconnected">
<arg name="hopindex" type="i" direction="out"/>
</signal>
</interface>
</node>

View File

@@ -1,25 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE policyconfig PUBLIC
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
"http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
<policyconfig>
<action id="org.mozilla.vpn.activate">
<description>Activate the Mozilla VPN</description>
<message>Activate the Mozilla VPN</message>
<defaults>
<allow_inactive>no</allow_inactive>
<allow_active>auth_admin</allow_active>
</defaults>
</action>
<action id="org.mozilla.vpn.deactivate">
<description>Deactivate the Mozilla VPN</description>
<message>Deactivate the Mozilla VPN</message>
<defaults>
<allow_inactive>no</allow_inactive>
<allow_active>auth_admin</allow_active>
</defaults>
</action>
</policyconfig>

View File

@@ -1,227 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "pidtracker.h"
#include "leakdetector.h"
#include "logger.h"
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <limits.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <linux/netlink.h>
#include <linux/connector.h>
#include <linux/cn_proc.h>
constexpr size_t CN_MCAST_MSG_SIZE =
sizeof(struct cn_msg) + sizeof(enum proc_cn_mcast_op);
namespace {
Logger logger(LOG_LINUX, "PidTracker");
}
PidTracker::PidTracker(QObject* parent) : QObject(parent) {
MVPN_COUNT_CTOR(PidTracker);
logger.debug() << "PidTracker created.";
m_nlsock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
if (m_nlsock < 0) {
logger.error() << "Failed to create netlink socket:" << strerror(errno);
return;
}
struct sockaddr_nl nladdr;
nladdr.nl_family = AF_NETLINK;
nladdr.nl_groups = CN_IDX_PROC;
nladdr.nl_pid = getpid();
nladdr.nl_pad = 0;
if (bind(m_nlsock, (struct sockaddr*)&nladdr, sizeof(nladdr)) < 0) {
logger.error() << "Failed to bind netlink socket:" << strerror(errno);
close(m_nlsock);
m_nlsock = -1;
return;
}
char buf[NLMSG_SPACE(CN_MCAST_MSG_SIZE)];
struct nlmsghdr* nlmsg = (struct nlmsghdr*)buf;
struct cn_msg* cnmsg = (struct cn_msg*)NLMSG_DATA(nlmsg);
enum proc_cn_mcast_op mcast_op = PROC_CN_MCAST_LISTEN;
memset(buf, 0, sizeof(buf));
nlmsg->nlmsg_len = NLMSG_LENGTH(CN_MCAST_MSG_SIZE);
nlmsg->nlmsg_type = NLMSG_DONE;
nlmsg->nlmsg_flags = 0;
nlmsg->nlmsg_seq = 0;
nlmsg->nlmsg_pid = getpid();
cnmsg->id.idx = CN_IDX_PROC;
cnmsg->id.val = CN_VAL_PROC;
cnmsg->seq = 0;
cnmsg->ack = 0;
cnmsg->len = sizeof(mcast_op);
memcpy(cnmsg->data, &mcast_op, sizeof(mcast_op));
if (send(m_nlsock, nlmsg, sizeof(buf), 0) != sizeof(buf)) {
logger.error() << "Failed to send netlink message:" << strerror(errno);
close(m_nlsock);
m_nlsock = -1;
return;
}
m_socket = new QSocketNotifier(m_nlsock, QSocketNotifier::Read, this);
connect(m_socket, &QSocketNotifier::activated, this, &PidTracker::readData);
}
PidTracker::~PidTracker() {
MVPN_COUNT_DTOR(PidTracker);
logger.debug() << "PidTracker destroyed.";
m_processTree.clear();
while (!m_processGroups.isEmpty()) {
ProcessGroup* group = m_processGroups.takeFirst();
delete group;
}
if (m_nlsock > 0) {
close(m_nlsock);
}
}
ProcessGroup* PidTracker::track(const QString& name, int rootpid) {
ProcessGroup* group = m_processTree.value(rootpid, nullptr);
if (group) {
logger.warning() << "Ignoring attempt to track duplicate PID";
return group;
}
group = new ProcessGroup(name, rootpid);
group->kthreads[rootpid] = 1;
group->refcount = 1;
m_processGroups.append(group);
m_processTree[rootpid] = group;
return group;
}
void PidTracker::handleProcEvent(struct cn_msg* cnmsg) {
struct proc_event* ev = (struct proc_event*)cnmsg->data;
if (ev->what == proc_event::PROC_EVENT_FORK) {
auto forkdata = &ev->event_data.fork;
/* If the child process already exists, track a new kernel thread. */
ProcessGroup* group = m_processTree.value(forkdata->child_tgid, nullptr);
if (group) {
group->kthreads[forkdata->child_tgid]++;
return;
}
/* Track a new userspace process if was forked from a known parent. */
group = m_processTree.value(forkdata->parent_tgid, nullptr);
if (!group) {
return;
}
m_processTree[forkdata->child_tgid] = group;
group->kthreads[forkdata->child_tgid] = 1;
group->refcount++;
emit pidForked(group->name, forkdata->parent_tgid, forkdata->child_tgid);
}
if (ev->what == proc_event::PROC_EVENT_EXIT) {
auto exitdata = &ev->event_data.exit;
ProcessGroup* group = m_processTree.value(exitdata->process_tgid, nullptr);
if (!group) {
return;
}
/* Decrement the number of kernel threads in this userspace process. */
uint threadcount = group->kthreads.value(exitdata->process_tgid, 0);
if (threadcount == 0) {
return;
}
if (threadcount > 1) {
group->kthreads[exitdata->process_tgid] = threadcount - 1;
return;
}
group->kthreads.remove(exitdata->process_tgid);
/* A userspace process exits when all of its kernel threads exit. */
Q_ASSERT(group->refcount > 0);
group->refcount--;
if (group->refcount == 0) {
emit terminated(group->name, group->rootpid);
m_processGroups.removeAll(group);
delete group;
}
}
}
void PidTracker::readData() {
struct sockaddr_nl src;
socklen_t srclen = sizeof(src);
ssize_t recvlen;
recvlen = recvfrom(m_nlsock, m_readBuf, sizeof(m_readBuf), MSG_DONTWAIT,
(struct sockaddr*)&src, &srclen);
if (recvlen == ENOBUFS) {
logger.error()
<< "Failed to read netlink socket: buffer full, message dropped";
return;
}
if (recvlen < 0) {
logger.error() << "Failed to read netlink socket:" << strerror(errno);
return;
}
if (srclen != sizeof(src)) {
logger.error() << "Failed to read netlink socket: invalid address length";
return;
}
/* We are only interested in process-control messages from the kernel */
if ((src.nl_groups != CN_IDX_PROC) || (src.nl_pid != 0)) {
return;
}
/* Handle the process-control messages. */
struct nlmsghdr* msg;
for (msg = (struct nlmsghdr*)m_readBuf; NLMSG_OK(msg, recvlen);
msg = NLMSG_NEXT(msg, recvlen)) {
struct cn_msg* cnmsg = (struct cn_msg*)NLMSG_DATA(msg);
if (msg->nlmsg_type == NLMSG_NOOP) {
continue;
}
if ((msg->nlmsg_type == NLMSG_ERROR) ||
(msg->nlmsg_type == NLMSG_OVERRUN)) {
break;
}
handleProcEvent(cnmsg);
if (msg->nlmsg_type == NLMSG_DONE) {
break;
}
}
}
bool ProcessGroup::moveToCgroup(const QString& name) {
/* Do nothing if Cgroups are not supported. */
if (name.isNull()) {
return true;
}
QString cgProcsFile = name + "/cgroup.procs";
FILE* fp = fopen(qPrintable(cgProcsFile), "w");
if (!fp) {
return false;
}
for (auto pid : kthreads.keys()) {
fprintf(fp, "%d\n", pid);
fflush(fp);
}
fclose(fp);
return true;
}

View File

@@ -1,72 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef PIDTRACKER_H
#define PIDTRACKER_H
#include <QHash>
#include <QList>
#include <QSocketNotifier>
#include <QString>
#include "leakdetector.h"
struct cn_msg;
class ProcessGroup {
public:
ProcessGroup(const QString& groupName, int groupRootPid,
const QString& groupState = "active") {
MVPN_COUNT_CTOR(ProcessGroup);
name = groupName;
rootpid = groupRootPid;
state = groupState;
refcount = 0;
}
~ProcessGroup() { MVPN_COUNT_DTOR(ProcessGroup); }
bool moveToCgroup(const QString& name);
QHash<int, uint> kthreads;
QString name;
QString state;
int rootpid;
int refcount;
};
class PidTracker final : public QObject {
Q_OBJECT
Q_DISABLE_COPY_MOVE(PidTracker)
public:
explicit PidTracker(QObject* parent);
~PidTracker();
ProcessGroup* track(const QString& name, int rootpid);
QList<int> pids() { return m_processTree.keys(); }
QList<ProcessGroup*>::iterator begin() { return m_processGroups.begin(); }
QList<ProcessGroup*>::iterator end() { return m_processGroups.end(); }
ProcessGroup* group(int pid) { return m_processTree.value(pid); }
signals:
void pidForked(const QString& name, int parent, int child);
void pidExited(const QString& name, int pid);
void terminated(const QString& name, int rootpid);
private:
void handleProcEvent(struct cn_msg*);
private slots:
void readData();
private:
int m_nlsock;
char m_readBuf[2048];
QSocketNotifier* m_socket = nullptr;
QHash<int, ProcessGroup*> m_processTree;
QList<ProcessGroup*> m_processGroups;
};
#endif // PIDTRACKER_H

View File

@@ -1,66 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "polkithelper.h"
#include <QDebug>
// No extra QT includes after this line!
#undef Q_SIGNALS
#include "polkit/polkit.h"
class Helper final {
public:
Helper() = default;
~Helper() {
if (m_error) {
g_error_free(m_error);
}
if (m_subject) {
g_object_unref(m_subject);
}
if (m_result) {
g_object_unref(m_result);
}
}
public:
GError* m_error = nullptr;
PolkitSubject* m_subject = nullptr;
PolkitAuthorizationResult* m_result = nullptr;
};
// static
PolkitHelper* PolkitHelper::instance() {
static PolkitHelper s_instance;
return &s_instance;
}
bool PolkitHelper::checkAuthorization(const QString& actionId) {
qDebug() << "Check Authorization for" << actionId;
Helper h;
PolkitAuthority* authority = polkit_authority_get_sync(NULL, &h.m_error);
if (h.m_error) {
qDebug() << "Fail to generate a polkit authority object:"
<< h.m_error->message;
return false;
}
h.m_subject = polkit_unix_process_new_for_owner(getpid(), 0, -1);
h.m_result = polkit_authority_check_authorization_sync(
authority, h.m_subject, actionId.toLatin1().data(), nullptr,
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, nullptr,
&h.m_error);
if (h.m_error) {
qDebug() << "Authorization sync failed:" << h.m_error->message;
return false;
}
return polkit_authorization_result_get_is_authorized(h.m_result);
}

View File

@@ -1,23 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef POLKITHELPER_H
#define POLKITHELPER_H
#include <QString>
class PolkitHelper final {
public:
static PolkitHelper* instance();
bool checkAuthorization(const QString& actionId);
private:
PolkitHelper() = default;
~PolkitHelper() = default;
Q_DISABLE_COPY(PolkitHelper)
};
#endif // POLKITHELPER_H

Some files were not shown because too many files have changed in this diff Show More