code: func validateAdvertiseAddr(ctx context.Context, advAddr, listenAddr string) (string, string, string, error) {
  	listenScheme, listenHost, listenPort, err := splitAddr(listenAddr)
  	if err != nil {
  		return "", "", "", errors.Wrap(err, "getting listen address")
  	}
  
  	advScheme, advHostPort := splitScheme(advAddr)
  	advHost, advPort := "", ""
  	if advHostPort != "" {
  		var err error
  		advHost, advPort, err = net.SplitHostPort(advHostPort)
  		if err != nil {
  			return "", "", "", errors.Wrapf(err, "splitting host port: %s", advHostPort)
  		}
  	}
  	// If no advertise scheme was specified, use the one from
  	// the listen address.
  	if advScheme == "" {
  		advScheme = listenScheme
  	}
  	// If there was no port number, reuse the one from the listen
  	// address.
  	if advPort == "" || advPort == "0" {
  		advPort = listenPort
  	}
  	// Resolve non-numeric to numeric.
  	portNumber, err := net.DefaultResolver.LookupPort(ctx, "tcp", advPort)
  	if err != nil {
  		return "", "", "", errors.Wrapf(err, "looking up non-numeric port: %v", advPort)
  	}
  	advPort = strconv.Itoa(portNumber)
  
  	// If the advertise host is empty, then we have two cases.
  	if advHost == "" {
  		if listenHost == "0.0.0.0" {
  			advHost = outboundIP().String()
  		} else {
  			advHost = listenHost
  		}
  	}
  	return advScheme, advHost, advPort, nil
  }
docs: // validateAdvertiseAddr validates and normalizes an address accessible
  // Ensures that if the "host" part is empty, it gets filled in with
  // the configured listen address if any, otherwise it makes a best
  // guess at the outbound IP address.
  // Returns scheme, host, port as strings.