Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
C
Coredns
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Railgun
Coredns
Commits
b6341e8b
Commit
b6341e8b
authored
Mar 20, 2016
by
Miek Gieben
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
more etcd stuff
parent
0c94de4f
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
609 additions
and
206 deletions
+609
-206
core/setup/etcd.go
core/setup/etcd.go
+18
-31
core/setup/file.go
core/setup/file.go
+1
-0
middleware/etcd/backend.go
middleware/etcd/backend.go
+0
-164
middleware/etcd/etcd.go
middleware/etcd/etcd.go
+124
-8
middleware/etcd/etcd.md
middleware/etcd/etcd.md
+6
-3
middleware/etcd/handler.go
middleware/etcd/handler.go
+105
-0
middleware/etcd/lookup.go
middleware/etcd/lookup.go
+327
-0
middleware/exchange.go
middleware/exchange.go
+4
-0
middleware/state.go
middleware/state.go
+24
-0
No files found.
core/setup/etcd.go
View file @
b6341e8b
...
@@ -8,24 +8,24 @@ import (
...
@@ -8,24 +8,24 @@ import (
"net/http"
"net/http"
"time"
"time"
etcdc
"github.com/coreos/etcd/client"
"github.com/miekg/coredns/middleware"
"github.com/miekg/coredns/middleware"
"github.com/miekg/coredns/middleware/file"
"github.com/miekg/coredns/middleware/etcd"
etcdc
"github.com/coreos/etcd/client"
)
)
const
default
Address
=
"http://127.0.0.1:2379"
const
default
Endpoint
=
"http://127.0.0.1:2379"
// Etcd sets up the etcd middleware.
// Etcd sets up the etcd middleware.
func
Etcd
(
c
*
Controller
)
(
middleware
.
Middleware
,
error
)
{
func
Etcd
(
c
*
Controller
)
(
middleware
.
Middleware
,
error
)
{
keysapi
,
err
:=
etcdParse
(
c
)
client
,
err
:=
etcdParse
(
c
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
return
func
(
next
middleware
.
Handler
)
middleware
.
Handler
{
return
func
(
next
middleware
.
Handler
)
middleware
.
Handler
{
return
file
.
File
{
Next
:
next
,
Zones
:
zones
}
return
etcd
.
NewEtcd
(
client
,
next
,
c
.
ServerBlockHosts
)
},
nil
},
nil
}
}
func
etcdParse
(
c
*
Controller
)
(
etcdc
.
KeysAPI
,
error
)
{
func
etcdParse
(
c
*
Controller
)
(
etcdc
.
KeysAPI
,
error
)
{
...
@@ -33,43 +33,30 @@ func etcdParse(c *Controller) (etcdc.KeysAPI, error) {
...
@@ -33,43 +33,30 @@ func etcdParse(c *Controller) (etcdc.KeysAPI, error) {
if
c
.
Val
()
==
"etcd"
{
if
c
.
Val
()
==
"etcd"
{
// etcd [address...]
// etcd [address...]
if
!
c
.
NextArg
()
{
if
!
c
.
NextArg
()
{
// TODO(certs) and friends, this is client side
return
file
.
Zones
{},
c
.
ArgErr
()
client
,
err
:=
newEtcdClient
([]
string
{
defaultEndpoint
},
""
,
""
,
""
)
}
return
client
,
err
args1
:=
c
.
RemainingArgs
()
fileName
:=
c
.
Val
()
origin
:=
c
.
ServerBlockHosts
[
c
.
ServerBlockHostIndex
]
if
c
.
NextArg
()
{
c
.
Next
()
origin
=
c
.
Val
()
}
// normalize this origin
origin
=
middleware
.
Host
(
origin
)
.
StandardHost
()
zone
,
err
:=
parseZone
(
origin
,
fileName
)
if
err
==
nil
{
z
[
origin
]
=
zone
}
}
names
=
append
(
names
,
origin
)
client
,
err
:=
newEtcdClient
(
c
.
RemainingArgs
(),
""
,
""
,
""
)
return
client
,
err
}
}
}
}
return
file
.
Zones
{
Z
:
z
,
Names
:
names
}
,
nil
return
nil
,
nil
}
}
func
newEtcdClient
(
machines
[]
string
,
tlsCert
,
tlsKey
,
tlsCACert
string
)
(
etcd
.
KeysAPI
,
error
)
{
func
newEtcdClient
(
endpoints
[]
string
,
tlsCert
,
tlsKey
,
tlsCACert
string
)
(
etcdc
.
KeysAPI
,
error
)
{
etcdCfg
:=
etcd
.
Config
{
etcdCfg
:=
etcd
c
.
Config
{
Endpoints
:
machine
s
,
Endpoints
:
endpoint
s
,
Transport
:
newHTTPSTransport
(
tlsCert
,
tlsKey
,
tlsCACert
),
Transport
:
newHTTPSTransport
(
tlsCert
,
tlsKey
,
tlsCACert
),
}
}
cli
,
err
:=
etcd
.
New
(
etcdCfg
)
cli
,
err
:=
etcd
c
.
New
(
etcdCfg
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
return
etcd
.
NewKeysAPI
(
cli
),
nil
return
etcd
c
.
NewKeysAPI
(
cli
),
nil
}
}
func
newHTTPSTransport
(
tlsCertFile
,
tlsKeyFile
,
tlsCACertFile
string
)
etcd
.
CancelableTransport
{
func
newHTTPSTransport
(
tlsCertFile
,
tlsKeyFile
,
tlsCACertFile
string
)
etcd
c
.
CancelableTransport
{
var
cc
*
tls
.
Config
=
nil
var
cc
*
tls
.
Config
=
nil
if
tlsCertFile
!=
""
&&
tlsKeyFile
!=
""
{
if
tlsCertFile
!=
""
&&
tlsKeyFile
!=
""
{
...
...
core/setup/file.go
View file @
b6341e8b
...
@@ -6,6 +6,7 @@ import (
...
@@ -6,6 +6,7 @@ import (
"github.com/miekg/coredns/middleware"
"github.com/miekg/coredns/middleware"
"github.com/miekg/coredns/middleware/file"
"github.com/miekg/coredns/middleware/file"
"github.com/miekg/dns"
"github.com/miekg/dns"
)
)
...
...
middleware/etcd/backend.go
deleted
100644 → 0
View file @
0c94de4f
// Package etcd provides the etcd server Backend implementation,
package
etcd
import
(
"encoding/json"
"fmt"
"strings"
"github.com/miekg/coredns/middleware/etcd/msg"
etcdc
"github.com/coreos/etcd/client"
)
const
(
priority
=
10
// default priority when nothing is set
ttl
=
3600
// default ttl when nothing is set
)
func
(
g
*
Backend
)
Records
(
name
string
,
exact
bool
)
([]
msg
.
Service
,
error
)
{
path
,
star
:=
msg
.
PathWithWildcard
(
name
)
r
,
err
:=
g
.
get
(
path
,
true
)
if
err
!=
nil
{
return
nil
,
err
}
segments
:=
strings
.
Split
(
msg
.
Path
(
name
),
"/"
)
switch
{
case
exact
&&
r
.
Node
.
Dir
:
return
nil
,
nil
case
r
.
Node
.
Dir
:
return
g
.
loopNodes
(
r
.
Node
.
Nodes
,
segments
,
star
,
nil
)
default
:
return
g
.
loopNodes
([]
*
etcdc
.
Node
{
r
.
Node
},
segments
,
false
,
nil
)
}
}
func
(
g
*
Backend
)
ReverseRecord
(
name
string
)
(
*
msg
.
Service
,
error
)
{
path
,
star
:=
msg
.
PathWithWildcard
(
name
)
if
star
{
return
nil
,
fmt
.
Errorf
(
"reverse can not contain wildcards"
)
}
r
,
err
:=
g
.
get
(
path
,
true
)
if
err
!=
nil
{
return
nil
,
err
}
if
r
.
Node
.
Dir
{
return
nil
,
fmt
.
Errorf
(
"reverse must not be a directory"
)
}
segments
:=
strings
.
Split
(
msg
.
Path
(
name
),
"/"
)
records
,
err
:=
g
.
loopNodes
([]
*
etcdc
.
Node
{
r
.
Node
},
segments
,
false
,
nil
)
if
err
!=
nil
{
return
nil
,
err
}
if
len
(
records
)
!=
1
{
return
nil
,
fmt
.
Errorf
(
"must be only one service record"
)
}
return
&
records
[
0
],
nil
}
// get is a wrapper for client.Get that uses SingleInflight to suppress multiple
// outstanding queries.
func
(
g
*
Backend
)
get
(
path
string
,
recursive
bool
)
(
*
etcdc
.
Response
,
error
)
{
resp
,
err
:=
g
.
inflight
.
Do
(
path
,
func
()
(
interface
{},
error
)
{
r
,
e
:=
g
.
client
.
Get
(
g
.
ctx
,
path
,
&
etcdc
.
GetOptions
{
Sort
:
false
,
Recursive
:
recursive
})
if
e
!=
nil
{
return
nil
,
e
}
return
r
,
e
})
if
err
!=
nil
{
return
nil
,
err
}
return
resp
.
(
*
etcdc
.
Response
),
err
}
type
bareService
struct
{
Host
string
Port
int
Priority
int
Weight
int
Text
string
}
// skydns/local/skydns/east/staging/web
// skydns/local/skydns/west/production/web
//
// skydns/local/skydns/*/*/web
// skydns/local/skydns/*/web
// loopNodes recursively loops through the nodes and returns all the values. The nodes' keyname
// will be match against any wildcards when star is true.
func
(
g
*
Backend
)
loopNodes
(
ns
[]
*
etcdc
.
Node
,
nameParts
[]
string
,
star
bool
,
bx
map
[
bareService
]
bool
)
(
sx
[]
msg
.
Service
,
err
error
)
{
if
bx
==
nil
{
bx
=
make
(
map
[
bareService
]
bool
)
}
Nodes
:
for
_
,
n
:=
range
ns
{
if
n
.
Dir
{
nodes
,
err
:=
g
.
loopNodes
(
n
.
Nodes
,
nameParts
,
star
,
bx
)
if
err
!=
nil
{
return
nil
,
err
}
sx
=
append
(
sx
,
nodes
...
)
continue
}
if
star
{
keyParts
:=
strings
.
Split
(
n
.
Key
,
"/"
)
for
i
,
n
:=
range
nameParts
{
if
i
>
len
(
keyParts
)
-
1
{
// name is longer than key
continue
Nodes
}
if
n
==
"*"
||
n
==
"any"
{
continue
}
if
keyParts
[
i
]
!=
n
{
continue
Nodes
}
}
}
serv
:=
new
(
msg
.
Service
)
if
err
:=
json
.
Unmarshal
([]
byte
(
n
.
Value
),
serv
);
err
!=
nil
{
return
nil
,
err
}
b
:=
bareService
{
serv
.
Host
,
serv
.
Port
,
serv
.
Priority
,
serv
.
Weight
,
serv
.
Text
}
if
_
,
ok
:=
bx
[
b
];
ok
{
continue
}
bx
[
b
]
=
true
serv
.
Key
=
n
.
Key
serv
.
Ttl
=
g
.
calculateTtl
(
n
,
serv
)
if
serv
.
Priority
==
0
{
serv
.
Priority
=
priority
}
sx
=
append
(
sx
,
*
serv
)
}
return
sx
,
nil
}
// calculateTtl returns the smaller of the etcd TTL and the service's
// TTL. If neither of these are set (have a zero value), the server
// default is used.
func
(
g
*
Backend
)
calculateTtl
(
node
*
etcdc
.
Node
,
serv
*
msg
.
Service
)
uint32
{
etcdTtl
:=
uint32
(
node
.
TTL
)
if
etcdTtl
==
0
&&
serv
.
Ttl
==
0
{
return
ttl
}
if
etcdTtl
==
0
{
return
serv
.
Ttl
}
if
serv
.
Ttl
==
0
{
return
etcdTtl
}
if
etcdTtl
<
serv
.
Ttl
{
return
etcdTtl
}
return
serv
.
Ttl
}
// Client exposes the underlying Etcd client (used in tests).
func
(
g
*
Backend
)
Client
()
etcdc
.
KeysAPI
{
return
g
.
client
}
middleware/etcd/etcd.go
View file @
b6341e8b
...
@@ -2,9 +2,12 @@
...
@@ -2,9 +2,12 @@
package
etcd
package
etcd
import
(
import
(
"encoding/json"
"strings"
"github.com/miekg/coredns/middleware"
"github.com/miekg/coredns/middleware"
"github.com/miekg/
dns
"
"github.com/miekg/
coredns/middleware/etcd/msg
"
"github.com/
skynetservices/skydns
/singleflight"
"github.com/
miekg/coredns/middleware/etcd
/singleflight"
etcdc
"github.com/coreos/etcd/client"
etcdc
"github.com/coreos/etcd/client"
"golang.org/x/net/context"
"golang.org/x/net/context"
...
@@ -12,23 +15,136 @@ import (
...
@@ -12,23 +15,136 @@ import (
type
(
type
(
Etcd
struct
{
Etcd
struct
{
Next
middleware
.
Handler
Next
middleware
.
Handler
Zones
[]
string
client
etcd
.
KeysAPI
client
etcd
c
.
KeysAPI
ctx
context
.
Context
ctx
context
.
Context
inflight
*
singleflight
.
Group
inflight
*
singleflight
.
Group
}
}
)
)
func
NewEtcd
(
client
etcdc
.
KeysAPI
,
next
middleware
.
Handler
)
Etcd
{
func
NewEtcd
(
client
etcdc
.
KeysAPI
,
next
middleware
.
Handler
,
zones
[]
string
)
Etcd
{
return
Etcd
{
return
Etcd
{
Next
:
next
,
Next
:
next
,
Zones
:
zones
,
client
:
client
,
client
:
client
,
ctx
:
context
.
Background
(),
ctx
:
context
.
Background
(),
inflight
:
&
singleflight
.
Group
{},
inflight
:
&
singleflight
.
Group
{},
}
}
}
}
func
(
e
Etcd
)
ServerDNS
(
ctx
context
.
Context
,
w
dns
.
ResponseWriter
,
r
*
dns
.
Msg
)
(
int
,
error
)
{
func
(
g
Etcd
)
Records
(
name
string
,
exact
bool
)
([]
msg
.
Service
,
error
)
{
return
0
,
nil
path
,
star
:=
msg
.
PathWithWildcard
(
name
)
r
,
err
:=
g
.
Get
(
path
,
true
)
if
err
!=
nil
{
return
nil
,
err
}
segments
:=
strings
.
Split
(
msg
.
Path
(
name
),
"/"
)
switch
{
case
exact
&&
r
.
Node
.
Dir
:
return
nil
,
nil
case
r
.
Node
.
Dir
:
return
g
.
loopNodes
(
r
.
Node
.
Nodes
,
segments
,
star
,
nil
)
default
:
return
g
.
loopNodes
([]
*
etcdc
.
Node
{
r
.
Node
},
segments
,
false
,
nil
)
}
}
// Get is a wrapper for client.Get that uses SingleInflight to suppress multiple outstanding queries.
func
(
g
Etcd
)
Get
(
path
string
,
recursive
bool
)
(
*
etcdc
.
Response
,
error
)
{
resp
,
err
:=
g
.
inflight
.
Do
(
path
,
func
()
(
interface
{},
error
)
{
r
,
e
:=
g
.
client
.
Get
(
g
.
ctx
,
path
,
&
etcdc
.
GetOptions
{
Sort
:
false
,
Recursive
:
recursive
})
if
e
!=
nil
{
return
nil
,
e
}
return
r
,
e
})
if
err
!=
nil
{
return
nil
,
err
}
return
resp
.
(
*
etcdc
.
Response
),
err
}
// skydns/local/skydns/east/staging/web
// skydns/local/skydns/west/production/web
//
// skydns/local/skydns/*/*/web
// skydns/local/skydns/*/web
// loopNodes recursively loops through the nodes and returns all the values. The nodes' keyname
// will be match against any wildcards when star is true.
func
(
g
Etcd
)
loopNodes
(
ns
[]
*
etcdc
.
Node
,
nameParts
[]
string
,
star
bool
,
bx
map
[
msg
.
Service
]
bool
)
(
sx
[]
msg
.
Service
,
err
error
)
{
if
bx
==
nil
{
bx
=
make
(
map
[
msg
.
Service
]
bool
)
}
Nodes
:
for
_
,
n
:=
range
ns
{
if
n
.
Dir
{
nodes
,
err
:=
g
.
loopNodes
(
n
.
Nodes
,
nameParts
,
star
,
bx
)
if
err
!=
nil
{
return
nil
,
err
}
sx
=
append
(
sx
,
nodes
...
)
continue
}
if
star
{
keyParts
:=
strings
.
Split
(
n
.
Key
,
"/"
)
for
i
,
n
:=
range
nameParts
{
if
i
>
len
(
keyParts
)
-
1
{
// name is longer than key
continue
Nodes
}
if
n
==
"*"
||
n
==
"any"
{
continue
}
if
keyParts
[
i
]
!=
n
{
continue
Nodes
}
}
}
serv
:=
new
(
msg
.
Service
)
if
err
:=
json
.
Unmarshal
([]
byte
(
n
.
Value
),
serv
);
err
!=
nil
{
return
nil
,
err
}
b
:=
msg
.
Service
{
Host
:
serv
.
Host
,
Port
:
serv
.
Port
,
Priority
:
serv
.
Priority
,
Weight
:
serv
.
Weight
,
Text
:
serv
.
Text
}
if
_
,
ok
:=
bx
[
b
];
ok
{
continue
}
bx
[
b
]
=
true
serv
.
Key
=
n
.
Key
serv
.
Ttl
=
g
.
Ttl
(
n
,
serv
)
if
serv
.
Priority
==
0
{
serv
.
Priority
=
priority
}
sx
=
append
(
sx
,
*
serv
)
}
return
sx
,
nil
}
// Ttl returns the smaller of the etcd TTL and the service's
// TTL. If neither of these are set (have a zero value), a default is used.
func
(
g
Etcd
)
Ttl
(
node
*
etcdc
.
Node
,
serv
*
msg
.
Service
)
uint32
{
etcdTtl
:=
uint32
(
node
.
TTL
)
if
etcdTtl
==
0
&&
serv
.
Ttl
==
0
{
return
ttl
}
if
etcdTtl
==
0
{
return
serv
.
Ttl
}
if
serv
.
Ttl
==
0
{
return
etcdTtl
}
if
etcdTtl
<
serv
.
Ttl
{
return
etcdTtl
}
return
serv
.
Ttl
}
}
const
(
priority
=
10
// default priority when nothing is set
ttl
=
300
// default ttl when nothing is set
minTtl
=
60
hostmaster
=
"hostmaster"
)
middleware/etcd/etcd.md
View file @
b6341e8b
...
@@ -2,7 +2,10 @@
...
@@ -2,7 +2,10 @@
`etcd`
enabled reading zone data from an etcd instance. The data in etcd has to be encoded as
`etcd`
enabled reading zone data from an etcd instance. The data in etcd has to be encoded as
a
[
message
](
https://github.com/skynetservices/skydns/blob/2fcff74cdc9f9a7dd64189a447ef27ac354b725f/msg/service.go#L26
)
a
[
message
](
https://github.com/skynetservices/skydns/blob/2fcff74cdc9f9a7dd64189a447ef27ac354b725f/msg/service.go#L26
)
like SkyDNS.
like
[
SkyDNS
](
https//github.com/skynetservices/skydns
)
.
If you need replies to SOA and NS queries you should add a little zone after etcd directive that has
these resource records.
## Syntax
## Syntax
...
@@ -14,16 +17,16 @@ etcd [endpoint...]
...
@@ -14,16 +17,16 @@ etcd [endpoint...]
The will default to
`/skydns`
as the path and the local etcd proxy (http://127.0.0.1:2379).
The will default to
`/skydns`
as the path and the local etcd proxy (http://127.0.0.1:2379).
If you want to
`round robin`
A and AAAA responses look at the
`round_robin`
middleware.
~~~
~~~
etcd {
etcd {
round_robin
path /skydns
path /skydns
endpoint address...
endpoint address...
stubzones
stubzones
}
}
~~~
~~~
*
`round_robin`
*
`path`
/skydns
*
`path`
/skydns
*
`endpoint`
address...
*
`endpoint`
address...
*
`stubzones`
*
`stubzones`
...
...
middleware/etcd/handler.go
View file @
b6341e8b
package
etcd
import
(
"github.com/miekg/coredns/middleware"
"github.com/miekg/dns"
"golang.org/x/net/context"
)
func
(
e
Etcd
)
ServeDNS
(
ctx
context
.
Context
,
w
dns
.
ResponseWriter
,
r
*
dns
.
Msg
)
(
int
,
error
)
{
println
(
"ETCD MIDDLEWARE HIT"
)
state
:=
middleware
.
State
{
W
:
w
,
Req
:
r
}
m
:=
state
.
AnswerMessage
()
m
.
Authoritative
=
true
m
.
RecursionAvailable
=
true
m
.
Compress
=
true
return
0
,
nil
}
// only needs state and current zone name we are auth for.
/*
func (s *server) ServeDNS(w dns.ResponseWriter, req *dns.Msg) {
q := req.Question[0]
name := strings.ToLower(q.Name)
switch q.Qtype {
case dns.TypeNS:
records, extra, err := s.NSRecords(q, s.config.dnsDomain)
if isEtcdNameError(err, s) {
m = s.NameError(req)
return
}
m.Answer = append(m.Answer, records...)
m.Extra = append(m.Extra, extra...)
case dns.TypeA, dns.TypeAAAA:
records, err := s.AddressRecords(q, name, nil, bufsize, dnssec, false)
if isEtcdNameError(err, s) {
m = s.NameError(req)
return
}
m.Answer = append(m.Answer, records...)
case dns.TypeTXT:
records, err := s.TXTRecords(q, name)
if isEtcdNameError(err, s) {
m = s.NameError(req)
return
}
m.Answer = append(m.Answer, records...)
case dns.TypeCNAME:
records, err := s.CNAMERecords(q, name)
if isEtcdNameError(err, s) {
m = s.NameError(req)
return
}
m.Answer = append(m.Answer, records...)
case dns.TypeMX:
records, extra, err := s.MXRecords(q, name, bufsize, dnssec)
if isEtcdNameError(err, s) {
m = s.NameError(req)
return
}
m.Answer = append(m.Answer, records...)
m.Extra = append(m.Extra, extra...)
default:
fallthrough // also catch other types, so that they return NODATA
case dns.TypeSRV:
records, extra, err := s.SRVRecords(q, name, bufsize, dnssec)
if err != nil {
if isEtcdNameError(err, s) {
m = s.NameError(req)
return
}
logf("got error from backend: %s", err)
if q.Qtype == dns.TypeSRV { // Otherwise NODATA
m = s.ServerFailure(req)
return
}
}
// if we are here again, check the types, because an answer may only
// be given for SRV. All other types should return NODATA, the
// NXDOMAIN part is handled in the above code. TODO(miek): yes this
// can be done in a more elegant manor.
if q.Qtype == dns.TypeSRV {
m.Answer = append(m.Answer, records...)
m.Extra = append(m.Extra, extra...)
}
}
if len(m.Answer) == 0 { // NODATA response
m.Ns = []dns.RR{s.NewSOA()}
m.Ns[0].Header().Ttl = s.config.MinTtl
}
}
// etcNameError checks if the error is ErrorCodeKeyNotFound from etcd.
func isEtcdNameError(err error, s *server) bool {
if e, ok := err.(etcd.Error); ok && e.Code == etcd.ErrorCodeKeyNotFound {
return true
}
return false
}
*/
middleware/etcd/lookup.go
0 → 100644
View file @
b6341e8b
package
etcd
/*
func (s *server) AddressRecords(q dns.Question, name string, previousRecords []dns.RR, state middleware.State) (records []dns.RR, err error) {
services, err := s.backend.Records(name, false)
if err != nil {
return nil, err
}
services = msg.Group(services)
for _, serv := range services {
ip := net.ParseIP(serv.Host)
switch {
case ip == nil:
// Try to resolve as CNAME if it's not an IP, but only if we don't create loops.
if q.Name == dns.Fqdn(serv.Host) {
// x CNAME x is a direct loop, don't add those
continue
}
newRecord := serv.NewCNAME(q.Name, dns.Fqdn(serv.Host))
if len(previousRecords) > 7 {
logf("CNAME lookup limit of 8 exceeded for %s", newRecord)
// don't add it, and just continue
continue
}
if s.isDuplicateCNAME(newRecord, previousRecords) {
logf("CNAME loop detected for record %s", newRecord)
continue
}
nextRecords, err := s.AddressRecords(dns.Question{Name: dns.Fqdn(serv.Host), Qtype: q.Qtype, Qclass: q.Qclass},
strings.ToLower(dns.Fqdn(serv.Host)), append(previousRecords, newRecord), state)
if err == nil {
// Only have we found something we should add the CNAME and the IP addresses.
if len(nextRecords) > 0 {
records = append(records, newRecord)
records = append(records, nextRecords...)
}
continue
}
// This means we can not complete the CNAME, try to look else where.
target := newRecord.Target
if dns.IsSubDomain(s.config.Domain, target) {
// We should already have found it
continue
}
m1, e1 := s.Lookup(target, q.Qtype, bufsize, dnssec)
if e1 != nil {
logf("incomplete CNAME chain: %s", e1)
continue
}
// Len(m1.Answer) > 0 here is well?
records = append(records, newRecord)
records = append(records, m1.Answer...)
continue
case ip.To4() != nil && (q.Qtype == dns.TypeA || both):
records = append(records, serv.NewA(q.Name, ip.To4()))
case ip.To4() == nil && (q.Qtype == dns.TypeAAAA || both):
records = append(records, serv.NewAAAA(q.Name, ip.To16()))
}
}
return records, nil
}
// NSRecords returns NS records from etcd.
func (s *server) NSRecords(q dns.Question, state middleware.State) (records []dns.RR, extra []dns.RR, err error) {
services, err := s.backend.Records(name, false)
if err != nil {
return nil, nil, err
}
services = msg.Group(services)
for _, serv := range services {
ip := net.ParseIP(serv.Host)
switch {
case ip == nil:
return nil, nil, fmt.Errorf("NS record must be an IP address")
case ip.To4() != nil:
serv.Host = msg.Domain(serv.Key)
records = append(records, serv.NewNS(q.Name, serv.Host))
extra = append(extra, serv.NewA(serv.Host, ip.To4()))
case ip.To4() == nil:
serv.Host = msg.Domain(serv.Key)
records = append(records, serv.NewNS(q.Name, serv.Host))
extra = append(extra, serv.NewAAAA(serv.Host, ip.To16()))
}
}
return records, extra, nil
}
// SRVRecords returns SRV records from etcd.
// If the Target is not a name but an IP address, a name is created.
func (s *server) SRVRecords(s middleware.State) (records []dns.RR, extra []dns.RR, err error) {
services, err := s.backend.Records(name, false)
if err != nil {
return nil, nil, err
}
services = msg.Group(services)
// Looping twice to get the right weight vs priority
w := make(map[int]int)
for _, serv := range services {
weight := 100
if serv.Weight != 0 {
weight = serv.Weight
}
if _, ok := w[serv.Priority]; !ok {
w[serv.Priority] = weight
continue
}
w[serv.Priority] += weight
}
lookup := make(map[string]bool)
for _, serv := range services {
w1 := 100.0 / float64(w[serv.Priority])
if serv.Weight == 0 {
w1 *= 100
} else {
w1 *= float64(serv.Weight)
}
weight := uint16(math.Floor(w1))
ip := net.ParseIP(serv.Host)
switch {
case ip == nil:
srv := serv.NewSRV(q.Name, weight)
records = append(records, srv)
if _, ok := lookup[srv.Target]; ok {
break
}
lookup[srv.Target] = true
if !dns.IsSubDomain(s.config.Domain, srv.Target) {
m1, e1 := s.Lookup(srv.Target, dns.TypeA, bufsize, dnssec)
if e1 == nil {
extra = append(extra, m1.Answer...)
}
m1, e1 = s.Lookup(srv.Target, dns.TypeAAAA, bufsize, dnssec)
if e1 == nil {
// If we have seen CNAME's we *assume* that they are already added.
for _, a := range m1.Answer {
if _, ok := a.(*dns.CNAME); !ok {
extra = append(extra, a)
}
}
}
break
}
// Internal name, we should have some info on them, either v4 or v6
// Clients expect a complete answer, because we are a recursor in their
// view.
addr, e1 := s.AddressRecords(dns.Question{srv.Target, dns.ClassINET, dns.TypeA},
srv.Target, nil, bufsize, dnssec, true)
if e1 == nil {
extra = append(extra, addr...)
}
case ip.To4() != nil:
serv.Host = msg.Domain(serv.Key)
srv := serv.NewSRV(q.Name, weight)
records = append(records, srv)
extra = append(extra, serv.NewA(srv.Target, ip.To4()))
case ip.To4() == nil:
serv.Host = msg.Domain(serv.Key)
srv := serv.NewSRV(q.Name, weight)
records = append(records, srv)
extra = append(extra, serv.NewAAAA(srv.Target, ip.To16()))
}
}
return records, extra, nil
}
// MXRecords returns MX records from etcd.
// If the Target is not a name but an IP address, a name is created.
func (s *server) MXRecords(q dns.Question, name string, s middleware.State) (records []dns.RR, extra []dns.RR, err error) {
services, err := s.backend.Records(name, false)
if err != nil {
return nil, nil, err
}
lookup := make(map[string]bool)
for _, serv := range services {
if !serv.Mail {
continue
}
ip := net.ParseIP(serv.Host)
switch {
case ip == nil:
mx := serv.NewMX(q.Name)
records = append(records, mx)
if _, ok := lookup[mx.Mx]; ok {
break
}
lookup[mx.Mx] = true
if !dns.IsSubDomain(s.config.Domain, mx.Mx) {
m1, e1 := s.Lookup(mx.Mx, dns.TypeA, bufsize, dnssec)
if e1 == nil {
extra = append(extra, m1.Answer...)
}
m1, e1 = s.Lookup(mx.Mx, dns.TypeAAAA, bufsize, dnssec)
if e1 == nil {
// If we have seen CNAME's we *assume* that they are already added.
for _, a := range m1.Answer {
if _, ok := a.(*dns.CNAME); !ok {
extra = append(extra, a)
}
}
}
break
}
// Internal name
addr, e1 := s.AddressRecords(dns.Question{mx.Mx, dns.ClassINET, dns.TypeA},
mx.Mx, nil, bufsize, dnssec, true)
if e1 == nil {
extra = append(extra, addr...)
}
case ip.To4() != nil:
serv.Host = msg.Domain(serv.Key)
records = append(records, serv.NewMX(q.Name))
extra = append(extra, serv.NewA(serv.Host, ip.To4()))
case ip.To4() == nil:
serv.Host = msg.Domain(serv.Key)
records = append(records, serv.NewMX(q.Name))
extra = append(extra, serv.NewAAAA(serv.Host, ip.To16()))
}
}
return records, extra, nil
}
func (s *server) CNAMERecords(q dns.Question, state middleware.State) (records []dns.RR, err error) {
services, err := s.backend.Records(name, true)
if err != nil {
return nil, err
}
services = msg.Group(services)
if len(services) > 0 {
serv := services[0]
if ip := net.ParseIP(serv.Host); ip == nil {
records = append(records, serv.NewCNAME(q.Name, dns.Fqdn(serv.Host)))
}
}
return records, nil
}
func (s *server) TXTRecords(q dns.Question, state middleware.State) (records []dns.RR, err error) {
services, err := s.backend.Records(name, false)
if err != nil {
return nil, err
}
services = msg.Group(services)
for _, serv := range services {
if serv.Text == "" {
continue
}
records = append(records, serv.NewTXT(q.Name))
}
return records, nil
}
func isDuplicateCNAME(r *dns.CNAME, records []dns.RR) bool {
for _, rec := range records {
if v, ok := rec.(*dns.CNAME); ok {
if v.Target == r.Target {
return true
}
}
}
return false
}
// Move to state.go somehow?
func (s *server) NameError(req *dns.Msg) *dns.Msg {
m := new(dns.Msg)
m.SetRcode(req, dns.RcodeNameError)
m.Ns = []dns.RR{s.NewSOA()}
m.Ns[0].Header().Ttl = s.config.MinTtl
return m
}
// overflowOrTruncated writes back an error to the client if the message does not fit.
// It updates prometheus metrics. If something has been written to the client, true
// will be returned.
func (s *server) overflowOrTruncated(w dns.ResponseWriter, m *dns.Msg, bufsize int, sy metrics.System) bool {
switch isTCP(w) {
case true:
if _, overflow := Fit(m, dns.MaxMsgSize, true); overflow {
metrics.ReportErrorCount(m, sy)
msgFail := s.ServerFailure(m)
w.WriteMsg(msgFail)
return true
}
case false:
// Overflow with udp always results in TC.
Fit(m, bufsize, false)
metrics.ReportErrorCount(m, sy)
if m.Truncated {
w.WriteMsg(m)
return true
}
}
return false
}
// etcNameError return a NameError to the client if the error
// returned from etcd has ErrorCode == 100.
func isEtcdNameError(err error, s *server) bool {
if e, ok := err.(etcd.Error); ok && e.Code == etcd.ErrorCodeKeyNotFound {
return true
}
if err != nil {
logf("error from backend: %s", err)
}
return false
}
*/
middleware/exchange.go
View file @
b6341e8b
...
@@ -8,3 +8,7 @@ func Exchange(c *dns.Client, m *dns.Msg, server string) (*dns.Msg, error) {
...
@@ -8,3 +8,7 @@ func Exchange(c *dns.Client, m *dns.Msg, server string) (*dns.Msg, error) {
r
,
_
,
err
:=
c
.
Exchange
(
m
,
server
)
r
,
_
,
err
:=
c
.
Exchange
(
m
,
server
)
return
r
,
err
return
r
,
err
}
}
// Lookup functions, ala
// LookupHost
// LookupCNAME
middleware/state.go
View file @
b6341e8b
...
@@ -83,6 +83,30 @@ func (s State) Family() int {
...
@@ -83,6 +83,30 @@ func (s State) Family() int {
return
2
return
2
}
}
// Do returns if the request has the DO (DNSSEC OK) bit set.
func
(
s
State
)
Do
()
bool
{
if
o
:=
s
.
Req
.
IsEdns0
();
o
!=
nil
{
return
o
.
Do
()
}
return
false
}
// UDPSize returns if UDP buffer size advertised in the requests OPT record.
// Or when the request was over TCP, we return the maximum allowed size of 64K.
func
(
s
State
)
Size
()
int
{
if
s
.
Proto
()
==
"tcp"
{
return
dns
.
MaxMsgSize
}
if
o
:=
s
.
Req
.
IsEdns0
();
o
!=
nil
{
s
:=
o
.
UDPSize
()
if
s
<
dns
.
MinMsgSize
{
s
=
dns
.
MinMsgSize
}
return
int
(
s
)
}
return
dns
.
MinMsgSize
}
// Type returns the type of the question as a string.
// Type returns the type of the question as a string.
func
(
s
State
)
Type
()
string
{
func
(
s
State
)
Type
()
string
{
return
dns
.
Type
(
s
.
Req
.
Question
[
0
]
.
Qtype
)
.
String
()
return
dns
.
Type
(
s
.
Req
.
Question
[
0
]
.
Qtype
)
.
String
()
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment