aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plugin/hosts/README.md15
-rw-r--r--plugin/hosts/hostsfile.go16
-rw-r--r--plugin/hosts/setup.go6
-rw-r--r--plugin/hosts/setup_test.go74
-rw-r--r--test/hosts_file_test.go48
5 files changed, 157 insertions, 2 deletions
diff --git a/plugin/hosts/README.md b/plugin/hosts/README.md
index 60c738077..5e6ab0ec7 100644
--- a/plugin/hosts/README.md
+++ b/plugin/hosts/README.md
@@ -11,6 +11,7 @@ available hosts files that block access to advertising servers.
~~~
hosts [FILE [ZONES...]] {
+ [INLINE]
fallthrough
}
~~~
@@ -18,7 +19,10 @@ hosts [FILE [ZONES...]] {
* **FILE** the hosts file to read and parse. If the path is relative the path from the *root*
directive will be prepended to it. Defaults to /etc/hosts if omitted
* **ZONES** zones it should be authoritative for. If empty, the zones from the configuration block
- are used.
+ are used.
+* **INLINE** the hosts file contents inlined in Corefile. If there are any lines before fallthrough
+ then all of them will be treated as the additional content for hosts file. The specified hosts
+ file path will still be read but entries will be overrided.
* `fallthrough` If zone matches and no record can be generated, pass request to the next plugin.
## Examples
@@ -43,3 +47,12 @@ hosts example.hosts example.org example.net {
fallthrough
}
~~~
+
+Load hosts file inlined in Corefile.
+
+~~~
+hosts example.hosts example.org {
+ 10.0.0.1 example.org
+ fallthrough
+}
+~~~
diff --git a/plugin/hosts/hostsfile.go b/plugin/hosts/hostsfile.go
index 91e828099..0862711fe 100644
--- a/plugin/hosts/hostsfile.go
+++ b/plugin/hosts/hostsfile.go
@@ -53,6 +53,11 @@ type Hostsfile struct {
// We don't support old-classful IP address notation.
byAddr map[string][]string
+ // inline saves the hosts file is inlined in Corefile
+ // We need a copy here as we want to use inline to override
+ // the default /etc/hosts
+ inline []string
+
expire time.Time
path string
mtime time.Time
@@ -74,6 +79,10 @@ func (h *Hostsfile) ReadHosts() {
var file *os.File
if file, _ = os.Open(h.path); file == nil {
+ // If this is the first time then we will try to parse inline
+ if len(h.byAddr) == 0 && len(h.inline) > 0 {
+ h.Parse(nil)
+ }
return
}
defer file.Close()
@@ -92,7 +101,12 @@ func (h *Hostsfile) Parse(file io.Reader) {
hsv6 := make(map[string][]net.IP)
is := make(map[string][]string)
- scanner := bufio.NewScanner(file)
+ var readers []io.Reader
+ if file != nil {
+ readers = append(readers, file)
+ }
+ readers = append(readers, strings.NewReader(strings.Join(h.inline, "\n")))
+ scanner := bufio.NewScanner(io.MultiReader(readers...))
for scanner.Scan() {
line := scanner.Bytes()
if i := bytes.Index(line, []byte{'#'}); i >= 0 {
diff --git a/plugin/hosts/setup.go b/plugin/hosts/setup.go
index c7c0c728a..0c13140bb 100644
--- a/plugin/hosts/setup.go
+++ b/plugin/hosts/setup.go
@@ -4,6 +4,7 @@ import (
"log"
"os"
"path"
+ "strings"
"github.com/coredns/coredns/core/dnsserver"
"github.com/coredns/coredns/plugin"
@@ -80,6 +81,11 @@ func hostsParse(c *caddy.Controller) (Hosts, error) {
}
return h, c.ArgErr()
default:
+ if !h.Fallthrough {
+ line := strings.Join(append([]string{c.Val()}, c.RemainingArgs()...), " ")
+ h.inline = append(h.inline, line)
+ continue
+ }
return h, c.Errf("unknown property '%s'", c.Val())
}
}
diff --git a/plugin/hosts/setup_test.go b/plugin/hosts/setup_test.go
index a4c95b1c6..fefed6c72 100644
--- a/plugin/hosts/setup_test.go
+++ b/plugin/hosts/setup_test.go
@@ -84,3 +84,77 @@ func TestHostsParse(t *testing.T) {
}
}
}
+
+func TestHostsInlineParse(t *testing.T) {
+ tests := []struct {
+ inputFileRules string
+ shouldErr bool
+ expectedbyAddr map[string][]string
+ expectedFallthrough bool
+ }{
+ {
+ `hosts highly_unlikely_to_exist_hosts_file example.org {
+ 10.0.0.1 example.org
+ fallthrough
+ }`,
+ false,
+ map[string][]string{
+ `10.0.0.1`: {
+ `example.org.`,
+ },
+ },
+ true,
+ },
+ {
+ `hosts highly_unlikely_to_exist_hosts_file example.org {
+ 10.0.0.1 example.org
+ }`,
+ false,
+ map[string][]string{
+ `10.0.0.1`: {
+ `example.org.`,
+ },
+ },
+ false,
+ },
+ {
+ `hosts highly_unlikely_to_exist_hosts_file example.org {
+ fallthrough
+ 10.0.0.1 example.org
+ }`,
+ true,
+ map[string][]string{},
+ true,
+ },
+ }
+
+ for i, test := range tests {
+ c := caddy.NewTestController("dns", test.inputFileRules)
+ h, err := hostsParse(c)
+
+ if err == nil && test.shouldErr {
+ t.Fatalf("Test %d expected errors, but got no error", i)
+ } else if err != nil && !test.shouldErr {
+ t.Fatalf("Test %d expected no errors, but got '%v'", i, err)
+ } else if !test.shouldErr {
+ if h.Fallthrough != test.expectedFallthrough {
+ t.Fatalf("Test %d expected fallthrough of %v, got %v", i, test.expectedFallthrough, h.Fallthrough)
+ }
+ for k, expectedVal := range test.expectedbyAddr {
+ if val, ok := h.byAddr[k]; !ok {
+ t.Fatalf("Test %d expected %v, got no entry", i, k)
+ } else {
+ if len(expectedVal) != len(val) {
+ t.Fatalf("Test %d expected %v records for %v, got %v", i, len(expectedVal), k, len(val))
+ }
+ for j := range expectedVal {
+ if expectedVal[j] != val[j] {
+ t.Fatalf("Test %d expected %v for %v, got %v", i, expectedVal[j], j, val[j])
+ }
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/test/hosts_file_test.go b/test/hosts_file_test.go
new file mode 100644
index 000000000..fa50233f0
--- /dev/null
+++ b/test/hosts_file_test.go
@@ -0,0 +1,48 @@
+package test
+
+import (
+ "io/ioutil"
+ "log"
+ "testing"
+
+ "github.com/coredns/coredns/plugin/proxy"
+ "github.com/coredns/coredns/plugin/test"
+ "github.com/coredns/coredns/request"
+
+ "github.com/miekg/dns"
+)
+
+func TestHostsInlineLookup(t *testing.T) {
+ corefile := `example.org:0 {
+ hosts highly_unlikely_to_exist_hosts_file example.org {
+ 10.0.0.1 example.org
+ fallthrough
+ }
+ }`
+
+ i, udp, _, err := CoreDNSServerAndPorts(corefile)
+ if err != nil {
+ t.Fatalf("Could not get CoreDNS serving instance: %s", err)
+ }
+ defer i.Stop()
+
+ log.SetOutput(ioutil.Discard)
+
+ p := proxy.NewLookup([]string{udp})
+ state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)}
+
+ resp, err := p.Lookup(state, "example.org.", dns.TypeA)
+ if err != nil {
+ t.Fatal("Expected to receive reply, but didn't")
+ }
+ // expect answer section with A record in it
+ if len(resp.Answer) == 0 {
+ t.Fatal("Expected to at least one RR in the answer section, got none")
+ }
+ if resp.Answer[0].Header().Rrtype != dns.TypeA {
+ t.Errorf("Expected RR to A, got: %d", resp.Answer[0].Header().Rrtype)
+ }
+ if resp.Answer[0].(*dns.A).A.String() != "10.0.0.1" {
+ t.Errorf("Expected 10.0.0.1, got: %s", resp.Answer[0].(*dns.A).A.String())
+ }
+}