30 Commits
v6.8 ... v6.9

Author SHA1 Message Date
arraykeys@gmail.com
c14a3b2773 a 2019-01-22 14:50:03 +08:00
arraykeys@gmail.com
e6e61b3cdb v6.9 2019-01-22 14:30:45 +08:00
arraykeys@gmail.com
303587f91b fix #210 2019-01-22 14:24:38 +08:00
arraykeys@gmail.com
5c3fd53fab 修复了socks5代理错误处理超时的问题. 2019-01-21 16:58:21 +08:00
arraykeys@gmail.com
0c675e6ff6 sps代理增加了对单个上级认证信息的支持,如果没有单独设置,就使用全局-A设置.
现在上级格式: YTpi#2.2.2.2:33080@1
  说明:
  YTpi 是经过base64编码的认证信息,比如是http(s)/socks原始认证信息a:b,用户是a密码是b,base64编码之后是:YTpi
     如果是ss,那么a就是加密方法,b是密码,比如:aes-192-cfb:your_pass,base64编码之后是:YWVzLTE5Mi1jZmI6eW91cl9wYXNz
  # 是间隔符号,如果有认证信息,必须有#,没有认证信息可以省略#.
  2.2.2.2:33080 是上级地址
  @1 是设置权重,可以参考手册权重部分.
2019-01-21 16:21:46 +08:00
arraykeys@gmail.com
942b026a05 sps代理增加了对单个上级认证信息的支持,如果没有单独设置,就使用全局-A设置.
现在上级格式: YTpi#2.2.2.2:33080@1
  说明:
  YTpi 是经过base64编码的认证信息,比如是http(s)/socks原始认证信息a:b,用户是a密码是b,base64编码之后是:YTpi
     如果是ss,那么a就是加密方法,b是密码,比如:aes-192-cfb:your_pass,base64编码之后是:YWVzLTE5Mi1jZmI6eW91cl9wYXNz
  # 是间隔符号,如果有认证信息,必须有#,没有认证信息可以省略#.
  2.2.2.2:33080 是上级地址
  @1 是设置权重,可以参考手册权重部分.
2019-01-21 13:33:51 +08:00
arraykeys@gmail.com
0b347b7f8d sps代理增加了对单个上级认证信息的支持,如果没有单独设置,就使用全局-A设置.
现在上级格式: YTpi#2.2.2.2:33080@1
  说明:
  YTpi 是经过base64编码的认证信息,比如是http(s)/socks原始认证信息a:b,用户是a密码是b,base64编码之后是:YTpi
     如果是ss,那么a就是加密方法,b是密码,比如:aes-192-cfb:your_pass,base64编码之后是:YWVzLTE5Mi1jZmI6eW91cl9wYXNz
  # 是间隔符号,如果有认证信息,必须有#,没有认证信息可以省略#.
  2.2.2.2:33080 是上级地址
  @1 是设置权重,可以参考手册权重部分.
2019-01-21 12:15:54 +08:00
arraykeys
87322c335e a 2019-01-19 18:54:42 +08:00
arraykeys@gmail.com
1bf4b38268 fix sps start crash 2019-01-07 12:43:44 +08:00
snail007
60b1742088 Merge pull request #205 from snail007/master
fix qq group no.
2018-12-30 17:08:03 +08:00
snail007
266fa6f6fa Update README.md 2018-12-30 17:06:28 +08:00
snail007
02c3c9b374 Update README_ZH.md 2018-12-30 17:05:46 +08:00
arraykeys@gmail.com
8f7da3ed94 sdk log->os.Stdout 2018-12-29 18:28:45 +08:00
arraykeys@gmail.com
a5a6e8645a A 2018-12-29 17:06:07 +08:00
arraykeys@gmail.com
83022d3efc v6.8 2018-12-29 13:29:45 +08:00
arraykeys@gmail.com
78143ce638 Merge branch 'master' into dev 2018-12-27 17:18:48 +08:00
snail007
c66147c686 Merge pull request #201 from snail007/master
add more en markdown
2018-12-25 13:13:04 +08:00
arraykeys@gmail.com
e5bf95bdc8 a 2018-12-21 17:54:37 +08:00
arraykeys@gmail.com
ed1e7253f3 fix #194
fix #134
2018-12-19 17:28:24 +08:00
arraykeys@gmail.com
db43daea0b Merge branch 'master' into dev 2018-12-14 13:12:14 +08:00
arraykeys@gmail.com
89b5744e27 Merge branch 'a' into dev 2018-12-14 13:08:52 +08:00
arraykeys@gmail.com
ebba94b9d1 add profiling support for sdk 2018-12-06 13:01:39 +08:00
arraykeys@gmail.com
23c379faf9 add profiling support for sdk 2018-12-06 10:12:04 +08:00
arraykeys@gmail.com
124852b3a2 add profiling support for sdk 2018-12-05 17:08:05 +08:00
arraykeys@gmail.com
e6d557f61e add profiling support for sdk 2018-12-05 16:27:58 +08:00
arraykeys@gmail.com
0a2ed2e498 add profiling support for sdk 2018-12-05 14:52:59 +08:00
arraykeys@gmail.com
4ce5fd463d add profiling support for sdk 2018-12-05 14:52:00 +08:00
arraykeys@gmail.com
367cfb36dd add profiling support for sdk 2018-12-05 14:09:32 +08:00
arraykeys@gmail.com
57555ffc1e a 2018-12-03 18:06:38 +08:00
arraykeys@gmail.com
8a86a53bd2 add local_ip for auth url 2018-12-03 15:40:52 +08:00
19 changed files with 346 additions and 75 deletions

View File

@ -1,5 +1,25 @@
proxy更新日志
v6.9
1.修复了sps的start潜在的crash问题.
2.sps代理增加了--parent-tls-single参数用来支持单向tls上级。
3.sps代理增加了对单个上级认证信息的支持,如果没有单独设置,就使用全局-A设置.
现在上级格式: -P YTpi#2.2.2.2:33080@1
说明:
YTpi 是经过base64编码的认证信息,比如是http(s)/socks原始认证信息a:b,用户是a密码是b,base64编码之后是:YTpi
如果是ss,那么a就是加密方法,b是密码,比如:aes-192-cfb:your_pass,base64编码之后是:YWVzLTE5Mi1jZmI6eW91cl9wYXNz
# 是间隔符号,如果有认证信息,必须有#,没有认证信息可以省略#.
2.2.2.2:33080 是上级地址
@1 是设置权重,可以参考手册权重部分.
4.修复了socks5代理错误处理超时的问题.
5.修复了http(s)代理错误处理-Z的问题.
v6.8
1.HTTP(S)\SOCKS5代理,API认证功能,发送给认证接口的参数增加了本地IP,local_ip字段,
代表用户访问的是本地服务器的哪个IP.
2.fix #194 , fix #134 , 代理更稳定.
3.增加了一波英文文档.
v6.6
1.优化了limitconn的关闭逻辑,释放更多资源.
2.http(s)\socks代理增加了--intelligent,智能模式设置,可以是intelligent|direct|parent三者之一,

View File

@ -201,6 +201,8 @@ chmod +x install.sh
#### Docker installation
[docker](https://hub.docker.com/r/snail007/goproxy)
Dockerfile root of project uses multistage build and alpine project to comply with best practices. Uses golang 1.10.3 for building as noted in the project README.md and will be pretty small image. total extracted size will be 17.3MB for goproxy latest version.
The default build process builds the master branch (latest commits/ cutting edge), and it can be configured to build specific version, just edit Dockerfile before build, following builds release version 6.0:
@ -1271,7 +1273,7 @@ execution: `go run *.go`
Proxy is licensed under GPLv3 license.
### Contact
proxy QQ group: 7930152191 , 189618940 (full)
proxy QQ group: 793015219 , 189618940 (full)
### Donation
if proxy help you a lot,you can support us by:

View File

@ -1,7 +1,7 @@
<img src="https://github.com/snail007/goproxy/blob/master/docs/images/logo.jpg?raw=true" width="200"/>
Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5,ss代理服务器,支持正向代理、反向代理、透明代理、内网穿透、TCP/UDP端口映射、SSH中转、TLS加密传输、协议转换、防污染DNS代理。
[点击下载](https://github.com/snail007/goproxy/releases) 官方QQ交流群: 7930152191 (2群), 189618940 (1群满)
[点击下载](https://github.com/snail007/goproxy/releases) 官方QQ交流群: 793015219 (2群), 189618940 (1群满)
---
@ -202,6 +202,9 @@ chmod +x install.sh
```
#### Docker安装
[docker](https://hub.docker.com/r/snail007/goproxy)
项目根目录的Dockerfile文件用来构建,使用golang 1.10.3,构建基于goproxy的master分支最新版本,
全部大小17.3MB,默认情况下使用master分支,不过可以通过修改配置文件Dockerfile
或者使用参数GOPROXY_VERSION指定构建的goproxy版本.
@ -366,11 +369,12 @@ weight 根据每个上级的权重和连接数情况,选择出一个上级
比如:
`./proxy http -t tcp -p ":33080" --auth-url "http://test.com/auth.php"`
用户连接的时候,proxy会GET方式请求这url("http://test.com/auth.php"),
带上user,pass,ip,target四个参数:
http://test.com/auth.php?user={USER}&pass={PASS}&ip={IP}&target={TARGET}
带上user,pass,ip,local_ip,target五个参数:
http://test.com/auth.php?user={USER}&pass={PASS}&ip={IP}&local_ip={LOCAL_IP}&target={TARGET}
user:用户名
pass:密码
ip:用户的IP,比如:192.168.1.200
local_ip:用户访问的服务器的IP,比如:3.3.3.3
target:用户访问的URL,比如:http://demo.com:80/1.html或https://www.baidu.com:80
如果没有-a或-F或--auth-url参数,就是关闭Basic认证.
@ -915,11 +919,12 @@ SOCKS5代理,支持CONNECT,UDP协议,不支持BIND,支持用户名密码认证.
比如:
`./proxy socks -t tcp -p ":33080" --auth-url "http://test.com/auth.php"`
用户连接的时候,proxy会GET方式请求这url("http://test.com/auth.php"),
带上user,pass,ip,个参数:
http://test.com/auth.php?user={USER}&pass={PASS}&ip={IP}
带上user,pass,ip,local_ip四个参数:
http://test.com/auth.php?user={USER}&pass={PASS}&ip={IP}&local_ip={LOCAL_IP}
user:用户名
pass:密码
ip:用户的IP,比如:192.168.1.200
local_ip:用户访问的服务器的IP,比如:3.3.3.3
如果没有-a或-F或--auth-url参数,就是关闭认证.
@ -1155,6 +1160,18 @@ target:如果客户端是http(s)代理请求,这里代表的是请求的完整ur
如果没有-a或-F或--auth-url参数,就是关闭本地认证.
如果没有-A参数,连接上级不使用认证.
**设置单独认证信息**
如果存在多个不同上级,而且他们的密码有的一样有的不一样,那么可以针对每个上级设置认证信息,
同时还可以用-A参数设置一个全局认证信息,如果某个上级没有单独设置认证信息就使用全局设置的认证信息.
认证信息和上级写在一起.
格式是: YTpi#2.2.2.2:33080@1
说明:
YTpi 是经过base64编码的认证信息,比如是http(s)/socks原始认证信息a:b,用户是a密码是b,base64编码之后是:YTpi
如果是ss,那么a就是加密方法,b是密码,比如:aes-192-cfb:your_pass,base64编码之后是:YWVzLTE5Mi1jZmI6eW91cl9wYXNz
# 是间隔符号,如果有认证信息,必须有#,没有认证信息可以省略#
2.2.2.2:33080 是上级地址
@1 是设置权重,没有可以省略,详细说明可以参考手册***权重部分***
#### **6.8 自定义加密**
proxy的sps代理在tcp之上可以通过tls标准加密以及kcp协议加密tcp数据,除此之外还支持在tls和kcp之后进行
@ -1368,7 +1385,7 @@ utils是工具包,service是具体的每个服务类.
### License
Proxy is licensed under GPLv3 license.
### Contact
官方QQ交流群: 7930152191 (2群), 189618940 (1群满)
官方QQ交流群: 793015219 (2群), 189618940 (1群满)
### Donation

View File

@ -1 +1 @@
6.6
6.9

View File

@ -303,6 +303,7 @@ func initConfig() (err error) {
spsArgs.LoadBalanceOnlyHA = sps.Flag("lb-onlyha", "use only `high availability mode` to choose parent for LB").Default("false").Bool()
spsArgs.RateLimit = sps.Flag("rate-limit", "rate limit (bytes/second) of each connection, such as: 100K 1.5M . 0 means no limitation").Short('l').Default("0").String()
spsArgs.Jumper = sps.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Default("").String()
spsArgs.ParentTLSSingle = sps.Flag("parent-tls-single", "conntect to parent insecure skip verify").Default("false").Bool()
spsArgs.Debug = isDebug
//########dns#########

View File

@ -14,7 +14,7 @@ import (
)
type BasicAuther interface {
CheckUserPass(username, password, fromIP, ToTarget string) bool
CheckUserPass(username, password, userIP, localIP, toTarget string) bool
}
type Request struct {
ver uint8
@ -239,15 +239,16 @@ func (s *ServerConn) Target() string {
}
func (s *ServerConn) Handshake() (err error) {
remoteAddr := (*s.conn).RemoteAddr()
localAddr := (*s.conn).LocalAddr()
//协商开始
//method select request
var methodReq MethodsRequest
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
(*s.conn).SetReadDeadline(time.Now().Add(s.timeout))
methodReq, e := NewMethodsRequest((*s.conn), s.header)
(*s.conn).SetReadDeadline(time.Time{})
if e != nil {
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
(*s.conn).SetReadDeadline(time.Now().Add(s.timeout))
methodReq.Reply(socks5c.Method_NONE_ACCEPTABLE)
(*s.conn).SetReadDeadline(time.Time{})
err = fmt.Errorf("new methods request fail,ERR: %s", e)
@ -264,7 +265,7 @@ func (s *ServerConn) Handshake() (err error) {
// }
s.method = socks5c.Method_NO_AUTH
//method select reply
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
(*s.conn).SetReadDeadline(time.Now().Add(s.timeout))
err = methodReq.Reply(socks5c.Method_NO_AUTH)
(*s.conn).SetReadDeadline(time.Time{})
if err != nil {
@ -275,7 +276,7 @@ func (s *ServerConn) Handshake() (err error) {
} else {
//auth
if !methodReq.Select(socks5c.Method_USER_PASS) {
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
(*s.conn).SetReadDeadline(time.Now().Add(s.timeout))
methodReq.Reply(socks5c.Method_NONE_ACCEPTABLE)
(*s.conn).SetReadDeadline(time.Time{})
err = fmt.Errorf("none method found : Method_USER_PASS")
@ -283,7 +284,7 @@ func (s *ServerConn) Handshake() (err error) {
}
s.method = socks5c.Method_USER_PASS
//method reply need auth
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
(*s.conn).SetReadDeadline(time.Now().Add(s.timeout))
err = methodReq.Reply(socks5c.Method_USER_PASS)
(*s.conn).SetReadDeadline(time.Time{})
if err != nil {
@ -293,7 +294,7 @@ func (s *ServerConn) Handshake() (err error) {
//read auth
buf := make([]byte, 500)
var n int
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
(*s.conn).SetReadDeadline(time.Now().Add(s.timeout))
n, err = (*s.conn).Read(buf)
(*s.conn).SetReadDeadline(time.Time{})
if err != nil {
@ -305,9 +306,10 @@ func (s *ServerConn) Handshake() (err error) {
s.password = string(r[2+r[1]+1:])
//err = fmt.Errorf("user:%s,pass:%s", user, pass)
//auth
_addr := strings.Split(remoteAddr.String(), ":")
if s.auth == nil || (*s.auth).CheckUserPass(s.user, s.password, _addr[0], "") {
(*s.conn).SetDeadline(time.Now().Add(time.Millisecond * time.Duration(s.timeout)))
_userAddr := strings.Split(remoteAddr.String(), ":")
_localAddr := strings.Split(localAddr.String(), ":")
if s.auth == nil || (*s.auth).CheckUserPass(s.user, s.password, _userAddr[0], _localAddr[0], "") {
(*s.conn).SetDeadline(time.Now().Add(s.timeout))
_, err = (*s.conn).Write([]byte{0x01, 0x00})
(*s.conn).SetDeadline(time.Time{})
if err != nil {
@ -315,7 +317,7 @@ func (s *ServerConn) Handshake() (err error) {
return
}
} else {
(*s.conn).SetDeadline(time.Now().Add(time.Millisecond * time.Duration(s.timeout)))
(*s.conn).SetDeadline(time.Now().Add(s.timeout))
_, err = (*s.conn).Write([]byte{0x01, 0x01})
(*s.conn).SetDeadline(time.Time{})
if err != nil {
@ -327,7 +329,7 @@ func (s *ServerConn) Handshake() (err error) {
}
}
//request detail
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
(*s.conn).SetReadDeadline(time.Now().Add(s.timeout))
request, e := NewRequest(*s.conn)
(*s.conn).SetReadDeadline(time.Time{})
if e != nil {

View File

@ -1,3 +1,4 @@
v4.8
1.修复了多个服务同时开启日志,只会输出到最后一个日志文件的bug.
2.增加了获取sdk版本的Version()方法.
v6.8
1.sdk增加了调试锁,避免潜在的并发问题.
2.sdk的Stop方法增加了锁,避免潜在的并发问题.
3.sdk的Stop方法增加了关闭文件操作.

View File

@ -8,7 +8,9 @@ import (
"os"
"path"
"path/filepath"
"runtime/pprof"
"strings"
"sync"
"github.com/snail007/goproxy/core/lib/kcpcfg"
encryptconn "github.com/snail007/goproxy/core/lib/transport/encrypt"
@ -31,6 +33,10 @@ var SDK_VERSION = "No Version Provided"
var (
app *kingpin.Application
cpuProfilingFile, memProfilingFile, blockProfilingFile,
goroutineProfilingFile, threadcreateProfilingFile *os.File
isProfiling bool
profilingLock = &sync.Mutex{}
)
type LogCallback interface {
@ -320,6 +326,7 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback)
spsArgs.LoadBalanceOnlyHA = sps.Flag("lb-onlyha", "use only `high availability mode` to choose parent for LB").Default("false").Bool()
spsArgs.RateLimit = sps.Flag("rate-limit", "rate limit (bytes/second) of each connection, such as: 100K 1.5M . 0 means no limitation").Short('l').Default("0").String()
spsArgs.Jumper = sps.Flag("jumper", "https or socks5 proxies used when connecting to parent, only worked of -T is tls or tcp, format is https://username:password@host:port https://host:port or socks5://username:password@host:port socks5://host:port").Default("").String()
spsArgs.ParentTLSSingle = sps.Flag("parent-tls-single", "conntect to parent insecure skip verify").Default("false").Bool()
spsArgs.Debug = debug
//########dns#########
@ -411,7 +418,7 @@ func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback)
muxClientArgs.KCP = kcpArgs
dnsArgs.KCP = kcpArgs
log := logger.New(os.Stderr, "", logger.Ldate|logger.Ltime)
log := logger.New(os.Stdout, "", logger.Ldate|logger.Ltime)
flags := logger.Ldate
if *debug {
flags |= logger.Lshortfile | logger.Lmicroseconds
@ -477,3 +484,41 @@ func Stop(serviceID string) {
func Version() string {
return SDK_VERSION
}
func StartProfiling(storePath string) {
profilingLock.Lock()
defer profilingLock.Unlock()
if !isProfiling {
isProfiling = true
if storePath == "" {
storePath = "."
}
cpuProfilingFile, _ = os.Create(filepath.Join(storePath, "cpu.prof"))
memProfilingFile, _ = os.Create(filepath.Join(storePath, "memory.prof"))
blockProfilingFile, _ = os.Create(filepath.Join(storePath, "block.prof"))
goroutineProfilingFile, _ = os.Create(filepath.Join(storePath, "goroutine.prof"))
threadcreateProfilingFile, _ = os.Create(filepath.Join(storePath, "threadcreate.prof"))
pprof.StartCPUProfile(cpuProfilingFile)
}
}
func StopProfiling() {
profilingLock.Lock()
defer profilingLock.Unlock()
if isProfiling {
isProfiling = false
pprof.StopCPUProfile()
goroutine := pprof.Lookup("goroutine")
goroutine.WriteTo(goroutineProfilingFile, 1)
heap := pprof.Lookup("heap")
heap.WriteTo(memProfilingFile, 1)
block := pprof.Lookup("block")
block.WriteTo(blockProfilingFile, 1)
threadcreate := pprof.Lookup("threadcreate")
threadcreate.WriteTo(threadcreateProfilingFile, 1)
//close
goroutineProfilingFile.Close()
memProfilingFile.Close()
blockProfilingFile.Close()
threadcreateProfilingFile.Close()
}
}

View File

@ -21,5 +21,15 @@ func Version() (ver *C.char) {
return C.CString(sdk.Version())
}
//export StartProfiling
func StartProfiling(storePath *C.char) {
sdk.StartProfiling(C.GoString(storePath))
}
//export StopProfiling
func StopProfiling() {
sdk.StopProfiling()
}
func main() {
}

View File

@ -210,7 +210,7 @@ func (s *HTTP) InitService() (err error) {
return
}
conn, err := utils.ConnectHost(s.Resolve(s.lb.Select("", *s.cfg.LoadBalanceOnlyHA)), *s.cfg.Timeout*2)
if err == nil {
if err == nil && conn != nil {
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = conn.Write([]byte{0})
conn.SetDeadline(time.Time{})
@ -386,12 +386,17 @@ func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *ut
}
if useProxy {
// s.log.Printf("%v", s.outPool)
if *s.cfg.ParentType == "ssh" {
outConn, err = s.getSSHConn(address)
} else {
selectAddr := (*inConn).RemoteAddr().String()
if utils.LBMethod(*s.cfg.LoadBalanceMethod) == lb.SELECT_HASH && *s.cfg.LoadBalanceHashTarget {
selectAddr = address
}
lbAddr = s.lb.Select(selectAddr, *s.cfg.LoadBalanceOnlyHA)
outConn, err = s.GetParentConn(lbAddr)
}
} else {
outConn, err = s.GetDirectConn(s.Resolve(address), inLocalAddr)
}
@ -411,7 +416,7 @@ func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *ut
if *s.cfg.ParentCompress {
outConn = utils.NewCompConn(outConn)
}
if *s.cfg.ParentKey != "" {
if useProxy && *s.cfg.ParentKey != "" {
outConn = conncrypt.New(outConn, &conncrypt.Config{
Password: *s.cfg.ParentKey,
})
@ -515,6 +520,9 @@ func (s *HTTP) ConnectSSH() (err error) {
s.sshClient.Close()
}
s.sshClient, err = ssh.Dial("tcp", s.Resolve(s.lb.Select("", *s.cfg.LoadBalanceOnlyHA)), &config)
if err != nil {
s.log.Printf("connect to ssh %s fail", s.cfg.Parent)
}
<-s.lockChn
return
}

View File

@ -39,6 +39,9 @@ func GetService(name string) *ServiceItem {
func Stop(name string) {
if s, ok := servicesMap.Load(name); ok && s.(*ServiceItem).S != nil {
s.(*ServiceItem).S.Clean()
*s.(*ServiceItem) = ServiceItem{}
s = nil
servicesMap.Store(name, nil)
servicesMap.Delete(name)
}
}

View File

@ -101,7 +101,7 @@ func (s *SPS) proxyUDP(inConn *net.Conn, serverConn *socks.ServerConn) {
s.log.Printf("connect %s for udp", serverConn.Target())
//socks client
client, err := s.HandshakeSocksParent(&outconn, "udp", serverConn.Target(), serverConn.AuthData(), false)
client, err := s.HandshakeSocksParent(s.getParentAuth(lbAddr), &outconn, "udp", serverConn.Target(), serverConn.AuthData(), false)
if err != nil {
clean("handshake fail", fmt.Sprintf("%s", err))
return

View File

@ -13,6 +13,7 @@ import (
"runtime/debug"
"strconv"
"strings"
"sync"
"time"
"github.com/snail007/goproxy/core/cs/server"
@ -20,6 +21,7 @@ import (
"github.com/snail007/goproxy/services"
"github.com/snail007/goproxy/utils"
"github.com/snail007/goproxy/utils/conncrypt"
cryptool "github.com/snail007/goproxy/utils/crypt"
"github.com/snail007/goproxy/utils/datasize"
"github.com/snail007/goproxy/utils/dnsx"
"github.com/snail007/goproxy/utils/iolimiter"
@ -71,6 +73,7 @@ type SPSArgs struct {
LoadBalanceRetryTime *int
LoadBalanceHashTarget *bool
LoadBalanceOnlyHA *bool
ParentTLSSingle *bool
RateLimit *string
RateLimitBytes float64
@ -91,6 +94,8 @@ type SPS struct {
udpLocalKey []byte
udpParentKey []byte
jumper *jumper.Jumper
parentAuthData *sync.Map
parentCipherData *sync.Map
}
func NewSPS() services.Service {
@ -100,6 +105,8 @@ func NewSPS() services.Service {
serverChannels: []*server.ServerChannel{},
userConns: mapx.NewConcurrentMap(),
udpRelatedPacketConns: mapx.NewConcurrentMap(),
parentAuthData: &sync.Map{},
parentCipherData: &sync.Map{},
}
}
func (s *SPS) CheckArgs() (err error) {
@ -121,10 +128,12 @@ func (s *SPS) CheckArgs() (err error) {
return
}
if *s.cfg.ParentType == "tls" || *s.cfg.LocalType == "tls" {
if !*s.cfg.ParentTLSSingle {
s.cfg.CertBytes, s.cfg.KeyBytes, err = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
if err != nil {
return
}
}
if *s.cfg.CaCertFile != "" {
s.cfg.CaCertBytes, err = ioutil.ReadFile(*s.cfg.CaCertFile)
if err != nil {
@ -166,7 +175,10 @@ func (s *SPS) InitService() (err error) {
}
if len(*s.cfg.Parent) > 0 {
s.InitLB()
err = s.InitLB()
if err != nil {
return
}
}
err = s.InitBasicAuth()
@ -208,6 +220,8 @@ func (s *SPS) StopService() {
s.udpParentKey = nil
s.udpRelatedPacketConns = nil
s.userConns = nil
s.parentAuthData = nil
s.parentCipherData = nil
s = nil
}()
for _, sc := range s.serverChannels {
@ -257,6 +271,9 @@ func (s *SPS) Start(args interface{}, log *logger.Logger) (err error) {
} else if *s.cfg.LocalType == "kcp" {
err = sc.ListenKCP(s.cfg.KCP, s.callback, s.log)
}
if err != nil {
return
}
if *s.cfg.ParentServiceType == "socks" {
err = s.RunSSUDP(addr)
} else {
@ -431,8 +448,8 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (lbAddr string, err error) {
utils.CloseConn(inConn)
return
}
if *s.cfg.ParentAuth != "" || *s.cfg.ParentSSKey != "" || s.IsBasicAuth() {
ParentAuth := s.getParentAuth(lbAddr)
if ParentAuth != "" || *s.cfg.ParentSSKey != "" || s.IsBasicAuth() {
forwardBytes = utils.RemoveProxyHeaders(forwardBytes)
}
@ -452,8 +469,8 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (lbAddr string, err error) {
pb.WriteString("Connection: Keep-Alive\r\n")
u := ""
if *s.cfg.ParentAuth != "" {
a := strings.Split(*s.cfg.ParentAuth, ":")
if ParentAuth != "" {
a := strings.Split(ParentAuth, ":")
if len(a) != 2 {
err = fmt.Errorf("parent auth data format error")
return
@ -504,7 +521,7 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (lbAddr string, err error) {
s.log.Printf("connect %s", address)
//socks client
_, err = s.HandshakeSocksParent(&outConn, "tcp", address, auth, false)
_, err = s.HandshakeSocksParent(ParentAuth, &outConn, "tcp", address, auth, false)
if err != nil {
s.log.Printf("handshake fail, %s", err)
return
@ -517,7 +534,7 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (lbAddr string, err error) {
return
}
outConn, err = ss.DialWithRawAddr(&outConn, ra, "", s.parentCipher.Copy())
outConn, err = ss.DialWithRawAddr(&outConn, ra, "", s.getParentCipher(lbAddr))
if err != nil {
err = fmt.Errorf("dial ss parent fail, err : %s", err)
return
@ -577,10 +594,41 @@ func (s *SPS) InitBasicAuth() (err error) {
}
return
}
func (s *SPS) InitLB() {
func (s *SPS) InitLB() (err error) {
configs := lb.BackendsConfig{}
for _, addr := range *s.cfg.Parent {
_addrInfo := strings.Split(addr, "@")
var _addrInfo []string
if strings.Contains(addr, "#") {
_s := addr[:strings.Index(addr, "#")]
_auth, err := cryptool.CryptTools.Base64Decode(_s)
if err != nil {
s.log.Printf("decoding parent auth data [ %s ] fail , error : %s", _s, err)
return err
}
_addrInfo = strings.Split(addr[strings.Index(addr, "#")+1:], "@")
if *s.cfg.ParentServiceType == "ss" {
_s := strings.Split(_auth, ":")
m := _s[0]
k := _s[1]
if m == "" {
m = *s.cfg.ParentSSMethod
}
if k == "" {
k = *s.cfg.ParentSSKey
}
cipher, err := ss.NewCipher(m, k)
if err != nil {
s.log.Printf("error generating cipher, ssMethod: %s, ssKey: %s, error : %s", m, k, err)
return err
}
s.parentCipherData.Store(_addrInfo[0], cipher)
} else {
s.parentAuthData.Store(_addrInfo[0], _auth)
}
} else {
_addrInfo = strings.Split(addr, "@")
}
_addr := _addrInfo[0]
weight := 1
if len(_addrInfo) == 2 {
@ -597,6 +645,19 @@ func (s *SPS) InitLB() {
}
LB := lb.NewGroup(utils.LBMethod(*s.cfg.LoadBalanceMethod), configs, &s.domainResolver, s.log, *s.cfg.Debug)
s.lb = &LB
return
}
func (s *SPS) getParentAuth(lbAddr string) string {
if v, ok := s.parentAuthData.Load(lbAddr); ok {
return v.(string)
}
return *s.cfg.ParentAuth
}
func (s *SPS) getParentCipher(lbAddr string) *ss.Cipher {
if v, ok := s.parentCipherData.Load(lbAddr); ok {
return v.(*ss.Cipher).Copy()
}
return s.parentCipher.Copy()
}
func (s *SPS) IsBasicAuth() bool {
return *s.cfg.AuthFile != "" || len(*s.cfg.Auth) > 0 || *s.cfg.AuthURL != ""
@ -654,12 +715,21 @@ func (s *SPS) GetParentConn(address string) (conn net.Conn, err error) {
if *s.cfg.ParentType == "tls" {
if s.jumper == nil {
var _conn tls.Conn
if *s.cfg.ParentTLSSingle {
_conn, err = utils.SingleTlsConnectHost(address, *s.cfg.Timeout, s.cfg.CaCertBytes)
} else {
_conn, err = utils.TlsConnectHost(address, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes)
}
if err == nil {
conn = net.Conn(&_conn)
}
} else {
conf, err := utils.TlsConfig(s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes)
var conf *tls.Config
if *s.cfg.ParentTLSSingle {
conf, err = utils.SingleTlsConfig(s.cfg.CaCertBytes)
} else {
conf, err = utils.TlsConfig(s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes)
}
if err != nil {
return nil, err
}
@ -691,9 +761,9 @@ func (s *SPS) GetParentConn(address string) (conn net.Conn, err error) {
}
return
}
func (s *SPS) HandshakeSocksParent(outconn *net.Conn, network, dstAddr string, auth socks.Auth, fromSS bool) (client *socks.ClientConn, err error) {
if *s.cfg.ParentAuth != "" {
a := strings.Split(*s.cfg.ParentAuth, ":")
func (s *SPS) HandshakeSocksParent(parentAuth string, outconn *net.Conn, network, dstAddr string, auth socks.Auth, fromSS bool) (client *socks.ClientConn, err error) {
if parentAuth != "" {
a := strings.Split(parentAuth, ":")
if len(a) != 2 {
err = fmt.Errorf("parent auth data format error")
return
@ -717,7 +787,9 @@ func (s *SPS) ParentUDPKey() (key []byte) {
return []byte(v)[:24]
}
case "tls":
if s.cfg.KeyBytes != nil {
return s.cfg.KeyBytes[:24]
}
case "kcp":
v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.KCP.Key)))
return []byte(v)[:24]

View File

@ -88,7 +88,7 @@ func (s *SPS) RunSSUDP(addr string) (err error) {
return
}
client, err := s.HandshakeSocksParent(&outconn, "udp", socksPacket.Addr(), socks.Auth{}, true)
client, err := s.HandshakeSocksParent(s.getParentAuth(lbAddr), &outconn, "udp", socksPacket.Addr(), socks.Auth{}, true)
if err != nil {
clean("handshake fail", fmt.Sprintf("%s", err))
return

38
utils/crypt/misc.go Normal file
View File

@ -0,0 +1,38 @@
package utils
import (
"crypto/md5"
"encoding/base64"
"encoding/hex"
)
type CryptTool struct{}
var CryptTools = NewCryptTool()
func NewCryptTool() *CryptTool {
return &CryptTool{}
}
func (encrypt *CryptTool) Base64Encode(str string) string {
return string([]byte(base64.StdEncoding.EncodeToString([]byte(str))))
}
func (encrypt *CryptTool) Base64EncodeBytes(bytes []byte) []byte {
return []byte(base64.StdEncoding.EncodeToString(bytes))
}
func (encrypt *CryptTool) Base64Decode(str string) (string, error) {
by, err := base64.StdEncoding.DecodeString(str)
return string(by), err
}
func (encrypt *CryptTool) Base64DecodeBytes(str string) ([]byte, error) {
return base64.StdEncoding.DecodeString(str)
}
func (encrypt *CryptTool) MD5(str string) string {
hash := md5.New()
hash.Write([]byte(str))
return hex.EncodeToString(hash.Sum(nil))
}

View File

@ -122,12 +122,55 @@ func ioCopy(dst io.ReadWriter, src io.ReadWriter) (err error) {
}
}
}
func SingleTlsConnectHost(host string, timeout int, caCertBytes []byte) (conn tls.Conn, err error) {
h := strings.Split(host, ":")
port, _ := strconv.Atoi(h[1])
return SingleTlsConnect(h[0], port, timeout, caCertBytes)
}
func SingleTlsConnect(host string, port, timeout int, caCertBytes []byte) (conn tls.Conn, err error) {
conf, err := getRequestSingleTlsConfig(caCertBytes)
if err != nil {
return
}
_conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", host, port), time.Duration(timeout)*time.Millisecond)
if err != nil {
return
}
return *tls.Client(_conn, conf), err
}
func SingleTlsConfig(caCertBytes []byte) (conf *tls.Config, err error) {
return getRequestSingleTlsConfig(caCertBytes)
}
func getRequestSingleTlsConfig(caCertBytes []byte) (conf *tls.Config, err error) {
conf = &tls.Config{InsecureSkipVerify: true}
serverCertPool := x509.NewCertPool()
if caCertBytes != nil {
ok := serverCertPool.AppendCertsFromPEM(caCertBytes)
if !ok {
err = errors.New("failed to parse root certificate")
}
conf.RootCAs = serverCertPool
conf.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
opts := x509.VerifyOptions{
Roots: serverCertPool,
}
for _, rawCert := range rawCerts {
cert, _ := x509.ParseCertificate(rawCert)
_, err := cert.Verify(opts)
if err != nil {
return err
}
}
return nil
}
}
return
}
func TlsConnectHost(host string, timeout int, certBytes, keyBytes, caCertBytes []byte) (conn tls.Conn, err error) {
h := strings.Split(host, ":")
port, _ := strconv.Atoi(h[1])
return TlsConnect(h[0], port, timeout, certBytes, keyBytes, caCertBytes)
}
func TlsConnect(host string, port, timeout int, certBytes, keyBytes, caCertBytes []byte) (conn tls.Conn, err error) {
conf, err := getRequestTlsConfig(certBytes, keyBytes, caCertBytes)
if err != nil {

View File

@ -73,6 +73,7 @@ func NewGroup(selectType int, configs BackendsConfig, dr *dnsx.DomainResolver, l
}
}
func (g *Group) Select(srcAddr string, onlyHa bool) (addr string) {
addr=""
if len(g.bks) == 1 {
return g.bks[0].Address
}

View File

@ -106,15 +106,16 @@ func (s *ServerConn) Port() string {
}
func (s *ServerConn) Handshake() (err error) {
remoteAddr := (*s.conn).RemoteAddr()
localAddr := (*s.conn).LocalAddr()
//协商开始
//method select request
var methodReq MethodsRequest
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
(*s.conn).SetReadDeadline(time.Now().Add(s.timeout))
methodReq, e := NewMethodsRequest((*s.conn), s.header)
(*s.conn).SetReadDeadline(time.Time{})
if e != nil {
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
(*s.conn).SetReadDeadline(time.Now().Add(s.timeout))
methodReq.Reply(Method_NONE_ACCEPTABLE)
(*s.conn).SetReadDeadline(time.Time{})
err = fmt.Errorf("new methods request fail,ERR: %s", e)
@ -123,7 +124,7 @@ func (s *ServerConn) Handshake() (err error) {
//log.Printf("%v,s.auth == %v && methodReq.Select(Method_NO_AUTH) %v", methodReq.methods, s.auth, methodReq.Select(Method_NO_AUTH))
if s.auth == nil && methodReq.Select(Method_NO_AUTH) && !methodReq.Select(Method_USER_PASS) {
// if !methodReq.Select(Method_NO_AUTH) {
// (*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
// (*s.conn).SetReadDeadline(time.Now().Add(s.timeout))
// methodReq.Reply(Method_NONE_ACCEPTABLE)
// (*s.conn).SetReadDeadline(time.Time{})
// err = fmt.Errorf("none method found : Method_NO_AUTH")
@ -131,7 +132,7 @@ func (s *ServerConn) Handshake() (err error) {
// }
s.method = Method_NO_AUTH
//method select reply
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
(*s.conn).SetReadDeadline(time.Now().Add(s.timeout))
err = methodReq.Reply(Method_NO_AUTH)
(*s.conn).SetReadDeadline(time.Time{})
if err != nil {
@ -142,7 +143,7 @@ func (s *ServerConn) Handshake() (err error) {
} else {
//auth
if !methodReq.Select(Method_USER_PASS) {
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
(*s.conn).SetReadDeadline(time.Now().Add(s.timeout))
methodReq.Reply(Method_NONE_ACCEPTABLE)
(*s.conn).SetReadDeadline(time.Time{})
err = fmt.Errorf("none method found : Method_USER_PASS")
@ -150,7 +151,7 @@ func (s *ServerConn) Handshake() (err error) {
}
s.method = Method_USER_PASS
//method reply need auth
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
(*s.conn).SetReadDeadline(time.Now().Add(s.timeout))
err = methodReq.Reply(Method_USER_PASS)
(*s.conn).SetReadDeadline(time.Time{})
if err != nil {
@ -160,7 +161,7 @@ func (s *ServerConn) Handshake() (err error) {
//read auth
buf := make([]byte, 500)
var n int
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
(*s.conn).SetReadDeadline(time.Now().Add(s.timeout))
n, err = (*s.conn).Read(buf)
(*s.conn).SetReadDeadline(time.Time{})
if err != nil {
@ -172,9 +173,10 @@ func (s *ServerConn) Handshake() (err error) {
s.password = string(r[2+r[1]+1:])
//err = fmt.Errorf("user:%s,pass:%s", user, pass)
//auth
_addr := strings.Split(remoteAddr.String(), ":")
if s.auth == nil || s.auth.CheckUserPass(s.user, s.password, _addr[0], "") {
(*s.conn).SetDeadline(time.Now().Add(time.Millisecond * time.Duration(s.timeout)))
_userAddr := strings.Split(remoteAddr.String(), ":")
_localAddr := strings.Split(localAddr.String(), ":")
if s.auth == nil || s.auth.CheckUserPass(s.user, s.password, _userAddr[0], _localAddr[0], "") {
(*s.conn).SetDeadline(time.Now().Add(s.timeout))
_, err = (*s.conn).Write([]byte{0x01, 0x00})
(*s.conn).SetDeadline(time.Time{})
if err != nil {
@ -182,7 +184,7 @@ func (s *ServerConn) Handshake() (err error) {
return
}
} else {
(*s.conn).SetDeadline(time.Now().Add(time.Millisecond * time.Duration(s.timeout)))
(*s.conn).SetDeadline(time.Now().Add(s.timeout))
_, err = (*s.conn).Write([]byte{0x01, 0x01})
(*s.conn).SetDeadline(time.Time{})
if err != nil {
@ -194,7 +196,7 @@ func (s *ServerConn) Handshake() (err error) {
}
}
//request detail
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
(*s.conn).SetReadDeadline(time.Now().Add(s.timeout))
request, e := NewRequest(*s.conn)
(*s.conn).SetReadDeadline(time.Time{})
if e != nil {

View File

@ -268,18 +268,23 @@ func (ba *BasicAuth) Add(userpassArr []string) (n int) {
}
return
}
func (ba *BasicAuth) CheckUserPass(user, pass, ip, target string) (ok bool) {
return ba.Check(user+":"+pass, ip, target)
func (ba *BasicAuth) Delete(userArr []string) {
for _, u := range userArr {
ba.data.Remove(u)
}
}
func (ba *BasicAuth) Check(userpass string, ip, target string) (ok bool) {
func (ba *BasicAuth) CheckUserPass(user, pass, userIP, localIP, target string) (ok bool) {
return ba.Check(user+":"+pass, userIP, localIP, target)
}
func (ba *BasicAuth) Check(userpass string, userIP, localIP, target string) (ok bool) {
u := strings.Split(strings.Trim(userpass, " "), ":")
if len(u) == 2 {
if p, _ok := ba.data.Get(u[0]); _ok {
return p.(string) == u[1]
}
if ba.authURL != "" {
err := ba.checkFromURL(userpass, ip, target)
err := ba.checkFromURL(userpass, userIP, localIP, target)
if err == nil {
return true
}
@ -289,7 +294,7 @@ func (ba *BasicAuth) Check(userpass string, ip, target string) (ok bool) {
}
return
}
func (ba *BasicAuth) checkFromURL(userpass, ip, target string) (err error) {
func (ba *BasicAuth) checkFromURL(userpass, userIP, localIP, target string) (err error) {
u := strings.Split(strings.Trim(userpass, " "), ":")
if len(u) != 2 {
return
@ -301,7 +306,7 @@ func (ba *BasicAuth) checkFromURL(userpass, ip, target string) (err error) {
} else {
URL += "?"
}
URL += fmt.Sprintf("user=%s&pass=%s&ip=%s&target=%s", u[0], u[1], ip, url.QueryEscape(target))
URL += fmt.Sprintf("user=%s&pass=%s&ip=%s&local_ip=%s&target=%s", u[0], u[1], userIP, localIP, url.QueryEscape(target))
getURL := URL
var domain string
if ba.dns != nil {
@ -318,7 +323,7 @@ func (ba *BasicAuth) checkFromURL(userpass, ip, target string) (err error) {
if err == nil && code == ba.authOkCode {
break
} else if err != nil {
err = fmt.Errorf("auth fail from url %s,resonse err:%s , %s", URL, err, ip)
err = fmt.Errorf("auth fail from url %s,resonse err:%s , %s -> %s", URL, err, userIP, localIP)
} else {
if len(body) > 0 {
err = fmt.Errorf(string(body[0:100]))
@ -329,7 +334,7 @@ func (ba *BasicAuth) checkFromURL(userpass, ip, target string) (err error) {
if len(b) > 50 {
b = b[:50]
}
err = fmt.Errorf("auth fail from url %s,resonse code: %d, except: %d , %s , %s", URL, code, ba.authOkCode, ip, b)
err = fmt.Errorf("auth fail from url %s,resonse code: %d, except: %d , %s -> %s, %s", URL, code, ba.authOkCode, userIP, localIP, b)
}
if err != nil && tryCount < ba.authRetry {
ba.log.Print(err)
@ -483,7 +488,8 @@ func (req *HTTPRequest) GetAuthDataStr() (basicInfo string, err error) {
return
}
func (req *HTTPRequest) BasicAuth() (err error) {
addr := strings.Split((*req.conn).RemoteAddr().String(), ":")
userIP := strings.Split((*req.conn).RemoteAddr().String(), ":")
localIP := strings.Split((*req.conn).LocalAddr().String(), ":")
URL := ""
if req.IsHTTPS() {
URL = "https://" + req.Host
@ -494,7 +500,7 @@ func (req *HTTPRequest) BasicAuth() (err error) {
if err != nil {
return
}
authOk := (*req.basicAuth).Check(string(user), addr[0], URL)
authOk := (*req.basicAuth).Check(string(user), userIP[0], localIP[0], URL)
//log.Printf("auth %s,%v", string(user), authOk)
if !authOk {
fmt.Fprintf((*req.conn), "HTTP/1.1 %s Proxy Authentication Required\r\n\r\nProxy Authentication Required", "407")