package network import ( "crypto/ecdsa" "encoding/base64" "errors" "fmt" "net" "strings" "koti.casa/numenor-labs/dsfx/pkg/crypto/identity" ) var ( ErrInvalidFormat = errors.New("address must be in the format 'dsfx://:#'") ) // Addr is a wrapper around net.Addr that adds a public key to the address. This // means that all connections between two nodes can be verified by their public // keys. type Addr struct { network string ip net.IP port int publicKey *ecdsa.PublicKey } // NewAddr creates a new Addr. func NewAddr(ip net.IP, port int, publicKey *ecdsa.PublicKey) *Addr { network := "dsfx" return &Addr{network, ip, port, publicKey} } func ParseAddr(addrRaw string) (*Addr, error) { addrWoNet := strings.ReplaceAll(addrRaw, "dsfx://", "") parts := strings.Split(addrWoNet, "#") if len(parts) != 2 { return nil, ErrInvalidFormat } addr := parts[0] publicKeyBase64 := parts[1] parts = strings.Split(addr, ":") if len(parts) != 2 { return nil, ErrInvalidFormat } ip := net.ParseIP(parts[0]) if ip == nil { return nil, ErrInvalidFormat } port, err := net.LookupPort("tcp", parts[1]) if err != nil { return nil, ErrInvalidFormat } publicKeyBytes, err := base64.StdEncoding.DecodeString(publicKeyBase64) if err != nil { return nil, ErrInvalidFormat } publicKey, err := identity.ImportPublicKey(publicKeyBytes) if err != nil { return nil, ErrInvalidFormat } network := "dsfx" return &Addr{network, ip, port, publicKey}, nil } // FromTCPAddr creates a new Addr from a net.TCPAddr. func FromTCPAddr(addr *net.TCPAddr, publicKey *ecdsa.PublicKey) *Addr { return &Addr{ network: "dsfx", ip: addr.IP, port: addr.Port, publicKey: publicKey, } } // Network implements net.Addr. func (a *Addr) Network() string { return a.network } // String implements net.Addr. func (a *Addr) String() string { exported, _ := identity.ExportPublicKey(a.publicKey) exportedBase64 := base64.StdEncoding.EncodeToString(exported) return fmt.Sprintf("%s://%s:%d#%s", a.network, a.ip, a.port, exportedBase64) } // IP returns the IP address of the Addr. func (a *Addr) IP() net.IP { return a.ip } // Port returns the port of the Addr. func (a *Addr) Port() int { return a.port } // PublicKey returns the public key of the Addr. func (a *Addr) PublicKey() *ecdsa.PublicKey { return a.publicKey } // TCPAddr returns a net.TCPAddr for the Addr. func (a *Addr) TCPAddr() *net.TCPAddr { return &net.TCPAddr{ IP: a.ip, Port: a.port, } }