Ip限制 npc代理连接

pull/59/head
刘河 2019-02-16 20:43:26 +08:00
parent 9f6b33a62b
commit 3b18d66835
64 changed files with 1414 additions and 132 deletions

View File

@ -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()

View File

@ -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)

View File

@ -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
client/register.go Normal file
View File

@ -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
client/status.go Normal file
View File

@ -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)
}

View File

@ -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 modestdout|file")
connType = flag.String("type", "tcp", "Connection type with the serverkcp|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 modestdout|file")
connType = flag.String("type", "tcp", "Connection type with the serverkcp|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)
}

View File

@ -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"

View File

@ -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

View File

@ -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

1 a.o.com 127.0.0.1:8080 7 / 1
2 a.o.com 127.0.0.1:7002 7 /test 3
3 b.o.com 127.0.0.1:8082 7 4

View File

@ -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

View File

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

View File

@ -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"

View File

@ -59,7 +59,7 @@ func GetLogPath() string {
func GetTmpPath() string {
var path string
if IsWindows() {
path = "./"
path = GetRunPath()
} else {
path = "/tmp"
}

View File

@ -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]
}

View File

@ -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"

View File

@ -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)

View File

@ -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"
)

View File

@ -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 {

View File

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

View File

@ -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")

View File

@ -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())

View File

@ -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 {

View File

@ -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

View File

@ -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
}

View File

@ -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)

View File

@ -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)

View File

@ -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"
)

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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)))
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}
}

View File

@ -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 = ""
}

View File

@ -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()
}

View File

@ -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
}

View File

@ -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

View File

@ -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())

View File

@ -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 {

View File

@ -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"
)

View File

@ -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'},