From 3b18d66835b8c34cecef8314664913fd94b980e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=B2=B3?= Date: Sat, 16 Feb 2019 20:43:26 +0800 Subject: [PATCH] =?UTF-8?q?Ip=E9=99=90=E5=88=B6=20npc=E4=BB=A3=E7=90=86?= =?UTF-8?q?=E8=BF=9E=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bridge/bridge.go | 63 +++- client/client.go | 34 +- client/client_test.go | 8 +- client/register.go | 20 ++ client/status.go | 52 +++ cmd/npc/npc.go | 64 +--- cmd/nps/nps.go | 2 +- conf/app.conf | 5 +- conf/hosts.csv | 4 +- conf/npc.conf | 7 +- conf/tasks.csv | 1 + lib/common/const.go | 1 + lib/common/run.go | 2 +- lib/common/util.go | 5 + lib/config/config.go | 5 + lib/conn/conn.go | 15 +- lib/conn/snappy.go | 2 +- lib/daemon/daemon.go | 2 +- lib/file/csv.go | 1 + lib/file/file.go | 10 +- lib/install/install.go | 2 +- server/proxy/http.go | 29 +- server/proxy/socks5.go | 5 +- server/proxy/tcp.go | 9 +- server/proxy/udp.go | 2 +- server/server.go | 17 +- server/test/test.go | 2 +- server/tool/utils.go | 2 +- .../github.com/golang}/snappy/decode.go | 0 .../github.com/golang}/snappy/decode_amd64.go | 0 .../github.com/golang}/snappy/decode_amd64.s | 0 .../github.com/golang}/snappy/decode_other.go | 0 .../github.com/golang}/snappy/encode.go | 0 .../github.com/golang}/snappy/encode_amd64.go | 0 .../github.com/golang}/snappy/encode_amd64.s | 0 .../github.com/golang}/snappy/encode_other.go | 0 .../github.com/golang}/snappy/golden_test.go | 0 .../github.com/golang}/snappy/snappy.go | 0 .../github.com/golang}/snappy/snappy_test.go | 0 {lib => vender/github.com/xtaci}/kcp/crypt.go | 0 .../github.com/xtaci}/kcp/crypt_test.go | 0 .../github.com/xtaci}/kcp/entropy.go | 0 {lib => vender/github.com/xtaci}/kcp/fec.go | 0 .../github.com/xtaci}/kcp/fec_test.go | 0 {lib => vender/github.com/xtaci}/kcp/kcp.go | 0 .../github.com/xtaci}/kcp/kcp_test.go | 0 {lib => vender/github.com/xtaci}/kcp/sess.go | 0 .../github.com/xtaci}/kcp/sess_test.go | 0 {lib => vender/github.com/xtaci}/kcp/snmp.go | 0 .../github.com/xtaci}/kcp/updater.go | 0 .../golang.org/x/net/internal/socks/client.go | 168 ++++++++++ .../x/net/internal/socks/dial_test.go | 170 ++++++++++ .../golang.org/x/net/internal/socks/socks.go | 317 ++++++++++++++++++ vender/golang.org/x/net/proxy/direct.go | 18 + vender/golang.org/x/net/proxy/per_host.go | 140 ++++++++ .../golang.org/x/net/proxy/per_host_test.go | 55 +++ vender/golang.org/x/net/proxy/proxy.go | 134 ++++++++ vender/golang.org/x/net/proxy/proxy_test.go | 123 +++++++ vender/golang.org/x/net/proxy/socks5.go | 36 ++ web/controllers/base.go | 4 +- web/controllers/index.go | 4 + web/controllers/login.go | 2 +- web/routers/router.go | 2 +- web/views/index/hlist.html | 2 + 64 files changed, 1414 insertions(+), 132 deletions(-) create mode 100644 client/register.go create mode 100644 client/status.go rename {lib => vender/github.com/golang}/snappy/decode.go (100%) rename {lib => vender/github.com/golang}/snappy/decode_amd64.go (100%) rename {lib => vender/github.com/golang}/snappy/decode_amd64.s (100%) rename {lib => vender/github.com/golang}/snappy/decode_other.go (100%) rename {lib => vender/github.com/golang}/snappy/encode.go (100%) rename {lib => vender/github.com/golang}/snappy/encode_amd64.go (100%) rename {lib => vender/github.com/golang}/snappy/encode_amd64.s (100%) rename {lib => vender/github.com/golang}/snappy/encode_other.go (100%) rename {lib => vender/github.com/golang}/snappy/golden_test.go (100%) rename {lib => vender/github.com/golang}/snappy/snappy.go (100%) rename {lib => vender/github.com/golang}/snappy/snappy_test.go (100%) rename {lib => vender/github.com/xtaci}/kcp/crypt.go (100%) rename {lib => vender/github.com/xtaci}/kcp/crypt_test.go (100%) rename {lib => vender/github.com/xtaci}/kcp/entropy.go (100%) rename {lib => vender/github.com/xtaci}/kcp/fec.go (100%) rename {lib => vender/github.com/xtaci}/kcp/fec_test.go (100%) rename {lib => vender/github.com/xtaci}/kcp/kcp.go (100%) rename {lib => vender/github.com/xtaci}/kcp/kcp_test.go (100%) rename {lib => vender/github.com/xtaci}/kcp/sess.go (100%) rename {lib => vender/github.com/xtaci}/kcp/sess_test.go (100%) rename {lib => vender/github.com/xtaci}/kcp/snmp.go (100%) rename {lib => vender/github.com/xtaci}/kcp/updater.go (100%) create mode 100644 vender/golang.org/x/net/internal/socks/client.go create mode 100644 vender/golang.org/x/net/internal/socks/dial_test.go create mode 100644 vender/golang.org/x/net/internal/socks/socks.go create mode 100644 vender/golang.org/x/net/proxy/direct.go create mode 100644 vender/golang.org/x/net/proxy/per_host.go create mode 100644 vender/golang.org/x/net/proxy/per_host_test.go create mode 100644 vender/golang.org/x/net/proxy/proxy.go create mode 100644 vender/golang.org/x/net/proxy/proxy_test.go create mode 100644 vender/golang.org/x/net/proxy/socks5.go diff --git a/bridge/bridge.go b/bridge/bridge.go index ac34d5f..e378f55 100755 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -3,14 +3,15 @@ package bridge import ( "encoding/binary" "errors" + "fmt" "github.com/cnlh/nps/lib/common" "github.com/cnlh/nps/lib/conn" "github.com/cnlh/nps/lib/crypt" "github.com/cnlh/nps/lib/file" - "github.com/cnlh/nps/lib/kcp" "github.com/cnlh/nps/lib/lg" "github.com/cnlh/nps/lib/pool" "github.com/cnlh/nps/server/tool" + "github.com/cnlh/nps/vender/github.com/xtaci/kcp" "log" "net" "strconv" @@ -38,25 +39,28 @@ func NewClient(t *conn.Conn, s *conn.Conn) *Client { } type Bridge struct { - TunnelPort int //通信隧道端口 - tcpListener *net.TCPListener //server端监听 - kcpListener *kcp.Listener //server端监听 - Client map[int]*Client - tunnelType string //bridge type kcp or tcp - OpenTask chan *file.Tunnel - CloseClient chan int - lock sync.Mutex - tunnelLock sync.Mutex - clientLock sync.RWMutex + TunnelPort int //通信隧道端口 + tcpListener *net.TCPListener //server端监听 + kcpListener *kcp.Listener //server端监听 + Client map[int]*Client + tunnelType string //bridge type kcp or tcp + OpenTask chan *file.Tunnel + CloseClient chan int + clientLock sync.RWMutex + Register map[string]time.Time + registerLock sync.RWMutex + ipVerify bool } -func NewTunnel(tunnelPort int, tunnelType string) *Bridge { +func NewTunnel(tunnelPort int, tunnelType string, ipVerify bool) *Bridge { t := new(Bridge) t.TunnelPort = tunnelPort t.Client = make(map[int]*Client) t.tunnelType = tunnelType t.OpenTask = make(chan *file.Tunnel) t.CloseClient = make(chan int) + t.Register = make(map[string]time.Time) + t.ipVerify = ipVerify return t } @@ -128,7 +132,6 @@ func (s *Bridge) cliProcess(c *conn.Conn) { if flag, err := c.ReadFlag(); err == nil { s.typeDeal(flag, c, id) } else { - log.Println(222) log.Println(err, flag) } return @@ -180,13 +183,25 @@ func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int) { go s.clientCopy(id) case common.WORK_CONFIG: go s.GetConfig(c) + case common.WORK_REGISTER: + go s.register(c) } c.SetAlive(s.tunnelType) return } +func (s *Bridge) register(c *conn.Conn) { + var hour int32 + if err := binary.Read(c, binary.LittleEndian, &hour); err == nil { + s.registerLock.Lock() + s.Register[common.GetIpByAddr(c.Conn.RemoteAddr().String())] = time.Now().Add(time.Hour * time.Duration(hour)) + lg.Println(s.Register[common.GetIpByAddr(c.Conn.RemoteAddr().String())]) + s.registerLock.Unlock() + } +} + //等待 -func (s *Bridge) waitStatus(clientId, id int) (bool) { +func (s *Bridge) waitStatus(clientId, id int) bool { ticker := time.NewTicker(time.Millisecond * 100) stop := time.After(time.Second * 10) for { @@ -209,13 +224,26 @@ func (s *Bridge) waitStatus(clientId, id int) (bool) { return false } } - return false } -func (s *Bridge) SendLinkInfo(clientId int, link *conn.Link) (tunnel *conn.Conn, err error) { +func (s *Bridge) SendLinkInfo(clientId int, link *conn.Link, linkAddr string) (tunnel *conn.Conn, err error) { s.clientLock.Lock() if v, ok := s.Client[clientId]; ok { s.clientLock.Unlock() + if s.ipVerify { + s.registerLock.Lock() + ip := common.GetIpByAddr(linkAddr) + if v, ok := s.Register[ip]; !ok { + s.registerLock.Unlock() + return nil, errors.New(fmt.Sprintf("The ip %s is not in the validation list", ip)) + } else { + if !v.After(time.Now()) { + return nil, errors.New(fmt.Sprintf("The validity of the ip %s has expired", ip)) + } + } + s.registerLock.Unlock() + } + v.signal.SendLinkInfo(link) if err != nil { lg.Println("send link information error:", err, link.Id) @@ -300,7 +328,7 @@ func (s *Bridge) GetConfig(c *conn.Conn) { fail = true c.WriteAddFail() break - } else if file.GetCsvDb().IsHostExist(h.Host) { + } else if file.GetCsvDb().IsHostExist(h) { fail = true c.WriteAddFail() } else { @@ -383,7 +411,6 @@ func (s *Bridge) clientCopy(clientId int) { for { if id, err := client.tunnel.GetLen(); err != nil { s.closeClient(clientId) - lg.Println("读取msg id 错误", err, id) break } else { client.Lock() diff --git a/client/client.go b/client/client.go index 2f0988b..1de78bf 100755 --- a/client/client.go +++ b/client/client.go @@ -5,11 +5,13 @@ import ( "github.com/cnlh/nps/lib/common" "github.com/cnlh/nps/lib/config" "github.com/cnlh/nps/lib/conn" - "github.com/cnlh/nps/lib/kcp" "github.com/cnlh/nps/lib/lg" "github.com/cnlh/nps/lib/pool" + "github.com/cnlh/nps/vender/github.com/xtaci/kcp" + "github.com/cnlh/nps/vender/golang.org/x/net/proxy" "io/ioutil" "net" + "net/url" "path/filepath" "sync" "time" @@ -21,12 +23,13 @@ type TRPClient struct { tunnel *conn.Conn bridgeConnType string stop chan bool + proxyUrl string sync.Mutex vKey string } //new client -func NewRPClient(svraddr string, vKey string, bridgeConnType string) *TRPClient { +func NewRPClient(svraddr string, vKey string, bridgeConnType string, proxyUrl string) *TRPClient { return &TRPClient{ svrAddr: svraddr, linkMap: make(map[int]*conn.Link), @@ -34,13 +37,14 @@ func NewRPClient(svraddr string, vKey string, bridgeConnType string) *TRPClient vKey: vKey, bridgeConnType: bridgeConnType, stop: make(chan bool), + proxyUrl: proxyUrl, } } //start func (s *TRPClient) Start() { retry: - c, err := NewConn(s.bridgeConnType, s.vKey, s.svrAddr, common.WORK_MAIN) + c, err := NewConn(s.bridgeConnType, s.vKey, s.svrAddr, common.WORK_MAIN, s.proxyUrl) if err != nil { lg.Println("The connection server failed and will be reconnected in five seconds") time.Sleep(time.Second * 5) @@ -130,7 +134,7 @@ func (s *TRPClient) linkProcess(link *conn.Link, c *conn.Conn) { //隧道模式处理 func (s *TRPClient) dealChan() { var err error - s.tunnel, err = NewConn(s.bridgeConnType, s.vKey, s.svrAddr, common.WORK_CHAN) + s.tunnel, err = NewConn(s.bridgeConnType, s.vKey, s.svrAddr, common.WORK_CHAN, s.proxyUrl) if err != nil { lg.Println("connect to ", s.svrAddr, "error:", err) return @@ -184,7 +188,7 @@ re: return } first = false - c, err := NewConn(cnf.CommonConfig.Tp, cnf.CommonConfig.VKey, cnf.CommonConfig.Server, common.WORK_CONFIG) + c, err := NewConn(cnf.CommonConfig.Tp, cnf.CommonConfig.VKey, cnf.CommonConfig.Server, common.WORK_CONFIG, cnf.CommonConfig.ProxyUrl) if err != nil { lg.Println(err) goto re @@ -220,24 +224,36 @@ re: goto re } if !c.GetAddStatus() { - lg.Println(errAdd, v.Port) + lg.Println(errAdd, v.Ports) goto re } } c.Close() - NewRPClient(cnf.CommonConfig.Server, string(b), cnf.CommonConfig.Tp).Start() + NewRPClient(cnf.CommonConfig.Server, string(b), cnf.CommonConfig.Tp, cnf.CommonConfig.ProxyUrl).Start() goto re } //Create a new connection with the server and verify it -func NewConn(tp string, vkey string, server string, connType string) (*conn.Conn, error) { +func NewConn(tp string, vkey string, server string, connType string, proxyUrl string) (*conn.Conn, error) { var err error var connection net.Conn var sess *kcp.UDPSession if tp == "tcp" { - connection, err = net.Dial("tcp", server) + if proxyUrl != "" { + u, er := url.Parse(proxyUrl) + if er != nil { + return nil, er + } + n, er := proxy.FromURL(u, nil) + if er != nil { + return nil, er + } + connection, err = n.Dial("tcp", server) + } else { + connection, err = net.Dial("tcp", server) + } } else { sess, err = kcp.DialWithOptions(server, nil, 10, 3) conn.SetUdpSession(sess) diff --git a/client/client_test.go b/client/client_test.go index aa51af4..574a02a 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -42,10 +42,10 @@ func TestConfig(t *testing.T) { RWMutex: sync.RWMutex{}, } tunnel := &file.Tunnel{ - Port: 9001, - Mode: "tcpServer", - Target: "127.0.0.1:8082", - Remark: "333", + Port: 9001, + Mode: "tcpServer", + Target: "127.0.0.1:8082", + Remark: "333", } var b []byte if b, err = c.ReadLen(16); err != nil { diff --git a/client/register.go b/client/register.go new file mode 100644 index 0000000..2d994aa --- /dev/null +++ b/client/register.go @@ -0,0 +1,20 @@ +package client + +import ( + "encoding/binary" + "github.com/cnlh/nps/lib/common" + "log" + "os" +) + +func RegisterLocalIp(server string, vKey string, tp string, proxyUrl string, hour int) { + c, err := NewConn(tp, vKey, server, common.WORK_REGISTER, proxyUrl) + if err != nil { + log.Fatalln(err) + } + if err := binary.Write(c, binary.LittleEndian, int32(hour)); err != nil { + log.Fatalln(err) + } + log.Printf("Successful ip registration for local public network, the validity period is %d hours.", hour) + os.Exit(0) +} diff --git a/client/status.go b/client/status.go new file mode 100644 index 0000000..0f3a5f4 --- /dev/null +++ b/client/status.go @@ -0,0 +1,52 @@ +package client + +import ( + "github.com/cnlh/nps/lib/common" + "github.com/cnlh/nps/lib/config" + "github.com/cnlh/nps/lib/lg" + "log" + "os" + "path/filepath" + "strings" +) + +func GetTaskStatus(path string) { + cnf, err := config.NewConfig(path) + if err != nil { + log.Fatalln(err) + } + c, err := NewConn(cnf.CommonConfig.Tp, cnf.CommonConfig.VKey, cnf.CommonConfig.Server, common.WORK_CONFIG, cnf.CommonConfig.ProxyUrl) + if err != nil { + log.Fatalln(err) + } + if _, err := c.Write([]byte(common.WORK_STATUS)); err != nil { + log.Fatalln(err) + } + if f, err := common.ReadAllFromFile(filepath.Join(common.GetTmpPath(), "npc_vkey.txt")); err != nil { + log.Fatalln(err) + } else if _, err := c.Write([]byte(string(f))); err != nil { + log.Fatalln(err) + } + if l, err := c.GetLen(); err != nil { + log.Fatalln(err) + } else if b, err := c.ReadLen(l); err != nil { + lg.Fatalln(err) + } else { + arr := strings.Split(string(b), common.CONN_DATA_SEQ) + for _, v := range cnf.Hosts { + if common.InArr(arr, v.Remark) { + log.Println(v.Remark, "ok") + } else { + log.Println(v.Remark, "not running") + } + } + for _, v := range cnf.Tasks { + if common.InArr(arr, v.Remark) { + log.Println(v.Remark, "ok") + } else { + log.Println(v.Remark, "not running") + } + } + } + os.Exit(0) +} diff --git a/cmd/npc/npc.go b/cmd/npc/npc.go index 252bbed..5a9e807 100644 --- a/cmd/npc/npc.go +++ b/cmd/npc/npc.go @@ -4,23 +4,23 @@ import ( "flag" "github.com/cnlh/nps/client" "github.com/cnlh/nps/lib/common" - "github.com/cnlh/nps/lib/config" "github.com/cnlh/nps/lib/daemon" "github.com/cnlh/nps/lib/lg" - "log" "os" - "path/filepath" "strings" + "time" ) const VERSION = "v0.0.15" var ( - serverAddr = flag.String("server", "", "Server addr (ip:port)") - configPath = flag.String("config", "npc.conf", "Configuration file path") - verifyKey = flag.String("vkey", "", "Authentication key") - logType = flag.String("log", "stdout", "Log output mode(stdout|file)") - connType = flag.String("type", "tcp", "Connection type with the server(kcp|tcp)") + serverAddr = flag.String("server", "", "Server addr (ip:port)") + configPath = flag.String("config", "npc.conf", "Configuration file path") + verifyKey = flag.String("vkey", "", "Authentication key") + logType = flag.String("log", "stdout", "Log output mode(stdout|file)") + connType = flag.String("type", "tcp", "Connection type with the server(kcp|tcp)") + proxyUrl = flag.String("proxy", "", "proxy socks5 url(eg:socks5://111:222@127.0.0.1:9007)") + registerTime = flag.Int("time", 2, "register time long /h") ) func main() { @@ -29,44 +29,10 @@ func main() { switch os.Args[1] { case "status": path := strings.Replace(os.Args[2], "-config=", "", -1) - cnf, err := config.NewConfig(path) - if err != nil { - log.Fatalln(err) - } - c, err := client.NewConn(cnf.CommonConfig.Tp, cnf.CommonConfig.VKey, cnf.CommonConfig.Server, common.WORK_CONFIG) - if err != nil { - log.Fatalln(err) - } - if _, err := c.Write([]byte(common.WORK_STATUS)); err != nil { - log.Fatalln(err) - } - if f, err := common.ReadAllFromFile(filepath.Join(common.GetTmpPath(), "npc_vkey.txt")); err != nil { - log.Fatalln(err) - } else if _, err := c.Write([]byte(string(f))); err != nil { - log.Fatalln(err) - } - if l, err := c.GetLen(); err != nil { - log.Fatalln(err) - } else if b, err := c.ReadLen(l); err != nil { - lg.Fatalln(err) - } else { - arr := strings.Split(string(b), common.CONN_DATA_SEQ) - for _, v := range cnf.Hosts { - if common.InArr(arr, v.Remark) { - log.Println(v.Remark, "ok") - } else { - log.Println(v.Remark, "not running") - } - } - for _, v := range cnf.Tasks { - if common.InArr(arr, v.Remark) { - log.Println(v.Remark, "ok") - } else { - log.Println(v.Remark, "not running") - } - } - } - return + client.GetTaskStatus(path) + case "register": + flag.CommandLine.Parse(os.Args[2:]) + client.RegisterLocalIp(*serverAddr, *verifyKey, *connType, *proxyUrl, *registerTime) } } daemon.InitDaemon("npc", common.GetRunPath(), common.GetTmpPath()) @@ -76,7 +42,11 @@ func main() { lg.InitLogFile("npc", false, common.GetLogPath()) } if *verifyKey != "" && *serverAddr != "" { - client.NewRPClient(*serverAddr, *verifyKey, *connType).Start() + for { + client.NewRPClient(*serverAddr, *verifyKey, *connType, *proxyUrl).Start() + lg.Println("It will be reconnected in five seconds") + time.Sleep(time.Second * 5) + } } else { client.StartFromFile(*configPath) } diff --git a/cmd/nps/nps.go b/cmd/nps/nps.go index 889b790..cfe4073 100644 --- a/cmd/nps/nps.go +++ b/cmd/nps/nps.go @@ -2,7 +2,6 @@ package main import ( "flag" - "github.com/cnlh/nps/lib/beego" "github.com/cnlh/nps/lib/common" "github.com/cnlh/nps/lib/daemon" "github.com/cnlh/nps/lib/file" @@ -10,6 +9,7 @@ import ( "github.com/cnlh/nps/lib/lg" "github.com/cnlh/nps/server" "github.com/cnlh/nps/server/test" + "github.com/cnlh/nps/vender/github.com/astaxie/beego" _ "github.com/cnlh/nps/web/routers" "log" "os" diff --git a/conf/app.conf b/conf/app.conf index 9e55cc4..2577004 100755 --- a/conf/app.conf +++ b/conf/app.conf @@ -37,4 +37,7 @@ bridgeType=tcp publicVkey=123 #Open ports allowed on the server side -allowPorts=9001-9100,10001,11000-12000 \ No newline at end of file +allowPorts=9001-9100,10001,11000-12000 + + +ipLimit=true \ No newline at end of file diff --git a/conf/hosts.csv b/conf/hosts.csv index 8d7d25d..9a61b11 100644 --- a/conf/hosts.csv +++ b/conf/hosts.csv @@ -1 +1,3 @@ -a.o.com,127.0.0.1:8080,7,,,,,1 +a.o.com,127.0.0.1:8080,7,,,,/,1 +a.o.com,127.0.0.1:7002,7,,,,/test,3 +b.o.com,127.0.0.1:8082,7,,,,,4 diff --git a/conf/npc.conf b/conf/npc.conf index e834271..79c9dbe 100644 --- a/conf/npc.conf +++ b/conf/npc.conf @@ -1,15 +1,18 @@ [common] -server=127.0.0.1:8284 +server=123.206.77.88:8284 tp=tcp vkey=123 compress=snappy crypt=true auto_reconnection=true - +username=111 +password=222 +proxy_socks5_url=socks5://111:222@118.89.159.126:8024 [web1] host=a.o.com host_change=www.proxy.com target=127.0.0.1:8080 +location=/test2 [web2] host=a.proxy.com diff --git a/conf/tasks.csv b/conf/tasks.csv index e69de29..c0a0acf 100644 --- a/conf/tasks.csv +++ b/conf/tasks.csv @@ -0,0 +1 @@ +9010,socks5Server,,1,27,7, diff --git a/lib/common/const.go b/lib/common/const.go index 6a2d430..aaa463d 100644 --- a/lib/common/const.go +++ b/lib/common/const.go @@ -11,6 +11,7 @@ const ( WORK_MAIN = "main" WORK_CHAN = "chan" WORK_CONFIG = "conf" + WORK_REGISTER = "rgst" WORK_STATUS = "stus" RES_SIGN = "sign" RES_MSG = "msg0" diff --git a/lib/common/run.go b/lib/common/run.go index b6261c5..7d19e85 100644 --- a/lib/common/run.go +++ b/lib/common/run.go @@ -59,7 +59,7 @@ func GetLogPath() string { func GetTmpPath() string { var path string if IsWindows() { - path = "./" + path = GetRunPath() } else { path = "/tmp" } diff --git a/lib/common/util.go b/lib/common/util.go index 9534a94..e1a106b 100755 --- a/lib/common/util.go +++ b/lib/common/util.go @@ -241,3 +241,8 @@ func FormatAddress(s string) string { } return "127.0.0.1:" + s } + +func GetIpByAddr(addr string) string { + arr := strings.Split(addr, ":") + return arr[0] +} diff --git a/lib/config/config.go b/lib/config/config.go index 5c87527..4fafad4 100644 --- a/lib/config/config.go +++ b/lib/config/config.go @@ -13,6 +13,7 @@ type CommonConfig struct { Tp string //bridgeType kcp or tcp AutoReconnection bool Cnf *file.Config + ProxyUrl string } type Config struct { content string @@ -94,6 +95,8 @@ func dealCommon(s string) *CommonConfig { c.Cnf.Compress = item[1] case "crypt": c.Cnf.Crypt = common.GetBoolByStr(item[1]) + case "proxy_socks5_url": + c.ProxyUrl = item[1] } } return c @@ -115,6 +118,8 @@ func dealHost(s string) *file.Host { h.Target = strings.Replace(item[1], ",", "\n", -1) case "host_change": h.HostChange = item[1] + case "location": + h.Location = item[1] default: if strings.Contains(item[0], "header") { headerChange += strings.Replace(item[0], "header_", "", -1) + ":" + item[1] + "\n" diff --git a/lib/conn/conn.go b/lib/conn/conn.go index 5594b77..72f2a89 100755 --- a/lib/conn/conn.go +++ b/lib/conn/conn.go @@ -7,9 +7,9 @@ import ( "errors" "github.com/cnlh/nps/lib/common" "github.com/cnlh/nps/lib/file" - "github.com/cnlh/nps/lib/kcp" "github.com/cnlh/nps/lib/pool" "github.com/cnlh/nps/lib/rate" + "github.com/cnlh/nps/vender/github.com/xtaci/kcp" "io" "net" "net/http" @@ -82,7 +82,7 @@ func (s *Conn) ReadLen(cLen int) ([]byte, error) { defer pool.BufPoolMax.Put(buf) } if n, err := io.ReadFull(s, buf); err != nil || n != cLen { - return buf, errors.New("Error reading specified length" + err.Error()) + return buf, errors.New("Error reading specified length " + err.Error()) } return buf, nil } @@ -187,7 +187,7 @@ func (s *Conn) SendMsg(content []byte, link *Link) (n int, err error) { +----+--------+ | 4 | ... | +----+--------+ -*/ + */ s.Lock() defer s.Unlock() raw := bytes.NewBuffer([]byte{}) @@ -273,10 +273,10 @@ func (s *Conn) SendHostInfo(h *file.Host) (int, error) { +----+---------------+ | 4 | 4 | ... | +----+---------------+ -*/ + */ raw := bytes.NewBuffer([]byte{}) binary.Write(raw, binary.LittleEndian, []byte(common.NEW_HOST)) - common.BinaryWrite(raw, h.Host, h.Target, h.HeaderChange, h.HostChange, h.Remark) + common.BinaryWrite(raw, h.Host, h.Target, h.HeaderChange, h.HostChange, h.Remark, h.Location) s.Lock() defer s.Unlock() return s.Write(raw.Bytes()) @@ -312,6 +312,7 @@ func (s *Conn) GetHostInfo() (h *file.Host, err error) { h.HeaderChange = arr[2] h.HostChange = arr[3] h.Remark = arr[4] + h.Location = arr[5] h.Flow = new(file.Flow) h.NoStore = true } @@ -327,7 +328,7 @@ func (s *Conn) SendConfigInfo(c *file.Config) (int, error) { +----+---------------+ | 4 | 4 | ... | +----+---------------+ -*/ + */ raw := bytes.NewBuffer([]byte{}) binary.Write(raw, binary.LittleEndian, []byte(common.NEW_CONF)) common.BinaryWrite(raw, c.U, c.P, common.GetStrByBool(c.Crypt), c.Compress) @@ -365,7 +366,7 @@ func (s *Conn) SendTaskInfo(t *file.Tunnel) (int, error) { +----+---------------+ | 4 | 4 | ... | +----+---------------+ -*/ + */ raw := bytes.NewBuffer([]byte{}) binary.Write(raw, binary.LittleEndian, []byte(common.NEW_TASK)) common.BinaryWrite(raw, t.Mode, t.Ports, t.Target, t.Remark) diff --git a/lib/conn/snappy.go b/lib/conn/snappy.go index 7750c1b..b6cf84e 100644 --- a/lib/conn/snappy.go +++ b/lib/conn/snappy.go @@ -5,7 +5,7 @@ import ( "github.com/cnlh/nps/lib/lg" "github.com/cnlh/nps/lib/pool" "github.com/cnlh/nps/lib/rate" - "github.com/cnlh/nps/lib/snappy" + "github.com/cnlh/nps/vender/github.com/golang/snappy" "log" "net" ) diff --git a/lib/daemon/daemon.go b/lib/daemon/daemon.go index 7d15e73..2155971 100644 --- a/lib/daemon/daemon.go +++ b/lib/daemon/daemon.go @@ -49,7 +49,7 @@ func status(f string, pidPath string) bool { if !common.IsWindows() { cmd = exec.Command("/bin/sh", "-c", "ps -ax | awk '{ print $1 }' | grep "+string(b)) } else { - cmd = exec.Command("tasklist", ) + cmd = exec.Command("tasklist") } out, _ := cmd.Output() if strings.Index(string(out), string(b)) > -1 { diff --git a/lib/file/csv.go b/lib/file/csv.go index e5347b5..01e4358 100644 --- a/lib/file/csv.go +++ b/lib/file/csv.go @@ -9,6 +9,7 @@ var ( CsvDb *Csv once sync.Once ) + //init csv from file func GetCsvDb() *Csv { once.Do(func() { diff --git a/lib/file/file.go b/lib/file/file.go index bad041d..95448f0 100644 --- a/lib/file/file.go +++ b/lib/file/file.go @@ -293,9 +293,9 @@ func (s *Csv) DelHost(id int) error { return errors.New("不存在") } -func (s *Csv) IsHostExist(host string) bool { +func (s *Csv) IsHostExist(h *Host) bool { for _, v := range s.Hosts { - if v.Host == host { + if v.Host == h.Host && h.Location == v.Location { return true } } @@ -459,12 +459,6 @@ func (s *Csv) GetInfoByHost(host string, r *http.Request) (h *Host, err error) { } } if h != nil { - if h.Location != "/" { - r.RequestURI = strings.Replace(r.RequestURI, h.Location, "", 1) - } - if r.RequestURI == "" { - r.RequestURI = "/" - } return } err = errors.New("The host could not be parsed") diff --git a/lib/install/install.go b/lib/install/install.go index 63d2e46..323d563 100644 --- a/lib/install/install.go +++ b/lib/install/install.go @@ -48,7 +48,7 @@ func InstallNps() { log.Println("You can copy executable files to any directory and start working with nps.exe test|start|stop|restart|status") } } -func MkidrDirAll(path string, v ... string) { +func MkidrDirAll(path string, v ...string) { for _, item := range v { if err := os.MkdirAll(filepath.Join(path, item), 0755); err != nil { log.Fatalf("Failed to create directory %s error:%s", path, err.Error()) diff --git a/server/proxy/http.go b/server/proxy/http.go index 7bcaf3c..f0b2bc3 100644 --- a/server/proxy/http.go +++ b/server/proxy/http.go @@ -4,11 +4,11 @@ import ( "bufio" "crypto/tls" "github.com/cnlh/nps/bridge" - "github.com/cnlh/nps/lib/beego" "github.com/cnlh/nps/lib/common" "github.com/cnlh/nps/lib/conn" "github.com/cnlh/nps/lib/file" "github.com/cnlh/nps/lib/lg" + "github.com/cnlh/nps/vender/github.com/astaxie/beego" "log" "net/http" "net/http/httputil" @@ -117,14 +117,14 @@ func (s *httpServer) process(c *conn.Conn, r *http.Request) { lastHost *file.Host err error ) + if host, err = file.GetCsvDb().GetInfoByHost(r.Host, r); err != nil { + lg.Printf("the url %s %s Can't be parsed!", r.Host, r.RequestURI) + goto end + } else { + lastHost = host + } for { - if host, err = file.GetCsvDb().GetInfoByHost(r.Host, r); err != nil { - lg.Printf("the url %s %s is not found !", r.Host, r.RequestURI) - break - } else if host != lastHost { - lastHost = host - isConn = true - } + start: if isConn { //流量限制 if host.Client.Flow.FlowLimit > 0 && (host.Client.Flow.FlowLimit<<20) < (host.Client.Flow.ExportFlow+host.Client.Flow.InletFlow) { @@ -136,7 +136,7 @@ func (s *httpServer) process(c *conn.Conn, r *http.Request) { break } lk = conn.NewLink(host.Client.GetId(), common.CONN_TCP, host.GetRandomTarget(), host.Client.Cnf.CompressEncode, host.Client.Cnf.CompressDecode, host.Client.Cnf.Crypt, c, host.Flow, nil, host.Client.Rate, nil) - if tunnel, err = s.bridge.SendLinkInfo(host.Client.Id, lk); err != nil { + if tunnel, err = s.bridge.SendLinkInfo(host.Client.Id, lk, c.Conn.RemoteAddr().String()); err != nil { log.Println(err) break } @@ -146,11 +146,18 @@ func (s *httpServer) process(c *conn.Conn, r *http.Request) { if err != nil { break } + if host, err = file.GetCsvDb().GetInfoByHost(r.Host, r); err != nil { + lg.Printf("the url %s %s is not found !", r.Host, r.RequestURI) + break + } else if host != lastHost { + lastHost = host + isConn = true + goto start + } } //根据设定,修改header和host common.ChangeHostAndHeader(r, host.HostChange, host.HeaderChange, c.Conn.RemoteAddr().String()) b, err := httputil.DumpRequest(r, true) - lg.Println(string(b), r.RequestURI) if err != nil { break } @@ -160,7 +167,7 @@ func (s *httpServer) process(c *conn.Conn, r *http.Request) { break } } - +end: if isConn { s.writeConnFail(c.Conn) } else { diff --git a/server/proxy/socks5.go b/server/proxy/socks5.go index 5e07825..ee3c121 100755 --- a/server/proxy/socks5.go +++ b/server/proxy/socks5.go @@ -143,7 +143,7 @@ func (s *Sock5ModeServer) doConnect(c net.Conn, command uint8) { } link := conn.NewLink(s.task.Client.GetId(), ltype, addr, s.task.Client.Cnf.CompressEncode, s.task.Client.Cnf.CompressDecode, s.task.Client.Cnf.Crypt, conn.NewConn(c), s.task.Flow, nil, s.task.Client.Rate, nil) - if tunnel, err := s.bridge.SendLinkInfo(s.task.Client.Id, link); err != nil { + if tunnel, err := s.bridge.SendLinkInfo(s.task.Client.Id, link, c.RemoteAddr().String()); err != nil { c.Close() return } else { @@ -244,7 +244,7 @@ func (s *Sock5ModeServer) Auth(c net.Conn) error { if _, err := io.ReadAtLeast(c, pass, passLen); err != nil { return err } - if string(pass) == s.task.Client.Cnf.U && string(user) == s.task.Client.Cnf.P { + if string(user) == s.task.Client.Cnf.U && string(pass) == s.task.Client.Cnf.P { if _, err := c.Write([]byte{userAuthVersion, authSuccess}); err != nil { return err } @@ -255,7 +255,6 @@ func (s *Sock5ModeServer) Auth(c net.Conn) error { } return errors.New("验证不通过") } - return errors.New("未知错误") } //start diff --git a/server/proxy/tcp.go b/server/proxy/tcp.go index df369ae..0d259f3 100755 --- a/server/proxy/tcp.go +++ b/server/proxy/tcp.go @@ -3,11 +3,11 @@ package proxy import ( "errors" "github.com/cnlh/nps/bridge" - "github.com/cnlh/nps/lib/beego" "github.com/cnlh/nps/lib/common" "github.com/cnlh/nps/lib/conn" "github.com/cnlh/nps/lib/file" "github.com/cnlh/nps/lib/lg" + "github.com/cnlh/nps/vender/github.com/astaxie/beego" "net" "path/filepath" "strings" @@ -53,7 +53,7 @@ func (s *TunnelModeServer) Start() error { func (s *TunnelModeServer) dealClient(c *conn.Conn, cnf *file.Config, addr string, method string, rb []byte) error { link := conn.NewLink(s.task.Client.GetId(), common.CONN_TCP, addr, cnf.CompressEncode, cnf.CompressDecode, cnf.Crypt, c, s.task.Flow, nil, s.task.Client.Rate, nil) - if tunnel, err := s.bridge.SendLinkInfo(s.task.Client.Id, link); err != nil { + if tunnel, err := s.bridge.SendLinkInfo(s.task.Client.Id, link, c.Conn.RemoteAddr().String()); err != nil { c.Close() return err } else { @@ -105,8 +105,13 @@ func ProcessHttp(c *conn.Conn, s *TunnelModeServer) error { method, addr, rb, err, r := c.GetHost() if err != nil { c.Close() + lg.Println(err) return err } + if r.Method == "CONNECT" { + c.Write([]byte("HTTP/1.1 200 Connection Established\r\n")) + rb = nil //reset + } if err := s.auth(r, c, s.task.Client.Cnf.U, s.task.Client.Cnf.P); err != nil { return err } diff --git a/server/proxy/udp.go b/server/proxy/udp.go index bb5889a..1c65f01 100755 --- a/server/proxy/udp.go +++ b/server/proxy/udp.go @@ -50,7 +50,7 @@ func (s *UdpModeServer) process(addr *net.UDPAddr, data []byte) { if err := s.checkFlow(); err != nil { return } - if tunnel, err := s.bridge.SendLinkInfo(s.task.Client.Id, link); err != nil { + if tunnel, err := s.bridge.SendLinkInfo(s.task.Client.Id, link, addr.String()); err != nil { return } else { s.task.Flow.Add(len(data), 0) diff --git a/server/server.go b/server/server.go index 866fb3a..c63a6a9 100644 --- a/server/server.go +++ b/server/server.go @@ -3,11 +3,12 @@ package server import ( "errors" "github.com/cnlh/nps/bridge" - "github.com/cnlh/nps/lib/beego" + "github.com/cnlh/nps/lib/common" "github.com/cnlh/nps/lib/file" "github.com/cnlh/nps/lib/lg" "github.com/cnlh/nps/server/proxy" "github.com/cnlh/nps/server/tool" + "github.com/cnlh/nps/vender/github.com/astaxie/beego" "reflect" ) @@ -23,13 +24,15 @@ func init() { //从csv文件中恢复任务 func InitFromCsv() { //Add a public password - c := file.NewClient(beego.AppConfig.String("publicVkey"), true, true) - file.GetCsvDb().NewClient(c) - RunList[c.Id] = nil + if vkey := beego.AppConfig.String("publicVkey"); vkey != "" { + c := file.NewClient(vkey, true, true) + file.GetCsvDb().NewClient(c) + RunList[c.Id] = nil + } //Initialize services in server-side files for _, v := range file.GetCsvDb().Tasks { if v.Status { - lg.Println("启动模式:", v.Mode, "监听端口:", v.Port) + lg.Println("task start info: mode:", v.Mode, "port:", v.Port) AddTask(v) } } @@ -48,7 +51,7 @@ func DealBridgeTask() { //start a new server func StartNewServer(bridgePort int, cnf *file.Tunnel, bridgeType string) { - Bridge = bridge.NewTunnel(bridgePort, bridgeType) + Bridge = bridge.NewTunnel(bridgePort, bridgeType, common.GetBoolByStr(beego.AppConfig.String("ipLimit"))) if err := Bridge.StartTunnel(); err != nil { lg.Fatalln("服务端开启失败", err) } else { @@ -241,7 +244,7 @@ func DelClientConnect(clientId int) { func GetDashboardData() map[string]int { data := make(map[string]int) - data["hostCount"] = len(file.GetCsvDb().Hosts) + data["hostCount"] = len(file.GetCsvDb().Hosts) - 1 //Remove the public key client data["clientCount"] = len(file.GetCsvDb().Clients) list := file.GetCsvDb().Clients dealClientData(list) diff --git a/server/test/test.go b/server/test/test.go index a50919e..acb1f55 100644 --- a/server/test/test.go +++ b/server/test/test.go @@ -1,9 +1,9 @@ package test import ( - "github.com/cnlh/nps/lib/beego" "github.com/cnlh/nps/lib/common" "github.com/cnlh/nps/lib/file" + "github.com/cnlh/nps/vender/github.com/astaxie/beego" "log" "strconv" ) diff --git a/server/tool/utils.go b/server/tool/utils.go index 0274a22..19f641e 100644 --- a/server/tool/utils.go +++ b/server/tool/utils.go @@ -1,8 +1,8 @@ package tool import ( - "github.com/cnlh/nps/lib/beego" "github.com/cnlh/nps/lib/common" + "github.com/cnlh/nps/vender/github.com/astaxie/beego" ) var ports []int diff --git a/lib/snappy/decode.go b/vender/github.com/golang/snappy/decode.go similarity index 100% rename from lib/snappy/decode.go rename to vender/github.com/golang/snappy/decode.go diff --git a/lib/snappy/decode_amd64.go b/vender/github.com/golang/snappy/decode_amd64.go similarity index 100% rename from lib/snappy/decode_amd64.go rename to vender/github.com/golang/snappy/decode_amd64.go diff --git a/lib/snappy/decode_amd64.s b/vender/github.com/golang/snappy/decode_amd64.s similarity index 100% rename from lib/snappy/decode_amd64.s rename to vender/github.com/golang/snappy/decode_amd64.s diff --git a/lib/snappy/decode_other.go b/vender/github.com/golang/snappy/decode_other.go similarity index 100% rename from lib/snappy/decode_other.go rename to vender/github.com/golang/snappy/decode_other.go diff --git a/lib/snappy/encode.go b/vender/github.com/golang/snappy/encode.go similarity index 100% rename from lib/snappy/encode.go rename to vender/github.com/golang/snappy/encode.go diff --git a/lib/snappy/encode_amd64.go b/vender/github.com/golang/snappy/encode_amd64.go similarity index 100% rename from lib/snappy/encode_amd64.go rename to vender/github.com/golang/snappy/encode_amd64.go diff --git a/lib/snappy/encode_amd64.s b/vender/github.com/golang/snappy/encode_amd64.s similarity index 100% rename from lib/snappy/encode_amd64.s rename to vender/github.com/golang/snappy/encode_amd64.s diff --git a/lib/snappy/encode_other.go b/vender/github.com/golang/snappy/encode_other.go similarity index 100% rename from lib/snappy/encode_other.go rename to vender/github.com/golang/snappy/encode_other.go diff --git a/lib/snappy/golden_test.go b/vender/github.com/golang/snappy/golden_test.go similarity index 100% rename from lib/snappy/golden_test.go rename to vender/github.com/golang/snappy/golden_test.go diff --git a/lib/snappy/snappy.go b/vender/github.com/golang/snappy/snappy.go similarity index 100% rename from lib/snappy/snappy.go rename to vender/github.com/golang/snappy/snappy.go diff --git a/lib/snappy/snappy_test.go b/vender/github.com/golang/snappy/snappy_test.go similarity index 100% rename from lib/snappy/snappy_test.go rename to vender/github.com/golang/snappy/snappy_test.go diff --git a/lib/kcp/crypt.go b/vender/github.com/xtaci/kcp/crypt.go similarity index 100% rename from lib/kcp/crypt.go rename to vender/github.com/xtaci/kcp/crypt.go diff --git a/lib/kcp/crypt_test.go b/vender/github.com/xtaci/kcp/crypt_test.go similarity index 100% rename from lib/kcp/crypt_test.go rename to vender/github.com/xtaci/kcp/crypt_test.go diff --git a/lib/kcp/entropy.go b/vender/github.com/xtaci/kcp/entropy.go similarity index 100% rename from lib/kcp/entropy.go rename to vender/github.com/xtaci/kcp/entropy.go diff --git a/lib/kcp/fec.go b/vender/github.com/xtaci/kcp/fec.go similarity index 100% rename from lib/kcp/fec.go rename to vender/github.com/xtaci/kcp/fec.go diff --git a/lib/kcp/fec_test.go b/vender/github.com/xtaci/kcp/fec_test.go similarity index 100% rename from lib/kcp/fec_test.go rename to vender/github.com/xtaci/kcp/fec_test.go diff --git a/lib/kcp/kcp.go b/vender/github.com/xtaci/kcp/kcp.go similarity index 100% rename from lib/kcp/kcp.go rename to vender/github.com/xtaci/kcp/kcp.go diff --git a/lib/kcp/kcp_test.go b/vender/github.com/xtaci/kcp/kcp_test.go similarity index 100% rename from lib/kcp/kcp_test.go rename to vender/github.com/xtaci/kcp/kcp_test.go diff --git a/lib/kcp/sess.go b/vender/github.com/xtaci/kcp/sess.go similarity index 100% rename from lib/kcp/sess.go rename to vender/github.com/xtaci/kcp/sess.go diff --git a/lib/kcp/sess_test.go b/vender/github.com/xtaci/kcp/sess_test.go similarity index 100% rename from lib/kcp/sess_test.go rename to vender/github.com/xtaci/kcp/sess_test.go diff --git a/lib/kcp/snmp.go b/vender/github.com/xtaci/kcp/snmp.go similarity index 100% rename from lib/kcp/snmp.go rename to vender/github.com/xtaci/kcp/snmp.go diff --git a/lib/kcp/updater.go b/vender/github.com/xtaci/kcp/updater.go similarity index 100% rename from lib/kcp/updater.go rename to vender/github.com/xtaci/kcp/updater.go diff --git a/vender/golang.org/x/net/internal/socks/client.go b/vender/golang.org/x/net/internal/socks/client.go new file mode 100644 index 0000000..3d6f516 --- /dev/null +++ b/vender/golang.org/x/net/internal/socks/client.go @@ -0,0 +1,168 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package socks + +import ( + "context" + "errors" + "io" + "net" + "strconv" + "time" +) + +var ( + noDeadline = time.Time{} + aLongTimeAgo = time.Unix(1, 0) +) + +func (d *Dialer) connect(ctx context.Context, c net.Conn, address string) (_ net.Addr, ctxErr error) { + host, port, err := splitHostPort(address) + if err != nil { + return nil, err + } + if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() { + c.SetDeadline(deadline) + defer c.SetDeadline(noDeadline) + } + if ctx != context.Background() { + errCh := make(chan error, 1) + done := make(chan struct{}) + defer func() { + close(done) + if ctxErr == nil { + ctxErr = <-errCh + } + }() + go func() { + select { + case <-ctx.Done(): + c.SetDeadline(aLongTimeAgo) + errCh <- ctx.Err() + case <-done: + errCh <- nil + } + }() + } + + b := make([]byte, 0, 6+len(host)) // the size here is just an estimate + b = append(b, Version5) + if len(d.AuthMethods) == 0 || d.Authenticate == nil { + b = append(b, 1, byte(AuthMethodNotRequired)) + } else { + ams := d.AuthMethods + if len(ams) > 255 { + return nil, errors.New("too many authentication methods") + } + b = append(b, byte(len(ams))) + for _, am := range ams { + b = append(b, byte(am)) + } + } + if _, ctxErr = c.Write(b); ctxErr != nil { + return + } + + if _, ctxErr = io.ReadFull(c, b[:2]); ctxErr != nil { + return + } + if b[0] != Version5 { + return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0]))) + } + am := AuthMethod(b[1]) + if am == AuthMethodNoAcceptableMethods { + return nil, errors.New("no acceptable authentication methods") + } + if d.Authenticate != nil { + if ctxErr = d.Authenticate(ctx, c, am); ctxErr != nil { + return + } + } + + b = b[:0] + b = append(b, Version5, byte(d.cmd), 0) + if ip := net.ParseIP(host); ip != nil { + if ip4 := ip.To4(); ip4 != nil { + b = append(b, AddrTypeIPv4) + b = append(b, ip4...) + } else if ip6 := ip.To16(); ip6 != nil { + b = append(b, AddrTypeIPv6) + b = append(b, ip6...) + } else { + return nil, errors.New("unknown address type") + } + } else { + if len(host) > 255 { + return nil, errors.New("FQDN too long") + } + b = append(b, AddrTypeFQDN) + b = append(b, byte(len(host))) + b = append(b, host...) + } + b = append(b, byte(port>>8), byte(port)) + if _, ctxErr = c.Write(b); ctxErr != nil { + return + } + + if _, ctxErr = io.ReadFull(c, b[:4]); ctxErr != nil { + return + } + if b[0] != Version5 { + return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0]))) + } + if cmdErr := Reply(b[1]); cmdErr != StatusSucceeded { + return nil, errors.New("unknown error " + cmdErr.String()) + } + if b[2] != 0 { + return nil, errors.New("non-zero reserved field") + } + l := 2 + var a Addr + switch b[3] { + case AddrTypeIPv4: + l += net.IPv4len + a.IP = make(net.IP, net.IPv4len) + case AddrTypeIPv6: + l += net.IPv6len + a.IP = make(net.IP, net.IPv6len) + case AddrTypeFQDN: + if _, err := io.ReadFull(c, b[:1]); err != nil { + return nil, err + } + l += int(b[0]) + default: + return nil, errors.New("unknown address type " + strconv.Itoa(int(b[3]))) + } + if cap(b) < l { + b = make([]byte, l) + } else { + b = b[:l] + } + if _, ctxErr = io.ReadFull(c, b); ctxErr != nil { + return + } + if a.IP != nil { + copy(a.IP, b) + } else { + a.Name = string(b[:len(b)-2]) + } + a.Port = int(b[len(b)-2])<<8 | int(b[len(b)-1]) + return &a, nil +} + +func splitHostPort(address string) (string, int, error) { + host, port, err := net.SplitHostPort(address) + if err != nil { + return "", 0, err + } + portnum, err := strconv.Atoi(port) + if err != nil { + return "", 0, err + } + if 1 > portnum || portnum > 0xffff { + return "", 0, errors.New("port number out of range " + port) + } + return host, portnum, nil +} diff --git a/vender/golang.org/x/net/internal/socks/dial_test.go b/vender/golang.org/x/net/internal/socks/dial_test.go new file mode 100644 index 0000000..3a7a31b --- /dev/null +++ b/vender/golang.org/x/net/internal/socks/dial_test.go @@ -0,0 +1,170 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package socks_test + +import ( + "context" + "io" + "math/rand" + "net" + "os" + "testing" + "time" + + "golang.org/x/net/internal/socks" + "golang.org/x/net/internal/sockstest" +) + +func TestDial(t *testing.T) { + t.Run("Connect", func(t *testing.T) { + ss, err := sockstest.NewServer(sockstest.NoAuthRequired, sockstest.NoProxyRequired) + if err != nil { + t.Fatal(err) + } + defer ss.Close() + d := socks.NewDialer(ss.Addr().Network(), ss.Addr().String()) + d.AuthMethods = []socks.AuthMethod{ + socks.AuthMethodNotRequired, + socks.AuthMethodUsernamePassword, + } + d.Authenticate = (&socks.UsernamePassword{ + Username: "username", + Password: "password", + }).Authenticate + c, err := d.DialContext(context.Background(), ss.TargetAddr().Network(), ss.TargetAddr().String()) + if err != nil { + t.Fatal(err) + } + c.(*socks.Conn).BoundAddr() + c.Close() + }) + t.Run("ConnectWithConn", func(t *testing.T) { + ss, err := sockstest.NewServer(sockstest.NoAuthRequired, sockstest.NoProxyRequired) + if err != nil { + t.Fatal(err) + } + defer ss.Close() + c, err := net.Dial(ss.Addr().Network(), ss.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer c.Close() + d := socks.NewDialer(ss.Addr().Network(), ss.Addr().String()) + d.AuthMethods = []socks.AuthMethod{ + socks.AuthMethodNotRequired, + socks.AuthMethodUsernamePassword, + } + d.Authenticate = (&socks.UsernamePassword{ + Username: "username", + Password: "password", + }).Authenticate + a, err := d.DialWithConn(context.Background(), c, ss.TargetAddr().Network(), ss.TargetAddr().String()) + if err != nil { + t.Fatal(err) + } + if _, ok := a.(*socks.Addr); !ok { + t.Fatalf("got %+v; want socks.Addr", a) + } + }) + t.Run("Cancel", func(t *testing.T) { + ss, err := sockstest.NewServer(sockstest.NoAuthRequired, blackholeCmdFunc) + if err != nil { + t.Fatal(err) + } + defer ss.Close() + d := socks.NewDialer(ss.Addr().Network(), ss.Addr().String()) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + dialErr := make(chan error) + go func() { + c, err := d.DialContext(ctx, ss.TargetAddr().Network(), ss.TargetAddr().String()) + if err == nil { + c.Close() + } + dialErr <- err + }() + time.Sleep(100 * time.Millisecond) + cancel() + err = <-dialErr + if perr, nerr := parseDialError(err); perr != context.Canceled && nerr == nil { + t.Fatalf("got %v; want context.Canceled or equivalent", err) + } + }) + t.Run("Deadline", func(t *testing.T) { + ss, err := sockstest.NewServer(sockstest.NoAuthRequired, blackholeCmdFunc) + if err != nil { + t.Fatal(err) + } + defer ss.Close() + d := socks.NewDialer(ss.Addr().Network(), ss.Addr().String()) + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(100*time.Millisecond)) + defer cancel() + c, err := d.DialContext(ctx, ss.TargetAddr().Network(), ss.TargetAddr().String()) + if err == nil { + c.Close() + } + if perr, nerr := parseDialError(err); perr != context.DeadlineExceeded && nerr == nil { + t.Fatalf("got %v; want context.DeadlineExceeded or equivalent", err) + } + }) + t.Run("WithRogueServer", func(t *testing.T) { + ss, err := sockstest.NewServer(sockstest.NoAuthRequired, rogueCmdFunc) + if err != nil { + t.Fatal(err) + } + defer ss.Close() + d := socks.NewDialer(ss.Addr().Network(), ss.Addr().String()) + for i := 0; i < 2*len(rogueCmdList); i++ { + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(100*time.Millisecond)) + defer cancel() + c, err := d.DialContext(ctx, ss.TargetAddr().Network(), ss.TargetAddr().String()) + if err == nil { + t.Log(c.(*socks.Conn).BoundAddr()) + c.Close() + t.Error("should fail") + } + } + }) +} + +func blackholeCmdFunc(rw io.ReadWriter, b []byte) error { + if _, err := sockstest.ParseCmdRequest(b); err != nil { + return err + } + var bb [1]byte + for { + if _, err := rw.Read(bb[:]); err != nil { + return err + } + } +} + +func rogueCmdFunc(rw io.ReadWriter, b []byte) error { + if _, err := sockstest.ParseCmdRequest(b); err != nil { + return err + } + rw.Write(rogueCmdList[rand.Intn(len(rogueCmdList))]) + return nil +} + +var rogueCmdList = [][]byte{ + {0x05}, + {0x06, 0x00, 0x00, 0x01, 192, 0, 2, 1, 0x17, 0x4b}, + {0x05, 0x00, 0xff, 0x01, 192, 0, 2, 2, 0x17, 0x4b}, + {0x05, 0x00, 0x00, 0x01, 192, 0, 2, 3}, + {0x05, 0x00, 0x00, 0x03, 0x04, 'F', 'Q', 'D', 'N'}, +} + +func parseDialError(err error) (perr, nerr error) { + if e, ok := err.(*net.OpError); ok { + err = e.Err + nerr = e + } + if e, ok := err.(*os.SyscallError); ok { + err = e.Err + } + perr = err + return +} diff --git a/vender/golang.org/x/net/internal/socks/socks.go b/vender/golang.org/x/net/internal/socks/socks.go new file mode 100644 index 0000000..6929a9f --- /dev/null +++ b/vender/golang.org/x/net/internal/socks/socks.go @@ -0,0 +1,317 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package socks provides a SOCKS version 5 client implementation. +// +// SOCKS protocol version 5 is defined in RFC 1928. +// Username/Password authentication for SOCKS version 5 is defined in +// RFC 1929. +package socks + +import ( + "context" + "errors" + "io" + "net" + "strconv" +) + +// A Command represents a SOCKS command. +type Command int + +func (cmd Command) String() string { + switch cmd { + case CmdConnect: + return "socks connect" + case cmdBind: + return "socks bind" + default: + return "socks " + strconv.Itoa(int(cmd)) + } +} + +// An AuthMethod represents a SOCKS authentication method. +type AuthMethod int + +// A Reply represents a SOCKS command reply code. +type Reply int + +func (code Reply) String() string { + switch code { + case StatusSucceeded: + return "succeeded" + case 0x01: + return "general SOCKS server failure" + case 0x02: + return "connection not allowed by ruleset" + case 0x03: + return "network unreachable" + case 0x04: + return "host unreachable" + case 0x05: + return "connection refused" + case 0x06: + return "TTL expired" + case 0x07: + return "command not supported" + case 0x08: + return "address type not supported" + default: + return "unknown code: " + strconv.Itoa(int(code)) + } +} + +// Wire protocol constants. +const ( + Version5 = 0x05 + + AddrTypeIPv4 = 0x01 + AddrTypeFQDN = 0x03 + AddrTypeIPv6 = 0x04 + + CmdConnect Command = 0x01 // establishes an active-open forward proxy connection + cmdBind Command = 0x02 // establishes a passive-open forward proxy connection + + AuthMethodNotRequired AuthMethod = 0x00 // no authentication required + AuthMethodUsernamePassword AuthMethod = 0x02 // use username/password + AuthMethodNoAcceptableMethods AuthMethod = 0xff // no acceptable authentication methods + + StatusSucceeded Reply = 0x00 +) + +// An Addr represents a SOCKS-specific address. +// Either Name or IP is used exclusively. +type Addr struct { + Name string // fully-qualified domain name + IP net.IP + Port int +} + +func (a *Addr) Network() string { return "socks" } + +func (a *Addr) String() string { + if a == nil { + return "" + } + port := strconv.Itoa(a.Port) + if a.IP == nil { + return net.JoinHostPort(a.Name, port) + } + return net.JoinHostPort(a.IP.String(), port) +} + +// A Conn represents a forward proxy connection. +type Conn struct { + net.Conn + + boundAddr net.Addr +} + +// BoundAddr returns the address assigned by the proxy server for +// connecting to the command target address from the proxy server. +func (c *Conn) BoundAddr() net.Addr { + if c == nil { + return nil + } + return c.boundAddr +} + +// A Dialer holds SOCKS-specific options. +type Dialer struct { + cmd Command // either CmdConnect or cmdBind + proxyNetwork string // network between a proxy server and a client + proxyAddress string // proxy server address + + // ProxyDial specifies the optional dial function for + // establishing the transport connection. + ProxyDial func(context.Context, string, string) (net.Conn, error) + + // AuthMethods specifies the list of request authention + // methods. + // If empty, SOCKS client requests only AuthMethodNotRequired. + AuthMethods []AuthMethod + + // Authenticate specifies the optional authentication + // function. It must be non-nil when AuthMethods is not empty. + // It must return an error when the authentication is failed. + Authenticate func(context.Context, io.ReadWriter, AuthMethod) error +} + +// DialContext connects to the provided address on the provided +// network. +// +// The returned error value may be a net.OpError. When the Op field of +// net.OpError contains "socks", the Source field contains a proxy +// server address and the Addr field contains a command target +// address. +// +// See func Dial of the net package of standard library for a +// description of the network and address parameters. +func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { + if err := d.validateTarget(network, address); err != nil { + proxy, dst, _ := d.pathAddrs(address) + return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} + } + if ctx == nil { + proxy, dst, _ := d.pathAddrs(address) + return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")} + } + var err error + var c net.Conn + if d.ProxyDial != nil { + c, err = d.ProxyDial(ctx, d.proxyNetwork, d.proxyAddress) + } else { + var dd net.Dialer + c, err = dd.DialContext(ctx, d.proxyNetwork, d.proxyAddress) + } + if err != nil { + proxy, dst, _ := d.pathAddrs(address) + return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} + } + a, err := d.connect(ctx, c, address) + if err != nil { + c.Close() + proxy, dst, _ := d.pathAddrs(address) + return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} + } + return &Conn{Conn: c, boundAddr: a}, nil +} + +// DialWithConn initiates a connection from SOCKS server to the target +// network and address using the connection c that is already +// connected to the SOCKS server. +// +// It returns the connection's local address assigned by the SOCKS +// server. +func (d *Dialer) DialWithConn(ctx context.Context, c net.Conn, network, address string) (net.Addr, error) { + if err := d.validateTarget(network, address); err != nil { + proxy, dst, _ := d.pathAddrs(address) + return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} + } + if ctx == nil { + proxy, dst, _ := d.pathAddrs(address) + return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")} + } + a, err := d.connect(ctx, c, address) + if err != nil { + proxy, dst, _ := d.pathAddrs(address) + return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} + } + return a, nil +} + +// Dial connects to the provided address on the provided network. +// +// Unlike DialContext, it returns a raw transport connection instead +// of a forward proxy connection. +// +// Deprecated: Use DialContext or DialWithConn instead. +func (d *Dialer) Dial(network, address string) (net.Conn, error) { + if err := d.validateTarget(network, address); err != nil { + proxy, dst, _ := d.pathAddrs(address) + return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} + } + var err error + var c net.Conn + if d.ProxyDial != nil { + c, err = d.ProxyDial(context.Background(), d.proxyNetwork, d.proxyAddress) + } else { + c, err = net.Dial(d.proxyNetwork, d.proxyAddress) + } + if err != nil { + proxy, dst, _ := d.pathAddrs(address) + return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} + } + if _, err := d.DialWithConn(context.Background(), c, network, address); err != nil { + c.Close() + return nil, err + } + return c, nil +} + +func (d *Dialer) validateTarget(network, address string) error { + switch network { + case "tcp", "tcp6", "tcp4": + default: + return errors.New("network not implemented") + } + switch d.cmd { + case CmdConnect, cmdBind: + default: + return errors.New("command not implemented") + } + return nil +} + +func (d *Dialer) pathAddrs(address string) (proxy, dst net.Addr, err error) { + for i, s := range []string{d.proxyAddress, address} { + host, port, err := splitHostPort(s) + if err != nil { + return nil, nil, err + } + a := &Addr{Port: port} + a.IP = net.ParseIP(host) + if a.IP == nil { + a.Name = host + } + if i == 0 { + proxy = a + } else { + dst = a + } + } + return +} + +// NewDialer returns a new Dialer that dials through the provided +// proxy server's network and address. +func NewDialer(network, address string) *Dialer { + return &Dialer{proxyNetwork: network, proxyAddress: address, cmd: CmdConnect} +} + +const ( + authUsernamePasswordVersion = 0x01 + authStatusSucceeded = 0x00 +) + +// UsernamePassword are the credentials for the username/password +// authentication method. +type UsernamePassword struct { + Username string + Password string +} + +// Authenticate authenticates a pair of username and password with the +// proxy server. +func (up *UsernamePassword) Authenticate(ctx context.Context, rw io.ReadWriter, auth AuthMethod) error { + switch auth { + case AuthMethodNotRequired: + return nil + case AuthMethodUsernamePassword: + if len(up.Username) == 0 || len(up.Username) > 255 || len(up.Password) == 0 || len(up.Password) > 255 { + return errors.New("invalid username/password") + } + b := []byte{authUsernamePasswordVersion} + b = append(b, byte(len(up.Username))) + b = append(b, up.Username...) + b = append(b, byte(len(up.Password))) + b = append(b, up.Password...) + // TODO(mikio): handle IO deadlines and cancelation if + // necessary + if _, err := rw.Write(b); err != nil { + return err + } + if _, err := io.ReadFull(rw, b[:2]); err != nil { + return err + } + if b[0] != authUsernamePasswordVersion { + return errors.New("invalid username/password version") + } + if b[1] != authStatusSucceeded { + return errors.New("username/password authentication failed") + } + return nil + } + return errors.New("unsupported authentication method " + strconv.Itoa(int(auth))) +} diff --git a/vender/golang.org/x/net/proxy/direct.go b/vender/golang.org/x/net/proxy/direct.go new file mode 100644 index 0000000..4c5ad88 --- /dev/null +++ b/vender/golang.org/x/net/proxy/direct.go @@ -0,0 +1,18 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package proxy + +import ( + "net" +) + +type direct struct{} + +// Direct is a direct proxy: one that makes network connections directly. +var Direct = direct{} + +func (direct) Dial(network, addr string) (net.Conn, error) { + return net.Dial(network, addr) +} diff --git a/vender/golang.org/x/net/proxy/per_host.go b/vender/golang.org/x/net/proxy/per_host.go new file mode 100644 index 0000000..0689bb6 --- /dev/null +++ b/vender/golang.org/x/net/proxy/per_host.go @@ -0,0 +1,140 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package proxy + +import ( + "net" + "strings" +) + +// A PerHost directs connections to a default Dialer unless the host name +// requested matches one of a number of exceptions. +type PerHost struct { + def, bypass Dialer + + bypassNetworks []*net.IPNet + bypassIPs []net.IP + bypassZones []string + bypassHosts []string +} + +// NewPerHost returns a PerHost Dialer that directs connections to either +// defaultDialer or bypass, depending on whether the connection matches one of +// the configured rules. +func NewPerHost(defaultDialer, bypass Dialer) *PerHost { + return &PerHost{ + def: defaultDialer, + bypass: bypass, + } +} + +// Dial connects to the address addr on the given network through either +// defaultDialer or bypass. +func (p *PerHost) Dial(network, addr string) (c net.Conn, err error) { + host, _, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + + return p.dialerForRequest(host).Dial(network, addr) +} + +func (p *PerHost) dialerForRequest(host string) Dialer { + if ip := net.ParseIP(host); ip != nil { + for _, net := range p.bypassNetworks { + if net.Contains(ip) { + return p.bypass + } + } + for _, bypassIP := range p.bypassIPs { + if bypassIP.Equal(ip) { + return p.bypass + } + } + return p.def + } + + for _, zone := range p.bypassZones { + if strings.HasSuffix(host, zone) { + return p.bypass + } + if host == zone[1:] { + // For a zone ".example.com", we match "example.com" + // too. + return p.bypass + } + } + for _, bypassHost := range p.bypassHosts { + if bypassHost == host { + return p.bypass + } + } + return p.def +} + +// AddFromString parses a string that contains comma-separated values +// specifying hosts that should use the bypass proxy. Each value is either an +// IP address, a CIDR range, a zone (*.example.com) or a host name +// (localhost). A best effort is made to parse the string and errors are +// ignored. +func (p *PerHost) AddFromString(s string) { + hosts := strings.Split(s, ",") + for _, host := range hosts { + host = strings.TrimSpace(host) + if len(host) == 0 { + continue + } + if strings.Contains(host, "/") { + // We assume that it's a CIDR address like 127.0.0.0/8 + if _, net, err := net.ParseCIDR(host); err == nil { + p.AddNetwork(net) + } + continue + } + if ip := net.ParseIP(host); ip != nil { + p.AddIP(ip) + continue + } + if strings.HasPrefix(host, "*.") { + p.AddZone(host[1:]) + continue + } + p.AddHost(host) + } +} + +// AddIP specifies an IP address that will use the bypass proxy. Note that +// this will only take effect if a literal IP address is dialed. A connection +// to a named host will never match an IP. +func (p *PerHost) AddIP(ip net.IP) { + p.bypassIPs = append(p.bypassIPs, ip) +} + +// AddNetwork specifies an IP range that will use the bypass proxy. Note that +// this will only take effect if a literal IP address is dialed. A connection +// to a named host will never match. +func (p *PerHost) AddNetwork(net *net.IPNet) { + p.bypassNetworks = append(p.bypassNetworks, net) +} + +// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of +// "example.com" matches "example.com" and all of its subdomains. +func (p *PerHost) AddZone(zone string) { + if strings.HasSuffix(zone, ".") { + zone = zone[:len(zone)-1] + } + if !strings.HasPrefix(zone, ".") { + zone = "." + zone + } + p.bypassZones = append(p.bypassZones, zone) +} + +// AddHost specifies a host name that will use the bypass proxy. +func (p *PerHost) AddHost(host string) { + if strings.HasSuffix(host, ".") { + host = host[:len(host)-1] + } + p.bypassHosts = append(p.bypassHosts, host) +} diff --git a/vender/golang.org/x/net/proxy/per_host_test.go b/vender/golang.org/x/net/proxy/per_host_test.go new file mode 100644 index 0000000..a7d8095 --- /dev/null +++ b/vender/golang.org/x/net/proxy/per_host_test.go @@ -0,0 +1,55 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package proxy + +import ( + "errors" + "net" + "reflect" + "testing" +) + +type recordingProxy struct { + addrs []string +} + +func (r *recordingProxy) Dial(network, addr string) (net.Conn, error) { + r.addrs = append(r.addrs, addr) + return nil, errors.New("recordingProxy") +} + +func TestPerHost(t *testing.T) { + var def, bypass recordingProxy + perHost := NewPerHost(&def, &bypass) + perHost.AddFromString("localhost,*.zone,127.0.0.1,10.0.0.1/8,1000::/16") + + expectedDef := []string{ + "example.com:123", + "1.2.3.4:123", + "[1001::]:123", + } + expectedBypass := []string{ + "localhost:123", + "zone:123", + "foo.zone:123", + "127.0.0.1:123", + "10.1.2.3:123", + "[1000::]:123", + } + + for _, addr := range expectedDef { + perHost.Dial("tcp", addr) + } + for _, addr := range expectedBypass { + perHost.Dial("tcp", addr) + } + + if !reflect.DeepEqual(expectedDef, def.addrs) { + t.Errorf("Hosts which went to the default proxy didn't match. Got %v, want %v", def.addrs, expectedDef) + } + if !reflect.DeepEqual(expectedBypass, bypass.addrs) { + t.Errorf("Hosts which went to the bypass proxy didn't match. Got %v, want %v", bypass.addrs, expectedBypass) + } +} diff --git a/vender/golang.org/x/net/proxy/proxy.go b/vender/golang.org/x/net/proxy/proxy.go new file mode 100644 index 0000000..9f232f8 --- /dev/null +++ b/vender/golang.org/x/net/proxy/proxy.go @@ -0,0 +1,134 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package proxy provides support for a variety of protocols to proxy network +// data. +package proxy + +import ( + "errors" + "net" + "net/url" + "os" + "sync" +) + +// A Dialer is a means to establish a connection. +type Dialer interface { + // Dial connects to the given address via the proxy. + Dial(network, addr string) (c net.Conn, err error) +} + +// Auth contains authentication parameters that specific Dialers may require. +type Auth struct { + User, Password string +} + +// FromEnvironment returns the dialer specified by the proxy related variables in +// the environment. +func FromEnvironment() Dialer { + allProxy := allProxyEnv.Get() + if len(allProxy) == 0 { + return Direct + } + + proxyURL, err := url.Parse(allProxy) + if err != nil { + return Direct + } + proxy, err := FromURL(proxyURL, Direct) + if err != nil { + return Direct + } + + noProxy := noProxyEnv.Get() + if len(noProxy) == 0 { + return proxy + } + + perHost := NewPerHost(proxy, Direct) + perHost.AddFromString(noProxy) + return perHost +} + +// proxySchemes is a map from URL schemes to a function that creates a Dialer +// from a URL with such a scheme. +var proxySchemes map[string]func(*url.URL, Dialer) (Dialer, error) + +// RegisterDialerType takes a URL scheme and a function to generate Dialers from +// a URL with that scheme and a forwarding Dialer. Registered schemes are used +// by FromURL. +func RegisterDialerType(scheme string, f func(*url.URL, Dialer) (Dialer, error)) { + if proxySchemes == nil { + proxySchemes = make(map[string]func(*url.URL, Dialer) (Dialer, error)) + } + proxySchemes[scheme] = f +} + +// FromURL returns a Dialer given a URL specification and an underlying +// Dialer for it to make network requests. +func FromURL(u *url.URL, forward Dialer) (Dialer, error) { + var auth *Auth + if u.User != nil { + auth = new(Auth) + auth.User = u.User.Username() + if p, ok := u.User.Password(); ok { + auth.Password = p + } + } + + switch u.Scheme { + case "socks5": + return SOCKS5("tcp", u.Host, auth, forward) + } + + // If the scheme doesn't match any of the built-in schemes, see if it + // was registered by another package. + if proxySchemes != nil { + if f, ok := proxySchemes[u.Scheme]; ok { + return f(u, forward) + } + } + + return nil, errors.New("proxy: unknown scheme: " + u.Scheme) +} + +var ( + allProxyEnv = &envOnce{ + names: []string{"ALL_PROXY", "all_proxy"}, + } + noProxyEnv = &envOnce{ + names: []string{"NO_PROXY", "no_proxy"}, + } +) + +// envOnce looks up an environment variable (optionally by multiple +// names) once. It mitigates expensive lookups on some platforms +// (e.g. Windows). +// (Borrowed from net/http/transport.go) +type envOnce struct { + names []string + once sync.Once + val string +} + +func (e *envOnce) Get() string { + e.once.Do(e.init) + return e.val +} + +func (e *envOnce) init() { + for _, n := range e.names { + e.val = os.Getenv(n) + if e.val != "" { + return + } + } +} + +// reset is used by tests +func (e *envOnce) reset() { + e.once = sync.Once{} + e.val = "" +} diff --git a/vender/golang.org/x/net/proxy/proxy_test.go b/vender/golang.org/x/net/proxy/proxy_test.go new file mode 100644 index 0000000..0be1b42 --- /dev/null +++ b/vender/golang.org/x/net/proxy/proxy_test.go @@ -0,0 +1,123 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package proxy + +import ( + "bytes" + "fmt" + "net/url" + "os" + "strings" + "testing" + + "golang.org/x/net/internal/sockstest" +) + +type proxyFromEnvTest struct { + allProxyEnv string + noProxyEnv string + wantTypeOf Dialer +} + +func (t proxyFromEnvTest) String() string { + var buf bytes.Buffer + space := func() { + if buf.Len() > 0 { + buf.WriteByte(' ') + } + } + if t.allProxyEnv != "" { + fmt.Fprintf(&buf, "all_proxy=%q", t.allProxyEnv) + } + if t.noProxyEnv != "" { + space() + fmt.Fprintf(&buf, "no_proxy=%q", t.noProxyEnv) + } + return strings.TrimSpace(buf.String()) +} + +func TestFromEnvironment(t *testing.T) { + ResetProxyEnv() + + type dummyDialer struct { + direct + } + + RegisterDialerType("irc", func(_ *url.URL, _ Dialer) (Dialer, error) { + return dummyDialer{}, nil + }) + + proxyFromEnvTests := []proxyFromEnvTest{ + {allProxyEnv: "127.0.0.1:8080", noProxyEnv: "localhost, 127.0.0.1", wantTypeOf: direct{}}, + {allProxyEnv: "ftp://example.com:8000", noProxyEnv: "localhost, 127.0.0.1", wantTypeOf: direct{}}, + {allProxyEnv: "socks5://example.com:8080", noProxyEnv: "localhost, 127.0.0.1", wantTypeOf: &PerHost{}}, + {allProxyEnv: "irc://example.com:8000", wantTypeOf: dummyDialer{}}, + {noProxyEnv: "localhost, 127.0.0.1", wantTypeOf: direct{}}, + {wantTypeOf: direct{}}, + } + + for _, tt := range proxyFromEnvTests { + os.Setenv("ALL_PROXY", tt.allProxyEnv) + os.Setenv("NO_PROXY", tt.noProxyEnv) + ResetCachedEnvironment() + + d := FromEnvironment() + if got, want := fmt.Sprintf("%T", d), fmt.Sprintf("%T", tt.wantTypeOf); got != want { + t.Errorf("%v: got type = %T, want %T", tt, d, tt.wantTypeOf) + } + } +} + +func TestFromURL(t *testing.T) { + ss, err := sockstest.NewServer(sockstest.NoAuthRequired, sockstest.NoProxyRequired) + if err != nil { + t.Fatal(err) + } + defer ss.Close() + url, err := url.Parse("socks5://user:password@" + ss.Addr().String()) + if err != nil { + t.Fatal(err) + } + proxy, err := FromURL(url, nil) + if err != nil { + t.Fatal(err) + } + c, err := proxy.Dial("tcp", "fqdn.doesnotexist:5963") + if err != nil { + t.Fatal(err) + } + c.Close() +} + +func TestSOCKS5(t *testing.T) { + ss, err := sockstest.NewServer(sockstest.NoAuthRequired, sockstest.NoProxyRequired) + if err != nil { + t.Fatal(err) + } + defer ss.Close() + proxy, err := SOCKS5("tcp", ss.Addr().String(), nil, nil) + if err != nil { + t.Fatal(err) + } + c, err := proxy.Dial("tcp", ss.TargetAddr().String()) + if err != nil { + t.Fatal(err) + } + c.Close() +} + +func ResetProxyEnv() { + for _, env := range []*envOnce{allProxyEnv, noProxyEnv} { + for _, v := range env.names { + os.Setenv(v, "") + } + } + ResetCachedEnvironment() +} + +func ResetCachedEnvironment() { + allProxyEnv.reset() + noProxyEnv.reset() +} diff --git a/vender/golang.org/x/net/proxy/socks5.go b/vender/golang.org/x/net/proxy/socks5.go new file mode 100644 index 0000000..00d225e --- /dev/null +++ b/vender/golang.org/x/net/proxy/socks5.go @@ -0,0 +1,36 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package proxy + +import ( + "context" + "net" + + "github.com/cnlh/nps/vender/golang.org/x/net/internal/socks" +) + +// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given +// address with an optional username and password. +// See RFC 1928 and RFC 1929. +func SOCKS5(network, address string, auth *Auth, forward Dialer) (Dialer, error) { + d := socks.NewDialer(network, address) + if forward != nil { + d.ProxyDial = func(_ context.Context, network string, address string) (net.Conn, error) { + return forward.Dial(network, address) + } + } + if auth != nil { + up := socks.UsernamePassword{ + Username: auth.User, + Password: auth.Password, + } + d.AuthMethods = []socks.AuthMethod{ + socks.AuthMethodNotRequired, + socks.AuthMethodUsernamePassword, + } + d.Authenticate = up.Authenticate + } + return d, nil +} diff --git a/web/controllers/base.go b/web/controllers/base.go index 0431ac5..d52e952 100755 --- a/web/controllers/base.go +++ b/web/controllers/base.go @@ -1,9 +1,9 @@ package controllers import ( - "github.com/cnlh/nps/lib/beego" "github.com/cnlh/nps/lib/common" "github.com/cnlh/nps/server" + "github.com/cnlh/nps/vender/github.com/astaxie/beego" "strconv" "strings" ) @@ -90,7 +90,7 @@ func (s *BaseController) AjaxErr(str string) { } //组装ajax -func ajax(str string, status int) (map[string]interface{}) { +func ajax(str string, status int) map[string]interface{} { json := make(map[string]interface{}) json["status"] = status json["msg"] = str diff --git a/web/controllers/index.go b/web/controllers/index.go index 06e60f9..0fb676c 100755 --- a/web/controllers/index.go +++ b/web/controllers/index.go @@ -3,6 +3,7 @@ package controllers import ( "github.com/cnlh/nps/lib/file" "github.com/cnlh/nps/server" + "github.com/cnlh/nps/server/tool" ) type IndexController struct { @@ -81,6 +82,9 @@ func (s *IndexController) Add() { Remark: s.GetString("remark"), Flow: &file.Flow{}, } + if !tool.TestServerPort(t.Port, t.Mode) { + s.AjaxErr("The port cannot be opened because it may has been occupied or is no longer allowed.") + } var err error if t.Client, err = file.GetCsvDb().GetClient(s.GetIntNoErr("client_id")); err != nil { s.AjaxErr(err.Error()) diff --git a/web/controllers/login.go b/web/controllers/login.go index 53ea676..67c1cfa 100755 --- a/web/controllers/login.go +++ b/web/controllers/login.go @@ -1,7 +1,7 @@ package controllers import ( - "github.com/cnlh/nps/lib/beego" + "github.com/cnlh/nps/vender/github.com/astaxie/beego" ) type LoginController struct { diff --git a/web/routers/router.go b/web/routers/router.go index 869be07..23f8837 100755 --- a/web/routers/router.go +++ b/web/routers/router.go @@ -1,7 +1,7 @@ package routers import ( - "github.com/cnlh/nps/lib/beego" + "github.com/cnlh/nps/vender/github.com/astaxie/beego" "github.com/cnlh/nps/web/controllers" ) diff --git a/web/views/index/hlist.html b/web/views/index/hlist.html index c43d6bb..9cde04f 100755 --- a/web/views/index/hlist.html +++ b/web/views/index/hlist.html @@ -9,6 +9,7 @@ 客户端id 备注 host + location 内网目标 host改写 压缩方式 @@ -77,6 +78,7 @@ {data: 'Remark'}, {data: 'Remark'}, {data: 'Host'}, + {data: 'Location'}, {data: 'Target'}, {data: 'HostChange'}, {data: 'HostChange'},