package netaddrimport ()// TransportAddr is a network-level address at which an endpoint may be reached.// It is one of [RelayAddr], [IPAddr], or [CustomAddr].//// The interface is closed: only the implementations in this package satisfy it.typeTransportAddrinterface {// Network returns the transport kind: "relay", "ip", or "custom".Network() string// String renders the address in its "kind:value" form, e.g. "ip:127.0.0.1:9".String() string// Compare returns -1, 0, or +1 ordering this address against other. The // order matches the Rust reference's derived Ord on the TransportAddr enum: // by kind first (relay < ip < custom), then by value (relay URLs by their // normalized string, IP addresses numerically, custom by id then data).Compare(other TransportAddr) int isTransportAddr()}// transportKind is the kind ordinal used as the primary ordering key, matching// the Rust enum variant order: Relay(0) < Ip(1) < Custom(2).func transportKind( TransportAddr) int {switch .(type) {caseRelayAddr:return0caseIPAddr:return1caseCustomAddr:return2default:return3 }}// RelayAddr is a [TransportAddr] reachable via a relay server.typeRelayAddrstruct{ URL RelayURL }// IPAddr is a [TransportAddr] reachable at an IP socket address.typeIPAddrstruct{ Addr netip.AddrPort }// CustomAddr is a custom transport address: a freely-chosen u64 transport id// plus opaque, unvalidated address data.//// A registry of well-known transport ids is at// https://github.com/n0-computer/iroh/blob/main/TRANSPORTS.md.//// CustomAddr mirrors upstream iroh's experimental custom-transport address// surface, which upstream excludes from its stability guarantees. The Go API// below follows this module's normal compatibility policy, but the// endpoint-ticket wire encoding of a CustomAddr may change to track upstream// without a major go-iroh version bump.//// String encoding ([CustomAddr.String], [ParseCustomAddr]): "<id>_<data>" where// <id> is the transport ID as lowercase hex (no "0x", no leading zeros) and// <data> is the address bytes as lowercase hex.//// Binary encoding ([CustomAddr.MarshalBinary], [CustomAddr.UnmarshalBinary]):// 8-byte little-endian u64 ID followed by the raw data bytes (minimum 8 bytes).//// CustomAddr mirrors upstream iroh's experimental custom-transport address// surface, which upstream excludes from its stability guarantees. The Go API// below follows this module's normal compatibility policy, but the// endpoint-ticket wire encoding of a CustomAddr may change to track upstream// without a major go-iroh version bump.typeCustomAddrstruct { id uint64 data []byte}func (RelayAddr) () {}func (IPAddr) () {}func (CustomAddr) () {}func (RelayAddr) () string { return"relay" }func (IPAddr) () string { return"ip" }func (CustomAddr) () string { return"custom" }func ( RelayAddr) () string { return"relay:" + .URL.String() }func ( IPAddr) () string { return"ip:" + .Addr.String() }func ( CustomAddr) () string { return .customString() }// MarshalText implements encoding.TextMarshaler using the string encoding// described on [TransportAddr].func ( RelayAddr) () ([]byte, error) {return []byte(.String()), nil}// UnmarshalText implements encoding.TextUnmarshaler using the string encoding// described on [TransportAddr].func ( *RelayAddr) ( []byte) error { , := ParseTransportAddr(string())if != nil {return } , := .(RelayAddr)if ! {returnfmt.Errorf("transport address %q: got %T, want RelayAddr", , ) } * = returnnil}// MarshalText implements encoding.TextMarshaler using the string encoding// described on [TransportAddr].func ( IPAddr) () ([]byte, error) {return []byte(.String()), nil}// UnmarshalText implements encoding.TextUnmarshaler using the string encoding// described on [TransportAddr].func ( *IPAddr) ( []byte) error { , := ParseTransportAddr(string())if != nil {return } , := .(IPAddr)if ! {returnfmt.Errorf("transport address %q: got %T, want IPAddr", , ) } * = returnnil}// Compare orders relay addresses by their normalized URL string.func ( RelayAddr) ( TransportAddr) int {if , := .(RelayAddr); {return .URL.Compare(.URL) }returncmp.Compare(transportKind(), transportKind())}// Compare orders IP addresses numerically (by [netip.AddrPort.Compare]).func ( IPAddr) ( TransportAddr) int {if , := .(IPAddr); {return .Addr.Compare(.Addr) }returncmp.Compare(transportKind(), transportKind())}// Compare orders custom addresses by numeric transport id, then by data bytes.func ( CustomAddr) ( TransportAddr) int {if , := .(CustomAddr); {if := cmp.Compare(.id, .id); != 0 {return }returnbytes.Compare(.data, .data) }returncmp.Compare(transportKind(), transportKind())}// NewCustomAddr creates a CustomAddr from a transport ID and raw address data.// The data is copied.func ( uint64, []byte) CustomAddr {returnCustomAddr{id: , data: slices.Clone()}}// ID returns the transport ID.func ( CustomAddr) () uint64 { return .id }// Data returns the opaque address data.func ( CustomAddr) () []byte { returnslices.Clone(.data) }func ( CustomAddr) () string {returnstrconv.FormatUint(.id, 16) + "_" + hex.EncodeToString(.data)}// CustomAddr parse/encode errors.var (ErrCustomAddrMissingSeparator = errors.New("missing '_' separator")ErrCustomAddrInvalidID = errors.New("invalid ID")ErrCustomAddrInvalidData = errors.New("invalid data")ErrCustomAddrTooShort = errors.New("data too short"))// ParseCustomAddr parses a CustomAddr from its "<id>_<data>" string form.// It also accepts the "custom:" prefix used by [ParseTransportAddr].func ( string) (CustomAddr, error) { = strings.TrimPrefix(, "custom:") , , := strings.Cut(, "_")if ! {returnCustomAddr{}, ErrCustomAddrMissingSeparator } , := strconv.ParseUint(, 16, 64)if != nil {returnCustomAddr{}, ErrCustomAddrInvalidID } , := hex.DecodeString()if != nil {returnCustomAddr{}, ErrCustomAddrInvalidData }returnNewCustomAddr(, ), nil}// MarshalText implements encoding.TextMarshaler using the string encoding// described on [CustomAddr].func ( CustomAddr) () ([]byte, error) {return []byte(.String()), nil}// UnmarshalText implements encoding.TextUnmarshaler using the string encoding// described on [CustomAddr].func ( *CustomAddr) ( []byte) error { , := ParseCustomAddr(string())if != nil {return } * = returnnil}// MarshalBinary implements encoding.BinaryMarshaler using the binary encoding// described on [CustomAddr].func ( CustomAddr) () ([]byte, error) { := make([]byte, 8+len(.data))binary.LittleEndian.PutUint64([:8], .id)copy([8:], .data)return , nil}// UnmarshalBinary implements encoding.BinaryUnmarshaler using the binary// encoding described on [CustomAddr].func ( *CustomAddr) ( []byte) error {iflen() < 8 {returnErrCustomAddrTooShort } .id = binary.LittleEndian.Uint64([:8]) .data = slices.Clone([8:])returnnil}// ParseTransportAddr parses a TransportAddr from its "kind:value" string form.func ( string) (TransportAddr, error) { , , := strings.Cut(, ":")if ! {returnParseCustomAddr() }switch {case"relay": , := ParseRelayURL()if != nil {returnnil, }returnRelayAddr{URL: }, nilcase"ip": , := netip.ParseAddrPort()if != nil {returnnil, fmt.Errorf("transport address %q: %w", , ) }returnIPAddr{Addr: }, nilcase"custom":returnParseCustomAddr()default:returnnil, fmt.Errorf("transport address %q: unknown kind %q", , ) }}// EndpointAddr combines an endpoint's [key.EndpointID] with the network-level// addresses at which it may be reached.//// To establish a connection both the key.EndpointID and at least one path (a relay// URL or a direct IP address) are needed; an EndpointAddr with no addresses is// still usable together with an address-lookup service.typeEndpointAddrstruct {// ID is the endpoint's identifier. ID key.EndpointID// addrs is the sorted, deduplicated set of transport addresses. addrs []TransportAddr}// NewEndpointAddr creates an EndpointAddr with the given id and transport// addresses. Addresses are deduplicated and sorted.func ( key.EndpointID, ...TransportAddr) EndpointAddr { := EndpointAddr{ID: }return .WithAddrs(...)}// WithRelayURL returns a copy of a with the given relay URL added.func ( EndpointAddr) ( RelayURL) EndpointAddr {return .WithAddrs(RelayAddr{URL: })}// WithIP returns a copy of a with the given IP address added.func ( EndpointAddr) ( netip.AddrPort) EndpointAddr {return .WithAddrs(IPAddr{Addr: })}// WithAddrs returns a copy of a with the given addresses added. The result's// address set is sorted and deduplicated.func ( EndpointAddr) ( ...TransportAddr) EndpointAddr { := append(slices.Clone(.addrs), ...) = sortDedupAddrs()returnEndpointAddr{ID: .ID, addrs: }}// Addrs returns the sorted, deduplicated transport addresses.func ( EndpointAddr) () []TransportAddr { returnslices.Clone(.addrs) }// IsEmpty reports whether only the key.EndpointID is present.func ( EndpointAddr) () bool { returnlen(.addrs) == 0 }// String returns a diagnostic string for a.func ( EndpointAddr) () string {varstrings.Builder .WriteString("EndpointAddr{id:") .WriteString(.ID.String()) .WriteString(", addrs:[")for , := range .addrs {if > 0 { .WriteString(", ") } .WriteString(.String()) } .WriteString("]}")return .String()}// IPAddrs returns the IP socket addresses of this endpoint.func ( EndpointAddr) () []netip.AddrPort {var []netip.AddrPortfor , := range .addrs {if , := .(IPAddr); { = append(, .Addr) } }return}// RelayURLs returns the relay URLs of this endpoint. In practice this is// expected to be zero or one home relay.func ( EndpointAddr) () []RelayURL {var []RelayURLfor , := range .addrs {if , := .(RelayAddr); { = append(, .URL) } }return}func sortDedupAddrs( []TransportAddr) []TransportAddr {slices.SortFunc(, TransportAddr.Compare)returnslices.CompactFunc(, func(, TransportAddr) bool {return .Compare() == 0 })}
The pages are generated with Goldsv0.8.4. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.