Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ednsopt #91

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ Application Options:
--refuse-any If specified, refuse ANY requests
--edns Use EDNS Client Subnet extension
--edns-addr= Send EDNS Client Address
--ednsopt= List of EDNS extensions to send along with the DNS query (ex: 8:deadbeaf)
--ipv6-disabled If specified, all AAAA requests will be replied with NoError RCode and empty answer
--bogus-nxdomain= Transform responses that contain only given IP addresses into NXDOMAIN. Can be specified multiple times.
--version Prints the program version
Expand Down Expand Up @@ -202,6 +203,25 @@ If you want to use EDNS CS feature when you're connecting to the proxy from a lo

Now even if your IP address is 192.168.0.1 and it's not a public IP, the proxy will pass through 72.72.72.72 to the upstream server.


### EDNS Generic raw option

You can add any EDNS extension of your choice with the `--ednsopt` flag.

The option argument for this flag is of the form:

option_code:base64_data

where:

`option_code` is a 16 bit unsigned integer (0-65535)
`base64_data` is a base64 encoded byte array

```
DATA=$(echo -n "This is a binary string" | base64)
./dnsproxy -u 8.8.8.8:53 --ednsopt="4242:${DATA}"
```

### Bogus NXDomain

This option is similar to dnsmasq `bogus-nxdomain`. If specified, `dnsproxy` transforms responses that contain only the given IP addresses into `NXDOMAIN`. Can be specified multiple times.
Expand All @@ -210,4 +230,4 @@ In the example below, we use AdGuard DNS server that returns `0.0.0.0` for block

```
./dnsproxy -u 176.103.130.130:53 --bogus-nxdomain=0.0.0.0
```
```
27 changes: 27 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ package main

import (
"crypto/tls"
"encoding/base64"
"fmt"
"io/ioutil"
"net"
"os"
"os/signal"
"strconv"
"strings"
"syscall"
"time"

Expand Down Expand Up @@ -100,6 +103,9 @@ type Options struct {
// Use Custom EDNS Client Address
EDNSAddr string `long:"edns-addr" description:"Send EDNS Client Address"`

// Add user-specific EDNS exstension
EDNSOpt []string `long:"ednsopt" description:"List of EDNS extensions to send along with the DNS query (ex: 8:deadbeaf)"`

// Other settings and options
// --

Expand Down Expand Up @@ -218,6 +224,27 @@ func createProxyConfig(options Options) proxy.Config {
}
}

if len(options.EDNSOpt) > 0 {
config.EDNSOpts = make(map[uint16][]byte)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please move this code to a separate function and also add a unit-test for it?

for _, s := range options.EDNSOpt {
kv := strings.Split(s, ":")
if len(kv) != 2 {
log.Fatalf("parse error for '%s', --ednsopt must be of the form: option-code:base64encodeddata (ex: --ednsopt 8:SGVsbG8gd29ybGQK)", s)
} else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

else branch is not needed here, just put the code below without it

r, err := strconv.ParseUint(kv[0], 10, 16)
if err != nil {
log.Fatalf("parse error for '%s', --ednsopt option-code must be a valid 16 bits numbers (between 0 and 65535)", kv[0])
}
optionCode := uint16(r)
data, err := base64.StdEncoding.DecodeString(kv[1])
if err != nil {
log.Fatalf("parse error for '%s', --ednsopt data must be a valid base64 encoded string: (%s)", kv[1], err)
}
config.EDNSOpts[optionCode] = data
}
}
}

if options.Fallbacks != nil {
fallbacks := []upstream.Upstream{}
for i, f := range options.Fallbacks {
Expand Down
3 changes: 3 additions & 0 deletions proxy/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ type Config struct {
EnableEDNSClientSubnet bool
EDNSAddr net.IP // ECS IP used in request

// List of raw EDNS options
EDNSOpts map[uint16][]byte

// Cache settings
// --

Expand Down
30 changes: 30 additions & 0 deletions proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,10 @@ func (p *Proxy) Resolve(d *DNSContext) error {
p.processECS(d)
}

if p.Config.EDNSOpts != nil {
p.processEDNSOpts(d)
}

if p.replyFromCache(d) {
return nil
}
Expand Down Expand Up @@ -349,3 +353,29 @@ func (p *Proxy) processECS(d *DNSContext) {
d.ecsReqIP = ip
d.ecsReqMask = mask
}

// Set EDNSOpts
func (p *Proxy) processEDNSOpts(d *DNSContext) {
var o (*dns.OPT) = nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var o *dns.OPT is a better looking variant

for _, ex := range d.Req.Extra {
if ex.Header().Rrtype == dns.TypeOPT {
o = ex.(*dns.OPT)
break
}
}

if o == nil { // OPT Record does not already exist, create it
o = new(dns.OPT)
o.SetUDPSize(4096)
o.Hdr.Name = "."
o.Hdr.Rrtype = dns.TypeOPT
d.Req.Extra = append(d.Req.Extra, o)
}

for k, v := range p.Config.EDNSOpts {
e := new(dns.EDNS0_LOCAL)
e.Code = k
e.Data = v
o.Option = append(o.Option, e)
}
}