WIP
This commit is contained in:
@@ -44,7 +44,8 @@ func main() {
|
||||
log.Fatalf("init: %v", err)
|
||||
}
|
||||
|
||||
app, err := peer.New(state, *hub, apiKey, *name)
|
||||
ifaceName := strings.TrimSuffix(state.LocalDomain, ".local")
|
||||
app, err := peer.New(state, *hub, apiKey, ifaceName, state.LocalDomain)
|
||||
if err != nil {
|
||||
log.Fatalf("start: %v", err)
|
||||
}
|
||||
|
||||
@@ -95,6 +95,9 @@ func Peer_Validate(p *Peer) error {
|
||||
return ErrInvalidPort
|
||||
}
|
||||
|
||||
if len(p.Name) == 0 {
|
||||
return ErrInvalidPeerName
|
||||
}
|
||||
for _, c := range p.Name {
|
||||
if c >= 'a' && c <= 'z' {
|
||||
continue
|
||||
@@ -102,7 +105,7 @@ func Peer_Validate(p *Peer) error {
|
||||
if c >= '0' && c <= '9' {
|
||||
continue
|
||||
}
|
||||
if c == '.' || c == '-' || c == '_' {
|
||||
if c == '-' {
|
||||
continue
|
||||
}
|
||||
return ErrInvalidPeerName
|
||||
|
||||
@@ -332,8 +332,9 @@ func (a *App) _peerInit(peer *api.Peer, w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
resp := m.PeerInitResp{
|
||||
PeerIP: peer.PeerIP,
|
||||
Network: net.Network,
|
||||
PeerIP: peer.PeerIP,
|
||||
Network: net.Network,
|
||||
LocalDomain: net.LocalDomain,
|
||||
}
|
||||
|
||||
resp.NetworkState.Peers, err = a.peersArray(net.NetworkID)
|
||||
@@ -345,19 +346,11 @@ func (a *App) _peerInit(peer *api.Peer, w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
func (a *App) _peerFetchState(peer *api.Peer, w http.ResponseWriter, r *http.Request) error {
|
||||
net, err := a.api.Network_Get(peer.NetworkID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
peers, err := a.peersArray(peer.NetworkID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return a.sendJSON(w, m.NetworkState{
|
||||
LocalDomain: net.LocalDomain,
|
||||
Peers: peers,
|
||||
})
|
||||
return a.sendJSON(w, m.NetworkState{Peers: peers})
|
||||
}
|
||||
|
||||
func (a *App) peersArray(networkID int64) (peers [256]*m.Peer, err error) {
|
||||
|
||||
@@ -9,6 +9,7 @@ type PeerInitArgs struct {
|
||||
type PeerInitResp struct {
|
||||
PeerIP byte
|
||||
Network []byte
|
||||
LocalDomain string
|
||||
NetworkState NetworkState
|
||||
}
|
||||
|
||||
@@ -25,6 +26,5 @@ type Peer struct {
|
||||
}
|
||||
|
||||
type NetworkState struct {
|
||||
LocalDomain string
|
||||
Peers [256]*Peer
|
||||
Peers [256]*Peer
|
||||
}
|
||||
|
||||
20
peer/app.go
20
peer/app.go
@@ -25,6 +25,7 @@ const (
|
||||
type HubPeer struct {
|
||||
PubKey wgtypes.Key
|
||||
VPNIP netip.Addr
|
||||
Name string
|
||||
IsRelay bool
|
||||
IsPublic bool
|
||||
EndpointV4 netip.AddrPort // zero if none
|
||||
@@ -47,12 +48,13 @@ type MulticastEvent struct {
|
||||
// accessed only from the Run goroutine.
|
||||
type App struct {
|
||||
// Identity
|
||||
vpnIP netip.Addr
|
||||
vpnNet netip.Prefix
|
||||
privKey wgtypes.Key
|
||||
pubKey wgtypes.Key
|
||||
isRelay bool
|
||||
isPublic bool
|
||||
vpnIP netip.Addr
|
||||
vpnNet netip.Prefix
|
||||
privKey wgtypes.Key
|
||||
pubKey wgtypes.Key
|
||||
isRelay bool
|
||||
isPublic bool
|
||||
localDomain string
|
||||
|
||||
// Infrastructure
|
||||
dev WGDevice
|
||||
@@ -76,6 +78,11 @@ type App struct {
|
||||
|
||||
// Run is the main event loop. It runs until SIGTERM/SIGINT.
|
||||
func (a *App) Run() error {
|
||||
// Establish a clean hosts section before the first poll lands, clearing
|
||||
// any stale entries left by a prior run (e.g. crash, or peers removed
|
||||
// while we were down).
|
||||
a.updateHosts()
|
||||
|
||||
ticker := time.NewTicker(PingInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
@@ -102,5 +109,6 @@ func (a *App) Run() error {
|
||||
}
|
||||
|
||||
func (a *App) onShutdown() error {
|
||||
// TODO: removeHosts() ?
|
||||
return wginterface.Delete(a.dev.Name())
|
||||
}
|
||||
|
||||
@@ -38,30 +38,36 @@ func (a *App) devPeers() []wgtypes.Peer {
|
||||
}
|
||||
|
||||
func (a *App) devAddPeer(p *Peer) {
|
||||
log.Printf("RELAYED: %s - %s ", p.Name, p.VPNIP.String())
|
||||
devRetry(p.VPNIP, "AddPeer", func() error { return a.dev.AddPeer(p.PubKey()) })
|
||||
p.State = StateRelayed
|
||||
}
|
||||
|
||||
func (a *App) devAddDirect(p *Peer, endpoint netip.AddrPort) {
|
||||
log.Printf("DIRECT: %s - %s @ %s", p.Name, p.VPNIP.String(), endpoint.String())
|
||||
devRetry(p.VPNIP, "AddDirect", func() error { return a.dev.AddDirect(p.PubKey(), endpoint, p.VPNIP) })
|
||||
p.State = StateDirect
|
||||
}
|
||||
|
||||
func (a *App) devSetRelay(p *Peer, endpoint netip.AddrPort) {
|
||||
log.Printf("RELAY: %s - %s @ %s", p.Name, p.VPNIP.String(), endpoint.String())
|
||||
devRetry(p.VPNIP, "SetRelay", func() error { return a.dev.SetRelay(p.PubKey(), endpoint, a.vpnNet) })
|
||||
p.State = StateDirect
|
||||
}
|
||||
|
||||
func (a *App) devPromote(p *Peer) {
|
||||
log.Printf("PROMOTED: %s - %s @ %s", p.Name, p.VPNIP.String(), p.WGEndpoint().String())
|
||||
devRetry(p.VPNIP, "Promote", func() error { return a.dev.Promote(p.PubKey(), p.VPNIP) })
|
||||
p.State = StateDirect
|
||||
}
|
||||
|
||||
func (a *App) devAddProbe(p *Peer, endpoint netip.AddrPort) {
|
||||
log.Printf("PROBE: %s - %s @ %s", p.Name, p.VPNIP.String(), endpoint.String())
|
||||
devRetry(p.VPNIP, "AddProbe", func() error { return a.dev.AddProbe(p.PubKey(), endpoint) })
|
||||
p.State = StateProbing
|
||||
}
|
||||
|
||||
func (a *App) devRemove(p *Peer) {
|
||||
log.Printf("REMOVED: %s - %s", p.Name, p.VPNIP.String())
|
||||
devRetry(p.VPNIP, "RemovePeer", func() error { return a.dev.RemovePeer(p.PubKey()) })
|
||||
}
|
||||
|
||||
121
peer/hosts.go
Normal file
121
peer/hosts.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package peer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/netip"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"git.crumpington.com/lib/go/flock"
|
||||
)
|
||||
|
||||
const (
|
||||
hostsFile = "/etc/hosts"
|
||||
hostsBegin = "# BEGIN vppn "
|
||||
hostsEnd = "# END vppn "
|
||||
)
|
||||
|
||||
// updateHosts rewrites the managed vppn section in /etc/hosts using the
|
||||
// current peersByIP map. Peers without a Name are skipped.
|
||||
func (a *App) updateHosts() {
|
||||
if a.localDomain == "" {
|
||||
return
|
||||
}
|
||||
if err := updateHosts(hostsFile, a.localDomain, a.peersByIP); err != nil {
|
||||
log.Printf("Failed to update hosts file: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func updateHosts(hostsPath, localDomain string, peers map[netip.Addr]*Peer) error {
|
||||
lockFile, err := flock.Lock(hostsPath + ".vppn.lock")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer lockFile.Close()
|
||||
|
||||
begin := hostsBegin + localDomain
|
||||
end := hostsEnd + localDomain
|
||||
|
||||
info, err := os.Stat(hostsPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
raw, err := os.ReadFile(hostsPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := string(raw)
|
||||
|
||||
before := strings.TrimSpace(data)
|
||||
after := ""
|
||||
|
||||
if idxBegin := strings.Index(data, begin); idxBegin != -1 {
|
||||
idxEnd := strings.Index(data, end)
|
||||
if idxEnd != -1 {
|
||||
after = strings.TrimSpace(data[idxEnd+len(end):])
|
||||
}
|
||||
before = strings.TrimSpace(data[:idxBegin])
|
||||
}
|
||||
|
||||
b := strings.Builder{}
|
||||
b.WriteString(before)
|
||||
b.WriteRune('\n')
|
||||
b.WriteString(after)
|
||||
b.WriteRune('\n')
|
||||
b.WriteRune('\n')
|
||||
|
||||
b.WriteString(begin)
|
||||
b.WriteRune('\n')
|
||||
|
||||
// Collect entries so we can sort by IP for stable output. Pad the IP
|
||||
// column to the width of the widest possible address ("255.255.255.255")
|
||||
// for readability.
|
||||
type entry struct {
|
||||
ip netip.Addr
|
||||
host string
|
||||
}
|
||||
var entries []entry
|
||||
for ip, p := range peers {
|
||||
if p.Name == "" {
|
||||
continue
|
||||
}
|
||||
entries = append(entries, entry{ip: ip, host: p.Name + "." + localDomain})
|
||||
}
|
||||
sort.Slice(entries, func(i, j int) bool {
|
||||
return entries[i].ip.Less(entries[j].ip)
|
||||
})
|
||||
|
||||
for _, e := range entries {
|
||||
b.WriteString(fmt.Sprintf("%-15s %s\n", e.ip.String(), e.host))
|
||||
}
|
||||
|
||||
b.WriteString(end)
|
||||
b.WriteRune('\n')
|
||||
|
||||
// Write to a temp file in the same directory, then rename over the
|
||||
// original so readers never observe a partial file. Preserve the
|
||||
// original's mode and ownership, since rename replaces the inode.
|
||||
tmpPath := hostsPath + ".vppn.tmp"
|
||||
if err := os.WriteFile(tmpPath, []byte(b.String()), info.Mode().Perm()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if st, ok := info.Sys().(*syscall.Stat_t); ok {
|
||||
if err := os.Chown(tmpPath, int(st.Uid), int(st.Gid)); err != nil {
|
||||
os.Remove(tmpPath)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.Rename(tmpPath, hostsPath); err != nil {
|
||||
os.Remove(tmpPath)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
167
peer/hosts_test.go
Normal file
167
peer/hosts_test.go
Normal file
@@ -0,0 +1,167 @@
|
||||
package peer
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// writeTempHosts creates a temp hosts file with the given content and returns
|
||||
// its path.
|
||||
func writeTempHosts(t *testing.T, content string) string {
|
||||
t.Helper()
|
||||
path := filepath.Join(t.TempDir(), "hosts")
|
||||
if err := os.WriteFile(path, []byte(content), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
// readManagedSection returns the lines between the begin/end markers for the
|
||||
// given localDomain, plus everything outside the section ("outside").
|
||||
func readManagedSection(t *testing.T, path, localDomain string) (inside, outside []string) {
|
||||
t.Helper()
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
begin := hostsBegin + localDomain
|
||||
end := hostsEnd + localDomain
|
||||
|
||||
inSection := false
|
||||
for _, line := range strings.Split(string(raw), "\n") {
|
||||
switch {
|
||||
case strings.HasPrefix(line, begin):
|
||||
inSection = true
|
||||
case strings.HasPrefix(line, end):
|
||||
inSection = false
|
||||
case inSection:
|
||||
if f := strings.Join(strings.Fields(line), " "); f != "" {
|
||||
inside = append(inside, f)
|
||||
}
|
||||
default:
|
||||
if f := strings.Join(strings.Fields(line), " "); f != "" {
|
||||
outside = append(outside, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
return inside, outside
|
||||
}
|
||||
|
||||
func peer(name string) *Peer {
|
||||
return &Peer{Name: name}
|
||||
}
|
||||
|
||||
func TestUpdateHosts_AddsSection(t *testing.T) {
|
||||
path := writeTempHosts(t, "127.0.0.1 localhost\n")
|
||||
|
||||
peers := map[netip.Addr]*Peer{
|
||||
netip.MustParseAddr("10.11.12.1"): peer("hub"),
|
||||
netip.MustParseAddr("10.11.12.10"): peer("laptop"),
|
||||
}
|
||||
|
||||
if err := updateHosts(path, "mynet.local", peers); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
inside, outside := readManagedSection(t, path, "mynet.local")
|
||||
|
||||
sort.Strings(inside)
|
||||
want := []string{
|
||||
"10.11.12.1 hub.mynet.local",
|
||||
"10.11.12.10 laptop.mynet.local",
|
||||
}
|
||||
if strings.Join(inside, "\n") != strings.Join(want, "\n") {
|
||||
t.Errorf("managed section = %v, want %v", inside, want)
|
||||
}
|
||||
|
||||
if !contains(outside, "127.0.0.1 localhost") {
|
||||
t.Errorf("original content lost; outside = %v", outside)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateHosts_ReplacesExistingSection(t *testing.T) {
|
||||
path := writeTempHosts(t, "127.0.0.1 localhost\n")
|
||||
|
||||
// First write.
|
||||
first := map[netip.Addr]*Peer{
|
||||
netip.MustParseAddr("10.11.12.1"): peer("hub"),
|
||||
}
|
||||
if err := updateHosts(path, "mynet.local", first); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Second write with a different set of peers.
|
||||
second := map[netip.Addr]*Peer{
|
||||
netip.MustParseAddr("10.11.12.20"): peer("phone"),
|
||||
}
|
||||
if err := updateHosts(path, "mynet.local", second); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
inside, outside := readManagedSection(t, path, "mynet.local")
|
||||
|
||||
if len(inside) != 1 || inside[0] != "10.11.12.20 phone.mynet.local" {
|
||||
t.Errorf("section not replaced; inside = %v", inside)
|
||||
}
|
||||
if contains(inside, "10.11.12.1 hub.mynet.local") {
|
||||
t.Errorf("stale entry remained; inside = %v", inside)
|
||||
}
|
||||
if !contains(outside, "127.0.0.1 localhost") {
|
||||
t.Errorf("original content lost; outside = %v", outside)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateHosts_SkipsEmptyNames(t *testing.T) {
|
||||
path := writeTempHosts(t, "127.0.0.1 localhost\n")
|
||||
|
||||
peers := map[netip.Addr]*Peer{
|
||||
netip.MustParseAddr("10.11.12.1"): peer("hub"),
|
||||
netip.MustParseAddr("10.11.12.99"): peer(""), // no name
|
||||
}
|
||||
if err := updateHosts(path, "mynet.local", peers); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
inside, _ := readManagedSection(t, path, "mynet.local")
|
||||
if len(inside) != 1 || inside[0] != "10.11.12.1 hub.mynet.local" {
|
||||
t.Errorf("expected only named peer; inside = %v", inside)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateHosts_Idempotent(t *testing.T) {
|
||||
path := writeTempHosts(t, "127.0.0.1 localhost\n")
|
||||
|
||||
peers := map[netip.Addr]*Peer{
|
||||
netip.MustParseAddr("10.11.12.1"): peer("hub"),
|
||||
}
|
||||
if err := updateHosts(path, "mynet.local", peers); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
first, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := updateHosts(path, "mynet.local", peers); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
second, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(first) != string(second) {
|
||||
t.Errorf("repeated update changed file:\nfirst:\n%s\nsecond:\n%s", first, second)
|
||||
}
|
||||
}
|
||||
|
||||
func contains(ss []string, s string) bool {
|
||||
for _, x := range ss {
|
||||
if x == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -149,6 +149,7 @@ func hubPeerFrom(pubKey wgtypes.Key, vpnIP netip.Addr, p *m.Peer) HubPeer {
|
||||
return HubPeer{
|
||||
PubKey: pubKey,
|
||||
VPNIP: vpnIP,
|
||||
Name: p.Name,
|
||||
IsRelay: p.Relay,
|
||||
IsPublic: ep4.IsValid() || ep6.IsValid(),
|
||||
EndpointV4: ep4,
|
||||
|
||||
75
peer/init.go
75
peer/init.go
@@ -20,24 +20,26 @@ import (
|
||||
// LocalState is the persisted identity for this peer, written on first run and
|
||||
// loaded on every subsequent run.
|
||||
type LocalState struct {
|
||||
PrivKey wgtypes.Key
|
||||
SignKey [64]byte // nacl/sign Ed25519 private key
|
||||
VPNIP netip.Addr
|
||||
VPNNet netip.Prefix
|
||||
WGPort uint16
|
||||
IsRelay bool
|
||||
IsPublic bool
|
||||
PrivKey wgtypes.Key
|
||||
SignKey [64]byte // nacl/sign Ed25519 private key
|
||||
VPNIP netip.Addr
|
||||
VPNNet netip.Prefix
|
||||
WGPort uint16
|
||||
IsRelay bool
|
||||
IsPublic bool
|
||||
LocalDomain string
|
||||
}
|
||||
|
||||
// localStateJSON is the on-disk representation.
|
||||
type localStateJSON struct {
|
||||
PrivKey string `json:"priv_key"` // standard base64
|
||||
SignKey string `json:"sign_key"` // standard base64
|
||||
VPNIP netip.Addr `json:"vpn_ip"`
|
||||
VPNNet netip.Prefix `json:"vpn_net"`
|
||||
WGPort uint16 `json:"wg_port"`
|
||||
IsRelay bool `json:"is_relay"`
|
||||
IsPublic bool `json:"is_public"`
|
||||
PrivKey string `json:"priv_key"` // standard base64
|
||||
SignKey string `json:"sign_key"` // standard base64
|
||||
VPNIP netip.Addr `json:"vpn_ip"`
|
||||
VPNNet netip.Prefix `json:"vpn_net"`
|
||||
WGPort uint16 `json:"wg_port"`
|
||||
IsRelay bool `json:"is_relay"`
|
||||
IsPublic bool `json:"is_public"`
|
||||
LocalDomain string `json:"local_domain"`
|
||||
}
|
||||
|
||||
// LoadOrInit loads LocalState from path, or registers with the hub and creates
|
||||
@@ -117,13 +119,14 @@ func initFromHub(hubURL, apiKey string, privKey wgtypes.Key) (LocalState, error)
|
||||
}
|
||||
|
||||
return LocalState{
|
||||
PrivKey: privKey,
|
||||
SignKey: *signPrivKey,
|
||||
VPNIP: vpnIP,
|
||||
VPNNet: vpnNet,
|
||||
WGPort: wgPort,
|
||||
IsRelay: isRelay,
|
||||
IsPublic: isPublic,
|
||||
PrivKey: privKey,
|
||||
SignKey: *signPrivKey,
|
||||
VPNIP: vpnIP,
|
||||
VPNNet: vpnNet,
|
||||
WGPort: wgPort,
|
||||
IsRelay: isRelay,
|
||||
IsPublic: isPublic,
|
||||
LocalDomain: r.LocalDomain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -150,25 +153,27 @@ func parseLocalState(data []byte) (LocalState, error) {
|
||||
var signKey [64]byte
|
||||
copy(signKey[:], signKeyBytes)
|
||||
return LocalState{
|
||||
PrivKey: key,
|
||||
SignKey: signKey,
|
||||
VPNIP: j.VPNIP,
|
||||
VPNNet: j.VPNNet,
|
||||
WGPort: j.WGPort,
|
||||
IsRelay: j.IsRelay,
|
||||
IsPublic: j.IsPublic,
|
||||
PrivKey: key,
|
||||
SignKey: signKey,
|
||||
VPNIP: j.VPNIP,
|
||||
VPNNet: j.VPNNet,
|
||||
WGPort: j.WGPort,
|
||||
IsRelay: j.IsRelay,
|
||||
IsPublic: j.IsPublic,
|
||||
LocalDomain: j.LocalDomain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func saveLocalState(path string, s LocalState) error {
|
||||
j := localStateJSON{
|
||||
PrivKey: base64.StdEncoding.EncodeToString(s.PrivKey[:]),
|
||||
SignKey: base64.StdEncoding.EncodeToString(s.SignKey[:]),
|
||||
VPNIP: s.VPNIP,
|
||||
VPNNet: s.VPNNet,
|
||||
WGPort: s.WGPort,
|
||||
IsRelay: s.IsRelay,
|
||||
IsPublic: s.IsPublic,
|
||||
PrivKey: base64.StdEncoding.EncodeToString(s.PrivKey[:]),
|
||||
SignKey: base64.StdEncoding.EncodeToString(s.SignKey[:]),
|
||||
VPNIP: s.VPNIP,
|
||||
VPNNet: s.VPNNet,
|
||||
WGPort: s.WGPort,
|
||||
IsRelay: s.IsRelay,
|
||||
IsPublic: s.IsPublic,
|
||||
LocalDomain: s.LocalDomain,
|
||||
}
|
||||
data, err := json.MarshalIndent(j, "", " ")
|
||||
if err != nil {
|
||||
|
||||
14
peer/new.go
14
peer/new.go
@@ -16,6 +16,7 @@ func New(
|
||||
state LocalState,
|
||||
hubURL, apiKey string,
|
||||
ifaceName string,
|
||||
localDomain string,
|
||||
) (*App, error) {
|
||||
|
||||
a4 := state.VPNIP.As4()
|
||||
@@ -65,12 +66,13 @@ func New(
|
||||
}
|
||||
|
||||
return &App{
|
||||
vpnIP: state.VPNIP,
|
||||
vpnNet: state.VPNNet,
|
||||
privKey: state.PrivKey,
|
||||
pubKey: state.PrivKey.PublicKey(),
|
||||
isRelay: state.IsRelay,
|
||||
isPublic: state.IsPublic,
|
||||
vpnIP: state.VPNIP,
|
||||
vpnNet: state.VPNNet,
|
||||
privKey: state.PrivKey,
|
||||
pubKey: state.PrivKey.PublicKey(),
|
||||
isRelay: state.IsRelay,
|
||||
isPublic: state.IsPublic,
|
||||
localDomain: localDomain,
|
||||
|
||||
dev: dev,
|
||||
controlConn: cc,
|
||||
|
||||
@@ -17,6 +17,7 @@ func (a *App) onAddPeer(p HubPeer) {
|
||||
peer := &Peer{
|
||||
wgPeer: wgtypes.Peer{PublicKey: p.PubKey},
|
||||
VPNIP: p.VPNIP,
|
||||
Name: p.Name,
|
||||
IsRelay: p.IsRelay,
|
||||
IsPublic: p.IsPublic,
|
||||
Endpoint4: p.EndpointV4,
|
||||
@@ -35,6 +36,7 @@ func (a *App) onAddPeer(p HubPeer) {
|
||||
|
||||
a.peersByKey[p.PubKey] = peer
|
||||
a.peersByIP[peer.VPNIP] = peer
|
||||
defer a.updateHosts()
|
||||
|
||||
if !peer.IsPublic {
|
||||
if a.isPublic {
|
||||
@@ -59,6 +61,7 @@ func (a *App) onRemovePeer(key wgtypes.Key) {
|
||||
a.devRemove(peer)
|
||||
delete(a.peersByKey, key)
|
||||
delete(a.peersByIP, peer.VPNIP)
|
||||
a.updateHosts()
|
||||
|
||||
if peer == a.relay {
|
||||
a.relay = nil
|
||||
|
||||
@@ -21,6 +21,7 @@ const (
|
||||
type Peer struct {
|
||||
wgPeer wgtypes.Peer
|
||||
VPNIP netip.Addr // VPN IP address.
|
||||
Name string // Human-readable DNS label.
|
||||
IsRelay bool // Peer is a relay.
|
||||
IsPublic bool // Peer has a public IP.
|
||||
Endpoint4 netip.AddrPort // Reported IPv4 endpoint.
|
||||
|
||||
Reference in New Issue
Block a user