From 40ef949dbe9f933d2fc4746154f7b74f6fe2f87e Mon Sep 17 00:00:00 2001 From: Robert Bendun Date: Fri, 16 Dec 2022 02:11:43 +0100 Subject: [PATCH] integrated new host recognition algorithm with Musique --- musique/main.cc | 7 +++ scripts/build.mk | 2 +- server/main.go | 107 ++++++++++++++++----------------------- server/musique-bridge.go | 92 ++++++++++++++------------------- server/proto/net.go | 35 +++++++++---- server/scan/scanner.go | 22 ++++---- 6 files changed, 127 insertions(+), 138 deletions(-) diff --git a/musique/main.cc b/musique/main.cc index b58b316..9a70f87 100644 --- a/musique/main.cc +++ b/musique/main.cc @@ -334,6 +334,13 @@ static Result handle_repl_session_commands(std::string_view input, Runner return std::nullopt; } }, + Command { + "remotes", + +[](Runner&, std::optional) -> std::optional { + ListKnownRemotes(); + return std::nullopt; + } + } }; if (input.starts_with('!')) { diff --git a/scripts/build.mk b/scripts/build.mk index 6190cc0..0e6b7dd 100644 --- a/scripts/build.mk +++ b/scripts/build.mk @@ -2,7 +2,7 @@ Release_Obj=$(addprefix bin/$(os)/,$(Obj)) Server=bin/$(os)/server/server.h bin/$(os)/server/server.o -$(Server) &: server/*.go +$(Server) &: server/*.go server/**/*.go cd server/; GOOS="$(GOOS)" GOARCH="$(GOARCH)" CGO_ENABLED=1 CC="$(CC)" \ go build -o ../bin/$(os)/server/server.o -buildmode=c-archive diff --git a/server/main.go b/server/main.go index dd5b057..05d0f5e 100644 --- a/server/main.go +++ b/server/main.go @@ -2,13 +2,13 @@ package main import ( "bufio" - "encoding/json" "errors" "flag" "fmt" "log" "musique/server/proto" "musique/server/scan" + "musique/server/router" "net" "os" "strings" @@ -136,22 +136,15 @@ func notifyAll(clients []client) <-chan time.Time { return startDeadline } -func handleIncoming(incoming net.Conn) { - defer incoming.Close() - - request := proto.Request{} - json.NewDecoder(incoming).Decode(&request) - log.Printf("%s: %+v\n", incoming.RemoteAddr(), request) - - if request.Type == "handshake" { +func registerRoutes(r *router.Router) { + r.Add("handshake", func(incoming net.Conn, request proto.Request) interface{} { var response proto.HandshakeResponse response.Version = proto.Version response.Nick = nick - json.NewEncoder(incoming).Encode(response) - return - } + return response + }) - if request.Type == "hosts" { + r.Add("hosts", func(incoming net.Conn, request proto.Request) interface{} { var response proto.HostsResponse for _, remote := range remotes { response.Hosts = append(response.Hosts, proto.HostsResponseEntry{ @@ -160,43 +153,18 @@ func handleIncoming(incoming net.Conn) { Address: remote.Address, }) } - json.NewEncoder(incoming).Encode(response) - return - } + return response + }) - if request.Type == "synchronize-hosts" { - response := synchronizeHosts(request.HostsResponse) - json.NewEncoder(incoming).Encode(response) - return - } + r.Add("synchronize-hosts", func(incoming net.Conn, request proto.Request) interface{} { + return synchronizeHosts(request.HostsResponse) + }) - if request.Type == "synchronize-hosts-with-remotes" { + + r.Add("synchronize-hosts-with-remotes", func(incoming net.Conn, request proto.Request) interface{} { synchronizeHostsWithRemotes() - return - } -} - -func runCommandServer(port uint16) <-chan struct{} { - listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", baseIP, port)) - if err != nil { - log.Fatalln(err) - } - - exit := make(chan struct{}) - - go func() { - defer listener.Close() - defer close(exit) - for { - incoming, err := listener.Accept() - if err != nil { - log.Fatal(err) - } - go handleIncoming(incoming) - } - }() - - return exit + return nil + }) } type Remote struct { @@ -206,9 +174,9 @@ type Remote struct { } var ( - baseIP string + baseIP string = "" nick string - port int + port int = 8888 remotes map[string]Remote ) @@ -309,6 +277,28 @@ func synchronizeHostsWithRemotes() { } } +func registerRemotes() error { + networks, err := scan.AvailableNetworks() + if err != nil { + return err + } + + hosts := scan.TCPHosts(networks, []uint16{8081, 8082, 8083, 8084}) + + remotes = make(map[string]Remote) + for host := range hosts { + if !isThisMyAddress(host.Address) { + remotes[host.Address] = Remote{ + Address: host.Address, + Nick: host.Nick, + Version: host.Version, + } + } + } + + return nil +} + func main() { var ( logsPath string @@ -334,24 +324,15 @@ func main() { log.Fatalln("Please provide nick via --nick flag") } - exit := runCommandServer(uint16(port)) - - networks, err := scan.AvailableNetworks() + r := router.Router{} + registerRoutes(&r) + exit, err := r.Run(baseIP, uint16(port)) if err != nil { log.Fatalln(err) } - hosts := scan.TCPHosts(networks, []uint16{8081, 8082, 8083, 8084}) - - remotes = make(map[string]Remote) - for host := range hosts { - if !isThisMyAddress(host.Address) { - remotes[host.Address] = Remote{ - Address: host.Address, - Nick: host.Nick, - Version: host.Version, - } - } + if err := registerRemotes(); err != nil { + log.Fatalln(err) } for range exit { diff --git a/server/musique-bridge.go b/server/musique-bridge.go index 6943362..2e44747 100644 --- a/server/musique-bridge.go +++ b/server/musique-bridge.go @@ -2,69 +2,51 @@ package main import ( "C" - "bufio" "fmt" - "net" - "strings" - "sync" - "time" + "log" + "musique/server/router" + "os" + "sort" ) -var clients []client -var pinger chan struct{} - //export ServerInit func ServerInit() { - // scanResult = scan() - pinger = make(chan struct{}, 100) + r := router.Router{} + registerRoutes(&r) + _, err := r.Run(baseIP, uint16(port)) + if err != nil { + log.Fatalln(err) + } - waitForConnection := sync.WaitGroup{} - waitForConnection.Add(1) - - go func() { - l, err := net.Listen("tcp", ":8081") - if err != nil { - return - } - defer l.Close() - waitForConnection.Done() - for { - conn, err := l.Accept() - if err != nil { - continue - } - go func(c net.Conn) { - defer c.Close() - s := bufio.NewScanner(c) - for s.Scan() { - response := s.Text() - if response == "time" { - fmt.Fprintln(conn, time.Now().UnixMilli()) - continue - } - - if strings.HasPrefix(response, "start") { - startTimeString := strings.TrimSpace(response[len("start"):]) - startTime := int64(0) - fmt.Sscanf(startTimeString, "%d", &startTime) - time.Sleep(time.Duration(startTime) * time.Millisecond) - pinger <- struct{}{} - continue - } - } - }(conn) - } - }() - waitForConnection.Wait() - scanResult := []string{} // scan() - clients = timesync(scanResult) + if err := registerRemotes(); err != nil { + log.Fatalln(err) + } } //export ServerBeginProtocol func ServerBeginProtocol() { - self := notifyAll(clients) - select { - case <-self: - case <-pinger: - } +} + +//export ListKnownRemotes +func ListKnownRemotes() { + type nickAddr struct { + nick, addr string + } + + list := []nickAddr{} + for _, remote := range remotes { + list = append(list, nickAddr { remote.Nick, remote.Address}) + } + + sort.Slice(list, func (i, j int) bool { + if list[i].nick == list[j].nick { + return list[i].addr < list[j].addr + } + return list[i].nick < list[j].nick + }) + + for _, nickAddr := range list { + fmt.Printf("%s@%s\n", nickAddr.nick, nickAddr.addr) + } + os.Stdout.Sync() } diff --git a/server/proto/net.go b/server/proto/net.go index 0f4b310..c4e9851 100644 --- a/server/proto/net.go +++ b/server/proto/net.go @@ -2,6 +2,7 @@ package proto import ( "encoding/json" + "errors" "net" "time" ) @@ -21,15 +22,31 @@ func Command(target string, request interface{}, response interface{}) error { } func CommandTimeout(target string, request interface{}, response interface{}, timeout time.Duration) error { - conn, err := net.DialTimeout("tcp", target, timeout) - if err != nil { - return err - } - defer conn.Close() + responseChan := make(chan interface{}) + errorChan := make(chan error) - if err = json.NewEncoder(conn).Encode(request); err != nil { - return err - } + go func() { + conn, err := net.DialTimeout("tcp", target, timeout) + if err != nil { + errorChan <- err + return + } + defer conn.Close() - return json.NewDecoder(conn).Decode(response) + if err = json.NewEncoder(conn).Encode(request); err != nil { + errorChan <- err + return + } + + responseChan <- json.NewDecoder(conn).Decode(response) + }() + + select { + case response = <-responseChan: + return nil + case err := <-errorChan: + return err + case <-time.After(timeout): + return errors.New("timout") + } } diff --git a/server/scan/scanner.go b/server/scan/scanner.go index fb38b79..3d52508 100644 --- a/server/scan/scanner.go +++ b/server/scan/scanner.go @@ -15,12 +15,13 @@ type Network struct { const timeoutTCPHosts = time.Duration(1) * time.Second -func nextIP(ip net.IP) net.IP { +func nextIP(ip net.IP) (next net.IP) { // FIXME Proper next IP address in network calculation - next := make([]byte, 4) - copy(next, ip) - next[3]++ - return next + next = make([]byte, 4) + bytes := []byte(ip) + bytes = bytes[len(bytes)-4:] + next[0], next[1], next[2], next[3] = bytes[0], bytes[1], bytes[2], bytes[3]+1 + return } // AvailableNetworks returns all IPv4 networks that are available to the host @@ -38,10 +39,12 @@ func AvailableNetworks() ([]Network, error) { continue } - if ipNet.IP.IsGlobalUnicast() && ipNet.IP.To4() != nil { - // FIXME We assume mask /24. This is a reasonable assumption performance wise - // but may lead to inability to recognize some of the host in network - networks = append(networks, Network{nextIP(ipNet.IP).String(), 254}) + if ipNet.IP.IsGlobalUnicast() { + if ip := ipNet.IP.To4(); ip != nil { + // FIXME We assume mask /24. This is a reasonable assumption performance wise + // but may lead to inability to recognize some of the host in network + networks = append(networks, Network{nextIP(ipNet.IP).String(), 253}) + } } } @@ -68,7 +71,6 @@ func TCPHosts(networks []Network, ports []uint16) <-chan Response { defer wg.Done() target := fmt.Sprintf("%s:%d", ip, port) var hs proto.HandshakeResponse - err := proto.CommandTimeout(target, proto.Handshake(), &hs, timeoutTCPHosts) if err == nil { ips <- Response{hs, target}