更换库
This commit is contained in:
61
README.md
61
README.md
@@ -1,39 +1,44 @@
|
|||||||
# db_tunnel
|
# db_tunnel
|
||||||
|
|
||||||
结合TELEPORT堡垒机使用的DB SSH数据库隧道工具
|
结合TELEPORT堡垒机使用的DB SSH数据库隧道工具
|
||||||
数据库配置文件请放到/etc或者与bin文件同一路径
|
数据库配置文件请放到/etc或者与bin文件同一路径
|
||||||
|
|
||||||
# build
|
# build
|
||||||
|
|
||||||
git clone https://git.aixiao.me/aixiao/db_tunnel.git
|
git clone https://git.aixiao.me/aixiao/db_tunnel.git
|
||||||
cd db_tunnel
|
cd db_tunnel
|
||||||
bash bash build.sh(可能会提示安装包)
|
bash bash build.sh(可能会提示安装包)
|
||||||
|
|
||||||
# test
|
# test
|
||||||
root@NIUYULING:/mnt/c/Users/niuyuling/Desktop/db_tunnel# ./db_tunnel -h
|
|
||||||
flag needs an argument: -h
|
root@NIUYULING:/mnt/c/Users/niuyuling/Desktop/tunnel# ./bin/tunnel -h
|
||||||
Usage of ./db_tunnel:
|
flag needs an argument: -h
|
||||||
-d run in daemon
|
Usage of ./bin/tunnel:
|
||||||
-f run forever
|
-c int
|
||||||
-h string
|
建立隧道最大次数(与DB服务器断开) (default 999)
|
||||||
DB服务器SSH IP地址
|
-d run in daemon
|
||||||
-l string
|
-f run forever
|
||||||
本地监听端口(或者堡垒机监听端口)
|
-h string
|
||||||
-p string
|
DB服务器SSH IP地址
|
||||||
DB服务器SSH PORT
|
-l string
|
||||||
-r string
|
本地监听端口(或者堡垒机监听端口)
|
||||||
DB服务器端口(如: 3306、1521 ...)
|
-p string
|
||||||
-u string
|
DB服务器SSH PORT
|
||||||
DB服务器SSH USER用户
|
-r string
|
||||||
root@NIUYULING:/mnt/c/Users/niuyuling/Desktop/db_tunnel#
|
DB服务器端口(如: 3306、1521 ...)
|
||||||
|
-u string
|
||||||
|
DB服务器SSH USER用户
|
||||||
|
root@NIUYULING:/mnt/c/Users/niuyuling/Desktop/tunnel#
|
||||||
|
|
||||||
|
|
||||||
root@NIUYULING:/mnt/c/Users/niuyuling/Desktop/db_tunnel/bin# ./db_tunnel -d -f -h 39.104.27.21 -l 3308 -r 3306 -p 22 -u app
|
root@NIUYULING:/mnt/c/Users/niuyuling/Desktop/tunnel/bin# ./tunnel -d -f -c 500 -h 47.108.253.59 -p 22 -u app -r 3306 -l 3306
|
||||||
[*] Daemon running in PID: 12918 PPID: 67
|
[*] Daemon running in PID: 28640 PPID: 24
|
||||||
root@NIUYULING:/mnt/c/Users/niuyuling/Desktop/db_tunnel/bin# [*] Forever running in PID: 12922 PPID: 1
|
root@NIUYULING:/mnt/c/Users/niuyuling/Desktop/tunnel/bin# [*] Forever running in PID: 28645 PPID: 1
|
||||||
[*] Service running in PID: 12927 PPID: 12922
|
[*] Service running in PID: 28649 PPID: 28645
|
||||||
39.104.27.21 22 app 3306 3308
|
数据库连接成功
|
||||||
39.104.27.21 app 22
|
47.108.253.59 22 app hyj4Sxq1UQUJ0RF95MQGf1oNUSdZ/rlIYNXu9DPSCLt3sNsXCpKmRtm85tFCRPN
|
||||||
数据库连接成功
|
0Hv5m&uRwYKFZdu
|
||||||
39.104.27.21 22 app 6+sNDSN4QL7VCLSr+Vt/fNSAX1XsTUwf6fYRTf3pGS8rYBn8Ik
|
2023/03/15 12:15:55.161984 PID: 28649 PPID: 28645 本地端口: 3306 当前连接数:1
|
||||||
I9EKbb
|
2023/03/15 12:15:55.162630 listening for new connections...
|
||||||
设置SSH配置,服务器:39.104.27.21:22; 用户/密码: app/I9EKbbH; 远程:0.0.0.0:3306; 本地:0.0.0.0:3308
|
|
||||||
root@NIUYULING:/mnt/c/Users/niuyuling/Desktop/db_tunnel/bin#
|
root@NIUYULING:/mnt/c/Users/niuyuling/Desktop/tunnel/bin#
|
||||||
|
|||||||
BIN
bin/cmdline
Normal file
BIN
bin/cmdline
Normal file
Binary file not shown.
BIN
bin/db_tunnel
BIN
bin/db_tunnel
Binary file not shown.
36
bin/kill.sh
Normal file
36
bin/kill.sh
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
init()
|
||||||
|
{
|
||||||
|
DB_PORT=$1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
PROCESS=`ps -axww | grep tunnel | grep ${DB_PORT} | grep "\-f" | awk '{print $1}'`
|
||||||
|
if test "${PROCESS}" = ""; then
|
||||||
|
echo "进程不存在!!!"
|
||||||
|
exit -1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "重启参数:" `./cmdline $PROCESS | sed "s|-f|-d -f|g"`
|
||||||
|
|
||||||
|
|
||||||
|
echo "关闭进程: $PROCESS, 关闭端口: ${DB_PORT}"
|
||||||
|
kill `ps -ax | grep tunnel | grep ${DB_PORT} | awk '{print $1}' | xargs `
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if test "$1" = ""; then
|
||||||
|
echo "参数错误!"
|
||||||
|
exit -1
|
||||||
|
fi
|
||||||
|
|
||||||
|
init $1
|
||||||
|
main
|
||||||
|
|
||||||
BIN
bin/tp_decrypt
BIN
bin/tp_decrypt
Binary file not shown.
BIN
bin/tunnel
Normal file
BIN
bin/tunnel
Normal file
Binary file not shown.
16
build.sh
16
build.sh
@@ -1,2 +1,16 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Build Project
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
# 构建主程序
|
||||||
|
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags '-w -s' -o bin/tunnel src/*.go && upx -9 bin/tunnel
|
||||||
|
|
||||||
|
# 处理进程参数
|
||||||
|
gcc -Wall -Os -g cmdline/cmdline.c -o bin/cmdline -static
|
||||||
|
|
||||||
|
# 解密程序
|
||||||
|
gcc -Wall tp_decrypt/tp_decrypt.py.c -o bin/tp_decrypt -static
|
||||||
|
|
||||||
|
|
||||||
CGO_ENABLED=0 go build -ldflags '-w -s' -o bin/db_tunnel src/main.go && upx -9 bin/db_tunnel
|
|
||||||
47
cmdline/cmdline.c
Normal file
47
cmdline/cmdline.c
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define BUFFER 270
|
||||||
|
|
||||||
|
char *cmdline(char *pid)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
char path[BUFFER];
|
||||||
|
char temp[BUFFER * 100];
|
||||||
|
char *p;
|
||||||
|
unsigned char ch;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
memset(path, 0, BUFFER);
|
||||||
|
memset(temp, 0, BUFFER * 100);
|
||||||
|
snprintf(path, BUFFER, "/proc/%s/cmdline", pid);
|
||||||
|
|
||||||
|
if ((fp = fopen(path, "r")) == NULL) {
|
||||||
|
perror("fopen");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!feof(fp)) {
|
||||||
|
ch = fgetc(fp);
|
||||||
|
if (ch == 0) {
|
||||||
|
temp[i] = ' ';
|
||||||
|
} else {
|
||||||
|
temp[i] = ch;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = strrchr(temp, ' ');
|
||||||
|
temp[strlen(temp) - strlen(p)] = '\0';
|
||||||
|
printf("%s\n", temp);
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[], char **envlp)
|
||||||
|
{
|
||||||
|
cmdline(argv[1]);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
10
go.mod
10
go.mod
@@ -1,14 +1,10 @@
|
|||||||
module db_tunnel
|
module tunnel
|
||||||
|
|
||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
require github.com/docker/docker v23.0.0+incompatible
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
gitee.com/dtapps/go-ssh-tunnel v1.0.4 // indirect
|
|
||||||
github.com/CodyGuo/godaemon v0.0.0-20200413142854-c36b39fdd071 // indirect
|
|
||||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||||
golang.org/x/crypto v0.3.0 // indirect
|
golang.org/x/crypto v0.6.0 // indirect
|
||||||
golang.org/x/sys v0.2.0 // indirect
|
golang.org/x/sys v0.5.0 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
35
go.sum
35
go.sum
@@ -1,23 +1,20 @@
|
|||||||
gitee.com/dtapps/go-ssh-tunnel v1.0.4 h1:jQfS6WeoSWmMz+hSBxopUcgb0xi3fssMjHUYpF1BnYY=
|
|
||||||
gitee.com/dtapps/go-ssh-tunnel v1.0.4/go.mod h1:VzlCBrBerVEL1fgikHzApbRU5/Ru+KrzktRjnDfxu1M=
|
|
||||||
github.com/CodyGuo/godaemon v0.0.0-20200413142854-c36b39fdd071 h1:GFI7Rs86D4qip+tBvMcv0ux5kHbngC0rNWfgpTeVoAQ=
|
|
||||||
github.com/CodyGuo/godaemon v0.0.0-20200413142854-c36b39fdd071/go.mod h1:VBC/JvjvRkcgE7wMjDJs7Y94Ta6KSpCWDquUKW+WbJo=
|
|
||||||
github.com/docker/docker v23.0.0+incompatible h1:L6c28tNyqZ4/ub9AZC9d5QUuunoHHfEH4/Ue+h/E5nE=
|
|
||||||
github.com/docker/docker v23.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
|
||||||
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
||||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
|
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c h1:/nJuwDLoL/zrqY6gf57vxC+Pi+pZ8bfhpPkicO5H7W4=
|
||||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||||
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|
||||||
|
|||||||
BIN
src/db_tunnel
Normal file
BIN
src/db_tunnel
Normal file
Binary file not shown.
35
src/endpoint.go
Normal file
35
src/endpoint.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Endpoint struct {
|
||||||
|
Host string
|
||||||
|
Port int
|
||||||
|
User string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEndpoint(s string) *Endpoint {
|
||||||
|
endpoint := &Endpoint{
|
||||||
|
Host: s,
|
||||||
|
}
|
||||||
|
|
||||||
|
if parts := strings.Split(endpoint.Host, "@"); len(parts) > 1 {
|
||||||
|
endpoint.User = parts[0]
|
||||||
|
endpoint.Host = parts[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
if parts := strings.Split(endpoint.Host, ":"); len(parts) > 1 {
|
||||||
|
endpoint.Host = parts[0]
|
||||||
|
endpoint.Port, _ = strconv.Atoi(parts[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
return endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
func (endpoint *Endpoint) String() string {
|
||||||
|
return fmt.Sprintf("%s:%d", endpoint.Host, endpoint.Port)
|
||||||
|
}
|
||||||
617
src/main.go
617
src/main.go
@@ -1,356 +1,261 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io/ioutil"
|
||||||
"io/ioutil"
|
"log"
|
||||||
"log"
|
"net"
|
||||||
"net"
|
"os"
|
||||||
"os"
|
"os/exec"
|
||||||
"os/exec"
|
"path/filepath"
|
||||||
"path/filepath"
|
"strconv"
|
||||||
"strconv"
|
"strings"
|
||||||
"strings"
|
"syscall"
|
||||||
"syscall"
|
"time"
|
||||||
"time"
|
|
||||||
|
_ "github.com/go-sql-driver/mysql"
|
||||||
_ "github.com/go-sql-driver/mysql"
|
"golang.org/x/crypto/ssh"
|
||||||
"golang.org/x/crypto/ssh"
|
"gopkg.in/ini.v1"
|
||||||
"gopkg.in/ini.v1"
|
)
|
||||||
)
|
|
||||||
|
const (
|
||||||
// 转发
|
DAEMON = "d"
|
||||||
func sForward(serverAddr string, remoteAddr string, localConn net.Conn, config *ssh.ClientConfig) {
|
FOREVER = "f"
|
||||||
// 设置sshClientConn
|
HOST_IP = "h"
|
||||||
sshClientConn, err := ssh.Dial("tcp", serverAddr, config)
|
HOST_PORT = "p"
|
||||||
if err != nil {
|
HOST_USER = "u"
|
||||||
fmt.Printf("ssh.Dial failed: %s", err)
|
HOST_REMOTE = "r"
|
||||||
os.Exit(1)
|
HOST_LOCAL = "l"
|
||||||
}
|
)
|
||||||
|
|
||||||
// 设置Connection
|
func StripSlice(slice []string, element string) []string {
|
||||||
sshConn, _ := sshClientConn.Dial("tcp", remoteAddr)
|
for i := 0; i < len(slice); {
|
||||||
/*
|
if slice[i] == element && i != len(slice)-1 {
|
||||||
// 将localConn.Reader复制到sshConn.Writer
|
slice = append(slice[:i], slice[i+1:]...)
|
||||||
go func() {
|
} else if slice[i] == element && i == len(slice)-1 {
|
||||||
_, err = io.Copy(sshConn, localConn)
|
slice = slice[:i]
|
||||||
if err != nil {
|
} else {
|
||||||
fmt.Printf("io.Copy failed: %v", err)
|
i++
|
||||||
os.Exit(1)
|
}
|
||||||
}
|
}
|
||||||
}()
|
return slice
|
||||||
|
}
|
||||||
// 将sshConn.Reader复制到localConn.Writer
|
|
||||||
go func() {
|
func SubProcess(args []string) *exec.Cmd {
|
||||||
_, err = io.Copy(localConn, sshConn)
|
cmd := exec.Command(args[0], args[1:]...)
|
||||||
if err != nil {
|
cmd.Stdin = os.Stdin
|
||||||
fmt.Printf("io.Copy failed: %v", err)
|
cmd.Stdout = os.Stdout
|
||||||
os.Exit(1)
|
cmd.Stderr = os.Stderr
|
||||||
}
|
err := cmd.Start()
|
||||||
}()
|
if err != nil {
|
||||||
*/
|
fmt.Fprintf(os.Stderr, "[-] Error: %s\n", err)
|
||||||
|
}
|
||||||
copyConn := func(writer, reader net.Conn) {
|
return cmd
|
||||||
_, err := io.Copy(writer, reader)
|
}
|
||||||
if err != nil {
|
|
||||||
fmt.Printf(" io.Copy error: %s", err)
|
// Check if a port is available
|
||||||
}
|
func Check(port int) (status bool, err error) {
|
||||||
}
|
|
||||||
|
// Concatenate a colon and the port
|
||||||
go copyConn(localConn, sshConn)
|
host := ":" + strconv.Itoa(port)
|
||||||
go copyConn(sshConn, localConn)
|
|
||||||
|
// Try to create a server with the port
|
||||||
}
|
server, err := net.Listen("tcp", host)
|
||||||
|
|
||||||
func Tunnel(username string, password string, serverAddr string, remoteAddr string, localAddr string, connect_max int) {
|
// if it fails then the port is likely taken
|
||||||
// 设置SSH配置
|
if err != nil {
|
||||||
fmt.Printf("%s,服务器:%s; 用户/密码: %s; 远程:%s; 本地:%s\n", "设置SSH配置", serverAddr, username+"/"+password, remoteAddr, localAddr)
|
return false, err
|
||||||
config := &ssh.ClientConfig{
|
}
|
||||||
User: username,
|
|
||||||
Auth: []ssh.AuthMethod{
|
// close the server
|
||||||
ssh.Password(password),
|
server.Close()
|
||||||
},
|
|
||||||
Timeout: 7 * time.Second,
|
// we successfully used and closed the port
|
||||||
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
// so it's now available to be used again
|
||||||
return nil
|
return true, nil
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置本地监听器
|
func GetCurrentDirectory() string {
|
||||||
localListener, err := net.Listen("tcp", localAddr)
|
dir, err := filepath.Abs(filepath.Dir(os.Args[0])) // 返回绝对路径 filepath.Dir(os.Args[0])去除最后一个元素的路径
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("net.Listen failed: %v\n", err)
|
log.Fatal(err)
|
||||||
os.Exit(1)
|
}
|
||||||
}
|
return strings.Replace(dir, "\\", "/", -1) // 将\替换成/
|
||||||
|
}
|
||||||
var connect int // 连接数
|
|
||||||
connect = 0
|
func PathExists(path string) (bool, error) {
|
||||||
|
_, err := os.Stat(path)
|
||||||
for {
|
if err == nil {
|
||||||
|
return true, nil
|
||||||
connect++
|
}
|
||||||
fmt.Println("当前连接数:", connect)
|
|
||||||
|
if os.IsNotExist(err) {
|
||||||
// 设置本地
|
return false, nil
|
||||||
localConn, err := localListener.Accept()
|
}
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("localListener.Accept failed: %v\n", err)
|
return false, err
|
||||||
os.Exit(1)
|
}
|
||||||
}
|
|
||||||
if connect >= connect_max { // 连接数达到1000时重启进程
|
type user struct {
|
||||||
fmt.Printf("连接数达到上线,重启进程!%d\n", connect)
|
id string
|
||||||
syscall.Kill(os.Getpid(), syscall.SIGKILL)
|
host_id string
|
||||||
}
|
host_ip string
|
||||||
|
router_ip string
|
||||||
sForward(serverAddr, remoteAddr, localConn, config)
|
router_port string
|
||||||
}
|
protocol_type string
|
||||||
}
|
protocol_port string
|
||||||
|
state string
|
||||||
type user struct {
|
acc_auth_id string
|
||||||
id string
|
auth_type string
|
||||||
host_id string
|
username string
|
||||||
host_ip string
|
username_prompt string
|
||||||
router_ip string
|
password_prompt string
|
||||||
router_port string
|
password string
|
||||||
protocol_type string
|
password_decrypt string
|
||||||
protocol_port string
|
pri_key string
|
||||||
state string
|
creator_id string
|
||||||
acc_auth_id string
|
create_time string
|
||||||
auth_type string
|
last_secret string
|
||||||
username string
|
}
|
||||||
username_prompt string
|
|
||||||
password_prompt string
|
func ExecCommand(strCommand string) string {
|
||||||
password string
|
cmd := exec.Command("/bin/bash", "-c", strCommand)
|
||||||
pri_key string
|
|
||||||
creator_id string
|
stdout, _ := cmd.StdoutPipe()
|
||||||
create_time string
|
if err := cmd.Start(); err != nil {
|
||||||
last_secret string
|
fmt.Println("Execute failed when Start: " + err.Error())
|
||||||
}
|
return ""
|
||||||
|
}
|
||||||
func ExecCommand(strCommand string) string {
|
|
||||||
cmd := exec.Command("/bin/bash", "-c", strCommand)
|
out_bytes, _ := ioutil.ReadAll(stdout)
|
||||||
|
stdout.Close()
|
||||||
stdout, _ := cmd.StdoutPipe()
|
|
||||||
if err := cmd.Start(); err != nil {
|
if err := cmd.Wait(); err != nil {
|
||||||
fmt.Println("Execute failed when Start: " + err.Error())
|
fmt.Println("Execute failed when Wait: " + err.Error())
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
out_bytes, _ := ioutil.ReadAll(stdout)
|
return string(out_bytes)
|
||||||
stdout.Close()
|
}
|
||||||
|
|
||||||
if err := cmd.Wait(); err != nil {
|
// 读取mysql数据库内信息
|
||||||
fmt.Println("Execute failed when Wait: " + err.Error())
|
func mysql_show(connect_info string, host_ip string, host_user string, host_port string) user {
|
||||||
return ""
|
|
||||||
}
|
var db *sql.DB
|
||||||
|
db, _ = sql.Open("mysql", connect_info) // 设置连接数据库的参数
|
||||||
return string(out_bytes)
|
defer db.Close() // 关闭数据库
|
||||||
}
|
err := db.Ping() // 连接数据库
|
||||||
|
if err != nil {
|
||||||
func ssh_tunnel(host_ip string, host_port string, host_name string, remote_ip string, local_ip string, mysql_info string, connect_max int) {
|
// 退出
|
||||||
|
log.Printf("数据库连接失败")
|
||||||
fmt.Println(host_ip, host_name, host_port)
|
syscall.Kill(os.Getppid(), syscall.SIGKILL)
|
||||||
|
syscall.Kill(os.Getpid(), syscall.SIGKILL)
|
||||||
// "用户名:密码@[连接方式](主机名:端口号)/数据库名"
|
} else {
|
||||||
var db *sql.DB
|
log.Printf("数据库连接成功")
|
||||||
db, _ = sql.Open("mysql", mysql_info) // 设置连接数据库的参数
|
|
||||||
defer db.Close() // 关闭数据库
|
}
|
||||||
err := db.Ping() // 连接数据库
|
sqlStr := "select host_ip, username, protocol_port, password from tp_acc where host_ip = ? and username = ? and protocol_port = ?"
|
||||||
if err != nil {
|
rows, err := db.Query(sqlStr, host_ip, host_user, host_port)
|
||||||
fmt.Println("数据库连接失败")
|
if err != nil {
|
||||||
// 退出
|
log.Printf("query failed, err:%v\n", err)
|
||||||
syscall.Kill(os.Getppid(), syscall.SIGKILL)
|
}
|
||||||
syscall.Kill(os.Getpid(), syscall.SIGKILL)
|
// 非常重要关闭rows释放持有的数据库链接
|
||||||
} else {
|
defer rows.Close()
|
||||||
fmt.Println("数据库连接成功")
|
var u user
|
||||||
|
// 循环读取结果集中的数据
|
||||||
}
|
for rows.Next() {
|
||||||
|
err := rows.Scan(&u.host_ip, &u.username, &u.protocol_port, &u.password)
|
||||||
sqlStr := "select host_ip, username, protocol_port, password from tp_acc where host_ip = ? and username = ? and protocol_port = ?"
|
if err != nil {
|
||||||
rows, err := db.Query(sqlStr, host_ip, host_name, host_port)
|
log.Printf("scan failed, err:%v\n", err)
|
||||||
if err != nil {
|
}
|
||||||
fmt.Printf("query failed, err:%v\n", err)
|
log.Printf("%s %s %s %s\n", u.host_ip, u.protocol_port, u.username, u.password)
|
||||||
return
|
}
|
||||||
}
|
|
||||||
// 非常重要关闭rows释放持有的数据库链接
|
// 解密密钥
|
||||||
defer rows.Close()
|
command := "./tp_decrypt "
|
||||||
var u user
|
command = command + u.password
|
||||||
// 循环读取结果集中的数据
|
strData := ExecCommand(command)
|
||||||
for rows.Next() {
|
strData = strings.Replace(strData, "\n", "", -1)
|
||||||
err := rows.Scan(&u.host_ip, &u.username, &u.protocol_port, &u.password)
|
fmt.Println(strData)
|
||||||
if err != nil {
|
if strData == "" {
|
||||||
fmt.Printf("scan failed, err:%v\n", err)
|
// 判断密码是否解密成功,不成功退出
|
||||||
return
|
syscall.Kill(os.Getppid(), syscall.SIGKILL)
|
||||||
}
|
syscall.Kill(os.Getpid(), syscall.SIGKILL)
|
||||||
fmt.Printf("%s %s %s %s\n", u.host_ip, u.protocol_port, u.username, u.password)
|
}
|
||||||
}
|
u.password_decrypt = strData
|
||||||
|
|
||||||
// 解密密钥
|
return u
|
||||||
command := "./tp_decrypt "
|
}
|
||||||
command = command + u.password
|
|
||||||
strData := ExecCommand(command)
|
func main() {
|
||||||
strData = strings.Replace(strData, "\n", "", -1)
|
// 参数处理
|
||||||
fmt.Println(strData)
|
daemon := flag.Bool(DAEMON, false, "run in daemon")
|
||||||
if strData == "" {
|
forever := flag.Bool(FOREVER, false, "run forever")
|
||||||
// 判断密码是否解密成功,不成功退出
|
host_ip := flag.String(HOST_IP, "", "DB服务器SSH IP地址")
|
||||||
syscall.Kill(os.Getppid(), syscall.SIGKILL)
|
host_port := flag.String(HOST_PORT, "", "DB服务器SSH PORT")
|
||||||
syscall.Kill(os.Getpid(), syscall.SIGKILL)
|
host_user := flag.String(HOST_USER, "", "DB服务器SSH USER用户")
|
||||||
}
|
remote_port := flag.String(HOST_REMOTE, "", "DB服务器端口(如: 3306、1521 ...)")
|
||||||
|
local_port := flag.String(HOST_LOCAL, "", "本地监听端口(或者堡垒机监听端口)")
|
||||||
Tunnel(host_name, strData, u.host_ip+":"+u.protocol_port, "0.0.0.0:"+remote_ip, "0.0.0.0:"+local_ip, connect_max)
|
connect_max := flag.Int("c", 999, "建立隧道最大次数(与DB服务器断开)")
|
||||||
|
flag.Parse()
|
||||||
}
|
|
||||||
|
// 判断配置文件是否存在
|
||||||
func StripSlice(slice []string, element string) []string {
|
INIFILE := GetCurrentDirectory() + "/" + "tunnel.ini"
|
||||||
for i := 0; i < len(slice); {
|
b, _ := PathExists(INIFILE)
|
||||||
if slice[i] == element && i != len(slice)-1 {
|
if !b {
|
||||||
slice = append(slice[:i], slice[i+1:]...)
|
INIFILE = "/etc/tunnel.ini"
|
||||||
} else if slice[i] == element && i == len(slice)-1 {
|
}
|
||||||
slice = slice[:i]
|
// 读取配置文件
|
||||||
} else {
|
cfg, inierr := ini.Load(INIFILE)
|
||||||
i++
|
if inierr != nil {
|
||||||
}
|
fmt.Printf("Fail to read file: %v", inierr)
|
||||||
}
|
os.Exit(1)
|
||||||
return slice
|
}
|
||||||
}
|
// 读取数据库连接信息
|
||||||
|
MYSQL_INFO := cfg.Section("global").Key("MYSQL_INFO").String()
|
||||||
func SubProcess(args []string) *exec.Cmd {
|
if MYSQL_INFO == "" {
|
||||||
cmd := exec.Command(args[0], args[1:]...)
|
log.Printf("读取Mysql配置出错!\n")
|
||||||
cmd.Stdin = os.Stdin
|
os.Exit(0)
|
||||||
cmd.Stdout = os.Stdout
|
}
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
err := cmd.Start()
|
if *daemon {
|
||||||
if err != nil {
|
SubProcess(StripSlice(os.Args, "-"+DAEMON))
|
||||||
fmt.Fprintf(os.Stderr, "[-] Error: %s\n", err)
|
fmt.Printf("[*] Daemon running in PID: %d PPID: %d\n", os.Getpid(), os.Getppid())
|
||||||
}
|
os.Exit(0)
|
||||||
return cmd
|
} else if *forever {
|
||||||
}
|
for {
|
||||||
|
cmd := SubProcess(StripSlice(os.Args, "-"+FOREVER))
|
||||||
// Check if a port is available
|
fmt.Printf("[*] Forever running in PID: %d PPID: %d\n", os.Getpid(), os.Getppid())
|
||||||
func Check(port int) (status bool, err error) {
|
time.Sleep(time.Second * 5)
|
||||||
|
cmd.Wait()
|
||||||
// Concatenate a colon and the port
|
}
|
||||||
host := ":" + strconv.Itoa(port)
|
//os.Exit(0)
|
||||||
|
} else {
|
||||||
// Try to create a server with the port
|
fmt.Printf("[*] Service running in PID: %d PPID: %d\n", os.Getpid(), os.Getppid())
|
||||||
server, err := net.Listen("tcp", host)
|
}
|
||||||
|
|
||||||
// if it fails then the port is likely taken
|
local_port_, _ := strconv.Atoi(*local_port)
|
||||||
if err != nil {
|
r, _ := Check(local_port_)
|
||||||
return false, err
|
if r {
|
||||||
}
|
u := mysql_show(MYSQL_INFO, *host_ip, *host_user, *host_port)
|
||||||
|
tunnel := NewSSHTunnel(u.username+"@"+u.host_ip+":"+u.protocol_port, ssh.Password(u.password_decrypt), "0.0.0.0:"+*remote_port, *local_port)
|
||||||
// close the server
|
tunnel.Log = log.New(os.Stdout, "", log.Ldate|log.Lmicroseconds)
|
||||||
server.Close()
|
|
||||||
|
tunnel.Start(*connect_max)
|
||||||
// we successfully used and closed the port
|
tunnel.Close()
|
||||||
// so it's now available to be used again
|
time.Sleep(100 * time.Millisecond)
|
||||||
return true, nil
|
} else {
|
||||||
|
log.Printf("端口: %d 不可用!退出!\n", local_port_)
|
||||||
}
|
log.Printf("DoSomething running in PPID: %d\n", os.Getppid())
|
||||||
|
|
||||||
func GetCurrentDirectory() string {
|
syscall.Kill(os.Getppid(), syscall.SIGKILL)
|
||||||
dir, err := filepath.Abs(filepath.Dir(os.Args[0])) // 返回绝对路径 filepath.Dir(os.Args[0])去除最后一个元素的路径
|
time.Sleep(time.Second * 5)
|
||||||
if err != nil {
|
os.Exit(0)
|
||||||
log.Fatal(err)
|
}
|
||||||
}
|
|
||||||
return strings.Replace(dir, "\\", "/", -1) // 将\替换成/
|
time.Sleep(time.Second * 5)
|
||||||
}
|
os.Exit(0)
|
||||||
|
}
|
||||||
func PathExists(path string) (bool, error) {
|
|
||||||
_, err := os.Stat(path)
|
|
||||||
if err == nil {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
DAEMON = "d"
|
|
||||||
FOREVER = "f"
|
|
||||||
HOST_IP = "h"
|
|
||||||
HOST_PORT = "p"
|
|
||||||
HOST_USER = "u"
|
|
||||||
HOST_REMOTE = "r"
|
|
||||||
HOST_LOCAL = "l"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
// 判断配置文件是否存在
|
|
||||||
INIFILE := GetCurrentDirectory() + "/" + "tunnel.ini"
|
|
||||||
b, _ := PathExists(INIFILE)
|
|
||||||
|
|
||||||
if !b {
|
|
||||||
INIFILE = "/etc/tunnel.ini"
|
|
||||||
}
|
|
||||||
|
|
||||||
// 读取配置文件
|
|
||||||
cfg, inierr := ini.Load(INIFILE)
|
|
||||||
if inierr != nil {
|
|
||||||
fmt.Printf("Fail to read file: %v", inierr)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
// 读取数据库连接信息
|
|
||||||
MYSQL_INFO := cfg.Section("global").Key("MYSQL_INFO").String()
|
|
||||||
if MYSQL_INFO == "" {
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
daemon := flag.Bool(DAEMON, false, "run in daemon")
|
|
||||||
forever := flag.Bool(FOREVER, false, "run forever")
|
|
||||||
host_ip := flag.String(HOST_IP, "", "DB服务器SSH IP地址")
|
|
||||||
host_port := flag.String(HOST_PORT, "", "DB服务器SSH PORT")
|
|
||||||
host_user := flag.String(HOST_USER, "", "DB服务器SSH USER用户")
|
|
||||||
remote_port := flag.String(HOST_REMOTE, "", "DB服务器端口(如: 3306、1521 ...)")
|
|
||||||
local_port := flag.String(HOST_LOCAL, "", "本地监听端口(或者堡垒机监听端口)")
|
|
||||||
connect_max := flag.Int("c", 999, "建立隧道最大次数(与DB服务器断开)")
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if *daemon {
|
|
||||||
SubProcess(StripSlice(os.Args, "-"+DAEMON))
|
|
||||||
fmt.Printf("[*] Daemon running in PID: %d PPID: %d\n", os.Getpid(), os.Getppid())
|
|
||||||
os.Exit(0)
|
|
||||||
} else if *forever {
|
|
||||||
for {
|
|
||||||
cmd := SubProcess(StripSlice(os.Args, "-"+FOREVER))
|
|
||||||
fmt.Printf("[*] Forever running in PID: %d PPID: %d\n", os.Getpid(), os.Getppid())
|
|
||||||
time.Sleep(time.Second * 5)
|
|
||||||
cmd.Wait()
|
|
||||||
}
|
|
||||||
//os.Exit(0)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("[*] Service running in PID: %d PPID: %d\n", os.Getpid(), os.Getppid())
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(*host_ip, *host_port, *host_user, *remote_port, *local_port)
|
|
||||||
|
|
||||||
//
|
|
||||||
local_port_, _ := strconv.Atoi(*local_port)
|
|
||||||
r, _ := Check(local_port_)
|
|
||||||
//fmt.Println(r)
|
|
||||||
if r {
|
|
||||||
fmt.Println(*connect_max)
|
|
||||||
ssh_tunnel(*host_ip, *host_port, *host_user, *remote_port, *local_port, MYSQL_INFO, *connect_max)
|
|
||||||
} else {
|
|
||||||
fmt.Println(local_port_, "端口不可用!", r, "退出!")
|
|
||||||
fp, _ := os.OpenFile("./pid.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
|
|
||||||
log.SetOutput(fp)
|
|
||||||
|
|
||||||
log.Printf("DoSomething running in PPID: %d\n", os.Getppid())
|
|
||||||
|
|
||||||
syscall.Kill(os.Getppid(), syscall.SIGKILL)
|
|
||||||
time.Sleep(time.Second * 10)
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(time.Second * 5)
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|||||||
21
src/private_key_file.go
Normal file
21
src/private_key_file.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PrivateKeyFile(file string) ssh.AuthMethod {
|
||||||
|
buffer, err := ioutil.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := ssh.ParsePrivateKey(buffer)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return ssh.PublicKeys(key)
|
||||||
|
}
|
||||||
16
src/ssh_agent.go
Normal file
16
src/ssh_agent.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
"golang.org/x/crypto/ssh/agent"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SSHAgent() ssh.AuthMethod {
|
||||||
|
if sshAgent, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {
|
||||||
|
return ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
194
src/ssh_tunnel.go
Normal file
194
src/ssh_tunnel.go
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
type logger interface {
|
||||||
|
Printf(string, ...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type SSHTunnel struct {
|
||||||
|
Local *Endpoint
|
||||||
|
Server *Endpoint
|
||||||
|
Remote *Endpoint
|
||||||
|
Config *ssh.ClientConfig
|
||||||
|
Log logger
|
||||||
|
Conns []net.Conn
|
||||||
|
SvrConns []*ssh.Client
|
||||||
|
MaxConnectionAttempts int
|
||||||
|
isOpen bool
|
||||||
|
close chan interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tunnel *SSHTunnel) logf(fmt string, args ...interface{}) {
|
||||||
|
if tunnel.Log != nil {
|
||||||
|
tunnel.Log.Printf(fmt, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConnectionWaiter(listener net.Listener, c chan net.Conn) {
|
||||||
|
conn, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c <- conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tunnel *SSHTunnel) Close() {
|
||||||
|
tunnel.close <- struct{}{}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tunnel *SSHTunnel) Start(connect_max int) error {
|
||||||
|
listener, err := net.Listen("tcp", tunnel.Local.String())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tunnel.isOpen = true
|
||||||
|
tunnel.Local.Port = listener.Addr().(*net.TCPAddr).Port
|
||||||
|
|
||||||
|
// Ensure that MaxConnectionAttempts is at least 1. This check is done here
|
||||||
|
// since the library user can set the value at any point before Start() is called,
|
||||||
|
// and this check protects against the case where the programmer set MaxConnectionAttempts
|
||||||
|
// to 0 for some reason.
|
||||||
|
if tunnel.MaxConnectionAttempts <= 0 {
|
||||||
|
tunnel.MaxConnectionAttempts = 9
|
||||||
|
}
|
||||||
|
|
||||||
|
var connect = 0 // 连接数
|
||||||
|
|
||||||
|
for {
|
||||||
|
|
||||||
|
connect++
|
||||||
|
tunnel.logf("PID: %d PPID: %d 本地端口: %d 当前连接数:%d\n", os.Getpid(), os.Getppid(), tunnel.Local.Port, connect)
|
||||||
|
|
||||||
|
if connect >= connect_max { // 连接数达到1000时重启进程
|
||||||
|
tunnel.logf("连接数达到上线,重启进程!%d\n", connect)
|
||||||
|
syscall.Kill(os.Getpid(), syscall.SIGKILL)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tunnel.isOpen {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
c := make(chan net.Conn)
|
||||||
|
go newConnectionWaiter(listener, c)
|
||||||
|
tunnel.logf("正在侦听新连接 ...")
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-tunnel.close:
|
||||||
|
tunnel.logf("收到关闭信号,关闭 ...")
|
||||||
|
tunnel.isOpen = false
|
||||||
|
case conn := <-c:
|
||||||
|
tunnel.Conns = append(tunnel.Conns, conn)
|
||||||
|
tunnel.logf("accepted connection")
|
||||||
|
|
||||||
|
go tunnel.forward(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var total int
|
||||||
|
total = len(tunnel.Conns)
|
||||||
|
for i, conn := range tunnel.Conns {
|
||||||
|
tunnel.logf("closing the netConn (%d of %d)", i+1, total)
|
||||||
|
err := conn.Close()
|
||||||
|
if err != nil {
|
||||||
|
tunnel.logf(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
total = len(tunnel.SvrConns)
|
||||||
|
for i, conn := range tunnel.SvrConns {
|
||||||
|
tunnel.logf("closing the serverConn (%d of %d)", i+1, total)
|
||||||
|
err := conn.Close()
|
||||||
|
if err != nil {
|
||||||
|
tunnel.logf(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = listener.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tunnel.logf("tunnel closed")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tunnel *SSHTunnel) forward(localConn net.Conn) {
|
||||||
|
var (
|
||||||
|
serverConn *ssh.Client
|
||||||
|
err error
|
||||||
|
attemptsLeft int = tunnel.MaxConnectionAttempts
|
||||||
|
)
|
||||||
|
|
||||||
|
for {
|
||||||
|
serverConn, err = ssh.Dial("tcp", tunnel.Server.String(), tunnel.Config)
|
||||||
|
if err != nil {
|
||||||
|
attemptsLeft--
|
||||||
|
|
||||||
|
if attemptsLeft <= 0 {
|
||||||
|
tunnel.logf("服务器拨号错误: %v: exceeded %d attempts", err, tunnel.MaxConnectionAttempts)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tunnel.logf("connected to %s (1 of 2)\n", tunnel.Server.String())
|
||||||
|
tunnel.SvrConns = append(tunnel.SvrConns, serverConn)
|
||||||
|
|
||||||
|
remoteConn, err := serverConn.Dial("tcp", tunnel.Remote.String())
|
||||||
|
if err != nil {
|
||||||
|
tunnel.logf("remote dial error: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tunnel.Conns = append(tunnel.Conns, remoteConn)
|
||||||
|
tunnel.logf("connected to %s (2 of 2)\n", tunnel.Remote.String())
|
||||||
|
|
||||||
|
copyConn := func(writer, reader net.Conn) {
|
||||||
|
_, err := io.Copy(writer, reader)
|
||||||
|
if err != nil {
|
||||||
|
tunnel.logf("io.Copy error: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
go copyConn(localConn, remoteConn)
|
||||||
|
go copyConn(remoteConn, localConn)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSSHTunnel creates a new single-use tunnel. Supplying "0" for localport will use a random port.
|
||||||
|
func NewSSHTunnel(tunnel string, auth ssh.AuthMethod, destination string, localport string) *SSHTunnel {
|
||||||
|
|
||||||
|
localEndpoint := NewEndpoint("0.0.0.0:" + localport)
|
||||||
|
|
||||||
|
server := NewEndpoint(tunnel)
|
||||||
|
if server.Port == 0 {
|
||||||
|
server.Port = 22
|
||||||
|
}
|
||||||
|
|
||||||
|
sshTunnel := &SSHTunnel{
|
||||||
|
Config: &ssh.ClientConfig{
|
||||||
|
User: server.User,
|
||||||
|
Auth: []ssh.AuthMethod{auth},
|
||||||
|
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||||||
|
// Always accept key.
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Local: localEndpoint,
|
||||||
|
Server: server,
|
||||||
|
Remote: NewEndpoint(destination),
|
||||||
|
close: make(chan interface{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
return sshTunnel
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user