package resolver import ( "context" "fmt" "github.com/nspcc-dev/neo-go/pkg/rpc/client" neofsclient "github.com/nspcc-dev/neofs-sdk-go/client" apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" "github.com/nspcc-dev/neofs-sdk-go/netmap" "github.com/nspcc-dev/neofs-sdk-go/pool" "github.com/nspcc-dev/neofs-sdk-go/resolver" ) const ( NNSResolver = "nns" DNSResolver = "dns" networkSystemDNSParam = "SystemDNS" ) type Config struct { Pool pool.Pool RPC *client.Client } type BucketResolver struct { Name string resolve func(context.Context, string) (*cid.ID, error) next *BucketResolver } func (r *BucketResolver) Resolve(ctx context.Context, name string) (*cid.ID, error) { cnrID, err := r.resolve(ctx, name) if err != nil { if r.next != nil { return r.next.Resolve(ctx, name) } return nil, err } return cnrID, err } func NewResolver(order []string, cfg *Config) (*BucketResolver, error) { if len(order) == 0 { return nil, fmt.Errorf("resolving order must not be empty") } bucketResolver, err := newResolver(order[len(order)-1], cfg, nil) if err != nil { return nil, err } for i := len(order) - 2; i >= 0; i-- { resolverName := order[i] next := bucketResolver bucketResolver, err = newResolver(resolverName, cfg, next) if err != nil { return nil, err } } return bucketResolver, nil } func newResolver(name string, cfg *Config, next *BucketResolver) (*BucketResolver, error) { switch name { case DNSResolver: return NewDNSResolver(cfg.Pool, next) case NNSResolver: return NewNNSResolver(cfg.RPC, next) default: return nil, fmt.Errorf("unknown resolver: %s", name) } } func NewDNSResolver(p pool.Pool, next *BucketResolver) (*BucketResolver, error) { if p == nil { return nil, fmt.Errorf("pool must not be nil for DNS resolver") } resolveFunc := func(ctx context.Context, name string) (*cid.ID, error) { conn, _, err := p.Connection() if err != nil { return nil, err } networkInfoRes, err := conn.NetworkInfo(ctx, neofsclient.PrmNetworkInfo{}) if err == nil { err = apistatus.ErrFromStatus(networkInfoRes.Status()) } if err != nil { return nil, err } networkInfo := networkInfoRes.Info() var domain string networkInfo.NetworkConfig().IterateParameters(func(parameter *netmap.NetworkParameter) bool { if string(parameter.Key()) == networkSystemDNSParam { domain = string(parameter.Value()) return true } return false }) if domain == "" { return nil, fmt.Errorf("couldn't resolve container '%s': not found", name) } domain = name + "." + domain cnrID, err := resolver.ResolveContainerDomainName(domain) if err != nil { return nil, fmt.Errorf("couldn't resolve container '%s' as '%s': %w", name, domain, err) } return cnrID, nil } return &BucketResolver{ Name: DNSResolver, resolve: resolveFunc, next: next, }, nil } func NewNNSResolver(rpc *client.Client, next *BucketResolver) (*BucketResolver, error) { if rpc == nil { return nil, fmt.Errorf("rpc client must not be nil for NNS resolver") } nnsRPCResolver, err := resolver.NewNNSResolver(rpc) if err != nil { return nil, err } resolveFunc := func(_ context.Context, name string) (*cid.ID, error) { cnrID, err := nnsRPCResolver.ResolveContainerName(name) if err != nil { return nil, fmt.Errorf("couldn't resolve container '%s': %w", name, err) } return cnrID, nil } return &BucketResolver{ Name: NNSResolver, resolve: resolveFunc, next: next, }, nil }