mirror of
https://github.com/ehang-io/nps.git
synced 2026-11-01 01:02:12 +08:00
Ip限制 npc代理连接
This commit is contained in:
1 parent
9f6b33a62b
commit
3b18d66835
64 files changed
+1414
-132
No files matched your search
+45
-18
@@ -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
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
9010,socks5Server,,1,27,7,
|
||||
|
@@ -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
@@ -59,7 +59,7 @@ func GetLogPath() string {
|
||||
func GetTmpPath() string {
|
||||
var path string
|
||||
if IsWindows() {
|
||||
path = "./"
|
||||
path = GetRunPath()
|
||||
} else {
|
||||
path = "/tmp"
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -9,6 +9,7 @@ var (
|
||||
CsvDb *Csv
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
//init csv from file
|
||||
func GetCsvDb() *Csv {
|
||||
once.Do(func() {
|
||||
|
||||
+2
-8
@@ -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")
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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,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
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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 = ""
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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,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,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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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'},
|
||||
|
||||
Reference in new issue
Block a user