2017-09-26 17:45:33 +02:00
|
|
|
package dnstapio
|
|
|
|
|
|
|
|
|
|
import (
|
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
|
|
|
|
2018-04-22 21:40:33 +01:00
|
|
|
clog "github.com/coredns/coredns/plugin/pkg/log"
|
2018-04-19 07:41:56 +01:00
|
|
|
|
2017-09-26 17:45:33 +02:00
|
|
|
tap "github.com/dnstap/golang-dnstap"
|
2017-11-28 00:36:14 +03:00
|
|
|
fs "github.com/farsightsec/golang-framestream"
|
2017-09-26 17:45:33 +02:00
|
|
|
)
|
|
|
|
|
|
2018-04-22 21:40:33 +01:00
|
|
|
var log = clog.NewWithPlugin("dnstap")
|
|
|
|
|
|
2017-11-28 00:36:14 +03:00
|
|
|
const (
|
2017-12-06 13:36:04 +03:00
|
|
|
tcpWriteBufSize = 1024 * 1024
|
|
|
|
|
tcpTimeout = 4 * time.Second
|
|
|
|
|
flushTimeout = 1 * time.Second
|
|
|
|
|
queueSize = 10000
|
2017-11-28 00:36:14 +03:00
|
|
|
)
|
|
|
|
|
|
2020-10-12 19:10:35 +02:00
|
|
|
// Tapper interface is used in testing to mock the Dnstap method.
|
|
|
|
|
type Tapper interface {
|
|
|
|
|
Dnstap(tap.Dnstap)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// dio implements the Tapper interface.
|
|
|
|
|
type dio struct {
|
2017-12-01 13:16:14 +02:00
|
|
|
endpoint string
|
|
|
|
|
socket bool
|
|
|
|
|
conn net.Conn
|
2017-12-06 13:36:04 +03:00
|
|
|
enc *dnstapEncoder
|
2017-12-01 13:16:14 +02:00
|
|
|
queue chan tap.Dnstap
|
2017-12-06 13:36:04 +03:00
|
|
|
dropped uint32
|
2018-02-02 11:59:22 +02:00
|
|
|
quit chan struct{}
|
2017-09-26 17:45:33 +02:00
|
|
|
}
|
|
|
|
|
|
2020-10-12 19:10:35 +02:00
|
|
|
// New returns a new and initialized pointer to a dio.
|
|
|
|
|
func New(endpoint string, socket bool) *dio {
|
|
|
|
|
return &dio{
|
2017-12-01 13:16:14 +02:00
|
|
|
endpoint: endpoint,
|
|
|
|
|
socket: socket,
|
2017-12-06 13:36:04 +03:00
|
|
|
enc: newDnstapEncoder(&fs.EncoderOptions{
|
|
|
|
|
ContentType: []byte("protobuf:dnstap.Dnstap"),
|
|
|
|
|
Bidirectional: true,
|
|
|
|
|
}),
|
|
|
|
|
queue: make(chan tap.Dnstap, queueSize),
|
2018-02-02 11:59:22 +02:00
|
|
|
quit: make(chan struct{}),
|
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-10-12 19:10:35 +02:00
|
|
|
func (d *dio) newConnect() error {
|
2017-11-28 00:36:14 +03:00
|
|
|
var err error
|
2020-10-12 19:10:35 +02:00
|
|
|
if d.socket {
|
|
|
|
|
if d.conn, err = net.Dial("unix", d.endpoint); err != nil {
|
2017-12-06 13:36:04 +03:00
|
|
|
return err
|
|
|
|
|
}
|
2017-11-28 00:36:14 +03:00
|
|
|
} else {
|
2020-10-12 19:10:35 +02:00
|
|
|
if d.conn, err = net.DialTimeout("tcp", d.endpoint, tcpTimeout); err != nil {
|
2017-12-06 13:36:04 +03:00
|
|
|
return err
|
|
|
|
|
}
|
2020-10-12 19:10:35 +02:00
|
|
|
if tcpConn, ok := d.conn.(*net.TCPConn); ok {
|
2017-12-06 13:36:04 +03:00
|
|
|
tcpConn.SetWriteBuffer(tcpWriteBufSize)
|
|
|
|
|
tcpConn.SetNoDelay(false)
|
|
|
|
|
}
|
2017-11-28 00:36:14 +03:00
|
|
|
}
|
2017-12-06 13:36:04 +03:00
|
|
|
|
2020-10-12 19:10:35 +02:00
|
|
|
return d.enc.resetWriter(d.conn)
|
2017-09-26 17:45:33 +02:00
|
|
|
}
|
|
|
|
|
|
2019-08-23 00:44:25 +08:00
|
|
|
// Connect connects to the dnstap endpoint.
|
2020-10-12 19:10:35 +02:00
|
|
|
func (d *dio) Connect() {
|
|
|
|
|
if err := d.newConnect(); err != nil {
|
2018-04-19 07:41:56 +01:00
|
|
|
log.Error("No connection to dnstap endpoint")
|
2017-12-01 13:16:14 +02:00
|
|
|
}
|
2020-10-12 19:10:35 +02:00
|
|
|
go d.serve()
|
2017-12-01 13:16:14 +02:00
|
|
|
}
|
|
|
|
|
|
2017-09-26 17:45:33 +02:00
|
|
|
// Dnstap enqueues the payload for log.
|
2020-10-12 19:10:35 +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-10-12 19:10:35 +02:00
|
|
|
func (d *dio) closeConnection() {
|
|
|
|
|
d.enc.close()
|
|
|
|
|
if d.conn != nil {
|
|
|
|
|
d.conn.Close()
|
|
|
|
|
d.conn = nil
|
2017-12-06 13:36:04 +03:00
|
|
|
}
|
2017-12-01 13:16:14 +02:00
|
|
|
}
|
|
|
|
|
|
2017-11-28 00:36:14 +03:00
|
|
|
// Close waits until the I/O routine is finished to return.
|
2020-10-12 19:10:35 +02:00
|
|
|
func (d *dio) Close() { close(d.quit) }
|
2017-11-28 00:36:14 +03:00
|
|
|
|
2020-10-12 19:10:35 +02:00
|
|
|
func (d *dio) flushBuffer() {
|
|
|
|
|
if d.conn == nil {
|
|
|
|
|
if err := d.newConnect(); err != nil {
|
2017-12-01 13:16:14 +02:00
|
|
|
return
|
|
|
|
|
}
|
2018-04-19 07:41:56 +01:00
|
|
|
log.Info("Reconnected to dnstap")
|
2017-12-01 13:16:14 +02:00
|
|
|
}
|
2017-12-06 13:36:04 +03:00
|
|
|
|
2020-10-12 19:10:35 +02:00
|
|
|
if err := d.enc.flushBuffer(); err != nil {
|
2018-04-19 07:41:56 +01:00
|
|
|
log.Warningf("Connection lost: %s", err)
|
2020-10-12 19:10:35 +02:00
|
|
|
d.closeConnection()
|
|
|
|
|
if err := d.newConnect(); err != nil {
|
2018-04-19 07:41:56 +01:00
|
|
|
log.Errorf("Cannot connect to dnstap: %s", err)
|
2017-12-06 13:36:04 +03:00
|
|
|
} else {
|
2018-04-19 07:41:56 +01:00
|
|
|
log.Info("Reconnected to dnstap")
|
2017-12-01 13:16:14 +02:00
|
|
|
}
|
|
|
|
|
}
|
2017-12-06 13:36:04 +03:00
|
|
|
}
|
|
|
|
|
|
2020-10-12 19:10:35 +02:00
|
|
|
func (d *dio) write(payload *tap.Dnstap) {
|
|
|
|
|
if err := d.enc.writeMsg(payload); err != nil {
|
|
|
|
|
atomic.AddUint32(&d.dropped, 1)
|
2017-12-01 13:16:14 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-12 19:10:35 +02:00
|
|
|
func (d *dio) serve() {
|
2017-11-28 00:36:14 +03:00
|
|
|
timeout := time.After(flushTimeout)
|
2017-09-26 17:45:33 +02:00
|
|
|
for {
|
|
|
|
|
select {
|
2020-10-12 19:10:35 +02:00
|
|
|
case <-d.quit:
|
|
|
|
|
d.flushBuffer()
|
|
|
|
|
d.closeConnection()
|
2018-02-02 11:59:22 +02:00
|
|
|
return
|
2020-10-12 19:10:35 +02:00
|
|
|
case payload := <-d.queue:
|
|
|
|
|
d.write(&payload)
|
2017-11-28 00:36:14 +03:00
|
|
|
case <-timeout:
|
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-10-12 19:10:35 +02:00
|
|
|
d.flushBuffer()
|
2017-11-28 00:36:14 +03:00
|
|
|
timeout = time.After(flushTimeout)
|
2017-09-26 17:45:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|