Merge remote-tracking branch 'origin/main' into lichang
This commit is contained in:
@@ -135,13 +135,14 @@ omc:
|
||||
# TLS Skip verify: true/false
|
||||
# email/sms
|
||||
# smProxy: sms(Short Message Service)/smsc(SMS Centre)
|
||||
# dataCoding: 0:UCS2, 1:ASCII, 2:LATIN1
|
||||
alarm:
|
||||
alarmEmailForward:
|
||||
enable: true
|
||||
emailList:
|
||||
smtp: mail.agrandtech.com
|
||||
smtp: mail.smtp.com
|
||||
port: 25
|
||||
user: smtpext@agrandtech.com
|
||||
user: smtpext@smtp.com
|
||||
password: "1000smtp@omc!"
|
||||
tlsSkipVerify: true
|
||||
alarmSMSForward:
|
||||
@@ -151,6 +152,8 @@ alarm:
|
||||
systemID: "omc"
|
||||
password: "omc123"
|
||||
systemType: "UTRAN"
|
||||
dataCoding: 0
|
||||
serviceNumber: "OMC"
|
||||
sms:
|
||||
apiURL: http://smsc.xxx.com/
|
||||
accessKeyID: xxxx
|
||||
|
||||
@@ -98,3 +98,17 @@ omc:
|
||||
filter: ""
|
||||
display: "System Type"
|
||||
comment: ""
|
||||
- name: "dataCoding"
|
||||
type: "enum"
|
||||
value: "ASCII"
|
||||
access: "rw"
|
||||
filter: '{"0":"UCS2","1":"ASCII","2":"LATIN1"}'
|
||||
display: "Data Coding"
|
||||
comment: "Short message coding type"
|
||||
- name: "serviceNumber"
|
||||
type: "string"
|
||||
value: "OMC"
|
||||
access: "rw"
|
||||
filter: "3~20"
|
||||
display: "Service Number"
|
||||
comment: "It is the source address and length is between 3 and 20"
|
||||
@@ -90,37 +90,24 @@ func AlarmForwardBySMS(alarmData *Alarm) (string, error) {
|
||||
case http.StatusOK, http.StatusAccepted, http.StatusNoContent, http.StatusCreated:
|
||||
return userList, nil
|
||||
default:
|
||||
err := fmt.Errorf("Failed to send SMS: %s(Code=%d)", resp.Status, resp.StatusCode)
|
||||
log.Error(err)
|
||||
log.Error(fmt.Errorf("failed to send SMS: %s(Code=%d)", resp.Status, resp.StatusCode))
|
||||
return userList, err
|
||||
}
|
||||
}
|
||||
|
||||
var smsForward = &(config.GetYamlConfig().Alarm.SMSCForward)
|
||||
|
||||
func AlarmForwardBySMPP(alarmData *Alarm) (string, error) {
|
||||
log.Info("AlarmForwardBySMPP processing... ")
|
||||
|
||||
// toUsers, err := dborm.XormGetAlarmForward("SMS")
|
||||
// if err != nil {
|
||||
// log.Error("Failed to XormGetAlarmForward:", err)
|
||||
// return "", err
|
||||
// } else if toUsers == nil {
|
||||
// err := errors.New("not found forward phone number")
|
||||
// log.Error(err)
|
||||
// return "", err
|
||||
// }
|
||||
// userList := strings.Join(*toUsers, ",")
|
||||
|
||||
userList := config.GetYamlConfig().Alarm.SMSCForward.MobileList
|
||||
userList := smsForward.MobileList
|
||||
auth := gosmpp.Auth{
|
||||
SMSC: config.GetYamlConfig().Alarm.SMSCForward.SMSCAddr,
|
||||
SystemID: config.GetYamlConfig().Alarm.SMSCForward.SystemID,
|
||||
Password: config.GetYamlConfig().Alarm.SMSCForward.Password,
|
||||
SystemType: config.GetYamlConfig().Alarm.SMSCForward.SystemType,
|
||||
SMSC: smsForward.SMSCAddr,
|
||||
SystemID: smsForward.SystemID,
|
||||
Password: smsForward.Password,
|
||||
SystemType: smsForward.SystemType,
|
||||
}
|
||||
|
||||
// conn, err := gosmpp.NonTLSDialer(auth.SMSC)
|
||||
// connection := gosmpp.NewConnection(conn)
|
||||
|
||||
trans, err := gosmpp.NewSession(
|
||||
gosmpp.TXConnector(gosmpp.NonTLSDialer, auth),
|
||||
gosmpp.Settings{
|
||||
@@ -150,17 +137,22 @@ func AlarmForwardBySMPP(alarmData *Alarm) (string, error) {
|
||||
_ = trans.Close()
|
||||
}()
|
||||
|
||||
// sending SMS(s)
|
||||
// var results []string
|
||||
// for _, toUser := range *toUsers {
|
||||
message := "Alarm Notification: " + alarmData.AlarmTitle + " from " + alarmData.NeType + " " + alarmData.NeId + " at " + alarmData.EventTime
|
||||
if err = trans.Transceiver().Submit(newSubmitSM(userList, message)); err != nil {
|
||||
// result := fmt.Sprintf("Failed to submit %s hort message:%s", toUser, err.Error())
|
||||
// results = append(results, result)
|
||||
log.Error("Failed to submit hort message:", err)
|
||||
return userList, err
|
||||
message := "Alarm Notification: " + alarmData.AlarmTitle + " from " + alarmData.NeType + "_" + alarmData.NeId + " at " + alarmData.EventTime
|
||||
for _, user := range strings.Split(userList, ",") {
|
||||
sm, err := newSubmitSM(user, message)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to newSubmitSM %s short message: %v", user, err)
|
||||
writeLog(alarmData, user, "SMS", err)
|
||||
continue
|
||||
}
|
||||
if err = trans.Transceiver().Submit(sm); err != nil {
|
||||
log.Errorf("Failed to Submit %s short message: %v", user, err)
|
||||
writeLog(alarmData, user, "SMS", err)
|
||||
continue
|
||||
}
|
||||
writeLog(alarmData, user, "SMS", nil)
|
||||
}
|
||||
// }
|
||||
|
||||
return userList, nil
|
||||
}
|
||||
|
||||
@@ -191,61 +183,42 @@ func writeLog(alarmData *Alarm, toUser, forwardBy string, err error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func handlePDU() func(pdu.PDU) (pdu.PDU, bool) {
|
||||
return func(p pdu.PDU) (pdu.PDU, bool) {
|
||||
switch pd := p.(type) {
|
||||
case *pdu.Unbind:
|
||||
log.Trace("Unbind Received")
|
||||
return pd.GetResponse(), true
|
||||
|
||||
case *pdu.UnbindResp:
|
||||
log.Trace("UnbindResp Received")
|
||||
|
||||
case *pdu.SubmitSMResp:
|
||||
log.Trace("SubmitSMResp Received")
|
||||
|
||||
case *pdu.GenericNack:
|
||||
log.Trace("GenericNack Received")
|
||||
|
||||
case *pdu.EnquireLinkResp:
|
||||
fmt.Println("EnquireLinkResp Received")
|
||||
|
||||
case *pdu.EnquireLink:
|
||||
log.Trace("EnquireLink Received")
|
||||
return pd.GetResponse(), false
|
||||
|
||||
case *pdu.DataSM:
|
||||
log.Trace("DataSM receiver")
|
||||
return pd.GetResponse(), false
|
||||
|
||||
case *pdu.DeliverSM:
|
||||
log.Trace("DeliverSM receiver")
|
||||
return pd.GetResponse(), false
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
func newSubmitSM(phoneNumber string, message string) *pdu.SubmitSM {
|
||||
func newSubmitSM(phoneNumber string, message string) (*pdu.SubmitSM, error) {
|
||||
// build up submitSM
|
||||
srcAddr := pdu.NewAddress()
|
||||
srcAddr.SetTon(5)
|
||||
srcAddr.SetNpi(0)
|
||||
_ = srcAddr.SetAddress("alarm notification:")
|
||||
|
||||
err := srcAddr.SetAddress(smsForward.ServiceNumber)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
destAddr := pdu.NewAddress()
|
||||
destAddr.SetTon(1)
|
||||
destAddr.SetNpi(1)
|
||||
_ = destAddr.SetAddress(phoneNumber)
|
||||
|
||||
err = destAddr.SetAddress(phoneNumber)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
submitSM := pdu.NewSubmitSM().(*pdu.SubmitSM)
|
||||
submitSM.SourceAddr = srcAddr
|
||||
submitSM.DestAddr = destAddr
|
||||
_ = submitSM.Message.SetMessageWithEncoding(message, data.UCS2)
|
||||
dataCoding := data.UCS2
|
||||
switch smsForward.DataCoding {
|
||||
case config.CODING_UCS2:
|
||||
dataCoding = data.UCS2
|
||||
case config.CODING_ASCII:
|
||||
dataCoding = data.ASCII
|
||||
case config.CODING_LATIN1:
|
||||
dataCoding = data.LATIN1
|
||||
}
|
||||
err = submitSM.Message.SetMessageWithEncoding(message, dataCoding)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
submitSM.ProtocolID = 0
|
||||
submitSM.RegisteredDelivery = 1
|
||||
submitSM.ReplaceIfPresentFlag = 0
|
||||
submitSM.EsmClass = 0
|
||||
|
||||
return submitSM
|
||||
return submitSM, nil
|
||||
}
|
||||
|
||||
@@ -150,6 +150,16 @@ type DbConfig struct {
|
||||
Backup string `yaml:"backup"`
|
||||
}
|
||||
|
||||
//type codingType int
|
||||
|
||||
const (
|
||||
// Short message data coding type
|
||||
CODING_UCS2 int = iota
|
||||
CODING_ASCII
|
||||
CODING_LATIN1
|
||||
CODING_NODEF
|
||||
)
|
||||
|
||||
type AlarmConfig struct {
|
||||
SplitEventAlarm bool `yaml:"splitEventAlarm"`
|
||||
//ForwardAlarm bool `yaml:"forwardAlarm"`
|
||||
@@ -164,12 +174,14 @@ type AlarmConfig struct {
|
||||
TLSSkipVerify bool `yaml:"tlsSkipVerify" json:"tlsSkipVerify"`
|
||||
} `yaml:"alarmEmailForward"`
|
||||
SMSCForward struct {
|
||||
Enable bool `yaml:"enable" json:"enable"`
|
||||
MobileList string `yaml:"mobileList" json:"mobileList"`
|
||||
SMSCAddr string `yaml:"smscAddr" json:"smscAddr"`
|
||||
SystemID string `yaml:"systemID" json:"systemID"`
|
||||
Password string `yaml:"password" json:"password"`
|
||||
SystemType string `yaml:"systemType" json:"systemType"`
|
||||
Enable bool `yaml:"enable" json:"enable"`
|
||||
MobileList string `yaml:"mobileList" json:"mobileList"`
|
||||
SMSCAddr string `yaml:"smscAddr" json:"smscAddr"`
|
||||
SystemID string `yaml:"systemID" json:"systemID"`
|
||||
Password string `yaml:"password" json:"password"`
|
||||
SystemType string `yaml:"systemType" json:"systemType"`
|
||||
DataCoding int `yaml:"dataCoding" json:"dataCoding"`
|
||||
ServiceNumber string `yaml:"serviceNumber" json:"serviceNumber"`
|
||||
} `yaml:"alarmSMSForward"`
|
||||
SMS struct {
|
||||
ApiURL string `yaml:"apiURL"`
|
||||
@@ -304,7 +316,19 @@ func WriteOrignalConfig(configFile string, paramName string, paramData map[strin
|
||||
for j := i + 1; j < len(lines); j++ {
|
||||
if strings.Contains(lines[j], k+":") {
|
||||
index := strings.Index(lines[j], k)
|
||||
lines[j] = lines[j][:index] + fmt.Sprintf("%s: %v", k, v)
|
||||
// Determine the type of v
|
||||
switch v := v.(type) {
|
||||
case string:
|
||||
lines[j] = lines[j][:index] + fmt.Sprintf("%s: \"%s\"", k, v)
|
||||
// case int:
|
||||
// lines[j] = lines[j][:index] + fmt.Sprintf("%s: %d", k, v)
|
||||
// case float64:
|
||||
// lines[j] = lines[j][:index] + fmt.Sprintf("%s: %f", k, v)
|
||||
case bool:
|
||||
lines[j] = lines[j][:index] + fmt.Sprintf("%s: %t", k, v)
|
||||
default:
|
||||
lines[j] = lines[j][:index] + fmt.Sprintf("%s: %v", k, v)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,13 +132,14 @@ omc:
|
||||
# TLS Skip verify: true/false
|
||||
# email/sms
|
||||
# smProxy: sms(Short Message Service)/smsc(SMS Centre)
|
||||
# dataCoding: 0:UCS2, 1:ASCII, 2:LATIN1
|
||||
alarm:
|
||||
alarmEmailForward:
|
||||
enable: true
|
||||
emailList:
|
||||
smtp: mail.agrandtech.com
|
||||
smtp: mail.smtp.com
|
||||
port: 25
|
||||
user: smtpext@agrandtech.com
|
||||
user: smtpext@smtp.com
|
||||
password: "1000smtp@omc!"
|
||||
tlsSkipVerify: true
|
||||
alarmSMSForward:
|
||||
@@ -148,6 +149,8 @@ alarm:
|
||||
systemID: "omc"
|
||||
password: "omc123"
|
||||
systemType: "UTRAN"
|
||||
dataCoding: 0
|
||||
serviceNumber: "OMC"
|
||||
sms:
|
||||
apiURL: http://smsc.xxx.com/
|
||||
accessKeyID: xxxx
|
||||
|
||||
@@ -29,7 +29,7 @@ const (
|
||||
// 配置文件路径
|
||||
configParamDir = "../../../config/param"
|
||||
// configParamFile = "*" // 目录下全部更新
|
||||
configParamFile = "smsc_param_config.yaml" // 单文件更新
|
||||
configParamFile = "omc_param_config.yaml" // 单文件更新
|
||||
)
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"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"
|
||||
@@ -110,22 +111,34 @@ func main() {
|
||||
log.Fatal("Failed to Listen: ", err)
|
||||
os.Exit(4)
|
||||
}
|
||||
fmt.Printf("MML SSH server startup, listen port:%d\n", conf.Sshd.ListenPort)
|
||||
//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)
|
||||
//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)
|
||||
//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)
|
||||
// }
|
||||
|
||||
go startTelnetServer(telnetUri)
|
||||
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,
|
||||
@@ -183,7 +196,7 @@ func handleAuth(authType, userName, password string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func startTelnetServer(addr string) {
|
||||
func StartTelnetServer(addr string) {
|
||||
listener, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
fmt.Println("Error starting Telnet server:", err)
|
||||
@@ -202,7 +215,7 @@ func startTelnetServer(addr string) {
|
||||
telnetMu.Lock()
|
||||
if telnetCC >= int(conf.TelnetServer.MaxConnNum) {
|
||||
telnetMu.Unlock()
|
||||
io.WriteString(conn, "Connection limit reached. Try again later.\n")
|
||||
io.WriteString(conn, "Connection limit reached. Try again later.\r\n")
|
||||
conn.Close()
|
||||
continue
|
||||
}
|
||||
@@ -225,7 +238,7 @@ func handleTelnetConnection(conn net.Conn) {
|
||||
writer := bufio.NewWriter(conn)
|
||||
|
||||
// 发送欢迎信息
|
||||
writer.WriteString("Welcome to the Telnet server!\n")
|
||||
writer.WriteString("Welcome to the Telnet server!\r\n")
|
||||
writer.Flush()
|
||||
|
||||
// 请求用户名
|
||||
@@ -266,41 +279,67 @@ func handleTelnetConnection(conn net.Conn) {
|
||||
writer.Flush()
|
||||
|
||||
if handleAuth(conf.TelnetServer.AuthType, user, pass) {
|
||||
writer.WriteString("\nAuthentication successful!\n")
|
||||
writer.WriteString("\r\nAuthentication successful!\r\n")
|
||||
writer.Flush()
|
||||
handleCommands(user, conf.TelnetServer.TagNE, reader, writer)
|
||||
HandleCommands(user, conf.TelnetServer.TagNE, reader, writer)
|
||||
} else {
|
||||
writer.WriteString("\nAuthentication failed!\n")
|
||||
writer.WriteString("\r\nAuthentication failed!\r\n")
|
||||
writer.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
// 处理命令输入
|
||||
func handleCommands(user, tag string, reader *bufio.Reader, writer *bufio.Writer) {
|
||||
// 处理命令输
|
||||
func HandleCommands(user, tag string, reader *bufio.Reader, writer *bufio.Writer) {
|
||||
header := fmt.Sprintf("[%s@%s]> ", user, tag)
|
||||
clearLine := "\033[2K\r" // ANSI 转义序列,用于清除当前行
|
||||
for {
|
||||
command, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return
|
||||
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(command)
|
||||
command := strings.TrimSpace(commandBuilder.String())
|
||||
|
||||
// 处理其他命令
|
||||
switch command {
|
||||
case "hello":
|
||||
writer.WriteString("Hello, world!\n")
|
||||
writer.WriteString("\r\nHello, world!\r\n")
|
||||
case "time":
|
||||
writer.WriteString(fmt.Sprintf("Current time: %s\n", time.Now().Format(time.RFC1123)))
|
||||
writer.WriteString(fmt.Sprintf("\r\nCurrent time: %s\r\n", time.Now().Format(time.RFC1123)))
|
||||
case "exit", "quit":
|
||||
writer.WriteString("Goodbye!\n")
|
||||
writer.WriteString("\r\nGoodbye!\r\n")
|
||||
writer.Flush()
|
||||
return
|
||||
case "":
|
||||
case "\n":
|
||||
case "\xff\xfe\x01":
|
||||
default:
|
||||
writer.WriteString("Unknown command\n")
|
||||
writer.WriteString("\r\nUnknown command\r\n")
|
||||
writer.Flush()
|
||||
}
|
||||
writer.WriteString(header)
|
||||
writer.WriteString(clearLine + header)
|
||||
writer.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
195
sshsvc/telnet/telnet.go
Normal file
195
sshsvc/telnet/telnet.go
Normal file
@@ -0,0 +1,195 @@
|
||||
package omctelnet
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"be.ems/lib/dborm"
|
||||
)
|
||||
|
||||
type TelnetHandler struct {
|
||||
ListenAddr string
|
||||
ListenPort uint16
|
||||
UserName string
|
||||
Password string
|
||||
AuthType string
|
||||
MaxConnNum uint8
|
||||
TagNE string
|
||||
ListenHost string
|
||||
|
||||
connCount int
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (t *TelnetHandler) handleTelnetAuth(authType, userName, password string) bool {
|
||||
switch authType {
|
||||
case "local":
|
||||
if userName == t.UserName && password == t.Password {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
case "radius", "omc":
|
||||
exist, err := dborm.XEngDB().Table("OMC_PUB.sysUser").Where("userName=? AND password=md5(?)", userName, password).Exist()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return exist
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *TelnetHandler) StartTelnetServer() {
|
||||
listener, err := net.Listen("tcp", t.ListenHost)
|
||||
if err != nil {
|
||||
fmt.Println("Error starting Telnet server:", err)
|
||||
return
|
||||
}
|
||||
defer listener.Close()
|
||||
fmt.Println("Telnet server started on", t.ListenHost)
|
||||
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
fmt.Println("Error accepting Telnet connection:", err)
|
||||
continue
|
||||
}
|
||||
|
||||
t.mu.Lock()
|
||||
if t.connCount >= int(t.MaxConnNum) {
|
||||
t.mu.Unlock()
|
||||
io.WriteString(conn, "Connection limit reached. Try again later.\r\n")
|
||||
conn.Close()
|
||||
continue
|
||||
}
|
||||
t.connCount++
|
||||
t.mu.Unlock()
|
||||
|
||||
go t.handleTelnetConnection(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TelnetHandler) handleTelnetConnection(conn net.Conn) {
|
||||
defer func() {
|
||||
t.mu.Lock()
|
||||
t.connCount--
|
||||
t.mu.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 t.handleTelnetAuth(t.AuthType, user, pass) {
|
||||
writer.WriteString("\r\nAuthentication successful!\r\n")
|
||||
writer.Flush()
|
||||
t.HandleCommands(user, t.TagNE, reader, writer)
|
||||
} else {
|
||||
writer.WriteString("\r\nAuthentication failed!\r\n")
|
||||
writer.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
// 处理命令输
|
||||
func (t *TelnetHandler) 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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user