134 lines
3.3 KiB
Go
134 lines
3.3 KiB
Go
// The package `m` contains models shared between the hub and peer programs.
|
|
package m
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/netip"
|
|
|
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
|
)
|
|
|
|
type PeerInitArgs struct {
|
|
WGPubKey []byte
|
|
SignPubKey []byte
|
|
}
|
|
|
|
type PeerInitResp struct {
|
|
PeerIP byte
|
|
Network []byte
|
|
LocalDomain string
|
|
NetworkState NetworkState
|
|
}
|
|
|
|
// Peer is the network membership record for a single peer, exchanged between
|
|
// the hub and peers. Addr4/Addr6 are the peer's public endpoint addresses (zero
|
|
// if it has none); Port is its WireGuard listen port, meaningful even for a
|
|
// non-public peer (it is the peer's own bind/beacon port).
|
|
type Peer struct {
|
|
PeerIP byte
|
|
Name string
|
|
Addr4 netip.Addr // zero if none
|
|
Addr6 netip.Addr // zero if none
|
|
Port uint16
|
|
Relay bool
|
|
WGPubKey wgtypes.Key
|
|
SignPubKey [32]byte
|
|
}
|
|
|
|
// IsPublic reports whether the peer advertises at least one reachable endpoint.
|
|
func (p Peer) IsPublic() bool {
|
|
return p.Addr4.IsValid() || p.Addr6.IsValid()
|
|
}
|
|
|
|
// Endpoint4 returns the IPv4 endpoint (addr+port), or the zero AddrPort if the
|
|
// peer has no IPv4 address.
|
|
func (p Peer) Endpoint4() netip.AddrPort {
|
|
if !p.Addr4.IsValid() {
|
|
return netip.AddrPort{}
|
|
}
|
|
return netip.AddrPortFrom(p.Addr4, p.Port)
|
|
}
|
|
|
|
// Endpoint6 returns the IPv6 endpoint (addr+port), or the zero AddrPort if the
|
|
// peer has no IPv6 address.
|
|
func (p Peer) Endpoint6() netip.AddrPort {
|
|
if !p.Addr6.IsValid() {
|
|
return netip.AddrPort{}
|
|
}
|
|
return netip.AddrPortFrom(p.Addr6, p.Port)
|
|
}
|
|
|
|
// PreferredEndpoint returns the IPv4 endpoint if present, else IPv6.
|
|
func (p Peer) PreferredEndpoint() netip.AddrPort {
|
|
if ep := p.Endpoint4(); ep.IsValid() {
|
|
return ep
|
|
}
|
|
return p.Endpoint6()
|
|
}
|
|
|
|
// peerJSON is the wire representation. netip.Addr fields round-trip as text
|
|
// strings automatically; only the fixed-size key arrays need base64 (otherwise
|
|
// encoding/json would emit them as arrays of numbers).
|
|
type peerJSON struct {
|
|
PeerIP byte
|
|
Name string
|
|
Addr4 netip.Addr
|
|
Addr6 netip.Addr
|
|
Port uint16
|
|
Relay bool
|
|
WGPubKey string
|
|
SignPubKey string
|
|
}
|
|
|
|
func (p Peer) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(peerJSON{
|
|
PeerIP: p.PeerIP,
|
|
Name: p.Name,
|
|
Addr4: p.Addr4,
|
|
Addr6: p.Addr6,
|
|
Port: p.Port,
|
|
Relay: p.Relay,
|
|
WGPubKey: base64.StdEncoding.EncodeToString(p.WGPubKey[:]),
|
|
SignPubKey: base64.StdEncoding.EncodeToString(p.SignPubKey[:]),
|
|
})
|
|
}
|
|
|
|
func (p *Peer) UnmarshalJSON(data []byte) error {
|
|
var j peerJSON
|
|
if err := json.Unmarshal(data, &j); err != nil {
|
|
return err
|
|
}
|
|
wg, err := base64.StdEncoding.DecodeString(j.WGPubKey)
|
|
if err != nil {
|
|
return fmt.Errorf("decode WGPubKey: %w", err)
|
|
}
|
|
key, err := wgtypes.NewKey(wg)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid WGPubKey: %w", err)
|
|
}
|
|
sign, err := base64.StdEncoding.DecodeString(j.SignPubKey)
|
|
if err != nil {
|
|
return fmt.Errorf("decode SignPubKey: %w", err)
|
|
}
|
|
if len(sign) != 32 {
|
|
return fmt.Errorf("invalid SignPubKey length: %d", len(sign))
|
|
}
|
|
*p = Peer{
|
|
PeerIP: j.PeerIP,
|
|
Name: j.Name,
|
|
Addr4: j.Addr4,
|
|
Addr6: j.Addr6,
|
|
Port: j.Port,
|
|
Relay: j.Relay,
|
|
WGPubKey: key,
|
|
SignPubKey: [32]byte(sign),
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type NetworkState struct {
|
|
Peers []Peer
|
|
}
|