Skip to content

Commit

Permalink
feat(client/linux): revamp the Linux routing logic
Browse files Browse the repository at this point in the history
  • Loading branch information
jyyi1 committed Nov 22, 2024
1 parent 769fc25 commit 787e605
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 6 deletions.
2 changes: 1 addition & 1 deletion client/electron/go_plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {pathToBackendLibrary} from './app_paths';

let invokeGoAPIFunc: Function | undefined;

export type GoApiName = 'FetchResource';
export type GoApiName = 'FetchResource' | 'EstablishVPN';

/**
* Calls a Go function by invoking the `InvokeGoAPI` function in the native backend library.
Expand Down
21 changes: 16 additions & 5 deletions client/electron/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {GoVpnTunnel} from './go_vpn_tunnel';
import {installRoutingServices, RoutingDaemon} from './routing_service';
import {TunnelStore} from './tunnel_store';
import {VpnTunnel} from './vpn_tunnel';
import {closeVpn, establishVpn} from './vpn_service';

Check failure on line 42 in client/electron/index.ts

View workflow job for this annotation

GitHub Actions / Lint

`./vpn_service` import should occur before import of `./vpn_tunnel`
import * as config from '../src/www/app/outline_server_repository/config';
import {
StartRequestJson,
Expand All @@ -56,7 +57,7 @@ declare const APP_VERSION: string;
// Run-time environment variables:
const debugMode = process.env.OUTLINE_DEBUG === 'true';

const isLinux = os.platform() === 'linux';
const IS_LINUX = os.platform() === 'linux';

// Used for the auto-connect feature. There will be a tunnel in store
// if the user was connected at shutdown.
Expand Down Expand Up @@ -158,7 +159,7 @@ function setupWindow(): void {
//
// The ideal solution would be: either electron-builder supports the app icon; or we add
// dpi-aware features to this app.
if (isLinux) {
if (IS_LINUX) {
mainWindow.setIcon(
path.join(
app.getAppPath(),
Expand Down Expand Up @@ -251,7 +252,7 @@ function updateTray(status: TunnelStatus) {
{type: 'separator'} as MenuItemConstructorOptions,
{label: localizedStrings['quit'], click: quitApp},
];
if (isLinux) {
if (IS_LINUX) {
// Because the click event is never fired on Linux, we need an explicit open option.
menuTemplate = [
{
Expand Down Expand Up @@ -309,7 +310,7 @@ function interceptShadowsocksLink(argv: string[]) {
async function setupAutoLaunch(request: StartRequestJson): Promise<void> {
try {
await tunnelStore.save(request);
if (isLinux) {
if (IS_LINUX) {
if (process.env.APPIMAGE) {
const outlineAutoLauncher = new autoLaunch({
name: 'OutlineClient',
Expand All @@ -327,7 +328,7 @@ async function setupAutoLaunch(request: StartRequestJson): Promise<void> {

async function tearDownAutoLaunch() {
try {
if (isLinux) {
if (IS_LINUX) {
const outlineAutoLauncher = new autoLaunch({
name: 'OutlineClient',
});
Expand Down Expand Up @@ -368,6 +369,11 @@ async function createVpnTunnel(

// Invoked by both the start-proxying event handler and auto-connect.
async function startVpn(request: StartRequestJson, isAutoConnect: boolean) {
if (IS_LINUX) {
await establishVpn(request);
return;
}

if (currentTunnel) {
throw new Error('already connected');
}
Expand Down Expand Up @@ -401,6 +407,11 @@ async function startVpn(request: StartRequestJson, isAutoConnect: boolean) {

// Invoked by both the stop-proxying event and quit handler.
async function stopVpn() {
if (IS_LINUX) {
await closeVpn();
return;
}

if (!currentTunnel) {
return;
}
Expand Down
35 changes: 35 additions & 0 deletions client/electron/vpn_service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright 2024 The Outline Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import {TransportConfigJson} from '../src/www/app/outline_server_repository/config';
import {invokeGoApi} from './go_plugin';

Check failure on line 16 in client/electron/vpn_service.ts

View workflow job for this annotation

GitHub Actions / Lint

`./go_plugin` import should occur before import of `../src/www/app/outline_server_repository/config`

interface VpnConfig {
interfaceName: string;
ipAddress: string;
dnsServers: string[];
transport: string;
}

export async function establishVpn(transportConfig: TransportConfigJson) {
const config: VpnConfig = {
interfaceName: 'outline-tun0',
ipAddress: '10.0.85.2',
dnsServers: ['9.9.9.9'],
transport: JSON.stringify(transportConfig),
};
const connection = await invokeGoApi('EstablishVPN', JSON.stringify(config));

Check failure on line 32 in client/electron/vpn_service.ts

View workflow job for this annotation

GitHub Actions / Lint

'connection' is assigned a value but never used. Allowed unused vars must match /^_/u

Check failure on line 32 in client/electron/vpn_service.ts

View workflow job for this annotation

GitHub Actions / Lint

'connection' is assigned a value but never used. Allowed unused vars must match /^_/u
}

export async function closeVpn() {}
13 changes: 13 additions & 0 deletions client/go/outline/electron/go_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ const (
// - Input: the URL string of the resource to fetch
// - Output: the content in raw string of the fetched resource
FetchResourceAPI = "FetchResource"

// EstablishVPNAPI initiates a VPN connection and directs all network traffic through Outline.
//
// - Input: a JSON string of [VPNConfig].
// - Output: a JSON string of [VPNConnection].
EstablishVPNAPI = "EstablishVPN"
)

// InvokeGoAPI is the unified entry point for TypeScript to invoke various Go functions.
Expand All @@ -69,6 +75,13 @@ func InvokeGoAPI(api *C.char, input *C.char) C.InvokeGoAPIResult {
ErrorJson: marshalCGoErrorJson(platerrors.ToPlatformError(res.Error)),
}

case EstablishVPNAPI:
res, err := EstablishVPN(C.GoString(input))
return C.InvokeGoAPIResult{
Output: newCGoString(res),
ErrorJson: marshalCGoErrorJson(err),
}

default:
err := &platerrors.PlatformError{
Code: platerrors.InternalError,
Expand Down
65 changes: 65 additions & 0 deletions client/go/outline/electron/routing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 2024 The Outline Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"encoding/json"

"github.com/Jigsaw-Code/outline-apps/client/go/outline/platerrors"
)

type VPNConfig struct {
InterfaceName string `json:"interfaceName"`
IPAddress string `json:"ipAddress"`
DNSServers []string `json:"dnsServers"`
TransportConfig string `json:"transport"`
}

type VPNConnection struct {
RouteUDP bool `json:"routeUDP"`
}

func EstablishVPN(configStr string) (string, *platerrors.PlatformError) {
var config VPNConfig
err := json.Unmarshal([]byte(configStr), &config)
if err != nil {
return "", &platerrors.PlatformError{
Code: platerrors.IllegalConfig,
Message: "illegal VPN config format",
Cause: platerrors.ToPlatformError(err),
}
}

conn, perr := establishVPN(&config)
if perr != nil {
return "", perr
}

if conn == nil {
return "", &platerrors.PlatformError{
Code: platerrors.InternalError,
Message: "unexpected nil VPN connection",
}
}
connJson, err := json.Marshal(conn)
if err != nil {
return "", &platerrors.PlatformError{
Code: platerrors.InternalError,
Message: "failed to marshal VPN connection",
Cause: platerrors.ToPlatformError(err),
}
}
return string(connJson), nil
}
23 changes: 23 additions & 0 deletions client/go/outline/electron/routing_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright 2024 The Outline Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"github.com/Jigsaw-Code/outline-apps/client/go/outline/platerrors"
)

func establishVPN(config *VPNConfig) (*VPNConnection, *platerrors.PlatformError) {
return nil, platerrors.NewPlatformError(platerrors.InternalError, "Working in progress !!!")
}
24 changes: 24 additions & 0 deletions client/go/outline/electron/routing_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2024 The Outline Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import "github.com/Jigsaw-Code/outline-apps/client/go/outline/platerrors"

func establishVPN(config *VPNConfig) (*VPNConnection, *platerrors.PlatformError) {
return nil, &platerrors.PlatformError{
Code: platerrors.InternalError,
Message: "not implemented yet",
}
}

0 comments on commit 787e605

Please sign in to comment.