Skip to content

Commit

Permalink
Merge pull request shadowsocks#144 from lixin9311/SIP003
Browse files Browse the repository at this point in the history
Support SIP003
  • Loading branch information
riobard authored Sep 18, 2019
2 parents a030dd9 + 48c3647 commit fe3e7f2
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 23 deletions.
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ GoDoc at https://godoc.org/github.com/shadowsocks/go-shadowsocks2/
- [x] Support for Netfilter TCP redirect (IPv6 should work but not tested)
- [x] UDP tunneling (e.g. relay DNS packets)
- [x] TCP tunneling (e.g. benchmark with iperf3)
- [x] SIP003 plugins


## Install
Expand Down Expand Up @@ -95,6 +96,28 @@ Start iperf3 client to connect to the tunneld port instead
iperf3 -c localhost -p 1090
```

### SIP003 Plugins (Experimental)

Both client and server support SIP003 plugins.
Use `-plugin` and `-plugin-opts` parameters to enable.

Client:

```sh
shadowsocks2 -c 'ss://AEAD_CHACHA20_POLY1305:your-password@[server_address]:8488' \
-verbose -socks :1080 -u -plugin v2ray
```
Server:

```sh
shadowsocks2 -s 'ss://AEAD_CHACHA20_POLY1305:your-password@:8488' -verbose \
-plugin v2ray -plugin-opts "server"
```
Note:

It will look for the plugin in the current directory first, then `$PATH`.

UDP connections will not be affected by SIP003.

## Design Principles

Expand Down
31 changes: 31 additions & 0 deletions log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package main

import (
"fmt"
"log"
"os"
)

var logger = log.New(os.Stderr, "", log.Lshortfile|log.LstdFlags)

func logf(f string, v ...interface{}) {
if config.Verbose {
logger.Output(2, fmt.Sprintf(f, v...))
}
}

type logHelper struct {
prefix string
}

func (l *logHelper) Write(p []byte) (n int, err error) {
if config.Verbose {
logger.Printf("%s%s\n", l.prefix, p)
return len(p), nil
}
return
}

func newLogHelper(prefix string) *logHelper {
return &logHelper{prefix}
}
60 changes: 37 additions & 23 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,29 +23,23 @@ var config struct {
UDPTimeout time.Duration
}

var logger = log.New(os.Stderr, "", log.Lshortfile|log.LstdFlags)

func logf(f string, v ...interface{}) {
if config.Verbose {
logger.Output(2, fmt.Sprintf(f, v...))
}
}

func main() {

var flags struct {
Client string
Server string
Cipher string
Key string
Password string
Keygen int
Socks string
RedirTCP string
RedirTCP6 string
TCPTun string
UDPTun string
UDPSocks bool
Client string
Server string
Cipher string
Key string
Password string
Keygen int
Socks string
RedirTCP string
RedirTCP6 string
TCPTun string
UDPTun string
UDPSocks bool
Plugin string
PluginOpts string
}

flag.BoolVar(&config.Verbose, "verbose", false, "verbose mode")
Expand All @@ -61,6 +55,8 @@ func main() {
flag.StringVar(&flags.RedirTCP6, "redir6", "", "(client-only) redirect TCP IPv6 from this address")
flag.StringVar(&flags.TCPTun, "tcptun", "", "(client-only) TCP tunnel (laddr1=raddr1,laddr2=raddr2,...)")
flag.StringVar(&flags.UDPTun, "udptun", "", "(client-only) UDP tunnel (laddr1=raddr1,laddr2=raddr2,...)")
flag.StringVar(&flags.Plugin, "plugin", "", "Enable SIP003 plugin. (e.g., v2ray-plugin)")
flag.StringVar(&flags.PluginOpts, "plugin-opts", "", "Set SIP003 plugin options. (e.g., \"server;tls;host=mydomain.me\")")
flag.DurationVar(&config.UDPTimeout, "udptimeout", 5*time.Minute, "UDP tunnel timeout")
flag.Parse()

Expand Down Expand Up @@ -98,15 +94,24 @@ func main() {
}
}

udpAddr := addr

ciph, err := core.PickCipher(cipher, key, password)
if err != nil {
log.Fatal(err)
}

if flags.Plugin != "" {
addr, err = startPlugin(flags.Plugin, flags.PluginOpts, addr, false)
if err != nil {
log.Fatal(err)
}
}

if flags.UDPTun != "" {
for _, tun := range strings.Split(flags.UDPTun, ",") {
p := strings.Split(tun, "=")
go udpLocal(p[0], addr, p[1], ciph.PacketConn)
go udpLocal(p[0], udpAddr, p[1], ciph.PacketConn)
}
}

Expand All @@ -121,7 +126,7 @@ func main() {
socks.UDPEnabled = flags.UDPSocks
go socksLocal(flags.Socks, addr, ciph.StreamConn)
if flags.UDPSocks {
go udpSocksLocal(flags.Socks, addr, ciph.PacketConn)
go udpSocksLocal(flags.Socks, udpAddr, ciph.PacketConn)
}
}

Expand All @@ -147,12 +152,21 @@ func main() {
}
}

udpAddr := addr

if flags.Plugin != "" {
addr, err = startPlugin(flags.Plugin, flags.PluginOpts, addr, true)
if err != nil {
log.Fatal(err)
}
}

ciph, err := core.PickCipher(cipher, key, password)
if err != nil {
log.Fatal(err)
}

go udpRemote(addr, ciph.PacketConn)
go udpRemote(udpAddr, ciph.PacketConn)
go tcpRemote(addr, ciph.StreamConn)
}

Expand Down
88 changes: 88 additions & 0 deletions plugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package main

import (
"fmt"
"net"
"os"
"os/exec"
)

func startPlugin(plugin, pluginOpts, ssAddr string, isServer bool) (newAddr string, err error) {
logf("starting plugin (%s) with option (%s)....", plugin, pluginOpts)
freePort, err := getFreePort()
if err != nil {
return "", fmt.Errorf("failed to fetch an unused port for plugin (%v)", err)
}
localHost := "127.0.0.1"
ssHost, ssPort, err := net.SplitHostPort(ssAddr)
if err != nil {
return "", err
}
newAddr = localHost + ":" + freePort
if isServer {
if ssHost == "" {
ssHost = "0.0.0.0"
}
logf("plugin (%s) will listen on %s:%s", plugin, ssHost, ssPort)
} else {
logf("plugin (%s) will listen on %s:%s", plugin, localHost, freePort)
}
err = execPlugin(plugin, pluginOpts, ssHost, ssPort, localHost, freePort)
return
}

func execPlugin(plugin, pluginOpts, remoteHost, remotePort, localHost, localPort string) error {
if fileExists(plugin) {
plugin = "./" + plugin
}
logH := newLogHelper("[" + plugin + "]: ")
env := append(os.Environ(),
"SS_REMOTE_HOST="+remoteHost,
"SS_REMOTE_PORT="+remotePort,
"SS_LOCAL_HOST="+localHost,
"SS_LOCAL_PORT="+localPort,
"SS_PLUGIN_OPTIONS="+pluginOpts,
)
cmd := &exec.Cmd{
Path: plugin,
Args: []string{plugin},
Env: env,
Stdout: logH,
Stderr: logH,
}
if err := cmd.Start(); err != nil {
return err
}
go func() {
if err := cmd.Wait(); err != nil {
logf("plugin exited (%v)\n", err)
os.Exit(2)
}
logf("plugin exited\n")
os.Exit(0)
}()
return nil
}

func fileExists(filename string) bool {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}

func getFreePort() (string, error) {
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
if err != nil {
return "", err
}

l, err := net.ListenTCP("tcp", addr)
if err != nil {
return "", err
}
port := fmt.Sprintf("%d", l.Addr().(*net.TCPAddr).Port)
l.Close()
return port, nil
}

0 comments on commit fe3e7f2

Please sign in to comment.