diff --git a/config/etc/default/restconf.yaml b/config/etc/default/restconf.yaml index d9df8e6e..39004b71 100644 --- a/config/etc/default/restconf.yaml +++ b/config/etc/default/restconf.yaml @@ -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 diff --git a/config/param/omc_param_config.yaml b/config/param/omc_param_config.yaml index 7b3ad248..b2c96186 100644 --- a/config/param/omc_param_config.yaml +++ b/config/param/omc_param_config.yaml @@ -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" \ No newline at end of file diff --git a/features/fm/smsforward.go b/features/fm/smsforward.go index e5d25bde..01f85d33 100644 --- a/features/fm/smsforward.go +++ b/features/fm/smsforward.go @@ -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 } diff --git a/restagent/config/config.go b/restagent/config/config.go index 682e99b5..aad08afd 100644 --- a/restagent/config/config.go +++ b/restagent/config/config.go @@ -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 } } diff --git a/restagent/etc/restconf.yaml b/restagent/etc/restconf.yaml index 92f80b5c..d7196fca 100644 --- a/restagent/etc/restconf.yaml +++ b/restagent/etc/restconf.yaml @@ -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 diff --git a/src/modules/network_element/ne_config_test.go b/src/modules/network_element/ne_config_test.go index 33ad2a9d..92636a6c 100644 --- a/src/modules/network_element/ne_config_test.go +++ b/src/modules/network_element/ne_config_test.go @@ -29,7 +29,7 @@ const ( // 配置文件路径 configParamDir = "../../../config/param" // configParamFile = "*" // 目录下全部更新 - configParamFile = "smsc_param_config.yaml" // 单文件更新 + configParamFile = "omc_param_config.yaml" // 单文件更新 ) func TestConfig(t *testing.T) { diff --git a/sshsvc/sshsvc.go b/sshsvc/sshsvc.go index 6952f893..5964e062 100644 --- a/sshsvc/sshsvc.go +++ b/sshsvc/sshsvc.go @@ -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() } } diff --git a/sshsvc/telnet/telnet.go b/sshsvc/telnet/telnet.go new file mode 100644 index 00000000..28622b44 --- /dev/null +++ b/sshsvc/telnet/telnet.go @@ -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() + } +}