| 
									
										
										
										
											2020-11-05 14:37:16 +01:00
										 |  |  | package dnstap
 | 
					
						
							| 
									
										
										
										
											2017-09-26 17:45:33 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | import (
 | 
					
						
							| 
									
										
										
										
											2023-02-21 00:34:48 +01:00
										 |  |  | 	"crypto/tls"
 | 
					
						
							| 
									
										
										
										
											2017-11-28 00:36:14 +03:00
										 |  |  | 	"net"
 | 
					
						
							| 
									
										
										
										
											2017-12-06 13:36:04 +03:00
										 |  |  | 	"sync/atomic"
 | 
					
						
							| 
									
										
										
										
											2017-11-28 00:36:14 +03:00
										 |  |  | 	"time"
 | 
					
						
							| 
									
										
										
										
											2017-09-26 17:45:33 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	tap "github.com/dnstap/golang-dnstap"
 | 
					
						
							|  |  |  | )
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-28 00:36:14 +03:00
										 |  |  | const (
 | 
					
						
							| 
									
										
										
										
											2020-11-05 14:37:16 +01:00
										 |  |  | 	tcpWriteBufSize = 1024 * 1024 // there is no good explanation for why this number has this value.
 | 
					
						
							|  |  |  | 	queueSize       = 10000       // idem.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tcpTimeout   = 4 * time.Second
 | 
					
						
							|  |  |  | 	flushTimeout = 1 * time.Second
 | 
					
						
							| 
									
										
										
										
											2023-02-21 00:34:48 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	skipVerify = false // by default, every tls connection is verified to be secure
 | 
					
						
							| 
									
										
										
										
											2017-11-28 00:36:14 +03:00
										 |  |  | )
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-05 14:37:16 +01:00
										 |  |  | // tapper interface is used in testing to mock the Dnstap method.
 | 
					
						
							|  |  |  | type tapper interface {
 | 
					
						
							| 
									
										
										
										
											2022-07-10 20:06:33 +02:00
										 |  |  | 	Dnstap(*tap.Dnstap)
 | 
					
						
							| 
									
										
										
										
											2020-10-12 19:10:35 +02:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // dio implements the Tapper interface.
 | 
					
						
							|  |  |  | type dio struct {
 | 
					
						
							| 
									
										
										
										
											2020-11-03 15:31:34 +01:00
										 |  |  | 	endpoint     string
 | 
					
						
							|  |  |  | 	proto        string
 | 
					
						
							| 
									
										
										
										
											2020-11-05 14:37:16 +01:00
										 |  |  | 	enc          *encoder
 | 
					
						
							| 
									
										
										
										
											2022-07-10 20:06:33 +02:00
										 |  |  | 	queue        chan *tap.Dnstap
 | 
					
						
							| 
									
										
										
										
											2020-11-03 15:31:34 +01:00
										 |  |  | 	dropped      uint32
 | 
					
						
							|  |  |  | 	quit         chan struct{}
 | 
					
						
							|  |  |  | 	flushTimeout time.Duration
 | 
					
						
							|  |  |  | 	tcpTimeout   time.Duration
 | 
					
						
							| 
									
										
										
										
											2023-02-21 00:34:48 +01:00
										 |  |  | 	skipVerify   bool
 | 
					
						
							| 
									
										
										
										
											2017-09-26 17:45:33 +02:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-05 14:37:16 +01:00
										 |  |  | // newIO returns a new and initialized pointer to a dio.
 | 
					
						
							|  |  |  | func newIO(proto, endpoint string) *dio {
 | 
					
						
							| 
									
										
										
										
											2020-10-12 19:10:35 +02:00
										 |  |  | 	return &dio{
 | 
					
						
							| 
									
										
										
										
											2020-11-03 15:31:34 +01:00
										 |  |  | 		endpoint:     endpoint,
 | 
					
						
							|  |  |  | 		proto:        proto,
 | 
					
						
							| 
									
										
										
										
											2022-07-10 20:06:33 +02:00
										 |  |  | 		queue:        make(chan *tap.Dnstap, queueSize),
 | 
					
						
							| 
									
										
										
										
											2020-11-03 15:31:34 +01:00
										 |  |  | 		quit:         make(chan struct{}),
 | 
					
						
							|  |  |  | 		flushTimeout: flushTimeout,
 | 
					
						
							|  |  |  | 		tcpTimeout:   tcpTimeout,
 | 
					
						
							| 
									
										
										
										
											2023-02-21 00:34:48 +01:00
										 |  |  | 		skipVerify:   skipVerify,
 | 
					
						
							| 
									
										
										
										
											2017-12-01 13:16:14 +02:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2017-11-28 00:36:14 +03:00
										 |  |  | }
 | 
					
						
							| 
									
										
										
										
											2017-09-26 17:45:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-03 15:31:34 +01:00
										 |  |  | func (d *dio) dial() error {
 | 
					
						
							| 
									
										
										
										
											2023-02-21 00:34:48 +01:00
										 |  |  | 	var conn net.Conn
 | 
					
						
							|  |  |  | 	var err error
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if d.proto == "tls" {
 | 
					
						
							|  |  |  | 		config := &tls.Config{
 | 
					
						
							|  |  |  | 			InsecureSkipVerify: d.skipVerify,
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		dialer := &net.Dialer{
 | 
					
						
							|  |  |  | 			Timeout: d.tcpTimeout,
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		conn, err = tls.DialWithDialer(dialer, "tcp", d.endpoint, config)
 | 
					
						
							|  |  |  | 		if err != nil {
 | 
					
						
							|  |  |  | 			return err
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	} else {
 | 
					
						
							|  |  |  | 		conn, err = net.DialTimeout(d.proto, d.endpoint, d.tcpTimeout)
 | 
					
						
							|  |  |  | 		if err != nil {
 | 
					
						
							|  |  |  | 			return err
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2020-11-03 15:31:34 +01:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2023-02-21 00:34:48 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-03 15:31:34 +01:00
										 |  |  | 	if tcpConn, ok := conn.(*net.TCPConn); ok {
 | 
					
						
							|  |  |  | 		tcpConn.SetWriteBuffer(tcpWriteBufSize)
 | 
					
						
							|  |  |  | 		tcpConn.SetNoDelay(false)
 | 
					
						
							| 
									
										
										
										
											2017-11-28 00:36:14 +03:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2017-12-06 13:36:04 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-03 15:31:34 +01:00
										 |  |  | 	d.enc, err = newEncoder(conn, d.tcpTimeout)
 | 
					
						
							|  |  |  | 	return err
 | 
					
						
							| 
									
										
										
										
											2017-09-26 17:45:33 +02:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-23 00:44:25 +08:00
										 |  |  | // Connect connects to the dnstap endpoint.
 | 
					
						
							| 
									
										
										
										
											2020-11-05 14:37:16 +01:00
										 |  |  | func (d *dio) connect() error {
 | 
					
						
							|  |  |  | 	err := d.dial()
 | 
					
						
							| 
									
										
										
										
											2020-10-12 19:10:35 +02:00
										 |  |  | 	go d.serve()
 | 
					
						
							| 
									
										
										
										
											2020-11-05 14:37:16 +01:00
										 |  |  | 	return err
 | 
					
						
							| 
									
										
										
										
											2017-12-01 13:16:14 +02:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-26 17:45:33 +02:00
										 |  |  | // Dnstap enqueues the payload for log.
 | 
					
						
							| 
									
										
										
										
											2022-07-10 20:06:33 +02:00
										 |  |  | func (d *dio) Dnstap(payload *tap.Dnstap) {
 | 
					
						
							| 
									
										
										
										
											2017-09-26 17:45:33 +02:00
										 |  |  | 	select {
 | 
					
						
							| 
									
										
										
										
											2020-10-12 19:10:35 +02:00
										 |  |  | 	case d.queue <- payload:
 | 
					
						
							| 
									
										
										
										
											2017-09-26 17:45:33 +02:00
										 |  |  | 	default:
 | 
					
						
							| 
									
										
										
										
											2020-10-12 19:10:35 +02:00
										 |  |  | 		atomic.AddUint32(&d.dropped, 1)
 | 
					
						
							| 
									
										
										
										
											2017-09-26 17:45:33 +02:00
										 |  |  | 	}
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-05 14:37:16 +01:00
										 |  |  | // close waits until the I/O routine is finished to return.
 | 
					
						
							|  |  |  | func (d *dio) close() { close(d.quit) }
 | 
					
						
							| 
									
										
										
										
											2017-11-28 00:36:14 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-03 15:31:34 +01:00
										 |  |  | func (d *dio) write(payload *tap.Dnstap) error {
 | 
					
						
							|  |  |  | 	if d.enc == nil {
 | 
					
						
							|  |  |  | 		atomic.AddUint32(&d.dropped, 1)
 | 
					
						
							|  |  |  | 		return nil
 | 
					
						
							| 
									
										
										
										
											2017-12-01 13:16:14 +02:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2020-10-12 19:10:35 +02:00
										 |  |  | 	if err := d.enc.writeMsg(payload); err != nil {
 | 
					
						
							|  |  |  | 		atomic.AddUint32(&d.dropped, 1)
 | 
					
						
							| 
									
										
										
										
											2020-11-03 15:31:34 +01:00
										 |  |  | 		return err
 | 
					
						
							| 
									
										
										
										
											2017-12-01 13:16:14 +02:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2020-11-03 15:31:34 +01:00
										 |  |  | 	return nil
 | 
					
						
							| 
									
										
										
										
											2017-12-01 13:16:14 +02:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-12 19:10:35 +02:00
										 |  |  | func (d *dio) serve() {
 | 
					
						
							| 
									
										
										
										
											2022-03-04 02:36:02 -05:00
										 |  |  | 	timeout := time.NewTimer(d.flushTimeout)
 | 
					
						
							|  |  |  | 	defer timeout.Stop()
 | 
					
						
							| 
									
										
										
										
											2017-09-26 17:45:33 +02:00
										 |  |  | 	for {
 | 
					
						
							| 
									
										
										
										
											2022-03-04 02:36:02 -05:00
										 |  |  | 		timeout.Reset(d.flushTimeout)
 | 
					
						
							| 
									
										
										
										
											2017-09-26 17:45:33 +02:00
										 |  |  | 		select {
 | 
					
						
							| 
									
										
										
										
											2020-10-12 19:10:35 +02:00
										 |  |  | 		case <-d.quit:
 | 
					
						
							| 
									
										
										
										
											2020-11-03 15:31:34 +01:00
										 |  |  | 			if d.enc == nil {
 | 
					
						
							|  |  |  | 				return
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 			d.enc.flush()
 | 
					
						
							|  |  |  | 			d.enc.close()
 | 
					
						
							| 
									
										
										
										
											2018-02-02 11:59:22 +02:00
										 |  |  | 			return
 | 
					
						
							| 
									
										
										
										
											2020-10-12 19:10:35 +02:00
										 |  |  | 		case payload := <-d.queue:
 | 
					
						
							| 
									
										
										
										
											2022-07-10 20:06:33 +02:00
										 |  |  | 			if err := d.write(payload); err != nil {
 | 
					
						
							| 
									
										
										
										
											2020-11-03 15:31:34 +01:00
										 |  |  | 				d.dial()
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							| 
									
										
										
										
											2022-03-04 02:36:02 -05:00
										 |  |  | 		case <-timeout.C:
 | 
					
						
							| 
									
										
										
										
											2020-10-12 19:10:35 +02:00
										 |  |  | 			if dropped := atomic.SwapUint32(&d.dropped, 0); dropped > 0 {
 | 
					
						
							| 
									
										
										
										
											2018-04-19 07:41:56 +01:00
										 |  |  | 				log.Warningf("Dropped dnstap messages: %d", dropped)
 | 
					
						
							| 
									
										
										
										
											2017-12-06 13:36:04 +03:00
										 |  |  | 			}
 | 
					
						
							| 
									
										
										
										
											2020-11-03 15:31:34 +01:00
										 |  |  | 			if d.enc == nil {
 | 
					
						
							|  |  |  | 				d.dial()
 | 
					
						
							|  |  |  | 			} else {
 | 
					
						
							|  |  |  | 				d.enc.flush()
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							| 
									
										
										
										
											2017-09-26 17:45:33 +02:00
										 |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | }
 |