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 Aug 15, 2017
2 parents 3439427 + 1efb3c5 commit 12bec9b
Show file tree
Hide file tree
Showing 352 changed files with 71,689 additions and 2,025 deletions.
10 changes: 10 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
language: go
sudo: required
go:
- 1.8.3
addons:
Expand All @@ -12,13 +13,22 @@ script:
- go test -race -v ./common
- go test -race -v ./common/osl
- go test -race -v ./common/protocol
- go test -race -v -run TestObfuscatedSessionTicket ./common/tls
# TODO: enable once known race condition is addressed
# also, see comment below
#- sudo -E env "PATH=$PATH" go test -race -v ./common/tun
- go test -race -v ./transferstats
- go test -race -v ./server
- go test -race -v ./server/psinet
- go test -race -v
- go test -v -covermode=count -coverprofile=common.coverprofile ./common
- go test -v -covermode=count -coverprofile=osl.coverprofile ./common/osl
- go test -v -covermode=count -coverprofile=protocol.coverprofile ./common/protocol
# TODO: fix and reenable test, which is failing in TravisCI environment:
# --- FAIL: TestTunneledTCPIPv4
# tun_test.go:226: startTestTCPClient failed: syscall.Connect failed: connection timed out
#
#- sudo -E env "PATH=$PATH" go test -v -covermode=count -coverprofile=tun.coverprofile ./common/tun
- go test -v -covermode=count -coverprofile=transferstats.coverprofile ./transferstats
- go test -v -covermode=count -coverprofile=server.coverprofile ./server
- go test -v -covermode=count -coverprofile=psinet.coverprofile ./server/psinet
Expand Down
2 changes: 1 addition & 1 deletion ConsoleClient/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ func main() {
return
}
// TODO: stream embedded server list data? also, the cast makes an unnecessary copy of a large buffer?
serverEntries, err := protocol.DecodeAndValidateServerEntryList(
serverEntries, err := protocol.DecodeServerEntryList(
string(serverEntryList),
common.GetCurrentTimestamp(),
protocol.SERVER_ENTRY_SOURCE_EMBEDDED)
Expand Down
106 changes: 86 additions & 20 deletions MobileLibrary/Android/PsiphonTunnel/PsiphonTunnel.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ public interface HostService {
private Thread mTun2SocksThread;
private AtomicBoolean mIsWaitingForNetworkConnectivity;

// mUsePacketTunnel specifies whether to use the packet
// tunnel instead of tun2socks; currently this is for
// testing only and is disabled.
private boolean mUsePacketTunnel = false;

// Only one PsiphonVpn instance may exist at a time, as the underlying
// go.psi.Psi and tun2socks implementations each contain global state.
private static PsiphonTunnel mPsiphonTunnel;
Expand Down Expand Up @@ -134,12 +139,17 @@ public Object clone() throws CloneNotSupportedException {
//----------------------------------------------------------------------------------------------

// To start, call in sequence: startRouting(), then startTunneling(). After startRouting()
// succeeds, the caller must call stop() to clean up.
// succeeds, the caller must call stop() to clean up. These functions should not be called
// concurrently. Do not call stop() while startRouting() or startTunneling() is in progress.

// Returns true when the VPN routing is established; returns false if the VPN could not
// be started due to lack of prepare or revoked permissions (called should re-prepare and
// try again); throws exception for other error conditions.
public synchronized boolean startRouting() throws Exception {

// Note: tun2socks is loaded even in mUsePacketTunnel mode,
// as disableUdpGwKeepalive will still be called.

// Load tun2socks library embedded in the aar
// If this method is called more than once with the same library name, the second and subsequent calls are ignored.
// http://docs.oracle.com/javase/7/docs/api/java/lang/Runtime.html#loadLibrary%28java.lang.String%29
Expand Down Expand Up @@ -210,14 +220,22 @@ private boolean startVpn() throws Exception {
// Workaround for https://code.google.com/p/android/issues/detail?id=61096
Locale.setDefault(new Locale("en"));

int mtu = VPN_INTERFACE_MTU;
String dnsResolver = mPrivateAddress.mRouter;

if (mUsePacketTunnel) {
mtu = (int)Psi.GetPacketTunnelMTU();
dnsResolver = Psi.GetPacketTunnelDNSResolverIPv4Address();
}

ParcelFileDescriptor tunFd =
((VpnService.Builder) mHostService.newVpnServiceBuilder())
.setSession(mHostService.getAppName())
.setMtu(VPN_INTERFACE_MTU)
.setMtu(mtu)
.addAddress(mPrivateAddress.mIpAddress, mPrivateAddress.mPrefixLength)
.addRoute("0.0.0.0", 0)
.addRoute(mPrivateAddress.mSubnet, mPrivateAddress.mPrefixLength)
.addDnsServer(mPrivateAddress.mRouter)
.addDnsServer(dnsResolver)
.establish();
if (tunFd == null) {
// As per http://developer.android.com/reference/android/net/VpnService.Builder.html#establish%28%29,
Expand Down Expand Up @@ -255,28 +273,36 @@ private void routeThroughTunnel() {
if (!mRoutingThroughTunnel.compareAndSet(false, true)) {
return;
}
ParcelFileDescriptor tunFd = mTunFd.getAndSet(null);
if (tunFd == null) {
return;

if (!mUsePacketTunnel) {
ParcelFileDescriptor tunFd = mTunFd.getAndSet(null);
if (tunFd == null) {
return;
}
String socksServerAddress = "127.0.0.1:" + Integer.toString(mLocalSocksProxyPort.get());
String udpgwServerAddress = "127.0.0.1:" + Integer.toString(UDPGW_SERVER_PORT);
startTun2Socks(
tunFd,
VPN_INTERFACE_MTU,
mPrivateAddress.mRouter,
VPN_INTERFACE_NETMASK,
socksServerAddress,
udpgwServerAddress,
true);
}
String socksServerAddress = "127.0.0.1:" + Integer.toString(mLocalSocksProxyPort.get());
String udpgwServerAddress = "127.0.0.1:" + Integer.toString(UDPGW_SERVER_PORT);
startTun2Socks(
tunFd,
VPN_INTERFACE_MTU,
mPrivateAddress.mRouter,
VPN_INTERFACE_NETMASK,
socksServerAddress,
udpgwServerAddress,
true);

mHostService.onDiagnosticMessage("routing through tunnel");

// TODO: should double-check tunnel routing; see:
// https://bitbucket.org/psiphon/psiphon-circumvention-system/src/1dc5e4257dca99790109f3bf374e8ab3a0ead4d7/Android/PsiphonAndroidLibrary/src/com/psiphon3/psiphonlibrary/TunnelCore.java?at=default#cl-779
}

private void stopVpn() {
stopTun2Socks();

if (!mUsePacketTunnel) {
stopTun2Socks();
}

ParcelFileDescriptor tunFd = mTunFd.getAndSet(null);
if (tunFd != null) {
try {
Expand Down Expand Up @@ -345,16 +371,50 @@ public String GetSecondaryDnsServer() {
private void startPsiphon(String embeddedServerEntries) throws Exception {
stopPsiphon();
mHostService.onDiagnosticMessage("starting Psiphon library");

// In packet tunnel mode, Psi.Start will dup the tun file descriptor
// passed in via the config. So here we "check out" mTunFd, to ensure
// it can't be closed before it's duplicated. (This would only happen
// if stop() is called concurrently with startTunneling(), which should
// not be done -- this could also cause file descriptor issues in
// tun2socks mode. With the "check out", a closed and recycled file
// descriptor will not be copied; but a different race condition takes
// the place of that one: stop() may fail to close the tun fd. So the
// prohibition on concurrent calls remains.)
//
// In tun2socks mode, the ownership of the fd is transferred to tun2socks.
// In packet tunnel mode, tunnel code dups the fd and manages that copy
// while PsiphonTunnel retains ownership of the original mTunFd copy. Both
// file descriptors must be closed to halt VpnService, and stop() does
// this.

ParcelFileDescriptor tunFd = null;
int fd = -1;
if (mUsePacketTunnel) {
tunFd = mTunFd.getAndSet(null);
if (tunFd != null) {
fd = tunFd.getFd();
}
}

try {
Psi.Start(
loadPsiphonConfig(mHostService.getContext()),
loadPsiphonConfig(mHostService.getContext(), fd),
embeddedServerEntries,
this,
isVpnMode(),
false /* Do not use IPv6 synthesizer for android */);
false // Do not use IPv6 synthesizer for android
);
} catch (java.lang.Exception e) {
throw new Exception("failed to start Psiphon library", e);
} finally {

if (mUsePacketTunnel) {
mTunFd.getAndSet(tunFd);
}

}

mHostService.onDiagnosticMessage("Psiphon library started");
}

Expand All @@ -364,7 +424,7 @@ private void stopPsiphon() {
mHostService.onDiagnosticMessage("Psiphon library stopped");
}

private String loadPsiphonConfig(Context context)
private String loadPsiphonConfig(Context context, int tunFd)
throws IOException, JSONException {

// Load settings from the raw resource JSON config file and
Expand Down Expand Up @@ -433,6 +493,12 @@ private String loadPsiphonConfig(Context context)

json.put("DeviceRegion", getDeviceRegion(mHostService.getContext()));

if (mUsePacketTunnel) {
json.put("PacketTunnelTunFileDescriptor", tunFd);
json.put("DisableLocalSocksProxy", true);
json.put("DisableLocalHTTPProxy", true);
}

return json.toString();
}

Expand Down
2 changes: 1 addition & 1 deletion MobileLibrary/Android/make.bash
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ BUILD_TAGS="OPENSSL ${PRIVATE_PLUGINS_TAG}"
# the latest versions. Outside of Docker, be aware that these dependencies
# will not be overridden w/ new versions if they already exist in $GOPATH

GOOS=arm go get -d -v -tags "${BUILD_TAGS}" github.com/Psiphon-Labs/psiphon-tunnel-core/MobileLibrary/psi
GOOS=android go get -d -v -tags "${BUILD_TAGS}" github.com/Psiphon-Labs/psiphon-tunnel-core/MobileLibrary/psi
if [ $? != 0 ]; then
echo "..'go get -d -v github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon' failed, exiting"
exit $?
Expand Down
53 changes: 37 additions & 16 deletions MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/PsiphonTunnel.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,18 +103,18 @@ typedef NS_ENUM(NSInteger, PsiphonConnectionState)
- `FetchRoutesTimeoutSeconds`
- `HttpProxyOriginServerTimeoutSeconds`
- Fields which should only be set by Psiphon proper:
- `TunnelWholeDevice`
- `LocalHttpProxyPort`
- `LocalSocksProxyPort`
- `TunnelWholeDevice`: For stats purposes, but must be accurate. Defaults to 0 (false).
@endcode
@note All other config fields must not be set.
See the tunnel-core config code for details about the fields.
https://github.com/Psiphon-Labs/psiphon-tunnel-core/blob/master/psiphon/config.go
@return JSON string with config that should used to run the Psiphon tunnel, or NULL on error.
Swift: @code func getPsiphonConfig() -> String? @endcode
*/
- (NSString * _Nullable)getPsiphonConfig;
Expand All @@ -138,7 +138,7 @@ typedef NS_ENUM(NSInteger, PsiphonConnectionState)
*/
- (void)onDiagnosticMessage:(NSString * _Nonnull)message;

/*!
/*!
Called when the tunnel is in the process of connecting.
Swift: @code func onConnecting() @endcode
*/
Expand Down Expand Up @@ -178,8 +178,8 @@ typedef NS_ENUM(NSInteger, PsiphonConnectionState)
/*!
Called when the device's Internet connection state is interrupted.
This may mean that it had connectivity and now doesn't, or went from Wi-Fi to
WWAN or vice versa.
@note For many/most apps, the response to this callback should be to restart
WWAN or vice versa.
@note For many/most apps, the response to this callback should be to restart
the Psiphon tunnel. It will eventually notice and begin reconnecting, but it
may take much longer, depending on attempts to use the tunnel.
Swift: @code func onDeviceInternetConnectivityInterrupted() @endcode
Expand Down Expand Up @@ -299,44 +299,65 @@ typedef NS_ENUM(NSInteger, PsiphonConnectionState)
Returns an instance of PsiphonTunnel. This is either a new instance or the pre-existing singleton. If an instance already exists, it will be stopped when this function is called.
@param tunneledAppDelegate The delegate implementation to use for callbacks.
@return The PsiphonTunnel instance.
Swift: @code open class func newPsiphonTunnel(_ tunneledAppDelegate: TunneledAppDelegate) -> Self @endcode
Swift: @code class func newPsiphonTunnel(_ tunneledAppDelegate: TunneledAppDelegate) -> Self @endcode
*/
+ (PsiphonTunnel * _Nonnull)newPsiphonTunnel:(id<TunneledAppDelegate> _Nonnull)tunneledAppDelegate;

/*!
Start connecting the PsiphonTunnel. Returns before connection is complete -- delegate callbacks (such as `onConnected` and `onConnectionStateChanged`) are used to indicate progress and state.
@param ifNeeded If TRUE, the tunnel will only be started if it's not already connected and healthy. If FALSE, the tunnel will be forced to stop and reconnect.
@return TRUE if the connection start was successful, FALSE otherwise.
Swift: @code open func start(_ ifNeeded: Bool) -> Bool @endcode
Swift: @code func start(_ ifNeeded: Bool) -> Bool @endcode
*/
- (BOOL)start:(BOOL)ifNeeded;

/*!
Stop the tunnel (regardless of its current connection state). Returns before full stop is complete -- `TunneledAppDelegate::onExiting` is called when complete.
Swift: @code open func stop() @endcode
Swift: @code func stop() @endcode
*/
- (void)stop;

/*!
Returns the current tunnel connection state.
@return The current connection state.
Swift: @code open func getConnectionState() -> PsiphonConnectionState @endcode
Swift: @code func getConnectionState() -> PsiphonConnectionState @endcode
*/
- (PsiphonConnectionState)getConnectionState;

/*!
Provides the port number of the local SOCKS proxy. Only valid when currently connected (will return 0 otherwise).
@return The current local SOCKS proxy port number.
Swift: @code open func getLocalSocksProxyPort() -> Int @endcode
Swift: @code func getLocalSocksProxyPort() -> Int @endcode
*/
-(NSInteger)getLocalSocksProxyPort;
- (NSInteger)getLocalSocksProxyPort;

/*!
Provides the port number of the local HTTP proxy. Only valid when currently connected (will return 0 otherwise).
@return The current local HTTP proxy port number.
Swift: @code open func getLocalHttpProxyPort() -> Int @endcode
Swift: @code func getLocalHttpProxyPort() -> Int @endcode
*/
-(NSInteger)getLocalHttpProxyPort;
- (NSInteger)getLocalHttpProxyPort;

/*!
Only valid in whole device mode. Provides the MTU the packet tunnel requires the device to use.
@return The MTU size.
Swift: @code func getPacketTunnelMTU() -> Int @endcode
*/
- (long)getPacketTunnelMTU;

/*!
Only valid in whole device mode. Provides the DNS resolver IP address that is provided by the packet tunnel to the device.
@return The IP address of the DNS resolver as a string.
Swift: @code func getPacketTunnelDNSResolverIPv4Address() -> String @endcode
*/
- (NSString * _Nonnull)getPacketTunnelDNSResolverIPv4Address;

/*!
Only valid in whole device mode. Provides the DNS resolver IP address that is provided by the packet tunnel to the device.
@return The IP address of the DNS resolver as a string.
Swift: @code func getPacketTunnelDNSResolverIPv6Address() -> String @endcode
*/
- (NSString * _Nonnull)getPacketTunnelDNSResolverIPv6Address;

/*!
Upload a feedback package to Psiphon Inc. The app collects feedback and diagnostics information in a particular format, then calls this function to upload it for later investigation.
Expand All @@ -345,11 +366,11 @@ typedef NS_ENUM(NSInteger, PsiphonConnectionState)
@param b64EncodedPublicKey The key that will be used to encrypt the payload before uploading.
@param uploadServer The server and path to which the data will be uploaded.
@param uploadServerHeaders The request headers that will be used when uploading.
Swift: @code open func sendFeedback(_ feedbackJson: String, publicKey b64EncodedPublicKey: String, uploadServer: String, uploadServerHeaders: String) @endcode
Swift: @code func sendFeedback(_ feedbackJson: String, publicKey b64EncodedPublicKey: String, uploadServer: String, uploadServerHeaders: String) @endcode
*/
- (void)sendFeedback:(NSString * _Nonnull)feedbackJson
publicKey:(NSString * _Nonnull)b64EncodedPublicKey
uploadServer:(NSString * _Nonnull)uploadServer
uploadServerHeaders:(NSString * _Nonnull)uploadServerHeaders;

@end
@end
Loading

0 comments on commit 12bec9b

Please sign in to comment.