Files
Archive/go-shadowsocks2/main.go
2024-03-05 02:32:38 -08:00

208 lines
5.1 KiB
Go

package main
import (
"crypto/rand"
"encoding/base64"
"flag"
"fmt"
"io"
"log"
"net/url"
"os"
"os/signal"
"strings"
"syscall"
"time"
"github.com/shadowsocks/go-shadowsocks2/core"
"github.com/shadowsocks/go-shadowsocks2/socks"
)
var config struct {
Verbose bool
UDPTimeout time.Duration
TCPCork bool
}
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
UDP bool
TCP bool
Plugin string
PluginOpts string
}
flag.BoolVar(&config.Verbose, "verbose", false, "verbose mode")
flag.StringVar(&flags.Cipher, "cipher", "AEAD_CHACHA20_POLY1305", "available ciphers: "+strings.Join(core.ListCipher(), " "))
flag.StringVar(&flags.Key, "key", "", "base64url-encoded key (derive from password if empty)")
flag.IntVar(&flags.Keygen, "keygen", 0, "generate a base64url-encoded random key of given length in byte")
flag.StringVar(&flags.Password, "password", "", "password")
flag.StringVar(&flags.Server, "s", "", "server listen address or url")
flag.StringVar(&flags.Client, "c", "", "client connect address or url")
flag.StringVar(&flags.Socks, "socks", "", "(client-only) SOCKS listen address")
flag.BoolVar(&flags.UDPSocks, "u", false, "(client-only) Enable UDP support for SOCKS")
flag.StringVar(&flags.RedirTCP, "redir", "", "(client-only) redirect TCP from this address")
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.BoolVar(&flags.UDP, "udp", false, "(server-only) enable UDP support")
flag.BoolVar(&flags.TCP, "tcp", true, "(server-only) enable TCP support")
flag.BoolVar(&config.TCPCork, "tcpcork", false, "coalesce writing first few packets")
flag.DurationVar(&config.UDPTimeout, "udptimeout", 5*time.Minute, "UDP tunnel timeout")
flag.Parse()
if flags.Keygen > 0 {
key := make([]byte, flags.Keygen)
io.ReadFull(rand.Reader, key)
fmt.Println(base64.URLEncoding.EncodeToString(key))
return
}
if flags.Client == "" && flags.Server == "" {
flag.Usage()
return
}
var key []byte
if flags.Key != "" {
k, err := base64.URLEncoding.DecodeString(flags.Key)
if err != nil {
log.Fatal(err)
}
key = k
}
if flags.Client != "" { // client mode
addr := flags.Client
cipher := flags.Cipher
password := flags.Password
if flags.Password == "" {
password = os.Getenv("SS_PASSWORD")
}
var err error
if strings.HasPrefix(addr, "ss://") {
addr, cipher, password, err = parseURL(addr)
if err != nil {
log.Fatal(err)
}
}
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], udpAddr, p[1], ciph.PacketConn)
}
}
if flags.TCPTun != "" {
for _, tun := range strings.Split(flags.TCPTun, ",") {
p := strings.Split(tun, "=")
go tcpTun(p[0], addr, p[1], ciph.StreamConn)
}
}
if flags.Socks != "" {
socks.UDPEnabled = flags.UDPSocks
go socksLocal(flags.Socks, addr, ciph.StreamConn)
if flags.UDPSocks {
go udpSocksLocal(flags.Socks, udpAddr, ciph.PacketConn)
}
}
if flags.RedirTCP != "" {
go redirLocal(flags.RedirTCP, addr, ciph.StreamConn)
}
if flags.RedirTCP6 != "" {
go redir6Local(flags.RedirTCP6, addr, ciph.StreamConn)
}
}
if flags.Server != "" { // server mode
addr := flags.Server
cipher := flags.Cipher
password := flags.Password
if flags.Password == "" {
password = os.Getenv("SS_PASSWORD")
}
var err error
if strings.HasPrefix(addr, "ss://") {
addr, cipher, password, err = parseURL(addr)
if err != nil {
log.Fatal(err)
}
}
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)
}
if flags.UDP {
go udpRemote(udpAddr, ciph.PacketConn)
}
if flags.TCP {
go tcpRemote(addr, ciph.StreamConn)
}
}
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
<-sigCh
killPlugin()
}
func parseURL(s string) (addr, cipher, password string, err error) {
u, err := url.Parse(s)
if err != nil {
return
}
addr = u.Host
if u.User != nil {
cipher = u.User.Username()
password, _ = u.User.Password()
}
return
}