mirror of
https://github.com/nadoo/glider.git
synced 2025-02-23 09:25:41 +08:00
dns: move dns to package, add dnstun support
This commit is contained in:
parent
ea7c8bcfad
commit
5867055b10
@ -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
12
main.go
@ -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
65
proxy/dnstun/dnstun.go
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user