Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Propagating a Proxy Config [JSON] String from browser down to go-tun2socks layer #1605

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

bi-kh
Copy link
Contributor

@bi-kh bi-kh commented Mar 23, 2023

The config string is mostly opaque (except host retrieval in iOS) and passed along the layers unchanged.

Note: In iOS, We no longer update the config's host with the resolved IP address and pass along the original hostname, if not an IP already, to tun2socks layer so the config string effectively remains unchanged all the way.

…socks layer

The config string is mostly opaque (except host retrieval in iOS) and passed along the layers unchanged.

Note: In iOS, We no longer update the config's host with the resolved IP address and pass along the original hostname, if not an IP already, to tun2socks layer so the config string effectively remains unchanged all the way.
@bi-kh bi-kh requested review from a team as code owners March 23, 2023 02:10
* @throws IllegalArgumentException if `tunnelId` or `config` are null.
* @throws JSONException if parsing `config` fails.
* @return populated TunnelConfig
*/
public static TunnelConfig makeTunnelConfig(final String tunnelId, final JSONObject config)
public static TunnelConfig makeTunnelConfig(final String tunnelId, final String configString)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Revert to the JSONObject. You'll extract the proxy string from a field in the JSON.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See CordovaTunnel in cordova_main.ts
start receives a ShadowsocksSessionConfig, right? And now I pass the opaque string version rather than its original JSON format.
Do we intend to introduce a new container config which wraps this proxy config [string] and pass that from Cordova layer?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I believe we can get rid of the IS_REACHABLE call too. It's not really useful. We use it to determine the status of the Tunnel, but we can do that in a different way. It makes sense to have a separate PR to just remove IsReachable first.

public static func decode(_ jsonData: Data) -> OutlineTunnel? {
return try? JSONDecoder().decode(OutlineTunnel.self, from: jsonData)
// Private helper to retrieve the host from the config string.
private func configToDictionary() -> [String: Any]? {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This means the config is not opaque to the platform-specific code, defeating the purpose of wiring the config.

What is the host used for besides the reachability test?

We may need to think about what we will show in the case of dynamic keys and what abstractions we need to support that. Perhaps we need to give different configs different ids. Or have the Client have a method to return the information to show.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fair point. I had an earlier version passing 'host' explicitly, for the reachability purpose, along with proxyString, i.e., the Cordova command arguments like (tunnelId, proxyString, host). Does that make sense?

Copy link
Collaborator

Choose a reason for hiding this comment

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

The action item here is to first understand what the host is being used for. We've talked about the Is Reachable: we should get rid of that. What else?

@fortuna fortuna requested a review from jyyi1 April 14, 2023 15:39
@fortuna
Copy link
Collaborator

fortuna commented Apr 14, 2023

FYI, we've finally merged the change to support config-based clients. See Jigsaw-Code/outline-go-tun2socks#117

That means some of the calls will need to change.

* @throws IllegalArgumentException if `tunnelId` or `config` are null.
* @throws JSONException if parsing `config` fails.
* @return populated TunnelConfig
*/
public static TunnelConfig makeTunnelConfig(final String tunnelId, final JSONObject config)
public static TunnelConfig makeTunnelConfig(final String tunnelId, final String configString)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's qualify all mentions to "config", since it's ambiguous.

Suggested change
public static TunnelConfig makeTunnelConfig(final String tunnelId, final String configString)
public static TunnelConfig makeTunnelConfig(final String tunnelId, final String proxyConfigString)

@@ -251,7 +224,7 @@ private ErrorCode startTunnel(final TunnelConfig config) {
private synchronized ErrorCode startTunnel(
final TunnelConfig config, boolean isAutoStart) {
LOG.info(String.format(Locale.ROOT, "Starting tunnel %s.", config.id));
if (config.id == null || config.proxy == null) {
if (config.id == null || config.proxyString == null) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if (config.id == null || config.proxyString == null) {
if (tunnelConfig.id == null || tunnelConfig.proxyConfigString == null) {

String serverName = config.name;
if (serverName == null || serverName.equals("")) {
serverName = config.proxy.host;
JSONObject object = (JSONObject) new JSONTokener(config.proxyString).nextValue();
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should treat the proxy config as opaque. Move the name back to the TunnelConfig.

@@ -621,17 +573,23 @@ public int getResourceId(final String name, final String type) {
return getResources().getIdentifier(name, type, getPackageName());
}

/* Returns the server's name from |serverConfig|. If the name is not present, it falls back to the
/* Returns the server's name from |proxyString|. If the name is not present, it falls back to the
* host name (IP address), or the application name if neither can be retrieved. */
private String getServerName(final TunnelConfig config) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Qualify the config.

Suggested change
private String getServerName(final TunnelConfig config) {
private String getServerName(final TunnelConfig tunnelConfig) {

return ["host": host ?? "", "port": port ?? "", "password": password ?? "",
"method": method ?? "", "prefix": prefixStr]
}
public var configString: String?
Copy link
Collaborator

Choose a reason for hiding this comment

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

Qualify the config:

Suggested change
public var configString: String?
public var proxyConfigString: String?

@@ -41,26 +26,39 @@ public class OutlineTunnel: NSObject, Codable {
case reconnecting = 2
}

public convenience init(id: String, config: [String: Any]) {
public convenience init(id: String?, configString: String) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
public convenience init(id: String?, configString: String) {
public convenience init(id: String?, proxyConfigString: String) {

final JSONObject config = args.getJSONObject(1);
int errorCode = startVpnTunnel(tunnelId, config);
final String configString = args.getString(1);
int errorCode = startVpnTunnel(tunnelId, configString);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Qualify the config

@@ -259,11 +259,11 @@ public void onActivityResult(int request, int result, Intent data) {
startVpnRequest = null;
}

private int startVpnTunnel(final String tunnelId, final JSONObject config) throws Exception {
private int startVpnTunnel(final String tunnelId, final String configString) throws Exception {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Qualify the config.

return sendError("Invalid configuration", callbackId: command.callbackId,
errorCode: OutlineVpn.ErrorCode.illegalServerConfiguration)
}
let tunnel = OutlineTunnel(id: tunnelId, config: config)
let tunnel = OutlineTunnel(id: tunnelId, configString: configString)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Qualify the config.

…owser layer

- revises Cordova 'start' command arguments# to 1 by wrapping (id, host, proxyConfigstring) in TunnelConfig. This will simplify future updates.
- propagates 'host' explicitly to keep proxyConfigString opaque
- simplifies some method arguments
- revises OutlineTunnel.swift

Note: not tested yet
Copy link
Collaborator

@fortuna fortuna left a comment

Choose a reason for hiding this comment

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

FYI, outline-go-tun2socks is not ready yet. We are working on a fix, then we'll release the library to use in outline-client.

*/
func start(_ command: CDVInvokedUrlCommand) {
guard let tunnelId = command.argument(at: 0) as? String else {
return sendError("Missing tunnel ID", callbackId: command.callbackId,
guard let tunnelCnfig = command.argument(at: 0) as? [String: Any] else {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Cnfig -> Config

* @throws IllegalArgumentException if `tunnelId` or `config` are null.
* @throws JSONException if parsing `config` fails.
* @return populated TunnelConfig
*/
public static TunnelConfig makeTunnelConfig(final String tunnelId, final JSONObject config)
public static TunnelConfig makeTunnelConfig(final String tunnelId, final String configString)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I believe we can get rid of the IS_REACHABLE call too. It's not really useful. We use it to determine the status of the Tunnel, but we can do that in a different way. It makes sense to have a separate PR to just remove IsReachable first.

public static func decode(_ jsonData: Data) -> OutlineTunnel? {
return try? JSONDecoder().decode(OutlineTunnel.self, from: jsonData)
// Private helper to retrieve the host from the config string.
private func configToDictionary() -> [String: Any]? {
Copy link
Collaborator

Choose a reason for hiding this comment

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

The action item here is to first understand what the host is being used for. We've talked about the Is Reachable: we should get rid of that. What else?

…ser layer (iOS)

- Refactors app/VpnExtension layers 'start' args to a 'Data" encoded 'OutlineTunnel' format instead of individual arg for each property
- Compiled/tested iOS

TODO:
- Compile/test Android
- Update the app/tun2socks interface from the initial ProxyClient to the merged tun2socks
*
* @param client provides access to the Shadowsocks proxy.
* @param client provides access to the proxy.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
* @param client provides access to the proxy.
* @param client provides access to the proxy.

Comment on lines 227 to 228
private synchronized ErrorCode startTunnel(
final TunnelConfig config, boolean isAutoStart) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd like to reduce the dependency on the TunnelConfig and be more explicit about what's needed here:

Suggested change
private synchronized ErrorCode startTunnel(
final TunnelConfig config, boolean isAutoStart) {
private synchronized ErrorCode startTunnel(
String id, String proxyConfigString, boolean isAutoStart) {

In fact, can we get rid of TunnelConfig and makeTunnelConfig for now?

@@ -259,13 +258,14 @@ public void onActivityResult(int request, int result, Intent data) {
startVpnRequest = null;
}

private int startVpnTunnel(final String tunnelId, final JSONObject config) throws Exception {
LOG.info(String.format(Locale.ROOT, "Starting VPN tunnel %s", tunnelId));
private int startVpnTunnel(final JSONObject jsonTunnelConfig) throws Exception {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's revert to the more explicit interface we had before.
I'd rather not have the TunnelConfig for now, as we don't configure anything else other than the proxy.
Also, I'm not sure the id belongs in a tunnel config if it's not used to configure the VPN.

@@ -621,19 +575,9 @@ public int getResourceId(final String name, final String type) {
return getResources().getIdentifier(name, type, getPackageName());
}

/* Returns the server's name from |serverConfig|. If the name is not present, it falls back to the
* host name (IP address), or the application name if neither can be retrieved. */
/* Returns the server's host name (IP address). */
private String getServerName(final TunnelConfig config) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

It looks like this is only used to build the foreground service notification.

We should be more explicit about that. If we need a name to identify the service we are using, we should pass that down instead of a host address. The name is nice because it's protocol-agnostic. Some strategies may need multiple IP addresses, which doesn't fit the host address model.

It seems we need:

  • id (why?)
  • service name (for system VPN UI)
  • proxy config (for setting up the proxy)

parcelable TunnelConfig {
String id;
String name;
ShadowsocksConfig proxy;
String host;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Revert. We need a service name, not a host address.
Perhaps we should rename the field to serviceName.

@@ -14,10 +14,8 @@

package org.outline;

import org.outline.shadowsocks.ShadowsocksConfig;

parcelable TunnelConfig {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm, not convinced we need this type, versus passing the values that are needed explicitly in the different points. The extra type adds an intermediate step that sometimes hurts readability, especially since not all fields are needed everywhere this type is being used.

…ndroid)

- Adds 'serverName' to the browser layer TunnelConfig and fills it with 'host' for now
- Renames Android TunnelConfig's 'host' to 'serverName'
- Tested

TODO:
- Update the app/tun2socks interface from the initial ProxyClient to the merged tun2socks (iOS & Android)
- Wire the actual 'serverName' to the browser's TunnelConfig
…ndroid)

- Wired the server name to the browser-side TunnelConfig propagated to the native-side 'start'. Verified that Android makeTunnelConfig retrieves it properly.

TODO:
- Update the app/tun2socks interface from the initial ProxyClient to the merged tun2socks (iOS & Android)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants