Add inline support for middleware/hosts (#1072)

This fix add inline support for middleware/hosts so that
it is possible to specify hosts file insides the Corefile:
```
hosts inline example.org {
    10.0.0.1 example.org
    fallthrough
}
```

This fix fixes 999.

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
This commit is contained in:
Yong Tang
2017-09-21 04:18:13 -07:00
committed by GitHub
parent aecf916377
commit 7109c6715c
5 changed files with 157 additions and 2 deletions

View File

@@ -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
}
~~~

View File

@@ -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 {

View File

@@ -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())
}
}

View File

@@ -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])
}
}
}
}
}
}
}