Skip to content

Commit

Permalink
Merge branch 'master' into help-link-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
fortuna authored Aug 30, 2023
2 parents 6751ace + b068199 commit 0e4c782
Show file tree
Hide file tree
Showing 117 changed files with 2,446 additions and 471 deletions.
7 changes: 4 additions & 3 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ labels: 'bug'
assignees: ''

---
This issue tracker is for bug reports only. **If you want to request troubleshooting assistance with Outline Manager or Outline Client, please do not submit a bug here. Instead, please contact us using our [support website](https://support.getoutline.org/s/contactsupport).** Developers, please post your questions on the [SDK Discussion board](https://github.com/Jigsaw-Code/outline-sdk/discussions).

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
**To reproduce**
How can the broken behavior be reproduced?

**Expected behavior**
Expand All @@ -19,11 +20,11 @@ What behavior did you expect?
**Screenshots**
If applicable, add screenshots to help explain your problem.

**Client System (please complete the following information):**
**Client system (please complete the following information):**
- Outline Client Version [e.g. iOS 1.3.1]
- Your Operating System: [e.g. iOS 14.2, Windows 10]

**Submit Feedback**
**Submit feedback**
Please submit feedback through the app and label it with this issue number.

**Additional context**
Expand Down
8 changes: 5 additions & 3 deletions .github/ISSUE_TEMPLATE/question.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
---
name: Question
about: The issue tracker is not for questions. Please ask questions at [email protected].
about: Questions about Outline
title: ''
labels: ''
labels: 'question'
assignees: ''

---

The issue tracker is not for questions. Please ask questions at [email protected].
The issue tracker is not for questions.

If you would like support with Outline Manager or Outline Client, please review the help articles on [support.getoutline.org](https://support.getoutline.org/), and [contact us](https://support.getoutline.org/s/contactsupport) if you need further assistance. Developers, please post your questions on the SDK [Discussion board](https://github.com/Jigsaw-Code/outline-sdk/discussions).
11 changes: 11 additions & 0 deletions .github/ISSUE_TEMPLATE/support_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
name: Support request
about: Get help troubleshooting an issue
title: ''
labels: 'customer support'
assignees: ''

---
The issue tracker is not for support requests.

If you would like support with Outline Manager or Outline Client, please review the help articles on [support.getoutline.org](https://support.getoutline.org/), and [contact us](https://support.getoutline.org/s/contactsupport) if you need further assistance. Developers, please post your questions on the SDK [Discussion board](https://github.com/Jigsaw-Code/outline-sdk/discussions).
2 changes: 1 addition & 1 deletion config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<access origin="*" />
<allow-intent href="http://*/*" />
<allow-intent href="https://*/*" />
<preference name="android-minSdkVersion" value="22" />
<preference name="android-minSdkVersion" value="23" />
<preference name="android-targetSdkVersion" value="32" />
<preference name="AndroidLaunchMode" value="singleInstance" />
<preference name="ShowSplashScreenSpinner" value="false" />
Expand Down
23 changes: 23 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module github.com/Jigsaw-Code/outline-apps

go 1.20

require (
github.com/Jigsaw-Code/outline-sdk v0.0.2
github.com/Jigsaw-Code/outline-sdk/x v0.0.0-20230807220427-893de7fdc6b8
github.com/stretchr/testify v1.8.4
golang.org/x/sys v0.11.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/eycorsican/go-tun2socks v1.16.11 // indirect
github.com/miekg/dns v1.1.54 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/shadowsocks/go-shadowsocks2 v0.1.5 // indirect
golang.org/x/crypto v0.9.0 // indirect
golang.org/x/mod v0.10.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/tools v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
45 changes: 45 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
github.com/Jigsaw-Code/outline-sdk v0.0.2 h1:uCuyJMaWj57IYEG/Hdml8YMdk9chU60ZkSxJXBhyGHU=
github.com/Jigsaw-Code/outline-sdk v0.0.2/go.mod h1:hhlKz0+r9wSDFT8usvN8Zv/BFToCIFAUn1P2Qk8G2CM=
github.com/Jigsaw-Code/outline-sdk/x v0.0.0-20230807220427-893de7fdc6b8 h1:BxOHmmuppPM8K0DGUsfvajKF4PKfGxv9boNDhmbszFU=
github.com/Jigsaw-Code/outline-sdk/x v0.0.0-20230807220427-893de7fdc6b8/go.mod h1:tBqJXpVm+kym+EAUdwNodcFxy872FfjVErfj8Br+gs0=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/eycorsican/go-tun2socks v1.16.11 h1:+hJDNgisrYaGEqoSxhdikMgMJ4Ilfwm/IZDrWRrbaH8=
github.com/eycorsican/go-tun2socks v1.16.11/go.mod h1:wgB2BFT8ZaPKyKOQ/5dljMG/YIow+AIXyq4KBwJ5sGQ=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI=
github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
github.com/shadowsocks/go-shadowsocks2 v0.1.5 h1:PDSQv9y2S85Fl7VBeOMF9StzeXZyK1HakRm86CUbr28=
github.com/shadowsocks/go-shadowsocks2 v0.1.5/go.mod h1:AGGpIoek4HRno4xzyFiAtLHkOpcoznZEkAccaI/rplM=
github.com/songgao/water v0.0.0-20190725173103-fd331bda3f4b/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20191021144547-ec77196f6094/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
98 changes: 98 additions & 0 deletions outline/device/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright 2023 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 device

import (
"encoding/json"
"fmt"
"net"
"strconv"

"github.com/Jigsaw-Code/outline-sdk/transport/shadowsocks"
)

// An internal configuration data structure to be used by Outline transports.
type transportConfig struct {
RemoteAddress string // the remote server address of "host:port"
CryptoKey *shadowsocks.EncryptionKey
Prefix []byte
}

// The configuration interface between the Outline backend and Outline apps.
// Must match the ShadowsocksSessionConfig interface defined in Outline Client.
type configJSON struct {
Host string `json:"host"`
Port uint16 `json:"port"`
Password string `json:"password"`
Method string `json:"method"`
Prefix string `json:"prefix"`
}

// parseConfigFromJSON parses a transport configuration string in JSON format, and returns a corresponding
// TransportConfig. The JSON string `in` must match the ShadowsocksSessionConfig interface defined in Outline Client.
func parseConfigFromJSON(in string) (config *transportConfig, err error) {
var confJson configJSON
if err = json.Unmarshal([]byte(in), &confJson); err != nil {
return nil, err
}
if err = validateConfig(&confJson); err != nil {
return nil, fmt.Errorf("invalid configuration: %w", err)
}

config = &transportConfig{
RemoteAddress: net.JoinHostPort(confJson.Host, strconv.Itoa(int(confJson.Port))),
}
if config.CryptoKey, err = shadowsocks.NewEncryptionKey(confJson.Method, confJson.Password); err != nil {
return nil, fmt.Errorf("invalid cipher: %w", err)
}
if len(confJson.Prefix) > 0 {
if config.Prefix, err = parseStringPrefix(confJson.Prefix); err != nil {
return nil, fmt.Errorf("invalid configuration prefix: %w", err)
}
}

return config, nil
}

// validateConfig validates whether an Outline transport configuration is valid (it won't do any connectivity tests).
//
// Returns nil if it is valid; or an error if not.
func validateConfig(config *configJSON) error {
if len(config.Host) == 0 {
return fmt.Errorf("must provide a hostname or IP address")
}
if config.Port <= 0 || config.Port > 65535 {
return fmt.Errorf("port must be within range [1..65535]")
}
if len(config.Method) == 0 {
return fmt.Errorf("must provide an encryption cipher method")
}
if len(config.Password) == 0 {
return fmt.Errorf("must provide an encryption cipher password")
}
return nil
}

func parseStringPrefix(utf8Str string) ([]byte, error) {
runes := []rune(utf8Str)
rawBytes := make([]byte, len(runes))
for i, r := range runes {
if (r & 0xFF) != r {
return nil, fmt.Errorf("character out of range: %d", r)
}
rawBytes[i] = byte(r)
}
return rawBytes, nil
}
133 changes: 133 additions & 0 deletions outline/device/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Copyright 2023 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 device

import (
"testing"

"github.com/stretchr/testify/require"
)

func Test_ParseConfigFromJSON(t *testing.T) {
tests := []struct {
name string
input string
expectErr bool
expectAddress string
expectPrefix []byte
}{
{
name: "normal config",
input: `{"host":"192.0.2.1","port":12345,"method":"chacha20-ietf-poly1305","password":"abcd1234"}`,
expectAddress: "192.0.2.1:12345",
},
{
name: "normal config with prefix",
input: `{"host":"192.0.2.1","port":12345,"method":"aes-128-gcm","password":"abcd1234","prefix":"abc 123"}`,
expectAddress: "192.0.2.1:12345",
expectPrefix: []byte("abc 123"),
},
{
name: "normal config with extra fields",
input: `{"extra_field":"ignored","host":"192.0.2.1","port":12345,"method":"aes-192-gcm","password":"abcd1234"}`,
expectAddress: "192.0.2.1:12345",
},
{
name: "unprintable prefix",
input: `{"host":"192.0.2.1","port":12345,"method":"AES-256-gcm","password":"abcd1234","prefix":"abc 123","prefix":"\u0000\u0080\u00ff"}`,
expectAddress: "192.0.2.1:12345",
expectPrefix: []byte{0x00, 0x80, 0xff},
},
{
name: "multi-byte utf-8 prefix",
input: `{"host":"192.0.2.1","port":12345,"method":"chacha20-ietf-poly1305","password":"abcd1234","prefix":"abc 123","prefix":"` + "\xc2\x80\xc2\x81\xc3\xbd\xc3\xbf" + `"}`,
expectAddress: "192.0.2.1:12345",
expectPrefix: []byte{0x80, 0x81, 0xfd, 0xff},
},
{
name: "missing host",
input: `{"port":12345,"method":"AES-128-GCM","password":"abcd1234"}`,
expectErr: true,
},
{
name: "missing port",
input: `{"host":"192.0.2.1","method":"aes-192-gcm","password":"abcd1234"}`,
expectErr: true,
},
{
name: "missing method",
input: `{"host":"192.0.2.1","port":12345,"password":"abcd1234"}`,
expectErr: true,
},
{
name: "missing password",
input: `{"host":"192.0.2.1","port":12345,"method":"chacha20-ietf-poly1305"}`,
expectErr: true,
},
{
name: "empty host",
input: `{"host":"","port":12345,"method":"chacha20-ietf-poly1305","password":"abcd1234"}`,
expectErr: true,
},
{
name: "zero port",
input: `{"host":"192.0.2.1","port":0,"method":"chacha20-ietf-poly1305","password":"abcd1234"}`,
expectErr: true,
},
{
name: "empty method",
input: `{"host":"192.0.2.1","port":12345,"method":"","password":"abcd1234"}`,
expectErr: true,
},
{
name: "empty password",
input: `{"host":"192.0.2.1","port":12345,"method":"chacha20-ietf-poly1305","password":""}`,
expectErr: true,
},
{
name: "empty prefix",
input: `{"host":"192.0.2.1","port":12345,"method":"some-cipher","password":"abcd1234","prefix":""}`,
expectErr: true,
},
{
name: "port -1",
input: `{"host":"192.0.2.1","port":-1,"method":"aes-128-gcm","password":"abcd1234"}`,
expectErr: true,
},
{
name: "port 65536",
input: `{"host":"192.0.2.1","port":65536,"method":"aes-128-gcm","password":"abcd1234"}`,
expectErr: true,
},
{
name: "prefix out-of-range",
input: `{"host":"192.0.2.1","port":8080,"method":"aes-128-gcm","password":"abcd1234","prefix":"\x1234"}`,
expectErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parseConfigFromJSON(tt.input)
if tt.expectErr {
require.Error(t, err)
} else {
require.NoError(t, err)
require.Equal(t, tt.expectAddress, got.RemoteAddress)
require.NotNil(t, got.CryptoKey)
require.Equal(t, tt.expectPrefix, got.Prefix)
}
})
}
}
Loading

0 comments on commit 0e4c782

Please sign in to comment.