| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | package config |
| 4 | |
| 5 | import ( |
| 6 | "fmt" |
| 7 | "strings" |
| 8 | ) |
| 9 | |
| 10 | // UnboundConfig represents Unbound configuration file. |
| 11 | type UnboundConfig struct { |
| 12 | cumulative string // statistics-cumulative |
| 13 | enable string // control-enable |
| 14 | iface string // control-interface |
| 15 | port string // control-port |
| 16 | useCert string // control-use-cert |
| 17 | keyFile string // control-key-file |
| 18 | certFile string // control-cert-file |
| 19 | } |
| 20 | |
| 21 | func (c UnboundConfig) String() string { |
| 22 | format := strings.Join([]string{ |
| 23 | "[", |
| 24 | `"statistics-cumulative": '%s', `, |
| 25 | `"control-enable": '%s', `, |
| 26 | `"control-interface": '%s', `, |
| 27 | `"control-port": '%s', `, |
| 28 | `"control-user-cert": '%s', `, |
| 29 | `"control-key-file": '%s', `, |
| 30 | `"control-cert-file": '%s'`, |
| 31 | "]", |
| 32 | }, "") |
| 33 | return fmt.Sprintf(format, c.cumulative, c.enable, c.iface, c.port, c.useCert, c.keyFile, c.certFile) |
| 34 | } |
| 35 | |
| 36 | func (c UnboundConfig) Empty() bool { return c == UnboundConfig{} } |
| 37 | func (c UnboundConfig) Cumulative() (bool, bool) { return c.cumulative == "yes", c.cumulative != "" } |
| 38 | func (c UnboundConfig) ControlEnabled() (bool, bool) { return c.enable == "yes", c.enable != "" } |
| 39 | func (c UnboundConfig) ControlInterface() (string, bool) { return c.iface, c.iface != "" } |
| 40 | func (c UnboundConfig) ControlPort() (string, bool) { return c.port, c.port != "" } |
| 41 | func (c UnboundConfig) ControlUseCert() (bool, bool) { return c.useCert == "yes", c.useCert != "" } |
| 42 | func (c UnboundConfig) ControlKeyFile() (string, bool) { return c.keyFile, c.keyFile != "" } |
| 43 | func (c UnboundConfig) ControlCertFile() (string, bool) { return c.certFile, c.certFile != "" } |
| 44 | |
| 45 | func fromOptions(options []option) *UnboundConfig { |
| 46 | cfg := &UnboundConfig{} |
| 47 | for _, opt := range options { |
| 48 | switch opt.name { |
| 49 | default: |
| 50 | case optInterface: |
| 51 | applyControlInterface(cfg, opt.value) |
| 52 | case optCumulative: |
| 53 | cfg.cumulative = opt.value |
| 54 | case optEnable: |
| 55 | cfg.enable = opt.value |
| 56 | case optPort: |
| 57 | cfg.port = opt.value |
| 58 | case optUseCert: |
| 59 | cfg.useCert = opt.value |
| 60 | case optKeyFile: |
| 61 | cfg.keyFile = opt.value |
| 62 | case optCertFile: |
| 63 | cfg.certFile = opt.value |
| 64 | } |
| 65 | } |
| 66 | return cfg |
| 67 | } |
| 68 | |
| 69 | // Unbound doesn't allow to query stats from unix socket when control-interface is enabled on ip interface. |
| 70 | func applyControlInterface(cfg *UnboundConfig, value string) { |
| 71 | if cfg.iface == "" || !isUnixSocket(value) || isUnixSocket(cfg.iface) { |
| 72 | cfg.iface = value |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | func isUnixSocket(address string) bool { |
| 77 | return strings.HasPrefix(address, "/") |
| 78 | } |