Skip to content

Commit

Permalink
Merge pull request #401 from evidolob/hosts-file-records
Browse files Browse the repository at this point in the history
Check 'hosts' file records, during handling DNS requests
  • Loading branch information
openshift-merge-bot[bot] authored Oct 2, 2024
2 parents d1683b9 + 3ea66d1 commit de690ca
Show file tree
Hide file tree
Showing 17 changed files with 1,660 additions and 3 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.22.0
require (
github.com/Microsoft/go-winio v0.6.2
github.com/apparentlymart/go-cidr v1.1.0
github.com/areYouLazy/libhosty v1.1.0
github.com/containers/winquit v1.1.0
github.com/coreos/stream-metadata-go v0.4.4
github.com/dustin/go-humanize v1.0.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU=
github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
github.com/areYouLazy/libhosty v1.1.0 h1:kO6UTk9z72cHW28A/V1kKi7C8iKQGqINiVGXp+05Eao=
github.com/areYouLazy/libhosty v1.1.0/go.mod h1:dV4ir3feRrTbWdcJ21mt3MeZlASg0sc8db6nimL9GOA=
github.com/armon/go-proxyproto v0.0.0-20210323213023-7e956b284f0a/go.mod h1:QmP9hvJ91BbJmGVGSbutW19IC0Q9phDCLGaomwTJbgU=
github.com/containers/winquit v1.1.0 h1:jArun04BNDQvt2W0Y78kh9TazN2EIEMG5Im6/JY7+pE=
github.com/containers/winquit v1.1.0/go.mod h1:PsPeZlnbkmGGIToMPHF1zhWjBUkd8aHjMOr/vFcPxw8=
Expand Down
36 changes: 33 additions & 3 deletions pkg/services/dns/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package dns

import (
"encoding/json"
"errors"
"fmt"
"net"
"net/http"
"strings"
"sync"

"github.com/areYouLazy/libhosty"
"github.com/containers/gvisor-tap-vsock/pkg/types"
"github.com/miekg/dns"
log "github.com/sirupsen/logrus"
Expand All @@ -18,6 +20,7 @@ type dnsHandler struct {
zonesLock sync.RWMutex
udpClient *dns.Client
tcpClient *dns.Client
hostsFile *HostsFile
nameserver string
}

Expand All @@ -28,11 +31,17 @@ func newDNSHandler(zones []types.Zone) (*dnsHandler, error) {
return nil, err
}

hostsFile, err := NewHostsFile("")
if err != nil {
return nil, err
}

return &dnsHandler{
zones: zones,
tcpClient: &dns.Client{Net: "tcp"},
udpClient: &dns.Client{Net: "udp"},
nameserver: net.JoinHostPort(nameserver, port),
hostsFile: hostsFile,
}, nil

}
Expand All @@ -58,15 +67,17 @@ func (h *dnsHandler) handleUDP(w dns.ResponseWriter, r *dns.Msg) {
}

func (h *dnsHandler) addLocalAnswers(m *dns.Msg, q dns.Question) bool {
// resolve only ipv4 requests
if q.Qtype != dns.TypeA {
return false
}

h.zonesLock.RLock()
defer h.zonesLock.RUnlock()

for _, zone := range h.zones {
zoneSuffix := fmt.Sprintf(".%s", zone.Name)
if strings.HasSuffix(q.Name, zoneSuffix) {
if q.Qtype != dns.TypeA {
return false
}
for _, record := range zone.Records {
withoutZone := strings.TrimSuffix(q.Name, zoneSuffix)
if (record.Name != "" && record.Name == withoutZone) ||
Expand Down Expand Up @@ -98,6 +109,24 @@ func (h *dnsHandler) addLocalAnswers(m *dns.Msg, q dns.Question) bool {
m.Rcode = dns.RcodeNameError
return true
}
ip, err := h.hostsFile.LookupByHostname(q.Name)
if err != nil {
// ignore only ErrHostnameNotFound error
if !errors.Is(err, libhosty.ErrHostnameNotFound) {
log.Errorf("Error during looking in hosts file records: %v", err)
}
} else {
m.Answer = append(m.Answer, &dns.A{
Hdr: dns.RR_Header{
Name: q.Name,
Rrtype: dns.TypeA,
Class: dns.ClassINET,
Ttl: 0,
},
A: ip,
})
return true
}
}
return false
}
Expand All @@ -106,6 +135,7 @@ func (h *dnsHandler) addAnswers(dnsClient *dns.Client, r *dns.Msg) *dns.Msg {
m := new(dns.Msg)
m.SetReply(r)
m.RecursionAvailable = true

for _, q := range m.Question {
if done := h.addLocalAnswers(m, q); done {
return m
Expand Down
97 changes: 97 additions & 0 deletions pkg/services/dns/hosts_file.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package dns

import (
"net"
"path/filepath"
"sync"

"github.com/areYouLazy/libhosty"
"github.com/fsnotify/fsnotify"
log "github.com/sirupsen/logrus"
)

type HostsFile struct {
hostsReadLock sync.RWMutex
hostsFilePath string
hostsFile *libhosty.HostsFile
}

// NewHostsFile Creates new HostsFile instance
// Pass ""(empty string) if you want to use default hosts file
func NewHostsFile(hostsPath string) (*HostsFile, error) {
hostsFile, err := readHostsFile(hostsPath)
if err != nil {
return nil, err
}
watcher, err := fsnotify.NewWatcher()
if err != nil {
return nil, err
}

h := &HostsFile{
hostsFile: hostsFile,
hostsFilePath: hostsFile.Config.FilePath,
}
go func() {
h.startWatch(watcher)
}()
return h, nil
}

func (h *HostsFile) startWatch(w *fsnotify.Watcher) {
err := w.Add(filepath.Dir(h.hostsFilePath))

if err != nil {
log.Errorf("Hosts file adding watcher error:%s", err)
return
}
for {
select {
case err, ok := <-w.Errors:
if !ok {
return
}
log.Errorf("Hosts file watcher error:%s", err)
case event, ok := <-w.Events:
if !ok {
return
}
if event.Name == h.hostsFilePath && event.Op&fsnotify.Write == fsnotify.Write {
err := h.updateHostsFile()
if err != nil {
log.Errorf("Hosts file read error:%s", err)
return
}
}
}
}
}

func (h *HostsFile) LookupByHostname(name string) (net.IP, error) {
h.hostsReadLock.RLock()
defer h.hostsReadLock.RUnlock()

_, ip, err := h.hostsFile.LookupByHostname(name)
return ip, err
}

func (h *HostsFile) updateHostsFile() error {
newHosts, err := readHostsFile(h.hostsFilePath)
if err != nil {
return err
}

h.hostsReadLock.Lock()
defer h.hostsReadLock.Unlock()

h.hostsFile = newHosts
return nil
}

func readHostsFile(hostsFilePath string) (*libhosty.HostsFile, error) {
config, err := libhosty.NewHostsFileConfig(hostsFilePath)
if err != nil {
return nil, err
}
return libhosty.InitWithConfig(config)
}
41 changes: 41 additions & 0 deletions pkg/services/dns/hosts_file_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package dns

import (
"os"
"path/filepath"
"testing"
"time"

"github.com/stretchr/testify/assert"
)

func TestHostsFile(t *testing.T) {
hostsFile := filepath.Join(t.TempDir(), "hosts")
assert.NoError(t, os.WriteFile(hostsFile, []byte(`127.0.0.1 entry1`), 0600))

hosts, err := NewHostsFile(hostsFile)
assert.NoError(t, err)
ip, err := hosts.LookupByHostname("entry1")
assert.NoError(t, err)
assert.Equal(t, "127.0.0.1", ip.String())
}

func TestReloadingHostsFile(t *testing.T) {
hostsFile := filepath.Join(t.TempDir(), "hosts")
assert.NoError(t, os.WriteFile(hostsFile, []byte(`127.0.0.1 entry1`), 0600))

hosts, err := NewHostsFile(hostsFile)
time.Sleep(time.Second)
assert.NoError(t, err)
ip, err := hosts.LookupByHostname("entry1")
assert.NoError(t, err)
assert.Equal(t, "127.0.0.1", ip.String())

assert.NoError(t, os.WriteFile(hostsFile, []byte(`127.0.0.1 entry2 foobar`), 0600))
time.Sleep(time.Second)

ipBar, err := hosts.LookupByHostname("foobar")
assert.NoError(t, err)
assert.Equal(t, "127.0.0.1", ipBar.String())

}
5 changes: 5 additions & 0 deletions vendor/github.com/areYouLazy/libhosty/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit de690ca

Please sign in to comment.