amnezia-client/client/mozilla/models/server.cpp
pokamest 35ecb8499d
WireGuard for MacOS (#248)
* WireGuard for MacOS
* Fix openvpn block-outside-dns
2023-07-15 22:19:48 +01:00

209 lines
5 KiB
C++

/* 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 "server.h"
#include <QDateTime>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonValue>
#include <QRandomGenerator>
#include "leakdetector.h"
Server::Server() { MZ_COUNT_CTOR(Server); }
Server::Server(const QString& countryCode, const QString& cityName) {
MZ_COUNT_CTOR(Server);
m_countryCode = countryCode;
m_cityName = cityName;
}
Server::Server(const Server& other) {
MZ_COUNT_CTOR(Server);
*this = other;
}
Server& Server::operator=(const Server& other) {
if (this == &other) return *this;
m_hostname = other.m_hostname;
m_ipv4AddrIn = other.m_ipv4AddrIn;
m_ipv4Gateway = other.m_ipv4Gateway;
m_ipv6AddrIn = other.m_ipv6AddrIn;
m_ipv6Gateway = other.m_ipv6Gateway;
m_portRanges = other.m_portRanges;
m_publicKey = other.m_publicKey;
m_weight = other.m_weight;
m_socksName = other.m_socksName;
m_multihopPort = other.m_multihopPort;
m_countryCode = other.m_countryCode;
m_cityName = other.m_cityName;
return *this;
}
Server::~Server() { MZ_COUNT_DTOR(Server); }
bool Server::fromJson(const QJsonObject& obj) {
// Reset.
m_hostname = "";
QJsonValue hostname = obj.value("hostname");
if (!hostname.isString()) {
return false;
}
QJsonValue ipv4AddrIn = obj.value("ipv4_addr_in");
if (!ipv4AddrIn.isString()) {
return false;
}
QJsonValue ipv4Gateway = obj.value("ipv4_gateway");
if (!ipv4Gateway.isString()) {
return false;
}
QJsonValue ipv6AddrIn = obj.value("ipv6_addr_in");
// If this object comes from the IOS migration, the ipv6_addr_in is missing.
QJsonValue ipv6Gateway = obj.value("ipv6_gateway");
if (!ipv6Gateway.isString()) {
return false;
}
QJsonValue publicKey = obj.value("public_key");
if (!publicKey.isString()) {
return false;
}
QJsonValue weight = obj.value("weight");
if (!weight.isDouble()) {
return false;
}
QJsonValue portRanges = obj.value("port_ranges");
if (!portRanges.isArray()) {
return false;
}
// optional properties.
QJsonValue socks5_name = obj.value("socks5_name");
QJsonValue multihop_port = obj.value("multihop_port");
QList<QPair<uint32_t, uint32_t>> prList;
QJsonArray portRangesArray = portRanges.toArray();
for (const QJsonValue& portRangeValue : portRangesArray) {
if (!portRangeValue.isArray()) {
return false;
}
QJsonArray port = portRangeValue.toArray();
if (port.count() != 2) {
return false;
}
QJsonValue a = port.at(0);
if (!a.isDouble()) {
return false;
}
QJsonValue b = port.at(1);
if (!b.isDouble()) {
return false;
}
prList.append(QPair<uint32_t, uint32_t>(a.toInt(), b.toInt()));
}
m_hostname = hostname.toString();
m_ipv4AddrIn = ipv4AddrIn.toString();
m_ipv4Gateway = ipv4Gateway.toString();
m_ipv6AddrIn = ipv6AddrIn.toString();
m_ipv6Gateway = ipv6Gateway.toString();
m_portRanges.swap(prList);
m_publicKey = publicKey.toString();
m_weight = weight.toInt();
m_socksName = socks5_name.toString();
m_multihopPort = multihop_port.toInt();
return true;
}
bool Server::fromMultihop(const Server& exit, const Server& entry) {
m_hostname = exit.m_hostname;
m_ipv4Gateway = exit.m_ipv4Gateway;
m_ipv6Gateway = exit.m_ipv6Gateway;
m_publicKey = exit.m_publicKey;
m_socksName = exit.m_socksName;
m_multihopPort = exit.m_multihopPort;
m_ipv4AddrIn = entry.m_ipv4AddrIn;
m_ipv6AddrIn = entry.m_ipv6AddrIn;
return forcePort(exit.m_multihopPort);
}
bool Server::forcePort(uint32_t port) {
m_portRanges.clear();
m_portRanges.append(QPair<uint32_t, uint32_t>(port, port));
return true;
}
// static
const Server& Server::weightChooser(const QList<Server>& servers) {
static const Server emptyServer;
Q_ASSERT(!emptyServer.initialized());
if (servers.isEmpty()) {
return emptyServer;
}
uint32_t weightSum = 0;
for (const Server& server : servers) {
weightSum += server.weight();
}
quint32 r = QRandomGenerator::global()->generate() % (weightSum + 1);
for (const Server& server : servers) {
if (server.weight() >= r) {
return server;
}
r -= server.weight();
}
// This should not happen.
Q_ASSERT(false);
return emptyServer;
}
uint32_t Server::choosePort() const {
if (m_portRanges.isEmpty()) {
return 0;
}
// Count the total number of potential ports.
quint32 length = 0;
for (const QPair<uint32_t, uint32_t>& range : m_portRanges) {
Q_ASSERT(range.first <= range.second);
length += range.second - range.first + 1;
}
Q_ASSERT(length < 65536);
Q_ASSERT(length > 0);
// Pick a port at random.
quint32 r = QRandomGenerator::global()->generate() % length;
quint32 port = 0;
for (const QPair<uint32_t, uint32_t>& range : m_portRanges) {
if (r <= (range.second - range.first)) {
port = r + range.first;
break;
}
r -= (range.second - range.first + 1);
}
Q_ASSERT(port != 0);
return port;
}