Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>

This commit is contained in:
arraykeys@gmail.com
2017-10-20 10:20:28 +08:00
parent bde10ad8ef
commit 95db78bc0b
6 changed files with 105 additions and 23 deletions

View File

@ -1,6 +1,6 @@
proxy更新日志 proxy更新日志
v3.4 v3.4
1. 1.socks5代理新增了用户名密码验证支持.
v3.3 v3.3
1.修复了socks代理模式对证书文件的判断逻辑. 1.修复了socks代理模式对证书文件的判断逻辑.

View File

@ -31,6 +31,8 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务
- [v3.0手册](https://github.com/snail007/goproxy/tree/v3.0) - [v3.0手册](https://github.com/snail007/goproxy/tree/v3.0)
- [v2.x手册](https://github.com/snail007/goproxy/tree/v2.2) - [v2.x手册](https://github.com/snail007/goproxy/tree/v2.2)
[TOC]
### Fast Start ### Fast Start
提示:所有操作需要root权限. 提示:所有操作需要root权限.
**0.如果你的VPS是linux64位的系统,那么只需要执行下面一句,就可以完成自动安装和配置.** **0.如果你的VPS是linux64位的系统,那么只需要执行下面一句,就可以完成自动安装和配置.**
@ -420,11 +422,18 @@ server连接到bridge的时候,如果同时有多个client连接到同一个brid
那么访问本地的28080端口就是通过VPS访问目标地址. 那么访问本地的28080端口就是通过VPS访问目标地址.
**5.7.查看帮助** **5.7.认证**
对于socks5代理协议我们可以进行用户名密码认证,认证的用户名和密码可以在命令行指定
`./proxy socks -t tcp -p ":33080" -a "user1:pass1" -a "user2:pass2"`
多个用户,重复-a参数即可.
也可以放在文件中,格式是一行一个"用户名:密码",然后用-F指定.
`./proxy socks -t tcp -p ":33080" -F auth-file.txt`
如果没有-a或-F参数,就是关闭认证.
**5.8.查看帮助**
`./proxy help socks` `./proxy help socks`
### TODO ### TODO
- SOCKS5增加用户名密码认证?
- http,socks代理多个上级负载均衡? - http,socks代理多个上级负载均衡?
- 内网穿透server<->bridge心跳机制? - 内网穿透server<->bridge心跳机制?
- 欢迎加群反馈... - 欢迎加群反馈...

View File

@ -124,6 +124,8 @@ func initConfig() (err error) {
socksArgs.Interval = socks.Flag("interval", "check domain if blocked every interval seconds").Default("10").Int() socksArgs.Interval = socks.Flag("interval", "check domain if blocked every interval seconds").Default("10").Int()
socksArgs.Blocked = socks.Flag("blocked", "blocked domain file , one domain each line").Default("blocked").Short('b').String() socksArgs.Blocked = socks.Flag("blocked", "blocked domain file , one domain each line").Default("blocked").Short('b').String()
socksArgs.Direct = socks.Flag("direct", "direct domain file , one domain each line").Default("direct").Short('d').String() socksArgs.Direct = socks.Flag("direct", "direct domain file , one domain each line").Default("direct").Short('d').String()
socksArgs.AuthFile = socks.Flag("auth-file", "http basic auth file,\"username:password\" each line in file").Short('F').String()
socksArgs.Auth = socks.Flag("auth", "socks auth username and password, mutiple user repeat -a ,such as: -a user1:pass1 -a user2:pass2").Short('a').Strings()
//parse args //parse args
serviceName := kingpin.MustParse(app.Parse(os.Args[1:])) serviceName := kingpin.MustParse(app.Parse(os.Args[1:]))
flags := log.Ldate flags := log.Ldate

View File

@ -118,6 +118,8 @@ type SocksArgs struct {
Interval *int Interval *int
Blocked *string Blocked *string
Direct *string Direct *string
AuthFile *string
Auth *[]string
} }
func (a *TCPArgs) Protocol() string { func (a *TCPArgs) Protocol() string {

View File

@ -78,6 +78,7 @@ func (s *Socks) CheckArgs() {
} }
func (s *Socks) InitService() { func (s *Socks) InitService() {
s.InitBasicAuth()
s.checker = utils.NewChecker(*s.cfg.Timeout, int64(*s.cfg.Interval), *s.cfg.Blocked, *s.cfg.Direct) s.checker = utils.NewChecker(*s.cfg.Timeout, int64(*s.cfg.Interval), *s.cfg.Blocked, *s.cfg.Direct)
if *s.cfg.ParentType == "ssh" { if *s.cfg.ParentType == "ssh" {
err := s.ConnectSSH() err := s.ConnectSSH()
@ -94,9 +95,6 @@ func (s *Socks) InitService() {
if err != nil { if err != nil {
if s.sshClient != nil { if s.sshClient != nil {
s.sshClient.Close() s.sshClient.Close()
if s.sshClient.Conn != nil {
s.sshClient.Conn.Close()
}
} }
log.Printf("ssh offline, retrying...") log.Printf("ssh offline, retrying...")
s.ConnectSSH() s.ConnectSSH()
@ -290,18 +288,26 @@ func (s *Socks) socksConnCallback(inConn net.Conn) {
} }
utils.CloseConn(&inConn) utils.CloseConn(&inConn)
}() }()
//协商开始
//method select request //method select request
inConn.SetReadDeadline(time.Now().Add(time.Second * 3))
methodReq, err := socks.NewMethodsRequest(inConn) methodReq, err := socks.NewMethodsRequest(inConn)
if err != nil || !methodReq.Select(socks.Method_NO_AUTH) { inConn.SetReadDeadline(time.Time{})
if err != nil {
methodReq.Reply(socks.Method_NONE_ACCEPTABLE) methodReq.Reply(socks.Method_NONE_ACCEPTABLE)
utils.CloseConn(&inConn) utils.CloseConn(&inConn)
if err != nil {
log.Printf("new methods request fail,ERR: %s", err) log.Printf("new methods request fail,ERR: %s", err)
}
return return
} }
if !s.IsBasicAuth() {
if !methodReq.Select(socks.Method_NO_AUTH) {
methodReq.Reply(socks.Method_NONE_ACCEPTABLE)
utils.CloseConn(&inConn)
log.Printf("none method found : Method_NO_AUTH")
return
}
//method select reply //method select reply
err = methodReq.Reply(socks.Method_NO_AUTH) err = methodReq.Reply(socks.Method_NO_AUTH)
if err != nil { if err != nil {
@ -309,8 +315,44 @@ func (s *Socks) socksConnCallback(inConn net.Conn) {
utils.CloseConn(&inConn) utils.CloseConn(&inConn)
return return
} }
// log.Printf("% x", methodReq.Bytes()) // log.Printf("% x", methodReq.Bytes())
} else {
//auth
if !methodReq.Select(socks.Method_USER_PASS) {
methodReq.Reply(socks.Method_NONE_ACCEPTABLE)
utils.CloseConn(&inConn)
log.Printf("none method found : Method_USER_PASS")
return
}
//method reply need auth
err = methodReq.Reply(socks.Method_USER_PASS)
if err != nil {
log.Printf("reply answer data fail,ERR: %s", err)
utils.CloseConn(&inConn)
return
}
//read auth
buf := make([]byte, 500)
inConn.SetReadDeadline(time.Now().Add(time.Second * 3))
n, err := inConn.Read(buf)
inConn.SetReadDeadline(time.Time{})
if err != nil {
utils.CloseConn(&inConn)
return
}
r := buf[:n]
user := string(r[2 : r[1]+2])
pass := string(r[2+r[1]+1:])
//log.Printf("user:%s,pass:%s", user, pass)
//auth
if s.basicAuth.CheckUserPass(user, pass) {
inConn.Write([]byte{0x01, 0x00})
} else {
inConn.Write([]byte{0x01, 0x01})
utils.CloseConn(&inConn)
return
}
}
//request detail //request detail
request, err := socks.NewRequest(inConn) request, err := socks.NewRequest(inConn)
@ -319,6 +361,7 @@ func (s *Socks) socksConnCallback(inConn net.Conn) {
utils.CloseConn(&inConn) utils.CloseConn(&inConn)
return return
} }
//协商结束
switch request.CMD() { switch request.CMD() {
case socks.CMD_BIND: case socks.CMD_BIND:
@ -447,7 +490,7 @@ func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string) (outConn n
return return
} }
wait := make(chan bool, 1) wait := make(chan bool, 1)
func() { go func() {
defer func() { defer func() {
if err == nil { if err == nil {
err = recover() err = recover()
@ -458,8 +501,9 @@ func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string) (outConn n
}() }()
select { select {
case <-wait: case <-wait:
case <-time.After(time.Second * 5): case <-time.After(time.Millisecond * time.Duration(*s.cfg.Timeout) * 2):
err = fmt.Errorf("ssh dial %s timeout", host) err = fmt.Errorf("ssh dial %s timeout", host)
s.sshClient.Close()
} }
if err != nil { if err != nil {
log.Printf("connect ssh fail, ERR: %s, retrying...", err) log.Printf("connect ssh fail, ERR: %s, retrying...", err)
@ -498,3 +542,23 @@ func (s *Socks) ConnectSSH() (err error) {
<-s.lockChn <-s.lockChn
return return
} }
func (s *Socks) InitBasicAuth() (err error) {
s.basicAuth = utils.NewBasicAuth()
if *s.cfg.AuthFile != "" {
var n = 0
n, err = s.basicAuth.AddFromFile(*s.cfg.AuthFile)
if err != nil {
err = fmt.Errorf("auth-file ERR:%s", err)
return
}
log.Printf("auth data added from file %d , total:%d", n, s.basicAuth.Total())
}
if len(*s.cfg.Auth) > 0 {
n := s.basicAuth.Add(*s.cfg.Auth)
log.Printf("auth data added %d, total:%d", n, s.basicAuth.Total())
}
return
}
func (s *Socks) IsBasicAuth() bool {
return *s.cfg.AuthFile != "" || len(*s.cfg.Auth) > 0
}

View File

@ -213,7 +213,12 @@ func (ba *BasicAuth) Add(userpassArr []string) (n int) {
} }
return return
} }
func (ba *BasicAuth) CheckUserPass(user, pass string) (ok bool) {
if p, _ok := ba.data.Get(user); _ok {
return p.(string) == pass
}
return
}
func (ba *BasicAuth) Check(userpass string) (ok bool) { func (ba *BasicAuth) Check(userpass string) (ok bool) {
u := strings.Split(strings.Trim(userpass, " "), ":") u := strings.Split(strings.Trim(userpass, " "), ":")
if len(u) == 2 { if len(u) == 2 {