feat: add reachview proxy utility
This commit is contained in:
17
cmd/proxy/README.md
Normal file
17
cmd/proxy/README.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Reach Proxy
|
||||
|
||||
Ce petit utilitaire permet de créer un proxy vers un module Reach, permettant ainsi à une instance [Fieldnotes](https://forge.cadoles.com/Pyxis/fieldnotes) de découvrir et travailler avec des modules qui seraient sur un autre réseau, par exemple:
|
||||
|
||||
```
|
||||
Fieldnotes ----- [ Réseau WiFi local ] ------> PC avec proxy ----- [ VPN ] ------> Module Reach
|
||||
```
|
||||
|
||||
Le proxy s'annonce en mDNS permettant ainsi à l'application mobile de l'identifier automatiquement sur le réseau, comme si le proxy était un module Reach.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
go run ./cmd/proxy -address <listening_address> -host <reachview_module_host>
|
||||
```
|
||||
|
||||
Plus d'informations sont disponibles sur les différents flags via la commande `go run ./cmd/proxy -h`.
|
188
cmd/proxy/main.go
Normal file
188
cmd/proxy/main.go
Normal file
@ -0,0 +1,188 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
|
||||
"forge.cadoles.com/cadoles/go-emlid/reach/discovery"
|
||||
"github.com/grandcat/zeroconf"
|
||||
"github.com/pkg/errors"
|
||||
sloghttp "github.com/samber/slog-http"
|
||||
"github.com/wlynxg/anet"
|
||||
)
|
||||
|
||||
var (
|
||||
address = ":8080"
|
||||
host = "192.168.42.1"
|
||||
name = ""
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&address, "address", address, "proxy listening address")
|
||||
flag.StringVar(&host, "host", host, "reachview host adress")
|
||||
flag.StringVar(&name, "name", name, "reachview service name, default machine hostname")
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
server, err := startProxy(ctx, address, host)
|
||||
if err != nil {
|
||||
slog.Error("could not start proxy", slog.Any("error", errors.WithStack(err)))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
defer server.Close()
|
||||
|
||||
service, err := startMDNSService(ctx, address, name)
|
||||
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 startProxy(ctx context.Context, address string, host string) (*http.Server, error) {
|
||||
backendURL, err := url.Parse(fmt.Sprintf("http://%s", host))
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
proxy := httputil.NewSingleHostReverseProxy(backendURL)
|
||||
|
||||
handler := sloghttp.Recovery(proxy)
|
||||
handler = sloghttp.New(slog.Default())(handler)
|
||||
|
||||
server := &http.Server{
|
||||
Addr: address,
|
||||
Handler: handler,
|
||||
}
|
||||
|
||||
go func() {
|
||||
slog.Info("starting proxy server", slog.Any("address", server.Addr))
|
||||
|
||||
if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
slog.Error("could not listen and proxy http requests", slog.Any("error", errors.WithStack(err)))
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
if err := server.Close(); err != nil {
|
||||
slog.Error("could not close proxy server", slog.Any("error", errors.WithStack(err)))
|
||||
}
|
||||
}()
|
||||
|
||||
return server, nil
|
||||
}
|
||||
|
||||
func startMDNSService(ctx context.Context, address string, name string) (*zeroconf.Server, error) {
|
||||
if name == "" {
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
name = hostname
|
||||
}
|
||||
|
||||
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", name),
|
||||
"device=ReachProxy",
|
||||
"manufacturer_data=egoHIABggkMnZWSuoy0KAgAA",
|
||||
}
|
||||
|
||||
host, rawPort, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
var port int64
|
||||
if rawPort != "" {
|
||||
port, err = strconv.ParseInt(rawPort, 10, 32)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
}
|
||||
|
||||
if host != "" {
|
||||
ips = []string{host}
|
||||
}
|
||||
|
||||
slog.Info("announcing mdns service", slog.Any("ips", ips), slog.Any("info", info))
|
||||
|
||||
server, err := zeroconf.RegisterProxy("Reach", discovery.ReachService, "local.", int(port), name, ips, info, ifaces)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
server.Shutdown()
|
||||
}()
|
||||
|
||||
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
|
||||
}
|
Reference in New Issue
Block a user