diff --git a/nameserver/zone.go b/nameserver/zone.go index b7360fd8..95ab4809 100644 --- a/nameserver/zone.go +++ b/nameserver/zone.go @@ -48,6 +48,10 @@ type Zone interface { DeleteRecord(ident string, ip net.IP) error // Delete all records for an ident in the local database DeleteRecordsFor(ident string) error + // Lookup for a name in the whole domain + DomainLookupName(name string) ([]ZoneRecord, error) + // Lookup for an address in the whole domain + DomainLookupInaddr(inaddr string) ([]ZoneRecord, error) // Return a status string Status() string } diff --git a/nameserver/zone_lookup.go b/nameserver/zone_lookup.go index 985f84ae..f64c7fb3 100644 --- a/nameserver/zone_lookup.go +++ b/nameserver/zone_lookup.go @@ -28,6 +28,67 @@ func (zone *zoneDb) LookupName(name string) (res []ZoneRecord, err error) { return } +// Perform a lookup for a name in the zone +// The name can be resolved locally with the local database or with some other resolution method (eg, a mDNS query) +func (zone *zoneDb) DomainLookupName(name string) (res []ZoneRecord, err error) { + name = dns.Fqdn(name) + Debug.Printf("[zonedb] Looking for name '%s' in local(&remote) database", name) + + zone.mx.RLock() + now := zone.clock.Now() + for identName, nameset := range zone.idents { + for _, ze := range nameset.getEntriesForName(name) { + // filter the entries with expired TTL + // locally introduced entries are nver expired as the always have TTL=0 + if ze.hasExpired(now) { + Debug.Printf("[zonedb] '%s': expired entry '%s' ignored: removing", name, ze) + nameset.deleteName(name) + } else { + res = append(res, ze) + } + } + if identName != defaultRemoteIdent { + nameset.touchName(name, now) + } + } + zone.mx.RUnlock() + + if len(res) > 0 { + Debug.Printf("[zonedb] '%s' resolved in local database", name) + } else { + // no local results have been obtained in the local database: try with a mDNS query + // (this request is not a background query, so we cannot delay the response) + Debug.Printf("[zonedb] name '%s' not in local database: trying with mDNS", name) + ips, err := zone.mdnsCli.LookupName(name) + if err != nil { + Debug.Printf("[zonedb] mDNS lookup error for '%s': %s", name, err) + return nil, err + } + + // if the request has been successful, save the IP in the local database and return the corresponding ZoneRecord + // (we do not get the remote ident in the mDNS reply, so we save it in a "remote" ident) + Debug.Printf("[zonedb] adding '%s' (obtained with mDNS) to '%s'", ips, name) + res = make([]ZoneRecord, len(ips)) + zone.mx.Lock() + now = zone.clock.Now() + for i, zr := range ips { + res[i], err = zone.getNamesSet(defaultRemoteIdent).addIPToName(zr, now) + if err != nil { + zone.mx.Unlock() + Warning.Printf("[zonedb] IP [%s] insertion for '%s' failed: %s", ips, name, err) + return nil, err + } + } + zone.mx.Unlock() + } + + if len(res) > 0 { + return res, nil + } + + return nil, LookupError(name) +} + // Perform a lookup for a IP address in the zone // The address can be resolved locally with the local database func (zone *zoneDb) LookupInaddr(inaddr string) (res []ZoneRecord, err error) { @@ -53,3 +114,70 @@ func (zone *zoneDb) LookupInaddr(inaddr string) (res []ZoneRecord, err error) { } return } + +// Perform a lookup for a IP address in the zone +// The address can be resolved either with the local database or +// with some other resolution method (eg, a mDNS query) +func (zone *zoneDb) DomainLookupInaddr(inaddr string) (res []ZoneRecord, err error) { + revIPv4, err := raddrToIPv4(inaddr) + if err != nil { + return nil, newParseError("lookup address", inaddr) + } + + Debug.Printf("[zonedb] Looking for address in local(&remote) database: '%s' (%s)", revIPv4, inaddr) + + zone.mx.RLock() + now := zone.clock.Now() + for identName, nameset := range zone.idents { + for _, ze := range nameset.getEntriesForIP(revIPv4) { + // filter the entries with expired TTL + // locally introduced entries are nver expired as the always have TTL=0 + if ze.hasExpired(now) { + Debug.Printf("[zonedb] '%s': expired entry '%s' ignored: removing", revIPv4, ze) + nameset.deleteIP(revIPv4) + } else { + res = append(res, ZoneRecord(ze)) + if identName != defaultRemoteIdent { + nameset.touchName(ze.Name(), now) + } + } + } + } + zone.mx.RUnlock() + + if len(res) > 0 { + Debug.Printf("[zonedb] '%s' resolved in local database", inaddr) + } else { + // no local results have been obtained in the local database: try with a mDNS query + Debug.Printf("[zonedb] '%s'(%+v) not in local database... trying with mDNS", inaddr, revIPv4) + names, err := zone.mdnsCli.LookupInaddr(inaddr) + if err != nil { + Debug.Printf("[zonedb] mDNS lookup error for '%s': %s", inaddr, err) + return nil, err + } + + // if the request has been successful, save the IP in the local database and return the corresponding ZoneRecord + // (we do not get the remote ident in the mDNS reply, so we save it in a "remote" ident) + Debug.Printf("[zonedb] adding '%s' (obtained with mDNS) to '%s'", names, revIPv4) + res = make([]ZoneRecord, len(names)) + zone.mx.Lock() + now = zone.clock.Now() + for i, name := range names { + res[i], err = zone.getNamesSet(defaultRemoteIdent).addIPToName(name, now) + if err != nil { + zone.mx.Unlock() + Warning.Printf("[zonedb] Name '%s' insertion for %s failed: %s", name.Name(), revIPv4, err) + return nil, err + } + } + zone.mx.Unlock() + } + + if len(res) > 0 { + return res, nil + } + + return nil, LookupError(inaddr) + +} +