189 lines
3.9 KiB
Go
189 lines
3.9 KiB
Go
|
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
|
||
|
}
|