2019-08-26 11:54:38 +03:00
|
|
|
package querylog
|
|
|
|
|
|
|
|
import (
|
2023-03-23 13:46:57 +03:00
|
|
|
"fmt"
|
2019-08-26 11:54:38 +03:00
|
|
|
"net"
|
2021-04-02 17:30:39 +03:00
|
|
|
"path/filepath"
|
2023-03-31 18:44:51 +03:00
|
|
|
"sync"
|
2019-08-26 11:54:38 +03:00
|
|
|
"time"
|
|
|
|
|
2022-08-04 19:05:28 +03:00
|
|
|
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
2021-12-06 17:26:43 +03:00
|
|
|
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
2021-05-21 16:15:47 +03:00
|
|
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
2024-08-07 15:49:32 +03:00
|
|
|
"github.com/AdguardTeam/golibs/container"
|
2021-05-24 17:28:11 +03:00
|
|
|
"github.com/AdguardTeam/golibs/errors"
|
2019-08-26 11:54:38 +03:00
|
|
|
"github.com/miekg/dns"
|
|
|
|
)
|
|
|
|
|
|
|
|
// QueryLog - main interface
|
|
|
|
type QueryLog interface {
|
2020-01-16 14:25:40 +03:00
|
|
|
Start()
|
|
|
|
|
2019-09-04 14:12:00 +03:00
|
|
|
// Close query log object
|
2019-08-26 11:54:38 +03:00
|
|
|
Close()
|
|
|
|
|
2019-09-04 14:12:00 +03:00
|
|
|
// Add a log entry
|
2021-12-13 18:06:01 +03:00
|
|
|
Add(params *AddParams)
|
2019-09-04 14:12:00 +03:00
|
|
|
|
2019-09-27 18:58:57 +03:00
|
|
|
// WriteDiskConfig - write configuration
|
2020-05-28 15:29:36 +03:00
|
|
|
WriteDiskConfig(c *Config)
|
2023-02-07 17:50:39 +03:00
|
|
|
|
|
|
|
// ShouldLog returns true if request for the host should be logged.
|
2023-04-07 13:17:40 +03:00
|
|
|
ShouldLog(host string, qType, qClass uint16, ids []string) bool
|
2019-08-26 11:54:38 +03:00
|
|
|
}
|
|
|
|
|
2022-05-25 18:00:50 +03:00
|
|
|
// Config is the query log configuration structure.
|
2023-04-05 15:50:14 +03:00
|
|
|
//
|
|
|
|
// Do not alter any fields of this structure after using it.
|
2019-08-26 11:54:38 +03:00
|
|
|
type Config struct {
|
2023-09-05 15:13:35 +03:00
|
|
|
// Ignored contains the list of host names, which should not be written to
|
|
|
|
// log, and matches them.
|
|
|
|
Ignored *aghnet.IgnoreEngine
|
2023-03-31 18:44:51 +03:00
|
|
|
|
2022-05-25 18:00:50 +03:00
|
|
|
// Anonymizer processes the IP addresses to anonymize those if needed.
|
|
|
|
Anonymizer *aghnet.IPMut
|
|
|
|
|
2023-03-31 18:44:51 +03:00
|
|
|
// ConfigModified is called when the configuration is changed, for example
|
|
|
|
// by HTTP requests.
|
2019-09-27 18:58:57 +03:00
|
|
|
ConfigModified func()
|
|
|
|
|
2021-04-02 17:30:39 +03:00
|
|
|
// HTTPRegister registers an HTTP handler.
|
2022-08-04 19:05:28 +03:00
|
|
|
HTTPRegister aghhttp.RegisterFunc
|
2021-04-02 17:30:39 +03:00
|
|
|
|
|
|
|
// FindClient returns client information by their IDs.
|
|
|
|
FindClient func(ids []string) (c *Client, err error)
|
|
|
|
|
|
|
|
// BaseDir is the base directory for log files.
|
|
|
|
BaseDir string
|
|
|
|
|
2023-03-31 18:44:51 +03:00
|
|
|
// RotationIvl is the interval for log rotation. After that period, the old
|
|
|
|
// log file will be renamed, NOT deleted, so the actual log retention time
|
|
|
|
// is twice the interval.
|
2021-07-01 18:50:28 +03:00
|
|
|
RotationIvl time.Duration
|
2021-04-02 17:30:39 +03:00
|
|
|
|
2023-03-31 18:44:51 +03:00
|
|
|
// MemSize is the number of entries kept in a memory buffer before they are
|
|
|
|
// flushed to disk.
|
2024-01-11 18:38:30 +03:00
|
|
|
MemSize uint
|
2021-04-02 17:30:39 +03:00
|
|
|
|
|
|
|
// Enabled tells if the query log is enabled.
|
|
|
|
Enabled bool
|
|
|
|
|
|
|
|
// FileEnabled tells if the query log writes logs to files.
|
|
|
|
FileEnabled bool
|
|
|
|
|
|
|
|
// AnonymizeClientIP tells if the query log should anonymize clients' IP
|
|
|
|
// addresses.
|
|
|
|
AnonymizeClientIP bool
|
2019-08-26 11:54:38 +03:00
|
|
|
}
|
|
|
|
|
2021-12-07 17:43:51 +03:00
|
|
|
// AddParams is the parameters for adding an entry.
|
2019-11-21 16:13:19 +03:00
|
|
|
type AddParams struct {
|
2021-12-07 17:43:51 +03:00
|
|
|
Question *dns.Msg
|
2021-12-13 18:06:01 +03:00
|
|
|
|
2022-03-03 17:52:11 +03:00
|
|
|
// ReqECS is the IP network extracted from EDNS Client-Subnet option of a
|
|
|
|
// request.
|
|
|
|
ReqECS *net.IPNet
|
|
|
|
|
2021-12-07 17:43:51 +03:00
|
|
|
// Answer is the response which is sent to the client, if any.
|
|
|
|
Answer *dns.Msg
|
2021-12-13 18:06:01 +03:00
|
|
|
|
2021-12-07 17:43:51 +03:00
|
|
|
// OrigAnswer is the response from an upstream server. It's only set if the
|
|
|
|
// answer has been modified by filtering.
|
|
|
|
OrigAnswer *dns.Msg
|
2021-12-13 18:06:01 +03:00
|
|
|
|
2021-12-07 17:43:51 +03:00
|
|
|
// Result is the filtering result (optional).
|
|
|
|
Result *filtering.Result
|
2021-12-13 18:06:01 +03:00
|
|
|
|
2021-12-07 17:43:51 +03:00
|
|
|
ClientID string
|
2021-12-13 18:06:01 +03:00
|
|
|
|
2021-12-07 17:43:51 +03:00
|
|
|
// Upstream is the URL of the upstream DNS server.
|
2021-12-13 18:06:01 +03:00
|
|
|
Upstream string
|
|
|
|
|
2020-11-03 15:39:55 +03:00
|
|
|
ClientProto ClientProto
|
2021-12-13 18:06:01 +03:00
|
|
|
|
2022-05-25 18:00:50 +03:00
|
|
|
ClientIP net.IP
|
|
|
|
|
|
|
|
// Elapsed is the time spent for processing the request.
|
|
|
|
Elapsed time.Duration
|
|
|
|
|
2021-12-13 18:06:01 +03:00
|
|
|
// Cached indicates if the response is served from cache.
|
|
|
|
Cached bool
|
|
|
|
|
|
|
|
// AuthenticatedData shows if the response had the AD bit set.
|
|
|
|
AuthenticatedData bool
|
2019-11-21 16:13:19 +03:00
|
|
|
}
|
|
|
|
|
2021-04-02 17:30:39 +03:00
|
|
|
// validate returns an error if the parameters aren't valid.
|
|
|
|
func (p *AddParams) validate() (err error) {
|
|
|
|
switch {
|
|
|
|
case p.Question == nil:
|
2021-05-24 17:28:11 +03:00
|
|
|
return errors.Error("question is nil")
|
2021-04-02 17:30:39 +03:00
|
|
|
case len(p.Question.Question) != 1:
|
2021-05-24 17:28:11 +03:00
|
|
|
return errors.Error("more than one question")
|
2021-04-02 17:30:39 +03:00
|
|
|
case len(p.Question.Question[0].Name) == 0:
|
2021-05-24 17:28:11 +03:00
|
|
|
return errors.Error("no host in question")
|
2021-04-02 17:30:39 +03:00
|
|
|
case p.ClientIP == nil:
|
2021-05-24 17:28:11 +03:00
|
|
|
return errors.Error("no client ip")
|
2021-04-02 17:30:39 +03:00
|
|
|
default:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// New creates a new instance of the query log.
|
2023-03-23 13:46:57 +03:00
|
|
|
func New(conf Config) (ql QueryLog, err error) {
|
2019-08-26 11:54:38 +03:00
|
|
|
return newQueryLog(conf)
|
|
|
|
}
|
2021-04-02 17:30:39 +03:00
|
|
|
|
|
|
|
// newQueryLog crates a new queryLog.
|
2023-03-23 13:46:57 +03:00
|
|
|
func newQueryLog(conf Config) (l *queryLog, err error) {
|
2021-04-02 17:30:39 +03:00
|
|
|
findClient := conf.FindClient
|
|
|
|
if findClient == nil {
|
|
|
|
findClient = func(_ []string) (_ *Client, _ error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-11 18:38:30 +03:00
|
|
|
memSize := conf.MemSize
|
|
|
|
if memSize == 0 {
|
|
|
|
// If query log is enabled, we still need to write entries to a file.
|
|
|
|
// And all writing goes through a buffer.
|
|
|
|
memSize = 1
|
2023-10-03 20:50:43 +03:00
|
|
|
}
|
|
|
|
|
2021-04-02 17:30:39 +03:00
|
|
|
l = &queryLog{
|
|
|
|
findClient: findClient,
|
|
|
|
|
2024-08-07 15:49:32 +03:00
|
|
|
buffer: container.NewRingBuffer[*logEntry](memSize),
|
2023-10-03 20:50:43 +03:00
|
|
|
|
2023-03-31 18:44:51 +03:00
|
|
|
conf: &Config{},
|
|
|
|
confMu: &sync.RWMutex{},
|
|
|
|
logFile: filepath.Join(conf.BaseDir, queryLogFileName),
|
|
|
|
|
2021-12-06 17:26:43 +03:00
|
|
|
anonymizer: conf.Anonymizer,
|
2021-04-02 17:30:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
*l.conf = conf
|
|
|
|
|
2023-03-23 13:46:57 +03:00
|
|
|
err = validateIvl(conf.RotationIvl)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("unsupported interval: %w", err)
|
2021-04-02 17:30:39 +03:00
|
|
|
}
|
|
|
|
|
2023-03-23 13:46:57 +03:00
|
|
|
return l, nil
|
2021-04-02 17:30:39 +03:00
|
|
|
}
|