Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Assorted fixes for Xcode 16 #10092

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
shallow = true
[submodule "3rdparty/wireguard-apple"]
path = 3rdparty/wireguard-apple
url = https://github.com/WireGuard/wireguard-apple
url = https://github.com/mozilla/wireguard-apple.git
shallow = true
[submodule "3rdparty/openSSL"]
path = 3rdparty/openSSL
Expand Down
2 changes: 1 addition & 1 deletion 3rdparty/wireguard-apple
Submodule wireguard-apple updated 126 files
3 changes: 2 additions & 1 deletion docs/Building/ios.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,5 @@ Once Xcode has opened the project, select the `mozillavpn` target and start the

Tips:
* If you can't see a simulator target in the Xcode interface, look at Product -> Destination -> Destination Architectures -> Show Both
* Due to lack of low level networking support, it is not possible to turn on the VPN from the iOS simulator in Xcode.
* Simulator builds can only be run on Rosetta versions of simulators. Simulator builds use the mocked VPN controller.
* When switching between building for Simulator and building for a device, the build folder must be completely removed.
24 changes: 0 additions & 24 deletions docs/Building/macos.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,6 @@ Install extra conda packages

./scripts/macos/conda_install_extras.sh

Your Xcode install comes with a copy of the MacOS-SDK.
We need to tell the conda environment where to find it.

Find the sdk path

xcrun --sdk macosx --show-sdk-path

If xcrun didn't work, default paths where you probably find your SDK:
* Default Xcode-command-line tool path: `/Library/Developer/CommandLineTools/SDKs/MacOSX.<VersionNumber>.sdk`
* Default Xcode.app path: `/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk`

Add it to the conda env

conda env config vars set SDKROOT=$SDK_PATH

Reactivate your conda env

conda activate vpn

You can view your set variables

conda env config vars list

The variable config step only needs to be done once.
When you next want to start building the VPN, all you need to do is activate your conda environment (`conda activate vpn`).

## Get Qt
Expand Down
2 changes: 1 addition & 1 deletion env.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ dependencies:
- rust-std-x86_64-linux-android=1.75
- rust-std-i686-linux-android=1.75
- rust-std-aarch64-linux-android=1.75
- go=1.18
- go=1.22
- compiler-rt
- cmake=3.26.3
- ninja=1.11.0
Expand Down
13 changes: 13 additions & 0 deletions ios/networkextension/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,16 @@ set_source_files_properties(
target_sources(networkextension PRIVATE
${CMAKE_BINARY_DIR}/src/generated/VPNMetrics.swift
)

# The IOSGlean framework is built under the src directory, so we'll need to
# explicitly add it to the framework search paths. However, this gets messy
# in Xcode because the actual path is somewhat somewhat buried in
# subdirectories.
if(XCODE)
# Xcode is a multi-config generator, so technically we need to do this for
# each supported config too.
foreach(CONFIG ${CMAKE_CONFIGURATION_TYPES})
set_target_properties(networkextension PROPERTIES
"XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS[variant=${CONFIG}]" "${CMAKE_BINARY_DIR}/src/${CONFIG}$(EFFECTIVE_PLATFORM_NAME)")
endforeach()
endif()
7 changes: 0 additions & 7 deletions scripts/cmake/rustlang.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -381,11 +381,4 @@ function(add_rust_library TARGET_NAME)

add_dependencies(${TARGET_NAME} ${TARGET_NAME}_builder)
set_property(TARGET ${TARGET_NAME} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${CMAKE_DL_LIBS})

## When including multiple rust staticlibs, we often wind up with duplicate
## symbols from the rust runtime. Work around it by permitting duplicates
## during linking.
set_property(TARGET ${TARGET_NAME} APPEND PROPERTY INTERFACE_LINK_OPTIONS
$<$<C_COMPILER_ID:AppleClang>:-Xlink=-force:multiple>
)
endfunction()
5 changes: 4 additions & 1 deletion src/cmake/ios.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ target_sources(mozillavpn PRIVATE
${CMAKE_SOURCE_DIR}/src/platforms/ios/iosgleanbridge.h
)


## Install the Network Extension into the bundle.
add_dependencies(mozillavpn networkextension)

Expand Down Expand Up @@ -74,6 +73,10 @@ set_target_properties(mozillavpn PROPERTIES
# Do not strip debug symbols on copy
XCODE_ATTRIBUTE_COPY_PHASE_STRIP "NO"
XCODE_ATTRIBUTE_STRIP_INSTALLED_PRODUCT "NO"
# Do not build with debug dylibs - it breaks the Qt/ios entrypoint.
# This can be fixed by rolling out own entrypoint and invoking
# UIApplicationMain() ourselves.
XCODE_ATTRIBUTE_ENABLE_DEBUG_DYLIB "NO"
)
target_include_directories(mozillavpn PRIVATE ${CMAKE_SOURCE_DIR})

Expand Down
4 changes: 4 additions & 0 deletions src/commands/commandstatus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ int CommandStatus::run(QStringList& tokens) {
case Controller::StateSwitching:
stream << "switching";
break;

case Controller::State::StateOnboarding:
stream << "onboarding";
break;
}

stream << Qt::endl;
Expand Down
8 changes: 8 additions & 0 deletions src/commands/commandui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,14 @@ int CommandUI::run(QStringList& tokens) {
MockDaemon* daemon = new MockDaemon(qApp);
qputenv("MVPN_CONTROL_SOCKET", daemon->socketPath().toLocal8Bit());
}
#ifdef MZ_IOS
else if (!qEnvironmentVariable("SIMULATOR_DEVICE_NAME").isEmpty()) {
// If we are running in the iOS device simulator, the network extension
// is not supported in this environment - use a mocked daemon instead.
MockDaemon* daemon = new MockDaemon(qApp);
qputenv("MVPN_CONTROL_SOCKET", daemon->socketPath().toLocal8Bit());
}
#endif

MozillaVPN vpn;
logger.info() << "MozillaVPN" << Constants::versionString();
Expand Down
37 changes: 31 additions & 6 deletions src/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ Controller::Reason stateToReason(Controller::State state) {
return Controller::ReasonConfirming;
}

if (state == Controller::StateOnboarding) {
return Controller::ReasonOnboarding;
}

return Controller::ReasonNone;
}
} // namespace
Expand Down Expand Up @@ -122,6 +126,12 @@ QString Controller::useLocalSocketPath() const {
}
#elif defined(MZ_WINDOWS)
return Constants::WINDOWS_DAEMON_PATH;
#elif defined(MZ_IOS)
// The IOS simulator also uses a mocked daemon.
bool isSimDevice = !qEnvironmentVariable("SIMULATOR_DEVICE_NAME").isEmpty();
if (isSimDevice && !path.isEmpty()) {
return path;
}
#endif

// Otherwise, we will need some other controller.
Expand Down Expand Up @@ -332,6 +342,8 @@ qint64 Controller::connectionTimestamp() const {
case Controller::State::StateInitializing:
[[fallthrough]];
case Controller::State::StateOff:
[[fallthrough]];
case Controller::State::StateOnboarding:
return 0;
case Controller::State::StateOn:
[[fallthrough]];
Expand Down Expand Up @@ -627,7 +639,7 @@ void Controller::activateNext() {
return;
}

if (m_state != StateSilentSwitching) {
if ((m_state != StateSilentSwitching) && (m_state != StateOnboarding)) {
// Move to the StateConfirming if we are awaiting any connection handshakes
setState(StateConfirming);
}
Expand Down Expand Up @@ -774,6 +786,12 @@ void Controller::disconnected() {

NextStep nextStep = m_nextStep;

// Mobile onboarding is completed when we receive the disconnected signal.
if (m_state == StateOnboarding) {
logger.debug() << "Onboarding completed";
MozillaVPN::instance()->onboardingCompleted();
}

if (processNextStep()) {
setState(StateOff);
return;
Expand Down Expand Up @@ -973,6 +991,16 @@ bool Controller::activate(const ServerData& serverData,
return true;
}

// If we are in the onboarding state, this connection is being made just
// to establish system permissions. We don't actually want to connect to
// a server.
if (App::instance()->state() == App::StateOnboarding) {
setState(StateOnboarding);
clearRetryCounter();
activateInternal(DoNotForceDNSPort, RandomizeServerSelection, ClientUser);
return true;
}

if (Feature::get(Feature::Feature_checkConnectivityOnActivation)
->isSupported()) {
// Ensure that the device is connected to the Internet.
Expand Down Expand Up @@ -1007,11 +1035,8 @@ bool Controller::activate(const ServerData& serverData,

// Check if the error propagation has changed the Mozilla VPN
// state. Continue only if the user is still authenticated and
// subscribed. We can ignore this during onboarding because we are
// not actually turning the VPN on (only asking for VPN system
// config permissions)
if (App::instance()->state() != App::StateMain &&
App::instance()->state() != App::StateOnboarding) {
// subscribed.
if (App::instance()->state() != App::StateMain) {
return;
}

Expand Down
4 changes: 4 additions & 0 deletions src/controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,18 @@ class Controller : public QObject, public LogSerializer {
StateDisconnecting,
StateSilentSwitching,
StateSwitching,
StateOnboarding,
};
Q_ENUM(State)

enum Reason {
ReasonNone = 0,
ReasonSwitching,
ReasonConfirming,
ReasonOnboarding,
};
Q_ENUM(Reason)

/**
* @brief Who asked the Connection
* to be Initiated? A Webextension
Expand Down
11 changes: 11 additions & 0 deletions src/daemon/daemonlocalserverconnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,17 @@ void DaemonLocalServerConnection::parseCommand(const QByteArray& data) {
return;
}

// If this connection is being made for onboading. Don't actually connect,
// just emit a disconnected signal to confirm.
QString reason = obj.value("reason").toString();
if (!reason.isEmpty()) {
logger.error() << "Connection reason:" << reason;
}
if (reason == "onboarding") {
emit disconnected();
return;
}

if (!m_daemon->activate(config)) {
logger.error() << "Failed to activate the interface";
emit disconnected();
Expand Down
9 changes: 9 additions & 0 deletions src/daemon/mock/mockdaemon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ MockDaemon::MockDaemon(const QString& name, QObject* parent)

logger.debug() << "Mock daemon created";

#ifdef MZ_IOS
// We have to go out of our way to keep the path length under sizeof(sun_path)
// for iOS because the QDir::tempPath() used by QLocalServer winds up being
// way too long.
if (!m_socketName.startsWith('/')) {
m_socketName.prepend("/tmp/");
}
#endif

#ifndef MZ_WASM
m_server.setSocketOptions(QLocalServer::UserAccessOption);
if (!m_server.listen(m_socketName)) {
Expand Down
16 changes: 12 additions & 4 deletions src/localsocketcontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <QJsonObject>
#include <QJsonValue>
#include <QMetaType>
#include <QMetaEnum>

#include "daemon/daemonerrors.h"
#include "errorhandler.h"
Expand Down Expand Up @@ -49,8 +50,11 @@ LocalSocketController::LocalSocketController(const QString& path)
m_socket = new QLocalSocket(this);
connect(m_socket, &QLocalSocket::connected, this,
&LocalSocketController::daemonConnected);
connect(m_socket, &QLocalSocket::disconnected, this,
[&] { errorOccurred(QLocalSocket::PeerClosedError); });
connect(m_socket, &QLocalSocket::disconnected, this, [&] {
if (m_daemonState != eInitializing) {
errorOccurred(QLocalSocket::PeerClosedError);
}
});
connect(m_socket, &QLocalSocket::errorOccurred, this,
&LocalSocketController::errorOccurred);
connect(m_socket, &QLocalSocket::readyRead, this,
Expand Down Expand Up @@ -124,11 +128,15 @@ void LocalSocketController::daemonConnected() {

void LocalSocketController::activate(const InterfaceConfig& config,
Controller::Reason reason) {
Q_UNUSED(reason);

QJsonObject json = config.toJson();
json.insert("type", "activate");

if (reason != Controller::ReasonNone) {
QMetaEnum metaEnum = QMetaEnum::fromType<Controller::Reason>();
QString reasonString = metaEnum.valueToKey(reason);
json.insert("reason", reasonString.toLower().mid(6));
}

write(json);
}

Expand Down
29 changes: 14 additions & 15 deletions src/models/devicemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,20 +71,21 @@ bool DeviceModel::fromSettings(const Keys* keys) {

namespace {

bool sortCallback(const Device& a, const Device& b, const Keys* keys) {
if (a.isCurrentDevice(keys)) {
return true;
}

if (b.isCurrentDevice(keys)) {
return false;
}

mcleinman marked this conversation as resolved.
Show resolved Hide resolved
bool sortCallback(const Device& a, const Device& b) {
return a.createdAt() > b.createdAt();
}

} // anonymous namespace

// Check if the current device exists, and if so move it to to the front.
void DeviceModel::moveCurrentDevice(const Keys* keys) {
mcleinman marked this conversation as resolved.
Show resolved Hide resolved
for (qsizetype index = 0; index < m_devices.length(); index++) {
if (m_devices.at(index).isCurrentDevice(keys)) {
m_devices.move(index, 0);
}
}
}

bool DeviceModel::fromJsonInternal(const Keys* keys, const QByteArray& json) {
beginResetModel();

Expand Down Expand Up @@ -132,9 +133,8 @@ bool DeviceModel::fromJsonInternal(const Keys* keys, const QByteArray& json) {
}
}

std::sort(m_devices.begin(), m_devices.end(),
std::bind(sortCallback, std::placeholders::_1,
std::placeholders::_2, keys));
std::sort(m_devices.begin(), m_devices.end(), sortCallback);
moveCurrentDevice(keys);

endResetModel();
emit changed();
Expand Down Expand Up @@ -207,9 +207,8 @@ void DeviceModel::stopDeviceRemovalFromPublicKey(const QString& publicKey,

m_devices.append(*i);

std::sort(m_devices.begin(), m_devices.end(),
std::bind(sortCallback, std::placeholders::_1,
std::placeholders::_2, keys));
std::sort(m_devices.begin(), m_devices.end(), sortCallback);
moveCurrentDevice(keys);

m_removedDevices.erase(i);

Expand Down
1 change: 1 addition & 0 deletions src/models/devicemodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ class DeviceModel final : public QAbstractListModel, public LogSerializer {
private:
[[nodiscard]] bool fromJsonInternal(const Keys* keys, const QByteArray& json);

void moveCurrentDevice(const Keys* keys);
bool removeRows(int row, int count,
const QModelIndex& parent = QModelIndex()) override;

Expand Down
Loading
Loading