blob: f2e1eb4b12033648a5c4eb17fce41d908eb82f77 [file] [log] [blame]
Garrick Evans066dc2c2020-12-10 10:43:55 +09001// Copyright 2021 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "dns-proxy/proxy.h"
6
7#include <sys/types.h>
8#include <unistd.h>
9
10#include <utility>
11
12#include <base/bind.h>
13#include <base/threading/thread_task_runner_handle.h>
Garrick Evans48c84ef2021-01-28 11:29:42 +090014#include <chromeos/patchpanel/net_util.h>
15#include <shill/dbus-constants.h>
Garrick Evans066dc2c2020-12-10 10:43:55 +090016
17namespace dns_proxy {
18
19constexpr char kSystemProxyType[] = "sys";
20constexpr char kDefaultProxyType[] = "def";
21constexpr char kARCProxyType[] = "arc";
22
23// static
24const char* Proxy::TypeToString(Type t) {
25 switch (t) {
26 case Type::kSystem:
27 return kSystemProxyType;
28 case Type::kDefault:
29 return kDefaultProxyType;
30 case Type::kARC:
31 return kARCProxyType;
32 }
33}
34
35// static
36std::optional<Proxy::Type> Proxy::StringToType(const std::string& s) {
37 if (s == kSystemProxyType)
38 return Type::kSystem;
39
40 if (s == kDefaultProxyType)
41 return Type::kDefault;
42
43 if (s == kARCProxyType)
44 return Type::kARC;
45
46 return std::nullopt;
47}
48
49std::ostream& operator<<(std::ostream& stream, Proxy::Type type) {
50 stream << Proxy::TypeToString(type);
51 return stream;
52}
53
54std::ostream& operator<<(std::ostream& stream, Proxy::Options opt) {
55 stream << "{" << Proxy::TypeToString(opt.type) << ":" << opt.ifname << "}";
56 return stream;
57}
58
59Proxy::Proxy(const Proxy::Options& opts) : opts_(opts) {}
60
61int Proxy::OnInit() {
62 LOG(INFO) << "Starting DNS proxy " << opts_;
63
64 /// Run after Daemon::OnInit()
65 base::ThreadTaskRunnerHandle::Get()->PostTask(
66 FROM_HERE, base::Bind(&Proxy::Setup, weak_factory_.GetWeakPtr()));
67 return DBusDaemon::OnInit();
68}
69
70void Proxy::OnShutdown(int*) {
71 LOG(INFO) << "Stopping DNS proxy " << opts_;
Garrick Evans48c84ef2021-01-28 11:29:42 +090072 SetShillProperty("");
Garrick Evans066dc2c2020-12-10 10:43:55 +090073}
74
75void Proxy::Setup() {
76 shill_.reset(new shill::Client(bus_));
77 shill_->Init();
78 shill_->RegisterDefaultServiceChangedHandler(base::BindRepeating(
79 &Proxy::OnDefaultServiceChanged, weak_factory_.GetWeakPtr()));
80 shill_->RegisterDefaultDeviceChangedHandler(
81 base::BindRepeating(&Proxy::OnDeviceChanged, weak_factory_.GetWeakPtr(),
82 true /* is_default */));
83 shill_->RegisterDeviceChangedHandler(
84 base::BindRepeating(&Proxy::OnDeviceChanged, weak_factory_.GetWeakPtr(),
85 false /* is_default */));
86 // TODO(garrick) - add service state callback - proxy cannot run unless the
87 // device's selected service is online
88
89 patchpanel_ = patchpanel::Client::New();
90 CHECK(patchpanel_) << "Failed to initialize patchpanel client";
91 patchpanel_->RegisterOnAvailableCallback(base::BindRepeating(
92 &Proxy::OnPatchpanelReady, weak_factory_.GetWeakPtr()));
93}
94
95void Proxy::OnPatchpanelReady(bool success) {
96 CHECK(success) << "Failed to connect to patchpanel";
97
98 // The default network proxy might actually be carrying Chrome, Crostini or
99 // if a VPN is on, even ARC traffic, but we attribute this as as "user"
100 // sourced.
101 patchpanel::TrafficCounter::Source traffic_source;
102 switch (opts_.type) {
103 case Type::kSystem:
104 traffic_source = patchpanel::TrafficCounter::SYSTEM;
105 break;
106 case Type::kARC:
107 traffic_source = patchpanel::TrafficCounter::ARC;
108 break;
109 default:
110 traffic_source = patchpanel::TrafficCounter::USER;
111 }
112
113 // Note that using getpid() here requires that this minijail is not creating a
114 // new PID namespace.
115 // The default proxy (only) needs to use the VPN, if applicable, the others
116 // expressly need to avoid it.
117 auto res = patchpanel_->ConnectNamespace(
118 getpid(), opts_.ifname, true /* forward_user_traffic */,
119 opts_.type == Type::kDefault /* route_on_vpn */, traffic_source);
120 CHECK(res.first.is_valid())
121 << "Failed to establish private network namespace";
122 ns_fd_ = std::move(res.first);
123 // TODO(garrick): Use the response... for now just ack it worked.
124 LOG(INFO) << "Sucessfully connected private network namespace:"
125 << res.second.host_ifname() << " <--> " << res.second.peer_ifname();
Garrick Evans48c84ef2021-01-28 11:29:42 +0900126
127 // If this is the system proxy, tell shill about it. We should start receiving
128 // DNS traffic on success. If this fails, we don't have much choice but to
129 // just die and try again...
130 if (opts_.type == Type::kSystem)
131 CHECK(SetShillProperty(
132 patchpanel::IPv4AddressToString(res.second.host_ipv4_address())));
Garrick Evans066dc2c2020-12-10 10:43:55 +0900133}
134
135void Proxy::OnDefaultServiceChanged(const std::string& type) {}
136
137void Proxy::OnDeviceChanged(bool is_default,
138 const shill::Client::Device* const device) {}
139
Garrick Evans48c84ef2021-01-28 11:29:42 +0900140bool Proxy::SetShillProperty(const std::string& addr) {
141 if (opts_.type != Type::kSystem)
142 return false;
143
144 if (!shill_) {
145 LOG(WARNING)
146 << "Lost connection to shill - cannot set dns-proxy address property ["
147 << addr << "]";
148 return false;
149 }
150
151 brillo::ErrorPtr error;
152 if (!shill_->ManagerProperties()->Set(shill::kDNSProxyIPv4AddressProperty,
153 addr, &error)) {
154 LOG(ERROR) << "Failed to set dns-proxy address property [" << addr
155 << "] on shill: " << error->GetMessage();
156 return false;
157 }
158 return true;
159}
160
Garrick Evans066dc2c2020-12-10 10:43:55 +0900161} // namespace dns_proxy