dns: move dns to package, add dnstun support

This commit is contained in:
nadoo 2018-06-26 21:22:24 +08:00
parent ea7c8bcfad
commit 5867055b10
3 changed files with 110 additions and 43 deletions

View File

@ -1,6 +1,6 @@
// https://tools.ietf.org/html/rfc1035 // https://tools.ietf.org/html/rfc1035
package main package dns
import ( import (
"encoding/binary" "encoding/binary"
@ -13,25 +13,25 @@ import (
"github.com/nadoo/glider/proxy" "github.com/nadoo/glider/proxy"
) )
// DNSHeaderLen is the length of dns msg header // HeaderLen is the length of dns msg header
const DNSHeaderLen = 12 const HeaderLen = 12
// DNSUDPMaxLen is the max size of udp dns request. // UDPMaxLen is the max size of udp dns request.
// https://tools.ietf.org/html/rfc1035#section-4.2.1 // https://tools.ietf.org/html/rfc1035#section-4.2.1
// Messages carried by UDP are restricted to 512 bytes (not counting the IP // Messages carried by UDP are restricted to 512 bytes (not counting the IP
// or UDP headers). Longer messages are truncated and the TC bit is set in // or UDP headers). Longer messages are truncated and the TC bit is set in
// the header. // the header.
// TODO: If the request length > 512 then the client will send TCP packets instead, // TODO: If the request length > 512 then the client will send TCP packets instead,
// so we should also serve tcp requests. // so we should also serve tcp requests.
const DNSUDPMaxLen = 512 const UDPMaxLen = 512
// DNSQTypeA ipv4 // QType .
const DNSQTypeA = 1 const (
QTypeA = 1 //ipv4
QTypeAAAA = 28 ///ipv6
)
// DNSQTypeAAAA ipv6 // Msg format
const DNSQTypeAAAA = 28
// DNSMsg format
// https://tools.ietf.org/html/rfc1035#section-4.1 // https://tools.ietf.org/html/rfc1035#section-4.1
// All communications inside of the domain protocol are carried in a single // All communications inside of the domain protocol are carried in a single
// format called a message. The top level format of message is divided // format called a message. The top level format of message is divided
@ -47,13 +47,13 @@ const DNSQTypeAAAA = 28
// | Authority | RRs pointing toward an authority // | Authority | RRs pointing toward an authority
// +---------------------+ // +---------------------+
// | Additional | RRs holding additional information // | Additional | RRs holding additional information
// type DNSMsg struct { // type Msg struct {
// DNSHeader // Header
// Questions []DNSQuestion // Questions []Question
// Answers []DNSRR // Answers []RR
// } // }
// DNSHeader format // Header format
// https://tools.ietf.org/html/rfc1035#section-4.1.1 // https://tools.ietf.org/html/rfc1035#section-4.1.1
// The header contains the following fields: // The header contains the following fields:
// //
@ -73,11 +73,11 @@ const DNSQTypeAAAA = 28
// | ARCOUNT | // | ARCOUNT |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// //
// type DNSHeader struct { // type Header struct {
// ID uint16 // ID uint16
// } // }
// DNSQuestion format // Question format
// https://tools.ietf.org/html/rfc1035#section-4.1.2 // https://tools.ietf.org/html/rfc1035#section-4.1.2
// The question section is used to carry the "question" in most queries, // The question section is used to carry the "question" in most queries,
// i.e., the parameters that define what is being asked. The section // i.e., the parameters that define what is being asked. The section
@ -94,7 +94,7 @@ const DNSQTypeAAAA = 28
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | QCLASS | // | QCLASS |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
type DNSQuestion struct { type Question struct {
QNAME string QNAME string
QTYPE uint16 QTYPE uint16
QCLASS uint16 QCLASS uint16
@ -102,7 +102,7 @@ type DNSQuestion struct {
Offset int Offset int
} }
// DNSRR format // RR format
// https://tools.ietf.org/html/rfc1035#section-3.2.1 // https://tools.ietf.org/html/rfc1035#section-3.2.1
// All RRs have the same top level format shown below: // All RRs have the same top level format shown below:
// //
@ -126,7 +126,7 @@ type DNSQuestion struct {
// / RDATA / // / RDATA /
// / / // / /
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
type DNSRR struct { type RR struct {
// NAME string // NAME string
TYPE uint16 TYPE uint16
CLASS uint16 CLASS uint16
@ -137,8 +137,8 @@ type DNSRR struct {
IP string IP string
} }
// DNSAnswerHandler function handles the dns TypeA or TypeAAAA answer // AnswerHandler function handles the dns TypeA or TypeAAAA answer
type DNSAnswerHandler func(Domain, ip string) error type AnswerHandler func(Domain, ip string) error
// DNS . // DNS .
type DNS struct { type DNS struct {
@ -150,7 +150,7 @@ type DNS struct {
DNSServer string DNSServer string
DNSServerMap map[string]string DNSServerMap map[string]string
AnswerHandlers []DNSAnswerHandler AnswerHandlers []AnswerHandler
} }
// NewDNS returns a dns forwarder. client[dns.udp] -> glider[tcp] -> forwarder[dns.tcp] -> remote dns addr // NewDNS returns a dns forwarder. client[dns.udp] -> glider[tcp] -> forwarder[dns.tcp] -> remote dns addr
@ -186,7 +186,7 @@ func (s *DNS) ListenAndServeUDP() {
log.F("proxy-dns listening UDP on %s", s.addr) log.F("proxy-dns listening UDP on %s", s.addr)
for { for {
b := make([]byte, DNSUDPMaxLen) b := make([]byte, UDPMaxLen)
n, clientAddr, err := c.ReadFrom(b) n, clientAddr, err := c.ReadFrom(b)
if err != nil { if err != nil {
log.F("proxy-dns local read error: %v", err) log.F("proxy-dns local read error: %v", err)
@ -195,7 +195,7 @@ func (s *DNS) ListenAndServeUDP() {
reqLen := uint16(n) reqLen := uint16(n)
// TODO: check here // TODO: check here
if reqLen <= DNSHeaderLen+2 { if reqLen <= HeaderLen+2 {
log.F("proxy-dns not enough data") log.F("proxy-dns not enough data")
continue continue
} }
@ -253,7 +253,7 @@ func (s *DNS) ServeTCP(c net.Conn) {
} }
// TODO: check here // TODO: check here
if reqLen <= DNSHeaderLen+2 { if reqLen <= HeaderLen+2 {
log.F("proxy-dns-tcp not enough data") log.F("proxy-dns-tcp not enough data")
return return
} }
@ -333,10 +333,10 @@ func (s *DNS) Exchange(reqLen uint16, reqMsg []byte, addr string) (respLen uint1
return return
} }
if (respReq.QTYPE == DNSQTypeA || respReq.QTYPE == DNSQTypeAAAA) && if (respReq.QTYPE == QTypeA || respReq.QTYPE == QTypeAAAA) &&
len(respMsg) > respReq.Offset { len(respMsg) > respReq.Offset {
var answers []*DNSRR var answers []*RR
answers, err = parseAnswers(respMsg[respReq.Offset:]) answers, err = parseAnswers(respMsg[respReq.Offset:])
if err != nil { if err != nil {
log.F("proxy-dns error in parseAnswers: %s", err) log.F("proxy-dns error in parseAnswers: %s", err)
@ -380,17 +380,17 @@ func (s *DNS) GetServer(domain string) string {
} }
// AddAnswerHandler . // AddAnswerHandler .
func (s *DNS) AddAnswerHandler(h DNSAnswerHandler) { func (s *DNS) AddAnswerHandler(h AnswerHandler) {
s.AnswerHandlers = append(s.AnswerHandlers, h) s.AnswerHandlers = append(s.AnswerHandlers, h)
} }
func parseQuestion(p []byte) (*DNSQuestion, error) { func parseQuestion(p []byte) (*Question, error) {
q := &DNSQuestion{} q := &Question{}
lenP := len(p) lenP := len(p)
var i int var i int
var domain []byte var domain []byte
for i = DNSHeaderLen; i < lenP; { for i = HeaderLen; i < lenP; {
l := int(p[i]) l := int(p[i])
if l == 0 { if l == 0 {
@ -425,8 +425,8 @@ func parseQuestion(p []byte) (*DNSQuestion, error) {
return q, nil return q, nil
} }
func parseAnswers(p []byte) ([]*DNSRR, error) { func parseAnswers(p []byte) ([]*RR, error) {
var answers []*DNSRR var answers []*RR
lenP := len(p) lenP := len(p)
for i := 0; i < lenP; { for i := 0; i < lenP; {
@ -448,7 +448,7 @@ func parseAnswers(p []byte) ([]*DNSRR, error) {
return nil, errors.New("not enough data") return nil, errors.New("not enough data")
} }
answer := &DNSRR{} answer := &RR{}
answer.TYPE = binary.BigEndian.Uint16(p[i:]) answer.TYPE = binary.BigEndian.Uint16(p[i:])
answer.CLASS = binary.BigEndian.Uint16(p[i+2:]) answer.CLASS = binary.BigEndian.Uint16(p[i+2:])
@ -456,9 +456,9 @@ func parseAnswers(p []byte) ([]*DNSRR, error) {
answer.RDLENGTH = binary.BigEndian.Uint16(p[i+8:]) answer.RDLENGTH = binary.BigEndian.Uint16(p[i+8:])
answer.RDATA = p[i+10 : i+10+int(answer.RDLENGTH)] answer.RDATA = p[i+10 : i+10+int(answer.RDLENGTH)]
if answer.TYPE == DNSQTypeA { if answer.TYPE == QTypeA {
answer.IP = net.IP(answer.RDATA[:net.IPv4len]).String() answer.IP = net.IP(answer.RDATA[:net.IPv4len]).String()
} else if answer.TYPE == DNSQTypeAAAA { } else if answer.TYPE == QTypeAAAA {
answer.IP = net.IP(answer.RDATA[:net.IPv6len]).String() answer.IP = net.IP(answer.RDATA[:net.IPv6len]).String()
} }

12
main.go
View File

@ -8,8 +8,10 @@ import (
"syscall" "syscall"
"github.com/nadoo/glider/common/log" "github.com/nadoo/glider/common/log"
"github.com/nadoo/glider/dns"
"github.com/nadoo/glider/proxy" "github.com/nadoo/glider/proxy"
_ "github.com/nadoo/glider/proxy/dnstun"
_ "github.com/nadoo/glider/proxy/http" _ "github.com/nadoo/glider/proxy/http"
_ "github.com/nadoo/glider/proxy/mixed" _ "github.com/nadoo/glider/proxy/mixed"
_ "github.com/nadoo/glider/proxy/socks5" _ "github.com/nadoo/glider/proxy/socks5"
@ -68,7 +70,7 @@ func main() {
} }
if conf.DNS != "" { if conf.DNS != "" {
dns, err := NewDNS(conf.DNS, conf.DNSServer[0], sDialer, false) d, err := dns.NewDNS(conf.DNS, conf.DNSServer[0], sDialer, false)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -77,18 +79,18 @@ func main() {
for _, r := range conf.rules { for _, r := range conf.rules {
for _, domain := range r.Domain { for _, domain := range r.Domain {
if len(r.DNSServer) > 0 { if len(r.DNSServer) > 0 {
dns.SetServer(domain, r.DNSServer[0]) d.SetServer(domain, r.DNSServer[0])
} }
} }
} }
// add a handler to update proxy rules when a domain resolved // add a handler to update proxy rules when a domain resolved
dns.AddAnswerHandler(sDialer.AddDomainIP) d.AddAnswerHandler(sDialer.AddDomainIP)
if ipsetM != nil { if ipsetM != nil {
dns.AddAnswerHandler(ipsetM.AddDomainIP) d.AddAnswerHandler(ipsetM.AddDomainIP)
} }
go dns.ListenAndServe() go d.ListenAndServe()
} }
sigCh := make(chan os.Signal, 1) sigCh := make(chan os.Signal, 1)

65
proxy/dnstun/dnstun.go Normal file
View File

@ -0,0 +1,65 @@
// https://tools.ietf.org/html/rfc1035
package dnstun
import (
"net/url"
"strings"
"github.com/nadoo/glider/common/log"
"github.com/nadoo/glider/dns"
"github.com/nadoo/glider/proxy"
"github.com/nadoo/glider/proxy/tcptun"
)
// DNSTun struct
type DNSTun struct {
dialer proxy.Dialer
addr string
raddr string
dns *dns.DNS
tcp *tcptun.TCPTun
}
func init() {
proxy.RegisterServer("dnstun", NewDNSTunServer)
}
// NewDNSTun returns a dns tunnel forwarder.
func NewDNSTun(s string, dialer proxy.Dialer) (*DNSTun, error) {
u, err := url.Parse(s)
if err != nil {
log.F("parse err: %s", err)
return nil, err
}
addr := u.Host
d := strings.Split(addr, "=")
addr, raddr := d[0], d[1]
p := &DNSTun{
dialer: dialer,
addr: addr,
raddr: raddr,
}
p.dns, _ = dns.NewDNS(addr, raddr, dialer, true)
return p, nil
}
// NewDNSTunServer returns a dns tunnel server.
func NewDNSTunServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
return NewDNSTun(s, dialer)
}
// ListenAndServe .
func (s *DNSTun) ListenAndServe() {
if s.dns != nil {
go s.dns.ListenAndServe()
}
}