Skip to content
This repository was archived by the owner on May 6, 2024. It is now read-only.

Commit a7f0bfb

Browse files
author
Benjamin M. Schwartz
authored
Merge pull request #98 from Jigsaw-Code/bemasc-prefix
Use the specified prefix for the connectivity checks
2 parents c36d16e + d0b2784 commit a7f0bfb

File tree

5 files changed

+93
-61
lines changed

5 files changed

+93
-61
lines changed

outline/android/tun2socks.go

+6-20
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,11 @@
1515
package tun2socks
1616

1717
import (
18-
"fmt"
19-
"math"
2018
"runtime/debug"
2119

2220
"github.com/Jigsaw-Code/outline-go-tun2socks/outline"
21+
"github.com/Jigsaw-Code/outline-go-tun2socks/outline/shadowsocks"
2322
"github.com/Jigsaw-Code/outline-go-tun2socks/tunnel"
24-
"github.com/Jigsaw-Code/outline-ss-server/client"
2523
"github.com/eycorsican/go-tun2socks/common/log"
2624
)
2725

@@ -40,32 +38,20 @@ type OutlineTunnel interface {
4038
// Returns an OutlineTunnel instance and does *not* take ownership of the TUN file descriptor; the
4139
// caller is responsible for closing after OutlineTunnel disconnects.
4240
//
43-
// `fd` is the TUN device. The OutlineTunnel acquires an additional reference to it, which
41+
// - `fd` is the TUN device. The OutlineTunnel acquires an additional reference to it, which
4442
// is released by OutlineTunnel.Disconnect(), so the caller must close `fd` _and_ call
4543
// Disconnect() in order to close the TUN device.
46-
// `host` is IP address of the Shadowsocks proxy server.
47-
// `port` is the port of the Shadowsocks proxy server.
48-
// `password` is the password of the Shadowsocks proxy.
49-
// `cipher` is the encryption cipher the Shadowsocks proxy.
50-
// `prefix` is the salt prefix to use for TCP connections (optional, use with care).
51-
// `isUDPEnabled` indicates whether the tunnel and/or network enable UDP proxying.
44+
// - `client` is the Shadowsocks client (created by [shadowsocks.NewClient]).
45+
// - `isUDPEnabled` indicates whether the tunnel and/or network enable UDP proxying.
5246
//
5347
// Throws an exception if the TUN file descriptor cannot be opened, or if the tunnel fails to
5448
// connect.
55-
func ConnectShadowsocksTunnel(fd int, host string, port int, password, cipher string, prefix []byte, isUDPEnabled bool) (OutlineTunnel, error) {
56-
if port <= 0 || port > math.MaxUint16 {
57-
return nil, fmt.Errorf("Invalid port number: %v", port)
58-
}
49+
func ConnectShadowsocksTunnel(fd int, client *shadowsocks.Client, isUDPEnabled bool) (OutlineTunnel, error) {
5950
tun, err := tunnel.MakeTunFile(fd)
6051
if err != nil {
6152
return nil, err
6253
}
63-
ssclient, err := client.NewClient(host, port, password, cipher)
64-
if err != nil {
65-
return nil, fmt.Errorf("failed to construct Shadowsocks client: %v", err)
66-
}
67-
ssclient.SetTCPSaltGenerator(client.NewPrefixSaltGenerator(prefix))
68-
t, err := outline.NewTunnel(ssclient, isUDPEnabled, tun)
54+
t, err := outline.NewTunnel(client, isUDPEnabled, tun)
6955
if err != nil {
7056
return nil, err
7157
}

outline/apple/tun2socks.go

+7-18
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,12 @@ package tun2socks
1616

1717
import (
1818
"errors"
19-
"fmt"
2019
"io"
21-
"math"
2220
"runtime/debug"
2321
"time"
2422

2523
"github.com/Jigsaw-Code/outline-go-tun2socks/outline"
26-
"github.com/Jigsaw-Code/outline-ss-server/client"
24+
"github.com/Jigsaw-Code/outline-go-tun2socks/outline/shadowsocks"
2725
)
2826

2927
// OutlineTunnel embeds the tun2socks.Tunnel interface so it gets exported by gobind.
@@ -54,24 +52,15 @@ func init() {
5452
// Returns an OutlineTunnel instance that should be used to input packets to the tunnel.
5553
//
5654
// `tunWriter` is used to output packets to the TUN (VPN).
57-
// `host` is IP address of the Shadowsocks proxy server.
58-
// `port` is the port of the Shadowsocks proxy server.
59-
// `password` is the password of the Shadowsocks proxy.
60-
// `cipher` is the encryption cipher the Shadowsocks proxy.
61-
// `prefix` is the salt prefix to use for TCP connections (optional, use with care).
55+
// `client` is the Shadowsocks client (created by [shadowsocks.NewClient]).
6256
// `isUDPEnabled` indicates whether the tunnel and/or network enable UDP proxying.
6357
//
6458
// Sets an error if the tunnel fails to connect.
65-
func ConnectShadowsocksTunnel(tunWriter TunWriter, host string, port int, password, cipher string, prefix []byte, isUDPEnabled bool) (OutlineTunnel, error) {
59+
func ConnectShadowsocksTunnel(tunWriter TunWriter, client *shadowsocks.Client, isUDPEnabled bool) (OutlineTunnel, error) {
6660
if tunWriter == nil {
67-
return nil, errors.New("Must provide a TunWriter")
68-
} else if port <= 0 || port > math.MaxUint16 {
69-
return nil, fmt.Errorf("Invalid port number: %v", port)
61+
return nil, errors.New("must provide a TunWriter")
62+
} else if client == nil {
63+
return nil, errors.New("must provide a client")
7064
}
71-
ssclient, err := client.NewClient(host, port, password, cipher)
72-
if err != nil {
73-
return nil, fmt.Errorf("failed to construct Shadowsocks client: %v", err)
74-
}
75-
ssclient.SetTCPSaltGenerator(client.NewPrefixSaltGenerator(prefix))
76-
return outline.NewTunnel(ssclient, isUDPEnabled, tunWriter)
65+
return outline.NewTunnel(client, isUDPEnabled, tunWriter)
7766
}

outline/electron/main.go

+29-17
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import (
1818
"flag"
1919
"fmt"
2020
"io"
21-
"net/url"
2221
"os"
2322
"os/signal"
2423
"strings"
@@ -27,7 +26,6 @@ import (
2726

2827
oss "github.com/Jigsaw-Code/outline-go-tun2socks/outline/shadowsocks"
2928
"github.com/Jigsaw-Code/outline-go-tun2socks/shadowsocks"
30-
"github.com/Jigsaw-Code/outline-ss-server/client"
3129
"github.com/eycorsican/go-tun2socks/common/log"
3230
_ "github.com/eycorsican/go-tun2socks/common/log/simple" // Register a simple logger.
3331
"github.com/eycorsican/go-tun2socks/core"
@@ -70,7 +68,7 @@ func main() {
7068
args.proxyPort = flag.Int("proxyPort", 0, "Shadowsocks proxy port number")
7169
args.proxyPassword = flag.String("proxyPassword", "", "Shadowsocks proxy password")
7270
args.proxyCipher = flag.String("proxyCipher", "chacha20-ietf-poly1305", "Shadowsocks proxy encryption cipher")
73-
args.proxyPrefix = flag.String("proxyPrefix", "", "Shadowsocks connection prefix, URI-encoded (unsafe)")
71+
args.proxyPrefix = flag.String("proxyPrefix", "", "Shadowsocks connection prefix, UTF8-encoded (unsafe)")
7472
args.logLevel = flag.String("logLevel", "info", "Logging level: debug|info|warn|error|none")
7573
args.dnsFallback = flag.Bool("dnsFallback", false, "Enable DNS fallback over TCP (overrides the UDP handler).")
7674
args.checkConnectivity = flag.Bool("checkConnectivity", false, "Check the proxy TCP and UDP connectivity and exit.")
@@ -100,8 +98,33 @@ func main() {
10098
os.Exit(oss.IllegalConfiguration)
10199
}
102100

101+
config := oss.Config{
102+
Host: *args.proxyHost,
103+
Port: *args.proxyPort,
104+
Password: *args.proxyPassword,
105+
CipherName: *args.proxyCipher,
106+
}
107+
108+
// The prefix is an 8-bit-clean byte sequence, stored in the codepoint
109+
// values of a unicode string, which arrives here encoded in UTF-8.
110+
prefixRunes := []rune(*args.proxyPrefix)
111+
config.Prefix = make([]byte, len(prefixRunes))
112+
for i, r := range prefixRunes {
113+
if (r & 0xFF) != r {
114+
log.Errorf("Character out of range: %r", r)
115+
os.Exit(oss.IllegalConfiguration)
116+
}
117+
config.Prefix[i] = byte(r)
118+
}
119+
120+
client, err := oss.NewClient(&config)
121+
if err != nil {
122+
log.Errorf("Failed to construct Shadowsocks client: %v", err)
123+
os.Exit(oss.IllegalConfiguration)
124+
}
125+
103126
if *args.checkConnectivity {
104-
connErrCode, err := oss.CheckConnectivity(*args.proxyHost, *args.proxyPort, *args.proxyPassword, *args.proxyCipher)
127+
connErrCode, err := oss.CheckConnectivity(client)
105128
log.Debugf("Connectivity checks error code: %v", connErrCode)
106129
if err != nil {
107130
log.Errorf("Failed to perform connectivity checks: %v", err)
@@ -119,25 +142,14 @@ func main() {
119142
// Output packets to TUN device
120143
core.RegisterOutputFn(tunDevice.Write)
121144

122-
ssclient, err := client.NewClient(*args.proxyHost, *args.proxyPort, *args.proxyPassword, *args.proxyCipher)
123-
if err != nil {
124-
log.Errorf("Failed to construct Shadowsocks client: %v", err)
125-
os.Exit(oss.IllegalConfiguration)
126-
}
127-
prefixBytes, err := url.PathUnescape(*args.proxyPrefix)
128-
if err != nil {
129-
log.Errorf("\"%s\" could not be URI-decoded", *args.proxyPrefix)
130-
os.Exit(oss.IllegalConfiguration)
131-
}
132-
ssclient.SetTCPSaltGenerator(client.NewPrefixSaltGenerator([]byte(prefixBytes)))
133145
// Register TCP and UDP connection handlers
134-
core.RegisterTCPConnHandler(shadowsocks.NewTCPHandler(ssclient))
146+
core.RegisterTCPConnHandler(shadowsocks.NewTCPHandler(client))
135147
if *args.dnsFallback {
136148
// UDP connectivity not supported, fall back to DNS over TCP.
137149
log.Debugf("Registering DNS fallback UDP handler")
138150
core.RegisterUDPConnHandler(dnsfallback.NewUDPHandler())
139151
} else {
140-
core.RegisterUDPConnHandler(shadowsocks.NewUDPHandler(ssclient, udpTimeout))
152+
core.RegisterUDPConnHandler(shadowsocks.NewUDPHandler(client, udpTimeout))
141153
}
142154

143155
// Configure LWIP stack to receive input data from the TUN device

outline/shadowsocks/config.go

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright 2022 The Outline Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package shadowsocks
16+
17+
import (
18+
"github.com/Jigsaw-Code/outline-ss-server/client"
19+
"github.com/eycorsican/go-tun2socks/common/log"
20+
)
21+
22+
// Config represents a shadowsocks server configuration.
23+
// Exported via gobind.
24+
type Config struct {
25+
Host string
26+
Port int
27+
Password string
28+
CipherName string
29+
Prefix []byte
30+
}
31+
32+
// Client provides a transparent container for [client.Client] that
33+
// is exportable (as an opaque object) via gobind.
34+
type Client struct {
35+
client.Client
36+
}
37+
38+
// NewClient provides a gobind-compatible wrapper for [client.NewClient].
39+
func NewClient(config *Config) (*Client, error) {
40+
c, err := client.NewClient(config.Host, config.Port, config.Password, config.CipherName)
41+
if err != nil {
42+
return nil, err
43+
}
44+
if len(config.Prefix) > 0 {
45+
log.Debugf("Using salt prefix: %s", string(config.Prefix))
46+
c.SetTCPSaltGenerator(client.NewPrefixSaltGenerator(config.Prefix))
47+
}
48+
49+
return &Client{c}, nil
50+
}

outline/shadowsocks/connectivity.go

+1-6
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,7 @@ const reachabilityTimeout = 10 * time.Second
3232
// the current network. Parallelizes the execution of TCP and UDP checks, selects the appropriate
3333
// error code to return accounting for transient network failures.
3434
// Returns an error if an unexpected error ocurrs.
35-
func CheckConnectivity(host string, port int, password, cipher string) (int, error) {
36-
client, err := shadowsocks.NewClient(host, port, password, cipher)
37-
if err != nil {
38-
// TODO: Inspect error for invalid cipher error or proxy host resolution failure.
39-
return Unexpected, err
40-
}
35+
func CheckConnectivity(client *Client) (int, error) {
4136
tcpChan := make(chan error)
4237
// Check whether the proxy is reachable and that the client is able to authenticate to the proxy
4338
go func() {

0 commit comments

Comments
 (0)