From 64732cb9e983cc2171b4731df1042948f2b2d450 Mon Sep 17 00:00:00 2001 From: MQjehovah <1421706030@qq.com> Date: Tue, 9 Jan 2024 21:00:48 +0800 Subject: [PATCH] init --- client/client.code-workspace | 11 ++ client/go.mod | 10 ++ client/go.sum | 8 ++ client/main.go | 272 +++++++++++++++++++++++++++++++++++ server/broker.go | 159 ++++++++++++++++++++ server/client.go | 265 ++++++++++++++++++++++++++++++++++ server/go.mod | 23 +++ server/go.sum | 34 +++++ server/main.go | 82 +++++++++++ server/utils.go | 41 ++++++ 10 files changed, 905 insertions(+) create mode 100644 client/client.code-workspace create mode 100644 client/go.mod create mode 100644 client/go.sum create mode 100644 client/main.go create mode 100644 server/broker.go create mode 100644 server/client.go create mode 100644 server/go.mod create mode 100644 server/go.sum create mode 100644 server/main.go create mode 100644 server/utils.go diff --git a/client/client.code-workspace b/client/client.code-workspace new file mode 100644 index 0000000..986af1d --- /dev/null +++ b/client/client.code-workspace @@ -0,0 +1,11 @@ +{ + "folders": [ + { + "path": "." + }, + { + "path": "../server" + } + ], + "settings": {} +} \ No newline at end of file diff --git a/client/go.mod b/client/go.mod new file mode 100644 index 0000000..5a950fa --- /dev/null +++ b/client/go.mod @@ -0,0 +1,10 @@ +module main + +go 1.21.5 + +require ( + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/urfave/cli/v2 v2.27.1 // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect +) diff --git a/client/go.sum b/client/go.sum new file mode 100644 index 0000000..31517c0 --- /dev/null +++ b/client/go.sum @@ -0,0 +1,8 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= +github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= diff --git a/client/main.go b/client/main.go new file mode 100644 index 0000000..a8215b8 --- /dev/null +++ b/client/main.go @@ -0,0 +1,272 @@ +package main + +import ( + "bufio" + "encoding/binary" + "fmt" + "io" + "net" + "os" + "strings" + "time" + + "github.com/rs/zerolog/log" + "github.com/urfave/cli/v2" +) + +type Config struct { + AddrServer string +} + +const HEART_BEAT_INTERVAL = time.Second * 5 // 心跳超时时间 + +const ( + MSG_TYPE_UNKOWNM = iota + MSG_TYPE_HEARTBEAT + MSG_TYPE_REGISTER + MSG_TYPE_SESSION_CREATE + MSG_TYPE_SESSION_DATA + MSG_TYPE_SESSION_DESTORY + MSG_TYPE_TUNNEL_CREATE + MSG_TYPE_MAX +) + +type Device struct { + id string + category string + desc string /* description of the device */ + conn net.Conn + create_time int64 /* connection time */ + active time.Time + uptime uint32 + token string + registered bool + closed uint32 + send chan []byte // Buffered channel of outbound messages. +} + +func main() { + app := &cli.App{ + Name: "XZRobot Ops Server", + Usage: "The Server Side For xzrobot ops", + Version: "1.0.0", + Commands: []*cli.Command{ + { + Name: "run", + Usage: "Run Server", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "log", + Value: "log.txt", + Usage: "log file path", + }, + &cli.StringFlag{ + Name: "conf", + Aliases: []string{"c"}, + Value: "./rttys.conf", + Usage: "config file to load", + }, + &cli.StringFlag{ + Name: "addr-dev", + Value: ":9011", + Usage: "address to listen device", + }, + &cli.StringFlag{ + Name: "addr-user", + Value: ":9012", + Usage: "address to listen user", + }, + &cli.StringFlag{ + Name: "db", + Value: "sqlite://database.db", + Usage: "database source", + }, + }, + Action: func(c *cli.Context) error { + runClient(c) + return nil + }, + }, + }, + Action: func(c *cli.Context) error { + c.App.Command("run").Run(c) + return nil + }, + } + + err := app.Run(os.Args) + if err != nil { + fmt.Println(err) + os.Exit(1) + } +} + +func runClient(c *cli.Context) { + + cfg := &Config{ + AddrServer: "localhost:9011", + } + + tcpConn, err := createTcpConn(cfg.AddrServer) + if err != nil { + fmt.Println("TCP Connect Error! " + err.Error()) + return + } + fmt.Println(tcpConn.LocalAddr().String() + " : Client Connected") + + reader := bufio.NewReader(tcpConn) + for { + s, err := reader.ReadString('\n') + if err != nil || err == io.EOF { + break + } else { + //接收到new的指令的时候,新建一个tcp连接 + if s == "new\n" { + + } + if s == "hi" { + //忽略掉hi的请求 + } + } + } +} + +func readLoop(conn *net.TCPConn) { + defer conn.Close() + + reader := bufio.NewReader(conn) + + for { + b, err := reader.Peek(5) + if err != nil { + if err != io.EOF && !strings.Contains(err.Error(), "use of closed network connection") { + log.Error().Msg(err.Error()) + } + return + } + + reader.Discard(5) + + msg_type := b[0] + + if msg_type >= MSG_TYPE_MAX { + log.Error().Msgf("invalid msg type: %d", msg_type) + return + } + + msg_length := binary.BigEndian.Uint32(b[1:]) + data := make([]byte, msg_length) + _, err = io.ReadFull(reader, data) + if err != nil { + log.Error().Msg(err.Error()) + return + } + + // dev.active = time.Now() + + switch msg_type { + case MSG_TYPE_HEARTBEAT: + parseHeartbeat(b) + + default: + log.Error().Msgf("invalid msg type: %d", msg_type) + } + } +} + +func parseHeartbeat(b []byte) { + uptime := binary.BigEndian.Uint32(b[:4]) + log.Info().Msgf("Heartbeat Time: %d", uptime) +} + +func writeLoop(conn *net.TCPConn) { + ticker := time.NewTicker(time.Second) + + defer conn.Close() + + ninactive := 0 + lastHeartbeat := time.Now() + + for { + select { + case msg, ok := <-dev.send: + if !ok { + return + } + + _, err := dev.conn.Write(msg) + if err != nil { + log.Error().Msg(err.Error()) + return + } + + case <-ticker.C: + now := time.Now() + if now.Sub(dev.active) > HEART_BEAT_INTERVAL*3/2 { + if dev.id == "" { + return + } + + log.Error().Msgf("Inactive device in long time: %s", dev.id) + if ninactive > 3 { + log.Error().Msgf("Inactive 3 times, now kill it: %s", dev.id) + return + } + ninactive = ninactive + 1 + } + + if now.Sub(lastHeartbeat) > HEART_BEAT_INTERVAL-1 { + lastHeartbeat = now + if len(dev.send) < 1 { + dev.WriteMsg(MSG_TYPE_HEARTBEAT, []byte{}) + } + } + } + } +} + +func createTcpConn(addr string) (*net.TCPConn, error) { + tcpConn, err := net.ResolveTCPAddr("tcp", addr) + if err != nil { + fmt.Println("TCP Address error ! " + err.Error()) + return nil, err + } + conn, err := net.DialTCP("tcp", nil, tcpConn) + if err != nil { + fmt.Println("TCP Connect error ! " + err.Error()) + return nil, err + } + return conn, nil +} + +func createTunnel(localAddr string, remoteAddr string) { + localConn, err := createTcpConn(localAddr) + if err != nil { + fmt.Println("Create LocalConn Error! " + err.Error()) + return + } + remoteConn, err := createTcpConn(remoteAddr) + if err != nil { + fmt.Println("Create remoteAddr Error! " + err.Error()) + return + } + + defer localConn.Close() + defer remoteConn.Close() + + go func() { + _, err := io.Copy(localConn, remoteConn) + if err != nil { + fmt.Println("Copy error! " + err.Error()) + return + } + }() + + go func() { + _, err := io.Copy(remoteConn, localConn) + if err != nil { + fmt.Println("Copy error! " + err.Error()) + return + } + }() +} diff --git a/server/broker.go b/server/broker.go new file mode 100644 index 0000000..dabdc29 --- /dev/null +++ b/server/broker.go @@ -0,0 +1,159 @@ +package main + +import ( + "sync/atomic" + "time" + + "github.com/gorilla/websocket" + "github.com/rs/zerolog/log" +) + +type Session struct { + dev Client + user Client + confirmed uint32 +} + +type Broker struct { + cfg *Config + devices map[string]Client + sessions map[string]*Session + register chan Client + unregister chan Client + deviceMessageChan chan deviceMessage + // userMessageChan chan userMessage +} + +func newBroker(cfg *Config) *Broker { + return &Broker{ + cfg: cfg, + devices: make(map[string]Client), + sessions: make(map[string]*Session), + register: make(chan Client, 1000), + unregister: make(chan Client, 1000), + deviceMessageChan: make(chan deviceMessage, 1000), + // userMessageChan: make(chan userMessage, 1000), + } +} + +func (br *Broker) run() { + for { + select { + case c := <-br.register: + devid := c.DeviceID() + + if c.DeviceCategory() == "device" { + dev := c.(*Device) + err := byte(0) + msg := "OK" + + if _, ok := br.devices[devid]; ok { + log.Error().Msg("Device ID conflicting: " + devid) + msg = "ID conflicting" + err = 1 + } else { + dev.registered = true + br.devices[devid] = c + log.Info().Msgf("Device '%s' registered", devid) + } + + c.WriteMsg(MSG_TYPE_REGISTER, append([]byte{err}, msg...)) + + if err > 0 { + // ensure the last packet was sent + time.AfterFunc(time.Millisecond*100, func() { + dev.Close() + }) + } + } else { + if dev, ok := br.devices[devid]; ok { + sid := GenUniqueID("sid") + + s := &Session{ + dev: dev, + user: c, + } + + time.AfterFunc(time.Second*3, func() { + if atomic.LoadUint32(&s.confirmed) == 0 { + c.Close() + } + }) + + br.sessions[sid] = s + + dev.WriteMsg(MSG_TYPE_SESSION_CREATE, []byte(sid)) + log.Info().Msg("New session: " + sid) + } else { + log.Error().Msgf("Not found the device '%s'", devid) + } + } + + case c := <-br.unregister: + devid := c.DeviceID() + + c.Close() + + if c.DeviceCategory() == "device" { + if !c.(*Device).registered { + break + } + + delete(br.devices, devid) + + for sid, s := range br.sessions { + if s.dev == c { + s.user.Close() + delete(br.sessions, sid) + log.Info().Msg("Delete session: " + sid) + } + } + + log.Info().Msgf("Device '%s' unregistered", devid) + } else { + sid := c.(*Device).DeviceID() + + if _, ok := br.sessions[sid]; ok { + delete(br.sessions, sid) + + if dev, ok := br.devices[devid]; ok { + dev.WriteMsg(MSG_TYPE_SESSION_DESTORY, []byte(sid)) + } + + log.Info().Msg("Delete session: " + sid) + } + } + + case msg := <-br.deviceMessageChan: + if msg.typ == MSG_TYPE_SESSION_CREATE { + if s, ok := br.sessions[msg.sid]; ok { + + atomic.StoreUint32(&s.confirmed, 1) + + // u := s.user.(*user) + // u.sid = msg.sid + + // userLoginAck(loginErrorNone, s.user) + + } + } + + if msg.typ == MSG_TYPE_SESSION_DATA { + if s, ok := br.sessions[msg.sid]; ok { + s.user.WriteMsg(websocket.BinaryMessage, msg.data) + } + } + + // case msg := <-br.userMessageChan: + // if s, ok := br.sessions[msg.sid]; ok { + // if dev, ok := br.devices[s.dev.DeviceID()]; ok { + // data := msg.data + + // if msg.typ == websocket.BinaryMessage { + // dev.WriteMsg(MSG_TYPE_SESSION_DATA, append([]byte(msg.sid), data[1:]...)) + // } + // } + // } + } + } +} diff --git a/server/client.go b/server/client.go new file mode 100644 index 0000000..0d4e7d6 --- /dev/null +++ b/server/client.go @@ -0,0 +1,265 @@ +package main + +import ( + "bufio" + "bytes" + "encoding/binary" + "io" + "net" + "strings" + "sync/atomic" + "time" + + "github.com/rs/zerolog/log" +) + +type Client interface { + DeviceID() string + DeviceCategory() string + + WriteMsg(typ int, data []byte) + Close() +} + +const HEART_BEAT_INTERVAL = time.Second * 5 // 心跳超时时间 + +const ( + MSG_TYPE_UNKOWNM = iota + MSG_TYPE_HEARTBEAT + MSG_TYPE_REGISTER + MSG_TYPE_SESSION_CREATE + MSG_TYPE_SESSION_DATA + MSG_TYPE_SESSION_DESTORY + MSG_TYPE_TUNNEL_CREATE + MSG_TYPE_MAX +) + +type Device struct { + br *Broker + id string + category string + desc string /* description of the device */ + conn net.Conn + create_time int64 /* connection time */ + active time.Time + uptime uint32 + token string + registered bool + closed uint32 + send chan []byte // Buffered channel of outbound messages. +} + +type deviceMessage struct { + sid string + typ int + data []byte +} + +func (dev *Device) DeviceID() string { + return dev.id +} + +func (dev *Device) DeviceCategory() string { + return dev.category +} + +// 往设备写数据 +func (dev *Device) WriteMsg(typ int, data []byte) { + b := []byte{byte(typ), 0, 0} + + binary.BigEndian.PutUint16(b[1:], uint16(len(data))) + + dev.send <- append(b, data...) +} + +func (dev *Device) Close() { + if atomic.LoadUint32(&dev.closed) == 1 { + return + } + atomic.StoreUint32(&dev.closed, 1) + + log.Debug().Msgf("Device '%s' disconnected", dev.conn.RemoteAddr()) + + dev.conn.Close() + + close(dev.send) +} + +// 解析设备信息 +func parseDeviceInfo(dev *Device, b []byte) bool { + + fields := bytes.Split(b, []byte{0}) + + if len(fields) < 3 { + log.Error().Msg("msgTypeRegister: invalid") + return false + } + + dev.id = string(fields[0]) + dev.desc = string(fields[1]) + dev.token = string(fields[2]) + + return true +} + +// 解析设备心跳 +func parseHeartbeat(dev *Device, b []byte) { + dev.uptime = binary.BigEndian.Uint32(b[:4]) +} + +func (dev *Device) readLoop() { + defer func() { + dev.br.unregister <- dev + }() + + reader := bufio.NewReader(dev.conn) + + for { + b, err := reader.Peek(5) + if err != nil { + if err != io.EOF && !strings.Contains(err.Error(), "use of closed network connection") { + log.Error().Msg(err.Error()) + } + return + } + + reader.Discard(5) + + msg_type := b[0] + + if msg_type >= MSG_TYPE_MAX { + log.Error().Msgf("invalid msg type: %d", msg_type) + return + } + + msg_length := binary.BigEndian.Uint32(b[1:]) + data := make([]byte, msg_length) + _, err = io.ReadFull(reader, data) + if err != nil { + log.Error().Msg(err.Error()) + return + } + + dev.active = time.Now() + + switch msg_type { + case MSG_TYPE_REGISTER: + if msg_length < 2 { + log.Error().Msg("msgTypeRegister: invalid") + return + } + if !parseDeviceInfo(dev, data) { + return + } + + dev.br.register <- dev + + case MSG_TYPE_SESSION_CREATE: + + if msg_length < 33 { + log.Error().Msg("msgTypeLogin: invalid") + return + } + + dev.br.deviceMessageChan <- deviceMessage{string(data[:32]), MSG_TYPE_SESSION_CREATE, data[32:]} + + case MSG_TYPE_SESSION_DATA: + if msg_length < 32 { + log.Error().Msg("msgTypeTermData|msgTypeFile: invalid") + return + } + + dev.br.deviceMessageChan <- deviceMessage{string(data[:32]), MSG_TYPE_SESSION_DATA, data[32:]} + + case MSG_TYPE_HEARTBEAT: + parseHeartbeat(dev, b) + + default: + log.Error().Msgf("invalid msg type: %d", msg_type) + } + } +} + +func (dev *Device) writeLoop() { + ticker := time.NewTicker(time.Second) + + defer func() { + ticker.Stop() + dev.br.unregister <- dev + }() + + ninactive := 0 + lastHeartbeat := time.Now() + + for { + select { + case msg, ok := <-dev.send: + if !ok { + return + } + + _, err := dev.conn.Write(msg) + if err != nil { + log.Error().Msg(err.Error()) + return + } + + case <-ticker.C: + now := time.Now() + if now.Sub(dev.active) > HEART_BEAT_INTERVAL*3/2 { + if dev.id == "" { + return + } + + log.Error().Msgf("Inactive device in long time: %s", dev.id) + if ninactive > 3 { + log.Error().Msgf("Inactive 3 times, now kill it: %s", dev.id) + return + } + ninactive = ninactive + 1 + } + + if now.Sub(lastHeartbeat) > HEART_BEAT_INTERVAL-1 { + lastHeartbeat = now + if len(dev.send) < 1 { + dev.WriteMsg(MSG_TYPE_HEARTBEAT, []byte{}) + } + } + } + } +} + +func listenDevice(br *Broker) { + + ln, err := net.Listen("tcp", br.cfg.AddrDev) + if err != nil { + log.Error().Msgf(err.Error()) + } + + log.Info().Msgf("Listen Device on: %s", br.cfg.AddrDev) + + go func() { + defer ln.Close() + + for { + conn, err := ln.Accept() + if err != nil { + log.Error().Msg(err.Error()) + continue + } + + log.Debug().Msgf("Device '%s' connected", conn.RemoteAddr()) + + dev := &Device{ + br: br, + conn: conn, + create_time: time.Now().Unix(), + send: make(chan []byte, 256), + } + + go dev.readLoop() + go dev.writeLoop() + } + }() + +} diff --git a/server/go.mod b/server/go.mod new file mode 100644 index 0000000..e20a1b3 --- /dev/null +++ b/server/go.mod @@ -0,0 +1,23 @@ +module main + +go 1.21.5 + +require github.com/rs/zerolog v1.31.0 + +require ( + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/term v0.13.0 // indirect +) + +require ( + github.com/gorilla/websocket v1.5.1 + github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/urfave/cli/v2 v2.27.1 + golang.org/x/sys v0.13.0 // indirect +) diff --git a/server/go.sum b/server/go.sum new file mode 100644 index 0000000..7f48fa5 --- /dev/null +++ b/server/go.sum @@ -0,0 +1,34 @@ +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb28sjiS3i7tcKCkflWFEkHfuAgM= +github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= +github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= +github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= diff --git a/server/main.go b/server/main.go new file mode 100644 index 0000000..682b8fd --- /dev/null +++ b/server/main.go @@ -0,0 +1,82 @@ +package main + +import ( + "fmt" + "os" + + "github.com/urfave/cli/v2" +) + +type Config struct { + AddrDev string + AddrUser string +} + +func runServer(c *cli.Context) { + + cfg := &Config{ + AddrDev: "0.0.0.0:9011", + AddrUser: "0.0.0.0:9012", + } + + br := newBroker(cfg) + listenDevice(br) + + br.run() +} + +func main() { + app := &cli.App{ + Name: "XZRobot Ops Server", + Usage: "The Server Side For xzrobot ops", + Version: "1.0.0", + Commands: []*cli.Command{ + { + Name: "run", + Usage: "Run Server", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "log", + Value: "log.txt", + Usage: "log file path", + }, + &cli.StringFlag{ + Name: "conf", + Aliases: []string{"c"}, + Value: "./rttys.conf", + Usage: "config file to load", + }, + &cli.StringFlag{ + Name: "addr-dev", + Value: ":9011", + Usage: "address to listen device", + }, + &cli.StringFlag{ + Name: "addr-user", + Value: ":9012", + Usage: "address to listen user", + }, + &cli.StringFlag{ + Name: "db", + Value: "sqlite://database.db", + Usage: "database source", + }, + }, + Action: func(c *cli.Context) error { + runServer(c) + return nil + }, + }, + }, + Action: func(c *cli.Context) error { + c.App.Command("run").Run(c) + return nil + }, + } + + err := app.Run(os.Args) + if err != nil { + fmt.Println(err) + os.Exit(1) + } +} diff --git a/server/utils.go b/server/utils.go new file mode 100644 index 0000000..25b8078 --- /dev/null +++ b/server/utils.go @@ -0,0 +1,41 @@ +package main + +import ( + "crypto/md5" + "crypto/rand" + "encoding/binary" + "encoding/hex" + "fmt" + "io" + "os" + "time" + + "github.com/howeyc/gopass" + "github.com/rs/zerolog/log" +) + +// GenUniqueID generate a unique ID +func GenUniqueID(extra string) string { + buf := make([]byte, 20) + + binary.BigEndian.PutUint32(buf, uint32(time.Now().Unix())) + io.ReadFull(rand.Reader, buf[4:]) + + h := md5.New() + h.Write(buf) + h.Write([]byte(extra)) + + return hex.EncodeToString(h.Sum(nil)) +} + +// GenToken generate a token +func GenToken() { + password, err := gopass.GetPasswdPrompt("Please set a password:", true, os.Stdin, os.Stdout) + if err != nil { + log.Fatal().Msg(err.Error()) + } + + token := GenUniqueID(string(password)) + + fmt.Println("Your token is:", token) +}