Last active
August 17, 2021 06:16
-
-
Save ayanamist/cb8f2e09fd2f4541052ae4739766acf1 to your computer and use it in GitHub Desktop.
FCM proxy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "flag" | |
| "fmt" | |
| "io" | |
| "log" | |
| "net" | |
| "net/url" | |
| "os" | |
| "os/signal" | |
| "runtime" | |
| "strings" | |
| "syscall" | |
| "time" | |
| "github.com/hashicorp/yamux" | |
| "github.com/polvi/sni" | |
| ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" | |
| "golang.org/x/net/proxy" | |
| ) | |
| const ( | |
| connectTimeout = 30 * time.Second | |
| ) | |
| var ( | |
| dialer = &net.Dialer{ | |
| Timeout: connectTimeout, | |
| Resolver: &net.Resolver{ | |
| PreferGo: true, | |
| }, | |
| } | |
| ciph, _ = ss.NewCipher("rc4-md5", "fcmproxy") | |
| proxyStr string | |
| proxyDial = func(network, addr string) (net.Conn, error) { | |
| conn, err := dialer.Dial(network, addr) | |
| if err == nil { | |
| if c, ok := conn.(*net.TCPConn); ok { | |
| c.SetNoDelay(true) | |
| } | |
| } | |
| return conn, err | |
| } | |
| localPort uint | |
| ) | |
| func newClientProxyFunc(srvAddrs []string) func(net.Conn) { | |
| return func(localConn net.Conn) { | |
| defer localConn.Close() | |
| sniHost, localConn, err := sniServerNameFromConn(localConn) | |
| if err != nil { | |
| log.Printf("sniServerNameFromConn from %s error: %v", localConn.RemoteAddr(), err) | |
| return | |
| } | |
| log.Printf("sniServernameFromConn got %s from %s", sniHost, localConn.RemoteAddr()) | |
| var remoteConn net.Conn | |
| connCh := make(chan net.Conn) | |
| closeCh := make(chan struct{}) | |
| connectTimer := time.NewTimer(2 * connectTimeout) | |
| delayTimer := time.NewTimer(0) | |
| LOOP: | |
| for _, srvAddr := range srvAddrs { | |
| select { | |
| case <-delayTimer.C: | |
| delayTimer.Reset(time.Second) | |
| log.Printf("try serverConnect %s from %s", srvAddr, localConn.RemoteAddr()) | |
| go serverConnect(localConn.RemoteAddr().String(), srvAddr, connCh, closeCh) | |
| case remoteConn = <-connCh: | |
| delayTimer.Stop() | |
| close(closeCh) | |
| break LOOP | |
| } | |
| } | |
| if remoteConn == nil { | |
| select { | |
| case <-connectTimer.C: | |
| log.Printf("remote conn not got, local conn: %s", localConn.RemoteAddr()) | |
| close(closeCh) | |
| return | |
| case remoteConn = <-connCh: | |
| close(closeCh) | |
| } | |
| } | |
| connectTimer.Stop() | |
| defer remoteConn.Close() | |
| go relay(localConn, remoteConn) | |
| relay(remoteConn, localConn) | |
| } | |
| } | |
| func serverConnect(localAddr, remoteAddr string, connCh chan<- net.Conn, closeCh <-chan struct{}) { | |
| remoteConn, err := proxyDial("tcp", remoteAddr) | |
| if err != nil { | |
| log.Printf("dial from %s to %s error: %v", localAddr, remoteAddr, err) | |
| return | |
| } | |
| remoteConn = ss.NewConn(remoteConn, ciph.Copy()) | |
| sess, _ := yamux.Client(remoteConn, yamux.DefaultConfig()) | |
| if _, err := sess.Ping(); err != nil { | |
| log.Printf("sess.Ping from %s to %s error: %v", localAddr, remoteAddr, err) | |
| sess.Close() | |
| return | |
| } | |
| stream, err := sess.Open() | |
| if err != nil { | |
| log.Printf("sess.Open from %s to %s error: %v", localAddr, remoteAddr, err) | |
| sess.Close() | |
| return | |
| } | |
| select { | |
| case <-closeCh: | |
| sess.Close() | |
| case connCh <- stream: | |
| } | |
| } | |
| func sniServerNameFromConn(conn net.Conn) (string, net.Conn, error) { | |
| host, conn, err := sni.ServerNameFromConn(conn) | |
| if err != nil { | |
| return "", nil, fmt.Errorf("invalid sni: %v", err) | |
| } | |
| if !strings.Contains(host, ":") { | |
| host = fmt.Sprintf("%s:%d", host, localPort) | |
| } | |
| if !strings.HasSuffix(host, fmt.Sprintf(".google.com:%d", localPort)) { | |
| return "", nil, fmt.Errorf("not allowed host: %s", host) | |
| } | |
| return host, conn, nil | |
| } | |
| func serverProxyFunc(localConn net.Conn) { | |
| defer localConn.Close() | |
| localConn = ss.NewConn(localConn, ciph.Copy()) | |
| sess, _ := yamux.Server(localConn, yamux.DefaultConfig()) | |
| defer sess.Close() | |
| localStream, err := sess.Accept() | |
| if err != nil { | |
| log.Printf("sess.Accept from %s error: %v", localConn.RemoteAddr(), err) | |
| return | |
| } | |
| remoteAddr, localStream, err := sniServerNameFromConn(localStream) | |
| if err != nil { | |
| log.Printf("sniServerNameFromConn from %s error: %v", localConn.RemoteAddr(), err) | |
| return | |
| } | |
| log.Printf("sniConnect got %s from %s", remoteAddr, localConn.RemoteAddr()) | |
| remoteConn, err := dialer.Dial("tcp", remoteAddr) | |
| if err != nil { | |
| log.Printf("dial from %s to %s error: %v", localConn.RemoteAddr(), remoteAddr, err) | |
| return | |
| } | |
| defer remoteConn.Close() | |
| if c, ok := remoteConn.(*net.TCPConn); ok { | |
| c.SetNoDelay(true) | |
| } | |
| go relay(localStream, remoteConn) | |
| relay(remoteConn, localStream) | |
| } | |
| func sigQuitHandle() { | |
| ch := make(chan os.Signal, 5) | |
| signal.Notify(ch, syscall.SIGQUIT) | |
| for range ch { | |
| buf := make([]byte, 1024*1024) | |
| n := runtime.Stack(buf, true) | |
| buf = buf[:n] | |
| log.Printf("SIGQUIT got, stack:\n%s", buf) | |
| } | |
| } | |
| func relay(src, dst net.Conn) { | |
| log.Printf("relay %s <-> %s start", src.RemoteAddr(), dst.RemoteAddr()) | |
| n, err := io.Copy(dst, src) | |
| log.Printf("relay %s <-> %s %d bytes, error: %v", src.RemoteAddr(), dst.RemoteAddr(), n, err) | |
| src.Close() | |
| dst.Close() | |
| } | |
| func main() { | |
| log.SetFlags(log.Lmicroseconds) | |
| go sigQuitHandle() | |
| flag.StringVar(&proxyStr, "proxy", "", "Set proxy url") | |
| flag.UintVar(&localPort, "port", 5228, "Local port") | |
| flag.Parse() | |
| if proxyStr != "" { | |
| u, err := url.Parse(proxyStr) | |
| if err != nil { | |
| log.Fatalf("invalid proxy url: %v", err) | |
| } | |
| d, err := proxy.FromURL(u, dialer) | |
| if err != nil { | |
| log.Fatalf("parse proxy failed: %v", err) | |
| } | |
| proxyDial = d.Dial | |
| } | |
| srvAddr := flag.Args() | |
| var proxyFunc func(net.Conn) | |
| if len(srvAddr) > 0 { | |
| proxyFunc = newClientProxyFunc(srvAddr) | |
| log.Printf("working on client, servers: %v", srvAddr) | |
| } else { | |
| proxyFunc = serverProxyFunc | |
| log.Printf("working on server") | |
| } | |
| l, err := net.Listen("tcp", fmt.Sprintf(":%d", localPort)) | |
| if err != nil { | |
| log.Fatalf("listen: %v", err) | |
| } | |
| for { | |
| conn, err := l.Accept() | |
| if err != nil { | |
| log.Fatalf("accept: %v", err) | |
| } | |
| if c, ok := conn.(*net.TCPConn); ok { | |
| c.SetNoDelay(true) | |
| } | |
| go proxyFunc(conn) | |
| } | |
| } |
Author
看了下栈,没有我自己写的代码,都是三方库的代码,另外看了下报错的地方,不知道是什么go版本,go1.11这里不应该会报错。
实际在我自己环境里测了下,怎么重启也无法复现。
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
当第一个server1重启的时候,境内端会出错退出。错误log
`use of closed network connection
00:42:56.916414 sniServernameFromConn got mtalk.google.com:5228 from 124.226.57.185:56500
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0x46b809]
goroutine 27 [running]:
io.ReadAtLeast(0x0, 0x0, 0xc00001a6f0, 0x10, 0x10, 0x10, 0x53ef20, 0x523401, 0xc00001a6f0)
/usr/local/go/src/io/io.go:310 +0x59
io.ReadFull(0x0, 0x0, 0xc00001a6f0, 0x10, 0x10, 0x10, 0x43be4e, 0xc00002ef58)
/usr/local/go/src/io/io.go:329 +0x58
github.com/shadowsocks/shadowsocks-go/shadowsocks.(*Conn).Read(0xc000072a50, 0xc0000f5000, 0x1000, 0x1000, 0xc0000b6c00, 0x4, 0x5998b0)
/root/go/src/github.com/shadowsocks/shadowsocks-go/shadowsocks/conn.go:96 +0x21d
bufio.(*Reader).Read(0xc000055680, 0xc00001a6e0, 0xc, 0xc, 0xc, 0xc00001a6e0, 0x0)
/usr/local/go/src/bufio/bufio.go:216 +0x22f
io.ReadAtLeast(0x59a0a0, 0xc000055680, 0xc00001a6e0, 0xc, 0xc, 0xc, 0x53ef20, 0xc000055501, 0xc00001a6e0)
/usr/local/go/src/io/io.go:310 +0x88
io.ReadFull(0x59a0a0, 0xc000055680, 0xc00001a6e0, 0xc, 0xc, 0xc, 0x0, 0x0)
/usr/local/go/src/io/io.go:329 +0x58
github.com/hashicorp/yamux.(*Session).recvLoop(0xc0000da0b0, 0x0, 0x0)
/root/go/src/github.com/hashicorp/yamux/session.go:458 +0xef
github.com/hashicorp/yamux.(*Session).recv(0xc0000da0b0)
/root/go/src/github.com/hashicorp/yamux/session.go:437 +0x2b
created by github.com/hashicorp/yamux.newSession
/root/go/src/github.com/hashicorp/yamux/session.go:113 +0x2aa`