Skip to content

Commit 7403646

Browse files
author
Jan Broer
committed
Merge branch 'master' into minimal
2 parents 561a386 + 2da0269 commit 7403646

File tree

5 files changed

+192
-35
lines changed

5 files changed

+192
-35
lines changed

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# go-dnsmasq
2-
*Version 1.0.1*
2+
*Version 1.0.2*
33

44
go-dnsmasq is a light weight (1.2 MB) DNS caching server/forwarder with minimal filesystem and runtime overhead.
55

@@ -89,3 +89,14 @@ docker run -d -p 53:53/udp -p 53:53 janeczku/go-dnsmasq:latest
8989
```
9090

9191
You can configure the container by passing the corresponding environmental variables with docker run's `--env` flag.
92+
93+
#### Serving A/AAAA records from a hosts file
94+
The `--hostsfile` parameter expects a standard plain text [hosts file](https://en.wikipedia.org/wiki/Hosts_(file)) with the only difference being that a wildcard `*` in the left-most label of hostnames is allowed. Wildcard entries will match any subdomain that is not explicitely defined.
95+
For example, given a hosts file with the following content:
96+
97+
```
98+
192.168.0.1 db1.db.local
99+
192.168.0.2 *.db.local
100+
```
101+
102+
Queries for `db2.db.local` would be answered with an A record pointing to 192.168.0.2, while queries for `db1.db.local` would yield an A record pointing to 192.168.0.1.

hostsfile/hostsfile.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,7 @@ func (h *Hostsfile) FindHosts(name string) (addrs []net.IP, err error) {
6767
name = strings.TrimSuffix(name, ".")
6868
h.hostMutex.RLock()
6969
defer h.hostMutex.RUnlock()
70-
71-
for _, hostname := range *h.hosts {
72-
if hostname.domain == name {
73-
addrs = append(addrs, hostname.ip)
74-
}
75-
}
76-
70+
addrs = h.hosts.FindHosts(name);
7771
return
7872
}
7973

hostsfile/hostsfile_test.go

Lines changed: 107 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ package hosts
33
import (
44
"fmt"
55
"net"
6-
"runtime"
7-
"strings"
86
"testing"
97
)
108

@@ -27,6 +25,7 @@ const ipv4Fail = `
2725
const domain = "localhost"
2826
const ip = "127.0.0.1"
2927
const ipv6 = false
28+
const wildcard = false
3029

3130
func Diff(expected, actual string) string {
3231
return fmt.Sprintf(`
@@ -47,6 +46,38 @@ func (h *hostlist) Contains(b *hostname) bool {
4746
return false
4847
}
4948

49+
func TestEquality(t *testing.T) {
50+
var host1 *hostname
51+
var host2 *hostname
52+
53+
host1 = newHostname("hello", net.ParseIP("255.255.255.255"), false, false);
54+
host2 = newHostname("hello", net.ParseIP("255.255.255.255"), false, false);
55+
if !host1.Equal(host2) {
56+
t.Error("Hosts are expected equal, got: ", host1, host2);
57+
}
58+
59+
host2 = newHostname("hello2", net.ParseIP("255.255.255.255"), false, false);
60+
if host1.Equal(host2) {
61+
t.Error("Hosts are expected different, got: ", host1, host2);
62+
}
63+
64+
host2 = newHostname("hello1", net.ParseIP("255.255.255.254"), false, false);
65+
if host1.Equal(host2) {
66+
t.Error("Hosts are expected different, got: ", host1, host2);
67+
}
68+
69+
host2 = newHostname("hello1", net.ParseIP("255.255.255.255"), true, false);
70+
if host1.Equal(host2) {
71+
t.Error("Hosts are expected different, got: ", host1, host2);
72+
}
73+
74+
host2 = newHostname("hello1", net.ParseIP("255.255.255.255"), false, true);
75+
if host1.Equal(host2) {
76+
t.Error("Hosts are expected different, got: ", host1, host2);
77+
}
78+
79+
}
80+
5081
func TestParseLine(t *testing.T) {
5182
var hosts hostlist
5283

@@ -73,39 +104,100 @@ func TestParseLine(t *testing.T) {
73104
t.Error("Expected to find zero hostnames when line is commented out")
74105
}
75106

107+
var err error;
108+
err = hosts.add(newHostname("aaa", net.ParseIP("192.168.0.1"), false, false));
109+
if err != nil {
110+
t.Error("Did not expect error on first hostname");
111+
}
112+
err = hosts.add(newHostname("aaa", net.ParseIP("192.168.0.1"), false, false));
113+
if err == nil {
114+
t.Error("Expected error on duplicate host");
115+
}
116+
76117
// Not Commented stuff
77-
hosts = parseLine("255.255.255.255 broadcasthost test.domain.com domain.com")
78-
if !hosts.Contains(newHostname("broadcasthost", net.ParseIP("255.255.255.255"), false)) ||
79-
!hosts.Contains(newHostname("test.domain.com", net.ParseIP("255.255.255.255"), false)) ||
80-
!hosts.Contains(newHostname("domain.com", net.ParseIP("255.255.255.255"), false)) ||
118+
hosts = parseLine("192.168.0.1 broadcasthost test.domain.com domain.com")
119+
if !hosts.Contains(newHostname("broadcasthost", net.ParseIP("192.168.0.1"), false, false)) ||
120+
!hosts.Contains(newHostname("test.domain.com", net.ParseIP("192.168.0.1"), false, false)) ||
121+
!hosts.Contains(newHostname("domain.com", net.ParseIP("192.168.0.1"), false, false)) ||
81122
len(hosts) != 3 {
82123
t.Error("Expected to find broadcasthost, domain.com, and test.domain.com")
83124
}
84125

126+
// Wildcard stuff
127+
hosts = parseLine("192.168.0.1 *.domain.com mail.domain.com serenity")
128+
if !hosts.Contains(newHostname("domain.com", net.ParseIP("192.168.0.1"), false, true)) ||
129+
!hosts.Contains(newHostname("mail.domain.com", net.ParseIP("192.168.0.1"), false, false)) ||
130+
!hosts.Contains(newHostname("serenity", net.ParseIP("192.168.0.1"), false, false)) ||
131+
len(hosts) != 3 {
132+
t.Error("Expected to find *.domain.com, mail.domain.com and serenity.")
133+
}
134+
135+
var ip net.IP;
136+
137+
ip = hosts.FindHost("api.domain.com");
138+
if !net.ParseIP("192.168.0.1").Equal(ip) {
139+
t.Error("Can't match wildcard host api.domain.com");
140+
}
141+
142+
ip = hosts.FindHost("google.com")
143+
if ip != nil {
144+
t.Error("We shouldn't resolve google.com");
145+
}
146+
147+
hosts = *newHostlistString(`192.168.0.1 *.domain.com mail.domain.com serenity
148+
192.168.0.2 api.domain.com`);
149+
150+
if (!net.ParseIP("192.168.0.2").Equal(hosts.FindHost("api.domain.com"))) {
151+
t.Error("Failed matching api.domain.com explicitly");
152+
}
153+
if (!net.ParseIP("192.168.0.1").Equal(hosts.FindHost("mail.domain.com"))) {
154+
t.Error("Failed matching api.domain.com explicitly");
155+
}
156+
if (!net.ParseIP("192.168.0.1").Equal(hosts.FindHost("wildcard.domain.com"))) {
157+
t.Error("Failed matching wildcard.domain.com explicitly");
158+
}
159+
if (net.ParseIP("192.168.0.1").Equal(hosts.FindHost("sub.wildcard.domain.com"))) {
160+
t.Error("Failed not matching sub.wildcard.domain.com explicitly");
161+
}
162+
163+
// IPv6 (not link-local)
164+
hosts = parseLine("2a02:7a8:1:250::80:1 rtvslo.si img.rtvslo.si")
165+
if !hosts.Contains(newHostname("img.rtvslo.si", net.ParseIP("2a02:7a8:1:250::80:1"), true, false)) ||
166+
len(hosts) != 2 {
167+
t.Error("Expected to find rtvslo.si ipv6, two hosts")
168+
}
169+
170+
/* the following all fails since the addressses are link-local */
171+
172+
/*
85173
// Ipv6 stuff
86-
hosts = hostess.parseLine("::1 localhost")
87-
if !hosts.Contains(newHostname("localhost", net.ParseIP("::1"), true)) ||
174+
hosts = parseLine("::1 localhost")
175+
if !hosts.Contains(newHostname("localhost", net.ParseIP("::1"), true, false)) ||
88176
len(hosts) != 1 {
89-
t.Error("Expected to find localhost ipv6 (enabled)")
177+
t.Error("Expected to find localhost ipv6")
90178
}
91179
92-
hosts = hostess.parseLine("ff02::1 ip6-allnodes")
93-
if !hosts.Contains(newHostname("ip6-allnodes", net.ParseIP("ff02::1"), true)) ||
180+
hosts = parseLine("ff02::1 ip6-allnodes")
181+
if !hosts.Contains(newHostname("ip6-allnodes", net.ParseIP("ff02::1"), true, false)) ||
94182
len(hosts) != 1 {
95-
t.Error("Expected to find ip6-allnodes ipv6 (enabled)")
183+
t.Error("Expected to find ip6-allnodes ipv6")
96184
}
185+
*/
97186
}
98187

99188
func TestHostname(t *testing.T) {
100-
h := newHostname(domain, net.ParseIP(ip), ipv6)
189+
h := newHostname(domain, net.ParseIP(ip), ipv6, wildcard)
101190

102191
if h.domain != domain {
103192
t.Errorf("Domain should be %s", domain)
104193
}
105194
if !h.ip.Equal(net.ParseIP(ip)) {
106195
t.Errorf("IP should be %s", ip)
107196
}
108-
if h.ipv6 != enabled {
109-
t.Errorf("Enabled should be %t", enabled)
197+
if h.ipv6 != ipv6 {
198+
t.Errorf("IPv6 should be %t", ipv6)
199+
}
200+
if h.wildcard != wildcard {
201+
t.Errorf("Wildcard should be %t", wildcard)
110202
}
111203
}

hostsfile/utils.go

Lines changed: 71 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,20 @@ import (
1717
type hostlist []*hostname
1818

1919
type hostname struct {
20-
domain string
21-
ip net.IP
22-
ipv6 bool
20+
domain string
21+
ip net.IP
22+
ipv6 bool
23+
wildcard bool
2324
}
2425

2526
// newHostlist creates a hostlist by parsing a file
2627
func newHostlist(data []byte) *hostlist {
28+
return newHostlistString(string(data));
29+
}
30+
31+
func newHostlistString(data string) *hostlist {
2732
hostlist := hostlist{}
28-
for _, v := range strings.Split(string(data), "\n") {
33+
for _, v := range strings.Split(data, "\n") {
2934
for _, hostname := range parseLine(v) {
3035
err := hostlist.add(hostname)
3136
if err != nil {
@@ -36,22 +41,71 @@ func newHostlist(data []byte) *hostlist {
3641
return &hostlist
3742
}
3843

44+
func (h *hostname) Equal(hostnamev *hostname) bool {
45+
if (h.wildcard != hostnamev.wildcard || h.ipv6 != hostnamev.ipv6) {
46+
return false
47+
}
48+
if (!h.ip.Equal(hostnamev.ip)) {
49+
return false
50+
}
51+
if (h.domain != hostnamev.domain) {
52+
return false
53+
}
54+
return true
55+
}
56+
57+
// return first match
58+
func (h *hostlist) FindHost(name string) (addr net.IP) {
59+
var ips []net.IP;
60+
ips = h.FindHosts(name)
61+
if len(ips) > 0 {
62+
addr = ips[0];
63+
}
64+
return
65+
}
66+
67+
// return exact matches, if existing -> else, return wildcard
68+
func (h *hostlist) FindHosts(name string) (addrs []net.IP) {
69+
for _, hostname := range *h {
70+
if hostname.wildcard == false && hostname.domain == name {
71+
addrs = append(addrs, hostname.ip)
72+
}
73+
}
74+
75+
if len(addrs) == 0 {
76+
var domain_match string;
77+
for _, hostname := range *h {
78+
if hostname.wildcard == true && len(hostname.domain) < len(name) {
79+
domain_match = strings.Join([]string{".", hostname.domain}, "");
80+
if name[len(name)-len(domain_match):] == domain_match {
81+
var left string;
82+
left = name[0:len(name)-len(domain_match)]
83+
if !strings.Contains(left, ".") {
84+
addrs = append(addrs, hostname.ip)
85+
}
86+
}
87+
}
88+
}
89+
}
90+
91+
return
92+
}
93+
3994
func (h *hostlist) add(hostnamev *hostname) error {
40-
hostname := newHostname(hostnamev.domain, hostnamev.ip, hostnamev.ipv6)
95+
hostname := newHostname(hostnamev.domain, hostnamev.ip, hostnamev.ipv6, hostnamev.wildcard)
4196
for _, found := range *h {
42-
if found.domain == hostname.domain && found.ip.Equal(hostname.ip) {
43-
return fmt.Errorf("Duplicate hostname entry for %s -> %s",
44-
hostname.domain, hostname.ip)
97+
if found.Equal(hostname) {
98+
return fmt.Errorf("Duplicate hostname entry for %#v", hostname)
4599
}
46100
}
47101
*h = append(*h, hostname)
48102
return nil
49103
}
50104

51105
// newHostname creates a new Hostname struct
52-
func newHostname(domain string, ip net.IP, ipv6 bool) (host *hostname) {
106+
func newHostname(domain string, ip net.IP, ipv6 bool, wildcard bool) (host *hostname) {
53107
domain = strings.ToLower(domain)
54-
host = &hostname{domain, ip, ipv6}
108+
host = &hostname{domain, ip, ipv6, wildcard}
55109
return
56110
}
57111

@@ -114,8 +168,14 @@ func parseLine(line string) hostlist {
114168
return hostnames
115169
}
116170

171+
var isWildcard bool
117172
for _, v := range domains {
118-
hostname := newHostname(v, ip, isIPv6)
173+
isWildcard = false
174+
if v[0:2] == "*." {
175+
v = v[2:]
176+
isWildcard = true
177+
}
178+
hostname := newHostname(v, ip, isIPv6, isWildcard)
119179
hostnames = append(hostnames, hostname)
120180
}
121181

main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import (
2626
)
2727

2828
// var Version string
29-
const Version = "1.0.1"
29+
const Version = "1.0.2"
3030

3131
var (
3232
nameservers = []string{}

0 commit comments

Comments
 (0)