diff --git a/MobileLibrary/Android/PsiphonTunnel/PsiphonTunnel.java b/MobileLibrary/Android/PsiphonTunnel/PsiphonTunnel.java index 0ab7766a8..9c4115d54 100644 --- a/MobileLibrary/Android/PsiphonTunnel/PsiphonTunnel.java +++ b/MobileLibrary/Android/PsiphonTunnel/PsiphonTunnel.java @@ -131,6 +131,11 @@ default public void onActiveAuthorizationIDs(List<String> authorizations) {} default public void onTrafficRateLimits(long upstreamBytesPerSecond, long downstreamBytesPerSecond) {} default public void onApplicationParameters(Object parameters) {} default public void onServerAlert(String reason, String subject, List<String> actionURLs) {} + /** + * Called when tunnel-core reports connected server region information. + * @param region The server region received. + */ + default public void onConnectedServerRegion(String region) {} default public void onExiting() {} } @@ -1079,6 +1084,9 @@ private void handlePsiphonNotice(String noticeJSON) { enableUdpGwKeepalive(); } } + // Also report the tunnel's egress region to the host service + mHostService.onConnectedServerRegion( + notice.getJSONObject("data").getString("serverRegion")); } else if (noticeType.equals("ApplicationParameters")) { mHostService.onApplicationParameters( notice.getJSONObject("data").get("parameters")); diff --git a/MobileLibrary/Android/SampleApps/TunneledWebView/app/build.gradle b/MobileLibrary/Android/SampleApps/TunneledWebView/app/build.gradle index 62d433924..4cc11be52 100644 --- a/MobileLibrary/Android/SampleApps/TunneledWebView/app/build.gradle +++ b/MobileLibrary/Android/SampleApps/TunneledWebView/app/build.gradle @@ -35,4 +35,11 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.0.0' // always specify exact library version in your real project to avoid non-deterministic builds implementation 'ca.psiphon:psiphontunnel:2.+' + + // For the latest version compile the library from source, see MobileLibrary/Android/README.md + // in the Psiphon-Labs/psiphon-tunnel-core repository, copy the ca.psiphon.aar artifact to + // the libs folder under the app module and replace the above line + // (e.g. replace implementation 'ca.psiphon:psiphontunnel:2.+') + // with the following line: + // implementation files('libs/ca.psiphon.aar') } diff --git a/MobileLibrary/Android/SampleApps/TunneledWebView/app/src/main/java/ca/psiphon/tunneledwebview/MainActivity.java b/MobileLibrary/Android/SampleApps/TunneledWebView/app/src/main/java/ca/psiphon/tunneledwebview/MainActivity.java index 8acdfcee0..c65d7a8b7 100644 --- a/MobileLibrary/Android/SampleApps/TunneledWebView/app/src/main/java/ca/psiphon/tunneledwebview/MainActivity.java +++ b/MobileLibrary/Android/SampleApps/TunneledWebView/app/src/main/java/ca/psiphon/tunneledwebview/MainActivity.java @@ -8,6 +8,8 @@ Licensed under Creative Commons Zero (CC0). import android.content.Context; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; + +import android.util.Log; import android.webkit.WebSettings; import android.webkit.WebView; import android.widget.ArrayAdapter; @@ -61,6 +63,7 @@ Licensed under Creative Commons Zero (CC0). public class MainActivity extends AppCompatActivity implements PsiphonTunnel.HostService { + private static final String TAG = "TunneledWebView"; private ListView mListView; private WebView mWebView; @@ -152,6 +155,7 @@ private void logMessage(final String message) { public void run() { mLogMessages.add(message); mListView.setSelection(mLogMessages.getCount() - 1); + Log.d(TAG, "logMessage: " + message); } }); } @@ -249,6 +253,11 @@ public void onConnected() { loadWebView(); } + @Override + public void onConnectedServerRegion(String region) { + logMessage("connected server region: " + region); + } + @Override public void onHomepage(String url) { logMessage("home page: " + url); diff --git a/MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/PsiphonTunnel.h b/MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/PsiphonTunnel.h index 3ff66d45a..7aa5b1d4e 100644 --- a/MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/PsiphonTunnel.h +++ b/MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/PsiphonTunnel.h @@ -299,6 +299,12 @@ WWAN or vice versa or VPN state changed */ - (void)onApplicationParameters:(NSDictionary * _Nonnull)parameters; + +/*! + Called when tunnel-core reports connected server region information + @param region The server region received. + */ +- (void)onConnectedServerRegion:(NSString * _Nonnull)region; @end /*! diff --git a/MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/PsiphonTunnel.m b/MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/PsiphonTunnel.m index 1b50a806c..bb756fb5b 100644 --- a/MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/PsiphonTunnel.m +++ b/MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/PsiphonTunnel.m @@ -1174,6 +1174,18 @@ - (void)handlePsiphonNotice:(NSString * _Nonnull)noticeJSON { }); } } + else if ([noticeType isEqualToString:@"ActiveTunnel"]) { + id region = [notice valueForKeyPath:@"data.serverRegion"]; + if (![region isKindOfClass:[NSString class]]) { + [self logMessage:[NSString stringWithFormat: @"ActiveTunnel notice missing data.serverRegion: %@", noticeJSON]]; + return; + } + if ([self.tunneledAppDelegate respondsToSelector:@selector(onConnectedServerRegion:)]) { + dispatch_sync(self->callbackQueue, ^{ + [self.tunneledAppDelegate onConnectedServerRegion:region]; + }); + } + } else if ([noticeType isEqualToString:@"InternalError"]) { internalError = TRUE; } diff --git a/MobileLibrary/iOS/SampleApps/TunneledWebRequest/TunneledWebRequest/AppDelegate.swift b/MobileLibrary/iOS/SampleApps/TunneledWebRequest/TunneledWebRequest/AppDelegate.swift index ebc857d4f..aa907f7bd 100644 --- a/MobileLibrary/iOS/SampleApps/TunneledWebRequest/TunneledWebRequest/AppDelegate.swift +++ b/MobileLibrary/iOS/SampleApps/TunneledWebRequest/TunneledWebRequest/AppDelegate.swift @@ -365,4 +365,8 @@ extension AppDelegate: TunneledAppDelegate { self.httpProxyPort = port } } + + func onConnectedServerRegion(_ region: String) { + NSLog("onConnectedServerRegion(%@)", region) + } } diff --git a/psiphon/controller.go b/psiphon/controller.go index aa6ee302b..f2217cc3d 100755 --- a/psiphon/controller.go +++ b/psiphon/controller.go @@ -1001,7 +1001,8 @@ loop: NoticeActiveTunnel( connectedTunnel.dialParams.ServerEntry.GetDiagnosticID(), connectedTunnel.dialParams.TunnelProtocol, - connectedTunnel.dialParams.ServerEntry.SupportsSSHAPIRequests()) + connectedTunnel.dialParams.ServerEntry.SupportsSSHAPIRequests(), + connectedTunnel.dialParams.ServerEntry.Region) if isFirstTunnel { diff --git a/psiphon/notice.go b/psiphon/notice.go index 910fb5feb..b41a3edef 100644 --- a/psiphon/notice.go +++ b/psiphon/notice.go @@ -678,12 +678,13 @@ func NoticeRequestedTactics(dialParams *DialParameters) { } // NoticeActiveTunnel is a successful connection that is used as an active tunnel for port forwarding -func NoticeActiveTunnel(diagnosticID, protocol string, isTCS bool) { +func NoticeActiveTunnel(diagnosticID, protocol string, isTCS bool, serverRegion string) { singletonNoticeLogger.outputNotice( "ActiveTunnel", noticeIsDiagnostic, "diagnosticID", diagnosticID, "protocol", protocol, - "isTCS", isTCS) + "isTCS", isTCS, + "serverRegion", serverRegion) } // NoticeSocksProxyPortInUse is a failure to use the configured LocalSocksProxyPort diff --git a/psiphon/server/discovery.go b/psiphon/server/discovery.go index 96cdeb92e..b8e803f54 100644 --- a/psiphon/server/discovery.go +++ b/psiphon/server/discovery.go @@ -110,8 +110,6 @@ func (d *Discovery) reload(reloadedTactics bool) error { // Initialize and set underlying discovery component. Replaces old // component if discovery is already initialized. - oldDiscovery := d.discovery - discovery := discovery.MakeDiscovery( d.support.PsinetDatabase.GetDiscoveryServers(), discoveryStrategy) @@ -120,6 +118,7 @@ func (d *Discovery) reload(reloadedTactics bool) error { d.Lock() + oldDiscovery := d.discovery d.discovery = discovery d.currentStrategy = strategy @@ -143,6 +142,8 @@ func (d *Discovery) reload(reloadedTactics bool) error { // Stop stops discovery and cleans up underlying resources. func (d *Discovery) Stop() { + d.Lock() + defer d.Unlock() d.discovery.Stop() } diff --git a/psiphon/server/discovery/discovery.go b/psiphon/server/discovery/discovery.go index 608dc356b..28b705893 100644 --- a/psiphon/server/discovery/discovery.go +++ b/psiphon/server/discovery/discovery.go @@ -167,7 +167,8 @@ func (d *Discovery) Start() { // Note: servers with a discovery date range in the past are not // removed from d.all in case the wall clock has drifted; // otherwise, we risk removing them prematurely. - servers, nextUpdate := discoverableServers(d.all, d.clk) + var servers []*psinet.DiscoveryServer + servers, nextUpdate = discoverableServers(d.all, d.clk) // Update the set of discoverable servers. d.strategy.serversChanged(servers) diff --git a/psiphon/server/discovery/discovery_test.go b/psiphon/server/discovery/discovery_test.go index 3cb3bdb1a..2f293e7ef 100644 --- a/psiphon/server/discovery/discovery_test.go +++ b/psiphon/server/discovery/discovery_test.go @@ -149,9 +149,9 @@ func runDiscoveryTest(tt *discoveryTest, now time.Time) error { discovery.Start() for _, check := range tt.checks { - time.Sleep(1 * time.Second) // let async code complete + time.Sleep(10 * time.Millisecond) // let async code complete clk.SetNow(check.t) - time.Sleep(1 * time.Second) // let async code complete + time.Sleep(10 * time.Millisecond) // let async code complete discovered := discovery.SelectServers(net.IP{}) discoveredIPs := make([]string, len(discovered)) for i := range discovered {