Skip to content

Commit bed22e8

Browse files
authored
Merge pull request #3 from blinklabs-io/feat/basic-config-and-server
feat: basic server functionality
2 parents ab9ac91 + 750fca1 commit bed22e8

File tree

6 files changed

+350
-2
lines changed

6 files changed

+350
-2
lines changed

cmd/chnsd/main.go

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,76 @@
11
// Copyright 2023 Blink Labs, LLC.
2-
//
2+
//
33
// Use of this source code is governed by an MIT-style
44
// license that can be found in the LICENSE file or at
55
// https://opensource.org/licenses/MIT.
66

77
package main
88

9-
func main() {}
9+
import (
10+
"flag"
11+
"fmt"
12+
"net/http"
13+
_ "net/http/pprof"
14+
"os"
15+
16+
"github.com/blinklabs-io/chnsd/internal/config"
17+
"github.com/blinklabs-io/chnsd/internal/dns"
18+
"github.com/blinklabs-io/chnsd/internal/logging"
19+
)
20+
21+
var cmdlineFlags struct {
22+
configFile string
23+
}
24+
25+
func main() {
26+
flag.StringVar(&cmdlineFlags.configFile, "config", "", "path to config file to load")
27+
flag.Parse()
28+
29+
// Load config
30+
cfg, err := config.Load(cmdlineFlags.configFile)
31+
if err != nil {
32+
fmt.Printf("Failed to load config: %s\n", err)
33+
os.Exit(1)
34+
}
35+
36+
// Configure logging
37+
logging.Setup()
38+
logger := logging.GetLogger()
39+
// Sync logger on exit
40+
defer func() {
41+
if err := logger.Sync(); err != nil {
42+
// We don't actually care about the error here, but we have to do something
43+
// to appease the linter
44+
return
45+
}
46+
}()
47+
48+
/*
49+
// Test node connection
50+
if oConn, err := node.GetConnection(); err != nil {
51+
logger.Fatalf("failed to connect to node: %s", err)
52+
} else {
53+
oConn.Close()
54+
}
55+
*/
56+
57+
// Start debug listener
58+
if cfg.Debug.ListenPort > 0 {
59+
logger.Infof("starting debug listener on %s:%d", cfg.Debug.ListenAddress, cfg.Debug.ListenPort)
60+
go func() {
61+
err := http.ListenAndServe(fmt.Sprintf("%s:%d", cfg.Debug.ListenAddress, cfg.Debug.ListenPort), nil)
62+
if err != nil {
63+
logger.Fatalf("failed to start debug listener: %s", err)
64+
}
65+
}()
66+
}
67+
68+
// Start DNS listener
69+
logger.Infof("starting DNS listener on %s:%d", cfg.Dns.ListenAddress, cfg.Dns.ListenPort)
70+
if err := dns.Start(); err != nil {
71+
logger.Fatalf("failed to start DNS listener: %s", err)
72+
}
73+
74+
// Wait forever
75+
select {}
76+
}

go.mod

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
11
module github.com/blinklabs-io/chnsd
22

33
go 1.19
4+
5+
require (
6+
github.com/blinklabs-io/gouroboros v0.47.0
7+
github.com/kelseyhightower/envconfig v1.4.0
8+
github.com/miekg/dns v1.1.55
9+
go.uber.org/zap v1.24.0
10+
gopkg.in/yaml.v2 v2.4.0
11+
)
12+
13+
require (
14+
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
15+
github.com/jinzhu/copier v0.3.5 // indirect
16+
github.com/pkg/errors v0.9.1 // indirect
17+
github.com/x448/float16 v0.8.4 // indirect
18+
go.uber.org/atomic v1.7.0 // indirect
19+
go.uber.org/multierr v1.6.0 // indirect
20+
golang.org/x/crypto v0.10.0 // indirect
21+
golang.org/x/mod v0.9.0 // indirect
22+
golang.org/x/net v0.10.0 // indirect
23+
golang.org/x/sys v0.9.0 // indirect
24+
golang.org/x/tools v0.7.0 // indirect
25+
)

go.sum

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
2+
github.com/blinklabs-io/gouroboros v0.47.0 h1:N8SoWoT6h6m+w60OU5qNGNyAHj/MxJzSZaw6cnR1Dc8=
3+
github.com/blinklabs-io/gouroboros v0.47.0/go.mod h1:J3Zf9Fx65LoLldIZB4+dZZpsUsGfwXMQQKQOhNBNlwY=
4+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
5+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
6+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
7+
github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
8+
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
9+
github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg=
10+
github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
11+
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
12+
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
13+
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
14+
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
15+
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
16+
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
17+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
18+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
19+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
20+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
21+
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
22+
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
23+
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
24+
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
25+
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
26+
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
27+
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
28+
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
29+
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
30+
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
31+
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
32+
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
33+
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
34+
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
35+
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
36+
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
37+
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
38+
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
39+
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
40+
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
41+
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
42+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
43+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
44+
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
45+
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
46+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

internal/config/config.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package config
2+
3+
import (
4+
"fmt"
5+
"os"
6+
7+
ouroboros "github.com/blinklabs-io/gouroboros"
8+
"github.com/kelseyhightower/envconfig"
9+
"gopkg.in/yaml.v2"
10+
)
11+
12+
type Config struct {
13+
Logging LoggingConfig `yaml:"logging"`
14+
Metrics MetricsConfig `yaml:"metrics"`
15+
Dns DnsConfig `yaml:"dns"`
16+
Debug DebugConfig `yaml:"debug"`
17+
Node NodeConfig `yaml:"node"`
18+
}
19+
20+
type LoggingConfig struct {
21+
Healthchecks bool `yaml:"healthchecks" envconfig:"LOGGING_HEALTHCHECKS"`
22+
Level string `yaml:"level" envconfig:"LOGGING_LEVEL"`
23+
}
24+
25+
type DnsConfig struct {
26+
ListenAddress string `yaml:"address" envconfig:"DNS_LISTEN_ADDRESS"`
27+
ListenPort uint `yaml:"port" envconfig:"DNS_LISTEN_PORT"`
28+
}
29+
30+
type DebugConfig struct {
31+
ListenAddress string `yaml:"address" envconfig:"DEBUG_ADDRESS"`
32+
ListenPort uint `yaml:"port" envconfig:"DEBUG_PORT"`
33+
}
34+
35+
type MetricsConfig struct {
36+
ListenAddress string `yaml:"address" envconfig:"METRICS_LISTEN_ADDRESS"`
37+
ListenPort uint `yaml:"port" envconfig:"METRICS_LISTEN_PORT"`
38+
}
39+
40+
type NodeConfig struct {
41+
Network string `yaml:"network" envconfig:"CARDANO_NETWORK"`
42+
NetworkMagic uint32 `yaml:"networkMagic" envconfig:"CARDANO_NODE_NETWORK_MAGIC"`
43+
Address string `yaml:"address" envconfig:"CARDANO_NODE_SOCKET_TCP_HOST"`
44+
Port uint `yaml:"port" envconfig:"CARDANO_NODE_SOCKET_TCP_PORT"`
45+
SocketPath string `yaml:"socketPath" envconfig:"CARDANO_NODE_SOCKET_PATH"`
46+
}
47+
48+
// Singleton config instance with default values
49+
var globalConfig = &Config{
50+
Logging: LoggingConfig{
51+
Level: "info",
52+
Healthchecks: false,
53+
},
54+
Dns: DnsConfig{
55+
ListenAddress: "",
56+
ListenPort: 8053,
57+
},
58+
Debug: DebugConfig{
59+
ListenAddress: "localhost",
60+
ListenPort: 0,
61+
},
62+
Metrics: MetricsConfig{
63+
ListenAddress: "",
64+
ListenPort: 8081,
65+
},
66+
Node: NodeConfig{
67+
Network: "mainnet",
68+
SocketPath: "/node-ipc/node.socket",
69+
},
70+
}
71+
72+
func Load(configFile string) (*Config, error) {
73+
// Load config file as YAML if provided
74+
if configFile != "" {
75+
buf, err := os.ReadFile(configFile)
76+
if err != nil {
77+
return nil, fmt.Errorf("error reading config file: %s", err)
78+
}
79+
err = yaml.Unmarshal(buf, globalConfig)
80+
if err != nil {
81+
return nil, fmt.Errorf("error parsing config file: %s", err)
82+
}
83+
}
84+
// Load config values from environment variables
85+
// We use "dummy" as the app name here to (mostly) prevent picking up env
86+
// vars that we hadn't explicitly specified in annotations above
87+
err := envconfig.Process("dummy", globalConfig)
88+
if err != nil {
89+
return nil, fmt.Errorf("error processing environment: %s", err)
90+
}
91+
// Populate network magic value from network name
92+
if globalConfig.Node.Network != "" {
93+
network := ouroboros.NetworkByName(globalConfig.Node.Network)
94+
if network == ouroboros.NetworkInvalid {
95+
return nil, fmt.Errorf("unknown network: %s", globalConfig.Node.Network)
96+
}
97+
globalConfig.Node.NetworkMagic = network.NetworkMagic
98+
}
99+
return globalConfig, nil
100+
}
101+
102+
// Config returns the global config instance
103+
func GetConfig() *Config {
104+
return globalConfig
105+
}

internal/dns/dns.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package dns
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/blinklabs-io/chnsd/internal/config"
7+
"github.com/blinklabs-io/chnsd/internal/logging"
8+
9+
"github.com/miekg/dns"
10+
)
11+
12+
func Start() error {
13+
cfg := config.GetConfig()
14+
listenAddr := fmt.Sprintf("%s:%d", cfg.Dns.ListenAddress, cfg.Dns.ListenPort)
15+
// Setup handler
16+
dns.HandleFunc(".", handleTest)
17+
// UDP listener
18+
serverUdp := &dns.Server{Addr: listenAddr, Net: "udp", TsigSecret: nil, ReusePort: true}
19+
go startListener(serverUdp)
20+
// TCP listener
21+
serverTcp := &dns.Server{Addr: listenAddr, Net: "tcp", TsigSecret: nil, ReusePort: true}
22+
go startListener(serverTcp)
23+
return nil
24+
}
25+
26+
func startListener(server *dns.Server) {
27+
if err := server.ListenAndServe(); err != nil {
28+
logging.GetLogger().Fatalf("failed to start DNS listener: %s", err)
29+
}
30+
}
31+
32+
func handleTest(w dns.ResponseWriter, r *dns.Msg) {
33+
logger := logging.GetLogger()
34+
m := new(dns.Msg)
35+
m.SetReply(r)
36+
37+
queryStr := fmt.Sprintf(
38+
"%s %s",
39+
dns.Type(r.Question[0].Qtype).String(),
40+
r.Question[0].Name,
41+
)
42+
43+
logger.Infof("request: %s", queryStr)
44+
45+
t := &dns.TXT{
46+
Hdr: dns.RR_Header{Name: "test.zone.", Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 0},
47+
Txt: []string{queryStr},
48+
}
49+
50+
m.Answer = append(m.Answer, t)
51+
if err := w.WriteMsg(m); err != nil {
52+
logger.Errorf("failed to write response: %s", err)
53+
}
54+
}

internal/logging/logging.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package logging
2+
3+
import (
4+
"log"
5+
"time"
6+
7+
"github.com/blinklabs-io/chnsd/internal/config"
8+
"go.uber.org/zap"
9+
"go.uber.org/zap/zapcore"
10+
)
11+
12+
type Logger = zap.SugaredLogger
13+
14+
var globalLogger *Logger
15+
16+
func Setup() {
17+
cfg := config.GetConfig()
18+
// Build our custom logging config
19+
loggerConfig := zap.NewProductionConfig()
20+
// Change timestamp key name
21+
loggerConfig.EncoderConfig.TimeKey = "timestamp"
22+
// Use a human readable time format
23+
loggerConfig.EncoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout(time.RFC3339)
24+
25+
// Set level
26+
if cfg.Logging.Level != "" {
27+
level, err := zapcore.ParseLevel(cfg.Logging.Level)
28+
if err != nil {
29+
log.Fatalf("error configuring logger: %s", err)
30+
}
31+
loggerConfig.Level.SetLevel(level)
32+
}
33+
34+
// Create the logger
35+
l, err := loggerConfig.Build()
36+
if err != nil {
37+
log.Fatal(err)
38+
}
39+
40+
// Store the "sugared" version of the logger
41+
globalLogger = l.Sugar()
42+
}
43+
44+
func GetLogger() *zap.SugaredLogger {
45+
return globalLogger
46+
}
47+
48+
func GetDesugaredLogger() *zap.Logger {
49+
return globalLogger.Desugar()
50+
}
51+
52+
func GetAccessLogger() *zap.Logger {
53+
return globalLogger.Desugar().With(zap.String("type", "access")).WithOptions(zap.WithCaller(false))
54+
}

0 commit comments

Comments
 (0)