package gost import ( "bufio" "crypto/tls" "io" "net" "net/http" "strconv" "strings" "github.com/ginuerzh/gosocks4" "github.com/ginuerzh/gosocks5" "github.com/golang/glog" ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" "golang.org/x/crypto/ssh" ) type ProxyServer struct { Node ProxyNode Chain *ProxyChain TLSConfig *tls.Config selector *ServerSelector cipher *ss.Cipher ota bool } func NewProxyServer(node ProxyNode, chain *ProxyChain) *ProxyServer { certFile, keyFile := node.certFile(), node.keyFile() cert, err := LoadCertificate(certFile, keyFile) if err != nil { glog.Fatal(err) } config := &tls.Config{ Certificates: []tls.Certificate{cert}, } if chain == nil { chain = NewProxyChain() } var cipher *ss.Cipher var ota bool if node.Protocol == "ss" || node.Transport == "ssu" { var err error var method, password string if len(node.Users) > 0 { method = node.Users[0].Username() password, _ = node.Users[0].Password() } ota = node.getBool("ota") if strings.HasSuffix(method, "-auth") { ota = true method = strings.TrimSuffix(method, "-auth") } cipher, err = ss.NewCipher(method, password) if err != nil { glog.Fatal(err) } } return &ProxyServer{ Node: node, Chain: chain, TLSConfig: config, selector: &ServerSelector{ // socks5 server selector // methods that socks5 server supported methods: []uint8{ gosocks5.MethodNoAuth, gosocks5.MethodUserPass, MethodTLS, MethodTLSAuth, }, // Users: node.Users, TLSConfig: config, }, cipher: cipher, ota: ota, } } func (s *ProxyServer) Serve() error { var ln net.Listener var err error node := s.Node switch node.Transport { case "ws": // websocket connection return NewWebsocketServer(s).ListenAndServe() case "wss": // websocket security connection return NewWebsocketServer(s).ListenAndServeTLS(s.TLSConfig) case "tls": // tls connection ln, err = tls.Listen("tcp", node.Addr, s.TLSConfig) case "http2": // Standard HTTP2 proxy server, compatible with HTTP1.x. server := NewHttp2Server(s) server.Handler = http.HandlerFunc(server.HandleRequest) return server.ListenAndServeTLS(s.TLSConfig) case "tcp": // Local TCP port forwarding return NewTcpForwardServer(s).ListenAndServe() case "udp": // Local UDP port forwarding ttl, _ := strconv.Atoi(s.Node.Get("ttl")) if ttl <= 0 { ttl = DefaultTTL } return NewUdpForwardServer(s, ttl).ListenAndServe() case "rtcp": // Remote TCP port forwarding return NewRTcpForwardServer(s).Serve() case "rudp": // Remote UDP port forwarding return NewRUdpForwardServer(s).Serve() case "quic": return NewQuicServer(s).ListenAndServeTLS(s.TLSConfig) case "kcp": config, err := ParseKCPConfig(s.Node.Get("c")) if err != nil { glog.V(LWARNING).Infoln("[kcp]", err) } if config == nil { config = DefaultKCPConfig } // override crypt and key if specified explicitly if s.Node.Users != nil { config.Crypt = s.Node.Users[0].Username() config.Key, _ = s.Node.Users[0].Password() } return NewKCPServer(s, config).ListenAndServe() case "redirect": return NewRedsocksTCPServer(s).ListenAndServe() case "ssu": // shadowsocks udp relay ttl, _ := strconv.Atoi(s.Node.Get("ttl")) if ttl <= 0 { ttl = DefaultTTL } return NewShadowUdpServer(s, ttl).ListenAndServe() case "pht": // pure http tunnel return NewPureHttpServer(s).ListenAndServe() case "ssh": // SSH tunnel /* key := s.Node.Get("key") privateBytes, err := ioutil.ReadFile(key) if err != nil { glog.V(LWARNING).Infoln("[ssh]", err) privateBytes = defaultRawKey } private, err := ssh.ParsePrivateKey(privateBytes) if err != nil { return err } */ config := ssh.ServerConfig{ PasswordCallback: DefaultPasswordCallback(s.Node.Users), } if len(s.Node.Users) == 0 { config.NoClientAuth = true } signer, err := ssh.NewSignerFromKey(s.TLSConfig.Certificates[0].PrivateKey) if err != nil { return err } config.AddHostKey(signer) s := &SSHServer{ Addr: node.Addr, Base: s, Config: &config, } return s.ListenAndServe() default: ln, err = net.Listen("tcp", node.Addr) } if err != nil { return err } defer ln.Close() for { conn, err := ln.Accept() if err != nil { glog.V(LWARNING).Infoln(err) continue } setKeepAlive(conn, KeepAliveTime) go s.handleConn(conn) } } func (s *ProxyServer) handleConn(conn net.Conn) { defer conn.Close() switch s.Node.Protocol { case "ss": // shadowsocks //server := NewShadowServer(ss.NewConn(conn, s.cipher.Copy()), s) //server.OTA = s.ota //server.Serve() return case "http": req, err := http.ReadRequest(bufio.NewReader(conn)) if err != nil { glog.V(LWARNING).Infoln("[http]", err) return } NewHttpServer(conn, s).HandleRequest(req) return case "socks", "socks5": conn = gosocks5.ServerConn(conn, s.selector) req, err := gosocks5.ReadRequest(conn) if err != nil { glog.V(LWARNING).Infoln("[socks5]", err) return } NewSocks5Server(conn, s).HandleRequest(req) return case "socks4", "socks4a": req, err := gosocks4.ReadRequest(conn) if err != nil { glog.V(LWARNING).Infoln("[socks4]", err) return } NewSocks4Server(conn, s).HandleRequest(req) return } br := bufio.NewReader(conn) b, err := br.Peek(1) if err != nil { glog.V(LWARNING).Infoln(err) return } switch b[0] { case gosocks4.Ver4: req, err := gosocks4.ReadRequest(br) if err != nil { glog.V(LWARNING).Infoln("[socks4]", err) return } NewSocks4Server(conn, s).HandleRequest(req) case gosocks5.Ver5: methods, err := gosocks5.ReadMethods(br) if err != nil { glog.V(LWARNING).Infoln("[socks5]", err) return } method := s.selector.Select(methods...) if _, err := conn.Write([]byte{gosocks5.Ver5, method}); err != nil { glog.V(LWARNING).Infoln("[socks5] select:", err) return } c, err := s.selector.OnSelected(method, conn) if err != nil { glog.V(LWARNING).Infoln("[socks5] onselected:", err) return } conn = c req, err := gosocks5.ReadRequest(conn) if err != nil { glog.V(LWARNING).Infoln("[socks5] request:", err) return } NewSocks5Server(conn, s).HandleRequest(req) default: // http req, err := http.ReadRequest(br) if err != nil { glog.V(LWARNING).Infoln("[http]", err) return } NewHttpServer(conn, s).HandleRequest(req) } } func (_ *ProxyServer) transport(conn1, conn2 net.Conn) (err error) { errc := make(chan error, 2) go func() { _, err := io.Copy(conn1, conn2) errc <- err }() go func() { _, err := io.Copy(conn2, conn1) errc <- err }() select { case err = <-errc: // glog.V(LWARNING).Infoln("transport exit", err) } return }