Skip to content

Instantly share code, notes, and snippets.

@ayanamist
Last active August 17, 2021 06:16
Show Gist options
  • Select an option

  • Save ayanamist/cb8f2e09fd2f4541052ae4739766acf1 to your computer and use it in GitHub Desktop.

Select an option

Save ayanamist/cb8f2e09fd2f4541052ae4739766acf1 to your computer and use it in GitHub Desktop.

Revisions

  1. ayanamist revised this gist Aug 17, 2021. 4 changed files with 30 additions and 20 deletions.
    21 changes: 8 additions & 13 deletions fcmproxy.go
    Original file line number Diff line number Diff line change
    @@ -34,17 +34,10 @@ var (

    ciph *ss.Cipher

    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
    }
    proxyDial = dialer.Dial

    localPort uint
    remotePort uint
    )

    // httpProxy is a HTTP/HTTPS connect proxy.
    @@ -89,15 +82,16 @@ func (s *httpProxy) Dial(network, addr string) (net.Conn, error) {

    resp, err := http.ReadResponse(bufio.NewReader(c), req)
    if err != nil {
    // TODO close resp body ?
    resp.Body.Close()
    if resp != nil {
    resp.Body.Close()
    }
    c.Close()
    return nil, err
    }
    resp.Body.Close()
    if resp.StatusCode != 200 {
    c.Close()
    err = fmt.Errorf("Connect server using proxy error, StatusCode [%d]", resp.StatusCode)
    err = fmt.Errorf("connect server using proxy error %s", resp.Status)
    return nil, err
    }

    @@ -188,7 +182,7 @@ func sniServerNameFromConn(conn net.Conn) (string, net.Conn, error) {
    return "", nil, fmt.Errorf("invalid sni: %v", err)
    }
    if !strings.Contains(host, ":") {
    host = fmt.Sprintf("%s:%d", host, localPort)
    host = fmt.Sprintf("%s:%d", host, remotePort)
    }

    if !strings.Contains(host, ".google.com:") {
    @@ -322,6 +316,7 @@ func main() {

    flag.StringVar(&proxyStr, "proxy", "", "Set proxy url")
    flag.UintVar(&localPort, "port", 5228, "Local port")
    flag.UintVar(&remotePort, "remote-port", 5228, "Remote port")
    flag.StringVar(&shadowPass, "key", "fcmproxy", "Encrypt key")

    flag.Parse()
    13 changes: 9 additions & 4 deletions fcmproxy.service
    Original file line number Diff line number Diff line change
    @@ -1,13 +1,18 @@
    [Unit]
    Description=GCM Proxy
    Description=Google DOH proxy
    After=network.target
    After=v2ray.service

    [Service]
    User=nobody
    User=doh-proxy
    Type=simple
    Restart=always
    ExecStart=/usr/local/bin/fcmproxy
    EnvironmentFile=/etc/default/doh-proxy
    ExecStart=/usr/local/bin/doh-proxy $ARGS
    LimitNOFILE=65536
    CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
    AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
    NoNewPrivileges=true

    [Install]
    WantedBy=multi-user.target
    WantedBy=multi-user.target
    7 changes: 4 additions & 3 deletions go.mod
    Original file line number Diff line number Diff line change
    @@ -4,9 +4,10 @@ go 1.16

    require (
    github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
    github.com/hashicorp/yamux v0.0.0-20210316155119-a95892c5f864
    github.com/hashicorp/yamux v0.0.0-20210707203944-259a57b3608c
    github.com/polvi/sni v0.0.0-20151108162443-a08e954206fd
    github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601
    golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf // indirect
    golang.org/x/net v0.0.0-20210510120150-4163338589ed
    golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e // indirect
    golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d
    golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912 // indirect
    )
    9 changes: 9 additions & 0 deletions go.sum
    Original file line number Diff line number Diff line change
    @@ -2,18 +2,27 @@ github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmH
    github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
    github.com/hashicorp/yamux v0.0.0-20210316155119-a95892c5f864 h1:Y4V+SFe7d3iH+9pJCoeWIOS5/xBJIFsltS7E+KJSsJY=
    github.com/hashicorp/yamux v0.0.0-20210316155119-a95892c5f864/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
    github.com/hashicorp/yamux v0.0.0-20210707203944-259a57b3608c h1:nqkErwUGfpZZMqj29WZ9U/wz2OpJVDuiokLhE/3Y7IQ=
    github.com/hashicorp/yamux v0.0.0-20210707203944-259a57b3608c/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
    github.com/polvi/sni v0.0.0-20151108162443-a08e954206fd h1:moB2BJUF99MbSLkvr+Y++oiZvadc9ToYX4p5oHRMWlQ=
    github.com/polvi/sni v0.0.0-20151108162443-a08e954206fd/go.mod h1:0tydZ0+WbhXnymEaRDY/DzO3GvKQAiYune6xvHRFzSw=
    github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601 h1:XU9hik0exChEmY92ALW4l9WnDodxLVS9yOSNh2SizaQ=
    github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601/go.mod h1:mttDPaeLm87u74HMrP+n2tugXvIKWcwff/cqSX0lehY=
    golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf h1:B2n+Zi5QeYRDAEodEu72OS36gmTWjgpXr2+cWcBW90o=
    golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
    golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e h1:VvfwVmMH40bpMeizC9/K7ipM5Qjucuu16RWfneFPyhQ=
    golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
    golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
    golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I=
    golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
    golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c=
    golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
    golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
    golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c=
    golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
    golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
    golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912 h1:uCLL3g5wH2xjxVREVuAbP9JM5PPKjRbXKRa6IBjkzmU=
    golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
    golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
    golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
    golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
  2. ayanamist revised this gist May 12, 2021. 3 changed files with 45 additions and 0 deletions.
    13 changes: 13 additions & 0 deletions fcmproxy.service
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,13 @@
    [Unit]
    Description=GCM Proxy
    After=network.target

    [Service]
    User=nobody
    Type=simple
    Restart=always
    ExecStart=/usr/local/bin/fcmproxy
    LimitNOFILE=65536

    [Install]
    WantedBy=multi-user.target
    12 changes: 12 additions & 0 deletions go.mod
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,12 @@
    module test/golang/fcmproxy

    go 1.16

    require (
    github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
    github.com/hashicorp/yamux v0.0.0-20210316155119-a95892c5f864
    github.com/polvi/sni v0.0.0-20151108162443-a08e954206fd
    github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601
    golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf // indirect
    golang.org/x/net v0.0.0-20210510120150-4163338589ed
    )
    20 changes: 20 additions & 0 deletions go.sum
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,20 @@
    github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
    github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
    github.com/hashicorp/yamux v0.0.0-20210316155119-a95892c5f864 h1:Y4V+SFe7d3iH+9pJCoeWIOS5/xBJIFsltS7E+KJSsJY=
    github.com/hashicorp/yamux v0.0.0-20210316155119-a95892c5f864/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
    github.com/polvi/sni v0.0.0-20151108162443-a08e954206fd h1:moB2BJUF99MbSLkvr+Y++oiZvadc9ToYX4p5oHRMWlQ=
    github.com/polvi/sni v0.0.0-20151108162443-a08e954206fd/go.mod h1:0tydZ0+WbhXnymEaRDY/DzO3GvKQAiYune6xvHRFzSw=
    github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601 h1:XU9hik0exChEmY92ALW4l9WnDodxLVS9yOSNh2SizaQ=
    github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601/go.mod h1:mttDPaeLm87u74HMrP+n2tugXvIKWcwff/cqSX0lehY=
    golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf h1:B2n+Zi5QeYRDAEodEu72OS36gmTWjgpXr2+cWcBW90o=
    golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
    golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
    golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I=
    golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
    golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
    golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c=
    golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
    golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
    golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
    golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
    golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
  3. ayanamist revised this gist Apr 4, 2021. 1 changed file with 11 additions and 14 deletions.
    25 changes: 11 additions & 14 deletions fcmproxy.go
    Original file line number Diff line number Diff line change
    @@ -104,20 +104,6 @@ func (s *httpProxy) Dial(network, addr string) (net.Conn, error) {
    return c, nil
    }

    func init() {
    proxy.RegisterDialerType("http", func(u *url.URL, forward proxy.Dialer) (d proxy.Dialer, err error) {
    s := new(httpProxy)
    s.host = u.Host
    s.forward = forward
    if u.User != nil {
    s.haveAuth = true
    s.username = u.User.Username()
    s.password, _ = u.User.Password()
    }
    return s, nil
    })
    }

    func newClientProxyFunc(srvAddrs []string) func(net.Conn) {
    return func(conn net.Conn) {
    localConn := conn
    @@ -310,6 +296,17 @@ func ssDial(u *url.URL, d proxy.Dialer) (proxy.Dialer, error) {
    }

    func init() {
    proxy.RegisterDialerType("http", func(u *url.URL, forward proxy.Dialer) (d proxy.Dialer, err error) {
    s := new(httpProxy)
    s.host = u.Host
    s.forward = forward
    if u.User != nil {
    s.haveAuth = true
    s.username = u.User.Username()
    s.password, _ = u.User.Password()
    }
    return s, nil
    })
    proxy.RegisterDialerType("ss", ssDial)
    }

  4. ayanamist revised this gist Nov 26, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion fcmproxy.go
    Original file line number Diff line number Diff line change
    @@ -205,7 +205,7 @@ func sniServerNameFromConn(conn net.Conn) (string, net.Conn, error) {
    host = fmt.Sprintf("%s:%d", host, localPort)
    }

    if !strings.HasSuffix(host, fmt.Sprintf(".google.com:%d", localPort)) {
    if !strings.Contains(host, ".google.com:") {
    return "", nil, fmt.Errorf("not allowed host: %s", host)
    }
    return host, conn, nil
  5. ayanamist revised this gist Mar 26, 2020. 1 changed file with 3 additions and 2 deletions.
    5 changes: 3 additions & 2 deletions fcmproxy.go
    Original file line number Diff line number Diff line change
    @@ -119,12 +119,13 @@ func init() {
    }

    func newClientProxyFunc(srvAddrs []string) func(net.Conn) {
    return func(localConn net.Conn) {
    return func(conn net.Conn) {
    localConn := conn
    defer localConn.Close()

    sniHost, localConn, err := sniServerNameFromConn(localConn)
    if err != nil {
    log.Printf("sniServerNameFromConn from %s error: %v", localConn.RemoteAddr(), err)
    log.Printf("sniServerNameFromConn from %s error: %v", conn.RemoteAddr(), err)
    return
    }
    log.Printf("sniServernameFromConn got %s from %s", sniHost, localConn.RemoteAddr())
  6. ayanamist revised this gist Dec 14, 2019. 1 changed file with 73 additions and 0 deletions.
    73 changes: 73 additions & 0 deletions fcmproxy.go
    Original file line number Diff line number Diff line change
    @@ -1,12 +1,14 @@
    package main

    import (
    "bufio"
    "errors"
    "flag"
    "fmt"
    "io"
    "log"
    "net"
    "net/http"
    "net/url"
    "os"
    "os/signal"
    @@ -45,6 +47,77 @@ var (
    localPort uint
    )

    // httpProxy is a HTTP/HTTPS connect proxy.
    type httpProxy struct {
    host string
    haveAuth bool
    username string
    password string
    forward proxy.Dialer
    }

    func (s *httpProxy) Dial(network, addr string) (net.Conn, error) {
    // Dial and create the https client connection.
    c, err := s.forward.Dial("tcp", s.host)
    if err != nil {
    return nil, err
    }

    // HACK. http.ReadRequest also does this.
    reqURL, err := url.Parse("http://" + addr)
    if err != nil {
    c.Close()
    return nil, err
    }
    reqURL.Scheme = ""

    req, err := http.NewRequest("CONNECT", reqURL.String(), nil)
    if err != nil {
    c.Close()
    return nil, err
    }
    req.Close = false
    if s.haveAuth {
    req.SetBasicAuth(s.username, s.password)
    }

    err = req.Write(c)
    if err != nil {
    c.Close()
    return nil, err
    }

    resp, err := http.ReadResponse(bufio.NewReader(c), req)
    if err != nil {
    // TODO close resp body ?
    resp.Body.Close()
    c.Close()
    return nil, err
    }
    resp.Body.Close()
    if resp.StatusCode != 200 {
    c.Close()
    err = fmt.Errorf("Connect server using proxy error, StatusCode [%d]", resp.StatusCode)
    return nil, err
    }

    return c, nil
    }

    func init() {
    proxy.RegisterDialerType("http", func(u *url.URL, forward proxy.Dialer) (d proxy.Dialer, err error) {
    s := new(httpProxy)
    s.host = u.Host
    s.forward = forward
    if u.User != nil {
    s.haveAuth = true
    s.username = u.User.Username()
    s.password, _ = u.User.Password()
    }
    return s, nil
    })
    }

    func newClientProxyFunc(srvAddrs []string) func(net.Conn) {
    return func(localConn net.Conn) {
    defer localConn.Close()
  7. ayanamist revised this gist Feb 11, 2019. No changes.
  8. ayanamist revised this gist Feb 11, 2019. 1 changed file with 60 additions and 7 deletions.
    67 changes: 60 additions & 7 deletions fcmproxy.go
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,7 @@
    package main

    import (
    "errors"
    "flag"
    "fmt"
    "io"
    @@ -27,19 +28,14 @@ const (
    var (
    dialer = &net.Dialer{
    Timeout: connectTimeout,
    Resolver: &net.Resolver{
    PreferGo: true,
    },
    }

    ciph, _ = ss.NewCipher("rc4-md5", "fcmproxy")

    proxyStr string
    ciph *ss.Cipher

    proxyDial = func(network, addr string) (net.Conn, error) {
    conn, err := dialer.Dial(network, addr)
    if err == nil {
    if c, ok := conn.(*net.TCPConn); ok {
    if c, ok := conn.(*net.TCPConn); ok {
    c.SetNoDelay(true)
    }
    }
    @@ -194,13 +190,68 @@ func relay(src, dst net.Conn) {
    dst.Close()
    }

    type ssProxyDialer struct {
    server string
    ciph *ss.Cipher
    upstreamDialer proxy.Dialer
    }

    func (s *ssProxyDialer) Dial(network, addr string) (net.Conn, error) {
    rawaddr, err := ss.RawAddr(addr)
    if err != nil {
    return nil, err
    }
    conn, err := s.upstreamDialer.Dial("tcp", s.server)
    if err != nil {
    return nil, err
    }
    if c, ok := conn.(*net.TCPConn); ok {
    c.SetNoDelay(true)
    }
    ssConn := ss.NewConn(conn, s.ciph.Copy())
    if _, err := ssConn.Write(rawaddr); err != nil {
    conn.Close()
    return nil, err
    }
    log.Printf("dial %s via shadowsocks %s", addr, s.server)
    return ssConn, nil
    }

    func ssDial(u *url.URL, d proxy.Dialer) (proxy.Dialer, error) {
    if u.User == nil {
    return nil, errors.New("no shadowsocks method")
    }
    method := u.User.Username()
    p, _ := u.User.Password()
    ciph, err := ss.NewCipher(method, p)
    if err != nil {
    return nil, err
    }
    log.Printf("using proxy ss://%s:%s@%s", method, p, u.Host)
    return &ssProxyDialer{
    server: u.Host,
    ciph: ciph,
    upstreamDialer: d,
    }, nil
    }

    func init() {
    proxy.RegisterDialerType("ss", ssDial)
    }

    func main() {
    log.SetFlags(log.Lmicroseconds)

    go sigQuitHandle()

    var (
    proxyStr string
    shadowPass string
    )

    flag.StringVar(&proxyStr, "proxy", "", "Set proxy url")
    flag.UintVar(&localPort, "port", 5228, "Local port")
    flag.StringVar(&shadowPass, "key", "fcmproxy", "Encrypt key")

    flag.Parse()

    @@ -216,6 +267,8 @@ func main() {
    proxyDial = d.Dial
    }

    ciph, _ = ss.NewCipher("rc4-md5", shadowPass)

    srvAddr := flag.Args()
    var proxyFunc func(net.Conn)
    if len(srvAddr) > 0 {
  9. ayanamist renamed this gist Feb 11, 2019. 1 changed file with 7 additions and 4 deletions.
    11 changes: 7 additions & 4 deletions sniproxy.go → fcmproxy.go
    Original file line number Diff line number Diff line change
    @@ -43,7 +43,7 @@ var (
    c.SetNoDelay(true)
    }
    }
    return conn, nil
    return conn, err
    }

    localPort uint
    @@ -70,6 +70,7 @@ func newClientProxyFunc(srvAddrs []string) func(net.Conn) {
    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()
    @@ -107,20 +108,20 @@ func serverConnect(localAddr, remoteAddr string, connCh chan<- net.Conn, closeCh
    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)
    remoteConn.Close()
    sess.Close()
    return
    }

    stream, err := sess.Open()
    if err != nil {
    log.Printf("sess.Open from %s to %s error: %v", localAddr, remoteAddr, err)
    remoteConn.Close()
    sess.Close()
    return
    }

    select {
    case <-closeCh:
    remoteConn.Close()
    sess.Close()
    case connCh <- stream:
    }
    }
    @@ -146,6 +147,7 @@ func serverProxyFunc(localConn net.Conn) {
    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)
    @@ -164,6 +166,7 @@ func serverProxyFunc(localConn net.Conn) {
    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)
    }
  10. ayanamist revised this gist Feb 3, 2019. 1 changed file with 2 additions and 7 deletions.
    9 changes: 2 additions & 7 deletions sniproxy.go
    Original file line number Diff line number Diff line change
    @@ -5,7 +5,6 @@ import (
    "fmt"
    "io"
    "log"
    "math/rand"
    "net"
    "net/url"
    "os"
    @@ -16,9 +15,9 @@ import (
    "time"

    "github.com/hashicorp/yamux"
    "golang.org/x/net/proxy"
    "github.com/polvi/sni"
    ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
    "golang.org/x/net/proxy"
    )

    const (
    @@ -66,12 +65,8 @@ func newClientProxyFunc(srvAddrs []string) func(net.Conn) {
    closeCh := make(chan struct{})
    connectTimer := time.NewTimer(2 * connectTimeout)
    delayTimer := time.NewTimer(0)
    addrs := make([]string, len(srvAddrs))
    for i, randIdx := range rand.Perm(len(srvAddrs)) {
    addrs[i] = srvAddrs[randIdx]
    }
    LOOP:
    for _, srvAddr := range addrs {
    for _, srvAddr := range srvAddrs {
    select {
    case <-delayTimer.C:
    delayTimer.Reset(time.Second)
  11. ayanamist revised this gist Feb 3, 2019. 1 changed file with 30 additions and 12 deletions.
    42 changes: 30 additions & 12 deletions sniproxy.go
    Original file line number Diff line number Diff line change
    @@ -7,6 +7,7 @@ import (
    "log"
    "math/rand"
    "net"
    "net/url"
    "os"
    "os/signal"
    "runtime"
    @@ -15,13 +16,12 @@ import (
    "time"

    "github.com/hashicorp/yamux"
    "golang.org/x/net/proxy"
    "github.com/polvi/sni"
    ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
    )

    const (
    port = 5228

    connectTimeout = 30 * time.Second
    )

    @@ -37,13 +37,17 @@ var (

    proxyStr string

    proxyDial = func(addr string) (net.Conn, error) {
    conn, err := dialer.Dial("tcp", addr)
    proxyDial = func(network, addr string) (net.Conn, error) {
    conn, err := dialer.Dial(network, addr)
    if err == nil {
    conn.(*net.TCPConn).SetNoDelay(true)
    if c, ok := conn.(*net.TCPConn); ok {
    c.SetNoDelay(true)
    }
    }
    return conn, nil
    }

    localPort uint
    )

    func newClientProxyFunc(srvAddrs []string) func(net.Conn) {
    @@ -97,7 +101,7 @@ func newClientProxyFunc(srvAddrs []string) func(net.Conn) {
    }

    func serverConnect(localAddr, remoteAddr string, connCh chan<- net.Conn, closeCh <-chan struct{}) {
    remoteConn, err := proxyDial(remoteAddr)
    remoteConn, err := proxyDial("tcp", remoteAddr)
    if err != nil {
    log.Printf("dial from %s to %s error: %v", localAddr, remoteAddr, err)
    return
    @@ -132,10 +136,10 @@ func sniServerNameFromConn(conn net.Conn) (string, net.Conn, error) {
    return "", nil, fmt.Errorf("invalid sni: %v", err)
    }
    if !strings.Contains(host, ":") {
    host = fmt.Sprintf("%s:%d", host, port)
    host = fmt.Sprintf("%s:%d", host, localPort)
    }

    if !strings.HasSuffix(host, fmt.Sprintf(".google.com:%d", port)) {
    if !strings.HasSuffix(host, fmt.Sprintf(".google.com:%d", localPort)) {
    return "", nil, fmt.Errorf("not allowed host: %s", host)
    }
    return host, conn, nil
    @@ -165,7 +169,9 @@ func serverProxyFunc(localConn net.Conn) {
    log.Printf("dial from %s to %s error: %v", localConn.RemoteAddr(), remoteAddr, err)
    return
    }
    remoteConn.(*net.TCPConn).SetNoDelay(true)
    if c, ok := remoteConn.(*net.TCPConn); ok {
    c.SetNoDelay(true)
    }

    go relay(localStream, remoteConn)
    relay(remoteConn, localStream)
    @@ -196,10 +202,20 @@ func main() {
    go sigQuitHandle()

    flag.StringVar(&proxyStr, "proxy", "", "Set proxy url")
    flag.UintVar(&localPort, "port", 5228, "Local port")

    flag.Parse()

    if proxyStr != "" {
    // TODO
    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()
    @@ -212,7 +228,7 @@ func main() {
    log.Printf("working on server")
    }

    l, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
    l, err := net.Listen("tcp", fmt.Sprintf(":%d", localPort))
    if err != nil {
    log.Fatalf("listen: %v", err)
    }
    @@ -221,7 +237,9 @@ func main() {
    if err != nil {
    log.Fatalf("accept: %v", err)
    }
    conn.(*net.TCPConn).SetNoDelay(true)
    if c, ok := conn.(*net.TCPConn); ok {
    c.SetNoDelay(true)
    }
    go proxyFunc(conn)
    }
    }
  12. ayanamist revised this gist Oct 20, 2018. 2 changed files with 227 additions and 63 deletions.
    63 changes: 0 additions & 63 deletions gcmproxy.go
    Original file line number Diff line number Diff line change
    @@ -1,63 +0,0 @@
    package main

    import (
    "fmt"
    "io"
    "log"
    "net"
    "os"
    "strings"
    "time"

    "github.com/polvi/sni"
    )

    func main() {
    log.SetOutput(os.Stdout)
    dialer := &net.Dialer{
    Timeout: 5 * time.Second,
    }
    const port = 5228
    l, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
    if err != nil {
    log.Fatalf("listen: %v", err)
    }
    for {
    conn, err := l.Accept()
    if err != nil {
    log.Fatalf("accept: %v", err)
    }
    go func() {
    defer conn.Close()
    tcpConn := conn.(*net.TCPConn)
    tcpConn.SetKeepAlive(true)
    tcpConn.SetKeepAlivePeriod(time.Hour)
    host, conn, err := sni.ServerNameFromConn(conn)
    if err != nil {
    log.Printf("invalid sni: %v", err)
    return
    }
    if !strings.HasSuffix(host, ".google.com") {
    log.Printf("not allowed host: %s", host)
    return
    }
    remoteAddr := fmt.Sprintf("%s:%d", host, port)
    remoteConn, err := dialer.Dial("tcp", remoteAddr)
    if err != nil {
    log.Printf("dial %s: %v", remoteAddr, err)
    return
    }
    defer remoteConn.Close()
    remoteTcpConn := remoteConn.(*net.TCPConn)
    remoteTcpConn.SetKeepAlive(true)
    remoteTcpConn.SetKeepAlivePeriod(time.Hour)
    go func() {
    io.Copy(conn, remoteConn)
    remoteConn.Close()
    conn.Close()
    }()
    io.Copy(remoteConn, conn)
    }()
    }
    }

    227 changes: 227 additions & 0 deletions sniproxy.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,227 @@
    package main

    import (
    "flag"
    "fmt"
    "io"
    "log"
    "math/rand"
    "net"
    "os"
    "os/signal"
    "runtime"
    "strings"
    "syscall"
    "time"

    "github.com/hashicorp/yamux"
    "github.com/polvi/sni"
    ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
    )

    const (
    port = 5228

    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(addr string) (net.Conn, error) {
    conn, err := dialer.Dial("tcp", addr)
    if err == nil {
    conn.(*net.TCPConn).SetNoDelay(true)
    }
    return conn, nil
    }
    )

    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)
    addrs := make([]string, len(srvAddrs))
    for i, randIdx := range rand.Perm(len(srvAddrs)) {
    addrs[i] = srvAddrs[randIdx]
    }
    LOOP:
    for _, srvAddr := range addrs {
    select {
    case <-delayTimer.C:
    delayTimer.Reset(time.Second)
    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(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)
    remoteConn.Close()
    return
    }

    stream, err := sess.Open()
    if err != nil {
    log.Printf("sess.Open from %s to %s error: %v", localAddr, remoteAddr, err)
    remoteConn.Close()
    return
    }

    select {
    case <-closeCh:
    remoteConn.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, port)
    }

    if !strings.HasSuffix(host, fmt.Sprintf(".google.com:%d", port)) {
    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())
    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
    }
    remoteConn.(*net.TCPConn).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.Parse()

    if proxyStr != "" {
    // TODO
    }

    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", port))
    if err != nil {
    log.Fatalf("listen: %v", err)
    }
    for {
    conn, err := l.Accept()
    if err != nil {
    log.Fatalf("accept: %v", err)
    }
    conn.(*net.TCPConn).SetNoDelay(true)
    go proxyFunc(conn)
    }
    }
  13. ayanamist revised this gist Jun 18, 2018. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions gcmproxy.go
    Original file line number Diff line number Diff line change
    @@ -60,3 +60,4 @@ func main() {
    }()
    }
    }

  14. ayanamist created this gist Mar 18, 2018.
    62 changes: 62 additions & 0 deletions gcmproxy.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,62 @@
    package main

    import (
    "fmt"
    "io"
    "log"
    "net"
    "os"
    "strings"
    "time"

    "github.com/polvi/sni"
    )

    func main() {
    log.SetOutput(os.Stdout)
    dialer := &net.Dialer{
    Timeout: 5 * time.Second,
    }
    const port = 5228
    l, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
    if err != nil {
    log.Fatalf("listen: %v", err)
    }
    for {
    conn, err := l.Accept()
    if err != nil {
    log.Fatalf("accept: %v", err)
    }
    go func() {
    defer conn.Close()
    tcpConn := conn.(*net.TCPConn)
    tcpConn.SetKeepAlive(true)
    tcpConn.SetKeepAlivePeriod(time.Hour)
    host, conn, err := sni.ServerNameFromConn(conn)
    if err != nil {
    log.Printf("invalid sni: %v", err)
    return
    }
    if !strings.HasSuffix(host, ".google.com") {
    log.Printf("not allowed host: %s", host)
    return
    }
    remoteAddr := fmt.Sprintf("%s:%d", host, port)
    remoteConn, err := dialer.Dial("tcp", remoteAddr)
    if err != nil {
    log.Printf("dial %s: %v", remoteAddr, err)
    return
    }
    defer remoteConn.Close()
    remoteTcpConn := remoteConn.(*net.TCPConn)
    remoteTcpConn.SetKeepAlive(true)
    remoteTcpConn.SetKeepAlivePeriod(time.Hour)
    go func() {
    io.Copy(conn, remoteConn)
    remoteConn.Close()
    conn.Close()
    }()
    io.Copy(remoteConn, conn)
    }()
    }
    }