marge: 合并11.2版本
This commit is contained in:
318
sshsvc/sshsvc.go
318
sshsvc/sshsvc.go
@@ -1,13 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"be.ems/lib/dborm"
|
||||
"be.ems/lib/global"
|
||||
@@ -15,15 +18,23 @@ import (
|
||||
"be.ems/lib/mmlp"
|
||||
"be.ems/sshsvc/config"
|
||||
"be.ems/sshsvc/logmml"
|
||||
"be.ems/sshsvc/snmp"
|
||||
omctelnet "be.ems/sshsvc/telnet"
|
||||
|
||||
//"github.com/gliderlabs/ssh"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
var connNum int = 0
|
||||
var conf *config.YamlConfig
|
||||
|
||||
var (
|
||||
telnetCC int
|
||||
sshCC int
|
||||
telnetMu sync.Mutex
|
||||
sshMu sync.Mutex
|
||||
)
|
||||
|
||||
func init() {
|
||||
conf = config.GetYamlConfig()
|
||||
log.InitLogger(conf.Logger.File, conf.Logger.Duration, conf.Logger.Count, "omc:sshsvc", config.GetLogLevel())
|
||||
@@ -57,33 +68,32 @@ func main() {
|
||||
serverConfig := &ssh.ServerConfig{
|
||||
PasswordCallback: func(conn ssh.ConnMetadata, password []byte) (*ssh.Permissions, error) {
|
||||
// 这里可以进行密码验证逻辑,例如检查用户名和密码是否匹配
|
||||
validUser, _, err := dborm.XormCheckLoginUser(conn.User(), string(password), conf.OMC.UserCrypt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if validUser == true {
|
||||
sessionToken := fmt.Sprintf("%x", conn.SessionID()) // Generate new token to session ID
|
||||
sourceAddr := conn.RemoteAddr().String()
|
||||
timeOut := uint32(conf.Sshd.Timeout)
|
||||
sessionMode := conf.Sshd.Session
|
||||
log.Debugf("token:%s sourceAddr:%s", sessionToken, sourceAddr)
|
||||
affected, err := dborm.XormInsertSession(conn.User(), sourceAddr, sessionToken, timeOut, sessionMode)
|
||||
if err != nil {
|
||||
log.Error("Failed to insert Session table:", err)
|
||||
return nil, err
|
||||
}
|
||||
if affected == -1 {
|
||||
err := errors.New("Failed to get session")
|
||||
log.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
// validUser, _, err := dborm.XormCheckLoginUser(conn.User(), string(password), conf.OMC.UserCrypt)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// if validUser == true {
|
||||
// sessionToken := fmt.Sprintf("%x", conn.SessionID()) // Generate new token to session ID
|
||||
// sourceAddr := conn.RemoteAddr().String()
|
||||
// timeOut := uint32(conf.Sshd.Timeout)
|
||||
// sessionMode := conf.Sshd.Session
|
||||
// log.Debugf("token:%s sourceAddr:%s", sessionToken, sourceAddr)
|
||||
// affected, err := dborm.XormInsertSession(conn.User(), sourceAddr, sessionToken, timeOut, sessionMode)
|
||||
// if err != nil {
|
||||
// log.Error("Failed to insert Session table:", err)
|
||||
// return nil, err
|
||||
// }
|
||||
// if affected == -1 {
|
||||
// err := errors.New("Failed to get session")
|
||||
// log.Error(err)
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// if conn.User() == "admin" && string(password) == "123456" {
|
||||
// return nil, nil
|
||||
// }
|
||||
if handleAuth(conf.Sshd.AuthType, conn.User(), string(password)) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("invalid user or password")
|
||||
},
|
||||
PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
|
||||
@@ -101,8 +111,62 @@ func main() {
|
||||
log.Fatal("Failed to Listen: ", err)
|
||||
os.Exit(4)
|
||||
}
|
||||
//fmt.Printf("MML SSH server startup, listen port:%d\n", conf.Sshd.ListenPort)
|
||||
// 启动telnet服务器
|
||||
//telnetUri := fmt.Sprintf("%s:%d", conf.TelnetServer.ListenAddr, conf.TelnetServer.ListenPort)
|
||||
// telnetListener, err := net.Listen("tcp", telnetUri)
|
||||
// if err != nil {
|
||||
// log.Fatal("Failed to Listen: ", err)
|
||||
// os.Exit(4)
|
||||
// }
|
||||
//fmt.Printf("MML Telnet server startup, listen port:%d\n", conf.TelnetServer.ListenPort)
|
||||
// telnetconn, err := telnetListener.Accept()
|
||||
// if err != nil {
|
||||
// log.Fatal("Failed to accept telnet connection: ", err)
|
||||
// os.Exit(6)
|
||||
// }
|
||||
|
||||
fmt.Printf("MML SSH server startup, listen port:%d\n", conf.Sshd.ListenPort)
|
||||
telnetSvc := omctelnet.TelnetHandler{
|
||||
ListenAddr: conf.TelnetServer.ListenAddr,
|
||||
ListenPort: conf.TelnetServer.ListenPort,
|
||||
UserName: conf.TelnetServer.UserName,
|
||||
Password: conf.TelnetServer.Password,
|
||||
AuthType: conf.TelnetServer.AuthType,
|
||||
MaxConnNum: conf.TelnetServer.MaxConnNum,
|
||||
TagNE: conf.TelnetServer.TagNE,
|
||||
ListenHost: conf.TelnetServer.ListenAddr + ":" + strconv.Itoa(int(conf.TelnetServer.ListenPort)),
|
||||
}
|
||||
go telnetSvc.StartTelnetServer()
|
||||
// go StartTelnetServer(telnetSvc.ListenHost)
|
||||
|
||||
snmpSvc := snmp.SNMPService{
|
||||
ListenAddr: conf.SNMPServer.ListenAddr,
|
||||
ListenPort: conf.SNMPServer.ListenPort,
|
||||
UserName: conf.SNMPServer.UserName,
|
||||
AuthPass: conf.SNMPServer.AuthPass,
|
||||
AuthProto: conf.SNMPServer.AuthProto,
|
||||
PrivPass: conf.SNMPServer.PrivPass,
|
||||
PrivProto: conf.SNMPServer.PrivProto,
|
||||
EngineID: conf.SNMPServer.EngineID,
|
||||
TrapPort: conf.SNMPServer.TrapPort,
|
||||
TrapListen: conf.SNMPServer.TrapListen,
|
||||
TrapBool: conf.SNMPServer.TrapBool,
|
||||
TrapTick: conf.SNMPServer.TrapTick,
|
||||
TimeOut: conf.SNMPServer.TimeOut,
|
||||
TrapTarget: conf.SNMPServer.TrapTarget,
|
||||
|
||||
ListenHost: conf.SNMPServer.ListenAddr + ":" + strconv.Itoa(int(conf.SNMPServer.ListenPort)),
|
||||
TrapHost: conf.SNMPServer.ListenAddr + ":" + strconv.Itoa(int(conf.SNMPServer.TrapPort)),
|
||||
SysName: "HLR-0",
|
||||
SysStatus: "Normal",
|
||||
SysDescr: "HLR server(sysNO=0)",
|
||||
SysLocation: "Shanghai",
|
||||
SysContact: "",
|
||||
SysService: 0,
|
||||
}
|
||||
|
||||
go snmpSvc.StartSNMPServer()
|
||||
go snmpSvc.StartTrapServer()
|
||||
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
@@ -115,6 +179,175 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func handleAuth(authType, userName, password string) bool {
|
||||
switch authType {
|
||||
case "local":
|
||||
if userName == conf.Sshd.UserName && password == conf.Sshd.Password {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
case "radius":
|
||||
exist, err := dborm.XEngDB().Table("OMC_PUB.sysUser").Where("userName=? AND password=md5(?)", userName, password).Exist()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return exist
|
||||
case "omc":
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func StartTelnetServer(addr string) {
|
||||
listener, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
fmt.Println("Error starting Telnet server:", err)
|
||||
return
|
||||
}
|
||||
defer listener.Close()
|
||||
fmt.Println("Telnet server started on", addr)
|
||||
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
fmt.Println("Error accepting Telnet connection:", err)
|
||||
continue
|
||||
}
|
||||
|
||||
telnetMu.Lock()
|
||||
if telnetCC >= int(conf.TelnetServer.MaxConnNum) {
|
||||
telnetMu.Unlock()
|
||||
io.WriteString(conn, "Connection limit reached. Try again later.\r\n")
|
||||
conn.Close()
|
||||
continue
|
||||
}
|
||||
telnetCC++
|
||||
telnetMu.Unlock()
|
||||
|
||||
go handleTelnetConnection(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func handleTelnetConnection(conn net.Conn) {
|
||||
defer func() {
|
||||
telnetMu.Lock()
|
||||
telnetCC--
|
||||
telnetMu.Unlock()
|
||||
}()
|
||||
defer conn.Close()
|
||||
|
||||
reader := bufio.NewReader(conn)
|
||||
writer := bufio.NewWriter(conn)
|
||||
|
||||
// 发送欢迎信息
|
||||
writer.WriteString("Welcome to the Telnet server!\r\n")
|
||||
writer.Flush()
|
||||
|
||||
// 请求用户名
|
||||
writer.WriteString("Username: ")
|
||||
writer.Flush()
|
||||
user, _ := reader.ReadString('\n')
|
||||
user = strings.TrimSpace(user)
|
||||
|
||||
// 关闭回显模式
|
||||
writer.Write([]byte{255, 251, 1}) // IAC WILL ECHO
|
||||
writer.Flush()
|
||||
|
||||
// 请求密码
|
||||
writer.WriteString("Password: ")
|
||||
writer.Flush()
|
||||
|
||||
// 读取密码并清除控制序列
|
||||
var passBuilder strings.Builder
|
||||
for {
|
||||
b, err := reader.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if b == '\n' || b == '\r' {
|
||||
break
|
||||
}
|
||||
if b == 255 { // IAC
|
||||
reader.ReadByte() // 忽略下一个字节
|
||||
reader.ReadByte() // 忽略下一个字节
|
||||
} else {
|
||||
passBuilder.WriteByte(b)
|
||||
}
|
||||
}
|
||||
pass := passBuilder.String()
|
||||
|
||||
// 恢复回显模式
|
||||
writer.Write([]byte{255, 252, 1}) // IAC WONT ECHO
|
||||
writer.Flush()
|
||||
|
||||
if handleAuth(conf.TelnetServer.AuthType, user, pass) {
|
||||
writer.WriteString("\r\nAuthentication successful!\r\n")
|
||||
writer.Flush()
|
||||
HandleCommands(user, conf.TelnetServer.TagNE, reader, writer)
|
||||
} else {
|
||||
writer.WriteString("\r\nAuthentication failed!\r\n")
|
||||
writer.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
// 处理命令输
|
||||
func HandleCommands(user, tag string, reader *bufio.Reader, writer *bufio.Writer) {
|
||||
header := fmt.Sprintf("[%s@%s]> ", user, tag)
|
||||
clearLine := "\033[2K\r" // ANSI 转义序列,用于清除当前行
|
||||
for {
|
||||
var commandBuilder strings.Builder
|
||||
for {
|
||||
b, err := reader.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if b == '\n' || b == '\r' {
|
||||
break
|
||||
}
|
||||
if b == '\xff' || b == '\xfe' || b == '\x01' {
|
||||
continue
|
||||
}
|
||||
if b == 127 { // 处理退格键
|
||||
if commandBuilder.Len() > 0 {
|
||||
// 手动截断字符串
|
||||
command := commandBuilder.String()
|
||||
command = command[:len(command)-1]
|
||||
commandBuilder.Reset()
|
||||
commandBuilder.WriteString(command)
|
||||
writer.WriteString("\b \b") // 回显退格
|
||||
writer.Flush()
|
||||
}
|
||||
} else {
|
||||
// 回显用户输入的字符
|
||||
writer.WriteByte(b)
|
||||
writer.Flush()
|
||||
commandBuilder.WriteByte(b)
|
||||
}
|
||||
}
|
||||
command := strings.TrimSpace(commandBuilder.String())
|
||||
|
||||
// 处理其他命令
|
||||
switch command {
|
||||
case "hello":
|
||||
writer.WriteString("\r\nHello, world!\r\n")
|
||||
case "time":
|
||||
writer.WriteString(fmt.Sprintf("\r\nCurrent time: %s\r\n", time.Now().Format(time.RFC1123)))
|
||||
case "exit", "quit":
|
||||
writer.WriteString("\r\nGoodbye!\r\n")
|
||||
writer.Flush()
|
||||
return
|
||||
case "":
|
||||
default:
|
||||
writer.WriteString("\r\nUnknown command\r\n")
|
||||
writer.Flush()
|
||||
}
|
||||
writer.WriteString(clearLine + header)
|
||||
writer.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
func handleSSHConnection(conn net.Conn, serverConfig *ssh.ServerConfig) {
|
||||
// SSH握手
|
||||
sshConn, chans, reqs, err := ssh.NewServerConn(conn, serverConfig)
|
||||
@@ -141,13 +374,16 @@ func handleSSHConnection(conn net.Conn, serverConfig *ssh.ServerConfig) {
|
||||
continue
|
||||
}
|
||||
|
||||
connNum++
|
||||
if connNum > int(conf.Sshd.MaxConnNum) {
|
||||
sshMu.Lock()
|
||||
sshCC++
|
||||
if sshCC > int(conf.Sshd.MaxConnNum) {
|
||||
sshMu.Unlock()
|
||||
log.Error("Maximum number of connections exceeded")
|
||||
//conn.Write([]byte("Reach max connections"))
|
||||
channel.Write([]byte(fmt.Sprintf("Connection limit reached (limit=%d). Try again later.\r\n", conf.Sshd.MaxConnNum)))
|
||||
conn.Close()
|
||||
continue
|
||||
}
|
||||
sshMu.Unlock()
|
||||
|
||||
go handleSSHChannel(conn, sshConn, channel, requests)
|
||||
}
|
||||
@@ -196,8 +432,10 @@ func handleSSHChannel(conn net.Conn, sshConn *ssh.ServerConn, channel ssh.Channe
|
||||
}
|
||||
|
||||
func closeConnection(conn net.Conn) {
|
||||
sshMu.Lock()
|
||||
conn.Close()
|
||||
connNum--
|
||||
sshCC--
|
||||
sshMu.Unlock()
|
||||
}
|
||||
|
||||
func handleSSHShell(sshConn *ssh.ServerConn, channel ssh.Channel) {
|
||||
@@ -205,17 +443,22 @@ func handleSSHShell(sshConn *ssh.ServerConn, channel ssh.Channel) {
|
||||
// 检查通道是否支持终端
|
||||
|
||||
omcMmlVar := &mmlp.MmlVar{
|
||||
Version: "16.1.1",
|
||||
Version: global.Version,
|
||||
Output: mmlp.DefaultFormatType,
|
||||
MmlHome: conf.Sshd.MmlHome,
|
||||
Limit: 50,
|
||||
Limit: conf.Sshd.MaxConnNum,
|
||||
User: sshConn.User(),
|
||||
SessionToken: fmt.Sprintf("%x", sshConn.SessionID()),
|
||||
HttpUri: conf.OMC.HttpUri,
|
||||
UserAgent: config.GetDefaultUserAgent(),
|
||||
TagNE: conf.Sshd.TagNE,
|
||||
}
|
||||
term := term.NewTerminal(channel, fmt.Sprintf("[%s@%s]> ", omcMmlVar.User, omcMmlVar.TagNE))
|
||||
msg := fmt.Sprintf("\r\nWelcome to the %s server!\r\n", strings.ToUpper(omcMmlVar.TagNE))
|
||||
term.Write([]byte(msg))
|
||||
msg = fmt.Sprintf("Last login: %s from %s \r\n\r\n", time.Now().Format(time.RFC1123), sshConn.RemoteAddr())
|
||||
term.Write([]byte(msg))
|
||||
|
||||
term := term.NewTerminal(channel, fmt.Sprintf("[%s@omc]> ", omcMmlVar.User))
|
||||
// 启动交互式shell会话
|
||||
for {
|
||||
line, err := term.ReadLine()
|
||||
@@ -233,6 +476,13 @@ func handleSSHShell(sshConn *ssh.ServerConn, channel ssh.Channel) {
|
||||
}
|
||||
var response string
|
||||
switch cmdline {
|
||||
case "hello":
|
||||
term.Write([]byte("Hello, world!\r\n"))
|
||||
goto continueLoop
|
||||
case "time":
|
||||
response = fmt.Sprintf("Current time: %s\r\n", time.Now().Format(time.RFC1123))
|
||||
term.Write([]byte(response))
|
||||
goto continueLoop
|
||||
case "exit", "quit":
|
||||
goto exitLoop
|
||||
case "":
|
||||
|
||||
Reference in New Issue
Block a user