...

Source file src/github.com/letsencrypt/boulder/observer/probers/dns/dns_conf.go

Documentation: github.com/letsencrypt/boulder/observer/probers/dns

     1  package probers
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/letsencrypt/boulder/observer/probers"
    10  	"github.com/letsencrypt/boulder/strictyaml"
    11  	"github.com/miekg/dns"
    12  	"github.com/prometheus/client_golang/prometheus"
    13  )
    14  
    15  var (
    16  	validQTypes = map[string]uint16{"A": 1, "TXT": 16, "AAAA": 28, "CAA": 257}
    17  )
    18  
    19  // DNSConf is exported to receive YAML configuration
    20  type DNSConf struct {
    21  	Proto   string `yaml:"protocol"`
    22  	Server  string `yaml:"server"`
    23  	Recurse bool   `yaml:"recurse"`
    24  	QName   string `yaml:"query_name"`
    25  	QType   string `yaml:"query_type"`
    26  }
    27  
    28  // Kind returns a name that uniquely identifies the `Kind` of `Configurer`.
    29  func (c DNSConf) Kind() string {
    30  	return "DNS"
    31  }
    32  
    33  // UnmarshalSettings constructs a DNSConf object from YAML as bytes.
    34  func (c DNSConf) UnmarshalSettings(settings []byte) (probers.Configurer, error) {
    35  	var conf DNSConf
    36  	err := strictyaml.Unmarshal(settings, &conf)
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  	return conf, nil
    41  }
    42  
    43  func (c DNSConf) validateServer() error {
    44  	server := strings.Trim(strings.ToLower(c.Server), " ")
    45  	// Ensure `server` contains a port.
    46  	host, port, err := net.SplitHostPort(server)
    47  	if err != nil || port == "" {
    48  		return fmt.Errorf(
    49  			"invalid `server`, %q, could not be split: %s", c.Server, err)
    50  	}
    51  	// Ensure `server` port is valid.
    52  	portNum, err := strconv.Atoi(port)
    53  	if err != nil {
    54  		return fmt.Errorf(
    55  			"invalid `server`, %q, port must be a number", c.Server)
    56  	}
    57  	if portNum <= 0 || portNum > 65535 {
    58  		return fmt.Errorf(
    59  			"invalid `server`, %q, port number must be one in [1-65535]", c.Server)
    60  	}
    61  	// Ensure `server` is a valid FQDN or IPv4 / IPv6 address.
    62  	IPv6 := net.ParseIP(host).To16()
    63  	IPv4 := net.ParseIP(host).To4()
    64  	FQDN := dns.IsFqdn(dns.Fqdn(host))
    65  	if IPv6 == nil && IPv4 == nil && !FQDN {
    66  		return fmt.Errorf(
    67  			"invalid `server`, %q, is not an FQDN or IPv4 / IPv6 address", c.Server)
    68  	}
    69  	return nil
    70  }
    71  
    72  func (c DNSConf) validateProto() error {
    73  	validProtos := []string{"udp", "tcp"}
    74  	proto := strings.Trim(strings.ToLower(c.Proto), " ")
    75  	for _, i := range validProtos {
    76  		if proto == i {
    77  			return nil
    78  		}
    79  	}
    80  	return fmt.Errorf(
    81  		"invalid `protocol`, got: %q, expected one in: %s", c.Proto, validProtos)
    82  }
    83  
    84  func (c DNSConf) validateQType() error {
    85  	validQTypes = map[string]uint16{"A": 1, "TXT": 16, "AAAA": 28, "CAA": 257}
    86  	qtype := strings.Trim(strings.ToUpper(c.QType), " ")
    87  	q := make([]string, 0, len(validQTypes))
    88  	for i := range validQTypes {
    89  		q = append(q, i)
    90  		if qtype == i {
    91  			return nil
    92  		}
    93  	}
    94  	return fmt.Errorf(
    95  		"invalid `query_type`, got: %q, expected one in %s", c.QType, q)
    96  }
    97  
    98  // MakeProber constructs a `DNSProbe` object from the contents of the
    99  // bound `DNSConf` object. If the `DNSConf` cannot be validated, an
   100  // error appropriate for end-user consumption is returned instead.
   101  func (c DNSConf) MakeProber(_ map[string]prometheus.Collector) (probers.Prober, error) {
   102  	// validate `query_name`
   103  	if !dns.IsFqdn(dns.Fqdn(c.QName)) {
   104  		return nil, fmt.Errorf(
   105  			"invalid `query_name`, %q is not an fqdn", c.QName)
   106  	}
   107  
   108  	// validate `server`
   109  	err := c.validateServer()
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  
   114  	// validate `protocol`
   115  	err = c.validateProto()
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  
   120  	// validate `query_type`
   121  	err = c.validateQType()
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  
   126  	return DNSProbe{
   127  		proto:   strings.Trim(strings.ToLower(c.Proto), " "),
   128  		recurse: c.Recurse,
   129  		qname:   c.QName,
   130  		server:  c.Server,
   131  		qtype:   validQTypes[strings.Trim(strings.ToUpper(c.QType), " ")],
   132  	}, nil
   133  }
   134  
   135  // Instrument is a no-op to implement the `Configurer` interface.
   136  func (c DNSConf) Instrument() map[string]prometheus.Collector {
   137  	return nil
   138  }
   139  
   140  // init is called at runtime and registers `DNSConf`, a `Prober`
   141  // `Configurer` type, as "DNS".
   142  func init() {
   143  	probers.Register(DNSConf{})
   144  }
   145  

View as plain text