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

This commit is contained in:
arraykeys@gmail.com
2017-10-19 14:56:40 +08:00
parent e28d5449b5
commit 3020dc5c94
12 changed files with 135 additions and 35 deletions

View File

@ -1,6 +1,9 @@
proxy更新日志
v3.3
1.修复了socks代理模式对证书文件的判断逻辑.
2.增强了http代理,socks代理的ssh中转模式的稳定性.
3.socks代理tls,tcp模式新增了CMD_ASSOCIATE(udp)支持.socks代理ssh模式不支持udp.
4.修复了http代理某些情况下会崩溃的bug.
v3.2
1.内网穿透功能server端-r参数增加了协议和key设置.

View File

@ -423,7 +423,9 @@ server连接到bridge的时候,如果同时有多个client连接到同一个brid
`./proxy help socks`
### TODO
- SOCKS5增加用户名密码认证
- SOCKS5增加用户名密码认证?
- http,socks代理多个上级负载均衡?
- 欢迎加群反馈...
### 如何使用源码?
cd进入你的go src目录,然后git clone https://github.com/snail007/goproxy.git ./proxy 即可.

View File

@ -35,7 +35,7 @@ func initConfig() (err error) {
//build srvice args
app = kingpin.New("proxy", "happy with proxy")
app.Author("snail").Version(APP_VERSION)
debug := app.Flag("debug", "debug log output").Default("false").Bool()
//########http#########
http := app.Command("http", "proxy on http mode")
httpArgs.Parent = http.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
@ -126,7 +126,13 @@ func initConfig() (err error) {
socksArgs.Direct = socks.Flag("direct", "direct domain file , one domain each line").Default("direct").Short('d').String()
//parse args
serviceName := kingpin.MustParse(app.Parse(os.Args[1:]))
flags := log.Ldate
if *debug {
flags |= log.Lshortfile | log.Lmicroseconds
} else {
flags |= log.Ltime
}
log.SetFlags(flags)
poster()
//regist services and run service
services.Regist("http", services.NewHTTP(), httpArgs)

View File

@ -6,7 +6,7 @@ fi
mkdir /tmp/proxy
cd /tmp/proxy
wget https://github.com/reddec/monexec/releases/download/v0.1.1/monexec_0.1.1_linux_amd64.tar.gz
wget https://github.com/snail007/goproxy/releases/download/v3.2/proxy-linux-amd64.tar.gz
wget https://github.com/snail007/goproxy/releases/download/v3.3/proxy-linux-amd64.tar.gz
# install monexec
tar zxvf monexec_0.1.1_linux_amd64.tar.gz

View File

@ -9,7 +9,7 @@ import (
"syscall"
)
const APP_VERSION = "3.2"
const APP_VERSION = "3.3"
func main() {
err := initConfig()

View File

@ -1,5 +1,5 @@
#!/bin/bash
VER="3.2"
VER="3.3"
RELEASE="release-${VER}"
rm -rf .cert
mkdir .cert

View File

@ -20,6 +20,7 @@ type HTTP struct {
checker utils.Checker
basicAuth utils.BasicAuth
sshClient *ssh.Client
lockChn chan bool
}
func NewHTTP() Service {
@ -28,6 +29,7 @@ func NewHTTP() Service {
cfg: HTTPArgs{},
checker: utils.Checker{},
basicAuth: utils.BasicAuth{},
lockChn: make(chan bool, 1),
}
}
func (s *HTTP) CheckArgs() {
@ -115,7 +117,9 @@ func (s *HTTP) callback(inConn net.Conn) {
log.Printf("http(s) conn handler crashed with err : %s \nstack: %s", err, string(debug.Stack()))
}
}()
req, err := utils.NewHTTPRequest(&inConn, 4096, s.IsBasicAuth(), &s.basicAuth)
var err interface{}
var req utils.HTTPRequest
req, err = utils.NewHTTPRequest(&inConn, 4096, s.IsBasicAuth(), &s.basicAuth)
if err != nil {
if err != io.EOF {
log.Printf("decoder error , form %s, ERR:%s", err, inConn.RemoteAddr())
@ -153,7 +157,7 @@ func (s *HTTP) callback(inConn net.Conn) {
utils.CloseConn(&inConn)
}
}
func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *utils.HTTPRequest) (err error) {
func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *utils.HTTPRequest) (err interface{}) {
inAddr := (*inConn).RemoteAddr().String()
inLocalAddr := (*inConn).LocalAddr().String()
//防止死循环
@ -208,18 +212,27 @@ func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *ut
return
}
func (s *HTTP) getSSHConn(host string) (outConn net.Conn, err error) {
func (s *HTTP) getSSHConn(host string) (outConn net.Conn, err interface{}) {
maxTryCount := 1
tryCount := 0
errchn := make(chan interface{}, 1)
RETRY:
if tryCount >= maxTryCount {
return
}
outConn, err = s.sshClient.Dial("tcp", host)
//log.Printf("s.sshClient.Dial, host:%s)", host)
go func() {
defer func() {
if err == nil {
errchn <- recover()
} else {
errchn <- nil
}
}()
outConn, err = s.sshClient.Dial("tcp", host)
}()
err = <-errchn
if err != nil {
log.Printf("connect ssh fail, ERR: %s, retrying...", err)
s.sshClient.Close()
e := s.ConnectSSH()
if e == nil {
tryCount++
@ -232,14 +245,25 @@ RETRY:
return
}
func (s *HTTP) ConnectSSH() (err error) {
select {
case s.lockChn <- true:
default:
err = fmt.Errorf("can not connect at same time")
return
}
config := ssh.ClientConfig{
User: *s.cfg.SSHUser,
Auth: []ssh.AuthMethod{s.cfg.SSHAuthMethod},
Timeout: time.Duration(*s.cfg.Timeout) * time.Millisecond,
User: *s.cfg.SSHUser,
Auth: []ssh.AuthMethod{s.cfg.SSHAuthMethod},
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
}
if s.sshClient != nil {
s.sshClient.Close()
}
s.sshClient, err = ssh.Dial("tcp", *s.cfg.Parent, &config)
<-s.lockChn
return
}
func (s *HTTP) InitOutConnPool() {

View File

@ -37,7 +37,7 @@ func NewSocks() Service {
func (s *Socks) CheckArgs() {
var err error
if *s.cfg.LocalType == "tls" {
log.Println(*s.cfg.CertFile, *s.cfg.KeyFile)
//log.Println(*s.cfg.CertFile, *s.cfg.KeyFile)
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
}
if *s.cfg.Parent != "" {
@ -55,7 +55,6 @@ func (s *Socks) CheckArgs() {
if *s.cfg.SSHKeyFile == "" && *s.cfg.SSHPassword == "" {
log.Fatalf("ssh password or key required")
}
if *s.cfg.SSHPassword != "" {
s.cfg.SSHAuthMethod = ssh.Password(*s.cfg.SSHPassword)
} else {
@ -85,6 +84,25 @@ func (s *Socks) InitService() {
if err != nil {
log.Fatalf("init service fail, ERR: %s", err)
}
go func() {
//循环检查ssh网络连通性
for {
conn, err := utils.ConnectHost(*s.cfg.Parent, *s.cfg.Timeout*2)
if err != nil {
if s.sshClient != nil {
s.sshClient.Close()
if s.sshClient.Conn != nil {
s.sshClient.Conn.Close()
}
}
log.Printf("ssh offline, retrying...")
s.ConnectSSH()
} else {
conn.Close()
}
time.Sleep(time.Second * 3)
}
}()
}
if *s.cfg.ParentType == "ssh" {
log.Println("warn: socks udp not suppored for ssh")
@ -133,17 +151,17 @@ func (s *Socks) UDPKey() []byte {
return s.cfg.KeyBytes[:32]
}
func (s *Socks) udpCallback(b []byte, localAddr, srcAddr *net.UDPAddr) {
newB := b
rawB := b
var err error
if *s.cfg.LocalType == "tls" {
//decode b
newB, err = goaes.Decrypt(s.UDPKey(), b)
rawB, err = goaes.Decrypt(s.UDPKey(), b)
if err != nil {
log.Printf("decrypt udp packet fail from %s", srcAddr.String())
return
}
}
p, err := socks.ParseUDPPacket(newB)
p, err := socks.ParseUDPPacket(rawB)
log.Printf("udp revecived:%v", len(p.Data()))
if err != nil {
log.Printf("parse udp packet fail, ERR:%s", err)
@ -154,7 +172,7 @@ func (s *Socks) udpCallback(b []byte, localAddr, srcAddr *net.UDPAddr) {
//有上级代理,转发给上级
if *s.cfg.ParentType == "tls" {
//encode b
newB, err = goaes.Encrypt(s.UDPKey(), newB)
rawB, err = goaes.Encrypt(s.UDPKey(), rawB)
if err != nil {
log.Printf("encrypt udp data fail to %s", *s.cfg.Parent)
return
@ -172,18 +190,20 @@ func (s *Socks) udpCallback(b []byte, localAddr, srcAddr *net.UDPAddr) {
return
}
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout*2)))
_, err = conn.Write(newB)
log.Printf("udp request:%v", len(newB))
_, err = conn.Write(rawB)
log.Printf("udp request:%v", len(rawB))
if err != nil {
log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err)
conn.Close()
return
}
//log.Printf("send udp packet to %s success", dstAddr.String())
buf := make([]byte, 1024)
buf := make([]byte, 10*1024)
length, _, err := conn.ReadFromUDP(buf)
if err != nil {
log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err)
conn.Close()
return
}
respBody := buf[0:length]
@ -194,6 +214,7 @@ func (s *Socks) udpCallback(b []byte, localAddr, srcAddr *net.UDPAddr) {
respBody, err = goaes.Decrypt(s.UDPKey(), respBody)
if err != nil {
log.Printf("encrypt udp data fail to %s", *s.cfg.Parent)
conn.Close()
return
}
}
@ -201,6 +222,7 @@ func (s *Socks) udpCallback(b []byte, localAddr, srcAddr *net.UDPAddr) {
d, err := goaes.Encrypt(s.UDPKey(), respBody)
if err != nil {
log.Printf("encrypt udp data fail from %s", dstAddr.String())
conn.Close()
return
}
s.udpSC.UDPListener.WriteToUDP(d, srcAddr)
@ -223,33 +245,38 @@ func (s *Socks) udpCallback(b []byte, localAddr, srcAddr *net.UDPAddr) {
log.Printf("connect to udp %s fail,ERR:%s", dstAddr.String(), err)
return
}
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout*2)))
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout*3)))
_, err = conn.Write(p.Data())
log.Printf("udp send:%v", len(p.Data()))
if err != nil {
log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err)
conn.Close()
return
}
log.Printf("send udp packet to %s success", dstAddr.String())
buf := make([]byte, 1024)
//log.Printf("send udp packet to %s success", dstAddr.String())
buf := make([]byte, 10*1024)
length, _, err := conn.ReadFromUDP(buf)
if err != nil {
log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err)
conn.Close()
return
}
respBody := buf[0:length]
//封装来自真实服务器的数据,返回给访问者
respPacket := p.NewReply(respBody)
//log.Printf("revecived udp packet from %s", dstAddr.String())
if *s.cfg.LocalType == "tls" {
d, err := goaes.Encrypt(s.UDPKey(), respBody)
d, err := goaes.Encrypt(s.UDPKey(), respPacket)
if err != nil {
log.Printf("encrypt udp data fail from %s", dstAddr.String())
conn.Close()
return
}
s.udpSC.UDPListener.WriteToUDP(d, srcAddr)
} else {
s.udpSC.UDPListener.WriteToUDP(respBody, srcAddr)
s.udpSC.UDPListener.WriteToUDP(respPacket, srcAddr)
}
log.Printf("udp reply:%v", len(respBody))
log.Printf("udp reply:%v", len(respPacket))
}
}
@ -304,18 +331,18 @@ func (s *Socks) socksConnCallback(inConn net.Conn) {
}
func (s *Socks) proxyUDP(inConn *net.Conn, methodReq socks.MethodsRequest, request socks.Request) {
if *s.cfg.ParentType == "ssh" {
utils.CloseConn(inConn)
return
}
host, _, _ := net.SplitHostPort((*inConn).LocalAddr().String())
_, port, _ := net.SplitHostPort(s.udpSC.UDPListener.LocalAddr().String())
// log.Printf("proxy udp on %s", net.JoinHostPort(host, port))
request.UDPReply(socks.REP_SUCCESS, net.JoinHostPort(host, port))
// log.Printf("%v", request.NewReply(socks.REP_SUCCESS, net.JoinHostPort(host, port)))
}
func (s *Socks) proxyTCP(inConn *net.Conn, methodReq socks.MethodsRequest, request socks.Request) {
var outConn net.Conn
defer utils.CloseConn(&outConn)
var err error
var err interface{}
useProxy := true
if *s.cfg.Always {
outConn, err = s.getOutConn(methodReq.Bytes(), request.Bytes(), request.Addr())
@ -370,7 +397,8 @@ func (s *Socks) proxyTCP(inConn *net.Conn, methodReq socks.MethodsRequest, reque
utils.CloseConn(inConn)
utils.CloseConn(&outConn)
}
func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string) (outConn net.Conn, err error) {
func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string) (outConn net.Conn, err interface{}) {
errchn := make(chan interface{}, 1)
switch *s.cfg.ParentType {
case "tls":
fallthrough
@ -413,7 +441,17 @@ func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string) (outConn n
if tryCount >= maxTryCount {
return
}
outConn, err = s.sshClient.Dial("tcp", host)
go func() {
defer func() {
if err == nil {
errchn <- recover()
} else {
errchn <- nil
}
}()
outConn, err = s.sshClient.Dial("tcp", host)
}()
err = <-errchn
if err != nil {
log.Printf("connect ssh fail, ERR: %s, retrying...", err)
e := s.ConnectSSH()

View File

@ -64,12 +64,15 @@ func (s *TunnelServerManager) Start(args interface{}) (err error) {
CertBytes: s.cfg.CertBytes,
KeyBytes: s.cfg.KeyBytes,
Parent: s.cfg.Parent,
CertFile: s.cfg.CertFile,
KeyFile: s.cfg.KeyFile,
Local: &local,
IsUDP: &IsUDP,
Remote: &remote,
Key: &KEY,
Timeout: s.cfg.Timeout,
})
if err != nil {
return
}

View File

@ -315,6 +315,24 @@ func Uniqueid() string {
s := fmt.Sprintf("%d", src.Int63())
return s[len(s)-5:len(s)-1] + fmt.Sprintf("%d", uint64(time.Now().UnixNano()))[8:]
}
func SubStr(str string, start, end int) string {
if len(str) == 0 {
return ""
}
if end >= len(str) {
end = len(str) - 1
}
return str[start:end]
}
func SubBytes(bytes []byte, start, end int) []byte {
if len(bytes) == 0 {
return []byte{}
}
if end >= len(bytes) {
end = len(bytes) - 1
}
return bytes[start:end]
}
func TlsBytes(cert, key string) (certBytes, keyBytes []byte) {
certBytes, err := ioutil.ReadFile(cert)
if err != nil {

View File

@ -242,6 +242,12 @@ func ParseUDPPacket(b []byte) (p UDPPacket, err error) {
func (s *UDPPacket) Header() []byte {
return s.header
}
func (s *UDPPacket) NewReply(data []byte) []byte {
var buf bytes.Buffer
buf.Write(s.header)
buf.Write(data)
return buf.Bytes()
}
func (s *UDPPacket) Host() string {
return s.dstHost
}

View File

@ -256,13 +256,13 @@ func NewHTTPRequest(inConn *net.Conn, bufSize int, isBasicAuth bool, basicAuth *
req.HeadBuf = buf[:len]
index := bytes.IndexByte(req.HeadBuf, '\n')
if index == -1 {
err = fmt.Errorf("http decoder data line err:%s", string(req.HeadBuf)[:50])
err = fmt.Errorf("http decoder data line err:%s", SubStr(string(req.HeadBuf), 0, 50))
CloseConn(inConn)
return
}
fmt.Sscanf(string(req.HeadBuf[:index]), "%s%s", &req.Method, &req.hostOrURL)
if req.Method == "" || req.hostOrURL == "" {
err = fmt.Errorf("http decoder data err:%s", string(req.HeadBuf)[:50])
err = fmt.Errorf("http decoder data err:%s", SubStr(string(req.HeadBuf), 0, 50))
CloseConn(inConn)
return
}