| // Copyright 2021 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef DNS_PROXY_CONTROLLER_H_ |
| #define DNS_PROXY_CONTROLLER_H_ |
| |
| #include <iostream> |
| #include <map> |
| #include <memory> |
| #include <optional> |
| #include <set> |
| #include <string> |
| |
| #include <base/memory/weak_ptr.h> |
| #include <brillo/daemons/dbus_daemon.h> |
| #include <brillo/process/process_reaper.h> |
| #include <chromeos/patchpanel/dbus/client.h> |
| #include <chromeos/patchpanel/message_dispatcher.h> |
| #include <shill/dbus/client/client.h> |
| |
| #include "dns-proxy/chrome_features_service_client.h" |
| #include "dns-proxy/ipc.pb.h" |
| #include "dns-proxy/metrics.h" |
| #include "dns-proxy/proxy.h" |
| #include "dns-proxy/resolv_conf.h" |
| |
| namespace dns_proxy { |
| |
| // The parent process for the service. Responsible for managing the proxy |
| // subprocesses. |
| class Controller : public brillo::DBusDaemon { |
| public: |
| explicit Controller(const std::string& progname); |
| // For testing. |
| explicit Controller(std::unique_ptr<ResolvConf> resolv_conf); |
| |
| Controller(const Controller&) = delete; |
| Controller& operator=(const Controller&) = delete; |
| ~Controller() = default; |
| |
| protected: |
| int OnInit() override; |
| void OnShutdown(int*) override; |
| |
| private: |
| struct ProxyProc { |
| ProxyProc() : pid(0) {} |
| ProxyProc(Proxy::Type type, const std::string& ifname) : pid(0) { |
| opts.type = type; |
| opts.ifname = ifname; |
| } |
| |
| friend std::ostream& operator<<(std::ostream& stream, |
| const Controller::ProxyProc& proc) { |
| stream << proc.opts; |
| if (proc.pid > 0) { |
| stream << "(pid: " << proc.pid << ")"; |
| } |
| return stream; |
| } |
| |
| // |pid| is intentionally excluded as only the strings are used as a key. |
| bool operator<(const ProxyProc& that) const { |
| return (opts.type < that.opts.type || opts.ifname < that.opts.ifname); |
| } |
| |
| pid_t pid; |
| Proxy::Options opts; |
| }; |
| |
| struct ProxyRestarts { |
| static constexpr int kRestartLimit = 10; |
| static constexpr base::TimeDelta kRestartWindow = base::Seconds(20); |
| |
| bool is_valid() const { return count > 0; } |
| |
| bool try_next() { |
| if (base::Time::Now() - kRestartWindow <= since) |
| return (--count > 0); |
| |
| since = base::Time::Now(); |
| count = kRestartLimit; |
| return true; |
| } |
| |
| base::Time since{base::Time::Now()}; |
| int count{kRestartLimit}; |
| }; |
| |
| void Setup(); |
| void SetupPatchpanel(); |
| void OnPatchpanelReady(bool success); |
| void OnPatchpanelReset(bool reset); |
| void OnShillReady(bool success); |
| void OnShillReset(bool reset); |
| |
| void RunProxy(Proxy::Type type, const std::string& ifname = ""); |
| void KillProxy(Proxy::Type type, |
| const std::string& ifname = "", |
| bool forget = true); |
| void Kill(const ProxyProc& proc, bool forget = true); |
| void OnProxyExit(pid_t pid, const siginfo_t& siginfo); |
| void EvalProxyExit(const ProxyProc& proc); |
| bool RestartProxy(const ProxyProc& proc); |
| |
| // Callback to be triggered when there is a message from the proxy. On |
| // failure, restart the listener and the sender of the message (system |
| // proxy). |
| void OnProxyAddrMessageFailure(); |
| void OnProxyAddrMessage(const ProxyAddrMessage& msg); |
| |
| // Callback used to run/kill default proxy based on its dependencies. |
| // |has_deps| will be true if either VPN or a single-networked guest OS is |
| // running. |
| void EvalDefaultProxyDeps(bool has_deps); |
| |
| // Notified by shill whenever the default device changes. |
| void OnDefaultDeviceChanged(const shill::Client::Device* const device); |
| |
| // Notified by patchpanel whenever a change occurs in one of its virtual |
| // network devices. |
| void OnVirtualDeviceChanged(patchpanel::Client::VirtualDeviceEvent event, |
| const patchpanel::Client::VirtualDevice& device); |
| void VirtualDeviceAdded(const patchpanel::Client::VirtualDevice& device); |
| void VirtualDeviceRemoved(const patchpanel::Client::VirtualDevice& device); |
| |
| // Triggered by the Chrome features client in response to checking |
| // IsDNSProxyEnabled. |
| void OnFeatureEnabled(std::optional<bool> enabled); |
| |
| FRIEND_TEST(ControllerTest, SetProxyAddrs); |
| FRIEND_TEST(ControllerTest, ClearProxyAddrs); |
| |
| const std::string progname_; |
| brillo::ProcessReaper process_reaper_; |
| std::set<ProxyProc> proxies_; |
| std::map<ProxyProc, ProxyRestarts> restarts_; |
| |
| // Listener for system proxy's IP addresses to be written to /etc/resolv.conf. |
| std::unique_ptr<patchpanel::MessageDispatcher<ProxyAddrMessage>> |
| msg_dispatcher_; |
| |
| // Helper class to update resolv.conf entries. |
| std::unique_ptr<ResolvConf> resolv_conf_; |
| |
| bool shill_ready_{false}; |
| std::unique_ptr<shill::Client> shill_; |
| std::unique_ptr<patchpanel::Client> patchpanel_; |
| |
| bool is_shutdown_{false}; |
| |
| std::optional<bool> feature_enabled_; |
| std::unique_ptr<ChromeFeaturesServiceClient> features_; |
| |
| Metrics metrics_; |
| |
| base::WeakPtrFactory<Controller> weak_factory_{this}; |
| }; |
| |
| } // namespace dns_proxy |
| |
| #endif // DNS_PROXY_CONTROLLER_H_ |