Skip to content

Commit

Permalink
Add public-dns option
Browse files Browse the repository at this point in the history
  • Loading branch information
wadahiro committed Sep 5, 2017
1 parent 82e1381 commit cf6993b
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 61 deletions.
23 changes: 17 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ Usage:
Options:
-dns-endpoint string
-dns-over-https-enabled
Use DNS-over-HTTPS service as public DNS
-dns-over-https-endpoint string
DNS-over-HTTPS endpoint URL (default "https://dns.google.com/resolve")
-dns-proxy-listen [host]:port
DNS Proxy listen address, as [host]:port (default ":3131")
Expand All @@ -51,16 +53,18 @@ Options:
Log level, one of: debug, info, warn, error, fatal, panic (default "info")
-private-dns string
Private DNS address for no_proxy targets (IP[:port])
-public-dns string
Public DNS address (IP[:port]) Note: Your proxy needs to support CONNECT method to the Public DNS port, and the public DNS needs to support TCP
-tcp-proxy-dports port1,port2,...
TCP Proxy dports, as port1,port2,... (default "22")
-tcp-proxy-listen [host]:port
TCP Proxy listen address, as [host]:port (default ":3128")
```

Proxy configuration is used from standard environment variables, `http_proxy`, `https_proxy` and `no_proxy`.
Also You can use **IP Address**, **CIDR**, **Suffix Domain Name** in `no_proxy`.
Also We can use **IP Address**, **CIDR**, **Suffix Domain Name** in `no_proxy`.

### Example
### Example

```
# Set your proxy environment
Expand All @@ -70,10 +74,10 @@ export http_proxy=http://foo:[email protected]:3128
export no_proxy=example.org,192.168.0.0/24
# Start go-transproxy with admin privileges(sudo)
sudo go-transproxy -private-dns 192.168.0.100
sudo go-transproxy -private-dns 192.168.0.100 -public-dns 8.8.8.8
```

For testing, using docker is easy way. Now, you can access to google from docker container with no proxy configuration as follows.
For testing, using docker is easy way. Now, we can access to google from docker container with no proxy configuration as follows.

```
docker run --rm -it centos curl http://www.google.com
Expand All @@ -85,9 +89,16 @@ The document has moved
</BODY></HTML>
```

If your proxy doesn't support CONNECT method to DNS port, it cannot resolve public domain name transparently.
Fortunately, Google privides [DNS-over-HTTPS service](https://developers.google.com/speed/public-dns/docs/dns-over-https), so we can use this service as public DNS by adding `-dns-over-https-enabled` option instead of `-public-dns` option as below even if your proxy supports CONNECT method to 443 port only.

```
sudo go-transproxy -private-dns 192.168.0.100 -dns-over-https-enabled
```

## Current Limitation

* HTTP proxy: Only works with HTTP host header, e.g., HTTP 1.0.
* HTTP proxy: Only works with HTTP host header.
* HTTPS proxy: `no_proxy` only works with IP Address and CIDR if your https client doesn't support [SNI](https://en.wikipedia.org/wiki/Server_Name_Indication).
* TCP proxy: `no_proxy` only works with IP Address and CIDR.

Expand Down
35 changes: 26 additions & 9 deletions cmd/go-transproxy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@ var (
"Log level, one of: debug, info, warn, error, fatal, panic",
)

dnsPrivateServer = fs.String("private-dns", "",
privateDNS = fs.String("private-dns", "",
"Private DNS address for no_proxy targets (IP[:port])")

publicDNS = fs.String("public-dns", "",
"Public DNS address (IP[:port]) Note: Your proxy needs to support CONNECT method to the Public DNS port, and the public DNS needs to support TCP")

tcpProxyDestPorts = fs.String(
"tcp-proxy-dports", "22", "TCP Proxy dports, as `port1,port2,...`",
)
Expand All @@ -53,8 +56,11 @@ var (
"dns-proxy-listen", ":3131", "DNS Proxy listen address, as `[host]:port`",
)

dnsEndpoint = fs.String(
"dns-endpoint",
dnsOverHTTPSEnabled = fs.Bool("dns-over-https-enabled", false,
"Use DNS-over-HTTPS service as public DNS")

dnsOverHTTPSEndpoint = fs.String(
"dns-over-https-endpoint",
"https://dns.google.com/resolve",
"DNS-over-HTTPS endpoint URL",
)
Expand Down Expand Up @@ -106,12 +112,15 @@ func main() {

dnsProxy := tproxy.NewDNSProxy(
tproxy.DNSProxyConfig{
ListenAddress: *dnsProxyListenAddress,
EnableUDP: *dnsEnableUDP,
EnableTCP: *dnsEnableTCP,
Endpoint: *dnsEndpoint,
PrivateDNS: *dnsPrivateServer,
NoProxyDomains: np.Domains,
Enabled: useDNSProxy(),
ListenAddress: *dnsProxyListenAddress,
EnableUDP: *dnsEnableUDP,
EnableTCP: *dnsEnableTCP,
Endpoint: *dnsOverHTTPSEndpoint,
PublicDNS: *publicDNS,
PrivateDNS: *privateDNS,
DNSOverHTTPSEnabled: *dnsOverHTTPSEnabled,
NoProxyDomains: np.Domains,
},
)
dnsProxy.Start()
Expand Down Expand Up @@ -151,6 +160,7 @@ func main() {
HTTPSToPort: httpsToPort,
TCPToPort: tcpToPort,
TCPDPorts: tcpDPorts,
PublicDNS: *publicDNS,
})
if err != nil {
log.Fatalf("IPTables: %s", err.Error())
Expand Down Expand Up @@ -178,6 +188,13 @@ func main() {
log.Infoln("go-transproxy exited.")
}

func useDNSProxy() bool {
if *privateDNS == "" && *publicDNS == "" && *dnsOverHTTPSEnabled == false {
return false
}
return true
}

func toPort(addr string) int {
array := strings.Split(addr, ":")
if len(array) != 2 {
Expand Down
70 changes: 58 additions & 12 deletions dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,30 @@ type DNSProxy struct {
}

type DNSProxyConfig struct {
ListenAddress string
EnableUDP bool
EnableTCP bool
Endpoint string
PrivateDNS string
NoProxyDomains []string
Enabled bool
ListenAddress string
EnableUDP bool
EnableTCP bool
Endpoint string
PublicDNS string
PrivateDNS string
DNSOverHTTPSEnabled bool
NoProxyDomains []string
}

func NewDNSProxy(c DNSProxyConfig) *DNSProxy {

// fix internal dns address
// fix dns address
if c.PublicDNS != "" {
_, _, err := net.SplitHostPort(c.PublicDNS)
if err != nil {
c.PublicDNS = net.JoinHostPort(c.PublicDNS, "53")
}
}
if c.PrivateDNS != "" {
if !strings.HasSuffix(c.PrivateDNS, ":53") {
c.PrivateDNS += ":53"
_, _, err := net.SplitHostPort(c.PrivateDNS)
if err != nil {
c.PrivateDNS = net.JoinHostPort(c.PrivateDNS, "53")
}
}

Expand Down Expand Up @@ -66,8 +76,21 @@ func NewDNSProxy(c DNSProxyConfig) *DNSProxy {
}

func (s *DNSProxy) Start() error {
if !s.Enabled {
log.Infof("DNS-Proxy: Not enabled")
return nil
}

log.Infof("DNS-Proxy: Start listener on %s", s.ListenAddress)
log.Infof("DNS-Proxy: Use private DNS %s for %s domains", s.PrivateDNS, s.NoProxyDomains)
if s.DNSOverHTTPSEnabled {
log.Infof("DNS-Proxy: Use DNS-over-HTTPS service as public DNS")
}
if !s.DNSOverHTTPSEnabled && s.PublicDNS != "" {
log.Infof("DNS-Proxy: Use public DNS %s via TCP-Proxy", s.PublicDNS)
}
if s.PrivateDNS != "" {
log.Infof("DNS-Proxy: Use private DNS %s for %s domains", s.PrivateDNS, s.NoProxyDomains)
}

// Prepare external DNS handler
provider, err := secop.NewGDNSProvider(s.Endpoint, &secop.GDNSOptions{
Expand All @@ -79,7 +102,7 @@ func (s *DNSProxy) Start() error {
}

options := &secop.HandlerOptions{}
externalHandler := secop.NewHandler(provider, options)
publicOverHTTPSHandler := secop.NewHandler(provider, options)

// Setup DNS Handler
dnsHandle := func(w dns.ResponseWriter, req *dns.Msg) {
Expand All @@ -98,7 +121,13 @@ func (s *DNSProxy) Start() error {
}

// Resolve by public DNS over HTTPS over http proxy
externalHandler.Handle(w, req)
if s.DNSOverHTTPSEnabled {
publicOverHTTPSHandler.Handle(w, req)
return
}

// Resolve by specified public DNS over http proxy
s.handlePublic(w, req)
}

dns.HandleFunc(".", dnsHandle)
Expand Down Expand Up @@ -136,6 +165,19 @@ func (s *DNSProxy) Start() error {
return nil
}

func (s *DNSProxy) handlePublic(w dns.ResponseWriter, req *dns.Msg) {
log.Debugf("DNS-Proxy: DNS request. %#v, %s", req, req)

// Need to use TCP because of using TCP-Proxy
resp, _, err := s.tcpClient.Exchange(req, s.PublicDNS)
if err != nil {
log.Warnf("DNS-Proxy: DNS Client failed. %s, %#v, %s", err.Error(), req, req)
dns.HandleFailed(w, req)
return
}
w.WriteMsg(resp)
}

func (s *DNSProxy) handlePrivate(w dns.ResponseWriter, req *dns.Msg) {
var c *dns.Client
if _, ok := w.RemoteAddr().(*net.TCPAddr); ok {
Expand All @@ -156,6 +198,10 @@ func (s *DNSProxy) handlePrivate(w dns.ResponseWriter, req *dns.Msg) {
}

func (s *DNSProxy) Stop() {
if !s.Enabled {
return
}

log.Infof("DNS-Proxy: Shutting down DNS service on interrupt\n")

if s.udpServer != nil {
Expand Down
Loading

0 comments on commit cf6993b

Please sign in to comment.