├── .github ├── dependabot.yml └── workflows │ ├── codeql-analysis.yml │ └── shiftleft-analysis.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── go.mod ├── go.sum └── myresolver.go /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gomod 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "04:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL scan" 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: '0 21 * * 6' 8 | 9 | jobs: 10 | CodeQL-Build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout repository 16 | uses: actions/checkout@v2 17 | with: 18 | fetch-depth: 2 19 | 20 | - run: git checkout HEAD^2 21 | if: ${{ github.event_name == 'pull_request' }} 22 | 23 | - name: Initialize CodeQL 24 | uses: github/codeql-action/init@v1 25 | 26 | - name: Autobuild 27 | uses: github/codeql-action/autobuild@v1 28 | 29 | - name: Perform CodeQL Analysis 30 | uses: github/codeql-action/analyze@v1 31 | -------------------------------------------------------------------------------- /.github/workflows/shiftleft-analysis.yml: -------------------------------------------------------------------------------- 1 | name: ShiftLeft Scan 2 | 3 | on: push 4 | 5 | jobs: 6 | Scan-Build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v1 10 | 11 | - name: Perform ShiftLeft Scan 12 | uses: ShiftLeftSecurity/scan-action@master 13 | env: 14 | WORKSPACE: "" 15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 16 | with: 17 | output: reports 18 | 19 | - name: Upload report 20 | uses: github/codeql-action/upload-sarif@v1 21 | with: 22 | sarif_file: reports 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: go 4 | 5 | go: 6 | - 1.x 7 | 8 | script: 9 | - echo $TRAVIS_GO_VERSION 10 | - go build 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-2023 Frank Denis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # whatmyresolver 2 | 3 | This is the source code of the `resolver.00f.net` service. 4 | 5 | It responds to DNS queries with the client (resolver) IP address. 6 | 7 | Public demo API 8 | --------------- 9 | 10 | ```bash 11 | $ dig +short resolver.dnscrypt.info 12 | 74.125.181.207 13 | ``` 14 | -> Aww crap, this VPN service is actually sending DNS queries to Google. 15 | 16 | Installation 17 | ------------ 18 | 19 | ```bash 20 | $ go get github.com/jedisct1/whatsmyresolver 21 | # myresolver -listen :53 22 | ``` 23 | 24 | And delegate a zone to this IP: 25 | ``` 26 | resolver.example.com. IN NS resolver-ns.example.com. 27 | resolver-ns.example.com IN A 28 | ``` 29 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/jedisct1/whatsmyresolver 2 | 3 | go 1.23.4 4 | 5 | require github.com/miekg/dns v1.1.62 6 | 7 | require ( 8 | golang.org/x/mod v0.18.0 // indirect 9 | golang.org/x/net v0.27.0 // indirect 10 | golang.org/x/sync v0.7.0 // indirect 11 | golang.org/x/sys v0.22.0 // indirect 12 | golang.org/x/tools v0.22.0 // indirect 13 | ) 14 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= 2 | github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= 3 | golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= 4 | golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 5 | golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= 6 | golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= 7 | golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= 8 | golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 9 | golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= 10 | golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 11 | golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= 12 | golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= 13 | -------------------------------------------------------------------------------- /myresolver.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "net" 8 | 9 | "github.com/miekg/dns" 10 | ) 11 | 12 | var ( 13 | address = flag.String("listen", ":53", "Address to listen to (UDP)") 14 | ) 15 | 16 | func failWithRcode(w dns.ResponseWriter, r *dns.Msg, rCode int) { 17 | m := new(dns.Msg) 18 | m.SetRcode(r, rCode) 19 | w.WriteMsg(m) 20 | } 21 | 22 | func route(w dns.ResponseWriter, req *dns.Msg) { 23 | if len(req.Question) != 1 { 24 | failWithRcode(w, req, dns.RcodeRefused) 25 | return 26 | } 27 | question := req.Question[0] 28 | qtype := question.Qtype 29 | if question.Qclass != dns.ClassINET { 30 | failWithRcode(w, req, dns.RcodeRefused) 31 | return 32 | } 33 | remoteIP := w.RemoteAddr().(*net.UDPAddr).IP 34 | m := new(dns.Msg) 35 | m.Id = req.Id 36 | switch qtype { 37 | case dns.TypeA: 38 | if remoteIP4 := remoteIP.To4(); remoteIP4 != nil { 39 | rr := new(dns.A) 40 | rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: question.Qtype, 41 | Class: dns.ClassINET, Ttl: 10} 42 | rr.A = remoteIP4 43 | m.Answer = []dns.RR{rr} 44 | } 45 | case dns.TypeAAAA: 46 | if remoteIP16 := remoteIP.To16(); remoteIP16 != nil { 47 | rr := new(dns.AAAA) 48 | rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: question.Qtype, 49 | Class: dns.ClassINET, Ttl: 10} 50 | rr.AAAA = remoteIP16 51 | m.Answer = []dns.RR{rr} 52 | } 53 | case dns.TypeTXT: 54 | rr := new(dns.TXT) 55 | rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: question.Qtype, 56 | Class: dns.ClassINET, Ttl: 10} 57 | rr.Txt = []string{fmt.Sprintf("Resolver IP: %v", remoteIP.String())} 58 | 59 | if edns0 := req.IsEdns0(); edns0 != nil { 60 | for _, option := range edns0.Option { 61 | optionId := option.Option() 62 | if optionId == dns.EDNS0PADDING { 63 | ext := option.(*dns.EDNS0_PADDING) 64 | paddingLen := len(ext.Padding) 65 | rr.Txt = append(rr.Txt, fmt.Sprintf("EDNS0 padding: %v bytes", paddingLen)) 66 | } else if optionId == dns.EDNS0SUBNET { 67 | ext := option.(*dns.EDNS0_SUBNET) 68 | rr.Txt = append(rr.Txt, fmt.Sprintf("EDNS0 client subnet: %v", ext.String())) 69 | } else if optionId == dns.EDNS0NSID { 70 | ext := option.(*dns.EDNS0_NSID) 71 | rr.Txt = append(rr.Txt, fmt.Sprintf("EDNS0 nsid: %v", ext.Nsid)) 72 | } else if optionId == dns.EDNS0COOKIE { 73 | ext := option.(*dns.EDNS0_COOKIE) 74 | rr.Txt = append(rr.Txt, fmt.Sprintf("EDNS0 cookie: %v", ext.Cookie)) 75 | } 76 | } 77 | } 78 | 79 | m.Answer = []dns.RR{rr} 80 | } 81 | m.Question = req.Question 82 | m.Response = true 83 | m.Authoritative = true 84 | w.WriteMsg(m) 85 | } 86 | 87 | func main() { 88 | flag.Parse() 89 | dns.HandleFunc(".", route) 90 | defer dns.HandleRemove(".") 91 | udpServer := &dns.Server{Addr: *address, Net: "udp"} 92 | defer udpServer.Shutdown() 93 | udpAddr, err := net.ResolveUDPAddr(udpServer.Net, udpServer.Addr) 94 | if err != nil { 95 | log.Fatal(err) 96 | } 97 | udpPacketConn, err := net.ListenUDP(udpServer.Net, udpAddr) 98 | if err != nil { 99 | log.Fatal(err) 100 | } 101 | udpServer.PacketConn = udpPacketConn 102 | fmt.Println("Ready") 103 | udpServer.ActivateAndServe() 104 | } 105 | --------------------------------------------------------------------------------