Browse Source

Ip限制 npc代理连接

刘河 6 years ago
parent
commit
3b18d66835
64 changed files with 1414 additions and 132 deletions
  1. 45 18
      bridge/bridge.go
  2. 25 9
      client/client.go
  3. 4 4
      client/client_test.go
  4. 20 0
      client/register.go
  5. 52 0
      client/status.go
  6. 17 47
      cmd/npc/npc.go
  7. 1 1
      cmd/nps/nps.go
  8. 4 1
      conf/app.conf
  9. 3 1
      conf/hosts.csv
  10. 5 2
      conf/npc.conf
  11. 1 0
      conf/tasks.csv
  12. 1 0
      lib/common/const.go
  13. 1 1
      lib/common/run.go
  14. 5 0
      lib/common/util.go
  15. 5 0
      lib/config/config.go
  16. 8 7
      lib/conn/conn.go
  17. 1 1
      lib/conn/snappy.go
  18. 1 1
      lib/daemon/daemon.go
  19. 1 0
      lib/file/csv.go
  20. 2 8
      lib/file/file.go
  21. 1 1
      lib/install/install.go
  22. 18 11
      server/proxy/http.go
  23. 2 3
      server/proxy/socks5.go
  24. 7 2
      server/proxy/tcp.go
  25. 1 1
      server/proxy/udp.go
  26. 10 7
      server/server.go
  27. 1 1
      server/test/test.go
  28. 1 1
      server/tool/utils.go
  29. 0 0
      vender/github.com/golang/snappy/decode.go
  30. 0 0
      vender/github.com/golang/snappy/decode_amd64.go
  31. 0 0
      vender/github.com/golang/snappy/decode_amd64.s
  32. 0 0
      vender/github.com/golang/snappy/decode_other.go
  33. 0 0
      vender/github.com/golang/snappy/encode.go
  34. 0 0
      vender/github.com/golang/snappy/encode_amd64.go
  35. 0 0
      vender/github.com/golang/snappy/encode_amd64.s
  36. 0 0
      vender/github.com/golang/snappy/encode_other.go
  37. 0 0
      vender/github.com/golang/snappy/golden_test.go
  38. 0 0
      vender/github.com/golang/snappy/snappy.go
  39. 0 0
      vender/github.com/golang/snappy/snappy_test.go
  40. 0 0
      vender/github.com/xtaci/kcp/crypt.go
  41. 0 0
      vender/github.com/xtaci/kcp/crypt_test.go
  42. 0 0
      vender/github.com/xtaci/kcp/entropy.go
  43. 0 0
      vender/github.com/xtaci/kcp/fec.go
  44. 0 0
      vender/github.com/xtaci/kcp/fec_test.go
  45. 0 0
      vender/github.com/xtaci/kcp/kcp.go
  46. 0 0
      vender/github.com/xtaci/kcp/kcp_test.go
  47. 0 0
      vender/github.com/xtaci/kcp/sess.go
  48. 0 0
      vender/github.com/xtaci/kcp/sess_test.go
  49. 0 0
      vender/github.com/xtaci/kcp/snmp.go
  50. 0 0
      vender/github.com/xtaci/kcp/updater.go
  51. 168 0
      vender/golang.org/x/net/internal/socks/client.go
  52. 170 0
      vender/golang.org/x/net/internal/socks/dial_test.go
  53. 317 0
      vender/golang.org/x/net/internal/socks/socks.go
  54. 18 0
      vender/golang.org/x/net/proxy/direct.go
  55. 140 0
      vender/golang.org/x/net/proxy/per_host.go
  56. 55 0
      vender/golang.org/x/net/proxy/per_host_test.go
  57. 134 0
      vender/golang.org/x/net/proxy/proxy.go
  58. 123 0
      vender/golang.org/x/net/proxy/proxy_test.go
  59. 36 0
      vender/golang.org/x/net/proxy/socks5.go
  60. 2 2
      web/controllers/base.go
  61. 4 0
      web/controllers/index.go
  62. 1 1
      web/controllers/login.go
  63. 1 1
      web/routers/router.go
  64. 2 0
      web/views/index/hlist.html

+ 45 - 18
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()

+ 25 - 9
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)

+ 4 - 4
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 {

+ 20 - 0
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)
+}

+ 52 - 0
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)
+}

+ 17 - 47
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)
 	}

+ 1 - 1
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"

+ 4 - 1
conf/app.conf

@@ -37,4 +37,7 @@ bridgeType=tcp
 publicVkey=123
 
 #Open ports allowed on the server side
-allowPorts=9001-9100,10001,11000-12000
+allowPorts=9001-9100,10001,11000-12000
+
+
+ipLimit=true

+ 3 - 1
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

+ 5 - 2
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

+ 1 - 0
conf/tasks.csv

@@ -0,0 +1 @@
+9010,socks5Server,,1,27,7,

+ 1 - 0
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"

+ 1 - 1
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"
 	}

+ 5 - 0
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]
+}

+ 5 - 0
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"

+ 8 - 7
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)

+ 1 - 1
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"
 )

+ 1 - 1
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 {

+ 1 - 0
lib/file/csv.go

@@ -9,6 +9,7 @@ var (
 	CsvDb *Csv
 	once  sync.Once
 )
+
 //init csv from file
 func GetCsvDb() *Csv {
 	once.Do(func() {

+ 2 - 8
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")

+ 1 - 1
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())

+ 18 - 11
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 {

+ 2 - 3
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

+ 7 - 2
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
 	}

+ 1 - 1
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)

+ 10 - 7
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)

+ 1 - 1
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"
 )

+ 1 - 1
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

+ 0 - 0
lib/snappy/decode.go → vender/github.com/golang/snappy/decode.go


+ 0 - 0
lib/snappy/decode_amd64.go → vender/github.com/golang/snappy/decode_amd64.go


+ 0 - 0
lib/snappy/decode_amd64.s → vender/github.com/golang/snappy/decode_amd64.s


+ 0 - 0
lib/snappy/decode_other.go → vender/github.com/golang/snappy/decode_other.go


+ 0 - 0
lib/snappy/encode.go → vender/github.com/golang/snappy/encode.go


+ 0 - 0
lib/snappy/encode_amd64.go → vender/github.com/golang/snappy/encode_amd64.go


+ 0 - 0
lib/snappy/encode_amd64.s → vender/github.com/golang/snappy/encode_amd64.s


+ 0 - 0
lib/snappy/encode_other.go → vender/github.com/golang/snappy/encode_other.go


+ 0 - 0
lib/snappy/golden_test.go → vender/github.com/golang/snappy/golden_test.go


+ 0 - 0
lib/snappy/snappy.go → vender/github.com/golang/snappy/snappy.go


+ 0 - 0
lib/snappy/snappy_test.go → vender/github.com/golang/snappy/snappy_test.go


+ 0 - 0
lib/kcp/crypt.go → vender/github.com/xtaci/kcp/crypt.go


+ 0 - 0
lib/kcp/crypt_test.go → vender/github.com/xtaci/kcp/crypt_test.go


+ 0 - 0
lib/kcp/entropy.go → vender/github.com/xtaci/kcp/entropy.go


+ 0 - 0
lib/kcp/fec.go → vender/github.com/xtaci/kcp/fec.go


+ 0 - 0
lib/kcp/fec_test.go → vender/github.com/xtaci/kcp/fec_test.go


+ 0 - 0
lib/kcp/kcp.go → vender/github.com/xtaci/kcp/kcp.go


+ 0 - 0
lib/kcp/kcp_test.go → vender/github.com/xtaci/kcp/kcp_test.go


+ 0 - 0
lib/kcp/sess.go → vender/github.com/xtaci/kcp/sess.go


+ 0 - 0
lib/kcp/sess_test.go → vender/github.com/xtaci/kcp/sess_test.go


+ 0 - 0
lib/kcp/snmp.go → vender/github.com/xtaci/kcp/snmp.go


+ 0 - 0
lib/kcp/updater.go → vender/github.com/xtaci/kcp/updater.go


+ 168 - 0
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
+}

+ 170 - 0
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
+}

+ 317 - 0
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 "<nil>"
+	}
+	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)))
+}

+ 18 - 0
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)
+}

+ 140 - 0
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)
+}

+ 55 - 0
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)
+	}
+}

+ 134 - 0
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 = ""
+}

+ 123 - 0
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()
+}

+ 36 - 0
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
+}

+ 2 - 2
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

+ 4 - 0
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())

+ 1 - 1
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 {

+ 1 - 1
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"
 )
 

+ 2 - 0
web/views/index/hlist.html

@@ -9,6 +9,7 @@
                         <th>客户端id</th>
                         <th>备注</th>
                         <th>host</th>
+                        <th>location</th>
                         <th>内网目标</th>
                         <th>host改写</th>
                         <th>压缩方式</th>
@@ -77,6 +78,7 @@
                 {data: 'Remark'},
                 {data: 'Remark'},
                 {data: 'Host'},
+                {data: 'Location'},
                 {data: 'Target'},
                 {data: 'HostChange'},
                 {data: 'HostChange'},