/*
* Copyright (c) 2023 Proton AG
*
* This file is part of ProtonVPN.
*
* ProtonVPN is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ProtonVPN is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ProtonVPN. If not, see .
*/
using ProtonVPN.Client.EventMessaging.Contracts;
using ProtonVPN.Client.Logic.Connection.Contracts;
using ProtonVPN.Client.Logic.Connection.Contracts.Messages;
using ProtonVPN.Common.Legacy.PortForwarding;
using ProtonVPN.EntityMapping.Contracts;
using ProtonVPN.Logging.Contracts;
using ProtonVPN.Logging.Contracts.Events.AppLogs;
using ProtonVPN.ProcessCommunication.Contracts.Entities.PortForwarding;
namespace ProtonVPN.Client.Logic.Connection;
public class PortForwardingManager : IPortForwardingManager, IEventMessageReceiver
{
private readonly IEventMessageSender _eventMessageSender;
private readonly IEntityMapper _entityMapper;
private readonly ILogger _logger;
private readonly IConnectionManager _connectionManager;
private DateTime _lastPortChangeTimeUtc;
private int? _port;
private PortMappingStatus _status;
protected bool IsConnectedToP2PServer => _connectionManager.IsConnected && _connectionManager.CurrentConnectionDetails?.IsP2P == true;
public bool IsFetchingPort =>
IsConnectedToP2PServer &&
_status is not PortMappingStatus.Stopped
and not PortMappingStatus.Error
and not PortMappingStatus.DestroyPortMappingCommunication;
public bool HasError => _status is PortMappingStatus.Error;
protected bool IsConnectedToNonP2PServer => _connectionManager.IsConnected && _connectionManager.CurrentConnectionDetails?.IsP2P != true;
public int? ActivePort => IsConnectedToP2PServer && (_status is PortMappingStatus.PortMappingCommunication or PortMappingStatus.SleepingUntilRefresh)
? _port
: null;
public DateTime? LastPortChangeTimeUtc => ActivePort.HasValue ? _lastPortChangeTimeUtc : null;
public PortForwardingManager(
IEventMessageSender eventMessageSender,
IEntityMapper entityMapper,
ILogger logger,
IConnectionManager connectionManager)
{
_eventMessageSender = eventMessageSender;
_entityMapper = entityMapper;
_logger = logger;
_connectionManager = connectionManager;
}
public void Receive(PortForwardingStateIpcEntity message)
{
PortForwardingState portForwardingState = _entityMapper.Map(message);
int? newPort = portForwardingState.MappedPort?.MappedPort?.ExternalPort;
if (_port != newPort)
{
_logger.Info($"Port forwarding port changed from '{_port}' to '{newPort}'.");
_port = newPort;
_lastPortChangeTimeUtc = DateTime.UtcNow;
NotifyPortChange(newPort);
}
PortMappingStatus newStatus = portForwardingState.Status;
if (_status != newStatus)
{
_logger.Info($"Port forwarding status changed from '{_status}' to '{newStatus}'.");
_status = newStatus;
NotifyStatusChange(newStatus);
}
}
private void NotifyPortChange(int? newPort)
{
_eventMessageSender.Send(new PortForwardingPortChangedMessage(newPort));
}
private void NotifyStatusChange(PortMappingStatus newStatus)
{
_eventMessageSender.Send(new PortForwardingStatusChangedMessage(newStatus));
}
}