Fix godeps

This commit is contained in:
Andy Goldstein 2017-01-09 11:57:18 -05:00
parent de59ede6b2
commit d16b39abc5
23 changed files with 0 additions and 3071 deletions

30
Godeps/Godeps.json generated
View File

@ -676,11 +676,6 @@
"ImportPath": "github.com/coreos/go-semver/semver",
"Rev": "568e959cd89871e61434c1143528d9162da89ef2"
},
{
"ImportPath": "github.com/coreos/go-systemd/activation",
"Comment": "v8-2-g4484981",
"Rev": "4484981625c1a6a2ecb40a390fcb6a9bcfee76e3"
},
{
"ImportPath": "github.com/coreos/go-systemd/daemon",
"Comment": "v8-2-g4484981",
@ -2141,31 +2136,6 @@
"ImportPath": "github.com/shurcooL/sanitized_anchor_name",
"Rev": "10ef21a441db47d8b13ebcc5fd2310f636973c77"
},
{
"ImportPath": "github.com/skynetservices/skydns/cache",
"Comment": "2.5.3a-41-g00ade30",
"Rev": "00ade3024f047d26130abf161900e0adb72a06f1"
},
{
"ImportPath": "github.com/skynetservices/skydns/metrics",
"Comment": "2.5.3a-41-g00ade30",
"Rev": "00ade3024f047d26130abf161900e0adb72a06f1"
},
{
"ImportPath": "github.com/skynetservices/skydns/msg",
"Comment": "2.5.3a-41-g00ade30",
"Rev": "00ade3024f047d26130abf161900e0adb72a06f1"
},
{
"ImportPath": "github.com/skynetservices/skydns/server",
"Comment": "2.5.3a-41-g00ade30",
"Rev": "00ade3024f047d26130abf161900e0adb72a06f1"
},
{
"ImportPath": "github.com/skynetservices/skydns/singleflight",
"Comment": "2.5.3a-41-g00ade30",
"Rev": "00ade3024f047d26130abf161900e0adb72a06f1"
},
{
"ImportPath": "github.com/spf13/afero",
"Rev": "b28a7effac979219c2a2ed6205a4d70e4b1bcd02"

344
Godeps/LICENSES generated
View File

@ -23236,205 +23236,6 @@ Apache License
================================================================================
================================================================================
= vendor/github.com/coreos/go-systemd/activation licensed under: =
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, "control" means (i) the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising
permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
"Object" form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object code,
generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made
available under the License, as indicated by a copyright notice that is included
in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative Works
thereof, that is intentionally submitted to Licensor for inclusion in the Work
by the copyright owner or by an individual or Legal Entity authorized to submit
on behalf of the copyright owner. For the purposes of this definition,
"submitted" means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor for
the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
2. Grant of Copyright License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the Work and such
Derivative Works in Source or Object form.
3. Grant of Patent License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
such license applies only to those patent claims licensable by such Contributor
that are necessarily infringed by their Contribution(s) alone or by combination
of their Contribution(s) with the Work to which such Contribution(s) was
submitted. If You institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
Contribution incorporated within the Work constitutes direct or contributory
patent infringement, then any patent licenses granted to You under this License
for that Work shall terminate as of the date such litigation is filed.
4. Redistribution.
You may reproduce and distribute copies of the Work or Derivative Works thereof
in any medium, with or without modifications, and in Source or Object form,
provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of
this License; and
You must cause any modified files to carry prominent notices stating that You
changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source form
of the Work, excluding those notices that do not pertain to any part of the
Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then any
Derivative Works that You distribute must include a readable copy of the
attribution notices contained within such NOTICE file, excluding those notices
that do not pertain to any part of the Derivative Works, in at least one of the
following places: within a NOTICE text file distributed as part of the
Derivative Works; within the Source form or documentation, if provided along
with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents of
the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works that
You distribute, alongside or as an addendum to the NOTICE text from the Work,
provided that such additional attribution notices cannot be construed as
modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a whole,
provided Your use, reproduction, and distribution of the Work otherwise complies
with the conditions stated in this License.
5. Submission of Contributions.
Unless You explicitly state otherwise, any Contribution intentionally submitted
for inclusion in the Work by You to the Licensor shall be under the terms and
conditions of this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify the terms of
any separate license agreement you may have executed with Licensor regarding
such Contributions.
6. Trademarks.
This License does not grant permission to use the trade names, trademarks,
service marks, or product names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
7. Disclaimer of Warranty.
Unless required by applicable law or agreed to in writing, Licensor provides the
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
including, without limitation, any warranties or conditions of TITLE,
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
solely responsible for determining the appropriateness of using or
redistributing the Work and assume any risks associated with Your exercise of
permissions under this License.
8. Limitation of Liability.
In no event and under no legal theory, whether in tort (including negligence),
contract, or otherwise, unless required by applicable law (such as deliberate
and grossly negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special, incidental,
or consequential damages of any character arising as a result of this License or
out of the use or inability to use the Work (including but not limited to
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
any and all other commercial damages or losses), even if such Contributor has
been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability.
While redistributing the Work or Derivative Works thereof, You may choose to
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
other liability obligations and/or rights consistent with this License. However,
in accepting such obligations, You may act only on Your own behalf and on Your
sole responsibility, not on behalf of any other Contributor, and only if You
agree to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets "[]" replaced with your own
identifying information. (Don't include the brackets!) The text should be
enclosed in the appropriate comment syntax for the file format. We also
recommend that a file or class name and description of purpose be included on
the same "printed page" as the copyright notice for easier identification within
third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
= vendor/github.com/coreos/go-systemd/LICENSE 19cbd64715b51267a47bf3750cc6a8a5 -
================================================================================
================================================================================
= vendor/github.com/coreos/go-systemd/daemon licensed under: =
@ -67078,151 +66879,6 @@ THE SOFTWARE.
================================================================================
================================================================================
= vendor/github.com/skynetservices/skydns/cache licensed under: =
The MIT License (MIT)
Copyright (c) 2013 The SkyDNS Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
= vendor/github.com/skynetservices/skydns/LICENSE 132bec980d83cb58e26698e7f4832569 -
================================================================================
================================================================================
= vendor/github.com/skynetservices/skydns/metrics licensed under: =
The MIT License (MIT)
Copyright (c) 2013 The SkyDNS Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
= vendor/github.com/skynetservices/skydns/LICENSE 132bec980d83cb58e26698e7f4832569 -
================================================================================
================================================================================
= vendor/github.com/skynetservices/skydns/msg licensed under: =
The MIT License (MIT)
Copyright (c) 2013 The SkyDNS Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
= vendor/github.com/skynetservices/skydns/LICENSE 132bec980d83cb58e26698e7f4832569 -
================================================================================
================================================================================
= vendor/github.com/skynetservices/skydns/server licensed under: =
The MIT License (MIT)
Copyright (c) 2013 The SkyDNS Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
= vendor/github.com/skynetservices/skydns/LICENSE 132bec980d83cb58e26698e7f4832569 -
================================================================================
================================================================================
= vendor/github.com/skynetservices/skydns/singleflight licensed under: =
The MIT License (MIT)
Copyright (c) 2013 The SkyDNS Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
= vendor/github.com/skynetservices/skydns/LICENSE 132bec980d83cb58e26698e7f4832569 -
================================================================================
================================================================================
= vendor/github.com/spf13/afero licensed under: =

70
vendor/BUILD vendored
View File

@ -1946,16 +1946,6 @@ go_library(
tags = ["automanaged"],
)
go_library(
name = "github.com/coreos/go-systemd/activation",
srcs = [
"github.com/coreos/go-systemd/activation/files.go",
"github.com/coreos/go-systemd/activation/listeners.go",
"github.com/coreos/go-systemd/activation/packetconns.go",
],
tags = ["automanaged"],
)
go_library(
name = "github.com/coreos/go-systemd/daemon",
srcs = ["github.com/coreos/go-systemd/daemon/sdnotify.go"],
@ -6036,66 +6026,6 @@ go_library(
tags = ["automanaged"],
)
go_library(
name = "github.com/skynetservices/skydns/cache",
srcs = [
"github.com/skynetservices/skydns/cache/cache.go",
"github.com/skynetservices/skydns/cache/hit.go",
],
tags = ["automanaged"],
deps = ["//vendor:github.com/miekg/dns"],
)
go_library(
name = "github.com/skynetservices/skydns/metrics",
srcs = ["github.com/skynetservices/skydns/metrics/metrics.go"],
tags = ["automanaged"],
deps = [
"//vendor:github.com/miekg/dns",
"//vendor:github.com/prometheus/client_golang/prometheus",
],
)
go_library(
name = "github.com/skynetservices/skydns/msg",
srcs = ["github.com/skynetservices/skydns/msg/service.go"],
tags = ["automanaged"],
deps = ["//vendor:github.com/miekg/dns"],
)
go_library(
name = "github.com/skynetservices/skydns/server",
srcs = [
"github.com/skynetservices/skydns/server/backend.go",
"github.com/skynetservices/skydns/server/config.go",
"github.com/skynetservices/skydns/server/dnssec.go",
"github.com/skynetservices/skydns/server/doc.go",
"github.com/skynetservices/skydns/server/exchange.go",
"github.com/skynetservices/skydns/server/forwarding.go",
"github.com/skynetservices/skydns/server/log.go",
"github.com/skynetservices/skydns/server/msg.go",
"github.com/skynetservices/skydns/server/nsec3.go",
"github.com/skynetservices/skydns/server/server.go",
"github.com/skynetservices/skydns/server/stub.go",
],
tags = ["automanaged"],
deps = [
"//vendor:github.com/coreos/etcd/client",
"//vendor:github.com/coreos/go-systemd/activation",
"//vendor:github.com/miekg/dns",
"//vendor:github.com/skynetservices/skydns/cache",
"//vendor:github.com/skynetservices/skydns/metrics",
"//vendor:github.com/skynetservices/skydns/msg",
"//vendor:github.com/skynetservices/skydns/singleflight",
],
)
go_library(
name = "github.com/skynetservices/skydns/singleflight",
srcs = ["github.com/skynetservices/skydns/singleflight/singleflight.go"],
tags = ["automanaged"],
)
go_library(
name = "github.com/spf13/afero",
srcs = [

View File

@ -1,52 +0,0 @@
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package activation implements primitives for systemd socket activation.
package activation
import (
"os"
"strconv"
"syscall"
)
// based on: https://gist.github.com/alberts/4640792
const (
listenFdsStart = 3
)
func Files(unsetEnv bool) []*os.File {
if unsetEnv {
defer os.Unsetenv("LISTEN_PID")
defer os.Unsetenv("LISTEN_FDS")
}
pid, err := strconv.Atoi(os.Getenv("LISTEN_PID"))
if err != nil || pid != os.Getpid() {
return nil
}
nfds, err := strconv.Atoi(os.Getenv("LISTEN_FDS"))
if err != nil || nfds == 0 {
return nil
}
files := make([]*os.File, 0, nfds)
for fd := listenFdsStart; fd < listenFdsStart+nfds; fd++ {
syscall.CloseOnExec(fd)
files = append(files, os.NewFile(uintptr(fd), "LISTEN_FD_"+strconv.Itoa(fd)))
}
return files
}

View File

@ -1,62 +0,0 @@
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package activation
import (
"crypto/tls"
"net"
)
// Listeners returns a slice containing a net.Listener for each matching socket type
// passed to this process.
//
// The order of the file descriptors is preserved in the returned slice.
// Nil values are used to fill any gaps. For example if systemd were to return file descriptors
// corresponding with "udp, tcp, tcp", then the slice would contain {nil, net.Listener, net.Listener}
func Listeners(unsetEnv bool) ([]net.Listener, error) {
files := Files(unsetEnv)
listeners := make([]net.Listener, len(files))
for i, f := range files {
if pc, err := net.FileListener(f); err == nil {
listeners[i] = pc
}
}
return listeners, nil
}
// TLSListeners returns a slice containing a net.listener for each matching TCP socket type
// passed to this process.
// It uses default Listeners func and forces TCP sockets handlers to use TLS based on tlsConfig.
func TLSListeners(unsetEnv bool, tlsConfig *tls.Config) ([]net.Listener, error) {
listeners, err := Listeners(unsetEnv)
if listeners == nil || err != nil {
return nil, err
}
if tlsConfig != nil && err == nil {
tlsConfig.NextProtos = []string{"http/1.1"}
for i, l := range listeners {
// Activate TLS only for TCP sockets
if l.Addr().Network() == "tcp" {
listeners[i] = tls.NewListener(l, tlsConfig)
}
}
}
return listeners, err
}

View File

@ -1,37 +0,0 @@
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package activation
import (
"net"
)
// PacketConns returns a slice containing a net.PacketConn for each matching socket type
// passed to this process.
//
// The order of the file descriptors is preserved in the returned slice.
// Nil values are used to fill any gaps. For example if systemd were to return file descriptors
// corresponding with "udp, tcp, udp", then the slice would contain {net.PacketConn, nil, net.PacketConn}
func PacketConns(unsetEnv bool) ([]net.PacketConn, error) {
files := Files(unsetEnv)
conns := make([]net.PacketConn, len(files))
for i, f := range files {
if pc, err := net.FilePacketConn(f); err == nil {
conns[i] = pc
}
}
return conns, nil
}

View File

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2013 The SkyDNS Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,167 +0,0 @@
// Copyright (c) 2014 The SkyDNS Authors. All rights reserved.
// Use of this source code is governed by The MIT License (MIT) that can be
// found in the LICENSE file.
package cache
// Cache that holds RRs and for DNSSEC an RRSIG.
// TODO(miek): there is a lot of copying going on to copy myself out of data
// races. This should be optimized.
import (
"crypto/sha1"
"sync"
"time"
"github.com/miekg/dns"
)
// Elem hold an answer and additional section that returned from the cache.
// The signature is put in answer, extra is empty there. This wastes some memory.
type elem struct {
expiration time.Time // time added + TTL, after this the elem is invalid
msg *dns.Msg
}
// Cache is a cache that holds on the a number of RRs or DNS messages. The cache
// eviction is randomized.
type Cache struct {
sync.RWMutex
capacity int
m map[string]*elem
ttl time.Duration
}
// New returns a new cache with the capacity and the ttl specified.
func New(capacity, ttl int) *Cache {
c := new(Cache)
c.m = make(map[string]*elem)
c.capacity = capacity
c.ttl = time.Duration(ttl) * time.Second
return c
}
func (c *Cache) Capacity() int { return c.capacity }
func (c *Cache) Remove(s string) {
c.Lock()
delete(c.m, s)
c.Unlock()
}
// EvictRandom removes a random member a the cache.
// Must be called under a write lock.
func (c *Cache) EvictRandom() {
clen := len(c.m)
if clen < c.capacity {
return
}
i := c.capacity - clen
for k, _ := range c.m {
delete(c.m, k)
i--
if i == 0 {
break
}
}
}
// InsertMessage inserts a message in the Cache. We will cache it for ttl seconds, which
// should be a small (60...300) integer.
func (c *Cache) InsertMessage(s string, msg *dns.Msg) {
if c.capacity <= 0 {
return
}
c.Lock()
if _, ok := c.m[s]; !ok {
c.m[s] = &elem{time.Now().UTC().Add(c.ttl), msg.Copy()}
}
c.EvictRandom()
c.Unlock()
}
// InsertSignature inserts a signature, the expiration time is used as the cache ttl.
func (c *Cache) InsertSignature(s string, sig *dns.RRSIG) {
if c.capacity <= 0 {
return
}
c.Lock()
if _, ok := c.m[s]; !ok {
m := ((int64(sig.Expiration) - time.Now().Unix()) / (1 << 31)) - 1
if m < 0 {
m = 0
}
t := time.Unix(int64(sig.Expiration)-(m*(1<<31)), 0).UTC()
c.m[s] = &elem{t, &dns.Msg{Answer: []dns.RR{dns.Copy(sig)}}}
}
c.EvictRandom()
c.Unlock()
}
// Search returns a dns.Msg, the expiration time and a boolean indicating if we found something
// in the cache.
func (c *Cache) Search(s string) (*dns.Msg, time.Time, bool) {
if c.capacity <= 0 {
return nil, time.Time{}, false
}
c.RLock()
if e, ok := c.m[s]; ok {
e1 := e.msg.Copy()
c.RUnlock()
return e1, e.expiration, true
}
c.RUnlock()
return nil, time.Time{}, false
}
// Key creates a hash key from a question section. It creates a different key
// for requests with DNSSEC.
func Key(q dns.Question, dnssec, tcp bool) string {
h := sha1.New()
i := append([]byte(q.Name), packUint16(q.Qtype)...)
if dnssec {
i = append(i, byte(255))
}
if tcp {
i = append(i, byte(254))
}
return string(h.Sum(i))
}
// Key uses the name, type and rdata, which is serialized and then hashed as the key for the lookup.
func KeyRRset(rrs []dns.RR) string {
h := sha1.New()
i := []byte(rrs[0].Header().Name)
i = append(i, packUint16(rrs[0].Header().Rrtype)...)
for _, r := range rrs {
switch t := r.(type) { // we only do a few type, serialize these manually
case *dns.SOA:
// We only fiddle with the serial so store that.
i = append(i, packUint32(t.Serial)...)
case *dns.SRV:
i = append(i, packUint16(t.Priority)...)
i = append(i, packUint16(t.Weight)...)
i = append(i, packUint16(t.Weight)...)
i = append(i, []byte(t.Target)...)
case *dns.A:
i = append(i, []byte(t.A)...)
case *dns.AAAA:
i = append(i, []byte(t.AAAA)...)
case *dns.NSEC3:
i = append(i, []byte(t.NextDomain)...)
// Bitmap does not differentiate in SkyDNS.
case *dns.DNSKEY:
case *dns.NS:
case *dns.TXT:
}
}
return string(h.Sum(i))
}
func packUint16(i uint16) []byte { return []byte{byte(i >> 8), byte(i)} }
func packUint32(i uint32) []byte { return []byte{byte(i >> 24), byte(i >> 16), byte(i >> 8), byte(i)} }

View File

@ -1,31 +0,0 @@
// Copyright (c) 2014 The SkyDNS Authors. All rights reserved.
// Use of this source code is governed by The MIT License (MIT) that can be
// found in the LICENSE file.
package cache
import (
"time"
"github.com/miekg/dns"
)
// Hit returns a dns message from the cache. If the message's TTL is expired nil
// is returned and the message is removed from the cache.
func (c *Cache) Hit(question dns.Question, dnssec, tcp bool, msgid uint16) *dns.Msg {
key := Key(question, dnssec, tcp)
m1, exp, hit := c.Search(key)
if hit {
// Cache hit! \o/
if time.Since(exp) < 0 {
m1.Id = msgid
m1.Compress = true
// Even if something ended up with the TC bit *in* the cache, set it to off
m1.Truncated = false
return m1
}
// Expired! /o\
c.Remove(key)
}
return nil
}

View File

@ -1,186 +0,0 @@
// Copyright (c) 2014 The SkyDNS Authors. All rights reserved.
// Use of this source code is governed by The MIT License (MIT) that can be
// found in the LICENSE file.
package metrics
import (
"fmt"
"net/http"
"os"
"strconv"
"time"
"github.com/miekg/dns"
"github.com/prometheus/client_golang/prometheus"
)
var (
Port = os.Getenv("PROMETHEUS_PORT")
Path = envOrDefault("PROMETHEUS_PATH", "/metrics")
Namespace = envOrDefault("PROMETHEUS_NAMESPACE", "skydns")
Subsystem = envOrDefault("PROMETHEUS_SUBSYSTEM", "skydns")
requestCount *prometheus.CounterVec
requestDuration *prometheus.HistogramVec
responseSize *prometheus.HistogramVec
errorCount *prometheus.CounterVec
cacheMiss *prometheus.CounterVec
)
type (
System string
Cause string
CacheType string
)
var (
Auth System = "auth"
Cache System = "cache"
Rec System = "recursive"
Reverse System = "reverse"
Stub System = "stub"
Nxdomain Cause = "nxdomain"
Nodata Cause = "nodata"
Truncated Cause = "truncated"
Refused Cause = "refused"
Overflow Cause = "overflow"
Fail Cause = "servfail"
Response CacheType = "response"
Signature CacheType = "signature"
)
func defineMetrics() {
requestCount = prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: Namespace,
Subsystem: Subsystem,
Name: "dns_request_count_total",
Help: "Counter of DNS requests made.",
}, []string{"system"})
requestDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
Namespace: Namespace,
Subsystem: Subsystem,
Name: "dns_request_duration_seconds",
Help: "Histogram of the time (in seconds) each request took to resolve.",
Buckets: append([]float64{0.001, 0.003}, prometheus.DefBuckets...),
}, []string{"system"})
responseSize = prometheus.NewHistogramVec(prometheus.HistogramOpts{
Namespace: Namespace,
Subsystem: Subsystem,
Name: "dns_response_size_bytes",
Help: "Size of the returns response in bytes.",
Buckets: []float64{0, 512, 1024, 1500, 2048, 4096,
8192, 12288, 16384, 20480, 24576, 28672, 32768, 36864,
40960, 45056, 49152, 53248, 57344, 61440, 65536,
},
}, []string{"system"})
errorCount = prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: Namespace,
Subsystem: Subsystem,
Name: "dns_error_count_total",
Help: "Counter of DNS requests resulting in an error.",
}, []string{"system", "cause"})
cacheMiss = prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: Namespace,
Subsystem: Subsystem,
Name: "dns_cachemiss_count_total",
Help: "Counter of DNS requests that result in a cache miss.",
}, []string{"cache"})
}
// Metrics registers the DNS metrics to Prometheus, and starts the internal metrics
// server if the environment variable PROMETHEUS_PORT is set.
func Metrics() error {
// We do this in a function instead of using var + init(), because we want to
// able to set Namespace and/or Subsystem.
if Port == "" {
return nil
}
_, err := strconv.Atoi(Port)
if err != nil {
fmt.Errorf("bad port for prometheus: %s", Port)
}
defineMetrics()
prometheus.MustRegister(requestCount)
prometheus.MustRegister(requestDuration)
prometheus.MustRegister(responseSize)
prometheus.MustRegister(errorCount)
prometheus.MustRegister(cacheMiss)
http.Handle(Path, prometheus.Handler())
go func() {
fmt.Errorf("%s", http.ListenAndServe(":"+Port, nil))
}()
return nil
}
func ReportDuration(resp *dns.Msg, start time.Time, sys System) {
if requestDuration == nil || responseSize == nil {
return
}
rlen := float64(0)
if resp != nil {
rlen = float64(resp.Len())
}
requestDuration.WithLabelValues(string(sys)).Observe(float64(time.Since(start)) / float64(time.Second))
responseSize.WithLabelValues(string(sys)).Observe(rlen)
}
func ReportRequestCount(req *dns.Msg, sys System) {
if requestCount == nil {
return
}
requestCount.WithLabelValues(string(sys)).Inc()
}
func ReportErrorCount(resp *dns.Msg, sys System) {
if resp == nil || errorCount == nil {
return
}
if resp.Truncated {
errorCount.WithLabelValues(string(sys), string(Truncated)).Inc()
return
}
if resp.Len() > dns.MaxMsgSize {
errorCount.WithLabelValues(string(sys), string(Overflow)).Inc()
return
}
switch resp.Rcode {
case dns.RcodeServerFailure:
errorCount.WithLabelValues(string(sys), string(Fail)).Inc()
case dns.RcodeRefused:
errorCount.WithLabelValues(string(sys), string(Refused)).Inc()
case dns.RcodeNameError:
errorCount.WithLabelValues(string(sys), string(Nxdomain)).Inc()
// nodata ??
}
}
func ReportCacheMiss(ca CacheType) {
if cacheMiss == nil {
return
}
cacheMiss.WithLabelValues(string(ca)).Inc()
}
func envOrDefault(env, def string) string {
e := os.Getenv(env)
if e != "" {
return e
}
return def
}

View File

@ -1,221 +0,0 @@
// Copyright (c) 2014 The SkyDNS Authors. All rights reserved.
// Use of this source code is governed by The MIT License (MIT) that can be
// found in the LICENSE file.
package msg
import (
"net"
"path"
"strings"
"github.com/miekg/dns"
)
// PathPrefix is the prefix used to store SkyDNS data in the backend.
// It defaults to `skydns`.
// You can change it by set `path-prefix` configuration or SKYDNS_PATH_PREFIX env. variable.
// Then:
// The SkyDNS's configuration object should be stored under the key "/mydns/config";
// The etcd path of domain `service.staging.skydns.local.` will be "/mydns/local/skydns/staging/service".
var PathPrefix string = "skydns"
// This *is* the rdata from a SRV record, but with a twist.
// Host (Target in SRV) must be a domain name, but if it looks like an IP
// address (4/6), we will treat it like an IP address.
type Service struct {
Host string `json:"host,omitempty"`
Port int `json:"port,omitempty"`
Priority int `json:"priority,omitempty"`
Weight int `json:"weight,omitempty"`
Text string `json:"text,omitempty"`
Mail bool `json:"mail,omitempty"` // Be an MX record. Priority becomes Preference.
Ttl uint32 `json:"ttl,omitempty"`
// When a SRV record with a "Host: IP-address" is added, we synthesize
// a srv.Target domain name. Normally we convert the full Key where
// the record lives to a DNS name and use this as the srv.Target. When
// TargetStrip > 0 we strip the left most TargetStrip labels from the
// DNS name.
TargetStrip int `json:"targetstrip,omitempty"`
// Group is used to group (or *not* to group) different services
// together. Services with an identical Group are returned in the same
// answer.
Group string `json:"group,omitempty"`
// Etcd key where we found this service and ignored from json un-/marshalling
Key string `json:"-"`
}
// NewSRV returns a new SRV record based on the Service.
func (s *Service) NewSRV(name string, weight uint16) *dns.SRV {
host := targetStrip(dns.Fqdn(s.Host), s.TargetStrip)
return &dns.SRV{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeSRV, Class: dns.ClassINET, Ttl: s.Ttl},
Priority: uint16(s.Priority), Weight: weight, Port: uint16(s.Port), Target: host}
}
// NewMX returns a new MX record based on the Service.
func (s *Service) NewMX(name string) *dns.MX {
host := targetStrip(dns.Fqdn(s.Host), s.TargetStrip)
return &dns.MX{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: s.Ttl},
Preference: uint16(s.Priority), Mx: host}
}
// NewA returns a new A record based on the Service.
func (s *Service) NewA(name string, ip net.IP) *dns.A {
return &dns.A{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: s.Ttl}, A: ip}
}
// NewAAAA returns a new AAAA record based on the Service.
func (s *Service) NewAAAA(name string, ip net.IP) *dns.AAAA {
return &dns.AAAA{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: s.Ttl}, AAAA: ip}
}
// NewCNAME returns a new CNAME record based on the Service.
func (s *Service) NewCNAME(name string, target string) *dns.CNAME {
return &dns.CNAME{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: s.Ttl}, Target: target}
}
// NewNS returns a new NS record based on the Service.
func (s *Service) NewNS(name string, target string) *dns.NS {
return &dns.NS{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeNS, Class: dns.ClassINET, Ttl: s.Ttl}, Ns: target}
}
// NewTXT returns a new TXT record based on the Service.
func (s *Service) NewTXT(name string) *dns.TXT {
return &dns.TXT{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: s.Ttl}, Txt: split255(s.Text)}
}
// NewPTR returns a new PTR record based on the Service.
func (s *Service) NewPTR(name string, ttl uint32) *dns.PTR {
return &dns.PTR{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: ttl}, Ptr: dns.Fqdn(s.Host)}
}
// As Path, but if a name contains wildcards (* or any), the name will be
// chopped of before the (first) wildcard, and we do a highler evel search and
// later find the matching names. So service.*.skydns.local, will look for all
// services under skydns.local and will later check for names that match
// service.*.skydns.local. If a wildcard is found the returned bool is true.
func PathWithWildcard(s string) (string, bool) {
l := dns.SplitDomainName(s)
for i, j := 0, len(l)-1; i < j; i, j = i+1, j-1 {
l[i], l[j] = l[j], l[i]
}
for i, k := range l {
if k == "*" || k == "any" {
return path.Join(append([]string{"/" + PathPrefix + "/"}, l[:i]...)...), true
}
}
return path.Join(append([]string{"/" + PathPrefix + "/"}, l...)...), false
}
// Path converts a domainname to an etcd path. If s looks like service.staging.skydns.local.,
// the resulting key will be /skydns/local/skydns/staging/service .
func Path(s string) string {
l := dns.SplitDomainName(s)
for i, j := 0, len(l)-1; i < j; i, j = i+1, j-1 {
l[i], l[j] = l[j], l[i]
}
return path.Join(append([]string{"/" + PathPrefix + "/"}, l...)...)
}
// Domain is the opposite of Path.
func Domain(s string) string {
l := strings.Split(s, "/")
// start with 1, to strip /skydns
for i, j := 1, len(l)-1; i < j; i, j = i+1, j-1 {
l[i], l[j] = l[j], l[i]
}
return dns.Fqdn(strings.Join(l[1:len(l)-1], "."))
}
// Group checks the services in sx, it looks for a Group attribute on the shortest
// keys. If there are multiple shortest keys *and* the group attribute disagrees (and
// is not empty), we don't consider it a group.
// If a group is found, only services with *that* group (or no group) will be returned.
func Group(sx []Service) []Service {
if len(sx) == 0 {
return sx
}
// Shortest key with group attribute sets the group for this set.
group := sx[0].Group
slashes := strings.Count(sx[0].Key, "/")
length := make([]int, len(sx))
for i, s := range sx {
x := strings.Count(s.Key, "/")
length[i] = x
if x < slashes {
if s.Group == "" {
break
}
slashes = x
group = s.Group
}
}
if group == "" {
return sx
}
ret := []Service{} // with slice-tricks in sx we can prolly save this allocation (TODO)
for i, s := range sx {
if s.Group == "" {
ret = append(ret, s)
continue
}
// Disagreement on the same level
if length[i] == slashes && s.Group != group {
return sx
}
if s.Group == group {
ret = append(ret, s)
}
}
return ret
}
// Split255 splits a string into 255 byte chunks.
func split255(s string) []string {
if len(s) < 255 {
return []string{s}
}
sx := []string{}
p, i := 0, 255
for {
if i <= len(s) {
sx = append(sx, s[p:i])
} else {
sx = append(sx, s[p:])
break
}
p, i = p+255, i+255
}
return sx
}
// targetStrip strips "targetstrip" labels from the left side of the fully qualified name.
func targetStrip(name string, targetStrip int) string {
if targetStrip == 0 {
return name
}
offset, end := 0, false
for i := 0; i < targetStrip; i++ {
offset, end = dns.NextLabel(name, offset)
}
if end {
// We overshot the name, use the orignal one.
offset = 0
}
name = name[offset:]
return name
}

View File

@ -1,46 +0,0 @@
// Copyright (c) 2014 The SkyDNS Authors. All rights reserved.
// Use of this source code is governed by The MIT License (MIT) that can be
// found in the LICENSE file.
package server
import "github.com/skynetservices/skydns/msg"
type Backend interface {
Records(name string, exact bool) ([]msg.Service, error)
ReverseRecord(name string) (*msg.Service, error)
}
// FirstBackend exposes the Backend interface over multiple Backends, returning
// the first Backend that answers the provided record request. If no Backend answers
// a record request, the last error seen will be returned.
type FirstBackend []Backend
// FirstBackend implements Backend
var _ Backend = FirstBackend{}
func (g FirstBackend) Records(name string, exact bool) (records []msg.Service, err error) {
var lastError error
for _, backend := range g {
if records, err = backend.Records(name, exact); err == nil && len(records) > 0 {
return records, nil
}
if err != nil {
lastError = err
}
}
return nil, lastError
}
func (g FirstBackend) ReverseRecord(name string) (record *msg.Service, err error) {
var lastError error
for _, backend := range g {
if record, err = backend.ReverseRecord(name); err == nil && record != nil {
return record, nil
}
if err != nil {
lastError = err
}
}
return nil, lastError
}

View File

@ -1,157 +0,0 @@
// Copyright (c) 2014 The SkyDNS Authors. All rights reserved.
// Use of this source code is governed by The MIT License (MIT) that can be
// found in the LICENSE file.
package server
import (
"crypto"
"fmt"
"net"
"os"
"strings"
"time"
"github.com/miekg/dns"
)
const (
SCacheCapacity = 10000
RCacheCapacity = 100000
RCacheTtl = 60
)
// Config provides options to the SkyDNS resolver.
type Config struct {
// The ip:port SkyDNS should be listening on for incoming DNS requests.
DnsAddr string `json:"dns_addr,omitempty"`
// bind to port(s) activated by systemd. If set to true, this overrides DnsAddr.
Systemd bool `json:"systemd,omitempty"`
// The domain SkyDNS is authoritative for, defaults to skydns.local.
Domain string `json:"domain,omitempty"`
// Domain pointing to a key where service info is stored when being queried
// for local.dns.skydns.local.
Local string `json:"local,omitempty"`
// The hostmaster responsible for this domain, defaults to hostmaster.<Domain>.
Hostmaster string `json:"hostmaster,omitempty"`
DNSSEC string `json:"dnssec,omitempty"`
// Round robin A/AAAA replies. Default is true.
RoundRobin bool `json:"round_robin,omitempty"`
// Round robin selection of nameservers from among those listed, rather than have all forwarded requests try the first listed server first every time.
NSRotate bool `json:"ns_rotate,omitempty"`
// List of ip:port, separated by commas of recursive nameservers to forward queries to.
Nameservers []string `json:"nameservers,omitempty"`
// Never provide a recursive service.
NoRec bool `json:"no_rec,omitempty"`
ReadTimeout time.Duration `json:"read_timeout,omitempty"`
// Default priority on SRV records when none is given. Defaults to 10.
Priority uint16 `json:"priority"`
// Default TTL, in seconds, when none is given in etcd. Defaults to 3600.
Ttl uint32 `json:"ttl,omitempty"`
// Minimum TTL, in seconds, for NXDOMAIN responses. Defaults to 300.
MinTtl uint32 `json:"min_ttl,omitempty"`
// SCache, capacity of the signature cache in signatures stored.
SCache int `json:"scache,omitempty"`
// RCache, capacity of response cache in resource records stored.
RCache int `json:"rcache,omitempty"`
// RCacheTtl, how long to cache in seconds.
RCacheTtl int `json:"rcache_ttl,omitempty"`
// How many labels a name should have before we allow forwarding. Default to 2.
Ndots int `json:"ndot,omitempty"`
// DNSSEC key material
PubKey *dns.DNSKEY `json:"-"`
KeyTag uint16 `json:"-"`
PrivKey crypto.Signer `json:"-"`
Verbose bool `json:"-"`
Version bool
// some predefined string "constants"
localDomain string // "local.dns." + config.Domain
dnsDomain string // "ns.dns". + config.Domain
// Stub zones support. Pointer to a map that we refresh when we see
// an update. Map contains domainname -> nameserver:port
stub *map[string][]string
}
func SetDefaults(config *Config) error {
if config.ReadTimeout == 0 {
config.ReadTimeout = 2 * time.Second
}
if config.DnsAddr == "" {
config.DnsAddr = "127.0.0.1:53"
}
if config.Domain == "" {
config.Domain = "skydns.local."
}
if config.Hostmaster == "" {
config.Hostmaster = appendDomain("hostmaster", config.Domain)
}
// People probably don't know that SOA's email addresses cannot
// contain @-signs, replace them with dots
config.Hostmaster = dns.Fqdn(strings.Replace(config.Hostmaster, "@", ".", -1))
if config.MinTtl == 0 {
config.MinTtl = 60
}
if config.Ttl == 0 {
config.Ttl = 3600
}
if config.Priority == 0 {
config.Priority = 10
}
if config.RCache < 0 {
config.RCache = 0
}
if config.SCache < 0 {
config.SCache = 0
}
if config.RCacheTtl == 0 {
config.RCacheTtl = RCacheTtl
}
if config.Ndots <= 0 {
config.Ndots = 2
}
if len(config.Nameservers) == 0 {
c, err := dns.ClientConfigFromFile("/etc/resolv.conf")
if !os.IsNotExist(err) {
if err != nil {
return err
}
for _, s := range c.Servers {
config.Nameservers = append(config.Nameservers, net.JoinHostPort(s, c.Port))
}
}
}
config.Domain = dns.Fqdn(strings.ToLower(config.Domain))
if config.DNSSEC != "" {
// For some reason the + are replaces by spaces in etcd. Re-replace them
keyfile := strings.Replace(config.DNSSEC, " ", "+", -1)
k, p, err := ParseKeyFile(keyfile)
if err != nil {
return err
}
if k.Header().Name != dns.Fqdn(config.Domain) {
return fmt.Errorf("ownername of DNSKEY must match SkyDNS domain")
}
k.Header().Ttl = config.Ttl
config.PubKey = k
config.KeyTag = k.KeyTag()
config.PrivKey = p
}
config.localDomain = appendDomain("local.dns", config.Domain)
config.dnsDomain = appendDomain("ns.dns", config.Domain)
stubmap := make(map[string][]string)
config.stub = &stubmap
return nil
}
func appendDomain(s1, s2 string) string {
if len(s2) > 0 && s2[0] == '.' {
return s1 + s2
}
return s1 + "." + s2
}

View File

@ -1,177 +0,0 @@
// Copyright (c) 2013 Erik St. Martin, Brian Ketelsen. All rights reserved.
// Use of this source code is governed by The MIT License (MIT) that can be
// found in the LICENSE file.
package server
import (
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"os"
"time"
"github.com/skynetservices/skydns/cache"
"github.com/skynetservices/skydns/metrics"
"github.com/skynetservices/skydns/singleflight"
"github.com/miekg/dns"
)
var (
inflight = &singleflight.Group{}
)
// ParseKeyFile read a DNSSEC keyfile as generated by dnssec-keygen or other
// utilities. It add ".key" for the public key and ".private" for the private key.
func ParseKeyFile(file string) (*dns.DNSKEY, crypto.Signer, error) {
f, e := os.Open(file + ".key")
if e != nil {
return nil, nil, e
}
k, e := dns.ReadRR(f, file+".key")
if e != nil {
return nil, nil, e
}
f, e = os.Open(file + ".private")
if e != nil {
return nil, nil, e
}
p, e := k.(*dns.DNSKEY).ReadPrivateKey(f, file+".private")
if e != nil {
return nil, nil, e
}
if v, ok := p.(*rsa.PrivateKey); ok {
return k.(*dns.DNSKEY), v, nil
}
if v, ok := p.(*ecdsa.PrivateKey); ok {
return k.(*dns.DNSKEY), v, nil
}
return k.(*dns.DNSKEY), nil, nil
}
// Sign signs a message m, it takes care of negative or nodata responses as
// well by synthesising NSEC3 records. It will also cache the signatures, using
// a hash of the signed data as a key.
// We also fake the origin TTL in the signature, because we don't want to
// throw away signatures when services decide to have longer TTL. So we just
// set the origTTL to 60.
// TODO(miek): revisit origTTL
func (s *server) Sign(m *dns.Msg, bufsize uint16) {
now := time.Now().UTC()
incep := uint32(now.Add(-3 * time.Hour).Unix()) // 2+1 hours, be sure to catch daylight saving time and such
expir := uint32(now.Add(7 * 24 * time.Hour).Unix()) // sign for a week
for _, r := range rrSets(m.Answer) {
if r[0].Header().Rrtype == dns.TypeRRSIG {
continue
}
if !dns.IsSubDomain(s.config.Domain, r[0].Header().Name) {
continue
}
if sig, err := s.signSet(r, now, incep, expir); err == nil {
m.Answer = append(m.Answer, sig)
}
}
for _, r := range rrSets(m.Ns) {
if r[0].Header().Rrtype == dns.TypeRRSIG {
continue
}
if !dns.IsSubDomain(s.config.Domain, r[0].Header().Name) {
continue
}
if sig, err := s.signSet(r, now, incep, expir); err == nil {
m.Ns = append(m.Ns, sig)
}
}
for _, r := range rrSets(m.Extra) {
if r[0].Header().Rrtype == dns.TypeRRSIG || r[0].Header().Rrtype == dns.TypeOPT {
continue
}
if !dns.IsSubDomain(s.config.Domain, r[0].Header().Name) {
continue
}
if sig, err := s.signSet(r, now, incep, expir); err == nil {
m.Extra = append(m.Extra, sig)
}
}
o := new(dns.OPT)
o.Hdr.Name = "."
o.Hdr.Rrtype = dns.TypeOPT
o.SetDo()
o.SetUDPSize(4096) // TODO(miek): echo client
m.Extra = append(m.Extra, o)
return
}
func (s *server) signSet(r []dns.RR, now time.Time, incep, expir uint32) (*dns.RRSIG, error) {
key := cache.KeyRRset(r)
if m, exp, hit := s.scache.Search(key); hit { // There can only be one sig in this cache.
// Is it still valid 24 hours from now?
if now.Add(+24*time.Hour).Sub(exp) < -24*time.Hour {
return m.Answer[0].(*dns.RRSIG), nil
}
s.scache.Remove(key)
}
if s.config.Verbose {
logf("scache miss for %s type %d", r[0].Header().Name, r[0].Header().Rrtype)
}
metrics.ReportCacheMiss("signature")
sig, err := inflight.Do(key, func() (interface{}, error) {
sig1 := s.NewRRSIG(incep, expir)
sig1.Header().Ttl = r[0].Header().Ttl
if r[0].Header().Rrtype == dns.TypeTXT {
sig1.OrigTtl = 0
}
e := sig1.Sign(s.config.PrivKey, r)
if e != nil {
logf("failed to sign: %s", e.Error())
}
return sig1, e
})
if err != nil {
return nil, err
}
s.scache.InsertSignature(key, sig.(*dns.RRSIG))
return dns.Copy(sig.(*dns.RRSIG)).(*dns.RRSIG), nil
}
func (s *server) NewRRSIG(incep, expir uint32) *dns.RRSIG {
sig := new(dns.RRSIG)
sig.Hdr.Rrtype = dns.TypeRRSIG
sig.Hdr.Ttl = s.config.Ttl
sig.OrigTtl = s.config.Ttl
sig.Algorithm = s.config.PubKey.Algorithm
sig.KeyTag = s.config.KeyTag
sig.Inception = incep
sig.Expiration = expir
sig.SignerName = s.config.PubKey.Hdr.Name
return sig
}
type rrset struct {
qname string
qtype uint16
}
func rrSets(rrs []dns.RR) map[rrset][]dns.RR {
m := make(map[rrset][]dns.RR)
for _, r := range rrs {
if s, ok := m[rrset{r.Header().Name, r.Header().Rrtype}]; ok {
s = append(s, r)
m[rrset{r.Header().Name, r.Header().Rrtype}] = s
} else {
s := make([]dns.RR, 1, 3)
s[0] = r
m[rrset{r.Header().Name, r.Header().Rrtype}] = s
}
}
if len(m) > 0 {
return m
}
return nil
}

View File

@ -1,8 +0,0 @@
// Copyright (c) 2014 The SkyDNS Authors. All rights reserved.
// Use of this source code is governed by The MIT License (MIT) that can be
// found in the LICENSE file.
// Package server provides a DNS server implementation that handles DNS
// queries. To answer a query, the server asks the provided Backend for
// DNS records, which are then converted to the proper answers.
package server

View File

@ -1,34 +0,0 @@
// Copyright (c) 2014 The SkyDNS Authors. All rights reserved.
// Use of this source code is governed by The MIT License (MIT) that can be
// found in the LICENSE file.
package server
import "github.com/miekg/dns"
// exchangeMsg returns a new dns message based on name, type, bufsize and dnssec.
func newExchangeMsg(name string, typ, bufsize uint16, dnssec bool) *dns.Msg {
m := new(dns.Msg)
m.SetQuestion(name, typ)
m.SetEdns0(bufsize, dnssec)
return m
}
// exchangeWithRetry sends message m to server, but retries on ServerFailure.
func exchangeWithRetry(c *dns.Client, m *dns.Msg, server string) (*dns.Msg, error) {
r, _, err := c.Exchange(m, server)
if err == nil && r.Rcode == dns.RcodeServerFailure {
// redo the query
r, _, err = c.Exchange(m, server)
}
return r, err
}
func (s *server) randomNameserverID(id uint16) int {
nsid := 0
if s.config.NSRotate {
// Use request Id for "random" nameserver selection.
nsid = int(id) % len(s.config.Nameservers)
}
return nsid
}

View File

@ -1,125 +0,0 @@
// Copyright (c) 2014 The SkyDNS Authors. All rights reserved.
// Use of this source code is governed by The MIT License (MIT) that can be
// found in the LICENSE file.
package server
import (
"fmt"
"github.com/miekg/dns"
)
// ServeDNSForward forwards a request to a nameservers and returns the response.
func (s *server) ServeDNSForward(w dns.ResponseWriter, req *dns.Msg) *dns.Msg {
if s.config.NoRec {
m := s.ServerFailure(req)
w.WriteMsg(m)
return m
}
if len(s.config.Nameservers) == 0 || dns.CountLabel(req.Question[0].Name) < s.config.Ndots {
if s.config.Verbose {
if len(s.config.Nameservers) == 0 {
logf("can not forward, no nameservers defined")
} else {
logf("can not forward, name too short (less than %d labels): `%s'", s.config.Ndots, req.Question[0].Name)
}
}
m := s.ServerFailure(req)
m.RecursionAvailable = true // this is still true
w.WriteMsg(m)
return m
}
var (
r *dns.Msg
err error
)
nsid := s.randomNameserverID(req.Id)
try := 0
Redo:
if isTCP(w) {
r, err = exchangeWithRetry(s.dnsTCPclient, req, s.config.Nameservers[nsid])
} else {
r, err = exchangeWithRetry(s.dnsUDPclient, req, s.config.Nameservers[nsid])
}
if err == nil {
r.Compress = true
r.Id = req.Id
w.WriteMsg(r)
return r
}
// Seen an error, this can only mean, "server not reached", try again
// but only if we have not exausted our nameservers.
if try < len(s.config.Nameservers) {
try++
nsid = (nsid + 1) % len(s.config.Nameservers)
goto Redo
}
logf("failure to forward request %q", err)
m := s.ServerFailure(req)
return m
}
// ServeDNSReverse is the handler for DNS requests for the reverse zone. If nothing is found
// locally the request is forwarded to the forwarder for resolution.
func (s *server) ServeDNSReverse(w dns.ResponseWriter, req *dns.Msg) *dns.Msg {
m := new(dns.Msg)
m.SetReply(req)
m.Compress = true
m.Authoritative = false // Set to false, because I don't know what to do wrt DNSSEC.
m.RecursionAvailable = true
var err error
if m.Answer, err = s.PTRRecords(req.Question[0]); err == nil {
// TODO(miek): Reverse DNSSEC. We should sign this, but requires a key....and more
// Probably not worth the hassle?
if err := w.WriteMsg(m); err != nil {
logf("failure to return reply %q", err)
}
return m
}
// Always forward if not found locally.
return s.ServeDNSForward(w, req)
}
// Lookup looks up name,type using the recursive nameserver defines
// in the server's config. If none defined it returns an error.
func (s *server) Lookup(n string, t, bufsize uint16, dnssec bool) (*dns.Msg, error) {
if len(s.config.Nameservers) == 0 {
return nil, fmt.Errorf("no nameservers configured can not lookup name")
}
if dns.CountLabel(n) < s.config.Ndots {
return nil, fmt.Errorf("name has fewer than %d labels", s.config.Ndots)
}
m := newExchangeMsg(n, t, bufsize, dnssec)
nsid := s.randomNameserverID(m.Id)
try := 0
Redo:
r, err := exchangeWithRetry(s.dnsUDPclient, m, s.config.Nameservers[nsid])
if err == nil {
if r.Rcode != dns.RcodeSuccess {
return nil, fmt.Errorf("rcode %d is not equal to success", r.Rcode)
}
// Reset TTLs to rcache TTL to make some of the other code
// and the tests not care about TTLs
for _, rr := range r.Answer {
rr.Header().Ttl = uint32(s.config.RCacheTtl)
}
for _, rr := range r.Extra {
rr.Header().Ttl = uint32(s.config.RCacheTtl)
}
return r, nil
}
// Seen an error, this can only mean, "server not reached", try again
// but only if we have not exausted our nameservers.
if try < len(s.config.Nameservers) {
try++
nsid = (nsid + 1) % len(s.config.Nameservers)
goto Redo
}
return nil, fmt.Errorf("failure to lookup name")
}

View File

@ -1,17 +0,0 @@
// Copyright (c) 2014 The SkyDNS Authors. All rights reserved.
// Use of this source code is governed by The MIT License (MIT) that can be
// found in the LICENSE file.
package server
import "log"
// printf calls log.Printf with the parameters given.
func logf(format string, a ...interface{}) {
log.Printf("skydns: "+format, a...)
}
// fatalf calls log.Fatalf with the parameters given.
func fatalf(format string, a ...interface{}) {
log.Fatalf("skydns: "+format, a...)
}

View File

@ -1,54 +0,0 @@
// Copyright (c) 2014 The SkyDNS Authors. All rights reserved.
// Use of this source code is governed by The MIT License (MIT) that can be
// found in the LICENSE file.
package server
import "github.com/miekg/dns"
// Fit will make m fit the size. If a message is larger than size then entire
// additional section is dropped. If it is still to large and the transport
// is udp we return a truncated message.
// If the transport is tcp we are going to drop RR from the answer section
// until it fits. When this is case the returned bool is true.
func Fit(m *dns.Msg, size int, tcp bool) (*dns.Msg, bool) {
if m.Len() > size {
// Check for OPT Records at the end and keep those. TODO(miek)
m.Extra = nil
}
if m.Len() < size {
return m, false
}
// With TCP setting TC does not mean anything.
if !tcp {
m.Truncated = true
// fall through here, so we at least return a message that can
// fit the udp buffer.
}
// Additional section is gone, binary search until we have length that fits.
min, max := 0, len(m.Answer)
original := make([]dns.RR, len(m.Answer))
copy(original, m.Answer)
for {
if min == max {
break
}
mid := (min + max) / 2
m.Answer = original[:mid]
if m.Len() < size {
min++
continue
}
max = mid
}
if max > 1 {
max--
}
m.Answer = m.Answer[:max]
return m, true
}

View File

@ -1,155 +0,0 @@
// Copyright (c) 2013 Erik St. Martin, Brian Ketelsen. All rights reserved.
// Use of this source code is governed by The MIT License (MIT) that can be
// found in the LICENSE file.
package server
import (
"encoding/base32"
"strings"
"github.com/miekg/dns"
)
// Do DNSSEC NXDOMAIN with NSEC3 whitelies: rfc 7129, appendix B.
// The closest encloser will be qname - the left most label and the
// next closer will be the full qname which we then will deny.
// Idem for source of synthesis.
func (s *server) Denial(m *dns.Msg) {
if m.Rcode == dns.RcodeNameError {
// ce is qname minus the left label
idx := dns.Split(m.Question[0].Name)
ce := m.Question[0].Name[idx[1]:]
nsec3ce, nsec3wildcard := newNSEC3CEandWildcard(s.config.Domain, ce, s.config.MinTtl)
// Add ce and wildcard
m.Ns = append(m.Ns, nsec3ce)
m.Ns = append(m.Ns, nsec3wildcard)
// Deny Qname nsec3
m.Ns = append(m.Ns, s.newNSEC3NameError(m.Question[0].Name))
}
if m.Rcode == dns.RcodeSuccess && len(m.Ns) == 1 {
// NODATA
if _, ok := m.Ns[0].(*dns.SOA); ok {
m.Ns = append(m.Ns, s.newNSEC3NoData(m.Question[0].Name))
}
}
}
func packBase32(s string) []byte {
b32len := base32.HexEncoding.DecodedLen(len(s))
buf := make([]byte, b32len)
n, _ := base32.HexEncoding.Decode(buf, []byte(s))
buf = buf[:n]
return buf
}
func unpackBase32(b []byte) string {
b32 := make([]byte, base32.HexEncoding.EncodedLen(len(b)))
base32.HexEncoding.Encode(b32, b)
return string(b32)
}
// newNSEC3NameError returns the NSEC3 record needed to denial qname.
func (s *server) newNSEC3NameError(qname string) *dns.NSEC3 {
n := new(dns.NSEC3)
n.Hdr.Class = dns.ClassINET
n.Hdr.Rrtype = dns.TypeNSEC3
n.Hdr.Ttl = s.config.MinTtl
n.Hash = dns.SHA1
n.Flags = 0
n.Salt = ""
n.TypeBitMap = []uint16{}
covername := dns.HashName(qname, dns.SHA1, 0, "")
buf := packBase32(covername)
byteArith(buf, false) // one before
n.Hdr.Name = appendDomain(strings.ToLower(unpackBase32(buf)), s.config.Domain)
byteArith(buf, true) // one next
byteArith(buf, true) // and another one
n.NextDomain = unpackBase32(buf)
return n
}
// newNSEC3NoData returns the NSEC3 record needed to denial the types
func (s *server) newNSEC3NoData(qname string) *dns.NSEC3 {
n := new(dns.NSEC3)
n.Hdr.Class = dns.ClassINET
n.Hdr.Rrtype = dns.TypeNSEC3
n.Hdr.Ttl = s.config.MinTtl
n.Hash = dns.SHA1
n.Flags = 0
n.Salt = ""
n.TypeBitMap = []uint16{dns.TypeA, dns.TypeAAAA, dns.TypeSRV, dns.TypeRRSIG}
n.Hdr.Name = dns.HashName(qname, dns.SHA1, 0, "")
buf := packBase32(n.Hdr.Name)
byteArith(buf, true) // one next
n.NextDomain = unpackBase32(buf)
n.Hdr.Name += appendDomain("", s.config.Domain)
return n
}
// newNSEC3CEandWildcard returns the NSEC3 for the closest encloser
// and the NSEC3 that denies that wildcard at that level.
func newNSEC3CEandWildcard(apex, ce string, ttl uint32) (*dns.NSEC3, *dns.NSEC3) {
n1 := new(dns.NSEC3)
n1.Hdr.Class = dns.ClassINET
n1.Hdr.Rrtype = dns.TypeNSEC3
n1.Hdr.Ttl = ttl
n1.Hash = dns.SHA1
n1.Flags = 0
n1.Iterations = 0
n1.Salt = ""
// for the apex we need another bitmap
n1.TypeBitMap = []uint16{dns.TypeA, dns.TypeAAAA, dns.TypeSRV, dns.TypeRRSIG}
prev := dns.HashName(ce, dns.SHA1, n1.Iterations, n1.Salt)
n1.Hdr.Name = strings.ToLower(prev) + "." + apex
buf := packBase32(prev)
byteArith(buf, true) // one next
n1.NextDomain = unpackBase32(buf)
n2 := new(dns.NSEC3)
n2.Hdr.Class = dns.ClassINET
n2.Hdr.Rrtype = dns.TypeNSEC3
n2.Hdr.Ttl = ttl
n2.Hash = dns.SHA1
n2.Flags = 0
n2.Iterations = 0
n2.Salt = ""
prev = dns.HashName("*."+ce, dns.SHA1, n2.Iterations, n2.Salt)
buf = packBase32(prev)
byteArith(buf, false) // one before
n2.Hdr.Name = appendDomain(strings.ToLower(unpackBase32(buf)), apex)
byteArith(buf, true) // one next
byteArith(buf, true) // and another one
n2.NextDomain = unpackBase32(buf)
return n1, n2
}
// byteArith adds either 1 or -1 to b, there is no check for under- or overflow.
func byteArith(b []byte, x bool) {
if x {
for i := len(b) - 1; i >= 0; i-- {
if b[i] == 255 {
b[i] = 0
continue
}
b[i]++
return
}
}
for i := len(b) - 1; i >= 0; i-- {
if b[i] == 0 {
b[i] = 255
continue
}
b[i]--
return
}
}

View File

@ -1,889 +0,0 @@
// Copyright (c) 2014 The SkyDNS Authors. All rights reserved.
// Use of this source code is governed by The MIT License (MIT) that can be
// found in the LICENSE file.
package server
import (
"fmt"
"math"
"net"
"strconv"
"strings"
"sync"
"time"
"github.com/skynetservices/skydns/cache"
"github.com/skynetservices/skydns/metrics"
"github.com/skynetservices/skydns/msg"
etcd "github.com/coreos/etcd/client"
"github.com/coreos/go-systemd/activation"
"github.com/miekg/dns"
)
const Version = "2.5.3a"
type server struct {
backend Backend
config *Config
group *sync.WaitGroup
dnsUDPclient *dns.Client // used for forwarding queries
dnsTCPclient *dns.Client // used for forwarding queries
scache *cache.Cache
rcache *cache.Cache
}
// New returns a new SkyDNS server.
func New(backend Backend, config *Config) *server {
return &server{
backend: backend,
config: config,
group: new(sync.WaitGroup),
scache: cache.New(config.SCache, 0),
rcache: cache.New(config.RCache, config.RCacheTtl),
dnsUDPclient: &dns.Client{Net: "udp", ReadTimeout: config.ReadTimeout, WriteTimeout: config.ReadTimeout, SingleInflight: true},
dnsTCPclient: &dns.Client{Net: "tcp", ReadTimeout: config.ReadTimeout, WriteTimeout: config.ReadTimeout, SingleInflight: true},
}
}
// Run is a blocking operation that starts the server listening on the DNS ports.
func (s *server) Run() error {
mux := dns.NewServeMux()
mux.Handle(".", s)
dnsReadyMsg := func(addr, net string) {
if s.config.DNSSEC == "" {
logf("ready for queries on %s for %s://%s [rcache %d]", s.config.Domain, net, addr, s.config.RCache)
} else {
logf("ready for queries on %s for %s://%s [rcache %d], signing with %s [scache %d]", s.config.Domain, net, addr, s.config.RCache, s.config.DNSSEC, s.config.SCache)
}
}
if s.config.Systemd {
packetConns, err := activation.PacketConns(false)
if err != nil {
return err
}
listeners, err := activation.Listeners(true)
if err != nil {
return err
}
if len(packetConns) == 0 && len(listeners) == 0 {
return fmt.Errorf("no UDP or TCP sockets supplied by systemd")
}
for _, p := range packetConns {
if u, ok := p.(*net.UDPConn); ok {
s.group.Add(1)
go func() {
defer s.group.Done()
if err := dns.ActivateAndServe(nil, u, mux); err != nil {
fatalf("%s", err)
}
}()
dnsReadyMsg(u.LocalAddr().String(), "udp")
}
}
for _, l := range listeners {
if t, ok := l.(*net.TCPListener); ok {
s.group.Add(1)
go func() {
defer s.group.Done()
if err := dns.ActivateAndServe(t, nil, mux); err != nil {
fatalf("%s", err)
}
}()
dnsReadyMsg(t.Addr().String(), "tcp")
}
}
} else {
s.group.Add(1)
go func() {
defer s.group.Done()
if err := dns.ListenAndServe(s.config.DnsAddr, "tcp", mux); err != nil {
fatalf("%s", err)
}
}()
dnsReadyMsg(s.config.DnsAddr, "tcp")
s.group.Add(1)
go func() {
defer s.group.Done()
if err := dns.ListenAndServe(s.config.DnsAddr, "udp", mux); err != nil {
fatalf("%s", err)
}
}()
dnsReadyMsg(s.config.DnsAddr, "udp")
}
s.group.Wait()
return nil
}
// Stop stops a server.
func (s *server) Stop() {
// TODO(miek)
//s.group.Add(-2)
}
// ServeDNS is the handler for DNS requests, responsible for parsing DNS request, possibly forwarding
// it to a real dns server and returning a response.
func (s *server) ServeDNS(w dns.ResponseWriter, req *dns.Msg) {
m := new(dns.Msg)
m.SetReply(req)
m.Authoritative = true
m.RecursionAvailable = true
m.Compress = true
bufsize := uint16(512)
dnssec := false
tcp := false
start := time.Now()
q := req.Question[0]
name := strings.ToLower(q.Name)
if q.Qtype == dns.TypeANY {
m.Authoritative = false
m.Rcode = dns.RcodeRefused
m.RecursionAvailable = false
m.RecursionDesired = false
m.Compress = false
w.WriteMsg(m)
metrics.ReportRequestCount(m, metrics.Auth)
metrics.ReportDuration(m, start, metrics.Auth)
metrics.ReportErrorCount(m, metrics.Auth)
return
}
if o := req.IsEdns0(); o != nil {
bufsize = o.UDPSize()
dnssec = o.Do()
}
if bufsize < 512 {
bufsize = 512
}
// with TCP we can send 64K
if tcp = isTCP(w); tcp {
bufsize = dns.MaxMsgSize - 1
}
if s.config.Verbose {
logf("received DNS Request for %q from %q with type %d", q.Name, w.RemoteAddr(), q.Qtype)
}
// Check cache first.
m1 := s.rcache.Hit(q, dnssec, tcp, m.Id)
if m1 != nil {
metrics.ReportRequestCount(req, metrics.Cache)
if send := s.overflowOrTruncated(w, m1, int(bufsize), metrics.Cache); send {
return
}
// Still round-robin even with hits from the cache.
// Only shuffle A and AAAA records with each other.
if q.Qtype == dns.TypeA || q.Qtype == dns.TypeAAAA {
s.RoundRobin(m1.Answer)
}
if err := w.WriteMsg(m1); err != nil {
logf("failure to return reply %q", err)
}
metrics.ReportDuration(m1, start, metrics.Cache)
metrics.ReportErrorCount(m1, metrics.Cache)
return
}
for zone, ns := range *s.config.stub {
if strings.HasSuffix(name, "." + zone) || name == zone {
metrics.ReportRequestCount(req, metrics.Stub)
resp := s.ServeDNSStubForward(w, req, ns)
if resp != nil {
s.rcache.InsertMessage(cache.Key(q, dnssec, tcp), resp)
}
metrics.ReportDuration(resp, start, metrics.Stub)
metrics.ReportErrorCount(resp, metrics.Stub)
return
}
}
// If the qname is local.ds.skydns.local. and s.config.Local != "", substitute that name.
if s.config.Local != "" && name == s.config.localDomain {
name = s.config.Local
}
if q.Qtype == dns.TypePTR && strings.HasSuffix(name, ".in-addr.arpa.") || strings.HasSuffix(name, ".ip6.arpa.") {
metrics.ReportRequestCount(req, metrics.Reverse)
resp := s.ServeDNSReverse(w, req)
if resp != nil {
s.rcache.InsertMessage(cache.Key(q, dnssec, tcp), resp)
}
metrics.ReportDuration(resp, start, metrics.Reverse)
metrics.ReportErrorCount(resp, metrics.Reverse)
return
}
if q.Qclass != dns.ClassCHAOS && !strings.HasSuffix(name, "." +s.config.Domain) && name != s.config.Domain {
metrics.ReportRequestCount(req, metrics.Rec)
resp := s.ServeDNSForward(w, req)
if resp != nil {
s.rcache.InsertMessage(cache.Key(q, dnssec, tcp), resp)
}
metrics.ReportDuration(resp, start, metrics.Rec)
metrics.ReportErrorCount(resp, metrics.Rec)
return
}
metrics.ReportCacheMiss(metrics.Response)
defer func() {
metrics.ReportDuration(m, start, metrics.Auth)
metrics.ReportErrorCount(m, metrics.Auth)
if m.Rcode == dns.RcodeServerFailure {
if err := w.WriteMsg(m); err != nil {
logf("failure to return reply %q", err)
}
return
}
// Set TTL to the minimum of the RRset and dedup the message, i.e. remove identical RRs.
m = s.dedup(m)
minttl := s.config.Ttl
if len(m.Answer) > 1 {
for _, r := range m.Answer {
if r.Header().Ttl < minttl {
minttl = r.Header().Ttl
}
}
for _, r := range m.Answer {
r.Header().Ttl = minttl
}
}
if dnssec {
if s.config.PubKey != nil {
m.AuthenticatedData = true
s.Denial(m)
s.Sign(m, bufsize)
}
}
if send := s.overflowOrTruncated(w, m, int(bufsize), metrics.Auth); send {
return
}
s.rcache.InsertMessage(cache.Key(q, dnssec, tcp), m)
if err := w.WriteMsg(m); err != nil {
logf("failure to return reply %q", err)
}
}()
if name == s.config.Domain {
if q.Qtype == dns.TypeSOA {
m.Answer = []dns.RR{s.NewSOA()}
return
}
if q.Qtype == dns.TypeDNSKEY {
if s.config.PubKey != nil {
m.Answer = []dns.RR{s.config.PubKey}
return
}
}
}
if q.Qclass == dns.ClassCHAOS {
if q.Qtype == dns.TypeTXT {
switch name {
case "authors.bind.":
fallthrough
case s.config.Domain:
hdr := dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassCHAOS, Ttl: 0}
authors := []string{"Erik St. Martin", "Brian Ketelsen", "Miek Gieben", "Michael Crosby"}
for _, a := range authors {
m.Answer = append(m.Answer, &dns.TXT{Hdr: hdr, Txt: []string{a}})
}
for j := 0; j < len(authors)*(int(dns.Id())%4+1); j++ {
q := int(dns.Id()) % len(authors)
p := int(dns.Id()) % len(authors)
if q == p {
p = (p + 1) % len(authors)
}
m.Answer[q], m.Answer[p] = m.Answer[p], m.Answer[q]
}
return
case "version.bind.":
fallthrough
case "version.server.":
hdr := dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassCHAOS, Ttl: 0}
m.Answer = []dns.RR{&dns.TXT{Hdr: hdr, Txt: []string{Version}}}
return
case "hostname.bind.":
fallthrough
case "id.server.":
// TODO(miek): machine name to return
hdr := dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassCHAOS, Ttl: 0}
m.Answer = []dns.RR{&dns.TXT{Hdr: hdr, Txt: []string{"localhost"}}}
return
}
}
// still here, fail
m.SetReply(req)
m.SetRcode(req, dns.RcodeServerFailure)
return
}
switch q.Qtype {
case dns.TypeNS:
if name != s.config.Domain {
break
}
// Lookup s.config.DnsDomain
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
}
}
func (s *server) AddressRecords(q dns.Question, name string, previousRecords []dns.RR, bufsize uint16, dnssec, both bool) (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) {
logf("CNAME loop detected: %q -> %q", q.Name, q.Name)
// 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), bufsize, dnssec, both)
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 from %q: %s", target, 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()))
}
}
s.RoundRobin(records)
return records, nil
}
// NSRecords returns NS records from etcd.
func (s *server) NSRecords(q dns.Question, name string) (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(q dns.Question, name string, bufsize uint16, dnssec bool) (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, bufsize uint16, dnssec bool) (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, name string) (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, name string) (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 (s *server) PTRRecords(q dns.Question) (records []dns.RR, err error) {
name := strings.ToLower(q.Name)
serv, err := s.backend.ReverseRecord(name)
if err != nil {
return nil, err
}
records = append(records, serv.NewPTR(q.Name, serv.Ttl))
return records, nil
}
// SOA returns a SOA record for this SkyDNS instance.
func (s *server) NewSOA() dns.RR {
return &dns.SOA{Hdr: dns.RR_Header{Name: s.config.Domain, Rrtype: dns.TypeSOA, Class: dns.ClassINET, Ttl: s.config.Ttl},
Ns: appendDomain("ns.dns", s.config.Domain),
Mbox: s.config.Hostmaster,
Serial: uint32(time.Now().Truncate(time.Hour).Unix()),
Refresh: 28800,
Retry: 7200,
Expire: 604800,
Minttl: s.config.MinTtl,
}
}
func (s *server) 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
}
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
}
func (s *server) ServerFailure(req *dns.Msg) *dns.Msg {
m := new(dns.Msg)
m.SetRcode(req, dns.RcodeServerFailure)
return m
}
func (s *server) RoundRobin(rrs []dns.RR) {
if !s.config.RoundRobin {
return
}
// If we have more than 1 CNAME don't touch the packet, because some stub resolver (=glibc)
// can't deal with the returned packet if the CNAMEs need to be accesses in the reverse order.
cname := 0
for _, r := range rrs {
if r.Header().Rrtype == dns.TypeCNAME {
cname++
if cname > 1 {
return
}
}
}
switch l := len(rrs); l {
case 2:
if dns.Id()%2 == 0 {
rrs[0], rrs[1] = rrs[1], rrs[0]
}
default:
for j := 0; j < l*(int(dns.Id())%4+1); j++ {
q := int(dns.Id()) % l
p := int(dns.Id()) % l
if q == p {
p = (p + 1) % l
}
rrs[q], rrs[p] = rrs[p], rrs[q]
}
}
}
// dedup will de-duplicate a message on a per section basis.
// Multiple identical (same name, class, type and rdata) RRs will be coalesced into one.
func (s *server) dedup(m *dns.Msg) *dns.Msg {
// Answer section
ma := make(map[string]dns.RR)
for _, a := range m.Answer {
// Or use Pack()... Think this function also could be placed in go dns.
s1 := a.Header().Name
s1 += strconv.Itoa(int(a.Header().Class))
s1 += strconv.Itoa(int(a.Header().Rrtype))
// there can only be one CNAME for an ownername
if a.Header().Rrtype == dns.TypeCNAME {
if _, ok := ma[s1]; ok {
// already exist, randomly overwrite if roundrobin is true
// Note: even with roundrobin *off* this depends on the
// order we get the names.
if s.config.RoundRobin && dns.Id()%2 == 0 {
ma[s1] = a
continue
}
}
ma[s1] = a
continue
}
for i := 1; i <= dns.NumField(a); i++ {
s1 += dns.Field(a, i)
}
ma[s1] = a
}
// Only is our map is smaller than the #RR in the answer section we should reset the RRs
// in the section it self
if len(ma) < len(m.Answer) {
i := 0
for _, v := range ma {
m.Answer[i] = v
i++
}
m.Answer = m.Answer[:len(ma)]
}
// Additional section
me := make(map[string]dns.RR)
for _, e := range m.Extra {
s1 := e.Header().Name
s1 += strconv.Itoa(int(e.Header().Class))
s1 += strconv.Itoa(int(e.Header().Rrtype))
// there can only be one CNAME for an ownername
if e.Header().Rrtype == dns.TypeCNAME {
if _, ok := me[s1]; ok {
// already exist, randomly overwrite if roundrobin is true
if s.config.RoundRobin && dns.Id()%2 == 0 {
me[s1] = e
continue
}
}
me[s1] = e
continue
}
for i := 1; i <= dns.NumField(e); i++ {
s1 += dns.Field(e, i)
}
me[s1] = e
}
if len(me) < len(m.Extra) {
i := 0
for _, v := range me {
m.Extra[i] = v
i++
}
m.Extra = m.Extra[:len(me)]
}
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
}
// isTCP returns true if the client is connecting over TCP.
func isTCP(w dns.ResponseWriter) bool {
_, ok := w.RemoteAddr().(*net.TCPAddr)
return ok
}
// 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
}

View File

@ -1,124 +0,0 @@
// Copyright (c) 2014 The SkyDNS Authors. All rights reserved.
// Use of this source code is governed by The MIT License (MIT) that can be
// found in the LICENSE file.
package server
import (
"net"
"strconv"
"strings"
"github.com/miekg/dns"
"github.com/skynetservices/skydns/msg"
)
const ednsStubCode = dns.EDNS0LOCALSTART + 10
// ednsStub is the EDNS0 record we add to stub queries. Queries which have this record are
// not forwarded again.
var ednsStub = func() *dns.OPT {
o := new(dns.OPT)
o.Hdr.Name = "."
o.Hdr.Rrtype = dns.TypeOPT
e := new(dns.EDNS0_LOCAL)
e.Code = ednsStubCode
e.Data = []byte{1}
o.Option = append(o.Option, e)
return o
}()
// Look in .../dns/stub/<domain>/xx for msg.Services. Loop through them
// extract <domain> and add them as forwarders (ip:port-combos) for
// the stub zones. Only numeric (i.e. IP address) hosts are used.
func (s *server) UpdateStubZones() {
stubmap := make(map[string][]string)
services, err := s.backend.Records("stub.dns."+s.config.Domain, false)
if err != nil {
logf("stub zone update failed: %s", err)
return
}
for _, serv := range services {
if serv.Port == 0 {
serv.Port = 53
}
ip := net.ParseIP(serv.Host)
if ip == nil {
logf("stub zone non-address %s seen for: %s", serv.Key, serv.Host)
continue
}
domain := msg.Domain(serv.Key)
// Chop of left most label, because that is used as the nameserver place holder
// and drop the right most labels that belong to localDomain.
labels := dns.SplitDomainName(domain)
domain = dns.Fqdn(strings.Join(labels[1:len(labels)-dns.CountLabel(s.config.localDomain)], "."))
// If the remaining name equals s.config.LocalDomain we ignore it.
if domain == s.config.localDomain {
logf("not adding stub zone for my own domain")
continue
}
stubmap[domain] = append(stubmap[domain], net.JoinHostPort(serv.Host, strconv.Itoa(serv.Port)))
}
s.config.stub = &stubmap
}
// ServeDNSStubForward forwards a request to a nameservers and returns the response.
func (s *server) ServeDNSStubForward(w dns.ResponseWriter, req *dns.Msg, ns []string) *dns.Msg {
// Check EDNS0 Stub option, if set drop the packet.
option := req.IsEdns0()
if option != nil {
for _, o := range option.Option {
if o.Option() == ednsStubCode && len(o.(*dns.EDNS0_LOCAL).Data) == 1 &&
o.(*dns.EDNS0_LOCAL).Data[0] == 1 {
// Maybe log source IP here?
logf("not fowarding stub request to another stub")
return nil
}
}
}
// Add a custom EDNS0 option to the packet, so we can detect loops
// when 2 stubs are forwarding to each other.
if option != nil {
option.Option = append(option.Option, &dns.EDNS0_LOCAL{ednsStubCode, []byte{1}})
} else {
req.Extra = append(req.Extra, ednsStub)
}
var (
r *dns.Msg
err error
)
// Use request Id for "random" nameserver selection.
nsid := int(req.Id) % len(ns)
try := 0
Redo:
if isTCP(w) {
r, err = exchangeWithRetry(s.dnsTCPclient, req, ns[nsid])
} else {
r, err = exchangeWithRetry(s.dnsUDPclient, req, ns[nsid])
}
if err == nil {
r.Compress = true
r.Id = req.Id
w.WriteMsg(r)
return r
}
// Seen an error, this can only mean, "server not reached", try again
// but only if we have not exausted our nameservers.
if try < len(ns) {
try++
nsid = (nsid + 1) % len(ns)
goto Redo
}
logf("failure to forward stub request %q", err)
m := s.ServerFailure(req)
w.WriteMsg(m)
return m
}

View File

@ -1,64 +0,0 @@
/*
Copyright 2012 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package singleflight provides a duplicate function call suppression
// mechanism.
package singleflight
import "sync"
// call is an in-flight or completed Do call
type call struct {
wg sync.WaitGroup
val interface{}
err error
}
// Group represents a class of work and forms a namespace in which
// units of work can be executed with duplicate suppression.
type Group struct {
mu sync.Mutex // protects m
m map[string]*call // lazily initialized
}
// Do executes and returns the results of the given function, making
// sure that only one execution is in-flight for a given key at a
// time. If a duplicate comes in, the duplicate caller waits for the
// original to complete and receives the same results.
func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
g.mu.Lock()
if g.m == nil {
g.m = make(map[string]*call)
}
if c, ok := g.m[key]; ok {
g.mu.Unlock()
c.wg.Wait()
return c.val, c.err
}
c := new(call)
c.wg.Add(1)
g.m[key] = c
g.mu.Unlock()
c.val, c.err = fn()
c.wg.Done()
g.mu.Lock()
delete(g.m, key)
g.mu.Unlock()
return c.val, c.err
}