1 // Copyright 2015 The Weave 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 "examples/provider/event_network.h"
6 
7 #include <weave/enum_to_string.h>
8 
9 #include <base/bind.h>
10 #include <event2/bufferevent.h>
11 #include <event2/dns.h>
12 
13 #include "examples/provider/event_task_runner.h"
14 #include "examples/provider/ssl_stream.h"
15 
16 namespace weave {
17 namespace examples {
18 
19 namespace {
20 const char kNetworkProbeHostname[] = "talk.google.com";
21 const int kNetworkProbePort = 5223;
22 const int kNetworkProbeTimeoutS = 2;
23 }  // namespace
24 
operator ()(evdns_base * dns_base)25 void EventNetworkImpl::Deleter::operator()(evdns_base* dns_base) {
26   evdns_base_free(dns_base, 0);
27 }
28 
operator ()(bufferevent * bev)29 void EventNetworkImpl::Deleter::operator()(bufferevent* bev) {
30   bufferevent_free(bev);
31 }
32 
EventNetworkImpl(EventTaskRunner * task_runner)33 EventNetworkImpl::EventNetworkImpl(EventTaskRunner* task_runner)
34     : task_runner_(task_runner) {
35   UpdateNetworkState();
36 }
37 
AddConnectionChangedCallback(const ConnectionChangedCallback & callback)38 void EventNetworkImpl::AddConnectionChangedCallback(
39     const ConnectionChangedCallback& callback) {
40   callbacks_.push_back(callback);
41 }
42 
UpdateNetworkState()43 void EventNetworkImpl::UpdateNetworkState() {
44   if (simulate_offline_) {
45     LOG(INFO) << "Simulating offline state";
46     connectivity_probe_.reset();
47     return UpdateNetworkStateCallback(State::kOffline);
48   }
49 
50   connectivity_probe_.reset(
51       bufferevent_socket_new(task_runner_->GetEventBase(), -1,
52                              BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS));
53   timeval timeout{kNetworkProbeTimeoutS, 0};
54   bufferevent_set_timeouts(connectivity_probe_.get(), &timeout, &timeout);
55   bufferevent_setcb(
56       connectivity_probe_.get(), nullptr, nullptr,
57       [](struct bufferevent* buf, short events, void* ctx) {
58         EventNetworkImpl* network = static_cast<EventNetworkImpl*>(ctx);
59         if (events & BEV_EVENT_CONNECTED) {
60           network->UpdateNetworkStateCallback(State::kOnline);
61           return;
62         }
63         if (events & (BEV_EVENT_ERROR | BEV_EVENT_EOF | BEV_EVENT_TIMEOUT)) {
64           int err = bufferevent_socket_get_dns_error(buf);
65           if (err) {
66             LOG(ERROR) << "network connect dns error: "
67                        << evutil_gai_strerror(err);
68           }
69           network->UpdateNetworkStateCallback(State::kOffline);
70           return;
71         }
72       },
73       this);
74   int err = bufferevent_socket_connect_hostname(
75       connectivity_probe_.get(), dns_base_.get(), AF_INET,
76       kNetworkProbeHostname, kNetworkProbePort);
77   if (err) {
78     LOG(ERROR) << " network connect socket error: " << evutil_gai_strerror(err);
79     return UpdateNetworkStateCallback(State::kOffline);
80   }
81 }
82 
UpdateNetworkStateCallback(provider::Network::State state)83 void EventNetworkImpl::UpdateNetworkStateCallback(
84     provider::Network::State state) {
85   if (state != network_state_) {
86     LOG(INFO) << "network state updated: " << weave::EnumToString(state);
87     network_state_ = state;
88 
89     // In general it's better to send false notification than miss one.
90     // However current implementation can only send them very often on every
91     // UpdateNetworkStateCallback or just here, guarder with this if condition.
92     for (const auto& cb : callbacks_)
93       cb.Run();
94   }
95 
96   // Reset current posted task.
97   weak_ptr_factory_.InvalidateWeakPtrs();
98 
99   // TODO(proppy): use netlink interface event instead of polling
100   task_runner_->PostDelayedTask(
101       FROM_HERE, base::Bind(&EventNetworkImpl::UpdateNetworkState,
102                             weak_ptr_factory_.GetWeakPtr()),
103       base::TimeDelta::FromSeconds(10));
104 }
105 
GetConnectionState() const106 weave::provider::Network::State EventNetworkImpl::GetConnectionState() const {
107   return network_state_;
108 }
109 
OpenSslSocket(const std::string & host,uint16_t port,const OpenSslSocketCallback & callback)110 void EventNetworkImpl::OpenSslSocket(const std::string& host,
111                                      uint16_t port,
112                                      const OpenSslSocketCallback& callback) {
113   SSLStream::Connect(task_runner_, host, port, callback);
114 }
115 
116 }  // namespace examples
117 }  // namespace weave
118