Skip to content

Commit

Permalink
add wss, go mod support
Browse files Browse the repository at this point in the history
  • Loading branch information
IrineSistiana committed Apr 27, 2020
1 parent c96942c commit da94c5d
Show file tree
Hide file tree
Showing 9 changed files with 334 additions and 73 deletions.
45 changes: 23 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,28 @@

---

在这里下载:[release](https://github.com/IrineSistiana/simple-tls/releases)

特点:

* 简单:除TLS加密不提供格外的功能 ~~这叫特点?~~
* 安全:强制使用TLS1.3。
* 支持shadowsocks插件。
* 支持Android。
* 强制使用TLS1.3
* 支持Websocket
* 支持shadowsocks插件
* 支持Android
* 简单

## 命令

|client|-->|simple-tls client|--TLS1.3-->|simple-tls server|-->|destination|

# 服务端与客户端都需要(插件模式除外)
-b string
[Host:Port] 监听地址 (必需,插件模式除外)
-d string
[Host:Port] 目的地地址 (必需,插件模式除外)
-wss
使用 Websocket Secure 协议
-path string
Websocket 的路径

# 客户端模式
-cca string
Expand Down Expand Up @@ -49,6 +55,8 @@
-t int
timeout after sec (default 300)

**无补全的base64编码:**如果base64编码末尾有`=`,去掉它们。

## SIP003

支援shadowsocks [SIP003](https://shadowsocks.org/en/spec/Plugin.html)插件协议。接受的键值对[同上](#命令)
Expand All @@ -57,36 +65,29 @@

[shadowsocks-libev](https://github.com/shadowsocks/shadowsocks-libev)为例:

# TLS
ss-server -c config.json --plugin simple-tls --plugin-opts "s;key=/path/to/your/key;cert=/path/to/your/cert"
ss-local -c config.json --plugin simple-tls --plugin-opts "n=your.server.certificates.dnsname"

# WSS
ss-server -c config.json --plugin simple-tls --plugin-opts "s;wss;key=/path/to/your/key;cert=/path/to/your/cert"
ss-local -c config.json --plugin simple-tls --plugin-opts "wss;n=your.server.certificates.dnsname"

## Android

simple-tls-android是[shadowsocks-android](https://github.com/shadowsocks/shadowsocks-android)的插件.
[shadowsocks-android](https://github.com/shadowsocks/shadowsocks-android)的插件

支援Android 7以上系统。

## 自己编译

安装go:[golang.org](https://golang.org/dl/)

在本地系统安装:

go get -u github.com/IrineSistiana/simple-tls

或者交叉编译:[参考](https://golang.org/cmd/go/#hdr-Compile_packages_and_dependencies)

## Tips

无补全的base64编码: 可以理解为:如果编码末尾有`=`,去掉它们。

`-gen-cert` 可以快速的生成一个ECC证书,并打印出无补全的base64编码后的cert的用于客户端用`-cca`导入。证书DNSName取自`-n`参数或随机生成。key和cert文件会放在`-key``-cert`指定的位置或当前目录`./`
`-gen-cert` 可以快速的生成一个ECC证书,并打印出无补全的base64编码后的cert的用于客户端用`-cca`导入。证书DNSName取自`-n`参数或随机生成。key和cert文件会放在`-key``-cert`指定的位置或当前目录`./`。比如:

条件允许的话还是建议从[Let's Encrypt](https://letsencrypt.org/)整一个合法的证书。
simple-tls -gen-cert -n example.com

高级用户建议用nginx,又小又快老牌软件稳定可靠功能全。参考nginx官方文档:[Securing TCP Traffic to Upstream Servers](https://docs.nginx.com/nginx/admin-guide/security-controls/securing-tcp-traffic-upstream/)。simple-tls只是配置和使用简单点
[Let's Encrypt](https://letsencrypt.org/)可以免费获得一个合法的证书

tls 1.3的加密强度足够。下层的加密强度可降低或不加密。
TLS 1.3的加密强度足够。下层的加密强度可降低或不加密。

---

Expand Down
62 changes: 49 additions & 13 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,35 @@ import (
"fmt"
"log"
"net"
"strings"
"sync"
"time"

"github.com/gorilla/websocket"
)

func doClient(l net.Listener, server string, tlsConfig *tls.Config, timeout time.Duration, vpnMode, tfo bool) error {
func doClient(l net.Listener, server string, tlsConfig *tls.Config, wss bool, path string, timeout time.Duration, vpnMode, tfo bool) error {
dialer := net.Dialer{
Timeout: time.Second * 5,
Control: getControlFunc(&tcpConfig{vpnMode: vpnMode, tfo: tfo}),
}

wsDialer := &websocket.Dialer{
TLSClientConfig: tlsConfig,
NetDial: func(network, _ string) (net.Conn, error) {
// overwrite url host addr
return dialer.Dial(network, server)
},
WriteBufferPool: &sync.Pool{},
HandshakeTimeout: time.Second * 8,
}

if !strings.HasPrefix(path, "/") {
path = "/" + path
}

url := "wss://" + tlsConfig.ServerName + path

for {
localConn, err := l.Accept()
if err != nil {
Expand All @@ -38,22 +59,37 @@ func doClient(l net.Listener, server string, tlsConfig *tls.Config, timeout time

go func() {
defer localConn.Close()
var serverConn net.Conn

serverRawConn, err := dialer.Dial("tcp", server)
if err != nil {
log.Printf("doClient: dialer.Dial: %v", err)
return
}
defer serverRawConn.Close()
if wss {
serverWSSConn, err := dialWebsocketConn(wsDialer, url)
if err != nil {
log.Printf("ERROR: doClient: dialWebsocketConn: %v", err)
return
}
defer serverWSSConn.Close()

serverConn = serverWSSConn
} else {
serverRawConn, err := dialer.Dial("tcp", server)
if err != nil {
log.Printf("ERROR: doClient: dialer.Dial: %v", err)
return
}
defer serverRawConn.Close()

serverTLSConn := tls.Client(serverRawConn, tlsConfig)

if err := tlsHandshakeTimeout(serverTLSConn, time.Second*5); err != nil {
log.Printf("ERROR: doClient: tlsHandshakeTimeout: %v", err)
return
}

serverTLSConn := tls.Client(serverRawConn, tlsConfig)
if err := serverTLSConn.Handshake(); err != nil {
log.Printf("doServer: serverTLSConn.Handshake: %v", err)
return
serverConn = serverTLSConn
}

if err := openTunnel(localConn, serverTLSConn, timeout); err != nil {
log.Printf("doServer: openTunnel: %v", err)
if err := openTunnel(localConn, serverConn, timeout); err != nil {
log.Printf("ERROR: doClient: openTunnel: %v", err)
}
}()
}
Expand Down
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module github.com/IrineSistiana/simple-tls

go 1.14

require (
github.com/gorilla/websocket v1.4.2
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f
)
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f h1:gWF768j/LaZugp8dyS4UwsslYCYz9XgFxvlgsn0n9H8=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
39 changes: 25 additions & 14 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ func main() {
os.Exit(0)
}()

var bindAddr, dstAddr, serverName, cca, cert, key string
var isServer, tfo, vpn, genCert bool
var bindAddr, dstAddr, serverName, cca, cert, key, path string
var isServer, wss, tfo, vpn, genCert bool
var cpu int
var timeout time.Duration
var timeoutFlag int
Expand All @@ -55,6 +55,8 @@ func main() {

commandLine.StringVar(&bindAddr, "b", "", "[Host:Port] bind address")
commandLine.StringVar(&dstAddr, "d", "", "[Host:Port] destination address")
commandLine.BoolVar(&wss, "wss", false, "using wss protocol")
commandLine.StringVar(&path, "path", "/", "[path] wss path")

// client only
commandLine.StringVar(&serverName, "n", "", "server name")
Expand Down Expand Up @@ -158,13 +160,14 @@ func main() {
timeout = time.Duration(timeoutFlag) * time.Second
runtime.GOMAXPROCS(cpu)

if isServer {
lc := net.ListenConfig{Control: getControlFunc(&tcpConfig{tfo: tfo, vpnMode: vpn})}
l, err := lc.Listen(context.Background(), "tcp", bindAddr)
if err != nil {
log.Fatalf("main: net.Listen: %v", err)
}
if len(bindAddr) == 0 {
log.Fatal("main: bind addr is required")
}
if len(dstAddr) == 0 {
log.Fatal("main: destination addr is required")
}

if isServer {
tlsConfig := new(tls.Config)
tlsConfig.MinVersion = tls.VersionTLS13
if len(cert) == 0 || len(key) == 0 {
Expand All @@ -177,17 +180,18 @@ func main() {
tlsConfig.Certificates = []tls.Certificate{cer}
}

err = doServer(l, tlsConfig, dstAddr, timeout)
lc := net.ListenConfig{Control: getControlFunc(&tcpConfig{tfo: tfo})}
l, err := lc.Listen(context.Background(), "tcp", bindAddr)
if err != nil {
log.Fatalf("main: doServer: %v", err)
log.Fatalf("main: net.Listen: %v", err)
}

} else { // do client
l, err := net.Listen("tcp", bindAddr)
err = doServer(l, tlsConfig, dstAddr, wss, path, timeout)
if err != nil {
log.Fatalf("main: net.Listen: %v", err)
log.Fatalf("main: doServer: %v", err)
}

} else { // do client
tlsConfig := new(tls.Config)
tlsConfig.ClientSessionCache = tls.NewLRUClientSessionCache(8)
tlsConfig.MinVersion = tls.VersionTLS13
Expand All @@ -208,7 +212,14 @@ func main() {
}
tlsConfig.RootCAs = rootCAs
}
err = doClient(l, dstAddr, tlsConfig, timeout, vpn, tfo)

lc := net.ListenConfig{}
l, err := lc.Listen(context.Background(), "tcp", bindAddr)
if err != nil {
log.Fatalf("main: net.Listen: %v", err)
}

err = doClient(l, dstAddr, tlsConfig, wss, path, timeout, vpn, tfo)
if err != nil {
log.Fatalf("main: doServer: %v", err)
}
Expand Down
98 changes: 75 additions & 23 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,39 +29,91 @@ import (
"log"
"math/big"
"net"
"net/http"
"time"

mathRand "math/rand"
)

func doServer(l net.Listener, tlsConfig *tls.Config, dst string, timeout time.Duration) error {
"github.com/gorilla/websocket"
)

for {
clientRawConn, err := l.Accept()
func doServer(l net.Listener, tlsConfig *tls.Config, dst string, wss bool, path string, timeout time.Duration) error {
if wss {
httpMux := http.NewServeMux()
wsss := &wssServer{
upgrader: websocket.Upgrader{
HandshakeTimeout: time.Second * 8,
ReadBufferSize: 0,
WriteBufferSize: 0,
},
dst: dst,
timeout: timeout,
}
httpMux.Handle(path, wsss)
err := http.Serve(tls.NewListener(l, tlsConfig), httpMux)
if err != nil {
return fmt.Errorf("l.Accept(): %w", err)
return fmt.Errorf("http.Serve: %v", err)
}

go func() {
defer clientRawConn.Close()
clientTLSConn := tls.Server(clientRawConn, tlsConfig)
// check client conn before dial dst
if err := clientTLSConn.Handshake(); err != nil {
log.Printf("doServer: %s, clientTLSConn.Handshake: %v", clientRawConn.RemoteAddr(), err)
return
}

dstConn, err := net.Dial("tcp", dst)
} else {
for {
clientRawConn, err := l.Accept()
if err != nil {
log.Printf("doServer: %s: net.Dial: %v", clientRawConn.RemoteAddr(), err)
return
return fmt.Errorf("l.Accept(): %w", err)
}
defer dstConn.Close()

if err := openTunnel(dstConn, clientTLSConn, timeout); err != nil {
log.Printf("doServer: %s: openTunnel: %v", clientRawConn.RemoteAddr(), err)
}
}()
go func() {
defer clientRawConn.Close()
clientTLSConn := tls.Server(clientRawConn, tlsConfig)

// check client conn before dial dst
clientRawConn.SetDeadline(time.Now().Add(time.Second * 5))
if err := clientTLSConn.Handshake(); err != nil {
log.Printf("ERROR: doServer: %s, clientTLSConn.Handshake: %v", clientRawConn.RemoteAddr(), err)
return
}
clientRawConn.SetDeadline(time.Time{})

dstConn, err := net.Dial("tcp", dst)
if err != nil {
log.Printf("ERROR: doServer: %s: net.Dial: %v", clientRawConn.RemoteAddr(), err)
return
}
defer dstConn.Close()

if err := openTunnel(dstConn, clientTLSConn, timeout); err != nil {
log.Printf("ERROR: doServer: %s: openTunnel: %v", clientRawConn.RemoteAddr(), err)
}
}()
}
}
return nil
}

type wssServer struct {
upgrader websocket.Upgrader
dst string
timeout time.Duration
}

// ServeHTTP implements http.Handler interface
func (s *wssServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
leftWSConn, err := s.upgrader.Upgrade(w, r, nil)
if err != nil {
log.Printf("ERROR: ServeHTTP: %s, Upgrade: %v", r.RemoteAddr, err)
return
}

leftConn := wrapWebSocketConn(leftWSConn)

dstConn, err := net.Dial("tcp", s.dst)
if err != nil {
log.Printf("ERROR: ServeHTTP: %s: net.Dial: %v", r.RemoteAddr, err)
return
}
defer dstConn.Close()

if err := openTunnel(dstConn, leftConn, s.timeout); err != nil {
log.Printf(": ServeHTTP: %s: openTunnel: %v", r.RemoteAddr, err)
}
}

Expand Down
1 change: 0 additions & 1 deletion tcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package main

Expand Down
Loading

0 comments on commit da94c5d

Please sign in to comment.