Compare commits

..

94 commits
main ... dev

Author SHA1 Message Date
346df8fc9d Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#94) from main into dev 2026-04-03 23:26:53 +00:00
f0940f52f2 Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#93) from main into dev 2026-04-03 23:24:37 +00:00
da6fad18e4 Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#92) from main into dev 2026-04-03 23:22:22 +00:00
3237e835b6 Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#91) from main into dev 2026-04-03 23:20:55 +00:00
27a772eb7b Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#90) from main into dev 2026-04-03 23:19:13 +00:00
d070100fab Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#89) from main into dev 2026-04-03 23:17:22 +00:00
7761225db7 Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#88) from main into dev 2026-04-03 23:15:43 +00:00
013776960e Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#87) from main into dev 2026-04-03 23:13:07 +00:00
7776600b06 Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#86) from main into dev 2026-04-03 23:08:24 +00:00
37392b44f0 Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#85) from main into dev 2026-04-03 23:06:50 +00:00
5de9cb515d Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#84) from main into dev 2026-04-03 23:05:26 +00:00
775b1f5067 Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#83) from main into dev 2026-04-03 23:03:24 +00:00
0e29e82565 Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#82) from main into dev 2026-04-03 23:01:46 +00:00
c82d6b2d05 Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#81) from main into dev 2026-04-03 23:00:14 +00:00
7726fc1e91 Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#80) from main into dev 2026-04-03 22:58:42 +00:00
82fcd3cc00 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#79) from main into dev 2026-04-03 22:57:19 +00:00
0789bbe9a1 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#78) from main into dev 2026-04-03 22:55:02 +00:00
4045217ec0 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#77) from main into dev 2026-04-03 22:52:31 +00:00
cec826012e Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#76) from main into dev 2026-04-03 22:50:35 +00:00
c93c9c54d0 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#75) from main into dev 2026-04-03 22:48:05 +00:00
60455cdf78 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#74) from main into dev 2026-04-03 22:45:57 +00:00
cea034ce66 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#73) from main into dev 2026-04-03 22:43:40 +00:00
2e58035e08 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#72) from main into dev 2026-04-03 22:41:14 +00:00
98c1db1b00 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#71) from main into dev 2026-04-03 22:38:34 +00:00
887d9fee42 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#70) from main into dev 2026-04-03 22:36:34 +00:00
704d79e3a7 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#69) from main into dev 2026-04-03 22:34:38 +00:00
e72c6e3940 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#68) from main into dev 2026-04-03 22:31:43 +00:00
8685c40acc Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#67) from main into dev 2026-04-03 22:28:58 +00:00
4bfe044a54 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#66) from main into dev 2026-04-03 22:26:16 +00:00
0f8803ecc8 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#65) from main into dev 2026-04-03 22:24:09 +00:00
f9e58f5b02 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#64) from main into dev 2026-04-03 22:20:52 +00:00
4d09aaa431 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#63) from main into dev 2026-04-03 22:18:34 +00:00
b868602c7f Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#62) from main into dev 2026-04-03 22:15:55 +00:00
47f63e454e Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#61) from main into dev 2026-04-03 22:13:30 +00:00
a1e0b94c44 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#60) from main into dev 2026-04-03 22:11:14 +00:00
133534e436 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#59) from main into dev 2026-04-03 22:08:52 +00:00
3198ee1801 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#58) from main into dev 2026-04-03 22:06:35 +00:00
16f77d1620 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#57) from main into dev 2026-04-03 22:04:23 +00:00
06c0739b0c Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#56) from main into dev 2026-04-03 22:01:49 +00:00
2ed8beae59 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#55) from main into dev 2026-04-03 21:58:31 +00:00
af794914b1 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#54) from main into dev 2026-04-03 21:55:25 +00:00
8a770c9f07 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#53) from main into dev 2026-04-03 21:52:34 +00:00
1fa9f20506 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#52) from main into dev 2026-04-03 21:49:44 +00:00
d8461e8cf0 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#51) from main into dev 2026-04-03 21:48:04 +00:00
878de29532 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#50) from main into dev 2026-04-03 21:46:14 +00:00
a75bdf856e Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#49) from main into dev 2026-04-03 21:44:01 +00:00
992b5539ed Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#48) from main into dev 2026-04-03 21:41:15 +00:00
b9a3af64ff Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#47) from main into dev 2026-04-03 21:37:47 +00:00
9ee3c31ba5 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#46) from main into dev 2026-04-03 21:35:09 +00:00
b26e1db3e7 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#45) from main into dev 2026-04-03 21:32:44 +00:00
e1baf4919f Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#44) from main into dev 2026-04-03 21:30:16 +00:00
54b262280a Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#43) from main into dev 2026-04-03 21:28:10 +00:00
6fdb8187c3 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#42) from main into dev 2026-04-03 21:26:24 +00:00
898abe80d3 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#41) from main into dev 2026-04-03 21:23:31 +00:00
2f50f7ee30 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#40) from main into dev 2026-04-03 21:20:20 +00:00
fce7e80c40 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#39) from main into dev 2026-04-03 21:17:33 +00:00
1baab6a293 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#38) from main into dev 2026-04-03 21:14:37 +00:00
44fefb6e93 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#37) from main into dev 2026-04-03 21:11:33 +00:00
24769f880c Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#36) from main into dev 2026-04-03 21:09:29 +00:00
c83ed5e044 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#35) from main into dev 2026-04-03 21:06:57 +00:00
43d85bdb8e Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#34) from main into dev 2026-04-03 21:03:44 +00:00
0b3efb56d5 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#33) from main into dev 2026-04-03 21:01:26 +00:00
479e5e2d81 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#32) from main into dev 2026-04-03 20:59:39 +00:00
dc6fc7b8b7 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#31) from main into dev 2026-04-03 20:56:49 +00:00
b80f7c9cc4 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#30) from main into dev 2026-04-03 20:54:00 +00:00
4c83997e82 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#29) from main into dev 2026-04-03 20:51:08 +00:00
158469368e Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#28) from main into dev 2026-04-03 20:48:25 +00:00
ade8dca586 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#27) from main into dev 2026-04-03 20:45:22 +00:00
fd15bdd54b Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#26) from main into dev 2026-04-03 20:42:34 +00:00
aa7951346a Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#25) from main into dev 2026-04-03 20:40:12 +00:00
7be5d1ee73 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#24) from main into dev 2026-04-03 20:38:13 +00:00
37c812af80 Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#23) from main into dev 2026-04-03 20:35:26 +00:00
9fb615feab Merge pull request '[agent/codex:gpt-5.4-mini] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#22) from main into dev 2026-04-03 20:32:12 +00:00
e835f193d5 Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#21) from main into dev 2026-04-03 20:15:44 +00:00
7c50080319 Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#20) from main into dev 2026-04-03 20:14:20 +00:00
2b8bfeb239 Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#19) from main into dev 2026-04-03 20:13:01 +00:00
85f8c28d3a Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#18) from main into dev 2026-04-03 20:11:40 +00:00
a0eaf99af0 Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#17) from main into dev 2026-04-03 20:10:28 +00:00
fb1bad845b Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#16) from main into dev 2026-04-03 20:08:50 +00:00
b6c840dcf2 Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#15) from main into dev 2026-04-03 20:07:35 +00:00
0303c988d0 Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#14) from main into dev 2026-04-03 20:06:13 +00:00
356158aa49 Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#13) from main into dev 2026-04-03 20:04:42 +00:00
73a54be669 Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#12) from main into dev 2026-04-03 20:03:14 +00:00
ced2b6700c Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#11) from main into dev 2026-04-03 20:01:26 +00:00
26f6af219f Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#10) from main into dev 2026-04-03 19:59:50 +00:00
741603e105 Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#9) from main into dev 2026-04-03 19:57:27 +00:00
a8ea73787c Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#8) from main into dev 2026-04-03 19:56:23 +00:00
f02c58420b Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#7) from main into dev 2026-04-03 19:54:52 +00:00
5f0f008a5e Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#6) from main into dev 2026-04-03 19:53:46 +00:00
8f0733a156 Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#5) from main into dev 2026-04-03 19:51:48 +00:00
4185f3d9be Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#4) from main into dev 2026-04-03 19:50:54 +00:00
9ce1f7d0b0 Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#3) from main into dev 2026-04-03 19:50:05 +00:00
3e9039667f Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#2) from main into dev 2026-04-03 19:49:16 +00:00
1d3b40017d Merge pull request '[agent/codex:gpt-5.3-codex-spark] Read docs/RFC.md fully. Find ONE feature described in the sp...' (#1) from agent/read-docs-rfc-md-fully--find-one-feature into dev 2026-04-03 19:48:25 +00:00
11 changed files with 296 additions and 2319 deletions

144
action.go
View file

@ -25,19 +25,10 @@ var (
)
const (
actionArgBind = "bind"
actionArgBindAddress = "bindAddress"
actionArgBindAddressSnake = "bind_address"
actionArgIP = "ip"
actionArgAddress = "address"
actionArgName = "name"
actionArgHost = "host"
actionArgHostName = "hostname"
actionArgPort = "port"
actionArgDNSPort = "dnsPort"
actionArgDNSPortSnake = "dns_port"
actionArgHealthPort = "health_port"
actionArgHealthPortCamel = "healthPort"
actionArgBind = "bind"
actionArgIP = "ip"
actionArgName = "name"
actionArgPort = "port"
)
type ActionDefinition struct {
@ -49,7 +40,7 @@ type ActionDefinition struct {
// ActionRegistrar publishes DNS actions into another Core surface.
//
// registrar.RegisterAction(ActionResolve, func(values map[string]any) (any, bool, error) {
// return service.ResolveAddresses("gateway.charon.lthn")
// return service.ResolveAddress("gateway.charon.lthn")
// })
type ActionRegistrar interface {
RegisterAction(name string, invoke func(map[string]any) (any, bool, error))
@ -84,7 +75,6 @@ type ActionCaller interface {
// for _, definition := range definitions {
// 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 {
return []ActionDefinition{
{
@ -147,13 +137,13 @@ func (service *Service) ActionDefinitions() []ActionDefinition {
if err := service.DiscoverAliases(context.Background()); err != nil {
return nil, false, err
}
return nil, true, nil
return service.Health(), true, nil
},
InvokeContext: func(ctx context.Context, _ map[string]any) (any, bool, error) {
if err := service.DiscoverAliases(ctx); err != nil {
return nil, false, err
}
return nil, true, nil
return service.Health(), true, nil
},
},
}
@ -176,7 +166,6 @@ func (service *Service) ActionNames() []string {
//
// service.RegisterActions(registrar)
// // 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) {
if registrar == nil {
return
@ -202,8 +191,6 @@ func (service *Service) RegisterActions(registrar ActionRegistrar) {
// 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)
// // 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 {
@ -213,14 +200,6 @@ func NewServiceWithRegistrar(options ServiceOptions, registrar ActionRegistrar)
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.
//
// payload, ok, err := service.HandleAction(ActionResolve, map[string]any{
@ -252,11 +231,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) {
_ = ctx
host, err := stringActionValueFromKeys(values, actionArgName, actionArgHost, actionArgHostName)
host, err := stringActionValue(values, actionArgName)
if err != nil {
return nil, false, err
}
result, ok := service.ResolveAddresses(host)
result, ok := service.ResolveAddress(host)
if !ok {
return nil, false, nil
}
@ -265,7 +244,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) {
_ = ctx
host, err := stringActionValueFromKeys(values, actionArgName, actionArgHost, actionArgHostName)
host, err := stringActionValue(values, actionArgName)
if err != nil {
return nil, false, err
}
@ -278,7 +257,7 @@ func (service *Service) handleResolveTXTRecords(ctx context.Context, values map[
func (service *Service) handleResolveAll(ctx context.Context, values map[string]any) (any, bool, error) {
_ = ctx
host, err := stringActionValueFromKeys(values, actionArgName, actionArgHost, actionArgHostName)
host, err := stringActionValue(values, actionArgName)
if err != nil {
return nil, false, err
}
@ -291,7 +270,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) {
_ = ctx
ip, err := stringActionValueFromKeys(values, actionArgIP, actionArgAddress, actionArgName, actionArgHost, actionArgHostName)
ip, err := stringActionValue(values, actionArgIP)
if err != nil {
return nil, false, err
}
@ -304,37 +283,17 @@ func (service *Service) handleReverseLookup(ctx context.Context, values map[stri
func (service *Service) handleServe(ctx context.Context, values map[string]any) (any, bool, error) {
_ = ctx
bind, _, err := stringActionValueOptionalFromKeys(values, actionArgBind, actionArgBindAddress, actionArgBindAddressSnake)
bind, err := stringActionValueOptional(values, actionArgBind)
if err != nil {
return nil, false, err
}
port, portProvided, err := intActionValueOptionalFromKeys(values, actionArgPort, actionArgDNSPort, actionArgDNSPortSnake)
port, portProvided, err := intActionValueOptional(values, actionArgPort)
if err != nil {
return nil, false, err
}
if !portProvided {
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)
if err != nil {
return nil, false, err
@ -360,46 +319,6 @@ func stringActionValue(values map[string]any, key string) (string, error) {
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) {
if values == nil {
return "", nil
@ -415,18 +334,6 @@ func stringActionValueOptional(values map[string]any, key string) (string, error
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) {
if values == nil {
return 0, errActionMissingValue
@ -513,26 +420,3 @@ func intActionValueOptional(values map[string]any, key string) (int, bool, error
}
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
View file

@ -2,10 +2,7 @@ module dappco.re/go/dns
go 1.22
require (
github.com/miekg/dns v1.1.62
github.com/patrickmn/go-cache v2.1.0+incompatible
)
require github.com/miekg/dns v1.1.62
require (
golang.org/x/mod v0.18.0 // indirect

2
go.sum
View file

@ -1,7 +1,5 @@
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/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/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=

166
hsd.go
View file

@ -28,20 +28,6 @@ type HSDClientOptions struct {
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 {
baseURL string
username string
@ -107,28 +93,6 @@ 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) {
normalized := strings.TrimSpace(name)
if normalized == "" {
@ -221,127 +185,51 @@ func (client *HSDClient) call(ctx context.Context, request HSDRPCRequest) (json.
}
func parseHSDNameResource(raw json.RawMessage) (NameRecords, error) {
var result NameRecords
var wrapper map[string]json.RawMessage
if err := json.Unmarshal(raw, &wrapper); err != nil {
return NameRecords{}, err
return result, err
}
if recordsRaw, ok := wrapper["records"]; ok {
result, err := parseHSDNameResourceRecords(recordsRaw)
if err != nil {
if err := json.Unmarshal(recordsRaw, &result); err != nil {
return NameRecords{}, err
}
return result, nil
}
if hasHSDNameResourceField(wrapper) {
result, err := parseHSDNameResourceRecords(raw)
if err != nil {
if _, ok := wrapper["a"]; ok {
if err := json.Unmarshal(raw, &result); err != nil {
return NameRecords{}, err
}
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")
}
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) {
var info HSDBlockchainInfo
var wrapper map[string]json.RawMessage

View file

@ -52,18 +52,6 @@ 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) {
server := httptest.NewServer(http.HandlerFunc(func(responseWriter http.ResponseWriter, request *http.Request) {
var payload struct {
@ -104,50 +92,6 @@ 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) {
server := httptest.NewServer(http.HandlerFunc(func(responseWriter http.ResponseWriter, request *http.Request) {
var payload struct {
@ -224,48 +168,6 @@ 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) {
server := httptest.NewServer(http.HandlerFunc(func(responseWriter http.ResponseWriter, request *http.Request) {
var payload struct {

View file

@ -34,13 +34,6 @@ func (server *HealthServer) Address() string {
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 {
if server == nil {
return nil
@ -72,7 +65,7 @@ func (service *Service) ServeHTTPHealth(bind string, port int) (*HealthServer, e
bind = "127.0.0.1"
}
if port <= 0 {
port = service.resolveHealthPort()
port = service.resolveHTTPPort()
}
address := net.JoinHostPort(bind, strconv.Itoa(port))

View file

@ -8,7 +8,6 @@ import (
"fmt"
"io"
"net/http"
"slices"
"strings"
)
@ -26,20 +25,6 @@ type MainchainClientOptions struct {
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 {
baseURL string
username string
@ -85,28 +70,6 @@ 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.
//
// client := dns.NewMainchainAliasClient(dns.MainchainClientOptions{
@ -259,39 +222,19 @@ func decodeString(raw json.RawMessage) (string, bool) {
}
func extractAliasFromComment(comment string) string {
fields := strings.Fields(comment)
for index, token := range fields {
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)
}
for _, token := range strings.Fields(comment) {
if strings.HasPrefix(token, "hns=") {
return normalizeName(strings.TrimSuffix(strings.TrimPrefix(token, "hns="), ";"))
}
}
commentLower := strings.ToLower(comment)
if marker := strings.Index(commentLower, "hns"); marker >= 0 {
alias := comment[marker+3:]
alias = strings.TrimLeft(alias, " \t:=;")
if trim := strings.IndexAny(alias, " ;,\t\r\n"); trim >= 0 {
if marker := strings.Index(comment, "hns="); marker >= 0 {
alias := comment[marker+4:]
if trim := strings.IndexAny(alias, " ;,"); trim >= 0 {
alias = alias[:trim]
}
alias = strings.TrimSpace(alias)
alias = strings.TrimSuffix(alias, ";")
alias = strings.TrimSuffix(alias, ",")
return normalizeName(alias)
}
@ -312,6 +255,5 @@ func normalizeAliasList(raw []string) []string {
seen[next] = true
normalized = append(normalized, next)
}
slices.Sort(normalized)
return normalized
}

View file

@ -54,18 +54,6 @@ 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) {
var chainCalls int32
var hsdTreeRootCalls int32
@ -571,48 +559,3 @@ 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))
}
}
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
View file

@ -30,60 +30,23 @@ type DNSServer struct {
// defer func() { _ = runtime.Close() }()
// fmt.Println(runtime.DNSAddress(), runtime.HealthAddress())
type ServiceRuntime struct {
// 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.
DNS *DNSServer
Health *HealthServer
// HTTP is retained for compatibility with older call sites.
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 {
if runtime == nil {
if runtime == nil || runtime.DNS == nil {
return ""
}
if runtime.DNSServer != nil {
return runtime.DNSServer.DNSAddress()
}
if runtime.DNS != nil {
return runtime.DNS.DNSAddress()
}
return ""
return runtime.DNS.DNSAddress()
}
func (runtime *ServiceRuntime) HealthAddress() string {
if runtime == nil {
return ""
}
if runtime.HealthServer != nil {
return runtime.HealthServer.HealthAddress()
}
if runtime.HTTPServer != nil {
return runtime.HTTPServer.HealthAddress()
}
if runtime.Health != nil {
return runtime.Health.HealthAddress()
}
@ -104,32 +67,17 @@ func (runtime *ServiceRuntime) Close() error {
}
var firstError error
if runtime.DNSServer != nil {
if err := runtime.DNSServer.Close(); err != nil && firstError == nil {
firstError = err
}
}
if runtime.DNS != nil && runtime.DNS != runtime.DNSServer {
if runtime.DNS != nil {
if err := runtime.DNS.Close(); err != nil && firstError == nil {
firstError = err
}
}
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 runtime.Health != nil {
if err := runtime.Health.Close(); err != nil && firstError == nil {
firstError = err
}
}
if runtime.HTTP != nil && runtime.HTTP != runtime.HealthServer && runtime.HTTP != runtime.Health {
if runtime.HTTP != nil && runtime.HTTP != runtime.Health {
if err := runtime.HTTP.Close(); err != nil && firstError == nil {
firstError = err
}
@ -156,18 +104,14 @@ func (server *DNSServer) Close() error {
if server.tcpListener != nil {
_ = server.tcpListener.Close()
}
var firstError error
var err error
if server.udpServer != nil {
if err := server.udpServer.Shutdown(); err != nil && firstError == nil {
firstError = err
}
err = server.udpServer.Shutdown()
}
if server.tcpServer != nil {
if err := server.tcpServer.Shutdown(); err != nil && firstError == nil {
firstError = err
}
err = server.tcpServer.Shutdown()
}
return firstError
return err
}
// ResolveDNSPort returns the DNS port used for `dns.serve` and `Serve`.
@ -175,50 +119,22 @@ func (server *DNSServer) Close() error {
// port := service.ResolveDNSPort()
// server, err := service.Serve("127.0.0.1", port)
func (service *Service) ResolveDNSPort() int {
if service == nil {
return DefaultDNSPort
}
if service.dnsPort <= 0 {
if service == nil || service.dnsPort <= 0 {
return DefaultDNSPort
}
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.
//
// port := service.DNSPort()
// server, err := service.Serve("127.0.0.1", port)
func (service *Service) DNSPort() int {
if service == nil {
return DefaultDNSPort
}
return service.ResolveDNSPort()
}
// 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.
// resolveServePort keeps internal callers aligned with existing behavior.
func (service *Service) resolveServePort() int {
if service == nil {
return DefaultDNSPort
}
return service.ResolveDNSPort()
}
@ -227,73 +143,22 @@ func (service *Service) resolveServePort() int {
// port := service.ResolveHTTPPort()
// healthServer, err := service.ServeHTTPHealth("127.0.0.1", port)
func (service *Service) ResolveHTTPPort() int {
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 {
if service == nil || service.httpPort <= 0 {
return DefaultHTTPPort
}
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.
//
// port := service.HTTPPort()
// healthServer, err := service.ServeHTTPHealth("127.0.0.1", port)
func (service *Service) HTTPPort() int {
return service.ResolveHealthPort()
return service.ResolveHTTPPort()
}
// 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 {
return service.ResolveHealthPort()
}
// resolveHealthPort is a legacy compatibility helper.
func (service *Service) resolveHealthPort() int {
return service.ResolveHealthPort()
return service.ResolveHTTPPort()
}
// Serve starts DNS over UDP and TCP.
@ -303,15 +168,9 @@ func (service *Service) resolveHealthPort() int {
// lookup := new(dnsprotocol.Msg)
// lookup.SetQuestion("gateway.charon.lthn.", dnsprotocol.TypeA)
func (service *Service) Serve(bind string, port int) (*DNSServer, error) {
if service == nil {
return nil, fmt.Errorf("service is required")
}
if bind == "" {
bind = "127.0.0.1"
}
if port <= 0 {
port = service.ResolveDNSPort()
}
addr := net.JoinHostPort(bind, strconv.Itoa(port))
udpListener, err := net.ListenPacket("udp", addr)
@ -348,41 +207,17 @@ func (service *Service) Serve(bind string, port int) (*DNSServer, error) {
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.
//
// runtime, err := service.ServeAll("127.0.0.1", 53, 5554)
// defer func() { _ = runtime.Close() }()
// fmt.Println("dns:", runtime.DNSAddress(), "health:", runtime.HealthAddress())
func (service *Service) ServeAll(bind string, dnsPort int, httpPort int) (*ServiceRuntime, error) {
if service == nil {
return nil, fmt.Errorf("service is required")
}
if dnsPort <= 0 {
dnsPort = service.resolveDNSListenPort()
if dnsPort == 0 {
dnsPort = service.dnsPort
}
if httpPort <= 0 {
httpPort = service.resolveHealthListenPort()
httpPort = service.resolveHTTPPort()
}
dnsServer, err := service.Serve(bind, dnsPort)
@ -397,12 +232,9 @@ func (service *Service) ServeAll(bind string, dnsPort int, httpPort int) (*Servi
}
return &ServiceRuntime{
DNSServer: dnsServer,
DNS: dnsServer,
HealthServer: httpServer,
Health: httpServer,
HTTP: httpServer,
HTTPServer: httpServer,
DNS: dnsServer,
Health: httpServer,
HTTP: httpServer,
}, nil
}
@ -414,19 +246,9 @@ func (service *Service) ServeAll(bind string, dnsPort int, httpPort int) (*Servi
// })
// runtime, err := service.ServeConfigured("127.0.0.1")
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)
}
// 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 {
service *Service
}
@ -463,7 +285,7 @@ func (handler *dnsRequestHandler) ServeDNS(responseWriter dnsprotocol.ResponseWr
if !found {
goto noRecord
}
for _, value := range normalizeRecordValues(record.A) {
for _, value := range record.A {
parsedIP := net.ParseIP(value)
if parsedIP == nil || parsedIP.To4() == nil {
continue
@ -477,7 +299,7 @@ func (handler *dnsRequestHandler) ServeDNS(responseWriter dnsprotocol.ResponseWr
if !found {
goto noRecord
}
for _, value := range normalizeRecordValues(record.AAAA) {
for _, value := range record.AAAA {
parsedIP := net.ParseIP(value)
if parsedIP == nil || parsedIP.To4() != nil {
continue
@ -491,7 +313,7 @@ func (handler *dnsRequestHandler) ServeDNS(responseWriter dnsprotocol.ResponseWr
if !found {
goto noRecord
}
for _, value := range normalizeRecordValues(record.TXT) {
for _, value := range record.TXT {
reply.Answer = append(reply.Answer, &dnsprotocol.TXT{
Hdr: dnsprotocol.RR_Header{Name: question.Name, Rrtype: dnsprotocol.TypeTXT, Class: dnsprotocol.ClassINET, Ttl: defaultDNSTTL},
Txt: []string{value},
@ -499,7 +321,7 @@ func (handler *dnsRequestHandler) ServeDNS(responseWriter dnsprotocol.ResponseWr
}
case dnsprotocol.TypeNS:
if found {
for _, value := range normalizeRecordValues(record.NS) {
for _, value := range record.NS {
reply.Answer = append(reply.Answer, &dnsprotocol.NS{
Hdr: dnsprotocol.RR_Header{Name: question.Name, Rrtype: dnsprotocol.TypeNS, Class: dnsprotocol.ClassINET, Ttl: defaultDNSTTL},
Ns: normalizeName(value) + ".",
@ -561,17 +383,17 @@ func (handler *dnsRequestHandler) ServeDNS(responseWriter dnsprotocol.ResponseWr
if !found {
goto noRecord
}
appendDNSSECResourceRecords(reply, question.Name, dnsprotocol.TypeDS, normalizeRecordValues(record.DS))
appendDNSSECResourceRecords(reply, question.Name, dnsprotocol.TypeDS, record.DS)
case dnsprotocol.TypeDNSKEY:
if !found {
goto noRecord
}
appendDNSSECResourceRecords(reply, question.Name, dnsprotocol.TypeDNSKEY, normalizeRecordValues(record.DNSKEY))
appendDNSSECResourceRecords(reply, question.Name, dnsprotocol.TypeDNSKEY, record.DNSKEY)
case dnsprotocol.TypeRRSIG:
if !found {
goto noRecord
}
appendDNSSECResourceRecords(reply, question.Name, dnsprotocol.TypeRRSIG, normalizeRecordValues(record.RRSIG))
appendDNSSECResourceRecords(reply, question.Name, dnsprotocol.TypeRRSIG, record.RRSIG)
default:
reply.SetRcode(request, dnsprotocol.RcodeNotImplemented)
_ = responseWriter.WriteMsg(reply)
@ -644,7 +466,7 @@ func parsePTRIP(name string) (string, bool) {
}
func appendAnyAnswers(reply *dnsprotocol.Msg, questionName string, lookupName string, record NameRecords, zoneApex string) {
for _, value := range normalizeRecordValues(record.A) {
for _, value := range record.A {
parsedIP := net.ParseIP(value)
if parsedIP == nil || parsedIP.To4() == nil {
continue
@ -655,7 +477,7 @@ func appendAnyAnswers(reply *dnsprotocol.Msg, questionName string, lookupName st
})
}
for _, value := range normalizeRecordValues(record.AAAA) {
for _, value := range record.AAAA {
parsedIP := net.ParseIP(value)
if parsedIP == nil || parsedIP.To4() != nil {
continue
@ -666,7 +488,7 @@ func appendAnyAnswers(reply *dnsprotocol.Msg, questionName string, lookupName st
})
}
for _, value := range normalizeRecordValues(record.TXT) {
for _, value := range record.TXT {
reply.Answer = append(reply.Answer, &dnsprotocol.TXT{
Hdr: dnsprotocol.RR_Header{Name: questionName, Rrtype: dnsprotocol.TypeTXT, Class: dnsprotocol.ClassINET, Ttl: defaultDNSTTL},
Txt: []string{value},
@ -674,7 +496,7 @@ func appendAnyAnswers(reply *dnsprotocol.Msg, questionName string, lookupName st
}
if len(record.NS) > 0 {
for _, value := range normalizeRecordValues(record.NS) {
for _, value := range record.NS {
reply.Answer = append(reply.Answer, &dnsprotocol.NS{
Hdr: dnsprotocol.RR_Header{Name: questionName, Rrtype: dnsprotocol.TypeNS, Class: dnsprotocol.ClassINET, Ttl: defaultDNSTTL},
Ns: normalizeName(value) + ".",
@ -682,15 +504,15 @@ func appendAnyAnswers(reply *dnsprotocol.Msg, questionName string, lookupName st
}
}
for _, value := range normalizeRecordValues(record.DNSKEY) {
for _, value := range record.DNSKEY {
appendDNSSECResourceRecords(reply, questionName, dnsprotocol.TypeDNSKEY, []string{value})
}
for _, value := range normalizeRecordValues(record.DS) {
for _, value := range record.DS {
appendDNSSECResourceRecords(reply, questionName, dnsprotocol.TypeDS, []string{value})
}
for _, value := range normalizeRecordValues(record.RRSIG) {
for _, value := range record.RRSIG {
appendDNSSECResourceRecords(reply, questionName, dnsprotocol.TypeRRSIG, []string{value})
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff