'首次提交'

This commit is contained in:
2023-04-12 16:39:10 +08:00
parent 0c3a01b1ec
commit cbc416b73b
6 changed files with 240 additions and 0 deletions

14
config.json Normal file
View File

@@ -0,0 +1,14 @@
[
{
"addr": "192.168.10.201:22",
"user": "root",
"pass": "root",
"tunnels": [
{
"remote": "127.0.0.1:6379",
"local": "127.0.0.1:6379"
}
]
}
]

10
go.mod Normal file
View File

@@ -0,0 +1,10 @@
module prot
go 1.17
require golang.org/x/crypto v0.0.0-20220214200702-86341886e292
require (
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect
)

11
go.sum Normal file
View File

@@ -0,0 +1,11 @@
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

44
main.go Normal file
View File

@@ -0,0 +1,44 @@
package main
import (
"encoding/json"
"io/ioutil"
"log"
"os"
"os/signal"
"prot/sshtunnel"
"syscall"
)
func main() {
var sts []*sshtunnel.Config
p := "config.json"
if len(os.Args) == 2 {
p = os.Args[1]
}
f, err := ioutil.ReadFile(p)
if err != nil {
log.Printf("载入配置文件出错, 错误: %v", err)
os.Exit(-1)
}
err = json.Unmarshal(f, &sts)
if nil != err {
log.Printf("解析配置文件内容出错, 错误: %v", err)
os.Exit(-1)
}
var tunnels []*sshtunnel.SSHTunnel
for _, st := range sts {
tunnel := sshtunnel.NewSSHTunnel(st)
tunnel.Start()
tunnels = append(tunnels, tunnel)
}
signalChan := make(chan os.Signal)
signal.Notify(signalChan, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
<-signalChan
for _, t := range tunnels {
t.Close()
}
os.Exit(0)
}

13
sshtunnel/config.go Normal file
View File

@@ -0,0 +1,13 @@
package sshtunnel
type Tunnel struct {
Remote string `json:"remote"`
Local string `json:"local"`
}
type Config struct {
Addr string `json:"addr"`
User string `json:"user"`
Pass string `json:"pass,omitempty"`
Tunnels []Tunnel `json:"tunnels,omitempty"`
}

148
sshtunnel/ssh.go Normal file
View File

@@ -0,0 +1,148 @@
package sshtunnel
import (
"fmt"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/terminal"
"io"
"log"
"math"
"net"
"strings"
"syscall"
"time"
)
type SSHTunnel struct {
config *Config
client *ssh.Client
}
func NewSSHTunnel(config *Config) *SSHTunnel {
st := new(SSHTunnel)
st.config = config
return st
}
func (st *SSHTunnel) Start() {
if len(st.config.Pass) == 0 {
st.setPass()
}
st.initSSHClient()
for _, t := range st.config.Tunnels {
go st.connect(t)
}
}
func (st *SSHTunnel) Close() {
if nil != st.client {
st.client.Close()
}
}
func (st *SSHTunnel) GetSSHClient() (*ssh.Client, error) {
if st.client != nil {
return st.client, nil
}
var auth []ssh.AuthMethod
auth = make([]ssh.AuthMethod, 0)
auth = append(auth, ssh.Password(st.config.Pass))
sc := &ssh.ClientConfig{
User: st.config.User,
Auth: auth,
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
}
var err error
st.client, err = ssh.Dial("tcp", st.config.Addr, sc)
if err != nil {
return nil, err
}
log.Printf("连接到服务器成功: %s", st.config.Addr)
return st.client, err
}
func (st *SSHTunnel) connect(t Tunnel) {
tid := fmt.Sprintf("%s-%s", t.Local, t.Remote)
ll, err := net.Listen("tcp", t.Local)
if err != nil {
log.Printf("隧道[%s]接收开启失败, 错误: %v", tid, err)
return
}
defer func() {
ll.Close()
log.Printf("隧道[%s]接收关闭!", tid)
}()
log.Printf("隧道[%s]接收开启!", tid)
sno := int64(0)
for {
lc, err := ll.Accept()
if err != nil {
log.Printf("隧道[%s]接收连接失败, 错误: %v", tid, err)
return
}
sc, err := st.GetSSHClient()
if err != nil {
log.Printf("隧道[%s]接入服务失败, 错误: %v", tid, err)
lc.Close()
continue
}
rc, err := sc.Dial("tcp", t.Remote)
if err != nil {
log.Printf("隧道[%s]接入获取连接失败, 错误: %v", tid, err)
sc.Close()
lc.Close()
continue
}
if sno >= math.MaxInt64 {
sno = 0
}
sno += 1
cid := fmt.Sprintf("%s:%d", tid, sno)
go st.transfer(cid, lc, rc)
}
}
func (st *SSHTunnel) setPass() {
fmt.Printf("请输入登陆密码[%s@%s]:", st.config.User, st.config.Addr)
bytePassword, _ := terminal.ReadPassword(int(syscall.Stdin))
st.config.Pass = string(bytePassword)
fmt.Println()
}
func (st *SSHTunnel) initSSHClient() {
var err error
for {
st.client, err = st.GetSSHClient()
if nil != err {
error := err.Error()
log.Printf("连接到服务器[%s]失败, 错误: %s", st.config.Addr, error)
if strings.Contains(error, "unable to authenticate") {
st.config.Pass = ""
st.setPass()
continue
}
if strings.Contains(error, "i/o timeout") {
log.Printf("连接到服务器[%s]超时!", st.config.Addr)
time.Sleep(2 * time.Second)
continue
}
}
return
}
}
func (st *SSHTunnel) transfer(cid string, lc net.Conn, rc net.Conn) {
defer rc.Close()
defer lc.Close()
go func() {
defer lc.Close()
defer rc.Close()
io.Copy(rc, lc)
}()
log.Printf("通道[%s]已连接!", cid)
io.Copy(lc, rc)
log.Printf("通道[%s]已断开!", cid)
}