blob: 17aa44bc7bab692918d33c36b931497c2d1f196b [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package botanist
import (
"context"
"fmt"
"net"
"time"
"go.fuchsia.dev/tools/logger"
"go.fuchsia.dev/tools/mdns"
"go.fuchsia.dev/tools/retry"
)
// Interval at which ResolveIP will wait for a response to a question packet.
const mDNSTimeout time.Duration = 2 * time.Second
func getLocalDomain(nodename string) string {
return nodename + ".local"
}
// ResolveIP returns the IPv4 address of a fuchsia node via mDNS.
//
// TODO(joshuaseaton): Refactor dev_finder to share 'resolve' logic with botanist.
func ResolveIPv4(ctx context.Context, nodename string, timeout time.Duration) (net.IP, error) {
m := mdns.NewMDNS()
m.EnableIPv4()
out := make(chan net.IP)
domain := getLocalDomain(nodename)
m.AddHandler(func(iface net.Interface, addr net.Addr, packet mdns.Packet) {
for _, a := range packet.Answers {
if a.Class == mdns.IN && a.Type == mdns.A && a.Domain == domain {
out <- net.IP(a.Data)
return
}
}
})
m.AddWarningHandler(func(addr net.Addr, err error) {
logger.Infof(ctx, "from: %v; warn: %v", addr, err)
})
errs := make(chan error)
m.AddErrorHandler(func(err error) {
errs <- err
})
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
if err := m.Start(ctx, mdns.DefaultPort); err != nil {
return nil, fmt.Errorf("could not start mDNS client: %v", err)
}
// Send question packets to the mDNS server at intervals of mDNSTimeout for a total of
// |timeout|; retry, as it takes time for the netstack and server to be brought up.
var ip net.IP
var err error
err = retry.Retry(ctx, &retry.ZeroBackoff{}, func() error {
m.Send(mdns.QuestionPacket(domain))
ctx, cancel := context.WithTimeout(context.Background(), mDNSTimeout)
defer cancel()
select {
case <-ctx.Done():
return fmt.Errorf("timeout")
case err = <-errs:
return err
case ip = <-out:
return nil
}
}, nil)
return ip, err
}