Compare commits

...

35 Commits

Author SHA1 Message Date
Dingjun Fang b3bd7c3421
Merge pull request #1 from fangdingjun/dependabot/go_modules/github.com/pires/go-proxyproto-0.6.1
Bump github.com/pires/go-proxyproto from 0.6.0 to 0.6.1
2 years ago
dependabot[bot] c8aac178b8
Bump github.com/pires/go-proxyproto from 0.6.0 to 0.6.1
Bumps [github.com/pires/go-proxyproto](https://github.com/pires/go-proxyproto) from 0.6.0 to 0.6.1.
- [Release notes](https://github.com/pires/go-proxyproto/releases)
- [Commits](https://github.com/pires/go-proxyproto/compare/v0.6.0...v0.6.1)

---
updated-dependencies:
- dependency-name: github.com/pires/go-proxyproto
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2 years ago
dingjun f8a6b1bb0a add err check 2 years ago
dingjun 9d3d523309 line ending: crlf->lf 2 years ago
dingjun aad8f7d3e2 fix static check warnings 2 years ago
dingjun 2d4867ee39 getResponseFromUpstream: use timeout in conf 2 years ago
dingjun b5fd23222e update dependence 3 years ago
dingjun 5d56089426 update log library 3 years ago
dingjun 7096afd7d6 update miekg/dns to 1.1.43 3 years ago
dingjun 67ff5ead55 fix crash issue 3 years ago
dingjun f36fdafdd4 update README 3 years ago
dingjun 73a94dbd4e Merge branch 'master' of github.com:fangdingjun/gdns 3 years ago
dingjun 4bfd632bbd support http proxy on DOH 3 years ago
Dingjun Fang 552bda0e98
Update README.md 4 years ago
dingjun 7e84fc502a filter some upstream errors 4 years ago
dingjun 072b83c49d remove unused field 4 years ago
dingjun 4eaa46ff45 not check content-type 4 years ago
dingjun 934f6fed2a use http.Error send error message 4 years ago
dingjun a4258c73de support plain http 5 years ago
dingjun 1e4c6e5c05 update go.mod 5 years ago
dingjun 18f6ec0139 update dependence 6 years ago
dingjun 8bbe643f4d seperate protolistener 6 years ago
dingjun e5010163d1 add proxy proto v2 support 6 years ago
dingjun 6238ed3e39 add go modules file 6 years ago
dingjun 638a66a14c add go module files 6 years ago
dingjun f04444c210 add signal handle 6 years ago
dingjun 560a38afcf new DoH client
miekg/dns dropped DoH support in v1.0.15,
so use my own DoH client
6 years ago
fangdingjun c50ba09949 log library updated 6 years ago
fangdingjun 96df0202b5 define dns message type as constant 6 years ago
fangdingjun 640d3e2018 correct file name man.go->main.go 6 years ago
fangdingjun db0d021927 DoH: add http/1.1 support 6 years ago
fangdingjun 0c196e8267 set DOH content-type 6 years ago
fangdingjun 20c5e4794d query upstream on concurrency 6 years ago
fangdingjun e52286ebc7 update example config file 6 years ago
fangdingjun f54e957d42 use go http2 library 6 years ago

7
.gitignore vendored

@ -22,3 +22,10 @@ _testmain.go
*.exe
*.test
*.prof
*~
*.swp
gdns*
*.key
*.crt
client.yaml
server.yaml

@ -23,3 +23,5 @@ use
[dns](https://github.com/miekg/dns)
library to parse the dns message

@ -0,0 +1,59 @@
package main
import (
"bytes"
"context"
"fmt"
"io"
"net/http"
"time"
"github.com/miekg/dns"
)
type httpclient struct {
Net string
Timeout time.Duration
HTTPClient *http.Client
}
func (c *httpclient) Exchange(msg *dns.Msg, upstream string) (*dns.Msg, int, error) {
data, err := msg.Pack()
if err != nil {
return nil, 0, err
}
req, err := http.NewRequest("POST", upstream, bytes.NewReader(data))
if err != nil {
return nil, 0, err
}
req.Header.Set("Content-Type", dnsMsgType)
ctx, cancel := context.WithTimeout(context.Background(), c.Timeout)
defer cancel()
req = req.WithContext(ctx)
resp, err := c.HTTPClient.Do(req)
if err != nil {
return nil, 0, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, 0, fmt.Errorf("http error %d", resp.StatusCode)
}
data, err = io.ReadAll(resp.Body)
if err != nil {
return nil, 0, err
}
m := new(dns.Msg)
if err = m.Unpack(data); err != nil {
return nil, 0, err
}
return m, 0, nil
}

@ -1,7 +1,7 @@
package main
import (
"io/ioutil"
"os"
"github.com/go-yaml/yaml"
)
@ -21,7 +21,7 @@ type listen struct {
}
func loadConfig(f string) (*conf, error) {
data, err := ioutil.ReadFile(f)
data, err := os.ReadFile(f)
if err != nil {
return nil, err
}

@ -9,7 +9,7 @@ upstream_servers:
# bootstrap dns server,
# this used to resolve the domain name used in Doh and TLS,
# not needed if you don't use DoH and tls
bootstrap_severs:
bootstrap_servers:
- tcp://1.0.0.1:53
- udp://1.0.0.1:53
@ -37,4 +37,4 @@ upstream_timeout: 3
# check upstream certificate or not
# if you use self signed certificate, set this to true
upstream_insecure: true
upstream_insecure: false

@ -0,0 +1,22 @@
module github.com/fangdingjun/gdns
go 1.17
require (
github.com/fangdingjun/go-log/v5 v5.0.0
github.com/fangdingjun/protolistener v0.0.0-20210804081554-626e6590d6e7
github.com/go-yaml/yaml v2.1.0+incompatible
github.com/miekg/dns v1.1.43
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985
)
require (
github.com/kr/pretty v0.3.0 // indirect
github.com/pires/go-proxyproto v0.6.1 // indirect
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect
golang.org/x/text v0.3.6 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

@ -0,0 +1,51 @@
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/fangdingjun/go-log/v5 v5.0.0 h1:vdh9Bk9C4ZFL6KoO6rII73zQIyaLf7hFdBvucO/ckiE=
github.com/fangdingjun/go-log/v5 v5.0.0/go.mod h1:V012Oxo0/pSbccX4OFSp9MJglXwNsZo2ByBBorr7zzM=
github.com/fangdingjun/protolistener v0.0.0-20210804081554-626e6590d6e7 h1:z5NlLvUNbGZxQhtUZELvIorAqpDmcIhGQXE5GdO7+5I=
github.com/fangdingjun/protolistener v0.0.0-20210804081554-626e6590d6e7/go.mod h1:ljbjhI4fVrT5GwMu1iBhWTZwLJSqsXKwKlGKas5GudM=
github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o=
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
github.com/pires/go-proxyproto v0.6.0/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
github.com/pires/go-proxyproto v0.6.1 h1:EBupykFmo22SDjv4fQVQd2J9NOoLPmyZA/15ldOGkPw=
github.com/pires/go-proxyproto v0.6.1/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 h1:4CSI6oo7cOjJKajidEljs9h+uP0rRZBPPPhcCbj5mw8=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

@ -4,12 +4,14 @@ import (
"flag"
"fmt"
"os"
"os/signal"
"syscall"
"github.com/fangdingjun/go-log"
"github.com/fangdingjun/go-log/formatters"
"github.com/fangdingjun/go-log/writers"
log "github.com/fangdingjun/go-log/v5"
)
var cfg *conf
func main() {
var configfile string
var logFileCount int
@ -22,11 +24,11 @@ func main() {
flag.Int64Var(&logFileSize, "log_size", 10, "max log file size MB")
flag.StringVar(&loglevel, "log_level", "INFO",
"log level, values:\nOFF, FATAL, PANIC, ERROR, WARN, INFO, DEBUG")
flag.StringVar(&configfile, "c", "", "config file")
flag.StringVar(&configfile, "c", "gdns.yaml", "config file")
flag.Parse()
if logfile != "" {
log.Default.Out = &writers.FixedSizeFileWriter{
log.Default.Out = &log.FixedSizeFileWriter{
MaxCount: logFileCount,
Name: logfile,
MaxSize: logFileSize * 1024 * 1024,
@ -42,18 +44,24 @@ func main() {
log.Default.Level = lv
}
log.Default.Formatter = &formatters.TextFormatter{
TimeFormat: "2006-01-02 15:04:05.000"}
cfg, err := loadConfig(configfile)
_cfg, err := loadConfig(configfile)
if err != nil {
log.Fatal(err)
}
cfg = _cfg
if cfg.UpstreamTimeout == 0 {
cfg.UpstreamTimeout = 5
}
initDNSClient(cfg)
log.Debugf("%+v", cfg)
makeServers(cfg)
select {}
ch := make(chan os.Signal, 2)
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
s := <-ch
log.Errorf("received signal %s, exit...", s)
log.Println("exit.")
}

@ -0,0 +1,56 @@
package main
import (
"net/http"
log "github.com/fangdingjun/go-log/v5"
)
type logHandler struct {
status int
w http.ResponseWriter
size int
}
func (lh *logHandler) WriteHeader(status int) {
lh.status = status
lh.w.WriteHeader(status)
}
func (lh *logHandler) Write(buf []byte) (int, error) {
lh.size += len(buf)
return lh.w.Write(buf)
}
func (lh *logHandler) Header() http.Header {
return lh.w.Header()
}
func (lh *logHandler) Status() int {
if lh.status != 0 {
return lh.status
}
return 200
}
var _ http.ResponseWriter = &logHandler{}
// LogHandler is a log middleware
// record request log
func LogHandler(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
log.Error(err)
w.WriteHeader(http.StatusInternalServerError)
log.Infof("\"%s - %s %s %s\" - %d %d \"%s\"",
r.RemoteAddr, r.Method, r.RequestURI, r.Proto, 500, 0, r.UserAgent())
}
}()
lh := &logHandler{w: w}
handler.ServeHTTP(lh, r)
log.Infof("\"%s - %s %s %s\" - %d %d \"%s\"",
r.RemoteAddr, r.Method, r.RequestURI, r.Proto, lh.Status(), lh.size, r.UserAgent())
})
}

@ -2,11 +2,14 @@ package main
import (
"crypto/tls"
"fmt"
"net"
"net/http"
"net/url"
"strconv"
"github.com/fangdingjun/go-log"
log "github.com/fangdingjun/go-log/v5"
"github.com/fangdingjun/protolistener"
)
type server struct {
@ -14,7 +17,6 @@ type server struct {
cert string
key string
upstreams []*url.URL
bootstrap []*url.URL
}
func (srv *server) serve() {
@ -25,8 +27,10 @@ func (srv *server) serve() {
srv.serveTCP()
case "tls":
srv.serveTLS()
case "http":
srv.serveHTTP()
case "https":
srv.serveHTTPS()
srv.serveHTTP()
default:
log.Fatalf("unsupported type %s", srv.addr.Scheme)
}
@ -79,18 +83,21 @@ func (srv *server) serveTLS() {
if err != nil {
log.Fatalln("load certificate failed", err)
}
l, err := tls.Listen("tcp", srv.addr.Host,
&tls.Config{
Certificates: []tls.Certificate{cert},
//NextProtos: []string{"h2"},
})
l, err := net.Listen("tcp", srv.addr.Host)
if err != nil {
log.Fatalln("listen tls", err)
}
defer l.Close()
log.Debugf("listen tls://%s", l.Addr().String())
tl := tls.NewListener(protolistener.New(l), &tls.Config{
Certificates: []tls.Certificate{cert},
//NextProtos: []string{"h2"},
})
for {
conn, err := l.Accept()
conn, err := tl.Accept()
if err != nil {
log.Debugln("tls accept", err)
break
@ -99,34 +106,34 @@ func (srv *server) serveTLS() {
}
}
func (srv *server) serveHTTPS() {
cert, err := tls.LoadX509KeyPair(srv.cert, srv.key)
if err != nil {
log.Fatalln("load certificate failed", err)
}
l, err := tls.Listen("tcp", srv.addr.Host,
&tls.Config{
Certificates: []tls.Certificate{cert},
NextProtos: []string{"h2"},
})
func (srv *server) serveHTTP() {
log.Debugf("listen %s://%s", srv.addr.Scheme, srv.addr.Host)
l, err := net.Listen("tcp", srv.addr.Host)
if err != nil {
log.Fatalln("listen https", err)
}
defer l.Close()
log.Debugf("listen https://%s", l.Addr().String())
for {
conn, err := l.Accept()
if err != nil {
log.Debugln("https accept", err)
break
httpsrv := &http.Server{
Handler: LogHandler(srv),
}
switch srv.addr.Scheme {
case "http":
err = httpsrv.Serve(protolistener.New(l))
case "https":
err = httpsrv.ServeTLS(protolistener.New(l), srv.cert, srv.key)
default:
err = fmt.Errorf("not supported %s", srv.addr.Scheme)
}
go srv.handleHTTPSConn(conn)
if err != nil {
log.Fatal(err)
}
}
func makeServers(c *conf) {
upstreams := []*url.URL{}
bootstraps := []*url.URL{}
for _, a := range c.UpstreamServers {
u, err := url.Parse(a)
if err != nil {
@ -135,14 +142,6 @@ func makeServers(c *conf) {
upstreams = append(upstreams, u)
}
for _, a := range c.BootstrapServers {
u, err := url.Parse(a)
if err != nil {
log.Fatal(err)
}
bootstraps = append(bootstraps, u)
}
for _, l := range c.Listen {
u, err := url.Parse(l.Addr)
if err != nil {
@ -153,7 +152,6 @@ func makeServers(c *conf) {
cert: l.Cert,
key: l.Key,
upstreams: upstreams,
bootstrap: bootstraps,
}
go srv.serve()
}

@ -1,83 +1,66 @@
package main
import (
"crypto/tls"
"io/ioutil"
"net"
"io"
"net/http"
"github.com/fangdingjun/go-log"
"github.com/fangdingjun/nghttp2-go"
log "github.com/fangdingjun/go-log/v5"
"github.com/miekg/dns"
)
func (srv *server) handleHTTPSConn(c net.Conn) {
defer c.Close()
tlsconn := c.(*tls.Conn)
if err := tlsconn.Handshake(); err != nil {
log.Errorln("handshake", err)
return
}
state := tlsconn.ConnectionState()
if state.NegotiatedProtocol != "h2" {
log.Errorln("http2 is needed")
return
}
h2conn, err := nghttp2.Server(tlsconn, srv)
if err != nil {
log.Errorf("create http2 error: %s", err)
return
}
h2conn.Run()
}
const dnsMsgType = "application/dns-message"
func (srv *server) handleHTTP2Req(w http.ResponseWriter, r *http.Request) {
func (srv *server) handleHTTPReq(w http.ResponseWriter, r *http.Request) {
/*
ctype := r.Header.Get("content-type")
if ctype != "application/dns-message" {
if !strings.HasPrefix(ctype, dnsMsgType) {
log.Errorf("request type %s, require %s", ctype, dnsMsgType)
http.Error(w, "dns message is required", http.StatusBadRequest)
return
}
*/
if r.ContentLength < 10 {
log.Errorf("message is too small, %v", r.ContentLength)
http.Error(w, "message is too small", http.StatusBadRequest)
return
}
data, err := ioutil.ReadAll(r.Body)
data, err := io.ReadAll(r.Body)
if err != nil {
log.Errorln("read request body", err)
w.WriteHeader(http.StatusBadRequest)
http.Error(w, "read request failed", http.StatusBadRequest)
return
}
msg := new(dns.Msg)
if err := msg.Unpack(data); err != nil {
log.Errorln("parse dns message", err)
http.Error(w, "parse dns message error", http.StatusBadRequest)
return
}
reply := false
for _, up := range srv.upstreams {
log.Debugf("from %s query upstream %s", r.RemoteAddr, up.String())
log.Debugln("query", msg.Question[0].String())
m, err := queryUpstream(msg, up)
if err == nil {
w.Header().Set("content-type", "application/dns-message")
w.WriteHeader(http.StatusOK)
m, err := getResponseFromUpstream(msg, srv.upstreams)
if err != nil {
log.Debugln("query", msg.Question[0].String(), "timeout")
http.Error(w, "query upstream server failed", http.StatusServiceUnavailable)
return
}
for _, a := range m.Answer {
log.Debugln("result", a.String())
}
w.Header().Set("content-type", dnsMsgType)
d, _ := m.Pack()
w.Write(d)
reply = true
break
} else {
log.Errorf("https query upstream %s", err)
}
}
if !reply {
w.WriteHeader(http.StatusServiceUnavailable)
if _, err := w.Write(d); err != nil {
log.Errorln(err)
}
}
func (srv *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.RequestURI != srv.addr.Path {
w.WriteHeader(http.StatusNotFound)
http.Error(w, "Path not found", http.StatusNotFound)
return
}
srv.handleHTTP2Req(w, r)
srv.handleHTTPReq(w, r)
}

@ -3,7 +3,7 @@ package main
import (
"net"
"github.com/fangdingjun/go-log"
log "github.com/fangdingjun/go-log/v5"
"github.com/miekg/dns"
)
@ -17,23 +17,18 @@ func (srv *server) handleTCP(c net.Conn) {
log.Debugln("tcp read message", err)
break
}
reply := false
for _, up := range srv.upstreams {
log.Debugf("from %s query upstream %s", conn.RemoteAddr(), up.String())
log.Debugln("query", msg.Question[0].String())
m, err := queryUpstream(msg, up)
if err == nil {
for _, a := range m.Answer {
log.Debugln("result", a.String())
}
log.Debugln("got reply", m.String())
conn.WriteMsg(m)
reply = true
m, err := getResponseFromUpstream(msg, srv.upstreams)
if err != nil {
log.Debugln("query", msg.Question[0].String(), "timeout")
break
}
log.Debugln("query upstream", up.String(), err)
for _, a := range m.Answer {
log.Debugln("result", a.String())
}
if !reply {
if err := conn.WriteMsg(m); err != nil {
log.Errorln(err)
break
}
}

@ -3,7 +3,7 @@ package main
import (
"net"
"github.com/fangdingjun/go-log"
log "github.com/fangdingjun/go-log/v5"
"github.com/miekg/dns"
)
@ -13,19 +13,18 @@ func (srv *server) handleUDP(buf []byte, addr net.Addr, conn *net.UDPConn) {
log.Debugln("udp parse msg", err)
return
}
for _, up := range srv.upstreams {
log.Debugf("from %s query upstream %s", addr, up.String())
log.Debugln("query", msg.Question[0].String())
m, err := queryUpstream(msg, up)
if err == nil {
m, err := getResponseFromUpstream(msg, srv.upstreams)
if err != nil {
log.Debugln("query", msg.Question[0].String(), "timeout")
return
}
for _, a := range m.Answer {
log.Debugln("result", a.String())
}
d, _ := m.Pack()
conn.WriteTo(d, addr)
break
} else {
log.Debugln("udp query upstream err", err)
}
if _, err := conn.WriteTo(d, addr); err != nil {
log.Errorln(err)
}
}

@ -1,23 +1,63 @@
package main
import (
"bufio"
"context"
"crypto/tls"
"errors"
"fmt"
"net"
"net/http"
"net/url"
"os"
"strings"
"time"
"github.com/fangdingjun/go-log"
nghttp2 "github.com/fangdingjun/nghttp2-go"
log "github.com/fangdingjun/go-log/v5"
"github.com/miekg/dns"
"golang.org/x/net/http2"
)
var dnsClientTCP *dns.Client
var dnsClientHTTPS *dns.Client
var dnsClientUDP *dns.Client
var dnsClientTLS *dns.Client
var dnsClientHTTPS *httpclient
func getResponseFromUpstream(msg *dns.Msg, upstreams []*url.URL) (*dns.Msg, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(cfg.UpstreamTimeout+1)*time.Second)
defer cancel()
resch := make(chan *dns.Msg, len(upstreams))
for _, up := range upstreams {
go func(u *url.URL) {
m, err := queryUpstream(msg, u)
if err == nil {
resch <- m
return
}
log.Errorln(u.String(), err)
}(up)
}
var errmsg *dns.Msg
for i := 0; i < len(upstreams); i++ {
select {
case <-ctx.Done():
return nil, errors.New("time out")
case m := <-resch:
if m.MsgHdr.Rcode == dns.RcodeSuccess {
return m, nil
}
errmsg = m
}
}
if errmsg != nil {
return errmsg, nil
}
return nil, errors.New("empty result")
}
func queryUpstream(msg *dns.Msg, upstream *url.URL) (*dns.Msg, error) {
switch upstream.Scheme {
@ -104,17 +144,54 @@ func initDNSClient(c *conf) {
Net: "tcp",
Timeout: time.Duration(c.UpstreamTimeout) * time.Second,
}
dnsClientHTTPS = &dns.Client{
dnsClientHTTPS = &httpclient{
Net: "https",
Timeout: time.Duration(c.UpstreamTimeout) * time.Second,
HTTPClient: &http.Client{
Transport: &nghttp2.Transport{
DialTLS: func(network, addr string, cfg *tls.Config) (*tls.Conn, error) {
Transport: &http2.Transport{
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
log.Debugln("dial to", network, addr)
p := os.Getenv("https_proxy")
if p == "" {
p = os.Getenv("http_proxy")
}
if p == "" {
conn, err := tls.DialWithDialer(dialer, network, addr, cfg)
return conn, err
}
u, _ := url.Parse(p)
log.Debugf("dial to proxy %s", u.Host)
conn, err := net.Dial(network, u.Host)
if err != nil {
return nil, err
}
fmt.Fprintf(conn, "CONNECT %s HTTP/1.1\r\nHost: %s\r\n\r\n", addr, addr)
r := bufio.NewReader(conn)
line, err := r.ReadString('\n')
if err != nil {
return nil, err
}
ss := strings.Fields(line)
if ss[1] != "200" {
return nil, fmt.Errorf("http code %s", ss[1])
}
for {
line, err := r.ReadString('\n')
if err != nil {
return nil, err
}
line = strings.Trim(line, "\r\n")
if line == "" {
break
}
}
tlsconn := tls.Client(conn, cfg)
if err = tlsconn.Handshake(); err != nil {
return nil, err
}
return tlsconn, nil
},
TLSConfig: &tls.Config{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: c.UpstreamInsecure,
NextProtos: []string{"h2"},
},

Loading…
Cancel
Save