package main import ( "errors" "fmt" "io" "net" "strings" "time" "github.com/jedisct1/dlog" "github.com/miekg/dns" ) type PluginQueryLog struct { logger io.Writer format string ignoredQtypes []string } func (plugin *PluginQueryLog) Name() string { return "query_log" } func (plugin *PluginQueryLog) Description() string { return "Log DNS queries." } func (plugin *PluginQueryLog) Init(proxy *Proxy) error { plugin.logger = Logger(proxy.logMaxSize, proxy.logMaxAge, proxy.logMaxBackups, proxy.queryLogFile) plugin.format = proxy.queryLogFormat plugin.ignoredQtypes = proxy.queryLogIgnoredQtypes return nil } func (plugin *PluginQueryLog) Drop() error { return nil } func (plugin *PluginQueryLog) Reload() error { return nil } func (plugin *PluginQueryLog) Eval(pluginsState *PluginsState, msg *dns.Msg) error { var clientIPStr string switch pluginsState.clientProto { case "udp": clientIPStr = (*pluginsState.clientAddr).(*net.UDPAddr).IP.String() case "tcp", "local_doh": clientIPStr = (*pluginsState.clientAddr).(*net.TCPAddr).IP.String() default: // Ignore internal flow. return nil } question := msg.Question[0] qType, ok := dns.TypeToString[question.Qtype] if !ok { qType = string(qType) } if len(plugin.ignoredQtypes) > 0 { for _, ignoredQtype := range plugin.ignoredQtypes { if strings.EqualFold(ignoredQtype, qType) { return nil } } } qName := pluginsState.qName if pluginsState.cacheHit { pluginsState.serverName = "-" } else { switch pluginsState.returnCode { case PluginsReturnCodeSynth, PluginsReturnCodeCloak, PluginsReturnCodeParseError: pluginsState.serverName = "-" } } returnCode, ok := PluginsReturnCodeToString[pluginsState.returnCode] if !ok { returnCode = string(returnCode) } var requestDuration time.Duration if !pluginsState.requestStart.IsZero() && !pluginsState.requestEnd.IsZero() { requestDuration = pluginsState.requestEnd.Sub(pluginsState.requestStart) } var line string if plugin.format == "tsv" { now := time.Now() year, month, day := now.Date() hour, minute, second := now.Clock() tsStr := fmt.Sprintf("[%d-%02d-%02d %02d:%02d:%02d]", year, int(month), day, hour, minute, second) line = fmt.Sprintf( "%s\t%s\t%s\t%s\t%s\t%dms\t%s\n", tsStr, clientIPStr, StringQuote(qName), qType, returnCode, requestDuration/time.Millisecond, StringQuote(pluginsState.serverName), ) } else if plugin.format == "ltsv" { cached := 0 if pluginsState.cacheHit { cached = 1 } line = fmt.Sprintf("time:%d\thost:%s\tmessage:%s\ttype:%s\treturn:%s\tcached:%d\tduration:%d\tserver:%s\n", time.Now().Unix(), clientIPStr, StringQuote(qName), qType, returnCode, cached, requestDuration/time.Millisecond, StringQuote(pluginsState.serverName)) } else { dlog.Fatalf("Unexpected log format: [%s]", plugin.format) } if plugin.logger == nil { return errors.New("Log file not initialized") } _, _ = plugin.logger.Write([]byte(line)) return nil }