You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
175 lines
3.8 KiB
175 lines
3.8 KiB
package tunnel |
|
|
|
import ( |
|
"context" |
|
"fmt" |
|
"github.com/go-redis/redis/v8" |
|
"golang.org/x/crypto/ssh" |
|
"gorm.io/driver/mysql" |
|
"gorm.io/gorm" |
|
"io" |
|
"log" |
|
"net" |
|
"sync/atomic" |
|
"time" |
|
) |
|
|
|
/* |
|
notice: 利用隧道代理实现本地连接服务器程序 |
|
*/ |
|
|
|
var ( |
|
// ssh配置 |
|
sshPem = `` // todo 私钥填充 |
|
sshHost = "47.106.150.32" |
|
sshPort = 22 |
|
sshUser = "root" |
|
sshPass = "cH6YIEPCCrFsTNJ4" |
|
|
|
// mysql配置 |
|
mysqlHost = "127.0.0.1" |
|
mysqlPort = 3306 |
|
mysqlUser = "root" |
|
mysqlPass = "pFlu4oNgTKhMdttQ" |
|
mysqlDB = "c_game" |
|
|
|
// redis配置 |
|
redisHost = "127.0.0.1" |
|
redisPort = 6379 |
|
redisPass = "cYUTZma6wEzrdvDJ" |
|
redisDB = 1 |
|
|
|
// 本地转发端口 |
|
LocalPort int32 = 1000 // 从1000开始尝试20次 |
|
) |
|
|
|
// 转发数据 |
|
func forward(local, remote net.Conn) { |
|
defer local.Close() |
|
defer remote.Close() |
|
|
|
done := make(chan struct{}) |
|
go func() { |
|
_, _ = io.Copy(local, remote) |
|
done <- struct{}{} |
|
}() |
|
go func() { |
|
_, _ = io.Copy(remote, local) |
|
done <- struct{}{} |
|
}() |
|
<-done |
|
} |
|
|
|
func getListener() (net.Listener, int32) { |
|
var ( |
|
listener net.Listener |
|
err error |
|
port int32 |
|
tryCount int32 = 100 |
|
) |
|
for index := int32(0); index < tryCount; index++ { |
|
port = atomic.AddInt32(&LocalPort, 1) |
|
address := fmt.Sprintf("localhost:%d", port) |
|
listener, err = net.Listen("tcp", address) |
|
if err == nil && listener != nil { |
|
break |
|
} |
|
log.Printf("[WARN]: listen %s, listener[%+v] err[%+v]", address, listener, err) |
|
} |
|
if listener == nil && err != nil { |
|
log.Fatalf("listener err, tryCount[%d] ", tryCount) |
|
} |
|
return listener, port |
|
} |
|
|
|
func getSshConfig() (cfg *ssh.ClientConfig) { |
|
cfg = &ssh.ClientConfig{ |
|
User: sshUser, |
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(), |
|
Timeout: 10 * time.Second, |
|
} |
|
if sshPem != "" { |
|
signer, err := ssh.ParsePrivateKey([]byte(sshPem)) |
|
if err != nil { |
|
log.Fatalf("Failed to parse private key: %s", err.Error()) |
|
} |
|
cfg.Auth = append(cfg.Auth, ssh.PublicKeys(signer)) |
|
} |
|
if sshPass != "" { |
|
cfg.Auth = append(cfg.Auth, ssh.Password(sshPass)) |
|
} |
|
return cfg |
|
} |
|
|
|
func binding(listener net.Listener, sshClient *ssh.Client, processHost string, processPort int) { |
|
for { |
|
localConn, err := listener.Accept() |
|
if err != nil { |
|
log.Printf("Failed to accept connection: %v", err) |
|
return |
|
} |
|
|
|
remoteConn, err := sshClient.Dial("tcp", fmt.Sprintf("%s:%d", processHost, processPort)) |
|
if err != nil { |
|
log.Printf("Failed to dial remote Redis: %v", err) |
|
localConn.Close() |
|
continue |
|
} |
|
|
|
go forward(localConn, remoteConn) |
|
} |
|
} |
|
|
|
func getSshClient() *ssh.Client { |
|
sshConfig := getSshConfig() |
|
sshClient, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", sshHost, sshPort), sshConfig) |
|
if err != nil { |
|
log.Fatalf("Failed to dial SSH: %v", err) |
|
} |
|
return sshClient |
|
} |
|
|
|
func GetMysql() *gorm.DB { |
|
// 连接服务器 |
|
sshClient := getSshClient() |
|
// 获取本地监听 |
|
listener, localPort := getListener() |
|
// 转发进程数据 |
|
go binding(listener, sshClient, mysqlHost, mysqlPort) |
|
// 连接本地端口 |
|
dsn := fmt.Sprintf("%s:%s@tcp(127.0.0.1:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", mysqlUser, mysqlPass, localPort, mysqlDB) |
|
// 打开数据库连接 |
|
db, err := gorm.Open(mysql.New(mysql.Config{ |
|
DSN: dsn, |
|
}), &gorm.Config{}) |
|
|
|
if err != nil { |
|
log.Fatalf("create db err, %s", err.Error()) |
|
} |
|
|
|
return db |
|
} |
|
|
|
func GetRedis() (*redis.Client, int32) { |
|
// 连接服务器 |
|
sshClient := getSshClient() |
|
// 建立本地监听 |
|
listener, localPort := getListener() |
|
// 转发进程数据 |
|
go binding(listener, sshClient, redisHost, redisPort) |
|
// 连接本地端口 |
|
rdb := redis.NewClient(&redis.Options{ |
|
Addr: fmt.Sprintf("127.0.0.1:%d", localPort), |
|
Password: redisPass, |
|
DB: redisDB, |
|
}) |
|
|
|
ctx := context.Background() |
|
v, err := rdb.Ping(ctx).Result() |
|
_ = v |
|
if err != nil { |
|
log.Fatalf("Failed to connect to Redis: %v", err) |
|
} |
|
|
|
return rdb, localPort |
|
}
|
|
|