Compare commits
55 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b9a6c1bba | ||
|
|
d964b98e0c | ||
|
|
93f22e6942 | ||
|
|
c41756c2df | ||
|
|
1c91ff091f | ||
|
|
8b6fcf0946 | ||
|
|
3197355258 | ||
|
|
9aab7dde69 | ||
|
|
95663717f4 | ||
|
|
833db1974d | ||
|
|
3efa3308a5 | ||
|
|
d0fe2199c4 | ||
|
|
b7f6912ef0 | ||
|
|
58509bba4d | ||
|
|
b72191f03b | ||
|
|
89b71c9391 | ||
|
|
2a76e5ef0c | ||
|
|
612cf06c06 | ||
|
|
d1e884f2e2 | ||
|
|
d0b3da9494 | ||
|
|
0be2f529a0 | ||
|
|
ff0ab358df | ||
|
|
04c3bd5997 | ||
|
|
da91954490 | ||
|
|
d10a9f9073 | ||
|
|
25d4b85e56 | ||
|
|
5968a4cc50 | ||
|
|
b4b1e5c930 | ||
|
|
b417373f5b | ||
|
|
1810959b89 | ||
|
|
fcdc2c54f9 | ||
|
|
6a69356d51 | ||
|
|
fb7236bf12 | ||
|
|
56be52f7bb | ||
|
|
a78523b085 | ||
|
|
32543b2e12 | ||
|
|
08e0d201e1 | ||
|
|
f0a6c12443 | ||
|
|
5fd82dd342 | ||
|
|
b6f9d50393 | ||
|
|
bcf714d54c | ||
|
|
8807fee752 | ||
|
|
50b2394fdd | ||
|
|
added0ece8 | ||
|
|
3af5018f35 | ||
|
|
edb852ce23 | ||
|
|
f1c0f9cf2b | ||
|
|
33993b9780 | ||
|
|
6e6b2b63c2 | ||
|
|
958a799c45 | ||
|
|
8857ed4e51 | ||
|
|
f6afe97b35 | ||
|
|
5d002f8192 | ||
|
|
5b223e850c | ||
|
|
9b077efe4e |
11 changed files with 2321 additions and 298 deletions
144
action.go
144
action.go
|
|
@ -25,10 +25,19 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
actionArgBind = "bind"
|
actionArgBind = "bind"
|
||||||
actionArgIP = "ip"
|
actionArgBindAddress = "bindAddress"
|
||||||
actionArgName = "name"
|
actionArgBindAddressSnake = "bind_address"
|
||||||
actionArgPort = "port"
|
actionArgIP = "ip"
|
||||||
|
actionArgAddress = "address"
|
||||||
|
actionArgName = "name"
|
||||||
|
actionArgHost = "host"
|
||||||
|
actionArgHostName = "hostname"
|
||||||
|
actionArgPort = "port"
|
||||||
|
actionArgDNSPort = "dnsPort"
|
||||||
|
actionArgDNSPortSnake = "dns_port"
|
||||||
|
actionArgHealthPort = "health_port"
|
||||||
|
actionArgHealthPortCamel = "healthPort"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ActionDefinition struct {
|
type ActionDefinition struct {
|
||||||
|
|
@ -40,7 +49,7 @@ type ActionDefinition struct {
|
||||||
// ActionRegistrar publishes DNS actions into another Core surface.
|
// ActionRegistrar publishes DNS actions into another Core surface.
|
||||||
//
|
//
|
||||||
// registrar.RegisterAction(ActionResolve, func(values map[string]any) (any, bool, error) {
|
// registrar.RegisterAction(ActionResolve, func(values map[string]any) (any, bool, error) {
|
||||||
// return service.ResolveAddress("gateway.charon.lthn")
|
// return service.ResolveAddresses("gateway.charon.lthn")
|
||||||
// })
|
// })
|
||||||
type ActionRegistrar interface {
|
type ActionRegistrar interface {
|
||||||
RegisterAction(name string, invoke func(map[string]any) (any, bool, error))
|
RegisterAction(name string, invoke func(map[string]any) (any, bool, error))
|
||||||
|
|
@ -75,6 +84,7 @@ type ActionCaller interface {
|
||||||
// for _, definition := range definitions {
|
// for _, definition := range definitions {
|
||||||
// fmt.Println(definition.Name)
|
// fmt.Println(definition.Name)
|
||||||
// }
|
// }
|
||||||
|
// // dns.resolve, dns.resolve.txt, dns.resolve.all, dns.reverse, dns.serve, dns.health, dns.discover
|
||||||
func (service *Service) ActionDefinitions() []ActionDefinition {
|
func (service *Service) ActionDefinitions() []ActionDefinition {
|
||||||
return []ActionDefinition{
|
return []ActionDefinition{
|
||||||
{
|
{
|
||||||
|
|
@ -137,13 +147,13 @@ func (service *Service) ActionDefinitions() []ActionDefinition {
|
||||||
if err := service.DiscoverAliases(context.Background()); err != nil {
|
if err := service.DiscoverAliases(context.Background()); err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
return service.Health(), true, nil
|
return nil, true, nil
|
||||||
},
|
},
|
||||||
InvokeContext: func(ctx context.Context, _ map[string]any) (any, bool, error) {
|
InvokeContext: func(ctx context.Context, _ map[string]any) (any, bool, error) {
|
||||||
if err := service.DiscoverAliases(ctx); err != nil {
|
if err := service.DiscoverAliases(ctx); err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
return service.Health(), true, nil
|
return nil, true, nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -166,6 +176,7 @@ func (service *Service) ActionNames() []string {
|
||||||
//
|
//
|
||||||
// service.RegisterActions(registrar)
|
// service.RegisterActions(registrar)
|
||||||
// // registrar.RegisterAction("dns.health", ...)
|
// // registrar.RegisterAction("dns.health", ...)
|
||||||
|
// // registrar.RegisterActionContext("dns.discover", func(ctx context.Context, values map[string]any) (any, bool, error) { ... })
|
||||||
func (service *Service) RegisterActions(registrar ActionRegistrar) {
|
func (service *Service) RegisterActions(registrar ActionRegistrar) {
|
||||||
if registrar == nil {
|
if registrar == nil {
|
||||||
return
|
return
|
||||||
|
|
@ -191,6 +202,8 @@ func (service *Service) RegisterActions(registrar ActionRegistrar) {
|
||||||
|
|
||||||
// NewServiceWithRegistrar builds a DNS service and registers its actions in one step.
|
// NewServiceWithRegistrar builds a DNS service and registers its actions in one step.
|
||||||
//
|
//
|
||||||
|
// Deprecated: use NewDNSServiceWithRegistrar for a more explicit constructor name.
|
||||||
|
//
|
||||||
// service := dns.NewServiceWithRegistrar(dns.ServiceConfiguration{}, registrar)
|
// service := dns.NewServiceWithRegistrar(dns.ServiceConfiguration{}, registrar)
|
||||||
// // registrar now exposes dns.resolve, dns.resolve.txt, dns.resolve.all, dns.reverse, dns.serve, dns.health, dns.discover
|
// // registrar now exposes dns.resolve, dns.resolve.txt, dns.resolve.all, dns.reverse, dns.serve, dns.health, dns.discover
|
||||||
func NewServiceWithRegistrar(options ServiceOptions, registrar ActionRegistrar) *Service {
|
func NewServiceWithRegistrar(options ServiceOptions, registrar ActionRegistrar) *Service {
|
||||||
|
|
@ -200,6 +213,14 @@ func NewServiceWithRegistrar(options ServiceOptions, registrar ActionRegistrar)
|
||||||
return NewService(options)
|
return NewService(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewDNSServiceWithRegistrar builds a DNS service and registers its actions in one step.
|
||||||
|
//
|
||||||
|
// service := dns.NewDNSServiceWithRegistrar(dns.ServiceConfiguration{}, registrar)
|
||||||
|
// // registrar now exposes dns.resolve, dns.resolve.txt, dns.resolve.all, dns.reverse, dns.serve, dns.health, dns.discover
|
||||||
|
func NewDNSServiceWithRegistrar(options ServiceOptions, registrar ActionRegistrar) *Service {
|
||||||
|
return NewServiceWithRegistrar(options, registrar)
|
||||||
|
}
|
||||||
|
|
||||||
// HandleAction executes a DNS action by name.
|
// HandleAction executes a DNS action by name.
|
||||||
//
|
//
|
||||||
// payload, ok, err := service.HandleAction(ActionResolve, map[string]any{
|
// payload, ok, err := service.HandleAction(ActionResolve, map[string]any{
|
||||||
|
|
@ -231,11 +252,11 @@ func (service *Service) HandleActionContext(ctx context.Context, name string, va
|
||||||
|
|
||||||
func (service *Service) handleResolveAddress(ctx context.Context, values map[string]any) (any, bool, error) {
|
func (service *Service) handleResolveAddress(ctx context.Context, values map[string]any) (any, bool, error) {
|
||||||
_ = ctx
|
_ = ctx
|
||||||
host, err := stringActionValue(values, actionArgName)
|
host, err := stringActionValueFromKeys(values, actionArgName, actionArgHost, actionArgHostName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
result, ok := service.ResolveAddress(host)
|
result, ok := service.ResolveAddresses(host)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, false, nil
|
return nil, false, nil
|
||||||
}
|
}
|
||||||
|
|
@ -244,7 +265,7 @@ func (service *Service) handleResolveAddress(ctx context.Context, values map[str
|
||||||
|
|
||||||
func (service *Service) handleResolveTXTRecords(ctx context.Context, values map[string]any) (any, bool, error) {
|
func (service *Service) handleResolveTXTRecords(ctx context.Context, values map[string]any) (any, bool, error) {
|
||||||
_ = ctx
|
_ = ctx
|
||||||
host, err := stringActionValue(values, actionArgName)
|
host, err := stringActionValueFromKeys(values, actionArgName, actionArgHost, actionArgHostName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
|
@ -257,7 +278,7 @@ func (service *Service) handleResolveTXTRecords(ctx context.Context, values map[
|
||||||
|
|
||||||
func (service *Service) handleResolveAll(ctx context.Context, values map[string]any) (any, bool, error) {
|
func (service *Service) handleResolveAll(ctx context.Context, values map[string]any) (any, bool, error) {
|
||||||
_ = ctx
|
_ = ctx
|
||||||
host, err := stringActionValue(values, actionArgName)
|
host, err := stringActionValueFromKeys(values, actionArgName, actionArgHost, actionArgHostName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
|
@ -270,7 +291,7 @@ func (service *Service) handleResolveAll(ctx context.Context, values map[string]
|
||||||
|
|
||||||
func (service *Service) handleReverseLookup(ctx context.Context, values map[string]any) (any, bool, error) {
|
func (service *Service) handleReverseLookup(ctx context.Context, values map[string]any) (any, bool, error) {
|
||||||
_ = ctx
|
_ = ctx
|
||||||
ip, err := stringActionValue(values, actionArgIP)
|
ip, err := stringActionValueFromKeys(values, actionArgIP, actionArgAddress, actionArgName, actionArgHost, actionArgHostName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
|
@ -283,17 +304,37 @@ func (service *Service) handleReverseLookup(ctx context.Context, values map[stri
|
||||||
|
|
||||||
func (service *Service) handleServe(ctx context.Context, values map[string]any) (any, bool, error) {
|
func (service *Service) handleServe(ctx context.Context, values map[string]any) (any, bool, error) {
|
||||||
_ = ctx
|
_ = ctx
|
||||||
bind, err := stringActionValueOptional(values, actionArgBind)
|
bind, _, err := stringActionValueOptionalFromKeys(values, actionArgBind, actionArgBindAddress, actionArgBindAddressSnake)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
port, portProvided, err := intActionValueOptional(values, actionArgPort)
|
port, portProvided, err := intActionValueOptionalFromKeys(values, actionArgPort, actionArgDNSPort, actionArgDNSPortSnake)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
if !portProvided {
|
if !portProvided {
|
||||||
port = service.ResolveDNSPort()
|
port = service.ResolveDNSPort()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
healthPort, healthPortProvided, err := intActionValueOptionalFromKeys(values, actionArgHealthPort, actionArgHealthPortCamel)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
if !healthPortProvided && service.httpPort > 0 {
|
||||||
|
runtime, err := service.ServeAll(bind, port, service.httpPort)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
return runtime, true, nil
|
||||||
|
}
|
||||||
|
if healthPortProvided {
|
||||||
|
runtime, err := service.ServeAll(bind, port, healthPort)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
return runtime, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
result, err := service.Serve(bind, port)
|
result, err := service.Serve(bind, port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
|
|
@ -319,6 +360,46 @@ func stringActionValue(values map[string]any, key string) (string, error) {
|
||||||
return "", errActionMissingValue
|
return "", errActionMissingValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func lookupActionValue(values map[string]any, keys ...string) (any, string, bool) {
|
||||||
|
if values == nil {
|
||||||
|
return nil, "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range keys {
|
||||||
|
if value, exists := values[key]; exists {
|
||||||
|
return value, key, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range keys {
|
||||||
|
for alias, value := range values {
|
||||||
|
if strings.EqualFold(alias, key) {
|
||||||
|
return value, alias, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringActionValueFromKeys(values map[string]any, keys ...string) (string, error) {
|
||||||
|
value, key, found := lookupActionValue(values, keys...)
|
||||||
|
if !found {
|
||||||
|
return "", errActionMissingValue
|
||||||
|
}
|
||||||
|
text, ok := value.(string)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("%w: %s", errActionMissingValue, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
text = strings.TrimSpace(text)
|
||||||
|
if text == "" {
|
||||||
|
return "", errActionMissingValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return text, nil
|
||||||
|
}
|
||||||
|
|
||||||
func stringActionValueOptional(values map[string]any, key string) (string, error) {
|
func stringActionValueOptional(values map[string]any, key string) (string, error) {
|
||||||
if values == nil {
|
if values == nil {
|
||||||
return "", nil
|
return "", nil
|
||||||
|
|
@ -334,6 +415,18 @@ func stringActionValueOptional(values map[string]any, key string) (string, error
|
||||||
return strings.TrimSpace(value), nil
|
return strings.TrimSpace(value), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func stringActionValueOptionalFromKeys(values map[string]any, keys ...string) (string, bool, error) {
|
||||||
|
value, key, found := lookupActionValue(values, keys...)
|
||||||
|
if !found {
|
||||||
|
return "", false, nil
|
||||||
|
}
|
||||||
|
text, ok := value.(string)
|
||||||
|
if !ok {
|
||||||
|
return "", false, fmt.Errorf("%w: %s", errActionMissingValue, key)
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(text), true, nil
|
||||||
|
}
|
||||||
|
|
||||||
func intActionValue(values map[string]any, key string) (int, error) {
|
func intActionValue(values map[string]any, key string) (int, error) {
|
||||||
if values == nil {
|
if values == nil {
|
||||||
return 0, errActionMissingValue
|
return 0, errActionMissingValue
|
||||||
|
|
@ -420,3 +513,26 @@ func intActionValueOptional(values map[string]any, key string) (int, bool, error
|
||||||
}
|
}
|
||||||
return value, true, nil
|
return value, true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func intActionValueOptionalFromKeys(values map[string]any, keys ...string) (int, bool, error) {
|
||||||
|
for _, key := range keys {
|
||||||
|
value, valueProvided, err := intActionValueOptional(values, key)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false, err
|
||||||
|
}
|
||||||
|
if valueProvided {
|
||||||
|
return value, true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value, key, found := lookupActionValue(values, keys...)
|
||||||
|
if !found {
|
||||||
|
return 0, false, nil
|
||||||
|
}
|
||||||
|
intValue, err := intActionValue(map[string]any{key: value}, key)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return intValue, true, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
5
go.mod
5
go.mod
|
|
@ -2,7 +2,10 @@ module dappco.re/go/dns
|
||||||
|
|
||||||
go 1.22
|
go 1.22
|
||||||
|
|
||||||
require github.com/miekg/dns v1.1.62
|
require (
|
||||||
|
github.com/miekg/dns v1.1.62
|
||||||
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
golang.org/x/mod v0.18.0 // indirect
|
golang.org/x/mod v0.18.0 // indirect
|
||||||
|
|
|
||||||
2
go.sum
2
go.sum
|
|
@ -1,5 +1,7 @@
|
||||||
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
|
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
|
||||||
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
|
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
|
||||||
|
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||||
|
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||||
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
|
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
|
||||||
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||||
|
|
|
||||||
166
hsd.go
166
hsd.go
|
|
@ -28,6 +28,20 @@ type HSDClientOptions struct {
|
||||||
HTTPClient *http.Client
|
HTTPClient *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HSDClientConfiguration is the explicit long-form alias for HSDClientOptions.
|
||||||
|
//
|
||||||
|
// client := dns.NewHSDClientFromConfiguration(dns.HSDClientConfiguration{
|
||||||
|
// URL: "http://127.0.0.1:14037",
|
||||||
|
// Username: "user",
|
||||||
|
// Password: "pass",
|
||||||
|
// })
|
||||||
|
type HSDClientConfiguration = HSDClientOptions
|
||||||
|
|
||||||
|
// HSDClientConfig is kept for compatibility with older call sites.
|
||||||
|
//
|
||||||
|
// Deprecated: use HSDClientConfiguration instead.
|
||||||
|
type HSDClientConfig = HSDClientOptions
|
||||||
|
|
||||||
type HSDClient struct {
|
type HSDClient struct {
|
||||||
baseURL string
|
baseURL string
|
||||||
username string
|
username string
|
||||||
|
|
@ -93,6 +107,28 @@ func NewHSDClient(options HSDClientOptions) *HSDClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewHSDClientFromOptions is the explicit long-form constructor name.
|
||||||
|
//
|
||||||
|
// client := dns.NewHSDClientFromOptions(dns.HSDClientOptions{
|
||||||
|
// URL: "http://127.0.0.1:14037",
|
||||||
|
// Username: "user",
|
||||||
|
// Password: "pass",
|
||||||
|
// })
|
||||||
|
func NewHSDClientFromOptions(options HSDClientOptions) *HSDClient {
|
||||||
|
return NewHSDClient(options)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHSDClientFromConfiguration is the explicit constructor name for the HSD configuration alias.
|
||||||
|
//
|
||||||
|
// client := dns.NewHSDClientFromConfiguration(dns.HSDClientConfiguration{
|
||||||
|
// URL: "http://127.0.0.1:14037",
|
||||||
|
// Username: "user",
|
||||||
|
// Password: "pass",
|
||||||
|
// })
|
||||||
|
func NewHSDClientFromConfiguration(options HSDClientConfiguration) *HSDClient {
|
||||||
|
return NewHSDClient(options)
|
||||||
|
}
|
||||||
|
|
||||||
func (client *HSDClient) GetNameResource(ctx context.Context, name string) (NameRecords, error) {
|
func (client *HSDClient) GetNameResource(ctx context.Context, name string) (NameRecords, error) {
|
||||||
normalized := strings.TrimSpace(name)
|
normalized := strings.TrimSpace(name)
|
||||||
if normalized == "" {
|
if normalized == "" {
|
||||||
|
|
@ -185,51 +221,127 @@ func (client *HSDClient) call(ctx context.Context, request HSDRPCRequest) (json.
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseHSDNameResource(raw json.RawMessage) (NameRecords, error) {
|
func parseHSDNameResource(raw json.RawMessage) (NameRecords, error) {
|
||||||
var result NameRecords
|
|
||||||
var wrapper map[string]json.RawMessage
|
var wrapper map[string]json.RawMessage
|
||||||
if err := json.Unmarshal(raw, &wrapper); err != nil {
|
if err := json.Unmarshal(raw, &wrapper); err != nil {
|
||||||
return result, err
|
return NameRecords{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if recordsRaw, ok := wrapper["records"]; ok {
|
if recordsRaw, ok := wrapper["records"]; ok {
|
||||||
if err := json.Unmarshal(recordsRaw, &result); err != nil {
|
result, err := parseHSDNameResourceRecords(recordsRaw)
|
||||||
|
if err != nil {
|
||||||
return NameRecords{}, err
|
return NameRecords{}, err
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := wrapper["a"]; ok {
|
if hasHSDNameResourceField(wrapper) {
|
||||||
if err := json.Unmarshal(raw, &result); err != nil {
|
result, err := parseHSDNameResourceRecords(raw)
|
||||||
|
if err != nil {
|
||||||
return NameRecords{}, err
|
return NameRecords{}, err
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var wrapped struct {
|
|
||||||
A []string `json:"a"`
|
|
||||||
AAAA []string `json:"aaaa"`
|
|
||||||
TXT []string `json:"txt"`
|
|
||||||
NS []string `json:"ns"`
|
|
||||||
DS []string `json:"ds"`
|
|
||||||
DNSKEY []string `json:"dnskey"`
|
|
||||||
RRSIG []string `json:"rrsig"`
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(raw, &wrapped); err == nil {
|
|
||||||
result = NameRecords{
|
|
||||||
A: wrapped.A,
|
|
||||||
AAAA: wrapped.AAAA,
|
|
||||||
TXT: wrapped.TXT,
|
|
||||||
NS: wrapped.NS,
|
|
||||||
DS: wrapped.DS,
|
|
||||||
DNSKEY: wrapped.DNSKEY,
|
|
||||||
RRSIG: wrapped.RRSIG,
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return NameRecords{}, errors.New("unable to parse getnameresource result")
|
return NameRecords{}, errors.New("unable to parse getnameresource result")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getCaseInsensitiveRecordField(fields map[string]json.RawMessage, key string) json.RawMessage {
|
||||||
|
if fields == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for candidate, value := range fields {
|
||||||
|
if strings.EqualFold(candidate, key) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseHSDNameResourceRecords(raw json.RawMessage) (NameRecords, error) {
|
||||||
|
var fields map[string]json.RawMessage
|
||||||
|
if err := json.Unmarshal(raw, &fields); err != nil {
|
||||||
|
return NameRecords{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
a, err := parseHSDRecordValue(getCaseInsensitiveRecordField(fields, "a"))
|
||||||
|
if err != nil {
|
||||||
|
return NameRecords{}, err
|
||||||
|
}
|
||||||
|
aaaa, err := parseHSDRecordValue(getCaseInsensitiveRecordField(fields, "aaaa"))
|
||||||
|
if err != nil {
|
||||||
|
return NameRecords{}, err
|
||||||
|
}
|
||||||
|
txt, err := parseHSDRecordValue(getCaseInsensitiveRecordField(fields, "txt"))
|
||||||
|
if err != nil {
|
||||||
|
return NameRecords{}, err
|
||||||
|
}
|
||||||
|
ns, err := parseHSDRecordValue(getCaseInsensitiveRecordField(fields, "ns"))
|
||||||
|
if err != nil {
|
||||||
|
return NameRecords{}, err
|
||||||
|
}
|
||||||
|
ds, err := parseHSDRecordValue(getCaseInsensitiveRecordField(fields, "ds"))
|
||||||
|
if err != nil {
|
||||||
|
return NameRecords{}, err
|
||||||
|
}
|
||||||
|
dnsKey, err := parseHSDRecordValue(getCaseInsensitiveRecordField(fields, "dnskey"))
|
||||||
|
if err != nil {
|
||||||
|
return NameRecords{}, err
|
||||||
|
}
|
||||||
|
rrSig, err := parseHSDRecordValue(getCaseInsensitiveRecordField(fields, "rrsig"))
|
||||||
|
if err != nil {
|
||||||
|
return NameRecords{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NameRecords{
|
||||||
|
A: a,
|
||||||
|
AAAA: aaaa,
|
||||||
|
TXT: txt,
|
||||||
|
NS: ns,
|
||||||
|
DS: ds,
|
||||||
|
DNSKEY: dnsKey,
|
||||||
|
RRSIG: rrSig,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasHSDNameResourceField(fields map[string]json.RawMessage) bool {
|
||||||
|
if len(fields) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return getCaseInsensitiveRecordField(fields, "a") != nil ||
|
||||||
|
getCaseInsensitiveRecordField(fields, "aaaa") != nil ||
|
||||||
|
getCaseInsensitiveRecordField(fields, "txt") != nil ||
|
||||||
|
getCaseInsensitiveRecordField(fields, "ns") != nil ||
|
||||||
|
getCaseInsensitiveRecordField(fields, "ds") != nil ||
|
||||||
|
getCaseInsensitiveRecordField(fields, "dnskey") != nil ||
|
||||||
|
getCaseInsensitiveRecordField(fields, "rrsig") != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseHSDRecordValue(raw json.RawMessage) ([]string, error) {
|
||||||
|
if len(bytes.TrimSpace(raw)) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if trimmed := bytes.TrimSpace(raw); bytes.Equal(trimmed, []byte("null")) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var values []string
|
||||||
|
if err := json.Unmarshal(raw, &values); err == nil {
|
||||||
|
return values, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var value string
|
||||||
|
if err := json.Unmarshal(raw, &value); err == nil {
|
||||||
|
if value == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return []string{value}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("unable to parse DNS record field")
|
||||||
|
}
|
||||||
|
|
||||||
func parseHSDBlockchainInfo(raw json.RawMessage) (HSDBlockchainInfo, error) {
|
func parseHSDBlockchainInfo(raw json.RawMessage) (HSDBlockchainInfo, error) {
|
||||||
var info HSDBlockchainInfo
|
var info HSDBlockchainInfo
|
||||||
var wrapper map[string]json.RawMessage
|
var wrapper map[string]json.RawMessage
|
||||||
|
|
|
||||||
98
hsd_test.go
98
hsd_test.go
|
|
@ -52,6 +52,18 @@ func TestHSDClientGetNameResourceCallsRPCAndParsesResult(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewHSDClientFromOptionsMatchesNewHSDClient(t *testing.T) {
|
||||||
|
clientFromOptions := NewHSDClientFromOptions(HSDClientOptions{URL: "http://127.0.0.1:14037"})
|
||||||
|
clientFromConfiguration := NewHSDClientFromConfiguration(HSDClientConfiguration{URL: "http://127.0.0.1:14037"})
|
||||||
|
|
||||||
|
if clientFromOptions == nil || clientFromConfiguration == nil {
|
||||||
|
t.Fatal("expected explicit HSD constructors to return clients")
|
||||||
|
}
|
||||||
|
if clientFromOptions.baseURL != clientFromConfiguration.baseURL {
|
||||||
|
t.Fatalf("expected explicit HSD constructors to normalize the same base URL, got %q and %q", clientFromOptions.baseURL, clientFromConfiguration.baseURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestHSDClientGetNameResourceParsesWrappedRecords(t *testing.T) {
|
func TestHSDClientGetNameResourceParsesWrappedRecords(t *testing.T) {
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(responseWriter http.ResponseWriter, request *http.Request) {
|
server := httptest.NewServer(http.HandlerFunc(func(responseWriter http.ResponseWriter, request *http.Request) {
|
||||||
var payload struct {
|
var payload struct {
|
||||||
|
|
@ -92,6 +104,50 @@ func TestHSDClientGetNameResourceParsesWrappedRecords(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHSDClientGetNameResourceParsesCaseInsensitiveFields(t *testing.T) {
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(responseWriter http.ResponseWriter, request *http.Request) {
|
||||||
|
var payload struct {
|
||||||
|
Method string `json:"method"`
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(request.Body).Decode(&payload); err != nil {
|
||||||
|
t.Fatalf("unexpected request payload: %v", err)
|
||||||
|
}
|
||||||
|
if payload.Method != "getnameresource" {
|
||||||
|
t.Fatalf("expected method getnameresource, got %s", payload.Method)
|
||||||
|
}
|
||||||
|
|
||||||
|
responseWriter.Header().Set("Content-Type", "application/json")
|
||||||
|
_ = json.NewEncoder(responseWriter).Encode(map[string]any{
|
||||||
|
"result": map[string]any{
|
||||||
|
"records": map[string]any{
|
||||||
|
"A": []string{"10.10.10.10"},
|
||||||
|
"Txt": []string{"v=lthn1 type=case"},
|
||||||
|
"NS": []string{"ns.example.lthn"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
client := NewHSDClient(HSDClientOptions{
|
||||||
|
URL: server.URL,
|
||||||
|
})
|
||||||
|
|
||||||
|
record, err := client.GetNameResource(context.Background(), "case.lthn")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected getnameresource error: %v", err)
|
||||||
|
}
|
||||||
|
if len(record.A) != 1 || record.A[0] != "10.10.10.10" {
|
||||||
|
t.Fatalf("unexpected wrapped A result with mixed-case key: %#v", record.A)
|
||||||
|
}
|
||||||
|
if len(record.TXT) != 1 || record.TXT[0] != "v=lthn1 type=case" {
|
||||||
|
t.Fatalf("unexpected wrapped TXT result with mixed-case key: %#v", record.TXT)
|
||||||
|
}
|
||||||
|
if len(record.NS) != 1 || record.NS[0] != "ns.example.lthn" {
|
||||||
|
t.Fatalf("unexpected wrapped NS result with mixed-case key: %#v", record.NS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestHSDClientGetNameResourceParsesDSRecords(t *testing.T) {
|
func TestHSDClientGetNameResourceParsesDSRecords(t *testing.T) {
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(responseWriter http.ResponseWriter, request *http.Request) {
|
server := httptest.NewServer(http.HandlerFunc(func(responseWriter http.ResponseWriter, request *http.Request) {
|
||||||
var payload struct {
|
var payload struct {
|
||||||
|
|
@ -168,6 +224,48 @@ func TestHSDClientGetNameResourceParsesDNSSECRecords(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHSDClientGetNameResourceParsesSingleValueDNSSECRecords(t *testing.T) {
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(responseWriter http.ResponseWriter, request *http.Request) {
|
||||||
|
var payload struct {
|
||||||
|
Method string `json:"method"`
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(request.Body).Decode(&payload); err != nil {
|
||||||
|
t.Fatalf("unexpected request payload: %v", err)
|
||||||
|
}
|
||||||
|
if payload.Method != "getnameresource" {
|
||||||
|
t.Fatalf("expected method getnameresource, got %s", payload.Method)
|
||||||
|
}
|
||||||
|
|
||||||
|
responseWriter.Header().Set("Content-Type", "application/json")
|
||||||
|
_ = json.NewEncoder(responseWriter).Encode(map[string]any{
|
||||||
|
"result": map[string]any{
|
||||||
|
"dnskey": "257 3 13 AA==",
|
||||||
|
"rrsig": "A 8 2 3600 20260101000000 20250101000000 12345 gateway.lthn. AA==",
|
||||||
|
"ds": "60485 8 2 A1B2C3D4E5F60718293A4B5C6D7E8F9012345678",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
client := NewHSDClient(HSDClientOptions{
|
||||||
|
URL: server.URL,
|
||||||
|
})
|
||||||
|
|
||||||
|
record, err := client.GetNameResource(context.Background(), "gateway.lthn")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected getnameresource error: %v", err)
|
||||||
|
}
|
||||||
|
if len(record.DNSKEY) != 1 || record.DNSKEY[0] != "257 3 13 AA==" {
|
||||||
|
t.Fatalf("unexpected DNSKEY result: %#v", record.DNSKEY)
|
||||||
|
}
|
||||||
|
if len(record.RRSIG) != 1 || record.RRSIG[0] != "A 8 2 3600 20260101000000 20250101000000 12345 gateway.lthn. AA==" {
|
||||||
|
t.Fatalf("unexpected RRSIG result: %#v", record.RRSIG)
|
||||||
|
}
|
||||||
|
if len(record.DS) != 1 || record.DS[0] != "60485 8 2 A1B2C3D4E5F60718293A4B5C6D7E8F9012345678" {
|
||||||
|
t.Fatalf("unexpected DS result: %#v", record.DS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestHSDClientGetBlockchainInfo(t *testing.T) {
|
func TestHSDClientGetBlockchainInfo(t *testing.T) {
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(responseWriter http.ResponseWriter, request *http.Request) {
|
server := httptest.NewServer(http.HandlerFunc(func(responseWriter http.ResponseWriter, request *http.Request) {
|
||||||
var payload struct {
|
var payload struct {
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,13 @@ func (server *HealthServer) Address() string {
|
||||||
return server.HealthAddress()
|
return server.HealthAddress()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StartHealthServer is an explicit convenience wrapper around ServeHTTPHealth.
|
||||||
|
//
|
||||||
|
// health, err := service.StartHealthServer("127.0.0.1", 5554)
|
||||||
|
func (service *Service) StartHealthServer(bind string, port int) (*HealthServer, error) {
|
||||||
|
return service.ServeHTTPHealth(bind, port)
|
||||||
|
}
|
||||||
|
|
||||||
func (server *HealthServer) Close() error {
|
func (server *HealthServer) Close() error {
|
||||||
if server == nil {
|
if server == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -65,7 +72,7 @@ func (service *Service) ServeHTTPHealth(bind string, port int) (*HealthServer, e
|
||||||
bind = "127.0.0.1"
|
bind = "127.0.0.1"
|
||||||
}
|
}
|
||||||
if port <= 0 {
|
if port <= 0 {
|
||||||
port = service.resolveHTTPPort()
|
port = service.resolveHealthPort()
|
||||||
}
|
}
|
||||||
|
|
||||||
address := net.JoinHostPort(bind, strconv.Itoa(port))
|
address := net.JoinHostPort(bind, strconv.Itoa(port))
|
||||||
|
|
|
||||||
70
mainchain.go
70
mainchain.go
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -25,6 +26,20 @@ type MainchainClientOptions struct {
|
||||||
HTTPClient *http.Client
|
HTTPClient *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MainchainAliasClientConfiguration is the explicit long-form alias for MainchainClientOptions.
|
||||||
|
//
|
||||||
|
// client := dns.NewMainchainAliasClientFromConfiguration(dns.MainchainAliasClientConfiguration{
|
||||||
|
// URL: "http://127.0.0.1:14037",
|
||||||
|
// Username: "user",
|
||||||
|
// Password: "pass",
|
||||||
|
// })
|
||||||
|
type MainchainAliasClientConfiguration = MainchainClientOptions
|
||||||
|
|
||||||
|
// MainchainAliasClientConfig is kept for compatibility with older call sites.
|
||||||
|
//
|
||||||
|
// Deprecated: use MainchainAliasClientConfiguration instead.
|
||||||
|
type MainchainAliasClientConfig = MainchainClientOptions
|
||||||
|
|
||||||
type MainchainAliasClient struct {
|
type MainchainAliasClient struct {
|
||||||
baseURL string
|
baseURL string
|
||||||
username string
|
username string
|
||||||
|
|
@ -70,6 +85,28 @@ func NewMainchainAliasClient(options MainchainClientOptions) *MainchainAliasClie
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewMainchainAliasClientFromOptions is the explicit long-form constructor name.
|
||||||
|
//
|
||||||
|
// client := dns.NewMainchainAliasClientFromOptions(dns.MainchainClientOptions{
|
||||||
|
// URL: "http://127.0.0.1:14037",
|
||||||
|
// Username: "user",
|
||||||
|
// Password: "pass",
|
||||||
|
// })
|
||||||
|
func NewMainchainAliasClientFromOptions(options MainchainClientOptions) *MainchainAliasClient {
|
||||||
|
return NewMainchainAliasClient(options)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMainchainAliasClientFromConfiguration is the explicit constructor name for the main-chain configuration alias.
|
||||||
|
//
|
||||||
|
// client := dns.NewMainchainAliasClientFromConfiguration(dns.MainchainAliasClientConfiguration{
|
||||||
|
// URL: "http://127.0.0.1:14037",
|
||||||
|
// Username: "user",
|
||||||
|
// Password: "pass",
|
||||||
|
// })
|
||||||
|
func NewMainchainAliasClientFromConfiguration(options MainchainAliasClientConfiguration) *MainchainAliasClient {
|
||||||
|
return NewMainchainAliasClient(options)
|
||||||
|
}
|
||||||
|
|
||||||
// GetAllAliasDetails returns alias names mapped to HNS DNS names.
|
// GetAllAliasDetails returns alias names mapped to HNS DNS names.
|
||||||
//
|
//
|
||||||
// client := dns.NewMainchainAliasClient(dns.MainchainClientOptions{
|
// client := dns.NewMainchainAliasClient(dns.MainchainClientOptions{
|
||||||
|
|
@ -222,19 +259,39 @@ func decodeString(raw json.RawMessage) (string, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractAliasFromComment(comment string) string {
|
func extractAliasFromComment(comment string) string {
|
||||||
for _, token := range strings.Fields(comment) {
|
fields := strings.Fields(comment)
|
||||||
if strings.HasPrefix(token, "hns=") {
|
for index, token := range fields {
|
||||||
return normalizeName(strings.TrimSuffix(strings.TrimPrefix(token, "hns="), ";"))
|
normalizedToken := strings.ToLower(strings.TrimSpace(token))
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(normalizedToken, "hns="):
|
||||||
|
alias := strings.TrimSuffix(strings.TrimPrefix(normalizedToken, "hns="), ";")
|
||||||
|
if alias != "" {
|
||||||
|
return normalizeName(alias)
|
||||||
|
}
|
||||||
|
case normalizedToken == "hns" && index+2 < len(fields):
|
||||||
|
nextToken := strings.TrimSpace(strings.ToLower(fields[index+1]))
|
||||||
|
if nextToken != "=" && nextToken != ":" && nextToken != ":=" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
alias := strings.TrimSpace(fields[index+2])
|
||||||
|
alias = strings.TrimSuffix(alias, ";")
|
||||||
|
alias = strings.TrimSuffix(alias, ",")
|
||||||
|
if alias != "" {
|
||||||
|
return normalizeName(alias)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if marker := strings.Index(comment, "hns="); marker >= 0 {
|
commentLower := strings.ToLower(comment)
|
||||||
alias := comment[marker+4:]
|
if marker := strings.Index(commentLower, "hns"); marker >= 0 {
|
||||||
if trim := strings.IndexAny(alias, " ;,"); trim >= 0 {
|
alias := comment[marker+3:]
|
||||||
|
alias = strings.TrimLeft(alias, " \t:=;")
|
||||||
|
if trim := strings.IndexAny(alias, " ;,\t\r\n"); trim >= 0 {
|
||||||
alias = alias[:trim]
|
alias = alias[:trim]
|
||||||
}
|
}
|
||||||
alias = strings.TrimSpace(alias)
|
alias = strings.TrimSpace(alias)
|
||||||
alias = strings.TrimSuffix(alias, ";")
|
alias = strings.TrimSuffix(alias, ";")
|
||||||
|
alias = strings.TrimSuffix(alias, ",")
|
||||||
return normalizeName(alias)
|
return normalizeName(alias)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -255,5 +312,6 @@ func normalizeAliasList(raw []string) []string {
|
||||||
seen[next] = true
|
seen[next] = true
|
||||||
normalized = append(normalized, next)
|
normalized = append(normalized, next)
|
||||||
}
|
}
|
||||||
|
slices.Sort(normalized)
|
||||||
return normalized
|
return normalized
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,18 @@ func TestMainchainClientGetsAliasDetailsAndParsesHNSComments(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewMainchainAliasClientFromOptionsMatchesNewMainchainAliasClient(t *testing.T) {
|
||||||
|
clientFromOptions := NewMainchainAliasClientFromOptions(MainchainClientOptions{URL: "http://127.0.0.1:14037"})
|
||||||
|
clientFromConfiguration := NewMainchainAliasClientFromConfiguration(MainchainAliasClientConfiguration{URL: "http://127.0.0.1:14037"})
|
||||||
|
|
||||||
|
if clientFromOptions == nil || clientFromConfiguration == nil {
|
||||||
|
t.Fatal("expected explicit main-chain constructors to return clients")
|
||||||
|
}
|
||||||
|
if clientFromOptions.baseURL != clientFromConfiguration.baseURL {
|
||||||
|
t.Fatalf("expected explicit main-chain constructors to normalize the same base URL, got %q and %q", clientFromOptions.baseURL, clientFromConfiguration.baseURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestServiceDiscoverFromMainchainAliasesUsesMainchainThenHSD(t *testing.T) {
|
func TestServiceDiscoverFromMainchainAliasesUsesMainchainThenHSD(t *testing.T) {
|
||||||
var chainCalls int32
|
var chainCalls int32
|
||||||
var hsdTreeRootCalls int32
|
var hsdTreeRootCalls int32
|
||||||
|
|
@ -559,3 +571,48 @@ func TestServiceDiscoverFromMainchainAliasesFallsBackToChainClientWhenPrimaryDis
|
||||||
t.Fatalf("expected one tree-root and two name-resource RPC calls, got treeRoot=%d nameResource=%d", atomic.LoadInt32(&hsdTreeRootCalls), atomic.LoadInt32(&hsdAliasCalls))
|
t.Fatalf("expected one tree-root and two name-resource RPC calls, got treeRoot=%d nameResource=%d", atomic.LoadInt32(&hsdTreeRootCalls), atomic.LoadInt32(&hsdAliasCalls))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestExtractAliasFromCommentParsesCaseInsensitiveHNSPrefix(t *testing.T) {
|
||||||
|
got := extractAliasFromComment("gateway alias HNS=gateway.charon.lthn")
|
||||||
|
if got != "gateway.charon.lthn" {
|
||||||
|
t.Fatalf("expected gateway.charon.lthn, got %s", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtractAliasFromCommentParsesSpacedHNSAssignment(t *testing.T) {
|
||||||
|
got := extractAliasFromComment("gateway alias hns = gateway.charon.lthn;")
|
||||||
|
if got != "gateway.charon.lthn" {
|
||||||
|
t.Fatalf("expected gateway.charon.lthn, got %s", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewServiceBuildsMainchainAliasClientWithHSDFallbackCredentials(t *testing.T) {
|
||||||
|
service := NewService(ServiceOptions{
|
||||||
|
MainchainURL: "http://127.0.0.1:14037",
|
||||||
|
HSDUsername: "mainchain-user",
|
||||||
|
HSDPassword: "mainchain-pass",
|
||||||
|
HSDApiKey: "ignored-token",
|
||||||
|
})
|
||||||
|
|
||||||
|
if service.mainchainAliasClient == nil {
|
||||||
|
t.Fatal("expected default mainchain alias client with fallback credentials")
|
||||||
|
}
|
||||||
|
if got := service.mainchainAliasClient.username; got != "mainchain-user" {
|
||||||
|
t.Fatalf("expected mainchain username to fall back to hsd username, got %q", got)
|
||||||
|
}
|
||||||
|
if got := service.mainchainAliasClient.password; got != "mainchain-pass" {
|
||||||
|
t.Fatalf("expected mainchain password to fall back to hsd password, got %q", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceFromToken := NewService(ServiceOptions{
|
||||||
|
MainchainURL: "http://127.0.0.1:14037",
|
||||||
|
HSDUsername: "token-user",
|
||||||
|
HSDApiKey: "token-pass",
|
||||||
|
})
|
||||||
|
if serviceFromToken.mainchainAliasClient == nil {
|
||||||
|
t.Fatal("expected default mainchain alias client with fallback token credentials")
|
||||||
|
}
|
||||||
|
if got := serviceFromToken.mainchainAliasClient.password; got != "token-pass" {
|
||||||
|
t.Fatalf("expected mainchain password to fall back to hsd api key token, got %q", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
248
serve.go
248
serve.go
|
|
@ -30,23 +30,60 @@ type DNSServer struct {
|
||||||
// defer func() { _ = runtime.Close() }()
|
// defer func() { _ = runtime.Close() }()
|
||||||
// fmt.Println(runtime.DNSAddress(), runtime.HealthAddress())
|
// fmt.Println(runtime.DNSAddress(), runtime.HealthAddress())
|
||||||
type ServiceRuntime struct {
|
type ServiceRuntime struct {
|
||||||
DNS *DNSServer
|
// DNSServer is the explicit name for the DNS listener.
|
||||||
|
DNSServer *DNSServer
|
||||||
|
// DNS is retained for compatibility with older call sites.
|
||||||
|
DNS *DNSServer
|
||||||
|
// HealthServer is the explicit name for the `/health` listener.
|
||||||
|
HealthServer *HealthServer
|
||||||
|
// Health is retained for compatibility with older call sites.
|
||||||
Health *HealthServer
|
Health *HealthServer
|
||||||
// HTTP is retained for compatibility with older call sites.
|
// HTTP is retained for compatibility with older call sites.
|
||||||
HTTP *HealthServer
|
HTTP *HealthServer
|
||||||
|
// HTTPServer is the explicit name for the `/health` listener when the
|
||||||
|
// HTTP surface is addressed directly.
|
||||||
|
HTTPServer *HealthServer
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSServiceRuntime is the explicit long-form alias for ServiceRuntime.
|
||||||
|
//
|
||||||
|
// runtime, err := service.ServeAll("127.0.0.1", 53, 5554)
|
||||||
|
// var dnsRuntime dns.DNSServiceRuntime = *runtime
|
||||||
|
// fmt.Println(dnsRuntime.DNSAddress(), dnsRuntime.HealthAddress())
|
||||||
|
type DNSServiceRuntime = ServiceRuntime
|
||||||
|
|
||||||
|
// Address is the compatibility alias for HealthAddress.
|
||||||
|
//
|
||||||
|
// runtime, err := service.ServeAll("127.0.0.1", 53, 5554)
|
||||||
|
// defer func() { _ = runtime.Close() }()
|
||||||
|
// fmt.Println(runtime.Address())
|
||||||
|
func (runtime *ServiceRuntime) Address() string {
|
||||||
|
return runtime.HealthAddress()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *ServiceRuntime) DNSAddress() string {
|
func (runtime *ServiceRuntime) DNSAddress() string {
|
||||||
if runtime == nil || runtime.DNS == nil {
|
if runtime == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return runtime.DNS.DNSAddress()
|
if runtime.DNSServer != nil {
|
||||||
|
return runtime.DNSServer.DNSAddress()
|
||||||
|
}
|
||||||
|
if runtime.DNS != nil {
|
||||||
|
return runtime.DNS.DNSAddress()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (runtime *ServiceRuntime) HealthAddress() string {
|
func (runtime *ServiceRuntime) HealthAddress() string {
|
||||||
if runtime == nil {
|
if runtime == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
if runtime.HealthServer != nil {
|
||||||
|
return runtime.HealthServer.HealthAddress()
|
||||||
|
}
|
||||||
|
if runtime.HTTPServer != nil {
|
||||||
|
return runtime.HTTPServer.HealthAddress()
|
||||||
|
}
|
||||||
if runtime.Health != nil {
|
if runtime.Health != nil {
|
||||||
return runtime.Health.HealthAddress()
|
return runtime.Health.HealthAddress()
|
||||||
}
|
}
|
||||||
|
|
@ -67,17 +104,32 @@ func (runtime *ServiceRuntime) Close() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
var firstError error
|
var firstError error
|
||||||
if runtime.DNS != nil {
|
if runtime.DNSServer != nil {
|
||||||
|
if err := runtime.DNSServer.Close(); err != nil && firstError == nil {
|
||||||
|
firstError = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if runtime.DNS != nil && runtime.DNS != runtime.DNSServer {
|
||||||
if err := runtime.DNS.Close(); err != nil && firstError == nil {
|
if err := runtime.DNS.Close(); err != nil && firstError == nil {
|
||||||
firstError = err
|
firstError = err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if runtime.Health != nil {
|
if runtime.HealthServer != nil {
|
||||||
|
if err := runtime.HealthServer.Close(); err != nil && firstError == nil {
|
||||||
|
firstError = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if runtime.HTTPServer != nil && runtime.HTTPServer != runtime.HealthServer {
|
||||||
|
if err := runtime.HTTPServer.Close(); err != nil && firstError == nil {
|
||||||
|
firstError = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if runtime.Health != nil && runtime.Health != runtime.HealthServer {
|
||||||
if err := runtime.Health.Close(); err != nil && firstError == nil {
|
if err := runtime.Health.Close(); err != nil && firstError == nil {
|
||||||
firstError = err
|
firstError = err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if runtime.HTTP != nil && runtime.HTTP != runtime.Health {
|
if runtime.HTTP != nil && runtime.HTTP != runtime.HealthServer && runtime.HTTP != runtime.Health {
|
||||||
if err := runtime.HTTP.Close(); err != nil && firstError == nil {
|
if err := runtime.HTTP.Close(); err != nil && firstError == nil {
|
||||||
firstError = err
|
firstError = err
|
||||||
}
|
}
|
||||||
|
|
@ -104,14 +156,18 @@ func (server *DNSServer) Close() error {
|
||||||
if server.tcpListener != nil {
|
if server.tcpListener != nil {
|
||||||
_ = server.tcpListener.Close()
|
_ = server.tcpListener.Close()
|
||||||
}
|
}
|
||||||
var err error
|
var firstError error
|
||||||
if server.udpServer != nil {
|
if server.udpServer != nil {
|
||||||
err = server.udpServer.Shutdown()
|
if err := server.udpServer.Shutdown(); err != nil && firstError == nil {
|
||||||
|
firstError = err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if server.tcpServer != nil {
|
if server.tcpServer != nil {
|
||||||
err = server.tcpServer.Shutdown()
|
if err := server.tcpServer.Shutdown(); err != nil && firstError == nil {
|
||||||
|
firstError = err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return err
|
return firstError
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveDNSPort returns the DNS port used for `dns.serve` and `Serve`.
|
// ResolveDNSPort returns the DNS port used for `dns.serve` and `Serve`.
|
||||||
|
|
@ -119,22 +175,50 @@ func (server *DNSServer) Close() error {
|
||||||
// port := service.ResolveDNSPort()
|
// port := service.ResolveDNSPort()
|
||||||
// server, err := service.Serve("127.0.0.1", port)
|
// server, err := service.Serve("127.0.0.1", port)
|
||||||
func (service *Service) ResolveDNSPort() int {
|
func (service *Service) ResolveDNSPort() int {
|
||||||
if service == nil || service.dnsPort <= 0 {
|
if service == nil {
|
||||||
|
return DefaultDNSPort
|
||||||
|
}
|
||||||
|
if service.dnsPort <= 0 {
|
||||||
return DefaultDNSPort
|
return DefaultDNSPort
|
||||||
}
|
}
|
||||||
return service.dnsPort
|
return service.dnsPort
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DNSListenPort resolves the configured DNS listen port for `dns.serve`.
|
||||||
|
//
|
||||||
|
// port := service.DNSListenPort()
|
||||||
|
// server, err := service.Serve("127.0.0.1", port)
|
||||||
|
func (service *Service) DNSListenPort() int {
|
||||||
|
if service == nil {
|
||||||
|
return DefaultDNSPort
|
||||||
|
}
|
||||||
|
return service.ResolveDNSPort()
|
||||||
|
}
|
||||||
|
|
||||||
// DNSPort is an explicit alias for ResolveDNSPort.
|
// DNSPort is an explicit alias for ResolveDNSPort.
|
||||||
//
|
//
|
||||||
// port := service.DNSPort()
|
// port := service.DNSPort()
|
||||||
// server, err := service.Serve("127.0.0.1", port)
|
// server, err := service.Serve("127.0.0.1", port)
|
||||||
func (service *Service) DNSPort() int {
|
func (service *Service) DNSPort() int {
|
||||||
|
if service == nil {
|
||||||
|
return DefaultDNSPort
|
||||||
|
}
|
||||||
return service.ResolveDNSPort()
|
return service.ResolveDNSPort()
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolveServePort keeps internal callers aligned with existing behavior.
|
// resolveDNSListenPort keeps internal callers aligned with explicit naming.
|
||||||
|
func (service *Service) resolveDNSListenPort() int {
|
||||||
|
if service == nil {
|
||||||
|
return DefaultDNSPort
|
||||||
|
}
|
||||||
|
return service.DNSListenPort()
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolveServePort is a legacy compatibility helper.
|
||||||
func (service *Service) resolveServePort() int {
|
func (service *Service) resolveServePort() int {
|
||||||
|
if service == nil {
|
||||||
|
return DefaultDNSPort
|
||||||
|
}
|
||||||
return service.ResolveDNSPort()
|
return service.ResolveDNSPort()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,22 +227,73 @@ func (service *Service) resolveServePort() int {
|
||||||
// port := service.ResolveHTTPPort()
|
// port := service.ResolveHTTPPort()
|
||||||
// healthServer, err := service.ServeHTTPHealth("127.0.0.1", port)
|
// healthServer, err := service.ServeHTTPHealth("127.0.0.1", port)
|
||||||
func (service *Service) ResolveHTTPPort() int {
|
func (service *Service) ResolveHTTPPort() int {
|
||||||
if service == nil || service.httpPort <= 0 {
|
return service.ResolveHealthPort()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolveHealthPort returns the health listener port used by `ServeHTTPHealth`.
|
||||||
|
//
|
||||||
|
// port := service.ResolveHealthPort()
|
||||||
|
// healthServer, err := service.ServeHTTPHealth("127.0.0.1", port)
|
||||||
|
func (service *Service) ResolveHealthPort() int {
|
||||||
|
if service == nil {
|
||||||
|
return DefaultHTTPPort
|
||||||
|
}
|
||||||
|
if service.httpPort <= 0 {
|
||||||
return DefaultHTTPPort
|
return DefaultHTTPPort
|
||||||
}
|
}
|
||||||
return service.httpPort
|
return service.httpPort
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTTPListenPort resolves the configured health/listener port for `dns.serve`.
|
||||||
|
//
|
||||||
|
// port := service.HTTPListenPort()
|
||||||
|
// server, err := service.ServeHTTPHealth("127.0.0.1", port)
|
||||||
|
func (service *Service) HTTPListenPort() int {
|
||||||
|
return service.ResolveHealthPort()
|
||||||
|
}
|
||||||
|
|
||||||
|
// HealthListenPort is the explicit alias for ResolveHealthPort.
|
||||||
|
//
|
||||||
|
// port := service.HealthListenPort()
|
||||||
|
// server, err := service.ServeHTTPHealth("127.0.0.1", port)
|
||||||
|
func (service *Service) HealthListenPort() int {
|
||||||
|
return service.ResolveHealthPort()
|
||||||
|
}
|
||||||
|
|
||||||
|
// HealthPort returns the health listener port used by `ServeHTTPHealth`.
|
||||||
|
//
|
||||||
|
// port := service.HealthPort()
|
||||||
|
// healthServer, err := service.ServeHTTPHealth("127.0.0.1", port)
|
||||||
|
func (service *Service) HealthPort() int {
|
||||||
|
return service.ResolveHealthPort()
|
||||||
|
}
|
||||||
|
|
||||||
// HTTPPort is an explicit alias for ResolveHTTPPort.
|
// HTTPPort is an explicit alias for ResolveHTTPPort.
|
||||||
//
|
//
|
||||||
// port := service.HTTPPort()
|
// port := service.HTTPPort()
|
||||||
// healthServer, err := service.ServeHTTPHealth("127.0.0.1", port)
|
// healthServer, err := service.ServeHTTPHealth("127.0.0.1", port)
|
||||||
func (service *Service) HTTPPort() int {
|
func (service *Service) HTTPPort() int {
|
||||||
return service.ResolveHTTPPort()
|
return service.ResolveHealthPort()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resolveHTTPListenPort keeps internal callers aligned with explicit naming.
|
||||||
|
func (service *Service) resolveHTTPListenPort() int {
|
||||||
|
return service.ResolveHealthPort()
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolveHealthListenPort keeps internal callers aligned with explicit naming.
|
||||||
|
func (service *Service) resolveHealthListenPort() int {
|
||||||
|
return service.ResolveHealthPort()
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolveHTTPPort is a legacy compatibility helper.
|
||||||
func (service *Service) resolveHTTPPort() int {
|
func (service *Service) resolveHTTPPort() int {
|
||||||
return service.ResolveHTTPPort()
|
return service.ResolveHealthPort()
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolveHealthPort is a legacy compatibility helper.
|
||||||
|
func (service *Service) resolveHealthPort() int {
|
||||||
|
return service.ResolveHealthPort()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve starts DNS over UDP and TCP.
|
// Serve starts DNS over UDP and TCP.
|
||||||
|
|
@ -168,9 +303,15 @@ func (service *Service) resolveHTTPPort() int {
|
||||||
// lookup := new(dnsprotocol.Msg)
|
// lookup := new(dnsprotocol.Msg)
|
||||||
// lookup.SetQuestion("gateway.charon.lthn.", dnsprotocol.TypeA)
|
// lookup.SetQuestion("gateway.charon.lthn.", dnsprotocol.TypeA)
|
||||||
func (service *Service) Serve(bind string, port int) (*DNSServer, error) {
|
func (service *Service) Serve(bind string, port int) (*DNSServer, error) {
|
||||||
|
if service == nil {
|
||||||
|
return nil, fmt.Errorf("service is required")
|
||||||
|
}
|
||||||
if bind == "" {
|
if bind == "" {
|
||||||
bind = "127.0.0.1"
|
bind = "127.0.0.1"
|
||||||
}
|
}
|
||||||
|
if port <= 0 {
|
||||||
|
port = service.ResolveDNSPort()
|
||||||
|
}
|
||||||
addr := net.JoinHostPort(bind, strconv.Itoa(port))
|
addr := net.JoinHostPort(bind, strconv.Itoa(port))
|
||||||
|
|
||||||
udpListener, err := net.ListenPacket("udp", addr)
|
udpListener, err := net.ListenPacket("udp", addr)
|
||||||
|
|
@ -207,17 +348,41 @@ func (service *Service) Serve(bind string, port int) (*DNSServer, error) {
|
||||||
return dnsServer, nil
|
return dnsServer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StartDNSServer is an explicit convenience wrapper around Serve.
|
||||||
|
//
|
||||||
|
// dnsServer, err := service.StartDNSServer("127.0.0.1", 53)
|
||||||
|
func (service *Service) StartDNSServer(bind string, port int) (*DNSServer, error) {
|
||||||
|
return service.Serve(bind, port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartDNSAndHealth is an explicit convenience wrapper around ServeAll.
|
||||||
|
//
|
||||||
|
// runtime, err := service.StartDNSAndHealth("127.0.0.1", 53, 5554)
|
||||||
|
func (service *Service) StartDNSAndHealth(bind string, dnsPort int, healthPort int) (*ServiceRuntime, error) {
|
||||||
|
return service.ServeAll(bind, dnsPort, healthPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeDNSAndHealth is the explicit alias for ServeAll.
|
||||||
|
//
|
||||||
|
// runtime, err := service.ServeDNSAndHealth("127.0.0.1", 53, 5554)
|
||||||
|
func (service *Service) ServeDNSAndHealth(bind string, dnsPort int, httpPort int) (*ServiceRuntime, error) {
|
||||||
|
return service.ServeAll(bind, dnsPort, httpPort)
|
||||||
|
}
|
||||||
|
|
||||||
// ServeAll starts DNS and health together.
|
// ServeAll starts DNS and health together.
|
||||||
//
|
//
|
||||||
// runtime, err := service.ServeAll("127.0.0.1", 53, 5554)
|
// runtime, err := service.ServeAll("127.0.0.1", 53, 5554)
|
||||||
// defer func() { _ = runtime.Close() }()
|
// defer func() { _ = runtime.Close() }()
|
||||||
// fmt.Println("dns:", runtime.DNSAddress(), "health:", runtime.HealthAddress())
|
// fmt.Println("dns:", runtime.DNSAddress(), "health:", runtime.HealthAddress())
|
||||||
func (service *Service) ServeAll(bind string, dnsPort int, httpPort int) (*ServiceRuntime, error) {
|
func (service *Service) ServeAll(bind string, dnsPort int, httpPort int) (*ServiceRuntime, error) {
|
||||||
if dnsPort == 0 {
|
if service == nil {
|
||||||
dnsPort = service.dnsPort
|
return nil, fmt.Errorf("service is required")
|
||||||
|
}
|
||||||
|
if dnsPort <= 0 {
|
||||||
|
dnsPort = service.resolveDNSListenPort()
|
||||||
}
|
}
|
||||||
if httpPort <= 0 {
|
if httpPort <= 0 {
|
||||||
httpPort = service.resolveHTTPPort()
|
httpPort = service.resolveHealthListenPort()
|
||||||
}
|
}
|
||||||
|
|
||||||
dnsServer, err := service.Serve(bind, dnsPort)
|
dnsServer, err := service.Serve(bind, dnsPort)
|
||||||
|
|
@ -232,9 +397,12 @@ func (service *Service) ServeAll(bind string, dnsPort int, httpPort int) (*Servi
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ServiceRuntime{
|
return &ServiceRuntime{
|
||||||
DNS: dnsServer,
|
DNSServer: dnsServer,
|
||||||
Health: httpServer,
|
DNS: dnsServer,
|
||||||
HTTP: httpServer,
|
HealthServer: httpServer,
|
||||||
|
Health: httpServer,
|
||||||
|
HTTP: httpServer,
|
||||||
|
HTTPServer: httpServer,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -246,9 +414,19 @@ func (service *Service) ServeAll(bind string, dnsPort int, httpPort int) (*Servi
|
||||||
// })
|
// })
|
||||||
// runtime, err := service.ServeConfigured("127.0.0.1")
|
// runtime, err := service.ServeConfigured("127.0.0.1")
|
||||||
func (service *Service) ServeConfigured(bind string) (*ServiceRuntime, error) {
|
func (service *Service) ServeConfigured(bind string) (*ServiceRuntime, error) {
|
||||||
|
if service == nil {
|
||||||
|
return nil, fmt.Errorf("service is required")
|
||||||
|
}
|
||||||
return service.ServeAll(bind, service.dnsPort, service.httpPort)
|
return service.ServeAll(bind, service.dnsPort, service.httpPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StartConfigured is an explicit convenience wrapper around ServeConfigured.
|
||||||
|
//
|
||||||
|
// runtime, err := service.StartConfigured("127.0.0.1")
|
||||||
|
func (service *Service) StartConfigured(bind string) (*ServiceRuntime, error) {
|
||||||
|
return service.ServeConfigured(bind)
|
||||||
|
}
|
||||||
|
|
||||||
type dnsRequestHandler struct {
|
type dnsRequestHandler struct {
|
||||||
service *Service
|
service *Service
|
||||||
}
|
}
|
||||||
|
|
@ -285,7 +463,7 @@ func (handler *dnsRequestHandler) ServeDNS(responseWriter dnsprotocol.ResponseWr
|
||||||
if !found {
|
if !found {
|
||||||
goto noRecord
|
goto noRecord
|
||||||
}
|
}
|
||||||
for _, value := range record.A {
|
for _, value := range normalizeRecordValues(record.A) {
|
||||||
parsedIP := net.ParseIP(value)
|
parsedIP := net.ParseIP(value)
|
||||||
if parsedIP == nil || parsedIP.To4() == nil {
|
if parsedIP == nil || parsedIP.To4() == nil {
|
||||||
continue
|
continue
|
||||||
|
|
@ -299,7 +477,7 @@ func (handler *dnsRequestHandler) ServeDNS(responseWriter dnsprotocol.ResponseWr
|
||||||
if !found {
|
if !found {
|
||||||
goto noRecord
|
goto noRecord
|
||||||
}
|
}
|
||||||
for _, value := range record.AAAA {
|
for _, value := range normalizeRecordValues(record.AAAA) {
|
||||||
parsedIP := net.ParseIP(value)
|
parsedIP := net.ParseIP(value)
|
||||||
if parsedIP == nil || parsedIP.To4() != nil {
|
if parsedIP == nil || parsedIP.To4() != nil {
|
||||||
continue
|
continue
|
||||||
|
|
@ -313,7 +491,7 @@ func (handler *dnsRequestHandler) ServeDNS(responseWriter dnsprotocol.ResponseWr
|
||||||
if !found {
|
if !found {
|
||||||
goto noRecord
|
goto noRecord
|
||||||
}
|
}
|
||||||
for _, value := range record.TXT {
|
for _, value := range normalizeRecordValues(record.TXT) {
|
||||||
reply.Answer = append(reply.Answer, &dnsprotocol.TXT{
|
reply.Answer = append(reply.Answer, &dnsprotocol.TXT{
|
||||||
Hdr: dnsprotocol.RR_Header{Name: question.Name, Rrtype: dnsprotocol.TypeTXT, Class: dnsprotocol.ClassINET, Ttl: defaultDNSTTL},
|
Hdr: dnsprotocol.RR_Header{Name: question.Name, Rrtype: dnsprotocol.TypeTXT, Class: dnsprotocol.ClassINET, Ttl: defaultDNSTTL},
|
||||||
Txt: []string{value},
|
Txt: []string{value},
|
||||||
|
|
@ -321,7 +499,7 @@ func (handler *dnsRequestHandler) ServeDNS(responseWriter dnsprotocol.ResponseWr
|
||||||
}
|
}
|
||||||
case dnsprotocol.TypeNS:
|
case dnsprotocol.TypeNS:
|
||||||
if found {
|
if found {
|
||||||
for _, value := range record.NS {
|
for _, value := range normalizeRecordValues(record.NS) {
|
||||||
reply.Answer = append(reply.Answer, &dnsprotocol.NS{
|
reply.Answer = append(reply.Answer, &dnsprotocol.NS{
|
||||||
Hdr: dnsprotocol.RR_Header{Name: question.Name, Rrtype: dnsprotocol.TypeNS, Class: dnsprotocol.ClassINET, Ttl: defaultDNSTTL},
|
Hdr: dnsprotocol.RR_Header{Name: question.Name, Rrtype: dnsprotocol.TypeNS, Class: dnsprotocol.ClassINET, Ttl: defaultDNSTTL},
|
||||||
Ns: normalizeName(value) + ".",
|
Ns: normalizeName(value) + ".",
|
||||||
|
|
@ -383,17 +561,17 @@ func (handler *dnsRequestHandler) ServeDNS(responseWriter dnsprotocol.ResponseWr
|
||||||
if !found {
|
if !found {
|
||||||
goto noRecord
|
goto noRecord
|
||||||
}
|
}
|
||||||
appendDNSSECResourceRecords(reply, question.Name, dnsprotocol.TypeDS, record.DS)
|
appendDNSSECResourceRecords(reply, question.Name, dnsprotocol.TypeDS, normalizeRecordValues(record.DS))
|
||||||
case dnsprotocol.TypeDNSKEY:
|
case dnsprotocol.TypeDNSKEY:
|
||||||
if !found {
|
if !found {
|
||||||
goto noRecord
|
goto noRecord
|
||||||
}
|
}
|
||||||
appendDNSSECResourceRecords(reply, question.Name, dnsprotocol.TypeDNSKEY, record.DNSKEY)
|
appendDNSSECResourceRecords(reply, question.Name, dnsprotocol.TypeDNSKEY, normalizeRecordValues(record.DNSKEY))
|
||||||
case dnsprotocol.TypeRRSIG:
|
case dnsprotocol.TypeRRSIG:
|
||||||
if !found {
|
if !found {
|
||||||
goto noRecord
|
goto noRecord
|
||||||
}
|
}
|
||||||
appendDNSSECResourceRecords(reply, question.Name, dnsprotocol.TypeRRSIG, record.RRSIG)
|
appendDNSSECResourceRecords(reply, question.Name, dnsprotocol.TypeRRSIG, normalizeRecordValues(record.RRSIG))
|
||||||
default:
|
default:
|
||||||
reply.SetRcode(request, dnsprotocol.RcodeNotImplemented)
|
reply.SetRcode(request, dnsprotocol.RcodeNotImplemented)
|
||||||
_ = responseWriter.WriteMsg(reply)
|
_ = responseWriter.WriteMsg(reply)
|
||||||
|
|
@ -466,7 +644,7 @@ func parsePTRIP(name string) (string, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendAnyAnswers(reply *dnsprotocol.Msg, questionName string, lookupName string, record NameRecords, zoneApex string) {
|
func appendAnyAnswers(reply *dnsprotocol.Msg, questionName string, lookupName string, record NameRecords, zoneApex string) {
|
||||||
for _, value := range record.A {
|
for _, value := range normalizeRecordValues(record.A) {
|
||||||
parsedIP := net.ParseIP(value)
|
parsedIP := net.ParseIP(value)
|
||||||
if parsedIP == nil || parsedIP.To4() == nil {
|
if parsedIP == nil || parsedIP.To4() == nil {
|
||||||
continue
|
continue
|
||||||
|
|
@ -477,7 +655,7 @@ func appendAnyAnswers(reply *dnsprotocol.Msg, questionName string, lookupName st
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, value := range record.AAAA {
|
for _, value := range normalizeRecordValues(record.AAAA) {
|
||||||
parsedIP := net.ParseIP(value)
|
parsedIP := net.ParseIP(value)
|
||||||
if parsedIP == nil || parsedIP.To4() != nil {
|
if parsedIP == nil || parsedIP.To4() != nil {
|
||||||
continue
|
continue
|
||||||
|
|
@ -488,7 +666,7 @@ func appendAnyAnswers(reply *dnsprotocol.Msg, questionName string, lookupName st
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, value := range record.TXT {
|
for _, value := range normalizeRecordValues(record.TXT) {
|
||||||
reply.Answer = append(reply.Answer, &dnsprotocol.TXT{
|
reply.Answer = append(reply.Answer, &dnsprotocol.TXT{
|
||||||
Hdr: dnsprotocol.RR_Header{Name: questionName, Rrtype: dnsprotocol.TypeTXT, Class: dnsprotocol.ClassINET, Ttl: defaultDNSTTL},
|
Hdr: dnsprotocol.RR_Header{Name: questionName, Rrtype: dnsprotocol.TypeTXT, Class: dnsprotocol.ClassINET, Ttl: defaultDNSTTL},
|
||||||
Txt: []string{value},
|
Txt: []string{value},
|
||||||
|
|
@ -496,7 +674,7 @@ func appendAnyAnswers(reply *dnsprotocol.Msg, questionName string, lookupName st
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(record.NS) > 0 {
|
if len(record.NS) > 0 {
|
||||||
for _, value := range record.NS {
|
for _, value := range normalizeRecordValues(record.NS) {
|
||||||
reply.Answer = append(reply.Answer, &dnsprotocol.NS{
|
reply.Answer = append(reply.Answer, &dnsprotocol.NS{
|
||||||
Hdr: dnsprotocol.RR_Header{Name: questionName, Rrtype: dnsprotocol.TypeNS, Class: dnsprotocol.ClassINET, Ttl: defaultDNSTTL},
|
Hdr: dnsprotocol.RR_Header{Name: questionName, Rrtype: dnsprotocol.TypeNS, Class: dnsprotocol.ClassINET, Ttl: defaultDNSTTL},
|
||||||
Ns: normalizeName(value) + ".",
|
Ns: normalizeName(value) + ".",
|
||||||
|
|
@ -504,15 +682,15 @@ func appendAnyAnswers(reply *dnsprotocol.Msg, questionName string, lookupName st
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, value := range record.DNSKEY {
|
for _, value := range normalizeRecordValues(record.DNSKEY) {
|
||||||
appendDNSSECResourceRecords(reply, questionName, dnsprotocol.TypeDNSKEY, []string{value})
|
appendDNSSECResourceRecords(reply, questionName, dnsprotocol.TypeDNSKEY, []string{value})
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, value := range record.DS {
|
for _, value := range normalizeRecordValues(record.DS) {
|
||||||
appendDNSSECResourceRecords(reply, questionName, dnsprotocol.TypeDS, []string{value})
|
appendDNSSECResourceRecords(reply, questionName, dnsprotocol.TypeDS, []string{value})
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, value := range record.RRSIG {
|
for _, value := range normalizeRecordValues(record.RRSIG) {
|
||||||
appendDNSSECResourceRecords(reply, questionName, dnsprotocol.TypeRRSIG, []string{value})
|
appendDNSSECResourceRecords(reply, questionName, dnsprotocol.TypeRRSIG, []string{value})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
755
service.go
755
service.go
File diff suppressed because it is too large
Load diff
1065
service_test.go
1065
service_test.go
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue