Files
dns-over-https/json-dns/marshal.go
2017-10-04 12:54:26 +08:00

116 lines
3.4 KiB
Go

/*
DNS-over-HTTPS
Copyright (C) 2017 Star Brilliant <m13253@hotmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package jsonDNS
import (
"net"
"strconv"
"strings"
"time"
"github.com/miekg/dns"
)
func Marshal(msg *dns.Msg) *Response {
now := time.Now().UTC()
resp := new(Response)
resp.Status = uint32(msg.Rcode)
resp.TC = msg.Truncated
resp.RD = msg.RecursionDesired
resp.RA = msg.RecursionAvailable
resp.AD = msg.AuthenticatedData
resp.CD = msg.CheckingDisabled
resp.Question = make([]Question, 0, len(msg.Question))
for _, question := range msg.Question {
jsonQuestion := Question {
Name: question.Name,
Type: question.Qtype,
}
resp.Question = append(resp.Question, jsonQuestion)
}
resp.Answer = make([]RR, 0, len(msg.Answer))
for _, rr := range msg.Answer {
jsonAnswer := marshalRR(rr, now)
if !resp.HaveTTL || jsonAnswer.TTL < resp.LeastTTL {
resp.HaveTTL = true
resp.LeastTTL = jsonAnswer.TTL
resp.EarliestExpires = jsonAnswer.Expires
}
resp.Answer = append(resp.Answer, jsonAnswer)
}
resp.Authority = make([]RR, 0, len(msg.Ns))
for _, rr := range msg.Ns {
jsonAuthority := marshalRR(rr, now)
if !resp.HaveTTL || jsonAuthority.TTL < resp.LeastTTL {
resp.HaveTTL = true
resp.LeastTTL = jsonAuthority.TTL
resp.EarliestExpires = jsonAuthority.Expires
}
resp.Authority = append(resp.Authority, jsonAuthority)
}
resp.Additional = make([]RR, 0, len(msg.Extra))
for _, rr := range msg.Extra {
jsonAdditional := marshalRR(rr, now)
header := rr.Header()
if header.Rrtype == dns.TypeOPT {
opt := rr.(*dns.OPT)
resp.Status = ((opt.Hdr.Ttl & 0xff000000) >> 20) | (resp.Status & 0xff)
for _, option := range opt.Option {
if option.Option() == dns.EDNS0SUBNET {
edns0 := option.(*dns.EDNS0_SUBNET)
clientAddress := edns0.Address
if clientAddress == nil {
clientAddress = net.IPv4(0, 0, 0, 0)
}
scopeMask := net.CIDRMask(int(edns0.SourceScope), len(edns0.Address))
resp.EdnsClientSubnet = clientAddress.Mask(scopeMask).String() + "/" + strconv.Itoa(int(edns0.SourceScope))
}
}
continue
}
if !resp.HaveTTL || jsonAdditional.TTL < resp.LeastTTL {
resp.HaveTTL = true
resp.LeastTTL = jsonAdditional.TTL
resp.EarliestExpires = jsonAdditional.Expires
}
resp.Additional = append(resp.Additional, jsonAdditional)
}
return resp
}
func marshalRR(rr dns.RR, now time.Time) RR {
jsonRR := RR {}
rrHeader := rr.Header()
jsonRR.Name = rrHeader.Name
jsonRR.Type = rrHeader.Rrtype
jsonRR.TTL = rrHeader.Ttl
jsonRR.Expires = now.Add(time.Duration(jsonRR.TTL) * time.Second)
jsonRR.ExpiresStr = jsonRR.Expires.Format(time.RFC1123)
data := strings.SplitN(rr.String(), "\t", 5)
if len(data) >= 5 {
jsonRR.Data = data[4]
}
return jsonRR
}