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

feat: add Backend that uses outline-sdk to replace tun2socks #485

Closed
wants to merge 9 commits into from

Conversation

jyyi1
Copy link
Contributor

@jyyi1 jyyi1 commented Sep 1, 2023

Actions

@jyyi1 jyyi1 requested a review from fortuna September 5, 2023 23:01
Copy link
Contributor

@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.

It's hard to review this code without the corresponding Android change, and the build code.
It's not clear what is actually consumed by Android.

I need to clearly see what the interface between Android and Go is. It seems like:

  • Tun2socks.connectIntraTunnel
  • Tun2socks.newDoHTransport

What else? Can you put the entire interface used somewhere we can discuss?

Let's move the backend code out of Android, so it's clear it could be used by iOS if we add support. But also to make the boundary clear.
We should rename it though. "Backend" is ambiguous.

Perhaps we put the Go code in some /go/internal root dir. The Go backend would be a specific package (or packages) in that directory. Perhaps we call it /go/internal/intrago or /go/internal/intraapi or /go/internal/gobackend. Let's discuss.

The doh code shouldn't be nested in the backend, since that's code that perhaps could be migrated to the SDK one day.


require (
github.com/Jigsaw-Code/choir v1.0.1
github.com/Jigsaw-Code/outline-go-tun2socks v0.0.0-20230815223204-dada2652ae2c
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need this?

"sync/atomic"
"time"

intraLegacy "github.com/Jigsaw-Code/outline-go-tun2socks/intra"
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's move the code we need to this repo instead of depending on the old code.

package tools

import (
_ "golang.org/x/mobile/bind"
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 technique is for binaries, not libraries.

You need the gomobile and gobind binaries. See my outline-sdk pr

Copy link
Contributor Author

Choose a reason for hiding this comment

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

gomobile still needs this package. Otherwise it will report missing package error.

Copy link
Contributor

Choose a reason for hiding this comment

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

The best approach I've found is to build both binaries locally to avoid that issue.
See https://github.com/Jigsaw-Code/outline-sdk/tree/fortuna-appproxy/x/examples/mobileproxy
Notice the tools.go file too.

Unfortunately gomobile calls gobind as a binary, instead of a library, so it must be on the PATH to be found. That's why the PATH trick there.

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's put IP-level stuff in its own package. Perhaps name it vpn?

Copy link
Contributor

Choose a reason for hiding this comment

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

Move to the vpn library

Android/backend/intra/device.go Show resolved Hide resolved

import "strings"

func parseFallbackAddrs(ipList string) []string {
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps move this to transport.go, since it's only used there?

Copy link
Contributor

@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.

Perhaps to minimize change, we just move everything we need from outline-go-tun2socks into /go/internal/..., then make the minimal changes needed to use the SDK network APIs.

We should have no dependencies on outline-go-tun2socks.

I suggest PR #1 is just to move the code here, and figure out the build.

Then PR #2 can actually make use the SDK network apis.

@jyyi1
Copy link
Contributor Author

jyyi1 commented Sep 6, 2023

@fortuna No I removed all old interfaces, and only expose types in backend/intra package. Just pushed the Java change to this repo. I didn't figure out a good way to call gomobile using gradle (there was an official plugin, but it was deprecated long time ago), so you still need to call gomobile in terminal (as mentioned in backend/README.md), and then build the Android project using gradle.

// For go-tun2socks
implementation project(":tun2socks")
// For Golang based backend
implementation project(":backend")
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we depend on the .aar file directly here if you are not automatically building it?

Suggested change
implementation project(":backend")
implementation files('backend.aar')

Copy link
Contributor

Choose a reason for hiding this comment

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

You can also define a task like this:

task generateGoMobileAAR(type: Exec) {
    workingDir '../path/to/go/package'
    commandLine 'gomobile', 'bind', '-o', '../path/to/android/app/libs/hello.aar', '-target=android', '.'
}

preBuild.dependsOn generateGoMobileAAR

Copy link
Contributor

Choose a reason for hiding this comment

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

It's really hard to follow with the code in two places.

## Build

```sh
gomobile clean
Copy link
Contributor

Choose a reason for hiding this comment

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

It's not clear how to set up go mobile.
We need to specify it in tools.go to make sure the version is fixed.
You shouldn't use the system binary.

package tools

import (
_ "golang.org/x/mobile/bind"
Copy link
Contributor

Choose a reason for hiding this comment

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

The best approach I've found is to build both binaries locally to avoid that issue.
See https://github.com/Jigsaw-Code/outline-sdk/tree/fortuna-appproxy/x/examples/mobileproxy
Notice the tools.go file too.

Unfortunately gomobile calls gobind as a binary, instead of a library, so it must be on the PATH to be found. That's why the PATH trick there.


```sh
gomobile clean
gomobile bind -target=android -o ./Android/backend/backend.aar ./Android/backend/intra
Copy link
Contributor

Choose a reason for hiding this comment

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

I see the api is all in one package. Nice.

Can you move it so that there aren't things nested in it?
I was imagining something like

  • go/
    • backend/ (or some name that is clearer)
    • internal/
      • doh/
      • sni/
      • vpn/ (we should move the vpn stuff out of doh)

// The returned os.File holds a separate reference to the underlying file,
// so the file will not be closed until both `fd` and the os.File are
// separately closed. (UNIX only.)
func NewTunFromFile(fd int) (IPDevice, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

NewDeviceFromFileDescriptor or NewDeviceFromFD?

Android/backend/intra/device.go Show resolved Hide resolved
Comment on lines +35 to +36
type IntraDevice struct {
t2s network.IPDevice
Copy link
Contributor

Choose a reason for hiding this comment

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

Since you are just extending the IPDevice, you can do this, so you don't need to specify all method wrappers:

Suggested change
type IntraDevice struct {
t2s network.IPDevice
type IntraDevice struct {
network.IPDevice


var _ DoHStreamDialer = (*dohSplitStreamDialer)(nil)

func MakeDoHStreamDialer(fakeDNS netip.AddrPort, dohServer DoHTransport, protector Protector, listener intraLegacy.TCPListener, sniReporter sni.TCPSNIReporter) (DoHStreamDialer, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This isn't a DoH StreamDialer. It's an IntraStreamDialer. It does a lot more than DoH. We need to rename it.

"github.com/Jigsaw-Code/outline-go-tun2socks/intra/protect"
)

type DoHTransport = dohLegacy.Transport
Copy link
Contributor

Choose a reason for hiding this comment

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

These are really hurting readability. We should move all the code here.

Copy link
Contributor

@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.

There are two main issues with this PR:

  • Duplication of definitions
  • Not clear what's original, and what's changing

It makes it really hard to review because you are moving some code, but not all, and changing it at the same time.

To make this more understandable, can you split the PR, so the move and the change are separate and clear? You can move then change, or change then move.

The move shouldn't change code. Moving to Intra first would give us the benefit of being able to build everything in one repo, though it seems we would still depend on some dependencies shared with Outline in outline-go-tun2socks, which is bad.

Perhaps it's best to change in outline-go-tun2socks first, removing all dependencies on legacy code, then move to Intra. The code in Intra should no longer depend on outline-go-tun2socks, and there shouldn't be duplication of definitions in the new code.

try {
// Protection isn't needed for Lollipop+, or if the VPN is not active.
Protector protector = VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP ? null :
SocketProtector protector = VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP ? null :
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's get rid of the protector, it complicates things and is no longer needed.

I took a look in the Play Store, and only 0.2% of the installed audience is below Lollipop (Android 5): https://docs.google.com/spreadsheets/d/15zsfgMhJ7TsLumE6S5ZykETwMBvrBptFllX0Z02gFPw/edit?usp=sharing&resourcekey=0-inA8jaG0XS58zXNXU9YOzQ

The global stats say 0.66%: https://apilevels.com/

It's not worth the trouble.

Let's make that change separately though. Change, then move.

Copy link
Contributor

Choose a reason for hiding this comment

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

As we remove the protector, we should also update the minSdkVersion to 21 (Lollipop), to make sure older phones don't get the new version that will break.

@intra882
Copy link

intra882 commented Sep 8, 2023 via email

Android/backend/build.gradle Outdated Show resolved Hide resolved
Android/backend/build.gradle Outdated Show resolved Hide resolved
workingDir goBuildDir
environment 'ANDROID_HOME', android.sdkDirectory
environment 'PATH', goBuildDir.getPath() + System.getProperty("path.separator") + System.getenv("PATH")
commandLine "${goBuildDir}/gomobile", 'bind', '-target=android', '-o', outputAAR, "${rootDir}/backend/intra"
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be nice to specify the -androidapi here.

jyyi1 and others added 2 commits September 15, 2023 19:24
@jyyi1
Copy link
Contributor Author

jyyi1 commented Feb 29, 2024

This has been partially implemented in a bunch of other PRs, such as #496, #501 . Since there has been already so many conflicts , it would be better to just create new PRs continuing the work instead of modifying this one.

@jyyi1 jyyi1 closed this Feb 29, 2024
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.

3 participants