/* * Copyright (c) 2013-2014 by Farsight Security, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dnstap import ( "bytes" "fmt" "net" "strconv" "time" "github.com/miekg/dns" ) const quietTimeFormat = "15:04:05" func textConvertTime(s *bytes.Buffer, secs *uint64, nsecs *uint32) { if secs != nil { s.WriteString(time.Unix(int64(*secs), 0).Format(quietTimeFormat)) } else { s.WriteString("??:??:??") } if nsecs != nil { s.WriteString(fmt.Sprintf(".%06d", *nsecs/1000)) } else { s.WriteString(".??????") } } func textConvertIP(s *bytes.Buffer, ip []byte) { if ip != nil { s.WriteString(net.IP(ip).String()) } else { s.WriteString("MISSING_ADDRESS") } } func textConvertMessage(m *Message, s *bytes.Buffer) { isQuery := false printQueryAddress := false switch *m.Type { case Message_CLIENT_QUERY, Message_RESOLVER_QUERY, Message_AUTH_QUERY, Message_FORWARDER_QUERY, Message_TOOL_QUERY, Message_UPDATE_QUERY: isQuery = true case Message_CLIENT_RESPONSE, Message_RESOLVER_RESPONSE, Message_AUTH_RESPONSE, Message_FORWARDER_RESPONSE, Message_TOOL_RESPONSE, Message_UPDATE_RESPONSE: isQuery = false default: s.WriteString("[unhandled Message.Type]\n") return } if isQuery { textConvertTime(s, m.QueryTimeSec, m.QueryTimeNsec) } else { textConvertTime(s, m.ResponseTimeSec, m.ResponseTimeNsec) } s.WriteString(" ") switch *m.Type { case Message_CLIENT_QUERY, Message_CLIENT_RESPONSE: { s.WriteString("C") } case Message_RESOLVER_QUERY, Message_RESOLVER_RESPONSE: { s.WriteString("R") } case Message_AUTH_QUERY, Message_AUTH_RESPONSE: { s.WriteString("A") } case Message_FORWARDER_QUERY, Message_FORWARDER_RESPONSE: { s.WriteString("F") } case Message_STUB_QUERY, Message_STUB_RESPONSE: { s.WriteString("S") } case Message_TOOL_QUERY, Message_TOOL_RESPONSE: { s.WriteString("T") } case Message_UPDATE_QUERY, Message_UPDATE_RESPONSE: { s.WriteString("U") } } if isQuery { s.WriteString("Q ") } else { s.WriteString("R ") } switch *m.Type { case Message_CLIENT_QUERY, Message_CLIENT_RESPONSE, Message_AUTH_QUERY, Message_AUTH_RESPONSE: printQueryAddress = true } if printQueryAddress { textConvertIP(s, m.QueryAddress) } else { textConvertIP(s, m.ResponseAddress) } s.WriteString(" ") if m.SocketProtocol != nil { s.WriteString(m.SocketProtocol.String()) } s.WriteString(" ") var err error msg := new(dns.Msg) if isQuery { s.WriteString(strconv.Itoa(len(m.QueryMessage))) s.WriteString("b ") err = msg.Unpack(m.QueryMessage) } else { s.WriteString(strconv.Itoa(len(m.ResponseMessage))) s.WriteString("b ") err = msg.Unpack(m.ResponseMessage) } if err != nil || len(msg.Question) == 0 { s.WriteString("X ") } else { s.WriteString("\"" + msg.Question[0].Name + "\" ") s.WriteString(dns.Class(msg.Question[0].Qclass).String() + " ") s.WriteString(dns.Type(msg.Question[0].Qtype).String()) } s.WriteString("\n") } // TextFormat renders a dnstap message in a compact human-readable text // form. func TextFormat(dt *Dnstap) (out []byte, ok bool) { var s bytes.Buffer if *dt.Type == Dnstap_MESSAGE { textConvertMessage(dt.Message, &s) return s.Bytes(), true } return nil, false }