diff --git a/dns.go b/dns/dns.go similarity index 90% rename from dns.go rename to dns/dns.go index 97006ee..3ccea19 100644 --- a/dns.go +++ b/dns/dns.go @@ -1,6 +1,6 @@ // https://tools.ietf.org/html/rfc1035 -package main +package dns import ( "encoding/binary" @@ -13,25 +13,25 @@ import ( "github.com/nadoo/glider/proxy" ) -// DNSHeaderLen is the length of dns msg header -const DNSHeaderLen = 12 +// HeaderLen is the length of dns msg header +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 // 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 // the header. // TODO: If the request length > 512 then the client will send TCP packets instead, // so we should also serve tcp requests. -const DNSUDPMaxLen = 512 +const UDPMaxLen = 512 -// DNSQTypeA ipv4 -const DNSQTypeA = 1 +// QType . +const ( + QTypeA = 1 //ipv4 + QTypeAAAA = 28 ///ipv6 +) -// DNSQTypeAAAA ipv6 -const DNSQTypeAAAA = 28 - -// DNSMsg format +// Msg format // https://tools.ietf.org/html/rfc1035#section-4.1 // All communications inside of the domain protocol are carried in a single // 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 // +---------------------+ // | Additional | RRs holding additional information -// type DNSMsg struct { -// DNSHeader -// Questions []DNSQuestion -// Answers []DNSRR +// type Msg struct { +// Header +// Questions []Question +// Answers []RR // } -// DNSHeader format +// Header format // https://tools.ietf.org/html/rfc1035#section-4.1.1 // The header contains the following fields: // @@ -73,11 +73,11 @@ const DNSQTypeAAAA = 28 // | ARCOUNT | // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ // -// type DNSHeader struct { +// type Header struct { // ID uint16 // } -// DNSQuestion format +// Question format // https://tools.ietf.org/html/rfc1035#section-4.1.2 // The question section is used to carry the "question" in most queries, // i.e., the parameters that define what is being asked. The section @@ -94,7 +94,7 @@ const DNSQTypeAAAA = 28 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ // | QCLASS | // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ -type DNSQuestion struct { +type Question struct { QNAME string QTYPE uint16 QCLASS uint16 @@ -102,7 +102,7 @@ type DNSQuestion struct { Offset int } -// DNSRR format +// RR format // https://tools.ietf.org/html/rfc1035#section-3.2.1 // All RRs have the same top level format shown below: // @@ -126,7 +126,7 @@ type DNSQuestion struct { // / RDATA / // / / // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ -type DNSRR struct { +type RR struct { // NAME string TYPE uint16 CLASS uint16 @@ -137,8 +137,8 @@ type DNSRR struct { IP string } -// DNSAnswerHandler function handles the dns TypeA or TypeAAAA answer -type DNSAnswerHandler func(Domain, ip string) error +// AnswerHandler function handles the dns TypeA or TypeAAAA answer +type AnswerHandler func(Domain, ip string) error // DNS . type DNS struct { @@ -150,7 +150,7 @@ type DNS struct { DNSServer 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 @@ -186,7 +186,7 @@ func (s *DNS) ListenAndServeUDP() { log.F("proxy-dns listening UDP on %s", s.addr) for { - b := make([]byte, DNSUDPMaxLen) + b := make([]byte, UDPMaxLen) n, clientAddr, err := c.ReadFrom(b) if err != nil { log.F("proxy-dns local read error: %v", err) @@ -195,7 +195,7 @@ func (s *DNS) ListenAndServeUDP() { reqLen := uint16(n) // TODO: check here - if reqLen <= DNSHeaderLen+2 { + if reqLen <= HeaderLen+2 { log.F("proxy-dns not enough data") continue } @@ -253,7 +253,7 @@ func (s *DNS) ServeTCP(c net.Conn) { } // TODO: check here - if reqLen <= DNSHeaderLen+2 { + if reqLen <= HeaderLen+2 { log.F("proxy-dns-tcp not enough data") return } @@ -333,10 +333,10 @@ func (s *DNS) Exchange(reqLen uint16, reqMsg []byte, addr string) (respLen uint1 return } - if (respReq.QTYPE == DNSQTypeA || respReq.QTYPE == DNSQTypeAAAA) && + if (respReq.QTYPE == QTypeA || respReq.QTYPE == QTypeAAAA) && len(respMsg) > respReq.Offset { - var answers []*DNSRR + var answers []*RR answers, err = parseAnswers(respMsg[respReq.Offset:]) if err != nil { log.F("proxy-dns error in parseAnswers: %s", err) @@ -380,17 +380,17 @@ func (s *DNS) GetServer(domain string) string { } // AddAnswerHandler . -func (s *DNS) AddAnswerHandler(h DNSAnswerHandler) { +func (s *DNS) AddAnswerHandler(h AnswerHandler) { s.AnswerHandlers = append(s.AnswerHandlers, h) } -func parseQuestion(p []byte) (*DNSQuestion, error) { - q := &DNSQuestion{} +func parseQuestion(p []byte) (*Question, error) { + q := &Question{} lenP := len(p) var i int var domain []byte - for i = DNSHeaderLen; i < lenP; { + for i = HeaderLen; i < lenP; { l := int(p[i]) if l == 0 { @@ -425,8 +425,8 @@ func parseQuestion(p []byte) (*DNSQuestion, error) { return q, nil } -func parseAnswers(p []byte) ([]*DNSRR, error) { - var answers []*DNSRR +func parseAnswers(p []byte) ([]*RR, error) { + var answers []*RR lenP := len(p) for i := 0; i < lenP; { @@ -448,7 +448,7 @@ func parseAnswers(p []byte) ([]*DNSRR, error) { return nil, errors.New("not enough data") } - answer := &DNSRR{} + answer := &RR{} answer.TYPE = binary.BigEndian.Uint16(p[i:]) 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.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() - } else if answer.TYPE == DNSQTypeAAAA { + } else if answer.TYPE == QTypeAAAA { answer.IP = net.IP(answer.RDATA[:net.IPv6len]).String() } diff --git a/main.go b/main.go index ffc8198..9533e40 100644 --- a/main.go +++ b/main.go @@ -8,8 +8,10 @@ import ( "syscall" "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/dns" "github.com/nadoo/glider/proxy" + _ "github.com/nadoo/glider/proxy/dnstun" _ "github.com/nadoo/glider/proxy/http" _ "github.com/nadoo/glider/proxy/mixed" _ "github.com/nadoo/glider/proxy/socks5" @@ -68,7 +70,7 @@ func main() { } 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 { log.Fatal(err) } @@ -77,18 +79,18 @@ func main() { for _, r := range conf.rules { for _, domain := range r.Domain { 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 - dns.AddAnswerHandler(sDialer.AddDomainIP) + d.AddAnswerHandler(sDialer.AddDomainIP) if ipsetM != nil { - dns.AddAnswerHandler(ipsetM.AddDomainIP) + d.AddAnswerHandler(ipsetM.AddDomainIP) } - go dns.ListenAndServe() + go d.ListenAndServe() } sigCh := make(chan os.Signal, 1) diff --git a/proxy/dnstun/dnstun.go b/proxy/dnstun/dnstun.go new file mode 100644 index 0000000..872ee4e --- /dev/null +++ b/proxy/dnstun/dnstun.go @@ -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() + } +}