go-emlid/cmd/fake-reach/main.go

163 lines
3.5 KiB
Go

package main
import (
"context"
"fmt"
"log/slog"
"net"
"net/http"
"os"
"os/signal"
"forge.cadoles.com/cadoles/go-emlid/reach/discovery"
socketio "github.com/googollee/go-socket.io"
"github.com/grandcat/zeroconf"
"github.com/pkg/errors"
sloghttp "github.com/samber/slog-http"
"github.com/wlynxg/anet"
)
func main() {
ctx := context.Background()
server, err := startHTTPServer(ctx)
if err != nil {
slog.Error("could not start http server", slog.Any("error", errors.WithStack(err)))
os.Exit(1)
}
defer server.Close()
service, err := startMDNSService(ctx)
if err != nil {
slog.Error("could not start mdns service", slog.Any("error", errors.WithStack(err)))
os.Exit(1)
}
defer service.Shutdown()
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
slog.Info("Ctrl+C to interrupt")
<-interrupt
}
func startHTTPServer(ctx context.Context) (*http.Server, error) {
mux := http.NewServeMux()
sioServer := NewSocketIOServer()
go func() {
if err := sioServer.Serve(); err != nil {
slog.Error("could not serve socketio", slog.Any("error", errors.WithStack(err)))
}
}()
sioServer.OnEvent("/", "action", func(s socketio.Conn, msg map[string]any) {
slog.Info("action event", slog.Any("id", s.ID()), slog.Any("message", msg))
name, ok := msg["name"]
if !ok {
return
}
switch name {
case "reboot":
if err := s.Close(); err != nil {
slog.Error("could not close connection", slog.Any("error", errors.WithStack(err)))
}
}
})
mux.Handle("/socket.io/", sioServer)
mux.HandleFunc("GET /info", GetInfo)
mux.HandleFunc("GET /configuration", GetConfiguration)
mux.HandleFunc("POST /configuration/base_mode/base_coordinates", PostJSON)
handler := sloghttp.Recovery(mux)
handler = sloghttp.New(slog.Default())(handler)
server := &http.Server{
Addr: ":8080",
Handler: handler,
}
go func() {
defer sioServer.Close()
slog.Info("starting http server", slog.Any("address", server.Addr))
if err := server.ListenAndServe(); err != nil {
slog.Error("could not listen and serve http requests", slog.Any("error", errors.WithStack(err)))
}
}()
return server, nil
}
func startMDNSService(ctx context.Context) (*zeroconf.Server, error) {
hostname, err := os.Hostname()
if err != nil {
return nil, errors.WithStack(err)
}
ifaces, err := anet.Interfaces()
if err != nil {
return nil, errors.WithStack(err)
}
ips, err := GetLANIPv4Addrs()
if err != nil {
return nil, errors.WithStack(err)
}
info := []string{
fmt.Sprintf("local_name=%s", hostname),
"device=FakeReach",
"manufacturer_data=egoHIABggkMnZWSuoy0KAgAA",
}
slog.Info("announcing mdns service", slog.Any("ips", ips), slog.Any("info", info))
server, err := zeroconf.RegisterProxy("Reach", discovery.ReachService, "local.", 8080, hostname, ips, info, ifaces)
if err != nil {
return nil, errors.WithStack(err)
}
return server, nil
}
var (
_, lanA, _ = net.ParseCIDR("10.0.0.0/8")
_, lanB, _ = net.ParseCIDR("172.16.0.0/12")
_, lanC, _ = net.ParseCIDR("192.168.0.0/16")
)
func GetLANIPv4Addrs() ([]string, error) {
ips := make([]string, 0)
addrs, err := anet.InterfaceAddrs()
if err != nil {
return nil, errors.WithStack(err)
}
for _, address := range addrs {
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
ipv4 := ipnet.IP.To4()
if ipv4 == nil {
continue
}
isLAN := lanA.Contains(ipv4) || lanB.Contains(ipv4) || lanC.Contains(ipv4)
if !isLAN {
continue
}
ips = append(ips, ipv4.String())
}
}
return ips, nil
}