// SPDX-FileCopyrightText: 2023 The Pion community // SPDX-License-Identifier: MIT package vnet import ( "errors" "fmt" "net" "sync" "github.com/pion/logging" ) var ( errHostnameEmpty = errors.New("host name must not be empty") errFailedToParseIPAddr = errors.New("failed to parse IP address") ) type resolverConfig struct { LoggerFactory logging.LoggerFactory } type resolver struct { parent *resolver // read-only hosts map[string]net.IP // requires mutex mutex sync.RWMutex // thread-safe log logging.LeveledLogger // read-only } func newResolver(config *resolverConfig) *resolver { r := &resolver{ hosts: map[string]net.IP{}, log: config.LoggerFactory.NewLogger("vnet"), } if err := r.addHost("localhost", "127.0.0.1"); err != nil { r.log.Warn("failed to add localhost to resolver") } return r } func (r *resolver) setParent(parent *resolver) { r.mutex.Lock() defer r.mutex.Unlock() r.parent = parent } func (r *resolver) addHost(name string, ipAddr string) error { r.mutex.Lock() defer r.mutex.Unlock() if len(name) == 0 { return errHostnameEmpty } ip := net.ParseIP(ipAddr) if ip == nil { return fmt.Errorf("%w \"%s\"", errFailedToParseIPAddr, ipAddr) } r.hosts[name] = ip return nil } func (r *resolver) lookUp(hostName string) (net.IP, error) { ip := func() net.IP { r.mutex.RLock() defer r.mutex.RUnlock() if ip2, ok := r.hosts[hostName]; ok { return ip2 } return nil }() if ip != nil { return ip, nil } // mutex must be unlocked before calling into parent resolver if r.parent != nil { return r.parent.lookUp(hostName) } return nil, &net.DNSError{ Err: "host not found", Name: hostName, Server: "vnet resolver", IsTimeout: false, IsTemporary: false, } }