2017-09-26 17:45:33 +02:00
|
|
|
package dnstapio
|
|
|
|
|
|
|
|
|
|
import (
|
2017-09-29 21:29:33 +02:00
|
|
|
"log"
|
2017-11-28 00:36:14 +03:00
|
|
|
"net"
|
|
|
|
|
"time"
|
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
|
|
|
"github.com/golang/protobuf/proto"
|
|
|
|
|
)
|
|
|
|
|
|
2017-11-28 00:36:14 +03:00
|
|
|
const (
|
|
|
|
|
tcpTimeout = 4 * time.Second
|
|
|
|
|
flushTimeout = 1 * time.Second
|
|
|
|
|
queueSize = 1000
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type dnstapIO struct {
|
|
|
|
|
enc *fs.Encoder
|
|
|
|
|
conn net.Conn
|
|
|
|
|
queue chan tap.Dnstap
|
2017-09-26 17:45:33 +02:00
|
|
|
}
|
|
|
|
|
|
2017-11-28 00:36:14 +03:00
|
|
|
// New returns a new and initialized DnstapIO.
|
|
|
|
|
func New() DnstapIO {
|
|
|
|
|
return &dnstapIO{queue: make(chan tap.Dnstap, queueSize)}
|
|
|
|
|
}
|
2017-09-26 17:45:33 +02:00
|
|
|
|
2017-11-28 00:36:14 +03:00
|
|
|
// DnstapIO interface
|
|
|
|
|
type DnstapIO interface {
|
|
|
|
|
Connect(endpoint string, socket bool) error
|
|
|
|
|
Dnstap(payload tap.Dnstap)
|
|
|
|
|
Close()
|
2017-09-26 17:45:33 +02:00
|
|
|
}
|
|
|
|
|
|
2017-11-28 00:36:14 +03:00
|
|
|
// Connect connects to the dnstop endpoint.
|
|
|
|
|
func (dio *dnstapIO) Connect(endpoint string, socket bool) error {
|
|
|
|
|
var err error
|
|
|
|
|
if socket {
|
|
|
|
|
dio.conn, err = net.Dial("unix", endpoint)
|
|
|
|
|
} else {
|
|
|
|
|
dio.conn, err = net.DialTimeout("tcp", endpoint, tcpTimeout)
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
dio.enc, err = fs.NewEncoder(dio.conn, &fs.EncoderOptions{
|
|
|
|
|
ContentType: []byte("protobuf:dnstap.Dnstap"),
|
|
|
|
|
Bidirectional: true,
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2017-09-26 17:45:33 +02:00
|
|
|
go dio.serve()
|
2017-11-28 00:36:14 +03:00
|
|
|
return nil
|
2017-09-26 17:45:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Dnstap enqueues the payload for log.
|
2017-11-28 00:36:14 +03:00
|
|
|
func (dio *dnstapIO) Dnstap(payload tap.Dnstap) {
|
2017-09-26 17:45:33 +02:00
|
|
|
select {
|
|
|
|
|
case dio.queue <- payload:
|
|
|
|
|
default:
|
2017-11-28 00:36:14 +03:00
|
|
|
log.Printf("[ERROR] Dnstap payload dropped")
|
2017-09-26 17:45:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-28 00:36:14 +03:00
|
|
|
// Close waits until the I/O routine is finished to return.
|
|
|
|
|
func (dio *dnstapIO) Close() {
|
|
|
|
|
close(dio.queue)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (dio *dnstapIO) serve() {
|
|
|
|
|
timeout := time.After(flushTimeout)
|
2017-09-26 17:45:33 +02:00
|
|
|
for {
|
|
|
|
|
select {
|
2017-11-28 00:36:14 +03:00
|
|
|
case payload, ok := <-dio.queue:
|
|
|
|
|
if !ok {
|
|
|
|
|
dio.enc.Close()
|
|
|
|
|
dio.conn.Close()
|
|
|
|
|
return
|
|
|
|
|
}
|
2017-09-26 17:45:33 +02:00
|
|
|
frame, err := proto.Marshal(&payload)
|
2017-11-28 00:36:14 +03:00
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("[ERROR] Invalid dnstap payload dropped: %s", err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
_, err = dio.enc.Write(frame)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("[ERROR] Cannot write dnstap payload: %s", err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
case <-timeout:
|
|
|
|
|
err := dio.enc.Flush()
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("[ERROR] Cannot flush dnstap payloads: %s", err)
|
2017-09-26 17:45:33 +02:00
|
|
|
}
|
2017-11-28 00:36:14 +03:00
|
|
|
timeout = time.After(flushTimeout)
|
2017-09-26 17:45:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|