154 lines
3.3 KiB
Go
154 lines
3.3 KiB
Go
package nameserver
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"github.com/miekg/dns"
|
|
. "github.com/weaveworks/weave/common"
|
|
"net"
|
|
"sync"
|
|
)
|
|
|
|
const (
|
|
RDNSDomain = "in-addr.arpa."
|
|
)
|
|
|
|
// +1 to also exclude a dot
|
|
var rdnsDomainLen = len(RDNSDomain) + 1
|
|
|
|
type Zone interface {
|
|
AddRecord(ident string, name string, ip net.IP) error
|
|
DeleteRecord(ident string, ip net.IP) error
|
|
DeleteRecordsFor(ident string) error
|
|
Domain() string
|
|
ZoneLookup
|
|
}
|
|
|
|
type dbRecord struct {
|
|
Ident string
|
|
Name string
|
|
IP net.IP
|
|
}
|
|
|
|
// Very simple data structure for now, with linear searching.
|
|
// TODO: make more sophisticated to improve performance.
|
|
type ZoneDb struct {
|
|
mx sync.RWMutex
|
|
recs []dbRecord
|
|
domain string
|
|
}
|
|
|
|
type LookupError string
|
|
|
|
func (ops LookupError) Error() string {
|
|
return "Unable to find " + string(ops)
|
|
}
|
|
|
|
type DuplicateError struct {
|
|
}
|
|
|
|
func (dup DuplicateError) Error() string {
|
|
return "Tried to add a duplicate entry"
|
|
}
|
|
|
|
func NewZoneDb(domain string) *ZoneDb {
|
|
return &ZoneDb{
|
|
domain: domain,
|
|
}
|
|
}
|
|
|
|
func (zone *ZoneDb) Domain() string {
|
|
return zone.domain
|
|
}
|
|
|
|
func (zone *ZoneDb) indexOf(match func(dbRecord) bool) int {
|
|
for i, r := range zone.recs {
|
|
if match(r) {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
func (zone *ZoneDb) String() string {
|
|
zone.mx.RLock()
|
|
defer zone.mx.RUnlock()
|
|
var buf bytes.Buffer
|
|
for _, r := range zone.recs {
|
|
fmt.Fprintf(&buf, "%.12s %s %v\n", r.Ident, r.IP, r.Name)
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
func (zone *ZoneDb) LookupName(name string) ([]ZoneRecord, error) {
|
|
zone.mx.RLock()
|
|
defer zone.mx.RUnlock()
|
|
for _, r := range zone.recs {
|
|
if r.Name == name {
|
|
return []ZoneRecord{Record{r.Name, r.IP, 0, 0, 0}}, nil
|
|
}
|
|
}
|
|
return nil, LookupError(name)
|
|
}
|
|
|
|
func (zone *ZoneDb) LookupInaddr(inaddr string) ([]ZoneRecord, error) {
|
|
if revIP := net.ParseIP(inaddr[:len(inaddr)-rdnsDomainLen]); revIP != nil {
|
|
revIP4 := revIP.To4()
|
|
ip := []byte{revIP4[3], revIP4[2], revIP4[1], revIP4[0]}
|
|
Debug.Printf("[zonedb] Looking for address: %+v", ip)
|
|
zone.mx.RLock()
|
|
defer zone.mx.RUnlock()
|
|
for _, r := range zone.recs {
|
|
if r.IP.Equal(ip) {
|
|
return []ZoneRecord{Record{r.Name, r.IP, 0, 0, 0}}, nil
|
|
}
|
|
}
|
|
return nil, LookupError(inaddr)
|
|
}
|
|
Warning.Printf("[zonedb] Asked to reverse lookup %s", inaddr)
|
|
return nil, LookupError(inaddr)
|
|
}
|
|
|
|
func (zone *ZoneDb) AddRecord(ident string, name string, ip net.IP) error {
|
|
zone.mx.Lock()
|
|
defer zone.mx.Unlock()
|
|
fqdn := dns.Fqdn(name)
|
|
pred := func(r dbRecord) bool { return r.Name == fqdn && r.IP.Equal(ip) && r.Ident == ident }
|
|
if index := zone.indexOf(pred); index != -1 {
|
|
return DuplicateError{}
|
|
}
|
|
zone.recs = append(zone.recs, dbRecord{ident, fqdn, ip})
|
|
return nil
|
|
}
|
|
|
|
func (zone *ZoneDb) DeleteRecord(ident string, ip net.IP) error {
|
|
zone.mx.Lock()
|
|
defer zone.mx.Unlock()
|
|
pred := func(r dbRecord) bool { return r.Ident == ident && r.IP.Equal(ip) }
|
|
if index := zone.indexOf(pred); index != -1 {
|
|
zone.recs = append(zone.recs[:index], zone.recs[index+1:]...)
|
|
return nil
|
|
}
|
|
return LookupError(ident)
|
|
}
|
|
|
|
func (zone *ZoneDb) DeleteRecordsFor(ident string) error {
|
|
zone.mx.Lock()
|
|
defer zone.mx.Unlock()
|
|
w := 0 // write index
|
|
|
|
for _, r := range zone.recs {
|
|
if r.Ident != ident {
|
|
zone.recs[w] = r
|
|
w++
|
|
}
|
|
}
|
|
zone.recs = zone.recs[:w]
|
|
return nil
|
|
}
|
|
|
|
func (zone *ZoneDb) ContainerDied(ident string) error {
|
|
Info.Printf("[zonedb] Container %s down. Removing records", ident)
|
|
return zone.DeleteRecordsFor(ident)
|
|
}
|