package types import ( "database/sql/driver" "encoding/json" "errors" "fmt" "net/netip" "tailscale.com/tailcfg" ) var ErrCannotParsePrefix = errors.New("cannot parse prefix") // This is a "wrapper" type around tailscales // Hostinfo to allow us to add database "serialization" // methods. This allows us to use a typed values throughout // the code and not have to marshal/unmarshal and error // check all over the code. type HostInfo tailcfg.Hostinfo func (hi *HostInfo) Scan(destination interface{}) error { switch value := destination.(type) { case []byte: return json.Unmarshal(value, hi) case string: return json.Unmarshal([]byte(value), hi) default: return fmt.Errorf("%w: unexpected data type %T", ErrNodeAddressesInvalid, destination) } } // Value return json value, implement driver.Valuer interface. func (hi HostInfo) Value() (driver.Value, error) { bytes, err := json.Marshal(hi) return string(bytes), err } type IPPrefix netip.Prefix func (i *IPPrefix) Scan(destination interface{}) error { switch value := destination.(type) { case string: prefix, err := netip.ParsePrefix(value) if err != nil { return err } *i = IPPrefix(prefix) return nil default: return fmt.Errorf("%w: unexpected data type %T", ErrCannotParsePrefix, destination) } } // Value return json value, implement driver.Valuer interface. func (i IPPrefix) Value() (driver.Value, error) { prefixStr := netip.Prefix(i).String() return prefixStr, nil } type IPPrefixes []netip.Prefix func (i *IPPrefixes) Scan(destination interface{}) error { switch value := destination.(type) { case []byte: return json.Unmarshal(value, i) case string: return json.Unmarshal([]byte(value), i) default: return fmt.Errorf("%w: unexpected data type %T", ErrNodeAddressesInvalid, destination) } } // Value return json value, implement driver.Valuer interface. func (i IPPrefixes) Value() (driver.Value, error) { bytes, err := json.Marshal(i) return string(bytes), err } type StringList []string func (i *StringList) Scan(destination interface{}) error { switch value := destination.(type) { case []byte: return json.Unmarshal(value, i) case string: return json.Unmarshal([]byte(value), i) default: return fmt.Errorf("%w: unexpected data type %T", ErrNodeAddressesInvalid, destination) } } // Value return json value, implement driver.Valuer interface. func (i StringList) Value() (driver.Value, error) { bytes, err := json.Marshal(i) return string(bytes), err } type StateUpdateType int const ( StateFullUpdate StateUpdateType = iota StatePeerChanged StatePeerRemoved StateDERPUpdated ) // StateUpdate is an internal message containing information about // a state change that has happened to the network. type StateUpdate struct { // The type of update Type StateUpdateType // Changed must be set when Type is StatePeerChanged and // contain the Node IDs of nodes that have changed. Changed Nodes // Removed must be set when Type is StatePeerRemoved and // contain a list of the nodes that has been removed from // the network. Removed []tailcfg.NodeID // DERPMap must be set when Type is StateDERPUpdated and // contain the new DERP Map. DERPMap tailcfg.DERPMap }