package dns
import (
"errors"
"fmt"
"net/netip"
"net/url"
"strings"
"github.com/tmc/go-iroh/internal/pkarr"
"github.com/tmc/go-iroh/key"
"github.com/tmc/go-iroh/netaddr"
)
var (
ErrInvalidTXTAttr = errors .New ("invalid TXT attribute" )
ErrUnknownAttr = errors .New ("could not convert key to attr" )
ErrNumLabels = errors .New ("expected at least 2 labels" )
ErrNotIrohRecord = errors .New ("not an iroh record, expected `_iroh`" )
)
type irohAttr string
const (
attrRelay irohAttr = "relay"
attrAddr irohAttr = "addr"
attrUserData irohAttr = "user-data"
)
func parseIrohAttr(s string ) (irohAttr , bool ) {
switch irohAttr (s ) {
case attrRelay , attrAddr , attrUserData :
return irohAttr (s ), true
default :
return "" , false
}
}
type txtAttrs struct {
endpointID key .EndpointID
attrs map [irohAttr ][]string
}
func endpointIDFromTxtName(name string ) (key .EndpointID , error ) {
labels := strings .Split (name , "." )
if len (labels ) < 2 {
return key .EndpointID {}, fmt .Errorf ("%w, received %d" , ErrNumLabels , len (labels ))
}
if labels [0 ] != IrohTXTName {
return key .EndpointID {}, fmt .Errorf ("%w, got %q" , ErrNotIrohRecord , labels [0 ])
}
return key .ParseEndpointIDZ32 (labels [1 ])
}
func txtAttrsFromStrings(id key .EndpointID , values []string ) (*txtAttrs , error ) {
attrs := map [irohAttr ][]string {}
for _ , s := range values {
key , value , ok := splitAttr (s )
if !ok {
return nil , fmt .Errorf ("%w: expected key=value, received %q" , ErrInvalidTXTAttr , s )
}
attr , ok := parseIrohAttr (key )
if !ok {
return nil , fmt .Errorf ("%w: %q" , ErrUnknownAttr , key )
}
attrs [attr ] = append (attrs [attr ], value )
}
return &txtAttrs {endpointID : id , attrs : attrs }, nil
}
func splitAttr(s string ) (key , value string , ok bool ) {
parts := strings .SplitN (s , "=" , 3 )
if len (parts ) < 2 {
return "" , "" , false
}
return parts [0 ], parts [1 ], true
}
func txtAttrsFromTXTLookup(name string , values []string ) (*txtAttrs , error ) {
id , err := endpointIDFromTxtName (name )
if err != nil {
return nil , err
}
return txtAttrsFromStrings (id , values )
}
func txtAttrsFromPkarrSignedPacket(packet *SignedPacket ) (*txtAttrs , error ) {
id := packet .PublicKey ().EndpointID ()
return txtAttrsFromStrings (id , packet .TXTRecords (IrohTXTName ))
}
var attrOrder = []irohAttr {attrRelay , attrAddr , attrUserData }
func (a *txtAttrs ) toTxtStrings () []string {
var out []string
for _ , k := range attrOrder {
for _ , v := range a .attrs [k ] {
out = append (out , string (k )+"=" +v )
}
}
return out
}
func (a *txtAttrs ) toPkarrSignedPacket (secretKey key .SecretKey , ttl uint32 ) (*pkarr .SignedPacket , error ) {
return pkarr .FromTxtStrings (secretKey , IrohTXTName , a .toTxtStrings (), ttl )
}
func (e EndpointInfo ) toAttrs () *txtAttrs {
attrs := map [irohAttr ][]string {}
for _ , addr := range e .Data .addrs {
switch v := addr .(type ) {
case netaddr .RelayAddr :
attrs [attrRelay ] = append (attrs [attrRelay ], v .URL .String ())
case netaddr .IPAddr :
attrs [attrAddr ] = append (attrs [attrAddr ], v .Addr .String ())
case netaddr .CustomAddr :
attrs [attrAddr ] = append (attrs [attrAddr ], v .String ())
}
}
if e .Data .userData != nil {
attrs [attrUserData ] = append (attrs [attrUserData ], e .Data .userData .String ())
}
return &txtAttrs {endpointID : e .ID , attrs : attrs }
}
func endpointInfoFromAttrs(attrs *txtAttrs ) EndpointInfo {
var addrs []netaddr .TransportAddr
for _ , s := range attrs .attrs [attrRelay ] {
if u , err := url .Parse (s ); err == nil {
addrs = append (addrs , netaddr .RelayAddr {URL : netaddr .RelayURLFromURL (u )})
}
}
for _ , s := range attrs .attrs [attrAddr ] {
if ap , err := netip .ParseAddrPort (s ); err == nil {
addrs = append (addrs , netaddr .IPAddr {Addr : ap })
} else if ca , err := netaddr .ParseCustomAddr (s ); err == nil {
addrs = append (addrs , ca )
}
}
data := EndpointData {}
if vs := attrs .attrs [attrUserData ]; len (vs ) > 0 {
if u , err := NewUserData (vs [0 ]); err == nil {
data .SetUserData (&u )
}
}
data .AddAddrs (addrs ...)
return EndpointInfo {ID : attrs .endpointID , Data : data }
}
The pages are generated with Golds v0.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 .