Skip to content

Commit

Permalink
Merge branch 'master' into staging-client
Browse files Browse the repository at this point in the history
  • Loading branch information
rod-hynes committed May 22, 2024
2 parents 3612a5a + db032a5 commit 8fcc4b9
Show file tree
Hide file tree
Showing 46 changed files with 3,159 additions and 325 deletions.
8 changes: 8 additions & 0 deletions MobileLibrary/Android/PsiphonTunnel/PsiphonTunnel.java
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
}

Expand Down Expand Up @@ -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"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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')
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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);
}
});
}
Expand Down Expand Up @@ -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);
Expand Down
8 changes: 8 additions & 0 deletions MobileLibrary/go-mobile/cmd/gomobile/bind_iosapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,14 @@ func goAppleBind(gobind string, pkgs []*packages.Package, targets []targetInfo)
xcframeworkArgs := []string{"-create-xcframework"}

for _, dir := range frameworkDirs {
// On macOS, a temporary directory starts with /var, which is a symbolic link to /private/var.
// And in gomobile, a temporary directory is usually used as a working directly.
// Unfortunately, xcodebuild in Xcode 15 seems to have a bug and might not be able to understand fullpaths with symbolic links.
// As a workaround, resolve the path with symbolic links by filepath.EvalSymlinks.
dir, err := filepath.EvalSymlinks(dir)
if err != nil {
return err
}
xcframeworkArgs = append(xcframeworkArgs, "-framework", dir)
}

Expand Down
6 changes: 6 additions & 0 deletions MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/PsiphonTunnel.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

/*!
Expand Down
12 changes: 12 additions & 0 deletions MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/PsiphonTunnel.m
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,4 +365,8 @@ extension AppDelegate: TunneledAppDelegate {
self.httpProxyPort = port
}
}

func onConnectedServerRegion(_ region: String) {
NSLog("onConnectedServerRegion(%@)", region)
}
}
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,22 @@ $ ./ConsoleClient -config ./client.config
Use the local SOCKS proxy (port 1080) or HTTP proxy (port 8080) to tunnel traffic.


Using Psiphon with Go modules
--------------------------------------------------------------------------------

The github.com/Psiphon-Labs/psiphon-tunnel-core Go module may be imported into
other Go programs. Due to legacy release tags predating use of Go modules in
this repository, neither `go get ...@latest` nor `go get ...@tag` are
supported at this time. To use the psiphon-tunnel-core Go module and its
dependencies, reference a specific commit, or reference the `staging-client`
branch, which is the client-side, production-ready branch:

```
% go get github.com/Psiphon-Labs/psiphon-tunnel-core@staging-client
go: added github.com/Psiphon-Labs/psiphon-tunnel-core v1.0.11-0.20240424194431-3612a5a6fb4c
```


Acknowledgements
--------------------------------------------------------------------------------

Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ replace github.com/pion/dtls/v2 => github.com/mingyech/dtls/v2 v2.0.0
require (
github.com/Psiphon-Inc/rotate-safe-writer v0.0.0-20210303140923-464a7a37606e
github.com/Psiphon-Labs/bolt v0.0.0-20200624191537-23cedaef7ad7
github.com/Psiphon-Labs/consistent v0.0.0-20240322131436-20aaa4e05737
github.com/Psiphon-Labs/goptlib v0.0.0-20200406165125-c0e32a7a3464
github.com/Psiphon-Labs/psiphon-tls v0.0.0-20240424193802-52b2602ec60c
github.com/Psiphon-Labs/quic-go v0.0.0-20240424181006-45545f5e1536
github.com/armon/go-proxyproto v0.0.0-20180202201750-5b7edb60ff5f
github.com/bifurcation/mint v0.0.0-20180306135233-198357931e61
github.com/cespare/xxhash v1.1.0
github.com/cheekybits/genny v0.0.0-20170328200008-9127e812e1e9
github.com/cognusion/go-cache-lru v0.0.0-20170419142635-f73e2280ecea
github.com/deckarep/golang-set v0.0.0-20171013212420-1d4478f51bed
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ github.com/AndreasBriese/bbloom v0.0.0-20170702084017-28f7e881ca57/go.mod h1:bOv
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/Psiphon-Inc/rotate-safe-writer v0.0.0-20210303140923-464a7a37606e h1:NPfqIbzmijrl0VclX2t8eO5EPBhqe47LLGKpRrcVjXk=
github.com/Psiphon-Inc/rotate-safe-writer v0.0.0-20210303140923-464a7a37606e/go.mod h1:ZdY5pBfat/WVzw3eXbIf7N1nZN0XD5H5+X8ZMDWbCs4=
github.com/Psiphon-Labs/bolt v0.0.0-20200624191537-23cedaef7ad7 h1:Hx/NCZTnvoKZuIBwSmxE58KKoNLXIGG6hBJYN7pj9Ag=
github.com/Psiphon-Labs/bolt v0.0.0-20200624191537-23cedaef7ad7/go.mod h1:alTtZBo3j4AWFvUrAH6F5ZaHcTj4G5Y01nHz8dkU6vU=
github.com/Psiphon-Labs/consistent v0.0.0-20240322131436-20aaa4e05737 h1:QTMy7Uc2Xc7fz6O/Khy1xi0VBND13GqzLUE2mHw6HUU=
github.com/Psiphon-Labs/consistent v0.0.0-20240322131436-20aaa4e05737/go.mod h1:Enj/Gszv2zCbuRbHbabmNvfO9EM+5kmaGj8CyjwNPlY=
github.com/Psiphon-Labs/goptlib v0.0.0-20200406165125-c0e32a7a3464 h1:VmnMMMheFXwLV0noxYhbJbLmkV4iaVW3xNnj6xcCNHo=
github.com/Psiphon-Labs/goptlib v0.0.0-20200406165125-c0e32a7a3464/go.mod h1:Pe5BqN2DdIdChorAXl6bDaQd/wghpCleJfid2NoSli0=
github.com/Psiphon-Labs/psiphon-tls v0.0.0-20240305020009-09f917290799 h1:dHFQz6jeIr2RdtlioyGIdJw2UfKF7G+g7GYnQxhbgrk=
Expand All @@ -29,6 +33,8 @@ github.com/armon/go-proxyproto v0.0.0-20180202201750-5b7edb60ff5f h1:SaJ6yqg936T
github.com/armon/go-proxyproto v0.0.0-20180202201750-5b7edb60ff5f/go.mod h1:QmP9hvJ91BbJmGVGSbutW19IC0Q9phDCLGaomwTJbgU=
github.com/bifurcation/mint v0.0.0-20180306135233-198357931e61 h1:BU+NxuoaYPIvvp8NNkNlLr8aA0utGyuunf4Q3LJ0bh0=
github.com/bifurcation/mint v0.0.0-20180306135233-198357931e61/go.mod h1:zVt7zX3K/aDCk9Tj+VM7YymsX66ERvzCJzw8rFCX2JU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheekybits/genny v0.0.0-20170328200008-9127e812e1e9 h1:a1zrFsLFac2xoM6zG1u72DWJwZG3ayttYLfmLbxVETk=
Expand Down Expand Up @@ -201,6 +207,8 @@ github.com/sergeyfrolov/bsbuffer v0.0.0-20180903213811-94e85abb8507 h1:ML7ZNtcln
github.com/sergeyfrolov/bsbuffer v0.0.0-20180903213811-94e85abb8507/go.mod h1:DbI1gxrXI2jRGw7XGEUZQOOMd6PsnKzRrCKabvvMrwM=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
Expand Down
68 changes: 53 additions & 15 deletions psiphon/common/osl/osl.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ type Scheme struct {
// SeedSpecs is the set of different client network activity patterns
// that will result in issuing SLOKs. For a given time period, a distinct
// SLOK is issued for each SeedSpec.
// Duplicate subnets may appear in multiple SeedSpecs.
// Duplicate subnets and ASNs may appear in multiple SeedSpecs.
SeedSpecs []*SeedSpec

// SeedSpecThreshold is the threshold scheme for combining SLOKs to
Expand Down Expand Up @@ -135,7 +135,7 @@ type Scheme struct {
// SeedPeriodNanoseconds = 100,000,000 = 100 milliseconds
// SeedPeriodKeySplits = [{10, 7}, {60, 5}]
//
// In these scheme, up to 3 distinct SLOKs, one per spec, are issued
// In this scheme, up to 3 distinct SLOKs, one per spec, are issued
// every 100 milliseconds.
//
// Distinct OSLs are paved for every minute (60 seconds). Each OSL
Expand All @@ -156,15 +156,16 @@ type Scheme struct {
// SeedSpec defines a client traffic pattern that results in a seeded SLOK.
// For each time period, a unique SLOK is issued to a client that meets the
// traffic levels specified in Targets. All upstream port forward traffic to
// UpstreamSubnets is counted towards the targets.
// UpstreamSubnets and UpstreamASNs are counted towards the targets.
//
// ID is a SLOK key derivation component and must be 32 random bytes, base64
// encoded. UpstreamSubnets is a list of CIDRs. Description is not used; it's
// for JSON config file comments.
// encoded. UpstreamSubnets is a list of CIDRs. UpstreamASNs is a list of
// ASNs. Description is not used; it's for JSON config file comments.
type SeedSpec struct {
Description string
ID []byte
UpstreamSubnets []string
UpstreamASNs []string
Targets TrafficValues
}

Expand Down Expand Up @@ -213,7 +214,7 @@ type ClientSeedProgress struct {

// ClientSeedPortForward map a client port forward, which is relaying
// traffic to a specific upstream address, to all seed state progress
// counters for SeedSpecs with subnets containing the upstream address.
// counters for SeedSpecs with subnets and ASNs containing the upstream address.
// As traffic is relayed through the port forwards, the bytes transferred
// and duration count towards the progress of these SeedSpecs and
// associated SLOKs.
Expand Down Expand Up @@ -342,6 +343,16 @@ func LoadConfig(configJSON []byte) (*Config, error) {
}

scheme.subnetLookups[index] = subnetLookup

// Ensure there are no duplicates.
ASNs := make(map[string]struct{}, len(seedSpec.UpstreamASNs))
for _, ASN := range seedSpec.UpstreamASNs {
if _, ok := ASNs[ASN]; ok {
return nil, errors.Tracef("invalid upstream ASNs, duplicate ASN: %s", ASN)
} else {
ASNs[ASN] = struct{}{}
}
}
}

if !isValidShamirSplit(len(scheme.SeedSpecs), scheme.SeedSpecThreshold) {
Expand Down Expand Up @@ -450,13 +461,14 @@ func (state *ClientSeedState) Resume(
// NewClientSeedPortForward creates a new client port forward
// traffic progress tracker. Port forward progress reported to the
// ClientSeedPortForward is added to seed state progress for all
// seed specs containing upstreamIPAddress in their subnets.
// seed specs containing upstreamIPAddress in their subnets or ASNs.
// The return value will be nil when activity for upstreamIPAddress
// does not count towards any progress.
// NewClientSeedPortForward may be invoked concurrently by many
// psiphond port forward establishment goroutines.
func (state *ClientSeedState) NewClientSeedPortForward(
upstreamIPAddress net.IP) *ClientSeedPortForward {
upstreamIPAddress net.IP,
lookupASN func(net.IP) string) *ClientSeedPortForward {

// Concurrency: access to ClientSeedState is unsynchronized
// but references only read-only fields.
Expand All @@ -467,18 +479,46 @@ func (state *ClientSeedState) NewClientSeedPortForward(

var progressReferences []progressReference

// Determine which seed spec subnets contain upstreamIPAddress
// Determine which seed spec subnets and ASNs contain upstreamIPAddress
// and point to the progress for each. When progress is reported,
// it is added directly to all of these TrafficValues instances.
// Assumes state.progress entries correspond 1-to-1 with
// Assumes state.seedProgress entries correspond 1-to-1 with
// state.scheme.subnetLookups.
// Note: this implementation assumes a small number of schemes and
// seed specs. For larger numbers, instead of N SubnetLookups, create
// a single SubnetLookup which returns, for a given IP address, all
// matching subnets and associated seed specs.
for seedProgressIndex, seedProgress := range state.seedProgress {
for trafficProgressIndex, subnetLookup := range seedProgress.scheme.subnetLookups {
if subnetLookup.ContainsIPAddress(upstreamIPAddress) {

var upstreamASN string
var upstreamASNSet bool

for trafficProgressIndex, seedSpec := range seedProgress.scheme.SeedSpecs {

matchesSeedSpec := false

// First check for subnet match before performing more expensive
// check for ASN match.
subnetLookup := seedProgress.scheme.subnetLookups[trafficProgressIndex]
matchesSeedSpec = subnetLookup.ContainsIPAddress(upstreamIPAddress)

if !matchesSeedSpec && lookupASN != nil {
// No subnet match. Check for ASN match.
if len(seedSpec.UpstreamASNs) > 0 {
// Lookup ASN on demand and only once.
if !upstreamASNSet {
upstreamASN = lookupASN(upstreamIPAddress)
upstreamASNSet = true
}
// TODO: use a map for faster lookups when the number of
// string values to compare against exceeds a threshold
// where benchmarks show maps are faster than looping
// through a string slice.
matchesSeedSpec = common.Contains(seedSpec.UpstreamASNs, upstreamASN)
}
}

if matchesSeedSpec {
progressReferences = append(
progressReferences,
progressReference{
Expand Down Expand Up @@ -671,9 +711,7 @@ func (state *ClientSeedState) GetSeedPayload() *SeedPayload {
state.issueSLOKs()

sloks := make([]*SLOK, len(state.payloadSLOKs))
for index, slok := range state.payloadSLOKs {
sloks[index] = slok
}
copy(sloks, state.payloadSLOKs)

return &SeedPayload{
SLOKs: sloks,
Expand Down
Loading

0 comments on commit 8fcc4b9

Please sign in to comment.