Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -123,16 +123,15 @@ public synchronized void tearDownVpn() {
/**
* Connects a tunnel between a Outline proxy server and the VPN TUN interface.
*
* @param client provides access to the Outline client.
* @param isUdpEnabled conveys the result of UDP probing. TODO: Roll this into `client`.
* @param remoteDevice provides access to the Outline RemoteDevice.
* @throws IllegalArgumentException if |socksServerAddress| is null.
* @throws IllegalStateException if the VPN has not been established, or the tunnel is already
* connected.
*/
public synchronized PlatformError connectTunnel(final outline.Client client, boolean isUdpEnabled) {
public synchronized PlatformError connectTunnel(final outline.RemoteDevice remoteDevice) {
LOG.info("Connecting the tunnel.");
if (client == null) {
throw new IllegalArgumentException("Must provide an Outline client.");
if (remoteDevice == null) {
throw new IllegalArgumentException("Must provide an Outline RemoteDevice.");
}
if (tunFd == null) {
throw new IllegalStateException("Must establish the VPN before connecting the tunnel.");
Expand All @@ -143,7 +142,7 @@ public synchronized PlatformError connectTunnel(final outline.Client client, boo

LOG.fine("Starting tun2socks...");
final ConnectOutlineTunnelResult result =
Tun2socks.connectOutlineTunnel(tunFd.getFd(), client, isUdpEnabled);
Tun2socks.connectOutlineTunnel(tunFd.getFd(), remoteDevice);
if (result.getError() != null) {
return result.getError();
}
Expand All @@ -166,11 +165,11 @@ public synchronized void disconnectTunnel() {
*
* @return boolean indicating whether UDP is supported.
*/
public synchronized boolean updateUDPSupport() {
public synchronized boolean notifyNetworkChange() {
if (!isTunnelConnected()) {
return false;
}
return tunnel.updateUDPSupport();
return this.remoteDevice.notifyNetworkChange();
}

private boolean isTunnelConnected() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,25 +197,24 @@ private synchronized PlatformError startTunnel(
}
}

final NewClientResult clientResult = Outline.newClient(config.transportConfig);
if (clientResult.getError() != null) {
LOG.log(Level.WARNING, "Failed to create Outline Client", clientResult.getError());
final NewRemoteDeviceResult deviceResult = Outline.newRemoteDevice(config.transportConfig);
if (deviceResult.getError() != null) {
LOG.log(Level.WARNING, "Failed to create Outline remote IP device", deviceResult.getError());
tearDownActiveTunnel();
return clientResult.getError();
return deviceResult.getError();
}
final outline.Client client = clientResult.getClient();
final outline.RemoteDevice remoteDevice = deviceResult.getRemoteDevice();

PlatformError udpConnError = null;
PlatformError deviceConnError = null;
if (!isAutoStart) {
try {
// Do not perform connectivity checks when connecting on startup. We should avoid failing
// the connection due to a network error, as network may not be ready.
final TCPAndUDPConnectivityResult connResult = checkServerConnectivity(client);
if (connResult.getTCPError() != null) {
deviceConnError = remoteDevice.HasConnectivity();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably TestConnectivity would be a better name. HasConnectivity sounds like it will return a bool instead of an Error.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good.

if (deviceConnError != null) {
tearDownActiveTunnel();
return connResult.getTCPError();
return deviceConnError;
}
udpConnError = connResult.getUDPError();
} catch (Exception e) {
tearDownActiveTunnel();
return new PlatformError(Platerrors.InternalError, "failed to check connectivity");
Expand All @@ -233,10 +232,8 @@ private synchronized PlatformError startTunnel(
startNetworkConnectivityMonitor();
}

final boolean remoteUdpForwardingEnabled =
isAutoStart ? tunnelStore.isUdpSupported() : udpConnError == null;
try {
final PlatformError tunError = vpnTunnel.connectTunnel(client, remoteUdpForwardingEnabled);
final PlatformError tunError = vpnTunnel.connectTunnel(remoteDevice);
if (tunError != null) {
LOG.log(Level.SEVERE, "Failed to connect the tunnel", tunError);
tearDownActiveTunnel();
Expand All @@ -249,7 +246,7 @@ private synchronized PlatformError startTunnel(
"failed to connect the tunnel");
}
startForegroundWithNotification(config.name);
storeActiveTunnel(config, remoteUdpForwardingEnabled);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is where we store the UDP connectivity state for resuming a connection. Do we need to store it somewhere in the new arch?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking we should check it again. I don't think it makes sense to save this, since we check on network changes.

storeActiveTunnel(config);
return null;
}

Expand Down Expand Up @@ -279,20 +276,13 @@ private void tearDownActiveTunnel() {
tunnelStore.setTunnelStatus(TunnelStatus.DISCONNECTED);
}

/* Helper method that stops the Outline client, tun2socks, and tears down the VPN. */
/* Helper method that stops the Outline remote device, and tears down the VPN. */
private void stopVpnTunnel() {
vpnTunnel.disconnectTunnel();
vpnTunnel.tearDownVpn();
}

// Connectivity

private TCPAndUDPConnectivityResult checkServerConnectivity(final outline.Client client) {
final TCPAndUDPConnectivityResult result = Outline.checkTCPAndUDPConnectivity(client);
LOG.info(String.format(Locale.ROOT, "Go connectivity check result: %s", result));
return result;
}

private class NetworkConnectivityMonitor extends ConnectivityManager.NetworkCallback {
private final ConnectivityManager connectivityManager;

Expand Down Expand Up @@ -320,10 +310,7 @@ public void onAvailable(Network network) {
// `getActiveNetworkInfo` have been observed to return the underlying network set by us.
setUnderlyingNetworks(new Network[] {network});
}
boolean isUdpSupported = vpnTunnel.updateUDPSupport();
LOG.info(
String.format("UDP support: %s -> %s", tunnelStore.isUdpSupported(), isUdpSupported));
tunnelStore.setIsUdpSupported(isUdpSupported);
vpnTunnel.notifyNetworkChange();
}

@Override
Expand Down Expand Up @@ -416,7 +403,7 @@ private void startLastSuccessfulTunnel() {
}
}

private void storeActiveTunnel(final TunnelConfig config, boolean isUdpSupported) {
private void storeActiveTunnel(final TunnelConfig config) {
LOG.info("Storing active tunnel.");
JSONObject tunnel = new JSONObject();
try {
Expand All @@ -427,7 +414,6 @@ private void storeActiveTunnel(final TunnelConfig config, boolean isUdpSupported
LOG.log(Level.SEVERE, "Failed to store JSON tunnel data", e);
}
tunnelStore.setTunnelStatus(TunnelStatus.CONNECTED);
tunnelStore.setIsUdpSupported(isUdpSupported);
}

// Error reporting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,4 @@ public void setTunnelStatus(VpnTunnelService.TunnelStatus status) {
SharedPreferences.Editor editor = preferences.edit();
editor.putString(TUNNEL_STATUS_KEY, status.toString()).commit();
}

public void setIsUdpSupported(boolean isUdpSupported) {
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean(TUNNEL_SUPPORTS_UDP, isUdpSupported).commit();
}

public boolean isUdpSupported() {
return preferences.getBoolean(TUNNEL_SUPPORTS_UDP, false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ @interface PacketTunnelProvider ()<Tun2socksTunWriter>
@property (nonatomic) DDFileLogger *fileLogger;
@property (nonatomic, nullable) NSString *transportConfig;
@property (nonatomic) dispatch_queue_t packetQueue;
@property (nonatomic) BOOL isUdpSupported;
@end

@implementation PacketTunnelProvider
Expand Down Expand Up @@ -102,28 +101,25 @@ - (void)startTunnelWithOptions:(NSDictionary *)options
// network unusable with no indication to the user. By bypassing the checks, the network would
// still be unusable, but at least the user will have a visual indication that Outline is the
// culprit and can explicitly disconnect.
PlaterrorsPlatformError *udpConnectionError = nil;
PlaterrorsPlatformError *connectionError = nil;
if (!isOnDemand) {
OutlineNewClientResult* clientResult = [SwiftBridge newClientWithTransportConfig:self.transportConfig];
if (clientResult.error != nil) {
return startDone([SwiftBridge newOutlineErrorFromPlatformError:clientResult.error]);
OutlineNewRemoteDeviceResult* deviceResult = [SwiftBridge newRemoteDeviceWithTransportConfig:self.transportConfig];
if (deviceResult.error != nil) {
return startDone([SwiftBridge newOutlineErrorFromPlatformError:deviceResult.error]);
}
OutlineTCPAndUDPConnectivityResult *connResult = OutlineCheckTCPAndUDPConnectivity(clientResult.client);
DDLogDebug(@"Check connectivity result: tcpErr=%@, udpErr=%@", connResult.tcpError, connResult.udpError);
if (connResult.tcpError != nil) {
return startDone([SwiftBridge newOutlineErrorFromPlatformError:connResult.tcpError]);
connectionError = deviceResult.remoteDevice.HasConnectivity();
DDLogDebug(@"Check connectivity result: %@", connectionError);
if (connectionError != nil) {
return startDone([SwiftBridge newOutlineErrorFromPlatformError:connectionError]);
}
udpConnectionError = connResult.udpError;
}

[self startRouting:[SwiftBridge getTunnelNetworkSettings]
completion:^(NSError *_Nullable error) {
if (error != nil) {
return startDone([SwiftBridge newOutlineErrorFromNsError:error]);
}
BOOL isUdpSupported =
isOnDemand ? self.isUdpSupported : udpConnectionError == nil;
PlaterrorsPlatformError *tun2socksError = [self startTun2Socks:isUdpSupported];
PlaterrorsPlatformError *tun2socksError = [self startTun2Socks];
if (tun2socksError != nil) {
return startDone([SwiftBridge newOutlineErrorFromPlatformError:tun2socksError]);
}
Expand Down Expand Up @@ -204,11 +200,7 @@ - (void)handleNetworkChange:(NWPath *)newDefaultPath {
DDLogInfo(@"Network connectivity changed");
if (newDefaultPath.status == NWPathStatusSatisfied) {
DDLogInfo(@"Reconnecting tunnel.");
// Check whether UDP support has changed with the network.
BOOL isUdpSupported = [self.tunnel updateUDPSupport];
DDLogDebug(@"UDP support: %d -> %d", self.isUdpSupported, isUdpSupported);
self.isUdpSupported = isUdpSupported;
[self reconnectTunnel];
[self.remoteDevice notifyNetworkUpdate];
} else {
DDLogInfo(@"Clearing tunnel settings.");
[self startRouting:nil completion:^(NSError * _Nullable error) {
Expand Down Expand Up @@ -246,21 +238,6 @@ bool getIpAddressString(const struct sockaddr *sa, char *s, socklen_t maxbytes)

#pragma mark - tun2socks

/** Restarts tun2socks if |configChanged| or the host's IP address has changed in the network. */
- (void)reconnectTunnel {
if (!self.transportConfig || !self.tunnel) {
DDLogError(@"Failed to reconnect tunnel, missing tunnel configuration.");
return;
}
// Nothing changed. Connect the tunnel with the current settings.
[self startRouting:[SwiftBridge getTunnelNetworkSettings]
completion:^(NSError *_Nullable error) {
if (error != nil) {
[self cancelTunnelWithError:error];
}
}];
}

- (BOOL)close:(NSError *_Nullable *)error {
return YES;
}
Expand All @@ -285,18 +262,18 @@ - (void)processPackets {
}];
}

- (PlaterrorsPlatformError*)startTun2Socks:(BOOL)isUdpSupported {
- (PlaterrorsPlatformError*)startTun2Socks {
BOOL isRestart = self.tunnel != nil && self.tunnel.isConnected;
if (isRestart) {
[self.tunnel disconnect];
}
__weak PacketTunnelProvider *weakSelf = self;
OutlineNewClientResult* clientResult = [SwiftBridge newClientWithTransportConfig:self.transportConfig];
if (clientResult.error != nil) {
return clientResult.error;
OutlineNewRemoteDeviceResult* deviceResult = [SwiftBridge newRemoteDEviceWithTransportConfig:self.transportConfig];
if (deviceResult.error != nil) {
return deviceResult.error;
}
Tun2socksConnectOutlineTunnelResult *result =
Tun2socksConnectOutlineTunnel(weakSelf, clientResult.client, isUdpSupported);
Tun2socksConnectOutlineTunnel(weakSelf, deviceResult.remoteDevice);
if (result.error != nil) {
DDLogError(@"Failed to start tun2socks: %@", result.error);
return result.error;
Expand Down
Loading