Skip to content
This repository has been archived by the owner on Apr 27, 2020. It is now read-only.

Commit

Permalink
refine: test, func ws close, func copyBuffer, readme.md....
Browse files Browse the repository at this point in the history
  • Loading branch information
IrineSistiana committed Mar 6, 2020
1 parent 64e7a07 commit 3db4656
Show file tree
Hide file tree
Showing 9 changed files with 276 additions and 92 deletions.
48 changes: 26 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,28 +104,6 @@ client ---> |mtt-client| ---> |mtt-server| ---> destination

See [here](#mtt-server-multi-user-version-mtt-mu-server)

## WebSocket Secure

mos-tls-tunnel support WebSocket Secure protocol (`wss`). WebSocket connections can be proxied by HTTP server such as Apache, as well as most of CDNs that support WebSocket.

`wss-path` will be the path of HTTP request.

## Multiplex (Experimental)

mos-tls-tunnel support connection Multiplex (`mux`). It significantly reduces handshake latency, at the cost of high throughput.

Client can set `mux-max-stream` to control the maximum number of data streams in one TCP connection. The value should be between 1 and 16.

if `wss` is enabled, server can automatically detect whether client enable `mux` or not. But you can still use the `mux` to force the server to enable multiplex if auto-detection fails.

## Self Signed Certificate

On the server, if both `key` and `cert` is empty, a self signed certificate will be used. And the string from `n` will be certificate's hostname. **This self signed certificate CANNOT be verified.**

On the client, if server's certificate can't be verified. You can enable `sv` to skip the verification. **Enable this option only if you know what you are doing. Use it with caution.**

We recommend that you use a valid certificate all the time. A free and valid certificate can be easily obtained here. [Let's Encrypt](https://letsencrypt.org/)

## Shadowsocks Plugin (SIP003)

mos-tls-tunnel support shadowsocks [SIP003](https://shadowsocks.org/en/spec/Plugin.html). Options keys are the same as [Usage](#usage) defined. You don't have to set client and server address: `b`,`d`,`s`, shadowsocks will set those automatically.
Expand Down Expand Up @@ -154,6 +132,28 @@ Below are example commands with [shadowsocks-libev](https://github.com/shadowsoc

The Android plugin project is maintained here: [mostunnel-android](https://github.com/IrineSistiana/mostunnel-android). This is a plugin of [shadowsocks-android](https://github.com/shadowsocks/shadowsocks-android).

## WebSocket Secure

mos-tls-tunnel support WebSocket Secure protocol (`wss`). WebSocket connections can be proxied by HTTP server such as Apache, as well as most of CDNs that support WebSocket.

`wss-path` will be the path of HTTP request.

## Multiplex (Experimental)

mos-tls-tunnel support connection Multiplex (`mux`). It significantly reduces handshake latency, at the cost of high throughput.

Client can set `mux-max-stream` to control the maximum number of data streams in one TCP connection. The value should be between 1 and 16.

if `wss` is enabled, server can automatically detect whether client enable `mux` or not. But you can still use the `mux` to force the server to enable multiplex if auto-detection fails.

## Self Signed Certificate

On the server, if both `key` and `cert` is empty, a self signed certificate will be used. And the string from `n` will be certificate's hostname. **This self signed certificate CANNOT be verified.**

On the client, if server's certificate can't be verified. You can enable `sv` to skip the verification. **Enable this option only if you know what you are doing. Use it with caution.**

We recommend that you use a valid certificate all the time. A free and valid certificate can be easily obtained here. [Let's Encrypt](https://letsencrypt.org/)

## mtt-server Multi-user Version (mtt-mu-server)

mtt-mu-server allows multiple users to use the `wss` mode of mtt-client to transfer data on the same server port (eg: 443). Users are offloaded to the corresponding backend (`dst` destination) according to the path (`wss-path`) of their HTTP request.
Expand All @@ -173,6 +173,8 @@ In general, you need the following build dependencies:

You might build mos-tls-tunnel like this:

<details><summary><code>Example</code></summary><br>

# get source
go get -d -u github.com/IrineSistiana/mos-tls-tunnel/cmd/mtt-client
go get -d -u github.com/IrineSistiana/mos-tls-tunnel/cmd/mtt-server
Expand All @@ -182,6 +184,8 @@ You might build mos-tls-tunnel like this:
go build -o ./ github.com/IrineSistiana/mos-tls-tunnel/cmd/mtt-client
go build -o ./ github.com/IrineSistiana/mos-tls-tunnel/cmd/mtt-server
go build -o ./ github.com/IrineSistiana/mos-tls-tunnel/cmd/mtt-mu-server

</details>

## Open Source Components / Libraries

Expand Down
22 changes: 17 additions & 5 deletions internal/core/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ type Client struct {
listener net.Listener

log *logrus.Logger

//test only
testDialServerRaw func() (net.Conn, error)
}

// NewClient inits a client instance
Expand Down Expand Up @@ -119,7 +122,7 @@ func NewClient(c *ClientConfig) (*Client, error) {
client.wssURL = "wss://" + c.ServerName + c.WSSPath
internelDial := func(network, addr string) (net.Conn, error) {
// overwrite url host addr
return client.netDialer.Dial(network, c.RemoteAddr)
return client.dialServerRaw()
}
client.wsDialer = &websocket.Dialer{
TLSClientConfig: client.tlsConf,
Expand Down Expand Up @@ -191,6 +194,7 @@ func (client *Client) Start() error {

//ForwardConn forwards this connection to server.
//It will block until server-side connection is closed
//or c is closed
func (client *Client) ForwardConn(c net.Conn) error {
var rightConn net.Conn
var err error
Expand All @@ -201,7 +205,7 @@ func (client *Client) ForwardConn(c net.Conn) error {
return fmt.Errorf("mux getStream: %v", err)
}
} else {
rightConn, err = client.newServerConn()
rightConn, err = client.dialServer()
if err != nil {
return fmt.Errorf("connect to remote: %v", err)
}
Expand Down Expand Up @@ -230,30 +234,38 @@ func (client *Client) dialWSS() (net.Conn, error) {
}

func (client *Client) dialTLS() (net.Conn, error) {
conn, err := tls.DialWithDialer(client.netDialer, "tcp", client.conf.RemoteAddr, client.tlsConf)
raw, err := client.dialServerRaw()
if err != nil {
return nil, err
}
conn := tls.Client(raw, client.tlsConf)
if err := conn.Handshake(); err != nil {
conn.Close()
return nil, err
}
return conn, nil
}

func (client *Client) newServerConn() (net.Conn, error) {
func (client *Client) dialServer() (net.Conn, error) {
if client.conf.EnableWSS {
return client.dialWSS()
}
return client.dialTLS()
}

func (client *Client) dialServerRaw() (net.Conn, error) {
if client.testDialServerRaw == nil {
return client.netDialer.Dial("tcp", client.conf.RemoteAddr)
}
return client.testDialServerRaw()
}

type smuxSessPool struct {
sync.Map
}

func (client *Client) dialNewSmuxSess() (*smux.Session, error) {
rightConn, err := client.newServerConn()
rightConn, err := client.dialServer()
if err != nil {
return nil, err
}
Expand Down
19 changes: 19 additions & 0 deletions internal/core/cmux.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
// Copyright (c) 2019-2020 IrineSistiana
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package core

import (
Expand Down
42 changes: 29 additions & 13 deletions internal/core/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,13 @@ import (
"github.com/xtaci/smux"
)

var ioCopybuffPool = &sync.Pool{New: func() interface{} {
return make([]byte, defaultCopyIOBufferSize)
}}
var (
ioCopybuffPool = &sync.Pool{New: func() interface{} {
return make([]byte, defaultCopyIOBufferSize)
}}

longTimeAgo = time.Unix(0, 1)
)

func acquireIOBuf() []byte {
return ioCopybuffPool.Get().([]byte)
Expand Down Expand Up @@ -73,40 +77,52 @@ func (fe *firstErr) getErr() error {
// both of them.
func openTunnel(a, b net.Conn, timeout time.Duration) error {
fe := firstErr{}

go openOneWayTunnel(a, b, timeout, &fe)
openOneWayTunnel(b, a, timeout, &fe)
muTimeout := atomic.Value{}
muTimeout.Store(timeout)

wg := sync.WaitGroup{}
wg.Add(1)
go func() {
openOneWayTunnel(a, b, &muTimeout, &fe)
wg.Done()
}()
openOneWayTunnel(b, a, &muTimeout, &fe)
wg.Wait()

return fe.getErr()
}

// don not use this func, use openTunnel instead
func openOneWayTunnel(dst, src net.Conn, timeout time.Duration, fe *firstErr) {
func openOneWayTunnel(dst, src net.Conn, muTimeout *atomic.Value, fe *firstErr) {
buf := acquireIOBuf()
_, err := copyBuffer(dst, src, buf, timeout)

// a nil err is io.EOF err, which is surpressed by copyBuffer.
_, err := copyBuffer(dst, src, buf, muTimeout)

// a nil err might be an io.EOF err, which is surpressed by copyBuffer.
// report a nil err means one conn was closed by peer.
fe.report(err)

//let another goroutine break from copy loop
dst.Close()
muTimeout.Store(time.Duration(0))
src.SetDeadline(longTimeAgo)
dst.SetDeadline(longTimeAgo)
src.Close()
dst.Close()

releaseIOBuf(buf)
}

func copyBuffer(dst net.Conn, src net.Conn, buf []byte, timeout time.Duration) (written int64, err error) {
func copyBuffer(dst net.Conn, src net.Conn, buf []byte, muTimeout *atomic.Value) (written int64, err error) {

if len(buf) <= 0 {
panic("buf size <= 0")
}

for {
src.SetReadDeadline(time.Now().Add(timeout))
src.SetReadDeadline(time.Now().Add(muTimeout.Load().(time.Duration)))
nr, er := src.Read(buf)
if nr > 0 {
dst.SetWriteDeadline(time.Now().Add(timeout))
dst.SetWriteDeadline(time.Now().Add(muTimeout.Load().(time.Duration)))
nw, ew := dst.Write(buf[0:nr])
if nw > 0 {
written += int64(nw)
Expand Down
Loading

0 comments on commit 3db4656

Please sign in to comment.